21_OWTEMP.pm 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618
  1. ################################################################
  2. #
  3. # Copyright notice
  4. #
  5. # (c) 2009 Copyright: Martin Fischer (m_fischer at gmx dot de)
  6. # All rights reserved
  7. #
  8. # This script free software; you can redistribute it and/or modify
  9. # it under the terms of the GNU General Public License as published by
  10. # the Free Software Foundation; either version 2 of the License, or
  11. # (at your option) any later version.
  12. #
  13. # The GNU General Public License can be found at
  14. # http://www.gnu.org/copyleft/gpl.html.
  15. # A copy is found in the textfile GPL.txt and important notices to the license
  16. # from the author is found in LICENSE.txt distributed with these scripts.
  17. #
  18. # This script is distributed in the hope that it will be useful,
  19. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  20. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  21. # GNU General Public License for more details.
  22. #
  23. ################################################################
  24. # $Id: 21_OWTEMP.pm 6164 2014-06-25 13:40:30Z ntruchsess $
  25. package main;
  26. use strict;
  27. use warnings;
  28. use Time::HiRes qw(gettimeofday);
  29. use OW;
  30. my %gets = (
  31. "address" => "",
  32. "alias" => "",
  33. "crc8" => "",
  34. "family" => "10",
  35. "id" => "",
  36. "locator" => "",
  37. "power" => "",
  38. "present" => "",
  39. # "r_address" => "",
  40. # "r_id" => "",
  41. # "r_locator" => "",
  42. "temperature" => "",
  43. "temphigh" => "",
  44. "templow" => "",
  45. "type" => "",
  46. );
  47. my %sets = (
  48. "alias" => "",
  49. "temphigh" => "",
  50. "templow" => "",
  51. "interval" => "",
  52. "alarminterval" => "",
  53. );
  54. my %updates = (
  55. "present" => "",
  56. "temperature" => "",
  57. "templow" => "",
  58. "temphigh" => "",
  59. );
  60. my %dummy = (
  61. "crc8" => "4D",
  62. "alias" => "dummy",
  63. "locator" => "FFFFFFFFFFFFFFFF",
  64. "power" => "0",
  65. "present" => "1",
  66. "temphigh" => "75",
  67. "templow" => "10",
  68. "type" => "DS18S20",
  69. "warnings" => "none",
  70. );
  71. #####################################
  72. sub
  73. OWTEMP_Initialize($)
  74. {
  75. my ($hash) = @_;
  76. $hash->{DefFn} = "OWTEMP_Define";
  77. $hash->{UndefFn} = "OWTEMP_Undef";
  78. $hash->{GetFn} = "OWTEMP_Get";
  79. $hash->{SetFn} = "OWTEMP_Set";
  80. $hash->{AttrList}= "IODev do_not_notify:0,1 showtime:0,1 model:DS18S20 loglevel:0,1,2,3,4,5";
  81. }
  82. #####################################
  83. sub
  84. OWTEMP_UpdateReading($$$$)
  85. {
  86. my ($hash,$reading,$now,$value) = @_;
  87. # define vars
  88. my $temp;
  89. # exit if empty value
  90. return 0
  91. if(!defined($value) || $value eq "");
  92. # trim value
  93. $value =~ s/\s//g
  94. if($reading ne "warnings");
  95. if($reading eq "temperature") {
  96. $value = sprintf("%.4f",$value);
  97. $temp = $value;
  98. $value = $value . " (".$hash->{OW_SCALE}.")";
  99. }
  100. # update readings
  101. $hash->{READINGS}{$reading}{TIME} = $now;
  102. $hash->{READINGS}{$reading}{VAL} = $value;
  103. Log 4, "OWTEMP $hash->{NAME} $reading: $value";
  104. return $value;
  105. }
  106. #####################################
  107. sub
  108. OWTEMP_GetUpdate($$)
  109. {
  110. my ($hash, $a) = @_;
  111. # define vars
  112. my $name = $hash->{NAME};
  113. my $now = TimeNow();
  114. my $value = "";
  115. my $temp = "";
  116. my $ret = "";
  117. my $count = 0;
  118. # define warnings
  119. my $warn = "none";
  120. $hash->{ALARM} = "0";
  121. # check for real sensor
  122. if($hash->{OW_ID} ne "none") {
  123. # real sensor
  124. if(!$hash->{LOCAL} || $a eq "") {
  125. foreach my $r (sort keys %updates) {
  126. $ret = "";
  127. $ret = OW::get("/uncached/".$hash->{OW_PATH}."/".$r);
  128. if(!defined($ret)) {
  129. #
  130. $hash->{PRESENT} = "0";
  131. $r = "present";
  132. $value = "0";
  133. $ret = OWTEMP_UpdateReading($hash,$r,$now,$value);
  134. $hash->{CHANGED}[$count] = "present: ".$value
  135. } else {
  136. $hash->{PRESENT} = "1";
  137. $value = $ret;
  138. if($r eq "temperature") {
  139. $temp = sprintf("%.4f",$value);
  140. $temp =~ s/\s//g;
  141. }
  142. $ret = OWTEMP_UpdateReading($hash,$r,$now,$value);
  143. }
  144. last if($hash->{PRESENT} eq "0");
  145. }
  146. } else {
  147. $ret = "";
  148. $ret = OW::get("/uncached/".$hash->{OW_PATH}."/".$a);
  149. if(!defined($ret)) {
  150. $hash->{PRESENT} = "0";
  151. $a = "present";
  152. $value = "0";
  153. $ret = OWTEMP_UpdateReading($hash,$a,$now,$value);
  154. } else {
  155. $hash->{PRESENT} = "1";
  156. $value = $ret;
  157. if($a eq "temperature") {
  158. $temp = sprintf("%.4f",$value);
  159. $temp =~ s/\s//g;
  160. $value = $temp;
  161. }
  162. $ret = OWTEMP_UpdateReading($hash,$a,$now,$value);
  163. }
  164. }
  165. } else {
  166. # dummy sensor
  167. $temp = sprintf("%.4f",rand(85));
  168. $dummy{temperature} = $temp;
  169. $dummy{present} = "1";
  170. $hash->{PRESENT} = $dummy{present};
  171. if(!$hash->{LOCAL} || $a eq "") {
  172. foreach my $r (sort keys %updates) {
  173. $ret = OWTEMP_UpdateReading($hash,$r,$now,$dummy{$r});
  174. }
  175. } else {
  176. $ret = "";
  177. $ret = $dummy{$a};
  178. if($ret ne "") {
  179. $value = $ret;
  180. if($a eq "temperature") {
  181. $temp = sprintf("%.4f",$value);
  182. $temp =~ s/\s//g;
  183. }
  184. $ret = OWTEMP_UpdateReading($hash,$a,$now,$value);
  185. }
  186. }
  187. }
  188. return 1
  189. if($hash->{LOCAL} && $a eq "" && $hash->{PRESENT} eq "0");
  190. # check for warnings
  191. my $templow = $hash->{READINGS}{templow}{VAL};
  192. my $temphigh = $hash->{READINGS}{temphigh}{VAL};
  193. if($hash->{PRESENT} eq "1") {
  194. if($temp <= $templow) {
  195. # low temperature
  196. $hash->{ALARM} = "1";
  197. $warn = "templow";
  198. } elsif($temp >= $temphigh) {
  199. # high temperature
  200. $hash->{ALARM} = "1";
  201. $warn = "temphigh";
  202. }
  203. } else {
  204. # set old state
  205. $temp = $hash->{READINGS}{temperature}{VAL};
  206. ($temp,undef) = split(" ",$temp);
  207. # sensor is missing
  208. $hash->{ALARM} = "1";
  209. $warn = "not present";
  210. }
  211. if(!$hash->{LOCAL} || $a eq "") {
  212. $ret = OWTEMP_UpdateReading($hash,"warnings",$now,$warn);
  213. }
  214. $hash->{STATE} = "T: ".$temp." ".
  215. "L: ".$templow." ".
  216. "H: ".$temphigh." ".
  217. "P: ".$hash->{PRESENT}." ".
  218. "A: ".$hash->{ALARM}." ".
  219. "W: ".$warn;
  220. # inform changes
  221. # state
  222. $hash->{CHANGED}[$count++] = $hash->{STATE};
  223. # present
  224. $hash->{CHANGED}[$count++] = "present: ".$hash->{PRESENT}
  225. if(defined($hash->{PRESENT}) && $hash->{PRESENT} ne "");
  226. # temperature
  227. $hash->{CHANGED}[$count++] = "temperature: ".$temp." (".$hash->{OW_SCALE}.")"
  228. if(defined($temp) && $temp ne "");
  229. # temperature raw
  230. $hash->{CHANGED}[$count++] = "tempraw: ".$temp
  231. if(defined($temp) && $temp ne "");
  232. # low temperature
  233. $hash->{CHANGED}[$count++] = "templow: ".$templow
  234. if(defined($templow) && $templow ne "");
  235. # high temperature
  236. $hash->{CHANGED}[$count++] = "temphigh: ".$temphigh
  237. if(defined($temphigh) && $temphigh ne "");
  238. # warnings
  239. $hash->{CHANGED}[$count++] = "warnings: ".$warn
  240. if(defined($warn) && $warn ne "");
  241. if(!$hash->{LOCAL}) {
  242. # update timer
  243. RemoveInternalTimer($hash);
  244. # check alarm
  245. if($hash->{ALARM} eq "0") {
  246. $hash->{INTERVAL} = $hash->{INTV_CHECK};
  247. } else {
  248. $hash->{INTERVAL} = $hash->{INTV_ALARM};
  249. }
  250. InternalTimer(gettimeofday()+$hash->{INTERVAL}, "OWTEMP_GetUpdate", $hash, 0);
  251. } else {
  252. return $value;
  253. }
  254. if(!$hash->{LOCAL}) {
  255. DoTrigger($name, undef) if($init_done);
  256. }
  257. return $hash->{STATE};
  258. }
  259. #####################################
  260. sub
  261. OWTEMP_Get($@)
  262. {
  263. my ($hash, @a) = @_;
  264. # check syntax
  265. return "argument is missing @a"
  266. if(int(@a) != 2);
  267. # check argument
  268. return "Unknown argument $a[1], choose one of ".join(",", sort keys %gets)
  269. if(!defined($gets{$a[1]}));
  270. # define vars
  271. my $value;
  272. # get value
  273. $hash->{LOCAL} = 1;
  274. $value = OWTEMP_GetUpdate($hash,$a[1]);
  275. delete $hash->{LOCAL};
  276. my $reading = $a[1];
  277. if(defined($hash->{READINGS}{$reading})) {
  278. $value = $hash->{READINGS}{$reading}{VAL};
  279. }
  280. return "$a[0] $reading => $value";
  281. }
  282. #####################################
  283. sub
  284. OWTEMP_Set($@)
  285. {
  286. my ($hash, @a) = @_;
  287. # check syntax
  288. return "set needs one parameter"
  289. if(int(@a) != 3);
  290. # check arguments
  291. return "Unknown argument $a[1], choose one of ".join(",", sort keys %sets)
  292. if(!defined($sets{$a[1]}));
  293. # define vars
  294. my $key = $a[1];
  295. my $value = $a[2];
  296. my $ret;
  297. # set new timer
  298. if($key eq "interval" || $key eq "alarminterval") {
  299. $key = "INTV_CHECK"
  300. if($key eq "interval");
  301. $key = "INTV_ALARM"
  302. if($key eq "alarminterval");
  303. # update timer
  304. $hash->{$key} = $value;
  305. RemoveInternalTimer($hash);
  306. # check alarm
  307. if($hash->{ALARM} eq "0") {
  308. $hash->{INTERVAL} = $hash->{INTV_CHECK};
  309. } else {
  310. $hash->{INTERVAL} = $hash->{INTV_ALARM};
  311. }
  312. InternalTimer(gettimeofday()+$hash->{INTERVAL}, "OWTEMP_GetUpdate", $hash, 1);
  313. }
  314. # set warnings
  315. if($key eq "templow" || $key eq "temphigh") {
  316. # check range
  317. return "wrong value: range -55°C - 125°C"
  318. if(int($value) < -55 || int($value) > 125);
  319. }
  320. # set value
  321. Log 4, "OWTEMP set $hash->{NAME} $key $value";
  322. # check for real sensor
  323. if($hash->{OW_ID} ne "none") {
  324. # real senson
  325. $ret = OW::put($hash->{OW_PATH}."/$key",$value);
  326. } else {
  327. # dummy sensor
  328. $dummy{$key} = $value;
  329. }
  330. # update readings
  331. if($key ne "interval" || $key ne "alarminterval") {
  332. $hash->{LOCAL} = 1;
  333. $ret = OWTEMP_GetUpdate($hash,$key);
  334. delete $hash->{LOCAL};
  335. }
  336. return undef;
  337. }
  338. #####################################
  339. sub
  340. OWTEMP_Define($$)
  341. {
  342. my ($hash, $def) = @_;
  343. # define <name> OWTEMP <id> [interval] [alarminterval]
  344. # e.g.: define flow OWTEMP 332670010800 300
  345. my @a = split("[ \t][ \t]*", $def);
  346. # check syntax
  347. return "wrong syntax: define <name> OWTEMP <id> [interval] [alarminterval]"
  348. if(int(@a) < 2 && int(@a) > 5);
  349. # check ID format
  350. return "Define $a[0]: missing ID or wrong ID format: specify a 12 digit value or set it to none for demo mode"
  351. if(lc($a[2]) ne "none" && lc($a[2]) !~ m/^[0-9|a-f]{12}$/);
  352. # define vars
  353. my $name = $a[0];
  354. my $id = $a[2];
  355. my $interval = 300;
  356. my $alarminterval = 300;
  357. my $scale = "";
  358. my $ret = "";
  359. # overwrite default intervals if set by define
  360. if(int(@a)==4) { $interval = $a[3]; }
  361. if(int(@a)==5) { $interval = $a[3]; $alarminterval = $a[4] }
  362. # define device internals
  363. $hash->{ALARM} = 0;
  364. $hash->{INTERVAL} = $interval;
  365. $hash->{INTV_CHECK} = $interval;
  366. $hash->{INTV_ALARM} = $alarminterval;
  367. $hash->{OW_ID} = $id;
  368. $hash->{OW_FAMILY} = $gets{family};
  369. $hash->{OW_PATH} = $hash->{OW_FAMILY}.".".$hash->{OW_ID};
  370. $hash->{PRESENT} = 0;
  371. $modules{OWTEMP}{defptr}{$a[2]} = $hash;
  372. # assign IO port
  373. AssignIoPort($hash);
  374. return "No I/O device found. Please define a OWFS device first."
  375. if(!defined($hash->{IODev}->{NAME}));
  376. # get scale from I/O device
  377. $scale = $attr{$hash->{IODev}->{NAME}}{"temp-scale"};
  378. # define scale for temperature values
  379. $scale = "Celsius" if ($scale eq "C");
  380. $scale = "Fahrenheit" if ($scale eq "F");
  381. $scale = "Kelvin" if ($scale eq "K");
  382. $scale = "Rankine" if ($scale eq "R");
  383. $hash->{OW_SCALE} = $scale;
  384. $hash->{STATE} = "Defined";
  385. # define dummy values for testing
  386. if($hash->{OW_ID} eq "none") {
  387. my $now = TimeNow();
  388. $dummy{address} = $hash->{OW_FAMILY}.$hash->{OW_ID}.$dummy{crc8};
  389. $dummy{family} = $hash->{OW_FAMILY};
  390. $dummy{id} = $hash->{OW_ID};
  391. $dummy{temperature} = "80.0000 (".$hash->{OW_SCALE}.")";
  392. foreach my $r (sort keys %gets) {
  393. $hash->{READINGS}{$r}{TIME} = $now;
  394. $hash->{READINGS}{$r}{VAL} = $dummy{$r};
  395. Log 4, "OWTEMP $hash->{NAME} $r: ".$dummy{$r};
  396. }
  397. }
  398. $hash->{STATE} = "Initialized";
  399. # initalize
  400. $hash->{LOCAL} = 1;
  401. $ret = OWTEMP_GetUpdate($hash,"");
  402. delete $hash->{LOCAL};
  403. # exit if sensor is not present
  404. return "Define $hash->{NAME}: Sensor is not reachable. Check first your 1-wire connection."
  405. if(defined($ret) && $ret eq 1);
  406. if(!$hash->{LOCAL}) {
  407. if($hash->{ALARM} eq "0") {
  408. $hash->{INTERVAL} = $hash->{INTV_CHECK};
  409. } else {
  410. $hash->{INTERVAL} = $hash->{INTV_ALARM};
  411. }
  412. InternalTimer(gettimeofday()+$hash->{INTERVAL}, "OWTEMP_GetUpdate", $hash, 0);
  413. }
  414. Log 1, "OWTEMP ########################################";
  415. Log 1, "OWTEMP # IMPORTANT NOTE:";
  416. Log 1, "OWTEMP # This module is deprecated and will be removed in a future release!";
  417. Log 1, "OWTEMP # Please use OWServer / OWDevice.";
  418. Log 1, "OWTEMP ########################################";
  419. return undef;
  420. }
  421. #####################################
  422. sub
  423. OWTEMP_Undef($$)
  424. {
  425. my ($hash, $name) = @_;
  426. delete($modules{OWTEMP}{defptr}{$hash->{NAME}});
  427. RemoveInternalTimer($hash);
  428. return undef;
  429. }
  430. 1;
  431. =pod
  432. =begin html
  433. <a name="OWTEMP"></a>
  434. <h3>OWTEMP</h3>
  435. <ul>
  436. High-Precision 1-Wire Digital Thermometer.
  437. <br><br>
  438. Note:<br>
  439. Please define an <a href="#OWFS">OWFS</a> device first.
  440. <br><br>
  441. <a name="OWTEMPdefine"></a>
  442. <b>Define</b>
  443. <ul>
  444. <code>define &lt;name&gt; OWTEMP &lt;id&gt; [&lt;interval&gt;] [&lt;alarminterval&gt;]</code>
  445. <br><br>
  446. Define a 1-wire Digital Thermometer device.<br><br>
  447. <code>&lt;id&gt;</code>
  448. <ul>
  449. Corresponding to the <a href="#owfs_id">id</a> of the input device.<br>
  450. Set &lt;id&gt; to <code>none</code>for demo mode.
  451. </ul>
  452. <code>&lt;interval&gt;</code>
  453. <ul>
  454. Sets the status polling intervall in seconds to the given value. The default is 300 seconds.
  455. </ul>
  456. <code>&lt;alarminterval&gt;</code>
  457. <ul>
  458. Sets the alarm polling intervall in seconds to the given value. The default is 300 seconds.
  459. <br><br>
  460. </ul>
  461. Note:<br>
  462. Currently supported <a href="#owfs_type">type</a>: <code>DS18S20</code>.<br><br>
  463. Example:
  464. <ul>
  465. <code>define KG.hz.TF.01 OWTEMP 14B598010800 300 60</code><br>
  466. </ul>
  467. <br>
  468. </ul>
  469. <a name="OWTEMPset"></a>
  470. <b>Set</b>
  471. <ul>
  472. <code>set &lt;name&gt; &lt;value&gt;</code>
  473. <br><br>
  474. where <code>value</code> is one of:<br>
  475. <ul>
  476. <li><a name="owtemp_templow"></a>
  477. <code>templow</code> (read-write)<br>
  478. The upper limit for the low temperature alarm state.
  479. </li>
  480. <li><a name="owtemp_temphigh"></a>
  481. <code>temphigh</code> (read-write)<br>
  482. The lower limit for the high temperature alarm state.
  483. </li>
  484. <li><a name="owtemp_ALARMINT"></a>
  485. <code>ALARMINT</code> (write-only)<br>
  486. Sets the alarm polling intervall in seconds to the given value.
  487. </li>
  488. <li><a name="owtemp_INTERVAL"></a>
  489. <code>INTERVAL</code> (write-only)<br>
  490. Sets the status polling intervall in seconds to the given value.
  491. </li>
  492. </ul>
  493. </ul><br>
  494. <a name="OWTEMPget"></a>
  495. <b>Get</b>
  496. <ul>
  497. <code>get &lt;name&gt; &lt;value&gt;</code>
  498. <br><br>
  499. where <code>value</code> is one of:<br>
  500. <ul>
  501. <li><a href="#owfs_address">address</a> (read-only)</li>
  502. <li><a href="#owfs_crc8">crc8</a> (read-only)</li>
  503. <li><a href="#owfs_family">family</a> (read-only)</li>
  504. <li><a href="#owfs_id">id</a> (read-only)</li>
  505. <li><a href="#owfs_locator">locator</a> (read-only)</li>
  506. <li><a href="#owfs_present">present</a> (read-only)</li>
  507. <li><a name="owtemp_temperature"></a>
  508. <code>temperature</code> (read-only)<br>
  509. Read by the chip at high resolution (~12 bits). Units are selected from
  510. the defined OWFS Device. See <a href="#owfs_temp-scale">temp-scale</a> for choices.
  511. </li>
  512. <li><a href="#owtemp_templow">templow</a> (read-write)</li>
  513. <li><a href="#owtemp_temphigh">temphigh</a> (read-write)</li>
  514. <li><a href="#owfs_type">type</a> (read-only)</li>
  515. <br>
  516. </ul>
  517. Examples:
  518. <ul>
  519. <code>get KG.hz.TF.01 type</code><br>
  520. <code>KG.hz.TF.01 type => DS18S20</code><br><br>
  521. <code>get KG.hz.TF.01 temperature</code><br>
  522. <code>KG.hz.TF.01 temperature => 38.2500 (Celsius)</code>
  523. </ul>
  524. <br>
  525. </ul>
  526. <a name="OWTEMPattr"></a>
  527. <b>Attributes</b>
  528. <ul>
  529. <li><a href="#do_not_notify">do_not_notify</a></li>
  530. <li><a href="#loglevel">loglevel</a></li>
  531. <li><a href="#showtime">showtime</a></li>
  532. <li><a href="#IODev">IODev</a></li>
  533. </ul>
  534. <br>
  535. </ul>
  536. =end html
  537. =cut