mirror of
https://github.com/fhem/fhem-mirror.git
synced 2025-05-03 13:45:36 +00:00
76_Solarforcast: contrib 0.69.0
git-svn-id: https://svn.fhem.de/fhem/trunk@26527 2b470e98-0d58-463d-a4d8-8e2adae1ed80
This commit is contained in:
parent
e1d2a39e62
commit
29ec17591e
@ -126,6 +126,7 @@ BEGIN {
|
|||||||
|
|
||||||
# Versions History intern
|
# Versions History intern
|
||||||
my %vNotesIntern = (
|
my %vNotesIntern = (
|
||||||
|
"0.69.0 "=> "12.10.2022 Autocorrection function for model SolCast-API, __solCast_ApiRequest: request only 48 hours ",
|
||||||
"0.68.7 "=> "07.10.2022 new function _calcCAQwithSolCastPercentil, check missed modules in _getRoofTopData ",
|
"0.68.7 "=> "07.10.2022 new function _calcCAQwithSolCastPercentil, check missed modules in _getRoofTopData ",
|
||||||
"0.68.6 "=> "06.10.2022 new attribute solCastPercentile, change _calcMaxEstimateToday ",
|
"0.68.6 "=> "06.10.2022 new attribute solCastPercentile, change _calcMaxEstimateToday ",
|
||||||
"0.68.5 "=> "03.10.2022 extent plant configuration check ",
|
"0.68.5 "=> "03.10.2022 extent plant configuration check ",
|
||||||
@ -912,7 +913,7 @@ sub Set {
|
|||||||
"reset:$resets ".
|
"reset:$resets ".
|
||||||
"roofIdentPair ".
|
"roofIdentPair ".
|
||||||
"writeHistory:noArg ".
|
"writeHistory:noArg ".
|
||||||
$cf
|
(!isSolCastUsed ($hash) ? $cf : q{});
|
||||||
;
|
;
|
||||||
|
|
||||||
my $params = {
|
my $params = {
|
||||||
@ -1485,6 +1486,7 @@ sub _setpvCorrectionFactorAuto { ## no critic "not used"
|
|||||||
$n = sprintf "%02d", $n;
|
$n = sprintf "%02d", $n;
|
||||||
my $rv = ReadingsVal ($name, "pvCorrectionFactor_${n}", "");
|
my $rv = ReadingsVal ($name, "pvCorrectionFactor_${n}", "");
|
||||||
deleteReadingspec ($hash, "pvCorrectionFactor_${n}.*") if($rv !~ /manual/xs);
|
deleteReadingspec ($hash, "pvCorrectionFactor_${n}.*") if($rv !~ /manual/xs);
|
||||||
|
deleteReadingspec ($hash, "pvSolCastPercentile_${n}.*");
|
||||||
}
|
}
|
||||||
|
|
||||||
deleteReadingspec ($hash, "pvCorrectionFactor_.*_autocalc");
|
deleteReadingspec ($hash, "pvCorrectionFactor_.*_autocalc");
|
||||||
@ -1853,11 +1855,11 @@ sub __solCast_ApiRequest {
|
|||||||
my $url = "https://api.solcast.com.au/rooftop_sites/".
|
my $url = "https://api.solcast.com.au/rooftop_sites/".
|
||||||
$roofid.
|
$roofid.
|
||||||
"/forecasts?format=json".
|
"/forecasts?format=json".
|
||||||
"&hours=72".
|
"&hours=48".
|
||||||
"&api_key=".
|
"&api_key=".
|
||||||
$apikey;
|
$apikey;
|
||||||
|
|
||||||
Log3 ($name, 4, qq{$name - Request SolCast API: $url});
|
Log3 ($name, 4, qq{$name - Request SolCast API for string "$string": $url});
|
||||||
|
|
||||||
my $caller = (caller(0))[3]; # Rücksprungmarke
|
my $caller = (caller(0))[3]; # Rücksprungmarke
|
||||||
|
|
||||||
@ -1973,7 +1975,7 @@ sub __solCast_ApiResponse {
|
|||||||
next;
|
next;
|
||||||
}
|
}
|
||||||
|
|
||||||
my $pvest = $jdata->{'forecasts'}[$k]{'pv_estimate'};
|
my $pvest50 = $jdata->{'forecasts'}[$k]{'pv_estimate'};
|
||||||
my $pvest10 = $jdata->{'forecasts'}[$k]{'pv_estimate10'};
|
my $pvest10 = $jdata->{'forecasts'}[$k]{'pv_estimate10'};
|
||||||
my $pvest90 = $jdata->{'forecasts'}[$k]{'pv_estimate90'};
|
my $pvest90 = $jdata->{'forecasts'}[$k]{'pv_estimate90'};
|
||||||
my $period = $jdata->{'forecasts'}[$k]{'period'};
|
my $period = $jdata->{'forecasts'}[$k]{'period'};
|
||||||
@ -1991,32 +1993,33 @@ sub __solCast_ApiResponse {
|
|||||||
if($debug) { # nur für Debugging
|
if($debug) { # nur für Debugging
|
||||||
if (exists $data{$type}{$name}{solcastapi}{$string}{$starttmstr}) {
|
if (exists $data{$type}{$name}{solcastapi}{$string}{$starttmstr}) {
|
||||||
Log (1, qq{DEBUG> $name SolCast API Hash - Start Date/Time: }. $starttmstr);
|
Log (1, qq{DEBUG> $name SolCast API Hash - Start Date/Time: }. $starttmstr);
|
||||||
Log (1, qq{DEBUG> $name SolCast API Hash - pv_estimate already present: }. $data{$type}{$name}{solcastapi}{$string}{$starttmstr}{pv_estimate});
|
Log (1, qq{DEBUG> $name SolCast API Hash - pv_estimate50 add: }.(sprintf "%.0f", ($pvest50 * ($period/60) * 1000)).qq{, contains already: }.SolCastAPIVal ($hash, $string, $starttmstr, 'pv_estimate50', 0));
|
||||||
Log (1, qq{DEBUG> $name SolCast API Hash - pv_estimate to add: }. ($pvest * ($period/60) * 1000));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$data{$type}{$name}{solcastapi}{$string}{$starttmstr}{pv_estimate} += sprintf "%.0f", ($pvest * ($period/60) * 1000);
|
$data{$type}{$name}{solcastapi}{$string}{$starttmstr}{pv_estimate50} += sprintf "%.0f", ($pvest50 * ($period/60) * 1000);
|
||||||
$data{$type}{$name}{solcastapi}{$string}{$starttmstr}{pv_estimate10} += sprintf "%.0f", ($pvest10 * ($period/60) * 1000);
|
$data{$type}{$name}{solcastapi}{$string}{$starttmstr}{pv_estimate10} += sprintf "%.0f", ($pvest10 * ($period/60) * 1000);
|
||||||
$data{$type}{$name}{solcastapi}{$string}{$starttmstr}{pv_estimate90} += sprintf "%.0f", ($pvest90 * ($period/60) * 1000);
|
$data{$type}{$name}{solcastapi}{$string}{$starttmstr}{pv_estimate90} += sprintf "%.0f", ($pvest90 * ($period/60) * 1000);
|
||||||
|
|
||||||
## erstellen Zusatzpercentile
|
## erstellen Zusatzpercentile
|
||||||
###############################
|
###############################
|
||||||
my $lowdm = ($data{$type}{$name}{solcastapi}{$string}{$starttmstr}{pv_estimate} - $data{$type}{$name}{solcastapi}{$string}{$starttmstr}{pv_estimate10}) / 4;
|
my $lowdm = ($data{$type}{$name}{solcastapi}{$string}{$starttmstr}{pv_estimate50} - $data{$type}{$name}{solcastapi}{$string}{$starttmstr}{pv_estimate10}) / 4;
|
||||||
my $highdm = ($data{$type}{$name}{solcastapi}{$string}{$starttmstr}{pv_estimate90} - $data{$type}{$name}{solcastapi}{$string}{$starttmstr}{pv_estimate}) / 4;
|
my $highdm = ($data{$type}{$name}{solcastapi}{$string}{$starttmstr}{pv_estimate90} - $data{$type}{$name}{solcastapi}{$string}{$starttmstr}{pv_estimate50}) / 4;
|
||||||
|
|
||||||
$data{$type}{$name}{solcastapi}{$string}{$starttmstr}{pv_estimate20} = sprintf "%.0f", ($data{$type}{$name}{solcastapi}{$string}{$starttmstr}{pv_estimate} - ($lowdm * 3));
|
$data{$type}{$name}{solcastapi}{$string}{$starttmstr}{pv_estimate20} = sprintf "%.0f", ($data{$type}{$name}{solcastapi}{$string}{$starttmstr}{pv_estimate50} - ($lowdm * 3));
|
||||||
$data{$type}{$name}{solcastapi}{$string}{$starttmstr}{pv_estimate30} = sprintf "%.0f", ($data{$type}{$name}{solcastapi}{$string}{$starttmstr}{pv_estimate} - ($lowdm * 2));
|
$data{$type}{$name}{solcastapi}{$string}{$starttmstr}{pv_estimate30} = sprintf "%.0f", ($data{$type}{$name}{solcastapi}{$string}{$starttmstr}{pv_estimate50} - ($lowdm * 2));
|
||||||
$data{$type}{$name}{solcastapi}{$string}{$starttmstr}{pv_estimate40} = sprintf "%.0f", ($data{$type}{$name}{solcastapi}{$string}{$starttmstr}{pv_estimate} - ($lowdm * 1));
|
$data{$type}{$name}{solcastapi}{$string}{$starttmstr}{pv_estimate40} = sprintf "%.0f", ($data{$type}{$name}{solcastapi}{$string}{$starttmstr}{pv_estimate50} - ($lowdm * 1));
|
||||||
|
|
||||||
$data{$type}{$name}{solcastapi}{$string}{$starttmstr}{pv_estimate60} = sprintf "%.0f", ($data{$type}{$name}{solcastapi}{$string}{$starttmstr}{pv_estimate} + ($highdm * 1));
|
$data{$type}{$name}{solcastapi}{$string}{$starttmstr}{pv_estimate60} = sprintf "%.0f", ($data{$type}{$name}{solcastapi}{$string}{$starttmstr}{pv_estimate50} + ($highdm * 1));
|
||||||
$data{$type}{$name}{solcastapi}{$string}{$starttmstr}{pv_estimate70} = sprintf "%.0f", ($data{$type}{$name}{solcastapi}{$string}{$starttmstr}{pv_estimate} + ($highdm * 2));
|
$data{$type}{$name}{solcastapi}{$string}{$starttmstr}{pv_estimate70} = sprintf "%.0f", ($data{$type}{$name}{solcastapi}{$string}{$starttmstr}{pv_estimate50} + ($highdm * 2));
|
||||||
$data{$type}{$name}{solcastapi}{$string}{$starttmstr}{pv_estimate80} = sprintf "%.0f", ($data{$type}{$name}{solcastapi}{$string}{$starttmstr}{pv_estimate} + ($highdm * 3));
|
$data{$type}{$name}{solcastapi}{$string}{$starttmstr}{pv_estimate80} = sprintf "%.0f", ($data{$type}{$name}{solcastapi}{$string}{$starttmstr}{pv_estimate50} + ($highdm * 3));
|
||||||
|
|
||||||
$k += 1;
|
$k += 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Log3 ($name, 4, qq{$name - SolCast API answer received for string "$string"});
|
||||||
|
|
||||||
___setLastAPIcallKeyData ($hash, $t);
|
___setLastAPIcallKeyData ($hash, $t);
|
||||||
|
|
||||||
$data{$type}{$name}{solcastapi}{'?All'}{'?All'}{response_message} = 'success';
|
$data{$type}{$name}{solcastapi}{'?All'}{'?All'}{response_message} = 'success';
|
||||||
@ -2792,6 +2795,7 @@ sub centralTask {
|
|||||||
_evaluateThresholds ($centpars); # Schwellenwerte bewerten und signalisieren
|
_evaluateThresholds ($centpars); # Schwellenwerte bewerten und signalisieren
|
||||||
_calcReadingsTomorrowPVFc ($centpars); # zusätzliche Readings Tomorrow_HourXX_PVforecast berechnen
|
_calcReadingsTomorrowPVFc ($centpars); # zusätzliche Readings Tomorrow_HourXX_PVforecast berechnen
|
||||||
_createSummaries ($centpars); # Zusammenfassungen erstellen
|
_createSummaries ($centpars); # Zusammenfassungen erstellen
|
||||||
|
_calcTodayPVdeviation ($centpars); # Vorhersageabweichung erstellen (nach Sonnenuntergang)
|
||||||
|
|
||||||
createReadingsFromArray ($hash, \@da, 1); # Readings erzeugen
|
createReadingsFromArray ($hash, \@da, 1); # Readings erzeugen
|
||||||
|
|
||||||
@ -3003,6 +3007,7 @@ sub _specialActivities {
|
|||||||
deleteReadingspec ($hash, "Today_Hour.*_Bat.*");
|
deleteReadingspec ($hash, "Today_Hour.*_Bat.*");
|
||||||
deleteReadingspec ($hash, "powerTrigger_.*");
|
deleteReadingspec ($hash, "powerTrigger_.*");
|
||||||
deleteReadingspec ($hash, "Today_MaxPVforecast.*");
|
deleteReadingspec ($hash, "Today_MaxPVforecast.*");
|
||||||
|
deleteReadingspec ($hash, "Today_PVdeviation");
|
||||||
|
|
||||||
if(ReadingsVal ($name, "pvCorrectionFactor_Auto", "off") eq "on") {
|
if(ReadingsVal ($name, "pvCorrectionFactor_Auto", "off") eq "on") {
|
||||||
for my $n (1..24) {
|
for my $n (1..24) {
|
||||||
@ -3445,7 +3450,6 @@ sub __calcSolCastEstimates {
|
|||||||
my $wantdt = $paref->{wantdt};
|
my $wantdt = $paref->{wantdt};
|
||||||
my $hod = $paref->{hod};
|
my $hod = $paref->{hod};
|
||||||
my $fd = $paref->{fd};
|
my $fd = $paref->{fd};
|
||||||
my $fh1 = $paref->{fh1};
|
|
||||||
my $num = $paref->{num};
|
my $num = $paref->{num};
|
||||||
|
|
||||||
my $type = $hash->{TYPE};
|
my $type = $hash->{TYPE};
|
||||||
@ -3463,10 +3467,7 @@ sub __calcSolCastEstimates {
|
|||||||
|
|
||||||
my $temp = NexthoursVal ($hash, "NextHour".sprintf("%02d",$num), "temp", $tempbasedef); # vorhergesagte Temperatur Stunde X
|
my $temp = NexthoursVal ($hash, "NextHour".sprintf("%02d",$num), "temp", $tempbasedef); # vorhergesagte Temperatur Stunde X
|
||||||
|
|
||||||
#my $range = calcRange ($cloudcover); # Range errechnen
|
my ($hcfound, $perc, $hq) = ___readPercAndQuality ($paref); # liest den anzuwendenden Korrekturfaktor
|
||||||
#$paref->{range} = $range;
|
|
||||||
#my ($hcfound, $hc, $hq) = ___readCorrfAndQuality ($paref); # liest den anzuwendenden Korrekturfaktor
|
|
||||||
#delete $paref->{range};
|
|
||||||
|
|
||||||
my ($lh,$sq);
|
my ($lh,$sq);
|
||||||
my $pvsum = 0;
|
my $pvsum = 0;
|
||||||
@ -3488,11 +3489,9 @@ sub __calcSolCastEstimates {
|
|||||||
|
|
||||||
$peak *= 1000;
|
$peak *= 1000;
|
||||||
|
|
||||||
my $perc = AttrVal ($name, 'solCastPercentile', 50); # Percentile Auswahl
|
my $edef = SolCastAPIVal ($hash, $string, $wantdt, 'pv_estimate', 0); # Back-Kompatibilität
|
||||||
$perc = q{} if($perc == 50);
|
my $ed50 = SolCastAPIVal ($hash, $string, $wantdt, 'pv_estimate50', $edef);
|
||||||
|
my $est = SolCastAPIVal ($hash, $string, $wantdt, 'pv_estimate'.$perc, $ed50);
|
||||||
my $est = SolCastAPIVal ($hash, $string, $wantdt, 'pv_estimate', 0); # Estimate mittleres Percentile
|
|
||||||
$est = SolCastAPIVal ($hash, $string, $wantdt, 'pv_estimate'.$perc, $est);
|
|
||||||
my $pv = sprintf "%.1f", ($est * $ccf * $rcf);
|
my $pv = sprintf "%.1f", ($est * $ccf * $rcf);
|
||||||
|
|
||||||
$lh = { # Log-Hash zur Ausgabe
|
$lh = { # Log-Hash zur Ausgabe
|
||||||
@ -3502,7 +3501,6 @@ sub __calcSolCastEstimates {
|
|||||||
"Module Temp (calculated)" => $modtemp." °C",
|
"Module Temp (calculated)" => $modtemp." °C",
|
||||||
"Loss String Peak Power by Temp" => $peakloss." kWP",
|
"Loss String Peak Power by Temp" => $peakloss." kWP",
|
||||||
"Cloudcover" => $cloudcover,
|
"Cloudcover" => $cloudcover,
|
||||||
#"CloudRange" => $range,
|
|
||||||
"CloudFactorDamping" => $clouddamp." %",
|
"CloudFactorDamping" => $clouddamp." %",
|
||||||
"Cloudfactor" => $ccf,
|
"Cloudfactor" => $ccf,
|
||||||
"Rainprob" => $rainprob,
|
"Rainprob" => $rainprob,
|
||||||
@ -3525,7 +3523,6 @@ sub __calcSolCastEstimates {
|
|||||||
|
|
||||||
$data{$type}{$name}{current}{allstringspeak} = $peaksum; # temperaturbedingte Korrektur der installierten Peakleistung in W
|
$data{$type}{$name}{current}{allstringspeak} = $peaksum; # temperaturbedingte Korrektur der installierten Peakleistung in W
|
||||||
|
|
||||||
#$pvsum *= $hc; # Korrekturfaktor anwenden
|
|
||||||
$pvsum = $peaksum if($pvsum > $peaksum); # Vorhersage nicht größer als die Summe aller PV-Strings Peak
|
$pvsum = $peaksum if($pvsum > $peaksum); # Vorhersage nicht größer als die Summe aller PV-Strings Peak
|
||||||
|
|
||||||
my $invcapacity = CurrentVal ($hash, "invertercapacity", 0); # Max. Leistung des Invertrs
|
my $invcapacity = CurrentVal ($hash, "invertercapacity", 0); # Max. Leistung des Invertrs
|
||||||
@ -3541,9 +3538,9 @@ sub __calcSolCastEstimates {
|
|||||||
($pvsum, $logao) = ___70percentRule ($paref);
|
($pvsum, $logao) = ___70percentRule ($paref);
|
||||||
|
|
||||||
$lh = { # Log-Hash zur Ausgabe
|
$lh = { # Log-Hash zur Ausgabe
|
||||||
#"CloudCorrFoundInStore" => $hcfound,
|
"CloudCorrFoundInStore" => $hcfound,
|
||||||
#"PV correction factor" => $hc,
|
"SolCast selected percentile" => $perc,
|
||||||
#"PV correction quality" => $hq,
|
"PV correction quality" => $hq,
|
||||||
"PV generation forecast" => $pvsum." Wh ".$logao,
|
"PV generation forecast" => $pvsum." Wh ".$logao,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -3557,6 +3554,56 @@ sub __calcSolCastEstimates {
|
|||||||
return $pvsum;
|
return $pvsum;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
######################################################################
|
||||||
|
# Liest das anzuwendende Percentil (Qualität) und
|
||||||
|
# speichert die Werte im Nexthours / PVhistory Hash
|
||||||
|
######################################################################
|
||||||
|
sub ___readPercAndQuality {
|
||||||
|
my $paref = shift;
|
||||||
|
my $hash = $paref->{hash};
|
||||||
|
my $name = $paref->{name};
|
||||||
|
my $num = $paref->{num}; # Nexthour
|
||||||
|
my $fh1 = $paref->{fh1};
|
||||||
|
my $fd = $paref->{fd};
|
||||||
|
|
||||||
|
my $type = $hash->{TYPE};
|
||||||
|
|
||||||
|
my $uac = ReadingsVal($name, "pvCorrectionFactor_Auto", "off"); # Auto- oder manuelle Korrektur
|
||||||
|
my $perc = ReadingsNum ($name, "pvSolCastPercentile_".sprintf("%02d",$fh1), 50); # Estimate Percentil
|
||||||
|
my $hcfound = "use manual percentile selection";
|
||||||
|
my $hq = "m";
|
||||||
|
|
||||||
|
if ($uac eq 'on') { # Autokorrektur soll genutzt werden
|
||||||
|
$hcfound = "yes"; # Status ob Autokorrekturfaktor im Wertevorrat gefunden wurde
|
||||||
|
($perc, $hq) = CircularAutokorrVal ($hash, sprintf("%02d",$fh1), 'percentile', undef); # Korrekturfaktor/KF-Qualität der Stunde des Tages der entsprechenden Bewölkungsrange
|
||||||
|
$hq //= 0;
|
||||||
|
if (!$perc) {
|
||||||
|
$hcfound = "no";
|
||||||
|
$perc = 50; # keine Korrektur
|
||||||
|
$hq = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
$perc = 50 if(!$perc);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$perc = AttrVal ($name, 'solCastPercentile', $perc); # Percentile manuelle Auswahl
|
||||||
|
}
|
||||||
|
|
||||||
|
$perc = sprintf "%.0f", $perc;
|
||||||
|
|
||||||
|
$data{$type}{$name}{nexthours}{"NextHour".sprintf("%02d",$num)}{pvcorrf} = $perc."/".$hq;
|
||||||
|
|
||||||
|
if($fd == 0 && $fh1) {
|
||||||
|
$paref->{pvcorrf} = $perc."/".$hq;
|
||||||
|
$paref->{nhour} = sprintf("%02d",$fh1);
|
||||||
|
$paref->{histname} = "pvcorrfactor";
|
||||||
|
setPVhistory ($paref);
|
||||||
|
delete $paref->{histname};
|
||||||
|
}
|
||||||
|
|
||||||
|
return ($hcfound, $perc, $hq);
|
||||||
|
}
|
||||||
|
|
||||||
###################################################################
|
###################################################################
|
||||||
# Zellen Leistungskorrektur Einfluss durch Wärmekoeffizienten
|
# Zellen Leistungskorrektur Einfluss durch Wärmekoeffizienten
|
||||||
# berechnen
|
# berechnen
|
||||||
@ -5572,6 +5619,34 @@ sub _createSummaries {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
################################################################
|
||||||
|
# berechnet die prozentuale Abweichung von Today_PVforecast
|
||||||
|
# und Today_PVreal
|
||||||
|
################################################################
|
||||||
|
sub _calcTodayPVdeviation {
|
||||||
|
my $paref = shift;
|
||||||
|
my $hash = $paref->{hash};
|
||||||
|
my $name = $paref->{name};
|
||||||
|
my $t = $paref->{t};
|
||||||
|
my $date = $paref->{date};
|
||||||
|
my $daref = $paref->{daref};
|
||||||
|
|
||||||
|
my $sstime = timestringToTimestamp ($date.' '.ReadingsVal($name, "Today_SunSet", '22:00').':00');
|
||||||
|
|
||||||
|
return if($t < $sstime);
|
||||||
|
|
||||||
|
my $pvfc = ReadingsNum ($name, 'Today_PVforecast', 0);
|
||||||
|
my $pvre = ReadingsNum ($name, 'Today_PVreal', 0);
|
||||||
|
|
||||||
|
my $diff = $pvfc - $pvre;
|
||||||
|
|
||||||
|
my $dp = sprintf("%.2f" , 100 * $diff / $pvre) if($pvre);
|
||||||
|
|
||||||
|
push @$daref, "Today_PVdeviation<>". $dp." %";
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
################################################################
|
################################################################
|
||||||
# Berechnen Forecast Tag / Stunden Verschieber
|
# Berechnen Forecast Tag / Stunden Verschieber
|
||||||
# aus aktueller Stunde + lfd. Nummer
|
# aus aktueller Stunde + lfd. Nummer
|
||||||
@ -6099,7 +6174,7 @@ sub _graphicHeader {
|
|||||||
|
|
||||||
my $pvcorrf00 = NexthoursVal($hash, "NextHour00", "pvcorrf", "-/m");
|
my $pvcorrf00 = NexthoursVal($hash, "NextHour00", "pvcorrf", "-/m");
|
||||||
my ($pcf,$pcq) = split "/", $pvcorrf00;
|
my ($pcf,$pcq) = split "/", $pvcorrf00;
|
||||||
my $pvcanz = "factor: $pcf / quality: $pcq";
|
my $pvcanz = (isSolCastUsed ($hash) ? 'Percentile' : 'factor').qq{: $pcf / quality: $pcq};
|
||||||
$pcq =~ s/m/-1/xs;
|
$pcq =~ s/m/-1/xs;
|
||||||
my $pvfc00 = NexthoursVal($hash, "NextHour00", "pvforecast", undef);
|
my $pvfc00 = NexthoursVal($hash, "NextHour00", "pvforecast", undef);
|
||||||
|
|
||||||
@ -7586,28 +7661,8 @@ return;
|
|||||||
}
|
}
|
||||||
|
|
||||||
################################################################
|
################################################################
|
||||||
# Ist Attribut 'numHistDays' gesetzt ?
|
# Korrekturen und Qualität berechnen / speichern
|
||||||
# $usenhd: 1 - ja, 0 - nein
|
# bei useAutoCorrection
|
||||||
# $nhd : Anzahl der zu verwendenden HistDays
|
|
||||||
################################################################
|
|
||||||
sub useNumHistDays {
|
|
||||||
my $name = shift;
|
|
||||||
|
|
||||||
my $usenhd = 0;
|
|
||||||
my $nhd = AttrVal($name, 'numHistDays', $calcmaxd+1);
|
|
||||||
|
|
||||||
if($nhd == $calcmaxd+1) {
|
|
||||||
$nhd = $calcmaxd;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
$usenhd = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return ($usenhd, $nhd);
|
|
||||||
}
|
|
||||||
|
|
||||||
################################################################
|
|
||||||
# Korrekturfaktoren und Qualität berechnen / speichern
|
|
||||||
################################################################
|
################################################################
|
||||||
sub calcCorrAndQuality {
|
sub calcCorrAndQuality {
|
||||||
my $paref = shift;
|
my $paref = shift;
|
||||||
@ -7673,16 +7728,14 @@ sub _calcCAQfromDWDcloudcover {
|
|||||||
next;
|
next;
|
||||||
}
|
}
|
||||||
|
|
||||||
Log3 ($name, 5, "$name - Hour: ".sprintf("%02d",$h).", Today PVreal: $pvval, PVforecast: $fcval");
|
|
||||||
|
|
||||||
$paref->{hour} = $h;
|
$paref->{hour} = $h;
|
||||||
my ($pvhis,$fchis,$dnum,$range) = calcAvgFromHistory ($paref); # historische PV / Forecast Vergleichswerte ermitteln
|
my ($pvhis,$fchis,$dnum,$range) = __avgCloudcoverCorrFromHistory ($paref); # historische PV / Forecast Vergleichswerte ermitteln
|
||||||
|
|
||||||
my ($oldfac, $oldq) = CircularAutokorrVal ($hash, sprintf("%02d",$h), $range, 0); # bisher definierter Korrekturfaktor/KF-Qualität der Stunde des Tages der entsprechenden Bewölkungsrange
|
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);
|
$oldfac = 1 if(1 * $oldfac == 0);
|
||||||
|
|
||||||
my $factor;
|
my $factor;
|
||||||
my ($usenhd) = useNumHistDays ($name); # ist Attr numHistDays gesetzt ?
|
my ($usenhd) = __useNumHistDays ($name); # ist Attr numHistDays gesetzt ?
|
||||||
|
|
||||||
if($dnum) { # Werte in History vorhanden -> haben Prio !
|
if($dnum) { # Werte in History vorhanden -> haben Prio !
|
||||||
$dnum = $dnum + 1;
|
$dnum = $dnum + 1;
|
||||||
@ -7729,106 +7782,11 @@ sub _calcCAQfromDWDcloudcover {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
################################################################
|
|
||||||
# PVreal mit den SolCast Percentilen vergleichen und das
|
|
||||||
# beste Percentil als Auswahl speichern
|
|
||||||
################################################################
|
|
||||||
sub _calcCAQwithSolCastPercentil {
|
|
||||||
my $paref = shift;
|
|
||||||
my $hash = $paref->{hash};
|
|
||||||
my $name = $paref->{name};
|
|
||||||
my $chour = $paref->{chour}; # aktuelle Stunde
|
|
||||||
my $date = $paref->{date};
|
|
||||||
my $daref = $paref->{daref};
|
|
||||||
|
|
||||||
return if(!isSolCastUsed ($hash));
|
|
||||||
|
|
||||||
my $debug = AttrVal ($name, "debug", 0);
|
|
||||||
|
|
||||||
for my $h (1..23) {
|
|
||||||
next if(!$chour || $h > $chour);
|
|
||||||
|
|
||||||
my $pvval = ReadingsNum ($name, "Today_Hour".sprintf("%02d",$h)."_PVreal", 0);
|
|
||||||
next if(!$pvval);
|
|
||||||
|
|
||||||
my $cdone = ReadingsVal ($name, "pvSolCastPercentile_".sprintf("%02d",$h)."_autocalc", "");
|
|
||||||
|
|
||||||
if($cdone eq "done") {
|
|
||||||
Log3 ($name, 5, "$name - pvSolCastPercentile Hour: ".sprintf("%02d",$h)." already calculated");
|
|
||||||
next;
|
|
||||||
}
|
|
||||||
|
|
||||||
my ($oldperc, $oldq) = CircularAutokorrVal ($hash, sprintf("%02d",$h), 'percentile', 0); # bisher definiertes Percentil/Qualität der Stunde des Tages der entsprechenden Bewölkungsrange
|
|
||||||
$oldperc = q{default} if(1 * $oldperc == 0);
|
|
||||||
|
|
||||||
my @sts = split ",", ReadingsVal($name, 'inverterStrings', '');
|
|
||||||
|
|
||||||
my ($est,$est10,$est20,$est30,$est40,$est60,$est70,$est80,$est90);
|
|
||||||
|
|
||||||
for my $s (@sts) {
|
|
||||||
$est += SolCastAPIVal ($hash, $s, $date.' '.sprintf("%02d",$h-1).':00:00', 'pv_estimate', 0);
|
|
||||||
$est10 += SolCastAPIVal ($hash, $s, $date.' '.sprintf("%02d",$h-1).':00:00', 'pv_estimate10', 0);
|
|
||||||
$est20 += SolCastAPIVal ($hash, $s, $date.' '.sprintf("%02d",$h-1).':00:00', 'pv_estimate20', 0);
|
|
||||||
$est30 += SolCastAPIVal ($hash, $s, $date.' '.sprintf("%02d",$h-1).':00:00', 'pv_estimate30', 0);
|
|
||||||
$est40 += SolCastAPIVal ($hash, $s, $date.' '.sprintf("%02d",$h-1).':00:00', 'pv_estimate40', 0);
|
|
||||||
$est60 += SolCastAPIVal ($hash, $s, $date.' '.sprintf("%02d",$h-1).':00:00', 'pv_estimate60', 0);
|
|
||||||
$est70 += SolCastAPIVal ($hash, $s, $date.' '.sprintf("%02d",$h-1).':00:00', 'pv_estimate70', 0);
|
|
||||||
$est80 += SolCastAPIVal ($hash, $s, $date.' '.sprintf("%02d",$h-1).':00:00', 'pv_estimate80', 0);
|
|
||||||
$est90 += SolCastAPIVal ($hash, $s, $date.' '.sprintf("%02d",$h-1).':00:00', 'pv_estimate90', 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($debug) { # nur für Debugging
|
|
||||||
Log (1, qq{DEBUG> $name summary PV estimates for hour of day "$h":\n}.
|
|
||||||
qq{est: $est, est10: $est10, est20: $est20, est30: $est30, est40: $est40, est60: $est60, est70: $est70, est80: $est80, est90: $est90});
|
|
||||||
}
|
|
||||||
|
|
||||||
my %pc = (
|
|
||||||
10 => $est10,
|
|
||||||
20 => $est20,
|
|
||||||
30 => $est30,
|
|
||||||
40 => $est40,
|
|
||||||
60 => $est60,
|
|
||||||
70 => $est70,
|
|
||||||
80 => $est80,
|
|
||||||
90 => $est90,
|
|
||||||
);
|
|
||||||
|
|
||||||
my $dnum = 1;
|
|
||||||
my $perc = q{}; # Standardpercentil
|
|
||||||
my $diff0 = abs ($est - $pvval);
|
|
||||||
## no critic 'NoStrict'
|
|
||||||
for my $p (sort keys %pc) {
|
|
||||||
my $diff1 = abs ($pc{$p} - $pvval);
|
|
||||||
|
|
||||||
if($diff1 < $diff0) {
|
|
||||||
$diff0 = $diff1;
|
|
||||||
$perc = $p;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($debug) { # nur für Debugging
|
|
||||||
Log (1, qq{DEBUG> $name percentile -> hour: $h, number checked days: $dnum, pvreal: $pvval, diffbest: $diff0, best percentile: $perc});
|
|
||||||
}
|
|
||||||
|
|
||||||
#Log3 ($name, 5, "$name - write percentile into circular Hash: $perc, Hour $h");
|
|
||||||
|
|
||||||
#my $type = $hash->{TYPE};
|
|
||||||
|
|
||||||
#$data{$type}{$name}{circular}{sprintf("%02d",$h)}{pvcorrf}{percentile} = $perc; # bestes Percentil für die jeweilige Stunde speichern
|
|
||||||
#$data{$type}{$name}{circular}{sprintf("%02d",$h)}{quality}{percentile} = $dnum; # Percentil Qualität
|
|
||||||
|
|
||||||
push @$daref, "pvSolCastPercentile_".sprintf("%02d",$h)."<>".$perc." (automatic - old percentile: $oldperc)";
|
|
||||||
push @$daref, "pvSolCastPercentile_".sprintf("%02d",$h)."_autocalc<>done";
|
|
||||||
}
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
################################################################
|
################################################################
|
||||||
# Berechne Durchschnitte PV Vorhersage / PV Ertrag
|
# Berechne Durchschnitte PV Vorhersage / PV Ertrag
|
||||||
# aus Werten der PV History
|
# aus Werten der PV History
|
||||||
################################################################
|
################################################################
|
||||||
sub calcAvgFromHistory {
|
sub __avgCloudcoverCorrFromHistory {
|
||||||
my $paref = shift;
|
my $paref = shift;
|
||||||
my $hash = $paref->{hash};
|
my $hash = $paref->{hash};
|
||||||
my $hour = $paref->{hour}; # Stunde des Tages für die der Durchschnitt bestimmt werden soll
|
my $hour = $paref->{hour}; # Stunde des Tages für die der Durchschnitt bestimmt werden soll
|
||||||
@ -7840,7 +7798,7 @@ sub calcAvgFromHistory {
|
|||||||
my $type = $hash->{TYPE};
|
my $type = $hash->{TYPE};
|
||||||
my $pvhh = $data{$type}{$name}{pvhist};
|
my $pvhh = $data{$type}{$name}{pvhist};
|
||||||
|
|
||||||
my ($usenhd, $calcd) = useNumHistDays ($name); # ist Attr numHistDays gesetzt ? und welcher Wert
|
my ($usenhd, $calcd) = __useNumHistDays ($name); # ist Attr numHistDays gesetzt ? und welcher Wert
|
||||||
|
|
||||||
my @k = sort {$a<=>$b} keys %{$pvhh};
|
my @k = sort {$a<=>$b} keys %{$pvhh};
|
||||||
my $ile = $#k; # Index letztes Arrayelement
|
my $ile = $#k; # Index letztes Arrayelement
|
||||||
@ -7917,6 +7875,225 @@ sub calcAvgFromHistory {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
################################################################
|
||||||
|
# Ist Attribut 'numHistDays' gesetzt ?
|
||||||
|
# $usenhd: 1 - ja, 0 - nein
|
||||||
|
# $nhd : Anzahl der zu verwendenden HistDays
|
||||||
|
################################################################
|
||||||
|
sub __useNumHistDays {
|
||||||
|
my $name = shift;
|
||||||
|
|
||||||
|
my $usenhd = 0;
|
||||||
|
my $nhd = AttrVal($name, 'numHistDays', $calcmaxd+1);
|
||||||
|
|
||||||
|
if($nhd == $calcmaxd+1) {
|
||||||
|
$nhd = $calcmaxd;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$usenhd = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ($usenhd, $nhd);
|
||||||
|
}
|
||||||
|
|
||||||
|
################################################################
|
||||||
|
# PVreal mit den SolCast Percentilen vergleichen und das
|
||||||
|
# beste Percentil als Auswahl speichern
|
||||||
|
################################################################
|
||||||
|
sub _calcCAQwithSolCastPercentil {
|
||||||
|
my $paref = shift;
|
||||||
|
my $hash = $paref->{hash};
|
||||||
|
my $name = $paref->{name};
|
||||||
|
my $chour = $paref->{chour}; # aktuelle Stunde
|
||||||
|
my $date = $paref->{date};
|
||||||
|
my $daref = $paref->{daref};
|
||||||
|
|
||||||
|
return if(!isSolCastUsed ($hash));
|
||||||
|
|
||||||
|
my $debug = AttrVal ($name, "debug", 0);
|
||||||
|
|
||||||
|
for my $h (1..23) {
|
||||||
|
next if(!$chour || $h > $chour);
|
||||||
|
|
||||||
|
my $pvval = ReadingsNum ($name, "Today_Hour".sprintf("%02d",$h)."_PVreal", 0);
|
||||||
|
next if(!$pvval);
|
||||||
|
|
||||||
|
my $cdone = ReadingsVal ($name, "pvSolCastPercentile_".sprintf("%02d",$h)."_autocalc", "");
|
||||||
|
|
||||||
|
if($cdone eq "done") {
|
||||||
|
Log3 ($name, 5, "$name - pvSolCastPercentile Hour: ".sprintf("%02d",$h)." already calculated");
|
||||||
|
next;
|
||||||
|
}
|
||||||
|
|
||||||
|
$paref->{hour} = $h;
|
||||||
|
my ($dnum,$avgperc) = __avgSolCastPercFromHistory ($paref); # historische Percentile / Qualität ermitteln
|
||||||
|
|
||||||
|
my ($oldperc, $oldq) = CircularAutokorrVal ($hash, sprintf("%02d",$h), 'percentile', 0); # bisher definiertes Percentil/Qualität der Stunde des Tages der entsprechenden Bewölkungsrange
|
||||||
|
$oldperc = 50 if(1 * $oldperc == 0);
|
||||||
|
|
||||||
|
my @sts = split ",", ReadingsVal($name, 'inverterStrings', '');
|
||||||
|
|
||||||
|
my ($est10,$est20,$est30,$est40,$est50,$est60,$est70,$est80,$est90);
|
||||||
|
|
||||||
|
for my $s (@sts) {
|
||||||
|
$est10 += SolCastAPIVal ($hash, $s, $date.' '.sprintf("%02d",$h-1).':00:00', 'pv_estimate10', 0);
|
||||||
|
$est20 += SolCastAPIVal ($hash, $s, $date.' '.sprintf("%02d",$h-1).':00:00', 'pv_estimate20', 0);
|
||||||
|
$est30 += SolCastAPIVal ($hash, $s, $date.' '.sprintf("%02d",$h-1).':00:00', 'pv_estimate30', 0);
|
||||||
|
$est40 += SolCastAPIVal ($hash, $s, $date.' '.sprintf("%02d",$h-1).':00:00', 'pv_estimate40', 0);
|
||||||
|
$est50 += SolCastAPIVal ($hash, $s, $date.' '.sprintf("%02d",$h-1).':00:00', 'pv_estimate50', 0); # Standardpercentil
|
||||||
|
$est60 += SolCastAPIVal ($hash, $s, $date.' '.sprintf("%02d",$h-1).':00:00', 'pv_estimate60', 0);
|
||||||
|
$est70 += SolCastAPIVal ($hash, $s, $date.' '.sprintf("%02d",$h-1).':00:00', 'pv_estimate70', 0);
|
||||||
|
$est80 += SolCastAPIVal ($hash, $s, $date.' '.sprintf("%02d",$h-1).':00:00', 'pv_estimate80', 0);
|
||||||
|
$est90 += SolCastAPIVal ($hash, $s, $date.' '.sprintf("%02d",$h-1).':00:00', 'pv_estimate90', 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!$est50) { # kein Standardpercentile vorhanden
|
||||||
|
Log (1, qq{DEBUG> $name percentile -> hour: $h, the best percentile can't be identified because of the default percentile has no value yet}) if($debug);
|
||||||
|
next;
|
||||||
|
}
|
||||||
|
|
||||||
|
my %pc = (
|
||||||
|
10 => $est10,
|
||||||
|
20 => $est20,
|
||||||
|
30 => $est30,
|
||||||
|
40 => $est40,
|
||||||
|
60 => $est60,
|
||||||
|
70 => $est70,
|
||||||
|
80 => $est80,
|
||||||
|
90 => $est90,
|
||||||
|
);
|
||||||
|
|
||||||
|
my $perc = 50; # Standardpercentil
|
||||||
|
my $diff0 = abs ($est50 - $pvval);
|
||||||
|
## no critic 'NoStrict'
|
||||||
|
for my $p (sort keys %pc) {
|
||||||
|
my $diff1 = abs ($pc{$p} - $pvval);
|
||||||
|
|
||||||
|
if($diff1 < $diff0) {
|
||||||
|
$diff0 = $diff1;
|
||||||
|
$perc = $p;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($debug) { # nur für Debugging
|
||||||
|
Log (1, qq{DEBUG> $name summary PV estimates for hour of day "$h":\n}.
|
||||||
|
qq{est10: $est10, est20: $est20, est30: $est30, est40: $est40, est50 (default): $est50, est60: $est60, est70: $est70, est80: $est80, est90: $est90});
|
||||||
|
Log (1, qq{DEBUG> $name percentile -> hour: $h, number checked days: $dnum, pvreal: $pvval, diffbest: $diff0, best percentile: $perc});
|
||||||
|
}
|
||||||
|
|
||||||
|
my ($usenhd) = __useNumHistDays ($name); # ist Attr numHistDays gesetzt ?
|
||||||
|
|
||||||
|
if($dnum) { # Werte in History vorhanden -> haben Prio !
|
||||||
|
$avgperc = $avgperc * $dnum;
|
||||||
|
$dnum++;
|
||||||
|
$perc = sprintf "%.0f", ((($avgperc + $perc) / $dnum) / 10);
|
||||||
|
}
|
||||||
|
elsif($oldperc && !$usenhd) { # keine Werte in History vorhanden, aber in CircularVal && keine Beschränkung durch Attr numHistDays
|
||||||
|
$oldperc = $oldperc * $oldq;
|
||||||
|
$dnum = $oldq + 1;
|
||||||
|
$perc = sprintf "%.0f", ((($oldperc + $perc) / $dnum) / 10);
|
||||||
|
}
|
||||||
|
else { # ganz neuer Wert
|
||||||
|
$perc = sprintf "%.0f", ($perc / 10);
|
||||||
|
$dnum = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
$perc = $perc * 10;
|
||||||
|
|
||||||
|
if ($debug) { # nur für Debugging
|
||||||
|
Log (1, qq{DEBUG> $name percentile -> old avg percentile: }.($avgperc ? $avgperc : '-').qq{, new avg percentile: $perc});
|
||||||
|
}
|
||||||
|
|
||||||
|
Log3 ($name, 5, "$name - write percentile into circular Hash: $perc, Hour $h");
|
||||||
|
|
||||||
|
my $type = $hash->{TYPE};
|
||||||
|
|
||||||
|
$data{$type}{$name}{circular}{sprintf("%02d",$h)}{pvcorrf}{percentile} = $perc; # bestes Percentil für die jeweilige Stunde speichern
|
||||||
|
$data{$type}{$name}{circular}{sprintf("%02d",$h)}{quality}{percentile} = $dnum; # Percentil Qualität
|
||||||
|
|
||||||
|
push @$daref, "pvSolCastPercentile_".sprintf("%02d",$h)."<>".$perc." (automatic - old percentile: $oldperc)";
|
||||||
|
push @$daref, "pvSolCastPercentile_".sprintf("%02d",$h)."_autocalc<>done";
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
################################################################
|
||||||
|
# Berechne das durchschnittlich verwendete Percentil
|
||||||
|
# aus Werten der PV History
|
||||||
|
################################################################
|
||||||
|
sub __avgSolCastPercFromHistory {
|
||||||
|
my $paref = shift;
|
||||||
|
my $hash = $paref->{hash};
|
||||||
|
my $hour = $paref->{hour}; # Stunde des Tages für die der Durchschnitt bestimmt werden soll
|
||||||
|
my $day = $paref->{day}; # aktueller Tag
|
||||||
|
|
||||||
|
$hour = sprintf("%02d",$hour);
|
||||||
|
|
||||||
|
my $name = $hash->{NAME};
|
||||||
|
my $type = $hash->{TYPE};
|
||||||
|
my $pvhh = $data{$type}{$name}{pvhist};
|
||||||
|
|
||||||
|
my ($usenhd, $calcd) = __useNumHistDays ($name); # ist Attr numHistDays gesetzt ? und welcher Wert
|
||||||
|
|
||||||
|
my @k = sort {$a<=>$b} keys %{$pvhh};
|
||||||
|
my $ile = $#k; # Index letztes Arrayelement
|
||||||
|
my ($idx) = grep {$k[$_] eq "$day"} (0..@k-1); # Index des aktuellen Tages
|
||||||
|
|
||||||
|
return 0 if(!defined $idx);
|
||||||
|
|
||||||
|
my $ei = $idx-1;
|
||||||
|
$ei = $ei < 0 ? $ile : $ei;
|
||||||
|
my @efa;
|
||||||
|
|
||||||
|
for my $e (0..$calcmaxd) {
|
||||||
|
last if($e == $calcmaxd || $k[$ei] == $day);
|
||||||
|
unshift @efa, $k[$ei];
|
||||||
|
$ei--;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(scalar(@efa)) {
|
||||||
|
Log3 ($name, 4, "$name - PV History -> Raw Days ($calcmaxd) for average check: ".join " ",@efa);
|
||||||
|
}
|
||||||
|
else { # vermeide Fehler: Illegal division by zero
|
||||||
|
Log3 ($name, 4, "$name - PV History -> Day $day has index $idx. Use only current day for average calc");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
my ($dnum, $percsum) = (0, 0);
|
||||||
|
my ($perc, $qual) = (50,0);
|
||||||
|
|
||||||
|
for my $dayfa (@efa) {
|
||||||
|
my $histval = HistoryVal ($hash, $dayfa, $hour, 'pvcorrf', undef); # historisches Percentil/Qualität
|
||||||
|
|
||||||
|
next if(!defined $histval);
|
||||||
|
|
||||||
|
($perc, $qual) = split "/", $histval; # Percentil und Qualität splitten
|
||||||
|
|
||||||
|
next if($qual eq 'm'); # manuell eingestellte Percentile überspringen
|
||||||
|
|
||||||
|
$perc = 50 if(!$perc || $perc < 10);
|
||||||
|
|
||||||
|
Log3 ($name, 5, qq{$name - PV History -> historical Day/hour $dayfa/$hour included - percentile: $perc});
|
||||||
|
|
||||||
|
$dnum++;
|
||||||
|
$percsum += $perc ;
|
||||||
|
|
||||||
|
last if($dnum == $calcd);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!$dnum) {
|
||||||
|
Log3 ($name, 5, "$name - PV History -> no historical percentile selected");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
$perc = sprintf "%.0f", ($percsum/$dnum);
|
||||||
|
|
||||||
|
Log3 ($name, 5, "$name - PV History -> Summary - days: $dnum, average percentile: $perc");
|
||||||
|
|
||||||
|
return ($dnum,$perc);
|
||||||
|
}
|
||||||
|
|
||||||
################################################################
|
################################################################
|
||||||
# Bewölkungs- bzw. Regenrange berechnen
|
# Bewölkungs- bzw. Regenrange berechnen
|
||||||
################################################################
|
################################################################
|
||||||
@ -10056,12 +10233,39 @@ Ein/Ausschaltzeiten sowie deren Ausführung vom SolarForecast Modul übernehmen
|
|||||||
Die Wirkungsweise unterscheidet sich zwischen dem Model DWD und dem Model SolCastAPI. <br><br>
|
Die Wirkungsweise unterscheidet sich zwischen dem Model DWD und dem Model SolCastAPI. <br><br>
|
||||||
|
|
||||||
<b>Model SolCastAPI:</b> <br>
|
<b>Model SolCastAPI:</b> <br>
|
||||||
(in Entwicklung)
|
Eine eingeschaltete Autokorrektur ermittelt am Ende jeder relevanten Stunde durch Vergleich von PV Prognose und
|
||||||
|
realer Erzeugung das beste Percentil (10-90).
|
||||||
|
Bevor die Autokorrektur eingeschaltet ist die Prognose zu optimieren mit folgenden Schritten: <br><br>
|
||||||
|
<ul>
|
||||||
|
<li>
|
||||||
|
definiere im RoofTop-Editor der SolCast API den
|
||||||
|
<a href="https://articles.solcast.com.au/en/articles/2959798-what-is-the-efficiency-factor?_ga=2.119610952.1991905456.1665567573-1390691316.1665567573"><b>efficiency factor</b></a>
|
||||||
|
entsprechend dem Alter der Anlage. <br>
|
||||||
|
Bei einer 8 Jahre alten Anlage (8 x 2%) wäre er 84. <br>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
nach Sonnenuntergang wird das Reading Today_PVdeviation erstellt, welches die Abweichung zwischen Prognose und
|
||||||
|
realer PV Erzeugung in Prozent darstellt.
|
||||||
|
</li>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
entsprechend der Abweichung wird der efficiency factor in Schritten angepasst bis ein Optimum, d.h. die kleinste
|
||||||
|
Tagesabweichung gefunden ist
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
<br>
|
||||||
|
|
||||||
|
Idealerweise wird dieser Prozess in einer Phase stabiler meteorologischer Bedingungen (gleichmäßige Sonne bzw.
|
||||||
|
Bewölkung) durchgeführt. <br>
|
||||||
|
Ist die minimale Tagesabweichung gefunden, kann die Autokorrektur aktiviert werden um für jede Stunde separat das
|
||||||
|
beste Percentil ermitteln zu lassen. Dieser Vorgang ist dynamisch und verwendet ebenso historische Werte zur
|
||||||
|
Durchschnittsbildung.
|
||||||
|
Siehe auch Attribut <a href="#SolarForecast-attr-numHistDays">numHistDays</a>.
|
||||||
<br><br>
|
<br><br>
|
||||||
|
|
||||||
<b>Model DWD:</b> <br>
|
<b>Model DWD:</b> <br>
|
||||||
Ist die Automatik eingeschaltet, wird für jede Stunde ein Korrekturfaktor der Solarvorhersage berechnet und intern
|
Ist die Autokorrektur eingeschaltet, wird für jede Stunde ein Korrekturfaktor der Solarvorhersage berechnet und
|
||||||
gespeichert.
|
intern gespeichert.
|
||||||
Dazu wird die tatsächliche Energieerzeugung mit dem vorhergesagten Wert des aktuellen Tages und Stunde verglichen,
|
Dazu wird die tatsächliche Energieerzeugung mit dem vorhergesagten Wert des aktuellen Tages und Stunde verglichen,
|
||||||
die Korrekturwerte historischer Tage unter Berücksichtigung der Bewölkung einbezogen und daraus ein neuer Korrekturfaktor
|
die Korrekturwerte historischer Tage unter Berücksichtigung der Bewölkung einbezogen und daraus ein neuer Korrekturfaktor
|
||||||
abgeleitet. Es werden nur historische Daten mit gleicher Bewölkungsrange einbezogen. <br>
|
abgeleitet. Es werden nur historische Daten mit gleicher Bewölkungsrange einbezogen. <br>
|
||||||
@ -10225,10 +10429,10 @@ Ein/Ausschaltzeiten sowie deren Ausführung vom SolarForecast Modul übernehmen
|
|||||||
<tr><td> <b>wid</b> </td><td>ID des vorhergesagten Wetters </td></tr>
|
<tr><td> <b>wid</b> </td><td>ID des vorhergesagten Wetters </td></tr>
|
||||||
<tr><td> <b>wcc</b> </td><td>vorhergesagter Grad der Bewölkung </td></tr>
|
<tr><td> <b>wcc</b> </td><td>vorhergesagter Grad der Bewölkung </td></tr>
|
||||||
<tr><td> <b>crange</b> </td><td>berechneter Bewölkungsbereich </td></tr>
|
<tr><td> <b>crange</b> </td><td>berechneter Bewölkungsbereich </td></tr>
|
||||||
<tr><td> <b>correff</b> </td><td>effektiv verwendeter Korrekturfaktor/Qualität </td></tr>
|
<tr><td> <b>correff</b> </td><td>verwendeter Korrekturfaktor bzw. SolCast Percentil/Qualität </td></tr>
|
||||||
<tr><td> </td><td>Faktor/m - manuell </td></tr>
|
<tr><td> </td><td>Korrektur/m - manuell </td></tr>
|
||||||
<tr><td> </td><td>Faktor/0 - Korrekturfaktor nicht in Store vorhanden (default wird verwendet) </td></tr>
|
<tr><td> </td><td>Korrektur/0 - Korrektur nicht in Store vorhanden (default wird verwendet) </td></tr>
|
||||||
<tr><td> </td><td>Faktor/1...X - Korrekturfaktor aus Store genutzt (höhere Zahl = bessere Qualität) </td></tr>
|
<tr><td> </td><td>Korrektur/1...X - Korrektur aus Store genutzt (höhere Zahl = bessere Qualität) </td></tr>
|
||||||
<tr><td> <b>wrp</b> </td><td>vorhergesagter Grad der Regenwahrscheinlichkeit </td></tr>
|
<tr><td> <b>wrp</b> </td><td>vorhergesagter Grad der Regenwahrscheinlichkeit </td></tr>
|
||||||
<tr><td> <b>Rad1h</b> </td><td>vorhergesagte Globalstrahlung </td></tr>
|
<tr><td> <b>Rad1h</b> </td><td>vorhergesagte Globalstrahlung </td></tr>
|
||||||
<tr><td> <b>temp</b> </td><td>vorhergesagte Außentemperatur </td></tr>
|
<tr><td> <b>temp</b> </td><td>vorhergesagte Außentemperatur </td></tr>
|
||||||
@ -10262,7 +10466,7 @@ Ein/Ausschaltzeiten sowie deren Ausführung vom SolarForecast Modul übernehmen
|
|||||||
<tr><td> <b>wid</b> </td><td>Identifikationsnummer des Wetters </td></tr>
|
<tr><td> <b>wid</b> </td><td>Identifikationsnummer des Wetters </td></tr>
|
||||||
<tr><td> <b>wcc</b> </td><td>effektive Wolkenbedeckung </td></tr>
|
<tr><td> <b>wcc</b> </td><td>effektive Wolkenbedeckung </td></tr>
|
||||||
<tr><td> <b>wrp</b> </td><td>Wahrscheinlichkeit von Niederschlag > 0,1 mm während der jeweiligen Stunde </td></tr>
|
<tr><td> <b>wrp</b> </td><td>Wahrscheinlichkeit von Niederschlag > 0,1 mm während der jeweiligen Stunde </td></tr>
|
||||||
<tr><td> <b>pvcorrf</b> </td><td>abgeleiteter Autokorrekturfaktor </td></tr>
|
<tr><td> <b>pvcorrf</b> </td><td>abgeleiteter Autokorrekturfaktor bzw. SolCast Percentil </td></tr>
|
||||||
<tr><td> <b>csmtXX</b> </td><td>Summe Energieverbrauch von ConsumerXX </td></tr>
|
<tr><td> <b>csmtXX</b> </td><td>Summe Energieverbrauch von ConsumerXX </td></tr>
|
||||||
<tr><td> <b>csmeXX</b> </td><td>Anteil der jeweiligen Stunde des Tages am Energieverbrauch von ConsumerXX </td></tr>
|
<tr><td> <b>csmeXX</b> </td><td>Anteil der jeweiligen Stunde des Tages am Energieverbrauch von ConsumerXX </td></tr>
|
||||||
<tr><td> <b>minutescsmXX</b> </td><td>Summe Aktivminuten in der Stunde von ConsumerXX </td></tr>
|
<tr><td> <b>minutescsmXX</b> </td><td>Summe Aktivminuten in der Stunde von ConsumerXX </td></tr>
|
||||||
@ -10853,7 +11057,7 @@ Ein/Ausschaltzeiten sowie deren Ausführung vom SolarForecast Modul übernehmen
|
|||||||
<li><b>optimizeSolCastAPIreqInterval </b><br>
|
<li><b>optimizeSolCastAPIreqInterval </b><br>
|
||||||
(nur bei Verwendung Model SolCastAPI) <br><br>
|
(nur bei Verwendung Model SolCastAPI) <br><br>
|
||||||
|
|
||||||
Das default Abrufintervall der SolCast API beträgt fest 1 Stunde. Ist dieses Attribut gesetzt erfolgt ein dynamische
|
Das default Abrufintervall der SolCast API beträgt 1 Stunde. Ist dieses Attribut gesetzt erfolgt ein dynamische
|
||||||
Anpassung des Intervalls mit dem Ziel die maximal möglichen Abrufe innerhalb von Sonnenauf- und untergang
|
Anpassung des Intervalls mit dem Ziel die maximal möglichen Abrufe innerhalb von Sonnenauf- und untergang
|
||||||
auszunutzen. <br>
|
auszunutzen. <br>
|
||||||
(default: 0)
|
(default: 0)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user