diff --git a/fhem/contrib/DS_Starter/76_SolarForecast.pm b/fhem/contrib/DS_Starter/76_SolarForecast.pm index 9fe2d9b06..44c66a76e 100644 --- a/fhem/contrib/DS_Starter/76_SolarForecast.pm +++ b/fhem/contrib/DS_Starter/76_SolarForecast.pm @@ -160,8 +160,9 @@ BEGIN { # Versions History intern my %vNotesIntern = ( - "1.58.6" => "25.09.2025 __batChargeMgmt code changed, new sub ___batChargeSaveResults, remove reading Battery_ChargeRecommended_XX ". - "_calcReadingsTomorrowPVFc: bugfix generating readings of tomorrow ", + "1.58.6" => "01.10.2025 __batChargeMgmt code changed, new sub ___batChargeSaveResults, remove reading Battery_ChargeRecommended_XX ". + "_calcReadingsTomorrowPVFc: bugfix generating readings of tomorrow ". + "__batChargeOptTargetPower: complete rework, Attr ctrlBatSocManagementXX new key 'loadStrategy' ", "1.58.5" => "24.09.2025 __batChargeOptTargetPower: fix if battery load control is deactivated ", "1.58.4" => "23.09.2025 __batChargeOptTargetPower: user a better surplus value, excess based on average removed & some other code optimization ", "1.58.3" => "17.09.2025 __batChargeOptTargetPower: minor code change, consider bpinmax & lcintime ", @@ -1566,10 +1567,15 @@ my %hfspvh = ( $hfspvh{'batprogsoc'.$bn}{validkey} = undef; $hfspvh{'batprogsoc'.$bn}{fpar} = undef; - $hfspvh{'lcintimebat'.$bn}{fn} = \&_storeVal; # Ladesteurung der Batterie In Time, d.h. war sie aktiv? (1 - Ja, 0 - Nein) + $hfspvh{'lcintimebat'.$bn}{fn} = \&_storeVal; # Ladesteuerung der Batterie In Time, d.h. war sie aktiv? (1 - Ja, 0 - Nein) $hfspvh{'lcintimebat'.$bn}{storname} = 'lcintimebat'.$bn; $hfspvh{'lcintimebat'.$bn}{validkey} = undef; $hfspvh{'lcintimebat'.$bn}{fpar} = undef; + + $hfspvh{'strategybat'.$bn}{fn} = \&_storeVal; # Ladestrategie der Batterie + $hfspvh{'strategybat'.$bn}{storname} = 'strategybat'.$bn; + $hfspvh{'strategybat'.$bn}{validkey} = undef; + $hfspvh{'strategybat'.$bn}{fpar} = undef; $hfspvh{'batmaxsoc'.$bn}{fn} = \&_storeVal; # max. erreichter SOC des Tages $hfspvh{'batmaxsoc'.$bn}{storname} = 'batmaxsoc'.$bn; @@ -7519,6 +7525,7 @@ sub _attrBatSocManagement { ## no critic "not used" careCycle => { comp => '\d+', must => 0, act => 0 }, loadAbort => { comp => '(?:100|[1-9]?[0-9]):\d+(?::(?:100|[1-9]?[0-9]))?', must => 0, act => 0 }, safetyMargin => { comp => '(?:100|[1-9]?\d)(?::(?:100|[1-9]?\d))?', must => 0, act => 0 }, + loadStrategy => { comp => '(loadRelease|optPower)', must => 0, act => 0 }, }; my ($a, $h) = parseParams ($aVal); @@ -11532,14 +11539,15 @@ sub __parseAttrBatSoc { ($urMargin, $otpMargin) = split ':', $ph->{safetyMargin} if(defined $ph->{safetyMargin}); my $parsed = { - lowSoc => $ph->{lowSoc}, - upSoc => $ph->{upSoC}, - maxSoc => $ph->{maxSoC} // MAXSOCDEF, # optional (default: MAXSOCDEF) - careCycle => $ph->{careCycle} // CARECYCLEDEF, # Ladungszyklus (Maintenance) für maxSoC in Tagen - lcslot => $ph->{lcSlot}, - loadAbort => $ph->{loadAbort}, - urMargin => $urMargin, - otpMargin => $otpMargin, + lowSoc => $ph->{lowSoc}, + upSoc => $ph->{upSoC}, + maxSoc => $ph->{maxSoC} // MAXSOCDEF, # optional (default: MAXSOCDEF) + careCycle => $ph->{careCycle} // CARECYCLEDEF, # Ladungszyklus (Maintenance) für maxSoC in Tagen + lcslot => $ph->{lcSlot}, + loadAbort => $ph->{loadAbort}, + loadStrategy => $ph->{loadStrategy}, + urMargin => $urMargin, + otpMargin => $otpMargin, }; return $parsed; @@ -11603,7 +11611,8 @@ sub _batChargeMgmt { my $hsurp = {}; # Hashreferenz Überschuß my $hsoc = {}; # Hashreferenz my $values = {}; # Hashreferenz - + my $progsoc; + ## Inverter Limits ermitteln ############################## for my $in (1..MAXINVERTER) { @@ -11647,22 +11656,25 @@ sub _batChargeMgmt { my $batoptsoc = ReadingsNum ($name, 'Battery_OptimumTargetSoC_'.$bn, 0); # aktueller optimierter SoC my $confcss = CurrentVal ($name, 'tdConFcTillSunset', 0); # Verbrauchsprognose bis Sonnenuntergang my $csoc = BatteryVal ($name, $bn, 'bcharge', 0); # aktuelle Ladung in % + my $csocwh = BatteryVal ($name, $bn, 'bchargewh', 0); # aktuelle Ladung in Wh my $bpinmax = BatteryVal ($name, $bn, 'bpinmax', INFINITE); # max. mögliche Ladeleistung W my $bpoutmax = BatteryVal ($name, $bn, 'bpoutmax', INFINITE); # max. mögliche Entladeleistung W my $bpowerin = BatteryVal ($name, $bn, 'bpowerin', INFINITE); # aktuelle Ladeleistung W my $cgbt = AttrVal ($name, 'ctrlBatSocManagement'.$bn, undef); my $sf = __batCapShareFactor ($hash, $bn); # Anteilsfaktor der Batterie XX Kapazität an Gesamtkapazität + my $strategy = 'loadRelease'; # 'loadRelease' oder 'optPower' my $lowSoc = 0; my $loadAbort = ''; my ($lcslot, $urMargin, $otpMargin); if ($cgbt) { - my $parsed = __parseAttrBatSoc ($name, $cgbt); - $lowSoc = $parsed->{lowSoc} // 0; - $lcslot = $parsed->{lcslot}; - $loadAbort = $parsed->{loadAbort}; - $urMargin = $parsed->{urMargin}; - $otpMargin = $parsed->{otpMargin}; + my $parsed = __parseAttrBatSoc ($name, $cgbt); + $lowSoc = $parsed->{lowSoc} // 0; + $lcslot = $parsed->{lcslot}; + $loadAbort = $parsed->{loadAbort}; + $urMargin = $parsed->{urMargin}; + $otpMargin = $parsed->{otpMargin}; + $strategy = $parsed->{loadStrategy} // $strategy; } my $margin = defined $urMargin ? $urMargin : SFTYMARGIN_50; # Sicherheitszuschlag (%) @@ -11717,6 +11729,8 @@ sub _batChargeMgmt { my $nhr = sprintf "%02d", $num; my $hod = NexthoursVal ($name, 'NextHour'.$nhr, 'hourofday', undef); my $nhstt = NexthoursVal ($name, 'NextHour'.$nhr, 'starttime', undef); + my $stt = (split /[-:]/, $nhstt)[2]; + $stt =~ s/\s/\//; next if(!defined ($hod) || !defined ($nhstt)); @@ -11725,8 +11739,8 @@ sub _batChargeMgmt { my $pvfc = NexthoursVal ($name, 'NextHour'.$nhr, 'pvfc', 0); if ($fd == 2 && $fh == 0) { - $tompvfc = CurrentVal ($name, 'dayAfterTomorrowPVfc', 0); # PV Prognose übernächster Tag - $tomconfc = CurrentVal ($name, 'dayAfterTomorrowConfc', 0); # Verbrauchsprognose übernächster Tag + $tompvfc = CurrentVal ($name, 'dayAfterTomorrowPVfc', 0); # PV Prognose übernächster Tag + $tomconfc = CurrentVal ($name, 'dayAfterTomorrowConfc', 0); # Verbrauchsprognose übernächster Tag } ## Zeitfenster für aktives Lademanagement anwenden @@ -11779,48 +11793,49 @@ sub _batChargeMgmt { if ( !$cgbt ) {$crel = 1} # Ladefreigabe wenn kein BatSoc-Management if ( !$lcintime ) {$crel = 1} # Ladefreigabe wenn nicht innerhalb Zeitslot für Ladesteuerung if ( $labortCond ) {$crel = 0} # keine Ladefreigabe bei genereller Abbruchbedingung - + # Steuerhash für optimimierte Ladeleistung erstellen ###################################################### my $surplswh = max (0, $surpls); - if ($today) { # nur Heute - $hsurp->{$hod}{hod} = $hod; - $hsurp->{$hod}{nhr} = $nhr; - $hsurp->{$hod}{surpls} = $lcintime ? $surplswh.'.'.$hod : 0; # Überschuß in Wh der Stunde mit Sortierhilfe - $hsurp->{$hod}{$bn}{spday} = $spday; # (Rest)PV-Überschuß am laufenden Tag - $hsurp->{$hod}{$bn}{initsocwh} = $socwh; - $hsurp->{$hod}{$bn}{batinstcap} = $batinstcap; - $hsurp->{$hod}{$bn}{bpinmax} = $bpinmax; # max. mögliche Ladeleistung - $hsurp->{$hod}{$bn}{otpMargin} = $otpMargin; # Sicherheitszuschlag für Berechnungen - $hsurp->{$hod}{$bn}{lcintime} = $lcintime; # Ladesteuerung "In Time" oder "nicht In Time" + if ($strategy eq 'optPower' || $strategy eq 'loadRelease' && $today) { # bei loadRelease' nur den aktuellen Tag betrachten + $hsurp->{$fd}{$hod}{nhr} = $nhr; + $hsurp->{$fd}{$hod}{speff} = $surpls; # effektiver PV Überschuß bzw. effektiver Verbrauch wenn < 0 + $hsurp->{$fd}{$hod}{surplswh} = $surplswh.'.'.$hod; # absoluter Überschuß in Wh der Stunde mit Sortierhilfe + $hsurp->{$fd}{$hod}{$bn}{spday} = $spday; # (Rest)PV-Überschuß am laufenden Tag + $hsurp->{$fd}{$hod}{$bn}{initsocwh} = $socwh; + $hsurp->{$fd}{$hod}{$bn}{batinstcap} = $batinstcap; # installierte Batteriekapazität (Wh) + $hsurp->{$fd}{$hod}{$bn}{bpinmax} = $bpinmax; # max. mögliche Ladeleistung + $hsurp->{$fd}{$hod}{$bn}{bpoutmax} = $bpoutmax; # max. mögliche Entladeleistung + $hsurp->{$fd}{$hod}{$bn}{lowSocwh} = $lowSocwh; # eingestellter lowSoC in Wh + $hsurp->{$fd}{$hod}{$bn}{csocwh} = $csocwh; # aktueller SoC in Wh + $hsurp->{$fd}{$hod}{$bn}{otpMargin} = $otpMargin; # Sicherheitszuschlag für Berechnungen + $hsurp->{$fd}{$hod}{$bn}{lcintime} = $lcintime; # Ladesteuerung "In Time" oder "nicht In Time" + $hsurp->{$fd}{$hod}{$bn}{stt} = $stt; # Day/Time für Debuglog + $hsurp->{$fd}{$hod}{$bn}{strategy} = $strategy; # Ladestrategie } - ## SOC-Prognose - ################# - my $fceff = $surpls; # effektiver PV Überschuß bzw. effektiver Verbrauch wenn < 0 + ## SOC-Prognose LR + #################### + my $speff = $surpls; # effektiver PV Überschuß bzw. effektiver Verbrauch wenn < 0 - $fceff = $fceff > 0 ? ($fceff >= $bpinmax ? $bpinmax : $fceff) : - $fceff < 0 ? ($fceff <= $bpoutmax * -1 ? $bpoutmax * -1 : $fceff) : - $fceff; + $speff = $speff > 0 ? ($speff >= $bpinmax ? $bpinmax : $speff) : + $speff < 0 ? ($speff <= $bpoutmax * -1 ? $bpoutmax * -1 : $speff) : + $speff; - $socwh += $crel ? ($fceff > 0 ? $fceff * STOREFFDEF : $fceff / STOREFFDEF) : - ($fceff > 0 ? 0 : $fceff / STOREFFDEF); # PV Überschuß (d.h. Aufladung) nur einbeziehen wenn Ladefreigabe + $socwh += $crel ? ($speff > 0 ? $speff * STOREFFDEF : $speff / STOREFFDEF) : + ($speff > 0 ? 0 : $speff / STOREFFDEF); # PV Überschuß (d.h. Aufladung) nur einbeziehen wenn Ladefreigabe $socwh = $socwh < $lowSocwh ? $lowSocwh : $socwh < $batoptsocwh ? $batoptsocwh : # SoC Prognose in Wh $socwh > $batinstcap ? $batinstcap : $socwh; - $socwh = sprintf "%.0f", $socwh; - my $progsoc = sprintf "%.1f", (100 * $socwh / $batinstcap); # Prognose SoC in % - + $socwh = sprintf "%.0f", $socwh; + $progsoc = sprintf "%.1f", (100 * $socwh / $batinstcap); # Prognose SoC in % ## Debuglog LR ################ - my $stt = (split /[-:]/, $nhstt)[2]; - $stt =~ s/\s/\//; - my $msg = "CurrSoc: $csoc %, SoCfc: $socwh Wh, whneed: $whneed, pvfc: $pvfc, rodpvfc: $rodpvfc, confcss: $confcss, SurpDay: $spday Wh, CurrPV: $pvCu W, CurrCons: $curcon W, Limit: $inplim W, inTime: ".($cgbt ? $lcintime : '-'); if ($num) { @@ -11831,67 +11846,98 @@ sub _batChargeMgmt { } } - debugLog ($paref, 'batteryManagement', "Bat $bn ChargeLR $stt -> $crel ($msg)"); + debugLog ($paref, 'batteryManagement', "Bat $bn ChargeLR $stt - lc: $crel, $msg"); ## Fortschreibung ################### $whneed = $batinstcap - $socwh; - ## Speicherung und Readings erstellen - ####################################### + ## Speicherung und Readings erstellen LR + ########################################## $values = { hsoc => $hsoc, - loop => 'LR', - num => $num, - crel => $crel, bn => $bn, - labortCond => $labortCond, - loadAbort => $loadAbort, nhr => $nhr, progsoc => $progsoc, - lcintime => $lcintime, - cgbt => $cgbt, socwh => $socwh, today => $today, hod => $hod, + loopid => 'LR', + strategy => $strategy, + crel => $crel, + labortCond => $labortCond, + loadAbort => $loadAbort, + cgbt => $cgbt, + lcintime => $lcintime, }; ___batChargeSaveResults ($paref, $values); + + $values = {}; } } - # leistungsoptimierte Beladungssteuerung - ########################################## - $paref->{hsurp} = $hsurp; - my ($hopt, $otp) = __batChargeOptTargetPower ($paref); - delete $paref->{hsurp}; - - ## Debuglog OTP - ################# - if ($paref->{debug} =~ /batteryManagement/) { - Log3 ($name, 1, "$name DEBUG> ChargeOTP - The limit for grid feed-in is $feedinlim W"); - Log3 ($name, 1, "$name DEBUG> ChargeOTP - NOTE: The hours listed below are the estimated number of hours remaining on the current day with at least the respective PV surplus."); - - for my $k (sort { $a <=> $b } keys %{$hopt}) { - my @batteries = grep { !/^(?:hod|surpls|nhr)$/xs } keys %{$hsurp->{24}}; - for my $bat (sort @batteries) { - my $sphrs = defined $hopt->{$k}{$bat}{sphrs} ? - '('.($hopt->{$k}{$bat}{sphrs} ? $hopt->{$k}{$bat}{sphrs} : 1).' hrs)' : - ''; - - my $ssocwh = $hopt->{$k}{$bat}{runwh} // '-'; - my $otpMargin = $hopt->{$k}{$bat}{otpMargin}; - my $margin = defined $otpMargin ? $otpMargin : SFTYMARGIN_20; - my $spls = int $hopt->{$k}{surpls}; - my $needmin = max (BatteryVal ($name, $bat, 'bpinreduced', 0), $hopt->{$k}{$bat}{pneedmin} // 0); + # leistungsoptimierte (optPower) Beladungssteuerung + ##################################################### + for my $lfd (0..max (0, keys %{$hsurp})) { + $paref->{hsurp} = $hsurp->{$lfd}; + my ($hopt, $otp) = __batChargeOptTargetPower ($paref); + delete $paref->{hsurp}; + + ## Debuglog OTP + ################# + if ($paref->{debug} =~ /batteryManagement/ && !$lfd) { + Log3 ($name, 1, "$name DEBUG> ChargeOTP - The limit for grid feed-in is $feedinlim W"); + } + + ## Speicherung und Readings erstellen OTP + ########################################### + for my $shod (sort { $a <=> $b } keys %{$hopt}) { + my $nhr = $hopt->{$shod}{nhr}; + my @batteries = grep { !/^(?:fd|speff|surplswh|nhr)$/xs } keys %{$hopt->{24}}; + + for my $bat (sort @batteries) { + my $ssocwh = $hopt->{$shod}{$bat}{runwh} // '-'; + my $fcendwh = $hopt->{$shod}{$bat}{fcendwh} // 0; + $progsoc = sprintf "%.1f", (100 * $fcendwh / $hopt->{$shod}{$bat}{batinstcap}); # Prognose SoC in % + + ## Speicherung und Readings erstellen OTP + ########################################## + $values = { hsoc => $hsoc, + otp => $otp, + bn => $bat, + nhr => $nhr, + progsoc => $progsoc, + socwh => $fcendwh, + hod => $shod, + loopid => 'OTP', + strategy => $hopt->{$shod}{$bat}{strategy}, + }; + + ___batChargeSaveResults ($paref, $values); - if ($hopt->{$k}{nhr} eq '00') { # Target für aktuelle Stunde - $needmin = $otp->{$bat}{target} // 0; + ## Debuglog OTP + ################# + if ($paref->{debug} =~ /batteryManagement/) { + my $lcintime = $hopt->{$shod}{$bat}{lcintime}; + my $spls = int $hopt->{$shod}{surplswh}; + my $pneedmin = $hopt->{$shod}{$bat}{pneedmin}; + my $ttt = $hopt->{$shod}{$bat}{stt}; + + if ($nhr eq '00') { + $pneedmin = $otp->{$bat}{target} // 0; + my $achievable = $hopt->{$shod}{$bat}{achievable}; + my $otpMargin = $hopt->{$shod}{$bat}{otpMargin}; + my $margin = defined $otpMargin ? $otpMargin : SFTYMARGIN_20; + Log3 ($name, 1, "$name DEBUG> Bat $bat ChargeOTP - used safety margin: $margin %"); + Log3 ($name, 1, "$name DEBUG> Bat $bat ChargeOTP - is the charging goal likely to be achieved? - $achievable"); + } + + Log3 ($name, 1, "$name DEBUG> Bat $bat ChargeOTP $ttt - hod: $shod / $nhr, lc: $lcintime, SoC S/E: $ssocwh / $fcendwh Wh, Surplus: $spls Wh, OTP: $pneedmin W"); } - - Log3 ($name, 1, "$name DEBUG> Bat $bat ChargeOTP - hod: $k, Start SoC: $ssocwh Wh, Surplus: $spls Wh $sphrs, OTP: $needmin W, safety: $margin %"); } } } + # prognostizierten SOC über alle Batterien speichern ###################################################### @@ -11941,102 +11987,142 @@ return; sub __batChargeOptTargetPower { my $paref = shift; my $name = $paref->{name}; - my $hsurp = $paref->{hsurp} // return; # Hashref Überschußhash + my $hsurp = $paref->{hsurp}; # Hashref Überschußhash - my $fipl = CurrentVal ($name, 'feedinPowerLimit', INFINITE); - my @sorted = sort { $hsurp->{$a}{surpls} <=> $hsurp->{$b}{surpls} } keys %{$hsurp}; - my $sphrs = scalar grep { int( $hsurp->{$_}{surpls} ) >= 1 } @sorted; # Stunden mit Überschuß >= 1 - my @batteries = grep { !/^(?:hod|surpls|nhr)$/xs } keys %{$hsurp->{24}}; + my $fipl = CurrentVal ($name, 'feedinPowerLimit', INFINITE); + my @sortedhods = sort { $hsurp->{$a}{surplswh} <=> $hsurp->{$b}{surplswh} } keys %{$hsurp}; # Stunden aufsteigend nach PV-Überschuß sortiert + my @batteries = grep { !/^(?:fd|speff|surplswh|nhr)$/xs } keys %{$hsurp->{24}}; my $otp; - for my $shod (@sorted) { - my $spls = int $hsurp->{$shod}{surpls}; - my $newshod = sprintf "%02d", (int $shod + 1); + for my $hod (sort { $a <=> $b } keys %{$hsurp}) { + my $spls = int $hsurp->{$hod}{surplswh}; + my $newshod = sprintf "%02d", (int $hod + 1); + my $nhr = $hsurp->{$hod}{nhr}; - for my $sbn (sort @batteries) { # jede Batterie - my $bpinmax = $hsurp->{$shod}{$sbn}{bpinmax}; # Bat max. mögliche Ladelesitung - - if (!$hsurp->{$shod}{$sbn}{lcintime}) { # Ladesteuerung nicht "In Time" - $hsurp->{$shod}{$sbn}{pneedmin} = $bpinmax; + my @remaining_hods = grep { int $_ >= int $hod } @sortedhods; + + for my $sbn (sort { $a <=> $b } @batteries) { # jede Batterie + my $bpinmax = $hsurp->{$hod}{$sbn}{bpinmax}; # Bat max. mögliche Ladelesitung + my $sbatinstcap = $hsurp->{$hod}{$sbn}{batinstcap}; # Kapa dieser Batterie + my $lowSocwh = $hsurp->{$hod}{$sbn}{lowSocwh}; # eingestellter lowSoc in Wh + my $csocwh = $hsurp->{$hod}{$sbn}{csocwh}; # aktueller SoC in Wh + my $bpinreduced = BatteryVal ($name, $sbn, 'bpinreduced', 0); # Standardwert wenn z.B. kein Überschuß oder Zwangsladung vom Grid + + my $runwh = defined $hsurp->{$hod}{$sbn}{fcnextwh} ? # Auswahl des zu verwendenden Prognose-SOC (Wh) + $hsurp->{$hod}{$sbn}{fcnextwh} : + ( $nhr eq '00' ? + $csocwh : + $hsurp->{$hod}{$sbn}{initsocwh} + ); + + $runwh = min ($runwh, $sbatinstcap); + $hsurp->{$hod}{$sbn}{runwh} = sprintf "%.0f", $runwh; + + if (!$spls || !$hsurp->{$hod}{$sbn}{lcintime}) { # Ladesteuerung nicht "In Time" + $hsurp->{$hod}{$sbn}{achievable} = 'undetermined'; + $hsurp->{$hod}{$sbn}{pneedmin} = $bpinmax; + $hsurp->{$hod}{$sbn}{fcendwh} = sprintf "%.0f", $runwh; - if ($hsurp->{$shod}{nhr} eq '00') { - $otp->{$sbn}{target} = $bpinmax; - storeReading ('Battery_ChargeOptTargetPower_'.$sbn, $bpinmax.' W'); + if ($nhr eq '00') { + $otp->{$sbn}{target} = $csocwh <= $lowSocwh ? $bpinreduced : $bpinmax; } next; - } - - my $bpinreduced = BatteryVal ($name, $sbn, 'bpinreduced', 0); # Standardwert wenn z.B. kein Überschuß oder Zwangsladung vom Grid - my $crgwh = BatteryVal ($name, $sbn, 'bchargewh', 0); # aktueller Ladezustand Batterie - my $runwh = defined $hsurp->{$shod}{$sbn}{fcnextwh} ? # Auswahl des zu verwendenden Prognose-SOC (Wh) - $hsurp->{$shod}{$sbn}{fcnextwh} : - ( $hsurp->{$shod}{nhr} eq '00' ? - $crgwh : - $hsurp->{$shod}{$sbn}{initsocwh} - ); + } - my $otpMargin = $hsurp->{$shod}{$sbn}{otpMargin}; - my $margin = defined $otpMargin ? $otpMargin : SFTYMARGIN_20; + my $otpMargin = $hsurp->{$hod}{$sbn}{otpMargin}; + my $margin = defined $otpMargin ? $otpMargin : SFTYMARGIN_20; + my $runwhneed = $sbatinstcap - $runwh; + my $fref = ___batFindMinPhWh ($hsurp, \@remaining_hods, $runwhneed); + my $needraw = min ($fref->{ph}, $spls); # Ladeleistung auf Surplus begrenzen + + $needraw *= 1 + ($margin / 100); # 1. Sicherheitsaufschlag + $needraw = sprintf "%.0f", $needraw; + my $pneedmin = max ($needraw, $bpinreduced); # Mindestladeleistung bpinreduced sicherstellen - if (!$spls) { # auf kleine Sollladeleistung setzen wenn kein Überschuß - $hsurp->{$shod}{$sbn}{pneedmin} = $bpinreduced; - - if ($hsurp->{$shod}{nhr} eq '00') { - $otp->{$sbn}{target} = $bpinreduced; - storeReading ('Battery_ChargeOptTargetPower_'.$sbn, $bpinreduced.' W'); - } - - next; - } - - my $sbatinstcap = $hsurp->{$shod}{$sbn}{batinstcap}; # Kapa dieser Batterie - $runwh = min ($runwh, $sbatinstcap); - my $runwhneed = $sbatinstcap - $runwh; - my $needraw = $sphrs ? $runwhneed / $sphrs : $runwhneed; # Ladeleistung initial - $needraw *= 1 + ($margin / 100); # Sicherheitsaufschlag - - $needraw = max (0, $needraw); - $hsurp->{$shod}{$sbn}{runwh} = sprintf "%.0f", $runwh; - $hsurp->{$shod}{$sbn}{sphrs} = $sphrs; # Reststunden mit diesem Überschuß - my $pneedmin = sprintf "%.0f", $spls > $needraw ? # Mindestladeleistung bzw. Energie bei 1h (Wh) - $needraw ? $needraw : $bpinreduced : - $spls; - $hsurp->{$shod}{$sbn}{pneedmin} = min ($pneedmin, $hsurp->{$shod}{$sbn}{bpinmax}); # Begrenzung auf max. mögliche Batterieleistung - - if (defined $hsurp->{$newshod}) { - $hsurp->{$newshod}{$sbn}{fcnextwh} = min ($sbatinstcap, $runwh + $hsurp->{$shod}{$sbn}{pneedmin}); - } - - if ($hsurp->{$shod}{nhr} eq '00') { - my $target = max ($bpinreduced, $hsurp->{$shod}{$sbn}{pneedmin}); - - if (NexthoursVal ($name, 'NextHour00', 'DoN', 0)) { - $target *= 1 + ($margin / 100); # 2. Sicherheitsaufschlag - } + $hsurp->{$hod}{$sbn}{achievable} = 'goal: '.(sprintf "%.0f", $runwhneed).' Wh -> '.($fref->{achievable} ? 'yes' : 'no'); + $hsurp->{$hod}{$sbn}{pneedmin} = min ($pneedmin, $hsurp->{$hod}{$sbn}{bpinmax}); # Begrenzung auf max. mögliche Batterieleistung + + if ($nhr eq '00') { + my $target = max ($bpinreduced, $hsurp->{$hod}{$sbn}{pneedmin}); + $target *= 1 + ($margin / 100); # 2. Sicherheitsaufschlag my $gfeedin = CurrentVal ($name, 'gridfeedin', 0); # aktuelle Netzeinspeisung my $bpin = CurrentVal ($name, 'batpowerinsum', 0); # aktuelle Batterie Ladeleistung (Summe über alle Batterien) my $inc = 0; - if ( !$bpin && $gfeedin > $fipl ) {$inc = $gfeedin - $fipl} # Ladefreigabe wenn akt. keine Bat-Ladung UND akt. Einspeisung > Einspeiselimit der Anlage - if ( $bpin && ($gfeedin - $bpin) > $fipl ) {$inc = $bpin + (($gfeedin - $bpin) - $fipl)} # Ladefreigabe wenn akt. Bat-Ladung UND Eispeisung - Bat-Ladung > Einspeiselimit der Anlage - - + if ( !$bpin && $gfeedin > $fipl ) {$inc = $gfeedin - $fipl} # Ladeleistung wenn akt. keine Bat-Ladung UND akt. Einspeisung > Einspeiselimit der Anlage + if ( $bpin && ($gfeedin - $bpin) > $fipl ) {$inc = $bpin + (($gfeedin - $bpin) - $fipl)} # Ladeleistung wenn akt. Bat-Ladung UND Eispeisung - Bat-Ladung > Einspeiselimit der Anlage + $target = sprintf "%.0f", max ($target, $inc); # Einspeiselimit berücksichtigen - $target = min ($bpinmax, $target); # 2. Begrenzung auf max. mögliche Batterieleistung + $target = min (($csocwh <= $lowSocwh ? $bpinreduced : $bpinmax), $target); # 2. Begrenzung auf max. mögliche Batterieleistung bzw. bpinreduced bei Unterschreitung lowSoc $otp->{$sbn}{target} = $target; - - storeReading ('Battery_ChargeOptTargetPower_'.$sbn, $target.' W'); - } + } + + $hsurp->{$hod}{$sbn}{fcendwh} = sprintf "%.0f", min ($sbatinstcap, $runwh + ($nhr eq '00' ? # Endwert Prognose aktuelle Stunde + $otp->{$sbn}{target} : + $hsurp->{$hod}{$sbn}{pneedmin} + )); + + $hsurp->{$newshod}{$sbn}{fcnextwh} = $hsurp->{$hod}{$sbn}{fcendwh}; # Startwert kommende Stunde } - - $sphrs-- if($spls); # Reststunden mit Überschuß } return ($hsurp, $otp); } +############################################################################################### +# Errechnet minimal benötigte konstante Ladeleistung: $ph Wh via Binärsuche Iteration +# +# - Wenn die Summe aller surplswh geringer ist als der Bedarf, wird Ereq automatisch auf +# diesen Maximalwert gesetzt und liefert so die tatsächlich erreichbare Energie. +# - gewichtete Stundenkapazität @hods enthält die Stunden-Keys sortiert von der niedrigsten +# bis zur höchsten Leistung. In jeder Binärsuche-Iteration addiert das Skript +# min(ph, surplswh) für jede Stunde, wodurch die konstant gewählte Leistung ph gemäß der +# verfügbaren Kapazität gewichtet wird. +# - Rückgabe Nach X Iterationen steht $high als kleinstmöglicher Wert für ph bereit. Er +# garantiert entweder das Erreichen von Ereq Wh oder – falls das Ziel unerreichbar war – +# die vollständige Ausnutzung der vorhandenen Kapazität. +############################################################################################### +sub ___batFindMinPhWh { + my ($hsurp, $aref, $runwhneed) = @_; + my @hods = @$aref; + + my $Ereq = $runwhneed; # 1. Benötigte Energie (Wh) bestimmen + my $achievable = 1; + my $total = 0; # 2. Gesamtkapazität aller Stunden mit PV-Überschuß ermitteln + $total += $hsurp->{$_}{surplswh} for @hods; + + if ($total < $Ereq) { + $achievable = 0; + #$Ereq = $total; # 3. Wenn Ziel nicht erreichbar: Ereq auf Maximum setzen + } + + my $low = 0; # 4. Binärsuche für konstante Ladeleistung ph (Wh/h) + my $high = max map { $hsurp->{$_}{surplswh} } @hods; + my $eps = 0.5; # minimale Genauigkeit in Wh (1e-3) + my $max_iter = 100; # Zwangsabbruch nach X Durchläufen + my $loop = 0; + + while (($high - $low) > $eps) { + last if ++$loop > $max_iter; + + my $mid = ($low + $high) / 2; + my $charged = 0; + + for my $hod (@hods) { + my $cap = $hsurp->{$hod}{surplswh}; + $charged += $mid < $cap ? $mid : $cap; + } + + $charged >= $Ereq ? ($high = $mid) : ($low = $mid); + } + + $high = max (0, $high); + +return { ph => (sprintf "%.0f", $high), iterations => $loop, blur => (sprintf "%.4f", ($high - $low)), achievable => $achievable }; +} + ################################################################ # Speicherung Ergebnisse aus Batterie Lademanagement ################################################################ @@ -12046,14 +12132,16 @@ sub ___batChargeSaveResults { my $name = $paref->{name}; my $hsoc = $values->{hsoc}; # Referenz SoC-Hash + my $otp = $values->{otp}; # Referenz OTP-Hash my $bn = $values->{bn}; # Batterie Nummer - my $num = $values->{num}; # Nexthours Schleife (0..MAXNEXTHOURS) my $nhr = $values->{nhr}; # zweistellige lfd. Nexthour my $progsoc = $values->{progsoc}; # Prognose-SoC in % my $socwh = $values->{socwh}; # Prognose-SoC in Wh my $today = $values->{today}; # Statusbit aktueller Tag my $hod = $values->{hod}; # Stunde des Tages - my $loop = $values->{loop}; # in welcher Loop gestartet? + my $loopid = $values->{loopid}; # in welcher Loop ist sub aufgerufen? + my $strategy = $values->{strategy}; # welche Lade-Strategie wird verwendet + my $crel = $values->{crel}; # nur in Schleife 'loadRelease' mitgeben my $labortCond = $values->{labortCond}; # nur in Schleife 'loadRelease' mitgeben my $loadAbort = $values->{loadAbort}; # nur in Schleife 'loadRelease' mitgeben @@ -12062,36 +12150,50 @@ sub ___batChargeSaveResults { ## in Schleife 'loadRelease' setzen ##################################### - if ($loop eq 'LR') { # nur in Schleife 'loadRelease' setzen + if ($loopid eq 'LR') { # nur in Schleife 'loadRelease' setzen $data{$name}{nexthours}{'NextHour'.$nhr}{'rcdchargebat'.$bn} = $crel; $data{$name}{nexthours}{'NextHour'.$nhr}{'lcintimebat'.$bn} = $lcintime if($cgbt); # nur einmal bei 'loadRelease' setzen -> Ladesteuerung "In Time", "nicht In Time" oder nicht verwendet + $data{$name}{nexthours}{'NextHour'.$nhr}{'strategybat'.$bn} = $strategy; - if (!$num) { + if ($nhr eq '00') { storeReading ('Battery_ChargeUnrestricted_'.$bn, $crel); storeReading ('Battery_ChargeAbort_'.$bn, $labortCond) if ($loadAbort); # Ladeabbruchbedingung } if ($today && $hod) { - writeToHistory ( { paref => $paref, key => 'lcintimebat'.$bn, val => $lcintime, hour => $hod } ) if($cgbt); # nur einmal bei 'loadRelease' setzen + writeToHistory ( { paref => $paref, key => 'lcintimebat'.$bn, val => $lcintime, hour => $hod } ) if($cgbt); + writeToHistory ( { paref => $paref, key => 'strategybat'.$bn, val => $strategy, hour => $hod } ); } } - - if ($today && $hod) { - writeToHistory ( { paref => $paref, key => 'batprogsoc'.$bn, val => $progsoc, hour => $hod } ); + ## in Schleife 'optPower' setzen + ################################## + if ($loopid eq 'OTP') { + if ($nhr eq '00') { # Target für aktuelle Stunde + my $needmin = $otp->{$bn}{target} // 0; + storeReading ('Battery_ChargeOptTargetPower_'.$bn, $needmin.' W'); + } } - __createNextHoursSFCReadings ( {name => $name, - nhr => $nhr, - bn => $bn, - progsoc => $progsoc - } - ); # Readings NextHourXX_Bat_XX_ChargeForecast erstellen + ## abhängig von Strategie in entsprechender Schleife setzen + ############################################################# + if (($loopid eq 'LR' && $strategy eq 'loadRelease') || ($loopid eq 'OTP' && $strategy eq 'optPower')) { + if ($today && $hod) { + writeToHistory ( { paref => $paref, key => 'batprogsoc'.$bn, val => $progsoc, hour => $hod } ); + } + + __createNextHoursSFCReadings ( {name => $name, + nhr => $nhr, + bn => $bn, + progsoc => $progsoc + } + ); # Readings NextHourXX_Bat_XX_ChargeForecast erstellen - $data{$name}{nexthours}{'NextHour'.$nhr}{'soc'.$bn} = $progsoc; + $data{$name}{nexthours}{'NextHour'.$nhr}{'soc'.$bn} = $progsoc; + + $hsoc->{$nhr}{socprogwhsum} += $socwh; # Hilfshash Aufsummierung SoC-Prognose (Wh) über alle Batterien + } - $hsoc->{$nhr}{socprogwhsum} += $socwh; # Hilfshash Aufsummierung SoC-Prognose (Wh) über alle Batterien - return; } @@ -12211,13 +12313,12 @@ sub _createSummaries { $tdConFcTillSunset -= ($confc / 60) * $diflasth; } } - else { - $tomorrowSum->{PV} += $pvfc if(int($nhday) == int($tmoday)); - - if (int($nhday) == int($datmoday)) { - $daftertomSum->{PV} += $pvfc; - $daftertomSum->{Consumption} += $confc; - } + elsif ($nhday eq $tmoday) { + $tomorrowSum->{PV} += $pvfc; + } + elsif ($nhday eq $datmoday) { + $daftertomSum->{PV} += $pvfc; + $daftertomSum->{Consumption} += $confc; } } @@ -17535,6 +17636,7 @@ sub _beamFillupBatValues { $hh->{$day_str}{$time_str}{'rcdchargebat'.$bn} = $rcdc; $hh->{$day_str}{$time_str}{'lcintimebat'.$bn} = NexthoursVal ($name, $idx, 'lcintimebat'.$bn, undef); + $hh->{$day_str}{$time_str}{'strategybat'.$bn} = NexthoursVal ($name, $idx, 'strategybat'.$bn, undef); $hh->{$day_str}{$time_str}{'soc'.$bn} = NexthoursVal ($name, $idx, 'soc'.$bn, undef); } } @@ -17557,6 +17659,7 @@ sub _beamFillupBatValues { ##################################### $hfcg->{$kdx}{'rcdchargebat'.$bn} = $hh->{$ds}{$ts}{'rcdchargebat'.$bn} if(defined $hh->{$ds}{$ts}{'rcdchargebat'.$bn}); $hfcg->{$kdx}{'lcintimebat'.$bn} = $hh->{$ds}{$ts}{'lcintimebat'.$bn} if(defined $hh->{$ds}{$ts}{'lcintimebat'.$bn}); + $hfcg->{$kdx}{'strategybat'.$bn} = $hh->{$ds}{$ts}{'strategybat'.$bn} if(defined $hh->{$ds}{$ts}{'strategybat'.$bn}); $hfcg->{$kdx}{'soc'.$bn} = $hh->{$ds}{$ts}{'soc'.$bn} if(defined $hh->{$ds}{$ts}{'soc'.$bn}); ## Auffüllen mit History Werten (Achtung: Stundenverschieber relativ zu Nexthours) @@ -17564,10 +17667,12 @@ sub _beamFillupBatValues { if (!defined $hh->{$ds}{$ts}{'rcdchargebat'.$bn}) { my $histsoc = HistoryVal ($hash, $ds, (sprintf "%02d", $ts+1), 'batsoc'.$bn, undef); my $lcintime = HistoryVal ($hash, $ds, (sprintf "%02d", $ts+1), 'lcintimebat'.$bn, undef); + my $strategy = HistoryVal ($hash, $ds, (sprintf "%02d", $ts+1), 'strategybat'.$bn, undef); if (defined $histsoc) { $hfcg->{$kdx}{'rcdchargebat'.$bn} = 'hist'; $hfcg->{$kdx}{'lcintimebat'.$bn} = $lcintime; + $hfcg->{$kdx}{'strategybat'.$bn} = $strategy; $hfcg->{$kdx}{'soc'.$bn} = $histsoc; } } @@ -18161,6 +18266,11 @@ sub __batteryOnBeam { $time_str = (split ":", $time_str)[0]; # Forum: https://forum.fhem.de/index.php?msg=1332721 my $soc = $hfcg->{$i}{'soc'.$bn}; my $lcintime = $hfcg->{$i}{'lcintimebat'.$bn}; # Lademanagement für Batterie XX ist aktiviert + my $strategy = $hfcg->{$i}{'strategybat'.$bn} // '-'; # Ladestrategie + + my $stysymbol = $strategy eq 'loadRelease' ? 'ldreleas' : + $strategy eq 'optPower' ? 'optchpow' : + 'norate'; my ($bpower, $currsoc); @@ -18178,7 +18288,7 @@ sub __batteryOnBeam { flag => $hfcg->{$i}{'rcdchargebat'.$bn}, msg1 => $balias, msg2 => $lcintime, - msg3 => $htitles{ldreleas}{$lang}, + msg3 => $htitles{$stysymbol}{$lang}, soc => $soc, pcurr => $bpower, lang => $lang @@ -20972,7 +21082,7 @@ sub _listDataPoolPvHist { $prdl .= "pprl${pn}: $pprl"; } - my ($btotin, $batin, $btotout, $batout, $batmsoc, $batssoc, $batprogsoc, $batsoc, $lcintime); + my ($btotin, $batin, $btotout, $batout, $batmsoc, $batssoc, $batprogsoc, $batsoc, $lcintime, $lcstrategy); for my $bn (1..MAXBATTERIES) { # + alle Batterien $bn = sprintf "%02d", $bn; my $hbtotin = HistoryVal ($name, $day, $key, 'batintotal'.$bn, '-'); @@ -20984,6 +21094,7 @@ sub _listDataPoolPvHist { my $hbatprogsoc = HistoryVal ($name, $day, $key, 'batprogsoc'.$bn, '-'); my $hbatsoc = HistoryVal ($name, $day, $key, 'batsoc'.$bn, '-'); my $intime = HistoryVal ($name, $day, $key, 'lcintimebat'.$bn, '-'); + my $strategy = HistoryVal ($name, $day, $key, 'strategybat'.$bn, '-'); if ($export eq 'csv') { $hexp->{$day}{$key}{"BatteryInTotal${bn}"} = $hbtotin; @@ -20995,6 +21106,7 @@ sub _listDataPoolPvHist { $hexp->{$day}{$key}{"BatteryProgSoc${bn}"} = $hbatprogsoc; $hexp->{$day}{$key}{"BatterySoc${bn}"} = $hbatsoc; $hexp->{$day}{$key}{"BatteryLCinTime${bn}"} = $intime; + $hexp->{$day}{$key}{"BatteryStrategy${bn}"} = $strategy; } $btotin .= ', ' if($btotin); @@ -21015,6 +21127,8 @@ sub _listDataPoolPvHist { $batsoc .= "batsoc${bn}: $hbatsoc"; $lcintime .= ', ' if($lcintime); $lcintime .= "lcintimebat${bn}: $intime"; + $lcstrategy .= ', ' if($lcstrategy); + $lcstrategy .= "strategybat${bn}: $strategy"; } $ret .= "\n " if($ret); @@ -21047,7 +21161,9 @@ sub _listDataPoolPvHist { $ret .= "\n " if($key ne '99'); $ret .= $lcintime if($key ne '99'); $ret .= "\n " if($key ne '99'); - + $ret .= $lcstrategy if($key ne '99'); + $ret .= "\n " if($key ne '99'); + $ret .= $batin; $ret .= "\n "; $ret .= $batout; @@ -21448,16 +21564,19 @@ sub _listDataPoolNextHours { my $socprgs = NexthoursVal ($name, $idx, 'socprogwhsum', '-'); my $dinrang = NexthoursVal ($name, $idx, 'DaysInRange', '-'); - my ($rcdbat, $socs, $lcintime); + my ($rcdbat, $socs, $lcintime, $lcstrategy); for my $bn (1..MAXBATTERIES) { # alle Batterien $bn = sprintf "%02d", $bn; my $rcdcharge = NexthoursVal ($name, $idx, 'rcdchargebat'.$bn, '-'); my $intime = NexthoursVal ($name, $idx, 'lcintimebat'.$bn, '-'); + my $strategy = NexthoursVal ($name, $idx, 'strategybat'.$bn, '-'); my $socxx = NexthoursVal ($name, $idx, 'soc'.$bn, '-'); $rcdbat .= ', ' if($rcdbat); $rcdbat .= "rcdchargebat${bn}: $rcdcharge"; $lcintime .= ', ' if($lcintime); $lcintime .= "lcintimebat${bn}: $intime"; + $lcstrategy .= ', ' if($lcstrategy); + $lcstrategy .= "strategybat${bn}: $strategy"; $socs .= ', ' if($socs); $socs .= "soc${bn}: $socxx"; } @@ -21479,6 +21598,8 @@ sub _listDataPoolNextHours { $sq .= $rcdbat; $sq .= "\n "; $sq .= $lcintime; + $sq .= "\n "; + $sq .= $lcstrategy; } return $sq; @@ -26283,6 +26404,7 @@ to ensure that the system configuration is correct. today has value '1' if start date on current day rcdchargebatXX Charging recommendation with full power for battery XX (1 - Yes, 0 - No) lcintimebatXX Charge management for battery XX is activated or will be activated (1 - Yes, 0 - No) + strategybatXX the selected charging strategy rr1c Total precipitation during the last hour kg/m2 rrange range of total rain socXX current (NextHour00) or predicted SoC (%) of battery XX @@ -26334,6 +26456,7 @@ to ensure that the system configuration is correct. feedprice Remuneration for the feed-in of one kWh. The currency of the price is defined in the setupMeterDev. hourscsmeXX total active hours of the day from ConsumerXX lcintimebatXX the charge management for battery XX was activated (1 - Yes, 0 - No) + strategybatXX the selected charging strategy minutescsmXX total active minutes in the hour of ConsumerXX plantderated Timestamp of the first curtailment event of the system in this hour, otherwise '0' pprlXX Energy generation of producer XX (see attribute setupOtherProducerXX) in the hour (Wh) @@ -26918,7 +27041,7 @@ to ensure that the system configuration is correct.
  • ctrlBatSocManagementXX lowSoc=<Value> upSoC=<Value> [maxSoC=<Value>] [careCycle=<Value>] [lcSlot=<hh:mm>-<hh:mm>] [loadAbort=<SoC1>:<MinPwr>:<SoC2>] - [safetyMargin=<Value>[:<Value>]]

    + [safetyMargin=<Value>[:<Value>]] [loadStrategy=<Value>]

    If a battery device (setupBatteryDevXX) is installed, this attribute activates the battery SoC and charge management for this battery device.
    @@ -26971,6 +27094,11 @@ to ensure that the system configuration is correct. surcharge used to calculate the optimized load capacity. Both values are percentages. Value: 0..100[:0..100] (integers) + loadStrategy The selected charging strategy is taken into account when displaying the battery in bar graph. + The generation of tax readings is not affected. The specification is optional. + For more information on selecting a strategy, see german Wiki. + Value: loadRelease or optPower, default: loadRelease +
    @@ -28960,6 +29088,7 @@ die ordnungsgemäße Anlagenkonfiguration geprüft werden. today hat Wert '1' wenn Startdatum am aktuellen Tag rcdchargebatXX Aufladeempfehlung mit voller Leistung für Batterie XX (1 - Ja, 0 - Nein) lcintimebatXX Lademanagement für Batterie XX ist aktiviert bzw. wird aktiviert sein (1 - Ja, 0 - Nein) + strategybatXX die gewählte Ladestrategie rr1c Gesamtniederschlag in der letzten Stunde kg/m2 rrange Bereich des Gesamtniederschlags socXX aktueller (NextHour00) oder prognostizierter SoC (%) der Batterie XX @@ -29012,6 +29141,7 @@ die ordnungsgemäße Anlagenkonfiguration geprüft werden. avgcycmntscsmXX durchschnittliche Dauer eines Einschaltzyklus des Tages von ConsumerXX in Minuten hourscsmeXX Summe Aktivstunden des Tages von ConsumerXX lcintimebatXX das Lademanagement für Batterie XX war aktiviert (1 - Ja, 0 - Nein) + strategybatXX die gewählte Ladestrategie minutescsmXX Summe Aktivminuten in der Stunde von ConsumerXX plantderated Zeitstempel des ersten Abregelungsvorfalls der Anlage in dieser Stunde, sonst '0' pprlXX Energieerzeugung des Produzenten XX (siehe Attribut setupOtherProducerXX) in der Stunde (Wh) @@ -29595,7 +29725,7 @@ die ordnungsgemäße Anlagenkonfiguration geprüft werden.
  • ctrlBatSocManagementXX lowSoc=<Wert> upSoC=<Wert> [maxSoC=<Wert>] [careCycle=<Wert>] [lcSlot=<hh:mm>-<hh:mm>] [loadAbort=<SoC1>:<MinPwr>:<SoC2>] - [safetyMargin=<Wert>[:<Wert>]]

    + [safetyMargin=<Wert>[:<Wert>]] [loadStrategy=<Wert>]

    Sofern ein Batterie Device (setupBatteryDevXX) installiert ist, aktiviert dieses Attribut das Batterie SoC- und Lade-Management für dieses Batteriegerät.
    @@ -29649,6 +29779,11 @@ die ordnungsgemäße Anlagenkonfiguration geprüft werden. Zuschlag bei der Berechnung der optimierten Ladeleistung. Beide Angaben sind Prozentwerte. Wert: 0..100[:0..100] (Ganzzahlen) + loadStrategy Bei der Anzeige der Batterie in der Balkengrafik wird die gewählte Ladestrategie berücksichtigt. + Die Generierung der Steuerreadings wird nicht beeinflusst. Die Angabe ist optional. + Weitere Informationen zur Auswahl der Strategie siehe Wiki. + Wert: loadRelease oder optPower, default: loadRelease +