From e02351dd0834eb620fc3022decbd7b865b4a5e61 Mon Sep 17 00:00:00 2001 From: nasseeder1 Date: Thu, 28 Nov 2024 20:35:12 +0000 Subject: [PATCH] 76_SolarForecast: extended Debug collectData for EnergyConsumption git-svn-id: https://svn.fhem.de/fhem/trunk@29371 2b470e98-0d58-463d-a4d8-8e2adae1ed80 --- fhem/FHEM/76_SolarForecast.pm | 357 ++++++++++++++------ fhem/contrib/DS_Starter/76_SolarForecast.pm | 357 ++++++++++++++------ 2 files changed, 496 insertions(+), 218 deletions(-) diff --git a/fhem/FHEM/76_SolarForecast.pm b/fhem/FHEM/76_SolarForecast.pm index 98cbdf17d..e71d1e1be 100644 --- a/fhem/FHEM/76_SolarForecast.pm +++ b/fhem/FHEM/76_SolarForecast.pm @@ -156,8 +156,9 @@ BEGIN { # Versions History intern my %vNotesIntern = ( - "1.37.8" => "27.11.2024 edit commref, func _searchCacheFiles for renaming Cache files when device is renamed ". - "_saveEnergyConsumption: extended for Debug collectData ", + "1.37.8" => "28.11.2024 edit commref, func _searchCacheFiles for renaming Cache files when device is renamed ". + "_saveEnergyConsumption: extended for Debug collectData, preparation of weatherApiData ". + "new func WeatherAPIVal ", "1.37.7" => "26.11.2024 Attr flowGraphicControl: key shift changed to shiftx, new key shifty ". "change: 'trackFlex' && \$wcc >= 70 to \$wcc >= 80 ". "obsolete Attr deleted: flowGraphicCss, flowGraphicSize, flowGraphicAnimate, flowGraphicConsumerDistance, ". @@ -598,6 +599,7 @@ my %hget = ( # Ha nextHours => { fn => \&_getlistNextHours, needcred => 0 }, rooftopData => { fn => \&_getRoofTopData, needcred => 0 }, solApiData => { fn => \&_getlistSolCastData, needcred => 0 }, + weatherApiData => { fn => \&_getlistWeatherApiData, needcred => 0 }, valDecTree => { fn => \&_getaiDecTree, needcred => 0 }, ftuiFramefiles => { fn => \&_ftuiFramefiles, needcred => 0 }, dwdCatalog => { fn => \&_getdwdCatalog, needcred => 0 }, @@ -1159,7 +1161,8 @@ my %hfspvh = ( # $data{$type}{$name}{inverters} # Inverter Hash # $data{$type}{$name}{producers} # non-PV Producer Hash # $data{$type}{$name}{strings} # Stringkonfiguration Hash -# $data{$type}{$name}{solcastapi} # Zwischenspeicher API-Daten +# $data{$type}{$name}{solcastapi} # Zwischenspeicher API-Solardaten +# $data{$type}{$name}{weatherapi} # Zwischenspeicher API-Wetterdaten # $data{$type}{$name}{aidectree}{object} # AI Decision Tree Object # $data{$type}{$name}{aidectree}{aitrained} # AI Decision Tree trainierte Daten # $data{$type}{$name}{aidectree}{airaw} # Rohdaten für AI Input = Raw Trainigsdaten @@ -2472,7 +2475,8 @@ sub Get { "pvHistory:#,exportToCsv,$pvl ". "rooftopData:noArg ". "solApiData:noArg ". - "valCurrent:noArg " + "valCurrent:noArg ". + "weatherApiData:noArg " ; ## KI spezifische Getter @@ -2800,7 +2804,7 @@ sub __solCast_ApiResponse { my $apimaxreq = AttrVal ($name, 'ctrlSolCastAPImaxReq', $solcmaxreqdef); Log3 ($name, 1, "$name DEBUG> SolCast API Call - response status: ".$jdata->{'response_status'}{'message'}); - Log3 ($name, 1, "$name DEBUG> SolCast API Call - todayRemainingAPIrequests: ".SolCastAPIVal($hash, '?All', '?All', 'todayRemainingAPIrequests', $apimaxreq)); + Log3 ($name, 1, "$name DEBUG> SolCast API Call - todayRemainingAPIrequests: ".SolCastAPIVal ($hash, '?All', '?All', 'todayRemainingAPIrequests', $apimaxreq)); } return; @@ -4117,7 +4121,10 @@ sub __openMeteoDWD_ApiRequest { if (!$allstrings) { # alle Strings wurden abgerufen my $apiitv = SolCastAPIVal ($hash, '?All', '?All', 'currentAPIinterval', $ometeorepdef); readingsSingleUpdate ($hash, 'nextRadiationAPICall', $hqtxt{after}{$lang}.' '.(timestampToTimestring ($t + $apiitv, $lang))[0], 1); - $data{$type}{$name}{solcastapi}{'?All'}{'?All'}{todayDoneAPIcalls} += 1; + + $data{$type}{$name}{solcastapi}{'?All'}{'?All'}{todayDoneAPIcalls} += 1; + $data{$type}{$name}{weatherapi}{OpenMeteo}{'?All'}{todayDoneAPIcalls} += 1; + return; } @@ -4228,7 +4235,8 @@ sub __openMeteoDWD_ApiResponse { Log3 ($name, 1, "$name - $msg"); - $data{$type}{$name}{solcastapi}{'?All'}{'?All'}{response_message} = $err; + $data{$type}{$name}{solcastapi}{'?All'}{'?All'}{response_message} = $err; + $data{$type}{$name}{weatherapi}{OpenMeteo}{'?All'}{response_message} = $err; singleUpdateState ( {hash => $hash, state => $msg, evt => 1} ); $data{$type}{$name}{current}{runTimeLastAPIProc} = sprintf "%.4f", tv_interval($sta); # Verarbeitungszeit ermitteln @@ -4254,9 +4262,11 @@ sub __openMeteoDWD_ApiResponse { my $rt = (timestampToTimestring ($t, $lang))[3]; my $jdata = decode_json ($myjson); - $data{$type}{$name}{solcastapi}{'?All'}{'?All'}{lastretrieval_time} = $rt; # letzte Abrufzeit - $data{$type}{$name}{solcastapi}{'?All'}{'?All'}{lastretrieval_timestamp} = $t; # letzter Abrufzeitstempel - + $data{$type}{$name}{solcastapi}{'?All'}{'?All'}{lastretrieval_time} = $rt; # letzte Abrufzeit + $data{$type}{$name}{solcastapi}{'?All'}{'?All'}{lastretrieval_timestamp} = $t; # letzter Abrufzeitstempel + $data{$type}{$name}{weatherapi}{OpenMeteo}{'?All'}{lastretrieval_time} = $rt; + $data{$type}{$name}{weatherapi}{OpenMeteo}{'?All'}{lastretrieval_timestamp} = $t; + ## bei Fehler in API intern kommt ################################### # error: true @@ -4269,14 +4279,17 @@ sub __openMeteoDWD_ApiResponse { singleUpdateState ( {hash => $hash, state => $msg, evt => 1} ); - $data{$type}{$name}{solcastapi}{'?All'}{'?All'}{response_message} = $jdata->{'reason'}; - $data{$type}{$name}{current}{runTimeLastAPIProc} = sprintf "%.4f", tv_interval($sta); # Verarbeitungszeit ermitteln - $data{$type}{$name}{current}{runTimeLastAPIAnswer} = sprintf "%.4f", (tv_interval($stc) - tv_interval($sta)); # API Laufzeit ermitteln + $data{$type}{$name}{solcastapi}{'?All'}{'?All'}{response_message} = $jdata->{'reason'}; + $data{$type}{$name}{weatherapi}{OpenMeteo}{'?All'}{response_message} = $jdata->{'reason'}; + + $data{$type}{$name}{current}{runTimeLastAPIProc} = sprintf "%.4f", tv_interval($sta); # Verarbeitungszeit ermitteln + $data{$type}{$name}{current}{runTimeLastAPIAnswer} = sprintf "%.4f", (tv_interval($stc) - tv_interval($sta)); # API Laufzeit ermitteln return; } - $data{$type}{$name}{solcastapi}{'?All'}{'?All'}{response_message} = 'success'; + $data{$type}{$name}{solcastapi}{'?All'}{'?All'}{response_message} = 'success'; + $data{$type}{$name}{weatherapi}{OpenMeteo}{'?All'}{response_message} = 'success'; if ($debug =~ /apiCall/xs) { Log3 ($name, 1, qq{$name DEBUG> Open-Meteo API Call - server response for PV string "$string"}); @@ -4364,20 +4377,36 @@ sub __openMeteoDWD_ApiResponse { } $data{$type}{$name}{solcastapi}{'?All'}{$pvtmstr}{Rad1h} += $rad; # Startstunde verschieben, Rad Werte aller Strings addieren - $data{$type}{$name}{solcastapi}{'?All'}{$fwtg}{rr1c} = $rain; # Startstunde verschieben - + + ## Wetterdaten + ################ + $data{$type}{$name}{solcastapi}{'?All'}{$fwtg}{rr1c} = $rain; # Startstunde verschieben + $data{$type}{$name}{weatherapi}{OpenMeteo}{$fwtg}{rr1c} = $rain; + $data{$type}{$name}{weatherapi}{OpenMeteo}{$fwtg}{StartTime} = $pvtmstr; + $fwtg = formatWeatherTimestrg ($otmstr); - $data{$type}{$name}{solcastapi}{'?All'}{$fwtg}{don} = $don; - $data{$type}{$name}{solcastapi}{'?All'}{$fwtg}{neff} = $wcc; - $data{$type}{$name}{solcastapi}{'?All'}{$fwtg}{ww} = $wid; - $data{$type}{$name}{solcastapi}{'?All'}{$fwtg}{ttt} = $temp; - $data{$type}{$name}{solcastapi}{'?All'}{$fwtg}{UpdateTime} = $rt; + $data{$type}{$name}{solcastapi}{'?All'}{$fwtg}{don} = $don; + $data{$type}{$name}{solcastapi}{'?All'}{$fwtg}{neff} = $wcc; + $data{$type}{$name}{solcastapi}{'?All'}{$fwtg}{ww} = $wid; + $data{$type}{$name}{solcastapi}{'?All'}{$fwtg}{ttt} = $temp; + $data{$type}{$name}{solcastapi}{'?All'}{$fwtg}{UpdateTime} = $rt; + + $data{$type}{$name}{weatherapi}{OpenMeteo}{$fwtg}{don} = $don; + $data{$type}{$name}{weatherapi}{OpenMeteo}{$fwtg}{neff} = $wcc; + $data{$type}{$name}{weatherapi}{OpenMeteo}{$fwtg}{ww} = $wid; + $data{$type}{$name}{weatherapi}{OpenMeteo}{$fwtg}{ttt} = $temp; + $data{$type}{$name}{weatherapi}{OpenMeteo}{$fwtg}{UpdateTime} = $rt; + $data{$type}{$name}{weatherapi}{OpenMeteo}{$fwtg}{StartTime} = $otmstr; if ($k == 0) { - $data{$type}{$name}{solcastapi}{'?All'}{$fwtg}{neff} = $curwcc if(defined $curwcc); - $data{$type}{$name}{solcastapi}{'?All'}{$fwtg}{ww} = $curwid if(defined $curwid); - $data{$type}{$name}{solcastapi}{'?All'}{$fwtg}{ttt} = $curtmp if(defined $curtmp); + $data{$type}{$name}{solcastapi}{'?All'}{$fwtg}{neff} = $curwcc if(defined $curwcc); + $data{$type}{$name}{solcastapi}{'?All'}{$fwtg}{ww} = $curwid if(defined $curwid); + $data{$type}{$name}{solcastapi}{'?All'}{$fwtg}{ttt} = $curtmp if(defined $curtmp); + + $data{$type}{$name}{weatherapi}{OpenMeteo}{$fwtg}{neff} = $curwcc if(defined $curwcc); + $data{$type}{$name}{weatherapi}{OpenMeteo}{$fwtg}{ww} = $curwid if(defined $curwid); + $data{$type}{$name}{weatherapi}{OpenMeteo}{$fwtg}{ttt} = $curtmp if(defined $curtmp); } $k++; @@ -4401,8 +4430,11 @@ sub __openMeteoDWD_ApiResponse { } if ($k == 0) { - $data{$type}{$name}{solcastapi}{'?All'}{sunrise}{today} = $sunrise; - $data{$type}{$name}{solcastapi}{'?All'}{sunset}{today} = $sunset; + $data{$type}{$name}{solcastapi}{'?All'}{sunrise}{today} = $sunrise; + $data{$type}{$name}{solcastapi}{'?All'}{sunset}{today} = $sunset; + + $data{$type}{$name}{weatherapi}{OpenMeteo}{sunrise}{today} = $sunrise; + $data{$type}{$name}{weatherapi}{OpenMeteo}{sunset}{today} = $sunset; if ($debug =~ /apiProcess/xs) { Log3 ($name, 1, "$name DEBUG> Open-Meteo DWD ICON API - Sunrise Today: $sunrise"); @@ -4411,8 +4443,11 @@ sub __openMeteoDWD_ApiResponse { } if ($k == 1) { - $data{$type}{$name}{solcastapi}{'?All'}{sunrise}{tomorrow} = $sunrise; - $data{$type}{$name}{solcastapi}{'?All'}{sunset}{tomorrow} = $sunset; + $data{$type}{$name}{solcastapi}{'?All'}{sunrise}{tomorrow} = $sunrise; + $data{$type}{$name}{solcastapi}{'?All'}{sunset}{tomorrow} = $sunset; + + $data{$type}{$name}{weatherapi}{OpenMeteo}{sunrise}{tomorrow} = $sunrise; + $data{$type}{$name}{weatherapi}{OpenMeteo}{sunset}{tomorrow} = $sunset; if ($debug =~ /apiProcess/xs) { Log3 ($name, 1, "$name DEBUG> Open-Meteo DWD ICON API - Sunrise Tomorrow: $sunrise"); @@ -4458,7 +4493,8 @@ sub ___setOpenMeteoAPIcallKeyData { my $hash = $defs{$name}; - $data{$type}{$name}{solcastapi}{'?All'}{'?All'}{todayDoneAPIrequests} += $cequ; + $data{$type}{$name}{solcastapi}{'?All'}{'?All'}{todayDoneAPIrequests} += $cequ; + $data{$type}{$name}{weatherapi}{OpenMeteo}{'?All'}{todayDoneAPIrequests} += $cequ; my $dar = SolCastAPIVal ($hash, '?All', '?All', 'todayDoneAPIrequests', 0); my $dac = SolCastAPIVal ($hash, '?All', '?All', 'todayDoneAPIcalls', 0); @@ -4467,8 +4503,11 @@ sub ___setOpenMeteoAPIcallKeyData { my $drr = $ometmaxreq - $dar; $drr = 0 if($drr < 0); - $data{$type}{$name}{solcastapi}{'?All'}{'?All'}{todayRemainingAPIrequests} = $drr; - $data{$type}{$name}{solcastapi}{'?All'}{'?All'}{currentAPIinterval} = $ometeorepdef; + $data{$type}{$name}{solcastapi}{'?All'}{'?All'}{todayRemainingAPIrequests} = $drr; + $data{$type}{$name}{solcastapi}{'?All'}{'?All'}{currentAPIinterval} = $ometeorepdef; + + $data{$type}{$name}{weatherapi}{OpenMeteo}{'?All'}{todayRemainingAPIrequests} = $drr; + $data{$type}{$name}{weatherapi}{OpenMeteo}{'?All'}{currentAPIinterval} = $ometeorepdef; ## Berechnung des optimalen Request Intervalls ################################################ @@ -4479,7 +4518,9 @@ sub ___setOpenMeteoAPIcallKeyData { if ($drr) { my $optrep = $rmdif / ($drr / ($cequ * $asc)); $optrep = $ometeorepdef if($optrep < $ometeorepdef); - $data{$type}{$name}{solcastapi}{'?All'}{'?All'}{currentAPIinterval} = $optrep; + + $data{$type}{$name}{solcastapi}{'?All'}{'?All'}{currentAPIinterval} = $optrep; + $data{$type}{$name}{weatherapi}{OpenMeteo}{'?All'}{currentAPIinterval} = $optrep; } debugLog ($paref, "apiProcess|apiCall", "Open-Meteo API Call - remaining API Requests: $drr, Request equivalents p. call: $cequ, new call interval: ".SolCastAPIVal ($hash, '?All', '?All', 'currentAPIinterval', $ometeorepdef)); @@ -4673,6 +4714,20 @@ sub _getlistSolCastData { return $ret; } +############################################################### +# Getter weatherApiData +############################################################### +sub _getlistWeatherApiData { + my $paref = shift; + my $name = $paref->{name}; + my $hash = $defs{$name}; + + my $ret = listDataPool ($hash, 'weatherApiData'); + $ret .= lineFromSpaces ($ret, 20); + +return $ret; +} + ############################################################### # Getter dwdCatalog ############################################################### @@ -6110,7 +6165,8 @@ sub _attrWeatherDev { ## no critic "not used" return $err if($err); } - InternalTimer (gettimeofday()+2, 'FHEM::SolarForecast::createAssociatedWith', $hash, 0); + InternalTimer (gettimeofday() + 1, 'FHEM::SolarForecast::setModel', $hash, 0); + InternalTimer (gettimeofday() + 2, 'FHEM::SolarForecast::createAssociatedWith', $hash, 0); return; } @@ -7758,34 +7814,49 @@ sub __delObsoleteAPIData { my $type = $paref->{type}; my $date = $paref->{date}; # aktuelles Datum my $hash = $defs{$name}; - - if (!keys %{$data{$type}{$name}{solcastapi}}) { - return; + + ## Wetter-API Daten löschen + ############################# + if (keys %{$data{$type}{$name}{weatherapi}}) { + if (isWeatherModelOpenMeteo ($hash)) { + for my $idx (keys %{$data{$type}{$name}{weatherapi}{OpenMeteo}}) { + delete $data{$type}{$name}{weatherapi}{OpenMeteo}{$idx} if($idx =~ /^fc?([0-9]{1,2})_?([0-9]{1,2})$/xs); + } + } + else { + delete $data{$type}{$name}{weatherapi}{OpenMeteo}; + } } + + ## Solar-API Daten löschen + ############################# + if (keys %{$data{$type}{$name}{solcastapi}}) { + my $refts = timestringToTimestamp ($date.' 00:00:00'); # Referenztimestring - my $refts = timestringToTimestamp ($date.' 00:00:00'); # Referenztimestring + for my $idx (sort keys %{$data{$type}{$name}{solcastapi}}) { # alle Datumschlüssel kleiner aktueller Tag 00:00:00 selektieren + for my $scd (sort keys %{$data{$type}{$name}{solcastapi}{$idx}}) { + my $ds = timestringToTimestamp ($scd); + delete $data{$type}{$name}{solcastapi}{$idx}{$scd} if($ds && $ds < $refts); + } + } - for my $idx (sort keys %{$data{$type}{$name}{solcastapi}}) { # alle Datumschlüssel kleiner aktueller Tag 00:00:00 selektieren - for my $scd (sort keys %{$data{$type}{$name}{solcastapi}{$idx}}) { - my $ds = timestringToTimestamp ($scd); - delete $data{$type}{$name}{solcastapi}{$idx}{$scd} if ($ds && $ds < $refts); + for my $idx (keys %{$data{$type}{$name}{solcastapi}{'?All'}}) { # Wetterindexe löschen (kann später raus) + delete $data{$type}{$name}{solcastapi}{'?All'}{$idx} if($idx =~ /^fc?([0-9]{1,2})_?([0-9]{1,2})$/xs); } } - for my $idx (keys %{$data{$type}{$name}{solcastapi}{'?All'}}) { # Wetterindexe löschen - delete $data{$type}{$name}{solcastapi}{'?All'}{$idx} if($idx =~ /^fc?([0-9]{1,2})_?([0-9]{1,2})$/xs); - } - + ## veraltete Strings aus Strings-Hash löschen + ################################################ my @as = split ",", AttrVal ($name, 'setupInverterStrings', ''); - return if(!scalar @as); + if (scalar @as) { + for my $k (keys %{$data{$type}{$name}{strings}}) { + next if($k =~ /\?All/); + next if(grep /^$k$/, @as); - for my $k (keys %{$data{$type}{$name}{strings}}) { # veraltete Strings aus Strings-Hash löschen - next if($k =~ /\?All/); - next if(grep /^$k$/, @as); + delete $data{$type}{$name}{strings}{$k}; - delete $data{$type}{$name}{strings}{$k}; - - Log3 ($name, 2, "$name - obsolete PV-String >$k< was deleted from Strings-Hash"); + Log3 ($name, 2, "$name - obsolete PV-String >$k< was deleted from Strings-Hash"); + } } return; @@ -11930,7 +12001,9 @@ sub _genStatisticReadings { } if ($def eq 'apimaxreq') { - $def = AttrVal ($name, 'ctrlSolCastAPImaxReq', $solcmaxreqdef); + $def = isSolCastUsed ($hash) ? (AttrVal ($name, 'ctrlSolCastAPImaxReq', $solcmaxreqdef)) : + isOpenMeteoUsed ($hash) ? $ometmaxreq : + 'n.a.'; } if ($hcsr{$kpi}{fnr} == 1) { @@ -16596,7 +16669,7 @@ sub listDataPool { my $git = sub { my $it = shift; - my @sorted = sort keys %$it; + my @sorted = sort { $a cmp $b } keys %$it; my $key = shift @sorted; my $ret = {}; @@ -16605,10 +16678,12 @@ sub listDataPool { return $ret; }; - if ($htol eq "solApiData") { + if ($htol =~ /solApiData|weatherApiData/xs) { $h = $data{$type}{$name}{solcastapi}; + $h = $data{$type}{$name}{weatherapi} if($htol eq 'weatherApiData'); + if (!keys %{$h}) { - return qq{SolCast API values cache is empty.}; + return qq{The API values cache is empty.}; } my $pve = q{}; @@ -17738,30 +17813,21 @@ return; ################################################################ sub setModel { my $hash = shift; + my $name = $hash->{NAME}; - my $api = AttrVal ($hash->{NAME}, 'setupRadiationAPI', 'DWD'); + my $radapi = AttrVal ($name, 'setupRadiationAPI', 'DWD'); + my $wthapi = AttrVal ($name, 'setupWeatherDev1', 'DWD'); - if ($api =~ /SolCast-/xs) { - $hash->{MODEL} = 'SolCastAPI'; - } - elsif ($api =~ /ForecastSolar-/xs) { - $hash->{MODEL} = 'ForecastSolarAPI'; - } - elsif ($api =~ /VictronKI-/xs) { - $hash->{MODEL} = 'VictronKiAPI'; - } - elsif ($api =~ /OpenMeteoDWDEnsemble-/xs) { - $hash->{MODEL} = 'OpenMeteoDWDEnsembleAPI'; - } - elsif ($api =~ /OpenMeteoDWD-/xs) { - $hash->{MODEL} = 'OpenMeteoDWDAPI'; - } - elsif ($api =~ /OpenMeteoWorld-/xs) { - $hash->{MODEL} = 'OpenMeteoWorldAPI'; - } - else { - $hash->{MODEL} = 'DWD'; - } + if ($radapi =~ /SolCast-/xs) { $hash->{MODEL} = 'SolCastAPI'; } + elsif ($radapi =~ /ForecastSolar-/xs) { $hash->{MODEL} = 'ForecastSolarAPI'; } + elsif ($radapi =~ /VictronKI-/xs) { $hash->{MODEL} = 'VictronKiAPI'; } + elsif ($radapi =~ /OpenMeteoDWDEnsemble-/xs) { $hash->{MODEL} = 'OpenMeteoDWDEnsembleAPI'; } + elsif ($radapi =~ /OpenMeteoDWD-/xs) { $hash->{MODEL} = 'OpenMeteoDWDAPI'; } + elsif ($radapi =~ /OpenMeteoWorld-/xs) { $hash->{MODEL} = 'OpenMeteoWorldAPI'; } + else { $hash->{MODEL} = 'DWD'; } + + if ($wthapi =~ /OpenMeteo.*-API/xs) { $hash->{WEATHERMODEL} = 'OpenMeteo'; } + else { $hash->{WEATHERMODEL} = 'DWD'; } return; } @@ -18307,6 +18373,22 @@ sub isOpenMeteoUsed { return $ret; } +################################################################ +# Prüfung auf Verwendung von Open-Meteo API als +# Lieferant für Wetterdaten +################################################################ +sub isWeatherModelOpenMeteo { + my $hash = shift; + + my $ret = 0; + + if (InternalVal ($hash->{NAME}, 'WEATHERMODEL', '') =~ /^OpenMeteo/xs) { + $ret = 1; + } + +return $ret; +} + ################################################################ # welche PV Autokorrektur wird verwendet ? # Standard bei nur "on" -> on_simple @@ -19625,11 +19707,51 @@ sub SolCastAPIVal { my $name = $hash->{NAME}; my $type = $hash->{TYPE}; - if(defined $data{$type}{$name}{solcastapi} && - defined $data{$type}{$name}{solcastapi}{$string} && - defined $data{$type}{$name}{solcastapi}{$string}{$ststr} && - defined $data{$type}{$name}{solcastapi}{$string}{$ststr}{$key}) { - return $data{$type}{$name}{solcastapi}{$string}{$ststr}{$key}; + if (defined $data{$type}{$name}{solcastapi} && + defined $data{$type}{$name}{solcastapi}{$string} && + defined $data{$type}{$name}{solcastapi}{$string}{$ststr} && + defined $data{$type}{$name}{solcastapi}{$string}{$ststr}{$key}) { + return $data{$type}{$name}{solcastapi}{$string}{$ststr}{$key}; + } + +return $def; +} + +########################################################################################################################################################## +# Wert des weatherAPI-Hash zurückliefern +# Usage: +# WeatherAPIVal ($hash, $apiname, $tstr, $key, $def) +# +# $apiname: Hauptname der API gemäß setupWeatherDevX (z.B. OpenMeteo) +# $tstr: Zeitwert der Form fcX_XX (z.B. fc1_19) +# $key: Parameter (z.B. don, neff, rr1c, ...) +# $def: Defaultwert +# +# Sonderabfragen +# WeatherAPIVal ($hash, $apiname, '?All', 'lastretrieval_time', $def) - letzte Abfrage Zeitstring +# WeatherAPIVal ($hash, $apiname, '?All', 'lastretrieval_timestamp', $def) - letzte Abfrage Unix Timestamp +# WeatherAPIVal ($hash, $apiname, '?All', 'todayDoneAPIrequests', $def) - heute ausgeführte API Requests +# WeatherAPIVal ($hash, $apiname, '?All', 'todayDoneAPIcalls', $def) - heute ausgeführte API Calls (hat u.U. mehrere Requests) +# WeatherAPIVal ($hash, $apiname, '?All', 'todayRemainingAPIrequests $def) - heute verbleibende API Requests +# WeatherAPIVal ($hash, $apiname, '?All', 'todayRemainingAPIcalls', $def) - heute noch mögliche API Calls (ungl. Requests !) +# WeatherAPIVal ($hash, $apiname, '?All', 'currentAPIinterval', $def) - aktuelles API Request Intervall +# WeatherAPIVal ($hash, $apiname, '?All', 'response_message', $def) - letzte API Antwort +########################################################################################################################################################## +sub WeatherAPIVal { + my $hash = shift; + my $apiname = shift; + my $tstr = shift; + my $key = shift; + my $def = shift; + + my $name = $hash->{NAME}; + my $type = $hash->{TYPE}; + + if (defined $data{$type}{$name}{weatherapi} && + defined $data{$type}{$name}{weatherapi}{$apiname} && + defined $data{$type}{$name}{weatherapi}{$apiname}{$tstr} && + defined $data{$type}{$name}{weatherapi}{$apiname}{$tstr}{$key}) { + return $data{$type}{$name}{weatherapi}{$apiname}{$tstr}{$key}; } return $def; @@ -20470,7 +20592,8 @@ to ensure that the system configuration is correct.
@@ -20598,6 +20721,14 @@ to ensure that the system configuration is correct.
+ + +

