Anwendungsbeispiel JsonMod - SolCast Solar-Vorhersage abrufen

Begonnen von freetz, 11 August 2021, 09:05:22

Vorheriges Thema - Nächstes Thema

Torxgewinde

Vielen Dank für die Definition für Solcast.
Ich denke es gibt noch einen Punkt bei der Zeitzone der nicht genau stimmt. Ich habe den aktuell gültigen Wert als UserReading dargestellt und musste dabei den Offset der Zeitzone mit einberechnen:

defmod Solcast JsonMod https://api.solcast.com.au/rooftop_sites/AAAA-BBBB-CCCC-DDDD/forecasts?format=json&api_key=ABCDEFGHIJKLMNOPQRSTUVWXYZ&hours=72
attr Solcast group Strom
attr Solcast interval 1,31 * * * *
attr Solcast readingList multi(jsonPath('$.forecasts[*]'), concat('pv_estimate_', property('period_end')), property('pv_estimate'));;\
single(jsonPath('$.forecasts[0].period'),'period','');;
attr Solcast stateFormat Jetzt: forecast_now W, Heute: forecast_today kWh, Morgen: forecast_tomorrow kWh, Übermorgen: forecast_overmorrow kWh
attr Solcast userReadings forecast_now {\
my $period = ReadingsVal($NAME, "period", 0);;\
$period =~ s/.*(\d\d).*/$1/;;\
\
my $offset = timegm(localtime()) - timelocal(localtime());;\
\
my ($date, $time) = split(" ", FmtDateTime(time()-$offset));;\
my ($hour, $min, $sec) = split(":", $time);;\
$time = $hour . $min . "00";;\
\
my ($date_plus, $time_plus) = split(" ", FmtDateTime(time()+30*60-$offset));;\
my ($hour_plus, $min_plus, $sec_plus) = split(":", $time_plus);;\
$time_plus = $hour_plus . $min_plus . "00";;\
\
my $hash = $defs{$NAME};;\
my $readings = $hash->{READINGS};;\
\
foreach my $reading ( keys %{$readings} ) {\
if ($reading =~ /$date/) {\
my ($dummy, $period_end) = split("T", $reading);;\
$period_end = substr($period_end, 0, 6);;\
\
##Log(0, "period_end: ".$period_end .">". $time ." && ". $time_plus);;\
\
if (($period_end > $time) && ($period_end <= $time_plus)) {\
##Log(0, "Treffer");;\
return round(1000*ReadingsVal($NAME,$reading,0),0);;\
}\
}\
}\
},\
forecast_today {\
my $total = 0;;\
my $period = ReadingsVal($NAME, "period", 0);;\
$period =~ s/.*(\d\d).*/$1/;;\
my ($date, $time) = split(" ", FmtDateTime(time()));;\
my ($hour, $min, $sec) = split(":", $time);;\
$time = $hour . $min . "00";;\
my $hash = $defs{$NAME};;\
my $readings = $hash->{READINGS};;\
foreach my $reading ( keys %{$readings} ) {\
if ($reading =~ /$date/) {\
my ($dummy, $period_end) = split("T", $reading);;\
$period_end = substr($period_end, 0, 6);;\
\
if ($period_end > $time)\
{\
my $val = ReadingsVal($NAME,$reading,0);;\
$total += $val/(60/$period);;\
}\
}\
}\
return round($total,2);;\
},\
forecast_tomorrow {\
use Time::Piece ();;\
use Time::Seconds;;\
\
my $total = 0;;\
my $period = ReadingsVal($NAME, "period", 0);;\
$period =~ s/.*(\d\d).*/$1/;;\
my ($date, $time) = split(" ", FmtDateTime(time()));;\
$date = Time::Piece->strptime($date, '%Y-%m-%d');;\
$date += ONE_DAY;;\
$date = $date->strftime('%Y-%m-%d');;\
my $hash = $defs{$NAME};;\
my $readings = $hash->{READINGS};;\
foreach my $reading (keys %{$readings}) {\
if ($reading =~ /$date/) {\
my $val = ReadingsVal($NAME, $reading, 0);;\
$total += $val/(60/$period);;\
}\
}\
return round($total,2);;\
},\
forecast_overmorrow {\
use Time::Piece ();;\
use Time::Seconds;;\
\
my $total = 0;;\
my $period = ReadingsVal($NAME, "period", 0);;\
$period =~ s/.*(\d\d).*/$1/;;\
my ($date, $time) = split(" ", FmtDateTime(time()));;\
$date = Time::Piece->strptime($date, '%Y-%m-%d');;\
$date += ONE_DAY;;\
$date += ONE_DAY;;\
$date = $date->strftime('%Y-%m-%d');;\
my $hash = $defs{$NAME};;\
my $readings = $hash->{READINGS};;\
foreach my $reading (keys %{$readings}) {\
if ($reading =~ /$date/) {\
my $val = ReadingsVal($NAME, $reading, 0);;\
$total += $val/(60/$period);;\
}\
}\
return round($total,2);;\
}
attr Solcast webCmd reread


