21_OWID.pm 15 KB

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