2
0
mirror of https://github.com/fhem/fhem-mirror.git synced 2025-02-27 20:34:52 +00:00

77_SMAEM.pm: Added support for SMA Energymeter

77_SMASTP.pm: Added support for SMA Sunny Tripower



git-svn-id: https://svn.fhem.de/fhem/trunk@11812 2b470e98-0d58-463d-a4d8-8e2adae1ed80
This commit is contained in:
vsauer 2016-07-18 08:17:50 +00:00
parent 35f04a77bc
commit ea0709f979
4 changed files with 1207 additions and 0 deletions

@ -1,5 +1,7 @@
# Add changes at the top of the list. Keep it in ASCII, and 80-char wide.
# Do not insert empty lines here, update check depends on it.
- added: 77_SMASTP: Support for SMA Sunny Tripower Inverter
- added: 77_SMAEM: Support for SMA Energy Meter
- added: 00_HMUARTLGW: new module for eQ-3 HomeMatic UART/LanGateway
- change: 93_DbRep: function "diffValue" added, readingnames changed to
ensure valid reading order, write "-" for reading-value if no

505
fhem/FHEM/77_SMAEM.pm Normal file

@ -0,0 +1,505 @@
################################################################
# $Id$
#
# Copyright notice
#
# (c) 2016 Copyright: Volker Kettenbach
# e-mail: volker at kettenbach minus it dot de
#
# Credits:
# - DS_Starter (Heiko) for persistent readings
# and various improvements
#
# Description:
# This is an FHEM-Module for the SMA Energy Meter,
# a bidirectional energy meter/counter used in photovoltaics
#
# Requirements:
# This module requires:
# - Perl Module: IO::Socket::Multicast
# On a Debian (based) system, these requirements can be fullfilled by:
# - apt-get install install libio-socket-multicast-perl
#
# Origin:
# https://github.com/kettenbach-it/FHEM-SMA-Speedwire
#
################################################################
package main;
use strict;
use warnings;
use bignum;
use IO::Socket::Multicast;
#####################################
sub
SMAEM_Initialize($)
{
my ($hash) = @_;
$hash->{ReadFn} = "SMAEM_Read";
$hash->{DefFn} = "SMAEM_Define";
$hash->{UndefFn} = "SMAEM_Undef";
$hash->{DeleteFn} = "SMAEM_Delete";
#$hash->{WriteFn} = "SMAEM_Write";
#$hash->{ReadyFn} = "SMAEM_Ready";
#$hash->{GetFn} = "SMAEM_Get";
#$hash->{SetFn} = "SMAEM_Set";
$hash->{AttrFn} = "SMAEM_Attr";
$hash->{AttrList} = "interval ".
"disableSernoInReading:1,0 ".
"feedinPrice ".
"powerCost ".
"$readingFnAttributes";
}
#####################################
sub
SMAEM_Define($$)
{
my ($hash, $def) = @_;
my $name= $hash->{NAME};
my ($success, $gridin_sum, $gridout_sum);
$hash->{INTERVAL} = 60 ;
$hash->{LASTUPDATE}=0;
$hash->{HELPER}{LASTUPDATE} = 0;
Log3 $hash, 3, "$name - Opening multicast socket...";
my $socket = IO::Socket::Multicast->new(
Proto => 'udp',
LocalPort => '9522',
ReuseAddr => '1',
ReusePort => defined(&ReusePort) ? 1 : 0,
) or return "Can't bind : $@";
$socket->mcast_add('239.12.255.254');
$hash->{TCPDev}= $socket;
$hash->{FD} = $socket->fileno();
delete($readyfnlist{"$name"});
$selectlist{"$name"} = $hash;
# gespeicherte Energiezählerwerte von File einlesen
($success, $gridin_sum, $gridout_sum) = getsum($hash);
if ($success) {
$hash->{GRIDIN_SUM} = $gridin_sum;
$hash->{GRIDOUT_SUM} = $gridout_sum;
Log3 $name, 3, "$name - read saved energy values from file - GRIDIN_SUM: $gridin_sum, GRIDOUT_SUM: $gridout_sum";
}
return undef;
}
sub
SMAEM_Undef($$)
{
my ($hash, $arg) = @_;
my $name= $hash->{NAME};
my $socket= $hash->{TCPDev};
Log3 $hash, 3, "$name: Closing multicast socket...";
$socket->mcast_drop('239.12.255.254');
# $socket->close;
my $ret = close($hash->{TCPDev});
Log3 $hash, 4, "$name: Close-ret: $ret";
delete($hash->{TCPDev});
delete($selectlist{"$name"});
delete($hash->{FD});
return;
}
sub SMAEM_Delete {
my ($hash, $arg) = @_;
my $index = $hash->{TYPE}."_".$hash->{NAME}."_energysum";
# gespeicherte Energiezählerwerte löschen
setKeyValue($index, undef);
return undef;
}
sub SMAEM_Attr {
my ($cmd,$name,$aName,$aVal) = @_;
my $hash = $defs{$name};
# $cmd can be "del" or "set"
# $name is device name
# aName and aVal are Attribute name and value
if ($aName eq "interval") {
if($cmd eq "set") {
$hash->{INTERVAL} = $aVal;
} else {
$hash->{INTERVAL} = "60";
}
}
if ($aName eq "disableSernoInReading") {
delete $defs{$name}{READINGS};
readingsSingleUpdate($hash, "state", "readingsreset", 1);
}
return undef;
}
#####################################
# called from the global loop, when the select for hash->{FD} reports data
sub SMAEM_Read($)
{
my ($hash) = @_;
my $name= $hash->{NAME};
my $socket= $hash->{TCPDev};
my $data;
return unless $socket->recv($data, 600); # Each SMAEM packet is 600 bytes of packed payload
Log3 $hash, 5, "$name: Received " . length($data) . " bytes.";
if ($hash->{HELPER}{LASTUPDATE} == 0 || time() >= $hash->{HELPER}{LASTUPDATE}+$hash->{INTERVAL}) {
# Format of the udp packets of the SMAEM:
# http://www.sma.de/fileadmin/content/global/Partner/Documents/SMA_Labs/EMETER-Protokoll-TI-de-10.pdf
# http://www.eb-systeme.de/?page_id=1240
# Conversion like in this python code:
# http://www.unifox.at/sma_energy_meter/
# https://github.com/datenschuft/SMA-EM
# unpack big-endian to 2-digit hex (bin2hex)
my $hex=unpack('H*', $data);
# Extract datasets from hex:
# Generic:
my $susyid=hex(substr($hex,36,4));
my $smaserial=hex(substr($hex,40,8));
my $milliseconds=hex(substr($hex,48,8));
#readingsBulkUpdate($hash, "SUSy-ID", $susyid);
#readingsBulkUpdate($hash, "Seriennummer", $smaserial);
# Counter Divisor: [Hex-Value]=Ws => Ws/1000*3600=kWh => divide by 3600000
# Sum L1-3
my $bezug_wirk=hex(substr($hex,64,8))/10;
my $bezug_wirk_count=hex(substr($hex,80,16))/3600000;
my $einspeisung_wirk=hex(substr($hex,104,8))/10;
my $einspeisung_wirk_count=hex(substr($hex,120,16))/3600000;
# Prestring with NAME and SERIALNO or not
my $ps = (!AttrVal($name, "disableSernoInReading", undef)) ? "SMAEM".$smaserial."_" : "";
readingsBeginUpdate($hash);
readingsBulkUpdate($hash, "state", sprintf("%.1f", $einspeisung_wirk-$bezug_wirk));
readingsBulkUpdate($hash, $ps."Saldo_Wirkleistung", sprintf("%.1f",$einspeisung_wirk-$bezug_wirk));
readingsBulkUpdate($hash, $ps."Saldo_Wirkleistung_Zaehler", sprintf("%.1f",$einspeisung_wirk_count-$bezug_wirk_count));
readingsBulkUpdate($hash, $ps."Bezug_Wirkleistung", sprintf("%.1f",$bezug_wirk));
readingsBulkUpdate($hash, $ps."Bezug_Wirkleistung_Zaehler", sprintf("%.4f",$bezug_wirk_count));
readingsBulkUpdate($hash, $ps."Einspeisung_Wirkleistung", sprintf("%.1f",$einspeisung_wirk));
readingsBulkUpdate($hash, $ps."Einspeisung_Wirkleistung_Zaehler", sprintf("%.4f",$einspeisung_wirk_count));
if(!$hash->{GRIDOUT_SUM} || ReadingsVal($name,$ps."Bezug_Wirkleistung_Zaehler","") < $hash->{GRIDOUT_SUM}) {
$hash->{GRIDOUT_SUM} = sprintf("%.4f",$bezug_wirk_count);
} else {
if (ReadingsVal($name,$ps."Bezug_Wirkleistung_Zaehler","") >= $hash->{GRIDOUT_SUM}) {
my $diffb = $bezug_wirk_count - $hash->{GRIDOUT_SUM};
$hash->{GRIDOUT_SUM} = sprintf("%.4f",$bezug_wirk_count);
readingsBulkUpdate($hash, $ps."Bezug_WirkP_Zaehler_Diff", $diffb);
readingsBulkUpdate($hash, $ps."Bezug_WirkP_Kosten_Diff", sprintf("%.4f", $diffb*AttrVal($hash->{NAME}, "powerCost", 0)));
}
}
if(!$hash->{GRIDIN_SUM} || ReadingsVal($name,$ps."Einspeisung_Wirkleistung_Zaehler","") < $hash->{GRIDIN_SUM}) {
$hash->{GRIDIN_SUM} = sprintf("%.4f",$einspeisung_wirk_count);
} else {
if (ReadingsVal($name,$ps."Einspeisung_Wirkleistung_Zaehler","") >= $hash->{GRIDIN_SUM}) {
my $diffe = $einspeisung_wirk_count - $hash->{GRIDIN_SUM};
$hash->{GRIDIN_SUM} = sprintf("%.4f",$einspeisung_wirk_count);
readingsBulkUpdate($hash, $ps."Einspeisung_WirkP_Zaehler_Diff", $diffe);
readingsBulkUpdate($hash, $ps."Einspeisung_WirkP_Verguet_Diff", sprintf("%.4f", $diffe*AttrVal($hash->{NAME}, "feedinPrice", 0)));
}
}
# GRIDIN_SUM und GRIDOUT_SUM in File schreiben
my $success = setsum($hash, $hash->{GRIDIN_SUM}, $hash->{GRIDOUT_SUM});
my $bezug_blind=hex(substr($hex,144,8))/10;
my $bezug_blind_count=hex(substr($hex,160,16))/3600000;
my $einspeisung_blind=hex(substr($hex,184,8))/10;
my $einspeisung_blind_count=hex(substr($hex,200,16))/3600000;
readingsBulkUpdate($hash, $ps."Bezug_Blindleistung", sprintf("%.1f",$bezug_blind));
readingsBulkUpdate($hash, $ps."Bezug_Blindleistung_Zaehler", sprintf("%.1f",$bezug_blind_count));
readingsBulkUpdate($hash, $ps."Einspeisung_Blindleistung", sprintf("%.1f",$einspeisung_blind));
readingsBulkUpdate($hash, $ps."Einspeisung_Blindleistung_Zaehler", sprintf("%.1f",$einspeisung_blind_count));
my $bezug_schein=hex(substr($hex,224,8))/10;
my $bezug_schein_count=hex(substr($hex,240,16))/3600000;
my $einspeisung_schein=hex(substr($hex,264,8))/10;
my $einspeisung_schein_count=hex(substr($hex,280,16))/3600000;
readingsBulkUpdate($hash, $ps."Bezug_Scheinleistung", sprintf("%.1f",$bezug_schein));
readingsBulkUpdate($hash, $ps."Bezug_Scheinleistung_Zaehler", sprintf("%.1f",$bezug_schein_count));
readingsBulkUpdate($hash, $ps."Einspeisung_Scheinleistung", sprintf("%.1f",$einspeisung_schein));
readingsBulkUpdate($hash, $ps."Einspeisung_Scheinleistung_Zaehler", sprintf("%.1f",$einspeisung_schein_count));
my $cosphi=hex(substr($hex,304,8))/1000;
readingsBulkUpdate($hash, $ps."CosPhi", sprintf("%.3f",$cosphi));
# L1
my $l1_bezug_wirk=hex(substr($hex,320,8))/10;
my $l1_bezug_wirk_count=hex(substr($hex,336,16))/3600000;
my $l1_einspeisung_wirk=hex(substr($hex,360,8))/10;
my $l1_einspeisung_wirk_count=hex(substr($hex,376,16))/3600000;
readingsBulkUpdate($hash, $ps."L1_Saldo_Wirkleistung", sprintf("%.1f",$l1_einspeisung_wirk-$l1_bezug_wirk));
readingsBulkUpdate($hash, $ps."L1_Saldo_Wirkleistung_Zaehler", sprintf("%.1f",$l1_einspeisung_wirk_count-$l1_bezug_wirk_count));
readingsBulkUpdate($hash, $ps."L1_Bezug_Wirkleistung", sprintf("%.1f",$l1_bezug_wirk));
readingsBulkUpdate($hash, $ps."L1_Bezug_Wirkleistung_Zaehler", sprintf("%.1f",$l1_bezug_wirk_count));
readingsBulkUpdate($hash, $ps."L1_Einspeisung_Wirkleistung", sprintf("%.1f",$l1_einspeisung_wirk));
readingsBulkUpdate($hash, $ps."L1_Einspeisung_Wirkleistung_Zaehler", sprintf("%.1f",$l1_einspeisung_wirk_count));
my $l1_bezug_blind=hex(substr($hex,400,8))/10;
my $l1_bezug_blind_count=hex(substr($hex,416,16))/3600000;
my $l1_einspeisung_blind=hex(substr($hex,440,8))/10;
my $l1_einspeisung_blind_count=hex(substr($hex,456,16))/3600000;
readingsBulkUpdate($hash, $ps."L1_Bezug_Blindleistung", sprintf("%.1f",$l1_bezug_blind));
readingsBulkUpdate($hash, $ps."L1_Bezug_Blindleistung_Zaehler", sprintf("%.1f",$l1_bezug_blind_count));
readingsBulkUpdate($hash, $ps."L1_Einspeisung_Blindleistung", sprintf("%.1f",$l1_einspeisung_blind));
readingsBulkUpdate($hash, $ps."L1_Einspeisung_Blindleistung_Zaehler", sprintf("%.1f",$l1_einspeisung_blind_count));
my $l1_bezug_schein=hex(substr($hex,480,8))/10;
my $l1_bezug_schein_count=hex(substr($hex,496,16))/3600000;
my $l1_einspeisung_schein=hex(substr($hex,520,8))/10;
my $l1_einspeisung_schein_count=hex(substr($hex,536,16))/3600000;
readingsBulkUpdate($hash, $ps."L1_Bezug_Scheinleistung", sprintf("%.1f",$l1_bezug_schein));
readingsBulkUpdate($hash, $ps."L1_Bezug_Scheinleistung_Zaehler", sprintf("%.1f",$l1_bezug_schein_count));
readingsBulkUpdate($hash, $ps."L1_Einspeisung_Scheinleistung", sprintf("%.1f",$l1_einspeisung_schein));
readingsBulkUpdate($hash, $ps."L1_Einspeisung_Scheinleistung_Zaehler", sprintf("%.1f",$l1_einspeisung_schein_count));
my $l1_thd=hex(substr($hex,560,8))/1000;
my $l1_v=hex(substr($hex,576,8))/1000;
my $l1_cosphi=hex(substr($hex,592,8))/1000;
readingsBulkUpdate($hash, $ps."L1_THD", sprintf("%.2f",$l1_thd));
readingsBulkUpdate($hash, $ps."L1_Spannung", sprintf("%.1f",$l1_v));
readingsBulkUpdate($hash, $ps."L1_CosPhi", sprintf("%.3f",$l1_cosphi));
# L2
my $l2_bezug_wirk=hex(substr($hex,608,8))/10;
my $l2_bezug_wirk_count=hex(substr($hex,624,16))/3600000;
my $l2_einspeisung_wirk=hex(substr($hex,648,8))/10;
my $l2_einspeisung_wirk_count=hex(substr($hex,664,16))/3600000;
readingsBulkUpdate($hash, $ps."L2_Saldo_Wirkleistung", sprintf("%.1f",$l2_einspeisung_wirk-$l2_bezug_wirk));
readingsBulkUpdate($hash, $ps."L2_Saldo_Wirkleistung_Zaehler", sprintf("%.1f",$l2_einspeisung_wirk_count-$l2_bezug_wirk_count));
readingsBulkUpdate($hash, $ps."L2_Bezug_Wirkleistung", sprintf("%.1f",$l2_bezug_wirk));
readingsBulkUpdate($hash, $ps."L2_Bezug_Wirkleistung_Zaehler", sprintf("%.1f",$l2_bezug_wirk_count));
readingsBulkUpdate($hash, $ps."L2_Einspeisung_Wirkleistung", sprintf("%.1f",$l2_einspeisung_wirk));
readingsBulkUpdate($hash, $ps."L2_Einspeisung_Wirkleistung_Zaehler", sprintf("%.1f",$l2_einspeisung_wirk_count));
my $l2_bezug_blind=hex(substr($hex,688,8))/10;
my $l2_bezug_blind_count=hex(substr($hex,704,16))/3600000;
my $l2_einspeisung_blind=hex(substr($hex,728,8))/10;
my $l2_einspeisung_blind_count=hex(substr($hex,744,16))/3600000;
readingsBulkUpdate($hash, $ps."L2_Bezug_Blindleistung", sprintf("%.1f",$l2_bezug_blind));
readingsBulkUpdate($hash, $ps."L2_Bezug_Blindleistung_Zaehler", sprintf("%.1f",$l2_bezug_blind_count));
readingsBulkUpdate($hash, $ps."L2_Einspeisung_Blindleistung", sprintf("%.1f",$l2_einspeisung_blind));
readingsBulkUpdate($hash, $ps."L2_Einspeisung_Blindleistung_Zaehler", sprintf("%.1f",$l2_einspeisung_blind_count));
my $l2_bezug_schein=hex(substr($hex,768,8))/10;
my $l2_bezug_schein_count=hex(substr($hex,784,16))/3600000;
my $l2_einspeisung_schein=hex(substr($hex,808,8))/10;
my $l2_einspeisung_schein_count=hex(substr($hex,824,16))/3600000;
readingsBulkUpdate($hash, $ps."L2_Bezug_Scheinleistung", sprintf("%.1f",$l2_bezug_schein));
readingsBulkUpdate($hash, $ps."L2_Bezug_Scheinleistung_Zaehler", sprintf("%.1f",$l2_bezug_schein_count));
readingsBulkUpdate($hash, $ps."L2_Einspeisung_Scheinleistung", sprintf("%.1f",$l2_einspeisung_schein));
readingsBulkUpdate($hash, $ps."L2_Einspeisung_Scheinleistung_Zaehler", sprintf("%.1f",$l2_einspeisung_schein_count));
my $l2_thd=hex(substr($hex,848,8))/1000;
my $l2_v=hex(substr($hex,864,8))/1000;
my $l2_cosphi=hex(substr($hex,880,8))/1000;
readingsBulkUpdate($hash, $ps."L2_THD", sprintf("%.2f",$l2_thd));
readingsBulkUpdate($hash, $ps."L2_Spannung", sprintf("%.1f",$l2_v));
readingsBulkUpdate($hash, $ps."L2_CosPhi", sprintf("%.3f",$l2_cosphi));
# L3
my $l3_bezug_wirk=hex(substr($hex,896,8))/10;
my $l3_bezug_wirk_count=hex(substr($hex,912,16))/3600000;
my $l3_einspeisung_wirk=hex(substr($hex,936,8))/10;
my $l3_einspeisung_wirk_count=hex(substr($hex,952,16))/3600000;
readingsBulkUpdate($hash, $ps."L3_Saldo_Wirkleistung", sprintf("%.1f",$l3_einspeisung_wirk-$l3_bezug_wirk));
readingsBulkUpdate($hash, $ps."L3_Saldo_Wirkleistung_Zaehler", sprintf("%.1f",$l3_einspeisung_wirk_count-$l3_bezug_wirk_count));
readingsBulkUpdate($hash, $ps."L3_Bezug_Wirkleistung", sprintf("%.1f",$l3_bezug_wirk));
readingsBulkUpdate($hash, $ps."L3_Bezug_Wirkleistung_Zaehler", sprintf("%.1f",$l3_bezug_wirk_count));
readingsBulkUpdate($hash, $ps."L3_Einspeisung_Wirkleistung", sprintf("%.1f",$l3_einspeisung_wirk));
readingsBulkUpdate($hash, $ps."L3_Einspeisung_Wirkleistung_Zaehler", sprintf("%.1f",$l3_einspeisung_wirk_count));
my $l3_bezug_blind=hex(substr($hex,976,8))/10;
my $l3_bezug_blind_count=hex(substr($hex,992,16))/3600000;
my $l3_einspeisung_blind=hex(substr($hex,1016,8))/10;
my $l3_einspeisung_blind_count=hex(substr($hex,1032,16))/3600000;
readingsBulkUpdate($hash, $ps."L3_Bezug_Blindleistung", sprintf("%.1f",$l3_bezug_blind));
readingsBulkUpdate($hash, $ps."L3_Bezug_Blindleistung_Zaehler", sprintf("%.1f",$l3_bezug_blind_count));
readingsBulkUpdate($hash, $ps."L3_Einspeisung_Blindleistung", sprintf("%.1f",$l3_einspeisung_blind));
readingsBulkUpdate($hash, $ps."L3_Einspeisung_Blindleistung_Zaehler", sprintf("%.1f",$l3_einspeisung_blind_count));
my $l3_bezug_schein=hex(substr($hex,1056,8))/10;
my $l3_bezug_schein_count=hex(substr($hex,1072,16))/3600000;
my $l3_einspeisung_schein=hex(substr($hex,1096,8))/10;
my $l3_einspeisung_schein_count=hex(substr($hex,1112,16))/3600000;
readingsBulkUpdate($hash, $ps."L3_Bezug_Scheinleistung", sprintf("%.1f",$l3_bezug_schein));
readingsBulkUpdate($hash, $ps."L3_Bezug_Scheinleistung_Zaehler", sprintf("%.1f",$l3_bezug_schein_count));
readingsBulkUpdate($hash, $ps."L3_Einspeisung_Scheinleistung", sprintf("%.1f",$l3_einspeisung_schein));
readingsBulkUpdate($hash, $ps."L3_Einspeisung_Scheinleistung_Zaehler", sprintf("%.1f",$l3_einspeisung_schein_count));
my $l3_thd=hex(substr($hex,1136,8))/1000;
my $l3_v=hex(substr($hex,1152,8))/1000;
my $l3_cosphi=hex(substr($hex,1168,8))/1000;
readingsBulkUpdate($hash, $ps."L3_THD", sprintf("%.2f",$l3_thd));
readingsBulkUpdate($hash, $ps."L3_Spannung", sprintf("%.1f",$l3_v));
readingsBulkUpdate($hash, $ps."L3_CosPhi", sprintf("%.3f",$l3_cosphi));
readingsEndUpdate($hash, 1);
$hash->{HELPER}{LASTUPDATE}=time();
# $update time
my ($sec,$min,$hour,$mday,$mon,$year,undef,undef,undef) = localtime;
$hash->{LASTUPDATE} = sprintf "%02d.%02d.%04d / %02d:%02d:%02d" , $mday , $mon+=1 ,$year+=1900 , $hour , $min , $sec ;
}
}
######################################################################################
### Summenwerte für GridIn, GridOut speichern
sub setsum ($$$) {
my ($hash, $gridin_sum, $gridout_sum) = @_;
my $name = $hash->{NAME};
my $success;
my $index;
my $retcode;
my $sumstr;
$sumstr = $gridin_sum."_".$gridout_sum;
$index = $hash->{TYPE}."_".$hash->{NAME}."_energysum";
$retcode = setKeyValue($index, $sumstr);
if ($retcode) {
Log3($name, 1, "$name - Error while saving summary of energy values - $retcode");
$success = 0;
}
else
{
Log3($name, 4, "$name - summary of energy values saved - GRIDIN_SUM: $gridin_sum, GRIDOUT_SUM: $gridout_sum");
$success = 1;
}
return ($success);
}
######################################################################################
### Summenwerte für GridIn, GridOut abtufen
sub getsum ($) {
my ($hash) = @_;
my $name = $hash->{NAME};
my $success;
my $index;
my $retcode;
my $sumstr;
my ($gridin_sum, $gridout_sum);
$index = $hash->{TYPE}."_".$hash->{NAME}."_energysum";
($retcode, $sumstr) = getKeyValue($index);
if ($retcode) {
Log3($name, 1, "$name - ERROR -unable to read summary of energy values from file - $retcode");
$success = 0;
}
if ($sumstr) {
($gridin_sum, $gridout_sum) = split(/_/, $sumstr);
Log3($name, 4, "$name - summary of energy values was read from file - GRIDIN_SUM: $gridin_sum, GRIDOUT_SUM: $gridout_sum");
$success = 1;
}
return ($success, $gridin_sum, $gridout_sum);
}
######################################################################################
1;
=pod
=begin html
<a name="SMAEM"></a>
<h3>SMAEM</h3>
<ul>
<br>
<a name="SMAEM"></a>
<b>Define</b>
<ul>
<code>define &lt;name&gt; SMAEM </code><br>
<br>
Defines a SMA Energy Meter (SMAEM), a bidirectional energy meter/counter used in photovoltaics.
<br><br>
You need at least one SMAEM on your local subnet or behind a multicast enabled network of routers to receive multicast messages from the SMAEM over the
multicast group 239.12.255.254 on udp/9522. Multicast messages are sent by SMAEM once a second (firmware 1.02.04.R, March 2016).
<br><br>
The update interval will be set by attribute "interval". If not set, it defaults to 60s. Since the SMAEM sends updates once a second, you can
update the readings once a second by lowering the interval to 1 (Not recommended, since it puts FHEM under heavy load).
<br><br>
The parameter "disableSernoInReading" changes the way readings are named: if disableSernoInReading is false or unset, the readings will be named
"SMAEMserialnooftheEM_.....
If set to true, the prefix SMA_serialnooftheEM_ is skipped.
Set this to true if you only have one SMAEM device on your network and you want shorter reading names.
If unsure, leave it unset.
<br><br>
You need the perl module IO::Socket::Multicast. Under Debian (based) systems it can be installed with <code>apt-get install libio-socket-multicast-perl</code>.
</ul>
</ul>
=end html
=begin html_DE
<a name="SMAEM"></a>
<h3>SMAEM</h3>
<ul>
<br>
<a name="SMAEM"></a>
<b>Define</b>
<ul>
<code>define &lt;name&gt; SMAEM </code><br>
<br>
Definiert ein SMA Energy Meter (SMAEM), einen bidirektionalen Stromzähler, der häufig in Photovolatikanlagen der Firma SMA zum Einsatz kommt.
<br><br>
Sie brauchen mindest ein SMAEM in Ihrem lokalen Netzwerk oder hinter einemmulticast fähigen Netz von Routern, um die Daten des SMAEM über die
Multicastgruppe 239.12.255.254 auf udp/9522 zu empfangen. Die Multicastpakete werden vom SMAEM einmal pro Sekunde ausgesendet (firmware 1.02.04.R, März 2016).
<br><br>
Das update interval kann über das Attribut "interval" gesetzt werden. Wenn es nicht gesetzt wird, werden updates per default alle 60 Sekunden durchgeführt.
Da das SMAEM seine Daten sekündlich aktualisiert, kann das update interval auf bis zu einer Sekunde reduziert werden. Das wird nicht empfohlen, da FHEM
sonst unter große Last gesetzt wird.
<br><br>
Der Parameter "disableSernoInReading" ändert die Art und Weise, wie die Readings des SMAEN bezeichnet werden: ist der Parameter false oder nicht gesetzt,
werden die Readings mit "SMAEMserialnodesEM_....." bezeichnet.
Wird der Parameter auf true gesetzt, wird das Prefix "SMAEMserialnodesEM_....." weg gelassen.
Sie können diesen Parameter auf true setzen, wenn Sie nicht mehr als ein SMAEM-Gerät in Ihrem Netzwerk haben und kürzere Namen für die Readings wünschen.
Falls Sie unsicher sind, setzen Sie diesen Parameter nicht.
<br><br>
Sie benötigen das Perl-Module IO::Socket::Multicast für dieses FHEM Modul. Unter Debian (basierten) System, kann dies mittels <code>apt-get install libio-socket-multicast-perl</code> installiert werden.
</ul>
</ul>
=end html_DE