Plotte ich mir die Werte von "forecast_now" now in einem Graphen über den Ertrag, passt das besser.

Ingo298

Hallo zusammen,
hier ist bezüglich der Solarvorschau alles gut erklärt wurden, leider hat jedoch Solcast den Request auf 10 pro Tag geändert. Momentan habe ich das über
interval*/59 7-17 * * *
gelöst. Aber ich denke für den Sommer wird das knapp werden.

Kann man das evtl. für https://www.solarprognose.de auch machen?
RPi2: Buster FHEM 6.2, FTUI-3, AMAD,10.1" Tablet; MiLight;IT;HM;Dect200;VZLogger;MQTT

sn0000py

welches solcast verwendest du?
ich habe 50 API Abrufe pro Tag frei und brauche pro Stunde 3 Abfragen (3 Seiten Ost,Süd,West)

Ingo298

Zitat von: sn0000py am 03 Januar 2023, 18:36:58
welches solcast verwendest du?
ich habe 50 API Abrufe pro Tag frei und brauche pro Stunde 3 Abfragen (3 Seiten Ost,Süd,West)

Ich habe mich auf https://solcast.com/ als free User (Home User) registriert, gibt es da noch was anderes ?
Ich benötige nur eine Seite kann auch nur max. 2 Seiten bzw. Dachflächen in Solcast anlegen
RPi2: Buster FHEM 6.2, FTUI-3, AMAD,10.1" Tablet; MiLight;IT;HM;Dect200;VZLogger;MQTT

sn0000py

ich glaub ich habe das gar nicht angelegt

https://api.solcast.com.au/world_pv_power/forecasts?latitude=47.1&longitude=14.1&capacity=18.085&tilt=28&azimuth=-60&loss_factor=0.9&hours=168&format=json&api_key=meinKey

so lese ich die direkt aus, und da darf ich 50 Aufrufe pro Tag machen

Ingo298

Zitat von: sn0000py am 03 Januar 2023, 19:45:36
ich glaub ich habe das gar nicht angelegt

https://api.solcast.com.au/world_pv_power/forecasts?latitude=47.1&longitude=14.1&capacity=18.085&tilt=28&azimuth=-60&loss_factor=0.9&hours=168&format=json&api_key=meinKey

so lese ich die direkt aus, und da darf ich 50 Aufrufe pro Tag machen

geht trotzdem nicht, Solcast hat das Tageslimit bei Neuregistrierung auf 10 gesetzt, aber troztdem dake
RPi2: Buster FHEM 6.2, FTUI-3, AMAD,10.1" Tablet; MiLight;IT;HM;Dect200;VZLogger;MQTT

jnewton957

Zitat von: Torxgewinde am 21 Oktober 2022, 20:00:57
Vielen Dank für die Definition für Solcast.
Ich denke es gibt noch einen Punkt bei der Zeitzone der nicht genau stimmt. Ich habe den aktuell gültigen Wert als UserReading dargestellt und musste dabei den Offset der Zeitzone mit einberechnen:

