2
0
mirror of https://github.com/fhem/fhem-mirror.git synced 2025-04-30 12:07:09 +00:00

76_SolarForecast: contrib 0.80.10

git-svn-id: https://svn.fhem.de/fhem/trunk@27762 2b470e98-0d58-463d-a4d8-8e2adae1ed80
This commit is contained in:
nasseeder1 2023-07-13 20:25:12 +00:00
parent 9d760d1836
commit ecd6e4a357

View File

@ -1,5 +1,5 @@
######################################################################################################################## ########################################################################################################################
# $Id: 76_SolarForecast.pm 21735 2023-07-12 23:53:24Z DS_Starter $ # $Id: 76_SolarForecast.pm 21735 2023-07-13 23:53:24Z DS_Starter $
######################################################################################################################### #########################################################################################################################
# 76_SolarForecast.pm # 76_SolarForecast.pm
# #
@ -136,6 +136,8 @@ BEGIN {
# Versions History intern # Versions History intern
my %vNotesIntern = ( my %vNotesIntern = (
"0.80.10"=> "13.07.2023 ccc ",
"0.80.9" => "13.07.2023 new method of prediction quality calculation -> sub __calcFcQuality, minor bug fixes ",
"0.80.8" => "12.07.2023 store battery values initdaybatintot, initdaybatouttot, batintot, batouttot in circular hash ". "0.80.8" => "12.07.2023 store battery values initdaybatintot, initdaybatouttot, batintot, batouttot in circular hash ".
"new Attr ctrlStatisticReadings parameter todayBatIn, todayBatOut ", "new Attr ctrlStatisticReadings parameter todayBatIn, todayBatOut ",
"0.80.7" => "10.07.2023 Model SolCastAPI: retrieve forecast data of 72h (old 48), create statistic reading dayAfterTomorrowPVforecast if possible ", "0.80.7" => "10.07.2023 Model SolCastAPI: retrieve forecast data of 72h (old 48), create statistic reading dayAfterTomorrowPVforecast if possible ",
@ -1455,10 +1457,10 @@ sub _setmeterDevice { ## no critic "not used"
## alte Speicherwerte löschen ## alte Speicherwerte löschen
############################### ###############################
delete $data{$type}{$name}{circular}{99}{feedintotal}; delete $data{$type}{$name}{circular}{'99'}{feedintotal};
delete $data{$type}{$name}{circular}{99}{initdayfeedin}; delete $data{$type}{$name}{circular}{'99'}{initdayfeedin};
delete $data{$type}{$name}{circular}{99}{gridcontotal}; delete $data{$type}{$name}{circular}{'99'}{gridcontotal};
delete $data{$type}{$name}{circular}{99}{initdaygcon}; delete $data{$type}{$name}{circular}{'99'}{initdaygcon};
readingsSingleUpdate ($hash, "currentMeterDev", $arg, 1); readingsSingleUpdate ($hash, "currentMeterDev", $arg, 1);
createAssociatedWith ($hash); createAssociatedWith ($hash);
@ -1504,10 +1506,10 @@ sub _setbatteryDevice { ## no critic "not used"
## alte Speicherwerte löschen ## alte Speicherwerte löschen
############################### ###############################
delete $data{$type}{$name}{circular}{99}{initdaybatintot}; delete $data{$type}{$name}{circular}{'99'}{initdaybatintot};
delete $data{$type}{$name}{circular}{99}{batintot}; delete $data{$type}{$name}{circular}{'99'}{batintot};
delete $data{$type}{$name}{circular}{99}{initdaybatouttot}; delete $data{$type}{$name}{circular}{'99'}{initdaybatouttot};
delete $data{$type}{$name}{circular}{99}{batouttot}; delete $data{$type}{$name}{circular}{'99'}{batouttot};
readingsSingleUpdate ($hash, "currentBatteryDev", $arg, 1); readingsSingleUpdate ($hash, "currentBatteryDev", $arg, 1);
createAssociatedWith ($hash); createAssociatedWith ($hash);
@ -1930,10 +1932,10 @@ sub _setreset { ## no critic "not used"
if($prop eq "currentMeterDev") { if($prop eq "currentMeterDev") {
readingsDelete($hash, "Current_GridConsumption"); readingsDelete($hash, "Current_GridConsumption");
readingsDelete($hash, "Current_GridFeedIn"); readingsDelete($hash, "Current_GridFeedIn");
delete $data{$type}{$name}{circular}{99}{initdayfeedin}; delete $data{$type}{$name}{circular}{'99'}{initdayfeedin};
delete $data{$type}{$name}{circular}{99}{gridcontotal}; delete $data{$type}{$name}{circular}{'99'}{gridcontotal};
delete $data{$type}{$name}{circular}{99}{initdaygcon}; delete $data{$type}{$name}{circular}{'99'}{initdaygcon};
delete $data{$type}{$name}{circular}{99}{feedintotal}; delete $data{$type}{$name}{circular}{'99'}{feedintotal};
delete $data{$type}{$name}{current}{gridconsumption}; delete $data{$type}{$name}{current}{gridconsumption};
delete $data{$type}{$name}{current}{tomorrowconsumption}; delete $data{$type}{$name}{current}{tomorrowconsumption};
delete $data{$type}{$name}{current}{gridfeedin}; delete $data{$type}{$name}{current}{gridfeedin};
@ -1949,10 +1951,10 @@ sub _setreset { ## no critic "not used"
readingsDelete($hash, "Current_PowerBatIn"); readingsDelete($hash, "Current_PowerBatIn");
readingsDelete($hash, "Current_PowerBatOut"); readingsDelete($hash, "Current_PowerBatOut");
readingsDelete($hash, "Current_BatCharge"); readingsDelete($hash, "Current_BatCharge");
delete $data{$type}{$name}{circular}{99}{initdaybatintot}; delete $data{$type}{$name}{circular}{'99'}{initdaybatintot};
delete $data{$type}{$name}{circular}{99}{initdaybatouttot}; delete $data{$type}{$name}{circular}{'99'}{initdaybatouttot};
delete $data{$type}{$name}{circular}{99}{batintot}; delete $data{$type}{$name}{circular}{'99'}{batintot};
delete $data{$type}{$name}{circular}{99}{batouttot}; delete $data{$type}{$name}{circular}{'99'}{batouttot};
delete $data{$type}{$name}{current}{powerbatout}; delete $data{$type}{$name}{current}{powerbatout};
delete $data{$type}{$name}{current}{powerbatin}; delete $data{$type}{$name}{current}{powerbatin};
delete $data{$type}{$name}{current}{batcharge}; delete $data{$type}{$name}{current}{batcharge};
@ -3711,6 +3713,7 @@ sub centralTask {
#delete $data{$type}{$name}{solcastapi}{'?All'}{'?All'}{todaySolCastAPIcalls}; #delete $data{$type}{$name}{solcastapi}{'?All'}{'?All'}{todaySolCastAPIcalls};
#delete $data{$type}{$name}{solcastapi}{'?All'}{'?All'}{todayRemaingAPIcalls}; #delete $data{$type}{$name}{solcastapi}{'?All'}{'?All'}{todayRemaingAPIcalls};
#delete $data{$type}{$name}{circular}{99}{initgridfeedintotal}; #delete $data{$type}{$name}{circular}{99}{initgridfeedintotal};
delete $data{$type}{$name}{circular}{'00'};
#for my $n (1..24) { #for my $n (1..24) {
# $n = sprintf "%02d", $n; # $n = sprintf "%02d", $n;
@ -4113,17 +4116,17 @@ sub _specialActivities {
delete $data{$type}{$name}{solcastapi}{'?All'}{'?All'}{todayRemainingAPIrequests}; delete $data{$type}{$name}{solcastapi}{'?All'}{'?All'}{todayRemainingAPIrequests};
delete $data{$type}{$name}{solcastapi}{'?All'}{'?All'}{todayRemainingAPIcalls}; delete $data{$type}{$name}{solcastapi}{'?All'}{'?All'}{todayRemainingAPIcalls};
delete $data{$type}{$name}{circular}{99}{initdayfeedin}; delete $data{$type}{$name}{circular}{'99'}{initdayfeedin};
delete $data{$type}{$name}{circular}{99}{initdaygcon}; delete $data{$type}{$name}{circular}{'99'}{initdaygcon};
delete $data{$type}{$name}{circular}{99}{initdaybatintot}; delete $data{$type}{$name}{circular}{'99'}{initdaybatintot};
delete $data{$type}{$name}{circular}{99}{initdaybatouttot}; delete $data{$type}{$name}{circular}{'99'}{initdaybatouttot};
delete $data{$type}{$name}{current}{sunriseToday}; delete $data{$type}{$name}{current}{sunriseToday};
delete $data{$type}{$name}{current}{sunriseTodayTs}; delete $data{$type}{$name}{current}{sunriseTodayTs};
delete $data{$type}{$name}{current}{sunsetToday}; delete $data{$type}{$name}{current}{sunsetToday};
delete $data{$type}{$name}{current}{sunsetTodayTs}; delete $data{$type}{$name}{current}{sunsetTodayTs};
$data{$type}{$name}{circular}{99}{ydayDvtn} = CircularVal ($hash, 99, 'tdayDvtn', '-'); $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 delete $data{$type}{$name}{pvhist}{$day}; # den (alten) aktuellen Tag aus History löschen
Log3 ($name, 3, qq{$name - history day "$day" deleted}); Log3 ($name, 3, qq{$name - history day "$day" deleted});
@ -5790,15 +5793,19 @@ sub __setConsRcmdState {
my $hash = $paref->{hash}; my $hash = $paref->{hash};
my $name = $paref->{name}; my $name = $paref->{name};
my $type = $paref->{type}; my $type = $paref->{type};
my $c = $paref->{consumer}; # aktueller Unixtimestamp my $c = $paref->{consumer}; # aktueller Unix Timestamp
my $daref = $paref->{daref}; my $daref = $paref->{daref};
my $debug = $paref->{debug};
my $surplus = CurrentVal ($hash, "surplus", 0); # aktueller Energieüberschuß my $surplus = CurrentVal ($hash, 'surplus', 0); # aktueller Energieüberschuß
my $nompower = ConsumerVal ($hash, $c, "power", 0); # Consumer nominale Leistungsaufnahme (W) my $nompower = ConsumerVal ($hash, $c, 'power', 0); # Consumer nominale Leistungsaufnahme (W)
my $ccr = AttrVal ($name, 'ctrlConsRecommendReadings', ''); # Liste der Consumer für die ConsumptionRecommended-Readings erstellt werden sollen my $ccr = AttrVal ($name, 'ctrlConsRecommendReadings', ''); # Liste der Consumer für die ConsumptionRecommended-Readings erstellt werden sollen
my $rescons = isConsumerPhysOn($hash, $c) ? 0 : $nompower; # resultierender Verbauch nach Einschaltung Consumer my $rescons = isConsumerPhysOn($hash, $c) ? 0 : $nompower; # resultierender Verbauch nach Einschaltung Consumer
if (!$nompower || $surplus - $rescons > 0) { my ($spignore, $info, $err) = isSurplusIgnoCond ($hash, $c, $debug); # Vorhandensein PV Überschuß ignorieren ?
Log3 ($name, 1, "$name - $err") if($err);
if (!$nompower || $surplus - $rescons > 0 || $spignore) {
$data{$type}{$name}{consumers}{$c}{isConsumptionRecommended} = 1; # Einschalten des Consumers günstig bzw. Freigabe für "on" von Überschußseite erteilt $data{$type}{$name}{consumers}{$c}{isConsumptionRecommended} = 1; # Einschalten des Consumers günstig bzw. Freigabe für "on" von Überschußseite erteilt
} }
else { else {
@ -7016,6 +7023,11 @@ sub collectAllRegConsumers {
($dswoffcond,$rswoffcond,$swoffcondregex) = split ":", $hc->{swoffcond}; ($dswoffcond,$rswoffcond,$swoffcondregex) = split ":", $hc->{swoffcond};
} }
my ($dspignorecond,$rigncond,$spignorecondregex);
if(exists $hc->{spignorecond}) { # Bedingung um vorhandenen PV Überschuß zu ignorieren
($dspignorecond,$rigncond,$spignorecondregex) = split ":", $hc->{spignorecond};
}
my $interruptable = 0; my $interruptable = 0;
my ($hyst); my ($hyst);
if(exists $hc->{interruptable} && $hc->{interruptable} ne '0') { if(exists $hc->{interruptable} && $hc->{interruptable} ne '0') {
@ -7078,6 +7090,9 @@ sub collectAllRegConsumers {
$data{$type}{$name}{consumers}{$c}{dswoffcond} = $dswoffcond // q{}; # Device zur Lieferung einer vorrangigen Ausschaltbedingung $data{$type}{$name}{consumers}{$c}{dswoffcond} = $dswoffcond // q{}; # Device zur Lieferung einer vorrangigen Ausschaltbedingung
$data{$type}{$name}{consumers}{$c}{rswoffcond} = $rswoffcond // q{}; # Reading zur Lieferung einer vorrangigen Ausschaltbedingung $data{$type}{$name}{consumers}{$c}{rswoffcond} = $rswoffcond // q{}; # Reading zur Lieferung einer vorrangigen Ausschaltbedingung
$data{$type}{$name}{consumers}{$c}{swoffcondregex} = $swoffcondregex // q{}; # Regex einer vorrangigen Ausschaltbedingung $data{$type}{$name}{consumers}{$c}{swoffcondregex} = $swoffcondregex // q{}; # Regex einer vorrangigen Ausschaltbedingung
$data{$type}{$name}{consumers}{$c}{dspignorecond} = $dspignorecond // q{}; # Device liefert Ignore Bedingung
$data{$type}{$name}{consumers}{$c}{rigncond} = $rigncond // q{}; # Reading liefert Ignore Bedingung
$data{$type}{$name}{consumers}{$c}{spignorecondregex} = $spignorecondregex // q{}; # Regex der Ignore Bedingung
$data{$type}{$name}{consumers}{$c}{interruptable} = $interruptable; # Ein-Zustand des Verbrauchers ist unterbrechbar $data{$type}{$name}{consumers}{$c}{interruptable} = $interruptable; # Ein-Zustand des Verbrauchers ist unterbrechbar
$data{$type}{$name}{consumers}{$c}{hysteresis} = $hyst // $defhyst; # Hysterese $data{$type}{$name}{consumers}{$c}{hysteresis} = $hyst // $defhyst; # Hysterese
$data{$type}{$name}{consumers}{$c}{sunriseshift} = $riseshift if(defined $riseshift); # Verschiebung (Sekunden) Sonnenaufgang bei SunPath Verwendung $data{$type}{$name}{consumers}{$c}{sunriseshift} = $riseshift if(defined $riseshift); # Verschiebung (Sekunden) Sonnenaufgang bei SunPath Verwendung
@ -7689,20 +7704,10 @@ sub _graphicHeader {
## Qualitäts-Icon ## Qualitäts-Icon
###################### ######################
my $pcqicon; my $pcqicon = $pcq < 0.00 ? FW_makeImage('15px-blank', $pvcanz) :
$pcq < 0.60 ? FW_makeImage('10px-kreis-rot.png', $pvcanz) :
if ($acu =~ /on_complex/xs) { $pcq < 0.80 ? FW_makeImage('10px-kreis-gelb.png', $pvcanz) :
$pcqicon = $pcq < 0 ? FW_makeImage('15px-blank', $pvcanz) :
$pcq < 10 ? FW_makeImage('10px-kreis-rot.png', $pvcanz) :
$pcq < 20 ? FW_makeImage('10px-kreis-gelb.png', $pvcanz) :
FW_makeImage('10px-kreis-gruen.png', $pvcanz); FW_makeImage('10px-kreis-gruen.png', $pvcanz);
}
else {
$pcqicon = $pcq < 0 ? FW_makeImage('15px-blank', $pvcanz) :
$pcq < 3 ? FW_makeImage('10px-kreis-rot.png', $pvcanz) :
$pcq < 5 ? FW_makeImage('10px-kreis-gelb.png', $pvcanz) :
FW_makeImage('10px-kreis-gruen.png', $pvcanz);
}
$pcqicon = "-" if(!$pvfc00 || $pcq == -1); $pcqicon = "-" if(!$pvfc00 || $pcq == -1);
@ -9229,11 +9234,11 @@ sub _calcCaQcloudcover {
next; next;
} }
my $fcval = ReadingsNum ($name, "Today_Hour".sprintf("%02d",$h)."_PVforecast", 0); my $pvfc = ReadingsNum ($name, "Today_Hour".sprintf("%02d",$h)."_PVforecast", 0);
next if(!$fcval); next if(!$pvfc);
my $pvval = ReadingsNum ($name, "Today_Hour".sprintf("%02d",$h)."_PVreal", 0); my $pvre = ReadingsNum ($name, "Today_Hour".sprintf("%02d",$h)."_PVreal", 0);
next if(!$pvval); next if(!$pvre);
$paref->{hour} = $h; $paref->{hour} = $h;
my ($pvhis,$fchis,$dnum,$range) = __Pv_Fc_Ccover_Dnum_Hist ($paref); # historische PV / Forecast Vergleichswerte ermitteln my ($pvhis,$fchis,$dnum,$range) = __Pv_Fc_Ccover_Dnum_Hist ($paref); # historische PV / Forecast Vergleichswerte ermitteln
@ -9246,17 +9251,17 @@ sub _calcCaQcloudcover {
if ($dnum) { # Werte in History vorhanden -> haben Prio ! if ($dnum) { # Werte in History vorhanden -> haben Prio !
$dnum = $dnum + 1; $dnum = $dnum + 1;
$pvval = ($pvval + $pvhis) / $dnum; # Ertrag aktuelle Stunde berücksichtigen $pvre = ($pvre + $pvhis) / $dnum; # Ertrag aktuelle Stunde berücksichtigen
$fcval = ($fcval + $fchis) / $dnum; # Vorhersage aktuelle Stunde berücksichtigen $pvfc = ($pvfc + $fchis) / $dnum; # Vorhersage aktuelle Stunde berücksichtigen
$factor = sprintf "%.2f", ($pvval / $fcval); # Faktorberechnung: reale PV / Prognose $factor = sprintf "%.2f", ($pvre / $pvfc); # Faktorberechnung: reale PV / Prognose
} }
elsif ($oldfac && !$usenhd) { # keine Werte in History vorhanden, aber in CircularVal && keine Beschränkung durch Attr affectNumHistDays elsif ($oldfac && !$usenhd) { # keine Werte in History vorhanden, aber in CircularVal && keine Beschränkung durch Attr affectNumHistDays
$dnum = $oldq + 1; $dnum = $oldq + 1;
$factor = sprintf "%.2f", ($pvval / $fcval); $factor = sprintf "%.2f", ($pvre / $pvfc);
$factor = sprintf "%.2f", ($factor + $oldfac) / 2; $factor = sprintf "%.2f", ($factor + $oldfac) / 2;
} }
else { # ganz neuer Wert else { # ganz neuer Wert
$factor = sprintf "%.2f", ($pvval / $fcval); $factor = sprintf "%.2f", ($pvre / $pvfc);
$dnum = 1; $dnum = 1;
} }
@ -9268,7 +9273,7 @@ sub _calcCaQcloudcover {
Log3 ($name, 3, "$name - new correction factor calculated: $factor (old: $oldfac) for hour: $h calculated") if($factor != $oldfac); Log3 ($name, 3, "$name - new correction factor calculated: $factor (old: $oldfac) for hour: $h calculated") if($factor != $oldfac);
} }
debugLog ($paref, 'pvCorrection', "Cloudcover Corrf -> cloudiness range: $range, hour: $h, days: $dnum, real: $pvval, forecast: $fcval, factor: $factor"); debugLog ($paref, 'pvCorrection', "Cloudcover Corrf -> cloudiness range: $range, hour: $h, days: $dnum, real: $pvre, forecast: $pvfc, factor: $factor");
if (defined $range) { if (defined $range) {
my $type = $paref->{type}; my $type = $paref->{type};
@ -9276,7 +9281,7 @@ sub _calcCaQcloudcover {
debugLog ($paref, 'saveData2Cache', "Cloudcover Corrf -> write Complex correction factor into Circular: Factor $factor, Hour $h, Range $range"); debugLog ($paref, 'saveData2Cache', "Cloudcover Corrf -> write Complex correction factor into Circular: Factor $factor, Hour $h, Range $range");
$data{$type}{$name}{circular}{sprintf("%02d",$h)}{pvcorrf}{$range} = $factor; # Korrekturfaktor für Bewölkung der jeweiligen Stunde als Datenquelle eintragen $data{$type}{$name}{circular}{sprintf("%02d",$h)}{pvcorrf}{$range} = $factor; # Korrekturfaktor für Bewölkung der jeweiligen Stunde als Datenquelle eintragen
$data{$type}{$name}{circular}{sprintf("%02d",$h)}{quality}{$range} = $dnum; # Korrekturfaktor Qualität $data{$type}{$name}{circular}{sprintf("%02d",$h)}{quality}{$range} = __calcFcQuality ($pvfc, $pvre); # Qualität der Vorhersage für die vergangene Stunde
push @$daref, ".pvCorrectionFactor_".sprintf("%02d",$h)."_cloudcover<>done"; push @$daref, ".pvCorrectionFactor_".sprintf("%02d",$h)."_cloudcover<>done";
} }
@ -9432,11 +9437,11 @@ sub _calcCaQsimple {
next; next;
} }
my $fcval = ReadingsNum ($name, "Today_Hour".sprintf("%02d",$h)."_PVforecast", 0); my $pvfc = ReadingsNum ($name, "Today_Hour".sprintf("%02d",$h)."_PVforecast", 0);
next if(!$fcval); next if(!$pvfc);
my $pvval = ReadingsNum ($name, "Today_Hour".sprintf("%02d",$h)."_PVreal", 0); my $pvre = ReadingsNum ($name, "Today_Hour".sprintf("%02d",$h)."_PVreal", 0);
next if(!$pvval); next if(!$pvre);
$paref->{hour} = $h; $paref->{hour} = $h;
my ($pvhis,$fchis,$dnum) = __Pv_Fc_Dnum_Hist ($paref); # historischen Percentilfaktor / Qualität ermitteln my ($pvhis,$fchis,$dnum) = __Pv_Fc_Dnum_Hist ($paref); # historischen Percentilfaktor / Qualität ermitteln
@ -9458,7 +9463,7 @@ sub _calcCaQsimple {
next; next;
} }
my $perc = sprintf "%.2f", ($pvval / $est50); # berechneter Faktor der Stunde -> speichern in pvHistory my $perc = sprintf "%.2f", ($pvre / $est50); # berechneter Faktor der Stunde -> speichern in pvHistory
$paref->{pvcorrf} = $perc.'/1'; # Korrekturfaktor / Qualität in History speichern $paref->{pvcorrf} = $perc.'/1'; # Korrekturfaktor / Qualität in History speichern
$paref->{nhour} = sprintf("%02d",$h); $paref->{nhour} = sprintf("%02d",$h);
@ -9470,25 +9475,25 @@ sub _calcCaQsimple {
delete $paref->{nhour}; delete $paref->{nhour};
delete $paref->{pvcorrf}; delete $paref->{pvcorrf};
debugLog ($paref, 'pvCorrection', "Simple Corrf -> PV for hour >$h< -> estimate without corr factor: $est50, real: $pvval"); debugLog ($paref, 'pvCorrection', "Simple Corrf -> PV for hour >$h< -> estimate without corr factor: $est50, real: $pvre");
my $factor; my $factor;
my ($usenhd) = __useNumHistDays ($name); # ist Attr affectNumHistDays gesetzt ? my ($usenhd) = __useNumHistDays ($name); # ist Attr affectNumHistDays gesetzt ?
if ($dnum) { # Werte in History vorhanden -> haben Prio ! if ($dnum) { # Werte in History vorhanden -> haben Prio !
$dnum++; $dnum++;
$pvval = ($pvval + $pvhis) / $dnum; # Ertrag aktuelle Stunde berücksichtigen $pvre = ($pvre + $pvhis) / $dnum; # Ertrag aktuelle Stunde berücksichtigen
$fcval = ($fcval + $fchis) / $dnum; # Vorhersage aktuelle Stunde berücksichtigen $pvfc = ($pvfc + $fchis) / $dnum; # Vorhersage aktuelle Stunde berücksichtigen
$factor = sprintf "%.2f", ($pvval / $fcval); # Faktorberechnung: reale PV / Prognose $factor = sprintf "%.2f", ($pvre / $pvfc); # Faktorberechnung: reale PV / Prognose
} }
elsif ($oldfac && !$usenhd) { # keine Werte in History vorhanden, aber in CircularVal && keine Beschränkung durch Attr affectNumHistDays elsif ($oldfac && !$usenhd) { # keine Werte in History vorhanden, aber in CircularVal && keine Beschränkung durch Attr affectNumHistDays
$dnum = $oldq + 1; $dnum = $oldq + 1;
$factor = sprintf "%.2f", ($pvval / $fcval); $factor = sprintf "%.2f", ($pvre / $pvfc);
$factor = sprintf "%.2f", ($factor + $oldfac) / 2; $factor = sprintf "%.2f", ($factor + $oldfac) / 2;
} }
else { # ganz neuer Wert else { # ganz neuer Wert
$dnum = 1; $dnum = 1;
$factor = sprintf "%.2f", ($pvval / $fcval); $factor = sprintf "%.2f", ($pvre / $pvfc);
$oldfac = '-'; $oldfac = '-';
} }
@ -9506,7 +9511,7 @@ sub _calcCaQsimple {
my $type = $paref->{type}; my $type = $paref->{type};
$data{$type}{$name}{circular}{sprintf("%02d",$h)}{pvcorrf}{percentile} = $factor; # Korrekturfaktor der jeweiligen Stunde als Datenquelle eintragen $data{$type}{$name}{circular}{sprintf("%02d",$h)}{pvcorrf}{percentile} = $factor; # Korrekturfaktor der jeweiligen Stunde als Datenquelle eintragen
$data{$type}{$name}{circular}{sprintf("%02d",$h)}{quality}{percentile} = $dnum; # Korrekturfaktor Qualität $data{$type}{$name}{circular}{sprintf("%02d",$h)}{quality}{percentile} = __calcFcQuality ($pvfc, $pvre); # Qualität der Vorhersage für die vergangene Stunde
push @$daref, ".pvCorrectionFactor_".sprintf("%02d",$h)."_apipercentil<>done"; push @$daref, ".pvCorrectionFactor_".sprintf("%02d",$h)."_apipercentil<>done";
@ -9586,6 +9591,19 @@ sub __Pv_Fc_Dnum_Hist {
return ($pvhis,$fchis,$dnum); return ($pvhis,$fchis,$dnum);
} }
################################################################
# Qualität der Vorhersage berechnen
################################################################
sub __calcFcQuality {
my $pvfc = shift; # PV Vorhersagewert
my $pvre = shift; # PV reale Erzeugung
my $diff = $pvfc - $pvre;
my $hdv = 1 - sprintf "%.2f", abs ($diff / $pvre); # Abweichung der Stunde, 1 = bestmöglicher Wert
return $hdv;
}
################################################################ ################################################################
# Bewölkungs- bzw. Regenrange berechnen # Bewölkungs- bzw. Regenrange berechnen
################################################################ ################################################################
@ -10964,22 +10982,22 @@ sub isAddSwitchOnCond {
my $info = q{}; my $info = q{};
my $err = q{}; my $err = q{};
my $dswoncond = ConsumerVal ($hash, $c, "dswoncond", ""); # Device zur Lieferung einer zusätzlichen Einschaltbedingung my $dswoncond = ConsumerVal ($hash, $c, 'dswoncond', ''); # Device zur Lieferung einer zusätzlichen Einschaltbedingung
if($dswoncond && !$defs{$dswoncond}) { if($dswoncond && !$defs{$dswoncond}) {
$err = qq{ERROR - the device "$dswoncond" doesn't exist! Check the key "swoncond" in attribute "consumer${c}"}; $err = qq{ERROR - the device "$dswoncond" doesn't exist! Check the key "swoncond" in attribute "consumer${c}"};
return (0, $info, $err); return (0, $info, $err);
} }
my $rswoncond = ConsumerVal ($hash, $c, "rswoncond", ""); # Reading zur Lieferung einer zusätzlichen Einschaltbedingung my $rswoncond = ConsumerVal ($hash, $c, 'rswoncond', ''); # Reading zur Lieferung einer zusätzlichen Einschaltbedingung
my $swoncondregex = ConsumerVal ($hash, $c, "swoncondregex", ""); # Regex einer zusätzliche Einschaltbedingung my $swoncondregex = ConsumerVal ($hash, $c, 'swoncondregex', ''); # Regex einer zusätzliche Einschaltbedingung
my $condval = ReadingsVal ($dswoncond, $rswoncond, ""); # Wert zum Vergleich mit Regex my $condval = ReadingsVal ($dswoncond, $rswoncond, ''); # Wert zum Vergleich mit Regex
if ($condval =~ m/^$swoncondregex$/x) { if ($condval =~ m/^$swoncondregex$/x) {
return (1, $info, $err); return (1, $info, $err);
} }
$info = qq{The device "$dswoncond", reading "$rswoncond" doen't match the Regex "$swoncondregex"}; $info = qq{The device "$dswoncond", reading "$rswoncond" doesn't match the Regex "$swoncondregex"};
return (0, $info, $err); return (0, $info, $err);
} }
@ -11011,9 +11029,9 @@ sub isAddSwitchOffCond {
($dswoffcond,$rswoffcond,$swoffcondregex) = split ":", $cond; ($dswoffcond,$rswoffcond,$swoffcondregex) = split ":", $cond;
} }
else { else {
$dswoffcond = ConsumerVal ($hash, $c, "dswoffcond", ""); $dswoffcond = ConsumerVal ($hash, $c, 'dswoffcond', '');
$rswoffcond = ConsumerVal ($hash, $c, "rswoffcond", ""); $rswoffcond = ConsumerVal ($hash, $c, 'rswoffcond', '');
$swoffcondregex = ConsumerVal ($hash, $c, "swoffcondregex", ""); $swoffcondregex = ConsumerVal ($hash, $c, 'swoffcondregex', '');
} }
if($dswoffcond && !$defs{$dswoffcond}) { if($dswoffcond && !$defs{$dswoffcond}) {
@ -11041,6 +11059,47 @@ sub isAddSwitchOffCond {
return (0, $info, $err); return (0, $info, $err);
} }
################################################################
# Funktion liefert "1" wenn die angegebene Bedingung
# aus dem Consumerschlüssel 'spignorecond' erfüllt ist.
#
# $info - den Info-Status
# $err - einen Error-Status
#
################################################################
sub isSurplusIgnoCond {
my $hash = shift;
my $c = shift;
my $debug = shift;
my $info = q{};
my $err = q{};
my $digncond = ConsumerVal ($hash, $c, 'dspignorecond', ''); # Device zur Lieferung einer "Überschuß Ignore-Bedingung"
if($digncond && !$defs{$digncond}) {
$err = qq{ERROR - the device "$digncond" doesn't exist! Check the key "spignorecond" in attribute "consumer${c}"};
return (0, $info, $err);
}
my $rigncond = ConsumerVal ($hash, $c, 'rigncond', ''); # Reading zur Lieferung einer zusätzlichen Einschaltbedingung
my $spignorecondregex = ConsumerVal ($hash, $c, 'spignorecondregex', ''); # Regex einer zusätzliche Einschaltbedingung
my $condval = ReadingsVal ($digncond, $rigncond, ''); # Wert zum Vergleich mit Regex
if ($condval && $debug =~ /consumerSwitching/x) {
my $name = $hash->{NAME};
Log3 ($name, 1, qq{$name DEBUG> consumer "$c" - PV surplus ignore condition ist set - device: $digncond, reading: $rigncond, condition: $spignorecondregex});
}
if ($condval && $condval =~ m/^$spignorecondregex$/x) {
return (1, $info, $err);
}
$info = qq{The device "$digncond", reading "$rigncond" doesn't match the Regex "$spignorecondregex"};
return (0, $info, $err);
}
################################################################ ################################################################
# liefert den Status des Timeframe von Consumer $c # liefert den Status des Timeframe von Consumer $c
################################################################ ################################################################
@ -12543,7 +12602,7 @@ die ordnungsgemäße Anlagenkonfiguration geprüft werden.
<a id="SolarForecast-get-pvCircular"></a> <a id="SolarForecast-get-pvCircular"></a>
<li><b>pvCircular </b> <br><br> <li><b>pvCircular </b> <br><br>
Listet die vorhandenen Werte im Ringspeicher auf. Listet die vorhandenen Werte im Ringspeicher auf.
Die Stundenangaben 00 - 24 beziehen sich auf die Stunde des Tages, z.B. bezieht sich die Stunde 09 auf die Zeit von Die Stundenangaben 01 - 24 beziehen sich auf die Stunde des Tages, z.B. bezieht sich die Stunde 09 auf die Zeit von
08 - 09 Uhr. <br> 08 - 09 Uhr. <br>
Die Stunde 99 hat eine Sonderfunktion. <br> Die Stunde 99 hat eine Sonderfunktion. <br>
Erläuterung der Werte: <br><br> Erläuterung der Werte: <br><br>
@ -12773,7 +12832,8 @@ die ordnungsgemäße Anlagenkonfiguration geprüft werden.
[on=&lt;Kommando&gt;] [off=&lt;Kommando&gt;] [swstate=&lt;Readingname&gt;:&lt;on-Regex&gt;:&lt;off-Regex&gt] [asynchron=&lt;Option&gt] <br> [on=&lt;Kommando&gt;] [off=&lt;Kommando&gt;] [swstate=&lt;Readingname&gt;:&lt;on-Regex&gt;:&lt;off-Regex&gt] [asynchron=&lt;Option&gt] <br>
[notbefore=&lt;Stunde&gt;] [notafter=&lt;Stunde&gt;] [locktime=&lt;Sekunden&gt;]<br> [notbefore=&lt;Stunde&gt;] [notafter=&lt;Stunde&gt;] [locktime=&lt;Sekunden&gt;]<br>
[auto=&lt;Readingname&gt;] [pcurr=&lt;Readingname&gt;:&lt;Einheit&gt;[:&lt;Schwellenwert&gt]] [etotal=&lt;Readingname&gt;:&lt;Einheit&gt;[:&lt;Schwellenwert&gt]] <br> [auto=&lt;Readingname&gt;] [pcurr=&lt;Readingname&gt;:&lt;Einheit&gt;[:&lt;Schwellenwert&gt]] [etotal=&lt;Readingname&gt;:&lt;Einheit&gt;[:&lt;Schwellenwert&gt]] <br>
[swoncond=&lt;Device&gt;:&lt;Reading&gt;:&lt;Regex&gt] [swoffcond=&lt;Device&gt;:&lt;Reading&gt;:&lt;Regex&gt] [interruptable=&lt;Option&gt] </b><br> [swoncond=&lt;Device&gt;:&lt;Reading&gt;:&lt;Regex&gt] [swoffcond=&lt;Device&gt;:&lt;Reading&gt;:&lt;Regex&gt] [spignorecond=&lt;Device&gt;:&lt;Reading&gt;:&lt;Regex&gt] <br>
[interruptable=&lt;Option&gt] </b><br>
<br> <br>
Registriert einen Verbraucher &lt;Device Name&gt; beim SolarForecast Device. Dabei ist &lt;Device Name&gt; Registriert einen Verbraucher &lt;Device Name&gt; beim SolarForecast Device. Dabei ist &lt;Device Name&gt;
@ -12862,11 +12922,17 @@ die ordnungsgemäße Anlagenkonfiguration geprüft werden.
<tr><td> <b>swoncond</b> </td><td>Bedingung die zusätzlich erfüllt sein muß um den Verbraucher einzuschalten (optional). Der geplante Zyklus wird gestartet. </td></tr> <tr><td> <b>swoncond</b> </td><td>Bedingung die zusätzlich erfüllt sein muß um den Verbraucher einzuschalten (optional). Der geplante Zyklus wird gestartet. </td></tr>
<tr><td> </td><td><b>Device</b> - Device zur Lieferung der zusätzlichen Einschaltbedingung </td></tr> <tr><td> </td><td><b>Device</b> - Device zur Lieferung der zusätzlichen Einschaltbedingung </td></tr>
<tr><td> </td><td><b>Reading</b> - Reading zur Lieferung der zusätzlichen Einschaltbedingung </td></tr> <tr><td> </td><td><b>Reading</b> - Reading zur Lieferung der zusätzlichen Einschaltbedingung </td></tr>
<tr><td> </td><td><b>Regex</b> - regulärer Ausdruck der für die Einschaltbedingung erfüllt sein muß </td></tr> <tr><td> </td><td><b>Regex</b> - regulärer Ausdruck der für eine 'wahre' Bedingung erfüllt sein muß </td></tr>
<tr><td> <b>swoffcond</b> </td><td>vorrangige Bedingung um den Verbraucher auszuschalten (optional). Der geplante Zyklus wird gestoppt. </td></tr> <tr><td> <b>swoffcond</b> </td><td>vorrangige Bedingung um den Verbraucher auszuschalten (optional). Der geplante Zyklus wird gestoppt. </td></tr>
<tr><td> </td><td><b>Device</b> - Device zur Lieferung der vorrangigen Ausschaltbedingung </td></tr> <tr><td> </td><td><b>Device</b> - Device zur Lieferung der vorrangigen Ausschaltbedingung </td></tr>
<tr><td> </td><td><b>Reading</b> - Reading zur Lieferung der vorrangigen Ausschaltbedingung </td></tr> <tr><td> </td><td><b>Reading</b> - Reading zur Lieferung der vorrangigen Ausschaltbedingung </td></tr>
<tr><td> </td><td><b>Regex</b> - regulärer Ausdruck der für die Ausschaltbedingung erfüllt sein muß </td></tr> <tr><td> </td><td><b>Regex</b> - regulärer Ausdruck der für eine 'wahre' Bedingung erfüllt sein muß </td></tr>
<tr><td> <b>spignorecond</b> </td><td>Bedingung um einen PV Überschuß zu ignorieren (optional). Bei erfüllter Bedingung wird der Verbraucher entsprechend der Planung eingeschaltet </td></tr>
<tr><td> </td><td>auch wenn zu dem Zeitpunkt kein PV Überschuß vorliegt. </td></tr>
<tr><td> </td><td><b>ACHTUNG:</b> Die Verwendung beider Schlüssel <I>spignorecond</I> und <I>interruptable</I> kann zu einem unerwünschten Verhalten führen! </td></tr>
<tr><td> </td><td><b>Device</b> - Device zur Lieferung der Bedingung </td></tr>
<tr><td> </td><td><b>Reading</b> - Reading welches die Bedingung enthält </td></tr>
<tr><td> </td><td><b>Regex</b> - regulärer Ausdruck der für eine 'wahre' Bedingung erfüllt sein muß </td></tr>
<tr><td> <b>interruptable</b> </td><td>definiert die möglichen Unterbrechungsoptionen für den Verbraucher nachdem er gestartet wurde (optional) </td></tr> <tr><td> <b>interruptable</b> </td><td>definiert die möglichen Unterbrechungsoptionen für den Verbraucher nachdem er gestartet wurde (optional) </td></tr>
<tr><td> </td><td><b>0</b> - Verbraucher wird nicht temporär ausgeschaltet auch wenn der PV Überschuß die benötigte Energie unterschreitet (default) </td></tr> <tr><td> </td><td><b>0</b> - Verbraucher wird nicht temporär ausgeschaltet auch wenn der PV Überschuß die benötigte Energie unterschreitet (default) </td></tr>
<tr><td> </td><td><b>1</b> - Verbraucher wird temporär ausgeschaltet falls der PV Überschuß die benötigte Energie unterschreitet </td></tr> <tr><td> </td><td><b>1</b> - Verbraucher wird temporär ausgeschaltet falls der PV Überschuß die benötigte Energie unterschreitet </td></tr>