diff --git a/fhem/contrib/DS_Starter/76_SolarForecast.pm b/fhem/contrib/DS_Starter/76_SolarForecast.pm index 9e65d47d1..7962e567d 100644 --- a/fhem/contrib/DS_Starter/76_SolarForecast.pm +++ b/fhem/contrib/DS_Starter/76_SolarForecast.pm @@ -108,12 +108,12 @@ my %vNotesIntern = ( # Voreinstellungen my %hset = ( # Hash der Set-Funktion - forecastDevice => { fn => \&_setforecastDevice }, + currentForecastDev => { fn => \&_setcurrentForecastDev }, moduleArea => { fn => \&_setmoduleArea }, moduleEfficiency => { fn => \&_setmoduleEfficiency }, inverterEfficiency => { fn => \&_setinverterEfficiency }, - inverterDevice => { fn => \&_setinverterDevice }, - meterDevice => { fn => \&_setmeterDevice }, + currentInverterDev => { fn => \&_setinverterDevice }, + currentMeterDev => { fn => \&_setmeterDevice }, pvCorrectionFactor_05 => { fn => \&_setpvCorrectionFactor }, pvCorrectionFactor_06 => { fn => \&_setpvCorrectionFactor }, pvCorrectionFactor_07 => { fn => \&_setpvCorrectionFactor }, @@ -132,11 +132,12 @@ my %hset = ( # Ha pvCorrectionFactor_20 => { fn => \&_setpvCorrectionFactor }, pvCorrectionFactor_21 => { fn => \&_setpvCorrectionFactor }, pvCorrectionFactor_Auto => { fn => \&_setpvCorrectionFactorAuto }, + reset => { fn => \&_setreset }, moduleTiltAngle => { fn => \&_setmoduleTiltAngle }, ); -my %htilt = ( # Neigungswinkel der Solarmodule - "0" => 1.00, # https://www.labri.fr/perso/billaud/travaux/Helios/Helios2/resources/de04/Chapter_4_DE.pdf +my %htilt = ( # Faktor für Neigungswinkel der Solarmodule + "0" => 1.00, # https://www.labri.fr/perso/billaud/travaux/Helios/Helios2/resources/de04/Chapter_4_DE.pdf "10" => 1.06, "20" => 1.15, "30" => 1.35, @@ -153,7 +154,7 @@ my @chours = (5..21); # St my $defpvme = 16.52; # default Wirkungsgrad Solarmodule my $definve = 98.3; # default Wirkungsgrad Wechselrichter my $kJtokWh = 0.00027778; # Umrechnungsfaktor kJ in kWh -my $maxvariance = 0.6; # max. Varianz pro Durchlauf Berechnung Autokorrekturfaktor +my $defmaxvar = 0.5; # max. Varianz pro Tagesberechnung Autokorrekturfaktor my $definterval = 70; # Standard Abfrageintervall ################################################################ @@ -259,14 +260,15 @@ sub Set { my $tilt = join ",", sort keys %htilt; $setlist = "Unknown argument $opt, choose one of ". - "forecastDevice:$fcd ". - "inverterDevice ". + "currentForecastDev:$fcd ". + "currentInverterDev:textField-long ". + "currentMeterDev:textField-long ". "inverterEfficiency ". - "meterDevice ". "moduleArea ". "moduleEfficiency ". "moduleTiltAngle:$tilt ". "pvCorrectionFactor_Auto:on,off ". + "reset:currentForecastDev,currentInverterDev,currentMeterDev ". $cf ; @@ -289,9 +291,9 @@ return "$setlist"; } ################################################################ -# Setter forecastDevice +# Setter currentForecastDev ################################################################ -sub _setforecastDevice { ## no critic "not used" +sub _setcurrentForecastDev { ## no critic "not used" my $paref = shift; my $hash = $paref->{hash}; my $name = $paref->{name}; @@ -308,7 +310,7 @@ return; } ################################################################ -# Setter inverterDevice +# Setter currentInverterDev ################################################################ sub _setinverterDevice { ## no critic "not used" my $paref = shift; @@ -335,7 +337,7 @@ return; } ################################################################ -# Setter meterDevice +# Setter currentMeterDev ################################################################ sub _setmeterDevice { ## no critic "not used" my $paref = shift; @@ -376,7 +378,30 @@ sub _setmoduleArea { ## no critic "not used" $prop =~ s/,/./x; - readingsSingleUpdate($hash, "moduleArea", $prop, 1); + readingsSingleUpdate($hash, "moduleArea", $prop." qm", 1); + +return; +} + +################################################################ +# Setter reset +################################################################ +sub _setreset { ## no critic "not used" + my $paref = shift; + my $hash = $paref->{hash}; + my $name = $paref->{name}; + my $prop = $paref->{prop} // return qq{no reading to reset specified}; + + readingsDelete($hash, $prop); + + if($prop eq "currentMeterDev") { + readingsDelete($hash, "Current_GridConsumption"); + } + + if($prop eq "currentInverterDev") { + readingsDelete ($hash, "Current_PV"); + deleteReadingspec ($hash, ".*_PVreal" ); + } return; } @@ -470,11 +495,11 @@ sub _setpvCorrectionFactor { ## no critic "not used" daref => \@da }; - _transferForecastValues ($params); + _transferDWDForecastValues ($params); if(@da) { push @da, "state:updated"; # Abschluß state - createReadings ($hash, \@da); + createReadingsFromArray ($hash, \@da, 1); } return; @@ -619,18 +644,19 @@ sub centralTask { daref => \@da }; - _transferForecastValues ($params); # Forecast Werte übertragen - _transferInverterValues ($params); # WR Werte übertragen - _transferMeterValues ($params); + _transferDWDForecastValues ($params); # Forecast Werte übertragen + _transWeatherValues ($params); # Wetterwerte übertragen + _transferInverterValues ($params); # WR Werte übertragen + _transferMeterValues ($params); if(@da) { - createReadings ($hash, \@da); + createReadingsFromArray ($hash, \@da, 1); } - sumNextHours ($hash, $chour, \@da); # Zusammenfassung nächste 4 Stunden erstellen - calcVariance ($params); # Autokorrektur berechnen + sumNextHours ($hash, $chour, \@da); # Zusammenfassung nächste 4 Stunden erstellen + calcVariance ($params); # Autokorrektur berechnen - readingsSingleUpdate($hash, "state", "updated", 1); # Abschluß state + readingsSingleUpdate($hash, "state", "updated", 1); # Abschluß state } else { InternalTimer(gettimeofday()+5, "FHEM::SolarForecast::centralTask", $hash, 0); @@ -651,9 +677,10 @@ return $interval; } ################################################################ -# Werte Forecast Device ermitteln und übertragen +# Forecast Werte Device (DWD_OpenData) ermitteln und +# übertragen ################################################################ -sub _transferForecastValues { +sub _transferDWDForecastValues { my $paref = shift; my $hash = $paref->{hash}; my $name = $paref->{name}; @@ -664,33 +691,21 @@ sub _transferForecastValues { my $fcname = ReadingsVal($name, "currentForecastDev", ""); # aktuelles Forecast Device return if(!$fcname || !$defs{$fcname}); - my ($time_str,$epoche,$fd,$v); - - my $fc0_SunRise = ReadingsVal($fcname, "fc0_SunRise", "00:00"); # Sonnenaufgang heute - my $fc0_SunSet = ReadingsVal($fcname, "fc0_SunSet", "00:00"); # Sonnenuntergang heute - my $fc1_SunRise = ReadingsVal($fcname, "fc1_SunRise", "00:00"); # Sonnenaufgang morgen - my $fc1_SunSet = ReadingsVal($fcname, "fc1_SunSet", "00:00"); # Sonnenuntergang morgen - - push @$daref, "Today_SunRise:". $fc0_SunRise; - push @$daref, "Today_SunSet:". $fc0_SunSet; - push @$daref, "Tomorrow_SunRise:".$fc1_SunRise; - push @$daref, "Tomorrow_SunSet:". $fc1_SunSet; + my ($time_str,$epoche); deleteReadingspec ($hash, "NextHour.*"); for my $num (0..47) { my $fh = $chour + $num; - $fd = int ($fh / 24) ; + my $fd = int ($fh / 24) ; $fh = $fh - ($fd * 24); next if($fd > 1); - $v = ReadingsVal($fcname, "fc${fd}_${fh}_Rad1h", 0); + my $v = ReadingsVal($fcname, "fc${fd}_${fh}_Rad1h", 0); - Log3($name, 5, "$name - collect DWD data: device=$fcname, rad=fc${fd}_${fh}_Rad1h, wid=fc${fd}_${fh}_ww, Val=$v"); + Log3($name, 5, "$name - collect DWD forecast data: device=$fcname, rad=fc${fd}_${fh}_Rad1h, Val=$v"); - ## PV Forecast - ############### if($num == 0) { $time_str = "ThisHour"; $epoche = $t; # Epoche Zeit @@ -703,19 +718,64 @@ sub _transferForecastValues { my $calcpv = calcPVforecast ($name, $v, $fh); # Vorhersage gewichtet kalkulieren push @$daref, "${time_str}_PVforecast:".$calcpv." Wh"; - push @$daref, "${time_str}_Time:" .TimeAdjust ($epoche); # Zeit fortschreiben + push @$daref, "${time_str}_Time:" .TimeAdjust ($epoche); # Zeit fortschreiben $hash->{HELPER}{"fc${fd}_".sprintf("%02d",$fh)."_PVforecast"} = $v." Wh"; # original Vorhersagedaten zur Berechnung Auto-Korrekturfaktor in Helper speichern if($fd == 0 && int $calcpv > 0) { # Vorhersagedaten des aktuellen Tages zum manuellen Vergleich in Reading speichern push @$daref, "Today_Hour".sprintf("%02d",$fh)."_PVforecast:$calcpv Wh"; } + } - ## Wetter Forecast - ################### +return; +} + +################################################################ +# Wetter Werte aus dem angebenen Wetterdevice extrahieren +################################################################ +sub _transWeatherValues { + my $paref = shift; + my $hash = $paref->{hash}; + my $name = $paref->{name}; + my $t = $paref->{t}; + my $chour = $paref->{chour}; + my $daref = $paref->{daref}; + + my $fcname = ReadingsVal($name, "currentForecastDev", ""); # aktuelles Forecast Device + return if(!$fcname || !$defs{$fcname}); + + my ($time_str,$epoche); + + my $fc0_SunRise = ReadingsVal($fcname, "fc0_SunRise", "00:00"); # Sonnenaufgang heute + my $fc0_SunSet = ReadingsVal($fcname, "fc0_SunSet", "00:00"); # Sonnenuntergang heute + my $fc1_SunRise = ReadingsVal($fcname, "fc1_SunRise", "00:00"); # Sonnenaufgang morgen + my $fc1_SunSet = ReadingsVal($fcname, "fc1_SunSet", "00:00"); # Sonnenuntergang morgen + + push @$daref, "Today_SunRise:". $fc0_SunRise; + push @$daref, "Today_SunSet:". $fc0_SunSet; + push @$daref, "Tomorrow_SunRise:".$fc1_SunRise; + push @$daref, "Tomorrow_SunSet:". $fc1_SunSet; + + + for my $num (0..47) { + my $fh = $chour + $num; + my $fd = int ($fh / 24) ; + $fh = $fh - ($fd * 24); + + next if($fd > 1); + + if($num == 0) { + $time_str = "ThisHour"; + $epoche = $t; # Epoche Zeit + } + else { + $time_str = "NextHour".sprintf "%02d", $num; + $epoche = $t + (3600*$num); + } my $wid = ReadingsVal($fcname, "fc${fd}_${fh}_ww", 0); $wid = sprintf "%02d", $wid; # führende 0 einfügen wenn nötig + my $fhstr = sprintf "%02d", $fh; if($fd == 0 && ($fhstr lt $fc0_SunRise || $fhstr gt $fc0_SunSet)) { # Zeit vor Sonnenaufgang oder nach Sonnenuntergang heute @@ -725,6 +785,8 @@ sub _transferForecastValues { $wid = "1".$wid; # "1" der WeatherID voranstellen wenn Nacht } + Log3($name, 5, "$name - collect Weather data: device=$fcname, wid=fc${fd}_${fh}_ww, val=$wid"); + $hash->{HELPER}{"${time_str}_WeatherId"} = $wid; } @@ -747,7 +809,7 @@ sub _transferInverterValues { $indev = $a->[0] // ""; return if(!$indev || !$defs{$indev}); - my $tlim = "0|23"; # Stunde 0/23 -> bestimmte Aktionen + my $tlim = "00|23"; # Stunde 00/23 -> bestimmte Aktionen if($chour =~ /^($tlim)$/x) { deleteReadingspec ($hash, "Today_Hour.*_PV.*"); @@ -776,14 +838,13 @@ sub _transferInverterValues { my $ethishour = $etoday - $edaypast; - #if($ethishour < 0) { # fehlerhaft berechnete etoday eliminieren - # push @$daref, "Today_Hour".sprintf("%02d",$chour-1)."_PVreal:0 Wh"; - # push @$daref, "Today_Hour".sprintf("%02d",$chour)."_PVreal:0 Wh"; - if($chour <= 3 && $ethishour > 0) { - push @$daref, "Today_Hour".sprintf("%02d",$chour)."_PVreal:0 Wh"; - } - else { - push @$daref, "Today_Hour".sprintf("%02d",$chour)."_PVreal:". $ethishour." Wh" if($chour !~ /^($tlim)$/x); # nicht setzen wenn Stunde 23 des Tages + if($chour !~ /^($tlim)$/x) { # nicht setzen wenn Stunde 23 des Tages + if($ethishour < 0) { + push @$daref, "Today_Hour".sprintf("%02d",$chour)."_PVreal:0 Wh"; + } + else { + push @$daref, "Today_Hour".sprintf("%02d",$chour)."_PVreal:". $ethishour." Wh"; + } } return; @@ -992,10 +1053,10 @@ sub forecastGraphic { $ret .= "
forecastDays | 1 |
forecastProperties | Rad1h,TTT,Neff,R600,ww,SunUp,SunRise,SunSet |
forecastResolution | 1 |
forecastStation | <Stationscode der ausgewerteten DWD Station> |
forecastDays | 1 |
forecastProperties | Rad1h,TTT,Neff,R600,ww,SunUp,SunRise,SunSet |
forecastResolution | 1 |
forecastStation | <Stationscode der ausgewerteten DWD Station> |
Hinweis: Die ausgewählte forecastStation muß Strahlungswerte (Rad1h Readings) liefern. |