FHEM Forum

FHEM => Automatisierung => Thema gestartet von: klausw am 30 Oktober 2013, 15:43:06

Titel: notify bei eingehender TCP Botschaft auslösen
Beitrag von: klausw am 30 Oktober 2013, 15:43:06
Hallo zusammen,

ich habe ein fertiges Modul auf dem ein Mikrocontroller mit Ethernet Schnittstelle sitzt.
Die Ports des Mikrocontrollers lassen sich über die eingebaute Website auf Input, Output, A/D Conv., PWM... konfigurieren.
Über einfache TCP Kommandos lassen sich die Ports auslesen und Outputs setzen. Mit den ECMD Modulen funktioniert das auch super.

nun zu meinem Problem:
Inputs lassen sich so einstellen, das bei einem Triggerevent am Port eine TCP Botschaft gesendet wird.
Nun würde ich das gern mit FHEM auswerten und bei ankommender Botschaft ein notify auslösen. Also ohne pollen.
Gibt es da bereits Lösungen?

Gruß
Klaus
Titel: Antw:notify bei eingehender TCP Botschaft auslösen
Beitrag von: UliM am 30 Oktober 2013, 19:02:40
Hi,
wenn Du die gesendete "Botschaft" einstellen kannst, sicher - dann einfach den entspr. Schaltvorgang als URI absetzen.
Sonst bräuchtest Du halt nen port-listener.

Was genau setzt denn der MC ab? Konfigurierbar? Wie weit?

=8-)
Titel: Antw:notify bei eingehender TCP Botschaft auslösen
Beitrag von: klausw am 31 Oktober 2013, 10:51:36
Hi Uli,

war ja klar, das ich die wichtigsten Infos vergessen habe ;)
Die Botschaft lässt sich leider nicht ändern. Ich habe mal angefragt ob es möglich ist das nach zurüsten, aber noch keine Antwort.

Der MC hat 13 konfigurierbare Ports. Die von a bis m durchbenannt sind.
Wenn z.B. Port A auf Input mit Trigger auf allen Flanken eingestellt ist und ich mit z.B. Telnet, Socat etc. verbunden bin sendet er bei jedem Pegelwechsel auf High: "a=1" und auf Low: "a=0"

Sieht also so aus, als ob ich einen port-listener brauche.
Gibt es schon ein ähnliches Modul, welches ich anpassen kann?

Grüße
Klaus
Titel: Antw:notify bei eingehender TCP Botschaft auslösen
Beitrag von: klausw am 04 November 2013, 22:40:12
ich habe ne Antwort bekommen
In der neuen Firmware kann man die Kommandos anpassen, also werde ich erstmal abwarten :)
Titel: Antw:notify bei eingehender TCP Botschaft auslösen
Beitrag von: stevestrong am 16 Dezember 2013, 01:37:23
hi Klaus,
der port-listener würde mich auch interessieren.
hast du neuigkeiten diesbezüglich?
lg steve
Titel: Antw:notify bei eingehender TCP Botschaft auslösen
Beitrag von: rudolfkoenig am 16 Dezember 2013, 08:40:17
Vielleicht hilft cmdalias:

define a1 cmdalias a=1 AS trigger a1 a1
Titel: Antw:notify bei eingehender TCP Botschaft auslösen
Beitrag von: klausw am 16 Dezember 2013, 15:03:55
Zitat von: rudolfkoenig am 16 Dezember 2013, 08:40:17
Vielleicht hilft cmdalias:

define a1 cmdalias a=1 AS trigger a1 a1

Oh, das habe ich bisher übersehen
Leider ist es eine Raw TCP Verbindung.
Das heisst, es wird nur "a=1" gesendet, und nicht "fhemhost:48083/fhem?cmd=a=1".
Und die Verbindung muss von FHEM Seite aus aufgebaut werden.
Titel: Antw:notify bei eingehender TCP Botschaft auslösen
Beitrag von: klausw am 16 Dezember 2013, 15:12:07
Zitat von: stevestrong am 16 Dezember 2013, 01:37:23
hi laus,
der port-listener würde mich auch interessieren.
hast du neuigkeiten diesbezüglich?
lg steve
Hatte das Thema aus den Augen verloren.
Vielleicht habe ich über die Feiertage ein bisschen Zeit.
Titel: Antw:notify bei eingehender TCP Botschaft auslösen
Beitrag von: rudolfkoenig am 16 Dezember 2013, 15:54:10
ZitatDas heisst, es wird nur "a=1" gesendet, und nicht "fhemhost:48083/fhem?cmd=a=1".

