00_CM11.pm 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819
  1. ################################################################
  2. #
  3. # Copyright notice
  4. #
  5. # (c) 2008 Dr. Boris Neubert (omega@online.de)
  6. #
  7. # This script is free software; you can redistribute it and/or modify
  8. # it under the terms of the GNU General Public License as published by
  9. # the Free Software Foundation; either version 2 of the License, or
  10. # (at your option) any later version.
  11. #
  12. # The GNU General Public License can be found at
  13. # http://www.gnu.org/copyleft/gpl.html.
  14. # A copy is found in the textfile GPL.txt and important notices to the license
  15. # from the author is found in LICENSE.txt distributed with these scripts.
  16. #
  17. # This script is distributed in the hope that it will be useful,
  18. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  19. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  20. # GNU General Public License for more details.
  21. #
  22. # This copyright notice MUST APPEAR in all copies of the script!
  23. #
  24. ################################################################
  25. # $Id: 00_CM11.pm 16375 2018-03-10 15:40:02Z neubert $
  26. package main;
  27. use strict;
  28. use warnings;
  29. use Time::HiRes qw(gettimeofday);
  30. sub CM11_Write($$$);
  31. sub CM11_Read($);
  32. sub CM11_Ready($$);
  33. my $msg_pollpc = pack("H*", "5a"); # interface poll signal (CM11->PC)
  34. my $msg_pollpcpf = pack("H*", "a5"); # power fail poll signal (CM11->PC)
  35. my $msg_pollack = pack("H*", "c3"); # response to poll signal (PC->CM11)
  36. my $msg_pollackpf= pack("H*", "fb"); # response to power fail poll signal (PC->CM11)
  37. my $msg_txok = pack("H*", "00"); # OK for transmission (PC->CM11)
  38. my $msg_ifrdy = pack("H*", "55"); # interface ready (CM11->PC)
  39. my $msg_statusrq = pack("H*", "8b"); # status request (PC->CM11)
  40. my %housecodes_rcv = qw(0110 A 1110 B 0010 C 1010 D
  41. 0001 E 1001 F 0101 G 1101 H
  42. 0111 I 1111 J 0011 K 1011 L
  43. 0000 M 1000 N 0100 O 1100 P);
  44. my %unitcodes_rcv = qw(0110 1 1110 2 0010 3 1010 4
  45. 0001 5 1001 6 0101 7 1101 8
  46. 0111 9 1111 10 0011 11 1011 12
  47. 0000 13 1000 14 0100 15 1100 16);
  48. my %functions_rcv = qw(0000 ALL_UNITS_OFF
  49. 0001 ALL_LIGHTS_ON
  50. 0010 ON
  51. 0011 OFF
  52. 0100 DIM
  53. 0101 BRIGHT
  54. 0110 ALL_LIGHTS_OFF
  55. 0111 EXTENDED_CODE
  56. 1000 HAIL_REQUEST
  57. 1001 HAIL_ACK
  58. 1010 PRESET_DIM1
  59. 1011 PRESET_DIM2
  60. 1100 EXTENDED_DATA_TRANSFER
  61. 1101 STATUS_ON
  62. 1110 STATUS_OFF
  63. 1111 STATUS_REQUEST);
  64. my %gets = (
  65. "fwrev" => "xxx",
  66. "time" => "xxx",
  67. );
  68. my %sets = (
  69. "reopen" => "xxx",
  70. );
  71. #####################################
  72. sub
  73. CM11_Initialize($)
  74. {
  75. my ($hash) = @_;
  76. # Provider
  77. $hash->{ReadFn} = "CM11_Read";
  78. $hash->{WriteFn} = "CM11_Write";
  79. $hash->{Clients} = ":X10:";
  80. $hash->{ReadyFn} = "CM11_Ready";
  81. # Normal Device
  82. $hash->{DefFn} = "CM11_Define";
  83. $hash->{UndefFn} = "CM11_Undef";
  84. $hash->{GetFn} = "CM11_Get";
  85. $hash->{SetFn} = "CM11_Set";
  86. $hash->{StateFn} = "CM11_SetState";
  87. $hash->{AttrList}= "do_not_notify:1,0 dummy:1,0 " .
  88. "model:CM11";
  89. }
  90. #####################################
  91. sub
  92. CM11_DoInit($$$)
  93. {
  94. my ($name,$type,$po) = @_;
  95. my @init;
  96. $po->reset_error();
  97. $po->baudrate(4800);
  98. $po->databits(8);
  99. $po->parity('none');
  100. $po->stopbits(1);
  101. $po->handshake('none');
  102. if($type && $type eq "strangetty") {
  103. # This part is for some Linux kernel versions whih has strange default
  104. # settings. Device::SerialPort is nice: if the flag is not defined for your
  105. # OS then it will be ignored.
  106. $po->stty_icanon(0);
  107. #$po->stty_parmrk(0); # The debian standard install does not have it
  108. $po->stty_icrnl(0);
  109. $po->stty_echoe(0);
  110. $po->stty_echok(0);
  111. $po->stty_echoctl(0);
  112. # Needed for some strange distros
  113. $po->stty_echo(0);
  114. $po->stty_icanon(0);
  115. $po->stty_isig(0);
  116. $po->stty_opost(0);
  117. $po->stty_icrnl(0);
  118. }
  119. $po->write_settings;
  120. $defs{$name}{STATE} = "Initialized";
  121. }
  122. #####################################
  123. sub
  124. CM11_Reopen($)
  125. {
  126. my ($hash) = @_;
  127. my $name = $hash->{NAME};
  128. my $dev = $hash->{DeviceName};
  129. $hash->{PortObj}->close();
  130. Log3($name, 1, "Device $dev closed");
  131. for(;;) {
  132. sleep(5);
  133. if($^O =~ m/Win/) {
  134. $hash->{PortObj} = new Win32::SerialPort($dev);
  135. }else{
  136. $hash->{PortObj} = new Device::SerialPort($dev);
  137. }
  138. if($hash->{PortObj}) {
  139. Log3($name, 1, "Device $dev reopened");
  140. $hash->{FD} = $hash->{PortObj}->FILENO if($^O !~ m/Win/);
  141. CM11_DoInit($hash->{NAME}, $hash->{ttytype}, $hash->{PortObj});
  142. return;
  143. }
  144. }
  145. }
  146. #####################################
  147. sub
  148. CM11_Define($$)
  149. {
  150. my ($hash, $def) = @_;
  151. my @a = split("[ \t][ \t]*", $def);
  152. my $po;
  153. return "wrong syntax: define <name> CM11 devicename ".
  154. "[normal|strangetty] [mobile]" if(@a < 3 || @a > 5);
  155. delete $hash->{PortObj};
  156. delete $hash->{FD};
  157. my $name = $a[0];
  158. my $dev = $a[2];
  159. $hash->{ttytype} = $a[3] if($a[3]);
  160. $hash->{MOBILE} = 1 if($a[4] && $a[4] eq "mobile");
  161. $hash->{STATE} = "defined";
  162. if($dev eq "none") {
  163. Log3($name, 1, "CM11 device is none, commands will be echoed only");
  164. $attr{$name}{dummy} = 1;
  165. return undef;
  166. }
  167. $hash->{DeviceName} = $dev;
  168. $hash->{PARTIAL} = "";
  169. Log3($name, 3, "CM11 opening CM11 device $dev");
  170. if ($^O=~/Win/) {
  171. require Win32::SerialPort;
  172. $po = new Win32::SerialPort ($dev);
  173. } else {
  174. require Device::SerialPort;
  175. $po = new Device::SerialPort ($dev);
  176. }
  177. if(!$po) {
  178. my $msg = "Can't open $dev: $!";
  179. Log3($name, 3, $msg) if($hash->{MOBILE});
  180. return $msg if(!$hash->{MOBILE});
  181. $readyfnlist{"$name.$dev"} = $hash;
  182. return "";
  183. }
  184. Log3($name, 3, "CM11 opened CM11 device $dev");
  185. $hash->{PortObj} = $po;
  186. if( $^O !~ /Win/ ) {
  187. $hash->{FD} = $po->FILENO;
  188. $selectlist{"$name.$dev"} = $hash;
  189. } else {
  190. $readyfnlist{"$name.$dev"} = $hash;
  191. }
  192. CM11_DoInit($name, $hash->{ttytype}, $po);
  193. #CM11_SetInterfaceTime($hash);
  194. #CM11_GetInterfaceStatus($hash);
  195. return undef;
  196. }
  197. #####################################
  198. sub
  199. CM11_Undef($$)
  200. {
  201. my ($hash, $arg) = @_;
  202. my $name = $hash->{NAME};
  203. foreach my $d (sort keys %defs) {
  204. if(defined($defs{$d}) &&
  205. defined($defs{$d}{IODev}) &&
  206. $defs{$d}{IODev} == $hash)
  207. {
  208. my $lev = ($reread_active ? 4 : 2);
  209. Log3($name, $lev, "deleting port for $d");
  210. delete $defs{$d}{IODev};
  211. }
  212. }
  213. $hash->{PortObj}->close() if($hash->{PortObj});
  214. return undef;
  215. }
  216. #####################################
  217. sub
  218. CM11_SetState($$$$)
  219. {
  220. my ($hash, $tim, $vt, $val) = @_;
  221. return undef;
  222. }
  223. #####################################
  224. sub
  225. CM11_LogReadWrite($@)
  226. {
  227. my ($rw,$hash, $msg, $trlr) = @_;
  228. my $name= $hash->{NAME};
  229. Log3($name, 5, "CM11 device " . $name . ": $rw " .
  230. sprintf("%2d: ", length($msg)) . unpack("H*", $msg));
  231. }
  232. sub
  233. CM11_LogRead(@)
  234. {
  235. CM11_LogReadWrite("read ", @_);
  236. }
  237. sub
  238. CM11_LogWrite(@)
  239. {
  240. CM11_LogReadWrite("write", @_);
  241. }
  242. #####################################
  243. sub
  244. CM11_SimpleWrite($$)
  245. {
  246. my ($hash, $msg) = @_;
  247. return if(!$hash || !defined($hash->{PortObj}));
  248. CM11_LogWrite($hash,$msg);
  249. $hash->{PortObj}->write($msg);
  250. }
  251. #####################################
  252. sub
  253. CM11_ReadDirect($$)
  254. {
  255. # This is a direct read for CM11_Write
  256. my ($hash,$arg) = @_;
  257. return undef if(!$hash || !defined($hash->{FD}));
  258. my $name= $hash->{NAME};
  259. my $prefix= "CM11 device " . $name . ":";
  260. my $rin= '';
  261. my $nfound;
  262. if($^O eq 'MSWin32') {
  263. $nfound= CM11_Ready($hash, undef);
  264. } else {
  265. vec($rin, $hash->{FD}, 1) = 1;
  266. my $to = 20; # seconds timeout (response might be damn slow)
  267. $to = $hash->{RA_Timeout} if($hash->{RA_Timeout}); # ...or less
  268. $nfound = select($rin, undef, undef, $to);
  269. if($nfound < 0) {
  270. next if ($! == EAGAIN() || $! == EINTR() || $! == 0);
  271. Log3($name, 3, "$prefix Select error $nfound / $!");
  272. return undef;
  273. }
  274. }
  275. if(!$nfound) {
  276. Log3($name, 3, "$prefix Timeout reading $arg");
  277. return undef;
  278. }
  279. my $buf = $hash->{PortObj}->input();
  280. CM11_LogRead($hash,$buf);
  281. return $buf;
  282. }
  283. #####################################
  284. sub
  285. CM11_Write($$$)
  286. {
  287. # send two bytes, verify checksum, send ok
  288. my ($hash,$b1,$b2) = @_;
  289. my $name = $hash->{NAME};
  290. my $prefix= "CM11 device $name:";
  291. if(!$hash || !defined($hash->{PortObj})) {
  292. Log3($name, 3, "$prefix device is not active, cannot send");
  293. return;
  294. }
  295. # checksum
  296. my $b1d = unpack('C', $b1);
  297. my $b2d = unpack('C', $b2);
  298. my $checksum_w = ($b1d + $b2d) & 0xff;
  299. my $data;
  300. # try 5 times to send
  301. my $try= 5;
  302. for(;;) {
  303. $try--;
  304. # send two bytes
  305. $data= $b1 . $b2;
  306. CM11_LogWrite($hash,$data);
  307. $hash->{PortObj}->write($data);
  308. # get checksum
  309. my $checksum= CM11_ReadDirect($hash, "checksum");
  310. return 0 if(!defined($checksum)); # read failure
  311. my $checksum_r= unpack('C', $checksum);
  312. if($checksum_w ne $checksum_r) {
  313. Log3($name, 5, "$prefix wrong checksum (send: $checksum_w, received: $checksum_r)");
  314. return 0 if(!$try);
  315. my $nexttry= 6-$try;
  316. Log3($name, 5, "$prefix retrying (" . $nexttry . "/5)");
  317. } else {
  318. Log3($name, 5, "$prefix checksum correct, OK for transmission");
  319. last;
  320. }
  321. }
  322. # checksum ok => send OK for transmission
  323. $data= $msg_txok;
  324. CM11_LogWrite($hash,$data);
  325. $hash->{PortObj}->write($data);
  326. my $ready= CM11_ReadDirect($hash, "ready");
  327. return 0 if(!defined($ready)); # read failure
  328. if($ready ne $msg_ifrdy) {
  329. Log3($name, 3, "$prefix strange ready signal (" . unpack('C', $ready) . ")");
  330. return 0
  331. } else {
  332. Log3($name, 5, "$prefix ready");
  333. }
  334. # we are fine
  335. return 1;
  336. }
  337. #####################################
  338. sub
  339. CM11_GetInterfaceStatus($)
  340. {
  341. my ($hash)= @_;
  342. CM11_SimpleWrite($hash, $msg_statusrq);
  343. my $statusmsg= "";
  344. while(length($statusmsg)<14) {
  345. my $buf= CM11_ReadDirect($hash, "status");
  346. return if(!defined($buf)); # read error
  347. $statusmsg.= $buf;
  348. }
  349. return $statusmsg;
  350. }
  351. #####################################
  352. sub CM11_Get($@)
  353. {
  354. my ($hash, @a) = @_;
  355. return "CM11: get needs only one parameter" if(@a != 2);
  356. return "Unknown argument $a[1], choose one of " . join(" ", sort keys %gets)
  357. if(!defined($gets{$a[1]}));
  358. my ($fn, $arg) = split(" ", $gets{$a[1]});
  359. my $v = join(" ", @a);
  360. my $name = $hash->{NAME};
  361. Log3($name, 2, "CM11 get $v");
  362. my $statusmsg= CM11_GetInterfaceStatus($hash);
  363. if(!defined($statusmsg)) {
  364. $v= "error";
  365. Log3($name, 2, "CM11 error, device is irresponsive.")
  366. } else {
  367. my $msg= unpack("H*", $statusmsg);
  368. Log3($name, 5, "CM11 got ". $msg);
  369. if($a[1] eq "fwrev") {
  370. $v = hex(substr($msg, 14, 1));
  371. } elsif($a[1] eq "time") {
  372. my $sec= hex(substr($msg, 4, 2));
  373. my $hour= hex(substr($msg, 8, 2))*2;
  374. my $min= hex(substr($msg, 6, 2));
  375. if($min>59) {
  376. $min-= 60;
  377. $hour++;
  378. }
  379. my $day= hex(substr($msg, 10, 2));
  380. $day+= 256 if(hex(substr($msg, 12, 1)) & 0xf);
  381. $v= sprintf("%d.%02d:%02d:%02d", $day,$hour,$min,$sec);
  382. }
  383. }
  384. $hash->{READINGS}{$a[1]}{VAL} = $v;
  385. $hash->{READINGS}{$a[1]}{TIME} = TimeNow();
  386. return "$a[0] $a[1] => $v";
  387. }
  388. #####################################
  389. sub
  390. CM11_Set($@)
  391. {
  392. my ($hash, @a) = @_;
  393. return "CM11: set needs one parameter" if(@a != 2);
  394. return "Unknown argument $a[1], choose one of " . join(" ", sort keys %sets)
  395. if(!defined($sets{$a[1]}));
  396. my ($fn, $arg) = split(" ", $sets{$a[1]});
  397. my $v = join(" ", @a);
  398. my $name = $hash->{NAME};
  399. Log3($name, 2, "CM11 set $v");
  400. if($a[1] eq "reopen") {
  401. CM11_Reopen($hash);
  402. }
  403. return undef;
  404. }
  405. #####################################
  406. sub
  407. CM11_SetInterfaceTime($)
  408. {
  409. my ($hash)= @_;
  410. # 7 Bytes, Bits 0..55 are
  411. # 55 to 48 timer download header (0x9b)
  412. # 47 to 40 Current time (seconds)
  413. # 39 to 32 Current time (minutes ranging from 0 to 119)
  414. # 31 to 23 Current time (hours/2, ranging from 0 to 11)
  415. # 23 to 16 Current year day (bits 0 to 7)
  416. # 15 Current year day (bit 8)
  417. # 14 to 8 Day mask (SMTWTFS)
  418. # 7 to 4 Monitored house code
  419. # 3 Reserved
  420. # 2 Battery timer clear flag
  421. # 1 Monitored status clear flag
  422. # 0 Timer purge flag
  423. # make the interface happy (time is set to zero)
  424. my $data = pack('C7', 0x9b,0x00,0x00,0x00,0x00,0x00,0x03);
  425. CM11_SimpleWrite($hash, $data);
  426. # get checksum (ignored)
  427. my $checksum= CM11_ReadDirect($hash, "checksum");
  428. return 0 if(!defined($checksum)); # read failure
  429. # tx OK
  430. CM11_SimpleWrite($hash, $msg_txok);
  431. # get ready (ignored)
  432. my $ready= CM11_ReadDirect($hash, "ready");
  433. return 0 if(!defined($ready)); # read failure
  434. return 1;
  435. }
  436. #####################################
  437. sub
  438. CM11_Read($)
  439. {
  440. #
  441. # prolog
  442. #
  443. my ($hash) = @_;
  444. my $buf = $hash->{PortObj}->input();
  445. my $name = $hash->{NAME};
  446. # prefix for logging
  447. my $prefix= "CM11 device " . $name . ":";
  448. # Lets' try again: Some drivers return len(0) on the first read...
  449. if(defined($buf) && length($buf) == 0) {
  450. $buf = $hash->{PortObj}->input();
  451. }
  452. # USB troubleshooting
  453. if(!defined($buf) || length($buf) == 0) {
  454. my $dev = $hash->{DeviceName};
  455. Log3($name, 1, "USB device $dev disconnected, waiting to reappear");
  456. $hash->{PortObj}->close();
  457. DoTrigger($name, "DISCONNECTED");
  458. delete($hash->{PortObj});
  459. delete($selectlist{"$name.$dev"});
  460. $readyfnlist{"$name.$dev"} = $hash; # Start polling
  461. $hash->{STATE} = "disconnected";
  462. # Without the following sleep the open of the device causes a SIGSEGV,
  463. # and following opens block infinitely. Only a reboot helps.
  464. sleep(5);
  465. }
  466. #
  467. # begin of message digesting
  468. #
  469. # concatenate yet unparsed message and newly received data
  470. my $x10data = $hash->{PARTIAL} . $buf;
  471. CM11_LogRead($hash,$buf);
  472. Log3($name, 5, "$prefix Data: " . unpack('H*',$x10data));
  473. # normally the while loop will run only once
  474. while(length($x10data) > 0) {
  475. # we cut off everything before the latest poll signal
  476. my $p= index(reverse($x10data), $msg_pollpc);
  477. if($p<0) { $p= index(reverse($x10data), $msg_pollpcpf); }
  478. if($p>=0) { $x10data= substr($x10data, -$p-1); }
  479. # to start with, a single 0x5a is received
  480. if( substr($x10data,0,1) eq $msg_pollpc ) { # CM11 polls PC
  481. Log3($name, 5, "$prefix start of message");
  482. CM11_SimpleWrite($hash, $msg_pollack); # PC ready
  483. $x10data= substr($x10data,1); # $x10data now empty
  484. next;
  485. }
  486. # experimental code follows
  487. #if( substr($x10data,0,2) eq pack("H*", "98e6") ) { # CM11 polls PC
  488. # Log 5, "$prefix 98e6";
  489. # CM11_SimpleWrite($hash, $msg_pollack); # PC ready
  490. # $x10data= "";
  491. # next;
  492. #}
  493. #if( substr($x10data,0,1) eq pack("H*", "98") ) { # CM11 polls PC
  494. # Log 5, "$prefix 98";
  495. # next;
  496. #}
  497. # a single 0xa5 is a power-fail macro download poll
  498. if( substr($x10data,0,1) eq $msg_pollpcpf ) { # CM11 polls PC
  499. Log3($name, 5, "$prefix power-fail poll");
  500. # the documentation wrongly says that the macros should be downloaded
  501. # in fact, the time must be set!
  502. if(CM11_SetInterfaceTime($hash)) {
  503. Log3($name, 5, "$prefix power-fail poll satisfied");
  504. } else {
  505. Log3($name, 5, "$prefix power-fail poll satisfaction failed");
  506. }
  507. $x10data= substr($x10data,1); # $x10data now empty
  508. next;
  509. }
  510. # a single 0x55 is a leftover from a failed transmission
  511. if( substr($x10data,0,1) eq $msg_ifrdy ) { # CM11 polls PC
  512. Log3($name, 5, "$prefix skipping leftover ready signal");
  513. $x10data= substr($x10data,1);
  514. next;
  515. }
  516. # the message comes in small chunks of 1 or few bytes instead of the
  517. # whole buffer at once
  518. my $len= ord(substr($x10data,0,1))-1; # upload buffer size
  519. last if(length($x10data)< $len+2); # wait for complete msg
  520. # message is now complete, start interpretation
  521. # mask: Bits 0 (LSB)..7 (MSB) correspond to data bytes 0..7
  522. # bit= 0: unitcode, bit= 1: function
  523. my $mask= unpack('B8', substr($x10data,1,1));
  524. $x10data= substr($x10data,2); # cut off length and mask
  525. # $x10data now contains $len data bytes
  526. my $databytes= unpack('H*', substr($x10data,0));
  527. Log3($name, 5, "$prefix message complete " .
  528. "(length $len, mask $mask, data $databytes)");
  529. # the following lines decode the messages into unitcodes and functions
  530. # in general we have 0..n unitcodes followed by 1..m functions in the
  531. # message
  532. my $i= 0;
  533. my $dmsg= "";
  534. while($i< $len) {
  535. my $data= substr($x10data, $i);
  536. my $bits = unpack('B8', $data);
  537. my $nibble_hi = substr($bits, 0, 4);
  538. my $nibble_lo = substr($bits, 4, 4);
  539. my $housecode= $housecodes_rcv{$nibble_hi};
  540. # one hash for unitcodes X_UNIT and one hash for functions
  541. # X_FUNC is maintained per housecode X= A..P
  542. my $housecode_unit= $housecode . "_UNIT";
  543. my $housecode_func= $housecode . "_FUNC";
  544. my $isfunc= (substr($mask, -$i-1, 1));
  545. if($isfunc) {
  546. # data byte is function
  547. my $x10func= $functions_rcv{$nibble_lo};
  548. if(($x10func eq "DIM") || ($x10func eq "BRIGHT")) {
  549. my $level= ord(substr($x10data, ++$i));
  550. $x10func.= " $level";
  551. }
  552. elsif($x10func eq "EXTENDED_DATA_TRANSFER") {
  553. $data= substr($x10data, 2+(++$i));
  554. my $command= substr($x10data, ++$i);
  555. $x10func.= unpack("H*", $data) . ":" .
  556. unpack("H*", $command);
  557. }
  558. $hash->{$housecode_func}= $x10func;
  559. Log3($name, 5, "$prefix $housecode_func: " .
  560. $hash->{$housecode_func});
  561. # dispatch message to clients
  562. my $hu = $hash->{$housecode_unit};
  563. $hu= "" unless(defined($hu));
  564. my $hf = $hash->{$housecode_func};
  565. my $dmsg= "X10:$housecode;$hu;$hf";
  566. Dispatch($hash, $dmsg, undef);
  567. } else {
  568. # data byte is unitcode
  569. # if a command was executed before, clear unitcode list
  570. if(defined($hash->{$housecode_func})) {
  571. undef $hash->{$housecode_unit};
  572. undef $hash->{$housecode_func};
  573. }
  574. # get unitcode of unitcode
  575. my $unitcode= $unitcodes_rcv{$nibble_lo};
  576. # append to list of unitcodes
  577. my $unitcodes= $hash->{$housecode_unit};
  578. if(defined($hash->{$housecode_unit})) {
  579. $unitcodes= $hash->{$housecode_unit} . " ";
  580. } else {
  581. $unitcodes= "";
  582. }
  583. $hash->{$housecode_unit}= "$unitcodes$unitcode";
  584. Log3($name, 5, "$prefix $housecode_unit: " .
  585. $hash->{$housecode_unit});
  586. }
  587. $i++;
  588. }
  589. $x10data= '';
  590. }
  591. $hash->{PARTIAL} = $x10data;
  592. }
  593. #####################################
  594. sub
  595. CM11_Ready($$)
  596. {
  597. my ($hash, $dev) = @_;
  598. my $po=$hash->{PortObj};
  599. if(!$po) { # Looking for the device
  600. my $dev = $hash->{DeviceName};
  601. my $name = $hash->{NAME};
  602. $hash->{PARTIAL} = "";
  603. if ($^O=~/Win/) {
  604. $po = new Win32::SerialPort ($dev);
  605. } else {
  606. $po = new Device::SerialPort ($dev);
  607. }
  608. return undef if(!$po);
  609. Log3($name, 1, "USB device $dev reappeared");
  610. $hash->{PortObj} = $po;
  611. if( $^O !~ /Win/ ) {
  612. $hash->{FD} = $po->FILENO;
  613. delete($readyfnlist{"$name.$dev"});
  614. $selectlist{"$name.$dev"} = $hash;
  615. } else {
  616. $readyfnlist{"$name.$dev"} = $hash;
  617. }
  618. CM11_DoInit($name, $hash->{ttytype}, $po);
  619. DoTrigger($name, "CONNECTED");
  620. return undef;
  621. }
  622. # This is relevant for windows only
  623. return undef if !$po;
  624. my ($BlockingFlags, $InBytes, $OutBytes, $ErrorFlags)=$po->status;
  625. return ($InBytes>0);
  626. }
  627. 1;
  628. =pod
  629. =item summary CM11 PC interface to receive and send X10 messages
  630. =item summary_DE CM11 PC Interface zum Empfangen und Senden von X10
  631. =begin html
  632. <a name="CM11"></a>
  633. <h3>CM11</h3>
  634. <ul>
  635. Note: this module requires the Device::SerialPort or Win32::SerialPort module.
  636. <br><br>
  637. <a name="CM11define"></a>
  638. <b>Define</b>
  639. <ul>
  640. <code>define &lt;name&gt; CM11 &lt;serial-device&gt;</code>
  641. <br><br>
  642. CM11 is the X10 module to interface X10 devices with the PC.<br><br>
  643. The current implementation can evaluate incoming data on the powerline of
  644. any kind. It can send on, off, dimdown and dimup commands.
  645. <br><br>
  646. The name of the serial-device depends on your distribution. If
  647. serial-device is none, then no device will be opened, so you can experiment
  648. without hardware attached.<br>
  649. If you experience problems (for verbose 4 you get a lot of "Bad CRC message"
  650. in the log), then try to define your device as <br>
  651. <code>define &lt;name&gt; FHZ &lt;serial-device&gt; strangetty</code><br>
  652. <br>
  653. Example:
  654. <ul>
  655. <code>define x10if CM11 /dev/ttyUSB3</code><br>
  656. </ul>
  657. <br>
  658. </ul>
  659. <a name="CM11set"></a>
  660. <b>Set</b>
  661. <ul>
  662. <code>set &lt;name&gt; reopen</code>
  663. <br><br>
  664. Reopens the serial port.
  665. </ul>
  666. <br>
  667. <a name="CM11get"></a>
  668. <b>Get</b>
  669. <ul>
  670. <code>get &lt;name&gt; fwrev</code>
  671. <br><br>
  672. Reads the firmware revision of the CM11 device. Returns <code>error</code>
  673. if the serial connection to the device times out. Can be used for error
  674. detection.
  675. <br><br>
  676. <code>get &lt;name&gt; time</code>
  677. <br><br>
  678. Reads the internal time of the device which is the total uptime (modulo one
  679. year), since fhem sets the time to 0.00:00:00 if the device requests the time
  680. to be set after being powered on. Returns <code>error</code>
  681. if the serial connection to the device times out. Can be used for error
  682. detection.
  683. </ul>
  684. <br>
  685. <a name="CM11attr"></a>
  686. <b>Attributes</b>
  687. <ul>
  688. <li><a href="#do_not_notify">do_not_notify</a></li>
  689. <li><a href="#attrdummy">dummy</a></li>
  690. <li><a href="#model">model</a> (CM11)</li>
  691. </ul>
  692. <br>
  693. </ul>
  694. =end html
  695. =cut