76_SolarForecast: minor code changes after v 1.60.0

git-svn-id: https://svn.fhem.de/fhem/trunk@30477 2b470e98-0d58-463d-a4d8-8e2adae1ed80
This commit is contained in:
DS_Starter
2025-11-02 20:37:27 +00:00
parent 23c20842c3
commit af6b8a1b0e

View File

@@ -160,13 +160,14 @@ BEGIN {
# Versions History intern
my %vNotesIntern = (
"1.60.1" => "02.11.2025 ___batAdjustPowerByMargin: minor code change, preparation for barrierSoC ",
"1.60.0" => "01.11.2025 ___ownSpecGetFWwidget: handling of line breaks in attributes & can hamdle a key=value pair separateley ".
"Width of a text field in graphicHeaderOwnspec fixed to 10, edit commandref ".
"__batChargeOptTargetPower: use an average for the charging power if smartPower set and charging target are not achievable ".
"__createOwnSpec: an empty field can be created within a line by simply using a colon (:). ".
"add new key pvshare to CustomerXX attributes -> __setConsRcmdState add PV share calculation ".
"___doPlanning: code improvements and implement PV share needed ".
" Task 2: chamge timestamp of day before to 24:00:00, _restorePlantConfig: fix problem with attr sequence ".
"_restorePlantConfig: fix problem with attr sequence ".
"_setreset: set reset is reworked with widgetList, aiData can be deleted by index ".
"_flowGraphic: new variable node2home_direction ".
"new sub askLogtime to avoid error logs too often, Forum: https://forum.fhem.de/index.php?msg=1350716 ",
@@ -7649,6 +7650,7 @@ sub _attrBatSocManagement { ## no critic "not used"
lowSoc => { comp => '(100|[1-9]?[0-9])', must => 1, act => 0 },
upSoC => { comp => '(100|[1-9]?[0-9])', must => 1, act => 0 },
maxSoC => { comp => '(100|[1-9]?[0-9])', must => 0, act => 0 },
barrierSoC => { comp => '(100|[1-9]?[0-9])', must => 0, act => 0 },
stepSoC => { comp => '[0-5]', must => 0, act => 0 },
careCycle => { comp => '\d+', must => 0, act => 0 },
lcSlot => { comp => '((?:[01]\d|2[0-3]):[0-5]\d-(?:[01]\d|2[0-3]):[0-5]\d)', must => 0, act => 1 },
@@ -11700,6 +11702,7 @@ sub __parseAttrBatSoc {
my $parsed = {
lowSoc => $ph->{lowSoc},
barrierSoC => $ph->{barrierSoC}, # SoC Barriere ab der eine Ladeleistungssteuerung aktiv sein soll
upSoc => $ph->{upSoC},
maxSoc => $ph->{maxSoC} // MAXSOCDEF, # optional (default: MAXSOCDEF)
stepSoc => $ph->{stepSoC} // BATSOCCHGDAY, # mögliche SoC-Änderung pro Tag
@@ -11871,6 +11874,7 @@ sub _batChargeMgmt {
$strategy = 'loadRelease'; # 'loadRelease', 'optPower', 'smartPower'
my $wou = 0; # Gewichtung Prognose-Verbrauch als Anteil "Eigennutzung" (https://forum.fhem.de/index.php?msg=1348429)
my $lowSoc = 0;
my $barrierSoC = 0;
my $loadAbort = '';
my $goalwh = $batinstcap; # initiales Ladeziel (Wh)
my $lrMargin = SFTYMARGIN_50;
@@ -11878,22 +11882,24 @@ sub _batChargeMgmt {
my $lcslot;
if ($cgbt) {
my $parsed = __parseAttrBatSoc ($name, $cgbt);
$lowSoc = $parsed->{lowSoc} // 0;
$lcslot = $parsed->{lcslot};
$loadAbort = $parsed->{loadAbort};
$lrMargin = $parsed->{lrMargin} // $lrMargin; # Sicherheitszuschlag LR (%)
$otpMargin = $parsed->{otpMargin} // $otpMargin; # Sicherheitszuschlag OTP (%)
$strategy = $parsed->{loadStrategy} // $strategy;
$wou = $parsed->{weightOwnUse} // $wou;
my $tgt = $parsed->{loadTarget}; # Ladeziel-SoC in %
$tgt = $batoptsoc if(defined $tgt && $tgt < $batoptsoc); # Wert Battery_OptimumTargetSoC_XX beachten
$goalwh = defined $tgt
? sprintf "%.0f", ___batSocPercentToWh ($batinstcap, $tgt)
: $goalwh; # Ladeziel-SoC in Wh
my $parsed = __parseAttrBatSoc ($name, $cgbt);
$lowSoc = $parsed->{lowSoc} // 0;
$barrierSoC = $parsed->{barrierSoC} // $barrierSoC; # SoC-Barriere, ab der die Ladesteuerung akitv sein soll
$lcslot = $parsed->{lcslot};
$loadAbort = $parsed->{loadAbort};
$lrMargin = $parsed->{lrMargin} // $lrMargin; # Sicherheitszuschlag LR (%)
$otpMargin = $parsed->{otpMargin} // $otpMargin; # Sicherheitszuschlag OTP (%)
$strategy = $parsed->{loadStrategy} // $strategy;
$wou = $parsed->{weightOwnUse} // $wou;
my $tgt = $parsed->{loadTarget}; # Ladeziel-SoC in %
$tgt = $batoptsoc if(defined $tgt && $tgt < $batoptsoc); # Wert Battery_OptimumTargetSoC_XX beachten
$goalwh = defined $tgt
? sprintf "%.0f", ___batSocPercentToWh ($batinstcap, $tgt)
: $goalwh; # Ladeziel-SoC in Wh
}
my $goalpercent = sprintf "%.0f", ___batSocWhToPercent ($batinstcap, $goalwh); # Ladeziel in %
my $barrierSoCWh = sprintf "%.0f", ___batSocPercentToWh ($batinstcap, $barrierSoC);
my $goalpercent = sprintf "%.0f", ___batSocWhToPercent ($batinstcap, $goalwh); # Ladeziel in %
## generelle Ladeabbruchbedingung evaluieren
##############################################
@@ -11932,6 +11938,7 @@ sub _batChargeMgmt {
Log3 ($name, 1, "$name DEBUG> ChargeMgmt Bat $bn - selected charging strategy: $strategy");
Log3 ($name, 1, "$name DEBUG> ChargeMgmt Bat $bn - General load termination condition: $labortCond");
Log3 ($name, 1, "$name DEBUG> ChargeMgmt Bat $bn - control time Slot - Slot start: $lcstart, Slot end: $lcend");
Log3 ($name, 1, "$name DEBUG> ChargeMgmt Bat $bn - control barrier SoC - $barrierSoC % / $barrierSoCWh Wh");
Log3 ($name, 1, "$name DEBUG> ChargeMgmt Bat $bn - Battery efficiency used: ".($befficiency * 100)." %");
Log3 ($name, 1, "$name DEBUG> ChargeMgmt Bat $bn - weighted self-consumption: $wou %");
Log3 ($name, 1, "$name DEBUG> ChargeMgmt Bat $bn - charging target: $goalpercent % / $goalwh Wh");
@@ -12017,8 +12024,9 @@ sub _batChargeMgmt {
if ( !$num && ($pvCu - $curcon) >= $inplim ) {$crel = 1} # Ladefreigabe wenn akt. PV Leistung - Abschläge >= WR-Leistungsbegrenzung
if ( !$bpin && $gfeedin > $feedinlim ) {$crel = 1} # V 1.49.6 Ladefreigabe wenn akt. keine Bat-Ladung UND akt. Einspeisung > Einspeiselimit der Anlage
if ( $bpin && ($gfeedin - $bpin) > $feedinlim ) {$crel = 1} # V 1.49.6 Ladefreigabe wenn akt. Bat-Ladung UND Eispeisung - Bat-Ladung > Einspeiselimit der Anlage
if ( !$cgbt ) {$crel = 1} # Ladefreigabe wenn kein BatSoc-Management
if ( !$lcintime ) {$crel = 1} # Ladefreigabe wenn nicht innerhalb Zeitslot für Ladesteuerung
if ( !$cgbt ) {$crel = 1} # generelle Ladefreigabe wenn kein BatSoc/Lade-Management
if ( !$lcintime ) {$crel = 1} # generelle Ladefreigabe wenn nicht innerhalb Zeitslot für Ladesteuerung
if ( $csocwh <= $barrierSoCWh) {$crel = 1} # generelle Ladefreigabe wenn aktueller SoC <= Barriere-SoC
if ( $labortCond ) {$crel = 0} # keine Ladefreigabe bei genereller Abbruchbedingung
# Steuerhash für optimimierte Ladeleistung erstellen
@@ -12037,6 +12045,7 @@ sub _batChargeMgmt {
$hsurp->{$fd}{$hod}{$bn}{bpinreduced} = $bpinreduced; # Standardwert bei <=lowSoC -> Anforderungsladung vom Grid
$hsurp->{$fd}{$hod}{$bn}{bpoutmax} = $bpoutmax; # max. mögliche Entladeleistung
$hsurp->{$fd}{$hod}{$bn}{lowSocwh} = $lowSocwh; # eingestellter lowSoC in Wh
$hsurp->{$fd}{$hod}{$bn}{barrierSoCWh} = $barrierSoCWh; # eingestellter Barriere SoC in Wh
$hsurp->{$fd}{$hod}{$bn}{batoptsocwh} = $batoptsocwh; # optimaler SoC in Wh
$hsurp->{$fd}{$hod}{$bn}{csocwh} = $csocwh; # aktueller SoC in Wh
$hsurp->{$fd}{$hod}{$bn}{otpMargin} = $otpMargin; # Sicherheitszuschlag für Berechnungen
@@ -12280,14 +12289,15 @@ sub __batChargeOptTargetPower {
}
for my $sbn (sort { $a <=> $b } @batteries) { # jede Batterie
my $bpinmax = $hsurp->{$hod}{$sbn}{bpinmax}; # Bat max. mögliche Ladelesitung
my $batinstcap = $hsurp->{$hod}{$sbn}{batinstcap}; # Kapa dieser Batterie
my $lowSocwh = $hsurp->{$hod}{$sbn}{lowSocwh}; # eingestellter lowSoc in Wh
my $batoptsocwh = $hsurp->{$hod}{$sbn}{batoptsocwh}; # optimaler SoC in Wh
my $csocwh = $hsurp->{$hod}{$sbn}{csocwh}; # aktueller SoC in Wh
my $bpinreduced = $hsurp->{$hod}{$sbn}{bpinreduced}; # Standardwert bei <=lowSoC -> Anforderungsladung vom Grid
my $befficiency = $hsurp->{$hod}{$sbn}{befficiency}; # Speicherwirkungsgrad
my $strategy = $hsurp->{$hod}{$sbn}{strategy}; # Ladestrategie
my $bpinmax = $hsurp->{$hod}{$sbn}{bpinmax}; # Bat max. mögliche Ladelesitung
my $batinstcap = $hsurp->{$hod}{$sbn}{batinstcap}; # Kapa dieser Batterie
my $lowSocwh = $hsurp->{$hod}{$sbn}{lowSocwh}; # eingestellter lowSoc in Wh
my $batoptsocwh = $hsurp->{$hod}{$sbn}{batoptsocwh}; # optimaler SoC in Wh
my $csocwh = $hsurp->{$hod}{$sbn}{csocwh}; # aktueller SoC in Wh
my $barrierSoCWh = $hsurp->{$hod}{$sbn}{barrierSoCWh}; # Barriere SoC in Wh
my $bpinreduced = $hsurp->{$hod}{$sbn}{bpinreduced}; # Standardwert bei <=lowSoC -> Anforderungsladung vom Grid
my $befficiency = $hsurp->{$hod}{$sbn}{befficiency}; # Speicherwirkungsgrad
my $strategy = $hsurp->{$hod}{$sbn}{strategy}; # Ladestrategie
# Initialisierung / Fortschreibung Prognose-SOC (Wh)
######################################################
@@ -12329,8 +12339,12 @@ sub __batChargeOptTargetPower {
if ($nhr eq '00') {
$diff = $diff / 60 * (60 - int $minute); # aktuelle (Rest)-Stunde -> zeitgewichteter Ladungsabfluß
$otp->{$sbn}{target} = $csocwh <= $lowSocwh ? $bpinreduced : $bpinmax;
$otp->{$sbn}{ratio} = 0;
$otp->{$sbn}{target} = $csocwh <= $lowSocwh
? $bpinreduced
: $csocwh <= $barrierSoCWh
? $bpinmax
: $bpinmax;
}
$runwh += $diff / $befficiency; # um Verbrauch reduzieren
@@ -12341,21 +12355,24 @@ sub __batChargeOptTargetPower {
next;
}
## weiter mit Überschuß
#########################
my $otpMargin = $hsurp->{$hod}{$sbn}{otpMargin};
my $fref = ___batFindMinPhWh ( $hsurp,
\@remaining_hods,
$remainingSurp,
$runwhneed,
$replacement,
$achievable
my $fref = ___batFindMinPhWh ( { hsurp => $hsurp,
hodsref => \@remaining_hods,
remainingSurp => $remainingSurp,
Ereq => $runwhneed,
replacement => $replacement,
achievable => $achievable,
befficiency => $befficiency
}
);
my $limpower = $strategy eq 'optPower'
? min ($fref->{ph}, $spls) # Ladeleistung auf den kleineren Wert begrenzen (es kommen Nachberechnungen)
: $fref->{ph};
$limpower = $limpower // 0 > 0 ? $limpower / $befficiency : 0; # Zielleistung mit Batterie Effizienzgrad erhöhen
$limpower = $bpinmax if(!$hsurp->{$hod}{$sbn}{lcintime});
$limpower = max ($limpower, $bpinreduced); # Mindestladeleistung bpinreduced sicherstellen
@@ -12366,8 +12383,7 @@ sub __batChargeOptTargetPower {
my $pneedmin = $limpower * (1 + $otpMargin / 100); # optPower: Sicherheitsaufschlag
if ($strategy eq 'smartPower') {
($pneedmin) = ___batAdjustPowerByMargin ($name, # smartPower: Sicherheitsaufschlag abfallend proportional zum linearen Überschuss
$limpower,
($pneedmin) = ___batAdjustPowerByMargin ($limpower, # smartPower: Sicherheitsaufschlag abfallend proportional zum linearen Überschuss
$bpinmax,
$runwhneed,
$otpMargin,
@@ -12377,14 +12393,15 @@ sub __batChargeOptTargetPower {
$pneedmin = sprintf "%.0f", $pneedmin;
$pneedmin = min ($pneedmin, $bpinmax); # Begrenzung auf max. mögliche Batterieladeleistung
$pneedmin = max ($pneedmin, 0);
$hsurp->{$hod}{$sbn}{pneedmin} = $pneedmin > 0 ? $pneedmin : 0; # Ladeleistung abhängig von Ziel-SoC Erfüllung
$hsurp->{$hod}{$sbn}{pneedmin} = $pneedmin;
## NextHour 00 (aktuelle Stunde) behandeln
############################################
if ($nhr eq '00') {
my $target = $limpower > 0 ? $limpower / $befficiency : 0; # Zielleistung mit Batterie Effizienzgrad erhöhen
my $target = $limpower;
if ($achievable) { # Tagesziel erreichbar: Basisziel um otpMargin% erhöhen
$target *= 1 + ($otpMargin / 100); # optPower: Sicherheitsaufschlag
}
@@ -12393,8 +12410,7 @@ sub __batChargeOptTargetPower {
}
if ($strategy eq 'smartPower') { # smartPower: Sicherheitsaufschlag linear absenkend
($target, $ratio) = ___batAdjustPowerByMargin ($name, # smartPower: agressivere Ladeleistung, Sicherheitsaufschlag abfallend proportional zum linearen Überschuss
$limpower,
($target, $ratio) = ___batAdjustPowerByMargin ($limpower, # smartPower: agressivere Ladeleistung, Sicherheitsaufschlag abfallend proportional zum linearen Überschuss
$bpinmax,
$runwhneed,
$otpMargin,
@@ -12410,17 +12426,24 @@ sub __batChargeOptTargetPower {
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 Einspeisung - Bat-Ladung > Einspeiselimit der Anlage
$target = sprintf "%.0f", max ($target, $inc); # Einspeiselimit berücksichtigen
$target = min (($csocwh <= $lowSocwh ? $bpinreduced : $bpinmax), $target); # 2. Begrenzung auf max. mögliche Batterieleistung bzw. bpinreduced bei Unterschreitung lowSoc
my $lowph = $csocwh <= $lowSocwh
? $bpinreduced
: $bpinmax;
$target = max ($target, $inc); # Einspeiselimit berücksichtigen
$target = min ($target, $lowph); # Begrenzung auf diverse Limits
$target = sprintf "%.0f", $target;
$otp->{$sbn}{target} = $target;
}
$diff = min ($spls, $hsurp->{$hod}{$sbn}{pneedmin}); # kleinster Wert aus PV-Überschuß oder Ladeleistungsbegrenzung
## Fortschreibung
###################
if ($nhr eq '00') { $diff = $otp->{$sbn}{target} / 60 * (60 - int $minute) } # aktuelle (Rest)-Stunde -> zeitgewichteter Ladungszufluß
else { $diff = min ($spls, $hsurp->{$hod}{$sbn}{pneedmin}) } # kleinster Wert aus PV-Überschuß oder Ladeleistungsbegrenzung
if ($nhr eq '00') { # aktuelle (Rest)-Stunde -> zeitgewichteter Ladungszufluß
$diff = $spls / 60 * (60 - int $minute);
}
$diff = sprintf "%.0f", $diff;
$runwh = min ($goalwh, $runwh + $diff * $befficiency); # Endwert Prognose
$runwh = ___batClampValue ($runwh, $lowSocwh, $batoptsocwh, $batinstcap); # runwh begrenzen
@@ -12443,23 +12466,22 @@ return ($hsurp, $otp);
# Forum: https://forum.fhem.de/index.php?msg=1349579
################################################################
sub ___batAdjustPowerByMargin {
my ($name, $limpower, $pinmax, $whneed, $otpMargin, $remainingSurp) = @_;
my ($limpower, $pinmax, $whneed, $otpMargin, $remainingSurp) = @_;
my $pow;
my $ratio = 0;
$ratio = $remainingSurp * 100 / $whneed if($whneed);
return ($pinmax, $ratio) if($limpower == $pinmax);
return ($limpower * (1 + $otpMargin / 100), $ratio) if($limpower == 0 || !$otpMargin || $ratio >= 100 + $otpMargin);
return ($limpower, $ratio) if(!defined $whneed || $whneed <= 0);
$ratio = $remainingSurp * 100 / $whneed;
$limpower = min ($limpower, $pinmax); # limpower !> pinmax um invertierte Interpolation zu vermeiden
if ($limpower <= 0 || !$otpMargin) {$pow = $limpower}
elsif ($limpower == $pinmax || $ratio <= 100) {$pow = $pinmax}
elsif ($ratio >= 100 + $otpMargin) {$pow = $limpower}
else {$pow = $pinmax - ($pinmax - $limpower) * ($ratio - 100) / $otpMargin}
if ($ratio <= 100) {
$pow = $pinmax;
}
else {
$pow = $pinmax - ($pinmax - $limpower) * ($ratio - 100) / $otpMargin;
}
return ($pow, $ratio);
return ($pow, $ratio);
}
################################################################
@@ -12510,9 +12532,17 @@ return $value;
# - 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, $hodsref, $remainingSurp, $Ereq, $replacement, $achievable) = @_;
my $paref = shift;
my $hsurp = $paref->{hsurp};
my $hodsref = $paref->{hodsref};
my $remainingSurp = $paref->{remainingSurp};
my $Ereq = $paref->{Ereq};
my $replacement = $paref->{replacement};
my $achievable = $paref->{achievable};
my $befficiency = $paref->{befficiency};
my @hods = @$hodsref;
my $low = 0;
@@ -12531,7 +12561,7 @@ sub ___batFindMinPhWh {
}
while (($high - $low) > $eps) {
last if ++$loop > $max_iter;
last if(++$loop > $max_iter);
my $mid = ($low + $high) / 2;
my $charged = 0;
@@ -12540,6 +12570,7 @@ sub ___batFindMinPhWh {
my $nhr = $hsurp->{$hod}{nhr};
next if(!defined $nhr);
my $cap = $nhr eq '00' ? int $replacement : $hsurp->{$hod}{surplswh};
$cap *= $befficiency;
$charged += min ($mid, $cap);
}