Anwendungsbeispiel JsonMod - SolCast Solar-Vorhersage abrufen

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

Vorheriges Thema - Nächstes Thema

freetz

Hallo zusammen und erst einmal ein großes Danke an @herrmannj für dieses klasse Modul! Das wird mir in Zukunft so einige Geräteabfragen vereinfachen.

Im Moment stehe ich vor einem etwas komplexeren Problem:
Ich möchte für die vorhersagebasierte Ladung der Batterie meiner Photovoltaik-Anlage die Solareinstrahlung von SolCast abrufen. Das Problem dabei ist, dass bei den Vorhersagedaten generell die nächsten 168 Halbstundenvorhersagen kommen, das entsprechende Datum wird dann auf untergeordneter Ebene mitgeliefert. Verkürzt sieht das Ganze dann so aus:
{
  "forecasts": [
    {
      "pv_estimate": "9.5",
      "pv_estimate10": "6",
      "pv_estimate90": "13.8",
      "period_end": "2018-01-01T01:00:00.00000Z",
      "period": "PT30M"
    },
    {
      "pv_estimate": "10",
      "pv_estimate10": "8",
      "pv_estimate90": "12",
      "period_end": "2018-01-01T12:30:00.00000Z",
      "period": "PT30M"
    }
  ]
}


Für die Vorhersage für den heutigen Solarertrag müsste ich also $.forecasts[0], $.forecasts[1] bis $.forecasts
  • einlesen und dann immer vergleichen, ob period_end noch dem heutigen Tag entspricht. Idealerweise müsste dabei noch dieser UTC-codierte Zeitstempel an die lokalen Zeitzone angepasst werden (oder umgekehrt).
    Für die Tagessumme müssten dann alle Werte des Tages aus pv_estimate (Werte in kWh) addiert werden und durch (60 / period) geteilt werden, weil die Werte nicht stündlich, sondern halbstündlich (bei kostenpflichtigen Paketen auch in noch geringeren Abständen) ausgegeben werden.

    Am Ende sollte dann ein (halb-)stündlich aktualisierter Wert stehen, der den voraussichtlich noch zu erwartenden Solarertrag ausgibt. Anhand dessen könnte man dann die Ladung der Batterie oder das Einschalten anderer Verbraucher steuern.

    Meine Frage ist jetzt, ob sich das irgendwie mit JsonMod umsetzen lässt, oder ob ich dafür doch ein eigenes Modul stricken muss. Momentan habe ich ein selbstgestricktes Python-Programm am Laufen, das das oben genannte macht, vielleicht hilft das noch zum Verständnis:
    https://github.com/fredlcore/SolarEdge_Predictive_Charging/

    Da ich sonst aber alles mit FHEM steuere, würde ich mich sehr freuen, wenn ich diese Funktionalität nicht auslagern müsste.

    Danke schon einmal für jeden Hinweis!
Alle Infos zur Anbindung von Heizungssystemen mit PPS-, LPB- bzw. BSB-Bus ans LAN gibt es hier:
https://github.com/fredlcore/bsb_lan

Alle Infos zum WLAN-Interface "Robotan" für Ambrogio/Stiga/Wolf und baugleiche Rasenmähroboter:
https://github.com/fredlcore/robotan

frober

Raspi 3b mit Raspbian Buster und relativ aktuellem Fhem,  FS20, LGW, PCA301, Zigbee, MQTT, MySensors mit RS485(CAN-Receiver) und RFM69, etc.,
einiges umgesetzt, vieles in Planung, smile

********************************************
...man wächst mit der Herausforderung...

freetz

#2
Ah, das ist schon mal ein guter Hinweis! Aber "nativ" wäre natürlich noch schöner :).
Ich habe jetzt mit
multi(jsonPath('$.forecasts[*]'), concat('pv_estimate_', index()), property('pv_estimate'));
eine Liste aller über 300 Einträge mit den jeweiligen Ertragsvoraussagen bekommen.
Wenn ich hier jetzt statt index() den Inhalt von period_end einsetzen könnte, dann ließen sich die Readings schon mal nach dem jeweiligen Tag filtern, und dann wäre das mit der Summierung auch schon mal einen guten Schritt einfacher...

EDIT: jsonPath('$.forecasts[index()].pv_period') anstelle von index() gibt leider einen Syntax Error
Alle Infos zur Anbindung von Heizungssystemen mit PPS-, LPB- bzw. BSB-Bus ans LAN gibt es hier:
https://github.com/fredlcore/bsb_lan

Alle Infos zum WLAN-Interface "Robotan" für Ambrogio/Stiga/Wolf und baugleiche Rasenmähroboter:
https://github.com/fredlcore/robotan

freetz

Yay, mit
multi(jsonPath('$.forecasts[*]'), concat('pv_estimate_', property('period_end')), property('pv_estimate'));
hat's geklappt - sehr schön :).
Ich denke, jetzt komme ich weiter, werde das Ergebnis dann aber gerne hier bei Interesse noch posten...
Alle Infos zur Anbindung von Heizungssystemen mit PPS-, LPB- bzw. BSB-Bus ans LAN gibt es hier:
https://github.com/fredlcore/bsb_lan

Alle Infos zum WLAN-Interface "Robotan" für Ambrogio/Stiga/Wolf und baugleiche Rasenmähroboter:
https://github.com/fredlcore/robotan

MadMax-FHEM

Zitat von: frober am 11 August 2021, 09:30:22
Du kannst Python direkt mit Fhem nutzen, vielleicht ist das eine Alternative
https://forum.fhem.de/index.php/topic,63816.60.html

Zitat von: freetz am 11 August 2021, 10:05:14
Ah, das ist schon mal ein guter Hinweis! Aber "nativ" wäre natürlich noch schöner :).

Evtl. eine (weitere) Alternative: https://forum.fhem.de/index.php/topic,115230.msg1094931.html#msg1094931

Gruß, Joachim
FHEM PI3B+ Bullseye: HM-CFG-USB, 40x HM, ZWave-USB, 13x ZWave, EnOcean-PI, 15x EnOcean, HUE/deCONZ, CO2, ESP-Multisensor, Shelly, alexa-fhem, ...
FHEM PI2 Buster: HM-CFG-USB, 25x HM, ZWave-USB, 4x ZWave, EnOcean-PI, 3x EnOcean, Shelly, ha-bridge, ...
FHEM PI3 Buster (Test)

freetz

Sehr cool! ich bin mit Python an sich aber noch am Fremdeln (auch, weil ich seit über 20 Jahren Perl als erste Wahl beim Programmieren verwende), insofern würde ich erst einmal schauen, ob ich es auch mit "Bordmitteln" hinbekomme. Aber als Backup ist das eine sehr coole Option!
Alle Infos zur Anbindung von Heizungssystemen mit PPS-, LPB- bzw. BSB-Bus ans LAN gibt es hier:
https://github.com/fredlcore/bsb_lan

Alle Infos zum WLAN-Interface "Robotan" für Ambrogio/Stiga/Wolf und baugleiche Rasenmähroboter:
https://github.com/fredlcore/robotan

freetz

So sieht die (für diesen Teil) fertige Lösung aus, für alle, die es interessiert:
attr SolCast readingList multi(jsonPath('$.forecasts[*]'), concat('pv_estimate_', property('period_end')), property('pv_estimate'));;\
single(jsonPath('$.forecasts[0].period'),'period','');;
attr SolCast userReadings forecast_today {\
  my $total=0;;\
  my $period = ReadingsVal("SolCast", "period", 0);;\
  $period =~ s/.*(\d\d).*/$1/;;\
  my ($date, $time) = split(" ", FmtDateTime(time()));;\
  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 $total;;\
}
Alle Infos zur Anbindung von Heizungssystemen mit PPS-, LPB- bzw. BSB-Bus ans LAN gibt es hier:
https://github.com/fredlcore/bsb_lan

Alle Infos zum WLAN-Interface "Robotan" für Ambrogio/Stiga/Wolf und baugleiche Rasenmähroboter:
https://github.com/fredlcore/robotan

xerion

Bzgl. Solarprognose kann ich dir dieses Modul auch empfehlen, dann brauchst du nicht alles selber machen, was es schon gibt ;-)
https://forum.fhem.de/index.php/topic,117864.1155.html
Ich würde mich  freuen, wenn du meinen Einladungscode für Tibber, der Stromanbieter, der dir hilft, deinen Stromverbrauch zu verstehen und zu reduzieren, nutzt: https://invite.tibber.com/5fc08jbs. So bekommen wir beide 50 Euro und 100 % Ökostrom / https://geld-fuer-eauto.de/ref/334561880

freetz

Danke, ich glaube, da war ich bei meiner Recherche auch schon darauf gestoßen, aber das scheint alles auf DWD-Daten zu basieren. Der Punkt bei mir ist, dass die DWD-Daten im Tagesverlauf nur alle sechs Stunden aktualisiert werden, SolCast hingegen aktualisiert alle 30 Minuten. So sind die Werte für den gesamten Tag am Morgen noch relativ identisch, aber zum Nachmittag hin kommen doch Abweichungen von bis zu 20 Prozent zustande (bei mir zumindest). Das ist gerade für mein Anwendungsziel, wo ich die Batterie so spät wie möglich am Tag laden möchte, ein no-go, so dass ich DWD nur als Backup einsetzen würde, falls SolCast irgendwann auch für kleinere Installationen kostenpflichtig wird.

Da sich die Tagesprognose für SolCast dank JsonMod mit den oben genannten wenigen Zeilen in FHEM einbinden lässt, war der Aufwand zum Glück auch überschaubar. Vielleicht wäre das umgekehrt etwas, was die Modul-Maintainer übernehmen wollen würden, um mehrere Datenbasen anbieten zu können.
Alle Infos zur Anbindung von Heizungssystemen mit PPS-, LPB- bzw. BSB-Bus ans LAN gibt es hier:
https://github.com/fredlcore/bsb_lan

Alle Infos zum WLAN-Interface "Robotan" für Ambrogio/Stiga/Wolf und baugleiche Rasenmähroboter:
https://github.com/fredlcore/robotan

xerion

Es wäre vielleicht ganz hilfreich wenn du deine komplette Definition zur Verfügung stellen könntest damit haben es dann die nächsten Einsteiger einfacher.
Ich würde mich  freuen, wenn du meinen Einladungscode für Tibber, der Stromanbieter, der dir hilft, deinen Stromverbrauch zu verstehen und zu reduzieren, nutzt: https://invite.tibber.com/5fc08jbs. So bekommen wir beide 50 Euro und 100 % Ökostrom / https://geld-fuer-eauto.de/ref/334561880

freetz

