21_OWTHERM.pm 47 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409
  1. ########################################################################################
  2. #
  3. # OWTHERM.pm
  4. #
  5. # FHEM module to commmunicate with 1-Wire temperature sensors DS1820, DS18S20, DS18B20, DS1822
  6. #
  7. # Prof. Dr. Peter A. Henning
  8. # Norbert Truchsess
  9. #
  10. # $Id: 21_OWTHERM.pm 11130 2016-03-27 09:14:38Z pahenning $
  11. #
  12. ########################################################################################
  13. #
  14. # define <name> OWTHERM [<model>] <ROM_ID> [interval] or <FAM_ID>.<ROM_ID> [interval]
  15. #
  16. # where <name> may be replaced by any name string
  17. #
  18. # <model> is a 1-Wire device type. If omitted AND no FAM_ID given we assume this to be an
  19. # DS1820 temperature sensor. Currently allowed values are DS1820, DS18B20, DS1822
  20. # <FAM_ID> is a 1-Wire family id, currently allowed values are 10, 22, 28
  21. # <ROM_ID> is a 12 character (6 byte) 1-Wire ROM ID
  22. # without Family ID, e.g. A2D90D000800
  23. # [interval] is an optional query interval in seconds
  24. #
  25. # get <name> id => FAM_ID.ROM_ID.CRC
  26. # get <name> present => 1 if device present, 0 if not
  27. # get <name> interval => query interval
  28. # get <name> temperature => temperature measurement
  29. # get <name> alarm => alarm temperature settings
  30. # get <name> version => OWX version number
  31. #
  32. # set <name> interval => period for measurement
  33. # set <name> tempLow => lower alarm temperature setting
  34. # set <name> tempHigh => higher alarm temperature setting
  35. #
  36. # Additional attributes are defined in fhem.cfg
  37. #
  38. # attr <name> stateAL "<string>" = character string for denoting low alarm condition, default is ↓
  39. # attr <name> stateAH "<string>" = character string for denoting high alarm condition, default is ↑
  40. # attr <name> tempOffset <float> = temperature offset in degree Celsius added to the raw temperature reading
  41. # attr <name> tempUnit <string> = unit of measurement, e.g. Celsius/Kelvin/Fahrenheit, default is Celsius
  42. # attr <name> tempConv onkick|onread = determines, whether a temperature measurement will happen when "kicked"
  43. # through the OWX backend module (all temperature sensors at the same time), or on
  44. # reading the sensor (1 second waiting time).
  45. # attr <name> resolution 9|10|11|12 = resolution in bit for measurement
  46. # attr <name> interval <floar> = period for measurement
  47. # attr <name> tempLow <float> = lower alarm temperature setting
  48. # attr <name> tempHigh <float> = higher alarm temperature setting
  49. #
  50. ########################################################################################
  51. #
  52. # This programm is free software; you can redistribute it and/or modify
  53. # it under the terms of the GNU General Public License as published by
  54. # the Free Software Foundation; either version 2 of the License, or
  55. # (at your option) any later version.
  56. #
  57. # The GNU General Public License can be found at
  58. # http://www.gnu.org/copyleft/gpl.html.
  59. # A copy is found in the textfile GPL.txt and important notices to the license
  60. # from the author is found in LICENSE.txt distributed with these scripts.
  61. #
  62. # This script is distributed in the hope that it will be useful,
  63. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  64. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  65. # GNU General Public License for more details.
  66. #
  67. ########################################################################################
  68. package main;
  69. use vars qw{%attr %defs %modules $readingFnAttributes $init_done};
  70. use strict;
  71. use warnings;
  72. use Time::HiRes qw( gettimeofday );
  73. #add FHEM/lib to @INC if it's not allready included. Should rather be in fhem.pl than here though...
  74. BEGIN {
  75. if (!grep(/FHEM\/lib$/,@INC)) {
  76. foreach my $inc (grep(/FHEM$/,@INC)) {
  77. push @INC,$inc."/lib";
  78. };
  79. };
  80. };
  81. use ProtoThreads;
  82. no warnings 'deprecated';
  83. sub Log3($$$);
  84. sub AttrVal($$$);
  85. my $owx_version="6.0";
  86. my %gets = (
  87. "id" => "",
  88. "present" => "",
  89. "interval" => "",
  90. "temperature" => "",
  91. "alarm" => "",
  92. "version" => ""
  93. );
  94. my %sets = (
  95. "interval" => "",
  96. "tempHigh" => "",
  97. "tempLow" => ""
  98. );
  99. my %updates = (
  100. "present" => "",
  101. "temperature" => "",
  102. "alarm" => ""
  103. );
  104. #-- conversion times in milliseconds depend on resolution
  105. my %convtimes = (
  106. 9 => 100,
  107. 10 => 200,
  108. 11 => 400,
  109. 12 => 800,
  110. );
  111. ########################################################################################
  112. #
  113. # The following subroutines are independent of the bus interface
  114. #
  115. # Prefix = OWTHERM
  116. #
  117. ########################################################################################
  118. #
  119. # OWTHERM_Initialize
  120. #
  121. # Parameter hash = hash of device addressed
  122. #
  123. ########################################################################################
  124. sub OWTHERM_Initialize ($) {
  125. my ($hash) = @_;
  126. $hash->{DefFn} = "OWTHERM_Define";
  127. $hash->{UndefFn} = "OWTHERM_Undef";
  128. $hash->{GetFn} = "OWTHERM_Get";
  129. $hash->{SetFn} = "OWTHERM_Set";
  130. $hash->{NotifyFn}= "OWTHERM_Notify";
  131. $hash->{InitFn} = "OWTHERM_Init";
  132. $hash->{AttrFn} = "OWTHERM_Attr";
  133. $hash->{AttrList}= "IODev model:DS1820,DS18B20,DS1822 ".
  134. "stateAL stateAH ".
  135. "tempOffset tempUnit:Celsius,Fahrenheit,Kelvin ".
  136. "tempConv:onkick,onread tempLow tempHigh ".
  137. "resolution:9,10,11,12 interval ".
  138. $readingFnAttributes;
  139. #-- make sure OWX is loaded so OWX_CRC is available if running with OWServer
  140. main::LoadModule("OWX");
  141. }
  142. ########################################################################################
  143. #
  144. # OWTHERM_Define - Implements DefFn function
  145. #
  146. # Parameter hash = hash of device addressed, def = definition string
  147. #
  148. ########################################################################################
  149. sub OWTHERM_Define ($$) {
  150. my ($hash, $def) = @_;
  151. # define <name> OWTHERM [<model>] <id> [interval]
  152. # e.g.: define flow OWTHERM 525715020000 300
  153. my @a = split("[ \t][ \t]*", $def);
  154. my ($name,$model,$fam,$id,$crc,$interval,$ret);
  155. #-- default
  156. $name = $a[0];
  157. $interval = 300;
  158. $ret = "";
  159. #-- check syntax
  160. return "OWTHERM: Wrong syntax, must be define <name> OWTHERM [<model>] <id> [interval] or OWTHERM <fam>.<id> [interval]"
  161. if(int(@a) < 2 || int(@a) > 6);
  162. #-- different types of definition allowed
  163. my $a2 = $a[2];
  164. my $a3 = defined($a[3]) ? $a[3] : "";
  165. #-- no model, 12 characters
  166. if( $a2 =~ m/^[0-9|a-f|A-F]{12}$/ ) {
  167. $model = "DS1820";
  168. CommandAttr (undef,"$name model DS1820");
  169. $fam = "10";
  170. $id = $a[2];
  171. if(int(@a)>=4) { $interval = $a[3]; }
  172. #-- no model, 2+12 characters
  173. } elsif( $a2 =~ m/^[0-9|a-f|A-F]{2}\.[0-9|a-f|A-F]{12}$/ ) {
  174. $fam = substr($a[2],0,2);
  175. $id = substr($a[2],3);
  176. if(int(@a)>=4) { $interval = $a[3]; }
  177. if( $fam eq "10" ){
  178. $model = "DS1820";
  179. CommandAttr (undef,"$name model DS1820");
  180. }elsif( $fam eq "22" ){
  181. $model = "DS1822";
  182. CommandAttr (undef,"$name model DS1822");
  183. }elsif( $fam eq "28" ){
  184. $model = "DS18B20";
  185. CommandAttr (undef,"$name model DS18B20");
  186. }else{
  187. return "OWTHERM: Wrong 1-Wire device family $fam";
  188. }
  189. #-- model, 12 characters
  190. } elsif( $a3 =~ m/^[0-9|a-f|A-F]{12}$/ ) {
  191. $model = $a[2];
  192. $id = $a[3];
  193. if(int(@a)>=5) { $interval = $a[4]; }
  194. if( $model eq "DS1820" ){
  195. $fam = "10";
  196. CommandAttr (undef,"$name model DS1820");
  197. }elsif( $model eq "DS1822" ){
  198. $fam = "22";
  199. CommandAttr (undef,"$name model DS1822");
  200. }elsif( $model eq "DS18B20" ){
  201. $fam = "28";
  202. CommandAttr (undef,"$name model DS18B20");
  203. }else{
  204. return "OWTHERM: Wrong 1-Wire device model $model";
  205. }
  206. } else {
  207. return "OWTHERM: $a[0] ID $a[2] invalid, specify a 12 or 2.12 digit value";
  208. }
  209. #-- determine CRC Code
  210. $crc = sprintf("%02X",OWX_CRC($fam.".".$id."00"));
  211. #-- define device internals
  212. $hash->{ALARM} = 0;
  213. $hash->{OW_ID} = $id;
  214. $hash->{OW_FAMILY} = $fam;
  215. $hash->{PRESENT} = 0;
  216. $hash->{ROM_ID} = "$fam.$id.$crc";
  217. $hash->{INTERVAL} = $interval;
  218. $hash->{ERRCOUNT} = 0;
  219. #-- temperature globals - always the raw values from/for the device
  220. $hash->{owg_temp} = "";
  221. $hash->{owg_th} = "";
  222. $hash->{owg_tl} = "";
  223. #-- Couple to I/O device
  224. AssignIoPort($hash);
  225. if( !defined($hash->{IODev}) or !defined($hash->{IODev}->{NAME}) ){
  226. return "OWTHERM: Warning, no 1-Wire I/O device found for $name.";
  227. #-- if coupled, test if ASYNC or not
  228. } else {
  229. $hash->{ASYNC} = $hash->{IODev}->{TYPE} eq "OWX_ASYNC" ? 1 : 0;
  230. }
  231. $modules{OWTHERM}{defptr}{$id} = $hash;
  232. #--
  233. readingsSingleUpdate($hash,"state","defined",1);
  234. Log3 $name, 3, "OWTHERM: Device $name defined.";
  235. $hash->{NOTIFYDEV} = "global";
  236. if ($init_done) {
  237. OWTHERM_Init($hash);
  238. }
  239. return undef;
  240. }
  241. #######################################################################################
  242. #
  243. # OWTHERM_Notify - Implements the Notify function
  244. #
  245. # Parameter hash = hash of device addressed
  246. # a = argument array
  247. #
  248. ########################################################################################
  249. sub OWTHERM_Notify ($$) {
  250. my ($hash,$dev) = @_;
  251. if( grep(m/^(INITIALIZED|REREADCFG)$/, @{$dev->{CHANGED}}) ) {
  252. OWTHERM_Init($hash);
  253. } elsif( grep(m/^SAVE$/, @{$dev->{CHANGED}}) ) {
  254. }
  255. }
  256. #######################################################################################
  257. #
  258. # OWTHERM_Init - Implements the Init function
  259. #
  260. # Parameter hash = hash of device addressed
  261. # a = argument array
  262. #
  263. ########################################################################################
  264. sub OWTHERM_Init ($) {
  265. my ($hash)=@_;
  266. #-- Start timer for updates
  267. RemoveInternalTimer($hash);
  268. InternalTimer(gettimeofday()+10, "OWTHERM_GetValues", $hash, 0);
  269. return undef;
  270. }
  271. #######################################################################################
  272. #
  273. # OWTHERM_Attr - Set one attribute value for device
  274. #
  275. # Parameter hash = hash of device addressed
  276. # a = argument array
  277. #
  278. ########################################################################################
  279. sub OWTHERM_Attr(@) {
  280. my ($do,$name,$key,$value) = @_;
  281. my $hash = $defs{$name};
  282. my $ret;
  283. if ( $do eq "set") {
  284. ARGUMENT_HANDLER: {
  285. #-- interval modified at runtime
  286. $key eq "interval" and do {
  287. #-- check value
  288. return "OWTHERM: Set with short interval, must be > 1" if(int($value) < 1);
  289. #-- update timer
  290. $hash->{INTERVAL} = $value;
  291. if ($init_done) {
  292. RemoveInternalTimer($hash);
  293. InternalTimer(gettimeofday()+$hash->{INTERVAL}, "OWTHERM_GetValues", $hash, 0);
  294. }
  295. last;
  296. };
  297. #-- resolution modified at runtime
  298. $key eq "resolution" and do {
  299. $hash->{owg_cf} = $value;
  300. last;
  301. };
  302. #-- alarm settings modified at runtime
  303. $key =~ m/(.*)(Low|High)/ and do {
  304. #-- safeguard against uninitialized devices
  305. return undef
  306. if( $hash->{READINGS}{"state"}{VAL} eq "defined" );
  307. $ret = OWTHERM_Set($hash,($name,$key,$value));
  308. last;
  309. };
  310. $key eq "IODev" and do {
  311. AssignIoPort($hash,$value);
  312. if( defined($hash->{IODev}) ) {
  313. $hash->{ASYNC} = $hash->{IODev}->{TYPE} eq "OWX_ASYNC" ? 1 : 0;
  314. if ($init_done) {
  315. OWTHERM_Init($hash);
  316. }
  317. }
  318. last;
  319. };
  320. }
  321. }
  322. return $ret;
  323. }
  324. ########################################################################################
  325. #
  326. # OWTHERM_FormatValues - put together various format strings
  327. #
  328. # Parameter hash = hash of device addressed, fs = format string
  329. #
  330. ########################################################################################
  331. sub OWTHERM_FormatValues($) {
  332. my ($hash) = @_;
  333. my $name = $hash->{NAME};
  334. my $interface = $hash->{IODev}->{TYPE};
  335. my ($unit,$offset,$factor,$abbr,$vval,$vlow,$vhigh,$statef,$stateal,$stateah);
  336. my $svalue = "";
  337. #-- attributes defined ?
  338. $stateal = AttrVal($name,"stateAL","↓");
  339. $stateah = AttrVal($name,"stateAH","↑");
  340. $unit = AttrVal($name,"tempUnit","Celsius");
  341. $offset = AttrVal($name,"tempOffset",0.0);
  342. $factor = 1.0;
  343. if( $unit eq "none" ){
  344. $abbr = "";
  345. }elsif( $unit eq "Celsius" ){
  346. $abbr = " °C";
  347. } elsif ($unit eq "Kelvin" ){
  348. $abbr = " K";
  349. $offset += "273.16"
  350. } elsif ($unit eq "Fahrenheit" ){
  351. $abbr = " °F";
  352. $offset = ($offset+32)/1.8;
  353. $factor = 1.8;
  354. } else {
  355. $abbr=" ?";
  356. Log3 $name, 3, "OWTHERM_FormatValues: Unknown temperature unit $unit";
  357. }
  358. #-- these values are rather complex to obtain, therefore save them in the hash
  359. $hash->{READINGS}{"temperature"}{UNIT} = $abbr;
  360. $hash->{tempf}{offset} = $offset;
  361. $hash->{tempf}{factor} = $factor;
  362. #-- no change in any value if invalid reading
  363. return if( $hash->{owg_temp} eq "");
  364. #-- correct values for proper offset, factor
  365. $vval = ($hash->{owg_temp} + $offset)*$factor;
  366. $vlow = floor(($hash->{owg_tl} + $offset)*$factor+0.5);
  367. $vhigh = floor(($hash->{owg_th} + $offset)*$factor+0.5);
  368. $main::attr{$name}{"tempLow"} = $vlow;
  369. $main::attr{$name}{"tempHigh"} = $vhigh;
  370. #-- formats for output
  371. $statef = "T: %5.2f".$abbr;
  372. $svalue = sprintf($statef,$vval);
  373. #-- Test for alarm condition
  374. $hash->{ALARM} = 1;
  375. if( ($vval <= $vlow) && ( $vval >= $vhigh ) ){
  376. $svalue .= " ".$stateal.$stateah;
  377. }elsif( $vval <= $vlow ){
  378. $svalue .= " ".$stateal;
  379. }elsif( $vval >= $vhigh ){
  380. $svalue .= " ".$stateah;
  381. } else {
  382. $hash->{ALARM} = 0;
  383. }
  384. #-- put into READINGS
  385. readingsBeginUpdate($hash);
  386. readingsBulkUpdate($hash,"temperature",$vval);
  387. #-- STATE
  388. readingsBulkUpdate($hash,"state",$svalue);
  389. readingsEndUpdate($hash,1);
  390. return $svalue;
  391. }
  392. ########################################################################################
  393. #
  394. # OWTHERM_Get - Implements GetFn function
  395. #
  396. # Parameter hash = hash of device addressed, a = argument array
  397. #
  398. ########################################################################################
  399. sub OWTHERM_Get($@) {
  400. my ($hash, @a) = @_;
  401. my $reading = $a[1];
  402. my $name = $hash->{NAME};
  403. my $model = $hash->{OW_MODEL};
  404. my $value = undef;
  405. my $ret = "";
  406. #-- check syntax
  407. return "OWTHERM: Get argument is missing @a"
  408. if(int(@a) != 2);
  409. #-- check argument
  410. return "OWTHERM: Get with unknown argument $a[1], choose one of ".join(" ", sort keys %gets)
  411. if(!defined($gets{$a[1]}));
  412. #-- get id
  413. if($a[1] eq "id") {
  414. $value = $hash->{ROM_ID};
  415. return "$name.id => $value";
  416. }
  417. #-- hash of the busmaster
  418. my $master = $hash->{IODev};
  419. #-- Get other values according to interface type
  420. my $interface= $hash->{IODev}->{TYPE};
  421. #-- get present
  422. if($a[1] eq "present" ) {
  423. #-- OWX interface
  424. if( $interface =~ /^OWX/ ){
  425. #-- asynchronous mode
  426. if( $hash->{ASYNC} ){
  427. eval {
  428. OWX_ASYNC_RunToCompletion($hash,OWX_ASYNC_PT_Verify($hash));
  429. };
  430. return GP_Catch($@) if $@;
  431. return "$name.present => ".ReadingsVal($name,"present","unknown");
  432. } else {
  433. $value = OWX_Verify($master,$hash->{ROM_ID});
  434. }
  435. $hash->{PRESENT} = $value;
  436. return "$name.present => $value";
  437. } else {
  438. return "OWTHERM: Verification not yet implemented for interface $interface";
  439. }
  440. }
  441. #-- get interval
  442. if($a[1] eq "interval") {
  443. $value = $hash->{INTERVAL};
  444. return "$name.interval => $value";
  445. }
  446. #-- get version
  447. if( $a[1] eq "version") {
  448. return "$name.version => $owx_version";
  449. }
  450. #-- OWX interface
  451. if( $interface eq "OWX" ){
  452. #-- not different from getting all values ..
  453. $ret = OWXTHERM_GetValues($hash);
  454. }elsif( $interface eq "OWX_ASYNC" ){
  455. eval {
  456. $ret = OWX_ASYNC_RunToCompletion($hash,OWXTHERM_PT_GetValues($hash));
  457. };
  458. $ret = GP_Catch($@) if $@;
  459. #-- OWFS interface
  460. }elsif( $interface eq "OWServer" ){
  461. $ret = OWFSTHERM_GetValues($hash);
  462. #-- Unknown interface
  463. }else{
  464. return "OWTHERM: Get with wrong IODev type $interface";
  465. }
  466. #-- process results
  467. if( $master->{ASYNCHRONOUS} ){
  468. return "OWTHERM: $name getting values, please wait for completion";
  469. }else{
  470. #-- when we have a return code, we have an error
  471. if( defined($ret) ){
  472. return "OWTHERM: Could not get values from device $name, return was $ret";
  473. }
  474. #-- return the special reading
  475. if ($reading eq "temperature") {
  476. return "OWTHERM: $name.temperature => ".
  477. $hash->{READINGS}{"temperature"}{VAL};
  478. } elsif ($reading eq "alarm") {
  479. return "OWTHERM: $name.alarm => L ".$main::attr{$name}{"tempLow"}.
  480. " H ".$main::attr{$name}{"tempHigh"};
  481. } else {
  482. return undef;
  483. }
  484. }
  485. }
  486. #######################################################################################
  487. #
  488. # OWTHERM_GetValues - Updates the readings from device
  489. #
  490. # Parameter hash = hash of device addressed
  491. #
  492. ########################################################################################
  493. sub OWTHERM_GetValues($@) {
  494. my $hash = shift;
  495. my $name = $hash->{NAME};
  496. my $value = "";
  497. my $ret;
  498. #-- check if device needs to be initialized
  499. if( $hash->{READINGS}{"state"}{VAL} eq "defined"){
  500. OWTHERM_InitializeDevice($hash);
  501. OWTHERM_FormatValues($hash);
  502. }
  503. #-- restart timer for updates
  504. RemoveInternalTimer($hash);
  505. InternalTimer(time()+$hash->{INTERVAL}, "OWTHERM_GetValues", $hash, 0);
  506. #-- Get values according to interface type
  507. my $interface= $hash->{IODev}->{TYPE};
  508. if( $interface eq "OWX" ){
  509. #-- max 3 tries
  510. for(my $try=0; $try<3; $try++){
  511. $ret = OWXTHERM_GetValues($hash);
  512. last
  513. if( !defined($ret) );
  514. }
  515. }elsif( $interface eq "OWX_ASYNC" ){
  516. #-- skip, if the conversion is driven by master
  517. unless ( defined($attr{$name}{tempConv}) && ( $attr{$name}{tempConv} eq "onkick") ){
  518. eval {
  519. OWX_ASYNC_Schedule( $hash, OWXTHERM_PT_GetValues($hash) );
  520. };
  521. $ret = GP_Catch($@) if $@;
  522. }
  523. }elsif( $interface eq "OWServer" ){
  524. $ret = OWFSTHERM_GetValues($hash);
  525. }else{
  526. Log3 $name, 3, "OWTHERM: GetValues with wrong IODev type $interface";
  527. return 1;
  528. }
  529. #-- process results
  530. if( defined($ret) ){
  531. $hash->{ERRCOUNT}=$hash->{ERRCOUNT}+1;
  532. if( $hash->{ERRCOUNT} > 5 ){
  533. $hash->{INTERVAL} = 9999;
  534. }
  535. return "OWTHERM: Could not get values from device $name for ".$hash->{ERRCOUNT}." times, reason $ret";
  536. }
  537. return undef;
  538. }
  539. ########################################################################################
  540. #
  541. # OWTHERM_InitializeDevice - delayed setting of initial readings
  542. #
  543. # Parameter hash = hash of device addressed
  544. #
  545. ########################################################################################
  546. sub OWTHERM_InitializeDevice($) {
  547. my ($hash) = @_;
  548. my $name = $hash->{NAME};
  549. my $master = $hash->{IODev};
  550. my $interface = $master->{TYPE};
  551. my @a = ($name,"",0);
  552. my ($unit,$offset,$factor,$abbr,$value,$ret);
  553. #-- attributes defined ?
  554. $unit = AttrVal($name,"tempUnit","Celsius");
  555. $offset = AttrVal($name,"tempOffset",0.0);
  556. $factor = 1.0;
  557. if( $unit eq "Celsius" ){
  558. $abbr = "°C";
  559. } elsif ($unit eq "Kelvin" ){
  560. $abbr = "K";
  561. $offset += "273.16"
  562. } elsif ($unit eq "Fahrenheit" ){
  563. $abbr = "°F";
  564. $offset = ($offset+32)/1.8;
  565. $factor = 1.8;
  566. } else {
  567. $abbr="?";
  568. Log3 $name, 3, "OWTHERM_InitializeDevice: unknown unit $unit";
  569. }
  570. #-- these values are rather complex to obtain, therefore save them in the hash
  571. $hash->{READINGS}{"temperature"}{TYPE} = "temperature";
  572. $hash->{READINGS}{"temperature"}{UNIT} = $abbr;
  573. $hash->{ERRCOUNT} = 0;
  574. $hash->{tempf}{offset} = $offset;
  575. $hash->{tempf}{factor} = $factor;
  576. #-- Check if temperature conversion is consistent
  577. if( $interface =~ /^OWX/ ){
  578. if( defined($attr{$name}{tempConv}) && ( $attr{$name}{tempConv} eq "onkick") ){
  579. if( !(defined($hash->{IODev}->{dokick})) ||
  580. ( defined($hash->{IODev}->{dokick}) && ($hash->{IODev}->{dokick} ne "1") )){
  581. Log3 $name, 1,"OWTHERM: Attribute tempConv=onkick changed to onread for $name because interface is not kicking";
  582. $attr{$name}{tempConv}="onread";
  583. }
  584. }
  585. }elsif( $interface eq "OWServer" ){
  586. if( !(defined($attr{$name}{tempConv})) ||
  587. (defined($attr{$name}{tempConv}) && ($attr{$name}{tempConv} eq "onread") ) ){
  588. Log3 $name, 1,"OWTHERM: Attribute tempConv=onread changed to onkick for $name because interface is OWFS";
  589. $attr{$name}{tempConv}="onread";
  590. }
  591. }
  592. my $args = {};
  593. #-- Set the attribute values if defined
  594. if ( defined($attr{$name}{resolution}) ) {
  595. $args->{resolution} = $attr{$name}{resolution};
  596. }
  597. if( defined($attr{$name}{"tempLow"}) ){
  598. $args->{tempLow} = floor($attr{$name}{"tempLow"}/$factor-$offset+0.5);
  599. }
  600. if( defined($attr{$name}{"tempHigh"}) ){
  601. $args->{tempHigh} = floor($attr{$name}{"tempHigh"}/$factor-$offset+0.5);
  602. }
  603. #-- put into device
  604. #-- OWX interface
  605. if( $interface eq "OWX" ){
  606. $ret = OWXTHERM_SetValues($hash,$args);
  607. }elsif( $interface eq "OWX_ASYNC" ){
  608. eval {
  609. $ret = OWX_ASYNC_RunToCompletion($hash,OWXTHERM_PT_SetValues($hash,$args));
  610. };
  611. $ret = GP_Catch($@) if $@;
  612. #-- OWFS interface
  613. }elsif( $interface eq "OWServer" ){
  614. $ret = OWFSTHERM_SetValues($hash,$args);
  615. }
  616. #-- process results
  617. if( defined($ret) ){
  618. return "OWTHERM: Could not initialize device $name, reason: ".$ret;
  619. }
  620. #-- Set state to initialized
  621. readingsSingleUpdate($hash,"state","initialized",1);
  622. return undef;
  623. }
  624. #######################################################################################
  625. #
  626. # OWTHERM_Set - Set one value for device
  627. #
  628. # Parameter hash = hash of device addressed
  629. # a = argument string
  630. #
  631. ########################################################################################
  632. sub OWTHERM_Set($@) {
  633. my ($hash, @a) = @_;
  634. #-- for the selector: which values are possible
  635. return join(" ", sort keys %sets) if(@a == 2);
  636. #-- check syntax
  637. return "OWTHERM: Set needs one parameter"
  638. if(int(@a) != 3);
  639. #-- check argument
  640. return "OWTHERM: Set with unknown argument $a[1], choose one of ".join(",", sort keys %sets)
  641. if(!defined($sets{$a[1]}));
  642. #-- define vars
  643. my $key = $a[1];
  644. my $value = $a[2];
  645. my $ret = undef;
  646. my $name = $hash->{NAME};
  647. my $model = $hash->{OW_MODEL};
  648. #-- set new timer interval
  649. if($key eq "interval") {
  650. # check value
  651. return "OWTHERM: Set with short interval, must be >= 1"
  652. if(int($value) < 1);
  653. # update timer
  654. return OWTHERM_Attr("set",@a);
  655. }
  656. #-- set tempLow or tempHigh
  657. if( (lc($key) eq "templow") || (lc($key) eq "temphigh")) {
  658. my $interface = $hash->{IODev}->{TYPE};
  659. my $offset = defined($hash->{tempf}{offset}) ? $hash->{tempf}{offset} : 0.0;
  660. my $factor = defined($hash->{tempf}{factor}) ? $hash->{tempf}{factor} : 1.0;
  661. #-- Only integer values are allowed
  662. $value = floor($value+0.5);
  663. #-- First we have to read the current data, because alarms may not be set independently
  664. $hash->{owg_tl} = floor($main::attr{$name}{"tempLow"}/$factor-$offset+0.5);
  665. $hash->{owg_th} = floor($main::attr{$name}{"tempHigh"}/$factor-$offset+0.5);
  666. #-- find upper and lower boundaries for given offset/factor
  667. my $mmin = floor((-55+$offset)*$factor+0.5);
  668. my $mmax = floor((125+$offset)*$factor+0.5);
  669. return sprintf("OWTHERM: Set with wrong value $value for $key, range is [%3.1f,%3.1f]",$mmin,$mmax)
  670. if($value < $mmin || $value > $mmax);
  671. #-- seems to be ok, correcting for offset and factor
  672. my $args = {
  673. $key => floor($value/$factor-$offset+0.5),
  674. };
  675. #-- put into attribute value
  676. if( lc($key) eq "templow" ){
  677. if( $main::attr{$name}{"tempLow"} != $value ){
  678. $main::attr{$name}{"tempLow"} = $value;
  679. }
  680. }
  681. if( lc($key) eq "temphigh" ){
  682. if( $main::attr{$name}{"tempHigh"} != $value ){
  683. $main::attr{$name}{"tempHigh"} = $value;
  684. }
  685. }
  686. #-- put into device
  687. #-- OWX interface
  688. if( $interface eq "OWX" ){
  689. $ret = OWXTHERM_SetValues($hash,$args);
  690. }elsif( $interface eq "OWX_ASYNC" ){
  691. $args->{format} = 1;
  692. eval {
  693. OWX_ASYNC_Schedule( $hash, OWXTHERM_PT_SetValues($hash,$args) );
  694. };
  695. $ret = GP_Catch($@) if $@;
  696. #-- OWFS interface
  697. }elsif( $interface eq "OWServer" ){
  698. $ret = OWFSTHERM_SetValues($hash,$args);
  699. } else {
  700. return "OWTHERM: Set with wrong IODev type $interface";
  701. }
  702. #-- process results
  703. if( defined($ret) ){
  704. return "OWTHERM: Could not set device $name, reason: ".$ret;
  705. }
  706. }
  707. #-- process results
  708. $hash->{PRESENT} = 1;
  709. OWTHERM_FormatValues($hash);
  710. Log3 $name, 4, "OWTHERM: Set $hash->{NAME} $key $value";
  711. return undef;
  712. }
  713. ########################################################################################
  714. #
  715. # OWTHERM_Undef - Implements UndefFn function
  716. #
  717. # Parameter hash = hash of device addressed
  718. #
  719. ########################################################################################
  720. sub OWTHERM_Undef ($) {
  721. my ($hash) = @_;
  722. delete($modules{OWTHERM}{defptr}{$hash->{OW_ID}});
  723. RemoveInternalTimer($hash);
  724. return undef;
  725. }
  726. ########################################################################################
  727. #
  728. # The following subroutines in alphabetical order are only for a 1-Wire bus connected
  729. # via OWFS
  730. #
  731. # Prefix = OWFSTHERM
  732. #
  733. ########################################################################################
  734. #
  735. # OWFSTHERM_GetValues - Get values from device
  736. #
  737. # Parameter hash = hash of device addressed
  738. #
  739. ########################################################################################
  740. sub OWFSTHERM_GetValues($) {
  741. my ($hash) = @_;
  742. #-- ID of the device
  743. my $owx_add = substr($hash->{ROM_ID},0,15);
  744. #-- hash of the busmaster
  745. my $master = $hash->{IODev};
  746. my $name = $hash->{NAME};
  747. #-- reset presence
  748. $hash->{PRESENT} = 0;
  749. #-- resolution (set by Attribute 'resolution' on OWFS)
  750. my $resolution = defined $hash->{owg_cf} ? $hash->{owg_cf} : "";
  751. #-- get values - or should we rather get the uncached ones ?
  752. $hash->{owg_temp} = OWServer_Read($master,"/$owx_add/temperature$resolution");
  753. my $ow_thn = OWServer_Read($master,"/$owx_add/temphigh");
  754. my $ow_tln = OWServer_Read($master,"/$owx_add/templow");
  755. return "no return from OWServer"
  756. if( (!defined($hash->{owg_temp})) || (!defined($ow_thn)) || (!defined($ow_tln)) );
  757. return "empty return from OWServer"
  758. if( ($hash->{owg_temp} eq "") || ($ow_thn eq "") || ($ow_tln eq "") );
  759. #-- process alarm settings
  760. $hash->{owg_tl} = $ow_tln;
  761. $hash->{owg_th} = $ow_thn;
  762. #-- and now from raw to formatted values
  763. $hash->{PRESENT} = 1;
  764. my $value = OWTHERM_FormatValues($hash);
  765. Log3 $name, 5, $value;
  766. return undef;
  767. }
  768. ########################################################################################
  769. #
  770. # OWFSTHERM_SetValues - Set values in device
  771. #
  772. # Parameter hash = hash of device addressed
  773. #
  774. ########################################################################################
  775. sub OWFSTHERM_SetValues($$) {
  776. my ($hash,$args) = @_;
  777. #-- ID of the device
  778. my $owx_add = substr($hash->{ROM_ID},0,15);
  779. #-- hash of the busmaster
  780. my $master = $hash->{IODev};
  781. my $name = $hash->{NAME};
  782. #-- $owg_tl and $owg_th are preset and may be changed here
  783. foreach my $key (keys %$args) {
  784. my $value = $args->{$key};
  785. next unless (defined $value and $value ne "");
  786. if( lc($key) eq "templow") {
  787. $hash->{owg_tl} = $value;
  788. } elsif( lc($key) eq "temphigh") {
  789. $hash->{owg_th} = $value;
  790. } elsif( lc($key) eq "resolution") {
  791. $hash->{owg_cf} = $value;
  792. next;
  793. } else {
  794. next;
  795. }
  796. OWServer_Write($master, "/$owx_add/".lc($key),$value);
  797. }
  798. return undef
  799. }
  800. ########################################################################################
  801. #
  802. # The following subroutines in alphabetical order are only for a 1-Wire bus connected
  803. # directly to the FHEM server
  804. #
  805. # Prefix = OWXTHERM
  806. #
  807. ########################################################################################
  808. #
  809. # OWXTHERM_BinValues - Process reading from one device - translate binary into raw
  810. #
  811. # Parameter hash = hash of device addressed
  812. # context = mode for evaluating the binary data
  813. # proc = processing instruction, also passed to OWX_Read.
  814. # bitwise interpretation !!
  815. # if 0, nothing special
  816. # if 1 = bit 0, a reset will be performed not only before, but also after
  817. # the last operation in OWX_Read
  818. # if 2 = bit 1, the initial reset of the bus will be suppressed
  819. # if 8 = bit 3, the fillup of the data with 0xff will be suppressed
  820. # if 16= bit 4, the insertion will be at the top of the queue
  821. # owx_dev = ROM ID of slave device
  822. # crcpart = part of the data that needs to be part of the CRC check
  823. # numread = number of bytes to receive
  824. # res = result string
  825. #
  826. #
  827. ########################################################################################
  828. sub OWXTHERM_BinValues($$$$$$$) {
  829. my ($hash, $context, $reset, $owx_dev, $crcpart, $numread, $res) = @_;
  830. my ($i,$j,$k,@data,$ow_thn,$ow_tln);
  831. my $change = 0;
  832. my $name = $hash->{NAME};
  833. my $msg;
  834. OWX_WDBG($name,"OWTHERM_BinValues called for device $name with ",$res)
  835. if( $main::owx_debug>2 );
  836. #-- process results
  837. @data=split(//,$res);
  838. if (@data != 9){
  839. $msg="Error - $name returns invalid data length, ".int(@data)." instead of 9 bytes, ";
  840. }elsif(ord($data[7])<=0){
  841. $msg="Error - $name returns invalid data, ";
  842. }elsif(OWX_CRC8(substr($res,0,8),$data[8])==0){
  843. $msg="Error - invalid data from device $name, invalid CRC, ";
  844. }else{
  845. $msg="No error, ";
  846. for(my $i=0;$i<8;$i++){
  847. $hash->{owg_val}->[$i] = (ord($data[0])>>$i) & 1;
  848. $hash->{owg_vax}->[$i] = (ord($data[1])>>$i) & 1;
  849. };
  850. }
  851. OWX_WDBG($name,"OWXTHERM_BinValues: ".$msg,$res)
  852. if( $main::owx_debug>2 );
  853. #-- this must be different for the different device types
  854. # family = 10 => DS1820, DS18S20
  855. if( $hash->{OW_FAMILY} eq "10" ) {
  856. my $count_remain = ord($data[6]);
  857. my $count_perc = ord($data[7]);
  858. my $delta = -0.25 + ($count_perc - $count_remain)/$count_perc;
  859. my $lsb = ord($data[0]);
  860. my $msb = 0;
  861. my $sign = ord($data[1]) & 255;
  862. #-- test with -25 degrees
  863. #$lsb = 12*16+14;
  864. #$sign = 1;
  865. #$delta = 0;
  866. #-- 2's complement form = signed bytes
  867. $hash->{owg_temp} = int($lsb/2) + $delta;
  868. if( $sign !=0 ){
  869. $hash->{owg_temp} = -128+$hash->{owg_temp};
  870. }
  871. $ow_thn = ord($data[2]) > 127 ? 128-ord($data[2]) : ord($data[2]);
  872. $ow_tln = ord($data[3]) > 127 ? 128-ord($data[3]) : ord($data[3]);
  873. } elsif ( ($hash->{OW_FAMILY} eq "22") || ($hash->{OW_FAMILY} eq "28") ) {
  874. my $lsb = ord($data[0]);
  875. my $msb = ord($data[1]) & 7;
  876. my $sign = ord($data[1]) & 248;
  877. #-- test with -55 degrees
  878. #$lsb = 9*16;
  879. #$sign = 1;
  880. #$msb = 7;
  881. #-- 2's complement form = signed bytes
  882. $hash->{owg_temp} = $msb*16+ $lsb/16;
  883. if( $sign !=0 ){
  884. $hash->{owg_temp} = -128+$hash->{owg_temp};
  885. }
  886. $ow_thn = ord($data[2]) > 127 ? 128-ord($data[2]) : ord($data[2]);
  887. $ow_tln = ord($data[3]) > 127 ? 128-ord($data[3]) : ord($data[3]);
  888. } else {
  889. die "OWTHERM: $name: Unknown device family $hash->{OW_FAMILY}\n";
  890. }
  891. #-- process alarm settings
  892. $hash->{owg_tl} = $ow_tln;
  893. $hash->{owg_th} = $ow_thn;
  894. #-- and now from raw to formatted values
  895. $hash->{PRESENT} = 1;
  896. my $value = OWTHERM_FormatValues($hash);
  897. return undef;
  898. }
  899. ########################################################################################
  900. #
  901. # OWXTHERM_GetValues - Trigger reading from one device
  902. #
  903. # Parameter hash = hash of device addressed
  904. #
  905. ########################################################################################
  906. sub OWXTHERM_GetValues($) {
  907. my ($hash) = @_;
  908. #-- For default, perform the conversion now
  909. my $con=1;
  910. #-- ID of the device
  911. my $owx_dev = $hash->{ROM_ID};
  912. #-- hash of the busmaster
  913. my $master = $hash->{IODev};
  914. my $name = $hash->{NAME};
  915. my $res;
  916. #-- check, if the conversion has been called before for all sensors
  917. if( defined($attr{$name}{tempConv}) && ( $attr{$name}{tempConv} eq "onkick") ){
  918. $con=0;
  919. }
  920. #-- if the conversion has not been called before
  921. #-- issue the match ROM command \x55 and the start conversion command \x44
  922. #-- conversion needs some 950 ms - but we may also do it in shorter time !
  923. if( $con==1 ){
  924. #-- OLD OWX interface
  925. if( !$master->{ASYNCHRONOUS} ){
  926. OWX_Reset($master);
  927. if( OWX_Complex($master,$owx_dev,"\x44",0) eq 0 ){
  928. return "OWTHERM: $name not accessible";
  929. }
  930. select(undef,undef,undef,$convtimes{AttrVal($name,"resolution",12)}*0.001);
  931. #-- NEW OWX interface
  932. }else{
  933. #### master slave context proc owx_dev data crcpart numread startread callback delay
  934. OWX_Qomplex($master, $hash, undef, 0, $owx_dev, "\x44", 0, 0, undef, undef, $convtimes{AttrVal($name,"resolution",12)}*0.001);
  935. }
  936. }
  937. #-- NOW ask the specific device
  938. #-- issue the match ROM command \x55 and the read scratchpad command \xBE
  939. #-- reading 9 + 1 + 8 data bytes and 1 CRC byte = 19 bytes
  940. #-- OLD OWX interface
  941. if( !$master->{ASYNCHRONOUS} ){
  942. OWX_Reset($master);
  943. my $res=OWX_Complex($master,$owx_dev,"\xBE",9);
  944. return "OWTHERM: $name not accessible in reading"
  945. if( $res eq 0 );
  946. return "OWTHERM: $name has returned invalid data"
  947. if( length($res)!=19);
  948. eval {
  949. OWXTHERM_BinValues($hash,undef,$owx_dev,undef,undef,9,substr($res,10,9));
  950. };
  951. return $@ ? $@ : undef;
  952. #-- NEW OWX interface
  953. }else{
  954. #### master slave context proc owx_dev data crcpart numread startread callback delay
  955. OWX_Qomplex($master, $hash, undef, 0, $owx_dev, "\xBE", 0, 9, 10, \&OWXTHERM_BinValues, 0);
  956. return undef;
  957. }
  958. }
  959. #######################################################################################
  960. #
  961. # OWXTHERM_SetValues - Implements SetFn function
  962. #
  963. # Parameter hash = hash of device addressed
  964. # a = argument array
  965. #
  966. ########################################################################################
  967. sub OWXTHERM_SetValues($$) {
  968. my ($hash, $args) = @_;
  969. my $name = $hash->{NAME};
  970. #-- ID of the device
  971. my $owx_dev = $hash->{ROM_ID};
  972. #-- hash of the busmaster
  973. my $master = $hash->{IODev};
  974. return undef unless (defined $args->{resolution} or defined $args->{tempLow} or defined $args->{tempHigh});
  975. #-- $owg_tl and $owg_th are preset and may be changed here
  976. foreach my $key (keys %$args) {
  977. $hash->{owg_tl} = $args->{$key} if( lc($key) eq "templow");
  978. $hash->{owg_th} = $args->{$key} if( lc($key) eq "temphigh");
  979. $hash->{owg_cf} = $args->{$key} if( lc($key) eq "resolution");
  980. }
  981. #-- put into 2's complement formed (signed byte)
  982. my $tlp = $hash->{owg_tl} < 0 ? 128 - $hash->{owg_tl} : $hash->{owg_tl};
  983. my $thp = $hash->{owg_th} < 0 ? 128 - $hash->{owg_th} : $hash->{owg_th};
  984. #-- resolution is defined in bits 5+6 of configuration register
  985. my $cfg = defined $hash->{owg_cf} ? (($hash->{owg_cf}-9) << 5) | 0x1f : 0x7f;
  986. #-- issue the match ROM command \x55 and the write scratchpad command \x4E,
  987. # followed by 3 bytes of data (alarm_temp_high, alarm_temp_low, config)
  988. # config-byte of 0x7F means 12 bit resolution (750ms convert time)
  989. #
  990. # so far writing the EEPROM does not work properly.
  991. # 1. \x48 directly appended to the write scratchpad command => command ok, no effect on EEPROM
  992. # 2. \x48 appended to match ROM => command not ok.
  993. # 3. \x48 sent by WriteBytePower after match ROM => command ok, no effect on EEPROM
  994. my $select=sprintf("\x4E%c%c%c",$thp,$tlp,$cfg);
  995. #-- OLD OWX interface
  996. if( !$master->{ASYNCHRONOUS} ){
  997. OWX_Reset($master);
  998. my $res=OWX_Complex($master,$owx_dev,$select,3);
  999. if( $res eq 0 ){
  1000. return "OWTHERM: $name not accessible for setting";
  1001. }
  1002. #-- NEW OWX interface
  1003. }else{
  1004. #### master slave context proc owx_dev data crcpart numread startread callback delay
  1005. OWX_Qomplex($master, $hash, undef, 0, $owx_dev, $select, 0, 3, 10, undef, 0);
  1006. }
  1007. return undef;
  1008. }
  1009. ########################################################################################
  1010. #
  1011. # OWXTHERM_GetValues - Trigger reading from one device
  1012. #
  1013. # Parameter hash = hash of device addressed
  1014. #
  1015. ########################################################################################
  1016. sub OWXTHERM_PT_GetValues($) {
  1017. my ($hash) = @_;
  1018. return PT_THREAD(sub {
  1019. my ($thread) = @_;
  1020. #-- For default, perform the conversion now
  1021. my $con=1;
  1022. #-- ID of the device
  1023. my $owx_dev = $hash->{ROM_ID};
  1024. #-- hash of the busmaster
  1025. my $master = $hash->{IODev};
  1026. my $name = $hash->{NAME};
  1027. PT_BEGIN($thread);
  1028. #-- check, if the conversion has been called before for all sensors
  1029. if( defined($attr{$name}{tempConv}) && ( $attr{$name}{tempConv} eq "onkick") ){
  1030. $con=0;
  1031. }
  1032. #-- if the conversion has not been called before
  1033. if( $con==1 ){
  1034. #-- issue the match ROM command \x55 and the start conversion command \x44
  1035. my $now = gettimeofday();
  1036. my $delay = $convtimes{AttrVal($name,"resolution",12)};
  1037. $thread->{ExecuteTime} = $now + $delay*0.001;
  1038. $thread->{pt_execute} = OWX_ASYNC_PT_Execute($master,1,$owx_dev,"\x44",0);
  1039. PT_WAIT_THREAD($thread->{pt_execute});
  1040. die $thread->{pt_execute}->PT_CAUSE() if ($thread->{pt_execute}->PT_STATE() == PT_ERROR);
  1041. PT_YIELD_UNTIL(gettimeofday() >= $thread->{ExecuteTime});
  1042. delete $thread->{ExecuteTime};
  1043. }
  1044. #-- NOW ask the specific device
  1045. #-- issue the match ROM command \x55 and the read scratchpad command \xBE
  1046. #-- reading 9 + 1 + 8 data bytes and 1 CRC byte = 19 bytes
  1047. $thread->{pt_execute} = OWX_ASYNC_PT_Execute($master,1,$owx_dev,"\xBE",9);
  1048. PT_WAIT_THREAD($thread->{pt_execute});
  1049. die $thread->{pt_execute}->PT_CAUSE() if ($thread->{pt_execute}->PT_STATE() == PT_ERROR);
  1050. OWXTHERM_BinValues($hash,undef,1,$owx_dev,undef,9,$thread->{pt_execute}->PT_RETVAL());
  1051. PT_END;
  1052. });
  1053. }
  1054. #######################################################################################
  1055. #
  1056. # OWXTHERM_PT_SetValues - Implements SetFn function async
  1057. #
  1058. # Parameter hash = hash of device addressed
  1059. # a = argument array
  1060. #
  1061. ########################################################################################
  1062. sub OWXTHERM_PT_SetValues($$) {
  1063. my ($hash,$args) = @_;
  1064. return PT_THREAD( sub {
  1065. my ($thread) = @_;
  1066. my ($i,$j,$k);
  1067. my $name = $hash->{NAME};
  1068. #-- ID of the device
  1069. my $owx_dev = $hash->{ROM_ID};
  1070. #-- hash of the busmaster
  1071. my $master = $hash->{IODev};
  1072. PT_BEGIN($thread);
  1073. unless (defined $args->{resolution} or defined $args->{tempLow} or defined $args->{tempHigh}) {
  1074. PT_EXIT;
  1075. }
  1076. #-- $owg_tl and $owg_th are preset and may be changed here
  1077. foreach my $key (keys %$args) {
  1078. $hash->{owg_tl} = $args->{$key} if( lc($key) eq "templow");
  1079. $hash->{owg_th} = $args->{$key} if( lc($key) eq "temphigh");
  1080. $hash->{owg_cf} = $args->{$key} if( lc($key) eq "resolution");
  1081. }
  1082. #-- put into 2's complement formed (signed byte)
  1083. my $tlp = $hash->{owg_tl} < 0 ? 128 - $hash->{owg_tl} : $hash->{owg_tl};
  1084. my $thp = $hash->{owg_th} < 0 ? 128 - $hash->{owg_th} : $hash->{owg_th};
  1085. #-- resolution is defined in bits 5+6 of configuration register
  1086. my $cfg = defined $hash->{owg_cf} ? (($hash->{owg_cf}-9) << 5) | 0x1f : 0x7f;
  1087. #-- issue the match ROM command \x55 and the write scratchpad command \x4E,
  1088. # followed by 3 bytes of data (alarm_temp_high, alarm_temp_low, config)
  1089. # config-byte of 0x7F means 12 bit resolution (750ms convert time)
  1090. #
  1091. # so far writing the EEPROM does not work properly.
  1092. # 1. \x48 directly appended to the write scratchpad command => command ok, no effect on EEPROM
  1093. # 2. \x48 appended to match ROM => command not ok.
  1094. # 3. \x48 sent by WriteBytePower after match ROM => command ok, no effect on EEPROM
  1095. my $select=sprintf("\x4E%c%c%c",$thp,$tlp,$cfg);
  1096. $thread->{pt_execute} = OWX_ASYNC_PT_Execute($master,1,$owx_dev,$select,3);
  1097. PT_WAIT_THREAD($thread->{pt_execute});
  1098. die $thread->{pt_execute}->PT_CAUSE() if ($thread->{pt_execute}->PT_STATE() == PT_ERROR);
  1099. #-- process results
  1100. $hash->{PRESENT} = 1;
  1101. if ($args->{format}) {
  1102. OWTHERM_FormatValues($hash);
  1103. }
  1104. PT_END;
  1105. });
  1106. }
  1107. 1;
  1108. =pod
  1109. =begin html
  1110. <a name="OWTHERM"></a>
  1111. <h3>OWTHERM</h3>
  1112. <p>FHEM module to commmunicate with 1-Wire bus digital thermometer devices<br />
  1113. <br />This 1-Wire module works with the OWX interface module or with the OWServer interface module
  1114. (prerequisite: Add this module's name to the list of clients in OWServer).
  1115. Please define an <a href="#OWX">OWX</a> device or <a href="#OWServer">OWServer</a> device first. <br />
  1116. </p>
  1117. <h4>Example</h4>
  1118. <p>
  1119. <code>define OWX_T OWTHERM DS18B20 E8D09B030000 300</code>
  1120. <br />
  1121. <code>attr OWX_T tempUnit Kelvin</code>
  1122. <br />
  1123. </p><br />
  1124. <a name="OWTHERMdefine"></a>
  1125. <h4>Define</h4>
  1126. <p>
  1127. <code>define &lt;name&gt; OWTHERM [&lt;model&gt;] &lt;id&gt; [&lt;interval&gt;]</code> or <br/>
  1128. <code>define &lt;name&gt; OWTHERM &lt;fam&gt;.&lt;id&gt; [&lt;interval&gt;]</code>
  1129. <br /><br /> Define a 1-Wire digital thermometer device.</p>
  1130. <ul>
  1131. <li>
  1132. <code>[&lt;model&gt;]</code><br /> Defines the thermometer model (and thus 1-Wire family
  1133. id) currently the following values are permitted: </p>
  1134. <ul>
  1135. <li>model DS1820 with family id 10 (default if the model parameter is omitted)</li>
  1136. <li>model DS1822 with family id 22</li>
  1137. <li>model DS18B20 with family id 28</li>
  1138. </ul>
  1139. </li>
  1140. <li>
  1141. <code>&lt;fam&gt;</code>
  1142. <br />2-character unique family id, see above </li>
  1143. <li>
  1144. <code>&lt;id&gt;</code>
  1145. <br />12-character unique ROM id of the thermometer device without family id and CRC
  1146. code
  1147. </li>
  1148. <li>
  1149. <code>&lt;interval&gt;</code>
  1150. <br /> Temperature measurement interval in seconds. The default is 300 seconds.
  1151. </li>
  1152. </ul>
  1153. <a name="OWTHERMset"></a>
  1154. <h4>Set</h4>
  1155. <ul>
  1156. <li><a name="owtherm_interval">
  1157. <code>set &lt;name&gt; interval &lt;int&gt;</code></a><br /> Temperature
  1158. readout interval in seconds. The default is 300 seconds. <b>Attention:</b>This is the
  1159. readout interval. Whether an actual temperature measurement is performed, is determined by the
  1160. tempConv attribute </li>
  1161. <li><a name="owtherm_tempHigh">
  1162. <code>set &lt;name&gt; tempHigh &lt;float&gt;</code></a>
  1163. <br /> The high alarm temperature (on the temperature scale chosen by the attribute
  1164. value) </li>
  1165. <li><a name="owtherm_tempLow">
  1166. <code>set &lt;name&gt; tempLow &lt;float&gt;</code></a>
  1167. <br /> The low alarm temperature (on the temperature scale chosen by the attribute
  1168. value) </li>
  1169. </ul>
  1170. <br />
  1171. <a name="OWTHERMget"></a>
  1172. <h4>Get</h4>
  1173. <ul>
  1174. <li><a name="owtherm_id">
  1175. <code>get &lt;name&gt; id</code></a>
  1176. <br /> Returns the full 1-Wire device id OW_FAMILY.ROM_ID.CRC </li>
  1177. <li><a name="owtherm_present">
  1178. <code>get &lt;name&gt; present</code></a>
  1179. <br /> Returns 1 if this 1-Wire device is present, otherwise 0. </li>
  1180. <li><a name="owtherm_interval2">
  1181. <code>get &lt;name&gt; interval</code></a><br />Returns temperature measurement
  1182. interval in seconds.</li>
  1183. <li><a name="owtherm_temperature">
  1184. <code>get &lt;name&gt; temperature</code></a><br />Obtain the temperature. </li>
  1185. <li><a name="owtherm_alarm">
  1186. <code>get &lt;name&gt; alarm</code></a><br />Obtain the alarm temperature
  1187. values. </li>
  1188. </ul>
  1189. <br />
  1190. <a name="OWTHERMattr"></a>
  1191. <h4>Attributes</h4>
  1192. <ul>
  1193. <li><a name="owtherm_stateAL"><code>attr &lt;name&gt; stateAL &lt;string&gt;</code>
  1194. </a>
  1195. <br />character string for denoting low alarm condition, default is ↓</li>
  1196. <li><a name="owtherm_stateAH"><code>attr &lt;name&gt; stateAH &lt;string&gt;</code>
  1197. </a>
  1198. <br />character string for denoting high alarm condition, default is ↑</li>
  1199. <li><a name="owtherm_tempConv">
  1200. <code>attr &lt;name&gt; tempConv onkick|onread</code>
  1201. </a>
  1202. <br /> determines, whether a temperature measurement will happen when "kicked"
  1203. through the OWX backend module (all temperature sensors at the same time), or on
  1204. reading the sensor (1 second waiting time, default). </li>
  1205. <li><a name="owtherm_tempOffset"><code>attr &lt;name&gt; tempOffset &lt;float&gt;</code>
  1206. </a>
  1207. <br />temperature offset in °C added to the raw temperature reading. </li>
  1208. <li><a name="owtherm_tempUnit"><code>attr &lt;name&gt; tempUnit
  1209. none|Celsius|Kelvin|Fahrenheit</code>
  1210. </a>
  1211. <br />unit of measurement (temperature scale) for state reading (default is Celsius = °C, use "none" for empty).</li>
  1212. <li><a name="owtherm_resolution">
  1213. <code>attr &lt;name&gt; resolution 9|10|11|12</code></a><br /> Temperature
  1214. resolution in bit, only relevant for DS18B20 </li>
  1215. <li><a name="owtherm_interval2">
  1216. <code>attr &lt;name&gt; interval &lt;int&gt;</code></a><br /> Temperature
  1217. readout interval in seconds. The default is 300 seconds. <b>Attention:</b>This is the
  1218. readout interval. Whether an actual temperature measurement is performed, is determined by the
  1219. tempConv attribute </li>
  1220. <li><a name="owtherm_tempHigh2">
  1221. <code>attr &lt;name&gt; tempHigh &lt;float&gt;</code>
  1222. </a>
  1223. <br /> high alarm temperature (on the temperature scale chosen by the attribute
  1224. value). </li>
  1225. <li><a name="owtherm_tempLow2">
  1226. <code>attr &lt;name&gt; tempLow &lt;float&gt;</code>
  1227. </a>
  1228. <br /> low alarm temperature (on the temperature scale chosen by the attribute
  1229. value). </li>
  1230. <li><a href="#readingFnAttributes">readingFnAttributes</a></li>
  1231. </ul>
  1232. =end html
  1233. =cut