############################################## # $Id$ # # # modifikation fuer Energiemessung von martin-s # # Todo: # - unbekannte Strings mit Log1 speichern # - unbekannte Steckermodelle mit Log1 speichern # # Protokoll: # Prefix (5a a5), Anzahl Nutzbytes (2 Byte), Payload, Checksumme (FF - LowByte der Summe aller Payloadbytes), Postfix (5b b5) # Antwort von Dose hat immer die letzen 3 Bloecke der MAC vom 11-13 Byte # # Payload immer in "|" # #Init1 (vom Server): # 5a a5 00 07|02 05 0d 07 05 07 12|c6 5b b5 # ** ** ** ** ** ** ** scheinen zufaellig zu sein # 5a a5 00 01|02|fd 5b b5 #Antwort auf Init1 von Dose: # 5A A5 00 0B|03 01 0A C0 32 23 62 8A 7E 01 C2|AF 5B B5 # MM MM MM ** MM: letzte 3 Stellen der MAC, ** scheinbar eine Checksumme basierend auf den 6 zufaelligen Bytes von Init1 # ?? ?? ??: Unterschiedlich bei verschiedenen Steckermodellen #Init2 (vom Server): # 5a a5 00 02|05 01|f9 5b b5 #Antwort auf Init2 von Dose: # 5A A5 00 12|07 01 0A C0 32 23 62 8A 7E 00 01 06 AC CF 23 62 8A 7E|5F 5B B5 # MM MM MM MM: letzte 3 Stellen der MAC # MM MM MM MM MM MM MM: komplette MAC # ?? ?? ??: Unterschiedlich bei verschiedenen Steckermodellen # 5A A5 00 12|07 01 0A C0 32 23 62 8A 7E 00 02 05 00 01 01 08 11|4C 5B B5 Anzahl Bytes stimmt nicht! ist aber immer so # FF FF FF FF: Firmware Version # 5A A5 00 15|90 01 0A E0 32 23 62 8A 7E 00 00 00 81 11 00 00 01 00 00 00 00|32 5B B5 Status der Dose (wird auch immer bei Zustandsaenderung geschickt) # MM MM MM MM: letzte 3 Stellen der MAC # qq qq: Schaltquelle 81=lokal geschaltet, 11=remote geschaltet # oo oo: Schaltzustand ff=an, 00=aus # ?? ?? ??: Unterschiedlich bei verschiedenen Steckermodellen # Danach kommt alle x Sekunden ein Heartbeat von der Dose: # 5A A5 00 09|04 01 0A C0 32 23 62 8A 7E|71 5B B5 # MM MM MM # ?? ?? ??: Unterschiedlich bei verschiedenen Steckermodellen # Antwort vom Server (wenn die nicht kommt blinkt Dose wieder und muss neu initialisiert werden): # 5a a5 00 01|06|f9 5b b5 #--------------------------------------------------------------------------------------------------------- # Einschalten der Dose: # 5a a5 00 17|10 01 01 0a e0 32 23 62 8a 7e ff fe 00 00 10 11 00 00 01 00 00 00 ff|26 5b b5 # MM MM MM # ?? ?? ??: Unterschiedlich bei verschiedenen Steckermodellen # Ausschalten der Dose # 5a a5 00 17|10 01 01 0a e0 32 23 62 8a 7e ff fe 00 00 10 11 00 00 01 00 00 00 00|25 5b b5 # MM MM MM # ?? ?? ??: Unterschiedlich bei verschiedenen Steckermodellen # beides wird quittiert (ebenso wird auch bei lokaler betaetigung quittiert) -> siehe 3. Antwort auf Init 2 #--------------------------------------------------------------------------------------------------------- # Bei Dosen mit Verbrauchsdaten: # 5A A5 00 16|90 01 0a e0 35 23 d3 48 d4 ff fe 01 81 39 00 00 01 03 20 00 56 9b|70 5b b5 Verbrauchsdaten # ?? ?? MM MM MM # id id: Art der Daten 01 = Leistung, 02 = Energie, 03 = Spannung, 04 = Strom, 05 = Frequenz, 07 = maxpower, 08 = Cosphi # VV VV VV VV: Verbrauchswerte (muss durch 100 geteilt werden) package main; use strict; use warnings; use SetExtensions; use TcpServerUtils; my $prefix = pack('C*', (0x5a,0xa5)); my $postfix = pack('C*', (0x5b,0xb5)); my $init1a = pack('C*', (0x02,0x05,0x0d,0x07,0x05,0x07,0x12)); my $init1b = pack('C*', (0x02)); my $init2 = pack('C*', (0x05,0x01)); my $hbeat = pack('C*', (0x06)); my $switch1 = pack('C*', (0x10,0x01,0x01,0x0a,0xe0)); my $switch2 = pack('C*', (0xff,0xfe,0x00,0x00,0x10,0x11,0x00,0x00,0x01,0x00,0x00,0x00)); my $dosehb = pack('C*', (0x00,0x09,0x04,0x01,0x0a,0xc0)); my $cinit1 = pack('C*', (0x03,0x01,0x0a,0xc0)); my $cmac = pack('C*', (0x07,0x01,0x0a,0xc0)); my $cswitch = pack('C*', (0x90,0x01,0x0a,0xe0)); my $measure = pack('C*', (0xff,0xfe,0x01,0x81,0x39,0x00,0x00,0x01)); my %values = ( '01' => 'power', '02' => 'energy', '03' => 'voltage', '04' => 'current', '05' => 'frequency', '07' => 'maxpower', '08' => 'cosphi', ); my $timeout = 60; ##################################### sub GHoma_Initialize($) { # my ($hash) = @_; $hash->{SetFn} = "GHoma_Set"; # evtl. noch in define rein!!! $hash->{DefFn} = "GHoma_Define"; $hash->{ReadFn} = "GHoma_Read"; # wird von der globalen loop aufgerufen (ueber $hash->{FD} gefunden), wenn Daten verfuegbar sind $hash->{UndefFn} = "GHoma_Undef"; $hash->{AttrFn} = "GHoma_Attr"; $hash->{StateFn} = "GHoma_State"; $hash->{AttrList} = "restoreOnStartup:last,on,off restoreOnReinit:last,on,off blocklocal:yes,no ". "allowfrom connectTimeout connectInterval $readingFnAttributes"; $hash->{noAutocreatedFilelog} = 1; # kein Filelog bei Autocreate anlegen $hash->{ShutdownFn} = "GHoma_Shutdown"; $hash->{DbLog_splitFn} = "GHoma_DbLog_splitFn"; } ##################################### sub GHoma_ClientConnect($) { # im Mom unnuetz my ($hash) = @_; my $name = $hash->{NAME}; $hash->{DEF} =~ m/^(IPV6:)?(.*):(\d+)$/; my ($isIPv6, $server, $port) = ($1, $2, $3); Log3 $name, 4, "$name: Connecting to $server:$port..."; my @opts = ( PeerAddr => "$server:$port", Timeout => AttrVal($name, "connectTimeout", 60), ); my $client; if($hash->{SSL}) { $client = IO::Socket::SSL->new(@opts); } else { $client = IO::Socket::INET->new(@opts); } if($client) { $hash->{FD} = $client->fileno(); $hash->{CD} = $client; # sysread / close won't work on fileno $hash->{BUF} = ""; $hash->{CONNECTS}++; $selectlist{$name} = $hash; $hash->{STATE} = "Connected"; RemoveInternalTimer($hash); Log3 $name, 3, "$name: connected to $server:$port"; syswrite($hash->{CD}, ( GHoma_BuildString($init1a) . GHoma_BuildString($init1b) ) ); InternalTimer(gettimeofday()+ $timeout + 30, "GHoma_Timer", $hash,0); } else { GHoma_ClientDisconnect($hash, 1); } } ##################################### sub GHoma_ClientDisconnect($$) { # im Mom unnuetz my ($hash, $connect) = @_; my $name = $hash->{NAME}; close($hash->{CD}) if($hash->{CD}); delete($hash->{FD}); delete($hash->{CD}); delete($selectlist{$name}); $hash->{STATE} = "Offline"; InternalTimer(gettimeofday()+AttrVal($name, "connectInterval", 60), "GHoma_ClientConnect", $hash, 0); if($connect) { Log3 $name, 4, "$name: Connect failed."; } else { Log3 $name, 3, "$name: Offline"; } } ##################################### sub GHoma_Shutdown($) { # my ($hash) = @_; return unless defined $hash->{Id}; #nicht f?r Server # state auf letzten Schaltwert setzen oder auf fixen Startwert (wird bereitsbeim Shutdown ausgefuehrt) if (AttrVal($hash->{NAME},"restoreOnStartup","last") eq "on") { readingsSingleUpdate($hash, "state", "on", 1); } elsif (AttrVal($hash->{NAME},"restoreOnStartup","last") eq "last" && defined $hash->{LASTSTATE} && $hash->{LASTSTATE} eq "on" ) { readingsSingleUpdate($hash, "state", "on", 1); } else { readingsSingleUpdate($hash, "state", "off", 1); } return undef; } ##################################### sub GHoma_Define($$$) { # my ($hash, $def) = @_; #my @a = split("[ \t][ \t]*", $def); my ($name, $type, $pport, $global) = split("[ \t]+", $def); my $port = $pport; $port =~ s/^IPV6://; my $isServer = 1 if(defined($port) && $port =~ m/^\d+$/); my $isClient = 1 if($port && $port =~ m/^(.+):\d+$/); my $isSerCli = 1 if(defined($port) && $port =~ m/^([\da-f]{6})$/i); #return "Usage: define GHoma { [IPV6:]| }" if(!($isServer || $isClient || $isSerCli)); return "Usage: define GHoma { [IPV6:] }" if(!($isServer || $isClient || $isSerCli)); #$hash->{DeviceName} = $pport; if($isSerCli) { #ServerClient #my $name = $a[0]; # my $addr = $a[2]; #$hash->{Id} = pack('C*', ( hex(substr($pport,0,2)), hex(substr($pport,2,2)), hex(substr($pport,4,2)) ) ); $hash->{Id} = $pport; return; } # Make sure that fhem only runs once if($isServer) { my $ret = TcpServer_Open($hash, $pport, "global"); if($ret && !$init_done) { Log3 $name, 1, "$ret. Exiting."; exit(1); } return $ret; } if($isClient) { $hash->{isClient} = 1; GHoma_ClientConnect($hash); } return; } ##################################### sub GHoma_BuildString($) { # Botschaft zum senden erzeugen my ($data) = @_; my $count = pack('n*', length($data)); my $checksum = pack ('C*', 0xFF - (unpack("%8c*", $data)) ); #(my $smsg = ($prefix . $count . $data . $checksum . $postfix)) =~ s/(.|\n)/sprintf("%.2X ",ord($1))/eg; #Log3 undef, 1, "GHoma TX: $smsg"; return $prefix . $count . $data . $checksum . $postfix; } # ##################################### sub GHoma_moveclient($$) { # Handles von temporaerem Client zu Statischem uebertragen und Temporaeren dann loeschen my ($thash, $chash) = @_; if(defined($chash->{CD})) { # alte Verbindung entfernen, falls noch offen close($chash->{CD}); delete($chash->{CD}); #delete($selectlist{$chash->{NAME}}); delete($chash->{FD}); # Avoid Read->Close->Write } $chash->{FD} = $thash->{FD}; $chash->{CD} = $thash->{CD}; $chash->{SNAME} = $thash->{SNAME}; my @client = split("_",$thash->{NAME}); $chash->{IP} = $client[1]; $chash->{PORT} = $client[2]; $selectlist{$chash->{NAME}} = $chash; readingsSingleUpdate($chash, "state", "Initialize...", 1); delete($selectlist{$thash->{NAME}}); delete $thash->{FD}; CommandDelete(undef, $thash->{NAME}); syswrite( $chash->{CD}, GHoma_BuildString($init2) ); InternalTimer(gettimeofday()+ $timeout, "GHoma_Timer", $chash,0); } ##################################### sub GHoma_Read($) { # wird von der globalen loop aufgerufen (ueber $hash->{FD} gefunden), wenn Daten verfuegbar sind my ($hash) = @_; my $name = $hash->{NAME}; if($hash->{SERVERSOCKET}) { # Accept and create a child my $chash = TcpServer_Accept($hash, "GHoma"); return if(!$chash); Log3 $name, 4, "$name: angelegt: $chash->{NAME}"; syswrite($chash->{CD}, ( GHoma_BuildString($init1a) . GHoma_BuildString($init1b) ) ); InternalTimer(gettimeofday()+ $timeout, "GHoma_Timer", $chash,0); $chash->{CD}->flush(); return; } my $buf; my $ret = sysread($hash->{CD}, $buf, 256); if(!defined($ret)) { if($hash->{isClient}) { Log3 $name, 1, "$name \$buf nicht definiert"; GHoma_ClientDisconnect($hash, 0); } else { CommandDelete(undef, $name); } return; } if ( substr($buf,0,8) eq ($prefix . $dosehb )) { # Heartbeat (Dosen Id wird nicht ueberprueft) #DevIo_SimpleWrite($hash, GHoma_BuildString($hbeat) , undef); RemoveInternalTimer($hash); $buf =~ s/(.|\n)/sprintf("%.2X ",ord($1))/eg; #empfangene Zeichen in Hexwerte wandeln Log3 $name, 5, "$name Heartbeatanfrage empfangen: $buf"; syswrite( $hash->{CD}, GHoma_BuildString($hbeat) ); Log3 $hash, 5, "$hash->{NAME} Heartbeat gesendet"; InternalTimer(gettimeofday()+ $timeout, "GHoma_Timer", $hash,0); } else { # alles ausser Heartbeat my @msg = split(/$prefix/,$buf); foreach (@msg) { next if ( $_ eq "" ); if ( hex(unpack('H*', substr($_,length($_)-2,2))) != hex(unpack('H*', $postfix ))) { # Check Postfix Log3 $hash, 1, "$hash->{NAME} Fehler: postfix = " . unpack('H*', substr($_,length($_)-2,2)); next; } if ( hex(unpack('H*', substr($_,length($_)-3,1))) != ( 0xFF - unpack("%8c*", substr($_,2,length($_)-5) ) ) ) { # Check Checksum Log3 $hash, 1, "$hash->{NAME} Fehler: Checksum soll = " . hex(unpack('H*', substr($_,length($_)-3,1))) . " ist = ". ( 0xFF - unpack("%8c*", substr($_,2,length($_)-5) ) ); next; } if ( hex(unpack('H*', substr($_,0,2))) != ( length($_) - 5 ) ) { # Check Laenge Log3 $hash, 4, "$hash->{NAME} laengesoll = " . hex(unpack('H*', substr($_,0,2))) . " laengeist = " . ( length($_) - 5 ) } (my $smsg = $_) =~ s/(.|\n)/sprintf("%.2X ",ord($1))/eg; # empfangene Zeichen in Hexwerte wandeln Log3 $hash, 4, "$hash->{NAME} RX: 5A A5 $smsg"; # ...und ins Log schreiben $hash->{Pattern} = unpack('H*', substr($_,6,2) ) unless defined $hash->{Pattern}; if ( substr($_,2,4) eq ($cinit1)) { # Antwort auf erstes Init $hash->{Id} = unpack('H*', substr($_,8,3) ); unless ($hash->{isClient}) { # fuer Server Loesung bei erster Antwort von Dose nach bestehendem Device mit gleicher Id suchen und Verbindung auf dieses Modul uebertragen my $clientdefined = undef; foreach my $dev (devspec2array("TYPE=$hash->{TYPE}")) { # bereits bestehendes define mit dieser Id suchen if ($hash->{Id} eq InternalVal($dev,"Id","") && $hash->{NAME} ne $dev && InternalVal($dev,"TEMPORARY","") ne "1") { #Log3 $hash, 5, "$hash->{NAME}: $dev passt -> Handles uebertragen"; GHoma_moveclient($hash, $defs{$dev}); $clientdefined = 1; last } } unless ( defined $clientdefined) { # ...ein Neues anlegen, falls keins existiert Log3 $name, 4, "GHoma Unknown device $hash->{Id}, please define it"; DoTrigger("global", "UNDEFINED GHoma_$hash->{Id} GHoma $hash->{Id}"); GHoma_moveclient($hash, $defs{"GHoma_$hash->{Id}"}) if ($defs{"GHoma_$hash->{Id}"}); } } else { readingsSingleUpdate($hash, "state", "Initialize...", 1); syswrite( $hash->{CD}, GHoma_BuildString($init2) ); RemoveInternalTimer($hash); InternalTimer(gettimeofday()+ $timeout, "GHoma_Timer", $hash,0); } } elsif ( substr($_,2,4) eq $cmac && substr($_,8,3) eq substr($_,17,3) ) { # Nachricht mit MAC (kommt unter Anderem als Antwort auf Init2) my $mac; for my $i (0...5) { # MAC formattieren $mac .= sprintf("%.2X",ord( substr($_,14+$i,1) )); last if $i == 5; $mac .= ":"; } $hash->{MAC} = $mac; } elsif ( substr($_,2,4) eq $cmac && (( length($_) - 5 ) == 0x11 ) ) { # Nachricht mit Firmware Version my ($high,$mid,$low)=unpack('CCC',substr($_,16,3)); $hash->{FWVERSION} = $high.".".$mid.".".$low; } elsif ( substr($_,2,4) eq $cswitch && (( length($_) - 5 ) == 0x15 ) ) { # An oder Aus my $id = unpack('H*', substr($_,8,3) ); my $rstate = hex(unpack('H*', substr($_,22,1))) == 0xFF ? "on" : "off"; my $src = hex(unpack('H*', substr($_,14,1))) == 0x81 ? "local" : "remote"; if ( defined $hash->{LASTSTATE} && $hash->{STATE} eq "Initialize..." ) { # wenn dies erste Statusbotschaft nach Anmeldung my $nstate = AttrVal($name, "restoreOnReinit", "last"); if ( $nstate ne "last" && $nstate ne $rstate ) { GHoma_Set( $hash, $hash->{NAME}, $nstate ); } elsif ($nstate eq "last" && $hash->{LASTSTATE} ne $rstate) { GHoma_Set( $hash, $hash->{NAME}, $hash->{LASTSTATE} ); } } elsif ($src eq "local") { # bei schalten direkt an Steckdose soll... if (AttrVal($name, "blocklocal", "no") eq "yes") { # ...wieder zurueckgeschaltet werden, wenn Attribut blocklocal yes ist GHoma_Set($hash, $hash->{NAME}, $hash->{LASTSTATE}); } else { # ...laststate angepasst werden (um bei reinit richtigen wert zu haben) $hash->{LASTSTATE} = $rstate; } } if (defined $hash->{SNAME} && defined $defs{$hash->{SNAME}} ) { # Readings auch im Server eintragen readingsBeginUpdate($defs{$hash->{SNAME}}); readingsBulkUpdate($defs{$hash->{SNAME}}, $id .'_state', $rstate); readingsBulkUpdate($defs{$hash->{SNAME}}, $id .'_source', $src); readingsEndUpdate($defs{$hash->{SNAME}}, 1); } readingsBeginUpdate($hash); readingsBulkUpdate($hash, 'state', $rstate); readingsBulkUpdate($hash, 'source', $src); readingsEndUpdate($hash, 1); } elsif ( substr($_,2,4) eq $cswitch && substr($_,11,8) eq $measure && ( length($_) - 5 ) == 0x16) { # Botschaft mit Verbrauchsdaten readingsBeginUpdate($hash); my $value=$values{unpack('H*',substr($_,19,1))}; if (defined($value)) { my ($high,$mid,$low)=unpack('CCC',substr($_,21,3)); readingsBulkUpdate($hash, $value, ($high*65536+$mid*256+$low)/($value eq 'energy' ? 1000 : 100)); } else { # readingsBulkUpdate($hash, 'message_'.unpack('H*',substr($_,19,1)), unpack('H*',substr($_,20))); Log3 $name, 3, "$name unknown control message Id: " . unpack('H*',substr($_,19,1)) . " value: " . unpack('H*',substr($_,20)); } readingsEndUpdate($hash, 1); } else { # readingsBeginUpdate($hash); # readingsBulkUpdate($hash, 'message_unkn', unpack('H*',$_)); # readingsEndUpdate($hash, 1); Log3 $name, 3, "$name unknown message: " . unpack('H*',$_); } } } #Log3 $name, 5, "$name empfangen: $buf"; return } ##################################### sub GHoma_Timer($) { # wird ausgeloest wenn heartbeat nicht mehr kommt my ($hash) = @_; Log3 $hash, 3, "$hash->{NAME}: Timer abgelaufen"; readingsSingleUpdate($hash, "state", "offline", 1); GHoma_ClientDisconnect($hash, 0) if $hash->{isClient}; return TcpServer_Close($hash) if defined $hash->{FD}; #DevIo_Disconnected($hash); } ##################################### sub GHoma_Attr(@) { # my ($command, $name, $attr, $val) = @_; my $hash = $defs{$name}; # if($a[0] eq "set" && $a[2] eq "SSL") { # TcpServer_SetSSL($hash); # if($hash->{CD}) { # my $ret = IO::Socket::SSL->start_SSL($hash->{CD}); # Log3 $a[1], 1, "$hash->{NAME} start_SSL: $ret" if($ret); # } # } return undef; } ##################################### sub GHoma_Set($@) { # my ($hash, @a) = @_; my $name = $a[0]; my $type = $a[1]; return "Unknown argument $type, choose one of ConfigAll ConfigSingle" unless (defined $hash->{Id} || $type eq "ConfigAll" || $type eq "ConfigSingle"); # set fuer den Server my @sets = ('on:noArg', 'off:noArg'); my $status = ReadingsVal($hash->{NAME},"state",""); if($type eq "ConfigAll") { GHoma_udpsend($hash, defined $a[2] ? $a[2] : undef, undef ); } elsif($type eq "ConfigSingle") { return "$a[2] ist not an correct IP or hostname" unless $a[2] =~ /^((([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5]))|(([a-zA-Z0-9]+(-[a-zA-Z0-9]+)*)+(\.([a-zA-Z0-9]+(-[a-zA-Z0-9]+)*))*$)$/; GHoma_udpsend($hash, defined $a[3] ? $a[3] : undef, $a[2] ); } elsif($type eq "on") { $type = pack('C*', (0xff)); readingsSingleUpdate($hash, "state", "set_on", 1) if ( $status =~ m/([set_]?o[n|ff])$/i ); $hash->{LASTSTATE} = "on"; } elsif($type eq "off") { $type = pack('C*', (0x00)); readingsSingleUpdate($hash, "state", "set_off", 1) if ( $status =~ m/([set_]?o[n|ff])$/i ); $hash->{LASTSTATE} = "off"; } else { my $slist = join(' ', @sets); return SetExtensions($hash, $slist, @a); } if (defined $hash->{CD}) { Log3 $hash, 2, "$hash->{NAME}: Pattern noch nicht empfangen" unless defined $hash->{Pattern}; syswrite( $hash->{CD}, GHoma_BuildString($switch1 . pack('C*', ( hex(substr($hash->{Pattern},0,2)), hex(substr($hash->{Pattern},2,2)) ) ) . pack('C*', ( hex(substr($hash->{Id},0,2)), hex(substr($hash->{Id},2,2)), hex(substr($hash->{Id},4,2)) ) ) . $switch2 . $type) ); } return undef; } ##################################### sub GHoma_State($$$$) { # reload readings at FHEM start my ($hash, $tim, $sname, $sval) = @_; Log3 $hash, 4, "$hash->{NAME}: $sname kann auf $sval wiederhergestellt werden $tim"; if ( $sname eq "state" && defined $hash->{Id} ) { #wenn kein Server $hash->{LASTSTATE} = $sval; readingsSingleUpdate($hash, "state", "offline", 1) } return; } ##################################### sub GHoma_Undef($$) { # my ($hash, $arg) = @_; RemoveInternalTimer($hash); return TcpServer_Close($hash) if defined $hash->{FD}; } ##################################### sub GHoma_udpsend{ eval "use IO::Socket::INET;"; return "please install IO::Socket::INET" if($@); my ($hash, $ownIP, $clientIP) = @_; # flush after every write $| = 1; my ($socket,$data); $socket = new IO::Socket::INET ( PeerAddr => (defined $clientIP ? $clientIP : '255.255.255.255'), PeerPort => '48899', Proto => 'udp', Broadcast => (defined $clientIP ? 0 : 1) ) or die "ERROR in Socket Creation : $!\n"; #send operation unless (defined $ownIP) { my $ownIPl = `hostname -I`; my @ownIPs = grep { /^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$/ } split / /, $ownIPl; $ownIP = $ownIPs[0]; } else { return "$ownIP ist not an correct IP or hostname" unless $ownIP =~ /^((([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5]))|(([a-zA-Z0-9]+(-[a-zA-Z0-9]+)*)+(\.([a-zA-Z0-9]+(-[a-zA-Z0-9]+)*))*$)$/ } Log3 $hash, 1, "$hash->{NAME}: setting server address for GHoma plugs to $ownIP:$hash->{PORT}"; my @sdata = ( "HF-A11ASSISTHREAD", "+ok", "AT+NETP=TCP,Client,$hash->{PORT},$ownIP\r", "AT+TCPTO=120\r" ); foreach (@sdata) { $socket->send($_); Log3 $hash, 1, "$hash->{NAME}: sende Multicast: $_"; } $socket->close(); } sub GHoma_DbLog_splitFn($) { # Einheiten my ($event) = @_; Log3 undef, 5, "in DbLog_splitFn empfangen: $event"; my ($reading, $value, $unit) = ""; my @parts = split(/ /,$event); $reading = shift @parts; $reading =~ tr/://d; $value = $parts[0]; $unit = "W" if(lc($reading) =~ m/power/); $unit = "kWh" if(lc($reading) =~ m/energy/); $unit = "V" if(lc($reading) =~ m/voltage/); $unit = "A" if(lc($reading) =~ m/current/); $unit = "Hz" if(lc($reading) =~ m/frequency/); $unit = "W" if(lc($reading) =~ m/maxpower/); return ($reading, $value, $unit); } 1; =pod =item device =item summary controls an G-Homa wlan adapter plug =item summary_DE Steuerung einer G-Homa Wlan Steckdose =begin html