Ich meinte das telnet Interface, aber der benoetigt auch ein NL.
/fhem?cmd= wird vom FHEMWEB benoetigt zusammen mit einem HTTP header.
Titel: Antw:notify bei eingehender TCP Botschaft auslösen
Beitrag von: klausw am 16 Dezember 2013, 22:04:15
Achso, ja darüber hatte ich auch schon nachgedacht.
Gerade habe ich mir das Teil nochmal angeschaut. Es liefert bei Statusänderung der GPIOs den Status aller Pins in Hex zurück. Also wenn ich GPIO1 auf 1 wechsle und der Rest 0 ist: "x=01 " und wenn er auf 0 wechselt "x=00 ".
Als Trennzeichen kommt also nur ein Leerzeichen.
Ausserdem möchte ich auf den gleichen Port auch senden können(um GPIOs die als Output eingestellt sind zu setzen).

Wenn es da nix gibt, kann ich aber auf DevIo aufsetzen oder?
Gibt es da ein einfaches Modulbeispiel, was ich mir zum Verständnis anschauen kann?
Titel: Antw:notify bei eingehender TCP Botschaft auslösen
Beitrag von: justme1968 am 16 Dezember 2013, 22:15:02
wenn du so weit bist das du lesen und schreiben kannst schau dir mal readingsProxy an damit kannst du ein device das alle ports kombiniert hat auf mehrere fhem devices aufsplitten die dann getrennt in fhem darstellbar sind. also mit eigenen icon, eigenen webCmd, ....

in dem thread hier: http://forum.fhem.de/index.php/topic,16374.msg109816.html#msg109816 (http://forum.fhem.de/index.php/topic,16374.msg109816.html#msg109816) findest du weiter unten ein paar beispiele.

gruss
  andre
Titel: Antw:notify bei eingehender TCP Botschaft auslösen
Beitrag von: klausw am 16 Dezember 2013, 22:22:13
Zitat von: justme1968 am 16 Dezember 2013, 22:15:02
wenn du so weit bist das du lesen und schreiben kannst schau dir mal readingsProxy an damit kannst du ein device das alle ports kombiniert hat auf mehrere fhem devices aufsplitten die dann getrennt in fhem darstellbar sind. also mit eigenen icon, eigenen webCmd, ....

in dem thread hier: http://forum.fhem.de/index.php/topic,16374.msg109816.html#msg109816 (http://forum.fhem.de/index.php/topic,16374.msg109816.html#msg109816) findest du weiter unten ein paar beispiele.

gruss
  andre
Du meinst um den Hexwert in einzelne Geräte auseinander zu klabustern?
Wenn ich schon so weit wäre  8)
Titel: Antw:notify bei eingehender TCP Botschaft auslösen
Beitrag von: justme1968 am 17 Dezember 2013, 11:11:34
mit IO::Socket::INET müsste es etwa in der art funktionieren:

- ReadFn definieren
- etwa so ein docket auf und zu machen:sub
iTunes_startUpdater($)
{
  my ($hash) = @_;
  my $name   = $hash->{NAME};

  iTunes_stopUpdater($hash,0);

  my $conn = IO::Socket::INET->new(PeerAddr=>"$hash->{Host}:$hash->{Port}");

  if($conn) {
    $hash->{STATE} = "Connected";

    $hash->{FD}    = $conn->fileno();
    $hash->{CD}    = $conn;         # sysread / close won't work on fileno
    $hash->{CONNECTS}++;
    $selectlist{$name} = $hash;
    Log(GetLogLevel($name,3), "$name: updater connected to $hash->{Host}:$hash->{Port}");

    iTunes_requestUpdate($hash);

  } else {
    iTunes_stopUpdater($hash, 1);

  }
}