Was meinst Du mit "kompletter Definition"? Welche DbLogExcludes, interval etc. ich da noch verwende, ist ja von Fall zu Fall unterschiedlich und verwirrt gerade Einsteiger vielleicht dahingehend, was denn nun das Relevante ist. Oder meinst Du alle damit zusammenhängenden Devices (DOIFs, SolCast Reporting, Max Eco Schalter zum vorzeitigen Batterieladen, Wechselrichter-Batterie-Anbindung)? Auch das ist ja am Ende ziemlich speziell auf meine Situation hier zugeschnitten.
Aber ich teile gerne, was sinnvoll erscheint...
Alle Infos zur Anbindung von Heizungssystemen mit PPS-, LPB- bzw. BSB-Bus ans LAN gibt es hier:
https://github.com/fredlcore/bsb_lan

Alle Infos zum WLAN-Interface "Robotan" für Ambrogio/Stiga/Wolf und baugleiche Rasenmähroboter:
https://github.com/fredlcore/robotan

xerion

#11
Als grundsätzlich meinte ich die defmod von dem JsonMod, das würde ja schon helfen wie ein Anfänger es definieren muss bzgl. der API. Dann wäre natürlich interessant wie du mit den den ganzen Daten (Readings)  weiter umgehst. Was berechnest du wie, auch wenn es auf dein System zugeschnitten hilft es m.E. um das System zu verstehen und sich ggf. selber davon Use Cases ableiten zu können.
Es gibt auch "halbe" Beispiele" über HTTPMOD. Du hast dich ja jetzt für JsonMod entschieden und bestimmt einen Grund dafür gehabt.

gerade im Bezug auf den Titel des Threads "Anwendungsbeispiel" fehlt mir der Kontext um aus diesen Einträgen ein komplettes Anwendungsbeispiel ableiten zu können.
Bitte nicht falsch verstehen, aber mit den Informationen könnte man kein Anwendungsbeispiel für SolCast ableiten. Hier geht es eher darum wie man ein usereading in JsonMod definiert.
Ich würde mich  freuen, wenn du meinen Einladungscode für Tibber, der Stromanbieter, der dir hilft, deinen Stromverbrauch zu verstehen und zu reduzieren, nutzt: https://invite.tibber.com/5fc08jbs. So bekommen wir beide 50 Euro und 100 % Ökostrom / https://geld-fuer-eauto.de/ref/334561880

freetz

Ich weiß nicht, ob das defmod wirklich den großen Unterschied macht, denn das besteht ja nur aus der URL, aber bitte:
defmod SolCast JsonMod https://api.solcast.com.au/rooftop_sites/xxx/forecasts?format=json&api_key=yyy
Wobei xxx durch die Site-ID und yyy durch den API Key ersetzt werden muss. Wer dies nicht direkt im Device speichern möchte, findet hier Alternativen:
https://forum.fhem.de/index.php/topic,109398.0.html

Wie man mit den Readings umgeht, ist ja eben sehr auf den Anwendungskontext zugeschnitten, das userReading "forecast_today" gibt zurück, wie viel prognostizierte Sonnenenergie für den Tag noch erwartet wird. Die Info kann mir schon reichen, wenn ich einfach nur Graphen pflegen will, ich kann damit die Ladung meines E-Autos aktivieren oder wie in meinem Fall das Aufladen des PV-Speichers zu einem bestimmten Zeitpunkt starten.

Insofern entspricht der Titel exakt dem, was das userReading macht: Es ruft über JsonMod die SolCast Solar-Vorhersage ab, und stellt diese für weitere Zwecke zur Verfügung, allerdings kumuliert für den Tag und nicht halbstundenweise, wie die Daten "roh" ankommen. Insofern ist es schon mehr, als nur ein userReading in JsonMod.

Im Endeffekt ist es so, wie bei Proplanta oder einem anderen Wetter-Modul auch. Da gibt es ja ebenfalls viele Einsatzmöglichkeiten, die sehr unterschiedlich ausfallen können, aber auch der Wert alleine (insbesondere, wenn er wie hier als kumuliertem Tageswert nicht direkt abrufbar ist) ist an sich schon aussagekräftig. Alles weitere, was ich damit mache, sind verschiedene DOIFs die basierend auf diesem Wert Aktionen starten. Da gibt es schon sehr viele, sehr gute Tutorials, als dass ich da noch etwas Sinnvolles beitragen könnte.

JsonMod habe ich gegenüber HTTPMOD bevorzugt, weil ich Readings generieren muss, deren Bezeichnung aus abgerufenen Werten bestehen und die dann wiederum einen anderen abgerufenen Wert zugewiesen bekommen. Konkret: Der Zeitstempel eines Wertes wird Teil des Namens des Readings, der eigentliche Solarertrag dann der Wert des Readings, und das für alle Einträge des Tages.
Kann gut sein, dass man das in HTTPMOD (was ich sonst auch gerne verwende) auch irgendwie hinbekommen hätte, aber so ist es mit einer attr-Zeile möglich, die Readings in die richtige Form zu bekommen. Das eigentliche userReading würde mit HTTPMOD genau so funktionieren, wenn man damit eine JSON-Struktur abgerufen und entsprechend umbenannt bekommt.
Alle Infos zur Anbindung von Heizungssystemen mit PPS-, LPB- bzw. BSB-Bus ans LAN gibt es hier:
https://github.com/fredlcore/bsb_lan

Alle Infos zum WLAN-Interface "Robotan" für Ambrogio/Stiga/Wolf und baugleiche Rasenmähroboter:
https://github.com/fredlcore/robotan

xerion

Danke für deine ausführliche Rückmeldung. Ich habe das jetzt mal mit den Informationen aus diesem Thread versucht aufzubauen und bekomme bei den userReading immer folgenden Fehler:

Error evaluating SolCast userReading forecast_today: Experimental aliasing via reference not enabled at (eval 233678) line 2.
Ich würde mich  freuen, wenn du meinen Einladungscode für Tibber, der Stromanbieter, der dir hilft, deinen Stromverbrauch zu verstehen und zu reduzieren, nutzt: https://invite.tibber.com/5fc08jbs. So bekommen wir beide 50 Euro und 100 % Ökostrom / https://geld-fuer-eauto.de/ref/334561880

xerion

Fehler gefunden, leider Copy&Paste Fehler, ein / war noch versteckt ;-)
Ich würde mich  freuen, wenn du meinen Einladungscode für Tibber, der Stromanbieter, der dir hilft, deinen Stromverbrauch zu verstehen und zu reduzieren, nutzt: https://invite.tibber.com/5fc08jbs. So bekommen wir beide 50 Euro und 100 % Ökostrom / https://geld-fuer-eauto.de/ref/334561880

freetz

Prima, das freut mich - vielleicht mache ich für die Batteriesteuerung mal einen eigenen Thread auf, wenn es dafür Interesse gibt, das würde nämlich hier wirklich nicht mehr zum Topic passen, da das dann über ModbusAttr läuft.
Alle Infos zur Anbindung von Heizungssystemen mit PPS-, LPB- bzw. BSB-Bus ans LAN gibt es hier:
https://github.com/fredlcore/bsb_lan

Alle Infos zum WLAN-Interface "Robotan" für Ambrogio/Stiga/Wolf und baugleiche Rasenmähroboter:
https://github.com/fredlcore/robotan

Aurel_B

Danke dir freetz für deine Arbeit! Ich habe diese etwas weiterentwickelt und zusätzlich noch ein UserReading für morgen erstellt sowie eine Funktion, welche bei jedem Aufruf die aktuellen Prognosewerte in einen dblog schreibt. Historische Werte werden dabei belassen, es werden nur die abgerufenen Werte überschrieben. Hintergrund: so kann ich mir einen Graphen plotten der mir eine Vorhersage liefert, aber auch einen Rückblick damit ich schauen kann, wie gut die Prognose stimmt. Das UserReading für morgen brauche ich um z.B. am Abend vorher gewisse Grossverbraucher mit dem günstigen Nachttarif laufen lassen zu können falls der prognostizierte Solarstrom nicht reichen sollte.

Hier das UserReading:


forecast_today {
my $total = 0;
my $period = ReadingsVal("SolCast", "period", 0);
$period =~ s/.*(\d\d).*/$1/;
my ($date, $time) = split(" ", FmtDateTime(time()));
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 $total;
},
forecast_tomorrow {
use Time::Piece ();
use Time::Seconds;

my $total = 0;
my $period = ReadingsVal("SolCast", "period", 0);
$period =~ s/.*(\d\d).*/$1/;
my ($date, $time) = split(" ", FmtDateTime(time()));
my $date = Time::Piece->strptime($date, '%Y-%m-%d');
$date += ONE_DAY;
$date = $date->strftime('%Y-%m-%d');
my $hash = $defs{"SolCast"};
my $readings = $hash->{READINGS};
foreach my $reading (keys %{$readings}) {
if ($reading =~ /$date/) {
my $val = ReadingsVal("SolCast", $reading, 0);
$total += $val/(60/$period);
}
}
return $total;
}


Und hier die Funktion um die Vorhersage im dblog zu speichern (gehört in MyUtils):


# Braucht 3 Argumente: $name = Name des "Solcast" Devices, $dblog und $dbrep Namen der dblog/dbrep Devices. Bei jedem Aufruf wird der Forecast für morgen sowie alle aktuellen Prognosewerte gespeichert. Die Prognosewerte werden dabei überschrieben, alte Werte bleiben bestehen
sub forecast_in_DB_speichern($$$) {
my ( $name, $dblog, $dbrep ) = @_;
my $val = ReadingsVal($name, "forecast_tomorrow", 0);
my $time = strftime('%Y-%m-%d %H:%M:%S', localtime(time + 86400));
fhem("set ".$dblog." addCacheLine $time|$name|JSONMOD|forecast|forecast|$val");
my %hash = %{$defs{$name}{READINGS}};
my @pv_estimates = sort grep /pv_estimate/, keys %hash;
# Max/Min Forecast Zeitpunkt ermitteln
$pv_estimates[-1] =~ m/pv_estimate_(.*)/;
my $max = str2time($1);
$pv_estimates[0] =~ m/pv_estimate_(.*)/;
my $min = str2time($1);
# Alle alten Werte löschen die aktualisiert werden sollen
fhem("set ".$dbrep." sqlCmd DELETE FROM `history` WHERE DEVICE = '".$name."' AND `TIMESTAMP` >= FROM_UNIXTIME(".$min.") AND `TIMESTAMP` <= FROM_UNIXTIME(".$max.")");

foreach my $reading (@pv_estimates) {
$reading =~ m/pv_estimate_(.*)/;
$time = strftime('%Y-%m-%d %H:%M:%S', localtime(str2time($1)));
$val = ReadingsVal($name, $reading, 0) * 1000;
fhem("set ".$dblog." addCacheLine $time|$name|JSONMOD|pv_estimate|pv_estimate|$val");
}
}