GHoma

      Connects fhem to an G-Homa adapter plug

      preliminary:
    • Configure WLAN settings (Firmware <= 1.06):
      bring device in AP mode (press button for more than 3s, repeat this step until the LED is permanently on)
      Now connect with your computer to G-Home network.
      Browse to 10.10.100.254 (username:password = admin:admin)
      In STA Setting insert your WLAN settings
    • Configure WLAN settings:
      bring device in AP mode (press button for more than 3s, repeat this step until the LED is permanently on)
      Configure WLAN with G-Homa App.
    • Configure Network Parameters setting (Firmware <= 1.06):
      Other Setting -> Protocol to TCP-Client
      Other Setting -> Port ID (remember value for FHEM settings)
      Other Setting -> Server Address (IP of your FHEM Server)
    • Configure Network Parameters settings:
      Use set ... ConfigAll from server device to set parameters automaticly.
    • Optional:
      Block all outgoing connections for G-Homa in your router.


    Define
      define <name> GHoma <port>
      Specifies the GHoma server device.
      New adapters will be added automaticaly after first connection.
      You can also manyally add an adapter:
      define <name> GHoma <Id>
      where Id is the last 6 numbers of the plug's MAC address
      Example: MAC= AC:CF:23:A5:E2:3B -> Id= A5E23B

    Set
      set <name> <value>

      where value is one of:
        off
        on
      The set extensions are also supported.

      For server device:
      set <name> ConfigAll [IP|hostname|FQDN of FHEM]
      Setting all GHoma plugs via UDP broadcast to TCP client of FHEM servers address and port of GHoma server device.
      set <name> ConfigSingle <IP of Plug> [IP|hostname|FQDN of FHEM]
      Setting an specific GHoma plug via UDP to TCP client of FHEM servers address and port of GHoma server device.
    Attributes
      For plug devices:
      • restoreOnStartup
        Restore switch state after reboot
        Default: last, valid values: last, on, off

      • restoreOnReinit
        Restore switch state after reconnect
        Default: last, valid values: last, on, off

      • blocklocal
        Restore switch state to reading state immideately after local switching
        Default: no, valid values: no, yes

      For server devices:
      • allowfrom
        Regexp of allowed ip-addresses or hostnames. If set, only connections from these addresses are allowed.

    • readingFnAttributes

