53_GHoma.pm 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651
  1. ##############################################
  2. # $Id: 53_GHoma.pm 14991 2017-09-02 17:36:55Z klausw $
  3. #
  4. # Protokoll:
  5. # Prefix (5a a5), Anzahl Nutzbytes (2 Byte), Payload, Checksumme (FF - LowByte der Summe aller Payloadbytes), Postfix (5b b5)
  6. # Antwort von Dose hat immer die letzen 3 Bloecke der MAC vom 11-13 Byte
  7. #
  8. # Payload immer in "|"
  9. # Init1 (vom Server):
  10. # 5a a5 00 07|02 05 0d 07 05 07 12|c6 5b b5
  11. # ** ** ** ** ** ** ** scheinen zufaellig zu sein
  12. # 5a a5 00 01|02|fd 5b b5
  13. # Antwort auf Init1 von Dose:
  14. # 5A A5 00 0B|03 01 0A C0 32 23 62 8A 7E 01 C2|AF 5B B5
  15. # MM MM MM ** MM: letzte 3 Stellen der MAC, ** scheinbar eine Checksumme basierend auf den 6 zufaelligen Bytes von Init1
  16. # Init2 (vom Server):
  17. # 5a a5 00 02|05 01|f9 5b b5
  18. # Antwort auf Init2 von Dose:
  19. # 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
  20. # MM MM MM MM: letzte 3 Stellen der MAC
  21. # MM MM MM MM MM MM MM: komplette MAC
  22. # 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
  23. # 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)
  24. # MM MM MM MM: letzte 3 Stellen der MAC
  25. # qq qq: Schaltquelle 81=lokal geschaltet, 11=remote geschaltet
  26. # oo oo: Schaltzustand ff=an, 00=aus
  27. # Danach kommt alle x Sekunden ein Heartbeat von der Dose:
  28. # 5A A5 00 09|04 01 0A C0 32 23 62 8A 7E|71 5B B5
  29. # MM MM MM
  30. # Antwort vom Server (wenn die nicht kommt blinkt Dose wieder und muss neu initialisiert werden):
  31. # 5a a5 00 01|06|f9 5b b5
  32. #---------------------------------------------------------------------------------------------------------
  33. # Einschalten der Dose:
  34. # 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
  35. # MM MM MM
  36. # Ausschalten der Dose
  37. # 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
  38. # MM MM MM
  39. # beides wird quittiert (ebenso wird auch bei lokaler betaetigung quittiert) -> siehe 3. Antwort auf Init 2
  40. package main;
  41. use strict;
  42. use warnings;
  43. use SetExtensions;
  44. use TcpServerUtils;
  45. use constant { PREFIX => pack('C*', (0x5a,0xa5)),
  46. POSTFIX => pack('C*', (0x5b,0xb5)),
  47. INIT1A => pack('C*', (0x02,0x05,0x0d,0x07,0x05,0x07,0x12)), };
  48. my $prefix = pack('C*', (0x5a,0xa5));
  49. my $postfix = pack('C*', (0x5b,0xb5));
  50. my $init1a = pack('C*', (0x02,0x05,0x0d,0x07,0x05,0x07,0x12));
  51. my $init1b = pack('C*', (0x02));
  52. my $init2 = pack('C*', (0x05,0x01));
  53. my $hbeat = pack('C*', (0x06));
  54. my $switch1 = pack('C*', (0x10,0x01,0x01,0x0a,0xe0,0x32,0x23));
  55. my $switch2 = pack('C*', (0xff,0xfe,0x00,0x00,0x10,0x11,0x00,0x00,0x01,0x00,0x00,0x00));
  56. my $dosehb = pack('C*', (0x00,0x09,0x04,0x01,0x0A,0xC0,0x32,0x23));
  57. my $cinit1 = pack('C*', (0x03,0x01,0x0a,0xc0,0x32,0x23));
  58. my $cmac = pack('C*', (0x07,0x01,0x0a,0xc0,0x32,0x23));
  59. my $cswitch = pack('C*', (0x90,0x01,0x0a,0xe0,0x32,0x23));
  60. my $timeout = 60;
  61. #####################################
  62. sub GHoma_Initialize($) { #
  63. my ($hash) = @_;
  64. $hash->{SetFn} = "GHoma_Set"; # evtl. noch in define rein!!!
  65. $hash->{DefFn} = "GHoma_Define";
  66. $hash->{ReadFn} = "GHoma_Read"; # wird von der globalen loop aufgerufen (ueber $hash->{FD} gefunden), wenn Daten verfuegbar sind
  67. $hash->{UndefFn} = "GHoma_Undef";
  68. $hash->{AttrFn} = "GHoma_Attr";
  69. $hash->{StateFn} = "GHoma_State";
  70. $hash->{AttrList} = "restoreOnStartup:last,on,off restoreOnReinit:last,on,off blocklocal:yes,no ".
  71. "allowfrom connectTimeout connectInterval";
  72. $hash->{noAutocreatedFilelog} = 1; # kein Filelog bei Autocreate anlegen
  73. $hash->{ShutdownFn} = "GHoma_Shutdown";
  74. }
  75. #####################################
  76. sub GHoma_ClientConnect($) { # im Mom unnuetz
  77. my ($hash) = @_;
  78. my $name = $hash->{NAME};
  79. $hash->{DEF} =~ m/^(IPV6:)?(.*):(\d+)$/;
  80. my ($isIPv6, $server, $port) = ($1, $2, $3);
  81. Log3 $name, 4, "$name: Connecting to $server:$port...";
  82. my @opts = (
  83. PeerAddr => "$server:$port",
  84. Timeout => AttrVal($name, "connectTimeout", 60),
  85. );
  86. my $client;
  87. if($hash->{SSL}) {
  88. $client = IO::Socket::SSL->new(@opts);
  89. } else {
  90. $client = IO::Socket::INET->new(@opts);
  91. }
  92. if($client) {
  93. $hash->{FD} = $client->fileno();
  94. $hash->{CD} = $client; # sysread / close won't work on fileno
  95. $hash->{BUF} = "";
  96. $hash->{CONNECTS}++;
  97. $selectlist{$name} = $hash;
  98. $hash->{STATE} = "Connected";
  99. RemoveInternalTimer($hash);
  100. Log3 $name, 3, "$name: connected to $server:$port";
  101. syswrite($hash->{CD}, ( GHoma_BuildString($init1a) . GHoma_BuildString($init1b) ) );
  102. InternalTimer(gettimeofday()+ $timeout + 30, "GHoma_Timer", $hash,0);
  103. } else {
  104. GHoma_ClientDisconnect($hash, 1);
  105. }
  106. }
  107. #####################################
  108. sub GHoma_ClientDisconnect($$) { # im Mom unnuetz
  109. my ($hash, $connect) = @_;
  110. my $name = $hash->{NAME};
  111. close($hash->{CD}) if($hash->{CD});
  112. delete($hash->{FD});
  113. delete($hash->{CD});
  114. delete($selectlist{$name});
  115. $hash->{STATE} = "Offline";
  116. InternalTimer(gettimeofday()+AttrVal($name, "connectInterval", 60),
  117. "GHoma_ClientConnect", $hash, 0);
  118. if($connect) {
  119. Log3 $name, 4, "$name: Connect failed.";
  120. } else {
  121. Log3 $name, 3, "$name: Offline";
  122. }
  123. }
  124. #####################################
  125. sub GHoma_Shutdown($) { #
  126. my ($hash) = @_;
  127. return unless defined $hash->{Id}; #nicht f?r Server
  128. # state auf letzten Schaltwert setzen oder auf fixen Startwert (wird bereitsbeim Shutdown ausgefuehrt)
  129. if (AttrVal($hash->{NAME},"restoreOnStartup","last") eq "on") {
  130. readingsSingleUpdate($hash, "state", "on", 1);
  131. } elsif (AttrVal($hash->{NAME},"restoreOnStartup","last") eq "last" && defined $hash->{LASTSTATE} && $hash->{LASTSTATE} eq "on" ) {
  132. readingsSingleUpdate($hash, "state", "on", 1);
  133. } else {
  134. readingsSingleUpdate($hash, "state", "off", 1);
  135. }
  136. return undef;
  137. }
  138. #####################################
  139. sub GHoma_Define($$$) { #
  140. my ($hash, $def) = @_;
  141. #my @a = split("[ \t][ \t]*", $def);
  142. my ($name, $type, $pport, $global) = split("[ \t]+", $def);
  143. my $port = $pport;
  144. $port =~ s/^IPV6://;
  145. my $isServer = 1 if(defined($port) && $port =~ m/^\d+$/);
  146. my $isClient = 1 if($port && $port =~ m/^(.+):\d+$/);
  147. my $isSerCli = 1 if(defined($port) && $port =~ m/^([\da-f]{6})$/i);
  148. #return "Usage: define <name> GHoma { [IPV6:]<tcp-portnr>|<serverName:port> }" if(!($isServer || $isClient || $isSerCli));
  149. return "Usage: define <name> GHoma { [IPV6:]<tcp-portnr> }" if(!($isServer || $isClient || $isSerCli));
  150. #$hash->{DeviceName} = $pport;
  151. if($isSerCli) { #ServerClient
  152. #my $name = $a[0];
  153. # my $addr = $a[2];
  154. #$hash->{Id} = pack('C*', ( hex(substr($pport,0,2)), hex(substr($pport,2,2)), hex(substr($pport,4,2)) ) );
  155. $hash->{Id} = $pport;
  156. return;
  157. }
  158. # Make sure that fhem only runs once
  159. if($isServer) {
  160. my $ret = TcpServer_Open($hash, $pport, "global");
  161. if($ret && !$init_done) {
  162. Log3 $name, 1, "$ret. Exiting.";
  163. exit(1);
  164. }
  165. return $ret;
  166. }
  167. if($isClient) {
  168. $hash->{isClient} = 1;
  169. GHoma_ClientConnect($hash);
  170. }
  171. return;
  172. }
  173. #####################################
  174. sub GHoma_BuildString($) { # Botschaft zum senden erzeugen
  175. my ($data) = @_;
  176. my $count = pack('n*', length($data));
  177. my $checksum = pack ('C*', 0xFF - (unpack("%8c*", $data)) );
  178. #(my $smsg = ($prefix . $count . $data . $checksum . $postfix)) =~ s/(.|\n)/sprintf("%.2X ",ord($1))/eg;
  179. #Log3 undef, 1, "GHoma TX: $smsg";
  180. return $prefix . $count . $data . $checksum . $postfix;
  181. } #
  182. #####################################
  183. sub GHoma_moveclient($$) { # Handles von temporaerem Client zu Statischem uebertragen und Temporaeren dann loeschen
  184. my ($thash, $chash) = @_;
  185. if(defined($chash->{CD})) { # alte Verbindung entfernen, falls noch offen
  186. close($chash->{CD});
  187. delete($chash->{CD});
  188. #delete($selectlist{$chash->{NAME}});
  189. delete($chash->{FD}); # Avoid Read->Close->Write
  190. }
  191. $chash->{FD} = $thash->{FD};
  192. $chash->{CD} = $thash->{CD};
  193. $chash->{SNAME} = $thash->{SNAME};
  194. my @client = split("_",$thash->{NAME});
  195. $chash->{IP} = $client[1];
  196. $chash->{PORT} = $client[2];
  197. $selectlist{$chash->{NAME}} = $chash;
  198. readingsSingleUpdate($chash, "state", "Initialize...", 1);
  199. delete($selectlist{$thash->{NAME}});
  200. delete $thash->{FD};
  201. CommandDelete(undef, $thash->{NAME});
  202. syswrite( $chash->{CD}, GHoma_BuildString($init2) );
  203. InternalTimer(gettimeofday()+ $timeout, "GHoma_Timer", $chash,0);
  204. }
  205. #####################################
  206. sub GHoma_Read($) { # wird von der globalen loop aufgerufen (ueber $hash->{FD} gefunden), wenn Daten verfuegbar sind
  207. my ($hash) = @_;
  208. my $name = $hash->{NAME};
  209. if($hash->{SERVERSOCKET}) { # Accept and create a child
  210. my $chash = TcpServer_Accept($hash, "GHoma");
  211. return if(!$chash);
  212. Log3 $name, 4, "$name: angelegt: $chash->{NAME}";
  213. syswrite($chash->{CD}, ( GHoma_BuildString($init1a) . GHoma_BuildString($init1b) ) );
  214. InternalTimer(gettimeofday()+ $timeout, "GHoma_Timer", $chash,0);
  215. $chash->{CD}->flush();
  216. return;
  217. }
  218. my $buf;
  219. my $ret = sysread($hash->{CD}, $buf, 256);
  220. if(!defined($ret)) {
  221. if($hash->{isClient}) {
  222. Log3 $name, 1, "$name \$buf nicht definiert";
  223. GHoma_ClientDisconnect($hash, 0);
  224. } else {
  225. CommandDelete(undef, $name);
  226. }
  227. return;
  228. }
  229. if ( substr($buf,0,10) eq ($prefix . $dosehb )) { # Heartbeat (Dosen Id wird nicht ueberprueft)
  230. #DevIo_SimpleWrite($hash, GHoma_BuildString($hbeat) , undef);
  231. RemoveInternalTimer($hash);
  232. $buf =~ s/(.|\n)/sprintf("%.2X ",ord($1))/eg; #empfangene Zeichen in Hexwerte wandeln
  233. Log3 $name, 5, "$name empfangen: $buf";
  234. syswrite( $hash->{CD}, GHoma_BuildString($hbeat) );
  235. Log3 $hash, 5, "$hash->{NAME} Heartbeat gesendet";
  236. InternalTimer(gettimeofday()+ $timeout, "GHoma_Timer", $hash,0);
  237. } else { # alles ausser Heartbeat
  238. my @msg = split(/$prefix/,$buf);
  239. foreach (@msg) {
  240. next if ( $_ eq "" );
  241. if ( hex(unpack('H*', substr($_,length($_)-2,2))) != hex(unpack('H*', $postfix ))) { # Check Postfix
  242. Log3 $hash, 1, "$hash->{NAME} Fehler: postfix = " . unpack('H*', substr($_,length($_)-2,2));
  243. next;
  244. }
  245. if ( hex(unpack('H*', substr($_,length($_)-3,1))) != ( 0xFF - unpack("%8c*", substr($_,2,length($_)-5) ) ) ) { # Check Checksum
  246. Log3 $hash, 1, "$hash->{NAME} Fehler: Checksum soll = " . hex(unpack('H*', substr($_,length($_)-3,1))) . " ist = ". ( 0xFF - unpack("%8c*", substr($_,2,length($_)-5) ) );
  247. next;
  248. }
  249. if ( hex(unpack('H*', substr($_,0,2))) != ( length($_) - 5 ) ) { # Check Laenge
  250. Log3 $hash, 4, "$hash->{NAME} laengesoll = " . hex(unpack('H*', substr($_,0,2))) . " laengeist = " . ( length($_) - 5 )
  251. }
  252. (my $smsg = $_) =~ s/(.|\n)/sprintf("%.2X ",ord($1))/eg; # empfangene Zeichen in Hexwerte wandeln
  253. Log3 $hash, 5, "$hash->{NAME} RX: 5A A5 $smsg"; # ...und ins Log schreiben
  254. if ( substr($_,2,6) eq ($cinit1)) { # Antwort auf erstes Init
  255. #$hash->{Id} = substr($_,8,3);
  256. $hash->{Id} = unpack('H*', substr($_,8,3) );
  257. unless ($hash->{isClient}) {
  258. # fuer Server Loesung bei erster Antwort von Dose nach bestehendem Device mit gleicher Id suchen und Verbindung auf dieses Modul uebertragen
  259. my $clientdefined = undef;
  260. foreach my $dev (devspec2array("TYPE=$hash->{TYPE}")) { # bereits bestehendes define mit dieser Id suchen
  261. if ($hash->{Id} eq InternalVal($dev,"Id","") && $hash->{NAME} ne $dev && InternalVal($dev,"TEMPORARY","") ne "1") {
  262. #Log3 $hash, 5, "$hash->{NAME}: $dev passt -> Handles uebertragen";
  263. GHoma_moveclient($hash, $defs{$dev});
  264. $clientdefined = 1;
  265. last
  266. }
  267. }
  268. unless ( defined $clientdefined) { # ...ein Neues anlegen, falls keins existiert
  269. #my $id = unpack('H*', $hash->{Id} );
  270. #Log3 $name, 4, "GHoma Unknown device $id, please define it";
  271. #DoTrigger("global", "UNDEFINED GHoma_$id GHoma $id");
  272. #GHoma_moveclient($hash, $defs{"GHoma_$id"}) if ($defs{"GHoma_$id"});
  273. Log3 $name, 4, "GHoma Unknown device $hash->{Id}, please define it";
  274. DoTrigger("global", "UNDEFINED GHoma_$hash->{Id} GHoma $hash->{Id}");
  275. GHoma_moveclient($hash, $defs{"GHoma_$hash->{Id}"}) if ($defs{"GHoma_$hash->{Id}"});
  276. }
  277. } else {
  278. readingsSingleUpdate($hash, "state", "Initialize...", 1);
  279. syswrite( $hash->{CD}, GHoma_BuildString($init2) );
  280. RemoveInternalTimer($hash);
  281. InternalTimer(gettimeofday()+ $timeout, "GHoma_Timer", $hash,0);
  282. }
  283. } elsif ( substr($_,2,6) eq $cmac && substr($_,8,3) eq substr($_,17,3) ) { # Nachricht mit MAC (kommt unter Anderem als Antwort auf Init2)
  284. my $mac;
  285. for my $i (0...5) { # MAC formattieren
  286. $mac .= sprintf("%.2X",ord( substr($_,14+$i,1) ));
  287. last if $i == 5;
  288. $mac .= ":";
  289. }
  290. $hash->{MAC} = $mac;
  291. } elsif ( substr($_,2,6) eq $cswitch && (( length($_) - 5 ) == 0x15 ) ) { # An oder Aus
  292. my $id = unpack('H*', substr($_,8,3) );
  293. my $rstate = hex(unpack('H*', substr($_,22,1))) == 0xFF ? "on" : "off";
  294. my $src = hex(unpack('H*', substr($_,14,1))) == 0x81 ? "local" : "remote";
  295. if ( defined $hash->{LASTSTATE} && $hash->{STATE} eq "Initialize..." ) { # wenn dies erste Statusbotschaft nach Anmeldung
  296. my $nstate = AttrVal($name, "restoreOnReinit", "last");
  297. if ( $nstate ne "last" && $nstate ne $rstate ) {
  298. GHoma_Set( $hash, $hash->{NAME}, $nstate );
  299. } elsif ($nstate eq "last" && $hash->{LASTSTATE} ne $rstate) {
  300. GHoma_Set( $hash, $hash->{NAME}, $hash->{LASTSTATE} );
  301. }
  302. } elsif ($src eq "local") { # bei schalten direkt an Steckdose soll...
  303. if (AttrVal($name, "blocklocal", "no") eq "yes") { # ...wieder zurueckgeschaltet werden, wenn Attribut blocklocal yes ist
  304. GHoma_Set($hash, $hash->{NAME}, $hash->{LASTSTATE});
  305. } else { # ...laststate angepasst werden (um bei reinit richtigen wert zu haben)
  306. $hash->{LASTSTATE} = $rstate;
  307. }
  308. }
  309. if (defined $hash->{SNAME} && defined $defs{$hash->{SNAME}} ) { # Readings auch im Server eintragen
  310. readingsBeginUpdate($defs{$hash->{SNAME}});
  311. readingsBulkUpdate($defs{$hash->{SNAME}}, $id .'_state', $rstate);
  312. readingsBulkUpdate($defs{$hash->{SNAME}}, $id .'_source', $src);
  313. readingsEndUpdate($defs{$hash->{SNAME}}, 1);
  314. }
  315. readingsBeginUpdate($hash);
  316. readingsBulkUpdate($hash, 'state', $rstate);
  317. readingsBulkUpdate($hash, 'source', $src);
  318. readingsEndUpdate($hash, 1);
  319. }
  320. }
  321. }
  322. #Log3 $name, 5, "$name empfangen: $buf";
  323. return
  324. }
  325. #####################################
  326. sub GHoma_Timer($) { # wird ausgeloest wenn heartbeat nicht mehr kommt
  327. my ($hash) = @_;
  328. Log3 $hash, 3, "$hash->{NAME}: Timer abgelaufen";
  329. readingsSingleUpdate($hash, "state", "offline", 1);
  330. GHoma_ClientDisconnect($hash, 0) if $hash->{isClient};
  331. return TcpServer_Close($hash) if defined $hash->{FD};
  332. #DevIo_Disconnected($hash);
  333. }
  334. #####################################
  335. sub GHoma_Attr(@) { #
  336. my ($command, $name, $attr, $val) = @_;
  337. my $hash = $defs{$name};
  338. # if($a[0] eq "set" && $a[2] eq "SSL") {
  339. # TcpServer_SetSSL($hash);
  340. # if($hash->{CD}) {
  341. # my $ret = IO::Socket::SSL->start_SSL($hash->{CD});
  342. # Log3 $a[1], 1, "$hash->{NAME} start_SSL: $ret" if($ret);
  343. # }
  344. # }
  345. return undef;
  346. }
  347. #####################################
  348. sub GHoma_Set($@) { #
  349. my ($hash, @a) = @_;
  350. my $name = $a[0];
  351. my $type = $a[1];
  352. return "Unknown argument $type, choose one of ConfigAll" unless (defined $hash->{Id} || $type eq "ConfigAll"); # set fuer den Server
  353. my @sets = ('on:noArg', 'off:noArg');
  354. my $status = ReadingsVal($hash->{NAME},"state","");
  355. if($type eq "ConfigAll") {
  356. GHoma_udpbroad($hash, defined $a[2] ? $a[2] : undef);
  357. } elsif($type eq "on") {
  358. $type = pack('C*', (0xff));
  359. readingsSingleUpdate($hash, "state", "set_on", 1) if ( $status =~ m/([set_]?o[n|ff])$/i );
  360. $hash->{LASTSTATE} = "on";
  361. } elsif($type eq "off") {
  362. $type = pack('C*', (0x00));
  363. readingsSingleUpdate($hash, "state", "set_off", 1) if ( $status =~ m/([set_]?o[n|ff])$/i );
  364. $hash->{LASTSTATE} = "off";
  365. } else {
  366. my $slist = join(' ', @sets);
  367. return SetExtensions($hash, $slist, @a);
  368. }
  369. if (defined $hash->{CD}) {
  370. syswrite( $hash->{CD}, GHoma_BuildString($switch1 . pack('C*', ( hex(substr($hash->{Id},0,2)), hex(substr($hash->{Id},2,2)), hex(substr($hash->{Id},4,2)) ) ) . $switch2 . $type) );
  371. }
  372. return undef;
  373. }
  374. #####################################
  375. sub GHoma_State($$$$) { # reload readings at FHEM start
  376. my ($hash, $tim, $sname, $sval) = @_;
  377. Log3 $hash, 4, "$hash->{NAME}: $sname kann auf $sval wiederhergestellt werden $tim";
  378. if ( $sname eq "state" && defined $hash->{Id} ) { #wenn kein Server
  379. $hash->{LASTSTATE} = $sval;
  380. readingsSingleUpdate($hash, "state", "offline", 1)
  381. }
  382. return;
  383. }
  384. #####################################
  385. sub GHoma_Undef($$) { #
  386. my ($hash, $arg) = @_;
  387. RemoveInternalTimer($hash);
  388. return TcpServer_Close($hash) if defined $hash->{FD};
  389. }
  390. #####################################
  391. sub GHoma_udpbroad {
  392. eval "use IO::Socket::INET;";
  393. return "please install IO::Socket::INET" if($@);
  394. my ($hash, $ownIP) = @_;
  395. # flush after every write
  396. $| = 1;
  397. my ($socket,$data);
  398. $socket = new IO::Socket::INET (
  399. PeerAddr => '255.255.255.255',
  400. PeerPort => '48899',
  401. Proto => 'udp',
  402. Broadcast => 1
  403. ) or die "ERROR in Socket Creation : $!\n";
  404. #send operation
  405. unless (defined $ownIP) {
  406. my $ownIPl = `hostname -I`;
  407. 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;
  408. $ownIP = $ownIPs[0];
  409. } else {
  410. 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]+)*))*$)$/
  411. }
  412. Log3 $hash, 1, "$hash->{NAME}: setting server address for GHoma plugs to $ownIP:$hash->{PORT}";
  413. my @sdata = (
  414. "HF-A11ASSISTHREAD",
  415. "+ok",
  416. "AT+NETP=TCP,Client,$hash->{PORT},$ownIP\r",
  417. "AT+TCPTO=120\r"
  418. );
  419. foreach (@sdata) {
  420. $socket->send($_);
  421. Log3 $hash, 1, "$hash->{NAME}: sende Multicast: $_";
  422. }
  423. $socket->close();
  424. }
  425. 1;
  426. =pod
  427. =item device
  428. =item summary controls an G-Homa wlan adapter plug
  429. =item summary_DE Steuerung einer G-Homa Wlan Steckdose
  430. =begin html
  431. <a name="GHoma"></a>
  432. <h3>GHoma</h3>
  433. (en | <a href="commandref_DE.html#GHoma">de</a>)
  434. <ul>
  435. <ul>
  436. Connects fhem to an G-Homa adapter plug<br><br>
  437. <b>preliminary:</b><br>
  438. <li>Configure WLAN settings (Firmware <= 1.06):<br>
  439. bring device in AP mode (press button for more than 3s, repeat this step until the LED is permanently on)<br>
  440. Now connect with your computer to G-Home network.<br>
  441. Browse to 10.10.100.254 (username:password = admin:admin)<br>
  442. In STA Setting insert your WLAN settings<br>
  443. </li>
  444. <li>Configure WLAN settings:<br>
  445. bring device in AP mode (press button for more than 3s, repeat this step until the LED is permanently on)<br>
  446. Configure WLAN with G-Homa App.<br>
  447. </li>
  448. <li>Configure Network Parameters setting (Firmware <= 1.06):<br>
  449. Other Setting -> Protocol to TCP-Client<br>
  450. Other Setting -> Port ID (remember value for FHEM settings)<br>
  451. Other Setting -> Server Address (IP of your FHEM Server)<br>
  452. </li>
  453. <li>Configure Network Parameters settings:<br>
  454. Use <code>set ... ConfigAll</code> from server device to set parameters automaticly.<br>
  455. </li>
  456. <li>Optional:<br>
  457. Block all outgoing connections for G-Homa in your router.<br>
  458. </li>
  459. </ul>
  460. <br><br>
  461. <a name="GHomadefine"></a>
  462. <b>Define</b><br>
  463. <ul>
  464. <code>define &lt;name&gt; GHoma &lt;port&gt;</code> <br>
  465. Specifies the GHoma server device.<br>
  466. New adapters will be added automaticaly after first connection.<br>
  467. You can also manyally add an adapter:<br>
  468. <code>define &lt;name&gt; GHoma &lt;Id&gt;</code> <br>
  469. where <code>Id</code> is the last 6 numbers of the plug's MAC address<br>
  470. Example: MAC= AC:CF:23:A5:E2:3B -> Id= A5E23B<br>
  471. <br>
  472. </ul>
  473. <a name="GHomaset"></a>
  474. <b>Set</b>
  475. <ul>
  476. <code>set &lt;name&gt; &lt;value&gt;</code>
  477. <br><br>
  478. where <code>value</code> is one of:<br>
  479. <ul><code>
  480. off<br>
  481. on<br>
  482. </code>
  483. </ul>
  484. The <a href="#setExtensions"> set extensions</a> are also supported.<br>
  485. <br>
  486. For server device:
  487. <code>set &lt;name&gt; ConfigAll [IP|hostname|FQDN]</code><br>
  488. Setting all GHoma plugs via UDP broadcast to TCP client of FHEM servers address and port of GHoma server device.<br>
  489. </ul>
  490. <a name="GHomaattr"></a>
  491. <b>Attributes</b><br>
  492. <ul>
  493. For plug devices:
  494. <ul><li>restoreOnStartup<br>
  495. Restore switch state after reboot<br>
  496. Default: last, valid values: last, on, off<br><br>
  497. </li>
  498. <li>restoreOnReinit<br>
  499. Restore switch state after reconnect<br>
  500. Default: last, valid values: last, on, off<br><br>
  501. </li>
  502. <li>blocklocal<br>
  503. Restore switch state to reading state immideately after local switching<br>
  504. Default: no, valid values: no, yes<br><br>
  505. </li></ul>
  506. For server devices:
  507. <ul><li>allowfrom<br>
  508. Regexp of allowed ip-addresses or hostnames. If set,
  509. only connections from these addresses are allowed.<br><br>
  510. </li></ul>
  511. <li><a href="#readingFnAttributes">readingFnAttributes</a></li>
  512. </ul>
  513. <br>
  514. </ul>
  515. =end html
  516. =begin html_DE
  517. <a name="GHoma"></a>
  518. <h3>GHoma</h3>
  519. (<a href="commandref.html#GHoma">en</a> | de)
  520. <ul>
  521. <ul>
  522. Verbindet fhem mit einem G-Homa Zwischenstecker<br><br>
  523. <b>Vorbereitung:</b><br>
  524. <li>WLAN konfigurieren (bis Firmware 1.06):<br>
  525. Ger&auml;t in den AP modus bringen (Knopf f&uuml;r mehr als 3s dr&uuml;cken, diesen Schritt wiederholen bis die LED permanent leuchtet)<br>
  526. Nun einen Computer mit der SSID G-Home verbinden.<br>
  527. Im Browser zu 10.10.100.254 (username:passwort = admin:admin)<br>
  528. In STA Setting WLAN Einstellungen eintragen<br>
  529. </li>
  530. <li>WLAN konfigurieren:<br>
  531. Ger&auml;t in den AP modus bringen (Knopf f&uuml;r mehr als 3s dr&uuml;cken, diesen Schritt wiederholen bis die LED permanent leuchtet)<br>
  532. Mit der G-Homa App das WLAN des Zwischensteckers einstellen<br>
  533. </li>
  534. <li>Network Parameters settings (bis Firmware 1.06):<br>
  535. Other Setting -> Protocol auf TCP-Server<br>
  536. Other Setting -> Port ID (wird sp&auml;ter f&uuml;r FHEM ben&ouml;tigt)<br>
  537. Other Setting -> Server Address (IP Adresse des FHEM Servers)<br>
  538. </li>
  539. <li>Network Parameters settings:<br>
  540. &Uuml;ber <code>set ... ConfigAll</code> des Server Ger&auml;tes die Parameter automatisch setzen.<br>
  541. </li>
  542. <li>Optional:<br>
  543. Im Router alle ausgehenden Verbindungen f&uuml;r G-Homa blockieren.<br>
  544. </li>
  545. </ul>
  546. <br><br>
  547. <a name="GHomadefine"></a>
  548. <b>Define</b><br>
  549. <ul>
  550. <code>define &lt;name&gt; GHoma &lt;port&gt;</code> <br>
  551. Legt ein GHoma Server device an.<br>
  552. Neue Zwischenstecker werden beim ersten verbinden automatisch angelegt.<br>
  553. Diese k&ouml;nnen aber auch manuell angelegt werden:<br>
  554. <code>define &lt;name&gt; GHoma &lt;Id&gt;</code> <br>
  555. Die <code>Id</code> besteht aus den letzten 6 Stellen der MAC Adresse des Zwischensteckers.<br>
  556. Beispiel: MAC= AC:CF:23:A5:E2:3B -> Id= A5E23B<br>
  557. <br>
  558. </ul>
  559. <a name="GHomaset"></a>
  560. <b>Set</b>
  561. <ul>
  562. <code>set &lt;name&gt; &lt;value&gt;</code>
  563. <br><br>
  564. G&uuml;ltige Werte f&uuml;r <code>value</code>:<br>
  565. <ul><code>
  566. off<br>
  567. on<br>
  568. </code>
  569. </ul>
  570. Die <a href="#setExtensions"> set extensions</a> werden auch unterst&uuml;tzt.<br>
  571. <br>
  572. F&uuml;r Server Device:
  573. <code>set &lt;name&gt; ConfigAll [IP|hostname|FQDN]</code><br>
  574. Einstellen aller GHoma Zwischenstecker &uuml;ber UDP broadcast auf TCP client mit FHEM Server Adresse und Port des GHoma Server Devices.<br>
  575. </ul>
  576. <a name="GHomaattr"></a>
  577. <b>Attributes</b><br>
  578. <ul>
  579. F&uuml;r Zwischenstecker devices:
  580. <ul><li>restoreOnStartup<br>
  581. Wiederherstellen der Portzust&auml;nde nach Neustart<br>
  582. Standard: last, g&uuml;ltige Werte: last, on, off<br><br>
  583. </li>
  584. <li>restoreOnReinit<br>
  585. Wiederherstellen der Portzust&auml;nde nach Neustart<br>
  586. Standard: last, g&uuml;ltige Werte: last, on, off<br><br>
  587. </li>
  588. <li>blocklocal<br>
  589. Wert im Reading State sofort nach &Auml;nderung &uuml;ber lokale Taste wiederherstellen<br>
  590. Standard: no, g&uuml;ltige Werte: no, yes<br><br>
  591. </li></ul>
  592. F&uuml;r Server devices:
  593. <ul><li>allowfrom<br>
  594. Regexp der erlaubten IP-Adressen oder Hostnamen. Wenn dieses Attribut
  595. gesetzt wurde, werden ausschlie&szlig;lich Verbindungen von diesen
  596. Adressen akzeptiert.<br><br>
  597. </li></ul>
  598. <li><a href="#readingFnAttributes">readingFnAttributes</a></li>
  599. </ul>
  600. <br>
  601. </ul>
  602. =end html_DE
  603. =cut