Die eigentliche Speicherung erfolgt dann in einem at:

defmod at_SolCast_Prognose_speichern at *22:00 {forecast_in_DB_speichern('SolCast','dblog','DB_Bereinigung_Kostal')}

Prognosen siehe Anhang. Es besteht noch Optimierungsbedarf, im Moment habe ich alle meine Module (O, S & W) zusammengefasst zu einer Anlage (Südausrichtung). Wenn ich Zeit und Lust habe, splitte ich das auf 3 Anlagen mit der korrekten Ausrichtung und Neigung und aktualisiere dann meinen Code. Was mir auch schon aufgefallen ist: Nebel/"grauer Himmel" wird nicht richtig prognostiziert, so ist z.B. bei uns im Moment Hochnebel und der PV Ertrag entsprechend gering, das spiegelt sich aber nicht in der SolCast Prognose wieder (siehe auch 10. Oktober, das war auch so ein Tag wo bis am Mittag Hochnebel herrschte). Die DWD Werte kann ich leider nicht benutzen da keine Station bei mir in der Nähe ist.


DS_Starter

Hallo zusammen,

ich bin zufällig auf diesen Thread gestoßen.
Nur als Info ... ich hatte ursprünglich noch vor SolCast als weitere Datenquelle zu DWD in das SolarForecast Modul zu integrieren.
Mir war nur nicht klar ob es diesen Bedarf überhaupt gibt.

Meines Wissens können kostenfrei nur ca. 20 Abfragen bei SolCast pro Tag getätigt werden oder bin ich im Irrtum ?

Da das Modul Solarforecast ohne Datenbank arbeitet, benötigt es keine DbLog Definition. Dadurch ist es für die breite Masse der Nicht-DB User zugänglich was ein wesentliches Kriterium bei der Erstellung des Moduls war.
Vielleicht schaffe ich die Integration es über das Winterhalbjahr.
ESXi@NUC+Debian+MariaDB, PV: SMA, Victron MPII+Pylontech+CerboGX
Maintainer: SSCam, SSChatBot, SSCal, SSFile, DbLog/DbRep, Log2Syslog, SolarForecast,Watches, Dashboard, PylonLowVoltage
Kaffeekasse: https://www.paypal.me/HMaaz
Contrib: https://svn.fhem.de/trac/browser/trunk/fhem/contrib/DS_Starter

freetz

Es gibt da widersprüchliche Angaben, ich habe 50 Abfragen pro Tag, das reicht dicke, weil öfter als alle 30 Minuten eh' nicht aktualisiert wird, d.h. mit 48 käme man aus. Aber selbst die brauche ich nicht, weil die Sonne ja nicht 24 Stunden scheint. Selbst mit 20 Abfragen käme man im Sommer gut durch, wenn man einmal pro Stunde abfragt. Ich finde die Werte deutlich genauer als DWD, das ich vorher im Einsatz hatte.
Alle Infos zur Anbindung von Heizungssystemen mit PPS-, LPB- bzw. BSB-Bus ans LAN gibt es hier:
https://github.com/fredlcore/bsb_lan

Alle Infos zum WLAN-Interface "Robotan" für Ambrogio/Stiga/Wolf und baugleiche Rasenmähroboter:
https://github.com/fredlcore/robotan

DS_Starter

Zitat... weil die Sonne ja nicht 24 Stunden scheint
Ja, stimmt natürlich. Es reicht ja von Sonnenauf- bis untergang. Die Angaben ziehe ich z.Z. aus dem DWD Device.
Benötigt man zur Abschätzung des Ertrages bei Verwendung von SolCast auch noch die Angaben zu Bewölkung bzw. Regen aus DWD oder sind diese Faktoren bei SolarCast schon mit berücksichtigt ?
Ich vermute SolCast liefert auch nur die Globalstrahlung ?

Meine Idee ist die Strahlungswerte von SolCast zu holen und natürlich noch die Bewölkung / Niederschlag als reduzierende Faktoren einzubeziehen wie es jetzt auch mit dem DWD Device funktioniert.
ESXi@NUC+Debian+MariaDB, PV: SMA, Victron MPII+Pylontech+CerboGX
Maintainer: SSCam, SSChatBot, SSCal, SSFile, DbLog/DbRep, Log2Syslog, SolarForecast,Watches, Dashboard, PylonLowVoltage
Kaffeekasse: https://www.paypal.me/HMaaz
Contrib: https://svn.fhem.de/trac/browser/trunk/fhem/contrib/DS_Starter

freetz

SolCast hat alles schon mit drin und die Ergebnisse sind wie gesagt deutlich präziser, weil man nicht auf die nächstgelegene DWD-Station angewiesen ist. Am besten, Du schaust Dir die API direkt auf deren Seiten mal an, dann wird es am deutlichsten.
Alle Infos zur Anbindung von Heizungssystemen mit PPS-, LPB- bzw. BSB-Bus ans LAN gibt es hier:
https://github.com/fredlcore/bsb_lan

Alle Infos zum WLAN-Interface "Robotan" für Ambrogio/Stiga/Wolf und baugleiche Rasenmähroboter:
https://github.com/fredlcore/robotan

Aurel_B

#21
@freetz: mich würde wunder nehmen, wie du die Präzision der SolCast Vorhersagen ermittelst? Schaust du am Ende des Tages wie gross die Abweichung ist? Oder im Tagesverlauf? Wie oft frägst du die Werte ab?

Als Vergleich: ich lasse einmal am Abend um 21:45 die Prognose abrufen um den morgigen Tag planen zu können. Manchmal sind die Prognosen extrem genau, manchmal liegen sie ordentlich daneben. Das scheint - wie weiter oben von mir bereits geschrieben - immer dann der Fall zu sein, wenn Bewölkung auftritt. Im Anhang habe ich einen Screenshot welcher den tatsächlichen Ertrag (grün) mit dem prognostizierten (gelb) vergleicht (für die letzten 8 Tage, mehr habe ich bisher nicht).

freetz

Wenn Du SolCast selber nutzt, weißt Du ja, dass es da direkt im Backend die Möglichkeit die Vorhersage mit den Estimated Actuals zu vergleichen. Darüber hinaus übermittele ich auch meine vom WR ermittelten Erträge an SolCast, so dass da noch mal eine Optimierung stattfindet. Da sehe ich das dann noch mal genauer.

Und klar, SolCast kann nicht 24h hellsehen, deswegen frage ich die Werte auch alle halbe Stunde ab. Die können sich dann schon mal ändern, so wie beim DWD auch, nur ist SolCast meiner Erfahrung nach da deutlich genauer. Ich merke das ganz konkret daran, dass ich es mit SolCast schaffe, meine Batterie aufgrund der Prognose zum spätmöglichsten Zeitpunkt zu laden und sie dann in den allermeisten Fällen trotzdem noch voll wird. Beim DWD hat das so gut wie nie geklappt.
Alle Infos zur Anbindung von Heizungssystemen mit PPS-, LPB- bzw. BSB-Bus ans LAN gibt es hier:
https://github.com/fredlcore/bsb_lan

Alle Infos zum WLAN-Interface "Robotan" für Ambrogio/Stiga/Wolf und baugleiche Rasenmähroboter:
https://github.com/fredlcore/robotan

Aurel_B

#23
Ah danke dir, wusste nicht, dass man die tatsächlichen Erträge senden kann. Habe irgendwo gelesen, dass es das Tuning nicht mehr gibt und mich daher gar nie mehr damit beschäftigt. Auf https://articles.solcast.com.au/en/articles/2442084-send-rooftop-site-measurements-via-our-api steht beschrieben, wie's geht.

Meine Frage an dich: hättest du zufälligerweise eine Skript oder FHEM Code wie du die Daten des WRs an SolCast sendest? Hätte jetzt spontan gesagt, ich sende jede Stunde den Stundenertrag etwa in so:


{
my $Stand_jetzt = ReadingsVal("WR","Total_yield",undef);
my $Letzter_Stand = ReadingsVal("WR","Total_yield_Stand_vorher_Wh",undef);
my $Zeitdauer = ReadingsAge("WR","Total_yield_Stand_vorher_Wh",undef);
fhem("setreading WR Total_yield_Stand_vorher_Wh ".$Stand_jetzt);
my $Ertrag_seit_letztem_Stand = $Stand_jetzt - $Letzter_Stand;
# $Zeitdauer in ISO8601 umrechnen und alles an SolCast senden
}


Man könnte auch "oldreadings" verwenden, so ist es aber transparenter für mich...

