36_LaCrosseGateway.pm 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829
  1. # $Id: 36_LaCrosseGateway.pm 12990 2017-01-06 19:34:30Z HCS $
  2. package main;
  3. use strict;
  4. use warnings;
  5. use Time::HiRes qw(gettimeofday);
  6. use Time::Local;
  7. my $clients = ":PCA301:EC3000:LaCrosse:Level:EMT7110:KeyValueProtocol";
  8. my %matchList = (
  9. "1:PCA301" => "^\\S+\\s+24",
  10. "2:EC3000" => "^\\S+\\s+22",
  11. "3:LaCrosse" => "^(\\S+\\s+9 |OK\\sWS\\s)",
  12. "4:EMT7110" => "^OK\\sEMT7110\\s",
  13. "5:Level" => "^OK\\sLS\\s",
  14. "6:KeyValueProtocol" => "^OK\\sVALUES\\s",
  15. );
  16. sub LaCrosseGateway_Initialize($) {
  17. my ($hash) = @_;
  18. require "$attr{global}{modpath}/FHEM/DevIo.pm";
  19. $hash->{ReadFn} = "LaCrosseGateway_Read";
  20. $hash->{WriteFn} = "LaCrosseGateway_Write";
  21. $hash->{ReadyFn} = "LaCrosseGateway_Ready";
  22. $hash->{DefFn} = "LaCrosseGateway_Define";
  23. $hash->{FingerprintFn} = "LaCrosseGateway_Fingerprint";
  24. $hash->{UndefFn} = "LaCrosseGateway_Undef";
  25. $hash->{SetFn} = "LaCrosseGateway_Set";
  26. $hash->{AttrFn} = "LaCrosseGateway_Attr";
  27. $hash->{AttrList} = " Clients"
  28. ." MatchList"
  29. ." dummy"
  30. ." initCommands"
  31. ." timeout"
  32. ." watchdog"
  33. ." disable:0,1"
  34. ." tftFile"
  35. ." kvp:dispatch,readings,both"
  36. ." ownSensors:dispatch,readings,both"
  37. ." mode:USB,WiFi,Cable"
  38. ." usbFlashCommand"
  39. ." $readingFnAttributes";
  40. }
  41. #=======================================================================================
  42. sub LaCrosseGateway_Fingerprint($$) {
  43. }
  44. #=======================================================================================
  45. sub LaCrosseGateway_Define($$) {
  46. my ($hash, $def) = @_;
  47. my @a = split("[ \t][ \t]*", $def);
  48. if(@a != 3) {
  49. my $msg = "wrong syntax: define <name> LaCrosseGateway {none | devicename[\@baudrate] | devicename\@directio | hostname:port}";
  50. Log3 undef, 2, $msg;
  51. return $msg;
  52. }
  53. DevIo_CloseDev($hash);
  54. my $name = $a[0];
  55. my $dev = $a[2];
  56. $hash->{Clients} = $clients;
  57. $hash->{MatchList} = \%matchList;
  58. $hash->{TIMEOUT} = 0.5;
  59. if( !defined( $attr{$name}{usbFlashCommand} ) ) {
  60. $attr{$name}{usbFlashCommand} = "./FHEM/firmware/esptool.py -b 921600 -p [PORT] write_flash -ff 80m -fm dio -fs 4MB-c1 0x00000 [BINFILE] > [LOGFILE]"
  61. }
  62. if($dev eq "none") {
  63. Log3 $name, 1, "$name device is none, commands will be echoed only";
  64. $attr{$name}{dummy} = 1;
  65. return undef;
  66. }
  67. $dev .= "\@57600" if( $dev !~ m/\@/ && $def !~ m/:/ );
  68. $hash->{DeviceName} = $dev;
  69. my $ret = LaCrosseGateway_Connect($hash);
  70. return $ret;
  71. }
  72. #=======================================================================================
  73. sub LaCrosseGateway_Undef($$) {
  74. my ($hash, $arg) = @_;
  75. my $name = $hash->{NAME};
  76. foreach my $d (sort keys %defs) {
  77. if(defined($defs{$d}) && defined($defs{$d}{IODev}) && $defs{$d}{IODev} == $hash) {
  78. my $lev = ($reread_active ? 4 : 2);
  79. Log3 $name, $lev, "deleting port for $d";
  80. delete $defs{$d}{IODev};
  81. }
  82. }
  83. DevIo_CloseDev($hash);
  84. return undef;
  85. }
  86. #=======================================================================================
  87. sub LaCrosseGateway_RemoveLaCrossePair($) {
  88. my $hash = shift;
  89. delete($hash->{LaCrossePair});
  90. }
  91. #=======================================================================================
  92. sub LaCrosseGateway_Set($@) {
  93. my ($hash, @a) = @_;
  94. my $name = shift @a;
  95. my $cmd = shift @a;
  96. my $arg = join(" ", @a);
  97. my $list = "raw connect LaCrossePairForSec flash nextionUpload parse reboot";
  98. return $list if( $cmd eq '?' || $cmd eq '');
  99. if ($cmd eq "raw") {
  100. Log3 $name, 4, "set $name $cmd $arg";
  101. LaCrosseGateway_SimpleWrite($hash, $arg);
  102. }
  103. elsif ($cmd eq "flash") {
  104. my @args = split(' ', $arg);
  105. my $log = "";
  106. my @deviceName = split('@', $hash->{DeviceName});
  107. my $port = $deviceName[0];
  108. my $logFile = AttrVal("global", "logdir", "./log") . "/LaCrosseGatewayFlash.log";
  109. my $hexFile = "./FHEM/firmware/JeeLink_LaCrosseGateway.bin";
  110. return "The file '$hexFile' does not exist" if(!-e $hexFile);
  111. $log .= "flashing LaCrosseGateway $name\n";
  112. $log .= "hex file: $hexFile\n";
  113. if(AttrVal($name, "mode", "WiFi") eq "WiFi") {
  114. eval "use LWP::UserAgent";
  115. return "\nERROR: Please install LWP::UserAgent" if($@);
  116. eval "use HTTP::Request::Common";
  117. return "\nERROR: Please install HTTP::Request::Common" if($@);
  118. $log .= "Mode is LaCrosseGateway OTA-update\n";
  119. DevIo_CloseDev($hash);
  120. readingsSingleUpdate($hash, "state", "disconnected", 1);
  121. $log .= "$name closed\n";
  122. my @spl = split(':', $hash->{DeviceName});
  123. my $targetIP = $spl[0];
  124. my $targetURL = "http://" . $targetIP . "/ota/firmware.bin";
  125. $log .= "target: $targetURL\n";
  126. my $request = POST($targetURL, Content_Type => 'multipart/form-data', Content => [ file => [$hexFile, "firmware.bin"] ]);
  127. my $userAgent = LWP::UserAgent->new;
  128. $userAgent->timeout(60);
  129. my $response = $userAgent->request($request);
  130. if ($response->is_success) {
  131. $log .= "\n\nSketch reports:\n";
  132. $log .= $response->decoded_content;
  133. }
  134. else {
  135. $log .= "\nERROR: " . $response->code . " " . $response->decoded_content;
  136. }
  137. }
  138. else {
  139. $log .= "Mode is LaCrosseGateway USB-update\n";
  140. my $usbFlashCommand = AttrVal($name, "usbFlashCommand", "");
  141. if ($usbFlashCommand ne "") {
  142. if (-e $logFile) {
  143. unlink $logFile;
  144. }
  145. my $command = $usbFlashCommand;
  146. $command =~ s/\Q[PORT]\E/$port/g;
  147. $command =~ s/\Q[BINFILE]\E/$hexFile/g;
  148. $command =~ s/\Q[LOGFILE]\E/$logFile/g;
  149. $log .= "command: $command\n\n";
  150. `$command`;
  151. local $/ = undef;
  152. if (-e $logFile) {
  153. open FILE, $logFile;
  154. my $logText = <FILE>;
  155. close FILE;
  156. $log .= "--- esptool ---------------------------------------------------------------------------------\n";
  157. $log .= $logText;
  158. $log .= "--- esptool ---------------------------------------------------------------------------------\n\n";
  159. }
  160. else {
  161. $log .= "WARNING: esptool created no log file\n\n";
  162. }
  163. }
  164. else {
  165. $log .= "\n\nNo usbFlashCommand found. Please define this attribute.\n\n";
  166. }
  167. }
  168. LaCrosseGateway_Connect($hash);
  169. $log .= "$name opened\n";
  170. return $log;
  171. }
  172. elsif ($cmd eq "nextionUpload") {
  173. my $log = "";
  174. my @deviceName = split('@', $hash->{DeviceName});
  175. my $port = $deviceName[0];
  176. my $logFile = AttrVal("global", "logdir", "./log") . "/NextionUpload.log";
  177. my $tftFile = AttrVal($name, "tftFile", "./FHEM/firmware/nextion.tft");
  178. return "The file '$tftFile' does not exist" if(!-e $tftFile);
  179. $log .= "upload Nextion firmware to $name\n";
  180. $log .= "tft file: $tftFile\n";
  181. eval "use LWP::UserAgent";
  182. return "\nERROR: Please install LWP::UserAgent" if($@);
  183. eval "use HTTP::Request::Common";
  184. return "\nERROR: Please install HTTP::Request::Common" if($@);
  185. my @spl = split(':', $hash->{DeviceName});
  186. my $targetIP = $spl[0];
  187. my $targetURL = "http://" . $targetIP . "/ota/nextion";
  188. $log .= "target: $targetURL\n";
  189. my $request = POST($targetURL, Content_Type => 'multipart/form-data', Content => [ file => [$tftFile, "nextion.tft"] ]);
  190. my $userAgent = LWP::UserAgent->new;
  191. $userAgent->timeout(300);
  192. my $response = $userAgent->request($request);
  193. if ($response->is_success) {
  194. $log .= "\n\nLGW reports:\n";
  195. $log .= $response->decoded_content;
  196. }
  197. else {
  198. $log .= "\nERROR: " . $response->code . " " . $response->decoded_content;
  199. }
  200. return $log;
  201. }
  202. elsif ($cmd eq "LaCrossePairForSec") {
  203. my @args = split(' ', $arg);
  204. return "Usage: set $name LaCrossePairForSec <seconds_active> [ignore_battery]" if(!$arg || $args[0] !~ m/^\d+$/ || ($args[1] && $args[1] ne "ignore_battery") );
  205. $hash->{LaCrossePair} = $args[1]?2:1;
  206. InternalTimer(gettimeofday()+$args[0], "LaCrosseGateway_RemoveLaCrossePair", $hash, 0);
  207. }
  208. elsif ($cmd eq "connect") {
  209. DevIo_CloseDev($hash);
  210. return LaCrosseGateway_Connect($hash);
  211. }
  212. elsif ($cmd eq "reboot") {
  213. if(AttrVal($name, "mode", "WiFi") eq "WiFi") {
  214. LaCrosseGateway_SimpleWrite($hash, "8377e\n");
  215. }
  216. else {
  217. my $po = $hash->{USBDev};
  218. $po->rts_active(0);
  219. $po->dtr_active(0);
  220. select undef, undef, undef, 0.01;
  221. $po->rts_active(1);
  222. $po->dtr_active(1);
  223. }
  224. }
  225. elsif ($cmd eq "parse") {
  226. LaCrosseGateway_Parse($hash, $hash, $name, $arg);
  227. }
  228. else {
  229. return "Unknown argument $cmd, choose one of ".$list;
  230. }
  231. return undef;
  232. }
  233. #=======================================================================================
  234. sub LaCrosseGateway_OnInitTimer($) {
  235. my ($hash) = @_;
  236. my $name = $hash->{NAME};
  237. LaCrosseGateway_SimpleWrite($hash, "v\n");
  238. }
  239. #=======================================================================================
  240. sub LaCrosseGateway_DoInit($) {
  241. my $hash = shift;
  242. my $name = $hash->{NAME};
  243. my $enabled = AttrVal($name, "disable", "0") != "1";
  244. if($enabled) {
  245. readingsSingleUpdate($hash, "state", "opened", 1);
  246. if(AttrVal($name, "mode", "") ne "USB") {
  247. InternalTimer(gettimeofday() +3, "LaCrosseGateway_OnInitTimer", $hash, 1);
  248. }
  249. }
  250. else {
  251. readingsSingleUpdate($hash, "state", "disabled", 1);
  252. }
  253. return undef;
  254. }
  255. #=======================================================================================
  256. sub LaCrosseGateway_Ready($) {
  257. my ($hash) = @_;
  258. my $name = $hash->{NAME};
  259. LaCrosseGateway_Connect($hash, 1);
  260. # This is relevant for windows/USB only
  261. my $po = $hash->{USBDev};
  262. my ($BlockingFlags, $InBytes, $OutBytes, $ErrorFlags);
  263. if($po) {
  264. ($BlockingFlags, $InBytes, $OutBytes, $ErrorFlags) = $po->status;
  265. }
  266. return ($InBytes && $InBytes>0);
  267. }
  268. #=======================================================================================
  269. sub LaCrosseGateway_Write($$) {
  270. my ($hash, $cmd, $msg) = @_;
  271. my $name = $hash->{NAME};
  272. my $arg = $cmd;
  273. $arg .= " " . $msg if(defined($msg));
  274. LaCrosseGateway_SimpleWrite($hash, $arg);
  275. }
  276. #=======================================================================================
  277. sub LaCrosseGateway_Read($) {
  278. my ($hash) = @_;
  279. my $name = $hash->{NAME};
  280. my $buf = DevIo_SimpleRead($hash);
  281. return "" if(!defined($buf));
  282. my $data = $hash->{PARTIAL};
  283. $data .= $buf;
  284. while($data =~ m/\n/) {
  285. my $rmsg;
  286. ($rmsg,$data) = split("\n", $data, 2);
  287. $rmsg =~ s/\r//;
  288. LaCrosseGateway_Parse($hash, $hash, $name, $rmsg) if($rmsg);
  289. }
  290. $hash->{PARTIAL} = $data;
  291. }
  292. #=======================================================================================
  293. sub LaCrosseGateway_DeleteKVPReadings($) {
  294. my ($hash) = @_;
  295. delete $hash->{READINGS}{"FramesPerMinute"};
  296. delete $hash->{READINGS}{"RSSI"};
  297. delete $hash->{READINGS}{"UpTime"};
  298. }
  299. #=======================================================================================
  300. sub LaCrosseGateway_HandleKVP($$) {
  301. my ($hash, $kvp) = @_;
  302. readingsBeginUpdate($hash);
  303. if($kvp =~ m/UpTimeText=(.*?)(\,|\ ,)/) {
  304. readingsBulkUpdate($hash, "UpTime", $1);
  305. }
  306. if($kvp =~ m/RSSI=(.*?)(\,|\ ,)/) {
  307. readingsBulkUpdate($hash, "RSSI", $1);
  308. }
  309. if($kvp =~ m/FramesPerMinute=(.*?)(\,|\ ,)/) {
  310. readingsBulkUpdate($hash, "FramesPerMinute", $1);
  311. }
  312. readingsEndUpdate($hash, 1);
  313. }
  314. #=======================================================================================
  315. sub LaCrosseGateway_DeleteOwnSensorsReadings($) {
  316. my ($hash) = @_;
  317. delete $hash->{READINGS}{"temperature"};
  318. delete $hash->{READINGS}{"humidity"};
  319. delete $hash->{READINGS}{"pressure"};
  320. }
  321. #=======================================================================================
  322. sub LaCrosseGateway_HandleOwnSensors($$) {
  323. my ($hash, $data) = @_;
  324. readingsBeginUpdate($hash);
  325. my @bytes = split( ' ', substr($data, 5) );
  326. return "" if(@bytes < 14);
  327. my $temperature = undef;
  328. my $humidity = undef;
  329. my $pressure = undef;
  330. if($bytes[2] != 0xFF) {
  331. $temperature = ($bytes[2]*256 + $bytes[3] - 1000)/10;
  332. readingsBulkUpdate($hash, "temperature", $temperature);
  333. }
  334. if($bytes[4] != 0xFF) {
  335. $humidity = $bytes[4];
  336. readingsBulkUpdate($hash, "humidity", $humidity);
  337. }
  338. if(@bytes > 15 && $bytes[14] != 0xFF) {
  339. $pressure = $bytes[14] * 256 + $bytes[15];
  340. readingsBulkUpdate($hash, "pressure", $pressure);
  341. }
  342. readingsEndUpdate($hash, 1);
  343. delete $hash->{READINGS}{"temperature"} if !$temperature;
  344. delete $hash->{READINGS}{"humidity"} if !$humidity;
  345. delete $hash->{READINGS}{"pressure"} if !$pressure;
  346. }
  347. #=======================================================================================
  348. sub LaCrosseGateway_HandleAnalogData($$) {
  349. my ($hash, $data) = @_;
  350. if ($data =~ m/^LGW ANALOG /) {
  351. readingsBeginUpdate($hash);
  352. my @bytes = split( ' ', substr($data, 10) );
  353. return "" if(@bytes < 2);
  354. my $value = $bytes[0]*256 + $bytes[1];
  355. readingsBulkUpdate($hash, "analog", $value);
  356. readingsEndUpdate($hash, 1);
  357. }}
  358. #=======================================================================================
  359. sub LaCrosseGateway_Parse($$$$) {
  360. my ($hash, $iohash, $name, $msg) = @_;
  361. next if (!$msg || length($msg) < 1);
  362. return if ($msg =~ m/^\*\*\*CLEARLOG/);
  363. return if ($msg =~ m/[^\x20-\x7E]/);
  364. if ($msg =~ m/^LGW/) {
  365. if ($msg =~ /ALIVE/) {
  366. $hash->{Alive} = TimeNow();
  367. }
  368. LaCrosseGateway_HandleAnalogData($hash, $msg);
  369. return;
  370. }
  371. if($msg =~ m/^\[LaCrosseITPlusReader.Gateway/ ) {
  372. $hash->{model} = $msg;
  373. my $attrVal = AttrVal($name, "timeout", undef);
  374. if(defined($attrVal)) {
  375. my ($timeout, $interval) = split(',', $attrVal);
  376. if (!$interval) {
  377. $hash->{Alive} = TimeNow();
  378. }
  379. }
  380. if (ReadingsVal($name, "state", "") eq "opened") {
  381. if (my $initCommandsString = AttrVal($name, "initCommands", undef)) {
  382. my @initCommands = split(' ', $initCommandsString);
  383. foreach my $command (@initCommands) {
  384. LaCrosseGateway_SimpleWrite($hash, $command);
  385. }
  386. }
  387. readingsSingleUpdate($hash, "state", "initialized", 1);
  388. }
  389. return;
  390. }
  391. $hash->{"${name}_MSGCNT"}++;
  392. $hash->{"${name}_TIME"} = TimeNow();
  393. readingsSingleUpdate($hash, "state", $hash->{READINGS}{state}{VAL}, 0);
  394. $hash->{RAWMSG} = $msg;
  395. if($msg =~ m/^OK WS \d{1,3} 4 /) {
  396. my $osa = AttrVal($name, "ownSensors", "dispatch");
  397. if($osa eq "readings") {
  398. LaCrosseGateway_HandleOwnSensors($hash, $msg);
  399. return;
  400. }
  401. elsif ($osa eq "both") {
  402. LaCrosseGateway_HandleOwnSensors($hash, $msg);
  403. }
  404. else {
  405. LaCrosseGateway_DeleteOwnSensorsReadings($hash);
  406. }
  407. }
  408. if($msg =~ m/^OK VALUES LGW/) {
  409. my $osa = AttrVal($name, "kvp", "dispatch");
  410. if($osa eq "readings") {
  411. LaCrosseGateway_HandleKVP($hash, $msg);
  412. return;
  413. }
  414. elsif ($osa eq "both") {
  415. LaCrosseGateway_HandleKVP($hash, $msg);
  416. }
  417. else {
  418. LaCrosseGateway_DeleteKVPReadings($hash);
  419. }
  420. return if AttrVal($name, "dispatchKVP", "1") ne "1";
  421. }
  422. Dispatch($hash, $msg, "");
  423. }
  424. #=======================================================================================
  425. sub LaCrosseGateway_SimpleWrite(@) {
  426. my ($hash, $msg, $nocr) = @_;
  427. return if(!$hash);
  428. my $name = $hash->{NAME};
  429. Log3 $name, 5, "SW: $msg";
  430. $msg .= "\n" unless($nocr);
  431. $hash->{USBDev}->write($msg) if($hash->{USBDev});
  432. syswrite($hash->{TCPDev}, $msg) if($hash->{TCPDev});
  433. syswrite($hash->{DIODev}, $msg) if($hash->{DIODev});
  434. # Some linux installations are broken with 0.001, T01 returns no answer
  435. select(undef, undef, undef, 0.01);
  436. }
  437. #=======================================================================================
  438. sub LaCrosseGateway_Connect($;$) {
  439. my ($hash, $mode) = @_;
  440. my $name = $hash->{NAME};
  441. $mode = 0 if!($mode);
  442. my $enabled = AttrVal($name, "disable", "0") != "1";
  443. if($enabled) {
  444. my $ret = DevIo_OpenDev($hash, $mode, "LaCrosseGateway_DoInit");
  445. return $ret;
  446. }
  447. return undef;
  448. }
  449. #=======================================================================================
  450. sub LaCrosseGateway_TriggerWatchdog($) {
  451. my ($hash) = @_;
  452. my $name = $hash->{NAME};
  453. my $watchDog = "";
  454. my $watchDogAttribute = AttrVal($name, "watchdog", undef);
  455. if($watchDogAttribute) {
  456. $watchDog = "=$watchDogAttribute"
  457. }
  458. my $command = "\"WATCHDOG Ping$watchDog\"";
  459. LaCrosseGateway_SimpleWrite($hash, $command);
  460. }
  461. #=======================================================================================
  462. sub LaCrosseGateway_OnConnectTimer($) {
  463. my ($hash) = @_;
  464. my $name = $hash->{NAME};
  465. RemoveInternalTimer($hash, "LaCrosseGateway_OnConnectTimer");
  466. my $attrVal = AttrVal($name, "timeout", undef);
  467. if(defined($attrVal)) {
  468. my ($timeout, $interval) = split(',', $attrVal);
  469. my $useOldMethod = $interval;
  470. $interval = $timeout if !$interval;
  471. InternalTimer(gettimeofday() + $interval, "LaCrosseGateway_OnConnectTimer", $hash, 0);
  472. if(AttrVal($name, "disable", "0") != "1") {
  473. my ($date, $time, $year, $month, $day, $hour, $min, $sec, $timestamp, $alive);
  474. if($useOldMethod) {
  475. $alive = InternalVal($name, "${name}_TIME", "2000-01-01 00:00:00");
  476. }
  477. else {
  478. LaCrosseGateway_TriggerWatchdog($hash);
  479. $timeout += 5;
  480. $alive = $hash->{Alive};
  481. $alive = "2000-01-01 00:00:00" if !$alive;
  482. }
  483. ($date, $time) = split( ' ', $alive);
  484. ($year, $month, $day) = split( '-', $date);
  485. ($hour, $min, $sec) = split( ':', $time);
  486. $month -= 01;
  487. $timestamp = timelocal($sec, $min, $hour, $day, $month, $year);
  488. if (gettimeofday() - $timestamp > $timeout) {
  489. return LaCrosseGateway_Connect($hash, 1);
  490. }
  491. }
  492. }
  493. }
  494. #=======================================================================================
  495. sub LaCrosseGateway_Attr(@) {
  496. my ($cmd,$name,$aName,$aVal) = @_;
  497. my $hash = $defs{$name};
  498. if( $aName eq "Clients" ) {
  499. $hash->{Clients} = $aVal;
  500. $hash->{Clients} = $clients if( !$hash->{Clients});
  501. }
  502. elsif ($aName eq "timeout") {
  503. return "Usage: attr $name $aName <checkInterval>" if($aVal && $aVal !~ m/^[0-9]{1,6}(,[0-9]{1,6})*/);
  504. RemoveInternalTimer($hash, "LaCrosseGateway_OnConnectTimer");
  505. if($aVal) {
  506. LaCrosseGateway_TriggerWatchdog($hash);
  507. my ($timeout, $interval) = split(',', $aVal);
  508. $interval = $timeout if !$interval;
  509. InternalTimer(gettimeofday()+$interval, "LaCrosseGateway_OnConnectTimer", $hash, 0);
  510. }
  511. }
  512. elsif ($aName eq "disable") {
  513. if($aVal eq "1") {
  514. DevIo_CloseDev($hash);
  515. readingsSingleUpdate($hash, "state", "disabled", 1);
  516. $hash->{"RAWMSG"} = "";
  517. $hash->{"model"} = "";
  518. }
  519. else {
  520. if($hash->{READINGS}{state}{VAL} eq "disabled") {
  521. readingsSingleUpdate($hash, "state", "disconnected", 1);
  522. InternalTimer(gettimeofday()+1, "LaCrosseGateway_Connect", $hash, 0);
  523. }
  524. }
  525. }
  526. elsif ($aName eq "MatchList") {
  527. my $match_list;
  528. if( $cmd eq "set" ) {
  529. $match_list = eval $aVal;
  530. if( $@ ) {
  531. Log3 $name, 2, $name .": $aVal: ". $@;
  532. }
  533. }
  534. if (ref($match_list) eq 'HASH') {
  535. $hash->{MatchList} = $match_list;
  536. }
  537. else {
  538. $hash->{MatchList} = \%matchList;
  539. Log3 $name, 2, $name .": $aVal: not a HASH" if( $aVal );
  540. }
  541. }
  542. elsif ($aName eq "ownSensors" && $aVal eq "dispatch") {
  543. LaCrosseGateway_DeleteOwnSensorsReadings($hash);
  544. }
  545. elsif ($aName eq "kvp" && $aVal eq "dispatch") {
  546. LaCrosseGateway_DeleteKVPReadings($hash);
  547. }
  548. return undef;
  549. }
  550. #=======================================================================================
  551. 1;
  552. =pod
  553. =item summary The IODevice for the LaCrosseGateway
  554. =item summary_DE Das IODevice für das LaCrosseGateway
  555. =begin html
  556. <a name="LaCrosseGateway"></a>
  557. <h3>LaCrosseGateway</h3>
  558. <ul>
  559. For more information about the LaCrosseGateway see here: <a href="http://www.fhemwiki.de/wiki/LaCrosseGateway">FHEM wiki</a>
  560. <br><br>
  561. <a name="LaCrosseGateway_Define"></a>
  562. <b>Define</b>
  563. <ul>
  564. <code>define &lt;name&gt; LaCrosseGateway &lt;device&gt;</code> <br>
  565. <br>
  566. USB-connected devices:<br><ul>
  567. &lt;device&gt; specifies the serial port to communicate with the LaCrosseGateway.
  568. The name of the serial-device depends on your distribution, under
  569. linux it is something like /dev/ttyACM0 or /dev/ttyUSB0.<br><br>
  570. </ul>
  571. Network-connected devices:<br><ul>
  572. &lt;device&gt; specifies the network device<br>
  573. Normally this is the IP-address and the port in the form ip:port<br>
  574. Example: 192.168.1.100:81<br>
  575. You must define the port number on the setup page of the LaCrosseGateway and use the same number here.<br>
  576. The default is 81
  577. <br><br>
  578. </ul>
  579. <br>
  580. </ul>
  581. <a name="LaCrosseGateway_Set"></a>
  582. <b>Set</b>
  583. <ul>
  584. <li>raw &lt;data&gt;<br>
  585. send &lt;data&gt; to the LaCrosseGateway. The possible command can be found in the wiki.
  586. </li><br>
  587. <li>connect<br>
  588. tries to (re-)connect to the LaCrosseGateway. It does not reset the LaCrosseGateway but only try to get a connection to it.
  589. </li><br>
  590. <li>reboot<br>
  591. Reboots the ESP8266. Works only if we are connected (state is opened or initialized)
  592. </li><br>
  593. <li>LaCrossePairForSec &lt;sec&gt; [ignore_battery]<br>
  594. enable autocreate of new LaCrosse sensors for &lt;sec&gt; seconds. If ignore_battery is not given only sensors
  595. sending the 'new battery' flag will be created.
  596. </li><br>
  597. <li>flash<br>
  598. The LaCrosseGateway needs the right firmware to be able to receive and deliver the sensor data to fhem.<br>
  599. This provides a way to flash it directly from FHEM.
  600. </li><br>
  601. <li>nextionUpload<br>
  602. Requires LaCrosseGateway V1.24 or newer.<br>
  603. Sends a Nextion firmware file (.tft) to the LaCrosseGateway. The LaCrosseGateway then distributes it to a connected Nextion display.<br>
  604. You can define the .tft file that shall be uploaded in the tftFile attribute. If this attribute does not exists, it will try to use FHEM/firmware/nextion.tft
  605. </li><br>
  606. </ul>
  607. <a name="LaCrosseGateway_Get"></a>
  608. <b>Get</b>
  609. <ul>
  610. ---
  611. </ul>
  612. <br>
  613. <a name="LaCrosseGateway_Attr"></a>
  614. <b>Attributes</b>
  615. <ul>
  616. <li>Clients<br>
  617. The received data gets distributed to a client (e.g. LaCrosse, EMT7110, ...) that handles the data.
  618. This attribute tells, which are the clients, that handle the data. If you add a new module to FHEM, that shall handle
  619. data distributed by the LaCrosseGateway module, you must add it to the Clients attribute.
  620. </li><br>
  621. <li>MatchList<br>
  622. Can be set to a perl expression that returns a hash that is used as the MatchList
  623. </li><br>
  624. <li>initCommands<br>
  625. Space separated list of commands to send for device initialization.
  626. </li><br>
  627. <li>timeout<br>
  628. format: &lt;timeout&gt<br>
  629. Asks the LaCrosseGateway every timeout seconds if it is still alive. If there is no response it reconnects to the LaCrosseGateway.<br>
  630. Can be combined with the watchdog attribute. If the watchdog attribute is set, the LaCrosseGateway also will check if it gets
  631. a request within watchdog seconds and if not, it will reboot.
  632. watchdog must be longer than timeout and does only work in combination with timeout.<br>
  633. Both should not be too short because the LaCrosseGateway needs enough time to boot before the next check.<br>
  634. Good values are: timeout 60 and watchdog 300<br>
  635. This mode needs LaCrosseGateway V1.24 or newer.
  636. <br><br><u>Old version (still working):</u><br>
  637. format: &lt;timeout, checkInterval&gt;<br>
  638. Checks every 'checkInterval' seconds if the last data reception is longer than 'timeout' seconds ago.<br>
  639. If this is the case, a new connect will be tried.
  640. </li><br>
  641. <li>watchdog<br>
  642. see timeout attribute.
  643. </li><br>
  644. <li>disable<br>
  645. if disabled, it does not try to connect and does not dispatch data
  646. </li><br>
  647. <li>kvp<br>
  648. defines how the incoming KVP-data of the LaCrosseGateway is handled<br>
  649. dispatch: (default) dispatch it to a KVP device<br>
  650. readings: create readings (e.g. RSSI, ...) in this device<br>
  651. both: dispatch and create readings
  652. </li><br>
  653. <li>ownSensors<br>
  654. defines how the incoming data of the internal LaCrosseGateway sensors is handled<br>
  655. dispatch: (default) dispatch it to a LaCrosse device<br>
  656. readings: create readings (e.g. temperature, humidity, ...) in this device<br>
  657. both: dispatch and create readings
  658. </li><br>
  659. <li>mode<br>
  660. USB, WiFi or Cable<br>
  661. Depending on how the LaCrosseGateway is connected, it must be handled differently (init, ...)
  662. </li><br>
  663. <li>tftFile<br>
  664. defines the .tft file that shall be used by the Nextion firmware upload (set nextionUpload)
  665. </li><br>
  666. </ul>
  667. <br>
  668. </ul>
  669. =end html
  670. =cut