@@ -21105,30 +21236,30 @@ to ensure that the system configuration is correct. allStringsFullfilled Fulfillment status of error-free generation of all strings conForecastTillNextSunrise Consumption forecast from current hour to the coming sunrise - currentAPIinterval the current call interval of the SolCast API (only model SolCastAPI) in seconds + currentAPIinterval the current polling interval of the selected radiation data API in seconds currentRunMtsConsumer_XX the running time (minutes) of the consumer "XX" since the last switch-on. (last running cycle) dayAfterTomorrowPVforecast provides the forecast of PV generation for the day after tomorrow (if available) without autocorrection (raw data) daysUntilBatteryCare Days until the next battery maintenance (reaching the charge 'maxSoC' from attribute ctrlBatSocManagement) - lastretrieval_time the last call time of the API (only Model SolCastAPI, ForecastSolarAPI) - lastretrieval_timestamp the timestamp of the last call time of the API (only Model SolCastAPI, ForecastSolarAPI) - response_message the last status message of the API (only Model SolCastAPI, ForecastSolarAPI) + lastretrieval_time the last retrieval time of the selected radiation data API + lastretrieval_timestamp the timestamp of the last retrieval time of the selected radiation data API + response_message the last status message of the selected radiation data API runTimeAvgDayConsumer_XX the average running time (minutes) of consumer "XX" on one day runTimeCentralTask the runtime of the last SolarForecast interval (total process) in seconds runTimeTrainAI the runtime of the last AI training cycle in seconds - runTimeLastAPIAnswer the last response time of the API call to a request in seconds (only model SolCastAPI, ForecastSolarAPI) - runTimeLastAPIProc the last process time for processing the received API data (only model SolCastAPI, ForecastSolarAPI) + runTimeLastAPIAnswer the last response time of the radiation data API retrieval to a request in seconds + runTimeLastAPIProc the last process time for processing the received radiation data API data SunMinutes_Remain the remaining minutes until sunset of the current day SunHours_Remain the remaining hours until sunset of the current day todayConsumptionForecast Consumption forecast per hour of the current day (01-24) todayConForecastTillSunset Consumption forecast from current hour to hour before sunset - todayDoneAPIcalls the number of API calls executed on the current day (only model SolCastAPI, ForecastSolarAPI) - todayDoneAPIrequests the number of API requests executed on the current day (only model SolCastAPI, ForecastSolarAPI) + todayDoneAPIcalls the number of radiation data API calls executed on the current day + todayDoneAPIrequests the number of radiation data API requests executed on the current day todayGridConsumption the energy drawn from the public grid on the current day todayGridFeedIn PV energy fed into the public grid on the current day - todayMaxAPIcalls the maximum possible number of SolCast API calls (SolCastAPI model only). + todayMaxAPIcalls the maximum possible number of radiation data API calls. A call can contain multiple API requests. - todayRemainingAPIcalls the number of SolCast API calls still possible on the current day (only model SolCastAPI) - todayRemainingAPIrequests the number of SolCast API requests still possible on the current day (only model SolCastAPI) + todayRemainingAPIcalls the number of radiation data API calls still possible on the current day + todayRemainingAPIrequests the number of radiation data API requests still possible on the current day todayBatIn the energy charged into the battery on the current day todayBatOut the energy taken from the battery on the current day @@ -22839,7 +22970,8 @@ die ordnungsgemäße Anlagenkonfiguration geprüft werden.
@@ -22957,12 +23089,19 @@ die ordnungsgemäße Anlagenkonfiguration geprüft werden.
- + +
+ +
@@ -23475,30 +23614,30 @@ die ordnungsgemäße Anlagenkonfiguration geprüft werden. allStringsFullfilled Erfüllungsstatus der fehlerfreien Generierung aller Strings conForecastTillNextSunrise Verbrauchsprognose von aktueller Stunde bis zum kommenden Sonnenaufgang - currentAPIinterval das aktuelle Abrufintervall der SolCast API (nur Model SolCastAPI) in Sekunden + currentAPIinterval das aktuelle Abrufintervall der gewählten Strahlungsdaten-API in Sekunden currentRunMtsConsumer_XX die Laufzeit (Minuten) des Verbrauchers "XX" seit dem letzten Einschalten. (letzter Laufzyklus) dayAfterTomorrowPVforecast liefert die Vorhersage der PV Erzeugung für Übermorgen (sofern verfügbar) ohne Autokorrektur (Rohdaten). daysUntilBatteryCare Tage bis zur nächsten Batteriepflege (Erreichen der Ladung 'maxSoC' aus Attribut ctrlBatSocManagement) - lastretrieval_time der letzte Abrufzeitpunkt der API (nur Model SolCastAPI, ForecastSolarAPI) - lastretrieval_timestamp der Timestamp der letzen Abrufzeitpunkt der API (nur Model SolCastAPI, ForecastSolarAPI) - response_message die letzte Statusmeldung der API (nur Model SolCastAPI, ForecastSolarAPI) + lastretrieval_time der letzte Abrufzeitpunkt der gewählten Strahlungsdaten-API + lastretrieval_timestamp der Timestamp der letzen Abrufzeitpunkt der gewählten Strahlungsdaten-API + response_message die letzte Statusmeldung der gewählten Strahlungsdaten-API runTimeAvgDayConsumer_XX die durchschnittliche Laufzeit (Minuten) des Verbrauchers "XX" an einem Tag runTimeCentralTask die Laufzeit des letzten SolarForecast Intervalls (Gesamtprozess) in Sekunden runTimeTrainAI die Laufzeit des letzten KI Trainingszyklus in Sekunden - runTimeLastAPIAnswer die letzte Antwortzeit des API Abrufs auf einen Request in Sekunden (nur Model SolCastAPI, ForecastSolarAPI) - runTimeLastAPIProc die letzte Prozesszeit zur Verarbeitung der empfangenen API Daten (nur Model SolCastAPI, ForecastSolarAPI) + runTimeLastAPIAnswer die letzte Antwortzeit des Strahlungsdaten-API Abrufs auf einen Request in Sekunden + runTimeLastAPIProc die letzte Prozesszeit zur Verarbeitung der empfangenen Strahlungsdaten-API Daten SunMinutes_Remain die verbleibenden Minuten bis Sonnenuntergang des aktuellen Tages SunHours_Remain die verbleibenden Stunden bis Sonnenuntergang des aktuellen Tages todayConsumptionForecast Verbrauchsprognose pro Stunde des aktuellen Tages (01-24) todayConForecastTillSunset Verbrauchsprognose von aktueller Stunde bis Stunde vor Sonnenuntergang - todayDoneAPIcalls die Anzahl der am aktuellen Tag ausgeführten API Calls (nur Model SolCastAPI, ForecastSolarAPI) - todayDoneAPIrequests die Anzahl der am aktuellen Tag ausgeführten API Requests (nur Model SolCastAPI, ForecastSolarAPI) + todayDoneAPIcalls die Anzahl der am aktuellen Tag ausgeführten Strahlungsdaten-API Calls + todayDoneAPIrequests die Anzahl der am aktuellen Tag ausgeführten Strahlungsdaten-API Requests todayGridConsumption die aus dem öffentlichen Netz bezogene Energie am aktuellen Tag todayGridFeedIn die in das öffentliche Netz eingespeiste PV Energie am aktuellen Tag - todayMaxAPIcalls die maximal mögliche Anzahl SolCast API Calls (nur Model SolCastAPI). + todayMaxAPIcalls die maximal mögliche Anzahl Strahlungsdaten-API Calls. Ein Call kann mehrere API Requests enthalten. - todayRemainingAPIcalls die Anzahl der am aktuellen Tag noch möglichen SolCast API Calls (nur Model SolCastAPI) - todayRemainingAPIrequests die Anzahl der am aktuellen Tag noch möglichen SolCast API Requests (nur Model SolCastAPI) + todayRemainingAPIcalls die Anzahl der am aktuellen Tag noch möglichen Strahlungsdaten-API Calls + todayRemainingAPIrequests die Anzahl der am aktuellen Tag noch möglichen Strahlungsdaten-API Requests todayBatIn die am aktuellen Tag in die Batterie geladene Energie todayBatOut die am aktuellen Tag aus der Batterie entnommene Energie diff --git a/fhem/contrib/DS_Starter/76_SolarForecast.pm b/fhem/contrib/DS_Starter/76_SolarForecast.pm index 98cbdf17d..e71d1e1be 100644 --- a/fhem/contrib/DS_Starter/76_SolarForecast.pm +++ b/fhem/contrib/DS_Starter/76_SolarForecast.pm @@ -156,8 +156,9 @@ BEGIN { # Versions History intern my %vNotesIntern = ( - "1.37.8" => "27.11.2024 edit commref, func _searchCacheFiles for renaming Cache files when device is renamed ". - "_saveEnergyConsumption: extended for Debug collectData ", + "1.37.8" => "28.11.2024 edit commref, func _searchCacheFiles for renaming Cache files when device is renamed ". + "_saveEnergyConsumption: extended for Debug collectData, preparation of weatherApiData ". + "new func WeatherAPIVal ", "1.37.7" => "26.11.2024 Attr flowGraphicControl: key shift changed to shiftx, new key shifty ". "change: 'trackFlex' && \$wcc >= 70 to \$wcc >= 80 ". "obsolete Attr deleted: flowGraphicCss, flowGraphicSize, flowGraphicAnimate, flowGraphicConsumerDistance, ". @@ -598,6 +599,7 @@ my %hget = ( # Ha nextHours => { fn => \&_getlistNextHours, needcred => 0 }, rooftopData => { fn => \&_getRoofTopData, needcred => 0 }, solApiData => { fn => \&_getlistSolCastData, needcred => 0 }, + weatherApiData => { fn => \&_getlistWeatherApiData, needcred => 0 }, valDecTree => { fn => \&_getaiDecTree, needcred => 0 }, ftuiFramefiles => { fn => \&_ftuiFramefiles, needcred => 0 }, dwdCatalog => { fn => \&_getdwdCatalog, needcred => 0 }, @@ -1159,7 +1161,8 @@ my %hfspvh = ( # $data{$type}{$name}{inverters} # Inverter Hash # $data{$type}{$name}{producers} # non-PV Producer Hash # $data{$type}{$name}{strings} # Stringkonfiguration Hash -# $data{$type}{$name}{solcastapi} # Zwischenspeicher API-Daten +# $data{$type}{$name}{solcastapi} # Zwischenspeicher API-Solardaten +# $data{$type}{$name}{weatherapi} # Zwischenspeicher API-Wetterdaten # $data{$type}{$name}{aidectree}{object} # AI Decision Tree Object # $data{$type}{$name}{aidectree}{aitrained} # AI Decision Tree trainierte Daten # $data{$type}{$name}{aidectree}{airaw} # Rohdaten für AI Input = Raw Trainigsdaten @@ -2472,7 +2475,8 @@ sub Get { "pvHistory:#,exportToCsv,$pvl ". "rooftopData:noArg ". "solApiData:noArg ". - "valCurrent:noArg " + "valCurrent:noArg ". + "weatherApiData:noArg " ; ## KI spezifische Getter @@ -2800,7 +2804,7 @@ sub __solCast_ApiResponse { my $apimaxreq = AttrVal ($name, 'ctrlSolCastAPImaxReq', $solcmaxreqdef); Log3 ($name, 1, "$name DEBUG> SolCast API Call - response status: ".$jdata->{'response_status'}{'message'}); - Log3 ($name, 1, "$name DEBUG> SolCast API Call - todayRemainingAPIrequests: ".SolCastAPIVal($hash, '?All', '?All', 'todayRemainingAPIrequests', $apimaxreq)); + Log3 ($name, 1, "$name DEBUG> SolCast API Call - todayRemainingAPIrequests: ".SolCastAPIVal ($hash, '?All', '?All', 'todayRemainingAPIrequests', $apimaxreq)); } return; @@ -4117,7 +4121,10 @@ sub __openMeteoDWD_ApiRequest { if (!$allstrings) { # alle Strings wurden abgerufen my $apiitv = SolCastAPIVal ($hash, '?All', '?All', 'currentAPIinterval', $ometeorepdef); readingsSingleUpdate ($hash, 'nextRadiationAPICall', $hqtxt{after}{$lang}.' '.(timestampToTimestring ($t + $apiitv, $lang))[0], 1); - $data{$type}{$name}{solcastapi}{'?All'}{'?All'}{todayDoneAPIcalls} += 1; + + $data{$type}{$name}{solcastapi}{'?All'}{'?All'}{todayDoneAPIcalls} += 1; + $data{$type}{$name}{weatherapi}{OpenMeteo}{'?All'}{todayDoneAPIcalls} += 1; + return; } @@ -4228,7 +4235,8 @@ sub __openMeteoDWD_ApiResponse { Log3 ($name, 1, "$name - $msg"); - $data{$type}{$name}{solcastapi}{'?All'}{'?All'}{response_message} = $err; + $data{$type}{$name}{solcastapi}{'?All'}{'?All'}{response_message} = $err; + $data{$type}{$name}{weatherapi}{OpenMeteo}{'?All'}{response_message} = $err; singleUpdateState ( {hash => $hash, state => $msg, evt => 1} ); $data{$type}{$name}{current}{runTimeLastAPIProc} = sprintf "%.4f", tv_interval($sta); # Verarbeitungszeit ermitteln @@ -4254,9 +4262,11 @@ sub __openMeteoDWD_ApiResponse { my $rt = (timestampToTimestring ($t, $lang))[3]; my $jdata = decode_json ($myjson); - $data{$type}{$name}{solcastapi}{'?All'}{'?All'}{lastretrieval_time} = $rt; # letzte Abrufzeit - $data{$type}{$name}{solcastapi}{'?All'}{'?All'}{lastretrieval_timestamp} = $t; # letzter Abrufzeitstempel - + $data{$type}{$name}{solcastapi}{'?All'}{'?All'}{lastretrieval_time} = $rt; # letzte Abrufzeit + $data{$type}{$name}{solcastapi}{'?All'}{'?All'}{lastretrieval_timestamp} = $t; # letzter Abrufzeitstempel + $data{$type}{$name}{weatherapi}{OpenMeteo}{'?All'}{lastretrieval_time} = $rt; + $data{$type}{$name}{weatherapi}{OpenMeteo}{'?All'}{lastretrieval_timestamp} = $t; + ## bei Fehler in API intern kommt ################################### # error: true @@ -4269,14 +4279,17 @@ sub __openMeteoDWD_ApiResponse { singleUpdateState ( {hash => $hash, state => $msg, evt => 1} ); - $data{$type}{$name}{solcastapi}{'?All'}{'?All'}{response_message} = $jdata->{'reason'}; - $data{$type}{$name}{current}{runTimeLastAPIProc} = sprintf "%.4f", tv_interval($sta); # Verarbeitungszeit ermitteln - $data{$type}{$name}{current}{runTimeLastAPIAnswer} = sprintf "%.4f", (tv_interval($stc) - tv_interval($sta)); # API Laufzeit ermitteln + $data{$type}{$name}{solcastapi}{'?All'}{'?All'}{response_message} = $jdata->{'reason'}; + $data{$type}{$name}{weatherapi}{OpenMeteo}{'?All'}{response_message} = $jdata->{'reason'}; + + $data{$type}{$name}{current}{runTimeLastAPIProc} = sprintf "%.4f", tv_interval($sta); # Verarbeitungszeit ermitteln + $data{$type}{$name}{current}{runTimeLastAPIAnswer} = sprintf "%.4f", (tv_interval($stc) - tv_interval($sta)); # API Laufzeit ermitteln return; } - $data{$type}{$name}{solcastapi}{'?All'}{'?All'}{response_message} = 'success'; + $data{$type}{$name}{solcastapi}{'?All'}{'?All'}{response_message} = 'success'; + $data{$type}{$name}{weatherapi}{OpenMeteo}{'?All'}{response_message} = 'success'; if ($debug =~ /apiCall/xs) { Log3 ($name, 1, qq{$name DEBUG> Open-Meteo API Call - server response for PV string "$string"}); @@ -4364,20 +4377,36 @@ sub __openMeteoDWD_ApiResponse { } $data{$type}{$name}{solcastapi}{'?All'}{$pvtmstr}{Rad1h} += $rad; # Startstunde verschieben, Rad Werte aller Strings addieren - $data{$type}{$name}{solcastapi}{'?All'}{$fwtg}{rr1c} = $rain; # Startstunde verschieben - + + ## Wetterdaten + ################ + $data{$type}{$name}{solcastapi}{'?All'}{$fwtg}{rr1c} = $rain; # Startstunde verschieben + $data{$type}{$name}{weatherapi}{OpenMeteo}{$fwtg}{rr1c} = $rain; + $data{$type}{$name}{weatherapi}{OpenMeteo}{$fwtg}{StartTime} = $pvtmstr; + $fwtg = formatWeatherTimestrg ($otmstr); - $data{$type}{$name}{solcastapi}{'?All'}{$fwtg}{don} = $don; - $data{$type}{$name}{solcastapi}{'?All'}{$fwtg}{neff} = $wcc; - $data{$type}{$name}{solcastapi}{'?All'}{$fwtg}{ww} = $wid; - $data{$type}{$name}{solcastapi}{'?All'}{$fwtg}{ttt} = $temp; - $data{$type}{$name}{solcastapi}{'?All'}{$fwtg}{UpdateTime} = $rt; + $data{$type}{$name}{solcastapi}{'?All'}{$fwtg}{don} = $don; + $data{$type}{$name}{solcastapi}{'?All'}{$fwtg}{neff} = $wcc; + $data{$type}{$name}{solcastapi}{'?All'}{$fwtg}{ww} = $wid; + $data{$type}{$name}{solcastapi}{'?All'}{$fwtg}{ttt} = $temp; + $data{$type}{$name}{solcastapi}{'?All'}{$fwtg}{UpdateTime} = $rt; + + $data{$type}{$name}{weatherapi}{OpenMeteo}{$fwtg}{don} = $don; + $data{$type}{$name}{weatherapi}{OpenMeteo}{$fwtg}{neff} = $wcc; + $data{$type}{$name}{weatherapi}{OpenMeteo}{$fwtg}{ww} = $wid; + $data{$type}{$name}{weatherapi}{OpenMeteo}{$fwtg}{ttt} = $temp; + $data{$type}{$name}{weatherapi}{OpenMeteo}{$fwtg}{UpdateTime} = $rt; + $data{$type}{$name}{weatherapi}{OpenMeteo}{$fwtg}{StartTime} = $otmstr; if ($k == 0) { - $data{$type}{$name}{solcastapi}{'?All'}{$fwtg}{neff} = $curwcc if(defined $curwcc); - $data{$type}{$name}{solcastapi}{'?All'}{$fwtg}{ww} = $curwid if(defined $curwid); - $data{$type}{$name}{solcastapi}{'?All'}{$fwtg}{ttt} = $curtmp if(defined $curtmp); + $data{$type}{$name}{solcastapi}{'?All'}{$fwtg}{neff} = $curwcc if(defined $curwcc); + $data{$type}{$name}{solcastapi}{'?All'}{$fwtg}{ww} = $curwid if(defined $curwid); + $data{$type}{$name}{solcastapi}{'?All'}{$fwtg}{ttt} = $curtmp if(defined $curtmp); + + $data{$type}{$name}{weatherapi}{OpenMeteo}{$fwtg}{neff} = $curwcc if(defined $curwcc); + $data{$type}{$name}{weatherapi}{OpenMeteo}{$fwtg}{ww} = $curwid if(defined $curwid); + $data{$type}{$name}{weatherapi}{OpenMeteo}{$fwtg}{ttt} = $curtmp if(defined $curtmp); } $k++; @@ -4401,8 +4430,11 @@ sub __openMeteoDWD_ApiResponse { } if ($k == 0) { - $data{$type}{$name}{solcastapi}{'?All'}{sunrise}{today} = $sunrise; - $data{$type}{$name}{solcastapi}{'?All'}{sunset}{today} = $sunset; + $data{$type}{$name}{solcastapi}{'?All'}{sunrise}{today} = $sunrise; + $data{$type}{$name}{solcastapi}{'?All'}{sunset}{today} = $sunset; + + $data{$type}{$name}{weatherapi}{OpenMeteo}{sunrise}{today} = $sunrise; + $data{$type}{$name}{weatherapi}{OpenMeteo}{sunset}{today} = $sunset; if ($debug =~ /apiProcess/xs) { Log3 ($name, 1, "$name DEBUG> Open-Meteo DWD ICON API - Sunrise Today: $sunrise"); @@ -4411,8 +4443,11 @@ sub __openMeteoDWD_ApiResponse { } if ($k == 1) { - $data{$type}{$name}{solcastapi}{'?All'}{sunrise}{tomorrow} = $sunrise; - $data{$type}{$name}{solcastapi}{'?All'}{sunset}{tomorrow} = $sunset; + $data{$type}{$name}{solcastapi}{'?All'}{sunrise}{tomorrow} = $sunrise; + $data{$type}{$name}{solcastapi}{'?All'}{sunset}{tomorrow} = $sunset; + + $data{$type}{$name}{weatherapi}{OpenMeteo}{sunrise}{tomorrow} = $sunrise; + $data{$type}{$name}{weatherapi}{OpenMeteo}{sunset}{tomorrow} = $sunset; if ($debug =~ /apiProcess/xs) { Log3 ($name, 1, "$name DEBUG> Open-Meteo DWD ICON API - Sunrise Tomorrow: $sunrise"); @@ -4458,7 +4493,8 @@ sub ___setOpenMeteoAPIcallKeyData { my $hash = $defs{$name}; - $data{$type}{$name}{solcastapi}{'?All'}{'?All'}{todayDoneAPIrequests} += $cequ; + $data{$type}{$name}{solcastapi}{'?All'}{'?All'}{todayDoneAPIrequests} += $cequ; + $data{$type}{$name}{weatherapi}{OpenMeteo}{'?All'}{todayDoneAPIrequests} += $cequ; my $dar = SolCastAPIVal ($hash, '?All', '?All', 'todayDoneAPIrequests', 0); my $dac = SolCastAPIVal ($hash, '?All', '?All', 'todayDoneAPIcalls', 0); @@ -4467,8 +4503,11 @@ sub ___setOpenMeteoAPIcallKeyData { my $drr = $ometmaxreq - $dar; $drr = 0 if($drr < 0); - $data{$type}{$name}{solcastapi}{'?All'}{'?All'}{todayRemainingAPIrequests} = $drr; - $data{$type}{$name}{solcastapi}{'?All'}{'?All'}{currentAPIinterval} = $ometeorepdef; + $data{$type}{$name}{solcastapi}{'?All'}{'?All'}{todayRemainingAPIrequests} = $drr; + $data{$type}{$name}{solcastapi}{'?All'}{'?All'}{currentAPIinterval} = $ometeorepdef; + + $data{$type}{$name}{weatherapi}{OpenMeteo}{'?All'}{todayRemainingAPIrequests} = $drr; + $data{$type}{$name}{weatherapi}{OpenMeteo}{'?All'}{currentAPIinterval} = $ometeorepdef; ## Berechnung des optimalen Request Intervalls ################################################ @@ -4479,7 +4518,9 @@ sub ___setOpenMeteoAPIcallKeyData { if ($drr) { my $optrep = $rmdif / ($drr / ($cequ * $asc)); $optrep = $ometeorepdef if($optrep < $ometeorepdef); - $data{$type}{$name}{solcastapi}{'?All'}{'?All'}{currentAPIinterval} = $optrep; + + $data{$type}{$name}{solcastapi}{'?All'}{'?All'}{currentAPIinterval} = $optrep; + $data{$type}{$name}{weatherapi}{OpenMeteo}{'?All'}{currentAPIinterval} = $optrep; } debugLog ($paref, "apiProcess|apiCall", "Open-Meteo API Call - remaining API Requests: $drr, Request equivalents p. call: $cequ, new call interval: ".SolCastAPIVal ($hash, '?All', '?All', 'currentAPIinterval', $ometeorepdef)); @@ -4673,6 +4714,20 @@ sub _getlistSolCastData { return $ret; } +############################################################### +# Getter weatherApiData +############################################################### +sub _getlistWeatherApiData { + my $paref = shift; + my $name = $paref->{name}; + my $hash = $defs{$name}; + + my $ret = listDataPool ($hash, 'weatherApiData'); + $ret .= lineFromSpaces ($ret, 20); + +return $ret; +} + ############################################################### # Getter dwdCatalog ############################################################### @@ -6110,7 +6165,8 @@ sub _attrWeatherDev { ## no critic "not used" return $err if($err); } - InternalTimer (gettimeofday()+2, 'FHEM::SolarForecast::createAssociatedWith', $hash, 0); + InternalTimer (gettimeofday() + 1, 'FHEM::SolarForecast::setModel', $hash, 0); + InternalTimer (gettimeofday() + 2, 'FHEM::SolarForecast::createAssociatedWith', $hash, 0); return; } @@ -7758,34 +7814,49 @@ sub __delObsoleteAPIData { my $type = $paref->{type}; my $date = $paref->{date}; # aktuelles Datum my $hash = $defs{$name}; - - if (!keys %{$data{$type}{$name}{solcastapi}}) { - return; + + ## Wetter-API Daten löschen + ############################# + if (keys %{$data{$type}{$name}{weatherapi}}) { + if (isWeatherModelOpenMeteo ($hash)) { + for my $idx (keys %{$data{$type}{$name}{weatherapi}{OpenMeteo}}) { + delete $data{$type}{$name}{weatherapi}{OpenMeteo}{$idx} if($idx =~ /^fc?([0-9]{1,2})_?([0-9]{1,2})$/xs); + } + } + else { + delete $data{$type}{$name}{weatherapi}{OpenMeteo}; + } } + + ## Solar-API Daten löschen + ############################# + if (keys %{$data{$type}{$name}{solcastapi}}) { + my $refts = timestringToTimestamp ($date.' 00:00:00'); # Referenztimestring - my $refts = timestringToTimestamp ($date.' 00:00:00'); # Referenztimestring + for my $idx (sort keys %{$data{$type}{$name}{solcastapi}}) { # alle Datumschlüssel kleiner aktueller Tag 00:00:00 selektieren + for my $scd (sort keys %{$data{$type}{$name}{solcastapi}{$idx}}) { + my $ds = timestringToTimestamp ($scd); + delete $data{$type}{$name}{solcastapi}{$idx}{$scd} if($ds && $ds < $refts); + } + } - for my $idx (sort keys %{$data{$type}{$name}{solcastapi}}) { # alle Datumschlüssel kleiner aktueller Tag 00:00:00 selektieren - for my $scd (sort keys %{$data{$type}{$name}{solcastapi}{$idx}}) { - my $ds = timestringToTimestamp ($scd); - delete $data{$type}{$name}{solcastapi}{$idx}{$scd} if ($ds && $ds < $refts); + for my $idx (keys %{$data{$type}{$name}{solcastapi}{'?All'}}) { # Wetterindexe löschen (kann später raus) + delete $data{$type}{$name}{solcastapi}{'?All'}{$idx} if($idx =~ /^fc?([0-9]{1,2})_?([0-9]{1,2})$/xs); } } - for my $idx (keys %{$data{$type}{$name}{solcastapi}{'?All'}}) { # Wetterindexe löschen - delete $data{$type}{$name}{solcastapi}{'?All'}{$idx} if($idx =~ /^fc?([0-9]{1,2})_?([0-9]{1,2})$/xs); - } - + ## veraltete Strings aus Strings-Hash löschen + ################################################ my @as = split ",", AttrVal ($name, 'setupInverterStrings', ''); - return if(!scalar @as); + if (scalar @as) { + for my $k (keys %{$data{$type}{$name}{strings}}) { + next if($k =~ /\?All/); + next if(grep /^$k$/, @as); - for my $k (keys %{$data{$type}{$name}{strings}}) { # veraltete Strings aus Strings-Hash löschen - next if($k =~ /\?All/); - next if(grep /^$k$/, @as); + delete $data{$type}{$name}{strings}{$k}; - delete $data{$type}{$name}{strings}{$k}; - - Log3 ($name, 2, "$name - obsolete PV-String >$k< was deleted from Strings-Hash"); + Log3 ($name, 2, "$name - obsolete PV-String >$k< was deleted from Strings-Hash"); + } } return; @@ -11930,7 +12001,9 @@ sub _genStatisticReadings { } if ($def eq 'apimaxreq') { - $def = AttrVal ($name, 'ctrlSolCastAPImaxReq', $solcmaxreqdef); + $def = isSolCastUsed ($hash) ? (AttrVal ($name, 'ctrlSolCastAPImaxReq', $solcmaxreqdef)) : + isOpenMeteoUsed ($hash) ? $ometmaxreq : + 'n.a.'; } if ($hcsr{$kpi}{fnr} == 1) { @@ -16596,7 +16669,7 @@ sub listDataPool { my $git = sub { my $it = shift; - my @sorted = sort keys %$it; + my @sorted = sort { $a cmp $b } keys %$it; my $key = shift @sorted; my $ret = {}; @@ -16605,10 +16678,12 @@ sub listDataPool { return $ret; }; - if ($htol eq "solApiData") { + if ($htol =~ /solApiData|weatherApiData/xs) { $h = $data{$type}{$name}{solcastapi}; + $h = $data{$type}{$name}{weatherapi} if($htol eq 'weatherApiData'); + if (!keys %{$h}) { - return qq{SolCast API values cache is empty.}; + return qq{The API values cache is empty.}; } my $pve = q{}; @@ -17738,30 +17813,21 @@ return; ################################################################ sub setModel { my $hash = shift; + my $name = $hash->{NAME}; - my $api = AttrVal ($hash->{NAME}, 'setupRadiationAPI', 'DWD'); + my $radapi = AttrVal ($name, 'setupRadiationAPI', 'DWD'); + my $wthapi = AttrVal ($name, 'setupWeatherDev1', 'DWD'); - if ($api =~ /SolCast-/xs) { - $hash->{MODEL} = 'SolCastAPI'; - } - elsif ($api =~ /ForecastSolar-/xs) { - $hash->{MODEL} = 'ForecastSolarAPI'; - } - elsif ($api =~ /VictronKI-/xs) { - $hash->{MODEL} = 'VictronKiAPI'; - } - elsif ($api =~ /OpenMeteoDWDEnsemble-/xs) { - $hash->{MODEL} = 'OpenMeteoDWDEnsembleAPI'; - } - elsif ($api =~ /OpenMeteoDWD-/xs) { - $hash->{MODEL} = 'OpenMeteoDWDAPI'; - } - elsif ($api =~ /OpenMeteoWorld-/xs) { - $hash->{MODEL} = 'OpenMeteoWorldAPI'; - } - else { - $hash->{MODEL} = 'DWD'; - } + if ($radapi =~ /SolCast-/xs) { $hash->{MODEL} = 'SolCastAPI'; } + elsif ($radapi =~ /ForecastSolar-/xs) { $hash->{MODEL} = 'ForecastSolarAPI'; } + elsif ($radapi =~ /VictronKI-/xs) { $hash->{MODEL} = 'VictronKiAPI'; } + elsif ($radapi =~ /OpenMeteoDWDEnsemble-/xs) { $hash->{MODEL} = 'OpenMeteoDWDEnsembleAPI'; } + elsif ($radapi =~ /OpenMeteoDWD-/xs) { $hash->{MODEL} = 'OpenMeteoDWDAPI'; } + elsif ($radapi =~ /OpenMeteoWorld-/xs) { $hash->{MODEL} = 'OpenMeteoWorldAPI'; } + else { $hash->{MODEL} = 'DWD'; } + + if ($wthapi =~ /OpenMeteo.*-API/xs) { $hash->{WEATHERMODEL} = 'OpenMeteo'; } + else { $hash->{WEATHERMODEL} = 'DWD'; } return; } @@ -18307,6 +18373,22 @@ sub isOpenMeteoUsed { return $ret; } +################################################################ +# Prüfung auf Verwendung von Open-Meteo API als +# Lieferant für Wetterdaten +################################################################ +sub isWeatherModelOpenMeteo { + my $hash = shift; + + my $ret = 0; + + if (InternalVal ($hash->{NAME}, 'WEATHERMODEL', '') =~ /^OpenMeteo/xs) { + $ret = 1; + } + +return $ret; +} + ################################################################ # welche PV Autokorrektur wird verwendet ? # Standard bei nur "on" -> on_simple @@ -19625,11 +19707,51 @@ sub SolCastAPIVal { my $name = $hash->{NAME}; my $type = $hash->{TYPE}; - if(defined $data{$type}{$name}{solcastapi} && - defined $data{$type}{$name}{solcastapi}{$string} && - defined $data{$type}{$name}{solcastapi}{$string}{$ststr} && - defined $data{$type}{$name}{solcastapi}{$string}{$ststr}{$key}) { - return $data{$type}{$name}{solcastapi}{$string}{$ststr}{$key}; + if (defined $data{$type}{$name}{solcastapi} && + defined $data{$type}{$name}{solcastapi}{$string} && + defined $data{$type}{$name}{solcastapi}{$string}{$ststr} && + defined $data{$type}{$name}{solcastapi}{$string}{$ststr}{$key}) { + return $data{$type}{$name}{solcastapi}{$string}{$ststr}{$key}; + } + +return $def; +} + +########################################################################################################################################################## +# Wert des weatherAPI-Hash zurückliefern +# Usage: +# WeatherAPIVal ($hash, $apiname, $tstr, $key, $def) +# +# $apiname: Hauptname der API gemäß setupWeatherDevX (z.B. OpenMeteo) +# $tstr: Zeitwert der Form fcX_XX (z.B. fc1_19) +# $key: Parameter (z.B. don, neff, rr1c, ...) +# $def: Defaultwert +# +# Sonderabfragen +# WeatherAPIVal ($hash, $apiname, '?All', 'lastretrieval_time', $def) - letzte Abfrage Zeitstring +# WeatherAPIVal ($hash, $apiname, '?All', 'lastretrieval_timestamp', $def) - letzte Abfrage Unix Timestamp +# WeatherAPIVal ($hash, $apiname, '?All', 'todayDoneAPIrequests', $def) - heute ausgeführte API Requests +# WeatherAPIVal ($hash, $apiname, '?All', 'todayDoneAPIcalls', $def) - heute ausgeführte API Calls (hat u.U. mehrere Requests) +# WeatherAPIVal ($hash, $apiname, '?All', 'todayRemainingAPIrequests $def) - heute verbleibende API Requests +# WeatherAPIVal ($hash, $apiname, '?All', 'todayRemainingAPIcalls', $def) - heute noch mögliche API Calls (ungl. Requests !) +# WeatherAPIVal ($hash, $apiname, '?All', 'currentAPIinterval', $def) - aktuelles API Request Intervall +# WeatherAPIVal ($hash, $apiname, '?All', 'response_message', $def) - letzte API Antwort +########################################################################################################################################################## +sub WeatherAPIVal { + my $hash = shift; + my $apiname = shift; + my $tstr = shift; + my $key = shift; + my $def = shift; + + my $name = $hash->{NAME}; + my $type = $hash->{TYPE}; + + if (defined $data{$type}{$name}{weatherapi} && + defined $data{$type}{$name}{weatherapi}{$apiname} && + defined $data{$type}{$name}{weatherapi}{$apiname}{$tstr} && + defined $data{$type}{$name}{weatherapi}{$apiname}{$tstr}{$key}) { + return $data{$type}{$name}{weatherapi}{$apiname}{$tstr}{$key}; } return $def; @@ -20470,7 +20592,8 @@ to ensure that the system configuration is correct.
@@ -20598,6 +20721,14 @@ to ensure that the system configuration is correct.
+ + +

@@ -21105,30 +21236,30 @@ to ensure that the system configuration is correct. allStringsFullfilled Fulfillment status of error-free generation of all strings conForecastTillNextSunrise Consumption forecast from current hour to the coming sunrise - currentAPIinterval the current call interval of the SolCast API (only model SolCastAPI) in seconds + currentAPIinterval the current polling interval of the selected radiation data API in seconds currentRunMtsConsumer_XX the running time (minutes) of the consumer "XX" since the last switch-on. (last running cycle) dayAfterTomorrowPVforecast provides the forecast of PV generation for the day after tomorrow (if available) without autocorrection (raw data) daysUntilBatteryCare Days until the next battery maintenance (reaching the charge 'maxSoC' from attribute ctrlBatSocManagement) - lastretrieval_time the last call time of the API (only Model SolCastAPI, ForecastSolarAPI) - lastretrieval_timestamp the timestamp of the last call time of the API (only Model SolCastAPI, ForecastSolarAPI) - response_message the last status message of the API (only Model SolCastAPI, ForecastSolarAPI) + lastretrieval_time the last retrieval time of the selected radiation data API + lastretrieval_timestamp the timestamp of the last retrieval time of the selected radiation data API + response_message the last status message of the selected radiation data API runTimeAvgDayConsumer_XX the average running time (minutes) of consumer "XX" on one day runTimeCentralTask the runtime of the last SolarForecast interval (total process) in seconds runTimeTrainAI the runtime of the last AI training cycle in seconds - runTimeLastAPIAnswer the last response time of the API call to a request in seconds (only model SolCastAPI, ForecastSolarAPI) - runTimeLastAPIProc the last process time for processing the received API data (only model SolCastAPI, ForecastSolarAPI) + runTimeLastAPIAnswer the last response time of the radiation data API retrieval to a request in seconds + runTimeLastAPIProc the last process time for processing the received radiation data API data SunMinutes_Remain the remaining minutes until sunset of the current day SunHours_Remain the remaining hours until sunset of the current day todayConsumptionForecast Consumption forecast per hour of the current day (01-24) todayConForecastTillSunset Consumption forecast from current hour to hour before sunset - todayDoneAPIcalls the number of API calls executed on the current day (only model SolCastAPI, ForecastSolarAPI) - todayDoneAPIrequests the number of API requests executed on the current day (only model SolCastAPI, ForecastSolarAPI) + todayDoneAPIcalls the number of radiation data API calls executed on the current day + todayDoneAPIrequests the number of radiation data API requests executed on the current day todayGridConsumption the energy drawn from the public grid on the current day todayGridFeedIn PV energy fed into the public grid on the current day - todayMaxAPIcalls the maximum possible number of SolCast API calls (SolCastAPI model only). + todayMaxAPIcalls the maximum possible number of radiation data API calls. A call can contain multiple API requests. - todayRemainingAPIcalls the number of SolCast API calls still possible on the current day (only model SolCastAPI) - todayRemainingAPIrequests the number of SolCast API requests still possible on the current day (only model SolCastAPI) + todayRemainingAPIcalls the number of radiation data API calls still possible on the current day + todayRemainingAPIrequests the number of radiation data API requests still possible on the current day todayBatIn the energy charged into the battery on the current day todayBatOut the energy taken from the battery on the current day @@ -22839,7 +22970,8 @@ die ordnungsgemäße Anlagenkonfiguration geprüft werden.
@@ -22957,12 +23089,19 @@ die ordnungsgemäße Anlagenkonfiguration geprüft werden.
- + +
+ +
@@ -23475,30 +23614,30 @@ die ordnungsgemäße Anlagenkonfiguration geprüft werden. allStringsFullfilled Erfüllungsstatus der fehlerfreien Generierung aller Strings conForecastTillNextSunrise Verbrauchsprognose von aktueller Stunde bis zum kommenden Sonnenaufgang - currentAPIinterval das aktuelle Abrufintervall der SolCast API (nur Model SolCastAPI) in Sekunden + currentAPIinterval das aktuelle Abrufintervall der gewählten Strahlungsdaten-API in Sekunden currentRunMtsConsumer_XX die Laufzeit (Minuten) des Verbrauchers "XX" seit dem letzten Einschalten. (letzter Laufzyklus) dayAfterTomorrowPVforecast liefert die Vorhersage der PV Erzeugung für Übermorgen (sofern verfügbar) ohne Autokorrektur (Rohdaten). daysUntilBatteryCare Tage bis zur nächsten Batteriepflege (Erreichen der Ladung 'maxSoC' aus Attribut ctrlBatSocManagement) - lastretrieval_time der letzte Abrufzeitpunkt der API (nur Model SolCastAPI, ForecastSolarAPI) - lastretrieval_timestamp der Timestamp der letzen Abrufzeitpunkt der API (nur Model SolCastAPI, ForecastSolarAPI) - response_message die letzte Statusmeldung der API (nur Model SolCastAPI, ForecastSolarAPI) + lastretrieval_time der letzte Abrufzeitpunkt der gewählten Strahlungsdaten-API + lastretrieval_timestamp der Timestamp der letzen Abrufzeitpunkt der gewählten Strahlungsdaten-API + response_message die letzte Statusmeldung der gewählten Strahlungsdaten-API runTimeAvgDayConsumer_XX die durchschnittliche Laufzeit (Minuten) des Verbrauchers "XX" an einem Tag runTimeCentralTask die Laufzeit des letzten SolarForecast Intervalls (Gesamtprozess) in Sekunden runTimeTrainAI die Laufzeit des letzten KI Trainingszyklus in Sekunden - runTimeLastAPIAnswer die letzte Antwortzeit des API Abrufs auf einen Request in Sekunden (nur Model SolCastAPI, ForecastSolarAPI) - runTimeLastAPIProc die letzte Prozesszeit zur Verarbeitung der empfangenen API Daten (nur Model SolCastAPI, ForecastSolarAPI) + runTimeLastAPIAnswer die letzte Antwortzeit des Strahlungsdaten-API Abrufs auf einen Request in Sekunden + runTimeLastAPIProc die letzte Prozesszeit zur Verarbeitung der empfangenen Strahlungsdaten-API Daten SunMinutes_Remain die verbleibenden Minuten bis Sonnenuntergang des aktuellen Tages SunHours_Remain die verbleibenden Stunden bis Sonnenuntergang des aktuellen Tages todayConsumptionForecast Verbrauchsprognose pro Stunde des aktuellen Tages (01-24) todayConForecastTillSunset Verbrauchsprognose von aktueller Stunde bis Stunde vor Sonnenuntergang - todayDoneAPIcalls die Anzahl der am aktuellen Tag ausgeführten API Calls (nur Model SolCastAPI, ForecastSolarAPI) - todayDoneAPIrequests die Anzahl der am aktuellen Tag ausgeführten API Requests (nur Model SolCastAPI, ForecastSolarAPI) + todayDoneAPIcalls die Anzahl der am aktuellen Tag ausgeführten Strahlungsdaten-API Calls + todayDoneAPIrequests die Anzahl der am aktuellen Tag ausgeführten Strahlungsdaten-API Requests todayGridConsumption die aus dem öffentlichen Netz bezogene Energie am aktuellen Tag todayGridFeedIn die in das öffentliche Netz eingespeiste PV Energie am aktuellen Tag - todayMaxAPIcalls die maximal mögliche Anzahl SolCast API Calls (nur Model SolCastAPI). + todayMaxAPIcalls die maximal mögliche Anzahl Strahlungsdaten-API Calls. Ein Call kann mehrere API Requests enthalten. - todayRemainingAPIcalls die Anzahl der am aktuellen Tag noch möglichen SolCast API Calls (nur Model SolCastAPI) - todayRemainingAPIrequests die Anzahl der am aktuellen Tag noch möglichen SolCast API Requests (nur Model SolCastAPI) + todayRemainingAPIcalls die Anzahl der am aktuellen Tag noch möglichen Strahlungsdaten-API Calls + todayRemainingAPIrequests die Anzahl der am aktuellen Tag noch möglichen Strahlungsdaten-API Requests todayBatIn die am aktuellen Tag in die Batterie geladene Energie todayBatOut die am aktuellen Tag aus der Batterie entnommene Energie