From 837e3a53982234da765644d351f9ac0066285f43 Mon Sep 17 00:00:00 2001 From: rudolfkoenig <> Date: Sat, 15 Jan 2011 11:14:35 +0000 Subject: [PATCH] ESA2000 added git-svn-id: https://svn.fhem.de/fhem/trunk@802 2b470e98-0d58-463d-a4d8-8e2adae1ed80 --- fhem/FHEM/00_CUL.pm | 9 +- fhem/FHEM/64_ESA2000.pm | 223 +++++++++++++++++++++++++++++++++++++ fhem/FHEM/98_autocreate.pm | 38 ++++--- fhem/docs/commandref.html | 44 +++++++- 4 files changed, 297 insertions(+), 17 deletions(-) create mode 100644 fhem/FHEM/64_ESA2000.pm diff --git a/fhem/FHEM/00_CUL.pm b/fhem/FHEM/00_CUL.pm index a0d6ddfc2..4f5e7aa0e 100755 --- a/fhem/FHEM/00_CUL.pm +++ b/fhem/FHEM/00_CUL.pm @@ -47,7 +47,7 @@ my %sets = ( my @ampllist = (24, 27, 30, 33, 36, 38, 40, 42); # rAmpl(dB) my $clientsSlowRF = ":FS20:FHT:FHT8V:KS300:USF1000:BS:HMS" . - ":CUL_EM:CUL_WS:CUL_FHTTK:CUL_RFR:CUL_HOERMANN:"; + ":CUL_EM:CUL_WS:CUL_FHTTK:CUL_RFR:CUL_HOERMANN:ESA2000:"; my $clientsHomeMatic = ":CUL_HM:HMS:"; my %matchListSlowRF = ( @@ -62,6 +62,7 @@ my %matchListSlowRF = ( "9:CUL_FHTTK" => "^T........", "A:CUL_RFR" => "^[0-9A-F]{4}U.", "B:CUL_HOERMANN"=> "^R..........", + "C:ESA2000" => "^S................................\$", ); my %matchListHomeMatic = ( "1:CUL_HM" => "^A......................", @@ -810,7 +811,7 @@ CUL_Parse($$$$$) my $rssi; my $dmsg = $rmsg; - if($dmsg =~ m/^[AFTKEHR]([A-F0-9][A-F0-9])+$/) { # RSSI + if($dmsg =~ m/^[AFTKEHRS]([A-F0-9][A-F0-9])+$/) { # RSSI my $l = length($dmsg); $rssi = hex(substr($dmsg, $l-2, 2)); $dmsg = substr($dmsg, 0, $l-2); @@ -880,7 +881,9 @@ CUL_Parse($$$$$) } elsif($fn eq "E" && $len >= 11) { # CUL_EM / Native ; - } elsif($fn eq "R" && $len >= 11) { # CUL_EM / Native + } elsif($fn eq "R" && $len >= 11) { # CUL_HOERMANN / Native + ; + } elsif($fn eq "S" && $len >= 33) { # CUL_ESA / ESA2000 / Native ; } elsif($fn eq "A" && $len >= 21) { # AskSin/BidCos/HomeMatic ; diff --git a/fhem/FHEM/64_ESA2000.pm b/fhem/FHEM/64_ESA2000.pm new file mode 100644 index 000000000..cd2e7052b --- /dev/null +++ b/fhem/FHEM/64_ESA2000.pm @@ -0,0 +1,223 @@ +############################################## +# (c) by STefan Mayer (stefan(at)clumsy.ch) # +# # +# please feel free to contact me for any # +# changes, improvments, suggestions, etc # +# # +############################################## + +package main; + +use strict; +use warnings; + +my %codes = ( + "19fa" => "ESA2000_LED", +); + + +##################################### +sub +ESA2000_Initialize($) +{ + my ($hash) = @_; + +# S0119FA011E00007D6E003100000007C9 ESA2000_LED + + $hash->{Match} = "^S................................\$"; + $hash->{DefFn} = "ESA2000_Define"; + $hash->{UndefFn} = "ESA2000_Undef"; + $hash->{ParseFn} = "ESA2000_Parse"; + $hash->{AttrList} = "IODev do_not_notify:0,1 showtime:0,1 model:esa2000-led loglevel:0,1,2,3,4,5,6 ignore:0,1 base_1 base_2"; +} + +##################################### +sub +ESA2000_Define($$) +{ + my ($hash, $def) = @_; + my @a = split("[ \t][ \t]*", $def); + + return "wrong syntax: define ESA2000 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]; + $modules{ESA2000}{defptr}{$a[2]} = $hash; + AssignIoPort($hash); + return undef; +} + +##################################### +sub +ESA2000_Undef($$) +{ + my ($hash, $name) = @_; + delete($modules{ESA2000}{defptr}{$hash->{CODE}}) + if(defined($hash->{CODE}) && + defined($modules{ESA2000}{defptr}{$hash->{CODE}})); + return undef; +} + +##################################### +sub +ESA2000_Parse($$) +{ + my ($hash, $msg) = @_; + +# 0123456789012345678901234567890123456789 +# S0119FA011E00007D6E003100000007C9F9 ESA2000_LED + $msg = lc($msg); + my $seq = substr($msg, 1, 2); + my $cde = substr($msg, 3, 4); + my $dev = substr($msg, 7, 4); + my $val = substr($msg, 11, 22); + + Log 5, "ESA2000 msg $msg"; + Log 5, "ESA2000 seq $seq"; + Log 5, "ESA2000 device $dev"; + Log 5, "ESA2000 code $cde"; + + my $type = ""; + foreach my $c (keys %codes) { + $c = lc($c); + if($cde =~ m/$c/) { + $type = $codes{$c}; + last; + } + } + + if(!defined($modules{ESA2000}{defptr}{$dev})) { + Log 3, "Unknown ESA2000 device $dev, please define it"; + $type = "ESA2000" if(!$type); + return "UNDEFINED ${type}_$dev ESA2000 $dev"; + } + + my $def = $modules{ESA2000}{defptr}{$dev}; + my $name = $def->{NAME}; + return "" if(IsIgnored($name)); + + my $now = TimeNow(); + my (@v, @txt); + + if($type eq "ESA2000_LED") { + + @txt = ( "repeat", "sequence", "total_ticks", "actual_ticks", "ticks_kwh", "raw", "total_kwh", "actual_kwh", "diff_kwh", "diff_sec", "diff_ticks", "last_sec", "raw_total_kwh", "max_kwh", "day_kwh", "month_kwh", "year_kwh" ); + + + # Codierung Hex + $v[0] = int(hex($seq) / 128) ? "+" : "-"; # repeated + $v[1] = hex($seq) % 128; + $v[2] = hex(substr($val,0,8)); + $v[3] = hex(substr($val,8,4)); + $v[4] = hex(substr($val,18,4)) ^ 25; # XOR 25, whyever bit 1,4,5 are swapped?!?! Probably a (receive-) error in CUL-FW? + + $v[5] = sprintf("CNT: %d%s CUM: %d CUR: %d TICKS: %d", + $v[1], $v[0], $v[2], $v[3], $v[4]); + $v[11] = time(); + if (defined($def->{READINGS}{$txt[11]}{VAL})) { + $v[9] = $v[11] - $def->{READINGS}{$txt[11]}{VAL}; # seconds since last update + } + if(defined($v[9]) && $v[9] != 0) { + $v[7] = $v[3]/$v[4]/$v[9]*3600; # calculate kW/h since last update + } else { + $v[7] = -1; + } + $v[8] = $v[3]/$v[4]; # calculate kWh diff from readings (raw from device....), whats this relly? + if(defined($def->{READINGS}{$txt[2]}{VAL})) { + $v[10] = $v[2] - $def->{READINGS}{$txt[2]}{VAL}; # shoudl be the same as actual_ticks if no packets are lost + } + if(defined($v[10])) { + $v[6] = $v[10]/$v[4] + (defined($def->{READINGS}{$txt[6]}{VAL}) ? $def->{READINGS}{$txt[6]}{VAL} : 0); # cumulate kWh to ensure tick-changes are calculated correctly (does this ever happen?) + if(defined($def->{READINGS}{$txt[14]}{TIME})) { + if(substr($now,0,10) eq substr($def->{READINGS}{$txt[14]}{TIME},0,10)) { # a bit clumsy, I agree, but it works and its logical and this is pearl, right? + $v[14] = $v[10]/$v[4] + (defined($def->{READINGS}{$txt[14]}{VAL}) ? $def->{READINGS}{$txt[14]}{VAL} : 0); # cumulate kWh to ensure tick-changes are calculated correctly (does this ever happen?) + } else { + $v[14] = $v[10]/$v[4] + } + } else { + $v[14] = $v[10]/$v[4] + } + if(defined($def->{READINGS}{$txt[15]}{TIME})) { + if(substr($now,0,7) eq substr($def->{READINGS}{$txt[15]}{TIME},0,7)) { # a bit clumsy, I agree, but it works and its logical and this is pearl, right? + $v[15] = $v[10]/$v[4] + (defined($def->{READINGS}{$txt[15]}{VAL}) ? $def->{READINGS}{$txt[15]}{VAL} : 0); # cumulate kWh to ensure tick-changes are calculated correctly (does this ever happen?) + } else { + $v[15] = $v[10]/$v[4] + } + } else { + $v[15] = $v[10]/$v[4] + } + if(defined($def->{READINGS}{$txt[16]}{TIME})) { + if(substr($now,0,4) eq substr($def->{READINGS}{$txt[16]}{TIME},0,4)) { # a bit clumsy, I agree, but it works and its logical and this is pearl, right? + $v[16] = $v[10]/$v[4] + (defined($def->{READINGS}{$txt[16]}{VAL}) ? $def->{READINGS}{$txt[16]}{VAL} : 0); # cumulate kWh to ensure tick-changes are calculated correctly (does this ever happen?) + } else { + $v[16] = $v[10]/$v[4] + } + } else { + $v[16] = $v[10]/$v[4] + } + } else { + $v[6] = 0; + } + + $v[12] = $v[2]/$v[4]; # calculate kWh total since reset of device (does only make sense if ticks per kWh does not change!!) + if(defined($def->{READINGS}{$txt[13]}{VAL})) { + if($v[7] >= $def->{READINGS}{$txt[13]}{VAL}) { + $v[13] = $v[7]; # update max kw/h + } + } else { + $v[13] = $v[7]; # update max kw/h + } + + + # add counter_1 and counter_2 (Hoch- und Niedertarif Basiswerte) + if(defined($attr{$name}) && + defined($attr{$name}{"count_1"}) && + ($attr{$name}{"count_1"}>0)) { + $v[13] = $v[12] + $attr{$name}{"count_1"}; + } + + if(defined($attr{$name}) && + defined($attr{$name}{"count_2"}) && + ($attr{$name}{"count_2"}>0)) { + $v[13] = $v[12] + $attr{$name}{"count_2"}; + } + + $val = sprintf("CNT: %d%s CUM: %0.3f CUR: %0.3f TICKS: %d", + $v[1], $v[0], $v[6], $v[7], $v[4]); + + } else { + + Log 3, "ESA2000 Device $dev (Unknown type: $type)"; + return ""; + + } + + + my $max = int(@txt); + + if ( (defined($def->{READINGS}{"sequence"}{VAL}) ? $def->{READINGS}{"sequence"}{VAL} : "") ne $v[1] ) { + Log GetLogLevel($name,4), "ESA2000 $name: $val"; + for( my $i = 0; $i < $max; $i++) { + if ( $v[$i] ) { + $def->{READINGS}{$txt[$i]}{TIME} = $now; + $def->{READINGS}{$txt[$i]}{VAL} = $v[$i]; + $def->{CHANGED}[$i] = "$txt[$i]: $v[$i]"; + } + } + $def->{READINGS}{type}{TIME} = $now; + $def->{READINGS}{type}{VAL} = $type; + + $def->{STATE} = $val; + $def->{CHANGED}[$max++] = $val; + } else { + Log GetLogLevel($name,4), "(ESA2000/DISCARDED $name: $val)"; + return "($name)"; + } + + return $name; +} + +1; diff --git a/fhem/FHEM/98_autocreate.pm b/fhem/FHEM/98_autocreate.pm index e32aaa40c..219e7eab0 100644 --- a/fhem/FHEM/98_autocreate.pm +++ b/fhem/FHEM/98_autocreate.pm @@ -11,25 +11,37 @@ use warnings; # - check "UNDEFINED" parameters for BS/USF1000/X10 my %flogpar = ( - "CUL_EM:.*" => { GPLOT => "cul_em:Power,", FILTER => "%NAME:CNT:.*" }, - "CUL_WS:.*" => { GPLOT => "hms:Temp/Hum,", FILTER => "%NAME" }, - "CUL_FHTTK:.*" => { GPLOT => "fht80tf:Window,", FILTER => "%NAME" }, - "FHT:.*" => { GPLOT => "fht:Temp/Act,", FILTER => "%NAME" }, - "HMS:HMS100TFK_.*" => { GPLOT => "fht80tf:Contact,", FILTER => "%NAME" }, - "HMS:HMS100T._.*" => { GPLOT => "hms:Temp/Hum,", FILTER => "%NAME:T:.*" }, - "KS300:.*" => { GPLOT => "ks300:Temp/Rain,ks300_2:Wind/Hum,", - FILTER => "%NAME:T:.*" }, + "CUL_EM:.*" + => { GPLOT => "cul_em:Power,", FILTER => "%NAME:CNT:.*" }, + "CUL_WS:.*" + => { GPLOT => "hms:Temp/Hum,", FILTER => "%NAME" }, + "CUL_FHTTK:.*" + => { GPLOT => "fht80tf:Window,", FILTER => "%NAME" }, + "FHT:.*" + => { GPLOT => "fht:Temp/Act,", FILTER => "%NAME" }, + "HMS:HMS100TFK_.*" + => { GPLOT => "fht80tf:Contact,", FILTER => "%NAME" }, + "HMS:HMS100T._.*" + => { GPLOT => "hms:Temp/Hum,", FILTER => "%NAME:T:.*" }, + "KS300:.*" + => { GPLOT => "ks300:Temp/Rain,ks300_2:Wind/Hum,", FILTER => "%NAME:T:.*" }, # Oregon sensors: # * temperature - "OREGON:(THR128|THWR288A|THN132N).*" => { GPLOT => "oregon_hms_t:Temp,", FILTER => "%NAME" }, + "OREGON:(THR128|THWR288A|THN132N).*" + => { GPLOT => "oregon_hms_t:Temp,", FILTER => "%NAME" }, # * temperature, humidity - "OREGON:(THGR228N|THGR810|THGR918|THGR328N|RTGR328N|WTGR800_T).*" => { GPLOT => "oregon_hms:Temp/Hum,", FILTER => "%NAME" }, + "OREGON:(THGR228N|THGR810|THGR918|THGR328N|RTGR328N|WTGR800_T).*" + => { GPLOT => "oregon_hms:Temp/Hum,", FILTER => "%NAME" }, # * temperature, humidity, pressure - "OREGON:(BTHR918N|BTHR918|BTHR918N).*" => { GPLOT => "oregon_temp_press:Temp/Press,oregon_hms:Temp/Hum,", FILTER => "%NAME" }, + "OREGON:(BTHR918N|BTHR918|BTHR918N).*" + => { GPLOT => "oregon_temp_press:Temp/Press,oregon_hms:Temp/Hum,", + FILTER => "%NAME" }, # * anenometer - "OREGON:(WGR800|WGR918|WTGR800_A).*" => { GPLOT => "oregon_wind:WindDir/WindSpeed,", FILTER => "%NAME" }, + "OREGON:(WGR800|WGR918|WTGR800_A).*" + => { GPLOT => "oregon_wind:WindDir/WindSpeed,", FILTER => "%NAME" }, # * Oregon sensors: Rain gauge - "OREGON:(PCR800|RGR918).*" => { GPLOT => "oregon_rain:RainRate", FILTER => "%NAME" }, + "OREGON:(PCR800|RGR918).*" + => { GPLOT => "oregon_rain:RainRate", FILTER => "%NAME" }, ); ##################################### diff --git a/fhem/docs/commandref.html b/fhem/docs/commandref.html index f27e97da6..2988f1a58 100644 --- a/fhem/docs/commandref.html +++ b/fhem/docs/commandref.html @@ -85,6 +85,7 @@ EMEM   EMGZ   EMWZ   + ESA2000   FHT   FHT8V   FHZ   @@ -2162,6 +2163,46 @@ A line ending with \ will be concatenated with the next one, so long lines
+ + +

ESA2000

+ + +

CUL_HM