(20 dazwischenliegende Versionen von 3 Benutzern werden nicht angezeigt)
Zeile 58:
Zeile 58:
==Kompatibilität zu anderen Kostal Wechselrichtern==
==Kompatibilität zu anderen Kostal Wechselrichtern==
Auf Grund von Anfragen scheint diese Implementierung auch für andere Kostal Wechselrichtern zu passen.
Auf Grund von Anfragen scheint diese Implementierung auch für andere Kostal Wechselrichtern zu passen.
- Plenticore Plus
- Plenticore Plus G1-G3
- Piko MP
- Piko MP
- Piko IQ
- Piko IQ
Dies hängt natürlich mit der Firmware zusammen und es gibt sicherlich noch einige Anpassungen die notwendig sind. Bitte tragt Eure kompatieblen Geräte hier ein.
- Piko CI 30
Dies hängt natürlich mit der Firmware zusammen und es gibt sicherlich noch einige Anpassungen die notwendig sind. Bitte tragt Eure kompatiblen Geräte hier ein.
Anmerkung: Kostal Piko CI 30 mit dem ModbusAttr Modul
Die Modbus-Attribute können zum großen Teil übernommen werden, aber Kostal fängt bei diesem Modell nicht bei "0", sondern bei "1" an zu zählen. Das heißt, alle [https://cdn-production.kostal.com/-/media/document-library-folder---kse/2023/11/20/15/12/ba_kostal-interface-description-modbus_piko-ci.pdf Modbus-Adressen die hier genannt werden], müssen um 1 vermindert werden.
==Einbindung in das Netzwerk==
==Einbindung in das Netzwerk==
Zeile 132:
Zeile 136:
Hier wäre mal ein Beispiel, wie die MySQL Datenbank innerhalb einer .yml Datei im Docker definiert werden kann. Im Beispiel ist FHEM und Portainer ebenfalls enthalten, wodurch dies auch somit eine Basis Installation sein könnte.
Hier wäre mal ein Beispiel, wie die MySQL Datenbank innerhalb einer .yml Datei im Docker definiert werden kann. Im Beispiel ist FHEM und Portainer ebenfalls enthalten, wodurch dies auch somit eine Basis Installation sein könnte.
[https://svn.fhem.de/fhem/trunk/fhem/contrib/ch.eick/Docker/docker-compose.yml Beispiel für eine docker-compose.yml]
* [https://svn.fhem.de/fhem/trunk/fhem/contrib/ch.eick/Docker/docker-compose.yml Beispiel für eine docker-compose.yml]
<syntaxhighlight lang="Perl">
<syntaxhighlight lang="Perl">
pi@raspberrypi:~/docker-compose/fhem_2022 $ pwd
pi@raspberrypi:~/docker-compose/fhem_2022 $ pwd
Zeile 175:
Zeile 179:
=====MySQL Kommandos=====
=====MySQL Kommandos=====
<syntaxhighlight lang="SQL">
<syntaxhighlight lang="sql">
-- Die fhem Datenbank neu anlegen
-- Die fhem Datenbank neu anlegen
mysql> CREATE DATABASE `fhem` DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_bin;
mysql> CREATE DATABASE `fhem` DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_bin;
Zeile 191:
Zeile 195:
-- Im Oracle MySQL wird im Standard das Passwort in einer anderen Verschlüsselung abgeregt, die FHEM noch nicht unterstützt
-- Im Oracle MySQL wird im Standard das Passwort in einer anderen Verschlüsselung abgeregt, die FHEM noch nicht unterstützt
-- Deshalb kann es erforderlich sein das Passwort, nach Änderung der Verschlüsselungsmethode neu zu setzen
-- Deshalb kann es erforderlich sein das Passwort, nach Änderung der Verschlüsselungsmethode neu zu setzen
mysql> DROP USER fhemuser;
mysql> CREATE USER 'fhemuser'@'%';
-- Nachschauen welche Passwort Verschlüsselung verwendet wird
-- Nachschauen welche Passwort Verschlüsselung verwendet wird
Zeile 202:
Zeile 204:
| % | fhemuser | caching_sha2_password | <<< Das unterstützt FHEM mit DbLog noch nicht
| % | fhemuser | caching_sha2_password | <<< Das unterstützt FHEM mit DbLog noch nicht
+------+----------+-----------------------+
+------+----------+-----------------------+
1 row in set (0.00 sec)
mysql> UPDATE mysql.user SET plugin = 'mysql_native_password' WHERE User='fhemuser';
mysql> DROP USER fhemuser;
mysql> CREATE USER 'fhemuser'@'%' IDENTIFIED WITH mysql_native_password BY '<password>';
mysql> SELECT Host,User,plugin FROM mysql.user WHERE User='fhemuser';
mysql> SELECT Host,User,plugin FROM mysql.user WHERE User='fhemuser';
Zeile 213:
Zeile 215:
+------+----------+-----------------------+
+------+----------+-----------------------+
1 row in set (0.01 sec)
1 row in set (0.01 sec)
mysql> ALTER USER 'fhemuser'@'%' IDENTIFIED BY '< Password >';
-- Bei den GRANDS muss ich nochmal nachschauen, was da wirklich notwendig ist
-- Bei den GRANDS muss ich nochmal nachschauen, was da wirklich notwendig ist
mysql> GRANT SELECT, INSERT, DELETE, UPDATE ON `fhem`.* TO 'fhemuser'@'%';
mysql> GRANT SELECT, INSERT, DELETE, UPDATE ON `fhem`.* TO 'fhemuser'@'%';
mysql> GRANT ALTER ROUTINE ON `fhem`.* TO 'fhemuser'@'%';
mysql> GRANT ALTER ROUTINE ON `fhem`.* TO 'fhemuser'@'%';
mysql> GRANT EXECUTE ON PROCEDURE `fhem`.`dwd_load` TO 'fhemuser'@'%';
mysql> GRANT EXECUTE ON PROCEDURE `fhem`.'dwd_load' TO 'fhemuser'@'%'; <<<< Das beschränkt auf nur eine Prozedure
-- Die Änderungen müssen mit einem FLUSH in der aktuellen Session übernommen werden
-- Die Änderungen müssen mit einem FLUSH in der aktuellen Session übernommen werden
-- Es könnte auch gut sein einen extra root user anzulegen, um mit DbRep die MySQL Datenbank zu optimieren
mysql> CREATE USER 'fhemroot'@'<IP Adresse>' IDENTIFIED WITH mysql_native_password BY '<password>';
mysql> GRANT ALL PRIVILEGES ON `fhem`.* TO 'fhemroot'@'<IP Adresse>' WITH GRANT OPTION;
mysql> GRANT SHOW_ROUTINE ON *.* TO 'fhemroot'@'<IP Adresse>';
mysql> GRANT EXECUTE, CREATE ROUTINE, ALTER ROUTINE ON `fhem`.* TO 'fhemroot'@'<IP Adresse>';
-- Die Änderungen müssen mit einem FLUSH in der aktuellen Session übernommen werden
mysql> FLUSH PRIVILEGES;
mysql> SELECT user,host FROM mysql.user;
+------------------+----------------+
| user | host |
+------------------+----------------+
| fhemuser | % |
| fhemroot | 192.168.178.57 | <<< Das sollte man dann auf eine IP einschränken.
| root | localhost |
+------------------+----------------+
-- Hier kann man mal seine Proceduren auflisten
mysql> SHOW PROCEDURE STATUS WHERE Db = 'fhem';
</syntaxhighlight>
</syntaxhighlight>
Zeile 295:
Zeile 319:
</pre>
</pre>
[https://svn.fhem.de/fhem/trunk/fhem/contrib/ch.eick/Photovoltaik/Wechselrichter/RAW_WR_1.txt Beispiel für WR_1]
* [https://svn.fhem.de/fhem/trunk/fhem/contrib/ch.eick/Photovoltaik/Wechselrichter/RAW_WR_1.txt Beispiel für WR_1]
======userReadings======
======userReadings======
Zeile 322:
Zeile 346:
In einem Schwarm kann man den zweiten Wechselrichter etwas sparsamer definieren, da es nur einen Wechselrichter mit Speicher geben darf und der zweite Wechselrichter auch keine KSEM Verbindung hat. Auch die SW_* readings werden hier nicht benötigt. Im userReading des WR_1 und WR_1_API wird auch auf den WR_2 und WR_2_API referenziert.
In einem Schwarm kann man den zweiten Wechselrichter etwas sparsamer definieren, da es nur einen Wechselrichter mit Speicher geben darf und der zweite Wechselrichter auch keine KSEM Verbindung hat. Auch die SW_* readings werden hier nicht benötigt. Im userReading des WR_1 und WR_1_API wird auch auf den WR_2 und WR_2_API referenziert.
[https://svn.fhem.de/fhem/trunk/fhem/contrib/ch.eick/Photovoltaik/Wechselrichter/RAW_WR_2.txt Beispiel für WR_2]
* [https://svn.fhem.de/fhem/trunk/fhem/contrib/ch.eick/Photovoltaik/Wechselrichter/RAW_WR_2.txt Beispiel für WR_2]
=====Kostal Plenticore Plus die API=====
=====Kostal Plenticore Plus die API=====
Zeile 329:
Zeile 353:
======Plenticore API======
======Plenticore API======
Die API Definition kann man sich mit folgendem Aufruf anschauen, sie wird von Kostal jedoch nicht supportet.
<syntaxhighlight lang="Perl">
<syntaxhighlight lang="Perl">
http://<IP-Address_Plenticore>/api/v1
http://<IP-Address_Plenticore>/api/v1
Zeile 367:
Zeile 392:
======KeyValue()======
======KeyValue()======
Die KeyValue() Funktion kommt in die 99_myUtils.pm .
Die KeyValue() Funktion kommt in die 99_myUtils.pm .
[https://svn.fhem.de/fhem/trunk/fhem/contrib/ch.eick/Photovoltaik/99_myUtils.pm_Erg%c3%a4nzungen.txt Beispiel 99_myUtils.pm Ergänzungen]
* [https://svn.fhem.de/fhem/trunk/fhem/contrib/ch.eick/Photovoltaik/99_myUtils.pm_Erg%c3%a4nzungen.txt Beispiel 99_myUtils.pm Ergänzungen]
======KeyValue() Test======
======KeyValue() Test======
Zeile 387:
Zeile 412:
Die plenticore_auth() kommt in die 99_myUtils.pm .
Die plenticore_auth() kommt in die 99_myUtils.pm .
[https://svn.fhem.de/fhem/trunk/fhem/contrib/ch.eick/Photovoltaik/99_myUtils.pm_Erg%c3%a4nzungen.txt Beispiel 99_myUtils.pm Ergänzungen]
* [https://svn.fhem.de/fhem/trunk/fhem/contrib/ch.eick/Photovoltaik/99_myUtils.pm_Erg%c3%a4nzungen.txt Beispiel 99_myUtils.pm Ergänzungen]
======plenticore_auth() Test======
======plenticore_auth() Test======
Zeile 540:
Zeile 565:
RAW Definition des WR_1_API Master
RAW Definition des WR_1_API Master
[https://svn.fhem.de/fhem/trunk/fhem/contrib/ch.eick/Photovoltaik/Wechselrichter/RAW_WR_1_API.txt Beispiel WR_1_API]
* [https://svn.fhem.de/fhem/trunk/fhem/contrib/ch.eick/Photovoltaik/Wechselrichter/RAW_WR_1_API.txt Beispiel WR_1_API]
Initiales setzen der WR_0_KSEM Zähler Stände. Dies sollte nach einem save config durch das setstate, bei einem restart wieder richtig gesetzt sein. Das ganze neu berechnen der Statistiken ist nur ein Work around, bis Kostal das Problem in der Firmware korrigiert hat.
Initiales setzen der WR_0_KSEM Zähler Stände. Dies sollte nach einem save config durch das setstate, bei einem restart wieder richtig gesetzt sein. Das ganze neu berechnen der Statistiken ist nur ein Work around, bis Kostal das Problem in der Firmware korrigiert hat.
Zeile 551:
Zeile 576:
setstate WR_1_API SW_Meter_init_Grid_Year xxxx << Jahreswert um 00:01 am 01.01 des Jahres
setstate WR_1_API SW_Meter_init_Grid_Year xxxx << Jahreswert um 00:01 am 01.01 des Jahres
</syntaxhighlight>
</syntaxhighlight>
Erweiterung im PV_Schedule, um die Zählerstände zu speichern
</syntaxhighlight>Man kann den Schaltausgangdes Wechselrichtern mit "set 41_01_DigitalOutputs" auch direkt schalten.
Erweiterung im WR_ctl, um die Zählerstände zu speichern
Im WR_ctl (DOIF im Perl Modus) gibt es einen Block für das Wiederherstellen der Zählerstände aus der DbLog, sofern das Monitoring bereits einen Jahres Zyklus gelaufen ist. Der Block 4_WR_1_API_init_Werte kann über das uiTable Pull Down Menü im WR_ctl im Bereich "WR_1_API Kommando Auswahl" auch manuell ausgeführt werden, was nach einem FHEM Absturz eventuell notwendig sein könnte. Die Notwendigkeit erkennt man an negativen Werten in der Spalte "aktuell".
======DigitalOutputs schalten======
Man kann den Schaltausgangdes Wechselrichtern mit "set 41_01_DigitalOutputs" auch direkt schalten.
Dies ist jedoch keine original Funktion des Wechselrichters, sondert manipuliert die Konfiguration des Schaltausgangs.
Dies ist jedoch keine original Funktion des Wechselrichters, sondert manipuliert die Konfiguration des Schaltausgangs.
Testaufbau: Ne lange Leitung bis ins warme Büro und ein Durchgangsprüfer am Potentialfreien Relais Ausgang des Plenticore.
Testaufbau: Ne lange Leitung bis ins warme Büro und ein Durchgangsprüfer am Potentialfreien Relais Ausgang des Plenticore.
Nebenergebnis: Der Summer am 40 Jahre alten Messgerät ist kaputt und die Gewährleistung ist rum.
Nebenergebnis: Der Summer am 40 Jahre alten Messgerät ist kaputt und die Gewährleistung ist rum.
Es soll festgestellt werden, ob man das Relais mit den oben vorgegebenen Angaben Ein- und Ausschalten kann.
Es soll festgestellt werden, ob man das Relais mit den oben vorgegebenen Angaben Ein- und Ausschalten kann.
Zeile 600:
Zeile 603:
9-0 => Aus, das Flag bleibt nun auf 0
9-0 => Aus, das Flag bleibt nun auf 0
9 => Ein nach 1 Minute
9 => Ein nach 1 Minute
9-0 => Aus, das Flag bleibt nun auf 0
9-0 => Aus, das Flag bleibt nun auf 0
Durch die 9 schaltet es bereits ab, würde jedoch nach 1 Minute wieder an gehen.
Durch die 9 schaltet es bereits ab, würde jedoch nach 1 Minute wieder an gehen.
Die 0 deaktiviert die Steuerung komplett.
Die 0 deaktiviert die Steuerung komplett.
Zeile 617:
Zeile 617:
======RAW Definition des WR_2_API Slave ab v1.16======
======RAW Definition des WR_2_API Slave ab v1.16======
Auch hier kann man im Schwarm für den zweiten Wechselrichter eine verkürzte definition verwenden.
Auch hier kann man im Schwarm für den zweiten Wechselrichter eine verkürzte definition verwenden.
<syntaxhighlight lang="Perl">
* [https://svn.fhem.de/fhem/trunk/fhem/contrib/ch.eick/Photovoltaik/Wechselrichter/RAW_WR_2_API.txt Beispiel WR_2_API]
======Tarifsteuerung mit einem DOIF (Beispiel)======
======Tarifsteuerung mit einem DOIF (Beispiel)======
<syntaxhighlight lang="Perl">
<syntaxhighlight lang="Perl">
Zeile 1.154:
Zeile 840:
======Middayhigh Kontrolle======
======Middayhigh Kontrolle======
Über die Solar_forecast() Funktion wird ein Middayhigh ermittelt, wenn der WR nur 70% einspeisen darf. Auch beim Betrieb von zwei Wechselrichtern ist dies wichtig, da mit Stand 03/2021, der Plenticore den Hausverbrauch nicht korrekt ermitteln kann. In diesem Fall funktioniert die "intelligente Batteriesteuerung" nicht mehr und muss deaktiviert werden. Durch die Middayhigh Kontrolle wird aus dem Forecast, durch die Funktion Solar_forecast() eine Überschreitung von Inverer_Max_Power (70%) Regelung ermittelt. Damit kann über die externe Speichersteuerung die Hauptladung des Speichers in die Mittagszeit verlagert werden, um die dynamische 70% Regelung zu nutzen, bevor der Wechselrichter abgeregelt wird.
Über die KI_Prognose() Funktion wird ein Middayhigh ermittelt, wenn der WR nur 70% einspeisen darf. Auch beim Betrieb von zwei Wechselrichtern ist dies wichtig, da mit Stand 03/2021, der Plenticore den Hausverbrauch nicht korrekt ermitteln kann. In diesem Fall funktioniert die "intelligente Batteriesteuerung" nicht mehr und muss deaktiviert werden. Durch die Middayhigh Kontrolle wird aus dem Forecast, durch die Funktion KI_Prognose() eine Überschreitung von Inverer_Max_Power (70%) Regelung ermittelt. Damit kann über die externe Speichersteuerung die Hauptladung des Speichers in die Mittagszeit verlagert werden, um die dynamische 70% Regelung zu nutzen, bevor der Wechselrichter abgeregelt wird.
Die KI_Prognose Werte werden in dem WR_ctl Device abgelegt und sind dort als Yield_fc* zu finden.
<pre>
<pre>
Aktivierung: SpeicherMiddayControlActive An
Aktivierung: SpeicherMiddayControlActive An
Aktivität : SpeicherMiddayControlRunning Aus = momentan keine Steuerung
Aktivität : SpeicherMiddayControlRunning Aus <<<< momentan keine Steuerung
WR_1:Solar_middayhigh_fc0 0 = Es gibt kein Mittags Hoch. Wird aus Solar_forecast() gesetzt
WR_ctl:Yield_fc0_middayhigh 0 <<<< Es gibt kein Mittags Hoch. Wird aus KI_Prognose() gesetzt
WR_1:Solar_middayhigh_fc0_start 00:00 <<<< wird aus dem Forecast berechnet
WR_ctl:Yield_fc0_middayhigh_start 00:00 <<<< wird aus dem Forecast in KI_Prognose() berechnet
WR_1:Solar_middayhigh_fc0_stop 00:00 <<<< wird aus dem Forecast berechnet
WR_ctl:Yield_fc0_middayhigh_stop 00:00 <<<< wird aus dem Forecast in KI_Prognose() berechnet
Konfiguration:
Konfiguration:
Der Wert von SpeicherMidday_MaxChargePowerAbs sollte so gewählt werden, dass der Speicher am Vormittag langsam und gleichmäßig bis auf SpeicherMidday_MaxSOC geladen wird.
Der Wert von SpeicherMidday_MaxChargePowerAbs sollte so gewählt werden, dass der Speicher am Vormittag langsam und gleichmäßig bis auf SpeicherMidday_MaxSOC geladen wird.
Ab der PV_1:Solar_middayhigh_fc0_start wird dann unlimitiert bis zur PV_1:Solar_middayhigh_fc0_stop in der Mittagszeit weiter geladen. Wenn SpeicherMaxSOCControlActive auf 1 ist, wird hierbei weiterhin das Laden limitiert.
Ab der WR_ctl:Yield_fc0_middayhigh_start wird dann unlimitiert bis zur WR_ctl:Yield_fc0_middayhigh_stop in der Mittagszeit weiter geladen. Wenn SpeicherMaxSOCControlActive auf 1 ist, wird hierbei weiterhin das Laden limitiert.
SpeicherMidday_Inverter_Max_Power 8500 <<<< hier kann man manuell einen Wert festlegen, wenn es keine 70% Regelung gibt
SpeicherMidday_Inverter_Max_Power 8500 <<<< hier kann man manuell einen Wert festlegen, wenn es keine 70% Regelung gibt
Zeile 1.194:
Zeile 881:
Das DOIF übernimmt die Externe Speichersteuerung.
Das DOIF übernimmt die Externe Speichersteuerung.
<syntaxhighlight lang="Perl">
* [https://svn.fhem.de/fhem/trunk/fhem/contrib/ch.eick/Photovoltaik/Wechselrichter/RAW_WR_1_Speicher_1_ExternControl.txt Beispiel WR_1_Speicher_1_ExternControl]
## 1 Speicher Status vom WR_1_Speicher_1 aktualisieren.\
===Kostal Smart Energy Manager (KSEM) (Modbus/TCP)===
## Dies geschieht über das WR_1_API Device, da der Speicher direkt am Wechselrichter angeschlossen ist.\
Sollte man mehrere AC Quellen im Haus haben werden die Messwerte benötigt, um den Hausverbrauch richtig zu berechnen.
##\
1_Status_WR_1_Speicher_1\
Um den ModBus am KSEM zu nutzen muss man im ModBus Menü die Option "Slave" aktivieren.
{if( !([$SELF:state] eq "off") ## DOIF enabled\
and\
Auch hier ist ein Interval von 60 Sekunden gesetzt worden. Das Logging ist auf Active_energy.* eingeschränkt, weshalb man seine zusätzlichen Werte noch selber definieren muss.
( [:52] ## jede Stunde\
\
==== RAW Definition des KSEM ====
or [$SELF:ui_command_1] eq "Status_Speicher" ## Hier wird das uiTable select ausgewertet\
Zun Thema KSEM bestand direkter Kontakt mit dem Kostal Service. Der KSEM ermittelt nicht alle Werte, welche in der SunSpec spezifiziert sind. Alle nicht unterstützen Werte sind in den Registern mit 0x8000 gekennzeichnet. Für die nicht unterstützten Zählerstände wird 0x800000000 ausgegeben.
)\
Der Summenstrom M_AC_Current (sum of active phases) kann aber durch den Endanwender selber aus der Summe der Einzelwerte (Phase A AC current, Phase B AC current Phase C AC current) berechnet werden. Die einzelnen Spannungen zwischen den Phasen können nicht gemessen werden und werden deshalb nicht ausgegeben.
) {\
\
Das Device wurde umbenannt, um es besser in die gesamt Implementierung einzugliedern.
if( [$SELF:ui_command_1] eq "Status_Speicher" ) { ## Hier wurde manuell eingeschaltet\
- WR , es wird vom Wechselrichter benötigt und sortiert sich im FHEM Web auch dort ein.
}\
- 0 , es wird von mehreren Geräten benötig, was z.B. auch eine Wallbox sein kann. WR_[1|2] könnte bedeuten, dass es nur von diesem Gerät benötigt wird.
Bei einer Schwarm Installation steuert der KSEM mehrere Wechselrichter bezüglich der 70% Regelung. Eine Wallbox benötigt ebenfalls eine Verbindung, wenn nur mit Überschuss geladen werden soll.
Auch wenn der KSEM im FHEM auf disable 1 steht ist er aktiv und steuert die Wechselrichter und Wallboxen. Es bedeutet nur, dass die Werte nicht zusätzlich im Fhem eingelesen werden. Der Plenticore bereitet die Daten bereits selber auf und liefert diese im WR_1 Device per ModBus bereits mit.
Da es beim Plenticore ein Problem mit den Statistiken im Schwarm gibt wird das Device WR_0_KSEM nun aktiv verwendet. Durch das Device PV_Schedule werden die Werte Active_energy[+|-] ins Device WR_1_API übertragen und bilden die initial Werte für die Day/Month/Year Statistiken.
SpeicherMinSOC_fc1_Limit 14000 Wenn im Herbst/Winter der Forecast zu schlecht wird muss dieser Wert auf die Anlage
set_Reading("SpeicherExternTrigger","none");; ## den externen Trigger wieder freigeben\
angepasst werden. Das signalisiert die Winter Zeit
}\
SpeicherTrigger none entladen/gesperrt/none wird über WR_1_Speicher_1_ExternControl gesetzt
}\
SpeicherZeitEnde 16:00 Die Zeiten geben das Entlade Fenster an und werden durch weitere DOIF oder WeekdayTimer gesetzt
\
SpeicherZeitStart 07:00 Dies kann zur Tarifsteuerung verwendet werden, oder um ein Entladung zeitlich zu verschieben
3_smart_Laden_beenden_WB_1\
Das Zeitfenster kann durch den MinSOC Schutz im Winter veriegelt sein.
{if( !([$SELF:state] eq "off") ## DOIF enabled\
and\
Beispiele:
(\
(\
1 SpeicherEntladung Automatik
[WB_1:lp_1_ChargeStat] ne "loading" ## Es wird gerade kein Fahrzeug geladen\
Nur grundlegende Steuerungen erfolgen automatisch.
and\
- Sommer/Winter Umschaltung des MinSOC Schützt den Speicher vor einer Notladung im Winter
[$SELF:WB_1_smart_laden_before] eq "inaktiv" ## Vorher war es nicht aktiv\
- smart_laden Sorgt dafür, das der Speicher im Winter nicht ständig geladen und wieder entladen wird
)\
- laden_beendet Gibt den Speicher nach dem smart_laden wieder frei
or [$SELF:SpeicherWB_1_buffer] eq "An" ## Der Speicher darf zum Laden verwendet werden\
or [$SELF:ui_command_1] eq "smart_Laden_beenden_WB_1" ## Hier wird das uiTable select ausgewertet\
2 SpeicherEntladung Zeit
)\
Zeitsteuerung für laden/entladen
) {\
- WR_1_Speicher_1_ExternControl:SpeicherZeitEnde/SpeicherZeitStart Die Start/Ende Zeiten müssen gesetzt werden, dies muss über weitere DOIF oder WeekdayTimer erfolgen.
\
- Sommer/Winter Umschaltung des MinSOC
if( [$SELF:ui_command_1] eq "smart_Laden_beenden_WB_1" ) { ## Hier wurde manuell eingeschaltet\
set_Reading("ui_command_1","---");; ## Hier wird das uiTable select wieder zurückgesetzt, ansonsten\
</pre>
## kann das Kommando nicht sofort wiederholt werden\
}\
==Wenn man mal etwas umbenennen möchte==
\
Es kommt immer wieder vor, dass man ein Device oder den Namen eines readings umbenennen möchte. Dies hat natürlich Auswirkungen auf andere Devices und auch auf die bisherigen Daten in der DbLog. Hier sollen dann jetzt Hilfestellungen gesammelt werden.
set_Reading("WB_1_smart_laden_before","---");; ## den Merker wieder zurück setzen\
===Allgemeine Hilfestellungen===
\
'''Es sollte immer vorher eine Datensicherung gemacht werden!'''<pre>
if (AttrVal("$SELF","verbose",0) >=3)\
Den Device Namen ändert man am Besten mit einem "rename".
{Log 3, "$SELF cmd_3.2: Batterie wird mit ".[?$SELF:SpeicherEntladung]." Steuerung gesteuert";;\
Damit nichts vergessen wird ruft man den RAW Editor auf und kann dann mit der Suchfunktion des Browsers nach dem zu ändernden Text suchen.
Log 3, "$SELF cmd_3.2: Batterie auf ".[?WR_1:Act_state_of_charge]." %, Entlademodus freigegeben"};;\
Wenn alle Devices im ersten Durchlauf geändert wurden und man meint man wäre fertig, dann durchsucht man am besten nochmal die fhem.cfg . Sollten dort noch alte Namen vorhanden sein, kann man erkennen in welchem Device das ist und dieses dann in der Fhem Oberfläche korrigieren.
'''Bitte nicht in der fhem.cfg Änderungen vornehmen! Dort nur zur Kontrolle suchen.'''
if ( [WB_1:lp_1_ChargeStat] eq "loading"\
and [$SELF:SpeicherWB_1_buffer] eq "An") {\
===Ein Device umbenennen===
if (AttrVal("$SELF","verbose",0) >=3)\
<pre>
{Log 3, "$SELF cmd_3.2: MaxSOC Limitierung wegen Wallboxnutzung abgeschaltet";;}\
Das ist schnell gemacht, indem man in der Fhem commandline ein "rename <Device> <neues Device>" macht.
}\
Es ist auch möglich das alte Device mit "disable 1" zu deaktivieren und dann einfach ein komplett neues z.B. aus dem Wiki zu definieren.
\
Das alte Device kann dann später gelöscht werden, sobald das neu richtig läuft. In der datenbank kann man die alten Werte dann auch wieder dem neuen Device zuordnen.
set_Reading("SpeicherExternTrigger","none");; ## den externen Trigger wieder freigeben\
Als nächstes haben viele Devices noch ein Attribut "alias", das meistens den selben Namen wie das Device beinhaltet.
}\
Ein Device Name kann auch in anderen Attributen als Variable verwendet worden sein. Das ist zu prüfen.
}\
Nun werden alle neuen, aktualisierten readings unter dem neuen Device Namen in die Datenbank geschrieben.
\
Die bisherigen Log Einträge müssen nun noch dem neuen Device zugeordnet werden.
## 3 Wenn vor dem WB_1 laden das smart_Laden aktiv gewesen ist geht es zurück in den Zustand\
===Ein reading umbenennen===
## \
<pre>
3_smart_Laden_umschalten_WB_1\
Dies geschieht innerhalb des Devices, indem man das Attribut, dass das reading erzeugt ändert und den neuen Namen einträgt.
{if( !([$SELF:state] eq "off") ## DOIF enabled\
Bei der nächsten Aktualisierung erscheint dann ein zweites reading mit dem neuen Namen.
and\
Der neue Name muss dann noch an allen Stellen innerhalb des Devices eingetragen werden, Das kann im userReading, stateFormat oder auch in anderen Attributen der Fall sein.
(\
Soll dieses Reading gelogged werden, ist "DbLogInclude" zu prüfen. Der alte Name kann raus und der neue muss rein, oder die RegEx muss geändert werden.
[WB_1:lp_1_ChargeStat] ne "loading" ## Es wird gerade kein Fahrzeug geladen\
Zum Schluss muss das alte reading noch entfernt werden, was mit "deletereading <Device> <alter reading Name>" erfolgen kann. Oft ist hier auch eine RegEx möglich.
and\
</pre>
[$SELF:WB_1_smart_laden_before] eq "aktiv" ## Vorher war es nicht aktiv\
)\
===DbLog aufräumen===
) {\
Als kleine Vorabinformation möchte ich geben, dass es hierbei eventuell zu '''duplicate keys''' kommen kann. Dies rührt daher, dass eventuell der alte und der neue Namen parallel geloggt wurde. Schaut Euch hier die Daten an, welche Ihr behalten möchtet, oder ob Ihr wirklich z.B. das alte reading und SW_* braucht. Ab dem Zeitpunkt wo es parallel gelaufen ist, wäre dann eins (das alte) zu löschen.
\
if( [$SELF:ui_command_1] eq "smart_Laden_umschalten_WB_1" ) { ## Hier wurde manuell eingeschaltet\
Beim Übergang zum Schwarm habe ich alle älteren Daten den neuen readings zugeordnet und momentan, ab diesem Zeitpunkt, beides gelogged.
Natürlich kann man die vorherigen UPDATE auch zusammenfassen, also DEVICE und READING in einem ändern. Das sollte aber nur machen, wer in SQL entsprechende Kenntnisse hat.
## 4 Freigabe der Batterie mit externem Trigger oder bei Zeitsteuerung\
Die Umbenennung des DEVICE zum neuen DEVICE und anschließend die READING Namen ist der praktikabelste Weg.
## z.B. ([07:00-16:00]\
4_Trigger\
===Grafiken korrigieren===
{if( !([$SELF:state] eq "off") ## DOIF enabled\
====Grafana====
and\
In Grafana sind die SQL Abfragen ebenfalls zu korrigieren
(\
( [$SELF:SpeicherExternTrigger] eq "frei" ## Verriegelung, wenn zwangsgeladen werden muss\
===Fhem Log===
and [WR_1_API:Battery_InternControl_MinHomeConsumption] > 100\
Das Fhem Log ist nach jedem größeren Änderungsschritt zu sichten, da man hier ziemlich schnell vergessene Devices oder readings erkennen kann.
and [$SELF:SpeicherTrigger] eq "entladen" ## also Speicherentladung freigeben\
=== RAW Definition WR_ctl (DOIF)===
or \
Das WR_ctl Device hat die zeitliche Steuerungder PV-Anlage übernommen und dient gleichzeitig der Anzeige von aktuellen und statistischen Werten im FHEMWEB.
[$SELF:SpeicherEntladung] eq "Zeit" ## oder bei Zeitsteuerung wenn das\
and [[$SELF:SpeicherZeitStart]-[$SELF:SpeicherZeitEnde]] ## Zeitfenster aktiv ist\
* [https://svn.fhem.de/fhem/trunk/fhem/contrib/ch.eick/Photovoltaik/Wechselrichter/RAW_WR_ctl.txt Beispiel WR_ctl]
)\
)\
==Energie Bilanz==
\
<big>Achtung, es gab eine Umstellung mit diesem Device! Die Bilanz wird nun direkt im WR_ctl Device mit uiTable angezeigt.</big>
or [$SELF:ui_command_1] eq "Trigger" ## Hier wird das uiTable select ausgewertet\
)\
Die Energie Bilanz soll einen kompakten Überblick über die Produktions- und Verbrauchswerte liefern. Hierbei werden die momentan Werte direkt berechnet, die restlichen Werte werden als Statistiken aus dem Gerät abgefragt. Mit DbRep Devicen kann man auch Vortag/Vormonat/Vorjahr im Wr_ctl direkt mitanzeigen lassen.
) {\
===Erstellen von zusätzlichen Werten in der Datenbank===
\
Hier werden Werte konsolidiert, weil z.B. der Wert PV_total_Month stetig steigt. Am Ende des Monats sind die gesamten Zwischenwerte ohne Aussagekraft und werden dann später mal gelöscht.
if( [$SELF:ui_command_1] eq "Trigger" ) { ## Hier wurde manuell eingeschaltet\
my $MaxChargePowerLimit = (1 - ::round($MaxChargePowerTime,2) * [$SELF:SpeicherMidday_MaxChargePowerSteigung]);; ## Zu Beginn etwas langsamer anfangen, empirisch ermittelt\
my $ret="position:absolute;;left:".(90*$val/100)."px;;width:90px;;height:20px;;background:linear-gradient( to right,#F8F8E0 ".(90-(90*$val/100))."px,rgba(0,0,0,0) ".(90-(90*$val/100))."px);;";;\
FUNC_Status([WR_1:Actual_Battery_charge_-minus_or_discharge_-plus_P],-10,"green",[WR_1:Actual_Battery_charge_-minus_or_discharge_-plus_P],"orange",[WR_1:Actual_Battery_charge_-minus_or_discharge_-plus_P],15,"red",[WR_1:Actual_Battery_charge_-minus_or_discharge_-plus_P])." W"."<div style='border-width:2px;;border-style:solid;;border-color:gray;;position:relative;;width:90px;;height:20px;;background:linear-gradient( to right, red 0px,yellow 30px,green 50px);;'>".STY(" ",FUNC_batt([WR_1:Act_state_of_charge])).STY(::round([WR_1:Act_state_of_charge],0)."%","font-size:16px;;position:absolute;;top:2px;;left:30px")."</div>"\
"<div style='border-width:2px;;border-style:solid;;border-color:gray;;position:relative;;width:90px;;height:20px;;background:linear-gradient( to right, red 0px,yellow 30px,green 50px);;'>".STY(" ",FUNC_batt([$SELF:SpeicherMaxSOC_DayBefore])).STY("gestern","font-size:12px;;position:absolute;;top:3px;;left:25px")."</div>".widget([$SELF:SpeicherMaxSOC_DayBefore],"selectnumbers,5,1,100,0,lin")."%" |\
"<div style='border-width:2px;;border-style:solid;;border-color:gray;;position:relative;;width:90px;;height:20px;;background:linear-gradient( to right, red 0px,yellow 30px,green 50px);;'>".STY(" ",FUNC_batt([$SELF:SpeicherMaxSOC_Actual])).STY("geplant","font-size:12px;;position:absolute;;top:3px;;left:25px")."</div>".widget([$SELF:SpeicherMaxSOC_Actual],"selectnumbers,5,1,100,0,lin")."%"\
Sollte man mehrere AC Quellen im Haus haben werden die Messwerte benötigt, um den Hausverbrauch richtig zu berechnen.
Dieser Wert gibt den gesamten Ertrag der PV Anlage im Monat an.
Das Gerät ist hier mit "'''disable 1'''" konfiguriert, um es zu verwenden muss das Attribut auf 0 gesetzt oder einfach gelöscht werden.
Gestartet über DB_Service_Schedule am ersten Tag der Folgewoche.
Momentan noch in keinem SVC verwendet.
Um den ModBus am KSEM zu nutzen muss man im ModBus Menü die Option "Slave" aktivieren.
Auch hier ist ein Interval von 60 Sekunden gesetzt worden. Das Logging ist noch komplett deaktiviert, weshalb man seine Werte noch selber definieren muss.
==== RAW Definition des KSEM ====
Zun Thema KSEM bestand direkter Kontakt mit dem Kostal Service. Der KSEM ermittelt nicht alle Werte, welche in der SunSpec spezifiziert sind. Alle nicht unterstützen Werte sind in den Registern mit 0x8000 gekennzeichnet. Für die nicht unterstützten Zählerstände wird 0x800000000 ausgegeben.
Der Summenstrom M_AC_Current (sum of active phases) kann aber durch den Endanwender selber aus der Summe der Einzelwerte (Phase A AC current, Phase B AC current Phase C AC current) berechnet werden. Die einzelnen Spannungen zwischen den Phasen können nicht gemessen werden und werden deshalb nicht ausgegeben.
Das Device wurde umbenannt, um es besser in die gesamt Implementierung einzugliedern.
<pre>
- WR , es wird vom Wechselrichter benötigt und sortiert sich im FHEM Web auch dort ein.
- 0 , es wird von mehreren Geräten benötig, was z.B. auch eine Wallbox sein kann. WR_[1|2] könnte bedeuten, dass es nur von diesem Gerät benötigt wird.
</pre>
Bei einer Schwarm Installation steuert der KSEM mehrere Wechselrichter bezüglich der 70% Regelung. Eine Wallbox benötigt ebenfalls eine Verbindung, wenn nur mit Überschuss geladen werden soll.
Auch wenn der KSEM im FHEM auf disable 1 steht ist er aktiv und steuert die Wechselrichter und Wallboxen. Es bedeutet nur, dass die Werte nicht zusätzlich im Fhem eingelesen werden. Der Plenticore bereitet die Daten bereits selber auf und liefert diese im WR_1 Device per ModBus bereits mit.
Da es beim Plenticore ein Problem mit den Statistiken im Schwarm gibt wird das Device WR_0_KSEM nun aktiv verwendet. Durch das Device PV_Schedule werden die Werte Active_energy[+|-] ins Device WR_1_API übertragen und bilden die initial Werte für die Day/Month/Year Statistiken.
===Löschen von nicht mehr benötigten Werten in der Datenbank===
Diese Einbindung ist nicht zwingend notwendig.
Hier wird endgültig aufgeräumt, alte momentan Werte werden gelöscht, wenn sie nach z.B. drei Monaten keine Relevanz mehr haben. Dafür wurden im vorherigen Abschnitt zusätzliche Werte in der Datenbank erzeugt, die in Diagrammen trotzdem noch einen Trend erkennen lassen.
Das Passwort wird mit KeyValue() (siehe oben) verwaltet.
Wenn eine immer größer werdende Datenbank mit steigenden Antwortzeiten nicht stört, der kann das Aufräumen auch weg lassen. Bei einer späteren Migration führt dies natürlich zu höherem Aufwand und hohen Laufzeiten.
Durch einen Test von einem anderen Mitstreiter hat sich herausgestellt, dass BYD nun die neue Version des Speichers BYD_HVS ausliefert. Dieser neue Speicher hat anscheinend noch kein WebGui und wird nur über eine Handy App Konfiguriert. Leider kann man den somit nicht mit dieser Lösung abfragen.
Der BYD HV Speicher wird über das HTTPMOD Modul angesprochen, ist jedoch noch nicht bis in die letzten Tiefen abfragbar.
Der Begriff "Array" bezeichnet einen Speicher mit mehreren Modulen, die mit dem Series_Battery_Counts angegeben werden.
Eine Battery hat dabei ca. 1.28 KW
- Die erste Abfrage führt das Login durch
- Für alle weiteren Abfragen besteht dann eine autorisierte Session mit der alle get Anfragen beantwortet werden.
- Die Abfrage von RunData liefert im Standard Fall immer "Array Num 1" mit "Series Battery Num 1". Dies kann leider noch nicht zur
Abfrage der weiteren "Series Battery Num *" umgeschaltet werden.
- Achtung, die Abfrage von "StatisticInformation" ruft eine Tabelle mit 500 Ereignissen ab, von denen jedoch nur die aktuellsten 5
als readings verarbeitet werden. Da aber alle 500 gelesen und verarbeitet werden müssen ist eine längere Laufzeit zu beachten.
Aus diesem Grund sollte die "StatisticInformation" nicht in einem kurzen Zyklus erfolgen!
Löschen aller Statistic_EnergyHomeBat_Day Werte, bis auf den maximal Wert des Tages.
InstallationConfig
Der aktuelle Tag bleibt noch erhalten.
DeviceInformation
Aufruf mit: maxValue deleteOther
BatteryInformation
StatisticInformation
userreading:
InstallationConfig_Array_Power Gibt die Nennleistung des Arrays aus der Anzahl der einzelnen Batterien an. Es wurde eine Leistung von 1.28 KW pro Batterie als Basis angenommen
======KeyValue() speichern======
Die Funktion befindet sich in der 99_myUtils und kann auch direkt in der Commandline aufgerufen werden.
<syntaxhighlight lang="Perl">
<syntaxhighlight lang="Perl">
Syntax für die Commandline im FHEM {KeyValue("[read|store]","PW_<device>_<key>","<password>")}
WR_1_Speicher_1 BYD HV HTTPMOD LAN/WLAN Speicher Details, auch über einzelne Zellen. Das kann man nur für den alten BYD HV verwenden.
==Wetter-/Leistungs-Prognose==
WR_1 rs485 4 Draht zum WR Verwendet von Plenticore zur Steuerung des Speichers
Bei der Leistungsprognose gibt es nun eine gravierende Veränderung. Die bisherige Leistungsprognose durch eine eigene Berechnung, die auf diversen Konfigurationsparametern basiert hat wurde vollständig durch eine KI_Prognose abgelöst. Die bisherige Implementierung wird nicht mehr weiter entwickelt und ist hier nur noch zu Dokumentationszwecke aufgeführt.
WR_2 Plenticore MODBUS LAN Messwerte und berechnete Werte. Achtung, im Schwarm hat nur der Master WR einen Speicher.
==Wetter-/Leistungs-Prognose KI_Prognose==
WR_2_API HTTPMOD LAN Statistiken
Erstmalig wurde die [https://forum.fhem.de/index.php?msg=1268412 KI_Prognose hier im Forumsthread in vier Teil Posts] beschrieben. Im weiteren Thread sind auch noch Informationen dazu zu finden.
===KI Prognose - Grundgedanke===
Nun ist der Ansatz der KI eingezogen und meine Ergebnisse, von bisherigen Tests, sehen schon ziemlich gut aus.
Der Grundgedanke ist, dass die Prognose keinerlei technischen Informationen über den Aufbau der PV-Anlage benötigt. Einzig allen der Ertrag der Anlage wird dabei in Bezug zu den Wetterdaten des jeweiligen Standortes gesetz, wobei die KI daraus Rückschlüsse zieht, wie bei ähnlichen Bedingungen der ertrag werden könnte. Je mehr vergleichbare Daten dazu zur Verfügung stehen, umso besser wird die Prognose.
In der momentan implementierten Prognose besteht darüber hinaus ein Problem, das man die momentan erzeugte Leistung eigentlich mit der zu erwartenden Energieprognose vergleicht.
Beim neuen Ansatz wird nun versucht das mit zu korrigieren, was auch im Diagramm durch die Stufen Darstellung verdeutlicht wird.
Die KI Prognose arbeitet nun über den Yield, den der Plenticore jede Stunde aktualisiert. Bei diesem Yield ist nun jedoch ein weiteres Problem, da der hybrid Wechselrichter natürlich auf der AC Seite den Yield angibt und somit das Laden des Speichers nicht aktuell mit zählt. Die Speicher Entladung wird später dann wiederum mit gerechnet, was die AC Yield Kurve dann sehr merkwürdig aussehen lässt. An dieser Problematik wurde auch bereits gearbeitet und das wird dann später nochmal erwähnt.
FHEM Steuerung MODBUS LAN Laufende Informationen im Minuten Takt
Im Diagramm sieht man nun in blau den korrigierten Yield unter Berücksichtigung des Speichers und in diesem Beispiel Fall für eien gesamten Schwarm (ich habe zwei WR). Jede Stufe im Diagramm ist dann nun der Ertrag (Yield) der entsprechenden Stunde in kWh.
HTTPMOD LAN Abfragen und Steuerung einzelner Devices
Zur Orientierung sieht man in gelb die AC Leistung in kW, gezeichnet aus den minütlichen Messwerten.
Die rosa Stufen sind dann nun endlich die Ertrags Prognose Werte aus der KI in kWh.
PV_Schedule DOIF Startet regelmäßige Aktionen
===KI Prognose Teil 1 - DWD und Astro Daten sammeln===
Solltet Ihr später mit in diese Richtung gehen wollen, so macht es Sinn [b]schon jetzt die Wetterdaten für Euren Standort zu sammeln[/b], da diese die Grundlage bilden und im Anschluss mit dem korrigierten Ertrag in Verbindung gebracht werden. Alle im comment angegebenen DWD Werte werden später von der KI ausgewertet und müssen somit in der DbLog vorliegen. Je mehr DWD Daten von den letzten Jahren vorliegen, umso besser kann die KI Rückschlüsse ziehen. Sollten diese nicht da sein, so lernt das ganze langsam dazu.
1 Stündlich
====RAW Definition DWD_Forecast====
1.1 WR_2_API 20_Statistic_EnergyFlow Statistiken vom Plenticore abholen; die Reihenfolge ist auch wichtig!
Erfordert ggf.
1.2 WR_1_API 20_Statistic_EnergyFlow Statistiken vom Plenticore abholen
<syntaxhighlight lang="Perl">
sudo apt-get install libxml-libxml-perl
</syntaxhighlight>
------------------------------
===RAW Definition DWD_Forecast===
Das ist veraltet, da die KI_Prognose nun verwendet werden sollte.
*[https://svn.fhem.de/fhem/trunk/fhem/contrib/ch.eick/Photovoltaik/KI_Prognose/RAW_DWD_Forecast.txt Beispiel DWD_Forecast]
2 Stündlich von 07:00 bis 20:00
2.1 WR_1_config module_1_covered Schnee auf den Modulen (noch in der Entwicklungsphase)
2.2 Solar_forecast() für fc0 Aktualisieren der fc0 Prognose
3 zweimal am Tag
===Astro Device===
3.1 Solar_forecast() für fc1 Aktualisieren der fc1 Prognose
Da die KI Prognose ja auch die Astro Daten für den Sonnenstand benötigt und dieser im Astro Device nicht als fc[0|1] vorliegt habe ich das Astro Device etwas modifiziert. In den userreadings werden dort die fc[0|1] Sonnenstände jetzt abgefragt und als readings eingetragen. Dies geschieht sobald es einen Event von ObsDate gibt, der einmal täglich kommen sollte. Somit beachtet auch die Änderung bei event-on-update-reading und beim DbLogInclude.
------------------------------
====RAW Definition Astro====
* [https://svn.fhem.de/fhem/trunk/fhem/contrib/ch.eick/Photovoltaik/KI_Prognose/RAW_Astro.txt Beispiel Astro]
4 alle 5 Minuten
====fhem.cfg Einträge für das Astro Device====
4.1 WR_2_API 04_auth_me Aktualisieren der Bilanz (es wird ein Event erzeugt)
Hier müsst Ihr Eure Position und Höhe eintragen.
4.2 WR_1_API 04_auth_me Der Master Wechselrichter kommt zum Schluss, damit die SW_* readings auch von anderen
<syntaxhighlight lang="Perl">
Wechselrichtern die richtigen Werte haben.
attr global altitude 110
attr global latitude 47.xxxxx
attr global longitude 9.yyyyy
</syntaxhighlight>
------------------------------
===KI Prognose Teil 2 - Vorbereitung der Daten===
Dies ist jetzt im WR_ctl Device enthalten
In diesem Teil geht es darum die Daten aus der FHEM History so aufzubereiten, dass sie für die KI Prognose verwendbar wird. Das Daten Model der FHEM History ist in der Form nicht für diese Verarbeitung brauchbar und wird deshalb in eine neu Tabelle überführt. Bei der Gelegenheit wird einiges noch aufbereitet und insbesondere der yield des Plenticore mit Speicher korrigiert.
WR_*_config DUMMY Konfiguration für Strings,Ausrichtung,Nennleistung,IP-Adressen,Forecast
Hier kommt nun die MySQL Procedure, die in der Datanbank hinterlegt wird. Dazu verwende ich z.B. die MySQL Workbench, wo dann die Procedure unter "Stored Precedures" auftaucht. Dies ermöglicht, dass man im FHEM DbRep Device nur diese eine Procedure aufrufen kann und nicht jedes einzelne SELECT zur Datenbank in einer separaten Session übermittelt werden muss.
1 Stündlich
* [https://svn.fhem.de/fhem/trunk/fhem/contrib/ch.eick/Photovoltaik/KI_Prognose/MySQL_dwd_load_Procedure.txt Beispiel dwd_load MySQL Procedure]
3. Die Ermittlung einer stunden basierten Tabelle ist etwas komplexer und bedarf diverser SELECT mit JOIN (Im MySQL gibt es kein full JOIN)
5 Speicher sperren bei Trigger oder Zeit Steuerung Dieses cmd_ löst die Abhängigkeiten von Zeit und Trigger auf
7. Der letzte Schritt ist dann die Möglichkeit einer Rückmeldung aus der MySQL Procedure ins FHEM
8. Über den Parameter show/none wird der Prozedure die Art der Rückmeldung mitgeteilt
1. none wäre der Default und gibt als Ergebnis das aktuelle Datum der Datenbank zurück
2. show würde den Inhalt der dwnfull Tabelle an FHEM zurück liefern, was jedoch einige hundert Zeilen sein werden
6 Wiederhole alle 180s die Kommandos der ExternControl Steuerung Wenn keine Wiederholung erfolgt geht der Plenticore wieder auf die interne Steuerung
9. Die Procedure selectiert nur die entscheidenden Daten für die jeweilige KI Prognose, um das Datenvolumen gering zu halten,
6.1 WR_1_Speicher_1_ExternControl:SpeicherMiddayControlActive wird durchlaufen, wenn der Forecast eine z.B. 70% Überschreitung erkannt hat und
denn es macht ja keinen Sinn, die Winter mit den Sommer Daten zu vergleichen
WR_1_Speicher_1_ExternControl:SpeicherMidday_MaxSOC begrenzt dann morgens den MaxSOC
WR_1_Speicher_1_ExternControl:SpeicherMidday_MaxChargePowerAbs_midday und den MaxChargePowerAbs
10. Hierbei werden deshalb folgende Zeiträume jeweils selectiert
WR_1_Speicher_1_ExternControl:SpeicherMidday_MaxChargePowerAbs_morning für morgens und mittags
1. Die letzten 30 Tage ab dem aktuellen Datum
6.2 WR_1_Speicher_1_ExternControl:SpeicherMaxSOC Wenn morgens der Speicher zu voll war, wird der MaxSOC bis abends begrenzt
2. Vom letzten Jahr 30 Tage vor dem Datum
6.3 WR_1:Solar_middayhigh_fc0_start <> WR_1:Solar_middayhigh_fc0_stop Das Laden wird mit voller Leistung freigegeben
3. Vom letzten Jahr 30 Tage nach dem Datum
6.4 nach Ablauf von WR_1:Solar_middayhigh_fc0_stop Die Midday Steuerung wird abgeschaltet, es wird normal weiter geladen, bis MaxSOC erreicht ist
4. Vom vorletzten Jahr 30 Tage vor dem Datum
6.5 WR_1_Speicher_1_ExternControl:SpeicherMaxSOC Batterie MaxSOC halten, der Default ist 100%
5. Vom vorletzten Jahr 30 Tage nach dem Datum
6. Die Forecast Daten für den nächsten Tag,
an dieser Stelle wäre es natürlich auch denkbar noch weiter in die Zukunft zu gehen,
was mir jedoch zu spekulativ ist und nach meiner Meinung bisher für keine Entscheidung von Wichtigkeit wäre.
11. Die Laufzeit dieser Procedure beträgt auf meinem RPI4 in einem Oracle MySQL Docker Container ca. 50-70 Sekunden,
deshalb musste ich bei mir den Timeout der MySQL Workbench für eine Session von 60 Sekunden auf z.B 90 Sekunden erhöhen
7 Initialisierung der externen Speichersteuerung
12. In einem Interface Eurer Wahl zur Datenbank könnt Ihr die Procedure zum Testen dann aufrufen und das Ergebnis testen.
7.1 Ist morgens der Speicher zu voll Wenn der Speicher morgens voller als 3x MinSOC ist wird MaxSOC gesetzt
</pre>
7.2 Wenn eine Überschreitung der 70% erwartet wird Aktivierung der Midday Steuerung
8 Zurücksetzen der externen Speichersteuerung
====dwd_load() Test in MySQL aufrufen====
<syntaxhighlight lang="SQL">
call dwd_load(curdate(),'none');
9 Umschaltung des MinSOC wenn zu wenig Leistung erwartet wird Schaltet im Herbst/Winter den MinSOC auf 20%
select * from dwdfull
-- WHERE TIMESTAMP > curdate()
order by TIMESTAMP desc
LIMIT 1000;
</syntaxhighlight>
10 Umschaltung des MinSoc wenn viel Leistung erwartet wir Setzt den MinSOC wieder im Frühling/Sommer auf 5%
Sollte nun der Test der Procedure eine gefüllte Tabelle anzeigen, so kann die Integration ins FHEM erfolgen. Hierzu wird dann ein DbRep Device angelegt, dass später zyklisch jede Stunde ausgeführt wird.
11 WR_1_Speicher_1 Status aktualisieren Nur beim BYD HV, Abfrage der Speicher Detailinformationen. Kann einfach entfernt werden
Achtung, bei diesem Device kommt im weiteren Fortschritt noch ein weiteres Attribut zum Aufruf des Python KI Prognose Skriptes hinzu. Im Kommentar wird dies bereits im Syntax erwähnt.
<syntaxhighlight lang="Perl">
defmod LogDBRep_PV_KI_Prognose DbRep LogDB
attr LogDBRep_PV_KI_Prognose DbLogExclude .*
attr LogDBRep_PV_KI_Prognose allowDeletion 0
attr LogDBRep_PV_KI_Prognose comment Version 2023.02.23 12:00\
\
Hier wird die Vorbereitung für die KI PV-Leistungsprognose durchgeführt\
Auch hier sollte nun getestet werden, indem man beim set das sqlCmd ausführt. Der MySQL Procedur Aufruf ist ebenfalls im Kommentar zu finden.
Als Ergebnis sollte soetwas zurück kommen. Nachdem das erschienen ist kann man den obigen Test mit dem SELECT der dwdfull Tabelle nochmals wiederholen.
<syntaxhighlight lang="Perl">
SqlResultRow_1 NOW()
SqlResultRow_2 2023-03-17 11:01:03
sqlCmd call dwd_load(curdate(),'none');
sqlResultNumRows 1
</syntaxhighlight>
WR_1_Speicher_1_ExternControl readings Konfiguration für die externe Speichersteuerung
===DbLog/DbRep Device===
====RAW Definition LogDB====
Achtung, bitte hier beachten, ob bereits eine andere DbLog verwendet wird.
<syntaxhighlight lang="Perl">
defmod LogDB DbLog ./db.conf .*:.*
attr LogDB DbLogExclude .*
attr LogDB DbLogSelectionMode Exclude/Include
attr LogDB DbLogType History
attr LogDB asyncMode 1
attr LogDB bulkInsert 1
attr LogDB disable 0
attr LogDB room System
attr LogDB showproctime 1
attr LogDB verbose 0
</syntaxhighlight>
====RAW Definition LogDBRep_PV_KI_Prognose====
Bitte beachtet, dass die Namen auch in anderen Devices eingetragen sind, wenn Ihr diese verändern wollt.
*[https://svn.fhem.de/fhem/trunk/fhem/contrib/ch.eick/Photovoltaik/KI_Prognose/RAW_LogDBRep_PV_KI_Prognose.txt Beispiel LogDBRep_PV_KI_Prognose]
====KI Prognose Teil 3 - Python KI Prognose Skript====
Für die Verwendung der KI Prognose werden die folgenden Python Packages noch benötigt. Die Basis wäre hierbei der FHEM Docker Container.
<pre>
Momentan habe ich das erstmal manuell im Container gemacht:
sudo apt-get install python3-pandas
sudo apt-get install python3-pymysql
sudo apt-get install python3-sqlalchemy # Version 1.2.18
- smart_laden Sorgt dafür, das der Speicher im Winter nicht ständig geladen und wieder entladen wird
z.B. /opt/fhem/python/bin/PV_KI_Prognose.py 192.168.178.XXX 192.168.178.YYY LogDBRep_PV_KI_Prognose WR_1 Solar_yield_fc
- laden_beendet Gibt den Speicher nach dem smart_laden wieder frei
</syntaxhighlight>
<pre>
1. Zum Test kann dies auch in "" in der FHEM Kommandozeile eingegeben werden, zuvor muss jedoch die MySQL Prozedur aufgerufen worden sein, damit die benötigte Tabelle mit den Daten erstellt worden ist.
2. Nach dem Testen kommt dieser Aufruf dann in das LogDBRep_PV_KI_Prognose Device und wird somit mit dem MySQL Prozeduraufruf synchronisiert.
Bitte das LogDBRep_PV_KI_Prognose Device (Teil 1) aus dem vorherigen Absatz verwenden.
Damit dann alles automatisch gestartet wird muss nun noch im WR_ctl Device ein Eintrag eingefügt werden.
3. Achtung, das WR_ctl Device beinhaltet jetzt die Forecast Daten und nicht wie früher das WR_1 Device.
- WR_1_Speicher_1_ExternControl:SpeicherZeitEnde/SpeicherZeitStart Die Start/Ende Zeiten müssen gesetzt werden, dies muss über weitere DOIF oder WeekdayTimer erfolgen.
- Sommer/Winter Umschaltung des MinSOC
- smart_laden
- laden_beendet
</pre>
==Wenn man mal etwas umbenennen möchte==
if (AttrVal("$SELF","verbose",0) >=3) {
Es kommt immer wieder vor, dass man ein Device oder den Namen eines readings umbenennen möchte. Dies hat natürlich Auswirkungen auf andere Devices und auch auf die bisherigen Daten in der DbLog. Hier sollen dann jetzt Hilfestellungen gesammelt werden.
Log 3, "$SELF 2_KI_Prognose : Start KI Prognose";
===Allgemeine Hilfestellungen===
}
'''Es sollte immer vorher eine Datensicherung gemacht werden!'''<pre>
Den Device Namen ändert man am Besten mit einem "rename".
set_Reading("ui_command_1","---"); ## Hier wird das uiTable select wieder zurückgesetzt, ansonsten
Damit nichts vergessen wird ruft man den RAW Editor auf und kann dann mit der Suchfunktion des Browsers nach dem zu ändernden Text suchen.
## kann das Kommando nicht sofort wiederholt werden
Wenn alle Devices im ersten Durchlauf geändert wurden und man meint man wäre fertig, dann durchsucht man am besten nochmal die fhem.cfg . Sollten dort noch alte Namen vorhanden sein, kann man erkennen in welchem Device das ist und dieses dann in der Fhem Oberfläche korrigieren.
}
</pre>
}
'''Bitte nicht in der fhem.cfg Änderungen vornehmen! Dort nur zur Kontrolle suchen.'''
< snip >
</syntaxhighlight>
===Ein Device umbenennen===
Für die Netzwerkverbindung aus dem KI Python Skript werden die Zugansdaten im Filesystem abgelegt, damit sie nicht mit dem Skript ausversehen weiter gegeben werden.
<pre>
<pre>
Das ist schnell gemacht, indem man in der Fhem commandline ein "rename <Device> <neues Device>" macht.
./python/pwd_fhem.json
Es ist auch möglich das alte Device mit "disable 1" zu deaktivieren und dann einfach ein komplett neues z.B. aus dem Wiki zu definieren.
./python/pwd_sql.json
Das alte Device kann dann später gelöscht werden, sobald das neu richtig läuft. In der datenbank kann man die alten Werte dann auch wieder dem neuen Device zuordnen.
Als nächstes haben viele Devices noch ein Attribut "alias", das meistens den selben Namen wie das Device beinhaltet.
Ein Device Name kann auch in anderen Attributen als Variable verwendet worden sein. Das ist zu prüfen.
Nun werden alle neuen, aktualisierten readings unter dem neuen Device Namen in die Datenbank geschrieben.
Die bisherigen Log Einträge müssen nun noch dem neuen Device zugeordnet werden.
</pre>
</pre>
===Ein reading umbenennen===
Die Verbindungsdaten werden in den Dateien wie folgt abgelegt:
Dies geschieht innerhalb des Devices, indem man das Attribut, dass das reading erzeugt ändert und den neuen Namen einträgt.
FHEM und die Datenbank müssen nicht auf dem selben Rechner installiert werden. Die IP-Adressen werden dem Skript beim Aufruf mitgegeben.
Bei der nächsten Aktualisierung erscheint dann ein zweites reading mit dem neuen Namen.
Der neue Name muss dann noch an allen Stellen innerhalb des Devices eingetragen werden, Das kann im userReading, stateFormat oder auch in anderen Attributen der Fall sein.
Es ist nicht erforderlich die neuen readings mit DbLogInclude aus dem WR_1 Device in die Datenbank zu loggen, da dies bereits durch das PV_KI_Prognose Skript direkt geschieht, um einen passenden TIMESTAMP pro Stunde zu bekommen.
Soll dieses Reading gelogged werden, ist "DbLogInclude" zu prüfen. Der alte Name kann raus und der neue muss rein, oder die RegEx muss geändert werden.
Zum Schluss muss das alte reading noch entfernt werden, was mit "deletereading <Device> <alter reading Name>" erfolgen kann. Oft ist hier auch eine RegEx möglich.
</pre>
</pre>
Wenn im LogDBRep_PV_KI_Prognose der verbose Level auf >= 3 steht kommen diverse Meldungen im Log:
<syntaxhighlight lang="Perl">
/usr/lib/python3/dist-packages/sklearn/externals/joblib.py:1: DeprecationWarning: the imp module is deprecated in favour of importlib; see the module's documentation for alternative uses
import imp
PV_KI_Prognose running - start
PV_KI_Prognose running - connected to 192.168.178.40
PV_KI_Prognose running - dwdfull read from DbLog 192.168.178.40
PV_KI_Prognose running - RandomForestRegressor fitted with yield
PV_KI_Prognose running - old forecast deleted
PV_KI_Prognose running - start forecast
Yield_fc0_06 06 71
Yield_fc0_07 07 406
Yield_fc0_08 08 1629
Yield_fc0_09 09 3248
Yield_fc0_10 10 4664
Yield_fc0_11 11 6210
Yield_fc0_12 12 7078
Yield_fc0_13 13 5455
Yield_fc0_14 14 4034
Yield_fc0_15 15 1189
Yield_fc0_16 16 275
Yield_fc0_17 17 170
Yield_fc0_18 18 56
Yield_fc0_19 19 43
Yield_fc0_20 20 0
--------------------------------------------
max off/at 7078 12:00
Middayhigh_start 00:00
Middayhigh_stop 00:00
4h 99
rest 99
morning 16228
afternoon 18300
day 34528
--------------------------------------------
PV_KI_Prognose running - forecast written to FHEM
PV_KI_Prognose running - old forecast deleted
PV_KI_Prognose running - start forecast
Yield_fc1_06 06 64
Yield_fc1_07 07 406
Yield_fc1_08 08 2103
Yield_fc1_09 09 4785
Yield_fc1_10 10 6902
Yield_fc1_11 11 7911
Yield_fc1_12 12 7078
Yield_fc1_13 13 5455
Yield_fc1_14 14 4034
Yield_fc1_15 15 1189
Yield_fc1_16 16 275
Yield_fc1_17 17 170
Yield_fc1_18 18 55
Yield_fc1_19 19 46
Yield_fc1_20 20 0
--------------------------------------------
max off/at 7911 11:00
Middayhigh_start 00:00
Middayhigh_stop 00:00
4h 101
rest 101
morning 22171
afternoon 18302
day 40473
--------------------------------------------
PV_KI_Prognose running - forecast written to FHEM
PV_KI_Prognose done
</syntaxhighlight>
===DbLog aufräumen===
Als kleine Vorabinformation möchte ich geben, dass es hierbei eventuell zu '''duplicate keys''' kommen kann. Dies rührt daher, dass eventuell der alte und der neue Namen parallel geloggt wurde. Schaut Euch hier die Daten an, welche Ihr behalten möchtet, oder ob Ihr wirklich z.B. das alte reading und SW_* braucht. Ab dem Zeitpunkt wo es parallel gelaufen ist, wäre dann eins (das alte) zu löschen.
Beim Übergang zum Schwarm habe ich alle älteren Daten den neuen readings zugeordnet und momentan, ab diesem Zeitpunkt, beides gelogged.
==Diagramme mit Grafana==
Grafana kann z.B. mit docker auf dem selben oder auch einem anderen System installiert werden. Es ermöglicht die Darstellung von Diagrammen und Dashboards durch die direkte Abfrage aus einer Datenbank.
===Beispiel Diagramme===
[[Datei:Leistung und Hauptverbraucher.png|mini|600px|rechts|]]
[[Datei:Forecast.png|mini|600px|rechts|]]
<pre>
Die verwendete Datenbank ist im Grafana als "FHEM MySQL" am besten vorher zu konfigurieren.
Achtung, dieses Dashboard verwendet die Schwarm readings bei den MySQL SELECT!
Eine Anpassung wäre denkbar, wenn man im JSON File "SW_" global entfernt.
Auch die Hauptverbraucher sind im Diagramm anzupassen, da sie bei mir durch eigene Zähler erfasst werden. Sollten bei Euch keine Zähler vorhanden sein, so müsstet Ihr den jeweiligen Verbraucher im Diagramm löschen.
</pre>
'''Im JSON File sind noch weitere Kommentare enthalten, die bitte auch gelesen werden sollten.'''
====An der Datenbank anmelden====
Dies kann man z.B. aus einer Terminal Session heraus machen.
<syntaxhighlight lang="Perl">
mysql -h 192.168.178.xxx --port 3306 --database fhem -u fhemuser -p
</syntaxhighlight>
====Alle Devices in der history anzeigen====
<syntaxhighlight lang="SQL">
## Alle Devices in der history Tabelle
SELECT DEVICE FROM history
GROUP BY DEVICE;
</syntaxhighlight>
====Alle readings eines Devices anzeigen====
*[https://svn.fhem.de/fhem/trunk/fhem/contrib/ch.eick/Grafana/Dashboard/Kostal-Flow_V2_JSON.txt Beispiel Grafana Dashboard]
Mit einem SELECT kann man alle bisher aufgetretenen readings eines Devices über einen definierten Zeitraum anzeigen lassen.
Der Zeitraum ist mit "1 DAY" definiert, kann aber auch auf z.B. "1 MONTH" oder "2 MONTH" gesetzt werden.
<syntaxhighlight lang="SQL">
SET @device = 'WR_1';
SELECT t1.TIMESTAMP,t1.DEVICE,t1.READING,t1.VALUE
FROM history t1
INNER JOIN
(SELECT max(TIMESTAMP) AS TIMESTAMP,DEVICE,READING
FROM history
WHERE DEVICE = @device AND
TIMESTAMP > NOW() - INTERVAL 1 DAY
GROUP BY READING) x
ON x.TIMESTAMP = t1.TIMESTAMP AND
x.DEVICE = t1.DEVICE AND
x.READING = t1.READING;
</syntaxhighlight>
====Einträge eines DEVICE einem neuen DEVICE zuordnen====
==Diagramme mit Grafana==
In diesem Beispiel würde das alte DEVICE PV_1 dem neuen DEVICE WR_1 zugeordnet werden.
Grafana ermöglicht das direkte Auslesen der SQL Datenbank und kann auch auf einer anderen Plattform betrieben werden. Bei mir befindet es sich in Docker Containern auf dem selben RPI4.
Im Anschluss müssten dann noch READING jeweils einem eventuell neuen READING Namen zu geordnet werden.
=== RAW Definition Hauptverbraucher ===
Sehr wichtig ist '''"TIMESTAMP = TIMESTAMP"''', da hierdurch der alte TIMESTAMP erhalten bleibt.
* [https://svn.fhem.de/fhem/trunk/fhem/contrib/ch.eick/Grafana/Diagramme/Diagramm_Hauptverbraucher_JSON.txt Beispiel Hauptverbraucher]
UPDATE history
* [https://svn.fhem.de/fhem/trunk/fhem/contrib/ch.eick/Grafana/Diagramme/Diagramm_KI_Prognose_JSON.txt Beispiel KI_Prognose]
SET
* [https://svn.fhem.de/fhem/trunk/fhem/contrib/ch.eick/Grafana/Diagramme/Diagramm_Leistungsbezug_JSON.txt Beispiel Leistungsbezug]
TIMESTAMP = TIMESTAMP,
DEVICE = 'PV_1'
WHERE
DEVICE = 'WR_1'
AND TIMESTAMP < '2021-03-23 17:25:15';
</syntaxhighlight>
====Ein altes READING einem neuen READING Namen zuordnen====
== PV Eigenverbrauch-Steuerung ==
Sehr wichtig ist '''"TIMESTAMP = TIMESTAMP"''', da hierdurch der alte TIMESTAMP erhalten bleibt.
'''Hier werde ich auch mal aktualisieren, bei bedarf einfach im Forum fragen.'''
<syntaxhighlight lang="SQL">
=== Beispiel Luft Wärme Pumpe Novelan LAD ===
UPDATE history
Hier mal ein paar Bilder für die Dokumentation der PV-Modus Anschaltung mit einem Shell 1 und das Heizelement, dass man über Lastrelais in drei Stufen Regeln könnte. Die Aktivierung der Zusatzheizung ist über das Luxtronik2 Modul möglich.
LAD_Shelly_Phase_und_Null.jpg|LAD Shelly Phase und Null
DEVICE = 'WR_1'
LAD_Shelly_SWT-Signal.jpg|LAD Shelly SWT-Signal
AND READING = 'Total_DC_PV_Energy_(sumOfAllPVInputs)'
</gallery>
AND TIMESTAMP < '2021-03-23 17:25:15';
==== RAW Definition LWP_PV (DOIF im Perl Modus) ====
</syntaxhighlight>
Hierbei wird das PV-Modus Signal über ein Shelly 1 zur LAD Wärmepumpe übermittelt, was natürlich auch durch ein beliebiges anderes Relais erfolgen kann.
Die setstate Attribute am Ende der RAW Definition sind ebenfalls wichtig, da dort die Default reading Werte für das DOIF gesetzt werden. Diese können dann über die uiTable Definitionen mit Pull Down Menüs geändert werden.
====Alles auf einmal====
Natürlich kann man die vorherigen UPDATE auch zusammenfassen, also DEVICE und READING in einem ändern. Das sollte aber nur machen, wer in SQL entsprechende Kenntnisse hat.
Die Umbenennung des DEVICE zum neuen DEVICE und anschließend die READING Namen ist der praktikabelste Weg.
===Grafiken korrigieren===
====SVG====
In eventuellen SVGs die Device und reading Namen korrigieren
====Grafana====
In Grafana sind die SQL Abfragen ebenfalls zu korrigieren
===Fhem Log===
Das Fhem Log ist nach jedem größeren Änderungsschritt zu sichten, da man hier ziemlich schnell vergessene Devices oder readings erkennen kann.
==Timeing für die PV extra Funktionen==
=== AW Definition PV_Schedule (DOIF)===
Aufgrund der Komplexität wurde die Speichersteuerung aus diesem Device entfernt und im Device PV_1_Speicher_1_ExternControl ausgelagert.
Weitere Neuerungen sind die Bereitstellung eines Schnee Faktors pro String für die Solar_forecast() Funktion, was aber bitte als Versuch anzusehen ist.
Für den Vergleich mit dem Solar_Foracast Modul wird der Forecast zwei mal aufgerufen und einmal davon ins DWD_Forecast_Test geschrieben.
Diese Beispiele können natürlich einfach herausgelöscht werden.
<big>Achtung, es gab eine Umstellung mit diesem Device! Die Bilanz wird nun direkt im WR_1_API Device als stateFormat angezeigt. Bitte holt diese Änderung mit den Informationen im Forum Thread nach.</big>
Die Energie Bilanz soll einen kompakten Überblick über die Produktions- und Verbrauchswerte liefern. Hierbei werden die momentan Werte direkt berechnet, die restlichen Werte werden als Statistiken aus dem Gerät abgefragt.
===Erstellen von zusätzlichen Werten in der Datenbank===
[[Bild:SVG LogDB PV Bilanz.png|mini|900px|rechts|Die Definition diese Diagramms ist weiter unten beschrieben.]]
Hier werden Werte konsolidiert, weil z.B. der Wert PV_total_Month stetig steigt. Am Ende des Monats sind die gesamten Zwischenwerte ohne Aussagekraft und werden dann später mal gelöscht.
===Löschen von nicht mehr benötigten Werten in der Datenbank===
Hier wird endgültig aufgeräumt, alte momentan Werte werden gelöscht, wenn sie nach z.B. drei Monaten keine Relevanz mehr haben. Dafür wurden im vorherigen Abschnitt zusätzliche Werte in der Datenbank erzeugt, die in Diagrammen trotzdem noch einen Trend erkennen lassen.
Wenn eine immer größer werdende Datenbank mit steigenden Antwortzeiten nicht stört, der kann das Aufräumen auch weg lassen. Bei einer späteren Migration führt dies natürlich zu höherem Aufwand und hohen Laufzeiten.
Bei der Leistungsprognose gibt es nun eine gravierende Veränderung. Die bisherige Leistungsprognose durch eine eigene Berechnung, die auf diversen Konfigurationsparametern basiert hat wurde vollständig durch eine KI_Prognose abgelöst. Die bisherige Implementierung wird nicht mehr weiter entwickelt und ist hier nur noch zu Dokumentationszwecke aufgeführt.
==Wetter-/Leistungs-Prognose KI_Prognose==
Erstmalig wurde die [https://forum.fhem.de/index.php?msg=1268412 KI_Prognose hier im Forumsthread in vier Teil Posts] beschrieben. Im weiteren Thread sind auch noch Informationen dazu zu finden.
===KI Prognose - Grundgedanke===
Nun ist der Ansatz der KI eingezogen und meine Ergebnisse, von bisherigen Tests, sehen schon ziemlich gut aus.
Der Grundgedanke ist, dass die Prognose keinerlei technischen Informationen über den Aufbau der PV-Anlage benötigt. Einzig allen der Ertrag der Anlage wird dabei in Bezug zu den Wetterdaten des jeweiligen Standortes gesetz, wobei die KI daraus Rückschlüsse zieht, wie bei ähnlichen Bedingungen der ertrag werden könnte. Je mehr vergleichbare Daten dazu zur Verfügung stehen, umso besser wird die Prognose.
In der momentan implementierten Prognose besteht darüber hinaus ein Problem, das man die momentan erzeugte Leistung eigentlich mit der zu erwartenden Energieprognose vergleicht.
Beim neuen Ansatz wird nun versucht das mit zu korrigieren, was auch im Diagramm durch die Stufen Darstellung verdeutlicht wird.
Die KI Prognose arbeitet nun über den Yield, den der Plenticore jede Stunde aktualisiert. Bei diesem Yield ist nun jedoch ein weiteres Problem, da der hybrid Wechselrichter natürlich auf der AC Seite den Yield angibt und somit das Laden des Speichers nicht aktuell mit zählt. Die Speicher Entladung wird später dann wiederum mit gerechnet, was die AC Yield Kurve dann sehr merkwürdig aussehen lässt. An dieser Problematik wurde auch bereits gearbeitet und das wird dann später nochmal erwähnt.
Im Diagramm sieht man nun in blau den korrigierten Yield unter Berücksichtigung des Speichers und in diesem Beispiel Fall für eien gesamten Schwarm (ich habe zwei WR). Jede Stufe im Diagramm ist dann nun der Ertrag (Yield) der entsprechenden Stunde in kWh.
Zur Orientierung sieht man in gelb die AC Leistung in kW, gezeichnet aus den minütlichen Messwerten.
Die rosa Stufen sind dann nun endlich die Ertrags Prognose Werte aus der KI in kWh.
===KI Prognose Teil 1 - DWD und Astro Daten sammeln===
Solltet Ihr später mit in diese Richtung gehen wollen, so macht es Sinn [b]schon jetzt die Wetterdaten für Euren Standort zu sammeln[/b], da diese die Grundlage bilden und im Anschluss mit dem korrigierten Ertrag in Verbindung gebracht werden. Alle im comment angegebenen DWD Werte werden später von der KI ausgewertet und müssen somit in der DbLog vorliegen. Je mehr DWD Daten von den letzten Jahren vorliegen, umso besser kann die KI Rückschlüsse ziehen. Sollten diese nicht da sein, so lernt das ganze langsam dazu.
Da die KI Prognose ja auch die Astro Daten für den Sonnenstand benötigt und dieser im Astro Device nicht als fc[0|1] vorliegt habe ich das Astro Device etwas modifiziert. In den userreadings werden dort die fc[0|1] Sonnenstände jetzt abgefragt und als readings eingetragen. Dies geschieht sobald es einen Event von ObsDate gibt, der einmal täglich kommen sollte. Somit beachtet auch die Änderung bei event-on-update-reading und beim DbLogInclude.
In diesem Teil geht es darum die Daten aus der FHEM History so aufzubereiten, dass sie für die KI Prognose verwendbar wird. Das Daten Model der FHEM History ist in der Form nicht für diese Verarbeitung brauchbar und wird deshalb in eine neu Tabelle überführt. Bei der Gelegenheit wird einiges noch aufbereitet und insbesondere der yield des Plenticore mit Speicher korrigiert.
====RAW Definition dwd_load() MySQL Procedure====
Hier kommt nun die MySQL Procedure, die in der Datanbank hinterlegt wird. Dazu verwende ich z.B. die MySQL Workbench, wo dann die Procedure unter "Stored Precedures" auftaucht. Dies ermöglicht, dass man im FHEM DbRep Device nur diese eine Procedure aufrufen kann und nicht jedes einzelne SELECT zur Datenbank in einer separaten Session übermittelt werden muss.
<syntaxhighlight lang="SQL">
CREATE DEFINER=`fhemuser`@`%` PROCEDURE `dwd_load`(IN var_date DATE, IN display char(10))
3. Die Ermittlung einer stunden basierten Tabelle ist etwas komplexer und bedarf diverser SELECT mit JOIN (Im MySQL gibt es kein full JOIN)
7. Der letzte Schritt ist dann die Möglichkeit einer Rückmeldung aus der MySQL Procedure ins FHEM
8. Über den Parameter show/none wird der Prozedure die Art der Rückmeldung mitgeteilt
1. none wäre der Default und gibt als Ergebnis das aktuelle Datum der Datenbank zurück
2. show würde den Inhalt der dwnfull Tabelle an FHEM zurück liefern, was jedoch einige hundert Zeilen sein werden
9. Die Procedure selectiert nur die entscheidenden Daten für die jeweilige KI Prognose, um das Datenvolumen gering zu halten,
denn es macht ja keinen Sinn, die Winter mit den Sommer Daten zu vergleichen
10. Hierbei werden deshalb folgende Zeiträume jeweils selectiert
1. Die letzten 30 Tage ab dem aktuellen Datum
2. Vom letzten Jahr 30 Tage vor dem Datum
3. Vom letzten Jahr 30 Tage nach dem Datum
4. Vom vorletzten Jahr 30 Tage vor dem Datum
5. Vom vorletzten Jahr 30 Tage nach dem Datum
6. Die Forecast Daten für den nächsten Tag,
an dieser Stelle wäre es natürlich auch denkbar noch weiter in die Zukunft zu gehen,
was mir jedoch zu spekulativ ist und nach meiner Meinung bisher für keine Entscheidung von Wichtigkeit wäre.
11. Die Laufzeit dieser Procedure beträgt auf meinem RPI4 in einem Oracle MySQL Docker Container ca. 50-70 Sekunden,
deshalb musste ich bei mir den Timeout der MySQL Workbench für eine Session von 60 Sekunden auf z.B 90 Sekunden erhöhen
12. In einem Interface Eurer Wahl zur Datenbank könnt Ihr die Procedure zum Testen dann aufrufen und das Ergebnis testen.
</pre>
====dwd_load() Test in MySQL aufrufen====
<syntaxhighlight lang="SQL">
call dwd_load(curdate(),'none');
select * from dwdfull
-- WHERE TIMESTAMP > curdate()
order by TIMESTAMP desc
LIMIT 1000;
</syntaxhighlight>
Sollte nun der Test der Procedure eine gefüllte Tabelle anzeigen, so kann die Integration ins FHEM erfolgen. Hierzu wird dann ein DbRep Device angelegt, dass später zyklisch jede Stunde ausgeführt wird.
Achtung, bei diesem Device kommt im weiteren Fortschritt noch ein weiteres Attribut zum Aufruf des Python KI Prognose Skriptes hinzu. Im Kommentar wird dies bereits im Syntax erwähnt.
<syntaxhighlight lang="Perl">
defmod LogDBRep_PV_KI_Prognose DbRep LogDB
attr LogDBRep_PV_KI_Prognose DbLogExclude .*
attr LogDBRep_PV_KI_Prognose allowDeletion 0
attr LogDBRep_PV_KI_Prognose comment Version 2023.02.23 12:00\
\
Hier wird die Vorbereitung für die KI PV-Leistungsprognose durchgeführt\
Auch hier sollte nun getestet werden, indem man beim set das sqlCmd ausführt. Der MySQL Procedur Aufruf ist ebenfalls im Kommentar zu finden.
Als Ergebnis sollte soetwas zurück kommen. Nachdem das erschienen ist kann man den obigen Test mit dem SELECT der dwdfull Tabelle nochmals wiederholen.
<syntaxhighlight lang="Perl">
SqlResultRow_1 NOW()
SqlResultRow_2 2023-03-17 11:01:03
sqlCmd call dwd_load(curdate(),'none');
sqlResultNumRows 1
</syntaxhighlight>
==Wetter-/Leistungs-Prognose (nicht mehr aktuell)==
Dies ist ein Thema, dass nicht wirklich gut zu fassen ist und ist eher etwas für Enthusiasten :-), wer schon mal mit Sonne, Wolken und Regen gerechnet hat versteht was ich meine. Dieser Ansatz ist nicht wissenschaftlicher Art und hat auch keinen Anspruch mathematischer Perfektion. Nach reinem Gefühl und mit aus dem Fenster schauen kommt jedoch ein respektables Ergebnis dabei heraus. Viel Vergnügen und Spaß beim mitbasteln ;-)
[[Bild:Plenticore_Forecast_Tagesanfang.png|mini|900px|rechts|Wenn der Tag begonnen hat ist die Prognose vom Vortag bereits im Diagramm. Der Wert Calculation in schwarz ist die aktuelle Korrektur.]]
====Wetter Forecast Grundlagen (nicht mehr aktuell)====
<pre>
1.) Astro Device mit dem Namen Astro und der
Konfiguration für den Standort (in der fhem.cfg eingetragen)
2.) DbLog / DbRep
2.1) DbLog Device, um die Daten in die Datenbank zu schreiben
2.2) DbRep Device um auch wieder alte Forecasts zu löschen
3.) Es kann auch ohne DbLog / DbRep gearbeitet werden
4.) Die Solar_* Funktionen in der 99_myUtils
5.) Das DWD Device nit dem Namen DWD_Forecast
6.) Das Wetter Device für wunderground wird nicht für den Forecast benötigt
</pre>
===Deutscher Wetter Dienst (DWD) (nicht mehr aktuell)===
Der DWD liefert über Mosmix kostenlos, stunden aktuelle Prognosedaten woraus für diese Anwendung die Werte Rad1h und TTT bezogen werden. In der Funktion Solar_forecast erfolgt noch eine Verschiebung um eine Stunde und die Umrechnung von Rad1h in Watt/m² .
'''Achtung: nicht alle Stationen liefern auch die Rad1h Daten, was deshalb bitte anhand der readings kontrolliert werden müsste.'''
[[DWD_OpenData|FHEM DWD_OpenData Modul]]
====RAW Definition DWD_Forecast (nicht mehr aktuell)====
Erfordert ggf.
<syntaxhighlight lang="Perl">
sudo apt-get install libxml-libxml-perl
</syntaxhighlight>
Es wurden einige extra Werte vom DWD abgefragt, die für das Solar_Forecast Modul verwendet werden. Dieses Modul ist noch in der experimental Phase (2021.02.23) und liefert noch nicht gleichwertige Ergebnisse. Ein Test zum vergleichen kann aber nicht schaden.
Achtung, diese Funktion ist noch nicht vollständig ausprogrammiert. Es wurden bereits Übergabeparameter integriert, um z.B. andere Wetterdienste zu berücksichtigen.
Um diese Funktion zu nutzen, muss ein Dummy WR_1_config vorhanden sein, in dem unter anderem die Modul und Anlagen Ausrichtung konfiguriert wird.
Rückfragen gerne im Forum.
<pre>
Letzte Neuerungen:
- IM WR_1_config kann man verbose auf >3 setzen und bekommt dann Log Meldungen
- Es wird eine Autokorrektur unterstützt. Aktivierung durch "setreading WR_1_config forecast_factor_autocorrection 1"
Für die Datenbank Anbindung wird ein DbRep Device LogDBRep_PV_Forecast_SQL verwendet. Die RAW definition kommt gleich im Anschluss.
- Das SQL für die Berechnung des Faktors der Autokorrektur Verwendet Konfigurationsvariablen aus den DbRep Device.
- Bei der Autokorrektur wird auch eine Bedeckung von Schnee (Strom im String < 1A) berücksichtigt. (das ist noch in der Entwicklung)
Dieser Faktor wird im PV_Schedule Device erzeugt und dann im "WR_1_config module_*_covered" für jeden String eingetragen.
Das muss jeder individuell für seine Anlage anpassen!
- Für die 70% Regelung wird nun auch ein Middayhigh Trigger ermittelt und die jeweilige Start/Stop Zeit. Dies steht dann im WR_1 Device bei den Solar_* readings
- Es besteht auch die Möglichkeit die Solar_forecast() Funktion ohne Datenbank zu verwenden, dann ist bei den Parametern "none" zu übergeben und
es muss auch die Autokorrektur abgeschaltet sein.
- Anstelle des Wechselrichter Devices kann nun auch ein beliebiges anderes Device angegeben werden, in das dann der Forecast geschrieben wird.
Auch hier kann dann keine Autokorrektur verwendet werden.
# Mit "attr global verbose 3" erscheinen Logmeldungen
#
my $logdb = $_[0] ; # Mit dieser Datenbank wird gearbeitet
my $logdbrep = $_[1] ; # Das wird zur Kommunikation mit der LogDB verwendet und muss entsprechend konfiguriert sein
my $logdevice = " " ; # Das ist der Wechselrichter, oder ein anderes Device, in das die Prognose geschrieben wird
$logdevice = $_[2] ;
# Hier könnte man noch andere Wetterdienste berücksichtigen bzw den Device Namen ändern
my $wetter = $_[4] ; if ($wetter ne "DWD_Forecast") {return("$wetter not supported")} ;
my $fc = $_[5] ; # Wieviel Tage in die Zukunft soll es gehen? 0,1,2
my $reading = $_[3].$fc ; # Der reading Name wird um 0 oder 1 verlängert
# Welcher Verbose Level ist gesetzt?
my $verbose = AttrVal($logdevice."_config","verbose",0) ;
# Gibt es einen festen Korrekturfaktor für jede Stunde?
my $Solar_Correction_Faktor = ReadingsVal($logdevice."_config","forecast_factor",1) ;
# Soll eine Autokorrektur gemacht werden? 0 = Nein 1 = Ja
my $autocorrection = ReadingsVal($logdevice."_config","forecast_factor_autocorrection",0) ;
# Beim DWD wird der Wert für die Stunde erst am Ende der Stunde eingetragen
my $timeshift = 1; # Verschiebt die Prognose um eine Stunde
# Hier werden die Variablen vorbelegt
my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time); $year += 1900; $mon += 1;
my ($Solar_Cloud,$Solar_Rain,$Solar_Temp,$Solar_SolarRadiation,$logentry1h,$logentry4h,$logentryrest,$logentry,$i) = (0) x 9 ;
my ($cloudk,$raink,$tempk,$cloudk_base,$raink_base,$tempk_base) = (0) x 6 ;
my ($module_covered,$Solar_Correction_Faktor_auto,$Solar_Correction_Cloud,$Solar_Correction_Rain,$Solar_Correction_Temp,$Solar_Plain) = (1) x 6 ;
my (@Solar_,@module_count) = (0,0,0) ;
# Initialisieren des Basis TIMESTAMP für den Forecast
my $timestring = time_str2num($year."-".$mon."-".$mday." 06:00:00") ;
my $timestamp = POSIX::strftime("%Y-%m-%d %H:00:00",localtime($timestring+$fc*24*60*60)) ;
if ( $logdbrep ne "none" ) {
# Bei Forecast zuerst die bisherigen Einträge in der Datenbank für den Tag löschen
CommandGet(undef, $logdbrep." sqlCmdBlocking DELETE FROM history WHERE DEVICE='".$logdevice."' AND READING='".$reading."' AND TIMESTAMP>='".$timestamp."'") ;
};
# Setzen der Tageszähler und Merker
$logentry = 0 ; # Summiert den Solar_Calculation Wert für den ganzen Tag
$logentry4h = 0 ; # Summierung für die nächsten vier Stunden
$logentryrest = 0 ; # Summierung für den Rest des Tages
my $middayhigh = 0 ; # Ein Merker, ob das Tagesmaximum überschritten wird
my $middayhigh_start = "00:00";
my $middayhigh_stop = "00:00";
my $middayhigh_tmp = 0;
my $middayhigh_start_tmp = 0;
my $middayhigh_stop_tmp = 0;
my $Inverter_Max_Power = ReadingsVal($logdevice."_Speicher_1_ExternControl","SpeicherMidday_Inverter_Max_Power","unused"); # Überschreiben des middayhigh
if ($Inverter_Max_Power eq "unused") {
$Inverter_Max_Power = ReadingsVal($logdevice,"Inverter_Max_Power",0) +500 ; # Hier wird ein Durchschnittsverbrauch des Hauses aufaddiert
$Solar_Correction_Faktor_auto = CommandGet(undef, $logdbrep." sqlCmdBlocking SELECT VALUE FROM history WHERE DEVICE='".$logdevice."' AND READING='Solar_Correction_Faktor_auto' AND TIMESTAMP='".sprintf("%4d-%02d-%02d %02d:00:00",$year,$mon,$mday,$i)."';") ;
Achtung, bitte hier beachten, ob bereits eine andere DbLog verwendet wird.
<syntaxhighlight lang="Perl">
defmod LogDB DbLog ./db.conf .*:.*
attr LogDB DbLogExclude .*
attr LogDB DbLogSelectionMode Exclude/Include
attr LogDB DbLogType History
attr LogDB asyncMode 1
attr LogDB bulkInsert 1
attr LogDB disable 0
attr LogDB room System
attr LogDB showproctime 1
attr LogDB verbose 0
</syntaxhighlight>
====RAW Definition LogDBRep_PV_Forecast_SQL (nicht mehr aktuell)====
Dieses Device war vormals das LogDBRep_PV_Forecast_SQL , es wird jedoch nun mehrfach verwendet und wurde deshalb umbenannt. Das LogDBRep_PV_Forecast_SQL kann gelöscht werden.
Achtung, bitte hier beachten, ob bereits eine andere DbLog verwendet wird.
Für die Solar_forecast() Funktion wurden hier SQL Variablen Definiert, die für die Autokorrektur verwendet werden:
- @days ist die Anzahl der Tage, über die ein stündlicher, durchschnitts Korrektur Faktor berechnet wird.
- @corr ermöglicht es diesen Faktor nochmals zu verändern <1 dämpft, >1 verstärkt
- @device ist der Plenticore Wechselrichter
- @reading1 ist die reale DC Leistung ohne die Batterie, hier wird '''SW_Total_DC_P_sumOfAllPVInputs''' für die Schwarm Implementierung verwendet.
- @reading2 wird der Basisname der readings im WR_1 Device
- @readingname wird der reading Name des Korrekturfaktors.
Bitte beachtet, dass die Namen auch in anderen Devices eingetragen sind, wenn Ihr diese verändern wollt.
Durch die Erweiterung zum Schwarm mit mehreren AC-Quellen wurde die Variable @reading1 verändert.
===Forecast Basiseinstellung (nicht mehr aktuell)===
Erste Werte wurden bereits mit dem Gerät WR_1_config von einer Ost/Süd/West Anlage mitgeliefert (setstate). Es werden bis zu 5 Strings unterstützt.
Grundlegend muss man als erstes jede Ausrichtung von Modulen definieren.
Steht *_count auf 0 so wird diese Ausrichtung nicht verwendet. Die Nennleistung ergibt sich aus der Anzahl der Module und der Nennleistung pro Modul.
Der Name ist frei wählbar und könnte auch "Garage" oder "Schuppen" lauten.
Die Nennleistung pro Modul wird mit dem reading *_power eingetragen.
Mit *.plain wird der Winkel der Module, bzw die Dachneigung eingetragen.
Das reading *_direction gibt die Orientierung an, wobei -90 exact Ost, 0 Richtung Süden und +90 Richtung Westen bedeutet. Diese Winkel können sehr gut auf der WEB Seite [https://www.sonnenverlauf.de/#/50.1121,8.6834,18/2020.09.06/15:41/1/3 Sonnenverlauf.de] ermittelt werden. Dort kann man bis auf sein Anlage hereinzoomen und die Orientierung entnehmen.
Sind diese Werte für alle Modulgruppen eingetragen, so wird eine Summe der Einzelleistungen ermittelt und im Gerät WR_1 als reading "Solar_Calculation" eingetragen.
Die weiteren readings "Solar_*" geben noch zusätzliche Werte an.
<syntaxhighlight lang="Perl">
module_1_count 13
module_1_direction -90
module_1_name East
module_1_plain 40
module_1_power 310
</syntaxhighlight>
===Berücksichtigung von Temperatur, Bewölkung und Regen (nicht mehr aktuell)===
Diese Wetterfaktoren haben einen starken Einfluss auf die Leistung, die durch die Module erzeugt wird. Ab hier wird es etwa wie Glaskugellesen, jedoch ist das Ergebnis wirklich sehenswert, wenn man sich die Mühe gemacht hat etwas zu experimentieren.
Für alle Faktoren wurde eine Art Heizungskurve verwendet, da keine lineare Abhängigkeit zu erkennen war. Die Implementierung erhebt keinen Wissenschaftlichen Anspruch!
Durch die Autokorrektur ist nun auch ein Schnee Faktor dazu gekommen. Dieser wird im PV_Schedule Device "berechnet" :-) und und in das PV_1_config geschrieben. Bei aktivierter Autokorrektur wird dieser dann berücksichtigt.
=====Temperatur (nicht mehr aktuell)=====
Je heißer die Module werden, je schlechter wird die Leistungsausbeute. Hierzu findet man in den Modulunterlagen einen Wert, der dies wiederspiegelt.
<syntaxhighlight lang="Perl">
tempk dies ist der Faktor aus den Unterlagen ( bei meinen Modulen 0.39 ) und wird dann mit 39 eingetragen
tempk_base Dieser Wert hebt die "Heizungskurve" an und wird mit 25 angegeben. Das bedeutet, bei einer Temperatur von 25° wird die Nennleistung erreicht.
</syntaxhighlight>
=====Wolken und Regen (nicht mehr aktuell)=====
Aus den DWD_Forecast werden Prozent Werte geliefert, die experimentell in der "Heizungskurve" zu einem Faktor berechnet werden, der dann die zu erwartende Leistung reduziert.
Um das möglichst gut hinzubekommen sollte man zuerst nur einen Wert verwenden, was man durch raink oder cloudk auf 0 setzen erreichen kann. Eventuell passen ja auch die bereits
mitgelieferten Werte. Sollte das nicht passen, muss man sich leider doch etwas mit "Heizungskurven" beschäftigen. Es kann die Steilheit der Kurve (cloudk) beeinflusst werden,
oder auch eine Parallelverschiebung (cloudk_base) stattfinden. Wenn die Wolken einen starken Einfluss haben sollen wäre cloudk z.B. zu verändern.
===wunderground===
Dieser Dienst wird nicht für die Prognose genutzt, jedoch kann man dort private Wetterstationen in seinem näheren Umfeld finden, die die Sonneneinstrahlung und den UV Index messen. Das kann dann als aktueller Wert in den Diagrammen angezeigt werden und lässt aktuelle Beschattung durch Wolken erkennen. Hier kann man dann auch mehrere Stationen definieren und eventuell mit einem Durchschnitt arbeiten, wenn nicht gerade der Nachbar eine Station hat.
Für diese Abfrage ist keine Registrierung notwendig und man muss auch nicht selber mit einer Station Daten liefern.
Aber bitte, die Abfrage nicht in einem zu kurzen Abstand, also mit hoher Frequenz stellen! Hier wird alle 15 Minuten (900 Sekunden) abgefragt.
attr wetter_<Wohnort> stateFormat T: temperature °C | F: humidity % | W: windSpeed km/h | D: pressure hPa | U: solarUV | R: solarRadiation W/m²
attr wetter_<Wohnort> timeout 5
</syntaxhighlight>
==Diagramme mit Grafana==
Grafana kann z.B. mit docker auf dem selben oder auch einem anderen System installiert werden. Es ermöglicht die Darstellung von Diagrammen und Dashboards durch die direkte Abfrage aus einer Datenbank.
===Beispiel Diagramme===
[[Datei:Leistung und Hauptverbraucher.png|mini|600px|rechts|]]
[[Datei:Forecast.png|mini|600px|rechts|]]
<pre>
Die verwendete Datenbank ist im Grafana als "FHEM MySQL" am besten vorher zu konfigurieren.
Achtung, dieses Dashboard verwendet die Schwarm readings bei den MySQL SELECT!
Eine Anpassung wäre denkbar, wenn man im JSON File "SW_" global entfernt.
Auch die Hauptverbraucher sind im Diagramm anzupassen, da sie bei mir durch eigene Zähler erfasst werden. Sollten bei Euch keine Zähler vorhanden sein, so müsstet Ihr den jeweiligen Verbraucher im Diagramm löschen.
"rawSql": "SELECT\n TIMESTAMP AS \"time\",\n VALUE AS 'SW_Total_DC_P'\nFROM history\nWHERE\n $__timeFilter(TIMESTAMP) AND\n DEVICE = 'WR_1' AND\n READING = 'SW_Total_DC_P'\nORDER BY TIMESTAMP",
"refId": "SW_Total_DC_P",
"select": [
[
{
"params": [
"VALUE"
],
"type": "column"
},
{
"params": [
"SW_Total_DC_P'"
],
"type": "alias"
}
]
],
"table": "history",
"timeColumn": "TIMESTAMP",
"timeColumnType": "timestamp",
"where": [
{
"name": "$__timeFilter",
"params": [],
"type": "macro"
},
{
"datatype": "varchar",
"name": "",
"params": [
"DEVICE",
"=",
"'WR_1'"
],
"type": "expression"
},
{
"datatype": "varchar",
"name": "",
"params": [
"READING",
"=",
"'SW_Total_DC_P'"
],
"type": "expression"
}
]
},
{
"datasource": "FHEM MySQL",
"format": "table",
"group": [],
"metricColumn": "none",
"rawQuery": true,
"rawSql": "SELECT\n $__timeGroupAlias(TIMESTAMP,$__interval,previous),\n avg(value) AS \"SW_Home_own_consumption_from_grid\"\nFROM history\nWHERE\n $__timeFilter(TIMESTAMP) AND\n DEVICE = 'WR_1' AND\n READING = 'SW_Home_own_consumption_from_grid'\nGROUP BY 1\nORDER BY $__timeGroup(TIMESTAMP,$__interval,previous)",
"refId": "SW_Home_own_consumption_from_grid",
"select": [
[
{
"params": [
"value"
],
"type": "column"
}
]
],
"table": "history",
"timeColumn": "TIMESTAMP",
"timeColumnType": "timestamp",
"where": [
{
"name": "$__timeFilter",
"params": [],
"type": "macro"
},
{
"datatype": "varchar",
"name": "",
"params": [
"DEVICE",
"=",
"'WR_1'"
],
"type": "expression"
}
]
},
{
"datasource": "FHEM MySQL",
"format": "table",
"group": [],
"metricColumn": "none",
"rawQuery": true,
"rawSql": "SELECT\n $__timeGroupAlias(TIMESTAMP,$__interval,previous),\n avg(value) AS \"SW_Home_own_consumption_from_PV\"\nFROM history\nWHERE\n $__timeFilter(TIMESTAMP) AND\n DEVICE = 'WR_1' AND\n READING = 'SW_Home_own_consumption_from_PV'\nGROUP BY 1\nORDER BY $__timeGroup(TIMESTAMP,$__interval,previous)",
"refId": "SW_Home_own_consumption_from_PV",
"select": [
[
{
"params": [
"value"
],
"type": "column"
}
]
],
"table": "history",
"timeColumn": "TIMESTAMP",
"timeColumnType": "timestamp",
"where": [
{
"name": "$__timeFilter",
"params": [],
"type": "macro"
},
{
"datatype": "varchar",
"name": "",
"params": [
"DEVICE",
"=",
"'WR_1'"
],
"type": "expression"
}
]
},
{
"datasource": "FHEM MySQL",
"format": "table",
"group": [],
"metricColumn": "none",
"rawQuery": true,
"rawSql": "SELECT\n $__timeGroupAlias(TIMESTAMP,$__interval,previous),\n avg(value) AS \"SW_Home_own_consumption_from_Battery\"\nFROM history\nWHERE\n $__timeFilter(TIMESTAMP) AND\n DEVICE = 'WR_1' AND\n READING = 'SW_Home_own_consumption_from_Battery'\nGROUP BY 1\nORDER BY $__timeGroup(TIMESTAMP,$__interval,previous)",
"refId": "SW_Home_own_consumption_from_Battery",
"select": [
[
{
"params": [
"value"
],
"type": "column"
}
]
],
"table": "history",
"timeColumn": "TIMESTAMP",
"timeColumnType": "timestamp",
"where": [
{
"name": "$__timeFilter",
"params": [],
"type": "macro"
},
{
"datatype": "varchar",
"name": "",
"params": [
"DEVICE",
"=",
"'WR_1'"
],
"type": "expression"
}
]
},
{
"datasource": "FHEM MySQL",
"format": "table",
"group": [],
"metricColumn": "none",
"rawQuery": true,
"rawSql": "SELECT\n $__timeGroupAlias(TIMESTAMP,$__interval,previous),\n avg(value) AS \"Actual_Battery_charge_usable_P\"\nFROM history\nWHERE\n $__timeFilter(TIMESTAMP) AND\n DEVICE = 'WR_1' AND\n READING = 'Actual_Battery_charge_usable_P'\nGROUP BY 1\nORDER BY $__timeGroup(TIMESTAMP,$__interval,previous)",
"refId": "Actual_Battery_charge_usable_P",
"select": [
[
{
"params": [
"value"
],
"type": "column"
}
]
],
"table": "history",
"timeColumn": "TIMESTAMP",
"timeColumnType": "timestamp",
"where": [
{
"name": "$__timeFilter",
"params": [],
"type": "macro"
},
{
"datatype": "varchar",
"name": "",
"params": [
"DEVICE",
"=",
"'WR_1'"
],
"type": "expression"
}
]
},
{
"datasource": "FHEM MySQL",
"format": "table",
"group": [],
"metricColumn": "none",
"rawQuery": true,
"rawSql": "SELECT\n TIMESTAMP AS \"time\",\n VALUE AS \"SW_Total_DC_P_Max\"\nFROM history\nWHERE\n $__timeFilter(TIMESTAMP) AND\n DEVICE = 'WR_1' AND\n READING = 'SW_Total_DC_P_Max'\nORDER BY TIMESTAMP",
"refId": "SW_Total_DC_P_Max",
"select": [
[
{
"params": [
"value"
],
"type": "column"
}
]
],
"table": "history",
"timeColumn": "TIMESTAMP",
"timeColumnType": "timestamp",
"where": [
{
"name": "$__timeFilter",
"params": [],
"type": "macro"
},
{
"datatype": "varchar",
"name": "",
"params": [
"DEVICE",
"=",
"'WR_1'"
],
"type": "expression"
}
]
},
{
"datasource": "FHEM MySQL",
"format": "table",
"group": [],
"metricColumn": "none",
"rawQuery": true,
"rawSql": "SELECT\n $__timeGroupAlias(TIMESTAMP,$__interval,previous),\n avg(value) AS \"SW_Total_AC_Active_P\"\nFROM history\nWHERE\n $__timeFilter(TIMESTAMP) AND\n DEVICE = 'WR_1' AND\n READING = 'SW_Total_AC_Active_P'\nGROUP BY 1\nORDER BY $__timeGroup(TIMESTAMP,$__interval,previous)",
"refId": "A",
"select": [
[
{
"params": [
"value"
],
"type": "column"
}
]
],
"table": "history",
"timeColumn": "TIMESTAMP",
"timeColumnType": "timestamp",
"where": [
{
"name": "$__timeFilter",
"params": [],
"type": "macro"
},
{
"datatype": "varchar",
"name": "",
"params": [
"DEVICE",
"=",
"'WR_1'"
],
"type": "expression"
}
]
}
],
"thresholds": [],
"timeFrom": null,
"timeRegions": [],
"timeShift": null,
"title": "Leistungsbezug",
"tooltip": {
"shared": true,
"sort": 0,
"value_type": "individual"
},
"type": "graph",
"xaxis": {
"buckets": null,
"mode": "time",
"name": null,
"show": true,
"values": []
},
"yaxes": [
{
"decimals": 0,
"format": "short",
"label": "Watt",
"logBase": 1,
"max": "14000",
"min": "0",
"show": true
},
{
"format": "short",
"label": null,
"logBase": 1,
"max": null,
"min": null,
"show": true
}
],
"yaxis": {
"align": false,
"alignLevel": null
}
},
{
"aliasColors": {
"Heizung": "dark-red",
"Heizung value": "dark-red",
"Pool": "dark-yellow",
"SW_Total_DC_P_Max": "dark-blue",
"SW_Total_DC_P_Max value": "dark-blue",
"Shaun": "dark-green",
"Shaun value": "dark-green",
"Waschmaschine": "light-red"
},
"bars": false,
"dashLength": 10,
"dashes": false,
"datasource": null,
"decimals": null,
"fieldConfig": {
"defaults": {
"links": []
},
"overrides": []
},
"fill": 1,
"fillGradient": 0,
"gridPos": {
"h": 12,
"w": 23,
"x": 0,
"y": 12
},
"hiddenSeries": false,
"id": 3,
"legend": {
"alignAsTable": true,
"avg": false,
"current": true,
"hideEmpty": false,
"hideZero": false,
"max": false,
"min": false,
"rightSide": true,
"show": true,
"sideWidth": 250,
"sort": "current",
"sortDesc": true,
"total": false,
"values": true
},
"lines": true,
"linewidth": 1,
"nullPointMode": "connected",
"options": {
"alertThreshold": true
},
"percentage": false,
"pluginVersion": "7.5.5",
"pointradius": 2,
"points": false,
"renderer": "flot",
"seriesOverrides": [
{
"alias": "SW_Total_DC_P_Max",
"fill": 0
},
{
"alias": "Pool",
"fill": 3,
"stack": true
},
{
"alias": "Heizung",
"fill": 3,
"stack": true
},
{
"alias": "Waschmaschine",
"fill": 3,
"stack": true,
"steppedLine": true
}
],
"spaceLength": 10,
"stack": false,
"steppedLine": false,
"targets": [
{
"datasource": "FHEM MySQL",
"format": "table",
"group": [
{
"params": [
"$__interval",
"previous"
],
"type": "time"
}
],
"metricColumn": "none",
"rawQuery": true,
"rawSql": "SELECT\n $__timeGroupAlias(TIMESTAMP,$__interval,previous),\n avg(value) AS \"SW_Total_DC_P_Max\"\nFROM history\nWHERE\n $__timeFilter(TIMESTAMP) AND\n DEVICE = 'WR_1' AND\n READING = 'SW_Total_DC_P_Max'\nGROUP BY 1\nORDER BY $__timeGroup(TIMESTAMP,$__interval,previous)",
"refId": "SW_Total_DC_P_Max",
"select": [
[
{
"params": [
"value"
],
"type": "column"
},
{
"params": [
"avg"
],
"type": "aggregate"
},
{
"params": [
"value"
],
"type": "alias"
}
]
],
"table": "history",
"timeColumn": "TIMESTAMP",
"timeColumnType": "timestamp",
"where": [
{
"name": "$__timeFilter",
"params": [],
"type": "macro"
},
{
"datatype": "varchar",
"name": "",
"params": [
"DEVICE",
"=",
"'WR_1'"
],
"type": "expression"
},
{
"datatype": "varchar",
"name": "",
"params": [
"READING",
"=",
"'SW_Total_DC_P_Max'"
],
"type": "expression"
}
]
},
{
"datasource": "FHEM MySQL",
"format": "table",
"group": [
{
"params": [
"$__interval",
"previous"
],
"type": "time"
}
],
"metricColumn": "none",
"rawQuery": true,
"rawSql": "SELECT\n $__timeGroupAlias(TIMESTAMP,$__interval,previous),\n abs(avg(value)) AS \"Heizung\"\nFROM history\nWHERE\n $__timeFilter(TIMESTAMP) AND\n DEVICE = 'StromZaehler_Heizung' AND\n READING = 'SMAEM1901401955_Saldo_Wirkleistung'\nGROUP BY 1\nORDER BY $__timeGroup(TIMESTAMP,$__interval,previous)",
"refId": "Heizung",
"select": [
[
{
"params": [
"value"
],
"type": "column"
},
{
"params": [
"avg"
],
"type": "aggregate"
},
{
"params": [
"value"
],
"type": "alias"
}
]
],
"table": "history",
"timeColumn": "TIMESTAMP",
"timeColumnType": "timestamp",
"where": [
{
"name": "$__timeFilter",
"params": [],
"type": "macro"
},
{
"datatype": "varchar",
"name": "",
"params": [
"DEVICE",
"=",
"'StromZaehler_Heizung'"
],
"type": "expression"
},
{
"datatype": "varchar",
"name": "",
"params": [
"READING",
"=",
"'SMAEM1901401955_Saldo_Wirkleistung'"
],
"type": "expression"
}
]
},
{
"datasource": "FHEM MySQL",
"format": "table",
"group": [
{
"params": [
"$__interval",
"previous"
],
"type": "time"
}
],
"metricColumn": "none",
"rawQuery": true,
"rawSql": "SELECT\n $__timeGroupAlias(TIMESTAMP,$__interval,previous),\n avg(value) AS \"Pool\"\nFROM history\nWHERE\n $__timeFilter(TIMESTAMP) AND\n DEVICE = 'shelly02' AND\n READING = 'Power_0'\nGROUP BY 1\nORDER BY $__timeGroup(TIMESTAMP,$__interval,previous)",
"refId": "Pool",
"select": [
[
{
"params": [
"value"
],
"type": "column"
},
{
"params": [
"avg"
],
"type": "aggregate"
},
{
"params": [
"value"
],
"type": "alias"
}
]
],
"table": "history",
"timeColumn": "TIMESTAMP",
"timeColumnType": "timestamp",
"where": [
{
"name": "$__timeFilter",
"params": [],
"type": "macro"
},
{
"datatype": "varchar",
"name": "",
"params": [
"DEVICE",
"=",
"'shelly02'"
],
"type": "expression"
},
{
"datatype": "varchar",
"name": "",
"params": [
"READING",
"=",
"'Power_0'"
],
"type": "expression"
}
]
},
{
"datasource": "FHEM MySQL",
"format": "table",
"group": [
{
"params": [
"$__interval",
"previous"
],
"type": "time"
}
],
"metricColumn": "none",
"rawQuery": true,
"rawSql": "SELECT\n $__timeGroupAlias(TIMESTAMP,$__interval,previous),\n avg(value) AS \"Waschmaschine\"\nFROM history\nWHERE\n $__timeFilter(TIMESTAMP) AND\n DEVICE = 'shelly03' AND\n READING = 'power'\nGROUP BY 1\nORDER BY $__timeGroup(TIMESTAMP,$__interval,previous)",
"refId": "Waschmaschine",
"select": [
[
{
"params": [
"value"
],
"type": "column"
},
{
"params": [
"avg"
],
"type": "aggregate"
},
{
"params": [
"value"
],
"type": "alias"
}
]
],
"table": "history",
"timeColumn": "TIMESTAMP",
"timeColumnType": "timestamp",
"where": [
{
"name": "$__timeFilter",
"params": [],
"type": "macro"
},
{
"datatype": "varchar",
"name": "",
"params": [
"DEVICE",
"=",
"'shelly03'"
],
"type": "expression"
},
{
"datatype": "varchar",
"name": "",
"params": [
"READING",
"=",
"'power'"
],
"type": "expression"
}
]
},
{
"datasource": "FHEM MySQL",
"format": "table",
"group": [
{
"params": [
"$__interval",
"previous"
],
"type": "time"
}
],
"metricColumn": "none",
"rawQuery": true,
"rawSql": "SELECT\n $__timeGroupAlias(TIMESTAMP,$__interval,previous),\n avg(value) AS \"Brunnen\"\nFROM history\nWHERE\n $__timeFilter(TIMESTAMP) AND\n DEVICE = 'shelly05' AND\n READING = 'power_0'\nGROUP BY 1\nORDER BY $__timeGroup(TIMESTAMP,$__interval,previous)",
"refId": "Brunnen",
"select": [
[
{
"params": [
"value"
],
"type": "column"
},
{
"params": [
"avg"
],
"type": "aggregate"
},
{
"params": [
"value"
],
"type": "alias"
}
]
],
"table": "history",
"timeColumn": "TIMESTAMP",
"timeColumnType": "timestamp",
"where": [
{
"name": "$__timeFilter",
"params": [],
"type": "macro"
},
{
"datatype": "varchar",
"name": "",
"params": [
"DEVICE",
"=",
"'shelly05'"
],
"type": "expression"
},
{
"datatype": "varchar",
"name": "",
"params": [
"READING",
"=",
"'power_0'"
],
"type": "expression"
}
]
},
{
"datasource": "FHEM MySQL",
"format": "table",
"group": [
{
"params": [
"$__interval",
"previous"
],
"type": "time"
}
],
"metricColumn": "none",
"rawQuery": true,
"rawSql": "SELECT\n $__timeGroupAlias(TIMESTAMP,$__interval,previous),\n avg(value) AS \"Shaun\"\nFROM history\nWHERE\n $__timeFilter(TIMESTAMP) AND\n DEVICE = 'shelly05' AND\n READING = 'power_1'\nGROUP BY 1\nORDER BY $__timeGroup(TIMESTAMP,$__interval,previous)",
"refId": "Shaun",
"select": [
[
{
"params": [
"value"
],
"type": "column"
},
{
"params": [
"avg"
],
"type": "aggregate"
},
{
"params": [
"value"
],
"type": "alias"
}
]
],
"table": "history",
"timeColumn": "TIMESTAMP",
"timeColumnType": "timestamp",
"where": [
{
"name": "$__timeFilter",
"params": [],
"type": "macro"
},
{
"datatype": "varchar",
"name": "",
"params": [
"DEVICE",
"=",
"'shelly05'"
],
"type": "expression"
},
{
"datatype": "varchar",
"name": "",
"params": [
"READING",
"=",
"'power_1'"
],
"type": "expression"
}
]
}
],
"thresholds": [],
"timeFrom": null,
"timeRegions": [],
"timeShift": null,
"title": "Hauptverbraucher",
"tooltip": {
"shared": true,
"sort": 0,
"value_type": "individual"
},
"type": "graph",
"xaxis": {
"buckets": null,
"mode": "time",
"name": null,
"show": true,
"values": []
},
"yaxes": [
{
"decimals": 0,
"format": "short",
"label": "Watt",
"logBase": 1,
"max": "14000",
"min": "0",
"show": true
},
{
"format": "short",
"label": null,
"logBase": 1,
"max": null,
"min": null,
"show": true
}
],
"yaxis": {
"align": false,
"alignLevel": null
}
},
{
"aliasColors": {
"SW_Total_DC_P_Max value": "dark-blue",
"Solar_Calculation": "dark-yellow",
"Solar_Calculation_fc0": "super-light-green",
"Solar_Calculation_fc0 value": "light-green",
"Solar_Calculation_fc1": "dark-red",
"Solar_Calculation_fc1 value": "dark-red",
"Solar_East": "super-light-blue",
"Solar_East value": "super-light-blue",
"Solar_South": "light-red",
"Solar_South value": "light-red",
"Solar_West": "light-purple",
"Solar_West value": "super-light-purple"
},
"bars": false,
"dashLength": 10,
"dashes": false,
"datasource": null,
"decimals": null,
"fieldConfig": {
"defaults": {
"links": []
},
"overrides": []
},
"fill": 0,
"fillGradient": 0,
"gridPos": {
"h": 13,
"w": 23,
"x": 0,
"y": 24
},
"hiddenSeries": false,
"id": 4,
"legend": {
"alignAsTable": true,
"avg": false,
"current": true,
"max": false,
"min": false,
"rightSide": true,
"show": true,
"sideWidth": 250,
"sort": null,
"sortDesc": null,
"total": false,
"values": true
},
"lines": true,
"linewidth": 1,
"nullPointMode": "null",
"options": {
"alertThreshold": true
},
"percentage": false,
"pluginVersion": "7.5.5",
"pointradius": 2,
"points": false,
"renderer": "flot",
"seriesOverrides": [
{
"alias": "Solar_Calculation_fc0",
"linewidth": 2
},
{
"alias": "Power_DC_Sum",
"fill": 2
}
],
"spaceLength": 10,
"stack": false,
"steppedLine": false,
"targets": [
{
"datasource": "FHEM MySQL",
"format": "table",
"group": [],
"metricColumn": "none",
"rawQuery": true,
"rawSql": "SELECT\n TIMESTAMP AS \"time\",\n value AS \"Solar_Calculation_fc0\"\nFROM history\nWHERE\n $__timeFilter(TIMESTAMP) AND\n DEVICE = 'WR_1' AND\n READING = 'Solar_Calculation_fc0'\nORDER BY TIMESTAMP",
"refId": "Solar_Calculation_fc0",
"select": [
[
{
"params": [
"value"
],
"type": "column"
}
]
],
"table": "history",
"timeColumn": "TIMESTAMP",
"timeColumnType": "timestamp",
"where": [
{
"name": "$__timeFilter",
"params": [],
"type": "macro"
},
{
"datatype": "varchar",
"name": "",
"params": [
"DEVICE",
"=",
"'WR_1'"
],
"type": "expression"
},
{
"datatype": "varchar",
"name": "",
"params": [
"READING",
"=",
"'Solar_Calculation_fc0'"
],
"type": "expression"
}
]
},
{
"datasource": "FHEM MySQL",
"format": "table",
"group": [],
"metricColumn": "none",
"rawQuery": true,
"rawSql": "SELECT\n TIMESTAMP AS \"time\",\n value AS \"Solar_Calculation_fc1\"\nFROM history\nWHERE\n $__timeFilter(TIMESTAMP) AND\n DEVICE = 'WR_1' AND\n READING = 'Solar_Calculation_fc1'\nORDER BY TIMESTAMP",
"refId": "Solar_Calculation_fc1",
"select": [
[
{
"params": [
"value"
],
"type": "column"
}
]
],
"table": "history",
"timeColumn": "TIMESTAMP",
"timeColumnType": "timestamp",
"where": [
{
"name": "$__timeFilter",
"params": [],
"type": "macro"
},
{
"datatype": "varchar",
"name": "",
"params": [
"DEVICE",
"=",
"'WR_1'"
],
"type": "expression"
},
{
"datatype": "varchar",
"name": "",
"params": [
"READING",
"=",
"'Solar_Calculation_fc1'"
],
"type": "expression"
}
]
},
{
"datasource": "FHEM MySQL",
"format": "table",
"group": [
{
"params": [
"$__interval",
"previous"
],
"type": "time"
}
],
"metricColumn": "none",
"rawQuery": true,
"rawSql": "SELECT\n $__timeGroupAlias(TIMESTAMP,$__interval,previous),\n avg(value) AS \"SW_Total_DC_P_sumOfAllPVInputs\"\nFROM history\nWHERE\n $__timeFilter(TIMESTAMP) AND\n DEVICE = 'WR_1' AND\n READING = 'SW_Total_DC_P_sumOfAllPVInputs'\nGROUP BY 1\nORDER BY $__timeGroup(TIMESTAMP,$__interval,previous)",
"refId": "Power_DC_Sum",
"select": [
[
{
"params": [
"value"
],
"type": "column"
},
{
"params": [
"avg"
],
"type": "aggregate"
},
{
"params": [
"value"
],
"type": "alias"
}
]
],
"table": "history",
"timeColumn": "TIMESTAMP",
"timeColumnType": "timestamp",
"where": [
{
"name": "$__timeFilter",
"params": [],
"type": "macro"
},
{
"datatype": "varchar",
"name": "",
"params": [
"DEVICE",
"=",
"'WR_1'"
],
"type": "expression"
},
{
"datatype": "varchar",
"name": "",
"params": [
"READING",
"=",
"'Power_DC_Sum'"
],
"type": "expression"
}
]
},
{
"datasource": "FHEM MySQL",
"format": "table",
"group": [
{
"params": [
"$__interval",
"previous"
],
"type": "time"
}
],
"metricColumn": "none",
"rawQuery": true,
"rawSql": "SELECT\n $__timeGroupAlias(TIMESTAMP,$__interval,previous),\n avg(value) AS \"Solar_Calculation\"\nFROM history\nWHERE\n $__timeFilter(TIMESTAMP) AND\n DEVICE = 'WR_1' AND\n READING = 'Solar_Calculation'\nGROUP BY 1\nORDER BY $__timeGroup(TIMESTAMP,$__interval,previous)",
"refId": "Solar_Calculation",
"select": [
[
{
"params": [
"value"
],
"type": "column"
},
{
"params": [
"avg"
],
"type": "aggregate"
},
{
"params": [
"value"
],
"type": "alias"
}
]
],
"table": "history",
"timeColumn": "TIMESTAMP",
"timeColumnType": "timestamp",
"where": [
{
"name": "$__timeFilter",
"params": [],
"type": "macro"
},
{
"datatype": "varchar",
"name": "",
"params": [
"DEVICE",
"=",
"'WR_1'"
],
"type": "expression"
},
{
"datatype": "varchar",
"name": "",
"params": [
"READING",
"=",
"'Solar_Calculation'"
],
"type": "expression"
}
]
},
{
"datasource": "FHEM MySQL",
"format": "table",
"group": [
{
"params": [
"$__interval",
"previous"
],
"type": "time"
}
],
"metricColumn": "none",
"rawQuery": true,
"rawSql": "SELECT\n $__timeGroupAlias(TIMESTAMP,$__interval,previous),\n avg(value) AS \"WR_1_Ost\"\nFROM history\nWHERE\n $__timeFilter(TIMESTAMP) AND\n DEVICE = 'WR_1' AND\n READING = 'Solar_WR_1_Ost'\nGROUP BY 1\nORDER BY $__timeGroup(TIMESTAMP,$__interval,previous)",
"refId": "WR_1_Ost",
"select": [
[
{
"params": [
"value"
],
"type": "column"
},
{
"params": [
"avg"
],
"type": "aggregate"
},
{
"params": [
"value"
],
"type": "alias"
}
]
],
"table": "history",
"timeColumn": "TIMESTAMP",
"timeColumnType": "timestamp",
"where": [
{
"name": "$__timeFilter",
"params": [],
"type": "macro"
},
{
"datatype": "varchar",
"name": "",
"params": [
"DEVICE",
"=",
"'WR_1'"
],
"type": "expression"
},
{
"datatype": "varchar",
"name": "",
"params": [
"READING",
"=",
"'WR_1_Ost'"
],
"type": "expression"
}
]
},
{
"datasource": "FHEM MySQL",
"format": "table",
"group": [
{
"params": [
"$__interval",
"previous"
],
"type": "time"
}
],
"metricColumn": "none",
"rawQuery": true,
"rawSql": "SELECT\n $__timeGroupAlias(TIMESTAMP,$__interval,previous),\n avg(value) AS \"WR_1_West\"\nFROM history\nWHERE\n $__timeFilter(TIMESTAMP) AND\n DEVICE = 'WR_1' AND\n READING = 'Solar_WR_1_West'\nGROUP BY 1\nORDER BY $__timeGroup(TIMESTAMP,$__interval,previous)",
"refId": "WR_1_West",
"select": [
[
{
"params": [
"value"
],
"type": "column"
},
{
"params": [
"avg"
],
"type": "aggregate"
},
{
"params": [
"value"
],
"type": "alias"
}
]
],
"table": "history",
"timeColumn": "TIMESTAMP",
"timeColumnType": "timestamp",
"where": [
{
"name": "$__timeFilter",
"params": [],
"type": "macro"
},
{
"datatype": "varchar",
"name": "",
"params": [
"DEVICE",
"=",
"'WR_1'"
],
"type": "expression"
},
{
"datatype": "varchar",
"name": "",
"params": [
"READING",
"=",
"'WR_1_West'"
],
"type": "expression"
}
]
},
{
"datasource": "FHEM MySQL",
"format": "table",
"group": [
{
"params": [
"$__interval",
"previous"
],
"type": "time"
}
],
"metricColumn": "none",
"rawQuery": true,
"rawSql": "SELECT\n $__timeGroupAlias(TIMESTAMP,$__interval,previous),\n avg(value) AS \"WR_2_Sued\"\nFROM history\nWHERE\n $__timeFilter(TIMESTAMP) AND\n DEVICE = 'WR_1' AND\n READING = 'Solar_WR_2_Sued'\nGROUP BY 1\nORDER BY $__timeGroup(TIMESTAMP,$__interval,previous)",
"refId": "WR_2_Sued",
"select": [
[
{
"params": [
"value"
],
"type": "column"
},
{
"params": [
"avg"
],
"type": "aggregate"
},
{
"params": [
"value"
],
"type": "alias"
}
]
],
"table": "history",
"timeColumn": "TIMESTAMP",
"timeColumnType": "timestamp",
"where": [
{
"name": "$__timeFilter",
"params": [],
"type": "macro"
},
{
"datatype": "varchar",
"name": "",
"params": [
"DEVICE",
"=",
"'WR_1'"
],
"type": "expression"
},
{
"datatype": "varchar",
"name": "",
"params": [
"READING",
"=",
"'WR_2_Sued'"
],
"type": "expression"
}
]
},
{
"datasource": "FHEM MySQL",
"format": "table",
"group": [
{
"params": [
"$__interval",
"previous"
],
"type": "time"
}
],
"metricColumn": "none",
"rawQuery": true,
"rawSql": "SELECT\n $__timeGroupAlias(TIMESTAMP,$__interval,previous),\n avg(value) AS \"WR_2_West\"\nFROM history\nWHERE\n $__timeFilter(TIMESTAMP) AND\n DEVICE = 'WR_1' AND\n READING = 'Solar_WR_2_West'\nGROUP BY 1\nORDER BY $__timeGroup(TIMESTAMP,$__interval,previous)",
"refId": "WR_2_West",
"select": [
[
{
"params": [
"value"
],
"type": "column"
},
{
"params": [
"avg"
],
"type": "aggregate"
},
{
"params": [
"value"
],
"type": "alias"
}
]
],
"table": "history",
"timeColumn": "TIMESTAMP",
"timeColumnType": "timestamp",
"where": [
{
"name": "$__timeFilter",
"params": [],
"type": "macro"
},
{
"datatype": "varchar",
"name": "",
"params": [
"DEVICE",
"=",
"'WR_1'"
],
"type": "expression"
},
{
"datatype": "varchar",
"name": "",
"params": [
"READING",
"=",
"'WR_2_West'"
],
"type": "expression"
}
]
}
],
"thresholds": [],
"timeFrom": null,
"timeRegions": [],
"timeShift": null,
"title": "Forecast/Prognose",
"tooltip": {
"shared": true,
"sort": 0,
"value_type": "individual"
},
"type": "graph",
"xaxis": {
"buckets": null,
"mode": "time",
"name": null,
"show": true,
"values": []
},
"yaxes": [
{
"decimals": 0,
"format": "short",
"label": "Watt",
"logBase": 1,
"max": "16000",
"min": "0",
"show": true
},
{
"format": "short",
"label": null,
"logBase": 1,
"max": null,
"min": null,
"show": true
}
],
"yaxis": {
"align": false,
"alignLevel": null
}
}
],
"refresh": "5m",
"schemaVersion": 27,
"style": "dark",
"tags": [],
"templating": {
"list": []
},
"time": {
"from": "now-1d/d",
"to": "now-1d/d"
},
"timepicker": {
"hidden": false,
"refresh_intervals": [
"5s",
"10s",
"30s",
"1m",
"5m",
"15m",
"30m",
"1h",
"2h",
"1d"
]
},
"timezone": "utc",
"title": "PV_Anlage_1",
"uid": "W-Y51Dmgk",
"version": 105
}
</syntaxhighlight>
==Diagramme mit SVG==
Die Diagramme werden bei mir nicht mehr weiterentwickelt, da ich auf Grafana umgestiegen bin. Sie stehen hier nur noch als Beispiele für den Anfang.
Grafana ermöglicht das direkte Auslesen der SQL Datenbank und kann auch auf einer anderen Plattform betrieben werden. Bei mir befindet es sich in Docker Containern auf dem selben RPI4.
[[Bild:Plenticore FHEM 1.png|mini|900px|rechts|Die Diagramme im Überblick]]
plot "<IN>" using 1:2 axes x1y2 title 'Sonnenhöhe' ls l7 lw 1 with lines,\
"<IN>" using 1:2 axes x1y2 title 'SolarRadiation' ls l8 lw 2 with lines,\
"<IN>" using 1:2 axes x1y2 title 'SolarRadiationPrognose' ls l8 lw 1 with lines,\
"<IN>" using 1:2 axes x1y2 title '70%' ls l0 lw 1 with lines,\
"<IN>" using 1:2 axes x1y2 title 'Calculation_fc1' ls l0 lw 2 with lines,\
"<IN>" using 1:2 axes x1y2 title 'Total_DC_Power_(sumOfAllPVInputs)' ls l1fill lw 1 with lines,\
"<IN>" using 1:2 axes x1y2 title 'Calculation' ls l5 lw 1 with lines,\
"<IN>" using 1:2 axes x1y2 title 'East' ls l2 lw 0.5 with lines,\
"<IN>" using 1:2 axes x1y2 title 'South' ls l3 lw 0.5 with lines,\
"<IN>" using 1:2 axes x1y2 title 'West' ls l4 lw 0.5 with lines
</pre>
== PV Eigenverbrauch-Steuerung ==
'''Hier werde ich auch mal aktualisieren, bei bedarf einfach im Forum fragen.'''
=== Beispiel Luft Wärme Pumpe Novelan LAD ===
Hier mal ein paar Bilder für die Dokumentation der PV-Modus Anschaltung mit einem Shell 1 und das Heizelement, dass man über Lastrelais in drei Stufen Regeln könnte. Die Aktivierung der Zusatzheizung ist über das Luxtronik2 Modul möglich.
LAD_Shelly_Phase_und_Null.jpg|LAD Shelly Phase und Null
LAD_Shelly_SWT-Signal.jpg|LAD Shelly SWT-Signal
</gallery>
==== RAW Definition LWP_PV_Perl (DOIF Modul) ====
Hierbei wird das PV-Modus Signal über ein Shelly 1 zur LAD Wärmepumpe übermittelt, was natürlich auch durch ein beliebiges anderes Relais erfolgen kann.
Die setstate Attribute am Ende der RAW Definition sind ebenfalls wichtig, da dort die Default reading Werte für das DOIF gesetzt werden. Diese können dann über die uiTable Definitionen mit Pull Down Menüs geändert werden.
* [https://svn.fhem.de/fhem/trunk/fhem/contrib/ch.eick/Photovoltaik/W%c3%a4rmepumpe_Novelan_LAD9/ Beispiel Wärmepumpe Novelan LAD9 mit vorgeschaltetem Stromzähler]
- RAW Definition LWP_Signale (Shelly Modul: shelly1pm)
* [https://svn.fhem.de/fhem/trunk/fhem/contrib/ch.eick/Photovoltaik/W%c3%a4rmepumpe_Novelan_LAD9/RAW_LWP_PV_Perl.txt Beispiel LWP_PV_Perl PV Eigenverbrauch Steuerung]
<syntaxhighlight lang="Perl">
* [https://svn.fhem.de/fhem/trunk/fhem/contrib/ch.eick/Photovoltaik/W%c3%a4rmepumpe_Novelan_LAD9/RAW_LWP_Counter.txt Beispiel LWP_Counter]
defmod shelly01 Shelly 192.168.178.54
* [https://svn.fhem.de/fhem/trunk/fhem/contrib/ch.eick/Photovoltaik/W%c3%a4rmepumpe_Novelan_LAD9/RAW_shelly01.txt Beispiel Shelly PV Modus Umschaltung]
Wechselrichter 1 mit StatusWR_1_Speicher_1_ExternControlWechselrichter 2 und gesteuerte GeräteWallBox mit Kia FahrzeugPV gesteuerte Geräte
Ein Hinweis allgemeiner Art: Die hier abgebildeten Code Stücke sind nicht ausschließlich durch mich entstanden. Ich bedanke mich für die Unterstützung und Bereitstellung vieler Einzelkomponenten durch Dritte. Der Einsatz ist auf eigene Gefahr und für etwaige Schäden wird keinerlei Haftung übernommen. Bitte beachtet bei der Hardware die Gewährleistungsbestimmungen und Vorgaben der Hersteller.
Er verfügt über einen LAN-Anschluss und ist auf die Steuerung via WebGUI des Herstellers ausgelegt. Weiterhin kann eine Abfrage mit Modbus/TCP oder auch über eine undokumentierte API erfolgen. Für die API bietet der Hersteller keinerlei Support!
Voraussetzungen Energietechnik
Der Wechselrichter, Speicher und KSEM wurden durch einen Fachbetrieb installiert und konfiguriert. Die gesamte Anlage läuft fehlerfrei und wurde durch den Fachbetrieb abgenommen, sowie beim Netzbetreiber angemeldet.
Geräte-Registrierung
Hierfür ist die Dokumentation des Herstellers heranzuziehen. Für eine Verlängerung der Gewährleistungszeit kann man den Plenticore im Herstellerportal registrieren. Dies ist jedoch für die Anbindung an FHEM nicht notwendig.
Weiterhin ist der Betreiber verpflichtet, eine Anmeldung im Marktstammdatenregister vorzunehmen.
Hersteller Dokumentation
Für Links auf dieser Wiki Seite wird keine Haftung übernommen. Die Inhalte unterliegen der Verantwortung der Firma Kostal.
Auf Grund von Anfragen scheint diese Implementierung auch für andere Kostal Wechselrichtern zu passen.
- Plenticore Plus G1-G3
- Piko MP
- Piko IQ
- Piko CI 30
Dies hängt natürlich mit der Firmware zusammen und es gibt sicherlich noch einige Anpassungen die notwendig sind. Bitte tragt Eure kompatiblen Geräte hier ein.
Anmerkung: Kostal Piko CI 30 mit dem ModbusAttr Modul
Die Modbus-Attribute können zum großen Teil übernommen werden, aber Kostal fängt bei diesem Modell nicht bei "0", sondern bei "1" an zu zählen. Das heißt, alle Modbus-Adressen die hier genannt werden, müssen um 1 vermindert werden.
Einbindung in das Netzwerk
Als Grundlage ist der Plenticore mit dem LAN zu verbinden, wodurch er eine TCP/IP Adresse per DHCP bekommt. Diese ist dann entweder am Display des Plenticore abzulesen, oder über die Oberfläche des Routers zu ermitteln.
Das gleiche gilt für den BYD Speicher, der jedoch zusätzlich auch über WLAN verfügt. Eine Netzwerkanbindung des Speichers ist beim Plenticore nicht zwingend notwendig, da der Plenticore mit dem Speicher über eine RS-485 Schnittstelle kommuniziert. Bei dieser Anbindung werden jedoch noch nicht alle möglichen Werte aus dem Speicher ausgelesen. Später wird hierzu jedoch noch mehr geschrieben, um alle Möglichkeiten offen zu halten.
Der KSEM kann ebenfalls auch direkt per LAN ausgelesen werden, was jedoch ebenfalls nicht zwingend notwendig ist. Eine Kommunikation des KSEM mit dem Plenticore erfolgt über zwei mögliche Wege. Beim Betrieb mit Speicher ist zwingend die RS485 Schnittstelle erforderlich, über die auch der Plenticore alle Werte übermittelt bekommt. Auch diese sind dann am Plenticore abfragbar. Der zweite Weg wäre dann über die LAN Verbindung, bei der jedoch kein Speicher am Plenticore konfigurierbar ist.
Namensgebung
Im Laufe der Zeit hat sich herausgestellt, dass es einfacher ist, die hier vorgeschlagenen Namen zu übernehmen. Es vereinfacht die Kommunikation bei der Hilfestellung und erspart einigen Umbenennungsaufwand, da es doch immer wieder zu Anpassungen kommt. Leider musste in der letzten Zeit durch das Wachsen des Umfeldes und durch anfängliche Unwissenheit auch viel aufgeräumt werden.
Voraussetzungen FHEM Umfeld
Alle Geräte müssen mit TCP/IP erreichbar sein
Alle Module sollten auf einem aktuellen Stand sein
Eine DbLog/DbRep sollte bereits vorhanden sein, was hier nicht weiter erklärt wird
Es wurde immer wieder gefragt, ob man auch FileLog verwenden könnte, was grundsätzlich natürlich geht. Im Fortschreiten wird dies jedoch ziemlich unhandlich und vom Modulautor nicht empfohlen. Spätestens wenn die Daten mal aufgeräumt werden sollen, ist man mit einer MySQL Datenbank im Vorteil. Auch die Aufbereitung von Diagrammen ist mit einer Datenbank flexibler.
Weiterhin wird empfohlen eine vollwertige MySQL Datenbank zu verwenden, damit auch komplexeres Reporting im vollen SQL Umfang zur Verfügung steht. Beim Einsatz von SQLite sind bereits zumindest bei den Reports inkompatibilitäten aufgetreten. MariaDB sollte immer auch aktuell gehalten werden, was auf einem RPI 32 Bit nicht immer gewährleistet ist. Die größte aktualität erhält man mit dem original MySQL Docker Container, der auch auf einem RPI4 64 Bit verfügbar ist. Beim Einsatz einer Datenbank sollte man dies auf keinen Fall auf einer SD-Card machen. die besten Ergebnisse für Geschwindigkeit und Langlebigkeit bekommt man mit einer SSD.
Verwendete Module
Modbus
HTTPMOD
expandJSON
DbLog
DbRep
DUMMY
DOIF
Shelly
SMAEM
HourCounter
DWD_OpenData
WeekdayTimer
Verwendetes Umfeld
Raspberry Pi 4
Debian
Docker
fhem/fhem:latest
mysql/mysql-server
grafana/grafana:latest
Perl
Python
Einbindung in FHEM: Überblick
MySQL etwas Basis Information
Docker Compose nach installieren
Falls Docker Compose nicht bereits installiert ist.
Für den Überblick:
docker stats
docker ps
Zum aktualisieren (nur ein Beispiel):
docker pull portainer/portainer:latest
docker-compose up -d
MySQL Docker Container mit .yml
Hier wäre mal ein Beispiel, wie die MySQL Datenbank innerhalb einer .yml Datei im Docker definiert werden kann. Im Beispiel ist FHEM und Portainer ebenfalls enthalten, wodurch dies auch somit eine Basis Installation sein könnte.
In der /etc/passwd habe ich dann noch ein Login und die Home Verzeichnisse für einige Container eingetragen. Somit kann man sich dann auf dem Basis Betriebsystem mit diesem Benutzer anmelden und das Dateisystem des Docker Containers direkt bearbeiten. Der Home Path muss natürlich angepasst werden.
Wenn der MySQL Docker Container läuft sind dort noch die Basis Konfigurationen für DbLog durchzuführen, für die es ein eigenes FHEM Wiki gibt. Einige wichtige Kommandos sollen nun jetzt hier trotzdem aufgelistet werden.
MySQL Docker Console für MySQL root Login
Achtung
Die Basis Konfiguration wird mit dem Benutzer root im MySQL durchgeführt und dieser kann sich bei Oracle MySQL nur lokal, also nicht aus dem Netz anmelden.
Dazu gibt es zwei Varianten:
1. Über den Docker Container anmelden
2. Oder man meldet sich am Portainerzugang (admin mit <Passwort>) an und selectiert den MySQL Container. Dort kann man dann mit der Console in den Container wechseln.
bash-4.4# mysql -u root -pEnterpassword:WelcometotheMySQLmonitor.Commandsendwith;or\g.YourMySQLconnectionidis44892Serverversion:8.0.28MySQLCommunityServer-GPLmysql>
MySQL Kommandos
-- Die fhem Datenbank neu anlegenmysql>CREATEDATABASE`fhem`DEFAULTCHARACTERSETutf8mb4COLLATEutf8mb4_bin;-- Die FHEM Tabellen neu anlegen-- Für die Spalte VALUE wurde mal eine längere Definition gewähltmysql>CREATETABLE`fhem`.`history`(TIMESTAMPTIMESTAMPNOTNULLDEFAULTCURRENT_TIMESTAMP,DEVICEvarchar(64),TYPEvarchar(64),EVENTvarchar(512),READINGvarchar(64),VALUEvarchar(255),UNITvarchar(32));mysql>CREATETABLE`fhem`.`current`(TIMESTAMPTIMESTAMP,DEVICEvarchar(64),TYPEvarchar(64),EVENTvarchar(512),READINGvarchar(64),VALUEvarchar(128),UNITvarchar(32));-- Den Index einrichten und über das READING mit definieren, damit kein duplicate Key entstehen kannmysql>CREATEINDEXSearch_IdxON`fhem`.`history`(TIMESTAMP,DEVICE,READING)USINGBTREE;QueryOK,0rowsaffected(0.11sec)Records:0Duplicates:0Warnings:0-- Im Oracle MySQL wird im Standard das Passwort in einer anderen Verschlüsselung abgeregt, die FHEM noch nicht unterstützt-- Deshalb kann es erforderlich sein das Passwort, nach Änderung der Verschlüsselungsmethode neu zu setzen-- Nachschauen welche Passwort Verschlüsselung verwendet wird-- FHEM unterstützt nur mysql_native_passwordmysql>SELECTHost,User,pluginFROMmysql.userWHEREUser='fhemuser';+------+----------+-----------------------+|Host|User|plugin|+------+----------+-----------------------+|%|fhemuser|caching_sha2_password|<<<DasunterstütztFHEMmitDbLognochnicht+------+----------+-----------------------+mysql>DROPUSERfhemuser;mysql>CREATEUSER'fhemuser'@'%'IDENTIFIEDWITHmysql_native_passwordBY'<password>';mysql>SELECTHost,User,pluginFROMmysql.userWHEREUser='fhemuser';+------+----------+-----------------------+|Host|User|plugin|+------+----------+-----------------------+|%|fhemuser|mysql_native_password|+------+----------+-----------------------+1rowinset(0.01sec)-- Bei den GRANDS muss ich nochmal nachschauen, was da wirklich notwendig istmysql>GRANTSELECT,INSERT,DELETE,UPDATEON`fhem`.*TO'fhemuser'@'%';mysql>GRANTALTERROUTINEON`fhem`.*TO'fhemuser'@'%';mysql>GRANTEXECUTEONPROCEDURE`fhem`.'dwd_load'TO'fhemuser'@'%';<<<<DasbeschränktaufnureineProzedure-- Die Änderungen müssen mit einem FLUSH in der aktuellen Session übernommen werdenmysql>FLUSHPRIVILEGES;-- Alle Einträge für den fhemuser anschauenmysql>SELECT*FROMmysql.userWHEREUser='fhemuser';<snip>EswirdeineTabellemitdenuserEinträgenfürfhemuserangezeigt,diemansichbesserinvollerBreiteimEditoranschautmysql>Connectfhem;Enterpassword:Connectionid:44902Currentdatabase:fhem-- Zum Test mal einige Einträge in der history anzeigen, wenn FHEM bereits was über DbLog geschieben hatmysql>SELECT*FROMhistoryLIMIT1;+---------------------+------------------+---------+-------+-----------------+----------+------+|TIMESTAMP|DEVICE|TYPE|EVENT|READING|VALUE|UNIT|+---------------------+------------------+---------+-------+-----------------+----------+------+|2019-04-0300:23:42|EVU_StromZaehler|HTTPMOD|NULL|Strom_Status-02|07152.96||+---------------------+------------------+---------+-------+-----------------+----------+------+-- Es könnte auch gut sein einen extra root user anzulegen, um mit DbRep die MySQL Datenbank zu optimierenmysql>CREATEUSER'fhemroot'@'<IP Adresse>'IDENTIFIEDWITHmysql_native_passwordBY'<password>';mysql>GRANTALLPRIVILEGESON`fhem`.*TO'fhemroot'@'<IP Adresse>'WITHGRANTOPTION;mysql>GRANTSHOW_ROUTINEON*.*TO'fhemroot'@'<IP Adresse>';mysql>GRANTEXECUTE,CREATEROUTINE,ALTERROUTINEON`fhem`.*TO'fhemroot'@'<IP Adresse>';-- Die Änderungen müssen mit einem FLUSH in der aktuellen Session übernommen werdenmysql>FLUSHPRIVILEGES;mysql>SELECTuser,hostFROMmysql.user;+------------------+----------------+|user|host|+------------------+----------------+|fhemuser|%||fhemroot|192.168.178.57|<<<DassolltemandannaufeineIPeinschränken.|root|localhost|+------------------+----------------+-- Hier kann man mal seine Proceduren auflistenmysql>SHOWPROCEDURESTATUSWHEREDb='fhem';
Hardware Anbindung (alles über LAN)
Kostal Plenticore Plus
Kostal Plenticore Plus die Basis information (Modbus/TCP)
Der Kostal Plenticore wird mittels des Moduls MODBUS eingebunden. Dazu gilt zunächst die folgende Konfiguration als Basis, die am Display des Plenticore oder auch in der Kostal Dokumentation zu entnehmen ist.
Plenticore Modbus Definition
GeräteId : 71
IP-Adresse: <IP-Adresse>
Port : 1502
Ein zweiter Wechselrichter
Durch einen Vollausbau des Daches ist ein zweiter Wechselrichter hinzugekommen. Das hat einen Umbau dieser Wiki Seite erfordert, wodurch sich diverse Änderungen bei der Namensgebung ergeben haben. Es ist jedoch auch weiterhin möglich, nur eine AC-Quelle zu verwenden, was an einigen Stellen entweder einen Rückbau in den Devices erfordert oder man freundet sich mit etwas anderen reading Namen an.
Modbus Timeing
Das Gerät aktualisiert sich im Abstand von 60 Sekunden durch den stetigen Modbus/TCP Datenstrom. Der Plenticore ist als Modbus Master implementiert und sendet somit alle Daten permanent ins Netzwerk.
Die Zeit kann auch verändert werden, jedoch sollte sie nicht zu kurz gewählt sein.
RAW Definition WR_1 (bei einem Wechselrichtern)
Generell kann die WR_1 und WR_1_API Definition, die im folgenden kommt auch für einen einzelnen Wechselrichter verwendet werden. Wer die readings für die Korrektur mit zwei AC-Quellen nicht haben möchte, der kann diese gerne wieder entfernen, was dann jedoch bei jedem Update immer wieder gemacht werden muss. Achtung es können dann jedoch Abweichungen bei der Plausibilität einzelner Readings auftreten. Alle stateFormat und weiteren DOIF Devices beziehen sich hierbei jedoch ebenfalls auf die SW_* readings, um die Implementierung für mehr Anwender verwendbar zu machen. Diejenigen unter Euch, die auch einen nicht Kostal Wechselrichter verwenden sollten sich bitte die userReadings anschauen, um dort die readings korrekt zu mappen. Es sollte auch kein Problem sein als zweite AC-Quelle ein Krafwärmekopplung ein zu binden.
Anzupassende Stellen, wenn man lieber keinen Schwarm vorbereiten möchte:
WR_1 und WR_1_API:
- userReadings SW_* löschen
- deletereading WR_1 SW_*
- stateFormat bei readings mit SW_* das SW_ löschen
- DbLogInclude überprüfen
- event-on-update-reading überprüfen
PV_Schedule:
- cmd_5 für die Zählerstände löschen
DbLog:
- Eventuell die Datenbank aufräumen
RAW Definition WR_1 Master (bei zwei Wechselrichtern)
Für den Zugriff auf die Plenticore ModBus/TCP Daten wird dieses Device verwendet.
Es kann für einen einzelnen oder auch mehrere Wechselrichter verwendet werden. Readings mit SW_* korrigieren die bisher fehlerhaften Ausgabewerte der Kostal Wechselrichter im Schwarm. Bei Verwendung nur eines einzelnen Wechselrichters können diese jedoch auch verwendet werden. Die Original readings sind auch weiterhin noch vorhanden, was jedoch zu einer Befüllung mit gleichen Werten führen kann. Damit diese nicht doppelt in der Datenbank landen, kann man das DbLogInclude entsprechend anpassen, nachdem man sich entschieden hat, welche Richtung man einschlagen möchte.
Letzte Änderungen:
- Bei den readings wurden ungültige Zeichen aus den Namen entfernt, wie z.B. "()"
Wer das bei sich ändert muss natürlich auch in der Datenbank aufräumen.
- Einige readings wurden eingekürzt z.B. P,U,I,L*
- SW_* gibt die korrigierten werte bei mehr als einer AC-Quelle an
- DbLogInclude logged nun auch SW_* und die eingekürzten reading Namen
- PV wurde zu WR, da eine PV-Anlage aus mehreren Wechselrichtern bestehen kann
- stateFormat und userReadings verwenden jetzt die SW_* readings
Um später einige Abfragen und Diagramme einfacher zu erstellen wurden einige userReadings erstellt, die bereits bei der RAW Definition mit vorhanden sind. Während der Integration in FHEM und der Konfiguration kann es hierdurch jedoch noch Fehlermeldungen im FHEM Log geben, da noch nicht alle Werte vorhanden sind.
Total_PV_Power_reserve
Trigger: Total_DC_Power.*
Hier wird ein reading erstellt, dass als Information über die verwendbare Reserve dient. Die Leistung, die in den Speicher geht
sollte man nämlich besser sofort verbrauchen, anstatt es erst zu speicher. Die 0.90 ist ein circa Wirkungsgrad, um von DC- auf AC-Leistung zu kommen.
Der Total_PV_Power_reserve Wert kann gerne nach eigenen Belangen kalkuliert werden.
Total_DC_Power_Max
Trigger: Total_DC_Power.*
Um die Batterieleistung mit zu berücksichtigen wird dieser Momentan Wert ermittelt.
Trigger: Actual_Battery_charge_-minus_or_discharge_-plus_current.*
Berechnung der Batterie Leistung aus Spannung und Strom , der wert kann positiv oder negativ sein, je nach dem ob geladen oder entladen wird.
Actual_Battery_charge_usable_Power
Trigger: Act_state_of_charge.*
Dieser Wert gibt an, wieviel Leistung im Speicher vorhanden ist, reduziert um 10% Verluste. An dieser Stelle muss die Nennleistung des Speichers über das PV_1_config Gerät
eingetragen werden, da diese noch nicht ausgelesen werden kann. Obwohl es im "Battery_Type 892941625" stecken könnte. 8929 >> 8.93 KW ???
SW_*
Bei mehreren Wechselrichtern werden einige Werte vom Plenticore fehlerhaft ermittelt und durch diese readings korrigiert.
Wann Kostal das in der Firmware korrigiert ist nicht bekannt.
RAW Definition WR_2 Slave (bei zwei Wechselrichtern)
In einem Schwarm kann man den zweiten Wechselrichter etwas sparsamer definieren, da es nur einen Wechselrichter mit Speicher geben darf und der zweite Wechselrichter auch keine KSEM Verbindung hat. Auch die SW_* readings werden hier nicht benötigt. Im userReading des WR_1 und WR_1_API wird auch auf den WR_2 und WR_2_API referenziert.
Der Plenticore erstellt intern noch diverse Statistiken, die auch über das WebGUI angesehen werden können. Diese werden jedoch nicht über MODBUS/TCP ausgegeben, aber zum Kostal Portal übermittelt, wo man dann bereits in Form von Diagrammen einen schönen Überblick bekommt. Das Ziel ist jedoch die Statistiken lokal im FHEM abzulegen, sie lassen sich nur über die API des Plenticore abfragen.
Auch hier werden bei mehreren AC-Quellen falsche Werte ausgegeben, die durch SW_* userReadings korrigiert wurden.
Plenticore API
Die API Definition kann man sich mit folgendem Aufruf anschauen, sie wird von Kostal jedoch nicht supportet.
http://<IP-Address_Plenticore>/api/v1
Plenticore Testablauf
Für die erste Implementierung ist es zu empfehlen alle Devices so zu belassen und zu benennen, wie sie im Wiki angegeben sind.
Eine persönliche Anpassung ist erst sinnvoll, wenn alles läuft, da hierdurch bereits schon im Vorfeld viele Fehler verursacht wurden, die eine Unterstützung erschweren.
Folgendes bitte aktivieren, damit genügend Meldungen zusehen sind
attrglobalverbose3attr<device>verbose5
Bitte unbedingt bei Problemen den Log Ausschnitt mit senden und die Meldungen anderer Devices vorher herauslöschen, da es ansonsten schwierig ist eine Diagnose zu betreiben.
Tests sind bei den einzelnen Schritten direkt mit angegeben.
plenticore_auth() und KeyValue() Voraussetzung
Diese Einträge müssen, falls noch nicht vorhanden in die 99_myUtils eingetragen werden. Hierdurch lädt man zusätzliche, nicht Standardfunktionen aus Perl Bibliotheken in ein Modul.
Je nach dem welche Installation Ihr verwendet kann es auch sein, dass noch weitere Module installiert werden müssen. Es wurde bereits folgendes gemeldet.
Das Passwort für den Plenticore wird mit der Funktion KeyValue() im KeyStore vom FHEM abgelegt und ausgelesen. Bitte beachtet, dass dies kein Schutz gegen das Stehlen von Passworten ist. Es dient lediglich der Ablage von Keys, damit sie nicht direkt offen im Datensystem oder im Konfigurationsfile zu finden sind.
Die Funktion befindet sich in der 99_myUtils .
KeyValue()
Die KeyValue() Funktion kommt in die 99_myUtils.pm .
Die Funktion befindet sich in der 99_myUtils und kann auch direkt in der Commandline von FHEM aufgerufen werden.
Der Syntax von "PW_<device>_<key>" ist so beizubehalten. Beim Umbenennen der WR_1_API Device ist hier dann auch das Passwort neu im KeyStore abzulegen.
Der Key "user" ist die Benutzerkennung des "Anlagenbetreibers" im Plenticore. Bei der Anmeldung am Plenticore Web Interface wird "Anlagenbetreiber" ausgewählt, jedoch wird dies im Hintergrund auf den Benutzernamen "user" gemapped. Somit ist für die API "user" zu verwenden und das Passwort des Anlagenbetreibers vom Web-GUI. Oft entspricht das Passwort des Anlagenbetreibers dem "Master Key" der sich auf dem Aufkleber des Plenticore befindet. Sollte das nicht der Fall sein, so kann man auch das Passwort auf den "Master Key" zurück setzen.
Wenn das Passwort aus dem KeyStore mit read abgeholt wird, wird es im Klartext angezeigt! Dies muss einzeilig und identisch zum Passwort des Anlagenbetreibers vom Web-GUI erscheinen. Bei etwaigen Sonderzeichen kam es hier schon zu Abweichungen. In solch einem Fall muss man dann leider das Passwort des Anlagenbetreibers vom Web-GUI im Plenticore ändern.
plenticore_auth() Erläuterung
Diese Funktion befindet sich in der 99_myUtils und dient der Erstellung von Keys für die mehrstufige Anmeldung des Kostal Plenticore Plus.
plenticore_auth()
Für die Abfrage des KeyValue gab es hier direkt am Anfang der Funktion eine Änderung (2021.04.07). Auch das Logging wurde verändert und kann nun durch das Setzen von "attr WR_1_API verbose 5" aktiviert werden.
Die plenticore_auth() kommt in die 99_myUtils.pm .
Nachdem vorher bereits mit KeyValue() das Passwort hinterlegt und getestet wurde kann man die Funktion bereits überprüfen.
Die Anzahl der Argumente ist je nach gewünschter Funktionalität (start|finish|session) unterschiedlich.
Da die auth_* Keys zur Laufzeit im Dialog mit dem Plenticore erstellt und ausgetauscht werden, wird hier für den Test mit Beispielwerten gearbeitet
SyntaxfürdieCommandlineimFHEM:{plenticore_auth("[start|finish|session]","user","<device>","auth_randomString64","auth_nonce","auth_salt","auth_rounds","auth_transactionId","auth_token")}Test1){plenticore_auth("start","user","WR_1_API")}# Im Hintergrund wird das Passwort aus dem KeyStore verwendet>>>{"nonce":"UUZ1dWNEZnowVzh2","username":"user"}Test2){plenticore_auth("finish","user","WR_1_API","TESMUWZnwkJZbnpF","TE2MUWZnwkJZbnpFQ5ulCfolNNdAD0vT","DbAC0R85jwF0rh+r","29000","1376720346bea40cdf770a8f84b5975cfeb20c5e6ac6d89b7862df3ca9695e43")}>>>{"transactionId":"1376720346bea40cdf770a8f84b5975cfeb20c5e6ac6d89b7862df3ca9695e43","proof":"5xZeOxoyR0hzPCVqvD/BPMqscQbT57wSONl049xiLjE="}Test3){plenticore_auth("session","user","WR_1_API","TESMUWZnwkJZbnpF","TE2MUWZnwkJZbnpFQ5ulCfolNNdAD0vT","DbAC0R85jwF0rh+r","29000","1376720346bea40cdf770a8f84b5975cfeb20c5e6ac6d89b7862df3ca9695e43","acafc66c0e1975293d35512a1e4bcceea55840b3109a703514e75b5ebce9b7c5")}>>>{"transactionId":"1376720346bea40cdf770a8f84b5975cfeb20c5e6ac6d89b7862df3ca9695e43","iv":"ckS6b3PIzcR7Iy4TEUUZOQ==","tag":"ROTpRrav38sLdt3EEuE3tQ==","payload":"nWraowAhLQVk5RCq8WOo8ZhGvUyHLMNxA13/21w7DuHDqq2LOQRXM143kJE5WNJQgeuoKeLiRunPaRpiJUzK3g=="}
Ablaufbeschreibung WR_1_API
Hier soll ein kurzer Überblick für den Anmeldeablauf geschaffen werden. Die mehrstufige Anmeldung wird vom HTTPMOD Modul über sid01 bis sid03 abgebildet. Die Keys werden dabei zwischen den Devices ausgetauscht und mit replacements in das HTML eingefügt. Das replacement ruft dafür noch die Funktion plenticore_auth() aus der 99_myUtils auf.
Automatischer Login mit Sessionaufbau
Der Ablauf startet mit einer beliebigen Abfrage und läuft dann vollautomatisch bis zur Ausführung des eigentlichen Aufrufes durch.
1. get WR_1_API 20_Statistic_EnergyFlow
2. Sollte noch keine Session aufgebaut sein erfolgt der Aufruf von sid01
3. Das Replacement %START% führt plenticore_auth("start","user","WR_1_API") aus
4. Die Rückmeldung vom Plenticore wird gelesen
5. Das Replacement %FINISH% führt plenticore_auth("finish","user","WR_1_API",...) aus
6. Die Rückmeldung vom Plenticore wird gelesen
7. Das Replacement %SESSION% führt plenticore_auth("session","user","WR_1_API",...,...) aus
8. Die Rückmeldung vom Plenticore wird gelesen und es sollte eine SessionId bestehen, mit der nun alle weiteren Abfragen laufen
9. Der eigentliche Aufruf wird ausgeführt und die readings bereit gestellt
Die Anmeldung kann auch teil automatisch erfolgen, was bei etwaigen Fehlern in der Anmeldung nützlich ist. Hier sind nur die manuellen Schritte beschrieben, der Ablauf ist identisch zur vorherigen Beschreibung.
1. get WR_1_API 01_auth_start
2. get WR_1_API 02_auth_finish
3. get WR_1_API 03_auth_create_session
4. Die Rückmeldung vom Plenticore wird gelesen und es sollte eine SessionId bestehen, mit der nun alle weiteren Abfragen laufen
Implementierte Abfragen
Sollte die Session abgelaufen, oder noch kein Login vollzogen worden sein, so wird automatisch ein Login mit Sessionaufbau durchgeführt.
get:
Login Funktionalität für eine manuelle Anmeldung
01_auth_start
02_auth_finish
03_auth_create_session
Auskunft über den Anmeldezustand
04_auth_me
Informationen zum Plenticore
05_info_version
Abfrage der Statistiken
20_Statistic_EnergyFlow
Informationen zum Speicher, die auch teilweise gesetzt werden können. Siehe '''set:'''
21_Battery_Information
22_Battery_InternControl
23_Battery_ExternControl
24_Battery_TimeControl
25_Battery_EM_State
Liste aller Plenticore Module. Es werden keine readings erzeugt. Das Ergebnis steht im httpbody, der mit "showBody" angezeigt werden kann. Es erfolgt keine Umsetzung des JSON in readings.
attr <Device> showBody 1
51_modules_list
Hier gibt es viele technische Loggings. Sollte es Probleme beim Abholen der Daten geben kann es am timeout liegen
attr <Device> timeout 7
59_logdata_download
Das zeigt den Firmware Update Status beim FW Laden an. Die Rückmeldung ist im httpbody zu sehen, es werden keine readings erzeugt.
60_update_status
set:
Bei den set Aufrufen ist automatisch ein get Aufruf im Anschluss gekoppelt, wodurch der neue Zustand wieder abgefragt wird.
Eine bestehende Session wird abgemeldet, als Rückmeldung erscheint im httpbody nur ein "null"
06_auth_logout
Die letzten Events können in deutsch oder englisch abgeholt werden
23_events latest_5 [en-gb,de-de]
Batterie Einstellungen können verändert werden. Es werden einige Werte vorgeschlagen. Bitte vorher immer den aktuellen Wert abfragen und besser aufschreiben!
Hier sollte nur etwas gesetzt werden, wenn man sich sicher ist, was man tut.
22_01_Battery_DynamicSoc_Enable
22_03_Battery_MinHomeConsumption
22_04_Battery_MinSoc
22_05_Battery_SmartBatteryControl_Enable
22_06_Battery_Strategy
22_07_Battery_Type [0,4]
23_00_Battery_ExternControl
23_01_Battery_ExternControl_AcPowerAbs
23_02_Battery_ExternControl_AcPowerRel
23_03_Battery_ExternControl_DcCurrentAbs
23_04_Battery_ExternControl_DcCurrentRel
23_05_Battery_ExternControl_DcPowerAbs
23_06_Battery_ExternControl_DcPowerRel
23_07_Battery_ExternControl_MaxChargePowerAbs
23_08_Battery_ExternControl_MaxDischargePowerAbs
23_09_Battery_ExternControl_MaxSocRel
23_10_Battery_ExternControl_MinSocRel
Hier kann eine Lister der letzten 5 Events abgeholt werden. Achtung lange Laufzeit, da immer die gesamte Liste abgerufen wird und dort sehr viele Einträge sind.
50_events_latest_5
RAW Definition des WR_1_API Master ab v1.16
Achtung, wenn Ihr zu dieser Definition wechselt, haben sich die get/set Bezeichnungen geändert. Bitte korrigiert dann auch das PV_Schedule Device entsprechend.
Beim Wechsel auf die v1.16 sind diverse Batterie Funktionalitäten hinzu gekommen, die man jedoch vom Installateur aktivieren lassen muss. Bitte denkt daran, dass der Installateur für jeden Wechsel der Batterie Konfiguration von intern auf extern und zurück, zu Euch kommen muss!
Die Abfrage der Settings für die Batterie ist nun in intern/extern gruppiert und es werden dann mehrere Settings gleichzeitig abgefragt. Beim Setzen hingegen erfolgt jedes Setting einzeln und das erfolgreiche Setzen ist am Ende mit einem erneuten get zu überprüfen. Stand 2021.04.07 im HTTPMOD wird ein Attribut set[*]FollowGet unterstützt, wodurch nun ein automatisches get nach dem set erfolgt. Dies vereinfacht das Timing, da das set bereits komplett erledigt wurde, bevor der Status neu abgefragt wird.
Die Nummerierung beim get/set soll die Zusammenhänge etwas klarer machen:
get 21_Battery_InternControl
set 21_04_Battery_MinSoc (mit automatischem FollowGet)
Achtung, Voraussetzung ist mindestens die HTTPMOD Version 4.1.00
Auch dieses Device ist für den Betrieb mehrerer Wechselrichter vorbereitet. Hierbei sollte der erste WR_1 und der zweite WR_2 benannt werden. Beim Betrieb nur eines Wechselrichters werden die userReadings SW_* ebenfalls befüllt und beinhalten die Werte des ersten Wechselrichters. Somit ist ein Betrieb in beiden Umgebungen möglich. Beim Logging muss man sich entscheiden und DbLogInclude entsprechend setzen, damit die Werte nicht doppelt ins DbLog geschrieben werden. Achtung, dies hat auch Auswirkungen auf die Diagramme.
Für die Korrektur der Statistiken werden die Stromzähler Stände des KSEM benötigt, die als reading mit Active_energy[+|-] bezeichnet sind. Bisher war es nicht erforderlich den KSEM im FHEM per ModBus abzufragen, was jedoch nun notwendig ist. In dieser Integration wurde der KSEM wegen des Schwarms auf WR_0_KSEM benannt. Im Device PV_Schedule wurde ein cmd_5 eingefügt, dass die Zählerstände zum Beginn einer Statistikperiode, Day/Month/Year in das Device WR_1_API überträgt. Initial müssen diese manuell korrekt gesetzt werden, damit die Berechnungen in den userReadings stimmen!
Die Definition des KSEM kommt etwas später im Kapitel Kostal_Smart_Energy_Manager_KSEM_Modbus_TCP
Sollte der Plenticore mal wegen eines Defektes ausgetauscht werden müssen, dann beginnt der neue Wechselrichter seine Statistiken wieder bei Null, was natürlich nicht so schön ist. Für die Tages Statistiken *_Day ist das nicht so tragisch, bei Monat und Jahr stört das schon einwenig. Um das dann kontinuierlich hin zu bekommen muss man aus der Datenbank die letzten gültigen Werte heraussuchen und diese manuell im userreading mit einrechnen. Zum Ende des Monats bzw. des Jahres sind dann die Offsets wieder heraus zu nehmen und gegebenenfalls nochmals in der Datenbank zu prüfen.
Betroffen sind die folgenden SW_* userreadings, die hier mit exemplarischen Zahlenwerten korrigiert werden.
Am besten macht Ihr Euch zum Ende der Periode einen Eintrag im Kalender, damit Ihr die Korrektur rechtzeitig wieder raus nehmt.
Bei allen anderen userreadings wurde ein solcher Wechsel bereits berücksichtigt und schreibt die Werte mit Hilfe der monotonik Funktion kontinuierlich weiter.
Initiales setzen der WR_0_KSEM Zähler Stände. Dies sollte nach einem save config durch das setstate, bei einem restart wieder richtig gesetzt sein. Das ganze neu berechnen der Statistiken ist nur ein Work around, bis Kostal das Problem in der Firmware korrigiert hat.
Erweiterung im WR_ctl, um die Zählerstände zu speichern
Im WR_ctl (DOIF im Perl Modus) gibt es einen Block für das Wiederherstellen der Zählerstände aus der DbLog, sofern das Monitoring bereits einen Jahres Zyklus gelaufen ist. Der Block 4_WR_1_API_init_Werte kann über das uiTable Pull Down Menü im WR_ctl im Bereich "WR_1_API Kommando Auswahl" auch manuell ausgeführt werden, was nach einem FHEM Absturz eventuell notwendig sein könnte. Die Notwendigkeit erkennt man an negativen Werten in der Spalte "aktuell".
DigitalOutputs schalten
Man kann den Schaltausgangdes Wechselrichtern mit "set 41_01_DigitalOutputs" auch direkt schalten.
Dies ist jedoch keine original Funktion des Wechselrichters, sondert manipuliert die Konfiguration des Schaltausgangs.
Testaufbau: Ne lange Leitung bis ins warme Büro und ein Durchgangsprüfer am Potentialfreien Relais Ausgang des Plenticore.
Nebenergebnis: Der Summer am 40 Jahre alten Messgerät ist kaputt und die Gewährleistung ist rum.
Es soll festgestellt werden, ob man das Relais mit den oben vorgegebenen Angaben Ein- und Ausschalten kann.
Ergebnis: Es funktioniert und konnte sogar noch vereinfacht werden, wenn man das "DigitalOutputs:Customer:ConfigurationFlags 0" verwendet. Alle anderen Parameter dienen lediglich der Basis Konfiguration und wurden im Test einfach per default mitgesendet. Nur "DigitalOutputs:Customer:ConfigurationFlags" wechselt, um das Ein- und Ausschalten zu erreichen.
Ausschalten wird mit der Sendefolge 9-0 erreicht und schaltet direkt nach der 9 ab. Die oben genannte Abschaltverzögerung wird anscheinend vom Flag 9 sofort zurückgesetzt. Das Senden von Flag 0 dektiviert die Digital Ausgang konfiguration und verhindert das erneute Einschalten durch die vorherige Sendung von Flag 9.
Einschalten erfolgt grundsätzlich mit einer minimum Verzögerung von 1 Minute. Der Startzustand ist Flag 0 und die Einschaltung erfolgt einfach mit Flag 9 (nach 1 Minute).
Umgesetzt habe ich das ganze in FHEM mit dem HTTPMOD Modul. Hier gibt es nun einen SET Aufruf, der das Flag inklusieve aller Konfigurations Parameter (siehe oben das json) übermittelt. Anschließend wird ein GET für diesen Aufruf gemacht, der den Status im FHEM aktualisiert.
Die sleep 1 sind hierbei nicht erforderlich, da die Kommunikation selber bereits syncronisiert ist und es sich beim Test gezeigt hat, das diese Verzögerung durch das GET ausreicht.
Generell ist natürlich nochmals zu erwähnen, dass es leider nicht möglich ist den Relais Status abzufragen. Die einzige Prüfung, die eventuell noch etwas mehr Sicherheit geben könnte wäre eine Nachverfolgung des Flags in einer Art Schrittschaltung. Hierbei könnte man wie oben erwähnt nach dem Senden des Flags eine direkte Abfrage anhängen und anschließend das nächste Flag senden und wieder abfragen.
Beispiel der Schaltreihenfolge:
9-0 => Aus, das Flag bleibt nun auf 0
9 => Ein nach 1 Minute
9-0 => Aus, das Flag bleibt nun auf 0
Durch die 9 schaltet es bereits ab, würde jedoch nach 1 Minute wieder an gehen.
Die 0 deaktiviert die Steuerung komplett.
Ich denke durch diese Vorgehensweise kann man auch die anderen Schaltkonfigurationen im Wechsel verwenden. Die Abschaltung sollte dann jeweils durch die Wiederholung der aktuell aktiven Konfiguration gefolgt vom Flag 0 erfolgen.
Anschließend kann dann die neu gewüschte Konfiguration folgen.
Somit wäre das Flag 0 dann auch das Signal für ein auf jeden Fall abgeschaltetes Relais.
RAW Definition des WR_2_API Slave ab v1.16
Auch hier kann man im Schwarm für den zweiten Wechselrichter eine verkürzte definition verwenden.
Beim Plenticore ist der Speicher an einem der Stringanschlüsse angeschlossen und die Steuerung obliegt direkt dem Wechselrichter. Aus diesem Grund ist eine Beeinflussung des Speicherverhaltens auch nur über den Wechselrichter möglich. Hierzu gibt es ab der Plenticore Version v1.16 zwei mögliche Schnittstellen. Die bisherige API Schnittstelle und auch die ModBus Schnittstelle, die nun auch das Setzen von Registern ermöglicht.
In der bisherigen Implementierung in FHEM wird die API Schnittstelle verwendet, da hierüber auch einzelne Funktionalitäten für den Betreiber möglich sind, die auch ohne die Freischaltung der Externen Speichersteuerung möglich sind. Tiefergehende Steuerungen bedürfen der Freischaltung durch den Installateur.
Die bisherige Steuerung des Speichers war im PV_Schedule Device implementiert, was sich jedoch als recht unübersichtlich erwiesen hat. Aktuell wurde ein weiteres DOIF Device eingerichtet, das nun die umfangreichere Bedienung der externen Speichersteuerung übernommen hat. Die Konfiguration wurde ebenfalls wieder in einem DUMMY abgebildet.
Batteriesteuerung Möglichkeiten
Speicher Basissteuerung
Einige Speichersteuerungen werden als Grundlegend angesehen und sind deshalb immer aktiv. Diese Funktionalität ist auch ohne die externe Speichersteuerung für den Betreiber möglich.
Die Steuerung von MinSOC und MinHomeConsumtion ist über das device WR_1_API auch für den Anlagenbetreiber möglich.
Bei einem schlechten Forecast, z.B. im Herbst/Winter, wurde ein Vorschlag aus dem Photovoltaikform umgesetzt.
Die Batterie wurde bisher am Tag immer kurz geladen, dann wieder durch z.B. eine Wärmepumpe geleert und das immer im Wechsel.
Da im Winter ja eh Strom zugekauft werden muss wird nun die Batterie einfach stetig mit dem Überschuss, der nicht sofort verbrauchen kann, geladen.
Dieser Vorgang wurde als smart_laden und laden_beendet implementiert. Nun ist Ruhe eingekehrt :-),
Laut dem EFT Service ist es zwar kein Problem die Batterie einfach immer wieder kurz zu laden und sofort wieder zu entladen, doch die jetzige Variante sieht etwas schonender aus.
Das könnte sich dann in längerer Lebenszeit bemerkbar machen, was man aber erst in 10 Jahren behaupten könnte :-)
Umsetzung:
Bei MinSOC 15% wird MinHomeConsumtion auf den Wert Battery_Info_WorkCapacity gesetzt und die Batterie kann schön den ganzen Tag laden, oder im Forum vorgeschlagen bis SOC 90% .
Dadurch, dass MinHomeConsumtion auf die Maximalleistung gesetzt wurde würde die Batterie erst bei diesem Wert entladen dürfen, was eher unwahrscheinlich ist. Wichtig ist nur, dass der gesetzte Wert höher ist, als der zu erwartende Maximalverbrauch des Hauses. Setzt man MinHomeConsumtion auf einen Wert, der den Verbrauch eines Großverbrauchers im Haus nahe kommt, dann würde nur dieser unterstützt. Das nur als Randbemerkung.
Nebeneffekte:
- Das Laden der Batterie kann somit auch mal Tage dauern, wenn zu wenig PV Leistung vom Dach kommt.
- Es wird vermutet, dass der MinSoC Wert bei geringer Leistung in der Batterie ungenau wird,
aber bei Ladung bis 100% exakter berechnet wird. MinSoC ist keine exakte Wissenschaft.
- Stellt der Kontroller der Batterie fest, das die Leistung in der Batterie einen Grenzwert unterschritten hat,
wird der MinSoC korrigiert und es kommt zu dem Effekt, das mitten in der Nacht der MinSoC auf einmal
nach unten fällt. Damit wird dem Wechselrichter signalisiert, das nachgeladen werden muss, um MinSoC
wieder zu erreichen. Das Resultat ist die bekannte Notladung in der Nacht aus dem Netz.
MinSOC Sommer/Winter Umschaltung
Dies gehört zur Basissteuerung und schaltet den MinSOC von Sommer auf Winterbetrieb, um eine Notladung aus dem Netz zu vermeiden.
Aktivierung: Ist immer aktiv
Aktivität : Im Speicher wird MinSOC verändert
Konfiguration:
Die MinSOC Werte wurden vom Hersteller empfohlen
SpeicherMinSOC_Sommer 5
SpeicherMinSOC_Winter 20
Der nächste Schwellwert sollte so gewählt werden, ab welcher Forecast Leistung der Speicher noch genügend nachgeladen wird, sodass es keine Notladung in der Nacht gibt. Es ist normal, dass der Speicher hierbei nicht den Bedarf der Nacht decken kann (Herbst/Winter). Das Nachladen sollte jedoch für die Deckung des Eigenverbrauchs vom Plenticore reichen.
SpeicherMinSOC_fc1_Limit 13000
Zeit Steuerung eines Lade-/Entladefensters (Tarifsteuerung)
Für diejenigen, die einen Zeittarif ihres EVU haben, ermöglicht dies ein Zeitfenster zu definieren, in dem der Speicher z.B. bei einem hohen Tarif entladen werden darf. Außerhalb dieser Zeit wird dann das Entladen gesperrt, damit der günstige Tarif genutzt werden kann.
Achtung, in der Schweiz ist es verboten den Speicher aus dem Netz zu laden
Für die Verwendung der DOIF Zeitsteuerung ist hier DOIF_Zeitsteuerung eine sehr gute Beschreibung zu finden.
Aktivierung über WR_1_Speicher_1_ExternControl
Um die Zeitsteuerung zu verwenden werden im WR_1_Speicher_1_ExternControl Device folgende readings gesetzt.
SpeicherEntladung:Automatik,Zeit,SpeicherTrigger
Automatik - Der Speicher wird vom Wechselrichter gesteuert, oder über die eigene ExternControl der API
Zeit - Das Laden und Entladen wird mit den Zeitwerten beeinflusst
SpeicherTrigger - beeinflusst das Laden und Entladen direkt ohne die Zeitsteuerung
SpeicherZeitEnde/SpeicherZeitEnde
Die Zeitangaben können manuell fest gesetzt werden, oder über zusätzliche Timer täglich neu überschrieben werden.
Eine gültige Zeit und entsprechendes Timeing obliegt dem Anwender.
Zwischen Start und Ende wird der Speicher zum Entladen freigegeben und zwischen Ende und Start gesperrt.
Um ein flexibles Zeitfenster zu nutzen, ist es möglich die Zeiten z.B. durch ein DOIF oder einen WeekdayTimer dynamisch zu verändern. Hierbei liegt die logische Kontrolle beim Anwender, der für plausible Änderungen zuständig ist.
PV_1_EVU_Tarif_1 WeekdayTimer (Beispiel)
Es gibt hierbei zwei Möglichkeiten, eine Zeitsteuerung umzusetzen, die etwas unterschiedliche Verhaltensweisen ermöglichen.
Nutzung von "SpeicherEntladung Zeit":
In diesem Beispiel wird der frei verwendbare Trigger für die Zeitsteuerung verwendet. Hierbei muss jeder Wechsel zwischen entladen und gesperrt über das reading "SpeicherTrigger [entladen|gesperrt]" gesteuert werden. Ein zurückfallen in ein Zeitfenster ist nicht möglich.
Nutzung von "SpeicherEntladung Trigger":
################################################################################################################## 1 Setzen des niedrig Tarifs. Das WR_1_Speicher_1_ExternControl DOIF reagiert dann auf das Zeitfenster und## aktiviert von 07:00 - 19:00 Uhr den Speicher zum Entladen([07:00|8])(setreadingWR_1_Speicher_1_ExternControlSpeicherZeitStart07:00)(setreadingWR_1_Speicher_1_ExternControlSpeicherZeitEnde19:00)(setreadingWR_1_Speicher_1_ExternControlSpeicherEntladungZeit)################################################################################################################## 2 Setzen des hohen Tarifs. Das WR_1_Speicher_1_ExternControl DOIF reagiert dann auf das Zeitfenster und## aktiviert von 00:00 - 23:59 Uhr den Speicher zum EntladenDOELSEIF([00:00|7])(setreadingWR_1_Speicher_1_ExternControlSpeicherZeitStart00:00)(setreadingWR_1_Speicher_1_ExternControlSpeicherZeitEnde23:59)(setreadingWR_1_Speicher_1_ExternControlSpeicherEntladungZeit)################################################################################################################## 3 Hier könnte dann die Berechnung der überschüssigen Zeit rein, wenn Du mit 40% aus der Nacht kommen würdestDOELSEIF(***)(setreadingWR_1_Speicher_1_ExternControlSpeicherZeitStart[neuberechneteSpeicherZeitStart])(setreadingWR_1_Speicher_1_ExternControlSpeicherEntladungZeit)## Triggert den ersten Event, damit die Zeit sofort aktiv wird.
Speicherentladung mit Zeit und Trigger (Beispiel)
Dies ist ein spezielles Beispiel, bei dem die Nutzung von "SpeicherEntladung Zeit" mit "SpeicherEntladung SpeicherTrigger" gemeinsam verwendet wird.
Die Basis ist wie oben beschrieben die Tarifsteuerung mit "SpeicherEntladung Zeit" dies wird durch einen oder mehrere WeekdayTimer mit Zeiten eingestellt. Nach dem setzen einer Zeit wird dies mit "fhem("set $NAME SpeicherEntladung Zeit")" aktiviert. Soll nun innerhalb des entlade Fensters der Speicher gesperrt werden, so kann man den SpeicherTrigger auf gesperrt setzen und dies dann mit "fhem("set $NAME SpeicherEntladung SpeicherTrigger")" aktivieren. Später lässt sich das dann wieder deaktivieren und die Zeitsteuerung läuft wieder weiter.
1.) Durch einen Timer wird folgendes täglich um 00:01 Uhr gesetzt
setreading WR_1_Speicher_1_ExternControl SpeicherZeitStart 07:00"
setreading WR_1_Speicher_1_ExternControl SpeicherZeitEnde 16:00"
setreading WR_1_Speicher_1_ExternControl SpeicherEntladung Zeit
Vor 07:00 Uhr ist der Speicher gesperrt, zwischen 07:00 und 16:00 Uhr geht er auf entladen, danach wieder auf gesperrt.
2.) Mit einem Trigger wird der Speicher in der definierten entlade Zeit für kurze Zeit gesperrt
setreading WR_1_Speicher_1_ExternControl SpeicherTrigger gesperrt
setreading WR_1_Speicher_1_ExternControl SpeicherEntladung SpeicherTrigger
Dadurch wird der Speicher zu einem beliebigen Zeitpunkt auf gesperrt gesetzt, egal wie die Situation der Zeit Steuerung ist.
3.) Rückfall zur vorher eingetragenen Zeit Steuerung
setreading WR_1_Speicher_1_ExternControl SpeicherEntladung Zeit
Der SpeicherTrigger wird abgeschaltet und die zeit Steuerung wieder aktiviert. Es gelten die zuvor eingetragenen Zeiten und der Speicher geht je nach Zeitfenster auf entladen oder gesperrt.
'''Wichtig ist hierbei, dass man den gewünschten Zustand zuerst setzt, bevor man die jeweilige Steuerung aktiviert.''' Ansonsten treten kurze Zustandswechsel auf, die vermieden werden können.
Externe Speichersteuerung (ExternControl)
Für die erweiterte Steuerung muss die externe Speichersteuerung des Plenticore aktiviert werden.
Externe Speichersteuerung Ladekontrolle
Dies ist eine kurz Beschreibung für einen kompletten Ladezyklusablauf
- Morgens wird geprüft, wie gut der Haushalt durch die Nacht gekommen und wie hoch der MinSOC noch ist
- Reserve ist 3x MinSOC, also im Sommer 15% und im Winter 60% (im Winter ist der Speicher eh morgens leer :-) )
- Dann wird ein MaxSOC für den Tag berechnet
- Bis zum Reserve SOC wir morgens sofort geladen
- Danach wird einstellbar mit geringer Leistung am Vormittag bis SOC 30% geladen
- Innerhalb des Mittagshoch, so meistens dynamisch in der Zeit von 10:00 - 16:00 Uhr
wird mit berechneter Leistung bis zum MaxSOC geladen
- Man kann auch eine feste Ladeleistung für das Mittagshoch angeben
- Am Nachmittag wird der MaxSOC dann weiter gehalten
- Wird der Speicher am Nachmittag, wegen schlechtem Wetter verwendet, wird der MaxSOC auf 100% angehoben
- Bei erreichen von SOC 100% wird eine Begrenzung auf MaxSOC 95% durchgeführt, damit nicht ständig nachgeladen wird
- Spätestens am Abend wird nochmals der MaxSOC von diesem Tag gemerkt, der Zeitpunkt variiert mit der Jahreszeit
- Nach ungefähr einer Woche gibt es, wegen des im Speicher berechneten SOC, morgens einen schnellen Abfall
der Entladeleistung. Dadurch fällt der SOC dann so niedrig, dass die Steuerung an diesem Tag auf 100% auflädt,
wodurch der Speicher dann intern den SOC wieder richtig berechnet
Und schon geht das Spiel von vorne los.
MaxSOC Kontrolle
Es wird versucht, den Speicher am Abend nicht zu 100% zu laden, aber morgens noch mit 3* MinSOC aus der Nacht zu kommen.
Aktivierung: SpeicherMaxSOCControlActive An
Aktivität : SpeicherMaxSOCControlRunning Aus = momentan keine Steuerung
SpeicherMaxSOC_Actual 100 <<< wird berechnet, kann jedoch für diesen Tag anschließend überschrieben werden
SpeicherMaxSOC_DayBefore 100 <<< wird berechnet und dient als Merker
Konfiguration:
Dieser Wert muss auf die Anlage angepasst werden und dient als Schwellwert. Die Leistungsangabe soll so hoch sein, dass der Speicher, bei diesem Forecast, auf jeden Fall mit einer Überschussleistung von 3* MinSOC aus der Nacht kommt. Im Frühjahr ist dies besonders gut abzuschätzen, sobald eine Wärmepumpe nachts nicht mehr heizt und nur noch die Grundversorgung in der Nacht aus dem Speicher gedeckt werden muss.
Durch die Abfrage des Forecasts von morgen ist hierbei berücksichtigt, dass bei einer schlechten Prognose die Speicherladung nicht limitiert wird, was auch im Sommer mal der Fall sein kann.
SpeicherMaxSOC_fc1_Limit 30000
Middayhigh Kontrolle
Über die KI_Prognose() Funktion wird ein Middayhigh ermittelt, wenn der WR nur 70% einspeisen darf. Auch beim Betrieb von zwei Wechselrichtern ist dies wichtig, da mit Stand 03/2021, der Plenticore den Hausverbrauch nicht korrekt ermitteln kann. In diesem Fall funktioniert die "intelligente Batteriesteuerung" nicht mehr und muss deaktiviert werden. Durch die Middayhigh Kontrolle wird aus dem Forecast, durch die Funktion KI_Prognose() eine Überschreitung von Inverer_Max_Power (70%) Regelung ermittelt. Damit kann über die externe Speichersteuerung die Hauptladung des Speichers in die Mittagszeit verlagert werden, um die dynamische 70% Regelung zu nutzen, bevor der Wechselrichter abgeregelt wird.
Die KI_Prognose Werte werden in dem WR_ctl Device abgelegt und sind dort als Yield_fc* zu finden.
Aktivierung: SpeicherMiddayControlActive An
Aktivität : SpeicherMiddayControlRunning Aus <<<< momentan keine Steuerung
WR_ctl:Yield_fc0_middayhigh 0 <<<< Es gibt kein Mittags Hoch. Wird aus KI_Prognose() gesetzt
WR_ctl:Yield_fc0_middayhigh_start 00:00 <<<< wird aus dem Forecast in KI_Prognose() berechnet
WR_ctl:Yield_fc0_middayhigh_stop 00:00 <<<< wird aus dem Forecast in KI_Prognose() berechnet
Konfiguration:
Der Wert von SpeicherMidday_MaxChargePowerAbs sollte so gewählt werden, dass der Speicher am Vormittag langsam und gleichmäßig bis auf SpeicherMidday_MaxSOC geladen wird.
Ab der WR_ctl:Yield_fc0_middayhigh_start wird dann unlimitiert bis zur WR_ctl:Yield_fc0_middayhigh_stop in der Mittagszeit weiter geladen. Wenn SpeicherMaxSOCControlActive auf 1 ist, wird hierbei weiterhin das Laden limitiert.
SpeicherMidday_Inverter_Max_Power 8500 <<<< hier kann man manuell einen Wert festlegen, wenn es keine 70% Regelung gibt
SpeicherMidday_MaxChargePowerAbs_midday 1000 <<<< hängt vom Speicher ab; Wird der wert auf 0 gesetzt, so erfolgt eine dynamische Berechnung zur Laufzeit
SpeicherMidday_MaxChargePowerAbs_morning 450 <<<< hängt vom Speicher ab
SpeicherMidday_MaxSOC 30 <<<< Es soll nur bis dahin geladen werden, damit Mittags genug Platz ist
RAW Definition des WR_1_Speicher_1_ExternControl
Achtung, es wurde eine Sperre beim Betrieb einer WallBox eingebaut, die natürlich jeder für sich anpassen, oder gegebenenfalls entfernen muss.
- Block 2_smart_Laden_start_WB_1
- Block 3_smart_Laden_beenden_WB_1
- Im Block 3_smart_Laden_beenden_Automatik ist eine zusätzliche Bedingung
and\
[$SELF:WB_1_smart_laden_before] eq "---" ## Es wird gerade kein Fahrzeug geladen\
- setstate WR_1_Speicher_1_ExternControl WB_1_smart_laden_before ---
Der Ladezustand wird hierbei von der WallBox abgefragt und unterscheidet sich bei verschiedenen Herstellern:
Hier bitte gerne noch weitere WallBoxen bei mir melden.
Sollte man mehrere AC Quellen im Haus haben werden die Messwerte benötigt, um den Hausverbrauch richtig zu berechnen.
Um den ModBus am KSEM zu nutzen muss man im ModBus Menü die Option "Slave" aktivieren.
Auch hier ist ein Interval von 60 Sekunden gesetzt worden. Das Logging ist auf Active_energy.* eingeschränkt, weshalb man seine zusätzlichen Werte noch selber definieren muss.
RAW Definition des KSEM
Zun Thema KSEM bestand direkter Kontakt mit dem Kostal Service. Der KSEM ermittelt nicht alle Werte, welche in der SunSpec spezifiziert sind. Alle nicht unterstützen Werte sind in den Registern mit 0x8000 gekennzeichnet. Für die nicht unterstützten Zählerstände wird 0x800000000 ausgegeben.
Der Summenstrom M_AC_Current (sum of active phases) kann aber durch den Endanwender selber aus der Summe der Einzelwerte (Phase A AC current, Phase B AC current Phase C AC current) berechnet werden. Die einzelnen Spannungen zwischen den Phasen können nicht gemessen werden und werden deshalb nicht ausgegeben.
Das Device wurde umbenannt, um es besser in die gesamt Implementierung einzugliedern.
- WR , es wird vom Wechselrichter benötigt und sortiert sich im FHEM Web auch dort ein.
- 0 , es wird von mehreren Geräten benötig, was z.B. auch eine Wallbox sein kann. WR_[1|2] könnte bedeuten, dass es nur von diesem Gerät benötigt wird.
Bei einer Schwarm Installation steuert der KSEM mehrere Wechselrichter bezüglich der 70% Regelung. Eine Wallbox benötigt ebenfalls eine Verbindung, wenn nur mit Überschuss geladen werden soll.
Auch wenn der KSEM im FHEM auf disable 1 steht ist er aktiv und steuert die Wechselrichter und Wallboxen. Es bedeutet nur, dass die Werte nicht zusätzlich im Fhem eingelesen werden. Der Plenticore bereitet die Daten bereits selber auf und liefert diese im WR_1 Device per ModBus bereits mit.
Da es beim Plenticore ein Problem mit den Statistiken im Schwarm gibt wird das Device WR_0_KSEM nun aktiv verwendet. Durch das Device PV_Schedule werden die Werte Active_energy[+|-] ins Device WR_1_API übertragen und bilden die initial Werte für die Day/Month/Year Statistiken.
Device über Device Hardware Protokoll Netzwerk Informationen
WR_0_KSEM WR_1 KSEM MODBUS LAN Messwerte vom Netzanschlusspunkt
WR_1 rs485 4 Draht zum WR Notwendig, wenn ein Speicher am Plenticore betrieben wird
WR_1 Plenticore MODBUS LAN Messwerte und berechnete Werte, teilweise Speicher Informationen
WR_1_API HTTPMOD LAN Statistiken, Speichersteuerung und Informationen
WR_2 Plenticore MODBUS LAN Messwerte und berechnete Werte. Achtung, im Schwarm hat nur der Master WR einen Speicher.
WR_2_API HTTPMOD LAN Statistiken
FHEM Steuerung MODBUS LAN Laufende Informationen im Minuten Takt
HTTPMOD LAN Abfragen und Steuerung einzelner Devices
WR_ctl DOIF Startet regelmäßige Aktionen zur zeitlichen Steuerung der PV-Anlage
Dient der Anzeige von aktuellen und Statistischen Daten
1 Stündlich
1.1 WR_2_API 20_Statistic_EnergyFlow Statistiken vom Plenticore abholen; die Reihenfolge ist auch wichtig!
1.2 WR_1_API 20_Statistic_EnergyFlow Statistiken vom Plenticore abholen
2 Stündlich von 05:00 bis 22:00
2.1 KI_Prognose() für fc0 und fc1 Aktualisieren der fc0 Prognose
3 kurz nach Mitternacht
3.1 4_WR_1_API_init_Werte Lesen und eventuell korrigieren der init Werte
WR_1_Speicher_1_ExternControl DOIF Externe Speichersteuerung
1 Stündlich
1.1 WR_1_API 21_Battery_Information Allgemeine Informationen
Battery_Info_SoC,
Battery_Info_WorkCapacity
1.2 WR_1_API 22_Battery_InternControl Speicher Information der Internen Steuerung
Battery_InternControl_MinSoc,
Battery_InternControl_MinHomeConsumption
1.3 WR_1_API 23_Battery_ExternControl Speicher Information der Externen Steuerung
1.4 WR_1_API 25_Battery_EM_State Speicher Status z.B. "Normal"
2 Unterschreitung des MinSOC im Winter
2.1 smart_laden PV Überschuss wird in Batterie geladen. Keine Entladung
2.2 WR_1_Speicher_1_ExternControl ExternTrigger gesperrt Batterie ExternTrigger, Entlademodus gesperrt . Die Zeit Steuerung wird verriegelt
3 Freigabe zur Entladung im Winter
3.1 bei überschreiten von SOC 90% Sobald der Speicher gut gefüllt ist oder
WR_1_API:Battery_Info_SoC
3.2 Bei Zeitsteuerung und guter Prognose mit SOC 40% bereits vorher, weil der Tarif teuer ist
4 Speicher Freigabe bei Trigger oder Zeit Steuerung Dieses cmd_ löst die Abhängigkeiten von Zeit und Trigger auf
5 Speicher sperren bei Trigger oder Zeit Steuerung Dieses cmd_ löst die Abhängigkeiten von Zeit und Trigger auf
6 Wiederhole alle 180s die Kommandos der ExternControl Steuerung Wenn keine Wiederholung erfolgt geht der Plenticore wieder auf die interne Steuerung
6.1 WR_1_Speicher_1_ExternControl:SpeicherMiddayControlActive wird durchlaufen, wenn der Forecast eine z.B. 70% Überschreitung erkannt hat und
WR_1_Speicher_1_ExternControl:SpeicherMidday_MaxSOC begrenzt dann morgens den MaxSOC
WR_1_Speicher_1_ExternControl:SpeicherMidday_MaxChargePowerAbs_midday und den MaxChargePowerAbs
WR_1_Speicher_1_ExternControl:SpeicherMidday_MaxChargePowerAbs_morning für morgens und mittags
6.2 WR_1_Speicher_1_ExternControl:SpeicherMaxSOC Wenn morgens der Speicher zu voll war, wird der MaxSOC bis abends begrenzt
6.3 WR_ctl:Yield_fc0_middayhigh_start <> WR_ctl:Yield_fc0_middayhigh_stop Das Laden wird mit voller Leistung freigegeben
6.4 nach Ablauf von WR_ctl:Yield_fc0_middayhigh_stop Die Midday Steuerung wird abgeschaltet, es wird normal weiter geladen, bis MaxSOC erreicht ist
6.5 WR_1_Speicher_1_ExternControl:SpeicherMaxSOC Batterie MaxSOC halten, der Default ist 100%
7 Initialisierung der externen Speichersteuerung
7.1 Ist morgens der Speicher zu voll Wenn der Speicher morgens voller als 3x MinSOC ist wird MaxSOC gesetzt
7.2 Wenn eine Überschreitung der 70% erwartet wird Aktivierung der Midday Steuerung
8 Zurücksetzen der externen Speichersteuerung
9 Umschaltung des MinSOC wenn zu wenig Leistung erwartet wird Schaltet im Herbst/Winter den MinSOC auf 20%
10 Umschaltung des MinSoc wenn viel Leistung erwartet wir Setzt den MinSOC wieder im Frühling/Sommer auf 5%
11 WR_1_Speicher_1 Status aktualisieren Nur beim BYD HV, Abfrage der Speicher Detailinformationen. Kann einfach entfernt werden
WR_1_Speicher_1_ExternControl readings Konfiguration für die externe Speichersteuerung
ExternTrigger none Wird automatisch gesetzt und dient der Verriegelung im WR_1_Speicher_1_ExternControl
Zustände: frei/gesperrt/none
SpeicherCmdRepeatActive [An|Aus] An/Aus Trigger für WR_1_Speicher_1_ExternControl, aktiviert die Kommandowiederholung
SpeicherMaxSOCControlRunning [An|Aus] Das reading signalisiert den aktuellen Laufzeitstatus
SpeicherEntladung Steuert den Modus der externen Speichersteuerung
Zustände:
Automatik - MinSOC Steuerung Sommer/Winter
Zeit - z.B. bei Tarifsteuerung. Zeiten werden über zusätzliche DOIF
oder WeekdayTimer gesetzt
Trigger - Ein beliebiger Mechanismus z.B. DOIF steuert den Speicher
SpeicherMaxSOCControlActive [An|Aus] Wird von KI_Prognose() gesetzt, wenn der fc0 über WR_1:Inverter_Max_Power (70% Regel) liegt
SpeicherMaxSOCControlRunning [An|Aus] Das reading signalisiert den aktuellen Laufzeitstatus
SpeicherMaxSOC_Actual 100 Wird im WR_1_Speicher_1_ExternControl cmd_7 berechnet, wenn der Speicher morgens zuviel Ladung hat
SpeicherMaxSOC_DayBefore xx Das ist der letzte berechnete Wert, damit sich die Berechnung langsam einem Optimum nähern kann
SpeicherMaxSOC_fc1_Limit 30000 Hier einen Wert setzen, bei dem der Speicher über Nacht bis morgens gereicht hat.
Damit wird dann der Übergang vom Winter zum Frühjahr erkannt.
SpeicherMiddayControlActive [An|Aus] Wird von KI_Prognose() gesetzt, wenn der fc0 über WR_1:Inverter_Max_Power (70% Regel) liegt
SpeicherMiddayControlRunning [An|Aus] Das reading signalisiert den aktuellen Laufzeitstatus
SpeicherMidday_Inverter_Max_Power 8500 Manuelles überschreiben für WR_1:Inverter_Max_Power, wenn man kontrollierter den Tag über laden möchte
SpeicherMidday_MaxChargePowerAbs_midday 1000 Begrenzung der Ladeleistung am Mittag, damit nicht zu schnell geladen wird.
Steht der Wert auf 0 wird dynamisch während der Laufzeit ein Wert berechnet.
SpeicherMidday_MaxChargePowerAbs_morning 450 Begrenzung der Ladeleistung am Vormittag, damit Mittags genug Platz im Speicher ist
SpeicherMidday_MaxSOC 30 Limitierung des Speichers um Mittags genug Platz zu haben
SpeicherMinSOC_Sommer 5 Sommer MinSOC von Kostal vorgegeben
SpeicherMinSOC_Winter 20 Winter MinSOC von Kostal vorgegeben
SpeicherMinSOC_fc1_Limit 14000 Wenn im Herbst/Winter der Forecast zu schlecht wird muss dieser Wert auf die Anlage
angepasst werden. Das signalisiert die Winter Zeit
SpeicherTrigger none entladen/gesperrt/none wird über WR_1_Speicher_1_ExternControl gesetzt
SpeicherZeitEnde 16:00 Die Zeiten geben das Entlade Fenster an und werden durch weitere DOIF oder WeekdayTimer gesetzt
SpeicherZeitStart 07:00 Dies kann zur Tarifsteuerung verwendet werden, oder um ein Entladung zeitlich zu verschieben
Das Zeitfenster kann durch den MinSOC Schutz im Winter veriegelt sein.
Beispiele:
1 SpeicherEntladung Automatik
Nur grundlegende Steuerungen erfolgen automatisch.
- Sommer/Winter Umschaltung des MinSOC Schützt den Speicher vor einer Notladung im Winter
- smart_laden Sorgt dafür, das der Speicher im Winter nicht ständig geladen und wieder entladen wird
- laden_beendet Gibt den Speicher nach dem smart_laden wieder frei
2 SpeicherEntladung Zeit
Zeitsteuerung für laden/entladen
- WR_1_Speicher_1_ExternControl:SpeicherZeitEnde/SpeicherZeitStart Die Start/Ende Zeiten müssen gesetzt werden, dies muss über weitere DOIF oder WeekdayTimer erfolgen.
- Sommer/Winter Umschaltung des MinSOC
- smart_laden
- laden_beendet
Wenn man mal etwas umbenennen möchte
Es kommt immer wieder vor, dass man ein Device oder den Namen eines readings umbenennen möchte. Dies hat natürlich Auswirkungen auf andere Devices und auch auf die bisherigen Daten in der DbLog. Hier sollen dann jetzt Hilfestellungen gesammelt werden.
Allgemeine Hilfestellungen
Es sollte immer vorher eine Datensicherung gemacht werden!
Den Device Namen ändert man am Besten mit einem "rename".
Damit nichts vergessen wird ruft man den RAW Editor auf und kann dann mit der Suchfunktion des Browsers nach dem zu ändernden Text suchen.
Wenn alle Devices im ersten Durchlauf geändert wurden und man meint man wäre fertig, dann durchsucht man am besten nochmal die fhem.cfg . Sollten dort noch alte Namen vorhanden sein, kann man erkennen in welchem Device das ist und dieses dann in der Fhem Oberfläche korrigieren.
Bitte nicht in der fhem.cfg Änderungen vornehmen! Dort nur zur Kontrolle suchen.
Ein Device umbenennen
Das ist schnell gemacht, indem man in der Fhem commandline ein "rename <Device> <neues Device>" macht.
Es ist auch möglich das alte Device mit "disable 1" zu deaktivieren und dann einfach ein komplett neues z.B. aus dem Wiki zu definieren.
Das alte Device kann dann später gelöscht werden, sobald das neu richtig läuft. In der datenbank kann man die alten Werte dann auch wieder dem neuen Device zuordnen.
Als nächstes haben viele Devices noch ein Attribut "alias", das meistens den selben Namen wie das Device beinhaltet.
Ein Device Name kann auch in anderen Attributen als Variable verwendet worden sein. Das ist zu prüfen.
Nun werden alle neuen, aktualisierten readings unter dem neuen Device Namen in die Datenbank geschrieben.
Die bisherigen Log Einträge müssen nun noch dem neuen Device zugeordnet werden.
Ein reading umbenennen
Dies geschieht innerhalb des Devices, indem man das Attribut, dass das reading erzeugt ändert und den neuen Namen einträgt.
Bei der nächsten Aktualisierung erscheint dann ein zweites reading mit dem neuen Namen.
Der neue Name muss dann noch an allen Stellen innerhalb des Devices eingetragen werden, Das kann im userReading, stateFormat oder auch in anderen Attributen der Fall sein.
Soll dieses Reading gelogged werden, ist "DbLogInclude" zu prüfen. Der alte Name kann raus und der neue muss rein, oder die RegEx muss geändert werden.
Zum Schluss muss das alte reading noch entfernt werden, was mit "deletereading <Device> <alter reading Name>" erfolgen kann. Oft ist hier auch eine RegEx möglich.
DbLog aufräumen
Als kleine Vorabinformation möchte ich geben, dass es hierbei eventuell zu duplicate keys kommen kann. Dies rührt daher, dass eventuell der alte und der neue Namen parallel geloggt wurde. Schaut Euch hier die Daten an, welche Ihr behalten möchtet, oder ob Ihr wirklich z.B. das alte reading und SW_* braucht. Ab dem Zeitpunkt wo es parallel gelaufen ist, wäre dann eins (das alte) zu löschen.
Beim Übergang zum Schwarm habe ich alle älteren Daten den neuen readings zugeordnet und momentan, ab diesem Zeitpunkt, beides gelogged.
An der Datenbank anmelden
Dies kann man z.B. aus einer Terminal Session heraus machen.
Mit einem SELECT kann man alle bisher aufgetretenen readings eines Devices über einen definierten Zeitraum anzeigen lassen.
Der Zeitraum ist mit "1 DAY" definiert, kann aber auch auf z.B. "1 MONTH" oder "2 MONTH" gesetzt werden.
In diesem Beispiel würde das alte DEVICE PV_1 dem neuen DEVICE WR_1 zugeordnet werden.
Im Anschluss müssten dann noch READING jeweils einem eventuell neuen READING Namen zu geordnet werden.
Sehr wichtig ist "TIMESTAMP = TIMESTAMP", da hierdurch der alte TIMESTAMP erhalten bleibt.
Natürlich kann man die vorherigen UPDATE auch zusammenfassen, also DEVICE und READING in einem ändern. Das sollte aber nur machen, wer in SQL entsprechende Kenntnisse hat.
Die Umbenennung des DEVICE zum neuen DEVICE und anschließend die READING Namen ist der praktikabelste Weg.
Grafiken korrigieren
Grafana
In Grafana sind die SQL Abfragen ebenfalls zu korrigieren
Fhem Log
Das Fhem Log ist nach jedem größeren Änderungsschritt zu sichten, da man hier ziemlich schnell vergessene Devices oder readings erkennen kann.
Timeing für die PV extra Funktionen
RAW Definition WR_ctl (DOIF)
Das WR_ctl Device hat die zeitliche Steuerungder PV-Anlage übernommen und dient gleichzeitig der Anzeige von aktuellen und statistischen Werten im FHEMWEB.
Achtung, es gab eine Umstellung mit diesem Device! Die Bilanz wird nun direkt im WR_ctl Device mit uiTable angezeigt.
Die Energie Bilanz soll einen kompakten Überblick über die Produktions- und Verbrauchswerte liefern. Hierbei werden die momentan Werte direkt berechnet, die restlichen Werte werden als Statistiken aus dem Gerät abgefragt. Mit DbRep Devicen kann man auch Vortag/Vormonat/Vorjahr im Wr_ctl direkt mitanzeigen lassen.
Erstellen von zusätzlichen Werten in der Datenbank
Hier werden Werte konsolidiert, weil z.B. der Wert PV_total_Month stetig steigt. Am Ende des Monats sind die gesamten Zwischenwerte ohne Aussagekraft und werden dann später mal gelöscht.
RAW Definition LogDBRep_Statistic_EnergyFeedInGrid_Year_diff_Week
Dieser Wert ist die wöchentliche Einspeisung ins Netz.
Gestartet über DB_Service_Schedule am ersten Tag der Folgewoche.
RAW Definition LogDBRep_Statistic_EnergyHomePvSum_Year_diff_Week
Dieser Wert ist der gesamte Verbrauch aus der PV Anlage inklusive Batterie pro Woche an.
Gestartet über DB_Service_Schedule am ersten Tag der Folgewoche.
Benötigt für SVG_LogDB_PV_Bilanz.
RAW Definition LogDBRep_Statistic_EnergyHomePvSum_Year_max_Month
Dieser Wert ist der gesamte Verbrauch aus der PV Anlage inklusive Batterie im Monat.
Gestartet über DB_Service_Schedule am ersten des Monats.
Benötigt für SVG_LogDB_PV_Bilanz.
RAW Definition LogDBRep_Statistic_Yield_Year_diff_Week
Dieser Wert gibt den gesamten Ertrag der PV Anlage pro Woche an.\
Gestartet über DB_Service_Schedule am ersten Tag der Folgewoche.\
Benötigt für SVG_LogDB_PV_Bilanz.
RAW Definition LogDBRep_Statistic_Yield_Month_max_Month
Dieser Wert gibt den gesamten Ertrag der PV Anlage im Monat an.
Gestartet über DB_Service_Schedule am ersten Tag der Folgewoche.
Momentan noch in keinem SVC verwendet.
Löschen von nicht mehr benötigten Werten in der Datenbank
Hier wird endgültig aufgeräumt, alte momentan Werte werden gelöscht, wenn sie nach z.B. drei Monaten keine Relevanz mehr haben. Dafür wurden im vorherigen Abschnitt zusätzliche Werte in der Datenbank erzeugt, die in Diagrammen trotzdem noch einen Trend erkennen lassen.
Wenn eine immer größer werdende Datenbank mit steigenden Antwortzeiten nicht stört, der kann das Aufräumen auch weg lassen. Bei einer späteren Migration führt dies natürlich zu höherem Aufwand und hohen Laufzeiten.
RAW Definition LogDBRep_delete_Statistic_EnergyHomeBat_Day_max_Day
Löschen aller Statistic_EnergyHomeBat_Day Werte, bis auf den maximal Wert des Tages.
Der aktuelle Tag bleibt noch erhalten.
Aufruf mit: maxValue deleteOther
RAW Definition LogDBRep_delete_Statistic_EnergyHomePvSum_Day_max_Day
Löschen aller Statistic_EnergyHomeBat_Day Werte, bis auf den maximal Wert des Tages.
Der aktuelle Tag bleibt noch erhalten.
Aufruf mit: maxValue deleteOther
Über dieses Scheduling werden in der Datenbank zusätzliche Wochen- und Monatseinträge gesteuert.
RAW Definition DB_Service_Schedule
Hier werden zusätzlich Werte in der Datenbank erzeugt.
ddefmodDB_Service_ScheduleDOIF## Monatlich Einträge\([01:13]and($mday==1))\(setLogDBRep_Statistic_EnergyHomePvSum_Year_max_MonthmaxValuewriteToDB)\(setLogDBRep_Statistic_Yield_Month_max_MonthmaxValuewriteToDB)\(setLogDBRep_Statistic_previous_MonthsqlCmdckey:1)## Bildet für verschiedene Devices die Monatsauswertung\\## Wöchentliche Einträge\DOELSEIF\([01:17]and($wday==1))\(setLogDBRep_Statistic_EnergyHomePvSum_Year_diff_WeekdiffValuewriteToDB)\(setLogDBRep_Statistic_EnergyFeedInGrid_Year_diff_WeekdiffValuewriteToDB)\(setLogDBRep_Statistic_Yield_Year_diff_WeekdiffValuewriteToDB)\\## Wöchentliche Einträge mit löschen\DOELSEIF\([02:17]and($wday==1))\(setLogDBRep_delete_Statistic_EnergyHomeBat_Day_max_DaymaxValuedeleteOther)\(setLogDBRep_delete_Statistic_EnergyHomePvSum_Day_max_DaymaxValuedeleteOther)\(setLogDBRep_delete_Statistic_TotalConsumption_Day_max_DaymaxValuedeleteOther)\\## Tägliche Einträge\DOELSEIF\([01:17])\(setLogDBRep_Statistic_previous_DaysqlCmdckey:1)## Bildet für verschiedene Devices die Tagesauswertung\\## Quartal Einträge\DOELSEIF\(($mdeq"01-01"or$mdeq"04-01"or$mdeq"07-01"or$mdeq"10-01")and[03:11])\(setLogDBRep_Statistic_previous_QuartersqlCmdckey:1)## Erstellt die Quartalsauswertung für WR_1\\## Jährliche Einträge\DOELSEIF\($mdeq"01-01"and[08:05])\(setLogDBRep_Statistic_previous_YearsqlCmdckey:1)## Bildet für verschiedene Devices die JahresauswertungattrDB_Service_ScheduleDbLogExclude.*attrDB_Service_SchedulecommentVersion2024.01.2317:00\HierwerdenzusätzlichWerteinderDatenbankerzeugt.attrDB_Service_ScheduledoalwaysattrDB_Service_ScheduleroomSystemattrDB_Service_Schedulewait0,3:0,5,5:0,5,5attrDB_Service_SchedulewebCmdcmd_1:cmd_2:cmd_3attrDB_Service_SchedulewebCmdLabelmonatlich:wöchentlich:wöchentlichLöschen:
Wetter-/Leistungs-Prognose
Bei der Leistungsprognose gibt es nun eine gravierende Veränderung. Die bisherige Leistungsprognose durch eine eigene Berechnung, die auf diversen Konfigurationsparametern basiert hat wurde vollständig durch eine KI_Prognose abgelöst. Die bisherige Implementierung wird nicht mehr weiter entwickelt und ist hier nur noch zu Dokumentationszwecke aufgeführt.
Nun ist der Ansatz der KI eingezogen und meine Ergebnisse, von bisherigen Tests, sehen schon ziemlich gut aus.
Der Grundgedanke ist, dass die Prognose keinerlei technischen Informationen über den Aufbau der PV-Anlage benötigt. Einzig allen der Ertrag der Anlage wird dabei in Bezug zu den Wetterdaten des jeweiligen Standortes gesetz, wobei die KI daraus Rückschlüsse zieht, wie bei ähnlichen Bedingungen der ertrag werden könnte. Je mehr vergleichbare Daten dazu zur Verfügung stehen, umso besser wird die Prognose.
In der momentan implementierten Prognose besteht darüber hinaus ein Problem, das man die momentan erzeugte Leistung eigentlich mit der zu erwartenden Energieprognose vergleicht.
Beim neuen Ansatz wird nun versucht das mit zu korrigieren, was auch im Diagramm durch die Stufen Darstellung verdeutlicht wird.
Die KI Prognose arbeitet nun über den Yield, den der Plenticore jede Stunde aktualisiert. Bei diesem Yield ist nun jedoch ein weiteres Problem, da der hybrid Wechselrichter natürlich auf der AC Seite den Yield angibt und somit das Laden des Speichers nicht aktuell mit zählt. Die Speicher Entladung wird später dann wiederum mit gerechnet, was die AC Yield Kurve dann sehr merkwürdig aussehen lässt. An dieser Problematik wurde auch bereits gearbeitet und das wird dann später nochmal erwähnt.
Im Diagramm sieht man nun in blau den korrigierten Yield unter Berücksichtigung des Speichers und in diesem Beispiel Fall für eien gesamten Schwarm (ich habe zwei WR). Jede Stufe im Diagramm ist dann nun der Ertrag (Yield) der entsprechenden Stunde in kWh.
Zur Orientierung sieht man in gelb die AC Leistung in kW, gezeichnet aus den minütlichen Messwerten.
Die rosa Stufen sind dann nun endlich die Ertrags Prognose Werte aus der KI in kWh.
KI Prognose Teil 1 - DWD und Astro Daten sammeln
Solltet Ihr später mit in diese Richtung gehen wollen, so macht es Sinn [b]schon jetzt die Wetterdaten für Euren Standort zu sammeln[/b], da diese die Grundlage bilden und im Anschluss mit dem korrigierten Ertrag in Verbindung gebracht werden. Alle im comment angegebenen DWD Werte werden später von der KI ausgewertet und müssen somit in der DbLog vorliegen. Je mehr DWD Daten von den letzten Jahren vorliegen, umso besser kann die KI Rückschlüsse ziehen. Sollten diese nicht da sein, so lernt das ganze langsam dazu.
Da die KI Prognose ja auch die Astro Daten für den Sonnenstand benötigt und dieser im Astro Device nicht als fc[0|1] vorliegt habe ich das Astro Device etwas modifiziert. In den userreadings werden dort die fc[0|1] Sonnenstände jetzt abgefragt und als readings eingetragen. Dies geschieht sobald es einen Event von ObsDate gibt, der einmal täglich kommen sollte. Somit beachtet auch die Änderung bei event-on-update-reading und beim DbLogInclude.
In diesem Teil geht es darum die Daten aus der FHEM History so aufzubereiten, dass sie für die KI Prognose verwendbar wird. Das Daten Model der FHEM History ist in der Form nicht für diese Verarbeitung brauchbar und wird deshalb in eine neu Tabelle überführt. Bei der Gelegenheit wird einiges noch aufbereitet und insbesondere der yield des Plenticore mit Speicher korrigiert.
RAW Definition dwd_load() MySQL Procedure
Hier kommt nun die MySQL Procedure, die in der Datanbank hinterlegt wird. Dazu verwende ich z.B. die MySQL Workbench, wo dann die Procedure unter "Stored Precedures" auftaucht. Dies ermöglicht, dass man im FHEM DbRep Device nur diese eine Procedure aufrufen kann und nicht jedes einzelne SELECT zur Datenbank in einer separaten Session übermittelt werden muss.
1. Löschen der bisherigen dwdfull Tabelle
2. Anlegen einer neuen dwnfull Tabelle
3. Füllen der Tabelle mit den älteren rad1h Werten
4. Ergänzen der rad1h Werte für den nächsten Tag
5. Nun erfolgen alle weiteren DWD Daten in weiteren Spalten der dwdfull Tabelle
1. TTT : Temperature 2m above surface [°C]
2. FF : Windspeed
3. Neff : Effective cloud cover [%]
4. R101 : Probability of precipitation > 0.1 mm during the last hour [%]
5. R600 : Probability of precipitation > 0.0mm during the last 6 hours [%]
6. RRs1c : Snow-Rain-Equivalent during the last 3 hours [kg/m2]
7. Rad1h : Global Irradiance [kJ/m2]
kJ/m² Umrechnung *0,277778 in kWh/m²
8. ww : Significant Weather
9. wwM : Probability for fog within the last hour [%]
6. Zum Schluss wird noch der yield der kompletten PV-Anlage ergänzt
1. Begonnen wird mit dem AC yield, der stundenweise aus dem Zähler "SW_Yield_Daily" berechnet wird
dieser ist jedoch wegen des DC seitigen Speichers nicht korrekt, da in einem Graphen die PV-Leistung
erst nach dem entladen zugerechnet wird
2. Nun wird der DC yield des Speichers berücksichtigt, was über diese Werte geschieht
1. Battery_Total_DC_ChargeEnergy_DCsideToBattery
2. Battery_Total_DC_DischargeEnergy_DCsideFromBattery
3. Die Ermittlung einer stunden basierten Tabelle ist etwas komplexer und bedarf diverser SELECT mit JOIN (Im MySQL gibt es kein full JOIN)
7. Der letzte Schritt ist dann die Möglichkeit einer Rückmeldung aus der MySQL Procedure ins FHEM
8. Über den Parameter show/none wird der Prozedure die Art der Rückmeldung mitgeteilt
1. none wäre der Default und gibt als Ergebnis das aktuelle Datum der Datenbank zurück
2. show würde den Inhalt der dwnfull Tabelle an FHEM zurück liefern, was jedoch einige hundert Zeilen sein werden
9. Die Procedure selectiert nur die entscheidenden Daten für die jeweilige KI Prognose, um das Datenvolumen gering zu halten,
denn es macht ja keinen Sinn, die Winter mit den Sommer Daten zu vergleichen
10. Hierbei werden deshalb folgende Zeiträume jeweils selectiert
1. Die letzten 30 Tage ab dem aktuellen Datum
2. Vom letzten Jahr 30 Tage vor dem Datum
3. Vom letzten Jahr 30 Tage nach dem Datum
4. Vom vorletzten Jahr 30 Tage vor dem Datum
5. Vom vorletzten Jahr 30 Tage nach dem Datum
6. Die Forecast Daten für den nächsten Tag,
an dieser Stelle wäre es natürlich auch denkbar noch weiter in die Zukunft zu gehen,
was mir jedoch zu spekulativ ist und nach meiner Meinung bisher für keine Entscheidung von Wichtigkeit wäre.
11. Die Laufzeit dieser Procedure beträgt auf meinem RPI4 in einem Oracle MySQL Docker Container ca. 50-70 Sekunden,
deshalb musste ich bei mir den Timeout der MySQL Workbench für eine Session von 60 Sekunden auf z.B 90 Sekunden erhöhen
12. In einem Interface Eurer Wahl zur Datenbank könnt Ihr die Procedure zum Testen dann aufrufen und das Ergebnis testen.
dwd_load() Test in MySQL aufrufen
calldwd_load(curdate(),'none');select*fromdwdfull-- WHERE TIMESTAMP > curdate()orderbyTIMESTAMPdescLIMIT1000;
Sollte nun der Test der Procedure eine gefüllte Tabelle anzeigen, so kann die Integration ins FHEM erfolgen. Hierzu wird dann ein DbRep Device angelegt, dass später zyklisch jede Stunde ausgeführt wird.
RAW Definition LogDBRep_PV_KI_Prognose (Teil 1)
Achtung, bei diesem Device kommt im weiteren Fortschritt noch ein weiteres Attribut zum Aufruf des Python KI Prognose Skriptes hinzu. Im Kommentar wird dies bereits im Syntax erwähnt.
Auch hier sollte nun getestet werden, indem man beim set das sqlCmd ausführt. Der MySQL Procedur Aufruf ist ebenfalls im Kommentar zu finden.
Als Ergebnis sollte soetwas zurück kommen. Nachdem das erschienen ist kann man den obigen Test mit dem SELECT der dwdfull Tabelle nochmals wiederholen.
Für die Verwendung der KI Prognose werden die folgenden Python Packages noch benötigt. Die Basis wäre hierbei der FHEM Docker Container.
Momentan habe ich das erstmal manuell im Container gemacht:
sudo apt-get install python3-pandas
sudo apt-get install python3-pymysql
sudo apt-get install python3-sqlalchemy # Version 1.2.18
sudo apt-get install python3-sklearn python3-sklearn-lib
pip3 install fhem
Für Docker sollte das im .yml File dann so aussehen:
-e PIP_PKGS="pandas pymysql sqlalchemy sklearn sklearn-lib"
ob das mit dem "pip3 install fhem" so geht habe ich nicht getestet
Die Python Skripte liegen bei mir im Ordner
./python/bin
[https://svn.fhem.de/fhem/trunk/fhem/contrib/ch.eick/Photovoltaik/KI_Prognose/PV_KI_Prognose.py ./python/bin/PV_KI_Prognose.py]
Das PV_KI_Prognose.py wird mit folgenden Parametern aufgerufen:
1. Zum Test kann dies auch in "" in der FHEM Kommandozeile eingegeben werden, zuvor muss jedoch die MySQL Prozedur aufgerufen worden sein, damit die benötigte Tabelle mit den Daten erstellt worden ist.
2. Nach dem Testen kommt dieser Aufruf dann in das LogDBRep_PV_KI_Prognose Device und wird somit mit dem MySQL Prozeduraufruf synchronisiert.
Bitte das LogDBRep_PV_KI_Prognose Device (Teil 1) aus dem vorherigen Absatz verwenden.
Damit dann alles automatisch gestartet wird muss nun noch im WR_ctl Device ein Eintrag eingefügt werden.
3. Achtung, das WR_ctl Device beinhaltet jetzt die Forecast Daten und nicht wie früher das WR_1 Device.
<snip>################################################################################################################## 2 Start der KI Prognose## Der Reading Name und das Device werden in LogDBRep_PV_KI_Prognose im executeAfterProc eingestellt## "/opt/fhem/python/bin/PV_KI_Prognose.py 192.168.178.40 192.168.178.40 LogDBRep_PV_KI_Prognose WR_1_ctl Yield_fc"##2_KI_Prognose{if(!([$SELF:state]eq"off")## DOIF enabledandReadingsVal("LogDBRep_PV_KI_Prognose","PV_KI_Prognose","null")eq"done"## Die Prognose darf nicht gerade laufen !!!and(([05:00-22:00]and[:03]## In der PV-Zeit jede Stunde aktualisieren)or[$SELF:ui_command_1]eq"2_KI_Prognose"## Hier wird das uiTable select ausgewertet)){::CommandSet(undef,"LogDBRep_PV_KI_Prognose sqlCmd call dwd_load(curdate(),'none')");if(AttrVal("$SELF","verbose",0)>=3){Log3,"$SELF 2_KI_Prognose : Start KI Prognose";}set_Reading("ui_command_1","---");## Hier wird das uiTable select wieder zurückgesetzt, ansonsten## kann das Kommando nicht sofort wiederholt werden}}<snip>
Für die Netzwerkverbindung aus dem KI Python Skript werden die Zugansdaten im Filesystem abgelegt, damit sie nicht mit dem Skript ausversehen weiter gegeben werden.
./python/pwd_fhem.json
./python/pwd_sql.json
Die Verbindungsdaten werden in den Dateien wie folgt abgelegt:
FHEM und die Datenbank müssen nicht auf dem selben Rechner installiert werden. Die IP-Adressen werden dem Skript beim Aufruf mitgegeben.
Es ist nicht erforderlich die neuen readings mit DbLogInclude aus dem WR_1 Device in die Datenbank zu loggen, da dies bereits durch das PV_KI_Prognose Skript direkt geschieht, um einen passenden TIMESTAMP pro Stunde zu bekommen.
Wenn im LogDBRep_PV_KI_Prognose der verbose Level auf >= 3 steht kommen diverse Meldungen im Log:
Grafana kann z.B. mit docker auf dem selben oder auch einem anderen System installiert werden. Es ermöglicht die Darstellung von Diagrammen und Dashboards durch die direkte Abfrage aus einer Datenbank.
Beispiel Diagramme
Die verwendete Datenbank ist im Grafana als "FHEM MySQL" am besten vorher zu konfigurieren.
Achtung, dieses Dashboard verwendet die Schwarm readings bei den MySQL SELECT!
Eine Anpassung wäre denkbar, wenn man im JSON File "SW_" global entfernt.
Auch die Hauptverbraucher sind im Diagramm anzupassen, da sie bei mir durch eigene Zähler erfasst werden. Sollten bei Euch keine Zähler vorhanden sein, so müsstet Ihr den jeweiligen Verbraucher im Diagramm löschen.
Im JSON File sind noch weitere Kommentare enthalten, die bitte auch gelesen werden sollten.
Grafana ermöglicht das direkte Auslesen der SQL Datenbank und kann auch auf einer anderen Plattform betrieben werden. Bei mir befindet es sich in Docker Containern auf dem selben RPI4.
Hier werde ich auch mal aktualisieren, bei bedarf einfach im Forum fragen.
Beispiel Luft Wärme Pumpe Novelan LAD
Hier mal ein paar Bilder für die Dokumentation der PV-Modus Anschaltung mit einem Shell 1 und das Heizelement, dass man über Lastrelais in drei Stufen Regeln könnte. Die Aktivierung der Zusatzheizung ist über das Luxtronik2 Modul möglich.
Zusatzheizung 2/4/6 kW
LAD Shelly Einbauposition
LAD Shelly Phase und Null
LAD Shelly SWT-Signal
RAW Definition LWP_PV (DOIF im Perl Modus)
Hierbei wird das PV-Modus Signal über ein Shelly 1 zur LAD Wärmepumpe übermittelt, was natürlich auch durch ein beliebiges anderes Relais erfolgen kann.
Die setstate Attribute am Ende der RAW Definition sind ebenfalls wichtig, da dort die Default reading Werte für das DOIF gesetzt werden. Diese können dann über die uiTable Definitionen mit Pull Down Menüs geändert werden.
defmodPool_PV_PerlDOIF################################################################################################################\## Eigenverbrauch einschalten: wenn PV Produktion über dem Mindestbedarf ist und die Laufzeit pro Tag noch nicht erreicht ist\##\01_1_Eigenverbrauch_automatisch_An\{if(!([$SELF:state]eq"off")## DOIF enabled\and\([WR_1:SW_Total_PV_P_reserve]>=[$SELF:PowerLimitOn]## Es besteht PV-Überschuss\and[[$SELF:TimeStart]-[$SELF:TimeEnd]]## Das Zeitfenster ist erreicht\andget_Exec("PV_Modus_Ein_timer")<1## Der Wait Timer ist noch nicht gestartet\and[$SELF:Pool_Status]eq"Aus"## Der Pool ist aus\and[Pool_Counter:pulseTimePerDay]<[$SELF:RunTimePerDay]## Die maximale Laufzeit des Pools ist noch nicht erreicht\)\or[$SELF:ui_command_1]eq"01_1_Eigenverbrauch_automatisch_An"## Hier wird das uiTable select ausgewertet\){\if(AttrVal("$SELF","verbose",0)>=3)\{Log3,"Pool_PV 01_1 : Pool on waiting"};;\\set_Exec("PV_Modus_Ein_timer",[$SELF:PowerLevelMinTime],'set_Reading("Pool_Status","An");;PV_Modus_Ein_Pool()');;## Den PV-Modus verzögert einschalten\set_Reading("Pool_Status","Wartend");;\\set_Reading("ui_command_1","---");;## Hier wird das uiTable select wieder zurückgesetzt, ansonsten\## kann das Kommando nicht sofort wiederholt werden\}\}\\################################################################################################################\## Manuell den Pool einschalten.\##\01_2_Eigenverbrauch_manuell_An\{if(!([$SELF:state]eq"off")## DOIF enabled\and\[$SELF:ui_command_1]eq"01_2_Eigenverbrauch_manuell_An"## Hier wird das uiTable select ausgewertet\){\if([$SELF:ui_command_1]eq"01_2_Eigenverbrauch_manuell_An"){## Hier wurde manuell eingeschaltet\set_Reading("ui_command_1_before",[$SELF:ui_command_1]);;\}\if(AttrVal("$SELF","verbose",0)>=3)\{Log3,"Pool_PV 01_2 : Pool on for manuel usage"};;\\set_Reading("Pool_Status","manuell");;\PV_Modus_Ein_Pool();;\\set_Reading("ui_command_1","---");;## Hier wird das uiTable select wieder zurückgesetzt, ansonsten\## kann das Kommando nicht sofort wiederholt werden\}\}\\################################################################################################################\## Manuell den Pool abschalten.\##\01_3_Eigenverbrauch_manuell_Aus\{if(!([$SELF:state]eq"off")## DOIF enabled\and\[$SELF:ui_command_1]eq"01_3_Eigenverbrauch_manuell_Aus"## Hier wird das uiTable select ausgewertet\){\if(AttrVal("$SELF","verbose",0)>=3)\{Log3,"Pool_PV 01_3 : Pool off after manuel usage"};;\\PV_Modus_Aus_Pool();;\\set_Reading("ui_command_1","---");;## Hier wird das uiTable select wieder zurückgesetzt, ansonsten\## kann das Kommando nicht sofort wiederholt werden\}\}\################################################################################################################\## Eigenverbrauch abschalten: wenn Mindestlaufzeit erreicht wurde und Maximallaufzeit pro Tag erreicht ist\##\02_1_Eigenverbrauch_Laufzeit_Aus\{if(!([$SELF:state]eq"off")## DOIF enabled\and\([Pool_Counter:pulseTimePerDay]>=[$SELF:RunTimePerDay]## Die Tages Laufzeit ist überschritten\and[Pool_Counter:pulseTimeIncrement]>=[$SELF:RunTimeMin]## Die Mindestlaufzeit ist überschritten\and([$SELF:Pool_Status]eq"An"## Der Pool läuft\or[$SELF:Pool_Status]eq"pflege")\)\\or[$SELF:ui_command_1]eq"02_1_Eigenverbrauch_Laufzeit_Aus"## Hier wird das uiTable select ausgewertet\){\if(AttrVal("$SELF","verbose",0)>=3)\{Log3,"Pool_PV 02_1 : Pool off Laufzeit"};;\\PV_Modus_Aus_Pool();;\\set_Reading("ui_command_1","---");;## Hier wird das uiTable select wieder zurückgesetzt, ansonsten\## kann das Kommando nicht sofort wiederholt werden\}\}\\\################################################################################################################\## Eigenverbrauch abschalten: wenn Mindestlaufzeit erreicht wurde und die PV Produktion unter dem Mindestbedarf ist\##\02_2_Eigenverbrauch_PV_Min_Aus\{if(!([$SELF:state]eq"off")## DOIF enabled\and\(([WR_1:Home_own_consumption_from_grid]## Nicht zuviel Bezug aus dem Netz\+[WR_1:Home_own_consumption_from_Battery])>100## oder dem Speicher\and[Pool_Counter:pulseTimeIncrement]>=[$SELF:RunTimeMin]## Die Mindestlaufzeit ist überschritten\and[$SELF:Pool_Status]eq"An"## Der Pool läuft (nicht bei manuell)\)\or[$SELF:ui_command_1]eq"02_2_Eigenverbrauch_PV_Min_Aus"## Hier wird das uiTable select ausgewertet\){\if(AttrVal("$SELF","verbose",0)>=3)\{Log3,"Pool_PV 02_2 : Pool off PV-Min"};;\\PV_Modus_Aus_Pool();;\\set_Reading("ui_command_1","---");;## Hier wird das uiTable select wieder zurückgesetzt, ansonsten\## kann das Kommando nicht sofort wiederholt werden\}\}\\\\################################################################################################################\## Stop, wenn es nur ein kurzer peak ist. Dieser Do Zweig setzt den wait timer vom Einschaltkommando cmd_4 wieder außer kraft,\## wenn während der Wartezeit die PV Anlage zuwenig liefert.\##\03___Stop_Wait_Timer\{if(!([$SELF:state]eq"off")## DOIF enabled\and\(\([WR_1:SW_Total_PV_P_reserve]<[$SELF:PowerLimitOn]## Ist die PV-Leistung zu niedrig?\andget_Exec("PV_Modus_Ein_timer")>0## läuft eine Wartezeit\andget_Exec("PV_Modus_Ein_timer")<5## läuft die Wartezeit bald ab\and[$SELF:Pool_Status]eq"Wartend"## und gibt es keine manuelle Einschaltung\)\)\or[$SELF:ui_command_1]eq"03___Stop_Wait_Timer"## Hier wird das uiTable select ausgewertet\){\if(AttrVal("$SELF","verbose",0)>=3)\{Log3,"Pool_PV 03__ : Stop wait timer Pool"};;\del_Exec("PV_Modus_Ein_timer");;## Der Pool wird nicht mehr eingeschaltet\\set_Reading("ui_command_1","---");;## Hier wird das uiTable select wieder zurückgesetzt, ansonsten\## kann das Kommando nicht sofort wiederholt werden\}\}\\################################################################################################################\## Pool Ende\##\05___Pool_Ende\{if(!([$SELF:state]eq"off")## DOIF enabled\and\([shelly02:power_0]<10## Die Poolpumpe ist bereits\and[Pool_Counter:pauseTimeIncrement]>900## seit 5 Minuten aus\and[$SELF:Pool_Status]ne"Aus"## und gibt es keine manuelle Einschaltung\and[$SELF:Pool_Status]ne"Wartend"## und es wird nicht wegen eines Peaks gewartet\)\or[$SELF:ui_command_1]eq"05___Pool_Ende"## Hier wird das uiTable select ausgewertet\){\if(AttrVal("$SELF","verbose",0)>=3)\{Log3,"Pool_PV 05__ : Pool run finished ".[shelly02:power_0]." ".[Pool_Counter:pauseTimeIncrement]};;\\PV_Modus_Aus_Pool();;\\set_Reading("ui_command_1","---");;## Hier wird das uiTable select wieder zurückgesetzt, ansonsten\## kann das Kommando nicht sofort wiederholt werden\}\}\\################################################################################################################\## Pool Startzeit durch Forecast verschieben. Der Forecast wird um 7:00 im Device PV_Schedule aktualisiert\##\08___Startzeit_nach_forecast\{if(!([$SELF:state]eq"off")## DOIF enabled\and\[07:17]## Der Forecast wird um 7:00 im Device PV_Schedule aktualisiert\or[$SELF:ui_command_1]eq"08___Startzeit_nach_forecast"## Hier wird das uiTable select ausgewertet\){\if([$SELF:ui_command_1]eq"08___Startzeit_nach_forecast"){## Hier wurde manuell eingeschaltet\set_Reading("ui_command_1_before",[$SELF:ui_command_1]);;\}\\if([WR_1:Solar_Calculation_fc0_day]<[WR_1_Speicher_1_ExternControl:SpeicherMinSOC_fc1_Limit]or\[Heizung:averageAmbientTemperature]<=10){\set_Reading("TimeStart",[$SELF:TimeStartWinter]);;\set_Reading("TimeEnd",[$SELF:TimeEndWinter]);;\}else{\set_Reading("TimeStart",[$SELF:TimeStartSummer]);;\set_Reading("TimeEnd",[$SELF:TimeEndSummer]);;\}\\if(AttrVal("$SELF","verbose",0)>=3)\{Log3,"Pool_PV 08__ : Pool switched to TimeStart ".ReadingsVal("$SELF","TimeStart",0)." TimeEnd ".ReadingsVal("$SELF","TimeEnd",0)};;\\set_Reading("ui_command_1","---");;## Hier wird das uiTable select wieder zurückgesetzt, ansonsten\## kann das Kommando nicht sofort wiederholt werden\}\}\\################################################################################################################\## Pool durch kürzere Laufzeit abkühlen lassen\##\09___Laufzeit_im_Sommer\{if(!([$SELF:state]eq"off")## DOIF enabled\and\[06:15]and[Heizung:averageAmbientTemperature]## Pool durch kürzere Laufzeit abkühlen lassen\or[$SELF:ui_command_1]eq"09___Laufzeit_im_Sommer"## Hier wird das uiTable select ausgewertet\){\if([$SELF:ui_command_1]eq"09___Laufzeit_im_Sommer"){## Hier wurde manuell eingeschaltet\set_Reading("ui_command_1_before",[$SELF:ui_command_1]);;\}\\if([Heizung:averageAmbientTemperature]>=18){\set_Reading("RunTimePerDay",[$SELF:RunTimePerDaySummer]);;\}else{\set_Reading("RunTimePerDay",[$SELF:RunTimePerDayWinter]);;\}\\if(AttrVal("$SELF","verbose",0)>=3)\{Log3,"Pool_PV 09__ : Pool switched to RunTimePerDay ".ReadingsVal("$SELF","RunTimePerDay",0)};;\\set_Reading("ui_command_1","---");;## Hier wird das uiTable select wieder zurückgesetzt, ansonsten\## kann das Kommando nicht sofort wiederholt werden\}\}\\################################################################################################################\## Pflege Zwangseinschaltung: Es muss mindestens einmal pro Tag eingeschaltet werden, auch wenn kein PV Strom vorhanden war.\##\10___Pool_Pflege\{if(!([$SELF:state]eq"off")## DOIF enabled\and\([[$SELF:TimeEnd]]## Hier sollte der Pool bereits gelaufen sein\and\([Pool_Counter:pulseTimePerDay]<[$SELF:RunTimePerDay]or## Ist er zuwenig gelaufen\[Pool_Counter:countsPerDay]eq0)## oder eventuell garnicht\)\or[$SELF:ui_command_1]eq"10___Pool_Pflege"## Hier wird das uiTable select ausgewertet\){\if([$SELF:ui_command_1]eq"10___Pool_Pflege"){## Hier wurde manuell eingeschaltet\set_Reading("ui_command_1_before",[$SELF:ui_command_1]);;\}\\if(AttrVal("$SELF","verbose",0)>=3)\{Log3,"Pool_PV 10__ : Pool on for maintanance"};;\\set_Reading("Pool_Status","pflege");;\PV_Modus_Ein_Pool();;\\set_Reading("ui_command_1","---");;## Hier wird das uiTable select wieder zurückgesetzt, ansonsten\## kann das Kommando nicht sofort wiederholt werden\}\}\\################################################################################################################\## Im Herbst Winter den Pool bei günstigem Strom in der Nacht zusätzlich einschalten\##\11___Nachtstrom_An\{if(!([$SELF:state]eq"off")## DOIF enabled\and\([WR_1:Solar_Calculation_fc0_day]<[WR_1_Speicher_1_ExternControl:SpeicherMinSOC_fc1_Limit]## Im Herbst/Winter ist wenig zu erwarten\and[EVU_Kosten:aWATTar_Trigger]eq"onx"## Gibt es günstigen Strom an der Börse\and[22:00-05:00]## nur in dieser Zeit verwenden\)\or[$SELF:ui_command_1]eq"11___Nachtstrom_An"## Hier wird das uiTable select ausgewertet\){\if([$SELF:ui_command_1]eq"11___Nachtstrom_An"){## Hier wurde manuell eingeschaltet\set_Reading("ui_command_1_before",[$SELF:ui_command_1]);;\}\\if(AttrVal("$SELF","verbose",0)>=3)\{Log3,"Pool_PV 10__ : Pool on Nachtstrom by aWATTar"};;\\set_Reading("Pool_Status","Nachtstrom");;\PV_Modus_Ein_Pool();;\\set_Reading("ui_command_1","---");;## Hier wird das uiTable select wieder zurückgesetzt, ansonsten\## kann das Kommando nicht sofort wiederholt werden\}\}\\################################################################################################################\## Sobald der Strompreis wieder teurer wird den Pool abschalten\##\12___Nachtstrom_Aus\{if(!([$SELF:state]eq"off")## DOIF enabled\and\([WR_1:Solar_Calculation_fc0_day]<[WR_1_Speicher_1_ExternControl:SpeicherMinSOC_fc1_Limit]## Im Herbst/Winter ist wenig zu erwarten\and[EVU_Kosten:aWATTar_Trigger]eq"off"## Gibt es günstigen Strom an der Börse\and[$SELF:Pool_Status]eq"Nachtstrom"## und gibt es keine manuelle Einschaltung\)\or[$SELF:ui_command_1]eq"12___Nachtstrom_Aus"## Hier wird das uiTable select ausgewertet\){\if([$SELF:ui_command_1]eq"12___Nachtstrom_Aus"){## Hier wurde manuell eingeschaltet\set_Reading("ui_command_1_before",[$SELF:ui_command_1]);;\}\\if(AttrVal("$SELF","verbose",0)>=3)\{Log3,"Pool_PV 10__ : Pool off after Nachtstrom by aWATTar"};;\\PV_Modus_Aus_Pool();;\\set_Reading("ui_command_1","---");;## Hier wird das uiTable select wieder zurückgesetzt, ansonsten\## kann das Kommando nicht sofort wiederholt werden\}\}\\\################################################################################################################\## Definition von Sub Routinen\subs{\subPV_Modus_Ein_Pool(){## PV-Modus Einschalten\if(AttrVal("$SELF","verbose",0)>=3)\{Log3,"Pool_PV sub : Pool on"};;\## {fhem("set Pool_Counter pulseTimeIncrement 0")} ## das sollte eigentlich raus\fhem("set Pool_Counter pauseTimeIncrement 0");;\fhem("".ReadingsVal("$SELF","SetCmdOn",0));;\}\\subPV_Modus_Aus_Pool(){## PV-Modus Ausschalten\if(AttrVal("$SELF","verbose",0)>=3)\{Log3,"Pool_PV sub : Pool off"};;\fhem("".ReadingsVal("$SELF","SetCmdOff",0));;\set_Reading("Pool_Status","Aus");;\}\}attrPool_PV_PerlDbLogExclude.*attrPool_PV_PerlaliasPool_PV_PerlattrPool_PV_PerlcommentVersion2021.11.0109:00attrPool_PV_Perldisable0attrPool_PV_Perlevent-on-change-reading.*attrPool_PV_PerlgroupPVEigenverbrauch-SteuerungattrPool_PV_Perliconscene_swimmingattrPool_PV_PerlroomStrom->PhotovoltaikattrPool_PV_Perlsortby421attrPool_PV_PerluiTable{\packageui_Table;;\$TABLE="style='width:100%;;'";;\\$TD{0..9}{0}="align='center' style='font-size:16px;;border-right-style:solid;;border-color:darkgreen;;border-right-width:2px;;width:26%'";;\\$TD{0..9}{1}="style='border-top-style:solid;;border-bottom-style:solid;;border-right-style:solid;;border-color:darkgreen;;border-top-width:2px;;border-bottom-width:2px;;border-right-width:1px;;width:36%;;font-weight:bold;;'";;\$TD{0..9}{2..4}="style='border-top-style:solid;;border-bottom-style:solid;;border-right-style:solid;;border-color:darkgreen;;border-top-width:2px;;border-bottom-width:2px;;border-right-width:1px;;width:8%;;text-align:center;;'";;\$TD{0..9}{5}="style='border-top-style:solid;;border-bottom-style:solid;;border-right-style:solid;;border-color:darkgreen;;border-top-width:2px;;border-bottom-width:2px;;border-right-width:2px;;width:8%;;text-align:center;;'";;\\subFUNC_Status{\my($value,$min,$colorMin,$statusMin,$colorMiddel,$statusMiddle,$max,$colorMax,$statusMax)=@_;;\my$ret=($value<$min)?'<span style="color:'.$colorMin.'">'.$statusMin.'</span>':($value>$max)?'<span style="color:'.$colorMax.'">'.$statusMax.'</span>':'<span style="color:'.$colorMiddel.'">'.$statusMiddle.'</span>';;\return$ret;;\}\\}\\"$SELF"|"Kommando<dd>Auswahl / Status / / Pumpe Status</dd>"|widget([$SELF:ui_command_1],"uzsuDropDown,---,01_1_Eigenverbrauch_automatisch_An,01_2_Eigenverbrauch_manuell_An,01_3_Eigenverbrauch_manuell_Aus,02_1_Eigenverbrauch_abschalten_Laufzeit,02_2_Eigenverbrauch_abschalten_PV_Min,03___Stop_Wait_Timer,05___Pool_Ende,08___Startzeit_nach_forecast,09___Laufzeit_im_Sommer,10___Pool_Pflege,11___Nachtstrom_An,12___Nachtstrom_Aus")|[$SELF:Pool_Status]|::ReadingsTimestamp("$SELF","timer_PV_Modus_Ein_timer","")|(([shelly02:power_0]>10)?'<span style="color:green">Pool_Pumpe_laeuft</span>':'<span style="color:black">Pool_Pumpe_aus</span>')\|"Konfiguration<dd>PowerLevelMinTime, | PowerLimit On/Off | Time Start/End</dd><dd>RunTime Min/PerDay</dd>"|""|widget([$SELF:PowerLevelMinTime],"selectnumbers,60,60,900,0,lin")."<br>".widget([$SELF:RunTimeMin],"selectnumbers,300,300,7200,0,lin").widget([$SELF:RunTimePerDay],"selectnumbers,900,300,28800,0,lin")|widget([$SELF:PowerLimitOn],"selectnumbers,100,50,2000,0,lin").widget([$SELF:PowerLimitOff],"selectnumbers,50,50,2000,0,lin")|widget([$SELF:TimeStart],"time").widget([$SELF:TimeEnd],"time")\|"<dd>Sommer, Winter / RunTimePerDay / Start / Ende</dd>"|""|widget([$SELF:RunTimePerDaySummer],"selectnumbers,900,300,28800,0,lin")."<br>".widget([$SELF:RunTimePerDayWinter],"selectnumbers,900,300,28800,0,lin")|widget([$SELF:TimeStartSummer],"time").widget([$SELF:TimeStartWinter],"time").|widget([$SELF:TimeEndSummer],"time").widget([$SELF:TimeEndWinter],"time")attrPool_PV_Perlverbose3setstatePool_PV_Perl2022-10-2316:44:33Pool_StatusAussetstatePool_PV_Perl2022-06-3012:48:47PowerLevelMinTime300setstatePool_PV_Perl2022-05-1015:51:44PowerLimitOff100setstatePool_PV_Perl2022-05-1015:50:44PowerLimitOn1000setstatePool_PV_Perl2022-05-1015:55:23RunTimeMin7200setstatePool_PV_Perl2022-10-2306:15:00RunTimePerDay28800setstatePool_PV_Perl2021-06-2314:55:42RunTimePerDaySummer7200setstatePool_PV_Perl2020-10-0614:14:13RunTimePerDayWinter28800setstatePool_PV_Perl2022-05-1016:23:06SetCmdOffsetshelly02off0setstatePool_PV_Perl2022-05-1016:23:24SetCmdOnsetshelly02on0setstatePool_PV_Perl2022-10-2307:17:00TimeEnd16:00setstatePool_PV_Perl2021-12-0117:39:32TimeEndSummer16:00setstatePool_PV_Perl2021-12-0514:20:48TimeEndWinter16:00setstatePool_PV_Perl2022-10-2307:17:00TimeStart12:35setstatePool_PV_Perl2022-06-3012:48:23TimeStartSummer12:35setstatePool_PV_Perl2020-09-0313:10:56TimeStartWinter09:10setstatePool_PV_Perl2022-10-3011:25:40ui_command_1---setstatePool_PV_Perl2022-10-1619:59:44ui_command_1_before---
RAW Definition Pool_Signale (Shelly Modul: shelly1pm)
defmodWaschmaschine_PV_PerlDOIF################################################################################################################\## Eigenverbrauch einschalten: wenn PV Produktion über dem Mindestbedarf ist und die Laufzeit pro Tag noch nicht erreicht ist\##\01_1_Eigenverbrauch_automatisch_An\{if(!([$SELF:state]eq"off")## DOIF enabled\and\([WR_1:SW_Total_PV_P_reserve]>=[$SELF:PowerLimitOn]## Es besteht PV-Überschuss\and[[$SELF:TimeStart]-[$SELF:TimeEnd]]## Das Zeitfenster ist erreicht\andget_Exec("PV_Modus_Ein_timer")<1## Der Wait Timer ist noch nicht gestartet\and[$SELF:Status_1]eq"Aus"## Die Waschmaschine ist aus\and[Waschmaschine_Counter:pulseTimePerDay]<[$SELF:RunTimePerDay]## Die maximale Laufzeit der Waschmaschine ist noch nicht erreicht\)\or[$SELF:ui_command_1]eq"01_1_Eigenverbrauch_automatisch_An"## Hier wird das uiTable select ausgewertet\){\if(AttrVal("$SELF","verbose",0)>=3)\{Log3,"$SELF 01_1 : Waiting for ".([$SELF:PowerLevelMinTime]/60)." Minutes"};;\\ set_Exec("PV_Modus_Ein_timer",[$SELF:PowerLevelMinTime],'set_Reading("Status_1","An");;set_Reading("Status_2","PV Überschuss An");;PV_Modus_Ein_Waschmaschine()');; ## Den PV-Modus verzögert einschalten\ set_Reading("Status_1","Wartend");;\ set_Reading("Status_2","für ".([$SELF:PowerLevelMinTime]/60)." Minuten");;\\set_Reading("ui_command_1","---");;## Hier wird das uiTable select wieder zurückgesetzt, ansonsten\## kann das Kommando nicht sofort wiederholt werden\}\}\\################################################################################################################\## Manuell das Gerät einschalten.\##\01_2_Eigenverbrauch_manuell_An\{if(!([$SELF:state]eq"off")## DOIF enabled\and\([$SELF:Status_1]eq"Aus"\and[shelly03:relay]eq"on"## Der Taster wurde gedrückt\)\or[$SELF:ui_command_1]eq"01_2_Eigenverbrauch_manuell_An"## Hier wird das uiTable select ausgewertet\){\if(AttrVal("$SELF","verbose",0)>=3)\{Log3,"$SELF 01_2 : On for manuel usage"};;\\del_Exec("PV_Modus_Ein_timer");;\set_Reading("Status_1","manuell");;\PV_Modus_Ein_Waschmaschine();;\set_Reading("Status_2","Steckdose manuell An");;\\set_Reading("ui_command_1","---");;## Hier wird das uiTable select wieder zurückgesetzt, ansonsten\## kann das Kommando nicht sofort wiederholt werden\}\}\\################################################################################################################\## Manuelle Verwendung abschalten.\##\01_3_Eigenverbrauch_manuell_Aus\{if(!([$SELF:state]eq"off")## DOIF enabled\and\(([$SELF:Status_1]eq"An"\or[$SELF:Status_1]eq"manuell")\and[shelly03:relay]eq"off"## Der Taster wurde gedrückt\)\or[$SELF:ui_command_1]eq"01_3_Eigenverbrauch_manuell_Aus"## Hier wird das uiTable select ausgewertet\){\if(AttrVal("$SELF","verbose",0)>=3)\{Log3,"$SELF 01_3 : Off after manuel usage"};;\\PV_Modus_Aus_Waschmaschine();;\set_Reading("Status_2","Steckdose manuell Aus");;\\set_Reading("ui_command_1","---");;## Hier wird das uiTable select wieder zurückgesetzt, ansonsten\## kann das Kommando nicht sofort wiederholt werden\}\}\################################################################################################################\## Eigenverbrauch abschalten: wenn Mindestlaufzeit erreicht wurde und Maximallaufzeit pro Tag erreicht ist\##\02_1_Eigenverbrauch_Laufzeit_Aus\{if(!([$SELF:state]eq"off")## DOIF enabled\and\([Waschmaschine_Counter:pulseTimePerDay]>=[$SELF:RunTimePerDay]## Die Tages Laufzeit ist überschritten\and[Waschmaschine_Counter:pulseTimeIncrement]>=[$SELF:RunTimeMin]## Die Mindestlaufzeit ist überschritten\and[$SELF:Status_1]eq"An"## Das Gerät läuft\)\\or[$SELF:ui_command_1]eq"02_1_Eigenverbrauch_Laufzeit_Aus"## Hier wird das uiTable select ausgewertet\){\if(AttrVal("$SELF","verbose",0)>=3)\{Log3,"$SELF 02_1 : Off by runtime"};;\\PV_Modus_Aus_Waschmaschine();;\set_Reading("Status_2","Laufzeit Max Aus");;\\set_Reading("ui_command_1","---");;## Hier wird das uiTable select wieder zurückgesetzt, ansonsten\## kann das Kommando nicht sofort wiederholt werden\}\}\\################################################################################################################\## Eigenverbrauch abschalten: falls die Waschmaschine doch nicht benötigt wurde\##\02_2_Eigenverbrauch_PV_Min_Aus\{if(!([$SELF:state]eq"off")## DOIF enabled\and\([WR_1:SW_Total_PV_P_reserve]<[$SELF:PowerLimitOn]## Der Überschuss ist zu wenig\and[$SELF:Status_1]eq"An"## und die Waschmaschine wartet\and[$SELF:Status_2]eq"PV Überschuss wartend"## auf den start\)\or[$SELF:ui_command_1]eq"02_2_Eigenverbrauch_PV_Min_Aus"## Hier wird das uiTable select ausgewertet\){\if(AttrVal("$SELF","verbose",0)>=3)\{Log3,"$SELF 02_2 : PV-Minimum unterschritten"};;\\PV_Modus_Aus_Waschmaschine();;\set_Reading("Status_2","PV Min Aus");;\\set_Reading("ui_command_1","---");;## Hier wird das uiTable select wieder zurückgesetzt, ansonsten\## kann das Kommando nicht sofort wiederholt werden\}\}\\################################################################################################################\## Stop, wenn es nur ein kurzer peak ist. Dieser Do Zweig setzt den wait timer vom Einschaltkommando cmd_4 wieder außer kraft,\## wenn während der Wartezeit die PV Anlage zuwenig liefert.\##\03___Stop_Wait_Timer\{if(!([$SELF:state]eq"off")## DOIF enabled\and\(\([WR_1:SW_Total_PV_P_reserve]<[$SELF:PowerLimitOn]## Ist die PV-Leistung zu niedrig?\andget_Exec("PV_Modus_Ein_timer")>0## läuft eine Wartezeit\andget_Exec("PV_Modus_Ein_timer")<[$SELF:PowerLevelMinTime]## läuft die Wartezeit bald ab\and[$SELF:Status_1]eq"Wartend"## und gibt es keine manuelle Einschaltung\)\)\or[$SELF:ui_command_1]eq"03___Stop_Wait_Timer"## Hier wird das uiTable select ausgewertet\){\if(AttrVal("$SELF","verbose",0)>=3)\{Log3,"$SELF 03__ : Stop wait timer"};;\del_Exec("PV_Modus_Ein_timer");;## Das Gerät wird nicht mehr eingeschaltet\set_Reading("Status_1","Aus");;\set_Reading("Status_2","warten gestoppt");;\\set_Reading("ui_command_1","---");;## Hier wird das uiTable select wieder zurückgesetzt, ansonsten\## kann das Kommando nicht sofort wiederholt werden\}\}\\################################################################################################################\## Statuswechsel wenn das Waschprogramm gestartet ist\##\04_1_Waschprogramm_wartend\{if(!([$SELF:state]eq"off")## DOIF enabled\and\([shelly03:power]<1## Das Gerät ist bereits\and[Waschmaschine_Counter:pauseTimeIncrement]>120## seit 2 Minuten aus\and[$SELF:Status_1]eq"An"## und gibt es keine manuelle Einschaltung\and\([$SELF:Status_2]eq"PV Überschuss An"## falls die Waschmaschine nicht gebraucht\or[$SELF:Status_2]eq"PV Überschuss wartend")## wird einfach weiter warten\and[$SELF:ui_command_1]eq"---"\)\or[$SELF:ui_command_1]eq"04_1_Waschprogramm_wartend"## Hier wird das uiTable select ausgewertet\){\\if(AttrVal("$SELF","verbose",0)>=3)\{Log3,"$SELF 04_1 : Waschmaschine ist nicht gestartet"};;\\set_Reading("Status_2","PV Überschuss wartend");;\set_Reading("ui_command_1","---");;## Hier wird das uiTable select wieder zurückgesetzt, ansonsten\## kann das Kommando nicht sofort wiederholt werden\}\}\\################################################################################################################\## Statuswechsel wenn das Waschprogramm gestartet ist\##\04_2_Waschprogramm_gestartet\{if(!([$SELF:state]eq"off")## DOIF enabled\and\([shelly03:power]>0## Das Gerät ist bereits gestartet\## and [shelly03:power_Waschmaschine_avg] < 70 ## und verbraucht mehr Leistung als im Standby\and[$SELF:Status_2]ne"Waschprogramm gestartet"## \)\or[$SELF:ui_command_1]eq"04_2_Waschprogramm_gestartet"## Hier wird das uiTable select ausgewertet\){\if(AttrVal("$SELF","verbose",0)>=3)\{Log3,"$SELF 04_2 : Waschprogramm gestartet"};;\\set_Reading("Status_2","Waschprogramm gestartet");;\fhem("set alias=Mobil speak 40 Das Waschprogramm ist gestartet");;\set_Reading("ui_command_1","---");;## Hier wird das uiTable select wieder zurückgesetzt, ansonsten\## kann das Kommando nicht sofort wiederholt werden\}\}\\################################################################################################################\## Gerät Ende\##\05_1_Waschprogramm_Ende\{if(!([$SELF:state]eq"off")## DOIF enabled\and\([shelly03:power]==0## Die Waschmaschine hat abgeschaltet\and[$SELF:Status_2]eq"Waschprogramm gestartet"## und vorher lief das Waschprogramm\\)\or[$SELF:ui_command_1]eq"05_1_Waschprogramm_Ende"## Hier wird das uiTable select ausgewertet\){\\set_Reading("Status_2","Waschprogramm beendet");;\fhem("set alias=Mobil speak 40 Das Waschprogramm ist fertig");;\\if(AttrVal("$SELF","verbose",0)>=3)\{Log3,"$SELF 05_1 : Waschprogramm beendet"};;\\PV_Modus_Aus_Waschmaschine();;\\if([$SELF:ui_command_1]eq"05_1_Waschprogramm_Ende"){\set_Reading("ui_command_1","---");;## Hier wird das uiTable select wieder zurückgesetzt, ansonsten\## kann das Kommando nicht sofort wiederholt werden\}\}\}\\################################################################################################################\## Gerät Abschalten, wenn es nicht verwendet wurde\##\05_2_Waschmaschine_Aus\{if(!([$SELF:state]eq"off")## DOIF enabled\and\([[$SELF:TimeEnd]-[$SELF:TimeStart]]## und auch nicht in der Nachtzeit\and[$SELF:Status_1]ne"manuell"## und gibt es keine manuelle Einschaltung\and[$SELF:Status_2]ne"Waschprogramm gestarted"## und es läuft kein Waschprogramm\and[$SELF:ui_command_1]eq"---"\)\or[$SELF:ui_command_1]eq"05_2_Waschmaschine_Aus"## Hier wird das uiTable select ausgewertet\){\\if(AttrVal("$SELF","verbose",0)>=3)\{Log3,"$SELF 05_2 : Keine PV-Zeit"};;\\del_Exec("PV_Modus_Ein_timer");;## Das Gerät wird nicht mehr eingeschaltet\PV_Modus_Aus_Waschmaschine();;\\if([$SELF:ui_command_1]eq"05_2_Waschmaschine_Aus"){\set_Reading("ui_command_1","---");;## Hier wird das uiTable select wieder zurückgesetzt, ansonsten\## kann das Kommando nicht sofort wiederholt werden\}\}\}\\################################################################################################################\## Geräte Startzeit durch Forecast verschieben. Der Forecast wird um 7:00 im Device PV_Schedule aktualisiert\##\08___Startzeit_nach_forecast\{if(!([$SELF:state]eq"off")## DOIF enabled\and\(\[07:17]## Der Forecast wird um 7:00 im Device PV_Schedule aktualisiert\or[$SELF:ui_command_1]eq"08___Startzeit_nach_forecast"## Hier wird das uiTable select ausgewertet\)\){\if([$SELF:ui_command_1]eq"08___Startzeit_nach_forecast"){## Hier wurde manuell eingeschaltet\set_Reading("ui_command_1_before",[$SELF:ui_command_1]);;\}\\if([WR_1:Solar_Calculation_fc0_day]<[WR_1_Speicher_1_ExternControl:SpeicherMinSOC_fc1_Limit]){\set_Reading("TimeStart",[$SELF:TimeStartWinter]);;\set_Reading("TimeEnd",[$SELF:TimeEndWinter]);;\set_Reading("Status_2","Startzeit für Winter");;\}else{\set_Reading("TimeStart",[$SELF:TimeStartSummer]);;\set_Reading("TimeEnd",[$SELF:TimeEndSummer]);;\set_Reading("Status_2","Startzeit für Sommer");;\}\\if(AttrVal("$SELF","verbose",0)>=3)\{Log3,"$SELF 08__ : Switched to TimeStart ".ReadingsVal("$SELF","TimeStart",0)." TimeEnd ".ReadingsVal("$SELF","TimeEnd",0)};;\\set_Reading("ui_command_1","---");;## Hier wird das uiTable select wieder zurückgesetzt, ansonsten\## kann das Kommando nicht sofort wiederholt werden\}\}\\################################################################################################################\## Definition von Sub Routinen\subs{\subPV_Modus_Ein_Waschmaschine(){## PV-Modus Einschalten\if(AttrVal("$SELF","verbose",0)>=3)\{Log3,"$SELF sub : On"};;\fhem("set Waschmaschine_Counter pauseTimeIncrement 0");;\fhem("".ReadingsVal("$SELF","SetCmdOn",0));;\}\\subPV_Modus_Aus_Waschmaschine(){## PV-Modus Ausschalten\if(AttrVal("$SELF","verbose",0)>=3)\{Log3,"$SELF sub : Off"};;\fhem("".ReadingsVal("$SELF","SetCmdOff",0));;\set_Reading("Status_1","Aus");;\set_Reading("Status_2","Steckdose ist ausgeschaltet");;\}\}attrWaschmaschine_PV_PerlDbLogExclude.*attrWaschmaschine_PV_PerlaliasWaschmaschine_PV_PerlattrWaschmaschine_PV_PerlcommentVersion2021.11.0109:00attrWaschmaschine_PV_Perldisable0attrWaschmaschine_PV_Perlevent-on-change-reading.*attrWaschmaschine_PV_Perlevent_ReadingsStatus_1:[$SELF:Status_1],Status_2:[$SELF:Status_2]attrWaschmaschine_PV_PerlgroupPVEigenverbrauch-SteuerungattrWaschmaschine_PV_Perliconscene_washing_machineattrWaschmaschine_PV_PerlroomStrom->PhotovoltaikattrWaschmaschine_PV_Perlsortby4311attrWaschmaschine_PV_PerluiTable{\packageui_Table;;\$TABLE="style='width:100%;;'";;\\$TD{0..9}{0}="align='center' style='font-size:16px;;border-right-style:solid;;border-color:darkgreen;;border-right-width:2px;;width:26%'";;\\$TD{0..9}{1}="style='border-top-style:solid;;border-bottom-style:solid;;border-right-style:solid;;border-color:darkgreen;;border-top-width:2px;;border-bottom-width:2px;;border-right-width:1px;;width:36%;;font-weight:bold;;'";;\$TD{0..9}{2..4}="style='border-top-style:solid;;border-bottom-style:solid;;border-right-style:solid;;border-color:darkgreen;;border-top-width:2px;;border-bottom-width:2px;;border-right-width:1px;;width:8%;;text-align:center;;'";;\$TD{0..9}{5}="style='border-top-style:solid;;border-bottom-style:solid;;border-right-style:solid;;border-color:darkgreen;;border-top-width:2px;;border-bottom-width:2px;;border-right-width:2px;;width:8%;;text-align:center;;'";;\\subFUNC_Status{\my($value,$min,$colorMin,$statusMin,$colorMiddel,$statusMiddle,$max,$colorMax,$statusMax)=@_;;\my$ret=($value<$min)?'<span style="color:'.$colorMin.'">'.$statusMin.'</span>':($value>$max)?'<span style="color:'.$colorMax.'">'.$statusMax.'</span>':'<span style="color:'.$colorMiddel.'">'.$statusMiddle.'</span>';;\return$ret;;\}\\}\\"$SELF"|"Kommando<dd>Auswahl / Status / / Waschmaschine Status</dd>"|widget([$SELF:ui_command_1],"uzsuDropDown,---,01_1_Eigenverbrauch_automatisch_An,01_2_Eigenverbrauch_manuell_An,01_3_Eigenverbrauch_manuell_Aus,02_1_Eigenverbrauch_Laufzeit_Aus,02_2_Eigenverbrauch_PV_Min_Aus,03___Stop_Wait_Timer,04_1_Waschprogramm_wartend,04_2_Waschprogramm_gestartet,05_1_Waschprogramm_Ende,05_2_Waschmaschine_Aus,08___Startzeit_nach_forecast")|[$SELF:Status_1]."<br>".[$SELF:Status_2]|::ReadingsTimestamp("$SELF","timer_PV_Modus_Ein_timer","")|(([shelly03:power]>7)?'<span style="color:green">Waschmaschine_laeuft</span>':'<span style="color:black">Waschmaschine_aus</span>')\|"Konfiguration<dd>PowerLevelMinTime, | PowerLimit On/Off | Time Start/End</dd><dd>RunTime Min/PerDay</dd>"|""|widget([$SELF:PowerLevelMinTime],"selectnumbers,60,60,900,0,lin")."<br>".widget([$SELF:RunTimeMin],"selectnumbers,300,300,7200,0,lin").widget([$SELF:RunTimePerDay],"selectnumbers,900,300,28800,0,lin")|widget([$SELF:PowerLimitOn],"selectnumbers,250,250,2000,0,lin").widget([$SELF:PowerLimitOff],"selectnumbers,0,50,1000,0,lin")|widget([$SELF:TimeStart],"time").widget([$SELF:TimeEnd],"time")\|"<dd>Sommer, Winter Start / Ende</dd>"|""|""|widget([$SELF:TimeStartSummer],"time").widget([$SELF:TimeStartWinter],"time").|widget([$SELF:TimeEndSummer],"time").widget([$SELF:TimeEndWinter],"time")attrWaschmaschine_PV_Perlverbose3setstateWaschmaschine_PV_Perl2022-08-1109:48:48PowerLevelMinTime300setstateWaschmaschine_PV_Perl2022-09-2613:47:21PowerLimitOff250setstateWaschmaschine_PV_Perl2022-10-0617:45:40PowerLimitOn2000setstateWaschmaschine_PV_Perl2022-08-1109:52:38RunTimeMin5400setstateWaschmaschine_PV_Perl2022-08-1109:52:45RunTimePerDay19200setstateWaschmaschine_PV_Perl2022-08-1112:27:42SetCmdOffsetshelly03off0setstateWaschmaschine_PV_Perl2022-08-1112:27:19SetCmdOnsetshelly03on0setstateWaschmaschine_PV_Perl2022-11-0109:21:23Status_1-setstateWaschmaschine_PV_Perl2022-11-0109:21:25Status_2-setstateWaschmaschine_PV_Perl2022-11-0107:17:00TimeEnd18:00setstateWaschmaschine_PV_Perl2022-10-0617:32:31TimeEndSummer18:00setstateWaschmaschine_PV_Perl2022-08-1109:46:58TimeEndWinter16:00setstateWaschmaschine_PV_Perl2022-11-0107:17:00TimeStart08:00setstateWaschmaschine_PV_Perl2022-08-1109:48:05TimeStartSummer08:00setstateWaschmaschine_PV_Perl2022-08-1109:46:54TimeStartWinter10:00setstateWaschmaschine_PV_Perl2022-11-0109:21:25ui_command_1---setstateWaschmaschine_PV_Perl2022-10-0619:34:48ui_command_1_before---
RAW Definition Waschmaschine_Signale (Shelly Modul: shelly1pm)
Beispiel Brunnenpumpe (mit extra Taster zum Aktivieren)
Das Beispiel für die Brunnenpumpe hat eine Besonderheit. Damit nicht vergessen wird die Brunnenpumpe nach der Benutzung wieder Stromlos zu schalten geschieht dies nach einer eingestellten Zeit, wenn sie nicht verwendet wurde. Also immer schön die Düse geöffnet halten :-) Des weitern kann die Pumpe über einen taster am Shelly2.5 aktiviert werden, was dem Elektriker gesagt werden sollte, damit er die Verdrahtung korrekt macht. Der Shelly2.5 aus diesem Beispiel wird ebenfalls für das nächste Beispiel verwendet, da die Steckdosen nebeneinander sind.
defmodBrunnen_PVDOIF################################################################################################################\## 1 Eigenverbrauch sperren: wenn Mindestlaufzeit erreicht wurde und Maximallaufzeit pro Tag erreicht ist\## jedoch nicht wenn manueller Betrieb aktiv ist.\##\([Brunnen_Counter:pulseTimePerDay]>=[Brunnen:RunTimePerDay]and\[Brunnen_Counter:pulseTimeIncrement]>=[Brunnen:RunTimeMin]and\[Brunnen:state]eq"on"and[Brunnen:Brunnen_Button]eq"off")\\({Log3,"Brunnen cmd_1 : Eigenverbrauch sperren"}\{fhem("".ReadingsVal("Brunnen","SetCmdOff",0))}\{fhem("setreading Brunnen_PV Brunnen_Status Steckdose ist ausgeschaltet")}\{fhem("set Brunnen off")}\)\################################################################################################################\## 2 Eigenverbrauch sperren: wenn Mindestlaufzeit erreicht wurde und die PV-Produktion unter dem Mindestbedarf ist\## ausser bei manuellem Einschalten und wenn der Brunnen bereits läuft\##\DOELSEIF\([PV_1:Total_PV_Power_reserve]<[Brunnen:PowerLimitOn]and\[Brunnen:state]eq"on"and\[$SELF:cmd_nr]ne"5"and[$SELF:cmd_nr]ne"7")\\({Log3,"Brunnen cmd_2 : Eigenverbrauch sperren"}\{fhem("".ReadingsVal("Brunnen","SetCmdOff",0))}\{fhem("setreading Brunnen_PV Brunnen_Status Steckdose ist ausgeschaltet")}\{fhem("set Brunnen off")}\)\################################################################################################################\## 3 Stop, wenn es nur ein kurzer Peak ist. Dieser Do Zweig setzt den wait timer vom Einschaltkommando cmd_4\## wieder außer kraft, wenn während der Wartezeit die PV-Anlage zuwenig liefert.\##\DOELSEIF\([PV_1:Total_PV_Power_reserve]<[Brunnen:PowerLimitOff]and\[Brunnen_PV:wait_timer]ne"no timer"and\[Brunnen_PV:wait_timer]ne""and\[Brunnen:state]eq"off")\\({Log3,"Brunnen cmd_3 : Brunnen stop wait timer"})\################################################################################################################\## 4 Eigenverbrauch freigeben: wenn PV-Produktion über dem Mindestbedarf ist und PV-Strom ins Netz eingespeist\## wird und die Laufzeit pro Tag noch nicht erreicht ist\## Dies ist beim Brunnen nicht aktiv, um eine Überschwemmung zu vermeiden\## Es wird nur eine log Meldung geschrieben\##\DOELSEIF\([PV_1:Total_PV_Power_reserve]>[Brunnen:PowerLimitOn]and\[Brunnen:state]eq"off"and\[[Brunnen:TimeStart]-[Brunnen:TimeEnd]]and\[Brunnen_Counter:pulseTimePerDay]<[Brunnen:RunTimePerDay])\\({Log3,"Brunnen cmd_4 : Brunnen freigabe"}\{fhem("".ReadingsVal("Brunnen","SetCmdOn",0))}\{fhem("setreading Brunnen_PV Brunnen_Status Steckdose kann eingeschaltet werden")}\## {fhem("set Brunnen on")}\)\################################################################################################################\## 5 Steckdose manuell für die Benutzung des Brunnens einschalten.\##\DOELSEIF\([Brunnen:Brunnen_Button]eq"on")\\({Log3,"Brunnen cmd_5 : Brunnen manuell ein"}\{fhem("".ReadingsVal("Brunnen","SetCmdOn",0))}\{fhem("setreading Brunnen_PV Brunnen_Status Steckdose ist eingeschaltet")}\{fhem("set Brunnen on")}\)\################################################################################################################\## 6 Steckdose des Brunnen manuell abschalten.\##\DOELSEIF\([Brunnen:Brunnen_Button]eq"off"and\([$SELF:cmd_nr]eq"5"or[$SELF:cmd_nr]eq"7"))\\({Log3,"Brunnen cmd_6 : Brunnen manuell aus"}\{fhem("".ReadingsVal("Brunnen","SetCmdOff",0))}\{fhem("setreading Brunnen_PV Brunnen_Status Steckdose ist ausgeschaltet")}\{fhem("set Brunnen off")}\)\################################################################################################################\## 7 Statuswechsel wenn die Brunnenpumpe läuft\##\DOELSEIF\([shelly05:power_0]>600and\[$SELF:cmd_nr]ne"7")\\({Log3,"Brunnen cmd_7 : Brunnenpumpe läuft"}\{fhem("setreading Brunnen_PV Brunnen_Status Brunnenpumpe gestartet")}\{fhem("set Brunnen on")}\)\################################################################################################################\## 8 Abschalten der Steckdose wenn Brunnenpumpe durch Druckschalter abschaltet\##\DOELSEIF\([shelly05:power_0]==0)\\({Log3,"Brunnen cmd_8 : Brunnenpumpe aus"}\{fhem("".ReadingsVal("Brunnen","SetCmdOff",0))}\{fhem("set Brunnen off")}\{fhem("setreading Brunnen Brunnen_Button off")}\{fhem("setreading Brunnen_PV Brunnen_Status Brunnenpumpe abgeschaltet")}\)\################################################################################################################\## 9 Abschalten der Steckdose wenn der Brunnen nicht gebraucht wird\##\DOELSEIF\([[Brunnen:TimeEnd]-[Brunnen:TimeStart]]and\([Brunnen:state]eq"on"or[shelly05:relay_0]eq"on"))\\({Log3,"Brunnen cmd_9 : Eigenverbrauch sperren"}\{fhem("".ReadingsVal("Brunnen","SetCmdOff",0))}\{fhem("set Brunnen off")}\{fhem("setreading Brunnen Brunnen_Button off")}\{fhem("setreading Brunnen_PV Brunnen_Status Steckdose ist ausgeschaltet")}\)attrBrunnen_PVDbLogExclude.*attrBrunnen_PVDbLogIncludestate,STATE,cmd.*,Device,Brunnen_Status,wait_timerattrBrunnen_PValiasBrunnen_PVattrBrunnen_PVcmdStateMaximalzeitproTagüberschritten|Eigenverbrauchgesperrt|Stopwaittimer|Eigenverbrauchfreigegeben|Brunnenmanuellein|Brunnenmanuellaus|Brunnenlaeuft|Brunnenaus|EigenverbrauchgesperrtattrBrunnen_PVcommentVersion2020.10.1918:28attrBrunnen_PVdoalwaysattrBrunnen_PVgroupPVEigenverbrauch-SteuerungattrBrunnen_PViconwellattrBrunnen_PVroomStrom->PhotovoltaikattrBrunnen_PVsortby31attrBrunnen_PVstateFormatstate:Brunnen_StatusattrBrunnen_PVverbose0attrBrunnen_PVwait0:0:0:[Brunnen:PowerLevelMinTime]:0:30:0:60:0
RAW Definition Brunnen_Signale (Shelly Modul: shelly2.5)
Die Einschaltung der Brunnenpumpe erfolgt mit Relais 1 . Am Shelly2.5 ist auch ein Taster verdrahtet, über den die Pumpe vor Ort aktiviert werden kann.
In diesem Beispiel wird der Akku eines Rasenroboters und in unserem Fall auch des E-Bikes an einer Steckdose geladen. Ziel ist es auch über den Winter immer wieder die Akkus am Leben zu erhalten.
defmodShaun_PVDOIF################################################################################################################\## 1 Eigenverbrauch sperren: wenn Mindestlaufzeit erreicht wurde und Maximallaufzeit pro Tag erreicht ist\## jedoch nicht wenn manueller Betrieb aktiv ist.\##\([Shaun_Counter:pulseTimePerDay]>=[Shaun:RunTimePerDay]and\[Shaun_Counter:pulseTimeIncrement]>=[Shaun:RunTimeMin]and\[Shaun:state]eq"on"and[Shaun:Shaun_Button]eq"off")\\({Log3,"Shaun cmd_1 : Eigenverbrauch sperren"}\{fhem("".ReadingsVal("Shaun","SetCmdOff",0))}\{fhem("setreading Shaun_PV Shaun_Status Steckdose ist ausgeschaltet")}\{fhem("set Shaun off")}\)\################################################################################################################\## 2 Eigenverbrauch sperren: wenn Mindestlaufzeit erreicht wurde und die PV-Produktion unter dem Mindestbedarf ist\## ausser bei manuellem Einschalten und wenn das Shaun Laden bereits läuft\##\DOELSEIF\([PV_1:Total_PV_Power_reserve]<[Shaun:PowerLimitOn]and\[Shaun:state]eq"on"and\[$SELF:cmd_nr]ne"5"and[$SELF:cmd_nr]ne"7")\\({Log3,"Shaun cmd_2 : Eigenverbrauch sperren"}\{fhem("".ReadingsVal("Shaun","SetCmdOff",0))}\{fhem("setreading Shaun_PV Shaun_Status Steckdose ist ausgeschaltet")}\{fhem("set Shaun off")}\)\################################################################################################################\## 3 Stop, wenn es nur ein kurzer Peak ist. Dieser Do Zweig setzt den wait timer vom Einschaltkommando cmd_4\## wieder außer kraft, wenn während der Wartezeit die PV-Anlage zuwenig liefert.\##\DOELSEIF\([PV_1:Total_PV_Power_reserve]<[Shaun:PowerLimitOff]and\[Shaun_PV:wait_timer]ne"no timer"and\[Shaun_PV:wait_timer]ne""and\[Shaun:state]eq"off")\\({Log3,"Shaun cmd_3 : Shaun stop wait timer"})\################################################################################################################\## 4 Eigenverbrauch freigeben: wenn PV-Produktion über dem Mindestbedarf ist und PV-Strom ins Netz eingespeist\## wird und die Laufzeit pro Tag noch nicht erreicht ist\##\DOELSEIF\([PV_1:Total_PV_Power_reserve]>[Shaun:PowerLimitOn]and\[Shaun:state]eq"off"and\[[Shaun:TimeStart]-[Shaun:TimeEnd]]and\[Shaun_Counter:pulseTimePerDay]<[Shaun:RunTimePerDay])\\({Log3,"Shaun cmd_4 : Shaun freigabe"}\{fhem("".ReadingsVal("Shaun","SetCmdOn",0))}\{fhem("setreading Shaun_PV Shaun_Status Steckdose ist eingeschaltet")}\{fhem("set Shaun on")}\)\################################################################################################################\## 5 Steckdose manuell für die Benutzung des Shaun einschalten.\##\DOELSEIF\([Brunnen:Brunnen_Button]eq"on")\\({Log3,"Shaun cmd_5 : Shaun manuell ein"}\{fhem("".ReadingsVal("Shaun","SetCmdOn",0))}\{fhem("setreading Shaun_PV Shaun_Status Steckdose ist eingeschaltet")}\{fhem("set Shaun on")}\)\################################################################################################################\## 6 Steckdose des Shaun manuell abschalten.\##\DOELSEIF\([Shaun:Shaun_Button]eq"off"and\([$SELF:cmd_nr]eq"5"or[$SELF:cmd_nr]eq"7"))\\({Log3,"Shaun cmd_6 : Shaun manuell aus"}\{fhem("".ReadingsVal("Shaun","SetCmdOff",0))}\{fhem("setreading Shaun_PV Shaun_Status Steckdose ist ausgeschaltet")}\{fhem("set Shaun off")}\)\################################################################################################################\## 7 Statuswechsel wenn das Laden läuft\##\DOELSEIF\([shelly05:power_1]>15and\[$SELF:cmd_nr]ne"7")\\({Log3,"Shaun cmd_7 : Shaun läuft"}\{fhem("setreading Shaun_PV Shaun_Status Shaun wird geladen")}\)\################################################################################################################\## 8 Abschalten der Steckdose wenn Shaun geladen ist\##\DOELSEIF\([shelly05:power_1]<=6and\[$SELF:cmd_nr]eq"7")\\({Log3,"Shaun cmd_8 : Shaun aus"}\{fhem("".ReadingsVal("Shaun","SetCmdOff",0))}\{fhem("set Shaun off")}\{fhem("setreading Shaun Shaun_Button off")}\{fhem("setreading Shaun_PV Shaun_Status Shaun hat geladen")}\)\################################################################################################################\## 9 Abschalten der Steckdose wenn es nicht mehr benötigt wird\##\DOELSEIF\(([PV_1:Total_PV_Power_reserve]<[Shaun:PowerLimitOn]or\[[Shaun:TimeEnd]-[Shaun:TimeStart]])and\[Shaun:state]eq"on"and\[$SELF:cmd_nr]ne"5")\\({Log3,"Shaun cmd_9 : Eigenverbrauch sperren"}\{fhem("".ReadingsVal("Shaun","SetCmdOff",0))}\{fhem("set Shaun off")}\{fhem("setreading Shaun Shaun_Button off")}\{fhem("setreading Shaun_PV Shaun_Status Steckdose ist ausgeschaltet")}\)attrShaun_PVDbLogExclude.*attrShaun_PVDbLogIncludestate,STATE,cmd.*,Device,Shaun_Status,wait_timerattrShaun_PValiasShaun_PVattrShaun_PVcmdStateMaximalzeitproTagüberschritten|Eigenverbrauchgesperrt|Stopwaittimer|Eigenverbrauchfreigegeben|Shaunmanuellein|Shaunmanuellaus|Shaunlaed|Shaunaus|EigenverbrauchgesperrtattrShaun_PVcommentVersion2020.10.1918:28attrShaun_PVdoalwaysattrShaun_PVgroupPVEigenverbrauch-SteuerungattrShaun_PViconscene_robo_lawnmowerattrShaun_PVroomStrom->PhotovoltaikattrShaun_PVsortby41attrShaun_PVstateFormatstate:Shaun_StatusattrShaun_PVverbose0attrShaun_PVwait0:0:0:[Shaun:PowerLevelMinTime]:0:30:0:60:30
RAW Definition Shaun_Signale (Shelly Modul: shelly2.5)
Für die Ladung der Akkus wird hier das Relais 0 verwendet.