Fronius Wechselrichter

Aus FHEMWiki

Beschreibung

Die Wechselrichter der österreichischen Fronius GmbH (gegründet 1945) lassen sich auf vielfältige Weise in FHEM einbinden. Die Abfrage ist sowohl über ModBus möglich, als auch über ein dezidiertes API (REST-Schnittstelle, d.h. Abfrage und Steuerung über HTTP Requests).

Die Wechselrichter sind seitens des Herstellers für einen Cloud-Zugriff des so genannten Fronius Solar Web konfiguriert. Das stellt zwar netterweise verschiedene Auswertungen zur Verfügung, liefert aber dem Hersteller auch wertvolle Daten.

Die Wechselrichter sind durch zwei verschiedene Passwörter geschützt, ein so genanntes "Customer"-Passwort für den Besitzer/Betreiber, und ein so genanntes "Technician"-Passwort. Die Installateure geben dieses zweite Passwort in der Regel nicht an den Betreiber heraus, mit dem Argument, dass man damit ja auch Parameter verändern könne, die die Netzstabilität beeinträchtigen, und dass sie dafür haften würden. Diese Behauptung kann man getrost als Märchen bezeichnen. Erstens haftet immer der Betreiber der Anlage, und zweitens ist die Veränderung systemkritischer Parameter nicht ohne Eingabe einer PIN (also einer weiteren Schutzebene) möglich. Tipp: Einigen Sie sich mit Ihrem Installateur und lassen Sie gleich bei der Installation wichtige Daten korrekt einstellen, etwa den Zugriff auf das API freischalten.

Das API ist durchaus gut dokumentiert. Um diese Dokumentation zu erhalten, muss man sich allerdings namentlich bei Fronius registrieren.

Einbindung per HTTPMOD

Zunächst muss der Zugriff auf das API unter Einsatz des Technikerpassworts im Wechselrichter freigeschaltet werden. Für den Zugriff auf den Wechselrichter werden drei unabhängige HTTPMOD-Devices eingerichtet. Das ist deshalb sinnvoll, weil unterschiedliche Daten mit unterschiedlicher zeitlicher Auflösung benötigt werden.

PowerFlow-Device

In diesem FHEM-Device werden alle 10 Sekunden die wesentlichen Daten über den Energiefluss im gesamten System gesammelt und in einer Tabelle angezeigt. Das Device wird nachstehend in mehreren Teil-Listings erläuter. Zunächst die Basisdefinition:

defmod PowerFlow HTTPMOD <Adresse des Wechselrichters>/solar_api/v1/GetPowerFlowRealtimeData.fcgi 10
attr PowerFlow event-min-interval energy.*:10
attr PowerFlow event-on-update-reading power_grid,power_eco,power_battery,energy_battery,power_house,energy_house,power_load,energy_load,power_PV2,energy_PV2,power_summary_in,power_summary_out,energy_summary_in,energy_summary_out,battery_SOC
attr PowerFlow extractAllJSON 0
attr PowerFlow group energyControl
attr PowerFlow reading103Format %.3f
attr PowerFlow reading103JSON Body_Data_Inverters_1_P
attr PowerFlow reading103Name power_eco
attr PowerFlow reading103OExpr $val/1000
attr PowerFlow reading105Format %.3f
attr PowerFlow reading105JSON Body_Data_Site_P_Akku
attr PowerFlow reading105Name power_battery
attr PowerFlow reading105OExpr -$val/1000
attr PowerFlow reading108JSON Head_Status_UserMessage
attr PowerFlow reading108Name Status_UserMessage
attr PowerFlow reading110JSON Body_Data_Site_BatteryStandby
attr PowerFlow reading110Name battery_Stby
attr PowerFlow reading111JSON Body_Data_Inverters_1_SOC
attr PowerFlow reading111Name battery_SOC
attr PowerFlow reading115Format %.1f
attr PowerFlow reading115JSON Body_Data_Site_rel_SelfConsumption
attr PowerFlow reading115Name selfConsumption_p
attr PowerFlow reading116JSON Head_Status_Reason
attr PowerFlow reading116Name Status_Reason
attr PowerFlow reading117JSON Head_Status_Code
attr PowerFlow reading117Name Status_Code
attr PowerFlow reading121Format %.3f
attr PowerFlow reading121JSON Body_Data_Site_P_Grid
attr PowerFlow reading121Name power_grid
attr PowerFlow reading121OExpr $val/1000
attr PowerFlow reading122Format %.3f
attr PowerFlow reading122JSON Body_Data_Site_P_Load
attr PowerFlow reading122Name power_load
attr PowerFlow reading122OExpr -$val/1000
attr PowerFlow reading123Format %.1f
attr PowerFlow reading123JSON Body_Data_Site_rel_Autonomy
attr PowerFlow reading123Name relativeAutonomy_p
attr PowerFlow reading124Format %.3f
attr PowerFlow reading124JSON Body_Data_Site_P_PV
attr PowerFlow reading124Name power_PV2
attr PowerFlow reading124OExpr $val/1000
attr PowerFlow room Energie