=end html =begin html_DE

GHoma

      Verbindet fhem mit einem G-Homa Zwischenstecker

      Vorbereitung:
    • WLAN konfigurieren (bis Firmware 1.06):
      Gerät in den AP modus bringen (Knopf für mehr als 3s drücken, diesen Schritt wiederholen bis die LED permanent leuchtet)
      Nun einen Computer mit der SSID G-Home verbinden.
      Im Browser zu 10.10.100.254 (username:passwort = admin:admin)
      In STA Setting WLAN Einstellungen eintragen
    • WLAN konfigurieren:
      Gerät in den AP modus bringen (Knopf für mehr als 3s drücken, diesen Schritt wiederholen bis die LED permanent leuchtet)
      Mit der G-Homa App das WLAN des Zwischensteckers einstellen
    • Network Parameters settings (bis Firmware 1.06):
      Other Setting -> Protocol auf TCP-Server
      Other Setting -> Port ID (wird später für FHEM benötigt)
      Other Setting -> Server Address (IP Adresse des FHEM Servers)
    • Network Parameters settings:
      Über set ... ConfigAll des Server Gerätes die Parameter automatisch setzen.
    • Optional:
      Im Router alle ausgehenden Verbindungen für G-Homa blockieren.


    Define
      define <name> GHoma <port>
      Legt ein GHoma Server device an.
      Neue Zwischenstecker werden beim ersten verbinden automatisch angelegt.
      Diese können aber auch manuell angelegt werden:
      define <name> GHoma <Id>
      Die Id besteht aus den letzten 6 Stellen der MAC Adresse des Zwischensteckers.
      Beispiel: MAC= AC:CF:23:A5:E2:3B -> Id= A5E23B

    Set
      set <name> <value>

      Gültige Werte für value:
        off
        on
      Die set extensions werden auch unterstützt.

      Für Server Device:
      set <name> ConfigAll [IP|hostname|FQDN von FHEM]
      Einstellen aller GHoma Zwischenstecker über UDP broadcast auf TCP client mit FHEM Server Adresse und Port des GHoma Server Devices.
      set <name> ConfigSingle <IP des Zwischensteckers> [IP|hostname|FQDN von FHEM]
      Einstellen eines einzelnen GHoma Zwischensteckers über UDP auf TCP client mit FHEM Server Adresse und Port des GHoma Server Devices.
    Attributes
      Für Zwischenstecker devices:
      • restoreOnStartup
        Wiederherstellen der Portzustände nach Neustart
        Standard: last, gültige Werte: last, on, off

      • restoreOnReinit
        Wiederherstellen der Portzustände nach Neustart
        Standard: last, gültige Werte: last, on, off

      • blocklocal
        Wert im Reading State sofort nach Änderung über lokale Taste wiederherstellen
        Standard: no, gültige Werte: no, yes

      Für Server devices:
      • allowfrom
        Regexp der erlaubten IP-Adressen oder Hostnamen. Wenn dieses Attribut gesetzt wurde, werden ausschließlich Verbindungen von diesen Adressen akzeptiert.

    • readingFnAttributes

=end html_DE =cut