21_OWVAR.pm 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981
  1. ########################################################################################
  2. #
  3. # OWVAR.pm
  4. #
  5. # FHEM module to commmunicate with 1-Wire variable resistor DS2890
  6. #
  7. # Prof. Dr. Peter A. Henning
  8. #
  9. # $Id: 21_OWVAR.pm 11131 2016-03-27 09:16:56Z pahenning $
  10. #
  11. ########################################################################################
  12. #
  13. # define <name> OWVAR [<model>] <ROM_ID> or <FAM_ID>.<ROM_ID>
  14. #
  15. # where <name> may be replaced by any name string
  16. #
  17. # <model> is a 1-Wire device type. If omitted AND no FAM_ID given we assume this to be an
  18. # DS2890 variable resistor
  19. # <FAM_ID> is a 1-Wire family id, currently allowed values are 2C
  20. # <ROM_ID> is a 12 character (6 byte) 1-Wire ROM ID
  21. # without Family ID, e.g. A2D90D000800
  22. #
  23. # get <name> id => FAM_ID.ROM_ID.CRC
  24. # get <name> present => 1 if device present, 0 if not
  25. # get <name> value => query value
  26. # get <name> version => OWX version number
  27. # set <name> value => wiper setting
  28. #
  29. # Additional attributes are defined in fhem.cfg
  30. # attr <name> Name <string>[|<string>] = name for the channel [|name used in state reading]
  31. # attr <name> Unit <string>[|<string>] = unit of measurement for this channel [|unit used in state reading]
  32. # attr <name> Function <string>|<string> = The first string is an arbitrary functional expression f(V) involving the variable V.
  33. # V is replaced by the raw potentiometer reading (in the range of [0,100]). The second string must be the inverse
  34. # function g(U) involving the variable U, such that U can be replaced by the value given in the
  35. # set argument. Care has to taken that g(U) is in the range [0,100].
  36. # No check on the validity of these functions is performed,
  37. # singularities my crash FHEM.
  38. #
  39. ########################################################################################
  40. #
  41. # This programm is free software; you can redistribute it and/or modify
  42. # it under the terms of the GNU General Public License as published by
  43. # the Free Software Foundation; either version 2 of the License, or
  44. # (at your option) any later version.
  45. #
  46. # The GNU General Public License can be found at
  47. # http://www.gnu.org/copyleft/gpl.html.
  48. # A copy is found in the textfile GPL.txt and important notices to the license
  49. # from the author is found in LICENSE.txt distributed with these scripts.
  50. #
  51. # This script is distributed in the hope that it will be useful,
  52. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  53. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  54. # GNU General Public License for more details.
  55. #
  56. ########################################################################################
  57. package main;
  58. use vars qw{%attr %defs %modules $readingFnAttributes $init_done};
  59. use strict;
  60. use warnings;
  61. use Time::HiRes qw( gettimeofday );
  62. #add FHEM/lib to @INC if it's not allready included. Should rather be in fhem.pl than here though...
  63. BEGIN {
  64. if (!grep(/FHEM\/lib$/,@INC)) {
  65. foreach my $inc (grep(/FHEM$/,@INC)) {
  66. push @INC,$inc."/lib";
  67. };
  68. };
  69. };
  70. use ProtoThreads;
  71. no warnings 'deprecated';
  72. sub Log3($$$);
  73. sub AttrVal($$$);
  74. my $owx_version="6.0";
  75. my $owg_channel = "";
  76. my %gets = (
  77. "id" => "",
  78. "present" => "",
  79. "value" => "",
  80. "version" => ""
  81. );
  82. my %sets = (
  83. "value" => ""
  84. );
  85. my %updates = (
  86. "value" => ""
  87. );
  88. ########################################################################################
  89. #
  90. # The following subroutines are independent of the bus interface
  91. #
  92. # Prefix = OWVAR
  93. #
  94. ########################################################################################
  95. #
  96. # OWVAR_Initialize
  97. #
  98. # Parameter hash = hash of device addressed
  99. #
  100. ########################################################################################
  101. sub OWVAR_Initialize ($) {
  102. my ($hash) = @_;
  103. $hash->{DefFn} = "OWVAR_Define";
  104. $hash->{UndefFn} = "OWVAR_Undef";
  105. $hash->{GetFn} = "OWVAR_Get";
  106. $hash->{SetFn} = "OWVAR_Set";
  107. $hash->{NotifyFn}= "OWVAR_Notify";
  108. $hash->{InitFn} = "OWVAR_Init";
  109. $hash->{AttrFn} = "OWVAR_Attr";
  110. $hash->{AttrList}= "IODev model:DS2890 ".
  111. "Name Function Unit ".
  112. $readingFnAttributes;
  113. #-- make sure OWX is loaded so OWX_CRC is available if running with OWServer
  114. main::LoadModule("OWX");
  115. }
  116. ########################################################################################
  117. #
  118. # OWVAR_Define - Implements DefFn function
  119. #
  120. # Parameter hash = hash of device addressed, def = definition string
  121. #
  122. ########################################################################################
  123. sub OWVAR_Define ($$) {
  124. my ($hash, $def) = @_;
  125. # define <name> OWVAR [<model>] <id>
  126. # e.g.: define flow OWVAR 525715020000
  127. my @a = split("[ \t][ \t]*", $def);
  128. my ($name,$model,$fam,$id,$crc,$ret);
  129. #-- default
  130. $name = $a[0];
  131. $ret = "";
  132. #-- check syntax
  133. return "OWVAR: Wrong syntax, must be define <name> OWVAR [<model>] <id> or OWVAR <fam>.<id> "
  134. if(int(@a) < 2 || int(@a) > 5);
  135. #-- different types of definition allowed
  136. my $a2 = $a[2];
  137. my $a3 = defined($a[3]) ? $a[3] : "";
  138. #-- no model, 12 characters
  139. if( $a2 =~ m/^[0-9|a-f|A-F]{12}$/ ) {
  140. $model = "DS2890";
  141. CommandAttr (undef,"$name model DS2890");
  142. $fam = "10";
  143. $id = $a[2];
  144. #-- no model, 2+12 characters
  145. } elsif( $a2 =~ m/^[0-9|a-f|A-F]{2}\.[0-9|a-f|A-F]{12}$/ ) {
  146. $fam = substr($a[2],0,2);
  147. $id = substr($a[2],3);
  148. if( $fam eq "2C" ){
  149. $model = "DS2890";
  150. CommandAttr (undef,"$name model DS2890");
  151. }else{
  152. return "OWVAR: Wrong 1-Wire device family $fam";
  153. }
  154. #-- model, 12 characters
  155. } elsif( $a3 =~ m/^[0-9|a-f|A-F]{12}$/ ) {
  156. $model = $a[2];
  157. $id = $a[3];
  158. if( $model eq "DS2890" ){
  159. $fam = "2C";
  160. CommandAttr (undef,"$name model DS2890");
  161. }else{
  162. return "OWVAR: Wrong 1-Wire device model $model";
  163. }
  164. } else {
  165. return "OWVAR: $a[0] ID $a[2] invalid, specify a 12 or 2.12 digit value";
  166. }
  167. #-- determine CRC Code
  168. $crc = sprintf("%02X",OWX_CRC($fam.".".$id."00"));
  169. #-- define device internals
  170. $hash->{OW_ID} = $id;
  171. $hash->{OW_FAMILY} = $fam;
  172. $hash->{PRESENT} = 0;
  173. $hash->{ROM_ID} = "$fam.$id.$crc";
  174. #-- value globals - always the raw values from/for the device
  175. $hash->{owg_val} = "";
  176. #-- Couple to I/O device
  177. AssignIoPort($hash);
  178. if( !defined($hash->{IODev}) or !defined($hash->{IODev}->{NAME}) ){
  179. return "OWVAR: Warning, no 1-Wire I/O device found for $name.";
  180. } else {
  181. $hash->{ASYNC} = $hash->{IODev}->{TYPE} eq "OWX_ASYNC" ? 1 : 0; #-- false for now
  182. }
  183. $modules{OWVAR}{defptr}{$id} = $hash;
  184. #--
  185. readingsSingleUpdate($hash,"state","defined",1);
  186. Log3 $name, 3, "OWVAR: Device $name defined.";
  187. $hash->{NOTIFYDEV} = "global";
  188. if ($init_done) {
  189. OWVAR_Init($hash);
  190. }
  191. return undef;
  192. }
  193. #######################################################################################
  194. #
  195. # OWVAR_Notify - Implements the Notify function
  196. #
  197. # Parameter hash = hash of device addressed
  198. # a = argument array
  199. #
  200. ########################################################################################
  201. sub OWVAR_Notify ($$) {
  202. my ($hash,$dev) = @_;
  203. if( grep(m/^(INITIALIZED|REREADCFG)$/, @{$dev->{CHANGED}}) ) {
  204. OWVAR_Init($hash);
  205. } elsif( grep(m/^SAVE$/, @{$dev->{CHANGED}}) ) {
  206. }
  207. }
  208. #######################################################################################
  209. #
  210. # OWVAR_Init - Implements the Init function
  211. #
  212. # Parameter hash = hash of device addressed
  213. #
  214. ########################################################################################
  215. sub OWVAR_Init ($) {
  216. my ($hash)=@_;
  217. #-- Start timer for updates
  218. RemoveInternalTimer($hash);
  219. InternalTimer(gettimeofday()+10, "OWVAR_GetValues", $hash, 0);
  220. return undef;
  221. }
  222. #######################################################################################
  223. #
  224. # OWVAR_Attr - Set one attribute value for device
  225. #
  226. # Parameter hash = hash of device addressed
  227. # a = argument array
  228. #
  229. ########################################################################################
  230. sub OWVAR_Attr(@) {
  231. my ($do,$name,$key,$value) = @_;
  232. my $hash = $defs{$name};
  233. my $ret;
  234. if ( $do eq "set") {
  235. ARGUMENT_HANDLER: {
  236. #-- interval modified at runtime
  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. OWVAR_Init($hash);
  243. }
  244. }
  245. last;
  246. };
  247. }
  248. }
  249. return $ret;
  250. }
  251. ########################################################################################
  252. #
  253. # OWVAR_ChannelNames - find the real channel names
  254. #
  255. # Parameter hash = hash of device addressed
  256. #
  257. ########################################################################################
  258. sub OWVAR_ChannelNames($) {
  259. my ($hash) = @_;
  260. my $name = $hash->{NAME};
  261. my $state = $hash->{READINGS}{"state"}{VAL};
  262. my ($cname,@cnama,$unit);
  263. #-- name
  264. $cname = defined($attr{$name}{"Name"}) ? $attr{$name}{"Name"} : "value";
  265. @cnama = split(/\|/,$cname);
  266. if( int(@cnama)!=2){
  267. push(@cnama,$cnama[0]);
  268. }
  269. $owg_channel=$cnama[0];
  270. #-- unit
  271. $unit = defined($attr{$name}{"Unit"}) ? $attr{$name}{"Unit"} : "\%";
  272. #-- put into readings
  273. $hash->{READINGS}{"value"}{ABBR} = $cnama[1];
  274. $hash->{READINGS}{"value"}{UNIT} = " ".$unit;
  275. }
  276. ########################################################################################
  277. #
  278. # OWVAR_FormatValues - put together various format strings
  279. #
  280. # Parameter hash = hash of device addressed, fs = format string
  281. #
  282. ########################################################################################
  283. sub OWVAR_FormatValues($) {
  284. my ($hash) = @_;
  285. my $name = $hash->{NAME};
  286. my $interface = $hash->{IODev}->{TYPE};
  287. my ($vval,$vlow,$vhigh,$vfunc,$ufunc,$ret);
  288. #-- no change in any value if invalid reading
  289. #for (my $i=0;$i<int(@owg_fixed);$i++){
  290. # return "" if( (!defined($hash->{owg_val})) || ($hash->{owg_val} eq "") );
  291. #}
  292. #-- obtain channel names
  293. OWVAR_ChannelNames($hash);
  294. #-- put into READINGS
  295. readingsBeginUpdate($hash);
  296. #-- formats for output
  297. if (defined($attr{$name}{"Function"})){
  298. ($vfunc,$ufunc) = split('\|',$attr{$name}{"Function"});
  299. #-- replace by proper values (V -> value)
  300. $vfunc =~ s/V/\$hash->{owg_val}/g;
  301. $vfunc = eval($vfunc);
  302. if( !$vfunc ){
  303. $vval = 0.0;
  304. } elsif( $vfunc ne "" ){
  305. $vval = $vfunc;
  306. } else {
  307. $vval = "???";
  308. }
  309. }else{
  310. $vval = $hash->{owg_val};
  311. }
  312. #-- string buildup for return value, STATE and alarm
  313. my $svalue .= sprintf( "%s: %5.3f%s", $hash->{READINGS}{"value"}{ABBR}, $vval,$hash->{READINGS}{"value"}{UNIT});
  314. #-- put into READINGS
  315. $vval = sprintf( "%5.3f", $vval);
  316. readingsBulkUpdate($hash,$owg_channel,$vval);
  317. #-- STATE
  318. readingsBulkUpdate($hash,"state",$svalue);
  319. readingsEndUpdate($hash,1);
  320. return $svalue;
  321. }
  322. ########################################################################################
  323. #
  324. # OWVAR_Get - Implements GetFn function
  325. #
  326. # Parameter hash = hash of device addressed, a = argument array
  327. #
  328. ########################################################################################
  329. sub OWVAR_Get($@) {
  330. my ($hash, @a) = @_;
  331. my $reading = $a[1];
  332. my $name = $hash->{NAME};
  333. my $model = $hash->{OW_MODEL};
  334. my $value = undef;
  335. my $ret = "";
  336. #-- check syntax
  337. return "OWVAR: Get argument is missing @a"
  338. if(int(@a) < 2);
  339. #-- check argument
  340. return "OWVAR: Get with unknown argument $a[1], choose one of ".join(" ", sort keys %gets)
  341. if(!defined($gets{$a[1]}));
  342. #-- get id
  343. if($a[1] eq "id") {
  344. $value = $hash->{ROM_ID};
  345. return "$name.id => $value";
  346. }
  347. #-- hash of the busmaster
  348. my $master = $hash->{IODev};
  349. #-- Get other values according to interface type
  350. my $interface= $hash->{IODev}->{TYPE};
  351. #-- get present
  352. if($a[1] eq "present" ) {
  353. #-- OWX interface
  354. if( $interface =~ /^OWX/ ){
  355. #-- asynchronous mode
  356. if( $hash->{ASYNC} ){
  357. Log3 $name, 1,"OWVAR: Get ASYNC interface not implemented";
  358. #eval {
  359. # OWX_ASYNC_RunToCompletion($hash,OWX_ASYNC_PT_Verify($hash));
  360. #};
  361. #return GP_Catch($@) if $@;
  362. return "$name.present => ".ReadingsVal($name,"present","unknown");
  363. } else {
  364. $value = OWX_Verify($master,$hash->{ROM_ID});
  365. }
  366. $hash->{PRESENT} = $value;
  367. return "$name.present => $value";
  368. } else {
  369. return "OWVAR: Verification not yet implemented for interface $interface";
  370. }
  371. }
  372. #-- get version
  373. if( $a[1] eq "version") {
  374. return "$name.version => $owx_version";
  375. }
  376. if( $a[1] eq "value") {
  377. #-- OWX interface
  378. if( $interface eq "OWX" ){
  379. #-- not different from getting all values ..
  380. $ret = OWXVAR_GetValues($hash);
  381. }elsif( $interface eq "OWX_ASYNC" ){
  382. Log3 $name,1,"OWVAR: Get ASYNC interface not implemented";
  383. #eval {
  384. # $ret = OWX_ASYNC_RunToCompletion($hash,OWXVAR_PT_GetValues($hash));
  385. #};
  386. #$ret = GP_Catch($@) if $@;
  387. #-- OWFS interface
  388. }elsif( $interface eq "OWServer" ){
  389. $ret = OWFSVAR_GetValues($hash);
  390. #-- Unknown interface
  391. }else{
  392. return "OWVAR: Get with wrong IODev type $interface";
  393. }
  394. }
  395. #-- process results
  396. if( $master->{ASYNCHRONOUS} ){
  397. return "OWVAR: $name getting value, please wait for completion";
  398. }else{
  399. #-- when we have a return code, we have an error
  400. if( defined($ret) ){
  401. return "OWVAR: Could not get values from device $name, return was $ret";
  402. }
  403. #-- return the special reading
  404. if ($reading eq "value") {
  405. return "OWVAR: $name.value => ".
  406. $hash->{READINGS}{"value"}{VAL};
  407. } else {
  408. return undef;
  409. }
  410. }
  411. }
  412. #######################################################################################
  413. #
  414. # OWVAR_GetValues - Updates the readings from device
  415. #
  416. # Parameter hash = hash of device addressed
  417. #
  418. ########################################################################################
  419. sub OWVAR_GetValues($@) {
  420. my $hash = shift;
  421. my $name = $hash->{NAME};
  422. my $value = "";
  423. my $ret;
  424. #-- check if device needs to be initialized
  425. if( $hash->{READINGS}{"state"}{VAL} eq "defined"){
  426. OWVAR_InitializeDevice($hash);
  427. OWVAR_FormatValues($hash);
  428. }
  429. #-- restart timer for updates
  430. RemoveInternalTimer($hash);
  431. #InternalTimer(time()+$hash->{INTERVAL}, "OWVAR_GetValues", $hash, 0);
  432. #-- Get values according to interface type
  433. my $interface= $hash->{IODev}->{TYPE};
  434. if( $interface eq "OWX" ){
  435. #-- max 3 tries
  436. for(my $try=0; $try<3; $try++){
  437. $ret = OWXVAR_GetValues($hash);
  438. last
  439. if( !defined($ret) );
  440. }
  441. }elsif( $interface eq "OWX_ASYNC" ){
  442. Log3 $name, 1,"OWVAR: Get ASYNC interface not implemented";
  443. }elsif( $interface eq "OWServer" ){
  444. $ret = OWFSVAR_GetValues($hash);
  445. }else{
  446. Log3 $name, 3, "OWVAR: GetValues with wrong IODev type $interface";
  447. return 1;
  448. }
  449. #-- process results
  450. if( defined($ret) ){
  451. $hash->{ERRCOUNT}=$hash->{ERRCOUNT}+1;
  452. if( $hash->{ERRCOUNT} > 5 ){
  453. $hash->{INTERVAL} = 9999;
  454. }
  455. return "OWVAR: Could not get values from device $name for ".$hash->{ERRCOUNT}." times, reason $ret";
  456. }
  457. return undef;
  458. }
  459. ########################################################################################
  460. #
  461. # OWVAR_InitializeDevice - delayed setting of initial readings
  462. #
  463. # Parameter hash = hash of device addressed
  464. #
  465. ########################################################################################
  466. sub OWVAR_InitializeDevice($) {
  467. my ($hash) = @_;
  468. my $name = $hash->{NAME};
  469. my $interface = $hash->{IODev}->{TYPE};
  470. my $ret="";
  471. my ($ret1,$ret2);
  472. #-- Initial readings
  473. $hash->{owg_val} = "0.0";
  474. $hash->{ERRCOUNT} = 0;
  475. #-- Set state to initialized
  476. readingsSingleUpdate($hash,"state","initialized",1);
  477. return OWVAR_GetValues($hash);
  478. }
  479. #######################################################################################
  480. #
  481. # OWVAR_Set - Set one value for device
  482. #
  483. # Parameter hash = hash of device addressed
  484. # a = argument string
  485. #
  486. ########################################################################################
  487. sub OWVAR_Set($@) {
  488. my ($hash, @a) = @_;
  489. #-- for the selector: which values are possible
  490. return join(" ", sort keys %sets) if(@a == 2);
  491. #-- check syntax
  492. return "OWVAR: Set needs one parameter"
  493. if(int(@a) != 3);
  494. #-- check argument
  495. return "OWVAR: Set with unknown argument $a[1], choose one of ".join(",", sort keys %sets)
  496. if(!defined($sets{$a[1]}));
  497. #-- define vars
  498. my $key = $a[1];
  499. my $value = $a[2];
  500. my $ret = undef;
  501. my($vfunc,$ufunc);
  502. my $name = $hash->{NAME};
  503. my $model = $hash->{OW_MODEL};
  504. my $interface = $hash->{IODev}->{TYPE};
  505. #-- formats for input
  506. if (defined($attr{$name}{"Function"})){
  507. ($vfunc,$ufunc) = split('\|',$attr{$name}{"Function"});
  508. #-- replace by proper values (U -> )
  509. $ufunc =~ s/U/\$value/g;
  510. $ufunc = eval($ufunc);
  511. if( !$ufunc ){
  512. $value = 0.0;
  513. } elsif( $ufunc ne "" ){
  514. $value = $ufunc;
  515. } else {
  516. $value = "???";
  517. }
  518. }
  519. #-- put into device
  520. #-- OWX interface
  521. if( $interface eq "OWX" ){
  522. $ret = OWXVAR_SetValues($hash,$key,$value);
  523. }elsif( $interface eq "OWX_ASYNC" ){
  524. Log3 $name, 1,"OWVAR: Set ASYNC interface not implemented";
  525. #-- OWFS interface
  526. }elsif( $interface eq "OWServer" ){
  527. $ret = OWFSVAR_SetValues($hash,$key,$value);
  528. } else {
  529. return "OWVAR: Set with wrong IODev type $interface";
  530. }
  531. #-- process results
  532. if( defined($ret) ){
  533. return "OWVAR: Could not set device $name, reason: ".$ret;
  534. }
  535. #-- process results
  536. $hash->{PRESENT} = 1;
  537. OWVAR_FormatValues($hash);
  538. Log3 $name, 4, "OWVAR: Set $hash->{NAME} $key $value";
  539. return undef;
  540. }
  541. ########################################################################################
  542. #
  543. # OWVAR_Undef - Implements UndefFn function
  544. #
  545. # Parameter hash = hash of device addressed
  546. #
  547. ########################################################################################
  548. sub OWVAR_Undef ($) {
  549. my ($hash) = @_;
  550. delete($modules{OWVAR}{defptr}{$hash->{OW_ID}});
  551. RemoveInternalTimer($hash);
  552. return undef;
  553. }
  554. ########################################################################################
  555. #
  556. # The following subroutines in alphabetical order are only for a 1-Wire bus connected
  557. # via OWFS
  558. #
  559. # Prefix = OWFSVAR
  560. #
  561. ########################################################################################
  562. #
  563. # OWFSVAR_GetValues - Get values from device
  564. #
  565. # Parameter hash = hash of device addressed
  566. #
  567. ########################################################################################
  568. sub OWFSVAR_GetValues($) {
  569. my ($hash) = @_;
  570. #-- ID of the device
  571. my $owx_add = substr($hash->{ROM_ID},0,15);
  572. #-- hash of the busmaster
  573. my $master = $hash->{IODev};
  574. my $name = $hash->{NAME};
  575. #-- reset presence
  576. $hash->{PRESENT} = 0;
  577. #-- get values - or should we rather get the uncached ones ?
  578. Log 1, "OWVAR: trying to read from OWserver /$owx_add/wiper";
  579. my $val = OWServer_Read($master,"/$owx_add/wiper");
  580. return "no return from OWServer"
  581. if( !defined($val) );
  582. return "empty return from OWServer"
  583. if( $val eq "" );
  584. $hash->{owg_val}=sprintf("%5.2f",(1.0-$val/255.0)*100);
  585. #-- and now from raw to formatted values
  586. $hash->{PRESENT} = 1;
  587. my $value = OWVAR_FormatValues($hash);
  588. return undef;
  589. }
  590. ########################################################################################
  591. #
  592. # OWFSVAR_SetValues - Set values in device
  593. #
  594. # Parameter hash = hash of device addressed
  595. #
  596. ########################################################################################
  597. sub OWFSVAR_SetValues($$$) {
  598. my ($hash,$key,$value) = @_;
  599. #-- ID of the device
  600. my $owx_add = substr($hash->{ROM_ID},0,15);
  601. #-- hash of the busmaster
  602. my $master = $hash->{IODev};
  603. my $name = $hash->{NAME};
  604. #-- translate from 0..100 to 0..255
  605. return sprintf("OWFSVAR: Set with wrong value $value for $key, range is [%3.1f,%3.1f]",0,100)
  606. if($value < 0 || $value > 100);
  607. my $pos = floor((100-$value)*2.55+0.5);
  608. Log 1,"OWVAR: trying to write to OWserver /$owx_add/wiper => $pos";
  609. OWServer_Write($master, "/$owx_add/wiper/",$pos);
  610. return undef
  611. }
  612. ########################################################################################
  613. #
  614. # The following subroutines in alphabetical order are only for a 1-Wire bus connected
  615. # directly to the FHEM server
  616. #
  617. # Prefix = OWXVAR
  618. #
  619. ########################################################################################
  620. #
  621. # OWXVAR_BinValues - Process reading from one device - translate binary into raw
  622. #
  623. # Parameter hash = hash of device addressed
  624. # context = mode for evaluating the binary data
  625. # proc = processing instruction, also passed to OWX_Read.
  626. # bitwise interpretation !!
  627. # if 0, nothing special
  628. # if 1 = bit 0, a reset will be performed not only before, but also after
  629. # the last operation in OWX_Read
  630. # if 2 = bit 1, the initial reset of the bus will be suppressed
  631. # if 8 = bit 3, the fillup of the data with 0xff will be suppressed
  632. # if 16= bit 4, the insertion will be at the top of the queue
  633. # owx_dev = ROM ID of slave device
  634. # crcpart = part of the data that needs to be part of the CRC check
  635. # numread = number of bytes to receive
  636. # res = result string
  637. #
  638. #
  639. ########################################################################################
  640. sub OWXVAR_BinValues($$$$$$$) {
  641. my ($hash, $context, $reset, $owx_dev, $crcpart, $numread, $res) = @_;
  642. my ($i,$j,$k,@data);
  643. my $change = 0;
  644. my $master = $hash->{IODev};
  645. my $name = $hash->{NAME};
  646. my $msg;
  647. OWX_WDBG($name,"OWVAR: $name: BinValues called with ",$res)
  648. if( $main::owx_debug>2 );
  649. #-- process results
  650. #die "OWVAR: $name not accessible in 2nd step" unless ( defined $res and $res ne 0 );
  651. if( $context eq "getstate" ) {
  652. #-- process results
  653. @data=split(//,$res);
  654. if (@data != 2) {
  655. $msg="Error- $name returns invalid data length, ".int(@data)." instead of 2 bytes ";
  656. }else{
  657. $msg="No error";
  658. my $stat = ord($data[0]);
  659. my $val = ord($data[1]);
  660. $hash->{owg_val}=sprintf("%5.2f",(1.0-$val/255.0)*100);
  661. #-- and now from raw to formatted values
  662. $hash->{PRESENT} = 1;
  663. my $value = OWVAR_FormatValues($hash);
  664. #Log3 $hash->{NAME}, 5, $value;
  665. }
  666. OWX_WDBG($name,"OWXVAR_BinValues: ".$msg,$res)
  667. if( $main::owx_debug>2 );
  668. return undef;
  669. }elsif( $context eq "setstate" ) {
  670. my $val = ord($res);
  671. #$hash->{owg_val}=sprintf("%5.2f",(1-$val/255.0)*100);
  672. #-- process results
  673. if (length($res) != 1){
  674. $msg="$name returns invalid data length, ".length($res)." instead of 1 bytes, ";
  675. }elsif($val ne $crcpart){
  676. $msg="$name returns invalid data $val instead of $crcpart, "
  677. }else{
  678. $msg="No error, val = $val";
  679. }
  680. OWX_WDBG($name,"OWXVAR_BinValues: ".$msg,$res)
  681. if( $main::owx_debug>2 );
  682. #### master slave context proc owx_dev data crcpart numread startread callback delay
  683. # 2 suppresses the initial bus reset, 16 inserts at top of queue
  684. OWX_Qomplex($master, $hash, "confirm", 18, $owx_dev, "\x96", 0, 0, 11, undef, 0);
  685. return undef;
  686. }
  687. }
  688. ########################################################################################
  689. #
  690. # OWXVAR_GetValues - Trigger reading from one device
  691. #
  692. # Parameter hash = hash of device addressed
  693. #
  694. ########################################################################################
  695. sub OWXVAR_GetValues($) {
  696. my ($hash) = @_;
  697. #-- ID of the device
  698. my $owx_dev = $hash->{ROM_ID};
  699. #-- hash of the busmaster
  700. my $master = $hash->{IODev};
  701. my $name = $hash->{NAME};
  702. #-- NOW ask the specific device
  703. #-- issue the match ROM command \x55 and the read wiper command \xF0
  704. #-- reading 9 + 1 + 2 data bytes and 0 CRC byte = 12 bytes
  705. #-- OLD OWX interface
  706. if( !$master->{ASYNCHRONOUS} ){
  707. OWX_Reset($master);
  708. my $res=OWX_Complex($master,$owx_dev,"\xF0",2);
  709. return "OWVAR: $name not accessible in reading"
  710. if( $res eq 0 );
  711. return "OWVAR: $name has returned invalid data"
  712. if( length($res)!=12);
  713. OWX_Reset($master);
  714. eval {
  715. OWXVAR_BinValues($hash,"getstate",0,$owx_dev,0,2,substr($res,10,2));
  716. };
  717. return $@ ? $@ : undef;
  718. #-- NEW OWX interface
  719. }else{
  720. #### master slave context proc owx_dev data crcpart numread startread callback delay
  721. OWX_Qomplex($master, $hash, "getstate", 0, $owx_dev, "\xF0", 0, 2, 10, \&OWXVAR_BinValues, 0);
  722. return undef;
  723. }
  724. }
  725. #######################################################################################
  726. #
  727. # OWXVAR_SetValues - Implements SetFn function
  728. #
  729. # Parameter hash = hash of device addressed
  730. # a = argument array
  731. #
  732. #######################################################################################
  733. sub OWXVAR_SetValues($$$) {
  734. my ($hash, $key,$value) = @_;
  735. #-- ID of the device
  736. my $owx_dev = $hash->{ROM_ID};
  737. #-- hash of the busmaster
  738. my $master = $hash->{IODev};
  739. my $name = $hash->{NAME};
  740. #-- translate from 0..100 to 0..255
  741. return sprintf("OWXVAR: Set with wrong value $value for $key, range is [%3.1f,%3.1f]",0,100)
  742. if($value < 0 || $value > 100);
  743. my $pos = floor((100-$value)*2.55+0.5);
  744. #-- issue the match ROM command \x55 and the write wiper command \x0F,
  745. # followed by 1 bytes of data
  746. #
  747. my $select=sprintf("\x0F%c",$pos);
  748. #-- OLD OWX interface
  749. if( !$master->{ASYNCHRONOUS} ){
  750. OWX_Reset($master);
  751. my $res=OWX_Complex($master,$owx_dev,$select,1);
  752. return "OWXVAR: $name not accessible"
  753. if( $res eq 0 );
  754. my $rv=ord(substr($res,11,1));
  755. return "OWXVAR: $name: Set failed with return value $rv from set value $pos"
  756. if($rv ne $pos);
  757. my $res2=OWX_Complex($master,$owx_dev,"\x96",1);
  758. my $rv2=ord(substr($res2,11,1));
  759. return "OWXVAR: $name: Set failed with return value $rv2 from release value"
  760. if($rv2 ne 0);
  761. OWX_Reset($master);
  762. $hash->{owg_val}=sprintf("%5.2f",(1-$pos/255.0)*100);
  763. #-- NEW OWX interface
  764. }else{
  765. #### master slave context proc owx_dev data crcpart numread startread callback delay
  766. OWX_Qomplex($master, $hash, "setstate", 0, $owx_dev, $select, $pos, 1, 11, \&OWXVAR_BinValues, 0);
  767. $hash->{owg_val}=sprintf("%5.2f",(1-$pos/255.0)*100);
  768. }
  769. return undef;
  770. }
  771. 1;
  772. =pod
  773. =begin html
  774. <a name="OWVAR"></a>
  775. <h3>OWVAR</h3>
  776. <p>FHEM module to commmunicate with 1-Wire bus digital potentiometer devices of type DS2890<br />
  777. <br />This 1-Wire module works with the OWX interface module, but not yet with the OWServer interface module.
  778. </p>
  779. <a name="OWVARexample"></a>
  780. <h4>Example</h4>
  781. <p>
  782. <code>define OWX_P OWVAR E8D09B030000 </code>
  783. <br />
  784. <code>attr OWX_P Function 1.02 * V + 0.58 | (U-0.58) / 1.02</code>
  785. <br />
  786. </p><br />
  787. <a name="OWVARdefine"></a>
  788. <h4>Define</h4>
  789. <p>
  790. <code>define &lt;name&gt; OWVAR &lt;id&gt;</code> or <br/>
  791. <code>define &lt;name&gt; OWVAR &lt;fam&gt;.&lt;id&gt; </code>
  792. <br /><br /> Define a 1-Wire digital potentiometer device.</p>
  793. <ul>
  794. <li>
  795. <code>&lt;fam&gt;</code>
  796. <br />2-character unique family id, must be 2C </li>
  797. <li>
  798. <code>&lt;id&gt;</code>
  799. <br />12-character unique ROM id of the thermometer device without family id and CRC
  800. code
  801. </li>
  802. </ul>
  803. <a name="OWVARset"></a>
  804. <h4>Set</h4>
  805. <ul>
  806. <li><a name="owvar_value">
  807. <code>set &lt;name&gt; value &lt;float&gt;</code></a>
  808. <br /> The value of the potentiometer resistance against ground. Arguments may be in the
  809. range of [0,100] without a Function attribute, or in the range needed for a <a href="#owvar_function">Function</a> </li>
  810. </ul>
  811. <br />
  812. <a name="OWVARget"></a>
  813. <h4>Get</h4>
  814. <ul>
  815. <li><a name="owvar_id">
  816. <code>get &lt;name&gt; id</code></a>
  817. <br /> Returns the full 1-Wire device id OW_FAMILY.ROM_ID.CRC </li>
  818. <li><a name="owvar_present">
  819. <code>get &lt;name&gt; present</code></a>
  820. <br /> Returns 1 if this 1-Wire device is present, otherwise 0. </li>
  821. <li><a name="owvar_value2">
  822. <code>get &lt;name&gt; value</code></a><br />Obtain the value. </li>
  823. </ul>
  824. <br />
  825. <a name="OWVARattr"></a>
  826. <h4>Attributes</h4>
  827. <ul>
  828. <li><a name="owvar_cname"><code>attr &lt;name&gt; Name
  829. &lt;string&gt;[|&lt;string&gt;]</code></a>
  830. <br />name for the reading [|name used in state reading]. </li>
  831. <li><a name="owvar_cunit"><code>attr &lt;name&gt; Unit
  832. &lt;string&gt;[|&lt;string&gt;]</code></a>
  833. <br />unit of measurement used in state reading. </li>
  834. <li><a name="owvar_cfunction"> <code>attr &lt;name&gt; Function
  835. &lt;string&gt;|&lt;string&gt;</code></a>
  836. <br />The first string is an arbitrary functional expression u(V) involving the variable V. V is replaced by
  837. the raw potentiometer reading (in the range of [0,100]). The second string must be the inverse
  838. function v(U) involving the variable U, such that U can be replaced by the value given in the
  839. <a href="#OWVARset">Set</a> argument. Care has to taken that v(U) is in the range [0,100].
  840. No check on the validity of these functions is performed,
  841. <b>singularities may crash FHEM.</b> <a href="#OWVARexample">Example see above</a>.
  842. </li>
  843. <li><a href="#readingFnAttributes">readingFnAttributes</a></li>
  844. </ul>
  845. =end html
  846. =cut