00_ZWCUL.pm 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771
  1. ##############################################
  2. # $Id: 00_ZWCUL.pm 11984 2016-08-19 12:47:50Z rudolfkoenig $
  3. package main;
  4. # TODO
  5. # static routing: to and from the device
  6. # automatic routing via neighborUpdate
  7. # explorer frames
  8. # check security
  9. # multicast
  10. use strict;
  11. use warnings;
  12. use Time::HiRes qw(gettimeofday);
  13. use ZWLib;
  14. use DevIo;
  15. sub ZWCUL_Parse($$$$$);
  16. sub ZWCUL_Read($@);
  17. sub ZWCUL_ReadAnswer($$$);
  18. sub ZWCUL_Ready($);
  19. sub ZWCUL_SimpleWrite($$);
  20. sub ZWCUL_Write($$$);
  21. sub ZWCUL_ProcessSendStack($);
  22. our %zwave_id2class;
  23. my %ZWCUL_sentIdx;
  24. my %ZWCUL_sentIdx2cbid;
  25. my %sets = (
  26. "reopen" => { cmd=>"" },
  27. "led" => { cmd=>"l%02x", param=>{ on=>1, off=>0, blink=>2 } },
  28. "addNode" => { cmd=>"x%x", param => { on=>1, off=>0, onSec=>2 } },
  29. "addNodeId" => { cmd=>"x%x" },
  30. "removeNode" => { cmd=>"x%x", param => { on=>1, off=>0 } },
  31. "raw" => { cmd=> "%s" },
  32. );
  33. my %gets = (
  34. "homeId" => { cmd=> "zi", regex => "^. [A-F0-9]{8} [A-F0-9]{2}\$" },
  35. "version" => { cmd=> "V", regex => "^V " },
  36. "nodeInfo" => { cmd=> "x%x" },
  37. "raw" => { cmd=> "%s", regex => ".*" }
  38. );
  39. sub
  40. ZWCUL_Initialize($)
  41. {
  42. my ($hash) = @_;
  43. # Provider
  44. $hash->{ReadFn} = "ZWCUL_Read";
  45. $hash->{WriteFn} = "ZWCUL_Write";
  46. $hash->{ReadyFn} = "ZWCUL_Ready";
  47. $hash->{ReadAnswerFn} = "ZWCUL_ReadAnswer";
  48. # Normal devices
  49. $hash->{DefFn} = "ZWCUL_Define";
  50. $hash->{SetFn} = "ZWCUL_Set";
  51. $hash->{GetFn} = "ZWCUL_Get";
  52. $hash->{AttrFn} = "ZWCUL_Attr";
  53. $hash->{UndefFn} = "ZWCUL_Undef";
  54. $hash->{AttrList}= "do_not_notify:1,0 dummy:1,0 model disable:0,1 ".
  55. "networkKey intruderMode dataRate:40k,100k,9600";
  56. }
  57. #####################################
  58. sub
  59. ZWCUL_Define($$)
  60. {
  61. my ($hash, $def) = @_;
  62. my @a = split("[ \t][ \t]*", $def);
  63. return "wrong syntax: define <name> ZWCUL device homeId ctrlId" if(@a != 5);
  64. return "wrong syntax: homeId is 8 digit hex" if($a[3] !~ m/^[0-9A-F]{8}$/i);
  65. return "wrong syntax: ctrlId is 2 digit hex" if($a[4] !~ m/^[0-9A-F]{2}$/i);
  66. DevIo_CloseDev($hash);
  67. my $name = $a[0];
  68. my $dev = $a[2];
  69. $hash->{homeId} = lc($a[3]);
  70. $hash->{homeIdSet} = lc($a[3]);
  71. $hash->{nodeIdHex} = lc($a[4]);
  72. $hash->{initString} = ($hash->{homeIdSet} =~ m/^0*$/ ? "zm4":"zr4");
  73. $hash->{baudRate} = "40k";
  74. $hash->{monitor} = 1 if($hash->{homeIdSet} eq "00000000");
  75. setReadingsVal($hash, "homeId", # ZWDongle compatibility
  76. "HomeId:$hash->{homeId} CtrlNodeIdHex:$hash->{nodeIdHex}", TimeNow());
  77. $hash->{Clients} = ":ZWave:STACKABLE_CC:";
  78. my %matchList = ( "1:ZWave" => ".*",
  79. "2:STACKABLE_CC"=>"^\\*");
  80. $hash->{MatchList} = \%matchList;
  81. if($dev eq "none") {
  82. Log3 $name, 1, "$name device is none, commands will be echoed only";
  83. $attr{$name}{dummy} = 1;
  84. readingsSingleUpdate($hash, "state", "dummy", 1);
  85. return undef;
  86. } elsif($dev !~ m/@/) {
  87. $def .= "\@9600"; # default baudrate
  88. }
  89. $hash->{DeviceName} = $dev;
  90. return DevIo_OpenDev($hash, 0, "ZWCUL_DoInit");
  91. }
  92. #####################################
  93. sub
  94. ZWCUL_DoInit($)
  95. {
  96. my $hash = shift;
  97. my $name = $hash->{NAME};
  98. $hash->{PARTIAL} = "";
  99. $hash->{RA_Timeout} = 0.5; # Clear the pipe
  100. for(;;) {
  101. my ($err, undef) = ZWCUL_ReadAnswer($hash, "Clear", "wontmatch");
  102. last if($err && ($err =~ m/^Timeout/ || $err =~ m/No FD/));
  103. }
  104. delete($hash->{RA_Timeout});
  105. my ($err, $ver, $try) = ("", "", 0);
  106. while($try++ < 3 && $ver !~ m/^V/) {
  107. ZWCUL_SimpleWrite($hash, "V");
  108. ($err, $ver) = ZWCUL_ReadAnswer($hash, "Version", "^V");
  109. return "$name: $err" if($err && ($err !~ m/Timeout/ || $try == 3));
  110. $ver = "" if(!$ver);
  111. }
  112. if($ver !~ m/^V/) {
  113. $attr{$name}{dummy} = 1;
  114. my $msg = "Not an CUL device, got for V: $ver";
  115. Log3 $name, 1, $msg;
  116. return $msg;
  117. }
  118. $ver =~ s/[\r\n]//g;
  119. $hash->{VERSION} = $ver;
  120. ZWCUL_SimpleWrite($hash, "zi".$hash->{homeIdSet}.$hash->{nodeIdHex});
  121. ZWCUL_SimpleWrite($hash, $hash->{initString});
  122. readingsSingleUpdate($hash, "state", "Initialized", 1);
  123. return undef;
  124. }
  125. #####################################
  126. sub
  127. ZWCUL_Undef($$)
  128. {
  129. my ($hash,$arg) = @_;
  130. ZWCUL_SimpleWrite($hash, "zx");
  131. DevIo_CloseDev($hash);
  132. return undef;
  133. }
  134. sub
  135. ZWCUL_tmp9600($$)
  136. {
  137. my ($hash, $on) = @_;
  138. $hash->{baudRate} = ($on ? "9600" : AttrVal($hash->{NAME},"dataRate","40k"));
  139. ZWCUL_SimpleWrite($hash, $on ? $on : $hash->{initString});
  140. }
  141. #####################################
  142. sub
  143. ZWCUL_cmd($$@)
  144. {
  145. my ($type, $cmdList, $hash, @a) = @_;
  146. my $name = shift @a;
  147. return "\"$type $name\" needs at least one parameter" if(@a < 1);
  148. my $cmdName = shift @a;
  149. if(!defined($cmdList->{$cmdName})) {
  150. my @r;
  151. map { my $p = $cmdList->{$_}{param};
  152. push @r,($p ? "$_:".join(",",sort keys %{$p}) : $_) }
  153. sort keys %{$cmdList};
  154. return "Unknown argument $cmdName, choose one of ".join(" ",@r);
  155. }
  156. Log3 $hash, 4, "ZWCUL $type $name $cmdName ".join(" ",@a);
  157. if($cmdName eq "reopen") {
  158. return if(AttrVal($name, "dummy",undef) || AttrVal($name, "disable",undef));
  159. delete $hash->{NEXT_OPEN};
  160. DevIo_CloseDev($hash);
  161. sleep(1);
  162. DevIo_OpenDev($hash, 0, "ZWCUL_DoInit");
  163. return;
  164. }
  165. my $cmd = $cmdList->{$cmdName}{cmd};
  166. my @ca = split("%", $cmd, -1);
  167. my $nargs = int(@ca)-1;
  168. return "$type $name $cmdName needs $nargs arguments" if($nargs != int(@a));
  169. my $param = $cmdList->{$cmdName}{param};
  170. if($param) {
  171. return "invalid parameter $a[0] for $cmdName" if(!defined($param->{$a[0]}));
  172. $a[0] = $param->{$a[0]};
  173. }
  174. if($cmdName =~ m/^addNode/) {
  175. delete $hash->{removeNode};
  176. delete $hash->{addNode};
  177. if($cmdName eq "addNodeId") {
  178. $hash->{addNode} = sprintf("%02x", $a[0]);
  179. } else {
  180. $hash->{addNode} = ZWCUL_getNextNodeId($hash) if($a[0]);
  181. $hash->{addSecure} = 1 if($a[0] == 2);
  182. }
  183. Log3 $hash, 3, "ZWCUL going to assigning new node id $hash->{addNode}"
  184. if($a[0]);
  185. ZWCUL_tmp9600($hash, $a[0] ? "zm9" : 0); # expect random homeId
  186. return;
  187. }
  188. if($cmdName eq "removeNode") {
  189. delete $hash->{addNode};
  190. delete $hash->{removeNode};
  191. $hash->{removeNode} = $a[0] if($a[0]);
  192. ZWCUL_tmp9600($hash, $a[0] ? "zr9" : 0);
  193. return;
  194. }
  195. if($cmdName eq "nodeInfo") {
  196. my $node = ZWCUL_getNode($hash, sprintf("%02x", $a[0]));
  197. return "Node with decimal id $a[0] not found" if(!$node);
  198. my $ni = ReadingsVal($node->{NAME}, "nodeInfo", undef);
  199. return "No nodeInfo present" if(!$ni);
  200. $ni = "0141${ni}041001"; # TODO: Remove fixed values
  201. my @r = map { ord($_) } split("", pack('H*', $ni));
  202. my $msg = zwlib_parseNodeInfo(@r);
  203. setReadingsVal($hash, "nodeInfo_".$a[0], $msg, TimeNow());
  204. return $msg;
  205. }
  206. $cmd = sprintf($cmd, @a);
  207. ZWCUL_SimpleWrite($hash, $cmd);
  208. return undef if($type eq "set");
  209. my $re = $cmdList->{$cmdName}{regexp};
  210. my ($e, $d) = ZWCUL_ReadAnswer($hash, $cmdName, $cmdList->{$cmdName}{regexp});
  211. return $e if($e);
  212. return $d;
  213. }
  214. sub ZWCUL_Set() { return ZWCUL_cmd("set", \%sets, @_); };
  215. sub ZWCUL_Get() { return ZWCUL_cmd("get", \%gets, @_); };
  216. #####################################
  217. sub
  218. ZWCUL_SimpleWrite($$)
  219. {
  220. my ($hash, $msg) = @_;
  221. return if(!$hash);
  222. my $name = $hash->{NAME};
  223. Log3 $name, 5, "SW: $msg";
  224. $msg .= "\n";
  225. $hash->{USBDev}->write($msg) if($hash->{USBDev});
  226. syswrite($hash->{TCPDev}, $msg) if($hash->{TCPDev});
  227. syswrite($hash->{DIODev}, $msg) if($hash->{DIODev});
  228. select(undef, undef, undef, 0.001);
  229. }
  230. #####################################
  231. sub
  232. ZWCUL_Write($$$)
  233. {
  234. my ($hash,$fn,$msg) = @_;
  235. Log3 $hash, 5, "ZWCUL_Write $fn $msg";
  236. if($msg =~ m/0013(..)(..)(.*)(..)(..)/) {
  237. my ($targetId,$l,$p,$flags,$cbid) = ($1,$2,$3,$4,$5);
  238. my ($homeId,$route) = split(",",$fn);
  239. my $sn = $ZWCUL_sentIdx{$targetId};
  240. $sn = (!$sn || $sn==15) ? 1 : ($sn+1);
  241. $ZWCUL_sentIdx2cbid{"$targetId$sn"} = $cbid;
  242. $ZWCUL_sentIdx{$targetId} = $sn;
  243. my $s100 = ($hash->{baudRate} eq "100k");
  244. my $rf = 0x41;
  245. if($route) {
  246. $rf = 0x81;
  247. $p = sprintf("00%d0%s%s",length($route)/2, $route, $p);
  248. }
  249. $msg = sprintf("%s%s%02x%02x%02x%s%s",
  250. $homeId, $hash->{nodeIdHex}, $rf, $sn,
  251. length($p)/2+($s100 ? 11 : 10), $targetId, $p);
  252. $msg .= ($s100 ? zwlib_checkSum_16($msg) : zwlib_checkSum_8($msg));
  253. ZWCUL_SimpleWrite($hash, "zs".$msg);
  254. }
  255. }
  256. #####################################
  257. # called from the global loop, when the select for hash->{FD} reports data
  258. sub
  259. ZWCUL_Read($@)
  260. {
  261. my ($hash, $local, $regexp) = @_;
  262. my $buf = (defined($local) ? $local : DevIo_SimpleRead($hash));
  263. return "" if(!defined($buf));
  264. my $name = $hash->{NAME};
  265. my $culdata = $hash->{PARTIAL};
  266. #Log3 $name, 5, "ZWCUL/RAW: $culdata/$buf";
  267. $culdata .= $buf;
  268. while($culdata =~ m/\n/) {
  269. my $rmsg;
  270. ($rmsg,$culdata) = split("\n", $culdata, 2);
  271. $rmsg =~ s/\r//;
  272. $hash->{PARTIAL} = $culdata; # for recursive calls
  273. return $rmsg
  274. if(defined($local) && (!defined($regexp) || ($rmsg =~ m/$regexp/)));
  275. ZWCUL_Parse($hash, $hash, $name, $rmsg, 0) if($rmsg);
  276. $culdata = $hash->{PARTIAL};
  277. }
  278. $hash->{PARTIAL} = $culdata;
  279. return undef;
  280. }
  281. sub
  282. ZWCUL_getNode($$)
  283. {
  284. my ($hash, $id) = @_;
  285. my @l = devspec2array(sprintf("TYPE=ZWave:".
  286. "FILTER=homeId=%s:FILTER=nodeIdHex=%s", $hash->{homeId}, $id));
  287. return undef if(!int(@l));
  288. return $defs{$l[0]};
  289. }
  290. sub
  291. ZWCUL_getNextNodeId($)
  292. {
  293. my ($hash) = @_;
  294. my @l = devspec2array(sprintf(".*:FILTER=homeId=%s",$hash->{homeId}));
  295. my %h = map { $defs{$_}{nodeIdHex} => 1 } @l;
  296. for(my $i = 1; $i <= 232; $i++) {
  297. my $s = sprintf("%02x", $i);
  298. return $s if(!$h{$s});
  299. }
  300. Log 1, "NOTE: NO MORE nodeIDs available";
  301. return "ff";
  302. }
  303. sub
  304. ZWCUL_assignId($$$$)
  305. {
  306. my ($hash, $oldNodeId, $newHomeId, $newNodeId) = @_;
  307. my $myHash;
  308. my $key = "$hash->{homeIdSet} $oldNodeId";
  309. if(!defined($modules{ZWave}{defptr}{$key})) {
  310. $modules{ZWave}{defptr}{$key} = { nodeIdHex => $oldNodeId };
  311. $myHash = 1;
  312. }
  313. ZWCUL_Write($hash, $hash->{homeIdSet},
  314. sprintf("0013%s080103%s%s####", $oldNodeId, $newNodeId, $newHomeId));
  315. delete $modules{ZWave}{defptr}{$key} if($myHash);
  316. }
  317. sub
  318. ZWCUL_Parse($$$$$)
  319. {
  320. my ($hash, $iohash, $name, $rmsg, $nodispatch) = @_;
  321. if($rmsg =~ m/^\*/) { # STACKABLE_CC
  322. Dispatch($hash, $rmsg, undef);
  323. return;
  324. }
  325. $hash->{"${name}_MSGCNT"}++;
  326. $hash->{"${name}_TIME"} = TimeNow();
  327. # showtime attribute
  328. readingsSingleUpdate($hash, "state", $hash->{READINGS}{state}{VAL}, 0);
  329. $hash->{RAWMSG} = $rmsg;
  330. my %addvals = (RAWMSG => $rmsg);
  331. Dispatch($hash, $rmsg, \%addvals) if($rmsg !~ m/^z/);
  332. $rmsg = lc($rmsg);
  333. my $me = $hash->{NAME};
  334. my $s100 = ($hash->{baudRate} eq "100k");
  335. if($rmsg =~ m/^za(..)$/) {
  336. Log3 $hash, 5, "$me sent ACK to $1";
  337. return;
  338. }
  339. if($rmsg =~ m/^zr(..)$/) {
  340. Log3 $hash, 5, "$me fw-resend nr ".hex($1);
  341. return;
  342. }
  343. my ($H, $S, $F, $f, $sn, $L, $T, $P, $C);
  344. if($s100 && $rmsg =~ '^z(........)(..)(..)(.)(.)(..)(..)(.*)(....)$') {
  345. ($H,$S,$F,$f,$sn,$L,$T,$P,$C) = ($1,$2,$3,$4,$5,$6,$7,$8,$9);
  346. } elsif(!$s100 && $rmsg =~ '^z(........)(..)(..)(.)(.)(..)(..)(.*)(..)$') {
  347. ($H,$S,$F,$f,$sn,$L,$T,$P,$C) = ($1,$2,$3,$4,$5,$6,$7,$8,$9);
  348. } else {
  349. Log3 $hash, 1, "ERROR: Unknown packet $rmsg";
  350. return;
  351. }
  352. my ($hF,$hf, $rf,$hc,$hp,$hops,$ri,$u1) = (hex($F),hex($f),"",0,0,"","","");
  353. # ITU G.9959, 8-4, 8-11
  354. if($hF&0x80) { # routing
  355. $hc = hex(substr($P,2,1));
  356. $hp = hex(substr($P,3,1));
  357. $ri = "R:".substr($P, 0, ($hc+2)*2)." ";
  358. $rf = substr($P, 0, 2);
  359. $hops = substr($P, 4, $hc*2);
  360. $hops =~ s/(..)/$1 /g;
  361. $P = substr($P,($hc+2)*2);
  362. }
  363. if($hF&4) { # Explorer?
  364. $u1 = " E:".substr($P,0,16)." ";
  365. $P = substr($P,16);
  366. }
  367. if(AttrVal($me, "verbose", 1) > 4) {
  368. Log3 $hash, 5, "$H S:$S F:$F f:$f SN:$sn L:$L T:$T ${ri}${u1}P:$P C:$C";
  369. Log3 $hash, 5, " F:".
  370. (($hF & 3)==1 ? " singleCast" :
  371. ($hF & 3)==2 ? " multiCast" :
  372. ($hF & 3)==3 ? " ack" : " unknownHeaderType:".($hF&0x3)).
  373. (($hF & 4) ? " explorer" : "").
  374. (($hF & 0x10)==0x10 ? " speedModified":"").
  375. (($hF & 0x20)==0x20 ? " lowPower":"").
  376. (($hF & 0x40)==0x40 ? " ackReq":"").
  377. (($hF & 0x80)==0x80 ?
  378. " routed, rf:$rf hopCnt:$hc hopPos:$hp hops:$hops":"").
  379. ((($hf>>1)&3)==0 ? " " :
  380. (($hf>>1)&3)==1 ? " shortBeam" :
  381. (($hf>>1)&3)==2 ? " longBeam" :" unknownBeam");
  382. }
  383. return if($hc && !$hash->{monitor} && $hc == $hp);
  384. return if($hash->{monitor} && !AttrVal($me, "intruderMode", 0));
  385. $hash->{homeId} = $H; # Fake homeId for monitor mode
  386. if(length($P)) {
  387. if($hash->{removeNode} && $T eq "ff") {
  388. ZWCUL_assignId($hash, $S, "00000000", "00");
  389. $hash->{removeNode} = $S;
  390. return;
  391. }
  392. if($hash->{addNode} && $T eq "ff" && $S eq "00" && $P =~ m/^0101/) {
  393. ZWCUL_assignId($hash, "00", $hash->{homeIdSet}, $hash->{addNode});
  394. $hash->{addNodeParam} = $P;
  395. return;
  396. }
  397. if($P =~ m/^0101(......)(..)..(.*)/) {
  398. my ($nodeInfo, $type6, $classes) = ($1, $2, $3);
  399. $rmsg = sprintf("004a0003%s####%s##%s", $S, $2, $3);
  400. } else {
  401. $rmsg = sprintf("0004%s%s%02x%s", $S, $S, length($P)/2, $P);
  402. }
  403. } else { # ACK
  404. if($hash->{removeNode} && $hash->{removeNode} eq $S) { #############
  405. Log3 $hash, 3, "Node $S excluded from network";
  406. delete $hash->{removeNode};
  407. ZWCUL_tmp9600($hash, 0);
  408. return;
  409. }
  410. if($hash->{addNode} && $S eq "00") { #############
  411. $hash->{addNodeParam} =~ m/^0101(......)(..)..(.*)/;
  412. my ($nodeInfo, $type6, $classes) = ($1, $2, $3);
  413. ZWCUL_tmp9600($hash, 0);
  414. $hash->{homeId} = $hash->{homeIdSet};
  415. Dispatch($hash, sprintf("004a0003%s####%s##%s",
  416. $hash->{addNode}, $type6, $classes), \%addvals);
  417. my $node = ZWCUL_getNode($hash, $hash->{addNode});
  418. if($node) { # autocreated a node
  419. readingsSingleUpdate($node, "nodeInfo", $nodeInfo, 0);
  420. Dispatch($hash, sprintf("004a0005%s##", $hash->{addNode}), \%addvals);
  421. }
  422. delete $hash->{addNode};
  423. delete $hash->{addNodeParam};
  424. return;
  425. }
  426. if($hash->{addNode} && $hash->{addNode} eq $S) { # Another hack
  427. Log3 $hash, 3,"ZWCUL node ".hex($S)." (hex $S) included into the network";
  428. delete $hash->{addNode};
  429. ZWCUL_tmp9600($hash, 0);
  430. return;
  431. }
  432. $rmsg = sprintf("0013%s00",
  433. $ZWCUL_sentIdx2cbid{"$S$sn"} ? $ZWCUL_sentIdx2cbid{"$S$sn"} : 00);
  434. }
  435. return $rmsg if($nodispatch);
  436. Dispatch($hash, $rmsg, \%addvals);
  437. }
  438. #####################################
  439. # This is a direct read for commands like get
  440. sub
  441. ZWCUL_ReadAnswer($$$)
  442. {
  443. my ($hash, $arg, $regexp) = @_;
  444. Log3 $hash, 4, "ZWCUL_ReadAnswer arg:$arg regexp:".($regexp ? $regexp:"");
  445. my $transform;
  446. if($regexp && $regexp =~ m/^\^000400(..)..(..)/) {
  447. $regexp = "^z........$1........$2";
  448. $transform = 1;
  449. }
  450. return ("No FD (dummy device?)", undef)
  451. if(!$hash || ($^O !~ /Win/ && !defined($hash->{FD})));
  452. my $to = ($hash->{RA_Timeout} ? $hash->{RA_Timeout} : 1);
  453. for(;;) {
  454. my $buf;
  455. if($^O =~ m/Win/ && $hash->{USBDev}) {
  456. $hash->{USBDev}->read_const_time($to*1000); # set timeout (ms)
  457. # Read anstatt input sonst funzt read_const_time nicht.
  458. $buf = $hash->{USBDev}->read(999);
  459. return ("Timeout reading answer for get $arg", undef)
  460. if(length($buf) == 0);
  461. } else {
  462. if(!$hash->{FD}) {
  463. Log3 $hash, 1, "ZWCUL_ReadAnswer: device lost";
  464. return ("Device lost when reading answer for get $arg", undef);
  465. }
  466. my $rin = '';
  467. vec($rin, $hash->{FD}, 1) = 1;
  468. my $nfound = select($rin, undef, undef, $to);
  469. if($nfound < 0) {
  470. my $err = $!;
  471. Log3 $hash, 5, "ZWCUL_ReadAnswer: nfound < 0 / err:$err";
  472. next if ($err == EAGAIN() || $err == EINTR() || $err == 0);
  473. DevIo_Disconnected($hash);
  474. return("ZWCUL_ReadAnswer $arg: $err", undef);
  475. }
  476. if($nfound == 0){
  477. Log3 $hash, 5, "ZWCUL_ReadAnswer: select timeout";
  478. return ("Timeout reading answer for get $arg", undef);
  479. }
  480. $buf = DevIo_SimpleRead($hash);
  481. if(!defined($buf)){
  482. Log3 $hash, 1,"ZWCUL_ReadAnswer: no data read";
  483. return ("No data", undef);
  484. }
  485. }
  486. my $ret = ZWCUL_Read($hash, $buf, $regexp);
  487. if(defined($ret)){
  488. if($transform) {
  489. my $name = $hash->{NAME};
  490. $ret = ZWCUL_Parse($hash, $hash, $name, $ret, 1);
  491. }
  492. Log3 $hash, 4, "ZWCUL_ReadAnswer for $arg: $ret";
  493. return (undef, $ret);
  494. }
  495. }
  496. }
  497. #####################################
  498. sub
  499. ZWCUL_Attr($$$$)
  500. {
  501. my ($cmd, $name, $attr, $value) = @_;
  502. my $hash = $defs{$name};
  503. if($attr eq "disable") {
  504. if($cmd eq "set" && ($value || !defined($value))) {
  505. DevIo_CloseDev($hash) if(!AttrVal($name,"dummy",undef));
  506. readingsSingleUpdate($hash, "state", "disabled", 1);
  507. } else {
  508. if(AttrVal($name,"dummy",undef)) {
  509. readingsSingleUpdate($hash, "state", "dummy", 1);
  510. return;
  511. }
  512. DevIo_OpenDev($hash, 0, "ZWCUL_DoInit");
  513. }
  514. } elsif($attr eq "networkKey" && $cmd eq "set") {
  515. if(!$value || $value !~ m/^[0-9A-F]{32}$/i) {
  516. return "attr $name networkKey: not a hex string with a length of 32";
  517. }
  518. return;
  519. } elsif($attr eq "dataRate" && $cmd eq "set") {
  520. my $sfx = ($value eq "100k" ? "1" :
  521. ($value eq "9600" ? "9" : "4"));
  522. $hash->{initString} = ($hash->{homeIdSet} =~ m/^0*$/ ? "zm$sfx":"zr$sfx");
  523. $hash->{baudRate} = $value;
  524. ZWCUL_SimpleWrite($hash, $hash->{initString});
  525. }
  526. return undef;
  527. }
  528. #####################################
  529. sub
  530. ZWCUL_Ready($)
  531. {
  532. my ($hash) = @_;
  533. return undef if (IsDisabled($hash->{NAME}));
  534. return DevIo_OpenDev($hash, 1, "ZWCUL_DoInit")
  535. if(ReadingsVal($hash->{NAME}, "state","") eq "disconnected");
  536. # This is relevant for windows/USB only
  537. my $po = $hash->{USBDev};
  538. if($po) {
  539. my ($BlockingFlags, $InBytes, $OutBytes, $ErrorFlags) = $po->status;
  540. return ($InBytes>0);
  541. }
  542. return 0;
  543. }
  544. 1;
  545. =pod
  546. =item summary connection to a culfw Device in ZWave mode (e.g. CUL)
  547. =item summary_DE Anbindung eines culfw Ger&auml;tes in ZWave Modus (z.Bsp. CUL)
  548. =begin html
  549. <a name="ZWCUL"></a>
  550. <h3>ZWCUL</h3>
  551. <ul>
  552. This module serves a CUL in ZWave mode (starting from culfw version 1.66),
  553. which is attached via USB or TCP/IP, and enables the use of ZWave devices
  554. (see also the <a href="#ZWave">ZWave</a> module).
  555. <br><br>
  556. <a name="ZWCULdefine"></a>
  557. <b>Define</b>
  558. <ul>
  559. <code>define &lt;name&gt; ZWCUL &lt;device&gt; &lt;homeId&gt;
  560. &lt;ctrlId&gt;</code>
  561. <br>
  562. <br>
  563. Since the DevIo module is used to open the device, you can also use devices
  564. connected via TCP/IP. See <a href="#CULdefine">this</a> paragraph on device
  565. naming details.
  566. <br>
  567. Example:
  568. <ul>
  569. <code>define ZWCUL_1 ZWCUL /dev/cu.usbmodemfa141@9600 12345678 01</code><br>
  570. </ul>
  571. If the homeId is set to 00000000, then culfw will enter monitor mode, i.e. no
  572. acks for received messages will be sent, and no homeId filtering will be done.
  573. </ul>
  574. <br>
  575. <a name="ZWCULset"></a>
  576. <b>Set</b>
  577. <ul>
  578. <li>reopen<br>
  579. First close and then open the device. Used for debugging purposes.
  580. </li>
  581. <li>led [on|off|blink]<br>
  582. Set the LED on the CUL.
  583. </li>
  584. <li>raw<br>
  585. send a raw string to culfw
  586. </li>
  587. <li>addNode [on|onSec|off]<br>
  588. Activate (or deactivate) inclusion mode. The CUL will switch to dataRate
  589. 9600 until terminating this mode with off, or a node is included. If onSec
  590. is specified, the ZWCUL networkKey ist set, and the device supports the
  591. SECURITY class, then a secure inclusion is attempted. </li>
  592. <li>addNodeId &lt;decimalNodeId&gt;<br>
  593. Activate inclusion mode, and assign decimalNodeId to the next node.
  594. To deactivate this mode, use addNode off. Note: addNode won't work for a
  595. FHEM2FHEM:RAW attached ZWCUL, use addNodeId instead</li>
  596. <li>removeNode [onNw|on|off]<br>
  597. Activate (or deactivate) exclusion mode. Like with addNode, the CUL will
  598. switch temporarily to dataRate 9600, potentially missing some packets sent
  599. on higher dataRates. Note: the corresponding fhem device have to be
  600. deleted manually.</li>
  601. </ul>
  602. <br>
  603. <a name="ZWCULget"></a>
  604. <b>Get</b>
  605. <ul>
  606. <li>homeId<br>
  607. return the homeId and the ctrlId of the controller.</li>
  608. <li>nodeInfo<br>
  609. return node specific information. Needed by developers only.</li>
  610. <li>raw<br>
  611. Send raw data to the controller.</li>
  612. </ul>
  613. <br>
  614. <a name="ZWCULattr"></a>
  615. <b>Attributes</b>
  616. <ul>
  617. <li><a name="dataRate">dataRate</a> [40k|100k|9600]<br>
  618. specify the data rate.
  619. </li>
  620. <li><a href="#dummy">dummy</a></li>
  621. <li><a href="#do_not_notify">do_not_notify</a></li>
  622. <li><a href="#model">model</a></li>
  623. <li><a href="#disable">disable</a></li>
  624. <li><a href="#networkKey">networkKey</a></li>
  625. <li><a name="intruderMode">intruderMode</a><br>
  626. In monitor mode (see above) events are not dispatched to the ZWave module
  627. per default. Setting this attribute will allow to get decoded messages,
  628. and to send commands to devices not included by this controller.
  629. </li>
  630. <li>verbose<br>
  631. If the verbose attribute of this device (not global!) is set to 5 or
  632. higher, then detailed logging of the RF message will be done.
  633. </li>
  634. </ul>
  635. <br>
  636. <a name="ZWCULevents"></a>
  637. <b>Generated events: TODO</b>
  638. </ul>
  639. =end html
  640. =cut