diff --git a/fhem/contrib/DS_Starter/76_SolarForecast.pm b/fhem/contrib/DS_Starter/76_SolarForecast.pm index 9d27e2d6f..0d0de3430 100644 --- a/fhem/contrib/DS_Starter/76_SolarForecast.pm +++ b/fhem/contrib/DS_Starter/76_SolarForecast.pm @@ -116,7 +116,8 @@ BEGIN { # Versions History intern my %vNotesIntern = ( - "0.18.0" => "21.03.2021 new sub forecastGraphic from Wzut ", + "0.19.0" => "22.03.2021 new sub HistoryVal, some fixes ", + "0.18.0" => "21.03.2021 implement sub forecastGraphic from Wzut ", "0.17.1" => "21.03.2021 bug fixes, delete Helper->NextHour ", "0.17.0" => "20.03.2021 new attr cloudFactorSlope / rainFactorSlope, fixes in Graphic sub ", "0.16.0" => "19.03.2021 new getter nextHours, some fixes ", @@ -1400,7 +1401,7 @@ sub _transferDWDForecastValues { Log3($name, 5, "$name - collect DWD forecast data: device=$fcname, rad=fc${fd}_${fh}_Rad1h, Rad1h=$v"); - my $calcpv = calcPVforecast ($name, $v, $num, $t, $fd); # Vorhersage gewichtet kalkulieren + my $calcpv = calcPVforecast ($name, $v, $num, $t, $fh, $fd); # Vorhersage gewichtet kalkulieren my $num1 = $num-1; if($num1 >= 0) { @@ -1413,6 +1414,7 @@ sub _transferDWDForecastValues { $data{$type}{$name}{nexthours}{$time_str}{pvforecast} = $calcpv; $data{$type}{$name}{nexthours}{$time_str}{starttime} = $ta; + $data{$type}{$name}{nexthours}{$time_str}{Rad1h} = $v; # nur Info: original Vorhersage Strahlungsdaten } if($num < 23 && $fh > 0 && $fh < 24) { # Ringspeicher PV forecast Forum: https://forum.fhem.de/index.php/topic,117864.msg1133350.html#msg1133350 @@ -1420,7 +1422,7 @@ sub _transferDWDForecastValues { } - $hash->{HELPER}{"fc${fd}_".sprintf("%02d",$fh)."_Rad1h"} = $v." kJ/m2"; # nur Info: original Vorhersage Strahlungsdaten zur Berechnung Auto-Korrekturfaktor in Helper speichern + # $hash->{HELPER}{"fc${fd}_".sprintf("%02d",$fh)."_Rad1h"} = $v." kJ/m2"; # nur Info: original Vorhersage Strahlungsdaten zur Berechnung Auto-Korrekturfaktor in Helper speichern if($fd == 0 && int $calcpv > 0) { # Vorhersagedaten des aktuellen Tages zum manuellen Vergleich in Reading speichern push @$daref, "Today_Hour".sprintf("%02d",$fh)."_PVforecast:$calcpv Wh"; @@ -1807,7 +1809,7 @@ sub forecastGraphic { my ($val,$height); my ($z2,$z3,$z4); my $he; # Balkenhöhe - #my (%is,%di); + my $hfcg = $data{$hash->{TYPE}}{$name}{html}; #(hfcg = hash forecast graphic) my $pvhist = $data{$hash->{TYPE}}{$name}{pvhist}; my $next_hour = $data{$hash->{TYPE}}{$name}{nexthours}; @@ -1993,9 +1995,7 @@ sub forecastGraphic { my $alias = AttrVal ($name, "alias", $name ); # Linktext als Aliasname my $dlink = "$alias"; - my $lup = ReadingsTimestamp($name, "NextHour00_PVforecast", "0000-00-00 00:00:00"); # letzter Forecast Update - # ToDo : warum geht das hier nicht ? - #my $lup = (exists($next_hour->{NextHour00}{starttime})) ? $next_hour->{NextHour00}{starttime} : '0000-00-00 00:00:00' ; + my $lup = ReadingsTimestamp($name, "NextHour00_PVforecast", "0000-00-00 00:00:00"); # letzter Forecast Update my $lupt = "last update:"; my $autoct = "automatic correction:"; @@ -2027,7 +2027,7 @@ sub forecastGraphic { $lup = "$year-$month-$day $time"; } - my $cmdupdate = "\"FW_cmd('$FW_ME$FW_subdir?XHR=1&cmd=get $name data')\""; # Update Button generieren + my $cmdupdate = "\"FW_cmd('$FW_ME$FW_subdir?XHR=1&cmd=get $name data')\""; # Update Button generieren if ($ftui eq "ftui") { $cmdupdate = "\"ftui.setFhemStatus('get $name data')\""; @@ -2113,23 +2113,19 @@ sub forecastGraphic { my $day; my $t = (exists($next_hour->{'NextHour00'}{starttime})) ? $next_hour->{'NextHour00'}{starttime} : (AttrVal('global', 'language', '') eq 'DE') ? '00.00.0000 24' : '0000-00-00 24'; - #($year,$month,$day,$thishour) = ReadingsVal($name, "NextHour00_Time", '0000-00-00 24') =~ m/(\d{4})-(\d{2})-(\d{2})\s(\d{2})/x; ($year,$month,$day_str,$thishour) = $t =~ m/(\d{4})-(\d{2})-(\d{2})\s(\d{2})/x; - - #($day,$month,$year,$thishour) = ReadingsVal($name, "NextHour00_Time", '00.00.0000 24') =~ m/(\d{2}).(\d{2}).(\d{4})\s(\d{2})/x if (AttrVal('global', 'language', '') eq 'DE'); - #$t = (exists($next_hour->{'NextHour00'}{starttime})) ? $next_hour->{'NextHour00'}{starttime} : '00.00.0000 24'; ($day_str,$month,$year,$thishour) = $t =~ m/(\d{2}).(\d{2}).(\d{4})\s(\d{2})/x if (AttrVal('global', 'language', '') eq 'DE'); + $thishour++; + $hfcg->{0}{time_str} = $thishour; - $thishour = int($thishour); # keine führende Null + $thishour = int($thishour); # keine führende Null $hfcg->{0}{time} = $thishour; $hfcg->{0}{day_str} = $day_str; $day = int($day_str); $hfcg->{0}{day} = $day; - # gleich die Unix Zeit dazu holen - #$hfcg->{0}{mktime} = timelocal(0,0,$thishour,$day,int($month),$year); - $hfcg->{0}{mktime} = fhemTimeLocal(0,0,$thishour,$day,int($month),$year); + $hfcg->{0}{mktime} = fhemTimeLocal(0,0,$thishour,$day,int($month),$year); # gleich die Unix Zeit dazu holen my $val1 = 0; my $val2 = 0; @@ -2141,21 +2137,16 @@ sub forecastGraphic { if ($hfcg->{0}{time} < 0) { $hfcg->{0}{time} += 24; - # Achtung : Tageswechsel - day muss jetzt neu berechnet werden ! - my $n_day = strftime "%d", localtime($hfcg->{0}{mktime} - (3600 * abs($offset))); + my $n_day = strftime "%d", localtime($hfcg->{0}{mktime} - (3600 * abs($offset))); # Achtung : Tageswechsel - day muss jetzt neu berechnet werden ! $hfcg->{0}{day} = int($n_day); $hfcg->{0}{day_str} = $n_day; - # hfcg->{0}{day_str} muss jetzt ungleich dem $day_str sein ! } $hfcg->{0}{time_str} = sprintf('%02d', $hfcg->{0}{time}); - - # Achtung Falle : - #if (exists($pvhist->{$hfcg->{0}{day_str}})) { - $val1 = (exists($pvhist->{$hfcg->{0}{day_str}}{$hfcg->{0}{time_str}}{pvfc})) ? $pvhist->{$hfcg->{0}{day_str}}{$hfcg->{0}{time_str}}{pvfc} : 0; - $val2 = (exists($pvhist->{$hfcg->{0}{day_str}}{$hfcg->{0}{time_str}}{pvrl})) ? $pvhist->{$hfcg->{0}{day_str}}{$hfcg->{0}{time_str}}{pvrl} : 0; - $val3 = (exists($pvhist->{$hfcg->{0}{day_str}}{$hfcg->{0}{time_str}}{gcons})) ? $pvhist->{$hfcg->{0}{day_str}}{$hfcg->{0}{time_str}}{gcons} : 0; - #} + + $val1 = HistoryVal ($hash, $hfcg->{0}{day_str}, $hfcg->{0}{time_str}, "pvfc", 0); + $val2 = HistoryVal ($hash, $hfcg->{0}{day_str}, $hfcg->{0}{time_str}, "pvrl", 0); + $val3 = HistoryVal ($hash, $hfcg->{0}{day_str}, $hfcg->{0}{time_str}, "gcons", 0); $hfcg->{0}{weather} = (exists($data{$hash->{TYPE}}{$name}{weather}{$hfcg->{0}{time_str}}{id})) ? $data{$hash->{TYPE}}{$name}{weather}{$hfcg->{0}{time_str}}{id} : undef; } @@ -2168,14 +2159,13 @@ sub forecastGraphic { #$val4 = (ReadingsVal($name,"ThisHour_IsConsumptionRecommended",'no') eq 'yes' ) ? $icon : undef; } - $hfcg->{0}{time_str} .= $hourstyle; + $hfcg->{0}{time_str} = sprintf('%02d', $hfcg->{0}{time}-1).$hourstyle; $hfcg->{0}{beam1} = ($beam1cont eq 'forecast') ? $val1 : ($beam1cont eq 'real') ? $val2 : ($beam1cont eq 'consumption') ? $val3 : $val4; $hfcg->{0}{beam2} = ($beam2cont eq 'forecast') ? $val1 : ($beam2cont eq 'real') ? $val2 : ($beam2cont eq 'consumption') ? $val3 : $val4; $hfcg->{0}{diff} = $hfcg->{0}{beam1} - $hfcg->{0}{beam2}; - # User Auswahl überschreiben wenn beide Werte die gleiche Basis haben ! - $lotype = 'pv' if ($beam1cont eq $beam2cont); + $lotype = 'pv' if ($beam1cont eq $beam2cont); # User Auswahl überschreiben wenn beide Werte die gleiche Basis haben ! ########################################################### # get consumer list and display it in portalGraphics @@ -2242,13 +2232,13 @@ sub forecastGraphic { $hfcg->{$i}{time} = $hfcg->{0}{time} + $i; - while ($hfcg->{$i}{time} > 23) { + while ($hfcg->{$i}{time} > 24) { $hfcg->{$i}{time} -= 24; # wird bis zu 2x durchlaufen } $hfcg->{$i}{time_str} = sprintf('%02d', $hfcg->{$i}{time}); - my $nh; # next hour + my $nh; # next hour if ($offset < 0) { if ($i <= abs($offset)) { @@ -2270,12 +2260,11 @@ sub forecastGraphic { if (defined($nh)) { $val1 = $next_hour->{'NextHour'.$nh} ? $next_hour->{'NextHour'.$nh}{pvforecast} // 0 : 0; - # ToDo : klären ob -1 oder nicht ! $hfcg->{$i}{weather} = $next_hour->{'NextHour'.$nh} ? $next_hour->{'NextHour'.$nh}{weatherid} // undef : undef; #$val4 = (ReadingsVal($name,"NextHour".$ii."_IsConsumptionRecommended",'no') eq 'yes') ? $icon : undef; } - $hfcg->{$i}{time_str} .= $hourstyle; + $hfcg->{$i}{time_str} = sprintf('%02d', $hfcg->{$i}{time}-1).$hourstyle; $hfcg->{$i}{beam1} = ($beam1cont eq 'forecast') ? $val1 : ($beam1cont eq 'real') ? $val2 : ($beam1cont eq 'consumption') ? $val3 : $val4; $hfcg->{$i}{beam2} = ($beam2cont eq 'forecast') ? $val1 : ($beam2cont eq 'real') ? $val2 : ($beam2cont eq 'consumption') ? $val3 : $val4; @@ -2291,349 +2280,343 @@ sub forecastGraphic { $minDif = $hfcg->{$i}{diff} if ($hfcg->{$i}{diff} < $minDif); } - #Log3($hash,3,Dumper($hfcg)); - ###################################### - # Tabellen Ausgabe erzeugen - ###################################### + #Log3($hash,3,Dumper($hfcg)); + ###################################### + # Tabellen Ausgabe erzeugen + ###################################### - # Wenn Table class=block alleine steht, zieht es bei manchen Styles die Ausgabe auf 100% Seitenbreite - # lässt sich durch einbetten in eine zusätzliche Table roomoverview eindämmen - # Die Tabelle ist recht schmal angelegt, aber nur so lassen sich Umbrüche erzwingen + # Wenn Table class=block alleine steht, zieht es bei manchen Styles die Ausgabe auf 100% Seitenbreite + # lässt sich durch einbetten in eine zusätzliche Table roomoverview eindämmen + # Die Tabelle ist recht schmal angelegt, aber nur so lassen sich Umbrüche erzwingen - $ret = ""; - $ret .= $html_start if (defined($html_start)); - $ret .= ""; - $ret .= ""; - $ret .= ""; + $ret .= ""; + } + } + + if ($lotype eq 'pvco') { + my ($color1, $color2, $style1, $style2, $v); + + $ret .="
"; - $ret .= "\n"; # das \n erleichtert das Lesen der debug Quelltextausgabe + $ret = ""; + $ret .= $html_start if (defined($html_start)); + $ret .= ""; + $ret .= "
"; + $ret .= "
"; + $ret .= "\n"; # das \n erleichtert das Lesen der debug Quelltextausgabe - if ($header) { # Header ausgeben - $ret .= ""; - # mit einem extra ein wenig mehr Platz machen, ergibt i.d.R. weniger als ein Zeichen - $ret .= ""; - } + if ($header) { # Header ausgeben + $ret .= ""; + # mit einem extra ein wenig mehr Platz machen, ergibt i.d.R. weniger als ein Zeichen + $ret .= ""; + } - if ($legend_txt && ($legend eq 'top')) { - $ret .= ""; - $ret .= ""; - } + if ($legend_txt && ($legend eq 'top')) { + $ret .= ""; + $ret .= ""; + } - if ($weather) { - $ret .= ""; # freier Platz am Anfang + if ($weather) { + $ret .= ""; # freier Platz am Anfang - my $ii; - for my $i (0..($maxhours*2)-1) { + my $ii; + for my $i (0..($maxhours*2)-1) { - last if (!exists($hfcg->{$i}{weather})); - next if (!$show_night && ($hfcg->{$i}{weather} > 99) && !$hfcg->{$i}{beam1} && !$hfcg->{$i}{beam2}); - # Lässt Nachticons aber noch durch wenn es einen Wert gibt , ToDo : klären ob die Nacht richtig gesetzt wurde - $ii++; # wieviele Stunden Icons haben wir bisher beechnet ? - last if ($ii > $maxhours); # vorzeitiger Abbruch + last if (!exists($hfcg->{$i}{weather})); + next if (!$show_night && defined($hfcg->{$i}{weather}) && ($hfcg->{$i}{weather} > 99) && !$hfcg->{$i}{beam1} && !$hfcg->{$i}{beam2}); + # Lässt Nachticons aber noch durch wenn es einen Wert gibt , ToDo : klären ob die Nacht richtig gesetzt wurde + $ii++; # wieviele Stunden Icons haben wir bisher beechnet ? + last if ($ii > $maxhours); # vorzeitiger Abbruch - # FHEM Wetter Icons (weather_xxx) , Skalierung und Farbe durch FHEM Bordmittel - # ToDo : weather_icon sollte im Fehlerfall Title mit der ID besetzen um in FHEMWEB sofort die ID sehen zu können - if (exists($hfcg->{$i}{weather}) && defined($hfcg->{$i}{weather})) { - my ($icon_name, $title) = (($hfcg->{$i}{weather} > 99)) ? weather_icon($hfcg->{$i}{weather}-100) : weather_icon($hfcg->{$i}{weather}); - # unknown -> FHEM Icon Fragezeichen im Kreis wird als Ersatz Icon ausgegeben - Log3($name, 4, "$name - unknown weather id: ".$hfcg->{$i}{weather}.", please inform the maintainer") if($icon_name eq 'unknown'); + # FHEM Wetter Icons (weather_xxx) , Skalierung und Farbe durch FHEM Bordmittel + # ToDo : weather_icon sollte im Fehlerfall Title mit der ID besetzen um in FHEMWEB sofort die ID sehen zu können + if (exists($hfcg->{$i}{weather}) && defined($hfcg->{$i}{weather})) { + my ($icon_name, $title) = (($hfcg->{$i}{weather} > 99)) ? weather_icon($hfcg->{$i}{weather}-100) : weather_icon($hfcg->{$i}{weather}); + # unknown -> FHEM Icon Fragezeichen im Kreis wird als Ersatz Icon ausgegeben + Log3($name, 4, "$name - unknown weather id: ".$hfcg->{$i}{weather}.", please inform the maintainer") if($icon_name eq 'unknown'); - $icon_name .= ($hfcg->{$i}{weather} < 100 ) ? '@'.$colorw : '@'.$colorwn; - $val = FW_makeImage($icon_name); + $icon_name .= ($hfcg->{$i}{weather} < 100 ) ? '@'.$colorw : '@'.$colorwn; + $val = FW_makeImage($icon_name); - if ($val eq $icon_name) { # passendes Icon beim User nicht vorhanden ! ( attr web iconPath falsch/prüfen/update ? ) - $val ='???'; - Log3($name, 3, qq{$name - the icon $hfcg->{$i}{weather} not found. Please check attribute "iconPath" of your FHEMWEB instance and/or update your FHEM software}); - } + if ($val eq $icon_name) { # passendes Icon beim User nicht vorhanden ! ( attr web iconPath falsch/prüfen/update ? ) + $val ='???'; + Log3($name, 3, qq{$name - the icon $hfcg->{$i}{weather} not found. Please check attribute "iconPath" of your FHEMWEB instance and/or update your FHEM software}); + } - $ret .= ""; # title -> Mouse Over Text - # mit $hfcg->{$i}{weather} = undef kann man unten leicht feststellen ob für diese Spalte bereits ein Icon ausgegeben wurde oder nicht - } - else { - $ret .= ""; - $hfcg->{$i}{weather} = undef; # ToDo : prüfen ob noch nötig - } - } + $ret .= ""; # title -> Mouse Over Text + # mit $hfcg->{$i}{weather} = undef kann man unten leicht feststellen ob für diese Spalte bereits ein Icon ausgegeben wurde oder nicht + } + else { + $ret .= ""; + $hfcg->{$i}{weather} = undef; # ToDo : prüfen ob noch nötig + } + } - $ret .= ""; # freier Platz am Ende der Icon Zeile - } + $ret .= ""; # freier Platz am Ende der Icon Zeile + } - if($show_diff eq 'top') { # Zusätzliche Zeile Ertrag - Verbrauch - $ret .= ""; # freier Platz am Anfang - my $ii; - for my $i (0..($maxhours*2)-1) { - # gleiche Bedingung wie oben - next if (!$show_night && ($hfcg->{$i}{weather} > 99) && !$hfcg->{$i}{beam1} && !$hfcg->{$i}{beam2}); - $ii++; # wieviele Stunden haben wir bisher angezeigt ? - last if ($ii > $maxhours); # vorzeitiger Abbruch + if($show_diff eq 'top') { # Zusätzliche Zeile Ertrag - Verbrauch + $ret .= ""; # freier Platz am Anfang + my $ii; + for my $i (0..($maxhours*2)-1) { + # gleiche Bedingung wie oben + next if (!$show_night && ($hfcg->{$i}{weather} > 99) && !$hfcg->{$i}{beam1} && !$hfcg->{$i}{beam2}); + $ii++; # wieviele Stunden haben wir bisher angezeigt ? + last if ($ii > $maxhours); # vorzeitiger Abbruch - $val = formatVal6($hfcg->{$i}{diff},$kw,$hfcg->{$i}{weather}); - $val = ($hfcg->{$i}{diff} < 0) ? ''.$val.'' : ($val>0) ? '+'.$val : $val; # negative Zahlen in Fettschrift, 0 aber ohne + - $ret .= ""; - } - $ret .= ""; # freier Platz am Ende - } + $val = formatVal6($hfcg->{$i}{diff},$kw,$hfcg->{$i}{weather}); + $val = ($hfcg->{$i}{diff} < 0) ? ''.$val.'' : ($val>0) ? '+'.$val : $val; # negative Zahlen in Fettschrift, 0 aber ohne + + $ret .= ""; + } + $ret .= ""; # freier Platz am Ende + } - $ret .= ""; # Neue Zeile mit freiem Platz am Anfang + $ret .= ""; # Neue Zeile mit freiem Platz am Anfang - my $ii = 0; - for my $i (0..($maxhours*2)-1) { - # gleiche Bedingung wie oben - next if (!$show_night && ($hfcg->{$i}{weather} > 99) && !$hfcg->{$i}{beam1} && !$hfcg->{$i}{beam2}); - $ii++; - last if ($ii > $maxhours); + my $ii = 0; + for my $i (0..($maxhours*2)-1) { + # gleiche Bedingung wie oben + next if (!$show_night && defined($hfcg->{$i}{weather}) && ($hfcg->{$i}{weather} > 99) && !$hfcg->{$i}{beam1} && !$hfcg->{$i}{beam2}); + $ii++; + last if ($ii > $maxhours); - # Achtung Falle, Division by Zero möglich, - # maxVal kann gerade bei kleineren maxhours Ausgaben in der Nacht leicht auf 0 fallen - $height = 200 if (!$height); # Fallback, sollte eigentlich nicht vorkommen, außer der User setzt es auf 0 - $maxVal = 1 if (!int $maxVal); - $maxCon = 1 if (!$maxCon); + # Achtung Falle, Division by Zero möglich, + # maxVal kann gerade bei kleineren maxhours Ausgaben in der Nacht leicht auf 0 fallen + $height = 200 if (!$height); # Fallback, sollte eigentlich nicht vorkommen, außer der User setzt es auf 0 + $maxVal = 1 if (!int $maxVal); + $maxCon = 1 if (!$maxCon); - # Der zusätzliche Offset durch $fsize verhindert bei den meisten Skins - # dass die Grundlinie der Balken nach unten durchbrochen wird + # Der zusätzliche Offset durch $fsize verhindert bei den meisten Skins + # dass die Grundlinie der Balken nach unten durchbrochen wird - if ($lotype eq 'pv') { - $he = int(($maxVal-$hfcg->{$i}{beam1}) / $maxVal*$height) + $fsize; - $z3 = int($height + $fsize - $he); - } + if ($lotype eq 'pv') { + $he = int(($maxVal-$hfcg->{$i}{beam1}) / $maxVal*$height) + $fsize; + $z3 = int($height + $fsize - $he); + } - if ($lotype eq 'pvco') { - # Berechnung der Zonen - # he - freier der Raum über den Balken. fsize wird nicht verwendet, da bei diesem Typ keine Zahlen über den Balken stehen - # z2 - der Ertrag ggf mit Icon - # z3 - der Verbrauch , bei zu kleinem Wert wird der Platz komplett Zone 2 zugeschlagen und nicht angezeigt - # z2 und z3 nach Bedarf tauschen, wenn der Verbrauch größer als der Ertrag ist + if ($lotype eq 'pvco') { + # Berechnung der Zonen + # he - freier der Raum über den Balken. fsize wird nicht verwendet, da bei diesem Typ keine Zahlen über den Balken stehen + # z2 - der Ertrag ggf mit Icon + # z3 - der Verbrauch , bei zu kleinem Wert wird der Platz komplett Zone 2 zugeschlagen und nicht angezeigt + # z2 und z3 nach Bedarf tauschen, wenn der Verbrauch größer als der Ertrag ist - $maxVal = $maxCon if ($maxCon > $maxVal); # wer hat den größten Wert ? + $maxVal = $maxCon if ($maxCon > $maxVal); # wer hat den größten Wert ? - if ($hfcg->{$i}{beam1} > $hfcg->{$i}{beam2}) { # pv oben , co unten - $z2 = $hfcg->{$i}{beam1}; $z3 = $hfcg->{$i}{beam2}; - } - else { # tauschen, Verbrauch ist größer als Ertrag - $z3 = $hfcg->{$i}{beam1}; $z2 = $hfcg->{$i}{beam2}; - } + if ($hfcg->{$i}{beam1} > $hfcg->{$i}{beam2}) { # pv oben , co unten + $z2 = $hfcg->{$i}{beam1}; $z3 = $hfcg->{$i}{beam2}; + } + else { # tauschen, Verbrauch ist größer als Ertrag + $z3 = $hfcg->{$i}{beam1}; $z2 = $hfcg->{$i}{beam2}; + } - $he = int(($maxVal-$z2)/$maxVal*$height); - $z2 = int(($z2 - $z3)/$maxVal*$height); + $he = int(($maxVal-$z2)/$maxVal*$height); + $z2 = int(($z2 - $z3)/$maxVal*$height); - $z3 = int($height - $he - $z2); # was von maxVal noch übrig ist - - if ($z3 < int($fsize/2)) { # dünnen Strichbalken vermeiden / ca. halbe Zeichenhöhe - $z2 += $z3; $z3 = 0; - } - } - - if ($lotype eq 'diff') { # Typ diff - # Berechnung der Zonen - # he - freier der Raum über den Balken , Zahl positiver Wert + fsize - # z2 - positiver Balken inkl Icon - # z3 - negativer Balken - # z4 - Zahl negativer Wert + fsize - - my ($px_pos,$px_neg); - my $maxPV = 0; # ToDo: maxPV noch aus Attribut maxPV ableiten - - if ($maxPV) { # Feste Aufteilung +/- , jeder 50 % bei maxPV = 0 - $px_pos = int($height/2); - $px_neg = $height - $px_pos; # Rundungsfehler vermeiden - } - else { # Dynamische hoch/runter Verschiebung der Null-Linie - if ($minDif >= 0 ) { # keine negativen Balken vorhanden, die Positiven bekommen den gesammten Raum - $px_neg = 0; - $px_pos = $height; - } - else { - if ($maxDif > 0) { - $px_neg = int($height * abs($minDif) / ($maxDif + abs($minDif))); # Wieviel % entfallen auf unten ? - $px_pos = $height-$px_neg; # der Rest ist oben - } - else { # keine positiven Balken vorhanden, die Negativen bekommen den gesammten Raum - $px_neg = $height; - $px_pos = 0; - } - } - } - - if ($hfcg->{$i}{diff} >= 0) { # Zone 2 & 3 mit ihren direkten Werten vorbesetzen - $z2 = $hfcg->{$i}{diff}; - $z3 = abs($minDif); - } - else { - $z2 = $maxDif; - $z3 = abs($hfcg->{$i}{diff}); # Nur Betrag ohne Vorzeichen - } - - # Alle vorbesetzen Werte umrechnen auf echte Ausgabe px - $he = (!$px_pos || !$maxDif) ? 0 : int(($maxDif-$z2)/$maxDif*$px_pos); # Teilung durch 0 vermeiden - $z2 = ($px_pos - $he) ; - - $z4 = (!$px_neg || !$minDif) ? 0 : int((abs($minDif)-$z3)/abs($minDif)*$px_neg); # Teilung durch 0 unbedingt vermeiden - $z3 = ($px_neg - $z4); - - # Beiden Zonen die Werte ausgeben könnten muß fsize als zusätzlicher Raum zugeschlagen werden ! - $he += $fsize; - $z4 += $fsize if ($z3); # komplette Grafik ohne negativ Balken, keine Ausgabe von z3 & z4 - } - - # das style des nächsten TD bestimmt ganz wesentlich das gesammte Design - # das \n erleichtert das lesen des Seitenquelltext beim debugging - # vertical-align:bottom damit alle Balken und Ausgaben wirklich auf der gleichen Grundlinie sitzen - - $ret .=""; - } ## for i + $ret .=""; + $ret .="
$header
$header
$legend_txt
$legend_txt
$val$val
$val
$val
\n"; - - if ($lotype eq 'pv') { - #my $v = ($lotype eq 'co') ? $hfcg->{$i}{beam2} : $hfcg->{$i}{beam1} ; - #$v = 0 if (($lotype eq 'co') && !$hfcg->{$i}{beam1} && !$show_night); # auch bei type co die Nacht ggf. unterdrücken - $val = formatVal6($hfcg->{$i}{beam1},$kw,$hfcg->{$i}{weather}); - - $ret .=""; # mit width=100% etwas bessere Füllung der Balken - $ret .=""; - $ret .=""; - - if ($hfcg->{$i}{beam1} || $show_night) { # Balken nur einfärben wenn der User via Attr eine Farbe vorgibt, sonst bestimmt class odd von TR alleine die Farbe - my $style = "style=\"padding-bottom:0px; vertical-align:top; margin-left:auto; margin-right:auto;"; - $style .= defined $colorfc ? " background-color:#$colorfc\"" : '"'; # Syntaxhilight - - $ret .= ""; - $ret .= ""; - } - } - - if ($lotype eq 'pvco') { - my ($color1, $color2, $style1, $style2, $v); + if ($lotype eq 'diff') { # Typ diff + # Berechnung der Zonen + # he - freier der Raum über den Balken , Zahl positiver Wert + fsize + # z2 - positiver Balken inkl Icon + # z3 - negativer Balken + # z4 - Zahl negativer Wert + fsize - $ret .="
".$val."
"; + $z3 = int($height - $he - $z2); # was von maxVal noch übrig ist - my $sicon = 1; - #$ret .= $is{$i} if (defined ($is{$i}) && $sicon); + if ($z3 < int($fsize/2)) { # dünnen Strichbalken vermeiden / ca. halbe Zeichenhöhe + $z2 += $z3; $z3 = 0; + } + } - ################################## - # inject the new icon if defined - #$ret .= consinject($hash,$i,@pgCDev) if($s); - - $ret .= "
\n"; # mit width=100% etwas bessere Füllung der Balken + my ($px_pos,$px_neg); + my $maxPV = 0; # ToDo: maxPV noch aus Attribut maxPV ableiten - # der Freiraum oben kann beim größten Balken ganz entfallen - $ret .="" if ($he); + if ($maxPV) { # Feste Aufteilung +/- , jeder 50 % bei maxPV = 0 + $px_pos = int($height/2); + $px_neg = $height - $px_pos; # Rundungsfehler vermeiden + } + else { # Dynamische hoch/runter Verschiebung der Null-Linie + if ($minDif >= 0 ) { # keine negativen Balken vorhanden, die Positiven bekommen den gesammten Raum + $px_neg = 0; + $px_pos = $height; + } + else { + if ($maxDif > 0) { + $px_neg = int($height * abs($minDif) / ($maxDif + abs($minDif))); # Wieviel % entfallen auf unten ? + $px_pos = $height-$px_neg; # der Rest ist oben + } + else { # keine positiven Balken vorhanden, die Negativen bekommen den gesammten Raum + $px_neg = $height; + $px_pos = 0; + } + } + } - if($hfcg->{$i}{beam1} > $hfcg->{$i}{beam2}) { # wer ist oben, co pder pv ? Wert und Farbe für Zone 2 & 3 vorbesetzen - $val = formatVal6($hfcg->{$i}{beam1},$kw,$hfcg->{$i}{weather}); - $color1 = $colorfc; - $style1 = "style=\"padding-bottom:0px; padding-top:1px; vertical-align:top; margin-left:auto; margin-right:auto;"; - $style1 .= (defined($color1)) ? " background-color:#$color1\"" : '"'; - - if($z3) { # die Zuweisung können wir uns sparen wenn Zone 3 nachher eh nicht ausgegeben wird - $v = formatVal6($hfcg->{$i}{beam2},$kw,$hfcg->{$i}{weather}); - $color2 = $colorc; - $style2 = "style=\"padding-bottom:0px; padding-top:1px; vertical-align:top; margin-left:auto; margin-right:auto;"; - $style2 .= (defined($color2)) ? " background-color:#$color2\"" : '"'; - } - } - else { - $val = formatVal6($hfcg->{$i}{beam2},$kw,$hfcg->{$i}{weather}); - $color1 = $colorc; - $style1 = "style=\"padding-bottom:0px; padding-top:1px; vertical-align:top; margin-left:auto; margin-right:auto;"; - $style1 .= (defined($color1)) ? " background-color:#$color1\"" : '"'; - - if($z3) { - $v = formatVal6($hfcg->{$i}{beam1},$kw,$hfcg->{$i}{weather}); - $color2 = $colorfc; - $style2 = "style=\"padding-bottom:0px; padding-top:1px; vertical-align:top; margin-left:auto; margin-right:auto;"; - $style2 .= (defined($color2)) ? " background-color:#$color2\"" : '"'; - } - } - - $ret .= ""; - $ret .= ""; - - if ($z3) { # die Zone 3 lassen wir bei zu kleinen Werten auch ganz weg - $ret .= ""; - $ret .= ""; - } - } - - if ($lotype eq 'diff') { # Type diff - my $style = "style=\"padding-bottom:0px; padding-top:1px; vertical-align:top; margin-left:auto; margin-right:auto;"; - $ret .= "
$val"; - #$ret .= $is{$i} if (defined $is{$i}); - - ################################## - # inject the new icon if defined - #$ret .= consinject($hash,$i,@pgCDev) if($s); - - $ret .= "
$v
\n"; # Tipp : das nachfolgende border=0 auf 1 setzen hilft sehr Ausgabefehler zu endecken - #$val = ($hfcg->{$i}{diff} >= 0) ? formatVal6($hfcg->{$i}{diff},$kw,$hfcg->{$i}{weather}) : ''; - - $val = ($hfcg->{$i}{diff} > 0) ? formatVal6($hfcg->{$i}{diff},$kw,$hfcg->{$i}{weather}) : ''; - $val = '   0  ' if ($hfcg->{$i}{diff} == 0); # Sonderfall , hier wird die 0 gebraucht ! - - if ($val) { - $ret .= ""; - $ret .= ""; - } - - if ($hfcg->{$i}{diff} >= 0) { # mit Farbe 1 colorfc füllen - $style .= defined $colorfc ? " background-color:#$colorfc\"" : '"'; - $z2 = 1 if ($hfcg->{$i}{diff} == 0); # Sonderfall , 1px dünnen Strich ausgeben - $ret .= ""; - $ret .= ""; - } - else { # ohne Farbe - $z2 = 2 if ($hfcg->{$i}{diff} == 0); # Sonderfall, hier wird die 0 gebraucht ! - if ($z2 && $val) { # z2 weglassen wenn nicht unbedigt nötig bzw. wenn zuvor he mit val keinen Wert hatte - $ret .= ""; - $ret .= ""; - } - } + if ($hfcg->{$i}{diff} >= 0) { # Zone 2 & 3 mit ihren direkten Werten vorbesetzen + $z2 = $hfcg->{$i}{diff}; + $z3 = abs($minDif); + } + else { + $z2 = $maxDif; + $z3 = abs($hfcg->{$i}{diff}); # Nur Betrag ohne Vorzeichen + } - if ($hfcg->{$i}{diff} < 0) { # Negativ Balken anzeigen ? - $style .= (defined($colorc)) ? " background-color:#$colorc\"" : '"'; # mit Farbe 2 colorc füllen - $ret .= ""; - $ret .= ""; - } - elsif ($z3) { # ohne Farbe - $ret .= ""; - $ret .= ""; - } + # Alle vorbesetzen Werte umrechnen auf echte Ausgabe px + $he = (!$px_pos || !$maxDif) ? 0 : int(($maxDif-$z2)/$maxDif*$px_pos); # Teilung durch 0 vermeiden + $z2 = ($px_pos - $he) ; - if($z4) { # kann entfallen wenn auch z3 0 ist - $val = ($hfcg->{$i}{diff} < 0) ? formatVal6($hfcg->{$i}{diff},$kw,$hfcg->{$i}{weather}) : ' '; - $ret .= ""; - $ret .= ""; - } - } + $z4 = (!$px_neg || !$minDif) ? 0 : int((abs($minDif)-$z3)/abs($minDif)*$px_neg); # Teilung durch 0 unbedingt vermeiden + $z3 = ($px_neg - $z4); - if ($show_diff eq 'bottom') { # zusätzliche diff Anzeige - $val = formatVal6($hfcg->{$i}{diff},$kw,$hfcg->{$i}{weather}); - #$val = 0 if ($val eq ' '); - #$hfcg->{$i}{diff} = 0 if ($hfcg->{$i}{diff} eq ' '); - $val = ($hfcg->{$i}{diff} < 0) ? ''.$val.'' : ($val > 0 ) ? '+'.$val : $val if ($val ne ' '); # negative Zahlen in Fettschrift, 0 aber ohne + - $ret .= ""; - } + # Beiden Zonen die Werte ausgeben könnten muß fsize als zusätzlicher Raum zugeschlagen werden ! + $he += $fsize; + $z4 += $fsize if ($z3); # komplette Grafik ohne negativ Balken, keine Ausgabe von z3 & z4 + } + + # das style des nächsten TD bestimmt ganz wesentlich das gesammte Design + # das \n erleichtert das lesen des Seitenquelltext beim debugging + # vertical-align:bottom damit alle Balken und Ausgaben wirklich auf der gleichen Grundlinie sitzen - $ret .= "
".$val."
"; - #$ret .= $is{$i} if (defined $is{$i}); - $ret .= "
".$val."
$val
"; - $ret .= (($hfcg->{$i}{time} == $thishour) && ($offset < 0)) ? ''.$hfcg->{$i}{time_str}.'' : $hfcg->{$i}{time_str}; - $thishour = 99 if ($hfcg->{$i}{time} == $thishour); # nur einmal verwenden ! - $ret .="
\n"; + if ($lotype eq 'pv') { + #my $v = ($lotype eq 'co') ? $hfcg->{$i}{beam2} : $hfcg->{$i}{beam1} ; + #$v = 0 if (($lotype eq 'co') && !$hfcg->{$i}{beam1} && !$show_night); # auch bei type co die Nacht ggf. unterdrücken + $val = formatVal6($hfcg->{$i}{beam1},$kw,$hfcg->{$i}{weather}); - $ret .= "
"; # mit width=100% etwas bessere Füllung der Balken + $ret .=""; + $ret .=""; - ################### - # Legende unten - if ($legend_txt && ($legend eq 'bottom')) { - $ret .= ""; - $ret .= ""; - } + if ($hfcg->{$i}{beam1} || $show_night) { # Balken nur einfärben wenn der User via Attr eine Farbe vorgibt, sonst bestimmt class odd von TR alleine die Farbe + my $style = "style=\"padding-bottom:0px; vertical-align:top; margin-left:auto; margin-right:auto;"; + $style .= defined $colorfc ? " background-color:#$colorfc\"" : '"'; # Syntaxhilight - $ret .= "
".$val."
"; - $ret .= "$legend_txt
"; - $ret .= $html_end if (defined($html_end)); - $ret .= ""; + $ret .= "
"; + + my $sicon = 1; + #$ret .= $is{$i} if (defined ($is{$i}) && $sicon); - return $ret; + ################################## + # inject the new icon if defined + #$ret .= consinject($hash,$i,@pgCDev) if($s); + + $ret .= "
\n"; # mit width=100% etwas bessere Füllung der Balken + + # der Freiraum oben kann beim größten Balken ganz entfallen + $ret .="" if ($he); + + if($hfcg->{$i}{beam1} > $hfcg->{$i}{beam2}) { # wer ist oben, co pder pv ? Wert und Farbe für Zone 2 & 3 vorbesetzen + $val = formatVal6($hfcg->{$i}{beam1},$kw,$hfcg->{$i}{weather}); + $color1 = $colorfc; + $style1 = "style=\"padding-bottom:0px; padding-top:1px; vertical-align:top; margin-left:auto; margin-right:auto;"; + $style1 .= (defined($color1)) ? " background-color:#$color1\"" : '"'; + + if($z3) { # die Zuweisung können wir uns sparen wenn Zone 3 nachher eh nicht ausgegeben wird + $v = formatVal6($hfcg->{$i}{beam2},$kw,$hfcg->{$i}{weather}); + $color2 = $colorc; + $style2 = "style=\"padding-bottom:0px; padding-top:1px; vertical-align:top; margin-left:auto; margin-right:auto;"; + $style2 .= (defined($color2)) ? " background-color:#$color2\"" : '"'; + } + } + else { + $val = formatVal6($hfcg->{$i}{beam2},$kw,$hfcg->{$i}{weather}); + $color1 = $colorc; + $style1 = "style=\"padding-bottom:0px; padding-top:1px; vertical-align:top; margin-left:auto; margin-right:auto;"; + $style1 .= (defined($color1)) ? " background-color:#$color1\"" : '"'; + + if($z3) { + $v = formatVal6($hfcg->{$i}{beam1},$kw,$hfcg->{$i}{weather}); + $color2 = $colorfc; + $style2 = "style=\"padding-bottom:0px; padding-top:1px; vertical-align:top; margin-left:auto; margin-right:auto;"; + $style2 .= (defined($color2)) ? " background-color:#$color2\"" : '"'; + } + } + + $ret .= ""; + $ret .= ""; + + if ($z3) { # die Zone 3 lassen wir bei zu kleinen Werten auch ganz weg + $ret .= ""; + $ret .= ""; + } + } + + if ($lotype eq 'diff') { # Type diff + my $style = "style=\"padding-bottom:0px; padding-top:1px; vertical-align:top; margin-left:auto; margin-right:auto;"; + $ret .= "
$val"; + + ################################## + # inject the new icon if defined + #$ret .= consinject($hash,$i,@pgCDev) if($s); + + $ret .= "
$v
\n"; # Tipp : das nachfolgende border=0 auf 1 setzen hilft sehr Ausgabefehler zu endecken + + $val = ($hfcg->{$i}{diff} > 0) ? formatVal6($hfcg->{$i}{diff},$kw,$hfcg->{$i}{weather}) : ''; + $val = '   0  ' if ($hfcg->{$i}{diff} == 0); # Sonderfall , hier wird die 0 gebraucht ! + + if ($val) { + $ret .= ""; + $ret .= ""; + } + + if ($hfcg->{$i}{diff} >= 0) { # mit Farbe 1 colorfc füllen + $style .= defined $colorfc ? " background-color:#$colorfc\"" : '"'; + $z2 = 1 if ($hfcg->{$i}{diff} == 0); # Sonderfall , 1px dünnen Strich ausgeben + $ret .= ""; + $ret .= ""; + } + else { # ohne Farbe + $z2 = 2 if ($hfcg->{$i}{diff} == 0); # Sonderfall, hier wird die 0 gebraucht ! + if ($z2 && $val) { # z2 weglassen wenn nicht unbedigt nötig bzw. wenn zuvor he mit val keinen Wert hatte + $ret .= ""; + $ret .= ""; + } + } + + if ($hfcg->{$i}{diff} < 0) { # Negativ Balken anzeigen ? + $style .= (defined($colorc)) ? " background-color:#$colorc\"" : '"'; # mit Farbe 2 colorc füllen + $ret .= ""; + $ret .= ""; + } + elsif ($z3) { # ohne Farbe + $ret .= ""; + $ret .= ""; + } + + if($z4) { # kann entfallen wenn auch z3 0 ist + $val = ($hfcg->{$i}{diff} < 0) ? formatVal6($hfcg->{$i}{diff},$kw,$hfcg->{$i}{weather}) : ' '; + $ret .= ""; + $ret .= ""; + } + } + + if ($show_diff eq 'bottom') { # zusätzliche diff Anzeige + $val = formatVal6($hfcg->{$i}{diff},$kw,$hfcg->{$i}{weather}); + $val = ($hfcg->{$i}{diff} < 0) ? ''.$val.'' : ($val > 0 ) ? '+'.$val : $val if ($val ne ' '); # negative Zahlen in Fettschrift, 0 aber ohne + + $ret .= ""; + } + + $ret .= "
".$val."
"; + $ret .= "
".$val."
$val
"; + $ret .= (($hfcg->{$i}{time} == $thishour) && ($offset < 0)) ? ''.$hfcg->{$i}{time_str}.'' : $hfcg->{$i}{time_str}; + $thishour = 99 if ($hfcg->{$i}{time} == $thishour); # nur einmal verwenden ! + $ret .="
"; + } + + $ret .= ""; + + ################### + # Legende unten + if ($legend_txt && ($legend eq 'bottom')) { + $ret .= ""; + $ret .= ""; + $ret .= "$legend_txt"; + } + + $ret .= ""; + $ret .= $html_end if (defined($html_end)); + $ret .= ""; + +return $ret; } ################################################################ @@ -2808,8 +2791,9 @@ return @aneeded; sub calcPVforecast { my $name = shift; my $rad = shift; # Nominale Strahlung aus DWD Device - my $num = shift; # Stunde des Tages + my $num = shift; # Nexthour my $t = shift; # aktueller Unix Timestamp + my $fh = shift; my $fd = shift; my $hash = $defs{$name}; @@ -2831,7 +2815,7 @@ sub calcPVforecast { my $rcf = 1 - ((($rainprob - $rain_base)/100) * $rainslope/100); # Rain Correction Faktor mit Steilheit my $kw = AttrVal ($name, 'Wh/kWh', 'Wh'); - my $hc = ReadingsNum ($name, "pvCorrectionFactor_".sprintf("%02d",$num), 1); # Korrekturfaktor für die Stunde des Tages + my $hc = ReadingsNum ($name, "pvCorrectionFactor_".sprintf("%02d",$fh+1), 1); # Korrekturfaktor der Stunde des Tages einbeziehen my $pvsum = 0; @@ -2945,6 +2929,9 @@ sub calcVariance { my ($pvavg,$fcavg) = calcFromHistory ($paref); # historische PV / Forecast Vergleichswerte ermitteln $pvval = $pvavg ? ($pvval + $pvavg) / 2 : $pvval; # Ertrag aktuelle Stunde berücksichtigen $fcval = $fcavg ? ($fcval + $fcavg) / 2 : $fcval; # Vorhersage aktuelle Stunde berücksichtigen + + Log3 ($name, 4, "$name - PV History -> average hour ($h) -> real: $pvval, forecast: $fcval"); + my $factor = sprintf "%.2f", ($pvval / $fcval); # Faktorberechnung: reale PV / Prognose if(abs($factor - $oldfac) > $maxvar) { @@ -3010,15 +2997,13 @@ sub calcFromHistory { my ($pvrl,$pvfc) = (0,0); for my $dayfa (@efa) { - $pvrl += $pvhh->{$dayfa}{$hour}{pvrl} // 0; - $pvfc += $pvhh->{$dayfa}{$hour}{pvfc} // 0; + $pvrl += HistoryVal ($hash, $dayfa, $hour, "pvrl", 0); + $pvfc += HistoryVal ($hash, $dayfa, $hour, "pvfc", 0); } my $pvavg = sprintf "%.2f", $pvrl / $anzavg; my $fcavg = sprintf "%.2f", $pvfc / $anzavg; - Log3 ($name, 4, "$name - PV History -> average hour ($hour) -> real: $pvavg, forecast: $fcavg"); - return ($pvavg,$fcavg); } @@ -3051,7 +3036,7 @@ sub setPVhistory { my $pvrlsum = 0; for my $k (keys %{$data{$type}{$name}{pvhist}{$day}}) { next if($k eq "99"); - $pvrlsum += $data{$type}{$name}{pvhist}{$day}{$k}{pvrl} // 0; + $pvrlsum += HistoryVal ($hash, $day, $k, "pvrl", 0); } $data{$type}{$name}{pvhist}{$day}{99}{pvrl} = $pvrlsum; } @@ -3063,7 +3048,7 @@ sub setPVhistory { my $pvfcsum = 0; for my $k (keys %{$data{$type}{$name}{pvhist}{$day}}) { next if($k eq "99"); - $pvfcsum += $data{$type}{$name}{pvhist}{$day}{$k}{pvfc} // 0; + $pvfcsum += HistoryVal ($hash, $day, $k, "pvfc", 0); } $data{$type}{$name}{pvhist}{$day}{99}{pvfc} = $pvfcsum; } @@ -3075,7 +3060,7 @@ sub setPVhistory { my $gcsum = 0; for my $k (keys %{$data{$type}{$name}{pvhist}{$day}}) { next if($k eq "99"); - $gcsum += $data{$type}{$name}{pvhist}{$day}{$k}{gcons} // 0; + $gcsum += HistoryVal ($hash, $day, $k, "gcons", 0); } $data{$type}{$name}{pvhist}{$day}{99}{gcons} = $gcsum; } @@ -3102,9 +3087,9 @@ sub listDataPool { my $day = shift; my $ret; for my $key (sort{$a<=>$b} keys %{$h->{$day}}) { - my $pvrl = $h->{$day}{$key}{pvrl} // 0; - my $pvfc = $h->{$day}{$key}{pvfc} // 0; - my $cons = $h->{$day}{$key}{gcons} // 0; + my $pvrl = HistoryVal ($hash, $day, $key, "pvrl", 0); + my $pvfc = HistoryVal ($hash, $day, $key, "pvfc", 0); + my $cons = HistoryVal ($hash, $day, $key, "gcons", 0); $ret .= "\n " if($ret); $ret .= $key." => pvreal: $pvrl, pvforecast: $pvfc, gridcon: $cons"; } @@ -3160,13 +3145,14 @@ sub listDataPool { return qq{NextHours cache is empty.}; } for my $idx (sort keys %{$h}) { - my $nhts = $data{$type}{$name}{nexthours}{$idx}{starttime}; - my $nhfc = $data{$type}{$name}{nexthours}{$idx}{pvforecast}; - my $wid = $data{$type}{$name}{nexthours}{$idx}{weatherid}; - my $neff = $data{$type}{$name}{nexthours}{$idx}{cloudcover}; - my $r101 = $data{$type}{$name}{nexthours}{$idx}{rainprob}; - $sq .= "\n" if($sq); - $sq .= $idx." => starttime: $nhts, pvforecast: $nhfc, weatherid: $wid, cloudcover: $neff, rainprob: $r101"; + my $nhts = $data{$type}{$name}{nexthours}{$idx}{starttime}; + my $nhfc = $data{$type}{$name}{nexthours}{$idx}{pvforecast}; + my $wid = $data{$type}{$name}{nexthours}{$idx}{weatherid}; + my $neff = $data{$type}{$name}{nexthours}{$idx}{cloudcover}; + my $r101 = $data{$type}{$name}{nexthours}{$idx}{rainprob}; + my $rad1h = $data{$type}{$name}{nexthours}{$idx}{Rad1h}; + $sq .= "\n" if($sq); + $sq .= $idx." => starttime: $nhts, pvforecast: $nhfc, weatherid: $wid, cloudcover: $neff, rainprob: $r101, Rad1h: $rad1h"; } } @@ -3360,6 +3346,40 @@ sub createNotifyDev { return; } +################################################################ +# Wert des pvhist-Hash zurückliefern +# Usage: +# HistoryVal ($hash, $day, $hod, $key, $def) +# +# $day: Tag des Monats (01,02,...,31) +# $hod: Stunde des Tages (01,02,...,24,99) +# $key: pvrl - realer PV Ertrag +# pvfc - PV Vorhersage +# gcons - realer Netzbezug +# $def: Defaultwert +# +################################################################ +sub HistoryVal { + my $hash = shift; + my $day = shift; + my $hod = shift; + my $key = shift; + my $def = shift; + + my $name = $hash->{NAME}; + my $type = $hash->{TYPE}; + + my ($d,$n,$default) = @_; + if(defined($data{$type}{$name}{pvhist}) && + defined($data{$type}{$name}{pvhist}{$day}) && + defined($data{$type}{$name}{pvhist}{$day}{$hod}) && + defined($data{$type}{$name}{pvhist}{$day}{$hod}{$key})) { + return $data{$type}{$name}{pvhist}{$day}{$hod}{$key}; + } + +return $def; +} + 1; =pod