PS: Ah und einfach damit es irgenwo festgehalten ist falls mal jemand danach suchen sollte: ich habe auch noch mit "Forecast.Solar" rumgespielt (https://forecast.solar/), das taugt nun gar nix, dort wird Tag für Tag ein Ertrag von ca. 9.5kWh +-400W prognostiziert mit keinem erkennbaren Zusammenhang zum tatsächlichen Ertrag.

Aurel_B

Sorry, es ist früh am Morgen ;D Steht ja ganz oben auf der Seite geschrieben, dass es das Tuning nicht mehr gibt und es daher keinen Sinn macht, die Daten zu senden. Jetzt stehe ich gerade völlig auf dem Schlauch. Was genau sendest du denn an SolCast und welchen Effekt hat das?

freetz

#25
Ja, da steht, dass es das Tuning (schon länger) nicht mehr gibt, aber angeblich für Bestandskunden weiter angeboten wird. Ich bin aber erst seit Juli dabei, also nach der Ankündigung von oben und kann sagen, dass bei mir zuerst da stand, dass noch zu wenige Daten da sind, aber nach einiger Zeit gab's dann den grünen Haken (s. Anhang).
Ist aber letztlich für die Fragestellung egal, denn das Tuning soll ja eher so Faktoren wie Verschattung etc. berücksichtigen. Das fällt i.d.R. aber nicht so extrem ins Gewicht, zumindest nicht so deutlich wie die Unterschiede zwischen DWD und SolCast Vorhersage auch ohne Tuning schon waren.

Mit Deinem Schnipsel kann ich jetzt auf die Schnelle nichts anfangen, hier ist meine entsprechende FHEM Config:

defmod SolCast_Reporting HTTPMOD https://api.solcast.com.au/rooftop_sites/... 0
attr SolCast_Reporting bodyDecode utf-8
attr SolCast_Reporting extractAllJSON 1
attr SolCast_Reporting replacement01Mode expression
attr SolCast_Reporting replacement01Regex {{([^}]+)}}
attr SolCast_Reporting replacement01Value ReadingsVal($name, $1, "")
attr SolCast_Reporting room Solar
attr SolCast_Reporting set01Data {"measurement": {"period_end": "{{period_end}}","period": "{{reporting_interval_ISO8601}}","total_power": $val}}
attr SolCast_Reporting set01Name pvProduction
attr SolCast_Reporting setHeader1 Content-Type: application/json
attr SolCast_Reporting setHeader2 Accept: */*
attr SolCast_Reporting stateFormat [SolCast_Reporting:value]
attr SolCast_Reporting userReadings reporting_interval {ReadingsVal($NAME, "reporting_interval", 300)},\
reporting_interval_ISO8601 {return "PT" . ReadingsVal($NAME, "reporting_interval", 300) / 60 . "M"},\
period_end {return strftime('%Y-%m-%dT%H:%M:%SZ', gmtime(time))}\


defmod di_reset_production DOIF ([+[SolCast_Reporting:reporting_interval]]) \
(setreading SolCast_Reporting period_end {strftime('%Y-%m-%dT%H:%M:%SZ', gmtime(time))},\
set SolCast_Reporting pvProduction {(ReadingsVal("BydBattery","avg_production","N/A")/1000)},\
setreading BydBattery avg_production_counter 0,\
setreading BydBattery avg_production 0,\
setreading BydBattery avg_production_sum 0)


Und hier noch der Ausschnitt aus dem userReading des Batterie-Devices "BydBattery":

avg_production_counter:Meter_Power.* {
  ReadingsVal($NAME,"avg_production_counter",0)+1;
},

avg_production_sum:Meter_Power.* {
  ReadingsVal($NAME,"avg_production_sum",0) + ReadingsVal($NAME, "pvProduction",0);
},

avg_production:Meter_Power.* {
  my $aps = ReadingsVal($NAME,"avg_production_sum",0);
  my $apc = ReadingsVal($NAME, "avg_production_counter", 0);
  if ($apc > 0) {
    return sprintf("%.1f", $aps/$apc);
  } else {
    return 0;
  }
}


Ich hoffe, das hilft weiter...
Alle Infos zur Anbindung von Heizungssystemen mit PPS-, LPB- bzw. BSB-Bus ans LAN gibt es hier:
https://github.com/fredlcore/bsb_lan

Alle Infos zum WLAN-Interface "Robotan" für Ambrogio/Stiga/Wolf und baugleiche Rasenmähroboter:
https://github.com/fredlcore/robotan

Aurel_B

Hier als mal der Monat November dargestellt: grün ist der tatsächliche Ertrag, Pink die Prognose (jeweils am Vorabend um 22h ermittelt). So richtig zuverlässig sind die Prognosen nicht... Ich bin jetzt parallel mit SolarForecast (ist ein FHEM Modul) am schauen und kann dann in 1-2 Monaten einen Vergleich anstellen.

sig10680

Zitat von: ansgru am 17 Oktober 2021, 10:55:47
Danke dir freetz für deine Arbeit! Ich habe diese etwas weiterentwickelt und zusätzlich noch ein UserReading für morgen erstellt sowie eine Funktion, welche bei jedem Aufruf die aktuellen Prognosewerte in einen dblog schreibt. Historische Werte werden dabei belassen, es werden nur die abgerufenen Werte überschrieben. Hintergrund: so kann ich mir einen Graphen plotten der mir eine Vorhersage liefert, aber auch einen Rückblick damit ich schauen kann, wie gut die Prognose stimmt. Das UserReading für morgen brauche ich um z.B. am Abend vorher gewisse Grossverbraucher mit dem günstigen Nachttarif laufen lassen zu können falls der prognostizierte Solarstrom nicht reichen sollte.

Hier das UserReading:


forecast_today {
my $total = 0;
my $period = ReadingsVal("SolCast", "period", 0);
$period =~ s/.*(\d\d).*/$1/;
my ($date, $time) = split(" ", FmtDateTime(time()));
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 $total;
},
forecast_tomorrow {
use Time::Piece ();
use Time::Seconds;

my $total = 0;
my $period = ReadingsVal("SolCast", "period", 0);
$period =~ s/.*(\d\d).*/$1/;
my ($date, $time) = split(" ", FmtDateTime(time()));
my $date = Time::Piece->strptime($date, '%Y-%m-%d');
$date += ONE_DAY;
$date = $date->strftime('%Y-%m-%d');
my $hash = $defs{"SolCast"};
my $readings = $hash->{READINGS};
foreach my $reading (keys %{$readings}) {
if ($reading =~ /$date/) {
my $val = ReadingsVal("SolCast", $reading, 0);
$total += $val/(60/$period);
}
}
return $total;
}


Und hier die Funktion um die Vorhersage im dblog zu speichern (gehört in MyUtils):


# Braucht 3 Argumente: $name = Name des "Solcast" Devices, $dblog und $dbrep Namen der dblog/dbrep Devices. Bei jedem Aufruf wird der Forecast für morgen sowie alle aktuellen Prognosewerte gespeichert. Die Prognosewerte werden dabei überschrieben, alte Werte bleiben bestehen
sub forecast_in_DB_speichern($$$) {
my ( $name, $dblog, $dbrep ) = @_;
my $val = ReadingsVal($name, "forecast_tomorrow", 0);
my $time = strftime('%Y-%m-%d %H:%M:%S', localtime(time + 86400));
fhem("set ".$dblog." addCacheLine $time|$name|JSONMOD|forecast|forecast|$val");
my %hash = %{$defs{$name}{READINGS}};
my @pv_estimates = sort grep /pv_estimate/, keys %hash;
# Max/Min Forecast Zeitpunkt ermitteln
$pv_estimates[-1] =~ m/pv_estimate_(.*)/;
my $max = str2time($1);
$pv_estimates[0] =~ m/pv_estimate_(.*)/;
my $min = str2time($1);
# Alle alten Werte löschen die aktualisiert werden sollen
fhem("set ".$dbrep." sqlCmd DELETE FROM `history` WHERE DEVICE = '".$name."' AND `TIMESTAMP` >= FROM_UNIXTIME(".$min.") AND `TIMESTAMP` <= FROM_UNIXTIME(".$max.")");

foreach my $reading (@pv_estimates) {
$reading =~ m/pv_estimate_(.*)/;
$time = strftime('%Y-%m-%d %H:%M:%S', localtime(str2time($1)));
$val = ReadingsVal($name, $reading, 0) * 1000;
fhem("set ".$dblog." addCacheLine $time|$name|JSONMOD|pv_estimate|pv_estimate|$val");
}
}


Die eigentliche Speicherung erfolgt dann in einem at:

defmod at_SolCast_Prognose_speichern at *22:00 {forecast_in_DB_speichern('SolCast','dblog','DB_Bereinigung_Kostal')}

Prognosen siehe Anhang. Es besteht noch Optimierungsbedarf, im Moment habe ich alle meine Module (O, S & W) zusammengefasst zu einer Anlage (Südausrichtung). Wenn ich Zeit und Lust habe, splitte ich das auf 3 Anlagen mit der korrekten Ausrichtung und Neigung und aktualisiere dann meinen Code. Was mir auch schon aufgefallen ist: Nebel/"grauer Himmel" wird nicht richtig prognostiziert, so ist z.B. bei uns im Moment Hochnebel und der PV Ertrag entsprechend gering, das spiegelt sich aber nicht in der SolCast Prognose wieder (siehe auch 10. Oktober, das war auch so ein Tag wo bis am Mittag Hochnebel herrschte). Die DWD Werte kann ich leider nicht benutzen da keine Station bei mir in der Nähe ist.

Hallo,
ich habe bei mir auch Solcast mit JsonMod eingebunden und es läuft!
Würde auch ein Plot mir Vorhersage ohne DBlog funktionieren! Wenn ja wie müsste man dies anstellen!

mfg Sig10680

Aurel_B

Zitat von: sig10680 am 26 Januar 2022, 12:14:23
Hallo,
ich habe bei mir auch Solcast mit JsonMod eingebunden und es läuft!
Würde auch ein Plot mir Vorhersage ohne DBlog funktionieren! Wenn ja wie müsste man dies anstellen!

mfg Sig10680

Mit meinem Ergänzungscode? Nope, da sind SQL Befehle und so drin die nur mit DBlog gehen. Grundsätzlich sollte es aber auch mit einem FileLog gehen falls sich jemand die Mühe macht, den Code entsprechend umzuschreiben.

sig10680

Zitat von: ansgru am 27 Januar 2022, 20:27:27
Mit meinem Ergänzungscode? Nope, da sind SQL Befehle und so drin die nur mit DBlog gehen. Grundsätzlich sollte es aber auch mit einem FileLog gehen falls sich jemand die Mühe macht, den Code entsprechend umzuschreiben.
Danke für die Info

Aurel_B

Hallo zusammen,

