########################################################################################
#
# 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 &lt;name&gt; EMX  &lt;code&gt; &lt;rpunit&gt;</code> or <br/>
            <code>define &lt;name&gt; EMX  emulator</code>
            <br /><br /> Define an EMX device or an emulated EM1000-WZ device <br /><br />
            
         </p>
        <ul>
            <li>
                <code>&lt;code&gt;</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>&lt;rpunit&gt;</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 &lt;name&gt; cnt_midnight &lt;int&gt;</code></a><br /> Midnight Value of internal counter </li>
            <li><a name="emx_pm_midnight">
                    <code>set &lt;name&gt; pm_midnight &lt;int&gt;</code></a><br /> Midnight value of external power meter</li>
            <li><a name="emx_pmeter">
                    <code>set &lt;name&gt; pm_current &lt;int&gt;</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 &lt;name&gt; midnight</code></a>
                <br /> Returns the midnight value of the counter and power meter </li>
                 <li><a name="emx_cnt_midnight2">
                    <code>get &lt;name&gt; cnt_midnight</code></a>
                <br /> Returns the midnight value of the internal counter</li>
                 <li><a name="emx_pm_midnight2">
                    <code>get &lt;name&gt; pm_midnight</code></a>
                <br /> Returns the midnight value of the power meter</li>
            <li><a name="emx_month">
                    <code>get &lt;name&gt; 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 &lt;name&gt; &lt;LogM&gt;
                        &lt;string&gt;</code></a>
                <br />Device name (<i>not file name</i>) of the monthly logfile </li>
            <li><a name="emx_logy"><code>attr &lt;name&gt; &lt;LogY&gt;
                        &lt;string&gt;</code></a>
                <br />Device name (<i>not file name</i>) of the yearly logfile </li>
               <li><a name="emx_costm"><code>attr &lt;name&gt; &lt;CostM&gt;
                        &lt;float&gt;</code></a>
                <br />Cost per month</li>
                <li><a name="emx_costd"><code>attr &lt;name&gt; &lt;CostD&gt;
                        &lt;float&gt;</code></a>
                <br />Cost per unit (during daytime, when the following attributes are given)</li>
                <li><a name="emx_costn"><code>attr &lt;name&gt; &lt;CostN&gt;
                        &lt;float&gt;</code></a>
                <br />Cost per unit during night time</li>
                <li><a name="emx_cdstart"><code>attr &lt;name&gt; &lt;CDStart&gt;
                        &lt;hh:mm&gt;</code></a>
                <br />Time of day when daytime cost rate starts</li>
                <li><a name="emx_cdend"><code>attr &lt;name&gt; &lt;CDEnd&gt;
                        &lt;hh:mm&gt;</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

