| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833 |
- #################################################################################################################
- # $Id: 76_SMAInverter.pm 15217 2017-10-09 16:15:04Z DS_Starter $
- #################################################################################################################
- #
- #
- # Copyright notice
- #
- # Published according Creative Commons : Attribution-NonCommercial-ShareAlike 3.0 Unported (CC BY-NC-SA 3.0)
- # Details: https://creativecommons.org/licenses/by-nc-sa/3.0/
- #
- # Credits:
- # - based on 77_SMASTP.pm by Volker Kettenbach with following credits:
- # - based on an Idea by SpenZerX and HDO
- # - Waldmensch for various improvements
- # - sbfspot (https://sbfspot.codeplex.com/)
- # - rewritten by Thomas Schoedl (sct14675) with inputs from Volker, waldmensch and DS_Starter
- #
- # Description:
- # This is an FHEM-Module for SMA Inverters.
- # Tested on Sunny Tripower 6000TL-20 and Sunny Island 4.4
- #
- # Requirements:
- # This module requires:
- # - Perl Module: IO::Socket::INET
- # - Perl Module: DateTime
- #
- #
- #################################################################################################################
- # Versions History done by DS_Starter
- #
- # 2.9.2 08.10.2017 adapted to use extended abortArg (Forum:77472)
- # 2.9.1 24.04.2017 fix for issue #24 (Wrong INV_TYPE for STP10000TL-20) and fix for issue #25 (unpack out of range for SB1.5-1VL-40)
- # 2.9.0 23.04.2017 fixed issue #22: wrong logon command for SunnyBoy systems
- # 2.8.3 19.04.2017 enhanced inverter Type-Hash
- # 2.8.2 23.03.2017 changed SMA_logon sub
- # 2.8.1 06.12.2016 SMAInverter version as internal
- # 2.8 05.12.2016 changed commandsections to make sure getting only data from inverters with preset
- # $inv_susyid and $inv_serial
- # 2.7.4 04.12.2016 change loading of IO::Socket::INET, DateTime
- # 2.7.3 04.12.2016 commandref adapted
- # 2.7.2 03.12.2016 use Time::HiRes qw(gettimeofday tv_interval)
- # 2.7.1 02.12.2016 showproctime improved
- # 2.7 02.12.2016 showproctime added
- # 2.6.1 29.11.2016 getstatus_DoParse changed due to inititialized issues
- # 2.6 28.11.2016 bugfix warnings ParseDone redefine at startup, uninitialized value $avg if FHEM was
- # restarted in sleeptime, switched avg_energy to avg_power, commandref updated
- # 2.5.2 27.11.2016 bugfix average calc, bugfix warnings at startup
- # 2.5.1 26.11.2016 calc of averagebuf changed to 5, 10, 15 minutes
- # 2.5 26.11.2016 averagebuf changed, Attr timeout added
- # 2.4 26.11.2016 create ringbuffer for calculating average energy last 5, 10, 15 cycles
- # 2.3 25.11.2016 bugfixing
- # 2.2 24.11.2016 further optimize of non-blocking operation
- # 2.1 24.11.2016 avg_energy_lastcycles added
- # 2.0 24.11.2016 switched module to non-blocking operation
- # 1.8.4 23.11.2016 prepare non-blocking operation
- # 1.8.3 23.11.2016 readings opertime_start, opertime_stop
- # 1.8.2 22.11.2016 eliminate global vars, prepare non-blocking operation
- # 1.8.1 22.11.2016 eliminate global vars, create command array
- # 1.8 21.11.2016 eliminate $r_OK, $r_FAIL, create command-array
- # 1.7 21.11.2016 devtypes completed, minor bugfixes, commandref completed
- # 1.6.1 19.11.2016 bugfix perl warning during fhem start
- # 1.6 09.11.2016 added operation control by sunrise,sunset, Attr offset, suppressSleep added
- # 1.5 08.11.2016 added device classes hash
- # 1.4 07.11.2016 compatibility to SBFSpot improved, bilingual dependend on attr "language" of global-device,
- # added hash of SMA device types
- # 1.3 07.11.2016 Attr SBFSpotComp added to get compatibility mode with SBFSpot
- # 1.2 06.11.2016 function get data added, log output level changed to 4 in sub SMAInverter_Attr,
- # some code changes
- # 1.1 06.11.2016 Attr mode manual, automatic added
- # 1.0 06.11.2016 Attr disable added,
- # $globalName replaced by $name in all expressions (due to module redesign to non-blocking later)
- package main;
- use strict;
- use warnings;
- eval "use IO::Socket::INET;1" or my $MissModulSocket = "IO::Socket::INET";
- eval "use DateTime;1" or my $MissModulDateTime = "DateTime";
- use Time::HiRes qw(gettimeofday tv_interval);
- use Blocking;
- use Time::Local;
- my $SMAInverterVersion = "2.9.2";
- # Inverter Data fields and supported commands flags.
- # $inv_SPOT_ETODAY # Today yield
- # $inv_SPOT_ETOTAL # Total yield
- # $inv_SPOT_PDC1 # DC power input 1
- # $inv_SPOT_PDC2 # DC power input 2
- # $inv_SPOT_PAC1 # Power L1
- # $inv_SPOT_PAC2 # Power L2
- # $inv_SPOT_PAC3 # Power L3
- # $inv_PACMAX1 # Nominal power in Ok Mode
- # $inv_PACMAX2 # Nominal power in Warning Mode
- # $inv_PACMAX3 # Nominal power in Fault Mode
- # $inv_PACMAX1_2 # Maximum active power device (Some inverters like SB3300/SB1200)
- # $inv_SPOT_PACTOT # Total Power
- # $inv_ChargeStatus # Battery Charge status
- # $inv_SPOT_UDC1 # DC voltage input
- # $inv_SPOT_UDC2 # DC voltage input
- # $inv_SPOT_IDC1 # DC current input
- # $inv_SPOT_IDC2 # DC current input
- # $inv_SPOT_UAC1 # Grid voltage phase L1
- # $inv_SPOT_UAC2 # Grid voltage phase L2
- # $inv_SPOT_UAC3 # Grid voltage phase L3
- # $inv_SPOT_IAC1 # Grid current phase L1
- # $inv_SPOT_IAC2 # Grid current phase L2
- # $inv_SPOT_IAC3 # Grid current phase L3
- # $inv_BAT_UDC # Battery Voltage
- # $inv_BAT_IDC # Battery Current
- # $inv_BAT_CYCLES # Battery recharge cycles
- # $inv_BAT_TEMP # Battery temperature
- # $inv_SPOT_FREQ # Grid Frequency
- # $inv_CLASS # Inverter Class
- # $inv_TYPE # Inverter Type
- # $inv_SPOT_OPERTM # Operation Time
- # $inv_SPOT_FEEDTM # Feed-in time
- # $inv_TEMP # Inverter temperature
- # $inv_GRIDRELAY # Grid Relay/Contactor Status
- # $inv_STATUS # Inverter Status
- # Aufbau Wechselrichter Type-Hash
- my %SMAInverter_devtypes = (
- 0000 => "Unknown Inverter Type",
- 9015 => "SB 700",
- 9016 => "SB 700U",
- 9017 => "SB 1100",
- 9018 => "SB 1100U",
- 9019 => "SB 1100LV",
- 9020 => "SB 1700",
- 9021 => "SB 1900TLJ",
- 9022 => "SB 2100TL",
- 9023 => "SB 2500",
- 9024 => "SB 2800",
- 9025 => "SB 2800i",
- 9026 => "SB 3000",
- 9027 => "SB 3000US",
- 9028 => "SB 3300",
- 9029 => "SB 3300U",
- 9030 => "SB 3300TL",
- 9031 => "SB 3300TL HC",
- 9032 => "SB 3800",
- 9033 => "SB 3800U",
- 9034 => "SB 4000US",
- 9035 => "SB 4200TL",
- 9036 => "SB 4200TL HC",
- 9037 => "SB 5000TL",
- 9038 => "SB 5000TLW",
- 9039 => "SB 5000TL HC",
- 9066 => "SB 1200",
- 9067 => "STP 10000TL-10",
- 9068 => "STP 12000TL-10",
- 9069 => "STP 15000TL-10",
- 9070 => "STP 17000TL-10",
- 9084 => "WB 3600TL-20",
- 9085 => "WB 5000TL-20",
- 9086 => "SB 3800US-10",
- 9098 => "STP 5000TL-20",
- 9099 => "STP 6000TL-20",
- 9100 => "STP 7000TL-20",
- 9101 => "STP 8000TL-10",
- 9102 => "STP 9000TL-20",
- 9103 => "STP 8000TL-20",
- 9104 => "SB 3000TL-JP-21",
- 9105 => "SB 3500TL-JP-21",
- 9106 => "SB 4000TL-JP-21",
- 9107 => "SB 4500TL-JP-21",
- 9108 => "SCSMC",
- 9109 => "SB 1600TL-10",
- 9131 => "STP 20000TL-10",
- 9139 => "STP 20000TLHE-10",
- 9140 => "STP 15000TLHE-10",
- 9157 => "Sunny Island 2012",
- 9158 => "Sunny Island 2224",
- 9159 => "Sunny Island 5048",
- 9160 => "SB 3600TL-20",
- 9168 => "SC630HE-11",
- 9169 => "SC500HE-11",
- 9170 => "SC400HE-11",
- 9171 => "WB 3000TL-21",
- 9172 => "WB 3600TL-21",
- 9173 => "WB 4000TL-21",
- 9174 => "WB 5000TL-21",
- 9175 => "SC 250",
- 9176 => "SMA Meteo Station",
- 9177 => "SB 240-10",
- 9171 => "WB 3000TL-21",
- 9172 => "WB 3600TL-21",
- 9173 => "WB 4000TL-21",
- 9174 => "WB 5000TL-21",
- 9179 => "Multigate-10",
- 9180 => "Multigate-US-10",
- 9181 => "STP 20000TLEE-10",
- 9182 => "STP 15000TLEE-10",
- 9183 => "SB 2000TLST-21",
- 9184 => "SB 2500TLST-21",
- 9185 => "SB 3000TLST-21",
- 9186 => "WB 2000TLST-21",
- 9187 => "WB 2500TLST-21",
- 9188 => "WB 3000TLST-21",
- 9189 => "WTP 5000TL-20",
- 9190 => "WTP 6000TL-20",
- 9191 => "WTP 7000TL-20",
- 9192 => "WTP 8000TL-20",
- 9193 => "WTP 9000TL-20",
- 9254 => "Sunny Island 3324",
- 9255 => "Sunny Island 4.0M",
- 9256 => "Sunny Island 4248",
- 9257 => "Sunny Island 4248U",
- 9258 => "Sunny Island 4500",
- 9259 => "Sunny Island 4548U",
- 9260 => "Sunny Island 5.4M",
- 9261 => "Sunny Island 5048U",
- 9262 => "Sunny Island 6048U",
- 9278 => "Sunny Island 3.0M",
- 9279 => "Sunny Island 4.4M",
- 9281 => "STP 10000TL-20",
- 9282 => "STP 11000TL-20",
- 9283 => "STP 12000TL-20",
- 9284 => "STP 20000TL-30",
- 9285 => "STP 25000TL-30",
- 9301 => "SB1.5-1VL-40",
- 9302 => "SB2.5-1VL-40",
- 9303 => "SB2.0-1VL-40",
- 9304 => "SB5.0-1SP-US-40",
- 9305 => "SB6.0-1SP-US-40",
- 9306 => "SB8.0-1SP-US-40",
- 9307 => "Energy Meter",
- );
- # Wechselrichter Class-Hash DE
- my %SMAInverter_classesDE = (
- 8000 => "Alle Geräte",
- 8001 => "Solar-Wechselrichter",
- 8002 => "Wind-Wechselrichter",
- 8007 => "Batterie-Wechselrichter",
- 8033 => "Verbraucher",
- 8064 => "Sensorik allgemein",
- 8065 => "Stromzähler",
- 8128 => "Kommunikationsprodukte",
- );
- # Wechselrichter Class-Hash EN
- my %SMAInverter_classesEN = (
- 8000 => "All Devices",
- 8001 => "Solar Inverters",
- 8002 => "Wind Turbine Inverter",
- 8007 => "Batterie Inverters",
- 8033 => "Consumer",
- 8064 => "Sensor System in General",
- 8065 => "Electricity meter",
- 8128 => "Communication products",
- );
- ###############################################################
- # SMAInverter Initialize
- ###############################################################
- sub SMAInverter_Initialize($) {
- my ($hash) = @_;
- $hash->{DefFn} = "SMAInverter_Define";
- $hash->{UndefFn} = "SMAInverter_Undef";
- $hash->{GetFn} = "SMAInverter_Get";
- $hash->{AttrList} = "interval " .
- "detail-level:0,1,2 " .
- "disable:1,0 " .
- "mode:manual,automatic ".
- "offset ".
- "suppressSleep:1,0 ".
- "SBFSpotComp:1,0 " .
- "showproctime:1,0 ".
- "timeout " .
- "target-susyid " .
- "target-serial " .
- $readingFnAttributes;
- $hash->{AttrFn} = "SMAInverter_Attr";
- }
- ###############################################################
- # SMAInverter Define
- ###############################################################
- sub SMAInverter_Define($$) {
- my ($hash, $def) = @_;
- my @a = split("[ \t][ \t]*", $def);
-
- return "Error: Perl module ".$MissModulSocket." is missing.
- Install it on Debian with: sudo apt-get install libio-socket-multicast-perl" if($MissModulSocket);
- return "Error: Perl module ".$MissModulDateTime." is missing.
- Install it on Debian with: sudo apt-get install libdatetime-perl" if($MissModulDateTime);
- return "Wrong syntax: use define <name> SMAInverter <inv-userpwd> <inv-hostname/inv-ip > " if ((int(@a) < 4) and (int(@a) > 5));
- my $name = $hash->{NAME};
- $hash->{LASTUPDATE} = 0;
- $hash->{INTERVAL} = $hash->{HELPER}{INTERVAL} = AttrVal($name, "interval", 60);
- $hash->{VERSION} = $SMAInverterVersion;
- $hash->{HELPER}{FAULTEDCYCLES} = 0;
- delete($hash->{HELPER}{AVERAGEBUF}) if($hash->{HELPER}{AVERAGEBUF});
-
- # protocol related defaults
- $hash->{HELPER}{MYSUSYID} = 233; # random number, has to be different from any device in local network
- $hash->{HELPER}{MYSERIALNUMBER} = 123321123; # random number, has to be different from any device in local network
- $hash->{HELPER}{DEFAULT_TARGET_SUSYID} = 0xFFFF; # 0xFFFF is any susyid
- $hash->{HELPER}{DEFAULT_TARGET_SERIAL} = 0xFFFFFFFF; # 0xFFFFFFFF is any serialnumber
- $hash->{HELPER}{PKT_ID} = 0x8001; # Packet ID
- $hash->{HELPER}{MAXBYTES} = 300; # constant MAXBYTES scalar 300
- my ($IP,$Host,$Caps);
- my $Pass = $a[2]; # to do: check 1-12 Chars
- # extract IP or Hostname from $a[3]
- 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;
- InternalTimer(gettimeofday()+5, "SMAInverter_GetData", $hash, 0); # Start Hauptroutine
- return undef;
- }
- ###############################################################
- # SMAInverter Undefine
- ###############################################################
- sub SMAInverter_Undef($$) {
- my ($hash, $name) = @_;
- RemoveInternalTimer($hash);
- BlockingKill($hash->{HELPER}{RUNNING_PID});
- return undef;
- }
- ###############################################################
- # SMAInverter Get
- ###############################################################
- sub SMAInverter_Get($$) {
- my ($hash, @a) = @_;
- return "\"get X\" needs at least an argument" if ( @a < 2 );
- my $name = shift @a;
- my $opt = shift @a;
- my $timeout = AttrVal($name, "timeout", 60);
-
- my $getlist = "Unknown argument $opt, choose one of ".
- "data:noArg ";
-
- return "module is disabled" if(IsDisabled($name));
-
- if ($opt eq "data") {
- SMAInverter_GetData($hash);
- } else {
- return "$getlist";
- }
- return undef;
- }
- ###############################################################
- # SMAInverter Attr
- ###############################################################
- sub SMAInverter_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 $do;
- if ($aName eq "mode") {
- if ($cmd eq "set" && $aVal eq "manual") {
- $hash->{INTERVAL} = $aVal;
- } else {
- $hash->{INTERVAL} = $hash->{HELPER}{INTERVAL};
- }
- InternalTimer(time+5, 'SMAInverter_GetData', $hash, 0);
- }
-
- if ($aName eq "disable") {
- if($cmd eq "set") {
- $do = ($aVal) ? 1 : 0;
- }
- $do = 0 if($cmd eq "del");
- my $val = ($do == 1 ? "disabled" : "initialized");
-
- readingsSingleUpdate($hash, "state", $val, 1);
-
- if ($do == 0) {
- my $mode = AttrVal($name, "mode", "automatic");
- RemoveInternalTimer($hash);
- InternalTimer(time+5, 'SMAInverter_GetData', $hash, 0);
- } else {
- RemoveInternalTimer($hash);
- }
- }
- if ($aName eq "detail-level") {
- delete $defs{$name}{READINGS};
- }
-
- if ($aName eq "SBFSpotComp") {
- delete $defs{$name}{READINGS};
- }
- if ($aName eq "interval") {
- if ($cmd eq "set") {
- $hash->{HELPER}{INTERVAL} = $aVal;
- $hash->{INTERVAL} = $aVal if(AttrVal($name, "mode", "") ne "manual");
- delete($hash->{HELPER}{AVERAGEBUF}) if($hash->{HELPER}{AVERAGEBUF});
- Log3 $name, 3, "$name - Set $aName to $aVal";
- } else {
- $hash->{INTERVAL} = $hash->{HELPER}{INTERVAL} = 60;
- }
- }
-
- if ($cmd eq "set" && $aName eq "offset") {
- if($aVal !~ /^\d+$/ || $aVal < 0 || $aVal > 7200) { return "The Value of $aName is not valid. Use value between 0 ... 7200 !";}
- }
- if ($cmd eq "set" && $aName eq "timeout") {
- unless ($aVal =~ /^[0-9]+$/) { return " The Value for $aName is not valid. Use only figures 1-9 !";}
- }
- return;
- }
- ###############################################################
- # Hauptschleife Datenabruf
- ###############################################################
- sub SMAInverter_GetData($) {
- my ($hash) = @_;
- my $name = $hash->{NAME};
- my $interval = AttrVal($name, "interval", 60);
- my $timeout = AttrVal($name, "timeout", 60);
-
- RemoveInternalTimer($hash, "SMAInverter_GetData");
-
- if ($init_done != 1) {
- InternalTimer(gettimeofday()+5, "SMAInverter_GetData", $hash, 0);
- return;
- }
-
- return if(IsDisabled($name));
-
- if (exists($hash->{HELPER}{RUNNING_PID})) {
- Log3 ($name, 3, "SMAInverter $name - WARNING - old process $hash->{HELPER}{RUNNING_PID}{pid} will be killed now to start a new BlockingCall");
- BlockingKill($hash->{HELPER}{RUNNING_PID});
- }
-
- Log3 ($name, 4, "$name - ###############################################################");
- Log3 ($name, 4, "$name - ########## Begin of new SMAInverter get data cycle ##########");
- Log3 ($name, 4, "$name - ###############################################################");
- Log3 ($name, 4, "$name - timeout cycles since module start: $hash->{HELPER}{FAULTEDCYCLES}");
-
- # decide of operation
- if(AttrVal($name,"mode","automatic") eq "automatic") {
- # automatic operation mode
- InternalTimer(gettimeofday()+$interval, "SMAInverter_GetData", $hash, 0);
- }
- $hash->{HELPER}{RUNNING_PID} = BlockingCall("getstatus_DoParse", "$name", "getstatus_ParseDone", $timeout, "getstatus_ParseAborted", $hash);
- $hash->{HELPER}{RUNNING_PID}{loglevel} = 4;
- return;
- }
- ###############################################################
- # non-blocking Inverter Datenabruf
- ###############################################################
- sub getstatus_DoParse($) {
- my ($name) = @_;
- my $hash = $defs{$name};
- my $interval = AttrVal($name, "interval", 60);
- my $sc = AttrVal($name, "SBFSpotComp", 0);
- my ($sup_EnergyProduction,
- $sup_SpotDCPower,
- $sup_SpotACPower,
- $sup_MaxACPower,
- $sup_MaxACPower2,
- $sup_SpotACTotalPower,
- $sup_ChargeStatus,
- $sup_SpotDCVoltage,
- $sup_SpotACVoltage,
- $sup_BatteryInfo,
- $sup_SpotGridFrequency,
- $sup_TypeLabel,
- $sup_OperationTime,
- $sup_InverterTemperature,
- $sup_GridRelayStatus,
- $sup_DeviceStatus);
- my ($inv_TYPE, $inv_CLASS,
- $inv_SPOT_ETODAY, $inv_SPOT_ETOTAL,
- $inv_susyid,
- $inv_serial,
- $inv_SPOT_PDC1, $inv_SPOT_PDC2,
- $inv_SPOT_PAC1, $inv_SPOT_PAC2, $inv_SPOT_PAC3, $inv_SPOT_PACTOT,
- $inv_PACMAX1, $inv_PACMAX2, $inv_PACMAX3, $inv_PACMAX1_2,
- $inv_ChargeStatus,
- $inv_SPOT_UDC1, $inv_SPOT_UDC2,
- $inv_SPOT_IDC1, $inv_SPOT_IDC2,
- $inv_SPOT_UAC1, $inv_SPOT_UAC2, $inv_SPOT_UAC3,
- $inv_SPOT_IAC1, $inv_SPOT_IAC2, $inv_SPOT_IAC3,
- $inv_BAT_UDC, $inv_BAT_IDC,
- $inv_BAT_CYCLES,
- $inv_BAT_TEMP,
- $inv_SPOT_FREQ, $inv_SPOT_OPERTM, $inv_SPOT_FEEDTM, $inv_TEMP, $inv_GRIDRELAY, $inv_STATUS,);
- my @row_array;
- my @array;
- my $avg = 0;
- my ($ist,$bst,$irt,$brt,$rt);
-
- # Background-Startzeit
- $bst = [gettimeofday];
-
- Log3 ($name, 4, "$name -> Start BlockingCall getstatus_DoParse");
-
- # set dependency from surise/sunset used for inverter operation time
- my $offset = AttrVal($name,"offset",0);
- my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime();
- my ($sunrise_h,$sunrise_m,$sunrise_s) = split(":",sunrise_abs('-'.$offset));
- my ($sunset_h,$sunset_m,$sunset_s) = split(":",sunset_abs('+'.$offset));
- my $oper_start = DateTime->new(year=>$year+1900,month=>$mon+1,day=>$mday,hour=>$sunrise_h,minute=>$sunrise_m,second=>$sunrise_s,time_zone=>'local');
- my $oper_stop = DateTime->new(year=>$year+1900,month=>$mon+1,day=>$mday,hour=>$sunset_h,minute=>$sunset_m,second=>$sunset_s,time_zone=>'local');
- my $dt_now = DateTime->now(time_zone=>'local');
- Log3 $name, 4, "$name - current time: ".$dt_now->dmy('.')." ".$dt_now->hms;
- Log3 $name, 4, "$name - operation time begin: ".$oper_start->dmy('.')." ".$oper_start->hms;
- Log3 $name, 4, "$name - operation time end: ".$oper_stop->dmy('.')." ".$oper_stop->hms;
- my $opertime_start = $oper_start->dmy('.')." ".$oper_start->hms;
- my $opertime_stop = $oper_stop->dmy('.')." ".$oper_stop->hms;
-
- if (($oper_start <= $dt_now && $dt_now <= $oper_stop) || AttrVal($name,"suppressSleep",0)) {
- # normal operation or suppressed sleepmode
-
- # Abfrage Inverter Startzeit
- $ist = [gettimeofday];
-
- # Get the current attributes
- my $detail_level = AttrVal($name, "detail-level", 0);
-
- # Aufbau Command-Array
- my @commands = ("sup_TypeLabel", # Check TypeLabel
- "sup_EnergyProduction", # Check EnergyProduction
- "sup_SpotDCPower", # Check SpotDCPower
- "sup_SpotACPower", # Check SpotACPower
- "sup_SpotACTotalPower", # Check SpotACTotalPower
- "sup_ChargeStatus" # Check BatteryChargeStatus
- );
-
- if($detail_level > 0) {
- # Detail Level 1 or 2 >> get voltage and current levels
- push(@commands, "sup_SpotDCVoltage"); # Check SpotDCVoltage
- push(@commands, "sup_SpotACVoltage"); # Check SpotACVoltage
- push(@commands, "sup_BatteryInfo"); # Check BatteryInfo
- }
-
- if($detail_level > 1) {
- # Detail Level 2 >> get all data
- push(@commands, "sup_SpotGridFrequency"); # Check SpotGridFrequency
- push(@commands, "sup_OperationTime"); # Check OperationTime
- push(@commands, "sup_InverterTemperature"); # Check InverterTemperature
- push(@commands, "sup_MaxACPower"); # Check MaxACPower
- push(@commands, "sup_MaxACPower2"); # Check MaxACPower2
- push(@commands, "sup_GridRelayStatus"); # Check GridRelayStatus
- push(@commands, "sup_DeviceStatus"); # Check DeviceStatus
- }
- if(SMA_logon($hash->{HOST}, $hash->{PASS}, $hash)) {
- Log3 $name, 5, "$name - Logged in now";
-
- foreach my $i(@commands) {
- if ($i eq "sup_TypeLabel") {
- ($sup_TypeLabel,$inv_TYPE,$inv_CLASS,$inv_susyid,$inv_serial) = SMA_command($hash, $hash->{HOST}, 0x58000200, 0x00821E00, 0x008220FF);
- }
- elsif ($i eq "sup_EnergyProduction") {
- ($sup_EnergyProduction,$inv_SPOT_ETODAY,$inv_SPOT_ETOTAL,$inv_susyid,$inv_serial) = SMA_command($hash, $hash->{HOST}, 0x54000200, 0x00260100, 0x002622FF);
- }
- elsif ($i eq "sup_SpotDCPower") {
- ($sup_SpotDCPower,$inv_SPOT_PDC1,$inv_SPOT_PDC2,$inv_susyid,$inv_serial) = SMA_command($hash, $hash->{HOST}, 0x53800200, 0x00251E00, 0x00251EFF);
- }
- elsif ($i eq "sup_SpotACPower") {
- ($sup_SpotACPower,$inv_SPOT_PAC1,$inv_SPOT_PAC2,$inv_SPOT_PAC3,$inv_susyid,$inv_serial) = SMA_command($hash, $hash->{HOST}, 0x51000200, 0x00464000, 0x004642FF);
- }
- elsif ($i eq "sup_SpotACTotalPower") {
- ($sup_SpotACTotalPower,$inv_SPOT_PACTOT,$inv_susyid,$inv_serial) = SMA_command($hash, $hash->{HOST}, 0x51000200, 0x00263F00, 0x00263FFF);
- }
- elsif ($i eq "sup_ChargeStatus") {
- ($sup_ChargeStatus,$inv_ChargeStatus,$inv_susyid,$inv_serial) = SMA_command($hash, $hash->{HOST}, 0x51000200, 0x00295A00, 0x00295AFF);
- }
- elsif ($i eq "sup_SpotDCVoltage") {
- ($sup_SpotDCVoltage,$inv_SPOT_UDC1,$inv_SPOT_UDC2,$inv_SPOT_IDC1,$inv_SPOT_IDC2,$inv_susyid,$inv_serial) = SMA_command($hash, $hash->{HOST}, 0x53800200, 0x00451F00, 0x004521FF);
- }
- elsif ($i eq "sup_SpotACVoltage") {
- ($sup_SpotACVoltage,$inv_SPOT_UAC1,$inv_SPOT_UAC2,$inv_SPOT_UAC3,$inv_SPOT_IAC1,$inv_SPOT_IAC2,$inv_SPOT_IAC3,$inv_susyid,$inv_serial) = SMA_command($hash, $hash->{HOST}, 0x51000200, 0x00464800, 0x004655FF);
- }
- elsif ($i eq "sup_BatteryInfo") {
- ($sup_BatteryInfo,$inv_BAT_CYCLES,$inv_BAT_TEMP,$inv_BAT_UDC,$inv_BAT_IDC,$inv_susyid,$inv_serial) = SMA_command($hash, $hash->{HOST}, 0x51000200, 0x00491E00, 0x00495DFF);
- }
- elsif ($i eq "sup_SpotGridFrequency") {
- ($sup_SpotGridFrequency,$inv_SPOT_FREQ,$inv_susyid,$inv_serial) = SMA_command($hash, $hash->{HOST}, 0x51000200, 0x00465700, 0x004657FF);
- }
- elsif ($i eq "sup_OperationTime") {
- ($sup_OperationTime,$inv_SPOT_OPERTM,$inv_SPOT_FEEDTM,$inv_susyid,$inv_serial) = SMA_command($hash, $hash->{HOST}, 0x54000200, 0x00462E00, 0x00462FFF);
- }
- elsif ($i eq "sup_InverterTemperature") {
- ($sup_InverterTemperature,$inv_TEMP,$inv_susyid,$inv_serial) = SMA_command($hash, $hash->{HOST}, 0x52000200, 0x00237700, 0x002377FF);
- }
- elsif ($i eq "sup_MaxACPower") {
- ($sup_MaxACPower,$inv_PACMAX1,$inv_PACMAX2,$inv_PACMAX3,$inv_susyid,$inv_serial) = SMA_command($hash, $hash->{HOST}, 0x51000200, 0x00411E00, 0x004120FF);
- }
- elsif ($i eq "sup_MaxACPower2") {
- ($sup_MaxACPower2,$inv_PACMAX1_2,$inv_susyid,$inv_serial) = SMA_command($hash, $hash->{HOST}, 0x51000200, 0x00832A00, 0x00832AFF);
- }
- elsif ($i eq "sup_GridRelayStatus") {
- ($sup_GridRelayStatus,$inv_GRIDRELAY,$inv_susyid,$inv_serial) = SMA_command($hash, $hash->{HOST}, 0x51800200, 0x00416400, 0x004164FF);
- }
- elsif ($i eq "sup_DeviceStatus") {
- ($sup_DeviceStatus,$inv_STATUS,$inv_susyid,$inv_serial) = SMA_command($hash, $hash->{HOST}, 0x51800200, 0x00214800, 0x002148FF);
- }
- }
-
- # nothing more to do, just log out
- SMA_logout($hash,$hash->{HOST});
-
- # Inverter Laufzeit ermitteln
- $irt = tv_interval($ist);
-
- # Aufbau Ergebnis-Array
- push(@row_array, "modulstate normal"."\n");
- push(@row_array, "opertime_start ".$opertime_start."\n");
- push(@row_array, "opertime_stop ".$opertime_stop."\n");
-
- # Durchschnittswerteberechnung Energieerzeugung der letzten 5, 10, 15 Messungen
-
- my ($sum05, $sum10, $sum15);
- my $cnt05 = int(300/$interval); # Anzahl der Zyklen innerhalb 5 Minuten
- my $cnt10 = int(600/$interval); # Anzahl der Zyklen innerhalb 10 Minuten
- my $cnt15 = int(900/$interval); # Anzahl der Zyklen innerhalb 15 Minuten = Summe aller Messzyklen
- my $cntsum = $cnt15+1; # Sicherheitszuschlag Summe Anzahl aller Zyklen
- my @averagebuf;
- if ($sup_TypeLabel && $sup_EnergyProduction && $inv_CLASS eq 8001) {
- # only for this block because of warnings if values not set at restart
- no warnings 'uninitialized';
- if (!$hash->{HELPER}{AVERAGEBUF}) {
- for my $count (0..$cntsum) {
- # fill with new values
- $inv_SPOT_PACTOT = $inv_SPOT_PACTOT?$inv_SPOT_PACTOT:0;
- push(@averagebuf, $inv_SPOT_PACTOT);
- }
- } else {
- @averagebuf = split(/,/, $hash->{HELPER}{AVERAGEBUF})
- }
-
- # rechtes Element aus average buffer löschen
- pop(@averagebuf);
- # und links mit neuem Wert füllen
- unshift(@averagebuf, $inv_SPOT_PACTOT);
- $avg = join(',', @averagebuf);
- # calculate average energy and write to array for generate readings
- my $k = 1;
- my $avgsum = $averagebuf[0];
- while ($k < $cntsum) {
- $avgsum = $avgsum + $averagebuf[$k] if($averagebuf[$k]);
- if ($k == $cnt05) {
- $sum05 = $avgsum;
- Log3 $name, 5, "$name - CNT05: $cnt05 SUM05: $sum05";
- }
- if ($k == $cnt10) {
- $sum10 = $avgsum;
- Log3 $name, 5, "$name - CNT10: $cnt10 SUM10: $sum10";
- }
- if ($k == $cnt15) {
- $sum15 = $avgsum;
- Log3 $name, 5, "$name - CNT15: $cnt15 SUM15: $sum15";
- }
- $k++;
- }
-
- my $AvP05 = int( $sum05 / ($cnt05+1) );
- my $AvP10 = int( $sum10 / ($cnt10+1) );
- my $AvP15 = int( $sum15 / ($cnt15+1) );
- Log3 $name, 5, "$name - Content of Averagebuffer:";
- Log3 $name, 5, "$name - $avg";
- Log3 $name, 5, "$name - avg_power_lastminutes_05 = $AvP05, avg_power_lastminutes_10 = $AvP10, avg_power_lastminutes_15 = $AvP15";
-
- push(@row_array, "avg_power_lastminutes_05 ".$AvP05."\n"); # Average Energy (last) 5 minutes
- push(@row_array, "avg_power_lastminutes_10 ".$AvP10."\n"); # Average Energy (last) 10 minutes
- push(@row_array, "avg_power_lastminutes_15 ".$AvP15."\n"); # Average Energy (last) 15 minutes
-
- use warnings;
- }
-
- if ($sc) { # SBFSpot Kompatibilitätsmodus
- if($sup_EnergyProduction) {
- push(@row_array, "etotal ".($inv_SPOT_ETOTAL/1000)."\n");
- push(@row_array, "etoday ".($inv_SPOT_ETODAY/1000)."\n");
- }
- if($sup_SpotDCPower) {
- push(@row_array, "string_1_pdc ".sprintf("%.3f",$inv_SPOT_PDC1/1000)."\n");
- push(@row_array, "string_2_pdc ".sprintf("%.3f",$inv_SPOT_PDC2/1000)."\n");
- }
- if($sup_SpotACPower) {
- push(@row_array, "phase_1_pac ".sprintf("%.3f",$inv_SPOT_PAC1/1000)."\n");
- push(@row_array, "phase_2_pac ".sprintf("%.3f",$inv_SPOT_PAC2/1000)."\n");
- push(@row_array, "phase_3_pac ".sprintf("%.3f",$inv_SPOT_PAC3/1000)."\n");
- }
- if($sup_SpotACTotalPower) {
- push(@row_array, "total_pac ".sprintf("%.3f",$inv_SPOT_PACTOT/1000)."\n");
- push(@row_array, "state ".sprintf("%.3f",$inv_SPOT_PACTOT/1000)."\n");
- }
- if($sup_ChargeStatus) {
- push(@row_array, "chargestatus ".$inv_ChargeStatus."\n");
- }
- if($inv_CLASS && $inv_CLASS eq 8007) {
- if($inv_SPOT_PACTOT < 0) {
- push(@row_array, "power_out "."0"."\n");
- push(@row_array, "power_in ".(-1 * $inv_SPOT_PACTOT)."\n");
- } else {
- push(@row_array, "power_out ".$inv_SPOT_PACTOT."\n");
- push(@row_array, "power_in "."0"."\n");
- }
- }
-
- if($detail_level > 0) {
- # For Detail Level 1
- if($sup_SpotDCVoltage) {
- push(@row_array, "string_1_udc ".sprintf("%.2f",$inv_SPOT_UDC1)."\n");
- push(@row_array, "string_2_udc ".sprintf("%.2f",$inv_SPOT_UDC2)."\n");
- push(@row_array, "string_1_idc ".sprintf("%.3f",$inv_SPOT_IDC1)."\n");
- push(@row_array, "string_2_idc ".sprintf("%.3f",$inv_SPOT_IDC2)."\n");
- }
- if($sup_SpotACVoltage) {
- push(@row_array, "phase_1_uac ".sprintf("%.2f",$inv_SPOT_UAC1)."\n");
- push(@row_array, "phase_2_uac ".sprintf("%.2f",$inv_SPOT_UAC2)."\n");
- push(@row_array, "phase_3_uac ".sprintf("%.2f",$inv_SPOT_UAC3)."\n");
- push(@row_array, "phase_1_iac ".sprintf("%.3f",$inv_SPOT_IAC1)."\n");
- push(@row_array, "phase_2_iac ".sprintf("%.3f",$inv_SPOT_IAC2)."\n");
- push(@row_array, "phase_3_iac ".sprintf("%.3f",$inv_SPOT_IAC3)."\n");
- }
- if($sup_BatteryInfo) {
- push(@row_array, "bat_udc ".$inv_BAT_UDC."\n");
- push(@row_array, "bat_idc ".$inv_BAT_IDC."\n");
- }
- }
-
- if($detail_level > 1) {
- # For Detail Level 2
- if($sup_BatteryInfo) {
- push(@row_array,"bat_cycles ".$inv_BAT_CYCLES."\n");
- push(@row_array, "bat_temp ".$inv_BAT_TEMP."\n");
- }
- if($sup_SpotGridFrequency) {
- push(@row_array, "grid_freq. ".sprintf("%.2f",$inv_SPOT_FREQ)."\n");
- }
- if($sup_TypeLabel) {
- push(@row_array, "device_type ".devtype($inv_TYPE)."\n");
- push(@row_array, "device_class ".classtype($inv_CLASS)."\n");
- push(@row_array, "susyid ".$inv_susyid." - SN: ".$inv_serial."\n") if($inv_susyid && $inv_serial);
- push(@row_array, "device_name "."SN: ".$inv_serial."\n") if($inv_serial);
- push(@row_array, "serial_number ".$inv_serial."\n") if($inv_serial);
- }
- if($sup_MaxACPower) {
- push(@row_array, "pac_max_phase_1 ".$inv_PACMAX1."\n");
- push(@row_array, "pac_max_phase_2 ".$inv_PACMAX2."\n");
- push(@row_array, "pac_max_phase_3 ".$inv_PACMAX3."\n");
- }
- if($sup_MaxACPower2) {
- push(@row_array, "pac_max_phase_1_2 ".$inv_PACMAX1_2."\n");
- }
- if($sup_InverterTemperature) {
- push(@row_array, "device_temperature ".sprintf("%.1f",$inv_TEMP)."\n");
- }
- if($sup_OperationTime) {
- push(@row_array, "feed-in_time ".$inv_SPOT_FEEDTM."\n");
- push(@row_array, "operation_time ".$inv_SPOT_OPERTM."\n");
- }
- if($sup_GridRelayStatus) {
- push(@row_array, "gridrelay_status ".StatusText($inv_GRIDRELAY)."\n");
- }
- if($sup_DeviceStatus) {
- push(@row_array, "device_status ".StatusText($inv_STATUS)."\n");
- }
- }
- } else { # kein SBFSpot Compatibility Mode
- if($sup_EnergyProduction) {
- push(@row_array, "SPOT_ETOTAL ".$inv_SPOT_ETOTAL."\n");
- push(@row_array, "SPOT_ETODAY ".$inv_SPOT_ETODAY."\n");
- }
- if($sup_SpotDCPower) {
- push(@row_array, "SPOT_PDC1 ".$inv_SPOT_PDC1."\n");
- push(@row_array, "SPOT_PDC2 ".$inv_SPOT_PDC2."\n");
- }
- if($sup_SpotACPower) {
- push(@row_array, "SPOT_PAC1 ".$inv_SPOT_PAC1."\n");
- push(@row_array, "SPOT_PAC2 ".$inv_SPOT_PAC2."\n");
- push(@row_array, "SPOT_PAC3 ".$inv_SPOT_PAC3."\n");
- }
- if($sup_SpotACTotalPower) {
- push(@row_array, "SPOT_PACTOT ".$inv_SPOT_PACTOT."\n");
- push(@row_array, "state ".$inv_SPOT_PACTOT."\n");
- }
- if($sup_ChargeStatus) {
- push(@row_array, "ChargeStatus ".$inv_ChargeStatus."\n");
- }
- if($inv_CLASS && $inv_CLASS eq 8007) {
- if($inv_SPOT_PACTOT < 0) {
- push(@row_array, "POWER_OUT "."0"."\n");
- push(@row_array, "POWER_IN ".(-1 * $inv_SPOT_PACTOT)."\n");
- } else {
- push(@row_array, "POWER_OUT ".$inv_SPOT_PACTOT."\n");
- push(@row_array, "POWER_IN "."0"."\n");
- }
- }
- if($detail_level > 0) {
- # For Detail Level 1
- if($sup_SpotDCVoltage) {
- push(@row_array, "SPOT_UDC1 ".$inv_SPOT_UDC1."\n");
- push(@row_array, "SPOT_UDC2 ".$inv_SPOT_UDC2."\n");
- push(@row_array, "SPOT_IDC1 ".$inv_SPOT_IDC1."\n");
- push(@row_array, "SPOT_IDC2 ".$inv_SPOT_IDC2."\n");
- }
- if($sup_SpotACVoltage) {
- push(@row_array, "SPOT_UAC1 ".$inv_SPOT_UAC1."\n");
- push(@row_array, "SPOT_UAC2 ".$inv_SPOT_UAC2."\n");
- push(@row_array, "SPOT_UAC3 ".$inv_SPOT_UAC3."\n");
- push(@row_array, "SPOT_IAC1 ".$inv_SPOT_IAC1."\n");
- push(@row_array, "SPOT_IAC2 ".$inv_SPOT_IAC2."\n");
- push(@row_array, "SPOT_IAC3 ".$inv_SPOT_IAC3."\n");
- }
- if($sup_BatteryInfo) {
- push(@row_array, "BAT_UDC ".$inv_BAT_UDC."\n");
- push(@row_array, "BAT_IDC ".$inv_BAT_IDC."\n");
- }
- }
-
- if($detail_level > 1) {
- # For Detail Level 2
- if($sup_BatteryInfo) {
- push(@row_array, "BAT_CYCLES ".$inv_BAT_CYCLES."\n");
- push(@row_array, "BAT_TEMP ".$inv_BAT_TEMP."\n");
- }
- if($sup_SpotGridFrequency) {
- push(@row_array, "SPOT_FREQ ".$inv_SPOT_FREQ."\n");
- }
- if($sup_TypeLabel) {
- push(@row_array, "INV_TYPE ".devtype($inv_TYPE)."\n");
- push(@row_array, "INV_CLASS ".classtype($inv_CLASS)."\n");
- push(@row_array, "SUSyID ".$inv_susyid."\n") if($inv_susyid);
- push(@row_array, "Serialnumber ".$inv_serial."\n") if($inv_serial);
- }
- if($sup_MaxACPower) {
- push(@row_array, "INV_PACMAX1 ".$inv_PACMAX1."\n");
- push(@row_array, "INV_PACMAX2 ".$inv_PACMAX2."\n");
- push(@row_array, "INV_PACMAX3 ".$inv_PACMAX3."\n");
- }
- if($sup_MaxACPower2) {
- push(@row_array, "INV_PACMAX1_2 ".$inv_PACMAX1_2."\n");
- }
- if($sup_InverterTemperature) {
- push(@row_array, "INV_TEMP ".$inv_TEMP."\n");
- }
- if($sup_OperationTime) {
- push(@row_array, "SPOT_FEEDTM ".$inv_SPOT_FEEDTM."\n");
- push(@row_array, "SPOT_OPERTM ".$inv_SPOT_OPERTM."\n");
- }
- if($sup_GridRelayStatus) {
- push(@row_array, "INV_GRIDRELAY ".StatusText($inv_GRIDRELAY)."\n");
- }
- if($sup_DeviceStatus) {
- push(@row_array, "INV_STATUS ".StatusText($inv_STATUS)."\n");
- }
- }
- }
- } else {
- # Login failed/not possible
- push(@row_array, "state Login failed"."\n");
- push(@row_array, "modulstate login failed"."\n");
- }
- } else {
- # sleepmode at current time and not suppressed
- push(@row_array, "modulstate sleep"."\n");
- push(@row_array, "opertime_start ".$opertime_start."\n");
- push(@row_array, "opertime_stop ".$opertime_stop."\n");
- push(@row_array, "state done"."\n");
- }
-
- Log3 ($name, 5, "$name -> row_array before encoding:");
- foreach my $row (@row_array) {
- chomp $row;
- Log3 ($name, 5, "$name -> $row");
- }
-
- # encoding result
- my $rowlist = join('|', @row_array);
- $rowlist = encode_base64($rowlist,"");
-
- # Background-Laufzeit ermitteln
- $brt = tv_interval($bst);
- $rt = ($irt?$irt:'').",".$brt;
-
- Log3 ($name, 4, "$name -> BlockingCall getstatus_DoParse finished");
-
- return "$name|$rowlist|$avg|$rt";
- }
-
- ###############################################################
- # Auswertung non-blocking Inverter Datenabruf
- ###############################################################
- sub getstatus_ParseDone ($) {
- my ($string) = @_;
- my @a = split("\\|",$string);
- my $name = $a[0];
- my $hash = $defs{$name};
- my $rowlist = decode_base64($a[1]);
- $hash->{HELPER}{AVERAGEBUF} = $a[2] if($a[2]);
- my $rt = $a[3];
- my ($irt,$brt) = split(",", $rt);
-
- Log3 ($name, 4, "$name -> Start BlockingCall getstatus_ParseDone");
-
- # proctime Readings löschen
- if(!AttrVal($name, "showproctime", undef)) {
- delete($defs{$name}{READINGS}{inverter_processing_time});
- delete($defs{$name}{READINGS}{background_processing_time});
- } else {
- delete($defs{$name}{READINGS}{inverter_processing_time}) if(!$irt);
- }
-
- # Get current time
- my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime();
- $hash->{LASTUPDATE} = sprintf "%02d.%02d.%04d / %02d:%02d:%02d" , $mday , $mon+=1 ,$year+=1900 , $hour , $min , $sec ;
- my @row_array = split("\\|", $rowlist);
-
- Log3 ($name, 5, "$name -> row_array after decoding:");
- foreach my $row (@row_array) {
- chomp $row;
- Log3 ($name, 5, "$name -> $row");
- }
-
- readingsBeginUpdate($hash);
- foreach my $row (@row_array) {
- chomp $row;
- my @a = split(" ", $row, 2);
- readingsBulkUpdate($hash, $a[0], $a[1]);
- }
- readingsBulkUpdate($hash, "background_processing_time", sprintf("%.4f",$brt)) if(AttrVal($name, "showproctime", undef));
- readingsBulkUpdate($hash, "inverter_processing_time", sprintf("%.4f",$irt)) if(AttrVal($name, "showproctime", undef) && $irt);
- readingsEndUpdate($hash, 1);
-
- delete($hash->{HELPER}{RUNNING_PID});
- Log3 ($name, 4, "$name -> BlockingCall getstatus_ParseDone finished");
-
- return;
- }
- ###############################################################
- # Abbruchroutine Timeout Inverter Abfrage
- ###############################################################
- sub getstatus_ParseAborted(@) {
- my ($hash,$cause) = @_;
- my $name = $hash->{NAME};
- my $discycles = $hash->{HELPER}{FAULTEDCYCLES};
- $cause = $cause?$cause:"Timeout: process terminated";
-
- # count of timeouts since module start
- $discycles++;
- $hash->{HELPER}{FAULTEDCYCLES} = $discycles;
-
- Log3 ($name, 1, "SMAInverter $name -> BlockingCall $hash->{HELPER}{RUNNING_PID}{fn} $cause");
- readingsSingleUpdate($hash,"state",$cause, 1);
-
- delete($hash->{HELPER}{RUNNING_PID});
- return;
- }
- ##########################################################################
- # SMA Command Execution
- ##########################################################################
- sub SMA_command($$$$$) {
- # Parameters: $hash - host - command - first - last
- my ($hash,$host,$command,$first,$last) = @_;
- my $name = $hash->{NAME};
- my $cmdheader = "534D4100000402A00000000100";
- my $pktlength = "26"; # length = 38 for data commands
- my $esignature = "0010606509A0";
- my ($inv_TYPE, $inv_CLASS,
- $inv_SPOT_ETODAY, $inv_SPOT_ETOTAL,
- $inv_susyid,
- $inv_serial,
- $inv_SPOT_PDC1, $inv_SPOT_PDC2,
- $inv_SPOT_PAC1, $inv_SPOT_PAC2, $inv_SPOT_PAC3, $inv_SPOT_PACTOT,
- $inv_PACMAX1, $inv_PACMAX2, $inv_PACMAX3, $inv_PACMAX1_2,
- $inv_ChargeStatus,
- $inv_SPOT_UDC1, $inv_SPOT_UDC2,
- $inv_SPOT_IDC1, $inv_SPOT_IDC2,
- $inv_SPOT_UAC1, $inv_SPOT_UAC2, $inv_SPOT_UAC3,
- $inv_SPOT_IAC1, $inv_SPOT_IAC2, $inv_SPOT_IAC3,
- $inv_BAT_UDC, $inv_BAT_IDC,
- $inv_BAT_CYCLES,
- $inv_BAT_TEMP,
- $inv_SPOT_FREQ, $inv_SPOT_OPERTM, $inv_SPOT_FEEDTM, $inv_TEMP, $inv_GRIDRELAY, $inv_STATUS);
- my $mysusyid = $hash->{HELPER}{MYSUSYID};
- my $myserialnumber = $hash->{HELPER}{MYSERIALNUMBER};
- my ($cmd, $myID, $target_ID, $spkt_ID, $cmd_ID);
- my ($socket,$data,$size,$data_ID);
- my ($i, $temp); # Variables for loops and calculation
-
- # Seriennummer und SuSyID des Ziel-WR setzen
- my $default_target_susyid = $hash->{HELPER}{DEFAULT_TARGET_SUSYID};
- my $default_target_serial = $hash->{HELPER}{DEFAULT_TARGET_SERIAL};
- my $target_susyid = AttrVal($name, "target-susyid", $default_target_susyid);
- my $target_serial = AttrVal($name, "target-serial", $default_target_serial);
- # Define own ID and target ID and packet ID
- $myID = ByteOrderShort(substr(sprintf("%04X",$mysusyid),0,4)) . ByteOrderLong(sprintf("%08X",$myserialnumber));
- $target_ID = ByteOrderShort(substr(sprintf("%04X",$target_susyid),0,4)) . ByteOrderLong(sprintf("%08X",$target_serial));
-
- # Increasing Packet ID
- $hash->{HELPER}{PKT_ID} = $hash->{HELPER}{PKT_ID} + 1;
- $spkt_ID = ByteOrderShort(sprintf("%04X",$hash->{HELPER}{PKT_ID}));
- $cmd_ID = ByteOrderLong(sprintf("%08X",$command)) . ByteOrderLong(sprintf("%08X",$first)) . ByteOrderLong(sprintf("%08X",$last));
- #build final command to send
- $cmd = $cmdheader . $pktlength . $esignature . $target_ID . "0000" . $myID . "0000" . "00000000" . $spkt_ID . $cmd_ID . "00000000";
- # flush after every write
- $| = 1;
-
- # Create Socket and check if successful
- $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 0;
- };
- # Send Data
- $data = pack("H*",$cmd);
- $socket->send($data);
- Log3 $name, 3, "$name - Send request $cmd_ID to $host on port 9522";
- Log3 $name, 5, "$name - send: $cmd";
- # Receive Data and do a first check regarding length
- # receive data
- $socket->recv($data, $hash->{HELPER}{MAXBYTES});
- $size = length($data);
- # check if something was received
- if (defined $size) {
- my $received = unpack("H*", $data);
- Log3 $name, 5, "$name - Received: $received";
- }
-
- # Nothing received -> exit
- if (not defined $size) {
- Log3 $name, 1, "$name - Nothing received...";
- return 0;
- } else {
- # We have received something!
- if ($size > 58) {
- # Check all parameters of answer
- my $r_susyid = unpack("v*", substr $data, 20, 2);
- my $r_serial = unpack("V*", substr $data, 22, 4);
- my $r_pkt_ID = unpack("v*", substr $data, 40, 2);
- my $r_error = unpack("V*", substr $data, 36, 4);
- if (($r_susyid ne $mysusyid) || ($r_serial ne $myserialnumber) || ($r_pkt_ID ne $hash->{HELPER}{PKT_ID}) || ($r_error ne 0)) {
- # Response does not match the parameters we have sent, maybe different target
- Log3 $name, 3, "$name - Inverter answer does not match our parameters.";
- Log3 $name, 5, "$name - Request/Response: SusyID $mysusyid/$r_susyid, Serial $myserialnumber/$r_serial, Packet ID $hash->{HELPER}{PKT_ID}/$r_pkt_ID, Error $r_error";
- $socket->close();
- return 0;
- }
- } else {
- Log3 $name, 3, "$name - Format of inverter response does not fit.";
- $socket->close();
- return 0;
- }
- }
-
- # All seems ok, data received
- $inv_susyid = unpack("v*", substr $data, 28, 2);
- $inv_serial = unpack("V*", substr $data, 30, 4);
- $socket->close();
-
- if (AttrVal($name, "target-serial", undef)) {
- return 0 unless($target_serial eq $inv_serial);
- }
- if (AttrVal($name, "target-susyid", undef)) {
- return 0 unless($target_susyid eq $inv_susyid);
- }
-
- # Check the data identifier
- $data_ID = unpack("v*", substr $data, 55, 2);
- Log3 $name, 5, "$name - Data identifier $data_ID";
-
- if($data_ID eq 0x2601) {
- $inv_SPOT_ETOTAL = unpack("V*", substr($data, 62, 4));
- $inv_SPOT_ETODAY = unpack("V*", substr $data, 78, 4);
- Log3 $name, 5, "$name - Found Data SPOT_ETOTAL=$inv_SPOT_ETOTAL and SPOT_ETODAY=$inv_SPOT_ETODAY";
- return (1,$inv_SPOT_ETODAY,$inv_SPOT_ETOTAL,$inv_susyid,$inv_serial);
- }
-
- if($data_ID eq 0x251E) {
- $inv_SPOT_PDC1 = unpack("V*", substr $data, 62, 4);
- if($size < 90) {$inv_SPOT_PDC2 = 0; } else {$inv_SPOT_PDC2 = unpack("V*", substr $data, 90, 4); } # catch short response, in case PDC2 not supported
- $inv_SPOT_PDC1 = ($inv_SPOT_PDC1 == 2147483648) ? 0 : $inv_SPOT_PDC1;
- $inv_SPOT_PDC2 = ($inv_SPOT_PDC2 == 2147483648) ? 0 : $inv_SPOT_PDC2;
- Log3 $name, 5, "$name - Found Data SPOT_PDC1=$inv_SPOT_PDC1 and SPOT_PDC2=$inv_SPOT_PDC2";
- return (1,$inv_SPOT_PDC1,$inv_SPOT_PDC2,$inv_susyid,$inv_serial);
- }
-
- if($data_ID eq 0x4640) {
- $inv_SPOT_PAC1 = unpack("l*", substr $data, 62, 4);
- if($inv_SPOT_PAC1 eq -2147483648) {$inv_SPOT_PAC1 = 0; } # Catch 0x80000000 as 0 value
- $inv_SPOT_PAC2 = unpack("l*", substr $data, 90, 4);
- if($inv_SPOT_PAC2 eq -2147483648) {$inv_SPOT_PAC2 = 0; } # Catch 0x80000000 as 0 value
- $inv_SPOT_PAC3 = unpack("l*", substr $data, 118, 4);
- if($inv_SPOT_PAC3 eq -2147483648) {$inv_SPOT_PAC3 = 0; } # Catch 0x80000000 as 0 value
- Log3 $name, 5, "$name - Found Data SPOT_PAC1=$inv_SPOT_PAC1 and SPOT_PAC2=$inv_SPOT_PAC2 and SPOT_PAC3=$inv_SPOT_PAC3";
- return (1,$inv_SPOT_PAC1,$inv_SPOT_PAC2,$inv_SPOT_PAC3,$inv_susyid,$inv_serial);
- }
-
- if($data_ID eq 0x411E) {
- $inv_PACMAX1 = unpack("V*", substr $data, 62, 4);
- $inv_PACMAX2 = unpack("V*", substr $data, 90, 4);
- $inv_PACMAX3 = unpack("V*", substr $data, 118, 4);
- Log3 $name, 5, "$name - Found Data INV_PACMAX1=$inv_PACMAX1 and INV_PACMAX2=$inv_PACMAX2 and INV_PACMAX3=$inv_PACMAX3";
- return (1,$inv_PACMAX1,$inv_PACMAX2,$inv_PACMAX3,$inv_susyid,$inv_serial);
- }
-
- if($data_ID eq 0x832A) {
- $inv_PACMAX1_2 = unpack("V*", substr $data, 62, 4);
- Log3 $name, 5, "$name - Found Data INV_PACMAX1_2=$inv_PACMAX1_2";
- return (1,$inv_PACMAX1_2,$inv_susyid,$inv_serial);
- }
-
- if($data_ID eq 0x263F) {
- $inv_SPOT_PACTOT = unpack("l*", substr $data, 62, 4);
- if($inv_SPOT_PACTOT eq -2147483648) {$inv_SPOT_PACTOT = 0; } # Catch 0x80000000 as 0 value
- Log3 $name, 5, "$name - Found Data SPOT_PACTOT=$inv_SPOT_PACTOT";
- return (1,$inv_SPOT_PACTOT,$inv_susyid,$inv_serial);
- }
-
- if($data_ID eq 0x295A) {
- $inv_ChargeStatus = unpack("V*", substr $data, 62, 4);
- Log3 $name, 5, "$name - Found Data Battery Charge Status=$inv_ChargeStatus";
- return (1,$inv_ChargeStatus,$inv_susyid,$inv_serial);
- }
- if($data_ID eq 0x451F) {
- $inv_SPOT_UDC1 = unpack("l*", substr $data, 62, 4);
- # catch shorter responses in case not second string supported
- if($size < 146) {
- $inv_SPOT_UDC2 = 0;
- $inv_SPOT_IDC1 = unpack("l*", substr $data, 90, 4);
- $inv_SPOT_IDC2 = 0;
- } else {
- $inv_SPOT_UDC2 = unpack("l*", substr $data, 90, 4);
- $inv_SPOT_IDC1 = unpack("l*", substr $data, 118, 4);
- $inv_SPOT_IDC2 = unpack("l*", substr $data, 146, 4);
- }
- if(($inv_SPOT_UDC1 eq -2147483648) || ($inv_SPOT_UDC1 eq 0xFFFFFFFF)) {$inv_SPOT_UDC1 = 0; } else {$inv_SPOT_UDC1 = $inv_SPOT_UDC1 / 100; } # Catch 0x80000000 and 0xFFFFFFFF as 0 value
- if(($inv_SPOT_UDC2 eq -2147483648) || ($inv_SPOT_UDC2 eq 0xFFFFFFFF)) {$inv_SPOT_UDC2 = 0; } else {$inv_SPOT_UDC2 = $inv_SPOT_UDC2 / 100; } # Catch 0x80000000 and 0xFFFFFFFF as 0 value
- if(($inv_SPOT_IDC1 eq -2147483648) || ($inv_SPOT_IDC1 eq 0xFFFFFFFF)) {$inv_SPOT_IDC1 = 0; } else {$inv_SPOT_IDC1 = $inv_SPOT_IDC1 / 1000; } # Catch 0x80000000 and 0xFFFFFFFF as 0 value
- if(($inv_SPOT_IDC2 eq -2147483648) || ($inv_SPOT_IDC2 eq 0xFFFFFFFF)) {$inv_SPOT_IDC2 = 0; } else {$inv_SPOT_IDC2 = $inv_SPOT_IDC2 / 1000; } # Catch 0x80000000 and 0xFFFFFFFF as 0 value
- Log3 $name, 5, "$name - Found Data SPOT_UDC1=$inv_SPOT_UDC1 and SPOT_UDC2=$inv_SPOT_UDC2 and SPOT_IDC1=$inv_SPOT_IDC1 and SPOT_IDC2=$inv_SPOT_IDC2";
- return (1,$inv_SPOT_UDC1,$inv_SPOT_UDC2,$inv_SPOT_IDC1,$inv_SPOT_IDC2,$inv_susyid,$inv_serial);
- }
- if($data_ID eq 0x4648) {
- $inv_SPOT_UAC1 = unpack("l*", substr $data, 62, 4);
- $inv_SPOT_UAC2 = unpack("l*", substr $data, 90, 4);
- $inv_SPOT_UAC3 = unpack("l*", substr $data, 118, 4);
- $inv_SPOT_IAC1 = unpack("l*", substr $data, 146, 4);
- $inv_SPOT_IAC2 = unpack("l*", substr $data, 174, 4);
- $inv_SPOT_IAC3 = unpack("l*", substr $data, 202, 4);
- if(($inv_SPOT_UAC1 eq -2147483648) || ($inv_SPOT_UAC1 eq 0xFFFFFFFF) || $inv_SPOT_UAC1 < 0) {$inv_SPOT_UAC1 = 0; } else {$inv_SPOT_UAC1 = $inv_SPOT_UAC1 / 100; } # Catch 0x80000000 and 0xFFFFFFFF as 0 value
- if(($inv_SPOT_UAC2 eq -2147483648) || ($inv_SPOT_UAC2 eq 0xFFFFFFFF) || $inv_SPOT_UAC2 < 0) {$inv_SPOT_UAC2 = 0; } else {$inv_SPOT_UAC2 = $inv_SPOT_UAC2 / 100; } # Catch 0x80000000 and 0xFFFFFFFF as 0 value
- if(($inv_SPOT_UAC3 eq -2147483648) || ($inv_SPOT_UAC3 eq 0xFFFFFFFF) || $inv_SPOT_UAC3 < 0) {$inv_SPOT_UAC3 = 0; } else {$inv_SPOT_UAC3 = $inv_SPOT_UAC3 / 100; } # Catch 0x80000000 and 0xFFFFFFFF as 0 value
- if(($inv_SPOT_IAC1 eq -2147483648) || ($inv_SPOT_IAC1 eq 0xFFFFFFFF)) {$inv_SPOT_IAC1 = 0; } else {$inv_SPOT_IAC1 = $inv_SPOT_IAC1 / 1000; } # Catch 0x80000000 and 0xFFFFFFFF as 0 value
- if(($inv_SPOT_IAC2 eq -2147483648) || ($inv_SPOT_IAC2 eq 0xFFFFFFFF)) {$inv_SPOT_IAC2 = 0; } else {$inv_SPOT_IAC2 = $inv_SPOT_IAC2 / 1000; } # Catch 0x80000000 and 0xFFFFFFFF as 0 value
- if(($inv_SPOT_IAC3 eq -2147483648) || ($inv_SPOT_IAC3 eq 0xFFFFFFFF)) {$inv_SPOT_IAC3 = 0; } else {$inv_SPOT_IAC3 = $inv_SPOT_IAC3 / 1000; } # Catch 0x80000000 and 0xFFFFFFFF as 0 value
- Log3 $name, 5, "$name - Found Data SPOT_UAC1=$inv_SPOT_UAC1 and SPOT_UAC2=$inv_SPOT_UAC2 and SPOT_UAC3=$inv_SPOT_UAC3 and SPOT_IAC1=$inv_SPOT_IAC1 and SPOT_IAC2=$inv_SPOT_IAC2 and SPOT_IAC3=$inv_SPOT_IAC3";
- return (1,$inv_SPOT_UAC1,$inv_SPOT_UAC2,$inv_SPOT_UAC3,$inv_SPOT_IAC1,$inv_SPOT_IAC2,$inv_SPOT_IAC3,$inv_susyid,$inv_serial);
- }
- if($data_ID eq 0x491E) {
- $inv_BAT_CYCLES = unpack("V*", substr $data, 62, 4);
- $inv_BAT_TEMP = unpack("V*", substr $data, 90, 4) / 10;
- $inv_BAT_UDC = unpack("V*", substr $data, 118, 4) / 100;
- $inv_BAT_IDC = unpack("l*", substr $data, 146, 4);
- if($inv_BAT_IDC eq -2147483648) {$inv_BAT_IDC = 0; } else { $inv_BAT_IDC = $inv_BAT_IDC / 1000;} # Catch 0x80000000 as 0 value
- Log3 $name, 5, "$name - Found Data BAT_CYCLES=$inv_BAT_CYCLES and BAT_TEMP=$inv_BAT_TEMP and BAT_UDC=$inv_BAT_UDC and BAT_IDC=$inv_BAT_IDC";
- return (1,$inv_BAT_CYCLES,$inv_BAT_TEMP,$inv_BAT_UDC,$inv_BAT_IDC,$inv_susyid,$inv_serial);
- }
- if($data_ID eq 0x2377) {
- $inv_TEMP = unpack("l*", substr $data, 62, 4);
- if($inv_TEMP eq -2147483648) {$inv_TEMP = 0; } else { $inv_TEMP = $inv_TEMP / 100;} # Catch 0x80000000 as 0 value
- Log3 $name, 5, "$name - Found Data Inverter Temp=$inv_TEMP";
- return (1,$inv_TEMP,$inv_susyid,$inv_serial);
- }
- if($data_ID eq 0x462E) {
- $inv_SPOT_OPERTM = int(unpack("V*", substr $data, 62, 4) / 36) / 100;
- $inv_SPOT_FEEDTM = int(unpack("V*", substr $data, 78, 4) / 36) / 100;
- Log3 $name, 5, "$name - Found Data SPOT_OPERTM=$inv_SPOT_OPERTM and SPOT_FEEDTM=$inv_SPOT_FEEDTM";
- return (1,$inv_SPOT_OPERTM,$inv_SPOT_FEEDTM,$inv_susyid,$inv_serial);
- }
- if($data_ID eq 0x4657) {
- $inv_SPOT_FREQ = unpack("V*", substr $data, 62, 4);
- if(($inv_SPOT_FREQ eq -2147483648) || ($inv_SPOT_FREQ eq 0xFFFFFFFF)) {$inv_SPOT_FREQ = 0; } else {$inv_SPOT_FREQ = $inv_SPOT_FREQ / 100; } # Catch 0x80000000 and 0xFFFFFFFF as 0 value
- Log3 $name, 5, "$name - Found Data SPOT_FREQ=$inv_SPOT_FREQ";
- return (1,$inv_SPOT_FREQ,$inv_susyid,$inv_serial);
- }
- if($data_ID eq 0x821E) {
- $inv_CLASS = unpack("V*", substr $data, 102, 4) & 0x00FFFFFF;
- $i = 142; # start address of INV_TYPE
- $inv_TYPE = 0; # initialize to unknown inverter type
- do {
- $temp = unpack("V*", substr $data, $i, 4);
- if(($temp & 0xFF000000) eq 0x01000000) { $inv_TYPE = $temp & 0x00FFFFFF; } # in some models a catalogue is transmitted, right model marked with: 0x01000000 OR INV_Type
- $i = $i+4;
- } while ((unpack("V*", substr $data, $i, 4) ne 0x00FFFFFE) && ($i<$size)); # 0x00FFFFFE is the end marker for attributes
- Log3 $name, 5, "$name - Found Data CLASS=$inv_CLASS and TYPE=$inv_TYPE";
- return (1,$inv_TYPE,$inv_CLASS,$inv_susyid,$inv_serial);
- }
- if($data_ID eq 0x4164) {
- $i = 0;
- $temp = 0;
- $inv_GRIDRELAY = 0x00FFFFFD; # Code for No Information;
- do {
- $temp = unpack("V*", substr $data, 62 + $i*4, 4);
- if(($temp & 0xFF000000) ne 0) { $inv_GRIDRELAY = $temp & 0x00FFFFFF; }
- $i = $i + 1;
- } while ((unpack("V*", substr $data, 62 + $i*4, 4) ne 0x00FFFFFE) && ($i < 5)); # 0x00FFFFFE is the end marker for attributes
- Log3 $name, 5, "$name - Found Data INV_GRIDRELAY=$inv_GRIDRELAY";
- return (1,$inv_GRIDRELAY,$inv_susyid,$inv_serial);
- }
- if($data_ID eq 0x2148) {
- $i = 0;
- $temp = 0;
- $inv_STATUS = 0x00FFFFFD; # Code for No Information;
- do {
- $temp = unpack("V*", substr $data, 62 + $i*4, 4);
- if(($temp & 0xFF000000) ne 0) { $inv_STATUS = $temp & 0x00FFFFFF; }
- $i = $i + 1;
- } while ((unpack("V*", substr $data, 62 + $i*4, 4) ne 0x00FFFFFE) && ($i < 5)); # 0x00FFFFFE is the end marker for attributes
- Log3 $name, 5, "$name - Found Data inv_STATUS=$inv_STATUS";
- return (1,$inv_STATUS,$inv_susyid,$inv_serial);
- }
-
- return 0;
- }
- ##########################################################################
- # Login
- ##########################################################################
- sub SMA_logon($$$) {
- # Parameters: host - passcode
- my ($host,$pass,$hash) = @_;
- my $cmdheader = "534D4100000402A00000000100";
- my $pktlength = "3A"; # length = 58 for logon command
- my $esignature = "001060650EA0";
- my $name = $hash->{NAME};
- my $mysusyid = $hash->{HELPER}{MYSUSYID};
- my $myserialnumber = $hash->{HELPER}{MYSERIALNUMBER};
- my $pkt_ID = $hash->{HELPER}{PKT_ID};
- my ($cmd, $timestmp, $myID, $target_ID, $spkt_ID, $cmd_ID);
- my ($socket,$data,$size);
-
- # Seriennummer und SuSyID des Ziel-WR setzen
- my $default_target_susyid = $hash->{HELPER}{DEFAULT_TARGET_SUSYID};
- my $default_target_serial = $hash->{HELPER}{DEFAULT_TARGET_SERIAL};
- my $target_susyid = AttrVal($name, "target-susyid", $default_target_susyid);
- my $target_serial = AttrVal($name, "target-serial", $default_target_serial);
- #Encode the password
- my $encpasswd = "888888888888888888888888"; # template for password
- for my $index (0..length $pass ) # encode password
- {
- if ( (hex(substr($encpasswd,($index*2),2)) + ord(substr($pass,$index,1))) < 256 ) {
- substr($encpasswd,($index*2),2) = substr(sprintf ("%lX", (hex(substr($encpasswd,($index*2),2)) + ord(substr($pass,$index,1)))),0,2);
- } else {
- substr($encpasswd,($index*2),2) = substr(sprintf ("%lX", (hex(substr($encpasswd,($index*2),2)) + ord(substr($pass,$index,1)))),1,2);
- }
- }
- # Get current timestamp in epoch format (unix format)
- $timestmp = ByteOrderLong(sprintf("%08X",int(time())));
- # Define own ID and target ID and packet ID
- $myID = ByteOrderShort(substr(sprintf("%04X",$mysusyid),0,4)) . ByteOrderLong(sprintf("%08X",$myserialnumber));
- $target_ID = ByteOrderShort(substr(sprintf("%04X",$target_susyid),0,4)) . ByteOrderLong(sprintf("%08X",$target_serial));
- $pkt_ID = 0x8001; # Reset to 0x8001
- $spkt_ID = ByteOrderShort(sprintf("%04X",$pkt_ID));
- #Logon command
- $cmd_ID = "0C04FDFF" . "07000000" . "84030000"; # Logon command + User group "User" + (maybe) Timeout
- #build final command to send
- $cmd = $cmdheader . $pktlength . $esignature . $target_ID . "0001" . $myID . "0001" . "00000000" . $spkt_ID . $cmd_ID . $timestmp . "00000000" . $encpasswd . "00000000";
- # flush after every write
- $| = 1;
- # Create Socket and check if successful
- $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 0;
- };
- # Send Data
- $data = pack("H*",$cmd);
- $socket->send($data);
- Log3 $name, 4, "$name - Send login to $host on Port 9522 with password $pass ";
- Log3 $name, 5, "$name - Send: $cmd ";
-
- # Receive Data and do a first check regarding length
- eval {
- $socket->recv($data, $hash->{HELPER}{MAXBYTES});
- $size = length($data);
- };
- # check if something was received
- if (defined $size) {
- my $received = unpack("H*", $data);
- Log3 $name, 5, "$name - Received: $received";
- }
- # Nothing received -> exit
- if (not defined $size) {
- Log3 $name, 1, "$name - Nothing received...";
- # send: cmd_logout
- $socket->close();
- SMA_logout($hash,$host);
- return 0;
- } else {
- # We have received something!
- if ($size > 62) {
- # Check all parameters of answer
- my $r_susyid = unpack("v*", substr $data, 20, 2);
- my $r_serial = unpack("V*", substr $data, 22, 4);
- my $r_pkt_ID = unpack("v*", substr $data, 40, 2);
- my $r_cmd_ID = unpack("V*", substr $data, 42, 4);
- my $r_error = unpack("V*", substr $data, 36, 4);
- if (($r_pkt_ID ne $pkt_ID) || ($r_cmd_ID ne 0xFFFD040D) || ($r_error ne 0)) {
- # Response does not match the parameters we have sent, maybe different target
- Log3 $name, 1, "$name - Inverter answer does not match our parameters.";
- Log3 $name, 5, "$name - Request/Response: SusyID $mysusyid/$r_susyid, Serial $myserialnumber/$r_serial, Packet ID $hash->{HELPER}{PKT_ID}/$r_pkt_ID, Command 0xFFFD040D/$r_cmd_ID, Error $r_error";
- # send: cmd_logout
- $socket->close();
- SMA_logout($hash,$host);
- return 0;
- }
- } else {
- Log3 $name, 1, "$name - Format of inverter response does not fit.";
- # send: cmd_logout
- $socket->close();
- SMA_logout($hash,$host);
- return 0;
- }
- }
- # All seems ok, logged in!
- my $inv_susyid = unpack("v*", substr $data, 28, 2);
- my $inv_serial = unpack("V*", substr $data, 30, 4);
- $socket->close();
-
- if (AttrVal($name, "target-serial", undef)) {
- return 0 unless($inv_serial eq $target_serial);
- }
- if (AttrVal($name, "target-susyid", undef)) {
- return 0 unless($inv_susyid eq $target_susyid);
- }
-
- Log3 $name, 4, "$name - logged in to inverter serial: $inv_serial, susyid: $inv_susyid";
- return 1;
- }
- ##########################################################################
- # Logout
- ##########################################################################
- sub SMA_logout($$) {
- # Parameters: host
- my ($hash,$host) = @_;
- my $name = $hash->{NAME};
- my $cmdheader = "534D4100000402A00000000100";
- my $pktlength = "22"; # length = 34 for logout command
- my $esignature = "0010606508A0";
- my $mysusyid = $hash->{HELPER}{MYSUSYID};
- my $myserialnumber = $hash->{HELPER}{MYSERIALNUMBER};
- my $pkt_ID = $hash->{HELPER}{PKT_ID};
- my ($cmd, $myID, $target_ID, $spkt_ID, $cmd_ID);
- my ($socket,$data,$size);
-
- # Seriennummer und SuSyID des Ziel-WR setzen
- my $default_target_susyid = $hash->{HELPER}{DEFAULT_TARGET_SUSYID};
- my $default_target_serial = $hash->{HELPER}{DEFAULT_TARGET_SERIAL};
- my $target_susyid = AttrVal($name, "target-susyid", $default_target_susyid);
- my $target_serial = AttrVal($name, "target-serial", $default_target_serial);
- # Define own ID and target ID and packet ID
- $myID = ByteOrderShort(substr(sprintf("%04X",$mysusyid),0,4)) . ByteOrderLong(sprintf("%08X",$myserialnumber));
- $target_ID = ByteOrderShort(substr(sprintf("%04X",$target_susyid),0,4)) . ByteOrderLong(sprintf("%08X",$target_serial));
- # Increasing Packet ID
- $hash->{HELPER}{PKT_ID} = $hash->{HELPER}{PKT_ID} + 1;
- $spkt_ID = ByteOrderShort(sprintf("%04X",$hash->{HELPER}{PKT_ID}));
-
- #Logout command
- $cmd_ID = "0E01FDFF" . "FFFFFFFF"; # Logout command
- #build final command to send
- $cmd = $cmdheader . $pktlength . $esignature . $target_ID . "0003" . $myID . "0003" . "00000000" . $spkt_ID . $cmd_ID . "00000000";
- # flush after every write
- $| = 1;
- # Create Socket and check if successful
- $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 0;
- };
- # Send Data
- $data = pack("H*",$cmd);
- $socket->send($data);
- Log3 $name, 4, "$name - Send logout to $host on Port 9522";
- Log3 $name, 5, "$name - Send: $cmd ";
-
- $target_serial = ($target_serial eq $default_target_serial)?"any inverter":$target_serial;
- $target_susyid = ($target_susyid eq $default_target_susyid)?"any susyid":$target_susyid;
- Log3 $name, 4, "$name - logged out now from inverter serial: $target_serial, susyid: $target_susyid";
-
- $socket->close();
- return 1;
- }
- ##########################################################################
- # Hilfsroutinen
- ##########################################################################
- ##########################
- sub ByteOrderShort($) {
- my $input = $_[0];
- my $output = "";
- $output = substr($input, 2, 2) . substr($input, 0, 2);
- return $output;
- }
- ##########################
- sub ByteOrderLong($) {
- my $input = $_[0];
- my $output = "";
- $output = substr($input, 6, 2) . substr($input, 4, 2) . substr($input, 2, 2) . substr($input, 0, 2);
- return $output;
- }
- ##########################
- sub StatusText($)
- {
- # Parameter is the code, return value is the Text or if not known then the code as string
- my $code = $_[0];
- if($code eq 51) { return (AttrVal("global", "language", "EN") eq "DE") ? "geschlossen" : "Closed"; }
- if($code eq 311) { return (AttrVal("global", "language", "EN") eq "DE") ? "offen" : "Open"; }
- if($code eq 16777213) { return (AttrVal("global", "language", "EN") eq "DE") ? "Information liegt nicht vor" : "No Information"; }
- if($code eq 35) { return (AttrVal("global", "language", "EN") eq "DE") ? "Fehler" : "Fault"; }
- if($code eq 303) { return "Off"; }
- if($code eq 307) { return "Ok"; }
- if($code eq 455) { return (AttrVal("global", "language", "EN") eq "DE") ? "Warnung" : "Warning"; }
- return sprintf("%d", $code);
- }
- ##########################
- # identify device type
- sub devtype ($) {
- my ($code) = @_;
-
- unless (exists($SMAInverter_devtypes{$code})) { return $code;}
- my $dev = $SMAInverter_devtypes{$code};
- return ($dev);
- }
- ##########################
- # identify device class
- sub classtype ($) {
- my ($code) = @_;
- my $class;
-
- if(AttrVal("global", "language", "EN") eq "DE") {
- unless (exists($SMAInverter_classesDE{$code})) { return $code;}
- $class = $SMAInverter_classesDE{$code};
- } else {
- unless (exists($SMAInverter_classesEN{$code})) { return $code;}
- $class = $SMAInverter_classesEN{$code};
- }
-
- return ($class);
- }
- 1;
- =pod
- =item summary Integration of SMA Inverters over it's Speedwire (=Ethernet) Interface
- =item summary_DE Integration von SMA Wechselrichtern über Speedwire (=Ethernet) Interface
- =begin html
- <a name="SMAInverter"></a>
- <h3>SMAInverter</h3>
- Module for the integration of a SMA Inverter over it's Speedwire (=Ethernet) Interface.<br>
- Tested on Sunny Tripower 6000TL-20 and Sunny Island 4.4 with Speedwire/Webconnect Piggyback.
- <br><br>
- Questions and discussions about this module you can find in the FHEM-Forum link:<br>
- <a href="https://forum.fhem.de/index.php/topic,56080.msg476525.html#msg476525">76_SMAInverter.pm - Abfrage von SMA Wechselrichter</a>.
- <br><br>
- <b>Requirements</b>
- <br><br>
- This module requires:
- <ul>
- <li>Perl Module: IO::Socket::INET (apt-get install libio-socket-multicast-perl) </li>
- <li>Perl Module: Date::Time (apt-get install libdatetime-perl) </li>
- <li>Perl Module: Time::HiRes</li>
- <li>FHEM Module: 99_SUNRISE_EL.pm</li>
- <li>FHEM Module: Blocking.pm</li>
- </ul>
- <br>
- <br>
- <b>Define</b>
- <ul>
- <code>define <name> SMAInverter <pin> <hostname/ip> </code><br>
- <br>
- <li>pin: User-Password of the SMA 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 of the inverter is 9522 by default. Firewall has to allow connection on this port !</li>
- </ul>
- <b>Operation method</b>
- <ul>
- The module sends commands to the inverter and checks if they are supported by the inverter.<br>
- In case of a positive answer the data is collected and displayed in the readings according to the detail-level. <br>
- If more than one inverter is installed, set attributes "target-susyid" and "target-serial" with an appropriate value. <br><br>
- The normal operation time of the inverter is supposed from sunrise to sunset. In that time period the inverter will be polled.
- The time of sunrise and sunset will be calculated by functions of FHEM module 99_SUNRISE_EL.pm which is loaded automatically by default.
- Therefore the global attribute "longitude" and "latitude" should be set to determine the position of the solar system
- (see <a href="#SUNRISE_EL">Commandref SUNRISE_EL</a>). <br><br>
- By the attribute "suppressSleep" the sleep mode between sunset and sunrise can be suppressed. Using attribute "offset" you may prefer the sunrise and
- defer the sunset virtually. So the working period of the inverter will be extended. <br><br>
- In operating mode "automatic" the inverter will be requested periodically corresponding the preset attribute "interval". The operating mode can be
- switched to "manual" to realize the retrieval manually (e.g. to synchronize the requst with a SMA energy meter by notify). <br><br>
- During inverter operating time the average energy production of the last 5, 10 and 15 minutes will be calculated and displayed in the readings
- "avg_power_lastminutes_05", "avg_power_lastminutes_10" and "avg_power_lastminutes_15". <b>Note:</b> To permit a precise calculation, you should
- also set the real request interval into the attribute "interval" although you would use the "manual" operation mode ! <br><br>
- The retrieval of the inverter will be executed non-blocking. You can adjust the timeout value for this background process by attribute "timeout". <br>
- </ul>
- <b>Get</b>
- <br>
- <ul>
- <code>get <name> data</code>
- <br><br>
- The request of the inverter will be executed. Those possibility is especifically created for the "manual" operation mode (see attribute "mode").
- </ul>
- <b>Attributes</b>
- <ul>
- <li><b>interval</b> : Queryintreval in seconds </li>
- <li><b>detail-level</b> : "0" - Only Power and Energy / "1" - Including Voltage and Current / "2" - All values </li>
- <li><b>disable</b> : 1 = the module is disabled </li>
- <li><b>mode</b> : automatic = the inverter will be polled by preset interval, manual = query only by command "get <name> data" </li>
- <li><b>offset</b> : time in seconds to prefer the sunrise respectively defer the sunset virtualy (0 ... 7200). You will be able to extend the working
- period of the module. </li>
- <li><b>SBFSpotComp</b> : 1 = the readings are created like SBFSpot-style </li>
- <li><b>suppressSleep</b> : the sleep mode (after sunset, before sunrise) is deactivated and the inverter will be polled continuously. </li>
- <li><b>showproctime</b> : shows processing time in background and wasted time to retrieve inverter data </li>
- <li><b>target-susyid</b> : In case of a Multigate the target SUSyID can be defined. If more than one inverter is installed you have to
- set the inverter-SUSyID to assign the inverter to the device definition.
- Default is 0xFFFF, means any SUSyID</li>
- <li><b>target-serial</b> : In case of a Multigate the target Serialnumber can be defined. If more than one inverter is installed you have to
- set the inverter-Serialnumber to assign the inverter to the device definition.
- Default is 0xFFFFFFFF, means any Serialnumber</li>
- <li><b>timeout</b> : setup timeout of inverter data request (default 60s) </li>
- </ul>
- <b>Readings</b>
- <ul>
- <li><b>BAT_CYCLES / bat_cycles</b> : Battery recharge cycles </li>
- <li><b>BAT_IDC / bat_idc</b> : Battery Current </li>
- <li><b>BAT_TEMP / bat_temp</b> : Battery temperature </li>
- <li><b>BAT_UDC / bat_udc</b> : Battery Voltage </li>
- <li><b>ChargeStatus / chargestatus</b> : Battery Charge status </li>
- <li><b>CLASS / device_class</b> : Inverter Class </li>
- <li><b>PACMAX1 / pac_max_phase_1</b> : Nominal power in Ok Mode </li>
- <li><b>PACMAX1_2 / pac_max_phase_1_2</b> : Maximum active power device (Some inverters like SB3300/SB1200) </li>
- <li><b>PACMAX2 / pac_max_phase_2</b> : Nominal power in Warning Mode </li>
- <li><b>PACMAX3 / pac_max_phase_3</b> : Nominal power in Fault Mode </li>
- <li><b>Serialnumber / serial_number</b> : Inverter Serialnumber </li>
- <li><b>SPOT_ETODAY / etoday</b> : Today yield </li>
- <li><b>SPOT_ETOTAL / etotal</b> : Total yield </li>
- <li><b>SPOT_FEEDTM / feed-in_time</b> : Feed-in time </li>
- <li><b>SPOT_FREQ / grid_freq.</b> : Grid Frequency </li>
- <li><b>SPOT_IAC1 / phase_1_iac</b> : Grid current phase L1 </li>
- <li><b>SPOT_IAC2 / phase_2_iac</b> : Grid current phase L2 </li>
- <li><b>SPOT_IAC3 / phase_3_iac</b> : Grid current phase L3 </li>
- <li><b>SPOT_IDC1 / string_1_idc</b> : DC current input </li>
- <li><b>SPOT_IDC2 / string_2_idc</b> : DC current input </li>
- <li><b>SPOT_OPERTM / operation_time</b> : Operation Time </li>
- <li><b>SPOT_PAC1 / phase_1_pac</b> : Power L1 </li>
- <li><b>SPOT_PAC2 / phase_2_pac</b> : Power L2 </li>
- <li><b>SPOT_PAC3 / phase_3_pac</b> : Power L3 </li>
- <li><b>SPOT_PACTOT / total_pac</b> : Total Power </li>
- <li><b>SPOT_PDC1 / string_1_pdc</b> : DC power input 1 </li>
- <li><b>SPOT_PDC2 / string_2_pdc</b> : DC power input 2 </li>
- <li><b>SPOT_UAC1 / phase_1_uac</b> : Grid voltage phase L1 </li>
- <li><b>SPOT_UAC2 / phase_2_uac</b> : Grid voltage phase L2 </li>
- <li><b>SPOT_UAC3 / phase_3_uac</b> : Grid voltage phase L3 </li>
- <li><b>SPOT_UDC1 / string_1_udc</b> : DC voltage input </li>
- <li><b>SPOT_UDC2 / string_2_udc</b> : DC voltage input </li>
- <li><b>SUSyID / susyid</b> : Inverter SUSyID </li>
- <li><b>INV_TEMP / device_temperature</b> : Inverter temperature </li>
- <li><b>INV_TYPE / device_type</b> : Inverter Type </li>
- <li><b>POWER_IN / power_in</b> : Battery Charging power </li>
- <li><b>POWER_OUT / power_out</b> : Battery Discharging power </li>
- <li><b>INV_GRIDRELAY / gridrelay_status</b> : Grid Relay/Contactor Status </li>
- <li><b>INV_STATUS / device_status</b> : Inverter Status </li>
- <li><b>opertime_start</b> : Begin of iverter operating time corresponding the calculated time of sunrise with consideration of the
- attribute "offset" (if set) </li>
- <li><b>opertime_stop</b> : End of iverter operating time corresponding the calculated time of sunrise with consideration of the
- attribute "offset" (if set) </li>
- <li><b>modulstate</b> : shows the current module state "normal" or "sleep" if the inverter won't be requested at the time. </li>
- <li><b>avg_power_lastminutes_05</b> : average power of the last 5 minutes. </li>
- <li><b>avg_power_lastminutes_10</b> : average power of the last 10 minutes. </li>
- <li><b>avg_power_lastminutes_15</b> : average power of the last 15 minutes. </li>
- <li><b>inverter_processing_time</b> : wasted time to retrieve the inverter data </li>
- <li><b>background_processing_time</b> : total wasted time by background process (BlockingCall) </li>
- </ul>
- <br><br>
- =end html
- =begin html_DE
- <a name="SMAInverter"></a>
- <h3>SMAInverter</h3>
- Modul zur Einbindung eines SMA Wechselrichters über Speedwire (Ethernet).<br>
- Getestet mit Sunny Tripower 6000TL-20 und Sunny Island 4.4 mit Speedwire/Webconnect Piggyback.
- <br><br>
- Fragen und Diskussionen rund um dieses Modul finden sie im FHEM-Forum unter:<br>
- <a href="https://forum.fhem.de/index.php/topic,56080.msg476525.html#msg476525">76_SMAInverter.pm - Abfrage von SMA Wechselrichter</a>.
- <br><br>
- <b>Voraussetzungen</b>
- <br><br>
- Dieses Modul benötigt:
- <ul>
- <li>Perl Modul: IO::Socket::INET (apt-get install libio-socket-multicast-perl) </li>
- <li>Perl Modul: Datetime (apt-get install libdatetime-perl) </li>
- <li>Perl Modul: Time::HiRes</li>
- <li>FHEM Modul: 99_SUNRISE_EL.pm</li>
- <li>FHEM Modul: Blocking.pm</li>
- </ul>
- <br>
- <br>
- <b>Define</b>
- <ul>
- <code>define <name> SMAInverter <pin> <hostname/ip></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>Der Port des Wechselrichters ist 9522. Dieser Port muss in der Firewall freigeschaltet sein !</li>
- </ul>
- <b>Arbeitsweise</b>
- <ul>
- Das Modul schickt Befehle an den Wechselrichter und überprüft, ob diese unterstützt werden.<br>
- Bei einer positiven Antwort werden die Daten gesammelt und je nach Detail-Level in den Readings dargestellt. <br>
- Sind mehr als ein Wechselrichter installiert, sind die Attribute "target-susyid" und "target-serial" entsprechend zu setzen um die korrekte
- Funktion zu gewährleisten. <br><br>
- Die normale Betriebszeit des Wechselrichters wird in der Zeit vom Sonnenaufgang bis Sonnenuntergang angenommen. In dieser Periode werden die Wechselrichterdaten
- abgefragt. Die Ermittlung von Sonnenaufgang / Sonnenuntergang wird über die Funktionen des FHEM-Moduls 99_SUNRISE_EL.pm vorgenommen. Zu diesem Zweck sollten die globalen
- Attribute longitude und latitude gesetzt sein um den Standort der Anlage genau zu ermitteln. (siehe <a href="#SUNRISE_EL">Commandref SUNRISE_EL</a>) <br><br>
- Mit dem Attribut "suppressSleep" kann der Schlafmodus unterdrückt werden. Das Attribut "offset" dient dazu den effektiven Zeitpunkt des Sonnenaufgangs / Sonnenuntergangs
- um den Betrag "offset" vorzuziehen (Sonnenaufgang) bzw. zu verzögern (Sonnenuntergang) und somit die Abfrageperiode des Wechselrichters zu verlängern. <br><br>
- Im Betriebsmodus "automatic" wird der Wechselrichter entsprechend des eingestellten Attributs "interval" abgefragt. Der Betriebsmodus kann in "manual"
- umgestellt werden um eine manuelle Abfrage zu realisieren (z.B. Synchronisierung mit einem SMA Energymeter über ein Notify). <br><br>
- Während der Betriebszeit des Wechselrichters wird die durchschnittliche Energieerzeugung der letzten 5, 10, 15 Minuten berechnet und in den Readings
- "avg_power_lastminutes_05", "avg_power_lastminutes_10" und "avg_power_lastminutes_15" ausgegeben. <b>Hinweis:</b> Um eine korrekte Berechnung zu
- ermöglichen, sollte auch im Betriebsmodus "manual" das tatsächliche Abfrageinterval im Attribute "interval" hinterlegt werden ! <br><br>
- Die Abfrage des Wechselrichters wird non-blocking ausgeführt. Der Timeoutwert für diesen Hintergrundprozess kann mit dem Attribut "timeout" eingestellt werden. <br>
- </ul>
- <b>Get</b>
- <br>
- <ul>
- <code>get <name> data</code>
- <br><br>
- Die Datenabfrage des Wechselrichters wird ausgeführt. Diese Möglichkeit ist speziell für den Betriebsmodus "manual" vorgesehen (siehe Attribut "mode").
- </ul>
- <b>Attribute</b>
- <ul>
- <li><b>interval</b> : Abfrageinterval in Sekunden </li>
- <li><b>detail-level</b> : "0" - Nur Leistung und Energie / "1" - zusätzlich Strom und Spannung / "2" - Alle Werte </li>
- <li><b>disable</b> : 1 = das Modul ist disabled </li>
- <li><b>mode</b> : automatic = die Wechselrichterwerte werden im eingestellten Interval abgefragt, manual = Abfrage nur mit "get <name> data" </li>
- <li><b>offset</b> : Zeit in Sekunden um die der Sonnenaufgang vorgezogen bzw. Sonnenuntergang verzögert wird (0 ... 7200). Dadurch wird die
- effektive Aktivzeit des Moduls erweitert. </li>
- <li><b>suppressSleep</b> : der Schlafmodus (nach Sonnenuntergang, vor Sonnenaufgang) wird ausgeschaltet und der WR abgefragt. </li>
- <li><b>showproctime</b> : zeigt die für den Hintergrundprozess und die Abfrage des Wechselrichter verbrauchte Zeit. </li>
- <li><b>SBFSpotComp</b> : 1 = die Readings werden kompatibel zu SBFSpot-Ausgaben erzeugt </li>
- <li><b>target-susyid</b> : Im Falle eines Multigate kann die Ziel-SUSyID definiert werden. Ist mehr als ein Wechselrichter installiert,
- muß die Wechselreichter-SUSyID gesetzt werden um den Wechselrichter der Device-Definition eindeutig zuzuweisen.
- Default ist 0xFFFF (=keine Einschränkung)</li>
- <li><b>target-serial</b> : Im Falle eines Multigate kann die Ziel-Seriennummer definiert werden. Ist mehr als ein Wechselrichter installiert,
- muß die Wechselreichter-Seriennummer gesetzt werden um den Wechselrichter der Device-Definition eindeutig zuzuweisen.
- Default ist 0xFFFFFFFF (=keine Einschränkung)</li>
- <li><b>timeout</b> : Einstellung des timeout für die Wechselrichterabfrage (default 60s) </li>
- </ul>
- <b>Readings</b>
- <ul>
- <li><b>BAT_CYCLES / bat_cycles</b> : Akku Ladezyklen </li>
- <li><b>BAT_IDC / bat_idc</b> : Akku Strom </li>
- <li><b>BAT_TEMP / bat_temp</b> : Akku Temperatur </li>
- <li><b>BAT_UDC / bat_udc</b> : Akku Spannung </li>
- <li><b>ChargeStatus / chargestatus</b> : Akku Ladestand </li>
- <li><b>CLASS / device_class</b> : Wechselrichter Klasse </li>
- <li><b>PACMAX1 / pac_max_phase_1</b> : Nominelle Leistung in Ok Mode </li>
- <li><b>PACMAX1_2 / pac_max_phase_1_2</b> : Maximale Leistung (für einige Wechselrichtertypen) </li>
- <li><b>PACMAX2 / pac_max_phase_2</b> : Nominelle Leistung in Warning Mode </li>
- <li><b>PACMAX3 / pac_max_phase_3</b> : Nominelle Leistung in Fault Mode </li>
- <li><b>Serialnumber / serial_number</b> : Wechselrichter Seriennummer </li>
- <li><b>SPOT_ETODAY / etoday</b> : Energie heute</li>
- <li><b>SPOT_ETOTAL / etotal</b> : Energie Insgesamt </li>
- <li><b>SPOT_FEEDTM / feed-in_time</b> : Einspeise-Stunden </li>
- <li><b>SPOT_FREQ / grid_freq.</b> : Netz Frequenz </li>
- <li><b>SPOT_IAC1 / phase_1_iac</b> : Netz Strom phase L1 </li>
- <li><b>SPOT_IAC2 / phase_2_iac</b> : Netz Strom phase L2 </li>
- <li><b>SPOT_IAC3 / phase_3_iac</b> : Netz Strom phase L3 </li>
- <li><b>SPOT_IDC1 / string_1_idc</b> : DC Strom Eingang 1 </li>
- <li><b>SPOT_IDC2 / string_2_idc</b> : DC Strom Eingang 2 </li>
- <li><b>SPOT_OPERTM / operation_time</b> : Betriebsstunden </li>
- <li><b>SPOT_PAC1 / phase_1_pac</b> : Leistung L1 </li>
- <li><b>SPOT_PAC2 / phase_2_pac</b> : Leistung L2 </li>
- <li><b>SPOT_PAC3 / phase_3_pac</b> : Leistung L3 </li>
- <li><b>SPOT_PACTOT / total_pac</b> : Gesamtleistung </li>
- <li><b>SPOT_PDC1 / string_1_pdc</b> : DC Leistung Eingang 1 </li>
- <li><b>SPOT_PDC2 / string_2_pdc</b> : DC Leistung Eingang 2 </li>
- <li><b>SPOT_UAC1 / phase_1_uac</b> : Netz Spannung phase L1 </li>
- <li><b>SPOT_UAC2 / phase_2_uac</b> : Netz Spannung phase L2 </li>
- <li><b>SPOT_UAC3 / phase_3_uac</b> : Netz Spannung phase L3 </li>
- <li><b>SPOT_UDC1 / string_1_udc</b> : DC Spannung Eingang 1 </li>
- <li><b>SPOT_UDC2 / string_2_udc</b> : DC Spannung Eingang 2 </li>
- <li><b>SUSyID / susyid</b> : Wechselrichter SUSyID </li>
- <li><b>INV_TEMP / device_temperature</b> : Wechselrichter Temperatur </li>
- <li><b>INV_TYPE / device_type</b> : Wechselrichter Typ </li>
- <li><b>POWER_IN / power_in</b> : Akku Ladeleistung </li>
- <li><b>POWER_OUT / power_out</b> : Akku Entladeleistung </li>
- <li><b>INV_GRIDRELAY / gridrelay_status</b> : Netz Relais Status </li>
- <li><b>INV_STATUS / device_status</b> : Wechselrichter Status </li>
- <li><b>opertime_start</b> : Beginn Aktivzeit des Wechselrichters entsprechend des ermittelten Sonnenaufgangs mit Berücksichtigung des
- Attributs "offset" (wenn gesetzt) </li>
- <li><b>opertime_stop</b> : Ende Aktivzeit des Wechselrichters entsprechend des ermittelten Sonnenuntergangs mit Berücksichtigung des
- Attributs "offset" (wenn gesetzt) </li>
- <li><b>modulstate</b> : zeigt den aktuellen Modulstatus "normal" oder "sleep" falls der Wechselrichter nicht abgefragt wird. </li>
- <li><b>avg_power_lastminutes_05</b> : durchschnittlich erzeugte Leistung der letzten 5 Minuten. </li>
- <li><b>avg_power_lastminutes_10</b> : durchschnittlich erzeugte Leistung der letzten 10 Minuten. </li>
- <li><b>avg_power_lastminutes_15</b> : durchschnittlich erzeugte Leistung der letzten 15 Minuten. </li>
- <li><b>inverter_processing_time</b> : verbrauchte Zeit um den Wechelrichter abzufragen. </li>
- <li><b>background_processing_time</b> : gesamte durch den Hintergrundprozess (BlockingCall) verbrauchte Zeit. </li>
-
- </ul>
- <br><br>
- =end html_DE
- =cut
|