From 81dd6e946c78fe8ed817c1097b02b6d8ee3e248f Mon Sep 17 00:00:00 2001 From: nasseeder1 Date: Wed, 29 May 2024 18:55:39 +0000 Subject: [PATCH] 76_SolarForecast: ctrlStatisticReadings: new runTimeAvgDayConsumer_XX git-svn-id: https://svn.fhem.de/fhem/trunk@28926 2b470e98-0d58-463d-a4d8-8e2adae1ed80 --- fhem/CHANGED | 1 + fhem/FHEM/76_SolarForecast.pm | 271 +++++++++++++++++++++------------- 2 files changed, 167 insertions(+), 105 deletions(-) diff --git a/fhem/CHANGED b/fhem/CHANGED index a13e431bd..d6c062ce3 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: ctrlStatisticReadings: new runTimeAvgDayConsumer - change: 76_SolarForecast: ctrlDebug consumerSwitching splitted into separated consumers, minor bug fix - feature: 74_AutomowerConnect: new design attribute hideSchedulerButton diff --git a/fhem/FHEM/76_SolarForecast.pm b/fhem/FHEM/76_SolarForecast.pm index b1589943e..9314b77c1 100644 --- a/fhem/FHEM/76_SolarForecast.pm +++ b/fhem/FHEM/76_SolarForecast.pm @@ -157,6 +157,9 @@ BEGIN { # Versions History intern my %vNotesIntern = ( + "1.21.4" => "28.05.2024 __getCyclesAndRuntime: rename numberDayStarts to cycleDayNum ". + "currentRunMtsConsumer_XX: edit commandref, Consumers: replace avgruntime by runtimeAvgDay ". + "ctrlStatisticReadings: new runTimeAvgDayConsumer_XX, pvHistory: new key avgcycmntscsmXX", "1.21.3" => "27.05.2024 __getCyclesAndRuntime: change procedure determine consumer runtime and cycles per day ". "__calcPVestimates: correct printout 'Estimated PV generation (calc)' and '(raw)' ". "ctrlDebug: consumerSwitching splitted into separated consumers ", @@ -1042,11 +1045,18 @@ my %hcsr = ( for my $csr (1..$maxconsumer) { $csr = sprintf "%02d", $csr; + $hcsr{'currentRunMtsConsumer_'.$csr}{fnr} = 4; $hcsr{'currentRunMtsConsumer_'.$csr}{fn} = \&ConsumerVal; $hcsr{'currentRunMtsConsumer_'.$csr}{par} = 'cycleTime'; $hcsr{'currentRunMtsConsumer_'.$csr}{unit} = ' min'; $hcsr{'currentRunMtsConsumer_'.$csr}{def} = 0; + + $hcsr{'runTimeAvgDayConsumer_'.$csr}{fnr} = 4; + $hcsr{'runTimeAvgDayConsumer_'.$csr}{fn} = \&ConsumerVal; + $hcsr{'runTimeAvgDayConsumer_'.$csr}{par} = 'runtimeAvgDay'; + $hcsr{'runTimeAvgDayConsumer_'.$csr}{unit} = ' min'; + $hcsr{'runTimeAvgDayConsumer_'.$csr}{def} = 0; } # Funktiontemplate zur Speicherung von Werten in pvHistory @@ -5620,7 +5630,7 @@ sub _attrcreateStatisticRdgs { ## no critic "not used" my $aName = $paref->{aName}; my $aVal = $paref->{aVal}; - my $te = 'currentRunMtsConsumer_'; + my $te = 'currentRunMtsConsumer_|runTimeAvgDayConsumer_'; if ($aVal =~ /$te/xs && $init_done) { my @aa = split ",", $aVal; @@ -8751,25 +8761,30 @@ sub _manageConsumerData { } deleteReadingspec ($hash, "consumer${c}_currentPower") if(!$etotread && !$paread); + + __getAutomaticState ($paref); # Automatic Status des Consumers abfragen + __calcEnergyPieces ($paref); # Energieverbrauch auf einzelne Stunden für Planungsgrundlage aufteilen + __planInitialSwitchTime ($paref); # Consumer Switch Zeiten planen + __setTimeframeState ($paref); # Timeframe Status ermitteln + __setConsRcmdState ($paref); # Consumption Recommended Status setzen + __switchConsumer ($paref); # Consumer schalten + + $paref->{pcurr} = $pcurr; + + __getCyclesAndRuntime ($paref); # Verbraucher - Laufzeit, Tagesstarts und Aktivminuten pro Stunde ermitteln + __setPhysLogSwState ($paref); # physischen / logischen Schaltzustand festhalten + __reviewSwitchTime ($paref); # Planungsdaten überprüfen und ggf. neu planen + __remainConsumerTime ($paref); # Restlaufzeit Verbraucher ermitteln - $paref->{val} = ConsumerVal ($hash, $c, "numberDayStarts", 0); # Anzahl Tageszyklen des Verbrauchers speichern - $paref->{histname} = "cyclescsm${c}"; - setPVhistory ($paref); - - $paref->{val} = ceil ConsumerVal ($hash, $c, "minutesOn", 0); # Verbrauchsminuten akt. Stunde des Consumers speichern - $paref->{histname} = "minutescsm${c}"; - setPVhistory ($paref); - - delete $paref->{histname}; - delete $paref->{val}; - + delete $paref->{pcurr}; + ## Durchschnittsverbrauch / Betriebszeit ermitteln + speichern ################################################################ my $consumerco = 0; my $runhours = 0; my $dnum = 0; - for my $n (sort{$a<=>$b} keys %{$data{$type}{$name}{pvhist}}) { # Betriebszeit und gemessenen Verbrauch ermitteln + for my $n (sort{$a<=>$b} keys %{$data{$type}{$name}{pvhist}}) { # Betriebszeit und gemessenen Verbrauch ermitteln my $csme = HistoryVal ($hash, $n, 99, "csme${c}", 0); my $hours = HistoryVal ($hash, $n, 99, "hourscsme${c}", 0); next if(!$hours); @@ -8787,25 +8802,8 @@ sub _manageConsumerData { delete $data{$type}{$name}{consumers}{$c}{avgenergy}; } - $data{$type}{$name}{consumers}{$c}{avgruntime} = sprintf "%.2f", (($runhours / $dnum) * 60); # Durchschnittslaufzeit am Tag in Minuten + $data{$type}{$name}{consumers}{$c}{runtimeAvgDay} = sprintf "%.2f", (($runhours / $dnum) * 60); # Durchschnittslaufzeit am Tag in Minuten } - - - __getAutomaticState ($paref); # Automatic Status des Consumers abfragen - __calcEnergyPieces ($paref); # Energieverbrauch auf einzelne Stunden für Planungsgrundlage aufteilen - __planInitialSwitchTime ($paref); # Consumer Switch Zeiten planen - __setTimeframeState ($paref); # Timeframe Status ermitteln - __setConsRcmdState ($paref); # Consumption Recommended Status setzen - __switchConsumer ($paref); # Consumer schalten - - $paref->{pcurr} = $pcurr; - - __getCyclesAndRuntime ($paref); # Verbraucher - Laufzeit, Tagesstarts und Aktivminuten pro Stunde ermitteln - __setPhysLogSwState ($paref); # physischen / logischen Schaltzustand festhalten - __reviewSwitchTime ($paref); # Planungsdaten überprüfen und ggf. neu planen - __remainConsumerTime ($paref); # Restlaufzeit Verbraucher ermitteln - - delete $paref->{pcurr}; ## Consumer Schaltstatus und Schaltzeit für Readings ermitteln ################################################################ @@ -10020,9 +10018,11 @@ return $state; } ################################################################ -## Verbraucher - Laufzeit, Tagesstarts und Aktivminuten pro -## Stunde ermitteln -## Tageswechsel Management +# Verbraucher - Laufzeit, Tagesstarts und Aktivminuten pro +# Stunde ermitteln +# Stundenwechsel + Tageswechsel Management +# +# startTime - wichtig für Wechselmanagement!! ################################################################ sub __getCyclesAndRuntime { my $paref = shift; @@ -10036,18 +10036,33 @@ sub __getCyclesAndRuntime { my $pcurr = $paref->{pcurr}; my $c = $paref->{consumer}; my $debug = $paref->{debug}; + + ### nicht mehr benötigte Daten verarbeiten - Bereich kann später wieder raus !! + ########################################################################################################################## + my $nds = ConsumerVal ($hash, $c, 'numberDayStarts', 'leer'); # 28.05.2024 + my $art = ConsumerVal ($hash, $c, 'avgruntime', 'leer'); + if ($nds ne 'leer') { + $data{$type}{$name}{consumers}{$c}{cycleDayNum} = $nds; + delete $data{$type}{$name}{consumers}{$c}{numberDayStarts}; + } + + if ($art ne 'leer') { + $data{$type}{$name}{consumers}{$c}{runtimeAvgDay} = $art; + delete $data{$type}{$name}{consumers}{$c}{avgruntime}; + } + ########################################################################################################################## - my ($startday, $starthour); + my ($starthour, $startday); if (isConsumerLogOn ($hash, $c, $pcurr)) { # Verbraucher ist logisch "an" - if (ConsumerVal ($hash, $c, 'onoff', 'off') eq 'off') { - $data{$type}{$name}{consumers}{$c}{onoff} = 'on'; - $data{$type}{$name}{consumers}{$c}{startTime} = $t; # startTime ist nicht von "Automatic" abhängig -> nicht identisch mit planswitchon !!! - $data{$type}{$name}{consumers}{$c}{cycleStarttime} = $t; - $data{$type}{$name}{consumers}{$c}{cycleTime} = 0; - #my $stimes = ConsumerVal ($hash, $c, 'numberDayStarts', 0); # Anzahl der On-Schaltungen am Tag - $data{$type}{$name}{consumers}{$c}{numberDayStarts}++; - $data{$type}{$name}{consumers}{$c}{lastMinutesOn} = ConsumerVal ($hash, $c, 'minutesOn', 0); + if (ConsumerVal ($hash, $c, 'onoff', 'off') eq 'off') { # Status im letzen Zyklus war "off" + $data{$type}{$name}{consumers}{$c}{onoff} = 'on'; + $data{$type}{$name}{consumers}{$c}{startTime} = $t; # startTime ist nicht von "Automatic" abhängig -> nicht identisch mit planswitchon !!! + $data{$type}{$name}{consumers}{$c}{cycleStarttime} = $t; + $data{$type}{$name}{consumers}{$c}{cycleTime} = 0; + $data{$type}{$name}{consumers}{$c}{lastMinutesOn} = ConsumerVal ($hash, $c, 'minutesOn', 0); + + $data{$type}{$name}{consumers}{$c}{cycleDayNum}++; # Anzahl der On-Schaltungen am Tag } else { $data{$type}{$name}{consumers}{$c}{cycleTime} = (($t - ConsumerVal ($hash, $c, 'cycleStarttime', $t)) / 60); # Minuten @@ -10060,48 +10075,61 @@ sub __getCyclesAndRuntime { my $runtime = (($t - ConsumerVal ($hash, $c, 'startTime', $t)) / 60); # in Minuten ! (gettimeofday sind ms !) $data{$type}{$name}{consumers}{$c}{minutesOn} = ConsumerVal ($hash, $c, 'lastMinutesOn', 0) + $runtime; } - else { # neue Stunde hat begonnen - if (ConsumerVal ($hash, $c, 'onoff', 'off') eq 'on') { - $data{$type}{$name}{consumers}{$c}{startTime} = timestringToTimestamp ($date.' '.sprintf("%02d", $chour).':00:00'); - $data{$type}{$name}{consumers}{$c}{minutesOn} = ($t - ConsumerVal ($hash, $c, 'startTime', $t)) / 60; # in Minuten ! (gettimeofday sind ms !) + else { # Stundenwechsel + if (ConsumerVal ($hash, $c, 'onoff', 'off') eq 'on') { # Status im letzen Zyklus war "on" + my $newst = timestringToTimestamp ($date.' '.sprintf("%02d", $chour).':00:00'); + $data{$type}{$name}{consumers}{$c}{startTime} = $newst; + $data{$type}{$name}{consumers}{$c}{minutesOn} = ($t - ConsumerVal ($hash, $c, 'startTime', $newst)) / 60; # in Minuten ! (gettimeofday sind ms !) $data{$type}{$name}{consumers}{$c}{lastMinutesOn} = 0; - if ($day ne $startday) { # Tageswechsel - $data{$type}{$name}{consumers}{$c}{numberDayStarts} = 1; + if ($day ne $startday) { # Tageswechsel + $data{$type}{$name}{consumers}{$c}{cycleDayNum} = 1; } } } } - else { # Verbraucher soll nicht aktiv sein - $data{$type}{$name}{consumers}{$c}{onoff} = 'off'; - #$data{$type}{$name}{consumers}{$c}{cycleTime} = 0; - $starthour = strftime "%H", localtime(ConsumerVal ($hash, $c, 'startTime', $t)); - $startday = strftime "%d", localtime(ConsumerVal ($hash, $c, 'startTime', $t)); + else { # Verbraucher soll nicht aktiv sein + $starthour = strftime "%H", localtime(ConsumerVal ($hash, $c, 'startTime', 1)); + $startday = strftime "%d", localtime(ConsumerVal ($hash, $c, 'startTime', 1)); # aktueller Tag (range 01 to 31) - if ($chour ne $starthour) { + if ($chour ne $starthour) { # Stundenwechsel $data{$type}{$name}{consumers}{$c}{minutesOn} = 0; - delete $data{$type}{$name}{consumers}{$c}{startTime}; } - if ($day ne $startday) { # Tageswechsel - $data{$type}{$name}{consumers}{$c}{numberDayStarts} = 0; + if ($day ne $startday) { # Tageswechsel + $data{$type}{$name}{consumers}{$c}{cycleDayNum} = 0; } + + $data{$type}{$name}{consumers}{$c}{onoff} = 'off'; } if ($debug =~ /consumerSwitching${c}/xs) { my $sr = 'still running'; - my $son = isConsumerLogOn ($hash, $c, $pcurr) ? $sr : ConsumerVal ($hash, $c, 'cycleTime', 0) * 60; # letzte Cycle-Zeitdauer in Sekunden + my $son = isConsumerLogOn ($hash, $c, $pcurr) ? $sr : ConsumerVal ($hash, $c, 'cycleTime', 0) * 60; # letzte Cycle-Zeitdauer in Sekunden my $cst = ConsumerVal ($hash, $c, 'cycleStarttime', 0); $son = $son && $son ne $sr ? timestampToTimestring ($cst + $son, $paref->{lang}) : $son eq $sr ? $sr : '-'; - $cst = $cst ? timestampToTimestring ($cst, $paref->{lang}) : '-'; + $cst = $cst ? timestampToTimestring ($cst, $paref->{lang}) : '-'; - Log3 ($name, 1, qq{$name DEBUG> consumer "$c" - numberDayStarts: }.ConsumerVal ($hash, $c, 'numberDayStarts', 0)); + Log3 ($name, 1, qq{$name DEBUG> consumer "$c" - cycleDayNum: }.ConsumerVal ($hash, $c, 'cycleDayNum', 0)); Log3 ($name, 1, qq{$name DEBUG> consumer "$c" - last cycle start time: $cst}); Log3 ($name, 1, qq{$name DEBUG> consumer "$c" - last cycle end time: $son}); } + ## History schreiben + ###################### + $paref->{val} = ConsumerVal ($hash, $c, "cycleDayNum", 0); # Anzahl Tageszyklen des Verbrauchers speichern + $paref->{histname} = "cyclescsm${c}"; + setPVhistory ($paref); + + $paref->{val} = ceil ConsumerVal ($hash, $c, "minutesOn", 0); # Verbrauchsminuten akt. Stunde des Consumers speichern + $paref->{histname} = "minutescsm${c}"; + setPVhistory ($paref); + + delete $paref->{histname}; + delete $paref->{val}; + return; } @@ -10859,6 +10887,11 @@ sub genStatisticReadings { for my $kpi (@csr) { my $def = $hcsr{$kpi}{def}; my $par = $hcsr{$kpi}{par}; + + if (!defined $def || !defined $par) { + Log3 ($name, 1, "$name - ERROR in Application - attribute ctrlStatisticReadings KPI '$kpi' has no Parameter or default value set. Set the attribute again or inform Maintainer."); + next; + } if ($def eq 'apimaxreq') { $def = AttrVal ($name, 'ctrlSolCastAPImaxReq', $solcmaxreqdef); @@ -10979,6 +11012,19 @@ sub genStatisticReadings { storeReading ('statistic_'.$kpi, (sprintf "%.0f", $mion).$hcsr{$kpi}{unit}); } + + if ($kpi =~ /runTimeAvgDayConsumer_/xs) { + my $c = (split "_", $kpi)[1]; # Consumer Nummer extrahieren + + if (!AttrVal ($name, 'consumer'.$c, '')) { + deleteReadingspec ($hash, 'statistic_runTimeAvgDayConsumer_'.$c); + return; + } + + my $radc = &{$hcsr{$kpi}{fn}} ($hash, $c, $hcsr{$kpi}{par}, $def); + + storeReading ('statistic_'.$kpi, $radc.$hcsr{$kpi}{unit}); + } if ($kpi eq 'todayConsumptionForecast') { my $type = $paref->{type}; @@ -14469,7 +14515,11 @@ sub setPVhistory { } my $cycles = HistoryVal ($hash, $day, 99, "cyclescsm${num}", 0); - $data{$type}{$name}{pvhist}{$day}{99}{"hourscsme${num}"} = sprintf "%.2f", ($minutes / 60 ) if($cycles); + + if ($cycles) { + $data{$type}{$name}{pvhist}{$day}{99}{"hourscsme${num}"} = sprintf "%.2f", ($minutes / 60 ); + $data{$type}{$name}{pvhist}{$day}{99}{"avgcycmntscsm${num}"} = sprintf "%.2f", ($minutes / $cycles); + } } if ($histname =~ /cyclescsm[0-9]+$/xs) { # Anzahl Tageszyklen des Verbrauchers @@ -14664,18 +14714,20 @@ sub listDataPool { for my $c (1..$maxconsumer) { $c = sprintf "%02d", $c; my $nl = 0; - my $csmc = HistoryVal ($hash, $day, $key, "cyclescsm${c}", undef); - my $csmt = HistoryVal ($hash, $day, $key, "csmt${c}", undef); - my $csme = HistoryVal ($hash, $day, $key, "csme${c}", undef); - my $csmm = HistoryVal ($hash, $day, $key, "minutescsm${c}", undef); - my $csmh = HistoryVal ($hash, $day, $key, "hourscsme${c}", undef); + my $csmc = HistoryVal ($hash, $day, $key, "cyclescsm${c}", undef); + my $csmt = HistoryVal ($hash, $day, $key, "csmt${c}", undef); + my $csme = HistoryVal ($hash, $day, $key, "csme${c}", undef); + my $csmm = HistoryVal ($hash, $day, $key, "minutescsm${c}", undef); + my $csmh = HistoryVal ($hash, $day, $key, "hourscsme${c}", undef); + my $csma = HistoryVal ($hash, $day, $key, "avgcycmntscsm${c}", undef); if ($export eq 'csv') { - $hexp->{$day}{$key}{"CyclesCsm${c}"} = $csmc if(defined $csmc); - $hexp->{$day}{$key}{"Csmt${c}"} = $csmt if(defined $csmt); - $hexp->{$day}{$key}{"Csme${c}"} = $csme if(defined $csme); - $hexp->{$day}{$key}{"MinutesCsm${c}"} = $csmm if(defined $csmm); - $hexp->{$day}{$key}{"HoursCsme${c}"} = $csmh if(defined $csmh); + $hexp->{$day}{$key}{"CyclesCsm${c}"} = $csmc if(defined $csmc); + $hexp->{$day}{$key}{"Csmt${c}"} = $csmt if(defined $csmt); + $hexp->{$day}{$key}{"Csme${c}"} = $csme if(defined $csme); + $hexp->{$day}{$key}{"MinutesCsm${c}"} = $csmm if(defined $csmm); + $hexp->{$day}{$key}{"HoursCsme${c}"} = $csmh if(defined $csmh); + $hexp->{$day}{$key}{"AvgCycleMinutesCsm${c}"} = $csma if(defined $csma); } if (defined $csmc) { @@ -14706,6 +14758,12 @@ sub listDataPool { $csm .= "hourscsme${c}: $csmh"; $nl = 1; } + + if (defined $csma) { + $csm .= ", " if($nl); + $csm .= "avgcycmntscsm${c}: $csma"; + $nl = 1; + } $csm .= "\n " if($nl); } @@ -17793,7 +17851,7 @@ return $def; # energythreshold - Schwellenwert (Wh pro Stunde) ab der ein Verbraucher als aktiv gewertet wird # upcurr - Unit des aktuellen Verbrauchs # avgenergy - initialer / gemessener Durchschnittsverbrauch pro Stunde -# avgruntime - durchschnittliche Einschalt- bzw. Zykluszeit (Minuten) +# runtimeAvgDay - durchschnittliche 'On'-Zeit an einem Tag (Minuten) # epieces - prognostizierte Energiescheiben (Hash) # ehodpieces - geplante Energiescheiben nach Tagesstunde (hour of day) (Hash) # dswoncond - Device zur Lieferung einer zusätzliche Einschaltbedingung @@ -18930,7 +18988,7 @@ to ensure that the system configuration is correct. gcon real power consumption (Wh) from the electricity grid gfeedin real feed-in (Wh) into the electricity grid feedprice Remuneration for the feed-in of one kWh. The currency of the price is defined in the currentMeterDev. - hourscsmeXX average hours of an active cycle of ConsumerXX of the day + hourscsmeXX total active hours of the day from ConsumerXX minutescsmXX total active minutes in the hour of ConsumerXX pvfc the predicted PV yield (Wh) pvrl real PV generation (Wh) @@ -19576,12 +19634,13 @@ 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 - currentRunMtsConsumer_XX the running time (minutes) of the consumer "XX" since the last switch-on. (0 - consumer is off) + 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) + 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) @@ -21196,35 +21255,36 @@ die ordnungsgemäße Anlagenkonfiguration geprüft werden. @@ -21860,12 +21920,13 @@ 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 - currentRunMtsConsumer_XX die Laufzeit (Minuten) des Verbrauchers "XX" seit dem letzten Einschalten. (0 - Verbraucher ist aus) + 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) + 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)