diff --git a/fhem/FHEM/11_FHT.pm b/fhem/FHEM/11_FHT.pm new file mode 100755 index 000000000..502eec381 --- /dev/null +++ b/fhem/FHEM/11_FHT.pm @@ -0,0 +1,369 @@ +############################################## +package main; + +use strict; +use warnings; + +my %codes = ( + "0000.6" => "actuator", + "00002c" => "synctime", # Not verified + "0100.6" => "actuator1", # Not verified (1-8) + "0200.6" => "actuator2", + "0300.6" => "actuator3", + "0400.6" => "actuator4", + "0500.6" => "actuator5", + "0600.6" => "actuator6", + "0700.6" => "actuator7", + "0800.6" => "actuator8", + "140069" => "mon-from1", + "150069" => "mon-to1", + "160069" => "mon-from2", + "170069" => "mon-to2", + "180069" => "tue-from1", + "190069" => "tue-to1", + "1a0069" => "tue-from2", + "1b0069" => "tue-to2", + "1c0069" => "wed-from1", + "1d0069" => "wed-to1", + "1e0069" => "wed-from2", + "1f0069" => "wed-to2", + "200069" => "thu-from1", + "210069" => "thu-to1", + "220069" => "thu-from2", + "230069" => "thu-to2", + "240069" => "fri-from1", + "250069" => "fri-to1", + "260069" => "fri-from2", + "270069" => "fri-to2", + "280069" => "sat-from1", + "290069" => "sat-to1", + "2a0069" => "sat-from2", + "2b0069" => "sat-to2", + "2c0069" => "sun-from1", + "2d0069" => "sun-to1", + "2e0069" => "sun-from2", + "2f0069" => "sun-to2", + "3e0069" => "mode", + "3f0069" => "holiday1", # Not verified + "400069" => "holiday2", # Not verified + "410069" => "desired-temp", + "XX0069" => "measured-temp", # sum of next. two, never "really" sent + "420069" => "measured-low", + "430069" => "measured-high", + "440069" => "state", + "600069" => "year", + "610069" => "month", + "620069" => "day", + "630069" => "hour", + "640069" => "minute", + "650069" => "init", + "820069" => "day-temp", + "840069" => "night-temp", + "850069" => "unknown_85", + "8a0069" => "windowopen-temp", + + + "0000aa" => "code_0000aa", + "0000ba" => "code_0000ba", + "430079" => "code_430079", + "440079" => "code_440079", + "4b0067" => "code_4b0067", + "4b0077" => "code_4b0077", + "7e0067" => "code_7e0067", +); + +my %cantset = ( + "actuator" => 1, + "actuator1" => 1, + "actuator2" => 1, + "actuator3" => 1, + "actuator4" => 1, + "actuator5" => 1, + "actuator6" => 1, + "actuator7" => 1, + "actuator8" => 1, + "synctime" => 1, + "measured-temp" => 1, + "measured-high" => 1, + "measured-low" => 1, + "state" => 1, + "init" => 1, + + "code_0000aa" => 1, + "code_0000ba" => 1, + "code_430079" => 1, + "code_440079" => 1, + "code_4b0067" => 1, + "code_4b0077" => 1, + "code_7e0067" => 1, +); + +my %nosetarg = ( + "help" => 1, + "refreshvalues" => 1, +); + +my %c2m = (0 => "auto", 1 => "manual", 2 => "holiday"); +my %m2c = ("auto" => 0, "manual" => 1, "holiday" => 2); + +my %defptr; +my %c2b; # command->button hash (reverse of codes) +my %c2bset; # Setteable values + + +##################################### +sub +FHT_Initialize($) +{ + my ($hash) = @_; + + foreach my $k (keys %codes) { + my $v = $codes{$k}; + $c2b{$v} = $k; + $c2bset{$v} = substr($k, 0, 2) if(!defined($cantset{$v})); + } + $c2bset{refreshvalues} = "65ff66ff"; + +# 810c0426 0909a001 1111 1600 +# 810c04b3 0909a001 1111 44006900 +# 810b0402 83098301 1111 41301d +# 81090421 c409c401 1111 00 + +# 810c0d20 0909a001 3232 7e006724 (NYI) + + $hash->{Match} = "^81..(04|09|0d)..(0909a001|83098301|c409c401).."; + $hash->{SetFn} = "FHT_Set"; + $hash->{StateFn} = "FHT_SetState"; + $hash->{DefFn} = "FHT_Define"; + $hash->{UndefFn} = "FHT_Undef"; + $hash->{ParseFn} = "FHT_Parse"; + $hash->{AttrList} = "do_not_notify:0,1 model;fht80b dummy:0,1 showtime:0,1 loglevel:0,1,2,3,4,5,6"; +} + + +##################################### +sub +FHT_Set($@) +{ + my ($hash, @a) = @_; + my $ret = undef; + + return "\"set $a[0]\" needs two parameters" if(@a < 2); + return "Unknown argument $a[1], choose one of " . + join(" ", sort {$c2bset{$a} cmp $c2bset{$b} } keys %c2bset) + if(!defined($c2bset{$a[1]})); + return "\"set $a[0]\" needs two parameters" + if(@a != 3 && !(@a == 2 && $nosetarg{$a[1]})); + + Log GetLogLevel($a[0],2), "FHT set " . join(" ", @a); + + my $arg = "020183" . $hash->{CODE} . $c2bset{$a[1]}; + + if($a[1] eq "refreshvalues") { + + # This is special. Without the sleep the next FHT won't send its data + if(!IsDummy($a[0])) { + my $havefhz; + $havefhz = 1 if($hash->{IODev} && defined($hash->{IODev}->{FD})); + + IOWrite($hash, "04", $arg); + sleep(1) if($havefhz); + IOWrite($hash, "04", "c90185"); # Check the fht buffer + sleep(1) if($havefhz); + } + return $ret; + + } elsif($a[1] =~ m/-temp/) { + return "Invalid temperature, use NN.N" if($a[2] !~ m/^\d*\.?\d+$/); + my $a = int($a[2]*2); + $arg .= sprintf("%02x", $a); + $ret = "Rounded temperature to " . $a/2 if($a/2 != $a[2]); + + } elsif($a[1] =~ m/-from/ || $a[1] =~ m/-to/) { + return "Invalid timeformat, use HH:MM" if($a[2] !~ m/^([0-2]\d):([0-5]\d)/); + my $a = ($1*6) + ($2/10); + $arg .= sprintf("%02x", $a); + + my $nt = sprintf("%02d:%02d", $1, ($2/10)*10); + $ret = "Rounded time to $nt" if($nt ne $a[2]); + + } elsif($a[1] eq "mode") { + return "Invalid mode, use one of " . join(" ", sort keys %m2c) + if(!defined($m2c{$a[2]})); + $arg .= sprintf("%02x", $m2c{$a[2]}); + + } else { # Holiday1, Holiday2 + $arg .= sprintf("%02x", $a[2]); + + } + + IOWrite($hash, "04", $arg) if(!IsDummy($a[0])); + return $ret; +} + +##################################### +sub +FHT_SetState($$$$) +{ + my ($hash, $tim, $vt, $val) = @_; + + return "Undefined type $vt" if(!defined($c2b{$vt})); + return undef; +} + + +##################################### +sub +FHT_Define($$) +{ + my ($hash, $def) = @_; + my @a = split("[ \t][ \t]*", $def); + + return "wrong syntax: define FHT CODE" if(int(@a) != 3); + $a[2] = lc($a[2]); + 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]$/i); + + + $hash->{CODE} = $a[2]; + $defptr{$a[2]} = $hash; + + AssignIoPort($hash); + + Log GetLogLevel($a[0],2),"Asking the FHT device $a[0]/$a[2] to send its data"; + FHT_Set($hash, ($a[0], "refreshvalues")); + + return undef; +} + +##################################### +sub +FHT_Undef($$) +{ + my ($hash, $name) = @_; + delete($defptr{$hash->{CODE}}); + return undef; +} + +##################################### +sub +FHT_Parse($$) +{ + my ($hash,$msg) = @_; + + my $dev = substr($msg, 16, 4); + my $cde = substr($msg, 20, 6); + my $val = substr($msg, 26, 2) if(length($msg) > 26); + + if(!defined($defptr{$dev})) { + Log 3, "FHT Unknown device $dev, please define it"; + return "UNDEFINED FHT $dev"; + } + + + my $def = $defptr{$dev}; + + # Unknown, but don't want report it. Should come with c409c401 + if($cde eq "00") { + return ""; + } + + if(length($cde) < 6) { + my $name = $def->{NAME}; + Log GetLogLevel($name,2), "FHT Unknown code from $name : $cde"; + $def->{CHANGED}[0] = "unknown code $cde"; + return $name; + } + + + if(!$val) { + # This is a confirmation message. We reformat it so that + # it looks like a real message, and let the rest parse it + Log 4, "FHT $def->{NAME} confirmation: $cde)"; + $val = substr($cde, 2, 2); + $cde = substr($cde, 0, 2) . "0069"; + } + + my $type; + foreach my $c (keys %codes) { + if($cde =~ m/$c/) { + $type = $codes{$c}; + last; + } + } + + $val = hex($val); + + if(!$type) { + Log 4, "FHT $def->{NAME} (Unknown: $cde => $val)"; + $def->{CHANGED}[0] = "unknown $cde: $val"; + return $def->{NAME}; + } + + + my $tn = TimeNow(); + + ########################### + # Reformat the values so they are readable + + if($type eq "actuator") { + $val = sprintf("%02d%%", int(100*$val/255 + 0.5)); + + } elsif($cde ge "140069" && $cde le "2f0069") { # Time specs + Log 5, "FHT $def->{NAME} ($type: $val)"; + return "" if($val == 144); # Empty, forget it + my $hour = $val / 6; + my $min = ($val % 6) * 10; + $val = sprintf("%02d:%02d", $hour, $min); + + } elsif($type eq "mode") { + $val = $c2m{$val} if(defined($c2m{$val})); + + } elsif($type eq "measured-low") { + + $def->{READINS}{$type}{TIME} = $tn; + $def->{READINS}{$type}{VAL} = $val; + return ""; + + } elsif($type eq "measured-high") { + + $def->{READINS}{$type}{TIME} = $tn; + $def->{READINS}{$type}{VAL} = $val; + + if(defined($def->{READINGS}{"measured-low"}{VAL})) { + + $val = $val*256 + $def->{READINGS}{"measured-low"}{VAL}; + $val /= 10; + $val = sprintf("%.1f (Celsius)", $val); + $type = "measured-temp" + + } else { + return ""; + } + + } elsif($type =~ m/.*-temp/) { + $val = sprintf("%.1f (Celsius)", $val / 2) + + } elsif($type eq "state") { + + my $nval; + $nval = "Bat: " . (($val & 1) ? "empty" : "ok"); + $nval .= ", Window: " . (($val & 32) ? "open" : "closed"); + $nval .= ", Fault: " . (($val & 16) ? "yes" : "no"); + $val = $nval; + + } elsif($type =~ m/echo_/) { # Ignore these messages + return ""; + + } + + $def->{READINGS}{$type}{TIME} = $tn; + $def->{READINGS}{$type}{VAL} = $val; + + Log 4, "FHT $def->{NAME} ($type: $val)"; + $def->{CHANGED}[0] = "$type: $val"; + $def->{STATE} = "$type: $val" if($type eq "measured-temp"); + return $def->{NAME}; +} + +1; diff --git a/fhem/FHEM/12_HMS.pm b/fhem/FHEM/12_HMS.pm new file mode 100755 index 000000000..740c960a4 --- /dev/null +++ b/fhem/FHEM/12_HMS.pm @@ -0,0 +1,206 @@ +############################################## +package main; + +use strict; +use warnings; + +my %codes = ( + "0" => "HMS100TF", + "1" => "HMS100T", + "2" => "HMS100WD", + "3" => "RM100-2", + "4" => "HMS100TFK", # Depending on the onboard jumper it is 4 or 5 + "5" => "HMS100TFK", + "6" => "HMS100MG", +); + +my %defptr; + + +##################################### +sub +HMS_Initialize($) +{ + my ($hash) = @_; + +# 810e047e0510a001473a000000120233 HMS100TF +# 810e04b90511a0018e63000001100000 HMS100T +# 810e04e80212a001ec46000001000000 HMS100WD +# 810e04d70213a001b16d000003000000 RM100-2 +# 810e047f0214a001a81f000001000000 HMS100TFK +# 810e048f0295a0010155000001000000 HMS100TFK (jumper) +# 810e04330216a001b4c5000001000000 HMS100MG + + $hash->{Match} = "^810e04....(1|5|9)[0-6]a001"; + $hash->{DefFn} = "HMS_Define"; + $hash->{UndefFn} = "HMS_Undef"; + $hash->{ParseFn} = "HMS_Parse"; + $hash->{AttrList} = "do_not_notify:0,1 showtime:0,1 model;hms100-t,hms100-tf,hms100-wd,hms100-mg,hms100-tfk,rm100-2 loglevel:0,1,2,3,4,5,6"; +} + +##################################### +sub +HMS_Define($$) +{ + my ($hash, $def) = @_; + my @a = split("[ \t][ \t]*", $def); + + return "wrong syntax: define HMS CODE" if(int(@a) != 3); + $a[2] = lc($a[2]); + 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]; + $defptr{$a[2]} = $hash; + + return undef; +} + +##################################### +sub +HMS_Undef($$) +{ + my ($hash, $name) = @_; + delete($defptr{$hash->{CODE}}); + return undef; +} + +##################################### +sub +HMS_Parse($$) +{ + my ($hash, $msg) = @_; + + my $dev = substr($msg, 16, 4); + my $cde = substr($msg, 11, 1); + my $val = substr($msg, 24, 8) if(length($msg) == 32); + + my $type = ""; + foreach my $c (keys %codes) { + if($cde =~ m/$c/) { + $type = $codes{$c}; + last; + } + } + + # As the HMS devices change their id on each battery change, we offer + # a wildcard too for each type: 100, + my $odev = $dev; + if(!defined($defptr{$dev})) { + Log 4, "HMS device $dev not defined, using the wildcard device 100$cde"; + $dev = "100$cde"; + } + + if(!defined($defptr{$dev})) { + Log 3, "Unknown HMS device $dev/$odev, please define it"; + $type = "HMS" if(!$type); + return "UNDEFINED $type $odev"; + } + + my $def = $defptr{$dev}; + + my (@v, @txt, @sfx); + + if($type eq "HMS100TF") { + + @txt = ( "temperature", "humidity", "battery"); + @sfx = ( "(Celsius)", "(%)", ""); + + # Codierung + my $status = hex(substr($val, 0, 1)); + $v[0] = int(substr($val, 5, 1) . substr($val, 2, 2))/10; + $v[1] = int(substr($val, 6, 2) . substr($val, 4, 1))/10; + $v[2] = "ok"; + if ( $status & 2 ) { $v[2] = "empty"; } + if ( $status & 4 ) { $v[2] = "replaced"; } + if ( $status & 8 ) { $v[0] = -$v[0]; } + + $val = "T: $v[0] H: $v[1] Bat: $v[2]"; + + } elsif ($type eq "HMS100T") { + + @txt = ( "temperature", "battery"); + @sfx = ( "(Celsius)", ""); + + my $status = hex(substr($val, 0, 1)); + $v[0] = int(substr($val, 5, 1) . substr($val, 2, 2))/10; + $v[1] = "ok"; + if ( $status & 2 ) { $v[1] = "empty"; } + if ( $status & 4 ) { $v[1] = "replaced"; } + if ( $status & 8 ) { $v[0] = -$v[0]; } + + $val = "T: $v[0] Bat: $v[1]"; + + } elsif ($type eq "HMS100WD") { + + @txt = ( "water_detect", "battery"); + @sfx = ( "", ""); + + # Battery-low condition detect is not yet properly + # implemented. As soon as my WD's batteries get low + # I am willing to supply a patch ;-) SEP7-RIPE, 2006/05/13 + my $status = hex(substr($val, 1, 1)); + $v[1] = "ok"; + $v[0] = "off"; + if ( $status & 1 ) { $v[0] = "on"; } + $val = "Water Detect: $v[0]"; + + } elsif ($type eq "HMS100TFK") { # By Peter P. + + @txt = ( "switch_detect", "battery"); + @sfx = ( "", ""); + # Battery-low condition detect is not yet properly implemented. + my $status = hex(substr($val, 1, 1)); + $v[0] = ($status ? "on" : "off"); + $v[1] = "off"; + $val = "Switch Detect: $v[0]"; + + } elsif($type eq "RM100-2") { + + @txt = ( "smoke_detect", "battery"); + @sfx = ( "", ""); + + $v[0] = ( hex(substr($val, 1, 1)) != "0" ) ? "on" : "off"; + $v[1] = "unknown"; # Battery-low detect is _NOT_ implemented. + $val = "smoke_detect: $v[0]"; + + } elsif ($type eq "HMS100MG") { # By Peter Stark + + @txt = ( "gas_detect", "battery"); + @sfx = ( "", ""); + + # Battery-low condition detect is not yet properly + # implemented. + my $status = hex(substr($val, 1, 1)); + $v[0] = ($status != "0") ? "on" : "off"; + $v[1] = "off"; + if ($status & 1) { $v[0] = "on"; } + $val = "Gas Detect: $v[0]"; + + } else { + + Log 4, "HMS Device $dev (Unknown type: $type)"; + return ""; + + } + + my $now = TimeNow(); + Log GetLogLevel($def->{NAME},4), "HMS Device $dev ($type: $val)"; + + my $max = int(@txt); + for( my $i = 0; $i < $max; $i++) { + $def->{READINGS}{$txt[$i]}{TIME} = $now; + my $v = "$v[$i] $sfx[$i]"; + $def->{READINGS}{$txt[$i]}{VAL} = $v; + $def->{CHANGED}[$i] = "$txt[$i]: $v"; + } + $def->{READINGS}{type}{TIME} = $now; + $def->{READINGS}{type}{VAL} = $type; + + $def->{STATE} = $val; + $def->{CHANGED}[$max] = $val; + return $def->{NAME}; +} + +1; diff --git a/fhem/FHEM/13_KS300.pm b/fhem/FHEM/13_KS300.pm new file mode 100755 index 000000000..6383f4ffd --- /dev/null +++ b/fhem/FHEM/13_KS300.pm @@ -0,0 +1,253 @@ +############################################## +package main; + +use strict; +use warnings; + +my %defptr; +my $negcount = 0; + +###################### +# Note: this is just an empty hull. + +##################################### +sub +KS300_Initialize($) +{ + my ($hash) = @_; + + # Message is like + # 810d04f94027a00171212730000008 + # 81 0d 04 f9 4027a00171 212730000008 + + $hash->{Match} = "^810.04..402.a001"; + $hash->{DefFn} = "KS300_Define"; + $hash->{UndefFn} = "KS300_Undef"; + $hash->{ParseFn} = "KS300_Parse"; + $hash->{AttrList} = "do_not_notify:0,1 showtime:0,1 model:ks300 loglevel:0,1"; +} + +##################################### +sub +KS300_Define($$) +{ + my ($hash, $def) = @_; + my @a = split("[ \t][ \t]*", $def); + + return "wrong syntax: define KS300 " . + "[ml/raincounter] [wind-factor]" if(int(@a) < 3 || int(@a) > 5); + $a[2] = lc($a[2]); + 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]; + my $rainunit = ((int(@a) > 3) ? $a[3] : 255); + my $windunit = ((int(@a) > 4) ? $a[4] : 1.0); + $hash->{CODE} = $a[2]; + $hash->{RAINUNIT} = $rainunit; + $hash->{WINDUNIT} = $windunit; + $defptr{$a[2]} = $hash; + + return undef; +} + +##################################### +sub +KS300_Undef($$) +{ + my ($hash, $name) = @_; + delete($defptr{$hash->{CODE}}); + return undef; +} + + +##################################### +sub +KS300_Parse($) +{ + my ($hash,$msg) = @_; + + if($msg !~ m/^810d04..4027a001/) { + Log 4, "KS300 unknown message $msg"; + return ""; + } + + ############################### + # 1 2 + #0123456789012345 67890123456789 + # + #810d04f94027a001 71212730000008 + ############################### + my @a = split("", $msg); + + ########################## + # I've seldom (1 out of 700) seen messages of length 10 and 11 with correct + # CRC, they seem to contain partial data (e.g. temp/wind/hum but not rain) + # They are suppressed as of now. + if(hex($a[3]) != 13) { + Log 4, "Strange KS400 message received, wont decode ($msg)"; + return ""; + } + + if(int(keys %defptr)) { + + my @arr = keys(%defptr); # No code is known yet + my $dev = shift(@arr); + my $def = $defptr{$dev}; + my $haverain = 0; + + my @v; + my @txt = ( "rain_raw", "rain", "wind", "humidity", "temperature", + "israining", "unknown1", "unknown2", "unknown3"); + my @sfx = ( "(counter)", "(l/m2)", "(km/h)", "(%)", "(Celsius)", + "(yes/no)", "","",""); + + # 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}; + + $v[0] = hex("$a[28]$a[27]$a[26]"); + + ############################# + # My KS300 sends a (quite huge) "negative" rain, when the rain begins, + # then the value is "normal" again. So we have to filter neg. rain out. + # But if the KS300 is sending this value more than once, then accept it, + # as the KS300 was probably reset + + if($r->{rain_raw}{VAL}) { + my ($rrv, undef) = split(" ", $r->{rain_raw}{VAL}); + $haverain = 1 if($v[0] != $rrv); + if($v[0] < $rrv) { + if($negcount++ < 3) { + Log 3, "KS300 negative rain, ignoring it"; + $v[0] = $rrv; + } else { + Log 1, "KS300 was probably reset, accepting new rain value"; + } + } else { + $negcount = 0; + } + } + + $v[1] = sprintf("%0.1f", $v[0] * $def->{RAINUNIT} / 1000); + $v[2] = sprintf("%0.1f", ("$a[25]$a[24].$a[23]"+0) * $def->{WINDUNIT}); + $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]; + + # Negative temp + $v[4] = -$v[4] if($v[8] & 8); + + my $tm = TimeNow(); + + Log GetLogLevel($def->{NAME},4), "KS300 $dev: $msg"; + + my $max = int(@v); + + + for(my $i = 0; $i < $max; $i++) { + $r->{$txt[$i]}{TIME} = $tm; + my $val = "$v[$i] $sfx[$i]"; + $r->{$txt[$i]}{VAL} = $val; + $def->{CHANGED}[$i] = "$txt[$i]: $val"; + } + + # For logging/summary + my $val = "T: $v[4] H: $v[3] W: $v[2] R: $v[1] IR: $v[5]"; + $def->{STATE} = $val; + $def->{CHANGED}[$max++] = $val; + + ################################### + # 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]"; + + } else { + + my @cv = split(" ", $r->{cum_day}{VAL}); + + 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 + + my @sd = split("[ :-]", "$cv[0] $cv[1]"); + my $ssec = 3600*$sd[3] + 60*$sd[4] + $sd[5]; # Sec at start of day + + my $difft = $sec - $csec; + $difft += 86400 if($d[2] != $cd[2]); # Sec since last reading + + my $t = $cv[3] + $difft * $v[4]; + my $h = $cv[5] + $difft * $v[3]; + 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"; + + $difft = $sec - $ssec; + $difft += 86400 if($d[2] != $sd[2]); # Sec since last reading + + $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}[$max++] = "avg_day $r->{avg_day}{VAL}"; + $r->{cum_day}{VAL} = "$tm T: 0 H: 0 W: 0 R: $v[1]"; + + if(!$r->{cum_month}) { # Check the month + + $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}[$max++] = "avg_month $r->{avg_month}{VAL}"; + $r->{cum_month}{VAL} = "0 T: 0 H: 0 W: 0 R: 0"; + + } + + } + $r->{cum_month}{TIME} = $r->{avg_month}{TIME} = $tm; + + } + + } + $r->{cum_day}{TIME} = $r->{avg_day}{TIME} = $tm; + # AVG computing + ################################### + + return $def->{NAME}; + + } else { + + Log 4, "KS300 detected: $msg"; + + } + + return ""; +} + +1; diff --git a/fhem/FHEM/90_at.pm b/fhem/FHEM/90_at.pm new file mode 100755 index 000000000..4c88cacaf --- /dev/null +++ b/fhem/FHEM/90_at.pm @@ -0,0 +1,113 @@ +############################################## +package main; + +use strict; +use warnings; +use IO::File; + +##################################### +sub +at_Initialize($) +{ + my ($hash) = @_; + + $hash->{DefFn} = "at_Define"; + $hash->{TimeFn} = "at_Exec"; + $hash->{AttrFn} = "at_Attr"; + $hash->{AttrList} = "disable:0,1 skip_next:0,1"; +} + + +##################################### +sub +at_Define($$) +{ + my ($hash, $def) = @_; + my ($name, undef, $tm, $command) = split("[ \t]+", $def, 4); + + return "Usage: define at " if(!$command); + return "Wrong timespec, use \"[+][*[{count}]]