defmod Solcast JsonMod https://api.solcast.com.au/rooftop_sites/AAAA-BBBB-CCCC-DDDD/forecasts?format=json&api_key=ABCDEFGHIJKLMNOPQRSTUVWXYZ&hours=72
attr Solcast group Strom
attr Solcast interval 1,31 * * * *
attr Solcast readingList multi(jsonPath('$.forecasts[*]'), concat('pv_estimate_', property('period_end')), property('pv_estimate'));;\
single(jsonPath('$.forecasts[0].period'),'period','');;
attr Solcast stateFormat Jetzt: forecast_now W, Heute: forecast_today kWh, Morgen: forecast_tomorrow kWh, Übermorgen: forecast_overmorrow kWh
attr Solcast userReadings forecast_now {\
my $period = ReadingsVal($NAME, "period", 0);;\
$period =~ s/.*(\d\d).*/$1/;;\
\
my $offset = timegm(localtime()) - timelocal(localtime());;\
\
my ($date, $time) = split(" ", FmtDateTime(time()-$offset));;\
my ($hour, $min, $sec) = split(":", $time);;\
$time = $hour . $min . "00";;\
\
my ($date_plus, $time_plus) = split(" ", FmtDateTime(time()+30*60-$offset));;\
my ($hour_plus, $min_plus, $sec_plus) = split(":", $time_plus);;\
$time_plus = $hour_plus . $min_plus . "00";;\
\
my $hash = $defs{$NAME};;\
my $readings = $hash->{READINGS};;\
\
foreach my $reading ( keys %{$readings} ) {\
if ($reading =~ /$date/) {\
my ($dummy, $period_end) = split("T", $reading);;\
$period_end = substr($period_end, 0, 6);;\
\
##Log(0, "period_end: ".$period_end .">". $time ." && ". $time_plus);;\
\
if (($period_end > $time) && ($period_end <= $time_plus)) {\
##Log(0, "Treffer");;\
return round(1000*ReadingsVal($NAME,$reading,0),0);;\
}\
}\
}\
},\
forecast_today {\
my $total = 0;;\
my $period = ReadingsVal($NAME, "period", 0);;\
$period =~ s/.*(\d\d).*/$1/;;\
my ($date, $time) = split(" ", FmtDateTime(time()));;\
my ($hour, $min, $sec) = split(":", $time);;\
$time = $hour . $min . "00";;\
my $hash = $defs{$NAME};;\
my $readings = $hash->{READINGS};;\
foreach my $reading ( keys %{$readings} ) {\
if ($reading =~ /$date/) {\
my ($dummy, $period_end) = split("T", $reading);;\
$period_end = substr($period_end, 0, 6);;\
\
if ($period_end > $time)\
{\
my $val = ReadingsVal($NAME,$reading,0);;\
$total += $val/(60/$period);;\
}\
}\
}\
return round($total,2);;\
},\
forecast_tomorrow {\
use Time::Piece ();;\
use Time::Seconds;;\
\
my $total = 0;;\
my $period = ReadingsVal($NAME, "period", 0);;\
$period =~ s/.*(\d\d).*/$1/;;\
my ($date, $time) = split(" ", FmtDateTime(time()));;\
$date = Time::Piece->strptime($date, '%Y-%m-%d');;\
$date += ONE_DAY;;\
$date = $date->strftime('%Y-%m-%d');;\
my $hash = $defs{$NAME};;\
my $readings = $hash->{READINGS};;\
foreach my $reading (keys %{$readings}) {\
if ($reading =~ /$date/) {\
my $val = ReadingsVal($NAME, $reading, 0);;\
$total += $val/(60/$period);;\
}\
}\
return round($total,2);;\
},\
forecast_overmorrow {\
use Time::Piece ();;\
use Time::Seconds;;\
\
my $total = 0;;\
my $period = ReadingsVal($NAME, "period", 0);;\
$period =~ s/.*(\d\d).*/$1/;;\
my ($date, $time) = split(" ", FmtDateTime(time()));;\
$date = Time::Piece->strptime($date, '%Y-%m-%d');;\
$date += ONE_DAY;;\
$date += ONE_DAY;;\
$date = $date->strftime('%Y-%m-%d');;\
my $hash = $defs{$NAME};;\
my $readings = $hash->{READINGS};;\
foreach my $reading (keys %{$readings}) {\
if ($reading =~ /$date/) {\
my $val = ReadingsVal($NAME, $reading, 0);;\
$total += $val/(60/$period);;\
}\
}\
return round($total,2);;\
}
attr Solcast webCmd reread


Plotte ich mir die Werte von "forecast_now" now in einem Graphen über den Ertrag, passt das besser.

Muss hier nicht auch noch die 99_myUtils.pm wegen des forecast_overmorrow und ggf. forecast_now angepasst werden, um die Daten auch im FTUI oder graph anzeigen zu können ?
FHEM6.2 auf Pi5
V 1.66 nanoCUL 433 (IT)
V 1.66 nanoCUL868 (HM)
sqlite3 LogDb
ELRO AB440, DECT200,  TFA30.3125, esp8266, HM, TabletUI, IR-Schreiblesekopf (Udo),tibber Pulse, Kostal Pico, cfos Wallbox, Modbus TCP

mähschaf

Guten Abend,

