UDP Server pollen, es wird kein Event generiert

Begonnen von Torxgewinde, 28 Februar 2016, 19:25:25

Vorheriges Thema - Nächstes Thema

Torxgewinde

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;

Torxgewinde

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()