diff --git a/CHANGED b/CHANGED index c162a439b..33cbb25d1 100644 --- a/CHANGED +++ b/CHANGED @@ -332,3 +332,4 @@ - bugfix: deleted FS20 items are still logging (zombie) (Gerhard, 16.5) - bugfix: added FS20S8, removed stty_parmrk (Martin, 24.5) - feature: added archivedir/archivecmd to the FileLog + - feature: added EM1010PC/EM1000WZ support diff --git a/FHEM/60_EM.pm b/FHEM/60_EM.pm new file mode 100755 index 000000000..7301e6478 --- /dev/null +++ b/FHEM/60_EM.pm @@ -0,0 +1,292 @@ +############################################## +package main; + +use strict; +use warnings; +use Device::SerialPort; + +sub EM_Write($$); +sub EmCrc($$); +sub EmCrcCheck($$); +sub EmEsc($); +sub EmGetData($$); +sub EmMakeMsg($); + +##################################### +sub +EM_Initialize($) +{ + my ($hash) = @_; + + +# Provider + $hash->{WriteFn} = "EM_Write"; + $hash->{Clients} = ":EMWZ:"; + +# Consumer + $hash->{DefFn} = "EM_Define"; + $hash->{UndefFn} = "EM_Undef"; + $hash->{GetFn} = "EM_Get"; + $hash->{SetFn} = "EM_Set"; + $hash->{AttrList}= "model:em1010pc loglevel:0,1,2,3,4,5,6"; +} + +##################################### +sub +EM_Define($$) +{ + my ($hash, $def) = @_; + my @a = split("[ \t][ \t]*", $def); + + $hash->{STATE} = "Initialized"; + + delete $hash->{PortObj}; + delete $hash->{FD}; + + my $dev = $a[2]; + $attr{$a[0]}{savefirst} = 1; + + if($dev eq "none") { + Log 1, "EM device is none, commands will be echoed only"; + return undef; + } + + Log 3, "EM opening device $dev"; + my $po = new Device::SerialPort ($dev); + return "Can't open $dev: $!\n" if(!$po); + Log 3, "EM opened device $dev"; + $po->close(); + + $hash->{DeviceName} = $dev; + return undef; +} + +##################################### +sub +EM_Undef($$) +{ + my ($hash, $arg) = @_; + my $name = $hash->{NAME}; + + foreach my $d (sort keys %defs) { + if(defined($defs{$d}) && + defined($defs{$d}{IODev}) && + $defs{$d}{IODev} == $hash) + { + Log GetLogLevel($name,2), "deleting port for $d"; + delete $defs{$d}{IODev}; + } + } + return undef; +} + + +##################################### +sub +EM_Set($@) +{ + return undef; +} + +######################### +sub +b($$) +{ + my ($t,$p) = @_; + return -1 if(length($t) < $p); + return ord(substr($t,$p,1)); +} + +sub +w($$) +{ + my ($t,$p) = @_; + return b($t,$p+1)*256 + b($t,$p); +} + +sub +dw($$) +{ + my ($t,$p) = @_; + return w($t,$p+2)*65536 + w($t,$p); +} + +##################################### +sub +EM_Get($@) +{ + my ($hash, @a) = @_; + + return "\"get EM\" needs only one parameter" if(@a != 2); + + my $v; + if($a[1] eq "time") { + + my $d = EmGetData($hash->{DeviceName}, "74"); + $v = sprintf "%4d-%02d-%02d %02d:%02d:%02d", + b($d,5)+2006, b($d,4), b($d,3), + b($d,0), b($d,1), b($d,2); + + } elsif($a[1] eq "version") { + + my $d = EmGetData($hash->{DeviceName},"76"); + $v = sprintf "%d.%d", b($d,0), b($d,1); + + } else { + return "Unknown argument $a[1], choose one of time,version"; + } + + $hash->{READINGS}{$a[1]}{VAL} = $v; + $hash->{READINGS}{$a[1]}{TIME} = TimeNow(); + + return "$a[0] $a[1] => $v"; +} + +##################################### +sub +EM_Write($$) +{ + my ($hash,$msg) = @_; + + return EmGetData($hash->{DeviceName}, $msg); +} + +##################################### +sub +EmCrc($$) +{ + my ($in, $val) = @_; + my ($crc, $bits) = (0, 8); + my $k = (($in >> 8) ^ $val) << 8; + while($bits--) { + if(($crc ^ $k) & 0x8000) { + $crc = ($crc << 1) ^ 0x8005; + } else { + $crc <<= 1; + } + $k <<= 1; + } + return (($in << 8) ^ $crc) & 0xffff; +} + +######################### +sub +EmEsc($) +{ + my ($b) = @_; + + my $out = ""; + $out .= chr(0x10) if($b==0x02 || $b==0x03 || $b==0x10); + $out .= chr($b); +} + + +##################################### +sub +EmCrcCheck($$) +{ + my ($otxt, $len) = @_; + my $crc = 0x8c27; + for(my $l = 2; $l < $len+4; $l++) { + my $b = ord(substr($otxt,$l,1)); + $crc = EmCrc($crc, 0x10) if($b==0x02 || $b==0x03 || $b==0x10); + $crc = EmCrc($crc, $b); + } + return ($crc == w($otxt, $len+4)); +} + +######################### +sub +EmMakeMsg($) +{ + my ($data) = @_; + my $len = length($data); + $data = chr($len&0xff) . chr(int($len/256)) . $data; + + my $out = pack('H*', "0200"); + my $crc = 0x8c27; + for(my $l = 0; $l < $len+2; $l++) { + my $b = ord(substr($data,$l,1)); + $crc = EmCrc($crc, 0x10) if($b==0x02 || $b==0x03 || $b==0x10); + $crc = EmCrc($crc, $b); + $out .= EmEsc($b); + } + $out .= EmEsc($crc&0xff); + $out .= EmEsc($crc/256); + $out .= chr(0x03); + return $out; +} + +##################################### +# This is the only +sub +EmGetData($$) +{ + my ($dev, $d) = @_; + $d = EmMakeMsg(pack('H*', $d)); + + my $serport = new Device::SerialPort ($dev); + die "Can't open $dev: $!\n" if(!$serport); + $serport->reset_error(); + $serport->baudrate(38400); + $serport->databits(8); + $serport->parity('none'); + $serport->stopbits(1); + $serport->handshake('none'); + + Log 5, "EM: Sending " . unpack('H*', $d) . "\n"; + + my $rm = "EM timeout reading the answer"; + for(my $rep = 0; $rep < 3; $rep++) { + + $serport->write($d); + + my $retval = ""; + my $esc = 0; + my $started = 0; + my $complete = 0; + for(;;) { + my ($rout, $rin) = ('', ''); + vec($rin, $serport->FILENO, 1) = 1; + my $nfound = select($rout=$rin, undef, undef, 1.0); + + if($nfound < 0) { + $rm = "EM Select error $nfound / $!"; + goto DONE; + } + last if($nfound == 0); + + my $buf = $serport->input(); + if(!defined($buf) || length($buf) == 0) { + $rm = "EM EOF on $dev"; + goto DONE; + } + + for(my $i = 0; $i < length($buf); $i++) { + my $b = ord(substr($buf,$i,1)); + + if(!$started && $b != 0x02) { next; } + $started = 1; + if($esc) { $retval .= chr($b); $esc = 0; next; } + if($b == 0x10) { $esc = 1; next; } + $retval .= chr($b); + if($b == 0x03) { $complete = 1; last; } + } + + if($complete) { + my $l = length($retval); + if($l < 8) { $rm = "EM Msg too short"; goto DONE; } + if(b($retval,1) != 0) { $rm = "EM Bad second byte"; goto DONE; } + if(w($retval,2) != $l-7) { $rm = "EM Length mismatch"; goto DONE; } + if(!EmCrcCheck($retval,$l-7)) { $rm = "EM Bad CRC"; goto DONE; } + return substr($retval, 4, $l-7); + } + } + } + +DONE: + $serport->close(); + return undef; +} + +1; diff --git a/FHEM/61_EMWZ.pm b/FHEM/61_EMWZ.pm new file mode 100755 index 000000000..e79684b46 --- /dev/null +++ b/FHEM/61_EMWZ.pm @@ -0,0 +1,165 @@ +############################################## +package main; + +use strict; +use warnings; +use Time::HiRes qw(gettimeofday); + +sub EMWZ_Get($@); +sub EMWZ_Set($@); +sub EMWZ_Define($$); +sub EMWZ_GetStatus($); + +################################### +sub +EMWZ_Initialize($) +{ + my ($hash) = @_; + + $hash->{GetFn} = "EMWZ_Get"; + $hash->{SetFn} = "EMWZ_Set"; + $hash->{DefFn} = "EMWZ_Define"; + + $hash->{AttrList} = "dummy:1,0 model;EM1000WZ loglevel:0,1,2,3,4,5,6"; +} + + +################################### +sub +EMWZ_GetStatus($) +{ + my ($hash) = @_; + + if(!$hash->{LOCAL}) { + InternalTimer(gettimeofday()+300, "EMWZ_GetStatus", $hash); + } + + my $dnr = $hash->{DEVNR}; + my $d = IOWrite($hash, sprintf("7a%02x", $dnr-1)); + + my $name = $hash->{NAME}; + + if($d eq ((pack('H*',"00") x 45) . pack('H*',"FF") x 6)) { + my $msg = "EMWZ no device no. $dnr present"; + Log GetLogLevel($name,2), $msg; + return $msg; + } + + my $pulses=w($d,13); + my $ec=w($d,49) / 10; + if($ec <= 0) { + my $msg = "EMWZ read error"; + Log GetLogLevel($name,2), $msg; + return $msg; + } + my $cur_energy = $pulses / $ec; # ec = U/kWh + my $cur_power = $cur_energy / 5 * 60; # 5minute interval scaled to 1h + + if($cur_power > 100) { + my $msg = "EMWZ Bogus reading: curr. power is reported to be $cur_power"; + Log GetLogLevel($name,2), $msg; + return $msg; + } + + my %vals; + $vals{"5min_pulses"} = $pulses; + $vals{"energy"} = sprintf("%0.3f", $cur_energy); + $vals{"power"} = sprintf("%.3f", $cur_power); + $vals{"alarm_PA"} = w($d,45) . " Watt"; + $vals{"price_CF"} = sprintf("%.3f", w($d,47)/10000); + $vals{"RperKW_EC"} = $ec; + + my $tn = TimeNow(); + my $idx = 0; + foreach my $k (keys %vals) { + my $v = $vals{$k}; + $hash->{CHANGED}[$idx++] = "$k: $v"; + $hash->{READINGS}{$k}{TIME} = $tn; + $hash->{READINGS}{$k}{VAL} = $v + } + + if(!$hash->{LOCAL}) { + DoTrigger($name, undef) if($init_done); + } + + $hash->{STATE} = "$cur_power kWh"; + + return $hash->{STATE}; +} + +################################### +sub +EMWZ_Get($@) +{ + my ($hash, @a) = @_; + + return "argument is missing" if(int(@a) != 2); + + my $d = $hash->{DEVNR}; + my $msg; + + if($a[1] ne "status") { + return "unknown get value, valid is status"; + } + $hash->{LOCAL} = 1; + my $v = EMWZ_GetStatus($hash); + delete $hash->{LOCAL}; + + return "$a[0] $a[1] => $v"; +} + +sub +EMWZ_Set($@) +{ + my ($hash, @a) = @_; + + return "argument is missing" if(int(@a) != 3); + + my $v = $a[2]; + my $d = $hash->{DEVNR}; + my $msg; + + if($a[1] eq "price") { + $v *= 10000; # Make display and input the same + $msg = sprintf("79%02x2f02%02x%02x", $d-1, $v%256, int($v/256)); + } elsif($a[1] eq "alarm") { + $msg = sprintf("79%02x2d02%02x%02x", $d-1, $v%256, int($v/256)); + } elsif($a[1] eq "rperkw") { + $v *= 10; # Make display and input the same + $msg = sprintf("79%02x3102%02x%02x", $d-1, $v%256, int($v/256)); + } else { + return "unknown set value, use one of price,alarm,rperkw"; + } + + my $ret = IOWrite($hash, $msg); + if(ord(substr($ret,0,1)) != 6) { + $ret = "EMWZ Error occured: " . unpack('H*', $ret); + Log GetLogLevel($hash->{NAME},2), $ret; + return $ret; + } + + return undef; +} + +############################# +sub +EMWZ_Define($$) +{ + my ($hash, $def) = @_; + my @a = split("[ \t][ \t]*", $def); + + return "syntax: define EMWZ devicenumber" + if(@a != 3 || $a[2] !~ m,^[1-4]$,); + $hash->{DEVNR} = $a[2]; + AssignIoPort($hash); + + + # InternalTimer blocks if init_done is not true + my $oid = $init_done; + $init_done = 1; + EMWZ_GetStatus($hash); + $init_done = $oid; + return undef; +} + +1; diff --git a/HISTORY b/HISTORY index 405adb917..cf0dd2060 100644 --- a/HISTORY +++ b/HISTORY @@ -98,3 +98,5 @@ - Rudi, Sun May 27 12:51:52 MEST 2007 - Archiving FileLogs. Added fhemweb.pl (pgm2) code, to show logs from the archive directory. See the attributes archivedir/archivecmd. + - Added EM1010PC suppoort (right now only with EM1000WZ). Support added + for displaying logs in the fhemweb.pl (webfrontends/pgm2) diff --git a/docs/commandref.html b/docs/commandref.html index 27d80178f..b2c55d72b 100644 --- a/docs/commandref.html +++ b/docs/commandref.html @@ -647,6 +647,44 @@ split in multiple lines