698
fhem/FHEM/77_SMASTP.pm Normal file

@ -0,0 +1,698 @@
###############################################################
# $Id$
#
# Copyright notice
#
# (c) 2016 Copyright: Volker Kettenbach (volker at kettenbach minus it dot de)
#
# Credits:
# - based on an Idea by SpenZerX and HDO
# - Waldmensch for various improvements
# - sbfspot (https://sbfspot.codeplex.com/)
#
# Description:
# This is an FHEM-Module for the SMA Sunny Tripower Inverter.
# Tested on Sunny Tripower 6000TL-20, 10000-TL20 and 10000TL-10 with
# Speedwire/Webconnect Piggyback
#
# Requirements:
# This module requires:
# - Perl Module: IO::Socket::INET
# - Perl Module: Datime
#
# Origin:
# https://github.com/kettenbach-it/FHEM-SMA-Speedwire
#
###############################################################
package main;
use strict;
use warnings;
use IO::Socket::INET;
use DateTime;
# Global vars
my $cmd_login = "534d4100000402a000000001003a001060650ea0ffffffffffff00017800C8E8033800010000000004800c04fdff07000000840300004c20cb5100000000encpw00000000";
my $cmd_logout = "534d4100000402a00000000100220010606508a0ffffffffffff00037800C8E80338000300000000d7840e01fdffffffffff00000000";
my $cmd_query_total_today = "534d4100000402a00000000100260010606509e0ffffffffffff00007800C8E80338000000000000f1b10002005400002600ffff260000000000";
my $cmd_query_spot_ac_power = "534d4100000402a00000000100260010606509e0ffffffffffff00007800C8E8033800000000000081f00002005100002600ffff260000000000";
my $cmd_query_spot_dc_power = "534d4100000402a00000000100260010606509e0ffffffffffff00007800C8E8033800000000000081f00002805300002500ffff260000000000";
my $averagebuf = "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000";
my $code_login = "0d04fdff"; #0xfffd040d;
my $code_total_today = "01020054"; #0x54000201;
my $code_spot_ac_power = "01020051"; #0x51000201;
my $code_spot_dc_power = "01028053"; #0x53800201;
my $default_starthour = "05:00";
my $starthour = 5;
my $startminute = 0;
my $default_endhour = "22:00";
my $endhour = 22;
my $endminute = 0;
my $force_sleep = 0;
my $sleep_forced = 0;
my $suppress_night_mode = 0;
my $suppress_inactivity_mode = 0;
my $modulstate_enabled = 0;
my ($alarm_value1,$alarm_value2,$alarm_value3);
###################################
sub SMASTP_Initialize($)
{
my ($hash) = @_;
my $name = $hash->{NAME};
my $hval;
my $mval;
$hash->{DefFn} = "SMASTP_Define";
$hash->{UndefFn} = "SMASTP_Undef";
$hash->{AttrList} = "suppress-night-mode:0,1 " .
"suppress-inactivity-mode:0,1 " .
"starttime " .
"endtime " .
"force-sleepmode:0,1 " .
"enable-modulstate:0,1 " .
"alarm1-value " .
"alarm2-value " .
"alarm3-value " .
"interval " .
$readingFnAttributes;
$hash->{AttrFn} = "SMASTP_Attr";
if ($attr{$name}{"starttime"})
{
($hval, $mval) = split(/:/,$attr{$name}{"starttime"});
}
else
{
($hval, $mval) = split(/:/,$default_starthour);
}
$starthour = int($hval);
$startminute = int($mval);
if ($attr{$name}{"endtime"})
{
($hval, $mval) = split(/:/,$attr{$name}{"endtime"});
}
else
{
($hval, $mval) = split(/:/,$default_endhour);
}
$endhour = int($hval);
$endminute = int($mval);
$suppress_night_mode = ($attr{$name}{"suppress-night-mode"}) ? $attr{$name}{"suppress-night-mode"} : 0;
$suppress_inactivity_mode = ($attr{$name}{"suppress-inactivity-mode"}) ? $attr{$name}{"suppress-inactivity-mode"} : 0;
$force_sleep = ($attr{$name}{"force-sleepmode"}) ? $attr{$name}{"force-sleepmode"} : 0;
$modulstate_enabled = ($attr{$name}{"enable-modulstate"}) ? $attr{$name}{"enable-modulstate"} : 0;
$alarm_value1 = ($attr{$name}{"alarm1-value"}) ? $attr{$name}{"alarm1-value"} : 0;
$alarm_value2 = ($attr{$name}{"alarm2-value"}) ? $attr{$name}{"alarm2-value"} : 0;
$alarm_value3 = ($attr{$name}{"alarm3-value"}) ? $attr{$name}{"alarm3-value"} : 0;
Log3 $name, 0, "$name: Started with sleepmode from $endhour:$endminute - $starthour:$startminute";
}
###################################
sub is_Sleepmode()
{
# Build 3 DateTime Objects to make the comparison more robust
my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime();
my $dt_startdate = DateTime->new(year=>$year+1900,month=>$mon+1,day=>$mday,hour=>$starthour,minute=>$startminute,second=>0,time_zone=>'local');
my $dt_enddate = DateTime->new(year=>$year+1900,month=>$mon+1,day=>$mday,hour=>$endhour,minute=>$endminute,second=>0,time_zone=>'local');
my $dt_now = DateTime->now(time_zone=>'local');
# Return of any value != 0 means "sleeping"
if ($dt_now >= $dt_enddate || $dt_now <= $dt_startdate)
{
# switch forced sleepmode off because we have reached normal sleepmode now
$sleep_forced = 0;
return 1;
}
elsif ($sleep_forced == 1)
{
# 2 = forced sleep
return 2;
}
else
{
return 0;
}
}
###################################
sub SMASTP_Define($$)
{
my ($hash, $def) = @_;
my @a = split("[ \t][ \t]*", $def);
return "Wrong syntax: use define <name> SMASTP <inv-userpwd> <inv-hostname/inv-ip > " if ((int(@a) < 4) and (int(@a) > 5));
my $name = $a[0];
$hash->{NAME} = $name;
$hash->{LASTUPDATE}=0;
$hash->{INTERVAL} = 60;
# SMASTP = $a[1];
my ($IP,$Host,$Caps);
my $Pass = $a[2]; # to do: check 1-12 Chars
# extract IP or Hostname from $a[4]
if ( $a[3] ~~ m/^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/ )
{
if ( $1 <= 255 && $2 <= 255 && $3 <= 255 && $4 <= 255 )
{
$Host = int($1).".".int($2).".".int($3).".".int($4);
}
}
if (!defined $Host)
{
if ( $a[3] =~ /^([A-Za-z0-9_.])/ )
{
$Host = $a[3];
}
}
if (!defined $Host)
{
return "Argument:{$a[3]} not accepted as Host or IP. Read device specific help file.";
}
$hash->{Pass} = $Pass;
$hash->{Host} = $Host;
# Use C8E80338, but NOT the number of the Inverter!
# my $src_serial = 939780296;
my $encpw = "888888888888888888888888"; # unencoded pw
for my $index (0..length $Pass ) # encode password
{
substr($encpw,($index*2),2) = substr(sprintf ("%lX", (hex(substr($encpw,($index*2),2)) + ord(substr($Pass,$index,1)))),0,2);
}
$cmd_login =~ s/encpw/$encpw/g; #replace the placeholder with password
InternalTimer(gettimeofday()+5, "SMASTP_GetStatus", $hash, 0); # refresh timer start
return undef;
}
#####################################
sub SMASTP_Undef($$)
{
my ($hash, $name) = @_;
RemoveInternalTimer($hash);
Log3 $hash, 0, "$name: Undefined!";
return undef;
}
###################################
sub SMASTP_Attr(@)
{
my ($cmd,$name,$aName,$aVal) = @_;
# $cmd can be "del" or "set"
# $name is device name
# aName and aVal are Attribute name and value
my $hash = $defs{$name};
my $hval;
my $mval;
if (($aName eq "starttime" || $aName eq "endtime") && not ($aVal =~ /^([0-1]?[0-9]|[2][0-3]):([0-5][0-9])$/))
{
return "value $aVal invalid"; # no correct time format hh:mm
}
if ($aName eq "enable-modulstate")
{
$modulstate_enabled = ($cmd eq "set") ? int($aVal) : 0;
Log3 $name, 3, "$name: Set $aName to $aVal";
}
if ($aName eq "alarm1-value")
{
$alarm_value1 = ($cmd eq "set") ? int($aVal) : 0;
Log3 $name, 3, "$name: Set $aName to $aVal";
}
if ($aName eq "alarm2-value")
{
$alarm_value2 = ($cmd eq "set") ? int($aVal) : 0;
Log3 $name, 3, "$name: Set $aName to $aVal";
}
if ($aName eq "alarm3-value")
{
$alarm_value3 = ($cmd eq "set") ? int($aVal) : 0;
Log3 $name, 3, "$name: Set $aName to $aVal";
}
if ($aName eq "starttime")
{
if ($cmd eq "set")
{
($hval, $mval) = split(/:/,$aVal);
}
else
{
($hval, $mval) = split(/:/,$default_starthour);
}
if (int($hval) < 12)
{
$starthour = int($hval);
$startminute = int($mval);
}
else
{
return "$name: Attr starttime must be set smaller than 12:00! Not set to $starthour:$startminute";
}
Log3 $name, 3, "$name: Attr starttime is set to " . sprintf("%02d:%02d",$starthour,$startminute);
}
if ($aName eq "endtime")
{
if ($cmd eq "set")
{
($hval, $mval) = split(/:/,$aVal);
}
else
{
($hval, $mval) = split(/:/,$default_endhour);
}
if (int($hval) > 12)
{
$endhour = int($hval);
$endminute = int($mval);
}
else
{
return "$name: Attr endtime must be set larger than 12:00! Not set to $endhour:$endminute";
}
Log3 $name, 3, "$name: Attr endtime is set to " . sprintf("%02d:%02d",$endhour,$endminute);
}
if ($aName eq "suppress-night-mode")
{
$suppress_night_mode = ($cmd eq "set") ? $aVal : 0;
Log3 $name, 3, "$name: Set $aName to $aVal";
}
if ($aName eq "suppress-inactivity-mode")
{
$suppress_inactivity_mode = ($cmd eq "set") ? $aVal : 0;
Log3 $name, 3, "$name: Set $aName to $aVal";
}
if ($aName eq "force-sleepmode")
{
if ($cmd eq "set")
{
$force_sleep = $aVal;
$sleep_forced = ($aVal == 0) ? 0 : $sleep_forced;
Log3 $name, 3, "$name: Set $aName to $aVal";
}
else
{
$force_sleep = 0;
$sleep_forced = 0;
}
}
if ($aName eq "interval")
{
if ($cmd eq "set")
{
$hash->{INTERVAL} = $aVal;
Log3 $name, 3, "$name: Set $aName to $aVal";
}
} else
{
$hash->{INTERVAL} = "60";
Log3 $name, 3, "$name: Set $aName to $aVal";
}
return undef;
}
#####################################
sub SMASTP_GetStatus($)
{
my ($hash) = @_;
my $name = $hash->{NAME};
$hash->{INTERVAL} = $attr{$name}{"interval"};
my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime();
if ($suppress_night_mode == 0)
{
if(is_Sleepmode() > 0)
{
Log3 $name, 5, "$name: " .
sprintf("%02d:%02d",$hour,$min) .
" is out of working hours " .
sprintf("%02d:%02d",$starthour,$startminute) .
" - " .
sprintf("%02d:%02d",$endhour,$endminute) .
" " .
(($sleep_forced == 1) ? " FORCED" : "");
my $modulstate = ($hash->{READINGS}{modulstate}{VAL}) ? $hash->{READINGS}{modulstate}{VAL} : "unknown";
if($modulstate ne "sleeping" && $modulstate_enabled == 1)
{
readingsBeginUpdate($hash);
readingsBulkUpdate($hash, "modulstate", "sleeping");
readingsEndUpdate($hash, 1);
}
InternalTimer(gettimeofday()+$hash->{INTERVAL}, "SMASTP_GetStatus", $hash, 1);
return;
}
}
use constant MAXBYTES => scalar 200; #1024 #80
my $Host = $hash->{Host};
my $interval = $hash->{INTERVAL};
# my $averagebuf = $hash->{averagebuf};
my ($AvP01,$AvP05,$AvP15,$TodayTotal,$SpotPower,$AlltimeTotal,$statusval,$PDC1,$PDC2);
my ($socket,$data,$size,$code);
my $error = 0;
# flush after every write
$| = 1;
$socket = new IO::Socket::INET (PeerHost => $Host, PeerPort => 9522, Proto => 'udp',); # open Socket
if (!$socket) {
# in case of error
Log3 $name, 1, "$name: ERROR. Can't open socket to inverter: $!";
return undef;
};
# send login command
Log3 $name, 2, "$name: Sending query to inverter $Host:9522";
$data = pack "H*",$cmd_login;
$socket->send($data);
do
{
eval
{
local $SIG{ALRM} = sub { die "alarm time out" };
alarm 5;
# receive data
$socket->recv($data, MAXBYTES) or die "recv: $!";
$size = length($data);
# too little data - exit loop
if ((defined $size) && ($size > 60))
{
my $received = unpack("H*", $data);
Log3 $name, 5, "$name: Received: ($received)";
} else {
if($size > 0)
{
my $received = unpack("H*", $data);
Log3 $name, 5, "$name: Received Garbage: ($received)";
}
}
alarm 0;
1;
} or Log3 $name, 1, "$name query timed out";
# too little data -> exit loop
if ((not defined $size) || ($size < 60))
{
Log3 $name, 1, "$name: Too little data received (Len:".((not defined $size) ? "NaN/timeout" : $size).")";
# send: cmd_logout
$data = pack "H*",$cmd_logout;
$size = $socket->send($data);
$socket->close();
$error = 1;
}
else
{
# unpack command
$code = unpack("H*", substr $data, 42, 4);
# answer to command login
if ($code_login eq $code)
{
# send: Query total today
$data = pack "H*",$cmd_query_total_today;
$size = $socket->send($data);
}
# answer to command total today
if ($code_total_today eq $code)
{
$TodayTotal = unpack("V*", substr $data, 78, 4);
$AlltimeTotal = unpack("V*", substr $data, 62, 4);
# send: Query spot power
$data = pack "H*",$cmd_query_spot_ac_power;
$size = $socket->send($data);
}
# answer to command AC Power
if ($code_spot_ac_power eq $code)
{
$SpotPower = unpack("V*", substr $data, 62, 4);
# special case at night ? Inverter off?
if ($SpotPower eq 0x80000000) {$SpotPower = 0};
# send: query spot DC power
$data = pack "H*",$cmd_query_spot_dc_power;
$size = $socket->send($data);
}
# answer to command DC Power
if ($code_spot_dc_power eq $code)
{
$PDC1 = unpack("V*", substr $data, 62, 4);
if ($PDC1 eq 0x80000000) {$PDC1 = 0};
$PDC2 = unpack("V*", substr $data, 90, 4);
if ($PDC2 eq 0x80000000) {$PDC2 = 0};
# send: cmd_logout
$data = pack "H*",$cmd_logout;
$size = $socket->send($data);
# close Socket
$socket->close();
}
}
} while (($code_spot_dc_power ne $code) && ($error eq 0)); # answer to command spot_ac_power
if ($error ne 1)
{
if ( (int(hex(substr($averagebuf,0*8,8)))) eq 0)
{
for my $count (0..15)
{
# fill with new values
substr($averagebuf,$count*8,1*8) = substr(sprintf ("%08X",$AlltimeTotal),0,8);
}
}
# average buffer shiften und mit neuem Wert füllen
substr($averagebuf,1*8,15*8) = substr($averagebuf,0*8,15*8);
# und mit neuem Wert füllen
substr($averagebuf,0*8,1*8) = substr(sprintf ("%08X",$AlltimeTotal),0,8);
$AvP01 = int( ( (hex(substr($averagebuf,0*8,8))) - (hex(substr($averagebuf,1*8,8))) ) * ((3600 / 01) / $interval) );
$AvP05 = int( ( (hex(substr($averagebuf,0*8,8))) - (hex(substr($averagebuf,5*8,8))) ) * ((3600 / 05) / $interval) );
$AvP15 = int( ( (hex(substr($averagebuf,0*8,8))) - (hex(substr($averagebuf,15*8,8))) ) * ((3600 / 15) / $interval) );
$statusval = "SP:$SpotPower W AvP1:$AvP01 W TTP:$TodayTotal Wh ATP:$AlltimeTotal Wh";
Log3 $name, 4, "$name: from ($Host): ($statusval) ";
Log3 $name, 5, "$name: AvP05 = $AvP05, SpotPower = $SpotPower, AvP15 = $AvP15";
#Filter out error zero values and stop readingsupdate after 15 Mins on zero power
if ( ((not ($AvP05 > 0 && $SpotPower == 0)) && $AvP15 > 0) || $suppress_inactivity_mode == 1 || $hash->{LASTUPDATE} eq 0)
{
readingsBeginUpdate($hash);
readingsBulkUpdate($hash, "state", $statusval); # Status Overview
readingsBulkUpdate($hash, "SpotP", $SpotPower); # Momentary Spot Power
readingsBulkUpdate($hash, "SpotPDC1", $PDC1); # Momentary Spot DC Power String1
readingsBulkUpdate($hash, "SpotPDC2", $PDC2); # Momentary Spot DC Power String2
readingsBulkUpdate($hash, "TodayTotalP", $TodayTotal); # Today Total Power
readingsBulkUpdate($hash, "AlltimeTotalP", $AlltimeTotal); # Alltime Total Power
readingsBulkUpdate($hash, "AvP01", $AvP01); # Average Power (last) 1 Minute (if delay 60)
readingsBulkUpdate($hash, "AvP05", $AvP05); # Average Power (last) 5 Minutes (if delay 60)
readingsBulkUpdate($hash, "AvP15", $AvP15); # Average Power (last) 15 Minutes (if delay 60)
readingsBulkUpdate($hash, "modulstate", "normal");
if($alarm_value1 > 0 && $SpotPower > $alarm_value1) { readingsBulkUpdate($hash, "Alarm1", 1); }
elsif ($alarm_value1 > 0 && $SpotPower < $alarm_value1) { readingsBulkUpdate($hash, "Alarm1", (-1)); }
else { readingsBulkUpdate($hash, "Alarm1", 0); }
if($alarm_value2 > 0 && $SpotPower > $alarm_value2) { readingsBulkUpdate($hash, "Alarm2", 1); }
elsif ($alarm_value2 > 0 && $SpotPower < $alarm_value2) { readingsBulkUpdate($hash, "Alarm2", (-1)); }
else { readingsBulkUpdate($hash, "Alarm2", 0); }
if($alarm_value3 > 0 && $SpotPower > $alarm_value3) { readingsBulkUpdate($hash, "Alarm3", 1); }
elsif ($alarm_value3 > 0 && $SpotPower < $alarm_value3) { readingsBulkUpdate($hash, "Alarm3", (-1)); }
else { readingsBulkUpdate($hash, "Alarm3", 0); }
readingsEndUpdate($hash, 1); # Notify is done by Dispatch
$hash->{LASTUPDATE} = sprintf "%02d.%02d.%04d / %02d:%02d:%02d" , $mday , $mon+=1 ,$year+=1900 , $hour , $min , $sec ;
Log3 $name, 5, "$name: Readings updated";
}
else
{
if ($AvP15 == 0 && $SpotPower == 0 && $force_sleep == 1 && $sleep_forced == 0 && $hour > 12)
{
$sleep_forced = 1;
Log3 $name, 1, "$name: sleepmode forced after 15 minutes zero power";
}
my $modulstate = ($hash->{READINGS}{modulstate}{VAL}) ? $hash->{READINGS}{modulstate}{VAL} : "";
if($modulstate ne "inactive" && $modulstate_enabled == 1)
{
readingsBeginUpdate($hash);
readingsBulkUpdate($hash, "modulstate", "inactive");
readingsEndUpdate($hash, 1);
}
Log3 $name, 5, "$name: Readings not updated";
}
}
InternalTimer(gettimeofday()+$interval, "SMASTP_GetStatus", $hash, 1);
#return undef;
}
1;
=pod
=begin html
<a name="SMASTP"></a>
<h3>SMASTP</h3>
Module for the integration of a Sunny Tripower Inverter build by SMA over it's Speedwire (=Ethernet) Interface.<br>
Tested on Sunny Tripower 6000TL-20, 10000-TL20 and 10000TL-10 with Speedwire/Webconnect Piggyback.
<p>
<b>Define</b>
<ul>
<code>define &lt;name&gt; SMASTP &lt;pin&gt; &lt;hostname/ip&gt; [port]</code><br>
<br>
<li>pin: User-Password of the SMA STP Inverter. Default is 0000. Can be changed by "Sunny Explorer" Windows Software</li>
<li>hostname/ip: Hostname or IP-Adress of the inverter (or it's speedwire piggyback module).</li>
<li>port: Port of the inverter. 9522 by default.</li>
</ul>
<p>
<b>Modus</b>
<ul>
The module automatically detects the inactvity of the inverter due to a lack of light (night). <br>
This inactivity is therefore called "nightmode". During nightmode, the inverter is not queried over the network.<br>
By default nightmode is between 9pm and 5am. This can be changed by "starttime" (start of inverter <br>
operation, end of nightmode) and "endtime" (end of inverter operation, start of nightmode).<br>
Further there is the inactivitymode: in inactivitymode, the inverter is queried but readings are not updated.
</ul>
<b>Parameter</b>
<ul>
<li>interval: Queryintreval in seconds </li>
<li>suppress-night-mode: The nightmode is deactivated </li>
<li>suppress-inactivity-mode: The inactivitymode is deactivated </li>
<li>starttime: Starttime of inverter operation (default 5am) </li>
<li>endtime: Endtime of inverter operation (default 9pm) </li>
<li>force-sleepmode: The nightmode is activated on inactivity, even the endtime is not reached </li>
<li>enable-modulstate: Turns the reading "modulstate" (normal / inactive / sleeping) on </li>
<li>alarm1-value, alarm2-value, alarm3-value: Set an alarm on the reading SpotP in watt.<br>
The readings Alarm1..Alarm3 are set accordingly: -1 for SpotP < alarmX-value and 1 for SpotP >= alarmX-value </li>
</ul>
<b>Readings</b>
<ul>
<li>SpotP: spotpower - Current power in watt delivered by the inverter </li>
<li>AvP01: average power 1 minute: average power in watt of the last minute </li>
<li>AvP05: average power 5 minutes: average power in watt of the five minutes </li>
<li>AvP15: average power 15 minutes: average power in watt of the fifteen minutes </li>
<li>SpotPDC1: current d.c. voltage delivered by string 1 </li>
<li>SpotPDC2: current d.c. voltage delivered by string 2 </li>
<li>TotalTodayP: generated power in Wh of the current day </li>
<li>AlltimeTotalP: all time generated power in Wh </li>
<li>Alarm1..3: alrm trigger 1..3. Set by parameter alarmN-value </li>
</ul>
=end html
=begin html_DE
<a name="SMASTP"></a>
<h3>SMASTP</h3>
Modul zur Einbindung eines Sunny Tripower Wechselrichters der Firma SMA über Speedwire (Ethernet).<br>
Getestet mit Sunny Tripower 6000TL-20, 10000-TL20 sowie 10000TL-10 mit Speedwire/Webconnect Piggyback
<p>
<b>Define</b>
<ul>
<code>define &lt;name&gt; SMASTP &lt;pin&gt; &lt;hostname/ip&gt; [port]</code><br>
<br>
<li>pin: Benutzer-Passwort des SMA STP Wechselrichters. Default ist 0000. Kann über die Windows-Software "Sunny Explorer" geändert werden </li>
<li>hostname/ip: Hostname oder IP-Adresse des Wechselrichters (bzw. dessen Speedwire Moduls mit Ethernetanschluss) </li>
<li>port: Optional der Ports des Wechselrichters. Per default 9522. </li>
</ul>
<p>
<b>Modus</b>
<ul>
Das Modul erkennt automatisch eine Inaktivität des Wechselrichters, wenn dieser aufgrund Dunkelheit seinen Betrieb einstellt. <br>
Diese Betriebspause wird als "Nightmode" bezeichnet. Im Nightmode wird der Wechelrichter nicht mehr über das Netzwerk abgefragt.<br>
Per default geht das Modul davon aus, dass vor 5:00 und nach 21:00 der Nightmode aktiv ist.<br>
Diese Grenzen lassen sich mit den Parametern "starttime" (Start des Wechelrichterbetriebs, also Ende des Nightmode) <br>
und "endtime" (Ende des Wechselrichterbetriebs, also Beginn des Nightmode) umdefinieren. <br>
Darüber hinaus gibt es den "Inactivitymode": hier wird der Wechselrichter abgefragt, aber es werden keine Readings mehr aktualisiert. <br>
</ul>
<b>Parameter</b>
<ul>
<li>interval: Abfrageinterval in Sekunden </li>
<li>suppress-night-mode: Der Nightmode wird deaktiviert </li>
<li>suppress-inactivity-mode: Der Inactivitymode wird deaktiviert </li>
<li>starttime: Startzeit des Betriebsmodus (Default 5:00 Uhr) </li>
<li>endtime: Endezeit des Betriebsmodus (Default 21:00 Uhr) </li>
<li>force-sleepmode: Der Nightmode wird bei entdeckter Inaktivität auch dann aktiviert, wenn endtime noch nicht erreicht ist </li>
<li>enable-modulstate: Schaltet das reading "modulstate" (normal / inactive / sleeping) ein </li>
<li>alarm1-value, alarm2-value, alarm3-value: Setzt einen Alarm in Watt auf das Reading SpotP.
<br>Die Readings Alarm1..Alarm3 werden entsprechend gesetzt: -1 für SpotP < alarmX-value und 1 für Spot >= alarmX-value. </li>
</ul>
<b>Readings</b>
<ul>
<li>SpotP: SpotPower - Leistung in W zum Zeitpunkt der Abfrage</li>
<li>AvP01: Average Power 1 Minute - Durchschnittliche Leistung in W der letzten Minute</li>
<li>AvP05: Average Power 5 Minuten - Durchschnittliche Leistung in W der letzten 5 Minuten</li>
<li>AvP15: Average Power 15 Minuten - Durchschnittliche Leistung in W der letzten 15 Minuten</li>
<li>SpotPDC1: Spot Gleichspannung String 1 </li>
<li>SpotPDC2: Spot Gleichspannung String 2 </li>
<li>TotalTodayP: Erzeuge Leistung (in Wh) des heutigen Tages </li>
<li>AlltimeTotalP: Erzeugte Leistung (in Wh) seit Inbetriebsnahme des Gerätes </li>
<li>Alarm1..3: Alarm Trigger 1-3. Können über die Parameter "alarmN-value" gesetzt werden </li>
</ul>
=end html_DE

@ -277,6 +277,8 @@ FHEM/75_msgConfig.pm loredo http://forum.fhem.de Automatis
FHEM/76_MSGFile.pm gandy http://forum.fhem.de Automatisierung
FHEM/76_MSGMail.pm gandy http://forum.fhem.de Automatisierung
FHEM/77_UWZ.pm CoolTux http://forum.fhem.de Unterstuetzende Dienste/Wettermodule
FHEM/77_SMAEM.pm VolkerKettenbach http://forum.fhem.de Sonstige Systeme
FHEM/77_SMASTP.pm VolkerKettenbach http://forum.fhem.de Sonstige Systeme
FHEM/79_BDKM.pm arnoaugustin http://forum.fhem.de Heizungssteuerung/Raumklima (bitte auch PM)
FHEM/80_M232.pm borisneubert http://forum.fhem.de Sonstige Systeme
FHEM/80_xxLG7000.pm markusbloch http://forum.fhem.de Multimedia