mirror of
https://github.com/fhem/fhem-mirror.git
synced 2025-02-25 09:55:38 +00:00
76_SolarForecast: attr setupRadiationAPI and setupWeatherDev1 can be set largely independently of each other
git-svn-id: https://svn.fhem.de/fhem/trunk@29377 2b470e98-0d58-463d-a4d8-8e2adae1ed80
This commit is contained in:
parent
a170dc94e9
commit
ae9aae6352
@ -1,5 +1,7 @@
|
||||
# Add changes at the top of the list. Keep it in ASCII, and 80-char wide.
|
||||
# Do not insert empty lines here, update check depends on it
|
||||
- featur: 76_SolarForecast: attr setupRadiationAPI and setupWeatherDev1 can
|
||||
be set largely independently of each other
|
||||
- change: 76_SolarForecast: Separation of radiation API-data, API-state data,
|
||||
weather-API data
|
||||
- change: 76_SolarForecast: extended Debug collectData for EnergyConsumption
|
||||
|
@ -156,6 +156,9 @@ BEGIN {
|
||||
|
||||
# Versions History intern
|
||||
my %vNotesIntern = (
|
||||
"1.38.0" => "30.11.2024 optimize data handling, rename getter solApiData to radiationApiData, ".
|
||||
"set setupStringAzimuth, setupStringDeclination is checked due to dependencies to OpenMeteo ".
|
||||
"attr setupRadiationAPI and setupWeatherDev1 can be set largely independently of each other ",
|
||||
"1.37.9" => "29.11.2024 activate StatusAPI-Hash, Separation of radiation API-data, API-state data, weather-API data ",
|
||||
"1.37.8" => "28.11.2024 edit commref, func _searchCacheFiles for renaming Cache files when device is renamed ".
|
||||
"_saveEnergyConsumption: extended for Debug collectData, preparation of weatherApiData ".
|
||||
@ -601,7 +604,7 @@ my %hget = ( # Ha
|
||||
forecastQualities => { fn => \&_getForecastQualities, needcred => 0 },
|
||||
nextHours => { fn => \&_getlistNextHours, needcred => 0 },
|
||||
rooftopData => { fn => \&_getRoofTopData, needcred => 0 },
|
||||
solApiData => { fn => \&_getlistSolCastData, needcred => 0 },
|
||||
radiationApiData => { fn => \&_getlistRadiationApiData, needcred => 0 },
|
||||
weatherApiData => { fn => \&_getlistWeatherApiData, needcred => 0 },
|
||||
statusApiData => { fn => \&_getlistStatusApiData, needcred => 0 },
|
||||
valDecTree => { fn => \&_getaiDecTree, needcred => 0 },
|
||||
@ -1293,9 +1296,6 @@ sub Initialize {
|
||||
|
||||
### nicht mehr benötigte Daten verarbeiten - Bereich kann später wieder raus !!
|
||||
##########################################################################################################################
|
||||
# my $av = 'obsolete#-#use#attr#flowGraphicControl#instead';
|
||||
# $hash->{AttrList} .= " flowGraphicCss:$av flowGraphicSize:$av flowGraphicAnimate:$av flowGraphicConsumerDistance:$av flowGraphicShowConsumer:$av flowGraphicShowConsumerDummy:$av flowGraphicShowConsumerPower:$av flowGraphicShowConsumerRemainTime:$av flowGraphicShift:$av ";
|
||||
|
||||
# my $av1 = "obsolete#-#the#attribute#will#be#deleted#soon";
|
||||
# $hash->{AttrList} .= " affect70percentRule:$av1 ctrlAutoRefresh:$av1 ctrlAutoRefreshFW:$av1 ";
|
||||
##########################################################################################################################
|
||||
@ -1364,7 +1364,7 @@ sub Define {
|
||||
|
||||
$params->{file} = $scpicache.$name; # Cache File SolCast API Werte einlesen wenn vorhanden
|
||||
$params->{cachename} = 'solcastapi';
|
||||
$params->{title} = 'solApiData';
|
||||
$params->{title} = 'radiationApiData';
|
||||
_readCacheFile ($params);
|
||||
|
||||
$params->{file} = $statcache.$name; # Cache File API-Status einlesen wenn vorhanden
|
||||
@ -1590,6 +1590,8 @@ sub Set {
|
||||
"powerTrigger:textField-long ".
|
||||
"pvCorrectionFactor_Auto:noLearning,on_simple".($ipai ? ',on_simple_ai,' : ',')."on_complex".($ipai ? ',on_complex_ai,' : ',')."off ".
|
||||
"reset:$resets ".
|
||||
"setupStringAzimuth ".
|
||||
"setupStringDeclination ".
|
||||
$cf." "
|
||||
;
|
||||
|
||||
@ -1599,20 +1601,10 @@ sub Set {
|
||||
$setlist .= "roofIdentPair "
|
||||
;
|
||||
}
|
||||
elsif (isForecastSolarUsed ($hash)) {
|
||||
$setlist .= "setupStringAzimuth ".
|
||||
"setupStringDeclination "
|
||||
;
|
||||
}
|
||||
elsif (isVictronKiUsed ($hash)) {
|
||||
$setlist .= "vrmCredentials "
|
||||
;
|
||||
}
|
||||
else {
|
||||
$setlist .= "setupStringAzimuth ".
|
||||
"setupStringDeclination "
|
||||
;
|
||||
}
|
||||
|
||||
## KI spezifische Setter
|
||||
##########################
|
||||
@ -2513,7 +2505,7 @@ sub Get {
|
||||
"pvCircular:noArg ".
|
||||
"pvHistory:#,exportToCsv,$pvl ".
|
||||
"rooftopData:noArg ".
|
||||
"solApiData:noArg ".
|
||||
"radiationApiData:noArg ".
|
||||
"statusApiData:noArg ".
|
||||
"valCurrent:noArg ".
|
||||
"weatherApiData:noArg "
|
||||
@ -2570,24 +2562,36 @@ sub _getRoofTopData {
|
||||
delete $data{$type}{$name}{current}{dwdRad1hAge};
|
||||
delete $data{$type}{$name}{current}{dwdRad1hAgeTS};
|
||||
|
||||
my $ret = "$name is not a valid SolarForeCast Model: ".$hash->{MODEL};
|
||||
my ($rapi, $wapi) = getStatusApiName ($hash); # $rapi - Radiation-API, $wapi - Weather-API
|
||||
my $ret = "$name is not a valid Radiation ($rapi) and/or Weather ($wapi) Model";
|
||||
|
||||
if ($hash->{MODEL} eq 'SolCastAPI') {
|
||||
|
||||
if ($rapi eq 'SolCast') {
|
||||
$ret = __getSolCastData ($paref);
|
||||
}
|
||||
elsif ($hash->{MODEL} eq 'ForecastSolarAPI') {
|
||||
elsif ($rapi eq 'ForecastSolar') {
|
||||
$ret = __getForecastSolarData ($paref);
|
||||
}
|
||||
elsif ($hash->{MODEL} eq 'DWD') {
|
||||
elsif ($rapi eq 'DWD') {
|
||||
$ret = __getDWDSolarData ($paref);
|
||||
}
|
||||
elsif ($hash->{MODEL} eq 'VictronKiAPI') {
|
||||
elsif ($rapi eq 'VictronKi') {
|
||||
$ret = __getVictronSolarData ($paref);
|
||||
}
|
||||
elsif ($hash->{MODEL} =~ /^OpenMeteo/xs) {
|
||||
|
||||
if ($rapi eq 'OpenMeteo' || $wapi eq 'OpenMeteo') {
|
||||
if ($rapi eq 'OpenMeteo') {
|
||||
$paref->{reqm} = 'MODEL';
|
||||
}
|
||||
else {
|
||||
$paref->{reqm} = 'WEATHERMODEL';
|
||||
}
|
||||
|
||||
$ret = __getopenMeteoData ($paref);
|
||||
}
|
||||
|
||||
delete $paref->{reqm};
|
||||
|
||||
return $ret;
|
||||
}
|
||||
|
||||
@ -2876,7 +2880,7 @@ sub __solCast_ApiResponse {
|
||||
$period =~ s/.*(\d\d).*/$1/; # -> die neuen Daten (in dem Fall nur die einer halben Stunde) im nächsten Schritt addiert werden
|
||||
|
||||
my $est50 = SolCastAPIVal ($hash, $string, $starttmstr, 'pv_estimate50', 0) / (60/$period);
|
||||
$data{$type}{$name}{solcastapi}{$string}{$starttmstr}{pv_estimate50} = $est50 if($est50);
|
||||
$data{$type}{$name}{solcastapi}{$string}{$starttmstr}{pv_estimate50} = sprintf "%.0f", $est50 if($est50);
|
||||
|
||||
$k++;
|
||||
next;
|
||||
@ -2903,14 +2907,16 @@ sub __solCast_ApiResponse {
|
||||
$period = $jdata->{'forecasts'}[$k]{'period'};
|
||||
$period =~ s/.*(\d\d).*/$1/;
|
||||
|
||||
$pvest50 = sprintf "%.0f", ($pvest50 * ($period/60) * 1000);
|
||||
|
||||
if ($debug =~ /apiProcess/x) { # nur für Debugging
|
||||
if (exists $data{$type}{$name}{solcastapi}{$string}{$starttmstr}) {
|
||||
Log3 ($name, 1, qq{$name DEBUG> SolCast API Hash - Start Date/Time: }. $starttmstr);
|
||||
Log3 ($name, 1, qq{$name DEBUG> SolCast API Hash - pv_estimate50 add: }.(sprintf "%.0f", ($pvest50 * ($period/60) * 1000)).qq{, contains already: }.SolCastAPIVal ($hash, $string, $starttmstr, 'pv_estimate50', 0));
|
||||
Log3 ($name, 1, qq{$name DEBUG> SolCast API Hash - pv_estimate50 add: }.$pvest50.qq{, contains already: }.SolCastAPIVal ($hash, $string, $starttmstr, 'pv_estimate50', 0));
|
||||
}
|
||||
}
|
||||
|
||||
$data{$type}{$name}{solcastapi}{$string}{$starttmstr}{pv_estimate50} += sprintf "%.0f", ($pvest50 * ($period/60) * 1000);
|
||||
$data{$type}{$name}{solcastapi}{$string}{$starttmstr}{pv_estimate50} += $pvest50;
|
||||
|
||||
$k++;
|
||||
}
|
||||
@ -4085,6 +4091,7 @@ sub __getopenMeteoData {
|
||||
my $t = $paref->{t};
|
||||
my $lang = $paref->{lang};
|
||||
my $debug = $paref->{debug};
|
||||
my $reqm = $paref->{reqm};
|
||||
|
||||
my $hash = $defs{$name};
|
||||
my $donearq = StatusAPIVal ($hash, 'OpenMeteo', '?All', 'todayDoneAPIrequests', 0);
|
||||
@ -4106,7 +4113,8 @@ sub __getopenMeteoData {
|
||||
|
||||
debugLog ($paref, 'apiCall', qq{Open-Meteo API Call - the daily API requests -> limited to: $ometmaxreq, done: $donearq});
|
||||
|
||||
my $submodel = InternalVal ($hash->{NAME}, 'MODEL', '');
|
||||
my $submodel = InternalVal ($hash->{NAME}, $reqm, '');
|
||||
|
||||
$paref->{allstrings} = AttrVal ($name, 'setupInverterStrings', '');
|
||||
$paref->{submodel} = $submodel eq 'OpenMeteoDWDAPI' ? 'DWD ICON Seamless' :
|
||||
$submodel eq 'OpenMeteoDWDEnsembleAPI' ? 'DWD ICON Seamless Ensemble' :
|
||||
@ -4117,6 +4125,7 @@ sub __getopenMeteoData {
|
||||
|
||||
$paref->{callequivalent} = $submodel eq 'OpenMeteoDWDEnsembleAPI' ? 20 : 1;
|
||||
$paref->{begin} = 1;
|
||||
$paref->{requestmode} = $reqm;
|
||||
|
||||
__openMeteoDWD_ApiRequest ($paref);
|
||||
|
||||
@ -4154,7 +4163,8 @@ sub __openMeteoDWD_ApiRequest {
|
||||
my $debug = $paref->{debug};
|
||||
my $lang = $paref->{lang};
|
||||
my $t = $paref->{t} // int time;
|
||||
my $submodel = $paref->{submodel}; # abzufragendes Wettermodell
|
||||
my $submodel = $paref->{submodel}; # abzufragendes Datenmodell
|
||||
my $requestmode = $paref->{requestmode};
|
||||
|
||||
my $hash = $defs{$name};
|
||||
|
||||
@ -4167,19 +4177,27 @@ sub __openMeteoDWD_ApiRequest {
|
||||
return;
|
||||
}
|
||||
|
||||
my $string;
|
||||
my ($string, $err);
|
||||
($string, $allstrings) = split ",", $allstrings, 2;
|
||||
my ($set, $lat, $lon, $elev) = locCoordinates();
|
||||
|
||||
if (!$set) {
|
||||
my $err = qq{the attribute 'latitude' and/or 'longitude' in global device is not set};
|
||||
singleUpdateState ( {hash => $hash, state => $err, evt => 1} );
|
||||
return $err;
|
||||
$err = qq{ERROR - the attribute 'latitude' and/or 'longitude' in global device is not set};
|
||||
Log3 ($name, 1, "$name - $err");
|
||||
return;
|
||||
}
|
||||
|
||||
my $tilt = StringVal ($hash, $string, 'tilt', '<unknown>');
|
||||
my $az = StringVal ($hash, $string, 'azimut', '<unknown>');
|
||||
|
||||
if ($requestmode eq 'WEATHERMODEL' && $string eq 'KI-based') {$tilt = 0; $az = 0;} # Dummy Settings
|
||||
|
||||
if ($tilt eq '<unknown>' || $az eq '<unknown>') {
|
||||
$err = qq{ERROR OpenMeteo API Call - the reading 'setupStringAzimuth' and/or 'setupStringDeclination' is not set};
|
||||
Log3 ($name, 1, "$name - $err");
|
||||
return;
|
||||
}
|
||||
|
||||
my $url = "https://api.open-meteo.com/v1/forecast?";
|
||||
$url = "https://ensemble-api.open-meteo.com/v1/ensemble?" if($submodel =~ /Ensemble/xs); # Ensemble Modell gewählt
|
||||
$url .= "models=icon_seamless" if($submodel =~ /DWD\sICON\sSeamless/xs);
|
||||
@ -4195,7 +4213,8 @@ sub __openMeteoDWD_ApiRequest {
|
||||
$url .= "&tilt=".$tilt;
|
||||
$url .= "&azimuth=".$az;
|
||||
|
||||
debugLog ($paref, 'apiCall', qq{Open-Meteo API Call - Request for PV-String "$string" with Weather Model >$submodel<:\n$url});
|
||||
debugLog ($paref, 'apiCall', qq{Open-Meteo API Call - Request for PV-String "$string" with Data Model >$submodel<:\n$url});
|
||||
debugLog ($paref, 'apiCall|apiProcess', qq{Open-Meteo API Call - Request Mode: $requestmode});
|
||||
|
||||
my $caller = (caller(0))[3]; # Rücksprungmarke
|
||||
|
||||
@ -4209,6 +4228,7 @@ sub __openMeteoDWD_ApiRequest {
|
||||
submodel => $submodel,
|
||||
begin => $paref->{begin},
|
||||
callequivalent => $paref->{callequivalent},
|
||||
requestmode => $requestmode,
|
||||
caller => \&$caller,
|
||||
stc => [gettimeofday],
|
||||
allstrings => $allstrings,
|
||||
@ -4257,6 +4277,7 @@ sub __openMeteoDWD_ApiResponse {
|
||||
my $caller = $paref->{caller};
|
||||
my $string = $paref->{string};
|
||||
my $allstrings = $paref->{allstrings};
|
||||
my $requestmode = $paref->{requestmode}; # MODEL oder WEATHERMODEL
|
||||
my $stc = $paref->{stc}; # Startzeit API Abruf
|
||||
my $lang = $paref->{lang};
|
||||
my $debug = $paref->{debug};
|
||||
@ -4379,6 +4400,7 @@ sub __openMeteoDWD_ApiResponse {
|
||||
my $rad1wh = $jdata->{hourly}{global_tilted_irradiance}[$k]; # Wh/m2
|
||||
my $rad = 10 * (sprintf "%.0f", ($rad1wh * $WhtokJ) / 10); # Umrechnung Wh/m2 in kJ/m2 ->
|
||||
my $pv = sprintf "%.2f", int ($rad1wh / 1000 * $peak * $prdef); # Rad wird in kWh/m2 erwartet
|
||||
|
||||
my $don = $jdata->{hourly}{is_day}[$k];
|
||||
my $temp = $jdata->{hourly}{temperature_2m}[$k];
|
||||
my $rain = $jdata->{hourly}{rain}[$k]; # Regen in Millimeter = kg/m2
|
||||
@ -4388,7 +4410,10 @@ sub __openMeteoDWD_ApiResponse {
|
||||
if ($k == 0 && $curwid) { $curwid = ($don ? 0 : 100) + $curwid }
|
||||
|
||||
if ($debug =~ /apiProcess/xs) {
|
||||
if ($requestmode eq 'MODEL') {
|
||||
Log3 ($name, 1, "$name DEBUG> Open-Meteo DWD ICON API $pvtmstr - Rad1Wh: $rad1wh, Rad1kJ: $rad, PV est: $pv Wh");
|
||||
}
|
||||
|
||||
Log3 ($name, 1, "$name DEBUG> Open-Meteo DWD ICON API $pvtmstr - RR1c: $rain");
|
||||
Log3 ($name, 1, "$name DEBUG> Open-Meteo DWD ICON API $otmstr - DoN: $don");
|
||||
Log3 ($name, 1, "$name DEBUG> Open-Meteo DWD ICON API $otmstr - Temp: $temp");
|
||||
@ -4402,29 +4427,24 @@ sub __openMeteoDWD_ApiResponse {
|
||||
}
|
||||
}
|
||||
|
||||
if ($requestmode eq 'MODEL') {
|
||||
$data{$type}{$name}{solcastapi}{$string}{$pvtmstr}{pv_estimate50} = $pv; # Startstunde verschieben
|
||||
|
||||
my $fwtg = formatWeatherTimestrg ($pvtmstr);
|
||||
|
||||
if ($paref->{begin}) { # im ersten Call den DS löschen -> dann Aufsummierung
|
||||
delete $data{$type}{$name}{solcastapi}{'?All'}{$pvtmstr}{Rad1h};
|
||||
}
|
||||
|
||||
$data{$type}{$name}{solcastapi}{'?All'}{$pvtmstr}{Rad1h} += $rad; # Startstunde verschieben, Rad Werte aller Strings addieren
|
||||
}
|
||||
|
||||
## Wetterdaten
|
||||
################
|
||||
$data{$type}{$name}{solcastapi}{'?All'}{$fwtg}{rr1c} = $rain; # Startstunde verschieben
|
||||
my $fwtg = formatWeatherTimestrg ($pvtmstr); # Zeit gemäß DWD_OpenData-Format
|
||||
|
||||
$data{$type}{$name}{weatherapi}{OpenMeteo}{$fwtg}{rr1c} = $rain;
|
||||
$data{$type}{$name}{weatherapi}{OpenMeteo}{$fwtg}{StartTime} = $pvtmstr;
|
||||
|
||||
$fwtg = formatWeatherTimestrg ($otmstr);
|
||||
|
||||
$data{$type}{$name}{solcastapi}{'?All'}{$fwtg}{don} = $don;
|
||||
$data{$type}{$name}{solcastapi}{'?All'}{$fwtg}{neff} = $wcc;
|
||||
$data{$type}{$name}{solcastapi}{'?All'}{$fwtg}{ww} = $wid;
|
||||
$data{$type}{$name}{solcastapi}{'?All'}{$fwtg}{ttt} = $temp;
|
||||
$data{$type}{$name}{solcastapi}{'?All'}{$fwtg}{UpdateTime} = $rt;
|
||||
$fwtg = formatWeatherTimestrg ($otmstr); # Zeit gemäß DWD_OpenData-Format
|
||||
|
||||
$data{$type}{$name}{weatherapi}{OpenMeteo}{$fwtg}{don} = $don;
|
||||
$data{$type}{$name}{weatherapi}{OpenMeteo}{$fwtg}{neff} = $wcc;
|
||||
@ -4434,10 +4454,6 @@ sub __openMeteoDWD_ApiResponse {
|
||||
$data{$type}{$name}{weatherapi}{OpenMeteo}{$fwtg}{StartTime} = $otmstr;
|
||||
|
||||
if ($k == 0) {
|
||||
$data{$type}{$name}{solcastapi}{'?All'}{$fwtg}{neff} = $curwcc if(defined $curwcc);
|
||||
$data{$type}{$name}{solcastapi}{'?All'}{$fwtg}{ww} = $curwid if(defined $curwid);
|
||||
$data{$type}{$name}{solcastapi}{'?All'}{$fwtg}{ttt} = $curtmp if(defined $curtmp);
|
||||
|
||||
$data{$type}{$name}{weatherapi}{OpenMeteo}{$fwtg}{neff} = $curwcc if(defined $curwcc);
|
||||
$data{$type}{$name}{weatherapi}{OpenMeteo}{$fwtg}{ww} = $curwid if(defined $curwid);
|
||||
$data{$type}{$name}{weatherapi}{OpenMeteo}{$fwtg}{ttt} = $curtmp if(defined $curtmp);
|
||||
@ -4464,9 +4480,6 @@ sub __openMeteoDWD_ApiResponse {
|
||||
}
|
||||
|
||||
if ($k == 0) {
|
||||
$data{$type}{$name}{solcastapi}{'?All'}{sunrise}{today} = $sunrise;
|
||||
$data{$type}{$name}{solcastapi}{'?All'}{sunset}{today} = $sunset;
|
||||
|
||||
$data{$type}{$name}{weatherapi}{OpenMeteo}{sunrise}{today} = $sunrise;
|
||||
$data{$type}{$name}{weatherapi}{OpenMeteo}{sunset}{today} = $sunset;
|
||||
|
||||
@ -4477,9 +4490,6 @@ sub __openMeteoDWD_ApiResponse {
|
||||
}
|
||||
|
||||
if ($k == 1) {
|
||||
$data{$type}{$name}{solcastapi}{'?All'}{sunrise}{tomorrow} = $sunrise;
|
||||
$data{$type}{$name}{solcastapi}{'?All'}{sunset}{tomorrow} = $sunset;
|
||||
|
||||
$data{$type}{$name}{weatherapi}{OpenMeteo}{sunrise}{tomorrow} = $sunrise;
|
||||
$data{$type}{$name}{weatherapi}{OpenMeteo}{sunset}{tomorrow} = $sunset;
|
||||
|
||||
@ -4504,6 +4514,7 @@ sub __openMeteoDWD_ApiResponse {
|
||||
allstrings => $allstrings,
|
||||
submodel => $submodel,
|
||||
callequivalent => $paref->{callequivalent},
|
||||
requestmode => $requestmode,
|
||||
lang => $lang
|
||||
};
|
||||
|
||||
@ -4730,14 +4741,14 @@ return $ret;
|
||||
}
|
||||
|
||||
###############################################################
|
||||
# Getter solApiData
|
||||
# Getter radiationApiData
|
||||
###############################################################
|
||||
sub _getlistSolCastData {
|
||||
sub _getlistRadiationApiData {
|
||||
my $paref = shift;
|
||||
my $name = $paref->{name};
|
||||
my $hash = $defs{$name};
|
||||
|
||||
my $ret = listDataPool ($hash, 'solApiData');
|
||||
my $ret = listDataPool ($hash, 'radiationApiData');
|
||||
$ret .= lineFromSpaces ($ret, 10);
|
||||
|
||||
return $ret;
|
||||
@ -5425,16 +5436,6 @@ sub Attr {
|
||||
|
||||
### nicht mehr benötigte Daten verarbeiten - Bereich kann später wieder raus !!
|
||||
######################################################################################################################
|
||||
# 20.10.2024
|
||||
if ($cmd eq 'set' && $aName =~ /^flowGraphicCss|flowGraphicSize|flowGraphicAnimate|flowGraphicConsumerDistance|flowGraphicShowConsumer|flowGraphicShowConsumerDummy|flowGraphicShowConsumerPower|flowGraphicShowConsumerRemainTime|flowGraphicShift$/) {
|
||||
if (!$init_done) {
|
||||
# return qq{Device "$name" -> The attribute '$aName' is replaced by 'flowGraphicControl'. Please press "save config" when restart is finished.};
|
||||
Log3 ($name, 1, qq{$name - The attribute '$aName' is replaced by 'flowGraphicControl'. Please press "save config" when restart is finished.});
|
||||
}
|
||||
else {
|
||||
return qq{The attribute '$aName' is obsolete and replaced by 'flowGraphicControl'.};
|
||||
}
|
||||
}
|
||||
# 31.10.2024
|
||||
if ($cmd eq 'set' && $aName =~ /^graphicStartHtml|affect70percentRule|graphicEndHtml|ctrlAutoRefresh|ctrlAutoRefreshFW$/) {
|
||||
if (!$init_done) {
|
||||
@ -6197,19 +6198,26 @@ sub _attrWeatherDev { ## no critic "not used"
|
||||
|
||||
if ($aVal =~ /^OpenMeteo/xs) {
|
||||
if ($aName ne 'setupWeatherDev1') {
|
||||
return qq{Only the leading attribute 'setupWeatherDev1' can set to '$aVal'};
|
||||
return qq{Only the leading weather attribute 'setupWeatherDev1' can set to '$aVal'};
|
||||
}
|
||||
|
||||
InternalTimer (gettimeofday()+1, 'FHEM::SolarForecast::__setRadAPIdelayed', $hash, 0); # automatisch setupRadiationAPI setzen wenn setupWeatherDev1
|
||||
return;
|
||||
my @istrings = split ",", AttrVal ($name, 'setupInverterStrings', ''); # Stringbezeichner
|
||||
|
||||
if ((!ReadingsVal ($name, 'setupStringAzimuth', '') || !ReadingsVal ($name, 'setupStringDeclination', '')) &&
|
||||
!grep /KI-based/, @istrings) {
|
||||
return qq{Execute 'set $name setupStringAzimuth' and/or 'set $name setupStringDeclination' first.};
|
||||
}
|
||||
}
|
||||
|
||||
if ($aVal !~ /-API$/xs) { # Attribute des DWD-Devices prüfen
|
||||
my $err = checkdwdattr ($name, $aVal, \@dweattrmust);
|
||||
return $err if($err);
|
||||
}
|
||||
}
|
||||
|
||||
InternalTimer (gettimeofday() + 1, 'FHEM::SolarForecast::setModel', $hash, 0);
|
||||
InternalTimer (gettimeofday() + 2, 'FHEM::SolarForecast::createAssociatedWith', $hash, 0);
|
||||
InternalTimer (gettimeofday() + 1, 'FHEM::SolarForecast::__harmonizeAPIdelayed', $hash, 0);
|
||||
InternalTimer (gettimeofday() + 2, 'FHEM::SolarForecast::setModel', $hash, 0);
|
||||
InternalTimer (gettimeofday() + 3, 'FHEM::SolarForecast::createAssociatedWith', $hash, 0);
|
||||
|
||||
return;
|
||||
}
|
||||
@ -6233,14 +6241,14 @@ sub _attrRadiationAPI { ## no critic "not used"
|
||||
return qq{The device "$aVal" doesn't exist or has no TYPE "DWD_OpenData"};
|
||||
}
|
||||
|
||||
my $awdev1 = AttrVal ($name, 'setupWeatherDev1', '');
|
||||
#my $awdev1 = AttrVal ($name, 'setupWeatherDev1', '');
|
||||
|
||||
if (($awdev1 eq 'OpenMeteoDWD-API' && $aVal ne 'OpenMeteoDWD-API') ||
|
||||
($awdev1 eq 'OpenMeteoDWDEnsemble-API' && $aVal ne 'OpenMeteoDWDEnsemble-API') ||
|
||||
($awdev1 eq 'OpenMeteoWorld-API' && $aVal ne 'OpenMeteoWorld-API')) {
|
||||
return "The attribute 'setupWeatherDev1' is set to '$awdev1'. \n".
|
||||
"Change that attribute to another weather device first if you want use an other API.";
|
||||
}
|
||||
#if (($awdev1 eq 'OpenMeteoDWD-API' && $aVal ne 'OpenMeteoDWD-API') ||
|
||||
# ($awdev1 eq 'OpenMeteoDWDEnsemble-API' && $aVal ne 'OpenMeteoDWDEnsemble-API') ||
|
||||
# ($awdev1 eq 'OpenMeteoWorld-API' && $aVal ne 'OpenMeteoWorld-API')) {
|
||||
# return "The attribute 'setupWeatherDev1' is set to '$awdev1'. \n".
|
||||
# "Change that attribute to another weather device first if you want use an other API.";
|
||||
#}
|
||||
|
||||
if ($aVal =~ /(SolCast|OpenMeteoDWD|OpenMeteoDWDEnsemble|OpenMeteoWorld)-API/xs) {
|
||||
return "The library FHEM::Utility::CTZ is missing. Please update FHEM completely." if($ctzAbsent);
|
||||
@ -6267,9 +6275,10 @@ sub _attrRadiationAPI { ## no critic "not used"
|
||||
|
||||
readingsDelete ($hash, 'nextRadiationAPICall');
|
||||
|
||||
InternalTimer (gettimeofday() + 1, 'FHEM::SolarForecast::setModel', $hash, 0); # Model setzen
|
||||
InternalTimer (gettimeofday() + 2, 'FHEM::SolarForecast::createAssociatedWith', $hash, 0);
|
||||
InternalTimer (gettimeofday() + 3, 'FHEM::SolarForecast::writeCacheToFile', [$name, 'plantconfig', $plantcfg.$name], 0); # Anlagenkonfiguration File schreiben
|
||||
InternalTimer (gettimeofday() + 1, 'FHEM::SolarForecast::__harmonizeAPIdelayed', $hash, 0);
|
||||
InternalTimer (gettimeofday() + 2, 'FHEM::SolarForecast::setModel', $hash, 0); # Model setzen
|
||||
InternalTimer (gettimeofday() + 3, 'FHEM::SolarForecast::createAssociatedWith', $hash, 0);
|
||||
InternalTimer (gettimeofday() + 4, 'FHEM::SolarForecast::writeCacheToFile', [$name, 'plantconfig', $plantcfg.$name], 0); # Anlagenkonfiguration File schreiben
|
||||
|
||||
return;
|
||||
}
|
||||
@ -6302,15 +6311,22 @@ return;
|
||||
}
|
||||
|
||||
################################################################
|
||||
# setupRadiationAPI verzögert aus Attr setzen
|
||||
# Attr setupRadiationAPI und setupWeatherDev1
|
||||
# harmonisieren wenn erforderlich
|
||||
# setupRadiationAPI ist führend
|
||||
################################################################
|
||||
sub __setRadAPIdelayed {
|
||||
sub __harmonizeAPIdelayed {
|
||||
my $hash = shift;
|
||||
|
||||
my $name = $hash->{NAME};
|
||||
my $awdev1 = AttrVal ($name, 'setupWeatherDev1', '');
|
||||
my $wedev1 = AttrVal ($name, 'setupWeatherDev1', '');
|
||||
my $radapi = AttrVal ($name, 'setupRadiationAPI', '');
|
||||
|
||||
CommandAttr (undef, "$name setupRadiationAPI $awdev1"); # automatisch setupRadiationAPI setzen
|
||||
return if($wedev1 eq $radapi);
|
||||
|
||||
if ($radapi =~ /OpenMeteo/xs && $wedev1 =~ /OpenMeteo/xs) { # auf OpenMeteo Datenmodell harmonisieren
|
||||
CommandAttr (undef, "$name setupWeatherDev1 $radapi");
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
@ -7083,35 +7099,6 @@ sub centralTask {
|
||||
|
||||
### nicht mehr benötigte Daten verarbeiten - Bereich kann später wieder raus !!
|
||||
##########################################################################################################################
|
||||
for my $n (1..24) { # 08.10.2024
|
||||
$n = sprintf "%02d", $n;
|
||||
readingsDelete ($hash, "pvCorrectionFactor_${n}_autocalc");
|
||||
}
|
||||
|
||||
my $fg1 = AttrVal ($name, 'flowGraphicSize', undef); # 20.10.2024
|
||||
my $fg2 = AttrVal ($name, 'flowGraphicAnimate', undef);
|
||||
my $fg3 = AttrVal ($name, 'flowGraphicConsumerDistance', undef);
|
||||
my $fg4 = AttrVal ($name, 'flowGraphicShowConsumer', undef);
|
||||
my $fg5 = AttrVal ($name, 'flowGraphicShowConsumerDummy', undef);
|
||||
my $fg6 = AttrVal ($name, 'flowGraphicShowConsumerPower', undef);
|
||||
my $fg7 = AttrVal ($name, 'flowGraphicShowConsumerRemainTime', undef);
|
||||
my $fg8 = AttrVal ($name, 'flowGraphicShift', undef);
|
||||
|
||||
my $newval;
|
||||
$newval .= "size=$fg1 " if(defined $fg1);
|
||||
$newval .= "animate=$fg2 " if(defined $fg2);
|
||||
$newval .= "consumerdist=$fg3 " if(defined $fg3);
|
||||
$newval .= "showconsumer=$fg4 " if(defined $fg4);
|
||||
$newval .= "showconsumerdummy=$fg5 " if(defined $fg5);
|
||||
$newval .= "showconsumerpower=$fg6 " if(defined $fg6);
|
||||
$newval .= "showconsumerremaintime=$fg7 " if(defined $fg7);
|
||||
$newval .= "shift=$fg8 " if(defined $fg8);
|
||||
|
||||
if ($newval) {
|
||||
CommandAttr (undef, "$name flowGraphicControl $newval");
|
||||
::CommandDeleteAttr (undef, "$name flowGraphicSize|flowGraphicAnimate|flowGraphicConsumerDistance|flowGraphicShowConsumer|flowGraphicShowConsumerDummy|flowGraphicShowConsumerPower|flowGraphicShowConsumerRemainTime|flowGraphicShift");
|
||||
}
|
||||
|
||||
if (exists $data{$type}{$name}{solcastapi}{'?IdPair'}) { # 29.11.2024
|
||||
for my $pk (keys %{$data{$type}{$name}{solcastapi}{'?IdPair'}}) {
|
||||
my $apikey = SolCastAPIVal ($hash, '?IdPair', $pk, 'apikey', '');
|
||||
@ -7132,7 +7119,7 @@ sub centralTask {
|
||||
for my $key (keys %{$data{$type}{$name}{solcastapi}{'?All'}{'?All'}}) {
|
||||
my $val = SolCastAPIVal ($hash, '?All', '?All', $key, '');
|
||||
|
||||
if ($val) {
|
||||
if ($rapi && $val) {
|
||||
$data{$type}{$name}{statusapi}{$rapi}{'?All'}{$key} = $val;
|
||||
delete $data{$type}{$name}{solcastapi}{'?All'}{'?All'}{$key};
|
||||
}
|
||||
@ -7156,7 +7143,6 @@ sub centralTask {
|
||||
$data{$type}{$name}{statusapi}{'?VRM'}{'?API'}{credentials} = $vrmcr;
|
||||
delete $data{$type}{$name}{solcastapi}{'?VRM'};
|
||||
}
|
||||
|
||||
##########################################################################################################################
|
||||
|
||||
my (undef, $disabled, $inactive) = controller ($name);
|
||||
@ -7225,7 +7211,7 @@ sub centralTask {
|
||||
|
||||
readingsDelete ($hash, 'AllPVforecastsToEvent');
|
||||
|
||||
_getRoofTopData ($centpars); # Strahlungswerte/Forecast-Werte in solcastapi-Hash erstellen
|
||||
_getRoofTopData ($centpars); # Strahlungs/Wetter-Daten der gewählten API's abrufen und in internen Strukturen speichern
|
||||
_transferInverterValues ($centpars); # WR Werte übertragen
|
||||
_transferAPIRadiationValues ($centpars); # Raw Erzeugungswerte aus solcastapi-Hash übertragen und Forecast mit/ohne Korrektur erstellen
|
||||
_calcMaxEstimateToday ($centpars); # heutigen Max PV Estimate & dessen Tageszeit ermitteln
|
||||
@ -7320,9 +7306,10 @@ sub createStringConfig { ## no critic "not used"
|
||||
return qq{You use a KI based model. Please set only "KI-based" as String with command "attr $name setupInverterStrings".};
|
||||
}
|
||||
}
|
||||
elsif (!isVictronKiUsed ($hash)) {
|
||||
|
||||
if (!grep /^KI-based$/, @istrings) {
|
||||
my $tilt = ReadingsVal ($name, 'setupStringDeclination', ''); # Modul Neigungswinkel für jeden Stringbezeichner
|
||||
return qq{Please complete command "set $name setupStringDeclination".} if(!$tilt);
|
||||
return qq{Please complete command "set $name setupStringDeclination"} if(!$tilt);
|
||||
|
||||
my ($at,$ht) = parseParams ($tilt);
|
||||
|
||||
@ -7336,7 +7323,7 @@ sub createStringConfig { ## no critic "not used"
|
||||
}
|
||||
|
||||
my $dir = ReadingsVal ($name, 'setupStringAzimuth', ''); # Modul Ausrichtung für jeden Stringbezeichner
|
||||
return qq{Please complete command "set $name setupStringAzimuth".} if(!$dir);
|
||||
return qq{Please complete command "set $name setupStringAzimuth"} if(!$dir);
|
||||
|
||||
my ($ad,$hd) = parseParams ($dir);
|
||||
my $iwrong = qq{Please check the input of set "setupStringAzimuth". It seems to be wrong.};
|
||||
@ -7934,29 +7921,24 @@ sub __delObsoleteAPIData {
|
||||
my $date = $paref->{date}; # aktuelles Datum
|
||||
my $hash = $defs{$name};
|
||||
|
||||
my ($rapi, $wapi) = getStatusApiName ($hash);
|
||||
|
||||
## Wetter-API Daten löschen
|
||||
#############################
|
||||
if (keys %{$data{$type}{$name}{weatherapi}}) {
|
||||
if (isWeatherOpenMeteoUsed ($hash)) {
|
||||
for my $idx (keys %{$data{$type}{$name}{weatherapi}{OpenMeteo}}) {
|
||||
delete $data{$type}{$name}{weatherapi}{OpenMeteo}{$idx} if($idx =~ /^fc?([0-9]{1,2})_?([0-9]{1,2})$/xs);
|
||||
}
|
||||
}
|
||||
else {
|
||||
delete $data{$type}{$name}{weatherapi}{OpenMeteo};
|
||||
}
|
||||
delete $data{$type}{$name}{weatherapi}{OpenMeteo} if($wapi ne 'OpenMeteo');
|
||||
}
|
||||
|
||||
## Status-API Daten löschen
|
||||
#############################
|
||||
if (keys %{$data{$type}{$name}{statusapi}}) {
|
||||
delete $data{$type}{$name}{statusapi}{OpenMeteo} if(!isOpenMeteoUsed ($hash) && !isWeatherOpenMeteoUsed ($hash));
|
||||
delete $data{$type}{$name}{statusapi}{ForecastSolar} if(!isForecastSolarUsed ($hash));
|
||||
delete $data{$type}{$name}{statusapi}{SolCast} if(!isSolCastUsed ($hash));
|
||||
delete $data{$type}{$name}{statusapi}{'?IdPair'} if(!isSolCastUsed ($hash));
|
||||
delete $data{$type}{$name}{statusapi}{DWD} if(!isDWDUsed ($hash));
|
||||
delete $data{$type}{$name}{statusapi}{VictronKi} if(!isVictronKiUsed ($hash));
|
||||
delete $data{$type}{$name}{statusapi}{'?VRM'} if(!isVictronKiUsed ($hash));
|
||||
delete $data{$type}{$name}{statusapi}{OpenMeteo} if($rapi ne 'OpenMeteo' && $wapi ne 'OpenMeteo');
|
||||
delete $data{$type}{$name}{statusapi}{ForecastSolar} if($rapi ne 'ForecastSolar');
|
||||
delete $data{$type}{$name}{statusapi}{SolCast} if($rapi ne 'SolCast');
|
||||
delete $data{$type}{$name}{statusapi}{'?IdPair'} if($rapi ne 'SolCast');
|
||||
delete $data{$type}{$name}{statusapi}{DWD} if($rapi ne 'DWD');
|
||||
delete $data{$type}{$name}{statusapi}{VictronKi} if($rapi ne 'VictronKi');
|
||||
delete $data{$type}{$name}{statusapi}{'?VRM'} if($rapi ne 'VictronKi');
|
||||
}
|
||||
|
||||
## Solar-API Daten löschen
|
||||
@ -12020,6 +12002,7 @@ return ($fd, $fh);
|
||||
}
|
||||
|
||||
################################################################
|
||||
# Zeit gemäß DWD_OpenData-Format
|
||||
# Berechnen Tag / Stunden Verschieber ab aktuellen Tag
|
||||
# Input: YYYY-MM-DD HH:MM:SS
|
||||
# Output: $fd - 0 (Heute), 1 (Morgen), 2 (Übermorgen), ....
|
||||
@ -12660,10 +12643,7 @@ sub _checkSetupNotComplete {
|
||||
$ret .= "<td>";
|
||||
$ret .= $hqtxt{entry}{$lang}; # Entry Text
|
||||
|
||||
if (!$wedev) { ## no critic 'Cascading'
|
||||
$ret .= $hqtxt{cfd}{$lang};
|
||||
}
|
||||
elsif (!$strings) {
|
||||
if (!$strings) { ## no critic 'Cascading'
|
||||
$ret .= $hqtxt{ist}{$lang};
|
||||
}
|
||||
elsif (!$peaks) {
|
||||
@ -12690,6 +12670,9 @@ sub _checkSetupNotComplete {
|
||||
elsif (!$mdec && !isSolCastUsed ($hash) && !isVictronKiUsed ($hash)) {
|
||||
$ret .= $hqtxt{mta}{$lang};
|
||||
}
|
||||
elsif (!$wedev) {
|
||||
$ret .= $hqtxt{cfd}{$lang};
|
||||
}
|
||||
elsif (!$vrmcr && isVictronKiUsed ($hash)) {
|
||||
$ret .= $hqtxt{vrmcr}{$lang};
|
||||
}
|
||||
@ -16820,7 +16803,7 @@ sub listDataPool {
|
||||
return $ret;
|
||||
};
|
||||
|
||||
if ($htol =~ /solApiData|weatherApiData|statusApiData/xs) {
|
||||
if ($htol =~ /radiationApiData|weatherApiData|statusApiData/xs) {
|
||||
$h = $data{$type}{$name}{solcastapi};
|
||||
$h = $data{$type}{$name}{weatherapi} if($htol eq 'weatherApiData');
|
||||
$h = $data{$type}{$name}{statusapi} if($htol eq 'statusApiData');
|
||||
@ -17028,7 +17011,7 @@ sub checkPlantConfig {
|
||||
|
||||
my $result = { # Ergebnishash
|
||||
'String Configuration' => { 'state' => $ok, 'result' => '', 'note' => '', 'info' => 0, 'warn' => 0, 'fault' => 0 },
|
||||
'DWD Weather Properties' => { 'state' => $ok, 'result' => '', 'note' => '', 'info' => 0, 'warn' => 0, 'fault' => 0 },
|
||||
'Weather Properties' => { 'state' => $ok, 'result' => '', 'note' => '', 'info' => 0, 'warn' => 0, 'fault' => 0 },
|
||||
'Common Settings' => { 'state' => $ok, 'result' => '', 'note' => '', 'info' => 0, 'warn' => 0, 'fault' => 0 },
|
||||
'FTUI Widget Files' => { 'state' => $ok, 'result' => '', 'note' => '', 'info' => 0, 'warn' => 0, 'fault' => 0 },
|
||||
};
|
||||
@ -17099,44 +17082,44 @@ sub checkPlantConfig {
|
||||
next if(!$fcname && $step ne 1);
|
||||
|
||||
if (!$valid) {
|
||||
$result->{'DWD Weather Properties'}{state} = $nok;
|
||||
$result->{'Weather Properties'}{state} = $nok;
|
||||
|
||||
if (!$fcname) {
|
||||
$result->{'DWD Weather Properties'}{result} .= qq{No DWD device is defined in attribute "setupWeatherDev$step". <br>};
|
||||
$result->{'Weather Properties'}{result} .= qq{No DWD device is defined in attribute "setupWeatherDev$step". <br>};
|
||||
}
|
||||
else {
|
||||
$result->{'DWD Weather Properties'}{result} .= qq{The DWD device "$fcname" doesn't exist. <br>};
|
||||
$result->{'Weather Properties'}{result} .= qq{The DWD device "$fcname" doesn't exist. <br>};
|
||||
}
|
||||
|
||||
$result->{'DWD Weather Properties'}{fault} = 1;
|
||||
$result->{'Weather Properties'}{fault} = 1;
|
||||
}
|
||||
else {
|
||||
if (!$apiu) {
|
||||
$err = checkdwdattr ($name, $fcname, \@dweattrmust);
|
||||
|
||||
if ($err) {
|
||||
$result->{'DWD Weather Properties'}{state} = $nok;
|
||||
$result->{'DWD Weather Properties'}{result} .= $err.'<br>';
|
||||
$result->{'DWD Weather Properties'}{fault} = 1;
|
||||
$result->{'Weather Properties'}{state} = $nok;
|
||||
$result->{'Weather Properties'}{result} .= $err.'<br>';
|
||||
$result->{'Weather Properties'}{fault} = 1;
|
||||
}
|
||||
else {
|
||||
$mosm = AttrVal ($fcname, 'forecastRefresh', 6) == 6 ? 'MOSMIX_L' : 'MOSMIX_S';
|
||||
|
||||
if ($mosm eq 'MOSMIX_L') {
|
||||
$result->{'DWD Weather Properties'}{state} = $info;
|
||||
$result->{'DWD Weather Properties'}{result} .= qq(The device "$fcname" uses "$mosm" which is only updated by DWD every 6 hours. <br>);
|
||||
$result->{'DWD Weather Properties'}{info} = 1;
|
||||
$result->{'Weather Properties'}{state} = $info;
|
||||
$result->{'Weather Properties'}{result} .= qq(The device "$fcname" uses "$mosm" which is only updated by DWD every 6 hours. <br>);
|
||||
$result->{'Weather Properties'}{info} = 1;
|
||||
}
|
||||
|
||||
$result->{'DWD Weather Properties'}{result} .= $hqtxt{fulfd}{$lang}." ($hqtxt{attrib}{$lang}: setupWeatherDev$step)<br>";
|
||||
$result->{'Weather Properties'}{result} .= $hqtxt{fulfd}{$lang}." ($hqtxt{attrib}{$lang}: setupWeatherDev$step)<br>";
|
||||
}
|
||||
|
||||
$result->{'DWD Weather Properties'}{note} .= qq{checked parameters and attributes of device "$fcname": <br>};
|
||||
$result->{'DWD Weather Properties'}{note} .= 'forecastProperties -> '.join (',', @dweattrmust).'<br>';
|
||||
$result->{'DWD Weather Properties'}{note} .= 'forecastRefresh '.($mosm eq 'MOSMIX_L' ? '-> set attribute to below "6" if possible' : '').'<br>';
|
||||
$result->{'Weather Properties'}{note} .= qq{checked parameters and attributes of device "$fcname": <br>};
|
||||
$result->{'Weather Properties'}{note} .= 'forecastProperties -> '.join (',', @dweattrmust).'<br>';
|
||||
$result->{'Weather Properties'}{note} .= 'forecastRefresh '.($mosm eq 'MOSMIX_L' ? '-> set attribute to below "6" if possible' : '').'<br>';
|
||||
}
|
||||
else {
|
||||
$result->{'DWD Weather Properties'}{result} .= $hqtxt{fulfd}{$lang}." ($hqtxt{attrib}{$lang}: setupWeatherDev$step)<br>";
|
||||
$result->{'Weather Properties'}{result} .= $hqtxt{fulfd}{$lang}." ($hqtxt{attrib}{$lang}: setupWeatherDev$step)<br>";
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -17146,16 +17129,16 @@ sub checkPlantConfig {
|
||||
($err, $resh) = isWeatherAgeExceeded ( {name => $name, lang => $lang} );
|
||||
|
||||
if (!$err && $resh->{exceed}) {
|
||||
$result->{'DWD Weather Properties'}{state} = $warn;
|
||||
$result->{'DWD Weather Properties'}{note} .= qq{The Prediction time of Weather data is older than expected when using $resh->{mosmix}. <br>};
|
||||
$result->{'DWD Weather Properties'}{note} .= qq{Data time forecast: $resh->{fctime} <br>};
|
||||
$result->{'DWD Weather Properties'}{note} .= qq{Check the DWD device(s) for proper functioning of the data retrieval. <br>};
|
||||
$result->{'DWD Weather Properties'}{warn} = 1;
|
||||
$result->{'Weather Properties'}{state} = $warn;
|
||||
$result->{'Weather Properties'}{note} .= qq{The Prediction time of Weather data is older than expected when using $resh->{mosmix}. <br>};
|
||||
$result->{'Weather Properties'}{note} .= qq{Data time forecast: $resh->{fctime} <br>};
|
||||
$result->{'Weather Properties'}{note} .= qq{Check the DWD device(s) for proper functioning of the data retrieval. <br>};
|
||||
$result->{'Weather Properties'}{warn} = 1;
|
||||
}
|
||||
|
||||
$result->{'DWD Weather Properties'}{note} .= '<br>';
|
||||
$result->{'DWD Weather Properties'}{note} .= qq{checked global Weather parameters: <br>};
|
||||
$result->{'DWD Weather Properties'}{note} .= 'MOSMIX variant or ICON Forecast Model, Age of Weather data. <br>';
|
||||
$result->{'Weather Properties'}{note} .= '<br>';
|
||||
$result->{'Weather Properties'}{note} .= qq{checked global Weather parameters: <br>};
|
||||
$result->{'Weather Properties'}{note} .= 'MOSMIX variant or ICON Forecast Model, Age of Weather data. <br>';
|
||||
|
||||
## Check DWD Radiation Device
|
||||
###############################
|
||||
@ -17969,7 +17952,9 @@ sub setModel {
|
||||
elsif ($radapi =~ /OpenMeteoWorld-/xs) { $hash->{MODEL} = 'OpenMeteoWorldAPI'; }
|
||||
else { $hash->{MODEL} = 'DWD'; }
|
||||
|
||||
if ($wthapi =~ /OpenMeteo.*-API/xs) { $hash->{WEATHERMODEL} = 'OpenMeteo'; }
|
||||
if ($wthapi =~ /OpenMeteoDWDEnsemble-/xs) { $hash->{WEATHERMODEL} = 'OpenMeteoDWDEnsembleAPI'; }
|
||||
elsif ($wthapi =~ /OpenMeteoDWD-/xs) { $hash->{WEATHERMODEL} = 'OpenMeteoDWDAPI'; }
|
||||
elsif ($wthapi =~ /OpenMeteoWorld-/xs) { $hash->{WEATHERMODEL} = 'OpenMeteoWorldAPI'; }
|
||||
else { $hash->{WEATHERMODEL} = 'DWD'; }
|
||||
|
||||
return;
|
||||
@ -18654,9 +18639,11 @@ sub isWeatherDevValid {
|
||||
if ($fcname) { $valid = 1 }
|
||||
if (!$defs{$fcname} || $defs{$fcname}{TYPE} ne "DWD_OpenData") { $valid = '' }
|
||||
|
||||
if (isOpenMeteoUsed($hash) && $fcname =~ /^OpenMeteo/xs) {
|
||||
my ($rapi, $wapi) = getStatusApiName ($hash); # $rapi - Radiation-API, $wapi - Weather-API
|
||||
|
||||
if ($wapi =~ /^OpenMeteo/xs) {
|
||||
$valid = 1;
|
||||
$apiu = $fcname;
|
||||
$apiu = $wapi;
|
||||
}
|
||||
|
||||
return ($valid, $fcname, $apiu);
|
||||
@ -18992,6 +18979,9 @@ return ($err, $cname, $dswname);
|
||||
################################################################
|
||||
# Namen der Strahlungs-API und Wetter-API ermitteln.
|
||||
# Wird als Schlüssel in statusapi bzw. weatherapi verwendet.
|
||||
# Return:
|
||||
# $rapi - Name der Strahlungsdaten-API
|
||||
# $wapi - Name der Wetter-API
|
||||
################################################################
|
||||
sub getStatusApiName {
|
||||
my $hash = shift;
|
||||
@ -20166,8 +20156,7 @@ to ensure that the system configuration is correct.
|
||||
|
||||
<ul>
|
||||
<a id="SolarForecast-set-setupStringAzimuth"></a>
|
||||
<li><b>setupStringAzimuth <Stringname1>=<dir> [<Stringname2>=<dir> <Stringname3>=<dir> ...] </b> <br>
|
||||
(only model DWD, OpenMeteo*, ForecastSolarAPI) <br><br>
|
||||
<li><b>setupStringAzimuth <Stringname1>=<dir> [<Stringname2>=<dir> <Stringname3>=<dir> ...] </b> <br><br>
|
||||
|
||||
Alignment <dir> of the solar modules in the string "StringnameX". The string name is a key value of the
|
||||
<b>setupInverterStrings</b> attribute. <br>
|
||||
@ -20204,8 +20193,7 @@ to ensure that the system configuration is correct.
|
||||
|
||||
<ul>
|
||||
<a id="SolarForecast-set-setupStringDeclination"></a>
|
||||
<li><b>setupStringDeclination <Stringname1>=<Angle> [<Stringname2>=<Angle> <Stringname3>=<Angle> ...] </b> <br>
|
||||
(only model DWD, ForecastSolarAPI) <br><br>
|
||||
<li><b>setupStringDeclination <Stringname1>=<Angle> [<Stringname2>=<Angle> <Stringname3>=<Angle> ...] </b> <br><br>
|
||||
|
||||
Tilt angle of the solar modules. The string name is a key value of the attribute <b>setupInverterStrings</b>. <br>
|
||||
Possible angles of inclination are: 0,5,10,15,20,25,30,35,40,45,50,55,60,65,70,75,80,85,90
|
||||
@ -20784,8 +20772,8 @@ to ensure that the system configuration is correct.
|
||||
<br>
|
||||
|
||||
<ul>
|
||||
<a id="SolarForecast-get-solApiData"></a>
|
||||
<li><b>solApiData </b> <br><br>
|
||||
<a id="SolarForecast-get-radiationApiData"></a>
|
||||
<li><b>radiationApiData </b> <br><br>
|
||||
|
||||
Lists the radiation data saved in the context of the API call.
|
||||
The forecast data supplied by the API regarding the global radiation Rad1h and the predicted PV yield (Wh)
|
||||
@ -22122,9 +22110,10 @@ to ensure that the system configuration is correct.
|
||||
<li><b>setupRadiationAPI </b> <br><br>
|
||||
|
||||
Defines the source for the delivery of the solar radiation data. You can select a device of the type DWD_OpenData or
|
||||
an implemented API can be selected. <br>
|
||||
<b>Note:</b> If OpenMeteoDWD-API is set in the 'setupWeatherDev1' attribute, no radiation data service other than
|
||||
OpenMeteoDWD-API can be selected. <br><br>
|
||||
an implemented API can be selected. <br><br>
|
||||
|
||||
<b>Note:</b> If an OpenMeteo API is also set in the 'setupWeatherDev1' attribute, the settings of both attributes
|
||||
are harmonized, whereby the setting of 'setupRadiationAPI' is leading. <br><br>
|
||||
|
||||
<b>OpenMeteoDWD-API</b> <br>
|
||||
|
||||
@ -22258,9 +22247,10 @@ to ensure that the system configuration is correct.
|
||||
<li><b>setupWeatherDevX </b> <br><br>
|
||||
|
||||
Specifies the device or API for providing the required weather data (cloud cover, precipitation, etc.).<br>
|
||||
The attribute 'setupWeatherDev1' specifies the leading weather service and is mandatory.<br>
|
||||
If an Open-Meteo API is selected in the 'setupWeatherDev1' attribute, this Open-Meteo service is automatically set as the
|
||||
source of the radiation data (Attribute setupRadiationAPI). <br><br>
|
||||
The attribute 'setupWeatherDev1' specifies the leading weather service and is mandatory.<br><br>
|
||||
|
||||
<b>Note:</b> If an OpenMeteo API is also set in the 'setupRadiationAPI' attribute, the settings of both attributes
|
||||
are harmonized, whereby the setting of 'setupRadiationAPI' is leading. <br><br>
|
||||
|
||||
<b>OpenMeteoDWD-API</b> <br>
|
||||
|
||||
@ -22552,8 +22542,7 @@ die ordnungsgemäße Anlagenkonfiguration geprüft werden.
|
||||
|
||||
<ul>
|
||||
<a id="SolarForecast-set-setupStringAzimuth"></a>
|
||||
<li><b>setupStringAzimuth <Stringname1>=<dir> [<Stringname2>=<dir> <Stringname3>=<dir> ...] </b> <br>
|
||||
(nur Model DWD, OpenMeteo*, ForecastSolarAPI) <br><br>
|
||||
<li><b>setupStringAzimuth <Stringname1>=<dir> [<Stringname2>=<dir> <Stringname3>=<dir> ...] </b> <br><br>
|
||||
|
||||
Ausrichtung <dir> der Solarmodule im String "StringnameX". Der Stringname ist ein Schlüsselwert des
|
||||
Attributs <b>setupInverterStrings</b>. <br>
|
||||
@ -22590,8 +22579,7 @@ die ordnungsgemäße Anlagenkonfiguration geprüft werden.
|
||||
|
||||
<ul>
|
||||
<a id="SolarForecast-set-setupStringDeclination"></a>
|
||||
<li><b>setupStringDeclination <Stringname1>=<Winkel> [<Stringname2>=<Winkel> <Stringname3>=<Winkel> ...] </b> <br>
|
||||
(nur Model DWD, ForecastSolarAPI) <br><br>
|
||||
<li><b>setupStringDeclination <Stringname1>=<Winkel> [<Stringname2>=<Winkel> <Stringname3>=<Winkel> ...] </b> <br><br>
|
||||
|
||||
Neigungswinkel der Solarmodule. Der Stringname ist ein Schlüsselwert des Attributs <b>setupInverterStrings</b>. <br>
|
||||
Mögliche Neigungswinkel sind: 0,5,10,15,20,25,30,35,40,45,50,55,60,65,70,75,80,85,90
|
||||
@ -23179,8 +23167,8 @@ die ordnungsgemäße Anlagenkonfiguration geprüft werden.
|
||||
<br>
|
||||
|
||||
<ul>
|
||||
<a id="SolarForecast-get-solApiData"></a>
|
||||
<li><b>solApiData </b> <br><br>
|
||||
<a id="SolarForecast-get-radiationApiData"></a>
|
||||
<li><b>radiationApiData </b> <br><br>
|
||||
|
||||
Listet die im Kontext des API-Abrufs gespeicherten Strahlungsdaten auf.
|
||||
Die von der API gelieferten Vorhersagedaten bzgl. der Globalstrahlung Rad1h und des auf einen String bezogenen
|
||||
@ -24517,9 +24505,10 @@ die ordnungsgemäße Anlagenkonfiguration geprüft werden.
|
||||
<li><b>setupRadiationAPI </b> <br><br>
|
||||
|
||||
Legt die Quelle zur Lieferung der solaren Strahlungsdaten fest. Es kann ein Device vom Typ DWD_OpenData oder
|
||||
eine implementierte API eines Dienstes ausgewählt werden. <br>
|
||||
<b>Hinweis:</b> Ist eine OpenMeteo API im Attribut 'setupWeatherDev1' gesetzt, kann kein anderer Strahlungsdatendienst als
|
||||
diese OpenMeteo API ausgewählt werden. <br><br>
|
||||
eine implementierte API eines Dienstes ausgewählt werden. <br><br>
|
||||
|
||||
<b>Hinweis:</b> Ist im Attribut 'setupWeatherDev1' ebenfalls eine OpenMeteo API gesetzt, werden die Einstellungen
|
||||
beider Attribute harmonisiert wobei die Einstellung von 'setupRadiationAPI' führend ist. <br><br>
|
||||
|
||||
<b>OpenMeteoDWD-API</b> <br>
|
||||
|
||||
@ -24655,9 +24644,10 @@ die ordnungsgemäße Anlagenkonfiguration geprüft werden.
|
||||
<li><b>setupWeatherDevX </b> <br><br>
|
||||
|
||||
Gibt das Gerät oder die API zur Lieferung der erforderlichen Wetterdaten (Wolkendecke, Niederschlag usw.) an.<br>
|
||||
Das Attribut 'setupWeatherDev1' definiert den führenden Wetterdienst und ist zwingend erforderlich.<br>
|
||||
Ist eine Open-Meteo API im Attribut 'setupWeatherDev1' ausgewählt, wird dieser Open-Meteo Dienst automatisch auch als Quelle
|
||||
der Strahlungsdaten (Attribut setupRadiationAPI) eingestellt. <br><br>
|
||||
Das Attribut 'setupWeatherDev1' definiert den führenden Wetterdienst und ist zwingend erforderlich. <br><br>
|
||||
|
||||
<b>Hinweis:</b> Ist im Attribut 'setupRadiationAPI' ebenfalls eine OpenMeteo API gesetzt, werden die Einstellungen
|
||||
beider Attribute harmonisiert wobei die Einstellung von 'setupRadiationAPI' führend ist. <br><br>
|
||||
|
||||
<b>OpenMeteoDWD-API</b> <br>
|
||||
|
||||
|
@ -156,6 +156,9 @@ BEGIN {
|
||||
|
||||
# Versions History intern
|
||||
my %vNotesIntern = (
|
||||
"1.38.0" => "30.11.2024 optimize data handling, rename getter solApiData to radiationApiData, ".
|
||||
"set setupStringAzimuth, setupStringDeclination is checked due to dependencies to OpenMeteo ".
|
||||
"attr setupRadiationAPI and setupWeatherDev1 can be set largely independently of each other ",
|
||||
"1.37.9" => "29.11.2024 activate StatusAPI-Hash, Separation of radiation API-data, API-state data, weather-API data ",
|
||||
"1.37.8" => "28.11.2024 edit commref, func _searchCacheFiles for renaming Cache files when device is renamed ".
|
||||
"_saveEnergyConsumption: extended for Debug collectData, preparation of weatherApiData ".
|
||||
@ -601,7 +604,7 @@ my %hget = ( # Ha
|
||||
forecastQualities => { fn => \&_getForecastQualities, needcred => 0 },
|
||||
nextHours => { fn => \&_getlistNextHours, needcred => 0 },
|
||||
rooftopData => { fn => \&_getRoofTopData, needcred => 0 },
|
||||
solApiData => { fn => \&_getlistSolCastData, needcred => 0 },
|
||||
radiationApiData => { fn => \&_getlistRadiationApiData, needcred => 0 },
|
||||
weatherApiData => { fn => \&_getlistWeatherApiData, needcred => 0 },
|
||||
statusApiData => { fn => \&_getlistStatusApiData, needcred => 0 },
|
||||
valDecTree => { fn => \&_getaiDecTree, needcred => 0 },
|
||||
@ -1293,9 +1296,6 @@ sub Initialize {
|
||||
|
||||
### nicht mehr benötigte Daten verarbeiten - Bereich kann später wieder raus !!
|
||||
##########################################################################################################################
|
||||
# my $av = 'obsolete#-#use#attr#flowGraphicControl#instead';
|
||||
# $hash->{AttrList} .= " flowGraphicCss:$av flowGraphicSize:$av flowGraphicAnimate:$av flowGraphicConsumerDistance:$av flowGraphicShowConsumer:$av flowGraphicShowConsumerDummy:$av flowGraphicShowConsumerPower:$av flowGraphicShowConsumerRemainTime:$av flowGraphicShift:$av ";
|
||||
|
||||
# my $av1 = "obsolete#-#the#attribute#will#be#deleted#soon";
|
||||
# $hash->{AttrList} .= " affect70percentRule:$av1 ctrlAutoRefresh:$av1 ctrlAutoRefreshFW:$av1 ";
|
||||
##########################################################################################################################
|
||||
@ -1364,7 +1364,7 @@ sub Define {
|
||||
|
||||
$params->{file} = $scpicache.$name; # Cache File SolCast API Werte einlesen wenn vorhanden
|
||||
$params->{cachename} = 'solcastapi';
|
||||
$params->{title} = 'solApiData';
|
||||
$params->{title} = 'radiationApiData';
|
||||
_readCacheFile ($params);
|
||||
|
||||
$params->{file} = $statcache.$name; # Cache File API-Status einlesen wenn vorhanden
|
||||
@ -1590,6 +1590,8 @@ sub Set {
|
||||
"powerTrigger:textField-long ".
|
||||
"pvCorrectionFactor_Auto:noLearning,on_simple".($ipai ? ',on_simple_ai,' : ',')."on_complex".($ipai ? ',on_complex_ai,' : ',')."off ".
|
||||
"reset:$resets ".
|
||||
"setupStringAzimuth ".
|
||||
"setupStringDeclination ".
|
||||
$cf." "
|
||||
;
|
||||
|
||||
@ -1599,20 +1601,10 @@ sub Set {
|
||||
$setlist .= "roofIdentPair "
|
||||
;
|
||||
}
|
||||
elsif (isForecastSolarUsed ($hash)) {
|
||||
$setlist .= "setupStringAzimuth ".
|
||||
"setupStringDeclination "
|
||||
;
|
||||
}
|
||||
elsif (isVictronKiUsed ($hash)) {
|
||||
$setlist .= "vrmCredentials "
|
||||
;
|
||||
}
|
||||
else {
|
||||
$setlist .= "setupStringAzimuth ".
|
||||
"setupStringDeclination "
|
||||
;
|
||||
}
|
||||
|
||||
## KI spezifische Setter
|
||||
##########################
|
||||
@ -2513,7 +2505,7 @@ sub Get {
|
||||
"pvCircular:noArg ".
|
||||
"pvHistory:#,exportToCsv,$pvl ".
|
||||
"rooftopData:noArg ".
|
||||
"solApiData:noArg ".
|
||||
"radiationApiData:noArg ".
|
||||
"statusApiData:noArg ".
|
||||
"valCurrent:noArg ".
|
||||
"weatherApiData:noArg "
|
||||
@ -2570,24 +2562,36 @@ sub _getRoofTopData {
|
||||
delete $data{$type}{$name}{current}{dwdRad1hAge};
|
||||
delete $data{$type}{$name}{current}{dwdRad1hAgeTS};
|
||||
|
||||
my $ret = "$name is not a valid SolarForeCast Model: ".$hash->{MODEL};
|
||||
my ($rapi, $wapi) = getStatusApiName ($hash); # $rapi - Radiation-API, $wapi - Weather-API
|
||||
my $ret = "$name is not a valid Radiation ($rapi) and/or Weather ($wapi) Model";
|
||||
|
||||
if ($hash->{MODEL} eq 'SolCastAPI') {
|
||||
|
||||
if ($rapi eq 'SolCast') {
|
||||
$ret = __getSolCastData ($paref);
|
||||
}
|
||||
elsif ($hash->{MODEL} eq 'ForecastSolarAPI') {
|
||||
elsif ($rapi eq 'ForecastSolar') {
|
||||
$ret = __getForecastSolarData ($paref);
|
||||
}
|
||||
elsif ($hash->{MODEL} eq 'DWD') {
|
||||
elsif ($rapi eq 'DWD') {
|
||||
$ret = __getDWDSolarData ($paref);
|
||||
}
|
||||
elsif ($hash->{MODEL} eq 'VictronKiAPI') {
|
||||
elsif ($rapi eq 'VictronKi') {
|
||||
$ret = __getVictronSolarData ($paref);
|
||||
}
|
||||
elsif ($hash->{MODEL} =~ /^OpenMeteo/xs) {
|
||||
|
||||
if ($rapi eq 'OpenMeteo' || $wapi eq 'OpenMeteo') {
|
||||
if ($rapi eq 'OpenMeteo') {
|
||||
$paref->{reqm} = 'MODEL';
|
||||
}
|
||||
else {
|
||||
$paref->{reqm} = 'WEATHERMODEL';
|
||||
}
|
||||
|
||||
$ret = __getopenMeteoData ($paref);
|
||||
}
|
||||
|
||||
delete $paref->{reqm};
|
||||
|
||||
return $ret;
|
||||
}
|
||||
|
||||
@ -2876,7 +2880,7 @@ sub __solCast_ApiResponse {
|
||||
$period =~ s/.*(\d\d).*/$1/; # -> die neuen Daten (in dem Fall nur die einer halben Stunde) im nächsten Schritt addiert werden
|
||||
|
||||
my $est50 = SolCastAPIVal ($hash, $string, $starttmstr, 'pv_estimate50', 0) / (60/$period);
|
||||
$data{$type}{$name}{solcastapi}{$string}{$starttmstr}{pv_estimate50} = $est50 if($est50);
|
||||
$data{$type}{$name}{solcastapi}{$string}{$starttmstr}{pv_estimate50} = sprintf "%.0f", $est50 if($est50);
|
||||
|
||||
$k++;
|
||||
next;
|
||||
@ -2903,14 +2907,16 @@ sub __solCast_ApiResponse {
|
||||
$period = $jdata->{'forecasts'}[$k]{'period'};
|
||||
$period =~ s/.*(\d\d).*/$1/;
|
||||
|
||||
$pvest50 = sprintf "%.0f", ($pvest50 * ($period/60) * 1000);
|
||||
|
||||
if ($debug =~ /apiProcess/x) { # nur für Debugging
|
||||
if (exists $data{$type}{$name}{solcastapi}{$string}{$starttmstr}) {
|
||||
Log3 ($name, 1, qq{$name DEBUG> SolCast API Hash - Start Date/Time: }. $starttmstr);
|
||||
Log3 ($name, 1, qq{$name DEBUG> SolCast API Hash - pv_estimate50 add: }.(sprintf "%.0f", ($pvest50 * ($period/60) * 1000)).qq{, contains already: }.SolCastAPIVal ($hash, $string, $starttmstr, 'pv_estimate50', 0));
|
||||
Log3 ($name, 1, qq{$name DEBUG> SolCast API Hash - pv_estimate50 add: }.$pvest50.qq{, contains already: }.SolCastAPIVal ($hash, $string, $starttmstr, 'pv_estimate50', 0));
|
||||
}
|
||||
}
|
||||
|
||||
$data{$type}{$name}{solcastapi}{$string}{$starttmstr}{pv_estimate50} += sprintf "%.0f", ($pvest50 * ($period/60) * 1000);
|
||||
$data{$type}{$name}{solcastapi}{$string}{$starttmstr}{pv_estimate50} += $pvest50;
|
||||
|
||||
$k++;
|
||||
}
|
||||
@ -4085,6 +4091,7 @@ sub __getopenMeteoData {
|
||||
my $t = $paref->{t};
|
||||
my $lang = $paref->{lang};
|
||||
my $debug = $paref->{debug};
|
||||
my $reqm = $paref->{reqm};
|
||||
|
||||
my $hash = $defs{$name};
|
||||
my $donearq = StatusAPIVal ($hash, 'OpenMeteo', '?All', 'todayDoneAPIrequests', 0);
|
||||
@ -4106,7 +4113,8 @@ sub __getopenMeteoData {
|
||||
|
||||
debugLog ($paref, 'apiCall', qq{Open-Meteo API Call - the daily API requests -> limited to: $ometmaxreq, done: $donearq});
|
||||
|
||||
my $submodel = InternalVal ($hash->{NAME}, 'MODEL', '');
|
||||
my $submodel = InternalVal ($hash->{NAME}, $reqm, '');
|
||||
|
||||
$paref->{allstrings} = AttrVal ($name, 'setupInverterStrings', '');
|
||||
$paref->{submodel} = $submodel eq 'OpenMeteoDWDAPI' ? 'DWD ICON Seamless' :
|
||||
$submodel eq 'OpenMeteoDWDEnsembleAPI' ? 'DWD ICON Seamless Ensemble' :
|
||||
@ -4117,6 +4125,7 @@ sub __getopenMeteoData {
|
||||
|
||||
$paref->{callequivalent} = $submodel eq 'OpenMeteoDWDEnsembleAPI' ? 20 : 1;
|
||||
$paref->{begin} = 1;
|
||||
$paref->{requestmode} = $reqm;
|
||||
|
||||
__openMeteoDWD_ApiRequest ($paref);
|
||||
|
||||
@ -4154,7 +4163,8 @@ sub __openMeteoDWD_ApiRequest {
|
||||
my $debug = $paref->{debug};
|
||||
my $lang = $paref->{lang};
|
||||
my $t = $paref->{t} // int time;
|
||||
my $submodel = $paref->{submodel}; # abzufragendes Wettermodell
|
||||
my $submodel = $paref->{submodel}; # abzufragendes Datenmodell
|
||||
my $requestmode = $paref->{requestmode};
|
||||
|
||||
my $hash = $defs{$name};
|
||||
|
||||
@ -4167,19 +4177,27 @@ sub __openMeteoDWD_ApiRequest {
|
||||
return;
|
||||
}
|
||||
|
||||
my $string;
|
||||
my ($string, $err);
|
||||
($string, $allstrings) = split ",", $allstrings, 2;
|
||||
my ($set, $lat, $lon, $elev) = locCoordinates();
|
||||
|
||||
if (!$set) {
|
||||
my $err = qq{the attribute 'latitude' and/or 'longitude' in global device is not set};
|
||||
singleUpdateState ( {hash => $hash, state => $err, evt => 1} );
|
||||
return $err;
|
||||
$err = qq{ERROR - the attribute 'latitude' and/or 'longitude' in global device is not set};
|
||||
Log3 ($name, 1, "$name - $err");
|
||||
return;
|
||||
}
|
||||
|
||||
my $tilt = StringVal ($hash, $string, 'tilt', '<unknown>');
|
||||
my $az = StringVal ($hash, $string, 'azimut', '<unknown>');
|
||||
|
||||
if ($requestmode eq 'WEATHERMODEL' && $string eq 'KI-based') {$tilt = 0; $az = 0;} # Dummy Settings
|
||||
|
||||
if ($tilt eq '<unknown>' || $az eq '<unknown>') {
|
||||
$err = qq{ERROR OpenMeteo API Call - the reading 'setupStringAzimuth' and/or 'setupStringDeclination' is not set};
|
||||
Log3 ($name, 1, "$name - $err");
|
||||
return;
|
||||
}
|
||||
|
||||
my $url = "https://api.open-meteo.com/v1/forecast?";
|
||||
$url = "https://ensemble-api.open-meteo.com/v1/ensemble?" if($submodel =~ /Ensemble/xs); # Ensemble Modell gewählt
|
||||
$url .= "models=icon_seamless" if($submodel =~ /DWD\sICON\sSeamless/xs);
|
||||
@ -4195,7 +4213,8 @@ sub __openMeteoDWD_ApiRequest {
|
||||
$url .= "&tilt=".$tilt;
|
||||
$url .= "&azimuth=".$az;
|
||||
|
||||
debugLog ($paref, 'apiCall', qq{Open-Meteo API Call - Request for PV-String "$string" with Weather Model >$submodel<:\n$url});
|
||||
debugLog ($paref, 'apiCall', qq{Open-Meteo API Call - Request for PV-String "$string" with Data Model >$submodel<:\n$url});
|
||||
debugLog ($paref, 'apiCall|apiProcess', qq{Open-Meteo API Call - Request Mode: $requestmode});
|
||||
|
||||
my $caller = (caller(0))[3]; # Rücksprungmarke
|
||||
|
||||
@ -4209,6 +4228,7 @@ sub __openMeteoDWD_ApiRequest {
|
||||
submodel => $submodel,
|
||||
begin => $paref->{begin},
|
||||
callequivalent => $paref->{callequivalent},
|
||||
requestmode => $requestmode,
|
||||
caller => \&$caller,
|
||||
stc => [gettimeofday],
|
||||
allstrings => $allstrings,
|
||||
@ -4257,6 +4277,7 @@ sub __openMeteoDWD_ApiResponse {
|
||||
my $caller = $paref->{caller};
|
||||
my $string = $paref->{string};
|
||||
my $allstrings = $paref->{allstrings};
|
||||
my $requestmode = $paref->{requestmode}; # MODEL oder WEATHERMODEL
|
||||
my $stc = $paref->{stc}; # Startzeit API Abruf
|
||||
my $lang = $paref->{lang};
|
||||
my $debug = $paref->{debug};
|
||||
@ -4379,6 +4400,7 @@ sub __openMeteoDWD_ApiResponse {
|
||||
my $rad1wh = $jdata->{hourly}{global_tilted_irradiance}[$k]; # Wh/m2
|
||||
my $rad = 10 * (sprintf "%.0f", ($rad1wh * $WhtokJ) / 10); # Umrechnung Wh/m2 in kJ/m2 ->
|
||||
my $pv = sprintf "%.2f", int ($rad1wh / 1000 * $peak * $prdef); # Rad wird in kWh/m2 erwartet
|
||||
|
||||
my $don = $jdata->{hourly}{is_day}[$k];
|
||||
my $temp = $jdata->{hourly}{temperature_2m}[$k];
|
||||
my $rain = $jdata->{hourly}{rain}[$k]; # Regen in Millimeter = kg/m2
|
||||
@ -4388,7 +4410,10 @@ sub __openMeteoDWD_ApiResponse {
|
||||
if ($k == 0 && $curwid) { $curwid = ($don ? 0 : 100) + $curwid }
|
||||
|
||||
if ($debug =~ /apiProcess/xs) {
|
||||
if ($requestmode eq 'MODEL') {
|
||||
Log3 ($name, 1, "$name DEBUG> Open-Meteo DWD ICON API $pvtmstr - Rad1Wh: $rad1wh, Rad1kJ: $rad, PV est: $pv Wh");
|
||||
}
|
||||
|
||||
Log3 ($name, 1, "$name DEBUG> Open-Meteo DWD ICON API $pvtmstr - RR1c: $rain");
|
||||
Log3 ($name, 1, "$name DEBUG> Open-Meteo DWD ICON API $otmstr - DoN: $don");
|
||||
Log3 ($name, 1, "$name DEBUG> Open-Meteo DWD ICON API $otmstr - Temp: $temp");
|
||||
@ -4402,29 +4427,24 @@ sub __openMeteoDWD_ApiResponse {
|
||||
}
|
||||
}
|
||||
|
||||
if ($requestmode eq 'MODEL') {
|
||||
$data{$type}{$name}{solcastapi}{$string}{$pvtmstr}{pv_estimate50} = $pv; # Startstunde verschieben
|
||||
|
||||
my $fwtg = formatWeatherTimestrg ($pvtmstr);
|
||||
|
||||
if ($paref->{begin}) { # im ersten Call den DS löschen -> dann Aufsummierung
|
||||
delete $data{$type}{$name}{solcastapi}{'?All'}{$pvtmstr}{Rad1h};
|
||||
}
|
||||
|
||||
$data{$type}{$name}{solcastapi}{'?All'}{$pvtmstr}{Rad1h} += $rad; # Startstunde verschieben, Rad Werte aller Strings addieren
|
||||
}
|
||||
|
||||
## Wetterdaten
|
||||
################
|
||||
$data{$type}{$name}{solcastapi}{'?All'}{$fwtg}{rr1c} = $rain; # Startstunde verschieben
|
||||
my $fwtg = formatWeatherTimestrg ($pvtmstr); # Zeit gemäß DWD_OpenData-Format
|
||||
|
||||
$data{$type}{$name}{weatherapi}{OpenMeteo}{$fwtg}{rr1c} = $rain;
|
||||
$data{$type}{$name}{weatherapi}{OpenMeteo}{$fwtg}{StartTime} = $pvtmstr;
|
||||
|
||||
$fwtg = formatWeatherTimestrg ($otmstr);
|
||||
|
||||
$data{$type}{$name}{solcastapi}{'?All'}{$fwtg}{don} = $don;
|
||||
$data{$type}{$name}{solcastapi}{'?All'}{$fwtg}{neff} = $wcc;
|
||||
$data{$type}{$name}{solcastapi}{'?All'}{$fwtg}{ww} = $wid;
|
||||
$data{$type}{$name}{solcastapi}{'?All'}{$fwtg}{ttt} = $temp;
|
||||
$data{$type}{$name}{solcastapi}{'?All'}{$fwtg}{UpdateTime} = $rt;
|
||||
$fwtg = formatWeatherTimestrg ($otmstr); # Zeit gemäß DWD_OpenData-Format
|
||||
|
||||
$data{$type}{$name}{weatherapi}{OpenMeteo}{$fwtg}{don} = $don;
|
||||
$data{$type}{$name}{weatherapi}{OpenMeteo}{$fwtg}{neff} = $wcc;
|
||||
@ -4434,10 +4454,6 @@ sub __openMeteoDWD_ApiResponse {
|
||||
$data{$type}{$name}{weatherapi}{OpenMeteo}{$fwtg}{StartTime} = $otmstr;
|
||||
|
||||
if ($k == 0) {
|
||||
$data{$type}{$name}{solcastapi}{'?All'}{$fwtg}{neff} = $curwcc if(defined $curwcc);
|
||||
$data{$type}{$name}{solcastapi}{'?All'}{$fwtg}{ww} = $curwid if(defined $curwid);
|
||||
$data{$type}{$name}{solcastapi}{'?All'}{$fwtg}{ttt} = $curtmp if(defined $curtmp);
|
||||
|
||||
$data{$type}{$name}{weatherapi}{OpenMeteo}{$fwtg}{neff} = $curwcc if(defined $curwcc);
|
||||
$data{$type}{$name}{weatherapi}{OpenMeteo}{$fwtg}{ww} = $curwid if(defined $curwid);
|
||||
$data{$type}{$name}{weatherapi}{OpenMeteo}{$fwtg}{ttt} = $curtmp if(defined $curtmp);
|
||||
@ -4464,9 +4480,6 @@ sub __openMeteoDWD_ApiResponse {
|
||||
}
|
||||
|
||||
if ($k == 0) {
|
||||
$data{$type}{$name}{solcastapi}{'?All'}{sunrise}{today} = $sunrise;
|
||||
$data{$type}{$name}{solcastapi}{'?All'}{sunset}{today} = $sunset;
|
||||
|
||||
$data{$type}{$name}{weatherapi}{OpenMeteo}{sunrise}{today} = $sunrise;
|
||||
$data{$type}{$name}{weatherapi}{OpenMeteo}{sunset}{today} = $sunset;
|
||||
|
||||
@ -4477,9 +4490,6 @@ sub __openMeteoDWD_ApiResponse {
|
||||
}
|
||||
|
||||
if ($k == 1) {
|
||||
$data{$type}{$name}{solcastapi}{'?All'}{sunrise}{tomorrow} = $sunrise;
|
||||
$data{$type}{$name}{solcastapi}{'?All'}{sunset}{tomorrow} = $sunset;
|
||||
|
||||
$data{$type}{$name}{weatherapi}{OpenMeteo}{sunrise}{tomorrow} = $sunrise;
|
||||
$data{$type}{$name}{weatherapi}{OpenMeteo}{sunset}{tomorrow} = $sunset;
|
||||
|
||||
@ -4504,6 +4514,7 @@ sub __openMeteoDWD_ApiResponse {
|
||||
allstrings => $allstrings,
|
||||
submodel => $submodel,
|
||||
callequivalent => $paref->{callequivalent},
|
||||
requestmode => $requestmode,
|
||||
lang => $lang
|
||||
};
|
||||
|
||||
@ -4730,14 +4741,14 @@ return $ret;
|
||||
}
|
||||
|
||||
###############################################################
|
||||
# Getter solApiData
|
||||
# Getter radiationApiData
|
||||
###############################################################
|
||||
sub _getlistSolCastData {
|
||||
sub _getlistRadiationApiData {
|
||||
my $paref = shift;
|
||||
my $name = $paref->{name};
|
||||
my $hash = $defs{$name};
|
||||
|
||||
my $ret = listDataPool ($hash, 'solApiData');
|
||||
my $ret = listDataPool ($hash, 'radiationApiData');
|
||||
$ret .= lineFromSpaces ($ret, 10);
|
||||
|
||||
return $ret;
|
||||
@ -5425,16 +5436,6 @@ sub Attr {
|
||||
|
||||
### nicht mehr benötigte Daten verarbeiten - Bereich kann später wieder raus !!
|
||||
######################################################################################################################
|
||||
# 20.10.2024
|
||||
if ($cmd eq 'set' && $aName =~ /^flowGraphicCss|flowGraphicSize|flowGraphicAnimate|flowGraphicConsumerDistance|flowGraphicShowConsumer|flowGraphicShowConsumerDummy|flowGraphicShowConsumerPower|flowGraphicShowConsumerRemainTime|flowGraphicShift$/) {
|
||||
if (!$init_done) {
|
||||
# return qq{Device "$name" -> The attribute '$aName' is replaced by 'flowGraphicControl'. Please press "save config" when restart is finished.};
|
||||
Log3 ($name, 1, qq{$name - The attribute '$aName' is replaced by 'flowGraphicControl'. Please press "save config" when restart is finished.});
|
||||
}
|
||||
else {
|
||||
return qq{The attribute '$aName' is obsolete and replaced by 'flowGraphicControl'.};
|
||||
}
|
||||
}
|
||||
# 31.10.2024
|
||||
if ($cmd eq 'set' && $aName =~ /^graphicStartHtml|affect70percentRule|graphicEndHtml|ctrlAutoRefresh|ctrlAutoRefreshFW$/) {
|
||||
if (!$init_done) {
|
||||
@ -6197,19 +6198,26 @@ sub _attrWeatherDev { ## no critic "not used"
|
||||
|
||||
if ($aVal =~ /^OpenMeteo/xs) {
|
||||
if ($aName ne 'setupWeatherDev1') {
|
||||
return qq{Only the leading attribute 'setupWeatherDev1' can set to '$aVal'};
|
||||
return qq{Only the leading weather attribute 'setupWeatherDev1' can set to '$aVal'};
|
||||
}
|
||||
|
||||
InternalTimer (gettimeofday()+1, 'FHEM::SolarForecast::__setRadAPIdelayed', $hash, 0); # automatisch setupRadiationAPI setzen wenn setupWeatherDev1
|
||||
return;
|
||||
my @istrings = split ",", AttrVal ($name, 'setupInverterStrings', ''); # Stringbezeichner
|
||||
|
||||
if ((!ReadingsVal ($name, 'setupStringAzimuth', '') || !ReadingsVal ($name, 'setupStringDeclination', '')) &&
|
||||
!grep /KI-based/, @istrings) {
|
||||
return qq{Execute 'set $name setupStringAzimuth' and/or 'set $name setupStringDeclination' first.};
|
||||
}
|
||||
}
|
||||
|
||||
if ($aVal !~ /-API$/xs) { # Attribute des DWD-Devices prüfen
|
||||
my $err = checkdwdattr ($name, $aVal, \@dweattrmust);
|
||||
return $err if($err);
|
||||
}
|
||||
}
|
||||
|
||||
InternalTimer (gettimeofday() + 1, 'FHEM::SolarForecast::setModel', $hash, 0);
|
||||
InternalTimer (gettimeofday() + 2, 'FHEM::SolarForecast::createAssociatedWith', $hash, 0);
|
||||
InternalTimer (gettimeofday() + 1, 'FHEM::SolarForecast::__harmonizeAPIdelayed', $hash, 0);
|
||||
InternalTimer (gettimeofday() + 2, 'FHEM::SolarForecast::setModel', $hash, 0);
|
||||
InternalTimer (gettimeofday() + 3, 'FHEM::SolarForecast::createAssociatedWith', $hash, 0);
|
||||
|
||||
return;
|
||||
}
|
||||
@ -6233,14 +6241,14 @@ sub _attrRadiationAPI { ## no critic "not used"
|
||||
return qq{The device "$aVal" doesn't exist or has no TYPE "DWD_OpenData"};
|
||||
}
|
||||
|
||||
my $awdev1 = AttrVal ($name, 'setupWeatherDev1', '');
|
||||
#my $awdev1 = AttrVal ($name, 'setupWeatherDev1', '');
|
||||
|
||||
if (($awdev1 eq 'OpenMeteoDWD-API' && $aVal ne 'OpenMeteoDWD-API') ||
|
||||
($awdev1 eq 'OpenMeteoDWDEnsemble-API' && $aVal ne 'OpenMeteoDWDEnsemble-API') ||
|
||||
($awdev1 eq 'OpenMeteoWorld-API' && $aVal ne 'OpenMeteoWorld-API')) {
|
||||
return "The attribute 'setupWeatherDev1' is set to '$awdev1'. \n".
|
||||
"Change that attribute to another weather device first if you want use an other API.";
|
||||
}
|
||||
#if (($awdev1 eq 'OpenMeteoDWD-API' && $aVal ne 'OpenMeteoDWD-API') ||
|
||||
# ($awdev1 eq 'OpenMeteoDWDEnsemble-API' && $aVal ne 'OpenMeteoDWDEnsemble-API') ||
|
||||
# ($awdev1 eq 'OpenMeteoWorld-API' && $aVal ne 'OpenMeteoWorld-API')) {
|
||||
# return "The attribute 'setupWeatherDev1' is set to '$awdev1'. \n".
|
||||
# "Change that attribute to another weather device first if you want use an other API.";
|
||||
#}
|
||||
|
||||
if ($aVal =~ /(SolCast|OpenMeteoDWD|OpenMeteoDWDEnsemble|OpenMeteoWorld)-API/xs) {
|
||||
return "The library FHEM::Utility::CTZ is missing. Please update FHEM completely." if($ctzAbsent);
|
||||
@ -6267,9 +6275,10 @@ sub _attrRadiationAPI { ## no critic "not used"
|
||||
|
||||
readingsDelete ($hash, 'nextRadiationAPICall');
|
||||
|
||||
InternalTimer (gettimeofday() + 1, 'FHEM::SolarForecast::setModel', $hash, 0); # Model setzen
|
||||
InternalTimer (gettimeofday() + 2, 'FHEM::SolarForecast::createAssociatedWith', $hash, 0);
|
||||
InternalTimer (gettimeofday() + 3, 'FHEM::SolarForecast::writeCacheToFile', [$name, 'plantconfig', $plantcfg.$name], 0); # Anlagenkonfiguration File schreiben
|
||||
InternalTimer (gettimeofday() + 1, 'FHEM::SolarForecast::__harmonizeAPIdelayed', $hash, 0);
|
||||
InternalTimer (gettimeofday() + 2, 'FHEM::SolarForecast::setModel', $hash, 0); # Model setzen
|
||||
InternalTimer (gettimeofday() + 3, 'FHEM::SolarForecast::createAssociatedWith', $hash, 0);
|
||||
InternalTimer (gettimeofday() + 4, 'FHEM::SolarForecast::writeCacheToFile', [$name, 'plantconfig', $plantcfg.$name], 0); # Anlagenkonfiguration File schreiben
|
||||
|
||||
return;
|
||||
}
|
||||
@ -6302,15 +6311,22 @@ return;
|
||||
}
|
||||
|
||||
################################################################
|
||||
# setupRadiationAPI verzögert aus Attr setzen
|
||||
# Attr setupRadiationAPI und setupWeatherDev1
|
||||
# harmonisieren wenn erforderlich
|
||||
# setupRadiationAPI ist führend
|
||||
################################################################
|
||||
sub __setRadAPIdelayed {
|
||||
sub __harmonizeAPIdelayed {
|
||||
my $hash = shift;
|
||||
|
||||
my $name = $hash->{NAME};
|
||||
my $awdev1 = AttrVal ($name, 'setupWeatherDev1', '');
|
||||
my $wedev1 = AttrVal ($name, 'setupWeatherDev1', '');
|
||||
my $radapi = AttrVal ($name, 'setupRadiationAPI', '');
|
||||
|
||||
CommandAttr (undef, "$name setupRadiationAPI $awdev1"); # automatisch setupRadiationAPI setzen
|
||||
return if($wedev1 eq $radapi);
|
||||
|
||||
if ($radapi =~ /OpenMeteo/xs && $wedev1 =~ /OpenMeteo/xs) { # auf OpenMeteo Datenmodell harmonisieren
|
||||
CommandAttr (undef, "$name setupWeatherDev1 $radapi");
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
@ -7083,35 +7099,6 @@ sub centralTask {
|
||||
|
||||
### nicht mehr benötigte Daten verarbeiten - Bereich kann später wieder raus !!
|
||||
##########################################################################################################################
|
||||
for my $n (1..24) { # 08.10.2024
|
||||
$n = sprintf "%02d", $n;
|
||||
readingsDelete ($hash, "pvCorrectionFactor_${n}_autocalc");
|
||||
}
|
||||
|
||||
my $fg1 = AttrVal ($name, 'flowGraphicSize', undef); # 20.10.2024
|
||||
my $fg2 = AttrVal ($name, 'flowGraphicAnimate', undef);
|
||||
my $fg3 = AttrVal ($name, 'flowGraphicConsumerDistance', undef);
|
||||
my $fg4 = AttrVal ($name, 'flowGraphicShowConsumer', undef);
|
||||
my $fg5 = AttrVal ($name, 'flowGraphicShowConsumerDummy', undef);
|
||||
my $fg6 = AttrVal ($name, 'flowGraphicShowConsumerPower', undef);
|
||||
my $fg7 = AttrVal ($name, 'flowGraphicShowConsumerRemainTime', undef);
|
||||
my $fg8 = AttrVal ($name, 'flowGraphicShift', undef);
|
||||
|
||||
my $newval;
|
||||
$newval .= "size=$fg1 " if(defined $fg1);
|
||||
$newval .= "animate=$fg2 " if(defined $fg2);
|
||||
$newval .= "consumerdist=$fg3 " if(defined $fg3);
|
||||
$newval .= "showconsumer=$fg4 " if(defined $fg4);
|
||||
$newval .= "showconsumerdummy=$fg5 " if(defined $fg5);
|
||||
$newval .= "showconsumerpower=$fg6 " if(defined $fg6);
|
||||
$newval .= "showconsumerremaintime=$fg7 " if(defined $fg7);
|
||||
$newval .= "shift=$fg8 " if(defined $fg8);
|
||||
|
||||
if ($newval) {
|
||||
CommandAttr (undef, "$name flowGraphicControl $newval");
|
||||
::CommandDeleteAttr (undef, "$name flowGraphicSize|flowGraphicAnimate|flowGraphicConsumerDistance|flowGraphicShowConsumer|flowGraphicShowConsumerDummy|flowGraphicShowConsumerPower|flowGraphicShowConsumerRemainTime|flowGraphicShift");
|
||||
}
|
||||
|
||||
if (exists $data{$type}{$name}{solcastapi}{'?IdPair'}) { # 29.11.2024
|
||||
for my $pk (keys %{$data{$type}{$name}{solcastapi}{'?IdPair'}}) {
|
||||
my $apikey = SolCastAPIVal ($hash, '?IdPair', $pk, 'apikey', '');
|
||||
@ -7132,7 +7119,7 @@ sub centralTask {
|
||||
for my $key (keys %{$data{$type}{$name}{solcastapi}{'?All'}{'?All'}}) {
|
||||
my $val = SolCastAPIVal ($hash, '?All', '?All', $key, '');
|
||||
|
||||
if ($val) {
|
||||
if ($rapi && $val) {
|
||||
$data{$type}{$name}{statusapi}{$rapi}{'?All'}{$key} = $val;
|
||||
delete $data{$type}{$name}{solcastapi}{'?All'}{'?All'}{$key};
|
||||
}
|
||||
@ -7156,7 +7143,6 @@ sub centralTask {
|
||||
$data{$type}{$name}{statusapi}{'?VRM'}{'?API'}{credentials} = $vrmcr;
|
||||
delete $data{$type}{$name}{solcastapi}{'?VRM'};
|
||||
}
|
||||
|
||||
##########################################################################################################################
|
||||
|
||||
my (undef, $disabled, $inactive) = controller ($name);
|
||||
@ -7225,7 +7211,7 @@ sub centralTask {
|
||||
|
||||
readingsDelete ($hash, 'AllPVforecastsToEvent');
|
||||
|
||||
_getRoofTopData ($centpars); # Strahlungswerte/Forecast-Werte in solcastapi-Hash erstellen
|
||||
_getRoofTopData ($centpars); # Strahlungs/Wetter-Daten der gewählten API's abrufen und in internen Strukturen speichern
|
||||
_transferInverterValues ($centpars); # WR Werte übertragen
|
||||
_transferAPIRadiationValues ($centpars); # Raw Erzeugungswerte aus solcastapi-Hash übertragen und Forecast mit/ohne Korrektur erstellen
|
||||
_calcMaxEstimateToday ($centpars); # heutigen Max PV Estimate & dessen Tageszeit ermitteln
|
||||
@ -7320,9 +7306,10 @@ sub createStringConfig { ## no critic "not used"
|
||||
return qq{You use a KI based model. Please set only "KI-based" as String with command "attr $name setupInverterStrings".};
|
||||
}
|
||||
}
|
||||
elsif (!isVictronKiUsed ($hash)) {
|
||||
|
||||
if (!grep /^KI-based$/, @istrings) {
|
||||
my $tilt = ReadingsVal ($name, 'setupStringDeclination', ''); # Modul Neigungswinkel für jeden Stringbezeichner
|
||||
return qq{Please complete command "set $name setupStringDeclination".} if(!$tilt);
|
||||
return qq{Please complete command "set $name setupStringDeclination"} if(!$tilt);
|
||||
|
||||
my ($at,$ht) = parseParams ($tilt);
|
||||
|
||||
@ -7336,7 +7323,7 @@ sub createStringConfig { ## no critic "not used"
|
||||
}
|
||||
|
||||
my $dir = ReadingsVal ($name, 'setupStringAzimuth', ''); # Modul Ausrichtung für jeden Stringbezeichner
|
||||
return qq{Please complete command "set $name setupStringAzimuth".} if(!$dir);
|
||||
return qq{Please complete command "set $name setupStringAzimuth"} if(!$dir);
|
||||
|
||||
my ($ad,$hd) = parseParams ($dir);
|
||||
my $iwrong = qq{Please check the input of set "setupStringAzimuth". It seems to be wrong.};
|
||||
@ -7934,29 +7921,24 @@ sub __delObsoleteAPIData {
|
||||
my $date = $paref->{date}; # aktuelles Datum
|
||||
my $hash = $defs{$name};
|
||||
|
||||
my ($rapi, $wapi) = getStatusApiName ($hash);
|
||||
|
||||
## Wetter-API Daten löschen
|
||||
#############################
|
||||
if (keys %{$data{$type}{$name}{weatherapi}}) {
|
||||
if (isWeatherOpenMeteoUsed ($hash)) {
|
||||
for my $idx (keys %{$data{$type}{$name}{weatherapi}{OpenMeteo}}) {
|
||||
delete $data{$type}{$name}{weatherapi}{OpenMeteo}{$idx} if($idx =~ /^fc?([0-9]{1,2})_?([0-9]{1,2})$/xs);
|
||||
}
|
||||
}
|
||||
else {
|
||||
delete $data{$type}{$name}{weatherapi}{OpenMeteo};
|
||||
}
|
||||
delete $data{$type}{$name}{weatherapi}{OpenMeteo} if($wapi ne 'OpenMeteo');
|
||||
}
|
||||
|
||||
## Status-API Daten löschen
|
||||
#############################
|
||||
if (keys %{$data{$type}{$name}{statusapi}}) {
|
||||
delete $data{$type}{$name}{statusapi}{OpenMeteo} if(!isOpenMeteoUsed ($hash) && !isWeatherOpenMeteoUsed ($hash));
|
||||
delete $data{$type}{$name}{statusapi}{ForecastSolar} if(!isForecastSolarUsed ($hash));
|
||||
delete $data{$type}{$name}{statusapi}{SolCast} if(!isSolCastUsed ($hash));
|
||||
delete $data{$type}{$name}{statusapi}{'?IdPair'} if(!isSolCastUsed ($hash));
|
||||
delete $data{$type}{$name}{statusapi}{DWD} if(!isDWDUsed ($hash));
|
||||
delete $data{$type}{$name}{statusapi}{VictronKi} if(!isVictronKiUsed ($hash));
|
||||
delete $data{$type}{$name}{statusapi}{'?VRM'} if(!isVictronKiUsed ($hash));
|
||||
delete $data{$type}{$name}{statusapi}{OpenMeteo} if($rapi ne 'OpenMeteo' && $wapi ne 'OpenMeteo');
|
||||
delete $data{$type}{$name}{statusapi}{ForecastSolar} if($rapi ne 'ForecastSolar');
|
||||
delete $data{$type}{$name}{statusapi}{SolCast} if($rapi ne 'SolCast');
|
||||
delete $data{$type}{$name}{statusapi}{'?IdPair'} if($rapi ne 'SolCast');
|
||||
delete $data{$type}{$name}{statusapi}{DWD} if($rapi ne 'DWD');
|
||||
delete $data{$type}{$name}{statusapi}{VictronKi} if($rapi ne 'VictronKi');
|
||||
delete $data{$type}{$name}{statusapi}{'?VRM'} if($rapi ne 'VictronKi');
|
||||
}
|
||||
|
||||
## Solar-API Daten löschen
|
||||
@ -12020,6 +12002,7 @@ return ($fd, $fh);
|
||||
}
|
||||
|
||||
################################################################
|
||||
# Zeit gemäß DWD_OpenData-Format
|
||||
# Berechnen Tag / Stunden Verschieber ab aktuellen Tag
|
||||
# Input: YYYY-MM-DD HH:MM:SS
|
||||
# Output: $fd - 0 (Heute), 1 (Morgen), 2 (Übermorgen), ....
|
||||
@ -12660,10 +12643,7 @@ sub _checkSetupNotComplete {
|
||||
$ret .= "<td>";
|
||||
$ret .= $hqtxt{entry}{$lang}; # Entry Text
|
||||
|
||||
if (!$wedev) { ## no critic 'Cascading'
|
||||
$ret .= $hqtxt{cfd}{$lang};
|
||||
}
|
||||
elsif (!$strings) {
|
||||
if (!$strings) { ## no critic 'Cascading'
|
||||
$ret .= $hqtxt{ist}{$lang};
|
||||
}
|
||||
elsif (!$peaks) {
|
||||
@ -12690,6 +12670,9 @@ sub _checkSetupNotComplete {
|
||||
elsif (!$mdec && !isSolCastUsed ($hash) && !isVictronKiUsed ($hash)) {
|
||||
$ret .= $hqtxt{mta}{$lang};
|
||||
}
|
||||
elsif (!$wedev) {
|
||||
$ret .= $hqtxt{cfd}{$lang};
|
||||
}
|
||||
elsif (!$vrmcr && isVictronKiUsed ($hash)) {
|
||||
$ret .= $hqtxt{vrmcr}{$lang};
|
||||
}
|
||||
@ -16820,7 +16803,7 @@ sub listDataPool {
|
||||
return $ret;
|
||||
};
|
||||
|
||||
if ($htol =~ /solApiData|weatherApiData|statusApiData/xs) {
|
||||
if ($htol =~ /radiationApiData|weatherApiData|statusApiData/xs) {
|
||||
$h = $data{$type}{$name}{solcastapi};
|
||||
$h = $data{$type}{$name}{weatherapi} if($htol eq 'weatherApiData');
|
||||
$h = $data{$type}{$name}{statusapi} if($htol eq 'statusApiData');
|
||||
@ -17028,7 +17011,7 @@ sub checkPlantConfig {
|
||||
|
||||
my $result = { # Ergebnishash
|
||||
'String Configuration' => { 'state' => $ok, 'result' => '', 'note' => '', 'info' => 0, 'warn' => 0, 'fault' => 0 },
|
||||
'DWD Weather Properties' => { 'state' => $ok, 'result' => '', 'note' => '', 'info' => 0, 'warn' => 0, 'fault' => 0 },
|
||||
'Weather Properties' => { 'state' => $ok, 'result' => '', 'note' => '', 'info' => 0, 'warn' => 0, 'fault' => 0 },
|
||||
'Common Settings' => { 'state' => $ok, 'result' => '', 'note' => '', 'info' => 0, 'warn' => 0, 'fault' => 0 },
|
||||
'FTUI Widget Files' => { 'state' => $ok, 'result' => '', 'note' => '', 'info' => 0, 'warn' => 0, 'fault' => 0 },
|
||||
};
|
||||
@ -17099,44 +17082,44 @@ sub checkPlantConfig {
|
||||
next if(!$fcname && $step ne 1);
|
||||
|
||||
if (!$valid) {
|
||||
$result->{'DWD Weather Properties'}{state} = $nok;
|
||||
$result->{'Weather Properties'}{state} = $nok;
|
||||
|
||||
if (!$fcname) {
|
||||
$result->{'DWD Weather Properties'}{result} .= qq{No DWD device is defined in attribute "setupWeatherDev$step". <br>};
|
||||
$result->{'Weather Properties'}{result} .= qq{No DWD device is defined in attribute "setupWeatherDev$step". <br>};
|
||||
}
|
||||
else {
|
||||
$result->{'DWD Weather Properties'}{result} .= qq{The DWD device "$fcname" doesn't exist. <br>};
|
||||
$result->{'Weather Properties'}{result} .= qq{The DWD device "$fcname" doesn't exist. <br>};
|
||||
}
|
||||
|
||||
$result->{'DWD Weather Properties'}{fault} = 1;
|
||||
$result->{'Weather Properties'}{fault} = 1;
|
||||
}
|
||||
else {
|
||||
if (!$apiu) {
|
||||
$err = checkdwdattr ($name, $fcname, \@dweattrmust);
|
||||
|
||||
if ($err) {
|
||||
$result->{'DWD Weather Properties'}{state} = $nok;
|
||||
$result->{'DWD Weather Properties'}{result} .= $err.'<br>';
|
||||
$result->{'DWD Weather Properties'}{fault} = 1;
|
||||
$result->{'Weather Properties'}{state} = $nok;
|
||||
$result->{'Weather Properties'}{result} .= $err.'<br>';
|
||||
$result->{'Weather Properties'}{fault} = 1;
|
||||
}
|
||||
else {
|
||||
$mosm = AttrVal ($fcname, 'forecastRefresh', 6) == 6 ? 'MOSMIX_L' : 'MOSMIX_S';
|
||||
|
||||
if ($mosm eq 'MOSMIX_L') {
|
||||
$result->{'DWD Weather Properties'}{state} = $info;
|
||||
$result->{'DWD Weather Properties'}{result} .= qq(The device "$fcname" uses "$mosm" which is only updated by DWD every 6 hours. <br>);
|
||||
$result->{'DWD Weather Properties'}{info} = 1;
|
||||
$result->{'Weather Properties'}{state} = $info;
|
||||
$result->{'Weather Properties'}{result} .= qq(The device "$fcname" uses "$mosm" which is only updated by DWD every 6 hours. <br>);
|
||||
$result->{'Weather Properties'}{info} = 1;
|
||||
}
|
||||
|
||||
$result->{'DWD Weather Properties'}{result} .= $hqtxt{fulfd}{$lang}." ($hqtxt{attrib}{$lang}: setupWeatherDev$step)<br>";
|
||||
$result->{'Weather Properties'}{result} .= $hqtxt{fulfd}{$lang}." ($hqtxt{attrib}{$lang}: setupWeatherDev$step)<br>";
|
||||
}
|
||||
|
||||
$result->{'DWD Weather Properties'}{note} .= qq{checked parameters and attributes of device "$fcname": <br>};
|
||||
$result->{'DWD Weather Properties'}{note} .= 'forecastProperties -> '.join (',', @dweattrmust).'<br>';
|
||||
$result->{'DWD Weather Properties'}{note} .= 'forecastRefresh '.($mosm eq 'MOSMIX_L' ? '-> set attribute to below "6" if possible' : '').'<br>';
|
||||
$result->{'Weather Properties'}{note} .= qq{checked parameters and attributes of device "$fcname": <br>};
|
||||
$result->{'Weather Properties'}{note} .= 'forecastProperties -> '.join (',', @dweattrmust).'<br>';
|
||||
$result->{'Weather Properties'}{note} .= 'forecastRefresh '.($mosm eq 'MOSMIX_L' ? '-> set attribute to below "6" if possible' : '').'<br>';
|
||||
}
|
||||
else {
|
||||
$result->{'DWD Weather Properties'}{result} .= $hqtxt{fulfd}{$lang}." ($hqtxt{attrib}{$lang}: setupWeatherDev$step)<br>";
|
||||
$result->{'Weather Properties'}{result} .= $hqtxt{fulfd}{$lang}." ($hqtxt{attrib}{$lang}: setupWeatherDev$step)<br>";
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -17146,16 +17129,16 @@ sub checkPlantConfig {
|
||||
($err, $resh) = isWeatherAgeExceeded ( {name => $name, lang => $lang} );
|
||||
|
||||
if (!$err && $resh->{exceed}) {
|
||||
$result->{'DWD Weather Properties'}{state} = $warn;
|
||||
$result->{'DWD Weather Properties'}{note} .= qq{The Prediction time of Weather data is older than expected when using $resh->{mosmix}. <br>};
|
||||
$result->{'DWD Weather Properties'}{note} .= qq{Data time forecast: $resh->{fctime} <br>};
|
||||
$result->{'DWD Weather Properties'}{note} .= qq{Check the DWD device(s) for proper functioning of the data retrieval. <br>};
|
||||
$result->{'DWD Weather Properties'}{warn} = 1;
|
||||
$result->{'Weather Properties'}{state} = $warn;
|
||||
$result->{'Weather Properties'}{note} .= qq{The Prediction time of Weather data is older than expected when using $resh->{mosmix}. <br>};
|
||||
$result->{'Weather Properties'}{note} .= qq{Data time forecast: $resh->{fctime} <br>};
|
||||
$result->{'Weather Properties'}{note} .= qq{Check the DWD device(s) for proper functioning of the data retrieval. <br>};
|
||||
$result->{'Weather Properties'}{warn} = 1;
|
||||
}
|
||||
|
||||
$result->{'DWD Weather Properties'}{note} .= '<br>';
|
||||
$result->{'DWD Weather Properties'}{note} .= qq{checked global Weather parameters: <br>};
|
||||
$result->{'DWD Weather Properties'}{note} .= 'MOSMIX variant or ICON Forecast Model, Age of Weather data. <br>';
|
||||
$result->{'Weather Properties'}{note} .= '<br>';
|
||||
$result->{'Weather Properties'}{note} .= qq{checked global Weather parameters: <br>};
|
||||
$result->{'Weather Properties'}{note} .= 'MOSMIX variant or ICON Forecast Model, Age of Weather data. <br>';
|
||||
|
||||
## Check DWD Radiation Device
|
||||
###############################
|
||||
@ -17969,7 +17952,9 @@ sub setModel {
|
||||
elsif ($radapi =~ /OpenMeteoWorld-/xs) { $hash->{MODEL} = 'OpenMeteoWorldAPI'; }
|
||||
else { $hash->{MODEL} = 'DWD'; }
|
||||
|
||||
if ($wthapi =~ /OpenMeteo.*-API/xs) { $hash->{WEATHERMODEL} = 'OpenMeteo'; }
|
||||
if ($wthapi =~ /OpenMeteoDWDEnsemble-/xs) { $hash->{WEATHERMODEL} = 'OpenMeteoDWDEnsembleAPI'; }
|
||||
elsif ($wthapi =~ /OpenMeteoDWD-/xs) { $hash->{WEATHERMODEL} = 'OpenMeteoDWDAPI'; }
|
||||
elsif ($wthapi =~ /OpenMeteoWorld-/xs) { $hash->{WEATHERMODEL} = 'OpenMeteoWorldAPI'; }
|
||||
else { $hash->{WEATHERMODEL} = 'DWD'; }
|
||||
|
||||
return;
|
||||
@ -18654,9 +18639,11 @@ sub isWeatherDevValid {
|
||||
if ($fcname) { $valid = 1 }
|
||||
if (!$defs{$fcname} || $defs{$fcname}{TYPE} ne "DWD_OpenData") { $valid = '' }
|
||||
|
||||
if (isOpenMeteoUsed($hash) && $fcname =~ /^OpenMeteo/xs) {
|
||||
my ($rapi, $wapi) = getStatusApiName ($hash); # $rapi - Radiation-API, $wapi - Weather-API
|
||||
|
||||
if ($wapi =~ /^OpenMeteo/xs) {
|
||||
$valid = 1;
|
||||
$apiu = $fcname;
|
||||
$apiu = $wapi;
|
||||
}
|
||||
|
||||
return ($valid, $fcname, $apiu);
|
||||
@ -18992,6 +18979,9 @@ return ($err, $cname, $dswname);
|
||||
################################################################
|
||||
# Namen der Strahlungs-API und Wetter-API ermitteln.
|
||||
# Wird als Schlüssel in statusapi bzw. weatherapi verwendet.
|
||||
# Return:
|
||||
# $rapi - Name der Strahlungsdaten-API
|
||||
# $wapi - Name der Wetter-API
|
||||
################################################################
|
||||
sub getStatusApiName {
|
||||
my $hash = shift;
|
||||
@ -20166,8 +20156,7 @@ to ensure that the system configuration is correct.
|
||||
|
||||
<ul>
|
||||
<a id="SolarForecast-set-setupStringAzimuth"></a>
|
||||
<li><b>setupStringAzimuth <Stringname1>=<dir> [<Stringname2>=<dir> <Stringname3>=<dir> ...] </b> <br>
|
||||
(only model DWD, OpenMeteo*, ForecastSolarAPI) <br><br>
|
||||
<li><b>setupStringAzimuth <Stringname1>=<dir> [<Stringname2>=<dir> <Stringname3>=<dir> ...] </b> <br><br>
|
||||
|
||||
Alignment <dir> of the solar modules in the string "StringnameX". The string name is a key value of the
|
||||
<b>setupInverterStrings</b> attribute. <br>
|
||||
@ -20204,8 +20193,7 @@ to ensure that the system configuration is correct.
|
||||
|
||||
<ul>
|
||||
<a id="SolarForecast-set-setupStringDeclination"></a>
|
||||
<li><b>setupStringDeclination <Stringname1>=<Angle> [<Stringname2>=<Angle> <Stringname3>=<Angle> ...] </b> <br>
|
||||
(only model DWD, ForecastSolarAPI) <br><br>
|
||||
<li><b>setupStringDeclination <Stringname1>=<Angle> [<Stringname2>=<Angle> <Stringname3>=<Angle> ...] </b> <br><br>
|
||||
|
||||
Tilt angle of the solar modules. The string name is a key value of the attribute <b>setupInverterStrings</b>. <br>
|
||||
Possible angles of inclination are: 0,5,10,15,20,25,30,35,40,45,50,55,60,65,70,75,80,85,90
|
||||
@ -20784,8 +20772,8 @@ to ensure that the system configuration is correct.
|
||||
<br>
|
||||
|
||||
<ul>
|
||||
<a id="SolarForecast-get-solApiData"></a>
|
||||
<li><b>solApiData </b> <br><br>
|
||||
<a id="SolarForecast-get-radiationApiData"></a>
|
||||
<li><b>radiationApiData </b> <br><br>
|
||||
|
||||
Lists the radiation data saved in the context of the API call.
|
||||
The forecast data supplied by the API regarding the global radiation Rad1h and the predicted PV yield (Wh)
|
||||
@ -22122,9 +22110,10 @@ to ensure that the system configuration is correct.
|
||||
<li><b>setupRadiationAPI </b> <br><br>
|
||||
|
||||
Defines the source for the delivery of the solar radiation data. You can select a device of the type DWD_OpenData or
|
||||
an implemented API can be selected. <br>
|
||||
<b>Note:</b> If OpenMeteoDWD-API is set in the 'setupWeatherDev1' attribute, no radiation data service other than
|
||||
OpenMeteoDWD-API can be selected. <br><br>
|
||||
an implemented API can be selected. <br><br>
|
||||
|
||||
<b>Note:</b> If an OpenMeteo API is also set in the 'setupWeatherDev1' attribute, the settings of both attributes
|
||||
are harmonized, whereby the setting of 'setupRadiationAPI' is leading. <br><br>
|
||||
|
||||
<b>OpenMeteoDWD-API</b> <br>
|
||||
|
||||
@ -22258,9 +22247,10 @@ to ensure that the system configuration is correct.
|
||||
<li><b>setupWeatherDevX </b> <br><br>
|
||||
|
||||
Specifies the device or API for providing the required weather data (cloud cover, precipitation, etc.).<br>
|
||||
The attribute 'setupWeatherDev1' specifies the leading weather service and is mandatory.<br>
|
||||
If an Open-Meteo API is selected in the 'setupWeatherDev1' attribute, this Open-Meteo service is automatically set as the
|
||||
source of the radiation data (Attribute setupRadiationAPI). <br><br>
|
||||
The attribute 'setupWeatherDev1' specifies the leading weather service and is mandatory.<br><br>
|
||||
|
||||
<b>Note:</b> If an OpenMeteo API is also set in the 'setupRadiationAPI' attribute, the settings of both attributes
|
||||
are harmonized, whereby the setting of 'setupRadiationAPI' is leading. <br><br>
|
||||
|
||||
<b>OpenMeteoDWD-API</b> <br>
|
||||
|
||||
@ -22552,8 +22542,7 @@ die ordnungsgemäße Anlagenkonfiguration geprüft werden.
|
||||
|
||||
<ul>
|
||||
<a id="SolarForecast-set-setupStringAzimuth"></a>
|
||||
<li><b>setupStringAzimuth <Stringname1>=<dir> [<Stringname2>=<dir> <Stringname3>=<dir> ...] </b> <br>
|
||||
(nur Model DWD, OpenMeteo*, ForecastSolarAPI) <br><br>
|
||||
<li><b>setupStringAzimuth <Stringname1>=<dir> [<Stringname2>=<dir> <Stringname3>=<dir> ...] </b> <br><br>
|
||||
|
||||
Ausrichtung <dir> der Solarmodule im String "StringnameX". Der Stringname ist ein Schlüsselwert des
|
||||
Attributs <b>setupInverterStrings</b>. <br>
|
||||
@ -22590,8 +22579,7 @@ die ordnungsgemäße Anlagenkonfiguration geprüft werden.
|
||||
|
||||
<ul>
|
||||
<a id="SolarForecast-set-setupStringDeclination"></a>
|
||||
<li><b>setupStringDeclination <Stringname1>=<Winkel> [<Stringname2>=<Winkel> <Stringname3>=<Winkel> ...] </b> <br>
|
||||
(nur Model DWD, ForecastSolarAPI) <br><br>
|
||||
<li><b>setupStringDeclination <Stringname1>=<Winkel> [<Stringname2>=<Winkel> <Stringname3>=<Winkel> ...] </b> <br><br>
|
||||
|
||||
Neigungswinkel der Solarmodule. Der Stringname ist ein Schlüsselwert des Attributs <b>setupInverterStrings</b>. <br>
|
||||
Mögliche Neigungswinkel sind: 0,5,10,15,20,25,30,35,40,45,50,55,60,65,70,75,80,85,90
|
||||
@ -23179,8 +23167,8 @@ die ordnungsgemäße Anlagenkonfiguration geprüft werden.
|
||||
<br>
|
||||
|
||||
<ul>
|
||||
<a id="SolarForecast-get-solApiData"></a>
|
||||
<li><b>solApiData </b> <br><br>
|
||||
<a id="SolarForecast-get-radiationApiData"></a>
|
||||
<li><b>radiationApiData </b> <br><br>
|
||||
|
||||
Listet die im Kontext des API-Abrufs gespeicherten Strahlungsdaten auf.
|
||||
Die von der API gelieferten Vorhersagedaten bzgl. der Globalstrahlung Rad1h und des auf einen String bezogenen
|
||||
@ -24517,9 +24505,10 @@ die ordnungsgemäße Anlagenkonfiguration geprüft werden.
|
||||
<li><b>setupRadiationAPI </b> <br><br>
|
||||
|
||||
Legt die Quelle zur Lieferung der solaren Strahlungsdaten fest. Es kann ein Device vom Typ DWD_OpenData oder
|
||||
eine implementierte API eines Dienstes ausgewählt werden. <br>
|
||||
<b>Hinweis:</b> Ist eine OpenMeteo API im Attribut 'setupWeatherDev1' gesetzt, kann kein anderer Strahlungsdatendienst als
|
||||
diese OpenMeteo API ausgewählt werden. <br><br>
|
||||
eine implementierte API eines Dienstes ausgewählt werden. <br><br>
|
||||
|
||||
<b>Hinweis:</b> Ist im Attribut 'setupWeatherDev1' ebenfalls eine OpenMeteo API gesetzt, werden die Einstellungen
|
||||
beider Attribute harmonisiert wobei die Einstellung von 'setupRadiationAPI' führend ist. <br><br>
|
||||
|
||||
<b>OpenMeteoDWD-API</b> <br>
|
||||
|
||||
@ -24655,9 +24644,10 @@ die ordnungsgemäße Anlagenkonfiguration geprüft werden.
|
||||
<li><b>setupWeatherDevX </b> <br><br>
|
||||
|
||||
Gibt das Gerät oder die API zur Lieferung der erforderlichen Wetterdaten (Wolkendecke, Niederschlag usw.) an.<br>
|
||||
Das Attribut 'setupWeatherDev1' definiert den führenden Wetterdienst und ist zwingend erforderlich.<br>
|
||||
Ist eine Open-Meteo API im Attribut 'setupWeatherDev1' ausgewählt, wird dieser Open-Meteo Dienst automatisch auch als Quelle
|
||||
der Strahlungsdaten (Attribut setupRadiationAPI) eingestellt. <br><br>
|
||||
Das Attribut 'setupWeatherDev1' definiert den führenden Wetterdienst und ist zwingend erforderlich. <br><br>
|
||||
|
||||
<b>Hinweis:</b> Ist im Attribut 'setupRadiationAPI' ebenfalls eine OpenMeteo API gesetzt, werden die Einstellungen
|
||||
beider Attribute harmonisiert wobei die Einstellung von 'setupRadiationAPI' führend ist. <br><br>
|
||||
|
||||
<b>OpenMeteoDWD-API</b> <br>
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user