11_OWX_FRM.pm 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717
  1. ########################################################################################
  2. #
  3. # OWX_FRM.pm
  4. #
  5. # FHEM module providing hardware dependent functions for the FRM interface of OWX
  6. #
  7. # Prof. Dr. Peter A. Henning
  8. #
  9. # $Id: 11_OWX_FRM.pm 16671 2018-04-29 05:06:35Z phenning $
  10. #
  11. ########################################################################################
  12. #
  13. # Provides the following methods for OWX
  14. #
  15. # Define
  16. # Detect
  17. # Alarms
  18. # Complex
  19. # Discover
  20. # Open
  21. # Close
  22. # Reopen
  23. # Read
  24. # Ready
  25. # Verify
  26. # Write
  27. # observer
  28. # device_to_firmata
  29. # firmata_to_device
  30. #
  31. ########################################################################################
  32. #
  33. # $hash->{DeviceName} = <firmata-device>:<firmata-pin>
  34. # $hash->{INTERFACE} = "firmata";
  35. # $hash->{HWDEVICE} = <firmata-device>
  36. # $hash->{PIN} = <firmata-pin>
  37. # $hash->{TYPE} = "OWX";
  38. #
  39. ########################################################################################
  40. package OWX_FRM;
  41. use strict;
  42. use warnings;
  43. use Device::Firmata::Constants qw/ :all /;
  44. use GPUtils qw(:all);
  45. ########################################################################################
  46. #
  47. # Constructor
  48. #
  49. ########################################################################################
  50. sub new($) {
  51. my ($class,$hash) = @_;
  52. return bless {
  53. hash => $hash,
  54. #-- module version
  55. version => "7.11"
  56. }, $class;
  57. }
  58. ########################################################################################
  59. #
  60. # Define - Implements Define method
  61. #
  62. # Parameter def = definition string
  63. #
  64. # Return undef if ok, otherwise error message
  65. #
  66. ########################################################################################
  67. sub Define($) {
  68. my ($self,$def) = @_;
  69. my $hash = $self->{hash};
  70. if (!defined($main::modules{FRM})) {
  71. my $ret = "OWX_FRM::Define module FRM not yet loaded, please define an FRM device first.";
  72. main::Log3 $hash->{NAME},1,$ret;
  73. return $ret;
  74. }
  75. my @a = split( "[ \t][ \t]*", $def );
  76. my $u = "wrong syntax: define <name> OWX <firmata-device>:<firmata-pin>";
  77. return $u unless int(@a) > 0;
  78. my($fdev,$pin) = split(':',$a[2]);
  79. $self->{pin} = $pin;
  80. $self->{id} = 0;
  81. $self->{name} = $hash->{NAME};
  82. $self->{hash} = $hash;
  83. #-- when the specified device name contains @<digits>, remove these.
  84. #my $dev =~ s/\@\d*//;
  85. #main::AssignIoPort($hash);
  86. #-- store with OWX device
  87. $hash->{DeviceName} = $a[2];
  88. $hash->{INTERFACE} = "firmata";
  89. $hash->{HWDEVICE} = $fdev;
  90. $hash->{PIN} = $pin;
  91. $hash->{ASYNCHRONOUS} = 0;
  92. main::Log3 $hash->{NAME},1,"OWX_FRM::Define warning: version ".$self->{version}." not identical to OWX version ".$main::owx_version
  93. if( $self->{version} ne $main::owx_version);
  94. #-- register IODev InitFn to be called by FRM after connection to Firmata device is initialized
  95. $hash->{IODev} = $main::defs{$hash->{HWDEVICE}};
  96. $main::modules{$main::defs{$hash->{NAME}}{TYPE}}->{InitFn} = "OWX_FRM::Init";
  97. return undef;
  98. }
  99. ########################################################################################
  100. #
  101. # Detect - Find out if we have the proper interface
  102. #
  103. # Return 1 if ok, otherwise 0
  104. #
  105. ########################################################################################
  106. sub Detect () {
  107. my ($self) = @_;
  108. my $hash = $self->{hash};
  109. my $ret;
  110. my $name = $hash->{NAME};
  111. my $ress = "OWX: 1-Wire bus $name: interface ";
  112. my $iodev = $hash->{IODev};
  113. if (defined $iodev and defined $iodev->{FirmataDevice} and defined $iodev->{FD}) {
  114. $ret=1;
  115. $ress .= "Firmata detected in $iodev->{NAME}";
  116. } else {
  117. $ret=0;
  118. $ress .= defined $iodev ? "$iodev->{NAME} is not connected to Firmata" : "not associated to any FRM device";
  119. }
  120. main::Log(1, $ress);
  121. return $ret;
  122. }
  123. ########################################################################################
  124. #
  125. # Alarms - Find devices on the 1-Wire bus, which have the alarm flag set
  126. #
  127. # Return number of alarmed devices
  128. #
  129. ########################################################################################
  130. sub Alarms() {
  131. my ($self) = @_;
  132. my $hash = $self->{hash};
  133. #-- get the interface
  134. my $frm = $hash->{IODev};
  135. return 0 unless defined $frm;
  136. my $firmata = $frm->{FirmataDevice};
  137. my $pin = $hash->{PIN};
  138. return 0 unless ( defined $firmata and defined $pin );
  139. $hash->{ALARMDEVS} = undef;
  140. eval {
  141. $firmata->onewire_search_alarms($hash->{PIN});
  142. };
  143. return 0 if ($@);
  144. my $times = main::AttrVal($hash,"ow-read-timeout",1000) / 50; #timeout in ms, defaults to 1 sec
  145. for (my $i=0;$i<$times;$i++) {
  146. if (main::FRM_poll($hash->{IODev})) {
  147. if (defined $hash->{ALARMDEVS}) {
  148. return 1;
  149. }
  150. } else {
  151. select (undef,undef,undef,0.05);
  152. }
  153. }
  154. $hash->{ALARMDEVS} = [];
  155. return 1;
  156. }
  157. ########################################################################################
  158. #
  159. # Reopen - Reopen Device
  160. #
  161. ########################################################################################
  162. sub Reopen () {
  163. main::Log 1,"[OWX_FRM] Warning: ->Reopen currently not defined";
  164. }
  165. ########################################################################################
  166. #
  167. # Init - Initialize the 1-wire device
  168. #
  169. # Parameter hash = hash of bus master
  170. #
  171. # Return 1 : OK
  172. # 0 : not OK
  173. #
  174. ########################################################################################
  175. sub Init() {
  176. my ($self) = @_;
  177. if (defined($self->{OWX})) {
  178. # class method called with parent hash instead of class hash as 1st parameter, fix
  179. $self = $self->{OWX};
  180. }
  181. my $hash = $self->{hash};
  182. my $dev = $hash->{DeviceName};
  183. my $name = $hash->{NAME};
  184. my $pin = $hash->{PIN};
  185. my $msg;
  186. #main::Log 1,"==================> STARTING INIT of 11_OWX_FRM";
  187. select(undef,undef,undef,0.01);
  188. my @args = ($pin);
  189. $hash->{IODev} = $main::defs{$hash->{HWDEVICE}};
  190. #-- 10_FRM.pm is broken
  191. #-- possible workaround
  192. $main::attr{$name}{IODev} = $hash->{IODev}->{NAME};
  193. my $ret = main::FRM_Init_Pin_Client($hash,\@args,PIN_ONEWIRE);
  194. if (defined $ret){
  195. $msg = "Error ".$ret;
  196. main::Log3 $name,1,"OWX_FRM::Init ".$msg;
  197. return $msg;
  198. }
  199. eval {
  200. my $firmata = main::FRM_Client_FirmataDevice($hash);
  201. $hash->{FRM_OWX_CORRELATIONID} = 0;
  202. $firmata->observe_onewire($pin,\&observer,$hash);
  203. $hash->{FRM_OWX_REPLIES} = {};
  204. $hash->{DEVS} = [];
  205. if ( main::AttrVal($hash->{NAME},"buspower","") eq "parasitic" ) {
  206. $firmata->onewire_config($pin,1);
  207. }
  208. };
  209. return GP_Catch($@) if ($@);
  210. $hash->{STATE}="Initialized";
  211. main::InternalTimer(main::gettimeofday()+10, "OWX_Discover", $hash,0);
  212. return undef;
  213. }
  214. ########################################################################################
  215. #
  216. # Complex - Send match ROM, data block and receive bytes as response
  217. #
  218. # Parameter hash = hash of bus master,
  219. # owx_dev = ROM ID of device
  220. # data = string to send
  221. # numread = number of bytes to receive
  222. #
  223. # Return response, if OK
  224. # 0 if not OK
  225. #
  226. ########################################################################################
  227. sub Complex ($$$$) {
  228. my ($self,$owx_dev,$data,$numread) =@_;
  229. my $hash = $self->{hash};
  230. my $name = $hash->{NAME};
  231. my $res = "";
  232. #-- get the interface
  233. my $frm = $hash->{IODev};
  234. return 0 unless defined $frm;
  235. my $firmata = $frm->{FirmataDevice};
  236. my $pin = $hash->{PIN};
  237. return 0 unless ( defined $firmata and defined $pin );
  238. my $ow_command = {};
  239. #-- has match ROM part
  240. if ($owx_dev) {
  241. $ow_command->{"select"} = device_to_firmata($owx_dev);
  242. #-- padding first 9 bytes into result string, since we have this
  243. # in the serial interfaces as well
  244. $res .= "000000000";
  245. }
  246. #-- has data part
  247. if ($data) {
  248. my @data = unpack "C*", $data;
  249. $ow_command->{"write"} = \@data;
  250. $res.=$data;
  251. }
  252. #-- has receive part
  253. if ( $numread > 0 ) {
  254. $ow_command->{"read"} = $numread;
  255. #Firmata sends 0-address on read after skip
  256. $owx_dev = '00.000000000000.00' unless defined $owx_dev;
  257. my $id = $hash->{FRM_OWX_CORRELATIONID};
  258. $ow_command->{"id"} = $hash->{FRM_OWX_CORRELATIONID};
  259. $hash->{FRM_OWX_REQUESTS}->{$id} = {
  260. command => $ow_command,
  261. device => $owx_dev
  262. };
  263. delete $hash->{FRM_OWX_REPLIES}->{$owx_dev};
  264. $hash->{FRM_OWX_CORRELATIONID} = ($id + 1) & 0xFFFF;
  265. }
  266. eval {
  267. $firmata->onewire_command_series( $pin, $ow_command );
  268. };
  269. return 0 if ($@);
  270. my $oldResLength = length($res);
  271. if ($numread) {
  272. my $times = main::AttrVal($hash,"ow-read-timeout",1000) / 50; #timeout in ms, defaults to 1 sec
  273. for (my $i=0;$i<$times;$i++) {
  274. if (main::FRM_poll($hash->{IODev})) {
  275. if (defined $hash->{FRM_OWX_REPLIES}->{$owx_dev}) {
  276. $res .= $hash->{FRM_OWX_REPLIES}->{$owx_dev};
  277. main::OWX_WDBGL($name,5,"OWX_FRM::Complex receiving inside loop no. $i (" . (length($res) - $oldResLength) . " bytes received) ",$res);
  278. return $res;
  279. }
  280. } else {
  281. select (undef,undef,undef,0.05);
  282. }
  283. }
  284. }
  285. main::OWX_WDBGL($name,5,"OWX_FRM::Complex receiving outside loop (" . (length($res) - $oldResLength) . " bytes received) ", $res);
  286. return $res;
  287. }
  288. ########################################################################################
  289. #
  290. # Discover - Discover devices on the 1-Wire bus via internal firmware
  291. #
  292. # Parameter hash = hash of bus master
  293. #
  294. # Return 0 : error
  295. # 1 : OK
  296. #
  297. ########################################################################################
  298. sub Discover ($) {
  299. my ($self) = @_;
  300. my $hash = $self->{hash};
  301. #main::Log 1,"======================> FRM Discover called";
  302. #-- get the interface
  303. my $frm = $hash->{IODev};
  304. return 0 unless defined $frm;
  305. my $firmata = $frm->{FirmataDevice};
  306. my $pin = $hash->{PIN};
  307. return 0 unless ( defined $firmata and defined $pin );
  308. my $old_devices = $hash->{DEVS};
  309. $hash->{DEVS} = undef;
  310. eval {
  311. my $res = $firmata->onewire_search($hash->{PIN});
  312. #main::Log 1,"=============> result from search is $res, iodev is ".$hash->{IODev};
  313. };
  314. return 0 if ($@);
  315. my $times = main::AttrVal($hash,"ow-read-timeout",1000) / 50; #timeout in ms, defaults to 1 sec
  316. #main::Log 1,"===========> olddevices = $old_devices, tries =$times";
  317. for (my $i=0;$i<$times;$i++) {
  318. if (main::FRM_poll($hash->{IODev})) {
  319. if (defined $hash->{DEVS}) {
  320. return 1;
  321. }
  322. } else {
  323. select (undef,undef,undef,0.05);
  324. }
  325. }
  326. #main::Log 1,"===========> olddevices restored";
  327. $hash->{DEVS} = $old_devices;
  328. return 1;
  329. }
  330. ########################################################################################
  331. #
  332. # Open - Open Device
  333. #
  334. ########################################################################################
  335. sub Open () {
  336. my ($self) = @_;
  337. my $hash = $self->{hash};
  338. }
  339. ########################################################################################
  340. #
  341. # Close - Close Device
  342. #
  343. ########################################################################################
  344. sub Close () {
  345. my ($self) = @_;
  346. my $hash = $self->{hash};
  347. }
  348. #######################################################################################
  349. #
  350. # Read - Implement the Read function
  351. #
  352. # Parameter numexp = expected number of bytes
  353. #
  354. #######################################################################################
  355. sub Read(@) {
  356. my ($self,$numexp) = @_;
  357. my $hash = $self->{hash};
  358. my $name = $hash->{NAME};
  359. my $buffer = $hash->{PREBUFFER};
  360. my $owx_dev = $hash->{FRM_OWX_CURRDEV};
  361. my $times = main::AttrVal($hash,"ow-read-timeout",1000) / 50; #timeout in ms, defaults to 1 sec
  362. #-- first read
  363. $buffer .= $hash->{FRM_OWX_REPLIES}->{$owx_dev};
  364. main::OWX_WDBGL($name,5,"OWX_FRM::Read receiving in first read ",$buffer);
  365. return $buffer;
  366. }
  367. ########################################################################################
  368. #
  369. # Ready - Implement the Ready function
  370. #
  371. # Return 1 : OK
  372. # 0 : not OK
  373. #
  374. ########################################################################################
  375. sub Ready () {
  376. my ($self) = @_;
  377. my $hash = $self->{hash};
  378. my $name = $hash->{NAME};
  379. my $success= 0;
  380. main::Log3 $name,1,"OWX_FRM::Ready function called for bus $name. STATE=".$hash->{STATE};
  381. return $success;
  382. }
  383. ########################################################################################
  384. #
  385. # Reset - Reset the 1-Wire bus
  386. #
  387. # Parameter hash = hash of bus master
  388. #
  389. # Return 1 : OK
  390. # 0 : not OK
  391. #
  392. ########################################################################################
  393. sub Reset() {
  394. my ($self) = @_;
  395. my $hash = $self->{hash};
  396. #-- get the interface
  397. my $frm = $hash->{IODev};
  398. return undef unless defined $frm;
  399. my $firmata = $frm->{FirmataDevice};
  400. my $pin = $hash->{PIN};
  401. return undef unless ( defined $firmata and defined $pin );
  402. eval {
  403. $firmata->onewire_reset($pin);
  404. };
  405. return 0 if ($@);
  406. return 1;
  407. }
  408. ########################################################################################
  409. #
  410. # Verify - Verify a particular device on the 1-Wire bus
  411. #
  412. # Parameter hash = hash of bus master, dev = 8 Byte ROM ID of device to be tested
  413. #
  414. # Return 1 : device found
  415. # 0 : device not
  416. #
  417. ########################################################################################
  418. sub Verify($) {
  419. my ($self,$dev) = @_;
  420. my $hash = $self->{hash};
  421. foreach my $found ($hash->{DEVS}) {
  422. if ($dev eq $found) {
  423. return 1;
  424. }
  425. }
  426. return 0;
  427. }
  428. #######################################################################################
  429. #
  430. # Write - Implement the write function
  431. #
  432. #
  433. # Parameter cmd = string to be sent
  434. # reset = 1 if initial bus reset has to be done
  435. #
  436. ########################################################################################
  437. sub Write(@) {
  438. my ($self,$cmd, $reset) = @_;
  439. my $hash = $self->{hash};
  440. my $name = $hash->{NAME};
  441. my $res = "";
  442. #-- get the interface
  443. my $frm = $hash->{IODev};
  444. unless(defined $frm){
  445. main::Log3 $name,1,"OWX_FRM::Write attempted to undefined device $name";
  446. return 0
  447. }
  448. my $firmata = $frm->{FirmataDevice};
  449. my $pin = $hash->{PIN};
  450. unless ( defined $firmata and defined $pin ){
  451. main::Log3 $name,1,"OWX_FRM::Write attempted to ill-defined device $name";
  452. return 0
  453. }
  454. #-- if necessary, perform a reset operation
  455. $self->Reset()
  456. if( $reset );
  457. main::OWX_WDBGL($name,5,"OWX_FRM::Write Sending out ",$cmd);
  458. my $cmd2 = $cmd;
  459. my $owx_dev ="";
  460. my $ow_command = {};
  461. #-- take away trailing 0xFF
  462. my $takeoff=0;
  463. my $tchar;
  464. for( my $i=length($cmd)-1; $i>=0; $i--){
  465. $tchar = substr($cmd,$i,1);
  466. if( ord($tchar) == 0xff ){
  467. $takeoff++;
  468. }else{
  469. last;
  470. }
  471. }
  472. $cmd2 = substr($cmd,0,length($cmd)-$takeoff);
  473. #-- has match ROM part - need to extract this
  474. $tchar = substr($cmd2,0,1);
  475. if( ord($tchar) == 0x55 ){
  476. #-- ID of the device. Careful, because hash is the hash of busmaster
  477. for(my $i=0;$i<8;$i++){
  478. my $j=int(ord(substr($cmd2,$i+1,1))/16);
  479. my $k=ord(substr($cmd,$i+1,1))%16;
  480. $owx_dev.=sprintf "%1x%1x",$j,$k;
  481. $owx_dev.="."
  482. if($i==0 || $i==6);
  483. }
  484. $owx_dev=uc($owx_dev);
  485. $cmd2 = substr($cmd2,9);
  486. $ow_command->{"select"} = device_to_firmata($owx_dev);
  487. #-- padding first 9 bytes into result string, since we have this
  488. # in the serial interfaces as well
  489. $res .= "000000000";
  490. }
  491. #-- has data part
  492. if ($cmd2) {
  493. my @data = unpack "C*", $cmd2;
  494. $ow_command->{"write"} = \@data;
  495. $res.=$cmd2;
  496. }
  497. #-- pre-content of result buffer
  498. $hash->{PREBUFFER} = $res;
  499. #-- always receive part ??
  500. # if ( $numread > 0 ) {
  501. $ow_command->{"read"} = length($cmd);
  502. #Firmata sends 0-address on read after skip
  503. $owx_dev = '00.000000000000.00' unless defined $owx_dev;
  504. my $id = $hash->{FRM_OWX_CORRELATIONID};
  505. $ow_command->{"id"} = $hash->{FRM_OWX_CORRELATIONID};
  506. $hash->{FRM_OWX_REQUESTS}->{$id} = {
  507. command => $ow_command,
  508. device => $owx_dev
  509. };
  510. delete $hash->{FRM_OWX_REPLIES}->{$owx_dev};
  511. $hash->{FRM_OWX_CORRELATIONID} = ($id + 1) & 0xFFFF;
  512. #}
  513. eval {
  514. $firmata->onewire_command_series( $pin, $ow_command );
  515. };
  516. if ($@) {
  517. main::Log3 $name,1,"OWX_FRM::Write device $name exception " . GP_Catch($@);
  518. return 0
  519. }
  520. }
  521. #######################################################################################
  522. #
  523. # observer function for listening to the FRM device
  524. #
  525. #######################################################################################
  526. sub observer
  527. {
  528. my ( $data,$hash ) = @_;
  529. my $command = $data->{command};
  530. COMMAND_HANDLER: {
  531. $command eq "READ_REPLY" and do {
  532. my $id = $data->{id};
  533. my $request = (defined $id) ? $hash->{FRM_OWX_REQUESTS}->{$id} : undef;
  534. unless (defined $request) {
  535. return unless (defined $data->{device});
  536. my $owx_device = firmata_to_device($data->{device});
  537. my %requests = %{$hash->{FRM_OWX_REQUESTS}};
  538. foreach my $key (keys %requests) {
  539. if ($requests{$key}->{device} eq $owx_device) {
  540. $request = $requests{$key};
  541. $id = $key;
  542. last;
  543. };
  544. };
  545. };
  546. return unless (defined $request);
  547. my $owx_data = pack "C*",@{$data->{data}};
  548. my $owx_device = $request->{device};
  549. $hash->{FRM_OWX_REPLIES}->{$owx_device} = $owx_data;
  550. ##
  551. $hash->{FRM_OWX_CURRDEV} = $owx_device;
  552. delete $hash->{FRM_OWX_REQUESTS}->{$id};
  553. return main::OWX_Read($hash);
  554. };
  555. ($command eq "SEARCH_REPLY" or $command eq "SEARCH_ALARMS_REPLY") and do {
  556. my @owx_devices = ();
  557. foreach my $device (@{$data->{devices}}) {
  558. push @owx_devices, firmata_to_device($device);
  559. }
  560. if ($command eq "SEARCH_REPLY") {
  561. $hash->{DEVS} = \@owx_devices;
  562. #$main::attr{$hash->{NAME}}{"ow-devices"} = join " ",@owx_devices;
  563. } else {
  564. $hash->{ALARMDEVS} = \@owx_devices;
  565. }
  566. last;
  567. };
  568. }
  569. }
  570. #######################################################################################
  571. #
  572. # translation of strings
  573. #
  574. #######################################################################################
  575. sub device_to_firmata
  576. {
  577. my @device;
  578. foreach my $hbyte ( unpack "A2xA2A2A2A2A2A2xA2", shift ) {
  579. push @device, hex $hbyte;
  580. }
  581. return {
  582. family => shift @device,
  583. crc => pop @device,
  584. identity => \@device,
  585. }
  586. }
  587. sub firmata_to_device
  588. {
  589. my $device = shift;
  590. return sprintf( "%02X.%02X%02X%02X%02X%02X%02X.%02X", $device->{family}, @{ $device->{identity} }, $device->{crc} );
  591. }
  592. 1;
  593. =pod
  594. =item device
  595. =item summary to address an OWX interface device via Arduino Firmata
  596. =item summary_DE zur Adressierung eines OWX Interface Device mit Arduino Firmata
  597. =begin html
  598. <a name="OWX_FRM"></a>
  599. <h3>OWX_FRM</h3>
  600. <ul>
  601. See <a href="/fhem/docs/commandref.html#OWX">OWX</a>
  602. </ul>
  603. =end html
  604. =begin html_DE
  605. <a name="OWX_FRM"></a>
  606. <h3>OWX_FRM</h3>
  607. <ul>
  608. <a href="http://fhemwiki.de/wiki/Interfaces_f%C3%BCr_1-Wire">Deutsche Dokumentation im Wiki</a> vorhanden, die englische Version gibt es hier: <a href="/fhem/docs/commandref.html#OWX">OWX</a>
  609. </ul>
  610. =end html_DE
  611. =cut