ich habe unterdessen auch noch die Prognose von "SolarForecast" (nicht zu verwechseln mit "SolCast", Thread siehe: https://forum.fhem.de/index.php/topic,117864.1335.html) integriert und jetzt 2 Monate Erfahrung damit gesammelt. Fazit? SolarForecast ist eher pessimistisch, SolCast eher optimistisch in der Prognose: werde wohl den Mittelwert verwenden. Generell scheinen die Prognosen recht vernünftig zu sein wobei es keine grossen Unterschiede zwischen einer Prognose am Abend vorher (um 22h) und am Morgen früh (um 7h) zu geben. Details siehe Graphiken im Anhang.

ch.eick

#31
Zitat von: ansgru am 02 März 2022, 07:54:45
Hallo zusammen,

ich habe unterdessen auch noch die Prognose von "SolarForecast" (nicht zu verwechseln mit "SolCast", Thread siehe: https://forum.fhem.de/index.php/topic,117864.1335.html) integriert und jetzt 2 Monate Erfahrung damit gesammelt. Fazit? SolarForecast ist eher pessimistisch, SolCast eher optimistisch in der Prognose: werde wohl den Mittelwert verwenden. Generell scheinen die Prognosen recht vernünftig zu sein wobei es keine grossen Unterschiede zwischen einer Prognose am Abend vorher (um 22h) und am Morgen früh (um 7h) zu geben. Details siehe Graphiken im Anhang.
Hallo,
was Du aus dem SolarForecast für die Leistungsprognose entnommen hast kommt ursprünglich von mir aus der Solar_forecast() Funktion :-)

Dies basiert auf den DWD rad1h Daten und wurde extra pessimistisch ausgelegt.
In meiner Funktion kann man dann mit einem fixen Faktor - bei mir momentan 1.2 - alle Werte etwas anheben.
Das Original Solar_forecast() verwendet für die Autokorrektur noch das DbLog/DbRep und errechnet stündliche korrekturfaktoren,
die dann ebenfalls mit einem Faktor nochmal angehoben oder abgesenkt werden können.

Die Werte um 07:00 und um 22:00 Uhr sind aufgrund der Winkel Korrektur für den Einstrahlungswinkel auf die Solar Module sehr schwierig.
Bei mir habe ich es zwischen 6:00 und 21:00 Uhr als Solar Zeit begrenzt. Je nach Sonnenstand aus dem Astro Modul werden einige Winkel
bewust verworfen, wodurch dann als Ergebnis 0 zurück kommt. Momentan ergeben sich beim heutigen Datum dann plausieble Werte von
8:00 bis 18:00 Uhr, wobei das die 0 Werte im Graphen sind.

Je nach Aufruf der Solar_forecast() Funktion wird die Leistungsprognose für heute oder morgen erstellt. Die Werte für morgen stehen dann
auch schon bereits in der DbLog und lassen sich in Grafana mit Angabe des morgigen Datums schon anschauen. Am aktuellen Tag können dann
beide Kurven verglichen werden und man erkennt die Korrektur des DWD.

Wolken, Regen und die Modultemperatur werden ebenfalls berücksichtigt.

VG Christian

EDIT: ich habe nochmal ein Bild der Prognose mit der realität angehängt.
Das ist ohne Autokorrektur und mit einem festen Faktor von 1.2 , der für meine Anlage festgelegt wurde.
Man sieht auch schön die verworfenen Werte vor 8:00 und nach 18:00 Uhr mit ihrem Wert von 0.
Die bunten Linien sind die einzelnen Strings mit Ost/Süd/West Ausrichtung, wobei im Westen 2 Strings (oben/unten) sind.
Morgens und Abends schalte ich auch noch das Schatten Management ein, wodurch der Ertrag rein optisch gesehen verbessert wurde,
Messwerte zum Unterschied habe ich dazu nicht.
RPI4; Docker; CUNX; Eltako FSB61NP; SamsungTV H-Serie; Sonos; Vallox; Luxtronik; 3x FB7490; Stromzähler mit DvLIR; wunderground; Plenticore 10 mit BYD; EM410; SMAEM; Modbus TCP
Contrib: https://svn.fhem.de/trac/browser/trunk/fhem/contrib/ch.eick

Aurel_B

Hallo Christian,

vielen Dank für deine Nachricht, das sind sehr nützliche Infos! Im Moment passt die etwas pessimistischere Auslegung tiptop in's Konzept weil der Mittelwert von SolarForecast und SolCast eine ziemlich gute Prognose ergibt. Die Nulldurchgänge sind bei mir im Moment bei 6 & 18h -> vielleicht, weil wir etwas weiter südlich wohnen? Ich werde geg. die Prognosezeitpunkte anpassen.

Sehr cool an dieser Prognose ist z.B., das ich den Warmwasserspeicher bei Bedarf zusätzlich via Heizstab auf bis zu 70° aufheizen kann falls "magere" Tage prognostiziert werden. So habe ich quasi eine "thermische" Batterie :-)

Beste Grüsse, Anton

sn0000py

Hallo mal ne kurze Frage hier rein, seit der Sommerzeit, sind die Grafen von Solcast um eine Stunde verschoben, hat schon wer die Stelle korrigiert?

der-Lolo

Zitat von: ansgru am 15 Dezember 2021, 16:03:36
Hier als mal der Monat November dargestellt: grün ist der tatsächliche Ertrag, Pink die Prognose (jeweils am Vorabend um 22h ermittelt). So richtig zuverlässig sind die Prognosen nicht... Ich bin jetzt parallel mit SolarForecast (ist ein FHEM Modul) am schauen und kann dann in 1-2 Monaten einen Vergleich anstellen.

Und? Kannst Du nun einen vergleich zeigen..?
Wäre klasse.

Aurel_B

Oups, sorry, war einige Wochen nicht mehr hier. Wie gewünscht im Anhang die aktuellen Graphen (morgens: Prognose um 6h am selben Tag, abends: um 22h am Vortag).... generell ist die Vorhersage recht ordentlich, wie gehabt ist SolarForecast i.d.R. etwas pessimistischer als SolCast. Die Werte am Vorabend unterscheiden sich auch nicht wahnsinnig von den Prognosen morgens, d.h. ich kann Grossverbraucher abends bei günstigem Nachtstrom einschalten wenn die Prognose für den nächsten Tag mau ist.
Ich verwende im Moment nur die SolarForecast Werte um z.B. beim Boiler mit dem Heizstab (zusätzlich zur Wärmepumpe) das Warmwasser bis auf 72° zu erwärmen wenn die Prognose für den nächsten Tag mau ist -> so habe ich einen kleinen thermischen Speicher.
Bald kommt noch die Wärmepumpe, da werde ich wohl auch so Spielereien umsetzen um möglichst allen Strom lokal zu verwenden (auch wiederum abhängig von der Prognose)


J0EK3R

#36
Moin in die Runde :)

Aus den vorausgegangenen Posts dieses Threads und anderen Quellen hab ich mir ein funktionierendes SolCast Vorhersagesystem zusammengebastelt, das ich mit Euch teilen möchte.

Es gibt ein DbLog mit dem Namen LogDB und ein DbRep mit dem Namen LogDBRep_PV_Forecast_SQL für das Datenbank-Handling und eine Funktion forecast_in_DB_speichern in 99_myUtils.pm.
Außerdem für jede Dachfläche ein JsonMod - hier im Beispiel mit dem Namen SolCast_Sueddach.
Und ein DOIF mit dem Namen SolCast_Schedule, das für das Pollen und den Aufruf der Funktion forecast_in_DB_speichern zuständig ist.

Zunächst hier das DOIF, das zwischen 5 Uhr und 21 Uhr zu jeder vollen Stunde die Vorhersage von SolCast abholt und eine Minute später den Aufruf der Funktion forecast_in_DB_speichern in 99_myUtils.pm macht:

defmod SolCast_Schedule DOIF ################################################################################################################\
## 1 Daten von Solcast holen\
([05:00-21:00] and [:00])\
(set SolCast_Sueddach reread)\
\
################################################################################################################\
## 2 Daten in Datenbank speichern\
## \
DOELSEIF\
([05:00-21:00] and [:01])\
   ({forecast_in_DB_speichern('SolCast_Sueddach','LogDB','LogDBRep_PV_Forecast_SQL')})\
attr SolCast_Schedule DbLogExclude .*
attr SolCast_Schedule cmdState Get Data|Write Data
attr SolCast_Schedule do always
attr SolCast_Schedule verbose 0
attr SolCast_Schedule webCmd cmd_1:cmd_2
attr SolCast_Schedule webCmdLabel Get_Data :Write_Data :


Hier meine Definition des JsonMod SolCast_Sueddach.

Natürlich muss der Rooftop-Identifier und der API-Key angepasst werden!

Ich habe den Code der userreadings überarbeitet: SolCast liefert die Halb-Stunden-Werte nicht für den ganzen Tag, sondern nur ab eineinhalb Stunden vor dem Abfragezeitpunkt. Das bedeutet, dass die Summe der Stundenerträge im Laufe eines Tages immer kleiner wird, da die vorangegangenen Stundenerträge fehlen.
Deshalb speichere ich die gelieferten Stundenerträge intern in einem Hash mit den Uhrzeiten als Schlüssel (-> $hash->{Helper}{today}{$ttime}) und summiere sie dann zum Tagesertrag.

defmod SolCast_Sueddach JsonMod https://api.solcast.com.au/rooftop_sites/XXXX-YYYY-ZZZZ-AAAA/forecasts?format=json&api_key=ABC&hours=72
attr SolCast_Sueddach DbLogExclude .*
attr SolCast_Sueddach DbLogInclude forecast_today
attr SolCast_Sueddach event-on-change-reading .*
attr SolCast_Sueddach readingList multi(jsonPath('$.forecasts[*]'), concat('pv_estimate_', property('period_end')), property('pv_estimate'));;\
single(jsonPath('$.forecasts[0].period'),'period','');;
attr SolCast_Sueddach stateFormat forecast_today
attr SolCast_Sueddach userReadings forecast_today {\
use List::Util 'sum';;\
my ($date, $time) = split(" ", FmtDateTime(time()));;\
my $hash = $defs{$NAME};;\
my $readings = $hash->{READINGS};;\
foreach my $reading ( keys %{$readings} ) {\
if ($reading =~ /$date/) {\
my $val = ReadingsVal($NAME,$reading,0);;\
my $ttime = substr($reading, 23, 4);;\
$hash->{Helper}{today}{$ttime} = $val;;\
}\
}\
my $period = ReadingsVal($NAME, "period", 1);;\
$period =~ s/.*(\d\d).*/$1/;;\
my $values = $hash->{Helper}{today};;\
my $total = sum values %{$values};;\
$total /= (60/$period);;\
return $total;;\
},\
forecast_tomorrow \
{\
use List::Util 'sum';;\
use Time::Piece ();;\
use Time::Seconds;;\
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);;\
my $ttime = substr($reading, 23, 4);;\
$hash->{Helper}{tomorrow}{$ttime} = $val;;\
}\
}\
my $period = ReadingsVal($NAME, "period", 1);;\
$period =~ s/.*(\d\d).*/$1/;;\
my $values = $hash->{Helper}{tomorrow};;\
my $total = sum values %{$values};;\
$total /= (60/$period);;\
return $total;;\
}


In 99_myUtils.pm habe ich folgenden Code definiert:

use Time::Piece;

# Braucht 3 Argumente: $name = Name des "Solcast" Devices, $dblog und $dbrep Namen der dblog/dbrep Devices.
# Bei jedem Aufruf wird der Forecast für morgen sowie alle aktuellen Prognosewerte gespeichert.
# Die Prognosewerte werden dabei überschrieben, alte Werte bleiben bestehen.
sub forecast_in_DB_speichern($$$) {
my ( $name, $dblog, $dbrep ) = @_;

# Dauer des Vorhersageintervalls in Minuten
my $period_m = ReadingsVal($name, "period", "PT30M");
$period_m =~ s/.*(\d\d).*/$1/;

my $val = ReadingsVal($name, "forecast_tomorrow", 0);
my $time = strftime('%Y-%m-%d %H:%M:%S', localtime(time + 60 * 60 * 24));

CommandSet(undef, $dblog." addCacheLine $time|$name|JSONMOD|forecast|forecast|$val");

# Readings nach Namen sortieren
my %hash = %{$defs{$name}{READINGS}};
my @pv_estimates = sort grep /pv_estimate/, keys %hash;

# Max/Min Forecast Zeitpunkt ermitteln
$pv_estimates[-1] =~ m/pv_estimate_(.*)/;
my $max = strftime('%Y-%m-%d %H:%M:%S', localtime(str2time($1) -$period_m*60));

$pv_estimates[0] =~ m/pv_estimate_(.*)/;
my $min = strftime('%Y-%m-%d %H:%M:%S', localtime(str2time($1) -$period_m*60));

# Alle alten Werte löschen, die aktualisiert werden sollen
CommandGet(undef, $dbrep." sqlCmdBlocking DELETE FROM history WHERE DEVICE='".$name."' AND TIMESTAMP>='".$min."' AND TIMESTAMP<='".$max."'");

foreach my $reading (@pv_estimates) {
$reading =~ m/pv_estimate_(.*)/;
$time = strftime('%Y-%m-%d %H:%M:%S', localtime(str2time($1) -$period_m*60));

$val = ReadingsVal($name, $reading, 0) * 1000;

CommandSet(undef, $dblog." addCacheLine $time|$name|JSONMOD|pv_estimate|pv_estimate|$val");
}
};