sub
iTunes_stopUpdater($$)
{
  my ($hash, $connect) = @_;
  my $name   = $hash->{NAME};

  return if( !$hash->{CD} );

  close($hash->{CD}) if($hash->{CD});
  delete($hash->{FD});
  delete($hash->{CD});
  delete($selectlist{$name});
  $hash->{STATE} = "Disconnected";
  if($connect) {
    Log3 $name, 4, "$name: updater Connect failed.";
  } else {
    Log3 $name, 4, "$name: updater Disconnected";
  }
}

- sobald daten da sind wird die readFn aufgerufen und du kannst dort etwa so lesen:      my $buf;
      my $ret = sysread($hash->{CD}, $buf, 1024*1024);
      if(!defined($ret) || $ret <= 0) {
      iTunes_startUpdater($hash);
        return;
      }

- schreiben entsprechend mit syswrite

alles was iTunes heisst solltest du umbenennen :)

gruss
  andre
Titel: Antw:notify bei eingehender TCP Botschaft auslösen
Beitrag von: klausw am 19 Dezember 2013, 00:16:01
Danke Andre,

super Erklärung, das empfangen der Daten funktioniert schon.
Aber wenn die Gegenstelle nicht da ist dann versucht sich FHEM endlos zu verbinden, lässt sich da ein timeout einfügen?

Grüße
Klaus
Titel: Antw:notify bei eingehender TCP Botschaft auslösen
Beitrag von: justme1968 am 19 Dezember 2013, 00:18:26
wenn die verbindung nicht geht nicht sofort wieder versuchen sondern mit InternalTimer irgendwann später.

gruss
  andre
Titel: Antw:notify bei eingehender TCP Botschaft auslösen
Beitrag von: klausw am 19 Dezember 2013, 00:25:56
ich wiederhole nix, jedenfalls nicht vorsätzlich
im define habe ich iTunes_startUpdater (natürlich umbenannt ;)) aufgerufen, das ist alles
oder wird es da endlos wiederholt? Kann ich mir eigentlich nicht vorstellen
Titel: Antw:notify bei eingehender TCP Botschaft auslösen
Beitrag von: justme1968 am 19 Dezember 2013, 00:35:05
schau hin:) es wird bei einem lese fehler immer wieder eine neue verbindung auf gemacht. dir logik musst du für dich ändern.

gruss
  andre
Titel: Antw:notify bei eingehender TCP Botschaft auslösen
Beitrag von: klausw am 19 Dezember 2013, 15:42:35
erst vermutete ich ja, das ich zu müde war, aber scheinbar bin ich zu blöd ;)

##############################################################################
#
# 51_NetzerUtils.pm
#
##############################################################################
# Modul for NetzerUtils access
#
#
#
##############################################################################

package main;
use strict;
use warnings;
use POSIX;
use Scalar::Util qw(looks_like_number);
use IO::File;

sub NetzerUtils_Initialize($) {
  my ($hash) = @_;
  $hash->{DefFn}    = "NetzerUtils_Define";
  $hash->{ReadFn}  =  "NetzerUtils_Read";
  $hash->{GetFn}    = "NetzerUtils_Get";
  $hash->{SetFn}    = "NetzerUtils_Set";
  $hash->{AttrFn}   = "NetzerUtils_Attr";
  $hash->{UndefFn}  = "NetzerUtils_Undef";
  $hash->{ExceptFn} = "NetzerUtils_Except";
  $hash->{AttrList} = "poll_interval loglevel:0,1,2,3,4,5" .
                      " direction:input,output pud_resistor:off,up,down" .
                      " interrupt:none,falling,rising,both toggletostate:no,yes";
}

my %setsoutp = (
'on' => 0,
'off' => 0,
'toggle' => 0,
);

my %setsinpt = (
'readValue' => 0,
); 

sub NetzerUtils_Define($$) {
my ($hash, $def) = @_;

my @args = split("[ \t]+", $def);
my $menge = int(@args);
if (int(@args) < 3)
{
  return "Define: to less arguments. Usage:\n" .
         "define <name> NetzerUtils <Host> <port>";
}
#Prüfen, ob GPIO bereits verwendet
#foreach my $dev (devspec2array("TYPE=$hash->{TYPE}")) {
#  if ($args[2] eq InternalVal($dev,"RPI_pin","")) {
#    return "GPIO $args[2] already used by $dev";
#  }
#}

my $name = $args[0];
$hash->{Host} = $args[2];
$hash->{Port} = $args[3];
NetzerUtils_conn($hash);
# create default attributes
#my $msg = CommandAttr(undef, $name . ' direction input');
#return $msg if ($msg);

return undef;
}