Ein weiteres Attribut regelt die gesamte Statusanzeige in einer Tabelle. Dazu muss eine externe Funktion PF_status(parameter) aufgerufen werden, im Beispiel dient der übergebene Parameter 'sf' dazu, den Rückgabewert im 'Statusformat' zu machen. Ein Beispiel für diese Perl-Funktion ist unten zu finden.

attr PowerFlow stateFormat {PF_status("sf")}

Es gibt ferner eine Vielzahl von userReadings, die in der Form

attr PowerFlow userReadings ...

angegeben werden. Die erste Gruppe von userReadings berechnet Energien aus Leistungsdaten

...
energy_load:power_load.* integral {ReadingsVal("$NAME","power_load",0)/3600},\
energy_battery:power_load.* integral {ReadingsVal("$NAME","power_battery",0)/3600},\
energy_battery_in:power_load.* integral {max(ReadingsVal("$NAME","power_battery",0),0)/3600},\
energy_battery_out:power_load.* integral {min(ReadingsVal("$NAME","power_battery",0),0)/3600},\
energy_PV2:power_load.* integral {ReadingsVal("$NAME","power_PV2",0)/3600},\
energy_eco:power_load.* integral {ReadingsVal("$NAME","power_eco",0)/3600},\
...

Die zweite Gruppe von userReadings berechnet Teil-Energieflüsse: "house" enthält als separat ausgewiesene Bestandteile "wallbox", "aircondition", "washer" ,"outdoor" als wesentliche Verbraucher. Dieser Teil des Devices muss an die eigenen Bedürfnisse angepasst werden.

...
power_house:power_load.* {sprintf("%.3f",ReadingsVal("$NAME","power_load",0)-ReadingsVal("Wally","power",0))},\
energy_house:power_load.*  {sprintf("%.3f",ReadingsVal("$NAME","energy_load",0)-ReadingsVal("Wally","energy_today",0))},\
power_wallbox:power_load.* {ReadingsNum("Wally","power",0)},\
energy_wallbox:energy_PV2.* {ReadingsNum("Wally","energy_today",0)},\
power_aircondition:power_load.* {ReadingsNum("A.Power","c0_power",0)},\
energy_aircondition:power_load.* {ReadingsNum("A.Power","statC0_energyDay",0)},\
power_washer:power_load.* {ReadingsNum("UG.Power1","power",0)},\
energy_washer:power_load.* {ReadingsNum("UG.Power1","statEnergyDay",0)},\
power_outdoor:power_load.* {ReadingsNum("A.Power","c1_power",0)},\
energy_outdoor:power_load.* {ReadingsNum("A.Power","statC1_energyDay",0)},\
...

Die dritte Gruppe von userReadings holt aus dem Zähler des Netzbetreibers die bezogene und hochgeladene Energie und stellt diese in zwei "Übersichtsreadings" ein. Das spart enorm viel Performance beim Logging.

...
energy_grid:energy_PV2.* {ReadingsVal("E2.consumption","statEnergyDay",0)},\
energy_upload:energy_PV2.* {ReadingsVal("E2.production","statEnergyDay",0)},\
energy_consumed:energy_PV2.* {-min(ReadingsVal("$NAME","energy_battery",0),0)+ReadingsVal("$NAME","energy_PV2",0)+ReadingsVal("$NAME","energy_grid",0)},\
energy_summary_in:energy_load.* {sprintf("%.3f %.3f %.3f",\
 -min(ReadingsVal("$NAME","energy_battery",0),0),ReadingsVal("$NAME","energy_PV2",0),ReadingsVal("$NAME","energy_grid",0))},\
energy_summary_out:energy_load.* {sprintf("%.3f %.3f %.3f %.3f",\
 max(ReadingsVal("$NAME","energy_battery",0),0),ReadingsVal("$NAME","energy_wallbox",0),ReadingsVal("$NAME","energy_house",0),ReadingsVal("$NAME","energy_upload",0))},\