Beste Grüße
J0EK3R

---
Änderungen:
2022.09.12: Vorhersageintervall "$period_m" wird berücksichtigt

DS_Starter

Guten Abend,

aktuell bin ich dabei die SolCast API in das SolarForecast Modul zu integrieren.

Ich habe mir die Beiträge in diesem Thread angesehen und dabei ist mir aufgefallen dass ihr wahrscheinlich übersehen habt die von der API gelieferte UTC-Zeit (YYYY-MM-DDThh:mm:ss.0000000Z) in die lokale Zeit zu wandeln.
Aktuell sind das 2 Stunden Differenz.
Möglicherweise habe ich die Umwandlung in euren Codes auch überlesen, aber wenn nicht, kann es einfach mit dem Modul ConvertTimeZone erledigt werden.

Grüße,
Heiko
ESXi@NUC+Debian+MariaDB, PV: SMA, Victron MPII+Pylontech+CerboGX
Maintainer: SSCam, SSChatBot, SSCal, SSFile, DbLog/DbRep, Log2Syslog, SolarForecast,Watches, Dashboard, PylonLowVoltage
Kaffeekasse: https://www.paypal.me/HMaaz
Contrib: https://svn.fhem.de/trac/browser/trunk/fhem/contrib/DS_Starter

J0EK3R

Moin Heiko :)

Zitataktuell bin ich dabei die SolCast API in das SolarForecast Modul zu integrieren.

...das wäre toll! :)

So wie ich das ausprobiert hab, wird die UTC-Zeit von str2time schon richtig umgewandelt:

Eingabe direkt in die Commandozeile von FHEM:

{strftime('%Y-%m-%d %H:%M:%S', localtime(str2time('2022-09-12T053000.0000000Z')))}
2022-09-12 07:30:00

Ohne "Z":

{strftime('%Y-%m-%d %H:%M:%S', localtime(str2time('2022-09-12T053000.0000000')))}
2022-09-12 05:30:00


Die Rohdaten von SolCast im JSON-Format sehen ja so aus:

{"pv_estimate":0.0463,"pv_estimate10":0.043984999999999996,"pv_estimate90":0.048615000000000005,"period_end":"2022-09-12T05:30:00.0000000Z","period":"PT30M"},
{"pv_estimate":0.1055,"pv_estimate10":0.100225,"pv_estimate90":0.110775,"period_end":"2022-09-12T06:00:00.0000000Z","period":"PT30M"},
{"pv_estimate":0.385,"pv_estimate10":0.36574999999999996,"pv_estimate90":0.40425000000000005,"period_end":"2022-09-12T06:30:00.0000000Z","period":"PT30M"},


Die Zeitstempel, die JsonMod verwendet, sind ja immer die des Endes der Vorhersage-Periode: period_end.

Da ich für die Darstellung in meinen Diagrammen den Startzeitpunkt habe, ziehe ich eine Stunde ab:

$time = strftime('%Y-%m-%d %H:%M:%S', localtime(str2time($1) - 60 * 60));

...aber jetzt, wo ich drüber nachdenke, müsste es nicht die Länge der Vorhersage-Periode sein, also 30 Minuten!? Hmmm...


Beste Grüße
J0EK3R

DS_Starter

Moin,

ah ja, str2time  .... ich nehme alles zurück und behaupte das Gegenteil.  :)

Zitat
...aber jetzt, wo ich drüber nachdenke, müsste es nicht die Länge der Vorhersage-Periode sein, also 30 Minuten!? Hmmm..

Im Modul ist alles normiert auf Stundenbasis und die Angaben beziehen sich ebenfalls auf den Anfang der Stunde.
Deswegen gehe ich so vor zunächst die gelieferten Zeiten auf lokale Zeit umzuwandeln und dann abhängig von der Zeit entweder 30 Min oder 60 Min abzuziehen.

Zum Beipiel wird geliefert  period_end (umgewandelt)  2022-09-12 08:30:00  und  2022-09-12 09:00:00.
Vom ersten Date normiere ich auf 08 Uhr indem ich 30 auf 00 setze und von 09 ziehe ich 1 ab -> 08.

Die gelieferten Werte pv_estimate beider Zeiten werden dann mit ihrer Periode gewichtet zusammenaddiert (macht ihr auch so), im Prinzip:

total = pv_estimate1/(60/periode) + pv_estimate2/(60/periode)

Damit haben wir den Vorhersagewert dann umgewandelt auf den Anfang der jeweiligen Stunde lokaler Zeit.

Im Modul bin ich mit dem Einbau schon gut vorangekommen, aber es dauert noch einige Zeit. Doch das kommt.

Frage in die Runde.
Ich beziehe mich auf die freie Nutzung der API, d.h. man hat 50 Abfragen pro Tag frei. Für ein roofTop reicht das gut hin wenn z.B. 1 mal pro Stunde zwischen Sonnenauf- und untergang abgefragt wird.
Hat man aber mehr als ein roofTop, was jeweils mit einer eigenen ID separat abgefragt wird, dann wird es ggf. schon etwas enger.
Ich gehe davon aus, dass jede Abfrage in das Kontingent zählt und nicht pro roofTop ID oder sehe ich das falsch ?

LG,
Heiko
ESXi@NUC+Debian+MariaDB, PV: SMA, Victron MPII+Pylontech+CerboGX
Maintainer: SSCam, SSChatBot, SSCal, SSFile, DbLog/DbRep, Log2Syslog, SolarForecast,Watches, Dashboard, PylonLowVoltage
Kaffeekasse: https://www.paypal.me/HMaaz
Contrib: https://svn.fhem.de/trac/browser/trunk/fhem/contrib/DS_Starter

J0EK3R

Moin Heiko :)

ZitatFrage in die Runde.
Ich beziehe mich auf die freie Nutzung der API, d.h. man hat 50 Abfragen pro Tag frei. Für ein roofTop reicht das gut hin wenn z.B. 1 mal pro Stunde zwischen Sonnenauf- und untergang abgefragt wird.
Hat man aber mehr als ein roofTop, was jeweils mit einer eigenen ID separat abgefragt wird, dann wird es ggf. schon etwas enger.
Ich gehe davon aus, dass jede Abfrage in das Kontingent zählt und nicht pro roofTop ID oder sehe ich das falsch ?

Ich denke, dass jede einzelne Abfrage zählt - also pro Account, nicht pro Rooftop.

Bei der kostenfreien Nutzung von SolCast kann man nur zwei Rooftops anlegen - braucht man mehr, muss man weitere Accounts anlegen.

Unter "Manage Account" gibt es übrigens einen Zähler für die "API Usage":
https://toolkit.solcast.com.au/account

Beste Grüße
J0EK3R

DS_Starter

Zitat
Bei der kostenfreien Nutzung von SolCast kann man nur zwei Rooftops anlegen - braucht man mehr, muss man weitere Accounts anlegen.

Guter Hinweis, danke. Das ist für die Modulimplementierung wichtig. Stichwort Account und Rooftop-ID Management.
ESXi@NUC+Debian+MariaDB, PV: SMA, Victron MPII+Pylontech+CerboGX
Maintainer: SSCam, SSChatBot, SSCal, SSFile, DbLog/DbRep, Log2Syslog, SolarForecast,Watches, Dashboard, PylonLowVoltage
Kaffeekasse: https://www.paypal.me/HMaaz
Contrib: https://svn.fhem.de/trac/browser/trunk/fhem/contrib/DS_Starter

paul79

#42
Hallo,
ich habe alles wie oben beschrieben Post von J0EK3R eingerichtet leider bekomme ich diesen Fehler.

2022.09.25 20:20:24 3: DbLog logdb -> added by addCacheLine - TS: 2022-10-02 18:00:00, Device: SolCast_Sueddach, Type: JSONMOD, Event: pv_estimate, Reading: pv_estimate, Value: 18.6, Unit:
2022.09.25 20:20:24 3: DbLog logdb -> added by addCacheLine - TS: 2022-10-02 18:30:00, Device: SolCast_Sueddach, Type: JSONMOD, Event: pv_estimate, Reading: pv_estimate, Value: 1.3, Unit:
2022.09.25 20:20:24 3: DbLog logdb -> added by addCacheLine - TS: 2022-10-02 19:00:00, Device: SolCast_Sueddach, Type: JSONMOD, Event: pv_estimate, Reading: pv_estimate, Value: 0, Unit:
2022.09.25 20:20:45 1: PERL WARNING: DBD::mysql::st execute failed: You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near 'WHERE 1=1 AND DEVICE = 'SolCast_Sueddach' AND TIMESTAMP >= STR_TO_DATE('2022-09-' at line 6 at ./FHEM/93_DbLog.pm line 3745.


Was kann da der Fehler sein? Es werden keine Daten in die DB geschrieben. (bin kein SQL-Experte)

Hat sich erledigt, lag am DbRep.

Gruß Paul
FHEM5.7 auf Pi3
Busware CUL433 (IT), JeeLink Lacrosse, HM-MOD-RPI-PCB, HM, TabletUI

J0EK3R

#43
Moinmoin paul79 :)

...ich kucke und grüble und kucke und grüble und kann nichts Auffälliges finden und kucke und seh erst jetzt:

Zitat
Hat sich erledigt, lag am DbRep.

Na dann: viel Spaß! :)

limes1337

Der Beitrag hier hat mir sehr geholfen Solcast einzubinden und so möchte ich meine optimierte Version nun auch zurück mit Euch teilen. Ich habe im Wesentlichen den Code von J0EK3R aufgegriffen und an folgenden Stellen verfeinert:

  • Die Url hat noch den Parameter "hours" bekommen, so dass ich mit hours=72 nur die nächsten 3 Tage abhole. Bei allen Tagen darüber hinaus dürften die Werte sowieso ungenau werden und so spart man einige Readings.
  • Ich habe bei den UserReadings etwas aufgeräumt, so dass das Device nicht "Solcast" heißen muss, sondern man einen beliebigen Namen wählen kann.
  • Ich habe das UserReading "forecast_overmorrow" ergänzt - also Vorhersage für Übermorgen.
  • Ich habe Die Kalkulation für den forecast_today so angepasst, dass nur noch Perioden berücksichtigt werden, die in der Zukunft liegen. (Solcast liefert immer einige Halbstunden Einträge die etwas in der Vergangenheit liegen). Mit der Veränderung kann ich nun genau sehen, wie viel heute voraussichtlich noch produziert ist und wenn ich es mit meinem tatsächlichen Tagesertrag meiner PV Anlage addiere, dann erhalte ich den kompletten voraussichtlichen Tagesertrag.

