00_ZWDongle.pm 44 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400
  1. ##############################################
  2. # $Id: 00_ZWDongle.pm 17186 2018-08-20 20:10:55Z rudolfkoenig $
  3. package main;
  4. use strict;
  5. use warnings;
  6. use Time::HiRes qw(gettimeofday);
  7. use ZWLib;
  8. use vars qw($FW_ME);
  9. sub ZWDongle_Parse($$$);
  10. sub ZWDongle_Read($@);
  11. sub ZWDongle_ReadAnswer($$$);
  12. sub ZWDongle_Ready($);
  13. sub ZWDongle_Write($$$);
  14. sub ZWDongle_ProcessSendStack($);
  15. sub ZWDongle_NUCheck($$$$);
  16. # See also:
  17. # http://www.digiwave.dk/en/programming/an-introduction-to-the-z-wave-protocol/
  18. # http://open-zwave.googlecode.com/svn-history/r426/trunk/cpp/src/Driver.cpp
  19. # http://buzzdavidson.com/?p=68
  20. # https://bitbucket.org/bradsjm/aeonzstickdriver
  21. my %sets = (
  22. "addNode" => { cmd => "4a%02x@", # ZW_ADD_NODE_TO_NETWORK
  23. param => { onNw =>0xc1, on =>0x81, off=>0x05,
  24. onNwSec=>0xc1, onSec=>0x81 } },
  25. "backupCreate" => { cmd => "" },
  26. "backupRestore" => { cmd => "" },
  27. "clearStatistics" => { cmd => "39" }, # CLEAR_NETWORK_STATS
  28. "controllerChange" => { cmd => "4d%02x@", # ZW_CONTROLLER_CHANGE
  29. param => { on =>0x02, stop =>0x05,
  30. stopFailed =>0x06 } },
  31. "createNewPrimary" => { cmd => "4c%02x@", # ZW_CREATE_NEW_PRIMARY
  32. param => { on =>0x02, stop =>0x05,
  33. stopFailed =>0x06 } },
  34. "createNode" => { cmd => "60%02x" }, # ZW_REQUEST_NODE_INFO
  35. "createNodeSec" => { cmd => "60%02x" }, # ZW_REQUEST_NODE_INFO
  36. "factoryReset" => { cmd => "" }, # ZW_SET_DEFAULT
  37. "learnMode" => { cmd => "50%02x@", # ZW_SET_LEARN_MODE
  38. param => { onNw =>0x02, on =>0x01,
  39. disable=>0x00 } },
  40. "removeFailedNode" => { cmd => "61%02x@" }, # ZW_REMOVE_FAILED_NODE_ID
  41. "removeNode" => { cmd => "4b%02x@", # ZW_REMOVE_NODE_FROM_NETWORK
  42. param => {onNw=>0xc1, on=>0x81, off=>0x05 } },
  43. "reopen" => { cmd => "" },
  44. "replaceFailedNode"=> { cmd => "63%02x@" }, # ZW_REPLACE_FAILED_NODE
  45. "routeFor" => { cmd => "93%02x%02x%02x%02x%02x%02x" },
  46. # ZW_SET_PRIORITY_ROUTE
  47. "sendNIF" => { cmd => "12%02x05@" },# ZW_SEND_NODE_INFORMATION
  48. "setNIF" => { cmd => "03%02x%02x%02x%02x" },
  49. # SERIAL_API_APPL_NODE_INFORMATION
  50. "sucNodeId" => { cmd => "54%02x%02x00%02x@"},
  51. # ZW_SET_SUC_NODE_ID
  52. "sucRequestUpdate" => { cmd => "53%02x@"}, # ZW_REQUEST_NETWORK_UPDATE
  53. "sucSendNodeId" => { cmd => "57%02x25@"}, # ZW_SEND_SUC_ID
  54. "timeouts" => { cmd => "06%02x%02x" }, # SERIAL_API_SET_TIMEOUTS
  55. );
  56. my %gets = (
  57. "backgroundRSSI" => "3b", # GET_BACKGROUND_RSSI
  58. "caps" => "07", # SERIAL_API_GET_CAPABILITIES
  59. "ctrlCaps" => "05", # ZW_GET_CONTROLLER_CAPS
  60. "homeId" => "20", # MEMORY_GET_ID
  61. "isFailedNode" => "62%02x", # ZW_IS_FAILED_NODE
  62. "neighborList" => "80%02x", # GET_ROUTING_TABLE_LINE
  63. "nodeInfo" => "41%02x", # ZW_GET_NODE_PROTOCOL_INFO
  64. "nodeList" => "02", # SERIAL_API_GET_INIT_DATA
  65. "random" => "1c%02x", # ZW_GET_RANDOM
  66. "raw" => "%s", # hex
  67. "routeFor" => "92%02x", # hex
  68. "statistics" => "3a", # GET_NETWORK_STATS
  69. "sucNodeId" => "56", # ZW_GET_SUC_NODE_ID
  70. # "timeouts" => "06", # Forum #71333
  71. "version" => "15", # ZW_GET_VERSION
  72. );
  73. sub
  74. ZWDongle_Initialize($)
  75. {
  76. my ($hash) = @_;
  77. require "$attr{global}{modpath}/FHEM/DevIo.pm";
  78. # Provider
  79. $hash->{ReadFn} = "ZWDongle_Read";
  80. $hash->{WriteFn} = "ZWDongle_Write";
  81. $hash->{ReadyFn} = "ZWDongle_Ready";
  82. $hash->{ReadAnswerFn} = "ZWDongle_ReadAnswer";
  83. # Normal devices
  84. $hash->{DefFn} = "ZWDongle_Define";
  85. $hash->{SetFn} = "ZWDongle_Set";
  86. $hash->{GetFn} = "ZWDongle_Get";
  87. $hash->{AttrFn} = "ZWDongle_Attr";
  88. $hash->{UndefFn} = "ZWDongle_Undef";
  89. no warnings 'qw';
  90. my @attrList = qw(
  91. do_not_notify:1,0
  92. dummy:1,0
  93. model:ZWDongle
  94. disable:0,1
  95. helpSites:multiple,pepper,alliance
  96. homeId
  97. networkKey
  98. neighborListPos
  99. neighborListFmt
  100. showSetInState:1,0
  101. );
  102. use warnings 'qw';
  103. $hash->{AttrList} = join(" ", @attrList);
  104. $hash->{FW_detailFn} = "ZWDongle_fhemwebFn";
  105. }
  106. #####################################
  107. sub
  108. ZWDongle_Define($$)
  109. {
  110. my ($hash, $def) = @_;
  111. my @a = split("[ \t][ \t]*", $def);
  112. if(@a != 3) {
  113. my $msg = "wrong syntax: define <name> ZWDongle {none[:homeId] | ".
  114. "devicename[\@baudrate] | ".
  115. "devicename\@directio | ".
  116. "hostname:port}";
  117. return $msg;
  118. }
  119. DevIo_CloseDev($hash);
  120. my $name = $a[0];
  121. my $dev = $a[2];
  122. $hash->{Clients} = ":ZWave:";
  123. my %matchList = ( "1:ZWave" => ".*" );
  124. $hash->{MatchList} = \%matchList;
  125. if($dev =~ m/none:(.*)/) {
  126. $hash->{homeId} = $1;
  127. Log3 $name, 1,
  128. "$name device is none (homeId:$1), commands will be echoed only";
  129. $attr{$name}{dummy} = 1;
  130. readingsSingleUpdate($hash, "state", "dummy", 1);
  131. return undef;
  132. } elsif($dev !~ m/@/ && $dev !~ m/:/) {
  133. $dev .= "\@115200"; # default baudrate
  134. }
  135. $hash->{DeviceName} = $dev;
  136. $hash->{CallbackNr} = 0;
  137. $hash->{nrNAck} = 0;
  138. my @empty;
  139. $hash->{SendStack} = \@empty;
  140. ZWDongle_shiftSendStack($hash, 0, 5, undef); # Init variables
  141. my $ret = DevIo_OpenDev($hash, 0, "ZWDongle_DoInit");
  142. return $ret;
  143. }
  144. #####################################
  145. sub
  146. ZWDongle_fhemwebFn($$$$)
  147. {
  148. my ($FW_wname, $d, $room, $pageHash) = @_; # pageHash is set for summaryFn.
  149. my $js = "$FW_ME/pgm2/zwave_neighborlist.js";
  150. return
  151. "<div id='ZWDongleNr'><a id='zw_snm' href='#'>Show neighbor map</a></div>".
  152. "<div id='ZWDongleNrSVG'></div>".
  153. "<script type='text/javascript' src='$js'></script>".
  154. '<script type="text/javascript">'.<<"JSEND"
  155. \$(document).ready(function() {
  156. \$("div#ZWDongleNr a#zw_snm")
  157. .click(function(e){
  158. e.preventDefault();
  159. zw_nl('ZWDongle_nlData("$d")');
  160. });
  161. });
  162. </script>
  163. JSEND
  164. }
  165. sub
  166. ZWDongle_nlData($)
  167. {
  168. my ($d) = @_;
  169. my @a = devspec2array("TYPE=ZWave,FILTER=IODev=$d");
  170. my (@dn, %nb, @ret);
  171. my $fmt = eval AttrVal($d, "neighborListFmt",
  172. '{ txt=>"NAME", img=>"IMAGE", title=>"Time to ack: timeToAck" }');
  173. for my $e (@a) {
  174. my $h = $defs{$e};
  175. next if($h->{ZWaveSubDevice} ne "no");
  176. $h->{IMAGE} = ZWave_getPic($d, ReadingsVal($e, "modelId", ""));
  177. my $nl = ReadingsVal($e, "neighborList", "");
  178. $nl = ReadingsVal($d, "neighborList_".hex($h->{nodeIdHex}), "")
  179. if(!$nl);
  180. $nl =~ s/,/ /g; $nl =~ s/\bempty\b//g;
  181. push @dn, $e if($nl =~ m/\b$d\b/);
  182. $nl = '"'.join('","',split(" ", $nl)).'"' if($nl);
  183. my %line = (
  184. pos => '['.AttrVal($e, "neighborListPos", "").']',
  185. class => '"zwBox col_link col_oddrow"',
  186. neighbors => '['.$nl.']'
  187. );
  188. my $r = $h->{READINGS};
  189. my $a = $attr{$e};
  190. for my $key (keys %{$fmt}) {
  191. my $val = $fmt->{$key};
  192. $val =~ s/\b(\w+)\b/{ $h->{$1} ? $h->{$1} :
  193. $r->{$1} ? $r->{$1}{VAL} :
  194. $a->{$1} ? $a->{$1} : $1 }/ge;
  195. $line{$key} = "\"$val\"" if($val ne $fmt->{$key}); # Skip unchanged
  196. }
  197. push @ret, "\"$e\":{". join(',',map({"\"$_\":$line{$_}" } keys %line)) ."}";
  198. $nb{$e} = $nl;
  199. }
  200. my $pos = AttrVal($d, "neighborListPos", "");
  201. my $nl = (@dn ? '"'.join('","',@dn).'"' : '');
  202. push @ret, "\"$d\":{\"txt\":\"$d\", \"pos\":[$pos],".
  203. "\"class\":\"zwDongle col_oddrow col_link\",\"neighbors\":[$nl] }";
  204. return "{ \"saveFn\":\"attr {1} neighborListPos {2}\",".
  205. "\"firstObj\":\"$d\",".
  206. "\"el\":{".join(",",@ret)."} }";
  207. }
  208. #####################################
  209. sub
  210. ZWDongle_Undef($$)
  211. {
  212. my ($hash,$arg) = @_;
  213. DevIo_CloseDev($hash);
  214. return undef;
  215. }
  216. #####################################
  217. sub
  218. ZWDongle_Set($@)
  219. {
  220. my ($hash, @a) = @_;
  221. my $name = shift @a;
  222. return "\"set ZWDongle\" needs at least one parameter" if(@a < 1);
  223. my $type = shift @a;
  224. if(!defined($sets{$type})) {
  225. my @r;
  226. map { my $p = $sets{$_}{param};
  227. push @r,($p ? "$_:".join(",",sort keys %{$p}) : $_)} sort keys %sets;
  228. return "Unknown argument $type, choose one of " . join(" ",@r);
  229. }
  230. Log3 $hash, 4, "ZWDongle *** set $name $type ".join(" ",@a);
  231. if($type eq "reopen") {
  232. return if(AttrVal($name, "dummy",undef) || AttrVal($name, "disable",undef));
  233. delete $hash->{NEXT_OPEN};
  234. DevIo_CloseDev($hash);
  235. sleep(1);
  236. DevIo_OpenDev($hash, 0, "ZWDongle_DoInit");
  237. return;
  238. }
  239. if($type eq "backupCreate") {
  240. my $caps = ReadingsVal($name, "caps","");
  241. my $is4 = ($caps =~ m/MEMORY_GET_BUFFER/);
  242. my $is5 = ($caps =~ m/NVM_EXT_READ_LONG_BUFFER/);
  243. return "Creating a backup is not supported by this device"
  244. if(!$is4 && !$is5);
  245. return "Usage: set $name backupCreate [16k|32k|64k|128k|256k]"
  246. if(int(@a) != 1 || $a[0] !~ m/^(16|32|64|128|256)k$/);
  247. my $fn = ($is5 ? "NVM_EXT_READ_LONG_BUFFER" : "MEMORY_GET_BUFFER");
  248. my $cmdFormat = ($is5 ? "002a%06x0040" : "0023%04x20");
  249. my $cmdRe = ($is5 ? "^012a" : "^0123");
  250. my $inc = ($is5 ? 64 : 32);
  251. my $l = $1 * 1024;
  252. my $fName = "$attr{global}{modpath}/$name.bin";
  253. open(OUT, ">$fName") || return "Cant open $fName: $!";
  254. binmode(OUT);
  255. for(my $off = 0; $off < $l;) {
  256. ZWDongle_Write($hash, "", sprintf($cmdFormat, $off));
  257. my ($err, $ret) = ZWDongle_ReadAnswer($hash, $fn, $cmdRe);
  258. return $err if($err);
  259. print OUT pack('H*', substr($ret, 4));
  260. $off += $inc;
  261. Log 3, "$name backupCreate at $off bytes" if($off % 16384 == 0);
  262. }
  263. close(OUT);
  264. return "Wrote $l bytes to $fName";
  265. }
  266. if($type eq "backupRestore") {
  267. my $caps = ReadingsVal($name, "caps","");
  268. my $is4 = ($caps =~ m/MEMORY_PUT_BUFFER/);
  269. my $is5 = ($caps =~ m/NVM_EXT_WRITE_LONG_BUFFER/);
  270. return "Restoring a backup is not supported by this device"
  271. if(!$is4 && !$is5);
  272. my $fn = ($is5 ? "NVM_EXT_WRITE_LONG_BUFFER" : "MEMORY_PUT_BUFFER");
  273. my $cmdFormat = ($is5 ? "002b%06x0040%s" : "0024%04x40%s");
  274. my $cmdRe = ($is5 ? "^012b" : "^0124");
  275. my $cmdRet = ($is5 ? "^012b01" : "^012401");
  276. my $inc = ($is5 ? 64 : 32);
  277. return "Usage: set $name backupRestore" if(int(@a) != 0);
  278. my $fName = "$attr{global}{modpath}/$name.bin";
  279. my $l = -s $fName;
  280. return "$fName does not exists, or is empty" if(!$l);
  281. open(IN, $fName) || return "Cant open $fName: $!";
  282. binmode(IN);
  283. my $buf;
  284. for(my $off = 0; $off < $l;) {
  285. if(sysread(IN, $buf, $inc) != $inc) {
  286. return "Cant read $inc bytes from $fName";
  287. }
  288. ZWDongle_Write($hash, "", sprintf($cmdFormat, $off, unpack('H*',$buf)));
  289. my ($err, $ret) = ZWDongle_ReadAnswer($hash, $fn, $cmdRe);
  290. return $err if($err);
  291. return "Unexpected $fn return value $ret"
  292. if($ret !~ m/$cmdRet/);
  293. $off += $inc;
  294. Log 3, "$name backupRestore at $off bytes" if($off % 16384 == 0);
  295. }
  296. close(IN);
  297. return "Restored $l bytes from $fName";
  298. }
  299. if($type eq "factoryReset") {
  300. return "Reset to default is not supported by this device"
  301. if(ReadingsVal($name, "caps","") !~ m/ZW_SET_DEFAULT/);
  302. return "Read commandref before use! -> Usage: set $name factoryReset yes"
  303. if(int(@a) != 1 || $a[0] !~ m/^(yes)$/);
  304. ZWDongle_Write($hash,"","0042");
  305. return "Reseted $name to factory default and assigned new random HomeId";
  306. }
  307. if($type eq "removeFailedNode" ||
  308. $type eq "replaceFailedNode" ||
  309. $type =~ m/^createNode/ ||
  310. $type eq "sendNIF") {
  311. $a[0] =~ s/^UNKNOWN_//;
  312. $a[0] = hex($defs{$a[0]}{nodeIdHex})
  313. if($defs{$a[0]} && $defs{$a[0]}{nodeIdHex});
  314. }
  315. my $cmd = $sets{$type}{cmd};
  316. my $fb = substr($cmd, 0, 2);
  317. if($fb =~ m/^[0-8A-F]+$/i &&
  318. ReadingsVal($name, "caps","") !~ m/\b$zw_func_id{$fb}\b/) {
  319. return "$type is unsupported by this controller";
  320. }
  321. delete($hash->{addSecure});
  322. $hash->{addSecure} = 1 if($type eq "createNodeSec");
  323. if($type eq "addNode") {
  324. $hash->{addSecure} = 1 if($a[0] && $a[0] =~ m/sec/i);
  325. if($a[0]) { # Remember the client for the failed message
  326. if($a[0] eq "off") {
  327. delete($hash->{addCL});
  328. } elsif($hash->{CL}) {
  329. $hash->{addCL} = $hash->{CL};
  330. }
  331. }
  332. }
  333. if($type eq "routeFor") {
  334. for(@a = @a) {
  335. $_ =~ s/^UNKNOWN_//;
  336. $_ = hex($defs{$_}{nodeIdHex})
  337. if($defs{$_} && $defs{$_}{nodeIdHex});
  338. return "$_ is neither a device nor a decimal id" if($_ !~ m/\d+/);
  339. }
  340. }
  341. my $par = $sets{$type}{param};
  342. if($par && !$par->{noArg}) {
  343. return "Unknown argument for $type, choose one of ".join(" ",keys %{$par})
  344. if(!$a[0] || !defined($par->{$a[0]}));
  345. $a[0] = $par->{$a[0]};
  346. }
  347. if($cmd =~ m/\@/) {
  348. my $c = $hash->{CallbackNr}+1;
  349. $c = 1 if($c > 255);
  350. $hash->{CallbackNr} = $c;
  351. $c = sprintf("%02x", $c);
  352. $cmd =~ s/\@/$c/g;
  353. }
  354. my @ca = split("%", $cmd, -1);
  355. my $nargs = int(@ca)-1;
  356. return "set $name $type needs $nargs arguments" if($nargs != int(@a));
  357. ZWDongle_Write($hash, "", "00".sprintf($cmd, @a));
  358. return undef;
  359. }
  360. #####################################
  361. sub
  362. ZWDongle_Get($@)
  363. {
  364. my ($hash, @a) = @_;
  365. my $name = shift @a;
  366. return "\"get $name\" needs at least one parameter" if(@a < 1);
  367. my $cmd = shift @a;
  368. return "Unknown argument $cmd, choose one of " .
  369. join(" ", map { $gets{$_} =~ m/%/ ? $_ : "$_:noArg" } sort keys %gets)
  370. if(!defined($gets{$cmd}));
  371. my $fb = substr($gets{$cmd}, 0, 2);
  372. if($fb =~ m/^[0-8A-F]+$/i && $cmd ne "caps" &&
  373. ReadingsVal($name, "caps","") !~ m/\b$zw_func_id{$fb}\b/) {
  374. return "$cmd is unsupported by this controller";
  375. }
  376. if($cmd eq "raw") {
  377. if($a[0] =~ s/^42//) {
  378. Log3 $hash, 4, "ZWDongle *** get $name $cmd 42".join(" ",@a)." blocked";
  379. return "raw 0x42 (ZW_SET_DEFAULT) blocked. Read commandref first and ".
  380. "use instead: set $name factoryReset";
  381. }
  382. }
  383. Log3 $hash, 4, "ZWDongle *** get $name $cmd ".join(" ",@a);
  384. if($cmd eq "neighborList") {
  385. my @b;
  386. @b = grep(!/onlyRep/i, @a); my $onlyRep = (@b != @a); @a = @b;
  387. @b = grep(!/excludeDead/i, @a); my $exclDead = (@b != @a); @a = @b;
  388. $gets{neighborList} = "80%02x".($exclDead ?"00":"01").($onlyRep ?"01":"00");
  389. return "Usage: get $name $cmd [excludeDead] [onlyRep] nodeId"
  390. if(int(@a) != 1);
  391. }
  392. my @ga = split("%", $gets{$cmd}, -1);
  393. my $nargs = int(@ga)-1;
  394. return "get $name $cmd needs $nargs arguments" if($nargs != int(@a));
  395. return "No $cmd for dummies" if(IsDummy($name));
  396. my $a0 = $a[0];
  397. if($cmd eq "neighborList" ||
  398. $cmd eq "nodeInfo" ||
  399. $cmd eq "routeFor" ||
  400. $cmd eq "isFailedNode") {
  401. $a[0] =~ s/^UNKNOWN_//;
  402. $a[0] = hex($defs{$a[0]}{nodeIdHex})
  403. if($defs{$a[0]} && $defs{$a[0]}{nodeIdHex});
  404. }
  405. my $out = sprintf($gets{$cmd}, @a);
  406. ZWDongle_Write($hash, "", "00".$out);
  407. my $re = "^01".substr($out,0,2); # Start with <01><len><01><CMD>
  408. my ($err, $ret) = ZWDongle_ReadAnswer($hash, $cmd, $re);
  409. return $err if($err);
  410. my $msg="";
  411. $a[0] = $a0 if(defined($a0));
  412. $msg = $ret if($ret);
  413. my @r = map { ord($_) } split("", pack('H*', $ret)) if(defined($ret));
  414. if($cmd eq "nodeList") { ############################
  415. $msg =~ s/^.{10}(.{58}).*/$1/;
  416. $msg = zwlib_parseNeighborList($hash, $msg);
  417. } elsif($cmd eq "caps") { ############################
  418. $msg = sprintf("Vers:%d Rev:%d ", $r[2], $r[3]);
  419. $msg .= sprintf("ManufID:%02x%02x ", $r[4], $r[5]);
  420. $msg .= sprintf("ProductType:%02x%02x ", $r[6], $r[7]);
  421. $msg .= sprintf("ProductID:%02x%02x", $r[8], $r[9]);
  422. my @list;
  423. for my $byte (0..31) {
  424. my $bits = $r[10+$byte];
  425. for my $bit (0..7) {
  426. my $id = sprintf("%02x", $byte*8+$bit+1);
  427. push @list, ($zw_func_id{$id} ? $zw_func_id{$id} : "UNKNOWN_$id")
  428. if($bits & (1<<$bit));
  429. }
  430. }
  431. $msg .= " ".join(" ",@list);
  432. } elsif($cmd eq "homeId") { ############################
  433. $msg = sprintf("HomeId:%s CtrlNodeIdHex:%s",
  434. substr($ret,4,8), substr($ret,12,2));
  435. $hash->{homeId} = substr($ret,4,8);
  436. $hash->{nodeIdHex} = substr($ret,12,2);
  437. $attr{$name}{homeId} = substr($ret,4,8);
  438. } elsif($cmd eq "version") { ############################
  439. $msg = join("", map { chr($_) } @r[2..13]);
  440. my @type = qw( STATIC_CONTROLLER CONTROLLER ENHANCED_SLAVE
  441. SLAVE INSTALLER NO_INTELLIGENT_LIFE BRIDGE_CONTROLLER);
  442. my $idx = $r[14]-1;
  443. $msg .= " $type[$idx]" if($idx >= 0 && $idx <= $#type);
  444. } elsif($cmd eq "ctrlCaps") { ############################
  445. my @type = qw(SECONDARY OTHER MEMBER PRIMARY SUC);
  446. my @list;
  447. for my $bit (0..7) {
  448. push @list, $type[$bit] if(($r[2] & (1<<$bit)) && $bit < @type);
  449. }
  450. $msg = join(" ", @list);
  451. } elsif($cmd eq "nodeInfo") { ############################
  452. if($r[6] == 0) {
  453. $msg = "node $a[0] is not present";
  454. } else {
  455. $msg = zwlib_parseNodeInfo(@r);
  456. }
  457. } elsif($cmd eq "random") { ############################
  458. return "$name: Cannot generate" if($ret !~ m/^011c01(..)(.*)$/);
  459. $msg = $2; @a = ();
  460. } elsif($cmd eq "isFailedNode") { ############################
  461. $msg = ($r[2]==1)?"yes":"no";
  462. } elsif($cmd eq "neighborList") { ############################
  463. $msg =~ s/^....//;
  464. $msg = zwlib_parseNeighborList($hash, $msg);
  465. } elsif($cmd eq "sucNodeId") { ############################
  466. $msg = ($r[2]==0)?"no":$r[2];
  467. } elsif($cmd eq "routeFor") { ############################
  468. my $homeId = $hash->{homeId};
  469. my @list;
  470. my $e = hex(substr($msg, 6, 2));
  471. push @list, ($e==1 ? "last": ($e==2 ? "next":"application"))
  472. if($e !=0);
  473. for(my $off=8; $off<16; $off+=2) {
  474. my $dec = hex(substr($msg, $off, 2));
  475. my $hex = sprintf("%02x", $dec);
  476. my $h = ($hex eq $hash->{nodeIdHex} ?
  477. $hash : $modules{ZWave}{defptr}{"$homeId $hex"});
  478. push @list, ($h ? $h->{NAME} : "UNKNOWN_$dec") if($dec);
  479. }
  480. my $f = substr($msg, 17, 1);
  481. push @list, ("at ".($f==1 ? "9.6": ($f==2 ? "40":"100"))."kbps")
  482. if(@list && $f =~ m/[123]/);
  483. $msg = (@list ? join(" ", @list) : "N/A");
  484. } elsif($cmd eq "backgroundRSSI") { ############################
  485. my @list;
  486. my $i=0;
  487. my $maxlen = (length($msg) >= 10 ? 10 : length($msg));
  488. for(my $off=4; $off<$maxlen; $off+=2) {
  489. my $dec = hex(substr($msg, $off, 2));
  490. if($dec == 127) {
  491. push @list, ("ch".($i+1).":N/A");
  492. } elsif($dec == 126) {
  493. push @list, ("ch".($i+1).":aboveMaxPower");
  494. } elsif($dec == 125) {
  495. push @list, ("ch".($i+1).":belowReceiverSensitivity");
  496. } elsif($dec > 161 && $dec < 225) {
  497. push @list, ("ch".($i+1).":".unpack('c', pack('C', $dec))." dBm");
  498. } else {
  499. push @list, ("ch".($i+1).":reservedValue");
  500. }
  501. $i++
  502. }
  503. $msg = join(" ", @list);
  504. } elsif($cmd eq "statistics") { ############################
  505. $msg = sprintf("Transmitted:%s BackOffs:%s ReceivedNoErrors:%s
  506. ChecksumErrors:%s CRC16Errors:%s ForeignHomeId:%s",
  507. hex(substr($ret,4,4)), hex(substr($ret,8,4)),
  508. hex(substr($ret,12,4)), hex(substr($ret,16,4)),
  509. hex(substr($ret,20,4)), hex(substr($ret,24,4)));
  510. }
  511. $cmd .= "_".join("_", @a) if(@a);
  512. readingsSingleUpdate($hash, $cmd, $msg, 0);
  513. return "$name $cmd => $msg";
  514. }
  515. #####################################
  516. sub
  517. ZWDongle_Clear($)
  518. {
  519. my $hash = shift;
  520. # Clear the pipe
  521. for(;;) {
  522. my ($err, undef) = ZWDongle_ReadAnswer($hash, "Clear", "wontmatch");
  523. last if($err && ($err =~ m/^Timeout/ || $err =~ m/No FD/));
  524. }
  525. $hash->{PARTIAL} = "";
  526. }
  527. #####################################
  528. sub
  529. ZWDongle_DoInit($)
  530. {
  531. my $hash = shift;
  532. my $name = $hash->{NAME};
  533. DevIo_SetHwHandshake($hash) if($hash->{USBDev});
  534. $hash->{PARTIAL} = "";
  535. ZWDongle_Clear($hash);
  536. ZWDongle_Get($hash, $name, "caps");
  537. ZWDongle_Get($hash, $name, "ctrlCaps");
  538. ZWDongle_Get($hash, $name, "homeId");
  539. ZWDongle_Get($hash, $name, "sucNodeId");
  540. ZWDongle_Get($hash, $name, ("random", 32)); # Sec relevant
  541. ZWDongle_Set($hash, $name, ("timeouts", 100, 15)); # Sec relevant
  542. ZWDongle_ReadAnswer($hash, "timeouts", "^0106");
  543. # NODEINFO_LISTENING, Generic Static controller, Specific Static Controller, 0
  544. ZWDongle_Set($hash, $name, ("setNIF", 1, 2, 1, 0)); # Sec relevant (?)
  545. readingsSingleUpdate($hash, "state", "Initialized", 1);
  546. return undef;
  547. }
  548. #####################################
  549. # neighborUpdate special: have to serialize. Forum #54574
  550. my @nuStack;
  551. sub
  552. ZWDongle_NUCheck($$$$)
  553. {
  554. my($hash, $fn, $msg, $isWrite) = @_;
  555. if($isWrite) {
  556. return 0 if($msg !~ m/^0048/ || $hash->{calledFromNuCheck});
  557. push @nuStack, "$fn/$msg";
  558. if(@nuStack == 1) {
  559. InternalTimer(gettimeofday+20, sub { # ZME timeout is 9-11s
  560. ZWDongle_NUCheck($hash, undef, "0048xx23", 0); # simulate fail
  561. }, \@nuStack, 0);
  562. }
  563. return (@nuStack > 1);
  564. } else {
  565. return if($msg !~ m/^0048..(..)$/ || $1 eq "21"); # 21: started
  566. shift @nuStack;
  567. RemoveInternalTimer(\@nuStack);
  568. return if(@nuStack == 0);
  569. my @a = split("/", $nuStack[0]);
  570. $hash->{calledFromNuCheck} = 1;
  571. ZWDongle_Write($hash, $a[0], $a[1]);
  572. delete($hash->{calledFromNuCheck});
  573. InternalTimer(gettimeofday+20, sub {
  574. ZWDongle_NUCheck($hash, undef, "0048xx23", 0); # simulate fail
  575. }, \@nuStack, 0);
  576. }
  577. }
  578. #####################################
  579. sub
  580. ZWDongle_Write($$$)
  581. {
  582. my ($hash,$fn,$msg) = @_;
  583. return if(ZWDongle_NUCheck($hash, $fn, $msg, 1));
  584. Log3 $hash, 5, "ZWDongle_Write $msg ($fn)";
  585. # assemble complete message
  586. $msg = sprintf("%02x%s", length($msg)/2+1, $msg);
  587. $msg = "01$msg" . zwlib_checkSum_8($msg);
  588. push @{$hash->{SendStack}}, $msg;
  589. ZWDongle_ProcessSendStack($hash);
  590. }
  591. # Flags:
  592. # - WaitForAck: 0:Written, 1:SerialACK received, 2:RF-Sent
  593. # - SendRetries < MaxSendRetries(3, up to 7 when receiving CAN)
  594. sub
  595. ZWDongle_shiftSendStack($$$$;$)
  596. {
  597. my ($hash, $reason, $loglevel, $txt, $cbId) = @_;
  598. my $ss = $hash->{SendStack};
  599. my $cmd = $ss->[0];
  600. if($cmd && $reason==0 && $cmd =~ m/^01..0013/) { # ACK for SEND_DATA
  601. Log3 $hash, $loglevel, "$txt, WaitForAck=>2 for $cmd"
  602. if($txt);
  603. $hash->{WaitForAck}=2;
  604. } else {
  605. return if($cbId && $cmd && $cbId ne substr($cmd,-4,2));
  606. shift @{$ss};
  607. Log3 $hash, $loglevel, "$txt, removing $cmd from dongle sendstack"
  608. if($txt && $cmd);
  609. $hash->{WaitForAck}=0;
  610. $hash->{SendRetries}=0;
  611. $hash->{MaxSendRetries}=3;
  612. delete($hash->{GotCAN});
  613. }
  614. }
  615. sub
  616. ZWDongle_ProcessSendStack($)
  617. {
  618. my ($hash) = @_;
  619. #Log3 $hash, 1, "ZWDongle_ProcessSendStack: ".@{$hash->{SendStack}}.
  620. # " items on stack, waitForAck ".$hash->{WaitForAck};
  621. RemoveInternalTimer($hash);
  622. my $ts = gettimeofday();
  623. if($hash->{WaitForAck}){
  624. if($hash->{WaitForAck} == 1 && $ts-$hash->{SendTime} >= 1) {
  625. Log3 $hash, 2, "ZWDongle_ProcessSendStack: no ACK, resending message ".
  626. $hash->{SendStack}->[0];
  627. $hash->{SendRetries}++;
  628. $hash->{WaitForAck} = 0;
  629. } elsif($hash->{WaitForAck} == 2 && $ts-$hash->{SendTime} >= 2) {
  630. ZWDongle_shiftSendStack($hash, 1, 4, "no response from device");
  631. } else {
  632. InternalTimer($ts+1, "ZWDongle_ProcessSendStack", $hash, 0);
  633. return;
  634. }
  635. }
  636. if($hash->{SendRetries} > $hash->{MaxSendRetries}){
  637. ZWDongle_shiftSendStack($hash, 1, 1, "ERROR: max send retries reached");
  638. }
  639. return if(!@{$hash->{SendStack}} ||
  640. $hash->{WaitForAck} ||
  641. !DevIo_IsOpen($hash));
  642. my $msg = $hash->{SendStack}->[0];
  643. DevIo_SimpleWrite($hash, $msg, 1);
  644. $hash->{WaitForAck} = 1;
  645. $hash->{SendTime} = $ts;
  646. delete($hash->{GotCAN});
  647. InternalTimer($ts+1, "ZWDongle_ProcessSendStack", $hash, 0);
  648. }
  649. #####################################
  650. # called from the global loop, when the select for hash->{FD} reports data
  651. sub
  652. ZWDongle_Read($@)
  653. {
  654. my ($hash, $local, $regexp) = @_;
  655. my $buf = (defined($local) ? $local : DevIo_SimpleRead($hash));
  656. return "" if(!defined($buf));
  657. my $name = $hash->{NAME};
  658. $buf = unpack('H*', $buf);
  659. # The dongle looses data over USB for some commands(?), and dropping the old
  660. # buffer after a timeout is my only idea of solving this problem.
  661. my $ts = gettimeofday();
  662. my $data = ($hash->{ReadTime} && $ts-$hash->{ReadTime} > 1) ?
  663. $buf : $hash->{PARTIAL}.$buf;
  664. $hash->{ReadTime} = $ts;
  665. #Log3 $name, 5, "ZWDongle RAW buffer: $data";
  666. my $msg;
  667. while(length($data) >= 2) {
  668. my $fb = substr($data, 0, 2);
  669. if($fb eq "06") { # ACK
  670. ZWDongle_shiftSendStack($hash, 0, 5, "ACK received");
  671. $data = substr($data, 2);
  672. next;
  673. }
  674. if($fb eq "15") { # NACK
  675. Log3 $name, 4, "ZWDongle_Read $name: NACK received";
  676. $hash->{WaitForAck} = 0;
  677. $hash->{SendRetries}++;
  678. $data = substr($data, 2);
  679. next;
  680. }
  681. if($fb eq "18") { # CAN
  682. Log3 $name, 4, "ZWDongle_Read $name: CAN received";
  683. $hash->{MaxSendRetries}++ if($hash->{MaxSendRetries}<7);
  684. $data = substr($data, 2);
  685. $hash->{GotCAN} = 1;
  686. if(!$init_done) { # InternalTimer wont work
  687. $hash->{WaitForAck} = 0;
  688. $hash->{SendRetries}++;
  689. select(undef, undef, undef, 0.1);
  690. }
  691. next;
  692. }
  693. if($fb ne "01") { # SOF
  694. Log3 $name, 1, "$name: SOF missing (got $fb instead of 01)";
  695. if(++$hash->{nrNAck} < 5){
  696. Log3 $name, 5, "ZWDongle_Read SOF Error -> sending NACK";
  697. DevIo_SimpleWrite($hash, "15", 1); # Send NACK
  698. }
  699. $data="";
  700. last;
  701. }
  702. last if(length($data) < 4);
  703. my $len = substr($data, 2, 2);
  704. my $l = hex($len)*2;
  705. last if(length($data) < $l+4); # Message not yet complete
  706. if($l < 4) { # Bogus messages, forget the rest
  707. $data = "";
  708. last;
  709. }
  710. $msg = substr($data, 4, $l-2);
  711. my $rcs = substr($data, $l+2, 2); # Received Checksum
  712. $data = substr($data, $l+4);
  713. my $ccs = zwlib_checkSum_8("$len$msg"); # Computed Checksum
  714. if($rcs ne $ccs) {
  715. Log3 $name, 1,
  716. "$name: wrong checksum: received $rcs, computed $ccs for $len$msg";
  717. if(++$hash->{nrNAck} < 5) {
  718. Log3 $name, 5, "ZWDongle_Read wrong checksum -> sending NACK";
  719. DevIo_SimpleWrite($hash, "15", 1);
  720. }
  721. $msg = undef;
  722. $data="";
  723. next;
  724. }
  725. $hash->{nrNAck} = 0;
  726. next if($msg !~ m/^(..)(..)/);
  727. my $ztp = ($1 eq "00" ? "request" : ($1 eq "01" ? "answer" : "unknown $1"));
  728. my $zfi = $zw_func_id{$2} ? $zw_func_id{$2} : "unknown $2";
  729. Log3 $name, 4, "ZWDongle_Read $name: rcvd $msg ($ztp $zfi), sending ACK";
  730. DevIo_SimpleWrite($hash, "06", 1);
  731. ZWDongle_shiftSendStack($hash, 1, 5, "device ack reveived", $1)
  732. if($msg =~ m/^0013(..)/);
  733. last if(defined($local) && (!defined($regexp) || ($msg =~ m/$regexp/)));
  734. $hash->{PARTIAL} = $data; # Recursive call by ZWave get, Forum #37418
  735. ZWDongle_Parse($hash, $name, $msg) if($init_done);
  736. $data = $hash->{PARTIAL};
  737. $msg = undef;
  738. }
  739. $hash->{PARTIAL} = $data;
  740. # trigger sending of next message
  741. ZWDongle_ProcessSendStack($hash) if(length($data) == 0);
  742. return $msg if(defined($local));
  743. return undef;
  744. }
  745. #####################################
  746. # This is a direct read for commands like get
  747. sub
  748. ZWDongle_ReadAnswer($$$)
  749. {
  750. my ($hash, $arg, $regexp) = @_;
  751. Log3 $hash, 4, "ZWDongle_ReadAnswer arg:$arg regexp:".($regexp ? $regexp:"");
  752. return ("No FD (dummy device?)", undef)
  753. if(!$hash || ($^O !~ /Win/ && !defined($hash->{FD})));
  754. my $to = ($hash->{RA_Timeout} ? $hash->{RA_Timeout} : 1);
  755. for(;;) {
  756. my $buf;
  757. if($^O =~ m/Win/ && $hash->{USBDev}) {
  758. $hash->{USBDev}->read_const_time($to*1000); # set timeout (ms)
  759. # Read anstatt input sonst funzt read_const_time nicht.
  760. $buf = $hash->{USBDev}->read(999);
  761. if(length($buf) == 0) {
  762. if($hash->{GotCAN}) {
  763. ZWDongle_ProcessSendStack($hash);
  764. next;
  765. }
  766. return ("Timeout reading answer for get $arg", undef);
  767. }
  768. } else {
  769. if(!$hash->{FD}) {
  770. Log3 $hash, 1, "ZWDongle_ReadAnswer: device lost";
  771. return ("Device lost when reading answer for get $arg", undef);
  772. }
  773. my $rin = '';
  774. vec($rin, $hash->{FD}, 1) = 1;
  775. my $nfound = select($rin, undef, undef, $to);
  776. if($nfound < 0) {
  777. my $err = $!;
  778. Log3 $hash, 5, "ZWDongle_ReadAnswer: nfound < 0 / err:$err";
  779. next if ($err == EAGAIN() || $err == EINTR() || $err == 0);
  780. DevIo_Disconnected($hash);
  781. return("ZWDongle_ReadAnswer $arg: $err", undef);
  782. }
  783. if($nfound == 0){
  784. Log3 $hash, 5, "ZWDongle_ReadAnswer: select timeout";
  785. if($hash->{GotCAN}) {
  786. ZWDongle_ProcessSendStack($hash);
  787. next;
  788. }
  789. return ("Timeout reading answer for get $arg", undef);
  790. }
  791. $buf = DevIo_SimpleRead($hash);
  792. if(!defined($buf)){
  793. Log3 $hash, 1,"ZWDongle_ReadAnswer: no data read";
  794. return ("No data", undef);
  795. }
  796. }
  797. my $ret = ZWDongle_Read($hash, $buf, $regexp);
  798. if(defined($ret)){
  799. Log3 $hash, 4, "ZWDongle_ReadAnswer for $arg: $ret";
  800. return (undef, $ret);
  801. }
  802. }
  803. }
  804. sub
  805. ZWDongle_Parse($$$)
  806. {
  807. my ($hash, $name, $rmsg) = @_;
  808. if(!defined($hash->{STATE}) ||
  809. ReadingsVal($name, "state", "") ne "Initialized"){
  810. Log3 $hash, 4,"ZWDongle_Parse $rmsg: dongle not yet initialized";
  811. return;
  812. }
  813. $hash->{"${name}_MSGCNT"}++;
  814. $hash->{"${name}_TIME"} = TimeNow();
  815. $hash->{RAWMSG} = $rmsg;
  816. $hash->{SendTime}-- # Retry sending after a "real" msg from the dongle
  817. if($hash->{GotCAN} && $rmsg !~ m/^(0113|0013)/);
  818. my %addvals = (RAWMSG => $rmsg);
  819. ZWDongle_NUCheck($hash, undef, $rmsg, 0);
  820. Dispatch($hash, $rmsg, \%addvals);
  821. }
  822. #####################################
  823. sub
  824. ZWDongle_Attr($$$$)
  825. {
  826. my ($cmd, $name, $attr, $value) = @_;
  827. my $hash = $defs{$name};
  828. $attr = "" if(!$attr);
  829. if($attr eq "disable") {
  830. if($cmd eq "set" && ($value || !defined($value))) {
  831. DevIo_CloseDev($hash) if(!AttrVal($name,"dummy",undef));
  832. readingsSingleUpdate($hash, "state", "disabled", 1);
  833. } else {
  834. if(AttrVal($name,"dummy",undef)) {
  835. readingsSingleUpdate($hash, "state", "dummy", 1);
  836. return;
  837. }
  838. DevIo_OpenDev($hash, 0, "ZWDongle_DoInit");
  839. }
  840. } elsif($attr eq "homeId" && $cmd eq "set") {
  841. $hash->{homeId} = $value;
  842. } elsif($attr eq "networkKey" && $cmd eq "set") {
  843. if(!$value || $value !~ m/^[0-9A-F]{32}$/i) {
  844. return "attr $name networkKey: not a hex string with a length of 32";
  845. }
  846. return;
  847. } elsif($attr eq "showSetInState") {
  848. $hash->{showSetInState} = ($cmd eq "set" ? (defined($value) ? $value:1) :0);
  849. }
  850. return undef;
  851. }
  852. #####################################
  853. sub
  854. ZWDongle_Ready($)
  855. {
  856. my ($hash) = @_;
  857. return undef if (IsDisabled($hash->{NAME}));
  858. return DevIo_OpenDev($hash, 1, "ZWDongle_DoInit")
  859. if(ReadingsVal($hash->{NAME}, "state","") eq "disconnected");
  860. # This is relevant for windows/USB only
  861. my $po = $hash->{USBDev};
  862. if($po) {
  863. my ($BlockingFlags, $InBytes, $OutBytes, $ErrorFlags) = $po->status;
  864. if(!defined($InBytes)) {
  865. DevIo_Disconnected($hash);
  866. return 0;
  867. }
  868. return ($InBytes>0);
  869. }
  870. return 0;
  871. }
  872. 1;
  873. =pod
  874. =item summary connection to standard ZWave controller
  875. =item summary_DE Anbindung von standard ZWave Controller
  876. =begin html
  877. <a name="ZWDongle"></a>
  878. <h3>ZWDongle</h3>
  879. <ul>
  880. This module serves a ZWave dongle, which is attached via USB or TCP/IP, and
  881. enables the use of ZWave devices (see also the <a href="#ZWave">ZWave</a>
  882. module). It was tested wit a Goodway WD6001, but since the protocol is
  883. standardized, it should work with other devices too. A notable exception is
  884. the USB device from Merten.
  885. <br><br>
  886. <a name="ZWDongledefine"></a>
  887. <b>Define</b>
  888. <ul>
  889. <code>define &lt;name&gt; ZWDongle &lt;device&gt;</code>
  890. <br>
  891. <br>
  892. Upon initial connection the module will get the homeId of the attached
  893. device. Since the DevIo module is used to open the device, you can also use
  894. devices connected via TCP/IP. See <a href="#CULdefine">this</a> paragraph on
  895. device naming details.
  896. <br>
  897. Example:
  898. <ul>
  899. <code>define zwdongle_1 ZWDongle /dev/cu.PL2303-000014FA@115200</code><br>
  900. </ul>
  901. </ul>
  902. <br>
  903. <a name="ZWDongleset"></a>
  904. <b>Set</b>
  905. <ul>
  906. <li>addNode on|onNw|onSec|onNwSec|off<br>
  907. Activate (or deactivate) inclusion mode. The controller (i.e. the dongle)
  908. will accept inclusion (i.e. pairing/learning) requests only while in this
  909. mode. After activating inclusion mode usually you have to press a switch
  910. three times within 1.5 seconds on the node to be included into the network
  911. of the controller. If autocreate is active, a fhem device will be created
  912. after inclusion. "on" activates standard inclusion. "onNw" activates network
  913. wide inclusion (only SDK 4.5-4.9, SDK 6.x and above).<br>
  914. If onSec/onNwSec is specified, the ZWDongle networkKey ist set, and the
  915. device supports the SECURITY class, then a secure inclusion is attempted.
  916. </li>
  917. <li>backupCreate 16k|32k|64k|128k|256k<br>
  918. read out the NVRAM of the ZWDongle, and store it in a file called
  919. &lt;ZWDongle_Name&gt;.bin in the modpath folder. Since the size of the
  920. NVRAM is currently unknown to FHEM, you have to specify the size. The
  921. ZWave.me ZME_UZB1 Stick seems to have 256k of NVRAM. Note: writing the file
  922. takes some time, usually about 10s for each 64k (and significantly longer
  923. on Windows), and FHEM is blocked during this time.
  924. </li>
  925. <li>backupRestore<br>
  926. Restore the file created by backupCreate. Restoring the file takes about
  927. the same time as saving it, and FHEM is blocked during this time.
  928. Note / Important: this function is not yet tested for older devices using
  929. the MEMORY functions.
  930. </li>
  931. <li>clearStatistics<br>
  932. clear network statistics.
  933. </li>
  934. <li>controllerChange on|stop|stopFailed<br>
  935. Add a controller to the current network and transfer role as primary to it.
  936. Invoking controller is converted to secondary.<br>
  937. stop: stop controllerChange<br>
  938. stopFailed: stop controllerChange and report an error
  939. </li>
  940. <li>createNewPrimary on|stop|stopFailed<br>
  941. Add a controller to the current network as a replacement for an old
  942. primary. Command can be invoked only by a secondary configured as basic
  943. SUC<br>
  944. stop: stop createNewPrimary<br>
  945. stopFailed: stop createNewPrimary and report an error
  946. </li>
  947. <li>createNode &lt;device&gt;<br>
  948. createNodeSec &lt;device&gt;<br>
  949. Request the class information for the specified node, and create
  950. a FHEM device upon reception of the answer. Used to create FHEM devices for
  951. nodes included with another software or if the fhem.cfg got lost. For the
  952. node id see the get nodeList command below. Note: the node must be "alive",
  953. i.e. for battery based devices you have to press the "wakeup" button 1-2
  954. seconds before entering this command in FHEM.<br>
  955. &lt;device&gt; is either device name or decimal nodeId.<br>
  956. createNodeSec assumes a secure inclusion, see the comments for "addNode
  957. onSec" for details.
  958. </li>
  959. <li>factoryReset yes<br>
  960. Reset controller to default state.
  961. Erase all node and routing infos, assign a new random homeId.
  962. To control a device it must be re-included and re-configured.<br>
  963. !Use this with care AND only if You know what You do!<br>
  964. Note: the corresponding FHEM devices have to be deleted manually.
  965. </li>
  966. <li>learnMode on|onNw|disable<br>
  967. Add or remove controller to/from an other network.
  968. Assign a homeId, nodeId and receive/store nodeList and routing infos.
  969. </li>
  970. <li>removeFailedNode &lt;device&gt;<br>
  971. Remove non-responding node -that must be on the failed node list-
  972. from the routing table in controller. Instead, always use removeNode if
  973. possible. Note: the corresponding FHEM device have to be deleted
  974. manually.<br>
  975. &lt;device&gt; is either device name or decimal nodeId.
  976. </li>
  977. <li>removeNode onNw|on|off<br>
  978. Activate (or deactivate) exclusion mode. "on" activates standard exclusion.
  979. "onNw" activates network wide exclusion (only SDK 4.5-4.9, SDK 6.x and
  980. above). Note: the corresponding FHEM device have to be deleted
  981. manually.
  982. </li>
  983. <li>reopen<br>
  984. First close and then open the device. Used for debugging purposes.
  985. </li>
  986. <li>replaceFailedNode &lt;device&gt;<br>
  987. Replace a non-responding node with a new one. The non-responding node
  988. must be on the failed node list.<br>
  989. &lt;device&gt; is either device name or decimal nodeId.
  990. </li>
  991. <li>routeFor &lt;device&gt; &lt;hop1&gt; &lt;hop2&gt; &lt;hop3&gt;
  992. &lt;hop4&gt; &lt;speed&gt;<br>
  993. set priority routing for &lt;device&gt;. &lt;device&gt; and &lt;hopN&gt are
  994. either device name or decimal nodeId or 0 for unused.<br>
  995. &lt;speed&gt;: 1=9,6kbps; 2=40kbps; 3=100kbps
  996. </li>
  997. <li>sendNIF &lt;device&gt;<br>
  998. Send NIF to the specified &lt;device&gt;.
  999. &lt;device&gt; is either device name or decimal nodeId.
  1000. </li>
  1001. <li>sucNodeId &lt;decimal nodeId&gt; &lt;sucState&gt;
  1002. &lt;capabilities&gt;<br>
  1003. Configure a controller node to be a SUC/SIS or not.<br>
  1004. &lt;nodeId&gt;: decimal nodeId to be SUC/SIS<br>
  1005. &lt;sucState&gt;: 0 = deactivate; 1 = activate<br>
  1006. &lt;capabilities&gt;: 0 = basic SUC; 1 = SIS
  1007. </li>
  1008. <li>sucRequestUpdate &lt;decimal nodeId of SUC/SIS&gt;<br>
  1009. Request network updates from SUC/SIS. Primary do not need it.
  1010. </li>
  1011. <li>sucSendNodeId &lt;decimal nodeId&gt;<br>
  1012. Send SUC/SIS nodeId to the specified decimal controller nodeId.
  1013. </li>
  1014. </ul>
  1015. <br>
  1016. <a name="ZWDongleget"></a>
  1017. <b>Get</b>
  1018. <ul>
  1019. <li>homeId<br>
  1020. return the six hex-digit homeId of the controller.
  1021. </li>
  1022. <li>backgroundRSSI<br>
  1023. query the measured RSSI on the Z-Wave network
  1024. </li>
  1025. <li>caps, ctrlCaps, version<br>
  1026. return different controller specific information. Needed by developers
  1027. only.
  1028. </li>
  1029. <li>isFailedNode &lt;device&gt;<br>
  1030. return if a node is stored in the failed node list. &lt;device&gt; is
  1031. either device name or decimal nodeId.
  1032. </li>
  1033. <li>neighborList [excludeDead] [onlyRep] &lt;device&gt;<br>
  1034. return neighborList of the &lt;device&gt;.<br>
  1035. &lt;device&gt; is either device name or decimal nodeId.<br>
  1036. With onlyRep the result will include only nodes with repeater
  1037. functionality.
  1038. </li>
  1039. <li>nodeInfo &lt;device&gt;<br>
  1040. return node specific information. &lt;device&gt; is either device name or
  1041. decimal nodeId.
  1042. </li>
  1043. <li>nodeList<br>
  1044. return the list of included nodenames or UNKNOWN_id (decimal id), if there
  1045. is no corresponding device in FHEM. Can be used to recreate FHEM-nodes with
  1046. the createNode command.
  1047. </li>
  1048. <li>random &lt;N&gt;<br>
  1049. request &lt;N&gt; random bytes from the controller.
  1050. </li>
  1051. <li>raw &lt;hex&gt;<br>
  1052. Send raw data &lt;hex&gt; to the controller. Developer only.
  1053. </li>
  1054. <li>routeFor &lt;device&gt;<br>
  1055. request priority routing for &lt;device&gt;. &lt;device&gt; is either
  1056. device name or decimal nodeId.</li>
  1057. <li>statistics<br>
  1058. return the current network statistics.
  1059. </li>
  1060. <li>sucNodeId<br>
  1061. return the currently registered decimal SUC nodeId.
  1062. </li>
  1063. </ul>
  1064. <br>
  1065. <a name="ZWDongleattr"></a>
  1066. <b>Attributes</b>
  1067. <ul>
  1068. <li><a href="#dummy">dummy</a></li>
  1069. <li><a href="#do_not_notify">do_not_notify</a></li>
  1070. <li><a href="#model">model</a></li>
  1071. <li><a href="#disable">disable</a></li>
  1072. <li><a name="helpSites">helpSites</a><br>
  1073. Comma separated list of Help Sites to get device pictures from or to
  1074. show a link to in the detailed window. Valid values are pepper
  1075. and alliance.
  1076. </li>
  1077. <li><a name="homeId">homeId</a><br>
  1078. Stores the homeId of the dongle. Is a workaround for some buggy dongles,
  1079. wich sometimes report a wrong/nonexisten homeId (Forum #35126)</li>
  1080. <li><a name="networkKey">networkKey</a><br>
  1081. Needed for secure inclusion, hex string with length of 32
  1082. </li>
  1083. <li><a name="neighborListPos">neighborListPos</a><br>
  1084. Used by the "Show neighbor map" function in the FHEMWEB ZWDongle detail
  1085. screen to store the position of the box.
  1086. </li>
  1087. <li><a name="neighborListFmt">neighborListFmt</a><br>
  1088. Used by the "Show neighbor map" function in the FHEMWEB ZWDongle detail
  1089. screen. The value is a perl hash, specifiying the values for the keys
  1090. txt, img and title. In the value each word is replaced by the
  1091. corresponding Internal, Reading or Attribute of the device, if there is
  1092. one to replace. Default is
  1093. <ul><code>
  1094. { txt=>"NAME", img=>"IMAGE", title=>"Time to ack: timeToAck" }
  1095. </code></ul>
  1096. </li>
  1097. <li><a name="showSetInState">showSetInState</a><br>
  1098. If the attribute is set to 1, and a user issues a set command to a ZWave
  1099. device, then the state of the ZWave device will be changed to
  1100. set_&lt;cmd&gt; first, and after the ACK from the device is received, to
  1101. &lt;cmd&gt;. E.g.: Issuing the command on changes the state first to
  1102. set_on, and after the device ack is received, to on. This is analoguos
  1103. to the CUL_HM module. Default for this attribute is 0.
  1104. </li>
  1105. </ul>
  1106. <br>
  1107. <a name="ZWDongleevents"></a>
  1108. <b>Generated events:</b>
  1109. <ul>
  1110. <br><b>General</b>
  1111. <li>UNDEFINED ZWave_${type6}_$id ZWave $homeId $id $classes</li>
  1112. <li>ZW_APPLICATION_UPDATE addDone $nodeId</li>
  1113. <li>ZW_APPLICATION_UPDATE deleteDone $nodeId</li>
  1114. <li>ZW_APPLICATION_UPDATE sudId $nodeId</li>
  1115. <br><b>addNode</b>
  1116. <li>ZW_ADD_NODE_TO_NETWORK [learnReady|nodeFound|slave|controller|
  1117. done|failed]</li>
  1118. <br><b>clearStatistics</b>
  1119. <li>CLEAR_NETWORK_STATS ok</li>
  1120. <br><b>controllerChange</b>
  1121. <li>ZW_CONTROLLER_CHANGE [learnReady|nodeFound|controller|done|failed]</li>
  1122. <br><b>createNewPrimary</b>
  1123. <li>ZW_CREATE_NEW_PRIMARY [learnReady|nodeFound|controller|done|failed]</li>
  1124. <br><b>factoryReset</b>
  1125. <li>ZW_SET_DEFAULT [done]</li>
  1126. <br><b>learnMode</b>
  1127. <li>ZW_SET_LEARN_MODE [started|done|failed|deleted]</li>
  1128. <br><b>neighborUpdate</b>
  1129. <li>ZW_REQUEST_NODE_NEIGHBOR_UPDATE [started|done|failed]</li>
  1130. <br><b>removeFailedNode</b>
  1131. <li>ZW_REMOVE_FAILED_NODE_ID
  1132. [failedNodeRemoveStarted|notPrimaryController|noCallbackFunction|
  1133. failedNodeNotFound|failedNodeRemoveProcessBusy|
  1134. failedNodeRemoveFail|nodeOk|nodeRemoved|nodeNotRemoved]</li>
  1135. <br><b>removeNode</b>
  1136. <li>ZW_REMOVE_NODE_FROM_NETWORK
  1137. [learnReady|nodeFound|slave|controller|done|failed]</li>
  1138. <br><b>replaceFailedNode</b>
  1139. <li>ZW_REPLACE_FAILED_NODE
  1140. [failedNodeRemoveStarted|notPrimaryController|noCallbackFunction|
  1141. failedNodeNotFound|failedNodeRemoveProcessBusy|
  1142. failedNodeRemoveFail|nodeOk|failedNodeReplace|
  1143. failedNodeReplaceDone|failedNodeRemoveFailed]</li>
  1144. <br><b>routeFor</b>
  1145. <li>ZW_SET_PRIORITY_ROUTE node $nodeId result $nr</li>
  1146. <br><b>sucNetworkUpdate</b>
  1147. <li>ZW_REQUEST_NETWORK_UPDATE [started|selfOrNoSUC|done|abort|wait|diabled|
  1148. overflow]</li>
  1149. <br><b>sucNodeId</b>
  1150. <li>ZW_SET_SUC_NODE_ID [ok|failed|callbackSucceeded|callbackFailed]</li>
  1151. <br><b>sucRouteAdd</b>
  1152. <li>ZW_ASSIGN_SUC_RETURN_ROUTE [started|alreadyActive|transmitOk|
  1153. transmitNoAck|transmitFail|transmitNotIdle|
  1154. transmitNoRoute]</li>
  1155. <br><b>sucRouteDel</b>
  1156. <li>ZW_DELETE_SUC_RETURN_ROUTE [started|alreadyActive|transmitOk|
  1157. transmitNoAck|transmitFail|transmitNotIdle|
  1158. transmitNoRoute]</li>
  1159. <br><b>sucSendNodeId</b>
  1160. <li>ZW_SEND_SUC_ID [started|alreadyActive|transmitOk|
  1161. transmitNoAck|transmitFail|transmitNotIdle|
  1162. transmitNoRoute]</li>
  1163. </ul>
  1164. </ul>
  1165. =end html
  1166. =cut