sub NetzerUtils_Get($) {
  my ($hash) = @_;
  my $name = $hash->{NAME};
  #my $dir = $attr{$hash->{NAME}}{direction} || "output";
  my $dir = "";
  my $zustand = undef;
  #my $val = fileaccess($hash, "value");
  if ( defined ($val) ) {
    if ( $val == 1) {
      if ($dir eq "output") {$zustand = "on";} else {$zustand = "high";}
    } elsif ( $val == 0 ) {
      if ($dir eq "output") {$zustand = "off";} else {$zustand = "low";}
    }   
  } else {
    Log 1, "$hash->{NAME} GetFn: readout of Pinvalue fail";
  }
  $hash->{READINGS}{Pinlevel}{VAL} = $zustand;
  $hash->{READINGS}{Pinlevel}{TIME} = TimeNow();
  return "Current Value for $name: $zustand";
}

sub NetzerUtils_Set($@) {
  my ($hash, @a) = @_;
  my $name =$a[0];
  my $cmd = $a[1];
  #my $val = $a[2];
 
  if(defined($attr{$name}) && defined($attr{$name}{"direction"})) {
      my $mt = $attr{$name}{"direction"};
      if($mt && $mt eq "output") {
       if(!defined($setsoutp{$cmd})) { #prüfe ob Kommando in Liste
         my $slist = join(' ', keys %setsoutp);
         #return 'Unknown argument ' . $cmd . ', choose one of ' . join(' ', keys %setsoutp)
         return SetExtensions($hash, $slist, @a);
         } else {
             if ($cmd eq 'on') {
                #fileaccess($hash, "value", "1");
                #$hash->{STATE} = 'on';
                my $buf = "m=1\r\n";
                my $cnt= length ($buf);
                syswrite($hash->{CD}, $buf, $cnt);
             }  else {
                my $buf = "m=0\r\n";
                my $cnt= length ($buf);
                syswrite($hash->{CD}, $buf, $cnt);
                #fileaccess($hash, "value", "0");
                #$hash->{STATE} = 'off';
             }           
         }
      } else {
       if(!defined($setsinpt{$cmd})) {
        return 'Unknown argument ' . $cmd . ', choose one of ' . join(' ', keys %setsinpt)
       } else {
       
       }
      }     
  }
 
  if ($cmd eq 'readValue') {
      updatevalue($hash); 
    }
}

sub NetzerUtils_Attr(@) {
my (undef, $name, $attr, $val) = @_;
my $hash = $defs{$name};
my $msg = '';

if ($attr eq 'direction') {
   if (!$val) { #$val nicht definiert: Einstellungen löschen
       $msg = "$hash->{NAME}: no direction value. Use input output";
   } elsif ($val eq "input") {
       fileaccess($hash, "direction", "in");
       #Log 1, "$hash->{NAME}: direction: input";
   } elsif( ( AttrVal($hash->{NAME}, "interrupt", "none") ) ne ( "none" ) ) {
       $msg = "$hash->{NAME}: Delete attribute interrupt or set it to none for output direction";
   } elsif ($val eq "output") {
       fileaccess($hash, "direction", "out");
       #Log 1, "$hash->{NAME}: direction: output";
   } else {
       $msg = "$hash->{NAME}: Wrong direction value. Use input output";
   }
}
return ($msg) ? $msg : undef;
}

sub NetzerUtils_Read($) {
  Log 1, "Read gestartet";
  my ($hash) = @_;
  my $name = $hash->{NAME};
  my $buf;
  my $ret = sysread($hash->{CD}, $buf, 1024*1024);
  if(!defined($ret) || $ret <= 0) {
     NetzerUtils_conn($hash);
     return;
  } else {

     readingsBeginUpdate($hash);
     readingsBulkUpdate($hash, 'zeichenmenge', $ret);
     #for ( $i = 1; $i <= 5; $i++) {
     #}
     readingsBulkUpdate($hash, 'inhalt', $buf);
     readingsEndUpdate($hash, 1);
  }
}

