Hi,
Ich habe einen Server, der auf port 5005 UDP Pakete annimmt, je nach Befehl wird dann eine Schaltaktion ausgeführt und es wird der neue Zustand an den Anfragenden zurückgesendet. Der Server funktioniert gut, ist in Python zusammengehackt.
Dazu passend habe ich ein FHEM Modul zusammengeschrieben, da meine Perl Erfahrungen sehr begrenzt sind ist das ein zeitintensives zusammensuchen jedes Befehls gewesen, aber ich kann nun Befehle ohne weiteres senden und die Antwort auswerten.
Jetzt ist es mir aber ein Rätsel wieso ich beim pollen keine Events im Eventviewer sehe, obwohl das Reading mitsamt Zeitstempel aktualisiert wird. Kann ich jemanden bitten sich mal das Modul anzuschauen?
Kurzes Beispiel wie der Server, ohne FHEM, getestet werden kann:
So schaltet man eine Lampe ein, das gleiche mit "False" schaltet sie aus:
$ echo -en "Lampe12 = True\n" | nc -u servername portnummer
So fragt man den Schaltzustand ab:
$ echo -en "Lampe12\n" | nc -u server port
Das meldet entweder True oder False zurueck, je nach Schaltzustand.
Anlegen kann ich mein UDP device mit:
define Lampe12 UDPLights 127.0.0.1 5005
Dann kann ich auch mit dem WebCmd die Lampe ein und ausschalten. Das Reading "state" aktualisiert sich entsprechend und alles funktioniert wie erwartet. Auch wenn ich dann an FHEM vorbei den Server schalte, aktualisiert sich das Reading scheinbar (sehe ich nach Page-Reload der WebGUI).
Es werden aber eben nie Events im Eventviewer gesehen. Ich dachte das erledigt die Funktion readingsSingleUpdate mit, aber irgendwas fehlt wohl noch.
Über hilfreiche Antworten würde ich mich freuen.
Torx
Datei: 99_UDPLights.pm
package main;
use strict;
use warnings;
use IO::Socket;
sub UDPLights_Initialize($) {
my ($hash) = @_;
$hash->{DefFn} = 'UDPLights_Define';
$hash->{UndefFn} = 'UDPLights_Undef';
$hash->{SetFn} = 'UDPLights_Set';
$hash->{GetFn} = 'UDPLights_Get';
}
sub UDPLights_Define($$) {
my ($hash, $def) = @_;
my @args = split("[ \t][ \t]*", $def);
if(int(@args) < 4) {
return "too few parameters: define <name> UDPLights <address> <port>";
}
my ($name, $type, $host, $port) = @args;
$hash->{name} = $name;
$hash->{address} = $host;
$hash->{port} = $port;
readingsSingleUpdate($hash, "state", "Initialized", 1);
$hash->{STATE} = "Initialized";
# open up the UDP socket, keep it during lifetime of the module instance
my $conn = IO::Socket::INET->new(Proto=>"udp", PeerAddr=>$host, PeerPort=>$port);
$hash->{CD} = $conn;
return undef;
}
sub UDPLights_Undef($$) {
my ($hash, $arg) = @_;
RemoveInternalTimer($hash);
$hash->{CD}->close();
return undef;
}
sub UDPLights_Get($@) {
my ($hash, @param) = @_;
return '"get UDPLights" does not require arguments' if (int(@param) < 1);
my $name = $hash->{name};
Log3 ( $hash, 1, "$hash->{NAME}_Get: checking state of lights...");
# send UDP command, here just the name + Line End
$hash->{CD}->send("$name\n");
# receive the response
my $buffer;
$hash->{CD}->recv($buffer, 255);
$buffer =~ /.* = (True|False).*/;
my $newState = $1 eq "True" ? "on" : "off";
# only update reading if it is new to us
if( $hash->{STATE} ne $newState) {
readingsSingleUpdate($hash, "state", $newState, 1);
$hash->{STATE} = $newState;
}
Log3 ( $hash, 1, "$hash->{NAME}_Get: checking state of lights: $newState");
# Check state every second
RemoveInternalTimer($hash);
InternalTimer(gettimeofday() + 1, "UDPLights_Get", $hash, 0);
return $newState;
}
sub UDPLights_Set($@) {
my ($hash, @param) = @_;
return "Unknown argument $param[1], choose one of on off" if( !($param[1] eq "on" or $param[1] eq "off") );
my $name = shift @param;
my $value = shift @param;
my $submittedValue = $value eq "on" ? "True" : "False";
$hash->{CD}->send("$name = $submittedValue\n");
my $buffer;
$hash->{CD}->recv($buffer, 255);
$buffer =~ /.* = (True|False).*/;
my $newState = $1 eq "True" ? "on" : "off";
readingsSingleUpdate($hash, "state", $newState, 1);
$hash->{STATE} = $newState;
#return "$name set to $value ($buffer) ($newState) $submittedValue";
return undef;
}
1;
Tjo,
habe es selbst ans Laufen bekommen. Das Problem war, dass der Timer nicht aufgerufen wurde, da in der GET Funktion die Anzahl der Parameter nicht stimmte.
Mit dieser Version des Moduls kann ich nun schön meinen UDP Server pollen und auf die Events reagieren. Auch steuerung des UDP Servers mit anderen Fernbedienungen wird korrekt erkannt und es werden die Events gefeuert.
99_UDPLights.pm
package main;
use strict;
use warnings;
use IO::Socket;
sub UDPLights_Initialize($) {
my ($hash) = @_;
$hash->{DefFn} = 'UDPLights_Define';
$hash->{UndefFn} = 'UDPLights_Undef';
$hash->{SetFn} = 'UDPLights_Set';
$hash->{GetFn} = 'UDPLights_Get';
}
sub UDPLights_Define($$) {
my ($hash, $def) = @_;
my @args = split("[ \t][ \t]*", $def);
if(int(@args) < 4) {
return "too few parameters: define <name> UDPLights <address> <port>";
}
my ($name, $type, $host, $port) = @args;
$hash->{name} = $name;
$hash->{address} = $host;
$hash->{port} = $port;
readingsSingleUpdate($hash, "state", "Initialized", 1);
$hash->{STATE} = "Initialized";
# open up the UDP socket, keep it during lifetime of the module instance
my $conn = IO::Socket::INET->new(Proto=>"udp", PeerAddr=>$host, PeerPort=>$port);
$hash->{CD} = $conn;
#start internal timer
InternalTimer(gettimeofday()+1, "UDPLights_Get", $hash, 0);
return undef;
}
sub UDPLights_Undef($$) {
my ($hash, $arg) = @_;
RemoveInternalTimer($hash);
$hash->{CD}->close();
return undef;
}
sub UDPLights_Get($@) {
my ($hash, @param) = @_;
return "Unknown argument $param[1], choose one of noArg" if (defined($param[1]) && $param[1] eq "?");
my $name = $hash->{name};
Log3 ( $hash, 5, "$hash->{NAME}_Get: checking state of lights...");
# send UDP command, here just the name + Line End
$hash->{CD}->send("$name\n");
# receive the response
my $buffer;
$hash->{CD}->recv($buffer, 255);
$buffer =~ /.* = (True|False).*/;
my $newState = $1 eq "True" ? "on" : "off";
# only update reading if it is new to us
if( $hash->{STATE} ne $newState) {
readingsSingleUpdate($hash, "state", $newState, 1);
$hash->{STATE} = $newState;
}
Log3 ( $hash, 5, "$hash->{NAME}_Get: checking state of lights: $newState");
# Check state every second
RemoveInternalTimer($hash);
InternalTimer(gettimeofday() + 1, "UDPLights_Get", $hash, 1);
return $newState;
}
sub UDPLights_Set($@) {
my ($hash, @param) = @_;
return "Unknown argument $param[1], choose one of on off" if( !($param[1] eq "on" or $param[1] eq "off") );
my $name = shift @param;
my $value = shift @param;
my $submittedValue = $value eq "on" ? "True" : "False";
$hash->{CD}->send("$name = $submittedValue\n");
my $buffer;
$hash->{CD}->recv($buffer, 255);
$buffer =~ /.* = (True|False).*/;
#return "$name set to $value ($buffer) ($newState) $submittedValue";
return undef;
}
1;
Falls es jemanden interessiert, hier ist der Python-Server. Er steuert über ULN2803 Relais mit 12 Lampen und vier Schaltern...
#!/usr/bin/python
import RPi.GPIO as GPIO
import time
import inspect
import socket
# To test from the bash:
# $ echo -en "Lampe12 = Toggle\n" | nc -u wohnzimmer 5005
# this will toggle Lamp12 output
GPIO.cleanup()
GPIO.setmode(GPIO.BOARD)
class LAMPE(object):
def __init__(self, pin, name="undef"):
self.__gpio = pin
self.__name = name
self.__state = False
self.__oldstate = self.__state
GPIO.setup(self.__gpio, GPIO.OUT)
GPIO.output(self.__gpio, False)
def set(self,state):
self.__state = state
def get(self):
return self.__state
def output(self):
if self.__oldstate == self.__state:
return
GPIO.output(self.__gpio, self.__state)
self.__oldstate = self.__state
#time.sleep(0.1)
class SCHALTER(object):
def __init__(self, pin, debounce=300):
self.__gpio = pin
self.__state = False
self.__oldstate = self.__state
GPIO.setup(self.__gpio, GPIO.IN, pull_up_down = GPIO.PUD_DOWN)
self.__state = (GPIO.input(self.__gpio) == 1)
self.__oldstate = self.__state
def get(self):
self.__state = (GPIO.input(self.__gpio) == 1)
self.__oldstate = self.__state
return self.__state
def changed(self):
return (GPIO.input(self.__gpio) == 1) != self.__oldstate
global Lampen
Lampen = [LAMPE(23,"Bla Bla"), \
LAMPE(15,"Bla Bla"), \
LAMPE(21,"Bla"), \
LAMPE(19, "Bla Bla"), \
LAMPE(18, "Bla"), \
LAMPE(26, "Bla Bla Bla"), \
LAMPE(22, "Bla"), \
LAMPE(24, "Bla Bla"), \
LAMPE(13, "Bla Bla"), \
LAMPE(3, "Bla"), \
LAMPE(5, "Bla"), \
LAMPE(11, "Bla"), \
LAMPE(7, "Bla")]
# Definition der GPIOs der Schalter
Schalter = [SCHALTER(8), \
SCHALTER(10), \
SCHALTER(16), \
SCHALTER(12)]
UDP_IP = "0.0.0.0"
UDP_PORT = 5005
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sock.bind((UDP_IP, UDP_PORT))
sock.setblocking(0)
def setlampe(nr, value, addr):
global Lampen
Lampen[nr-1].set(value)
sock.sendto('Lampe' + str(nr) + ' = '+ str(Lampen[nr-1].get()) +'\n', addr)
commands = {'Lampe1 = True\n' : lambda addr: setlampe(1, True, addr), \
'Lampe1 = False\n' : lambda addr: setlampe(1, False, addr), \
'Lampe1 = Toggle\n' : lambda addr: setlampe(1, not Lampen[0].get(), addr), \
'Lampe1\n' : lambda addr: setlampe(1, Lampen[0].get(), addr), \
'Lampe2 = True\n' : lambda addr: setlampe(2, True, addr), \
'Lampe2 = False\n' : lambda addr: setlampe(2, False, addr), \
'Lampe2 = Toggle\n' : lambda addr: setlampe(2, not Lampen[1].get(), addr), \
'Lampe2\n' : lambda addr: setlampe(2, Lampen[1].get(), addr), \
'Lampe3 = True\n' : lambda addr: setlampe(3, True, addr), \
'Lampe3 = False\n' : lambda addr: setlampe(3, False, addr), \
'Lampe3 = Toggle\n' : lambda addr: setlampe(3, not Lampen[2].get(), addr), \
'Lampe3\n' : lambda addr: setlampe(3, Lampen[2].get(), addr), \
'Lampe4 = True\n' : lambda addr: setlampe(4, True, addr), \
'Lampe4 = False\n' : lambda addr: setlampe(4, False, addr), \
'Lampe4 = Toggle\n' : lambda addr: setlampe(4, not Lampen[3].get(), addr), \
'Lampe4\n' : lambda addr: setlampe(4, Lampen[3].get(), addr), \
'Lampe5 = True\n' : lambda addr: setlampe(5, True, addr), \
'Lampe5 = False\n' : lambda addr: setlampe(5, False, addr), \
'Lampe5 = Toggle\n' : lambda addr: setlampe(5, not Lampen[4].get(), addr), \
'Lampe5\n' : lambda addr: setlampe(5, Lampen[4].get(), addr), \
'Lampe6 = True\n' : lambda addr: setlampe(6, True, addr), \
'Lampe6 = False\n' : lambda addr: setlampe(6, False, addr), \
'Lampe6 = Toggle\n' : lambda addr: setlampe(6, not Lampen[5].get(), addr), \
'Lampe6\n' : lambda addr: setlampe(6, Lampen[5].get(), addr), \
'Lampe7 = True\n' : lambda addr: setlampe(7, True, addr), \
'Lampe7 = False\n' : lambda addr: setlampe(7, False, addr), \
'Lampe7 = Toggle\n' : lambda addr: setlampe(7, not Lampen[6].get(), addr), \
'Lampe7\n' : lambda addr: setlampe(7, Lampen[6].get(), addr), \
'Lampe8 = True\n' : lambda addr: setlampe(8, True, addr), \
'Lampe8 = False\n' : lambda addr: setlampe(8, False, addr), \
'Lampe8 = Toggle\n' : lambda addr: setlampe(8, not Lampen[7].get(), addr), \
'Lampe8\n' : lambda addr: setlampe(8, Lampen[7].get(), addr), \
'Lampe9 = True\n' : lambda addr: setlampe(9, True, addr), \
'Lampe9 = False\n' : lambda addr: setlampe(9, False, addr), \
'Lampe9 = Toggle\n' : lambda addr: setlampe(9, not Lampen[8].get(), addr), \
'Lampe9\n' : lambda addr: setlampe(9, Lampen[8].get(), addr), \
'Lampe10 = True\n' : lambda addr: setlampe(10, True, addr), \
'Lampe10 = False\n' : lambda addr: setlampe(10, False, addr), \
'Lampe10 = Toggle\n' : lambda addr: setlampe(10, not Lampen[9].get(), addr), \
'Lampe10\n' : lambda addr: setlampe(10, Lampen[9].get(), addr), \
'Lampe11 = True\n' : lambda addr: setlampe(11, True, addr), \
'Lampe11 = False\n' : lambda addr: setlampe(11, False, addr), \
'Lampe11 = Toggle\n' : lambda addr: setlampe(11, not Lampen[10].get(), addr), \
'Lampe11\n' : lambda addr: setlampe(11, Lampen[10].get(), addr), \
'Lampe12 = True\n' : lambda addr: setlampe(12, True, addr), \
'Lampe12 = False\n' : lambda addr: setlampe(12, False, addr), \
'Lampe12 = Toggle\n' : lambda addr: setlampe(12, not Lampen[11].get(), addr), \
'Lampe12\n' : lambda addr: setlampe(12, Lampen[11].get(), addr), \
'Lampe13 = True\n' : lambda addr: setlampe(13, True, addr), \
'Lampe13 = False\n' : lambda addr: setlampe(13, False, addr), \
'Lampe13 = Toggle\n' : lambda addr: setlampe(13, not Lampen[12].get(), addr), \
'Lampe13\n' : lambda addr: setlampe(13, Lampen[12].get(), addr)}
for i in [0,1,2,3,4,5,6,7,8,9,10,11,12]:
Lampen[i].set(False)
while True:
#print("Start: %s" % time.ctime())
time.sleep(0.05)
if Schalter[0].changed():
for i in [0,1,2,3,4,5,6,7,8,9,10,11,12]:
Lampen[i].set(Schalter[0].get())
if Schalter[1].changed():
for i in [0,1,2,3]:
Lampen[i].set(Schalter[1].get())
if(Schalter[2].changed()):
for i in [6,3,7,12,9,10]:
Lampen[i].set(Schalter[2].get())
if(Schalter[3].changed()):
for i in [6,12,9]:
Lampen[i].set(Schalter[3].get())
data = ''
addr = ''
try:
data, addr = sock.recvfrom(1024)
except socket.error:
pass
if len(data) > 0:
try:
commands[data](addr)
except KeyError:
sock.sendto('500 - not implemented\n', addr)
for i in [0,1,2,3,4,5,6,7,8,9,10,11,12]:
Lampen[i].output()
GPIO.cleanup()