power_summary_in:power_load.* {sprintf("%.3f %.3f %.3f",\
-min(ReadingsVal("$NAME","power_battery",0),0),ReadingsVal("$NAME","power_PV2",0),max(ReadingsVal("$NAME","power_grid",0),0))},\
power_summary_out:energy_load.* {sprintf("%.3f %.3f %.3f %.3f",\
 max(ReadingsVal("$NAME","power_battery",0),0),ReadingsVal("$NAME","power_wallbox",0),ReadingsVal("$NAME","power_house",0),-min(ReadingsVal("$NAME","power_grid",0),0))},\
...

Damit ist das Device komplett, es folgt die im Beispiel verwendete Statusfunktion

Tabelle für den Energiefluss

In der nachfolgenden Funktion tauchen verschiedene Dinge auf, die höchstwahrscheinlich nicht bei jedem Anwender vorhanden sind und deshalb angepasst werden müssen, z.B. eine zweite Solaranlage "PV1", ferner "wallbox", "aircondition", "washer" und "outdoor". Der an die Funktion übergebene Parameter "target" kann hier nur den Wert "sf"=Statusformat haben. In der Implementation des Autors kann auch noch eine Nachricht für die Sprachausgabe erzeugt werden, das wurde der Einfachheit halber weggelassen.

sub PF_status($){
 my ($target) = @_;
 my $str;
 
 my $hash = $defs{"PowerFlow"};
 
 my $ppv1=$hash->{READINGS}{"power_PV1"}{VAL};
 my $epv1=$hash->{READINGS}{"energy_PV1"}{VAL};
 my $ppv2=$hash->{READINGS}{"power_PV2"}{VAL};
 my $epv2=$hash->{READINGS}{"energy_PV2"}{VAL};
 
 my $pbat=$hash->{READINGS}{"power_battery"}{VAL};
 my $ebat=$hash->{READINGS}{"energy_battery"}{VAL};
 my $ebati=$hash->{READINGS}{"energy_battery_in"}{VAL};
 my $ebato=$hash->{READINGS}{"energy_battery_out"}{VAL};
 my $soc=$hash->{READINGS}{"battery_SOC"}{VAL};
 my $soce=$soc*10.24/100;
 
 my $peco=$hash->{READINGS}{"power_eco"}{VAL};
 my $eeco=$hash->{READINGS}{"energy_eco"}{VAL};
 my $phouse=$hash->{READINGS}{"power_house"}{VAL};
 my $ehouse=$hash->{READINGS}{"energy_house"}{VAL};

 #-- air condition
 my $pairc=$hash->{READINGS}{"power_aircondition"}{VAL};
 my $eairc=$hash->{READINGS}{"energy_aircondition"}{VAL};
 my $eairwc=$defs{'WZ.AC'}->{READINGS}{"energy_cooling_day"}{VAL};
 my $eairwh=$defs{'WZ.AC'}->{READINGS}{"energy_heating_day"}{VAL};
 my $eairsc=$defs{'SZ.AC'}->{READINGS}{"energy_cooling_day"}{VAL};
 my $eairsh=$defs{'SZ.AC'}->{READINGS}{"energy_heating_day"}{VAL};
 
 my $eairstring;
 if( ($eairwc+$eairsc) > 0){
   if( ($eairwh + $eairsh) <= 0){
     $eairstring = sprintf("cooling WZ %.1f, SZ %.1f",$eairwc,$eairsc);
   }else{
     $eairstring = "error cooling+heating";
   }
 }else{
   if( ($eairwh + $eairsh) <= 0){
     $eairstring = "off"
   }else{
     $eairstring = sprintf("heating WZ %.1f, SZ %.1f",$eairwh,$eairsh);
   }
 }
 
 #-- washer/dryer
 my $pwasher = $defs{'UG.Power1'}->{READINGS}{"power"}{VAL};
 my $ewasher = $defs{'UG.Power1'}->{READINGS}{"statEnergyDay"}{VAL};
 
 #-- outdoor
 my $poutdoor=$hash->{READINGS}{"power_outdoor"}{VAL};
 my $eoutdoor=$hash->{READINGS}{"energy_outdoor"}{VAL};
 
 my $pwallbox=$hash->{READINGS}{"power_wallbox"}{VAL};
 my $ewallbox=$hash->{READINGS}{"energy_wallbox"}{VAL};
 
 my $pgrid=$hash->{READINGS}{"power_grid"}{VAL};
 my $egrid=$hash->{READINGS}{"energy_grid"}{VAL};
 my $eupl=$hash->{READINGS}{"energy_upload"}{VAL};
 
 my $ra=$hash->{READINGS}{"relativeAutonomy_p"}{VAL};
 my $sc=$hash->{READINGS}{"selfConsumption_p"}{VAL};
 my $rae=$hash->{READINGS}{"relativeAutonomy_e"}{VAL};
 my $sce=$hash->{READINGS}{"selfConsumption_e"}{VAL};
 
 my $teco=$epv2-$ebat-$eeco;
 my $tgrid=$egrid-$eupl+$epv2-$ebat-$ehouse-$ewallbox;
 
 if( $target eq "sf"){

$str="

". "". "". "". "". "". "". "". "". "". "". "". "". "". ".
Power [kW]Energy [kWh]
PV1%.3f%.3f
PV2%.3f%.3f
Battery%.3f%.3fSOC %.1f %% = %.2f kWh
%.3f
Eco%.3f%.3ftest %.2f
House%.3f%.3f
%s

Aircondition%.3f%.3f
Washer/Dryer%.3f%.3f
Outdoor%.3f%.3f
Wallbox%.3f%.3f
Grid%.3f%.3ftest %.2f
%.3f
Autonomy%d %%%d %%
Self Consumption%d %%%d %%

";

  return sprintf($str,$ppv1,$epv1,$ppv2,$epv2,
   $pbat,$ebati,$soc,$soce,$ebato,
   $peco,$eeco,$teco,$phouse,$ehouse,$eairstring,
  $pairc,$eairc,$pwasher,$ewasher,$poutdoor,$eoutdoor,$pwallbox,$ewallbox,
   $pgrid,$egrid,$tgrid,-$eupl,$ra,$rae,$sc,$sce);
 }else{ 
   Log 1,"[PF_status] unknown target $target";
 }     
}

