76_SolarForecast: contrib V1.58.6

git-svn-id: https://svn.fhem.de/fhem/trunk@30341 2b470e98-0d58-463d-a4d8-8e2adae1ed80
This commit is contained in:
DS_Starter
2025-10-01 20:26:21 +00:00
parent ab3f9d0e7f
commit 376df71804

View File

@@ -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.
<tr><td> <b>today</b> </td><td>has value '1' if start date on current day </td></tr>
<tr><td> <b>rcdchargebatXX</b> </td><td>Charging recommendation with full power for battery XX (1 - Yes, 0 - No) </td></tr>
<tr><td> <b>lcintimebatXX</b> </td><td>Charge management for battery XX is activated or will be activated (1 - Yes, 0 - No) </td></tr>
<tr><td> <b>strategybatXX</b> </td><td>the selected charging strategy </td></tr>
<tr><td> <b>rr1c</b> </td><td>Total precipitation during the last hour kg/m2 </td></tr>
<tr><td> <b>rrange</b> </td><td>range of total rain </td></tr>
<tr><td> <b>socXX</b> </td><td>current (NextHour00) or predicted SoC (%) of battery XX </td></tr>
@@ -26334,6 +26456,7 @@ to ensure that the system configuration is correct.
<tr><td> <b>feedprice</b> </td><td>Remuneration for the feed-in of one kWh. The currency of the price is defined in the setupMeterDev. </td></tr>
<tr><td> <b>hourscsmeXX</b> </td><td>total active hours of the day from ConsumerXX </td></tr>
<tr><td> <b>lcintimebatXX</b> </td><td>the charge management for battery XX was activated (1 - Yes, 0 - No) </td></tr>
<tr><td> <b>strategybatXX</b> </td><td>the selected charging strategy </td></tr>
<tr><td> <b>minutescsmXX</b> </td><td>total active minutes in the hour of ConsumerXX </td></tr>
<tr><td> <b>plantderated</b> </td><td>Timestamp of the first curtailment event of the system in this hour, otherwise '0' </td></tr>
<tr><td> <b>pprlXX</b> </td><td>Energy generation of producer XX (see attribute setupOtherProducerXX) in the hour (Wh) </td></tr>
@@ -26918,7 +27041,7 @@ to ensure that the system configuration is correct.
<a id="SolarForecast-attr-ctrlBatSocManagementXX" data-pattern="ctrlBatSocManagement.*"></a>
<li><b>ctrlBatSocManagementXX lowSoc=&lt;Value&gt; upSoC=&lt;Value&gt; [maxSoC=&lt;Value&gt;] [careCycle=&lt;Value&gt;]
[lcSlot=&lt;hh:mm&gt;-&lt;hh:mm&gt;] [loadAbort=&lt;SoC1&gt;:&lt;MinPwr&gt;:&lt;SoC2&gt;]
[safetyMargin=&lt;Value&gt;[:&lt;Value&gt;]] </b> <br><br>
[safetyMargin=&lt;Value&gt;[:&lt;Value&gt;]] [loadStrategy=&lt;Value&gt;] </b> <br><br>
If a battery device (setupBatteryDevXX) is installed, this attribute activates the battery SoC and charge management
for this battery device. <br>
@@ -26971,6 +27094,11 @@ to ensure that the system configuration is correct.
<tr><td> </td><td>surcharge used to calculate the optimized load capacity. Both values are percentages. </td></tr>
<tr><td> </td><td>Value: <b>0..100[:0..100]</b> (integers) </td></tr>
<tr><td> </td><td> </td></tr>
<tr><td> <b>loadStrategy</b> </td><td>The selected charging strategy is taken into account when displaying the battery in bar graph. </td></tr>
<tr><td> </td><td>The generation of tax readings is not affected. The specification is optional. </td></tr>
<tr><td> </td><td>For more information on selecting a strategy, see german <a href="https://wiki.fhem.de/wiki/SolarForecast_-_Solare_Prognose_(PV_Erzeugung)_und_Verbrauchersteuerung#Welche_Ladestrategie_soll_ich_w%C3%A4hlen?_-_eine_M%C3%B6glichkeit_zur_Best-Practice_Findung_mit_Codebeispiel">Wiki</a>. </td></tr>
<tr><td> </td><td>Value: <b>loadRelease or optPower</b>, default: loadRelease </td></tr>
<tr><td> </td><td> </td></tr>
</table>
</ul>
<br>
@@ -28960,6 +29088,7 @@ die ordnungsgemäße Anlagenkonfiguration geprüft werden.
<tr><td> <b>today</b> </td><td>hat Wert '1' wenn Startdatum am aktuellen Tag </td></tr>
<tr><td> <b>rcdchargebatXX</b> </td><td>Aufladeempfehlung mit voller Leistung für Batterie XX (1 - Ja, 0 - Nein) </td></tr>
<tr><td> <b>lcintimebatXX</b> </td><td>Lademanagement für Batterie XX ist aktiviert bzw. wird aktiviert sein (1 - Ja, 0 - Nein) </td></tr>
<tr><td> <b>strategybatXX</b> </td><td>die gewählte Ladestrategie </td></tr>
<tr><td> <b>rr1c</b> </td><td>Gesamtniederschlag in der letzten Stunde kg/m2 </td></tr>
<tr><td> <b>rrange</b> </td><td>Bereich des Gesamtniederschlags </td></tr>
<tr><td> <b>socXX</b> </td><td>aktueller (NextHour00) oder prognostizierter SoC (%) der Batterie XX </td></tr>
@@ -29012,6 +29141,7 @@ die ordnungsgemäße Anlagenkonfiguration geprüft werden.
<tr><td> <b>avgcycmntscsmXX</b> </td><td>durchschnittliche Dauer eines Einschaltzyklus des Tages von ConsumerXX in Minuten </td></tr>
<tr><td> <b>hourscsmeXX</b> </td><td>Summe Aktivstunden des Tages von ConsumerXX </td></tr>
<tr><td> <b>lcintimebatXX</b> </td><td>das Lademanagement für Batterie XX war aktiviert (1 - Ja, 0 - Nein) </td></tr>
<tr><td> <b>strategybatXX</b> </td><td>die gewählte Ladestrategie </td></tr>
<tr><td> <b>minutescsmXX</b> </td><td>Summe Aktivminuten in der Stunde von ConsumerXX </td></tr>
<tr><td> <b>plantderated</b> </td><td>Zeitstempel des ersten Abregelungsvorfalls der Anlage in dieser Stunde, sonst '0' </td></tr>
<tr><td> <b>pprlXX</b> </td><td>Energieerzeugung des Produzenten XX (siehe Attribut setupOtherProducerXX) in der Stunde (Wh) </td></tr>
@@ -29595,7 +29725,7 @@ die ordnungsgemäße Anlagenkonfiguration geprüft werden.
<a id="SolarForecast-attr-ctrlBatSocManagementXX" data-pattern="ctrlBatSocManagement.*"></a>
<li><b>ctrlBatSocManagementXX lowSoc=&lt;Wert&gt; upSoC=&lt;Wert&gt; [maxSoC=&lt;Wert&gt;] [careCycle=&lt;Wert&gt;]
[lcSlot=&lt;hh:mm&gt;-&lt;hh:mm&gt;] [loadAbort=&lt;SoC1&gt;:&lt;MinPwr&gt;:&lt;SoC2&gt;]
[safetyMargin=&lt;Wert&gt;[:&lt;Wert&gt;]] </b> <br><br>
[safetyMargin=&lt;Wert&gt;[:&lt;Wert&gt;]] [loadStrategy=&lt;Wert&gt;] </b> <br><br>
Sofern ein Batterie Device (setupBatteryDevXX) installiert ist, aktiviert dieses Attribut das Batterie
SoC- und Lade-Management für dieses Batteriegerät. <br>
@@ -29649,6 +29779,11 @@ die ordnungsgemäße Anlagenkonfiguration geprüft werden.
<tr><td> </td><td>Zuschlag bei der Berechnung der optimierten Ladeleistung. Beide Angaben sind Prozentwerte. </td></tr>
<tr><td> </td><td>Wert: <b>0..100[:0..100]</b> (Ganzzahlen) </td></tr>
<tr><td> </td><td> </td></tr>
<tr><td> <b>loadStrategy</b> </td><td>Bei der Anzeige der Batterie in der Balkengrafik wird die gewählte Ladestrategie berücksichtigt.</td></tr>
<tr><td> </td><td>Die Generierung der Steuerreadings wird nicht beeinflusst. Die Angabe ist optional. </td></tr>
<tr><td> </td><td>Weitere Informationen zur Auswahl der Strategie siehe <a href="https://wiki.fhem.de/wiki/SolarForecast_-_Solare_Prognose_(PV_Erzeugung)_und_Verbrauchersteuerung#Welche_Ladestrategie_soll_ich_w%C3%A4hlen?_-_eine_M%C3%B6glichkeit_zur_Best-Practice_Findung_mit_Codebeispiel">Wiki</a>. </td></tr>
<tr><td> </td><td>Wert: <b>loadRelease oder optPower</b>, default: loadRelease </td></tr>
<tr><td> </td><td> </td></tr>
</table>
</ul>
<br>