diff --git a/fhem/contrib/DS_Starter/76_SolarForecast.pm b/fhem/contrib/DS_Starter/76_SolarForecast.pm
index 5cebdc0e8..166692252 100644
--- a/fhem/contrib/DS_Starter/76_SolarForecast.pm
+++ b/fhem/contrib/DS_Starter/76_SolarForecast.pm
@@ -161,7 +161,7 @@ BEGIN {
# Versions History intern
my %vNotesIntern = (
"1.59.4" => "13.10.2025 new subs, ctrlBatSocManagementXX: new key loadTarget, replace __batCapShareFactor by __batDeficitShareFactor ".
- "__batChargeOptTargetPower: use pinmax if achievable==0 ",
+ "__batChargeOptTargetPower: use pinmax if achievable==0, new ctrlBatSocManagementXX->stepSoC key ",
"1.59.3" => "10.10.2025 ___batChargeSaveResults: fix writing 'rcdchargebatXX' ",
"1.59.2" => "09.10.2025 one more fix of color filling of svg icon ",
"1.59.1" => "08.10.2025 fixed transfer at day change, optimal SoC consideration in SoC forecast for optPower strategy ".
@@ -7532,6 +7532,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 },
+ 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 },
careCycle => { comp => '\d+', must => 0, act => 0 },
@@ -11372,10 +11373,7 @@ sub _batSocTarget {
my ($err, $badev, $h) = isDeviceValid ( { name => $name, obj => 'setupBatteryDev'.$bn, method => 'attr' } );
next if($err);
- my $oldd2care = CircularVal ($name, 99, 'days2care'.$bn, 0);
- my $ltsmsr = CircularVal ($name, 99, 'lastTsMaxSocRchd'.$bn, undef);
- my $soc = BatteryVal ($name, $bn, 'bcharge', 0); # aktuelle Ladung in %
- my $batinstcap = BatteryVal ($name, $bn, 'binstcap', 0); # installierte Batteriekapazität Wh
+ my $batinstcap = BatteryVal ($name, $bn, 'binstcap', 0); # installierte Batteriekapazität Wh
if (!$batinstcap) {
Log3 ($name, 1, "$name - WARNING - Attribute ctrlBatSocManagement${bn} is active, but required key 'cap' is not set. Go to Next...");
@@ -11386,17 +11384,37 @@ sub _batSocTarget {
my $lowSoc = $parsed->{lowSoc};
my $upSoc = $parsed->{upSoc};
my $maxSoc = $parsed->{maxSoc};
+ my $stepSoc = $parsed->{stepSoc};
my $careCycle = $parsed->{careCycle};
if (!$lowSoc || !$upSoc) {
Log3 ($name, 1, "$name - WARNING - Attribute ctrlBatSocManagement${bn} is active, but required keys 'lowSoc' and 'upSoC' are not set. Go to Next...");
next;
}
+
+ if (!$stepSoc) {
+ debugLog ($paref, 'batteryManagement', "Bat $bn SoC Step1 - The SoC-Management is switched off. Battery_OptimumTargetSoC_$bn is set to lowSoC and Battery_ChargeRequest_$bn to '0'.");
+
+ ## pvHistory/Readings schreiben
+ #################################
+ writeToHistory ( { paref => $paref, key => 'batsetsoc'.$bn, val => $lowSoc, hour => 99 } );
+ storeReading ('Battery_OptimumTargetSoC_'.$bn, $lowSoc.' %');
+ storeReading ('Battery_ChargeRequest_'.$bn, 0);
+
+ next;
+ }
+
+ my $oldd2care = CircularVal ($name, 99, 'days2care'.$bn, 0);
+ my $ltsmsr = CircularVal ($name, 99, 'lastTsMaxSocRchd'.$bn, undef);
+ my $soc = BatteryVal ($name, $bn, 'bcharge', 0); # aktuelle Ladung in %
$paref->{batnmb} = $bn;
$paref->{careCycle} = $careCycle;
__batSaveSocKeyFigures ($paref) if(!$ltsmsr || $soc >= $maxSoc || $soc >= MAXSOCDEF || $oldd2care < 0);
+
+ delete $paref->{batnmb};
+ delete $paref->{careCycle};
my $nt = '';
my $chargereq = 0; # Ladeanforderung wenn SoC unter Minimum SoC gefallen ist
@@ -11406,8 +11424,8 @@ sub _batSocTarget {
my $batymaxsoc = HistoryVal ($name, $yday, 99, 'batmaxsoc'.$bn, 0); # gespeicherter max. SOC des Vortages
my $batysetsoc = HistoryVal ($name, $yday, 99, 'batsetsoc'.$bn, $lowSoc); # gespeicherter SOC Sollwert des Vortages
- $target = $batymaxsoc < $maxSoc ? $batysetsoc + BATSOCCHGDAY :
- $batymaxsoc >= $maxSoc ? $batysetsoc - BATSOCCHGDAY :
+ $target = $batymaxsoc < $maxSoc ? $batysetsoc + $stepSoc :
+ $batymaxsoc >= $maxSoc ? $batysetsoc - $stepSoc :
$batysetsoc; # neuer Min SOC für den laufenden Tag
## erwartete PV ermitteln & Anteilsfaktor Bat anwenden
@@ -11422,14 +11440,14 @@ sub _batSocTarget {
my $pvexpect = $sf * $pvexpraw;
if ($debug =~ /batteryManagement/xs) {
- Log3 ($name, 1, "$name DEBUG> Bat $bn SoC Step1 - basics -> Battery share factor of total capacity: $sf");
+ Log3 ($name, 1, "$name DEBUG> Bat $bn SoC Step1 - basics -> Battery share factor of total required load: $sf");
Log3 ($name, 1, "$name DEBUG> Bat $bn SoC Step1 - basics -> Expected energy for charging raw: $pvexpraw Wh");
Log3 ($name, 1, "$name DEBUG> Bat $bn SoC Step1 - basics -> Expected energy for charging after application Share factor: $pvexpect Wh");
Log3 ($name, 1, "$name DEBUG> Bat $bn SoC Step1 - compare with SoC history -> preliminary new Target: $target %");
}
- ## Pflege-SoC (Soll SoC MAXSOCDEF bei BATSOCCHGDAY % Steigerung p. Tag)
- ###########################################################################
+ ## Pflege-SoC (Soll SoC MAXSOCDEF bei $stepSoc % Steigerung p. Tag)
+ #####################################################################
my $sunset = CurrentVal ($name, 'sunsetTodayTs', $t);
my $delayts = $sunset - 5400; # Pflege-SoC/Erhöhung SoC erst ab 1,5h vor Sonnenuntergang berechnen/anwenden
my $la = '';
@@ -11444,11 +11462,15 @@ sub _batSocTarget {
$whneed = sprintf "%.0f", $whneed;
if ($t > $delayts || $pvexpect < $whneed || !$days2care) {
+ $paref->{batnmb} = $bn;
$paref->{days2care} = $days2care;
+
__batSaveSocKeyFigures ($paref);
+
delete $paref->{days2care};
+ delete $paref->{batnmb};
- $careSoc = $maxSoc - ($days2care * BATSOCCHGDAY); # Pflege-SoC um rechtzeitig den $maxsoc zu erreichen bei BATSOCCHGDAY % Steigerung pro Tag
+ $careSoc = $maxSoc - ($days2care * $stepSoc); # Pflege-SoC um rechtzeitig den $maxsoc zu erreichen bei $stepSoc % Steigerung pro Tag
$careSoc = $careSoc < $lowSoc ? $lowSoc : $careSoc;
if ($careSoc >= $target) {
@@ -11513,14 +11535,14 @@ sub _batSocTarget {
debugLog ($paref, 'batteryManagement', "Bat $bn SoC Step4 - basics -> docare: $docare, lowSoc: $lowSoc %, upSoc: $upSoc %");
debugLog ($paref, 'batteryManagement', "Bat $bn SoC Step4 - observe low/up limits -> Target: $target %");
- ## auf BATSOCCHGDAY Schritte anpassen (40,45,50,...)
- ######################################################
- my $flo = floor ($target / BATSOCCHGDAY);
- my $rmn = $target - ($flo * BATSOCCHGDAY);
- my $add = $rmn <= 2.5 ? 0 : BATSOCCHGDAY;
- $target = ($flo * BATSOCCHGDAY) + $add;
+ ## auf $stepSoc Schritte anpassen (40,45,50,...)
+ ##################################################
+ my $flo = floor ($target / $stepSoc);
+ my $rmn = $target - ($flo * $stepSoc);
+ my $add = $rmn <= 2.5 ? 0 : $stepSoc;
+ $target = ($flo * $stepSoc) + $add;
- debugLog ($paref, 'batteryManagement', "Bat $bn SoC Step5 - rounding the SoC to steps of ".BATSOCCHGDAY." % -> Target: $target %");
+ debugLog ($paref, 'batteryManagement', "Bat $bn SoC Step5 - rounding the SoC to steps of ".$stepSoc." % -> Target: $target %");
## Ladeanforderung
####################
@@ -11536,9 +11558,6 @@ sub _batSocTarget {
writeToHistory ( { paref => $paref, key => 'batsetsoc'.$bn, val => $target, hour => 99 } );
storeReading ('Battery_OptimumTargetSoC_'.$bn, $target.' %');
storeReading ('Battery_ChargeRequest_'.$bn, $chargereq);
-
- delete $paref->{batnmb};
- delete $paref->{careCycle};
}
return;
@@ -11559,6 +11578,7 @@ sub __parseAttrBatSoc {
lowSoc => $ph->{lowSoc},
upSoc => $ph->{upSoC},
maxSoc => $ph->{maxSoC} // MAXSOCDEF, # optional (default: MAXSOCDEF)
+ stepSoc => $ph->{stepSoC} // BATSOCCHGDAY, # mögliche SoC-Änderung pro Tag
careCycle => $ph->{careCycle} // CARECYCLEDEF, # Ladungszyklus (Maintenance) für maxSoC in Tagen
lcslot => $ph->{lcSlot},
loadAbort => $ph->{loadAbort},
@@ -27212,7 +27232,7 @@ to ensure that the system configuration is correct.
-