21_OWCOUNT.pm 75 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229
  1. ########################################################################################
  2. #
  3. # OWCOUNT.pm
  4. #
  5. # FHEM module to commmunicate with 1-Wire Counter/RAM DS2423
  6. #
  7. # Prof. Dr. Peter A. Henning
  8. # Norbert Truchsess
  9. #
  10. # $Id: 21_OWCOUNT.pm 11196 2016-04-06 18:56:28Z pahenning $
  11. #
  12. ########################################################################################
  13. #
  14. # define <name> OWCOUNT [<model>] <ROM_ID> [interval] or OWCOUNT <FAM_ID>.<ROM_ID> [interval]
  15. #
  16. # where <name> may be replaced by any name string
  17. #
  18. # <model> is a 1-Wire device type DS2423,DS2423ene,wDS2423eold. If omitted, we assume this to be an
  19. # DS2423 Counter/RAM
  20. # <FAM_ID> is a 1-Wire family id, currently allowed value is 1D
  21. # <ROM_ID> is a 12 character (6 byte) 1-Wire ROM ID
  22. # without Family ID, e.g. A2D90D000800
  23. # [interval] is an optional query interval in seconds
  24. #
  25. # get <name> id => FAM_ID.ROM_ID.CRC
  26. # get <name> present => 1 if device present, 0 if not
  27. # get <name> interval => query interval
  28. # get <name> memory <page> => 32 byte string from page 0..13
  29. # get <name> midnight <channel> => todays starting value (formatted) for counter
  30. # get <name> month => summary and average for month
  31. # get <name> year => summary and average for year
  32. # get <name> raw <channel> => raw value for counter
  33. # get <name> counters => formatted values for both counters
  34. # get <name> version => OWX version number
  35. #
  36. # set <name> interval => set query interval for measurement
  37. # set <name> memory <page> => 32 byte string into page 0..13
  38. # set <name> midnight <channel> => todays starting value for counter
  39. # set <name> counter <channel> => correct midnight value such that
  40. # counter shows this value
  41. #
  42. # Additional attributes are defined in fhem.cfg, in some cases per channel, where <channel>=A,B
  43. #
  44. # attr <name> LogM <string> = device name (not file name) of monthly log file
  45. # attr <name> LogY <string> = device name (not file name) of yearly log file
  46. # attr <name> nomemory = 1|0 (when set to 1, disables use of internal memory)
  47. # attr <name> <channel>Name <string>[|<string>] = name for the channel [|short name used in state reading]
  48. # attr <name> <channel>Unit <string> = unit of measurement used in state reading (default cts, none for empty)
  49. # attr <name> <channel>Rate <string>[|<string>] = name for the channel rate [|short name used in state reading]
  50. # attr <name> <channel>Offset <float> = offset added to the reading in this channel
  51. # attr <name> <channel>Factor <float> = factor multiplied to (reading+offset) in this channel
  52. # attr <name> <channel>Mode <string> = counting mode = normal(default) or daily
  53. # attr <name> <channel>Period <string> = period for rate calculation = hour (default), minute or second
  54. #
  55. # In normal counting mode each returned counting value will be factor*(reading+offset)
  56. # In daily counting mode each returned counting value will be factor*(reading+offset)-midnight
  57. # where midnight is stored as string in the 32 byte memory associated with the counter
  58. #
  59. # Log Lines
  60. # after each interval <date> <name> <channel>: <value> <unit> <value> / <unit>/<period> <channel>: <value> <unit> / <value> <unit>/<period>
  61. # example: 2012-07-30_00:07:55 OWX_C Taste: 17.03 p 28.1 p/h B: 7.0 cts 0.0 cts/min
  62. # after midnight <new date> <name> <old day> <old date> <channel>: <value> <unit> <channel>: <value> <unit>
  63. # example: 2012-07-30_00:00:57 OWX_C D29: 2012-7-29_23:59:59 Taste: 110.0 p, B: 7.0 cts
  64. ########################################################################################
  65. #
  66. # This programm is free software; you can redistribute it and/or modify
  67. # it under the terms of the GNU General Public License as published by
  68. # the Free Software Foundation; either version 2 of the License, or
  69. # (at your option) any later version.
  70. #
  71. # The GNU General Public License can be found at
  72. # http://www.gnu.org/copyleft/gpl.html.
  73. # A copy is found in the textfile GPL.txt and important notices to the license
  74. # from the author is found in LICENSE.txt distributed with these scripts.
  75. #
  76. # This script is distributed in the hope that it will be useful,
  77. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  78. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  79. # GNU General Public License for more details.
  80. #
  81. ########################################################################################
  82. package main;
  83. use vars qw{%attr %defs %modules $readingFnAttributes $init_done};
  84. use strict;
  85. use warnings;
  86. #add FHEM/lib to @INC if it's not already included. Should rather be in fhem.pl than here though...
  87. BEGIN {
  88. if (!grep(/FHEM\/lib$/,@INC)) {
  89. foreach my $inc (grep(/FHEM$/,@INC)) {
  90. push @INC,$inc."/lib";
  91. };
  92. };
  93. };
  94. use ProtoThreads;
  95. no warnings 'deprecated';
  96. sub Log3($$$);
  97. my $owx_version="6.01";
  98. #-- fixed raw channel name, flexible channel name
  99. my @owg_fixed = ("A","B");
  100. my @owg_channel = ("A","B");
  101. my @owg_rate = ("A_rate","B_rate");
  102. #-- initially assume that both memory types (low, high) are present
  103. my @owg_memory = (1,1,0);
  104. my $owgmodel;
  105. my $owgauto = 0;
  106. my %gets = (
  107. "id" => "",
  108. "present" => "",
  109. "interval" => "",
  110. "memory" => "",
  111. "midnight" => "",
  112. "raw" => "",
  113. "counters" => "",
  114. "month" => "",
  115. "year" => "",
  116. "version" => ""
  117. );
  118. my %sets = (
  119. "interval" => "",
  120. "memory" => "",
  121. "midnight" => "",
  122. "counter" => ""
  123. );
  124. my %updates = (
  125. "present" => "",
  126. "counter" => ""
  127. );
  128. ########################################################################################
  129. #
  130. # The following subroutines are independent of the bus interface
  131. #
  132. # Prefix = OWCOUNT
  133. #
  134. ########################################################################################
  135. #
  136. # OWCOUNT_Initialize
  137. #
  138. # Parameter hash = hash of device addressed
  139. #
  140. ########################################################################################
  141. sub OWCOUNT_Initialize ($) {
  142. my ($hash) = @_;
  143. $hash->{DefFn} = "OWCOUNT_Define";
  144. $hash->{UndefFn} = "OWCOUNT_Undef";
  145. $hash->{GetFn} = "OWCOUNT_Get";
  146. $hash->{SetFn} = "OWCOUNT_Set";
  147. $hash->{NotifyFn}= "OWCOUNT_Notify";
  148. $hash->{InitFn} = "OWCOUNT_Init";
  149. $hash->{AttrFn} = "OWCOUNT_Attr";
  150. #-- see header for attributes
  151. my $attlist = "IODev do_not_notify:0,1 showtime:0,1 model:DS2423,DS2423enew,DS2423eold LogM LogY ".
  152. "nomemory:1,0 interval ".
  153. $readingFnAttributes;
  154. for( my $i=0;$i<int(@owg_fixed);$i++ ){
  155. $attlist .= " ".$owg_fixed[$i]."Name";
  156. $attlist .= " ".$owg_fixed[$i]."Rate";
  157. $attlist .= " ".$owg_fixed[$i]."Offset";
  158. $attlist .= " ".$owg_fixed[$i]."Factor";
  159. $attlist .= " ".$owg_fixed[$i]."Unit";
  160. $attlist .= " ".$owg_fixed[$i]."Mode:normal,daily";
  161. $attlist .= " ".$owg_fixed[$i]."Period:hour,minute,second";
  162. }
  163. $hash->{AttrList} = $attlist;
  164. #-- make sure OWX is loaded so OWX_CRC is available if running with OWServer
  165. main::LoadModule("OWX");
  166. }
  167. #########################################################################################
  168. #
  169. # OWCOUNT_Define - Implements DefFn function
  170. #
  171. # Parameter hash = hash of device addressed, def = definition string
  172. #
  173. #########################################################################################
  174. sub OWCOUNT_Define ($$) {
  175. my ($hash, $def) = @_;
  176. my @a = split("[ \t][ \t]*", $def);
  177. my ($name,$model,$fam,$id,$crc,$interval,$scale,$ret);
  178. #-- default
  179. $name = $a[0];
  180. $interval = 300;
  181. $scale = "";
  182. $ret = "";
  183. #-- check syntax
  184. return "OWCOUNT: Wrong syntax, must be define <name> OWCOUNT [<model>] <id> [interval] or OWCOUNT <fam>.<id> [interval]"
  185. if(int(@a) < 2 || int(@a) > 5);
  186. #-- different types of definition allowed
  187. my $a2 = $a[2];
  188. my $a3 = defined($a[3]) ? $a[3] : "";
  189. #-- The model may be DS2423 or DS2423emu. Some weird people are violating 1-Wire integrity by using the
  190. # the same family ID although the DS2423emu does not fully support the DS2423 commands.
  191. # Model attribute will be modified later when memory is checked
  192. #-- no model, 12 characters
  193. if( $a2 =~ m/^[0-9|a-f|A-F]{12}$/ ) {
  194. $model = "DS2423";
  195. $owgauto =1;
  196. #CommandAttr (undef,"$name model DS2423");
  197. $fam = "1D";
  198. $id = $a[2];
  199. if(int(@a)>=4) { $interval = $a[3]; }
  200. #-- no model, 2+12 characters
  201. } elsif( $a2 =~ m/^[0-9|a-f|A-F]{2}\.[0-9|a-f|A-F]{12}$/ ) {
  202. $fam = substr($a[2],0,2);
  203. $id = substr($a[2],3);
  204. if(int(@a)>=4) { $interval = $a[3]; }
  205. if( $fam eq "1D" ){
  206. $model = "DS2423";
  207. $owgauto =1;
  208. #CommandAttr (undef,"$name model DS2423");
  209. }else{
  210. return "OWCOUNT: Wrong 1-Wire device family $fam";
  211. }
  212. #-- model, 12 characters
  213. } elsif( $a3 =~ m/^[0-9|a-f|A-F]{12}$/ ) {
  214. $model = $a[2];
  215. $id = $a[3];
  216. if(int(@a)>=5) { $interval = $a[4]; }
  217. if( $model eq "DS2423" ){
  218. $fam = "1D";
  219. #CommandAttr (undef,"$name model DS2423");
  220. #-- Check, if name might be from autocreate -> checking memory
  221. if( $name =~ /OWX\_$fam\_$id/ ){
  222. $owgauto=1;
  223. }
  224. @owg_memory = (1,1,0);
  225. }elsif( $model eq "DS2423enew" ){
  226. $fam = "1D";
  227. @owg_memory = (1,1,0);
  228. #CommandAttr (undef,"$name model DS2423enew");
  229. }elsif( $model eq "DS2423eold" ){
  230. $fam = "1D";
  231. #CommandAttr (undef,"$name model DS2423eold");
  232. #CommandAttr (undef,"$name nomemory 1");
  233. @owg_memory = (0,0,0);
  234. }else{
  235. return "OWCOUNT: Wrong 1-Wire device model $model";
  236. }
  237. } else {
  238. return "OWCOUNT: $a[0] ID $a[2] invalid, specify a 12 or 2.12 digit value";
  239. }
  240. $owgmodel=$model;
  241. # determine CRC Code - only if this is a direct interface
  242. $crc = sprintf("%02x",OWX_CRC($fam.".".$id."00"));
  243. #-- Define device internals
  244. $hash->{ROM_ID} = "$fam.$id.$crc";
  245. $hash->{OW_ID} = $id;
  246. $hash->{OW_FAMILY} = $fam;
  247. $hash->{PRESENT} = 0;
  248. $hash->{INTERVAL} = $interval;
  249. #-- Couple to I/O device
  250. AssignIoPort($hash);
  251. if( !defined($hash->{IODev}) or !defined($hash->{IODev}->{NAME}) ){
  252. return "OWCOUNT: Warning, no 1-Wire I/O device found for $name.";
  253. } else {
  254. $hash->{ASYNC} = $hash->{IODev}->{TYPE} eq "OWX_ASYNC" ? 1 : 0; #-- false for now
  255. }
  256. $modules{OWCOUNT}{defptr}{$id} = $hash;
  257. #--
  258. readingsSingleUpdate($hash,"state","defined",1);
  259. Log3 $name, 3, "OWCOUNT: Device $name defined.";
  260. $hash->{NOTIFYDEV} = "global";
  261. if ($init_done) {
  262. OWCOUNT_Init($hash);
  263. }
  264. return undef;
  265. }
  266. #######################################################################################
  267. #
  268. # OWCOUNT_Notify - Implements Notify function
  269. #
  270. # Parameter hash = hash of device addressed
  271. # dev = device
  272. #
  273. ########################################################################################
  274. sub OWCOUNT_Notify ($$) {
  275. my ($hash,$dev) = @_;
  276. if( grep(m/^(INITIALIZED|REREADCFG)$/, @{$dev->{CHANGED}}) ) {
  277. OWCOUNT_Init($hash);
  278. } elsif( grep(m/^SAVE$/, @{$dev->{CHANGED}}) ) {
  279. }
  280. }
  281. #######################################################################################
  282. #
  283. # OWCOUNT_Init - Implements Init function
  284. #
  285. # Parameter hash = hash of device addressed
  286. #
  287. ########################################################################################
  288. sub OWCOUNT_Init ($) {
  289. my ($hash)=@_;
  290. #-- Start timer for updates
  291. RemoveInternalTimer($hash);
  292. InternalTimer(gettimeofday()+10, "OWCOUNT_GetValues", $hash, 0);
  293. return undef;
  294. }
  295. ########################################################################################
  296. #
  297. # OWCOUNT_InitializeDevice - delayed setting of initial readings and channel names
  298. #
  299. # Parameter hash = hash of device addressed
  300. #
  301. ########################################################################################
  302. sub OWCOUNT_InitializeDevice($) {
  303. my ($hash) = @_;
  304. my $name = $hash->{NAME};
  305. #-- get memory page/counter according to interface type
  306. my $master= $hash->{IODev};
  307. my $interface= $hash->{IODev}->{TYPE};
  308. my $olddata = "";
  309. my ($olddata1,$olddata2);
  310. my $newdata = "OWCOUNT ".$owx_version;
  311. my $ret;
  312. #-- post-define: check model
  313. if( !defined($attr{$hash->{NAME}}{"model"}) ){
  314. CommandAttr (undef,$hash->{NAME}." model $owgmodel");
  315. }
  316. #-- initial values
  317. for( my $i=0;$i<int(@owg_fixed);$i++) {
  318. #-- initial readings
  319. $hash->{owg_val}->[$i] = "";
  320. $hash->{owg_midnight}->[$i] = "";
  321. $hash->{owg_str}->[$i] = "";
  322. }
  323. #-- Set state to initialized
  324. readingsSingleUpdate($hash,"state","initialized",1);
  325. return undef;
  326. }
  327. #######################################################################################
  328. #
  329. # OWCOUNT_Attr - Set one attribute value for device
  330. #
  331. # Parameter hash = hash of device addressed
  332. # a = argument array
  333. #
  334. ########################################################################################
  335. sub OWCOUNT_Attr(@) {
  336. my ($do,$name,$key,$value) = @_;
  337. my $hash = $defs{$name};
  338. my $ret;
  339. if ( $do eq "set") {
  340. ARGUMENT_HANDLER: {
  341. #-- interval modified at runtime
  342. $key eq "interval" and do {
  343. #-- check value
  344. return "OWCOUNT: Set with short interval, must be > 1" if(int($value) < 1);
  345. #-- update timer
  346. $hash->{INTERVAL} = $value;
  347. if ($init_done) {
  348. RemoveInternalTimer($hash);
  349. InternalTimer(gettimeofday()+$hash->{INTERVAL}, "OWCOUNT_GetValues", $hash, 0);
  350. }
  351. last;
  352. };
  353. $key eq "IODev" and do {
  354. AssignIoPort($hash,$value);
  355. if( defined($hash->{IODev}) ) {
  356. $hash->{ASYNC} = $hash->{IODev}->{TYPE} eq "OWX_ASYNC" ? 1 : 0;
  357. if ($init_done) {
  358. OWCOUNT_Init($hash);
  359. }
  360. }
  361. last;
  362. };
  363. }
  364. }
  365. return $ret;
  366. }
  367. ########################################################################################
  368. #
  369. # OWCOUNT_ChannelNames - find the real channel names
  370. #
  371. # Parameter hash = hash of device addressed
  372. #
  373. ########################################################################################
  374. sub OWCOUNT_ChannelNames($) {
  375. my ($hash) = @_;
  376. my $name = $hash->{NAME};
  377. my $state = $hash->{READINGS}{"state"}{VAL};
  378. my ($cname,@cnama,$unit,@unarr,$runit,$period);
  379. for (my $i=0;$i<int(@owg_fixed);$i++){
  380. #-- name
  381. $cname = defined($attr{$name}{$owg_fixed[$i]."Name"}) ? $attr{$name}{$owg_fixed[$i]."Name"} : "$owg_fixed[$i]";
  382. @cnama = split(/\|/,$cname);
  383. if( int(@cnama)!=2){
  384. push(@cnama,$cnama[0]);
  385. }
  386. #-- unit
  387. $unit = defined($attr{$name}{$owg_fixed[$i]."Unit"}) ? $attr{$name}{$owg_fixed[$i]."Unit"} : "cts";
  388. if( $unit eq "none" ){
  389. $unit = "";
  390. }else{
  391. $unit = " ".$unit;
  392. }
  393. #-- put into readings
  394. $owg_channel[$i]=$cnama[0];
  395. $hash->{READINGS}{$owg_channel[$i]}{ABBR} = $cnama[1];
  396. $hash->{READINGS}{$owg_channel[$i]}{UNIT} = $unit;
  397. $period = defined($attr{$name}{$owg_fixed[$i]."Period"}) ? $attr{$name}{$owg_fixed[$i]."Period"} : "hour";
  398. #-- put into readings
  399. $hash->{READINGS}{$owg_channel[$i]}{PERIOD} = $period;
  400. #-- rate
  401. $cname = defined($attr{$name}{$owg_fixed[$i]."Rate"}) ? $attr{$name}{$owg_fixed[$i]."Rate"} :
  402. (defined($attr{$name}{$owg_fixed[$i]."Name"}) ? "$cnama[0]_rate|$cnama[1]_r" : "$owg_fixed[$i]_rate|$owg_fixed[$i]_r");
  403. @cnama = split(/\|/,$cname);
  404. if( int(@cnama)!=2){
  405. push(@cnama,$cnama[0]);
  406. }
  407. #-- rate unit
  408. if( $unit ne " " ){
  409. if( $period eq "hour" ){
  410. $unit .= "/h";
  411. }elsif( $period eq "minute" ){
  412. $unit .= "/min";
  413. } else {
  414. $unit .= "/s";
  415. }
  416. }
  417. #-- some special cases
  418. # Energy/Power
  419. $unit = " kW"
  420. if ($unit eq " kWh/h" );
  421. #-- put into readings
  422. $owg_rate[$i]=$cnama[0];
  423. $hash->{READINGS}{$owg_rate[$i]}{ABBR} = $cnama[1];
  424. $hash->{READINGS}{$owg_rate[$i]}{UNIT} = $unit;
  425. }
  426. }
  427. ########################################################################################
  428. #
  429. # OWCOUNT_FormatValues - put together various format strings and assemble STATE variable
  430. #
  431. # Parameter hash = hash of device addressed
  432. #
  433. ########################################################################################
  434. sub OWCOUNT_FormatValues($) {
  435. my ($hash) = @_;
  436. my $name = $hash->{NAME};
  437. my ($offset,$factor,$period,$unit,$runit,$vval,$vrate);
  438. my ($svalue,$dvalue,$mvalue) = ("","","");
  439. my $galarm = 0;
  440. my ($sec, $min, $hour, $day, $month, $year, $wday,$yday,$isdst) = localtime(time);
  441. my ($seco,$mino,$houro,$dayo,$montho,$yearo,$dayrest);
  442. my ($daily, $dt,$dval,$dval2,$deltim,$delt,$delf);
  443. my ($total,$total0,$total1,$total2,$total3,@monthv,@yearv);
  444. my $daybreak = 0;
  445. my $monthbreak = 0;
  446. #-- no change in any value if invalid reading
  447. for (my $i=0;$i<int(@owg_fixed);$i++){
  448. return if( $hash->{owg_val}->[$i] eq "");
  449. }
  450. #-- obtain channel names
  451. OWCOUNT_ChannelNames($hash);
  452. #-- Check, whether we have a new day at the next reading
  453. $deltim = $hour*60.0+$min+$sec/60.0 - (1440 - $hash->{INTERVAL}/60.0);
  454. #$deltim = $min+$sec/60.0 - 55;
  455. if( $deltim>=0 ){
  456. $daybreak = 1;
  457. $monthbreak = 0;
  458. #-- Timer data from tomorrow
  459. my ($secn,$minn,$hourn,$dayn,$monthn,$yearn,$wdayn,$ydayn,$isdstn) = localtime(time() + $hash->{INTERVAL} + 3600);
  460. #-- Check, whether we have a new month
  461. if( $dayn == 1 ){
  462. $monthbreak = 1;
  463. }
  464. }
  465. #-- put into READING
  466. readingsBeginUpdate($hash);
  467. #-- Check memory string only if model is defined automatically
  468. if( $owgauto ){
  469. my $msg = "OWCOUNT: In device $name, ";
  470. if( $owg_memory[2]==1 ){
  471. if( ($owg_memory[0]==1) && ($owg_memory[1]==1) ){
  472. Log 1, $msg."memory pages 0..13 and midnight store present, model should be DS2423"
  473. if( $attr{$name}{"model"} ne "DS2423");
  474. }elsif( ($owg_memory[0]==0) && ($owg_memory[1]==1) ){
  475. Log 1, $msg."midnight store only, model should be DS2423enew"
  476. if( $attr{$name}{"model"} ne "DS2423enew");
  477. }else{
  478. Log 1, $msg."no memory pages and no midnight store found, model should be DS2423eold"
  479. if( $attr{$name}{"model"} ne "DS2423eold");
  480. }
  481. $owg_memory[2]=0;
  482. }
  483. $owgauto=0;
  484. }
  485. #-- formats for output
  486. for (my $i=0;$i<int(@owg_fixed);$i++){
  487. #-- mode normal or daily
  488. $daily = 0;
  489. if( defined($attr{$name}{$owg_fixed[$i]."Mode"} )){
  490. if( $attr{$name}{$owg_fixed[$i]."Mode"} eq "daily"){
  491. $daily = 1;
  492. }
  493. }
  494. #-- offset and scale factor
  495. $offset = defined($attr{$name}{$owg_fixed[$i]."Offset"}) ? $attr{$name}{$owg_fixed[$i]."Offset"} : 0;
  496. $factor = defined($attr{$name}{$owg_fixed[$i]."Factor"}) ? $attr{$name}{$owg_fixed[$i]."Factor"} : 1;
  497. #-- put into READINGS
  498. $hash->{READINGS}{$owg_channel[$i]}{OFFSET} = $offset;
  499. $hash->{READINGS}{$owg_channel[$i]}{FACTOR} = $factor;
  500. $unit = $hash->{READINGS}{$owg_channel[$i]}{UNIT};
  501. $period = $hash->{READINGS}{$owg_channel[$i]}{PERIOD};
  502. $runit = $hash->{READINGS}{$owg_rate[$i]}{UNIT};
  503. #-- skip some things if undefined
  504. if( $hash->{owg_val}->[$i] eq ""){
  505. $svalue .= $owg_channel[$i]." ???";
  506. }else{
  507. #-- only if attribute value mode=daily, take the midnight value from memory
  508. if( $daily == 1){
  509. $vval = int( (($hash->{owg_val}->[$i] + $offset)*$factor - $hash->{owg_midnight}->[$i])*10000+0.5)/10000;
  510. } else {
  511. $vval = int( ($hash->{owg_val}->[$i] + $offset)*$factor*10000+0.5)/10000;
  512. }
  513. #-- rate calculation: get the old values
  514. my $oldval = $hash->{READINGS}{$owg_channel[$i]}{VAL};
  515. my $oldtim = $hash->{READINGS}{$owg_channel[$i]}{TIME};
  516. $oldtim = "" if(!defined($oldtim));
  517. #-- safeguard against the case where no previous measurement
  518. if( length($oldtim) > 0 ){
  519. #-- correct counter wraparound since last reading
  520. if( $vval < $oldval) {
  521. $oldval -= (65536*$factor);
  522. }
  523. #-- previous measurement time
  524. ($yearo,$montho,$dayrest) = split(/-/,$oldtim);
  525. $dayo = substr($dayrest,0,2);*60=
  526. ($houro,$mino,$seco) = split(/:/,substr($dayrest,3));
  527. #-- time difference to previous measurement and to midnight
  528. $delt = ($hour-$houro)*3600 + ($min-$mino)*60 + ($sec-$seco);
  529. $delf = $hour *3600 + $min *60 + $sec - 86400;
  530. #-- rate
  531. if( $delt > 0.0){
  532. $vrate = ($vval-$oldval)/$delt;
  533. } else {
  534. $vrate = 0.0;
  535. }
  536. #-- correct rate for period setting
  537. if( $period eq "hour" ){
  538. $vrate*=3600;
  539. }elsif( $period eq "minute" ){
  540. $vrate*=60;
  541. }
  542. $vrate = int($vrate * 10000+0.5)/10000;
  543. #--midnight extrapolation only possible if previous measurement
  544. if( $daybreak==1 ){
  545. #-- linear extrapolation
  546. $dt = -$delf/$delt;
  547. $dval = int(($vval+($vval-$oldval)*$dt)*10000+0.5)/10000;
  548. if( $daily == 1 ){
  549. $dval2 = $dval+$hash->{owg_midnight}->[$i];
  550. } else {
  551. $dval2 = $dval;
  552. }
  553. #-- in any mode store the interpolated value in the midnight store
  554. my $msg = sprintf("%4d-%02d-%02d midnight %7.2f",
  555. $year+1900,$month+1,$day,$dval2);
  556. OWCOUNT_SetPage($hash,14+$i,$msg);
  557. Log3 $name,5,"OWCOUNT: measured value $vval (midnight ".$hash->{owg_midnight}->[$i].") => new extrapolated midnight $dval2";
  558. #-- string buildup for monthly and yearly logging
  559. $dvalue .= sprintf( " %s: %5.2f %s %sM: %%5.2f %s", $owg_channel[$i],$dval,$unit,$owg_channel[$i],$unit);
  560. $mvalue .= sprintf( " %sM: %%5.2f %s %sY: %%5.2f %s", $owg_channel[$i],$unit,$owg_channel[$i],$unit);
  561. } #-- end daybreak
  562. #-- string buildup for return value and STATE
  563. #-- 1 or 3 decimals
  564. my $fs = ( $factor == 1.0 )? "%s: %5.2f %s %s: %5.2f %s" : "%s: %5.3f %s %s: %5.3f %s";
  565. $svalue .= sprintf( $fs, $hash->{READINGS}{$owg_channel[$i]}{ABBR}, $vval,$unit,$hash->{READINGS}{$owg_rate[$i]}{ABBR},$vrate,$runit);
  566. #-- 3 decimals
  567. }
  568. readingsBulkUpdate($hash,$owg_channel[$i],$vval);
  569. readingsBulkUpdate($hash,$owg_rate[$i],$vrate);
  570. }
  571. #-- insert space
  572. if( $i<int(@owg_fixed)-1 ){
  573. $svalue .= " ";
  574. $dvalue .= " ";
  575. }
  576. }#-- end channel loop
  577. #-- daybreak postprocessing
  578. if( $daybreak == 1 ){
  579. #-- daily/monthly accumulated value
  580. @monthv = OWCOUNT_GetMonth($hash);
  581. @yearv = OWCOUNT_GetYear($hash);
  582. #-- put in monthly and yearly sums
  583. if( int(@monthv) == 2 ){
  584. $total0 = $monthv[0]->[1];
  585. $total1 = ($day==1)?$total0:$monthv[1]->[1];
  586. $dvalue = sprintf("D%02d ",$day).$dvalue;
  587. $dvalue = sprintf($dvalue,$total0,$total1);
  588. readingsBulkUpdate($hash,"day",$dvalue);
  589. }else{
  590. Log3 $name,3,"OWCOUNT: No monthly summary possible, ".$monthv[0]." for device $name";
  591. $total0 = 0;
  592. $total1 = 0;
  593. };
  594. if( int(@yearv) == 2 ){
  595. $total2 = $yearv[0]->[1];
  596. $total3 = ($month==0)?$total2: $yearv[1]->[1];
  597. if ( $monthbreak == 1){
  598. $mvalue = sprintf("M%02d ",$month+1).$mvalue;
  599. $mvalue = sprintf($mvalue,$total0,$total2,$total1,$total3);
  600. readingsBulkUpdate($hash,"month",$mvalue);
  601. }
  602. }else{
  603. Log3 $name,3,"OWCOUNT: No yearly summary possible, ".$yearv[0]." for device $name";
  604. $total2 = 0;
  605. $total3 = 0;
  606. };
  607. }
  608. #-- STATE
  609. readingsBulkUpdate($hash,"state",$svalue);
  610. readingsEndUpdate($hash,1);
  611. return $svalue;
  612. }
  613. ########################################################################################
  614. #
  615. # OWCOUNT_Get - Implements GetFn function
  616. #
  617. # Parameter hash = hash of device addressed, a = argument array
  618. #
  619. ########################################################################################
  620. sub OWCOUNT_Get($@) {
  621. my ($hash, @a) = @_;
  622. my $reading = $a[1];
  623. my $name = $hash->{NAME};
  624. my $model = $hash->{OW_MODEL};
  625. my $master = $hash->{IODev};
  626. my $value = undef;
  627. my $ret = "";
  628. my $page;
  629. my ($unit,$daily);
  630. my ($ret1,$ret2);
  631. #-- check syntax
  632. return "OWCOUNT: Get argument is missing @a"
  633. if(int(@a) < 2);
  634. #-- check argument
  635. return "OWCOUNT: Get with unknown argument $a[1], choose one of ".join(" ", sort keys %gets)
  636. if(!defined($gets{$a[1]}));
  637. #-- get id
  638. if($a[1] eq "id") {
  639. $value = $hash->{ROM_ID};
  640. return "$name.id => $value";
  641. }
  642. #-- get present
  643. if($a[1] eq "present") {
  644. #-- hash of the busmaster
  645. my $master = $hash->{IODev};
  646. #-- normal OWX
  647. if( !$hash->{ASYNC} ){
  648. $value = OWX_Verify($master,$hash->{ROM_ID});
  649. $hash->{PRESENT} = $value;
  650. return "$name.present => $value";
  651. #-- old asynchronous mode
  652. }else{
  653. eval {
  654. OWX_ASYNC_RunToCompletion($hash,OWX_ASYNC_PT_Verify($hash));
  655. };
  656. return GP_Catch($@) if $@;
  657. return "$name.present => ".ReadingsVal($name,"present","unknown");
  658. }
  659. }
  660. #-- get interval
  661. if($a[1] eq "interval") {
  662. $value = $hash->{INTERVAL};
  663. return "$name.interval => $value";
  664. }
  665. #-- get version
  666. if( $a[1] eq "version") {
  667. return "$name.version => $owx_version";
  668. }
  669. #-- get channel names
  670. OWCOUNT_ChannelNames($hash);
  671. #-- get month
  672. if($a[1] eq "month") {
  673. $value="$name.month =>\n";
  674. my @month2 = OWCOUNT_GetMonth($hash);
  675. #-- error case
  676. if( int(@month2) != 2 ){
  677. return $value." no monthly summary possible, ".$month2[0];
  678. }
  679. #-- 3 entries for each day
  680. for(my $i=0;$i<int(@month2);$i++){
  681. $unit = $hash->{READINGS}{$owg_channel[$i]}{UNITABBR};
  682. #-- mode = daily ?
  683. $daily = 0;
  684. if( defined($attr{$name}{$owg_fixed[$i]."Mode"} )){
  685. if( $attr{$name}{$owg_fixed[$i]."Mode"} eq "daily"){
  686. $daily = 1;
  687. }
  688. }
  689. if( $daily==1){
  690. $value .= $owg_channel[$i]."M: ".$month2[$i]->[1]." ".$unit.
  691. " (monthly sum until now, average ".$month2[$i]->[2]." ".$unit."/d)\n";
  692. }else{
  693. $value .= $owg_channel[$i]."M: ".$month2[$i]->[1]." ".$unit." (last midnight)\n";
  694. }
  695. }
  696. return $value;
  697. }
  698. #-- get year
  699. if($a[1] eq "year") {
  700. $value="$name.year =>\n";
  701. my @year2 = OWCOUNT_GetYear($hash);
  702. #-- error case
  703. if( int(@year2) != 2 ){
  704. return $value." no yearly summary possible, ".$year2[0];
  705. }
  706. #-- 3 entries for each month
  707. for(my $i=0;$i<int(@year2);$i++){
  708. $unit = $hash->{READINGS}{$owg_channel[$i]}{UNITABBR};
  709. #-- mode = daily ?
  710. $daily = 0;
  711. if( defined($attr{$name}{$owg_fixed[$i]."Mode"} )){
  712. if( $attr{$name}{$owg_fixed[$i]."Mode"} eq "daily"){
  713. $daily = 1;
  714. }
  715. }
  716. if( $daily==1){
  717. $value .= $owg_channel[$i]."Y: ".$year2[$i]->[1]." ".$unit.
  718. " (yearly sum until now, average ".$year2[$i]->[2]." ".$unit."/d)\n";
  719. }else{
  720. $value .= $owg_channel[$i]."Y: ".$year2[$i]->[1]." ".$unit."\n";
  721. }
  722. }
  723. return $value;
  724. }
  725. #-- get memory page/counter according to interface type
  726. my $interface= $hash->{IODev}->{TYPE};
  727. #-- check syntax for getting memory page 0..13 or midnight A/B
  728. my $nomemory = defined($attr{$name}{"nomemory"}) ? $attr{$name}{"nomemory"} : 0;
  729. if( $reading eq "memory" ){
  730. return "OWCOUNT: Memory usage disabled"
  731. if( $nomemory==1 );
  732. return "OWCOUNT: Get needs parameter when reading memory: <page>"
  733. if( int(@a)<2 );
  734. $page=int($a[2]);
  735. if( ($page<0) || ($page>13) ){
  736. return "OWCOUNT: Wrong memory page requested";
  737. }
  738. $ret = OWCOUNT_GetPage($hash,$page,1,1);
  739. #-- process result
  740. if( $master->{ASYNCHRONOUS} ){
  741. return "OWCOUNT: $name getting memory $page, please wait for completion";
  742. }else{
  743. #-- when we have a return code, we have an error
  744. if( $ret ){
  745. return "OWCOUNT: Could not get values from device $name, reason: ".$ret;
  746. }else{
  747. return "OWCOUNT: $name.$reading [$page] =>".$hash->{owg_str}->[$page];
  748. }
  749. }
  750. }
  751. if( $reading eq "midnight" ){
  752. return "OWCOUNT: get needs parameter when reading midnight: <channel>"
  753. if( int(@a)<3 );
  754. #-- find out which channel we have
  755. if( ($a[2] eq $owg_channel[0]) || ($a[2] eq "A") ){
  756. $page=14;
  757. }elsif( ($a[2] eq $owg_channel[1]) || ($a[2] eq "B") ){
  758. $page=15;
  759. } else {
  760. return "OWCOUNT: Invalid midnight counter address, must be A, B or defined channel name"
  761. }
  762. $ret = OWCOUNT_GetPage($hash,$page,1,1);
  763. #-- process result
  764. if( $master->{ASYNCHRONOUS} ){
  765. return "OWCOUNT: $name getting midnight value $page and counter, please wait for completion";
  766. }else{
  767. #-- when we have a return code, we have an error
  768. if( $ret ){
  769. return "OWCOUNT: Could not get values from device $name, reason: ".$ret;
  770. }else{
  771. return "OWCOUNT: $name.$reading [$page] =>".$hash->{owg_midnight}->[$page-14];
  772. }
  773. }
  774. }
  775. #-- check syntax for getting counter
  776. if( $reading eq "raw" ){
  777. return "OWCOUNT: Get needs parameter when reading raw counter: <channel>"
  778. if( int(@a)<2 );
  779. #-- find out which channel we have
  780. if( ($a[2] eq $owg_channel[0]) || ($a[2] eq "A") ){
  781. $page=14;
  782. }elsif( ($a[2] eq $owg_channel[1]) || ($a[2] eq "B") ){
  783. $page=15;
  784. } else {
  785. return "OWCOUNT: Invalid counter address, must be A, B or defined channel name"
  786. }
  787. $ret = OWCOUNT_GetPage($hash,$page,1,1);
  788. #-- process result
  789. if( $master->{ASYNCHRONOUS} ){
  790. return "OWCOUNT: $name getting page $page and counter, please wait for completion";
  791. }else{
  792. #-- when we have a return code, we have an error
  793. if( $ret ){
  794. return "OWCOUNT: Could not get values from device $name, reason: ".$ret;
  795. }
  796. #-- only one counter will be returned
  797. return "OWCOUNT: $name.raw $a[2] => ".$hash->{owg_val}->[$page-14];
  798. }
  799. #-- check syntax for getting counters
  800. }elsif( $reading eq "counters" ){
  801. return "OWCOUNT: Get needs no parameter when reading counters"
  802. if( int(@a)==1 );
  803. $ret1 = OWCOUNT_GetPage($hash,14,0,1);
  804. $ret2 = OWCOUNT_GetPage($hash,15,1,1);
  805. #-- process result
  806. if( $master->{ASYNCHRONOUS} ){
  807. return "OWCOUNT: $name getting counters, please wait for completion";
  808. }else{
  809. $ret .= $ret1
  810. if( defined($ret1) );
  811. $ret .= $ret2
  812. if( defined($ret2) );
  813. if( defined($ret1) || defined($ret2) ){
  814. return "OWCOUNT: Could not get values from device $name, reason: ".$ret;
  815. }
  816. #-- both counters will be returned
  817. return "OWCOUNT: $name.counters => ".$hash->{READINGS}{"state"}{VAL};
  818. }
  819. }
  820. }
  821. #######################################################################################
  822. #
  823. # OWCOUNT_GetPage - Get a memory page
  824. #
  825. # Parameter hash = hash of device addressed
  826. # page = page addressed
  827. # final= 1 if FormatValues is to be called
  828. #
  829. ########################################################################################
  830. sub OWCOUNT_GetPage ($$$@) {
  831. my ($hash, $page,$final,$sync) = @_;
  832. #-- get memory page/counter according to interface type
  833. my $master= $hash->{IODev};
  834. my $interface= $hash->{IODev}->{TYPE};
  835. my $name = $hash->{NAME};
  836. my $ret;
  837. #-- check if memory usage has been disabled
  838. my $nomemory = defined($attr{$name}{"nomemory"}) ? $attr{$name}{"nomemory"} : 0;
  839. #-- even if memory usage has been disabled, we need to read the page because it contains the counter values
  840. if( ($nomemory==0) || ($nomemory==1 && (($page==14)||($page==15))) ){
  841. #-- OWX interface
  842. if( $interface eq "OWX" ){
  843. $ret = OWXCOUNT_GetPage($hash,$page,$final);
  844. }elsif( $interface eq "OWX_ASYNC" ){
  845. eval {
  846. if ($sync) {
  847. $ret = OWX_ASYNC_RunToCompletion($hash,OWXCOUNT_PT_GetPage($hash,$page,$final));
  848. } else {
  849. OWX_ASYNC_Schedule( $hash, OWXCOUNT_PT_GetPage($hash,$page,$final) );
  850. }
  851. };
  852. $ret = GP_Catch($@) if $@;
  853. #-- OWFS interface
  854. }elsif( $interface eq "OWServer" ){
  855. $ret = OWFSCOUNT_GetPage($hash,$page,$final);
  856. #-- Unknown interface
  857. }else{
  858. return "OWCOUNT: GetPage with wrong IODev type $interface";
  859. }
  860. #-- process results
  861. if( defined($ret) ){
  862. return "OWCOUNT: Could not get values from device $name, reason: ".$ret;
  863. }
  864. }
  865. return undef
  866. }
  867. ########################################################################################
  868. #
  869. # OWCOUNT_GetMonth Read monthly data from a file
  870. #
  871. # Parameter hash
  872. #
  873. # Returns total value up to last day, including this day and average including this day
  874. #
  875. ########################################################################################
  876. sub OWCOUNT_GetMonth($) {
  877. my ($hash) = @_;
  878. my $name = $hash->{NAME};
  879. my $regexp = ".*$name.*";
  880. my $val;
  881. my @month = ();
  882. my @month2 = ();
  883. my @mchannel;
  884. my @linarr;
  885. my $day;
  886. my $line;
  887. my ($total,$total2,$daily,$deltim,$av);
  888. #-- Check current logfile
  889. my $ln = $attr{$name}{"LogM"};
  890. if( !(defined($ln))){
  891. return "attribute LogM is missing";
  892. }
  893. #-- get channel names
  894. OWCOUNT_ChannelNames($hash);
  895. my $lf = $defs{$ln}{currentlogfile};
  896. if( !(defined($lf))){
  897. return "logfile of LogM is missing";
  898. }
  899. #-- current date
  900. my ($csec,$cmin,$chour,$cday,$cmonth,$cyear,$cwday,$cyday,$cisdst) = localtime(time);
  901. my $ret = open(OWXFILE, "< $lf" );
  902. if( $ret) {
  903. while( <OWXFILE> ){
  904. #-- line looks as
  905. # 2013-06-25_23:57:57 DG.CT1 day: D25 W: 42.2 kWh Wm: 84.4 kWh B: 2287295.7 cts Bm: 2270341.0 cts
  906. $line = $_;
  907. chomp($line);
  908. if ( $line =~ m/$regexp/i){
  909. @linarr = split(' ',$line);
  910. if( int(@linarr)==4+6*int(@owg_fixed) ){
  911. $day = $linarr[3];
  912. $day =~ s/D_+0+//;
  913. @mchannel = ();
  914. for (my $i=0;$i<int(@owg_fixed);$i++){
  915. $val = $linarr[5+6*$i];
  916. push(@mchannel,$val);
  917. }
  918. push(@month,[@mchannel]);
  919. }
  920. }
  921. }
  922. if( int(@month)<($cday-1) ){
  923. Log3 $name,3, "OWCOUNT: warning, found only ".int(@month)." lines in logfile $lf of LogM";
  924. }
  925. } else {
  926. return "cannot open logfile of LogM";
  927. }
  928. #-- sum and average
  929. for (my $i=0;$i<int(@owg_fixed);$i++){
  930. $total = 0.0;
  931. #-- summing only if mode daily (means daily reset !)
  932. $daily = 0;
  933. if( defined($attr{$name}{$owg_fixed[$i]."Mode"} )){
  934. if( $attr{$name}{$owg_fixed[$i]."Mode"} eq "daily"){
  935. $daily = 1;
  936. }
  937. }
  938. if( $daily==1){
  939. for (my $j=0;$j<int(@month);$j++){
  940. $total += $month[$j][$i];
  941. }
  942. }
  943. #-- add data from current day also for non-summed mode
  944. $total = int($total*100)/100;
  945. $total2 = int(100*($total+$hash->{READINGS}{$owg_channel[$i]}{VAL}))/100;
  946. #-- number of days so far, including the present day
  947. my $deltim = int(@month)+($chour+$cmin/60.0 + $csec/3600.0)/24.0;
  948. my $av = $deltim>0 ? int(100*$total2/$deltim)/100 : -1;
  949. #-- output format
  950. push(@month2,[($total,$total2,$av)]);
  951. }
  952. return @month2;
  953. }
  954. ########################################################################################
  955. #
  956. # OWCOUNT_GetYear Read yearly data from a file
  957. #
  958. # Parameter hash
  959. #
  960. # Returns total value up to last month including this month and average including this day
  961. #
  962. ########################################################################################
  963. sub OWCOUNT_GetYear($) {
  964. my ($hash) = @_;
  965. my $name = $hash->{NAME};
  966. my $regexp = ".*$name.*";
  967. my $val;
  968. my @year = ();
  969. my @year2 = ();
  970. my @mchannel;
  971. my @linarr;
  972. my $month;
  973. my $line;
  974. my ($total,$total2,$daily,$deltim,$av);
  975. #-- Check current logfile
  976. my $ln = $attr{$name}{"LogY"};
  977. if( !(defined($ln))){
  978. return "attribute LogY is missing";
  979. }
  980. #-- get channel names
  981. OWCOUNT_ChannelNames($hash);
  982. #-- current date
  983. my ($csec,$cmin,$chour,$cday,$cmonth,$cyear,$cwday,$cyday,$cisdst) = localtime(time);
  984. my $lf = $defs{$ln}{currentlogfile};
  985. if( !(defined($lf))){
  986. return "logfile of LogY is missing";
  987. }
  988. my $ret = open(OWXFILE, "< $lf" );
  989. if( $ret) {
  990. while( <OWXFILE> ){
  991. #-- line looks as
  992. # 2013-05-31_23:57:57 DG.CT1 month: M05 W: 42.2 kWh Wy: 84.4 kWh B: 2287295.7 cts By: 2270341.0 cts
  993. $line = $_;
  994. chomp($line);
  995. if ( $line =~ m/$regexp/i){
  996. @linarr = split(' ',$line);
  997. if( int(@linarr)==4+6*int(@owg_fixed) ){
  998. $month = $linarr[3];
  999. $month =~ s/M_+0+//;
  1000. @mchannel = ();
  1001. for (my $i=0;$i<int(@owg_fixed);$i++){
  1002. $val = $linarr[5+6*$i];
  1003. push(@mchannel,$val);
  1004. }
  1005. push(@year,[@mchannel]);
  1006. }
  1007. }
  1008. }
  1009. if( int(@year)<($cmonth-1) ){
  1010. Log3 $name,3, "OWCOUNT: warning, found only ".int(@year)." lines in logfile $lf of LogY";
  1011. }
  1012. } else {
  1013. return "cannot open logfile of LogY";
  1014. }
  1015. #-- sum and average
  1016. my @month2 = OWCOUNT_GetMonth($hash);
  1017. for (my $i=0;$i<int(@owg_fixed);$i++){
  1018. $total = 0.0;
  1019. $daily = 0;
  1020. if( defined($attr{$name}{$owg_fixed[$i]."Mode"} )){
  1021. if( $attr{$name}{$owg_fixed[$i]."Mode"} eq "daily"){
  1022. $daily = 1;
  1023. }
  1024. }
  1025. #-- sum previous months only in daily mode
  1026. if( $daily==1){
  1027. for (my $j=0;$j<int(@year);$j++){
  1028. $total += $year[$j][$i];
  1029. }
  1030. }else{
  1031. $total = $year[int(@year)-1][$i]
  1032. if (int(@year)>0);
  1033. };
  1034. $total = int($total*100)/100;
  1035. #-- data from this month is
  1036. $total2 = int(100*($month2[$i]->[1]))/100;
  1037. #-- add data from this month and previous months in daily mode
  1038. $total2+=$total
  1039. if( $daily==1 );
  1040. #-- number of days so far, including the present day
  1041. my $deltim = $cyday+($chour+$cmin/60.0 + $csec/3600.0)/24.0;
  1042. my $av = $deltim>0 ? int(100*$total2/$deltim)/100 : -1;
  1043. #-- output format
  1044. push(@year2,[($total,$total2,$av)]);
  1045. }
  1046. return @year2;
  1047. }
  1048. #######################################################################################
  1049. #
  1050. # OWCOUNT_GetValues - Updates the reading from one device
  1051. #
  1052. # Parameter hash = hash of device addressed
  1053. #
  1054. ########################################################################################
  1055. sub OWCOUNT_GetValues($) {
  1056. my $hash = shift;
  1057. my $name = $hash->{NAME};
  1058. my $model = $hash->{OW_MODEL};
  1059. my $value = "";
  1060. my $ret = "";
  1061. my ($ret1,$ret2);
  1062. #-- check if device needs to be initialized
  1063. OWCOUNT_InitializeDevice($hash)
  1064. if( $hash->{READINGS}{"state"}{VAL} eq "defined");
  1065. #-- restart timer for updates
  1066. RemoveInternalTimer($hash);
  1067. InternalTimer(time()+$hash->{INTERVAL}, "OWCOUNT_GetValues", $hash, 0);
  1068. #-- Get readings
  1069. $ret1 = OWCOUNT_GetPage($hash,14,0);
  1070. $ret2 = OWCOUNT_GetPage($hash,15,1);
  1071. #-- process results
  1072. $ret .= $ret1
  1073. if( defined($ret1) );
  1074. $ret .= $ret2
  1075. if( defined($ret2) );
  1076. if( $ret ne "" ){
  1077. return "OWCOUNT: Could not get values from device $name, reason: ".$ret;
  1078. }
  1079. return undef;
  1080. }
  1081. #######################################################################################
  1082. #
  1083. # OWCOUNT_ParseMidnight - Read the stored midnight value
  1084. #
  1085. # Parameter hash = hash of device addressed
  1086. # strval = data string
  1087. # page = page number
  1088. #
  1089. ########################################################################################
  1090. sub OWCOUNT_ParseMidnight($$$) {
  1091. my ($hash,$strval,$page) = @_;
  1092. #-- midnight value
  1093. #-- new format
  1094. if ( defined $strval and $strval =~ /^\d\d\d\d-\d\d-\d\d.*/ ) {
  1095. my @data=split(' ',$strval);
  1096. $strval = $data[2];
  1097. }
  1098. if ( defined $strval ) {
  1099. #-- parse float from midnight
  1100. $strval =~ s/[^-\d\.]+//g;
  1101. $strval = 0.0 if($strval !~ /^-?\d+\.\d*$/);
  1102. $strval = int($strval*100)/100;
  1103. } else {
  1104. $strval = 0.0;
  1105. }
  1106. $hash->{owg_midnight}->[$page-14] = $strval;
  1107. }
  1108. #######################################################################################
  1109. #
  1110. # OWCOUNT_Set - Set one value for device
  1111. #
  1112. # Parameter hash = hash of device addressed
  1113. # a = argument array
  1114. #
  1115. ########################################################################################
  1116. sub OWCOUNT_Set($@) {
  1117. my ($hash, @a) = @_;
  1118. my $key = $a[1];
  1119. my $value = $a[2];
  1120. #-- for the selector: which values are possible
  1121. if (@a == 2){
  1122. my $newkeys = join(" ", keys %sets);
  1123. return $newkeys ;
  1124. }
  1125. #-- check syntax
  1126. return "OWCOUNT: Set needs at least one parameter"
  1127. if( int(@a)<3 );
  1128. #-- check argument
  1129. if( !defined($sets{$a[1]}) ){
  1130. return "OWCOUNT: Set with unknown argument $a[1]";
  1131. }
  1132. #-- define vars
  1133. my $ret = undef;
  1134. my $page;
  1135. my $data;
  1136. my $name = $hash->{NAME};
  1137. my $model = $hash->{OW_MODEL};
  1138. my ($cname,@cnama,@channel);
  1139. #-- set new timer interval
  1140. if($key eq "interval") {
  1141. # check value
  1142. return "OWCOUNT: Set with short interval, must be > 1"
  1143. if(int($value) < 1);
  1144. # update timer
  1145. $hash->{INTERVAL} = $value;
  1146. RemoveInternalTimer($hash);
  1147. InternalTimer(gettimeofday()+$hash->{INTERVAL}, "OWCOUNT_GetValues", $hash, 0);
  1148. return undef;
  1149. }
  1150. #-- get channel names
  1151. OWCOUNT_ChannelNames($hash);
  1152. #-- set memory page/counter according to interface type
  1153. my $interface= $hash->{IODev}->{TYPE};
  1154. #-- check syntax for setting memory page 0..13
  1155. my $nomemory = defined($attr{$name}{"nomemory"}) ? $attr{$name}{"nomemory"} : 0;
  1156. if( $key eq "memory" ){
  1157. return "OWCOUNT: Memory usage disabled"
  1158. if( $nomemory==1 );
  1159. return "OWCOUNT: Set needs parameter when writing memory: <page>"
  1160. if( int(@a)<2 );
  1161. $page=int($a[2]);
  1162. if( ($page<0) || ($page>13) ){
  1163. return "OWXCOUNT: Wrong memory page write attempted";
  1164. }
  1165. $data=$a[3];
  1166. for( my $i=4;$i<int(@a);$i++){
  1167. $data.=" ".$a[$i];
  1168. }
  1169. if( length($data) > 32 ){
  1170. Log3 $name,3,"OWCOUNT: Memory data truncated to 32 characters";
  1171. $data=substr($data,0,32);
  1172. }elsif( length($data) < 32 ){
  1173. for(my $i=length($data)-1;$i<32;$i++){
  1174. $data.=" ";
  1175. }
  1176. }
  1177. $ret = OWCOUNT_SetPage($hash,$page,$data);
  1178. }
  1179. #-- other commands are per channel
  1180. if( ($key eq "midnight") || ($key eq "counter" )){
  1181. return "OWCOUNT: Set $key needs parameter: <channel>"
  1182. if( int(@a)<2 );
  1183. #-- find out which channel we have
  1184. if( ($a[2] eq $owg_channel[0]) || ($a[2] eq "A") ){
  1185. $page=14;
  1186. }elsif( ($a[2] eq $owg_channel[1]) || ($a[2] eq "B") ){
  1187. $page=15;
  1188. } else {
  1189. return "OWCOUNT: Invalid counter address, must be A, B or defined channel name"
  1190. }
  1191. #-- mode normal or daily
  1192. my $daily = 0;
  1193. if( defined($attr{$name}{$owg_fixed[$page-14]."Mode"} )){
  1194. if( $attr{$name}{$owg_fixed[$page-14]."Mode"} eq "daily"){
  1195. $daily = 1;
  1196. }
  1197. }
  1198. return "OWCOUNT: Set $key for channel $a[2] not possible, is not in daily mode"
  1199. if( $daily==0 );
  1200. my ($sec, $min, $hour, $day, $month, $year, $wday,$yday,$isdst) = localtime(time);
  1201. if( $key eq "midnight" ){
  1202. $data = sprintf("%4d-%02d-%02d midnight %7.2f",
  1203. $year+1900,$month+1,$day,$a[3]);
  1204. $ret = OWCOUNT_SetPage($hash,$page,$data);
  1205. }
  1206. #--
  1207. if( $key eq "counter" ){
  1208. my $midnew=($hash->{owg_val}->[$page-14] + $hash->{READINGS}{$owg_channel[$page-14]}{OFFSET})*
  1209. $hash->{READINGS}{$owg_channel[$page-14]}{FACTOR} - $a[3];
  1210. $data = sprintf("%4d-%02d-%02d midnight %7.2f",
  1211. $year+1900,$month+1,$day,$midnew);
  1212. Log3 $name,5,"OWCOUNT: old midnight vakue ".$hash->{owg_midnight}->[$page-14].", replaced by $midnew";
  1213. $ret = OWCOUNT_SetPage($hash,$page,$data);
  1214. }
  1215. }
  1216. #-- process results - we have to reread the device
  1217. if( defined($ret) && ($ret ne "") ){
  1218. return "OWCOUNT: Could not set device $name, reason: ".$ret;
  1219. }
  1220. OWCOUNT_GetValues($hash);
  1221. Log3 $name,5, "OWCOUNT: Set $hash->{NAME} $key $value";
  1222. }
  1223. #######################################################################################
  1224. #
  1225. # OWCOUNT_SetPage - Set one value for device
  1226. #
  1227. # Parameter hash = hash of device addressed
  1228. # page = page addressed
  1229. # data = data
  1230. #
  1231. ########################################################################################
  1232. sub OWCOUNT_SetPage ($$$) {
  1233. my ($hash, $page, $data) = @_;
  1234. #-- set memory page/counter according to interface type
  1235. my $interface= $hash->{IODev}->{TYPE};
  1236. my $name = $hash->{NAME};
  1237. my $ret;
  1238. #-- check if memory usage has been disabled
  1239. my $nomemory = defined($attr{$name}{"nomemory"}) ? $attr{$name}{"nomemory"} : 0;
  1240. if( $nomemory==0 ){
  1241. #-- OWX interface
  1242. if( $interface eq "OWX" ){
  1243. $ret = OWXCOUNT_SetPage($hash,$page,$data);
  1244. }elsif( $interface eq "OWX_ASYNC" ){
  1245. eval {
  1246. OWX_ASYNC_Schedule( $hash, OWXCOUNT_PT_SetPage($hash,$page,$data) );
  1247. };
  1248. $ret = GP_Catch($@) if $@;
  1249. #-- OWFS interface
  1250. }elsif( $interface eq "OWServer" ){
  1251. $ret = OWFSCOUNT_SetPage($hash,$page,$data);
  1252. #-- Unknown interface
  1253. }else{
  1254. return "OWCOUNT: SetPage with wrong IODev type $interface";
  1255. }
  1256. #-- process results
  1257. if( defined($ret) && ($ret ne "") ){
  1258. return "OWCOUNT: Could not set device $name, reason: ".$ret;
  1259. }
  1260. }else{
  1261. if( $page==14 ){
  1262. return OWCOUNT_store($hash,"OWCOUNT_".$name."_14.dat",$data);
  1263. }elsif( $page==15 ){
  1264. return OWCOUNT_store($hash,"OWCOUNT_".$name."_15.dat",$data);
  1265. } else {
  1266. return "OWCOUNT: file store with wrong page number";
  1267. }
  1268. }
  1269. }
  1270. ########################################################################################
  1271. #
  1272. # Store daily start value in a file
  1273. #
  1274. # Parameter hash, filename
  1275. #
  1276. ########################################################################################
  1277. sub OWCOUNT_store($$$) {
  1278. my ($hash,$filename,$data) = @_;
  1279. my $name = $hash->{NAME};
  1280. my $mp = AttrVal("global", "modpath", ".");
  1281. my $ret = open(OWXFILE, "> $mp/FHEM/$filename" );
  1282. if( $ret) {
  1283. print OWXFILE $data;
  1284. Log3 $name,1, "OWCOUNT_store: $name $data";
  1285. close(OWXFILE);
  1286. } else {
  1287. Log3 $name,1,"OWCOUNT_store: Cannot open $filename for writing!";
  1288. }
  1289. return $ret;
  1290. }
  1291. ########################################################################################
  1292. #
  1293. # OWCOUNT_recall Recall daily start value from a file
  1294. #
  1295. # Parameter hash,filename
  1296. #
  1297. ########################################################################################
  1298. sub OWCOUNT_recall($$) {
  1299. my ($hash,$filename) = @_;
  1300. my $name= $hash->{NAME};
  1301. my $mp = AttrVal("global", "modpath", ".");
  1302. my $ret = open(OWXFILE, "< $mp/FHEM/$filename" );
  1303. if( $ret ){
  1304. my $line = readline OWXFILE;
  1305. close(OWXFILE);
  1306. return $line;
  1307. }
  1308. Log3 $name,1, "OWCOUNT_recall: Cannot open $filename for reading, 2nd attempt";
  1309. $ret = OWCOUNT_store($hash,$filename,0);
  1310. if (!$ret){
  1311. $ret = open(OWXFILE, "< $mp/FHEM/$filename" );
  1312. if( $ret ){
  1313. my $line = readline OWXFILE;
  1314. close(OWXFILE);
  1315. return $line;
  1316. }
  1317. Log3 $name,1, "OWCOUNT_recall: Cannot open $filename for reading - tried twice";
  1318. return "error";
  1319. }
  1320. }
  1321. ########################################################################################
  1322. #
  1323. # OWCOUNT_Undef - Implements UndefFn function
  1324. #
  1325. # Parameter hash = hash of device addressed
  1326. #
  1327. ########################################################################################
  1328. sub OWCOUNT_Undef ($) {
  1329. my ($hash) = @_;
  1330. delete($modules{OWCOUNT}{defptr}{$hash->{OW_ID}});
  1331. RemoveInternalTimer($hash);
  1332. return undef;
  1333. }
  1334. ########################################################################################
  1335. #
  1336. # The following subroutines in alphabetical order are only for a 1-Wire bus connected
  1337. # via OWFS
  1338. #
  1339. # TODO: So far no automatic recognition of DS2423 / Emulator
  1340. #
  1341. # Prefix = OWFSCOUNT
  1342. #
  1343. ########################################################################################
  1344. #
  1345. # OWFSCOUNT_GetPage - Get page from device
  1346. #
  1347. # Parameter hash = hash of device addressed
  1348. # page = page to be read
  1349. # final= 1 if FormatValues is to be called
  1350. #
  1351. ########################################################################################
  1352. sub OWFSCOUNT_GetPage($$$) {
  1353. my ($hash,$page,$final) = @_;
  1354. #-- ID of the device
  1355. my $owx_add = substr($hash->{ROM_ID},0,15);
  1356. #-- hash of the busmaster
  1357. my $master = $hash->{IODev};
  1358. my $name = $hash->{NAME};
  1359. #-- reset presence
  1360. $hash->{PRESENT} = 0;
  1361. my $vval;
  1362. my $strval;
  1363. #=============== wrong value requested ===============================
  1364. if( ($page<0) || ($page>15) ){
  1365. return "wrong memory page requested";
  1366. }
  1367. my $nomemory = defined($attr{$name}{"nomemory"}) ? $attr{$name}{"nomemory"} : 0;
  1368. #-- get values - or shoud we rather get the uncached ones ?
  1369. if( $page == 14 || $page == 15 ) {
  1370. $vval = OWServer_Read($master,"/$owx_add/counters.$owg_fixed[$page-14]");
  1371. return "no return from OWServer for counter.$owg_fixed[$page-14]" unless defined $vval;
  1372. return "empty return from OWServer for counter.$owg_fixed[$page-14]" if($vval eq "");
  1373. if ($nomemory == 0) {
  1374. $strval = OWServer_Read($master,"/$owx_add/pages/page.$page");
  1375. return "no return from OWServer for page.$page" unless defined $strval;
  1376. return "empty return from OWServer for page.$page" if($strval eq "");
  1377. } else {
  1378. $strval = OWCOUNT_recall($hash,"OWCOUNT_".$hash->{NAME}."_".$page.".dat");
  1379. }
  1380. $hash->{owg_val}->[$page-14] = $vval;
  1381. $hash->{owg_str}->[$page] = defined $strval ? $strval : "";
  1382. #-- midnight value
  1383. OWCOUNT_ParseMidnight($hash,$strval,$page);
  1384. }else {
  1385. $strval = OWServer_Read($master,"/$owx_add/pages/page.".$page);
  1386. return "no return from OWServer"
  1387. if( !defined($strval) );
  1388. return "empty return from OWServer"
  1389. if( $strval eq "" );
  1390. $hash->{owg_str}->[$page] = $strval;
  1391. }
  1392. #-- and now from raw to formatted values
  1393. $hash->{PRESENT} = 1;
  1394. if($final==1){
  1395. my $value = OWCOUNT_FormatValues($hash);
  1396. Log3 $name,5, $value;
  1397. }
  1398. return undef;
  1399. }
  1400. ########################################################################################
  1401. #
  1402. # OWFSCOUNT_SetPage - Set page in device
  1403. #
  1404. # Parameter hash = hash of device addressed
  1405. #
  1406. ########################################################################################
  1407. sub OWFSCOUNT_SetPage($$$) {
  1408. my ($hash,$page,$data) = @_;
  1409. #-- ID of the device
  1410. my $owx_add = substr($hash->{ROM_ID},0,15);
  1411. #-- hash of the busmaster
  1412. my $master = $hash->{IODev};
  1413. my $name = $hash->{NAME};
  1414. #=============== wrong page requested ===============================
  1415. if( ($page<0) || ($page>15) ){
  1416. return "wrong memory page write attempt";
  1417. }
  1418. #=============== midnight value =====================================
  1419. if( ($page==14) || ($page==15) ){
  1420. OWCOUNT_ParseMidnight($hash,$data,$page);
  1421. }
  1422. OWServer_Write($master, "/$owx_add/pages/page.".$page,$data );
  1423. return undef
  1424. }
  1425. ########################################################################################
  1426. #
  1427. # The following subroutines in alphabetical order are only for a 1-Wire bus connected
  1428. # directly to the FHEM server
  1429. #
  1430. # Prefix = OWXCOUNT
  1431. #
  1432. ########################################################################################
  1433. #
  1434. # OWXCOUNT_BinValues - Process reading from one device - translate binary into raw
  1435. #
  1436. # Parameter hash = hash of device addressed
  1437. # context = mode for evaluating the binary data
  1438. # proc = processing instruction, also passed to OWX_Read.
  1439. # bitwise interpretation !!
  1440. # if 0, nothing special
  1441. # if 1 = bit 0, a reset will be performed not only before, but also after
  1442. # the last operation in OWX_Read
  1443. # if 2 = bit 1, the initial reset of the bus will be suppressed
  1444. # if 8 = bit 3, the fillup of the data with 0xff will be suppressed
  1445. # if 16= bit 4, the insertion will be at the top of the queue
  1446. # owx_dev = ROM ID of slave device
  1447. # crcpart = part of the data that needs to be part of the CRC check
  1448. # numread = number of bytes to receive
  1449. # res = result string
  1450. #
  1451. #
  1452. #######################################################################################
  1453. sub OWXCOUNT_BinValues($$$$$$$) {
  1454. my ($hash, $context, $proc, $owx_dev, $crcpart, $numread, $res) = @_;
  1455. #-- hash of the busmaster
  1456. my $master = $hash->{IODev};
  1457. my $name = $hash->{NAME};
  1458. my @data=[];
  1459. my $value;
  1460. my $msg;
  1461. OWX_WDBG($name,"OWCOUNT_BinValues called for device $name in context $context with ",$res)
  1462. if( $main::owx_debug>2 );
  1463. return undef unless (defined $context and $context =~ /^(get|set|check)page\.([\d]+)(\.final|)$/);
  1464. my $cmd = $1;
  1465. my $page = $2;
  1466. my $final = $3;
  1467. #=============== get memory + counter ===============================
  1468. if ($cmd eq "get") {
  1469. my (@data,$strval,$value);
  1470. my $change = 0;
  1471. @data=split(//,$res);
  1472. #-- process results
  1473. if(ord($data[17])<=0){
  1474. #-- invalid data: do not die, but change memory flags
  1475. if( $page > 13 ){
  1476. $owg_memory[1]=0;
  1477. }else{
  1478. $owg_memory[0]=0;
  1479. }
  1480. $owg_memory[2]=1;
  1481. $msg="memory data could not be read";
  1482. }else{
  1483. if( $page > 13 ){
  1484. $owg_memory[1]=1;
  1485. }else{
  1486. $owg_memory[0]=1;
  1487. }
  1488. $owg_memory[2]=1;
  1489. }
  1490. if( int(@data) < 42){
  1491. $msg="$name returns invalid data length, ".int(@data)." instead of 42 bytes";
  1492. }elsif (OWX_CRC16($crcpart.substr($res,0,40),$data[40],$data[41]) == 0){
  1493. $msg="$name returns invalid CRC, ".ord($data[40])." ".ord($data[41]);
  1494. }
  1495. OWX_WDBG($name,"OWXCOUNT_BinValues: ".$msg,"")
  1496. if( $main::owx_debug>2 );
  1497. #--
  1498. my $nomemory = defined($attr{$name}{"nomemory"}) ? $attr{$name}{"nomemory"} : 0;
  1499. if( $nomemory==0 ){
  1500. #-- memory part, treated as string
  1501. $strval=substr($res,0,32);
  1502. #Log3 $name,5,"OWCOUNT: retrieved from memory for page $page ==> $strval";
  1503. } else {
  1504. $strval = OWCOUNT_recall($hash,"OWCOUNT_".$hash->{NAME}."_".$page.".dat");
  1505. #Log3 $name,5,"OWCOUNT: retrieved from disk for page $page ==> $strval";
  1506. }
  1507. $hash->{owg_str}->[$page]= defined $strval ? $strval : "";
  1508. #-- counter part
  1509. if( ($page == 14) || ($page == 15) ){
  1510. @data=split(//,substr($res,32));
  1511. if ( ($data[4] | $data[5] | $data[6] | $data[7]) ne "\x00" ){
  1512. OWX_WDBG($name, "device $name returns invalid data ".ord($data[4])." ".ord($data[5])." ".ord($data[6])." ".ord($data[7])," ");
  1513. }
  1514. #-- counter value
  1515. $value = (ord($data[3])<<24) + (ord($data[2])<<16) +(ord($data[1])<<8) + ord($data[0]);
  1516. $hash->{owg_val}->[$page-14] = $value;
  1517. #-- midnight value
  1518. #Log3 $name,1, "OWCOUNT_BinValues ParseMidnight: ".(defined $strval ? $strval : "undef");
  1519. OWCOUNT_ParseMidnight($hash,$strval,$page);
  1520. }
  1521. #-- and now from raw to formatted values
  1522. $hash->{PRESENT} = 1;
  1523. if( $final ) {
  1524. my $value = OWCOUNT_FormatValues($hash);
  1525. }
  1526. #=============== set memory ===============================
  1527. #-- for setting a page we need a second step where the scratchpad is copied into memory
  1528. }elsif (($cmd eq "set") && !$final ) {
  1529. my $select="\x5A".substr($res,0,3);
  1530. #### master slave context proc owx_dev data crcpart numread startread callback delay
  1531. # 16 inserts at top of queue
  1532. OWX_Qomplex($master, $hash, "setpage.$page.final", 16, $owx_dev, $select, 0, 6, 10, \&OWXCOUNT_BinValues, 0);
  1533. #-- and a third step where this is finalized
  1534. }elsif (($cmd eq "set") && $final ) {
  1535. if( substr($res,4,1) ne "\xAA" ){
  1536. if( $page > 13 ){
  1537. $owg_memory[1]=0;
  1538. }else{
  1539. $owg_memory[0]=0;
  1540. }
  1541. }else{
  1542. if( $page > 13){
  1543. $owg_memory[1]=1;
  1544. }else{
  1545. $owg_memory[0]=1;
  1546. }
  1547. }
  1548. #-- change applied
  1549. $owg_memory[2]=1;
  1550. }
  1551. return undef;
  1552. }
  1553. ########################################################################################
  1554. #
  1555. # OWXCOUNT_GetPage - Get one memory page + counter from device
  1556. #
  1557. # Parameter hash = hash of device addressed
  1558. # page = 0..15
  1559. # final= 1 if FormatValues is to be called
  1560. #
  1561. ########################################################################################
  1562. sub OWXCOUNT_GetPage($$$) {
  1563. my ($hash,$page,$final) = @_;
  1564. my ($select, $res, $res2, $res3, @data);
  1565. #-- ID of the device, hash of the busmaster
  1566. my $owx_dev = $hash->{ROM_ID};
  1567. my $master = $hash->{IODev};
  1568. #-- reset presence
  1569. $hash->{PRESENT} = 0;
  1570. #=============== wrong value requested ===============================
  1571. if( ($page<0) || ($page>15) ){
  1572. return "wrong memory page requested";
  1573. }
  1574. #=============== get memory + counter ===============================
  1575. #-- issue the match ROM command \x55 and the read memory + counter command
  1576. # \xA5 TA1 TA2 reading 40 data bytes and 2 CRC bytes
  1577. my $ta2 = ($page*32) >> 8;
  1578. my $ta1 = ($page*32) & 255;
  1579. $select=sprintf("\xA5%c%c",$ta1,$ta2);
  1580. my $context = "getpage.".$page.($final ? ".final" : "");
  1581. #-- OLD OWX interface
  1582. if( !$master->{ASYNCHRONOUS} ){
  1583. #-- reset the bus
  1584. OWX_Reset($master);
  1585. #-- reading 9 + 3 + 40 data bytes (32 byte memory, 4 byte counter + 4 byte zeroes) and 2 CRC bytes = 54 bytes
  1586. $res=OWX_Complex($master,$owx_dev,$select,42);
  1587. if( $res eq 0 ){
  1588. return "device $owx_dev not accessible in reading page $page";
  1589. }
  1590. #-- process results
  1591. if( length($res) < 54 ) {
  1592. #Log 1, "OWXCOUNT: warning, have received ".length($res)." bytes in first step";
  1593. #-- read the data in a second step
  1594. $res.=OWX_Complex($master,"","",0);
  1595. #-- process results
  1596. if( length($res) < 54 ) {
  1597. #Log 1, "OWXCOUNT: warning, have received ".length($res)." bytes in second step";
  1598. #-- read the data in a third step
  1599. $res.=OWX_Complex($master,"","",0);
  1600. }
  1601. }
  1602. #-- reset the bus (needed to stop receiving data ?)
  1603. OWX_Reset($master);
  1604. #-- for processing we need 45 bytes
  1605. return "$owx_dev not accessible in reading"
  1606. if( $res eq 0 );
  1607. return "$owx_dev has returned invalid data"
  1608. if( length($res)!=54);
  1609. eval {
  1610. OWXCOUNT_BinValues($hash,$context,0,$owx_dev,$select,45,substr($res,12));
  1611. };
  1612. return $@ ? $@ : undef;
  1613. #-- NEW OWX interface
  1614. }else{
  1615. #### master slave context proc owx_dev data crcpart numread startread callback delay
  1616. # 1 additional reset after last action
  1617. OWX_Qomplex($master, $hash, $context, 1, $owx_dev, $select, $select, 45, 12, \&OWXCOUNT_BinValues, 0);
  1618. return undef;
  1619. }
  1620. }
  1621. ########################################################################################
  1622. #
  1623. # OWXCOUNT_SetPage - Set one memory page of device
  1624. #
  1625. # Parameter hash = hash of device addressed
  1626. # page = 0..15
  1627. # data = string to set into memory
  1628. #
  1629. ########################################################################################
  1630. sub OWXCOUNT_SetPage($$$) {
  1631. my ($hash,$page,$data) = @_;
  1632. my ($select, $res, $res2, $res3);
  1633. #-- ID of the device, hash of the busmaster
  1634. my $owx_dev = $hash->{ROM_ID};
  1635. my $master = $hash->{IODev};
  1636. #=============== wrong page requested ===============================
  1637. if( ($page<0) || ($page>15) ){
  1638. return "wrong memory page write attempt";
  1639. }
  1640. #=============== midnight value =====================================
  1641. if( ($page==14) || ($page==15) ){
  1642. OWCOUNT_ParseMidnight($hash,$data,$page);
  1643. }
  1644. #=============== set memory =========================================
  1645. #-- issue the match ROM command \x55 and the write scratchpad command
  1646. # \x0F TA1 TA2 followed by the data
  1647. my $ta2 = ($page*32) >> 8;
  1648. my $ta1 = ($page*32) & 255;
  1649. #Log 1, "OWXCOUNT: setting page Nr. $ta2 $ta1 $data";
  1650. $select=sprintf("\x0F%c%c",$ta1,$ta2).$data;
  1651. #-- OLD OWX interface
  1652. if( !$master->{ASYNCHRONOUS} ){
  1653. #-- reset the bus
  1654. OWX_Reset($master);
  1655. $res=OWX_Complex($master,$owx_dev,$select,0);
  1656. if( $res eq 0 ){
  1657. return "OWCOUNT: device $owx_dev not accessible in writing scratchpad";
  1658. }
  1659. #-- issue the match ROM command \x55 and the read scratchpad command
  1660. # \xAA, receiving 2 address bytes, 1 status byte and scratchpad content
  1661. $select = "\xAA";
  1662. #-- reset the bus
  1663. OWX_Reset($master);
  1664. #-- reading 9 + 3 + up to 32 bytes
  1665. # TODO: sometimes much less than 28
  1666. $res=OWX_Complex($master,$owx_dev,$select,28);
  1667. if( length($res) < 13 ){
  1668. return "OWCOUNT: device $owx_dev not accessible in reading scratchpad";
  1669. }
  1670. #-- issue the match ROM command \x55 and the copy scratchpad command
  1671. # \x5A followed by 3 byte authentication code obtained in previous read
  1672. $select="\x5A".substr($res,10,3);
  1673. #-- reset the bus
  1674. OWX_Reset($master);
  1675. $res=OWX_Complex($master,$owx_dev,$select,6);
  1676. #-- process results
  1677. if( $res eq 0 ){
  1678. return "OWCOUNT: device $owx_dev not accessible for copying scratchpad";
  1679. }
  1680. if( substr($res,13,1) ne "\xAA" ){
  1681. if( $page > 13){
  1682. $owg_memory[1]=0;
  1683. }else{
  1684. $owg_memory[0]=0;
  1685. }
  1686. }else{
  1687. if( $page > 13){
  1688. $owg_memory[1]=1;
  1689. }else{
  1690. $owg_memory[0]=1;
  1691. }
  1692. }
  1693. $owg_memory[2]=1;
  1694. return undef;
  1695. #-- NEW OWX interface
  1696. }else{
  1697. #### master slave context proc owx_dev data crcpart numread startread callback delay
  1698. # 8 prevents from filling up with 0xFF
  1699. OWX_Qomplex($master, $hash, undef, 8, $owx_dev, $select, $select, 35, 10, undef, 0);
  1700. #-- The third step of copying the scratchpad into memory can be scheduled only when the authorization code has been received
  1701. # will be done in the callback of the following line
  1702. OWX_Qomplex($master, $hash, "setpage.".$page, 0, $owx_dev, "\xAA", 0, 37, 10, \&OWXCOUNT_BinValues, 0);
  1703. }
  1704. return undef;
  1705. }
  1706. ########################################################################################
  1707. #
  1708. # OWXCOUNT_PT_GetPage - Get one memory page + counter from device async
  1709. #
  1710. # Parameter hash = hash of device addressed
  1711. # page = 0..15
  1712. # final= 1 if FormatValues is to be called
  1713. #
  1714. ########################################################################################
  1715. sub OWXCOUNT_PT_GetPage($$$) {
  1716. my ($hash,$page,$final) = @_;
  1717. return PT_THREAD(sub {
  1718. my ($thread) = @_;
  1719. #-- ID of the device, hash of the busmaster
  1720. my $owx_dev = $hash->{ROM_ID};
  1721. my $master = $hash->{IODev};
  1722. PT_BEGIN($thread);
  1723. #=============== wrong value requested ===============================
  1724. if( ($page<0) || ($page>15) ){
  1725. die("wrong memory page requested");
  1726. }
  1727. #=============== get memory + counter ===============================
  1728. #-- issue the match ROM command \x55 and the read memory + counter command
  1729. # \xA5 TA1 TA2 reading 40 data bytes and 2 CRC bytes
  1730. my $ta2 = ($page*32) >> 8;
  1731. my $ta1 = ($page*32) & 255;
  1732. $thread->{'select'}=sprintf("\xA5%c%c",$ta1,$ta2);
  1733. #-- reading 9 + 3 + 40 data bytes (32 byte memory, 4 byte counter + 4 byte zeroes) and 2 CRC bytes = 54 bytes
  1734. $thread->{pt_execute} = OWX_ASYNC_PT_Execute($master,1,$owx_dev, $thread->{'select'}, 42 );
  1735. PT_WAIT_THREAD($thread->{pt_execute});
  1736. die $thread->{pt_execute}->PT_CAUSE() if ($thread->{pt_execute}->PT_STATE() == PT_ERROR);
  1737. $thread->{response} = $thread->{pt_execute}->PT_RETVAL();
  1738. #-- reset the bus (needed to stop receiving data ?)
  1739. $thread->{pt_execute} = OWX_ASYNC_PT_Execute($master,1,undef,undef,undef);
  1740. PT_WAIT_THREAD($thread->{pt_execute});
  1741. die $thread->{pt_execute}->PT_CAUSE() if ($thread->{pt_execute}->PT_STATE() == PT_ERROR);
  1742. if (my $ret = OWXCOUNT_BinValues($hash,"getpage.".$page.($final ? ".final" : ""),0,$owx_dev,$thread->{'select'},0,$thread->{response})) {
  1743. die $ret;
  1744. }
  1745. PT_END;
  1746. });
  1747. }
  1748. ########################################################################################
  1749. #
  1750. # OWXCOUNT_PT_SetPage - Set one memory page of device async
  1751. #
  1752. # Parameter hash = hash of device addressed
  1753. # page = "alarm" or "status"
  1754. #
  1755. ########################################################################################
  1756. sub OWXCOUNT_PT_SetPage($$$) {
  1757. my ($hash,$page,$data) = @_;
  1758. return PT_THREAD(sub {
  1759. my ($thread) = @_;
  1760. my ($res, $response);
  1761. #-- ID of the device, hash of the busmaster
  1762. my $owx_dev = $hash->{ROM_ID};
  1763. my $master = $hash->{IODev};
  1764. PT_BEGIN($thread);
  1765. #=============== wrong page requested ===============================
  1766. if( ($page<0) || ($page>15) ){
  1767. PT_EXIT("wrong memory page write attempt");
  1768. }
  1769. #=============== midnight value =====================================
  1770. if( ($page==14) || ($page==15) ){
  1771. OWCOUNT_ParseMidnight($hash,$data,$page);
  1772. }
  1773. #=============== set memory =========================================
  1774. #-- issue the match ROM command \x55 and the write scratchpad command
  1775. # \x0F TA1 TA2 followed by the data
  1776. my $ta2 = ($page*32) >> 8;
  1777. my $ta1 = ($page*32) & 255;
  1778. #Log 1, "OWXCOUNT: setting page Nr. $ta2 $ta1 $data";
  1779. $thread->{'select'}=sprintf("\x0F%c%c",$ta1,$ta2).$data;
  1780. #"setpage.1"
  1781. $thread->{pt_execute} = OWX_ASYNC_PT_Execute($master,1,$owx_dev, $thread->{'select'}, 0 );
  1782. PT_WAIT_THREAD($thread->{pt_execute});
  1783. die $thread->{pt_execute}->PT_CAUSE() if ($thread->{pt_execute}->PT_STATE() == PT_ERROR);
  1784. #-- issue the match ROM command \x55 and the read scratchpad command
  1785. # \xAA, receiving 2 address bytes, 1 status byte and scratchpad content
  1786. $thread->{'select'} = "\xAA";
  1787. #-- reading 9 + 3 + up to 32 bytes
  1788. # TODO: sometimes much less than 28
  1789. #"setpage.2"
  1790. $thread->{pt_execute} = OWX_ASYNC_PT_Execute($master,1,$owx_dev, $thread->{'select'}, 28 );
  1791. PT_WAIT_THREAD($thread->{pt_execute});
  1792. die $thread->{pt_execute}->PT_CAUSE() if ($thread->{pt_execute}->PT_STATE() == PT_ERROR);
  1793. $res = $thread->{pt_execute}->PT_RETVAL();
  1794. if( length($res) < 13 ){
  1795. PT_EXIT("device $owx_dev not accessible in reading scratchpad");
  1796. }
  1797. #-- issue the match ROM command \x55 and the copy scratchpad command
  1798. # \x5A followed by 3 byte authentication code obtained in previous read
  1799. $thread->{'select'}="\x5A".substr($res,0,3);
  1800. #-- first command, next 2 are address, then data
  1801. #"setpage.3"
  1802. $thread->{pt_execute} = OWX_ASYNC_PT_Execute($master,1,$owx_dev, $thread->{'select'}, 6 );
  1803. PT_WAIT_THREAD($thread->{pt_execute});
  1804. die $thread->{pt_execute}->PT_CAUSE() if ($thread->{pt_execute}->PT_STATE() == PT_ERROR);
  1805. $res = $thread->{pt_execute}->PT_RETVAL();
  1806. #TODO validate whether testing '0' is appropriate with async interface
  1807. #-- process results
  1808. if( $res eq 0 ){
  1809. PT_EXIT("device $owx_dev error copying scratchpad");
  1810. }
  1811. PT_END;
  1812. });
  1813. }
  1814. sub OWXCOUNT_PT_InitializeDevicePage($$$) {
  1815. my ($hash,$page,$newdata) = @_;
  1816. return PT_THREAD(sub {
  1817. my ($thread) = @_;
  1818. my $ret;
  1819. PT_BEGIN($thread);
  1820. $thread->{task} = OWXCOUNT_PT_GetPage($hash,$page,0);
  1821. PT_WAIT_THREAD($thread->{task});
  1822. $ret = $thread->{task}->PT_RETVAL();
  1823. if ($ret) {
  1824. PT_EXIT($ret);
  1825. }
  1826. $thread->{olddata} = $hash->{owg_str}->[14];
  1827. $thread->{task} = OWXCOUNT_PT_SetPage($hash,$page,$newdata);
  1828. PT_WAIT_THREAD($thread->{task});
  1829. $ret = $thread->{task}->PT_RETVAL();
  1830. if ($ret) {
  1831. PT_EXIT($ret);
  1832. }
  1833. $thread->{task} = OWXCOUNT_PT_GetPage($hash,$page,0);
  1834. PT_WAIT_THREAD($thread->{task});
  1835. $ret = $thread->{task}->PT_RETVAL();
  1836. if ($ret) {
  1837. PT_EXIT($ret);
  1838. }
  1839. $thread->{task} = OWXCOUNT_PT_SetPage($hash,$page,$thread->{olddata});
  1840. PT_WAIT_THREAD($thread->{task});
  1841. $ret = $thread->{task}->PT_RETVAL();
  1842. if ($ret) {
  1843. PT_EXIT($ret);
  1844. }
  1845. PT_END;
  1846. });
  1847. }
  1848. 1;
  1849. =pod
  1850. =begin html
  1851. <a name="OWCOUNT"></a>
  1852. <h3>OWCOUNT</h3>
  1853. <p>FHEM module to commmunicate with 1-Wire Counter/RAM DS2423 or its emulation DS2423emu <br />
  1854. <br />This 1-Wire module works with the OWX interface module or with the OWServer interface module
  1855. (prerequisite: Add this module's name to the list of clients in OWServer).
  1856. Please define an <a href="#OWX">OWX</a> device or <a href="#OWServer">OWServer</a> device first. <br/><p/>
  1857. <br /><h4>Example</h4><br />
  1858. <code>define OWC OWCOUNT 1D.CE780F000000 60</code>
  1859. <br />
  1860. <code>attr OWC AName energy|W</code>
  1861. <br />
  1862. <code>attr OWC AUnit kWh</code>
  1863. <br />
  1864. <code>attr OWC APeriod hour</code>
  1865. <br />
  1866. <code>attr OWC ARate power|P</code>
  1867. <br />
  1868. <code>attr OWX_AMode daily</code>
  1869. <br />
  1870. <br />
  1871. <a name="OWCOUNTdefine"></a>
  1872. <h4>Define</h4>
  1873. <p>
  1874. <code>define &lt;name&gt; OWCOUNT [&lt;model&gt;] &lt;id&gt; [&lt;interval&gt;]</code> or <br/>
  1875. <code>define &lt;name&gt; OWCOUNT &lt;fam&gt;.&lt;id&gt; [&lt;interval&gt;]</code>
  1876. <br /><br /> Define a 1-Wire counter.<br /><br /></p>
  1877. <ul>
  1878. <li>
  1879. <code>[&lt;model&gt;]</code><br /> Defines the counter model (and thus 1-Wire family
  1880. id), currently the following values are permitted: <ul>
  1881. <li>model DS2423 with family id 1D (default if the model parameter is
  1882. omitted)</li>
  1883. <li>model DS2423enew with family id 1D - emulator, works like DS2423</li>
  1884. <li>model DS2423eold with family id 1D - emulator, works like DS2423 except that the internal memory is not present</li>
  1885. </ul>
  1886. </li>
  1887. <li>
  1888. <code>&lt;fam&gt;</code>
  1889. <br />2-character unique family id, see above
  1890. </li>
  1891. <li>
  1892. <code>&lt;id&gt;</code>
  1893. <br />12-character unique ROM id of the converter device without family id and CRC
  1894. code </li>
  1895. <li>
  1896. <code>&lt;interval&gt;</code>
  1897. <br />Measurement interval in seconds. The default is 300 seconds. </li>
  1898. </ul>
  1899. <br />
  1900. <a name="OWCOUNTset"></a>
  1901. <h4>Set</h4>
  1902. <ul>
  1903. <li><a name="owcount_interval">
  1904. <code>set &lt;name&gt; interval &lt;int&gt;</code></a><br /> Measurement
  1905. interval in seconds. The default is 300 seconds. </li>
  1906. <li><a name="owcount_memory">
  1907. <code>set &lt;name&gt; memory &lt;page&gt; &lt;string&gt;</code></a><br />Write 32 bytes to
  1908. memory page 0..13 </li>
  1909. <li><a name="owcount_midnight">
  1910. <code>set &lt;name&gt; midnight &lt;channel-name&gt; &lt;val&gt;</code></a><br />Write the
  1911. day's starting value for counter &lt;channel&gt; (A, B or named channel, see
  1912. below)</li>
  1913. <li><a name="owcount_counter">
  1914. <code>set &lt;name&gt; counter &lt;channel-name&gt; &lt;val&gt;</code></a><br />Correct the midnight
  1915. value such that counter &lt;channel&gt; (A, B or named channel, see
  1916. below) displays value &lt;val&gt;</li>
  1917. </ul>
  1918. <br />
  1919. <a name="OWCOUNTget"></a>
  1920. <h4>Get</h4>
  1921. <ul>
  1922. <li><a name="owcount_id">
  1923. <code>get &lt;name&gt; id</code></a>
  1924. <br /> Returns the full 1-Wire device id OW_FAMILY.ROM_ID.CRC </li>
  1925. <li><a name="owcount_present">
  1926. <code>get &lt;name&gt; present</code>
  1927. </a>
  1928. <br /> Returns 1 if this 1-Wire device is present, otherwise 0. </li>
  1929. <li><a name="owcount_interval2">
  1930. <code>get &lt;name&gt; interval</code></a><br />Returns measurement interval in
  1931. seconds. </li>
  1932. <li><a name="owcount_memory2">
  1933. <code>get &lt;name&gt; memory &lt;page&gt;</code></a><br />Obtain 32 bytes from
  1934. memory page 0..13 </li>
  1935. <li><a name="owcount_midnight2">
  1936. <code>get &lt;name&gt; midnight &lt;channel-name&gt;</code></a><br />Obtain the
  1937. day's starting value for counter &lt;channel&gt; (A, B or named channel, see
  1938. below)</li>
  1939. <li><a name="owcount_month">
  1940. <code>get &lt;name&gt; month</code></a><br />Returns cumulated and averaged monthly value if mode=daily, otherwise last day's and averaged value </li>
  1941. <li><a name="owcount_year">
  1942. <code>get &lt;name&gt; year</code></a><br />Returns cumulated and averaged yearly value if mode=daily, otherwise last months's and averaged value </li>
  1943. <li><a name="owcount_raw">
  1944. <code>get &lt;name&gt; raw &lt;channel-name&gt;</code></a><br />Obtain the
  1945. current raw value for counter &lt;channel&gt; (A, B or named channel, see below)</li>
  1946. <li><a name="owcount_counters">
  1947. <code>get &lt;name&gt; counters</code></a><br />Obtain the current value both
  1948. counters</li>
  1949. </ul>
  1950. <br />
  1951. <a name="OWCOUNTattr"></a>
  1952. <h4>Attributes</h4>
  1953. <ul>
  1954. <li><a name="owcount_logm"><code>attr &lt;name&gt; LogM
  1955. &lt;string&gt;</code></a>
  1956. <br />device name (not file name) of monthly log file.</li>
  1957. <li><a name="owcount_logy"><code>attr &lt;name&gt; LogY
  1958. &lt;string&gt;</code></a>
  1959. <br />device name (not file name) of yearly log file.</li>
  1960. <li><a name="owcount_interval2">
  1961. <code>attr &lt;name&gt; interval &lt;int&gt;</code></a>
  1962. <br /> Measurement
  1963. interval in seconds. The default is 300 seconds. </li>
  1964. <li><a name="owcount_nomemory"><code>attr &lt;name&gt; nomemory
  1965. 0|1</code></a>
  1966. <br />when set to 1, midnight values will be stored in files instead of the internal memory.</li>
  1967. </ul>
  1968. <p>For each of the following attributes, the channel identification A,B may be used.</p>
  1969. <ul>
  1970. <li><a name="owcount_cname"><code>attr &lt;name&gt; &lt;channel&gt;Name
  1971. &lt;string&gt;[|&lt;string&gt;]</code></a>
  1972. <br />name for the channel [|short name used in state reading]. </li>
  1973. <li><a name="owcount_cunit"><code>attr &lt;name&gt; &lt;channel&gt;Unit
  1974. &lt;string&</code></a>
  1975. <br />unit of measurement used in state reading (default "cts", set to "none" for empty).</li>
  1976. <li><a name="owcount_crate"><code>attr &lt;name&gt; &lt;channel&gt;Rate
  1977. &lt;string&gt;[|&lt;string&gt;]</code></a>
  1978. <br />name for the channel rate [|short name used in state reading]</li>
  1979. <li><a name="owcount_coffset"><code>attr &lt;name&gt; &lt;channel&gt;Offset
  1980. &lt;float&gt;</code></a>
  1981. <br />offset added to the reading in this channel. </li>
  1982. <li><a name="owcount_cfactor"><code>attr &lt;name&gt; &lt;channel&gt;Factor
  1983. &lt;float&gt;</code></a>
  1984. <br />factor multiplied to (reading+offset) in this channel. </li>
  1985. <li><a name="owcount_cmode"><code>attr &lt;name&gt; &lt;channel&gt;Mode daily |
  1986. normal</code></a>
  1987. <br />determines whether counter is nulled at start of day or running continuously </li>
  1988. <li><a name="owcount_cperiod"><code>attr &lt;name&gt; &lt;channel&gt;Period hour(default) | minute |
  1989. second</code></a>
  1990. <br />period for rate calculation </li>
  1991. <li><a href="#readingFnAttributes">readingFnAttributes</a></li>
  1992. </ul>
  1993. =end html
  1994. =cut