diff --git a/fhem/FHEM/76_SolarForecast.pm b/fhem/FHEM/76_SolarForecast.pm index b636e846d..afdd07137 100644 --- a/fhem/FHEM/76_SolarForecast.pm +++ b/fhem/FHEM/76_SolarForecast.pm @@ -36,6 +36,7 @@ use warnings; use POSIX; use GPUtils qw(GP_Import GP_Export); # wird für den Import der FHEM Funktionen aus der fhem.pl benötigt use Time::HiRes qw(gettimeofday tv_interval); +use Math::Trig; eval "use FHEM::Meta;1" or my $modMetaAbsent = 1; ## no critic 'eval' eval "use FHEM::Utility::CTZ qw(:all);1;" or my $ctzAbsent = 1; ## no critic 'eval' @@ -155,7 +156,8 @@ BEGIN { # Versions History intern my %vNotesIntern = ( - "1.33.1" => "27.09.2024 bugfix of 1.33.0 ", + "1.33.1" => "27.09.2024 bugfix of 1.33.0, add aiRulesNumber to pvCircular, limits of AI trained datasets for ". + "AI use (aiAccTRNLim, aiSpreadTRNLim)", "1.33.0" => "26.09.2024 substitute area factor hash by ___areaFactorPV function ", "1.32.0" => "02.09.2024 new attr setupOtherProducerXX, report calculation and storage of negative consumption values ". "Forum: https://forum.fhem.de/index.php?msg=1319083 ". @@ -421,6 +423,8 @@ my $aiSpreadUpLim = 120; my $aiSpreadLowLim = 80; # untere Abweichungsgrenze (%) AI 'Spread' von API Prognose my $aiAccUpLim = 130; # obere Abweichungsgrenze (%) AI 'Accurate' von API Prognose my $aiAccLowLim = 70; # untere Abweichungsgrenze (%) AI 'Accurate' von API Prognose +my $aiAccTRNLim = 1500; # Mindestanzahl KI Trainingssätze für Verwendung "KI Accurate" +my $aiSpreadTRNLim = 3500; # Mindestanzahl KI Trainingssätze für Verwendung "KI Spreaded" my $calcmaxd = 30; # Anzahl Tage die zur Berechnung Vorhersagekorrektur verwendet werden my @dweattrmust = qw(TTT Neff RR1c ww SunUp SunRise SunSet); # Werte die im Attr forecastProperties des Weather-DWD_Opendata Devices mindestens gesetzt sein müssen @@ -2031,8 +2035,8 @@ sub _setpvCorrectionFactorAuto { ## no critic "not used" } } } - - writeCacheToFile ($hash, 'plantconfig', $plantcfg.$name); # Anlagenkonfiguration sichern + + writeCacheToFile ($hash, 'plantconfig', $plantcfg.$name); # Anlagenkonfiguration sichern return; } @@ -3393,7 +3397,7 @@ sub __getDWDSolarData { $peak *= 1000; # kWp in Wp umrechnen my $ti = $data{$type}{$name}{strings}{$string}{tilt}; # Neigungswinkel Solarmodule my $az = $data{$type}{$name}{strings}{$string}{azimut}; # Ausrichtung der Solarmodule - $az += 180; # Umsetzung -180 - 180 in 0 - 360 + $az += 180; # Umsetzung -180 - 180 in 0 - 360 my $af = ___areaFactorPV ($ti, $az); # Flächenfaktor: https://wiki.fhem.de/wiki/Ertragsprognose_PV @@ -5480,10 +5484,10 @@ sub _attrMeterDev { ## no critic "not used" elsif ($paref->{cmd} eq 'del') { readingsDelete ($hash, "Current_GridConsumption"); readingsDelete ($hash, "Current_GridFeedIn"); - delete $data{$type}{$name}{circular}{'99'}{initdayfeedin}; - delete $data{$type}{$name}{circular}{'99'}{gridcontotal}; - delete $data{$type}{$name}{circular}{'99'}{initdaygcon}; - delete $data{$type}{$name}{circular}{'99'}{feedintotal}; + delete $data{$type}{$name}{circular}{99}{initdayfeedin}; + delete $data{$type}{$name}{circular}{99}{gridcontotal}; + delete $data{$type}{$name}{circular}{99}{initdaygcon}; + delete $data{$type}{$name}{circular}{99}{feedintotal}; delete $data{$type}{$name}{current}{gridconsumption}; delete $data{$type}{$name}{current}{tomorrowconsumption}; delete $data{$type}{$name}{current}{gridfeedin}; @@ -5772,12 +5776,12 @@ sub _attrBatteryDev { ## no critic "not used" readingsDelete ($hash, 'Current_BatCharge'); deleteReadingspec ($hash, 'Battery_.*'); undef @{$data{$type}{$name}{current}{socslidereg}}; - delete $data{$type}{$name}{circular}{'99'}{lastTsMaxSocRchd}; - delete $data{$type}{$name}{circular}{'99'}{nextTsMaxSocChge}; - delete $data{$type}{$name}{circular}{'99'}{initdaybatintot}; - delete $data{$type}{$name}{circular}{'99'}{initdaybatouttot}; - delete $data{$type}{$name}{circular}{'99'}{batintot}; - delete $data{$type}{$name}{circular}{'99'}{batouttot}; + delete $data{$type}{$name}{circular}{99}{lastTsMaxSocRchd}; + delete $data{$type}{$name}{circular}{99}{nextTsMaxSocChge}; + delete $data{$type}{$name}{circular}{99}{initdaybatintot}; + delete $data{$type}{$name}{circular}{99}{initdaybatouttot}; + delete $data{$type}{$name}{circular}{99}{batintot}; + delete $data{$type}{$name}{circular}{99}{batouttot}; delete $data{$type}{$name}{current}{powerbatout}; delete $data{$type}{$name}{current}{powerbatin}; delete $data{$type}{$name}{current}{batcharge}; @@ -7222,17 +7226,17 @@ sub _specialActivities { delete $data{$type}{$name}{solcastapi}{'?All'}{'?All'}{token}; delete $data{$type}{$name}{solcastapi}{'?All'}{'?All'}{verification_mode}; - delete $data{$type}{$name}{circular}{'99'}{initdayfeedin}; - delete $data{$type}{$name}{circular}{'99'}{initdaygcon}; - delete $data{$type}{$name}{circular}{'99'}{initdaybatintot}; - delete $data{$type}{$name}{circular}{'99'}{initdaybatouttot}; + delete $data{$type}{$name}{circular}{99}{initdayfeedin}; + delete $data{$type}{$name}{circular}{99}{initdaygcon}; + delete $data{$type}{$name}{circular}{99}{initdaybatintot}; + delete $data{$type}{$name}{circular}{99}{initdaybatouttot}; delete $data{$type}{$name}{current}{sunriseToday}; delete $data{$type}{$name}{current}{sunriseTodayTs}; delete $data{$type}{$name}{current}{sunsetToday}; delete $data{$type}{$name}{current}{sunsetTodayTs}; $data{$type}{$name}{circular}{99}{ydayDvtn} = CircularVal ($hash, 99, 'tdayDvtn', '-'); - delete $data{$type}{$name}{circular}{'99'}{tdayDvtn}; + delete $data{$type}{$name}{circular}{99}{tdayDvtn}; delete $data{$type}{$name}{pvhist}{$day}; # den (alten) aktuellen Tag aus History löschen @@ -7842,23 +7846,27 @@ sub _transferAPIRadiationValues { my $pvfc; if ($msg eq 'accurate' || $msg eq 'spreaded') { + my $airn = CircularVal ($hash, 99, 'aiRulesNumber', 0); my $aivar = 100; - $aivar = 100 * $pvaifc / $est if($est); + $aivar = sprintf "%.0f", (100 * $pvaifc / $est) if($est); # Übereinstimmungsgrad KI Forecast zu API Forecast in % - if ($msg eq 'accurate' && $aivar >= $aiAccLowLim && $aivar <= $aiAccUpLim) { # KI liefert 'accurate' Treffer -> verwenden - $data{$type}{$name}{nexthours}{$nhtstr}{aihit} = 1; - $pvfc = $pvaifc; - $useai = 1; + if ($msg eq 'accurate') { # KI liefert 'accurate' Treffer -> verwenden + if ($airn >= $aiAccTRNLim || ($aivar >= $aiAccLowLim && $aivar <= $aiAccUpLim)) { + $data{$type}{$name}{nexthours}{$nhtstr}{aihit} = 1; + $pvfc = $pvaifc; + $useai = 1; - debugLog ($paref, 'aiData', qq{AI Hit - accurate result found -> variance $aivar, hod: $hod, Rad1h: $rad1h, pvfc: $pvfc Wh}); + debugLog ($paref, 'aiData', qq{AI Hit - accurate result used -> aiRulesNum: $airn, variance: $aivar, hod: $hod, Rad1h: $rad1h, pvfc: $pvfc Wh}); + } } + elsif ($msg eq 'spreaded') { # Abweichung AI von Standardvorhersage begrenzen + if ($airn >= $aiSpreadTRNLim || ($aivar >= $aiSpreadLowLim && $aivar <= $aiSpreadUpLim)) { + $data{$type}{$name}{nexthours}{$nhtstr}{aihit} = 1; + $pvfc = $pvaifc; + $useai = 1; - if ($msg eq 'spreaded' && $aivar >= $aiSpreadLowLim && $aivar <= $aiSpreadUpLim) { # Abweichung AI von Standardvorhersage begrenzen - $data{$type}{$name}{nexthours}{$nhtstr}{aihit} = 1; - $pvfc = $pvaifc; - $useai = 1; - - debugLog ($paref, 'aiData', qq{AI Hit - spreaded result found and is in tolerance -> hod: $hod, Rad1h: $rad1h, pvfc: $pvfc Wh}); + debugLog ($paref, 'aiData', qq{AI Hit - spreaded result used -> aiRulesNum: $airn, hod: $hod, Rad1h: $rad1h, pvfc: $pvfc Wh}); + } } } else { @@ -14325,7 +14333,6 @@ return $hdv; sub manageTrain { my $paref = shift; my $name = $paref->{name}; - my $hash = $defs{$name}; if (CircularVal ($hash, 99, 'runTimeTrainAI', 0) < $aibcthhld) { @@ -14378,6 +14385,9 @@ sub finishTrain { my $runTimeTrainAI = $paref->{runTimeTrainAI}; my $aiinitstate = $paref->{aiinitstate}; my $aitrainFinishTs = $paref->{aitrainLastFinishTs}; + my $aiRulesNumber = $paref->{aiRulesNumber}; + + delete $data{$type}{$name}{circular}{99}{aiRulesNumber}; $data{$type}{$name}{current}{aiAddedToTrain} = 0; $data{$type}{$name}{current}{aicanuse} = $aicanuse; @@ -14385,7 +14395,8 @@ sub finishTrain { $data{$type}{$name}{current}{aiinitstate} = $aiinitstate if(defined $aiinitstate); $data{$type}{$name}{circular}{99}{runTimeTrainAI} = $runTimeTrainAI if(defined $runTimeTrainAI); # !! in Circular speichern um zu persistieren, setTimeTracking speichert zunächst in Current !! $data{$type}{$name}{circular}{99}{aitrainLastFinishTs} = $aitrainFinishTs if(defined $aitrainFinishTs); - + $data{$type}{$name}{circular}{99}{aiRulesNumber} = $aiRulesNumber if(defined $aiRulesNumber); + if ($aitrainstate eq 'ok') { _readCacheFile ({ name => $name, type => $type, @@ -14534,7 +14545,7 @@ sub aiTrain { ## no critic "not used" ), ""); $block ? return ($serial) : return \&finishTrain ($serial); } - + eval { $dtree->train(); 1; } @@ -14559,11 +14570,12 @@ sub aiTrain { ## no critic "not used" } setTimeTracking ($hash, $cst, 'runTimeTrainAI'); # Zyklus-Laufzeit ermitteln - + $serial = encode_base64 (Serialize ( {name => $name, aitrainstate => 'ok', runTimeTrainAI => CurrentVal ($hash, 'runTimeTrainAI', ''), aitrainLastFinishTs => int time, + aiRulesNumber => scalar $dtree->rule_statements(), # Returns a list of strings that describe the tree in rule-form aicanuse => 'ok' } ) @@ -14747,10 +14759,14 @@ sub aiInit { ## no critic "not used" my $hash = $defs{$name}; if (!isPrepared4AI ($hash)) { + delete $data{$type}{$name}{circular}{99}{aiRulesNumber}; + delete $data{$type}{$name}{circular}{99}{runTimeTrainAI}; + delete $data{$type}{$name}{circular}{99}{aitrainLastFinishTs}; + my $err = CurrentVal ($hash, 'aicanuse', ''); debugLog ($paref, 'aiProcess', $err); - + $data{$type}{$name}{current}{aiinitstate} = $err; return $err; } @@ -15390,6 +15406,7 @@ sub listDataPool { my $botot = CircularVal ($hash, $idx, 'batouttot', '-'); my $rtaitr = CircularVal ($hash, $idx, 'runTimeTrainAI', '-'); my $fsaitr = CircularVal ($hash, $idx, 'aitrainLastFinishTs', '-'); + my $airn = CircularVal ($hash, $idx, 'aiRulesNumber', '-'); my $aicts = CircularVal ($hash, $idx, 'attrInvChangedTs', '-'); my $pprl01 = CircularVal ($hash, $idx, 'pprl01', '-'); my $pprl02 = CircularVal ($hash, $idx, 'pprl02', '-'); @@ -15421,7 +15438,8 @@ sub listDataPool { $sq .= " batintot: $bitot, initdaybatintot: $idbitot \n"; $sq .= " batouttot: $botot, initdaybatouttot: $idbotot \n"; $sq .= " lastTsMaxSocRchd: $ltsmsr, nextTsMaxSocChge: $ntsmsc, days2care: $dtocare \n"; - $sq .= " runTimeTrainAI: $rtaitr, aitrainLastFinishTs: $fsaitr, attrInvChangedTs: $aicts \n"; + $sq .= " runTimeTrainAI: $rtaitr, aitrainLastFinishTs: $fsaitr, aiRulesNumber: $airn \n"; + $sq .= " attrInvChangedTs: $aicts \n"; } } } @@ -18059,6 +18077,7 @@ return $def; # initdaybatouttot - initialer Wert für Batterie outtotal zu Beginn des Tages (Wh) # batouttot - Batterie outtotal (Wh) # gridcontotal - Netzbezug total (Wh) +# aiRulesNumber - Anzahl der Regeln in der trainierten KI-Instanz # # $def: Defaultwert # @@ -19327,6 +19346,7 @@ to ensure that the system configuration is correct. quality Quality of the autocorrection factors (0..1), where 'simple' is the quality of the simple correction factor. runTimeTrainAI Duration of the last AI training aitrainLastFinishTs Timestamp of the last successful AI training + aiRulesNumber Number of rules in the trained AI instance tdayDvtn Today's deviation PV forecast/generation in % temp Outdoor temperature wcc Degree of cloud cover @@ -21653,6 +21673,7 @@ die ordnungsgemäße Anlagenkonfiguration geprüft werden. quality Qualität der Autokorrekturfaktoren (0..1), wobei 'simple' die Qualität des einfach berechneten Korrekturfaktors ist. runTimeTrainAI Laufzeit des letzten KI Trainings aitrainLastFinishTs Timestamp des letzten erfolgreichen KI Trainings + aiRulesNumber Anzahl der Regeln in der trainierten KI Instanz tdayDvtn heutige Abweichung PV Prognose/Erzeugung in % temp Außentemperatur wcc Grad der Wolkenüberdeckung @@ -23203,6 +23224,7 @@ die ordnungsgemäße Anlagenkonfiguration geprüft werden. "FHEM::SynoModules::SMUtils": 1.0270, "Time::HiRes": 0, "MIME::Base64": 0, + "Math::Trig": 0, "Storable": 0 }, "recommends": { diff --git a/fhem/contrib/DS_Starter/76_SolarForecast.pm b/fhem/contrib/DS_Starter/76_SolarForecast.pm index b636e846d..afdd07137 100644 --- a/fhem/contrib/DS_Starter/76_SolarForecast.pm +++ b/fhem/contrib/DS_Starter/76_SolarForecast.pm @@ -36,6 +36,7 @@ use warnings; use POSIX; use GPUtils qw(GP_Import GP_Export); # wird für den Import der FHEM Funktionen aus der fhem.pl benötigt use Time::HiRes qw(gettimeofday tv_interval); +use Math::Trig; eval "use FHEM::Meta;1" or my $modMetaAbsent = 1; ## no critic 'eval' eval "use FHEM::Utility::CTZ qw(:all);1;" or my $ctzAbsent = 1; ## no critic 'eval' @@ -155,7 +156,8 @@ BEGIN { # Versions History intern my %vNotesIntern = ( - "1.33.1" => "27.09.2024 bugfix of 1.33.0 ", + "1.33.1" => "27.09.2024 bugfix of 1.33.0, add aiRulesNumber to pvCircular, limits of AI trained datasets for ". + "AI use (aiAccTRNLim, aiSpreadTRNLim)", "1.33.0" => "26.09.2024 substitute area factor hash by ___areaFactorPV function ", "1.32.0" => "02.09.2024 new attr setupOtherProducerXX, report calculation and storage of negative consumption values ". "Forum: https://forum.fhem.de/index.php?msg=1319083 ". @@ -421,6 +423,8 @@ my $aiSpreadUpLim = 120; my $aiSpreadLowLim = 80; # untere Abweichungsgrenze (%) AI 'Spread' von API Prognose my $aiAccUpLim = 130; # obere Abweichungsgrenze (%) AI 'Accurate' von API Prognose my $aiAccLowLim = 70; # untere Abweichungsgrenze (%) AI 'Accurate' von API Prognose +my $aiAccTRNLim = 1500; # Mindestanzahl KI Trainingssätze für Verwendung "KI Accurate" +my $aiSpreadTRNLim = 3500; # Mindestanzahl KI Trainingssätze für Verwendung "KI Spreaded" my $calcmaxd = 30; # Anzahl Tage die zur Berechnung Vorhersagekorrektur verwendet werden my @dweattrmust = qw(TTT Neff RR1c ww SunUp SunRise SunSet); # Werte die im Attr forecastProperties des Weather-DWD_Opendata Devices mindestens gesetzt sein müssen @@ -2031,8 +2035,8 @@ sub _setpvCorrectionFactorAuto { ## no critic "not used" } } } - - writeCacheToFile ($hash, 'plantconfig', $plantcfg.$name); # Anlagenkonfiguration sichern + + writeCacheToFile ($hash, 'plantconfig', $plantcfg.$name); # Anlagenkonfiguration sichern return; } @@ -3393,7 +3397,7 @@ sub __getDWDSolarData { $peak *= 1000; # kWp in Wp umrechnen my $ti = $data{$type}{$name}{strings}{$string}{tilt}; # Neigungswinkel Solarmodule my $az = $data{$type}{$name}{strings}{$string}{azimut}; # Ausrichtung der Solarmodule - $az += 180; # Umsetzung -180 - 180 in 0 - 360 + $az += 180; # Umsetzung -180 - 180 in 0 - 360 my $af = ___areaFactorPV ($ti, $az); # Flächenfaktor: https://wiki.fhem.de/wiki/Ertragsprognose_PV @@ -5480,10 +5484,10 @@ sub _attrMeterDev { ## no critic "not used" elsif ($paref->{cmd} eq 'del') { readingsDelete ($hash, "Current_GridConsumption"); readingsDelete ($hash, "Current_GridFeedIn"); - delete $data{$type}{$name}{circular}{'99'}{initdayfeedin}; - delete $data{$type}{$name}{circular}{'99'}{gridcontotal}; - delete $data{$type}{$name}{circular}{'99'}{initdaygcon}; - delete $data{$type}{$name}{circular}{'99'}{feedintotal}; + delete $data{$type}{$name}{circular}{99}{initdayfeedin}; + delete $data{$type}{$name}{circular}{99}{gridcontotal}; + delete $data{$type}{$name}{circular}{99}{initdaygcon}; + delete $data{$type}{$name}{circular}{99}{feedintotal}; delete $data{$type}{$name}{current}{gridconsumption}; delete $data{$type}{$name}{current}{tomorrowconsumption}; delete $data{$type}{$name}{current}{gridfeedin}; @@ -5772,12 +5776,12 @@ sub _attrBatteryDev { ## no critic "not used" readingsDelete ($hash, 'Current_BatCharge'); deleteReadingspec ($hash, 'Battery_.*'); undef @{$data{$type}{$name}{current}{socslidereg}}; - delete $data{$type}{$name}{circular}{'99'}{lastTsMaxSocRchd}; - delete $data{$type}{$name}{circular}{'99'}{nextTsMaxSocChge}; - delete $data{$type}{$name}{circular}{'99'}{initdaybatintot}; - delete $data{$type}{$name}{circular}{'99'}{initdaybatouttot}; - delete $data{$type}{$name}{circular}{'99'}{batintot}; - delete $data{$type}{$name}{circular}{'99'}{batouttot}; + delete $data{$type}{$name}{circular}{99}{lastTsMaxSocRchd}; + delete $data{$type}{$name}{circular}{99}{nextTsMaxSocChge}; + delete $data{$type}{$name}{circular}{99}{initdaybatintot}; + delete $data{$type}{$name}{circular}{99}{initdaybatouttot}; + delete $data{$type}{$name}{circular}{99}{batintot}; + delete $data{$type}{$name}{circular}{99}{batouttot}; delete $data{$type}{$name}{current}{powerbatout}; delete $data{$type}{$name}{current}{powerbatin}; delete $data{$type}{$name}{current}{batcharge}; @@ -7222,17 +7226,17 @@ sub _specialActivities { delete $data{$type}{$name}{solcastapi}{'?All'}{'?All'}{token}; delete $data{$type}{$name}{solcastapi}{'?All'}{'?All'}{verification_mode}; - delete $data{$type}{$name}{circular}{'99'}{initdayfeedin}; - delete $data{$type}{$name}{circular}{'99'}{initdaygcon}; - delete $data{$type}{$name}{circular}{'99'}{initdaybatintot}; - delete $data{$type}{$name}{circular}{'99'}{initdaybatouttot}; + delete $data{$type}{$name}{circular}{99}{initdayfeedin}; + delete $data{$type}{$name}{circular}{99}{initdaygcon}; + delete $data{$type}{$name}{circular}{99}{initdaybatintot}; + delete $data{$type}{$name}{circular}{99}{initdaybatouttot}; delete $data{$type}{$name}{current}{sunriseToday}; delete $data{$type}{$name}{current}{sunriseTodayTs}; delete $data{$type}{$name}{current}{sunsetToday}; delete $data{$type}{$name}{current}{sunsetTodayTs}; $data{$type}{$name}{circular}{99}{ydayDvtn} = CircularVal ($hash, 99, 'tdayDvtn', '-'); - delete $data{$type}{$name}{circular}{'99'}{tdayDvtn}; + delete $data{$type}{$name}{circular}{99}{tdayDvtn}; delete $data{$type}{$name}{pvhist}{$day}; # den (alten) aktuellen Tag aus History löschen @@ -7842,23 +7846,27 @@ sub _transferAPIRadiationValues { my $pvfc; if ($msg eq 'accurate' || $msg eq 'spreaded') { + my $airn = CircularVal ($hash, 99, 'aiRulesNumber', 0); my $aivar = 100; - $aivar = 100 * $pvaifc / $est if($est); + $aivar = sprintf "%.0f", (100 * $pvaifc / $est) if($est); # Übereinstimmungsgrad KI Forecast zu API Forecast in % - if ($msg eq 'accurate' && $aivar >= $aiAccLowLim && $aivar <= $aiAccUpLim) { # KI liefert 'accurate' Treffer -> verwenden - $data{$type}{$name}{nexthours}{$nhtstr}{aihit} = 1; - $pvfc = $pvaifc; - $useai = 1; + if ($msg eq 'accurate') { # KI liefert 'accurate' Treffer -> verwenden + if ($airn >= $aiAccTRNLim || ($aivar >= $aiAccLowLim && $aivar <= $aiAccUpLim)) { + $data{$type}{$name}{nexthours}{$nhtstr}{aihit} = 1; + $pvfc = $pvaifc; + $useai = 1; - debugLog ($paref, 'aiData', qq{AI Hit - accurate result found -> variance $aivar, hod: $hod, Rad1h: $rad1h, pvfc: $pvfc Wh}); + debugLog ($paref, 'aiData', qq{AI Hit - accurate result used -> aiRulesNum: $airn, variance: $aivar, hod: $hod, Rad1h: $rad1h, pvfc: $pvfc Wh}); + } } + elsif ($msg eq 'spreaded') { # Abweichung AI von Standardvorhersage begrenzen + if ($airn >= $aiSpreadTRNLim || ($aivar >= $aiSpreadLowLim && $aivar <= $aiSpreadUpLim)) { + $data{$type}{$name}{nexthours}{$nhtstr}{aihit} = 1; + $pvfc = $pvaifc; + $useai = 1; - if ($msg eq 'spreaded' && $aivar >= $aiSpreadLowLim && $aivar <= $aiSpreadUpLim) { # Abweichung AI von Standardvorhersage begrenzen - $data{$type}{$name}{nexthours}{$nhtstr}{aihit} = 1; - $pvfc = $pvaifc; - $useai = 1; - - debugLog ($paref, 'aiData', qq{AI Hit - spreaded result found and is in tolerance -> hod: $hod, Rad1h: $rad1h, pvfc: $pvfc Wh}); + debugLog ($paref, 'aiData', qq{AI Hit - spreaded result used -> aiRulesNum: $airn, hod: $hod, Rad1h: $rad1h, pvfc: $pvfc Wh}); + } } } else { @@ -14325,7 +14333,6 @@ return $hdv; sub manageTrain { my $paref = shift; my $name = $paref->{name}; - my $hash = $defs{$name}; if (CircularVal ($hash, 99, 'runTimeTrainAI', 0) < $aibcthhld) { @@ -14378,6 +14385,9 @@ sub finishTrain { my $runTimeTrainAI = $paref->{runTimeTrainAI}; my $aiinitstate = $paref->{aiinitstate}; my $aitrainFinishTs = $paref->{aitrainLastFinishTs}; + my $aiRulesNumber = $paref->{aiRulesNumber}; + + delete $data{$type}{$name}{circular}{99}{aiRulesNumber}; $data{$type}{$name}{current}{aiAddedToTrain} = 0; $data{$type}{$name}{current}{aicanuse} = $aicanuse; @@ -14385,7 +14395,8 @@ sub finishTrain { $data{$type}{$name}{current}{aiinitstate} = $aiinitstate if(defined $aiinitstate); $data{$type}{$name}{circular}{99}{runTimeTrainAI} = $runTimeTrainAI if(defined $runTimeTrainAI); # !! in Circular speichern um zu persistieren, setTimeTracking speichert zunächst in Current !! $data{$type}{$name}{circular}{99}{aitrainLastFinishTs} = $aitrainFinishTs if(defined $aitrainFinishTs); - + $data{$type}{$name}{circular}{99}{aiRulesNumber} = $aiRulesNumber if(defined $aiRulesNumber); + if ($aitrainstate eq 'ok') { _readCacheFile ({ name => $name, type => $type, @@ -14534,7 +14545,7 @@ sub aiTrain { ## no critic "not used" ), ""); $block ? return ($serial) : return \&finishTrain ($serial); } - + eval { $dtree->train(); 1; } @@ -14559,11 +14570,12 @@ sub aiTrain { ## no critic "not used" } setTimeTracking ($hash, $cst, 'runTimeTrainAI'); # Zyklus-Laufzeit ermitteln - + $serial = encode_base64 (Serialize ( {name => $name, aitrainstate => 'ok', runTimeTrainAI => CurrentVal ($hash, 'runTimeTrainAI', ''), aitrainLastFinishTs => int time, + aiRulesNumber => scalar $dtree->rule_statements(), # Returns a list of strings that describe the tree in rule-form aicanuse => 'ok' } ) @@ -14747,10 +14759,14 @@ sub aiInit { ## no critic "not used" my $hash = $defs{$name}; if (!isPrepared4AI ($hash)) { + delete $data{$type}{$name}{circular}{99}{aiRulesNumber}; + delete $data{$type}{$name}{circular}{99}{runTimeTrainAI}; + delete $data{$type}{$name}{circular}{99}{aitrainLastFinishTs}; + my $err = CurrentVal ($hash, 'aicanuse', ''); debugLog ($paref, 'aiProcess', $err); - + $data{$type}{$name}{current}{aiinitstate} = $err; return $err; } @@ -15390,6 +15406,7 @@ sub listDataPool { my $botot = CircularVal ($hash, $idx, 'batouttot', '-'); my $rtaitr = CircularVal ($hash, $idx, 'runTimeTrainAI', '-'); my $fsaitr = CircularVal ($hash, $idx, 'aitrainLastFinishTs', '-'); + my $airn = CircularVal ($hash, $idx, 'aiRulesNumber', '-'); my $aicts = CircularVal ($hash, $idx, 'attrInvChangedTs', '-'); my $pprl01 = CircularVal ($hash, $idx, 'pprl01', '-'); my $pprl02 = CircularVal ($hash, $idx, 'pprl02', '-'); @@ -15421,7 +15438,8 @@ sub listDataPool { $sq .= " batintot: $bitot, initdaybatintot: $idbitot \n"; $sq .= " batouttot: $botot, initdaybatouttot: $idbotot \n"; $sq .= " lastTsMaxSocRchd: $ltsmsr, nextTsMaxSocChge: $ntsmsc, days2care: $dtocare \n"; - $sq .= " runTimeTrainAI: $rtaitr, aitrainLastFinishTs: $fsaitr, attrInvChangedTs: $aicts \n"; + $sq .= " runTimeTrainAI: $rtaitr, aitrainLastFinishTs: $fsaitr, aiRulesNumber: $airn \n"; + $sq .= " attrInvChangedTs: $aicts \n"; } } } @@ -18059,6 +18077,7 @@ return $def; # initdaybatouttot - initialer Wert für Batterie outtotal zu Beginn des Tages (Wh) # batouttot - Batterie outtotal (Wh) # gridcontotal - Netzbezug total (Wh) +# aiRulesNumber - Anzahl der Regeln in der trainierten KI-Instanz # # $def: Defaultwert # @@ -19327,6 +19346,7 @@ to ensure that the system configuration is correct. quality Quality of the autocorrection factors (0..1), where 'simple' is the quality of the simple correction factor. runTimeTrainAI Duration of the last AI training aitrainLastFinishTs Timestamp of the last successful AI training + aiRulesNumber Number of rules in the trained AI instance tdayDvtn Today's deviation PV forecast/generation in % temp Outdoor temperature wcc Degree of cloud cover @@ -21653,6 +21673,7 @@ die ordnungsgemäße Anlagenkonfiguration geprüft werden. quality Qualität der Autokorrekturfaktoren (0..1), wobei 'simple' die Qualität des einfach berechneten Korrekturfaktors ist. runTimeTrainAI Laufzeit des letzten KI Trainings aitrainLastFinishTs Timestamp des letzten erfolgreichen KI Trainings + aiRulesNumber Anzahl der Regeln in der trainierten KI Instanz tdayDvtn heutige Abweichung PV Prognose/Erzeugung in % temp Außentemperatur wcc Grad der Wolkenüberdeckung @@ -23203,6 +23224,7 @@ die ordnungsgemäße Anlagenkonfiguration geprüft werden. "FHEM::SynoModules::SMUtils": 1.0270, "Time::HiRes": 0, "MIME::Base64": 0, + "Math::Trig": 0, "Storable": 0 }, "recommends": {