Falls ihr im Code noch Optimierungspotential findet, dann gerne her damit - Perl ist nicht meine Muttersprache, aber es funktioniert.  :)

Insgesamt bin ich momentan noch am schauen, wie die Forecasts zu den Actuals passen. Das werde ich weiter beobachten. Ich weiß, dass es auch noch das Modul "SolarForecast" gibt, aber ich möchte es recht einfach halten und bin der Meinung, dass bei solchen Vorhersagen viel Aufwand nicht unbedingt zu viel Mehrwert führt. Sollten meine Vorhersagen nicht zu den Tageswerten meiner PV Anlage passen, dann werde ich erst einmal mit dem Efficiency Factor, den man für jede Site bei Solcast setzen kann spielen. Das dürfte für meine Zwecke reichen, da ich sowieso nur mit den Tageswerten arbeite und keine Aktionen aus den Stundenvorhersagen ableite. So steuere ich bspw. die "Min+PV" oder "Nur PV" Modi meiner OpenWB Wallbox, je nachdem was die Vorhersage für die nächsten 72 Stunden ist.

Grüße,
limes

defmod tech.online.solcast_so2 JsonMod https://api.solcast.com.au/rooftop_sites/<SITE ID>/forecasts?format=json&api_key=<API KEY>&hours=72

attr tech.online.solcast_so2 readingList multi(jsonPath('$.forecasts[*]'), concat('pv_estimate_', property('period_end')), property('pv_estimate'));;\
single(jsonPath('$.forecasts[0].period'),'period','');;
attr tech.online.solcast_so2 room Technik
attr tech.online.solcast_so2 userReadings 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 $total;;\
},\
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 $total;;\
},\
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 $total;;\
}



J0EK3R

#45
Moinmoin Limes  :)

Vielen Dank für Deinen Beitrag!  :D
Den Tipp mit "&hours=72" habe ich gleich eingebaut!

Überseh' ich was, oder lässt Du in Deinem Code die Pufferung der bereits vergangenen Tageswerte weg?

In meinem Code in der foreach-Schleife in forecast_today:

        $hash->{Helper}{today}{$ttime} = $val;

und hier die Bildung der Tagessumme

my $values = $hash->{Helper}{today};;\
        my $total = sum values %{$values};;\
$total /= (60/$period);;\
return $total;;\


Denn - wenn Du beispielsweise stündlich die Werte von Solcast aktualisierst - wird die Tagessumme immer kleiner, weil Solcast nicht die Werte des kompletten Tages liefert, sondern ab dem aktuellen Zeitpunkt...

Beste Grüße
J0EK3R

limes1337

Hi J0EK3R,

sehr schön, dass ich auch ein bisschen mithelfen kann :-).

Du hast das Skript vollkommen korrekt verstanden. forecast_today speichert erst einmal nur die verbleibende Prognose bis zum Tagesende. Ich habe 3 Dachausrichtungen und die Werte für die 3 Sites werden stündlich von Solcast abgeholt. Dabei werden dann, wie Du weist die UserReadings ermittelt. Was ich im letzten Beitrag allerdings nicht erwähnt habe ist, dass ich ein weiteres Skript (PvForecast) habe, dass dann noch Berechnungen durchführt und die Zusammenfassung von allen 3 Dachseiten einen Dummy schreibt. Das ist dann meine Grundlage für alles weitere. Das Skript findest Du unten und es macht stündlich folgendes:

  • Grundsätzlich rechnet es die Vorhersagen für alle 3 Dachausrichtungen zusammen
  • Es ermittelt die Gesamtvorhersage für die PV Anlage für heute verbleibend ($today_fc_remaining). Und es errechnet den voraussichtlichen Gesamtertrag für heute in $today_fc_total, wobei outdoor.pv meine PV Anlage ist.
  • Es ermittelt die Gesamtvorhersage für die PV Anlage für morgen und übermorgen.
  • Es ermittelt einmal morgens zwischen 4 und 5 Uhr, die Gesamtvorhersage für die anstehenden 3 Tage und speichert die Vorhersage für heute separat in forecast_today_total_morning. Letzteres erlaubt mir eben einen Vergleich aus ursprünglicher Tagesvorhersage und tatsächlichem Ertrag. Die 3 Tages Vorhersage verwende ich für weitere Aktionen wie bspw. die Wallbox Steuerung (s. letzter Beitrag).

Ich hoffe das ist verständlich und macht Sinn  :) Ich habe das mal vor 3-4 Tagen aufgesetzt und es gibt sicherlich noch Bedarf für etwas Feinschliff. Mit dem Grundkonzept bin ich aber ganz zufrieden.

Schönen Feiertag,
limes


sub PVForecast ()
{
$today_fc_remaining = round(ReadingsVal("tech.online.solcast_so","forecast_today","0") + ReadingsVal("tech.online.solcast_so2","forecast_today","0") + ReadingsVal("tech.online.solcast_nw","forecast_today","0"),1);
$today_fc_total = round($today_fc_remaining + ReadingsVal("outdoor.pv","Daily_yield_kwh","0"),1);
$tomorrow_fc = round(ReadingsVal("tech.online.solcast_so","forecast_tomorrow","0") + ReadingsVal("tech.online.solcast_so2","forecast_tomorrow","0") + ReadingsVal("tech.online.solcast_nw","forecast_tomorrow","0"),1);
$overmorrow_fc = round(ReadingsVal("tech.online.solcast_so","forecast_overmorrow","0") + ReadingsVal("tech.online.solcast_so2","forecast_overmorrow","0") + ReadingsVal("tech.online.solcast_nw","forecast_overmorrow","0"),1);

fhem("setreading cellar.control.energy forecast_today_remaining $today_fc_remaining");
fhem("setreading cellar.control.energy forecast_today_total $today_fc_total");
fhem("setreading cellar.control.energy forecast_tomorrow $tomorrow_fc");
fhem("setreading cellar.control.energy forecast_overmorrow $overmorrow_fc");

if (strftime ("%H",localtime (time)) == 4)
{
fhem("setreading cellar.control.energy forecast_today_total_morning $today_fc_total");
$allDays_fc = $today_fc_total + $tomorrow_fc + $overmorrow_fc;
fhem("setreading cellar.control.energy forecast_3days $allDays_fc");
}

}

mähschaf

Guten Abend,

kurze Frage: Ich nutze seit ca. einem Monat SolCast. Der Forecast betrug die ganze Zeit eine Woche. Ich meine, 20 tägliche  Abfragen waren in meinem freien Account drin.

Dieser Fred hat mir echt geholfen, die Vorhersagen "einzusammeln". Danke dafür!

Heute habe ich festgestellt, dass die json API "nur" noch 2 Tage Forecast (+heute) liefert. Die Anzahl der täglichen Requests beträgt allerdings 50.

Ist da irgendetwas passiert, was ich nicht mitbekommen habe? Wird der Plan nach einem Monat evt. angepasst?

Danke für Eure Zeit,
Martin

J0EK3R

Moin Martin  :)

Hast Du am Ende der Url "&hours=72" stehen - das würde entsprechend einschränken?  ???


defmod SolCast_Sueddach JsonMod https://api.solcast.com.au/rooftop_sites/XXXX-YYYY-ZZZZ-AAAA/forecasts?format=json&api_key=ABC&hours=72


Beste Grüße
J0EK3R

mähschaf

Hallo J0EK3R,

danke für die Antwort!

Meine URL sieht so aus:
https://api.solcast.com.au/rooftop_sites/<id>/forecasts?format=json&api_key=<key>

Also ohne hour...

J0EK3R

...verrückte Idee:

Wie wäre es mit "&hours=174" oder eben entsprechend Deiner Wünsche!?  8)

mähschaf

Unglaublich. Klappt.

Mann, da hätte ich auch selber drauf kommen können. Aber da es vorher problemlos lief.....  ::)

DANKE!!! Und ein schönes Wochenende!

Mickey

Hi Limes, ich habe deine SUB übernommen und bekomme vor lauter Ahnungslosigkeit folgendes:

ZitatERROR:

Global symbol "$today_fc_remaining" requires explicit package name (did you forget to declare "my $today_fc_remaining"?) at ./FHEM/99_myUtils.pm line 21. Global symbol "$today_fc_total" requires explicit package name (did you forget to declare "my $today_fc_total"?) at ./FHEM/99_myUtils.pm line 22. Global symbol "$today_fc_remaining" requires explicit package name (did you forget to declare "my $today_fc_remaining"?) at ./FHEM/99_myUtils.pm line 22. Global symbol "$tomorrow_fc" requires explicit package name (did you forget to declare "my $tomorrow_fc"?) at ./FHEM/99_myUtils.pm line 23. Global symbol "$overmorrow_fc" requires explicit package name (did you forget to declare "my $overmorrow_fc"?) at ./FHEM/99_myUtils.pm line 24. Global symbol "$today_fc_remaining" requires explicit package name (did you forget to declare "my $today_fc_remaining"?) at ./FHEM/99_myUtils.pm line 26. Global symbol "$today_fc_total" requires explicit package name (did you forget to declare "my $today_fc_total"?) at ./FHEM/99_myUtils.pm line 27. Global symbol "$tomorrow_fc" requires explicit package name (did you forget to declare "my $tomorrow_fc"?) at ./FHEM/99_myUtils.pm line 28. Global symbol "$overmorrow_fc" requires explicit package name (did you forget to declare "my $overmorrow_fc"?) at ./FHEM/99_myUtils.pm line 29. Global symbol "$today_fc_total" requires explicit package name (did you forget to declare "my $today_fc_total"?) at ./FHEM/99_myUtils.pm line 33. Global symbol "$allDays_fc" requires explicit package name (did you forget to declare "my $allDays_fc"?) at ./FHEM/99_myUtils.pm line 34. Global symbol "$today_fc_total" requires explicit package name (did you forget to declare "my $today_fc_total"?) at ./FHEM/99_myUtils.pm line 34. Global symbol "$tomorrow_fc" requires explicit package name (did you forget to declare "my $tomorrow_fc"?) at ./FHEM/99_myUtils.pm line 34. Global symbol "$overmorrow_fc" requires explicit package name (did you forget to declare "my $overmorrow_fc"?) at ./FHEM/99_myUtils.pm line 34. Global symbol "$allDays_fc" requires explicit package name (did you forget to declare "my $allDays_fc"?) at ./FHEM/99_myUtils.pm line 35.

