| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915 |
- ########################################################################################
- #
- # 15_EMX.pm MUST be saved as 15_CUL_EM.pm !!!
- #
- # FHEM module to read the data from an EM1000 WZ/EM/GZ power sensor
- #
- # Prof. Dr. Peter A. Henning, 2011
- #
- # $Id: 15_EMX.pm 2.0 2013-02 - pahenning $
- #
- ########################################################################################
- #
- # define <emx> EMX <code> <rpunit>
- #
- # where
- # <name> may be replaced by any name string
- # <code> is a number 1 - 12 or the keyword "emulator".
- # <rpunit> is the scale factor = rotations per kWh or m^3 (not needed for emulator)
- #
- # get <name> midnight => todays starting value for counter and power meter
- # get <name> cnt_midnight => todays starting value for counter
- # get <name> pm_midnight => todays starting value for power meter
- # get <name> month => summary of current month
- #
- # set <name> cnt_midnight => todays starting value for counter
- # set <name> pm_midnight => todays starting value for power meter
- # set <name> pm_current => current power meter reading
- #
- # Attributes are set as
- #
- # Monthly and yearly log file
- # attr emx LogM EnergyM
- # attr emx LogY EnergyY
- #
- # Basic fee per Month (€ per Month)
- # attr emx CostM
- #
- # Cost rate during daytime (€ per kWh)
- # attr emx CostD <cost rate in €/unit>
- #
- # Start and end of daytime cost rate - optional
- # attr emx CDStart <time as hh:mm>
- # attr emx CDEnd <time as hh:mm>
- #
- # Cost rate during nighttime (cost per unit) - only if needed
- # attr emx CostN <cost rate in €/unit>
- #
- ########################################################################################
- #
- # This programm is free software; you can redistribute it and/or modify
- # it under the terms of the GNU General Public License as published by
- # the Free Software Foundation; either version 2 of the License, or
- # (at your option) any later version.
- #
- # The GNU General Public License can be found at
- # http://www.gnu.org/copyleft/gpl.html.
- # A copy is found in the textfile GPL.txt and important notices to the license
- # from the author is found in LICENSE.txt distributed with these scripts.
- #
- # This script is distributed in the hope that it will be useful,
- # but WITHOUT ANY WARRANTY; without even the implied warranty of
- # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- # GNU General Public License for more details.
- #
- ########################################################################################
- package main;
- use strict;
- use warnings;
- my %gets = (
- "midnight" => "",
- "cnt_midnight" => "",
- "pm_midnight" => "",
- "month" => ""
- );
- my %sets = (
- "cnt_midnight" => "C",
- "pm_midnight" => "P",
- "pm_current" => "M",
- );
- #-- Global variables for the raw readings
- my $emx_seqno; # number of received datagram in sequence, runs from 2 to 255
- my $emx_cnt; # current count from device. This value has an arbitrary offset at each start of the device
- my $emx_5min; # count during last 5 min interval
- my $emx_peak; # peak count during last 5 min interval
- #--Forward definition
- sub EMX_Parse($$);
- ########################################################################################
- #
- # EMX_Initialize
- #
- ########################################################################################
- #-- stub function for CUL_EM replacement
- sub CUL_EM_Initialize ($) {
- my ($hash) = @_;
-
- return EMX_Initialize ($hash);
- }
-
- #-- real initialization function
- sub EMX_Initialize ($) {
- my ($hash) = @_;
- $hash->{DefFn} = "EMX_Define";
- $hash->{UndefFn} = "EMX_Undef";
- $hash->{ParseFn} = "EMX_Parse";
- $hash->{SetFn} = "EMX_Set";
- $hash->{GetFn} = "EMX_Get";
- $hash->{Match} = "^E0.................\$";
- $hash->{AttrList} = "IODev " .
- "model:EMEM,EMWZ,EMGZ loglevel LogM LogY CostD CDStart CDEnd CostN CostM ".
- $readingFnAttributes;
- }
- ########################################################################################
- #
- # EMX_Define - Implements DefFn function
- #
- # Parameter hash, definition string
- #
- ########################################################################################
- sub EMX_Define ($$) {
- my ($hash, $def) = @_;
- my @a = split("[ \t][ \t]*", $def);
- return "wrong syntax: define <name> EMX <code> <rpunit>"
- if(int(@a) < 3 || int(@a) > 4);
-
- my $name = $a[0];
-
- #-- emulator mode ------------------------------------------------------------
- if( $a[2] eq "emulator") {
- $hash->{CODE} = "emulator";
- Log 1, "EMX with emulator mode";
-
- #-- counts per unit etc.
- $hash->{READINGS}{"energy"}{FACTOR} = 150;
- $hash->{READINGS}{"energy"}{UNIT} = "Kilowattstunden";
- $hash->{READINGS}{"energy"}{UNITABBR}= "kWh";
- $hash->{READINGS}{"power"}{PERIOD} = "h";
- $hash->{READINGS}{"power"}{UNIT} = "Kilowatt";
- $hash->{READINGS}{"power"}{UNITABBR} = "kW";
- CommandAttr(undef,"$name model emulator");
- #-- set/ get artificial data
- my $msg=EMX_emu(0,12345);
- $hash->{READINGS}{"count"}{midnight} = 12345;
- $hash->{READINGS}{"pmeter"}{midnight} = 0;
- EMX_store($hash);
- $hash->{emumsg}=$msg;
-
- $modules{EMX}{defptr}{0} = $hash;
- # Call emulator in 15 seconds again, and then cyclic repetition
- InternalTimer(gettimeofday()+15, "EMX_Parse", $hash, 0);
-
- } else {
- #-- Real device definition -----------------------------------------------------
- return "EMX_Define $a[0]: wrong CODE format: valid is 1-12 or \"emulator\""
- if( $a[2] !~ m/^\d+$/ || $a[2] < 1 || $a[2] > 12 );
- $hash->{CODE} = $a[2];
-
- #--counts per unit etc.
- if($a[2] >= 1 && $a[2] <= 4) { # EMWZ
- $hash->{READINGS}{"energy"}{FACTOR} = $a[3];
- $hash->{READINGS}{"energy"}{UNIT} = "Kilowattstunden";
- $hash->{READINGS}{"energy"}{UNITABBR} = "kWh";
- $hash->{READINGS}{"power"}{PERIOD} = "h";
- $hash->{READINGS}{"power"}{UNIT} = "Kilowatt";
- $hash->{READINGS}{"power"}{UNITABBR} = "kW";
- CommandAttr (undef,"$name model EMWZ");
- } elsif($a[2] >= 5 && $a[2] <= 8) { # EMEM
- $hash->{READINGS}{"energy"}{FACTOR} = $a[3];
- $hash->{READINGS}{"energy"}{UNIT} = "Kilowattstunden";
- $hash->{READINGS}{"energy"}{UNITABBR} = "kWh";
- $hash->{READINGS}{"power"}{PERIOD} = "h";
- $hash->{READINGS}{"power"}{UNIT} = "Kilowatt";
- $hash->{READINGS}{"power"}{UNITABBR} = "kW";
- CommandAttr (undef,"$name model EMEM");
- } elsif($a[2] >= 9 && $a[2] <= 12) { # EMGZ
- $hash->{READINGS}{"energy"}{FACTOR} = $a[3];
- $hash->{READINGS}{"energy"}{UNIT} = "Kubikmeter";
- $hash->{READINGS}{"energy"}{UNITABBR} = "m^3";
- $hash->{READINGS}{"power"}{PERIOD} = "h";
- $hash->{READINGS}{"power"}{UNIT} = "Kubikmeter/Stunde";
- $hash->{READINGS}{"power"}{UNITABBR} = "m^3/h";
- CommandAttr (undef,"$name model EMGZ");
- }
- #-- Couple to I/O device
- $modules{EMX}{defptr}{$a[2]} = $hash;
- AssignIoPort($hash);
- }
-
- readingsSingleUpdate($hash,"state","defined",1);
- Log 3, "EMX: Device $name defined.";
- #-- Start timer for initialization in a few seconds
- InternalTimer(time()+3, "EMX_InitializeDevice", $hash, 0);
- return undef;
- }
- ########################################################################################
- #
- # EMX_InitializeDevice - Sets up the device after start
- #
- # Parameter hash
- #
- ########################################################################################
- sub EMX_InitializeDevice ($) {
- my ($hash) = @_;
- my $ret;
- my $name = $hash->{NAME};
- Log 1,"EMX_InitializeDevice $name";
- #-- read starting value of the day
- $ret = EMX_recall($hash);
- Log 1, $ret
- if( defined($ret));
- return $ret
- if( defined($ret));
-
- return undef;
- }
- ########################################################################################
- #
- # EMX_FormatValues - Calculate display values
- #
- # Parameter hash
- #
- ########################################################################################
- sub EMX_FormatValues ($) {
- my ($hash) = @_;
-
- #Log 1," seqno $emx_seqno cnt $emx_cnt 5min $emx_5min peak $emx_peak";
-
- my $name = $hash->{NAME};
- my ($model,$factor,$period,$unit,$runit,$midnight,$cval,$vval,$tval,$rval,$pval,$dval,$deltim,$delcnt,$msg);
- my ($svalue,$dvalue,$mvalue) = ("","","");
- my $cost = 0;
- my ($sec, $min, $hour, $day, $month, $year, $wday,$yday,$isdst) = localtime(time);
- my ($seco,$mino,$houro,$dayo,$montho,$yearo,$dayrest);
- my $daybreak = 0;
- my $monthbreak = 0;
-
- #-- Check, whether we have a new "day"
- # emulator: less than 15 seconds from 5-minute period
- if( $hash->{CODE} eq "emulator"){
- $deltim = $min%5+$sec/60.0 - 4.75;
- if( $deltim>0 ){
- $yearo = $year+1900;
- $montho = $month;
- $dayo = $day."-".$hour."-".$min."-".$sec;
- $daybreak = 1;
- #-- Check, whether we have a new "month" = three 5 minute periods
- if( $min%15 == 0){
- $monthbreak = 1;
- }
- }
- # normal mode: less than 5 minutes from midnight
- }else {
- $deltim = $hour*60.0+$min+$sec/60.0 - 1435.0;
- if( $deltim>=0 ){
- $daybreak = 1;
- #-- Timer data from tomorrow
- my ($secn,$minn,$hourn,$dayn,$monthn,$yearn,$wdayn,$ydayn,$isdstn) = localtime(time() + 3600);
- #-- Check, whether we have a new month
- if( $dayn == 1 ){
- $monthbreak = 1;
- }
- }
- }
-
- $model = $main::attr{$name}{"model"};
- $midnight = $hash->{READINGS}{"count"}{midnight};
- $factor = $hash->{READINGS}{"energy"}{FACTOR};
- $unit = $hash->{READINGS}{"energy"}{UNITABBR};
- $period = $hash->{READINGS}{"power"}{PERIOD};
- $runit = $hash->{READINGS}{"power"}{UNITABBR};
-
- my $emx_cnt_prev;
- my $emx_cnt_tim;
-
- #-- skip some things if undefined
- if( $emx_cnt eq ""){
- $svalue = "???";
- }else {
- #-- put into READINGS
- readingsBeginUpdate($hash);
- $svalue = "raw $emx_cnt";
-
- #-- get the old values (raw counts, always integer)
- $emx_cnt_prev = $hash->{READINGS}{"count"}{VAL};
- $emx_cnt_tim = $hash->{READINGS}{"count"}{TIME};
- $emx_cnt_tim = "" if(!defined($emx_cnt_tim));
- #-- safeguard against the case where no previous measurement
- if( length($emx_cnt_tim) > 0 ){
-
- #-- correct counter wraparound since last reading
- if( $emx_cnt < $emx_cnt_prev) {
- $emx_cnt_prev -= 65536;
- }
- #-- correct counter wraparound since last day
- if( $emx_cnt < $midnight) {
- $midnight -= 65536;
- }
-
- #-- For this calculation we could use either $emx_5min
- # or ($emx_cnt - $emx_cnt_prev) since they are the same.
- # But careful: we cannot be sure that measurement intervals are really
- # 5 minutes. Differ up to a second per interval !
- # Affects the rate by 0.3%, absolute count is not affected
- my $fivemin = 5.0;
- my $delcnt = ($emx_cnt-$emx_cnt_prev);
-
- #-- Extrapolate these values when a new day will be started (0<deltim<5)
- if( $daybreak==1 ) {
- $emx_cnt += $deltim/$fivemin *$delcnt;
- $cval = $emx_cnt-$midnight;
- #-- no daybreak -> subtract only midnight count
- }else{
- $cval = $emx_cnt-$midnight;
- }
- #-- Translate from device into physical units
- # $factor = no. of counts per unit
- # $emx_peak has to be divided by 20 = 60 min/ 5 min
- if( ($model eq "EMWZ") || ($model eq "emulator") ){
- $vval = int($cval/$factor*1000)/1000;
- $rval = int($emx_5min*12/$factor*1000)/1000;
- $pval = int($emx_peak/($factor*20)*1000)/1000;
- } elsif( $model eq "EMEM" ){
- $vval = int($cval/($factor*10)*1000)/1000;
- $rval = int($emx_5min/$factor*1000)/1000;
- $pval = int($emx_peak/($factor*20)*1000)/1000;
- } elsif( $model eq "EMGZ" ){
- $vval = int($cval/$factor*1000)/1000;
- $rval = int($emx_5min/$factor*1000)/1000;
- $pval = int($emx_peak/($factor*20)*1000)/1000;
- } else {
- Log 3,"EMX: Wrong device model $model";
- }
- #-- power meter value
- $tval = $vval + $hash->{READINGS}{"pmeter"}{midnight};
-
- #-- calculate cost
- if( defined($main::attr{$name}{"CostD"}) ){
- #-- single rate counter
- if( !defined($main::attr{$name}{"CostN"}) ){
- $cost = $vval*$main::attr{$name}{"CostD"};
- #-- dual rate counter
- }else{
- #--determine period 1 = still night, 2 = day, 3 = night again
- my @crs = split(':',$main::attr{$name}{"CDStart"});
- my @cre = split(':',$main::attr{$name}{"CDEnd"});
- my @tim = split(/[- :]/,$emx_cnt_tim);
- #-- if one of them fails, we switch to single rate mode
- if( (int(@crs) ne 2) || (int(@cre) ne 2) ){
- $cost = $vval*$main::attr{$name}{"CostD"};
- delete $main::attr{$name}{"CostN"};
- Log 3,"EMX: $name has improper cost rate time specification";
- } else {
- #-- period 1
- if ( (($hour-$crs[0])*60 + $min-$crs[1])<0 ){
- $cost = $vval*$main::attr{$name}{"CostN"};
- #-- period 2
- }elsif ( (($hour-$cre[0])*60 + $min-$cre[1])<0 ){
- my $delta = ($tim[3]-$crs[0])*60 + ($tim[4]-$crs[1]) + $tim[5]/60.0;
- my $oldval = $hash->{READINGS}{"energy"}{VAL};
- #-- previous measurement was in period 1
- if( $delta < 0 ){
- $cost = $hash->{READINGS}{"cost"}{VAL} +
- $main::attr{$name}{"CostN"}*($vval-$oldval)*(1+$delta/$fivemin)+
- $main::attr{$name}{"CostD"}*($vval-$oldval)*(-$delta/$fivemin);
- } else{
- $cost = $hash->{READINGS}{"cost"}{VAL} + $main::attr{$name}{"CostD"}*($vval-$oldval);
- }
- #-- period 3
- }else{
- my $delta = ($tim[3]-$cre[0])*60 + ($tim[4]-$cre[1]) +$tim[5]/60.0;
- my $oldval = $hash->{READINGS}{"energy"}{VAL};
- #-- previous measurement was in period 2
- if( $delta < 0 ){
- $cost = $hash->{READINGS}{"cost"}{VAL} +
- $main::attr{$name}{"CostD"}*($vval-$oldval)*(1+$delta/$fivemin)+
- $main::attr{$name}{"CostN"}*($vval-$oldval)*(-$delta/$fivemin);
- } else{
- $cost = $hash->{READINGS}{"cost"}{VAL} + $main::attr{$name}{"CostN"}*($vval-$oldval);
- }
- }
- }
- }
- $cost = floor($cost*10000+0.5)/10000;
- }
-
- #-- state format
- $svalue = sprintf("W: %5.2f %s P: %5.2f %s Pmax: %5.3f %s",$vval,$unit,$rval,$runit,$pval,$runit);
- #-- put into READINGS
- readingsBulkUpdate($hash,"count",$emx_cnt);
- readingsBulkUpdate($hash,"energy",$vval);
- readingsBulkUpdate($hash,"pmeter",$tval);
- readingsBulkUpdate($hash,"power",$rval);
- readingsBulkUpdate($hash,"peak",$pval);
- readingsBulkUpdate($hash,"cost",$cost);
-
- #-- daybreak postprocessing
- if( $daybreak == 1 ){
- #-- store corrected counter value at midnight
- $hash->{READINGS}{"count"}{midnight} = $emx_cnt;
- $hash->{READINGS}{"pmeter"}{midnight} = $tval;
- EMX_store($hash);
- #-- daily/monthly accumulated value
- my @monthv = EMX_GetMonth($hash);
- my $total = $monthv[0]+$vval;
- $dvalue = sprintf("D%02d Wd: %5.2f %s Wm: %6.2f %s Cd: %5.2f €",$day,$vval,$unit,$total,$unit,int($cost*100)/100);
- readingsBulkUpdate($hash,"day",$dvalue);
- if( $monthbreak == 1){
- $mvalue = sprintf("M%02d Wm: %6.2f %s",$month+1,$total,$unit);
- readingsBulkUpdate($hash,"month",$mvalue);
- Log 1,$name." has monthbreak $msg ".$mvalue;
- }
- }
- }
- #-- STATE
- readingsBulkUpdate($hash,"state",$svalue);
- readingsEndUpdate($hash,1);
- }
- }
- ########################################################################################
- #
- # EMX_Get - Implements GetFn function
- #
- # Parameter hash, argument array
- #
- ########################################################################################
- sub EMX_Get ($@) {
- my ($hash, @a) = @_;
- #-- empty argument list
- return join(" ", sort keys %gets)
- if(@a < 2);
- #-- check syntax
- my $name = $hash->{NAME};
- return "EMX_Get with unknown argument $a[1], choose one of " . join(" ", sort keys %gets)
- if(!defined($gets{$a[1]}));
- $name = shift @a;
- my $key = shift @a;
- my $value;
- my $ret;
-
- #-- both midnight values
- if($key eq "midnight"){
- return "EMX_Get => midhight counter ".$hash->{READINGS}{"count"}{midnight}." (pmeter ".$hash->{READINGS}{"pmeter"}{midnight}.")";
- }
- #-- midnight counter value
- if($key eq "cnt_midnight"){
- $value = $hash->{READINGS}{"count"}{midnight};
- }
- #-- midnight power meter value
- if($key eq "pm_midnight"){
- $value = $hash->{READINGS}{"pmeter"}{midnight};
- }
- #-- monthly summary
- if($key eq "month"){
- my @month = EMX_GetMonth($hash);
- $value = "Wm ".$month[1]." kWh (av. ".$month[2]." kWh)";
- }
- Log GetLogLevel($name,3), "EMX_Get => $key $value";
- return "EMX_Get => $key $value";
- }
- ########################################################################################
- #
- # EMX_Set - Implements SetFn function
- #
- # Parameter hash, argument array
- #
- ########################################################################################
- sub EMX_Set ($@) {
- my ($hash, @a) = @_;
- #-- empty argument list
- return join(" ", sort keys %sets)
- if(@a < 3);
- #-- check syntax
- return "EMX_Set needs at least two parameters"
- if(@a < 3);
- my $name = $hash->{NAME};
- Log GetLogLevel($name,3), "EMX Set request $a[1] $a[2]";
- return "EMX_Set with unknown argument $a[1], choose one of " . join(" ", sort keys %sets)
- if(!defined($sets{$a[1]}));
- $name = shift @a;
- my $key = shift @a;
- my $value = join("", @a);
- my $tn = TimeNow();
- my $ret;
- #-- value of midnight power meter reading may be set at runtime
- if($key eq "pm_midnight"){
- return "EMX_Set: Wrong midnight value for power meter, must be 0 <= value < 99999"
- if( ($value < 0) || ($value > 99999) );
- $hash->{READINGS}{"pmeter"}{midnight}=$value;
- #-- store this for later usage
- $ret = EMX_store($hash);
- return "EMX_Set: ".$ret
- if( defined($ret) );
- }
- #-- value of current power meter reading may be set at runtime
- if($key eq "pm_current"){
- return "EMX_Set: Wrong current value for power meter, must be 0 <= value < 99999"
- if( ($value < 0) || ($value > 99999) );
- $hash->{READINGS}{"pmeter"}{midnight}=$value-$hash->{READINGS}{"energy"}{VAL};
- #-- store this for later usage
- $ret = EMX_store($hash);
- return "EMX_Set: ".$ret
- if( defined($ret) );
- }
-
- #-- midnight counter value may be set at runtime
- if( ($key eq "midnight") || ($key eq "cnt_midnight") ){
- return "EMX_Set: Wrong midnight value for counter, must be -65536 <= value < 65536"
- if( ($value < -65536) || ($value > 65535) );
- $hash->{READINGS}{"count"}{midnight}=$value;
- #-- store this for later usage
- $ret = EMX_store($hash);
- return "EMX_Set: ".$ret
- if( defined($ret) );
- }
- Log GetLogLevel($name,3), "EMX_Set => $key $value";
- return "EMX_Set => $key $value";
- }
- ########################################################################################
- #
- # EMX_Undef - Implements UndefFn function
- #
- # Parameter hash, name
- #
- ########################################################################################
- sub EMX_Undef ($$) {
- my ($hash, $name) = @_;
- delete($modules{EMX}{defptr}{$hash->{CODE}});
- return undef;
- }
- ########################################################################################
- #
- # EMX_Parse - Parse the message string send by CUL_EM
- #
- # Parameter hash, msg = message string
- #
- ########################################################################################
- sub EMX_Parse ($$) {
- my ($hash,$msg) = @_;
-
- if( !($msg) ) {
- $msg=$hash->{emumsg};
- }
- # 0123456789012345678
- # E01012471B80100B80B -> Type 01, Code 01, Cnt 10
- my @a = split("", $msg);
- my $tpe = ($a[1].$a[2])+0;
- my $cde = hex($a[3].$a[4]);
- #-- emulator
- if( $cde eq "00"){
- $cde = "emulator";
- }
-
- #-- return, if the defice is undefided
- if( not($modules{EMX}{defptr}{$cde}) ){
- Log 1, "EMX detected, Code $cde";
- return "EMX_Parse: Undefined EMX_$cde EMX $cde";
- }
- my $def = $modules{EMX}{defptr}{$cde};
- $hash = $def;
- my $name = $hash->{NAME};
- return "" if(IsIgnored($name));
- $emx_seqno = hex($a[5].$a[6]);
- $emx_cnt = hex($a[ 9].$a[10].$a[ 7].$a[ 8]);
- $emx_5min = hex($a[13].$a[14].$a[11].$a[12]);
- $emx_peak = hex($a[17].$a[18].$a[15].$a[16]);
- EMX_FormatValues($hash);
-
- #-- emulator mode - must be triggered here since not received by CUL
- # Call us in 15 seconds minutes again.
- if( $hash->{CODE} eq "emulator"){
- # Next sequence number
- $emx_seqno++;
- $emx_seqno =0 if($emx_seqno > 255);
-
- # Get artificial data
- my $msg=EMX_emu($emx_seqno, $emx_cnt);
- $hash->{emumsg}=$msg;
-
- #-- restart timer for updates
- RemoveInternalTimer($hash);
- InternalTimer(gettimeofday()+15, "EMX_Parse", $hash,1);
- }
-
- return $hash->{NAME};
- }
- ########################################################################################
- #
- # Store daily start value in a file
- #
- # Parameter hash
- #
- ########################################################################################
- sub EMX_store($) {
- my ($hash) = @_;
-
- my $name = $hash->{NAME};
- my $mp = AttrVal("global", "modpath", ".");
- my $ret = open(EMXFILE, "> $mp/FHEM/EMX_$name.dat" );
- my $msg;
- if( $ret) {
- #-- Timer data
- my ($sec,$min,$hour,$day,$month,$year,$wday,$yday,$isdst) = localtime(time);
-
- if( $hash->{CODE} eq "emulator"){
- $msg = sprintf "%4d-%02d-%02d %02d:%02d:%02d %d %d",
- $year+1900,$month+1,$day,$hour,$min,$sec,
- $hash->{READINGS}{"count"}{midnight},
- $hash->{READINGS}{"pmeter"}{midnight};
- } else {
- $msg = sprintf "%4d-%02d-%02d midnight %7.2f %7.2f",
- $year+1900,$month+1,$day,
- $hash->{READINGS}{"count"}{midnight},
- $hash->{READINGS}{"pmeter"}{midnight};
- }
- print EMXFILE $msg;
- Log 1, "EMX_store: $name $msg";
- close(EMXFILE);
- } else {
- Log 1,"EMX_store: Cannot open EMX_$name.dat for writing!";
- }
- return undef;
- }
- ########################################################################################
- #
- # Recall daily start value from a file
- #
- # Parameter hash
- #
- ########################################################################################
- sub EMX_recall($) {
- my ($hash) = @_;
-
- my $name= $hash->{NAME};
- my $mp = AttrVal("global", "modpath", ".");
- my $ret = open(EMXFILE, "< $mp/FHEM/EMX_$name.dat" );
- my $msg;
- if( $ret ){
- my $line = readline EMXFILE;
- close(EMXFILE);
- my @a=split(' ',$line);
- #-- Timer data from yesterday
- my ($sec,$min,$hour,$day,$month,$year,$wday,$yday,$isdst) = localtime(time() - 24*60*60);
- $msg = sprintf "%4d-%02d-%02d", $year+1900,$month+1,$day;
- if( $msg ne $a[0]){
- Log 1, "EMX_recall: midnight value $a[2] for $name not from last day, but from $a[0]";
- $hash->{READINGS}{"count"}{midnight} = $a[2];
- $hash->{READINGS}{"pmeter"}{midnight} = defined($a[3]) ? $a[3] : 0;
- } else {
- Log 1, "EMX_recall: recalled midnight value $a[2] for $name";
- $hash->{READINGS}{"count"}{midnight} = $a[2];
- $hash->{READINGS}{"pmeter"}{midnight} = defined($a[3]) ? $a[3] : 0;
- }
- } else {
- Log 1, "EMX_recall: Cannot open EMX_$name.dat for reading!";
- $hash->{READINGS}{"count"}{midnight}=0;
- $hash->{READINGS}{"pmeter"}{midnight}=0;
- }
- return undef;
- }
- ########################################################################################
- #
- # Read monthly data from a file
- #
- # Parameter hash
- #
- # Returns total value up to last day, including this day and average including this day
- #
- ########################################################################################
- sub EMX_GetMonth($) {
- my ($hash) = @_;
- my $name = $hash->{NAME};
- my $regexp = ".*$name.*";
- my @month;
-
- #-- Check current logfile
- my $ln = $attr{$name}{"LogM"};
- if( !(defined($ln))){
- Log 1,"EMX_GetMonth: Attribute LogM is missing";
- return undef;
- } else {
- my $lf = $defs{$ln}{currentlogfile};
- my $ret = open(EMXFILE, "< $lf" );
- if( $ret) {
- while( <EMXFILE> ){
- #-- line looks like
- # 2013-02-09_23:59:31 <name> day D_09 Wd: 0.00 Wm: 171.70
- my $line = $_;
- chomp($line);
- if ( $line =~ m/$regexp/i){
- my @linarr = split(' ',$line);
- my $day = $linarr[3];
- $day =~ s/D_0+//;
- my $val = $linarr[5];
- push(@month,$val);
- }
- }
- }
- #-- sum and average
- my $total = 0.0;
- foreach(@month){
- $total +=$_;
- }
- #-- add data from current day
- $total = int($total*100)/100;
- my $total2 = int(100*($total+$hash->{READINGS}{"energy"}{VAL}))/100;
- #-- number of days so far, including the present day
- my ($sec,$min,$hour,$day,$month,$year,$wday,$yday,$isdst) = localtime(time);
- my $deltim = int(@month)+($hour+$min/60.0 + $sec/3600.0)/24.0;
- my $av = int(100*$total2/$deltim)/100;
- #-- output format
- return ($total,$total2,$av);
- }
- }
- ########################################################################################
- #
- # Emulator section - to be used, if the real device is not attached.
- #
- ########################################################################################
- sub EMX_emu ($$) {
- #-- Timer data
- my ($sec,$min,$hour,$day,$month,$year,$wday,$yday,$isdst) = localtime(time);
- #-- parse incoming parameters
- my ($seqno,$Wd_cnt_old)=@_;
- #-- setup message
- my $sj=sprintf("%02x",$seqno);
- # power value = 0.6 from 0:00 - 06:00 / 1.8 kW from 6:00 - 22:00/1.2 kW from 22:00 - 24:00
- my $Pac_cnt;
- my $Wd_cnt = $Wd_cnt_old%65536;
- if( ($hour+$min/60.0)<6.0 ){
- $Pac_cnt= 0.64*150.0/12.0;
- $Wd_cnt+= 0.64*150.0/12.0;
- } elsif ( ($hour+$min/60.0)<22.0 ) {
- $Pac_cnt= 1.92*150.0/12.0;
- $Wd_cnt+= 1.92*150.0/12.0;
- } else {
- $Pac_cnt= 1.28*150.0/12.0;
- $Wd_cnt+= 1.28*150.0/12.0;
- }
- my $cj=sprintf("%02x",int($Pac_cnt/256));
- my $ck=sprintf("%02x",$Pac_cnt%256);
- my $tj=sprintf("%02x",int($Wd_cnt/256));
- my $tk=sprintf("%02x",$Wd_cnt%256);
-
- my $msg="E0100".$sj.$tk.$tj.$ck.$cj."0000";
- #Log 1,"cj = $cj, ck=$ck, tj=$tj, tk=$tk";
- return $msg;
- }
- 1;
- =pod
- =begin html
- <a name="EMX"></a>
- <h3>EMX</h3>
- <p>FHEM module to commmunicate with the EM1000 WZ/EM/GZ power/gas sensors <br />
- <br /> <b>NOTE:</b> This module is currently NOT registered in the client list of 00_CUL.pm.
- Therefore ist must be saved under the name 15_CUL_EM.pm or entered into the client list manually.
- <br /></p>
- <br /><h4>Example</h4>
- <p>
- <code>define E_Verbrauch EMX 1 75</code>
- </p>
- <br />
- <a name="EMXdefine"></a>
- <h4>Define</h4>
- <p>
- <code>define <name> EMX <code> <rpunit></code> or <br/>
- <code>define <name> EMX emulator</code>
- <br /><br /> Define an EMX device or an emulated EM1000-WZ device <br /><br />
-
- </p>
- <ul>
- <li>
- <code><code></code><br /> Defines the sensor model, currently the following values are permitted:
- <ul>
- <li>1 .. 4: EM1000-WZ power meter sensor => unit is kWh</li>
- <li>5 .. 8: EM1000-EM power sensor => unit is kWh</li>
- <li>9 .. 12: EM1000-GZ gas meter sensor => unit is m<sup>3</sup></li>
- </ul>
- </li>
- <li>
- <code><rpunit></code><br/>Factor to scale the reading into units
- <ul>
- <li>EM1000-WZ devices: rotations per kWh, usually 75 or 150</li>
- <li>EM1000-EM devices: digits per kWh, usually 100</li>
- <li>EM1000-GZ devices: digits per <sup>3</sup>, usually 100</li>
- </ul>
- </li>
- </ul>
- <a name="EMXset"></a>
- <h4>Set</h4>
- <ul>
- <li><a name="emx_cnt_midnight">
- <code>set <name> cnt_midnight <int></code></a><br /> Midnight Value of internal counter </li>
- <li><a name="emx_pm_midnight">
- <code>set <name> pm_midnight <int></code></a><br /> Midnight value of external power meter</li>
- <li><a name="emx_pmeter">
- <code>set <name> pm_current <int></code></a><br /> Current value of external power meter</li>
- </ul>
- <br />
- <a name="EMXget"></a>
- <h4>Get</h4>
- <ul>
- <li><a name="emx_midnight">
- <code>get <name> midnight</code></a>
- <br /> Returns the midnight value of the counter and power meter </li>
- <li><a name="emx_cnt_midnight2">
- <code>get <name> cnt_midnight</code></a>
- <br /> Returns the midnight value of the internal counter</li>
- <li><a name="emx_pm_midnight2">
- <code>get <name> pm_midnight</code></a>
- <br /> Returns the midnight value of the power meter</li>
- <li><a name="emx_month">
- <code>get <name> month</code>
- </a>
- <br /> Returns a summary of the current month</li>
- </ul>
- <br />
- <a name="EMXattr"></a>
- <h4>Attributes</h4>
- <ul>
- <li><a name="emx_logm"><code>attr <name> <LogM>
- <string></code></a>
- <br />Device name (<i>not file name</i>) of the monthly logfile </li>
- <li><a name="emx_logy"><code>attr <name> <LogY>
- <string></code></a>
- <br />Device name (<i>not file name</i>) of the yearly logfile </li>
- <li><a name="emx_costm"><code>attr <name> <CostM>
- <float></code></a>
- <br />Cost per month</li>
- <li><a name="emx_costd"><code>attr <name> <CostD>
- <float></code></a>
- <br />Cost per unit (during daytime, when the following attributes are given)</li>
- <li><a name="emx_costn"><code>attr <name> <CostN>
- <float></code></a>
- <br />Cost per unit during night time</li>
- <li><a name="emx_cdstart"><code>attr <name> <CDStart>
- <hh:mm></code></a>
- <br />Time of day when daytime cost rate starts</li>
- <li><a name="emx_cdend"><code>attr <name> <CDEnd>
- <hh:mm></code></a>
- <br />Time of day when daytime cost rate ends</li>
- <li>Standard attributes <a href="#alias">alias</a>, <a href="#comment">comment</a>, <a
- href="#event-on-update-reading">event-on-update-reading</a>, <a
- href="#event-on-change-reading">event-on-change-reading</a>, <a
- href="#stateFormat">stateFormat</a>, <a href="#room"
- >room</a>, <a href="#eventMap">eventMap</a>, <a href="#loglevel">loglevel</a>,
- <a href="#webCmd">webCmd</a></li>
- </ul>
-
- =end html
- =cut
|