diff --git a/fhem/CHANGED b/fhem/CHANGED
index 15659d4b0..6f9fe5c28 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
+ - change: 76_SolarForecast: remove graphicBeam1MaxVal,ctrlAreaFactorUsage
- feature: 76_SolarForecast: new special readings todayBatInSum,todayBatOutSum
- change: 73_AutoShuttersControl: Change version
- feature: 76_SolarForecast: show historical battery SoC when displaying the
diff --git a/fhem/FHEM/76_SolarForecast.pm b/fhem/FHEM/76_SolarForecast.pm
index 13490fc24..cae8be787 100644
--- a/fhem/FHEM/76_SolarForecast.pm
+++ b/fhem/FHEM/76_SolarForecast.pm
@@ -34,6 +34,7 @@ package FHEM::SolarForecast;
use strict;
use warnings;
+#use lib qw(/opt/fhem/FHEM /opt/fhem/lib); # für Syntaxcheck mit: perl -c /opt/fhem/FHEM/76_SolarForecast.pm
use POSIX;
use GPUtils qw(GP_Import GP_Export); # wird für den Import der FHEM Funktionen aus der fhem.pl benötigt
use Time::HiRes qw(gettimeofday tv_interval);
@@ -159,6 +160,8 @@ BEGIN {
# Versions History intern
my %vNotesIntern = (
+ "1.44.4" => "26.01.2025 _getlistPVCircular: change width of output, new sub _listDataPoolPvHist, fix bug in hrepl Hash ".
+ "remove Attr graphicBeam1MaxVal,ctrlAreaFactorUsage ",
"1.44.3" => "25.01.2025 Notification System: minor changes, special Readings todayBatInSum todayBatOutSum ",
"1.44.2" => "23.01.2025 _batChargeRecmd: user storeffdef, show historical battery SoC when displaying the battery in the bar graph ",
"1.44.1" => "20.01.2025 Notification system: minor fixes, integration of controls_solarforecast_messages_test/prod ".
@@ -174,7 +177,7 @@ my %vNotesIntern = (
"trackFlex now default in DWD Model, replace title Charging recommendation by Charging release ".
"_saveEnergyConsumption: add dowrite flag, edit comref ",
"1.43.1" => "11.01.2025 _batChargeRecmd: bugfix PV daily surplus update, _collectAllRegConsumers: fix interruptable hysteresis ".
- "__batRcmdOnBeam: show soc forecast for hour 00 and fix english translation ".
+ "__batteryOnBeam: show soc forecast for hour 00 and fix english translation ".
"_batChargeRecmd: consider battery capacity as part of total capacity ",
"1.43.0" => "10.01.2025 graphicShowNight: add possible Time Sync of chart bar level 1 and the other ".
"_addDynAttr: minor fix for graphicBeamXContent, new attr ctrlNextHoursSoCForecastReadings ",
@@ -707,17 +710,17 @@ my %hmoon = (
);
my %hrepl = ( # Zeichenersetzungen
- '0' => 'a',
- '1' => 'b',
- '2' => 'c',
- '3' => 'd',
- '4' => 'e',
- '5' => 'f',
- '6' => 'g',
- '7' => 'h',
- '8' => 'i',
- '9' => 'j',
- '.' => 'k',
+ '0' => 'a',
+ '1' => 'b',
+ '2' => 'c',
+ '3' => 'd',
+ '4' => 'e',
+ '5' => 'f',
+ '6' => 'g',
+ '7' => 'h',
+ '8' => 'i',
+ '9' => 'j',
+ '\.' => 'k',
);
my %hqtxt = ( # Hash (Setup) Texte
@@ -1448,12 +1451,11 @@ sub Initialize {
$readingFnAttributes;
## Hinweis: graphicBeamXContent wird in _addDynAttr hinzugefügt
-
-
+
### nicht mehr benötigte Daten verarbeiten - Bereich kann später wieder raus !!
##########################################################################################################################
- my $av1 = "obsolete#-#the#attribute#will#be#deleted#soon"; # 12.01.25
- $hash->{AttrList} .= " graphicBeam1MaxVal:$av1 ctrlAreaFactorUsage:$av1 ";
+ # my $av1 = "obsolete#-#the#attribute#will#be#deleted#soon";
+ # $hash->{AttrList} .= " graphicBeam1MaxVal:$av1 ctrlAreaFactorUsage:$av1 ";
##########################################################################################################################
$hash->{FW_hideDisplayName} = 1; # Forum 88667
@@ -4743,7 +4745,7 @@ sub _getlistPVCircular {
my $hash = $defs{$name};
my $ret = listDataPool ($hash, 'circular', $arg);
- $ret .= lineFromSpaces ($ret, 20);
+ $ret .= lineFromSpaces ($ret, 5);
return $ret;
}
@@ -5536,16 +5538,16 @@ sub Attr {
### nicht mehr benötigte Daten verarbeiten - Bereich kann später wieder raus !!
######################################################################################################################
- if ($cmd eq 'set' && $aName =~ /^graphicBeam1MaxVal|ctrlAreaFactorUsage$/) { # 12.01.25
- my $msg = "The attribute $aName is obsolete and will be deleted soon. Please save your Configuration.";
- if (!$init_done) {
- Log3 ($name, 1, "$name - $msg");
- return qq{Device "$name" -> $msg};
- }
- else {
- return $msg;
- }
- }
+ #if ($cmd eq 'set' && $aName =~ /^graphicBeam1MaxVal|ctrlAreaFactorUsage$/) {
+ # my $msg = "The attribute $aName is obsolete and will be deleted soon. Please save your Configuration.";
+ # if (!$init_done) {
+ # Log3 ($name, 1, "$name - $msg");
+ # return qq{Device "$name" -> $msg};
+ # }
+ # else {
+ # return $msg;
+ # }
+ #}
######################################################################################################################
if ($aName eq 'disable') {
@@ -8289,14 +8291,6 @@ sub __delObsoleteAPIData {
delete $data{$name}{solcastapi}{$idx}{$scd} if($ds && $ds < $refts);
}
}
-
- ### nicht mehr benötigte Daten verarbeiten - Bereich kann später wieder raus !!
- ##########################################################################################################################
- # 01.12.2024
- for my $idx (keys %{$data{$name}{solcastapi}{'?All'}}) { # Wetterindexe löschen (kann später raus)
- delete $data{$name}{solcastapi}{'?All'}{$idx} if($idx =~ /^fc?([0-9]{1,2})_?([0-9]{1,2})$/xs);
- }
- #####################################################################################################################
}
## veraltete Strings aus Strings-Hash löschen
@@ -14848,7 +14842,7 @@ sub _beamGraphic {
my $ret = q{};
$ret .= __weatherOnBeam ($paref) if($weather);
- $ret .= __batRcmdOnBeam ($paref);
+ $ret .= __batteryOnBeam ($paref);
my $m = $paref->{modulo} % 2;
if ($show_diff eq 'top') { # Zusätzliche Zeile Ertrag - Verbrauch
@@ -15252,7 +15246,7 @@ return $ret;
################################################################
# Batterieladeempfehlung in Balkengrafik
################################################################
-sub __batRcmdOnBeam {
+sub __batteryOnBeam {
my $paref = shift;
my $name = $paref->{name};
my $maxhours = $paref->{maxhours};
@@ -15369,15 +15363,14 @@ sub __batRcmdOnBeam {
}
);
- $title .= defined $currsoc ? "\n".$htitles{socbacur}{$lang}.": ".$currsoc." %" : '';
-
- debugLog ($paref, 'graphic', "Battery $bn pos >$i< day: $day_str, time: $time_str, Power ('-' = out): ".(defined $bpower ? $bpower : 'undef').
- " W, Rcmd: ".(defined $hfcg->{$i}{'rcdchargebat'.$bn} ? $hfcg->{$i}{'rcdchargebat'.$bn} : 'undef').
- ", SoC: ".(defined $hfcg->{$i}{'soc'.$bn} ? $hfcg->{$i}{'soc'.$bn} : 'undef')." %");
-
+ $title .= defined $currsoc ? "\n".$htitles{socbacur}{$lang}.": ".$currsoc." %" : '';
my $image = defined $hfcg->{$i}{'rcdchargebat'.$bn} ? FW_makeImage ($bicon) : '';
$ret .= "
$image | ";
+
+ debugLog ($paref, 'graphic', "Battery $bn pos >$i< day: $day_str, time: $time_str, Power ('-' = out): ".(defined $bpower ? $bpower : 'undef').
+ " W, Rcmd: ".(defined $hfcg->{$i}{'rcdchargebat'.$bn} ? $hfcg->{$i}{'rcdchargebat'.$bn} : 'undef').
+ ", SoC: ".(defined $hfcg->{$i}{'soc'.$bn} ? $hfcg->{$i}{'soc'.$bn} : 'undef')." %");
}
$ret .= " | " if($ret); # freier Platz am Ende der Icon Zeile
@@ -17631,7 +17624,70 @@ sub listDataPool {
my $par = shift // q{};
my $name = $hash->{NAME};
- my $type = $hash->{TYPE};
+ my ($sq, $h);
+
+ if ($htol eq "pvhist") {
+ $sq = _listDataPoolPvHist ($hash, $par);
+ }
+
+ if ($htol =~ /consumers|inverters|producers|strings|batteries/xs) {
+ $sq = _listDataPoolVarious ($hash, $htol, $par);
+ }
+
+ if ($htol eq "circular") {
+ $sq = _listDataPoolCircular ($hash, $par);
+ }
+
+ if ($htol eq "nexthours") {
+ $sq = _listDataPoolNextHours ($name, $par);
+ }
+
+ if ($htol eq "qualities") {
+ $sq = _listDataPoolQualities ($name, $par);
+ }
+
+ if ($htol eq "current") {
+ $sq = _listDataPoolCurrent ($name, $par);
+ }
+
+ if ($htol =~ /radiationApiData|weatherApiData|statusApiData/xs) {
+ $sq = _listDataPoolApiData ($name, $htol, $par);
+ }
+
+ if ($htol eq "aiRawData") {
+ $h = $data{$name}{aidectree}{airaw};
+ my $maxcnt = keys %{$h};
+ if (!$maxcnt) {
+ return qq{aiRawData values cache is empty.};
+ }
+
+ $sq = "Number of datasets: ".$maxcnt."\n";
+
+ for my $idx (sort keys %{$h}) {
+ my $hod = AiRawdataVal ($name, $idx, 'hod', '-');
+ my $sunalt = AiRawdataVal ($name, $idx, 'sunalt', '-');
+ my $sunaz = AiRawdataVal ($name, $idx, 'sunaz', '-');
+ my $rad1h = AiRawdataVal ($name, $idx, 'rad1h', '-');
+ my $wcc = AiRawdataVal ($name, $idx, 'wcc', '-');
+ my $rr1c = AiRawdataVal ($name, $idx, 'rr1c', '-');
+ my $pvrl = AiRawdataVal ($name, $idx, 'pvrl', '-');
+ my $temp = AiRawdataVal ($name, $idx, 'temp', '-');
+ $sq .= "\n";
+ $sq .= "$idx => hod: $hod, sunaz: $sunaz, sunalt: $sunalt, rad1h: $rad1h, wcc: $wcc, rr1c: $rr1c, pvrl: $pvrl, temp: $temp";
+ }
+ }
+
+return $sq;
+}
+
+################################################################
+# Listing des pvHistory Speichers
+################################################################
+sub _listDataPoolPvHist {
+ my $hash = shift;
+ my $par = shift // q{};
+
+ my $name = $hash->{NAME};
my ($sq, $h, $hexp);
my $export = q{};
@@ -17875,288 +17931,89 @@ sub listDataPool {
return $ret;
};
- if ($htol eq "pvhist") {
- $h = $data{$name}{pvhist};
+ $h = $data{$name}{pvhist};
- if (!keys %{$h}) {
- return qq{PV cache is empty.};
- }
+ if (!keys %{$h}) {
+ return qq{PV cache is empty.};
+ }
- for my $i (keys %{$h}) {
- if (!isNumeric ($i)) {
- delete $data{$name}{pvhist}{$i};
- Log3 ($name, 2, qq{$name - INFO - invalid key "$i" was deleted from pvHistory storage});
- }
- }
-
- for my $idx (sort keys %{$h}) {
- next if($par && $idx ne $par);
- $sq .= $idx." => ".$sub->($idx)."\n";
- }
-
- if ($export eq 'csv') {
- return _writeAsCsv ($hash, $hexp, $pvhexprtcsv.$name.'.csv');
+ for my $i (keys %{$h}) {
+ if (!isNumeric ($i)) {
+ delete $data{$name}{pvhist}{$i};
+ Log3 ($name, 2, qq{$name - INFO - invalid key "$i" was deleted from pvHistory storage});
}
}
- if ($htol =~ /consumers|inverters|producers|strings|batteries/xs) {
- my $sub = $htol eq 'consumers' ? \&ConsumerVal :
- $htol eq 'inverters' ? \&InverterVal :
- $htol eq 'producers' ? \&ProducerVal :
- $htol eq 'strings' ? \&StringVal :
- $htol eq 'batteries' ? \&BatteryVal :
- '';
-
- $h = $data{$name}{$htol};
-
- if (!keys %{$h}) {
- return ucfirst($htol).qq{ cache is empty.};
- }
+ for my $idx (sort keys %{$h}) {
+ next if($par && $idx ne $par);
+ $sq .= $idx." => ".$sub->($idx)."\n";
+ }
- for my $i (keys %{$h}) {
- if ($i !~ /^[0-9]{2}$/ix && $htol ne 'strings') { # bereinigen ungültige Position, Forum: https://forum.fhem.de/index.php/topic,117864.msg1173219.html#msg1173219
- delete $data{$name}{$htol}{$i};
- Log3 ($name, 2, qq{$name - INFO - invalid key "$i" was deleted from }.ucfirst($htol).qq{ storage});
+ if ($export eq 'csv') {
+ return _writeAsCsv ($hash, $hexp, $pvhexprtcsv.$name.'.csv');
+ }
+
+return $sq;
+}
+
+################################################################
+# Listing des verschiedene Speicher
+################################################################
+sub _listDataPoolVarious {
+ my $hash = shift;
+ my $htol = shift;
+ my $par = shift // q{};
+
+ my $name = $hash->{NAME};
+
+ my $func = $htol eq 'consumers' ? \&ConsumerVal :
+ $htol eq 'inverters' ? \&InverterVal :
+ $htol eq 'producers' ? \&ProducerVal :
+ $htol eq 'strings' ? \&StringVal :
+ $htol eq 'batteries' ? \&BatteryVal :
+ '';
+
+ my $h = $data{$name}{$htol};
+
+ if (!keys %{$h}) {
+ return ucfirst($htol).qq{ cache is empty.};
+ }
+
+ for my $i (keys %{$h}) {
+ if ($i !~ /^[0-9]{2}$/ix && $htol ne 'strings') { # bereinigen ungültige Position, Forum: https://forum.fhem.de/index.php/topic,117864.msg1173219.html#msg1173219
+ delete $data{$name}{$htol}{$i};
+ Log3 ($name, 2, qq{$name - INFO - invalid key "$i" was deleted from }.ucfirst($htol).qq{ storage});
+ }
+ }
+
+ my $sq;
+
+ for my $idx (sort keys %{$h}) {
+ next if($par && $idx ne $par);
+ my ($cret, $s1);
+ my $sp1 = _ldpspaces ($idx, q{});
+
+ for my $ckey (sort keys %{$h->{$idx}}) {
+ if (ref $h->{$idx}{$ckey} eq 'ARRAY') {
+ my $aser = join " ", @{$h->{$idx}{$ckey}};
+ $cret .= ($s1 ? $sp1 : "").$ckey." => ".$aser."\n";
}
- }
-
- for my $idx (sort keys %{$h}) {
- next if($par && $idx ne $par);
- my ($cret, $s1);
- my $sp1 = _ldpspaces ($idx, q{});
- for my $ckey (sort keys %{$h->{$idx}}) {
- if (ref $h->{$idx}{$ckey} eq 'ARRAY') {
- my $aser = join " ", @{$h->{$idx}{$ckey}};
- $cret .= ($s1 ? $sp1 : "").$ckey." => ".$aser."\n";
- }
-
- if (ref $h->{$idx}{$ckey} eq 'HASH') {
- my $hk = qq{};
- for my $f (sort {$a<=>$b} keys %{$h->{$idx}{$ckey}}) {
- $hk .= " " if($hk);
- $hk .= "$f=".$h->{$idx}{$ckey}{$f};
- }
- $cret .= ($s1 ? $sp1 : "").$ckey." => ".$hk."\n";
- }
- else {
- $cret .= ($s1 ? $sp1 : "").$ckey." => ". &{$sub} ($hash, $idx, $ckey, "")."\n";
- }
-
- $s1 = 1;
- }
- $sq .= $idx." => ".$cret."\n";
- }
- }
-
- if ($htol eq "circular") {
- $sq = _listDataPoolCircular ($hash, $par);
- }
-
- if ($htol eq "nexthours") {
- $h = $data{$name}{nexthours};
-
- if (!keys %{$h}) {
- return qq{NextHours cache is empty.};
- }
-
- for my $idx (sort keys %{$h}) {
- my $nhts = NexthoursVal ($name, $idx, 'starttime', '-');
- my $hod = NexthoursVal ($name, $idx, 'hourofday', '-');
- my $today = NexthoursVal ($name, $idx, 'today', '-');
- my $pvfc = NexthoursVal ($name, $idx, 'pvfc', '-');
- my $pvapifc = NexthoursVal ($name, $idx, 'pvapifc', '-'); # PV Forecast der API
- my $pvaifc = NexthoursVal ($name, $idx, 'pvaifc', '-'); # PV Forecast der KI
- my $aihit = NexthoursVal ($name, $idx, 'aihit', '-'); # KI ForeCast Treffer Status
- my $wid = NexthoursVal ($name, $idx, 'weatherid', '-');
- my $wcc = NexthoursVal ($name, $idx, 'wcc', '-');
- my $crang = NexthoursVal ($name, $idx, 'cloudrange', '-');
- my $rr1c = NexthoursVal ($name, $idx, 'rr1c', '-');
- my $rrange = NexthoursVal ($name, $idx, 'rainrange', '-');
- my $rad1h = NexthoursVal ($name, $idx, 'rad1h', '-');
- my $pvcorrf = NexthoursVal ($name, $idx, 'pvcorrf', '-');
- my $temp = NexthoursVal ($name, $idx, 'temp', '-');
- my $confc = NexthoursVal ($name, $idx, 'confc', '-');
- my $confcex = NexthoursVal ($name, $idx, 'confcEx', '-');
- my $don = NexthoursVal ($name, $idx, 'DoN', '-');
- my $sunaz = NexthoursVal ($name, $idx, 'sunaz', '-');
- my $sunalt = NexthoursVal ($name, $idx, 'sunalt', '-');
-
- my ($rcdbat, $socs);
- for my $bn (1..$maxbatteries) { # alle Batterien
- $bn = sprintf "%02d", $bn;
- my $rcdcharge = NexthoursVal ($name, $idx, 'rcdchargebat'.$bn, '-');
- my $socxx = NexthoursVal ($name, $idx, 'soc'.$bn, '-');
- $rcdbat .= ', ' if($rcdbat);
- $rcdbat .= "rcdchargebat${bn}: $rcdcharge";
- $socs .= ', ' if($socs);
- $socs .= "soc${bn}: $socxx";
- }
-
- $sq .= "\n" if($sq);
- $sq .= $idx." => ";
- $sq .= "starttime: $nhts, hourofday: $hod, today: $today";
- $sq .= "\n ";
- $sq .= "pvapifc: $pvapifc, pvaifc: $pvaifc, pvfc: $pvfc, aihit: $aihit, confc: $confc";
- $sq .= "\n ";
- $sq .= "confcEx: $confcex, DoN: $don, weatherid: $wid, wcc: $wcc, rr1c: $rr1c, temp=$temp";
- $sq .= "\n ";
- $sq .= "rad1h: $rad1h, sunaz: $sunaz, sunalt: $sunalt";
- $sq .= "\n ";
- $sq .= "rrange: $rrange, crange: $crang, correff: $pvcorrf";
- $sq .= "\n ";
- $sq .= $socs;
- $sq .= "\n ";
- $sq .= $rcdbat;
- }
- }
-
- if ($htol eq "qualities") {
- $h = $data{$name}{nexthours};
- if (!keys %{$h}) {
- return qq{NextHours cache is empty.};
- }
- for my $idx (sort keys %{$h}) {
- my $nhfc = NexthoursVal ($hash, $idx, 'pvfc', undef);
- next if(!$nhfc);
-
- my $nhts = NexthoursVal ($hash, $idx, 'starttime', '-');
- my $pvcorrf = NexthoursVal ($hash, $idx, 'pvcorrf', '-/-');
- my $aihit = NexthoursVal ($hash, $idx, 'aihit', '-');
- my $pvfc = NexthoursVal ($hash, $idx, 'pvfc', '-');
- my $wcc = NexthoursVal ($hash, $idx, 'wcc', '-');
- my $sunalt = NexthoursVal ($hash, $idx, 'sunalt', '-');
-
- my ($f,$q) = split "/", $pvcorrf;
- $sq .= "\n" if($sq);
- $sq .= "Start: $nhts, Quality: $q, Factor: $f, AI usage: $aihit, PV expect: $pvfc Wh, Sun Alt: $sunalt, Cloud: $wcc";
- }
- }
-
- if ($htol eq "current") {
- $h = $data{$name}{current};
-
- if (!keys %{$h}) {
- return qq{Current values cache is empty.};
- }
-
- for my $idx (sort keys %{$h}) {
- if (ref $h->{$idx} eq 'ARRAY') {
- my $aser = join " ",@{$h->{$idx}};
- $sq .= $idx." => ".$aser."\n";
- }
- elsif (ref $h->{$idx} eq 'HASH') {
- my $s1;
- my $sp1 = _ldpspaces ($idx, q{});
- $sq .= $idx." => ";
-
- for my $idx1 (sort keys %{$h->{$idx}}) {
- if (ref $h->{$idx}{$idx1} eq 'HASH') {
- my $s2;
- my $sp2 = _ldpspaces ($idx1, $sp1);
- $sq .= ($s1 ? $sp1 : "").$idx1." => ";
-
- for my $idx2 (sort keys %{$h->{$idx}{$idx1}}) {
- my $s3;
- my $sp3 = _ldpspaces ($idx2, $sp2);
- $sq .= ($s2 ? $sp2 : "").$idx2." => ";
-
- if (ref $h->{$idx}{$idx1}{$idx2} eq 'HASH') {
- for my $idx3 (sort keys %{$h->{$idx}{$idx1}{$idx2}}) {
- $sq .= ($s3 ? $sp3 : "").$idx3." => ".(defined $h->{$idx}{$idx1}{$idx2}{$idx3} ? $h->{$idx}{$idx1}{$idx2}{$idx3} : '')."\n";
- $s3 = 1;
- }
- }
- else {
- $sq .= (defined $h->{$idx}{$idx1}{$idx2} ? $h->{$idx}{$idx1}{$idx2} : '')."\n";
- }
-
- $s1 = 1;
- $s2 = 1;
- }
- }
- else {
- $sq .= (defined $h->{$idx}{$idx1} ? $h->{$idx}{$idx1} : '')."\n";
- }
+ if (ref $h->{$idx}{$ckey} eq 'HASH') {
+ my $hk = qq{};
+ for my $f (sort {$a<=>$b} keys %{$h->{$idx}{$ckey}}) {
+ $hk .= " " if($hk);
+ $hk .= "$f=".$h->{$idx}{$ckey}{$f};
}
+ $cret .= ($s1 ? $sp1 : "").$ckey." => ".$hk."\n";
}
else {
- $sq .= $idx." => ".(defined $h->{$idx} ? $h->{$idx} : '')."\n";
+ $cret .= ($s1 ? $sp1 : "").$ckey." => ". &{$func} ($hash, $idx, $ckey, "")."\n";
}
+
+ $s1 = 1;
}
- }
-
- my $git = sub {
- my $it = shift;
- my @sorted = sort { $a cmp $b } keys %$it;
- my $key = shift @sorted;
-
- my $ret = {};
- $ret = { $key => $it->{$key} } if($key);
-
- return $ret;
- };
-
- if ($htol =~ /radiationApiData|weatherApiData|statusApiData/xs) {
- $h = $data{$name}{solcastapi};
- $h = $data{$name}{weatherapi} if($htol eq 'weatherApiData');
- $h = $data{$name}{statusapi} if($htol eq 'statusApiData');
-
- if (!keys %{$h}) {
- return qq{The API values cache is empty.};
- }
-
- my $pve = q{};
- my $itref = dclone $h; # Deep Copy von $h
-
- for my $idx (sort keys %{$itref}) {
- my $s1;
- my $sp1 = _ldpspaces ($idx, q{});
- $sq .= $idx." => ";
-
- while (my ($tag, $item) = each %{$git->($itref->{$idx})}) {
- $sq .= ($s1 ? $sp1 : "").$tag." => ";
-
- if (ref $item eq 'HASH') {
- my $s2;
- my $sp2 = _ldpspaces ($tag, $sp1);
-
- while (my ($tag1, $item1) = each %{$git->($itref->{$idx}{$tag})}) {
- $sq .= ($s2 ? $sp2 : "")."$tag1: ".$item1."\n";
- $s2 = 1;
- delete $itref->{$idx}{$tag}{$tag1};
- }
- }
-
- $s1 = 1;
- $sq .= "\n" if($sq !~ /\n$/xs);
-
- delete $itref->{$idx}{$tag};
- }
- }
- }
-
- if ($htol eq "aiRawData") {
- $h = $data{$name}{aidectree}{airaw};
- my $maxcnt = keys %{$h};
- if (!$maxcnt) {
- return qq{aiRawData values cache is empty.};
- }
-
- $sq = "Number of datasets: ".$maxcnt."\n";
-
- for my $idx (sort keys %{$h}) {
- my $hod = AiRawdataVal ($hash, $idx, 'hod', '-');
- my $sunalt = AiRawdataVal ($hash, $idx, 'sunalt', '-');
- my $sunaz = AiRawdataVal ($hash, $idx, 'sunaz', '-');
- my $rad1h = AiRawdataVal ($hash, $idx, 'rad1h', '-');
- my $wcc = AiRawdataVal ($hash, $idx, 'wcc', '-');
- my $rr1c = AiRawdataVal ($hash, $idx, 'rr1c', '-');
- my $pvrl = AiRawdataVal ($hash, $idx, 'pvrl', '-');
- my $temp = AiRawdataVal ($hash, $idx, 'temp', '-');
- $sq .= "\n";
- $sq .= "$idx => hod: $hod, sunaz: $sunaz, sunalt: $sunalt, rad1h: $rad1h, wcc: $wcc, rr1c: $rr1c, pvrl: $pvrl, temp: $temp";
- }
+ $sq .= $idx." => ".$cret."\n";
}
return $sq;
@@ -18178,35 +18035,6 @@ sub _listDataPoolCircular {
my $sq;
- ### 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}) {
next if($par && $idx ne $par);
@@ -18348,6 +18176,228 @@ sub _listDataPoolCircular {
return $sq;
}
+################################################################
+# Listing des NextHours Speicher
+################################################################
+sub _listDataPoolNextHours {
+ my $name = shift;
+ my $par = shift // q{};
+
+ my $h = $data{$name}{nexthours};
+ my $sq;
+
+ if (!keys %{$h}) {
+ return qq{NextHours cache is empty.};
+ }
+
+ for my $idx (sort keys %{$h}) {
+ my $nhts = NexthoursVal ($name, $idx, 'starttime', '-');
+ my $hod = NexthoursVal ($name, $idx, 'hourofday', '-');
+ my $today = NexthoursVal ($name, $idx, 'today', '-');
+ my $pvfc = NexthoursVal ($name, $idx, 'pvfc', '-');
+ my $pvapifc = NexthoursVal ($name, $idx, 'pvapifc', '-'); # PV Forecast der API
+ my $pvaifc = NexthoursVal ($name, $idx, 'pvaifc', '-'); # PV Forecast der KI
+ my $aihit = NexthoursVal ($name, $idx, 'aihit', '-'); # KI ForeCast Treffer Status
+ my $wid = NexthoursVal ($name, $idx, 'weatherid', '-');
+ my $wcc = NexthoursVal ($name, $idx, 'wcc', '-');
+ my $crang = NexthoursVal ($name, $idx, 'cloudrange', '-');
+ my $rr1c = NexthoursVal ($name, $idx, 'rr1c', '-');
+ my $rrange = NexthoursVal ($name, $idx, 'rainrange', '-');
+ my $rad1h = NexthoursVal ($name, $idx, 'rad1h', '-');
+ my $pvcorrf = NexthoursVal ($name, $idx, 'pvcorrf', '-');
+ my $temp = NexthoursVal ($name, $idx, 'temp', '-');
+ my $confc = NexthoursVal ($name, $idx, 'confc', '-');
+ my $confcex = NexthoursVal ($name, $idx, 'confcEx', '-');
+ my $don = NexthoursVal ($name, $idx, 'DoN', '-');
+ my $sunaz = NexthoursVal ($name, $idx, 'sunaz', '-');
+ my $sunalt = NexthoursVal ($name, $idx, 'sunalt', '-');
+
+ my ($rcdbat, $socs);
+ for my $bn (1..$maxbatteries) { # alle Batterien
+ $bn = sprintf "%02d", $bn;
+ my $rcdcharge = NexthoursVal ($name, $idx, 'rcdchargebat'.$bn, '-');
+ my $socxx = NexthoursVal ($name, $idx, 'soc'.$bn, '-');
+ $rcdbat .= ', ' if($rcdbat);
+ $rcdbat .= "rcdchargebat${bn}: $rcdcharge";
+ $socs .= ', ' if($socs);
+ $socs .= "soc${bn}: $socxx";
+ }
+
+ $sq .= "\n" if($sq);
+ $sq .= $idx." => ";
+ $sq .= "starttime: $nhts, hourofday: $hod, today: $today";
+ $sq .= "\n ";
+ $sq .= "pvapifc: $pvapifc, pvaifc: $pvaifc, pvfc: $pvfc, aihit: $aihit, confc: $confc";
+ $sq .= "\n ";
+ $sq .= "confcEx: $confcex, DoN: $don, weatherid: $wid, wcc: $wcc, rr1c: $rr1c, temp=$temp";
+ $sq .= "\n ";
+ $sq .= "rad1h: $rad1h, sunaz: $sunaz, sunalt: $sunalt";
+ $sq .= "\n ";
+ $sq .= "rrange: $rrange, crange: $crang, correff: $pvcorrf";
+ $sq .= "\n ";
+ $sq .= $socs;
+ $sq .= "\n ";
+ $sq .= $rcdbat;
+ }
+
+return $sq;
+}
+
+################################################################
+# Listing des Qualities Speicher
+################################################################
+sub _listDataPoolQualities {
+ my $name = shift;
+ my $par = shift // q{};
+
+ my $h = $data{$name}{nexthours};
+ my $sq;
+
+ if (!keys %{$h}) {
+ return qq{NextHours cache is empty.};
+ }
+
+ for my $idx (sort keys %{$h}) {
+ my $nhfc = NexthoursVal ($name, $idx, 'pvfc', undef);
+ next if(!$nhfc);
+
+ my $nhts = NexthoursVal ($name, $idx, 'starttime', '-');
+ my $pvcorrf = NexthoursVal ($name, $idx, 'pvcorrf', '-/-');
+ my $aihit = NexthoursVal ($name, $idx, 'aihit', '-');
+ my $pvfc = NexthoursVal ($name, $idx, 'pvfc', '-');
+ my $wcc = NexthoursVal ($name, $idx, 'wcc', '-');
+ my $sunalt = NexthoursVal ($name, $idx, 'sunalt', '-');
+
+ my ($f,$q) = split "/", $pvcorrf;
+ $sq .= "\n" if($sq);
+ $sq .= "Start: $nhts, Quality: $q, Factor: $f, AI usage: $aihit, PV expect: $pvfc Wh, Sun Alt: $sunalt, Cloud: $wcc";
+ }
+
+return $sq;
+}
+
+################################################################
+# Listing des Current Speicher
+################################################################
+sub _listDataPoolCurrent {
+ my $name = shift;
+ my $par = shift // q{};
+
+ my $h = $data{$name}{current};
+ my $sq;
+
+ if (!keys %{$h}) {
+ return qq{Current values cache is empty.};
+ }
+
+ for my $idx (sort keys %{$h}) {
+ if (ref $h->{$idx} eq 'ARRAY') {
+ my $aser = join " ",@{$h->{$idx}};
+ $sq .= $idx." => ".$aser."\n";
+ }
+ elsif (ref $h->{$idx} eq 'HASH') {
+ my $s1;
+ my $sp1 = _ldpspaces ($idx, q{});
+ $sq .= $idx." => ";
+
+ for my $idx1 (sort keys %{$h->{$idx}}) {
+ if (ref $h->{$idx}{$idx1} eq 'HASH') {
+ my $s2;
+ my $sp2 = _ldpspaces ($idx1, $sp1);
+ $sq .= ($s1 ? $sp1 : "").$idx1." => ";
+
+ for my $idx2 (sort keys %{$h->{$idx}{$idx1}}) {
+ my $s3;
+ my $sp3 = _ldpspaces ($idx2, $sp2);
+ $sq .= ($s2 ? $sp2 : "").$idx2." => ";
+
+ if (ref $h->{$idx}{$idx1}{$idx2} eq 'HASH') {
+ for my $idx3 (sort keys %{$h->{$idx}{$idx1}{$idx2}}) {
+ $sq .= ($s3 ? $sp3 : "").$idx3." => ".(defined $h->{$idx}{$idx1}{$idx2}{$idx3} ? $h->{$idx}{$idx1}{$idx2}{$idx3} : '')."\n";
+ $s3 = 1;
+ }
+ }
+ else {
+ $sq .= (defined $h->{$idx}{$idx1}{$idx2} ? $h->{$idx}{$idx1}{$idx2} : '')."\n";
+ }
+
+ $s1 = 1;
+ $s2 = 1;
+ }
+ }
+ else {
+ $sq .= (defined $h->{$idx}{$idx1} ? $h->{$idx}{$idx1} : '')."\n";
+ }
+ }
+ }
+ else {
+ $sq .= $idx." => ".(defined $h->{$idx} ? $h->{$idx} : '')."\n";
+ }
+ }
+
+return $sq;
+}
+
+################################################################
+# Listing der APiData Speicher
+################################################################
+sub _listDataPoolApiData {
+ my $name = shift;
+ my $htol = shift;
+ my $par = shift // q{};
+
+ my $h = $data{$name}{solcastapi};
+ $h = $data{$name}{weatherapi} if($htol eq 'weatherApiData');
+ $h = $data{$name}{statusapi} if($htol eq 'statusApiData');
+
+ if (!keys %{$h}) {
+ return qq{The API values cache is empty.};
+ }
+
+ my $git = sub {
+ my $it = shift;
+ my @sorted = sort { $a cmp $b } keys %$it;
+ my $key = shift @sorted;
+
+ my $ret = {};
+ $ret = { $key => $it->{$key} } if($key);
+
+ return $ret;
+ };
+
+ my $sq;
+ my $pve = q{};
+ my $itref = dclone $h; # Deep Copy von $h
+
+ for my $idx (sort keys %{$itref}) {
+ my $s1;
+ my $sp1 = _ldpspaces ($idx, q{});
+ $sq .= $idx." => ";
+
+ while (my ($tag, $item) = each %{$git->($itref->{$idx})}) {
+ $sq .= ($s1 ? $sp1 : "").$tag." => ";
+
+ if (ref $item eq 'HASH') {
+ my $s2;
+ my $sp2 = _ldpspaces ($tag, $sp1);
+
+ while (my ($tag1, $item1) = each %{$git->($itref->{$idx}{$tag})}) {
+ $sq .= ($s2 ? $sp2 : "")."$tag1: ".$item1."\n";
+ $s2 = 1;
+ delete $itref->{$idx}{$tag}{$tag1};
+ }
+ }
+
+ $s1 = 1;
+ $sq .= "\n" if($sq !~ /\n$/xs);
+
+ delete $itref->{$idx}{$tag};
+ }
+ }
+
+return $sq;
+}
+
################################################################
# Hashwert aus CircularVal in formatierten String umwandeln
################################################################