+ +

Type EM

+ + + +

Type EMWZ

+ +

Type FileLog

@@ -877,8 +915,6 @@ split in multiple lines

fhtbuf Notes: + +

Type EM:

+ + +

Type EMWZ:

+ @@ -1289,6 +1339,28 @@ split in multiple lines

Set some WS300 configuration parameters. + +

Type EMWZ:

+ + + diff --git a/docs/fhem.html b/docs/fhem.html index c3cdcca06..e97bb9d2d 100644 --- a/docs/fhem.html +++ b/docs/fhem.html @@ -16,34 +16,6 @@ fhz1000.pl

News (as of =DATE=, Version =VERS=)

@@ -65,6 +37,7 @@ Currently implemented features:
  • reading HMS data (HMS100-T,-TF,-WD,-MG,-TFK and RM100-2)
  • reading KS300 data
  • reading WS300 data
  • +
  • reading EM1010PC/EM1000WZ data
  • logging events to files (or database), with regexp filters
  • notifying external programs or internal modules when receiving certain events
  • diff --git a/fhem.pl b/fhem.pl index ff155381e..c31f2566a 100755 --- a/fhem.pl +++ b/fhem.pl @@ -119,6 +119,7 @@ use vars qw(%attr); # Attributes use vars qw(%value); # Current values, see commandref.html use vars qw(%oldvalue); # Old values, see commandref.html use vars qw($nextat); # used by the at module +use vars qw($init_done); # my $server; # Server socket my $currlogfile; # logfile, without wildcards @@ -132,10 +133,10 @@ my $devcount = 0; # To sort the devices my %defattr; # Default attributes my %intAt; # Internal at timer hash. my $intAtCnt=0; -my $init_done = 0; my $reread_active = 0; my $AttrList = "room comment"; +$init_done = 0; $modules{_internal_}{ORDER} = -1; $modules{_internal_}{AttrList} = "configfile logfile lastinclude modpath " . @@ -395,8 +396,9 @@ IOWrite($@) } no strict "refs"; - &{$modules{$iohash->{TYPE}}{WriteFn}}($iohash, @a); + my $ret = &{$modules{$iohash->{TYPE}}{WriteFn}}($iohash, @a); use strict "refs"; + return $ret; } ##################################### diff --git a/test/fhem.save b/test/fhem.save index 400b2e2e5..56969b2ff 100644 --- a/test/fhem.save +++ b/test/fhem.save @@ -1,4 +1,4 @@ -#Sun May 27 10:25:14 2007 +#Sun May 27 13:57:55 2007 setstate FHZ fhtbuf: 1c setstate FHZ 2006-02-12 14:03:39 fhtbuf 23 setstate FHZ 2006-03-26 08:47:36 init2 deadbeefdeadbe @@ -32,11 +32,13 @@ setstate fl 2006-03-26 08:50:55 unknown_85 4 setstate fl 2006-03-26 08:50:46 wed-from1 06:00 setstate fl 2006-03-26 08:50:46 wed-to1 23:00 setstate fl 2006-03-26 08:50:55 windowopen-temp 12.0 (Celsius) +setstate fllog active setstate floor.lamp ??? setstate global setstate marqee on setstate marqee 2006-04-01 12:46:02 state on setstate n_btn4 disabled +setstate outlog active setstate tf1 T: 20.3 H: 31.3 Bat: ok setstate tf1 2006-04-11 09:48:48 battery Bat: ok setstate tf1 2006-04-11 09:48:48 humidity 31.3 (%) @@ -68,3 +70,4 @@ setstate wz 2006-03-26 08:52:31 unknown_85 4 setstate wz 2006-03-26 08:50:36 wed-from1 06:00 setstate wz 2006-03-26 08:50:36 wed-to1 23:00 setstate wz 2006-03-26 08:52:31 windowopen-temp 12.0 (Celsius) +setstate wzlog active diff --git a/webfrontend/pgm2/em.gplot b/webfrontend/pgm2/em.gplot new file mode 100644 index 000000000..852f30801 --- /dev/null +++ b/webfrontend/pgm2/em.gplot @@ -0,0 +1,19 @@ +############################ +# Display the power reported by the EM1010 + + + +================= +set terminal png +set output '.png' +set xdata time +set timefmt "%Y-%m-%d_%H:%M:%S" +set xlabel " " + +set ytics nomirror +set y2tics +set title '' +set grid +set ylabel "KW" + +plot "" using 1:4 title 'Power' with lines