Kannst du mir (DUMMY) bitte Helfen?

Liebe Grüße, Michael
Zu mir:
Ich bin leider fast blind (EU Rentner) und nicht zu "faul" was nachzulesen. Bitte seht mir nach, wenn ich einmal zu viel frage oder auch mal Schreibfehler mache. Danke, Michael

limes1337

Hi Michael,

ich bin kein Perl Freak, aber ich vermute ich kann Dir helfen. Ich gehe davon aus, dass Du in Deinem myUtils irgendwo "use strict" eingebaut hast. Zur Folge musst Du Variablen auch strikt (um im Wortlaut zu bleiben) definieren, so ich Perl richtig verstanden habe. Mehr dazu lesen kannst Du bspw. hier:
https://perlmaven.com/global-symbol-requires-explicit-package-name

Im konkreten Beispiel heißt das einfach, dass Du in meinem Skript die Variablen mit einem "my" deklarieren musst. Oder um es Dir einfach zu machen, probier es mal damit:

sub PVForecast ()
{
my $today_fc_remaining = round(ReadingsVal("tech.online.solcast_so","forecast_today","0") + ReadingsVal("tech.online.solcast_so2","forecast_today","0") + ReadingsVal("tech.online.solcast_nw","forecast_today","0"),1);
my $today_fc_total = round($today_fc_remaining + ReadingsVal("outdoor.pv","Daily_yield_kwh","0"),1);
my $tomorrow_fc = round(ReadingsVal("tech.online.solcast_so","forecast_tomorrow","0") + ReadingsVal("tech.online.solcast_so2","forecast_tomorrow","0") + ReadingsVal("tech.online.solcast_nw","forecast_tomorrow","0"),1);
my $overmorrow_fc = round(ReadingsVal("tech.online.solcast_so","forecast_overmorrow","0") + ReadingsVal("tech.online.solcast_so2","forecast_overmorrow","0") + ReadingsVal("tech.online.solcast_nw","forecast_overmorrow","0"),1);

fhem("setreading cellar.control.energy forecast_today_remaining $today_fc_remaining");
fhem("setreading cellar.control.energy forecast_today_total $today_fc_total");
fhem("setreading cellar.control.energy forecast_tomorrow $tomorrow_fc");
fhem("setreading cellar.control.energy forecast_overmorrow $overmorrow_fc");

if (strftime ("%H",localtime (time)) == 4)
{
fhem("setreading cellar.control.energy forecast_today_total_morning $today_fc_total");
$allDays_fc = $today_fc_total + $tomorrow_fc + $overmorrow_fc;
fhem("setreading cellar.control.energy forecast_3days $allDays_fc");
}

}


Mickey

Hi Limes,

Zu dir: 10000000 mal DANKE - Das war echt einfach für meine Augen.

Ich habe noch ein my vor $allDays_fc
gemacht. Er hat jetzt kein "Error" mehr angezeigt.

Dummy cellar.control.energy ist angelegt

Wenn du Lust hast, kannst du es gerne kontrollieren:
Okay, okay - Sorry, wenn ich nerve.

sub PVForecast ()
{
my $today_fc_remaining = round(ReadingsVal("tech.online.solcast_so1","forecast_today","0") + ReadingsVal("tech.online.solcast_so2","forecast_today","0"),1);
my $today_fc_total = round($today_fc_remaining + ReadingsVal("outdoor.pv","Daily_yield_kwh","0"),1);
my $tomorrow_fc = round(ReadingsVal("tech.online.solcast_so1","forecast_tomorrow","0") + ReadingsVal("tech.online.solcast_so2","forecast_tomorrow","0"),1);
my $overmorrow_fc = round(ReadingsVal("tech.online.solcast_so1","forecast_overmorrow","0") + ReadingsVal("tech.online.solcast_so2","forecast_overmorrow","0"),1);

fhem("setreading cellar.control.energy forecast_today_remaining $today_fc_remaining");
fhem("setreading cellar.control.energy forecast_today_total $today_fc_total");
fhem("setreading cellar.control.energy forecast_tomorrow $tomorrow_fc");
fhem("setreading cellar.control.energy forecast_overmorrow $overmorrow_fc");

if (strftime ("%H",localtime (time)) == 4)
{
fhem("setreading cellar.control.energy forecast_today_total_morning $today_fc_total");
my $allDays_fc = $today_fc_total + $tomorrow_fc + $overmorrow_fc;
fhem("setreading cellar.control.energy forecast_3days $allDays_fc");
}

}


Ich habe nur 2 PV Dächer (Ost/West).

- Liebe Grüße, Michael
Zu mir:
Ich bin leider fast blind (EU Rentner) und nicht zu "faul" was nachzulesen. Bitte seht mir nach, wenn ich einmal zu viel frage oder auch mal Schreibfehler mache. Danke, Michael

Mickey

Hi Limes, ich nochmal

in der Not habe ich mir schnell 2 DOIF`s zusammengekloppt

defmod di_astro DOIF ([[Astro:NauticTwilightMorning]]) \
(setreading pv_forecast today_total [pv_forecas:today_remaining] )
attr di_astro DbLogExclude .*
attr di_astro do always
attr di_astro room Technik
attr di_astro stateFormat timer_01_c01


defmod di_pv_forecast DOIF ([tech.online.solcast_so1:forecast_today]) \
(setreading pv_forecast today_ost [tech.online.solcast_so1:forecast_today] )\
(setreading pv_forecast tomorrow_ost [tech.online.solcast_so1:forecast_tomorrow] )\
(setreading pv_forecast overmorrow_ost [tech.online.solcast_so1:forecast_overmorrow] )\
(setreading pv_forecast today_west [tech.online.solcast_so2:forecast_today] )\
(setreading pv_forecast tomorrow_west [tech.online.solcast_so2:forecast_tomorrow] )\
(setreading pv_forecast overmorrow_west [tech.online.solcast_so2:forecast_overmorrow] )
attr di_pv_forecast DbLogExclude .*
attr di_pv_forecast do always
attr di_pv_forecast room Technik


ZitatHeute verbleibend 0.0 Kw
Heute today_total Kw
Morgen 11.5 Kw
Übermorgen 18.5 Kw

Ich glaube, es hätte funktioniert - Nur deine Lösung ist Eleganter.

- Liebe Grüße, Michael
Zu mir:
Ich bin leider fast blind (EU Rentner) und nicht zu "faul" was nachzulesen. Bitte seht mir nach, wenn ich einmal zu viel frage oder auch mal Schreibfehler mache. Danke, Michael

Klinki

Moin Forum,

Seit zwei Tagen hole ich mir auch meine Daten von Solcast in´s fhem. Ein dickes Danke für eure Entwicklung!

Da ich ein Split-Array (Solar-Panels mit Ausrichtung OST/West auf dem Dach) habe, sind die Prognosen prinzipbedingt ungenau. Auf der Solcast-Seite werden dazu verschiedene Ansätze beschrieben. Letzten Endes wird aber nur helfen zwei Sites zu definieren und die Summe zu bilden.
Dann muss natürlich auch die Anzahl der täglichen Abfragen reduziert werden.
Oder fällt hierzu noch jemandem etwas Schlaueres ein?

Gruß,
klinki

limes1337

Hi klinki,

wie Du korrekt beschreibst, machen 2 Sites in Deinem Fall Sinn.
Es gab hier zwar schon Diskussionen darüber, aber nach meinem Verständnis hat man mit einem Free Account 50 kostenlose Abfragen pro Tag. Davon ausgehend, ein paar Gedanken, die sich sogar ergänzen:

  • Wenn man einmal stündlich abfragt, kommt man bei 2 Sties auf 2 x 24 = 48 Abfragen - sollte also passen.
  • Ansonsten kann man sich die Abfragen zwischen Sonnenuntergang und -aufgang eigentlich sparen - ob man das zeitlich steuert oder über sunrise, o.ä. sei jedem selbst überlassen
  • Für alles weitere - einfach einen zweiten Account mit neuer Mailadresse anlegen  :)

Grüße,
limes

DS_Starter

Moin,

Zitat
Ansonsten kann man sich die Abfragen zwischen Sonnenuntergang und -aufgang eigentlich sparen - ob man das zeitlich steuert oder über sunrise, o.ä. sei jedem selbst überlassen
In der Art habe ich die Steuerung der API-Calls in das Solarforecast Modul eingebaut.
LG
ESXi@NUC+Debian+MariaDB, PV: SMA, Victron MPII+Pylontech+CerboGX
Maintainer: SSCam, SSChatBot, SSCal, SSFile, DbLog/DbRep, Log2Syslog, SolarForecast,Watches, Dashboard, PylonLowVoltage
Kaffeekasse: https://www.paypal.me/HMaaz
Contrib: https://svn.fhem.de/trac/browser/trunk/fhem/contrib/DS_Starter

Klinki

#59
Ich hatte 10 Abfragen / Tag im Kopf. Du hast Recht: es sind 48 und die reichen auch. Abfragen mit Astro-Funktion zu koppeln ist also nicht nötig. Im Gegenteil: Ich finde es spannend, wenn die Wärmpepumpe um 6 Uhr morgens die Ansage bekommt mit der Erzeugung bis 10 Uhr zu warten.
Ja, klar, ein Minimum an Heiz-Speicher-Ladung muss natürlich vorhanden sein.

Ansonsten habe ich mir die Sache sehr einfach gemacht: Die Daten für beide Sites (Ost/West) werden heruntergeladen und in einem neuen Dummy einfach summiert. Mit DOIF. Einfach. Funktioniert!

Nach dem Forecast richten sich dann die Soll-Temperaturen und ob nur (WAF-)relevante Heizkörper aufgehen. Weiter wird die WasserSpeichertemperatur auf ein Minimum gesenkt und die Wärmepumpe richtet ihre Modulations- bzw. Leistungsvorgabe entsprechend ein.
Noch nicht ausgegoren - aber funktionabel und: es wird bares Geld gespart!

@DS_Starter: Schaue mir grad Dein Modul näher an. Bin kein Perl-Experte - aber da steckt viel Arbeit drin!

Alles Gute,
klinki

Nachtrag: das SolarForecast-Modul ist ja genial! Nach zwei Stunden kann man "nur" den Funktionsumfang und nicht die Vorhersage bewerten. Nach einer Aufbereitung dieser Art suche ich schon lange. Danke dafür!

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?
RPi4 8GB: Buster FHEM 6.3, 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
RPi4 8GB: Buster FHEM 6.3, 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
RPi4 8GB: Buster FHEM 6.3, 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