13_KS300.pm: changed to readings*Update by betateilchen (and me)

git-svn-id: https://svn.fhem.de/fhem/trunk/fhem@4963 2b470e98-0d58-463d-a4d8-8e2adae1ed80
This commit is contained in:
rudolfkoenig 2014-02-17 13:24:29 +00:00
parent 88252794e9
commit 6a80fc60ba

View File

@ -1,10 +1,18 @@
############################################## ##############################################
# $Id$ # $Id$
#
# modified: 2014-02-16 - betateilchen
# - added new reading for windIndex (bft)
# - changed to readingFnAttributes
# - some minor code cleanups
#
package main; package main;
use strict; use strict;
use warnings; use warnings;
sub KS300_windIndex($);
##################################### #####################################
sub sub
@ -21,7 +29,8 @@ KS300_Initialize($)
$hash->{UndefFn} = "KS300_Undef"; $hash->{UndefFn} = "KS300_Undef";
$hash->{ParseFn} = "KS300_Parse"; $hash->{ParseFn} = "KS300_Parse";
$hash->{AttrList} = "IODev do_not_notify:0,1 showtime:0,1 model:ks300 ". $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}= $hash->{AutoCreate}=
{ "KS300.*" => { { "KS300.*" => {
GPLOT => "temp4rain10:Temp/Rain,hum6wind8:Wind/Hum,", 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" 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]$/); 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 $rainunit = ((int(@a) > 3) ? $a[3] : 255);
my $windunit = ((int(@a) > 4) ? $a[4] : 1.0); my $windunit = ((int(@a) > 4) ? $a[4] : 1.0);
$hash->{CODE} = $a[2]; $hash->{CODE} = $a[2];
@ -51,6 +60,8 @@ KS300_Define($$)
$modules{KS300}{defptr}{$a[2]} = $hash; $modules{KS300}{defptr}{$a[2]} = $hash;
AssignIoPort($hash); AssignIoPort($hash);
readingsSingleUpdate($hash, 'state', 'defined', 0);
return undef; return undef;
} }
@ -63,7 +74,6 @@ KS300_Undef($$)
return undef; return undef;
} }
##################################### #####################################
sub sub
KS300_Parse($$) KS300_Parse($$)
@ -96,115 +106,106 @@ KS300_Parse($$)
my $name= $def->{NAME}; my $name= $def->{NAME};
return "" if(IsIgnored($name)); return "" if(IsIgnored($name));
readingsBeginUpdate($def);
my @v; my @v;
my @txt = ( "rain_raw", "rain", "wind", "humidity", "temperature", 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)", my @sfx = ( "(counter)", "(l/m2)", "(km/h)", "(%)", "(Celsius)",
"(yes/no)", "","",""); "(yes/no)", "","","","");
my %repchanged = ("rain"=>1, "wind"=>1, "humidity"=>1, "temperature"=>1, my %repchanged = ("rain"=>1, "wind"=>1, "humidity"=>1, "temperature"=>1,
"israining"=>1); "israining"=>1);
# counter for the change hash
my $n= 1; # 0 is STATE and will b explicitely set
# time # time
my $tm = TimeNow(); my $tm = TimeNow();
my $tsecs= time(); # number of non-leap seconds since January 1, 1970, UTC 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 # preset current $rain_raw
$v[0] = hex("$a[28]$a[27]$a[26]"); $v[0] = hex("$a[28]$a[27]$a[26]");
my $rain_raw= $v[0]; my $rain_raw= $v[0];
# get previous rain_raw # get previous rain_raw
my $rain_raw_prev= $rain_raw; my $rain_raw_prev= $rain_raw;
if(defined($r->{rain_raw})) { if(defined(ReadingsVal($name, 'rain_raw', undef))) {
($rain_raw_prev, undef)= split(" ", $r->{rain_raw}{VAL}); # cut off "(counter)" # cut off "(counter)"
}; ($rain_raw_prev, undef) = split(" ", ReadingsVal($hash, 'rain_raw',''));
}
# unadjusted value as default # unadjusted value as default
my $rain_raw_adj= $rain_raw; my $rain_raw_adj= $rain_raw;
# get previous rain_raw_adj # get previous rain_raw_adj
my $rain_raw_adj_prev= $rain_raw; 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}) && if(defined(ReadingsVal($name, 'rain_raw_adj', undef))) {
defined($attr{$name}{"rainadjustment"}) && $rain_raw_adj_prev = ReadingsVal($name, 'rain_raw_adj','');
($attr{$name}{"rainadjustment"}>0)) { }
# The rain values delivered by my KS300 randomly switch between two if( defined($attr{$name}) &&
# different values. The offset between the two values follows no defined($attr{$name}{"rainadjustment"}) &&
# identifiable principle. It is even unclear whether the problem is ($attr{$name}{"rainadjustment"}>0)) {
# 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.
my $rain_raw_ofs; # The rain values delivered by my KS300 randomly switch between two
my $rain_raw_ofs_prev; # different values. The offset between the two values follows no
my $tsecs_prev; # 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 my $rain_raw_ofs;
if(defined($r->{rain_raw_ofs})) { my $rain_raw_ofs_prev;
$rain_raw_ofs_prev= $r->{rain_raw_ofs}{VAL}; my $tsecs_prev;
} else{
$rain_raw_ofs_prev= 0;
}
# the current offset is the same, but this may change later if(defined(ReadingsVal($name, 'rain_raw_ofs', undef))) {
$rain_raw_ofs= $rain_raw_ofs_prev; $rain_raw_ofs_prev = ReadingsVal($name, 'rain_raw_ofs','');
} else {
$rain_raw_ofs_prev= 0;
}
# get previous tsecs # the current offset is the same, but this may change later
if(defined($r->{tsecs})) { $rain_raw_ofs= $rain_raw_ofs_prev;
$tsecs_prev= $r->{tsecs}{VAL};
} else{
$tsecs_prev= 0; # 1970-01-01
}
# detect error condition # get previous tsecs
# delta is negative or delta is too large if(defined(ReadingsVal($name, 'tsecs', undef))) {
# see http://de.wikipedia.org/wiki/Niederschlagsintensit??t#Niederschlagsintensit.C3.A4t $tsecs_prev= ReadingsVal($name, 'tsecs', 0);
# during a thunderstorm in middle europe, 50l/m^2 rain may fall per hour } else{
# 50l/(m^2*h) correspond to 200 ticks/h $tsecs_prev= 0; # 1970-01-01
# 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; # detect error condition
if($tsecs!= $tsecs_prev) { # avoids a rare but relevant condition # delta is negative or delta is too large
my $thours_delta= ($tsecs- $tsecs_prev)/3600.0; # in hours # see http://de.wikipedia.org/wiki/Niederschlagsintensit??t#Niederschlagsintensit.C3.A4t
my $rain_raw_per_hour= $rain_raw_delta/$thours_delta; # during a thunderstorm in middle europe, 50l/m^2 rain may fall per hour
if(($rain_raw_delta<0) || ($rain_raw_per_hour> 200.0)) { # 50l/(m^2*h) correspond to 200 ticks/h
$rain_raw_ofs= $rain_raw_ofs_prev-$rain_raw_delta; # Since KS300 sends every 2,5 minutes, a maximum delta of 8 ticks would
# If the switch in the tick count occurs simultaneously with an # be reasonable. The observed deltas are in most cases 1 or 2 orders
# increase due to rain, the tick is lost. We therefore assume that # of magnitude larger.
# offsets between -5 and 0 are indeed rain. # The code also handles counter resets after battery replacement
if(($rain_raw_ofs>=-5) && ($rain_raw_ofs<0)) { $rain_raw_ofs= 0; }
$r->{rain_raw_ofs}{TIME} = $tm; my $rain_raw_delta= $rain_raw- $rain_raw_prev;
$r->{rain_raw_ofs}{VAL} = $rain_raw_ofs; if($tsecs!= $tsecs_prev) { # avoids a rare but relevant condition
$def->{CHANGED}[$n++] = "rain_raw_ofs: $rain_raw_ofs"; 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_adj= $rain_raw+ $rain_raw_ofs; $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 # remember tsecs
$r->{tsecs}{TIME} = $tm; readingsBulkUpdate($def, 'tsecs', $tsecs, 0);
$r->{tsecs}{VAL} = "$tsecs";
# remember rain_raw_adj # remember rain_raw_adj
$r->{rain_raw_adj}{TIME} = $tm; readingsBulkUpdate($def, 'rain_raw_adj', $rain_raw_adj, 0);
$r->{rain_raw_adj}{VAL} = $rain_raw_adj;
# KS300 has a sensor which detects any drop of rain and immediately # KS300 has a sensor which detects any drop of rain and immediately
# sends out the israining message. The sensors consists of two parallel # 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 # 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 # israining information anyway, the respective flag is also set when the
# a positive amount of rain is detected. # a positive amount of rain is detected.
$haverain = 1 if($rain_raw_adj != $rain_raw_adj_prev); $haverain = 1 if($rain_raw_adj != $rain_raw_adj_prev);
$v[1] = sprintf("%0.1f", $rain_raw_adj * $def->{RAINUNIT} / 1000); $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[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] = "$a[20]$a[19].$a[18]" + 0; $v[4] = "-$v[4]" if($a[17] eq "7");
$v[4] = sprintf("%0.1f", $v[4]); $v[4] = sprintf("%0.1f", $v[4]);
$v[5] = ((hex($a[17]) & 0x2) || $haverain) ? "yes" : "no"; $v[5] = ((hex($a[17]) & 0x2) || $haverain) ? "yes" : "no";
$v[6] = $a[29]; $v[6] = $a[29];
$v[7] = $a[16]; $v[7] = $a[16];
$v[8] = $a[17]; $v[8] = $a[17];
$v[9] = KS300_windIndex($v[2]);
# Negative temp # Negative temp
$v[4] = -$v[4] if($v[8] & 8); $v[4] = -$v[4] if($v[8] & 8);
@ -234,34 +236,28 @@ KS300_Parse($$)
my $max = int(@v); my $max = int(@v);
# For logging/summary # 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"; Log3 $def, 4, "KS300 $dev: $val";
$def->{STATE} = $val; readingsBulkUpdate($def,'state', $val);
$def->{CHANGED}[0] = $val;
for(my $i = 0; $i < $max; $i++) { for(my $i = 0; $i < $max; $i++) {
$r->{$txt[$i]}{TIME} = $tm; readingsBulkUpdate($def, $txt[$i], $v[$i])
#$val = "$v[$i] $sfx[$i]";
$val = $v[$i];
$r->{$txt[$i]}{VAL} = $val;
$def->{CHANGED}[$n++] = "$txt[$i]: $val"
if(defined($repchanged{$txt[$i]})); if(defined($repchanged{$txt[$i]}));
} }
################################### ###################################
# AVG computing # AVG computing
if(!$r->{cum_day}) {
$r->{cum_day}{VAL} = "$tm T: 0 H: 0 W: 0 R: $v[1]"; if(!ReadingsVal($name, 'cum_day', undef)) {
$r->{avg_day}{VAL} = "T: $v[4] H: $v[3] W: $v[2] R: $v[1]"; readingsBulkUpdate($def, 'cum_day', "$tm T: 0 H: 0 W: 0 R: $v[1]", 0);
} else { } 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 $csec = 3600*$cd[3] + 60*$cd[4] + $cd[5]; # Sec of last reading
my @d = split("[ :-]", $tm); my @d = split("[ :-]", $tm);
my $sec = 3600*$d[3] + 60*$d[4] + $d[5]; # Sec now 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 $w = $cv[7] + $difft * $v[2];
my $e = $cv[9]; 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 = $sec - $ssec;
$difft += 86400 if($d[2] != $sd[2]); # Sec since last reading $difft += 86400 if($d[2] != $sd[2]); # Sec since last reading
$difft = 1 if(!$difft); # Don't want illegal division.
$difft = 1 if(!$difft); # Don't want illegal division.
$t /= $difft; $h /= $difft; $w /= $difft; $e = $v[1] - $cv[9]; $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}"; if($d[2] != $sd[2]) { # Day changed
$r->{cum_day}{VAL} = "$tm T: 0 H: 0 W: 0 R: $v[1]"; $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}"; } else {
$r->{avg_month}{VAL} = $r->{avg_day}{VAL}; my @cmv = split(" ", ReadingsVal($name, 'cum_month',''));
$t += $cmv[2]; $w += $cmv[4]; $h += $cmv[6];
} else { $cmv[0]++;
$val = sprintf("%d T: %.1f H: %d W: %.1f R: %.1f",
my @cmv = split(" ", $r->{cum_month}{VAL}); $cmv[0], $t, $h, $w, $cmv[8]+$e);
$t += $cmv[2]; $w += $cmv[4]; $h += $cmv[6]; readingsBulkUpdate($def, 'cum_month', $val, 0);
$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";
}
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 # AVG computing
################################### ###################################
readingsEndUpdate($def,1);
return $name; return $name;
} else { } else {
@ -334,7 +321,29 @@ KS300_Parse($$)
return "UNDEFINED KS300 KS300 1234"; 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; 1;