sub NetzerUtils_Undef($$) {
  my ($hash, $arg) = @_;
  if ( defined (AttrVal($hash->{NAME}, "poll_interval", undef)) ) {
    RemoveInternalTimer($hash);
  }
  NetzerUtils_disconn($hash,0);
  return undef;
}

sub NetzerUtils_conn($) {
  my ($hash) = @_;
  my $name   = $hash->{NAME};

  NetzerUtils_disconn($hash,0);

  my $conn = IO::Socket::INET->new(PeerAddr=>"$hash->{Host}:$hash->{Port}");

  if($conn) {
    $hash->{STATE} = "Connected";

    $hash->{FD}    = $conn->fileno();
    $hash->{CD}    = $conn;         # sysread / close won't work on fileno
    $hash->{CONNECTS}++;
    $selectlist{$name} = $hash;
    Log(GetLogLevel($name,3), "$name: updater connected to $hash->{Host}:$hash->{Port}");

    #iTunes_requestUpdate($hash);

  } else {
    NetzerUtils_disconn($hash, 1);
  }
}

sub NetzerUtils_disconn($$) {
  my ($hash, $connect) = @_;
  my $name   = $hash->{NAME};

  return if( !$hash->{CD} );

  close($hash->{CD}) if($hash->{CD});
  delete($hash->{FD});
  delete($hash->{CD});
  delete($selectlist{$name});
  $hash->{STATE} = "Disconnected";
  if($connect) {
    Log3 $name, 4, "$name: updater Connect failed.";
  } else {
    Log3 $name, 4, "$name: updater Disconnected";
  }
}

1;


bei der "if($conn)" Schleife springe ich nach else, da eine Verbindung nicht zustande gekommen ist, richtig?
Dort gehts dann ins sub "NetzerUtils_disconn"
Der wiederum wird verlassen, da "$hash->{CD}" nicht definiert ist.
Aber dann bin ich doch durch. Ich erkenne keine Schleife.

Grüße
Klaus
Titel: Antw:notify bei eingehender TCP Botschaft auslösen
Beitrag von: justme1968 am 19 Dezember 2013, 16:09:08
prüf mal nach dem erzeugen des sockets noch ob es auch verbunden ist. das geht mit $conn->connected.

es kann sein das das socket zwar angelegt wird aber trotzdem die verbindung nicht funktioniert. dadurch landest du dann im read obwohl keine daten da sind. da wird dann die verbindung neu aufgebaut und das ist deine schleife.

ach ja noch was: leg deine readings mit readingsSingleUpdate oder readingsBeginUpdate/readingsBulkUpdate/readingsEndUpdate und nicht direkt selber in den hash.

gruss
  andre
Titel: Antw:notify bei eingehender TCP Botschaft auslösen
Beitrag von: klausw am 19 Dezember 2013, 17:11:29
ja das mit den readings ist alter kram der noch wegkommt

juhu ich habs:
ein timeout wirkt wunder :)
  my $timeout = $hash->{TIMEOUT} ? $hash->{TIMEOUT} : 3;
  my $conn = IO::Socket::INET->new(PeerAddr=>"$hash->{Host}:$hash->{Port}", Timeout => $timeout);


und schon gehts

$conn->connected ist nicht notwendig da im fehlerfall $conn nicht definiert
Titel: Antw:notify bei eingehender TCP Botschaft auslösen
Beitrag von: klausw am 07 Januar 2014, 16:05:04
Zitat von: justme1968 am 16 Dezember 2013, 22:15:02
wenn du so weit bist das du lesen und schreiben kannst schau dir mal readingsProxy an damit kannst du ein device das alle ports kombiniert hat auf mehrere fhem devices aufsplitten die dann getrennt in fhem darstellbar sind. also mit eigenen icon, eigenen webCmd, ....

in dem thread hier: http://forum.fhem.de/index.php/topic,16374.msg109816.html#msg109816 (http://forum.fhem.de/index.php/topic,16374.msg109816.html#msg109816) findest du weiter unten ein paar beispiele.

gruss
  andre

Das funktioniert super. Mit nur 2 Zeilen (define und attr valueFn) pro Kanal habe ich ein separates Device erzeugt.
Danke für den Tip :)