21_OWID.pm 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542
  1. ########################################################################################
  2. #
  3. # OWID.pm
  4. #
  5. # FHEM module to commmunicate with general 1-Wire ID-ROMS
  6. #
  7. # Prof. Dr. Peter A. Henning
  8. # Norbert Truchsess
  9. #
  10. # $Id: 21_OWID.pm 15339 2017-10-29 08:14:07Z phenning $
  11. #
  12. ########################################################################################
  13. #
  14. # This programm is free software; you can redistribute it and/or modify
  15. # it under the terms of the GNU General Public License as published by
  16. # the Free Software Foundation; either version 2 of the License, or
  17. # (at your option) any later version.
  18. #
  19. # The GNU General Public License can be found at
  20. # http://www.gnu.org/copyleft/gpl.html.
  21. # A copy is found in the textfile GPL.txt and important notices to the license
  22. # from the author is found in LICENSE.txt distributed with these scripts.
  23. #
  24. # This script is distributed in the hope that it will be useful,
  25. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  26. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  27. # GNU General Public License for more details.
  28. #
  29. ########################################################################################
  30. package main;
  31. use vars qw{%attr %defs %modules $readingFnAttributes $init_done};
  32. use Time::HiRes qw(gettimeofday);
  33. use strict;
  34. use warnings;
  35. #add FHEM/lib to @INC if it is not already included. Should rather be in fhem.pl than here though...
  36. BEGIN {
  37. if (!grep(/FHEM\/lib$/,@INC)) {
  38. foreach my $inc (grep(/FHEM$/,@INC)) {
  39. push @INC,$inc."/lib";
  40. };
  41. };
  42. };
  43. use GPUtils qw(:all);
  44. use ProtoThreads;
  45. no warnings 'deprecated';
  46. sub Log3($$$);
  47. my $owx_version="7.01";
  48. #-- declare variables
  49. my %gets = (
  50. "present" => ":noArg",
  51. "id" => ":noArg",
  52. "version" => ":noArg"
  53. );
  54. my %sets = (
  55. "interval" => ""
  56. );
  57. my %updates = (
  58. "present" => ""
  59. );
  60. ########################################################################################
  61. #
  62. # The following subroutines are independent of the bus interface
  63. #
  64. # Prefix = OWID
  65. #
  66. ########################################################################################
  67. #
  68. # OWID_Initialize
  69. #
  70. # Parameter hash = hash of device addressed
  71. #
  72. ########################################################################################
  73. sub OWID_Initialize ($) {
  74. my ($hash) = @_;
  75. $hash->{DefFn} = "OWID_Define";
  76. $hash->{UndefFn} = "OWID_Undef";
  77. $hash->{GetFn} = "OWID_Get";
  78. $hash->{SetFn} = "OWID_Set";
  79. $hash->{AttrFn} = "OWID_Attr";
  80. $hash->{NotifyFn} = "OWID_Notify";
  81. $hash->{InitFn} = "OWID_Init";
  82. $hash->{AttrList} = "IODev do_not_notify:0,1 showtime:0,1 model interval ".
  83. $readingFnAttributes;
  84. #--make sure OWX is loaded so OWX_CRC is available if running with OWServer
  85. main::LoadModule("OWX");
  86. }
  87. #########################################################################################
  88. #
  89. # OWID_Define - Implements DefFn function
  90. #
  91. # Parameter hash = hash of device addressed, def = definition string
  92. #
  93. #########################################################################################
  94. sub OWID_Define ($$) {
  95. my ($hash, $def) = @_;
  96. #-- define <name> OWID <FAM_ID> <ROM_ID>
  97. my @a = split("[ \t][ \t]*", $def);
  98. my ($name,$interval,$model,$fam,$id,$crc,$ret);
  99. #-- default
  100. $name = $a[0];
  101. $interval = 300;
  102. $ret = "";
  103. #-- check syntax
  104. return "OWID: Wrong syntax, must be define <name> OWID [<model>] <id> [interval] or OWAD <fam>.<id> [interval]"
  105. if(int(@a) < 2 || int(@a) > 5);
  106. #-- different types of definition allowed
  107. my $a2 = $a[2];
  108. my $a3 = defined($a[3]) ? $a[3] : "";
  109. #-- no model, 2+12 characters
  110. if( $a2 =~ m/^[0-9|a-f|A-F]{2}\.[0-9|a-f|A-F]{12}$/ ) {
  111. $fam = substr($a[2],0,2);
  112. $id = substr($a[2],3);
  113. if(int(@a)>=4) { $interval = $a[3]; }
  114. if( $fam eq "01" ){
  115. $model = "DS2401";
  116. CommandAttr (undef,"$name model DS2401");
  117. }elsif( $fam eq "09" ){
  118. $model = "DS2502";
  119. CommandAttr (undef,"$name model DS2502");
  120. }else{
  121. $model = "unknown";
  122. CommandAttr (undef,"$name model unknown");
  123. }
  124. #-- model or family id, 12 characters
  125. } elsif( $a3 =~ m/^[0-9|a-f|A-F]{12}$/ ) {
  126. $id = $a[3];
  127. if(int(@a)>=5) { $interval = $a[4]; }
  128. #-- family id, 2 characters
  129. if( $a2 =~ m/^[0-9|a-f|A-F]{2}$/ ) {
  130. $fam = $a[2];
  131. if( $fam eq "01" ){
  132. $model = "DS2401";
  133. CommandAttr (undef,"$name model DS2401");
  134. }elsif( $fam eq "09" ){
  135. $model = "DS2502";
  136. CommandAttr (undef,"$name model DS2502");
  137. }else{
  138. $model = "unknown";
  139. CommandAttr (undef,"$name model unknown");
  140. }
  141. }else{
  142. $model = $a[2];
  143. if( $model eq "DS2401" ){
  144. $fam = "01";
  145. CommandAttr (undef,"$name model DS2401");
  146. }elsif( $model eq "DS2502" ){
  147. $fam = "09";
  148. CommandAttr (undef,"$name model DS2502");
  149. }else{
  150. return "OWID: Unknown 1-Wire device model $model";
  151. }
  152. }
  153. } else {
  154. return "OWID: $a[0] ID $a[2] invalid, specify a 12 or 2.12 digit value";
  155. }
  156. #-- determine CRC Code
  157. $crc = sprintf("%02X",OWX_CRC($fam.".".$id."00"));
  158. #-- Define device internals
  159. $hash->{ROM_ID} = "$fam.$id.$crc";
  160. $hash->{OW_ID} = $id;
  161. $hash->{OW_FAMILY} = $fam;
  162. $hash->{PRESENT} = 0;
  163. $hash->{INTERVAL} = $interval;
  164. #-- Couple to I/O device
  165. AssignIoPort($hash);
  166. if( !defined($hash->{IODev}) or !defined($hash->{IODev}->{NAME}) ){
  167. return "OWID: Warning, no 1-Wire I/O device found for $name.";
  168. } else {
  169. $hash->{ASYNC} = $hash->{IODev}->{TYPE} eq "OWX_ASYNC" ? 1 : 0; #-- false for now
  170. }
  171. $modules{OWID}{defptr}{$id} = $hash;
  172. #--
  173. readingsSingleUpdate($hash,"state","Defined",1);
  174. Log3 $name,1, "OWID: Device $name defined.";
  175. $hash->{NOTIFYDEV} = "global";
  176. return OWID_Init($hash);
  177. }
  178. #########################################################################################
  179. #
  180. # OWID_Notify - Implements NotifyFn function
  181. #
  182. # Parameter hash = hash of device addressed, dev = device name
  183. #
  184. #########################################################################################
  185. sub OWID_Notify ($$) {
  186. my ($hash,$dev) = @_;
  187. if( grep(m/^(INITIALIZED|REREADCFG)$/, @{$dev->{CHANGED}}) ) {
  188. OWID_Init($hash);
  189. } elsif( grep(m/^SAVE$/, @{$dev->{CHANGED}}) ) {
  190. }
  191. }
  192. #########################################################################################
  193. #
  194. # OWID_Init - Implements InitFn function
  195. #
  196. # Parameter hash = hash of device addressed
  197. #
  198. #########################################################################################
  199. sub OWID_Init ($) {
  200. my ($hash)=@_;
  201. #-- Start timer for updates
  202. RemoveInternalTimer($hash);
  203. InternalTimer(gettimeofday()+30, "OWID_GetValues", $hash, 0);
  204. #--
  205. readingsSingleUpdate($hash,"state","Initialized",1);
  206. if (! (defined AttrVal($hash->{NAME},"stateFormat",undef))) {
  207. $main::attr{$hash->{NAME}}{"stateFormat"} = "{ReadingsVal(\$name,\"present\",0) ? \"present\" : \"not present\"}";
  208. }
  209. return undef;
  210. }
  211. #######################################################################################
  212. #
  213. # OWID_Attr - Set one attribute value for device
  214. #
  215. # Parameter hash = hash of device addressed
  216. # a = argument array
  217. #
  218. ########################################################################################
  219. sub OWID_Attr(@) {
  220. my ($do,$name,$key,$value) = @_;
  221. my $hash = $defs{$name};
  222. my $ret;
  223. if ( $do eq "set") {
  224. ARGUMENT_HANDLER: {
  225. #-- interval modified at runtime
  226. $key eq "interval" and do {
  227. #-- check value
  228. return "OWID: set $name interval must be >= 0" if(int($value) < 0);
  229. #-- update timer
  230. $hash->{INTERVAL} = int($value);
  231. if ($init_done) {
  232. RemoveInternalTimer($hash);
  233. InternalTimer(gettimeofday()+$hash->{INTERVAL}, "OWID_GetValues", $hash, 0);
  234. }
  235. last;
  236. };
  237. $key eq "IODev" and do {
  238. AssignIoPort($hash,$value);
  239. if( defined($hash->{IODev}) ) {
  240. $hash->{ASYNC} = $hash->{IODev}->{TYPE} eq "OWX_ASYNC" ? 1 : 0;
  241. if ($init_done) {
  242. OWID_Init($hash);
  243. }
  244. }
  245. last;
  246. }
  247. }
  248. }
  249. return $ret;
  250. }
  251. ########################################################################################
  252. #
  253. # OWID_Get - Implements GetFn function
  254. #
  255. # Parameter hash = hash of device addressed, a = argument array
  256. #
  257. ########################################################################################
  258. sub OWID_Get($@) {
  259. my ($hash, @a) = @_;
  260. my $reading = $a[1];
  261. my $name = $hash->{NAME};
  262. my $model = $hash->{OW_MODEL};
  263. my $value = undef;
  264. my $ret = "";
  265. my $offset;
  266. my $factor;
  267. #-- check syntax
  268. return "OWID: Get argument is missing @a"
  269. if(int(@a) != 2);
  270. #-- check argument
  271. my $msg = "OWID: Get with unknown argument $a[1], choose one of ";
  272. $msg .= "$_$gets{$_} " foreach (keys%gets);
  273. return $msg
  274. if(!defined($gets{$a[1]}));
  275. #-- get id
  276. if($a[1] eq "id") {
  277. $value = $hash->{ROM_ID};
  278. return "$name.id => $value";
  279. }
  280. #-- get present
  281. if($a[1] eq "present") {
  282. #-- hash of the busmaster
  283. my $master = $hash->{IODev};
  284. my $interface = $master->{TYPE};
  285. #-- OWX interface
  286. if( $interface eq "OWX" ){
  287. $value = OWX_Verify($master,$name,$hash->{ROM_ID},0);
  288. #-- OWX_ASYNC interface
  289. }elsif( $interface eq "OWX_ASYNC" ){
  290. eval {
  291. OWX_ASYNC_RunToCompletion($hash,OWX_ASYNC_PT_Verify($hash));
  292. };
  293. return GP_Catch($@) if $@;
  294. #-- Unknown interface
  295. } else {
  296. return "OWID: Verification not yet implemented for interface $interface";
  297. }
  298. #-- process results
  299. if( $master->{ASYNCHRONOUS} ){
  300. return undef;
  301. }else{
  302. #-- generate an event only if presence has changed
  303. if( $value == 0 ){
  304. readingsSingleUpdate($hash,"present",0,$hash->{PRESENT});
  305. } else {
  306. readingsSingleUpdate($hash,"present",1,!$hash->{PRESENT});
  307. }
  308. $hash->{PRESENT} = $value;
  309. return "$name.present => $value";
  310. }
  311. }
  312. #-- get version
  313. if( $a[1] eq "version") {
  314. return "$name.version => $owx_version";
  315. }
  316. }
  317. ########################################################################################
  318. #
  319. # OWID_GetValues - Updates the reading from one device
  320. #
  321. # Parameter hash = hash of device addressed
  322. #
  323. ########################################################################################
  324. sub OWID_GetValues($) {
  325. my $hash = shift;
  326. my $name = $hash->{NAME};
  327. my $value = 0;
  328. my $ret = "";
  329. my $offset;
  330. my $factor;
  331. RemoveInternalTimer($hash);
  332. #-- auto-update for device disabled;
  333. return undef
  334. if( $hash->{INTERVAL} == 0 );
  335. #-- restart timer for updates
  336. InternalTimer(time()+$hash->{INTERVAL}, "OWID_GetValues", $hash, 0);
  337. #-- hash of the busmaster
  338. my $master = $hash->{IODev};
  339. my $interface = $master->{TYPE};
  340. #-- OWX interface
  341. if( $interface eq "OWX" ){
  342. $value = OWX_Verify($master,$name,$hash->{ROM_ID},0);
  343. #-- OWX_ASYNC interface
  344. }elsif( $interface eq "OWX_ASYNC" ){
  345. eval {
  346. OWX_ASYNC_RunToCompletion($hash,OWX_ASYNC_PT_Verify($hash));
  347. };
  348. return GP_Catch($@) if $@;
  349. }
  350. #-- process results
  351. if( $master->{ASYNCHRONOUS} ){
  352. return undef;
  353. }else{
  354. #-- generate an event only if presence has changed
  355. if( $value == 0 ){
  356. readingsSingleUpdate($hash,"present",0,$hash->{PRESENT});
  357. } else {
  358. readingsSingleUpdate($hash,"present",1,!$hash->{PRESENT});
  359. }
  360. $hash->{PRESENT} = $value;
  361. return "$name.present => $value";
  362. }
  363. }
  364. #######################################################################################
  365. #
  366. # OWID_Set - Set one value for device
  367. #
  368. # Parameter hash = hash of device addressed
  369. # a = argument array
  370. #
  371. ########################################################################################
  372. sub OWID_Set($@) {
  373. my ($hash, @a) = @_;
  374. my $key = $a[1];
  375. my $value = $a[2];
  376. #-- for the selector: which values are possible
  377. if (@a == 2){
  378. my $newkeys = join(" ", keys %sets);
  379. return $newkeys ;
  380. }
  381. #-- check syntax
  382. return "OWID: Set needs at least one parameter"
  383. if( int(@a)<3 );
  384. #-- check argument
  385. if( !defined($sets{$a[1]}) ){
  386. return "OWID: Set with unknown argument $a[1]";
  387. }
  388. my $name = $hash->{NAME};
  389. #-- set new timer interval
  390. if($key eq "interval") {
  391. # check value
  392. return "OWID: Set $name interval must be >= 0"
  393. if(int($value) < 0);
  394. # update timer
  395. $hash->{INTERVAL} = int($value);
  396. RemoveInternalTimer($hash);
  397. InternalTimer(gettimeofday()+$hash->{INTERVAL}, "OWID_GetValues", $hash, 0);
  398. return undef;
  399. }
  400. }
  401. ########################################################################################
  402. #
  403. # OWID_Undef - Implements UndefFn function
  404. #
  405. # Parameter hash = hash of device addressed
  406. #
  407. ########################################################################################
  408. sub OWID_Undef ($) {
  409. my ($hash) = @_;
  410. delete($modules{OWID}{defptr}{$hash->{OW_ID}});
  411. RemoveInternalTimer($hash);
  412. return undef;
  413. }
  414. 1;
  415. =pod
  416. =item device
  417. =item summary to control 1-Wire devices having only a serial number
  418. =begin html
  419. <a name="OWID"></a>
  420. <h3>OWID</h3>
  421. <p>FHEM module for 1-Wire devices that know only their unique ROM ID<br />
  422. <br />This 1-Wire module works with the OWX interface module or with the OWServer interface module
  423. Please define an <a href="#OWX">OWX</a> device or <a href="#OWServer">OWServer</a> device first. <br /></p>
  424. <br /><h4>Example</h4><br />
  425. <p>
  426. <code>define ROM1 OWX_ID OWCOUNT 09.CE780F000000 10</code>
  427. <br />
  428. </p><br />
  429. <a name="OWIDdefine"></a>
  430. <h4>Define</h4>
  431. <p>
  432. <code>define &lt;name&gt; OWID &lt;fam&gt; &lt;id&gt; [&lt;interval&gt;]</code> or <br/>
  433. <code>define &lt;name&gt; OWID &lt;fam&gt;.&lt;id&gt; [&lt;interval&gt;]</code>
  434. <br /><br /> Define a 1-Wire device.<br /><br />
  435. </p>
  436. <ul>
  437. <li>
  438. <code>&lt;fam&gt;</code>
  439. <br />2-character unique family id, see above
  440. </li>
  441. <li>
  442. <code>&lt;id&gt;</code>
  443. <br />12-character unique ROM id of the converter device without family id and CRC
  444. code
  445. </li>
  446. <li>
  447. <code>&lt;interval&gt;</code>
  448. <br />Interval in seconds for checking the presence of the device. The default is 300 seconds. </li>
  449. </ul>
  450. <br />
  451. <a name="OWIDset"></a>
  452. <h4>Set</h4>
  453. <ul>
  454. <li><a name="owid_interval">
  455. <code>set &lt;name&gt; interval &lt;int&gt;</code></a><br />
  456. Interval in seconds for checking the presence of the device. The default is 300 seconds. </li>
  457. </ul>
  458. <br />
  459. <a name="OWIDget"></a>
  460. <h4>Get</h4>
  461. <ul>
  462. <li><a name="owid_id">
  463. <code>get &lt;name&gt; id</code></a>
  464. <br /> Returns the full 1-Wire device id OW_FAMILY.ROM_ID.CRC </li>
  465. <li><a name="owid_present">
  466. <code>get &lt;name&gt; present</code>
  467. </a>
  468. <br /> Returns 1 if this 1-Wire device is present, otherwise 0. </li>
  469. </ul>
  470. <h4>Attributes</h4>
  471. <ul><li><a name="owtherm_interval2">
  472. <code>attr &lt;name&gt; interval &lt;int&gt;</code></a><br /> Measurement
  473. interval in seconds. The default is 300 seconds, a value of 0 disables the automatic update.</li>
  474. <li><a href="#readingFnAttributes">readingFnAttributes</a></li>
  475. </ul>
  476. =end html
  477. =cut