| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835 |
- #################################################################################################################
- # $Id: 76_SMAInverter.pm 16934 2018-07-02 20:11:29Z 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 by DS_Starter
- #
- # 2.10.0 29.06.2018 Internal MODEL added
- # 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.10.0";
- # 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);
- $hash->{MODEL} = $a[1] if($a[0] eq "device_type");
- 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
|