In diesem Thread soll es allein um die Weitergabe von Praxisbeispielen die im SF-Modul (76_SolarForecast.pm) entweder ohne jegliche Anpassung oder auch mit weiteren (externen) Anpassungen bei der Einrichtung der eigenen Konfiguration übernommen werden könnten.
==>> Alles unter Ausschluss jeglicher Gewähr auf die Funktion der jeweiligen Codefragmente ! O:-)
Bitte möglichst jeweils die SF-Version dazu schreiben unter der es eingesetzt wird/wurde.
Manchmal müssen (leider) bekanntlich Anpassungen an den Beispielen und Attributen bei neueren Versionen vorgenommen werden, da Funktionen/Attribute umbenannt, zusammengefaßt, gelöscht oder erweitert werden/wurden. ;)
Anfallende Probleme zu den Beispielen und deren Klärung sollten möglichst nicht hier, sondern im normalen SF-Modul-Faden behandelt werden.
Der Grund dazu ist allein das die hier im Thread zur Verfügung gestellten Beispiele nicht in den allgemeinen Diskussionen untergehen sollen.
Link zum <--SF-Modul--> (https://forum.fhem.de/index.php?topic=137058.new#new) und dessen Diskussion
Link zum <--FHEM-Wiki des SF-Modules--> (https://wiki.fhem.de/wiki/SolarForecast_-_Solare_Prognose_(PV_Erzeugung)_und_Verbrauchersteuerung)
Gruß
300P
Hier das erste Beispiel
Nr : 001
SF-Version : 76_SolarForecast.pm:v1.52.5-s29997/2025-05-25
Bereich : ctrlUserExitFn
Netzdienliches Laden innerhalb von Solarforecast mit SMA-Batterie-Wechselrichtern:
Für 1 BWR ist der Code etwas tiefer auch dabei
{
# BEGIN ######## Netzdienliches Laden ################
my $dt = timestringsFromOffset (time, 0);
my $hour = $dt->{hour}; # aktuelle Stunde in 24h format (00-23)
my $timestart = 9; # ab welcher Uhrzeit soll es sein ===>> Wert evtl. anpassen
my $timeend = 15; # bis wieviel Uhr soll es sein ===>> Wert evtl. anpassen
if (int $hour >= $timestart && int $hour < $timeend ) {
# Nur zwischen xy:00 bis max. xy:59 (!!!! $timeend -1 Stunde !!!!) Uhr
my $curpowerbatin01 = ReadingsNum ($name, 'Current_PowerBatIn_01', 0);
my $curpowerbatin02 = ReadingsNum ($name, 'Current_PowerBatIn_02', 0);
my $curpowerbatout01 = ReadingsNum ($name, 'Current_PowerBatOut_01', 0);
my $curpowerbatout02 = ReadingsNum ($name, 'Current_PowerBatOut_02', 0);
my $curbatcharge01 = CurrentVal ($name, 'Current_BatCharge_01', 0); # SoC Bat01 aktuell
my $curbatcharge02 = CurrentVal ($name, 'Current_BatCharge_02', 0); # SoC Bat02 aktuell
my $currentsurplus = (ReadingsNum ($name, 'Current_Surplus', 0) - 50); # etwas abziehen zwecks weniger als Puffer
my $batchargerequest01 = ReadingsNum ($name, 'Battery_ChargeRequest_01', 0); # Batterie01 muss geladen werden ?
my $batchargerequest02 = ReadingsNum ($name, 'Battery_ChargeRequest_02', 0); # Batterie02 muss geladen werden ?
my $gridfriendlychargebat01 = ReadingsNum ($name, 'Battery_ChargeRecommended_01', 0); # Batterie01 recommended ?
my $gridfriendlychargebat02 = ReadingsNum ($name, 'Battery_ChargeRecommended_02', 0); # Batterie02 recommended ?
my $curbatchargemin01= 30; # erst ab Mindes-BAT01-SOC XY netzdienlich ===>> Wert evtl. anpassen 0..30
my $curbatchargemin02= 30; # erst ab Mindes-BAT02-SOC XY netzdienlich ===>> Wert evtl. anpassen 0..30
my $bwr01 = "MB_SBS25"; # Name des 1ten BWR-Modbus-Device ===>> Wert evtl. anpassen
my $bwr02 = "MB_SBS25_2"; # Name des 2ten BWR-Modbus-Device ===>> Wert evtl. anpassen
if (int $gridfriendlychargebat01 eq 0 || $gridfriendlychargebat02 eq 0) {
# Nur wenn die Batterieladung01 oder 02 SF-Systemseitig nicht empfohlen wird dann
Log3 ($name, 5, qq{$name - userFn -> Gridfriendlyloadrecommended Battery01 $gridfriendlychargebat01 - Battery02 $gridfriendlychargebat02 });
my $curbatcharge01 = CurrentVal ($name, 'Current_BatCharge_01', 0); # SoC Bat01 aktuell
my $curbatcharge02 = CurrentVal ($name, 'Current_BatCharge_02', 0); # SoC Bat02 aktuell
if (int $gridfriendlychargebat01 eq 0 && $batchargerequest01 eq 0 && $curbatcharge01 > $curbatchargemin01) {
# wenn bei Bat01 = 0 -beides- und SOC groß genug ->>dann unterbinden Laden Bat01
Log3 ($name, 5, qq{$name - userFn -> Gridfriendlyloadrecommended only Battery01 Reading = $gridfriendlychargebat01 });
fhem("set $bwr01 Set_Leistung_W $currentsurplus"); # Watt Netzleistung am Übergabemesspunkt (WR1)
fhem("set $bwr01 Set_Aktiv 802"); # 802 - Steuerung aktiv (WR1)
Log3 ($name, 3, qq{$name - userFn -> Batteryload $bwr01 => activ - BatIn01 $curpowerbatin01 BatOut01 $curpowerbatout01 });
}
else {
if (int $gridfriendlychargebat01 eq 1 || $batchargerequest01 eq 1 || (($curbatcharge01 - 3) <= $curbatchargemin01)) {
# wenn bei Bat01 = beides oder nur eins auf 1 steht ---dann bitte jetzt Steuerung aus / kein Laden unterbinden Bat01
Log3 ($name, 5, qq{$name - userFn -> Gridfriendlyloadrecommended =>> Battery01 Reading = $gridfriendlychargebat01 and BatChargerequest =>> Battery01 Reading = $batchargerequest01 });
fhem("set $bwr01 Set_Leistung_W 0"); # Watt Netzleistung am Übergabemesspunkt (WR1)
fhem("set $bwr01 Set_Aktiv 803"); # 803 - Steuerung nicht aktiv (WR1)
Log3 ($name, 3, qq{$name - userFn -> Batteryload $bwr01 => not activ - BatIn01 $curpowerbatin01 BatOut01 $curpowerbatout01 });
}
}
if (int $gridfriendlychargebat02 eq 0 && $batchargerequest02 eq 0 && $curbatcharge02 > $curbatchargemin02) {
# wenn bei Bat02 = 0 -beides- und SOC groß genug --->>dann unterbinden Laden Bat02
Log3 ($name, 5, qq{$name - userFn -> Gridfriendlyloadrecommended only Battery02 Reading = $gridfriendlychargebat02 });
fhem("set $bwr02 Set_Leistung_W $currentsurplus"); # Watt Netzleistung am Übergabemesspunkt (WR2)
fhem("set $bwr02 Set_Aktiv 802"); # 802 - Steuerung aktiv (WR2)
Log3 ($name, 3, qq{$name - userFn -> Batteryload $bwr02 => not activ - BatIn02 $curpowerbatin02 BatOut02 $curpowerbatout02 });
}
else {
if (int $gridfriendlychargebat02 eq 1 || int $batchargerequest02 eq 1 || (($curbatcharge02 - 3) <= $curbatchargemin02)) {
# wenn bei Bat02 = beides oder nur eins auf 1 steht ---dann bitte jetzt Steuerung aus / kein Laden unterbinden Bat02
Log3 ($name, 5, qq{$name - userFn -> Gridfriendlyloadrecommended =>> Battery02 Reading = $gridfriendlychargebat02 and BatChargerequest =>> Battery02 Reading = $batchargerequest02 });
fhem("set $bwr02 Set_Leistung_W 0"); # Watt Netzleistung am Übergabemesspunkt (WR2)
fhem("set $bwr02 Set_Aktiv 803"); # 803 - Steuerung nicht aktiv (WR2)
Log3 ($name, 3, qq{$name - userFn -> Batteryload $bwr02 => not activ - BatIn02 $curpowerbatin02 BatOut02 $curpowerbatout02 });
}
}
}
}
# ENDE ######## Netzdienliches Laden ################
}
Hier nur für 1 BWR:
{
# BEGIN ######## Netzdienliches Laden ################
my $dt = timestringsFromOffset (time, 0);
my $hour = $dt->{hour}; # aktuelle Stunde in 24h format (00-23)
my $timestart = 9; # ab welcher Uhrzeit soll es sein ===>> Wert evtl. anpassen
my $timeend = 15; # bis wieviel Uhr soll es sein ===>> Wert evtl. anpassen
if (int $hour >= $timestart && int $hour < $timeend ) {
# Nur zwischen xy:00 bis max. xy:59 (!!!! $timeend -1 Stunde !!!!) Uhr
my $curpowerbatin01 = ReadingsNum ($name, 'Current_PowerBatIn_01', 0);
my $curpowerbatout01 = ReadingsNum ($name, 'Current_PowerBatOut_01', 0);
my $curbatcharge01 = CurrentVal ($name, 'Current_BatCharge_01', 0); # SoC Bat01 aktuell
my $currentsurplus = (ReadingsNum ($name, 'Current_Surplus', 0) - 50); # etwas abziehen zwecks weniger als Puffer
my $batchargerequest01 = ReadingsNum ($name, 'Battery_ChargeRequest_01', 0); # Batterie01 muss geladen werden ?
my $gridfriendlychargebat01 = ReadingsNum ($name, 'Battery_ChargeRecommended_01', 0); # Batterie01 recommended ?
my $curbatchargemin01= 30; # erst ab Mindes-BAT01-SOC XY netzdienlich ===>> Wert evtl. anpassen 0..30
my $bwr01 = "MB_SBS25"; # Name des 1ten BWR-Modbus-Device ===>> Wert evtl. anpassen
if (int $gridfriendlychargebat01 eq 0) {
# Nur wenn die Batterieladung01 SF-Systemseitig nicht empfohlen wird dann
Log3 ($name, 5, qq{$name - userFn -> Gridfriendlyloadrecommended Battery01 $gridfriendlychargebat01 });
my $curbatcharge01 = CurrentVal ($name, 'Current_BatCharge_01', 0); # SoC Bat01 aktuell
if (int $gridfriendlychargebat01 eq 0 && $batchargerequest01 eq 0 && $curbatcharge01 > $curbatchargemin01) {
# wenn bei Bat01 = 0 -beides- und SOC groß genug ->>dann unterbinden Laden Bat01
Log3 ($name, 5, qq{$name - userFn -> Gridfriendlyloadrecommended only Battery01 Reading = $gridfriendlychargebat01 });
fhem("set $bwr01 Set_Leistung_W $currentsurplus"); # Watt Netzleistung am Übergabemesspunkt (WR1)
fhem("set $bwr01 Set_Aktiv 802"); # 802 - Steuerung aktiv (WR1)
Log3 ($name, 3, qq{$name - userFn -> Batteryload $bwr01 => activ - BatIn01 $curpowerbatin01 BatOut01 $curpowerbatout01 });
}
else {
if (int $gridfriendlychargebat01 eq 1 || $batchargerequest01 eq 1 || (($curbatcharge01 - 3) <= $curbatchargemin01)) {
# wenn bei Bat01 = beides oder nur eins auf 1 steht ---dann bitte jetzt Steuerung aus / kein Laden unterbinden Bat01
Log3 ($name, 5, qq{$name - userFn -> Gridfriendlyloadrecommended =>> Battery01 Reading = $gridfriendlychargebat01 and BatChargerequest =>> Battery01 Reading = $batchargerequest01 });
fhem("set $bwr01 Set_Leistung_W 0"); # Watt Netzleistung am Übergabemesspunkt (WR1)
fhem("set $bwr01 Set_Aktiv 803"); # 803 - Steuerung nicht aktiv (WR1)
Log3 ($name, 3, qq{$name - userFn -> Batteryload $bwr01 => not activ - BatIn01 $curpowerbatin01 BatOut01 $curpowerbatout01 });
}
}
}
}
# ENDE ######## Netzdienliches Laden ################
}
ACHTUNG:
Voraussetzung ist das die Modbus-Device bereits existieren und sauber dazu passend eingerichtet sind !
obj-h40149-expr $val
obj-h40149-len 2
obj-h40149-name SBS25_Set_Leistung_W
obj-h40149-poll once
obj-h40149-reading Set_Leistung_W
obj-h40149-set 1
obj-h40149-unpack I>
obj-h40151-expr $val
obj-h40151-name SBS25_Set_Aktiv
obj-h40151-poll once
obj-h40151-reading Set_Aktiv
obj-h40151-set 1
Gruß
300P
Beispiel Nr : 002
SF-Version : 76_SolarForecast.pm:v1.52.5-s29997/2025-05-25
Bereich : ConsumerXX / ctrlUserExitFn
Netzdienliche Brauchwassererwärmung (WW-Erhitzung) am Tag per consumer06
und zusätzliche Nutzung der "überschüssigen" Rest-Batterieladung in der Nacht, jedoch unter bestimmten Bedingungen.
{
# BEGIN ######## WP-Heater in der Nacht mit Restladung der Batterien ################
my $dt = timestringsFromOffset (time, 0);
my $hour = $dt->{hour}; # aktuelle Stunde in 24h format (00-23)
my $timestart = 1; # ab welcher Uhrzeit soll es sein. ===>> Wert evtl. anpassen
my $timeend = 8; # bis wieviel Uhr soll es sein. ===>> Wert evtl. anpassen
if (int $hour >= $timestart && int $hour < $timeend ) {
# Nur zwischen xy:00 bis max. xy:59 (!!!! $timeend -1 Stunde !!!!) Uhr
my $minsocbat = 20;
# nur bis mindesten XY SoC aller Batterien. ===>> Wert evtl. anpassen
# Heater-Name bzw. consumerXX anpassen - hier ist consumer06 der "Heater"
my $heater = (split " ", AttrVal ($name, 'consumer06', ''))[0];
my $soctotal = CurrentVal ($name, 'batsoctotal', 0); # SoC über alle Bat als Durchschnitt
my $pvtot = ReadingsNum ($name, 'RestOfDayPVforecast', 0); # Restliche PV Prognose total heute in Wh
my $caps = CurrentVal ($name, 'batcapsum', 0); # Summe installierte Bat Wh
my $need = $caps - ($soctotal/100 * $caps); # benötigte Ladeenergie Bat bis 100% Ladung
Log3 ($name, 5, qq{$name - userFn -> WWHeater Reading ist = $heater - aktuelle Stunde = $hour aus $dt erzeugt });
Log3 ($name, 5, qq{$name - userFn -> StartZeit von = $timestart bis EndeZeit = $timeend und mindestens ein SoC von $minsocbat % muss in den vorhandenen Batterien gegeben sein });
Log3 ($name, 5, qq{$name - userFn -> Der PV-Tages-Restertrag betraegt $pvtot Wh ???mehr??? als die offene und noch zu ladende Batteriekapazitaet von $need Wh});
if ($soctotal >= ($minsocbat - 2) && $pvtot > $need) {
# Zwischen xy:00 bis max. xy:59 (!!!! $timeend -1 Stunde !!!!) Uhr
# und % SoC-Vorgabe erfüllt
# und der PV-Tages-Rest-Ertrag > 100% Batteriekapazitaet
# send "on"-Kommando an Heater Device if(ne "on");
if (ReadingsVal($heater,"state","") ne "on") {
fhem ("set $heater on");
Log3 ($name, 3, qq{$name - userFn -> $name Consumer $heater in $name set to --on--});
}
}
else {
if (($soctotal < $minsocbat) || ($pvtot <= $need)) {
# Wenn die (SoC-Vorgabe unterschritten !!!!! oder !!!! derTages-Rest-Ertrag < 100% Batteriekapazitaet)
# danach sollte der Heater-ConsumerXY wieder nach "seinen" Regeln steuern...
# send "off"-Kommando an Heater Device if(ne "off");
if (ReadingsVal($heater,"state","") ne "off") {
fhem ("set $heater off");
Log3 ($name, 3, qq{$name - userFn -> $name Consumer $heater in $name set to --off--});
}
}
}
}
# ENDE ######## WP-Heater ################
}
Hier noch der consumer06, der im SF-Modul dazu bei mir existiert. ;)
attr Forecast consumer06 FBDECT_fbahahttp_FF_FF_FF_FF_FF \
type=heater \
power=2170 \
mode=can \
icon=sani_buffer_electric_heater_side@orange \
mintime=SunPath \
on=on \
off=off \
asynchron=1\
notbefore=08:00 \
notafter=19:00 \
locktime=300:300 \
pcurr=power:W \
etotal=energy:Wh\
surpmeth=10 \
interruptable=1\
swstate=state:.*on.*:.*off.*
Gruß
300P
Beispiel Nr : 003
SF-Version : 76_SolarForecast.pm:v1.52.5-s29997/2025-05-25
Bereich : graphicHeaderOwnspec
Ausführliche Anzeige im Header mit Istwerten / Vorhersagewerten / Verbrauchswerten / Batteriewerten / Parameterabänderungsmöglichkeit
attr Forecast graphicHeaderOwnspec #aktuelle ;Werte\
Netz ;aktuell:Current_GridConsumption\
Einspeisung ;aktuell:Current_GridFeedIn\
Netz ;Heute ;bis ;jetzt:special_todayGridConsumption\
Einspeisung ;Heute ;bis ;jetzt:special_todayGridFeedIn\
#Verbrauch\
Heute ;bis ;jetzt:special_todayConsumption\
Heute ;erwartet:special_todayConsumptionForecastDay\
Heute ;bis ;Sonnenuntergang:special_todayConForecastTillSunset\
Heute ;bis ;Sonnenaufgang ;:special_conForecastTillNextSunrise\
#PV\
Heute ;bis ;jetzt:Today_PVreal\
Heute ;erwartet:Today_PVforecast\
Morgen ;erwartet:Tomorrow_PVforecast\
Übermorgen ;erwartet:special_dayAfterTomorrowPVforecast\
#Batterie ;Gesamt\
Ladung ;aktuell:special_BatPowerIn_Sum\
Entladung ;aktuell:special_BatPowerOut_Sum\
Ladung ;heute:special_todayBatInSum\
Entladung ;heute:special_todayBatOutSum\
#Batterie01 ;| ;02\
Bat01-Ladeanforderung:Battery_ChargeRequest_01\
Bat01-Ladung ;empfohlen:Battery_ChargeRecommended_01\
Bat02-Ladeanforderung:Battery_ChargeRequest_02\
Bat02-Ladung ;empfohlen:Battery_ChargeRecommended_02\
#\
:\
MaxPVForecastTime:Today_MaxPVforecastTime\
:\
:\
#\
Bat01-Ladung ;heute:special_todayBatIn_01\
Bat01-Entladung ;heute:special_todayBatOut_01\
Bat02-Ladung ;heute:special_todayBatIn_02\
Bat02-Entladung ;heute:special_todayBatOut_02\
#\
Bat01-Ladeleistung ;aktuell:Current_PowerBatIn_01\
Bat01-Entladeleistung ;aktuell:Current_PowerBatOut_01\
Bat02-Ladeleistung ;aktuell:Current_PowerBatIn_02\
Bat02-Entladeleistung ;aktuell:Current_PowerBatOut_02\
#\
Bat01-Ladestatus ;aktuell:Current_BatCharge_01\
Bat01-Restapazität ;aktuell:Current_CapBat_01\
Bat02-Ladestatus ;aktuell:Current_BatCharge_02\
Bat02-Restapazität ;aktuell:Current_CapBat_02\
#Settings\
Autokorrektur:pvCorrectionFactor_Auto\
Wetter:graphicShowWeather\
History:graphicHistoryHour\
ShowNight:graphicShowNight\
#\
Beam1:graphicBeam1Content\
Beam2:graphicBeam2Content\
:\
:\
#\
Beam3:graphicBeam3Content\
Beam4:graphicBeam4Content\
:\
:\
#\
Beam5:graphicBeam5Content\
Beam6:graphicBeam6Content\
Debug:ctrlDebug\
Beispiel Nr : 004
SF-Version : 76_SolarForecast.pm:v1.52.5-s29997/2025-05-25
Bereich : setupBatteryDevXX / setupInverterDevXX
Beispielsetup
setupBatteryDevXY : SMA-BWR SBS2.5 und SMA-BWR SBS3.7
attr Forecast setupBatteryDev01 SBS37 pin=-pout:kW pout=total_pac:kW pinmax=3680 poutmax=3680 intotal=bat_loadtotal:kWh outtotal=bat_unloadtotal:kWh charge=chargestatus cap=bat_residual_cap:Wh show=3:top icon=@yellow:@green:@red:@white asynchron=0
attr Forecast setupBatteryDev02 SBS25_2 pin=-pout:kW pout=total_pac:kW pinmax=2500 poutmax=2500 intotal=bat_loadtotal:kWh outtotal=bat_unloadtotal:kWh charge=chargestatus cap=bat_residual_cap:Wh show=3:top icon=@yellow:@green:@red:@blue asynchron=0
In den BWR gibt es je ein Userreading
attr SBSXY userReadings bat_residual_cap {ReadingsNum($name,"bat_rated_capacity",0)*ReadingsNum($name,"bat_capacity",0)*10}
Beispielsetup
setupInverterDevXY : SMA-WR SB2.5 (1 String) --- SMA-WR SB3.0 (2 String) und SMA-WR SB4.0 (2 String)
(die angegebenen Strings müssen aber real im SF-Modul angelegt sein)
attr Forecast setupInverterDev01 SB25 etotal=etotal:kWh asynchron=0 pvIn=string_sum_pdc:kW pvOut=total_pac:kW limit=70 strings=GarageSE capacity=2500
attr Forecast setupInverterDev02 SB30 etotal=etotal:kWh asynchron=0 pvIn=string_sum_pdc:kW pvOut=total_pac:kW limit=70 strings=GarageNW,HausNW capacity=3000
attr Forecast setupInverterDev03 SB40 etotal=etotal:kWh asynchron=0 pvIn=string_sum_pdc:kW pvOut=total_pac:kW limit=70 strings=HausSE1,HausSE2,HausSW capacity=4000