76_SolarForecast: contrib V1.58.6

git-svn-id: https://svn.fhem.de/fhem/trunk@30331 2b470e98-0d58-463d-a4d8-8e2adae1ed80
This commit is contained in:
DS_Starter
2025-09-27 16:47:46 +00:00
parent 9d8965159b
commit ffb4393018

View File

@@ -160,6 +160,8 @@ 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.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 ",
@@ -7465,7 +7467,6 @@ sub _attrBatteryDev { ## no critic "not used"
readingsDelete ($hash, 'Current_PowerBatOut_'.$bn);
readingsDelete ($hash, 'Current_BatCharge_'.$bn);
readingsDelete ($hash, 'Battery_ChargeOptTargetPower_'.$bn);
readingsDelete ($hash, 'Battery_ChargeRecommended_'.$bn);
readingsDelete ($hash, 'Battery_ChargeUnrestricted_'.$bn);
readingsDelete ($hash, 'Battery_ChargeRequest_'.$bn);
readingsDelete ($hash, 'Battery_OptimumTargetSoC_'.$bn);
@@ -8915,6 +8916,11 @@ sub centralTask {
}
}
for my $bn (1..MAXBATTERIES) { # 02.10.
$bn = sprintf "%02d", $bn;
readingsDelete ($hash, 'Battery_ChargeRecommended_'.$bn);
}
##########################################################################################################################
@@ -11593,6 +11599,10 @@ sub _batChargeMgmt {
my $bpin = CurrentVal ($name, 'batpowerinsum', 0); # aktuelle Batterie Ladeleistung (Summe über alle Batterien)
my $gfeedin = CurrentVal ($name, 'gridfeedin', 0); # aktuelle Netzeinspeisung
my $inplim = 0;
my $hsurp = {}; # Hashreferenz Überschuß
my $hsoc = {}; # Hashreferenz
my $values = {}; # Hashreferenz
## Inverter Limits ermitteln
##############################
@@ -11616,10 +11626,7 @@ sub _batChargeMgmt {
debugLog ($paref, 'batteryManagement', "ChargeMgmt - The limit for grid feed-in is: $feedinlim W");
## Schleife über alle Batterien
#################################
my %hsoc; # Hilfshash
my $hsurp = {}; # Hashreferenz Überschuß
#################################
for my $bn (1..MAXBATTERIES) { # für jede Batterie
$bn = sprintf "%02d", $bn;
@@ -11691,8 +11698,8 @@ sub _batChargeMgmt {
my $socwh = sprintf "%.0f", ($batinstcap * $csoc / 100); # aktueller SoC in Wh
my $whneed = $batinstcap - $socwh;
# Debug Log
#############
# Debuglog
############
if ($paref->{debug} =~ /batteryManagement/) {
Log3 ($name, 1, "$name DEBUG> Bat $bn ChargeMgmt - General load termination condition: $labortCond");
Log3 ($name, 1, "$name DEBUG> Bat $bn ChargeMgmt - control time Slot - Slot start: $lcstart, Slot end: $lcend");
@@ -11744,8 +11751,8 @@ sub _batChargeMgmt {
$tomconfc = sprintf "%.0f", ($sf * $tomconfc);
$tompvfc = sprintf "%.0f", ($sf * $tompvfc);
## PV-Überschuß und (Rest)Tagesüberschuß
##########################################
## PV-Überschuß und (Rest)Tagesüberschuß heute/morgen
#######################################################
if ($today) { # heutiger Tag
$confcss -= $confc; # Verbrauch bis Sonnenuntergang - Verbrauch Fc aktuelle Stunde
$confcss = 0 if($confcss < 0);
@@ -11761,7 +11768,7 @@ sub _batChargeMgmt {
}
$spday = 0 if($spday < 0); # PV Überschuß Prognose bis Sonnenuntergang
my $surpls = sprintf "%.0f", ($pvfc - $confc); # effektiver PV Überschuß bzw. effektiver Verbrauch wenn < 0
my $surpls = sprintf "%.0f", ($pvfc - $confc); # PV-Überschuß der Stunde, wichtig keine Nachkommastellen!
## Steuerung nach Ladefreigabe
################################
@@ -11790,8 +11797,8 @@ sub _batChargeMgmt {
}
## SOC-Prognose
################# # change V 1.47.0
my $fceff = $surpls; # wichtig keine Nachkommastellen!
#################
my $fceff = $surpls; # effektiver PV Überschuß bzw. effektiver Verbrauch wenn < 0
$fceff = $fceff > 0 ? ($fceff >= $bpinmax ? $bpinmax : $fceff) :
$fceff < 0 ? ($fceff <= $bpoutmax * -1 ? $bpoutmax * -1 : $fceff) :
@@ -11807,13 +11814,12 @@ sub _batChargeMgmt {
$socwh = sprintf "%.0f", $socwh;
my $progsoc = sprintf "%.1f", (100 * $socwh / $batinstcap); # Prognose SoC in %
__createNextHoursSFCReadings ( {name => $name,
nhr => $nhr,
bn => $bn,
progsoc => $progsoc
}
); # Readings NextHourXX_Bat_XX_ChargeForecast erstellen
## 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 : '-');
@@ -11824,50 +11830,80 @@ sub _batChargeMgmt {
$msg = "SoCfc: $progsoc % / $socwh Wh, whneed: $whneed, pvfc: $pvfc, roTomPV: $tompvfc, roTomCON: $tomconfc, SurpDay: $spday Wh, inTime: ".($cgbt ? $lcintime : '-');
}
}
else {
storeReading ('Battery_ChargeRecommended_'.$bn, $crel);
storeReading ('Battery_ChargeUnrestricted_'.$bn, $crel);
storeReading ('Battery_ChargeAbort_'.$bn, $labortCond) if ($loadAbort); # Ladeabbruchbedingung
}
$whneed = $batinstcap - $socwh;
$data{$name}{nexthours}{'NextHour'.$nhr}{'rcdchargebat'.$bn} = $crel;
$data{$name}{nexthours}{'NextHour'.$nhr}{'soc'.$bn} = $progsoc;
$data{$name}{nexthours}{'NextHour'.$nhr}{'lcintimebat'.$bn} = $lcintime if($cgbt); # Ladesteuerung "In Time", "nicht In Time" oder nicht verwendet
$hsoc{$nhr}{socprogwhsum} += $socwh; # Hilfshash Aufsummierung SoC-Prognose (Wh) über alle Batterien
# prognostizierten Daten in pvHistory speichern
#################################################
if ($today && $hod) { # heutiger Tag
writeToHistory ( { paref => $paref, key => 'batprogsoc'.$bn, val => $progsoc, hour => $hod } );
writeToHistory ( { paref => $paref, key => 'lcintimebat'.$bn, val => $lcintime, hour => $hod } ) if($cgbt);
}
my $stt = (split /[-:]/, $nhstt)[2];
$stt =~ s/\s/\//;
debugLog ($paref, 'batteryManagement', "Bat $bn ChargeLR $stt -> $crel ($msg)");
## Fortschreibung
###################
$whneed = $batinstcap - $socwh;
## Speicherung und Readings erstellen
#######################################
$values = { hsoc => $hsoc,
strategy => 'loadRelease',
num => $num,
crel => $crel,
bn => $bn,
labortCond => $labortCond,
loadAbort => $loadAbort,
nhr => $nhr,
progsoc => $progsoc,
lcintime => $lcintime,
cgbt => $cgbt,
socwh => $socwh,
today => $today,
hod => $hod,
};
___batChargeSaveResults ($paref, $values);
}
}
# leistungsoptimierte Beladungssteuerung
##########################################
$paref->{hsurp} = $hsurp;
my $hopt = __batChargeOptTargetPower ($paref);
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);
if ($hopt->{$k}{nhr} eq '00') { # Target für aktuelle Stunde
$needmin = $otp->{$bat}{target} // 0;
}
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
######################################################
for my $nhr (keys %hsoc) {
if (defined $hsoc{$nhr}{socprogwhsum}) {
$data{$name}{nexthours}{'NextHour'.$nhr}{socprogwhsum} = $hsoc{$nhr}{socprogwhsum};
for my $nhr (keys %{$hsoc}) {
if (defined $hsoc->{$nhr}{socprogwhsum}) {
$data{$name}{nexthours}{'NextHour'.$nhr}{socprogwhsum} = $hsoc->{$nhr}{socprogwhsum};
my $today = NexthoursVal ($name, 'NextHour'.$nhr, 'today', 0);
my $hod = NexthoursVal ($name, 'NextHour'.$nhr, 'hourofday', '');
if ($today && $hod) { # heutiger Tag
writeToHistory ( { paref => $paref, key => 'socprogwhsum', val => $hsoc{$nhr}{socprogwhsum}, hour => $hod } );
writeToHistory ( { paref => $paref, key => 'socprogwhsum', val => $hsoc->{$nhr}{socprogwhsum}, hour => $hod } );
}
}
}
@@ -11913,8 +11949,6 @@ sub __batChargeOptTargetPower {
my @batteries = grep { !/^(?:hod|surpls|nhr)$/xs } keys %{$hsurp->{24}};
my $otp;
## Kalkulation / Logik
########################
for my $shod (@sorted) {
my $spls = int $hsurp->{$shod}{surpls};
my $newshod = sprintf "%02d", (int $shod + 1);
@@ -12000,34 +12034,59 @@ sub __batChargeOptTargetPower {
$sphrs-- if($spls); # Reststunden mit Überschuß
}
## Debuglog
#############
if ($paref->{debug} =~ /batteryManagement/) {
Log3 ($name, 1, "$name DEBUG> ChargeOTP - The limit for grid feed-in is $fipl 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 %{$hsurp}) {
for my $bat (sort @batteries) {
my $sphrs = defined $hsurp->{$k}{$bat}{sphrs} ?
'('.($hsurp->{$k}{$bat}{sphrs} ? $hsurp->{$k}{$bat}{sphrs} : 1).' hrs)' :
'';
my $ssocwh = $hsurp->{$k}{$bat}{runwh} // '-';
my $otpMargin = $hsurp->{$k}{$bat}{otpMargin};
my $margin = defined $otpMargin ? $otpMargin : SFTYMARGIN_20;
my $spls = int $hsurp->{$k}{surpls};
my $needmin = max (BatteryVal ($name, $bat, 'bpinreduced', 0), $hsurp->{$k}{$bat}{pneedmin} // 0);
if ($hsurp->{$k}{nhr} eq '00') { # Target für aktuelle Stunde
$needmin = $otp->{$bat}{target} // 0;
}
Log3 ($name, 1, "$name DEBUG> Bat $bat ChargeOTP - hod: $k, Start SoC: $ssocwh Wh, Surplus: $spls Wh $sphrs, OTP: $needmin W, safety: $margin %");
}
return ($hsurp, $otp);
}
################################################################
# Speicherung Ergebnisse aus Batterie Lademanagement
################################################################
sub ___batChargeSaveResults {
my $paref = shift;
my $values = shift;
my $name = $paref->{name};
my $hsoc = $values->{hsoc};
my $strategy = $values->{strategy};
my $num = $values->{num};
my $crel = $values->{crel};
my $bn = $values->{bn};
my $labortCond = $values->{labortCond};
my $loadAbort = $values->{loadAbort};
my $nhr = $values->{nhr};
my $progsoc = $values->{progsoc};
my $lcintime = $values->{lcintime};
my $cgbt = $values->{cgbt}; # nur einmal bei 'loadRelease' mitgeben!
my $socwh = $values->{socwh};
my $today = $values->{today};
my $hod = $values->{hod};
if ($strategy eq 'loadRelease') { # nur in Schleife 'loadRelease' setzen
$data{$name}{nexthours}{'NextHour'.$nhr}{'rcdchargebat'.$bn} = $crel;
if (!$num) {
storeReading ('Battery_ChargeUnrestricted_'.$bn, $crel);
storeReading ('Battery_ChargeAbort_'.$bn, $labortCond) if ($loadAbort); # Ladeabbruchbedingung
}
}
return $hsurp;
if ($today && $hod) {
writeToHistory ( { paref => $paref, key => 'batprogsoc'.$bn, val => $progsoc, hour => $hod } );
writeToHistory ( { paref => $paref, key => 'lcintimebat'.$bn, val => $lcintime, hour => $hod } ) if($cgbt); # nur einmal bei 'loadRelease' setzen
}
__createNextHoursSFCReadings ( {name => $name,
nhr => $nhr,
bn => $bn,
progsoc => $progsoc
}
); # Readings NextHourXX_Bat_XX_ChargeForecast erstellen
$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}{'soc'.$bn} = $progsoc;
$hsoc->{$nhr}{socprogwhsum} += $socwh; # Hilfshash Aufsummierung SoC-Prognose (Wh) über alle Batterien
return;
}
################################################################
@@ -14316,23 +14375,25 @@ return;
# erstellen
################################################################
sub _calcReadingsTomorrowPVFc {
my $paref = shift;
my $name = $paref->{name};
my $paref = shift;
my $name = $paref->{name};
my $t = $paref->{t};
my $hash = $defs{$name};
my $h = $data{$name}{nexthours};
my $hods = AttrVal ($name, 'ctrlNextDayForecastReadings', '');
return if(!keys %{$h} || !$hods);
my $dt = timestringsFromOffset ($t, 86400);
my $tmoday = $dt->{day}; # Tomorrow Day (01..31)
for my $idx (sort keys %{$h}) {
my $today = NexthoursVal ($hash, $idx, 'today', 1);
next if($today); # aktueller Tag wird nicht benötigt
my $day = NexthoursVal ($hash, $idx, 'day', 'dd');
next if($day ne $tmoday); # aktueller Tag wird nicht benötigt
my $hod = NexthoursVal ($hash, $idx, 'hourofday', '');
next if(!$hod);
next if($hods !~ /$hod/xs); # diese Stunde des Tages soll nicht erzeugt werden
next if(!$hod || $hods !~ /$hod/xs); # diese Stunde des Tages soll nicht erzeugt werden
my $pvfc = NexthoursVal ($hash, $idx, 'pvfc', 0);