2
0
mirror of https://github.com/fhem/fhem-mirror.git synced 2025-01-31 06:39:11 +00:00

76_SolarForecast: internal code change for data collection

git-svn-id: https://svn.fhem.de/fhem/trunk@29531 2b470e98-0d58-463d-a4d8-8e2adae1ed80
This commit is contained in:
nasseeder1 2025-01-17 15:45:50 +00:00
parent 46e2aaa51e
commit 212641ccbd
3 changed files with 418 additions and 181 deletions

View File

@ -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
- change: 76_SolarForecast: internal code change for data collection
- change: 76_SolarForecast: FlowGraphic: SoC as a Cluster value of all Batts
- bugfix: 76_SolarForecast: bugfix of version 1.43.3
- change: 76_SolarForecast: consumption fc calc switch from average to median

View File

@ -157,6 +157,7 @@ BEGIN {
# Versions History intern
my %vNotesIntern = (
"1.43.6" => "17.01.2025 _calcCaQcomplex: additional write pvrl, pvfc to separate circular hash Array elements, listDataPool: show these Arrays ",
"1.43.5" => "15.01.2025 _flowGraphic: calculate the resulting SoC as a cluster of batteries ",
"1.43.4" => "14.01.2025 batsocslidereg: calculate the SoC as summary over all capacities in Wh, bugfix https://forum.fhem.de/index.php?msg=1330559 ",
"1.43.3" => "13.01.2025 add Wiki icon in graphic header, _calcConsumptionForecast: switch calc from average to median, edit comref ",
@ -7093,9 +7094,8 @@ sub writeCacheToFile {
return ('', $nr, $na);
}
my @arr;
return if(!keys %{$data{$name}{$cachename}});
push @arr, encode_json ($data{$name}{$cachename});
push my @arr, encode_json ($data{$name}{$cachename});
$error = FileWrite ($file, @arr);
@ -8747,7 +8747,7 @@ sub _transferAPIRadiationValues {
my $est = __calcPVestimates ($paref);
my ($msg, $pvaifc) = aiGetResult ($paref); # KI Entscheidungen abfragen
$data{$name}{nexthours}{$nhtstr}{pvapifc} = $est; # durch API gelieferte PV Forecast
$data{$name}{nexthours}{$nhtstr}{pvapifc} = $est; # durch API gelieferte PV Forecast
delete $paref->{fd};
delete $paref->{fh1};
@ -12225,20 +12225,28 @@ sub _calcCaQcomplex {
debugLog ($paref, 'pvCorrectionWrite', "Autolearning is switched off for hour: $h -> skip the recalculation of the complex correction factor");
return;
}
my $pvrl = CircularVal ($hash, sprintf("%02d",$h), 'pvrl', 0);
my $pvfc = CircularVal ($hash, sprintf("%02d",$h), 'pvapifc', 0);
my $hh = sprintf "%02d", $h;
my $pvrl = CircularVal ($hash, $hh, 'pvrl', 0); # real erzeugte PV Energie am Ende der vorherigen Stunde
my $pvfc = CircularVal ($hash, $hh, 'pvapifc', 0); # vorhergesagte PV Energie am Ende der vorherigen Stunde
if (!$pvrl || !$pvfc) {
storeReading ('.pvCorrectionFactor_'.sprintf("%02d",$h).'_cloudcover', 'done');
storeReading ('.pvCorrectionFactor_'.$hh.'_cloudcover', 'done');
return;
}
my $chwcc = HistoryVal ($hash, $day, sprintf("%02d",$h), 'wcc', 0); # Wolkenbedeckung Heute & abgefragte Stunde
my $sunalt = HistoryVal ($hash, $day, sprintf("%02d",$h), 'sunalt', 0); # Sonne Altitude
my $chwcc = HistoryVal ($hash, $day, $hh, 'wcc', 0); # Wolkenbedeckung heute & abgefragte Stunde
my $sunalt = HistoryVal ($hash, $day, $hh, 'sunalt', 0); # Sonne Altitude
my $crang = cloud2bin ($chwcc);
my $sabin = sunalt2bin ($sunalt);
## Speicherarrays schreiben
#############################
push @{$data{$name}{circular}{$hh}{'pvrl_'.$sabin}{"$crang"}}, $pvrl;
push @{$data{$name}{circular}{$hh}{'pvfc_'.$sabin}{"$crang"}}, $pvfc;
## neuen Korrekturfaktor berechnen
####################################
$paref->{pvrl} = $pvrl;
$paref->{pvfc} = $pvfc;
$paref->{crang} = $crang;
@ -12253,16 +12261,16 @@ sub _calcCaQcomplex {
delete $paref->{sabin};
delete $paref->{calc};
storeReading ('.pvCorrectionFactor_'.sprintf("%02d",$h).'_cloudcover', 'done');
storeReading ('.pvCorrectionFactor_'.$hh.'_cloudcover', 'done');
$aihit = $aihit ? ' AI result used,' : '';
if ($acu =~ /on_complex/xs) {
if ($paref->{cpcf} !~ /manual/xs) { # pcf-Reading nur überschreiben wenn nicht 'manual xxx' gesetzt
storeReading ('pvCorrectionFactor_'.sprintf("%02d",$h), $factor." (automatic - old factor: $oldfac,$aihit Sun Alt range: $sabin, Cloud range: $crang, Days in range: $dnum)");
storeReading ('pvCorrectionFactor_'.$hh, $factor." (automatic - old factor: $oldfac,$aihit Sun Alt range: $sabin, Cloud range: $crang, Days in range: $dnum)");
}
else {
storeReading ('pvCorrectionFactor_'.sprintf("%02d",$h), $paref->{cpcf}." / flexmatic result $factor for Sun Alt range: $sabin,$aihit Cloud range: $crang, Days in range: $dnum");
storeReading ('pvCorrectionFactor_'.$hh, $paref->{cpcf}." / flexmatic result $factor for Sun Alt range: $sabin,$aihit Cloud range: $crang, Days in range: $dnum");
}
}
@ -12284,28 +12292,29 @@ sub _calcCaQsimple {
my $aihit = $paref->{aihit};
my $hash = $defs{$name};
my $sr = ReadingsVal ($name, '.pvCorrectionFactor_'.sprintf("%02d",$h).'_apipercentil', '');
my $hh = sprintf "%02d", $h;
my $sr = ReadingsVal ($name, '.pvCorrectionFactor_'.$hh.'_apipercentil', '');
if($sr eq "done") {
# debugLog ($paref, 'pvCorrectionWrite', "Simple Corrf factor Hour: ".sprintf("%02d",$h)." already calculated");
# debugLog ($paref, 'pvCorrectionWrite', "Simple Corrf factor Hour: ".$hh." already calculated");
return;
}
if (!$aln) {
storeReading ('.pvCorrectionFactor_'.sprintf("%02d",$h).'_apipercentil', 'done');
storeReading ('.pvCorrectionFactor_'.$hh.'_apipercentil', 'done');
debugLog ($paref, 'pvCorrectionWrite', "Autolearning is switched off for hour: $h -> skip the recalculation of the simple correction factor");
return;
}
my $pvrl = CircularVal ($hash, sprintf("%02d",$h), 'pvrl', 0);
my $pvfc = CircularVal ($hash, sprintf("%02d",$h), 'pvapifc', 0);
my $pvrl = CircularVal ($hash, $hh, 'pvrl', 0);
my $pvfc = CircularVal ($hash, $hh, 'pvapifc', 0);
if (!$pvrl || !$pvfc) {
storeReading ('.pvCorrectionFactor_'.sprintf("%02d",$h).'_apipercentil', 'done');
storeReading ('.pvCorrectionFactor_'.$hh.'_apipercentil', 'done');
return;
}
my $sunalt = HistoryVal ($hash, $day, sprintf("%02d",$h), 'sunalt', 0); # Sonne Altitude
my $sunalt = HistoryVal ($hash, $day, $hh, 'sunalt', 0); # Sonne Altitude
my $sabin = sunalt2bin ($sunalt);
$paref->{pvrl} = $pvrl;
@ -12322,16 +12331,16 @@ sub _calcCaQsimple {
delete $paref->{crang};
delete $paref->{calc};
storeReading ('.pvCorrectionFactor_'.sprintf("%02d",$h).'_apipercentil', 'done');
storeReading ('.pvCorrectionFactor_'.$hh.'_apipercentil', 'done');
$aihit = $aihit ? ' AI result used,' : '';
if ($acu =~ /on_simple/xs) {
if ($paref->{cpcf} !~ /manual/xs) { # pcf-Reading nur überschreiben wenn nicht 'manual xxx' gesetzt
storeReading ('pvCorrectionFactor_'.sprintf("%02d",$h), $factor." (automatic - old factor: $oldfac,$aihit Days in range: $dnum)");
storeReading ('pvCorrectionFactor_'.$hh, $factor." (automatic - old factor: $oldfac,$aihit Days in range: $dnum)");
}
else {
storeReading ('pvCorrectionFactor_'.sprintf("%02d",$h), $paref->{cpcf}." / flexmatic result $factor,$aihit Days in range: $dnum");
storeReading ('pvCorrectionFactor_'.$hh, $paref->{cpcf}." / flexmatic result $factor,$aihit Days in range: $dnum");
}
}
@ -12359,8 +12368,9 @@ sub __calcNewFactor {
debugLog ($paref, 'pvCorrectionWrite', "$calc Corrf -> Start calculation correction factor for hour: $h");
my ($oldfac, $oldq) = CircularSunCloudkorrVal ($hash, sprintf("%02d",$h), $sabin, $crang, 0); # bisher definierter Korrekturfaktor / Qualität
my ($pvhis, $fchis, $dnum) = CircularSumVal ($hash, sprintf("%02d",$h), $sabin, $crang, 0);
my $hh = sprintf "%02d", $h;
my ($oldfac, $oldq) = CircularSunCloudkorrVal ($hash, $hh, $sabin, $crang, 0); # bisher definierter Korrekturfaktor / Qualität
my ($pvhis, $fchis, $dnum) = CircularSumVal ($hash, $hh, $sabin, $crang, 0);
$oldfac = 1 if(1 * $oldfac == 0);
debugLog ($paref, 'pvCorrectionWrite', "$calc Corrf -> read historical values: pv real sum: $pvhis, pv forecast sum: $fchis, days sum: $dnum");
@ -12403,18 +12413,18 @@ sub __calcNewFactor {
if ($crang ne 'simple') {
my $idx = $sabin.'.'.$crang; # value für pvcorrf Sonne Altitude
$data{$name}{circular}{sprintf("%02d",$h)}{pvrlsum}{$idx} = $pvrlsum; # PV Erzeugung Summe speichern
$data{$name}{circular}{sprintf("%02d",$h)}{pvfcsum}{$idx} = $pvfcsum; # PV Prognose Summe speichern
$data{$name}{circular}{sprintf("%02d",$h)}{dnumsum}{$idx} = $dnum; # Anzahl aller historischen Tade dieser Range
$data{$name}{circular}{sprintf("%02d",$h)}{pvcorrf}{$idx} = $factor;
$data{$name}{circular}{sprintf("%02d",$h)}{quality}{$idx} = $qual;
$data{$name}{circular}{$hh}{pvrlsum}{$idx} = $pvrlsum; # PV Erzeugung Summe speichern
$data{$name}{circular}{$hh}{pvfcsum}{$idx} = $pvfcsum; # PV Prognose Summe speichern
$data{$name}{circular}{$hh}{dnumsum}{$idx} = $dnum; # Anzahl aller historischen Tade dieser Range
$data{$name}{circular}{$hh}{pvcorrf}{$idx} = $factor;
$data{$name}{circular}{$hh}{quality}{$idx} = $qual;
}
else {
$data{$name}{circular}{sprintf("%02d",$h)}{pvrlsum}{$crang} = $pvrlsum;
$data{$name}{circular}{sprintf("%02d",$h)}{pvfcsum}{$crang} = $pvfcsum;
$data{$name}{circular}{sprintf("%02d",$h)}{dnumsum}{$crang} = $dnum;
$data{$name}{circular}{sprintf("%02d",$h)}{pvcorrf}{$crang} = $factor;
$data{$name}{circular}{sprintf("%02d",$h)}{quality}{$crang} = $qual;
$data{$name}{circular}{$hh}{pvrlsum}{$crang} = $pvrlsum;
$data{$name}{circular}{$hh}{pvfcsum}{$crang} = $pvfcsum;
$data{$name}{circular}{$hh}{dnumsum}{$crang} = $dnum;
$data{$name}{circular}{$hh}{pvcorrf}{$crang} = $factor;
$data{$name}{circular}{$hh}{quality}{$crang} = $qual;
}
$oldfac = sprintf "%.2f", $oldfac;
@ -12422,6 +12432,27 @@ sub __calcNewFactor {
return ($oldfac, $factor, $dnum);
}
################################################################
# Qualität der Vorhersage berechnen
################################################################
sub __calcFcQuality {
my $pvfc = shift; # PV Vorhersagewert
my $pvrl = shift; # PV reale Erzeugung
return if(!$pvfc || !$pvrl);
$pvrl = sprintf "%.0f", $pvrl;
$pvfc = sprintf "%.0f", $pvfc;
my $diff = $pvfc - $pvrl;
my $hdv = 1 - abs ($diff / $pvrl); # Abweichung der Stunde, 1 = bestmöglicher Wert
$hdv = $hdv < 0 ? 0 : $hdv;
$hdv = sprintf "%.2f", $hdv;
return $hdv;
}
################################################################
# Berechnen Tag / Stunden Verschieber
# aus aktueller Stunde + lfd. Nummer
@ -16252,27 +16283,6 @@ sub _addHourAiRawdata {
return;
}
################################################################
# Qualität der Vorhersage berechnen
################################################################
sub __calcFcQuality {
my $pvfc = shift; # PV Vorhersagewert
my $pvrl = shift; # PV reale Erzeugung
return if(!$pvfc || !$pvrl);
$pvrl = sprintf "%.0f", $pvrl;
$pvfc = sprintf "%.0f", $pvfc;
my $diff = $pvfc - $pvrl;
my $hdv = 1 - abs ($diff / $pvrl); # Abweichung der Stunde, 1 = bestmöglicher Wert
$hdv = $hdv < 0 ? 0 : $hdv;
$hdv = sprintf "%.2f", $hdv;
return $hdv;
}
###############################################################
# Eintritt in den KI Train Prozess normal/Blocking
###############################################################
@ -17112,14 +17122,6 @@ sub listDataPool {
my $sub = sub {
my $day = shift;
#for my $dh (keys %{$h->{$day}}) {
# if (!isNumeric ($dh)) {
# delete $data{$name}{pvhist}{$day}{$dh};
# Log3 ($name, 2, qq{$name - INFO - invalid key "$day -> $dh" was deleted from pvHistory storage});
# }
#}
my $ret;
for my $key (sort {$a<=>$b} keys %{$h->{$day}}) {
@ -17404,7 +17406,7 @@ sub listDataPool {
for my $ckey (sort keys %{$h->{$idx}}) {
if (ref $h->{$idx}{$ckey} eq 'ARRAY') {
my $aser = join " ",@{$h->{$idx}{$ckey}};
my $aser = join " ", @{$h->{$idx}{$ckey}};
$cret .= ($s1 ? $sp1 : "").$ckey." => ".$aser."\n";
}
@ -17431,6 +17433,35 @@ sub listDataPool {
if (!keys %{$h}) {
return qq{Circular cache is empty.};
}
### nicht mehr benötigte Daten verarbeiten - Bereich kann später wieder raus !!
##########################################################################################################################
delete $data{$name}{circular}{'01'}{pvrl_5};
delete $data{$name}{circular}{'01'}{pvrl_10};
delete $data{$name}{circular}{'01'}{pvrl_25};
delete $data{$name}{circular}{'01'}{pvrl_60};
delete $data{$name}{circular}{'01'}{pvrl_65};
delete $data{$name}{circular}{'01'}{pvrl_90};
#push @{$data{$name}{circular}{'01'}{pvrl_65}{100}}, 4561;
#push @{$data{$name}{circular}{'01'}{pvrl_65}{100}}, 4562;
#push @{$data{$name}{circular}{'01'}{pvrl_65}{100}}, 4563;
#push @{$data{$name}{circular}{'01'}{pvrl_65}{100}}, 4564;
#push @{$data{$name}{circular}{'01'}{pvrl_65}{100}}, 4565;
#
#push @{$data{$name}{circular}{'01'}{pvrl_65}{60}}, 3561;
#push @{$data{$name}{circular}{'01'}{pvrl_65}{60}}, 3562;
#
#push @{$data{$name}{circular}{'01'}{pvrl_90}{100}}, 4561;
#push @{$data{$name}{circular}{'01'}{pvrl_90}{100}}, 4562;
#push @{$data{$name}{circular}{'01'}{pvrl_90}{100}}, 4563;
#push @{$data{$name}{circular}{'01'}{pvrl_90}{100}}, 4564;
#push @{$data{$name}{circular}{'01'}{pvrl_90}{100}}, 4565;
#push @{$data{$name}{circular}{'01'}{pvrl_90}{60}}, 3561;
#push @{$data{$name}{circular}{'01'}{pvrl_90}{60}}, 3562;
############################################################################################################
for my $idx (sort keys %{$h}) {
my $pvrl = CircularVal ($hash, $idx, 'pvrl', '-');
@ -17490,17 +17521,41 @@ sub listDataPool {
$bout .= "batout${bn}: $batout";
}
$sq .= $idx." => pvapifc: $pvapifc, pvaifc: $pvaifc, pvfc: $pvfc, aihit: $aihit, pvrl: $pvrl\n";
$sq .= " $bin\n";
$sq .= " $bout\n";
$sq .= " confc: $confc, gcon: $gcons, gfeedin: $gfeedin, wcc: $wccv, rr1c: $rr1c\n";
$sq .= " temp: $temp, wid: $wid, wtxt: $wtxt\n";
$sq .= " $prdl\n";
$sq .= " pvcorrf: $pvcf\n";
$sq .= " quality: $cfq\n";
$sq .= " pvrlsum: $pvrs\n";
$sq .= " pvfcsum: $pvfs\n";
$sq .= " dnumsum: $dnus";
my ($pvrlnew, $pvfcnew);
my @pvrlkeys = map { $_ =~ /^pvrl_/xs ? $_ : '' } sort keys %{$h->{$idx}};
my @pvfckeys = map { $_ =~ /^pvfc_/xs ? $_ : '' } sort keys %{$h->{$idx}};
for my $prl (@pvrlkeys) {
next if(!$prl);
my $lref = CircularVal ($hash, $idx, $prl, '');
next if(!$lref);
$pvrlnew .= "\n " if($pvrlnew);
$pvrlnew .= _ldchash2val ( { pool => $h, idx => $idx, key => $prl, cval => $lref } );
}
for my $pfc (@pvfckeys) {
next if(!$pfc);
my $cref = CircularVal ($hash, $idx, $pfc, '');
next if(!$cref);
$pvfcnew .= "\n " if($pvfcnew);
$pvfcnew .= _ldchash2val ( { pool => $h, idx => $idx, key => $pfc, cval => $cref } );
}
$sq .= $idx." => pvapifc: $pvapifc, pvaifc: $pvaifc, pvfc: $pvfc, aihit: $aihit, pvrl: $pvrl";
$sq .= "\n $bin";
$sq .= "\n $bout";
$sq .= "\n confc: $confc, gcon: $gcons, gfeedin: $gfeedin, wcc: $wccv, rr1c: $rr1c";
$sq .= "\n temp: $temp, wid: $wid, wtxt: $wtxt";
$sq .= "\n $prdl";
$sq .= "\n pvcorrf: $pvcf";
$sq .= "\n quality: $cfq";
$sq .= "\n pvrlsum: $pvrs";
$sq .= "\n pvfcsum: $pvfs";
$sq .= "\n dnumsum: $dnus";
$sq .= "\n $pvrlnew" if($pvrlnew);
$sq .= "\n $pvfcnew" if($pvfcnew);
}
else {
my ($batvl1, $batvl2, $batvl3, $batvl4, $batvl5, $batvl6, $batvl7);
@ -17772,7 +17827,24 @@ sub _ldchash2val {
for my $f (sort {$a<=>$b} keys %{$pool->{$idx}{$key}}) {
next if($f eq 'simple');
if ($f !~ /\./xs) {
if (ref $pool->{$idx}{$key}{$f} eq 'ARRAY') {
my @sub_arrays = arraySplitBy (20, @{$pool->{$idx}{$key}{$f}}); # Array in Teil-Arrays zu je 20 Elemente aufteilen
for my $suaref (@sub_arrays) { # für jedes Teil-Array Join ausführen
my $suajoined = join ' ', @{$suaref};
if (!$ret) {
$ret .= $key.' => ';
$ret .= $f.' @ '.$suajoined;
}
else {
$ret .= "\n ";
$ret .= $f.' @ '.$suajoined;
}
}
}
elsif ($f !~ /\./xs) {
$ret .= " " if($ret);
$ret .= "$f=".$pool->{$idx}{$key}{$f};
my $ct = ($ret =~ tr/=// // 0) / 10;
@ -20219,6 +20291,48 @@ sub sunalt2bin {
return $bin;
}
###############################################################################
# Teilt das Original-Array in Unter-Arrays auf, die den Inhalt des
# Originals enthalten. Die Größe jedes Unterarrays ist gleich oder kleiner als
# $split_size, wobei das letzte Array in der Regel kleiner ist, wenn nicht
# genügend Elemente in @original vorhanden sind.
# (aus https://metacpan.org/dist/Array-Split/source/lib/Array/Split.pm)
#
# arraySplitBy ($split_size, @original)
###############################################################################
sub arraySplitBy {
my $split_size = shift;
$split_size = max ($split_size, 1);
my @sub_arrays;
while (@_) {
push @sub_arrays, [splice @_, 0, $split_size];
}
return @sub_arrays;
}
###############################################################################
# Teilt das angegebene Array in die Anzahl $count Unterarrays auf.
# Es wird versucht, so viele Unter-Arrays zu erstellen, wie $count angibt,
# aber es werden weniger zurückgegeben, wenn nicht genügend Elemente in
# @original vorhanden sind.
#
# Gibt eine Liste von Array-Referenzen zurück.
# (aus https://metacpan.org/dist/Array-Split/source/lib/Array/Split.pm)
#
# arraySplitInto ($count, @original)
###############################################################################
sub arraySplitInto {
my ($count, @original) = @_;
$count = max( $count, 1 );
my $size = ceil @original / $count;
return arraySplitBy ($size, @original);
}
###############################################################################
# verscrambelt einen String
###############################################################################

View File

@ -157,6 +157,8 @@ BEGIN {
# Versions History intern
my %vNotesIntern = (
"1.43.6" => "17.01.2025 _calcCaQcomplex: additional write pvrl, pvfc to separate circular hash Array elements, listDataPool: show these Arrays ",
"1.43.5" => "15.01.2025 _flowGraphic: calculate the resulting SoC as a cluster of batteries ",
"1.43.4" => "14.01.2025 batsocslidereg: calculate the SoC as summary over all capacities in Wh, bugfix https://forum.fhem.de/index.php?msg=1330559 ",
"1.43.3" => "13.01.2025 add Wiki icon in graphic header, _calcConsumptionForecast: switch calc from average to median, edit comref ",
"1.43.2" => "12.01.2025 _batChargeRecmd: bugfix calc socwh, Attr graphicBeam1MaxVal, (experimental) ctrlAreaFactorUsage are obsolete ".
@ -7092,9 +7094,8 @@ sub writeCacheToFile {
return ('', $nr, $na);
}
my @arr;
return if(!keys %{$data{$name}{$cachename}});
push @arr, encode_json ($data{$name}{$cachename});
push my @arr, encode_json ($data{$name}{$cachename});
$error = FileWrite ($file, @arr);
@ -8746,7 +8747,7 @@ sub _transferAPIRadiationValues {
my $est = __calcPVestimates ($paref);
my ($msg, $pvaifc) = aiGetResult ($paref); # KI Entscheidungen abfragen
$data{$name}{nexthours}{$nhtstr}{pvapifc} = $est; # durch API gelieferte PV Forecast
$data{$name}{nexthours}{$nhtstr}{pvapifc} = $est; # durch API gelieferte PV Forecast
delete $paref->{fd};
delete $paref->{fh1};
@ -9571,14 +9572,15 @@ sub _transferBatteryValues {
storeReading ('Current_PowerBatOut_'.$bn, $pbo.' W');
storeReading ('Current_BatCharge_'. $bn, $soc.' %');
$data{$name}{batteries}{$bn}{bname} = $badev; # Batterie Devicename
$data{$name}{batteries}{$bn}{balias} = AttrVal ($badev, 'alias', $badev); # Alias Batterie Device
$data{$name}{batteries}{$bn}{bpowerin} = $pbi; # momentane Batterieladung
$data{$name}{batteries}{$bn}{bpowerout} = $pbo; # momentane Batterieentladung
$data{$name}{batteries}{$bn}{bcharge} = $soc; # aktuelle Batterieladung
$data{$name}{batteries}{$bn}{basynchron} = $h->{asynchron} // 0; # asynchroner Modus = X
$data{$name}{batteries}{$bn}{bicon} = $h->{icon} if($h->{icon}); # Batterie Icon
$data{$name}{batteries}{$bn}{bshowingraph} = $h->{show} // 0; # Batterie in Balkengrafik anzeigen
$data{$name}{batteries}{$bn}{bname} = $badev; # Batterie Devicename
$data{$name}{batteries}{$bn}{balias} = AttrVal ($badev, 'alias', $badev); # Alias Batterie Device
$data{$name}{batteries}{$bn}{bpowerin} = $pbi; # momentane Batterieladung
$data{$name}{batteries}{$bn}{bpowerout} = $pbo; # momentane Batterieentladung
$data{$name}{batteries}{$bn}{bcharge} = $soc; # Batterie SoC (%)
$data{$name}{batteries}{$bn}{basynchron} = $h->{asynchron} // 0; # asynchroner Modus = X
$data{$name}{batteries}{$bn}{bicon} = $h->{icon} if($h->{icon}); # Batterie Icon
$data{$name}{batteries}{$bn}{bshowingraph} = $h->{show} // 0; # Batterie in Balkengrafik anzeigen
$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 } );
@ -9591,7 +9593,7 @@ sub _transferBatteryValues {
}
if ($num) {
my $soctotal = sprintf "%.0f", ($socwhsum / $bcapsum * 100) if($bcapsum); # resultierender SoC (%) aller Batterien als "eine"
my $soctotal = sprintf "%.0f", ($socwhsum / $bcapsum * 100) if($bcapsum); # resultierender SoC (%) aller Batterien als "eine"
push @{$data{$name}{current}{batsocslidereg}}, $soctotal; # Schieberegister average SOC aller Batterien
limitArray ($data{$name}{current}{batsocslidereg}, $slidenummax);
@ -12223,20 +12225,28 @@ sub _calcCaQcomplex {
debugLog ($paref, 'pvCorrectionWrite', "Autolearning is switched off for hour: $h -> skip the recalculation of the complex correction factor");
return;
}
my $pvrl = CircularVal ($hash, sprintf("%02d",$h), 'pvrl', 0);
my $pvfc = CircularVal ($hash, sprintf("%02d",$h), 'pvapifc', 0);
my $hh = sprintf "%02d", $h;
my $pvrl = CircularVal ($hash, $hh, 'pvrl', 0); # real erzeugte PV Energie am Ende der vorherigen Stunde
my $pvfc = CircularVal ($hash, $hh, 'pvapifc', 0); # vorhergesagte PV Energie am Ende der vorherigen Stunde
if (!$pvrl || !$pvfc) {
storeReading ('.pvCorrectionFactor_'.sprintf("%02d",$h).'_cloudcover', 'done');
storeReading ('.pvCorrectionFactor_'.$hh.'_cloudcover', 'done');
return;
}
my $chwcc = HistoryVal ($hash, $day, sprintf("%02d",$h), 'wcc', 0); # Wolkenbedeckung Heute & abgefragte Stunde
my $sunalt = HistoryVal ($hash, $day, sprintf("%02d",$h), 'sunalt', 0); # Sonne Altitude
my $chwcc = HistoryVal ($hash, $day, $hh, 'wcc', 0); # Wolkenbedeckung heute & abgefragte Stunde
my $sunalt = HistoryVal ($hash, $day, $hh, 'sunalt', 0); # Sonne Altitude
my $crang = cloud2bin ($chwcc);
my $sabin = sunalt2bin ($sunalt);
## Speicherarrays schreiben
#############################
push @{$data{$name}{circular}{$hh}{'pvrl_'.$sabin}{"$crang"}}, $pvrl;
push @{$data{$name}{circular}{$hh}{'pvfc_'.$sabin}{"$crang"}}, $pvfc;
## neuen Korrekturfaktor berechnen
####################################
$paref->{pvrl} = $pvrl;
$paref->{pvfc} = $pvfc;
$paref->{crang} = $crang;
@ -12251,16 +12261,16 @@ sub _calcCaQcomplex {
delete $paref->{sabin};
delete $paref->{calc};
storeReading ('.pvCorrectionFactor_'.sprintf("%02d",$h).'_cloudcover', 'done');
storeReading ('.pvCorrectionFactor_'.$hh.'_cloudcover', 'done');
$aihit = $aihit ? ' AI result used,' : '';
if ($acu =~ /on_complex/xs) {
if ($paref->{cpcf} !~ /manual/xs) { # pcf-Reading nur überschreiben wenn nicht 'manual xxx' gesetzt
storeReading ('pvCorrectionFactor_'.sprintf("%02d",$h), $factor." (automatic - old factor: $oldfac,$aihit Sun Alt range: $sabin, Cloud range: $crang, Days in range: $dnum)");
storeReading ('pvCorrectionFactor_'.$hh, $factor." (automatic - old factor: $oldfac,$aihit Sun Alt range: $sabin, Cloud range: $crang, Days in range: $dnum)");
}
else {
storeReading ('pvCorrectionFactor_'.sprintf("%02d",$h), $paref->{cpcf}." / flexmatic result $factor for Sun Alt range: $sabin,$aihit Cloud range: $crang, Days in range: $dnum");
storeReading ('pvCorrectionFactor_'.$hh, $paref->{cpcf}." / flexmatic result $factor for Sun Alt range: $sabin,$aihit Cloud range: $crang, Days in range: $dnum");
}
}
@ -12282,28 +12292,29 @@ sub _calcCaQsimple {
my $aihit = $paref->{aihit};
my $hash = $defs{$name};
my $sr = ReadingsVal ($name, '.pvCorrectionFactor_'.sprintf("%02d",$h).'_apipercentil', '');
my $hh = sprintf "%02d", $h;
my $sr = ReadingsVal ($name, '.pvCorrectionFactor_'.$hh.'_apipercentil', '');
if($sr eq "done") {
# debugLog ($paref, 'pvCorrectionWrite', "Simple Corrf factor Hour: ".sprintf("%02d",$h)." already calculated");
# debugLog ($paref, 'pvCorrectionWrite', "Simple Corrf factor Hour: ".$hh." already calculated");
return;
}
if (!$aln) {
storeReading ('.pvCorrectionFactor_'.sprintf("%02d",$h).'_apipercentil', 'done');
storeReading ('.pvCorrectionFactor_'.$hh.'_apipercentil', 'done');
debugLog ($paref, 'pvCorrectionWrite', "Autolearning is switched off for hour: $h -> skip the recalculation of the simple correction factor");
return;
}
my $pvrl = CircularVal ($hash, sprintf("%02d",$h), 'pvrl', 0);
my $pvfc = CircularVal ($hash, sprintf("%02d",$h), 'pvapifc', 0);
my $pvrl = CircularVal ($hash, $hh, 'pvrl', 0);
my $pvfc = CircularVal ($hash, $hh, 'pvapifc', 0);
if (!$pvrl || !$pvfc) {
storeReading ('.pvCorrectionFactor_'.sprintf("%02d",$h).'_apipercentil', 'done');
storeReading ('.pvCorrectionFactor_'.$hh.'_apipercentil', 'done');
return;
}
my $sunalt = HistoryVal ($hash, $day, sprintf("%02d",$h), 'sunalt', 0); # Sonne Altitude
my $sunalt = HistoryVal ($hash, $day, $hh, 'sunalt', 0); # Sonne Altitude
my $sabin = sunalt2bin ($sunalt);
$paref->{pvrl} = $pvrl;
@ -12320,16 +12331,16 @@ sub _calcCaQsimple {
delete $paref->{crang};
delete $paref->{calc};
storeReading ('.pvCorrectionFactor_'.sprintf("%02d",$h).'_apipercentil', 'done');
storeReading ('.pvCorrectionFactor_'.$hh.'_apipercentil', 'done');
$aihit = $aihit ? ' AI result used,' : '';
if ($acu =~ /on_simple/xs) {
if ($paref->{cpcf} !~ /manual/xs) { # pcf-Reading nur überschreiben wenn nicht 'manual xxx' gesetzt
storeReading ('pvCorrectionFactor_'.sprintf("%02d",$h), $factor." (automatic - old factor: $oldfac,$aihit Days in range: $dnum)");
storeReading ('pvCorrectionFactor_'.$hh, $factor." (automatic - old factor: $oldfac,$aihit Days in range: $dnum)");
}
else {
storeReading ('pvCorrectionFactor_'.sprintf("%02d",$h), $paref->{cpcf}." / flexmatic result $factor,$aihit Days in range: $dnum");
storeReading ('pvCorrectionFactor_'.$hh, $paref->{cpcf}." / flexmatic result $factor,$aihit Days in range: $dnum");
}
}
@ -12357,8 +12368,9 @@ sub __calcNewFactor {
debugLog ($paref, 'pvCorrectionWrite', "$calc Corrf -> Start calculation correction factor for hour: $h");
my ($oldfac, $oldq) = CircularSunCloudkorrVal ($hash, sprintf("%02d",$h), $sabin, $crang, 0); # bisher definierter Korrekturfaktor / Qualität
my ($pvhis, $fchis, $dnum) = CircularSumVal ($hash, sprintf("%02d",$h), $sabin, $crang, 0);
my $hh = sprintf "%02d", $h;
my ($oldfac, $oldq) = CircularSunCloudkorrVal ($hash, $hh, $sabin, $crang, 0); # bisher definierter Korrekturfaktor / Qualität
my ($pvhis, $fchis, $dnum) = CircularSumVal ($hash, $hh, $sabin, $crang, 0);
$oldfac = 1 if(1 * $oldfac == 0);
debugLog ($paref, 'pvCorrectionWrite', "$calc Corrf -> read historical values: pv real sum: $pvhis, pv forecast sum: $fchis, days sum: $dnum");
@ -12401,18 +12413,18 @@ sub __calcNewFactor {
if ($crang ne 'simple') {
my $idx = $sabin.'.'.$crang; # value für pvcorrf Sonne Altitude
$data{$name}{circular}{sprintf("%02d",$h)}{pvrlsum}{$idx} = $pvrlsum; # PV Erzeugung Summe speichern
$data{$name}{circular}{sprintf("%02d",$h)}{pvfcsum}{$idx} = $pvfcsum; # PV Prognose Summe speichern
$data{$name}{circular}{sprintf("%02d",$h)}{dnumsum}{$idx} = $dnum; # Anzahl aller historischen Tade dieser Range
$data{$name}{circular}{sprintf("%02d",$h)}{pvcorrf}{$idx} = $factor;
$data{$name}{circular}{sprintf("%02d",$h)}{quality}{$idx} = $qual;
$data{$name}{circular}{$hh}{pvrlsum}{$idx} = $pvrlsum; # PV Erzeugung Summe speichern
$data{$name}{circular}{$hh}{pvfcsum}{$idx} = $pvfcsum; # PV Prognose Summe speichern
$data{$name}{circular}{$hh}{dnumsum}{$idx} = $dnum; # Anzahl aller historischen Tade dieser Range
$data{$name}{circular}{$hh}{pvcorrf}{$idx} = $factor;
$data{$name}{circular}{$hh}{quality}{$idx} = $qual;
}
else {
$data{$name}{circular}{sprintf("%02d",$h)}{pvrlsum}{$crang} = $pvrlsum;
$data{$name}{circular}{sprintf("%02d",$h)}{pvfcsum}{$crang} = $pvfcsum;
$data{$name}{circular}{sprintf("%02d",$h)}{dnumsum}{$crang} = $dnum;
$data{$name}{circular}{sprintf("%02d",$h)}{pvcorrf}{$crang} = $factor;
$data{$name}{circular}{sprintf("%02d",$h)}{quality}{$crang} = $qual;
$data{$name}{circular}{$hh}{pvrlsum}{$crang} = $pvrlsum;
$data{$name}{circular}{$hh}{pvfcsum}{$crang} = $pvfcsum;
$data{$name}{circular}{$hh}{dnumsum}{$crang} = $dnum;
$data{$name}{circular}{$hh}{pvcorrf}{$crang} = $factor;
$data{$name}{circular}{$hh}{quality}{$crang} = $qual;
}
$oldfac = sprintf "%.2f", $oldfac;
@ -12420,6 +12432,27 @@ sub __calcNewFactor {
return ($oldfac, $factor, $dnum);
}
################################################################
# Qualität der Vorhersage berechnen
################################################################
sub __calcFcQuality {
my $pvfc = shift; # PV Vorhersagewert
my $pvrl = shift; # PV reale Erzeugung
return if(!$pvfc || !$pvrl);
$pvrl = sprintf "%.0f", $pvrl;
$pvfc = sprintf "%.0f", $pvfc;
my $diff = $pvfc - $pvrl;
my $hdv = 1 - abs ($diff / $pvrl); # Abweichung der Stunde, 1 = bestmöglicher Wert
$hdv = $hdv < 0 ? 0 : $hdv;
$hdv = sprintf "%.2f", $hdv;
return $hdv;
}
################################################################
# Berechnen Tag / Stunden Verschieber
# aus aktueller Stunde + lfd. Nummer
@ -15195,9 +15228,12 @@ sub _flowGraphic {
## definierte Batterien ermitteln und zusammenfassen
######################################################
my ($batin, $bat2home, $soc, @batsoc);
for my $bn (1..$maxbatteries) { # für jede definierte Batterie
$bn = sprintf "%02d", $bn;
my ($batin, $bat2home);
my $socwhsum = 0;
my $soc = 0;
for my $bn (1..$maxbatteries) { # für jede definierte Batterie
$bn = sprintf "%02d", $bn;
my ($err, $badev, $h) = isDeviceValid ( { name => $name, obj => 'setupBatteryDev'.$bn, method => 'attr' } );
next if($err);
@ -15206,10 +15242,11 @@ sub _flowGraphic {
$batin += $batinpow if(defined $batinpow);
$bat2home += $bat2homepow if(defined $bat2homepow);
push @batsoc, ReadingsNum ($name, 'Current_BatCharge_'.$bn, 0);
$socwhsum += BatteryVal ($name, $bn, 'bchargewh', 0); # Batterie SoC in Wh
}
$soc = avgArray (\@batsoc, scalar @batsoc) if(@batsoc);
my $batcapsum = CurrentVal ($hash, 'batcapsum', 0); # Summe installierte Batterie Kapazität
$soc = sprintf "%.0f", ($socwhsum / $batcapsum * 100) if($batcapsum); # resultierender SoC (%) aller Batterien als Cluster
if (!defined $batin && !defined $bat2home) {
$hasbat = 0;
@ -16246,27 +16283,6 @@ sub _addHourAiRawdata {
return;
}
################################################################
# Qualität der Vorhersage berechnen
################################################################
sub __calcFcQuality {
my $pvfc = shift; # PV Vorhersagewert
my $pvrl = shift; # PV reale Erzeugung
return if(!$pvfc || !$pvrl);
$pvrl = sprintf "%.0f", $pvrl;
$pvfc = sprintf "%.0f", $pvfc;
my $diff = $pvfc - $pvrl;
my $hdv = 1 - abs ($diff / $pvrl); # Abweichung der Stunde, 1 = bestmöglicher Wert
$hdv = $hdv < 0 ? 0 : $hdv;
$hdv = sprintf "%.2f", $hdv;
return $hdv;
}
###############################################################
# Eintritt in den KI Train Prozess normal/Blocking
###############################################################
@ -17106,14 +17122,6 @@ sub listDataPool {
my $sub = sub {
my $day = shift;
#for my $dh (keys %{$h->{$day}}) {
# if (!isNumeric ($dh)) {
# delete $data{$name}{pvhist}{$day}{$dh};
# Log3 ($name, 2, qq{$name - INFO - invalid key "$day -> $dh" was deleted from pvHistory storage});
# }
#}
my $ret;
for my $key (sort {$a<=>$b} keys %{$h->{$day}}) {
@ -17398,7 +17406,7 @@ sub listDataPool {
for my $ckey (sort keys %{$h->{$idx}}) {
if (ref $h->{$idx}{$ckey} eq 'ARRAY') {
my $aser = join " ",@{$h->{$idx}{$ckey}};
my $aser = join " ", @{$h->{$idx}{$ckey}};
$cret .= ($s1 ? $sp1 : "").$ckey." => ".$aser."\n";
}
@ -17425,6 +17433,35 @@ sub listDataPool {
if (!keys %{$h}) {
return qq{Circular cache is empty.};
}
### nicht mehr benötigte Daten verarbeiten - Bereich kann später wieder raus !!
##########################################################################################################################
delete $data{$name}{circular}{'01'}{pvrl_5};
delete $data{$name}{circular}{'01'}{pvrl_10};
delete $data{$name}{circular}{'01'}{pvrl_25};
delete $data{$name}{circular}{'01'}{pvrl_60};
delete $data{$name}{circular}{'01'}{pvrl_65};
delete $data{$name}{circular}{'01'}{pvrl_90};
#push @{$data{$name}{circular}{'01'}{pvrl_65}{100}}, 4561;
#push @{$data{$name}{circular}{'01'}{pvrl_65}{100}}, 4562;
#push @{$data{$name}{circular}{'01'}{pvrl_65}{100}}, 4563;
#push @{$data{$name}{circular}{'01'}{pvrl_65}{100}}, 4564;
#push @{$data{$name}{circular}{'01'}{pvrl_65}{100}}, 4565;
#
#push @{$data{$name}{circular}{'01'}{pvrl_65}{60}}, 3561;
#push @{$data{$name}{circular}{'01'}{pvrl_65}{60}}, 3562;
#
#push @{$data{$name}{circular}{'01'}{pvrl_90}{100}}, 4561;
#push @{$data{$name}{circular}{'01'}{pvrl_90}{100}}, 4562;
#push @{$data{$name}{circular}{'01'}{pvrl_90}{100}}, 4563;
#push @{$data{$name}{circular}{'01'}{pvrl_90}{100}}, 4564;
#push @{$data{$name}{circular}{'01'}{pvrl_90}{100}}, 4565;
#push @{$data{$name}{circular}{'01'}{pvrl_90}{60}}, 3561;
#push @{$data{$name}{circular}{'01'}{pvrl_90}{60}}, 3562;
############################################################################################################
for my $idx (sort keys %{$h}) {
my $pvrl = CircularVal ($hash, $idx, 'pvrl', '-');
@ -17484,17 +17521,41 @@ sub listDataPool {
$bout .= "batout${bn}: $batout";
}
$sq .= $idx." => pvapifc: $pvapifc, pvaifc: $pvaifc, pvfc: $pvfc, aihit: $aihit, pvrl: $pvrl\n";
$sq .= " $bin\n";
$sq .= " $bout\n";
$sq .= " confc: $confc, gcon: $gcons, gfeedin: $gfeedin, wcc: $wccv, rr1c: $rr1c\n";
$sq .= " temp: $temp, wid: $wid, wtxt: $wtxt\n";
$sq .= " $prdl\n";
$sq .= " pvcorrf: $pvcf\n";
$sq .= " quality: $cfq\n";
$sq .= " pvrlsum: $pvrs\n";
$sq .= " pvfcsum: $pvfs\n";
$sq .= " dnumsum: $dnus";
my ($pvrlnew, $pvfcnew);
my @pvrlkeys = map { $_ =~ /^pvrl_/xs ? $_ : '' } sort keys %{$h->{$idx}};
my @pvfckeys = map { $_ =~ /^pvfc_/xs ? $_ : '' } sort keys %{$h->{$idx}};
for my $prl (@pvrlkeys) {
next if(!$prl);
my $lref = CircularVal ($hash, $idx, $prl, '');
next if(!$lref);
$pvrlnew .= "\n " if($pvrlnew);
$pvrlnew .= _ldchash2val ( { pool => $h, idx => $idx, key => $prl, cval => $lref } );
}
for my $pfc (@pvfckeys) {
next if(!$pfc);
my $cref = CircularVal ($hash, $idx, $pfc, '');
next if(!$cref);
$pvfcnew .= "\n " if($pvfcnew);
$pvfcnew .= _ldchash2val ( { pool => $h, idx => $idx, key => $pfc, cval => $cref } );
}
$sq .= $idx." => pvapifc: $pvapifc, pvaifc: $pvaifc, pvfc: $pvfc, aihit: $aihit, pvrl: $pvrl";
$sq .= "\n $bin";
$sq .= "\n $bout";
$sq .= "\n confc: $confc, gcon: $gcons, gfeedin: $gfeedin, wcc: $wccv, rr1c: $rr1c";
$sq .= "\n temp: $temp, wid: $wid, wtxt: $wtxt";
$sq .= "\n $prdl";
$sq .= "\n pvcorrf: $pvcf";
$sq .= "\n quality: $cfq";
$sq .= "\n pvrlsum: $pvrs";
$sq .= "\n pvfcsum: $pvfs";
$sq .= "\n dnumsum: $dnus";
$sq .= "\n $pvrlnew" if($pvrlnew);
$sq .= "\n $pvfcnew" if($pvfcnew);
}
else {
my ($batvl1, $batvl2, $batvl3, $batvl4, $batvl5, $batvl6, $batvl7);
@ -17766,7 +17827,24 @@ sub _ldchash2val {
for my $f (sort {$a<=>$b} keys %{$pool->{$idx}{$key}}) {
next if($f eq 'simple');
if ($f !~ /\./xs) {
if (ref $pool->{$idx}{$key}{$f} eq 'ARRAY') {
my @sub_arrays = arraySplitBy (20, @{$pool->{$idx}{$key}{$f}}); # Array in Teil-Arrays zu je 20 Elemente aufteilen
for my $suaref (@sub_arrays) { # für jedes Teil-Array Join ausführen
my $suajoined = join ' ', @{$suaref};
if (!$ret) {
$ret .= $key.' => ';
$ret .= $f.' @ '.$suajoined;
}
else {
$ret .= "\n ";
$ret .= $f.' @ '.$suajoined;
}
}
}
elsif ($f !~ /\./xs) {
$ret .= " " if($ret);
$ret .= "$f=".$pool->{$idx}{$key}{$f};
my $ct = ($ret =~ tr/=// // 0) / 10;
@ -20213,6 +20291,48 @@ sub sunalt2bin {
return $bin;
}
###############################################################################
# Teilt das Original-Array in Unter-Arrays auf, die den Inhalt des
# Originals enthalten. Die Größe jedes Unterarrays ist gleich oder kleiner als
# $split_size, wobei das letzte Array in der Regel kleiner ist, wenn nicht
# genügend Elemente in @original vorhanden sind.
# (aus https://metacpan.org/dist/Array-Split/source/lib/Array/Split.pm)
#
# arraySplitBy ($split_size, @original)
###############################################################################
sub arraySplitBy {
my $split_size = shift;
$split_size = max ($split_size, 1);
my @sub_arrays;
while (@_) {
push @sub_arrays, [splice @_, 0, $split_size];
}
return @sub_arrays;
}
###############################################################################
# Teilt das angegebene Array in die Anzahl $count Unterarrays auf.
# Es wird versucht, so viele Unter-Arrays zu erstellen, wie $count angibt,
# aber es werden weniger zurückgegeben, wenn nicht genügend Elemente in
# @original vorhanden sind.
#
# Gibt eine Liste von Array-Referenzen zurück.
# (aus https://metacpan.org/dist/Array-Split/source/lib/Array/Split.pm)
#
# arraySplitInto ($count, @original)
###############################################################################
sub arraySplitInto {
my ($count, @original) = @_;
$count = max( $count, 1 );
my $size = ceil @original / $count;
return arraySplitBy ($size, @original);
}
###############################################################################
# verscrambelt einen String
###############################################################################
@ -21972,13 +22092,14 @@ to ensure that the system configuration is correct.
<ul>
<table>
<colgroup> <col width="25%"> <col width="75%"> </colgroup>
<tr><td> <b>bname </b> </td><td>Name of the device </td></tr>
<tr><td> <b>balias </b> </td><td>Alias of the device </td></tr>
<tr><td> <b>basynchron </b> </td><td>Mode of processing received battery events </td></tr>
<tr><td> <b>bcharge </b> </td><td>SOC (State of Charge) of the battery (%) </td></tr>
<tr><td> <b>binstcap </b> </td><td>installed battery capacity (Wh) </td></tr>
<tr><td> <b>bpowerin </b> </td><td>current charging power (W) </td></tr>
<tr><td> <b>bpowerout </b> </td><td>current discharge power (W) </td></tr>
<tr><td> <b>bname </b> </td><td>Name of the device </td></tr>
<tr><td> <b>balias </b> </td><td>Alias of the device </td></tr>
<tr><td> <b>basynchron </b> </td><td>Mode of processing received battery events </td></tr>
<tr><td> <b>bcharge </b> </td><td>current SoC (State of Charge) of the battery (%) </td></tr>
<tr><td> <b>bchargewh </b> </td><td>current SoC (State of Charge) of the battery (Wh) </td></tr>
<tr><td> <b>binstcap </b> </td><td>installed battery capacity (Wh) </td></tr>
<tr><td> <b>bpowerin </b> </td><td>current charging power (W) </td></tr>
<tr><td> <b>bpowerout </b> </td><td>current discharge power (W) </td></tr>
</table>
</ul>
@ -23687,7 +23808,7 @@ die ordnungsgemäße Anlagenkonfiguration geprüft werden.
<li><b>batteryTrigger &lt;1on&gt;=&lt;Wert&gt; &lt;1off&gt;=&lt;Wert&gt; [&lt;2on&gt;=&lt;Wert&gt; &lt;2off&gt;=&lt;Wert&gt; ...] </b> <br><br>
Generiert Trigger bei Über- bzw. Unterschreitung bestimmter Batterieladungswerte (SoC in %). <br>
Der verwendete SoC wird als resultierender SoC (Summe aktuelle Ladung aller Batterie Geräte zur im Verhältnis zur installierten
Der verwendete SoC wird als resultierender SoC (Summe aktuelle Ladung aller Batterie Geräte im Verhältnis zur installierten
Gesamtkapazität) gebildet, d.h. alle Batterien werden als ein Cluster betrachtet. <br>
Überschreiten die letzten drei SoC-Messungen eine definierte <b>Xon-Bedingung</b>, wird das Reading
<b>batteryTrigger_X = on</b> erstellt/gesetzt. <br>
@ -24444,13 +24565,14 @@ die ordnungsgemäße Anlagenkonfiguration geprüft werden.
<ul>
<table>
<colgroup> <col width="25%"> <col width="75%"> </colgroup>
<tr><td> <b>bname </b> </td><td>Name des Gerätes </td></tr>
<tr><td> <b>balias </b> </td><td>Alias des Gerätes </td></tr>
<tr><td> <b>basynchron </b> </td><td>Modus der Verarbeitung empfangener Batterie-Events </td></tr>
<tr><td> <b>bcharge </b> </td><td>SOC (State of Charge) der Batterie (%) </td></tr>
<tr><td> <b>binstcap </b> </td><td>installierte Batteriekapazität (Wh) </td></tr>
<tr><td> <b>bpowerin </b> </td><td>momentane Ladeleistung (W) </td></tr>
<tr><td> <b>bpowerout </b> </td><td>momentane Entladeleistung (W) </td></tr>
<tr><td> <b>bname </b> </td><td>Name des Gerätes </td></tr>
<tr><td> <b>balias </b> </td><td>Alias des Gerätes </td></tr>
<tr><td> <b>basynchron </b> </td><td>Modus der Verarbeitung empfangener Batterie-Events </td></tr>
<tr><td> <b>bcharge </b> </td><td>aktueller SoC (State of Charge) der Batterie (%) </td></tr>
<tr><td> <b>bchargewh </b> </td><td>aktueller SoC (State of Charge) der Batterie (Wh) </td></tr>
<tr><td> <b>binstcap </b> </td><td>installierte Batteriekapazität (Wh) </td></tr>
<tr><td> <b>bpowerin </b> </td><td>momentane Ladeleistung (W) </td></tr>
<tr><td> <b>bpowerout </b> </td><td>momentane Entladeleistung (W) </td></tr>
</table>
</ul>