diff --git a/fhem/CHANGED b/fhem/CHANGED index c3336c2dd..666bce821 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: new attr ctrlGenPVdeviation - feature: 70_ESCVP21net: add reading stateP for on/off - feature: 76_SolarForecast: allow key 'noshow' more values, KI improves - change: 22_HOMEMODE: change url for IP check diff --git a/fhem/FHEM/76_SolarForecast.pm b/fhem/FHEM/76_SolarForecast.pm index 110b8f7c4..69e4d1ded 100644 --- a/fhem/FHEM/76_SolarForecast.pm +++ b/fhem/FHEM/76_SolarForecast.pm @@ -144,6 +144,7 @@ BEGIN { # Versions History intern my %vNotesIntern = ( + "1.0.6" => "19.10.2023 new attr ctrlGenPVdeviation ", "1.0.5" => "11.10.2023 new sub _aiGetSpread for estimate AI results stepwise, allow key 'noshow' values 0,1,2,3 ". "calculate conForecastTillNextSunrise accurate to the minute ", "1.0.4" => "10.10.2023 fix: print always Log in _calcCaQ* subroutines even if calaculated factors are equal ". @@ -683,6 +684,8 @@ my %hqtxt = ( DE => qq{Verbrauch} }, tday => { EN => qq{today}, DE => qq{heute} }, + ctnsly => { EN => qq{continuously}, + DE => qq{fortlaufend} }, yday => { EN => qq{yesterday}, DE => qq{gestern} }, after => { EN => qq{after}, @@ -1012,6 +1015,7 @@ sub Initialize { "ctrlAutoRefreshFW:$fwd ". "ctrlConsRecommendReadings:multiple-strict,$allcs ". "ctrlDebug:multiple-strict,$dm ". + "ctrlGenPVdeviation:daily,continuously ". "ctrlInterval ". "ctrlLanguage:DE,EN ". "ctrlNextDayForecastReadings:multiple-strict,$hod ". @@ -3999,6 +4003,7 @@ sub Attr { my $name = shift; my $aName = shift; my $aVal = shift; + my $hash = $defs{$name}; my ($do,$val); @@ -4021,11 +4026,17 @@ sub Attr { delete $hash->{AUTOREFRESH}; } - if($aName eq "ctrlNextDayForecastReadings") { + if($aName eq 'ctrlNextDayForecastReadings') { deleteReadingspec ($hash, "Tomorrow_Hour.*"); } + + if($aName eq 'ctrlGenPVdeviation' && $aVal eq 'daily') { + my $type = $hash->{TYPE}; + deleteReadingspec ($hash, 'Today_PVdeviation'); + delete $data{$type}{$name}{circular}{99}{tdayDvtn}; + } - if ($cmd eq "set") { + if ($cmd eq 'set') { if ($aName eq 'ctrlInterval') { unless ($aVal =~ /^[0-9]+$/x) { return qq{The value for $aName is not valid. Use only figures 0-9 !}; @@ -4074,7 +4085,7 @@ sub Attr { aVal => $aVal }; - $aName = "consumer" if($aName =~ /consumer?(\d+)$/xs); + $aName = 'consumer' if($aName =~ /consumer?(\d+)$/xs); if($hattr{$aName} && defined &{$hattr{$aName}{fn}}) { my $ret = q{}; @@ -5719,12 +5730,11 @@ sub _transferInverterValues { debugLog ($paref, "collectData", "collect Inverter data - device: $indev =>"); debugLog ($paref, "collectData", "pv: $pv W, etotal: $etotal Wh"); - my $nhour = $chour+1; - + my $nhour = $chour + 1; my $histetot = HistoryVal ($hash, $day, sprintf("%02d",$nhour), "etotal", 0); # etotal zu Beginn einer Stunde my $ethishour; - if (!$histetot) { # etotal der aktuelle Stunde gesetzt ? + if (!$histetot) { # etotal der aktuelle Stunde gesetzt ? $paref->{etotal} = $etotal; $paref->{nhour} = sprintf("%02d",$nhour); $paref->{histname} = "etotal"; @@ -6161,9 +6171,11 @@ sub _createSummaries { } for my $th (1..24) { - $todaySumFc->{PV} += ReadingsNum($name, "Today_Hour".sprintf("%02d",$th)."_PVforecast", 0); - $todaySumRe->{PV} += ReadingsNum($name, "Today_Hour".sprintf("%02d",$th)."_PVreal", 0); + $todaySumFc->{PV} += ReadingsNum ($name, "Today_Hour".sprintf("%02d",$th)."_PVforecast", 0); + $todaySumRe->{PV} += ReadingsNum ($name, "Today_Hour".sprintf("%02d",$th)."_PVreal", 0); } + + my $pvre = int $todaySumRe->{PV}; push @{$data{$type}{$name}{current}{h4fcslidereg}}, int $next4HoursSum->{PV}; # Schieberegister 4h Summe Forecast limitArray ($data{$type}{$name}{current}{h4fcslidereg}, $defslidenum); @@ -6200,6 +6212,7 @@ sub _createSummaries { push @$daref, "Current_SelfConsumptionRate<>". $selfconsumptionrate. " %"; push @$daref, "Current_Surplus<>". $surplus. " W"; push @$daref, "Current_AutarkyRate<>". $autarkyrate. " %"; + push @$daref, "Today_PVreal<>". $pvre. " Wh" if($pvre > ReadingsNum ($name, 'Today_PVreal', 0)); push @$daref, "NextHours_Sum01_PVforecast<>". (int $next1HoursSum->{PV})." Wh"; push @$daref, "NextHours_Sum02_PVforecast<>". (int $next2HoursSum->{PV})." Wh"; @@ -6208,7 +6221,6 @@ sub _createSummaries { push @$daref, "RestOfDayPVforecast<>". (int $restOfDaySum->{PV}). " Wh"; push @$daref, "Tomorrow_PVforecast<>". (int $tomorrowSum->{PV}). " Wh"; push @$daref, "Today_PVforecast<>". (int $todaySumFc->{PV}). " Wh"; - push @$daref, "Today_PVreal<>". (int $todaySumRe->{PV}). " Wh" if(int $todaySumRe->{PV} > ReadingsNum($name, 'Today_PVreal', 0)); push @$daref, "Tomorrow_ConsumptionForecast<>". $tconsum. " Wh" if(defined $tconsum); push @$daref, "NextHours_Sum04_ConsumptionForecast<>". (int $next4HoursSum->{Consumption})." Wh"; @@ -7730,7 +7742,7 @@ return; } ################################################################ -# Kerrektur von Today_PVreal + +# Korrektur von Today_PVreal + # berechnet die prozentuale Abweichung von Today_PVforecast # und Today_PVreal ################################################################ @@ -7744,30 +7756,29 @@ sub _calcTodayPVdeviation { my $day = $paref->{day}; my $daref = $paref->{daref}; - my $sstime = timestringToTimestamp ($date.' '.ReadingsVal($name, "Today_SunSet", '22:00').':00'); - - return if($t < $sstime); - my $pvfc = ReadingsNum ($name, 'Today_PVforecast', 0); my $pvre = ReadingsNum ($name, 'Today_PVreal', 0); - - my $hsr = (split ":", ReadingsVal ($name, 'Today_SunRise', '03:00'))[0]; # die Stunde des Sonnenaufgangs - my $ehsr = HistoryVal ($hash, $day, $hsr, 'etotal', 0); # gespeichertes etotal vor Sonnenaufgang - - if ($ehsr) { - my $esset = CurrentVal ($hash, 'etotal', 0); # Erzeugung total (Wh) nach Sonnenuntergang - $pvre = $esset - $ehsr; + + return if(!$pvre); + + my $dp; + + if (AttrVal($name, 'ctrlGenPVdeviation', 'daily') eq 'daily') { + my $sstime = timestringToTimestamp ($date.' '.ReadingsVal ($name, "Today_SunSet", '22:00').':00'); + return if($t < $sstime); + + my $diff = $pvfc - $pvre; + $dp = sprintf "%.2f" , (100 * $diff / $pvre); } - - my $diff = $pvfc - $pvre; - - if ($pvre) { - my $dp = sprintf "%.2f" , (100 * $diff / $pvre); - $data{$type}{$name}{circular}{99}{tdayDvtn} = $dp; - - push @$daref, "Today_PVdeviation<>". $dp. " %"; - push @$daref, "Today_PVreal<>". (sprintf "%.0f", $pvre)." Wh"; + else { + my $rodfc = ReadingsNum ($name, 'RestOfDayPVforecast', 0); + my $dayfc = $pvre + $rodfc; # laufende Tagesprognose aus PVreal + Prognose Resttag + $dp = sprintf "%.2f", (100 * ($pvfc - $dayfc) / $dayfc); } + + $data{$type}{$name}{circular}{99}{tdayDvtn} = $dp; + + push @$daref, "Today_PVdeviation<>". $dp.' %'; return; } @@ -7848,7 +7859,7 @@ sub genStatisticReadings { my $t = $paref->{t}; # aktueller UNIX Timestamp my @srd = sort keys (%hcsr); - my @csr = split ',', AttrVal($name, 'ctrlStatisticReadings', ''); + my @csr = split ',', AttrVal ($name, 'ctrlStatisticReadings', ''); for my $item (@srd) { next if($item ~~ @csr); @@ -8340,6 +8351,7 @@ sub entryGraphic { flowgconsTime => AttrVal ($name, 'flowGraphicShowConsumerRemainTime', 1), # Verbraucher Restlaufeit in der Energieflußgrafik anzeigen flowgconsDist => AttrVal ($name, 'flowGraphicConsumerDistance', $fgCDdef), # Abstand Verbrauchericons zueinander css => AttrVal ($name, 'flowGraphicCss', $cssdef), # flowGraphicCss Styles + genpvdva => AttrVal ($name, 'ctrlGenPVdeviation', 'daily'), # Methode der Abweichungsberechnung lang => AttrVal ($name, 'ctrlLanguage', AttrVal ('global', 'language', $deflang)), debug => getDebug ($hash), # Debug Module }; @@ -8598,7 +8610,7 @@ sub _graphicHeader { my $hdrDetail = $paref->{hdrDetail}; # ermöglicht den Inhalt zu begrenzen, um bspw. passgenau in ftui einzubetten my $ftui = $paref->{ftui}; my $lang = $paref->{lang}; - my $name = $paref->{name}; + my $name = $paref->{name}; my $hash = $paref->{hash}; my $kw = $paref->{kw}; my $dstyle = $paref->{dstyle}; # TD-Style @@ -8615,14 +8627,14 @@ sub _graphicHeader { my $pvCu = ReadingsNum ($name, "Current_PV", 0); if ($kw eq 'kWh') { - $co4h = sprintf("%.1f" , $co4h/1000)." kWh"; - $coRe = sprintf("%.1f" , $coRe/1000)." kWh"; - $coTo = sprintf("%.1f" , $coTo/1000)." kWh"; - $coCu = sprintf("%.1f" , $coCu/1000)." kW"; - $pv4h = sprintf("%.1f" , $pv4h/1000)." kWh"; - $pvRe = sprintf("%.1f" , $pvRe/1000)." kWh"; - $pvTo = sprintf("%.1f" , $pvTo/1000)." kWh"; - $pvCu = sprintf("%.1f" , $pvCu/1000)." kW"; + $co4h = sprintf ("%.1f", $co4h/1000)." kWh"; + $coRe = sprintf ("%.1f", $coRe/1000)." kWh"; + $coTo = sprintf ("%.1f", $coTo/1000)." kWh"; + $coCu = sprintf ("%.1f", $coCu/1000)." kW"; + $pv4h = sprintf ("%.1f", $pv4h/1000)." kWh"; + $pvRe = sprintf ("%.1f", $pvRe/1000)." kWh"; + $pvTo = sprintf ("%.1f", $pvTo/1000)." kWh"; + $pvCu = sprintf ("%.1f", $pvCu/1000)." kW"; } else { $co4h .= " Wh"; @@ -8820,9 +8832,11 @@ sub _graphicHeader { $tdayDvtn =~ s/\,0//; $ydayDvtn =~ s/\./,/; $ydayDvtn =~ s/,0//; + + my $genpvdva = $paref->{genpvdva}; my $dvtntxt = $hqtxt{dvtn}{$lang}.' '; - my $tdaytxt = $hqtxt{tday}{$lang}.': '."".$tdayDvtn.""; + my $tdaytxt = ($genpvdva eq 'daily' ? $hqtxt{tday}{$lang} : $hqtxt{ctnsly}{$lang}).': '."".$tdayDvtn.""; my $ydaytxt = $hqtxt{yday}{$lang}.': '."".$ydayDvtn.""; my $text_tdayDvtn = $tdayDvtn =~ /^-[1-9]/? $hqtxt{pmtp}{$lang} : @@ -9085,10 +9099,10 @@ sub __createOwnSpec { $ownv .= "