sg6000-Device für den solaren Ertrag

Die separaten Daten für den solaren Ertrag werden nur alle 60 Sekunden geholt

defmod sg6000 HTTPMOD <Adresse des Wechselrichters>/solar_api/v1/GetInverterRealtimeData.cgi?Scope=Device&DataCollection=CommonInverterData 60
attr sg6000 event-on-update-reading energy.*,statEnergy.*,power.*,EOD_report,EOM_report
attr sg6000 group solarGenerator
attr sg6000 reading100JSON Body_Data_DeviceStatus_StatusCode
attr sg6000 reading100Name Device_StatusCode
attr sg6000 reading102Format %.3f
attr sg6000 reading102JSON Body_Data_UDC_2_Value
attr sg6000 reading102Name Udc2
attr sg6000 reading105JSON Head_RequestArguments_Scope
attr sg6000 reading105Name Scope
attr sg6000 reading109Format %.3f
attr sg6000 reading109JSON Body_Data_PAC_Value
attr sg6000 reading109Name power_ac
attr sg6000 reading109OExpr $val/1000
attr sg6000 reading111JSON Head_Timestamp
attr sg6000 reading111Name Head_Timestamp
attr sg6000 reading113Format %.3f
attr sg6000 reading113JSON Body_Data_UAC_Value
attr sg6000 reading113Name Uac
attr sg6000 reading115Format %.3f
attr sg6000 reading115JSON Body_Data_UDC_Value
attr sg6000 reading115Name Udc1
attr sg6000 reading117Format %.3f
attr sg6000 reading117JSON Body_Data_IDC_Value 
attr sg6000 reading117Name Idc1
attr sg6000 reading118Format %.3f
attr sg6000 reading118JSON Body_Data_IDC_2_Value
attr sg6000 reading118Name Idc2
attr sg6000 reading119JSON Head_Status_UserMessage
attr sg6000 reading119Name UserMessage
attr sg6000 reading120Format %.3f
attr sg6000 reading120JSON Body_Data_IAC_Value
attr sg6000 reading120Name Iac
attr sg6000 reading122Format %.3f
attr sg6000 reading122JSON Body_Data_SAC_Value
attr sg6000 reading122Name Sac
attr sg6000 reading124JSON Body_Data_DeviceStatus_InverterState
attr sg6000 reading124Name InverterState
attr sg6000 reading127Format %.3f
attr sg6000 reading127JSON Body_Data_TOTAL_ENERGY_Value
attr sg6000 reading127Name energy_total_ac
attr sg6000 reading127OExpr $val/1000
attr sg6000 reading130JSON Body_Data_DeviceStatus_ErrorCode
attr sg6000 reading130Name Device_ErrorCode
attr sg6000 reading131JSON Head_RequestArguments_DeviceId
attr sg6000 reading131Name DeviceId
attr sg6000 reading134JSON Head_RequestArguments_DataCollection
attr sg6000 reading134Name DataCollection
attr sg6000 reading138JSON Head_Status_Reason
attr sg6000 reading138Name Head_Status_Reason
attr sg6000 reading139JSON Head_Status_Code
attr sg6000 reading139Name Status_Code
attr sg6000 reading140Format %.3f
attr sg6000 reading140JSON Body_Data_FAC_Value
attr sg6000 reading140Name Fac
attr sg6000 room Solar
attr sg6000 stateFormat power kW (power_ac kW AC)
attr sg6000 useSetExtensions 0
attr sg6000 userReadings power:power_ac.* {sprintf("%.3f",ReadingsVal("PowerFlow","power_PV2",0))},\
 energy:power_ac.* {sprintf("%.3f",ReadingsVal("PowerFlow","energy_PV2",0))},\
 energy_month:none {},\
 energy_year:none {},\
 EOD_report:none {},\
 EOM_report:none {}

