00_OWX.pm 59 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740
  1. ########################################################################################
  2. #
  3. # FHEM module (Next Generation) to commmunicate with 1-Wire bus devices
  4. # * via an active DS2480/DS9097U bus master interface attached to an USB port
  5. # * via an active DS2480 bus master interface attached to a TCP/IP-UART device
  6. # * via a network-attached CUNO
  7. # * via a COC attached to a Raspberry Pi
  8. # * via an Arduino running OneWireFirmata
  9. #
  10. # Prof. Dr. Peter A. Henning
  11. #
  12. # $Id: 00_OWX.pm 16671 2018-04-29 05:06:35Z phenning $
  13. #
  14. ########################################################################################
  15. ########################################################################################
  16. #
  17. # This programm is free software; you can redistribute it and/or modify
  18. # it under the terms of the GNU General Public License as published by
  19. # the Free Software Foundation; either version 2 of the License, or
  20. # (at your option) any later version.
  21. #
  22. # The GNU General Public License can be found at
  23. # http://www.gnu.org/copyleft/gpl.html.
  24. # A copy is found in the textfile GPL.txt and important notices to the license
  25. # from the author is found in LICENSE.txt distributed with these scripts.
  26. #
  27. # This script is distributed in the hope that it will be useful,
  28. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  29. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  30. # GNU General Public License for more details.
  31. #
  32. ########################################################################################
  33. package main;
  34. use strict;
  35. use warnings;
  36. use Time::HiRes qw(gettimeofday);
  37. use Data::Dumper qw(Dumper);
  38. use DevIo;
  39. #-- unfortunately some things OS-dependent
  40. my $SER_regexp;
  41. if( $^O =~ /Win/ ) {
  42. require Win32::SerialPort;
  43. $SER_regexp= "com";
  44. } else {
  45. require Device::SerialPort;
  46. $SER_regexp= "/dev/";
  47. }
  48. sub Log3($$$);
  49. use vars qw{%owg_family %gets %sets $owx_version $owx_debug};
  50. # 1-Wire devices
  51. # http://owfs.sourceforge.net/family.html
  52. %owg_family = (
  53. "01" => ["DS2401/DS2411/DS1990A","OWID DS2401"],
  54. "05" => ["DS2405","OWID DS2405"],
  55. "09" => ["DS2502","OWID DS2502"],
  56. "10" => ["DS18S20/DS1920","OWTHERM DS1820"],
  57. "12" => ["DS2406/DS2507","OWSWITCH DS2406"],
  58. "1B" => ["DS2436","OWID 1B"],
  59. "1D" => ["DS2423","OWCOUNT DS2423"],
  60. "20" => ["DS2450","OWAD DS2450"],
  61. "22" => ["DS1822","OWTHERM DS1822"],
  62. "23" => ["DS2433","OWID 23"],
  63. "24" => ["DS2415/DS1904","OWID 24"],
  64. "26" => ["DS2438","OWMULTI DS2438"],
  65. "27" => ["DS2417","OWID 27"],
  66. "28" => ["DS18B20","OWTHERM DS18B20"],
  67. "29" => ["DS2408","OWSWITCH DS2408"],
  68. "2C" => ["DS2890","OWVAR DS2890"],
  69. "3A" => ["DS2413","OWSWITCH DS2413"],
  70. "3B" => ["DS1825","OWID 3B"],
  71. "7E" => ["OW-ENV","OWID 7E"], #Environmental sensor
  72. "81" => ["DS1420","OWID 81"],
  73. "A6" => ["DS2438","OWMULTI DS2438a"],
  74. "FF" => ["LCD","OWLCD"]
  75. );
  76. #-- These we may get on request
  77. %gets = (
  78. "alarms" => "A",
  79. "devices" => "D",
  80. "version" => "V",
  81. "qstatus" => "P"
  82. );
  83. #-- These occur in a pulldown menu as settable values for the bus master
  84. # (expert mode: all, standard mode: only reopen)
  85. %sets = (
  86. "close" => "c",
  87. "open" => "O",
  88. "reopen" => "R",
  89. "discover" => "C",
  90. "detect" => "T",
  91. "disconnected" => "D",
  92. "process" => "P"
  93. );
  94. #-- some globals needed for the 1-Wire module
  95. $owx_version="7.11";
  96. #-- debugging now verbosity, this is just for backward compatibility
  97. $owx_debug=0;
  98. ########################################################################################
  99. #
  100. # The following subroutines are independent of the bus interface
  101. #
  102. ########################################################################################
  103. #
  104. # OWX_Initialize
  105. #
  106. # Parameter hash = hash of device addressed
  107. #
  108. ########################################################################################
  109. sub OWX_Initialize ($) {
  110. my ($hash) = @_;
  111. $hash->{Clients} = ":OWAD:OWCOUNT:OWID:OWLCD:OWMULTI:OWSWITCH:OWTHERM:OWVAR:";
  112. $hash->{WriteFn} = "OWX_Write";
  113. $hash->{ReadFn} = "OWX_Read";
  114. $hash->{ReadyFn} = "OWX_Ready";
  115. $hash->{DefFn} = "OWX_Define";
  116. $hash->{UndefFn} = "OWX_Undef";
  117. $hash->{GetFn} = "OWX_Get";
  118. $hash->{SetFn} = "OWX_Set";
  119. $hash->{AttrFn} = "OWX_Attr";
  120. $hash->{AttrList}= "asynchronous:0,1 dokick:0,1 ".
  121. "interval timeout opendelay expert:0_def,1_detail ".
  122. "IODev ".
  123. $readingFnAttributes;
  124. }
  125. ########################################################################################
  126. #
  127. # OWX_Define - Implements Define function
  128. #
  129. # Parameter hash = hash of device addressed, def = definition string
  130. #
  131. ########################################################################################
  132. sub OWX_Define ($$) {
  133. my ($hash, $def) = @_;
  134. my @a = split("[ \t][ \t]*", $def);
  135. my $hwdevice;
  136. my $ret;
  137. #-- check syntax
  138. if(int(@a) < 3){
  139. return "OWX: Syntax error - must be define <name> OWX <serial-device>|<ip-address>[:<port>]|<i2c-bus>:<i2c-addr>|<cuno/coc-device>|<firmata-device>:<firmata-pin>"
  140. }
  141. Log3 $hash->{NAME},2,"OWX: Warning - Some parameter(s) ignored, must be define <name> OWX <serial-device>|<ip-address>[:<port>]|<i2c-bus>:<i2c-addr>|<cuno/coc-device>|<firmata-device>:<firmata-pin>"
  142. if( int(@a)>3 );
  143. my $dev = $a[2];
  144. #-- Dummy 1-Wire ROM identifier, empty device lists
  145. $hash->{ROM_ID} = "FF";
  146. $hash->{DEVS} = ();
  147. $hash->{DEVHASH}{"$a[0]"}="Busmaster";
  148. $hash->{INITDONE} = 0;
  149. #XXX
  150. $hash->{PARTIAL} = "";
  151. delete $hash->{ALARMDEVS};
  152. delete $hash->{followAlarms};
  153. delete $hash->{version};
  154. #-- Clear from leftovers of ASYNC
  155. delete $hash->{ASYNC};
  156. #-- First step - different methods and parameters for setup
  157. #-- check if we have a serial device attached
  158. if ($dev =~ m|$SER_regexp|i) {
  159. require "$attr{global}{modpath}/FHEM/11_OWX_SER.pm";
  160. $hwdevice = OWX_SER->new($hash);
  161. #-- check if we have a TCP connection
  162. }elsif( $dev =~ m|\d\d?\d?\.\d\d?\d?\.\d\d?\d?\.\d\d?\d?(\:\d+)?| ){
  163. require "$attr{global}{modpath}/FHEM/11_OWX_TCP.pm";
  164. #$hash->{Protocol} = "telnet"
  165. $hwdevice = OWX_TCP->new($hash);
  166. #-- check if we have an i2c interface attached
  167. }elsif( $dev =~ m|^\d\:\d\d| ){
  168. require "$attr{global}{modpath}/FHEM/11_OWX_I2C.pm";
  169. $hwdevice = OWX_I2C->new($hash);
  170. #-- check if we have a COC/CUNO interface attached
  171. }elsif( $defs{$dev} && $defs{$dev}->{VERSION} && $defs{$dev}->{VERSION} =~ m/CSM|CUNO|MapleCUN...(4|5|6|7|C|D|E|F)/ ){
  172. require "$attr{global}{modpath}/FHEM/11_OWX_CCC.pm";
  173. $hwdevice = OWX_CCC->new($hash);
  174. #-- check if we are connecting to Arduino (via FRM):
  175. } elsif ($dev =~ /.*\:\d{1,2}$/) {
  176. require "$attr{global}{modpath}/FHEM/11_OWX_FRM.pm";
  177. $hwdevice = OWX_FRM->new($hash);
  178. } else {
  179. return "OWX: Define failed, unable to identify interface type $dev for bus ".$hash->{NAME};
  180. };
  181. #-- Second step: perform low level init of device
  182. #Log 1,"OWX: Performing define and low level init of bus ".$hash->{NAME};
  183. $ret = $hwdevice->Define($def);
  184. #-- cancel definition of OWX if failed
  185. return $ret if $ret;
  186. $hash->{OWX} = $hwdevice;
  187. #-- Default settings
  188. $hash->{interval} = 300; # kick every 5 minutes
  189. $hash->{timeout} = 2; # timeout 2 seconds
  190. $hash->{ALARMED} = 0;
  191. $hash->{PRESENT} = 1;
  192. #-- Third step: perform high level init for 1-Wire Bus in minute or so
  193. ###
  194. InternalTimer(time()+60, "OWX_Init", $hash,0);
  195. return undef;
  196. }
  197. ########################################################################################
  198. #
  199. # OWX_Alarms - Find devices on the 1-Wire bus,
  200. # which have the alarm flag set
  201. #
  202. # Parameter hash = hash of bus master
  203. #
  204. # Return: Message or list of alarmed devices
  205. #
  206. ########################################################################################
  207. sub OWX_Alarms ($) {
  208. my ($hash) = @_;
  209. my @owx_alarm_names=();
  210. #-- get the interface
  211. my $name = $hash->{NAME};
  212. my $owx = $hash->{OWX};
  213. my $devhash = $hash->{DEVHASH};
  214. foreach my $owx_dev (sort keys %{$devhash}){
  215. push(@owx_alarm_names,$owx_dev)
  216. if $defs{$owx_dev}->{ALARM};
  217. }
  218. my $res = int(@owx_alarm_names);
  219. if( $res == 0){
  220. $hash->{ALARMED}=0;
  221. return "OWX: No alarmed 1-Wire devices found on bus $name";
  222. } else{
  223. $hash->{ALARMED}=$res;
  224. return "OWX: $res alarmed 1-Wire devices found on bus $name (".join(",",@owx_alarm_names).")";
  225. }
  226. ############### THE FOLLOWING IST DEAD CODE, BECAUSE ALARM SEARCH DOES NOT WORK PROPERLY - WHY ?
  227. if (defined $owx) {
  228. $res = $owx->Alarms();
  229. } else {
  230. #-- interface error
  231. my $owx_interface = $hash->{INTERFACE};
  232. if( !(defined($owx_interface))){
  233. return undef;
  234. } else {
  235. return "OWX: Alarms called with unknown interface $owx_interface on bus $name";
  236. }
  237. }
  238. if( $res == 0){
  239. return "OWX: No alarmed 1-Wire devices found on bus $name";
  240. }
  241. #-- walk through all the devices to get their proper fhem names
  242. foreach my $fhem_dev (sort keys %main::defs) {
  243. #-- skip if busmaster
  244. next if( $name eq $main::defs{$fhem_dev}{NAME} );
  245. #-- all OW types start with OW
  246. next if( substr($main::defs{$fhem_dev}{TYPE},0,2) ne "OW");
  247. foreach my $owx_dev (@{$hash->{ALARMDEVS}}) {
  248. #-- two pieces of the ROM ID found on the bus
  249. my $owx_rnf = substr($owx_dev,3,12);
  250. my $owx_f = substr($owx_dev,0,2);
  251. my $id_owx = $owx_f.".".$owx_rnf;
  252. #-- skip if not in alarm list
  253. if( $owx_dev eq $main::defs{$fhem_dev}{ROM_ID} ){
  254. $main::defs{$fhem_dev}{STATE} = "Alarmed";
  255. push(@owx_alarm_names,$main::defs{$fhem_dev}{NAME});
  256. }
  257. }
  258. }
  259. #-- so far, so good - what do we want to do with this ?
  260. return "OWX: $res alarmed 1-Wire devices found on bus $name (".join(",",@owx_alarm_names).")";
  261. }
  262. #######################################################################################
  263. #
  264. # OWX_Attr - Set one attribute value for device
  265. #
  266. # Parameter hash = hash of device addressed
  267. # a = argument array
  268. #
  269. ########################################################################################
  270. sub OWX_Attr(@) {
  271. my ($do,$name,$key,$value) = @_;
  272. my $hash = $main::defs{$name};
  273. my $queue = $hash->{QUEUE};
  274. my $ret;
  275. if ( $do eq "set") {
  276. ARGUMENT_HANDLER: {
  277. $key eq "asynchronous" and do {
  278. $hash->{ASYNCHRONOUS} = ($value==1 ? 1 : 0);
  279. #-- stop queue prcoessing and delete it
  280. if(!$value){
  281. RemoveInternalTimer ("queue:$name");
  282. delete($hash->{QUEUE});
  283. readingsSingleUpdate($hash,"queue",0,0);
  284. OWX_Reset($hash);
  285. }
  286. last;
  287. };
  288. $key eq "timeout" and do {
  289. $hash->{timeout} = $value;
  290. last;
  291. };
  292. $key eq "opendelay" and do {
  293. $hash->{opendelay} = $value;
  294. last;
  295. };
  296. $key eq "dokick" and do {
  297. $hash->{dokick} = $value;
  298. last;
  299. };
  300. $key eq "interval" and do {
  301. $hash->{interval} = $value;
  302. if ($main::init_done) {
  303. OWX_Kick($hash);
  304. }
  305. last;
  306. }
  307. }
  308. }
  309. return $ret;
  310. }
  311. ########################################################################################
  312. #
  313. # OWX_CRC - Check the CRC code of a device address in @owx_ROM_ID
  314. #
  315. # Parameter romid = if not reference to array, return the CRC8 value instead of checking it
  316. #
  317. ########################################################################################
  318. my @crc8_table = (
  319. 0, 94,188,226, 97, 63,221,131,194,156,126, 32,163,253, 31, 65,
  320. 157,195, 33,127,252,162, 64, 30, 95, 1,227,189, 62, 96,130,220,
  321. 35,125,159,193, 66, 28,254,160,225,191, 93, 3,128,222, 60, 98,
  322. 190,224, 2, 92,223,129, 99, 61,124, 34,192,158, 29, 67,161,255,
  323. 70, 24,250,164, 39,121,155,197,132,218, 56,102,229,187, 89, 7,
  324. 219,133,103, 57,186,228, 6, 88, 25, 71,165,251,120, 38,196,154,
  325. 101, 59,217,135, 4, 90,184,230,167,249, 27, 69,198,152,122, 36,
  326. 248,166, 68, 26,153,199, 37,123, 58,100,134,216, 91, 5,231,185,
  327. 140,210, 48,110,237,179, 81, 15, 78, 16,242,172, 47,113,147,205,
  328. 17, 79,173,243,112, 46,204,146,211,141,111, 49,178,236, 14, 80,
  329. 175,241, 19, 77,206,144,114, 44,109, 51,209,143, 12, 82,176,238,
  330. 50,108,142,208, 83, 13,239,177,240,174, 76, 18,145,207, 45,115,
  331. 202,148,118, 40,171,245, 23, 73, 8, 86,180,234,105, 55,213,139,
  332. 87, 9,235,181, 54,104,138,212,149,203, 41,119,244,170, 72, 22,
  333. 233,183, 85, 11,136,214, 52,106, 43,117,151,201, 74, 20,246,168,
  334. 116, 42,200,150, 21, 75,169,247,182,232, 10, 84,215,137,107, 53);
  335. sub OWX_CRC ($) {
  336. my ($romid) = @_;
  337. my $crc8=0;
  338. my @owx_ROM_ID;
  339. if( ref ($romid) eq 'ARRAY' ){
  340. @owx_ROM_ID = @$romid;
  341. for(my $i=0; $i<8; $i++){
  342. $crc8 = $crc8_table[ $crc8 ^ $owx_ROM_ID[$i] ];
  343. }
  344. return $crc8;
  345. } else {
  346. #-- from search string to byte id
  347. $romid=~s/\.//g;
  348. for(my $i=0;$i<8;$i++){
  349. $owx_ROM_ID[$i]=hex(substr($romid,2*$i,2));
  350. }
  351. for(my $i=0; $i<7; $i++){
  352. $crc8 = $crc8_table[ $crc8 ^ $owx_ROM_ID[$i] ];
  353. }
  354. return $crc8;
  355. }
  356. }
  357. ########################################################################################
  358. #
  359. # OWX_CRC8 - Check the CRC8 code of an a byte string
  360. #
  361. # Parameter string, crc.
  362. # If crc is defined, make a comparison, otherwise output crc8
  363. #
  364. ########################################################################################
  365. sub OWX_CRC8 ($$) {
  366. my ($string,$crc) = @_;
  367. my $crc8=0;
  368. my @strhex;
  369. for(my $i=0; $i<length($string); $i++){
  370. $strhex[$i]=ord(substr($string,$i,1));
  371. $crc8 = $crc8_table[ $crc8 ^ $strhex[$i] ];
  372. }
  373. if( defined($crc) ){
  374. my $crcx = ord($crc);
  375. if ( $crcx == $crc8 ){
  376. return 1;
  377. }else{
  378. return 0;
  379. }
  380. }else{
  381. return sprintf("%#2x", $crc8);
  382. }
  383. }
  384. ########################################################################################
  385. #
  386. # OWX_CRC16 - Calculate the CRC16 code of a string
  387. #
  388. # TODO UNFINISHED CODE
  389. #
  390. # Parameter crc - previous CRC code, c next character
  391. #
  392. ########################################################################################
  393. my @crc16_table = (
  394. 0x0000, 0xC0C1, 0xC181, 0x0140, 0xC301, 0x03C0, 0x0280, 0xC241,
  395. 0xC601, 0x06C0, 0x0780, 0xC741, 0x0500, 0xC5C1, 0xC481, 0x0440,
  396. 0xCC01, 0x0CC0, 0x0D80, 0xCD41, 0x0F00, 0xCFC1, 0xCE81, 0x0E40,
  397. 0x0A00, 0xCAC1, 0xCB81, 0x0B40, 0xC901, 0x09C0, 0x0880, 0xC841,
  398. 0xD801, 0x18C0, 0x1980, 0xD941, 0x1B00, 0xDBC1, 0xDA81, 0x1A40,
  399. 0x1E00, 0xDEC1, 0xDF81, 0x1F40, 0xDD01, 0x1DC0, 0x1C80, 0xDC41,
  400. 0x1400, 0xD4C1, 0xD581, 0x1540, 0xD701, 0x17C0, 0x1680, 0xD641,
  401. 0xD201, 0x12C0, 0x1380, 0xD341, 0x1100, 0xD1C1, 0xD081, 0x1040,
  402. 0xF001, 0x30C0, 0x3180, 0xF141, 0x3300, 0xF3C1, 0xF281, 0x3240,
  403. 0x3600, 0xF6C1, 0xF781, 0x3740, 0xF501, 0x35C0, 0x3480, 0xF441,
  404. 0x3C00, 0xFCC1, 0xFD81, 0x3D40, 0xFF01, 0x3FC0, 0x3E80, 0xFE41,
  405. 0xFA01, 0x3AC0, 0x3B80, 0xFB41, 0x3900, 0xF9C1, 0xF881, 0x3840,
  406. 0x2800, 0xE8C1, 0xE981, 0x2940, 0xEB01, 0x2BC0, 0x2A80, 0xEA41,
  407. 0xEE01, 0x2EC0, 0x2F80, 0xEF41, 0x2D00, 0xEDC1, 0xEC81, 0x2C40,
  408. 0xE401, 0x24C0, 0x2580, 0xE541, 0x2700, 0xE7C1, 0xE681, 0x2640,
  409. 0x2200, 0xE2C1, 0xE381, 0x2340, 0xE101, 0x21C0, 0x2080, 0xE041,
  410. 0xA001, 0x60C0, 0x6180, 0xA141, 0x6300, 0xA3C1, 0xA281, 0x6240,
  411. 0x6600, 0xA6C1, 0xA781, 0x6740, 0xA501, 0x65C0, 0x6480, 0xA441,
  412. 0x6C00, 0xACC1, 0xAD81, 0x6D40, 0xAF01, 0x6FC0, 0x6E80, 0xAE41,
  413. 0xAA01, 0x6AC0, 0x6B80, 0xAB41, 0x6900, 0xA9C1, 0xA881, 0x6840,
  414. 0x7800, 0xB8C1, 0xB981, 0x7940, 0xBB01, 0x7BC0, 0x7A80, 0xBA41,
  415. 0xBE01, 0x7EC0, 0x7F80, 0xBF41, 0x7D00, 0xBDC1, 0xBC81, 0x7C40,
  416. 0xB401, 0x74C0, 0x7580, 0xB541, 0x7700, 0xB7C1, 0xB681, 0x7640,
  417. 0x7200, 0xB2C1, 0xB381, 0x7340, 0xB101, 0x71C0, 0x7080, 0xB041,
  418. 0x5000, 0x90C1, 0x9181, 0x5140, 0x9301, 0x53C0, 0x5280, 0x9241,
  419. 0x9601, 0x56C0, 0x5780, 0x9741, 0x5500, 0x95C1, 0x9481, 0x5440,
  420. 0x9C01, 0x5CC0, 0x5D80, 0x9D41, 0x5F00, 0x9FC1, 0x9E81, 0x5E40,
  421. 0x5A00, 0x9AC1, 0x9B81, 0x5B40, 0x9901, 0x59C0, 0x5880, 0x9841,
  422. 0x8801, 0x48C0, 0x4980, 0x8941, 0x4B00, 0x8BC1, 0x8A81, 0x4A40,
  423. 0x4E00, 0x8EC1, 0x8F81, 0x4F40, 0x8D01, 0x4DC0, 0x4C80, 0x8C41,
  424. 0x4400, 0x84C1, 0x8581, 0x4540, 0x8701, 0x47C0, 0x4680, 0x8641,
  425. 0x8201, 0x42C0, 0x4380, 0x8341, 0x4100, 0x81C1, 0x8081, 0x4040
  426. );
  427. sub OWX_CRC16 ($$$) {
  428. my ($string,$crclo,$crchi) = @_;
  429. my $crc16=0;
  430. my @strhex;
  431. #Log3 $name, 1,"CRC16 calculated for string of length ".length($string);
  432. for(my $i=0; $i<length($string); $i++){
  433. $strhex[$i]=ord(substr($string,$i,1));
  434. $crc16 = $crc16_table[ ($crc16 ^ $strhex[$i]) & 0xFF ] ^ ($crc16 >> 8);
  435. }
  436. if( defined($crclo) & defined($crchi) ){
  437. my $crc = (255-ord($crclo))+256*(255-ord($crchi));
  438. if ($crc == $crc16 ){
  439. return 1;
  440. }else{
  441. return 0;
  442. }
  443. }else{
  444. return $crc16;
  445. }
  446. }
  447. ########################################################################################
  448. #
  449. # OWX_Discover - Discover devices on the 1-Wire bus,
  450. # autocreate devices if not already present
  451. #
  452. # Parameter hash = hash of bus master
  453. #
  454. # Return: List of devices in table format or undef
  455. #
  456. ########################################################################################
  457. sub OWX_Discover ($) {
  458. my ($hash) = @_;
  459. my $name = $hash->{NAME};
  460. my $res;
  461. my $ret= "";
  462. my ($chip,$acstring,$acname,$exname);
  463. my $ow_dev;
  464. my $owx_rnf;
  465. my $owx_f;
  466. my $owx_crc;
  467. my $id_owx;
  468. my $match;
  469. #-- get the interface - this should be the hardware device ???
  470. my $owx = $hash->{OWX};
  471. my $owx1 = $hash->{INTERFACE};
  472. my $owx2 = $hash->{DeviceName};
  473. my $owx3 = $hash->{HWDEVICE};
  474. my @owx_names =();
  475. $hash->{DEVHASH}=();
  476. $hash->{DEVHASH}{"$name"} = "Busmaster";
  477. #-- Discover all devices on the 1-Wire bus, they will be found in $hash->{DEVS}
  478. if (defined $owx) {
  479. $res = $owx->Discover();
  480. } else {
  481. my $owx_interface = $hash->{INTERFACE};
  482. if( !defined($owx_interface) ) {
  483. return undef;
  484. } else {
  485. Log3 $name, 1,"OWX_Discover on bus $name called with unknown interface $owx_interface";
  486. return undef;
  487. }
  488. }
  489. if (defined $res and (ref($res) eq "ARRAY")) {
  490. $hash->{DEVS} = $res;
  491. }
  492. #-- Go through all devices found on this bus
  493. foreach my $owx_dev (@{$hash->{DEVS}}) {
  494. #-- ignore those which do not have the proper pattern
  495. if( !($owx_dev =~ m/[0-9A-F]{2}\.[0-9A-F]{12}\.[0-9A-F]{2}/) ){
  496. Log3 $name, 3,"OWX_Discover found invalid 1-Wire device ID $owx_dev on bus $name, ignoring it";
  497. next;
  498. }
  499. #-- three pieces of the ROM ID found on the bus
  500. $owx_rnf = substr($owx_dev,3,12);
  501. $owx_f = substr($owx_dev,0,2);
  502. $owx_crc = substr($owx_dev,15,3);
  503. $id_owx = $owx_f.".".$owx_rnf;
  504. $match = 0;
  505. #-- Check against all existing devices
  506. foreach my $fhem_dev (sort keys %main::defs) {
  507. #-- skip if busmaster
  508. # next if( $hash->{NAME} eq $main::defs{$fhem_dev}{NAME} );
  509. #-- all OW types start with OW
  510. next if( !defined($main::defs{$fhem_dev}{TYPE}));
  511. next if( substr($main::defs{$fhem_dev}{TYPE},0,2) ne "OW");
  512. my $id_fhem = substr($main::defs{$fhem_dev}{ROM_ID},0,15);
  513. #-- skip interface device
  514. next if( length($id_fhem) != 15 );
  515. #-- yes, family id and ROM id are the same
  516. if( $id_fhem eq $id_owx ) {
  517. $exname=$main::defs{$fhem_dev}{NAME};
  518. push(@owx_names,$exname);
  519. #-- replace the ROM ID by the proper value including CRC
  520. $main::defs{$fhem_dev}{ROM_ID} = $owx_dev;
  521. $main::defs{$fhem_dev}{PRESENT} = 1;
  522. $main::defs{$fhem_dev}{ERRCOUNT} = 0;
  523. #-- add to DEVHASH
  524. $hash->{DEVHASH}{"$exname"}=$owx_dev;
  525. $match = 1;
  526. last;
  527. #-- no, only duplicate ROMID
  528. }elsif( substr($id_fhem,3,12) eq substr($id_owx,3,12) ) {
  529. Log3 $name, 1, "OWX_Discover: Warning, $fhem_dev on bus $name is defined with duplicate ROM ID ";
  530. }
  531. }
  532. #-- Determine the device type
  533. if(exists $owg_family{$owx_f}) {
  534. $chip = $owg_family{$owx_f}[0];
  535. $acstring = $owg_family{$owx_f}[1];
  536. }else{
  537. Log3 $name, 2, "OWX_Discover: Device with unknown family code '$owx_f' found on bus $name";
  538. #-- All unknown families are ID only
  539. $chip = "unknown";
  540. $acstring = "OWID $owx_f";
  541. }
  542. #Log3 $name, 1,"###\nfor the following device match=$match, chip=$chip name=$name acstring=$acstring";
  543. #-- device exists
  544. if( $match==1 ){
  545. $ret .= sprintf("%s.%s %-14s %s\n", $owx_f,$owx_rnf, $chip, $exname);
  546. #-- device unknown, autocreate
  547. }else{
  548. #-- example code for checking global autocreate - do we want this ?
  549. #foreach my $d (keys %defs) {
  550. #next if($defs{$d}{TYPE} ne "autocreate");
  551. #return undef if(AttrVal($defs{$d}{NAME},"disable",undef));
  552. $acname = sprintf "OWX_%s_%s",$owx_f,$owx_rnf;
  553. #Log3 $name, 1, "to define $acname $acstring $owx_rnf";
  554. $res = CommandDefine(undef,"$acname $acstring $owx_rnf");
  555. if($res) {
  556. $ret.= "OWX_Discover: Error autocreating device with $acname $acstring $owx_rnf: $res\n";
  557. } else{
  558. select(undef,undef,undef,0.01);
  559. push(@owx_names,$acname);
  560. $main::defs{$acname}{PRESENT}=1;
  561. #-- THIS IODev, default room (model is set in the device module)
  562. CommandAttr (undef,"$acname IODev $hash->{NAME}");
  563. CommandAttr (undef,"$acname room OWX");
  564. #-- replace the ROM ID by the proper value
  565. $main::defs{$acname}{ROM_ID}=$owx_dev;
  566. #-- add to DEVHASH
  567. $hash->{DEVHASH}{"$acname"}=$owx_dev;
  568. $ret .= sprintf("%s.%s %-10s %s\n", $owx_f,$owx_rnf, $chip, $acname);
  569. }
  570. }
  571. }
  572. #-- final step: Undefine all 1-Wire devices which
  573. # are autocreated and
  574. # not discovered on this bus
  575. # but have this IODev
  576. foreach my $fhem_dev (sort keys %main::defs) {
  577. #-- skip if malformed device
  578. #next if( !defined($main::defs{$fhem_dev}{NAME}) );
  579. #-- all OW types start with OW, but safeguard against deletion of other devices
  580. #next if( !defined($main::defs{$fhem_dev}{TYPE}));
  581. next if( substr($main::defs{$fhem_dev}{TYPE},0,2) ne "OW");
  582. next if( uc($main::defs{$fhem_dev}{TYPE}) eq "OWX");
  583. next if( uc($main::defs{$fhem_dev}{TYPE}) eq "OWFS");
  584. next if( uc($main::defs{$fhem_dev}{TYPE}) eq "OWSERVER");
  585. next if( uc($main::defs{$fhem_dev}{TYPE}) eq "OWDEVICE");
  586. #-- restrict to autocreated devices
  587. next if( $main::defs{$fhem_dev}{NAME} !~ m/OWX_[0-9a-fA-F]{2}_/);
  588. #-- skip if the device is present.
  589. next if( $main::defs{$fhem_dev}{PRESENT} == 1);
  590. #-- skip if different IODev, but only if other IODev exists
  591. if ( $main::defs{$fhem_dev}{IODev} ){
  592. next if( $main::defs{$fhem_dev}{IODev}{NAME} ne $hash->{NAME} );
  593. }
  594. Log3 $name, 1, "OWX_Discover: Device $main::defs{$fhem_dev}{NAME} of type $main::defs{$fhem_dev}{TYPE} is unused, consider deletion !";
  595. }
  596. #-- Log the discovered devices
  597. Log3 $name, 1, "OWX_Discover: 1-Wire devices found on bus $name (".join(",",@owx_names).")";
  598. #-- tabular view as return value
  599. return "OWX_Discover: 1-Wire devices found on bus $name \n".$ret;
  600. }
  601. ########################################################################################
  602. #
  603. # OWX_Get - Implements GetFn function
  604. #
  605. # Parameter hash = hash of the bus master a = argument array
  606. #
  607. ########################################################################################
  608. sub OWX_Get($@) {
  609. my ($hash, @a) = @_;
  610. return "OWX: Get needs exactly one parameter" if(@a != 2);
  611. my $name = $hash->{NAME};
  612. my $owx_dev = $hash->{ROM_ID};
  613. if( $a[1] eq "alarms") {
  614. my $res = OWX_Alarms($hash);
  615. #-- process result
  616. return $res
  617. } elsif( $a[1] eq "devices") {
  618. my $res = OWX_Discover($hash);
  619. #-- process result
  620. return $res
  621. } elsif( $a[1] eq "version") {
  622. return $owx_version;
  623. #-- expert mode
  624. } elsif( $a[1] eq "qstatus") {
  625. my $qlen = ($hash->{QUEUE} ? scalar(@{$hash->{QUEUE}}) : 0);
  626. my $state = $hash->{STATE};
  627. my $dev = $hash->{DeviceName};
  628. my $busy = ($hash->{BUSY})? "BUSY" : "NOT BUSY";
  629. my $block = ($hash->{BLOCK})? "BLOCKED" : "NOT BLOCKED";
  630. $hash->{BUSY} = 1;
  631. my $res = "OWX: Queue $name: => dev=$dev length=$qlen, state=$state, $busy, $block\n";
  632. foreach my $diapoint (@{$hash->{QUEUE}}) {
  633. $res .= " => ".$diapoint->{owx_dev}." context ".$diapoint->{context}." expecting ".$diapoint->{numread}." bytes, ".$diapoint->{status}."\n";
  634. }
  635. return $res;
  636. } else {
  637. return "OWX_Get with unknown argument $a[1], choose one of ".
  638. ( (AttrVal($name,"expert","") eq "1_detail") ? join(":noArg ", sort keys %gets).":noArg" : "alarms devices version");
  639. }
  640. }
  641. #######################################################################################
  642. #
  643. # OWX_Init - High Level Init of 1-Wire Bus
  644. #
  645. # Parameter hash = hash of bus master
  646. #
  647. # Return 1 : OK
  648. # 0 : not OK
  649. #
  650. ########################################################################################
  651. sub OWX_Init ($) {
  652. my ($hash)=@_;
  653. #-- get the interface
  654. my $owx = $hash->{OWX};
  655. my $name= $hash->{NAME};
  656. if( defined($attr{$name}{"asynchronous"}) && ($attr{$name}{"asynchronous"}==1) ){
  657. $hash->{ASYNCHRONOUS} = 1;
  658. }else{
  659. $hash->{ASYNCHRONOUS} = 0;
  660. }
  661. return "OWX_Init finds a disconnected interface"
  662. if($hash->{STATE} eq "disconnected");
  663. Log3 $name,1,"OWX_Init called for bus $name with interface state ".$hash->{STATE}.", now going for detect";
  664. if ($owx) {
  665. #-- Fourth step: see, if a bus interface is detected
  666. if (!($owx->Detect())) {
  667. $hash->{PRESENT} = 0;
  668. #$init_done = 1;
  669. Log3 $name,4,"OWX_Init: Detection failed";
  670. return "OWX_Init Detection failed";
  671. }
  672. } else {
  673. #-- interface error
  674. my $owx_interface = $hash->{INTERFACE};
  675. if( !(defined($owx_interface))){
  676. return "OWX_Init called with undefined interface";
  677. } else {
  678. return "OWX_Init called with unknown interface $owx_interface";
  679. }
  680. }
  681. #-- Fifth step: discovering devices on the bus
  682. OWX_Discover($hash);
  683. $hash->{INITDONE} = 1;
  684. #-- Intiate first alarm detection and eventually conversion
  685. OWX_Kick($hash);
  686. return undef;
  687. }
  688. ########################################################################################
  689. #
  690. # OWX_Kick - Initiate some processes in all devices
  691. #
  692. # Parameter hash = hash of bus master
  693. #
  694. # Return 1 : OK
  695. # 0 : Not OK
  696. #
  697. ########################################################################################
  698. sub OWX_Kick($) {
  699. my($hash) = @_;
  700. #-- Call us in n seconds again.
  701. InternalTimer(gettimeofday()+ $hash->{interval}, "OWX_Kick", $hash,1);
  702. #-- Only if we have the dokick attribute set to 1
  703. if( defined($attr{$hash->{NAME}}{dokick}) && ($attr{$hash->{NAME}}{dokick} eq "1") ){
  704. my $name = $hash->{NAME};
  705. my $interface = $hash->{TYPE};
  706. my $asynchronous = $hash->{ASYNCHRONOUS};
  707. #-- issue the skip ROM command \xCC followed by start conversion command \x44
  708. my $cmd = "\xCC\x44";
  709. #-- OWX interface
  710. if( $interface eq "OWX" ){
  711. #-- OLD OWX interface
  712. if( !$asynchronous ){
  713. OWX_Reset($hash);
  714. OWX_Complex($hash,"","\xCC\x44",0);
  715. #-- NEW OWX interface
  716. }else{
  717. #### master slave context proc owx_dev data crcpart numread startread callback delay
  718. OWX_Qomplex($hash, undef, "kick", 1, "", $cmd, 0, -10, undef, undef, undef)
  719. }
  720. }
  721. }
  722. return 1;
  723. }
  724. ########################################################################################
  725. #
  726. # OWX_Reset - Reset the 1-Wire bus
  727. #
  728. # Parameter hash = hash of bus master
  729. #
  730. # Return 1 : OK
  731. # 0 : not OK
  732. #
  733. ########################################################################################
  734. sub OWX_Reset ($) {
  735. my ($hash)=@_;
  736. #-- get the interface
  737. my $owx = $hash->{OWX};
  738. my $name = $hash->{NAME};
  739. my $queue = $hash->{QUEUE};
  740. my $status = (defined( $queue->[0]->{status})) ? $queue->[0]->{status} : "completed";
  741. if (defined $owx) {
  742. #-- reset only when there is no queue or when the status of the current entry is completed
  743. if( $status eq "completed" ){
  744. return $owx->Reset()
  745. } else {
  746. #Log 1,"==================> Reset attempted on waiting interface";
  747. my $i = 1;
  748. while ( (my @call_details = (caller($i++))) ){
  749. Log 1,$call_details[1].":".$call_details[2]." in function ".$call_details[3];
  750. }
  751. return 0;
  752. }
  753. } else {
  754. #-- interface error
  755. my $owx_interface = $hash->{INTERFACE};
  756. if( !(defined($owx_interface))){
  757. return 0;
  758. } else {
  759. Log3 $name, 3,"OWX: Reset called with unknown interface $owx_interface on bus $name";
  760. return 0;
  761. }
  762. }
  763. }
  764. #######################################################################################
  765. #
  766. # OWX_Read - Callback from FHEM main loop - read from the bus
  767. #
  768. # Parameter hash = hash of bus master
  769. #
  770. ########################################################################################
  771. sub OWX_Read(@) {
  772. my ($hash) = @_;
  773. #-- must not be used when acting synchronously
  774. return undef
  775. if( !$hash->{ASYNCHRONOUS} );
  776. my ($buffer, $buffer2, $qlen, $res, $ret);
  777. #-- master data
  778. my $owx = $hash->{OWX};
  779. my $name = $hash->{NAME};
  780. my $queue = $hash->{QUEUE};
  781. my $query = $queue->[0];
  782. my $staexp = $query->{startread};
  783. my $sldev = $query->{hash}->{NAME};
  784. my $slcont = $query->{context};
  785. my $devlevel = (defined($attr{$name}{verbose})) ? $attr{$name}{verbose} : 0;
  786. my $qdebug = ( ($devlevel > 3) || ($attr{global}{verbose}>3) ) ? 1 : 0;
  787. #--safeguard: really called from queue ?
  788. #if( !defined($sldev) ){
  789. #Log 1,"OWX_Read: Erroneous call from somewhere in FHEM ???";
  790. #my $i = 1;
  791. #Log 1, "=============> Empty call of OWX_Read Stack Trace:";
  792. #while ( (my @call_details = (caller($i++))) ){
  793. # Log 1,$call_details[1].":".$call_details[2]." in function ".$call_details[3];
  794. #}
  795. #return;
  796. #}
  797. #-- expected length
  798. my $numget;
  799. my $numexp = $query->{numread};
  800. $numexp = 0
  801. if( (!$numexp) || ($numexp eq "") );
  802. #-- actual read operation
  803. $buffer = $owx->Read($numexp);
  804. #-- actual length
  805. $numget = length($buffer)-10;
  806. #-- partial reception
  807. if( $numget < $numexp ){
  808. #-- empty device buffer
  809. if( length($hash->{PARTIAL}) == 0 ){
  810. $hash->{PARTIAL} = $buffer;
  811. return undef;
  812. #-- some data present already
  813. }else{
  814. $buffer = $hash->{PARTIAL}.$buffer;
  815. $numget = length($buffer)-10;
  816. #-- NOT done with this return to main loop
  817. if( $numget<$numexp ){
  818. $hash->{PARTIAL} = $buffer;
  819. return undef;
  820. }
  821. }
  822. }
  823. #-- IF WE ARE HERE, WE SHOULD HAVE ALL THE BYTES
  824. $hash->{PARTIAL} = "";
  825. $queue->[0]->{status} = "completed";
  826. #Log3 $name, 1, "OWX_Read: queue $name context ".$queue->[0]->{context}." received $numget bytes, expected $numexp. status completed, processing entry";
  827. #-- if necessary perform a reset after the full read operation
  828. if( ($queue->[0]->{proc} & 1)==1 ){
  829. #select(undef,undef,undef,0.01);
  830. $owx->Reset()
  831. };
  832. #-- slave data
  833. my $slave = $query->{hash};
  834. my $context = $query->{context};
  835. my $proc = (defined($query->{proc}))?($query->{proc}):1;
  836. my $owx_dev = $query->{owx_dev};
  837. my $crcpart = $query->{crcpart}; #-- needed for CRC check
  838. #-- successful completion, take off the queue
  839. shift(@{$queue});
  840. $qlen = @{$queue};
  841. #Log3 $name, 4, "OWX_Read: $name queue contains $qlen entries after removal of active entry";
  842. #-- calling callback
  843. if( $query->{callback} ){
  844. #Log3 $name, 1, "OWX_Read: $name received $numget bytes, expected $numexp. Now calling callback for context $context";
  845. $ret = $query->{callback}($slave,$context, $proc, $owx_dev, $crcpart, $numexp, substr($buffer,$staexp,$numexp));
  846. }else{
  847. #Log3 $name, 1, "OWX_Read: $name received $numget bytes, expected $numexp. No callback defined for context $context";
  848. }
  849. #-- we are done - but maybe still have to do a synchronous job
  850. if( $hash->{BLOCK} ){
  851. my $ret2 = $hash->{BLOCKCALL}->();
  852. };
  853. #-- reset busy flag
  854. $hash->{BUSY} = 0;
  855. #-- more items in queue -> schedule next process
  856. #if( $name eq "OWX_TEST"){
  857. # Log3 $name, 1,"----------------------------------------------";
  858. # Log3 $name,1, " queue $name contains ".scalar(@{$hash->{QUEUE}})." entries after read";
  859. # foreach my $diapoint (@{$hash->{QUEUE}}) {
  860. # Log3 $name, 1, " => ".$diapoint->{owx_dev}." context ".$diapoint->{context}." expecting ".$diapoint->{numread}." bytes, ".$diapoint->{status};
  861. # }
  862. # Log3 $name, 1,"----------------------------------------------";
  863. #}
  864. my $now = gettimeofday();
  865. if( ($qlen > 0) ) {
  866. InternalTimer($now+0.01, "OWX_PrQueue", "queue:$name", 0);
  867. }
  868. return $ret;
  869. }
  870. #######################################################################################
  871. #
  872. # OWX_Ready
  873. #
  874. # Parameter hash = hash of bus master
  875. #
  876. # Return 1 : OK
  877. # 0 : not OK
  878. #
  879. ########################################################################################
  880. sub OWX_Ready($) {
  881. my ($hash)=@_;
  882. #-- relevant for Windows/USB only
  883. if ( $hash->{STATE} ne "disconnected" ){
  884. if(defined($hash->{USBDev})) {
  885. my $po = $hash->{USBDev};
  886. my ( $BlockingFlags, $InBytes, $OutBytes, $ErrorFlags ) = $po->status;
  887. return ( $InBytes > 0 );
  888. }
  889. return undef;
  890. }
  891. #-- get the interface
  892. my $owx = $hash->{OWX};
  893. my $name = $hash->{NAME};
  894. my $time = time();
  895. #-- skip this if delay time is not yet over
  896. if($hash->{NEXT_OPEN} && $time < $hash->{NEXT_OPEN}) {
  897. #Log3 $hash, 5, "NEXT_OPEN prevented opening $name";
  898. }else{
  899. #-- call the specific interface if delay time is over
  900. my $success=$owx->Ready();
  901. if($success) {
  902. delete($hash->{NEXT_OPEN});
  903. #-- re-init the bus
  904. Log3 $name,1,"OWX_Ready calling low-level init of bus";
  905. my $ret = OWX_Init($hash);
  906. }else{
  907. $hash->{NEXT_OPEN} = $time+AttrVal($name, "opendelay", 60);
  908. }
  909. }
  910. return undef;
  911. }
  912. ########################################################################################
  913. #
  914. # OWX_Set - Implements SetFn function
  915. #
  916. # Parameter hash , a = argument array
  917. #
  918. ########################################################################################
  919. sub OWX_Set($@) {
  920. my ($hash, @a) = @_;
  921. my $name = shift @a;
  922. my $res = 0;
  923. #-- for the selector: all values are possible for expert, otherwise only reopen
  924. return ( (AttrVal($name,"expert","") eq "1_detail") ? join(":noArg ", sort keys %sets).":noArg" : "reopen:noArg")
  925. if(!defined($sets{$a[0]}));
  926. return "OWX_Set: With unknown argument $a[0], choose one of " .
  927. ( (AttrVal($name,"expert","") eq "1_detail") ? join(" ", sort keys %sets) : "reopen")
  928. if(!defined($sets{$a[0]}));
  929. my $owx = $hash->{OWX};
  930. #-- Set open
  931. if( $a[0] eq "open" ){
  932. $owx->Open();
  933. $res = 0;
  934. }
  935. #-- Set close
  936. if( $a[0] eq "close" ){
  937. $owx->Close();
  938. $res = 0;
  939. }
  940. #-- Set reopen
  941. if( $a[0] eq "reopen" ){
  942. $owx->Reopen();
  943. $res = 0;
  944. }
  945. #-- Set discover
  946. if( $a[0] eq "discover" ){
  947. OWX_Discover($hash);
  948. $res = 0;
  949. }
  950. #-- Set detect
  951. if( $a[0] eq "detect" ){
  952. my $owx = $hash->{OWX};
  953. $owx->Detect();
  954. $res = 0;
  955. }
  956. if( $a[0] eq "process") {
  957. my $res = OWX_PrQueue("queue:$name");
  958. #-- process result
  959. return $res
  960. }
  961. Log3 $name, 3, "OWX_Set $name ".join(" ",@a)." => $res";
  962. }
  963. ########################################################################################
  964. #
  965. # OWX_Undef - Implements UndefFn function
  966. #
  967. # Parameter hash = hash of the bus master, name
  968. #
  969. ########################################################################################
  970. sub OWX_Undef ($$) {
  971. my ($hash, $name) = @_;
  972. RemoveInternalTimer($hash);
  973. RemoveInternalTimer ("queue:$name");
  974. # TODO - THIS IS WRONG. DO NOT DELETE THEM, BUT INVALIDATE THEM
  975. #-- invalidate clients
  976. #foreach my $d (sort keys %defs) {
  977. # if(defined($defs{$d}) &&
  978. # defined($defs{$d}{IODev}) &&
  979. # $defs{$d}{IODev} == $hash) {
  980. # Log3 $hash, 3, "deleting port for $d";
  981. # delete $defs{$d}{IODev};
  982. # }
  983. #}
  984. DevIo_CloseDev($hash);
  985. return undef;
  986. }
  987. ########################################################################################
  988. #
  989. # OWX_Verify - Verify a particular device on the 1-Wire bus
  990. #
  991. # Parameter hash = hash of bus master, dev = 8 Byte ROM ID of device to be tested
  992. #
  993. # Return 1 : device found
  994. # 0 : device not found
  995. #
  996. # Unterbinden
  997. #
  998. ########################################################################################
  999. sub OWX_Verify ($$$$) {
  1000. my ($hashorname,$devname,$devid,$type) = @_;
  1001. my $hash;
  1002. my $i;
  1003. if( $type == 1){
  1004. $hash = $defs{$hashorname};
  1005. }else{
  1006. $hash = $hashorname;
  1007. }
  1008. #-- get the interface
  1009. my $name = $hash->{NAME};
  1010. my $owx = $hash->{OWX};
  1011. my $state = $hash->{STATE};
  1012. #-- Do nothing, if devices not yet discovered
  1013. return undef
  1014. if( !$hash->{INITDONE} );
  1015. my $now = gettimeofday();
  1016. my $qlen = ($hash->{QUEUE} ? scalar(@{$hash->{QUEUE}}) : 0);
  1017. #-- if called synchronously, or queue is blocked, or queue empty, do it now
  1018. if( !($hash->{ASYNCHRONOUS}) ||
  1019. ( $qlen == 0) ||
  1020. ( $hash->{ASYNCHRONOUS} && $hash->{BLOCK} ) ){
  1021. if( $state ne "opened" ) {
  1022. Log3 $name, 3,"OWX_Verify called while interface $name not opened";
  1023. return undef;
  1024. }
  1025. Log3 $name, 5,"OWX_Verify called for interface $name";
  1026. $hash->{BLOCK} = 1;
  1027. $hash->{BUSY} = 1;
  1028. my $ret = $owx->Verify($devid);
  1029. #-- remove queue block and restart queue
  1030. $hash->{BLOCK} = 0;
  1031. $hash->{BUSY} = 0;
  1032. my $slave = $defs{$devname};
  1033. #-- generate an event only if presence has changed
  1034. if( $ret == 0 ){
  1035. readingsSingleUpdate($slave,"present",0,$slave->{PRESENT});
  1036. } else {
  1037. readingsSingleUpdate($slave,"present",1,!$slave->{PRESENT});
  1038. }
  1039. $slave->{PRESENT} = $ret;
  1040. OWX_Ready($hash);
  1041. return $ret;
  1042. #-- if called asynchronously, and queue is not blocked, issue queue block and delay.
  1043. }elsif( $hash->{ASYNCHRONOUS} && !($hash->{BLOCK}) ){
  1044. #-- issue queue block
  1045. $hash->{BLOCK} = 1;
  1046. Log3 $name, 5,"OWX_Verify issued a queue block on interface $name";
  1047. $hash->{BLOCKCALL}=sub(){ OWX_Verify($name,$devname,$devid,1); };
  1048. return undef;
  1049. }
  1050. }
  1051. #######################################################################################
  1052. #
  1053. # OWX_Write - Write to the bus
  1054. #
  1055. # Parameter hash = hash of bus master,
  1056. #
  1057. ########################################################################################
  1058. sub OWX_Write($){
  1059. my $hash = shift;
  1060. my $queue = $hash->{QUEUE};
  1061. my $data = $queue->[0]{data};
  1062. my $proc = $queue->[0]{proc};
  1063. my $name = $hash->{NAME};
  1064. my $owx = $hash->{OWX};
  1065. my $buffer = "";
  1066. #-- catch late calls
  1067. return
  1068. if( !$queue );
  1069. #-- perform a send from queue
  1070. if($hash->{STATE} ne "disconnected"){
  1071. #-- empty the receive buffer
  1072. $hash->{PARTIAL} = "";
  1073. $queue->[0]{status} = "active";
  1074. my $reset = 1-(($proc & 2) >> 1);
  1075. $owx->Write($data, $reset);
  1076. }
  1077. #-- in this case we will never get a read operation
  1078. if( $queue->[0]{numread} == -10){
  1079. $hash->{PARTIAL} = "";
  1080. $queue->[0]->{status} = "completed";
  1081. #Log3 $name, 1, "OWX_Write: queue $name gets status completed because donotwait is set";
  1082. #-- if necessary perform a reset after this operation
  1083. if( ($queue->[0]->{proc} & 1)==1 ){
  1084. select(undef,undef,undef,0.01);
  1085. $owx->Reset()
  1086. };
  1087. #-- reset busy flag
  1088. shift(@{$queue});
  1089. $hash->{BUSY} = 0;
  1090. }
  1091. return
  1092. }
  1093. ########################################################################################
  1094. #
  1095. # Complex R/W operations and queue handling
  1096. #
  1097. ########################################################################################
  1098. #
  1099. # OWX_Complex - Synchronously send match ROM, data block and receive bytes as response
  1100. #
  1101. # Parameter hash = hash of bus master,
  1102. # owx_dev = ROM ID of device
  1103. # data = string to send
  1104. # numread = number of bytes to receive
  1105. #
  1106. # Return response, if OK
  1107. # 0 if not OK
  1108. #
  1109. ########################################################################################
  1110. sub OWX_Complex ($$$$) {
  1111. my ($hash,$owx_dev,$data,$numread) =@_;
  1112. #-- get the interface
  1113. my $name = $hash->{NAME};
  1114. my $owx = $hash->{OWX};
  1115. my $state = $hash->{STATE};
  1116. if( $state eq "disconnected" ) {
  1117. Log3 $name, 1,"OWX_Complex called while interface $name disconnected";
  1118. return 0;
  1119. }
  1120. my $ret = $owx->Complex($owx_dev,$data,$numread);
  1121. return (!defined($ret))?0:$ret;
  1122. }
  1123. #######################################################################################
  1124. #
  1125. # OWX_Qomplex - Put into queue:
  1126. # asynchronously send match ROM, data block and receive bytes as response
  1127. # but queued
  1128. #
  1129. # Parameter hash = hash of bus master,
  1130. # slave = hash of slave device
  1131. # context = mode for evaluating the binary data
  1132. # proc = processing instruction, also passed to OWX_Read.
  1133. # bitwise interpretation !!
  1134. # if 0, nothing special
  1135. # if 1 = bit 0, a reset will be performed also after
  1136. # the last operation in OWX_Read
  1137. # if 2 = bit 1, the initial reset of the bus will be suppressed
  1138. # if 4 = do not wait for a response for more than ???
  1139. # if 8 = bit 3, the fillup of the data with 0xff will be suppressed
  1140. # if 16= bit 4, the insertion will be at the top of the queue
  1141. # owx_dev = ROM ID of slave device
  1142. # data = string to be sent
  1143. # crcpart = part of the data that needs to be part of the CRC check
  1144. # numread = number of bytes to receive
  1145. # startread = start index to begin reading in the return value
  1146. # callback = address of callback function
  1147. # mindelay = minimum time until next call to this device
  1148. #
  1149. # Return response, if NOK (died)
  1150. # undef if OK
  1151. #
  1152. ########################################################################################
  1153. sub OWX_Qomplex(@){
  1154. my ( $hash, $slave, $context, $proc, $owx_dev, $data, $crcpart, $numread, $startread, $callback, $mindelay) = @_;
  1155. #### master slave context proc owx_dev data crcpart numread startread callback delay
  1156. my $name = $hash->{NAME};
  1157. my $data2;
  1158. my $res = "";
  1159. my $res2 = "";
  1160. if( !$defs{$name}) {
  1161. return undef;
  1162. }
  1163. #-- get the interface
  1164. my $interface = $hash->{INTERFACE};
  1165. my $owx = $hash->{OWX};
  1166. my $devlevel = (defined($attr{$name}{verbose})) ? $attr{$name}{verbose} : 0;
  1167. my $qdebug = ( ($devlevel > 3) || ($attr{global}{verbose}>3) ) ? 1 : 0;
  1168. #-- has match ROM part, prepend the rom id
  1169. if( $owx_dev ){
  1170. #-- ID of the device
  1171. my $owx_rnf = substr($owx_dev,3,12);
  1172. my $owx_f = substr($owx_dev,0,2);
  1173. #-- 8 byte 1-Wire device address
  1174. my @rom_id =(0,0,0,0 ,0,0,0,0);
  1175. #-- from search string to byte id
  1176. $owx_dev=~s/\.//g;
  1177. for(my $i=0;$i<8;$i++){
  1178. $rom_id[$i]=hex(substr($owx_dev,2*$i,2));
  1179. }
  1180. $data2=sprintf("\x55%c%c%c%c%c%c%c%c",@rom_id).$data;
  1181. #-- has no match ROM part, insert a "skip ROM" command
  1182. } else {
  1183. $data2="\xCC".$data;
  1184. }
  1185. #-- has receive data part, suppress fillup ?
  1186. if( (($proc & 8)>>3) != 1) {
  1187. if( $numread >0 ){
  1188. #$numread += length($data);
  1189. for( my $i=0;$i<$numread;$i++){
  1190. $data2 .= "\xFF";
  1191. }
  1192. }
  1193. }
  1194. my $now = gettimeofday();
  1195. my %dialog;
  1196. $dialog{hash} = $slave;
  1197. $dialog{context} = $context;
  1198. $dialog{proc} = $proc;
  1199. $dialog{owx_dev} = $owx_dev;
  1200. $dialog{data} = $data2;
  1201. $dialog{crcpart} = $crcpart;
  1202. $dialog{numread} = $numread;
  1203. $dialog{startread} = $startread;
  1204. $dialog{callback} = $callback;
  1205. $dialog{delay} = $mindelay;
  1206. $dialog{start} = $now;
  1207. $dialog{status} = "waiting";
  1208. if( !$context ){
  1209. Log3 $name,1,"OWX_Qomplex: context missing in queue $name entry for device $owx_dev";
  1210. $dialog{context} = ""
  1211. }
  1212. my $qlen = ($hash->{QUEUE} ? scalar(@{$hash->{QUEUE}}) : 0);
  1213. readingsSingleUpdate($hash,"queue",$qlen,0);
  1214. #-- single item => new queue, and start processing immediately
  1215. if(!$qlen) {
  1216. $hash->{QUEUE} = [ \%dialog ];
  1217. Log3 $name, 4, "OWX_Qomplex: Added dev $owx_dev to queue $name context=$context";
  1218. OWX_PrQueue("direct:".$name);
  1219. #-- List queue for debugging
  1220. if( $qdebug ){
  1221. Log3 $name,1, " queue $name contains ".scalar(@{$hash->{QUEUE}})." entries after insertion";
  1222. foreach my $diapoint (@{$hash->{QUEUE}}) {
  1223. Log3 $name, 1, " => ".$diapoint->{owx_dev}." context ".$diapoint->{context}." expecting ".$diapoint->{numread}." bytes, ".$diapoint->{status};
  1224. }
  1225. Log3 $name, 1,"----------------------------------------------";
  1226. return;
  1227. }
  1228. #-- priority insert at top of queue, and no immediate processing
  1229. } elsif( (($proc & 16)>>4)==1 ) {
  1230. #if ( $hash->{BUSY}==1 ) {
  1231. # my $dialogtop = shift(@{$hash->{QUEUE}});
  1232. # unshift( @{$hash->{QUEUE}}, \%dialog );
  1233. # unshift( @{$hash->{QUEUE}}, $dialogtop);
  1234. # Log 1,"------------> Prio insert as second task";
  1235. # Log 1, " queue $name contains ".scalar(@{$hash->{QUEUE}})." entries after insertion";
  1236. # foreach my $diapoint (@{$hash->{QUEUE}}) {
  1237. # Log 1, " => ".$diapoint->{owx_dev}." context ".$diapoint->{context}." expecting ".$diapoint->{numread}." bytes, ".$diapoint->{status};
  1238. # }
  1239. # Log 1,"----------------------------------------------";
  1240. #}else{
  1241. unshift( @{$hash->{QUEUE}}, \%dialog );
  1242. #Log 1,"------------> Prio insert as first task";
  1243. #Log 1, " queue $name contains ".scalar(@{$hash->{QUEUE}})." entries after insertion";
  1244. #foreach my $diapoint (@{$hash->{QUEUE}}) {
  1245. # Log 1, " => ".$diapoint->{owx_dev}." context ".$diapoint->{context}." expecting ".$diapoint->{numread}." bytes, ".$diapoint->{status};
  1246. #}
  1247. #Log 1,"----------------------------------------------";
  1248. #}
  1249. return;
  1250. #-- insert into existing queue
  1251. } else {
  1252. if ($qlen > AttrVal($name, "queueMax", 100)) {
  1253. Log 1,"OWX_Qomplex: $name queue too long, dropping data"
  1254. if( $qdebug );
  1255. } else {
  1256. push(@{$hash->{QUEUE}}, \%dialog);
  1257. Log 1, "OWX_Qomplex: Added dev $owx_dev to queue $name numread=$numread"
  1258. if( $qdebug );
  1259. }
  1260. }
  1261. #-- List queue for debugging
  1262. if( $qdebug ){
  1263. Log 1, " queue $name contains ".scalar(@{$hash->{QUEUE}})." entries after insertion";
  1264. foreach my $diapoint (@{$hash->{QUEUE}}) {
  1265. Log 1, " => ".$diapoint->{owx_dev}." context ".$diapoint->{context}." expecting ".$diapoint->{numread}." bytes, ".$diapoint->{status};
  1266. }
  1267. Log 1,"----------------------------------------------";
  1268. }
  1269. }
  1270. #######################################################################################
  1271. #
  1272. # OWX_PrQueue - ProcessQueue.
  1273. # Called by internal timer as"queue:$name"
  1274. # or directly with $direct:$name
  1275. #
  1276. # Flags: BUSY indicates an asynchronous r/w operation
  1277. # BLOCKED stops the asynchronous operations for synchronous r/w
  1278. #
  1279. #
  1280. # Return response, if OK
  1281. # 0 if not OK
  1282. #
  1283. ########################################################################################
  1284. sub OWX_PrQueue($){
  1285. my (undef,$name) = split(':', $_[0]);
  1286. my $hash = $defs{$name};
  1287. my $queue = $hash->{QUEUE};
  1288. my $slave;
  1289. #-- 1 seconds queue delay as default, 2 seconds for timeout
  1290. my $longDelay = 1.0;
  1291. my $shortDelay = 0.05;
  1292. my $maxrunning = ( defined($attr{$name}{timeout})) ? $attr{$name}{timeout} : 2;
  1293. my $devlevel = (defined($attr{$name}{verbose})) ? $attr{$name}{verbose} : 0;
  1294. my $qdebug = ( ($devlevel > 3) || ($attr{global}{verbose}>3) ) ? 1 : 0;
  1295. RemoveInternalTimer ("queue:$name");
  1296. my $now = gettimeofday();
  1297. #-- fhem not initialized, wait with IO || !$hash->{INITDONE}
  1298. if ( !$main::init_done ) {
  1299. Log3 $name,1, "OWX_PrQueue: init not done, delay sending from queue $name";
  1300. InternalTimer($now+$longDelay, "OWX_PrQueue", "queue:$name", 0);
  1301. return;
  1302. }
  1303. #-- Done with the queue
  1304. return
  1305. if( !$queue);
  1306. my $qlen = @{$queue};
  1307. return
  1308. if( $qlen == 0);
  1309. #-- remove top entry if garbage
  1310. if( !defined($queue->[0]{data}) || ($queue->[0]{data} eq "" || $queue->[0]->{start} eq "" )) {
  1311. my $reason = (!defined($queue->[0]{data})) ? "garbage" : (($queue->[0]{data} eq "") ? "no data" :
  1312. (($queue->[0]->{start} eq "") ? "no start" : "unknown"));
  1313. Log3 $name,1, "OWX_PrQueue: removing garbage from queue $name, reason $reason ";
  1314. shift(@{$queue});
  1315. InternalTimer($now+$shortDelay, "OWX_PrQueue", "queue:$name", 0);
  1316. return;
  1317. }
  1318. #-- still waiting for reply to last send
  1319. if ( $hash->{BUSY} && $hash->{BUSY}==1 ) {
  1320. my $running = int(100*($now-$queue->[0]->{start}))/100;
  1321. #-- donotwait flag is set
  1322. if( (($queue->[0]->{proc} & 4)>>2)==1 ){
  1323. #Log 1, "OWX_PrQueue: removing ".$queue->[0]->{owx_dev}." w. context ".$queue->[0]->{context}." from queue $name, because donotwait is set"
  1324. # if( $name eq "OWX_TEST");
  1325. $hash->{BUSY}=0;
  1326. shift(@{$queue});
  1327. InternalTimer($now+$shortDelay, "OWX_PrQueue", "queue:$name", 0);
  1328. return;
  1329. #-- timeout, so kill this entry
  1330. }elsif( $running > $maxrunning ){
  1331. #Log 1, "OWX_PrQueue: removing ".$queue->[0]->{owx_dev}." w. context ".$queue->[0]->{context}." from queue $name. Timeout $running > $maxrunning seconds."
  1332. # if( $name eq "OWX_TEST");
  1333. $hash->{BUSY}=0;
  1334. shift(@{$queue});
  1335. InternalTimer($now+$shortDelay, "OWX_PrQueue", "queue:$name", 0);
  1336. return;
  1337. #-- no timeout, but we have to wait for a reply
  1338. }else{
  1339. my $rout='';
  1340. #my $hash2 = $selectlist{'OWX_WIFI.192.168.0.97:23'};
  1341. #my $fd = ( defined($hash2->{FD}) ) ? $hash2->{FD} : "NO";
  1342. #my $vc = ( defined($hash2->{FD}) ) ? vec($rout, $hash2->{FD}, 1) : "NO";
  1343. #Log 1, "OWX_PrQueue: still waiting for reply, delay sending from queue $name"
  1344. # if( $name eq "OWX_TEST");
  1345. #Log 1, "OWX_PrQueue: still waiting for reply, delay sending from queue $name.";
  1346. # Log 1, " => ".$queue->[0]->{owx_dev}." context ".$queue->[0]->{context}." expecting ".$queue->[0]->{numread}." bytes, ".$queue->[0]->{status};
  1347. InternalTimer($now+$longDelay, "OWX_PrQueue", "queue:$name", 0);
  1348. return;
  1349. }
  1350. }
  1351. #-- if something to send, check if min delay for slave is over
  1352. if( $queue->[0]{data} ) {
  1353. $slave = $queue->[0]{hash}; # hash of 1-Wire device
  1354. #-- no, it is not
  1355. if ( defined($slave->{NEXTSEND})){
  1356. if( $now < $slave->{NEXTSEND} ){
  1357. #-- reschedule, if necessary
  1358. if( ($qlen==1) || (($qlen > 1) && ($queue->[1]{$hash} eq $slave)) ){
  1359. #Log 1, "OWX_PrQueue: device ".$slave->{NAME}." mindelay not over, rescheduling."
  1360. InternalTimer($now+$shortDelay, "OWX_PrQueue", "queue:$name", 0);
  1361. return;
  1362. #-- switch top positions of queue if possible
  1363. }else{
  1364. #Log 1, "OWX_PrQueue: exchanging top positions of queue";
  1365. my $dialogtop = shift(@{$queue});
  1366. my $dialogsecond = shift(@{$queue});
  1367. unshift( @{$queue}, $dialogtop);
  1368. unshift( @{$queue}, $dialogsecond);
  1369. }
  1370. }
  1371. }
  1372. }
  1373. #-- something to send, check min delay of 10 ms
  1374. if( $queue->[0]{data} ) {
  1375. $slave = $queue->[0]{hash}; # may be a different one than above
  1376. #-- no, it is not, reschedule
  1377. if ($hash->{LASTSEND} && $now < $hash->{LASTSEND} + 0.01) {
  1378. #-- List queue for debugging
  1379. #Log 1, "OWX_PrQueue: queue $name mindelay not over, rescheduling.";
  1380. # Log 1, " => ".$queue->[0]->{owx_dev}." context ".$queue->[0]->{context}." expecting ".$queue->[0]->{numread}." bytes, ".$queue->[0]->{status};
  1381. InternalTimer($now+$shortDelay, "OWX_PrQueue", "queue:$name", 0);
  1382. return;
  1383. }
  1384. #-- start write operation
  1385. $hash->{BUSY} = 1; # OWX queue is busy until response is received
  1386. $hash->{LASTSEND} = $now; # remember when last sent on this queue
  1387. #-- set delay for this device if necessary
  1388. if( defined($queue->[0]{delay}) ){
  1389. $slave->{NEXTSEND} = $now + $queue->[0]{delay};
  1390. }else{
  1391. delete $slave->{NEXTSEND};
  1392. }
  1393. # Log 1,"OWX_PrQueue: starting send-receive cycle, queue length $qlen. Setting entry to active";
  1394. # Log 1, " => ".$queue->[0]->{owx_dev}." context ".$queue->[0]->{context}." expecting ".$queue->[0]->{numread}." bytes, ".$queue->[0]->{status};
  1395. # Log 1,"----------------------------------------------";
  1396. #-- REALLY do it
  1397. #Log 1, "OWX_PrQueue: queue $name starting OWX_Write with context ".$queue->[0]->{context}
  1398. # if( $name eq "OWX_TEST");
  1399. $queue->[0]->{start} = $now;
  1400. OWX_Write($hash);
  1401. }
  1402. #-- schedule next processing
  1403. InternalTimer($now+$longDelay, "OWX_PrQueue", "queue:$name", 0);
  1404. return;
  1405. }
  1406. #######################################################################################
  1407. #
  1408. # OWX_WDBG - Write a debug message unconditionally
  1409. #
  1410. # Parameter $name= device name
  1411. # $msg = string message
  1412. # $bin = binary message
  1413. #
  1414. ########################################################################################
  1415. sub OWX_WDBG($$$) {
  1416. my ($name,$msg,$bin) = @_;
  1417. my ($i,$j,$k);
  1418. if( $bin ){
  1419. for($i=0;$i<length($bin);$i++){
  1420. $j=int(ord(substr($bin,$i,1))/16);
  1421. $k=ord(substr($bin,$i,1))%16;
  1422. $msg.=sprintf "0x%1x%1x ",$j,$k;
  1423. }
  1424. }
  1425. main::Log3($name, 1, $msg);
  1426. }
  1427. #######################################################################################
  1428. #
  1429. # OWX_WDBG - Write a debug message according to verbosity level
  1430. #
  1431. # Parameter $name= device name
  1432. # $lvl = verbosity level
  1433. # $msg = string message
  1434. # $bin = binary message
  1435. #
  1436. ########################################################################################
  1437. sub OWX_WDBGL($$$$) {
  1438. my ($name,$lvl,$msg,$bin) = @_;
  1439. if(defined($name) &&
  1440. defined($attr{$name}) &&
  1441. defined (my $devlevel = $attr{$name}{verbose})) {
  1442. return if($lvl > $devlevel);
  1443. } else {
  1444. return if($lvl > $attr{global}{verbose});
  1445. }
  1446. my ($i,$j,$k);
  1447. if( $bin ){
  1448. for($i=0;$i<length($bin);$i++){
  1449. $j=int(ord(substr($bin,$i,1))/16);
  1450. $k=ord(substr($bin,$i,1))%16;
  1451. $msg.=sprintf "0x%1x%1x ",$j,$k;
  1452. }
  1453. }
  1454. main::Log3($name, 1, $msg);
  1455. }
  1456. 1;
  1457. =pod
  1458. =item device
  1459. =item summary to commmunicate with 1-Wire bus devices
  1460. =item summary_DE zur Kommunikation mit 1-Wire Geräten
  1461. =begin html
  1462. <a name="OWX"></a>
  1463. <h3>OWX</h3>
  1464. <ul>
  1465. <p> Backend module to commmunicate with 1-Wire bus devices</p>
  1466. <ul>
  1467. <li>via an active DS2480/DS9097U bus master interface attached to an USB
  1468. port or </li>
  1469. <li>via an active DS2480 bus master interface attached to a TCP/IP-UART interface </li>
  1470. <li>via a network-attached CUNO or through a COC on the RaspBerry Pi</li>
  1471. <li>via an Arduino running OneWireFirmata</li>
  1472. </ul> Internally these interfaces are vastly different, read the corresponding <a
  1473. href="http://fhemwiki.de/wiki/Interfaces_f%C3%BCr_1-Wire"> Wiki pages </a>.
  1474. The passive DS9097 interface is no longer suppoorted.
  1475. <a name="OWXdefine"></a>
  1476. <h4>Define</h4>
  1477. <p>To define a 1-Wire interface to communicate with a 1-Wire bus, several possibilities exist:
  1478. <ul>
  1479. <li><code>define &lt;name&gt; OWX &lt;serial-device&gt;</code>, i.e. specify the serial device (e.g. USB port) to which the
  1480. 1-Wire bus is attached, for example<br/><code>define OWio1 OWX /dev/ttyUSB1</code></li>
  1481. <li><code>define &lt;name&gt; OWX &lt;tcpip&gt;[:&lt;port&gt;]</code>, i.e. specify the IP address and port to which the 1-Wire bus is attached. Attention: no socat program needed.
  1482. Example:<br/><code>define OWio1 OWX 192.168.0.1:23</code></li>
  1483. <li><code>define &lt;name&gt; OWX &lt;cuno/coc-device&gt;</code>, i.e. specify the previously defined COC/CUNO to which the 1-Wire bus
  1484. is attached, for example<br/><code>define OWio2 OWX COC</code></li>
  1485. <li><code>define &lt;name&gt; OWX &lt;firmata-device&gt;:&lt;firmata-pin&gt;</code>, i.e. specify the name and 1-Wire pin of the previously defined <a href="#FRM">FRM</a>
  1486. device to which the 1-Wire bus is attached, for example<br/><code>define OWio3 OWX FIRMATA:10</code></li>
  1487. </ul>
  1488. <a name="OWXset"></a>
  1489. <h4>Set</h4>
  1490. <ul>
  1491. <li><a name="owx_reopen">
  1492. <code>set &lt;name&gt; reopen</code>
  1493. </a>
  1494. <br />re-opens the interface and re-initializes the 1-Wire bus.
  1495. </li>
  1496. </ul>
  1497. <br />
  1498. <a name="OWXget"></a>
  1499. <h4>Get</h4>
  1500. <ul>
  1501. <li><a name="owx_alarms"></a>
  1502. <code>get &lt;name&gt; alarms</code>
  1503. <br />performs an "alarm search" for devices on the 1-Wire bus and, if found,
  1504. generates an event in the log (not with all interface types). </li>
  1505. <li><a name="owx_devices"></a>
  1506. <code>get &lt;name&gt; devices</code>
  1507. <br />redicovers all devices on the 1-Wire bus. If a device found has a
  1508. previous definition, this is automatically used. If a device is found but has no
  1509. definition, it is autocreated. If a defined device is not on the 1-Wire bus, it is
  1510. autodeleted. </li>
  1511. <li><a name="owx_version"></a>
  1512. <code>get &lt;name&gt; version</code>
  1513. <br />internal version number</li>
  1514. </ul>
  1515. <br />
  1516. <a name="OWXattr"></a>
  1517. <h4>Attributes</h4>
  1518. <ul>
  1519. <li><a name="OWXasynchronous"><code>attr &lt;name&gt; asynchronous 0(default)|1</code></a>
  1520. <br />if 1 the interface will run asynchronously;if 0 (default) then not</li>
  1521. <li><a name="OWXtimeout"><code>attr &lt;name&gt; timeout &lt;number&gt;</code></a>
  1522. <br />time in seconds waiting for response of any 1-Wire device, or 1-Wire interface,default 5 s</li>
  1523. <li><a name="OWXopendelay"><code>attr &lt;name&gt; opendelay &lt;number&gt; </code></a>
  1524. <br />time in seconds waiting until a reopen ist attempted, default 60 s</li>
  1525. <li><a name="OWXdokick"><code>attr &lt;name&gt; dokick 0|1</code></a>
  1526. <br />if 1, the interface regularly kicks thermometers on the bus to do a temperature conversion,
  1527. and to make an alarm check; if 0 (default) then not</li>
  1528. <li><a name="OWXinterval"><code>attr &lt;name&gt; interval &lt;number&gt;</code></a>
  1529. <br />time interval in seconds for kicking temperature sensors and checking for alarms, default 300 s</li>
  1530. <li><a href="#readingFnAttributes">readingFnAttributes</a></li>
  1531. </ul>
  1532. </ul>
  1533. =end html
  1534. =begin html_DE
  1535. <a name="OWX"></a>
  1536. <h3>OWX</h3>
  1537. <ul>
  1538. <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>
  1539. </ul>
  1540. =end html_DE
  1541. =cut