10_DUOFERNSTICK.pm 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655
  1. ##############################################
  2. # $Id: 10_DUOFERNSTICK.pm 14082 2017-04-23 11:46:58Z Telekatz $
  3. package main;
  4. use strict;
  5. use warnings;
  6. sub DUOFERNSTICK_Read($);
  7. sub DUOFERNSTICK_Ready($);
  8. my %matchList = ( "1:DUOFERN" => "^(06|0F|81).{42}") ;
  9. my %sets = (
  10. "reopen:noArg" => "",
  11. "statusBroadcast:noArg" => "",
  12. "pair:noArg" => "",
  13. "unpair:noArg" => "",
  14. "remotePair" => "",
  15. "raw" => "",
  16. );
  17. my $duoInit1 = "01000000000000000000000000000000000000000000";
  18. my $duoInit2 = "0E000000000000000000000000000000000000000000";
  19. my $duoSetDongle = "0Azzzzzz000100000000000000000000000000000000";
  20. my $duoInit3 = "14140000000000000000000000000000000000000000";
  21. my $duoSetPairs = "03nnyyyyyy0000000000000000000000000000000000";
  22. my $duoInitEnd = "10010000000000000000000000000000000000000000";
  23. my $duoACK = "81000000000000000000000000000000000000000000";
  24. my $duoStatusRequest = "0DFF0F400000000000000000000000000000FFFFFF01";
  25. my $duoStartPair = "04000000000000000000000000000000000000000000";
  26. my $duoStopPair = "05000000000000000000000000000000000000000000";
  27. my $duoStartUnpair = "07000000000000000000000000000000000000000000";
  28. my $duoStopUnpair = "08000000000000000000000000000000000000000000";
  29. my $duoRemotePair = "0D0006010000000000000000000000000000yyyyyy01";
  30. sub DUOFERNSTICK_Initialize($)
  31. {
  32. my ($hash) = @_;
  33. require "$attr{global}{modpath}/FHEM/DevIo.pm";
  34. $hash->{ReadFn} = "DUOFERNSTICK_Read";
  35. $hash->{ReadyFn} = "DUOFERNSTICK_Ready";
  36. $hash->{WriteFn} = "DUOFERNSTICK_Write";
  37. $hash->{DefFn} = "DUOFERNSTICK_Define";
  38. $hash->{UndefFn} = "DUOFERNSTICK_Undef";
  39. $hash->{SetFn} = "DUOFERNSTICK_Set";
  40. $hash->{NotifyFn}= "DUOFERNSTICK_Notify";
  41. $hash->{Clients} = ":DUOFERN:";
  42. $hash->{MatchList} = \%matchList;
  43. $hash->{AttrList}= $readingFnAttributes;
  44. }
  45. #####################################
  46. sub
  47. DUOFERNSTICK_Define($$)
  48. {
  49. my ($hash, $def) = @_;
  50. my @a = split("[ \t][ \t]*", $def);
  51. if(@a < 4 || @a > 5) {
  52. my $msg = "wrong syntax: define <name> DUOFERNSTICK devicename DongleSerial";
  53. Log3 undef, 2, $msg;
  54. return $msg;
  55. }
  56. DevIo_CloseDev($hash);
  57. my $name = $a[0];
  58. my $dev = $a[2];
  59. return "wrong DongleSerial format: specify a 6 digit hex value starting with 6F"
  60. if(uc($a[3]) !~ m/^6F[a-f0-9]{4}$/i);
  61. $hash->{DongleSerial} = uc($a[3]);
  62. $hash->{DeviceName} = $dev;
  63. my $ret = DevIo_OpenDev($hash, 0, "DUOFERNSTICK_DoInit");
  64. return $ret;
  65. }
  66. #####################################
  67. sub
  68. DUOFERNSTICK_setStates($$)
  69. {
  70. my ($hash, $val) = @_;
  71. $hash->{STATE} = $val;
  72. $val = "disconnected" if ($val eq "closed");
  73. setReadingsVal($hash, "state", $val, TimeNow());
  74. }
  75. #####################################
  76. sub
  77. DUOFERNSTICK_Undef($$)
  78. {
  79. my ($hash, $arg) = @_;
  80. my $name = $hash->{NAME};
  81. foreach my $d (sort keys %defs) {
  82. if(defined($defs{$d}) &&
  83. defined($defs{$d}{IODev}) &&
  84. $defs{$d}{IODev} == $hash)
  85. {
  86. my $lev = ($reread_active ? 4 : 2);
  87. Log3 $name, $lev, "deleting port for $d";
  88. delete $defs{$d}{IODev};
  89. }
  90. }
  91. DevIo_CloseDev($hash);
  92. return undef;
  93. }
  94. #####################################
  95. sub
  96. DUOFERNSTICK_Set($@)
  97. {
  98. my ($hash, @a) = @_;
  99. return "set $hash->{NAME} needs at least one parameter" if(@a < 2);
  100. my $me = shift @a;
  101. my $cmd = shift @a;
  102. my $arg = shift @a;
  103. my $err;
  104. my $buf;
  105. return join(" ", sort keys %sets) if ($cmd eq "?");
  106. if ($cmd eq "reopen") {
  107. return DUOFERNSTICK_Reopen($hash);
  108. } elsif ($cmd eq "statusBroadcast") {
  109. DUOFERNSTICK_AddSendQueue($hash, $duoStatusRequest);
  110. return undef;
  111. } elsif ($cmd eq "raw") {
  112. return "wrong raw format: specify a 44 digit hex value"
  113. if(!$arg || (uc($arg) !~ m/^[a-f0-9]{44}$/i));
  114. DUOFERNSTICK_AddSendQueue($hash, $arg);
  115. return undef;
  116. } elsif ($cmd eq "pair") {
  117. DUOFERNSTICK_AddSendQueue($hash, $duoStartPair);
  118. $hash->{pair} = 1;
  119. delete($hash->{unpair});
  120. InternalTimer(gettimeofday()+60, "DUOFERNSTICK_RemovePair", "$hash->{NAME}:RP", 1);
  121. return undef;
  122. } elsif ($cmd eq "unpair") {
  123. DUOFERNSTICK_AddSendQueue($hash, $duoStartUnpair);
  124. $hash->{unpair} = 1;
  125. delete($hash->{pair});
  126. InternalTimer(gettimeofday()+60, "DUOFERNSTICK_RemoveUnpair", "$hash->{NAME}:RU", 1);
  127. return undef;
  128. } elsif ($cmd eq "remotePair") {
  129. return "wrong serial format: specify a 6 digit hex value"
  130. if(!$arg || (uc($arg) !~ m/^[a-f0-9]{6}$/i));
  131. my $buf = $duoRemotePair;
  132. $buf =~ s/yyyyyy/$arg/;
  133. DUOFERNSTICK_AddSendQueue($hash, $buf);
  134. return undef;
  135. }
  136. return "Unknown argument $cmd, choose one of ". join(" ", sort keys %sets);
  137. }
  138. #####################################
  139. # called from the global loop, when the select for hash->{FD} reports data
  140. sub
  141. DUOFERNSTICK_Read($)
  142. {
  143. my ($hash) = @_;
  144. my $buf = "";
  145. my $rbuf = DevIo_SimpleRead($hash);
  146. return "" if(!defined($rbuf));
  147. if ($hash->{PARTIAL} ne "") {
  148. RemoveInternalTimer("$hash->{NAME}:FB");
  149. }
  150. my @array=split('',$rbuf);
  151. foreach (@array){
  152. $buf .= sprintf "%02x", ord($_) ;
  153. }
  154. my $name = $hash->{NAME};
  155. my $duodata = $hash->{PARTIAL};
  156. $duodata .= $buf;
  157. while(length($duodata) >= 44) {
  158. my $rmsg;
  159. my $me = $hash->{NAME};
  160. ($rmsg,$duodata) = unpack("a44 a*", $duodata);
  161. Log3 $name, 4, "$me: rx -> $rmsg";
  162. $hash->{PARTIAL} = $duodata; # for recursive calls
  163. DUOFERNSTICK_Parse($hash, uc($rmsg)) if($rmsg);
  164. $duodata = $hash->{PARTIAL};
  165. }
  166. $hash->{PARTIAL} = $duodata;
  167. my $now = gettimeofday();
  168. if ($hash->{PARTIAL} ne "") {
  169. InternalTimer($now+0.5, "DUOFERNSTICK_Flush_Buffer", "$hash->{NAME}:FB", 0);
  170. }
  171. }
  172. #####################################
  173. sub
  174. DUOFERNSTICK_Write($$)
  175. {
  176. my ($hash,$msg) = @_;
  177. my $err;
  178. my $buf;
  179. my $name = $hash->{NAME};
  180. Log3 $name, 5, "$hash->{NAME} sending $msg";
  181. $msg =~ s/zzzzzz/$hash->{DongleSerial}/;
  182. DUOFERNSTICK_AddSendQueue($hash,$msg);
  183. }
  184. #####################################
  185. sub
  186. DUOFERNSTICK_Parse($$)
  187. {
  188. my ($hash, $rmsg) = @_;
  189. DUOFERNSTICK_SimpleWrite($hash, $duoACK) if($rmsg ne $duoACK);;
  190. if($rmsg =~ m/81.{42}/) {
  191. DUOFERNSTICK_HandleWriteQueue($hash);
  192. }
  193. return if($rmsg eq $duoACK);
  194. $hash->{RAWMSG} = $rmsg;
  195. if($rmsg =~ m/0602.{40}/) {
  196. my %addvals = (RAWMSG => $rmsg);
  197. Dispatch($hash, $rmsg, \%addvals) if ($hash->{pair});
  198. delete($hash->{pair});
  199. RemoveInternalTimer($hash);
  200. return undef;
  201. } elsif ($rmsg =~ m/0603.{40}/) {
  202. my %addvals = (RAWMSG => $rmsg);
  203. Dispatch($hash, $rmsg, \%addvals) if ($hash->{unpair});
  204. delete($hash->{unpair});
  205. RemoveInternalTimer($hash);
  206. return undef;
  207. } elsif ($rmsg =~ m/0FFF11.{38}/) {
  208. return undef;
  209. } elsif ($rmsg =~ m/81000000.{36}/) {
  210. return undef;
  211. }
  212. my %addvals = (RAWMSG => $rmsg);
  213. Dispatch($hash, $rmsg, \%addvals);
  214. }
  215. #####################################
  216. sub
  217. DUOFERNSTICK_Ready($)
  218. {
  219. my ($hash) = @_;
  220. return DevIo_OpenDev($hash, 1, "DUOFERNSTICK_DoInit")
  221. if($hash->{STATE} eq "disconnected");
  222. # This is relevant for windows/USB only
  223. my $po = $hash->{USBDev};
  224. my ($BlockingFlags, $InBytes, $OutBytes, $ErrorFlags);
  225. if($po) {
  226. ($BlockingFlags, $InBytes, $OutBytes, $ErrorFlags) = $po->status;
  227. }
  228. return ($InBytes && $InBytes>0);
  229. }
  230. #####################################
  231. sub
  232. DUOFERNSTICK_RemovePair($)
  233. {
  234. my ($name,$id) = split(":",$_[0]);
  235. my ($hash) = $defs{$name};
  236. DUOFERNSTICK_AddSendQueue($hash, $duoStopPair);
  237. delete($hash->{pair});
  238. return undef;
  239. }
  240. #####################################
  241. sub
  242. DUOFERNSTICK_RemoveUnpair($)
  243. {
  244. my ($name,$id) = split(":",$_[0]);
  245. my ($hash) = $defs{$name};
  246. DUOFERNSTICK_AddSendQueue($hash, $duoStopUnpair);
  247. delete($hash->{unpair});
  248. return undef;
  249. }
  250. #####################################
  251. sub
  252. DUOFERNSTICK_Flush_Buffer($)
  253. {
  254. my ($name,$id) = split(":",$_[0]);
  255. if ($defs{$name}{PARTIAL} ne "") {
  256. Log3 $name, 4, "$name discard $defs{$name}{PARTIAL}";
  257. }
  258. $defs{$name}{PARTIAL} ="";
  259. return undef;
  260. }
  261. #####################################
  262. sub
  263. DUOFERNSTICK_Reopen($)
  264. {
  265. my ($hash) = @_;
  266. DevIo_CloseDev($hash);
  267. DevIo_OpenDev($hash, 1, "DUOFERNSTICK_DoInit");
  268. }
  269. #####################################
  270. sub
  271. DUOFERNSTICK_DoInit($)
  272. {
  273. my $hash = shift;
  274. my $name = $hash->{NAME};
  275. my $err;
  276. my $msg = undef;
  277. my $buf = "";
  278. my @pairs;
  279. foreach my $d (keys %defs)
  280. {
  281. my $module = $defs{$d}{TYPE};
  282. next if ($module ne "DUOFERN");
  283. my $code = $defs{$d}{CODE};
  284. if(AttrVal($defs{$d}{NAME}, "ignore", "0") == "0") {
  285. push(@pairs, $code) if(length($code) == 6);
  286. }
  287. }
  288. $hash->{helper}{cmdEx} = 0;
  289. @{$hash->{cmdStack}} = ();
  290. return undef if (!$init_done);
  291. for(my $i = 0; $i < 4; $i++) {
  292. DUOFERNSTICK_SimpleWrite($hash, $duoInit1);
  293. ($err, $buf) = DUOFERNSTICK_ReadAnswer($hash, "INIT1");
  294. next if($err);
  295. DUOFERNSTICK_SimpleWrite($hash, $duoInit2);
  296. ($err, $buf) = DUOFERNSTICK_ReadAnswer($hash, "INIT2");
  297. next if($err);
  298. $buf = $duoSetDongle;
  299. $buf =~ s/zzzzzz/$hash->{DongleSerial}/;
  300. DUOFERNSTICK_SimpleWrite($hash, $buf);
  301. ($err, $buf) = DUOFERNSTICK_ReadAnswer($hash, "SetDongle");
  302. next if($err);
  303. DUOFERNSTICK_SimpleWrite($hash, $duoACK);
  304. DUOFERNSTICK_SimpleWrite($hash, $duoInit3);
  305. ($err, $buf) = DUOFERNSTICK_ReadAnswer($hash, "INIT3");
  306. next if($err);
  307. DUOFERNSTICK_SimpleWrite($hash, $duoACK);
  308. my $counter = 0;
  309. foreach (@pairs){
  310. $buf = $duoSetPairs;
  311. my $chex .= sprintf "%02x", $counter;
  312. $buf =~ s/nn/$chex/;
  313. $buf =~ s/yyyyyy/$_/;
  314. DUOFERNSTICK_SimpleWrite($hash, $buf);
  315. ($err, $buf) = DUOFERNSTICK_ReadAnswer($hash, "SetPairs");
  316. next if($err);
  317. DUOFERNSTICK_SimpleWrite($hash, $duoACK);
  318. $counter++;
  319. }
  320. DUOFERNSTICK_SimpleWrite($hash, $duoInitEnd);
  321. ($err, $buf) = DUOFERNSTICK_ReadAnswer($hash, "INIT3");
  322. return "$name: $err" if($err);
  323. DUOFERNSTICK_SimpleWrite($hash, $duoACK);
  324. next if($err);
  325. DUOFERNSTICK_SimpleWrite($hash, $duoStatusRequest);
  326. ($err, $buf) = DUOFERNSTICK_ReadAnswer($hash, "statusRequest");
  327. next if($err);
  328. DUOFERNSTICK_SimpleWrite($hash, $duoACK);
  329. readingsSingleUpdate($hash, "state", "Initialized", 1);
  330. return undef;
  331. }
  332. return "$name: Init fail";
  333. }
  334. #####################################
  335. sub
  336. DUOFERNSTICK_SimpleWrite(@)
  337. {
  338. my ($hash, $msg) = @_;
  339. my $buf = "";
  340. return if(!$hash);
  341. my $name = $hash->{NAME};
  342. $msg =~ s/ //g;
  343. my $me = $hash->{NAME};
  344. Log3 $me, 4, "$me: snd -> $msg";
  345. my @hex = ($msg =~ /(..)/g);
  346. foreach (@hex){
  347. $buf .= chr(hex($_)) ;
  348. }
  349. DevIo_SimpleWrite($hash,$buf,0);
  350. return undef;
  351. }
  352. #####################################
  353. sub
  354. DUOFERNSTICK_ReadAnswer($$$$)
  355. {
  356. my ($hash, $arg) = @_;
  357. my $ohash = $hash;
  358. while($hash->{TYPE} ne "DUOFERNSTICK") {
  359. $hash = $hash->{IODev};
  360. }
  361. return ("No FD", undef)
  362. if(!$hash || ($^O !~ /Win/ && !defined($hash->{FD})));
  363. my ($mduodata, $rin) = ("", '');
  364. my $buf;
  365. my $to = 1; # 3 seconds timeout
  366. $mduodata = $hash->{PARTIAL} if(defined($hash->{PARTIAL}));
  367. $to = $ohash->{RA_Timeout} if($ohash->{RA_Timeout}); # ...or less
  368. for(;;) {
  369. if($^O =~ m/Win/ && $hash->{USBDev}) {
  370. $hash->{USBDev}->read_const_time($to*1000); # set timeout (ms)
  371. # Read anstatt input sonst funzt read_const_time nicht.
  372. $buf = $hash->{USBDev}->read(22);
  373. return ("Timeout reading answer for get $arg", undef)
  374. if(length($buf) == 0);
  375. } else {
  376. return ("Device lost when reading answer for get $arg", undef)
  377. if(!$hash->{FD});
  378. vec($rin, $hash->{FD}, 1) = 1;
  379. my $nfound = select($rin, undef, undef, $to);
  380. if($nfound < 0) {
  381. next if ($! == EAGAIN() || $! == EINTR() || $! == 0);
  382. my $err = $!;
  383. DevIo_Disconnected($hash);
  384. return("DUOFERNSTICK_ReadAnswer $arg: $err", undef);
  385. }
  386. return ("Timeout reading answer for get $arg", undef)
  387. if($nfound == 0);
  388. $buf = DevIo_SimpleRead($hash);
  389. return ("No data", undef) if(!defined($buf));
  390. }
  391. if(defined($buf)) {
  392. my $rbuf = "";
  393. my @array=split('',$buf);
  394. foreach (@array){
  395. $rbuf .= sprintf "%02x", ord($_) ;
  396. }
  397. Log3 $ohash->{NAME}, 5, "DUOFERNSTICK/RAW (ReadAnswer): $rbuf";
  398. $mduodata .= $rbuf;
  399. }
  400. # Dispatch data in the buffer before the proper answer.
  401. if(length($mduodata) >= 44) {
  402. my $rmsg;
  403. ($rmsg,$mduodata) = unpack("a44 a*", $mduodata);
  404. $hash->{PARTIAL} = $mduodata; # for recursive calls
  405. return (undef, $rmsg);
  406. }
  407. }
  408. }
  409. #####################################
  410. sub
  411. DUOFERNSTICK_Notify($$)
  412. {
  413. my ($own, $dev) = @_;
  414. my $me = $own->{NAME}; # own name / hash
  415. my $devName = $dev->{NAME}; # Device that created the events
  416. return undef if ($devName ne "global");
  417. my $max = int(@{$dev->{CHANGED}}); # number of events / changes
  418. for (my $i = 0; $i < $max; $i++) {
  419. my $s = $dev->{CHANGED}[$i];
  420. next if(!defined($s));
  421. my ($what,$who) = split(' ',$s);
  422. if ($what && ($what =~ m/^INITIALIZED$/)) {
  423. DUOFERNSTICK_DoInit($own);
  424. }
  425. }
  426. return undef;
  427. }
  428. #####################################
  429. sub
  430. DUOFERNSTICK_HandleWriteQueue($)
  431. {
  432. my ($hash) = @_;
  433. RemoveInternalTimer($hash);
  434. $hash->{helper}{cmdEx} -= 1 if ($hash->{helper}{cmdEx});
  435. my $entries = scalar @{$hash->{cmdStack}};
  436. if ($entries > 0) {
  437. readingsSingleUpdate($hash, "state", ($entries + $hash->{helper}{cmdEx})." CMDs_pending", 1);
  438. my $msg = shift @{$hash->{cmdStack}};
  439. $hash->{helper}{cmdEx} += 1;
  440. DUOFERNSTICK_SimpleWrite($hash, $msg);
  441. InternalTimer(gettimeofday()+5, "DUOFERNSTICK_HandleWriteQueue", $hash, 1);
  442. } else {
  443. readingsSingleUpdate($hash, "state","CMDs_done", 1);
  444. }
  445. }
  446. #####################################
  447. sub
  448. DUOFERNSTICK_AddSendQueue($$)
  449. {
  450. my ($hash, $msg) = @_;
  451. push(@{$hash->{cmdStack}}, $msg);
  452. my $entries = scalar @{$hash->{cmdStack}};
  453. if ($hash->{helper}{cmdEx} == 0 ) {
  454. DUOFERNSTICK_HandleWriteQueue($hash);
  455. } else {
  456. readingsSingleUpdate($hash, "state", ($entries + $hash->{helper}{cmdEx})." CMDs_pending", 1);
  457. InternalTimer(gettimeofday()+5, "DUOFERNSTICK_HandleWriteQueue", $hash, 1);
  458. };
  459. }
  460. 1;
  461. =pod
  462. =item summary IO device for Rademacher DuoFern devices
  463. =item summary_DE IO device für Rademacher DuoFern Ger&auml;te
  464. =begin html
  465. <a name="DUOFERNSTICK"></a>
  466. <h3>DUOFERNSTICK</h3>
  467. <ul>
  468. The DUOFERNSTICK is the fhem module for the Rademacher DuoFern USB stick. <br>
  469. <br><br>
  470. <a name="DUOFERNSTICK_define"></a>
  471. <b>Define</b>
  472. <ul>
  473. <code>define &lt;name&gt; DUOFERNSTICK &lt;device&gt; &lt;code&gt;</code><br><br>
  474. &lt;device&gt; specifies the serial port to communicate with the DuoFern stick.<br>
  475. &lt;code&gt; specifies the radio code of the DuoFern stick.<br>
  476. <br>
  477. The baud rate must be 115200 baud.<br>
  478. The code of the DuoFern stick must start with 6F.
  479. <br><br>
  480. Example:<br>
  481. <ul>
  482. <code>define myDuoFernStick DUOFERNSTICK COM5@115200 6FEDCB</code><br>
  483. <code>define myDuoFernStick DUOFERNSTICK /dev/serial/by-id/usb-Rademacher_DuoFern_USB-Stick_WR0455TN-if00-port0@115200 6FEDCB</code><br>
  484. </ul>
  485. </ul>
  486. <br>
  487. <a name="DUOFERNSTICK_set"></a>
  488. <p><b>Set</b></p>
  489. <ul>
  490. <li><b>pair</b><br>
  491. Set the DuoFern stick in pairing mode for 60 seconds. Any DouFern device set into
  492. pairing mode in this time will be paired with the DuoFern stick.
  493. </li><br>
  494. <li><b>unpair</b><br>
  495. Set the DuoFern stick in unpairing mode for 60 seconds. Any DouFern device set into
  496. unpairing mode in this time will be unpaired from the DuoFern stick.
  497. </li><br>
  498. <li><b>reopen</b><br>
  499. Reopens the connection to the device and reinitializes it.
  500. </li><br>
  501. <li><b>statusBroadcast</b><br>
  502. Sends a status request message to all DuoFern devices.
  503. </li><br>
  504. <li><b>remotePair &lt;code&gt</b><br>
  505. Activates the pairing mode on the device specified by the code.<br>
  506. Some actors accept this command in unpaired mode up to two hours afte power up.
  507. </li><br>
  508. <li><b>raw &lt;rawmsg&gt;</b><br>
  509. Sends a raw message.
  510. </li><br>
  511. </ul>
  512. <br>
  513. <b>Get</b> <ul>N/A</ul><br>
  514. <a name="DUOFERNSTICK_attr"></a>
  515. <b>Attributes</b>
  516. <ul>
  517. N/A
  518. </ul>
  519. <br>
  520. </ul>
  521. =end html
  522. =cut