76_SolarForecast: version 1.55.0, changes and fixes of V 1.54.7
git-svn-id: https://svn.fhem.de/fhem/trunk@30178 2b470e98-0d58-463d-a4d8-8e2adae1ed80
This commit is contained in:
@@ -1,5 +1,6 @@
|
|||||||
# Add changes at the top of the list. Keep it in ASCII, and 80-char wide.
|
# 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
|
# Do not insert empty lines here, update check depends on it
|
||||||
|
- change: 76_SolarForecast: version 1.55.0, changes and fixes of V 1.54.7
|
||||||
- feature: 76_SolarForecast: Version 1.54.7
|
- feature: 76_SolarForecast: Version 1.54.7
|
||||||
- feature: 76_SolarForecast: show surplus method/result in clegend mouse-over
|
- feature: 76_SolarForecast: show surplus method/result in clegend mouse-over
|
||||||
- feature: 93_DbRep: attr device/reading can now be entered in multiple lines
|
- feature: 93_DbRep: attr device/reading can now be entered in multiple lines
|
||||||
|
|||||||
@@ -160,6 +160,10 @@ BEGIN {
|
|||||||
|
|
||||||
# Versions History intern
|
# Versions History intern
|
||||||
my %vNotesIntern = (
|
my %vNotesIntern = (
|
||||||
|
"1.55.0" => "06.08.2025 DWD-Weather and DWD-Radiation device new minimum value of attr 'forecastDays' is 2 ".
|
||||||
|
"checkPlantConfig: check forecastDays of new minimum value ".
|
||||||
|
"___createOpenMeteoURL: set forecast_hours=72, bugfix of V 1.54.7 ".
|
||||||
|
"Nexthours: max 72 hours available but not more than 3 days ",
|
||||||
"1.54.7" => "01.08.2025 _transferAPIRadiationValues: Extension of Nexthours content up to 48 hours into the future ".
|
"1.54.7" => "01.08.2025 _transferAPIRadiationValues: Extension of Nexthours content up to 48 hours into the future ".
|
||||||
"attr graphicBeamHeightLevelX is obsolete -> use graphicControl instead ".
|
"attr graphicBeamHeightLevelX is obsolete -> use graphicControl instead ".
|
||||||
"attr graphicControl new key beamHeightlevel ",
|
"attr graphicControl new key beamHeightlevel ",
|
||||||
@@ -424,6 +428,9 @@ use constant {
|
|||||||
AIACCLOWLIM => 50, # untere Abweichungsgrenze (%) AI 'Accurate' von API Prognose
|
AIACCLOWLIM => 50, # untere Abweichungsgrenze (%) AI 'Accurate' von API Prognose
|
||||||
AIACCTRNMIN => 3500, # Mindestanzahl KI Regeln für Verwendung "KI Accurate"
|
AIACCTRNMIN => 3500, # Mindestanzahl KI Regeln für Verwendung "KI Accurate"
|
||||||
|
|
||||||
|
MAXNEXTHOURS => 71, # max. Anzahl Stunden der Wertebasis (Start mit 0 -> 72h) z.B. in Nexthours
|
||||||
|
MAXNEXTDAYS => 2, # max. Anzahl volle Tage in NextHours (Start mit 0 -> 3d)
|
||||||
|
DWDFCDAYSMIN => 2, # Mindestwert Attr 'forecastDays' im DWD-Device
|
||||||
SOLAPIREPDEF => 3600, # default Abrufintervall SolCast API (s)
|
SOLAPIREPDEF => 3600, # default Abrufintervall SolCast API (s)
|
||||||
FORAPIREPDEF => 900, # default Abrufintervall ForecastSolar API (s)
|
FORAPIREPDEF => 900, # default Abrufintervall ForecastSolar API (s)
|
||||||
OMETEOREPDEF => 900, # default Abrufintervall Open-Meteo API (s)
|
OMETEOREPDEF => 900, # default Abrufintervall Open-Meteo API (s)
|
||||||
@@ -975,7 +982,7 @@ my %hqtxt = ( # H
|
|||||||
strok => { EN => qq{Congratulations 😊, the system configuration is error-free. Please note any information (<I>).},
|
strok => { EN => qq{Congratulations 😊, the system configuration is error-free. Please note any information (<I>).},
|
||||||
DE => qq{Herzlichen Glückwunsch 😊, die Anlagenkonfiguration ist fehlerfrei. Bitte eventuelle Hinweise (<I>) beachten.} },
|
DE => qq{Herzlichen Glückwunsch 😊, die Anlagenkonfiguration ist fehlerfrei. Bitte eventuelle Hinweise (<I>) beachten.} },
|
||||||
strwn => { EN => qq{Looks quite good 😐, the system configuration is basically OK. Please note the warnings (<W>).},
|
strwn => { EN => qq{Looks quite good 😐, the system configuration is basically OK. Please note the warnings (<W>).},
|
||||||
DE => qq{Sieht ganz gut aus 😐, die Anlagenkonfiguration ist prinzipiell in Ordnung. Bitte beachten Sie die Warnungen (<W>).} },
|
DE => qq{Sieht ganz gut aus 😐, die Anlagenkonfiguration ist prinzipiell in Ordnung. Bitte beachte die Warnungen (<W>).} },
|
||||||
strnok => { EN => qq{Oh no 🙁, the system configuration is incorrect. Please check the settings and notes!},
|
strnok => { EN => qq{Oh no 🙁, the system configuration is incorrect. Please check the settings and notes!},
|
||||||
DE => qq{Oh nein 😢, die Anlagenkonfiguration ist fehlerhaft. Bitte überprüfen Sie die Einstellungen und Hinweise!} },
|
DE => qq{Oh nein 😢, die Anlagenkonfiguration ist fehlerhaft. Bitte überprüfen Sie die Einstellungen und Hinweise!} },
|
||||||
pstate => { EN => qq{Planning status: <pstate><br>Info: <supplmnt><br>Mode: <mode><br>On: <start><br>Off: <stop><br>Remaining lock time: <RLT> seconds},
|
pstate => { EN => qq{Planning status: <pstate><br>Info: <supplmnt><br>Mode: <mode><br>On: <start><br>Off: <stop><br>Remaining lock time: <RLT> seconds},
|
||||||
@@ -1378,7 +1385,7 @@ my %hcsr = (
|
|||||||
BatWeightedTotalSOC => { fnr => 2, fn => \&CurrentVal, par => 'batsoctotal', par1 => '', unit => ' %', def => 0 },
|
BatWeightedTotalSOC => { fnr => 2, fn => \&CurrentVal, par => 'batsoctotal', par1 => '', unit => ' %', def => 0 },
|
||||||
SunHours_Remain => { fnr => 5, fn => \&CurrentVal, par => '', par1 => '', unit => '', def => 0 }, # fnr => 3 -> Custom Calc
|
SunHours_Remain => { fnr => 5, fn => \&CurrentVal, par => '', par1 => '', unit => '', def => 0 }, # fnr => 3 -> Custom Calc
|
||||||
SunMinutes_Remain => { fnr => 5, fn => \&CurrentVal, par => '', par1 => '', unit => '', def => 0 },
|
SunMinutes_Remain => { fnr => 5, fn => \&CurrentVal, par => '', par1 => '', unit => '', def => 0 },
|
||||||
dayAfterTomorrowPVforecast => { fnr => 5, fn => \&RadiationAPIVal, par => 'pv_estimate50', par1 => '', unit => '', def => 0 },
|
dayAfterTomorrowPVforecast => { fnr => 5, fn => \&CurrentVal, par => 'dayAfterTomorrowPVfc', par1 => '', unit => ' Wh', def => 0 },
|
||||||
todayGridFeedIn => { fnr => 5, fn => \&CircularVal, par => 99, par1 => '', unit => '', def => 0 },
|
todayGridFeedIn => { fnr => 5, fn => \&CircularVal, par => 99, par1 => '', unit => '', def => 0 },
|
||||||
todayGridConsumption => { fnr => 5, fn => \&CircularVal, par => 99, par1 => '', unit => '', def => 0 },
|
todayGridConsumption => { fnr => 5, fn => \&CircularVal, par => 99, par1 => '', unit => '', def => 0 },
|
||||||
todayNotOwnerConsumption => { fnr => 5, fn => \&CircularVal, par => 99, par1 => 'todayConsumption', unit => ' Wh', def => 0 },
|
todayNotOwnerConsumption => { fnr => 5, fn => \&CircularVal, par => 99, par1 => 'todayConsumption', unit => ' Wh', def => 0 },
|
||||||
@@ -3681,7 +3688,7 @@ sub __getDWDSolarData {
|
|||||||
my $raname = AttrVal ($name, 'setupRadiationAPI', ''); # Radiation Forecast API
|
my $raname = AttrVal ($name, 'setupRadiationAPI', ''); # Radiation Forecast API
|
||||||
return if(!$raname || !$defs{$raname});
|
return if(!$raname || !$defs{$raname});
|
||||||
|
|
||||||
my $fcdays = AttrVal ($raname, 'forecastDays', 1); # Anzahl Forecast Days in DWD Device
|
my $fcdays = AttrVal ($raname, 'forecastDays', 2); # Anzahl Forecast Days in DWD Device
|
||||||
my $stime = $date.' 00:00:00'; # Startzeit Soll Übernahmedaten
|
my $stime = $date.' 00:00:00'; # Startzeit Soll Übernahmedaten
|
||||||
my $sts = timestringToTimestamp ($stime);
|
my $sts = timestringToTimestamp ($stime);
|
||||||
my @strings = sort keys %{$data{$name}{strings}};
|
my @strings = sort keys %{$data{$name}{strings}};
|
||||||
@@ -3698,10 +3705,10 @@ sub __getDWDSolarData {
|
|||||||
|
|
||||||
debugLog ($paref, "apiCall", "DWD API - collect DWD Radiation data with start >$stime<- device: $raname =>");
|
debugLog ($paref, "apiCall", "DWD API - collect DWD Radiation data with start >$stime<- device: $raname =>");
|
||||||
|
|
||||||
my $end = (24 + $fcdays * 24) - 1; # default 47
|
my $end = (24 + $fcdays * 24) - 1; # V 1.55.0 -> default 71
|
||||||
|
|
||||||
for my $num (0..$end) { # V 1.36.0
|
for my $num (0..$end) { # V 1.36.0
|
||||||
my ($fd,$fh) = calcDayHourMove (0, $num);
|
my ($fd, $fh) = calcDayHourMove (0, $num);
|
||||||
next if($fh == 24);
|
next if($fh == 24);
|
||||||
|
|
||||||
my $dateTime = strftime "%Y-%m-%d %H:%M:00", localtime($sts + (3600 * $num)); # abzurufendes Datum ' ' Zeit
|
my $dateTime = strftime "%Y-%m-%d %H:%M:00", localtime($sts + (3600 * $num)); # abzurufendes Datum ' ' Zeit
|
||||||
@@ -4918,7 +4925,7 @@ sub ___createOpenMeteoURL {
|
|||||||
$url .= "&latitude=".$lat;
|
$url .= "&latitude=".$lat;
|
||||||
$url .= "&longitude=".$lon;
|
$url .= "&longitude=".$lon;
|
||||||
$url .= "&hourly=temperature_2m,rain,weather_code,cloud_cover,is_day,global_tilted_irradiance,shortwave_radiation";
|
$url .= "&hourly=temperature_2m,rain,weather_code,cloud_cover,is_day,global_tilted_irradiance,shortwave_radiation";
|
||||||
$url .= "&forecast_hours=48";
|
$url .= "&forecast_hours=72";
|
||||||
$url .= "&forecast_days=2";
|
$url .= "&forecast_days=2";
|
||||||
$url .= "&tilt=".$tilt;
|
$url .= "&tilt=".$tilt;
|
||||||
$url .= "&azimuth=".$az;
|
$url .= "&azimuth=".$az;
|
||||||
@@ -4935,7 +4942,7 @@ sub ___createOpenMeteoURL {
|
|||||||
$url .= "¤t=temperature_2m,weather_code,rain,cloud_cover";
|
$url .= "¤t=temperature_2m,weather_code,rain,cloud_cover";
|
||||||
$url .= "&minutely_15=rain,global_tilted_irradiance,shortwave_radiation";
|
$url .= "&minutely_15=rain,global_tilted_irradiance,shortwave_radiation";
|
||||||
$url .= "&daily=sunrise,sunset";
|
$url .= "&daily=sunrise,sunset";
|
||||||
$url .= "&forecast_hours=48";
|
$url .= "&forecast_hours=72";
|
||||||
$url .= "&forecast_days=2";
|
$url .= "&forecast_days=2";
|
||||||
$url .= "&tilt=".$tilt;
|
$url .= "&tilt=".$tilt;
|
||||||
$url .= "&azimuth=".$az;
|
$url .= "&azimuth=".$az;
|
||||||
@@ -5074,7 +5081,9 @@ sub _getdata {
|
|||||||
my $name = $paref->{name};
|
my $name = $paref->{name};
|
||||||
my $hash = $defs{$name};
|
my $hash = $defs{$name};
|
||||||
|
|
||||||
return centralTask ($hash);
|
centralTask ($hash);
|
||||||
|
|
||||||
|
return 'Data cycle triggered, watch readings';
|
||||||
}
|
}
|
||||||
|
|
||||||
###############################################################
|
###############################################################
|
||||||
@@ -7538,7 +7547,7 @@ sub _attrWeatherDev { ## no critic "not used"
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ($aVal !~ /-API$/xs) { # Attribute des DWD-Devices prüfen
|
if ($aVal !~ /-API$/xs) { # Attribute des DWD-Devices prüfen
|
||||||
my $err = checkdwdattr ($name, $aVal, \@dweattrmust);
|
my ($err, $warnmsg) = checkdwdattr ($name, $aVal, \@dweattrmust);
|
||||||
return $err if($err);
|
return $err if($err);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -9324,9 +9333,20 @@ sub _specialActivities {
|
|||||||
$gcon = ReadingsNum ($name, "Today_Hour".sprintf("%02d",$chour)."_GridConsumption", 0);
|
$gcon = ReadingsNum ($name, "Today_Hour".sprintf("%02d",$chour)."_GridConsumption", 0);
|
||||||
storeReading ('LastHourGridconsumptionReal', "$gcon Wh", $ts1);
|
storeReading ('LastHourGridconsumptionReal', "$gcon Wh", $ts1);
|
||||||
|
|
||||||
|
## überhängende Daten in Nexthours löschen
|
||||||
|
############################################
|
||||||
|
for my $num (0..MAXNEXTHOURS) {
|
||||||
|
my ($fd, $fh) = calcDayHourMove ($chour, $num);
|
||||||
|
my $nhtstr = 'NextHour'.(sprintf "%02d", $num);
|
||||||
|
|
||||||
|
if ($fd > 2 && exists $data{$name}{nexthours}{$nhtstr}) {
|
||||||
|
delete $data{$name}{nexthours}{$nhtstr};
|
||||||
|
next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
## Planungsdaten spezifisch löschen (Anfang und Ende nicht am selben Tag)
|
## Planungsdaten spezifisch löschen (Anfang und Ende nicht am selben Tag)
|
||||||
##########################################################################
|
##########################################################################
|
||||||
|
|
||||||
for my $c (keys %{$data{$name}{consumers}}) {
|
for my $c (keys %{$data{$name}{consumers}}) {
|
||||||
next if(ConsumerVal ($hash, $c, 'plandelete', 'regular') eq 'regular');
|
next if(ConsumerVal ($hash, $c, 'plandelete', 'regular') eq 'regular');
|
||||||
|
|
||||||
@@ -9701,9 +9721,9 @@ sub _transferWeatherValues {
|
|||||||
|
|
||||||
__mergeDataWeather ($paref); # Wetterdaten zusammenfügen
|
__mergeDataWeather ($paref); # Wetterdaten zusammenfügen
|
||||||
|
|
||||||
for my $num (0..46) {
|
for my $num (0..MAXNEXTHOURS) {
|
||||||
my ($fd, $fh) = calcDayHourMove ($chour, $num);
|
my ($fd, $fh) = calcDayHourMove ($chour, $num);
|
||||||
last if($fd > 1);
|
last if($fd > MAXNEXTDAYS);
|
||||||
|
|
||||||
my $wid = $data{$name}{weatherdata}{"fc${fd}_${fh}"}{merge}{ww}; # signifikantes Wetter = Wetter ID
|
my $wid = $data{$name}{weatherdata}{"fc${fd}_${fh}"}{merge}{ww}; # signifikantes Wetter = Wetter ID
|
||||||
my $wwd = $data{$name}{weatherdata}{"fc${fd}_${fh}"}{merge}{wwd}; # Wetter Beschreibung
|
my $wwd = $data{$name}{weatherdata}{"fc${fd}_${fh}"}{merge}{wwd}; # Wetter Beschreibung
|
||||||
@@ -9717,7 +9737,7 @@ sub _transferWeatherValues {
|
|||||||
debugLog ($paref, 'collectData_long', "Adjust cloud cover ratio (wcc) due to significant weather (ww) - ww: $wid -> wcc: $wcc");
|
debugLog ($paref, 'collectData_long', "Adjust cloud cover ratio (wcc) due to significant weather (ww) - ww: $wid -> wcc: $wcc");
|
||||||
}
|
}
|
||||||
|
|
||||||
my $nhtstr = "NextHour".sprintf "%02d", $num;
|
my $nhtstr = 'NextHour'.(sprintf "%02d", $num);
|
||||||
$data{$name}{nexthours}{$nhtstr}{weatherid} = $wid;
|
$data{$name}{nexthours}{$nhtstr}{weatherid} = $wid;
|
||||||
$data{$name}{nexthours}{$nhtstr}{wcc} = $wcc;
|
$data{$name}{nexthours}{$nhtstr}{wcc} = $wcc;
|
||||||
$data{$name}{nexthours}{$nhtstr}{rr1c} = $rr1c;
|
$data{$name}{nexthours}{$nhtstr}{rr1c} = $rr1c;
|
||||||
@@ -9774,14 +9794,17 @@ sub __readDataWeather {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
my $err = checkdwdattr ($name, $fcname, \@dweattrmust);
|
my ($err, $warnmsg) = checkdwdattr ($name, $fcname, \@dweattrmust);
|
||||||
$paref->{state} = $err if($err);
|
$paref->{state} = $err if($err);
|
||||||
|
|
||||||
|
my $fcdays = AttrVal ($fcname, 'forecastDays', 2); # Anzahl Forecast Days in DWD Device
|
||||||
|
my $end = (24 + $fcdays * 24) - 1; # V 1.55.0 -> default 71
|
||||||
|
|
||||||
debugLog ($paref, 'collectData_long', "collect Weather data step $step - device: $fcname =>");
|
debugLog ($paref, 'collectData_long', "collect Weather data step $step - device: $fcname =>");
|
||||||
|
|
||||||
for my $n (0..46) {
|
for my $n (0..$end) {
|
||||||
my ($fd, $fh) = calcDayHourMove ($chour, $n);
|
my ($fd, $fh) = calcDayHourMove ($chour, $n);
|
||||||
last if($fd > 1);
|
last if($fd > MAXNEXTDAYS);
|
||||||
|
|
||||||
my $wid = ReadingsNum ($fcname, "fc${fd}_${fh}_ww", undef); # Signifikantes Wetter zum Vorhersagezeitpunkt
|
my $wid = ReadingsNum ($fcname, "fc${fd}_${fh}_ww", undef); # Signifikantes Wetter zum Vorhersagezeitpunkt
|
||||||
my $wwd = ReadingsVal ($fcname, "fc${fd}_${fh}_wwd", ''); # Wetter Beschreibung
|
my $wwd = ReadingsVal ($fcname, "fc${fd}_${fh}_wwd", ''); # Wetter Beschreibung
|
||||||
@@ -9808,8 +9831,6 @@ sub __readDataWeather {
|
|||||||
$fd1++;
|
$fd1++;
|
||||||
}
|
}
|
||||||
|
|
||||||
last if($fd1 > 1);
|
|
||||||
|
|
||||||
my $rr1c = ReadingsNum ($fcname, "fc${fd1}_${fh1}_RR1c", 0); # Gesamtniederschlag (1-stündig) letzte 1 Stunde -> wir schuen in die Zukunft
|
my $rr1c = ReadingsNum ($fcname, "fc${fd1}_${fh1}_RR1c", 0); # Gesamtniederschlag (1-stündig) letzte 1 Stunde -> wir schuen in die Zukunft
|
||||||
|
|
||||||
debugLog ($paref, 'collectData_long', "Weather $step: fc${fd}_${fh}, don: $sunup, wid: ".(defined $wid ? $wid : '<undef>').", RR1c: $rr1c, TTT: ".(defined $temp ? $temp : '<undef>').", Neff: ".(defined $neff ? $neff : '<undef>'));
|
debugLog ($paref, 'collectData_long', "Weather $step: fc${fd}_${fh}, don: $sunup, wid: ".(defined $wid ? $wid : '<undef>').", RR1c: $rr1c, TTT: ".(defined $temp ? $temp : '<undef>').", Neff: ".(defined $neff ? $neff : '<undef>'));
|
||||||
@@ -9841,7 +9862,7 @@ sub ___readDataWeatherAPI {
|
|||||||
my ($rapi, $wapi) = getStatusApiName ($hash);
|
my ($rapi, $wapi) = getStatusApiName ($hash);
|
||||||
|
|
||||||
for my $idx (sort keys %{$data{$name}{weatherapi}{$wapi}}) {
|
for my $idx (sort keys %{$data{$name}{weatherapi}{$wapi}}) {
|
||||||
if ($idx =~ /^fc?([0-9]{1,2})_?([0-9]{1,2})$/xs) { # valider Weather API Index
|
if ($idx =~ /^fc(?:[0-2])_(?:[0-9]|1[0-9]|2[0-3])$/xs) { # valider Weather API Index
|
||||||
my $rr1c = WeatherAPIVal ($hash, $wapi, $idx, 'rr1c', undef);
|
my $rr1c = WeatherAPIVal ($hash, $wapi, $idx, 'rr1c', undef);
|
||||||
my $wid = WeatherAPIVal ($hash, $wapi, $idx, 'ww', undef);
|
my $wid = WeatherAPIVal ($hash, $wapi, $idx, 'ww', undef);
|
||||||
my $neff = WeatherAPIVal ($hash, $wapi, $idx, 'neff', undef);
|
my $neff = WeatherAPIVal ($hash, $wapi, $idx, 'neff', undef);
|
||||||
@@ -10250,12 +10271,14 @@ sub _transferAPIRadiationValues {
|
|||||||
$invcapsum += InverterVal ($name, $in, 'invertercap', 0); # Limit Leistungssumme aller Inverters
|
$invcapsum += InverterVal ($name, $in, 'invertercap', 0); # Limit Leistungssumme aller Inverters
|
||||||
}
|
}
|
||||||
|
|
||||||
for my $num (0..47) {
|
for my $num (0..MAXNEXTHOURS) {
|
||||||
my ($fd,$fh) = calcDayHourMove ($chour, $num);
|
my ($fd, $fh) = calcDayHourMove ($chour, $num);
|
||||||
|
last if($fd > MAXNEXTDAYS);
|
||||||
|
|
||||||
my $fh1 = $fh + 1;
|
my $fh1 = $fh + 1;
|
||||||
my $wantts = (timestringToTimestamp ($date.' '.$chour.':00:00')) + ($num * 3600);
|
my $wantts = (timestringToTimestamp ($date.' '.$chour.':00:00')) + ($num * 3600);
|
||||||
my $wantdt = (timestampToTimestring ($wantts, $lang))[1];
|
my $wantdt = (timestampToTimestring ($wantts, $lang))[1];
|
||||||
my $nhtstr = 'NextHour'.sprintf "%02d", $num;
|
my $nhtstr = 'NextHour'.(sprintf "%02d", $num);
|
||||||
my ($wtday, $wthour) = $wantdt =~ /(\d{2})\s(\d{2}):/xs;
|
my ($wtday, $wthour) = $wantdt =~ /(\d{2})\s(\d{2}):/xs;
|
||||||
my $hod = sprintf "%02d", int $wthour + 1; # Stunde des Tages
|
my $hod = sprintf "%02d", int $wthour + 1; # Stunde des Tages
|
||||||
my $rad1h = RadiationAPIVal ($name, '?All', $wantdt, 'Rad1h', undef);
|
my $rad1h = RadiationAPIVal ($name, '?All', $wantdt, 'Rad1h', undef);
|
||||||
@@ -10594,7 +10617,7 @@ sub ___readCandQ {
|
|||||||
my $crang = 'simple';
|
my $crang = 'simple';
|
||||||
my $hc;
|
my $hc;
|
||||||
|
|
||||||
delete $data{$name}{nexthours}{"NextHour".sprintf("%02d",$num)}{cloudrange};
|
delete $data{$name}{nexthours}{'NextHour'.sprintf("%02d",$num)}{cloudrange};
|
||||||
|
|
||||||
if ($acu =~ /on_complex/xs) { # Autokorrektur complex soll genutzt werden
|
if ($acu =~ /on_complex/xs) { # Autokorrektur complex soll genutzt werden
|
||||||
$crang = cloud2bin ($wcc); # Range errechnen
|
$crang = cloud2bin ($wcc); # Range errechnen
|
||||||
@@ -10602,10 +10625,10 @@ sub ___readCandQ {
|
|||||||
my $daref = $data{$name}{circular}{$hod}{'pvrl_'.$sabin}{"$crang"};
|
my $daref = $data{$name}{circular}{$hod}{'pvrl_'.$sabin}{"$crang"};
|
||||||
|
|
||||||
if (ref $daref eq 'ARRAY') {
|
if (ref $daref eq 'ARRAY') {
|
||||||
$data{$name}{nexthours}{"NextHour".sprintf("%02d",$num)}{DaysInRange} = scalar (@{$daref}); # Anzahl Tage im selben Wetterbereich speichern
|
$data{$name}{nexthours}{'NextHour'.sprintf("%02d",$num)}{DaysInRange} = scalar (@{$daref}); # Anzahl Tage im selben Wetterbereich speichern
|
||||||
}
|
}
|
||||||
|
|
||||||
$data{$name}{nexthours}{"NextHour".sprintf("%02d",$num)}{cloudrange} = $crang;
|
$data{$name}{nexthours}{'NextHour'.sprintf("%02d",$num)}{cloudrange} = $crang;
|
||||||
}
|
}
|
||||||
elsif ($acu =~ /on_simple/xs) {
|
elsif ($acu =~ /on_simple/xs) {
|
||||||
($hc, $hq) = CircularSunCloudkorrVal ($hash, $hod, $sabin, 'simple', undef); # Korrekturfaktor/Qualität der Stunde des Tages (simple)
|
($hc, $hq) = CircularSunCloudkorrVal ($hash, $hod, $sabin, 'simple', undef); # Korrekturfaktor/Qualität der Stunde des Tages (simple)
|
||||||
@@ -10630,7 +10653,7 @@ sub ___readCandQ {
|
|||||||
debugLog ($paref, 'pvCorrectionRead', "$flex - fd: $fd, hod: $hod, Sun Altitude Bin: $sabin, Cloud range: $crang, corrf: $hc, quality: $hq");
|
debugLog ($paref, 'pvCorrectionRead', "$flex - fd: $fd, hod: $hod, Sun Altitude Bin: $sabin, Cloud range: $crang, corrf: $hc, quality: $hq");
|
||||||
}
|
}
|
||||||
|
|
||||||
$data{$name}{nexthours}{"NextHour".sprintf("%02d",$num)}{pvcorrf} = $hc."/".$hq;
|
$data{$name}{nexthours}{'NextHour'.sprintf("%02d",$num)}{pvcorrf} = $hc."/".$hq;
|
||||||
|
|
||||||
if ($fd == 0 && $hod) {
|
if ($fd == 0 && $hod) {
|
||||||
writeToHistory ( { paref => $paref, key => 'pvcorrfactor', val => $hc.'/'.$hq, hour => $hod } );
|
writeToHistory ( { paref => $paref, key => 'pvcorrfactor', val => $hc.'/'.$hq, hour => $hod } );
|
||||||
@@ -11614,7 +11637,7 @@ sub _batChargeMgmt {
|
|||||||
## Auswertung für jede kommende Stunde
|
## Auswertung für jede kommende Stunde
|
||||||
########################################
|
########################################
|
||||||
for my $num (0..47) {
|
for my $num (0..47) {
|
||||||
my ($fd,$fh) = calcDayHourMove ($chour, $num);
|
my ($fd, $fh) = calcDayHourMove ($chour, $num);
|
||||||
next if($fd > 1);
|
next if($fd > 1);
|
||||||
|
|
||||||
my $nhr = sprintf "%02d", $num;
|
my $nhr = sprintf "%02d", $num;
|
||||||
@@ -11787,13 +11810,20 @@ return;
|
|||||||
sub _createSummaries {
|
sub _createSummaries {
|
||||||
my $paref = shift;
|
my $paref = shift;
|
||||||
my $name = $paref->{name};
|
my $name = $paref->{name};
|
||||||
my $day = $paref->{day};
|
my $t = $paref->{t};
|
||||||
|
my $day = $paref->{day}; # aktueller Tag
|
||||||
my $chour = $paref->{chour}; # aktuelle Stunde
|
my $chour = $paref->{chour}; # aktuelle Stunde
|
||||||
my $minute = $paref->{minute}; # aktuelle Minute
|
my $minute = $paref->{minute}; # aktuelle Minute
|
||||||
my $debug = $paref->{debug};
|
my $debug = $paref->{debug};
|
||||||
|
|
||||||
$minute = int ($minute) + 1; # Minute Range umsetzen auf 1 bis 60
|
$minute = int ($minute) + 1; # Minute Range umsetzen auf 1 bis 60
|
||||||
|
|
||||||
|
my $dt = timestringsFromOffset ($t, 86400);
|
||||||
|
my $tmoday = $dt->{day}; # Tomorrow Day (01..31)
|
||||||
|
|
||||||
|
$dt = timestringsFromOffset ($t, 172800);
|
||||||
|
my $datmoday = $dt->{day}; # Übermorgen Day (01..31)
|
||||||
|
|
||||||
## Initialisierung
|
## Initialisierung
|
||||||
####################
|
####################
|
||||||
my $next1HoursSum = { "PV" => 0, "Consumption" => 0 };
|
my $next1HoursSum = { "PV" => 0, "Consumption" => 0 };
|
||||||
@@ -11802,6 +11832,7 @@ sub _createSummaries {
|
|||||||
my $next4HoursSum = { "PV" => 0, "Consumption" => 0 };
|
my $next4HoursSum = { "PV" => 0, "Consumption" => 0 };
|
||||||
my $restOfDaySum = { "PV" => 0, "Consumption" => 0 };
|
my $restOfDaySum = { "PV" => 0, "Consumption" => 0 };
|
||||||
my $tomorrowSum = { "PV" => 0, "Consumption" => 0 };
|
my $tomorrowSum = { "PV" => 0, "Consumption" => 0 };
|
||||||
|
my $daftertomSum = { "PV" => 0, "Consumption" => 0 }; # Werte für Übermorgen
|
||||||
my $todaySumFc = { "PV" => 0, "Consumption" => 0 };
|
my $todaySumFc = { "PV" => 0, "Consumption" => 0 };
|
||||||
my $todaySumRe = { "PV" => 0, "Consumption" => 0 };
|
my $todaySumRe = { "PV" => 0, "Consumption" => 0 };
|
||||||
|
|
||||||
@@ -11841,13 +11872,14 @@ sub _createSummaries {
|
|||||||
$next4HoursSum->{Consumption} = $hour00confcremain;
|
$next4HoursSum->{Consumption} = $hour00confcremain;
|
||||||
$restOfDaySum->{Consumption} = $hour00confcremain;
|
$restOfDaySum->{Consumption} = $hour00confcremain;
|
||||||
|
|
||||||
for my $h (1..47) {
|
for my $h (1..MAXNEXTHOURS) {
|
||||||
my $idx = sprintf "%02d", $h;
|
my $idx = sprintf "%02d", $h;
|
||||||
my $pvfc = NexthoursVal ($name, "NextHour".$idx, 'pvfc', 0);
|
my $pvfc = NexthoursVal ($name, "NextHour".$idx, 'pvfc', 0);
|
||||||
my $confc = NexthoursVal ($name, "NextHour".$idx, 'confc', 0);
|
my $confc = NexthoursVal ($name, "NextHour".$idx, 'confc', 0);
|
||||||
my $istdy = NexthoursVal ($name, "NextHour".$idx, 'today', 0);
|
my $istdy = NexthoursVal ($name, "NextHour".$idx, 'today', 0);
|
||||||
my $don = NexthoursVal ($name, "NextHour".$idx, 'DoN', 0);
|
my $don = NexthoursVal ($name, "NextHour".$idx, 'DoN', 0);
|
||||||
my $hod = NexthoursVal ($name, "NextHour".$idx, 'hourofday', 0);
|
my $hod = NexthoursVal ($name, "NextHour".$idx, 'hourofday', 0);
|
||||||
|
my $nhday = NexthoursVal ($name, "NextHour".$idx, 'day', 0);
|
||||||
|
|
||||||
$pvfc = max (0, $pvfc); # PV Prognose darf nicht negativ sein
|
$pvfc = max (0, $pvfc); # PV Prognose darf nicht negativ sein
|
||||||
$confc = max (0, $confc); # Verbrauchsprognose darf nicht negativ sein
|
$confc = max (0, $confc); # Verbrauchsprognose darf nicht negativ sein
|
||||||
@@ -11889,7 +11921,8 @@ sub _createSummaries {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
$tomorrowSum->{PV} += $pvfc;
|
$tomorrowSum->{PV} += $pvfc if(int($nhday) == int($tmoday));
|
||||||
|
$daftertomSum->{PV} += $pvfc if(int($nhday) == int($datmoday));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -11917,19 +11950,27 @@ sub _createSummaries {
|
|||||||
}
|
}
|
||||||
|
|
||||||
my $pv2node = 0;
|
my $pv2node = 0;
|
||||||
|
my $pv2bat = 0;
|
||||||
|
my $dc2inv2node = 0;
|
||||||
|
my $node2inv2dc = 0;
|
||||||
my $pv2grid = 0; # PV-Erzeugung zu Grid-only
|
my $pv2grid = 0; # PV-Erzeugung zu Grid-only
|
||||||
|
|
||||||
for my $in (1..MAXINVERTER) { # Summe alle Inverter
|
for my $in (1..MAXINVERTER) {
|
||||||
$in = sprintf "%02d", $in;
|
$in = sprintf "%02d", $in;
|
||||||
my ($err) = isDeviceValid ( { name => $name, obj => 'setupInverterDev'.$in, method => 'attr' } );
|
my ($err) = isDeviceValid ( { name => $name, obj => 'setupInverterDev'.$in, method => 'attr' } );
|
||||||
next if($err);
|
next if($err);
|
||||||
|
|
||||||
my $pvout = InverterVal ($name, $in, 'ipvout', 0);
|
my $pvout = InverterVal ($name, $in, 'ipvout', 0); # Erzeugung aus PV
|
||||||
|
my $pdc2ac = InverterVal ($name, $in, 'ipdc2ac', 0); # Wandlung DC->AC (Batterie-Wechselrichter)
|
||||||
|
my $pac2dc = InverterVal ($name, $in, 'ipac2dc', 0); # Rückwandlung AC->DC (Batterie-Wechselrichter)
|
||||||
my $ifeed = InverterVal ($name, $in, 'ifeed', 'default');
|
my $ifeed = InverterVal ($name, $in, 'ifeed', 'default');
|
||||||
my $isource = InverterVal ($name, $in, 'isource', 'pv');
|
my $isource = InverterVal ($name, $in, 'isource', 'pv');
|
||||||
my $pac2dc = InverterVal ($name, $in, 'ipac2dc', 0); # Rückwandlung AC->DC (Batterie-Wechselrichter)
|
|
||||||
$pv2node += $pvout if($ifeed ne 'grid' && $isource eq 'pv'); # nur PV Erzeugung berücksichtigen
|
$pv2node += $pvout if($ifeed eq 'default' && $isource eq 'pv'); # PV-Erzeugung Inverter für das Hausnetz
|
||||||
$pv2grid += $pvout if($ifeed eq 'grid' && $isource eq 'pv'); # nur PV Erzeugung mit Ziel 'Grid'
|
$pv2grid += $pvout if($ifeed eq 'grid' && $isource eq 'pv'); # PV nur für das öffentliche Netz
|
||||||
|
$pv2bat += $pvout if($ifeed eq 'bat' && $isource eq 'pv'); # Direktladen PV nur in die Batterie
|
||||||
|
$dc2inv2node += $pdc2ac if($ifeed eq 'hybrid' || ($ifeed eq 'default' && $isource eq 'bat')); # DC->AC / Speisung Inverter aus Batterie / Solar-Ladegerät statt PV
|
||||||
|
$node2inv2dc += $pac2dc if($ifeed eq 'hybrid' || ($ifeed eq 'default' && $isource eq 'bat')); # AC->DC (Batterie- oder Hybrid-Wechselrichter)
|
||||||
}
|
}
|
||||||
|
|
||||||
my $othprod = 0; # Summe Otherproducer
|
my $othprod = 0; # Summe Otherproducer
|
||||||
@@ -11939,15 +11980,16 @@ sub _createSummaries {
|
|||||||
$othprod += ProducerVal ($name, $pn, 'pgeneration', 0);
|
$othprod += ProducerVal ($name, $pn, 'pgeneration', 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
my $consumption = int ($pv2node + $othprod - $gfeedin + $gcon - $batin + $batout); # ohne PV2Grid
|
my $consumption = sprintf "%.0f", ($pv2node + $pv2bat + $othprod - $gfeedin + $gcon - $batin + $batout); # ohne PV2Grid
|
||||||
my $selfconsumption = int ($pv2node - $gfeedin - $batin);
|
my $selfconsumption = sprintf "%.0f", ($pv2node + $pv2bat - $gfeedin - $batin);
|
||||||
$selfconsumption = $selfconsumption < 0 ? 0 : $selfconsumption;
|
$selfconsumption = $selfconsumption < 0 ? 0 : $selfconsumption;
|
||||||
|
|
||||||
my $surplus = int ($pv2node - $pv2grid + $othprod - $consumption); # aktueller Überschuß
|
my $surplus = sprintf "%.0f", ($pv2node - $pv2grid + $othprod - $consumption); # aktueller Überschuß
|
||||||
$surplus = 0 if($surplus < 0); # wegen Vergleich nompower vs. surplus
|
$surplus = 0 if($surplus < 0); # wegen Vergleich nompower vs. surplus
|
||||||
|
|
||||||
if ($debug =~ /collectData/xs) {
|
if ($debug =~ /collectData/xs) {
|
||||||
Log3 ($name, 1, "$name DEBUG> current Power values -> PV2Node: $pv2node W, PV2Grid: $pv2grid, Other: $othprod W, GridIn: $gfeedin W, GridCon: $gcon W, BatIn: $batin W, BatOut: $batout W");
|
Log3 ($name, 1, "$name DEBUG> current Power values -> PV2Node: $pv2node W, PV2Bat: $pv2bat, PV2Grid: $pv2grid W, Other: $othprod W, GridIn: $gfeedin W, GridCon: $gcon W");
|
||||||
|
Log3 ($name, 1, "$name DEBUG> current Power Battery -> BatIn: $batin W (Node2Inv2DC: $node2inv2dc W), BatOut: $batout W (DC2Inv2Node: $dc2inv2node W)");
|
||||||
Log3 ($name, 1, "$name DEBUG> current Consumption result -> $consumption W");
|
Log3 ($name, 1, "$name DEBUG> current Consumption result -> $consumption W");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -11963,6 +12005,7 @@ sub _createSummaries {
|
|||||||
$data{$name}{current}{autarkyrate} = $autarkyrate;
|
$data{$name}{current}{autarkyrate} = $autarkyrate;
|
||||||
$data{$name}{current}{tdConFcTillSunset} = sprintf "%.0f", $tdConFcTillSunset;
|
$data{$name}{current}{tdConFcTillSunset} = sprintf "%.0f", $tdConFcTillSunset;
|
||||||
$data{$name}{current}{surplus} = $surplus;
|
$data{$name}{current}{surplus} = $surplus;
|
||||||
|
$data{$name}{current}{dayAfterTomorrowPVfc} = $daftertomSum->{PV};
|
||||||
|
|
||||||
push @{$data{$name}{current}{surplusslidereg}}, $surplus; # Schieberegister PV Überschuß
|
push @{$data{$name}{current}{surplusslidereg}}, $surplus; # Schieberegister PV Überschuß
|
||||||
limitArray ($data{$name}{current}{surplusslidereg}, SPLSLIDEMAX);
|
limitArray ($data{$name}{current}{surplusslidereg}, SPLSLIDEMAX);
|
||||||
@@ -12548,6 +12591,7 @@ sub ___doPlanning {
|
|||||||
}
|
}
|
||||||
|
|
||||||
my $order = 1;
|
my $order = 1;
|
||||||
|
|
||||||
for my $k (reverse sort{$a<=>$b} keys %max) {
|
for my $k (reverse sort{$a<=>$b} keys %max) {
|
||||||
my $ts = timestringToTimestamp ($max{$k}{starttime});
|
my $ts = timestringToTimestamp ($max{$k}{starttime});
|
||||||
|
|
||||||
@@ -13859,7 +13903,7 @@ sub _calcConsForecast_circular {
|
|||||||
|
|
||||||
debugLog ($paref, 'saveData2Cache|consumption_long', "store '$k' hod '$nhhr' confc: $usage{$nhhr}{con}, confcEx: $usage{$nhhr}{conex}");
|
debugLog ($paref, 'saveData2Cache|consumption_long', "store '$k' hod '$nhhr' confc: $usage{$nhhr}{con}, confcEx: $usage{$nhhr}{conex}");
|
||||||
|
|
||||||
if (NexthoursVal ($hash, $k, 'today', 0)) { # nur Werte des aktuellen Tags speichern
|
if (NexthoursVal ($name, $k, 'today', 0)) { # nur Werte des aktuellen Tags speichern
|
||||||
$data{$name}{circular}{$nhhr}{confc} = $usage{$nhhr}{con};
|
$data{$name}{circular}{$nhhr}{confc} = $usage{$nhhr}{con};
|
||||||
writeToHistory ( { paref => $paref, key => 'confc', val => $usage{$nhhr}{con}, hour => $nhhr } );
|
writeToHistory ( { paref => $paref, key => 'confc', val => $usage{$nhhr}{con}, hour => $nhhr } );
|
||||||
|
|
||||||
@@ -14664,26 +14708,13 @@ sub _genSpecialReadings {
|
|||||||
storeReading ($prpo.'_'.$kpi, (sprintf "%.1f", $dbo).' '.$hcsr{$kpi}{unit});
|
storeReading ($prpo.'_'.$kpi, (sprintf "%.1f", $dbo).' '.$hcsr{$kpi}{unit});
|
||||||
}
|
}
|
||||||
elsif ($kpi eq 'dayAfterTomorrowPVforecast') { # PV Vorhersage Summe für Übermorgen (falls Werte vorhanden), Forum:#134226
|
elsif ($kpi eq 'dayAfterTomorrowPVforecast') { # PV Vorhersage Summe für Übermorgen (falls Werte vorhanden), Forum:#134226
|
||||||
my $dayaftertomorrow = strftime "%Y-%m-%d", localtime($t + 172800); # Datum von Übermorgen
|
my $datpvfc = &{$hcsr{$kpi}{fn}} ($name, 'dayAfterTomorrowPVfc', $def);
|
||||||
my @allstrings = split ",", AttrVal ($name, 'setupInverterStrings', '');
|
|
||||||
my $fcsumdat = 0;
|
|
||||||
|
|
||||||
for my $strg (@allstrings) {
|
if ($datpvfc) {
|
||||||
for my $starttmstr (sort keys %{$data{$name}{solcastapi}{$strg}}) {
|
storeReading ($prpo.'_'.$kpi, (sprintf "%.0f", $datpvfc).$hcsr{$kpi}{unit});
|
||||||
next if($starttmstr !~ /$dayaftertomorrow/xs);
|
|
||||||
|
|
||||||
my $val = &{$hcsr{$kpi}{fn}} ($hash, $strg, $starttmstr, $hcsr{$kpi}{par}, $def);
|
|
||||||
$fcsumdat += $val;
|
|
||||||
|
|
||||||
debugLog ($paref, 'radiationProcess', "dayaftertomorrow PV forecast (raw) - $strg -> $starttmstr -> $val Wh");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($fcsumdat) {
|
|
||||||
storeReading ($prpo.'_'.$kpi, (int $fcsumdat). ' Wh');
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
storeReading ($prpo.'_'.$kpi, $fcsumdat. ' (no data available)');
|
storeReading ($prpo.'_'.$kpi, $datpvfc. ' (no data available)');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
elsif ($kpi =~ /currentRunMtsConsumer_/xs) {
|
elsif ($kpi =~ /currentRunMtsConsumer_/xs) {
|
||||||
@@ -14736,9 +14767,12 @@ sub _genSpecialReadings {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
elsif ($kpi eq 'tomorrowConsumptionForecast') {
|
elsif ($kpi eq 'tomorrowConsumptionForecast') {
|
||||||
|
my $dt = timestringsFromOffset ($t, 86400);
|
||||||
|
my $tmoday = $dt->{day};
|
||||||
|
|
||||||
for my $idx (sort keys %{$data{$name}{nexthours}}) {
|
for my $idx (sort keys %{$data{$name}{nexthours}}) {
|
||||||
my $istoday = NexthoursVal ($hash, $idx, 'today', 0);
|
my $nhday = NexthoursVal ($hash, $idx, 'day', 0);
|
||||||
next if($istoday);
|
next if(int ($nhday) != int ($tmoday));
|
||||||
|
|
||||||
my $hod = NexthoursVal ($hash, $idx, 'hourofday', '01');
|
my $hod = NexthoursVal ($hash, $idx, 'hourofday', '01');
|
||||||
my $confc = &{$hcsr{$kpi}{fn}} ($hash, $idx, $hcsr{$kpi}{par}, $def);
|
my $confc = &{$hcsr{$kpi}{fn}} ($hash, $idx, $hcsr{$kpi}{par}, $def);
|
||||||
@@ -17913,15 +17947,15 @@ sub _flowGraphic {
|
|||||||
$soc < 76 ? "$stna bat50" :
|
$soc < 76 ? "$stna bat50" :
|
||||||
"$stna bat75";
|
"$stna bat75";
|
||||||
|
|
||||||
my $node2bat = 0; # Verbindung Inv.Knoten <-> Batterie ((-) Bat -> Knoten, (+) Knoten -> Bat)
|
|
||||||
my $bat2home = 0;
|
|
||||||
|
|
||||||
my $grid2home_style = $gconMetered ? "$stna active_sig" : "$stna inactive"; # GridConsumption
|
my $grid2home_style = $gconMetered ? "$stna active_sig" : "$stna inactive"; # GridConsumption
|
||||||
my $bat2home_style = $bat2home ? "$stna active_normal" : "$stna inactive";
|
my $bat2home_style = "$stna inactive";
|
||||||
my $dc2inv2node_style = $dc2inv2node ? "$stna active_normal" : "$stna inactive"; # Batterie zu Inverter mit source=bat
|
my $dc2inv2node_style = $dc2inv2node ? "$stna active_normal" : "$stna inactive"; # Batterie zu Inverter mit source=bat
|
||||||
my $gconMetered_direction = "M250,515 L670,590";
|
my $gconMetered_direction = "M250,515 L670,590";
|
||||||
my $bat2home_direction = "M1200,515 L730,590";
|
my $bat2home_direction = "M1200,515 L730,590";
|
||||||
|
|
||||||
|
my $node2bat = 0; # Verbindung Inv.Knoten <-> Batterie ((-) Bat -> Knoten, (+) Knoten -> Bat)
|
||||||
|
my $bat2home = 0;
|
||||||
|
|
||||||
if ($batout || $batin) { # Batterie wird geladen oder entladen
|
if ($batout || $batin) { # Batterie wird geladen oder entladen
|
||||||
$node2bat = ($batin - $batout) - $pv2bat + $dc2inv2node - $node2inv2dc; # positiv: Richtung Knoten -> Bat, negativ: Richtung Bat -> Inv.Knoten
|
$node2bat = ($batin - $batout) - $pv2bat + $dc2inv2node - $node2inv2dc; # positiv: Richtung Knoten -> Bat, negativ: Richtung Bat -> Inv.Knoten
|
||||||
$node2bat = 0 if(($dc2inv2node || $node2inv2dc) && $node2bat != 0);
|
$node2bat = 0 if(($dc2inv2node || $node2inv2dc) && $node2bat != 0);
|
||||||
@@ -17946,6 +17980,7 @@ sub _flowGraphic {
|
|||||||
|
|
||||||
my $node2home = $pnodesum - $node2gridMetered - ($node2bat > 0 ? $node2bat : 0); # Energiefluß vom Knoten zum Haus
|
my $node2home = $pnodesum - $node2gridMetered - ($node2bat > 0 ? $node2bat : 0); # Energiefluß vom Knoten zum Haus
|
||||||
$node2home = __normDecPlaces ($node2home);
|
$node2home = __normDecPlaces ($node2home);
|
||||||
|
|
||||||
$consptn = $gconMetered + $node2home + $bat2home; # V 1.52.0 Anpassung Consumption wegen Verlustleistungsdifferenzen
|
$consptn = $gconMetered + $node2home + $bat2home; # V 1.52.0 Anpassung Consumption wegen Verlustleistungsdifferenzen
|
||||||
|
|
||||||
## definierte Verbraucher ermitteln
|
## definierte Verbraucher ermitteln
|
||||||
@@ -19180,8 +19215,9 @@ sub checkdwdattr {
|
|||||||
my $amref = shift;
|
my $amref = shift;
|
||||||
|
|
||||||
my @fcprop = map { trim($_) } split ",", AttrVal ($dwddev, "forecastProperties", "pattern");
|
my @fcprop = map { trim($_) } split ",", AttrVal ($dwddev, "forecastProperties", "pattern");
|
||||||
my $fcr = AttrVal ($dwddev, "forecastResolution", 3);
|
my $fcr = AttrVal ($dwddev, 'forecastResolution', 3);
|
||||||
my $err;
|
my $fcd = AttrVal ($dwddev, 'forecastDays', 0);
|
||||||
|
my ($err, $warn);
|
||||||
|
|
||||||
my @aneeded;
|
my @aneeded;
|
||||||
for my $am (@$amref) {
|
for my $am (@$amref) {
|
||||||
@@ -19198,9 +19234,14 @@ sub checkdwdattr {
|
|||||||
$err .= qq{ERROR - device "$dwddev" -> attribute "forecastResolution" must be set to "1"};
|
$err .= qq{ERROR - device "$dwddev" -> attribute "forecastResolution" must be set to "1"};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($fcd < DWDFCDAYSMIN) {
|
||||||
|
$warn = qq{WARNING - device "$dwddev" -> attribute "forecastDays" is not set to the minimum value of: }.DWDFCDAYSMIN;
|
||||||
|
}
|
||||||
|
|
||||||
|
Log3 ($name, 2, "$name - $warn") if($warn);
|
||||||
Log3 ($name, 2, "$name - $err") if($err);
|
Log3 ($name, 2, "$name - $err") if($err);
|
||||||
|
|
||||||
return $err;
|
return ($err, $warn);
|
||||||
}
|
}
|
||||||
|
|
||||||
################################################################
|
################################################################
|
||||||
@@ -21017,7 +21058,7 @@ sub _listDataPoolNextHours {
|
|||||||
$sq .= "\n ";
|
$sq .= "\n ";
|
||||||
$sq .= "pvapifcraw: $pvapifcraw, pvapifc: $pvapifc, pvaifc: $pvaifc, pvfc: $pvfc, aihit: $aihit";
|
$sq .= "pvapifcraw: $pvapifcraw, pvapifc: $pvapifc, pvaifc: $pvaifc, pvfc: $pvfc, aihit: $aihit";
|
||||||
$sq .= "\n ";
|
$sq .= "\n ";
|
||||||
$sq .= "confc: $confc, confcEx: $confcex, weatherid: $wid, wcc: $wcc, rr1c: $rr1c, temp=$temp";
|
$sq .= "confc: $confc, confcEx: $confcex, weatherid: $wid, wcc: $wcc, rr1c: $rr1c, temp: $temp";
|
||||||
$sq .= "\n ";
|
$sq .= "\n ";
|
||||||
$sq .= "rad1h: $rad1h, sunaz: $sunaz, sunalt: $sunalt, DoN: $don";
|
$sq .= "rad1h: $rad1h, sunaz: $sunaz, sunalt: $sunalt, DoN: $don";
|
||||||
$sq .= "\n ";
|
$sq .= "\n ";
|
||||||
@@ -21379,7 +21420,7 @@ sub checkPlantConfig {
|
|||||||
my $hash = shift;
|
my $hash = shift;
|
||||||
|
|
||||||
my $name = $hash->{NAME};
|
my $name = $hash->{NAME};
|
||||||
my $type = $hash->{TYPE};
|
my $warnmsg;
|
||||||
|
|
||||||
setModel ($hash); # Model setzen
|
setModel ($hash); # Model setzen
|
||||||
|
|
||||||
@@ -21472,7 +21513,7 @@ sub checkPlantConfig {
|
|||||||
|
|
||||||
for my $step (1..MAXWEATHERDEV) {
|
for my $step (1..MAXWEATHERDEV) {
|
||||||
my ($valid, $fcname, $apiu) = isWeatherDevValid ($hash, 'setupWeatherDev'.$step);
|
my ($valid, $fcname, $apiu) = isWeatherDevValid ($hash, 'setupWeatherDev'.$step);
|
||||||
next if(!$fcname && $step ne 1);
|
next if(!$valid && $step ne 1);
|
||||||
|
|
||||||
if (!$valid) {
|
if (!$valid) {
|
||||||
$result->{'Weather Properties'}{state} = $nok;
|
$result->{'Weather Properties'}{state} = $nok;
|
||||||
@@ -21487,8 +21528,14 @@ sub checkPlantConfig {
|
|||||||
$result->{'Weather Properties'}{fault} = 1;
|
$result->{'Weather Properties'}{fault} = 1;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if (!$apiu) {
|
if (!$apiu) { # keine Wetter-API -> Wetterdevice
|
||||||
$err = checkdwdattr ($name, $fcname, \@dweattrmust);
|
($err, $warnmsg) = checkdwdattr ($name, $fcname, \@dweattrmust);
|
||||||
|
|
||||||
|
if ($warnmsg) {
|
||||||
|
$result->{'Weather Properties'}{state} = $warn;
|
||||||
|
$result->{'Weather Properties'}{result} .= $warnmsg.'<br>';
|
||||||
|
$result->{'Weather Properties'}{warn} = 1;
|
||||||
|
}
|
||||||
|
|
||||||
if ($err) {
|
if ($err) {
|
||||||
$result->{'Weather Properties'}{state} = $nok;
|
$result->{'Weather Properties'}{state} = $nok;
|
||||||
@@ -21510,6 +21557,7 @@ sub checkPlantConfig {
|
|||||||
$result->{'Weather Properties'}{note} .= qq{checked parameters and attributes of device "$fcname": <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} .= 'forecastProperties -> '.join (',', @dweattrmust).'<br>';
|
||||||
$result->{'Weather Properties'}{note} .= 'forecastRefresh '.($mosm eq 'MOSMIX_L' ? '-> set attribute to below "6" if possible' : '').'<br>';
|
$result->{'Weather Properties'}{note} .= 'forecastRefresh '.($mosm eq 'MOSMIX_L' ? '-> set attribute to below "6" if possible' : '').'<br>';
|
||||||
|
$result->{'Weather Properties'}{note} .= 'forecastDays <br>';
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
$result->{'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>";
|
||||||
@@ -21547,7 +21595,7 @@ sub checkPlantConfig {
|
|||||||
$result->{'DWD Radiation Properties'}{fault} = 1;
|
$result->{'DWD Radiation Properties'}{fault} = 1;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
$err = checkdwdattr ($name, $raname, \@draattrmust);
|
($err, $warnmsg) = checkdwdattr ($name, $raname, \@draattrmust);
|
||||||
|
|
||||||
if ($err) {
|
if ($err) {
|
||||||
$result->{'DWD Radiation Properties'}{state} = $nok;
|
$result->{'DWD Radiation Properties'}{state} = $nok;
|
||||||
@@ -21587,6 +21635,7 @@ sub checkPlantConfig {
|
|||||||
$result->{'DWD Radiation Properties'}{note} .= 'MOSMIX variant, Age of Radiation data. <br>';
|
$result->{'DWD Radiation Properties'}{note} .= 'MOSMIX variant, Age of Radiation data. <br>';
|
||||||
$result->{'DWD Radiation Properties'}{note} .= qq{<br>checked parameters and attributes device "$raname": <br>};
|
$result->{'DWD Radiation Properties'}{note} .= qq{<br>checked parameters and attributes device "$raname": <br>};
|
||||||
$result->{'DWD Radiation Properties'}{note} .= 'forecastProperties -> '.join (',', @draattrmust).'<br>';
|
$result->{'DWD Radiation Properties'}{note} .= 'forecastProperties -> '.join (',', @draattrmust).'<br>';
|
||||||
|
$result->{'DWD Radiation Properties'}{note} .= 'forecastDays <br>';
|
||||||
$result->{'DWD Radiation Properties'}{note} .= 'forecastRefresh '.($mosm eq 'MOSMIX_L' ? '-> set attribute to below "6" if possible' : '').'<br>';
|
$result->{'DWD Radiation Properties'}{note} .= 'forecastRefresh '.($mosm eq 'MOSMIX_L' ? '-> set attribute to below "6" if possible' : '').'<br>';
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -22226,7 +22275,7 @@ sub calcDayHourMove {
|
|||||||
my $chour = shift;
|
my $chour = shift;
|
||||||
my $num = shift;
|
my $num = shift;
|
||||||
|
|
||||||
my $fh = $chour + $num;
|
my $fh = int ($chour) + $num;
|
||||||
my $fd = int ($fh / 24) ;
|
my $fd = int ($fh / 24) ;
|
||||||
$fh = $fh - ($fd * 24);
|
$fh = $fh - ($fd * 24);
|
||||||
|
|
||||||
@@ -23676,22 +23725,24 @@ return ($err, $dv, $h, $al);
|
|||||||
#####################################################################
|
#####################################################################
|
||||||
sub isWeatherDevValid {
|
sub isWeatherDevValid {
|
||||||
my $hash = shift;
|
my $hash = shift;
|
||||||
my $wdev = shift;
|
my $wattr = shift;
|
||||||
|
|
||||||
my $valid = '';
|
my ($rapi, $wapi) = ('', '');
|
||||||
|
my $valid = 0;
|
||||||
my $apiu = '';
|
my $apiu = '';
|
||||||
my $fcname = AttrVal ($hash->{NAME}, $wdev, ''); # Weather Forecast Device
|
my $fcname = AttrVal ($hash->{NAME}, $wattr, ''); # Weather Forecast Device/API
|
||||||
|
|
||||||
return if(!$fcname);
|
return if(!$fcname);
|
||||||
|
|
||||||
|
if (!$defs{$fcname}) { # kein Device -> API genutzt?
|
||||||
|
if ($fcname =~ /^OpenMeteo/xs) {
|
||||||
$valid = 1;
|
$valid = 1;
|
||||||
if (!$defs{$fcname} || $defs{$fcname}{TYPE} ne "DWD_OpenData") { $valid = '' }
|
$apiu = $fcname;
|
||||||
|
}
|
||||||
my ($rapi, $wapi) = getStatusApiName ($hash); # $rapi - Radiation-API, $wapi - Weather-API
|
}
|
||||||
|
else { # DWD Device -> Typ Prüfung
|
||||||
if ($wapi =~ /^OpenMeteo/xs) {
|
if ($defs{$fcname}{TYPE} eq 'DWD_OpenData') {
|
||||||
$valid = 1;
|
$valid = 1;
|
||||||
$apiu = $wapi;
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return ($valid, $fcname, $apiu);
|
return ($valid, $fcname, $apiu);
|
||||||
@@ -23735,17 +23786,16 @@ sub isWeatherAgeExceeded {
|
|||||||
|
|
||||||
for my $step (1..MAXWEATHERDEV) {
|
for my $step (1..MAXWEATHERDEV) {
|
||||||
my ($valid, $fcname, $apiu) = isWeatherDevValid ($hash, 'setupWeatherDev'.$step);
|
my ($valid, $fcname, $apiu) = isWeatherDevValid ($hash, 'setupWeatherDev'.$step);
|
||||||
next if(!$fcname && $step ne 1);
|
next if(!$valid && $step ne 1);
|
||||||
|
|
||||||
if (!$apiu) {
|
if (!$apiu) {
|
||||||
if (!$fcname || !$valid) {
|
|
||||||
if (!$fcname) {
|
if (!$fcname) {
|
||||||
return (qq{No DWD device is defined in attribute "setupWeatherDev$step"}, $resh);
|
return (qq{No DWD device is defined in attribute "setupWeatherDev$step"}, $resh);
|
||||||
}
|
}
|
||||||
else {
|
|
||||||
|
if (!$valid) {
|
||||||
return (qq{The DWD device "$fcname" doesn't exist}, $resh);
|
return (qq{The DWD device "$fcname" doesn't exist}, $resh);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
my $fct = ReadingsVal ($fcname, 'fc_time', '');
|
my $fct = ReadingsVal ($fcname, 'fc_time', '');
|
||||||
return (qq{The reading 'fc_time' ($fcname) doesn't exist or is empty}, $resh) if(!$fct);
|
return (qq{The reading 'fc_time' ($fcname) doesn't exist or is empty}, $resh) if(!$fct);
|
||||||
@@ -27749,7 +27799,7 @@ to ensure that the system configuration is correct.
|
|||||||
<ul>
|
<ul>
|
||||||
<table>
|
<table>
|
||||||
<colgroup> <col width="25%"> <col width="75%"> </colgroup>
|
<colgroup> <col width="25%"> <col width="75%"> </colgroup>
|
||||||
<tr><td> <b>forecastDays</b> </td><td>1 </td></tr>
|
<tr><td> <b>forecastDays</b> </td><td>2 </td></tr>
|
||||||
<tr><td> <b>forecastProperties</b> </td><td>TTT,Neff,RR1c,ww,SunUp,SunRise,SunSet </td></tr>
|
<tr><td> <b>forecastProperties</b> </td><td>TTT,Neff,RR1c,ww,SunUp,SunRise,SunSet </td></tr>
|
||||||
<tr><td> <b>forecastResolution</b> </td><td>1 </td></tr>
|
<tr><td> <b>forecastResolution</b> </td><td>1 </td></tr>
|
||||||
<tr><td> <b>forecastStation</b> </td><td><Station code of the evaluated DWD station> </td></tr>
|
<tr><td> <b>forecastStation</b> </td><td><Station code of the evaluated DWD station> </td></tr>
|
||||||
@@ -30242,7 +30292,7 @@ die ordnungsgemäße Anlagenkonfiguration geprüft werden.
|
|||||||
<ul>
|
<ul>
|
||||||
<table>
|
<table>
|
||||||
<colgroup> <col width="25%"> <col width="75%"> </colgroup>
|
<colgroup> <col width="25%"> <col width="75%"> </colgroup>
|
||||||
<tr><td> <b>forecastDays</b> </td><td>1 (auf >= 2 setzen wenn eine längere Vorhersage gewünscht ist) </td></tr>
|
<tr><td> <b>forecastDays</b> </td><td>2 (auf > 2 setzen wenn eine längere Vorhersage gewünscht ist) </td></tr>
|
||||||
<tr><td> <b>forecastProperties</b> </td><td>Rad1h </td></tr>
|
<tr><td> <b>forecastProperties</b> </td><td>Rad1h </td></tr>
|
||||||
<tr><td> <b>forecastResolution</b> </td><td>1 </td></tr>
|
<tr><td> <b>forecastResolution</b> </td><td>1 </td></tr>
|
||||||
<tr><td> <b>forecastStation</b> </td><td><Stationscode der ausgewerteten DWD Station> </td></tr>
|
<tr><td> <b>forecastStation</b> </td><td><Stationscode der ausgewerteten DWD Station> </td></tr>
|
||||||
@@ -30405,7 +30455,7 @@ die ordnungsgemäße Anlagenkonfiguration geprüft werden.
|
|||||||
<ul>
|
<ul>
|
||||||
<table>
|
<table>
|
||||||
<colgroup> <col width="25%"> <col width="75%"> </colgroup>
|
<colgroup> <col width="25%"> <col width="75%"> </colgroup>
|
||||||
<tr><td> <b>forecastDays</b> </td><td>1 </td></tr>
|
<tr><td> <b>forecastDays</b> </td><td>2 </td></tr>
|
||||||
<tr><td> <b>forecastProperties</b> </td><td>TTT,Neff,RR1c,ww,SunUp,SunRise,SunSet </td></tr>
|
<tr><td> <b>forecastProperties</b> </td><td>TTT,Neff,RR1c,ww,SunUp,SunRise,SunSet </td></tr>
|
||||||
<tr><td> <b>forecastResolution</b> </td><td>1 </td></tr>
|
<tr><td> <b>forecastResolution</b> </td><td>1 </td></tr>
|
||||||
<tr><td> <b>forecastStation</b> </td><td><Stationscode der ausgewerteten DWD Station> </td></tr>
|
<tr><td> <b>forecastStation</b> </td><td><Stationscode der ausgewerteten DWD Station> </td></tr>
|
||||||
|
|||||||
@@ -160,9 +160,10 @@ BEGIN {
|
|||||||
|
|
||||||
# Versions History intern
|
# Versions History intern
|
||||||
my %vNotesIntern = (
|
my %vNotesIntern = (
|
||||||
"1.55.0" => "04.08.2025 DWD-Weather and DWD-Radiation device new minimum value of attr 'forecastDays' is 2 ".
|
"1.55.0" => "06.08.2025 DWD-Weather and DWD-Radiation device new minimum value of attr 'forecastDays' is 2 ".
|
||||||
"checkPlantConfig: check forecastDays of new minimum value ".
|
"checkPlantConfig: check forecastDays of new minimum value ".
|
||||||
"___createOpenMeteoURL: set forecast_hours=72 ",
|
"___createOpenMeteoURL: set forecast_hours=72, bugfix of V 1.54.7 ".
|
||||||
|
"Nexthours: max 72 hours available but not more than 3 days ",
|
||||||
"1.54.7" => "01.08.2025 _transferAPIRadiationValues: Extension of Nexthours content up to 48 hours into the future ".
|
"1.54.7" => "01.08.2025 _transferAPIRadiationValues: Extension of Nexthours content up to 48 hours into the future ".
|
||||||
"attr graphicBeamHeightLevelX is obsolete -> use graphicControl instead ".
|
"attr graphicBeamHeightLevelX is obsolete -> use graphicControl instead ".
|
||||||
"attr graphicControl new key beamHeightlevel ",
|
"attr graphicControl new key beamHeightlevel ",
|
||||||
@@ -427,6 +428,8 @@ use constant {
|
|||||||
AIACCLOWLIM => 50, # untere Abweichungsgrenze (%) AI 'Accurate' von API Prognose
|
AIACCLOWLIM => 50, # untere Abweichungsgrenze (%) AI 'Accurate' von API Prognose
|
||||||
AIACCTRNMIN => 3500, # Mindestanzahl KI Regeln für Verwendung "KI Accurate"
|
AIACCTRNMIN => 3500, # Mindestanzahl KI Regeln für Verwendung "KI Accurate"
|
||||||
|
|
||||||
|
MAXNEXTHOURS => 71, # max. Anzahl Stunden der Wertebasis (Start mit 0 -> 72h) z.B. in Nexthours
|
||||||
|
MAXNEXTDAYS => 2, # max. Anzahl volle Tage in NextHours (Start mit 0 -> 3d)
|
||||||
DWDFCDAYSMIN => 2, # Mindestwert Attr 'forecastDays' im DWD-Device
|
DWDFCDAYSMIN => 2, # Mindestwert Attr 'forecastDays' im DWD-Device
|
||||||
SOLAPIREPDEF => 3600, # default Abrufintervall SolCast API (s)
|
SOLAPIREPDEF => 3600, # default Abrufintervall SolCast API (s)
|
||||||
FORAPIREPDEF => 900, # default Abrufintervall ForecastSolar API (s)
|
FORAPIREPDEF => 900, # default Abrufintervall ForecastSolar API (s)
|
||||||
@@ -1382,7 +1385,7 @@ my %hcsr = (
|
|||||||
BatWeightedTotalSOC => { fnr => 2, fn => \&CurrentVal, par => 'batsoctotal', par1 => '', unit => ' %', def => 0 },
|
BatWeightedTotalSOC => { fnr => 2, fn => \&CurrentVal, par => 'batsoctotal', par1 => '', unit => ' %', def => 0 },
|
||||||
SunHours_Remain => { fnr => 5, fn => \&CurrentVal, par => '', par1 => '', unit => '', def => 0 }, # fnr => 3 -> Custom Calc
|
SunHours_Remain => { fnr => 5, fn => \&CurrentVal, par => '', par1 => '', unit => '', def => 0 }, # fnr => 3 -> Custom Calc
|
||||||
SunMinutes_Remain => { fnr => 5, fn => \&CurrentVal, par => '', par1 => '', unit => '', def => 0 },
|
SunMinutes_Remain => { fnr => 5, fn => \&CurrentVal, par => '', par1 => '', unit => '', def => 0 },
|
||||||
dayAfterTomorrowPVforecast => { fnr => 5, fn => \&RadiationAPIVal, par => 'pv_estimate50', par1 => '', unit => '', def => 0 },
|
dayAfterTomorrowPVforecast => { fnr => 5, fn => \&CurrentVal, par => 'dayAfterTomorrowPVfc', par1 => '', unit => ' Wh', def => 0 },
|
||||||
todayGridFeedIn => { fnr => 5, fn => \&CircularVal, par => 99, par1 => '', unit => '', def => 0 },
|
todayGridFeedIn => { fnr => 5, fn => \&CircularVal, par => 99, par1 => '', unit => '', def => 0 },
|
||||||
todayGridConsumption => { fnr => 5, fn => \&CircularVal, par => 99, par1 => '', unit => '', def => 0 },
|
todayGridConsumption => { fnr => 5, fn => \&CircularVal, par => 99, par1 => '', unit => '', def => 0 },
|
||||||
todayNotOwnerConsumption => { fnr => 5, fn => \&CircularVal, par => 99, par1 => 'todayConsumption', unit => ' Wh', def => 0 },
|
todayNotOwnerConsumption => { fnr => 5, fn => \&CircularVal, par => 99, par1 => 'todayConsumption', unit => ' Wh', def => 0 },
|
||||||
@@ -9330,9 +9333,20 @@ sub _specialActivities {
|
|||||||
$gcon = ReadingsNum ($name, "Today_Hour".sprintf("%02d",$chour)."_GridConsumption", 0);
|
$gcon = ReadingsNum ($name, "Today_Hour".sprintf("%02d",$chour)."_GridConsumption", 0);
|
||||||
storeReading ('LastHourGridconsumptionReal', "$gcon Wh", $ts1);
|
storeReading ('LastHourGridconsumptionReal', "$gcon Wh", $ts1);
|
||||||
|
|
||||||
|
## überhängende Daten in Nexthours löschen
|
||||||
|
############################################
|
||||||
|
for my $num (0..MAXNEXTHOURS) {
|
||||||
|
my ($fd, $fh) = calcDayHourMove ($chour, $num);
|
||||||
|
my $nhtstr = 'NextHour'.(sprintf "%02d", $num);
|
||||||
|
|
||||||
|
if ($fd > 2 && exists $data{$name}{nexthours}{$nhtstr}) {
|
||||||
|
delete $data{$name}{nexthours}{$nhtstr};
|
||||||
|
next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
## Planungsdaten spezifisch löschen (Anfang und Ende nicht am selben Tag)
|
## Planungsdaten spezifisch löschen (Anfang und Ende nicht am selben Tag)
|
||||||
##########################################################################
|
##########################################################################
|
||||||
|
|
||||||
for my $c (keys %{$data{$name}{consumers}}) {
|
for my $c (keys %{$data{$name}{consumers}}) {
|
||||||
next if(ConsumerVal ($hash, $c, 'plandelete', 'regular') eq 'regular');
|
next if(ConsumerVal ($hash, $c, 'plandelete', 'regular') eq 'regular');
|
||||||
|
|
||||||
@@ -9707,9 +9721,9 @@ sub _transferWeatherValues {
|
|||||||
|
|
||||||
__mergeDataWeather ($paref); # Wetterdaten zusammenfügen
|
__mergeDataWeather ($paref); # Wetterdaten zusammenfügen
|
||||||
|
|
||||||
for my $num (0..71) {
|
for my $num (0..MAXNEXTHOURS) {
|
||||||
my ($fd, $fh) = calcDayHourMove ($chour, $num);
|
my ($fd, $fh) = calcDayHourMove ($chour, $num);
|
||||||
last if($fd > 2);
|
last if($fd > MAXNEXTDAYS);
|
||||||
|
|
||||||
my $wid = $data{$name}{weatherdata}{"fc${fd}_${fh}"}{merge}{ww}; # signifikantes Wetter = Wetter ID
|
my $wid = $data{$name}{weatherdata}{"fc${fd}_${fh}"}{merge}{ww}; # signifikantes Wetter = Wetter ID
|
||||||
my $wwd = $data{$name}{weatherdata}{"fc${fd}_${fh}"}{merge}{wwd}; # Wetter Beschreibung
|
my $wwd = $data{$name}{weatherdata}{"fc${fd}_${fh}"}{merge}{wwd}; # Wetter Beschreibung
|
||||||
@@ -9723,7 +9737,7 @@ sub _transferWeatherValues {
|
|||||||
debugLog ($paref, 'collectData_long', "Adjust cloud cover ratio (wcc) due to significant weather (ww) - ww: $wid -> wcc: $wcc");
|
debugLog ($paref, 'collectData_long', "Adjust cloud cover ratio (wcc) due to significant weather (ww) - ww: $wid -> wcc: $wcc");
|
||||||
}
|
}
|
||||||
|
|
||||||
my $nhtstr = "NextHour".sprintf "%02d", $num;
|
my $nhtstr = 'NextHour'.(sprintf "%02d", $num);
|
||||||
$data{$name}{nexthours}{$nhtstr}{weatherid} = $wid;
|
$data{$name}{nexthours}{$nhtstr}{weatherid} = $wid;
|
||||||
$data{$name}{nexthours}{$nhtstr}{wcc} = $wcc;
|
$data{$name}{nexthours}{$nhtstr}{wcc} = $wcc;
|
||||||
$data{$name}{nexthours}{$nhtstr}{rr1c} = $rr1c;
|
$data{$name}{nexthours}{$nhtstr}{rr1c} = $rr1c;
|
||||||
@@ -9790,7 +9804,7 @@ sub __readDataWeather {
|
|||||||
|
|
||||||
for my $n (0..$end) {
|
for my $n (0..$end) {
|
||||||
my ($fd, $fh) = calcDayHourMove ($chour, $n);
|
my ($fd, $fh) = calcDayHourMove ($chour, $n);
|
||||||
last if($fd > 2);
|
last if($fd > MAXNEXTDAYS);
|
||||||
|
|
||||||
my $wid = ReadingsNum ($fcname, "fc${fd}_${fh}_ww", undef); # Signifikantes Wetter zum Vorhersagezeitpunkt
|
my $wid = ReadingsNum ($fcname, "fc${fd}_${fh}_ww", undef); # Signifikantes Wetter zum Vorhersagezeitpunkt
|
||||||
my $wwd = ReadingsVal ($fcname, "fc${fd}_${fh}_wwd", ''); # Wetter Beschreibung
|
my $wwd = ReadingsVal ($fcname, "fc${fd}_${fh}_wwd", ''); # Wetter Beschreibung
|
||||||
@@ -10257,14 +10271,14 @@ sub _transferAPIRadiationValues {
|
|||||||
$invcapsum += InverterVal ($name, $in, 'invertercap', 0); # Limit Leistungssumme aller Inverters
|
$invcapsum += InverterVal ($name, $in, 'invertercap', 0); # Limit Leistungssumme aller Inverters
|
||||||
}
|
}
|
||||||
|
|
||||||
for my $num (0..71) {
|
for my $num (0..MAXNEXTHOURS) {
|
||||||
my ($fd,$fh) = calcDayHourMove ($chour, $num);
|
my ($fd, $fh) = calcDayHourMove ($chour, $num);
|
||||||
last if($fd > 2);
|
last if($fd > MAXNEXTDAYS);
|
||||||
|
|
||||||
my $fh1 = $fh + 1;
|
my $fh1 = $fh + 1;
|
||||||
my $wantts = (timestringToTimestamp ($date.' '.$chour.':00:00')) + ($num * 3600);
|
my $wantts = (timestringToTimestamp ($date.' '.$chour.':00:00')) + ($num * 3600);
|
||||||
my $wantdt = (timestampToTimestring ($wantts, $lang))[1];
|
my $wantdt = (timestampToTimestring ($wantts, $lang))[1];
|
||||||
my $nhtstr = 'NextHour'.sprintf "%02d", $num;
|
my $nhtstr = 'NextHour'.(sprintf "%02d", $num);
|
||||||
my ($wtday, $wthour) = $wantdt =~ /(\d{2})\s(\d{2}):/xs;
|
my ($wtday, $wthour) = $wantdt =~ /(\d{2})\s(\d{2}):/xs;
|
||||||
my $hod = sprintf "%02d", int $wthour + 1; # Stunde des Tages
|
my $hod = sprintf "%02d", int $wthour + 1; # Stunde des Tages
|
||||||
my $rad1h = RadiationAPIVal ($name, '?All', $wantdt, 'Rad1h', undef);
|
my $rad1h = RadiationAPIVal ($name, '?All', $wantdt, 'Rad1h', undef);
|
||||||
@@ -10603,7 +10617,7 @@ sub ___readCandQ {
|
|||||||
my $crang = 'simple';
|
my $crang = 'simple';
|
||||||
my $hc;
|
my $hc;
|
||||||
|
|
||||||
delete $data{$name}{nexthours}{"NextHour".sprintf("%02d",$num)}{cloudrange};
|
delete $data{$name}{nexthours}{'NextHour'.sprintf("%02d",$num)}{cloudrange};
|
||||||
|
|
||||||
if ($acu =~ /on_complex/xs) { # Autokorrektur complex soll genutzt werden
|
if ($acu =~ /on_complex/xs) { # Autokorrektur complex soll genutzt werden
|
||||||
$crang = cloud2bin ($wcc); # Range errechnen
|
$crang = cloud2bin ($wcc); # Range errechnen
|
||||||
@@ -10611,10 +10625,10 @@ sub ___readCandQ {
|
|||||||
my $daref = $data{$name}{circular}{$hod}{'pvrl_'.$sabin}{"$crang"};
|
my $daref = $data{$name}{circular}{$hod}{'pvrl_'.$sabin}{"$crang"};
|
||||||
|
|
||||||
if (ref $daref eq 'ARRAY') {
|
if (ref $daref eq 'ARRAY') {
|
||||||
$data{$name}{nexthours}{"NextHour".sprintf("%02d",$num)}{DaysInRange} = scalar (@{$daref}); # Anzahl Tage im selben Wetterbereich speichern
|
$data{$name}{nexthours}{'NextHour'.sprintf("%02d",$num)}{DaysInRange} = scalar (@{$daref}); # Anzahl Tage im selben Wetterbereich speichern
|
||||||
}
|
}
|
||||||
|
|
||||||
$data{$name}{nexthours}{"NextHour".sprintf("%02d",$num)}{cloudrange} = $crang;
|
$data{$name}{nexthours}{'NextHour'.sprintf("%02d",$num)}{cloudrange} = $crang;
|
||||||
}
|
}
|
||||||
elsif ($acu =~ /on_simple/xs) {
|
elsif ($acu =~ /on_simple/xs) {
|
||||||
($hc, $hq) = CircularSunCloudkorrVal ($hash, $hod, $sabin, 'simple', undef); # Korrekturfaktor/Qualität der Stunde des Tages (simple)
|
($hc, $hq) = CircularSunCloudkorrVal ($hash, $hod, $sabin, 'simple', undef); # Korrekturfaktor/Qualität der Stunde des Tages (simple)
|
||||||
@@ -10639,7 +10653,7 @@ sub ___readCandQ {
|
|||||||
debugLog ($paref, 'pvCorrectionRead', "$flex - fd: $fd, hod: $hod, Sun Altitude Bin: $sabin, Cloud range: $crang, corrf: $hc, quality: $hq");
|
debugLog ($paref, 'pvCorrectionRead', "$flex - fd: $fd, hod: $hod, Sun Altitude Bin: $sabin, Cloud range: $crang, corrf: $hc, quality: $hq");
|
||||||
}
|
}
|
||||||
|
|
||||||
$data{$name}{nexthours}{"NextHour".sprintf("%02d",$num)}{pvcorrf} = $hc."/".$hq;
|
$data{$name}{nexthours}{'NextHour'.sprintf("%02d",$num)}{pvcorrf} = $hc."/".$hq;
|
||||||
|
|
||||||
if ($fd == 0 && $hod) {
|
if ($fd == 0 && $hod) {
|
||||||
writeToHistory ( { paref => $paref, key => 'pvcorrfactor', val => $hc.'/'.$hq, hour => $hod } );
|
writeToHistory ( { paref => $paref, key => 'pvcorrfactor', val => $hc.'/'.$hq, hour => $hod } );
|
||||||
@@ -11858,7 +11872,7 @@ sub _createSummaries {
|
|||||||
$next4HoursSum->{Consumption} = $hour00confcremain;
|
$next4HoursSum->{Consumption} = $hour00confcremain;
|
||||||
$restOfDaySum->{Consumption} = $hour00confcremain;
|
$restOfDaySum->{Consumption} = $hour00confcremain;
|
||||||
|
|
||||||
for my $h (1..71) {
|
for my $h (1..MAXNEXTHOURS) {
|
||||||
my $idx = sprintf "%02d", $h;
|
my $idx = sprintf "%02d", $h;
|
||||||
my $pvfc = NexthoursVal ($name, "NextHour".$idx, 'pvfc', 0);
|
my $pvfc = NexthoursVal ($name, "NextHour".$idx, 'pvfc', 0);
|
||||||
my $confc = NexthoursVal ($name, "NextHour".$idx, 'confc', 0);
|
my $confc = NexthoursVal ($name, "NextHour".$idx, 'confc', 0);
|
||||||
@@ -11974,9 +11988,9 @@ sub _createSummaries {
|
|||||||
$surplus = 0 if($surplus < 0); # wegen Vergleich nompower vs. surplus
|
$surplus = 0 if($surplus < 0); # wegen Vergleich nompower vs. surplus
|
||||||
|
|
||||||
if ($debug =~ /collectData/xs) {
|
if ($debug =~ /collectData/xs) {
|
||||||
Log3 ($name, 1, "$name DEBUG> current Power values -> PV2Node: $pv2node W, PV2Bat: $pv2bat, PV2Grid: $pv2grid W, Other: $othprod W, GridIn: $gfeedin W, GridCon: $gcon W, BatIn: $batin W, BatOut: $batout W");
|
Log3 ($name, 1, "$name DEBUG> current Power values -> PV2Node: $pv2node W, PV2Bat: $pv2bat, PV2Grid: $pv2grid W, Other: $othprod W, GridIn: $gfeedin W, GridCon: $gcon W");
|
||||||
|
Log3 ($name, 1, "$name DEBUG> current Power Battery -> BatIn: $batin W (Node2Inv2DC: $node2inv2dc W), BatOut: $batout W (DC2Inv2Node: $dc2inv2node W)");
|
||||||
Log3 ($name, 1, "$name DEBUG> current Consumption result -> $consumption W");
|
Log3 ($name, 1, "$name DEBUG> current Consumption result -> $consumption W");
|
||||||
Log3 ($name, 1, "$name DEBUG> current Power Battery Inverter -> DC2Inv2Node: $dc2inv2node W, Node2Inv2DC: $node2inv2dc W");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
my $selfconsumptionrate = 0;
|
my $selfconsumptionrate = 0;
|
||||||
@@ -11991,6 +12005,7 @@ sub _createSummaries {
|
|||||||
$data{$name}{current}{autarkyrate} = $autarkyrate;
|
$data{$name}{current}{autarkyrate} = $autarkyrate;
|
||||||
$data{$name}{current}{tdConFcTillSunset} = sprintf "%.0f", $tdConFcTillSunset;
|
$data{$name}{current}{tdConFcTillSunset} = sprintf "%.0f", $tdConFcTillSunset;
|
||||||
$data{$name}{current}{surplus} = $surplus;
|
$data{$name}{current}{surplus} = $surplus;
|
||||||
|
$data{$name}{current}{dayAfterTomorrowPVfc} = $daftertomSum->{PV};
|
||||||
|
|
||||||
push @{$data{$name}{current}{surplusslidereg}}, $surplus; # Schieberegister PV Überschuß
|
push @{$data{$name}{current}{surplusslidereg}}, $surplus; # Schieberegister PV Überschuß
|
||||||
limitArray ($data{$name}{current}{surplusslidereg}, SPLSLIDEMAX);
|
limitArray ($data{$name}{current}{surplusslidereg}, SPLSLIDEMAX);
|
||||||
@@ -14693,26 +14708,13 @@ sub _genSpecialReadings {
|
|||||||
storeReading ($prpo.'_'.$kpi, (sprintf "%.1f", $dbo).' '.$hcsr{$kpi}{unit});
|
storeReading ($prpo.'_'.$kpi, (sprintf "%.1f", $dbo).' '.$hcsr{$kpi}{unit});
|
||||||
}
|
}
|
||||||
elsif ($kpi eq 'dayAfterTomorrowPVforecast') { # PV Vorhersage Summe für Übermorgen (falls Werte vorhanden), Forum:#134226
|
elsif ($kpi eq 'dayAfterTomorrowPVforecast') { # PV Vorhersage Summe für Übermorgen (falls Werte vorhanden), Forum:#134226
|
||||||
my $dayaftertomorrow = strftime "%Y-%m-%d", localtime($t + 172800); # Datum von Übermorgen
|
my $datpvfc = &{$hcsr{$kpi}{fn}} ($name, 'dayAfterTomorrowPVfc', $def);
|
||||||
my @allstrings = split ",", AttrVal ($name, 'setupInverterStrings', '');
|
|
||||||
my $fcsumdat = 0;
|
|
||||||
|
|
||||||
for my $strg (@allstrings) {
|
if ($datpvfc) {
|
||||||
for my $starttmstr (sort keys %{$data{$name}{solcastapi}{$strg}}) {
|
storeReading ($prpo.'_'.$kpi, (sprintf "%.0f", $datpvfc).$hcsr{$kpi}{unit});
|
||||||
next if($starttmstr !~ /$dayaftertomorrow/xs);
|
|
||||||
|
|
||||||
my $val = &{$hcsr{$kpi}{fn}} ($hash, $strg, $starttmstr, $hcsr{$kpi}{par}, $def);
|
|
||||||
$fcsumdat += $val;
|
|
||||||
|
|
||||||
debugLog ($paref, 'radiationProcess', "dayaftertomorrow PV forecast (raw) - $strg -> $starttmstr -> $val Wh");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($fcsumdat) {
|
|
||||||
storeReading ($prpo.'_'.$kpi, (int $fcsumdat). ' Wh');
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
storeReading ($prpo.'_'.$kpi, $fcsumdat. ' (no data available)');
|
storeReading ($prpo.'_'.$kpi, $datpvfc. ' (no data available)');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
elsif ($kpi =~ /currentRunMtsConsumer_/xs) {
|
elsif ($kpi =~ /currentRunMtsConsumer_/xs) {
|
||||||
@@ -14774,7 +14776,7 @@ sub _genSpecialReadings {
|
|||||||
|
|
||||||
my $hod = NexthoursVal ($hash, $idx, 'hourofday', '01');
|
my $hod = NexthoursVal ($hash, $idx, 'hourofday', '01');
|
||||||
my $confc = &{$hcsr{$kpi}{fn}} ($hash, $idx, $hcsr{$kpi}{par}, $def);
|
my $confc = &{$hcsr{$kpi}{fn}} ($hash, $idx, $hcsr{$kpi}{par}, $def);
|
||||||
#Log3 ($name, 1, "$name - tmoday: $tmoday -> $hod");
|
|
||||||
storeReading ($prpo.'_'.$kpi.'_'.$hod, $confc.$hcsr{$kpi}{unit});
|
storeReading ($prpo.'_'.$kpi.'_'.$hod, $confc.$hcsr{$kpi}{unit});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -22273,7 +22275,7 @@ sub calcDayHourMove {
|
|||||||
my $chour = shift;
|
my $chour = shift;
|
||||||
my $num = shift;
|
my $num = shift;
|
||||||
|
|
||||||
my $fh = $chour + $num;
|
my $fh = int ($chour) + $num;
|
||||||
my $fd = int ($fh / 24) ;
|
my $fd = int ($fh / 24) ;
|
||||||
$fh = $fh - ($fd * 24);
|
$fh = $fh - ($fd * 24);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user