00_OWX.pm 59 KB

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