From 35b318560dba0861ee2f66bc2da204f237bb5bd8 Mon Sep 17 00:00:00 2001 From: rudolfkoenig <> Date: Mon, 17 Feb 2014 13:24:29 +0000 Subject: [PATCH] 13_KS300.pm: changed to readings*Update by betateilchen (and me) git-svn-id: https://svn.fhem.de/fhem/trunk@4963 2b470e98-0d58-463d-a4d8-8e2adae1ed80 --- fhem/FHEM/13_KS300.pm | 267 ++++++++++++++++++++++-------------------- 1 file changed, 138 insertions(+), 129 deletions(-) diff --git a/fhem/FHEM/13_KS300.pm b/fhem/FHEM/13_KS300.pm index f55a70acf..657e1679a 100755 --- a/fhem/FHEM/13_KS300.pm +++ b/fhem/FHEM/13_KS300.pm @@ -1,10 +1,18 @@ ############################################## # $Id$ +# +# modified: 2014-02-16 - betateilchen +# - added new reading for windIndex (bft) +# - changed to readingFnAttributes +# - some minor code cleanups +# + package main; use strict; use warnings; +sub KS300_windIndex($); ##################################### sub @@ -21,7 +29,8 @@ KS300_Initialize($) $hash->{UndefFn} = "KS300_Undef"; $hash->{ParseFn} = "KS300_Parse"; $hash->{AttrList} = "IODev do_not_notify:0,1 showtime:0,1 model:ks300 ". - "rainadjustment:0,1 ignore:0,1"; + "rainadjustment:0,1 ignore:0,1 ". + $readingFnAttributes; $hash->{AutoCreate}= { "KS300.*" => { GPLOT => "temp4rain10:Temp/Rain,hum6wind8:Wind/Hum,", @@ -42,7 +51,7 @@ KS300_Define($$) return "Define $a[0]: wrong CODE format: specify a 4 digit hex value" if($a[2] !~ m/^[a-f0-9][a-f0-9][a-f0-9][a-f0-9]$/); - $hash->{CODE} = $a[2]; +# $hash->{CODE} = $a[2]; my $rainunit = ((int(@a) > 3) ? $a[3] : 255); my $windunit = ((int(@a) > 4) ? $a[4] : 1.0); $hash->{CODE} = $a[2]; @@ -51,6 +60,8 @@ KS300_Define($$) $modules{KS300}{defptr}{$a[2]} = $hash; AssignIoPort($hash); + readingsSingleUpdate($hash, 'state', 'defined', 0); + return undef; } @@ -63,7 +74,6 @@ KS300_Undef($$) return undef; } - ##################################### sub KS300_Parse($$) @@ -96,115 +106,106 @@ KS300_Parse($$) my $name= $def->{NAME}; return "" if(IsIgnored($name)); + readingsBeginUpdate($def); my @v; my @txt = ( "rain_raw", "rain", "wind", "humidity", "temperature", - "israining", "unknown1", "type_raw", "unknown3"); + "israining", "unknown1", "type_raw", "unknown3", "windIndex"); my @sfx = ( "(counter)", "(l/m2)", "(km/h)", "(%)", "(Celsius)", - "(yes/no)", "","",""); + "(yes/no)", "","","",""); my %repchanged = ("rain"=>1, "wind"=>1, "humidity"=>1, "temperature"=>1, - "israining"=>1); - - # counter for the change hash - my $n= 1; # 0 is STATE and will b explicitely set + "israining"=>1); # time my $tm = TimeNow(); my $tsecs= time(); # number of non-leap seconds since January 1, 1970, UTC - # The next instr wont work for empty hashes, so we init it now - $def->{READINGS}{$txt[0]}{VAL} = 0 if(!$def->{READINGS}); - my $r = $def->{READINGS}; - - # preset current $rain_raw $v[0] = hex("$a[28]$a[27]$a[26]"); my $rain_raw= $v[0]; # get previous rain_raw my $rain_raw_prev= $rain_raw; - if(defined($r->{rain_raw})) { - ($rain_raw_prev, undef)= split(" ", $r->{rain_raw}{VAL}); # cut off "(counter)" - }; + if(defined(ReadingsVal($name, 'rain_raw', undef))) { + # cut off "(counter)" + ($rain_raw_prev, undef) = split(" ", ReadingsVal($hash, 'rain_raw','')); + } # unadjusted value as default my $rain_raw_adj= $rain_raw; - # get previous rain_raw_adj my $rain_raw_adj_prev= $rain_raw; - if(defined($r->{rain_raw_adj})) { - $rain_raw_adj_prev= $r->{rain_raw_adj}{VAL}; - }; - if(defined($attr{$name}) && - defined($attr{$name}{"rainadjustment"}) && - ($attr{$name}{"rainadjustment"}>0)) { + if(defined(ReadingsVal($name, 'rain_raw_adj', undef))) { + $rain_raw_adj_prev = ReadingsVal($name, 'rain_raw_adj',''); + } - # The rain values delivered by my KS300 randomly switch between two - # different values. The offset between the two values follows no - # identifiable principle. It is even unclear whether the problem is - # caused by KS300 or by FHZ1300. ELV denies any problem with the KS300. - # The problem is known to several people. For instance, see - # http://www.ipsymcon.de/forum/showthread.php?t=3303&highlight=ks300+regen&page=3 - # The following code detects and automatically corrects these offsets. + if( defined($attr{$name}) && + defined($attr{$name}{"rainadjustment"}) && + ($attr{$name}{"rainadjustment"}>0)) { - my $rain_raw_ofs; - my $rain_raw_ofs_prev; - my $tsecs_prev; + # The rain values delivered by my KS300 randomly switch between two + # different values. The offset between the two values follows no + # identifiable principle. It is even unclear whether the problem is + # caused by KS300 or by FHZ1300. ELV denies any problem with the KS300. + # The problem is known to several people. For instance, see + # http://www.ipsymcon.de/forum/showthread.php?t=3303&highlight=ks300+regen&page=3 + # The following code detects and automatically corrects these offsets. - # get previous offet - if(defined($r->{rain_raw_ofs})) { - $rain_raw_ofs_prev= $r->{rain_raw_ofs}{VAL}; - } else{ - $rain_raw_ofs_prev= 0; - } + my $rain_raw_ofs; + my $rain_raw_ofs_prev; + my $tsecs_prev; - # the current offset is the same, but this may change later - $rain_raw_ofs= $rain_raw_ofs_prev; + if(defined(ReadingsVal($name, 'rain_raw_ofs', undef))) { + $rain_raw_ofs_prev = ReadingsVal($name, 'rain_raw_ofs',''); + } else { + $rain_raw_ofs_prev= 0; + } - # get previous tsecs - if(defined($r->{tsecs})) { - $tsecs_prev= $r->{tsecs}{VAL}; - } else{ - $tsecs_prev= 0; # 1970-01-01 - } + # the current offset is the same, but this may change later + $rain_raw_ofs= $rain_raw_ofs_prev; - # detect error condition - # delta is negative or delta is too large - # see http://de.wikipedia.org/wiki/Niederschlagsintensit??t#Niederschlagsintensit.C3.A4t - # during a thunderstorm in middle europe, 50l/m^2 rain may fall per hour - # 50l/(m^2*h) correspond to 200 ticks/h - # Since KS300 sends every 2,5 minutes, a maximum delta of 8 ticks would - # be reasonable. The observed deltas are in most cases 1 or 2 orders - # of magnitude larger. - # The code also handles counter resets after battery replacement + # get previous tsecs + if(defined(ReadingsVal($name, 'tsecs', undef))) { + $tsecs_prev= ReadingsVal($name, 'tsecs', 0); + } else{ + $tsecs_prev= 0; # 1970-01-01 + } - my $rain_raw_delta= $rain_raw- $rain_raw_prev; - if($tsecs!= $tsecs_prev) { # avoids a rare but relevant condition - my $thours_delta= ($tsecs- $tsecs_prev)/3600.0; # in hours - my $rain_raw_per_hour= $rain_raw_delta/$thours_delta; - if(($rain_raw_delta<0) || ($rain_raw_per_hour> 200.0)) { - $rain_raw_ofs= $rain_raw_ofs_prev-$rain_raw_delta; - # If the switch in the tick count occurs simultaneously with an - # increase due to rain, the tick is lost. We therefore assume that - # offsets between -5 and 0 are indeed rain. - if(($rain_raw_ofs>=-5) && ($rain_raw_ofs<0)) { $rain_raw_ofs= 0; } - $r->{rain_raw_ofs}{TIME} = $tm; - $r->{rain_raw_ofs}{VAL} = $rain_raw_ofs; - $def->{CHANGED}[$n++] = "rain_raw_ofs: $rain_raw_ofs"; - } - } - $rain_raw_adj= $rain_raw+ $rain_raw_ofs; + # detect error condition + # delta is negative or delta is too large + # see http://de.wikipedia.org/wiki/Niederschlagsintensit??t#Niederschlagsintensit.C3.A4t + # during a thunderstorm in middle europe, 50l/m^2 rain may fall per hour + # 50l/(m^2*h) correspond to 200 ticks/h + # Since KS300 sends every 2,5 minutes, a maximum delta of 8 ticks would + # be reasonable. The observed deltas are in most cases 1 or 2 orders + # of magnitude larger. + # The code also handles counter resets after battery replacement + + my $rain_raw_delta= $rain_raw- $rain_raw_prev; + if($tsecs!= $tsecs_prev) { # avoids a rare but relevant condition + my $thours_delta= ($tsecs- $tsecs_prev)/3600.0; # in hours + my $rain_raw_per_hour= $rain_raw_delta/$thours_delta; + if(($rain_raw_delta<0) || ($rain_raw_per_hour> 200.0)) { + $rain_raw_ofs= $rain_raw_ofs_prev-$rain_raw_delta; + + # If the switch in the tick count occurs simultaneously with an + # increase due to rain, the tick is lost. We therefore assume that + # offsets between -5 and 0 are indeed rain. + + if(($rain_raw_ofs>=-5) && ($rain_raw_ofs<0)) { $rain_raw_ofs= 0; } + + readingsBulkUpdate($def, 'rain_raw_ofs', $rain_raw_ofs, 0); + } + } + $rain_raw_adj= $rain_raw+ $rain_raw_ofs; } # remember tsecs - $r->{tsecs}{TIME} = $tm; - $r->{tsecs}{VAL} = "$tsecs"; + readingsBulkUpdate($def, 'tsecs', $tsecs, 0); # remember rain_raw_adj - $r->{rain_raw_adj}{TIME} = $tm; - $r->{rain_raw_adj}{VAL} = $rain_raw_adj; - + readingsBulkUpdate($def, 'rain_raw_adj', $rain_raw_adj, 0); # KS300 has a sensor which detects any drop of rain and immediately # sends out the israining message. The sensors consists of two parallel @@ -213,6 +214,7 @@ KS300_Parse($$) # drop runs along only one side and the contact is not closed. To get the # israining information anyway, the respective flag is also set when the # a positive amount of rain is detected. + $haverain = 1 if($rain_raw_adj != $rain_raw_adj_prev); $v[1] = sprintf("%0.1f", $rain_raw_adj * $def->{RAINUNIT} / 1000); @@ -220,11 +222,11 @@ KS300_Parse($$) $v[3] = "$a[22]$a[21]" + 0; $v[4] = "$a[20]$a[19].$a[18]" + 0; $v[4] = "-$v[4]" if($a[17] eq "7"); $v[4] = sprintf("%0.1f", $v[4]); - $v[5] = ((hex($a[17]) & 0x2) || $haverain) ? "yes" : "no"; $v[6] = $a[29]; $v[7] = $a[16]; $v[8] = $a[17]; + $v[9] = KS300_windIndex($v[2]); # Negative temp $v[4] = -$v[4] if($v[8] & 8); @@ -234,34 +236,28 @@ KS300_Parse($$) my $max = int(@v); # For logging/summary - my $val = "T: $v[4] H: $v[3] W: $v[2] R: $v[1] IR: $v[5]"; + my $val = "T: $v[4] H: $v[3] W: $v[2] R: $v[1] IR: $v[5] Wi: $v[9]"; Log3 $def, 4, "KS300 $dev: $val"; - $def->{STATE} = $val; - $def->{CHANGED}[0] = $val; + readingsBulkUpdate($def,'state', $val); for(my $i = 0; $i < $max; $i++) { - $r->{$txt[$i]}{TIME} = $tm; - #$val = "$v[$i] $sfx[$i]"; - $val = $v[$i]; - $r->{$txt[$i]}{VAL} = $val; - $def->{CHANGED}[$n++] = "$txt[$i]: $val" + readingsBulkUpdate($def, $txt[$i], $v[$i]) if(defined($repchanged{$txt[$i]})); } + ################################### # AVG computing - if(!$r->{cum_day}) { - $r->{cum_day}{VAL} = "$tm T: 0 H: 0 W: 0 R: $v[1]"; - $r->{avg_day}{VAL} = "T: $v[4] H: $v[3] W: $v[2] R: $v[1]"; + if(!ReadingsVal($name, 'cum_day', undef)) { + readingsBulkUpdate($def, 'cum_day', "$tm T: 0 H: 0 W: 0 R: $v[1]", 0); } else { - my @cv = split(" ", $r->{cum_day}{VAL}); + my @cv = split(" ", ReadingsVal($name, 'cum_day','')); + my @cd = split("[ :-]", ReadingsTimestamp($name, 'cum_day','')); - my @cd = split("[ :-]", $r->{cum_day}{TIME}); my $csec = 3600*$cd[3] + 60*$cd[4] + $cd[5]; # Sec of last reading - my @d = split("[ :-]", $tm); my $sec = 3600*$d[3] + 60*$d[4] + $d[5]; # Sec now @@ -276,56 +272,47 @@ KS300_Parse($$) my $w = $cv[7] + $difft * $v[2]; my $e = $cv[9]; - $r->{cum_day}{VAL} = "$cv[0] $cv[1] T: $t H: $h W: $w R: $e"; + $val = "$cv[0] $cv[1] T: $t H: $h W: $w R: $e"; + readingsBulkUpdate($def, 'cum_day', $val, 0); $difft = $sec - $ssec; - $difft += 86400 if($d[2] != $sd[2]); # Sec since last reading - - $difft = 1 if(!$difft); # Don't want illegal division. + $difft += 86400 if($d[2] != $sd[2]); # Sec since last reading + $difft = 1 if(!$difft); # Don't want illegal division. $t /= $difft; $h /= $difft; $w /= $difft; $e = $v[1] - $cv[9]; - $r->{avg_day}{VAL} = - sprintf("T: %.1f H: %d W: %.1f R: %.1f", $t, $h, $w, $e); - if($d[2] != $sd[2]) { # Day changed, report it - $def->{CHANGED}[$n++] = "avg_day $r->{avg_day}{VAL}"; - $r->{cum_day}{VAL} = "$tm T: 0 H: 0 W: 0 R: $v[1]"; + if($d[2] != $sd[2]) { # Day changed + $val = sprintf("T: %.1f H: %d W: %.1f R: %.1f", $t, $h, $w, $e); + readingsBulkUpdate($def, 'avg_day', $val); + $val = "$tm T: 0 H: 0 W: 0 R: $v[1]"; + readingsBulkUpdate($def, 'cum_day', $val, 0); - if(!$r->{cum_month}) { # Check the month + if(!ReadingsVal($name, 'cum_month', undef)) { + $val = "1 ".ReadingsVal($name, 'avg_day',''); + readingsBulkUpdate($def, 'cum_month', $val, 0); - $r->{cum_month}{VAL} = "1 $r->{avg_day}{VAL}"; - $r->{avg_month}{VAL} = $r->{avg_day}{VAL}; - - } else { - - my @cmv = split(" ", $r->{cum_month}{VAL}); - $t += $cmv[2]; $w += $cmv[4]; $h += $cmv[6]; - - $cmv[0]++; - $r->{cum_month}{VAL} = - sprintf("%d T: %.1f H: %d W: %.1f R: %.1f", - $cmv[0], $t, $h, $w, $cmv[8]+$e); - $r->{avg_month}{VAL} = - sprintf("T: %.1f H: %d W: %.1f R: %.1f", - $t/$cmv[0], $h/$cmv[0], $w/$cmv[0], $cmv[8]+$e); - - if($d[1] != $sd[1]) { # Month changed, report it - - $def->{CHANGED}[$n++] = "avg_month $r->{avg_month}{VAL}"; - $r->{cum_month}{VAL} = "0 T: 0 H: 0 W: 0 R: 0"; - - } + } else { + my @cmv = split(" ", ReadingsVal($name, 'cum_month','')); + $t += $cmv[2]; $w += $cmv[4]; $h += $cmv[6]; + $cmv[0]++; + $val = sprintf("%d T: %.1f H: %d W: %.1f R: %.1f", + $cmv[0], $t, $h, $w, $cmv[8]+$e); + readingsBulkUpdate($def, 'cum_month', $val, 0); + if($d[1] != $sd[1]) { # Month changed, report it + $val = sprintf("T: %.1f H: %d W: %.1f R: %.1f", + $t/$cmv[0], $h/$cmv[0], $w/$cmv[0], $cmv[8]+$e); + readingsBulkUpdate($def, 'avg_month', $val); + $val = "0 T: 0 H: 0 W: 0 R: 0"; + readingsBulkUpdate($def, 'cum_month', $val, 0); + } } - $r->{cum_month}{TIME} = $r->{avg_month}{TIME} = $tm; - } - } - $r->{cum_day}{TIME} = $r->{avg_day}{TIME} = $tm; # AVG computing ################################### + readingsEndUpdate($def,1); return $name; } else { @@ -334,7 +321,29 @@ KS300_Parse($$) return "UNDEFINED KS300 KS300 1234"; } +} +sub +KS300_windIndex($) +{ + # + # convert km/h to bft as described by + # http://www.meteotest.ch/wetterprognosen/prognosen_schweiz/windtabelle + # + my ($w) = @_; + return "0" if($w < 1); + return "1" if($w >= 1 && $w < 6); + return "2" if($w >= 6 && $w < 12); + return "3" if($w >= 12 && $w < 20); + return "4" if($w >= 20 && $w < 29); + return "5" if($w >= 29 && $w < 39); + return "6" if($w >= 39 && $w < 50); + return "7" if($w >= 50 && $w < 62); + return "8" if($w >= 62 && $w < 75); + return "9" if($w >= 75 && $w < 89); + return "10" if($w >= 89 && $w < 103); + return "11" if($w >= 103 && $w <= 117); + return "12" if($w > 117); } 1;