diff --git a/fhem/CHANGED b/fhem/CHANGED
index 0b9f3d2ec..ca4b36243 100644
--- a/fhem/CHANGED
+++ b/fhem/CHANGED
@@ -1,5 +1,6 @@
# Add changes at the top of the list. Keep it in ASCII, and 80-char wide.
# Do not insert empty lines here, update check depends on it
+ - feature: 76_SolarForecast: Version 1.51.4
- feature: 76_SolarForecast: Version 1.51.3
- feature: 76_SolarForecast: Version 1.51.2
- bugfix: 76_SMAInverter.pm: fix warning Versions History
diff --git a/fhem/FHEM/76_SolarForecast.pm b/fhem/FHEM/76_SolarForecast.pm
index 24ac73a10..acb68a551 100644
--- a/fhem/FHEM/76_SolarForecast.pm
+++ b/fhem/FHEM/76_SolarForecast.pm
@@ -160,6 +160,9 @@ BEGIN {
# Versions History intern
my %vNotesIntern = (
+ "1.51.4" => "27.04.2025 avoid warnings uninitialized value \$FW_ME, \$FW_subdir in line 14434 ".
+ "bugfix in attr .*Control, fix 'ERROR deleting file' if no consumers are registered ".
+ "save batprogsocXX to pvHistory, prepared for new Attr graphicControl ",
"1.51.3" => "22.04.2025 change battery text to 'load if above feed-in limit' ".
"transform set setupStringAzimuth, setupStringDeclination to attr setupStringAzimuth, setupStringDeclination ",
"1.51.2" => "21.04.2025 Attributes obsolet: graphicHeaderShow replaced by graphicSelect, Value 'none' of consumerControl->showLegend deleted ",
@@ -609,6 +612,7 @@ my @aconfigs = qw( aiControl
ctrlSpecialReadings
ctrlUserExitFn
disable
+ graphicControl
flowGraphicControl
graphicBeamWidth
graphicBeamHeightLevel1 graphicBeamHeightLevel2 graphicBeamHeightLevel3
@@ -736,6 +740,7 @@ my %hattr = ( # H
setupStringAzimuth => { fn => \&_attrstringAzimuth },
setupStringDeclination => { fn => \&_attrstringDeclination },
setupRoofTops => { fn => \&_attrRoofTops },
+ graphicControl => { fn => \&_attrgraphicControl },
flowGraphicControl => { fn => \&_attrflowGraphicControl },
aiControl => { fn => \&_attraiControl },
plantControl => { fn => \&_attrplantControl },
@@ -1466,6 +1471,11 @@ my %hfspvh = (
$hfspvh{'batoutthishour'.$bn}{storname} = 'batout'.$bn;
$hfspvh{'batoutthishour'.$bn}{validkey} = undef;
$hfspvh{'batoutthishour'.$bn}{fpar} = 'comp99';
+
+ $hfspvh{'batprogsoc'.$bn}{fn} = \&_storeVal; # Prognose-SOC des Tages
+ $hfspvh{'batprogsoc'.$bn}{storname} = 'batprogsoc'.$bn;
+ $hfspvh{'batprogsoc'.$bn}{validkey} = undef;
+ $hfspvh{'batprogsoc'.$bn}{fpar} = undef;
$hfspvh{'batmaxsoc'.$bn}{fn} = \&_storeVal; # max. erreichter SOC des Tages
$hfspvh{'batmaxsoc'.$bn}{storname} = 'batmaxsoc'.$bn;
@@ -1579,6 +1589,7 @@ sub Initialize {
"ctrlUserExitFn:textField-long ".
"disable:1,0 ".
"flowGraphicControl:textField-long ".
+ #"graphicControl:textField-long ".
"graphicBeamHeightLevel1 ".
"graphicBeamHeightLevel2 ".
"graphicBeamHeightLevel3 ".
@@ -1861,7 +1872,7 @@ sub _setconsumerImmediatePlanning { ## no critic "not used"
my $planstate = ConsumerVal ($hash, $c, 'planstate', '');
my $calias = ConsumerVal ($hash, $c, 'alias', '');
- writeCacheToFile ($hash, "consumers", $csmcache.$name); # Cache File Consumer schreiben
+ writeCacheToFile ($hash, 'consumers', $csmcache.$name); # Cache File Consumer schreiben
Log3 ($name, 3, qq{$name - Consumer "$calias" $planstate}) if($planstate);
@@ -2515,7 +2526,7 @@ sub _setreset { ## no critic "not used"
Log3($name, 3, qq{$name - roofIdentPair: all pair keys deleted});
}
- writeCacheToFile ($hash, 'solcastapi', $scpicache.$name); # Cache File SolCast API Werte schreiben
+ writeCacheToFile ($hash, 'solcastapi', $scpicache.$name); # Cache File SolCast API Werte schreiben
return;
}
@@ -2531,7 +2542,7 @@ sub _setreset { ## no critic "not used"
}
}
- writeCacheToFile ($hash, "consumers", $csmcache.$name); # Cache File Consumer schreiben
+ writeCacheToFile ($hash, 'consumers', $csmcache.$name, 'nolog'); # Cache File Consumer schreiben
}
if ($prop eq 'consumerMaster') { # Verbraucherhash löschen
@@ -2551,7 +2562,7 @@ sub _setreset { ## no critic "not used"
delete $paref->{c};
$data{$name}{current}{consumerCollected} = 0; # Consumer neu sammeln
- writeCacheToFile ($hash, "consumers", $csmcache.$name); # Cache File Consumer schreiben
+ writeCacheToFile ($hash, 'consumers', $csmcache.$name, 'nolog'); # Cache File Consumer schreiben
centralTask ($hash, 0);
}
@@ -6328,13 +6339,11 @@ sub _attrconsumerControl { ## no critic "not used"
showLegend => { comp => '(icon_top|icon_bottom|text_top|text_bottom)', act => 0 },
};
- for my $av (keys %{$valid}) {
- delete $data{$name}{current}{$av};
- }
-
my ($a, $h) = parseParams ($aVal);
if ($cmd eq 'set') {
+ ## 1. Durchlauf - Prüfungen
+ #############################
for my $key (keys %{$h}) {
if (!grep /^$key$/, keys %{$valid}) {
return qq{The key '$key' is not a valid key in attribute '$aName'};
@@ -6343,25 +6352,39 @@ sub _attrconsumerControl { ## no critic "not used"
my $comp = $valid->{$key}{comp};
next if(!$comp);
- if ($h->{$key} =~ /^$comp$/xs) {
- $data{$name}{current}{$key} = $h->{$key};
- }
- else {
+ if ($h->{$key} !~ /^$comp$/xs) {
return "The key '$key=$h->{$key}' is not specified correctly. Please refer to the command reference.";
}
}
+
+ ## 2. Durchlauf - Umsetzung
+ #############################
+ for my $av (keys %{$valid}) {
+ delete $data{$name}{current}{$av};
+ }
+
+ for my $key (keys %{$h}) {
+ $data{$name}{current}{$key} = $h->{$key};
+ }
+ }
+ else { # Current Keys mit Attribut löschen
+ for my $av (keys %{$valid}) {
+ delete $data{$name}{current}{$av};
+ }
}
for my $akey (keys %{$h}) { # von bestimmten Schlüsseln abhängige Aktionen ausführen
- next if(!$valid->{$akey}{act});
-
- $paref->{akey} = $akey;
- $paref->{keyval} = $h->{$akey};
-
- __attrKeyAction ($paref);
-
- delete $paref->{keyval};
- delete $paref->{akey};
+ if ($valid->{$akey}{act}) {
+ $paref->{akey} = $akey;
+ $paref->{keyval} = $h->{$akey};
+
+ my $err = __attrKeyAction ($paref);
+
+ delete $paref->{keyval};
+ delete $paref->{akey};
+
+ return $err if($err);
+ }
}
return;
@@ -6439,6 +6462,76 @@ sub _attrctrlDebug { ## no critic "not used"
return;
}
+################################################################
+# Attr graphicControl
+################################################################
+sub _attrgraphicControl { ## no critic "not used"
+ my $paref = shift;
+ my $name = $paref->{name};
+ my $aName = $paref->{aName};
+ my $aVal = $paref->{aVal};
+ my $cmd = $paref->{cmd};
+
+ my $valid = {
+ beamWidth => { comp => '([2-9][0-9]|100)', act => 0 },
+ hourCount => { comp => '([4-9]|1[0-9]|2[0-4])', act => 0 },
+ hourStyle => { comp => ':(0{1,2})', act => 0 },
+ headerDetail => { comp => '.*', act => 1 },
+ energyUnit => { comp => '(Wh|kWh)', act => 0 },
+ layoutType => { comp => '(single|double|diff)', act => 0 },
+ spaceSize => { comp => '\d+', act => 0 },
+ };
+
+ my ($a, $h) = parseParams ($aVal);
+
+ if ($cmd eq 'set') {
+ ## 1. Durchlauf - Prüfungen
+ #############################
+ for my $key (keys %{$h}) {
+ if (!grep /^$key$/, keys %{$valid}) {
+ return qq{The key '$key' is not a valid key in attribute '$aName'};
+ }
+
+ my $comp = $valid->{$key}{comp};
+ next if(!$comp);
+
+ if ($h->{$key} =~ /^$comp$/xs) {
+ if ($valid->{$key}{act}) {
+ $paref->{akey} = $key;
+ $paref->{keyval} = $h->{$key};
+
+ my $err = __attrKeyAction ($paref);
+
+ delete $paref->{keyval};
+ delete $paref->{akey};
+
+ return $err if($err);
+ }
+ }
+ else {
+ return "The key '$key=$h->{$key}' is not specified correctly. Please refer to the command reference.";
+ }
+ }
+
+ ## 2. Durchlauf - Umsetzung
+ #############################
+ for my $av (keys %{$valid}) {
+ delete $data{$name}{current}{$av};
+ }
+
+ for my $key (keys %{$h}) {
+ $data{$name}{current}{$key} = $h->{$key};
+ }
+ }
+ else { # Current Keys mit Attribut löschen
+ for my $av (keys %{$valid}) {
+ delete $data{$name}{current}{$av};
+ }
+ }
+
+return;
+}
+
################################################################
# Attr flowGraphicControl
################################################################
@@ -6452,47 +6545,71 @@ sub _attrflowGraphicControl { ## no critic "not used"
my $hash = $defs{$name};
my $valid = {
- animate => '(0|1)',
- consumerdist => '[89]\d{1}|[1234]\d{2}|500',
- h2consumerdist => '\d{1,3}',
- homenodedyncol => '(0|1)',
- shiftx => '-?[0-7]\d{0,1}|-?80',
- shifty => '\d+',
- size => '\d+',
- showconsumer => '(0|1)',
- showconsumerdummy => '(0|1)',
- showconsumerremaintime => '(0|1)',
- showconsumerpower => '(0|1)',
- strokeconsumerdyncol => '(0|1)',
- strokeCmrRedColLimit => '\d+',
- strokecolstd => '.*',
- strokecolsig => '.*',
- strokecolina => '.*',
- strokewidth => '\d+',
+ animate => { comp => '(0|1)', act => 0 },
+ consumerdist => { comp => '[89]\d{1}|[1234]\d{2}|500', act => 0 },
+ h2consumerdist => { comp => '\d{1,3}', act => 0 },
+ homenodedyncol => { comp => '(0|1)', act => 0 },
+ shiftx => { comp => '-?[0-7]\d{0,1}|-?80', act => 0 },
+ shifty => { comp => '\d+', act => 0 },
+ size => { comp => '\d+', act => 0 },
+ showconsumer => { comp => '(0|1)', act => 0 },
+ showconsumerdummy => { comp => '(0|1)', act => 0 },
+ showconsumerremaintime => { comp => '(0|1)', act => 0 },
+ showconsumerpower => { comp => '(0|1)', act => 0 },
+ strokeconsumerdyncol => { comp => '(0|1)', act => 0 },
+ strokeCmrRedColLimit => { comp => '\d+', act => 0 },
+ strokecolstd => { comp => '.*', act => 0 },
+ strokecolsig => { comp => '.*', act => 0 },
+ strokecolina => { comp => '.*', act => 0 },
+ strokewidth => { comp => '\d+', act => 0 },
};
- for my $av (keys %{$valid}) {
- delete $data{$name}{current}{$av};
- }
-
my ($a, $h) = parseParams ($aVal);
if ($cmd eq 'set') {
+ ## 1. Durchlauf - Prüfungen
+ #############################
for my $key (keys %{$h}) {
if (!grep /^$key$/, keys %{$valid}) {
return qq{The key '$key' is not a valid key in attribute '$aName'};
- }
+ }
- my $comp = $valid->{$key};
+ my $comp = $valid->{$key}{comp};
next if(!$comp);
- if ($h->{$key} =~ /^$comp$/xs) {
- $data{$name}{current}{$key} = $h->{$key};
- }
- else {
- return "The key '$key=$h->{$key}' is not specified correctly. Please use a valid value.";
+ if ($h->{$key} !~ /^$comp$/xs) {
+ return "The key '$key=$h->{$key}' is not specified correctly. Please refer to the command reference.";
}
}
+
+ ## 2. Durchlauf - Umsetzung
+ #############################
+ for my $av (keys %{$valid}) {
+ delete $data{$name}{current}{$av};
+ }
+
+ for my $key (keys %{$h}) {
+ $data{$name}{current}{$key} = $h->{$key};
+ }
+ }
+ else { # Current Keys mit Attribut löschen
+ for my $av (keys %{$valid}) {
+ delete $data{$name}{current}{$av};
+ }
+ }
+
+ for my $akey (keys %{$h}) { # von bestimmten Schlüsseln abhängige Aktionen ausführen
+ if ($valid->{$akey}{act}) {
+ $paref->{akey} = $akey;
+ $paref->{keyval} = $h->{$akey};
+
+ my $err = __attrKeyAction ($paref);
+
+ delete $paref->{keyval};
+ delete $paref->{akey};
+
+ return $err if($err);
+ }
}
return;
@@ -6513,14 +6630,12 @@ sub _attraiControl { ## no critic "not used"
aiTrainStart => { comp => '(1?[1-9]|10|2[0-3])', act => 0 },
aiTreesPV => { comp => '(1?[1-9]|10|[2-4][0-9]|50)', act => 0 },
};
-
- for my $av (keys %{$valid}) {
- delete $data{$name}{current}{$av};
- }
my ($a, $h) = parseParams ($aVal);
if ($cmd eq 'set') {
+ ## 1. Durchlauf - Prüfungen
+ #############################
for my $key (keys %{$h}) {
if (!grep /^$key$/, keys %{$valid}) {
return qq{The key '$key' is not a valid key in attribute '$aName'};
@@ -6529,25 +6644,39 @@ sub _attraiControl { ## no critic "not used"
my $comp = $valid->{$key}{comp};
next if(!$comp);
- if ($h->{$key} =~ /^$comp$/xs) {
- $data{$name}{current}{$key} = $h->{$key};
- }
- else {
+ if ($h->{$key} !~ /^$comp$/xs) {
return "The key '$key=$h->{$key}' is not specified correctly. Please refer to the command reference.";
}
}
+
+ ## 2. Durchlauf - Umsetzung
+ #############################
+ for my $av (keys %{$valid}) {
+ delete $data{$name}{current}{$av};
+ }
+
+ for my $key (keys %{$h}) {
+ $data{$name}{current}{$key} = $h->{$key};
+ }
+ }
+ else { # Current Keys mit Attribut löschen
+ for my $av (keys %{$valid}) {
+ delete $data{$name}{current}{$av};
+ }
}
for my $akey (keys %{$h}) { # von bestimmten Schlüsseln abhängige Aktionen ausführen
- next if(!$valid->{$akey}{act});
-
- $paref->{akey} = $akey;
- $paref->{keyval} = $h->{$akey};
-
- __attrKeyAction ($paref);
-
- delete $paref->{keyval};
- delete $paref->{akey};
+ if ($valid->{$akey}{act}) {
+ $paref->{akey} = $akey;
+ $paref->{keyval} = $h->{$akey};
+
+ my $err = __attrKeyAction ($paref);
+
+ delete $paref->{keyval};
+ delete $paref->{akey};
+
+ return $err if($err);
+ }
}
return;
@@ -6575,13 +6704,11 @@ sub _attrplantControl { ## no critic "not used"
showLink => { comp => '(0|1)', act => 0 },
};
- for my $av (keys %{$valid}) {
- delete $data{$name}{current}{$av};
- }
-
my ($a, $h) = parseParams ($aVal);
if ($cmd eq 'set') {
+ ## 1. Durchlauf - Prüfungen
+ #############################
for my $key (keys %{$h}) {
if (!grep /^$key$/, keys %{$valid}) {
return qq{The key '$key' is not a valid key in attribute '$aName'};
@@ -6590,25 +6717,39 @@ sub _attrplantControl { ## no critic "not used"
my $comp = $valid->{$key}{comp};
next if(!$comp);
- if ($h->{$key} =~ /^$comp$/xs) {
- $data{$name}{current}{$key} = $h->{$key};
- }
- else {
+ if ($h->{$key} !~ /^$comp$/xs) {
return "The key '$key=$h->{$key}' is not specified correctly. Please refer to the command reference.";
}
}
+
+ ## 2. Durchlauf - Umsetzung
+ #############################
+ for my $av (keys %{$valid}) {
+ delete $data{$name}{current}{$av};
+ }
+
+ for my $key (keys %{$h}) {
+ $data{$name}{current}{$key} = $h->{$key};
+ }
+ }
+ else { # Current Keys mit Attribut löschen
+ for my $av (keys %{$valid}) {
+ delete $data{$name}{current}{$av};
+ }
}
for my $akey (keys %{$h}) { # von bestimmten Schlüsseln abhängige Aktionen ausführen
- next if(!$valid->{$akey}{act});
-
- $paref->{akey} = $akey;
- $paref->{keyval} = $h->{$akey};
-
- __attrKeyAction ($paref);
-
- delete $paref->{keyval};
- delete $paref->{akey};
+ if ($valid->{$akey}{act}) {
+ $paref->{akey} = $akey;
+ $paref->{keyval} = $h->{$akey};
+
+ my $err = __attrKeyAction ($paref);
+
+ delete $paref->{keyval};
+ delete $paref->{akey};
+
+ return $err if($err);
+ }
}
return;
@@ -7311,6 +7452,7 @@ sub __attrKeyAction {
my $cmd = $paref->{cmd};
my $hash = $defs{$name};
+ my $err = q{};
if ($cmd eq 'set') {
if ($init_done && $akey eq 'cycleInterval') {
@@ -7318,6 +7460,16 @@ sub __attrKeyAction {
my $nct = CurrentVal ($name, 'nextCycleTime', 0); # gespeicherte nächste CyleTime
readingsSingleUpdate ($hash, 'nextCycletime', (!$nct ? 'Manual / Event-controlled' : FmtTime($nct)), 0);
}
+
+ if ($init_done && $akey eq 'headerDetail') {
+ my @hda = split ",", $keyval;
+
+ for my $val (@hda) {
+ if (!grep /^$val$/, qw (all co pv own status)) {
+ return qq{The value '$val' is not valid for key '$akey'};
+ }
+ }
+ }
}
if ($akey eq 'genPVdeviation' && $keyval eq 'daily') {
@@ -7325,7 +7477,7 @@ sub __attrKeyAction {
delete $data{$name}{circular}{99}{tdayDvtn};
}
-return;
+return $err;
}
################################################################
@@ -9040,7 +9192,7 @@ sub _specialActivities {
deleteConsumerPlanning ($hash, $c);
}
- writeCacheToFile ($hash, "consumers", $csmcache.$name); # Cache File Consumer schreiben
+ writeCacheToFile ($hash, 'consumers', $csmcache.$name, 'nolog'); # Cache File Consumer schreiben
Log3 ($name, 4, "$name - Daily special tasks - Task 3 finished");
}
@@ -9744,6 +9896,7 @@ sub _transferAPIRadiationValues {
$paref->{fd} = $fd;
$data{$name}{nexthours}{$nhtstr}{starttime} = $wantdt;
+ $data{$name}{nexthours}{$nhtstr}{day} = $wtday;
$data{$name}{nexthours}{$nhtstr}{hourofday} = $hod;
$data{$name}{nexthours}{$nhtstr}{today} = $fd == 0 ? 1 : 0;
$data{$name}{nexthours}{$nhtstr}{rad1h} = $rad1h;
@@ -10658,14 +10811,18 @@ sub _transferBatteryValues {
writeToHistory ( { paref => $paref, key => 'batoutthishour'.$bn, val => $batoutthishour, hour => $nhour } );
- # täglichen max. SOC in pvHistory speichern
- #############################################
+ # täglichen maximalen SOC in pvHistory speichern
+ ##################################################
my $batmaxsoc = HistoryVal ($hash, $day, 99, 'batmaxsoc'.$bn, 0); # gespeicherter max. SOC des Tages
if ($soc >= $batmaxsoc) {
writeToHistory ( { paref => $paref, key => 'batmaxsoc'.$bn, val => $soc, hour => 99 } );
}
+ # aktuellen SOC in pvHistory speichern
+ ########################################
+ writeToHistory ( { paref => $paref, key => 'batsoc'.$bn, val => $soc, hour => $nhour } );
+
######
storeReading ('Today_Hour'.sprintf("%02d",$nhour).'_BatIn_'. $bn, $batinthishour. ' Wh'.$warnin);
@@ -10687,8 +10844,6 @@ sub _transferBatteryValues {
$data{$name}{batteries}{$bn}{bposingraph} = $pos; # Anzeigeposition in Balkengrafik
$data{$name}{batteries}{$bn}{bchargewh} = BatteryVal ($name, $bn, 'binstcap', 0) * $soc / 100; # Batterie SoC (Wh)
- writeToHistory ( { paref => $paref, key => 'batsoc'.$bn, val => $soc, hour => $nhour } );
-
$num++;
$socsum += $soc;
$socwhsum += BatteryVal ($name, $bn, 'binstcap', 0) * $soc / 100; # Batterie SoC in Wh
@@ -11037,6 +11192,7 @@ sub _batChargeRecmd {
my $nhr = sprintf "%02d", $num;
my $today = NexthoursVal ($hash, 'NextHour'.$nhr, 'today', 0);
+ my $hod = NexthoursVal ($hash, 'NextHour'.$nhr, 'hourofday', '');
my $confc = NexthoursVal ($hash, 'NextHour'.$nhr, 'confc', 0);
my $pvfc = NexthoursVal ($hash, 'NextHour'.$nhr, 'pvfc', 0);
my $stt = NexthoursVal ($hash, 'NextHour'.$nhr, 'starttime', '');
@@ -11128,6 +11284,12 @@ sub _batChargeRecmd {
$data{$name}{nexthours}{'NextHour'.$nhr}{'rcdchargebat'.$bn} = $crel;
$data{$name}{nexthours}{'NextHour'.$nhr}{'soc'.$bn} = $progsoc;
+
+ # prognostizierten SOC in pvHistory speichern
+ ###############################################
+ if ($today && $hod) { # heutiger Tag
+ writeToHistory ( { paref => $paref, key => 'batprogsoc'.$bn, val => $progsoc, hour => $hod } );
+ }
debugLog ($paref, 'batteryManagement', "Bat $bn relLoad $stt -> $crel ($msg)");
}
@@ -12027,7 +12189,7 @@ sub ___doPlanning {
Log3 ($name, 3, qq{$name - Consumer "$calias" $planstate $planspmlt});
}
- writeCacheToFile ($hash, "consumers", $csmcache.$name); # Cache File Consumer schreiben
+ writeCacheToFile ($hash, 'consumers', $csmcache.$name); # Cache File Consumer schreiben
___setPlanningDeleteMeth ($paref);
@@ -12503,7 +12665,7 @@ sub ___switchConsumerOn {
___setConsumerPlanningState ($paref);
delete $paref->{ps};
- writeCacheToFile ($hash, "consumers", $csmcache.$name); # Cache File Consumer schreiben
+ writeCacheToFile ($hash, 'consumers', $csmcache.$name); # Cache File Consumer schreiben
}
}
elsif ((($isintable == 1 && $isConsRcmd) || # unterbrochenen Consumer fortsetzen
@@ -12527,7 +12689,7 @@ sub ___switchConsumerOn {
___setConsumerPlanningState ($paref);
delete $paref->{ps};
- writeCacheToFile ($hash, "consumers", $csmcache.$name); # Cache File Consumer schreiben
+ writeCacheToFile ($hash, 'consumers', $csmcache.$name); # Cache File Consumer schreiben
}
return $state;
@@ -12596,7 +12758,7 @@ sub ___switchConsumerOff {
___setConsumerPlanningState ($paref);
delete $paref->{ps};
- writeCacheToFile ($hash, "consumers", $csmcache.$name); # Cache File Consumer schreiben
+ writeCacheToFile ($hash, 'consumers', $csmcache.$name); # Cache File Consumer schreiben
}
elsif ((($isintable && !isConsRcmd ($hash, $c)) || $isintable == 2) && # Consumer unterbrechen
isInTimeframe ($hash, $c) && $auto && $offcom && !$iilt &&
@@ -12617,7 +12779,7 @@ sub ___switchConsumerOff {
___setConsumerPlanningState ($paref);
delete $paref->{ps};
- writeCacheToFile ($hash, "consumers", $csmcache.$name); # Cache File Consumer schreiben
+ writeCacheToFile ($hash, 'consumers', $csmcache.$name); # Cache File Consumer schreiben
}
return $state;
@@ -12738,7 +12900,7 @@ sub ___setConsumerSwitchingState {
if ($dowri) {
if (!$fscss) {
- writeCacheToFile ($hash, "consumers", $csmcache.$name); # Cache File Consumer schreiben
+ writeCacheToFile ($hash, 'consumers', $csmcache.$name); # Cache File Consumer schreiben
}
Log3 ($name, 2, "$name - $state");
@@ -13289,17 +13451,16 @@ sub _calcReadingsTomorrowPVFc {
for my $idx (sort keys %{$h}) {
my $today = NexthoursVal ($hash, $idx, 'today', 1);
- next if($today); # aktueller Tag wird nicht benötigt
+ next if($today); # aktueller Tag wird nicht benötigt
- my $h = NexthoursVal ($hash, $idx, 'hourofday', '');
- next if(!$h);
+ my $hod = NexthoursVal ($hash, $idx, 'hourofday', '');
+ next if(!$hod);
- next if($hods !~ /$h/xs); # diese Stunde des Tages soll nicht erzeugt werden
+ next if($hods !~ /$hod/xs); # diese Stunde des Tages soll nicht erzeugt werden
- my $st = NexthoursVal ($hash, $idx, 'starttime', 'XXXX-XX-XX XX:XX:XX'); # Starttime
my $pvfc = NexthoursVal ($hash, $idx, 'pvfc', 0);
- storeReading ('Tomorrow_Hour'.$h.'_PVforecast', $pvfc.' Wh');
+ storeReading ('Tomorrow_Hour'.$hod.'_PVforecast', $pvfc.' Wh');
}
return;
@@ -14100,6 +14261,8 @@ sub entryGraphic {
my $gsel = shift // ''; # direkte Auswahl welche Grafik zurück gegeben werden soll (both, flow, forecast)
my $pah = shift // 0; # 1 wenn durch pageAsHtml aufgerufen
+ return if(!$init_done);
+
my $hash = $defs{$name};
# Setup Vollständigkeit/disabled prüfen
@@ -14408,11 +14571,10 @@ return $ret;
# Vollständigkeit Setup prüfen
################################################################
sub _checkSetupNotComplete {
- my $hash = shift;
- my $ret = q{};
+ my $hash = shift;
+ my $ret = q{};
- my $name = $hash->{NAME};
- my $type = $hash->{TYPE};
+ my $name = $hash->{NAME};
my $strings = AttrVal ($name, 'setupInverterStrings', undef); # String Konfig
my $wedev = AttrVal ($name, 'setupWeatherDev1', undef); # Device Vorhersage Wetterdaten (Bewölkung etc.)
@@ -18829,7 +18991,7 @@ sub setPVhistory {
my $type = $paref->{type};
my $day = $paref->{day};
my $dayname = $paref->{dayname}; # aktueller Wochentagsname
- my $nhour = $paref->{nhour};
+ my $nhour = $paref->{nhour}; # Stunde des Tages
my $histname = $paref->{histname};
my $val = $paref->{val}; # Wert zur Speicherung in pvHistory (soll mal generell verwendet werden -> Change)
my $reorg = $paref->{reorg} // 0; # Neuberechnung von Werten in Stunde "99" nach Löschen von Stunden eines Tages
@@ -18960,7 +19122,7 @@ sub setPVhistory {
}
if ($histname) {
- debugLog ($paref, 'saveData2Cache', "setPVhistory -> store Day: $day, Hour: $nhour, Key: $histname, Value: ".(defined $val ? $val : 'undef'));
+ debugLog ($paref, 'saveData2Cache', "setPVhistory -> store Day: $day, Hour of Day: $nhour, Key: $histname, Value: ".(defined $val ? $val : 'undef'));
}
return;
@@ -19177,16 +19339,17 @@ sub _listDataPoolPvHist {
$prdl .= "pprl${pn}: $pprl";
}
- my ($btotin, $batin, $btotout, $batout, $batmsoc, $batssoc, $batsoc);
+ my ($btotin, $batin, $btotout, $batout, $batmsoc, $batssoc, $batprogsoc, $batsoc);
for my $bn (1..MAXBATTERIES) { # + alle Batterien
- $bn = sprintf "%02d", $bn;
- my $hbtotin = HistoryVal ($name, $day, $key, 'batintotal'.$bn, '-');
- my $hbtotout = HistoryVal ($name, $day, $key, 'batouttotal'.$bn, '-');
- my $hbatin = HistoryVal ($name, $day, $key, 'batin'.$bn, '-');
- my $hbatout = HistoryVal ($name, $day, $key, 'batout'.$bn, '-');
- my $hbatmsoc = HistoryVal ($name, $day, $key, 'batmaxsoc'.$bn, '-');
- my $hbatssoc = HistoryVal ($name, $day, $key, 'batsetsoc'.$bn, '-');
- my $hbatsoc = HistoryVal ($name, $day, $key, 'batsoc'.$bn, '-');
+ $bn = sprintf "%02d", $bn;
+ my $hbtotin = HistoryVal ($name, $day, $key, 'batintotal'.$bn, '-');
+ my $hbtotout = HistoryVal ($name, $day, $key, 'batouttotal'.$bn, '-');
+ my $hbatin = HistoryVal ($name, $day, $key, 'batin'.$bn, '-');
+ my $hbatout = HistoryVal ($name, $day, $key, 'batout'.$bn, '-');
+ my $hbatmsoc = HistoryVal ($name, $day, $key, 'batmaxsoc'.$bn, '-');
+ my $hbatssoc = HistoryVal ($name, $day, $key, 'batsetsoc'.$bn, '-');
+ my $hbatprogsoc = HistoryVal ($name, $day, $key, 'batprogsoc'.$bn, '-');
+ my $hbatsoc = HistoryVal ($name, $day, $key, 'batsoc'.$bn, '-');
if ($export eq 'csv') {
$hexp->{$day}{$key}{"BatteryInTotal${bn}"} = $hbtotin;
@@ -19195,23 +19358,26 @@ sub _listDataPoolPvHist {
$hexp->{$day}{$key}{"BatteryOut${bn}"} = $hbatout;
$hexp->{$day}{$key}{"BatteryMaxSoc${bn}"} = $hbatmsoc;
$hexp->{$day}{$key}{"BatterySetSoc${bn}"} = $hbatssoc;
+ $hexp->{$day}{$key}{"BatteryProgSoc${bn}"} = $hbatprogsoc;
$hexp->{$day}{$key}{"BatterySoc${bn}"} = $hbatsoc;
}
- $btotin .= ', ' if($btotin);
- $btotin .= "batintotal${bn}: $hbtotin";
- $btotout .= ', ' if($btotout);
- $btotout .= "batouttotal${bn}: $hbtotout";
- $batin .= ', ' if($batin);
- $batin .= "batin${bn}: $hbatin";
- $batout .= ', ' if($batout);
- $batout .= "batout${bn}: $hbatout";
- $batmsoc .= ', ' if($batmsoc);
- $batmsoc .= "batmaxsoc${bn}: $hbatmsoc";
- $batssoc .= ', ' if($batssoc);
- $batssoc .= "batsetsoc${bn}: $hbatssoc";
- $batsoc .= ', ' if($batsoc);
- $batsoc .= "batsoc${bn}: $hbatsoc";
+ $btotin .= ', ' if($btotin);
+ $btotin .= "batintotal${bn}: $hbtotin";
+ $btotout .= ', ' if($btotout);
+ $btotout .= "batouttotal${bn}: $hbtotout";
+ $batin .= ', ' if($batin);
+ $batin .= "batin${bn}: $hbatin";
+ $batout .= ', ' if($batout);
+ $batout .= "batout${bn}: $hbatout";
+ $batmsoc .= ', ' if($batmsoc);
+ $batmsoc .= "batmaxsoc${bn}: $hbatmsoc";
+ $batssoc .= ', ' if($batssoc);
+ $batssoc .= "batsetsoc${bn}: $hbatssoc";
+ $batprogsoc .= ', ' if($batprogsoc);
+ $batprogsoc .= "batprogsoc${bn}: $hbatprogsoc";
+ $batsoc .= ', ' if($batsoc);
+ $batsoc .= "batsoc${bn}: $hbatsoc";
}
$ret .= "\n " if($ret);
@@ -19237,6 +19403,9 @@ sub _listDataPoolPvHist {
$ret .= "\n " if($key ne '99');
$ret .= $btotout if($key ne '99');
$ret .= "\n " if($key ne '99');
+
+ $ret .= $batprogsoc if($key ne '99');
+ $ret .= "\n " if($key ne '99');
$ret .= $batsoc if($key ne '99');
$ret .= "\n " if($key ne '99');
@@ -19245,10 +19414,10 @@ sub _listDataPoolPvHist {
$ret .= $batout;
$ret .= "\n ";
- $ret .= $batmsoc if($key eq '99');
- $ret .= "\n " if($key eq '99');
- $ret .= $batssoc if($key eq '99');
- $ret .= "\n " if($key eq '99');
+ $ret .= $batmsoc if($key eq '99');
+ $ret .= "\n " if($key eq '99');
+ $ret .= $batssoc if($key eq '99');
+ $ret .= "\n " if($key eq '99');
if ($key ne '99') {
$ret .= "weatherid: $wid, ";
@@ -19604,6 +19773,7 @@ sub _listDataPoolNextHours {
for my $idx (sort keys %{$h}) {
my $nhts = NexthoursVal ($name, $idx, 'starttime', '-');
+ my $day = NexthoursVal ($name, $idx, 'day', '-');
my $hod = NexthoursVal ($name, $idx, 'hourofday', '-');
my $today = NexthoursVal ($name, $idx, 'today', '-');
my $pvfc = NexthoursVal ($name, $idx, 'pvfc', '-');
@@ -19637,7 +19807,7 @@ sub _listDataPoolNextHours {
$sq .= "\n" if($sq);
$sq .= $idx." => ";
- $sq .= "starttime: $nhts, hourofday: $hod, today: $today";
+ $sq .= "starttime: $nhts, day: $day, hourofday: $hod, today: $today";
$sq .= "\n ";
$sq .= "pvapifc: $pvapifc, pvaifc: $pvaifc, pvfc: $pvfc, aihit: $aihit";
$sq .= "\n ";
@@ -24172,7 +24342,7 @@ to ensure that the system configuration is correct.
- nextHours
- Lists the expected values for the coming hours.
+ Lists the stored values for the coming hours.
@@ -24184,6 +24354,7 @@ to ensure that the system configuration is correct.
| correff | correction factor/quality used |
| | <factor>/- -> no quality defined |
| | <factor>/0..1 - quality of the PV forecast (1 = best quality) |
+ | day | Date of day |
| DoN | sunrise and sunset status (0 - night, 1 - day) |
| hourofday | current hour of the day |
| pvapifc | expected PV generation (Wh) of the used API incl. a possible correction |
@@ -24227,7 +24398,8 @@ to ensure that the system configuration is correct.
| batinXX | Charge of battery XX within the hour (Wh) |
| batouttotalXX | total battery XX discharge (Wh) at the beginning of the hour |
| batoutXX | Discharge of battery XX within the hour (Wh) |
- | batsocXX | State of charge SOC (%) of battery XX at the end of the hour |
+ | batprogsocXX | predicted state of charge SOC (%) of battery XX at the end of the hour |
+ | batsocXX | real State of charge SOC (%) of battery XX at the end of the hour |
| batmaxsocXX | Maximum SOC (%) achieved by battery XX on the day |
| batsetsocXX | Optimum SOC setpoint (%) of battery XX for the day |
| confc | expected energy consumption (Wh) |
@@ -26718,7 +26890,7 @@ die ordnungsgemäße Anlagenkonfiguration geprüft werden.
- nextHours
- Listet die erwarteten Werte der kommenden Stunden auf.
+ Listet die gespeicherte Werte der kommenden Stunden auf.
@@ -26730,6 +26902,7 @@ die ordnungsgemäße Anlagenkonfiguration geprüft werden.
| correff | verwendeter Korrekturfaktor/Qualität |
| | <Faktor>/- -> keine Qualität definiert |
| | <Faktor>/0..1 - Qualität der PV Prognose (1 = beste Qualität) |
+ | day | Tagesdatum |
| DoN | Sonnenauf- und untergangsstatus (0 - Nacht, 1 - Tag) |
| hourofday | laufende Stunde des Tages |
| pvapifc | erwartete PV Erzeugung (Wh) der verwendeten API inkl. einer eventuellen Korrektur |
@@ -26773,7 +26946,8 @@ die ordnungsgemäße Anlagenkonfiguration geprüft werden.
| batinXX | Ladung der Batterie XX innerhalb der Stunde (Wh) |
| batouttotalXX | Gesamtentladung der Batterie XX (Wh) zu Beginn der Stunde |
| batoutXX | Entladung der Batterie XX innerhalb der Stunde (Wh) |
- | batsocXX | Ladezustand SOC (%) der Batterie XX am Ende der Stunde |
+ | batprogsocXX | prognostizierte Ladezustand SOC (%) der Batterie XX am Ende der Stunde |
+ | batsocXX | realer Ladezustand SOC (%) der Batterie XX am Ende der Stunde |
| batmaxsocXX | maximal erreichter SOC (%) der Batterie XX an dem Tag |
| batsetsocXX | optimaler SOC Sollwert (%) der Batterie XX für den Tag |
| csmtXX | Energieverbrauch total von ConsumerXX |
diff --git a/fhem/contrib/DS_Starter/76_SolarForecast.pm b/fhem/contrib/DS_Starter/76_SolarForecast.pm
index 24ac73a10..acb68a551 100644
--- a/fhem/contrib/DS_Starter/76_SolarForecast.pm
+++ b/fhem/contrib/DS_Starter/76_SolarForecast.pm
@@ -160,6 +160,9 @@ BEGIN {
# Versions History intern
my %vNotesIntern = (
+ "1.51.4" => "27.04.2025 avoid warnings uninitialized value \$FW_ME, \$FW_subdir in line 14434 ".
+ "bugfix in attr .*Control, fix 'ERROR deleting file' if no consumers are registered ".
+ "save batprogsocXX to pvHistory, prepared for new Attr graphicControl ",
"1.51.3" => "22.04.2025 change battery text to 'load if above feed-in limit' ".
"transform set setupStringAzimuth, setupStringDeclination to attr setupStringAzimuth, setupStringDeclination ",
"1.51.2" => "21.04.2025 Attributes obsolet: graphicHeaderShow replaced by graphicSelect, Value 'none' of consumerControl->showLegend deleted ",
@@ -609,6 +612,7 @@ my @aconfigs = qw( aiControl
ctrlSpecialReadings
ctrlUserExitFn
disable
+ graphicControl
flowGraphicControl
graphicBeamWidth
graphicBeamHeightLevel1 graphicBeamHeightLevel2 graphicBeamHeightLevel3
@@ -736,6 +740,7 @@ my %hattr = ( # H
setupStringAzimuth => { fn => \&_attrstringAzimuth },
setupStringDeclination => { fn => \&_attrstringDeclination },
setupRoofTops => { fn => \&_attrRoofTops },
+ graphicControl => { fn => \&_attrgraphicControl },
flowGraphicControl => { fn => \&_attrflowGraphicControl },
aiControl => { fn => \&_attraiControl },
plantControl => { fn => \&_attrplantControl },
@@ -1466,6 +1471,11 @@ my %hfspvh = (
$hfspvh{'batoutthishour'.$bn}{storname} = 'batout'.$bn;
$hfspvh{'batoutthishour'.$bn}{validkey} = undef;
$hfspvh{'batoutthishour'.$bn}{fpar} = 'comp99';
+
+ $hfspvh{'batprogsoc'.$bn}{fn} = \&_storeVal; # Prognose-SOC des Tages
+ $hfspvh{'batprogsoc'.$bn}{storname} = 'batprogsoc'.$bn;
+ $hfspvh{'batprogsoc'.$bn}{validkey} = undef;
+ $hfspvh{'batprogsoc'.$bn}{fpar} = undef;
$hfspvh{'batmaxsoc'.$bn}{fn} = \&_storeVal; # max. erreichter SOC des Tages
$hfspvh{'batmaxsoc'.$bn}{storname} = 'batmaxsoc'.$bn;
@@ -1579,6 +1589,7 @@ sub Initialize {
"ctrlUserExitFn:textField-long ".
"disable:1,0 ".
"flowGraphicControl:textField-long ".
+ #"graphicControl:textField-long ".
"graphicBeamHeightLevel1 ".
"graphicBeamHeightLevel2 ".
"graphicBeamHeightLevel3 ".
@@ -1861,7 +1872,7 @@ sub _setconsumerImmediatePlanning { ## no critic "not used"
my $planstate = ConsumerVal ($hash, $c, 'planstate', '');
my $calias = ConsumerVal ($hash, $c, 'alias', '');
- writeCacheToFile ($hash, "consumers", $csmcache.$name); # Cache File Consumer schreiben
+ writeCacheToFile ($hash, 'consumers', $csmcache.$name); # Cache File Consumer schreiben
Log3 ($name, 3, qq{$name - Consumer "$calias" $planstate}) if($planstate);
@@ -2515,7 +2526,7 @@ sub _setreset { ## no critic "not used"
Log3($name, 3, qq{$name - roofIdentPair: all pair keys deleted});
}
- writeCacheToFile ($hash, 'solcastapi', $scpicache.$name); # Cache File SolCast API Werte schreiben
+ writeCacheToFile ($hash, 'solcastapi', $scpicache.$name); # Cache File SolCast API Werte schreiben
return;
}
@@ -2531,7 +2542,7 @@ sub _setreset { ## no critic "not used"
}
}
- writeCacheToFile ($hash, "consumers", $csmcache.$name); # Cache File Consumer schreiben
+ writeCacheToFile ($hash, 'consumers', $csmcache.$name, 'nolog'); # Cache File Consumer schreiben
}
if ($prop eq 'consumerMaster') { # Verbraucherhash löschen
@@ -2551,7 +2562,7 @@ sub _setreset { ## no critic "not used"
delete $paref->{c};
$data{$name}{current}{consumerCollected} = 0; # Consumer neu sammeln
- writeCacheToFile ($hash, "consumers", $csmcache.$name); # Cache File Consumer schreiben
+ writeCacheToFile ($hash, 'consumers', $csmcache.$name, 'nolog'); # Cache File Consumer schreiben
centralTask ($hash, 0);
}
@@ -6328,13 +6339,11 @@ sub _attrconsumerControl { ## no critic "not used"
showLegend => { comp => '(icon_top|icon_bottom|text_top|text_bottom)', act => 0 },
};
- for my $av (keys %{$valid}) {
- delete $data{$name}{current}{$av};
- }
-
my ($a, $h) = parseParams ($aVal);
if ($cmd eq 'set') {
+ ## 1. Durchlauf - Prüfungen
+ #############################
for my $key (keys %{$h}) {
if (!grep /^$key$/, keys %{$valid}) {
return qq{The key '$key' is not a valid key in attribute '$aName'};
@@ -6343,25 +6352,39 @@ sub _attrconsumerControl { ## no critic "not used"
my $comp = $valid->{$key}{comp};
next if(!$comp);
- if ($h->{$key} =~ /^$comp$/xs) {
- $data{$name}{current}{$key} = $h->{$key};
- }
- else {
+ if ($h->{$key} !~ /^$comp$/xs) {
return "The key '$key=$h->{$key}' is not specified correctly. Please refer to the command reference.";
}
}
+
+ ## 2. Durchlauf - Umsetzung
+ #############################
+ for my $av (keys %{$valid}) {
+ delete $data{$name}{current}{$av};
+ }
+
+ for my $key (keys %{$h}) {
+ $data{$name}{current}{$key} = $h->{$key};
+ }
+ }
+ else { # Current Keys mit Attribut löschen
+ for my $av (keys %{$valid}) {
+ delete $data{$name}{current}{$av};
+ }
}
for my $akey (keys %{$h}) { # von bestimmten Schlüsseln abhängige Aktionen ausführen
- next if(!$valid->{$akey}{act});
-
- $paref->{akey} = $akey;
- $paref->{keyval} = $h->{$akey};
-
- __attrKeyAction ($paref);
-
- delete $paref->{keyval};
- delete $paref->{akey};
+ if ($valid->{$akey}{act}) {
+ $paref->{akey} = $akey;
+ $paref->{keyval} = $h->{$akey};
+
+ my $err = __attrKeyAction ($paref);
+
+ delete $paref->{keyval};
+ delete $paref->{akey};
+
+ return $err if($err);
+ }
}
return;
@@ -6439,6 +6462,76 @@ sub _attrctrlDebug { ## no critic "not used"
return;
}
+################################################################
+# Attr graphicControl
+################################################################
+sub _attrgraphicControl { ## no critic "not used"
+ my $paref = shift;
+ my $name = $paref->{name};
+ my $aName = $paref->{aName};
+ my $aVal = $paref->{aVal};
+ my $cmd = $paref->{cmd};
+
+ my $valid = {
+ beamWidth => { comp => '([2-9][0-9]|100)', act => 0 },
+ hourCount => { comp => '([4-9]|1[0-9]|2[0-4])', act => 0 },
+ hourStyle => { comp => ':(0{1,2})', act => 0 },
+ headerDetail => { comp => '.*', act => 1 },
+ energyUnit => { comp => '(Wh|kWh)', act => 0 },
+ layoutType => { comp => '(single|double|diff)', act => 0 },
+ spaceSize => { comp => '\d+', act => 0 },
+ };
+
+ my ($a, $h) = parseParams ($aVal);
+
+ if ($cmd eq 'set') {
+ ## 1. Durchlauf - Prüfungen
+ #############################
+ for my $key (keys %{$h}) {
+ if (!grep /^$key$/, keys %{$valid}) {
+ return qq{The key '$key' is not a valid key in attribute '$aName'};
+ }
+
+ my $comp = $valid->{$key}{comp};
+ next if(!$comp);
+
+ if ($h->{$key} =~ /^$comp$/xs) {
+ if ($valid->{$key}{act}) {
+ $paref->{akey} = $key;
+ $paref->{keyval} = $h->{$key};
+
+ my $err = __attrKeyAction ($paref);
+
+ delete $paref->{keyval};
+ delete $paref->{akey};
+
+ return $err if($err);
+ }
+ }
+ else {
+ return "The key '$key=$h->{$key}' is not specified correctly. Please refer to the command reference.";
+ }
+ }
+
+ ## 2. Durchlauf - Umsetzung
+ #############################
+ for my $av (keys %{$valid}) {
+ delete $data{$name}{current}{$av};
+ }
+
+ for my $key (keys %{$h}) {
+ $data{$name}{current}{$key} = $h->{$key};
+ }
+ }
+ else { # Current Keys mit Attribut löschen
+ for my $av (keys %{$valid}) {
+ delete $data{$name}{current}{$av};
+ }
+ }
+
+return;
+}
+
################################################################
# Attr flowGraphicControl
################################################################
@@ -6452,47 +6545,71 @@ sub _attrflowGraphicControl { ## no critic "not used"
my $hash = $defs{$name};
my $valid = {
- animate => '(0|1)',
- consumerdist => '[89]\d{1}|[1234]\d{2}|500',
- h2consumerdist => '\d{1,3}',
- homenodedyncol => '(0|1)',
- shiftx => '-?[0-7]\d{0,1}|-?80',
- shifty => '\d+',
- size => '\d+',
- showconsumer => '(0|1)',
- showconsumerdummy => '(0|1)',
- showconsumerremaintime => '(0|1)',
- showconsumerpower => '(0|1)',
- strokeconsumerdyncol => '(0|1)',
- strokeCmrRedColLimit => '\d+',
- strokecolstd => '.*',
- strokecolsig => '.*',
- strokecolina => '.*',
- strokewidth => '\d+',
+ animate => { comp => '(0|1)', act => 0 },
+ consumerdist => { comp => '[89]\d{1}|[1234]\d{2}|500', act => 0 },
+ h2consumerdist => { comp => '\d{1,3}', act => 0 },
+ homenodedyncol => { comp => '(0|1)', act => 0 },
+ shiftx => { comp => '-?[0-7]\d{0,1}|-?80', act => 0 },
+ shifty => { comp => '\d+', act => 0 },
+ size => { comp => '\d+', act => 0 },
+ showconsumer => { comp => '(0|1)', act => 0 },
+ showconsumerdummy => { comp => '(0|1)', act => 0 },
+ showconsumerremaintime => { comp => '(0|1)', act => 0 },
+ showconsumerpower => { comp => '(0|1)', act => 0 },
+ strokeconsumerdyncol => { comp => '(0|1)', act => 0 },
+ strokeCmrRedColLimit => { comp => '\d+', act => 0 },
+ strokecolstd => { comp => '.*', act => 0 },
+ strokecolsig => { comp => '.*', act => 0 },
+ strokecolina => { comp => '.*', act => 0 },
+ strokewidth => { comp => '\d+', act => 0 },
};
- for my $av (keys %{$valid}) {
- delete $data{$name}{current}{$av};
- }
-
my ($a, $h) = parseParams ($aVal);
if ($cmd eq 'set') {
+ ## 1. Durchlauf - Prüfungen
+ #############################
for my $key (keys %{$h}) {
if (!grep /^$key$/, keys %{$valid}) {
return qq{The key '$key' is not a valid key in attribute '$aName'};
- }
+ }
- my $comp = $valid->{$key};
+ my $comp = $valid->{$key}{comp};
next if(!$comp);
- if ($h->{$key} =~ /^$comp$/xs) {
- $data{$name}{current}{$key} = $h->{$key};
- }
- else {
- return "The key '$key=$h->{$key}' is not specified correctly. Please use a valid value.";
+ if ($h->{$key} !~ /^$comp$/xs) {
+ return "The key '$key=$h->{$key}' is not specified correctly. Please refer to the command reference.";
}
}
+
+ ## 2. Durchlauf - Umsetzung
+ #############################
+ for my $av (keys %{$valid}) {
+ delete $data{$name}{current}{$av};
+ }
+
+ for my $key (keys %{$h}) {
+ $data{$name}{current}{$key} = $h->{$key};
+ }
+ }
+ else { # Current Keys mit Attribut löschen
+ for my $av (keys %{$valid}) {
+ delete $data{$name}{current}{$av};
+ }
+ }
+
+ for my $akey (keys %{$h}) { # von bestimmten Schlüsseln abhängige Aktionen ausführen
+ if ($valid->{$akey}{act}) {
+ $paref->{akey} = $akey;
+ $paref->{keyval} = $h->{$akey};
+
+ my $err = __attrKeyAction ($paref);
+
+ delete $paref->{keyval};
+ delete $paref->{akey};
+
+ return $err if($err);
+ }
}
return;
@@ -6513,14 +6630,12 @@ sub _attraiControl { ## no critic "not used"
aiTrainStart => { comp => '(1?[1-9]|10|2[0-3])', act => 0 },
aiTreesPV => { comp => '(1?[1-9]|10|[2-4][0-9]|50)', act => 0 },
};
-
- for my $av (keys %{$valid}) {
- delete $data{$name}{current}{$av};
- }
my ($a, $h) = parseParams ($aVal);
if ($cmd eq 'set') {
+ ## 1. Durchlauf - Prüfungen
+ #############################
for my $key (keys %{$h}) {
if (!grep /^$key$/, keys %{$valid}) {
return qq{The key '$key' is not a valid key in attribute '$aName'};
@@ -6529,25 +6644,39 @@ sub _attraiControl { ## no critic "not used"
my $comp = $valid->{$key}{comp};
next if(!$comp);
- if ($h->{$key} =~ /^$comp$/xs) {
- $data{$name}{current}{$key} = $h->{$key};
- }
- else {
+ if ($h->{$key} !~ /^$comp$/xs) {
return "The key '$key=$h->{$key}' is not specified correctly. Please refer to the command reference.";
}
}
+
+ ## 2. Durchlauf - Umsetzung
+ #############################
+ for my $av (keys %{$valid}) {
+ delete $data{$name}{current}{$av};
+ }
+
+ for my $key (keys %{$h}) {
+ $data{$name}{current}{$key} = $h->{$key};
+ }
+ }
+ else { # Current Keys mit Attribut löschen
+ for my $av (keys %{$valid}) {
+ delete $data{$name}{current}{$av};
+ }
}
for my $akey (keys %{$h}) { # von bestimmten Schlüsseln abhängige Aktionen ausführen
- next if(!$valid->{$akey}{act});
-
- $paref->{akey} = $akey;
- $paref->{keyval} = $h->{$akey};
-
- __attrKeyAction ($paref);
-
- delete $paref->{keyval};
- delete $paref->{akey};
+ if ($valid->{$akey}{act}) {
+ $paref->{akey} = $akey;
+ $paref->{keyval} = $h->{$akey};
+
+ my $err = __attrKeyAction ($paref);
+
+ delete $paref->{keyval};
+ delete $paref->{akey};
+
+ return $err if($err);
+ }
}
return;
@@ -6575,13 +6704,11 @@ sub _attrplantControl { ## no critic "not used"
showLink => { comp => '(0|1)', act => 0 },
};
- for my $av (keys %{$valid}) {
- delete $data{$name}{current}{$av};
- }
-
my ($a, $h) = parseParams ($aVal);
if ($cmd eq 'set') {
+ ## 1. Durchlauf - Prüfungen
+ #############################
for my $key (keys %{$h}) {
if (!grep /^$key$/, keys %{$valid}) {
return qq{The key '$key' is not a valid key in attribute '$aName'};
@@ -6590,25 +6717,39 @@ sub _attrplantControl { ## no critic "not used"
my $comp = $valid->{$key}{comp};
next if(!$comp);
- if ($h->{$key} =~ /^$comp$/xs) {
- $data{$name}{current}{$key} = $h->{$key};
- }
- else {
+ if ($h->{$key} !~ /^$comp$/xs) {
return "The key '$key=$h->{$key}' is not specified correctly. Please refer to the command reference.";
}
}
+
+ ## 2. Durchlauf - Umsetzung
+ #############################
+ for my $av (keys %{$valid}) {
+ delete $data{$name}{current}{$av};
+ }
+
+ for my $key (keys %{$h}) {
+ $data{$name}{current}{$key} = $h->{$key};
+ }
+ }
+ else { # Current Keys mit Attribut löschen
+ for my $av (keys %{$valid}) {
+ delete $data{$name}{current}{$av};
+ }
}
for my $akey (keys %{$h}) { # von bestimmten Schlüsseln abhängige Aktionen ausführen
- next if(!$valid->{$akey}{act});
-
- $paref->{akey} = $akey;
- $paref->{keyval} = $h->{$akey};
-
- __attrKeyAction ($paref);
-
- delete $paref->{keyval};
- delete $paref->{akey};
+ if ($valid->{$akey}{act}) {
+ $paref->{akey} = $akey;
+ $paref->{keyval} = $h->{$akey};
+
+ my $err = __attrKeyAction ($paref);
+
+ delete $paref->{keyval};
+ delete $paref->{akey};
+
+ return $err if($err);
+ }
}
return;
@@ -7311,6 +7452,7 @@ sub __attrKeyAction {
my $cmd = $paref->{cmd};
my $hash = $defs{$name};
+ my $err = q{};
if ($cmd eq 'set') {
if ($init_done && $akey eq 'cycleInterval') {
@@ -7318,6 +7460,16 @@ sub __attrKeyAction {
my $nct = CurrentVal ($name, 'nextCycleTime', 0); # gespeicherte nächste CyleTime
readingsSingleUpdate ($hash, 'nextCycletime', (!$nct ? 'Manual / Event-controlled' : FmtTime($nct)), 0);
}
+
+ if ($init_done && $akey eq 'headerDetail') {
+ my @hda = split ",", $keyval;
+
+ for my $val (@hda) {
+ if (!grep /^$val$/, qw (all co pv own status)) {
+ return qq{The value '$val' is not valid for key '$akey'};
+ }
+ }
+ }
}
if ($akey eq 'genPVdeviation' && $keyval eq 'daily') {
@@ -7325,7 +7477,7 @@ sub __attrKeyAction {
delete $data{$name}{circular}{99}{tdayDvtn};
}
-return;
+return $err;
}
################################################################
@@ -9040,7 +9192,7 @@ sub _specialActivities {
deleteConsumerPlanning ($hash, $c);
}
- writeCacheToFile ($hash, "consumers", $csmcache.$name); # Cache File Consumer schreiben
+ writeCacheToFile ($hash, 'consumers', $csmcache.$name, 'nolog'); # Cache File Consumer schreiben
Log3 ($name, 4, "$name - Daily special tasks - Task 3 finished");
}
@@ -9744,6 +9896,7 @@ sub _transferAPIRadiationValues {
$paref->{fd} = $fd;
$data{$name}{nexthours}{$nhtstr}{starttime} = $wantdt;
+ $data{$name}{nexthours}{$nhtstr}{day} = $wtday;
$data{$name}{nexthours}{$nhtstr}{hourofday} = $hod;
$data{$name}{nexthours}{$nhtstr}{today} = $fd == 0 ? 1 : 0;
$data{$name}{nexthours}{$nhtstr}{rad1h} = $rad1h;
@@ -10658,14 +10811,18 @@ sub _transferBatteryValues {
writeToHistory ( { paref => $paref, key => 'batoutthishour'.$bn, val => $batoutthishour, hour => $nhour } );
- # täglichen max. SOC in pvHistory speichern
- #############################################
+ # täglichen maximalen SOC in pvHistory speichern
+ ##################################################
my $batmaxsoc = HistoryVal ($hash, $day, 99, 'batmaxsoc'.$bn, 0); # gespeicherter max. SOC des Tages
if ($soc >= $batmaxsoc) {
writeToHistory ( { paref => $paref, key => 'batmaxsoc'.$bn, val => $soc, hour => 99 } );
}
+ # aktuellen SOC in pvHistory speichern
+ ########################################
+ writeToHistory ( { paref => $paref, key => 'batsoc'.$bn, val => $soc, hour => $nhour } );
+
######
storeReading ('Today_Hour'.sprintf("%02d",$nhour).'_BatIn_'. $bn, $batinthishour. ' Wh'.$warnin);
@@ -10687,8 +10844,6 @@ sub _transferBatteryValues {
$data{$name}{batteries}{$bn}{bposingraph} = $pos; # Anzeigeposition in Balkengrafik
$data{$name}{batteries}{$bn}{bchargewh} = BatteryVal ($name, $bn, 'binstcap', 0) * $soc / 100; # Batterie SoC (Wh)
- writeToHistory ( { paref => $paref, key => 'batsoc'.$bn, val => $soc, hour => $nhour } );
-
$num++;
$socsum += $soc;
$socwhsum += BatteryVal ($name, $bn, 'binstcap', 0) * $soc / 100; # Batterie SoC in Wh
@@ -11037,6 +11192,7 @@ sub _batChargeRecmd {
my $nhr = sprintf "%02d", $num;
my $today = NexthoursVal ($hash, 'NextHour'.$nhr, 'today', 0);
+ my $hod = NexthoursVal ($hash, 'NextHour'.$nhr, 'hourofday', '');
my $confc = NexthoursVal ($hash, 'NextHour'.$nhr, 'confc', 0);
my $pvfc = NexthoursVal ($hash, 'NextHour'.$nhr, 'pvfc', 0);
my $stt = NexthoursVal ($hash, 'NextHour'.$nhr, 'starttime', '');
@@ -11128,6 +11284,12 @@ sub _batChargeRecmd {
$data{$name}{nexthours}{'NextHour'.$nhr}{'rcdchargebat'.$bn} = $crel;
$data{$name}{nexthours}{'NextHour'.$nhr}{'soc'.$bn} = $progsoc;
+
+ # prognostizierten SOC in pvHistory speichern
+ ###############################################
+ if ($today && $hod) { # heutiger Tag
+ writeToHistory ( { paref => $paref, key => 'batprogsoc'.$bn, val => $progsoc, hour => $hod } );
+ }
debugLog ($paref, 'batteryManagement', "Bat $bn relLoad $stt -> $crel ($msg)");
}
@@ -12027,7 +12189,7 @@ sub ___doPlanning {
Log3 ($name, 3, qq{$name - Consumer "$calias" $planstate $planspmlt});
}
- writeCacheToFile ($hash, "consumers", $csmcache.$name); # Cache File Consumer schreiben
+ writeCacheToFile ($hash, 'consumers', $csmcache.$name); # Cache File Consumer schreiben
___setPlanningDeleteMeth ($paref);
@@ -12503,7 +12665,7 @@ sub ___switchConsumerOn {
___setConsumerPlanningState ($paref);
delete $paref->{ps};
- writeCacheToFile ($hash, "consumers", $csmcache.$name); # Cache File Consumer schreiben
+ writeCacheToFile ($hash, 'consumers', $csmcache.$name); # Cache File Consumer schreiben
}
}
elsif ((($isintable == 1 && $isConsRcmd) || # unterbrochenen Consumer fortsetzen
@@ -12527,7 +12689,7 @@ sub ___switchConsumerOn {
___setConsumerPlanningState ($paref);
delete $paref->{ps};
- writeCacheToFile ($hash, "consumers", $csmcache.$name); # Cache File Consumer schreiben
+ writeCacheToFile ($hash, 'consumers', $csmcache.$name); # Cache File Consumer schreiben
}
return $state;
@@ -12596,7 +12758,7 @@ sub ___switchConsumerOff {
___setConsumerPlanningState ($paref);
delete $paref->{ps};
- writeCacheToFile ($hash, "consumers", $csmcache.$name); # Cache File Consumer schreiben
+ writeCacheToFile ($hash, 'consumers', $csmcache.$name); # Cache File Consumer schreiben
}
elsif ((($isintable && !isConsRcmd ($hash, $c)) || $isintable == 2) && # Consumer unterbrechen
isInTimeframe ($hash, $c) && $auto && $offcom && !$iilt &&
@@ -12617,7 +12779,7 @@ sub ___switchConsumerOff {
___setConsumerPlanningState ($paref);
delete $paref->{ps};
- writeCacheToFile ($hash, "consumers", $csmcache.$name); # Cache File Consumer schreiben
+ writeCacheToFile ($hash, 'consumers', $csmcache.$name); # Cache File Consumer schreiben
}
return $state;
@@ -12738,7 +12900,7 @@ sub ___setConsumerSwitchingState {
if ($dowri) {
if (!$fscss) {
- writeCacheToFile ($hash, "consumers", $csmcache.$name); # Cache File Consumer schreiben
+ writeCacheToFile ($hash, 'consumers', $csmcache.$name); # Cache File Consumer schreiben
}
Log3 ($name, 2, "$name - $state");
@@ -13289,17 +13451,16 @@ sub _calcReadingsTomorrowPVFc {
for my $idx (sort keys %{$h}) {
my $today = NexthoursVal ($hash, $idx, 'today', 1);
- next if($today); # aktueller Tag wird nicht benötigt
+ next if($today); # aktueller Tag wird nicht benötigt
- my $h = NexthoursVal ($hash, $idx, 'hourofday', '');
- next if(!$h);
+ my $hod = NexthoursVal ($hash, $idx, 'hourofday', '');
+ next if(!$hod);
- next if($hods !~ /$h/xs); # diese Stunde des Tages soll nicht erzeugt werden
+ next if($hods !~ /$hod/xs); # diese Stunde des Tages soll nicht erzeugt werden
- my $st = NexthoursVal ($hash, $idx, 'starttime', 'XXXX-XX-XX XX:XX:XX'); # Starttime
my $pvfc = NexthoursVal ($hash, $idx, 'pvfc', 0);
- storeReading ('Tomorrow_Hour'.$h.'_PVforecast', $pvfc.' Wh');
+ storeReading ('Tomorrow_Hour'.$hod.'_PVforecast', $pvfc.' Wh');
}
return;
@@ -14100,6 +14261,8 @@ sub entryGraphic {
my $gsel = shift // ''; # direkte Auswahl welche Grafik zurück gegeben werden soll (both, flow, forecast)
my $pah = shift // 0; # 1 wenn durch pageAsHtml aufgerufen
+ return if(!$init_done);
+
my $hash = $defs{$name};
# Setup Vollständigkeit/disabled prüfen
@@ -14408,11 +14571,10 @@ return $ret;
# Vollständigkeit Setup prüfen
################################################################
sub _checkSetupNotComplete {
- my $hash = shift;
- my $ret = q{};
+ my $hash = shift;
+ my $ret = q{};
- my $name = $hash->{NAME};
- my $type = $hash->{TYPE};
+ my $name = $hash->{NAME};
my $strings = AttrVal ($name, 'setupInverterStrings', undef); # String Konfig
my $wedev = AttrVal ($name, 'setupWeatherDev1', undef); # Device Vorhersage Wetterdaten (Bewölkung etc.)
@@ -18829,7 +18991,7 @@ sub setPVhistory {
my $type = $paref->{type};
my $day = $paref->{day};
my $dayname = $paref->{dayname}; # aktueller Wochentagsname
- my $nhour = $paref->{nhour};
+ my $nhour = $paref->{nhour}; # Stunde des Tages
my $histname = $paref->{histname};
my $val = $paref->{val}; # Wert zur Speicherung in pvHistory (soll mal generell verwendet werden -> Change)
my $reorg = $paref->{reorg} // 0; # Neuberechnung von Werten in Stunde "99" nach Löschen von Stunden eines Tages
@@ -18960,7 +19122,7 @@ sub setPVhistory {
}
if ($histname) {
- debugLog ($paref, 'saveData2Cache', "setPVhistory -> store Day: $day, Hour: $nhour, Key: $histname, Value: ".(defined $val ? $val : 'undef'));
+ debugLog ($paref, 'saveData2Cache', "setPVhistory -> store Day: $day, Hour of Day: $nhour, Key: $histname, Value: ".(defined $val ? $val : 'undef'));
}
return;
@@ -19177,16 +19339,17 @@ sub _listDataPoolPvHist {
$prdl .= "pprl${pn}: $pprl";
}
- my ($btotin, $batin, $btotout, $batout, $batmsoc, $batssoc, $batsoc);
+ my ($btotin, $batin, $btotout, $batout, $batmsoc, $batssoc, $batprogsoc, $batsoc);
for my $bn (1..MAXBATTERIES) { # + alle Batterien
- $bn = sprintf "%02d", $bn;
- my $hbtotin = HistoryVal ($name, $day, $key, 'batintotal'.$bn, '-');
- my $hbtotout = HistoryVal ($name, $day, $key, 'batouttotal'.$bn, '-');
- my $hbatin = HistoryVal ($name, $day, $key, 'batin'.$bn, '-');
- my $hbatout = HistoryVal ($name, $day, $key, 'batout'.$bn, '-');
- my $hbatmsoc = HistoryVal ($name, $day, $key, 'batmaxsoc'.$bn, '-');
- my $hbatssoc = HistoryVal ($name, $day, $key, 'batsetsoc'.$bn, '-');
- my $hbatsoc = HistoryVal ($name, $day, $key, 'batsoc'.$bn, '-');
+ $bn = sprintf "%02d", $bn;
+ my $hbtotin = HistoryVal ($name, $day, $key, 'batintotal'.$bn, '-');
+ my $hbtotout = HistoryVal ($name, $day, $key, 'batouttotal'.$bn, '-');
+ my $hbatin = HistoryVal ($name, $day, $key, 'batin'.$bn, '-');
+ my $hbatout = HistoryVal ($name, $day, $key, 'batout'.$bn, '-');
+ my $hbatmsoc = HistoryVal ($name, $day, $key, 'batmaxsoc'.$bn, '-');
+ my $hbatssoc = HistoryVal ($name, $day, $key, 'batsetsoc'.$bn, '-');
+ my $hbatprogsoc = HistoryVal ($name, $day, $key, 'batprogsoc'.$bn, '-');
+ my $hbatsoc = HistoryVal ($name, $day, $key, 'batsoc'.$bn, '-');
if ($export eq 'csv') {
$hexp->{$day}{$key}{"BatteryInTotal${bn}"} = $hbtotin;
@@ -19195,23 +19358,26 @@ sub _listDataPoolPvHist {
$hexp->{$day}{$key}{"BatteryOut${bn}"} = $hbatout;
$hexp->{$day}{$key}{"BatteryMaxSoc${bn}"} = $hbatmsoc;
$hexp->{$day}{$key}{"BatterySetSoc${bn}"} = $hbatssoc;
+ $hexp->{$day}{$key}{"BatteryProgSoc${bn}"} = $hbatprogsoc;
$hexp->{$day}{$key}{"BatterySoc${bn}"} = $hbatsoc;
}
- $btotin .= ', ' if($btotin);
- $btotin .= "batintotal${bn}: $hbtotin";
- $btotout .= ', ' if($btotout);
- $btotout .= "batouttotal${bn}: $hbtotout";
- $batin .= ', ' if($batin);
- $batin .= "batin${bn}: $hbatin";
- $batout .= ', ' if($batout);
- $batout .= "batout${bn}: $hbatout";
- $batmsoc .= ', ' if($batmsoc);
- $batmsoc .= "batmaxsoc${bn}: $hbatmsoc";
- $batssoc .= ', ' if($batssoc);
- $batssoc .= "batsetsoc${bn}: $hbatssoc";
- $batsoc .= ', ' if($batsoc);
- $batsoc .= "batsoc${bn}: $hbatsoc";
+ $btotin .= ', ' if($btotin);
+ $btotin .= "batintotal${bn}: $hbtotin";
+ $btotout .= ', ' if($btotout);
+ $btotout .= "batouttotal${bn}: $hbtotout";
+ $batin .= ', ' if($batin);
+ $batin .= "batin${bn}: $hbatin";
+ $batout .= ', ' if($batout);
+ $batout .= "batout${bn}: $hbatout";
+ $batmsoc .= ', ' if($batmsoc);
+ $batmsoc .= "batmaxsoc${bn}: $hbatmsoc";
+ $batssoc .= ', ' if($batssoc);
+ $batssoc .= "batsetsoc${bn}: $hbatssoc";
+ $batprogsoc .= ', ' if($batprogsoc);
+ $batprogsoc .= "batprogsoc${bn}: $hbatprogsoc";
+ $batsoc .= ', ' if($batsoc);
+ $batsoc .= "batsoc${bn}: $hbatsoc";
}
$ret .= "\n " if($ret);
@@ -19237,6 +19403,9 @@ sub _listDataPoolPvHist {
$ret .= "\n " if($key ne '99');
$ret .= $btotout if($key ne '99');
$ret .= "\n " if($key ne '99');
+
+ $ret .= $batprogsoc if($key ne '99');
+ $ret .= "\n " if($key ne '99');
$ret .= $batsoc if($key ne '99');
$ret .= "\n " if($key ne '99');
@@ -19245,10 +19414,10 @@ sub _listDataPoolPvHist {
$ret .= $batout;
$ret .= "\n ";
- $ret .= $batmsoc if($key eq '99');
- $ret .= "\n " if($key eq '99');
- $ret .= $batssoc if($key eq '99');
- $ret .= "\n " if($key eq '99');
+ $ret .= $batmsoc if($key eq '99');
+ $ret .= "\n " if($key eq '99');
+ $ret .= $batssoc if($key eq '99');
+ $ret .= "\n " if($key eq '99');
if ($key ne '99') {
$ret .= "weatherid: $wid, ";
@@ -19604,6 +19773,7 @@ sub _listDataPoolNextHours {
for my $idx (sort keys %{$h}) {
my $nhts = NexthoursVal ($name, $idx, 'starttime', '-');
+ my $day = NexthoursVal ($name, $idx, 'day', '-');
my $hod = NexthoursVal ($name, $idx, 'hourofday', '-');
my $today = NexthoursVal ($name, $idx, 'today', '-');
my $pvfc = NexthoursVal ($name, $idx, 'pvfc', '-');
@@ -19637,7 +19807,7 @@ sub _listDataPoolNextHours {
$sq .= "\n" if($sq);
$sq .= $idx." => ";
- $sq .= "starttime: $nhts, hourofday: $hod, today: $today";
+ $sq .= "starttime: $nhts, day: $day, hourofday: $hod, today: $today";
$sq .= "\n ";
$sq .= "pvapifc: $pvapifc, pvaifc: $pvaifc, pvfc: $pvfc, aihit: $aihit";
$sq .= "\n ";
@@ -24172,7 +24342,7 @@ to ensure that the system configuration is correct.
- nextHours
- Lists the expected values for the coming hours.
+ Lists the stored values for the coming hours.
@@ -24184,6 +24354,7 @@ to ensure that the system configuration is correct.
| correff | correction factor/quality used |
| | <factor>/- -> no quality defined |
| | <factor>/0..1 - quality of the PV forecast (1 = best quality) |
+ | day | Date of day |
| DoN | sunrise and sunset status (0 - night, 1 - day) |
| hourofday | current hour of the day |
| pvapifc | expected PV generation (Wh) of the used API incl. a possible correction |
@@ -24227,7 +24398,8 @@ to ensure that the system configuration is correct.
| batinXX | Charge of battery XX within the hour (Wh) |
| batouttotalXX | total battery XX discharge (Wh) at the beginning of the hour |
| batoutXX | Discharge of battery XX within the hour (Wh) |
- | batsocXX | State of charge SOC (%) of battery XX at the end of the hour |
+ | batprogsocXX | predicted state of charge SOC (%) of battery XX at the end of the hour |
+ | batsocXX | real State of charge SOC (%) of battery XX at the end of the hour |
| batmaxsocXX | Maximum SOC (%) achieved by battery XX on the day |
| batsetsocXX | Optimum SOC setpoint (%) of battery XX for the day |
| confc | expected energy consumption (Wh) |
@@ -26718,7 +26890,7 @@ die ordnungsgemäße Anlagenkonfiguration geprüft werden.
- nextHours
- Listet die erwarteten Werte der kommenden Stunden auf.
+ Listet die gespeicherte Werte der kommenden Stunden auf.
@@ -26730,6 +26902,7 @@ die ordnungsgemäße Anlagenkonfiguration geprüft werden.
| correff | verwendeter Korrekturfaktor/Qualität |
| | <Faktor>/- -> keine Qualität definiert |
| | <Faktor>/0..1 - Qualität der PV Prognose (1 = beste Qualität) |
+ | day | Tagesdatum |
| DoN | Sonnenauf- und untergangsstatus (0 - Nacht, 1 - Tag) |
| hourofday | laufende Stunde des Tages |
| pvapifc | erwartete PV Erzeugung (Wh) der verwendeten API inkl. einer eventuellen Korrektur |
@@ -26773,7 +26946,8 @@ die ordnungsgemäße Anlagenkonfiguration geprüft werden.
| batinXX | Ladung der Batterie XX innerhalb der Stunde (Wh) |
| batouttotalXX | Gesamtentladung der Batterie XX (Wh) zu Beginn der Stunde |
| batoutXX | Entladung der Batterie XX innerhalb der Stunde (Wh) |
- | batsocXX | Ladezustand SOC (%) der Batterie XX am Ende der Stunde |
+ | batprogsocXX | prognostizierte Ladezustand SOC (%) der Batterie XX am Ende der Stunde |
+ | batsocXX | realer Ladezustand SOC (%) der Batterie XX am Ende der Stunde |
| batmaxsocXX | maximal erreichter SOC (%) der Batterie XX an dem Tag |
| batsetsocXX | optimaler SOC Sollwert (%) der Batterie XX für den Tag |
| csmtXX | Energieverbrauch total von ConsumerXX |