Die Sache mit der Zeitzone scheint ja nicht nur für mich eine spannende Herausforderung zu sein, wie ich hier in mehreren Beiträgen sehe.

Meine folgende Lösung finde ich nicht ganz unelegant, weshalb ich sie gerne teilen möchte. Attribut readingList:

multi(jsonPath('$.forecasts[*]'), concat('pv_estimate_', POSIX::strftime('%Y-%m-%d %H:%M', POSIX::localtime(HTTP::Date::str2time(property('period_end'))))), property('pv_estimate'))
single(jsonPath('$.forecasts[0].period'),'period','')


Einen schönen Abend Euch,
Martin

Torxgewinde

Hallo,
@mähschaf: Das mit den auf die lokale Zeitzone angepassten Readings finde ich gut 👍. Das sich die nützliche str2time()-Funktion in HTTP::Date versteckt, muss man auch erstmal finden, Dankeschön.

Momentan sieht das Device bei mir so aus, wobei ich noch weiter umbauen möchte. Ich wollte analog zu einigen anderen Devices (z.B. Tibber, DWD) gerne "fc0_XX_value" Werte haben, die dann auf den relativen Tag und die Uhrzeit einen festen Bezug haben.

Hier der aktuelle Stand:
defmod Solcast JsonMod https://api.solcast.com.au/rooftop_sites/<ID>/forecasts?format=json&api_key=<KEY>&hours=72
attr Solcast userattr calc_fc
attr Solcast calc_fc my $sum = 0;;\
my($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime();;\
my $hour_expr = sprintf('^pv_estimate_EndsAt_LocalTime_%04d-%02d-%02d_%02d.*$', 1900+$year, $mon+1, $mday, $thisHour);;\
my $next_full_hour_expr = sprintf('^pv_estimate_EndsAt_LocalTime_%04d-%02d-%02d_%02d-00-00.*$', 1900+$year, $mon+1, $mday, $thisHour+1);;\
if ($thisHour == 23) {\
my($tsec,$tmin,$thour,$tmday,$tmon,$tyear,$twday,$tyday,$tisdst) = localtime(24*60*60+time());;\
$next_full_hour_expr = sprintf('^pv_estimate_EndsAt_LocalTime_%04d-%02d-%02d_%02d-00-00.*$', 1900+$tyear, $tmon+1, $tmday, 0);;\
}\
my $current_full_hour_expr = sprintf('^pv_estimate_EndsAt_LocalTime_%04d-%02d-%02d_%02d-00-00.*$', 1900+$year, $mon+1, $mday, $thisHour);;\
\
#find matches and sum up values\
my $matches = scalar #just return the count of matches\
map { $sum += ReadingsNum($NAME, $_, -1);; $_ } #sum up all values found\
grep { $_ =~ /$hour_expr|$next_full_hour_expr/ && $_ !~ /$current_full_hour_expr/} # filter readings to RegEx\
keys %{$defs{$NAME}{READINGS}};; # get all readings of device\
\
#since the estimate is in kW, but if there is more than one interval, we need to divide the sum\
if ($matches != 0) { $sum /= $matches };;\
\
#only update reading if all values for the hour are there\
my $period = ReadingsNum($NAME, 'period', -1);;\
$period =~ s/.*PT([0-9]+)M.*/$1/;;\
my $nr = int(60 / $period);;\
\
#only update if matches found, otherwise keep it as it is\
($matches == $nr) ? return $sum : return;;
attr Solcast group Strom
attr Solcast icon weather_sunrise
attr Solcast interval 1,31 * * * *
attr Solcast readingList multi(jsonPath('$.forecasts[*]'), concat('pv_estimate_', property('period_end')), property('pv_estimate'));;\
multi(jsonPath('$.forecasts[*]'), concat('pv_estimate_EndsAt_LocalTime_', POSIX::strftime('%Y-%m-%d_%H-%M-%S', POSIX::localtime(HTTP::Date::str2time(property('period_end'))))), property('pv_estimate'))\
single(jsonPath('$.forecasts[0].period'),'period','');;
attr Solcast room Photovoltaik
attr Solcast sortby 12
attr Solcast stateFormat Jetzt: forecast_now W, Heute: forecast_today kWh (fc0_total kWh), Morgen: forecast_tomorrow kWh, Übermorgen: forecast_overmorrow kWh
attr Solcast userReadings fc0_00_total {\
my $thisHour = 0;;\
return eval(AttrVal($NAME, "calc_fc", ""));;\
},\
fc0_01_total {\
my $thisHour = 1;;\
return eval(AttrVal($NAME, "calc_fc", ""));;\
},\
fc0_02_total {\
my $thisHour = 2;;\
return eval(AttrVal($NAME, "calc_fc", ""));;\
},\
fc0_03_total {\
my $thisHour = 3;;\
return eval(AttrVal($NAME, "calc_fc", ""));;\
},\
fc0_04_total {\
my $thisHour = 4;;\
return eval(AttrVal($NAME, "calc_fc", ""));;\
},\
fc0_05_total {\
my $thisHour = 5;;\
return eval(AttrVal($NAME, "calc_fc", ""));;\
},\
fc0_06_total {\
my $thisHour = 6;;\
return eval(AttrVal($NAME, "calc_fc", ""));;\
},\
fc0_07_total {\
my $thisHour = 7;;\
return eval(AttrVal($NAME, "calc_fc", ""));;\
},\
fc0_08_total {\
my $thisHour = 8;;\
return eval(AttrVal($NAME, "calc_fc", ""));;\
},\
fc0_09_total {\
my $thisHour = 9;;\
return eval(AttrVal($NAME, "calc_fc", ""));;\
},\
fc0_10_total {\
my $thisHour = 10;;\
return eval(AttrVal($NAME, "calc_fc", ""));;\
},\
fc0_11_total {\
my $thisHour = 11;;\
return eval(AttrVal($NAME, "calc_fc", ""));;\
},\
fc0_12_total {\
my $thisHour = 12;;\
return eval(AttrVal($NAME, "calc_fc", ""));;\
},\
fc0_13_total {\
my $thisHour = 13;;\
return eval(AttrVal($NAME, "calc_fc", ""));;\
},\
fc0_14_total {\
my $thisHour = 14;;\
return eval(AttrVal($NAME, "calc_fc", ""));;\
},\
fc0_15_total {\
my $thisHour = 15;;\
return eval(AttrVal($NAME, "calc_fc", ""));;\
},\
fc0_16_total {\
my $thisHour = 16;;\
return eval(AttrVal($NAME, "calc_fc", ""));;\
},\
fc0_17_total {\
my $thisHour = 17;;\
return eval(AttrVal($NAME, "calc_fc", ""));;\
},\
fc0_18_total {\
my $thisHour = 18;;\
return eval(AttrVal($NAME, "calc_fc", ""));;\
},\
fc0_19_total {\
my $thisHour = 19;;\
return eval(AttrVal($NAME, "calc_fc", ""));;\
},\
fc0_20_total {\
my $thisHour = 20;;\
return eval(AttrVal($NAME, "calc_fc", ""));;\
},\
fc0_21_total {\
my $thisHour = 21;;\
return eval(AttrVal($NAME, "calc_fc", ""));;\
},\
fc0_22_total {\
my $thisHour = 22;;\
return eval(AttrVal($NAME, "calc_fc", ""));;\
},\
fc0_23_total {\
my $thisHour = 23;;\
return eval(AttrVal($NAME, "calc_fc", ""));;\
},\
fc0_total {\
return round(ReadingsNum($NAME, "fc0_00_total", 0) +\
ReadingsNum($NAME, "fc0_01_total", 0) +\
ReadingsNum($NAME, "fc0_02_total", 0) +\
ReadingsNum($NAME, "fc0_03_total", 0) +\
ReadingsNum($NAME, "fc0_04_total", 0) +\
ReadingsNum($NAME, "fc0_05_total", 0) +\
ReadingsNum($NAME, "fc0_06_total", 0) +\
ReadingsNum($NAME, "fc0_07_total", 0) +\
ReadingsNum($NAME, "fc0_08_total", 0) +\
ReadingsNum($NAME, "fc0_09_total", 0) +\
ReadingsNum($NAME, "fc0_10_total", 0) +\
ReadingsNum($NAME, "fc0_11_total", 0) +\
ReadingsNum($NAME, "fc0_12_total", 0) +\
ReadingsNum($NAME, "fc0_13_total", 0) +\
ReadingsNum($NAME, "fc0_14_total", 0) +\
ReadingsNum($NAME, "fc0_15_total", 0) +\
ReadingsNum($NAME, "fc0_16_total", 0) +\
ReadingsNum($NAME, "fc0_17_total", 0) +\
ReadingsNum($NAME, "fc0_18_total", 0) +\
ReadingsNum($NAME, "fc0_19_total", 0) +\
ReadingsNum($NAME, "fc0_20_total", 0) +\
ReadingsNum($NAME, "fc0_21_total", 0) +\
ReadingsNum($NAME, "fc0_22_total", 0) +\
ReadingsNum($NAME, "fc0_23_total", 0), 2);;\
},\
forecast_now {\
my $period = ReadingsVal($NAME, "period", 0);;\
$period =~ s/.*(\d\d).*/$1/;;\
\
my $offset = timegm(localtime()) - timelocal(localtime());;\
\
my ($date, $time) = split(" ", FmtDateTime(time()-$offset));;\
my ($hour, $min, $sec) = split(":", $time);;\
$time = $hour . $min . "00";;\
\
my ($date_plus, $time_plus) = split(" ", FmtDateTime(time()+30*60-$offset));;\
my ($hour_plus, $min_plus, $sec_plus) = split(":", $time_plus);;\
$time_plus = $hour_plus . $min_plus . "00";;\
\
my $hash = $defs{$NAME};;\
my $readings = $hash->{READINGS};;\
\
foreach my $reading ( grep { $_ =~ /^pv_estimate_.*Z$/ } keys %{$readings} ) {\
if ($reading =~ /$date/) {\
my ($dummy, $period_end) = split("T", $reading);;\
$period_end = substr($period_end, 0, 6);;\
\
##Log(0, "🐞 period_end: ".$period_end .">". $time ." && ". $time_plus);;\
\
if (($period_end > $time) && ($period_end <= $time_plus)) {\
##Log(0, "🐞 Treffer");;\
return round(1000*ReadingsVal($NAME,$reading,0),0);;\
}\
}\
}\
},\
forecast_today {\
my $total = 0;;\
my $period = ReadingsVal($NAME, "period", 0);;\
$period =~ s/.*(\d\d).*/$1/;;\
my ($date, $time) = split(" ", FmtDateTime(time()));;\
my ($hour, $min, $sec) = split(":", $time);;\
$time = $hour . $min . "00";;\
my $hash = $defs{$NAME};;\
my $readings = $hash->{READINGS};;\
foreach my $reading ( grep { $_ =~ /^pv_estimate_.*Z$/ } keys %{$readings} ) {\
if ($reading =~ /$date/) {\
my ($dummy, $period_end) = split("T", $reading);;\
$period_end = substr($period_end, 0, 6);;\
\
if ($period_end > $time)\
{\
my $val = ReadingsVal($NAME,$reading,0);;\
$total += $val/(60/$period);;\
}\
}\
}\
return round($total,2);;\
},\
forecast_tomorrow {\
use Time::Piece ();;\
use Time::Seconds;;\
\
my $total = 0;;\
my $period = ReadingsVal($NAME, "period", 0);;\
$period =~ s/.*(\d\d).*/$1/;;\
my ($date, $time) = split(" ", FmtDateTime(time()));;\
$date = Time::Piece->strptime($date, '%Y-%m-%d');;\
$date += ONE_DAY;;\
$date = $date->strftime('%Y-%m-%d');;\
my $hash = $defs{$NAME};;\
my $readings = $hash->{READINGS};;\
foreach my $reading (grep { $_ =~ /^pv_estimate_.*Z$/ } keys %{$readings}) {\
if ($reading =~ /$date/) {\
my $val = ReadingsVal($NAME, $reading, 0);;\
$total += $val/(60/$period);;\
}\
}\
return round($total,2);;\
},\
forecast_overmorrow {\
use Time::Piece ();;\
use Time::Seconds;;\
\
my $total = 0;;\
my $period = ReadingsVal($NAME, "period", 0);;\
$period =~ s/.*(\d\d).*/$1/;;\
my ($date, $time) = split(" ", FmtDateTime(time()));;\
$date = Time::Piece->strptime($date, '%Y-%m-%d');;\
$date += ONE_DAY;;\
$date += ONE_DAY;;\
$date = $date->strftime('%Y-%m-%d');;\
my $hash = $defs{$NAME};;\
my $readings = $hash->{READINGS};;\
foreach my $reading (grep { $_ =~ /^pv_estimate_.*Z$/ } keys %{$readings}) {\
if ($reading =~ /$date/) {\
my $val = ReadingsVal($NAME, $reading, 0);;\
$total += $val/(60/$period);;\
}\
}\
return round($total,2);;\
}
attr Solcast webCmd reread
attr Solcast widgetOverride calc_fc:textField-long,87