00_ZWCUL.pm 21 KB

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