From db189d9208a32351f4af3b2a167da1e91ea8802d Mon Sep 17 00:00:00 2001 From: nasseeder1 Date: Thu, 3 Jun 2021 16:05:30 +0000 Subject: [PATCH] 76_SolarForecast.pm: contrib 0.51.0 git-svn-id: https://svn.fhem.de/fhem/trunk@24578 2b470e98-0d58-463d-a4d8-8e2adae1ed80 --- fhem/contrib/DS_Starter/76_SolarForecast.pm | 386 +++++++++++++------- 1 file changed, 258 insertions(+), 128 deletions(-) diff --git a/fhem/contrib/DS_Starter/76_SolarForecast.pm b/fhem/contrib/DS_Starter/76_SolarForecast.pm index cea4df56c..d36065eb2 100644 --- a/fhem/contrib/DS_Starter/76_SolarForecast.pm +++ b/fhem/contrib/DS_Starter/76_SolarForecast.pm @@ -33,7 +33,8 @@ use Encode; use utf8; eval "use JSON;1;" or my $jsonabs = "JSON"; ## no critic 'eval' # Debian: apt-get install libjson-perl -use FHEM::SynoModules::SMUtils qw( evaljson +use FHEM::SynoModules::SMUtils qw( + evaljson moduleVersion trim ); # Hilfsroutinen Modul @@ -117,7 +118,8 @@ BEGIN { # Versions History intern my %vNotesIntern = ( - "0.50.3" => "03.06.2021 some bugfixing ", + "0.51.0" => "03.06.2021 some bugfixing, Calculation of PV correction factors refined, new setter plantConfiguration ". + "delete getter stringConfig ", "0.50.2" => "02.06.2021 more refactoring, delete attr headerAlignment, consumerlegend as table ", "0.50.1" => "02.06.2021 switch to mathematical rounding of cloudiness range ", "0.50.0" => "01.06.2021 real switch off time in consumerXX_planned_stop when finished, change key 'ready' to 'auto' ". @@ -209,6 +211,7 @@ my %hset = ( # Ha currentMeterDev => { fn => \&_setmeterDevice }, currentBatteryDev => { fn => \&_setbatteryDevice }, energyH4Trigger => { fn => \&_setenergyH4Trigger }, + plantConfiguration => { fn => \&_setplantConfiguration }, powerTrigger => { fn => \&_setpowerTrigger }, pvCorrectionFactor_05 => { fn => \&_setpvCorrectionFactor }, pvCorrectionFactor_06 => { fn => \&_setpvCorrectionFactor }, @@ -244,7 +247,6 @@ my %hget = ( # Ha pvCircular => { fn => \&_getlistPVCircular, needcred => 0 }, forecastQualities => { fn => \&_getForecastQualities, needcred => 0 }, nextHours => { fn => \&_getlistNextHours, needcred => 0 }, - stringConfig => { fn => \&_getstringConfig, needcred => 0 }, ); my %hattr = ( # Hash für Attr-Funktion @@ -265,25 +267,29 @@ my %hff = ( "90" => { N => 33, NE => 43, E => 62, SE => 78, S => 85, SW => 78, W => 62, NW => 43 }, ); # mt = default mintime (Minuten) -my %hqtxt = ( # Hash Setup Texte - cfd => { EN => qq{Please select the Weather forecast device with "set LINK currentForecastDev"}, - DE => qq{Bitte geben sie das Wettervorhersage Device mit "set LINK currentForecastDev" an} }, - crd => { EN => qq{Please select the Radiation forecast device with "set LINK currentRadiationDev"}, - DE => qq{Bitte geben sie das Strahlungsvorhersage Device mit "set LINK currentRadiationDev" an} }, - cid => { EN => qq{Please specify the Inverter device with "set LINK currentInverterDev"}, - DE => qq{Bitte geben sie das Wechselrichter Device mit "set LINK currentInverterDev" an} }, - mid => { EN => qq{Please specify the device for energy measurement with "set LINK currentMeterDev"}, - DE => qq{Bitte geben sie das Device zur Energiemessung mit "set LINK currentMeterDev" an} }, - ist => { EN => qq{Please define all of your used string names with "set LINK inverterStrings"}, - DE => qq{Bitte geben sie alle von Ihnen verwendeten Stringnamen mit "set LINK inverterStrings" an} }, - mps => { EN => qq{Please specify the total peak power for every string with "set LINK modulePeakString"}, - DE => qq{Bitte geben sie die Gesamtspitzenleistung von jedem String mit "set LINK modulePeakString" an} }, - mdr => { EN => qq{Please specify the module direction with "set LINK moduleDirection"}, - DE => qq{Bitte geben Sie die Modulausrichtung mit "set LINK moduleDirection" an} }, - mta => { EN => qq{Please specify the module tilt angle with "set LINK moduleTiltAngle"}, - DE => qq{Bitte geben Sie den Modulneigungswinkel mit "set LINK moduleTiltAngle" an} }, - awd => { EN => qq{Awaiting data from Solar Forecast devices ...}, - DE => qq{Warten auf Daten von Solarvorhersagegeräten ...} }, +my %hqtxt = ( # Hash (Setup) Texte + cfd => { EN => qq{Please select the Weather forecast device with "set LINK currentForecastDev"}, + DE => qq{Bitte geben sie das Wettervorhersage Device mit "set LINK currentForecastDev" an} }, + crd => { EN => qq{Please select the Radiation forecast device with "set LINK currentRadiationDev"}, + DE => qq{Bitte geben sie das Strahlungsvorhersage Device mit "set LINK currentRadiationDev" an} }, + cid => { EN => qq{Please specify the Inverter device with "set LINK currentInverterDev"}, + DE => qq{Bitte geben sie das Wechselrichter Device mit "set LINK currentInverterDev" an} }, + mid => { EN => qq{Please specify the device for energy measurement with "set LINK currentMeterDev"}, + DE => qq{Bitte geben sie das Device zur Energiemessung mit "set LINK currentMeterDev" an} }, + ist => { EN => qq{Please define all of your used string names with "set LINK inverterStrings"}, + DE => qq{Bitte geben sie alle von Ihnen verwendeten Stringnamen mit "set LINK inverterStrings" an} }, + mps => { EN => qq{Please specify the total peak power for every string with "set LINK modulePeakString"}, + DE => qq{Bitte geben sie die Gesamtspitzenleistung von jedem String mit "set LINK modulePeakString" an} }, + mdr => { EN => qq{Please specify the module direction with "set LINK moduleDirection"}, + DE => qq{Bitte geben Sie die Modulausrichtung mit "set LINK moduleDirection" an} }, + mta => { EN => qq{Please specify the module tilt angle with "set LINK moduleTiltAngle"}, + DE => qq{Bitte geben Sie den Modulneigungswinkel mit "set LINK moduleTiltAngle" an} }, + awd => { EN => qq{Awaiting data from Solar Forecast devices ...}, + DE => qq{Warten auf Daten von Solarvorhersagegeräten ...} }, + strok => { EN => qq{Congratulations 😊, your string configuration checked without found errors !}, + DE => qq{Herzlichen Glückwunsch 😊, Ihre String-Konfiguration wurde ohne gefundene Fehler geprüft!} }, + strnok => { EN => qq{Oh no 🙁, your string configuration is inconsistent.\nPlease check the settings of modulePeakString, moduleDirection, moduleTiltAngle !}, + DE => qq{Oh nein 🙁, Ihre String-Konfiguration ist inkonsistent.\nBitte überprüfen Sie die Einstellungen von modulePeakString, moduleDirection, moduleTiltAngle !}}, ); my %htitles = ( # Hash Hilfetexte @@ -438,6 +444,7 @@ my $defslidenum = 3; my $pvhcache = $attr{global}{modpath}."/FHEM/FhemUtils/PVH_SolarForecast_"; # Filename-Fragment für PV History (wird mit Devicename ergänzt) my $pvccache = $attr{global}{modpath}."/FHEM/FhemUtils/PVC_SolarForecast_"; # Filename-Fragment für PV Circular (wird mit Devicename ergänzt) +my $plantcfg = $attr{global}{modpath}."/FHEM/FhemUtils/PVCfg_SolarForecast_"; # Filename-Fragment für PV Anlagenkonfiguration (wird mit Devicename ergänzt) my $calcmaxd = 30; # Anzahl Tage die zur Berechnung Vorhersagekorrektur verwendet werden my @dweattrmust = qw(TTT Neff R101 ww SunUp SunRise SunSet); # Werte die im Attr forecastProperties des Weather-DWD_Opendata Devices mindestens gesetzt sein müssen @@ -473,7 +480,6 @@ my %hef = ( # $data{$type}{$name}{pvhist} # historische Werte # $data{$type}{$name}{nexthours} # NextHours Werte # $data{$type}{$name}{consumers} # Consumer Hash -# $data{$type}{$name}{html} # hfcg = hash forecast graphic # $data{$type}{$name}{strings} # Stringkonfiguration ################################################################ @@ -679,6 +685,7 @@ sub Set { "modulePeakString ". "moduleTiltAngle ". "moduleDirection ". + "plantConfiguration:check,save,restore ". "powerTrigger:textField-long ". "pvCorrectionFactor_Auto:on,off ". "reset:$resets ". @@ -721,6 +728,7 @@ sub _setcurrentForecastDev { ## no critic "not used" readingsSingleUpdate($hash, "currentForecastDev", $prop, 1); createNotifyDev ($hash); + writeDataToFile ($hash, "plantconfig", $plantcfg.$name); # Anlagenkonfiguration File schreiben return; } @@ -740,6 +748,7 @@ sub _setcurrentRadiationDev { ## no critic "not used" readingsSingleUpdate($hash, "currentRadiationDev", $prop, 1); createNotifyDev ($hash); + writeDataToFile ($hash, "plantconfig", $plantcfg.$name); # Anlagenkonfiguration File schreiben return; } @@ -771,6 +780,7 @@ sub _setinverterDevice { ## no critic "not used" readingsSingleUpdate($hash, "currentInverterDev", $arg, 1); createNotifyDev ($hash); + writeDataToFile ($hash, "plantconfig", $plantcfg.$name); # Anlagenkonfiguration File schreiben return; } @@ -784,7 +794,8 @@ sub _setinverterStrings { ## no critic "not used" my $name = $paref->{name}; my $prop = $paref->{prop} // return qq{no inverter strings specified}; - readingsSingleUpdate($hash, "inverterStrings", $prop, 1); + readingsSingleUpdate($hash, "inverterStrings", $prop, 1); + writeDataToFile ($hash, "plantconfig", $plantcfg.$name); # Anlagenkonfiguration File schreiben return qq{REMINDER - After setting or changing "inverterStrings" please check / set all module parameter (e.g. moduleTiltAngle) again !}; } @@ -820,6 +831,7 @@ sub _setmeterDevice { ## no critic "not used" readingsSingleUpdate($hash, "currentMeterDev", $arg, 1); createNotifyDev ($hash); + writeDataToFile ($hash, "plantconfig", $plantcfg.$name); # Anlagenkonfiguration File schreiben return; } @@ -855,6 +867,7 @@ sub _setbatteryDevice { ## no critic "not used" readingsSingleUpdate($hash, "currentBatteryDev", $arg, 1); createNotifyDev ($hash); + writeDataToFile ($hash, "plantconfig", $plantcfg.$name); # Anlagenkonfiguration File schreiben return; } @@ -884,6 +897,8 @@ sub _setpowerTrigger { ## no critic "not used" return qq{The key "$key" is invalid. Please consider the commandref.}; } } + + writeDataToFile ($hash, "plantconfig", $plantcfg.$name); # Anlagenkonfiguration File schreiben readingsSingleUpdate($hash, "powerTrigger", $arg, 1); @@ -915,6 +930,8 @@ sub _setenergyH4Trigger { ## no critic "not used" return qq{The key "$key" is invalid. Please consider the commandref.}; } } + + writeDataToFile ($hash, "plantconfig", $plantcfg.$name); # Anlagenkonfiguration File schreiben readingsSingleUpdate($hash, "energyH4Trigger", $arg, 1); @@ -948,6 +965,8 @@ sub _setmodulePeakString { ## no critic "not used" my $ret = createStringConfig ($hash); return $ret if($ret); + + writeDataToFile ($hash, "plantconfig", $plantcfg.$name); # Anlagenkonfiguration File schreiben return; } @@ -978,7 +997,9 @@ sub _setmoduleTiltAngle { ## no critic "not used" readingsSingleUpdate($hash, "moduleTiltAngle", $arg, 1); my $ret = createStringConfig ($hash); - return $ret if($ret); + return $ret if($ret); + + writeDataToFile ($hash, "plantconfig", $plantcfg.$name); # Anlagenkonfiguration File schreiben return; } @@ -1010,10 +1031,68 @@ sub _setmoduleDirection { ## no critic "not used" my $ret = createStringConfig ($hash); return $ret if($ret); + + writeDataToFile ($hash, "plantconfig", $plantcfg.$name); # Anlagenkonfiguration File schreiben return; } +################################################################ +# Setter plantConfiguration +################################################################ +sub _setplantConfiguration { ## no critic "not used" + my $paref = shift; + my $hash = $paref->{hash}; + my $name = $paref->{name}; + my $opt = $paref->{opt}; + my $arg = $paref->{arg}; + + if(!$arg) { + return qq{The command "$opt" needs an argument !}; + } + + if($arg eq "check") { + my $ret = checkStringConfig ($hash); + return qq{$ret}; + } + + if($arg eq "save") { + my $error = writeDataToFile ($hash, "plantconfig", $plantcfg.$name); # Anlagenkonfiguration File schreiben + if($error) { + return $error; + } + else { + return qq{Plant Configuration has been written to file "$plantcfg.$name"}; + } + } + + if($arg eq "restore") { + my ($error, @pvconf) = FileRead ($plantcfg.$name); + + if(!$error) { + my $rbit = 0; + for my $elem (@pvconf) { + my ($reading, $val) = split "<>", $elem; + next if(!$reading || !defined $val); + CommandSetReading (undef,"$name $reading $val"); + $rbit = 1; + } + + if($rbit) { + return qq{Plant Configuration restored from file "$plantcfg.$name"}; + } + else { + return qq{The Plant Configuration file "$plantcfg.$name" was empty, nothing restored}; + } + } + else { + return $error; + } + } + +return; +} + ################################################################ # Setter pvCorrectionFactor ################################################################ @@ -1097,11 +1176,13 @@ sub _setreset { ## no critic "not used" if($prop eq "powerTrigger") { deleteReadingspec ($hash, "powerTrigger.*"); + writeDataToFile ($hash, "plantconfig", $plantcfg.$name); # Anlagenkonfiguration File schreiben return; } if($prop eq "energyH4Trigger") { deleteReadingspec ($hash, "energyH4Trigger.*"); + writeDataToFile ($hash, "plantconfig", $plantcfg.$name); # Anlagenkonfiguration File schreiben return; } @@ -1119,6 +1200,8 @@ sub _setreset { ## no critic "not used" delete $data{$type}{$name}{current}{autarkyrate}; delete $data{$type}{$name}{current}{selfconsumption}; delete $data{$type}{$name}{current}{selfconsumptionrate}; + + writeDataToFile ($hash, "plantconfig", $plantcfg.$name); # Anlagenkonfiguration File schreiben } if($prop eq "currentBatteryDev") { @@ -1128,15 +1211,18 @@ sub _setreset { ## no critic "not used" delete $data{$type}{$name}{current}{powerbatout}; delete $data{$type}{$name}{current}{powerbatin}; delete $data{$type}{$name}{current}{batcharge}; + + writeDataToFile ($hash, "plantconfig", $plantcfg.$name); # Anlagenkonfiguration File schreiben } if($prop eq "currentInverterDev") { readingsDelete ($hash, "Current_PV"); deleteReadingspec ($hash, ".*_PVreal" ); + writeDataToFile ($hash, "plantconfig", $plantcfg.$name); # Anlagenkonfiguration File schreiben } - if($prop eq "consumerPlanning") { # Verbraucherplanung resetten - my $c = $paref->{prop1} // ""; # bestimmten Verbraucher setzen falls angegeben + if($prop eq "consumerPlanning") { # Verbraucherplanung resetten + my $c = $paref->{prop1} // ""; # bestimmten Verbraucher setzen falls angegeben if ($c) { deleteConsumerPlanning ($hash, $c); @@ -1167,8 +1253,8 @@ sub _setwriteHistory { ## no critic "not used" my $ret; - $ret = writeCacheToFile ($hash, "circular", $pvccache.$name); # Cache File für PV Circular schreiben - $ret = writeCacheToFile ($hash, "pvhist", $pvhcache.$name); # Cache File für PV History schreiben + $ret = writeDataToFile ($hash, "circular", $pvccache.$name); # Cache File für PV Circular schreiben + $ret = writeDataToFile ($hash, "pvhist", $pvhcache.$name); # Cache File für PV History schreiben return $ret; } @@ -1230,7 +1316,6 @@ sub Get { "nextHours:noArg ". "pvCircular:noArg ". "pvHistory:noArg ". - "stringConfig:noArg ". "valCurrent:noArg " ; @@ -1358,18 +1443,6 @@ sub _getlistvalConsumerMaster { return $ret; } -############################################################### -# Getter stringConfig -############################################################### -sub _getstringConfig { - my $paref = shift; - my $hash = $paref->{hash}; - - my $ret = checkStringConfig ($hash); - -return $ret; -} - ################################################################ sub Attr { my $cmd = shift; @@ -1568,8 +1641,8 @@ sub Shutdown { my $name = $hash->{NAME}; my $type = $hash->{TYPE}; - writeCacheToFile ($hash, "pvhist", $pvhcache.$name); # Cache File für PV History schreiben - writeCacheToFile ($hash, "circular", $pvccache.$name); # Cache File für PV Circular schreiben + writeDataToFile ($hash, "pvhist", $pvhcache.$name); # Cache File für PV History schreiben + writeDataToFile ($hash, "circular", $pvccache.$name); # Cache File für PV Circular schreiben return; } @@ -1607,24 +1680,123 @@ sub Delete { my $arg = shift; my $name = $hash->{NAME}; - my $file = $pvhcache.$name; # Cache File PV History löschen + my $file = $pvhcache.$name; # Cache File PV History löschen my $error = FileDelete($file); if ($error) { - Log3 ($name, 1, qq{$name - ERROR deleting cache file "$file": $error}); + Log3 ($name, 1, qq{$name - ERROR deleting file "$file": $error}); } $error = qq{}; - $file = $pvccache.$name; # Cache File PV Circular löschen + $file = $pvccache.$name; # Cache File PV Circular löschen $error = FileDelete($file); if ($error) { - Log3 ($name, 1, qq{$name - ERROR deleting cache file "$file": $error}); + Log3 ($name, 1, qq{$name - ERROR deleting file "$file": $error}); + } + + + $error = qq{}; + $file = $plantcfg.$name; # File Anlagenkonfiguration löschen + $error = FileDelete($file); + + if ($error) { + Log3 ($name, 1, qq{$name - ERROR deleting file "$file": $error}); } return; } +################################################################ +# Timer für historische Daten schreiben +################################################################ +sub periodicWriteCachefiles { + my $hash = shift; + my $name = $hash->{NAME}; + + RemoveInternalTimer($hash, "FHEM::SolarForecast::periodicWriteCachefiles"); + InternalTimer (gettimeofday()+$whistrepeat, "FHEM::SolarForecast::periodicWriteCachefiles", $hash, 0); + + return if(IsDisabled($name)); + + writeDataToFile ($hash, "circular", $pvccache.$name); # Cache File für PV Circular schreiben + writeDataToFile ($hash, "pvhist", $pvhcache.$name); # Cache File für PV History schreiben + +return; +} + +################################################################ +# Daten in File wegschreiben +################################################################ +sub writeDataToFile { + my $hash = shift; + my $cachename = shift; + my $file = shift; + + my $name = $hash->{NAME}; + my $type = $hash->{TYPE}; + + my @data; + + if($cachename eq "plantconfig") { + @data = _savePlantConfig ($hash); + return "Plant configuration is empty, no data has been written" if(!@data); + } + else { + return if(!$data{$type}{$name}{$cachename}); + my $json = encode_json ($data{$type}{$name}{$cachename}); + push @data, $json; + } + + my $error = FileWrite($file, @data); + + if ($error) { + my $err = qq{ERROR writing cache file "$file": $error}; + Log3 ($name, 1, "$name - $err"); + readingsSingleUpdate($hash, "state", "ERROR writing cache file $file - $error", 1); + return $err; + } + else { + my $lw = gettimeofday(); + $hash->{HISTFILE} = "last write time: ".FmtTime($lw)." File: $file" if($cachename eq "pvhist"); + readingsSingleUpdate($hash, "state", "wrote cachefile $cachename successfully", 1); + } + +return; +} + +################################################################ +# Anlagenkonfiguration sichern +################################################################ +sub _savePlantConfig { + my $hash = shift; + my $name = $hash->{NAME}; + + my @pvconf; + + my @aconfigs = qw( + currentBatteryDev + currentForecastDev + currentInverterDev + currentMeterDev + currentRadiationDev + inverterStrings + moduleDirection + modulePeakString + moduleTiltAngle + powerTrigger + energyH4Trigger + ); + + for my $cfg (@aconfigs) { + my $val = ReadingsVal($name, $cfg, ""); + next if(!$val); + push @pvconf, $cfg."<>".$val; + } + +return @pvconf; +} + ################################################################ # Zentraler Datenabruf ################################################################ @@ -1813,59 +1985,6 @@ sub controlParams { return $interval; } -################################################################ -# Timer für historische Daten schreiben -################################################################ -sub periodicWriteCachefiles { - my $hash = shift; - my $name = $hash->{NAME}; - - RemoveInternalTimer($hash, "FHEM::SolarForecast::periodicWriteCachefiles"); - InternalTimer (gettimeofday()+$whistrepeat, "FHEM::SolarForecast::periodicWriteCachefiles", $hash, 0); - - return if(IsDisabled($name)); - - writeCacheToFile ($hash, "circular", $pvccache.$name); # Cache File für PV Circular schreiben - writeCacheToFile ($hash, "pvhist", $pvhcache.$name); # Cache File für PV History schreiben - -return; -} - -################################################################ -# historische Daten in File wegschreiben -################################################################ -sub writeCacheToFile { - my $hash = shift; - my $cachename = shift; - my $file = shift; - - my $name = $hash->{NAME}; - my $type = $hash->{TYPE}; - - return if(!$data{$type}{$name}{$cachename}); - - my @pvh; - - my $json = encode_json ($data{$type}{$name}{$cachename}); - push @pvh, $json; - - my $error = FileWrite($file, @pvh); - - if ($error) { - my $err = qq{ERROR writing cache file "$file": $error}; - Log3 ($name, 1, "$name - $err"); - readingsSingleUpdate($hash, "state", "ERROR writing cache file $file - $error", 1); - return $err; - } - else { - my $lw = gettimeofday(); - $hash->{HISTFILE} = "last write time: ".FmtTime($lw)." File: $file" if($cachename eq "pvhist"); - readingsSingleUpdate($hash, "state", "wrote cachefile $cachename successfully", 1); - } - -return; -} - ################################################################ # Zusätzliche Readings/ Events für Logging generieren und # Sonderaufgaben ! @@ -5013,11 +5132,11 @@ sub calcVariance { Log3 ($name, 5, "$name - Hour: ".sprintf("%02d",$h).", Today PVreal: $pvval, PVforecast: $fcval"); - $paref->{hour} = $h; - my ($pvavg,$fcavg,$anzavg,$range) = calcAvgFromHistory ($paref); # historische PV / Forecast Vergleichswerte ermitteln - $anzavg //= 1; # der aktuelle Wert ist nun der erste AVG im Store - $pvval = $pvavg ? ($pvval + $pvavg) / 2 : $pvval; # Ertrag aktuelle Stunde berücksichtigen - $fcval = $fcavg ? ($fcval + $fcavg) / 2 : $fcval; # Vorhersage aktuelle Stunde berücksichtigen + $paref->{hour} = $h; + my ($pvhis,$fchis,$dnum,$range) = calcAvgFromHistory ($paref); # historische PV / Forecast Vergleichswerte ermitteln + $dnum = $dnum ? $dnum++ : 1; # der aktuelle Wert ist nun der erste AVG im Store + $pvval = $pvhis ? ($pvval + $pvhis) / $dnum : $pvval; # Ertrag aktuelle Stunde berücksichtigen + $fcval = $fchis ? ($fcval + $fchis) / $dnum : $fcval; # Vorhersage aktuelle Stunde berücksichtigen my ($oldfac, $oldq) = CircularAutokorrVal ($hash, sprintf("%02d",$h), $range, 0); # bisher definierter Korrekturfaktor/KF-Qualität der Stunde des Tages der entsprechenden Bewölkungsrange $oldfac = 1 if(1*$oldfac == 0); @@ -5038,13 +5157,13 @@ sub calcVariance { Log3 ($name, 5, "$name - write correction factor into circular Hash: Factor $factor, Hour $h, Range $range"); $data{$type}{$name}{circular}{sprintf("%02d",$h)}{pvcorrf}{$range} = $factor; # Korrekturfaktor für Bewölkung Range 0..10 für die jeweilige Stunde als Datenquelle eintragen - $data{$type}{$name}{circular}{sprintf("%02d",$h)}{quality}{$range} = $anzavg; # Korrekturfaktor Qualität + $data{$type}{$name}{circular}{sprintf("%02d",$h)}{quality}{$range} = $dnum; # Korrekturfaktor Qualität } else { $range = ""; } - push @da, "pvCorrectionFactor_".sprintf("%02d",$h)."<>".$factor." (automatic - old factor: $oldfac, cloudiness range: $range, found history days in range: $anzavg)"; + push @da, "pvCorrectionFactor_".sprintf("%02d",$h)."<>".$factor." (automatic - old factor: $oldfac, cloudiness range: $range, days in range: $dnum)"; push @da, "pvCorrectionFactor_".sprintf("%02d",$h)."_autocalc<>done"; } @@ -5105,7 +5224,7 @@ sub calcAvgFromHistory { Log3 ($name, 4, "$name - cloudiness range of day/hour $day/$hour is: $range"); - my $anzavg = 0; + my $dnum = 0; my ($pvrl,$pvfc) = (0,0); for my $dayfa (@efa) { @@ -5121,24 +5240,24 @@ sub calcAvgFromHistory { if($range == $histwcc) { $pvrl += HistoryVal ($hash, $dayfa, $hour, "pvrl", 0); $pvfc += HistoryVal ($hash, $dayfa, $hour, "pvfc", 0); - $anzavg++; + $dnum++; Log3 ($name, 5, "$name - History Average -> current/historical cloudiness range identical: $range Day/hour $dayfa/$hour included."); - last if( $anzavg == $calcd); + last if( $dnum == $calcd); } else { Log3 ($name, 5, "$name - History Average -> current/historical cloudiness range different: $range/$histwcc Day/hour $dayfa/$hour discarded."); } } - if(!$anzavg) { + if(!$dnum) { Log3 ($name, 5, "$name - History Average -> all cloudiness ranges were different/not set -> no historical averages calculated"); return (undef,undef,undef,$range); } - my $pvavg = sprintf "%.2f", $pvrl / $anzavg; - my $fcavg = sprintf "%.2f", $pvfc / $anzavg; + my $pvhis = sprintf "%.2f", $pvrl; + my $fchis = sprintf "%.2f", $pvfc; - return ($pvavg,$fcavg,$anzavg,$range); + return ($pvhis,$fchis,$dnum,$range); } return; @@ -5596,6 +5715,7 @@ sub checkStringConfig { my $name = $hash->{NAME}; my $type = $hash->{TYPE}; my $stch = $data{$type}{$name}{strings}; + my $lang = AttrVal ("global", 'language', 'EN'); my $sub = sub { my $string = shift; @@ -5614,16 +5734,16 @@ sub checkStringConfig { my $sc; my $cf = 0; for my $sn (sort keys %{$stch}) { - my $sp = $sn." => ".$sub->($sn)."\n"; + my $sp = $sn." => ".$sub->($sn)."
"; $cf = 1 if($sp !~ /dir.*?peak.*?tilt/x); # Test Vollständigkeit: z.B. Süddach => dir: S, peak: 5.13, tilt: 45 $sc .= $sp; } if($cf) { - $sc .= "\n\nOh no 🙁, your string configuration is inconsistent.\nPlease check the settings of modulePeakString, moduleDirection, moduleTiltAngle !"; + $sc .= "

".encode ("utf8", $hqtxt{strnok}{$lang}); } else { - $sc .= "\n\nCongratulations 😊, your string configuration checked without found errors !"; + $sc .= "

".encode ("utf8", $hqtxt{strok}{$lang}); } return $sc; @@ -6143,7 +6263,7 @@ Ein/Ausschaltzeiten sowie deren Ausführung vom SolarForecast Modul übernehmen
+ +
+
- -
-