2
0
mirror of https://github.com/fhem/fhem-mirror.git synced 2025-02-25 03:44:52 +00:00

76_SolarForecast: remove graphicBeam1MaxVal,ctrlAreaFactorUsage

git-svn-id: https://svn.fhem.de/fhem/trunk@29581 2b470e98-0d58-463d-a4d8-8e2adae1ed80
This commit is contained in:
nasseeder1 2025-01-27 19:33:15 +00:00
parent a221850f21
commit f93176ff22
2 changed files with 393 additions and 342 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: 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

View File

@ -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 ",
@ -717,7 +720,7 @@ my %hrepl = ( # Z
'7' => 'h',
'8' => 'i',
'9' => 'j',
'.' => 'k',
'\.' => 'k',
);
my %hqtxt = ( # Hash (Setup) Texte
@ -1449,11 +1452,10 @@ sub Initialize {
## 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};
@ -15370,14 +15364,13 @@ sub __batRcmdOnBeam {
);
$title .= defined $currsoc ? "\n".$htitles{socbacur}{$lang}.": ".$currsoc." %" : '';
my $image = defined $hfcg->{$i}{'rcdchargebat'.$bn} ? FW_makeImage ($bicon) : '';
$ret .= "<td title='$title' class='solarfc' width='$width' style='margin:1px; vertical-align:middle align:center; padding-bottom:1px;'>$image</td>";
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')." %");
my $image = defined $hfcg->{$i}{'rcdchargebat'.$bn} ? FW_makeImage ($bicon) : '';
$ret .= "<td title='$title' class='solarfc' width='$width' style='margin:1px; vertical-align:middle align:center; padding-bottom:1px;'>$image</td>";
}
$ret .= "<td class='solarfc'></td></tr>" 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 = "<b>Number of datasets:</b> ".$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,7 +17931,6 @@ sub listDataPool {
return $ret;
};
if ($htol eq "pvhist") {
$h = $data{$name}{pvhist};
if (!keys %{$h}) {
@ -17897,17 +17952,28 @@ sub listDataPool {
if ($export eq 'csv') {
return _writeAsCsv ($hash, $hexp, $pvhexprtcsv.$name.'.csv');
}
}
if ($htol =~ /consumers|inverters|producers|strings|batteries/xs) {
my $sub = $htol eq 'consumers' ? \&ConsumerVal :
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 :
'';
$h = $data{$name}{$htol};
my $h = $data{$name}{$htol};
if (!keys %{$h}) {
return ucfirst($htol).qq{ cache is empty.};
@ -17920,6 +17986,8 @@ sub listDataPool {
}
}
my $sq;
for my $idx (sort keys %{$h}) {
next if($par && $idx ne $par);
my ($cret, $s1);
@ -17940,224 +18008,13 @@ sub listDataPool {
$cret .= ($s1 ? $sp1 : "").$ckey." => ".$hk."\n";
}
else {
$cret .= ($s1 ? $sp1 : "").$ckey." => ". &{$sub} ($hash, $idx, $ckey, "")."\n";
$cret .= ($s1 ? $sp1 : "").$ckey." => ". &{$func} ($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";
}
}
}
else {
$sq .= $idx." => ".(defined $h->{$idx} ? $h->{$idx} : '')."\n";
}
}
}
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 = "<b>Number of datasets:</b> ".$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";
}
}
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
################################################################