diff --git a/fhem/contrib/DS_Starter/76_SolarForecast.pm b/fhem/contrib/DS_Starter/76_SolarForecast.pm
index 7fd9fef0a..985268d0a 100644
--- a/fhem/contrib/DS_Starter/76_SolarForecast.pm
+++ b/fhem/contrib/DS_Starter/76_SolarForecast.pm
@@ -27,6 +27,8 @@
# Leerzeichen entfernen: sed -i 's/[[:space:]]*$//' 76_SolarForecast.pm
#
#########################################################################################################################
+main::LoadModule ('Astro'); # Astro Modul für Sonnenkennzahlen laden
+
package FHEM::SolarForecast; ## no critic 'package'
use strict;
@@ -53,7 +55,7 @@ use FHEM::SynoModules::SMUtils qw(
moduleVersion
trim
); # Hilfsroutinen Modul
-
+
use Data::Dumper;
use Blocking;
use Storable qw(dclone freeze thaw nstore store retrieve);
@@ -152,17 +154,27 @@ BEGIN {
NexthoursVal
)
);
-
}
# Versions History intern
my %vNotesIntern = (
+ "1.15.5" => "11.02.2024 change forecastQualities output, new limits for 'accurate' and 'spreaded' results from AI ".
+ "checkPlantConfig: change common check info output ".
+ "fix load Astro ",
+ "1.15.4" => "10.02.2024 integrate sun position from Astro module, setPVhistory: change some writes ".
+ "_transferAPIRadiationValues: consider 'accurate' or 'spreaded' result from AI".
+ "___calcPeaklossByTemp: bugfix temp, rename moduleDirection to moduleAzimuth ".
+ "rename moduleTiltAngle to moduleDeclination, checkPlantConfig: check global altitude attr ",
+ "1.15.3" => "06.02.2024 Header: add links to the API website dependend from the used API ",
+ "1.15.2" => "05.02.2024 __mergeDataWeather: fix merger failures, number of temperature decimal places ".
+ "cicrular Hash: replace 'percentile' by 'simple' ",
+ "1.15.1" => "04.02.2024 checkPlantConfig: fix check attribute ctrlWeatherDevX ",
"1.15.0" => "03.02.2024 reduce cpu utilization, add attributes ctrlWeatherDev2, ctrlWeatherDev3 ",
"1.14.3" => "02.02.2024 _transferWeatherValues: first step of multi weather device merger ",
"1.14.2" => "02.02.2024 fix warning, _transferAPIRadiationValues: Consider upper and lower deviation limit AI to API forecast ",
"1.14.1" => "01.02.2024 language support for ___setConsumerPlanningState -> supplement, fix setting 'swoncond not met' ",
- "1.14.0" => "31.01.2024 data maintenance, new sub _addDynAttr for adding attributes at runtime ".
- "replace setter currentWeatherDev by attr ctrlWeatherDev1, new data with sub CircularSumVal ".
+ "1.14.0" => "31.01.2024 data maintenance, new func _addDynAttr for adding attributes at runtime ".
+ "replace setter currentWeatherDev by attr ctrlWeatherDev1, new data with func CircularSumVal ".
"rewrite correction factor calculation with _calcCaQcomplex, _calcCaQsimple, __calcNewFactor ",
"1.13.0" => "27.01.2024 minor change of deleteOldBckpFiles, Setter writeHistory replaced by operatingMemory ".
"save, backup and recover in-memory operating data ",
@@ -173,13 +185,13 @@ my %vNotesIntern = (
"1.9.0" => "23.01.2024 modify disable, add operationMode: active/inactive ",
"1.8.0" => "22.01.2024 add 'noLearning' Option to Setter pvCorrectionFactor_Auto ",
"1.7.1" => "20.01.2024 optimize battery management ",
- "1.7.0" => "18.01.2024 Changeover Start centralTask completely to runCentralTask, ".
+ "1.7.0" => "18.01.2024 Changeover Start centralTask completely to runTask, ".
"aiAddRawData: Weekday from pvHistory not taken into account greater than current day ".
"__reviewSwitchTime: new function for review consumer planning state ".
"___switchonTimelimits: The current time is taken into account during planning ".
"take info-tag into consumerxx Reading ".
"fix deletion of currentBatteryDev, currentInverterDev, currentMeterDev ",
- "1.6.5" => "10.01.2024 new function runCentralTask in ReadyFn to run centralTask definitely at end/begin of an hour ",
+ "1.6.5" => "10.01.2024 new function runTask in ReadyFn to run centralTask definitely at end/begin of an hour ",
"1.6.4" => "09.01.2024 fix get Automatic State, use key switchdev for auto-Reading if switchdev is set in consumer attr ",
"1.6.3" => "08.01.2024 optimize battery management once more ",
"1.6.2" => "07.01.2024 optimize battery management ",
@@ -264,7 +276,7 @@ my %vNotesIntern = (
"0.80.4" => "06.07.2023 new transferprocess for DWD data from solcastapi-Hash to estimate calculation, consolidated ".
"the autocorrection model ",
"0.80.3" => "03.06.2023 preparation for get DWD radiation data to solcastapi-Hash, fix sub isConsumerLogOn (use powerthreshold) ",
- "0.80.2" => "02.06.2023 new ctrlDebug keys epiecesCalc, change selfconsumption with graphic Adjustment, moduleDirection ".
+ "0.80.2" => "02.06.2023 new ctrlDebug keys epiecesCalc, change selfconsumption with graphic Adjustment, moduleAzimuth ".
"accepts azimut values -180 .. 0 .. 180 as well as azimut identifier (S, SE ..) ",
"0.80.1" => "31.05.2023 adapt _calcCaQsimple to calculate corrfactor like _calcCaQcomplex ",
"0.80.0" => "28.05.2023 Support for Forecast.Solar-API (https://doc.forecast.solar/api), rename Getter solCastData to solApiData ".
@@ -319,8 +331,10 @@ my $airaw = $root."/FHEM/FhemUtils/AIraw_SolarForecast_";
my $aitrblto = 7200; # KI Training BlockingCall Timeout
my $aibcthhld = 0.2; # Schwelle der KI Trainigszeit ab der BlockingCall benutzt wird
my $aistdudef = 1095; # default Haltezeit KI Raw Daten (Tage)
-my $aiDevUpLim = 150; # obere Abweichungsgrenze (%) AI von API Prognose
-my $aiDevLowLim = 50; # untere Abweichungsgrenze (%) AI von API Prognose
+my $aiSpreadUpLim = 150; # obere Abweichungsgrenze (%) AI 'Spread' von API Prognose
+my $aiSpreadLowLim = 50; # untere Abweichungsgrenze (%) AI 'Spread' von API Prognose
+my $aiAccUpLim = 170; # obere Abweichungsgrenze (%) AI 'Accurate' von API Prognose
+my $aiAccLowLim = 30; # untere Abweichungsgrenze (%) AI 'Accurate' von API Prognose
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
@@ -448,8 +462,8 @@ my %hset = ( # Ha
reset => { fn => \&_setreset },
roofIdentPair => { fn => \&_setroofIdentPair },
moduleRoofTops => { fn => \&_setmoduleRoofTops },
- moduleTiltAngle => { fn => \&_setmoduleTiltAngle },
- moduleDirection => { fn => \&_setmoduleDirection },
+ moduleDeclination => { fn => \&_setmoduleDeclination },
+ moduleAzimuth => { fn => \&_setmoduleAzimuth },
operatingMemory => { fn => \&_setoperatingMemory },
vrmCredentials => { fn => \&_setVictronCredentials },
aiDecTree => { fn => \&_setaiDecTree },
@@ -533,10 +547,10 @@ my %hqtxt = (
DE => qq{Bitte geben sie alle von Ihnen verwendeten Stringnamen mit "set LINK inverterStrings" an} },
mps => { EN => qq{Please enter the DC peak power of each string with "set LINK modulePeakString"},
DE => qq{Bitte geben sie die DC Spitzenleistung 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} },
+ mdr => { EN => qq{Please specify the module direction with "set LINK moduleAzimuth"},
+ DE => qq{Bitte geben sie die Modulausrichtung mit "set LINK moduleAzimuth" an} },
+ mta => { EN => qq{Please specify the module tilt angle with "set LINK moduleDeclination"},
+ DE => qq{Bitte geben sie den Modulneigungswinkel mit "set LINK moduleDeclination" an} },
rip => { EN => qq{Please specify at least one combination Rooftop-ID/SolCast-API with "set LINK roofIdentPair"},
DE => qq{Bitte geben Sie mindestens eine Kombination Rooftop-ID/SolCast-API mit "set LINK roofIdentPair" an} },
mrt => { EN => qq{Please set the assignment String / Rooftop identification with "set LINK moduleRoofTops"},
@@ -567,6 +581,8 @@ my %hqtxt = (
DE => qq{Status} },
result => { EN => qq{Result},
DE => qq{Ergebnis} },
+ attrib => { EN => qq{attribute},
+ DE => qq{Attribut} },
note => { EN => qq{Note},
DE => qq{Hinweis} },
wfmdcf => { EN => qq{Wait for more days with a consumption figure},
@@ -924,7 +940,7 @@ sub Initialize {
$hash->{DbLog_splitFn} = \&DbLogSplit;
$hash->{AttrFn} = \&Attr;
$hash->{NotifyFn} = \&Notify;
- $hash->{ReadyFn} = \&runCentralTask;
+ $hash->{ReadyFn} = \&runTask;
$hash->{AttrList} = "affect70percentRule:1,dynamic,0 ".
"affectBatteryPreferredCharge:slider,0,1,100 ".
"affectConsForecastIdentWeekdays:1,0 ".
@@ -1230,8 +1246,8 @@ sub Set {
;
}
elsif (isForecastSolarUsed ($hash)) {
- $setlist .= "moduleDirection ".
- "moduleTiltAngle "
+ $setlist .= "moduleAzimuth ".
+ "moduleDeclination "
;
}
elsif (isVictronKiUsed ($hash)) {
@@ -1239,8 +1255,8 @@ sub Set {
;
}
else {
- $setlist .= "moduleDirection ".
- "moduleTiltAngle "
+ $setlist .= "moduleAzimuth ".
+ "moduleDeclination "
;
}
@@ -1393,11 +1409,11 @@ sub _setcurrentRadiationAPI { ## no critic "not used"
my ($set, $lat, $lon) = locCoordinates();
return qq{set attributes 'latitude' and 'longitude' in global device first} if(!$set);
- my $tilt = ReadingsVal ($name, 'moduleTiltAngle', ''); # Modul Neigungswinkel für jeden Stringbezeichner
- return qq{Please complete command "set $name moduleTiltAngle".} if(!$tilt);
+ my $tilt = ReadingsVal ($name, 'moduleDeclination', ''); # Modul Neigungswinkel für jeden Stringbezeichner
+ return qq{Please complete command "set $name moduleDeclination".} if(!$tilt);
- my $dir = ReadingsVal ($name, 'moduleDirection', ''); # Modul Ausrichtung für jeden Stringbezeichner
- return qq{Please complete command "set $name moduleDirection".} if(!$dir);
+ my $dir = ReadingsVal ($name, 'moduleAzimuth', ''); # Modul Ausrichtung für jeden Stringbezeichner
+ return qq{Please complete command "set $name moduleAzimuth".} if(!$dir);
}
if ($prop eq 'VictronVRM-API') {
@@ -1595,7 +1611,7 @@ sub _setinverterStrings { ## no critic "not used"
return if(_checkSetupNotComplete ($hash)); # keine Stringkonfiguration wenn Setup noch nicht komplett
my $ret = qq{NOTE: After setting or changing "inverterStrings" please check }.
- qq{/ set all module parameter (e.g. moduleTiltAngle) again ! \n}.
+ qq{/ set all module parameter (e.g. moduleDeclination) again ! \n}.
qq{Use "set $name plantConfiguration check" to validate your Setup.};
return $ret;
@@ -1789,9 +1805,9 @@ return;
}
################################################################
-# Setter moduleTiltAngle
+# Setter moduleDeclination
################################################################
-sub _setmoduleTiltAngle { ## no critic "not used"
+sub _setmoduleDeclination { ## no critic "not used"
my $paref = shift;
my $hash = $paref->{hash};
my $name = $paref->{name};
@@ -1811,8 +1827,8 @@ sub _setmoduleTiltAngle { ## no critic "not used"
}
}
- readingsSingleUpdate ($hash, "moduleTiltAngle", $arg, 1);
- writeCacheToFile ($hash, "plantconfig", $plantcfg.$name); # Anlagenkonfiguration File schreiben
+ readingsSingleUpdate ($hash, 'moduleDeclination', $arg, 1);
+ writeCacheToFile ($hash, 'plantconfig', $plantcfg.$name); # Anlagenkonfiguration File schreiben
return if(_checkSetupNotComplete ($hash)); # keine Stringkonfiguration wenn Setup noch nicht komplett
@@ -1823,13 +1839,13 @@ return;
}
################################################################
-# Setter moduleDirection
+# Setter moduleAzimuth
#
# Angabe entweder als Azimut-Bezeichner oder direkte
# Azimut Angabe -180 ...0...180
#
################################################################
-sub _setmoduleDirection { ## no critic "not used"
+sub _setmoduleAzimuth { ## no critic "not used"
my $paref = shift;
my $hash = $paref->{hash};
my $name = $paref->{name};
@@ -1849,8 +1865,8 @@ sub _setmoduleDirection { ## no critic "not used"
}
}
- readingsSingleUpdate ($hash, "moduleDirection", $arg, 1);
- writeCacheToFile ($hash, "plantconfig", $plantcfg.$name); # Anlagenkonfiguration File schreiben
+ readingsSingleUpdate ($hash, 'moduleAzimuth', $arg, 1);
+ writeCacheToFile ($hash, 'plantconfig', $plantcfg.$name); # Anlagenkonfiguration File schreiben
return if(_checkSetupNotComplete ($hash)); # keine Stringkonfiguration wenn Setup noch nicht komplett
@@ -2048,7 +2064,9 @@ sub _setreset { ## no critic "not used"
$paref->{reorg} = 1; # den Tag Stunde "99" reorganisieren
$paref->{reorgday} = $dday;
+
setPVhistory ($paref);
+
delete $paref->{reorg};
delete $paref->{reorgday};
}
@@ -4208,7 +4226,7 @@ sub Attr {
### nicht mehr benötigte Daten löschen - Bereich kann später wieder raus !!
##########################################################################################
- if ($cmd eq 'set' && $aName eq 'affectNumHistDays') {
+ if ($cmd eq 'set' && $aName eq 'affectNumHistDays') { # 30.01.2024
if (!$init_done) {
return qq{Device "$name" -> The attribute '$aName' is obsolete and will be deleted soon. Please press "save config" when restart is finished.};
}
@@ -4912,16 +4930,16 @@ sub _savePlantConfig {
currentMeterDev
currentRadiationAPI
inverterStrings
- moduleDirection
+ moduleAzimuth
modulePeakString
- moduleTiltAngle
+ moduleDeclination
moduleRoofTops
powerTrigger
energyH4Trigger
);
for my $cfg (@aconfigs) {
- my $val = ReadingsVal($name, $cfg, "");
+ my $val = ReadingsVal ($name, $cfg, "");
next if(!$val);
push @pvconf, $cfg."<>".$val;
}
@@ -4933,7 +4951,7 @@ return @pvconf;
# centralTask Start Management
# Achtung: relevant für CPU Auslastung!
################################################################
-sub runCentralTask {
+sub runTask {
my $hash = shift;
return if(!$init_done || CurrentVal ($hash, 'ctrunning', 0));
@@ -4984,14 +5002,15 @@ sub runCentralTask {
storeReading ('nextCycletime', FmtTime($new));
centralTask ($hash, 1);
}
+
+ my $debug = getDebug ($hash);
if ($min == 59 && $sec > 48) {
if (!exists $hash->{HELPER}{S48DONE}) {
- my $debug = getDebug ($hash);
$hash->{HELPER}{S48DONE} = 1;
if ($debug =~ /collectData/x) {
- Log3 ($name, 1, "$name DEBUG> INFO - Start of unscheduled data collection at the end of an hour");
+ Log3 ($name, 1, "$name DEBUG> INFO - runTask starts data collection at the end of an hour");
}
centralTask ($hash, 1);
@@ -5003,11 +5022,10 @@ sub runCentralTask {
if ($min == 0 && $sec > 3) {
if (!exists $hash->{HELPER}{S03DONE}) {
- my $debug = getDebug ($hash);
$hash->{HELPER}{S03DONE} = 1;
if ($debug =~ /collectData/x) {
- Log3 ($name, 1, "$name DEBUG> INFO - Start of unscheduled data collection at the beginning of an hour");
+ Log3 ($name, 1, "$name DEBUG> INFO - runTask starts data collection at the beginning of an hour");
}
centralTask ($hash, 1);
@@ -5016,6 +5034,32 @@ sub runCentralTask {
else {
delete $hash->{HELPER}{S03DONE};
}
+
+ if ($min =~ /^(2|22|42)$/xs && $sec > 20) {
+ if (!exists $hash->{HELPER}{SUNCALCDONE}) {
+ $hash->{HELPER}{SUNCALCDONE} = 1;
+
+ if ($debug =~ /collectData/x) {
+ Log3 ($name, 1, "$name DEBUG> INFO - runTask starts calculation of Sun positions");
+ }
+
+ my $day = strftime "%d", localtime($t); # aktueller Tag (range 01 to 31)
+ my $chour = strftime "%H", localtime($t); # aktuelle Stunde in 24h format (00-23)
+
+ _calcSunPosition ( { hash => $hash,
+ name => $name,
+ type => $hash->{TYPE},
+ t => $t,
+ day => $day,
+ chour => $chour,
+ debug => $debug
+ }
+ );
+ }
+ }
+ else {
+ delete $hash->{HELPER}{SUNCALCDONE};
+ }
return;
}
@@ -5050,6 +5094,62 @@ sub _addDynAttr {
return;
}
+################################################################
+# Ermittlung der Sonnenpositionen
+# Az,Alt = Azimuth und Höhe (in Dezimalgrad) des Körpers
+# über dem Horizont
+################################################################
+sub _calcSunPosition {
+ my $paref = shift;
+ my $hash = $paref->{hash};
+ my $name = $paref->{name};
+ my $type = $paref->{type};
+ my $t = $paref->{t}; # Epoche Zeit
+ my $chour = $paref->{chour};
+ my $day = $paref->{day};
+
+ debugLog ($paref, 'collectData', "collect Sun position data =>");
+
+ for my $num (0..46) {
+ my ($fd, $fh) = _calcDayHourMove ($chour, $num);
+ last if($fd > 1);
+
+ my $tstr = (timestampToTimestring ($t + ($num * 3600)))[3];
+ my ($date, $h, $m, $s) = split /[ :]/, $tstr;
+ $tstr = $date.' '.$h.':30:00';
+ my $hod = sprintf "%02d", $h + 1;
+ my $nhtstr = "NextHour".sprintf "%02d", $num;
+
+ my $az = sprintf "%.0f", FHEM::Astro::Get (undef, 'global', 'text', 'SunAz', $tstr); # statt Astro_Get geht auch FHEM::Astro::Get
+ my $alt = sprintf "%.0f", FHEM::Astro::Get (undef, 'global', 'text', 'SunAlt', $tstr);
+
+ $data{$type}{$name}{nexthours}{$nhtstr}{sunaz} = $az;
+ $data{$type}{$name}{nexthours}{$nhtstr}{sunalt} = $alt;
+
+ debugLog ($paref, 'collectData', "Sun position: hod: $hod, $tstr, azimuth: $az, altitude: $alt");
+
+ if ($fd == 0 && $hod) { # Sun Position in pvHistory speichern
+ $paref->{nhour} = sprintf "%02d", $hod;
+
+ $paref->{val} = $az;
+ $paref->{histname} = 'sunaz';
+ setPVhistory ($paref);
+
+ $paref->{val} = $alt;
+ $paref->{histname} = 'sunalt';
+ setPVhistory ($paref);
+
+ delete $paref->{histname};
+ delete $paref->{val};
+ delete $paref->{nhour};
+ }
+ }
+
+ debugLog ($paref, 'collectData', "All Sun position data collected");
+
+return;
+}
+
################################################################
# Zentraler Datenabruf
################################################################
@@ -5080,10 +5180,45 @@ sub centralTask {
### nicht mehr benötigte Daten löschen - Bereich kann später wieder raus !!
##########################################################################################
- ## nicht-Bin Werte löschen
- my $ra = '0|5|10|15|20|25|30|35|40|45|50|55|60|65|70|75|80|85|90|95|100|percentile';
+ ## AI Raw Daten formatieren # 09.02.2024
+ if (defined $data{$type}{$name}{aidectree}{airaw}) {
+ for my $idx (sort keys %{$data{$type}{$name}{aidectree}{airaw}}) {
+ $data{$type}{$name}{aidectree}{airaw}{$idx}{wrp} = 0 if(AiRawdataVal ($hash, $idx, 'wrp', 0) eq '00');
+ $data{$type}{$name}{aidectree}{airaw}{$idx}{wrp} = 5 if(AiRawdataVal ($hash, $idx, 'wrp', 0) eq '05');
+ $data{$type}{$name}{aidectree}{airaw}{$idx}{temp} = 0 if(AiRawdataVal ($hash, $idx, 'temp', 0) eq '00');
+ $data{$type}{$name}{aidectree}{airaw}{$idx}{temp} = 5 if(AiRawdataVal ($hash, $idx, 'temp', 0) eq '05');
+ $data{$type}{$name}{aidectree}{airaw}{$idx}{temp} = -5 if(AiRawdataVal ($hash, $idx, 'temp', 0) eq '-05');
+ }
+ }
- for my $hod (keys %{$data{$type}{$name}{circular}}) {
+ ## percentile in simple umsetzen # 05.02.2024
+ for my $idx (sort keys %{$data{$type}{$name}{circular}}) {
+ if(defined $data{$type}{$name}{circular}{$idx}{pvcorrf}{percentile}) {
+ $data{$type}{$name}{circular}{$idx}{pvcorrf}{simple} = $data{$type}{$name}{circular}{$idx}{pvcorrf}{percentile};
+ delete $data{$type}{$name}{circular}{$idx}{pvcorrf}{percentile};
+ }
+ if(defined $data{$type}{$name}{circular}{$idx}{quality}{percentile}) {
+ $data{$type}{$name}{circular}{$idx}{quality}{simple} = $data{$type}{$name}{circular}{$idx}{quality}{percentile};
+ delete $data{$type}{$name}{circular}{$idx}{quality}{percentile};
+ }
+ if(defined $data{$type}{$name}{circular}{$idx}{pvrlsum}{percentile}) {
+ $data{$type}{$name}{circular}{$idx}{pvrlsum}{simple} = $data{$type}{$name}{circular}{$idx}{pvrlsum}{percentile};
+ delete $data{$type}{$name}{circular}{$idx}{pvrlsum}{percentile};
+ }
+ if(defined $data{$type}{$name}{circular}{$idx}{pvfcsum}{percentile}) {
+ $data{$type}{$name}{circular}{$idx}{pvfcsum}{simple} = $data{$type}{$name}{circular}{$idx}{pvfcsum}{percentile};
+ delete $data{$type}{$name}{circular}{$idx}{pvfcsum}{percentile};
+ }
+ if(defined $data{$type}{$name}{circular}{$idx}{dnumsum}{percentile}) {
+ $data{$type}{$name}{circular}{$idx}{dnumsum}{simple} = $data{$type}{$name}{circular}{$idx}{dnumsum}{percentile};
+ delete $data{$type}{$name}{circular}{$idx}{dnumsum}{percentile};
+ }
+ }
+
+ ## nicht-Bin Werte löschen
+ my $ra = '0|00|05|5|10|15|20|25|30|35|40|45|50|55|60|65|70|75|80|85|90|95|100|percentile|simple';
+
+ for my $hod (keys %{$data{$type}{$name}{circular}}) { # 30.01.2024
for my $range (keys %{$data{$type}{$name}{circular}{$hod}{pvcorrf}}) {
delete $data{$type}{$name}{circular}{$hod}{pvcorrf}{$range} if($range !~ /^($ra)$/xs);
}
@@ -5093,12 +5228,25 @@ sub centralTask {
}
}
## currentWeatherDev in Attr umsetzen
- my $cwd = ReadingsVal ($name, 'currentWeatherDev', '');
+ my $cwd = ReadingsVal ($name, 'currentWeatherDev', ''); # 30.01.2024
if ($cwd) {
CommandAttr (undef, "$name ctrlWeatherDev1 $cwd");
readingsDelete ($hash, 'currentWeatherDev') if(AttrVal ($name, 'ctrlWeatherDev1', '')); # erst prüfen ob gesetzt
}
+ ## Reading umsetzen
+ my $mdr = ReadingsVal ($name, 'moduleDirection', undef); # 09.02.2024
+ if ($mdr) {
+ readingsSingleUpdate ($hash, 'moduleAzimuth', $mdr, 0);
+ readingsDelete ($hash, 'moduleDirection');
+ }
+
+ my $mta = ReadingsVal ($name, 'moduleTiltAngle', undef); # 09.02.2024
+ if ($mta) {
+ readingsSingleUpdate ($hash, 'moduleDeclination', $mta, 0);
+ readingsDelete ($hash, 'moduleTiltAngle');
+ }
+
############################################################################################
return if(!$init_done);
@@ -5162,7 +5310,7 @@ sub centralTask {
collectAllRegConsumers ($centpars); # alle Verbraucher Infos laden
_specialActivities ($centpars); # zusätzliche Events generieren + Sonderaufgaben
_transferWeatherValues ($centpars); # Wetterwerte übertragen
-
+
readingsDelete ($hash, 'AllPVforecastsToEvent');
_getRoofTopData ($centpars); # Strahlungswerte/Forecast-Werte in solcastapi-Hash erstellen
@@ -5259,8 +5407,8 @@ sub createStringConfig { ## no critic "not used"
}
}
elsif (!isVictronKiUsed ($hash)) {
- my $tilt = ReadingsVal ($name, 'moduleTiltAngle', ''); # Modul Neigungswinkel für jeden Stringbezeichner
- return qq{Please complete command "set $name moduleTiltAngle".} if(!$tilt);
+ my $tilt = ReadingsVal ($name, 'moduleDeclination', ''); # Modul Neigungswinkel für jeden Stringbezeichner
+ return qq{Please complete command "set $name moduleDeclination".} if(!$tilt);
my ($at,$ht) = parseParams ($tilt);
@@ -5269,15 +5417,15 @@ sub createStringConfig { ## no critic "not used"
$data{$type}{$name}{strings}{$key}{tilt} = $value;
}
else {
- return qq{Check "moduleTiltAngle" -> the stringname "$key" is not defined as valid string in reading "inverterStrings"};
+ return qq{Check "moduleDeclination" -> the stringname "$key" is not defined as valid string in reading "inverterStrings"};
}
}
- my $dir = ReadingsVal ($name, 'moduleDirection', ''); # Modul Ausrichtung für jeden Stringbezeichner
- return qq{Please complete command "set $name moduleDirection".} if(!$dir);
+ my $dir = ReadingsVal ($name, 'moduleAzimuth', ''); # Modul Ausrichtung für jeden Stringbezeichner
+ return qq{Please complete command "set $name moduleAzimuth".} if(!$dir);
my ($ad,$hd) = parseParams ($dir);
- my $iwrong = qq{Please check the input of set "moduleDirection". It seems to be wrong.};
+ my $iwrong = qq{Please check the input of set "moduleAzimuth". It seems to be wrong.};
while (my ($key, $value) = each %$hd) {
if ($key ~~ @istrings) {
@@ -5285,14 +5433,14 @@ sub createStringConfig { ## no critic "not used"
$data{$type}{$name}{strings}{$key}{azimut} = _ident2azimuth ($value) // return $iwrong;
}
else {
- return qq{Check "moduleDirection" -> the stringname "$key" is not defined as valid string in reading "inverterStrings"};
+ return qq{Check "moduleAzimuth" -> the stringname "$key" is not defined as valid string in reading "inverterStrings"};
}
}
}
if(!keys %{$data{$type}{$name}{strings}}) {
return qq{The string configuration seems to be incomplete. \n}.
- qq{Please check the settings of inverterStrings, modulePeakString, moduleDirection, moduleTiltAngle }.
+ qq{Please check the settings of inverterStrings, modulePeakString, moduleAzimuth, moduleDeclination }.
qq{and/or moduleRoofTops if SolCast-API is used.};
}
@@ -5633,7 +5781,6 @@ sub _transferWeatherValues {
__mergeDataWeather ($paref); # Wetterdaten zusammenfügen
- my $time_str;
my $cat = 'merge';
for my $num (0..46) {
@@ -5649,13 +5796,13 @@ sub _transferWeatherValues {
my $temp = $data{$type}{$name}{weatherdata}{"fc${fd}_${fh1}"}{$cat}{ttt}; # Außentemperatur
my $don = $data{$type}{$name}{weatherdata}{"fc${fd}_${fh1}"}{$cat}{don}; # Tag/Nacht-Grenze
- $time_str = "NextHour".sprintf "%02d", $num;
- $data{$type}{$name}{nexthours}{$time_str}{weatherid} = $wid;
- $data{$type}{$name}{nexthours}{$time_str}{cloudcover} = $neff;
- $data{$type}{$name}{nexthours}{$time_str}{rainprob} = $r101;
- $data{$type}{$name}{nexthours}{$time_str}{rainrange} = rain2bin ($r101);
- $data{$type}{$name}{nexthours}{$time_str}{temp} = $temp;
- $data{$type}{$name}{nexthours}{$time_str}{DoN} = $don;
+ my $nhtstr = "NextHour".sprintf "%02d", $num;
+ $data{$type}{$name}{nexthours}{$nhtstr}{weatherid} = $wid;
+ $data{$type}{$name}{nexthours}{$nhtstr}{cloudcover} = $neff;
+ $data{$type}{$name}{nexthours}{$nhtstr}{rainprob} = $r101;
+ $data{$type}{$name}{nexthours}{$nhtstr}{rainrange} = rain2bin ($r101);
+ $data{$type}{$name}{nexthours}{$nhtstr}{temp} = $temp;
+ $data{$type}{$name}{nexthours}{$nhtstr}{DoN} = $don;
if ($num < 23 && $fh < 24) { # Ringspeicher Weather Forum: https://forum.fhem.de/index.php/topic,117864.msg1139251.html#msg1139251
$data{$type}{$name}{circular}{sprintf("%02d",$fh1)}{weatherid} = $wid;
@@ -5664,7 +5811,7 @@ sub _transferWeatherValues {
$data{$type}{$name}{circular}{sprintf("%02d",$fh1)}{wrp} = $r101;
$data{$type}{$name}{circular}{sprintf("%02d",$fh1)}{temp} = $temp;
- if ($num == 0) { # aktuelle Außentemperatur
+ if ($num == 0) { # aktuelle Außentemperatur
$data{$type}{$name}{current}{temp} = $temp;
}
}
@@ -5766,11 +5913,17 @@ sub __mergeDataWeather {
debugLog ($paref, 'collectData', "merge Weather data =>");
+ my $ds = 0;
+
+ for my $wd (1..$weatherDevMax) {
+ my $fcname = AttrVal ($name, 'ctrlWeatherDev'.$wd, ''); # Weather Forecast Device
+ $ds++ if($fcname && $defs{$fcname});
+ }
+
my ($q, $m) = (0,0);
for my $key (sort keys %{$data{$type}{$name}{weatherdata}}) {
my ($z, $neff, $r101, $temp) = (0,0,0,0);
- $q++;
$data{$type}{$name}{weatherdata}{$key}{merge}{don} = $data{$type}{$name}{weatherdata}{$key}{1}{don};
$data{$type}{$name}{weatherdata}{$key}{merge}{ww} = $data{$type}{$name}{weatherdata}{$key}{1}{ww};
@@ -5779,7 +5932,9 @@ sub __mergeDataWeather {
$data{$type}{$name}{weatherdata}{$key}{merge}{r101} = $data{$type}{$name}{weatherdata}{$key}{1}{r101};
$data{$type}{$name}{weatherdata}{$key}{merge}{ttt} = $data{$type}{$name}{weatherdata}{$key}{1}{ttt};
- for my $step (1..$weatherDevMax) {
+ for my $step (1..$ds) {
+ $q++;
+
my $n = $data{$type}{$name}{weatherdata}{$key}{$step}{neff};
my $r = $data{$type}{$name}{weatherdata}{$key}{$step}{r101};
my $t = $data{$type}{$name}{weatherdata}{$key}{$step}{ttt};
@@ -5797,7 +5952,7 @@ sub __mergeDataWeather {
$data{$type}{$name}{weatherdata}{$key}{merge}{neff} = sprintf "%.0f", ($neff / $z);
$data{$type}{$name}{weatherdata}{$key}{merge}{r101} = sprintf "%.0f", ($r101 / $z);
- $data{$type}{$name}{weatherdata}{$key}{merge}{ttt} = $temp / $z;
+ $data{$type}{$name}{weatherdata}{$key}{merge}{ttt} = sprintf "%.2f", ($temp / $z);
debugLog ($paref, 'collectData', "Weather merged: $key, ".
"don: $data{$type}{$name}{weatherdata}{$key}{merge}{don}, ".
@@ -5896,15 +6051,15 @@ sub _transferAPIRadiationValues {
next;
}
- my $fh1 = $fh + 1;
- my $wantts = (timestringToTimestamp ($date.' '.$chour.':00:00')) + ($num * 3600);
- my $wantdt = (timestampToTimestring ($wantts, $lang))[1];
+ my $fh1 = $fh + 1;
+ my $wantts = (timestringToTimestamp ($date.' '.$chour.':00:00')) + ($num * 3600);
+ my $wantdt = (timestampToTimestring ($wantts, $lang))[1];
- my $time_str = 'NextHour'.sprintf "%02d", $num;
- my ($hod) = $wantdt =~ /\s(\d{2}):/xs;
- $hod = sprintf "%02d", int $hod + 1; # Stunde des Tages
+ my $nhtstr = 'NextHour'.sprintf "%02d", $num;
+ my ($hod) = $wantdt =~ /\s(\d{2}):/xs;
+ $hod = sprintf "%02d", int $hod + 1; # Stunde des Tages
- my $rad = SolCastAPIVal ($hash, '?All', $wantdt, 'Rad1h', undef);
+ my $rad1h = SolCastAPIVal ($hash, '?All', $wantdt, 'Rad1h', undef);
my $params = {
hash => $hash,
@@ -5912,7 +6067,7 @@ sub _transferAPIRadiationValues {
type => $type,
wantdt => $wantdt,
hod => $hod,
- nhidx => $time_str,
+ nhidx => $nhtstr,
num => $num,
fh1 => $fh1,
fd => $fd,
@@ -5922,42 +6077,57 @@ sub _transferAPIRadiationValues {
my $est = __calcPVestimates ($params);
- $data{$type}{$name}{nexthours}{$time_str}{pvapifc} = $est; # durch API gelieferte PV Forecast
- $data{$type}{$name}{nexthours}{$time_str}{starttime} = $wantdt;
- $data{$type}{$name}{nexthours}{$time_str}{hourofday} = $hod;
- $data{$type}{$name}{nexthours}{$time_str}{today} = $fd == 0 ? 1 : 0;
- $data{$type}{$name}{nexthours}{$time_str}{rad1h} = $rad;
+ $data{$type}{$name}{nexthours}{$nhtstr}{pvapifc} = $est; # durch API gelieferte PV Forecast
+ $data{$type}{$name}{nexthours}{$nhtstr}{starttime} = $wantdt;
+ $data{$type}{$name}{nexthours}{$nhtstr}{hourofday} = $hod;
+ $data{$type}{$name}{nexthours}{$nhtstr}{today} = $fd == 0 ? 1 : 0;
+ $data{$type}{$name}{nexthours}{$nhtstr}{rad1h} = $rad1h;
- my ($err, $pvaifc) = aiGetResult ($params); # KI Entscheidungen abfragen
+ my ($msg, $pvaifc) = aiGetResult ($params); # KI Entscheidungen abfragen
my $useai = 0;
my $pvfc;
- if (!$err) {
+ if ($msg eq 'accurate' || $msg eq 'spreaded') {
my $aivar = 100 * $pvaifc / $est;
- if ($aivar >= $aiDevLowLim && $aivar <= $aiDevUpLim) { # Abweichung AI von Standardvorhersage begrenzen
- $data{$type}{$name}{nexthours}{$time_str}{pvaifc} = $pvaifc; # durch AI gelieferte PV Forecast
- $data{$type}{$name}{nexthours}{$time_str}{pvfc} = $pvaifc;
- $data{$type}{$name}{nexthours}{$time_str}{aihit} = 1;
+ if ($msg eq 'accurate' && $aivar >= $aiAccLowLim && $aivar <= $aiAccUpLim) { # KI liefert 'accurate' Treffer -> verwenden
+ $data{$type}{$name}{nexthours}{$nhtstr}{aihit} = 1;
+ $pvfc = $pvaifc;
+ $useai = 1;
+ debugLog ($paref, 'aiData', qq{AI Hit - accurate result found -> hod: $hod, Rad1h: $rad1h, pvfc: $pvfc Wh});
+ }
+
+ if ($msg eq 'spreaded' && $aivar >= $aiSpreadLowLim && $aivar <= $aiSpreadUpLim) { # Abweichung AI von Standardvorhersage begrenzen
+ $data{$type}{$name}{nexthours}{$nhtstr}{aihit} = 1;
$pvfc = $pvaifc;
$useai = 1;
+
+ debugLog ($paref, 'aiData', qq{AI Hit - spreaded result found and is in tolerance -> hod: $hod, Rad1h: $rad1h, pvfc: $pvfc Wh});
}
}
-
- if (!$useai) {
- delete $data{$type}{$name}{nexthours}{$time_str}{pvaifc};
- $data{$type}{$name}{nexthours}{$time_str}{pvfc} = $est;
- $data{$type}{$name}{nexthours}{$time_str}{aihit} = 0;
-
- $pvfc = $est;
+ else {
+ debugLog ($paref, 'aiData', $msg);
}
+
+ if ($useai) {
+ $data{$type}{$name}{nexthours}{$nhtstr}{pvaifc} = $pvaifc; # durch AI gelieferte PV Forecast
+ }
+ else {
+ delete $data{$type}{$name}{nexthours}{$nhtstr}{pvaifc};
+ $data{$type}{$name}{nexthours}{$nhtstr}{aihit} = 0;
+ $pvfc = $est;
+
+ debugLog ($paref, 'aiData', "use PV from API (no AI or AI result tolerance overflow) -> hod: $hod, Rad1h: ".(defined $rad1h ? $rad1h : '-').", pvfc: $pvfc Wh");
+ }
+
+ $data{$type}{$name}{nexthours}{$nhtstr}{pvfc} = $pvfc; # resultierende PV Forecast zuweisen
if ($num < 23 && $fh < 24) { # Ringspeicher PV forecast Forum: https://forum.fhem.de/index.php/topic,117864.msg1133350.html#msg1133350
- $data{$type}{$name}{circular}{sprintf "%02d",$fh1}{pvapifc} = NexthoursVal ($hash, $time_str, 'pvapifc', undef);
+ $data{$type}{$name}{circular}{sprintf "%02d",$fh1}{pvapifc} = NexthoursVal ($hash, $nhtstr, 'pvapifc', undef);
$data{$type}{$name}{circular}{sprintf "%02d",$fh1}{pvfc} = $pvfc;
- $data{$type}{$name}{circular}{sprintf "%02d",$fh1}{pvaifc} = NexthoursVal ($hash, $time_str, 'pvaifc', undef);
- $data{$type}{$name}{circular}{sprintf "%02d",$fh1}{aihit} = NexthoursVal ($hash, $time_str, 'aihit', 0);
+ $data{$type}{$name}{circular}{sprintf "%02d",$fh1}{pvaifc} = NexthoursVal ($hash, $nhtstr, 'pvaifc', undef);
+ $data{$type}{$name}{circular}{sprintf "%02d",$fh1}{aihit} = NexthoursVal ($hash, $nhtstr, 'aihit', 0);
}
if($fd == 0 && int $pvfc > 0) { # Vorhersagedaten des aktuellen Tages zum manuellen Vergleich in Reading speichern
@@ -5971,7 +6141,7 @@ sub _transferAPIRadiationValues {
$paref->{histname} = 'pvfc';
setPVhistory ($paref);
- $paref->{rad1h} = $rad;
+ $paref->{rad1h} = $rad1h;
$paref->{histname} = 'radiation';
setPVhistory ($paref);
@@ -6075,8 +6245,8 @@ sub __calcPVestimates {
};
if ($acu =~ /on_complex/xs) {
- $lh->{"Module Temp (calculated)"} = $modtemp. " °C";
- $lh->{"Loss String Peak Power by Temp"} = $peakloss." kWP";
+ $lh->{"Module Temp (calculated)"} = $modtemp. " °C";
+ $lh->{"Win(+)/Loss(-) String Peak Power by Temp"} = $peakloss." kWp";
}
$sq = q{};
@@ -6084,7 +6254,7 @@ sub __calcPVestimates {
$sq .= $idx." => ".$lh->{$idx}."\n";
}
- Log3 ($name, 1, "$name DEBUG> PV estimate for $reld Hour ".sprintf ("%02d", $hod)." string $string ->\n$sq");
+ Log3 ($name, 1, "$name DEBUG> PV API estimate for $reld Hour ".sprintf ("%02d", $hod)." string $string ->\n$sq");
}
$pvsum += $pv;
@@ -6114,7 +6284,7 @@ sub __calcPVestimates {
"Forecasted temperature" => $temp." °C",
"Cloudcover" => $cloudcover,
"Rainprob" => $rainprob,
- "Use PV Correction" => ($acu ? $acu : 'no'),
+ "PV Correction mode" => ($acu ? $acu : 'no'),
"PV correction factor" => $hc,
"PV correction quality" => $hq,
"PV generation forecast" => $pvsum." Wh ".$logao,
@@ -6125,7 +6295,7 @@ sub __calcPVestimates {
$sq .= $idx." => ".$lh->{$idx}."\n";
}
- Log3 ($name, 1, "$name DEBUG> PV estimate for $reld Hour ".sprintf ("%02d", $hod)." summary: \n$sq");
+ Log3 ($name, 1, "$name DEBUG> PV API estimate for $reld Hour ".sprintf ("%02d", $hod)." summary: \n$sq");
}
return $pvsum;
@@ -6137,7 +6307,7 @@ return $pvsum;
# speichert die Werte im Nexthours / pvHistory Hash
#
# Simple:
-# Liest Korrekturfaktor/Qualität aus pvCircular percentile und
+# Liest Korrekturfaktor/Qualität aus pvCircular simple und
# speichert die Werte im Nexthours / pvHistory Hash
######################################################################
sub ___readCandQ {
@@ -6166,13 +6336,13 @@ sub ___readCandQ {
$data{$type}{$name}{nexthours}{"NextHour".sprintf("%02d",$num)}{cloudrange} = $range;
}
elsif ($acu =~ /on_simple/xs) {
- ($hc, $hq) = CircularAutokorrVal ($hash, sprintf("%02d",$fh1), 'percentile', undef); # Korrekturfaktor/Qualität der Stunde des Tages (simple)
+ ($hc, $hq) = CircularAutokorrVal ($hash, sprintf("%02d",$fh1), 'simple', undef); # Korrekturfaktor/Qualität der Stunde des Tages (simple)
$hq //= '-';
$hc //= 1; # Korrekturfaktor = 1
$hc = 1 if(1 * $hc == 0); # 0.0-Werte ignorieren (Schleifengefahr)
}
else { # keine Autokorrektur
- ($hc, $hq) = CircularAutokorrVal ($hash, sprintf("%02d",$fh1), 'percentile', undef); # Korrekturfaktor/Qualität der Stunde des Tages (simple)
+ ($hc, $hq) = CircularAutokorrVal ($hash, sprintf("%02d",$fh1), 'simple', undef); # Korrekturfaktor/Qualität der Stunde des Tages (simple)
$hq //= '-';
$hc = 1;
}
@@ -6222,7 +6392,7 @@ sub ___calcPeaklossByTemp {
my $temp = $paref->{temp} // return (0,0); # vorhergesagte Temperatur Stunde X
my $modtemp = $temp + ($tempmodinc * (1 - ($cloudcover/100))); # kalkulierte Modultemperatur
- my $peakloss = sprintf "%.2f", $tempcoeffdef * ($temp - $tempbasedef) * $peak / 100;
+ my $peakloss = sprintf "%.2f", $tempcoeffdef * ($modtemp - $tempbasedef) * $peak / 100;
return ($peakloss, $modtemp);
}
@@ -6684,11 +6854,15 @@ sub _transferBatteryValues {
my $batmaxsoc = HistoryVal ($hash, $day, 99, 'batmaxsoc', 0); # gespeicherter max. SOC des Tages
if ($soc >= $batmaxsoc) {
- $paref->{batmaxsoc} = $soc;
- $paref->{nhour} = 99;
- $paref->{histname} = 'batmaxsoc';
+ $paref->{val} = $soc;
+ $paref->{nhour} = 99;
+ $paref->{histname} = 'batmaxsoc';
+
setPVhistory ($paref);
+
delete $paref->{histname};
+ delete $paref->{nhour};
+ delete $paref->{val};
}
######
@@ -6835,11 +7009,15 @@ sub _batSocTarget {
## pvHistory/Readings schreiben
#################################
- $paref->{batsetsoc} = $target;
- $paref->{nhour} = 99;
- $paref->{histname} = 'batsetsoc';
+ $paref->{val} = $target;
+ $paref->{nhour} = 99;
+ $paref->{histname} = 'batsetsoc';
+
setPVhistory ($paref);
+
delete $paref->{histname};
+ delete $paref->{nhour};
+ delete $paref->{val};
storeReading ('Battery_OptimumTargetSoC', $target.' %');
storeReading ('Battery_ChargeRequest', $chargereq);
@@ -7056,7 +7234,7 @@ sub _manageConsumerData {
my $up = ConsumerVal ($hash, $c, "upcurr", "");
my $pcurr = 0;
- if($paread) {
+ if ($paread) {
my $eup = $up =~ /^kW$/xi ? 1000 : 1;
$pcurr = ReadingsNum ($consumer, $paread, 0) * $eup;
@@ -7069,7 +7247,7 @@ sub _manageConsumerData {
my $etotread = ConsumerVal ($hash, $c, "retotal", "");
my $u = ConsumerVal ($hash, $c, "uetotal", "");
- if($etotread) {
+ if ($etotread) {
my $eu = $u =~ /^kWh$/xi ? 1000 : 1;
my $etot = ReadingsNum ($consumer, $etotread, 0) * $eu; # Summe Energieverbrauch des Verbrauchers
my $ehist = HistoryVal ($hash, $day, sprintf("%02d",$nhour), "csmt${c}", undef); # gespeicherter Totalverbrauch
@@ -7077,7 +7255,7 @@ sub _manageConsumerData {
## aktuelle Leistung ermitteln wenn kein Reading d. aktuellen Leistung verfügbar
##################################################################################
- if(!$paread){
+ if (!$paread){
my $timespan = $t - ConsumerVal ($hash, $c, "old_etottime", $t);
my $delta = $etot - ConsumerVal ($hash, $c, "old_etotal", $etot);
$pcurr = sprintf "%.6f", $delta / (3600 * $timespan) if($delta); # Einheitenformel beachten !!: W = Wh / (3600 * s)
@@ -7088,7 +7266,7 @@ sub _manageConsumerData {
storeReading ("consumer${c}_currentPower", $pcurr.' W');
}
- if(defined $ehist && $etot >= $ehist && ($etot - $ehist) >= $ethreshold) {
+ if (defined $ehist && $etot >= $ehist && ($etot - $ehist) >= $ethreshold) {
my $consumerco = $etot - $ehist;
$consumerco += HistoryVal ($hash, $day, sprintf("%02d",$nhour), "csme${c}", 0);
@@ -7153,12 +7331,13 @@ sub _manageConsumerData {
$paref->{val} = ConsumerVal ($hash, $c, "numberDayStarts", 0); # Anzahl Tageszyklen des Verbrauchers speichern
$paref->{histname} = "cyclescsm${c}";
setPVhistory ($paref);
- delete $paref->{histname};
$paref->{val} = ceil ConsumerVal ($hash, $c, "minutesOn", 0); # Verbrauchsminuten akt. Stunde des Consumers
$paref->{histname} = "minutescsm${c}";
setPVhistory ($paref);
+
delete $paref->{histname};
+ delete $paref->{val};
## Durchschnittsverbrauch / Betriebszeit ermitteln + speichern
################################################################
@@ -7177,7 +7356,7 @@ sub _manageConsumerData {
}
if ($dnum) {
- if($consumerco && $runhours) {
+ if ($consumerco && $runhours) {
$data{$type}{$name}{consumers}{$c}{avgenergy} = sprintf "%.2f", ($consumerco/$runhours); # Durchschnittsverbrauch pro Stunde in Wh
}
else {
@@ -8956,7 +9135,7 @@ sub _calcCaQsimple {
$paref->{pvrl} = $pvrl;
$paref->{pvfc} = $pvfc;
- $paref->{range} = 'percentile';
+ $paref->{range} = 'simple';
$paref->{calc} = 'Simple';
my ($oldfac, $factor, $dnum) = __calcNewFactor ($paref);
@@ -9758,11 +9937,20 @@ sub _checkSetupNotComplete {
### nicht mehr benötigte Daten löschen - Bereich kann später wieder raus !!
##########################################################################################
## currentWeatherDev in Attr umsetzen
- ## currentWeatherDev in Attr umsetzen
- my $cwd = ReadingsVal ($name, 'currentWeatherDev', '');
+ my $cwd = ReadingsVal ($name, 'currentWeatherDev', ''); # 30.01.2024
if ($cwd) {
CommandAttr (undef, "$name ctrlWeatherDev1 $cwd");
}
+
+ my $mdr = ReadingsVal ($name, 'moduleDirection', undef); # 09.02.2024
+ if ($mdr) {
+ readingsSingleUpdate ($hash, 'moduleAzimuth', $mdr, 0);
+ }
+
+ my $mta = ReadingsVal ($name, 'moduleTiltAngle', undef);
+ if ($mta) {
+ readingsSingleUpdate ($hash, 'moduleDeclination', $mta, 0);
+ }
##########################################################################################
my $is = ReadingsVal ($name, 'inverterStrings', undef); # String Konfig
@@ -9772,8 +9960,8 @@ sub _checkSetupNotComplete {
my $medev = ReadingsVal ($name, 'currentMeterDev', undef); # Meter Device
my $peaks = ReadingsVal ($name, 'modulePeakString', undef); # String Peak
- my $dir = ReadingsVal ($name, 'moduleDirection', undef); # Modulausrichtung Konfig (Azimut)
- my $ta = ReadingsVal ($name, 'moduleTiltAngle', undef); # Modul Neigungswinkel Konfig
+ my $dir = ReadingsVal ($name, 'moduleAzimuth', undef); # Modulausrichtung Konfig (Azimut)
+ my $ta = ReadingsVal ($name, 'moduleDeclination', undef); # Modul Neigungswinkel Konfig
my $mrt = ReadingsVal ($name, 'moduleRoofTops', undef); # RoofTop Konfiguration (SolCast API)
my $vrmcr = SolCastAPIVal ($hash, '?VRM', '?API', 'credentials', ''); # Victron VRM Credentials gesetzt
@@ -9809,7 +9997,7 @@ sub _checkSetupNotComplete {
my $chkicon = "$img";
my $chktitle = $htitles{plchk}{$lang};
- if (!$is || !$wedev || !$radev || !$indev || !$medev || !$peaks ||
+ if (!$is || !$wedev || !$radev || !$indev || !$medev || !$peaks ||
(isSolCastUsed ($hash) ? (!$rip || !$mrt) : isVictronKiUsed ($hash) ? !$vrmcr : (!$dir || !$ta )) ||
(isForecastSolarUsed ($hash) ? !$coset : '') ||
!defined $pv0) {
@@ -9976,10 +10164,10 @@ sub _graphicHeader {
## Solare API Sektion
########################
- my $api = isSolCastUsed ($hash) ? 'SolCast:' :
- isForecastSolarUsed ($hash) ? 'Forecast.Solar:' :
- isVictronKiUsed ($hash) ? 'VictronVRM:' :
- isDWDUsed ($hash) ? 'DWD:' :
+ my $api = isSolCastUsed ($hash) ? 'SolCast:' :
+ isForecastSolarUsed ($hash) ? 'Forecast.Solar:' :
+ isVictronKiUsed ($hash) ? 'VictronVRM:' :
+ isDWDUsed ($hash) ? 'DWD:' :
q{};
my $nscc = ReadingsVal ($name, 'nextSolCastCall', '?');
@@ -9995,7 +10183,7 @@ sub _graphicHeader {
}
}
- if ($api eq 'SolCast:') {
+ if ($api =~ /SolCast/xs) {
$api .= ' '.$lrt;
if ($scrm eq 'success') {
@@ -10025,7 +10213,7 @@ sub _graphicHeader {
$api .= ')';
$api .= '';
}
- elsif ($api eq 'Forecast.Solar:') {
+ elsif ($api =~ /Forecast.Solar/xs) {
$api .= ' '.$lrt;
if ($scrm eq 'success') {
@@ -10049,7 +10237,7 @@ sub _graphicHeader {
$api .= ')';
$api .= '';
}
- elsif ($api eq 'VictronVRM:') {
+ elsif ($api =~ /VictronVRM/xs) {
$api .= ' '.$lrt;
if ($scrm eq 'success') {
@@ -10068,7 +10256,7 @@ sub _graphicHeader {
$api .= ')';
$api .= '';
}
- elsif ($api eq 'DWD:') {
+ elsif ($api =~ /DWD/xs) {
$nscc = ReadingsVal ($name, 'nextCycletime', '?');
$api .= ' '.$lrt;
@@ -11933,7 +12121,7 @@ sub formatVal6 {
my $t = $v - int($v); # Nachkommstelle ?
if (!$t) { # glatte Zahl ohne Nachkommastelle
- if(!$v) {
+ if (!$v) {
return ' '; # 0 nicht anzeigen, passt eigentlich immer bis auf einen Fall im Typ diff
}
elsif ($v < 10) {
@@ -12201,15 +12389,17 @@ sub aiAddInstance { ## no critic "not used"
my $rad1h = AiRawdataVal ($hash, $idx, 'rad1h', 0);
next if($rad1h <= 0);
- my $temp = AiRawdataVal ($hash, $idx, 'temp', 20);
- my $wcc = AiRawdataVal ($hash, $idx, 'wcc', 0);
- my $wrp = AiRawdataVal ($hash, $idx, 'wrp', 0);
+ my $temp = AiRawdataVal ($hash, $idx, 'temp', 20);
+ my $wcc = AiRawdataVal ($hash, $idx, 'wcc', 0);
+ my $wrp = AiRawdataVal ($hash, $idx, 'wrp', 0);
+ my $sunalt = AiRawdataVal ($hash, $idx, 'sunalt', 0);
- eval { $dtree->add_instance (attributes => { rad1h => $rad1h,
- temp => $temp,
- wcc => $wcc,
- wrp => $wrp,
- hod => $hod
+ eval { $dtree->add_instance (attributes => { rad1h => $rad1h,
+ temp => $temp,
+ wcc => $wcc,
+ wrp => $wrp,
+ sunalt => $sunalt,
+ hod => $hod
},
result => $pvrl
)
@@ -12219,7 +12409,7 @@ sub aiAddInstance { ## no critic "not used"
return;
};
- debugLog ($paref, 'aiProcess', qq{AI Instance added - hod: $hod, rad1h: $rad1h, pvrl: $pvrl, wcc: $wcc, wrp: $wrp, temp: $temp});
+ debugLog ($paref, 'aiProcess', qq{AI Instance added - hod: $hod, sunalt: $sunalt, rad1h: $rad1h, pvrl: $pvrl, wcc: $wcc, wrp: $wrp, temp: $temp});
}
$data{$type}{$name}{aidectree}{object} = $dtree;
@@ -12303,7 +12493,7 @@ return;
################################################################
# AI Ergebnis für ermitteln
################################################################
-sub aiGetResult { ## no critic "not used"
+sub aiGetResult {
my $paref = shift;
my $hash = $paref->{hash};
my $name = $paref->{name};
@@ -12311,7 +12501,7 @@ sub aiGetResult { ## no critic "not used"
my $hod = $paref->{hod};
my $nhidx = $paref->{nhidx};
- return 'the AI usage is not prepared' if(!isPrepared4AI ($hash, 'full'));
+ return 'AI usage is not prepared' if(!isPrepared4AI ($hash, 'full'));
my $dtree = AiDetreeVal ($hash, 'aitrained', undef);
@@ -12321,22 +12511,27 @@ sub aiGetResult { ## no critic "not used"
my $rad1h = NexthoursVal ($hash, $nhidx, "rad1h", 0);
return "no rad1h for hod: $hod" if($rad1h <= 0);
+
+ debugLog ($paref, 'aiData', 'Start AI result check now');
- my $wcc = NexthoursVal ($hash, $nhidx, "cloudcover", 0);
- my $wrp = NexthoursVal ($hash, $nhidx, "rainprob", 0);
- my $temp = NexthoursVal ($hash, $nhidx, "temp", 20);
+ my $wcc = NexthoursVal ($hash, $nhidx, 'cloudcover', 0);
+ my $wrp = NexthoursVal ($hash, $nhidx, 'rainprob', 0);
+ my $temp = NexthoursVal ($hash, $nhidx, 'temp', 20);
+ my $sunalt = NexthoursVal ($hash, $nhidx, 'sunalt', 0);
- my $tbin = temp2bin ($temp);
- my $cbin = cloud2bin ($wcc);
- my $rbin = rain2bin ($wrp);
+ my $tbin = temp2bin ($temp);
+ my $cbin = cloud2bin ($wcc);
+ my $rbin = rain2bin ($wrp);
+ my $sabin = sunalt2bin ($sunalt);
my $pvaifc;
- eval { $pvaifc = $dtree->get_result (attributes => { rad1h => $rad1h,
- temp => $tbin,
- wcc => $cbin,
- wrp => $rbin,
- hod => $hod
+ eval { $pvaifc = $dtree->get_result (attributes => { rad1h => $rad1h,
+ temp => $tbin,
+ wcc => $cbin,
+ wrp => $rbin,
+ sunalt => $sabin,
+ hod => $hod
}
);
};
@@ -12347,30 +12542,29 @@ sub aiGetResult { ## no critic "not used"
}
if (defined $pvaifc) {
- debugLog ($paref, 'aiData', qq{accurate result AI: pvaifc: $pvaifc (hod: $hod, rad1h: $rad1h, wcc: $wcc, wrp: $rbin, temp: $tbin)});
- return ('', $pvaifc);
+ debugLog ($paref, 'aiData', qq{AI accurate result found: pvaifc: $pvaifc (hod: $hod, sunalt: $sabin, Rad1h: $rad1h, wcc: $wcc, wrp: $rbin, temp: $tbin)});
+ return ('accurate', $pvaifc);
}
- my $msg = 'no decition delivered';
-
- ($msg, $pvaifc) = _aiGetSpread ( { hash => $hash,
- name => $name,
- type => $type,
- rad1h => $rad1h,
- temp => $tbin,
- wcc => $cbin,
- wrp => $rbin,
- hod => $hod,
- dtree => $dtree,
- debug => $paref->{debug}
- }
- );
+ (my $msg, $pvaifc) = _aiGetSpread ( { hash => $hash,
+ name => $name,
+ type => $type,
+ rad1h => $rad1h,
+ temp => $tbin,
+ wcc => $cbin,
+ wrp => $rbin,
+ sunalt => $sabin,
+ hod => $hod,
+ dtree => $dtree,
+ debug => $paref->{debug}
+ }
+ );
if (defined $pvaifc) {
- return ('', $pvaifc);
+ return ($msg, $pvaifc);
}
-return $msg;
+return 'No AI decition delivered';
}
################################################################
@@ -12378,32 +12572,38 @@ return $msg;
# rad1h-Abweichung schätzen
################################################################
sub _aiGetSpread {
- my $paref = shift;
- my $rad1h = $paref->{rad1h};
- my $temp = $paref->{temp};
- my $wcc = $paref->{wcc};
- my $wrp = $paref->{wrp};
- my $hod = $paref->{hod};
- my $dtree = $paref->{dtree};
+ my $paref = shift;
+ my $rad1h = $paref->{rad1h};
+ my $temp = $paref->{temp};
+ my $wcc = $paref->{wcc};
+ my $wrp = $paref->{wrp};
+ my $sunalt = $paref->{sunalt};
+ my $hod = $paref->{hod};
+ my $dtree = $paref->{dtree};
my $dtn = 20; # positive und negative rad1h Abweichung testen mit Schrittweite "$step"
my $step = 10;
my ($pos, $neg, $p, $n);
- debugLog ($paref, 'aiData', qq{no accurate result AI found with initial value "$rad1h"});
- debugLog ($paref, 'aiData', qq{test AI estimation with variance "$dtn", positive/negative step "$step"});
+ debugLog ($paref, 'aiData', qq{AI no accurate result found with initial value "Rad1h: $rad1h" (hod: $hod)});
+ debugLog ($paref, 'aiData', qq{AI test Rad1h variance "$dtn" and positive/negative spread with step size "$step"});
+
+ my $gra = {
+ temp => $temp,
+ wcc => $wcc,
+ wrp => $wrp,
+ sunalt => $sunalt,
+ hod => $hod
+ };
- for ($p = $rad1h; $p <= $rad1h + $dtn; $p += $step) {
- $p = sprintf "%.2f", $p;
+ for ($p = $rad1h + $step; $p <= $rad1h + $dtn; $p += $step) {
+ $p = sprintf "%.2f", $p;
+ $gra->{rad1h} = $p;
+
+ debugLog ($paref, 'aiData', qq{AI positive test value "Rad1h: $p"});
- eval { $pos = $dtree->get_result (attributes => { rad1h => $p,
- temp => $temp,
- wcc => $wcc,
- wrp => $wrp,
- hod => $hod
- }
- );
+ eval { $pos = $dtree->get_result (attributes => $gra);
};
if ($@) {
@@ -12411,22 +12611,19 @@ sub _aiGetSpread {
}
if ($pos) {
- debugLog ($paref, 'aiData', qq{AI estimation with test value "$p": $pos});
+ debugLog ($paref, 'aiData', qq{AI positive tolerance hit: $pos Wh});
last;
}
}
- for ($n = $rad1h; $n >= $rad1h - $dtn; $n -= $step) {
+ for ($n = $rad1h - $step; $n >= $rad1h - $dtn; $n -= $step) {
last if($n <= 0);
- $n = sprintf "%.2f", $n;
+ $n = sprintf "%.2f", $n;
+ $gra->{rad1h} = $n;
+
+ debugLog ($paref, 'aiData', qq{AI negative test value "Rad1h: $n"});
- eval { $neg = $dtree->get_result (attributes => { rad1h => $n,
- temp => $temp,
- wcc => $wcc,
- wrp => $wrp,
- hod => $hod
- }
- );
+ eval { $neg = $dtree->get_result (attributes => $gra);
};
if ($@) {
@@ -12434,19 +12631,22 @@ sub _aiGetSpread {
}
if ($neg) {
- debugLog ($paref, 'aiData', qq{AI estimation with test value "$n": $neg});
+ debugLog ($paref, 'aiData', qq{AI negative tolerance hit: $neg Wh});
last;
}
}
- my $pvaifc = $pos && $neg ? sprintf "%.0f", (($pos + $neg) / 2) : undef;
+ my $pvaifc = $pos && $neg ? sprintf "%.0f", (($pos + $neg) / 2) :
+ $pos ? sprintf "%.0f", $pos :
+ $neg ? sprintf "%.0f", $neg :
+ undef;
if (defined $pvaifc) {
- debugLog ($paref, 'aiData', qq{appreciated result AI: pvaifc: $pvaifc (hod: $hod, wcc: $wcc, wrp: $wrp, temp: $temp)});
- return ('', $pvaifc);
+ debugLog ($paref, 'aiData', qq{AI determined average result: pvaifc: $pvaifc Wh (hod: $hod, sunalt: $sunalt, wcc: $wcc, wrp: $wrp, temp: $temp)});
+ return ('spreaded', $pvaifc);
}
-return 'no decition delivered';
+return 'No AI decition delivered';
}
################################################################
@@ -12520,24 +12720,27 @@ sub aiAddRawData {
my $ridx = _aiMakeIdxRaw ($pvd, $hod);
- my $temp = HistoryVal ($hash, $pvd, $hod, 'temp', 20);
- my $wcc = HistoryVal ($hash, $pvd, $hod, 'wcc', 0);
- my $wrp = HistoryVal ($hash, $pvd, $hod, 'wrp', 0);
+ my $temp = HistoryVal ($hash, $pvd, $hod, 'temp', 20);
+ my $wcc = HistoryVal ($hash, $pvd, $hod, 'wcc', 0);
+ my $wrp = HistoryVal ($hash, $pvd, $hod, 'wrp', 0);
+ my $sunalt = HistoryVal ($hash, $pvd, $hod, 'sunalt', 0);
+
+ my $tbin = temp2bin ($temp);
+ my $cbin = cloud2bin ($wcc);
+ my $rbin = rain2bin ($wrp);
+ my $sabin = sunalt2bin ($sunalt);
- my $tbin = temp2bin ($temp);
- my $cbin = cloud2bin ($wcc);
- my $rbin = rain2bin ($wrp);
-
- $data{$type}{$name}{aidectree}{airaw}{$ridx}{rad1h} = $rad1h;
- $data{$type}{$name}{aidectree}{airaw}{$ridx}{temp} = $tbin;
- $data{$type}{$name}{aidectree}{airaw}{$ridx}{wcc} = $cbin;
- $data{$type}{$name}{aidectree}{airaw}{$ridx}{wrp} = $rbin;
- $data{$type}{$name}{aidectree}{airaw}{$ridx}{hod} = $hod;
- $data{$type}{$name}{aidectree}{airaw}{$ridx}{pvrl} = $pvrl;
+ $data{$type}{$name}{aidectree}{airaw}{$ridx}{rad1h} = $rad1h;
+ $data{$type}{$name}{aidectree}{airaw}{$ridx}{temp} = $tbin;
+ $data{$type}{$name}{aidectree}{airaw}{$ridx}{wcc} = $cbin;
+ $data{$type}{$name}{aidectree}{airaw}{$ridx}{wrp} = $rbin;
+ $data{$type}{$name}{aidectree}{airaw}{$ridx}{hod} = $hod;
+ $data{$type}{$name}{aidectree}{airaw}{$ridx}{pvrl} = $pvrl;
+ $data{$type}{$name}{aidectree}{airaw}{$ridx}{sunalt} = $sabin if($sabin);
$dosave = 1;
- debugLog ($paref, 'aiProcess', qq{AI raw data added - idx: $ridx, day: $pvd, hod: $hod, rad1h: $rad1h, pvrl: $pvrl, wcc: $cbin, wrp: $rbin, temp: $tbin});
+ debugLog ($paref, 'aiProcess', "AI raw add - idx: $ridx, day: $pvd, hod: $hod,".($sabin ? qq{ sunalt: $sabin,} : '')." rad1h: $rad1h, pvrl: $pvrl, wcc: $cbin, wrp: $rbin, temp: $tbin");
}
}
@@ -12631,9 +12834,7 @@ sub setPVhistory {
my $batinthishour = $paref->{batinthishour}; # Batterieladung in Stunde
my $btotin = $paref->{batintotal}; # totale Batterieladung
my $batoutthishour = $paref->{batoutthishour}; # Batterieentladung in Stunde
- my $btotout = $paref->{batouttotal}; # totale Batterieentladung
- my $batmaxsoc = $paref->{batmaxsoc}; # max. erreichter SOC des Tages
- my $batsetsoc = $paref->{batsetsoc}; # berechneter optimaler SOC für den laufenden Tag
+ my $btotout = $paref->{batouttotal}; # totale Batterieentladung
my $calcpv = $paref->{calcpv} // 0;
my $gcthishour = $paref->{gctotthishour} // 0; # Netzbezug
my $fithishour = $paref->{gftotthishour} // 0; # Netzeinspeisung
@@ -12645,7 +12846,7 @@ sub setPVhistory {
my $wrp = $paref->{wrp} // 0; # Wahrscheinlichkeit von Niederschlag
my $pvcorrf = $paref->{pvcorrf} // "1.00/0"; # pvCorrectionFactor
my $temp = $paref->{temp}; # Außentemperatur
- my $val = $paref->{val} // qq{}; # Wert zur Speicherung in pvHistory (soll mal generell verwendet werden -> Change)
+ my $val = $paref->{val}; # Wert zur Speicherung in pvHistory (soll mal generell verwendet werden -> Change)
my $rad1h = $paref->{rad1h}; # Strahlungsdaten speichern
my $reorg = $paref->{reorg} // 0; # Neuberechnung von Werten in Stunde "99" nach Löschen von Stunden eines Tages
my $reorgday = $paref->{reorgday} // q{}; # Tag der reorganisiert werden soll
@@ -12678,14 +12879,12 @@ sub setPVhistory {
$data{$type}{$name}{pvhist}{$day}{99}{batout} = $batoutsum;
}
- if ($histname eq "batmaxsoc") { # max. erreichter SOC des Tages
- $val = $batmaxsoc;
- $data{$type}{$name}{pvhist}{$day}{99}{batmaxsoc} = $batmaxsoc;
+ if ($histname eq 'batmaxsoc') { # max. erreichter SOC des Tages
+ $data{$type}{$name}{pvhist}{$day}{$nhour}{$histname} = $val;
}
- if ($histname eq "batsetsoc") { # optimaler SOC für den Tages
- $val = $batsetsoc;
- $data{$type}{$name}{pvhist}{$day}{99}{batsetsoc} = $batsetsoc;
+ if ($histname eq 'batsetsoc') { # optimaler SOC für den Tages
+ $data{$type}{$name}{pvhist}{$day}{$nhour}{$histname} = $val;
}
if ($histname eq 'pvrl') { # realer Energieertrag
@@ -12784,6 +12983,10 @@ sub setPVhistory {
}
}
+ if ($histname =~ /sunaz|sunalt/xs) { # Sonnenstand Azimuth / Altitude
+ $data{$type}{$name}{pvhist}{$day}{$nhour}{$histname} = $val;
+ }
+
if ($histname =~ /cyclescsm[0-9]+$/xs) { # Anzahl Tageszyklen des Verbrauchers
$data{$type}{$name}{pvhist}{$day}{99}{$histname} = $val;
}
@@ -12883,11 +13086,11 @@ sub setPVhistory {
$data{$type}{$name}{pvhist}{$reorgday}{99}{gfeedin} = $r7;
$data{$type}{$name}{pvhist}{$reorgday}{99}{con} = $r8;
- debugLog ($paref, 'saveData2Cache', "setPVhistory -> PV History day >$reorgday< reorganized keys: batin, batout, pvrl, pvfc, con, confc, gcons, gfeedin");
+ debugLog ($paref, 'saveData2Cache', "setPVhistory -> Day >$reorgday< reorganized keys: batin, batout, pvrl, pvfc, con, confc, gcons, gfeedin");
}
if ($histname) {
- debugLog ($paref, 'saveData2Cache', "setPVhistory -> save PV History Day: $day, Hour: $nhour, Key: $histname, Value: $val");
+ debugLog ($paref, 'saveData2Cache', "setPVhistory -> save Day: $day, Hour: $nhour, Key: $histname, Value: ".(defined $val ? $val : ''));
}
return;
@@ -12932,12 +13135,17 @@ sub listDataPool {
my $batmsoc = HistoryVal ($hash, $day, $key, 'batmaxsoc', '-');
my $batssoc = HistoryVal ($hash, $day, $key, 'batsetsoc', '-');
my $rad1h = HistoryVal ($hash, $day, $key, 'rad1h', '-');
+ my $sunaz = HistoryVal ($hash, $day, $key, 'sunaz', '-');
+ my $sunalt = HistoryVal ($hash, $day, $key, 'sunalt', '-');
$ret .= "\n " if($ret);
- $ret .= $key." => etotal: $etotal, pvfc: $pvfc, pvrl: $pvrl, pvrlvd: $pvrlvd, rad1h: $rad1h";
+ $ret .= $key." => ";
+ $ret .= "etotal: $etotal, pvfc: $pvfc, pvrl: $pvrl, pvrlvd: $pvrlvd, rad1h: $rad1h";
$ret .= "\n ";
$ret .= "confc: $confc, con: $con, gcon: $gcon, gfeedin: $gfeedin";
$ret .= "\n ";
+ $ret .= "sunaz: $sunaz, sunalt: $sunalt";
+ $ret .= "\n ";
$ret .= "batintotal: $btotin, batin: $batin, batouttotal: $btotout, batout: $batout";
$ret .= "\n ";
$ret .= "batmaxsoc: $batmsoc, batsetsoc: $batssoc";
@@ -13151,11 +13359,20 @@ sub listDataPool {
my $confc = NexthoursVal ($hash, $idx, 'confc', '-');
my $confcex = NexthoursVal ($hash, $idx, 'confcEx', '-');
my $don = NexthoursVal ($hash, $idx, 'DoN', '-');
+ my $sunaz = NexthoursVal ($hash, $idx, 'sunaz', '-');
+ my $sunalt = NexthoursVal ($hash, $idx, 'sunalt', '-');
+
$sq .= "\n" if($sq);
- $sq .= $idx." => starttime: $nhts, hourofday: $hod, today: $today\n";
- $sq .= " pvapifc: $pvapifc, pvaifc: $pvaifc, pvfc: $pvfc, aihit: $aihit, confc: $confc\n";
- $sq .= " confcEx: $confcex, DoN: $don, wid: $wid, wcc: $neff, wrp: $r101, temp=$temp\n";
- $sq .= " rad1h: $rad1h, rrange: $rrange, crange: $crange, correff: $pvcorrf";
+ $sq .= $idx." => ";
+ $sq .= "starttime: $nhts, hourofday: $hod, today: $today";
+ $sq .= "\n ";
+ $sq .= "pvapifc: $pvapifc, pvaifc: $pvaifc, pvfc: $pvfc, aihit: $aihit, confc: $confc";
+ $sq .= "\n ";
+ $sq .= "confcEx: $confcex, DoN: $don, wid: $wid, wcc: $neff, wrp: $r101, temp=$temp";
+ $sq .= "\n ";
+ $sq .= "rad1h: $rad1h, sunaz: $sunaz, sunalt: $sunalt";
+ $sq .= "\n ";
+ $sq .= "rrange: $rrange, crange: $crange, correff: $pvcorrf";
}
}
@@ -13167,15 +13384,16 @@ sub listDataPool {
for my $idx (sort keys %{$h}) {
my $nhfc = NexthoursVal ($hash, $idx, 'pvfc', undef);
next if(!$nhfc);
- my $nhts = NexthoursVal ($hash, $idx, 'starttime', undef);
- my $neff = NexthoursVal ($hash, $idx, 'cloudcover', '-');
- my $crange = NexthoursVal ($hash, $idx, 'cloudrange', '-');
- my $r101 = NexthoursVal ($hash, $idx, 'rainprob', '-');
- my $rrange = NexthoursVal ($hash, $idx, 'rainrange', '-');
- my $pvcorrf = NexthoursVal ($hash, $idx, 'pvcorrf', '-/-');
+
+ my $nhts = NexthoursVal ($hash, $idx, 'starttime', '-');
+ my $pvcorrf = NexthoursVal ($hash, $idx, 'pvcorrf', '-/-');
+ my $aihit = NexthoursVal ($hash, $idx, 'aihit', '-');
+ my $pvfc = NexthoursVal ($hash, $idx, 'pvfc', '-');
+ my $neff = NexthoursVal ($hash, $idx, 'cloudcover', '-');
+
my ($f,$q) = split "/", $pvcorrf;
$sq .= "\n" if($sq);
- $sq .= "starttime: $nhts, wrp: $r101, rrange: $rrange, wcc: $neff, crange: $crange, quality: $q, factor: $f";
+ $sq .= "Start: $nhts, Quality: $q, Factor: $f, AI usage: $aihit, PV expect: $pvfc Wh, Cloud: $neff";
}
}
@@ -13252,14 +13470,15 @@ sub listDataPool {
$sq = "Number of datasets: ".$maxcnt."\n";
for my $idx (sort keys %{$h}) {
- my $hod = AiRawdataVal ($hash, $idx, 'hod', '-');
- my $rad1h = AiRawdataVal ($hash, $idx, 'rad1h', '-');
- my $wcc = AiRawdataVal ($hash, $idx, 'wcc', '-');
- my $wrp = AiRawdataVal ($hash, $idx, 'wrp', '-');
- my $pvrl = AiRawdataVal ($hash, $idx, 'pvrl', '-');
- my $temp = AiRawdataVal ($hash, $idx, 'temp', '-');
- $sq .= "\n";
- $sq .= "$idx => hod: $hod, rad1h: $rad1h, wcc: $wcc, wrp: $wrp, pvrl: $pvrl, temp: $temp";
+ my $hod = AiRawdataVal ($hash, $idx, 'hod', '-');
+ my $sunalt = AiRawdataVal ($hash, $idx, 'sunalt', '-');
+ my $rad1h = AiRawdataVal ($hash, $idx, 'rad1h', '-');
+ my $wcc = AiRawdataVal ($hash, $idx, 'wcc', '-');
+ my $wrp = AiRawdataVal ($hash, $idx, 'wrp', '-');
+ my $pvrl = AiRawdataVal ($hash, $idx, 'pvrl', '-');
+ my $temp = AiRawdataVal ($hash, $idx, 'temp', '-');
+ $sq .= "\n";
+ $sq .= "$idx => hod: $hod, sunalt: $sunalt, rad1h: $rad1h, wcc: $wcc, wrp: $wrp, pvrl: $pvrl, temp: $temp";
}
}
@@ -13282,7 +13501,7 @@ sub _ldchash2val {
no warnings 'numeric';
for my $f (sort {$a<=>$b} keys %{$pool->{$idx}{$key}}) {
- next if($f eq 'percentile');
+ next if($f eq 'simple');
$ret .= " " if($ret);
$ret .= "$f=".$pool->{$idx}{$key}{$f};
my $ct = ($ret =~ tr/=// // 0) / 10;
@@ -13291,10 +13510,10 @@ sub _ldchash2val {
use warnings;
- if (defined $pool->{$idx}{$key}{percentile}) {
+ if (defined $pool->{$idx}{$key}{simple}) {
$ret .= "\n " if($ret && $ret !~ /\n\s+$/xs);
- $ret .= " " if($ret);
- $ret .= "percentile=".$pool->{$idx}{$key}{percentile};
+ $ret .= " " if($ret);
+ $ret .= "simple=".$pool->{$idx}{$key}{simple};
}
}
else {
@@ -13339,10 +13558,6 @@ sub checkPlantConfig {
my $raname = ReadingsVal ($name, 'currentRadiationAPI', '');
my ($acu, $aln) = isAutoCorrUsed ($name);
- my $cf = 0; # config fault: 1 -> Konfig fehlerhaft, 0 -> Konfig ok
- my $wn = 0; # Warnung wenn 1
- my $io = 0; # Info wenn 1
-
my $ok = FW_makeImage ('10px-kreis-gruen.png', '');
my $nok = FW_makeImage ('10px-kreis-rot.png', '');
my $warn = FW_makeImage ('message_attention@orange', '');
@@ -13415,23 +13630,31 @@ sub checkPlantConfig {
#####################################
for my $step (1..$weatherDevMax) {
my $fcname = AttrVal ($name, 'ctrlWeatherDev'.$step, '');
+ next if(!$fcname && $step ne 1);
if (!$fcname || !$defs{$fcname}) {
- $result->{'DWD Weather Attributes'}{state} = $nok;
- $result->{'DWD Weather Attributes'}{result} .= qq{The DWD device "$fcname" doesn't exist.
};
- $result->{'DWD Weather Attributes'}{fault} = 1;
+ $result->{'DWD Weather Attributes'}{state} = $nok;
+
+ if (!$fcname) {
+ $result->{'DWD Weather Attributes'}{result} .= qq{No DWD device is defined in attribute "ctrlWeatherDev$step".
};
+ }
+ else {
+ $result->{'DWD Weather Attributes'}{result} .= qq{The DWD device "$fcname" doesn't exist.
};
+ }
+
+ $result->{'DWD Weather Attributes'}{fault} = 1;
}
else {
- $result->{'DWD Weather Attributes'}{note} .= qq{checked attributes of device "$fcname":
}. join (' ', @dweattrmust). '
';
+ $result->{'DWD Weather Attributes'}{note} .= qq{checked attributes of device "$fcname":
}. join (' ', @dweattrmust).'
';
$err = checkdwdattr ($name, $fcname, \@dweattrmust);
if ($err) {
$result->{'DWD Weather Attributes'}{state} = $nok;
- $result->{'DWD Weather Attributes'}{result} .= $err. '
';
+ $result->{'DWD Weather Attributes'}{result} .= $err.'
';
$result->{'DWD Weather Attributes'}{fault} = 1;
}
else {
- $result->{'DWD Weather Attributes'}{result} = $hqtxt{fulfd}{$lang};
+ $result->{'DWD Weather Attributes'}{result} .= $hqtxt{fulfd}{$lang}." ($hqtxt{attrib}{$lang}: ctrlWeatherDev$step)
";
}
}
}
@@ -13519,11 +13742,11 @@ sub checkPlantConfig {
## Allgemeine Settings
########################
- my $eocr = AttrVal ($name, 'event-on-change-reading', '');
- my $aiprep = isPrepared4AI ($hash, 'full');
- my $aiusemsg = CurrentVal ($hash, 'aicanuse', '');
- my ($cset, $lat, $lon) = locCoordinates();
- my $einstds = "";
+ my $eocr = AttrVal ($name, 'event-on-change-reading', '');
+ my $aiprep = isPrepared4AI ($hash, 'full');
+ my $aiusemsg = CurrentVal ($hash, 'aicanuse', '');
+ my ($cset, $lat, $lon, $alt) = locCoordinates();
+ my $einstds = "";
if (!$eocr || $eocr ne '.*') {
$einstds = 'to .*' if($eocr ne '.*');
@@ -13553,6 +13776,13 @@ sub checkPlantConfig {
$result->{'Common Settings'}{note} .= qq{Set the coordinates of your installation in the longitude attribute of the global device.
};
$result->{'Common Settings'}{warn} = 1;
}
+
+ if (!$alt) {
+ $result->{'Common Settings'}{state} = $nok;
+ $result->{'Common Settings'}{result} .= qq{Attribute altitude in global device is not set.
};
+ $result->{'Common Settings'}{note} .= qq{Set the altitude in meters above sea level in the altitude attribute of the global device.
};
+ $result->{'Common Settings'}{fault} = 1;
+ }
if (!$aiprep) {
$result->{'Common Settings'}{state} = $info;
@@ -13583,9 +13813,7 @@ sub checkPlantConfig {
$result->{'Common Settings'}{warn} = 1;
}
- ## allg. Settings bei Nutzung Forecast.Solar API
- #################################################
- if (isForecastSolarUsed ($hash)) {
+ if (isForecastSolarUsed ($hash)) { # allg. Settings bei Nutzung Forecast.Solar API
if (!$pcf || $pcf !~ /on/xs) {
$result->{'Common Settings'}{state} = $info;
$result->{'Common Settings'}{result} .= qq{pvCorrectionFactor_Auto is set to "$pcf"
};
@@ -13606,17 +13834,14 @@ sub checkPlantConfig {
$result->{'Common Settings'}{fault} = 1;
}
- if (!$result->{'Common Settings'}{fault} && !$result->{'Common Settings'}{warn} && !$result->{'Common Settings'}{info}) {
+ if (!$result->{'Common Settings'}{fault}) {
$result->{'Common Settings'}{result} = $hqtxt{fulfd}{$lang};
- $result->{'Common Settings'}{note} .= qq{checked parameters:
};
- $result->{'Common Settings'}{note} .= qq{global latitude, global longitude
};
+ $result->{'Common Settings'}{note} .= qq{
checked parameters and attributes:
};
$result->{'Common Settings'}{note} .= qq{pvCorrectionFactor_Auto
};
}
}
- ## allg. Settings bei Nutzung SolCast
- #######################################
- if (isSolCastUsed ($hash)) {
+ if (isSolCastUsed ($hash)) { # allg. Settings bei Nutzung SolCast API
my $gdn = AttrVal ('global', 'dnsServer', '');
my $osi = AttrVal ($name, 'ctrlSolCastAPIoptimizeReq', 0);
@@ -13655,17 +13880,14 @@ sub checkPlantConfig {
$result->{'API Access'}{fault} = 1;
}
- if (!$result->{'Common Settings'}{fault} && !$result->{'Common Settings'}{warn} && !$result->{'Common Settings'}{info}) {
+ if (!$result->{'Common Settings'}{fault}) {
$result->{'Common Settings'}{result} = $hqtxt{fulfd}{$lang};
- $result->{'Common Settings'}{note} .= qq{checked parameters:
};
- $result->{'Common Settings'}{note} .= qq{ctrlSolCastAPIoptimizeReq
};
- $result->{'Common Settings'}{note} .= qq{pvCorrectionFactor_Auto, event-on-change-reading, ctrlLanguage, global language, global dnsServer
};
+ $result->{'Common Settings'}{note} .= qq{
checked parameters and attributes:
};
+ $result->{'Common Settings'}{note} .= qq{pvCorrectionFactor_Auto, ctrlSolCastAPIoptimizeReq, global dnsServer
};
}
}
- ## allg. Settings bei Nutzung DWD API
- #######################################
- if (isDWDUsed ($hash)) {
+ if (isDWDUsed ($hash)) { # allg. Settings bei Nutzung DWD API
my $lam = SolCastAPIVal ($hash, '?All', '?All', 'response_message', 'success');
if ($aidtabs) {
@@ -13690,20 +13912,18 @@ sub checkPlantConfig {
$result->{'API Access'}{fault} = 1;
}
- if (!$result->{'Common Settings'}{fault} && !$result->{'Common Settings'}{warn} && !$result->{'Common Settings'}{info}) {
+ if (!$result->{'Common Settings'}{fault}) {
$result->{'Common Settings'}{result} = $hqtxt{fulfd}{$lang};
- $result->{'Common Settings'}{note} .= qq{checked parameters:
};
- $result->{'Common Settings'}{note} .= qq{pvCorrectionFactor_Auto, event-on-change-reading, ctrlLanguage, global language
};
- $result->{'Common Settings'}{note} .= qq{checked Perl modules:
};
+ $result->{'Common Settings'}{note} .= qq{
checked Perl modules:
};
$result->{'Common Settings'}{note} .= qq{AI::DecisionTree
};
+ $result->{'Common Settings'}{note} .= qq{
checked parameters and attributes:
};
+ $result->{'Common Settings'}{note} .= qq{pvCorrectionFactor_Auto
};
}
}
- ## allg. Settings bei Nutzung VictronKI-API
- #############################################
- if (isVictronKiUsed ($hash)) {
+ if (isVictronKiUsed ($hash)) { # allg. Settings bei Nutzung VictronKI-API
my $gdn = AttrVal ('global', 'dnsServer', '');
- my $vrmcr = SolCastAPIVal ($hash, '?VRM', '?API', 'credentials', ''); # Victron VRM Credentials gesetzt
+ my $vrmcr = SolCastAPIVal ($hash, '?VRM', '?API', 'credentials', '');
if ($pcf && $pcf !~ /off/xs) {
$result->{'Common Settings'}{state} = $warn;
@@ -13726,13 +13946,18 @@ sub checkPlantConfig {
$result->{'API Access'}{fault} = 1;
}
- if (!$result->{'Common Settings'}{fault} && !$result->{'Common Settings'}{warn} && !$result->{'Common Settings'}{info}) {
+ if (!$result->{'Common Settings'}{fault}) {
$result->{'Common Settings'}{result} = $hqtxt{fulfd}{$lang};
- $result->{'Common Settings'}{note} .= qq{checked parameters:
};
- $result->{'Common Settings'}{note} .= qq{global dnsServer, global language
};
- $result->{'Common Settings'}{note} .= qq{pvCorrectionFactor_Auto, vrmCredentials, event-on-change-reading, ctrlLanguage
};
+ $result->{'Common Settings'}{note} .= qq{
checked parameters and attributes:
};
+ $result->{'Common Settings'}{note} .= qq{pvCorrectionFactor_Auto, global dnsServer, vrmCredentials
};
}
}
+
+ if (!$result->{'Common Settings'}{fault}) {
+ $result->{'Common Settings'}{result} = $hqtxt{fulfd}{$lang};
+ $result->{'Common Settings'}{note} .= qq{global latitude, global longitude, global altitude, global language
};
+ $result->{'Common Settings'}{note} .= qq{event-on-change-reading, ctrlLanguage
};
+ }
## FTUI Widget Support
########################
@@ -13799,28 +14024,33 @@ sub checkPlantConfig {
my $hz = keys %{$result};
my $hc = 0;
+ my $cf = 0; # config fault: 1 -> Konfig fehlerhaft, 0 -> Konfig ok
+ my $wn = 0; # Warnung wenn 1
+ my $io = 0; # Info wenn 1
for my $key (sort keys %{$result}) {
$hc++;
- $cf = $result->{$key}{fault} if($result->{$key}{fault});
- $wn = $result->{$key}{warn} if($result->{$key}{warn});
- $io = $result->{$key}{info} if($result->{$key}{info});
+ $cf = $result->{$key}{fault} if($result->{$key}{fault});
+ $wn = $result->{$key}{warn} if($result->{$key}{warn});
+ $io = $result->{$key}{info} if($result->{$key}{info});
+
+ $result->{$key}{state} = $warn if($result->{$key}{warn});
+ $result->{$key}{state} = $nok if($result->{$key}{fault});
+
$out .= qq{
};
$out .= qq{ $key | };
$out .= qq{ $result->{$key}{state} | };
- $out .= qq{ | };
+ $out .= qq{ | };
$out .= qq{ $result->{$key}{result} | };
- $out .= qq{ | };
+ $out .= qq{ | };
$out .= qq{ $result->{$key}{note} | };
$out .= qq{
};
- #if ($hc < $hz) { # Tabelle wird auf Tablet zu groß mit Zwischenzeilen
- # $out .= qq{};
- # $out .= qq{ | };
- # $out .= qq{
};
- #}
-
- $out .= qq{
};
+ if ($hc < $hz) { # Zwischenzeile
+ $out .= qq{};
+ $out .= qq{ | };
+ $out .= qq{
};
+ }
}
$out .= qq{};
@@ -13877,26 +14107,26 @@ sub timestampToTimestring {
return if($epoch !~ /[0-9]/xs);
- if (length ($epoch) == 13) { # Millisekunden
+ if (length ($epoch) == 13) { # Millisekunden
$epoch = $epoch / 1000;
}
my ($lyear,$lmonth,$lday,$lhour,$lmin,$lsec) = (localtime($epoch))[5,4,3,2,1,0];
my $tm;
- $lyear += 1900; # year is 1900 based
- $lmonth++; # month number is zero based
+ $lyear += 1900; # year is 1900 based
+ $lmonth++; # month number is zero based
- my ($sec,$min,$hour,$day,$mon,$year) = (localtime(time))[0,1,2,3,4,5]; # Standard f. z.B. Readingstimstamp
+ my ($sec,$min,$hour,$day,$mon,$year) = (localtime(time))[0,1,2,3,4,5]; # Standard f. z.B. Readingstimstamp
$year += 1900;
$mon++;
- my $realtm = sprintf("%04d-%02d-%02d %02d:%02d:%02d", $year,$mon,$day,$hour,$min,$sec); # engl. Variante von aktuellen timestamp
- my $tmdef = sprintf("%04d-%02d-%02d %02d:%s", $lyear,$lmonth,$lday,$lhour,"00:00"); # engl. Variante von $epoch für Logging-Timestamps etc. (Minute/Sekunde == 00)
- my $tmfull = sprintf("%04d-%02d-%02d %02d:%02d:%02d", $lyear,$lmonth,$lday,$lhour,$lmin,$lsec); # engl. Variante Vollzeit von $epoch
+ my $realtm = sprintf ("%04d-%02d-%02d %02d:%02d:%02d", $year,$mon,$day,$hour,$min,$sec); # engl. Variante von aktuellen timestamp
+ my $tmdef = sprintf ("%04d-%02d-%02d %02d:%s", $lyear,$lmonth,$lday,$lhour,"00:00"); # engl. Variante von $epoch für Logging-Timestamps etc. (Minute/Sekunde == 00)
+ my $tmfull = sprintf ("%04d-%02d-%02d %02d:%02d:%02d", $lyear,$lmonth,$lday,$lhour,$lmin,$lsec); # engl. Variante Vollzeit von $epoch
if($lang eq "DE") {
- $tm = sprintf("%02d.%02d.%04d %02d:%02d:%02d", $lday,$lmonth,$lyear,$lhour,$lmin,$lsec); # deutsche Variante Vollzeit von $epoch
+ $tm = sprintf ("%02d.%02d.%04d %02d:%02d:%02d", $lday,$lmonth,$lyear,$lhour,$lmin,$lsec); # deutsche Variante Vollzeit von $epoch
}
else {
$tm = $tmfull;
@@ -14761,19 +14991,20 @@ return ($riseshift, $setshift);
################################################################
# Prüfung ob global Attr latitude und longitude gesetzt sind
-# gibt latitude und longitude zurück
+# gibt latitude, longitude und altitude zurück
################################################################
sub locCoordinates {
my $set = 0;
my $lat = AttrVal ('global', 'latitude', '');
my $lon = AttrVal ('global', 'longitude', '');
+ my $alt = AttrVal ('global', 'altitude', '');
if($lat && $lon) {
$set = 1;
}
-return ($set, $lat, $lon);
+return ($set, $lat, $lon, $alt);
}
################################################################
@@ -14954,24 +15185,24 @@ return ($cname, $dswname);
# diskrete Temperaturen in "Bins" wandeln
################################################################
sub temp2bin {
- my $temp = shift;
+ my $val = shift;
- my $bin = $temp > 35 ? '35' :
- $temp > 32 ? '35' :
- $temp > 30 ? '30' :
- $temp > 27 ? '30' :
- $temp > 25 ? '25' :
- $temp > 22 ? '25' :
- $temp > 20 ? '20' :
- $temp > 17 ? '20' :
- $temp > 15 ? '15' :
- $temp > 12 ? '15' :
- $temp > 10 ? '10' :
- $temp > 7 ? '10' :
- $temp > 5 ? '05' :
- $temp > 2 ? '05' :
- $temp > 0 ? '00' :
- '-05';
+ my $bin = $val > 35 ? 35 :
+ $val > 32 ? 35 :
+ $val > 30 ? 30 :
+ $val > 27 ? 30 :
+ $val > 25 ? 25 :
+ $val > 22 ? 25 :
+ $val > 20 ? 20 :
+ $val > 17 ? 20 :
+ $val > 15 ? 15 :
+ $val > 12 ? 15 :
+ $val > 10 ? 10 :
+ $val > 7 ? 10 :
+ $val > 5 ? 5 :
+ $val > 2 ? 5 :
+ $val > 0 ? 0 :
+ -5;
return $bin;
}
@@ -14980,48 +15211,48 @@ return $bin;
# diskrete Bewölkung in "Bins" wandeln
################################################################
sub cloud2bin {
- my $wcc = shift;
+ my $val = shift;
- my $bin = $wcc == 100 ? '100' :
- $wcc > 97 ? '100' :
- $wcc > 95 ? '95' :
- $wcc > 92 ? '95' :
- $wcc > 90 ? '90' :
- $wcc > 87 ? '90' :
- $wcc > 85 ? '85' :
- $wcc > 82 ? '85' :
- $wcc > 80 ? '80' :
- $wcc > 77 ? '80' :
- $wcc > 75 ? '75' :
- $wcc > 72 ? '75' :
- $wcc > 70 ? '70' :
- $wcc > 67 ? '70' :
- $wcc > 65 ? '65' :
- $wcc > 62 ? '65' :
- $wcc > 60 ? '60' :
- $wcc > 57 ? '60' :
- $wcc > 55 ? '55' :
- $wcc > 52 ? '55' :
- $wcc > 50 ? '50' :
- $wcc > 47 ? '50' :
- $wcc > 45 ? '45' :
- $wcc > 42 ? '45' :
- $wcc > 40 ? '40' :
- $wcc > 37 ? '40' :
- $wcc > 35 ? '35' :
- $wcc > 32 ? '35' :
- $wcc > 30 ? '30' :
- $wcc > 27 ? '30' :
- $wcc > 25 ? '25' :
- $wcc > 22 ? '25' :
- $wcc > 20 ? '20' :
- $wcc > 17 ? '20' :
- $wcc > 15 ? '15' :
- $wcc > 12 ? '15' :
- $wcc > 10 ? '10' :
- $wcc > 7 ? '10' :
- $wcc > 5 ? '05' :
- $wcc > 2 ? '05' :
+ my $bin = $val == 100 ? '100' :
+ $val > 97 ? '100' :
+ $val > 95 ? '95' :
+ $val > 92 ? '95' :
+ $val > 90 ? '90' :
+ $val > 87 ? '90' :
+ $val > 85 ? '85' :
+ $val > 82 ? '85' :
+ $val > 80 ? '80' :
+ $val > 77 ? '80' :
+ $val > 75 ? '75' :
+ $val > 72 ? '75' :
+ $val > 70 ? '70' :
+ $val > 67 ? '70' :
+ $val > 65 ? '65' :
+ $val > 62 ? '65' :
+ $val > 60 ? '60' :
+ $val > 57 ? '60' :
+ $val > 55 ? '55' :
+ $val > 52 ? '55' :
+ $val > 50 ? '50' :
+ $val > 47 ? '50' :
+ $val > 45 ? '45' :
+ $val > 42 ? '45' :
+ $val > 40 ? '40' :
+ $val > 37 ? '40' :
+ $val > 35 ? '35' :
+ $val > 32 ? '35' :
+ $val > 30 ? '30' :
+ $val > 27 ? '30' :
+ $val > 25 ? '25' :
+ $val > 22 ? '25' :
+ $val > 20 ? '20' :
+ $val > 17 ? '20' :
+ $val > 15 ? '15' :
+ $val > 12 ? '15' :
+ $val > 10 ? '10' :
+ $val > 7 ? '10' :
+ $val > 5 ? '05' :
+ $val > 2 ? '05' :
'00';
return $bin;
@@ -15031,49 +15262,96 @@ return $bin;
# diskrete Rain Prob in "Bins" wandeln
################################################################
sub rain2bin {
- my $wrp = shift;
+ my $val = shift;
- my $bin = $wrp == 100 ? '100' :
- $wrp > 97 ? '100' :
- $wrp > 95 ? '95' :
- $wrp > 92 ? '95' :
- $wrp > 90 ? '90' :
- $wrp > 87 ? '90' :
- $wrp > 85 ? '85' :
- $wrp > 82 ? '85' :
- $wrp > 80 ? '80' :
- $wrp > 77 ? '80' :
- $wrp > 75 ? '75' :
- $wrp > 72 ? '75' :
- $wrp > 70 ? '70' :
- $wrp > 67 ? '70' :
- $wrp > 65 ? '65' :
- $wrp > 62 ? '65' :
- $wrp > 60 ? '60' :
- $wrp > 57 ? '60' :
- $wrp > 55 ? '55' :
- $wrp > 52 ? '55' :
- $wrp > 50 ? '50' :
- $wrp > 47 ? '50' :
- $wrp > 45 ? '45' :
- $wrp > 42 ? '45' :
- $wrp > 40 ? '40' :
- $wrp > 37 ? '40' :
- $wrp > 35 ? '35' :
- $wrp > 32 ? '35' :
- $wrp > 30 ? '30' :
- $wrp > 27 ? '30' :
- $wrp > 25 ? '25' :
- $wrp > 22 ? '25' :
- $wrp > 20 ? '20' :
- $wrp > 17 ? '20' :
- $wrp > 15 ? '15' :
- $wrp > 12 ? '15' :
- $wrp > 10 ? '10' :
- $wrp > 7 ? '10' :
- $wrp > 5 ? '05' :
- $wrp > 2 ? '05' :
- '00';
+ my $bin = $val == 100 ? 100 :
+ $val > 97 ? 100 :
+ $val > 95 ? 95 :
+ $val > 92 ? 95 :
+ $val > 90 ? 90 :
+ $val > 87 ? 90 :
+ $val > 85 ? 85 :
+ $val > 82 ? 85 :
+ $val > 80 ? 80 :
+ $val > 77 ? 80 :
+ $val > 75 ? 75 :
+ $val > 72 ? 75 :
+ $val > 70 ? 70 :
+ $val > 67 ? 70 :
+ $val > 65 ? 65 :
+ $val > 62 ? 65 :
+ $val > 60 ? 60 :
+ $val > 57 ? 60 :
+ $val > 55 ? 55 :
+ $val > 52 ? 55 :
+ $val > 50 ? 50 :
+ $val > 47 ? 50 :
+ $val > 45 ? 45 :
+ $val > 42 ? 45 :
+ $val > 40 ? 40 :
+ $val > 37 ? 40 :
+ $val > 35 ? 35 :
+ $val > 32 ? 35 :
+ $val > 30 ? 30 :
+ $val > 27 ? 30 :
+ $val > 25 ? 25 :
+ $val > 22 ? 25 :
+ $val > 20 ? 20 :
+ $val > 17 ? 20 :
+ $val > 15 ? 15 :
+ $val > 12 ? 15 :
+ $val > 10 ? 10 :
+ $val > 7 ? 10 :
+ $val > 5 ? 5 :
+ $val > 2 ? 5 :
+ 0;
+
+return $bin;
+}
+
+################################################################
+# diskrete Sonnen Höhe (altitude) in "Bins" wandeln
+################################################################
+sub sunalt2bin {
+ my $val = shift;
+
+ my $bin = $val == 90 ? 90 :
+ $val > 87 ? 90 :
+ $val > 85 ? 85 :
+ $val > 82 ? 85 :
+ $val > 80 ? 80 :
+ $val > 77 ? 80 :
+ $val > 75 ? 75 :
+ $val > 72 ? 75 :
+ $val > 70 ? 70 :
+ $val > 67 ? 70 :
+ $val > 65 ? 65 :
+ $val > 62 ? 65 :
+ $val > 60 ? 60 :
+ $val > 57 ? 60 :
+ $val > 55 ? 55 :
+ $val > 52 ? 55 :
+ $val > 50 ? 50 :
+ $val > 47 ? 50 :
+ $val > 45 ? 45 :
+ $val > 42 ? 45 :
+ $val > 40 ? 40 :
+ $val > 37 ? 40 :
+ $val > 35 ? 35 :
+ $val > 32 ? 35 :
+ $val > 30 ? 30 :
+ $val > 27 ? 30 :
+ $val > 25 ? 25 :
+ $val > 22 ? 25 :
+ $val > 20 ? 20 :
+ $val > 17 ? 20 :
+ $val > 15 ? 15 :
+ $val > 12 ? 15 :
+ $val > 10 ? 10 :
+ $val > 7 ? 10 :
+ $val > 5 ? 5 :
+ $val > 2 ? 5 :
+ 0;
return $bin;
}
@@ -15231,6 +15509,7 @@ return;
# batouttotal - totale Batterieentladung (Wh)
# batout - Batterieentladung der Stunde (Wh)
# batmsoc - max. SOC des Tages (%)
+# batmaxsoc - maximum SOC (%) des Tages
# batsetsoc - optimaler (berechneter) SOC (%) für den Tag
# weatherid - Wetter ID
# wcc - Grad der Bewölkung
@@ -15240,6 +15519,8 @@ return;
# dayname - Tagesname (Kürzel)
# csmt${c} - Totalconsumption Consumer $c (1..$maxconsumer)
# csme${c} - Consumption Consumer $c (1..$maxconsumer) in $hod
+# sunaz - Azimuth der Sonne (in Dezimalgrad)
+# sunalt - Höhe der Sonne (in Dezimalgrad)
# $def: Defaultwert
#
###############################################################################
@@ -15334,7 +15615,7 @@ return $def;
# $q: Qualität des Korrekturfaktors
#
# $hod: Stunde des Tages (01,02,...,24)
-# $range: Range Bewölkung (1...100) oder "percentile"
+# $range: Range Bewölkung (1...100) oder "simple"
# $def: Defaultwert
#
################################################################
@@ -15378,7 +15659,7 @@ return ($pvcorrf, $quality);
# $dnumsum: Anzahl Tage pro Bewölkungsbereich über die gesamte Laufzeit
#
# $hod: Stunde des Tages (01,02,...,24)
-# $range: Range Bewölkung (1...100) oder "percentile"
+# $range: Range Bewölkung (1...100) oder "simple"
# $def: Defaultwert
#
#######################################################################################################
@@ -15520,7 +15801,7 @@ return $def;
#
# $strg: - Name des Strings aus modulePeakString
# $key: peak - Peakleistung aus modulePeakString
-# tilt - Neigungswinkel der Module aus moduleTiltAngle
+# tilt - Neigungswinkel der Module aus moduleDeclination
# dir - Ausrichtung der Module als Azimut-Bezeichner (N,NE,E,SE,S,SW,W,NW)
# azimut - Ausrichtung der Module als Azimut Angabe -180 .. 0 .. 180
#
@@ -15585,6 +15866,7 @@ return $def;
# wcc - Bewölkung als Bin
# wrp - Regenwert als Bin
# hod - Stunde des Tages
+# sunalt - Höhe der Sonne (in Dezimalgrad)
# pvrl - reale PV Erzeugung
#
# $def: Defaultwert
@@ -15796,22 +16078,23 @@ to ensure that the system configuration is correct.
After the definition of the device, depending on the forecast sources used, it is mandatory to store additional
plant-specific information with the corresponding set commands.
- The following set commands are used to store information that is relevant for the function of the module:
+ The following set commands and attributes are used to store information that is relevant for the function of the
+ module:
- currentWeatherDev | DWD_OpenData Device which provides meteorological data (e.g. cloud cover) |
+ ctrlWeatherDevX | DWD_OpenData Device which provides meteorological data (e.g. cloud cover) |
currentRadiationAPI | DWD_OpenData Device or API for the delivery of radiation data. |
currentInverterDev | Device which provides PV performance data |
currentMeterDev | Device which supplies network I/O data |
currentBatteryDev | Device which provides battery performance data (if available) |
inverterStrings | Identifier of the existing plant strings |
- moduleDirection | Alignment (azimuth) of the plant strings |
+ moduleAzimuth | Azimuth of the plant strings |
modulePeakString | the DC peak power of the plant strings |
roofIdentPair | the identification data (when using the SolCast API) |
moduleRoofTops | the Rooftop parameters (when using the SolCast API) |
- moduleTiltAngle | the inclination angles of the plant modules |
+ moduleDeclination | the inclination angles of the plant modules |
@@ -16162,28 +16445,10 @@ to ensure that the system configuration is correct.
-
+
-
- - modulePeakString <Stringname1>=<Peak> [<Stringname2>=<Peak> <Stringname3>=<Peak> ...]
-
- The DC peak power of the string "StringnameX" in kWp. The string name is a key value of the
- Reading inverterStrings.
- When using an AI-based API (e.g. Model VictronKiAPI), the peak powers of all existing strings are to be assigned as
- a sum to the string name KI-based.
-
-
- Examples:
- set <name> modulePeakString eastroof=5.1 southgarage=2.0 S3=7.2
- set <name> modulePeakString KI-based=14.3 (for AI based API)
-
-
-
-
-
-
-
- - moduleDirection <Stringname1>=<dir> [<Stringname2>=<dir> <Stringname3>=<dir> ...]
+
+ - moduleAzimuth <Stringname1>=<dir> [<Stringname2>=<dir> <Stringname3>=<dir> ...]
(only model DWD, ForecastSolarAPI)
Alignment <dir> of the solar modules in the string "StringnameX". The string name is a key value of the
@@ -16214,7 +16479,42 @@ to ensure that the system configuration is correct.
Example:
- set <name> moduleDirection eastroof=-90 southgarage=S S3=NW
+ set <name> moduleAzimuth eastroof=-90 southgarage=S S3=NW
+
+
+
+
+
+
+
+ - moduleDeclination <Stringname1>=<Angle> [<Stringname2>=<Angle> <Stringname3>=<Angle> ...]
+ (only model DWD, ForecastSolarAPI)
+
+ Tilt angle of the solar modules. The string name is a key value of the reading inverterStrings.
+ Possible angles of inclination are: 0,5,10,15,20,25,30,35,40,45,50,55,60,65,70,75,80,85,90
+ (0 = horizontal, 90 = vertical).
+
+
+ Example:
+ set <name> moduleDeclination eastroof=40 southgarage=60 S3=30
+
+
+
+
+
+
+
+ - modulePeakString <Stringname1>=<Peak> [<Stringname2>=<Peak> <Stringname3>=<Peak> ...]
+
+ The DC peak power of the string "StringnameX" in kWp. The string name is a key value of the
+ Reading inverterStrings.
+ When using an AI-based API (e.g. Model VictronKiAPI), the peak powers of all existing strings are to be assigned as
+ a sum to the string name KI-based.
+
+
+ Examples:
+ set <name> modulePeakString eastroof=5.1 southgarage=2.0 S3=7.2
+ set <name> modulePeakString KI-based=14.3 (for AI based API)
@@ -16239,23 +16539,6 @@ to ensure that the system configuration is correct.
-
-
- - moduleTiltAngle <Stringname1>=<Angle> [<Stringname2>=<Angle> <Stringname3>=<Angle> ...]
- (only model DWD, ForecastSolarAPI)
-
- Tilt angle of the solar modules. The string name is a key value of the reading inverterStrings.
- Possible angles of inclination are: 0,5,10,15,20,25,30,35,40,45,50,55,60,65,70,75,80,85,90
- (0 = horizontal, 90 = vertical).
-
-
- Example:
- set <name> moduleTiltAngle eastroof=40 southgarage=60 S3=30
-
-
-
-
-
- operatingMemory backup | save | recover-<File>
@@ -16632,6 +16915,8 @@ to ensure that the system configuration is correct.
rad1h | predicted global radiation |
rrange | calculated range of rain probability |
starttime | start time of the record |
+ sunaz | Azimuth of the sun (in decimal degrees) |
+ sunalt | Altitude of the sun (in decimal degrees) |
temp | predicted outdoor temperature |
today | has value '1' if start date on current day |
wrp | predicted degree of rain probability |
@@ -16656,30 +16941,32 @@ to ensure that the system configuration is correct.
- etotal | total energy yield (Wh) at the beginning of the hour |
- pvfc | the predicted PV yield (Wh) |
- pvrl | real PV generation (Wh) |
- pvrlvd | 1-'pvrl' is valid and is taken into account in the learning process, 0-'pvrl' is assessed as abnormal |
- gcon | real power consumption (Wh) from the electricity grid |
- confc | expected energy consumption (Wh) |
- con | real energy consumption (Wh) of the house |
- gfeedin | real feed-in (Wh) into the electricity grid |
batintotal | total battery charge (Wh) at the beginning of the hour |
batin | Hour battery charge (Wh) |
batouttotal | total battery discharge (Wh) at the beginning of the hour |
batout | Battery discharge of the hour (Wh) |
batmaxsoc | maximum SOC (%) of the day |
batsetsoc | optimum SOC setpoint (%) for the day |
+ confc | expected energy consumption (Wh) |
+ con | real energy consumption (Wh) of the house |
+ csmtXX | total energy consumption of ConsumerXX |
+ csmeXX | Energy consumption of ConsumerXX in the hour of the day (hour 99 = daily energy consumption) |
+ cyclescsmXX | Number of active cycles of ConsumerXX of the day |
+ etotal | total energy yield (Wh) at the beginning of the hour |
+ gcon | real power consumption (Wh) from the electricity grid |
+ gfeedin | real feed-in (Wh) into the electricity grid |
+ hourscsmeXX | average hours of an active cycle of ConsumerXX of the day |
+ minutescsmXX | total active minutes in the hour of ConsumerXX |
+ pvfc | the predicted PV yield (Wh) |
+ pvrl | real PV generation (Wh) |
+ pvrlvd | 1-'pvrl' is valid and is taken into account in the learning process, 0-'pvrl' is assessed as abnormal |
+ pvcorrf | Autocorrection factor used / forecast quality achieved |
+ rad1h | global radiation (kJ/m2) |
+ sunalt | Altitude of the sun (in decimal degrees) |
+ sunaz | Azimuth of the sun (in decimal degrees) |
wid | Weather identification number |
wcc | effective cloud cover |
wrp | Probability of precipitation > 0.1 mm during the respective hour |
- pvcorrf | Autocorrection factor used / forecast quality achieved |
- rad1h | global radiation (kJ/m2) |
- csmtXX | total energy consumption of ConsumerXX |
- csmeXX | Energy consumption of ConsumerXX in the hour of the day (hour 99 = daily energy consumption) |
- minutescsmXX | total active minutes in the hour of ConsumerXX |
- hourscsmeXX | average hours of an active cycle of ConsumerXX of the day |
- cyclescsmXX | Number of active cycles of ConsumerXX of the day |
@@ -16719,11 +17006,11 @@ to ensure that the system configuration is correct.
pvapifc | expected PV generation (Wh) of the API used |
pvaifc | PV forecast (Wh) of the AI for the next 24h from the current hour of the day |
pvfc | PV forecast used for the next 24h from the current hour of the day |
- pvcorrf | Autocorrection factors for the hour of the day, where "percentile" is the simple correction factor. |
+ pvcorrf | Autocorrection factors for the hour of the day, where 'simple' is the simple correction factor. |
pvfcsum | summary PV forecast per cloud area over the entire term |
pvrl | real PV generation of the last 24h (Attention: pvforecast and pvreal do not refer to the same period!) |
pvrlsum | summary real PV generation per cloud area over the entire term |
- quality | Quality of the autocorrection factors (0..1), where "percentile" is the quality of the simple correction factor. |
+ quality | Quality of the autocorrection factors (0..1), where 'simple' is the quality of the simple correction factor. |
runTimeTrainAI | Duration of the last AI training |
tdayDvtn | Today's deviation PV forecast/generation in % |
temp | Outdoor temperature |
@@ -17363,8 +17650,7 @@ to ensure that the system configuration is correct.
- ctrlWeatherDevX
- Defines the device (type DWD_OpenData), which provides the required weather data (cloudiness, precipitation,
- sunrise/sunset, etc.).
+ Defines the device (type DWD_OpenData), which provides the required weather data (cloudiness, precipitation, etc.).
If no device of this type exists, the selection list is empty and a device must first be defined
(see DWD_OpenData Commandref).
If more than one ctrlWeatherDevX is specified, the average of all weather stations is determined and used
@@ -17381,6 +17667,10 @@ to ensure that the system configuration is correct.
forecastStation | <Station code of the evaluated DWD station> |
+
+
+ Note: If the latitude and longitude attributes are set in the global device, the sunrise and sunset
+ result from this information.
@@ -17865,23 +18155,23 @@ die ordnungsgemäße Anlagenkonfiguration geprüft werden.
Nach der Definition des Devices sind in Abhängigkeit der verwendeten Prognosequellen zwingend weitere
anlagenspezifische Angaben mit den entsprechenden set-Kommandos zu hinterlegen.
- Mit nachfolgenden set-Kommandos werden für die Funktion des Moduls maßgebliche Informationen
+ Mit nachfolgenden set-Kommandos und Attributen werden für die Funktion des Moduls maßgebliche Informationen
hinterlegt:
- currentWeatherDev | DWD_OpenData Device welches meteorologische Daten (z.B. Bewölkung) liefert |
+ ctrlWeatherDevX | DWD_OpenData Device welches meteorologische Daten (z.B. Bewölkung) liefert |
currentRadiationAPI | DWD_OpenData Device bzw. API zur Lieferung von Strahlungsdaten |
currentInverterDev | Device welches PV Leistungsdaten liefert |
currentMeterDev | Device welches Netz I/O-Daten liefert |
currentBatteryDev | Device welches Batterie Leistungsdaten liefert (sofern vorhanden) |
inverterStrings | Bezeichner der vorhandenen Anlagenstrings |
- moduleDirection | Ausrichtung (Azimut) der Anlagenstrings |
+ moduleAzimuth | Ausrichtung (Azimut) der Anlagenstrings |
modulePeakString | die DC-Peakleistung der Anlagenstrings |
roofIdentPair | die Identifikationsdaten (bei Nutzung der SolCast API) |
moduleRoofTops | die Rooftop Parameter (bei Nutzung der SolCast API) |
- moduleTiltAngle | die Neigungswinkel der der Anlagenmodule |
+ moduleDeclination | die Neigungswinkel der der Anlagenmodule |
@@ -18234,28 +18524,10 @@ die ordnungsgemäße Anlagenkonfiguration geprüft werden.
-
+
-
- - modulePeakString <Stringname1>=<Peak> [<Stringname2>=<Peak> <Stringname3>=<Peak> ...]
-
- Die DC Peakleistung des Strings "StringnameX" in kWp. Der Stringname ist ein Schlüsselwert des
- Readings inverterStrings.
- Bei Verwendung einer KI basierenden API (z.B. Model VictronKiAPI) sind die Peakleistungen aller vorhandenen
- Strings als Summe dem Stringnamen KI-based zuzuordnen.
-
-
- Beispiele:
- set <name> modulePeakString Ostdach=5.1 Südgarage=2.0 S3=7.2
- set <name> modulePeakString KI-based=14.3 (bei KI basierender API)
-
-
-
-
-
-
-
- - moduleDirection <Stringname1>=<dir> [<Stringname2>=<dir> <Stringname3>=<dir> ...]
+
+ - moduleAzimuth <Stringname1>=<dir> [<Stringname2>=<dir> <Stringname3>=<dir> ...]
(nur Model DWD, ForecastSolarAPI)
Ausrichtung <dir> der Solarmodule im String "StringnameX". Der Stringname ist ein Schlüsselwert des
@@ -18286,7 +18558,42 @@ die ordnungsgemäße Anlagenkonfiguration geprüft werden.
Beispiel:
- set <name> moduleDirection Ostdach=-90 Südgarage=S S3=NW
+ set <name> moduleAzimuth Ostdach=-90 Südgarage=S S3=NW
+
+
+
+
+
+
+
+ - moduleDeclination <Stringname1>=<Winkel> [<Stringname2>=<Winkel> <Stringname3>=<Winkel> ...]
+ (nur Model DWD, ForecastSolarAPI)
+
+ Neigungswinkel der Solarmodule. Der Stringname ist ein Schlüsselwert des Readings inverterStrings.
+ Mögliche Neigungswinkel sind: 0,5,10,15,20,25,30,35,40,45,50,55,60,65,70,75,80,85,90
+ (0 = waagerecht, 90 = senkrecht).
+
+
+ Beispiel:
+ set <name> moduleDeclination Ostdach=40 Südgarage=60 S3=30
+
+
+
+
+
+
+
+ - modulePeakString <Stringname1>=<Peak> [<Stringname2>=<Peak> <Stringname3>=<Peak> ...]
+
+ Die DC Peakleistung des Strings "StringnameX" in kWp. Der Stringname ist ein Schlüsselwert des
+ Readings inverterStrings.
+ Bei Verwendung einer KI basierenden API (z.B. Model VictronKiAPI) sind die Peakleistungen aller vorhandenen
+ Strings als Summe dem Stringnamen KI-based zuzuordnen.
+
+
+ Beispiele:
+ set <name> modulePeakString Ostdach=5.1 Südgarage=2.0 S3=7.2
+ set <name> modulePeakString KI-based=14.3 (bei KI basierender API)
@@ -18311,23 +18618,6 @@ die ordnungsgemäße Anlagenkonfiguration geprüft werden.
-
-
- - moduleTiltAngle <Stringname1>=<Winkel> [<Stringname2>=<Winkel> <Stringname3>=<Winkel> ...]
- (nur Model DWD, ForecastSolarAPI)
-
- Neigungswinkel der Solarmodule. Der Stringname ist ein Schlüsselwert des Readings inverterStrings.
- Mögliche Neigungswinkel sind: 0,5,10,15,20,25,30,35,40,45,50,55,60,65,70,75,80,85,90
- (0 = waagerecht, 90 = senkrecht).
-
-
- Beispiel:
- set <name> moduleTiltAngle Ostdach=40 Südgarage=60 S3=30
-
-
-
-
-
- operatingMemory backup | save | recover-<Datei>
@@ -18712,6 +19002,8 @@ die ordnungsgemäße Anlagenkonfiguration geprüft werden.
rad1h | vorhergesagte Globalstrahlung |
rrange | berechneter Bereich der Regenwahrscheinlichkeit |
starttime | Startzeit des Datensatzes |
+ sunaz | Azimuth der Sonne (in Dezimalgrad) |
+ sunalt | Höhe der Sonne (in Dezimalgrad) |
temp | vorhergesagte Außentemperatur |
today | hat Wert '1' wenn Startdatum am aktuellen Tag |
wrp | vorhergesagter Grad der Regenwahrscheinlichkeit |
@@ -18736,30 +19028,33 @@ die ordnungsgemäße Anlagenkonfiguration geprüft werden.
- etotal | totaler Energieertrag (Wh) zu Beginn der Stunde |
- pvfc | der prognostizierte PV Ertrag (Wh) |
- pvrl | reale PV Erzeugung (Wh) |
- pvrlvd | 1-'pvrl' ist gültig und wird im Lernprozess berücksichtigt, 0-'pvrl' ist als abnormal bewertet |
- gcon | realer Leistungsbezug (Wh) aus dem Stromnetz |
- confc | erwarteter Energieverbrauch (Wh) |
- con | realer Energieverbrauch (Wh) des Hauses |
- gfeedin | reale Einspeisung (Wh) in das Stromnetz |
batintotal | totale Batterieladung (Wh) zu Beginn der Stunde |
batin | Batterieladung der Stunde (Wh) |
batouttotal | totale Batterieentladung (Wh) zu Beginn der Stunde |
batout | Batterieentladung der Stunde (Wh) |
batmaxsoc | maximaler SOC (%) des Tages |
batsetsoc | optimaler SOC Sollwert (%) für den Tag |
+ csmtXX | Energieverbrauch total von ConsumerXX |
+ csmeXX | Energieverbrauch von ConsumerXX in der Stunde des Tages (Stunde 99 = Tagesenergieverbrauch) |
+ confc | erwarteter Energieverbrauch (Wh) |
+ con | realer Energieverbrauch (Wh) des Hauses |
+ cyclescsmXX | Anzahl aktive Zyklen von ConsumerXX des Tages |
+ etotal | totaler Energieertrag (Wh) zu Beginn der Stunde |
+ gcon | realer Leistungsbezug (Wh) aus dem Stromnetz |
+ gfeedin | reale Einspeisung (Wh) in das Stromnetz |
+ hourscsmeXX | durchschnittliche Stunden eines Aktivzyklus von ConsumerXX des Tages |
+ minutescsmXX | Summe Aktivminuten in der Stunde von ConsumerXX |
+ pvfc | der prognostizierte PV Ertrag (Wh) |
+ pvrl | reale PV Erzeugung (Wh) |
+ pvrlvd | 1-'pvrl' ist gültig und wird im Lernprozess berücksichtigt, 0-'pvrl' ist als abnormal bewertet |
+ pvcorrf | verwendeter Autokorrekturfaktor / erreichte Prognosequalität |
+ rad1h | Globalstrahlung (kJ/m2) |
+ sunalt | Höhe der Sonne (in Dezimalgrad) |
+ sunaz | Azimuth der Sonne (in Dezimalgrad) |
wid | Identifikationsnummer des Wetters |
wcc | effektive Wolkenbedeckung |
wrp | Wahrscheinlichkeit von Niederschlag > 0,1 mm während der jeweiligen Stunde |
- pvcorrf | verwendeter Autokorrekturfaktor / erreichte Prognosequalität |
- rad1h | Globalstrahlung (kJ/m2) |
- csmtXX | Energieverbrauch total von ConsumerXX |
- csmeXX | Energieverbrauch von ConsumerXX in der Stunde des Tages (Stunde 99 = Tagesenergieverbrauch) |
- minutescsmXX | Summe Aktivminuten in der Stunde von ConsumerXX |
- hourscsmeXX | durchschnittliche Stunden eines Aktivzyklus von ConsumerXX des Tages |
- cyclescsmXX | Anzahl aktive Zyklen von ConsumerXX des Tages |
+
@@ -18799,11 +19094,11 @@ die ordnungsgemäße Anlagenkonfiguration geprüft werden.
pvapifc | erwartete PV Erzeugung (Wh) der verwendeten API |
pvaifc | PV Vorhersage (Wh) der KI für die nächsten 24h ab aktueller Stunde des Tages |
pvfc | verwendete PV Prognose für die nächsten 24h ab aktueller Stunde des Tages |
- pvcorrf | Autokorrekturfaktoren für die Stunde des Tages, wobei "percentile" der einfache (simple) Korrekturfaktor ist. |
+ pvcorrf | Autokorrekturfaktoren für die Stunde des Tages, wobei 'simple' der einfach berechnete Korrekturfaktor ist. |
pvfcsum | Summe PV Prognose pro Bewölkungsbereich über die gesamte Laufzeit |
pvrl | reale PV Erzeugung der letzten 24h (Achtung: pvforecast und pvreal beziehen sich nicht auf den gleichen Zeitraum!) |
pvrlsum | Summe reale PV Erzeugung pro Bewölkungsbereich über die gesamte Laufzeit |
- quality | Qualität der Autokorrekturfaktoren (0..1), wobei "percentile" die Qualität des einfachen (simple) Korrekturfaktors ist. |
+ quality | Qualität der Autokorrekturfaktoren (0..1), wobei 'simple' die Qualität des einfach berechneten Korrekturfaktors ist. |
runTimeTrainAI | Laufzeit des letzten KI Trainings |
tdayDvtn | heutige Abweichung PV Prognose/Erzeugung in % |
temp | Außentemperatur |
@@ -19444,8 +19739,8 @@ die ordnungsgemäße Anlagenkonfiguration geprüft werden.
- ctrlWeatherDevX
- Legt das Device (Typ DWD_OpenData) fest, welches die benötigten Wetterdaten (Bewölkung, Niederschlag,
- Sonnenauf bzw. -untergang, usw.) liefert.
+ Legt das Device (Typ DWD_OpenData) fest, welches die benötigten Wetterdaten (Bewölkung, Niederschlag, usw.)
+ liefert.
Ist noch kein Device dieses Typs vorhanden, ist die Auswahlliste leer und es muß zunächst mindestens ein Device
definiert werden (siehe DWD_OpenData Commandref).
Sind mehr als ein ctrlWeatherDevX angegeben, wird der Durchschnitt aller Wetterstationen ermittelt und verwendet
@@ -19462,6 +19757,10 @@ die ordnungsgemäße Anlagenkonfiguration geprüft werden.
forecastStation | <Stationscode der ausgewerteten DWD Station> |
+
+
+ Hinweis: Sind die Attribute latitude und longitude im global Device gesetzt, ergibt sich der
+ Sonnenauf- und Sonnenuntergang aus diesen Angaben.