diff --git a/fhem/CHANGED b/fhem/CHANGED index 6a890e976..aec60aceb 100644 --- a/fhem/CHANGED +++ b/fhem/CHANGED @@ -1,5 +1,6 @@ # Add changes at the top of the list. Keep it in ASCII, and 80-char wide. # Do not insert empty lines here, update check depends on it + - feature: 76_SolarForecast: showDiff can be set separately for each level - bugfix: 76_SolarForecast: fix Perl warning in release 1.53.0 - feature: 76_SolarForecast: minor release 1.53.0 - feature: 76_SolarForecast: new special Reading conForecastComingNight diff --git a/fhem/FHEM/76_SolarForecast.pm b/fhem/FHEM/76_SolarForecast.pm index ae8756147..55445d77e 100644 --- a/fhem/FHEM/76_SolarForecast.pm +++ b/fhem/FHEM/76_SolarForecast.pm @@ -160,6 +160,9 @@ BEGIN { # Versions History intern my %vNotesIntern = ( + "1.53.3" => "04.07.2025 Change of the correction factor calculation to the ratio of real production and the API raw forecast ", + "1.53.2" => "03.07.2025 graphicControl->showDiff can be set separately for each level ". + "setupInverterDevXX: Check that there are no commas with spaces before and after (strings) ", "1.53.1" => "30.06.2025 add utf8 smileys, fix Perl warning uninitialized value \$color ", "1.53.0" => "28.06.2025 new battery style (batcontainer), new key setupBatteryDevXX->label, new reading Battery_ChargeUnrestricted_XX ". "attribute graphicShowDiff replaced by graphicControl->showDiff ". @@ -6487,17 +6490,18 @@ sub _attrgraphicControl { ## no critic "not used" my $cmd = $paref->{cmd}; my $valid = { - beamPaddingBottom => { comp => '\d+', act => 0 }, - beamPaddingTop => { comp => '\d+', act => 0 }, - beamWidth => { comp => '([2-9][0-9]|100)', act => 0 }, - energyUnit => { comp => '(Wh|kWh)', act => 0 }, - headerDetail => { comp => '.*', act => 1 }, - hourCount => { comp => '([4-9]|1[0-9]|2[0-4])', act => 0 }, - hourStyle => { comp => ':(0{1,2})', act => 0 }, - layoutType => { comp => '(single|double|diff)', act => 0 }, - scaleMode => { comp => '(?:[1-3]:(?:log|lin))(?:,(?:[1-3]:(?:log|lin)))*', act => 0 }, - showDiff => { comp => '(no|top|bottom)', act => 0 }, - spaceSize => { comp => '\d+', act => 0 }, + beamPaddingBottom => { comp => '\d+', act => 0 }, + beamPaddingTop => { comp => '\d+', act => 0 }, + beamWidth => { comp => '([2-9][0-9]|100)', act => 0 }, + energyUnit => { comp => '(Wh|kWh)', act => 0 }, + headerDetail => { comp => '.*', act => 1 }, + hourCount => { comp => '([4-9]|1[0-9]|2[0-4])', act => 0 }, + hourStyle => { comp => ':(0{1,2})', act => 0 }, + layoutType => { comp => '(single|double|diff)', act => 0 }, + scaleMode => { comp => '(?:[1-3]:(?:log|lin))(?:,(?:[1-3]:(?:log|lin)))*', act => 0 }, + #showDiff => { comp => '(no|top|bottom)', act => 0 }, + showDiff => { comp => '(?:[1-3]:(?:top|bottom))(?:,(?:[1-3]:(?:top|bottom)))*', act => 0 }, + spaceSize => { comp => '\d+', act => 0 }, }; my ($a, $h) = parseParams ($aVal); @@ -6952,7 +6956,11 @@ sub _attrInverterDev { ## no critic "not used" asynchron => { comp => '(0|1)', act => 0 }, }; - if ($paref->{cmd} eq 'set') { + if ($paref->{cmd} eq 'set') { + if ($aVal =~ /strings=/xs && $aVal !~ /strings=(?!.*(\s,|,\s)).*$/xs) { + return "The key 'string' is not specified correctly. Please refer to the command reference."; + } + my ($err, $indev, $h) = isDeviceValid ( { name => $name, obj => $aVal, method => 'string' } ); return $err if($err); @@ -10305,9 +10313,9 @@ sub _transferAPIRadiationValues { $sunaz = NexthoursVal ($name, $nhtstr, 'sunaz', 0); } - $paref->{sabin} = sunalt2bin ($sunalt); - my $pvapifc = __calcPVestimates ($paref); # API Wert ermitteln - my ($msg, $pvaifc) = aiGetResult ($paref); # KI Entscheidungen abfragen + $paref->{sabin} = sunalt2bin ($sunalt); + my ($pvapifc, $pvapifcraw) = __calcPVestimates ($paref); # API Wert mit Korrekturfaktor und ohne KF ermitteln + my ($msg, $pvaifc) = aiGetResult ($paref); # KI Entscheidungen abfragen delete $paref->{fd}; delete $paref->{fh1}; @@ -10365,18 +10373,22 @@ sub _transferAPIRadiationValues { debugLog ($paref, 'aiData', "use PV from API (no AI or AI result tolerance overflow) -> hod: $hod, Rad1h: ".(defined $rad1h ? $rad1h : '-').", pvfc: $pvfc Wh"); } - $data{$name}{nexthours}{$nhtstr}{pvapifc} = $pvapifc; # durch API gelieferte PV Forecast - $data{$name}{nexthours}{$nhtstr}{pvfc} = $pvfc; # resultierende PV Forecast zuweisen + $data{$name}{nexthours}{$nhtstr}{pvapifc} = $pvapifc; # durch API gelieferte PV Forecast mit Korrekturfaktor + $data{$name}{nexthours}{$nhtstr}{pvapifcraw} = $pvapifcraw; # durch API gelieferte PV Forecast Raw + $data{$name}{nexthours}{$nhtstr}{pvfc} = $pvfc; # resultierende PV Forecast zuweisen + my $hh1 = sprintf "%02d", $fh1; + if ($num < 23 && $fh < 24) { # Ringspeicher PV forecast Forum: https://forum.fhem.de/index.php/topic,117864.msg1133350.html#msg1133350 - $data{$name}{circular}{sprintf "%02d",$fh1}{pvapifc} = NexthoursVal ($name, $nhtstr, 'pvapifc', undef); - $data{$name}{circular}{sprintf "%02d",$fh1}{pvfc} = $pvfc; - $data{$name}{circular}{sprintf "%02d",$fh1}{pvaifc} = NexthoursVal ($name, $nhtstr, 'pvaifc', undef); - $data{$name}{circular}{sprintf "%02d",$fh1}{aihit} = NexthoursVal ($name, $nhtstr, 'aihit', 0); + $data{$name}{circular}{$hh1}{pvapifc} = NexthoursVal ($name, $nhtstr, 'pvapifc', undef); + $data{$name}{circular}{$hh1}{pvapifcraw} = NexthoursVal ($name, $nhtstr, 'pvapifcraw', undef); + $data{$name}{circular}{$hh1}{pvaifc} = NexthoursVal ($name, $nhtstr, 'pvaifc', undef); + $data{$name}{circular}{$hh1}{aihit} = NexthoursVal ($name, $nhtstr, 'aihit', 0); + $data{$name}{circular}{$hh1}{pvfc} = $pvfc; } if ($fd == 0 && int $pvfc > 0) { # Vorhersagedaten des aktuellen Tages zum manuellen Vergleich in Reading speichern - storeReading ('Today_Hour'.sprintf ("%02d",$fh1).'_PVforecast', "$pvfc Wh"); + storeReading ('Today_Hour'.$hh1.'_PVforecast', "$pvfc Wh"); } if ($fd == 0 && $fh1) { @@ -10385,7 +10397,7 @@ sub _transferAPIRadiationValues { } } - storeReading ('.lastupdateForecastValues', $t); # Statusreading letzter update + storeReading ('.lastupdateForecastValues', $t); # Statusreading letzter update return; } @@ -10463,8 +10475,9 @@ sub __calcPVestimates { delete $paref->{wcc}; my ($lh,$sq,$peakloss, $modtemp); - my $pvsum = 0; - my $peaksum = 0; + my $pvsum = 0; + my $peaksum = 0; + my $pvsumraw = 0; my %sum; for my $string (sort keys %{$data{$name}{strings}}) { @@ -10492,6 +10505,7 @@ sub __calcPVestimates { if ($istrings eq 'all' || grep /^$string$/, (split ',', $istrings)) { $sum{$in}{pvinvsum} += $pv; + $sum{$in}{pvrawsum} += $pvest; # PV Prognose ohne Faktorenanwendung $sum{$in}{string} = defined $sum{$in}{string} ? $sum{$in}{string}.','.$string : $string; } } @@ -10524,6 +10538,11 @@ sub __calcPVestimates { for my $ins (keys %sum) { my $cap = InverterVal ($name, $ins, 'invertercap', 0); # Max. Leistung des Inverters my $pvinvsum = $sum{$ins}{pvinvsum}; + my $pvrawsum = $sum{$ins}{pvrawsum}; + + if ($pvrawsum > $cap) { + $pvrawsum = $cap; + } if ($pvinvsum > $cap) { $pvinvsum = $cap; # betreffende Strings auf WR Kapazität begrenzen @@ -10531,12 +10550,15 @@ sub __calcPVestimates { debugLog ($paref, "radiationProcess", "String(s) ".$sum{$ins}{string}." in total limited to $cap Wh due to inverter $ins capacity"); } - $pvsum += $pvinvsum; + $pvsum += $pvinvsum; + $pvsumraw += $pvrawsum; # PV Prognose ohne Faktorenanwendung } $data{$name}{current}{allstringspeak} = $peaksum; # temperaturbedingte Korrektur der installierten Peakleistung in W $pvsum = $peaksum if($peaksum && $pvsum > $peaksum); # Vorhersage nicht größer als die Summe aller PV-Strings Peak $pvsum = sprintf "%.0f", $pvsum; + $pvsumraw = $peaksum if($peaksum && $pvsumraw > $peaksum); + $pvsumraw = sprintf "%.0f", $pvsumraw; if ($debug =~ /radiationProcess/xs) { $lh = { # Log-Hash zur Ausgabe @@ -10557,7 +10579,7 @@ sub __calcPVestimates { Log3 ($name, 1, "$name DEBUG> PV API estimate for $reld Hour ".sprintf ("%02d", $hod)." summary: \n$sq"); } -return $pvsum; +return ($pvsum, $pvsumraw); } ###################################################################### @@ -14092,77 +14114,6 @@ sub _calcDataEveryFullHour { return; } -################################################################ -# PV Ist/Forecast ermitteln und Korrekturfaktoren, Qualität -# in Abhängigkeit Bewölkung errechnen und speichern (komplex) -################################################################ -sub _calcCaQcomplex { - my $paref = shift; - my $name = $paref->{name}; - my $debug = $paref->{debug}; - my $acu = $paref->{acu}; - my $pvrlvd = $paref->{pvrlvd}; # PV-Wert valide 1/0 - my $h = $paref->{h}; - my $day = $paref->{day}; # aktueller Tag - my $yday = $paref->{yday}; # vorheriger Tag (falls gesetzt) - my $aihit = $paref->{aihit}; - - if (!$pvrlvd) { - debugLog ($paref, 'pvCorrectionWrite', "real PV generation is marked as invalid for hour: $h -> skip the recalculation of the complex correction factor"); - return; - } - - my $hh = sprintf "%02d", $h; - my $pvrl = CircularVal ($name, $hh, 'pvrl', 0); # real erzeugte PV Energie am Ende der vorherigen Stunde - my $pvfc = CircularVal ($name, $hh, 'pvapifc', 0); # vorhergesagte PV Energie am Ende der vorherigen Stunde - - if (!$pvrl || !$pvfc) { - return; - } - - my $chwcc = HistoryVal ($name, $day, $hh, 'wcc', 0); # Wolkenbedeckung heute & abgefragte Stunde - my $sunalt = HistoryVal ($name, $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; - - removeMinMaxArray ($data{$name}{circular}{$hh}{'pvrl_'.$sabin}{"$crang"}, SPLSLIDEMAX); - removeMinMaxArray ($data{$name}{circular}{$hh}{'pvfc_'.$sabin}{"$crang"}, SPLSLIDEMAX); - - ## neuen Korrekturfaktor berechnen - #################################### - $paref->{pvrl} = $pvrl; - $paref->{pvfc} = $pvfc; - $paref->{crang} = $crang; - $paref->{sabin} = $sabin; - $paref->{calc} = 'Complex'; - - my ($oldfac, $factor, $dnum) = __calcNewFactor_migrated ($paref); # migrierte Daten verwenden - - delete $paref->{pvrl}; - delete $paref->{pvfc}; - delete $paref->{crang}; - delete $paref->{sabin}; - delete $paref->{calc}; - - $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_'.$hh, $factor." (automatic - old factor: $oldfac,$aihit Sun Alt range: $sabin, Cloud range: $crang, Days in range: $dnum)"); - } - else { - storeReading ('pvCorrectionFactor_'.$hh, $paref->{cpcf}." / flexmatic result $factor for Sun Alt range: $sabin,$aihit Cloud range: $crang, Days in range: $dnum"); - } - } - -return; -} - ################################################################ # PV Ist/Forecast ermitteln und Korrekturfaktoren, Qualität # ohne Nebenfaktoren errechnen und speichern (simple) @@ -14183,27 +14134,30 @@ sub _calcCaQsimple { return; } - my $hh = sprintf "%02d", $h; - my $pvrl = CircularVal ($name, $hh, 'pvrl', 0); - my $pvfc = CircularVal ($name, $hh, 'pvapifc', 0); + my $hh = sprintf "%02d", $h; + my $pvrl = CircularVal ($name, $hh, 'pvrl', 0); + my $pvapifc = CircularVal ($name, $hh, 'pvapifc', 0); # vorhergesagte PV Energie incl. Korrekturfaktoren am Ende der vorherigen Stunde + my $pvapifcraw = CircularVal ($name, $hh, 'pvapifcraw', 0); # vorhergesagte PV Energie (raw) am Ende der vorherigen Stunde - if (!$pvrl || !$pvfc) { + if (!$pvrl || !$pvapifcraw) { return; } my $sunalt = HistoryVal ($name, $day, $hh, 'sunalt', 0); # Sonne Altitude my $sabin = sunalt2bin ($sunalt); - $paref->{pvrl} = $pvrl; - $paref->{pvfc} = $pvfc; - $paref->{sabin} = $sabin; - $paref->{crang} = 'simple'; - $paref->{calc} = 'Simple'; + $paref->{pvrl} = $pvrl; + $paref->{pvapifc} = $pvapifc; + $paref->{pvapifcraw} = $pvapifcraw; + $paref->{sabin} = $sabin; + $paref->{crang} = 'simple'; + $paref->{calc} = 'Simple'; my ($oldfac, $factor, $dnum) = __calcNewFactor_migrated ($paref); # migrierte Daten verwenden delete $paref->{pvrl}; - delete $paref->{pvfc}; + delete $paref->{pvapifc}; + delete $paref->{pvapifcraw}; delete $paref->{sabin}; delete $paref->{crang}; delete $paref->{calc}; @@ -14222,6 +14176,80 @@ sub _calcCaQsimple { return; } +################################################################ +# PV Ist/Forecast ermitteln und Korrekturfaktoren, Qualität +# in Abhängigkeit Bewölkung errechnen und speichern (komplex) +################################################################ +sub _calcCaQcomplex { + my $paref = shift; + my $name = $paref->{name}; + my $debug = $paref->{debug}; + my $acu = $paref->{acu}; + my $pvrlvd = $paref->{pvrlvd}; # PV-Wert valide 1/0 + my $h = $paref->{h}; + my $day = $paref->{day}; # aktueller Tag + my $yday = $paref->{yday}; # vorheriger Tag (falls gesetzt) + my $aihit = $paref->{aihit}; + + if (!$pvrlvd) { + debugLog ($paref, 'pvCorrectionWrite', "real PV generation is marked as invalid for hour: $h -> skip the recalculation of the complex correction factor"); + return; + } + + my $hh = sprintf "%02d", $h; + my $pvrl = CircularVal ($name, $hh, 'pvrl', 0); # real erzeugte PV Energie am Ende der vorherigen Stunde + my $pvapifc = CircularVal ($name, $hh, 'pvapifc', 0); # vorhergesagte PV Energie incl. Korrekturfaktoren am Ende der vorherigen Stunde + my $pvapifcraw = CircularVal ($name, $hh, 'pvapifcraw', 0); # vorhergesagte PV Energie (raw) am Ende der vorherigen Stunde + + if (!$pvrl || !$pvapifcraw) { + return; + } + + my $chwcc = HistoryVal ($name, $day, $hh, 'wcc', 0); # Wolkenbedeckung heute & abgefragte Stunde + my $sunalt = HistoryVal ($name, $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"}}, $pvapifcraw; + + removeMinMaxArray ($data{$name}{circular}{$hh}{'pvrl_'.$sabin}{"$crang"}, SPLSLIDEMAX); + removeMinMaxArray ($data{$name}{circular}{$hh}{'pvfc_'.$sabin}{"$crang"}, SPLSLIDEMAX); + + ## neuen Korrekturfaktor berechnen + #################################### + $paref->{pvrl} = $pvrl; + $paref->{pvapifc} = $pvapifc; + $paref->{pvapifcraw} = $pvapifcraw; + $paref->{crang} = $crang; + $paref->{sabin} = $sabin; + $paref->{calc} = 'Complex'; + + my ($oldfac, $factor, $dnum) = __calcNewFactor_migrated ($paref); # migrierte Daten verwenden + + delete $paref->{pvrl}; + delete $paref->{pvapifc}; + delete $paref->{pvapifcraw}; + delete $paref->{crang}; + delete $paref->{sabin}; + delete $paref->{calc}; + + $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_'.$hh, $factor." (automatic - old factor: $oldfac,$aihit Sun Alt range: $sabin, Cloud range: $crang, Days in range: $dnum)"); + } + else { + storeReading ('pvCorrectionFactor_'.$hh, $paref->{cpcf}." / flexmatic result $factor for Sun Alt range: $sabin,$aihit Cloud range: $crang, Days in range: $dnum"); + } + } + +return; +} + ################################################################ # den Hausverbrauch der vergangenen Stunde zum con-Array # im Circular Speicher hinzufügen @@ -14257,14 +14285,15 @@ return; # den neuen Korrekturfaktur berechnen (neue Median Funktion) ################################################################ sub __calcNewFactor_migrated { - my $paref = shift; - my $name = $paref->{name}; - my $pvrl = $paref->{pvrl}; - my $pvfc = $paref->{pvfc}; - my $crang = $paref->{crang}; - my $sabin = $paref->{sabin}; - my $h = $paref->{h}; - my $calc = $paref->{calc}; + my $paref = shift; + my $name = $paref->{name}; + my $pvrl = $paref->{pvrl}; + my $pvapifc = $paref->{pvapifc}; + my $pvfcraw = $paref->{pvapifcraw}; + my $crang = $paref->{crang}; + my $sabin = $paref->{sabin}; + my $h = $paref->{h}; + my $calc = $paref->{calc}; my $hash = $defs{$name}; my ($factor, $pvcirc, $fccirc, $pvrlsum, $pvfcsum, $dnum); @@ -14283,30 +14312,30 @@ sub __calcNewFactor_migrated { if ($dnum) { # Werte in History vorhanden -> haben Prio ! $dnum++; $pvrlsum = $pvrl + $pvcirc; - $pvfcsum = $pvfc + $fccirc; + $pvfcsum = $pvfcraw + $fccirc; $pvrl = $pvrlsum / $dnum; - $pvfc = $pvfcsum / $dnum; - $factor = sprintf "%.2f", ($pvrl / $pvfc); # Faktorberechnung: reale PV / Prognose + $pvfcraw = $pvfcsum / $dnum; + $factor = sprintf "%.2f", ($pvrl / $pvfcraw); # Faktorberechnung: reale PV / Prognose } elsif ($oldfac && (!$pvcirc || !$fccirc)) { # Circular Hash liefert einen vorhandenen Korrekturfaktor aber keine gespeicherten PV-Werte $dnum = 1; - $factor = sprintf "%.2f", ($pvrl / $pvfc); + $factor = sprintf "%.2f", ($pvrl / $pvfcraw); $factor = sprintf "%.2f", ($factor + $oldfac) / 2; } else { # ganz neuer Wert $dnum = 1; - $factor = sprintf "%.2f", ($pvrl / $pvfc); + $factor = sprintf "%.2f", ($pvrl / $pvfcraw); } } else { - $pvrl = medianArray (\@{$data{$name}{circular}{$hh}{'pvrl_'.$sabin}{"$crang"}}); # neuen Median berechnen - $pvfc = medianArray (\@{$data{$name}{circular}{$hh}{'pvfc_'.$sabin}{"$crang"}}); # neuen Median berechnen + $pvrl = medianArray (\@{$data{$name}{circular}{$hh}{'pvrl_'.$sabin}{"$crang"}}); # neuen Median berechnen + $pvfcraw = medianArray (\@{$data{$name}{circular}{$hh}{'pvfc_'.$sabin}{"$crang"}}); # neuen Median berechnen $factor = 0; $dnum = scalar (@{$data{$name}{circular}{$hh}{'pvrl_'.$sabin}{"$crang"}}); - $factor = sprintf "%.2f", ($pvrl / $pvfc) if($pvrl && $pvfc); # devision by zero Forum: https://forum.fhem.de/index.php?msg=1341884 + $factor = sprintf "%.2f", ($pvrl / $pvfcraw) if($pvrl && $pvfcraw); # devision by zero Forum: https://forum.fhem.de/index.php?msg=1341884 - debugLog ($paref, 'pvCorrectionWrite', "$calc Corrf -> read stored values: PVreal median: $pvrl, PVforecast median: $pvfc, days: $dnum"); + debugLog ($paref, 'pvCorrectionWrite', "$calc Corrf -> read stored values: PVreal median: $pvrl, PVforecast median: $pvfcraw, days: $dnum"); } $factor = 1.00 if(1 * $factor == 0); # 0.00-Werte ignorieren (Schleifengefahr) @@ -14324,7 +14353,7 @@ sub __calcNewFactor_migrated { ## Qualität berechnen ####################### $oldfac = sprintf "%.2f", $oldfac; - my $qual = __calcFcQuality ($pvfc, $pvrl); # Qualität der Vorhersage für die vergangene Stunde + my $qual = __calcFcQuality ($pvapifc, $pvrl); # Qualität der Vorhersage für die vergangene Stunde debugLog ($paref, 'pvCorrectionWrite', "$calc Corrf -> determined values - hour: $hh, Sun Altitude range: $sabin, Cloud range: $crang, old factor: $oldfac, new factor: $factor, days: $dnum"); debugLog ($paref, 'pvCorrectionWrite|saveData2Cache', "$calc Corrf -> write correction values into Circular - hour: $hh, Sun Altitude range: $sabin, Cloud range: $crang, factor: $factor, quality: $qual, days: $dnum"); @@ -14351,12 +14380,12 @@ return ($oldfac, $factor, $dnum); # Qualität der Vorhersage berechnen ################################################################ sub __calcFcQuality { - my $pvfc = shift; # PV Vorhersagewert - my $pvrl = shift; # PV reale Erzeugung + my $pvapifc = shift; # PV Vorhersagewert + my $pvrl = shift; # PV reale Erzeugung - return if(!$pvfc || !$pvrl); + return if(!$pvapifc || !$pvrl); - my $diff = $pvfc - $pvrl; + my $diff = $pvapifc - $pvrl; my $hdv = 1 - abs ($diff / $pvrl); # Abweichung der Stunde, 1 = bestmöglicher Wert $hdv = $hdv < 0 ? 0 : $hdv; @@ -14985,7 +15014,6 @@ sub entryGraphic { colorw => AttrVal ($name, 'graphicWeatherColor', WTHCOLDDEF), # Wetter Icon Farbe Tag colorwn => AttrVal ($name, 'graphicWeatherColorNight', WTHCOLNDEF), # Wetter Icon Farbe Nacht wlalias => AttrVal ($name, 'alias', $name), - show_diff => CurrentVal ($name, 'showDiff', 'no'), # zusätzliche Anzeige $di{} in allen Typen lotype => CurrentVal ($name, 'layoutType', 'double'), hourstyle => CurrentVal ($name, 'hourStyle', ''), hdrDetail => CurrentVal ($name, 'headerDetail', 'all'), # ermöglicht den Inhalt zu begrenzen, um bspw. passgenau in ftui einzubetten @@ -15079,7 +15107,8 @@ sub entryGraphic { ## Balkengrafiken ################################################################################### - my $scm = _parseScaleModes ($name); # Scale Modes auflösen + my $scm = _parseScaleModes ($name); # Scale Modes auflösen + my $sdf = _parseShowdiffModes ($name); ## Balkengrafik Ebene 1 ######################### @@ -15087,6 +15116,7 @@ sub entryGraphic { my %hfcg1; $paref->{chartlvl} = 1; # Balkengrafik Ebene 1 $paref->{scm} = $scm->{1}; # Scale Mode Level 1 + $paref->{showdiff} = $sdf->{1}; # show Diff Mode Level 1 $paref->{hfcg} = \%hfcg1; # hfcg = hash forecast graphic ## Werte aktuelle Stunde @@ -15125,6 +15155,7 @@ sub entryGraphic { $paref->{chartlvl} = 2; $paref->{scm} = $scm->{2}; # Scale Mode Level 2 + $paref->{showdiff} = $sdf->{2}; # show Diff Mode Level 2 $paref->{beam1cont} = $paref->{beam3cont}; $paref->{beam2cont} = $paref->{beam4cont}; $paref->{colorb1} = AttrVal ($name, 'graphicBeam3Color', B3COLDEF); @@ -15168,6 +15199,7 @@ sub entryGraphic { $paref->{chartlvl} = 3; $paref->{scm} = $scm->{3}; # Scale Mode Level 3 + $paref->{showdiff} = $sdf->{3}; # show Diff Mode Level 3 $paref->{beam1cont} = $paref->{beam5cont}; $paref->{beam2cont} = $paref->{beam6cont}; $paref->{colorb1} = AttrVal ($name, 'graphicBeam5Color', B5COLDEF); @@ -15366,7 +15398,7 @@ return; } ################################################################ -# Parsed den Scale Mode für jede Balkengrafik Ebene +# Parse den Scale Mode für jede Balkengrafik Ebene # z.B. scaleMode=1:log,2:lin,3:lin ################################################################ sub _parseScaleModes { @@ -15392,6 +15424,43 @@ sub _parseScaleModes { return $scm; } +################################################################ +# Parse den Diff Mode für jede Balkengrafik Ebene +# z.B. showDiff=1:top,2:bottom,3:bottom +################################################################ +sub _parseShowdiffModes { + my $name = shift; + my $sdf; + + for my $bl (1..MAXBEAMLEVEL) { # Hashref Diff Modes initial mit Standard füllen + $sdf->{"$bl"} = ''; + } + + my $mo = CurrentVal ($name, 'showDiff', ''); + + ### nicht mehr benötigte Daten verarbeiten - Bereich kann später wieder raus !! 03.07. + ########################################################################################### + if ($mo) { + $mo = $mo eq 'no' ? qq{1:'',2:'',3:''} : + $mo eq 'top' ? qq{1:top,2:top,3:top} : + $mo eq 'bottom' ? qq{1:bottom,2:bottom,3:bottom} : + $mo; + } + ########################################################################################## + + if ($mo) { + my @moa = split ',', $mo; + + for my $elem (@moa) { + my ($lvl, $mode) = split ':', $elem; + $sdf->{"$lvl"} = $mode; + } + + } + +return $sdf; +} + ################################################################ # forecastGraphic Headerzeile generieren ################################################################ @@ -16747,7 +16816,7 @@ sub _beamGraphicFirstHour { $hfcg->{0}{beam1} //= 0; $hfcg->{0}{beam2} //= 0; $hfcg->{0}{diff} = sprintf "%.1f", ($hfcg->{0}{beam1} - $hfcg->{0}{beam2}); - $hfcg->{0}{diff} = sprintf "%.0f", $hfcg->{0}{diff} if(int ($hfcg->{0}{diff}) - $hfcg->{0}{diff} == 0); + $hfcg->{0}{diff} = sprintf "%.0f", $hfcg->{0}{diff} if(($hfcg->{0}{beam1} - $hfcg->{0}{beam2}) * 1 == 0); my $epc = CurrentVal ($hash, 'ePurchasePriceCcy', 0); my $efc = CurrentVal ($hash, 'eFeedInTariffCcy', 0); @@ -16957,7 +17026,7 @@ sub _beamGraphicRemainingHours { $hfcg->{$i}{beam1} //= 0; $hfcg->{$i}{beam2} //= 0; $hfcg->{$i}{diff} = sprintf "%.1f", ($hfcg->{$i}{beam1} - $hfcg->{$i}{beam2}); - $hfcg->{$i}{diff} = sprintf "%.0f", $hfcg->{$i}{diff} if(int ($hfcg->{$i}{diff}) - $hfcg->{$i}{diff} == 0); + $hfcg->{$i}{diff} = sprintf "%.0f", $hfcg->{$i}{diff} if(($hfcg->{$i}{beam1} - $hfcg->{$i}{beam2}) * 1 == 0); $maxVal = $hfcg->{$i}{beam1} if($hfcg->{$i}{beam1} > $maxVal); $maxVal = $hfcg->{$i}{beam2} if($hfcg->{$i}{beam2} > $maxVal); @@ -17049,7 +17118,7 @@ sub _beamGraphic { my $maxhours = $paref->{maxhours}; my $weather = $paref->{weather}; my $show_night = $paref->{show_night}; # alle Balken (Spalten) anzeigen ? - my $show_diff = $paref->{show_diff}; # zusätzliche Anzeige $di{} in allen Typen + my $showdiff = $paref->{showdiff}; # zusätzliche Anzeige $di{} in allen Typen my $scm = $paref->{scm}; # Scale Mode my $lotype = $paref->{lotype}; my $height = $paref->{height}; @@ -17103,7 +17172,7 @@ sub _beamGraphic { #################################### $ret .= __batteryOnBeam ($paref); - if ($show_diff eq 'top') { # Zusätzliche Zeile Ertrag - Verbrauch + if ($showdiff eq 'top') { # Zusätzliche Zeile Ertrag - Verbrauch $ret .= "