Battery_WRAPI-Device

Das Fronius-API erlaubt auch den Zugriff auf Daten des Speichers, diese werden nur alle 5 Minuten benötigt.

defmod Battery_WRAPI HTTPMOD <Adresse des Wechselrichters>/solar_api/v1/GetStorageRealtimeData.cgi 300
attr Battery_WRAPI event-on-change-reading Body_Data_0_Controller_StateOfCharge_Relative
attr Battery_WRAPI group energyStorage
attr Battery_WRAPI reading100JSON Head_Status_UserMessage
attr Battery_WRAPI reading100Name Head_Status_UserMessage
attr Battery_WRAPI reading101JSON Body_Data_0_Controller_StateOfCharge_Relative
attr Battery_WRAPI reading101Name Body_Data_0_Controller_StateOfCharge_Relative
attr Battery_WRAPI reading102JSON Body_Data_0_Controller_Current_DC
attr Battery_WRAPI reading102Name Body_Data_0_Controller_Current_DC
attr Battery_WRAPI reading103JSON Head_Timestamp
attr Battery_WRAPI reading103Name Head_Timestamp
attr Battery_WRAPI reading104JSON Body_Data_0_Controller_Temperature_Cell
attr Battery_WRAPI reading104Name Body_Data_0_Controller_Temperature_Cell
attr Battery_WRAPI reading105JSON Body_Data_0_Controller_DesignedCapacity
attr Battery_WRAPI reading105Name Body_Data_0_Controller_DesignedCapacity
attr Battery_WRAPI reading106JSON Head_Status_Reason
attr Battery_WRAPI reading106Name Head_Status_Reason
attr Battery_WRAPI reading107JSON Head_Status_Code
attr Battery_WRAPI reading107Name Head_Status_Code
attr Battery_WRAPI reading108JSON Body_Data_0_Controller_Details_Manufacturer
attr Battery_WRAPI reading108Name Body_Data_0_Controller_Details_Manufacturer
attr Battery_WRAPI reading109JSON Body_Data_0_Controller_Capacity_Maximum
attr Battery_WRAPI reading109Name Body_Data_0_Controller_Capacity_Maximum
attr Battery_WRAPI reading110JSON Body_Data_0_Controller_Enable
attr Battery_WRAPI reading110Name Body_Data_0_Controller_Enable
attr Battery_WRAPI reading111JSON Body_Data_0_Controller_Status_BatteryCell
attr Battery_WRAPI reading111Name Body_Data_0_Controller_Status_BatteryCell
attr Battery_WRAPI reading112JSON Body_Data_0_Controller_Voltage_DC
attr Battery_WRAPI reading112Name Body_Data_0_Controller_Voltage_DC
attr Battery_WRAPI reading113JSON Head_RequestArguments_Scope
attr Battery_WRAPI reading113Name Head_RequestArguments_Scope
attr Battery_WRAPI reading114JSON Body_Data_0_Controller_Details_Model
attr Battery_WRAPI reading114Name Body_Data_0_Controller_Details_Model
attr Battery_WRAPI reading115JSON Body_Data_0_Controller_Details_Serial
attr Battery_WRAPI reading115Name Body_Data_0_Controller_Details_Serial
attr Battery_WRAPI reading116JSON Body_Data_0_Controller_TimeStamp
attr Battery_WRAPI reading116Name Body_Data_0_Controller_TimeStamp
attr Battery_WRAPI room Energie
attr Battery_WRAPI sortby 5

attr Battery_WRAPI stateFormat

Body_Data_0_Controller_StateOfCharge_Relative % (Body_Data_0_Controller_Temperature_Cell °C, Body_Data_0_Controller_Voltage_DC V)