Kostal Plenticore 10 Plus: Unterschied zwischen den Versionen

Aus FHEMWiki
K (diverse kleine Korrekturen)
 
(272 dazwischenliegende Versionen von 3 Benutzern werden nicht angezeigt)
Zeile 1: Zeile 1:
[[Bild:Kostal Flow.PNG|mini|700px|rechts|Sowas könnte am Schluss raus kommen. Schreibt mich gerne an ;-)]]
{{Infobox Hardware
{{Infobox Hardware
|Bild=Kostal_Plenticore_10_Plus.jpg
|Bild=Kostal_Plenticore_10_Plus.jpg
|Bildbeschreibung=Kostal Plenticore 10 Plus
|Bildbeschreibung=Kostal Plenticore Plus
|HWProtocol=IP und RS485
|HWProtocol=IP und RS485
|HWType=Hybrid Wechselrichter
|HWType=Hybrid Wechselrichter
Zeile 14: Zeile 15:
|HWManufacturer=KOSTAL Solar Electric GmbH
|HWManufacturer=KOSTAL Solar Electric GmbH
}}
}}
[[Bild:Plenticore FHEM 1.png|mini|900px|rechts|Die Diagramme im Überblick]]
[[Datei:Leistung und Hauptverbraucher.png|mini|700px|rechts|]]
[[Bild:Plenticore FHEM 2.png|mini|900px|rechts|Geräte Überblick mit manueller Schaltmöglichkeit]]
[[Datei:Forecast.png|mini|700px|rechts|]]
[[Bild:Plenticore FHEM 3.png|mini|900px|rechts|Steuerungsgeräte für die Schaltlogik]]
[[Bild:WR 1.PNG|mini|900px|rechts|Wechselrichter 1 mit Status]]
[[Bild:Plenticore FHEM 4.png|mini|900px|rechts|ReadingsGroup für die schnelle Parametereinstellung]]
[[Bild:WR 1 Speicher 1.PNG|mini|900px|rechts|WR_1_Speicher_1_ExternControl]]
[[Bild:WR 2.PNG|mini|900px|rechts|Wechselrichter 2 und gesteuerte Geräte]]
[[Bild:Kia.PNG|mini|900px|rechts|WallBox mit Kia Fahrzeug]]
[[Bild:Geräte.PNG|mini|900px|rechts|PV 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.
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.


Der [[Kostal Plenticore 10 Plus|Kostal Plenticore Plus]] ([https://www.kostal-solar-electric.com/de-de/produkte/hybrid-wechselrichter/ Webseite des Herstellers]) ist ein Hybrid Wechselrichter mit IP-Konnektivität.  
Der [[Kostal Plenticore 10 Plus|Kostal Plenticore Plus]] ([https://www.kostal-solar-electric.com/de-de/produkte/hybrid-wechselrichter/plenticore-plus/ Webseite des Herstellers]) ist ein Hybrid Wechselrichter mit IP-Konnektivität.  


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!
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 ==
==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.
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 ==
==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.
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.
Weiterhin ist der Betreiber verpflichtet, eine Anmeldung im Marktstammdatenregister vorzunehmen.


== Hersteller Dokumentation ==
 
==Hersteller Dokumentation==
Für Links auf dieser Wiki Seite wird keine Haftung übernommen. Die Inhalte unterliegen der Verantwortung der Firma Kostal.
Für Links auf dieser Wiki Seite wird keine Haftung übernommen. Die Inhalte unterliegen der Verantwortung der Firma Kostal.
* [https://www.kostal-solar-electric.com/de-de/service-und-support/smart-warranty/ Plenticore Plus - Verlängerung der Gewährleistungszeit]
* [https://www.kostal-solar-electric.com/de-de/service-und-support/smart-warranty/ Plenticore Plus - Verlängerung der Gewährleistungszeit]
* [https://www.kostal-solar-electric.com/de-de/download/-/media/document-library-folder---kse/2018/06/15/11/40/plenticore-plus_ba_de.pdf/ Plenticore Plus - Betriebsanleitung]
* [https://www.kostal-solar-electric.com/de-de/download/-/media/document-library-folder---kse/2020/12/15/13/14/plenticore-plus_ba_de.pdf/ Plenticore Plus - Betriebsanleitung]
* [https://www.kostal-solar-electric.com/de-de/download/-/media/document-library-folder---kse/2018/04/12/08/26/plenticore-plus_kba.pdf/ Plenticore Plus - Kurzanleitung]
* [https://www.kostal-solar-electric.com/de-de/download/-/media/document-library-folder---kse/2021/02/04/10/49/lf_verschaltung-mehrerer-kostal-wechselrichter_de.pdf/ Plenticore Plus - Verschaltung und Einrichtung mehrerer KOSTAL-Wechselrichter]
* [https://www.kostal-solar-electric.com/de-de/download/-/media/document-library-folder---kse/2018/04/12/08/26/db_plenticore-plus_de.pdf/ Plenticore Plus - Datenblatt]
* [https://www.kostal-solar-electric.com/de-de/download/-/media/document-library-folder---kse/2020/12/15/13/06/plenticore-plus_kba.pdf/ Plenticore Plus - Kurzanleitung]
* [https://www.kostal-solar-electric.com/de-de/download/-/media/document-library-folder---kse/2019/03/12/07/13/kostal_update_plenticore_piko_iq_011504581.swu/ Plenticore Plus - Software Update UI: 01.15.04581 FW: 01.43]
* [https://www.kostal-solar-electric.com/de-de/download/download/-/media/document-library-folder---kse/2020/12/15/13/06/db_plenticore-plus_de.pdf/ Plenticore Plus - Datenblatt]
* [https://www.kostal-solar-electric.com/de-de/download/download/-/media/document-library-folder---kse/2020/12/15/13/42/kostal_update_plenticore_piko_iq_012106586.swu/ Plenticore Plus - Software Update 01.21]


* [https://www.kostal-solar-electric.com/de-de/download/-/media/document-library-folder---kse/2018/08/30/08/53/ba_kostal_interface_modbus-tcp_sunspec.pdf/ Plenticore - Modbus\/ Sunspec]
* [https://www.kostal-solar-electric.com/de-de/download/download/-/media/document-library-folder---kse/2020/12/15/13/38/ba_kostal-interface-description-modbus-tcp_sunspec_hybrid.pdf/ Plenticore - Modbus\/ Sunspec]


* [https://www.kostal-solar-electric.com/de-de/download/-/media/document-library-folder---kse/2019/03/06/13/17/ksem-ba_de.pdf/ KSEM - Betriebsanleitung]
* [https://www.kostal-solar-electric.com/de-de/download/-/media/document-library-folder---kse/2020/12/15/13/41/ksem-ba_de.pdf/ KSEM - Betriebsanleitung]
* [https://www.kostal-solar-electric.com/de-de/download/-/media/document-library-folder---kse/2019/03/06/13/17/ksem-db_de.pdf/ KSEM - Datenblatt]
* [https://www.kostal-solar-electric.com/de-de/download/-/media/document-library-folder---kse/2020/12/15/13/41/ksem-db_de.pdf/ KSEM - Datenblatt]
* [https://www.kostal-solar-electric.com/de-de/download/-/media/document-library-folder---kse/2020/06/09/11/49/kostal_update_ksem_1_2_1.zip/ KSEM - Software Update - 1.2.1]
* [https://www.kostal-solar-electric.com/de-de/download/-/media/document-library-folder---kse/2020/12/15/14/13/kostal_update_ksem_1_3_0.raucb/ KSEM - Software Update - 1.3.0]


* [https://www.kostal-solar-electric.com/de-de/download/-/media/document-library-folder---kse/2019/05/09/13/57/ba_kostal_interface_ksem---201911.pdf/ KSEM - Modbus\/ Sunspec]
* [https://www.kostal-solar-electric.com/de-de/download/-/media/document-library-folder---kse/2020/12/15/14/22/ba_kostal_interface_ksem_de.pdf/ KSEM - Modbus\/ Sunspec]


* [https://www.kostal-solar-electric.com/de-de/service-und-support/kontakt/ Kostal - Service Kontakt]
* [https://www.kostal-solar-electric.com/de-de/service-und-support/kontakt/ Kostal - Service Kontakt]


== Einbindung in das Netzwerk ==
==Kompatibilität zu anderen Kostal Wechselrichtern==
Auf Grund von Anfragen scheint diese Implementierung auch für andere Kostal Wechselrichtern zu passen.
- Plenticore Plus
- Piko MP
- 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.
 
==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.
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.


Zeile 57: Zeile 70:
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.
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 ==
==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.
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 ==
==Voraussetzungen FHEM Umfeld==
=== Alle Geräte müssen mit TCP/IP erreichbar sein ===
===Alle Geräte müssen mit TCP/IP erreichbar sein===
=== Alle Module sollten auf einem aktuellen Stand sein ===
===Alle Module sollten auf einem aktuellen Stand sein===
=== Eine DbLog/DbRep sollte bereits vorhanden sein, was hier nicht weiter erklärt wird. ===
===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.
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 ===
===Verwendete Module===
* Modbus
* Modbus
* HTTPMOD
* HTTPMOD
Zeile 78: Zeile 92:
* HourCounter
* HourCounter
* DWD_OpenData
* DWD_OpenData
* readingsGroup
* WeekdayTimer
* weekDayTimer
* SVG


=== Verwendetes Umfeld ===
===Verwendetes Umfeld===
* Raspberry Pi 4
* Raspberry Pi 4
* Debian
* Debian
* Docker
* Docker
:: fhem/fhem:latest
:: fhem/fhem:latest
:: hypriot/rpi-mysql
:: mysql/mysql-server
:: grafana/grafana:latest
:: grafana/grafana:latest
* Perl
* Perl
* Python
==Einbindung in FHEM: Überblick==


== Einbindung in FHEM: Überblick ==
===MySQL etwas Basis Information===
=== Hardware Anbindung (alles über LAN) ===
====Docker Compose nach installieren====
==== Kostal Plenticore Plus ====
Falls Docker Compose nicht bereits installiert ist.
===== Kostal Plenticore Plus die Basis information (Modbus/TCP) =====
<syntaxhighlight lang="Perl">
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.
pi@raspberrypi:~ $ sudo apt-get install docker-compose
Paketlisten werden gelesen... Fertig
Abhängigkeitsbaum wird aufgebaut.
Statusinformationen werden eingelesen.... Fertig
docker-compose ist schon die neueste Version (1.21.0-3).
</syntaxhighlight>
https://docs.docker.com/engine/reference/commandline/compose/


====== Plenticore Modbus Definition ======
Einige Docker Kommandos:
<pre>
<pre>
GeräteId  : 71
Für den Überblick:
IP-Adresse: <IP-Adresse>
    docker stats
Port     : 1502
    docker ps
 
Zum aktualisieren (nur ein Beispiel):
     docker pull portainer/portainer:latest
    docker-compose up -d
</pre>
</pre>


==== Ein zweiter Wechselrichter ====
====MySQL Docker Container mit .yml====
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.
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]
<syntaxhighlight lang="Perl">
pi@raspberrypi:~/docker-compose/fhem_2022 $ pwd
/home/pi/docker-compose/fhem_2022
pi@raspberrypi:~/docker-compose/fhem_2022 $ ls -l docker-compose.yml
</syntaxhighlight>


====== Modbus Timing ======
====Administration etwas erleichtern====
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.  
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.
<syntaxhighlight lang="Perl">
pi@raspberrypi:~ $ cat /etc/passwd
< snip >
fhem:x:6061:6061:FHEM,,,:/home/pi/docker-compose/fhem_2024/fhem:/bin/bash
</syntaxhighlight>


Die Zeit kann auch verändert werden, jedoch sollte sie nicht zu kurz gewählt sein.  
====MySQL initial konfigurieren====
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
<pre>
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.
</pre>
Dazu gibt es zwei Varianten:
1. Über den Docker Container anmelden
<syntaxhighlight lang="Perl">
pi@raspberrypi:~ $ docker exec -it fhem_2022_mysql_1 mysql -u root -p
Enter password:
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 17517
Server version: 8.0.32 MySQL Community Server - GPL
mysql>
</syntaxhighlight>
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.
<syntaxhighlight lang="Perl">
bash-4.4# mysql -u root -p
Enter password:
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 44892
Server version: 8.0.28 MySQL Community Server - GPL
mysql>
</syntaxhighlight>


====== RAW Definition WR_1_config ======
=====MySQL Kommandos=====
Dieses Dummy soll alle Konfigurationsparameter halten, auf die dann die anderen Geräte Definitionen zentral zugreifen. Hier können auch default Namen und Vorschläge für Werte in Form von Slidern und Auswahllisten hinterlegt werden.
<syntaxhighlight lang="sql">
-- Die fhem Datenbank neu anlegen
mysql> CREATE DATABASE `fhem` DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_bin;


Bei mehreren Wechselrichtern kann man die Module mit ihren Ausrichtungen für die Leistungsprognose alle in die erste Konfiguration legen. So ergibt sich später eine gemeinsame Prognose für die gesamte PV-Anlage. Wenn man dies auf einzelne Konfigurationsdummys aufteilt, kann man für jeden Wechselrichter eine eigene Prognose erstellen. Eine Einzel- und Gesamtprognose wäre ebenfalls denkbar. Fragen dazu bitte im Forum stellen.
-- Die FHEM Tabellen neu anlegen
-- Für die Spalte VALUE wurde mal eine längere Definition gewählt
mysql> CREATE TABLE `fhem`.`history` (TIMESTAMP TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, DEVICE varchar(64), TYPE varchar(64), EVENT varchar(512), READING varchar(64), VALUE varchar(255), UNIT varchar(32));
mysql> CREATE TABLE `fhem`.`current` (TIMESTAMP TIMESTAMP, DEVICE varchar(64), TYPE varchar(64), EVENT varchar(512), READING varchar(64), VALUE varchar(128), UNIT varchar(32));


In der readingList und setList können die Namen der Ausrichtung frei gewählt werden.
-- Den Index einrichten und über das READING mit definieren, damit kein duplicate Key entstehen kann
mysql> CREATE INDEX Search_Idx ON `fhem`.`history` (TIMESTAMP, DEVICE, READING) USING BTREE;
Query OK, 0 rows affected (0.11 sec)
Records: 0  Duplicates: 0  Warnings: 0


Die default Einstellungen wurden bereits über Sommer und Winter getestet.
-- Im Oracle MySQL wird im Standard das Passwort in einer anderen Verschlüsselung abgeregt, die FHEM noch nicht unterstützt
<syntaxhighlight lang="Perl">
-- Deshalb kann es erforderlich sein das Passwort, nach Änderung der Verschlüsselungsmethode neu zu setzen
defmod WR_1_config dummy
attr WR_1_config DbLogExclude .*
attr WR_1_config alias WR_1_config
attr WR_1_config comment Version 2021.04.07 12:00\
Passworte für die Abfrage des WR_1_API werden im storeKeyValue abgelegt:\
  {KeyValue("[read|store]","PW_<Device Name>_<Benutzer Name>","<passwort>")}\
  {KeyValue("store","PW_WR_1_API_user","<passwort>")}\
\
Steht das reading module_*_count auf 0 wird diese Ausrichtung nicht berücksichtigt\
\
Korrekturkurven:\
        Steilheit  Parallel\
                    verschiebung\
tempk      -0.39      25\
cloudk    -0.65      0\
raink      -0.30      0\
Der Slider für die Steilheit wird mit - k/100 umgerechnet. 39 ==> -0.39
attr WR_1_config event-on-change-reading .*
attr WR_1_config group PV Eigenverbrauch
attr WR_1_config icon solar_icon
attr WR_1_config readingList IP-WR_1 IP-WR_1_Speicher_1 IP-WR_1_KSEM IP-FHEM module_1_active module_2_active module_3_active module_1_name module_2_name module_3_name  module_4_name module_5_name module_1_direction module_2_direction module_3_direction module_4_direction module_5_direction module_1_count module_2_count module_3_count module_4_count module_5_count module_1_power module_2_power module_3_power module_4_power module_5_power module_1_plain module_2_plain module_3_plain module_4_plain module_5_plain forecast_cloudk forecast_cloudk_base forecast_raink forecast_raink_base forecast_tempk forecast_tempk_base forecast_factor forecast_factor_autocorrection Forecast_Station
attr WR_1_config room Strom->Photovoltaik
attr WR_1_config setList IP-WR_1 IP-WR_1_Speicher_1 IP-WR_1_KSEM IP-FHEM module_1_name:WR_1_Ost,WR_1_West,WR_2_Sued,WR_2_West,East,SouthEast,South,SouthWest,West,Garage,CarPort module_2_name:WR_1_Ost,WR_1_West,WR_2_Sued,WR_2_West,East,SouthEast,South,SouthWest,West,Garage,CarPort module_3_name:WR_1_Ost,WR_1_West,WR_2_Sued,WR_2_West,East,SouthEast,South,SouthWest,West,Garage,CarPort,frei module_4_name:WR_1_Ost,WR_1_West,WR_2_Sued,WR_2_West,East,SouthEast,South,SouthWest,West,Garage,CarPort,frei module_5_name:WR_1_Ost,WR_1_West,WR_2_Sued,WR_2_West,East,SouthEast,South,SouthWest,West,Garage,CarPort,frei module_1_direction module_2_direction module_3_direction module_4_direction module_5_direction module_1_count module_2_count module_3_count module_4_count module_5_count module_1_power module_2_power module_3_power module_4_power module_5_power module_1_plain module_2_plain module_3_plain module_4_plain module_5_plain forecast_cloudk forecast_cloudk_base forecast_raink forecast_raink_base forecast_tempk forecast_tempk_base forecast_factor forecast_factor_autocorrection Forecast_Station
attr WR_1_config sortby 113
attr WR_1_config verbose 0


setstate WR_1_config 2020-09-11 07:36:39 Forecast_Station P0178
-- Nachschauen welche Passwort Verschlüsselung verwendet wird
setstate WR_1_config 2020-08-31 12:39:26 IP-FHEM 192.168.178.xxx
-- FHEM unterstützt nur mysql_native_password
setstate WR_1_config 2020-08-31 12:39:44 IP-WR_1 192.168.178.yyy
mysql> SELECT Host,User,plugin FROM mysql.user WHERE User='fhemuser';
setstate WR_1_config 2020-08-31 12:43:27 IP-WR_1_KSEM 192.168.178.zzz
+------+----------+-----------------------+
setstate WR_1_config 2020-08-31 12:44:46 IP-WR_1_Speicher_1 192.168.178.xyz
| Host | User    | plugin                |
setstate WR_1_config 2020-09-22 10:03:21 forecast_cloudk 45
+------+----------+-----------------------+
setstate WR_1_config 2020-09-22 10:12:17 forecast_cloudk_base 0
| %    | fhemuser | caching_sha2_password |    <<< Das unterstützt FHEM mit DbLog noch nicht
setstate WR_1_config 2020-12-07 15:49:18 forecast_factor 1
+------+----------+-----------------------+
setstate WR_1_config 2021-03-31 11:45:19 forecast_factor_autocorrection 1
setstate WR_1_config 2020-09-02 18:40:29 forecast_raink 20
setstate WR_1_config 2020-09-01 12:52:40 forecast_raink_base 0
setstate WR_1_config 2020-09-01 12:46:57 forecast_tempk 39
setstate WR_1_config 2020-09-01 12:50:06 forecast_tempk_base 25
setstate WR_1_config 2021-03-23 14:31:19 module_1_count 20
setstate WR_1_config 2020-08-31 12:27:38 module_1_direction -90
setstate WR_1_config 2021-03-23 14:33:27 module_1_name WR_1_Ost
setstate WR_1_config 2020-08-31 12:29:42 module_1_plain 40
setstate WR_1_config 2020-08-31 12:31:09 module_1_power 310
setstate WR_1_config 2021-03-23 14:41:10 module_2_count 16
setstate WR_1_config 2021-03-23 14:54:13 module_2_direction 90
setstate WR_1_config 2021-03-23 14:33:45 module_2_name WR_1_West
setstate WR_1_config 2020-08-31 12:34:14 module_2_plain 40
setstate WR_1_config 2021-02-04 12:23:00 module_2_power 310
setstate WR_1_config 2021-03-23 14:41:40 module_3_count 13
setstate WR_1_config 2021-03-23 14:54:26 module_3_direction 0
setstate WR_1_config 2021-03-23 14:34:09 module_3_name WR_2_Sued
setstate WR_1_config 2020-08-31 12:35:08 module_3_plain 40
setstate WR_1_config 2021-03-23 14:41:55 module_3_power 340
setstate WR_1_config 2021-03-29 12:20:49 module_4_count 11
setstate WR_1_config 2021-03-23 14:54:37 module_4_direction 90
setstate WR_1_config 2021-03-23 14:34:27 module_4_name WR_2_West
setstate WR_1_config 2020-08-31 12:34:14 module_4_plain 40
setstate WR_1_config 2021-02-04 12:23:00 module_4_power 340
setstate WR_1_config 2021-02-04 12:41:15 module_5_count 0
setstate WR_1_config 2021-03-23 14:53:22 module_5_direction 0
setstate WR_1_config 2021-03-23 14:43:38 module_5_name frei
setstate WR_1_config 2021-03-23 14:50:46 module_5_plain 0
setstate WR_1_config 2021-03-23 14:50:39 module_5_power 0
</syntaxhighlight>


====== RAW Definition WR_1 (bei zwei Wechselrichtern) ======
mysql> DROP USER fhemuser;
Für den Zugriff auf die Plenticore ModBus/TCP Daten wird dieses Device verwendet.
mysql> CREATE USER 'fhemuser'@'%' IDENTIFIED WITH mysql_native_password BY '<password>';
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.
<syntaxhighlight lang="Perl">
defmod PV_1 ModbusAttr 71 60 192.168.178.18:1502 TCP
attr PV_1 userattr obj-h208-format obj-h512-len obj-h515-len
attr PV_1 DbLogExclude .*
attr PV_1 DbLogInclude Act_state_of_charge,Actual_battery_charge_-minus_or_discharge_-plus_Power,Actual_battery_charge_usable_Power,Battery_Total.*,Battery_charge.*,Battery_gross.*,Battery_temperature,Home_own_consumption.*,Power_DC1,Power_DC2,Total_DC_Power.*,Total_DC_PV_Energy.*,Total_PV_Power_reserve,Solar_Calculation,Solar_Calculation_fc0_4h,Solar_Calculation_fc0_day,Solar_Calculation_fc0_rest,Solar_Correction.*,Solar_Cloud,Solar_East,Solar_East_Covered,Solar_Rain,Solar_SolarRadiation,Solar_South,Solar_Temp,Solar_West,Solar_middayhigh.*
attr PV_1 alias PV_1
attr PV_1 comment Version 2020.11.20 11:30\
Kostal Plenticore 10 Plus mit BYD Speicher
attr PV_1 dev-h-combine 8
attr PV_1 dev-h-defFormat %.2f
attr PV_1 dev-h-defLen 2
attr PV_1 dev-h-defPoll 1
attr PV_1 dev-h-defRevRegs 1
attr PV_1 dev-h-defUnpack f>
attr PV_1 dev-type-STR-format %s
attr PV_1 dev-type-STR-len 8
attr PV_1 dev-type-STR-revRegs 0
attr PV_1 dev-type-STR-unpack a*
attr PV_1 disable 0
attr PV_1 event-on-change-reading Act_state_of_charge,Actual_battery_charge_.*,Battery_Total.*,Battery_charge.*,Battery_gross.*,Battery_temperature,Home_own_consumption.*,Power_DC1,Power_DC2,Total_DC_Power.*,Total_DC_PV_Energy.*,Total_PV_Power_reserve,Solar_.*
attr PV_1 group PV Eigenverbrauch
attr PV_1 icon sani_solar
attr PV_1 obj-h100-reading Total_DC_Power
attr PV_1 obj-h104-format %s
attr PV_1 obj-h104-reading State_of_energy_manager
attr PV_1 obj-h104-revRegs 0
attr PV_1 obj-h104-unpack N
attr PV_1 obj-h1046-reading Battery_Total_DC_Charge_Energy_(DC-sideToBattery)
attr PV_1 obj-h1048-reading Battery_Total_DC_Discharge_Energy_(DC-sideFromBattery)
attr PV_1 obj-h1050-reading Battery_Total_AC_Charge_Energy_(AC-sideToBattery)
attr PV_1 obj-h1052-reading Battery_Total_AC_Discharge_Energy_(batteryToGrid)
attr PV_1 obj-h1054-reading Battery_Total_AC_Charge_Energy_(gridToBattery)
attr PV_1 obj-h1056-reading Total_DC_PV_Energy_(sumOfAllPVInputs)
attr PV_1 obj-h1058-reading Total_DC_Energy_From_PV1
attr PV_1 obj-h106-reading Home_own_consumption_from_battery
attr PV_1 obj-h1060-reading Total_DC_Energy_From_PV2
attr PV_1 obj-h1062-reading Total_DC_Energy_From_PV3
attr PV_1 obj-h1064-reading Total_AC_Energy_(AC-sideToGrid)
attr PV_1 obj-h1066-reading Total_DC_Power_(sumOfAllPVInputs)
attr PV_1 obj-h108-reading Home_own_consumption_from_grid
attr PV_1 obj-h110-reading Total_home_consumption_Battery
attr PV_1 obj-h112-reading Total_home_consumption_Grid
attr PV_1 obj-h114-reading Total_home_consumption_PV
attr PV_1 obj-h116-reading Home_own_consumption_from_PV
attr PV_1 obj-h118-reading Total_home_consumption
attr PV_1 obj-h120-reading Isolation_resistance
attr PV_1 obj-h122-reading Power_limit_from_EVU
attr PV_1 obj-h124-reading Total_home_consumption_rate
attr PV_1 obj-h14-reading Inverter_serial_number
attr PV_1 obj-h14-type STR
attr PV_1 obj-h144-reading Worktime
attr PV_1 obj-h150-reading Actual_cos_phi
attr PV_1 obj-h152-reading Grid_frequency
attr PV_1 obj-h154-reading Current_Phase_1
attr PV_1 obj-h156-reading Active_power_Phase_1
attr PV_1 obj-h158-reading Voltage_Phase_1
attr PV_1 obj-h160-reading Current_Phase_2
attr PV_1 obj-h162-reading Active_power_Phase_2
attr PV_1 obj-h164-reading Voltage_Phase_2
attr PV_1 obj-h166-reading Current_Phase_3
attr PV_1 obj-h168-reading Active_power_Phase_3
attr PV_1 obj-h170-reading Voltage_Phase_3
attr PV_1 obj-h172-reading Total_AC_active_power
attr PV_1 obj-h174-reading Total_AC_reactive_power
attr PV_1 obj-h178-reading Total_AC_apparent_power
attr PV_1 obj-h190-reading Battery_charge_current
attr PV_1 obj-h194-format %.0f
attr PV_1 obj-h194-reading Number_of_battery_cycles
attr PV_1 obj-h200-reading Actual_battery_charge_-minus_or_discharge_-plus_current
attr PV_1 obj-h202-reading PSSB_fuse_state
attr PV_1 obj-h208-reading Battery_ready_flag
attr PV_1 obj-h210-reading Act_state_of_charge
attr PV_1 obj-h212-reading Battery_state
attr PV_1 obj-h214-reading Battery_temperature
attr PV_1 obj-h216-reading Battery_voltage
attr PV_1 obj-h218-reading Cos_phi_(powermeter)
attr PV_1 obj-h220-reading Frequency_(powermeter)
attr PV_1 obj-h222-reading Current_phase_1_(powermeter)
attr PV_1 obj-h224-reading Active_power_phase_1_(powermeter)
attr PV_1 obj-h226-reading Reactive_power_phase_1_(powermeter)
attr PV_1 obj-h228-reading Apparent_power_phase_1_(powermeter)
attr PV_1 obj-h230-reading Voltage_phase_1_(powermeter)
attr PV_1 obj-h232-reading Current_phase_2_(powermeter)
attr PV_1 obj-h234-reading Active_power_phase_2_(powermeter)
attr PV_1 obj-h236-reading Reactive_power_phase_2_(powermeter)
attr PV_1 obj-h238-reading Apparent_power_phase_2_(powermeter)
attr PV_1 obj-h240-reading Voltage_phase_2_(powermeter)
attr PV_1 obj-h242-reading Current_phase_3_(powermeter)
attr PV_1 obj-h244-reading Active_power_phase_3_(powermeter)
attr PV_1 obj-h246-reading Reactive_power_phase_3_(powermeter)
attr PV_1 obj-h248-reading Apparent_power_phase_3_(powermeter)
attr PV_1 obj-h250-reading Voltage_phase_3_(powermeter)
attr PV_1 obj-h252-reading Total_active_power_(powermeter)
attr PV_1 obj-h254-reading Total_reactive_power_(powermeter)
attr PV_1 obj-h256-reading Total_apparent_power_(powermeter)
attr PV_1 obj-h258-reading Current_DC1
attr PV_1 obj-h260-reading Power_DC1
attr PV_1 obj-h266-reading Voltage_DC1
attr PV_1 obj-h268-reading Current_DC2
attr PV_1 obj-h270-reading Power_DC2
attr PV_1 obj-h276-reading Voltage_DC2
attr PV_1 obj-h278-reading Current_DC3
attr PV_1 obj-h280-reading Power_DC3
attr PV_1 obj-h286-reading Voltage_DC3
attr PV_1 obj-h320-reading Total_yield
attr PV_1 obj-h322-reading Daily_yield
attr PV_1 obj-h324-reading Yearly_yield
attr PV_1 obj-h326-reading Monthly_yield
attr PV_1 obj-h38-reading Software-Version_Maincontroller_(MC)
attr PV_1 obj-h38-type STR
attr PV_1 obj-h384-len 16
attr PV_1 obj-h384-reading Inverter_network_name
attr PV_1 obj-h384-type STR
attr PV_1 obj-h420-reading IP-address
attr PV_1 obj-h420-type STR
attr PV_1 obj-h428-reading IP-subnetmask
attr PV_1 obj-h428-type STR
attr PV_1 obj-h436-reading IP-gateway
attr PV_1 obj-h436-type STR
attr PV_1 obj-h446-reading IP-DNS1
attr PV_1 obj-h446-type STR
attr PV_1 obj-h454-reading IP-DNS2
attr PV_1 obj-h454-type STR
attr PV_1 obj-h46-reading Software-Version_IO-Controller_(IOC)
attr PV_1 obj-h46-type STR
attr PV_1 obj-h512-format %s
attr PV_1 obj-h512-reading Battery_gross_capacity
attr PV_1 obj-h512-unpack N
attr PV_1 obj-h514-len 1
attr PV_1 obj-h514-reading Battery_actual_SOC
attr PV_1 obj-h515-format %s
attr PV_1 obj-h515-reading Battery_Maincontroller_(MC)
attr PV_1 obj-h515-unpack N
attr PV_1 obj-h517-reading Battery_Manufacturer
attr PV_1 obj-h517-type STR
attr PV_1 obj-h525-format %s
attr PV_1 obj-h525-reading Battery_Model_ID
attr PV_1 obj-h525-unpack N
attr PV_1 obj-h527-format %s
attr PV_1 obj-h527-reading Battery_Serial_Number
attr PV_1 obj-h527-unpack N
attr PV_1 obj-h529-len 4
attr PV_1 obj-h529-reading Work_Capacity
attr PV_1 obj-h529-unpack N
attr PV_1 obj-h531-format %.0f
attr PV_1 obj-h531-reading Inverter_Max_Power
attr PV_1 obj-h531-unpack N
attr PV_1 obj-h535-revRegs 0
attr PV_1 obj-h535-unpack n
attr PV_1 obj-h551-revRegs 0
attr PV_1 obj-h559-revRegs 0
attr PV_1 obj-h56-format %.0f
attr PV_1 obj-h56-reading Inverter_state
attr PV_1 obj-h56-unpack N
attr PV_1 obj-h575-len 1
attr PV_1 obj-h575-reading Inverter_Generation_Power_(actual)
attr PV_1 obj-h577-len 2
attr PV_1 obj-h577-reading Generation_Energy
attr PV_1 obj-h577-unpack N
attr PV_1 obj-h578-reading Total_energy
attr PV_1 obj-h582-reading Actual_battery_charge-discharge_power
attr PV_1 obj-h586-format %s
attr PV_1 obj-h586-reading Battery_Firmware
attr PV_1 obj-h586-unpack N
attr PV_1 obj-h588-format %s
attr PV_1 obj-h588-len 1
attr PV_1 obj-h588-reading Battery_Type
attr PV_1 obj-h588-unpack N
attr PV_1 obj-h6-reading Inverter_article_number
attr PV_1 obj-h6-type STR
attr PV_1 obj-h768-len 32
attr PV_1 obj-h768-reading Productname
attr PV_1 obj-h768-type STR
attr PV_1 obj-h800-len 32
attr PV_1 obj-h800-reading Power_class
attr PV_1 obj-h800-type STR
attr PV_1 room Strom->Photovoltaik
attr PV_1 sortby 111
attr PV_1 stateFormat {sprintf("\
<TABLE>\
\
<TR>\
  <TH ALIGN=\"MIDDLE\" WIDTH=\"20\">Batterie %s</TH>\
  <TH ALIGN=\"MIDDLE\" WIDTH=\"20\">aktuell</TH>\
  <TH ALIGN=\"RIGHT\" WIDTH=\"20\">Hausverbrauch</TH>\
  <TH ALIGN=\"MIDDLE\" WIDTH=\"20\">Erträge</TH>\
</TR>\
\
<TR>\
  <TD ALIGN=\"MIDDLE\" WIDTH=\"20\">\
    Leistung:  %04d W<br>\
    Temp.: %02.1f °C<br>\
    Ladung total: %2d %%<br>\
    Ladung Res.: %04d Wh\
  </TD>\
\
  <TD ALIGN=\"RIGHT\" WIDTH=\"20\">\
    DC total: %05d W<br>\
    <br>\
    <br>\
    PV reserve: %05d W\
  </TD>\
\
  <TD ALIGN=\"RIGHT\" WIDTH=\"20\">\
    von PV: %05d W <br>\
    von Batterie: %05d W<br>\
    vom Netz: %05d W<br>\
    ins Haus: %05d W<br>\
    Netz: %05d W\
  </TD>\
\
  <TD ALIGN=\"RIGHT\" WIDTH=\"20\">\
    Tag: %05d KWh <br>\
    Monat: %05d KWh<br>\
    Jahr: %05d KWh<br>\
    Total: %05d KWh\
  </TD>\
</TR>\
\
</TABLE>\
" , \
(ReadingsVal($name,"Actual_battery_charge_-minus_or_discharge_-plus_Power",0) lt 0) ? "<span style='color:#00FF00'>Laden</span>":"<span style='color:#FF0000'>Entladen</span>" ,\
\
ReadingsVal($name,"Actual_battery_charge_-minus_or_discharge_-plus_Power",0),\
ReadingsVal($name,"Battery_temperature",0) ,\
ReadingsVal($name,"Act_state_of_charge",0) ,\
ReadingsVal($name,"Actual_battery_charge_usable_Power",0) ,\
\
ReadingsVal($name,"Total_DC_Power_(sumOfAllPVInputs)","0"),\
ReadingsVal($name,"Total_PV_Power_reserve","0"),\
\
ReadingsVal($name,"Home_own_consumption_from_PV",0) ,\
ReadingsVal($name,"Home_own_consumption_from_battery",0) ,\
ReadingsVal($name,"Home_own_consumption_from_grid",0),\
ReadingsVal($name,"Home_own_consumption_from_PV",0) +ReadingsVal($name,"Home_own_consumption_from_battery",0)+ReadingsVal($name,"Home_own_consumption_from_grid",0),\
ReadingsVal($name,"Total_active_power_(powermeter)",0),\
\
round(ReadingsVal($name,"Daily_yield",0)/1000 ,0),\
round(ReadingsVal($name,"Monthly_yield",0)/1000 ,0) ,\
round(ReadingsVal($name,"Yearly_yield",0)/1000 ,0) ,\
round(ReadingsVal($name,"Total_yield",0)/1000 ,0)\
)}
attr PV_1 userReadings Total_PV_Power_reserve:Total_DC_Power.* {my $reserve = ReadingsVal($NAME,"Total_DC_Power_(sumOfAllPVInputs)","0") * 0.90 - ReadingsVal($NAME,"Home_own_consumption_from_PV","0");;;; ($reserve lt 0)?0:round($reserve,3)  },\
\
Total_DC_Power_Max:Total_DC_Power.* { my $Bat_out = (ReadingsVal($NAME,"Actual_battery_charge_-minus_or_discharge_-plus_current","0")*ReadingsVal($NAME,"Battery_voltage","0"));;;; ($Bat_out gt 0)?ReadingsVal($NAME,"Total_DC_Power_(sumOfAllPVInputs)","0") + $Bat_out :ReadingsVal($NAME,"Total_DC_Power_(sumOfAllPVInputs)","0") },\
\
Actual_battery_charge_-minus_or_discharge_-plus_Power:Actual_battery_charge_-minus_or_discharge_-plus_current.* {round((ReadingsVal($NAME,"Actual_battery_charge_-minus_or_discharge_-plus_current","0")*ReadingsVal($NAME,"Battery_voltage","0")),0)},\
\
Actual_battery_charge_usable_Power:Act_state_of_charge.* {my $x = (ReadingsVal($NAME."_config","Battery_Total_Power","0")*(ReadingsVal($NAME,"Act_state_of_charge","0")-10)/100);;;; ($x lt 0)?0:round($x,0) }
attr PV_1 verbose 0
</syntaxhighlight>


====== Userreadings ======
mysql> SELECT Host,User,plugin FROM mysql.user WHERE User='fhemuser';
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. Dies betrifft insbesondere die Statistics_* readings, die durch ein Python Skript später erzeugt werden.
+------+----------+-----------------------+
| Host | User    | plugin                |
+------+----------+-----------------------+
| %    | fhemuser | mysql_native_password |
+------+----------+-----------------------+
1 row in set (0.01 sec)


Total_PV_Power_reserve
-- Bei den GRANDS muss ich nochmal nachschauen, was da wirklich notwendig ist
  Trigger: Total_DC_Power.*
mysql> GRANT SELECT, INSERT, DELETE, UPDATE ON `fhem`.* TO 'fhemuser'@'%';
  Hier wird ein reading erstellt, dass als Information über die verwendbare Reserve dient. Die Leistung, die in den Speicher geht
mysql> GRANT ALTER ROUTINE ON `fhem`.* TO 'fhemuser'@'%';
  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.
mysql> GRANT EXECUTE ON PROCEDURE `fhem`.'dwd_load' TO 'fhemuser'@'%';            <<<< Das beschränkt auf nur eine Prozedure
  Der Total_PV_Power_reserve Wert kann gerne nach eigenen Belangen kalkuliert werden.
 
Total_DC_Power_Max
-- Die Änderungen müssen mit einem FLUSH in der aktuellen Session übernommen werden
  Trigger: Total_DC_Power.*
mysql> FLUSH PRIVILEGES;
  Um die Batterieleistung mit zu berücksichtigen wird dieser Momentan Wert ermittelt.
Actual_battery_charge_-minus_or_discharge_-plus_Power
  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 ???


=====Kostal Plenticore Plus die API=====
-- Alle Einträge für den fhemuser anschauen
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.
mysql> SELECT * FROM mysql.user WHERE User='fhemuser';
  < snip >
  Es wird eine Tabelle mit den user Einträgen für fhemuser angezeigt, die man sich besser in voller Breite im Editor anschaut


======Plenticore API======
mysql> Connect fhem;
<syntaxhighlight lang="Perl">
  Enter password:  
http://<IP-Address_Plenticore>/api/v1
  Connection id:    44902
</syntaxhighlight>
  Current database: fhem
======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
-- Zum Test mal einige Einträge in der history anzeigen, wenn FHEM bereits was über DbLog geschieben hat
<syntaxhighlight lang="Perl">
mysql> SELECT * FROM history LIMIT 1;
attr global verbose 3
  +---------------------+------------------+---------+-------+-----------------+----------+------+
attr <device> verbose 5
  | TIMESTAMP          | DEVICE          | TYPE    | EVENT | READING        | VALUE    | UNIT |
</syntaxhighlight>
  +---------------------+------------------+---------+-------+-----------------+----------+------+
Bitte unbedingt bei Problemen den Logausschnitt mit senden und die Meldungen anderer Devices vorher herauslöschen, da es ansonsten schwierig ist eine Diagnose zu betreiben.  
  | 2019-04-03 00:23:42 | EVU_StromZaehler | HTTPMOD | NULL  | Strom_Status-02 | 07152.96 |      |
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.
* use Encode qw(decode encode);
* use PBKDF2::Tiny qw/derive verify/;
* use Digest::SHA qw(sha256 hmac_sha256);
* use Crypt::URandom qw( urandom );
* use Crypt::AuthEnc::GCM;
Je nach dem welche Installation Ihr verwendet kann es auch sein, dass noch weitere Module installiert werden. Es wurde bereits folgendes gemeldet.
* sudo apt-get install libcryptx-perl
* sudo apt-get install libpbkdf2-tiny-perl
======KeyValue() Erläuterung Passworte======
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()======
<syntaxhighlight lang="Perl">
sub KeyValue {
    my ($step, $index, $value) = @_;
    my $key = getUniqueId().$index;
    my $e_value = "";
    my $error;


    if (eval "use Digest::MD5;1") {
-- Es könnte auch gut sein einen extra root user anzulegen, um mit DbRep die MySQL Datenbank zu optimieren
      $key    = Digest::MD5::md5_hex(unpack "H*", $key);
      $key  .= Digest::MD5::md5_hex($key);
    }
 
    if ($step eq "read") {
      ($error, $value) = getKeyValue($index);


      if ( defined($error) ) {
mysql> CREATE USER 'fhemroot'@'<IP Adresse>' IDENTIFIED WITH mysql_native_password BY '<password>';
        Log3 $index,3, "$index, can't read key from FhemUtils/uniqueID: $error";
        return undef;
      }


      if ( defined($value) ) {
mysql> GRANT ALL PRIVILEGES ON `fhem`.* TO 'fhemroot'@'<IP Adresse>' WITH GRANT OPTION;
        my $dec_value = '';
mysql> GRANT SHOW_ROUTINE ON *.* TO 'fhemroot'@'<IP Adresse>';
mysql> GRANT EXECUTE, CREATE ROUTINE, ALTER ROUTINE ON `fhem`.* TO 'fhemroot'@'<IP Adresse>';


        for my $char (map { pack('C', hex($_)) } ($value =~ /(..)/g)) {
-- Die Änderungen müssen mit einem FLUSH in der aktuellen Session übernommen werden
          my $decode  = chop($key);
mysql> FLUSH PRIVILEGES;
          $dec_value .= chr(ord($char)^ord($decode));
          $key        = $decode.$key;
        }
        return $dec_value;
      }
      else {
        Log3 $index,3,"$index, no key found in FhemUtils/uniqueID";
        return undef;
      }
    }


    if ($step eq "store") {
mysql> SELECT user,host FROM mysql.user;
      for my $char (split //, $value) {
+------------------+----------------+
         my $encode = chop($key);
| user            | host          |
         $e_value  .= sprintf("%.2x",ord($char)^ord($encode));
+------------------+----------------+
        $key      = $encode.$key;
| fhemuser         | %              |
      }
| fhemroot         | 192.168.178.57 |  <<< Das sollte man dann auf eine IP einschränken.
      $error = setKeyValue($index, $e_value);
| root            | localhost      |
      return "error while saving key : $error" if(defined($error));
+------------------+----------------+
      return "Key successfully saved in FhemUtils/uniqueID Key $index";
    }


}
-- Hier kann man mal seine Proceduren auflisten
</syntaxhighlight>
mysql> SHOW PROCEDURE STATUS WHERE Db = 'fhem';


======KeyValue() Test======
Die Funktion befindet sich in der 99_myUtils und kann auch direkt in der Commandline aufgerufen werden.
<syntaxhighlight lang="Perl">
Syntax für die Commandline im FHEM {KeyValue("[read|store]","PW_<device>_<key>","<password>")}
{KeyValue("store","PW_PV_1_API_user","<passwort>")}
{KeyValue("read","PW_PV_1_API_user")}
</syntaxhighlight>
</syntaxhighlight>


======plenticore_auth() Erläuterung======
===Hardware Anbindung (alles über LAN)===
Diese Funktion befindet sich in der 99_myUtils und dient der Erstellung von Keys für die mehrstufige Anmeldung des Kostal Plenticore Plus.
====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_auth()======
======Plenticore Modbus Definition======
<syntaxhighlight lang="Perl">
<pre>
sub plenticore_auth {
GeräteId  : 71
  my ($step, $user, $logdevice, $randomString, $nonce, $salt, $rounds, $transactionId, $token) = @_;
IP-Adresse: <IP-Adresse>
Port      : 1502
</pre>


  my $PASSWD = KeyValue("read","PW_".$logdevice."_".$user);
====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.


  Log3 $logdevice,3,"====Start plenticore_auth==============================";
======Modbus Timeing======
  Log3 $logdevice,3,"auth_step        : ".$step;
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.  
  Log3 $logdevice,3,"auth_user        : ".$user;
  Log3 $logdevice,3,"auth_device      : ".$logdevice;
  Log3 $logdevice,3,"auth_KeyValue read: PW_".$logdevice."_".$user;


  if($step eq "start")
Die Zeit kann auch verändert werden, jedoch sollte sie nicht zu kurz gewählt sein.  
    {
    my @chars = ('0'..'9', 'A'..'Z', 'a'..'z');
    my $len = 12;
    my $string;


    Log3 $logdevice,3,"====End arguments======================================";
======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.


    while($len--){ $string .= $chars[rand @chars] };
Anzupassende Stellen, wenn man lieber keinen Schwarm vorbereiten möchte:
    $string = encode("UTF-8", $string);
<pre>
    $string = decode("UTF-8", $string);
WR_1 und WR_1_API:
    my $u = encode_base64($string);
- userReadings SW_* löschen
    $u =~ s/\n$//g;
- 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
</pre>


    my $message = '{"nonce": "'.$u.'","username": "'.$user.'"}';
======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:
<pre>
- 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
</pre>


    Log3 $logdevice,3,"auth_nonce        : ".$u;
* [https://svn.fhem.de/fhem/trunk/fhem/contrib/ch.eick/Photovoltaik/Wechselrichter/RAW_WR_1.txt Beispiel für WR_1]
    Log3 $logdevice,3,"auth_return      : ".$message;
    Log3 $logdevice,3,"====End output=========================================";


    fhem "setreading ".$logdevice."  auth_randomString64 ".$u ;
======userReadings======
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.


    return $message;
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.
Actual_Battery_charge_-minus_or_discharge_-plus_Power
  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.


  ######### This code is identical for finish and session #################
======RAW Definition WR_2 Slave (bei zwei Wechselrichtern)======
  my $bitSalt = decode_base64($salt);
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.
  my $r = derive( 'SHA-256', $PASSWD, $bitSalt, $rounds );
  my $ck = encode('UTF-8', "Client Key");
  my $s = hmac_sha256($ck, $r);
  my $underscore = sha256($s);
  my $d = "n=".$user.",r=".$randomString.",r=".$nonce.",s=".$salt.",i=".$rounds.",c=biws,r=".$nonce;


  Log3 $logdevice,3,"auth_randomString : ".$randomString;
* [https://svn.fhem.de/fhem/trunk/fhem/contrib/ch.eick/Photovoltaik/Wechselrichter/RAW_WR_2.txt Beispiel für WR_2]
  Log3 $logdevice,3,"auth_nonce        : ".$nonce;
  Log3 $logdevice,3,"auth_salt        : ".$salt;
  Log3 $logdevice,3,"auth_rounds      : ".$rounds;
  Log3 $logdevice,3,"auth_transactionId: ".$transactionId;


  if($step eq "finish")
=====Kostal Plenticore Plus die API=====
    {
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.
    Log3 $logdevice,3,"====End arguments======================================";
Auch hier werden bei mehreren AC-Quellen falsche Werte ausgegeben, die durch SW_* userReadings korrigiert wurden.


    my $sk = encode('UTF-8', "Server Key");
======Plenticore API======
    my $c = hmac_sha256($sk, $r);
Die API Definition kann man sich mit folgendem Aufruf anschauen, sie wird von Kostal jedoch nicht supportet.
    my $pd = encode('UTF-8', $d);
<syntaxhighlight lang="Perl">
    my $p = hmac_sha256($pd, $c);
http://<IP-Address_Plenticore>/api/v1
    my $gd = encode('UTF-8', $d);
</syntaxhighlight>
    my $g = hmac_sha256($gd, $underscore);
    my $f = "";
    my $g1 = "";
    my $s1 = "";
    my $f1 = "";
    my $j = 0;
    for($j=0; $j<length($g); $j++) {
        $g1 = substr($g,$j,1);
        $s1 = substr($s,$j,1);
        $f1 = $s1 ^ $g1 ;
        $f = $f.$f1;
    }
    my $pe = encode_base64($f);
    $pe =~ s/\n$//g;                        # Korrektur: \n am Ende des Strings entfernen, Ursache unbekannt
    my $proof = decode('UTF-8', $pe);


    my $message = '{"transactionId": "'.$transactionId.'", "proof": "'.$proof.'"}';
======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.


    Log3 $logdevice,3,"auth_proof        : ".$proof;
Folgendes bitte aktivieren, damit genügend Meldungen zusehen sind
    Log3 $logdevice,3,"auth_return      : ".$message;
<syntaxhighlight lang="Perl">
    Log3 $logdevice,3,"====End output=========================================";
attr global verbose 3
attr <device> verbose 5
</syntaxhighlight>
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.


    return $message;
======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.
<syntaxhighlight lang="Perl">
* use Encode qw(decode encode);
* use PBKDF2::Tiny qw/derive verify/;
* use Digest::SHA qw(sha256 hmac_sha256);
* use Crypt::URandom qw( urandom );
* use Crypt::AuthEnc::GCM;
</syntaxhighlight>
Je nach dem welche Installation Ihr verwendet kann es auch sein, dass noch weitere Module installiert werden müssen. Es wurde bereits folgendes gemeldet.
<syntaxhighlight lang="Perl">
* sudo apt-get install libcryptx-perl
* sudo apt-get install libpbkdf2-tiny-perl
* sudo apt-get install libcrypt-urandom-perl
</syntaxhighlight>


  if($step eq "session")
======KeyValue() Erläuterung Passworte======
    {
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.
    Log3 $logdevice,3,"auth_token        : ".$token;
Die Funktion befindet sich in der 99_myUtils .
    Log3 $logdevice,3,"====End arguments======================================";


    my $sk = encode('UTF-8', "Session Key");
======KeyValue()======
    my $dd = encode('UTF-8', $d);
Die KeyValue() Funktion kommt in die 99_myUtils.pm .
    my $protocol_key = hmac_sha256($sk, $dd, $s, $underscore);
* [https://svn.fhem.de/fhem/trunk/fhem/contrib/ch.eick/Photovoltaik/99_myUtils.pm_Erg%c3%a4nzungen.txt Beispiel 99_myUtils.pm Ergänzungen]


    my $t = "7244ba6f73c8cdc47b232e1311451939";
======KeyValue() Test======
    $t =~ s/([a-fA-F0-9][a-fA-F0-9])/chr(hex($1))/eg;
Die Funktion befindet sich in der 99_myUtils und kann auch direkt in der Commandline von FHEM aufgerufen werden.
    my $e2 = Crypt::AuthEnc::GCM->new("AES", $protocol_key, $t);
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.'''
    my $tt = encode('UTF-8', $token);
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.
    my $e2ct = $e2->encrypt_add($tt);
<syntaxhighlight lang="Perl">
    my $authtag = $e2->encrypt_done();
Syntax für die Commandline im FHEM {KeyValue("[read|store]","PW_<device>_<key>","<password>")}
{KeyValue("store","PW_WR_1_API_user","<passwort>")}
{KeyValue("read","PW_WR_1_API_user")}
</syntaxhighlight>
'''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.


    $tt = encode_base64($t);
======plenticore_auth() Erläuterung======
    $tt =~ s/\n$//g;                        # Korrektur: \n am Ende des Strings entfernen, Ursache unbekannt
Diese Funktion befindet sich in der 99_myUtils und dient der Erstellung von Keys für die mehrstufige Anmeldung des Kostal Plenticore Plus.
    my $iv = decode('UTF-8', $tt);


    my $aa = encode_base64($authtag);
======plenticore_auth()======
    $aa =~ s/\n$//g;                        # Korrektur: \n am Ende des Strings entfernen, Ursache unbekannt
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.
    $authtag = decode('UTF-8', $aa);
 
    my $pp = encode_base64($e2ct);
    $pp =~ s/\n//g;                        # Korrektur: \n am Ende des Strings entfernen, Ursache unbekannt
    my $payload = decode('UTF-8', $pp);
 
    my $message = '{"transactionId": "'.$transactionId.'", "iv": "'.$iv.'", "tag": "'.$authtag.'", "payload": "'.$payload.'"}';


    Log3 $logdevice,3,"auth_iv          : ".$iv;
Die plenticore_auth() kommt in die 99_myUtils.pm .
    Log3 $logdevice,3,"auth_authtag      : ".$authtag;
* [https://svn.fhem.de/fhem/trunk/fhem/contrib/ch.eick/Photovoltaik/99_myUtils.pm_Erg%c3%a4nzungen.txt Beispiel 99_myUtils.pm Ergänzungen]
    Log3 $logdevice,3,"auth_payload      : ".$payload;
    Log3 $logdevice,3,"auth_return      : ".$message;
    Log3 $logdevice,3,"====End output=========================================";
 
    return $message;
    }
};
</syntaxhighlight>


======plenticore_auth() Test======
======plenticore_auth() Test======
Nachdem vorher bereits mit KeyValue() das Passwort hinterlegt wurde kann man die Funktion bereits testen.
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.
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
Da die auth_* Keys zur Laufzeit im Dialog mit dem Plenticore erstellt und ausgetauscht werden, wird hier für den Test mit Beispielwerten gearbeitet
Zeile 693: Zeile 419:
{plenticore_auth("[start|finish|session]","user","<device>","auth_randomString64","auth_nonce","auth_salt","auth_rounds","auth_transactionId","auth_token")}
{plenticore_auth("[start|finish|session]","user","<device>","auth_randomString64","auth_nonce","auth_salt","auth_rounds","auth_transactionId","auth_token")}
Test 1)
Test 1)
{plenticore_auth("start","user","PV_1_API")}      # Im Hintergrund wird das Passwort aus dem KeyStore verwendet
{plenticore_auth("start","user","WR_1_API")}      # Im Hintergrund wird das Passwort aus dem KeyStore verwendet
>>> {"nonce": "UUZ1dWNEZnowVzh2","username": "user"}
>>> {"nonce": "UUZ1dWNEZnowVzh2","username": "user"}


{plenticore_auth("finish","user","PV_1_API","TESMUWZnwkJZbnpF","TE2MUWZnwkJZbnpFQ5ulCfolNNdAD0vT","DbAC0R85jwF0rh+r","29000","1376720346bea40cdf770a8f84b5975cfeb20c5e6ac6d89b7862df3ca9695e43")}
Test 2)
{plenticore_auth("finish","user","WR_1_API","TESMUWZnwkJZbnpF","TE2MUWZnwkJZbnpFQ5ulCfolNNdAD0vT","DbAC0R85jwF0rh+r","29000","1376720346bea40cdf770a8f84b5975cfeb20c5e6ac6d89b7862df3ca9695e43")}
>>> {"transactionId": "1376720346bea40cdf770a8f84b5975cfeb20c5e6ac6d89b7862df3ca9695e43", "proof": "5xZeOxoyR0hzPCVqvD/BPMqscQbT57wSONl049xiLjE="}
>>> {"transactionId": "1376720346bea40cdf770a8f84b5975cfeb20c5e6ac6d89b7862df3ca9695e43", "proof": "5xZeOxoyR0hzPCVqvD/BPMqscQbT57wSONl049xiLjE="}


 
Test 3)
{plenticore_auth("session","user","PV_1_API","TESMUWZnwkJZbnpF","TE2MUWZnwkJZbnpFQ5ulCfolNNdAD0vT","DbAC0R85jwF0rh+r","29000","1376720346bea40cdf770a8f84b5975cfeb20c5e6ac6d89b7862df3ca9695e43","acafc66c0e1975293d35512a1e4bcceea55840b3109a703514e75b5ebce9b7c5")}
{plenticore_auth("session","user","WR_1_API","TESMUWZnwkJZbnpF","TE2MUWZnwkJZbnpFQ5ulCfolNNdAD0vT","DbAC0R85jwF0rh+r","29000","1376720346bea40cdf770a8f84b5975cfeb20c5e6ac6d89b7862df3ca9695e43","acafc66c0e1975293d35512a1e4bcceea55840b3109a703514e75b5ebce9b7c5")}
>>> {"transactionId": "1376720346bea40cdf770a8f84b5975cfeb20c5e6ac6d89b7862df3ca9695e43", "iv": "ckS6b3PIzcR7Iy4TEUUZOQ==", "tag": "ROTpRrav38sLdt3EEuE3tQ==", "payload": "nWraowAhLQVk5RCq8WOo8ZhGvUyHLMNxA13/21w7DuHDqq2LOQRXM143kJE5WNJQgeuoKeLiRunPaRpiJUzK3g=="}
>>> {"transactionId": "1376720346bea40cdf770a8f84b5975cfeb20c5e6ac6d89b7862df3ca9695e43", "iv": "ckS6b3PIzcR7Iy4TEUUZOQ==", "tag": "ROTpRrav38sLdt3EEuE3tQ==", "payload": "nWraowAhLQVk5RCq8WOo8ZhGvUyHLMNxA13/21w7DuHDqq2LOQRXM143kJE5WNJQgeuoKeLiRunPaRpiJUzK3g=="}
</syntaxhighlight>
</syntaxhighlight>


======Ablaufbeschreibung PV_1_API======
======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 ins das html eingefügt. Das replacement ruft afür noch die Funktion plenticore_auth() aus der 99_myUtils auf.
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======
======Automatischer Login mit Sessionaufbau======
Der Ablauf startet mit einer beliebigen Abfrage und läuft dann vollautomatisch bis zur Ausführung des eigentlichen Aufrufes durch.
Der Ablauf startet mit einer beliebigen Abfrage und läuft dann vollautomatisch bis zur Ausführung des eigentlichen Aufrufes durch.
<pre>
<pre>
   1. get PV_1_API 20_/processdata/scb:statistic:EnergyFlow
   1. get WR_1_API 20_Statistic_EnergyFlow
   2. Sollte noch keine Session aufgebaut sein erfolgt der Aufruf von sid01
   2. Sollte noch keine Session aufgebaut sein erfolgt der Aufruf von sid01
   3. Das Replacement %START% führt plenticore_auth("start","user","PV_1_API") aus
   3. Das Replacement %START% führt plenticore_auth("start","user","WR_1_API") aus
   4. Die Rückmeldung vom Plenticore wird gelesen
   4. Die Rückmeldung vom Plenticore wird gelesen
   5. Das Replacement %FINISH% führt plenticore_auth("start","user","PV_1_API",...) aus
   5. Das Replacement %FINISH% führt plenticore_auth("finish","user","WR_1_API",...) aus
   6. Die Rückmeldung vom Plenticore wird gelesen
   6. Die Rückmeldung vom Plenticore wird gelesen
   7. Das Replacement %SESSION% führt plenticore_auth("start","user","PV_1_API",...,...) aus
   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
   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
   9. Der eigentliche Aufruf wird ausgeführt und die readings bereit gestellt
</pre>
</pre>
Die Anmeldung kann auch teilautomatisch 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.
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.
<pre>
<pre>
   1. get PV_1_API 01_/auth/start
   1. get WR_1_API 01_auth_start
   2. get PV_1_API 02_/auth/finish
   2. get WR_1_API 02_auth_finish
   3. get PV_1_API 03_/auth/session
   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
   4. Die Rückmeldung vom Plenticore wird gelesen und es sollte eine SessionId bestehen, mit der nun alle weiteren Abfragen laufen
</pre>
</pre>


====== Implementierte Abfragen ======
======Implementierte Abfragen======
Sollte die Session abgelaufen, oder noch kein Login vollzogen worden sein, so wird automatisch ein Login mit Sessionaufbau durchgeführt.
Sollte die Session abgelaufen, oder noch kein Login vollzogen worden sein, so wird automatisch ein Login mit Sessionaufbau durchgeführt.


Zeile 733: Zeile 460:
<pre>
<pre>
Login Funktionalität für eine manuelle Anmeldung
Login Funktionalität für eine manuelle Anmeldung
01_/auth/start
01_auth_start
02_/auth/finish
02_auth_finish
03_/auth/create_session
03_auth_create_session


Auskunft über den Anmeldezustand
Auskunft über den Anmeldezustand
04_/auth/me
04_auth_me


Informationen zum Plenticore
Informationen zum Plenticore
05_/info/version
05_info_version


Abfrage der Statistiken
Abfrage der Statistiken
20_/processdata/scb_statistic_EnergyFlow
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.
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
attr <Device> showBody 1
21_/modules_list
51_modules_list


Hier gibt es viele technische Loggings. Sollte es Probleme beim Abholen der Daten geben kann es am timeout liegen
Hier gibt es viele technische Loggings. Sollte es Probleme beim Abholen der Daten geben kann es am timeout liegen
attr <Device> timeout 7
attr <Device> timeout 7
24_/logdata/download
59_logdata_download
 
Diese Abfragewerte können auch gesetzt werden, siehe set Befehle
 
31_Battery_Type    Speicher Typen wurden folgende ermittelt 0=keine , 4=BYD
32_Battery_MinHomeComsumption
33_Battery_Strategy
34_Battery_MinSoc
35_Battery_SmartBatteryControl_Enable
36_Battery_DynamicSoc_Enable


Das zeigt den FW Update Status beim FW Laden an. Die Rückmeldung ist im httpbody zu sehen, es werden keine readings erzeugt.
Das zeigt den Firmware Update Status beim FW Laden an. Die Rückmeldung ist im httpbody zu sehen, es werden keine readings erzeugt.
41_/update/status
60_update_status
</pre>
</pre>


'''set:'''
'''set:'''
Bei den set Aufrufen ist automatisch ein get Aufruf im Anschluss gekoppelt, wodurch der neue Zustand wieder abgefragt wird.
<pre>
<pre>
Eine bestehende Session wird abgemeldet
Eine bestehende Session wird abgemeldet, als Rückmeldung erscheint im httpbody nur ein "null"
06_/auth/logout
06_auth_logout


Die letzten Events können in deutsch oder englisch abgeholt werden
Die letzten Events können in deutsch oder englisch abgeholt werden
23_/events/latest_5 [en-gb,de-de]
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!
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.
Hier sollte nur etwas gesetzt werden, wenn man sich sicher ist, was man tut.
31_Battery_Type [0,4]
22_01_Battery_DynamicSoc_Enable
32_Battery_MinHomeComsumption
22_03_Battery_MinHomeConsumption
33_Battery_Strategy
22_04_Battery_MinSoc
34_Battery_MinSoc
22_05_Battery_SmartBatteryControl_Enable
35_Battery_SmartBatteryControl_Enable
22_06_Battery_Strategy
36_Battery_DynamicSoc_Enable
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
</pre>
</pre>


======RAW Definition des PV_1_API für v1.16======
======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.
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!
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.'''
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:
Die Nummerierung beim get/set soll die Zusammenhänge etwas klarer machen:
<pre>
<pre>
get 21_Battery_InternControl
get 21_Battery_InternControl
set 21_04_Battery_MinSoc
set 21_04_Battery_MinSoc (mit automatischem FollowGet)
get 21_Battery_InternControl
</pre>
</pre>


## Achtung, Voraussetzung ist mindestens die HTTPMOD Version 4.1.00
'''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_Plenticore_10_Plus#Kostal_Smart_Energy_Manager_.28KSEM.29_.28Modbus.2FTCP.29|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.'''
<pre>
SW_Statistic_Yield_Month:Statistic_Yield_Month.* { round(ReadingsVal("$NAME","Statistic_Yield_Month",0)+ReadingsVal("WR_2_API","Statistic_Yield_Month",0),0) + 195000},
SW_Statistic_Yield_Year:Statistic_Yield_Year.* { round(ReadingsVal("$NAME","Statistic_Yield_Year",0)+ReadingsVal("WR_2_API","Statistic_Yield_Year",0),0) + 772000},


<syntaxhighlight lang="Perl">
SW_Statistic_EnergyHomePv_Month:SW_Statistic_EnergyHomeFeedInGrid_Month.* { round(ReadingsVal("$NAME","SW_Statistic_Yield_Month",0) - ReadingsVal("$NAME","SW_Statistic_EnergyHomeFeedInGrid_Month",0) - ReadingsVal("$NAME","Statistic_EnergyHomeBat_Month",0),0) - 42000 },
defmod PV_1_API HTTPMOD http://%IP-PV%/api/v1/auth/me 0
SW_Statistic_EnergyHomePv_Year:SW_Statistic_EnergyHomeFeedInGrid_Year.* { round(ReadingsVal("$NAME","SW_Statistic_Yield_Year",0) - ReadingsVal("$NAME","SW_Statistic_EnergyHomeFeedInGrid_Year",0) - ReadingsVal("$NAME","Statistic_EnergyHomeBat_Year",0),0) - 158000 },


attr PV_1_API DbLogExclude .*
SW_Statistic_TotalConsumption_Month:SW_Statistic_EnergyHomePv_Month.* { round( (ReadingsVal("$NAME","SW_Statistic_EnergyHomePv_Month",0)+ReadingsVal("$NAME","Statistic_EnergyHomeBat_Month",0) +ReadingsVal("$NAME","SW_Statistic_EnergyHomeGrid_Month",0) ),0) + 42000},
attr PV_1_API DbLogInclude Statistic_Autarky.*,Statistic_Energy.*,Statistic_Own.*,Statistic_Total.*,Statistic_Yield.*,Statistic_PV.*,Statistic_Grid.*
SW_Statistic_TotalConsumption_Year:SW_Statistic_EnergyHomePv_Year.* { round( (ReadingsVal("$NAME","SW_Statistic_EnergyHomePv_Year",0)+ReadingsVal("$NAME","Statistic_EnergyHomeBat_Year",0) +ReadingsVal("$NAME","SW_Statistic_EnergyHomeGrid_Year",0) ),0) + 158000},
attr PV_1_API authRetries 1
</pre>
attr PV_1_API comment Version 2021.02.04 18:40\
 
Passworte für die Abfrage des PV_1_API werden im storeKeyValue abgelegt:\
RAW Definition des WR_1_API Master
  {KeyValue("[read|store]","PW_<Device Name>_<Benutzer Name>","<passwort>")}\
* [https://svn.fhem.de/fhem/trunk/fhem/contrib/ch.eick/Photovoltaik/Wechselrichter/RAW_WR_1_API.txt Beispiel WR_1_API]
  {KeyValue("store","PW_PV_1_API_user","<passwort>")}
attr PV_1_API disable 0
attr PV_1_API dontRequeueAfterAuth 0
attr PV_1_API enableControlSet 0
attr PV_1_API enableCookies 1
attr PV_1_API event-on-change-reading Battery_.*
attr PV_1_API event-on-update-reading auth_.*,Statistic_Autarky.*,Statistic_Energy_.*arge.*,Statistic_EnergyFeedIn.*,Statistic_EnergyHome.*, Statistic_EnergyPv[1|2].*,Statistic_.*Consumption.*,Statistic_Yield.*
attr PV_1_API get01Data %START%
attr PV_1_API get01Name 01_auth_start
attr PV_1_API get01URL http://%IP-PV%/api/v1/auth/start
attr PV_1_API get02Data %FINISH%
attr PV_1_API get02Name 02_auth_finish
attr PV_1_API get02URL http://%IP-PV%/api/v1/auth/finish
attr PV_1_API get03Data %SESSION%
attr PV_1_API get03Name 03_auth_create_session
attr PV_1_API get03URL http://%IP-PV%/api/v1/auth/create_session
attr PV_1_API get04-1Name auth_me_active
attr PV_1_API get04-2Name auth_me_locked
attr PV_1_API get04-3Name auth_me_authenticated
attr PV_1_API get04-4Name auth_me_anonymous
attr PV_1_API get04-5Name auth_me_role
attr PV_1_API get04-6Name auth_me_permissions
attr PV_1_API get04Header authorization: Session %auth_sessionId%
attr PV_1_API get04JSON .
attr PV_1_API get04Name 04_auth_me
attr PV_1_API get04URL http://%IP-PV%/api/v1/auth/me
attr PV_1_API get05-1Name info_api_version
attr PV_1_API get05-2Name info_hostname
attr PV_1_API get05-3Name info_name
attr PV_1_API get05-4Name info_sw_version
attr PV_1_API get05JSON .
attr PV_1_API get05Name 05_info_version
attr PV_1_API get05URL http://%IP-PV%/api/v1/info/version
attr PV_1_API get20-10Format %.2f
attr PV_1_API get20-10Name Statistic_EnergyChargeGrid_Month
attr PV_1_API get20-11Format %.2f
attr PV_1_API get20-11Name Statistic_EnergyChargeGrid_Total
attr PV_1_API get20-12Format %.2f
attr PV_1_API get20-12Name Statistic_EnergyChargeGrid_Year
attr PV_1_API get20-13Format %.2f
attr PV_1_API get20-13Name Statistic_EnergyChargeInvIn_Day
attr PV_1_API get20-14Format %.2f
attr PV_1_API get20-14Name Statistic_EnergyChargeInvIn_Month
attr PV_1_API get20-15Format %.2f
attr PV_1_API get20-15Name Statistic_EnergyChargeInvIn_Total
attr PV_1_API get20-16Format %.2f
attr PV_1_API get20-16Name Statistic_EnergyChargeInvIn_Year
attr PV_1_API get20-17Format %.2f
attr PV_1_API get20-17Name Statistic_EnergyChargePv_Day
attr PV_1_API get20-18Format %.2f
attr PV_1_API get20-18Name Statistic_EnergyChargePv_Month
attr PV_1_API get20-19Format %.2f
attr PV_1_API get20-19Name Statistic_EnergyChargePv_Total
attr PV_1_API get20-1Format %.2f
attr PV_1_API get20-1Name Statistic_Autarky_Day
attr PV_1_API get20-20Format %.2f
attr PV_1_API get20-20Name Statistic_EnergyChargePv_Year
attr PV_1_API get20-21Format %.2f
attr PV_1_API get20-21Name Statistic_EnergyDischarge_Day
attr PV_1_API get20-22Format %.2f
attr PV_1_API get20-22Name Statistic_EnergyDischarge_Month
attr PV_1_API get20-23Format %.2f
attr PV_1_API get20-23Name Statistic_EnergyDischarge_Total
attr PV_1_API get20-24Format %.2f
attr PV_1_API get20-24Name Statistic_EnergyDischarge_Year
attr PV_1_API get20-25Format %.2f
attr PV_1_API get20-25Name Statistic_EnergyDischargeGrid_Day
attr PV_1_API get20-26Format %.2f
attr PV_1_API get20-26Name Statistic_EnergyDischargeGrid_Month
attr PV_1_API get20-27Format %.2f
attr PV_1_API get20-27Name Statistic_EnergyDischargeGrid_Total
attr PV_1_API get20-28Format %.2f
attr PV_1_API get20-28Name Statistic_EnergyDischargeGrid_Year
attr PV_1_API get20-29Format %.2f
attr PV_1_API get20-29Name Statistic_EnergyHome_Day
attr PV_1_API get20-2Format %.2f
attr PV_1_API get20-2Name Statistic_Autarky_Month
attr PV_1_API get20-30Format %.2f
attr PV_1_API get20-30Name Statistic_EnergyHome_Month
attr PV_1_API get20-31Format %.2f
attr PV_1_API get20-31Name Statistic_EnergyHome_Total
attr PV_1_API get20-32Format %.2f
attr PV_1_API get20-32Name Statistic_EnergyHome_Year
attr PV_1_API get20-33Format %.2f
attr PV_1_API get20-33Name Statistic_EnergyHomeBat_Day
attr PV_1_API get20-34Format %.2f
attr PV_1_API get20-34Name Statistic_EnergyHomeBat_Month
attr PV_1_API get20-35Format %.2f
attr PV_1_API get20-35Name Statistic_EnergyHomeBat_Total
attr PV_1_API get20-36Format %.2f
attr PV_1_API get20-36Name Statistic_EnergyHomeBat_Year
attr PV_1_API get20-37Format %.2f
attr PV_1_API get20-37Name Statistic_EnergyHomeGrid_Day
attr PV_1_API get20-38Format %.2f
attr PV_1_API get20-38Name Statistic_EnergyHomeGrid_Month
attr PV_1_API get20-39Format %.2f
attr PV_1_API get20-39Name Statistic_EnergyHomeGrid_Total
attr PV_1_API get20-3Format %.2f
attr PV_1_API get20-3Name Statistic_Autarky_Total
attr PV_1_API get20-40Format %.2f
attr PV_1_API get20-40Name Statistic_EnergyHomeGrid_Year
attr PV_1_API get20-41Format %.2f
attr PV_1_API get20-41Name Statistic_EnergyHomeOwn_Total
attr PV_1_API get20-42Format %.2f
attr PV_1_API get20-42Name Statistic_EnergyHomePv_Day
attr PV_1_API get20-43Format %.2f
attr PV_1_API get20-43Name Statistic_EnergyHomePv_Month
attr PV_1_API get20-44Format %.2f
attr PV_1_API get20-44Name Statistic_EnergyHomePv_Total
attr PV_1_API get20-45Format %.2f
attr PV_1_API get20-45Name Statistic_EnergyHomePv_Year
attr PV_1_API get20-46Format %.2f
attr PV_1_API get20-46Name Statistic_EnergyPv1_Day
attr PV_1_API get20-47Format %.2f
attr PV_1_API get20-47Name Statistic_EnergyPv1_Month
attr PV_1_API get20-48Format %.2f
attr PV_1_API get20-48Name Statistic_EnergyPv1_Total
attr PV_1_API get20-49Format %.2f
attr PV_1_API get20-49Name Statistic_EnergyPv1_Year
attr PV_1_API get20-4Format %.2f
attr PV_1_API get20-4Name Statistic_Autarky_Year
attr PV_1_API get20-50Format %.2f
attr PV_1_API get20-50Name Statistic_EnergyPv2_Day
attr PV_1_API get20-51Format %.2f
attr PV_1_API get20-51Name Statistic_EnergyPv2_Month
attr PV_1_API get20-52Format %.2f
attr PV_1_API get20-52Name Statistic_EnergyPv2_Total
attr PV_1_API get20-53Format %.2f
attr PV_1_API get20-53Name Statistic_EnergyPv2_Year
attr PV_1_API get20-54Format %.2f
attr PV_1_API get20-54Name Statistic_EnergyPv3_Day
attr PV_1_API get20-55Format %.2f
attr PV_1_API get20-55Name Statistic_EnergyPv3_Month
attr PV_1_API get20-56Format %.2f
attr PV_1_API get20-56Name Statistic_EnergyPv3_Total
attr PV_1_API get20-57Format %.2f
attr PV_1_API get20-57Name Statistic_EnergyPv3_Year
attr PV_1_API get20-58Format %.2f
attr PV_1_API get20-58Name Statistic_OwnConsumptionRate_Day
attr PV_1_API get20-59Format %.2f
attr PV_1_API get20-59Name Statistic_OwnConsumptionRate_Month
attr PV_1_API get20-5Format %.2f
attr PV_1_API get20-5Name Statistic_CO2Saving_Day
attr PV_1_API get20-60Format %.2f
attr PV_1_API get20-60Name Statistic_OwnConsumptionRate_Total
attr PV_1_API get20-61Format %.2f
attr PV_1_API get20-61Name Statistic_OwnConsumptionRate_Year
attr PV_1_API get20-62Format %.2f
attr PV_1_API get20-62Name Statistic_Yield_Day
attr PV_1_API get20-63Format %.2f
attr PV_1_API get20-63Name Statistic_Yield_Month
attr PV_1_API get20-64Format %.2f
attr PV_1_API get20-64Name Statistic_Yield_Total
attr PV_1_API get20-65Format %.2f
attr PV_1_API get20-65Name Statistic_Yield_Year
attr PV_1_API get20-6Format %.2f
attr PV_1_API get20-6Name Statistic_CO2Saving_Month
attr PV_1_API get20-7Format %.2f
attr PV_1_API get20-7Name Statistic_CO2Saving_Total
attr PV_1_API get20-8Format %.2f
attr PV_1_API get20-8Name Statistic_CO2Saving_Year
attr PV_1_API get20-9Format %.2f
attr PV_1_API get20-9Name Statistic_EnergyChargeGrid_Day
attr PV_1_API get20Header authorization: Session %auth_sessionId%
attr PV_1_API get20JSON 01_processdata_.._value
attr PV_1_API get20Name 20_Statistic_EnergyFlow
attr PV_1_API get20URL http://%IP-PV%/api/v1/processdata/scb:statistic:EnergyFlow
attr PV_1_API get21-1Name Battery_Info_Cycles
attr PV_1_API get21-2Name Battery_Info_FullChargeCap_E
attr PV_1_API get21-3Name Battery_Info_SoC
attr PV_1_API get21-4Format %d
attr PV_1_API get21-4Name Battery_Info_WorkCapacity
attr PV_1_API get21Header01 authorization: Session %auth_sessionId%
attr PV_1_API get21Header02 Content-type:application/json, Accept:application/json, Connection:keep-alive
attr PV_1_API get21JSON .._processdata_.._value
attr PV_1_API get21Name 21_Battery_Information
attr PV_1_API get21URL http://%IP-PV%/api/v1/processdata/devices:local:battery/Cycles,FullChargeCap_E,SoC,WorkCapacity
attr PV_1_API get22-1Name Battery_InternControl_DynamicSoc_Enable
attr PV_1_API get22-2Name Battery_Control
attr PV_1_API get22-3Format %d
attr PV_1_API get22-3Name Battery_InternControl_MinHomeConsumption
attr PV_1_API get22-4Name Battery_InternControl_MinSoc
attr PV_1_API get22-5Name Battery_InternControl_SmartBatteryControl_Enable
attr PV_1_API get22-6Name Battery_InternControl_Strategy
attr PV_1_API get22-7Name Battery_InternControl_Type
attr PV_1_API get22Header01 authorization: Session %auth_sessionId%
attr PV_1_API get22Header02 Content-type:application/json, Accept:application/json, Connection:keep-alive
attr PV_1_API get22JSON .._value
attr PV_1_API get22Name 22_Battery_InternControl
attr PV_1_API get22URL http://%IP-PV%/api/v1/settings/devices:local/Battery:ExternControl,Battery:Type,Battery:MinHomeComsumption,Battery:Strategy,Battery:MinSoc,Battery:SmartBatteryControl:Enable,Battery:DynamicSoc:Enable,Battery:Type
attr PV_1_API get23-10Format %d
attr PV_1_API get23-10Name Battery_ExternControl_MaxSocRel
attr PV_1_API get23-11Format %d
attr PV_1_API get23-11Name Battery_ExternControl_MinSocRel
attr PV_1_API get23-1Name Battery_Control
attr PV_1_API get23-22Name Battery_ExternControl_MinSocRel
attr PV_1_API get23-2Format %.2f
attr PV_1_API get23-2Name Battery_ExternControl_AcPowerAbs
attr PV_1_API get23-3Format %.2f
attr PV_1_API get23-3Name Battery_ExternControl_AcPowerRel
attr PV_1_API get23-4Format %.2f
attr PV_1_API get23-4Name Battery_ExternControl_DcCurrentAbs
attr PV_1_API get23-5Format %.2f
attr PV_1_API get23-5Name Battery_ExternControl_DcCurrentRel
attr PV_1_API get23-6Format %.2f
attr PV_1_API get23-6Name Battery_ExternControl_DcPowerAbs
attr PV_1_API get23-7Format %.2f
attr PV_1_API get23-7Name Battery_ExternControl_DcPowerRel
attr PV_1_API get23-8Format %d
attr PV_1_API get23-8Name Battery_ExternControl_MaxChargePowerAbs
attr PV_1_API get23-9Format %d
attr PV_1_API get23-9Name Battery_ExternControl_MaxDischargePowerAbs
attr PV_1_API get23Header01 authorization: Session %auth_sessionId%
attr PV_1_API get23Header02 Content-type: application/json, Accept: application/json, Connection: keep-alive
attr PV_1_API get23JSON .._value
attr PV_1_API get23Name 23_Battery_ExternControl
attr PV_1_API get23URL http://%IP-PV%/api/v1/settings/devices:local/Battery:ExternControl,Battery:ExternControl:AcPowerAbs,Battery:ExternControl:AcPowerRel,Battery:ExternControl:DcCurrentAbs,Battery:ExternControl:DcCurrentRel,Battery:ExternControl:DcPowerAbs,Battery:ExternControl:DcPowerRel,Battery:ExternControl:MaxChargePowerAbs,Battery:ExternControl:MaxDischargePowerAbs,Battery:ExternControl:MaxSocRel,Battery:ExternControl:MinSocRel
attr PV_1_API get24-1Name Battery_TimeControl_1
attr PV_1_API get24-2Name Battery_TimeControl_2
attr PV_1_API get24-3Name Battery_TimeControl_3
attr PV_1_API get24-4Name Battery_TimeControl_4
attr PV_1_API get24-5Name Battery_TimeControl_5
attr PV_1_API get24-6Name Battery_TimeControl_6
attr PV_1_API get24-7Name Battery_TimeControl_7
attr PV_1_API get24-8Name Battery_TimeControl
attr PV_1_API get24Header01 authorization: Session %auth_sessionId%
attr PV_1_API get24Header02 Content-type: application/json, Accept: application/json, Connection: keep-alive
attr PV_1_API get24JSON .._value
attr PV_1_API get24Name 24_Battery_TimeControl
attr PV_1_API get24URL http://%IP-PV%/api/v1/settings/devices:local/Battery:TimeControl:Enable,Battery:TimeControl:ConfMon,Battery:TimeControl:ConfTue,Battery:TimeControl:ConfWed,Battery:TimeControl:ConfThu,Battery:TimeControl:ConfFri,Battery:TimeControl:ConfSat,Battery:TimeControl:ConfSun
attr PV_1_API get25Header01 authorization: Session %auth_sessionId%
attr PV_1_API get25Header02 Content-type:application/json, Accept:application/json, Connection:keep-alive
attr PV_1_API get25Name 25_Battery_EM_State
attr PV_1_API get25URL http://%IP-PV%/api/v1/processdata/devices:local/EM_State
attr PV_1_API get51Name 51_modules_list
attr PV_1_API get51URL http://%IP-PV%/api/v1/modules
attr PV_1_API get59Data {"end":"%end_date%","begin":"%begin_date%"}
attr PV_1_API get59Header01 authorization: Session %auth_sessionId%
attr PV_1_API get59Header02 Content-type: application/json, Accept: application/json, Connection: keep-alive
attr PV_1_API get59Name 59_logdata_download
attr PV_1_API get59URL http://%IP-PV%/api/v1/logdata/download
attr PV_1_API get60Header authorization: Session %auth_sessionId%
attr PV_1_API get60Name 60_update_status
attr PV_1_API get60URL http://%IP-PV%/api/v1/update/status
attr PV_1_API getHeader01 Accept-Encoding: gzip,deflate
attr PV_1_API getHeader02 Content-type:application/json, Accept:application/json, Connection:keep-alive
attr PV_1_API group PV Eigenverbrauch
attr PV_1_API icon sani_solar
attr PV_1_API reAuthRegex "authenticated":false|"processdata":\[\]|wrong credentials|Not authorized
attr PV_1_API reading0101JSON nonce
attr PV_1_API reading0101Name auth_nonce
attr PV_1_API reading0102JSON rounds
attr PV_1_API reading0102Name auth_rounds
attr PV_1_API reading0103JSON salt
attr PV_1_API reading0103Name auth_salt
attr PV_1_API reading0104JSON transactionId
attr PV_1_API reading0104Name auth_transactionId
attr PV_1_API reading0201JSON signature
attr PV_1_API reading0201Name auth_signature
attr PV_1_API reading0202JSON token
attr PV_1_API reading0202Name auth_token
attr PV_1_API reading0301JSON message
attr PV_1_API reading0301Name info_message
attr PV_1_API reading0302JSON error
attr PV_1_API reading0302Name info_error
attr PV_1_API reading03JSON sessionId
attr PV_1_API reading03Name auth_sessionId
attr PV_1_API reading25Name Battery_EM_State
attr PV_1_API reading25OMap 0:Normal,8:Ruhe1,16:Ruhe2,32:Ausgleichsladung,64:Tiefentladeschutz
attr PV_1_API reading25Regex EM_State.*value":(\d+)
attr PV_1_API replacement01Mode expression
attr PV_1_API replacement01Regex %IP-PV%
attr PV_1_API replacement01Value {ReadingsVal("PV_1_config","IP-PV_1","")}
attr PV_1_API replacement02Mode expression
attr PV_1_API replacement02Regex %START%
attr PV_1_API replacement02Value {my $NAME="PV_1_API";; plenticore_auth("start","user","$NAME")}
attr PV_1_API replacement04Mode expression
attr PV_1_API replacement04Regex %FINISH%
attr PV_1_API replacement04Value {my $NAME="PV_1_API";; plenticore_auth("finish","user","$NAME",ReadingsVal("$NAME","auth_randomString64","missed"),ReadingsVal("$NAME","auth_nonce","missed"),ReadingsVal("$NAME","auth_salt","missed"),ReadingsVal("$NAME","auth_rounds","missed"),ReadingsVal("$NAME","auth_transactionId","missed"))}
attr PV_1_API replacement05Mode expression
attr PV_1_API replacement05Regex %SESSION%
attr PV_1_API replacement05Value {my $NAME="PV_1_API";; plenticore_auth("session","user","$NAME",ReadingsVal("$NAME","auth_randomString64","missed"),ReadingsVal("$NAME","auth_nonce","missed"),ReadingsVal("$NAME","auth_salt","missed"),ReadingsVal("$NAME","auth_rounds","missed"),ReadingsVal("$NAME","auth_transactionId","missed"),ReadingsVal("$NAME","auth_token","missed"))}
attr PV_1_API replacement06Mode reading
attr PV_1_API replacement06Regex %auth_signature%
attr PV_1_API replacement06Value auth_signature
attr PV_1_API replacement07Mode reading
attr PV_1_API replacement07Regex %auth_sessionId%
attr PV_1_API replacement07Value auth_sessionId
attr PV_1_API replacement08Mode expression
attr PV_1_API replacement08Regex %begin_date%
attr PV_1_API replacement08Value {POSIX::strftime("%Y-%m-%d",localtime(time))}
attr PV_1_API replacement09Mode expression
attr PV_1_API replacement09Regex %end_date%
attr PV_1_API replacement09Value {POSIX::strftime("%Y-%m-%d",localtime(time))}
attr PV_1_API room Strom->Energie,Strom->Photovoltaik
attr PV_1_API set06Header01 authorization: Session %auth_sessionId%
attr PV_1_API set06Header02 Content-type:application/json, Accept:application/json, Connection:keep-alive
attr PV_1_API set06Method POST
attr PV_1_API set06Name 06_auth_logout
attr PV_1_API set06NoArg 1
attr PV_1_API set06URL http://%IP-PV%/api/v1/auth/logout
attr PV_1_API set2201Data [{"moduleid":"devices:local","settings":[{"id":"Battery:DynamicSoc:Enable","value":"$val"}]}]
attr PV_1_API set2201FollowGet 22_Battery_InternControl
attr PV_1_API set2201Header01 authorization: Session %auth_sessionId%
attr PV_1_API set2201Header02 Content-type:application/json, Accept:application/json, Connection:keep-alive
attr PV_1_API set2201Hint 0,1
attr PV_1_API set2201Method PUT
attr PV_1_API set2201Name 22_01_Battery_DynamicSoc_Enable
attr PV_1_API set2201URL http://%IP-PV%/api/v1/settings
attr PV_1_API set2203Data [{"moduleid":"devices:local","settings":[{"id":"Battery:MinHomeComsumption","value":"$val"}]}]
attr PV_1_API set2203FollowGet 22_Battery_InternControl
attr PV_1_API set2203Header01 authorization: Session %auth_sessionId%
attr PV_1_API set2203Header02 Content-type:application/json, Accept:application/json, Connection:keep-alive
attr PV_1_API set2203Hint slider,50,50,8000
attr PV_1_API set2203Method PUT
attr PV_1_API set2203Name 22_03_Battery_MinHomeConsumption
attr PV_1_API set2203URL http://%IP-PV%/api/v1/settings
attr PV_1_API set2204Data [{"moduleid":"devices:local","settings":[{"id":"Battery:MinSoc","value":"$val"}]}]
attr PV_1_API set2204FollowGet 22_Battery_InternControl
attr PV_1_API set2204Header01 authorization: Session %auth_sessionId%
attr PV_1_API set2204Header02 Content-type:application/json, Accept:application/json, Connection:keep-alive
attr PV_1_API set2204Hint slider,10,5,100
attr PV_1_API set2204Method PUT
attr PV_1_API set2204Name 22_04_Battery_MinSoc
attr PV_1_API set2204URL http://%IP-PV%/api/v1/settings
attr PV_1_API set2205Data [{"moduleid":"devices:local","settings":[{"id":"Battery:SmartBatteryControl:Enable","value":"$val"}]}]
attr PV_1_API set2205FollowGet 22_Battery_InternControl
attr PV_1_API set2205Header01 authorization: Session %auth_sessionId%
attr PV_1_API set2205Header02 Content-type:application/json, Accept:application/json, Connection:keep-alive
attr PV_1_API set2205Hint 0,1
attr PV_1_API set2205Method PUT
attr PV_1_API set2205Name 22_05_Battery_SmartBatteryControl_Enable
attr PV_1_API set2205URL http://%IP-PV%/api/v1/settings
attr PV_1_API set2206Data [{"moduleid":"devices:local","settings":[{"id":"Battery:Strategy","value":"$val"}]}]
attr PV_1_API set2206FollowGet 22_Battery_InternControl
attr PV_1_API set2206Header01 authorization: Session %auth_sessionId%
attr PV_1_API set2206Header02 Content-type:application/json, Accept:application/json, Connection:keep-alive
attr PV_1_API set2206Hint 1,2
attr PV_1_API set2206Method PUT
attr PV_1_API set2206Name 22_06_Battery_Strategy
attr PV_1_API set2206URL http://%IP-PV%/api/v1/settings
attr PV_1_API set2207Data [{"moduleid":"devices:local","settings":[{"id":"Battery:Type","value":"$val"}]}]
attr PV_1_API set2207FollowGet 22_Battery_InternControl
attr PV_1_API set2207Header01 authorization: Session %auth_sessionId%
attr PV_1_API set2207Header02 Content-type:application/json, Accept:application/json, Connection:keep-alive
attr PV_1_API set2207Hint 0,4
attr PV_1_API set2207Method PUT
attr PV_1_API set2207Name 22_07_Battery_Type
attr PV_1_API set2207URL http://%IP-PV%/api/v1/settings
attr PV_1_API set2300Data [{"moduleid":"devices:local","settings":[{"id":"Battery:ExternControl","value":"$val"}]}]
attr PV_1_API set2300FollowGet 23_Battery_ExternControl
attr PV_1_API set2300Header01 authorization: Session %auth_sessionId%
attr PV_1_API set2300Header02 Content-type:application/json, Accept:application/json, Connection:keep-alive
attr PV_1_API set2300Hint 0,1,2
attr PV_1_API set2300Method PUT
attr PV_1_API set2300Name 23_00_Battery_ExternControl
attr PV_1_API set2300URL http://%IP-PV%/api/v1/settings
attr PV_1_API set2301Data [{"moduleid":"devices:local","settings":[{"id":"Battery:ExternControl:AcPowerAbs","value":"$val"}]}]
attr PV_1_API set2301FollowGet 23_Battery_ExternControl
attr PV_1_API set2301Header01 authorization: Session %auth_sessionId%
attr PV_1_API set2301Header02 Content-type:application/json, Accept:application/json, Connection:keep-alive
attr PV_1_API set2301Method PUT
attr PV_1_API set2301Name 23_01_Battery_ExternControl_AcPowerAbs
attr PV_1_API set2301URL http://%IP-PV%/api/v1/settings
attr PV_1_API set2302Data [{"moduleid":"devices:local","settings":[{"id":"Battery:ExternControl:AcPowerRel","value":"$val"}]}]
attr PV_1_API set2302FollowGet 23_Battery_ExternControl
attr PV_1_API set2302Header01 authorization: Session %auth_sessionId%
attr PV_1_API set2302Header02 Content-type:application/json, Accept:application/json, Connection:keep-alive
attr PV_1_API set2302Method PUT
attr PV_1_API set2302Name 23_02_Battery_ExternControl_AcPowerRel
attr PV_1_API set2302URL http://%IP-PV%/api/v1/settings
attr PV_1_API set2303Data [{"moduleid":"devices:local","settings":[{"id":"Battery:ExternControl:DcCurrentAbs","value":"$val"}]}]
attr PV_1_API set2303FollowGet 23_Battery_ExternControl
attr PV_1_API set2303Header01 authorization: Session %auth_sessionId%
attr PV_1_API set2303Header02 Content-type:application/json, Accept:application/json, Connection:keep-alive
attr PV_1_API set2303Method PUT
attr PV_1_API set2303Name 23_03_Battery_ExternControl_DcCurrentAbs
attr PV_1_API set2303URL http://%IP-PV%/api/v1/settings
attr PV_1_API set2304Data [{"moduleid":"devices:local","settings":[{"id":"Battery:ExternControl:DcCurrentRel","value":"$val"}]}]
attr PV_1_API set2304FollowGet 23_Battery_ExternControl
attr PV_1_API set2304Header01 authorization: Session %auth_sessionId%
attr PV_1_API set2304Header02 Content-type:application/json, Accept:application/json, Connection:keep-alive
attr PV_1_API set2304Method PUT
attr PV_1_API set2304Name 23_04_Battery_ExternControl_DcCurrentRel
attr PV_1_API set2304URL http://%IP-PV%/api/v1/settings
attr PV_1_API set2305Data [{"moduleid":"devices:local","settings":[{"id":"Battery:ExternControl:DcPowerAbs","value":"$val"}]}]
attr PV_1_API set2305FollowGet 23_Battery_ExternControl
attr PV_1_API set2305Header01 authorization: Session %auth_sessionId%
attr PV_1_API set2305Header02 Content-type:application/json, Accept:application/json, Connection:keep-alive
attr PV_1_API set2305Method PUT
attr PV_1_API set2305Name 23_05_Battery_ExternControl_DcPowerAbs
attr PV_1_API set2305URL http://%IP-PV%/api/v1/settings
attr PV_1_API set2306Data [{"moduleid":"devices:local","settings":[{"id":"Battery:ExternControl:DcPowerRel","value":"$val"}]}]
attr PV_1_API set2306FollowGet 23_Battery_ExternControl
attr PV_1_API set2306Header01 authorization: Session %auth_sessionId%
attr PV_1_API set2306Header02 Content-type:application/json, Accept:application/json, Connection:keep-alive
attr PV_1_API set2306Method PUT
attr PV_1_API set2306Name 23_06_Battery_ExternControl_DcPowerRel
attr PV_1_API set2306URL http://%IP-PV%/api/v1/settings
attr PV_1_API set2307Data [{"moduleid":"devices:local","settings":[{"id":"Battery:ExternControl:MaxChargePowerAbs","value":"$val"}]}]
attr PV_1_API set2307FollowGet 23_Battery_ExternControl
attr PV_1_API set2307Header01 authorization: Session %auth_sessionId%
attr PV_1_API set2307Header02 Content-type:application/json, Accept:application/json, Connection:keep-alive
attr PV_1_API set2307Method PUT
attr PV_1_API set2307Name 23_07_Battery_ExternControl_MaxChargePowerAbs
attr PV_1_API set2307URL http://%IP-PV%/api/v1/settings
attr PV_1_API set2308Data [{"moduleid":"devices:local","settings":[{"id":"Battery:ExternControl:MaxDischargePowerAbs","value":"$val"}]}]
attr PV_1_API set2308FollowGet 23_Battery_ExternControl
attr PV_1_API set2308Header01 authorization: Session %auth_sessionId%
attr PV_1_API set2308Header02 Content-type:application/json, Accept:application/json, Connection:keep-alive
attr PV_1_API set2308Method PUT
attr PV_1_API set2308Name 23_08_Battery_ExternControl_MaxDischargePowerAbs
attr PV_1_API set2308URL http://%IP-PV%/api/v1/settings
attr PV_1_API set2309Data [{"moduleid":"devices:local","settings":[{"id":"Battery:ExternControl:MaxSocRel","value":"$val"}]}]
attr PV_1_API set2309FollowGet 23_Battery_ExternControl
attr PV_1_API set2309Header01 authorization: Session %auth_sessionId%
attr PV_1_API set2309Header02 Content-type:application/json, Accept:application/json, Connection:keep-alive
attr PV_1_API set2309Hint slider,30,5,100
attr PV_1_API set2309Method PUT
attr PV_1_API set2309Name 23_09_Battery_ExternControl_MaxSocRel
attr PV_1_API set2309URL http://%IP-PV%/api/v1/settings
attr PV_1_API set2310Data [{"moduleid":"devices:local","settings":[{"id":"Battery:ExternControl:MinSocRel","value":"$val"}]}]
attr PV_1_API set2310FollowGet 23_Battery_ExternControl
attr PV_1_API set2310Header01 authorization: Session %auth_sessionId%
attr PV_1_API set2310Header02 Content-type:application/json, Accept:application/json, Connection:keep-alive
attr PV_1_API set2310Hint slider,5,5,30
attr PV_1_API set2310Method PUT
attr PV_1_API set2310Name 23_10_Battery_ExternControl_MinSocRel
attr PV_1_API set2310URL http://%IP-PV%/api/v1/settings
attr PV_1_API set50-10Name Event_02_code
attr PV_1_API set50-11Name Event_02_description
attr PV_1_API set50-12Name Event_02_end_time
attr PV_1_API set50-13Name Event_02_group
attr PV_1_API set50-14Name Event_02_is_active
attr PV_1_API set50-15Name Event_02_long_description
attr PV_1_API set50-16Name Event_02_start_time
attr PV_1_API set50-17Name Event_03_category
attr PV_1_API set50-18Name Event_03_code
attr PV_1_API set50-19Name Event_03_description
attr PV_1_API set50-1Name Event_01_category
attr PV_1_API set50-20Name Event_03_end_time
attr PV_1_API set50-21Name Event_03_group
attr PV_1_API set50-22Name Event_03_is_active
attr PV_1_API set50-23Name Event_03_long_description
attr PV_1_API set50-24Name Event_03_start_time
attr PV_1_API set50-25Name Event_04_category
attr PV_1_API set50-26Name Event_04_code
attr PV_1_API set50-27Name Event_04_description
attr PV_1_API set50-28Name Event_04_end_time
attr PV_1_API set50-29Name Event_04_group
attr PV_1_API set50-2Name Event_01_code
attr PV_1_API set50-30Name Event_04_is_active
attr PV_1_API set50-31Name Event_04_long_description
attr PV_1_API set50-32Name Event_04_start_time
attr PV_1_API set50-33Name Event_05_category
attr PV_1_API set50-34Name Event_05_code
attr PV_1_API set50-35Name Event_05_description
attr PV_1_API set50-36Name Event_05_end_time
attr PV_1_API set50-37Name Event_05_group
attr PV_1_API set50-38Name Event_05_is_active
attr PV_1_API set50-39Name Event_05_long_description
attr PV_1_API set50-3Name Event_01_description
attr PV_1_API set50-40Name Event_05_start_time
attr PV_1_API set50-4Name Event_01_end_time
attr PV_1_API set50-5Name Event_01_group
attr PV_1_API set50-6Name Event_01_is_active
attr PV_1_API set50-7Name Event_01_long_description
attr PV_1_API set50-8Name Event_01_start_time
attr PV_1_API set50-9Name Event_02_category
attr PV_1_API set50Data {"max":5,"language":"$val"}
attr PV_1_API set50Header01 authorization: Session %auth_sessionId%
attr PV_1_API set50Header02 Content-type:application/json, Accept:application/json, Connection:keep-alive
attr PV_1_API set50Hint en-gb,de-de
attr PV_1_API set50JSON .
attr PV_1_API set50Name 50_events_latest_5
attr PV_1_API set50ParseResponse 1
attr PV_1_API set50TextArg 1
attr PV_1_API set50URL http://%IP-PV%/api/v1/events/latest
attr PV_1_API showBody 1
attr PV_1_API showError 1
attr PV_1_API sid01Data %START%
attr PV_1_API sid01ParseResponse 1
attr PV_1_API sid01URL http://%IP-PV%/api/v1/auth/start
attr PV_1_API sid02Data %FINISH%
attr PV_1_API sid02ParseResponse 1
attr PV_1_API sid02URL http://%IP-PV%/api/v1/auth/finish
attr PV_1_API sid03Data %SESSION%
attr PV_1_API sid03ParseResponse 1
attr PV_1_API sid03URL http://%IP-PV%/api/v1/auth/create_session
attr PV_1_API sidHeader01 Accept-Encoding: gzip,deflate
attr PV_1_API sidHeader02 Content-type: application/json, Accept: application/json, Connection: keep-alive
attr PV_1_API sortby 112
attr PV_1_API stateFormat {\
my $calcVal=0;;\
\
my $pvt  = sprintf("%04d W",ReadingsVal("PV_1","Total_AC_active_power",0) );;\
my $pvtd  = sprintf("%05.2f kWh",ReadingsVal("$name","Statistic_Yield_Day",0)/1000 );;\
my $pvtm  = sprintf("%06.2f kWh",ReadingsVal("$name","Statistic_Yield_Month",0)/1000 );;\
my $pvty  = sprintf("%08.2f kWh",ReadingsVal("$name","Statistic_Yield_Year",0)/1000 );;\
\
my $pv  = sprintf("%04d W",ReadingsVal("PV_1","Home_own_consumption_from_battery",0)+ReadingsVal("PV_1","Home_own_consumption_from_PV",0) );;\
my $pvd  = sprintf("%05.2f kWh",ReadingsVal("$name","Statistic_EnergyHomePvSum_Day",0)/1000 );;\
my $pvm  = sprintf("%06.2f kWh",ReadingsVal("$name","Statistic_EnergyHomePvSum_Month",0)/1000 );;\
my $pvy  = sprintf("%08.2f kWh",ReadingsVal("$name","Statistic_EnergyHomePvSum_Year",0)/1000 );;\
\
my $gfi  =  sprintf("%04d W",(ReadingsVal("PV_1","Total_active_power_(powermeter)",0)<=0 ? abs(round(ReadingsVal("PV_1","Total_active_power_(powermeter)",0),0)): 0) );;\
my $gfid = sprintf("%05.2f kWh",ReadingsVal("$name","Statistic_EnergyFeedInGrid_Day",0)/1000 );;\
my $gfim = sprintf("%06.2f kWh",ReadingsVal("$name","Statistic_EnergyFeedInGrid_Month",0)/1000 );;\
my $gfiy = sprintf("%08.2f kWh",ReadingsVal("$name","Statistic_EnergyFeedInGrid_Year",0)/1000 );;\
\
my $eb  = sprintf("%04d W",(ReadingsVal("PV_1","Total_active_power_(powermeter)",0)>=0 ? round(ReadingsVal("PV_1","Total_active_power_(powermeter)",0),0) : 0) );;\
my $ebd  = sprintf("%05.2f kWh",ReadingsVal("$name","Statistic_EnergyHomeGrid_Day",0)/1000 );;\
my $ebm  = sprintf("%06.2f kWh",ReadingsVal("$name","Statistic_EnergyHomeGrid_Month",0)/1000 );;\
my $eby  = sprintf("%08.2f kWh",ReadingsVal("$name","Statistic_EnergyHomeGrid_Year",0)/1000 );;\
\
my $pvb  = sprintf("%04d W",ReadingsVal("PV_1","Home_own_consumption_from_battery",0));;\
my $pvbd  = sprintf("%05.2f kWh",ReadingsVal("$name","Statistic_EnergyHomeBat_Day",0)/1000 );;\
my $pvbm  = sprintf("%06.2f kWh",ReadingsVal("$name","Statistic_EnergyHomeBat_Month",0)/1000 );;\
my $pvby  = sprintf("%08.2f kWh",ReadingsVal("$name","Statistic_EnergyHomeBat_Year",0)/1000 );;\
\
my $et  = sprintf("%04d W",(ReadingsVal("PV_1","Home_own_consumption_from_PV",0)+ReadingsVal("PV_1","Home_own_consumption_from_battery",0)+ReadingsVal("PV_1","Home_own_consumption_from_grid",0)) );;\
my $etd  = sprintf("%05.2f kWh",ReadingsVal("$name","Statistic_TotalConsumption_Day",0)/1000 );;\
my $etm  = sprintf("%06.2f kWh",ReadingsVal("$name","Statistic_TotalConsumption_Month",0)/1000 );;\
my $ety  = sprintf("%08.2f kWh",ReadingsVal("$name","Statistic_TotalConsumption_Year",0)/1000 );;\
\
my $valA = ReadingsVal("PV_1", "Total_AC_active_power",0)-ReadingsVal("PV_1", "Home_own_consumption_from_grid",0);;\
    $calcVal = ($valA > 0) ? round($valA /($valA + ReadingsVal("PV_1", "Home_own_consumption_from_grid",""))*100 ,0) : 0;;\
my $aq = sprintf("%3.0f %%",(($calcVal > 100) ? 100 : $calcVal) );;\
\
my $aqd  = sprintf("%3.0f %%",ReadingsVal("$name","Statistic_Autarky_Day",0) );;\
my $aqm  = sprintf("%3.0f %%",ReadingsVal("$name","Statistic_Autarky_Month",0) );;\
my $aqy  = sprintf("%3.0f %%",ReadingsVal("$name","Statistic_Autarky_Year",0) );;\
\
my $valS = ReadingsVal("PV_1","Total_AC_active_power",0);;\
    $calcVal = ($valS > 0) ? round((ReadingsVal("PV_1","Home_own_consumption_from_PV",0) + ReadingsVal("PV_1","Home_own_consumption_from_battery",0)) / $valS * 100 ,0) : 0;;\
my $sq  =  sprintf("%3.0f %%",(($calcVal > 100) ? 100 : $calcVal) );;\
\
my $sqd  = sprintf("%3.0f %%",ReadingsVal("$name","Statistic_OwnConsumptionRate_Day",0) );;\
my $sqm  = sprintf("%3.0f %%",ReadingsVal("$name","Statistic_OwnConsumptionRate_Month",0) );;\
my $sqy  = sprintf("%3.0f %%",ReadingsVal("$name","Statistic_OwnConsumptionRate_Year",0) );;\
\
my $md  = ReadingsTimestamp("$name", "auth_me_authenticated",0);;\
my $cd  = ReadingsTimestamp("$name", "Statistic_Autarky_Day",0);;\
my $cm  = ReadingsTimestamp("$name", "Statistic_Autarky_Month",0);;\
my $cy  = ReadingsTimestamp("$name", "Statistic_Autarky_Year",0);;\
\
"<html><table border=2 bordercolor='darkgreen' cellspacing=0>\
<tr><td style='padding-right:5px;;padding-left:5px;;font-weight:bold'> </td><td style='padding-right:5px;;padding-left:5px;;font-weight:bold'>aktueller Wert</td><td style='padding-right:5px;;padding-left:5px;;font-weight:bold'>Heute</td><td style='padding-right:5px;;padding-left:5px;;font-weight:bold'>dieser Monat</td><td style='padding-right:5px;;padding-left:5px;;font-weight:bold'>dieses Jahr</td></tr>\
<tr><td style='padding-right:5px;;padding-left:5px;;text-align:left;;font-weight:bold'>Erzeugung PV-Total</td><td style='padding-right:5px;;padding-left:5px'>".$pvt."</td><td style='padding-right:5px;;padding-left:5px'>".$pvtd."</td><td style='padding-right:5px;;padding-left:5px'>".$pvtm."</td><td style='padding-right:5px;;padding-left:5px'>".$pvty."</td></tr>\
<tr><td style='padding-right:5px;;padding-left:5px;;text-align:left;;font-weight:bold'>Bezug von PV</td><td style='padding-right:5px;;padding-left:5px'>".$pv."</td><td style='padding-right:5px;;padding-left:5px'>".$pvd."</td><td style='padding-right:5px;;padding-left:5px'>".$pvm."</td><td style='padding-right:5px;;padding-left:5px'>".$pvy."</td></tr>\
<tr><td style='padding-right:5px;;padding-left:5px;;text-align:left;;font-weight:bold'>Bezug von Batterie</td><td style='padding-right:5px;;padding-left:5px'>".$pvb."</td><td style='padding-right:5px;;padding-left:5px'>".$pvbd."</td><td style='padding-right:5px;;padding-left:5px'>".$pvbm."</td><td style='padding-right:5px;;padding-left:5px'>".$pvby."</td></tr>\
<tr><td style='padding-right:5px;;padding-left:5px;;text-align:left;;font-weight:bold'>Bezug ins Haus (Energieverbrauch)</td><td style='padding-right:5px;;padding-left:5px'>".$et."</td><td style='padding-right:5px;;padding-left:5px'>".$etd."</td><td style='padding-right:5px;;padding-left:5px'>".$etm."</td><td style='padding-right:5px;;padding-left:5px'>".$ety."</td></tr>\
<tr><td style='padding-right:5px;;padding-left:5px;;text-align:left;;font-weight:bold'>Bezug vom Netz</td><td style='padding-right:5px;;padding-left:5px'>".$eb."</td><td style='padding-right:5px;;padding-left:5px'>".$ebd."</td><td style='padding-right:5px;;padding-left:5px'>".$ebm."</td><td style='padding-right:5px;;padding-left:5px'>".$eby."</td></tr>\
<tr><td style='padding-right:5px;;padding-left:5px;;text-align:left;;font-weight:bold'>Einspeisung ins Netz</td><td style='padding-right:5px;;padding-left:5px'>".$gfi."</td><td style='padding-right:5px;;padding-left:5px'>".$gfid."</td><td style='padding-right:5px;;padding-left:5px'>".$gfim."</td><td style='padding-right:5px;;padding-left:5px'>".$gfiy."</td></tr>\
<tr><td style='padding-right:5px;;padding-left:5px;;text-align:left;;font-weight:bold'>Autarkiequote</td><td style='padding-right:5px;;padding-left:5px'>".$aq."</td><td style='padding-right:5px;;padding-left:5px'>".$aqd."</td><td style='padding-right:5px;;padding-left:5px'>".$aqm."</td><td style='padding-right:5px;;padding-left:5px'>".$aqy."</td></tr>\
<tr><td style='padding-right:5px;;padding-left:5px;;text-align:left;;font-weight:bold'>Eigenverbrauchsquote</td><td style='padding-right:5px;;padding-left:5px'>".$sq."</td><td style='padding-right:5px;;padding-left:5px'>".$sqd."</td><td style='padding-right:5px;;padding-left:5px'>".$sqm."</td><td style='padding-right:5px;;padding-left:5px'>".$sqy."</td></tr>\
<tr><td style='padding-right:5px;;padding-left:5px;;text-align:left;;font-weight:bold'>Berechnung am</td><td style='padding-right:5px;;padding-left:5px'>".$md."</td><td style='padding-right:5px;;padding-left:5px'>".$cd."</td><td style='padding-right:5px;;padding-left:5px'>".$cm."</td><td style='padding-right:5px;;padding-left:5px'>".$cy."</td></tr>\
</table></html>"\
}
attr PV_1_API timeout 7
attr PV_1_API userReadings Statistic_EnergyHomePvSum_Day:Statistic_EnergyHomePv_Day.* {round( (ReadingsVal("$NAME","Statistic_EnergyHomeBat_Day", "0")+ReadingsVal("$NAME","Statistic_EnergyHomePv_Day", "0")) ,2)},\
\
Statistic_EnergyHomePvSum_Month:Statistic_EnergyHomePv_Month.* {round( (ReadingsVal("$NAME","Statistic_EnergyHomeBat_Month", "0")+ReadingsVal("$NAME","Statistic_EnergyHomePv_Month", "0")) ,2)},\
\
Statistic_EnergyHomePvSum_Year:Statistic_EnergyHomePv_Year.* {round( (ReadingsVal("$NAME","Statistic_EnergyHomeBat_Year", "0")+ReadingsVal("$NAME","Statistic_EnergyHomePv_Year", "0")) ,2)},\
\
\
Statistic_EnergyFeedInGrid_Day:Statistic_Yield_Day.* {round((ReadingsVal("$NAME","Statistic_Yield_Day", "")-ReadingsVal("$NAME","Statistic_EnergyHomeBat_Day", "0")-ReadingsVal("$NAME","Statistic_EnergyHomePv_Day", "0")),2)},\
\
Statistic_EnergyFeedInGrid_Month:Statistic_Yield_Month.* {round((ReadingsVal("$NAME","Statistic_Yield_Month", "")-ReadingsVal("$NAME","Statistic_EnergyHomeBat_Month", "0")-ReadingsVal("$NAME","Statistic_EnergyHomePv_Month", "0")),2)},\
\
Statistic_EnergyFeedInGrid_Year:Statistic_Yield_Year.* {round((ReadingsVal("$NAME","Statistic_Yield_Year", "")-ReadingsVal("$NAME","Statistic_EnergyHomeBat_Year", "0")-ReadingsVal("$NAME","Statistic_EnergyHomePv_Year", "0")),2)},\
\
\
Statistic_TotalConsumption_Day:Statistic_EnergyHomePv_Day.* {round( (ReadingsVal("$NAME","Statistic_EnergyHomePv_Day","0")+ReadingsVal("$NAME","Statistic_EnergyHomeBat_Day","0")+ReadingsVal("$NAME","Statistic_EnergyHomeGrid_Day","0") ) ,2)},\
\
Statistic_TotalConsumption_Month:Statistic_EnergyHomePv_Month.* {round( (ReadingsVal("$NAME","Statistic_EnergyHomePv_Month","0")+ReadingsVal("$NAME","Statistic_EnergyHomeBat_Month","0")+ReadingsVal("$NAME","Statistic_EnergyHomeGrid_Month","0") ) ,2)},\
\
Statistic_TotalConsumption_Year:Statistic_EnergyHomePv_Year.* {round( (ReadingsVal("$NAME","Statistic_EnergyHomePv_Year","0")+ReadingsVal("$NAME","Statistic_EnergyHomeBat_Year","0")+ReadingsVal("$NAME","Statistic_EnergyHomeGrid_Year","0") ),2)},\
\
\
Statistic_Yield_NoBat_Day:Statistic_Yield_Day.* {round((ReadingsVal("$NAME","Statistic_Yield_Day", "0")-ReadingsVal("$NAME","Statistic_EnergyHomeBat_Day", "0")),2)},\
\
Statistic_Yield_NoBat_Month:Statistic_Yield_Month.* {round((ReadingsVal("$NAME","Statistic_Yield_Month", "0")-ReadingsVal("$NAME","Statistic_EnergyHomeBat_Month", "0")),2)},\
\
Statistic_Yield_NoBat_Year:Statistic_Yield_Year.* {round((ReadingsVal("$NAME","Statistic_Yield_Year", "0")-ReadingsVal("$NAME","Statistic_EnergyHomeBat_Year", "0")),2)}
attr PV_1_API verbose 0


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.
<syntaxhighlight lang="Perl">
setstate WR_1_API SW_Meter_init_FeedInGrid_Day  xxxx  << Tageswert  um 00:01
setstate WR_1_API SW_Meter_init_FeedInGrid_Month xxxx  << Monatswert um 00:01 am 01. des Monats
setstate WR_1_API SW_Meter_init_FeedInGrid_Year  xxxx  << Jahreswert um 00:01 am 01.01 des Jahres
setstate WR_1_API SW_Meter_init_Grid_Day  xxxx  << Tageswert  um 00:01
setstate WR_1_API SW_Meter_init_Grid_Month xxxx  << Monatswert um 00:01 am 01. des Monats
setstate WR_1_API SW_Meter_init_Grid_Year  xxxx  << Jahreswert um 00:01 am 01.01 des Jahres
</syntaxhighlight>
</syntaxhighlight>


===Batteriesteuerung über den Plenticore===
Erweiterung im WR_ctl, um die Zählerstände zu speichern
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.
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".
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.
 
======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.
* [https://svn.fhem.de/fhem/trunk/fhem/contrib/ch.eick/Photovoltaik/Wechselrichter/RAW_WR_2_API.txt Beispiel WR_2_API]
 
===Batteriesteuerung über den Plenticore===
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.
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.
Zeile 1.414: Zeile 630:
======smart_laden und laden_beendet======
======smart_laden und laden_beendet======
<pre>
<pre>
Die Steuerung von MinSOC und MinHomeConsumtion ist über das device PV_1_API auch 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.
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.
Die Batterie wurde bisher am Tag immer kurz geladen, dann wieder durch z.B. eine Wärmepumpe geleert und das immer im Wechsel.
Zeile 1.425: Zeile 641:
'''Umsetzung:'''
'''Umsetzung:'''
<pre>
<pre>
Bei MinSOC 15% wird MinHomeConsumtion auf den Wert Battery_Info_SoC gesetzt und die Batterie kann schön den ganzen Tag laden, oder im Forum vorgeschlagen bis SOC 90% .
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.
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.
</pre>
</pre>
Zeile 1.450: Zeile 666:
SpeicherMinSOC_Winter 20
SpeicherMinSOC_Winter 20


Dieser 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.
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
SpeicherMinSOC_fc1_Limit 13000
Zeile 1.458: Zeile 674:
'''Achtung, in der Schweiz ist es verboten den Speicher aus dem Netz zu laden'''
'''Achtung, in der Schweiz ist es verboten den Speicher aus dem Netz zu laden'''


======Aktivierung über PV_1_Speicher_1_ExternTrigger======
Für die Verwendung der DOIF Zeitsteuerung ist hier [https://fhem.de/commandref_DE.html#DOIF_Zeitsteuerung DOIF_Zeitsteuerung] eine sehr gute Beschreibung zu finden.
Um die Zeitsteuerung zu verwenden werden im PV_1_Speicher_1_ExternTrigger Device folgende readings gesetzt.
 
======Aktivierung über WR_1_Speicher_1_ExternControl======
Um die Zeitsteuerung zu verwenden werden im WR_1_Speicher_1_ExternControl Device folgende readings gesetzt.
<pre>
<pre>
SpeicherEntladung:Automatik,Zeit,SpeicherTrigger  
SpeicherEntladung:Automatik,Zeit,SpeicherTrigger  
Automatik - Der Speicher wird vom Wechselrichter gesteuert, oder über die eigene ExternControl der API
  Automatik - Der Speicher wird vom Wechselrichter gesteuert, oder über die eigene ExternControl der API
Zeit - Das Laden und Entladen wird mit den Zeitwerten beeinflusst
  Zeit - Das Laden und Entladen wird mit den Zeitwerten beeinflusst
SpeicherTrigger - beeinflusst das Laden und Entladen direkt ohne die Zeitsteuerung
  SpeicherTrigger - beeinflusst das Laden und Entladen direkt ohne die Zeitsteuerung


ZeitStart/ZeitEnde
SpeicherZeitEnde/SpeicherZeitEnde
Die Zeitangaben können manuell fest gesetzt werden, oder über zusätzliche Timer täglich neu überschrieben werden.
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.
Eine gültige Zeit und entsprechendes Timeing obliegt dem Anwender.
Zeile 1.473: Zeile 691:
</pre>
</pre>
<pre>
<pre>
setreading PV_1_Speicher_1_ExternTrigger SpeicherEntladung Zeit
setreading WR_1_Speicher_1_ExternControl SpeicherEntladung Zeit
setreading PV_1_Speicher_1_ExternTrigger SpeicherZeitStart 00:00
setreading WR_1_Speicher_1_ExternControl SpeicherZeitStart 00:00
setreading PV_1_Speicher_1_ExternTrigger SpeicherZeitEnde 00:00
setreading WR_1_Speicher_1_ExternControl SpeicherZeitEnde 00:00
</pre>
</pre>
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.
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.
Zeile 1.483: Zeile 701:
'''Nutzung von "SpeicherEntladung Zeit":'''
'''Nutzung von "SpeicherEntladung Zeit":'''
<syntaxhighlight lang="Perl">
<syntaxhighlight lang="Perl">
defmod PV_1_EVU_Tarif_1 WeekdayTimer PV_1_Speicher_1_ExternTrigger de 12345|00:01|Arbeitstage {fhem("setreading $NAME SpeicherZeitStart 07:00");; fhem("setreading $NAME SpeicherZeitEnde 16:00");; fhem("setreading $NAME SpeicherEntladung Zeit")}
defmod PV_1_EVU_Tarif_1 WeekdayTimer WR_1_Speicher_1_ExternControl de 12345|00:01|Arbeitstage {fhem("setreading $NAME SpeicherZeitStart 07:00");; fhem("setreading $NAME SpeicherZeitEnde 16:00");; fhem("setreading $NAME SpeicherEntladung Zeit")}
attr PV_1_EVU_Tarif_1 DbLogExclude .*
attr PV_1_EVU_Tarif_1 DbLogExclude .*
attr PV_1_EVU_Tarif_1 alias PV_1_EVU_Tarif_1
attr PV_1_EVU_Tarif_1 alias PV_1_EVU_Tarif_1
Zeile 1.497: Zeile 715:
'''Nutzung von "SpeicherEntladung Zeit":'''
'''Nutzung von "SpeicherEntladung Zeit":'''
<syntaxhighlight lang="Perl">
<syntaxhighlight lang="Perl">
defmod PV_1_EVU_Tarif_2 WeekdayTimer PV_1_Speicher_1_ExternTrigger de  60|00:01|Wochenende {fhem("setreading $NAME SpeicherZeitStart 00:00");; fhem("setreading $NAME SpeicherZeitEnde 23:59");; fhem("setreading $NAME SpeicherEntladung Zeit")}
defmod PV_1_EVU_Tarif_2 WeekdayTimer WR_1_Speicher_1_ExternControl de  60|00:01|Wochenende {fhem("setreading $NAME SpeicherZeitStart 00:00");; fhem("setreading $NAME SpeicherZeitEnde 23:59");; fhem("setreading $NAME SpeicherEntladung Zeit")}
attr PV_1_EVU_Tarif_2 DbLogExclude .*
attr PV_1_EVU_Tarif_2 DbLogExclude .*
attr PV_1_EVU_Tarif_2 alias PV_1_EVU_Tarif_2
attr PV_1_EVU_Tarif_2 alias PV_1_EVU_Tarif_2
Zeile 1.509: Zeile 727:
</syntaxhighlight>
</syntaxhighlight>


======PV_1_Speicher_1_Zeiten WeekdayTimer (Beispiel)======
======WR_1_Speicher_1_Zeiten WeekdayTimer (Beispiel)======
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.
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":'''
'''Nutzung von "SpeicherEntladung Trigger":'''
<syntaxhighlight lang="Perl">
<syntaxhighlight lang="Perl">
defmod PV_1_Speicher_1_Zeiten WeekdayTimer SpeicherZeiten de 12345|07:00|entladen 12345|16:00|gesperrt 60|00:00|entladen 60|23:59|gesperrt {fhem("setreading $NAME SpeicherTrigger $EVENT")}
defmod WR_1_Speicher_1_Zeiten WeekdayTimer SpeicherZeiten de 12345|07:00|entladen 12345|16:00|gesperrt 60|00:00|entladen 60|23:59|gesperrt {fhem("setreading $NAME SpeicherTrigger $EVENT")}
attr PV_1_Speicher_1_Zeiten DbLogExclude .*
attr WR_1_Speicher_1_Zeiten DbLogExclude .*
attr PV_1_Speicher_1_Zeiten alias PV_1_Speicher_1_Zeiten
attr WR_1_Speicher_1_Zeiten alias WR_1_Speicher_1_Zeiten
attr PV_1_Speicher_1_Zeiten commandTemplate set $NAME  $EVENT
attr WR_1_Speicher_1_Zeiten commandTemplate set $NAME  $EVENT
attr PV_1_Speicher_1_Zeiten disable 0
attr WR_1_Speicher_1_Zeiten disable 0
attr PV_1_Speicher_1_Zeiten group PV Steuerung EVU
attr WR_1_Speicher_1_Zeiten group PV Steuerung EVU
attr PV_1_Speicher_1_Zeiten icon clock
attr WR_1_Speicher_1_Zeiten icon clock
attr PV_1_Speicher_1_Zeiten room Strom->Photovoltaik
attr WR_1_Speicher_1_Zeiten room Strom->Photovoltaik
attr PV_1_Speicher_1_Zeiten sortby 111
attr WR_1_Speicher_1_Zeiten sortby 111
attr PV_1_Speicher_1_Zeiten stateFormat {sprintf("geplant: %s %s", ReadingsVal($name,"nextUpdate","?"), ReadingsVal($name,"nextValue","?"))}
attr WR_1_Speicher_1_Zeiten stateFormat {sprintf("geplant: %s %s", ReadingsVal($name,"nextUpdate","?"), ReadingsVal($name,"nextValue","?"))}
</syntaxhighlight>
</syntaxhighlight>
======Tarifsteuerung mit einem DOIF (Beispiel)======
======Tarifsteuerung mit einem DOIF (Beispiel)======
<syntaxhighlight lang="Perl">
<syntaxhighlight lang="Perl">
################################################################################################################
################################################################################################################
## 1 Setzen des niedrig Tarifs. Das PV_1_Speicher_1_ExternControl DOIF reagiert dann auf das Zeitfenster und
## 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
##  aktiviert von 07:00 - 19:00 Uhr den Speicher zum Entladen
  ([07:00|8])
  ([07:00|8])


   (setreading PV_1_Speicher_1_ExternTrigger SpeicherZeitStart 07:00)
   (setreading WR_1_Speicher_1_ExternControl  SpeicherZeitStart 07:00)
   (setreading PV_1_Speicher_1_ExternTrigger SpeicherZeitStart 19:00)
   (setreading WR_1_Speicher_1_ExternControl  SpeicherZeitEnde 19:00)
   (setreading PV_1_Speicher_1_ExternTrigger SpeicherEntladung Zeit)
   (setreading WR_1_Speicher_1_ExternControl  SpeicherEntladung Zeit)


################################################################################################################
################################################################################################################
## 2 Setzen des hohen Tarifs. Das PV_1_Speicher_1_ExternControl DOIF reagiert dann auf das Zeitfenster und
## 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 Entladen
##  aktiviert von 00:00 - 23:59 Uhr den Speicher zum Entladen
DOELSEIF
DOELSEIF
  ([00:00]|9)
  ([00:00|7])


   (setreading PV_1_Speicher_1_ExternTrigger SpeicherZeitStart 00:00)
   (setreading WR_1_Speicher_1_ExternControl  SpeicherZeitStart 00:00)
   (setreading PV_1_Speicher_1_ExternTrigger SpeicherZeitStart 23:59)
   (setreading WR_1_Speicher_1_ExternControl  SpeicherZeitEnde 23:59)
   (setreading PV_1_Speicher_1_ExternTrigger SpeicherEntladung Zeit)
   (setreading WR_1_Speicher_1_ExternControl  SpeicherEntladung Zeit)


################################################################################################################
################################################################################################################
Zeile 1.550: Zeile 769:
  (*** )
  (*** )


   (setreading PV_1_Speicher_1_ExternTrigger SpeicherZeitStart [neu berechnete SpeicherZeitStart])
   (setreading WR_1_Speicher_1_ExternControl  SpeicherZeitStart [neu berechnete SpeicherZeitStart])
   (setreading PV_1_Speicher_1_ExternTrigger SpeicherEntladung Zeit)    ## Triggert den ersten Event, damit die Zeit sofort aktiv wird.
   (setreading WR_1_Speicher_1_ExternControl  SpeicherEntladung Zeit)    ## Triggert den ersten Event, damit die Zeit sofort aktiv wird.
</syntaxhighlight>
</syntaxhighlight>
======Speicherentladung mit Zeit und Trigger (Beispiel)======
======Speicherentladung mit Zeit und Trigger (Beispiel)======
Dies ist ein spezielles Beispiel, bei dem die Nutzung von "SpeicherEntladung Zeit" mit "SpeicherEntladung SpeicherTrigger" gemeinsam verwendet wird.
Dies ist ein spezielles Beispiel, bei dem die Nutzung von "SpeicherEntladung Zeit" mit "SpeicherEntladung SpeicherTrigger" gemeinsam verwendet wird.
Zeile 1.558: Zeile 778:
<pre>
<pre>
1.) Durch einen Timer wird folgendes täglich um 00:01 Uhr gesetzt
1.) Durch einen Timer wird folgendes täglich um 00:01 Uhr gesetzt
setreading PV_1_Speicher_1_ExternTrigger SpeicherZeitStart 07:00"
setreading WR_1_Speicher_1_ExternControl  SpeicherZeitStart 07:00"
setreading PV_1_Speicher_1_ExternTrigger SpeicherZeitEnde 16:00"
setreading WR_1_Speicher_1_ExternControl  SpeicherZeitEnde 16:00"
setreading PV_1_Speicher_1_ExternTrigger SpeicherEntladung Zeit
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.
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
2.) Mit einem Trigger wird der Speicher in der definierten entlade Zeit für kurze Zeit gesperrt
setreading PV_1_Speicher_1_ExternTrigger SpeicherTrigger gesperrt
setreading WR_1_Speicher_1_ExternControl  SpeicherTrigger gesperrt
setreading PV_1_Speicher_1_ExternTrigger SpeicherEntladung SpeicherTrigger
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.
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
3.) Rückfall zur vorher eingetragenen Zeit Steuerung
setreading PV_1_Speicher_1_ExternTrigger SpeicherEntladung Zeit
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.
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.
'''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.
</pre>
</pre>
=====Externe Speichersteuerung (ExternControl)=====
=====Externe Speichersteuerung (ExternControl)=====
Für die erweiterte Steuerung muss die externe Speichersteuerung des Plenticore aktiviert werden.
'''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
<pre>
- 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.​
</pre>


======MaxSOC Kontrolle======
======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.
Es wird versucht, den Speicher am Abend nicht zu 100% zu laden, aber morgens noch mit 3* MinSOC aus der Nacht zu kommen.
<pre>
<pre>
Aktivierung: SpeicherMaxSOCControlActive  1
Aktivierung: SpeicherMaxSOCControlActive  An
Aktivität  : SpeicherMaxSOCControlRunning 0 = momentan keine Steuerung
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
             SpeicherMaxSOC_DayBefore    100 <<< wird berechnet und dient als Merker
            SpeicherMaxSOC_Actual      100 <<< wird berechnet, kann jedoch für diesen Tag anschließend überschrieben werden


Konfiguration:
Konfiguration:
Zeile 1.593: Zeile 836:


======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       1
Aktivierung: SpeicherMiddayControlActive       An
Aktivität  : SpeicherMiddayControlRunning       0 = momentan keine Steuerung
Aktivität  : SpeicherMiddayControlRunning     Aus    <<<< momentan keine Steuerung
             PV_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  
             PV_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
             PV_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_MaxChargePowerAbs 1500 <<<< hängt vom Speicher ab
SpeicherMidday_Inverter_Max_Power        8500  <<<< hier kann man manuell einen Wert festlegen, wenn es keine 70% Regelung gibt
SpeicherMidday_MaxSOC             40   <<<< Es soll nur bis dahin geladen werden, damit Mittags genug Platz ist
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
</pre>
</pre>


====RAW Definition des PV_1_Speicher_1_ExternTrigger====
====RAW Definition des WR_1_Speicher_1_ExternControl====
In diesem Dummy Device werden Parameter für die Externe Speichersteuerung abgelegt. Darüber hinaus wird auch in einigen readings ein Zustandstatus, wie z.B. eine Verriegelung, festgehalten.
Achtung, es wurde eine Sperre beim Betrieb einer WallBox eingebaut, die natürlich jeder für sich anpassen, oder gegebenenfalls entfernen muss.
<pre>
- 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 ---
</pre>


<syntaxhighlight lang="Perl">
Der Ladezustand wird hierbei von der WallBox abgefragt und unterscheidet sich bei verschiedenen Herstellern:
defmod PV_1_Speicher_1_ExternTrigger dummy
Hier bitte gerne noch weitere WallBoxen bei mir melden.
attr PV_1_Speicher_1_ExternTrigger DbLogExclude .*
<pre>
attr PV_1_Speicher_1_ExternTrigger alias PV_1_Speicher_1_ExternTrigger
openWB:
attr PV_1_Speicher_1_ExternTrigger comment Version 2021.03.03 12:30\
    [WB_1:lp_1_ChargeStat] eq "loading"
\
Hier können externe Trigger für die Ladung und Entladung Der Batterie gesetzt werden.\
Die Zeiten können z.B. durch den WeekDayTimer entsprechend an einen Stromtarif angepasst werden.\
Das reading SpeicherEntladung Automatik/Zeit/SpeicherTrigger ermöglicht es die Zeitsteuerung zu überschreiben.\
\
ExternTrigger\
Das reading dient dem Freigeben und Sperren der externen Trigger, z.B. um im Herbst/Winter das smart_laden zu steuern.\
Es verriegelt somit die Zeitsteuerung oder den SpeicherTrigger.\
\
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\
\
SpeicherTrigger:entladen,gesperrt\
Dieser Trigger kann durch andere Logik gesetzt werden.\
Auch hier wäre eine Zeitsteuerung denkbar, die entladen/gesperrt entsprechend umschaltet.\
\
SpeicherZeitStart/SpeicherZeitEnde\
Die Zeitangaben können manuell fest gesetzt oder über zusätzliche Timer täglich neu überschrieben werden.\
Eine gültige Zeit und entsprechendes Timing obliegt dem Anwender.\
Zwischen Start und Ende wird der Speicher zum Entladen freigegeben und zwischen Ende und Start gesperrt.\
\
Speicher*ControlActive\
Das jeweilige reading aktiviert diese Teilkomponente für die Steuerung.\
Ein jeweiliges Speicher*ControlRunning signalisiert, ob gerade die Bedingungen erfüllt sind.\
\
SpeicherCmdRepeatActive\
Es muss im WR die externe Speichersteuerung aktiviert sein.\
Möchte man trotzdem die Sendung der ExternControl Kommandos stoppen, obwohl die Bedingungen erfüllt sind,\
kann man dieses reading zum Deaktivieren auf 0 setzen.\
\
SpeicherMiddayControl\
Über die Solar_forecast() Funktion wird ein Middayhigh ermittelt, wenn der WR nur 70% einspeisen darf.\
\
SpeicherMaxSOCControl\
Es wird versucht den Speicher am Abend nicht zu 100% zu laden, aber morgens noch mit 3* MinSOC aus der Nacht zu kommen.\
\
SpeicherMinSOC\
Dies gehört zur Basissteuerung und schaltet den MinSOC von Sommer auf Winterbetrieb,\
um eine Notladung aus dem Netz zu vermeiden.
attr PV_1_Speicher_1_ExternTrigger event-on-update-reading .*
attr PV_1_Speicher_1_ExternTrigger group PV Eigenverbrauch
attr PV_1_Speicher_1_ExternTrigger icon rc_USB
attr PV_1_Speicher_1_ExternTrigger readingList ExternTrigger SpeicherCmdRepeatActive SpeicherZeitStart SpeicherZeitEnde SpeicherEntladung SpeicherTrigger SpeicherMiddayControlActive SpeicherMidday_Inverter_Max_Power SpeicherMidday_MaxChargePowerAbs SpeicherMidday_MaxSOC SpeicherMinSOC_Sommer SpeicherMinSOC_Winter SpeicherMinSOC_fc1_Limit SpeicherMaxSOCControlActive SpeicherMaxSOC_Actual SpeicherMaxSOC_DayBefore SpeicherMaxSOC_fc1_Limit
attr PV_1_Speicher_1_ExternTrigger room Strom->Photovoltaik
attr PV_1_Speicher_1_ExternTrigger setList ExternTrigger:frei,gesperrt SpeicherCmdRepeatActive:0,1 SpeicherZeitStart:time SpeicherZeitEnde:time SpeicherEntladung:Automatik,Zeit,Trigger SpeicherTrigger:entladen,gesperrt,none SpeicherMiddayControlActive:0,1 SpeicherMidday_Inverter_Max_Power:slider,3000,500,10000 SpeicherMidday_MaxChargePowerAbs:slider,100,100,4700 SpeicherMidday_MaxSOC:slider,20,5,50 SpeicherMinSOC_Sommer:slider,5,1,20 SpeicherMinSOC_Winter:slider,5,1,20 SpeicherMinSOC_fc1_Limit:slider,8000,500,15000 SpeicherMaxSOCControlActive:0,1 SpeicherMaxSOC_Actual:slider,60,5,100 SpeicherMaxSOC_DayBefore:slider,15,5,100 SpeicherMaxSOC_fc1_Limit:slider,10000,2000,50000
attr PV_1_Speicher_1_ExternTrigger sortby 123
attr PV_1_Speicher_1_ExternTrigger stateFormat state
attr PV_1_Speicher_1_ExternTrigger verbose 0


setstate PV_1_Speicher_1_ExternTrigger 2021-03-02 17:09:08 ExternTrigger none
go-eCharger:
setstate PV_1_Speicher_1_ExternTrigger 2021-03-03 11:35:19 SpeicherCmdRepeatActive 1
    [WB_1:car_state] eq "2"                          ## Ladevorgang läuft
setstate PV_1_Speicher_1_ExternTrigger 2021-03-03 12:18:25 SpeicherCmdRepeatRunning 0
</pre>
setstate PV_1_Speicher_1_ExternTrigger 2021-02-18 14:33:07 SpeicherEntladung Automatik
setstate PV_1_Speicher_1_ExternTrigger 2021-03-03 09:15:36 SpeicherMaxSOCControlActive 0
setstate PV_1_Speicher_1_ExternTrigger 2021-03-03 12:18:25 SpeicherMaxSOCControlRunning 0
setstate PV_1_Speicher_1_ExternTrigger 2021-03-03 12:18:25 SpeicherMaxSOC_Actual 100
setstate PV_1_Speicher_1_ExternTrigger 2021-03-03 08:47:09 SpeicherMaxSOC_DayBefore 100
setstate PV_1_Speicher_1_ExternTrigger 2021-03-03 11:23:19 SpeicherMaxSOC_fc1_Limit 30000
setstate PV_1_Speicher_1_ExternTrigger 2021-03-03 07:13:00 SpeicherMiddayControlActive 0
setstate PV_1_Speicher_1_ExternTrigger 2021-03-03 12:18:25 SpeicherMiddayControlRunning 0
setstate PV_1_Speicher_1_ExternTrigger 2021-03-02 09:50:06 SpeicherMidday_MaxChargePowerAbs 1500
setstate PV_1_Speicher_1_ExternTrigger 2021-02-22 16:40:54 SpeicherMidday_MaxSOC 40
setstate PV_1_Speicher_1_ExternTrigger 2021-02-19 14:58:00 SpeicherMinSOC_Sommer 5
setstate PV_1_Speicher_1_ExternTrigger 2021-02-18 14:33:07 SpeicherMinSOC_Winter 20
setstate PV_1_Speicher_1_ExternTrigger 2021-02-19 17:32:58 SpeicherMinSOC_fc1_Limit 13000
setstate PV_1_Speicher_1_ExternTrigger 2021-02-18 14:33:27 SpeicherTrigger none
setstate PV_1_Speicher_1_ExternTrigger 2021-02-18 16:39:57 SpeicherZeitEnde 16:00
setstate PV_1_Speicher_1_ExternTrigger 2021-02-18 16:39:21 SpeicherZeitStart 07:00
</syntaxhighlight>


====RAW Definition des PV_1_Speicher_1_ExternControl====
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]
defmod PV_1_Speicher_1_ExternControl DOIF ################################################################################################################\
 
## 1 Speicher Status vom PV_1_Speicher_1 aktualisieren.\
===Kostal Smart Energy Manager (KSEM) (Modbus/TCP)===
##  Dies geschieht über das PV_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.
##\
 
([:58])\
Um den ModBus am KSEM zu nutzen muss man im ModBus Menü die Option "Slave" aktivieren.
\
 
  (get PV_1_API 21_Battery_Information)\
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.
  (get PV_1_API 22_Battery_InternControl)\
 
  (get PV_1_API 25_Battery_EM_State)\
==== 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.
## 2 Wenn die Ladung im Herbst/Winter unter MinSoc geht allen PV Überschuss in die Batterie laden\
 
##\
Das Device wurde umbenannt, um es besser in die gesamt Implementierung einzugliedern.
## Im Winter kann der MinSoc, durch den WR Eigenverbrauch, unterschritten werden, deshalb wird vorher auf\
<pre>
## smarte_laden umgeschaltet, bis die Batterie wieder einen hohen Soc erreicht hat. Siehe cmd_3 laden_beendet\
- 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.
DOELSEIF\
</pre>
([PV_1:Solar_Calculation_fc0_day] < [PV_1_Speicher_1_ExternTrigger:SpeicherMinSOC_fc1_Limit] and  ## Im Herbst/Winter ist wenig zu erwarten\
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.
  [PV_1:Act_state_of_charge] <= [PV_1_API:Battery_InternControl_MinSoc] and                        ## Achtung der Speicherstand wird zu niedrig\
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.
  [PV_1_API:Battery_InternControl_MinHomeConsumption] <= 100 )                                      ## Der Speicher steht auf Entladen\
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.
\
 
  (get PV_1_Speicher_1 BatteryInformation)\
*[https://svn.fhem.de/fhem/trunk/fhem/contrib/ch.eick/Photovoltaik/Wechselrichter/RAW_WR_0_KSEM.txt Beispiel WR_0_KSEM]
  (set PV_1_API 22_03_Battery_MinHomeConsumption [PV_1_API:Battery_Info_WorkCapacity])\
 
  ({if (AttrVal("$SELF","verbose",0) >=3){Log 3, "PV_1_Speicher_1_ExternControl cmd_2  : smart_laden aktiviert"}})\
==Device Übersicht mit Hilfen zur Orientierung==
\
<pre>
  ({if (AttrVal("$SELF","verbose",0) >=3){Log 3, "PV_1_Speicher_1_ExternControl cmd_2  : ExternTrigger, Entlademodus gesperrt"}})\
Device            über Device      Hardware   Protokoll Netzwerk              Informationen
  (setreading PV_1_Speicher_1_ExternTrigger ExternTrigger gesperrt)\
 
\
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
## 3 Beim erreichen von 90% Soc die Entladung wieder frei geben oder\
 
##  bei Zeitsteuerung und guter Prognose auch schon bei 40%\
WR_1                              Plenticore MODBUS    LAN                    Messwerte und berechnete Werte, teilweise Speicher Informationen
DOELSEIF\
WR_1_API                                      HTTPMOD  LAN                    Statistiken, Speichersteuerung und Informationen
([PV_1_API:Battery_InternControl_MinHomeConsumption] > 100 and                                    ## Der Speicher wird voll und wieder\
 
  ([PV_1:Act_state_of_charge] >= 90                                                                ## zum Entladen frei gegeben\
WR_2                              Plenticore MODBUS    LAN                    Messwerte und berechnete Werte. Achtung, im Schwarm hat nur der Master WR einen Speicher.
  or\
WR_2_API                                      HTTPMOD  LAN                    Statistiken
  [PV_1_Speicher_1_ExternTrigger:SpeicherEntladung]  eq "Zeit" and                                ## Bei Zeitsteuerung\
 
  [PV_1:Act_state_of_charge] >= 40 and                                                            ## und einem Stand von Soc 40%\
 
  ([PV_1:Solar_Calculation_fc0_day] > [PV_1_Speicher_1_ExternTrigger:SpeicherMaxSOC_fc1_Limit]    ## wenn es heute oder \
FHEM Steuerung                                MODBUS    LAN                    Laufende Informationen im Minuten Takt
    or\
                                              HTTPMOD  LAN                    Abfragen und Steuerung einzelner Devices
    [PV_1:Solar_Calculation_fc1_day] > [PV_1_Speicher_1_ExternTrigger:SpeicherMaxSOC_fc1_Limit])    ## morgen viel Leistung gibt\
 
  )\
WR_ctl                                        DOIF                            Startet regelmäßige Aktionen zur zeitlichen Steuerung der PV-Anlage
)\
                                                                              Dient der Anzeige von aktuellen und Statistischen Daten
\
 
({\
  1 Stündlich
  if ( ReadingsVal("PV_1_Speicher_1_ExternTrigger","SpeicherEntladung","Automatik") eq "Automatik" ) { ## Bei Automatik sofort auf Entladen\
  1.1 WR_2_API 20_Statistic_EnergyFlow                                        Statistiken vom Plenticore abholen; die Reihenfolge ist auch wichtig!
    if (AttrVal("$SELF","verbose",0) >=3){Log 3, "PV_1_Speicher_1_ExternControl cmd_3 : Batterie auf [PV_1_API:Battery_Info_SoC] %, Entlademodus freigegeben"};;\
  1.2 WR_1_API 20_Statistic_EnergyFlow                                        Statistiken vom Plenticore abholen
    CommandSet(undef, "PV_1_API 22_03_Battery_MinHomeConsumption 50");;\
 
    CommandSetReading(undef, "PV_1_Speicher_1_ExternTrigger ExternTrigger none");;\
  2 Stündlich von 05:00 bis 22:00
  } else {                                                                                        ## Ansonsten die Zeitsteuerung oder den\
  2.1 KI_Prognose() für fc0 und fc1                                          Aktualisieren der fc0 Prognose
    if (AttrVal("$SELF","verbose",0) >=3){Log 3, "PV_1_Speicher_1_ExternControl cmd_3  : Batterie auf [PV_1_API:Battery_Info_SoC] %, ExternTrigger, freigegeben"};;\
 
    CommandSetReading(undef, "PV_1_Speicher_1_ExternTrigger ExternTrigger frei");;                  ## Trigger freigeben\
  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
################################################################################################################\
 
## 4 Freigabe der Batterie mit externem Trigger\
     1 Stündlich
##  z.B. ([07:00-16:00]\
 
DOELSEIF\
     1.1 WR_1_API 21_Battery_Information                                        Allgemeine Informationen
([PV_1_Speicher_1_ExternTrigger:ExternTrigger] eq "frei" and                                      ## Verriegelung, wenn zwangsgeladen werden muss\
                    Battery_Info_SoC,
\
                    Battery_Info_WorkCapacity
  ([PV_1_Speicher_1_ExternTrigger:SpeicherEntladung] eq "Trigger" and                              ## Triggersteuerung\
     1.2 WR_1_API 22_Battery_InternControl                                      Speicher Information der Internen Steuerung
    [PV_1_Speicher_1_ExternTrigger:SpeicherTrigger]  eq "entladen"                                ## also Speicherentladung freigeben\
                    Battery_InternControl_MinSoc,
    or\
                    Battery_InternControl_MinHomeConsumption
    [PV_1_Speicher_1_ExternTrigger:SpeicherEntladung] eq "Zeit" and                                ## oder bei Zeitsteuerung wenn das\
     1.3 WR_1_API 23_Battery_ExternControl                                      Speicher Information der Externen Steuerung
    [[PV_1_Speicher_1_ExternTrigger:SpeicherZeitStart]-[PV_1_Speicher_1_ExternTrigger:SpeicherZeitEnde]])  ## Zeitfenster aktiv ist\
    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
  (set PV_1_API 22_03_Battery_MinHomeConsumption 50)                                                ## Speicher für Entladung freigeben\
    2.2 WR_1_Speicher_1_ExternControl ExternTrigger gesperrt                  Batterie ExternTrigger, Entlademodus gesperrt . Die Zeit Steuerung wird verriegelt
  ({if (AttrVal("$SELF","verbose",0) >=3){Log 3, "PV_1_Speicher_1_ExternControl cmd_4  : ExternTrigger, Entlademodus freigegeben"}})\
 
\
    3 Freigabe zur Entladung im Winter
################################################################################################################\
    3.1 bei überschreiten von SOC 90%                                         Sobald der Speicher gut gefüllt ist oder
## 5 Sperren der Batterie mit externem Trigger\
            WR_1_API:Battery_Info_SoC
##  z.B. [16:00-07:00]\
    3.2 Bei Zeitsteuerung und guter Prognose mit SOC 40%                       bereits vorher, weil der Tarif teuer ist
DOELSEIF\
 
([PV_1_Speicher_1_ExternTrigger:ExternTrigger] eq "frei" and                                      ## Verriegelung, wenn zwangsgeladen werden muss\
    4 Speicher Freigabe bei Trigger oder Zeit Steuerung                        Dieses cmd_ löst die Abhängigkeiten von Zeit und Trigger auf
\
 
  ([PV_1_Speicher_1_ExternTrigger:SpeicherEntladung]  eq "Trigger" and                            ## Triggersteuerung\
    5 Speicher sperren bei Trigger oder Zeit Steuerung                        Dieses cmd_ löst die Abhängigkeiten von Zeit und Trigger auf
    [PV_1_Speicher_1_ExternTrigger:SpeicherTrigger]    eq "gesperrt"                                ## also Speicherentladung sperren\
 
    or\
     6 Wiederhole alle 180s die Kommandos der ExternControl Steuerung          Wenn keine Wiederholung erfolgt geht der Plenticore wieder auf die interne Steuerung
    [PV_1_Speicher_1_ExternTrigger:SpeicherEntladung]  eq "Zeit" and                                ## oder bei Zeitsteuerung wenn das\
     6.1 WR_1_Speicher_1_ExternControl:SpeicherMiddayControlActive             wird durchlaufen, wenn der Forecast eine z.B. 70% Überschreitung erkannt hat und
    [[PV_1_Speicher_1_ExternTrigger:SpeicherZeitEnde]-[PV_1_Speicher_1_ExternTrigger:SpeicherZeitStart]])  ## Zeitfenster verlassen wurde\
         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
  (set PV_1_API 22_03_Battery_MinHomeConsumption [PV_1_API:Battery_Info_WorkCapacity])              ## Speicher für Entladung sperren\
    6.3 WR_ctl:Yield_fc0_middayhigh_start <> WR_ctl:Yield_fc0_middayhigh_stop Das Laden wird mit voller Leistung freigegeben
  ({if (AttrVal("$SELF","verbose",0) >=3){Log 3, "PV_1_Speicher_1_ExternControl cmd_5  : ExternTrigger, Entlademodus gesperrt (Tarif oder Trigger)"}})\
     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%
################################################################################################################\
 
## 6 Wiederhole alle 180s die Kommandos der ExternControl Steuerung\
    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
DOELSEIF\
    7.2 Wenn eine Überschreitung der 70% erwartet wird                         Aktivierung der Midday Steuerung
([PV_1_API:Battery_Control] > 0 and                                    ## Wenn die ExternControl am WR konfiguriert ist\
 
  [PV_1_Speicher_1_ExternTrigger:SpeicherCmdRepeatActive]  == 1 and      ## Wenn die ExternControl Aktiviert ist\
    8 Zurücksetzen der externen Speichersteuerung
  [PV_1_Speicher_1_ExternTrigger:SpeicherCmdRepeatRunning] == 1 and      ## Wenn es  ExternControl Kommandos zum Senden gibt\
 
  [07:00-20:00] and [+180] )                                            ## alle 3 Minuten den Befehl wiederholen\
    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%
    if ([PV_1_Speicher_1_ExternTrigger:SpeicherMiddayControlRunning] == 1 ) {\
 
\
  11 WR_1_Speicher_1 Status aktualisieren                                    Nur beim BYD HV, Abfrage der Speicher Detailinformationen. Kann einfach entfernt werden
       if ( time < time_str2num(POSIX::strftime("%Y-%m-%d",localtime(time))." [PV_1:Solar_middayhigh_fc0_start]") ) {\
 
         CommandSet(undef, "PV_1_API 23_07_Battery_ExternControl_MaxChargePowerAbs [PV_1_Speicher_1_ExternTrigger:SpeicherMidday_MaxChargePowerAbs]");;\
 
        CommandSet(undef, "PV_1_API 23_09_Battery_ExternControl_MaxSocRel [PV_1_Speicher_1_ExternTrigger:SpeicherMidday_MaxSOC]");;\
WR_1_Speicher_1_ExternControl                readings                        Konfiguration für die externe Speichersteuerung
        if (AttrVal("$SELF","verbose",0) >=3) {\
 
          Log 3, "PV_1_Speicher_1_ExternControl cmd_6  : SpeicherMiddayControl vor [PV_1:Solar_middayhigh_fc0_start] limitieren";;\
   ExternTrigger none                                                          Wird automatisch gesetzt und dient der Verriegelung im WR_1_Speicher_1_ExternControl
          Log 3, "PV_1_Speicher_1_ExternControl cmd_6  : Battery_ExternControl_MaxChargePowerAbs auf [PV_1_Speicher_1_ExternTrigger:SpeicherMidday_MaxChargePowerAbs] limitiert";;\
                                                                              Zustände: frei/gesperrt/none
          Log 3, "PV_1_Speicher_1_ExternControl cmd_6  : Battery_ExternControl_MaxSOC auf [PV_1_Speicher_1_ExternTrigger:SpeicherMidday_MaxSOC] % limitiert";;\
  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
      } else {\
                                                                              Zustände:
\
                                                                                Automatik - MinSOC Steuerung Sommer/Winter
        if ([PV_1_Speicher_1_ExternTrigger:SpeicherMaxSOCControlRunning] == 1 and\
                                                                                Zeit      - z.B. bei Tarifsteuerung. Zeiten werden über zusätzliche DOIF
            [PV_1_Speicher_1_ExternTrigger:SpeicherMaxSOC_Actual] != 100 ) {\
                                                                                            oder WeekdayTimer gesetzt
          CommandSet(undef, "PV_1_API 23_09_Battery_ExternControl_MaxSocRel [PV_1_Speicher_1_ExternTrigger:SpeicherMaxSOC_Actual]");;\
                                                                                Trigger  - Ein beliebiger Mechanismus z.B. DOIF steuert den Speicher
          if (AttrVal("$SELF","verbose",0) >=3)\
            {Log 3, "PV_1_Speicher_1_ExternControl cmd_6  : Battery_ExternControl_MaxSocRel auf SpeicherMaxSOC_Actual [PV_1_Speicher_1_ExternTrigger:SpeicherMaxSOC_Actual] % halten"};;\
        };;\
\
      };;\
\
      if (time_str2num(POSIX::strftime("%Y-%m-%d",localtime(time))." [PV_1:Solar_middayhigh_fc0_start]") <= time and\
          time <= time_str2num(POSIX::strftime("%Y-%m-%d",localtime(time))." [PV_1:Solar_middayhigh_fc0_stop]") ) {\
        CommandSet(undef, "PV_1_API 23_07_Battery_ExternControl_MaxChargePowerAbs 4864");;\
        CommandSet(undef, "PV_1_API 23_09_Battery_ExternControl_MaxSocRel [PV_1_Speicher_1_ExternTrigger:SpeicherMaxSOC_Actual]");;\
        if (AttrVal("$SELF","verbose",0) >=3)\
          {Log 3, "PV_1_Speicher_1_ExternControl cmd_6  : SpeicherMiddayControlActive laden von [PV_1:Solar_middayhigh_fc0_start] bis [PV_1:Solar_middayhigh_fc0_stop] freigegeben"};;\
      };;\
\
      if (time > time_str2num(POSIX::strftime("%Y-%m-%d",localtime(time))." [PV_1:Solar_middayhigh_fc0_stop]") ) {\
        CommandSetReading(undef, "PV_1_Speicher_1_ExternTrigger SpeicherMiddayControlRunning 0");;\
        if (AttrVal("$SELF","verbose",0) >=3)\
          {Log 3, "PV_1_Speicher_1_ExternControl cmd_6  : SpeicherMiddayControl nach [PV_1:Solar_middayhigh_fc0_stop] beendet"};;\
      };;\
\
    } else {\
\
      if ([PV_1_Speicher_1_ExternTrigger:SpeicherMaxSOCControlRunning] == 1 and\
          [PV_1_Speicher_1_ExternTrigger:SpeicherMaxSOC_Actual] != 100 ) {\
        CommandSet(undef, "PV_1_API 23_09_Battery_ExternControl_MaxSocRel [PV_1_Speicher_1_ExternTrigger:SpeicherMaxSOC_Actual]");;\
        if (AttrVal("$SELF","verbose",0) >=3)\
          {Log 3, "PV_1_Speicher_1_ExternControl cmd_6  : Battery_ExternControl_MaxSocRel [PV_1_Speicher_1_ExternTrigger:SpeicherMaxSOC_Actual] % halten"};;\
      };;\
\
     };;\
\
     if (AttrVal("$SELF","verbose",0) >=3)\
      {Log 3, "PV_1_Speicher_1_ExternControl cmd_6  : ExternControl Kommandowiederholung erledigt"};;\
  }\
  )\
\
################################################################################################################\
## 7 Bestimmung eines möglichen SOC für den nächsten Morgen und\
##  Vorbereitung für ein Leistungshoch am Mittag\
##\
DOELSEIF\
([PV_1_API:Battery_Control] > 0 and                                                                ## Ist die ExternControl am WR aktiviert\
  ([PV_1_Speicher_1_ExternTrigger:SpeicherMaxSOCControlActive] == 1 or                              ## Ist MaxSOC Limit konfiguriert\
  [PV_1_Speicher_1_ExternTrigger:SpeicherMiddayControlActive] == 1 ) and                          ## Ist Midday Kontrolle konfiguriert\
  [07:13])\
\
  ({\
     if ([PV_1_Speicher_1_ExternTrigger:SpeicherMaxSOCControlActive] == 1 ) {\
\
      my $SpeicherSOCMinimum = [PV_1_API:Battery_InternControl_MinSoc]*3;;\
      if ([PV_1:Solar_Calculation_fc1_day] > [PV_1_Speicher_1_ExternTrigger:SpeicherMaxSOC_fc1_Limit] and\
          [PV_1:Act_state_of_charge]      > $SpeicherSOCMinimum ) {\
        my $SpeicherSOCDayBefore = ReadingsVal("PV_1_Speicher_1_ExternTrigger","SpeicherMaxSOC_DayBefore", 100);;\
        my $SpeicherSOCDelta     = [PV_1:Act_state_of_charge]-$SpeicherSOCMinimum ;;\
        my $SpeicherSOCNew      = 0;;\
        if ($SpeicherSOCDelta <= 5) {\
          CommandSetReading(undef, "PV_1_Speicher_1_ExternTrigger SpeicherMaxSOC_Actual ".$SpeicherSOCDayBefore);;\
          if (AttrVal("$SELF","verbose",0) >=3)\
            {Log 3, "PV_1_Speicher_1_ExternControl cmd_7  : SpeicherMaxSOC_DayBefore ".$SpeicherSOCDayBefore." % gesichert"};;\
        } else {\
          $SpeicherSOCNew = ($SpeicherSOCDayBefore+$SpeicherSOCDayBefore-$SpeicherSOCDelta)/2;;\
          CommandSetReading(undef, "PV_1_Speicher_1_ExternTrigger SpeicherMaxSOC_DayBefore ".$SpeicherSOCNew);;\
          CommandSetReading(undef, "PV_1_Speicher_1_ExternTrigger SpeicherMaxSOC_Actual ".$SpeicherSOCNew);;\
          if (AttrVal("$SELF","verbose",0) >=3)\
            {Log 3, "PV_1_Speicher_1_ExternControl cmd_7  : SpeicherMaxSOC_DayBefore ".$SpeicherSOCNew." % neu berechnet und gesichert"};;\
        };;\
\
        CommandSetReading(undef, "PV_1_Speicher_1_ExternTrigger SpeicherMaxSOCControlRunning 1");;\
        CommandSetReading(undef, "PV_1_Speicher_1_ExternTrigger SpeicherCmdRepeatRunning 1");;\
        if (AttrVal("$SELF","verbose",0) >=3)\
          {Log 3, "PV_1_Speicher_1_ExternControl cmd_7  : SpeicherMaxSOC_Actual ".$SpeicherSOCNew." % geplant"};;\
      } else {\
        if (AttrVal("$SELF","verbose",0) >=3)\
          {Log 3, "PV_1_Speicher_1_ExternControl cmd_7 : SpeicherMaxSOC_Actual wird nicht begrenzt"};;\
      };;\
     };;\
\
     if ([PV_1_Speicher_1_ExternTrigger:SpeicherMiddayControlActive] == 1 and\
         [PV_1:Solar_middayhigh_fc0] == 1 ) {\
\
      CommandSetReading(undef, "PV_1_Speicher_1_ExternTrigger SpeicherMiddayControlRunning 1");;\
      CommandSetReading(undef, "PV_1_Speicher_1_ExternTrigger SpeicherCmdRepeatRunning 1");;\
      if (AttrVal("$SELF","verbose",0) >=3){\
         Log 3, "PV_1_Speicher_1_ExternControl cmd_7 : Batterie SpeicherMiddayControlRunning vorbereitet";;\
         Log 3, "PV_1_Speicher_1_ExternControl cmd_7  : Batterie Solar_middayhigh_fc0_start [PV_1:Solar_middayhigh_fc0_start] gesetzt";;\
        Log 3, "PV_1_Speicher_1_ExternControl cmd_7  : Batterie Solar_middayhigh_fc0_stop [PV_1:Solar_middayhigh_fc0_stop] gesetzt";;\
      };;\
     } else {\
      Log 3, "PV_1_Speicher_1_ExternControl cmd_7  : SpeicherMiddayControl es wird kein Middayhigh geben";;\
     };;\
  }\
  )\
\
################################################################################################################\
## 8 Reset der ExternControl Kommandos\
##\
DOELSEIF\
([20:07])\
\
  (setreading PV_1_Speicher_1_ExternTrigger SpeicherCmdRepeatRunning 0 )                            ## Stop das regelmäßige senden der Kommandos\
\
  (setreading PV_1_Speicher_1_ExternTrigger SpeicherMaxSOCControlRunning 0 )                         ## Midday Steuerung zurücksetzen\
  (setreading PV_1_Speicher_1_ExternTrigger SpeicherMaxSOC_Actual 100 )                              ## SpeicherMaxSOC_Actual auf Default\
\
  (setreading PV_1_Speicher_1_ExternTrigger SpeicherMiddayControlRunning 0 )                        ## Midday Steuerung zurücksetzen\
\
  (setreading PV_1 Solar_middayhigh_fc0 0 )\
  (setreading PV_1 Solar_middayhigh_fc0_start 00:00 )\
  (setreading PV_1 Solar_middayhigh_fc0_stop 00:00 )\
\
  (setreading PV_1 Solar_middayhigh_fc1 0 )\
  (setreading PV_1 Solar_middayhigh_fc1_start 00:00 )\
  (setreading PV_1 Solar_middayhigh_fc1_stop 00:00 )\
\
  ({if (AttrVal("$SELF","verbose",0) >=3){Log 3, "PV_1_Speicher_1_ExternControl cmd_8  : ExternControl zurückgesetzt"}})\
\
################################################################################################################\
## 9 Umschaltung des MinSOC wenn zu wenig Leistung erwartet wird, das ist dann im Herbst/Winter\
##\
DOELSEIF\
([PV_1:Solar_Calculation_fc1_day] < [PV_1_Speicher_1_ExternTrigger:SpeicherMinSOC_fc1_Limit] and    ## Wenn morgen das Minimum an Leistung nicht erreicht wird\
  [PV_1_API:Battery_InternControl_MinSoc] < [PV_1_Speicher_1_ExternTrigger:SpeicherMinSOC_Winter] and  ## und der MinSoc unter der Winter Wert eingestellt ist\
  [10:07] )\
  \
  (set PV_1_API 22_04_Battery_MinSoc [PV_1_Speicher_1_ExternTrigger:SpeicherMinSOC_Winter])          ## Den MinSOC anheben, um eine eventuelle\
  ({if (AttrVal("$SELF","verbose",0) >=3){Log 3, "PV_1_Speicher_1_ExternControl cmd_9  : Batterie MinSoc auf Winterbetrieb"}})  ## Notladung zu verhindern\
\
################################################################################################################\
## 10 Umschaltung des MinSoc wenn viel Leistung erwartet wir, das wäre dann Frühling/Sommer\
##\
DOELSEIF\
([PV_1:Solar_Calculation_fc1_day] > [PV_1_Speicher_1_ExternTrigger:SpeicherMinSOC_fc1_Limit] and   ## sobald viel Ladung erwartet wird und der MinSoc noch\
  [PV_1_API:Battery_InternControl_MinSoc] > [PV_1_Speicher_1_ExternTrigger:SpeicherMinSOC_Sommer] and  ## noch im Winter Modus ist\
  [10:09] )\
  \
  (set PV_1_API 22_04_Battery_MinSoc [PV_1_Speicher_1_ExternTrigger:SpeicherMinSOC_Sommer])          ## den MinSOC auf Sommerbetrieb herabsetzen, es kann\
  ({if (AttrVal("$SELF","verbose",0) >=3){Log 3, "PV_1_Speicher_1_ExternControl cmd_10 : Batterie MinSoc auf Sommerbetrieb"}}) ## wieder mehr Leistung genutzt werden\
\
################################################################################################################\
## 11 PV_1_Speicher_1 Status aktualisieren.\
##  Dies ist momentan nur für den BYD HV Speicher, da der BYD HVS eine direkte Abfrage nicht unterstützt.\
##\
DOELSEIF\
([+:06] and !([:00] or [:30]))                                                                      ## wer keinen BYD HV Speicher hat kann das löschen\
\
  (get PV_1_Speicher_1 BatteryInformation)\
  (get PV_1_Speicher_1 StatisticInformation)\
\


attr PV_1_Speicher_1_ExternControl DbLogExclude .*
  SpeicherMaxSOCControlActive  [An|Aus]                                      Wird von KI_Prognose() gesetzt, wenn der fc0 über WR_1:Inverter_Max_Power (70% Regel) liegt
attr PV_1_Speicher_1_ExternControl alias PV_1_Speicher_1_ExternControl
  SpeicherMaxSOCControlRunning [An|Aus]                                      Das reading signalisiert den aktuellen Laufzeitstatus
attr PV_1_Speicher_1_ExternControl cmdState Status_API|smart_laden|laden_beendet|Speicher_freigabe|Speicher_gesperrt|ExternControl wiederholen|setzen_MaxSoc|MinSoc Winter|MinSoc_Sommer|Status_direkt
  SpeicherMaxSOC_Actual      100                                              Wird im WR_1_Speicher_1_ExternControl cmd_7 berechnet, wenn der Speicher morgens zuviel Ladung hat
attr PV_1_Speicher_1_ExternControl comment Version 2021.03.01 10:30
  SpeicherMaxSOC_DayBefore    xx                                              Das ist der letzte berechnete Wert, damit sich die Berechnung langsam einem Optimum nähern kann
attr PV_1_Speicher_1_ExternControl do always
  SpeicherMaxSOC_fc1_Limit 30000                                              Hier einen Wert setzen, bei dem der Speicher über Nacht bis morgens gereicht hat.
attr PV_1_Speicher_1_ExternControl group PV Eigenverbrauch
                                                                              Damit wird dann der Übergang vom Winter zum Frühjahr erkannt.
attr PV_1_Speicher_1_ExternControl icon measure_battery_100
  SpeicherMiddayControlActive  [An|Aus]                                      Wird von KI_Prognose() gesetzt, wenn der fc0 über WR_1:Inverter_Max_Power (70% Regel) liegt
attr PV_1_Speicher_1_ExternControl room Strom->Photovoltaik
  SpeicherMiddayControlRunning [An|Aus]                                      Das reading signalisiert den aktuellen Laufzeitstatus
attr PV_1_Speicher_1_ExternControl sortby 122
  SpeicherMidday_Inverter_Max_Power        8500                              Manuelles überschreiben für WR_1:Inverter_Max_Power, wenn man kontrollierter den Tag über laden möchte
attr PV_1_Speicher_1_ExternControl verbose 3
  SpeicherMidday_MaxChargePowerAbs_midday  1000                              Begrenzung der Ladeleistung am Mittag, damit nicht zu schnell geladen wird.
attr PV_1_Speicher_1_ExternControl wait 0,3,3:0:0:0:0:0:0:0:0,3
                                                                              Steht der Wert auf 0 wird dynamisch während der Laufzeit ein Wert berechnet.
attr PV_1_Speicher_1_ExternControl webCmd cmd_1:cmd_2:cmd_3:cmd_4:cmd_5
  SpeicherMidday_MaxChargePowerAbs_morning  450                              Begrenzung der Ladeleistung am Vormittag, damit Mittags genug Platz im Speicher ist
attr PV_1_Speicher_1_ExternControl webCmdLabel Status_API :smart_laden :laden_beendet :Speicher_freigabe :Speicher_gesperrt :
  SpeicherMidday_MaxSOC 30                                                    Limitierung des Speichers um Mittags genug Platz zu haben
</syntaxhighlight>
  SpeicherMinSOC_Sommer 5                                                    Sommer MinSOC von Kostal vorgegeben
 
  SpeicherMinSOC_Winter 20                                                    Winter MinSOC von Kostal vorgegeben
===Kostal Smart Energy Manager (KSEM) (Modbus/TCP)===
  SpeicherMinSOC_fc1_Limit 14000                                              Wenn im Herbst/Winter der Forecast zu schlecht wird muss dieser Wert auf die Anlage
Diese Einbindung ist nicht zwingend notwendig, jedoch weil es möglich ist hier beschrieben.
                                                                              angepasst werden. Das signalisiert die Winter Zeit
Das Gerät ist hier mit "disable 1" konfiguriert, um es zu verwenden muss das Attribut auf 0 gesetzt oder einfach gelöscht werden.
  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.


Auch hier ist ein Interval von 60 Sekunden gesetzt worden. Das Logging ist noch komplett deaktiviert, weshalb man seine Werte noch selber definieren muss.
  Beispiele:


==== RAW Definition des KSEM ====
  1 SpeicherEntladung Automatik
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.
    Nur grundlegende Steuerungen erfolgen automatisch.
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.
      - Sommer/Winter Umschaltung des MinSOC                                  Schützt den Speicher vor einer Notladung im Winter
<syntaxhighlight lang="Perl">
      - smart_laden                                                            Sorgt dafür, das der Speicher im Winter nicht ständig geladen und wieder entladen wird
defmod PV_1_KSEM ModbusAttr 1 60 <IP-Address_KSEM>:502 TCP
      - laden_beendet                                                          Gibt den Speicher nach dem smart_laden wieder frei
attr PV_1_KSEM DbLogExclude .*
attr PV_1_KSEM alias PV_1_KSEM
attr PV_1_KSEM comment Version 2020.10.19 18:28\
Der KSEM ermittelt nicht alle Werte, welche in der SunSpec spezifiziert sind.\
Alle nicht unterstützen Werte sind mit 0x8000 gekennzeichnet.\
Für die nicht unterstützten Zählerstände wird die 0x800000000 ausgegeben.\
\
Der Summenstrom M_AC_Current (sum of active phases) kann aber durch den Endanwender selber\
berechnet werden aus der Summe der Einzelwerte (Phase A AC current, Phase B AC current Phase C AC current)\
\
Die einzelnen Spannungen zwischen den Phasen können nicht gemessen werden und werden deshalb nicht ausgegeben.
attr PV_1_KSEM dev-h-defPoll 1
attr PV_1_KSEM dev-type-INT16-len 1
attr PV_1_KSEM dev-type-INT16-unpack s>
attr PV_1_KSEM dev-type-INT16_Current-expr $val * (10 ** ReadingsNum("$name" ,"M_AC_Current_SF",0))
attr PV_1_KSEM dev-type-INT16_Current-format %.2f
attr PV_1_KSEM dev-type-INT16_Current-len 1
attr PV_1_KSEM dev-type-INT16_Current-unpack s>
attr PV_1_KSEM dev-type-INT16_Freq-expr $val * (10 ** ReadingsNum("$name" ,"M_AC_Freq_SF",0))
attr PV_1_KSEM dev-type-INT16_Freq-format %.2f
attr PV_1_KSEM dev-type-INT16_Freq-len 1
attr PV_1_KSEM dev-type-INT16_Freq-unpack s>
attr PV_1_KSEM dev-type-INT16_PF-expr $val * (10 ** ReadingsNum("$name" ,"M_AC_PF_SF",0))
attr PV_1_KSEM dev-type-INT16_PF-format %.2f
attr PV_1_KSEM dev-type-INT16_PF-len 1
attr PV_1_KSEM dev-type-INT16_PF-unpack s>
attr PV_1_KSEM dev-type-INT16_Power-expr $val * (10 ** ReadingsNum("$name" ,"M_AC_Power_SF",0))
attr PV_1_KSEM dev-type-INT16_Power-format %.2f
attr PV_1_KSEM dev-type-INT16_Power-len 1
attr PV_1_KSEM dev-type-INT16_Power-unpack s>
attr PV_1_KSEM dev-type-INT16_VA-expr $val * (10 ** ReadingsNum("$name" ,"M_AC_VA_SF",0))
attr PV_1_KSEM dev-type-INT16_VA-format %.2f
attr PV_1_KSEM dev-type-INT16_VA-len 1
attr PV_1_KSEM dev-type-INT16_VA-unpack s>
attr PV_1_KSEM dev-type-INT16_VAR-expr $val * (10 ** ReadingsNum("$name" ,"M_AC_VAR_SF",0))
attr PV_1_KSEM dev-type-INT16_VAR-format %.2f
attr PV_1_KSEM dev-type-INT16_VAR-len 1
attr PV_1_KSEM dev-type-INT16_VAR-unpack s>
attr PV_1_KSEM dev-type-INT16_Voltage-expr $val * (10 ** ReadingsNum("$name" ,"M_AC_Voltage_SF",0))
attr PV_1_KSEM dev-type-INT16_Voltage-format %.2f
attr PV_1_KSEM dev-type-INT16_Voltage-len 1
attr PV_1_KSEM dev-type-INT16_Voltage-unpack s>
attr PV_1_KSEM dev-type-STR32-expr $val =~ s/[\00]+//gr
attr PV_1_KSEM dev-type-STR32-format %s
attr PV_1_KSEM dev-type-STR32-len 16
attr PV_1_KSEM dev-type-STR32-unpack a*
attr PV_1_KSEM dev-type-UINT16-format %s
attr PV_1_KSEM dev-type-UINT16-len 1
attr PV_1_KSEM dev-type-UINT32-format %s
attr PV_1_KSEM dev-type-UINT32-len 2
attr PV_1_KSEM dev-type-UINT64-expr $val/10000
attr PV_1_KSEM dev-type-UINT64-format %s
attr PV_1_KSEM dev-type-UINT64-len 4
attr PV_1_KSEM dev-type-UINT64-unpack Q>
attr PV_1_KSEM disable 1
attr PV_1_KSEM group PV Eigenverbrauch
attr PV_1_KSEM icon measure_power
attr PV_1_KSEM obj-h40072-reading M_AC_Current_A
attr PV_1_KSEM obj-h40072-type INT16_Current
attr PV_1_KSEM obj-h40073-reading M_AC_Current_B
attr PV_1_KSEM obj-h40073-type INT16_Current
attr PV_1_KSEM obj-h40074-reading M_AC_Current_C
attr PV_1_KSEM obj-h40074-type INT16_Current
attr PV_1_KSEM obj-h40075-reading M_AC_Current_SF
attr PV_1_KSEM obj-h40075-type INT16
attr PV_1_KSEM obj-h40077-reading M_AC_Voltage_AN
attr PV_1_KSEM obj-h40077-type INT16_Voltage
attr PV_1_KSEM obj-h40078-reading M_AC_Voltage_BN
attr PV_1_KSEM obj-h40078-type INT16_Voltage
attr PV_1_KSEM obj-h40079-reading M_AC_Voltage_CN
attr PV_1_KSEM obj-h40079-type INT16_Voltage
attr PV_1_KSEM obj-h40084-reading M_AC_Voltage_SF
attr PV_1_KSEM obj-h40084-type INT16
attr PV_1_KSEM obj-h40085-reading M_AC_Freq
attr PV_1_KSEM obj-h40085-type INT16_Freq
attr PV_1_KSEM obj-h40086-reading M_AC_Freq_SF
attr PV_1_KSEM obj-h40086-type INT16
attr PV_1_KSEM obj-h40087-reading M_AC_Power
attr PV_1_KSEM obj-h40087-type INT16_Power
attr PV_1_KSEM obj-h40088-reading M_AC_Power_A
attr PV_1_KSEM obj-h40088-type INT16_Power
attr PV_1_KSEM obj-h40089-reading M_AC_Power_B
attr PV_1_KSEM obj-h40089-type INT16_Power
attr PV_1_KSEM obj-h40090-reading M_AC_Power_C
attr PV_1_KSEM obj-h40090-type INT16_Power
attr PV_1_KSEM obj-h40091-reading M_AC_Power_SF
attr PV_1_KSEM obj-h40091-type INT16
attr PV_1_KSEM obj-h40092-reading M_AC_VA
attr PV_1_KSEM obj-h40092-type INT16_VA
attr PV_1_KSEM obj-h40093-reading M_AC_VA_A
attr PV_1_KSEM obj-h40093-type INT16_VA
attr PV_1_KSEM obj-h40094-reading M_AC_VA_B
attr PV_1_KSEM obj-h40094-type INT16_VA
attr PV_1_KSEM obj-h40095-reading M_AC_VA_C
attr PV_1_KSEM obj-h40095-type INT16_VA
attr PV_1_KSEM obj-h40096-reading M_AC_VA_SF
attr PV_1_KSEM obj-h40096-type INT16
attr PV_1_KSEM obj-h40097-reading M_AC_VAR
attr PV_1_KSEM obj-h40097-type INT16_VAR
attr PV_1_KSEM obj-h40098-reading M_AC_VAR_A
attr PV_1_KSEM obj-h40098-type INT16_VAR
attr PV_1_KSEM obj-h40099-reading M_AC_VAR_B
attr PV_1_KSEM obj-h40099-type INT16_VAR
attr PV_1_KSEM obj-h40100-reading M_AC_VAR_C
attr PV_1_KSEM obj-h40100-type INT16_VAR
attr PV_1_KSEM obj-h40101-reading M_AC_VAR_SF
attr PV_1_KSEM obj-h40101-type INT16
attr PV_1_KSEM obj-h40102-reading M_AC_PF
attr PV_1_KSEM obj-h40102-type INT16_PF
attr PV_1_KSEM obj-h40103-reading M_AC_PF_A
attr PV_1_KSEM obj-h40103-type INT16_PF
attr PV_1_KSEM obj-h40104-reading M_AC_PF_B
attr PV_1_KSEM obj-h40104-type INT16_PF
attr PV_1_KSEM obj-h40105-reading M_AC_PF_C
attr PV_1_KSEM obj-h40105-type INT16_PF
attr PV_1_KSEM obj-h40106-reading M_AC_PF_SF
attr PV_1_KSEM obj-h40106-type INT16
attr PV_1_KSEM obj-h40108-reading M_Exported
attr PV_1_KSEM obj-h40108-type UINT32
attr PV_1_KSEM obj-h40110-reading M_Exported_A
attr PV_1_KSEM obj-h40110-type UINT32
attr PV_1_KSEM obj-h40112-reading M_Exported_B
attr PV_1_KSEM obj-h40112-type UINT32
attr PV_1_KSEM obj-h40114-reading M_Exported_C
attr PV_1_KSEM obj-h40114-type UINT32
attr PV_1_KSEM obj-h40116-reading M_Imported
attr PV_1_KSEM obj-h40116-type UINT32
attr PV_1_KSEM obj-h40118-reading M_Imported_A
attr PV_1_KSEM obj-h40118-type UINT32
attr PV_1_KSEM obj-h40120-reading M_Imported_B
attr PV_1_KSEM obj-h40120-type UINT32
attr PV_1_KSEM obj-h40122-reading M_Imported_C
attr PV_1_KSEM obj-h40122-type UINT32
attr PV_1_KSEM obj-h40125-reading M_Exported_VA
attr PV_1_KSEM obj-h40125-type UINT32
attr PV_1_KSEM obj-h40127-reading M_Exported_VA_A
attr PV_1_KSEM obj-h40127-type UINT32
attr PV_1_KSEM obj-h40129-reading M_Exported_VA_B
attr PV_1_KSEM obj-h40129-type UINT32
attr PV_1_KSEM obj-h40131-reading M_Exported_VA_C
attr PV_1_KSEM obj-h40131-type UINT32
attr PV_1_KSEM obj-h40133-reading M_Imported_VA
attr PV_1_KSEM obj-h40133-type UINT32
attr PV_1_KSEM obj-h40135-reading M_Imported_VA_A
attr PV_1_KSEM obj-h40135-type UINT32
attr PV_1_KSEM obj-h40137-reading M_Imported_VA_B
attr PV_1_KSEM obj-h40137-type UINT32
attr PV_1_KSEM obj-h40139-reading M_Imported_VA_C
attr PV_1_KSEM obj-h40139-type UINT32
attr PV_1_KSEM obj-h512-reading Active_energy+
attr PV_1_KSEM obj-h512-type UINT64
attr PV_1_KSEM obj-h516-reading Active_energy-
attr PV_1_KSEM obj-h516-type UINT64
attr PV_1_KSEM obj-h8192-reading ManufacturerID
attr PV_1_KSEM obj-h8192-type UINT16
attr PV_1_KSEM obj-h8193-reading ProductID
attr PV_1_KSEM obj-h8193-type UINT16
attr PV_1_KSEM obj-h8194-reading ProductVersion
attr PV_1_KSEM obj-h8194-type UINT16
attr PV_1_KSEM obj-h8195-reading FirmwareVersion
attr PV_1_KSEM obj-h8195-type UINT16
attr PV_1_KSEM obj-h8196-reading VendorName
attr PV_1_KSEM obj-h8196-type STR32
attr PV_1_KSEM obj-h8212-reading Productname
attr PV_1_KSEM obj-h8212-type STR32
attr PV_1_KSEM obj-h8228-reading SerialNumber
attr PV_1_KSEM obj-h8228-type STR32
attr PV_1_KSEM obj-h8244-reading MeasuringInterval
attr PV_1_KSEM obj-h8244-type UINT16
attr PV_1_KSEM room Strom->Photovoltaik
attr PV_1_KSEM sortby 140
attr PV_1_KSEM userReadings M_AC_Current:M_AC_Current_.* { ReadingsVal($NAME,"M_AC_Current_A",0) + ReadingsVal($NAME,"M_AC_Current_B",0) + ReadingsVal($NAME,"M_AC_Current_C",0) }
attr PV_1_KSEM verbose 0
</syntaxhighlight>


===BYD Speicher (mit HTTPMOD)===
  2 SpeicherEntladung Zeit
Diese Einbindung ist nicht zwingend notwendig.
    Zeitsteuerung für laden/entladen
Das Passwort wird mit KeyValue() (siehe oben) verwaltet.
      - WR_1_Speicher_1_ExternControl:SpeicherZeitEnde/SpeicherZeitStart      Die Start/Ende Zeiten müssen gesetzt werden, dies muss über weitere DOIF oder WeekdayTimer erfolgen.
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.
      - Sommer/Winter Umschaltung des MinSOC
Der HV Speicher wird über das HTTPMOD Modul angesprochen, ist jedoch noch nicht bis in die letzten Tiefen abfragbar.
      - smart_laden
Der Begriff "Array" bezeichnet einen Speicher mit mehreren Modulen, die mit dem Series_Battery_Counts angegeben werden.
      - laden_beendet
Eine Battery hat dabei ca. 1.28 KW
</pre>
- 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!


Implementiert sind derzeit:
==Wenn man mal etwas umbenennen möchte==
RunData
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.
InstallationConfig
===Allgemeine Hilfestellungen===
DeviceInformation
'''Es sollte immer vorher eine Datensicherung gemacht werden!'''<pre>
BatteryInformation
Den Device Namen ändert man am Besten mit einem "rename".
StatisticInformation
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.
</pre>
'''Bitte nicht in der fhem.cfg Änderungen vornehmen! Dort nur zur Kontrolle suchen.'''


userreading:
===Ein Device umbenennen===
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
<pre>
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.
</pre>
===Ein reading umbenennen===
<pre>
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.
</pre>


======KeyValue() speichern======
===DbLog aufräumen===
Die Funktion befindet sich in der 99_myUtils und kann auch direkt in der Commandline aufgerufen werden.
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.
<syntaxhighlight lang="Perl">
<syntaxhighlight lang="Perl">
Syntax für die Commandline im FHEM {KeyValue("[read|store]","PW_<device>_<key>","<password>")}
mysql -h 192.168.178.xxx --port 3306 --database fhem -u fhemuser -p
{KeyValue("store","PW_BYD_Status_installer","<passwort>")}
</syntaxhighlight>
{KeyValue("read","PW_BYD_Status_installer")}
====Alle Devices in der history anzeigen====
<syntaxhighlight lang="SQL">
## Alle Devices in der history Tabelle
SELECT DEVICE FROM history
GROUP BY DEVICE;
</syntaxhighlight>
</syntaxhighlight>


====RAW Definition BYD_Status====
====Alle readings eines Devices anzeigen====
<syntaxhighlight lang="Perl">
Mit einem SELECT kann man alle bisher aufgetretenen readings eines Devices über einen definierten Zeitraum anzeigen lassen.
defmod PV_1_Speicher_1 HTTPMOD http://%IP-PV_1_Speicher_1%/asp/BatteryInformation.asp 0
Der Zeitraum ist mit "1 DAY" definiert, kann aber auch auf z.B. "1 MONTH" oder "2 MONTH" gesetzt werden.
attr PV_1_Speicher_1 DbLogExclude .*
<syntaxhighlight lang="SQL">
attr PV_1_Speicher_1 DbLogInclude BatteryInformation_TotalVoltage,BatteryInformation_SOC,BatteryInformation_SOC,Statistic_GeneralInformation_Total.*
SET @device = 'WR_1';
attr PV_1_Speicher_1 authRetries 1
SELECT t1.TIMESTAMP,t1.DEVICE,t1.READING,t1.VALUE
attr PV_1_Speicher_1 comment Version 2021.02.03 15:40
  FROM history t1
attr PV_1_Speicher_1 dontRequeueAfterAuth 0
  INNER JOIN
attr PV_1_Speicher_1 enableControlSet 0
  (SELECT max(TIMESTAMP) AS TIMESTAMP,DEVICE,READING
attr PV_1_Speicher_1 enableCookies 1
      FROM history
attr PV_1_Speicher_1 event-on-change-reading auth_.*,Battery.*_.*,Device.*_.*,Installation.*_.*,Array_.*,Statistic_GeneralInformation_Total.*
      WHERE DEVICE    = @device AND
attr PV_1_Speicher_1 event-on-update-reading Statistic_SpecificInformation_05_EndTime
            TIMESTAMP > NOW() - INTERVAL 1 DAY
attr PV_1_Speicher_1 get01-101Name Array_Series_Battery_CellVol_02
      GROUP BY READING) x
attr PV_1_Speicher_1 get01-106Name Array_Series_Battery_CellVol_03
  ON x.TIMESTAMP = t1.TIMESTAMP AND
attr PV_1_Speicher_1 get01-111Name Array_Series_Battery_CellVol_04
    x.DEVICE    = t1.DEVICE    AND
attr PV_1_Speicher_1 get01-116Name Array_Series_Battery_CellVol_05
    x.READING  = t1.READING;
attr PV_1_Speicher_1 get01-11Name Array_Main_PackVoltage
attr PV_1_Speicher_1 get01-121Name Array_Series_Battery_CellVol_06
attr PV_1_Speicher_1 get01-126Name Array_Series_Battery_CellVol_07
attr PV_1_Speicher_1 get01-131Name Array_Series_Battery_CellVol_08
attr PV_1_Speicher_1 get01-136Name Array_Series_Battery_CellVol_09
attr PV_1_Speicher_1 get01-141Name Array_Series_Battery_CellVol_10
attr PV_1_Speicher_1 get01-146Name Array_Series_Battery_CellVol_11
attr PV_1_Speicher_1 get01-151Name Array_Series_Battery_CellVol_12
attr PV_1_Speicher_1 get01-156Name Array_Series_Battery_CellVol_13
attr PV_1_Speicher_1 get01-161Name Array_Series_Battery_CellVol_14
attr PV_1_Speicher_1 get01-166Name Array_Series_Battery_CellVol_15
attr PV_1_Speicher_1 get01-16Name Array_Main_Current
attr PV_1_Speicher_1 get01-171Name Array_Series_Battery_CellVol_16
attr PV_1_Speicher_1 get01-176Name Array_Series_Battery_CellVolMax
attr PV_1_Speicher_1 get01-181Name Array_Series_Battery_CellVolMin
attr PV_1_Speicher_1 get01-186Name Array_Series_Battery_CellTemp_1
attr PV_1_Speicher_1 get01-191Name Array_Series_Battery_CellTemp_2
attr PV_1_Speicher_1 get01-196Name Array_Series_Battery_CellTemp_3
attr PV_1_Speicher_1 get01-201Name Array_Series_Battery_CellTemp_4
attr PV_1_Speicher_1 get01-22Name Array_Main_SOC
attr PV_1_Speicher_1 get01-26Name Array_Main_SysTemp
attr PV_1_Speicher_1 get01-31Name Array_Main_MaxCellVol
attr PV_1_Speicher_1 get01-36Name Array_Main_MinCellVol
attr PV_1_Speicher_1 get01-41Name Array_Main_MaxCellTemp
attr PV_1_Speicher_1 get01-46Name Array_Main_MinCellTemp
attr PV_1_Speicher_1 get01-53Name Array_Main_MaxVolPos
attr PV_1_Speicher_1 get01-56Name Array_Series_Battery_SerialNumber
attr PV_1_Speicher_1 get01-58Name Array_Main_MinVolPos
attr PV_1_Speicher_1 get01-5Name Array_Main_ArrayNum
attr PV_1_Speicher_1 get01-63Name Array_Main_MaxTempPos
attr PV_1_Speicher_1 get01-68Name Array_Main_MinTempPos
attr PV_1_Speicher_1 get01-6Name Array_Main_ArrayVoltage
attr PV_1_Speicher_1 get01-73Name Array_Main_Power
attr PV_1_Speicher_1 get01-80Name Array_Series_Battery
attr PV_1_Speicher_1 get01-84Name Array_Series_Battery_SerialNumber
attr PV_1_Speicher_1 get01-86Name Array_Series_Battery_BattVol
attr PV_1_Speicher_1 get01-91Name Array_Series_Battery_CellVolDiff
attr PV_1_Speicher_1 get01-96Name Array_Series_Battery_CellVol_01
attr PV_1_Speicher_1 get01MaxAge 900
attr PV_1_Speicher_1 get01MaxAgeReplacementMode delete
attr PV_1_Speicher_1 get01Name RunData
attr PV_1_Speicher_1 get01RegOpt g
attr PV_1_Speicher_1 get01Regex value=([+|-]{0,1}\d+.\d+)>|value=(\d+.\d+)%>|value=(\d)>|value=(.*-\d+\s+.*\d)>|selected="selected">(\d)<
attr PV_1_Speicher_1 get01URL http://%IP-PV_1_Speicher_1%/asp/RunData.asp
attr PV_1_Speicher_1 get02-101Name Statistic_SpecificInformation_03_StartTime
attr PV_1_Speicher_1 get02-105Name Statistic_SpecificInformation_03_EndTime
attr PV_1_Speicher_1 get02-10Name Statistic_SpecificInformation_02_Type
attr PV_1_Speicher_1 get02-113Name Statistic_SpecificInformation_04_Type
attr PV_1_Speicher_1 get02-117Name Statistic_SpecificInformation_04_Ah
attr PV_1_Speicher_1 get02-11Name Statistic_SpecificInformation_02_Ah
attr PV_1_Speicher_1 get02-121Name Statistic_SpecificInformation_04_KWh
attr PV_1_Speicher_1 get02-125Name Statistic_SpecificInformation_04_EnvTemp
attr PV_1_Speicher_1 get02-129Name Statistic_SpecificInformation_04_BatTemp
attr PV_1_Speicher_1 get02-12Name Statistic_GeneralInformation_Total_Cycle_Counts
attr PV_1_Speicher_1 get02-133Name Statistic_SpecificInformation_04_StartTime
attr PV_1_Speicher_1 get02-137Name Statistic_SpecificInformation_04_EndTime
attr PV_1_Speicher_1 get02-13Name Statistic_SpecificInformation_02_EnvTemp
attr PV_1_Speicher_1 get02-145Name Statistic_SpecificInformation_05_Type
attr PV_1_Speicher_1 get02-149Name Statistic_SpecificInformation_05_Ah
attr PV_1_Speicher_1 get02-14Name Statistic_SpecificInformation_02_BatTemp
attr PV_1_Speicher_1 get02-153Name Statistic_SpecificInformation_05_KWh
attr PV_1_Speicher_1 get02-157Name Statistic_SpecificInformation_05_EnvTemp
attr PV_1_Speicher_1 get02-15Name Statistic_SpecificInformation_02_StartTime
attr PV_1_Speicher_1 get02-161Name Statistic_SpecificInformation_05_BatTemp
attr PV_1_Speicher_1 get02-165Name Statistic_SpecificInformation_05_StartTime
attr PV_1_Speicher_1 get02-169Name Statistic_SpecificInformation_05_EndTime
attr PV_1_Speicher_1 get02-16Name Statistic_SpecificInformation_02_EndTime
attr PV_1_Speicher_1 get02-17Name Statistic_SpecificInformation_01_Type
attr PV_1_Speicher_1 get02-18Name Statistic_SpecificInformation_03_Type
attr PV_1_Speicher_1 get02-19Name Statistic_SpecificInformation_03_Ah
attr PV_1_Speicher_1 get02-20Name Statistic_SpecificInformation_03_KWh
attr PV_1_Speicher_1 get02-21Name Statistic_SpecificInformation_01_Ah
attr PV_1_Speicher_1 get02-22Name Statistic_SpecificInformation_03_BatTemp
attr PV_1_Speicher_1 get02-23Name Statistic_SpecificInformation_03_StartTime
attr PV_1_Speicher_1 get02-24Name Statistic_SpecificInformation_03_EndTime
attr PV_1_Speicher_1 get02-25Name Statistic_SpecificInformation_01_KWh
attr PV_1_Speicher_1 get02-26Name Statistic_SpecificInformation_04_Type
attr PV_1_Speicher_1 get02-27Name Statistic_SpecificInformation_04_Ah
attr PV_1_Speicher_1 get02-28Name Statistic_SpecificInformation_04_KWh
attr PV_1_Speicher_1 get02-29Name Statistic_SpecificInformation_01_EnvTemp
attr PV_1_Speicher_1 get02-2Name Statistic_GeneralInformation_Total_Charge_Energy
attr PV_1_Speicher_1 get02-30Name Statistic_SpecificInformation_04_BatTemp
attr PV_1_Speicher_1 get02-31Name Statistic_SpecificInformation_04_StartTime
attr PV_1_Speicher_1 get02-32Name Statistic_SpecificInformation_04_EndTime
attr PV_1_Speicher_1 get02-33Name Statistic_SpecificInformation_01_BatTemp
attr PV_1_Speicher_1 get02-34Name Statistic_SpecificInformation_05_Type
attr PV_1_Speicher_1 get02-35Name Statistic_SpecificInformation_05_Ah
attr PV_1_Speicher_1 get02-36Name Statistic_SpecificInformation_05_KWh
attr PV_1_Speicher_1 get02-37Name Statistic_SpecificInformation_01_StartTime
attr PV_1_Speicher_1 get02-38Name Statistic_SpecificInformation_05_BatTemp
attr PV_1_Speicher_1 get02-39Name Statistic_SpecificInformation_05_StartTime
attr PV_1_Speicher_1 get02-3Name Statistic_SpecificInformation_01_Ah
attr PV_1_Speicher_1 get02-40Name Statistic_SpecificInformation_05_EndTime
attr PV_1_Speicher_1 get02-41Name Statistic_SpecificInformation_01_EndTime
attr PV_1_Speicher_1 get02-49Name Statistic_SpecificInformation_02_Type
attr PV_1_Speicher_1 get02-4Name Statistic_SpecificInformation_01_KWh
attr PV_1_Speicher_1 get02-53Name Statistic_SpecificInformation_02_Ah
attr PV_1_Speicher_1 get02-57Name Statistic_SpecificInformation_02_KWh
attr PV_1_Speicher_1 get02-5Name Statistic_SpecificInformation_01_EnvTemp
attr PV_1_Speicher_1 get02-61Name Statistic_SpecificInformation_02_EnvTemp
attr PV_1_Speicher_1 get02-65Name Statistic_SpecificInformation_02_BatTemp
attr PV_1_Speicher_1 get02-69Name Statistic_SpecificInformation_02_StartTime
attr PV_1_Speicher_1 get02-6Name Statistic_SpecificInformation_01_BatTemp
attr PV_1_Speicher_1 get02-73Name Statistic_SpecificInformation_02_EndTime
attr PV_1_Speicher_1 get02-7Name Statistic_GeneralInformation_Total_Discharge_Energy
attr PV_1_Speicher_1 get02-81Name Statistic_SpecificInformation_03_Type
attr PV_1_Speicher_1 get02-85Name Statistic_SpecificInformation_03_Ah
attr PV_1_Speicher_1 get02-89Name Statistic_SpecificInformation_03_KWh
attr PV_1_Speicher_1 get02-8Name Statistic_SpecificInformation_01_EndTime
attr PV_1_Speicher_1 get02-93Name Statistic_SpecificInformation_03_EnvTemp
attr PV_1_Speicher_1 get02-97Name Statistic_SpecificInformation_03_BatTemp
attr PV_1_Speicher_1 get02MaxAge 900
attr PV_1_Speicher_1 get02MaxAgeReplacementMode delete
attr PV_1_Speicher_1 get02Name StatisticInformation
attr PV_1_Speicher_1 get02RegOpt g
attr PV_1_Speicher_1 get02Regex <td class="text_l">(.*)<\/td>\n|Charge Energy:<\/td>\n.*>(\d+.\d+)|Discharge Energy:<\/td>\n.*>(\d+.\d+)|Cycle Counts:<\/td>\n.*>(\d+)
attr PV_1_Speicher_1 get02URL http://%IP-PV_1_Speicher_1%/asp/StatisticInformation.asp
attr PV_1_Speicher_1 get03-10Name DeviceInformation_Machine_Version
attr PV_1_Speicher_1 get03-10OExpr {$val =~ s/\n//g;; $val}
attr PV_1_Speicher_1 get03-15Name DeviceInformation_Board_SN
attr PV_1_Speicher_1 get03-1Name DeviceInformation_Machine_SN
attr PV_1_Speicher_1 get03-20Name DeviceInformation_Board_Factory_time
attr PV_1_Speicher_1 get03-8Name DeviceInformation_Machine_Factory_time
attr PV_1_Speicher_1 get03Name DeviceInformation
attr PV_1_Speicher_1 get03RegOpt g
attr PV_1_Speicher_1 get03Regex >(\d{9}-\d{5})<|Version:<\/td>\n<td>(V\d.\d{3}\n [A-Z])<|>SN:<\/td>\n.*>([\d|\w]{18})<|(\d{4}-\d+-\d+ \d+:\d+:\d+)
attr PV_1_Speicher_1 get03URL http://%IP-PV_1_Speicher_1%/asp/DeviceInformation.asp
attr PV_1_Speicher_1 get04-13Name BatteryInformation_SOC
attr PV_1_Speicher_1 get04-17Name BatteryInformation_SysTemp
attr PV_1_Speicher_1 get04-1Name BatteryInformation_TotalVoltage
attr PV_1_Speicher_1 get04-21Name BatteryInformation_MaxCellVol
attr PV_1_Speicher_1 get04-25Name BatteryInformation_MinCellVol
attr PV_1_Speicher_1 get04-29Name BatteryInformation_MaxCellTemp
attr PV_1_Speicher_1 get04-33Name BatteryInformation_MinCellTemp
attr PV_1_Speicher_1 get04-37Name BatteryInformation_Power
attr PV_1_Speicher_1 get04-42Name BatteryInformation_System_state
attr PV_1_Speicher_1 get04-47Name BatteryInformation_Date_and_Time
attr PV_1_Speicher_1 get04-52Name BatteryInformation_Alarm_state
attr PV_1_Speicher_1 get04-5Name BatteryInformation_PackVoltage
attr PV_1_Speicher_1 get04-9Name BatteryInformation_Current
attr PV_1_Speicher_1 get04DeleteIfUnmatched 1
attr PV_1_Speicher_1 get04MaxAge 900
attr PV_1_Speicher_1 get04MaxAgeReplacementMode delete
attr PV_1_Speicher_1 get04Name BatteryInformation
attr PV_1_Speicher_1 get04RegOpt g
attr PV_1_Speicher_1 get04Regex value=([+|-]{0,1}\d+.\d+)[%]{0,1}>|value=([A-Z]+)>|value="(\d{4}-\d+-\d+ \d+:\d+:\d+)"|Alarm state:<\/h3><\/td>\n<td>.*">(\w+\W)<\/font>
attr PV_1_Speicher_1 get04URL http://%IP-PV_1_Speicher_1%/asp/Home.asp
attr PV_1_Speicher_1 get05-1Name InstallationConfig_Array_Counts
attr PV_1_Speicher_1 get05-5Name InstallationConfig_Series_Battery_Counts
attr PV_1_Speicher_1 get05-9Name InstallationConfig_Installation_Time
attr PV_1_Speicher_1 get05Name InstallationConfig
attr PV_1_Speicher_1 get05RegOpt g
attr PV_1_Speicher_1 get05Regex >Array Counts :<\/td>\n<td class="text_l">(\d){1}<|>Series Battery Counts :<\/td>\n<td class="text_l">(\d){1}<|>Installation Time :<\/td>\n<td class="text_l">(\d{4}-\d+-\d+ \d+:\d+:\d+)<
attr PV_1_Speicher_1 get05URL http://%IP-PV_1_Speicher_1%/asp/UserInfo.asp
attr PV_1_Speicher_1 get10Data ArrayNum=1&SeriesBatteryNum=4
attr PV_1_Speicher_1 get10Header01 Authorization: Digest username="installer", realm="%auth_realm%", nonce="%auth_nonce%", uri="/asp/RunData.asp", algorithm="MD5", response="%auth_response%", opaque="%auth_opaque%", qop="auth", nc="00000001", cnonce="d789ea5b7e9a2377"
attr PV_1_Speicher_1 get10Header02 Referer: http://%IP-PV_1_Speicher_1%/asp/RunData.asp
attr PV_1_Speicher_1 get10Header03 Content-Type: application/x-www-form-urlencoded
attr PV_1_Speicher_1 get10Header04 Accept: text/html,application/xhtml+xml,application/xml
attr PV_1_Speicher_1 get10Name Test_Array
attr PV_1_Speicher_1 get10URL http://%IP-PV_1_Speicher_1%/goform/SetRunData
attr PV_1_Speicher_1 getHeader01 Authorization: Digest username="installer", realm="%auth_realm%", nonce="%auth_nonce%", uri="/asp/RunData.asp", algorithm="MD5", response="%auth_response%", opaque="%auth_opaque%", qop="auth", nc="00000001", cnonce="d789ea5b7e9a2377"
attr PV_1_Speicher_1 group PV Eigenverbrauch
attr PV_1_Speicher_1 handleRedirects 1
attr PV_1_Speicher_1 httpVersion 1.1
attr PV_1_Speicher_1 icon measure_battery_100
attr PV_1_Speicher_1 reAuthRegex Unauthorized
attr PV_1_Speicher_1 reading01Name auth_qop
attr PV_1_Speicher_1 reading01Regex qop="(.*)", nonce
attr PV_1_Speicher_1 reading02Name auth_nonce
attr PV_1_Speicher_1 reading02Regex nonce="(.*)", opaque
attr PV_1_Speicher_1 reading03Name auth_opaque
attr PV_1_Speicher_1 reading03Regex opaque="(.*)",algorithm
attr PV_1_Speicher_1 reading04Name auth_realm
attr PV_1_Speicher_1 reading04Regex realm="(.*)", domain
attr PV_1_Speicher_1 replacement01Mode expression
attr PV_1_Speicher_1 replacement01Regex %IP-PV_1_Speicher_1%
attr PV_1_Speicher_1 replacement01Value ReadingsVal("PV_1_config","IP-PV_1_Speicher_1","")
attr PV_1_Speicher_1 replacement03Mode reading
attr PV_1_Speicher_1 replacement03Regex %auth_realm%
attr PV_1_Speicher_1 replacement03Value auth_realm
attr PV_1_Speicher_1 replacement04Mode reading
attr PV_1_Speicher_1 replacement04Regex %auth_nonce%
attr PV_1_Speicher_1 replacement04Value auth_nonce
attr PV_1_Speicher_1 replacement05Mode reading
attr PV_1_Speicher_1 replacement05Regex %auth_opaque%
attr PV_1_Speicher_1 replacement05Value auth_opaque
attr PV_1_Speicher_1 replacement06Mode expression
attr PV_1_Speicher_1 replacement06Regex %auth_response%
attr PV_1_Speicher_1 replacement06Value {my $NAME="PV_1_Speicher_1";;my $pw=KeyValue("read","PW_BYD_Status_installer");; $pw =~ '"'.s/@/\\@/g.'"';; md5_hex(md5_hex("installer:".ReadingsVal($NAME,"auth_realm","").":".$pw).":".ReadingsVal($NAME,"auth_nonce","").":00000001:d789ea5b7e9a2377:".ReadingsVal($NAME,"auth_qop","").":".md5_hex("GET:/asp/RunData.asp"));;}
attr PV_1_Speicher_1 room Strom->Photovoltaik
attr PV_1_Speicher_1 showBody 0
attr PV_1_Speicher_1 showError 1
attr PV_1_Speicher_1 sid01Header Authorization: Digest username="installer", realm="%auth_realm%", nonce="%auth_nonce%", uri="/asp/RunData.asp", algorithm="MD5", response="%auth_response%", opaque="%auth_opaque%", qop="auth", nc="00000001", cnonce="d789ea5b7e9a2377"
attr PV_1_Speicher_1 sid01ParseResponse 1
attr PV_1_Speicher_1 sid01URL http://%IP-PV_1_Speicher_1%/asp/RunData.asp
attr PV_1_Speicher_1 sid02Header Authorization: Digest username="installer", realm="%auth_realm%", nonce="%auth_nonce%", uri="/asp/RunData.asp", algorithm="MD5", response="%auth_response%", opaque="%auth_opaque%", qop="auth", nc="00000001", cnonce="d789ea5b7e9a2377"
attr PV_1_Speicher_1 sid02ParseResponse 1
attr PV_1_Speicher_1 sid02URL http://%IP-PV_1_Speicher_1%/asp/RunData.asp
attr PV_1_Speicher_1 sortby 121
attr PV_1_Speicher_1 stateFormat {sprintf("Total_Charge_Energy: %.0f kWh<br>Total_Efficiency: %.1f %% Battery_EM_State: %s", ReadingsVal($name,"Statistic_GeneralInformation_Total_Charge_Energy","0"), ReadingsVal($name,"Statistic_GeneralInformation_Total_Efficiency","0"), ReadingsVal("PV_1_API","Battery_EM_State",""))}
attr PV_1_Speicher_1 userReadings Statistic_SpecificInformation_00_Date:Statistic_SpecificInformation_05_EndTime.* { CommandDeleteReading(undef, $NAME." .*-.*");;;; localtime()},\
\
InstallationConfig_Array_Power:InstallationConfig_Series_Battery_Counts.* {1.28 * ReadingsVal($NAME,"InstallationConfig_Series_Battery_Counts",0)},\
\
Statistic_GeneralInformation_Total_Difference_Charge_Energy:Statistic_GeneralInformation_Total_Charge_Energy.*  {ReadingsVal($NAME,"Statistic_GeneralInformation_Total_Charge_Energy",0) - ReadingsVal($NAME,"Statistic_GeneralInformation_Total_Discharge_Energy",0)},\
\
Statistic_GeneralInformation_Total_Efficiency:Statistic_GeneralInformation_Total_Charge_Energy.*  {round(((ReadingsVal($NAME,"Statistic_GeneralInformation_Total_Discharge_Energy",0)+((ReadingsVal("PV_1","Act_state_of_charge",0)/100)*11)) / ReadingsVal($NAME,"Statistic_GeneralInformation_Total_Charge_Energy",0))*100 , 2)}
attr PV_1_Speicher_1 verbose 0
</syntaxhighlight>
</syntaxhighlight>


== Device Übersicht mit Hilfen zur Orientierung ==
====Einträge eines DEVICE einem neuen DEVICE zuordnen====
<pre>
In diesem Beispiel würde das alte DEVICE PV_1 dem neuen DEVICE WR_1 zugeordnet werden.
Device            über Device      Hardware   Protokoll Netzwerk              Informationen
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.
<syntaxhighlight lang="SQL">
UPDATE history
   SET
    TIMESTAMP = TIMESTAMP,
    DEVICE    = 'PV_1'
  WHERE
        DEVICE    = 'WR_1'
    AND TIMESTAMP < '2021-01-24 17:25:15';
</syntaxhighlight>


PV_1_KSEM         PV_1            KSEM      MODBUS   LAN                    Messwerte vom Netzanschlusspunkt
====Ein altes READING einem neuen READING Namen zuordnen====
                  PV_1                        rs485     4 Draht zum WR        Notwendig, wenn ein Speicher am Plenticore betrieben wird
Sehr wichtig ist '''"TIMESTAMP = TIMESTAMP"''', da hierdurch der alte TIMESTAMP erhalten bleibt.
<syntaxhighlight lang="SQL">
UPDATE history
  SET
    TIMESTAMP = TIMESTAMP,
    READING  = 'Total_DC_PV_Energy_sumOfAllPVInputs'
  WHERE
         DEVICE   = 'WR_1'
    AND READING  = 'Total_DC_PV_Energy_(sumOfAllPVInputs)'
     AND TIMESTAMP < '2021-03-23 17:25:15';
</syntaxhighlight>


PV_1                              Plenticore MODBUS    LAN                    Messwerte und berechnete Werte, teilweise Speicher Informationen
====Alles auf einmal====
PV_1_API                                      HTTPMOD  LAN                    Statistiken, Speichersteuerung und Informationen
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.


PV_1_Speicher_1                    BYD HV    HTTPMOD  LAN/WLAN              Speicher Details, auch über einzelne Zellen
===Grafiken korrigieren===
                  PV_1                        rs485    4 Draht zum WR        Verwendet von Plenticore zur Steuerung des Speichers
====Grafana====
In Grafana sind die SQL Abfragen ebenfalls zu korrigieren


PV_2                              Plenticore MODBUS    LAN                    Messwerte und berechnete Werte, teilweise Speicher Informationen
===Fhem Log===
PV_2_API                                      HTTPMOD  LAN                    Statistiken, Speichersteuerung und Informationen
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.


* [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>


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.


FHEM Steuerung                                MODBUS    LAN                    Laufende Informationen im Minuten Takt
====RAW Definition LogDBRep_Statistic_EnergyFeedInGrid_Year_diff_Week====
                                              HTTPMOD  LAN                    Abfragen und Steuerung einzelner Devices
Dieser Wert ist die wöchentliche Einspeisung ins Netz.
 
Gestartet über DB_Service_Schedule am ersten Tag der Folgewoche.
PV_Schedule                                  DOIF                            Startet regelmäßige Aktionen
<syntaxhighlight lang="Perl">defmod LogDBRep_Statistic_EnergyFeedInGrid_Year_diff_Week DbRep LogDB
 
attr LogDBRep_Statistic_EnergyFeedInGrid_Year_diff_Week DbLogExclude .*
  1 Stündlich
attr LogDBRep_Statistic_EnergyFeedInGrid_Year_diff_Week aggregation week
  1.1 PV_1_API 20_Statistic_EnergyFlow                                          Statistiken vom Plenticore abholen
attr LogDBRep_Statistic_EnergyFeedInGrid_Year_diff_Week comment Version 2020.10.21 11:14
 
attr LogDBRep_Statistic_EnergyFeedInGrid_Year_diff_Week device PV_1_API
  2 Stündlich von 07:00 bis 20:00
attr LogDBRep_Statistic_EnergyFeedInGrid_Year_diff_Week diffAccept 15000
  2.1 PV_1_config module_1_covered                                              Schnee auf den Modulen
attr LogDBRep_Statistic_EnergyFeedInGrid_Year_diff_Week reading Statistic_EnergyFeedInGrid_Year
  2.2 Solar_forecast() für fc0                                                  Aktualisieren der fc0 Prognose
attr LogDBRep_Statistic_EnergyFeedInGrid_Year_diff_Week readingNameMap Statistic_EnergyFeedInGrid_Week
 
attr LogDBRep_Statistic_EnergyFeedInGrid_Year_diff_Week room Strom->Energie,System
  3 zweimal am Tag
attr LogDBRep_Statistic_EnergyFeedInGrid_Year_diff_Week timestamp_begin current_year_begin
  3.1 Solar_forecast() für fc1                                                  Aktualisieren der fc1 Prognose
attr LogDBRep_Statistic_EnergyFeedInGrid_Year_diff_Week timestamp_end previous_week_end
</syntaxhighlight>


  4 alle 5 Minuten
====RAW Definition LogDBRep_Statistic_EnergyHomePvSum_Year_diff_Week====
  4.1 PV_1_API 04_auth_me                                                      Aktualisieren der Bilanz (es wird ein Event erzeugt)
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.
<syntaxhighlight lang="Perl">
defmod LogDBRep_Statistic_EnergyHomePvSum_Year_diff_Week DbRep LogDB
attr LogDBRep_Statistic_EnergyHomePvSum_Year_diff_Week DbLogExclude .*
attr LogDBRep_Statistic_EnergyHomePvSum_Year_diff_Week aggregation week
attr LogDBRep_Statistic_EnergyHomePvSum_Year_diff_Week allowDeletion 0
attr LogDBRep_Statistic_EnergyHomePvSum_Year_diff_Week comment Version 2020.10.23 15:00\
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.
attr LogDBRep_Statistic_EnergyHomePvSum_Year_diff_Week device PV_1_API
attr LogDBRep_Statistic_EnergyHomePvSum_Year_diff_Week diffAccept 20000
attr LogDBRep_Statistic_EnergyHomePvSum_Year_diff_Week reading Statistic_EnergyHomePvSum_Year
attr LogDBRep_Statistic_EnergyHomePvSum_Year_diff_Week readingNameMap Statistic_EnergyHomePvSum_Week
attr LogDBRep_Statistic_EnergyHomePvSum_Year_diff_Week room Strom->Energie,System
attr LogDBRep_Statistic_EnergyHomePvSum_Year_diff_Week timestamp_begin current_year_begin
attr LogDBRep_Statistic_EnergyHomePvSum_Year_diff_Week timestamp_end previous_week_end
</syntaxhighlight>


====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.
<syntaxhighlight lang="Perl">
defmod LogDBRep_Statistic_EnergyHomePvSum_Year_max_Month DbRep LogDB
attr LogDBRep_Statistic_EnergyHomePvSum_Year_max_Month DbLogExclude .*
attr LogDBRep_Statistic_EnergyHomePvSum_Year_max_Month aggregation month
attr LogDBRep_Statistic_EnergyHomePvSum_Year_max_Month allowDeletion 1
attr LogDBRep_Statistic_EnergyHomePvSum_Year_max_Month comment Version 2020.10.23 15:00\
Dieser Wert ist der gesamte Verbrauch aus der PV Anlage inklusive Batterie im Monat.\
Gestartet über PV_Schedule am ersten des Monats\
Benötigt für SVG_LogDB_PV_Bilanz.
attr LogDBRep_Statistic_EnergyHomePvSum_Year_max_Month device PV_1_API
attr LogDBRep_Statistic_EnergyHomePvSum_Year_max_Month reading Statistic_EnergyHomePvSum_Month
attr LogDBRep_Statistic_EnergyHomePvSum_Year_max_Month room Strom->Energie,System
attr LogDBRep_Statistic_EnergyHomePvSum_Year_max_Month timestamp_begin current_year_begin
attr LogDBRep_Statistic_EnergyHomePvSum_Year_max_Month timestamp_end previous_month_end
</syntaxhighlight>


PV_*_config                                  DUMMY                            Konfiguration für Strings,Ausrichtung,Nennleistung,IP-Adressen,Forecast
====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.\
PV_1_Speicher_1_ExternControl                DOIF                            Externe Speichersteuerung
Benötigt für SVG_LogDB_PV_Bilanz.
 
<syntaxhighlight lang="Perl">
    1 Stündlich
defmod LogDBRep_Statistic_Yield_Year_diff_Week DbRep LogDB
 
attr LogDBRep_Statistic_Yield_Year_diff_Week DbLogExclude .*
    1.1 PV_1_API 21_Battery_Information                                        Allgemeine informationen
attr LogDBRep_Statistic_Yield_Year_diff_Week aggregation week
                    Battery_Info_SoC,
attr LogDBRep_Statistic_Yield_Year_diff_Week allowDeletion 0
                    Battery_Info_WorkCapacity
attr LogDBRep_Statistic_Yield_Year_diff_Week comment Version 2020.10.23 15:00\
    1.2 PV_1_API 22_Battery_InternControl                                      Speicher Information der Internen Steuerung
Dieser Wert gibt den gesamten Ertrag der PV Anlage pro Woche an.\
                    Battery_InternControl_MinSoc,
Gestartet über DB_Service_Schedule am ersten Tag der Folgewoche.\
                    Battery_InternControl_MinHomeConsumption
Benötigt für  SVG_LogDB_PV_Bilanz.
    1.3 PV_1_API 25_Battery_EM_State                                          Speicher Status z.B. "Normal"
attr LogDBRep_Statistic_Yield_Year_diff_Week device PV_1_API
attr LogDBRep_Statistic_Yield_Year_diff_Week diffAccept 20000
attr LogDBRep_Statistic_Yield_Year_diff_Week reading Statistic_Yield_Year
attr LogDBRep_Statistic_Yield_Year_diff_Week readingNameMap Statistic_Yield_Week
attr LogDBRep_Statistic_Yield_Year_diff_Week room Strom->Energie,System
attr LogDBRep_Statistic_Yield_Year_diff_Week timestamp_begin current_year_begin
attr LogDBRep_Statistic_Yield_Year_diff_Week timestamp_end previous_week_end
</syntaxhighlight>


    2 Unterschreitung des MinSOC im Winter
====RAW Definition LogDBRep_Statistic_Yield_Month_max_Month====
    2.1 smart_laden                                                            PV Überschuss wird in Batterie geladen. Keine Entladung
Dieser Wert gibt den gesamten Ertrag der PV Anlage im Monat an.
    2.2 PV_1_Speicher_1_ExternTrigger ExternTrigger gesperrt                  Batterie ExternTrigger, Entlademodus gesperrt . Die Zeit Steuerung wird verriegelt.
Gestartet über DB_Service_Schedule am ersten Tag der Folgewoche.
 
Momentan noch in keinem SVC verwendet.
    3 Freigabe zur Entladung im Winter
<syntaxhighlight lang="Perl">
    3.1 bei überschreiten von SOC 90%                                          Sobald der Speicher gut gefüllt ist oder
defmod LogDBRep_Statistic_Yield_Month_max_Month DbRep LogDB
    3.2 Bei Zeitsteuerung und guter Prognose mit SOC 40%                      bereits vorher, weil der Tarif teuer ist
attr LogDBRep_Statistic_Yield_Month_max_Month DbLogExclude .*
attr LogDBRep_Statistic_Yield_Month_max_Month aggregation month
attr LogDBRep_Statistic_Yield_Month_max_Month allowDeletion 0
attr LogDBRep_Statistic_Yield_Month_max_Month comment Version 2020.10.23 15:00\
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.
attr LogDBRep_Statistic_Yield_Month_max_Month device PV_1_API
attr LogDBRep_Statistic_Yield_Month_max_Month diffAccept 20000
attr LogDBRep_Statistic_Yield_Month_max_Month reading Statistic_Yield_Month
attr LogDBRep_Statistic_Yield_Month_max_Month room Strom->Energie,System
attr LogDBRep_Statistic_Yield_Month_max_Month timestamp_begin current_year_begin
attr LogDBRep_Statistic_Yield_Month_max_Month timestamp_end previous_month_end
</syntaxhighlight>


    4 Speicher freigabe bei Trigger oder Zeit Steuerung                        Dieses cmd_ löst die Abhängigkeiten von Zeit und Trigger auf
===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.


    5 Speicher sperren  bei Trigger oder Zeit Steuerung                        Dieses cmd_ löst die Abhängigkeiten von Zeit und Trigger auf
====RAW Definition LogDBRep_delete_Statistic_EnergyHomeBat_Day_max_Day====
 
Löschen aller Statistic_EnergyHomeBat_Day Werte, bis auf den maximal Wert des Tages.
    6 Wiederhole alle 180s die Kommandos der ExternControl Steuerung          Wenn keine Wiederholung erfolgt geht der Plenticore wieder auf die interne Steuerung
Der aktuelle Tag bleibt noch erhalten.
    6.1 PV_1_Speicher_1_ExternTrigger:SpeicherMiddayControlActive              wird durchlaufen, wenn der Forecast eine z.B. 70% Überschreitung erkannt hat und
Aufruf mit: maxValue deleteOther
            PV_1_Speicher_1_ExternTrigger:SpeicherMidday_MaxSOC              begrenzt dann morgens den MaxSOC
<syntaxhighlight lang="Perl">
            PV_1_Speicher_1_ExternTrigger:SpeicherMidday_MaxChargePowerAbs    und den MaxChargePowerAbs
defmod LogDBRep_delete_Statistic_EnergyHomeBat_Day_max_Day DbRep LogDB
    6.2 PV_1_Speicher_1_ExternTrigger:SpeicherMaxSOC                          Wenn morgens der Speicher zu voll war, wird der MaxSOC bis abends begrenzt
attr LogDBRep_delete_Statistic_EnergyHomeBat_Day_max_Day DbLogExclude .*
    6.3 PV_1:Solar_middayhigh_fc0_start <> PV_1:Solar_middayhigh_fc0_stop      Das Laden wird mit voller Leistung freigegeben
attr LogDBRep_delete_Statistic_EnergyHomeBat_Day_max_Day aggregation day
    6.4 nach Ablauf von PV_1:Solar_middayhigh_fc0_stop                        Die Midday Steuerung wird abgeschaltet, es wird normal weiter geladen, bis MaxSOC erreicht ist.
attr LogDBRep_delete_Statistic_EnergyHomeBat_Day_max_Day allowDeletion 1
    6.5 PV_1_Speicher_1_ExternTrigger:SpeicherMaxSOC                          Batterie MaxSOC halten, der Default ist 100%
attr LogDBRep_delete_Statistic_EnergyHomeBat_Day_max_Day comment Version 2020.10.30 18:30\
 
Löschen aller Statistic_EnergyHomeBat_Day Werte, bis auf den maximal Wert des Tages.\
    7 Initialisierung der externen Speichersteuerung
Der aktuelle Tag bleibt noch erhalten.\
    7.1 Ist morgens der Speicher zu voll                                      Wenn der Speicher morgens voller als 3x MinSOC ist wird MaxSOC gesetzt
Aufruf mit: maxValue deleteOther
    7.2 Wenn eine Überschreitung der 70% erwartet wird                        Aktivierung der Midday Steuerung
attr LogDBRep_delete_Statistic_EnergyHomeBat_Day_max_Day device PV_1_API
 
attr LogDBRep_delete_Statistic_EnergyHomeBat_Day_max_Day reading Statistic_EnergyHomeBat_Day
    8 Zurücksetzen der externen Speichersteuerung
attr LogDBRep_delete_Statistic_EnergyHomeBat_Day_max_Day room Strom->Energie,System
 
attr LogDBRep_delete_Statistic_EnergyHomeBat_Day_max_Day timestamp_begin previous_month_begin
    9 Umschaltung des MinSOC wenn zu wenig Leistung erwartet wird              Schaltet im Herbst/Winter den MinSOC auf 20%
attr LogDBRep_delete_Statistic_EnergyHomeBat_Day_max_Day timestamp_end current_day_end
 
</syntaxhighlight>
  10 Umschaltung des MinSoc wenn viel Leistung erwartet wir                  Setzt den MinSOC wieder im Frühling/Sommer auf 5%
 
  11 PV_1_Speicher_1 Status aktualisieren                                    Nur beim BYD HV, Abfrage der Speicher Detailinformationen. Kann einfach entfernt werden.


====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
<syntaxhighlight lang="Perl">
defmod LogDBRep_delete_Statistic_EnergyHomePvSum_Day_max_Day DbRep LogDB
attr LogDBRep_delete_Statistic_EnergyHomePvSum_Day_max_Day DbLogExclude .*
attr LogDBRep_delete_Statistic_EnergyHomePvSum_Day_max_Day aggregation day
attr LogDBRep_delete_Statistic_EnergyHomePvSum_Day_max_Day allowDeletion 1
attr LogDBRep_delete_Statistic_EnergyHomePvSum_Day_max_Day comment Version 2020.10.30 18:30\
Löschen aller Statistic_EnergyHomePvSum_Day Werte, bis auf den maximal Wert des Tages.\
Der aktuelle Tag bleibt noch erhalten.\
Aufruf mit: maxValue deleteOther
attr LogDBRep_delete_Statistic_EnergyHomePvSum_Day_max_Day device PV_1_API
attr LogDBRep_delete_Statistic_EnergyHomePvSum_Day_max_Day reading Statistic_EnergyHomePvSum_Day
attr LogDBRep_delete_Statistic_EnergyHomePvSum_Day_max_Day room Strom->Energie,System
attr LogDBRep_delete_Statistic_EnergyHomePvSum_Day_max_Day timestamp_begin previous_month_begin
attr LogDBRep_delete_Statistic_EnergyHomePvSum_Day_max_Day timestamp_end current_day_end
</syntaxhighlight>


PV_1_Speicher_1_ExternTrigger                DUMMY                            Konfiguration für die externe Speichersteuerung
====RAW Definition LogDBRep_delete_Statistic_TotalConsumption_Day_max_Day====
Löschen aller Statistic_TotalConsumption_Day Werte, bis auf den maximal Wert des Tages.\
Der aktuelle Tag bleibt noch erhalten.
<syntaxhighlight lang="Perl">
defmod LogDBRep_delete_Statistic_TotalConsumption_Day_max_Day DbRep LogDB
attr LogDBRep_delete_Statistic_TotalConsumption_Day_max_Day DbLogExclude .*
attr LogDBRep_delete_Statistic_TotalConsumption_Day_max_Day aggregation day
attr LogDBRep_delete_Statistic_TotalConsumption_Day_max_Day allowDeletion 1
attr LogDBRep_delete_Statistic_TotalConsumption_Day_max_Day comment Version 2020.10.30 18:30\
Löschen aller Statistic_TotalConsumption_Day Werte, bis auf den maximal Wert des Tages.\
Der aktuelle Tag bleibt noch erhalten.
attr LogDBRep_delete_Statistic_TotalConsumption_Day_max_Day device PV_1_API
attr LogDBRep_delete_Statistic_TotalConsumption_Day_max_Day reading Statistic_TotalConsumption_Day
attr LogDBRep_delete_Statistic_TotalConsumption_Day_max_Day room Strom->Energie,System
attr LogDBRep_delete_Statistic_TotalConsumption_Day_max_Day timestamp_begin previous_month_begin
attr LogDBRep_delete_Statistic_TotalConsumption_Day_max_Day timestamp_end current_day_end
</syntaxhighlight>


  ExternTrigger none                                                          Wird automatisch gesetzt und dient der Verriegelung im PV_1_Speicher_1_ExternControl
===Timing für die Datenbank Einträge===
                                                                              Zustände: frei/gesperrt/none
Über dieses Scheduling werden in der Datenbank zusätzliche Wochen- und Monatseinträge gesteuert.
  SpeicherCmdRepeatActive 1                                                  Trigger für PV_1_Speicher_1_ExternControl, aktiviert die Kommandowiederholung
====RAW Definition DB_Service_Schedule====
  SpeicherEntladung                                                          Steuert den Modus der externen Speichersteuerung
Hier werden zusätzlich Werte in der Datenbank erzeugt.
                                                                              Zustände:
<syntaxhighlight lang="Perl">
                                                                                Automatik - MinSOC Steuerung Sommer/Winter
ddefmod DB_Service_Schedule DOIF ## Monatlich Einträge\
                                                                                Zeit      - z.B. bei Tarifsteuerung. Zeiten werden über zusätzliche DOIF
([01:13] and ($mday==1))\
                                                                                            oder Weekdaytimer gesetzt
(set LogDBRep_Statistic_EnergyHomePvSum_Year_max_Month maxValue writeToDB)\
                                                                                Trigger  - Ein beliebiger Mechanismus z.B. DOIF steuert den Speicher
(set LogDBRep_Statistic_Yield_Month_max_Month maxValue writeToDB)\
 
(set LogDBRep_Statistic_previous_Month sqlCmd ckey:1)                      ## Bildet für verschiedene Devices die Monatsauswertung\
  SpeicherMaxSOC 100                                                          Wird im PV_1_Speicher_1_ExternControl cmd_7 berechnet, wenn der Speicher morgens zuviel Ladung hat
\
  SpeicherMaxSOC_fc1_Limit 30000                                              Hier einen Wert setzen, bei dem der Speicher über Nacht bis morgens gereicht hat.
## Wöchentliche Einträge\
                                                                              Damit wird dann der Übergang vom Winter zum Frühjahr erkannt.
DOELSEIF\
  SpeicherMiddayControlActive 0                                              Wird von Solar_forecast() gesetzt, wenn der fc0 über PV_1:Inverter_Max_Power (70% Regel) liegt
([01:17] and ($wday==1))\
  SpeicherMidday_MaxChargePowerAbs 500                                        Begrenzung der Ladeleistung am Vormittag, damit Mittags genug Platz im Speicher ist.
(set LogDBRep_Statistic_EnergyHomePvSum_Year_diff_Week diffValue writeToDB)\
  SpeicherMidday_MaxSOC 40                                                    Limitierung des Speichers um Mittags genug Platz zu haben
(set LogDBRep_Statistic_EnergyFeedInGrid_Year_diff_Week diffValue writeToDB)\
  SpeicherMinSOC_Sommer 5                                                    Sommer MinSOC von Kostal vorgegeben
(set LogDBRep_Statistic_Yield_Year_diff_Week diffValue writeToDB)\
  SpeicherMinSOC_Winter 20                                                    Winter MinSOC von Kostal vorgegeben
\
  SpeicherMinSOC_fc1_Limit 13000                                              Wenn im Herbst/Winter der Forecast zu schlecht wird muss dieser Wert auf die Anlage
## Wöchentliche Einträge mit löschen\
                                                                              angepasst werden. Das signalisiert die Winter Zeit.
DOELSEIF\
  SpeicherTrigger none                                                        entladen/gesperrt/none wird über PV_1_Speicher_1_ExternControl gesetzt
([02:17] and ($wday==1))\
  SpeicherZeitEnde 16:00                                                      Die Zeiten geben das Entlade Fenster an und werden durch weitere DOIF oder WeekdayTimer gesetzt.
(set LogDBRep_delete_Statistic_EnergyHomeBat_Day_max_Day maxValue deleteOther)\
  SpeicherZeitStart 07:00                                                    Dies kann zur Tarifsteuerung verwendet werden, oder um ein Entladung zeitlich zu verschieben
(set LogDBRep_delete_Statistic_EnergyHomePvSum_Day_max_Day maxValue deleteOther)\
                                                                              Das Zeitfenster kann durch den MinSOC Schutz im Winter veriegelt sein.
(set LogDBRep_delete_Statistic_TotalConsumption_Day_max_Day maxValue deleteOther)\
 
  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
      - PV_1_Speicher_1_ExternTrigger: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>
 
== Timeing für die PV extra Funktionen ==
=== RAW 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.
<syntaxhighlight lang="Perl">
defmod PV_Schedule DOIF ################################################################################################################\
## 1 Plenticore Status aktualisieren. Dies geschieht über das PV_1_API Device\
##\
([:57])\
\
  (get PV_1_API 20_Statistic_EnergyFlow)\
\
\
################################################################################################################\
## Tägliche Einträge\
## 2 PV Prognose vom aktuellen Tag aktualisieren\
##    zwischen 7 und 19 Uhr zur vollen Stunde\
DOELSEIF\
DOELSEIF\
([07:00-19:00] and [:00])\
([01:17])\
  ## Erste Versuche mit Schnee, wenn zuwenig Strom in den Modulen fließt wird ein Faktor von 0.1 gesetzt\
(set LogDBRep_Statistic_previous_Day sqlCmd ckey:1)                       ## Bildet für verschiedene Devices die Tagesauswertung\
  ## PV_1_config forecast_factor_autocorrection muss auf 1 gesetzt sein, damit das berücksichtigt wird\
  ({my $y=0;;;;my $x=ReadingsVal("PV_1","Current_DC1",1);;;;($x > 0 && $x < 1 && $hour < 14)?$y=0.1:$y=1;;;;CommandSetReading(undef, "PV_1_config module_1_covered ".$y)})\
  ({my $y=0;;;;my $x=ReadingsVal("PV_1","Current_DC2",1);;;;($x > 0 && $x < 1 && $hour > 12)?$y=0.1:$y=1;;;;CommandSetReading(undef, "PV_1_config module_3_covered ".$y)})\
\
  ({Solar_forecast("LogDB","LogDBRep_PV_Forecast_SQL","PV_1","Solar_Calculation_fc","DWD_Forecast",0)})\
  ({Solar_forecast("none","none","DWD_Forecast_Test","Solar_forecast_fc","DWD_Forecast",0)})\
\
\
################################################################################################################\
## Quartal Einträge\
## 3 PV Prognose für den nächsten Tag aktualisieren\
## \
DOELSEIF\
DOELSEIF\
([06:55] or [19:11])\
(($md eq "01-01" or $md eq "04-01" or $md eq "07-01" or $md eq "10-01") and [03:11])\
  ## Bei Schnee wurde der module_*_covered Faktor bereits am Vortag gesetzt.\
(set LogDBRep_Statistic_previous_Quarter sqlCmd ckey:1)                   ## Erstellt die Quartalsauswertung für WR_1\
  ({Solar_forecast("LogDB","LogDBRep_PV_Forecast_SQL","PV_1","Solar_Calculation_fc","DWD_Forecast",1)})\
  ({Solar_forecast("none","none","DWD_Forecast_Test","Solar_forecast_fc","DWD_Forecast",1)})\
\
\
################################################################################################################\
## Jährliche Einträge\
## 4 regelmäßig die Bilanz aktualisieren, alle 5 Minuten außer um :00\
##\
DOELSEIF\
DOELSEIF\
([+:05] and ![:00])\
($md eq "01-01" and [08:05])\
\
(set LogDBRep_Statistic_previous_Year sqlCmd ckey:1)                      ## Bildet für verschiedene Devices die Jahresauswertung
  (get PV_1_API 04_auth_me)\
attr DB_Service_Schedule DbLogExclude .*
attr DB_Service_Schedule comment Version 2024.01.23 17:00\
Hier werden zusätzlich Werte in der Datenbank erzeugt.
attr DB_Service_Schedule do always
attr DB_Service_Schedule room System
attr DB_Service_Schedule wait 0,3:0,5,5:0,5,5
attr DB_Service_Schedule webCmd cmd_1:cmd_2:cmd_3
attr DB_Service_Schedule webCmdLabel monatlich :wöchentlich :wöchentlich Löschen :
</syntaxhighlight>
 
==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.
 
==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.


attr PV_Schedule DbLogExclude .*
====RAW Definition DWD_Forecast====
attr PV_Schedule alias PV_Schedule
Erfordert ggf.
attr PV_Schedule cmdState Plenticore Status|Forecast 0|Forecast 1|Bilanz refresh
<syntaxhighlight lang="Perl">
attr PV_Schedule comment Version 2021.01.25 18:00
sudo apt-get install libxml-libxml-perl
attr PV_Schedule do always
attr PV_Schedule room Strom->Photovoltaik
attr PV_Schedule sortby 11
attr PV_Schedule verbose 0
attr PV_Schedule wait 0:0:0:0
attr PV_Schedule webCmd cmd_1:cmd_2:cmd_3:cmd_4
attr PV_Schedule webCmdLabel Statistic :Forecast_0 :Forecast_1 :Bilanz :
</syntaxhighlight>
</syntaxhighlight>


== Energie Bilanz ==
===RAW Definition DWD_Forecast===
[[Bild:Plenticore Bilanz.png|mini|900px|rechts|]]
*[https://svn.fhem.de/fhem/trunk/fhem/contrib/ch.eick/Photovoltaik/KI_Prognose/RAW_DWD_Forecast.txt Beispiel DWD_Forecast]
<big>Achtung, es gab eine Umstellung mit diesem Device! Die Bilanz wird nun direkt im PV_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 Momentanwerte direkt berechnet, die restlichen Werte werden als Statistiken aus dem Gerät abgefragt.
===Astro Device===
=== Erstellen von zusätzlichen Werten in der Datenbank ===
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.
[[Bild:SVG LogDB PV Bilanz.png|mini|900px|rechts|Die Definition diese Diagramms ist weiter unten beschrieben.]]
====RAW Definition Astro====
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.
* [https://svn.fhem.de/fhem/trunk/fhem/contrib/ch.eick/Photovoltaik/KI_Prognose/RAW_Astro.txt Beispiel Astro]


==== RAW Definition LogDBRep_Statistic_EnergyFeedInGrid_Year_diff_Week ====
====fhem.cfg Einträge für das Astro Device====
Dieser Wert ist die wöchentliche Einspeisung ins Netz.
Hier müsst Ihr Eure Position und Höhe eintragen.
Gestartet über DB_Service_Schedule am ersten Tag der Folgewoche.
<syntaxhighlight lang="Perl">
<syntaxhighlight lang="Perl">defmod LogDBRep_Statistic_EnergyFeedInGrid_Year_diff_Week DbRep LogDB
attr global altitude 110
attr LogDBRep_Statistic_EnergyFeedInGrid_Year_diff_Week DbLogExclude .*
attr global latitude 47.xxxxx
attr LogDBRep_Statistic_EnergyFeedInGrid_Year_diff_Week aggregation week
attr global longitude 9.yyyyy
attr LogDBRep_Statistic_EnergyFeedInGrid_Year_diff_Week comment Version 2020.10.21 11:14
</syntaxhighlight>
attr LogDBRep_Statistic_EnergyFeedInGrid_Year_diff_Week device PV_1_API
 
attr LogDBRep_Statistic_EnergyFeedInGrid_Year_diff_Week diffAccept 15000
===KI Prognose Teil 2 - Vorbereitung der Daten===
attr LogDBRep_Statistic_EnergyFeedInGrid_Year_diff_Week reading Statistic_EnergyFeedInGrid_Year
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.
attr LogDBRep_Statistic_EnergyFeedInGrid_Year_diff_Week readingNameMap Statistic_EnergyFeedInGrid_Week
 
attr LogDBRep_Statistic_EnergyFeedInGrid_Year_diff_Week room Strom->Energie,System
====RAW Definition dwd_load() MySQL Procedure====
attr LogDBRep_Statistic_EnergyFeedInGrid_Year_diff_Week timestamp_begin current_year_begin
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.
attr LogDBRep_Statistic_EnergyFeedInGrid_Year_diff_Week timestamp_end previous_week_end
 
* [https://svn.fhem.de/fhem/trunk/fhem/contrib/ch.eick/Photovoltaik/KI_Prognose/MySQL_dwd_load_Procedure.txt Beispiel dwd_load MySQL Procedure]
 
====dwd_load() in einzelnen Schritten====
<pre>
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.
</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>
</syntaxhighlight>


==== RAW Definition LogDBRep_Statistic_EnergyHomePvSum_Year_diff_Week ====
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.
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.
====RAW Definition LogDBRep_PV_KI_Prognose (Teil 1)====
Benötigt für SVG_LogDB_PV_Bilanz.
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">
<syntaxhighlight lang="Perl">
defmod LogDBRep_Statistic_EnergyHomePvSum_Year_diff_Week DbRep LogDB
defmod LogDBRep_PV_KI_Prognose DbRep LogDB
attr LogDBRep_Statistic_EnergyHomePvSum_Year_diff_Week DbLogExclude .*
attr LogDBRep_PV_KI_Prognose DbLogExclude .*
attr LogDBRep_Statistic_EnergyHomePvSum_Year_diff_Week aggregation week
attr LogDBRep_PV_KI_Prognose allowDeletion 0
attr LogDBRep_Statistic_EnergyHomePvSum_Year_diff_Week allowDeletion 0
attr LogDBRep_PV_KI_Prognose comment Version 2023.02.23 12:00\
attr LogDBRep_Statistic_EnergyHomePvSum_Year_diff_Week comment Version 2020.10.23 15:00\
\
Dieser Wert ist der gesamte Verbrauch aus der PV Anlage inklusive Batterie pro Woche an.\
Hier wird die Vorbereitung für die KI PV-Leistungsprognose durchgeführt\
Gestartet über DB_Service_Schedule am ersten Tag der Folgewoche.\
\
Benötigt für SVG_LogDB_PV_Bilanz.
sqlCmd call dwd_load(curdate(),'none');;\
attr LogDBRep_Statistic_EnergyHomePvSum_Year_diff_Week device PV_1_API
[none|show] zum Anzeigen des Ergebnisses\
attr LogDBRep_Statistic_EnergyHomePvSum_Year_diff_Week diffAccept 20000
\
attr LogDBRep_Statistic_EnergyHomePvSum_Year_diff_Week reading Statistic_EnergyHomePvSum_Year
executeAfterProc:\
attr LogDBRep_Statistic_EnergyHomePvSum_Year_diff_Week readingNameMap Statistic_EnergyHomePvSum_Week
<absoluter Skript Name> <DbLog IP-Adresse> <FHEM IP-Adresse> <DbRep Name> <Wechselricher Name> <Prefix Reading Name>
attr LogDBRep_Statistic_EnergyHomePvSum_Year_diff_Week room Strom->Energie,System
attr LogDBRep_PV_KI_Prognose executeAfterProc "/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"
attr LogDBRep_Statistic_EnergyHomePvSum_Year_diff_Week timestamp_begin current_year_begin
attr LogDBRep_PV_KI_Prognose room System
attr LogDBRep_Statistic_EnergyHomePvSum_Year_diff_Week timestamp_end previous_week_end
attr LogDBRep_PV_KI_Prognose verbose 3
</syntaxhighlight>
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>
</syntaxhighlight>


==== RAW Definition LogDBRep_Statistic_EnergyHomePvSum_Year_max_Month ====
===DbLog/DbRep Device===
Dieser Wert ist der gesamte Verbrauch aus der PV Anlage inklusive Batterie im Monat.
====RAW Definition LogDB====
Gestartet über DB_Service_Schedule am ersten des Monats.
Achtung, bitte hier beachten, ob bereits eine andere DbLog verwendet wird.
Benötigt für SVG_LogDB_PV_Bilanz.
<syntaxhighlight lang="Perl">
<syntaxhighlight lang="Perl">
defmod LogDBRep_Statistic_EnergyHomePvSum_Year_max_Month DbRep LogDB
defmod LogDB DbLog ./db.conf .*:.*
attr LogDBRep_Statistic_EnergyHomePvSum_Year_max_Month DbLogExclude .*
attr LogDB DbLogExclude .*
attr LogDBRep_Statistic_EnergyHomePvSum_Year_max_Month aggregation month
attr LogDB DbLogSelectionMode Exclude/Include
attr LogDBRep_Statistic_EnergyHomePvSum_Year_max_Month allowDeletion 1
attr LogDB DbLogType History
attr LogDBRep_Statistic_EnergyHomePvSum_Year_max_Month comment Version 2020.10.23 15:00\
attr LogDB asyncMode 1
Dieser Wert ist der gesamte Verbrauch aus der PV Anlage inklusive Batterie im Monat.\
attr LogDB bulkInsert 1
Gestartet über PV_Schedule am ersten des Monats\
attr LogDB disable 0
Benötigt für SVG_LogDB_PV_Bilanz.
attr LogDB room System
attr LogDBRep_Statistic_EnergyHomePvSum_Year_max_Month device PV_1_API
attr LogDB showproctime 1
attr LogDBRep_Statistic_EnergyHomePvSum_Year_max_Month reading Statistic_EnergyHomePvSum_Month
attr LogDB verbose 0
attr LogDBRep_Statistic_EnergyHomePvSum_Year_max_Month room Strom->Energie,System
attr LogDBRep_Statistic_EnergyHomePvSum_Year_max_Month timestamp_begin current_year_begin
attr LogDBRep_Statistic_EnergyHomePvSum_Year_max_Month timestamp_end previous_month_end
</syntaxhighlight>
</syntaxhighlight>


==== RAW Definition LogDBRep_Statistic_Yield_Year_diff_Week ====
====RAW Definition LogDBRep_PV_KI_Prognose====
Dieser Wert gibt den gesamten Ertrag der PV Anlage pro Woche an.\
Bitte beachtet, dass die Namen auch in anderen Devices eingetragen sind, wenn Ihr diese verändern wollt.
Gestartet über DB_Service_Schedule am ersten Tag der Folgewoche.\
 
Benötigt für  SVG_LogDB_PV_Bilanz.
*[https://svn.fhem.de/fhem/trunk/fhem/contrib/ch.eick/Photovoltaik/KI_Prognose/RAW_LogDBRep_PV_KI_Prognose.txt Beispiel LogDBRep_PV_KI_Prognose]
<syntaxhighlight lang="Perl">
 
defmod LogDBRep_Statistic_Yield_Year_diff_Week DbRep LogDB
====KI Prognose Teil 3 - Python KI Prognose Skript====
attr LogDBRep_Statistic_Yield_Year_diff_Week DbLogExclude .*
Für die Verwendung der KI Prognose werden die folgenden Python Packages noch benötigt. Die Basis wäre hierbei der FHEM Docker Container.
attr LogDBRep_Statistic_Yield_Year_diff_Week aggregation week
<pre>
attr LogDBRep_Statistic_Yield_Year_diff_Week allowDeletion 0
Momentan habe ich das erstmal manuell im Container gemacht:
attr LogDBRep_Statistic_Yield_Year_diff_Week comment Version 2020.10.23 15:00\
 
Dieser Wert gibt den gesamten Ertrag der PV Anlage pro Woche an.\
    sudo apt-get install python3-pandas
Gestartet über DB_Service_Schedule am ersten Tag der Folgewoche.\
    sudo apt-get install python3-pymysql
Benötigt für  SVG_LogDB_PV_Bilanz.
    sudo apt-get install python3-sqlalchemy
attr LogDBRep_Statistic_Yield_Year_diff_Week device PV_1_API
    sudo apt-get install python3-sklearn python3-sklearn-lib
attr LogDBRep_Statistic_Yield_Year_diff_Week diffAccept 20000
    pip3 install fhem
attr LogDBRep_Statistic_Yield_Year_diff_Week reading Statistic_Yield_Year
 
attr LogDBRep_Statistic_Yield_Year_diff_Week readingNameMap Statistic_Yield_Week
Für Docker sollte das im .yml File dann so aussehen:
attr LogDBRep_Statistic_Yield_Year_diff_Week room Strom->Energie,System
 
attr LogDBRep_Statistic_Yield_Year_diff_Week timestamp_begin current_year_begin
    -e PIP_PKGS="pandas pymysql sqlalchemy sklearn sklearn-lib"
attr LogDBRep_Statistic_Yield_Year_diff_Week timestamp_end previous_week_end
    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]
</pre>
 
Das PV_KI_Prognose.py wird mit folgenden Parametern aufgerufen:
<syntaxhighlight lang="Perl">
    <absoluter Skript Name> <DbLog IP-Adresse> <FHEM IP-Adresse> <DbRep Name> <Wechselricher Name> <Prefix Reading Name>
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
</syntaxhighlight>
</syntaxhighlight>
 
<pre>
==== RAW Definition LogDBRep_Statistic_Yield_Month_max_Month ====
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.
Dieser Wert gibt den gesamten Ertrag der PV Anlage im Monat an.
2. Nach dem Testen kommt dieser Aufruf dann in das LogDBRep_PV_KI_Prognose Device und wird somit mit dem MySQL Prozeduraufruf synchronisiert.
Gestartet über DB_Service_Schedule am ersten Tag der Folgewoche.
  Bitte das LogDBRep_PV_KI_Prognose Device (Teil 1) aus dem vorherigen Absatz verwenden.
Momentan noch in keinem SVC verwendet.
  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.
</pre>
<syntaxhighlight lang="Perl">
<syntaxhighlight lang="Perl">
defmod LogDBRep_Statistic_Yield_Month_max_Month DbRep LogDB
< snip >
attr LogDBRep_Statistic_Yield_Month_max_Month DbLogExclude .*
################################################################################################################
attr LogDBRep_Statistic_Yield_Month_max_Month aggregation month
## 2 Start der KI Prognose
attr LogDBRep_Statistic_Yield_Month_max_Month allowDeletion 0
## Der Reading Name und das Device werden in LogDBRep_PV_KI_Prognose im executeAfterProc eingestellt
attr LogDBRep_Statistic_Yield_Month_max_Month comment Version 2020.10.23 15:00\
##  "/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"
Dieser Wert gibt den gesamten Ertrag der PV Anlage im Monat an.\
##
Gestartet über DB_Service_Schedule am ersten Tag der Folgewoche.\
2_KI_Prognose
Momentan noch in keinem SVC verwendet.
{if( !([$SELF:state] eq "off")                                          ## DOIF enabled
attr LogDBRep_Statistic_Yield_Month_max_Month device PV_1_API
    and
attr LogDBRep_Statistic_Yield_Month_max_Month diffAccept 20000
      ReadingsVal("LogDBRep_PV_KI_Prognose","PV_KI_Prognose","null") eq "done"  ## Die Prognose darf nicht gerade laufen !!!
attr LogDBRep_Statistic_Yield_Month_max_Month reading Statistic_Yield_Month
    and
attr LogDBRep_Statistic_Yield_Month_max_Month room Strom->Energie,System
  (
attr LogDBRep_Statistic_Yield_Month_max_Month timestamp_begin current_year_begin
    ([05:00-22:00] and [:03]                                            ## In der PV-Zeit jede Stunde aktualisieren
attr LogDBRep_Statistic_Yield_Month_max_Month timestamp_end previous_month_end
    )
    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) {
      Log 3, "$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 >
</syntaxhighlight>
</syntaxhighlight>


=== Löschen von nicht mehr benötigten Werten in der Datenbank ===
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.
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.
<pre>
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.
    ./python/pwd_fhem.json
    ./python/pwd_sql.json
</pre>
Die Verbindungsdaten werden in den Dateien wie folgt abgelegt:
<syntaxhighlight lang="Perl">
fhem@raspberrypi:~/python$ cat pwd_[fhem|sql].json
{"username": "<Euer Username>",
"password": "<Euer Passwort>"}
</syntaxhighlight>
<pre>
FHEM und die Datenbank müssen nicht auf dem selben Rechner installiert werden. Die IP-Adressen werden dem Skript beim Aufruf mitgegeben.


==== RAW Definition LogDBRep_delete_Statistic_EnergyHomeBat_Day_max_Day ====
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.
Löschen aller Statistic_EnergyHomeBat_Day Werte, bis auf den maximal Wert des Tages.
</pre>
Der aktuelle Tag bleibt noch erhalten.
Wenn im LogDBRep_PV_KI_Prognose der verbose Level auf >= 3 steht kommen diverse Meldungen im Log:
Aufruf mit: maxValue deleteOther
<syntaxhighlight lang="Perl">
<syntaxhighlight lang="Perl">
defmod LogDBRep_delete_Statistic_EnergyHomeBat_Day_max_Day DbRep LogDB
/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
attr LogDBRep_delete_Statistic_EnergyHomeBat_Day_max_Day DbLogExclude .*
  import imp
attr LogDBRep_delete_Statistic_EnergyHomeBat_Day_max_Day aggregation day
PV_KI_Prognose  running - start
attr LogDBRep_delete_Statistic_EnergyHomeBat_Day_max_Day allowDeletion 1
PV_KI_Prognose  running - connected to 192.168.178.40
attr LogDBRep_delete_Statistic_EnergyHomeBat_Day_max_Day comment Version 2020.10.30 18:30\
PV_KI_Prognose  running - dwdfull read from DbLog 192.168.178.40
Löschen aller Statistic_EnergyHomeBat_Day Werte, bis auf den maximal Wert des Tages.\
PV_KI_Prognose  running - RandomForestRegressor loading
Der aktuelle Tag bleibt noch erhalten.\
PV_KI_Prognose  running - RandomForestRegressor loaded
Aufruf mit: maxValue deleteOther
PV_KI_Prognose  running - RandomForestRegressor trained
attr LogDBRep_delete_Statistic_EnergyHomeBat_Day_max_Day device PV_1_API
PV_KI_Prognose  running - RandomForestRegressor fitted with yield
attr LogDBRep_delete_Statistic_EnergyHomeBat_Day_max_Day reading Statistic_EnergyHomeBat_Day
PV_KI_Prognose  running - old forecast deleted
attr LogDBRep_delete_Statistic_EnergyHomeBat_Day_max_Day room Strom->Energie,System
PV_KI_Prognose  running - start forecast
attr LogDBRep_delete_Statistic_EnergyHomeBat_Day_max_Day timestamp_begin previous_month_begin
Yield_fc0_06  06 71
attr LogDBRep_delete_Statistic_EnergyHomeBat_Day_max_Day timestamp_end current_day_end
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>
</syntaxhighlight>


==== 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
<syntaxhighlight lang="Perl">
defmod LogDBRep_delete_Statistic_EnergyHomePvSum_Day_max_Day DbRep LogDB
attr LogDBRep_delete_Statistic_EnergyHomePvSum_Day_max_Day DbLogExclude .*
attr LogDBRep_delete_Statistic_EnergyHomePvSum_Day_max_Day aggregation day
attr LogDBRep_delete_Statistic_EnergyHomePvSum_Day_max_Day allowDeletion 1
attr LogDBRep_delete_Statistic_EnergyHomePvSum_Day_max_Day comment Version 2020.10.30 18:30\
Löschen aller Statistic_EnergyHomePvSum_Day Werte, bis auf den maximal Wert des Tages.\
Der aktuelle Tag bleibt noch erhalten.\
Aufruf mit: maxValue deleteOther
attr LogDBRep_delete_Statistic_EnergyHomePvSum_Day_max_Day device PV_1_API
attr LogDBRep_delete_Statistic_EnergyHomePvSum_Day_max_Day reading Statistic_EnergyHomePvSum_Day
attr LogDBRep_delete_Statistic_EnergyHomePvSum_Day_max_Day room Strom->Energie,System
attr LogDBRep_delete_Statistic_EnergyHomePvSum_Day_max_Day timestamp_begin previous_month_begin
attr LogDBRep_delete_Statistic_EnergyHomePvSum_Day_max_Day timestamp_end current_day_end
</syntaxhighlight>


==== RAW Definition LogDBRep_delete_Statistic_TotalConsumption_Day_max_Day ====
==Diagramme mit Grafana==
Löschen aller Statistic_TotalConsumption_Day Werte, bis auf den maximal Wert des Tages.\
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.
Der aktuelle Tag bleibt noch erhalten.
===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.'''
 
 
*[https://svn.fhem.de/fhem/trunk/fhem/contrib/ch.eick/Grafana/Dashboard/Kostal-Flow_V2_JSON.txt Beispiel Grafana Dashboard]
 
==Diagramme mit Grafana==
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.
=== RAW Definition Hauptverbraucher ===
* [https://svn.fhem.de/fhem/trunk/fhem/contrib/ch.eick/Grafana/Diagramme/ Beispiele]
* [https://svn.fhem.de/fhem/trunk/fhem/contrib/ch.eick/Grafana/Diagramme/Diagramm_Hauptverbraucher_JSON.txt Beispiel Hauptverbraucher]
* [https://svn.fhem.de/fhem/trunk/fhem/contrib/ch.eick/Grafana/Diagramme/Diagramm_KI_Prognose_JSON.txt Beispiel KI_Prognose]
* [https://svn.fhem.de/fhem/trunk/fhem/contrib/ch.eick/Grafana/Diagramme/Diagramm_Leistungsbezug_JSON.txt Beispiel Leistungsbezug]
 
== 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.
<gallery>
LAD_Zusatzheizung_2_4_6_kW.jpg|Zusatzheizung 2/4/6 kW
LAD_Shelly_Einbauposition.jpg|LAD Shelly Einbauposition
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 (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.
 
* [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]
* [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]
* [https://svn.fhem.de/fhem/trunk/fhem/contrib/ch.eick/Photovoltaik/W%c3%a4rmepumpe_Novelan_LAD9/RAW_LWP_Counter.txt Beispiel LWP_Counter]
* [https://svn.fhem.de/fhem/trunk/fhem/contrib/ch.eick/Photovoltaik/W%c3%a4rmepumpe_Novelan_LAD9/RAW_shelly01.txt Beispiel Shelly PV Modus Umschaltung]
 
=== Beispiel Pool Softube ===
==== RAW Definition Pool_PV (DOIF Modul) ====
<syntaxhighlight lang="Perl">
<syntaxhighlight lang="Perl">
defmod LogDBRep_delete_Statistic_TotalConsumption_Day_max_Day DbRep LogDB
defmod Pool_PV_Perl DOIF ################################################################################################################\
attr LogDBRep_delete_Statistic_TotalConsumption_Day_max_Day DbLogExclude .*
## Eigenverbrauch einschalten: wenn PV Produktion über dem Mindestbedarf ist und die Laufzeit pro Tag noch nicht erreicht ist\
attr LogDBRep_delete_Statistic_TotalConsumption_Day_max_Day aggregation day
##\
attr LogDBRep_delete_Statistic_TotalConsumption_Day_max_Day allowDeletion 1
01_1_Eigenverbrauch_automatisch_An\
attr LogDBRep_delete_Statistic_TotalConsumption_Day_max_Day comment Version 2020.10.30 18:30\
{if( !([$SELF:state] eq "off")                                          ## DOIF enabled\
Löschen aller Statistic_TotalConsumption_Day Werte, bis auf den maximal Wert des Tages.\
    and\
Der aktuelle Tag bleibt noch erhalten.
    (    [WR_1:SW_Total_PV_P_reserve] >= [$SELF:PowerLimitOn]            ## Es besteht PV-Überschuss\
attr LogDBRep_delete_Statistic_TotalConsumption_Day_max_Day device PV_1_API
    and [[$SELF:TimeStart]-[$SELF:TimeEnd]]                            ## Das Zeitfenster ist erreicht\
attr LogDBRep_delete_Statistic_TotalConsumption_Day_max_Day reading Statistic_TotalConsumption_Day
    and get_Exec("PV_Modus_Ein_timer") < 1                              ## Der Wait Timer ist noch nicht gestartet\
attr LogDBRep_delete_Statistic_TotalConsumption_Day_max_Day room Strom->Energie,System
    and [$SELF:Pool_Status] eq "Aus"                                    ## Der Pool ist aus\
attr LogDBRep_delete_Statistic_TotalConsumption_Day_max_Day timestamp_begin previous_month_begin
    and [Pool_Counter:pulseTimePerDay] < [$SELF:RunTimePerDay]          ## Die maximale Laufzeit des Pools ist noch nicht erreicht\
attr LogDBRep_delete_Statistic_TotalConsumption_Day_max_Day timestamp_end current_day_end
    )\
</syntaxhighlight>
    or [$SELF:ui_command_1] eq "01_1_Eigenverbrauch_automatisch_An"      ## Hier wird das uiTable select ausgewertet\
 
  ) {\
=== Timing für die Datenbank Einträge ===
    if (AttrVal("$SELF","verbose",0) >=3)\
Über dieses Scheduling werden in der Datenbank zusätzliche Wochen- und Monatseinträge gesteuert.
      {Log 3, "Pool_PV 01_1 : Pool on waiting"};;\
==== RAW Definition DB_Service_Schedule ====
\
Hier werden zusätzlich Werte in der Datenbank erzeugt.
    set_Exec("PV_Modus_Ein_timer",[$SELF:PowerLevelMinTime],'set_Reading("Pool_Status","An");;PV_Modus_Ein_Pool()');; ## Den PV-Modus verzögert einschalten\
<syntaxhighlight lang="Perl">
    set_Reading("Pool_Status","Wartend");;\
defmod DB_Service_Schedule DOIF ## Monatlich Einträge\
\
([01:13] and ($mday==1))\
    set_Reading("ui_command_1","---");;                                  ## Hier wird das uiTable select wieder zurückgesetzt, ansonsten\
(set LogDBRep_Statistic_EnergyHomePvSum_Year_max_Month maxValue writeToDB)\
                                                                        ## kann das Kommando nicht sofort wiederholt werden\
(set LogDBRep_Statistic_Yield_Month_max_Month maxValue writeToDB)\
    }\
}\
\
################################################################################################################\
## 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)\
        {Log 3, "Pool_PV 01_2 : Pool on for manuel usage"};;\
\
\
## Wöchentliche Einträge\
    set_Reading("Pool_Status","manuell");;\
DOELSEIF\
    PV_Modus_Ein_Pool();;\
([01:17] and ($wday==1))\
(set LogDBRep_Statistic_EnergyHomePvSum_Year_diff_Week diffValue writeToDB)\
(set LogDBRep_Statistic_EnergyFeedInGrid_Year_diff_Week diffValue writeToDB)\
(set LogDBRep_Statistic_Yield_Year_diff_Week diffValue writeToDB)\
\
\
## Wöchentliche Einträge mit löschen\
    set_Reading("ui_command_1","---");;                                  ## Hier wird das uiTable select wieder zurückgesetzt, ansonsten\
DOELSEIF\
                                                                        ## kann das Kommando nicht sofort wiederholt werden\
([02:17] and ($wday==1))\
  }\
(set LogDBRep_delete_Statistic_EnergyHomeBat_Day_max_Day maxValue deleteOther)\
}\
(set LogDBRep_delete_Statistic_EnergyHomePvSum_Day_max_Day maxValue deleteOther)\
\
(set LogDBRep_delete_Statistic_TotalConsumption_Day_max_Day maxValue deleteOther)\
################################################################################################################\
 
## Manuell den Pool abschalten.\
attr DB_Service_Schedule DbLogExclude .*
##\
attr DB_Service_Schedule comment Version 2020.10.23 17:00\
01_3_Eigenverbrauch_manuell_Aus\
Hier werden zusätzlich Werte in der Datenbank erzeugt.
{if( !([$SELF:state] eq "off")                                           ## DOIF enabled\
attr DB_Service_Schedule do always
    and\
attr DB_Service_Schedule room Strom->Energie,System
      [$SELF:ui_command_1] eq "01_3_Eigenverbrauch_manuell_Aus"        ## Hier wird das uiTable select ausgewertet\
attr DB_Service_Schedule wait 0,3:0,5,5:0,5,5
  ) {\
attr DB_Service_Schedule webCmd cmd_1:cmd_2:cmd_3
    if (AttrVal("$SELF","verbose",0) >=3)\
attr DB_Service_Schedule webCmdLabel monatlich :wöchentlich :wöchentlich Löschen :
        {Log 3, "Pool_PV 01_3 : Pool off after manuel usage"};;\
</syntaxhighlight>
\
 
     PV_Modus_Aus_Pool();;\
== Wetter-/Leistungs-Prognose ==
\
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 ;-)
    set_Reading("ui_command_1","---");;                                  ## Hier wird das uiTable select wieder zurückgesetzt, ansonsten\
[[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.]]
                                                                        ## kann das Kommando nicht sofort wiederholt werden\
==== Wetter Forecast Grundlagen ====
  }\
<pre>
}\
1.) Astro Device mit dem Namen Astro und der Konfiguration
################################################################################################################\
    für den jeweiligen Standort
## Eigenverbrauch abschalten: wenn Mindestlaufzeit erreicht wurde und Maximallaufzeit pro Tag erreicht ist\
2.) DbLog Device, um die Daten in die Datenbank zu schreiben
##\
3.) DbRep Device um auch wieder alte Forecasts zu löschen
02_1_Eigenverbrauch_Laufzeit_Aus\
4.) Das Wetter Device für wunderground wird nicht für den
{if( !([$SELF:state] eq "off")                                          ## DOIF enabled\
     Forecast benötigt.
    and\
</pre>
    (    [Pool_Counter:pulseTimePerDay] >= [$SELF:RunTimePerDay]        ## Die Tages Laufzeit ist überschritten\
 
    and [Pool_Counter:pulseTimeIncrement] >= [$SELF:RunTimeMin]        ## Die Mindestlaufzeit ist überschritten\
=== Deutscher Wetter Dienst (DWD) ===
    and (   [$SELF:Pool_Status] eq "An"                                ## Der Pool läuft\
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² .
          or [$SELF:Pool_Status] eq "pflege")\
'''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 ====
    or [$SELF:ui_command_1] eq "02_1_Eigenverbrauch_Laufzeit_Aus"        ## Hier wird das uiTable select ausgewertet\
Erfordert ggf.
  ) {\
<syntaxhighlight lang="Perl">
    if (AttrVal("$SELF","verbose",0) >=3)\
sudo apt-get install libxml-libxml-perl
        {Log 3, "Pool_PV 02_1 : Pool off Laufzeit"};;\
</syntaxhighlight>
\
 
    PV_Modus_Aus_Pool();;\
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.
\
<syntaxhighlight lang="Perl">
    set_Reading("ui_command_1","---");;                                  ## Hier wird das uiTable select wieder zurückgesetzt, ansonsten\
defmod DWD_Forecast DWD_OpenData
                                                                        ## kann das Kommando nicht sofort wiederholt werden\
attr DWD_Forecast DbLogExclude .*
  }\
attr DWD_Forecast comment Version 2020.10.19 18:28
}\
attr DWD_Forecast event-on-change-reading Rad1h,TTT,Neff,R600
\
attr DWD_Forecast forecastDays 1
\
attr DWD_Forecast forecastProperties Rad1h,TTT,Neff,R600,R101,wwM,ww,RRS1c,SunUp,SunRise,SunSet
################################################################################################################\
attr DWD_Forecast forecastResolution 1
## Eigenverbrauch abschalten: wenn Mindestlaufzeit erreicht wurde und die PV Produktion unter dem Mindestbedarf ist\
attr DWD_Forecast forecastStation P0178
##\
attr DWD_Forecast group PV Leistungsprognose
02_2_Eigenverbrauch_PV_Min_Aus\
attr DWD_Forecast icon weather_rain_fog
{if( !([$SELF:state] eq "off")                                           ## DOIF enabled\
attr DWD_Forecast room Informationen->Wetter,Strom->Photovoltaik
    and\
attr DWD_Forecast sortby 07
    ( (  [WR_1:Home_own_consumption_from_grid]                           ## Nicht zuviel Bezug aus dem Netz\
attr DWD_Forecast verbose 0
       + [WR_1:Home_own_consumption_from_Battery]) > 100                  ## oder dem Speicher\
</syntaxhighlight>
     and [Pool_Counter:pulseTimeIncrement] >= [$SELF:RunTimeMin]         ## Die Mindestlaufzeit ist überschritten\
 
     and [$SELF:Pool_Status] eq "An"                                     ## Der Pool läuft (nicht bei manuell)\
=== 99_myUtils.pm Funktionen ===
    )\
==== Solar_forecast() ====
    or [$SELF:ui_command_1] eq "02_2_Eigenverbrauch_PV_Min_Aus"          ## Hier wird das uiTable select ausgewertet\
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 PV_1_config vorhanden sein, in dem unter anderem die Modul und Anlagen Ausrichtung konfiguriert wird.
     if (AttrVal("$SELF","verbose",0) >=3)\
Rückfragen gerne im Forum.
        {Log 3, "Pool_PV 02_2 : Pool off PV-Min"};;\
 
\
<pre>
    PV_Modus_Aus_Pool();;\
Letzte Neuerungen:
\
- IM PV_1_config kann man verbose auf >3 setzen und bekommt dann Log Meldungen
     set_Reading("ui_command_1","---");;                                   ## Hier wird das uiTable select wieder zurückgesetzt, ansonsten\
- Es wird eine Autokorrektur unterstützt. Aktivierung durch "setreading PV_1_config forecast_factor_autocorrection 1"
                                                                        ## kann das Kommando nicht sofort wiederholt werden\
  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.
\
  Dieser Faktor wird im PV_Schedule Device erzeugt und dann im "PV_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 PV_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
## Stop, wenn es nur ein kurzer peak ist. Dieser Do Zweig setzt den wait timer vom Einschaltkommando cmd_4 wieder außer kraft,\
  es muss auch die Autokorrektur abgeschaltet sein.
## wenn während der Wartezeit die PV Anlage zuwenig liefert.\
- 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.
03___Stop_Wait_Timer\
</pre>
{if( !([$SELF:state] eq "off")                                           ## DOIF enabled\
 
    and\
<syntaxhighlight lang="Perl">
    (\
###########################################################
     (   [WR_1:SW_Total_PV_P_reserve] < [$SELF:PowerLimitOn]            ## Ist die PV-Leistung zu niedrig?\
# Subroutine to calculate radiation
      and get_Exec("PV_Modus_Ein_timer") > 0                            ## läuft eine Wartezeit\
###########################################################
      and get_Exec("PV_Modus_Ein_timer") < 5                            ## läuft die Wartezeit bald ab\
sub Solar_forecast($$$$$$) {
      and [$SELF:Pool_Status] eq "Wartend"                               ## und gibt es keine manuelle Einschaltung\
#
    )\
# 2021.03.03 12:00
    )\
#
    or [$SELF:ui_command_1] eq "03___Stop_Wait_Timer"                   ## Hier wird das uiTable select ausgewertet\
    my $logdb      = $_[0] ;       # Mit dieser Datenbank wird gearbeitet
  ) {\
     my $logdbrep    = $_[1] ;        # Das wird zur Kommunikation mit der LogDB verwendet und muss entsprechend konfiguriert sein
    if (AttrVal("$SELF","verbose",0) >=3)\
     my $logdevice  = " "   ;        # Das ist der Wechselrichter, oder ein anderes Device, in das die Prognose geschrieben wird
       {Log 3, "Pool_PV 03__ : Stop wait timer Pool"};;\
        $logdevice  = $_[2] ;
    del_Exec("PV_Modus_Ein_timer");;                                      ## Der Pool wird nicht mehr eingeschaltet\
    # 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")} ;
    set_Reading("ui_command_1","---");;                                   ## Hier wird das uiTable select wieder zurückgesetzt, ansonsten\
    my $fc          = $_[5] ;       # Wieviel Tage in die Zukunft soll es gehen? 0,1,2
                                                                        ## kann das Kommando nicht sofort wiederholt werden\
    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) ;
################################################################################################################\
 
## Pool Ende\
    # Gibt es einen festen Korrekturfaktor für jede Stunde?
##\
    my $Solar_Correction_Faktor = ReadingsVal($logdevice."_config","forecast_factor",1) ;
05___Pool_Ende\
 
{if( !([$SELF:state] eq "off")                                           ## DOIF enabled\
    # Soll eine Autokorrektur gemacht werden? 0 = Nein 1 = Ja
    and\
    my $autocorrection          = ReadingsVal($logdevice."_config","forecast_factor_autocorrection",0) ;
    (   [shelly02:power_0] < 10                                        ## Die Poolpumpe ist bereits\
 
    and [Pool_Counter:pauseTimeIncrement] > 900                        ## seit 5 Minuten aus\
    # Beim DWD wird der Wert für die Stunde erst am Ende der Stunde eingetragen
    and [$SELF:Pool_Status] ne "Aus"                                    ## und gibt es keine manuelle Einschaltung\
    my $timeshift = 1;              # Verschiebt die Prognose um eine Stunde
    and [$SELF:Pool_Status] ne "Wartend"                                ## und es wird nicht wegen eines Peaks gewartet\
    # Hier werden die Variablen vorbelegt
    )\
    my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time); $year += 1900; $mon += 1;
    or [$SELF:ui_command_1] eq "05___Pool_Ende"                          ## Hier wird das uiTable select ausgewertet\
    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 ;
    if (AttrVal("$SELF","verbose",0) >=3)\
    my ($module_covered,$Solar_Correction_Faktor_auto,$Solar_Correction_Cloud,$Solar_Correction_Rain,$Solar_Correction_Temp,$Solar_Plain) = (1) x 6 ;
      {Log 3, "Pool_PV 05__ : Pool run finished ".[shelly02:power_0]." ".[Pool_Counter:pauseTimeIncrement]};;\
     my (@Solar_,@module_count) = (0,0,0) ;
\
 
    PV_Modus_Aus_Pool();;\
    # Initialisieren des Basis TIMESTAMP für den Forecast
\
    my $timestring = time_str2num($year."-".$mon."-".$mday." 07:00:00") ;
    set_Reading("ui_command_1","---");;                                  ## Hier wird das uiTable select wieder zurückgesetzt, ansonsten\
    my $timestamp  = POSIX::strftime("%Y-%m-%d %H:00:00",localtime($timestring+$fc*24*60*60)) ;
                                                                        ## kann das Kommando nicht sofort wiederholt werden\
 
  }\
    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."'") ;
################################################################################################################\
    };
## Pool Startzeit durch Forecast verschieben. Der Forecast wird um 7:00 im Device PV_Schedule aktualisiert\
 
##\
    # Setzen der Tageszähler und Merker
08___Startzeit_nach_forecast\
    $logentry            = 0 ;  # Summiert den Solar_Calculation Wert für den ganzen Tag
{if( !([$SELF:state] eq "off")                                           ## DOIF enabled\
    $logentry4h          = 0 ;  # Summierung für die nächsten vier Stunden
    and\
    $logentryrest        = 0 ;  # Summierung für den Rest des Tages
         [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\
    my $middayhigh        = 0 ; # Ein Merker, ob das Tagesmaximum überschritten wird
  ) {\
    my $middayhigh_start  = "00:00";
  if( [$SELF:ui_command_1] eq "08___Startzeit_nach_forecast" ) {        ## Hier wurde manuell eingeschaltet\
    my $middayhigh_stop    = "00:00";
    set_Reading("ui_command_1_before",[$SELF:ui_command_1]);;\
 
  }\
    my $Inverter_Max_Power = ReadingsVal($logdevice."_Speicher_1_ExternTrigger","SpeicherMidday_Inverter_Max_Power","unused");  # Überschreiben des middayhigh
\
    if ($Inverter_Max_Power eq "unused") {
  if( [WR_1:Solar_Calculation_fc0_day] < [WR_1_Speicher_1_ExternControl:SpeicherMinSOC_fc1_Limit] or\
      $Inverter_Max_Power = ReadingsVal($logdevice,"Inverter_Max_Power",0) +500 ;      # Hier wird ein Durchschnittsverbrauch des Hauses aufaddiert
      [Heizung:averageAmbientTemperature] <= 10 ) {\
    } else {
    set_Reading("TimeStart",[$SELF:TimeStartWinter]);;\
      if ($verbose >= 3) { Log 3, "SpeicherMidday_Inverter_Max_Power manuell gesetzt" } ;
    set_Reading("TimeEnd",[$SELF:TimeEndWinter]);;\
    };
  } else {\
    if ($verbose >= 3) { Log 3, "SpeicherMidday_Inverter_Max_Power auf ".$Inverter_Max_Power." gesetzt" } ;
    set_Reading("TimeStart",[$SELF:TimeStartSummer]);;\
 
    set_Reading("TimeEnd",[$SELF:TimeEndSummer]);;\
    # Es werden Stundenwerte von 07:00 bis 19:00 Uhr berechnet
  }\
    for ($i = 7; $i <= 19; $i++) {
\
 
  if (AttrVal("$SELF","verbose",0) >=3)\
      if ($autocorrection ne 0 and $i == 7) {
    {Log 3, "Pool_PV 08__ : Pool switched to TimeStart ".ReadingsVal("$SELF","TimeStart",0)." TimeEnd ".ReadingsVal("$SELF","TimeEnd",0)};;\
        # Neuberechnung der stündlichen Autokorrektur Faktoren in der Datenbank. Das DbRep Device LogDBRep_PV_Forecast_SQL muss vorhanden sein.
\
        # Achtung, beim SQL muss '@' mit '\@' maskiert werden.
  set_Reading("ui_command_1","---");;                                    ## Hier wird das uiTable select wieder zurückgesetzt, ansonsten\
        CommandGet(undef, $logdbrep." sqlCmdBlocking ".sprintf("
                                                                        ## kann das Kommando nicht sofort wiederholt werden\
                INSERT INTO history
  }\
                (TIMESTAMP,DEVICE,READING,VALUE)
}\
                  SELECT
\
                    TIMESTAMP,DEVICE,READING,VALUE
################################################################################################################\
                  FROM (
## Pool durch kürzere Laufzeit abkühlen lassen\
                    SELECT
##\
                      DATE_ADD(CURDATE(),INTERVAL t2.HOUR HOUR) AS TIMESTAMP,
09___Laufzeit_im_Sommer\
                      t2.DEVICE,
{if( !([$SELF:state] eq "off")                                           ## DOIF enabled\
                      \@readingname                            AS READING,
    and\
                      cast(if(avg(t2.FACTOR) > 1.6, 1.6,
        [06:15] and [Heizung:averageAmbientTemperature]                ## Pool durch kürzere Laufzeit abkühlen lassen\
                              avg(t2.FACTOR) ) AS DECIMAL(2,1)) AS VALUE
      or [$SELF:ui_command_1] eq "09___Laufzeit_im_Sommer"               ## Hier wird das uiTable select ausgewertet\
                    FROM (
  ) {                                                              \
                      SELECT * FROM (
  if( [$SELF:ui_command_1] eq "09___Laufzeit_im_Sommer" ) {             ## Hier wurde manuell eingeschaltet\
                        SELECT
    set_Reading("ui_command_1_before",[$SELF:ui_command_1]);;\
                          t1.TIMESTAMP,
  }\
                          t1.HOUR,
\
                          t1.DEVICE,
  if( [Heizung:averageAmbientTemperature] >= 18 ) {\
                          t1.READING,
    set_Reading("RunTimePerDay",[$SELF:RunTimePerDaySummer]);;\
                          t1.VALUE,
  } else {\
                          if(\@diff = 0,NULL, \@temp:=cast((t1.VALUE-\@diff) AS DECIMAL(6,2))) AS DIFF,
    set_Reading("RunTimePerDay",[$SELF:RunTimePerDayWinter]);;\
                          cast((t1.VALUE/(t1.VALUE+(-1*\@temp))*\@corr) AS DECIMAL(2,1))      AS FACTOR,
  }\
                          \@diff:=t1.VALUE                                                    AS curr_V
\
                        FROM (
  if (AttrVal("$SELF","verbose",0) >=3)\
                          SELECT
    {Log 3, "Pool_PV 09__ : Pool switched to RunTimePerDay ".ReadingsVal("$SELF","RunTimePerDay",0)};;\
                            TIMESTAMP,
\
                            date(TIMESTAMP) AS DATE,
  set_Reading("ui_command_1","---");;                                   ## Hier wird das uiTable select wieder zurückgesetzt, ansonsten\
                            hour(TIMESTAMP) AS HOUR,
                                                                        ## kann das Kommando nicht sofort wiederholt werden\
                            DEVICE,
  }\
                            READING,
}\
                            VALUE
\
                          FROM history
################################################################################################################\
                          WHERE DEVICE    =  \@device
## Pflege Zwangseinschaltung: Es muss mindestens einmal pro Tag eingeschaltet werden, auch wenn kein PV Strom vorhanden war.\
                            AND (READING  =  \@reading1 OR READING = \@reading2)
##\
                            AND TIMESTAMP >= DATE_SUB(DATE(now()),INTERVAL \@days DAY)
10___Pool_Pflege\
                            AND TIMESTAMP <= CURDATE()
{if( !([$SELF:state] eq "off")                                           ## DOIF enabled\
                            AND MINUTE(TIMESTAMP) = 0
    and\
                            AND VALUE >= 0
    ( [[$SELF:TimeEnd]]                                                 ## Hier sollte der Pool bereits gelaufen sein\
                          GROUP BY DATE,HOUR,READING
      and\
                        )t1
      ([Pool_Counter:pulseTimePerDay] < [$SELF:RunTimePerDay] or        ## Ist er zuwenig gelaufen\
                      )tx
        [Pool_Counter:countsPerDay] eq 0)                                ## oder eventuell garnicht\
                        WHERE
    )\
                          READING != \@reading2
    or [$SELF:ui_command_1] eq "10___Pool_Pflege"                      ## Hier wird das uiTable select ausgewertet\
                    )t2
  ) {                                                              \
                      GROUP BY t2.HOUR
  if( [$SELF:ui_command_1] eq "10___Pool_Pflege" ) {                   ## Hier wurde manuell eingeschaltet\
                  )t3
    set_Reading("ui_command_1_before",[$SELF:ui_command_1]);;\
                    WHERE
  }\
                      t3.VALUE != 0
\
                    ON DUPLICATE KEY UPDATE
  if (AttrVal("$SELF","verbose",0) >=3)\
                      VALUE=t3.VALUE;
    {Log 3, "Pool_PV 10__ : Pool on for maintanance"};;\
          ") # Ende sprintf()
\
         );  # Ende CommandSet()
  set_Reading("Pool_Status","pflege");;\
      };
  PV_Modus_Ein_Pool();;\
 
\
 
  set_Reading("ui_command_1","---");;                                    ## Hier wird das uiTable select wieder zurückgesetzt, ansonsten\
      $timestring = time_str2num($year."-".$mon."-".$mday." ".$i.":00:00") ;
                                                                        ## kann das Kommando nicht sofort wiederholt werden\
      $timestamp  = POSIX::strftime("%Y-%m-%d %H:00:00",localtime($timestring+$fc*24*60*60)) ;
  }\
 
}\
      if ( $wetter eq "DWD_Forecast") {
\
        $Solar_Cloud          = ReadingsVal($wetter,"fc".$fc."_".($i+$timeshift)."_Neff" ,0);
################################################################################################################\
 
## Im Herbst Winter den Pool bei günstigem Strom in der Nacht zusätzlich einschalten\
        $Solar_Rain = 0;
##\
        for (my $r600 = $i+5; $r600 >= $i; $r600--) {
11___Nachtstrom_An\
          $Solar_Rain        += ReadingsVal($wetter,"fc".$fc."_".($r600+$timeshift)."_R600" ,0);
{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\
        $Solar_Temp          = ReadingsVal($wetter,"fc".$fc."_".($i+$timeshift)."_TTT" ,0)+10;
      and [EVU_Kosten:aWATTar_Trigger] eq "onx"                          ## Gibt es günstigen Strom an der Börse\
        $Solar_SolarRadiation = ReadingsVal($wetter,"fc".$fc."_".($i+$timeshift)."_Rad1h",0);
      and [22:00-05:00]                                                  ## nur in dieser Zeit verwenden\
        $Solar_SolarRadiation = round($Solar_SolarRadiation * 0.277778 ,0);
    )\
        if ($verbose >= 3) { Log 3, "Solar_SolarRadiation        : ".$Solar_SolarRadiation." W ".ReadingsVal($wetter,"fc".$fc."_".($i+$timeshift)."_Rad1h",0)." J" } ;
    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\
      $cloudk = ReadingsVal($logdevice."_config","forecast_cloudk",0) * -0.01 ;
    set_Reading("ui_command_1_before",[$SELF:ui_command_1]);;\
      if ($cloudk ne 0) {
  }\
        $cloudk_base = ReadingsVal($logdevice."_config","forecast_cloudk_base",0) ;
\
        $Solar_Correction_Cloud = round((1 + ($Solar_Cloud - $cloudk_base) * $cloudk / 100),3) ;
  if (AttrVal("$SELF","verbose",0) >=3)\
      };
    {Log 3, "Pool_PV 10__ : Pool on Nachtstrom by aWATTar"};;\
 
\
      $raink  = ReadingsVal($logdevice."_config","forecast_raink",0) * -0.01 ;
  set_Reading("Pool_Status","Nachtstrom");;\
      if ($raink ne 0) {
  PV_Modus_Ein_Pool();;\
        $raink_base  = ReadingsVal($logdevice."_config","forecast_raink_base" ,0) ;
\
        $Solar_Correction_Rain = round((1 + ($Solar_Rain  - $raink_base ) * $raink  / 100),3) ;
  set_Reading("ui_command_1","---");;                                    ## Hier wird das uiTable select wieder zurückgesetzt, ansonsten\
      };
                                                                        ## kann das Kommando nicht sofort wiederholt werden\
 
  }\
      $tempk  = ReadingsVal($logdevice."_config","forecast_tempk",0) * -0.01 ;
}\
      if ($tempk ne 0) {
\
        $tempk_base  = ReadingsVal($logdevice."_config","forecast_tempk_base" ,0) ;
################################################################################################################\
        $Solar_Correction_Temp = round((1 + ($Solar_Temp  - $tempk_base ) * $tempk  / 100),3) ;
## Sobald der Strompreis wieder teurer wird den Pool abschalten\
      };
##\
 
12___Nachtstrom_Aus\
      if ($autocorrection ne 0) {
{if( !([$SELF:state] eq "off")                                           ## DOIF enabled\
        $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)."';") ;
    and\
        if($Solar_Correction_Faktor_auto eq "") { $Solar_Correction_Faktor_auto = 1; };
    ( [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\
      $logentry1h = 0 ;  # Summierung für eine Stunde zurücksetzen
    )\
 
    or [$SELF:ui_command_1] eq "12___Nachtstrom_Aus"                   ## Hier wird das uiTable select ausgewertet\
      # Es werden 5 Modul Ausrichtungen durchlaufen, der Name der Ausrichtung befindet sich z.B. in PV_1_config
  ) {                                                              \
      for(my $j=1;$j<=5;$j++){
  if( [$SELF:ui_command_1] eq "12___Nachtstrom_Aus" ) {                ## Hier wurde manuell eingeschaltet\
        # lesen der Modul Anzahl
    set_Reading("ui_command_1_before",[$SELF:ui_command_1]);;\
        $module_count[$j] = ReadingsVal($logdevice."_config","module_".$j."_count",0) ;
  }\
 
\
        if ($module_count[$j] ne 0) {
  if (AttrVal("$SELF","verbose",0) >=3)\
          # Für diese Ausrichtung sind Module Installiert
    {Log 3, "Pool_PV 10__ : Pool off after Nachtstrom by aWATTar"};;\
 
\
          # Berechnung des Korrekturfaktors für die Modul Ausrichtung
  PV_Modus_Aus_Pool();;\
          $Solar_Plain = round(Solar_plain(ReadingsVal($logdevice."_config","module_".$j."_plain",0) , ReadingsVal($logdevice."_config","module_".$j."_direction",0) , $timestamp),3) ;
\
          if ($verbose >= 3) { Log 3, "factor/plain/direction      : ".$Solar_Plain." ".ReadingsVal($logdevice."_config","module_".$j."_plain",0)."/".ReadingsVal($logdevice."_config","module_".$j."_direction",0) };
  set_Reading("ui_command_1","---");;                                   ## Hier wird das uiTable select wieder zurückgesetzt, ansonsten\
          # Berechnung der Modul Nennleistung für diese Ausrichtung
                                                                        ## kann das Kommando nicht sofort wiederholt werden\
          $Solar_[$j] = $module_count[$j] * ReadingsVal($logdevice."_config","module_".$j."_power",1)/1000 ;
  }\
          # Anwendung der Korrekturfaktoren
}\
          $Solar_[$j] = $Solar_[$j] * $Solar_Plain ;
\
          $Solar_[$j] = $Solar_[$j] * $Solar_SolarRadiation ;
\
          $Solar_[$j] = $Solar_[$j] * $Solar_Correction_Temp  * $Solar_Correction_Cloud * $Solar_Correction_Rain ;
################################################################################################################\
          $Solar_[$j]  = $Solar_[$j] * $Solar_Correction_Faktor ;
## Definition von Sub Routinen\
 
subs {\
          if ($autocorrection ne 0) {
  sub PV_Modus_Ein_Pool() {                                                  ## PV-Modus Einschalten\
            # Nachsehen, ob dieser String mit Schnee bedeckt ist
    if (AttrVal("$SELF","verbose",0) >=3)\
            $module_covered = ReadingsVal($logdevice."_config","module_".$j."_covered",1) ;
       {Log 3, "Pool_PV sub  : Pool on"};;\
            $Solar_[$j]    = $Solar_[$j] * $Solar_Correction_Faktor_auto ;
##    {fhem("set Pool_Counter pulseTimeIncrement 0")}  ## das sollte eigentlich raus\
            $Solar_[$j]    = $Solar_[$j] * $module_covered ;
    fhem("set Pool_Counter pauseTimeIncrement 0");;\
            if ($verbose >= 3) { Log 3, "module_".$j."_covered            : ".$module_covered };
    fhem("".ReadingsVal("$SELF","SetCmdOn",0));;\
          };
    }\
 
\
          # Runden auf volle Watt Werte
  sub PV_Modus_Aus_Pool() {                                                  ## PV-Modus Ausschalten\
          $Solar_[$j]  = ($Solar_[$j] lt 0)?0:round($Solar_[$j],0) ;
     if (AttrVal("$SELF","verbose",0) >=3)\
 
      {Log 3, "Pool_PV sub  : Pool off"};;\
          if ($verbose >= 3) { Log 3, "module_".$j." estimation          : ".$Solar_[$j] };
    fhem("".ReadingsVal("$SELF","SetCmdOff",0));;\
 
    set_Reading("Pool_Status","Aus");;\
          # Aufsummieren aller konfigurierter Ausrichtungen
  }\
          $logentry1h += $Solar_[$j] ; # Summe für eine Stunde (wird mit jedem lauf von $i wieder auf 0 gesetzt)
}
 
attr Pool_PV_Perl DbLogExclude .*
          # Hier wird die Summe der nächsten 4 h gebildet
attr Pool_PV_Perl alias Pool_PV_Perl
          if ($fc == 0 and $i >= $hour and $i <= $hour+3) {
attr Pool_PV_Perl comment Version 2021.11.01 09:00
            $logentry4h += $Solar_[$j] ;
attr Pool_PV_Perl disable 0
          };
attr Pool_PV_Perl event-on-change-reading .*
 
attr Pool_PV_Perl group PV Eigenverbrauch-Steuerung
          # Hier wird die Summe für den Resttag gebildet
attr Pool_PV_Perl icon scene_swimming
          if ($fc == 0 and $i >= $hour) {
attr Pool_PV_Perl room Strom->Photovoltaik
            $logentryrest += $Solar_[$j] ;
attr Pool_PV_Perl sortby 421
          };
attr Pool_PV_Perl uiTable {\
 
package ui_Table;;\
          $logentry += $Solar_[$j] ; # Summe für den ganzen Tag
  $TABLE = "style='width:100%;;'";;\
 
\
          # Den Forecast Wert für die aktuelle Stunde in das Wechselrichter Device schreiben
  $TD{0..9}{0}     = "align='center' style='font-size:16px;;border-right-style:solid;;border-color:darkgreen;;border-right-width:2px;;width:26%'";;\
          if ($fc == 0 and $hour == $i) {
\
            if ($verbose >= 3) { Log 3, "Solar_".ReadingsVal($logdevice."_config","module_".$j."_name",0)."                  : ".$Solar_[$j]." ".$i." Uhr" };
  $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;;'";;\
            CommandSetReading(undef, $logdevice." Solar_".ReadingsVal($logdevice."_config","module_".$j."_name",0)." ".$Solar_[$j]) ;
  $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;;'";;\
        };
\
      };
sub FUNC_Status {\
 
    my($value, $min, $colorMin,  $statusMin,  $colorMiddel, $statusMiddle, $max, $colorMax, $statusMax)=@_;;\
      # Alle Forecast Werte für die jeweilie Stunde in die DbLog schreiben (Es wird der Cache verwendet)
    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;;\
      if ( $logdb ne "none" ) {
  }\
        CommandSet(undef, $logdb." addCacheLine ".$timestamp."|".$logdevice."|addlog|".$reading.": ".$logentry1h."|".$reading."|".$logentry1h."|") ;
\
 
}\
        if ( $middayhigh == 0 and $logentry1h > $Inverter_Max_Power ) {
\
          $middayhigh      = 1;
"$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>')\
          $middayhigh_start = sprintf("%02d:00",$i-1);
|"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")\
          CommandSetReading(undef, $logdevice." Solar_middayhigh_fc".$fc." ".$middayhigh) ;
|"<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")
          CommandSetReading(undef, $logdevice." Solar_middayhigh_fc".$fc."_start ".$middayhigh_start) ;
attr Pool_PV_Perl verbose 3
        };
        if ( $middayhigh == 1 and $logentry1h < $Inverter_Max_Power and $middayhigh_stop eq "00:00" )  {
          $middayhigh_stop  = sprintf("%02d:00",$i);
          CommandSetReading(undef, $logdevice." Solar_middayhigh_fc".$fc."_stop ".$middayhigh_stop) ;
        };
        ##   print($i." ".$logentry1h." ".$middayhigh." ".$middayhigh_start." ".$middayhigh_stop."\n");
      };
 
 
      # Sobald mindestens ein String configuriert ist sollen diese Werte, der aktuellen Stunde, in das Wechselrichter Device geschrieben werden
      if ($fc == 0 and $hour == $i and $module_count[1] ne 0) {
        CommandSetReading(undef, $logdevice." Solar_SolarRadiation ".$Solar_SolarRadiation) ;
        CommandSetReading(undef, $logdevice." Solar_Cloud ".$Solar_Cloud) ;
        CommandSetReading(undef, $logdevice." Solar_Rain ".$Solar_Rain) ;
        CommandSetReading(undef, $logdevice." Solar_Temp ".$Solar_Temp) ;
        CommandSetReading(undef, $logdevice." Solar_Correction_Cloud ".$Solar_Correction_Cloud) ;
        CommandSetReading(undef, $logdevice." Solar_Correction_Rain ".$Solar_Correction_Rain) ;
        CommandSetReading(undef, $logdevice." Solar_Correction_Temp ".$Solar_Correction_Temp) ;
        CommandSetReading(undef, $logdevice." Solar_Calculation ".$logentry1h) ;
      };
 
      # Auch die Solar_Calculation jeder einzelnen Stunde wird als reading in das Wechselrichter Device geschrieben
      CommandSetReading(undef, sprintf("%s %s_%02d %d",$logdevice,$reading,$i,$logentry1h)) ;
 
      # Für die Fehlersuche kommen noch einige Informationen ins Log
      if ($verbose >= 3) {
        Log 3, "Solar_SolarRadiation        : ".$Solar_SolarRadiation ;
        Log 3, "Solar_Cloud                  : ".$Solar_Cloud ;
        Log 3, "cloudk                      : ".$cloudk." ".$cloudk_base ;
        Log 3, "Solar_Correction_Cloud      : ".$Solar_Correction_Cloud ;
        Log 3, "Solar_Rain                  : ".$Solar_Rain ;
        Log 3, "raink                        : ".$raink." ".$raink_base ;
        Log 3, "Solar_Correction_Rain        : ".$Solar_Correction_Rain ;
        Log 3, "Solar_Temp                  : ".$Solar_Temp ;
        Log 3, "tempk                        : ".$tempk." ".$tempk_base ;
        Log 3, "Solar_Correction_Temp        : ".$Solar_Correction_Temp ;
        Log 3, "Solar_Correction_Faktor      : ".$Solar_Correction_Faktor ;
        Log 3, "Solar_Correction_Faktor_auto : ".$Solar_Correction_Faktor_auto ;
        Log 3, "Forecast,Hour,Estimation 1h  : ".$fc." ".$i." ".$logentry1h ;
      };
    };
 
    # Die Summe der nächsten 4 Stunden in das Wechselrichter Device schreiben
    if ($fc == 0) {
      CommandSetReading(undef, $logdevice." ".$reading."_4h ".$logentry4h) ;
      CommandSetReading(undef, $logdevice." ".$reading."_rest ".$logentryrest) ;
    };
 
    CommandSetReading(undef, $logdevice." ".$reading."_day ".$logentry) ;
 
    if ( $middayhigh == 0 ) {    # Auf Defaults zurücksetzen
       CommandSetReading(undef, $logdevice." Solar_middayhigh_fc".$fc." ".$middayhigh) ;
      CommandSetReading(undef, $logdevice." Solar_middayhigh_fc".$fc."_start ".$middayhigh_start) ;
      CommandSetReading(undef, $logdevice." Solar_middayhigh_fc".$fc."_stop ".$middayhigh_stop) ;
    };
 
     return (0);
};
</syntaxhighlight>
 
==== Solar_Plain() ====
<syntaxhighlight lang="Perl">
sub Solar_plain($$$) {
#
# 2021.02.28 17:00
#
    my $rad        = 57.296;
    my $factor      = 0.001;
 
    # read parameter
    my $angle      = $_[0];
    my $orienta    = $_[1];
    my $time        = $_[2];
 
    my $verbose    = AttrVal("Astro","verbose",0) ;
 
    # get Astro information
    my $azimuth     = CommandGet(undef, "Astro text SunAz ".$time) ;
    my $elevation  = CommandGet(undef, "Astro text SunAlt ".$time) ;
 
    # convert in radiant
    $elevation      = $elevation / $rad;
    $angle          = $angle    / $rad;
    my $orientation = ($azimuth - 180 - $orienta) / $rad;
 
    if(cos($orientation) < 0.05 && cos($orientation) > -0.2) {
      $orientation = $orientation - 0.2
    };
    if ($verbose >= 3) {
      Log 3, "Solar_plain azimuth          : ".$azimuth;
      Log 3, "Solar_plain elevation        : ".$elevation;
      Log 3, "Solar_plain orientation      : ".$orientation;
      Log 3, "Solar_plain angle            : ".$angle;
    };
    # avoid unrealistic values (normally formula should only be used within boundaries of orientation +/- 90 degrees)
    if ($elevation <= 0.14) {
      if ($verbose >= 3) { Log 3, "Solar_plain factor          : ".$factor };
      return($factor);
    };
 
    $factor = sin($angle) /
            (sin( $elevation) / cos( $elevation)) *
              cos($orientation) +
              cos($angle);
 
    # avoid too big values
    if ($factor > - 0.05 && $factor < 0.05) {
      $factor = 0.05
    };
    if ($verbose >= 3) { Log 3, "Solar_plain factor          : ".$factor };
    return ($factor);
};
 
</syntaxhighlight>


=== Astro Device ===
setstate Pool_PV_Perl 2022-10-23 16:44:33 Pool_Status Aus
==== RAW Definition Astro ====
setstate Pool_PV_Perl 2022-06-30 12:48:47 PowerLevelMinTime 300
<syntaxhighlight lang="Perl">
setstate Pool_PV_Perl 2022-05-10 15:51:44 PowerLimitOff 100
defmod Astro Astro
setstate Pool_PV_Perl 2022-05-10 15:50:44 PowerLimitOn 1000
attr Astro DbLogExclude .*
setstate Pool_PV_Perl 2022-05-10 15:55:23 RunTimeMin 7200
attr Astro DbLogInclude SunAlt,SunAz
setstate Pool_PV_Perl 2022-10-23 06:15:00 RunTimePerDay 28800
attr Astro alias Astro
setstate Pool_PV_Perl 2021-06-23 14:55:42 RunTimePerDaySummer 7200
attr Astro event-on-change-reading SunAlt,SunAz,ObsSeason,ObsSeasonN,.*Twilight.*
setstate Pool_PV_Perl 2020-10-06 14:14:13 RunTimePerDayWinter 28800
attr Astro group ASC Environment
setstate Pool_PV_Perl 2022-05-10 16:23:06 SetCmdOff set shelly02 off 0
attr Astro icon telescope
setstate Pool_PV_Perl 2022-05-10 16:23:24 SetCmdOn set shelly02 on 0
attr Astro interval 600
setstate Pool_PV_Perl 2022-10-23 07:17:00 TimeEnd 16:00
attr Astro recomputeAt NewDay,SunRise,SunSet,AstroTwilightEvening,AstroTwilightMorning,CivilTwilightEvening,CivilTwilightMorning,CustomTwilightEvening,CustomTwilightMorning
setstate Pool_PV_Perl 2021-12-01 17:39:32 TimeEndSummer 16:00
attr Astro room Informationen->Wetter,Rollos
setstate Pool_PV_Perl 2021-12-05 14:20:48 TimeEndWinter 16:00
attr Astro sortby 08
setstate Pool_PV_Perl 2022-10-23 07:17:00 TimeStart 12:35
</syntaxhighlight>
setstate Pool_PV_Perl 2022-06-30 12:48:23 TimeStartSummer 12:35
setstate Pool_PV_Perl 2020-09-03 13:10:56 TimeStartWinter 09:10


==== fhem.cfg Einträge für das Astro Device ====
setstate Pool_PV_Perl 2022-10-30 11:25:40 ui_command_1 ---
<syntaxhighlight lang="Perl">
setstate Pool_PV_Perl 2022-10-16 19:59:44 ui_command_1_before ---
attr global altitude 110
attr global latitude 47.85750
attr global longitude 9.49420
</syntaxhighlight>
</syntaxhighlight>
 
==== RAW Definition Pool_Signale (Shelly Modul: shelly1pm) ====
=== DbLog/DbRep Device ===
==== RAW Definition LogDB ====
Achtung, bitte hier beachten, ob bereits eine andere DbLog verwendet wird.
<syntaxhighlight lang="Perl">
<syntaxhighlight lang="Perl">
defmod LogDB DbLog ./db.conf .*:.*
defmod shelly02 Shelly 192.168.178.52
attr LogDB DbLogExclude .*
attr shelly02 DbLogExclude .*
attr LogDB DbLogSelectionMode Exclude/Include
attr shelly02 DbLogInclude relay.*,power.*,energy.*
attr LogDB DbLogType History
attr shelly02 alias Pool
attr LogDB asyncMode 1
attr shelly02 comment Version 2020.10.19 18:28\
attr LogDB bulkInsert 1
relais_0 => Pool limit 1000 W\
attr LogDB disable 0
relail_1 => Terrasse Lichterkette limit 100 W
attr LogDB room System
attr shelly02 event-on-change-reading relay.*,energy.*,state,network
attr LogDB showproctime 1
attr shelly02 event-on-update-reading power.*
attr LogDB verbose 0
attr shelly02 group PV Eigenverbrauch-Steuerung
</syntaxhighlight>
attr shelly02 icon taster_ch_1
attr shelly02 mode relay
attr shelly02 model shelly2.5
attr shelly02 room Shelly,Strom->Photovoltaik
attr shelly02 sortby 422
attr shelly02 stateFormat {\
my $status = (ReadingsVal($name,"state","none") eq "OK") ? "<span style='color:green'>OK</span>":"<span style='color:red'>Error</span>";;\
my $link = ReadingsVal($name,"WebLink","none");;\
\
my $e0 = sprintf("%08.2f KWh",ReadingsVal($name,"energy_0_Total",0)/1000);;\
my $r0 = (ReadingsVal($name,"relay_0","") eq "off") ? "<span style='color:red'>off</span>":"<span style='color:green'>on</span>";;\
my $p0 = sprintf("%06.1f Watt",ReadingsVal($name,"power_0",0));;\
\
my $e1 = sprintf("%08.2f KWh",ReadingsVal($name,"energy_1_Total",0)/1000);;\
my $r1 = (ReadingsVal($name,"relay_1","") eq "off") ? "<span style='color:red'>off</span>":"<span style='color:green'>on</span>";;\
my $p1 = sprintf("%06.1f Watt",ReadingsVal($name,"power_1",0));;\
\
"<html><table border=2 bordercolor='darkgreen' cellspacing=0 style='width: 100%'>\
<colgroup>\
  <col span='1' style='width: 30%;;'>\
  <col span='1' style='width: 30%;;'>\
  <col span='1' style='width: 20%;;'>\
</colgroup>\
<tr>\
  <td style='text-align:left'>\
    Status: $status\
  </td>\
  <td style='text-align:left'>\
    WebLink: $link\
  </td>\
  <td style='text-align:right'>\
    Pool Gesamt 0: $e0<br>\
    JEL Gesamt 1: $e1</td>\
  <td style='text-align:right'>\
    Relais 0: $r0 $p0<br>\
    Relais 1: $r1 $p1\
  </td>\
</tr>\
</table>\
</html>"\
}
attr shelly02 userReadings WebLink:network.* { my $ip=ReadingsVal($name,"network","na");; $ip =~ s/connected to //gs;; $ip =~ s/<[^>]*>//gs;; return("<a href='http://".$ip."/'>".$ip."</a>") },\
\
energy_0_Total:energy_0.* monotonic { ReadingsVal($name,"energy_0",0) },\
energy_1_Total:energy_1.* monotonic { ReadingsVal($name,"energy_1",0) }\


==== RAW Definition LogDBRep_PV_Forecast_SQL ====
attr shelly02 verbose 0
Dieses Device war vormals das LogDBRep_delete_PV_Forecast , es wird jedoch nun mehrfach verwendet und wurde deshalb umbenannt. Das LogDBRep_delete_PV_Forecast kann gelöscht werden.
attr shelly02 webCmd |
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
- @reading2 wird der Basisname der readings im PV_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.
<syntaxhighlight lang="Perl">
defmod LogDBRep_PV_Forecast_SQL DbRep LogDB
attr LogDBRep_PV_Forecast_SQL DbLogExclude .*
attr LogDBRep_PV_Forecast_SQL allowDeletion 1
attr LogDBRep_PV_Forecast_SQL room System
attr LogDBRep_PV_Forecast_SQL sqlCmdHistoryLength 5
attr LogDBRep_PV_Forecast_SQL sqlCmdVars SET @days:=3, @corr:=0.7, @diff:=0, @temp:=0, @device:='PV_1', @reading1:='Total_DC_Power_(sumOfAllPVInputs)', @reading2:='Solar_Calculation_fc0', @readingname:='Solar_Correction_Faktor_auto' ;;
</syntaxhighlight>
</syntaxhighlight>


==== Solar Forcast Tests ====
=== Beispiel Waschmaschine (mit Walzenschalter ;-) ) ===
Grundlagen hierfür sind:
==== RAW Definition Waschmaschine_PV (DOIF Modul) ====
1.) Astro Device mit dem Namen Astro und der Konfiguration für den Standort (in der fhem.cfg eingetragen)
2.) DbLog Device, um die Daten in die Datenbank zu schreiben
3.) DbRep Device um auch wieder alte Forecasts zu löschen
4.) Die Solar_ Funktionen in der 99_myUtils
5.) Das Wetter Device für wunderground wird nicht für den Forecast benötigt
 
===== Astro Device Test =====
Bei diesem Test wird der jeweilige Winkel des Sonnenstandes zu der gegebene Zeit und der eigenen Standortposition zurückgegeben.
<syntaxhighlight lang="Perl">
<syntaxhighlight lang="Perl">
get Astro text SunAz  "2020-10-10 15:00:00"
defmod Waschmaschine_PV_Perl DOIF ################################################################################################################\
get Astro text SunAlt "2020-10-10 15:00:00"
## Eigenverbrauch einschalten: wenn PV Produktion über dem Mindestbedarf ist und die Laufzeit pro Tag noch nicht erreicht ist\
</syntaxhighlight>
##\
 
01_1_Eigenverbrauch_automatisch_An\
===== Solar_plain() Test =====
{if( !([$SELF:state] eq "off")                                          ## DOIF enabled\
Diese Funktion kann man folgendermaßen testen. Für Logmeldungen muss man im Astro Device verbose auf 3 oder größer stellen.
    and\
 
    (    [WR_1:SW_Total_PV_P_reserve] >= [$SELF:PowerLimitOn]            ## Es besteht PV-Überschuss\
In der fhem comandline:
    and [[$SELF:TimeStart]-[$SELF:TimeEnd]]                            ## Das Zeitfenster ist erreicht\
<syntaxhighlight lang="Perl">
    and get_Exec("PV_Modus_Ein_timer") < 1                              ## Der Wait Timer ist noch nicht gestartet\
{Solar_plain(45,20,"2020-10-10 15:00:00")}
    and [$SELF:Status_1] eq "Aus"                                      ## Die Waschmaschine ist aus\
</syntaxhighlight>
    and [Waschmaschine_Counter:pulseTimePerDay] < [$SELF:RunTimePerDay] ## Die maximale Laufzeit der Waschmaschine ist noch nicht erreicht\
 
    )\
Hierbei ist 45 die Dachneigung und 20 die Ausrichtung, das Dach hätte demnach also Süd/West Lage.
    or [$SELF:ui_command_1] eq "01_1_Eigenverbrauch_automatisch_An"      ## Hier wird das uiTable select ausgewertet\
 
  ) {\
Datum mit Uhrzeit reicht dann stundenweise hochzuzählen.
    if (AttrVal("$SELF","verbose",0) >=3)\
 
      {Log 3, "$SELF 01_1 : Waiting for ".([$SELF:PowerLevelMinTime]/60)." Minutes"};;\
Im Log kommt dann Folgendes
\
<syntaxhighlight lang="Perl">
    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\
2020.10.10 14:40:05.038 3: get Astro text SunAz 2020-10-10 15:00:00 : 210.6
    set_Reading("Status_1","Wartend");;\
2020.10.10 14:40:05.046 3: get Astro text SunAlt 2020-10-10 15:00:00 : 28.6
    set_Reading("Status_2","für ".([$SELF:PowerLevelMinTime]/60)." Minuten");;\
2020.10.10 14:40:05.046 3: Solar_plain: azimuth = 210.6, orientation=0.185004188774085, elevation=0.49916224518291, angle=0.785395141022061
\
2020.10.10 14:40:05.046 3: Factor: 1.98190505984713
    set_Reading("ui_command_1","---");;                                  ## Hier wird das uiTable select wieder zurückgesetzt, ansonsten\
</syntaxhighlight>
                                                                        ## kann das Kommando nicht sofort wiederholt werden\
 
    }\
Sobald der Faktor unsinnig würde, wird von der Funktion 0.001 zurückgeliefert. Somit würde die Prognose auf fast null reduziert!
}\
<syntaxhighlight lang="Perl">
\
{Solar_plain(45,40,"2021-03-03 15:00:00") } => 2.00234055111251
################################################################################################################\
{Solar_plain(45,40,"2021-03-03 16:00:00") } => 2.42298713810404
## Manuell das Gerät einschalten.\
{Solar_plain(45,40,"2021-03-03 17:00:00") } => 3.20079343955795
##\
{Solar_plain(45,40,"2021-03-03 18:00:00") } => 0.001
01_2_Eigenverbrauch_manuell_An\
{Solar_plain(45,40,"2021-03-03 19:00:00") } => 0.001
{if( !([$SELF:state] eq "off")                                          ## DOIF enabled\
</syntaxhighlight>
    and\
 
    (    [$SELF:Status_1] eq "Aus"\
===== Solar_forecast() Test =====
    and [shelly03:relay] eq "on"                                        ## Der Taster wurde gedrückt\
Ein erster Test für diese Funktion wäre ein manueller Aufruf in der Kommandozeile. Hierbei ist 0 der aktuelle und 1 der nächste Tag.
    )\
<syntaxhighlight lang="Perl">
    or [$SELF:ui_command_1] eq "01_2_Eigenverbrauch_manuell_An"          ## Hier wird das uiTable select ausgewertet\
{Solar_forecast("LogDB","LogDBRep_delete_PV_Forecast","PV_1","Solar_Calculation_fc","DWD_Forecast",0)}
  ) {\
{Solar_forecast("LogDB","LogDBRep_delete_PV_Forecast","PV_1","Solar_Calculation_fc","DWD_Forecast",1)}
    if (AttrVal("$SELF","verbose",0) >=3)\
</syntaxhighlight>
        {Log 3, "$SELF 01_2 : On for manuel usage"};;\
 
\
=== Forecast Basiseinstellung ===
    del_Exec("PV_Modus_Ein_timer");;\
Erste Werte wurden bereits mit dem Gerät PV_1_config von einer Ost/Süd/West Anlage mitgeliefert. Es werden bis zu 5 Strings unterstützt.
    set_Reading("Status_1","manuell");;\
 
    PV_Modus_Ein_Waschmaschine();;\
Grundlegend muss man als erstes jede Ausrichtung von Modulen definieren.
    set_Reading("Status_2","Steckdose manuell An");;\
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.
    set_Reading("ui_command_1","---");;                                  ## Hier wird das uiTable select wieder zurückgesetzt, ansonsten\
Die Nennleistung pro Modul wird mit dem reading *_power eingetragen.
                                                                        ## kann das Kommando nicht sofort wiederholt werden\
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 PV_1 als reading "Solar_Calculation" eingetragen.
################################################################################################################\
Die weiteren readings "Solar_*" geben noch weitere Werte an.
## Manuelle Verwendung abschalten.\
<syntaxhighlight lang="Perl">
##\
module_1_count 13
01_3_Eigenverbrauch_manuell_Aus\
module_1_direction -90
{if( !([$SELF:state] eq "off")                                           ## DOIF enabled\
module_1_name East
    and\
module_1_plain 40
    ( (  [$SELF:Status_1] eq "An"\
module_1_power 310
      or [$SELF:Status_1] eq "manuell")\
</syntaxhighlight>
    and [shelly03:relay] eq "off"                                        ## Der Taster wurde gedrückt\
 
    )\
=== Berücksichtigung von Temperatur, Bewölkung und Regen ===
    or [$SELF:ui_command_1] eq "01_3_Eigenverbrauch_manuell_Aus"        ## Hier wird das uiTable select ausgewertet\
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!
    if (AttrVal("$SELF","verbose",0) >=3)\
 
        {Log 3, "$SELF 01_3 : Off after manuel usage"};;\
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 =====
    PV_Modus_Aus_Waschmaschine();;\
Je heißer die Module werden, je schlechter wird die Leistungsausbeute. Hierzu findet man in den Modulunterlagen einen Wert, der dies wiederspiegelt.
    set_Reading("Status_2","Steckdose manuell Aus");;\
<syntaxhighlight lang="Perl">
\
tempk          dies ist der Faktor aus den Unterlagen ( bei meinen Modulen 0.39 ) und wird dann mit 39 eingetragen
    set_Reading("ui_command_1","---");;                                  ## Hier wird das uiTable select wieder zurückgesetzt, ansonsten\
tempk_base    Dieser Wert hebt die "Heizungskurve" an und wird mit 25 angegeben. Das bedeutet, bei einer Temperatur von 25° wird die Nennleistung erreicht.
                                                                        ## kann das Kommando nicht sofort wiederholt werden\
</syntaxhighlight>
  }\
 
}\
===== Wolken und Regen =====
################################################################################################################\
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.
## Eigenverbrauch abschalten: wenn Mindestlaufzeit erreicht wurde und Maximallaufzeit pro Tag erreicht ist\
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,
02_1_Eigenverbrauch_Laufzeit_Aus\
oder auch eine Parallelverschiebung (cloudk_base) stattfinden. Wenn die Wolken einen starken einfluss haben sollen wäre cloudk z.B. zu verändern.
{if( !([$SELF:state] eq "off")                                           ## DOIF enabled\
 
    and\
=== wunderground ===
    (   [Waschmaschine_Counter:pulseTimePerDay] >= [$SELF:RunTimePerDay] ## Die Tages Laufzeit ist überschritten\
Diesen 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 ist dann als aktueller Wert in den Diagrammen zu sehen 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.
    and [Waschmaschine_Counter:pulseTimeIncrement] >= [$SELF:RunTimeMin] ## Die Mindestlaufzeit ist überschritten\
Für diese Abfrage ist keine Registrierung notwendig und man muss auch nicht selber mir einer Station Daten liefern.
    and [$SELF:Status_1] eq "An"                                        ## Das Gerät läuft\
Aber bitte, die Abfrage nicht in einem zu kurzen Abstand, also mit hoher Frequenz stellen! Hier wird alle 15 Minuten (900 Sekunden) abgefragt.
    )\
=== RAW Definition Wetter_<Wohnort> ===
\
Ggf. muss UConv vorher noch aktiviert werden:
    or [$SELF:ui_command_1] eq "02_1_Eigenverbrauch_Laufzeit_Aus"        ## Hier wird das uiTable select ausgewertet\
<syntaxhighlight lang="Perl">
  ) {\
define uconvInit notify global:INITIALIZED {use UConv}
    if (AttrVal("$SELF","verbose",0) >=3)\
</syntaxhighlight>
        {Log 3, "$SELF 02_1 : Off by runtime"};;\
 
\
<syntaxhighlight lang="Perl">
    PV_Modus_Aus_Waschmaschine();;\
defmod wetter_<Wohnort> HTTPMOD https://www.wunderground.com/dashboard/pws/<Wohnort_Station> 900
    set_Reading("Status_2","Laufzeit Max Aus");;\
 
\
attr wetter_<Wohnort> DbLogExclude .*
    set_Reading("ui_command_1","---");;                                  ## Hier wird das uiTable select wieder zurückgesetzt, ansonsten\
attr wetter_<Wohnort> DbLogInclude solarRadiation,solarUV,temperature
                                                                        ## kann das Kommando nicht sofort wiederholt werden\
attr wetter_<Wohnort> alias wetter_<Wohnort>
  }\
attr wetter_<Wohnort> enableControlSet 1
}\
attr wetter_<Wohnort> event-on-change-reading solarRadiation,solarUV,temperature
\
attr wetter_<Wohnort> group ASC Environment
################################################################################################################\
attr wetter_<Wohnort> icon weather_sunrise
## Eigenverbrauch abschalten: falls die Waschmaschine doch nicht benötigt wurde\
attr wetter_<Wohnort> reading01Name date
##\
attr wetter_<Wohnort> reading01Regex Summary<.*>([[:alpha:]]{1,9} [\d]{1,2}, [\d]{4})<\/strong
02_2_Eigenverbrauch_PV_Min_Aus\
attr wetter_<Wohnort> reading02Format %.1f
{if( !([$SELF:state] eq "off")                                          ## DOIF enabled\
attr wetter_<Wohnort> reading02Name dewpointTemperature
    and\
attr wetter_<Wohnort> reading02OExpr UConv::f2c($val,2)
    (    [WR_1:SW_Total_PV_P_reserve] < [$SELF:PowerLimitOn]             ## Der Überschuss ist zu wenig\
attr wetter_<Wohnort> reading02Regex DEWPOINT.*>([\d\.]+)<\/span>.*PRECIP RATE
    and [$SELF:Status_1] eq "An"                                        ## und die Waschmaschine wartet\
attr wetter_<Wohnort> reading03Name dewpointTemperature_EN
    and [$SELF:Status_2] eq "PV Überschuss wartend"                    ##  auf den start\
attr wetter_<Wohnort> reading03Regex DEWPOINT.*>([\d\.]+)<\/span>.*PRECIP RATE
    )\
attr wetter_<Wohnort> reading04Format %.0f
    or [$SELF:ui_command_1] eq "02_2_Eigenverbrauch_PV_Min_Aus"          ## Hier wird das uiTable select ausgewertet\
attr wetter_<Wohnort> reading04Name humidity
  ) {\
attr wetter_<Wohnort> reading04Regex HUMIDITY.*>([\d\.]+)<.*WIND
    if (AttrVal("$SELF","verbose",0) >=3)\
attr wetter_<Wohnort> reading05Name precip1hrmetric
        {Log 3, "$SELF 02_2 : PV-Minimum unterschritten"};;\
attr wetter_<Wohnort> reading05Regex PRECIP RATE.*>([\d\.]+)<.*PRECIP TOTAL
\
attr wetter_<Wohnort> reading06Name preciptodaymetric
    PV_Modus_Aus_Waschmaschine();;\
attr wetter_<Wohnort> reading06Regex PRECIP TOTAL.*>([\d\.]+)<.*tile-precipitation
    set_Reading("Status_2","PV Min Aus");;\
attr wetter_<Wohnort> reading07Format %.2f
\
attr wetter_<Wohnort> reading07Name pressure
    set_Reading("ui_command_1","---");;                                  ## Hier wird das uiTable select wieder zurückgesetzt, ansonsten\
attr wetter_<Wohnort> reading07OExpr UConv::inhg2hpa($val,2)
                                                                        ## kann das Kommando nicht sofort wiederholt werden\
attr wetter_<Wohnort> reading07Regex CURRENT.*>([\d\.]+)<.*tile-pressure
  }\
attr wetter_<Wohnort> reading08Format %.2f
}\
attr wetter_<Wohnort> reading08Name pressure_EN
\
attr wetter_<Wohnort> reading08Regex CURRENT.*>([\d\.]+)<.*tile-pressure
################################################################################################################\
attr wetter_<Wohnort> reading09Name solarRadiation
## Stop, wenn es nur ein kurzer peak ist. Dieser Do Zweig setzt den wait timer vom Einschaltkommando cmd_4 wieder außer kraft,\
attr wetter_<Wohnort> reading09Regex SOLAR RADIATION.*CURRENT.*weather__text">([\d\.]+)
## wenn während der Wartezeit die PV Anlage zuwenig liefert.\
attr wetter_<Wohnort> reading10Name solarUV
##\
attr wetter_<Wohnort> reading10Regex CURRENT UV.*>([\d\.]+)<.*UV RISK
03___Stop_Wait_Timer\
attr wetter_<Wohnort> reading11Format %.1f
{if( !([$SELF:state] eq "off")                                          ## DOIF enabled\
attr wetter_<Wohnort> reading11Name temperature
    and\
attr wetter_<Wohnort> reading11OExpr UConv::f2c($val,2)
    (\
attr wetter_<Wohnort> reading11Regex current-temp.*">([- ]*[\d\.]+).*DEWPOINT
    (    [WR_1:SW_Total_PV_P_reserve] < [$SELF:PowerLimitOn]            ## Ist die PV-Leistung zu niedrig?\
attr wetter_<Wohnort> reading12Format %.1f
      and get_Exec("PV_Modus_Ein_timer") > 0                            ## läuft eine Wartezeit\
attr wetter_<Wohnort> reading12Name temperature_EN
      and get_Exec("PV_Modus_Ein_timer") < [$SELF:PowerLevelMinTime]     ## läuft die Wartezeit bald ab\
attr wetter_<Wohnort> reading12Regex current-temp.*">([- ]*[\d\.]+).*DEWPOINT
      and [$SELF:Status_1] eq "Wartend"                                  ## und gibt es keine manuelle Einschaltung\
attr wetter_<Wohnort> reading13Format %.1f
    )\
attr wetter_<Wohnort> reading13Name windChill
    )\
attr wetter_<Wohnort> reading13OExpr UConv::f2c($val,2)
    or [$SELF:ui_command_1] eq "03___Stop_Wait_Timer"                    ## Hier wird das uiTable select ausgewertet\
attr wetter_<Wohnort> reading13Regex Feels Like.*>([\d\.]+)<.*wind-dial__container
  ) {\
attr wetter_<Wohnort> reading14Format %.1f
    if (AttrVal("$SELF","verbose",0) >=3)\
attr wetter_<Wohnort> reading14Name windChill_EN
      {Log 3, "$SELF 03__ : Stop wait timer"};;\
attr wetter_<Wohnort> reading14Regex Feels Like.*>([\d\.]+)<.*wind-dial__container
    del_Exec("PV_Modus_Ein_timer");;                                      ## Das Gerät wird nicht mehr eingeschaltet\
attr wetter_<Wohnort> reading15Name windDirection
    set_Reading("Status_1","Aus");;\
attr wetter_<Wohnort> reading15OExpr UConv::compasspoint2compasspoint($val,"en",1,"de")
    set_Reading("Status_2","warten gestoppt");;\
attr wetter_<Wohnort> reading15Regex WIND FROM.*>([NESW]+)<.*GUST
\
attr wetter_<Wohnort> reading16Name windDirection_EN
    set_Reading("ui_command_1","---");;                                  ## Hier wird das uiTable select wieder zurückgesetzt, ansonsten\
attr wetter_<Wohnort> reading16Regex WIND FROM.*>([NESW]+)<.*GUST
                                                                        ## kann das Kommando nicht sofort wiederholt werden\
attr wetter_<Wohnort> reading17Format %.0f
  }\
attr wetter_<Wohnort> reading17Name windSpeed
}\
attr wetter_<Wohnort> reading17OExpr UConv::mph2kph($val,1)
\
attr wetter_<Wohnort> reading17Regex wind-dial__container.*>([\d\.]+)<.*unit-speed
################################################################################################################\
attr wetter_<Wohnort> reading18Name windSpeed_EN
## Statuswechsel wenn das Waschprogramm gestartet ist\
attr wetter_<Wohnort> reading18Regex wind-dial__container.*>([\d\.]+)<.*unit-speed
##\
attr wetter_<Wohnort> reading19Name windGust
04_1_Waschprogramm_wartend\
attr wetter_<Wohnort> reading19OExpr UConv::mph2kph($val,1)
{if( !([$SELF:state] eq "off")                                          ## DOIF enabled\
attr wetter_<Wohnort> reading19Regex GUST.*>([\d\.]+)<.*mph
    and\
attr wetter_<Wohnort> reading20Name windGust_EN
    (    [shelly03:power] < 1                                            ## Das Gerät ist bereits\
attr wetter_<Wohnort> reading20Regex GUST.*>([\d\.]+)<.*mph
    and [Waschmaschine_Counter:pauseTimeIncrement] > 120                ## seit 2 Minuten aus\
attr wetter_<Wohnort> room Informationen->Wetter,Rollos
    and [$SELF:Status_1] eq "An"                                        ## und gibt es keine manuelle Einschaltung\
attr wetter_<Wohnort> sortby 03
    and\
attr wetter_<Wohnort> stateFormat T: temperature °C | F: humidity % | W: windSpeed km/h | D: pressure hPa | U: solarUV | R: solarRadiation W/m²
      (  [$SELF:Status_2] eq "PV Überschuss An"                        ## falls die Waschmaschine nicht gebraucht\
attr wetter_<Wohnort> timeout 5
        or [$SELF:Status_2] eq "PV Überschuss wartend")                  ## wird einfach weiter warten\
</syntaxhighlight>
    and [$SELF:ui_command_1] eq "---"\
 
    )\
== Diagramme ==
    or [$SELF:ui_command_1] eq "04_1_Waschprogramm_wartend"              ## Hier wird das uiTable select ausgewertet\
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.
\
=== RAW Definition Hauptverbraucher ===
    if (AttrVal("$SELF","verbose",0) >=3)\
[[Bild:Plenticore_Hauptverbraucher.png|mini|600px|rechts|]]
      {Log 3, "$SELF 04_1 : Waschmaschine ist nicht gestartet"};;\
==== SVG_LogDB_Photovoltaik_2 ====
\
<pre>
    set_Reading("Status_2","PV Überschuss wartend");;\
defmod SVG_LogDB_Photovoltaik_2 SVG LogDB:SVG_LogDB_Photovoltaik_2:HISTORY
    set_Reading("ui_command_1","---");;                                  ## Hier wird das uiTable select wieder zurückgesetzt, ansonsten\
attr SVG_LogDB_Photovoltaik_2 DbLogExclude .*
                                                                        ## kann das Kommando nicht sofort wiederholt werden\
attr SVG_LogDB_Photovoltaik_2 group PV Eigenverbrauch
  }\
attr SVG_LogDB_Photovoltaik_2 plotsize 1400,320
}\
attr SVG_LogDB_Photovoltaik_2 room Strom->Info,Strom->Photovoltaik
\
attr SVG_LogDB_Photovoltaik_2 sortby 00
################################################################################################################\
</pre>
## Statuswechsel wenn das Waschprogramm gestartet ist\
==== GPLOTFILE SVG_LogDB_Photovoltaik_2.gplot ====
##\
<pre>
04_2_Waschprogramm_gestartet\
# Created by FHEM/98_SVG.pm, 2020-03-16 10:23:52
{if( !([$SELF:state] eq "off")                                          ## DOIF enabled\
set terminal png transparent size <SIZE> crop
    and\
set output '<OUT>.png'
    (    [shelly03:power] > 0                                            ## Das Gerät ist bereits gestartet\
set xdata time
##    and [shelly03:power_Waschmaschine_avg] < 70                        ## und verbraucht mehr Leistung als im Standby\
set timefmt "%Y-%m-%d_%H:%M:%S"
    and [$SELF:Status_2] ne "Waschprogramm gestartet"                  ## \
set xlabel " "
    )\
set title 'Hauptverbraucher'
    or [$SELF:ui_command_1] eq "04_2_Waschprogramm_gestartet"            ## Hier wird das uiTable select ausgewertet\
set ytics
  ) {\
set y2tics
    if (AttrVal("$SELF","verbose",0) >=3)\
set grid
      {Log 3, "$SELF 04_2 : Waschprogramm gestartet"};;\
set ylabel "Leistung"
\
set y2label ""
    set_Reading("Status_2","Waschprogramm gestartet");;\
 
    fhem("set alias=Mobil speak 40 Das Waschprogramm ist gestartet");;\
#LogDB PV_1:Total_DC_Power:::$val=abs($val)
    set_Reading("ui_command_1","---");;                                  ## Hier wird das uiTable select wieder zurückgesetzt, ansonsten\
#LogDB PV_1:Total_PV_Power_reserve::
                                                                        ## kann das Kommando nicht sofort wiederholt werden\
#LogDB StromZaehler_Heizung:SMAEM1901401955_Saldo_Wirkleistung:::$val=abs($val)
  }\
#LogDB shelly02:Power_0::
}\
#LogDB shelly03:Power::
 
plot "<IN>" using 1:2 axes x1y2 title 'Total_DC' ls l1 lw 1 with lines,\
    "<IN>" using 1:2 axes x1y2 title 'Total_PV_reserve' ls l2 lw 1 with lines,\
    "<IN>" using 1:2 axes x1y2 title 'Heizung' ls l0 lw 1 with lines,\
    "<IN>" using 1:2 axes x1y2 title 'Pool' ls l8 lw 1 with lines,\
    "<IN>" using 1:2 axes x1y2 title 'Waschmaschine' ls l3 lw 1 with lines
</pre>
=== RAW Definition Leistungsbezug ===
[[Bild:Plenticore_Leistungsbezug.png|mini|600px|rechts|]]
==== SVG_LogDB_Photovoltaik_3 ====
<pre>
defmod SVG_LogDB_Photovoltaik_3 SVG LogDB:SVG_LogDB_Photovoltaik_3:HISTORY
attr SVG_LogDB_Photovoltaik_3 DbLogExclude .*
attr SVG_LogDB_Photovoltaik_3 group PV Eigenverbrauch
attr SVG_LogDB_Photovoltaik_3 plotsize 1400,320
attr SVG_LogDB_Photovoltaik_3 room Strom->Info,Strom->Photovoltaik
attr SVG_LogDB_Photovoltaik_3 sortby 00
</pre>
==== GPLOTFILE SVG_LogDB_Photovoltaik_3.gplot ====
<pre>
# Created by FHEM/98_SVG.pm, 2020-07-22 13:51:57
set terminal png transparent size <SIZE> crop
set output '<OUT>.png'
set xdata time
set timefmt "%Y-%m-%d_%H:%M:%S"
set xlabel " "
set title 'Leistungsbezug'
set ytics
set y2tics
set grid
set ylabel "Leistung"
set y2label ""
set yrange [0:9500]
set y2range [0:9500]
 
#LogDB PV_1:Total_DC_Power:::$val=abs($val)
#LogDB PV_1:Home_own_consumption_from_PV::
#LogDB PV_1:Home_own_consumption_from_battery::
#LogDB PV_1:Home_own_consumption_from_grid::
#LogDB PV_1:Actual_battery_charge_usable_Power::
#LogDB PV_1:Total_DC_Power_Max::
#LogDB PV_1:Battery_temperature:::$val=$val*100
#LogDB Heizung:heatSourceIN:::$val=$val*100
 
plot "<IN>" using 1:2 axes x1y2 title 'Total_DC_Power' ls l1fill lw 1 with lines,\
    "<IN>" using 1:2 axes x1y2 title 'own_PV' ls l2fill lw 1 with lines,\
    "<IN>" using 1:2 axes x1y2 title 'Bat_use' ls l0fill lw 1 with lines,\
    "<IN>" using 1:2 axes x1y2 title 'Grid_use' ls l5 lw 1 with lines,\
    "<IN>" using 1:2 axes x1y2 title 'Bat_usable' ls l4 lw 1 with lines,\
    "<IN>" using 1:2 axes x1y2 title 'Total_DC_Max' ls l6 lw 2 with lines,\
    "<IN>" using 1:2 axes x1y2 title 'Bat_Temp_Trend' ls l0 lw 2 with lines,\
    "<IN>" using 1:2 axes x1y2 title 'Out_Temp_Trend' ls l2 lw 2 with lines
</pre>
=== RAW Definition PV_Bilanz ===
[[Bild:SVG LogDB PV Bilanz.png|mini|600px|rechts|]]
==== SVG_LogDB_PV_Bilanz ====
<pre>
defmod SVG_LogDB_PV_Bilanz SVG LogDB:SVG_LogDB_PV_Bilanz:HISTORY
attr SVG_LogDB_PV_Bilanz DbLogExclude .*
attr SVG_LogDB_PV_Bilanz alias SVG_LogDB_PV_Bilanz
attr SVG_LogDB_PV_Bilanz comment Version 2020.10.21 11:37
attr SVG_LogDB_PV_Bilanz fixedrange year
attr SVG_LogDB_PV_Bilanz plotsize 1400,320
attr SVG_LogDB_PV_Bilanz room Strom->Energie
</pre>
 
==== GPLOTFILE SVG_LogDB_PV_Bilanz.gplot ====
<pre>
# Created by FHEM/98_SVG.pm, 2020-08-02 09:55:06
set terminal png transparent size <SIZE> crop
set output '<OUT>.png'
set xdata time
set timefmt "%Y-%m-%d_%H:%M:%S"
set xlabel " "
set title 'PV_Bilanz'
set ytics
set y2tics
set grid ytics
set ylabel "KWh"
set y2label "KWh"
 
#LogDB PV_1_API:max_month_Statistic_EnergyHomePvSum_Month:::$val=$val/1000
#LogDB PV_1_API:diff_week_Statistic_EnergyHomePvSum_Week:::$val=$val/1000
#LogDB PV_1_API:diff_week_Statistic_Yield_Week:::$val=$val/1000
 
plot "<IN>" using 1:2 axes x1y2 title 'Statistic_EnergyHomePvSum_Month' ls l2fill lw 2 with points,\
    "<IN>" using 1:2 axes x1y2 title 'Statistic_EnergyHomePvSum_Week' ls l2fill lw 1 with fsteps,\
    "<IN>" using 1:2 axes x1y2 title 'Statistic_Yield_Week' ls l1fill lw 1 with lines
</pre>
 
==== SVG_LogDB_PV_Netz ====
<pre>
defmod SVG_LogDB_PV_Netz SVG LogDB:SVG_LogDB_PV_Netz:HISTORY
attr SVG_LogDB_PV_Netz DbLogExclude .*
attr SVG_LogDB_PV_Netz alias SVG_LogDB_PV_Netz
attr SVG_LogDB_PV_Netz comment Version 2020.10.21 13:45
attr SVG_LogDB_PV_Netz fixedrange month
attr SVG_LogDB_PV_Netz plotsize 1400,320
attr SVG_LogDB_PV_Netz room Strom->Energie
</pre>
 
==== GPLOTFILE SVG_LogDB_PV_Netz.gplot ====
<pre>
# Created by FHEM/98_SVG.pm, 2020-10-21 13:43:37
set terminal png transparent size <SIZE> crop
set output '<OUT>.png'
set xdata time
set timefmt "%Y-%m-%d_%H:%M:%S"
set xlabel " "
set title 'PV_Netz'
set ytics
set y2tics
set grid ytics
set ylabel "KWh"
set y2label "KWh"
 
#LogDB PV_1_API:Statistic_EnergyFeedInGrid_Day::
#LogDB PV_1_API:Statistic_EnergyHomeGrid_Day:::$val=$val*-1
 
plot "<IN>" using 1:2 axes x1y2 title 'Statistic_EnergyFeedInGrid_Day' ls l1fill lw 1 with lines,\
    "<IN>" using 1:2 axes x1y2 title 'Statistic_EnergyHomeGrid_Day' ls l0fill lw 1 with lines
</pre>
 
=== RAW Definition Forecast / Calculation ===
[[Bild:Plenticore_Forecast.png|mini|600px|rechts|]]
==== SVG_LogDB_Photovoltaik_4 ====
<pre>
defmod SVG_LogDB_Photovoltaik_4 SVG LogDB:SVG_LogDB_Photovoltaik_4:HISTORY
attr SVG_LogDB_Photovoltaik_4 DbLogExclude .*
attr SVG_LogDB_Photovoltaik_4 group PV Eigenverbrauch
attr SVG_LogDB_Photovoltaik_4 plotsize 1400,320
attr SVG_LogDB_Photovoltaik_4 room Strom->Photovoltaik
attr SVG_LogDB_Photovoltaik_4 sortby 00
</pre>
==== GPLOTFILE SVG_LogDB_Photovoltaik_4.gplot ====
<pre>
# Created by FHEM/98_SVG.pm, 2020-08-17 08:58:42
set terminal png transparent size <SIZE> crop
set output '<OUT>.png'
set xdata time
set timefmt "%Y-%m-%d_%H:%M:%S"
set xlabel " "
set title 'Forecast / Calculation'
set ytics
set y2tics
set grid
set ylabel "Leistung"
set y2label "Leistung"
set yrange [0:10000]
set y2range [0:10000]
 
#LogDB Astro:SunAlt:::$val=($val>0?$val*50+7000:7000)
#LogDB wetter_<Wohnort>_II:solarRadiation:::$val=($val>0?$val*3+7000:7000)
#LogDB PV_1:Solar_SolarRadiation:::$val=($val>0?$val*3+7000:7000)
#LogDB Astro:SunAlt:::$val=7000
#LogDB PV_1:Solar_Calculation_fc1::
#LogDB PV_1:Total_DC_Power_(sumOfAllPVInputs)::
#LogDB PV_1:Solar_Calculation::
#LogDB PV_1:Solar_East::
#LogDB PV_1:Solar_South::
#LogDB PV_1:Solar_West::
 
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 ===
==== RAW Definition LWP_LuftWärmePumpe (dummy Modul) ====
<syntaxhighlight lang="Perl">
defmod LWP dummy
attr LWP DbLogExclude .*
attr LWP DbLogInclude state
attr LWP alias LWP_LuftWärmePumpe
attr LWP group PV Eigenverbrauch
attr LWP icon sani_earth_source_heat_pump
attr LWP readingList LWP_Button PowerLevelMinTime PowerLimitOn PowerLimitOff RunTimeMin RunTimePerDay SetCmdOff SetCmdOn TimeStart TimeEnd
attr LWP room Strom->Photovoltaik
attr LWP setList LWP_Button:uzsuToggle,on,off PowerLevelMinTime:slider,60,30,300 PowerLimitOn:slider,1000,250,4000 PowerLimitOff:slider,1000,250,4000 RunTimeMin:slider,300,300,7200 RunTimePerDay:slider,900,300,28800 SetCmdOff SetCmdOn TimeStart:time TimeEnd:time
attr LWP sortby 10
attr LWP stateFormat state
attr LWP verbose 0
attr LWP webCmd LWP_Button
 
setstate LWP off
setstate LWP 2020-08-05 17:38:03 LWP_Button off
setstate LWP 2020-03-02 11:56:45 PowerLevelMinTime 600
setstate LWP 2020-02-05 14:11:42 PowerLimitOff 2250
setstate LWP 2020-03-02 11:56:39 PowerLimitOn 3000
setstate LWP 2019-08-02 10:31:21 RunTimeMin 3600
setstate LWP 2020-02-05 14:13:01 RunTimePerDay 28800
setstate LWP 2019-12-29 10:47:55 SetCmdOff set shelly01 off 0
setstate LWP 2019-07-22 15:35:59 SetCmdOn set shelly01 on 0
setstate LWP 2019-11-09 12:13:04 TimeEnd 16:00
setstate LWP 2020-04-02 13:51:54 TimeStart 12:30
setstate LWP 2020-08-05 17:38:03 state off
</syntaxhighlight>
==== RAW Definition LWP_PV (DOIF Modul) ====
<syntaxhighlight lang="Perl">
defmod LWP_PV DOIF ################################################################################################################\
## 1 Eigenverbrauch abschalten: wenn Mindestlaufzeit erreicht wurde und Maximallaufzeit pro Tag erreicht ist\
##\
    ([LWP_Counter:pulseTimePerDay] >= [LWP:RunTimePerDay] and\
    [LWP_Counter:pulseTimeIncrement] >= [LWP:RunTimeMin] and\
    [LWP:state] ne "off" and [LWP:LWP_Button] eq "off" )\
\
\
    ({Log 3, " LWP_PV cmd_1 PV : LWP off"}\
    {fhem("".ReadingsVal("LWP","SetCmdOff",0))}\
    {fhem("set LWP off")}\
    {fhem("set Heizung hotWaterTemperatureTarget 50.0")}\
    )\
################################################################################################################\
################################################################################################################\
## 2 Eigenverbrauch abschalten: wenn Mindestlaufzeit erreicht wurde und die PV Produktion unter dem Mindestbedarf ist\
## Gerät Ende\
##\
##\
DOELSEIF\
05_1_Waschprogramm_Ende\
  ( ([PV_1:Total_PV_Power_reserve]+[StromZaehler_Heizung:SMAEM1901401955_Bezug_Wirkleistung]) < [LWP:PowerLimitOff] and\
{if( !([$SELF:state] eq "off")                                           ## DOIF enabled\
    [LWP_Counter:pulseTimeIncrement] >= [LWP:RunTimeMin] and\
    and\
     [LWP:state] ne "off" and\
    (    [shelly03:power] == 0                                          ## Die Waschmaschine hat abgeschaltet\
    [LWP:LWP_Button] ne "on" )\
     and [$SELF:Status_2] eq "Waschprogramm gestartet"                   ## und vorher lief das Waschprogramm\
\
\
    ({Log 3, " LWP_PV cmd_2 PV : LWP off"}\
    {fhem("".ReadingsVal("LWP","SetCmdOff",0))}\
    {fhem("set LWP off")}\
    {fhem("set Heizung hotWaterTemperatureTarget 50.0")}\
     )\
     )\
################################################################################################################\
    or [$SELF:ui_command_1] eq "05_1_Waschprogramm_Ende"                ## Hier wird das uiTable select ausgewertet\
## 3 Stop, wenn es nur ein kurzer peak ist. Dieser Do Zweig setzt den wait timer vom Einschaltkommando cmd_4 wieder außerkraft,\
  ) {\
##  wenn wärend der Wartezeit die PV Anlage zuwenig liefert.\
\
##\
      set_Reading("Status_2","Waschprogramm beendet");;\
DOELSEIF\
      fhem("set alias=Mobil speak 40 Das Waschprogramm ist fertig");;\
     ([PV_1:Total_PV_Power_reserve] < [LWP:PowerLimitOff] and\
\
    [LWP_PV:wait_timer] ne "no timer" and\
      if (AttrVal("$SELF","verbose",0) >=3)\
    [LWP_PV:wait_timer] ne "" and\
        {Log 3, "$SELF 05_1 : Waschprogramm beendet"};;\
    [LWP:state] eq "off" )\
\
    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\
    }\
  }\
}\
\
\
    ({Log 3, "LWP_PV cmd_3 PV : Stop wait timer LWP"})\
################################################################################################################\
################################################################################################################\
## 4 Eigenverbrauch einschalten: wenn PV Produktion über dem Mindestbedarf ist und die Laufzeit pro Tag noch nicht erreicht ist\
## Gerät Abschalten, wenn es nicht verwendet wurde\
##\
##\
DOELSEIF\
05_2_Waschmaschine_Aus\
    ([Astro:ObsSeason] ne "Sommer" and [Astro:ObsSeason] ne "Frühling" and\
{if( !([$SELF:state] eq "off")                                          ## DOIF enabled\
    [PV_1:Total_PV_Power_reserve] >= [LWP:PowerLimitOn] and\
    and\
    [[LWP:TimeStart]-[LWP:TimeEnd]] and\
    (    [[$SELF:TimeEnd]-[$SELF:TimeStart]]                             ## und auch nicht in der Nachtzeit\
     [LWP:state] eq "off" and\
     and [$SELF:Status_1] ne "manuell"                                   ## und gibt es keine manuelle Einschaltung\
     [LWP_Counter:pulseTimePerDay] < [LWP:RunTimePerDay] and\
     and [$SELF:Status_2] ne "Waschprogramm gestarted"                  ## und es läuft kein Waschprogramm\
     [Heizung:hotWaterTemperature] < 60 )\
     and [$SELF:ui_command_1] eq "---"\
\
    ({Log 3, "LWP_PV cmd_4 : LWP on"}\
    {fhem("".ReadingsVal("LWP","SetCmdOn",0))}\
    {fhem("set LWP on")}\
    {fhem("set Heizung hotWaterTemperatureTarget 60.0")}\
     )\
     )\
################################################################################################################\
     or [$SELF:ui_command_1] eq "05_2_Waschmaschine_Aus"                 ## Hier wird das uiTable select ausgewertet\
## 5 Signal für den PV-Modus der LWP einschalten.\
  ) {\
##\
DOELSEIF\
     ([LWP:LWP_Button] eq "on" )\
\
\
     ({Log 3, "LWP_PV cmd_5 PV : LWP on for manuel PV-Modus"}\
     if (AttrVal("$SELF","verbose",0) >=3)\
    {fhem("".ReadingsVal("LWP","SetCmdOn",0))}\
      {Log 3, "$SELF 05_2 : Keine PV-Zeit"};;\
    {fhem("set LWP on")}\
    {fhem("set Heizung hotWaterTemperatureTarget 60.0")}\
    )\
################################################################################################################\
## 6 Signal für den PV-Modus der LWP abschalten.\
##\
DOELSEIF\
    ([LWP:LWP_Button] eq "off" and\
    [$SELF:cmd_nr] eq "5"  )\
\
\
     ({Log 3, "LWP_PV cmd_6 PV : LWP off after manuel PV-Modus"}\
     del_Exec("PV_Modus_Ein_timer");;                                      ## Das Gerät wird nicht mehr eingeschaltet\
    {fhem("".ReadingsVal("LWP","SetCmdOff",0))}\
    PV_Modus_Aus_Waschmaschine();;\
    {fhem("set LWP off")}\
    {fhem("set Heizung hotWaterTemperatureTarget 50.0")}\
    )\
################################################################################################################\
## 7 Stop wait Timer für das Abschalten, wenn die LWP beim Starten noch anläuft\
##\
DOELSEIF\
  ([StromZaehler_Heizung:SMAEM1901401955_Bezug_Wirkleistung] > 300 and\
    [Heizung:opStateHeatPump1] eq "Wärmepumpe kommt" and\
    [Heizung:opStateHeatPump3] eq "Pumpenvorlauf" )\
\
\
    ({Log 3, "LWP_PV cmd_7 : Stop wait timer LWP"})\
    if ([$SELF:ui_command_1] eq "05_2_Waschmaschine_Aus") {\
################################################################################################################\
      set_Reading("ui_command_1","---");;                                  ## Hier wird das uiTable select wieder zurückgesetzt, ansonsten\
## 8 LWP Ende\
                                                                          ## kann das Kommando nicht sofort wiederholt werden\
##\
    }\
DOELSEIF\
   }\
   ([StromZaehler_Heizung:SMAEM1901401955_Bezug_Wirkleistung] < 300 and\
}\
    [LWP_Counter:pulseTimeIncrement] >= [LWP:RunTimeMin] and\
    ([Heizung:opStateHeatPump1] ne "Wärmepumpe läuft" or [Heizung:opStateHeatPump3] eq "Luftabtauen" ) and\
    ([$SELF:cmd_nr] eq "4" or [$SELF:cmd_nr] eq "5" or [$SELF:cmd_nr] eq "10") )\
\
\
    ({Log 3, "LWP_PV cmd_8 : LWP run finished"}\
    {fhem("".ReadingsVal("LWP","SetCmdOff",0))}\
    {fhem("set LWP off")}\
    {fhem("set Heizung hotWaterTemperatureTarget 50.0")}\
    {fhem("setreading LWP LWP_Button off")}\
    )\
################################################################################################################\
################################################################################################################\
## 9 LWP Zwangseinschalten: Sollte das Brauchwasser noch nicht aufgeheizt sein, wird um die Hysterese erhöht.\
## Geräte Startzeit durch Forecast verschieben. Der Forecast wird um 7:00 im Device PV_Schedule aktualisiert\
##  Dies kann passieren, wenn am Tag vorher der PV-Modus lief und dann das Wasser noch knapp über dem Mindestwert ist.\
##\
##\
DOELSEIF\
08___Startzeit_nach_forecast\
  ([Astro:ObsSeason] ne "Sommer" and [Astro:ObsSeason] ne "Frühling" and\
{if( !([$SELF:state] eq "off")                                          ## DOIF enabled\
    [[LWP:TimeEnd]] and\
    and\
    [Heizung:hotWaterTemperature] < 47 and\
    (\
    ([LWP_Counter:pulseTimePerDay] < [Pool:RunTimePerDay] or\
        [07:17]                                                         ## Der Forecast wird um 7:00 im Device PV_Schedule aktualisiert\
     [LWP_Counter:countsPerDay] eq 0) )\
      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]);;\
  }\
\
\
    ({Log 3, "LWP_PV cmd_9 : LWP on for water heating"}\
  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");;\
  }\
\
\
     {fhem("set Heizung hotWaterTemperatureTarget ". (ReadingsVal("Heizung","hotWaterTemperature",46)+4))}\
  if (AttrVal("$SELF","verbose",0) >=3)\
     {Log 3, "$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\
  }\
}\
\
\
    {Log 3, "LWP_PV cmd_9 : LWP hotWaterTemperatureTarget ".ReadingsVal("Heizung","hotWaterTemperatureTarget",0)}\
    )\
################################################################################################################\
################################################################################################################\
## 10 Hohe Priorität im Winter fuer die LWP\
## Definition von Sub Routinen\
##   Einschalten, wenn der Pool läuft, der Speicher geladen ist und noch Überschuss da ist.\
subs {\
##\
  sub PV_Modus_Ein_Waschmaschine() {                                                  ## PV-Modus Einschalten\
DOELSEIF\
    if (AttrVal("$SELF","verbose",0) >=3)\
     ([Astro:ObsSeason] eq "Winter" and\
      {Log 3, "$SELF sub  : On"};;\
    [PV_1:Total_PV_Power_reserve] >= 2000 and\
    fhem("set Waschmaschine_Counter pauseTimeIncrement 0");;\
    [shelly02:power_0] > 800 and\
    fhem("".ReadingsVal("$SELF","SetCmdOn",0));;\
    [PV_1:Act_state_of_charge] > 60 and\
    }\
    [Heizung:hotWaterTemperature] < 60 and \
\
    [$SELF:cmd_nr] ne "10" )\
  sub PV_Modus_Aus_Waschmaschine() {                                                  ## PV-Modus Ausschalten\
    if (AttrVal("$SELF","verbose",0) >=3)\
      {Log 3, "$SELF sub  : Off"};;\
    fhem("".ReadingsVal("$SELF","SetCmdOff",0));;\
    set_Reading("Status_1","Aus");;\
     set_Reading("Status_2","Steckdose ist ausgeschaltet");;\
  }\
}
attr Waschmaschine_PV_Perl DbLogExclude .*
attr Waschmaschine_PV_Perl alias Waschmaschine_PV_Perl
attr Waschmaschine_PV_Perl comment Version 2021.11.01 09:00
attr Waschmaschine_PV_Perl disable 0
attr Waschmaschine_PV_Perl event-on-change-reading .*
attr Waschmaschine_PV_Perl event_Readings Status_1:[$SELF:Status_1], Status_2:[$SELF:Status_2]
attr Waschmaschine_PV_Perl group PV Eigenverbrauch-Steuerung
attr Waschmaschine_PV_Perl icon scene_washing_machine
attr Waschmaschine_PV_Perl room Strom->Photovoltaik
attr Waschmaschine_PV_Perl sortby 4311
attr Waschmaschine_PV_Perl uiTable {\
package ui_Table;;\
  $TABLE = "style='width:100%;;'";;\
\
\
    ({Log 3, "LWP_PV cmd_10 : LWP Priorität"}\
  $TD{0..9}{0}    = "align='center' style='font-size:16px;;border-right-style:solid;;border-color:darkgreen;;border-right-width:2px;;width:26%'";;\
    {fhem("set LWP_PV cmd_4")}\
     )\
 
attr LWP_PV DbLogExclude .*
attr LWP_PV DbLogInclude state,cmd.*,Device,LWP_Status,wait_timer
attr LWP_PV alias LWP_PV
attr LWP_PV cmdState Maximalzeit pro Tag überschritten|Eigenverbrauch aus|Stop wait timer|Eigenverbrauch ein|LWP ein für manuellen PV-Modus|LWP aus nach manuellem PV-Modus|Stop wait timer fuer aus|LWP aus nach PV-Modus|LWP Brauchwasser nachheizen|LWP Priorität
attr LWP_PV do always
attr LWP_PV group PV Eigenverbrauch-Steuerung
attr LWP_PV icon sani_earth_source_heat_pump
attr LWP_PV room Strom->Photovoltaik
attr LWP_PV sortby 01
attr LWP_PV stateFormat state : LWP_Status : Brauchwasser e_Heizung_hotWaterTemperature °C
attr LWP_PV userReadings LWP_Status { ReadingsVal("Heizung","state","") }
attr LWP_PV verbose 5
attr LWP_PV wait 0:10:0:[LWP:PowerLevelMinTime]:0:0:900:0:0
</syntaxhighlight>
- RAW Definition LWP_Signale (Shelly Modul: shelly1pm)
<syntaxhighlight lang="Perl">
defmod shelly01 Shelly 192.168.178.55
attr shelly01 DbLogExclude .*
attr shelly01 DbLogInclude relay.*,power.*,energy.*
attr shelly01 alias LWP_Signale
attr shelly01 event-on-change-reading relay.*,power.*,energy.*,state,network
attr shelly01 group PV Eigenverbrauch-Steuerung
attr shelly01 icon taster_ch_1
attr shelly01 mode relay
attr shelly01 model shelly1pm
attr shelly01 room Shelly,Heizung->System,Strom->Photovoltaik
attr shelly01 sortby 02
attr shelly01 stateFormat {sprintf("\
<TABLE>\
\
\
<TR>\
   $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 VALIGN=\"TOP\" ALIGN=\"LEFT\" WIDTH=\"50\">\
  $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;;'";;\
    WebLink: %s\
   $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;;'";;\
   </TD>\
\
\
  <TD VALIGN=\"TOP\" ALIGN=\"RIGHT\" WIDTH=\"100\">\
sub FUNC_Status {\
     Gesamt 0: %08.2f KWh<br>\
    my($value, $min, $colorMin,  $statusMin,  $colorMiddel, $statusMiddle, $max, $colorMax, $statusMax)=@_;;\
   </TD>\
    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;;\
   }\
\
\
  <TD VALIGN=\"TOP\" ALIGN=\"RIGHT\" WIDTH=\"70\">\
}\
    Relais 0: %s %06.1f Watt<br>\
  </TD>\
</TR>\
\
\
</TABLE>\
"$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")\
ReadingsVal($name,"WebLink","none") ,\
|"<dd>Sommer, Winter Start / Ende</dd>"| ""|""|widget([$SELF:TimeStartSummer],"time").widget([$SELF:TimeStartWinter],"time").|widget([$SELF:TimeEndSummer],"time").widget([$SELF:TimeEndWinter],"time")
ReadingsVal($name,"energy_0",0)/1000,\
attr Waschmaschine_PV_Perl verbose 3
(ReadingsVal($name,"relay","") eq "off") ? "<span style='color:#FF0000'>off</span>":"<span style='color:#00FF00'>on</span>",\
 
ReadingsVal($name,"power",0),\
setstate Waschmaschine_PV_Perl 2022-08-11 09:48:48 PowerLevelMinTime 300
)}
setstate Waschmaschine_PV_Perl 2022-09-26 13:47:21 PowerLimitOff 250
attr shelly01 userReadings WebLink:network { my $ip=ReadingsVal($NAME,"network","");; $ip =~ s/connected to //gs;; $ip =~ s/<[^>]*>//gs;; return("<html><a href='http://".$ip."/'>WEB</a></html>") }
setstate Waschmaschine_PV_Perl 2022-10-06 17:45:40 PowerLimitOn 2000
attr shelly01 webCmd |
setstate Waschmaschine_PV_Perl 2022-08-11 09:52:38 RunTimeMin 5400
</pre>
setstate Waschmaschine_PV_Perl 2022-08-11 09:52:45 RunTimePerDay 19200
==== RAW Definition LWP_Counter (HourCounter Modul) ====
setstate Waschmaschine_PV_Perl 2022-08-11 12:27:42 SetCmdOff set shelly03 off 0
<pre>
setstate Waschmaschine_PV_Perl 2022-08-11 12:27:19 SetCmdOn set shelly03 on 0
defmod LWP_Counter HourCounter LWP:on.* LWP:off
setstate Waschmaschine_PV_Perl 2022-11-01 09:21:23 Status_1 -
attr LWP_Counter DbLogExclude .*
setstate Waschmaschine_PV_Perl 2022-11-01 09:21:25 Status_2 -
attr LWP_Counter alias LWP_Counter
setstate Waschmaschine_PV_Perl 2022-11-01 07:17:00 TimeEnd 18:00
attr LWP_Counter event-min-interval .*:600
setstate Waschmaschine_PV_Perl 2022-10-06 17:32:31 TimeEndSummer 18:00
attr LWP_Counter event-on-change-reading .*
setstate Waschmaschine_PV_Perl 2022-08-11 09:46:58 TimeEndWinter 16:00
attr LWP_Counter group PV Eigenverbrauch-Steuerung
setstate Waschmaschine_PV_Perl 2022-11-01 07:17:00 TimeStart 08:00
attr LWP_Counter icon time_timer
setstate Waschmaschine_PV_Perl 2022-08-11 09:48:05 TimeStartSummer 08:00
attr LWP_Counter interval 5
setstate Waschmaschine_PV_Perl 2022-08-11 09:46:54 TimeStartWinter 10:00
attr LWP_Counter room Strom->Photovoltaik
setstate Waschmaschine_PV_Perl 2022-11-01 09:21:25 ui_command_1 ---
attr LWP_Counter sortby 03
setstate Waschmaschine_PV_Perl 2022-10-06 19:34:48 ui_command_1_before ---
attr LWP_Counter verbose 0
</syntaxhighlight>
</syntaxhighlight>
==== RAW Definition rg_LWP_Status (readingsGroup Modul) ====
<syntaxhighlight lang="Perl">
defmod rg_LWP_Status readingsGroup <Device>,<Information>,<Wert> LWP:<Status>,state LWP:<PowerLevelMinTime>,PowerLevelMinTime LWP:<PowerLimitOn>,PowerLimitOn LWP:<PowerLimitOff>,PowerLimitOff LWP:<TimeStart>,!TimeStart LWP:<TimeEnd>,!TimeEnd LWP:<RunTimeMin>,RunTimeMin LWP_Counter:<RunTimeMin>,pulseTimeIncrement LWP:<RunTimePerDay>,RunTimePerDay LWP_Counter:<RunTimePerDay>,pulseTimePerDay LWP_PV:<wait>,wait_timer LWP_PV:<TimeStart>,timer_01_c04 LWP_PV:<TimeEnd>,timer_02_c04
attr rg_LWP_Status DbLogExclude .*
attr rg_LWP_Status alias Status LuftWärmePumpe Eigenverbrauch
attr rg_LWP_Status commands {state  => 'state:on,off',\
PowerLevelMinTime => 'PowerLevelMinTime:selectnumbers,60,60,600,0,lin',\
PowerLimitOn => 'PowerLimitOn:selectnumbers,1000,250,4000,0,lin',\
PowerLimitOff => 'PowerLimitOff:selectnumbers,1000,250,4000,0,lin',\
RunTimeMin => 'RunTimeMin:selectnumbers,300,300,14400,0,lin',\
RunTimePerDay => 'RunTimePerDay:selectnumbers,300,300,28800,0,lin',\
TimeStart => 'TimeStart:time',\
TimeEnd => 'TimeEnd:time'}
attr rg_LWP_Status group PV Status
attr rg_LWP_Status nameStyle style="color:grey"
attr rg_LWP_Status room Strom->Photovoltaik
attr rg_LWP_Status sortby 01
attr rg_LWP_Status style style="font-size:18px"
</syntaxhighlight>


=== Beispiel Pool===
==== RAW Definition Waschmaschine_Signale (Shelly Modul: shelly1pm) ====
==== RAW Definition Pool_Softube (dummy Modul) ====
<syntaxhighlight lang="Perl">
<syntaxhighlight lang="Perl">
defmod Pool dummy
defmod shelly03 Shelly 192.168.178.55
attr Pool DbLogExclude .*
attr shelly03 DbLogExclude .*
attr Pool DbLogInclude state
attr shelly03 DbLogInclude relay.*,power.*,energy.*
attr Pool alias Pool_Softube
attr shelly03 alias Waschmaschine
attr Pool event-on-change-reading .*
attr shelly03 comment Version 2020.10.19 18:28
attr Pool group PV Eigenverbrauch
attr shelly03 event-on-change-reading relay.*,power.*,energy.*,network
attr Pool icon scene_swimming
attr shelly03 group PV Eigenverbrauch-Steuerung
attr Pool readingList Pool_Button PowerLevelMinTime PowerLimitOn PowerLimitOff RunTimeMin RunTimePerDay RunTimePerDaySummer RunTimePerDayWinter SetCmdOff SetCmdOn TimeStart TimeEnd
attr shelly03 icon taster_ch_1
attr Pool room Strom->Photovoltaik
attr shelly03 interval 60
attr Pool setList Pool_Button:uzsuToggle,on,off PowerLevelMinTime:slider,30,30,300 PowerLimitOn:slider,500,100,1500 PowerLimitOff:slider,0,100,1000 RunTimeMin:slider,300,300,7200 RunTimePerDay:slider,900,300,64800 RunTimePerDaySummer:slider,900,300,7200 RunTimePerDayWinter:slider,3600,900,64800 SetCmdOff SetCmdOn TimeStart:time TimeEnd:time
attr shelly03 mode relay
attr Pool sortby 11
attr shelly03 model shelly1pm
attr Pool stateFormat state
attr shelly03 room Shelly,Strom->Photovoltaik
attr Pool verbose 0
attr shelly03 sortby 432
attr Pool webCmd Pool_Button
attr shelly03 stateFormat {\
 
my $link = ReadingsVal($name,"WebLink","none");;\
setstate Pool off
\
setstate Pool 2020-08-28 13:30:10 Pool_Button off
my $e0 = sprintf("%08.2f KWh",ReadingsVal($name,"energy_Total",0)/1000);;\
setstate Pool 2019-12-02 10:31:26 PowerLevelMinTime 600
my $r0 = (ReadingsVal($name,"relay","") eq "off") ? "<span style='color:red'>off</span>":"<span style='color:green'>on</span>";;\
setstate Pool 2019-11-13 10:58:18 PowerLimitOff 800
my $p0 = sprintf("%06.1f Watt",ReadingsVal($name,"power",0));;\
setstate Pool 2019-11-06 11:49:00 PowerLimitOn 1000
\
setstate Pool 2020-08-14 16:26:09 RunTimeMin 1800
"<html><table border=2 bordercolor='darkgreen' cellspacing=0 style='width: 100%'>\
setstate Pool 2020-08-28 06:15:00 RunTimePerDay 3600
<colgroup>\
setstate Pool 2020-08-16 13:17:45 RunTimePerDaySummer 3600
  <col span='1' style='width: 30%;;'>\
setstate Pool 2020-08-25 19:32:33 RunTimePerDayWinter 14400
  <col span='1' style='width: 30%;;'>\
setstate Pool 2019-08-01 14:18:08 SetCmdOff set shelly02 off 0
  <col span='1' style='width: 20%;;'>\
setstate Pool 2019-08-02 09:33:06 SetCmdOn set shelly02 on 0
</colgroup>\
setstate Pool 2019-10-31 21:53:28 TimeEnd 16:00
<tr>\
setstate Pool 2020-04-08 18:19:29 TimeStart 12:30
  <td style='text-align:left'>\
setstate Pool 2020-08-28 14:13:02 state off
  </td>\
  <td style='text-align:left'>\
    WebLink: $link\
  </td>\
  <td style='text-align:right'>\
    Waschmaschine Gesamt 0: $e0<br>\
  <td style='text-align:right'>\
    Relais 0: $r0 $p0<br>\
  </td>\
</tr>\
</table>\
</html>"\
}
attr shelly03 userReadings WebLink:network.* { my $ip=ReadingsVal($name,"network","na");; $ip =~ s/connected to //gs;; $ip =~ s/<[^>]*>//gs;; return("<a href='http://".$ip."/'>".$ip."</a>") },\
\
energy_Total:energy.* monotonic { ReadingsVal($name,"energy",0) }
attr shelly03 webCmd |
</syntaxhighlight>
</syntaxhighlight>
==== RAW Definition Pool_PV (DOIF Modul) ====
==== RAW Definition Waschmaschine_Counter (HourCounter Modul) ====
<syntaxhighlight lang="Perl">
<syntaxhighlight lang="Perl">
defmod Pool_PV DOIF ################################################################################################################\
defmod Waschmaschine_Counter HourCounter shelly03:power:\s([0-9]{2,}\.?[0-9]{0,}|[4-9]{1}\.?[0-9]{0,}) shelly03:power:\s([0-3]{1}\.?[0-9]{0,}|[0-3]{1})
## 1 Eigenverbrauch abschalten: wenn Mindestlaufzeit erreicht wurde und Maximallaufzeit pro Tag erreicht ist\
attr Waschmaschine_Counter DbLogExclude .*
##\
attr Waschmaschine_Counter alias Waschmaschine_Counter
([Pool_Counter:pulseTimePerDay] >= [Pool:RunTimePerDay] and\
attr Waschmaschine_Counter comment Version 2020.10.19 18:28\
  [Pool_Counter:pulseTimeIncrement] >= [Pool:RunTimeMin] and\
  [Pool:state] eq "on" and [Pool:Pool_Button] eq "off" )\
\
    ({Log 3, "Pool_PV cmd_1 : Pool off"}\
    {fhem("".ReadingsVal("Pool","SetCmdOff",0))}\
    {fhem("set Pool off")} )\
################################################################################################################\
## 2 Eigenverbrauch abschalten: wenn Mindestlaufzeit erreicht wurde und die PV-Produktion unter dem Mindestbedarf ist.\
##  Bei Pool Nutzung und Pflegeprogramm wird nicht abgeschaltet.\
##\
DOELSEIF\
([PV_1:Total_PV_Power_reserve] < [Pool:PowerLimitOff] and\
  [Pool_Counter:pulseTimeIncrement] >= [Pool:RunTimeMin] and\
  [Pool:state] eq "on" and \
  [$SELF:cmd_nr] ne "5" and [$SELF:cmd_nr] ne "7" and [$SELF:cmd_nr] ne "10" )\
\
    ({Log 3, "Pool_PV cmd_2 : Pool off"}\
    {fhem("".ReadingsVal("Pool","SetCmdOff",0))}\
    {fhem("set Pool off")}\
    {fhem("setreading Pool Pool_Button 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] < [Pool:PowerLimitOff] and\
  [$SELF:wait_timer] ne "no timer" and\
  [$SELF:wait_timer] ne "" and\
  [Pool:state] eq "off" )\
\
    ({Log 3, "Pool_PV cmd_3 : Stop wait timer Pool"})\
################################################################################################################\
## 4 Eigenverbrauch einschalten: wenn PV-Produktion über dem Mindestbedarf ist und PV-Strom ins Netz eingespeist wird\
##    und die Laufzeit pro Tag noch nicht erreicht ist;; Bei über 7000 Watt Einspeisung sofort aktivieren\
##\
DOELSEIF\
(([PV_1:Total_PV_Power_reserve] >= [Pool:PowerLimitOn] and\
  [[Pool:TimeStart]-[Pool:TimeEnd]] and\
  [Pool:state] eq "off" and\
  [Pool_Counter:pulseTimePerDay] < [Pool:RunTimePerDay]\
  ) or\
  [PV_1:Total_active_power_(powermeter)] <= -7000\
)\
\
    ({Log 3, "Pool_PV cmd_4 : Pool on"}\
    {fhem("set Pool_Counter pulseTimeIncrement 0")}\
    {fhem("".ReadingsVal("Pool","SetCmdOn",0))}\
    {fhem("set Pool on")} )\
################################################################################################################\
## 5 Steckdose für die Benutzung des Pools einschalten.\
##\
DOELSEIF\
([Pool:Pool_Button] eq "on" )\
\
    ({Log 3, "Pool_PV cmd_5 : Pool on for usage"}\
    {fhem("".ReadingsVal("Pool","SetCmdOn",0))}\
    {fhem("set Pool on")} )\
################################################################################################################\
## 6 Steckdose des Pools abschalten.\
##\
DOELSEIF\
([Pool:Pool_Button] eq "off" and\
  [$SELF:cmd_nr] eq "5" or [$SELF:cmd_nr] eq "7")\
\
    ({Log 3, "Pool_PV cmd_6 : Pool off after usage"}\
    {fhem("".ReadingsVal("Pool","SetCmdOff",0))}\
    {fhem("set Pool off")} )\
################################################################################################################\
## 7 Stop wait Timer für das Abschalten, wenn die Pumpe beim Starten noch anläuft\
##\
DOELSEIF\
([shelly02:power_0] > 10 and\
  ([$SELF:cmd_nr] eq "5" or [$SELF:cmd_nr] eq "7" or [$SELF:cmd_nr] eq "9") and\
  [$SELF:wait_timer] ne "no timer" and\
  [$SELF:wait_timer] ne "" and\
  [Pool:state] eq "on" )\
\
    ({Log 3, "Pool_PV cmd_7 : Stop wait timer Pool"})\
################################################################################################################\
## 8 Pool Ende\
##\
DOELSEIF\
([shelly02:power_0] < 10 and\
  ([$SELF:cmd_nr] eq "5" or [$SELF:cmd_nr] eq "7" or [$SELF:cmd_nr] eq "9") )\
\
    ({Log 3, "Pool_PV cmd_8 : Pool run finished"}\
    {fhem("".ReadingsVal("Pool","SetCmdOff",0))}\
    {fhem("set Pool off")}\
    {fhem("setreading Pool Pool_Button off")} )\
################################################################################################################\
## 9 Pflege Zwangseinschaltung: Es muss mindestens einmal pro Tag eingeschaltet werden, auch wenn kein PV Strom vorhanden war.\
##\
DOELSEIF\
([[Pool:TimeEnd]] and\
  ([Pool_Counter:pulseTimePerDay] < [Pool:RunTimePerDay] or\
  [Pool_Counter:countsPerDay] eq 0)\
)\
\
    ({Log 3, "Pool_PV cmd_9 : Pool on for maintanance"}\
    {fhem("set Pool_Counter pulseTimeIncrement 0")}\
    {fhem("".ReadingsVal("Pool","SetCmdOn",0))}\
    {fhem("set Pool on")} )\
################################################################################################################\
## 10 Pflege Zwangseinschaltung bei günstigem Strom nachts im Winter\
##\
DOELSEIF\
([Astro:ObsSeason] eq "Winter" and\
  [Strom_Kosten:aWATTar_Trigger] eq "on" and\
  [22:00-05:00]\
)\
\
    ({Log 3, "Pool_PV cmd_10 : Pool on for maintanance by aWATTar"}\
    {fhem("set Pool_Counter pulseTimeIncrement 0")}\
    {fhem("".ReadingsVal("Pool","SetCmdOn",0))}\
    {fhem("set Pool on")} )\
################################################################################################################\
## 11 Abschaltung bei teurem Strom\
##\
DOELSEIF\
([Astro:ObsSeason] eq "Winter" and\
  [Strom_Kosten:aWATTar_Trigger] eq "off" and\
  [$SELF:cmd_nr] eq "10"\
)\
\
    ({Log 3, "Pool_PV cmd_11 : Pool off after maintanance by aWATTar"}\
    {fhem("".ReadingsVal("Pool","SetCmdOff",0))}\
    {fhem("set Pool off")} )\
################################################################################################################\
## 12 Pool durch kürzere Laufzeit abkühlen lassen\
##\
DOELSEIF\
([06:15] and [Heizung:averageAmbientTemperature])\
\
    (\
    { if ( [Heizung:averageAmbientTemperature] >= 18 )\
        {fhem("setreading Pool RunTimePerDay ".ReadingsVal("Pool","RunTimePerDaySummer",0) )}\
      else \
        {fhem("setreading Pool RunTimePerDay ".ReadingsVal("Pool","RunTimePerDayWinter",0) )}\
    },\
    {Log 3, "Pool_PV cmd_12 : Pool RunTimePerDay switched"}\
    )\
\
 
attr Pool_PV DbLogExclude .*
attr Pool_PV DbLogInclude cmd.*,state,cmd.*,Device,Pool_Pumpe_Status,wait_timer
attr Pool_PV alias Pool_PV
attr Pool_PV cmdState Maximalzeit pro Tag überschritten|Eigenverbrauch aus|Stop wait timer|Eigenverbrauch freigegeben|Pool ein für Benutzung|Pool aus nach Benutzung|Stop wait timer für aus|Pool aus|Pflegemodus ohne PV|Strombörse ein|Strombörse aus|RunTimePerDay switched
attr Pool_PV disable 0
attr Pool_PV do always
attr Pool_PV event-on-change-reading .*
attr Pool_PV group PV Eigenverbrauch-Steuerung
attr Pool_PV icon scene_swimming
attr Pool_PV room Strom->Photovoltaik
attr Pool_PV sortby 11
attr Pool_PV stateFormat state : Pool_Pumpe_Status
attr Pool_PV userReadings Pool_Pumpe_Status { ReadingsVal("shelly02","power_0",0)>10 ? "Pool_Pumpe_laeuft" : "Pool_Pumpe_aus"}
attr Pool_PV verbose 0
attr Pool_PV wait 0:10:0:[Pool:PowerLevelMinTime]:0:0:0:300:0:300
</syntaxhighlight>
==== RAW Definition Pool_Signale (Shelly Modul: shelly1pm) ====
<syntaxhighlight lang="Perl">
defmod shelly02 Shelly 192.168.178.52
attr shelly02 DbLogExclude .*
attr shelly02 DbLogInclude relay.*,power.*,energy.*
attr shelly02 alias Pool_Signale
attr shelly02 comment relais_0 => Pool limit 1000 W\
relail_1 => Terrasse Lichterkette limit 100 W
attr shelly02 event-on-change-reading relay.*,power.*,energy.*,state,network
attr shelly02 group PV Eigenverbrauch-Steuerung
attr shelly02 icon taster_ch_1
attr shelly02 mode relay
attr shelly02 model shelly2.5
attr shelly02 room Shelly,Strom->Photovoltaik
attr shelly02 sortby 12
attr shelly02 stateFormat {sprintf("\
<TABLE>\
\
<TR>\
  <TD VALIGN=\"TOP\" ALIGN=\"LEFT\" WIDTH=\"50\">\
    Status: %s<br>\
    WebLink: %s\
  </TD>\
\
  <TD VALIGN=\"TOP\" ALIGN=\"RIGHT\" WIDTH=\"100\">\
    Gesamt 0: %08.2f KWh<br>Gesamt 1: %08.2f KWh\
  </TD>\
\
  <TD VALIGN=\"TOP\" ALIGN=\"RIGHT\" WIDTH=\"70\">\
    Relais 0: %s %06.1f Watt<br>\
    Relais 1: %s %06.1f Watt<br>\
  </TD>\
</TR>\
\
</TABLE>\
" ,\
(ReadingsVal($name,"state","none") eq "OK") ? "<span style='color:#00FF00'>OK</span>":"<span style='color:#FF0000'>Error</span>",\
ReadingsVal($name,"WebLink","none") ,\
ReadingsVal($name,"energy_0",0)/1000,\
ReadingsVal($name,"energy_1",0)/1000,\
(ReadingsVal($name,"relay_0","") eq "off") ? "<span style='color:#FF0000'>off</span>":"<span style='color:#00FF00'>on</span>",\
    ReadingsVal($name,"power_0",0),\
(ReadingsVal($name,"relay_1","") eq "off") ? "<span style='color:#FF0000'>off</span>":"<span style='color:#00FF00'>on</span>",\
    ReadingsVal($name,"power_1",0)\
)}
attr shelly02 userReadings WebLink:network { my $ip=ReadingsVal($name,"network","");; $ip =~ s/connected to //gs;; $ip =~ s/<[^>]*>//gs;; return("<html><a href='http://".$ip."/'>WEB</a></html>") }
attr shelly02 verbose 0
attr shelly02 webCmd |
</syntaxhighlight>
==== RAW Definition Pool_Counter (HourCounter Modul) ====
<syntaxhighlight lang="Perl">
defmod Pool_Counter HourCounter shelly02:power_0:\s[0-9]{2,}(\.[0-9]{1,2})*$  shelly02:power_0:\s[0-9]{1}(\.[0-9]{1,2})*$
attr Pool_Counter DbLogExclude .*
attr Pool_Counter alias Pool_Counter
attr Pool_Counter comment On und Off des Pools werden direkt über den Shelly Stromverbrauch getriggert.
attr Pool_Counter event-on-change-reading .*
attr Pool_Counter group PV Eigenverbrauch-Steuerung
attr Pool_Counter icon time_timer
attr Pool_Counter interval 5
attr Pool_Counter room Strom->Photovoltaik
attr Pool_Counter sortby 13
attr Pool_Counter verbose 0
</syntaxhighlight>
==== RAW Definition rg_Pool_Status (readingsGroup Modul) ====
<syntaxhighlight lang="Perl">
defmod rg_Pool_Status readingsGroup <Device>,<Information>,<Wert> Pool:<Status>,!state Pool:<PowerLevelMinTime>,!PowerLevelMinTime Pool:<PowerLimitOn>,!PowerLimitOn Pool:<PowerLimitOff>,!PowerLimitOff Pool:<TimeStart>,!TimeStart Pool:<TimeEnd>,!TimeEnd Pool:<RunTimeMin>,!RunTimeMin Pool_Counter:<RunTimeMin>,!pulseTimeIncrement Pool:<RunTimePerDay>,!RunTimePerDay Pool:<RunTimePerDaySummer>,!RunTimePerDaySummer Pool:<RunTimePerDayWinter>,!RunTimePerDayWinter Pool_Counter:<RunTimePerDay>,!pulseTimePerDay Pool_PV:<wait>,wait_timer Pool_PV:<TimeStart>,timer_01_c04 Pool_PV:<TimeEnd>,timer_02_c04
attr rg_Pool_Status DbLogExclude .*
attr rg_Pool_Status alias Status Softube Pool Eigenverbrauch
attr rg_Pool_Status commands {state  => 'state:on,off',\
PowerLevelMinTime => 'PowerLevelMinTime:selectnumbers,60,30,600,0,lin',\
PowerLimitOn => 'PowerLimitOn:selectnumbers,500,250,1500,0,lin',\
PowerLimitOff => 'PowerLimitOff:selectnumbers,0,100,1000,0,lin',\
RunTimeMin => 'RunTimeMin:selectnumbers,300,300,14400,0,lin',\
RunTimePerDaySummer => 'RunTimePerDaySummer:selectnumbers,300,300,3600,0,lin',\
RunTimePerDayWinter => 'RunTimePerDayWinter:selectnumbers,3600,900,64800,0,lin',\
TimeStart => 'TimeStart:time',\
TimeEnd => 'TimeEnd:time'}
attr rg_Pool_Status group PV Status
attr rg_Pool_Status nameStyle style="color:grey"
attr rg_Pool_Status room Strom->Photovoltaik
attr rg_Pool_Status sortby 02
attr rg_Pool_Status style style="font-size:18px"
</syntaxhighlight>
 
=== Beispiel Waschmaschine (mit Walzenschalter ;-) ) ===
==== RAW Definition Waschmaschine (dummy Modul) ====
<syntaxhighlight lang="Perl">
defmod Waschmaschine dummy
attr Waschmaschine DbLogExclude .*
attr Waschmaschine DbLogInclude state
attr Waschmaschine alias Waschmaschine
attr Waschmaschine group PV Eigenverbrauch
attr Waschmaschine icon scene_washing_machine
attr Waschmaschine readingList Waschmaschine_Button PowerLevelMinTime PowerPhaseUse PowerLimitOn PowerLimitOff RunTimeMin RunTimePerDay SetCmdOff SetCmdOn TimeStart TimeEnd
attr Waschmaschine room Strom->Photovoltaik
attr Waschmaschine setList Waschmaschine_Button:uzsuToggle,on,off PowerLevelMinTime:slider,30,30,300 PowerLimitOn:slider,250,250,2000 PowerLimitOff:slider,0,250,1000 RunTimeMin:slider,300,300,7200 RunTimePerDay:slider,7200,300,28800 SetCmdOff SetCmdOn TimeStart:time TimeEnd:time
attr Waschmaschine sortby 12
attr Waschmaschine stateFormat state
attr Waschmaschine verbose 0
attr Waschmaschine webCmd Waschmaschine_Button
 
setstate Waschmaschine off
setstate Waschmaschine 2019-12-02 10:29:49 PowerLevelMinTime 300
setstate Waschmaschine 2019-11-06 10:56:22 PowerLimitOff 250
setstate Waschmaschine 2020-04-20 13:06:04 PowerLimitOn 2000
setstate Waschmaschine 2019-12-02 15:01:18 RunTimeMin 5400
setstate Waschmaschine 2020-04-02 13:59:34 RunTimePerDay 19200
setstate Waschmaschine 2020-01-01 16:29:10 SetCmdOff set shelly03 off 0
setstate Waschmaschine 2020-01-01 16:29:24 SetCmdOn set shelly03 on 0
setstate Waschmaschine 2019-08-01 14:25:25 TimeEnd 18:00
setstate Waschmaschine 2019-10-28 09:13:30 TimeStart 09:00
setstate Waschmaschine 2020-08-27 16:17:23 Waschmaschine_Button off
setstate Waschmaschine 2020-08-28 16:29:42 state off
</syntaxhighlight>
==== RAW Definition Waschmaschine_PV (DOIF Modul) ====
<syntaxhighlight lang="Perl">
defmod Waschmaschine_PV DOIF ################################################################################################################\
## 1 Eigenverbrauch sperren: wenn Mindestlaufzeit erreicht wurde und Maximallaufzeit pro Tag erreicht ist\
##  jedoch nicht wenn manueller Betrieb aktiv ist.\
##\
([Waschmaschine_Counter:pulseTimePerDay] >= [Waschmaschine:RunTimePerDay] and\
  [Waschmaschine_Counter:pulseTimeIncrement] >= [Waschmaschine:RunTimeMin] and\
  [Waschmaschine:state] eq "on" and [Waschmaschine:Waschmaschine_Button] eq "off" )\
\
    ({Log 3, "Waschmaschine cmd_1 : Eigenverbrauch sperren"}\
    {fhem("".ReadingsVal("Waschmaschine","SetCmdOff",0))}\
    {fhem("setreading Waschmaschine_PV Waschmaschine_Status Steckdose ist ausgeschaltet")}\
    {fhem("set Waschmaschine off")}\
    )\
################################################################################################################\
## 2 Eigenverbrauch sperren: wenn Mindestlaufzeit erreicht wurde und die PV-Produktion unter dem Mindestbedarf ist\
##  ausser bei manuellem Einschalten und wenn das Waschprogramm bereits läuft\
##\
## ([PV_1:Total_PV_Power_reserve] < [Waschmaschine:PowerLimitOff] and\
##  [Waschmaschine_Counter:pulseTimeIncrement] >= [Waschmaschine:RunTimeMin] and\
##\
DOELSEIF\
([PV_1:Total_PV_Power_reserve] < [Waschmaschine:PowerLimitOn] and\
  [Waschmaschine:state] eq "on" and\
  [$SELF:cmd_nr] ne "5" and [$SELF:cmd_nr] ne "7" )\
\
    ({Log 3, "Waschmaschine cmd_2 : Eigenverbrauch sperren"}\
    {fhem("".ReadingsVal("Waschmaschine","SetCmdOff",0))}\
    {fhem("setreading Waschmaschine_PV Waschmaschine_Status Steckdose ist ausgeschaltet")}\
    {fhem("set Waschmaschine 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] < [Waschmaschine:PowerLimitOff] and\
  [Waschmaschine_PV:wait_timer] ne "no timer" and\
  [Waschmaschine_PV:wait_timer] ne "" and\
  [Waschmaschine:state] eq "off" )\
\
    ({Log 3, "Waschmaschine cmd_3 : Waschmaschine 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] > [Waschmaschine:PowerLimitOn] and\
  [Waschmaschine:state] eq "off" and\
  [[Waschmaschine:TimeStart]-[Waschmaschine:TimeEnd]] and\
  [Waschmaschine_Counter:pulseTimePerDay] < [Waschmaschine:RunTimePerDay] )\
\
    ({Log 3, "Waschmaschine cmd_4 : Waschmaschine freigabe"}\
    {fhem("".ReadingsVal("Waschmaschine","SetCmdOn",0))}\
    {fhem("setreading Waschmaschine_PV Waschmaschine_Status Steckdose ist eingeschaltet")}\
    {fhem("set Waschmaschine on")}\
    )\
################################################################################################################\
## 5 Steckdose manuell für die Benutzung der Waschmaschine einschalten.\
##\
DOELSEIF\
([Waschmaschine:Waschmaschine_Button] eq "on" )\
\
    ({Log 3, "Waschmaschine cmd_5 : Waschmaschine manuell ein"}\
    {fhem("".ReadingsVal("Waschmaschine","SetCmdOn",0))}\
    {fhem("setreading Waschmaschine_PV Waschmaschine_Status Steckdose ist eingeschaltet")}\
    {fhem("set Waschmaschine on")}\
    )\
################################################################################################################\
## 6 Steckdose der Waschmaschine manuell abschalten.\
##\
DOELSEIF\
([Waschmaschine:Waschmaschine_Button] eq "off" and\
  ([$SELF:cmd_nr] eq "5" or [$SELF:cmd_nr] eq "7") )\
\
    ({Log 3, "Waschmaschine cmd_6 : Waschmaschine manuell aus"}\
    {fhem("".ReadingsVal("Waschmaschine","SetCmdOff",0))}\
    {fhem("setreading Waschmaschine_PV Waschmaschine_Status Steckdose ist ausgeschaltet")}\
    {fhem("set Waschmaschine off")}\
    )\
################################################################################################################\
## 7 Statuswechsel wenn das Waschprogramm gestartet ist\
##\
DOELSEIF\
([shelly03:power] > 0 and\
  [shelly03:power_Waschmaschine_avg] < 70 and\
  [$SELF:cmd_nr] ne "7")\
\
    ({Log 3, "Waschmaschine cmd_7 : Waschmaschine Programm gestarted"}\
    {fhem("setreading Waschmaschine_PV Waschmaschine_Status Waschprogramm gestartet")}\
    )\
################################################################################################################\
## 8 Abschalten der Steckdose beim Waschprogramm Ende\
##\
DOELSEIF\
([shelly03:power] == 0 and\
  [shelly03:power_Waschmaschine_avg] > 0 and\
  [$SELF:cmd_nr] eq "7" )\
\
    ({Log 3, "Waschmaschine cmd_8 : Waschmaschine aus"}\
    {fhem("".ReadingsVal("Waschmaschine","SetCmdOff",0))}\
    {fhem("set Waschmaschine off")}\
    {fhem("setreading Waschmaschine Waschmaschine_Button off")}\
    {fhem("setreading Waschmaschine_PV Waschmaschine_Status Waschprogramm beendet")}\
    )\
################################################################################################################\
## 9 Abschalten der Steckdose wenn die Waschmaschine nicht gebraucht wurde\
##\
DOELSEIF\
([PV_1:Total_PV_Power_reserve] < [Waschmaschine:PowerLimitOn] and\
  [[Waschmaschine:TimeEnd]-[Waschmaschine:TimeStart]] and\
  [Waschmaschine:state] eq "on" and\
  [$SELF:cmd_nr] ne "5" and [$SELF:cmd_nr] ne "7" )\
\
    ({Log 3, "Waschmaschine cmd_9 : Eigenverbrauch sperren"}\
    {fhem("".ReadingsVal("Waschmaschine","SetCmdOff",0))}\
    {fhem("set Waschmaschine off")}\
    {fhem("setreading Waschmaschine Waschmaschine_Button off")}\
    {fhem("setreading Waschmaschine_PV Waschmaschine_Status Steckdose ist ausgeschaltet")}\
    )
attr Waschmaschine_PV DbLogExclude .*
attr Waschmaschine_PV DbLogInclude state,STATE,cmd.*,Device,Waschmaschine_Status,wait_timer
attr Waschmaschine_PV alias Waschmaschine_PV
attr Waschmaschine_PV cmdState Maximalzeit pro Tag überschritten|Eigenverbrauch gesperrt|Stop wait timer|Eigenverbrauch freigegeben|Waschmaschine manuell ein|Waschmaschine manuell aus|Waschmaschine laeuft|Waschmaschine aus|Eigenverbrauch gesperrt
attr Waschmaschine_PV do always
attr Waschmaschine_PV group PV Eigenverbrauch-Steuerung
attr Waschmaschine_PV icon scene_washing_machine
attr Waschmaschine_PV room Strom->Photovoltaik
attr Waschmaschine_PV sortby 21
attr Waschmaschine_PV stateFormat state : Waschmaschine_Status
attr Waschmaschine_PV verbose 0
attr Waschmaschine_PV wait 0:0:0:[Waschmaschine:PowerLevelMinTime]:0:30:0:60:30
</syntaxhighlight>
==== RAW Definition Waschmaschine_Signale (Shelly Modul: shelly1pm) ====
<syntaxhighlight lang="Perl">
defmod shelly03 Shelly 192.168.178.54
attr shelly03 DbLogExclude .*
attr shelly03 DbLogInclude relay.*,power.*,energy.*
attr shelly03 alias Waschmaschine_Signale
attr shelly03 event-on-change-reading relay.*,power.*,energy.*,network
attr shelly03 group PV Eigenverbrauch-Steuerung
attr shelly03 icon taster_ch_1
attr shelly03 interval 60
attr shelly03 mode relay
attr shelly03 model shelly1pm
attr shelly03 room Shelly,Strom->Photovoltaik
attr shelly03 sortby 22
attr shelly03 stateFormat {sprintf("\
<TABLE>\
\
<TR>\
  <TD VALIGN=\"TOP\" ALIGN=\"LEFT\" WIDTH=\"50\">\
    WebLink: %s\
  </TD>\
\
\
  <TD VALIGN=\"TOP\" ALIGN=\"RIGHT\" WIDTH=\"100\">\
Waschmaschine_PV:.*laeuft Waschmaschine_PV:Waschmaschine_Status:.*[beendet|ausgeschaltet]\
    Gesamt 0: %08.2f KWh<br>\
  </TD>\
\
  <TD VALIGN=\"TOP\" ALIGN=\"RIGHT\" WIDTH=\"70\">\
    Relais 0: %s %06.1f Watt<br>\
  </TD>\
</TR>\
\
\
</TABLE>\
Version 2022.09.20 16:00\
" ,\
Bis 3.9 Watt ist das Gerät aus, ab 4 Watt ist es eingeschaltet\
ReadingsVal($name,"WebLink","none") ,\
An  => shelly03:power:\s([0-9]{2,}\.?[0-9]{0,}|[4-9]{1}\.?[0-9]{0,})\
ReadingsVal($name,"energy",0)/1000,\
Aus => shelly03:power:\s([0-3]{1}\.?[0-9]{0,}|[0-3]{1})
(ReadingsVal($name,"relay","") eq "off") ? "<span style='color:#FF0000'>off</span>":"<span style='color:#00FF00'>on</span>",\
    ReadingsVal($name,"power",0)\
)}
attr shelly03 userReadings WebLink:network { my $ip=ReadingsVal($NAME,"network","");; $ip =~ s/connected to //gs;; $ip =~ s/<[^>]*>//gs;; return("<html><a href='http://".$ip."/'>WEB</a></html>") },\
power_Waschmaschine_avg:power.* { movingAverage($NAME,"power",300) }
attr shelly03 webCmd |
</syntaxhighlight>
==== RAW Definition Waschmaschine_Counter (HourCounter Modul) ====
<syntaxhighlight lang="Perl">
defmod Waschmaschine_Counter HourCounter Waschmaschine_PV:.*laeuft Waschmaschine_PV:Waschmaschine_Status:.*[beendet|ausgeschaltet]
attr Waschmaschine_Counter DbLogExclude .*
attr Waschmaschine_Counter alias Waschmaschine_Counter
attr Waschmaschine_Counter event-on-change-reading .*
attr Waschmaschine_Counter event-on-change-reading .*
attr Waschmaschine_Counter event-on-update-reading power.*
attr Waschmaschine_Counter group PV Eigenverbrauch-Steuerung
attr Waschmaschine_Counter group PV Eigenverbrauch-Steuerung
attr Waschmaschine_Counter icon time_timer
attr Waschmaschine_Counter icon time_timer
attr Waschmaschine_Counter interval 5
attr Waschmaschine_Counter interval 5
attr Waschmaschine_Counter room Strom->Photovoltaik
attr Waschmaschine_Counter room Strom->Photovoltaik
attr Waschmaschine_Counter sortby 23
attr Waschmaschine_Counter sortby 433
attr Waschmaschine_Counter verbose 0
attr Waschmaschine_Counter verbose 0
</syntaxhighlight>
==== RAW Definition rg_Waschmaschine_Status (readingsGroup Modul) ====
<syntaxhighlight lang="Perl">
defmod rg_Waschmaschine_Status readingsGroup <Device>,<Information>,<Wert> Waschmaschine:<Status>,!state Waschmaschine:<PowerLevelMinTime>,!PowerLevelMinTime Waschmaschine:<PowerLimitOn>,!PowerLimitOn Waschmaschine:<PowerLimitOff>,!PowerLimitOff Waschmaschine:<TimeStart>,!TimeStart Waschmaschine:<TimeEnd>,!TimeEnd Waschmaschine:<RunTimeMin>,!RunTimeMin Waschmaschine_Counter:<RunTimeMin>,!pulseTimeIncrement Waschmaschine:<RunTimePerDay>,!RunTimePerDay Waschmaschine_Counter:<RunTimePerDay>,!pulseTimePerDay Waschmaschine_PV:<wait>,wait_timer Waschmaschine_PV:<TimeStart>,timer_01_c04 Waschmaschine_PV:<TimeEnd>,timer_02_c04
attr rg_Waschmaschine_Status DbLogExclude .*
attr rg_Waschmaschine_Status alias Status Waschmaschine Eigenverbrauch
attr rg_Waschmaschine_Status commands {state  => 'state:on,off',\
PowerLevelMinTime => 'PowerLevelMinTime:selectnumbers,30,30,300,0,lin',\
PowerLimitOn => 'PowerLimitOn:selectnumbers,250,250,2000,0,lin',\
PowerLimitOff => 'PowerLimitOff:selectnumbers,0,50,800,0,lin',\
RunTimeMin => 'RunTimeMin:selectnumbers,300,300,7200,0,lin',\
RunTimePerDay => 'RunTimePerDay:selectnumbers,7200,300,28800,0,lin',\
TimeStart => 'TimeStart:time',\
TimeEnd => 'TimeEnd:time'}
attr rg_Waschmaschine_Status group PV Status
attr rg_Waschmaschine_Status nameStyle style="color:grey"
attr rg_Waschmaschine_Status room Strom->Photovoltaik
attr rg_Waschmaschine_Status sortby 03
attr rg_Waschmaschine_Status style style="font-size:18px"
</syntaxhighlight>
</syntaxhighlight>


Zeile 4.907: Zeile 3.060:
* [https://github.com/kilianknoll/ Github Kilian DWD Forecast]
* [https://github.com/kilianknoll/ Github Kilian DWD Forecast]
* [https://github.com/FL550/ Github FL550 DWD Forecast]
* [https://github.com/FL550/ Github FL550 DWD Forecast]
* [https://svn.fhem.de/fhem/trunk/fhem/contrib/ch.eick/ contrib/ch.eick]


[[Kategorie:IP Components]]
[[Kategorie:IP Components]]
[[Kategorie:Other Components]]
[[Kategorie:Wechselrichter]]
[[Kategorie:Energieerzeugungsmessung]]
[[Kategorie:Wallboxen]]

Aktuelle Version vom 17. November 2024, 12:31 Uhr

Sowas könnte am Schluss raus kommen. Schreibt mich gerne an ;-)
Kostal Plenticore 10 Plus
Kostal Plenticore Plus
Allgemein
Protokoll IP und RS485
Typ Hybrid Wechselrichter
Kategorie Wechselrichter
Technische Details
Kommunikation n/a
Kanäle n/a
Betriebsspannung 400 V
Leistungsaufnahme
Versorgung 3P AC
Abmessungen 56,3x40,5x23,3 cm (BxHxT)
Sonstiges
Modulname HTTPMOD, Modbus, Perl
Hersteller KOSTAL Solar Electric GmbH
Leistung und Hauptverbraucher.png
Forecast.png
Wechselrichter 1 mit Status
WR_1_Speicher_1_ExternControl
Wechselrichter 2 und gesteuerte Geräte
WallBox mit Kia Fahrzeug
PV 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.

Der Kostal Plenticore Plus (Webseite des Herstellers) ist ein Hybrid Wechselrichter mit IP-Konnektivität.

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.

Kompatibilität zu anderen Kostal Wechselrichtern

Auf Grund von Anfragen scheint diese Implementierung auch für andere Kostal Wechselrichtern zu passen. - Plenticore Plus - Piko MP - 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.

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.

pi@raspberrypi:~ $ sudo apt-get install docker-compose
Paketlisten werden gelesen... Fertig
Abhängigkeitsbaum wird aufgebaut.
Statusinformationen werden eingelesen.... Fertig
docker-compose ist schon die neueste Version (1.21.0-3).

https://docs.docker.com/engine/reference/commandline/compose/

Einige Docker Kommandos:

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.

pi@raspberrypi:~/docker-compose/fhem_2022 $ pwd
/home/pi/docker-compose/fhem_2022
pi@raspberrypi:~/docker-compose/fhem_2022 $ ls -l docker-compose.yml

Administration etwas erleichtern

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.

pi@raspberrypi:~ $ cat /etc/passwd
< snip >
fhem:x:6061:6061:FHEM,,,:/home/pi/docker-compose/fhem_2024/fhem:/bin/bash

MySQL initial konfigurieren

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

pi@raspberrypi:~ $ docker exec -it fhem_2022_mysql_1 mysql -u root -p
Enter password:
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 17517
Server version: 8.0.32 MySQL Community Server - GPL
mysql>

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 -p
Enter password: 
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 44892
Server version: 8.0.28 MySQL Community Server - GPL
mysql>
MySQL Kommandos
-- Die fhem Datenbank neu anlegen
mysql> CREATE DATABASE `fhem` DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_bin;

-- Die FHEM Tabellen neu anlegen
-- Für die Spalte VALUE wurde mal eine längere Definition gewählt
mysql> CREATE TABLE `fhem`.`history` (TIMESTAMP TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, DEVICE varchar(64), TYPE varchar(64), EVENT varchar(512), READING varchar(64), VALUE varchar(255), UNIT varchar(32));
mysql> CREATE TABLE `fhem`.`current` (TIMESTAMP TIMESTAMP, DEVICE varchar(64), TYPE varchar(64), EVENT varchar(512), READING varchar(64), VALUE varchar(128), UNIT varchar(32));

-- Den Index einrichten und über das READING mit definieren, damit kein duplicate Key entstehen kann
mysql> CREATE INDEX Search_Idx ON `fhem`.`history` (TIMESTAMP, DEVICE, READING) USING BTREE;
Query OK, 0 rows affected (0.11 sec)
Records: 0  Duplicates: 0  Warnings: 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_password
mysql> SELECT Host,User,plugin FROM mysql.user WHERE User='fhemuser';
+------+----------+-----------------------+
| Host | User     | plugin                |
+------+----------+-----------------------+
| %    | fhemuser | caching_sha2_password |     <<< Das unterstützt FHEM mit DbLog noch nicht
+------+----------+-----------------------+

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';
+------+----------+-----------------------+
| Host | User     | plugin                |
+------+----------+-----------------------+
| %    | fhemuser | mysql_native_password |
+------+----------+-----------------------+
1 row in set (0.01 sec)

-- Bei den GRANDS muss ich nochmal nachschauen, was da wirklich notwendig ist
mysql> GRANT SELECT, INSERT, DELETE, UPDATE ON `fhem`.* TO 'fhemuser'@'%';
mysql> GRANT ALTER ROUTINE ON `fhem`.* 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
mysql> FLUSH PRIVILEGES;

-- Alle Einträge für den fhemuser anschauen
mysql> SELECT * FROM mysql.user WHERE User='fhemuser';
  < snip >
  Es wird eine Tabelle mit den user Einträgen für fhemuser angezeigt, die man sich besser in voller Breite im Editor anschaut

mysql> Connect fhem;
  Enter password: 
  Connection id:    44902
  Current database: fhem

-- Zum Test mal einige Einträge in der history anzeigen, wenn FHEM bereits was über DbLog geschieben hat
mysql> SELECT * FROM history LIMIT 1;
  +---------------------+------------------+---------+-------+-----------------+----------+------+
  | TIMESTAMP           | DEVICE           | TYPE    | EVENT | READING         | VALUE    | UNIT |
  +---------------------+------------------+---------+-------+-----------------+----------+------+
  | 2019-04-03 00: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 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';

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
userReadings

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.

Actual_Battery_charge_-minus_or_discharge_-plus_Power

  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.

Kostal Plenticore Plus die API

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

attr global verbose 3
attr <device> verbose 5

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.

* use Encode qw(decode encode);
* use PBKDF2::Tiny qw/derive verify/;
* use Digest::SHA qw(sha256 hmac_sha256);
* use Crypt::URandom qw( urandom );
* use Crypt::AuthEnc::GCM;

Je nach dem welche Installation Ihr verwendet kann es auch sein, dass noch weitere Module installiert werden müssen. Es wurde bereits folgendes gemeldet.

* sudo apt-get install libcryptx-perl
* sudo apt-get install libpbkdf2-tiny-perl
* sudo apt-get install libcrypt-urandom-perl
KeyValue() Erläuterung Passworte

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 .

KeyValue() Test

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.

Syntax für die Commandline im FHEM {KeyValue("[read|store]","PW_<device>_<key>","<password>")}
{KeyValue("store","PW_WR_1_API_user","<passwort>")}
{KeyValue("read","PW_WR_1_API_user")}

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 .

plenticore_auth() Test

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

Syntax für die Commandline im FHEM:
{plenticore_auth("[start|finish|session]","user","<device>","auth_randomString64","auth_nonce","auth_salt","auth_rounds","auth_transactionId","auth_token")}
Test 1)
{plenticore_auth("start","user","WR_1_API")}       # Im Hintergrund wird das Passwort aus dem KeyStore verwendet
>>> {"nonce": "UUZ1dWNEZnowVzh2","username": "user"}

Test 2)
{plenticore_auth("finish","user","WR_1_API","TESMUWZnwkJZbnpF","TE2MUWZnwkJZbnpFQ5ulCfolNNdAD0vT","DbAC0R85jwF0rh+r","29000","1376720346bea40cdf770a8f84b5975cfeb20c5e6ac6d89b7862df3ca9695e43")}
>>> {"transactionId": "1376720346bea40cdf770a8f84b5975cfeb20c5e6ac6d89b7862df3ca9695e43", "proof": "5xZeOxoyR0hzPCVqvD/BPMqscQbT57wSONl049xiLjE="}

Test 3)
{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.

SW_Statistic_Yield_Month:Statistic_Yield_Month.* { round(ReadingsVal("$NAME","Statistic_Yield_Month",0)+ReadingsVal("WR_2_API","Statistic_Yield_Month",0),0) + 195000},
SW_Statistic_Yield_Year:Statistic_Yield_Year.* { round(ReadingsVal("$NAME","Statistic_Yield_Year",0)+ReadingsVal("WR_2_API","Statistic_Yield_Year",0),0) + 772000},

SW_Statistic_EnergyHomePv_Month:SW_Statistic_EnergyHomeFeedInGrid_Month.* { round(ReadingsVal("$NAME","SW_Statistic_Yield_Month",0) - ReadingsVal("$NAME","SW_Statistic_EnergyHomeFeedInGrid_Month",0) - ReadingsVal("$NAME","Statistic_EnergyHomeBat_Month",0),0) - 42000 },
SW_Statistic_EnergyHomePv_Year:SW_Statistic_EnergyHomeFeedInGrid_Year.* { round(ReadingsVal("$NAME","SW_Statistic_Yield_Year",0) - ReadingsVal("$NAME","SW_Statistic_EnergyHomeFeedInGrid_Year",0) - ReadingsVal("$NAME","Statistic_EnergyHomeBat_Year",0),0) - 158000 },

SW_Statistic_TotalConsumption_Month:SW_Statistic_EnergyHomePv_Month.* { round( (ReadingsVal("$NAME","SW_Statistic_EnergyHomePv_Month",0)+ReadingsVal("$NAME","Statistic_EnergyHomeBat_Month",0) +ReadingsVal("$NAME","SW_Statistic_EnergyHomeGrid_Month",0) ),0) + 42000},
SW_Statistic_TotalConsumption_Year:SW_Statistic_EnergyHomePv_Year.* { round( (ReadingsVal("$NAME","SW_Statistic_EnergyHomePv_Year",0)+ReadingsVal("$NAME","Statistic_EnergyHomeBat_Year",0) +ReadingsVal("$NAME","SW_Statistic_EnergyHomeGrid_Year",0) ),0) + 158000},

RAW Definition des WR_1_API Master

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.

setstate WR_1_API SW_Meter_init_FeedInGrid_Day   xxxx   << Tageswert  um 00:01
setstate WR_1_API SW_Meter_init_FeedInGrid_Month xxxx   << Monatswert um 00:01 am 01. des Monats
setstate WR_1_API SW_Meter_init_FeedInGrid_Year  xxxx   << Jahreswert um 00:01 am 01.01 des Jahres
setstate WR_1_API SW_Meter_init_Grid_Day   xxxx   << Tageswert  um 00:01
setstate WR_1_API SW_Meter_init_Grid_Month xxxx   << Monatswert um 00:01 am 01. des Monats
setstate WR_1_API SW_Meter_init_Grid_Year  xxxx   << Jahreswert um 00:01 am 01.01 des Jahres

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.

Batteriesteuerung über den Plenticore

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.

- smart_laden (bei geringer Leistung im Herbst/Winter)
- MinSOC Sommer/Winter Umschaltung
smart_laden und laden_beendet
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.
setreading WR_1_Speicher_1_ExternControl SpeicherEntladung Zeit
setreading WR_1_Speicher_1_ExternControl SpeicherZeitStart 00:00
setreading WR_1_Speicher_1_ExternControl SpeicherZeitEnde 00:00

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":

defmod PV_1_EVU_Tarif_1 WeekdayTimer WR_1_Speicher_1_ExternControl  de 12345|00:01|Arbeitstage {fhem("setreading $NAME SpeicherZeitStart 07:00");; fhem("setreading $NAME SpeicherZeitEnde 16:00");; fhem("setreading $NAME SpeicherEntladung Zeit")}
attr PV_1_EVU_Tarif_1 DbLogExclude .*
attr PV_1_EVU_Tarif_1 alias PV_1_EVU_Tarif_1
attr PV_1_EVU_Tarif_1 commandTemplate set $NAME  $EVENT
attr PV_1_EVU_Tarif_1 disable 0
attr PV_1_EVU_Tarif_1 group PV Steuerung EVU
attr PV_1_EVU_Tarif_1 icon clock
attr PV_1_EVU_Tarif_1 room Strom->Photovoltaik
attr PV_1_EVU_Tarif_1 sortby 112
attr PV_1_EVU_Tarif_1 stateFormat {sprintf("geplant: %s %s", ReadingsVal($name,"nextUpdate","?"), ReadingsVal($name,"nextValue","?"))}
PV_1_EVU_Tarif_2 WeekdayTimer (Beispiel)

Nutzung von "SpeicherEntladung Zeit":

defmod PV_1_EVU_Tarif_2 WeekdayTimer WR_1_Speicher_1_ExternControl  de  60|00:01|Wochenende {fhem("setreading $NAME SpeicherZeitStart 00:00");; fhem("setreading $NAME SpeicherZeitEnde 23:59");; fhem("setreading $NAME SpeicherEntladung Zeit")}
attr PV_1_EVU_Tarif_2 DbLogExclude .*
attr PV_1_EVU_Tarif_2 alias PV_1_EVU_Tarif_2
attr PV_1_EVU_Tarif_2 commandTemplate set $NAME  $EVENT
attr PV_1_EVU_Tarif_2 disable 0
attr PV_1_EVU_Tarif_2 group PV Steuerung EVU
attr PV_1_EVU_Tarif_2 icon clock
attr PV_1_EVU_Tarif_2 room Strom->Photovoltaik
attr PV_1_EVU_Tarif_2 sortby 113
attr PV_1_EVU_Tarif_2 stateFormat {sprintf("geplant: %s %s", ReadingsVal($name,"nextUpdate","?"), ReadingsVal($name,"nextValue","?"))}
WR_1_Speicher_1_Zeiten WeekdayTimer (Beispiel)

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":

defmod WR_1_Speicher_1_Zeiten WeekdayTimer SpeicherZeiten de 12345|07:00|entladen 12345|16:00|gesperrt 60|00:00|entladen 60|23:59|gesperrt {fhem("setreading $NAME SpeicherTrigger $EVENT")}
attr WR_1_Speicher_1_Zeiten DbLogExclude .*
attr WR_1_Speicher_1_Zeiten alias WR_1_Speicher_1_Zeiten
attr WR_1_Speicher_1_Zeiten commandTemplate set $NAME  $EVENT
attr WR_1_Speicher_1_Zeiten disable 0
attr WR_1_Speicher_1_Zeiten group PV Steuerung EVU
attr WR_1_Speicher_1_Zeiten icon clock
attr WR_1_Speicher_1_Zeiten room Strom->Photovoltaik
attr WR_1_Speicher_1_Zeiten sortby 111
attr WR_1_Speicher_1_Zeiten stateFormat {sprintf("geplant: %s %s", ReadingsVal($name,"nextUpdate","?"), ReadingsVal($name,"nextValue","?"))}
Tarifsteuerung mit einem DOIF (Beispiel)
################################################################################################################
## 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])

   (setreading WR_1_Speicher_1_ExternControl  SpeicherZeitStart 07:00)
   (setreading WR_1_Speicher_1_ExternControl  SpeicherZeitEnde 19:00)
   (setreading WR_1_Speicher_1_ExternControl  SpeicherEntladung Zeit)

################################################################################################################
## 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 Entladen
DOELSEIF
 ([00:00|7])

   (setreading WR_1_Speicher_1_ExternControl  SpeicherZeitStart 00:00)
   (setreading WR_1_Speicher_1_ExternControl  SpeicherZeitEnde 23:59)
   (setreading WR_1_Speicher_1_ExternControl  SpeicherEntladung Zeit)

################################################################################################################
## 3 Hier könnte dann die Berechnung der überschüssigen Zeit rein, wenn Du mit 40% aus der Nacht kommen würdest
DOELSEIF
 (*** )

   (setreading WR_1_Speicher_1_ExternControl  SpeicherZeitStart [neu berechnete SpeicherZeitStart])
   (setreading WR_1_Speicher_1_ExternControl  SpeicherEntladung Zeit)    ## 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.

openWB:
     [WB_1:lp_1_ChargeStat] eq "loading"

go-eCharger:
     [WB_1:car_state] eq "2"                           ## Ladevorgang läuft

Das DOIF übernimmt die Externe Speichersteuerung.

Kostal Smart Energy Manager (KSEM) (Modbus/TCP)

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 Übersicht mit Hilfen zur Orientierung

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.

mysql -h 192.168.178.xxx --port 3306 --database fhem -u fhemuser -p

Alle Devices in der history anzeigen

## Alle Devices in der history Tabelle
SELECT DEVICE FROM history
 GROUP BY DEVICE;

Alle readings eines Devices anzeigen

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.

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;

Einträge eines DEVICE einem neuen DEVICE zuordnen

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.

UPDATE history
  SET
    TIMESTAMP = TIMESTAMP,
    DEVICE    = 'PV_1'
  WHERE
        DEVICE    = 'WR_1'
    AND TIMESTAMP < '2021-01-24 17:25:15';

Ein altes READING einem neuen READING Namen zuordnen

Sehr wichtig ist "TIMESTAMP = TIMESTAMP", da hierdurch der alte TIMESTAMP erhalten bleibt.

UPDATE history
  SET
    TIMESTAMP = TIMESTAMP,
    READING   = 'Total_DC_PV_Energy_sumOfAllPVInputs'
  WHERE
        DEVICE    = 'WR_1'
    AND READING   = 'Total_DC_PV_Energy_(sumOfAllPVInputs)'
    AND TIMESTAMP < '2021-03-23 17:25:15';

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

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.

Energie Bilanz

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.

defmod LogDBRep_Statistic_EnergyFeedInGrid_Year_diff_Week DbRep LogDB
attr LogDBRep_Statistic_EnergyFeedInGrid_Year_diff_Week DbLogExclude .*
attr LogDBRep_Statistic_EnergyFeedInGrid_Year_diff_Week aggregation week
attr LogDBRep_Statistic_EnergyFeedInGrid_Year_diff_Week comment Version 2020.10.21 11:14
attr LogDBRep_Statistic_EnergyFeedInGrid_Year_diff_Week device PV_1_API
attr LogDBRep_Statistic_EnergyFeedInGrid_Year_diff_Week diffAccept 15000
attr LogDBRep_Statistic_EnergyFeedInGrid_Year_diff_Week reading Statistic_EnergyFeedInGrid_Year
attr LogDBRep_Statistic_EnergyFeedInGrid_Year_diff_Week readingNameMap Statistic_EnergyFeedInGrid_Week
attr LogDBRep_Statistic_EnergyFeedInGrid_Year_diff_Week room Strom->Energie,System
attr LogDBRep_Statistic_EnergyFeedInGrid_Year_diff_Week timestamp_begin current_year_begin
attr LogDBRep_Statistic_EnergyFeedInGrid_Year_diff_Week timestamp_end previous_week_end

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.

defmod LogDBRep_Statistic_EnergyHomePvSum_Year_diff_Week DbRep LogDB
attr LogDBRep_Statistic_EnergyHomePvSum_Year_diff_Week DbLogExclude .*
attr LogDBRep_Statistic_EnergyHomePvSum_Year_diff_Week aggregation week
attr LogDBRep_Statistic_EnergyHomePvSum_Year_diff_Week allowDeletion 0
attr LogDBRep_Statistic_EnergyHomePvSum_Year_diff_Week comment Version 2020.10.23 15:00\
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.
attr LogDBRep_Statistic_EnergyHomePvSum_Year_diff_Week device PV_1_API
attr LogDBRep_Statistic_EnergyHomePvSum_Year_diff_Week diffAccept 20000
attr LogDBRep_Statistic_EnergyHomePvSum_Year_diff_Week reading Statistic_EnergyHomePvSum_Year
attr LogDBRep_Statistic_EnergyHomePvSum_Year_diff_Week readingNameMap Statistic_EnergyHomePvSum_Week
attr LogDBRep_Statistic_EnergyHomePvSum_Year_diff_Week room Strom->Energie,System
attr LogDBRep_Statistic_EnergyHomePvSum_Year_diff_Week timestamp_begin current_year_begin
attr LogDBRep_Statistic_EnergyHomePvSum_Year_diff_Week timestamp_end previous_week_end

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.

defmod LogDBRep_Statistic_EnergyHomePvSum_Year_max_Month DbRep LogDB
attr LogDBRep_Statistic_EnergyHomePvSum_Year_max_Month DbLogExclude .*
attr LogDBRep_Statistic_EnergyHomePvSum_Year_max_Month aggregation month
attr LogDBRep_Statistic_EnergyHomePvSum_Year_max_Month allowDeletion 1
attr LogDBRep_Statistic_EnergyHomePvSum_Year_max_Month comment Version 2020.10.23 15:00\
Dieser Wert ist der gesamte Verbrauch aus der PV Anlage inklusive Batterie im Monat.\
Gestartet über PV_Schedule am ersten des Monats\
Benötigt für SVG_LogDB_PV_Bilanz.
attr LogDBRep_Statistic_EnergyHomePvSum_Year_max_Month device PV_1_API
attr LogDBRep_Statistic_EnergyHomePvSum_Year_max_Month reading Statistic_EnergyHomePvSum_Month
attr LogDBRep_Statistic_EnergyHomePvSum_Year_max_Month room Strom->Energie,System
attr LogDBRep_Statistic_EnergyHomePvSum_Year_max_Month timestamp_begin current_year_begin
attr LogDBRep_Statistic_EnergyHomePvSum_Year_max_Month timestamp_end previous_month_end

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.

defmod LogDBRep_Statistic_Yield_Year_diff_Week DbRep LogDB
attr LogDBRep_Statistic_Yield_Year_diff_Week DbLogExclude .*
attr LogDBRep_Statistic_Yield_Year_diff_Week aggregation week
attr LogDBRep_Statistic_Yield_Year_diff_Week allowDeletion 0
attr LogDBRep_Statistic_Yield_Year_diff_Week comment Version 2020.10.23 15:00\
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.
attr LogDBRep_Statistic_Yield_Year_diff_Week device PV_1_API
attr LogDBRep_Statistic_Yield_Year_diff_Week diffAccept 20000
attr LogDBRep_Statistic_Yield_Year_diff_Week reading Statistic_Yield_Year
attr LogDBRep_Statistic_Yield_Year_diff_Week readingNameMap Statistic_Yield_Week
attr LogDBRep_Statistic_Yield_Year_diff_Week room Strom->Energie,System
attr LogDBRep_Statistic_Yield_Year_diff_Week timestamp_begin current_year_begin
attr LogDBRep_Statistic_Yield_Year_diff_Week timestamp_end previous_week_end

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.

defmod LogDBRep_Statistic_Yield_Month_max_Month DbRep LogDB
attr LogDBRep_Statistic_Yield_Month_max_Month DbLogExclude .*
attr LogDBRep_Statistic_Yield_Month_max_Month aggregation month
attr LogDBRep_Statistic_Yield_Month_max_Month allowDeletion 0
attr LogDBRep_Statistic_Yield_Month_max_Month comment Version 2020.10.23 15:00\
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.
attr LogDBRep_Statistic_Yield_Month_max_Month device PV_1_API
attr LogDBRep_Statistic_Yield_Month_max_Month diffAccept 20000
attr LogDBRep_Statistic_Yield_Month_max_Month reading Statistic_Yield_Month
attr LogDBRep_Statistic_Yield_Month_max_Month room Strom->Energie,System
attr LogDBRep_Statistic_Yield_Month_max_Month timestamp_begin current_year_begin
attr LogDBRep_Statistic_Yield_Month_max_Month timestamp_end previous_month_end

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

defmod LogDBRep_delete_Statistic_EnergyHomeBat_Day_max_Day DbRep LogDB
attr LogDBRep_delete_Statistic_EnergyHomeBat_Day_max_Day DbLogExclude .*
attr LogDBRep_delete_Statistic_EnergyHomeBat_Day_max_Day aggregation day
attr LogDBRep_delete_Statistic_EnergyHomeBat_Day_max_Day allowDeletion 1
attr LogDBRep_delete_Statistic_EnergyHomeBat_Day_max_Day comment Version 2020.10.30 18:30\
Löschen aller Statistic_EnergyHomeBat_Day Werte, bis auf den maximal Wert des Tages.\
Der aktuelle Tag bleibt noch erhalten.\
Aufruf mit: maxValue deleteOther
attr LogDBRep_delete_Statistic_EnergyHomeBat_Day_max_Day device PV_1_API
attr LogDBRep_delete_Statistic_EnergyHomeBat_Day_max_Day reading Statistic_EnergyHomeBat_Day
attr LogDBRep_delete_Statistic_EnergyHomeBat_Day_max_Day room Strom->Energie,System
attr LogDBRep_delete_Statistic_EnergyHomeBat_Day_max_Day timestamp_begin previous_month_begin
attr LogDBRep_delete_Statistic_EnergyHomeBat_Day_max_Day timestamp_end current_day_end

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

defmod LogDBRep_delete_Statistic_EnergyHomePvSum_Day_max_Day DbRep LogDB
attr LogDBRep_delete_Statistic_EnergyHomePvSum_Day_max_Day DbLogExclude .*
attr LogDBRep_delete_Statistic_EnergyHomePvSum_Day_max_Day aggregation day
attr LogDBRep_delete_Statistic_EnergyHomePvSum_Day_max_Day allowDeletion 1
attr LogDBRep_delete_Statistic_EnergyHomePvSum_Day_max_Day comment Version 2020.10.30 18:30\
Löschen aller Statistic_EnergyHomePvSum_Day Werte, bis auf den maximal Wert des Tages.\
Der aktuelle Tag bleibt noch erhalten.\
Aufruf mit: maxValue deleteOther
attr LogDBRep_delete_Statistic_EnergyHomePvSum_Day_max_Day device PV_1_API
attr LogDBRep_delete_Statistic_EnergyHomePvSum_Day_max_Day reading Statistic_EnergyHomePvSum_Day
attr LogDBRep_delete_Statistic_EnergyHomePvSum_Day_max_Day room Strom->Energie,System
attr LogDBRep_delete_Statistic_EnergyHomePvSum_Day_max_Day timestamp_begin previous_month_begin
attr LogDBRep_delete_Statistic_EnergyHomePvSum_Day_max_Day timestamp_end current_day_end

RAW Definition LogDBRep_delete_Statistic_TotalConsumption_Day_max_Day

Löschen aller Statistic_TotalConsumption_Day Werte, bis auf den maximal Wert des Tages.\ Der aktuelle Tag bleibt noch erhalten.

defmod LogDBRep_delete_Statistic_TotalConsumption_Day_max_Day DbRep LogDB
attr LogDBRep_delete_Statistic_TotalConsumption_Day_max_Day DbLogExclude .*
attr LogDBRep_delete_Statistic_TotalConsumption_Day_max_Day aggregation day
attr LogDBRep_delete_Statistic_TotalConsumption_Day_max_Day allowDeletion 1
attr LogDBRep_delete_Statistic_TotalConsumption_Day_max_Day comment Version 2020.10.30 18:30\
Löschen aller Statistic_TotalConsumption_Day Werte, bis auf den maximal Wert des Tages.\
Der aktuelle Tag bleibt noch erhalten.
attr LogDBRep_delete_Statistic_TotalConsumption_Day_max_Day device PV_1_API
attr LogDBRep_delete_Statistic_TotalConsumption_Day_max_Day reading Statistic_TotalConsumption_Day
attr LogDBRep_delete_Statistic_TotalConsumption_Day_max_Day room Strom->Energie,System
attr LogDBRep_delete_Statistic_TotalConsumption_Day_max_Day timestamp_begin previous_month_begin
attr LogDBRep_delete_Statistic_TotalConsumption_Day_max_Day timestamp_end current_day_end

Timing für die Datenbank Einträge

Ü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.

ddefmod DB_Service_Schedule DOIF ## Monatlich Einträge\
([01:13] and ($mday==1))\
 (set LogDBRep_Statistic_EnergyHomePvSum_Year_max_Month maxValue writeToDB)\
 (set LogDBRep_Statistic_Yield_Month_max_Month maxValue writeToDB)\
 (set LogDBRep_Statistic_previous_Month sqlCmd ckey:1)                      ## Bildet für verschiedene Devices die Monatsauswertung\
\
## Wöchentliche Einträge\
DOELSEIF\
([01:17] and ($wday==1))\
 (set LogDBRep_Statistic_EnergyHomePvSum_Year_diff_Week diffValue writeToDB)\
 (set LogDBRep_Statistic_EnergyFeedInGrid_Year_diff_Week diffValue writeToDB)\
 (set LogDBRep_Statistic_Yield_Year_diff_Week diffValue writeToDB)\
\
## Wöchentliche Einträge mit löschen\
DOELSEIF\
([02:17] and ($wday==1))\
 (set LogDBRep_delete_Statistic_EnergyHomeBat_Day_max_Day maxValue deleteOther)\
 (set LogDBRep_delete_Statistic_EnergyHomePvSum_Day_max_Day maxValue deleteOther)\
 (set LogDBRep_delete_Statistic_TotalConsumption_Day_max_Day maxValue deleteOther)\
\
## Tägliche Einträge\
DOELSEIF\
([01:17])\
 (set LogDBRep_Statistic_previous_Day sqlCmd ckey:1)                        ## Bildet für verschiedene Devices die Tagesauswertung\
\
## Quartal Einträge\
DOELSEIF\
(($md eq "01-01" or $md eq "04-01" or $md eq "07-01" or $md eq "10-01") and [03:11])\
 (set LogDBRep_Statistic_previous_Quarter sqlCmd ckey:1)                    ## Erstellt die Quartalsauswertung für WR_1\
\
## Jährliche Einträge\
DOELSEIF\
($md eq "01-01" and [08:05])\
 (set LogDBRep_Statistic_previous_Year sqlCmd ckey:1)                       ## Bildet für verschiedene Devices die Jahresauswertung
attr DB_Service_Schedule DbLogExclude .*
attr DB_Service_Schedule comment Version 2024.01.23 17:00\
Hier werden zusätzlich Werte in der Datenbank erzeugt.
attr DB_Service_Schedule do always
attr DB_Service_Schedule room System
attr DB_Service_Schedule wait 0,3:0,5,5:0,5,5
attr DB_Service_Schedule webCmd cmd_1:cmd_2:cmd_3
attr DB_Service_Schedule webCmdLabel monatlich :wöchentlich :wöchentlich Lö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.

Wetter-/Leistungs-Prognose KI_Prognose

Erstmalig wurde die 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.

RAW Definition DWD_Forecast

Erfordert ggf.

sudo apt-get install libxml-libxml-perl

RAW Definition DWD_Forecast

Astro Device

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

fhem.cfg Einträge für das Astro Device

Hier müsst Ihr Eure Position und Höhe eintragen.

attr global altitude 110
attr global latitude 47.xxxxx
attr global longitude 9.yyyyy

KI Prognose Teil 2 - Vorbereitung der Daten

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.

dwd_load() in einzelnen Schritten

 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

call dwd_load(curdate(),'none');

select * from dwdfull
-- WHERE TIMESTAMP > curdate()
order by TIMESTAMP desc
LIMIT 1000;

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.

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\
\
sqlCmd call dwd_load(curdate(),'none');;\
[none|show] zum Anzeigen des Ergebnisses\
\
executeAfterProc:\
<absoluter Skript Name> <DbLog IP-Adresse> <FHEM IP-Adresse> <DbRep Name> <Wechselricher Name> <Prefix Reading Name>
attr LogDBRep_PV_KI_Prognose executeAfterProc "/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"
attr LogDBRep_PV_KI_Prognose room System
attr LogDBRep_PV_KI_Prognose verbose 3

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.

SqlResultRow_1 NOW()
SqlResultRow_2 2023-03-17 11:01:03
sqlCmd call dwd_load(curdate(),'none');
sqlResultNumRows 1

DbLog/DbRep Device

RAW Definition LogDB

Achtung, bitte hier beachten, ob bereits eine andere DbLog verwendet wird.

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

RAW Definition LogDBRep_PV_KI_Prognose

Bitte beachtet, dass die Namen auch in anderen Devices eingetragen sind, wenn Ihr diese verändern wollt.

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.

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
    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:

     <absoluter Skript Name> <DbLog IP-Adresse> <FHEM IP-Adresse> <DbRep Name> <Wechselricher Name> <Prefix Reading Name>
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
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 enabled
    and
      ReadingsVal("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) {
      Log 3, "$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@raspberrypi:~/python$ cat pwd_[fhem|sql].json
{"username": "<Euer Username>",
 "password": "<Euer Passwort>"}
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:

/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 loading
PV_KI_Prognose  running - RandomForestRegressor loaded
PV_KI_Prognose  running - RandomForestRegressor trained
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


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

Leistung und Hauptverbraucher.png
Forecast.png
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.


Diagramme mit Grafana

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.

RAW Definition Hauptverbraucher

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.

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.

Beispiel Pool Softube

RAW Definition Pool_PV (DOIF Modul)

defmod Pool_PV_Perl DOIF ################################################################################################################\
## 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\
     and get_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)\
       {Log 3, "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)\
        {Log 3, "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)\
        {Log 3, "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)\
        {Log 3, "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)\
        {Log 3, "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?\
      and get_Exec("PV_Modus_Ein_timer") > 0                             ## läuft eine Wartezeit\
      and get_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)\
       {Log 3, "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)\
       {Log 3, "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)\
     {Log 3, "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)\
     {Log 3, "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] eq 0)                                ## 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)\
     {Log 3, "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)\
     {Log 3, "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)\
     {Log 3, "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 {\
  sub PV_Modus_Ein_Pool() {                                                   ## PV-Modus Einschalten\
    if (AttrVal("$SELF","verbose",0) >=3)\
       {Log 3, "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));;\
    }\
\
  sub PV_Modus_Aus_Pool() {                                                   ## PV-Modus Ausschalten\
    if (AttrVal("$SELF","verbose",0) >=3)\
       {Log 3, "Pool_PV sub  : Pool off"};;\
    fhem("".ReadingsVal("$SELF","SetCmdOff",0));;\
    set_Reading("Pool_Status","Aus");;\
  }\
}
attr Pool_PV_Perl DbLogExclude .*
attr Pool_PV_Perl alias Pool_PV_Perl
attr Pool_PV_Perl comment Version 2021.11.01 09:00
attr Pool_PV_Perl disable 0
attr Pool_PV_Perl event-on-change-reading .*
attr Pool_PV_Perl group PV Eigenverbrauch-Steuerung
attr Pool_PV_Perl icon scene_swimming
attr Pool_PV_Perl room Strom->Photovoltaik
attr Pool_PV_Perl sortby 421
attr Pool_PV_Perl uiTable {\
package ui_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;;'";;\
\
sub FUNC_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")
attr Pool_PV_Perl verbose 3

setstate Pool_PV_Perl 2022-10-23 16:44:33 Pool_Status Aus
setstate Pool_PV_Perl 2022-06-30 12:48:47 PowerLevelMinTime 300
setstate Pool_PV_Perl 2022-05-10 15:51:44 PowerLimitOff 100
setstate Pool_PV_Perl 2022-05-10 15:50:44 PowerLimitOn 1000
setstate Pool_PV_Perl 2022-05-10 15:55:23 RunTimeMin 7200
setstate Pool_PV_Perl 2022-10-23 06:15:00 RunTimePerDay 28800
setstate Pool_PV_Perl 2021-06-23 14:55:42 RunTimePerDaySummer 7200
setstate Pool_PV_Perl 2020-10-06 14:14:13 RunTimePerDayWinter 28800
setstate Pool_PV_Perl 2022-05-10 16:23:06 SetCmdOff set shelly02 off 0
setstate Pool_PV_Perl 2022-05-10 16:23:24 SetCmdOn set shelly02 on 0
setstate Pool_PV_Perl 2022-10-23 07:17:00 TimeEnd 16:00
setstate Pool_PV_Perl 2021-12-01 17:39:32 TimeEndSummer 16:00
setstate Pool_PV_Perl 2021-12-05 14:20:48 TimeEndWinter 16:00
setstate Pool_PV_Perl 2022-10-23 07:17:00 TimeStart 12:35
setstate Pool_PV_Perl 2022-06-30 12:48:23 TimeStartSummer 12:35
setstate Pool_PV_Perl 2020-09-03 13:10:56 TimeStartWinter 09:10

setstate Pool_PV_Perl 2022-10-30 11:25:40 ui_command_1 ---
setstate Pool_PV_Perl 2022-10-16 19:59:44 ui_command_1_before ---

RAW Definition Pool_Signale (Shelly Modul: shelly1pm)

defmod shelly02 Shelly 192.168.178.52
attr shelly02 DbLogExclude .*
attr shelly02 DbLogInclude relay.*,power.*,energy.*
attr shelly02 alias Pool
attr shelly02 comment Version 2020.10.19 18:28\
relais_0 => Pool limit 1000 W\
relail_1 => Terrasse Lichterkette limit 100 W
attr shelly02 event-on-change-reading relay.*,energy.*,state,network
attr shelly02 event-on-update-reading power.*
attr shelly02 group PV Eigenverbrauch-Steuerung
attr shelly02 icon taster_ch_1
attr shelly02 mode relay
attr shelly02 model shelly2.5
attr shelly02 room Shelly,Strom->Photovoltaik
attr shelly02 sortby 422
attr shelly02 stateFormat {\
my $status = (ReadingsVal($name,"state","none") eq "OK") ? "<span style='color:green'>OK</span>":"<span style='color:red'>Error</span>";;\
my $link = ReadingsVal($name,"WebLink","none");;\
\
my $e0 = sprintf("%08.2f KWh",ReadingsVal($name,"energy_0_Total",0)/1000);;\
my $r0 = (ReadingsVal($name,"relay_0","") eq "off") ? "<span style='color:red'>off</span>":"<span style='color:green'>on</span>";;\
my $p0 = sprintf("%06.1f Watt",ReadingsVal($name,"power_0",0));;\
\
my $e1 = sprintf("%08.2f KWh",ReadingsVal($name,"energy_1_Total",0)/1000);;\
my $r1 = (ReadingsVal($name,"relay_1","") eq "off") ? "<span style='color:red'>off</span>":"<span style='color:green'>on</span>";;\
my $p1 = sprintf("%06.1f Watt",ReadingsVal($name,"power_1",0));;\
\
"<html><table border=2 bordercolor='darkgreen' cellspacing=0 style='width: 100%'>\
 <colgroup>\
   <col span='1' style='width: 30%;;'>\
   <col span='1' style='width: 30%;;'>\
   <col span='1' style='width: 20%;;'>\
 </colgroup>\
<tr>\
  <td style='text-align:left'>\
    Status: $status\
  </td>\
  <td style='text-align:left'>\
    WebLink: $link\
  </td>\
  <td style='text-align:right'>\
     Pool Gesamt 0: $e0<br>\
     JEL Gesamt 1: $e1</td>\
  <td style='text-align:right'>\
    Relais 0: $r0 $p0<br>\
    Relais 1: $r1 $p1\
  </td>\
</tr>\
</table>\
</html>"\
}
attr shelly02 userReadings WebLink:network.* { my $ip=ReadingsVal($name,"network","na");; $ip =~ s/connected to //gs;; $ip =~ s/<[^>]*>//gs;; return("<a href='http://".$ip."/'>".$ip."</a>") },\
\
energy_0_Total:energy_0.* monotonic { ReadingsVal($name,"energy_0",0) },\
energy_1_Total:energy_1.* monotonic { ReadingsVal($name,"energy_1",0) }\

attr shelly02 verbose 0
attr shelly02 webCmd |

Beispiel Waschmaschine (mit Walzenschalter ;-) )

RAW Definition Waschmaschine_PV (DOIF Modul)

defmod Waschmaschine_PV_Perl DOIF ################################################################################################################\
## 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\
     and get_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)\
       {Log 3, "$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)\
        {Log 3, "$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)\
        {Log 3, "$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)\
        {Log 3, "$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)\
        {Log 3, "$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?\
      and get_Exec("PV_Modus_Ein_timer") > 0                             ## läuft eine Wartezeit\
      and get_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)\
       {Log 3, "$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)\
      {Log 3, "$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)\
       {Log 3, "$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)\
        {Log 3, "$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)\
      {Log 3, "$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)\
     {Log 3, "$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 {\
  sub PV_Modus_Ein_Waschmaschine() {                                                   ## PV-Modus Einschalten\
    if (AttrVal("$SELF","verbose",0) >=3)\
       {Log 3, "$SELF sub  : On"};;\
    fhem("set Waschmaschine_Counter pauseTimeIncrement 0");;\
    fhem("".ReadingsVal("$SELF","SetCmdOn",0));;\
    }\
\
  sub PV_Modus_Aus_Waschmaschine() {                                                   ## PV-Modus Ausschalten\
    if (AttrVal("$SELF","verbose",0) >=3)\
       {Log 3, "$SELF sub  : Off"};;\
    fhem("".ReadingsVal("$SELF","SetCmdOff",0));;\
    set_Reading("Status_1","Aus");;\
    set_Reading("Status_2","Steckdose ist ausgeschaltet");;\
  }\
}
attr Waschmaschine_PV_Perl DbLogExclude .*
attr Waschmaschine_PV_Perl alias Waschmaschine_PV_Perl
attr Waschmaschine_PV_Perl comment Version 2021.11.01 09:00
attr Waschmaschine_PV_Perl disable 0
attr Waschmaschine_PV_Perl event-on-change-reading .*
attr Waschmaschine_PV_Perl event_Readings Status_1:[$SELF:Status_1], Status_2:[$SELF:Status_2]
attr Waschmaschine_PV_Perl group PV Eigenverbrauch-Steuerung
attr Waschmaschine_PV_Perl icon scene_washing_machine
attr Waschmaschine_PV_Perl room Strom->Photovoltaik
attr Waschmaschine_PV_Perl sortby 4311
attr Waschmaschine_PV_Perl uiTable {\
package ui_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;;'";;\
\
sub FUNC_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")
attr Waschmaschine_PV_Perl verbose 3

setstate Waschmaschine_PV_Perl 2022-08-11 09:48:48 PowerLevelMinTime 300
setstate Waschmaschine_PV_Perl 2022-09-26 13:47:21 PowerLimitOff 250
setstate Waschmaschine_PV_Perl 2022-10-06 17:45:40 PowerLimitOn 2000
setstate Waschmaschine_PV_Perl 2022-08-11 09:52:38 RunTimeMin 5400
setstate Waschmaschine_PV_Perl 2022-08-11 09:52:45 RunTimePerDay 19200
setstate Waschmaschine_PV_Perl 2022-08-11 12:27:42 SetCmdOff set shelly03 off 0
setstate Waschmaschine_PV_Perl 2022-08-11 12:27:19 SetCmdOn set shelly03 on 0
setstate Waschmaschine_PV_Perl 2022-11-01 09:21:23 Status_1 -
setstate Waschmaschine_PV_Perl 2022-11-01 09:21:25 Status_2 -
setstate Waschmaschine_PV_Perl 2022-11-01 07:17:00 TimeEnd 18:00
setstate Waschmaschine_PV_Perl 2022-10-06 17:32:31 TimeEndSummer 18:00
setstate Waschmaschine_PV_Perl 2022-08-11 09:46:58 TimeEndWinter 16:00
setstate Waschmaschine_PV_Perl 2022-11-01 07:17:00 TimeStart 08:00
setstate Waschmaschine_PV_Perl 2022-08-11 09:48:05 TimeStartSummer 08:00
setstate Waschmaschine_PV_Perl 2022-08-11 09:46:54 TimeStartWinter 10:00
setstate Waschmaschine_PV_Perl 2022-11-01 09:21:25 ui_command_1 ---
setstate Waschmaschine_PV_Perl 2022-10-06 19:34:48 ui_command_1_before ---

RAW Definition Waschmaschine_Signale (Shelly Modul: shelly1pm)

defmod shelly03 Shelly 192.168.178.55
attr shelly03 DbLogExclude .*
attr shelly03 DbLogInclude relay.*,power.*,energy.*
attr shelly03 alias Waschmaschine
attr shelly03 comment Version 2020.10.19 18:28
attr shelly03 event-on-change-reading relay.*,power.*,energy.*,network
attr shelly03 group PV Eigenverbrauch-Steuerung
attr shelly03 icon taster_ch_1
attr shelly03 interval 60
attr shelly03 mode relay
attr shelly03 model shelly1pm
attr shelly03 room Shelly,Strom->Photovoltaik
attr shelly03 sortby 432
attr shelly03 stateFormat {\
my $link = ReadingsVal($name,"WebLink","none");;\
\
my $e0 = sprintf("%08.2f KWh",ReadingsVal($name,"energy_Total",0)/1000);;\
my $r0 = (ReadingsVal($name,"relay","") eq "off") ? "<span style='color:red'>off</span>":"<span style='color:green'>on</span>";;\
my $p0 = sprintf("%06.1f Watt",ReadingsVal($name,"power",0));;\
\
"<html><table border=2 bordercolor='darkgreen' cellspacing=0 style='width: 100%'>\
 <colgroup>\
   <col span='1' style='width: 30%;;'>\
   <col span='1' style='width: 30%;;'>\
   <col span='1' style='width: 20%;;'>\
 </colgroup>\
<tr>\
  <td style='text-align:left'>\
  </td>\
  <td style='text-align:left'>\
    WebLink: $link\
  </td>\
  <td style='text-align:right'>\
     Waschmaschine Gesamt 0: $e0<br>\
  <td style='text-align:right'>\
    Relais 0: $r0 $p0<br>\
  </td>\
</tr>\
</table>\
</html>"\
}
attr shelly03 userReadings WebLink:network.* { my $ip=ReadingsVal($name,"network","na");; $ip =~ s/connected to //gs;; $ip =~ s/<[^>]*>//gs;; return("<a href='http://".$ip."/'>".$ip."</a>") },\
\
energy_Total:energy.* monotonic { ReadingsVal($name,"energy",0) }
attr shelly03 webCmd |

RAW Definition Waschmaschine_Counter (HourCounter Modul)

defmod Waschmaschine_Counter HourCounter shelly03:power:\s([0-9]{2,}\.?[0-9]{0,}|[4-9]{1}\.?[0-9]{0,}) shelly03:power:\s([0-3]{1}\.?[0-9]{0,}|[0-3]{1})
attr Waschmaschine_Counter DbLogExclude .*
attr Waschmaschine_Counter alias Waschmaschine_Counter
attr Waschmaschine_Counter comment Version 2020.10.19 18:28\
\
Waschmaschine_PV:.*laeuft Waschmaschine_PV:Waschmaschine_Status:.*[beendet|ausgeschaltet]\
\
Version 2022.09.20 16:00\
Bis 3.9 Watt ist das Gerät aus, ab 4 Watt ist es eingeschaltet\
An  => shelly03:power:\s([0-9]{2,}\.?[0-9]{0,}|[4-9]{1}\.?[0-9]{0,})\
Aus => shelly03:power:\s([0-3]{1}\.?[0-9]{0,}|[0-3]{1})
attr Waschmaschine_Counter event-on-change-reading .*
attr Waschmaschine_Counter event-on-update-reading power.*
attr Waschmaschine_Counter group PV Eigenverbrauch-Steuerung
attr Waschmaschine_Counter icon time_timer
attr Waschmaschine_Counter interval 5
attr Waschmaschine_Counter room Strom->Photovoltaik
attr Waschmaschine_Counter sortby 433
attr Waschmaschine_Counter verbose 0

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.

RAW Definition Brunnen (dummy Modul)

defmod Brunnen dummy
attr Brunnen DbLogExclude .*
attr Brunnen DbLogInclude state
attr Brunnen alias Brunnen
attr Brunnen comment Version 2020.10.19 18:28
attr Brunnen group PV Eigenverbrauch
attr Brunnen icon well
attr Brunnen readingList Brunnen_Button PowerLevelMinTime PowerPhaseUse PowerLimitOn PowerLimitOff RunTimeMin RunTimePerDay SetCmdOff SetCmdOn TimeStart TimeEnd
attr Brunnen room Strom->Photovoltaik
attr Brunnen setList Brunnen_Button:uzsuToggle,on,off PowerLevelMinTime:slider,30,30,300 PowerLimitOn:slider,250,250,2000 PowerLimitOff:slider,0,250,1000 RunTimeMin:slider,300,300,7200 RunTimePerDay:slider,7200,300,28800 SetCmdOff SetCmdOn TimeStart:time TimeEnd:time
attr Brunnen sortby 13
attr Brunnen stateFormat state
attr Brunnen verbose 0
attr Brunnen webCmd Brunnen_Button

setstate Brunnen off
setstate Brunnen 2020-09-21 16:50:48 Brunnen_Button off
setstate Brunnen 2019-12-02 10:29:49 PowerLevelMinTime 300
setstate Brunnen 2019-11-06 10:56:22 PowerLimitOff 600
setstate Brunnen 2020-04-20 13:06:04 PowerLimitOn 900
setstate Brunnen 2019-12-02 15:01:18 RunTimeMin 60
setstate Brunnen 2020-04-02 13:59:34 RunTimePerDay 10800
setstate Brunnen 2020-01-01 16:29:10 SetCmdOff set shelly05 off 0
setstate Brunnen 2020-01-01 16:29:24 SetCmdOn set shelly05 on 0
setstate Brunnen 2020-09-13 15:07:03 TimeEnd 09:00
setstate Brunnen 2019-10-28 09:13:30 TimeStart 09:00
setstate Brunnen 2020-09-21 16:50:48 state off

RAW Definition Brunnen_PV (DOIF Modul)

defmod Brunnen_PV DOIF ################################################################################################################\
## 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" )\
\
    ({Log 3, "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" )\
\
    ({Log 3, "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" )\
\
    ({Log 3, "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] )\
\
    ({Log 3, "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" )\
\
    ({Log 3, "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") )\
\
    ({Log 3, "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] > 600 and\
  [$SELF:cmd_nr] ne "7")\
\
    ({Log 3, "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 )\
\
    ({Log 3, "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") )\
\
    ({Log 3, "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")}\
    )
attr Brunnen_PV DbLogExclude .*
attr Brunnen_PV DbLogInclude state,STATE,cmd.*,Device,Brunnen_Status,wait_timer
attr Brunnen_PV alias Brunnen_PV
attr Brunnen_PV cmdState Maximalzeit pro Tag überschritten|Eigenverbrauch gesperrt|Stop wait timer|Eigenverbrauch freigegeben|Brunnen manuell ein|Brunnen manuell aus|Brunnen laeuft|Brunnen aus|Eigenverbrauch gesperrt
attr Brunnen_PV comment Version 2020.10.19 18:28
attr Brunnen_PV do always
attr Brunnen_PV group PV Eigenverbrauch-Steuerung
attr Brunnen_PV icon well
attr Brunnen_PV room Strom->Photovoltaik
attr Brunnen_PV sortby 31
attr Brunnen_PV stateFormat state : Brunnen_Status
attr Brunnen_PV verbose 0
attr Brunnen_PV wait 0: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.

defmod shelly05 Shelly <IP-Address>
attr shelly05 DbLogExclude .*
attr shelly05 DbLogInclude relay.*,power.*,energy.*
attr shelly05 alias Brunnen und Shaun
attr shelly05 comment Version 2020.10.19 18:28
attr shelly05 event-on-change-reading relay.*,power.*,energy.*,state,network
attr shelly05 group PV Eigenverbrauch-Steuerung
attr shelly05 icon taster_ch_1
attr shelly05 interval 60
attr shelly05 mode relay
attr shelly05 model shelly2.5
attr shelly05 room Shelly,Strom->Photovoltaik
attr shelly05 sortby 32
attr shelly05 stateFormat {sprintf("\
<TABLE>\
\
<TR>\
  <TD VALIGN=\"TOP\" ALIGN=\"LEFT\" WIDTH=\"50\">\
    Status: %s<br>\
    WebLink: %s\
  </TD>\
\
  <TD VALIGN=\"TOP\" ALIGN=\"RIGHT\" WIDTH=\"100\">\
    Brunnen Gesamt 0: %08.2f KWh<br>\
    Shaun Gesamt 1: %08.2f KWh\
  </TD>\
\
  <TD VALIGN=\"TOP\" ALIGN=\"RIGHT\" WIDTH=\"70\">\
    Relais 0: %s %06.1f Watt<br>\
    Relais 1: %s %06.1f Watt<br>\
  </TD>\
</TR>\
\
</TABLE>\
" ,\
(ReadingsVal($name,"state","none") eq "OK") ? "<span style='color:#00FF00'>OK</span>":"<span style='color:#FF0000'>Error</span>",\
ReadingsVal($name,"WebLink","none"),\
ReadingsVal($name,"energy_0",0)/1000,\
ReadingsVal($name,"energy_1",0)/1000,\
(ReadingsVal($name,"relay_0","") eq "off") ? "<span style='color:#FF0000'>off</span>":"<span style='color:#00FF00'>on</span>",\
    ReadingsVal($name,"power_0",0),\
(ReadingsVal($name,"relay_1","") eq "off") ? "<span style='color:#FF0000'>off</span>":"<span style='color:#00FF00'>on</span>",\
    ReadingsVal($name,"power_1",0)\
)}
attr shelly05 userReadings WebLink:network { my $ip=ReadingsVal($NAME,"network","");; $ip =~ s/connected to //gs;; $ip =~ s/<[^>]*>//gs;; return("<html><a href='http://".$ip."/'>WEB</a></html>") }
attr shelly05 webCmd |

RAW Definition Brunnen_Counter (HourCounter Modul)

defmod Brunnen_Counter HourCounter shelly05:power_0:\s[0-9]{2,}(\.[0-9]{1,2})*$  shelly05:power_0:\s[0-9]{1}(\.[0-9]{1,2})*$
attr Brunnen_Counter DbLogExclude .*
attr Brunnen_Counter alias Brunnen_Counter
attr Brunnen_Counter comment Version 2020.10.19 18:28\
On und Off des Pools werden direkt über den Shelly Stromverbrauch getriggert.
attr Brunnen_Counter event-on-change-reading .*
attr Brunnen_Counter group PV Eigenverbrauch-Steuerung
attr Brunnen_Counter icon time_timer
attr Brunnen_Counter interval 5
attr Brunnen_Counter room Strom->Photovoltaik
attr Brunnen_Counter sortby 33
attr Brunnen_Counter verbose 0

Beispiel Akku laden

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.

RAW Definition Shaun (dummy Modul)

defmod Shaun dummy
attr Shaun DbLogExclude .*
attr Shaun DbLogInclude state
attr Shaun alias Shaun
attr Shaun comment Version 2020.10.19 18:28
attr Shaun group PV Eigenverbrauch
attr Shaun icon scene_robo_lawnmower
attr Shaun readingList Shaun_Button PowerLevelMinTime PowerPhaseUse PowerLimitOn PowerLimitOff RunTimeMin RunTimePerDay SetCmdOff SetCmdOn TimeStart TimeEnd
attr Shaun room Strom->Photovoltaik
attr Shaun setList Shaun_Button:uzsuToggle,on,off PowerLevelMinTime:slider,30,30,300 PowerLimitOn:slider,250,250,2000 PowerLimitOff:slider,0,250,1000 RunTimeMin:slider,300,300,7200 RunTimePerDay:slider,7200,300,28800 SetCmdOff SetCmdOn TimeStart:time TimeEnd:time
attr Shaun sortby 14
attr Shaun stateFormat state
attr Shaun verbose 0
attr Shaun webCmd Shaun_Button

setstate Shaun off
setstate Shaun 2019-12-02 10:29:49 PowerLevelMinTime 300
setstate Shaun 2019-11-06 10:56:22 PowerLimitOff 100
setstate Shaun 2020-04-20 13:06:04 PowerLimitOn 300
setstate Shaun 2019-12-02 15:01:18 RunTimeMin 300
setstate Shaun 2020-04-02 13:59:34 RunTimePerDay 21600
setstate Shaun 2020-01-01 16:29:10 SetCmdOff set shelly05 off 1
setstate Shaun 2020-01-01 16:29:24 SetCmdOn set shelly05 on 1
setstate Shaun 2020-10-21 12:30:05 Shaun_Button off
setstate Shaun 2020-09-21 12:05:19 TimeEnd 15:00
setstate Shaun 2020-09-21 12:05:34 TimeStart 12:00
setstate Shaun 2020-10-21 14:00:38 state off

RAW Definition Shaun_PV (DOIF Modul)

defmod Shaun_PV DOIF ################################################################################################################\
## 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" )\
\
    ({Log 3, "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" )\
\
    ({Log 3, "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" )\
\
    ({Log 3, "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] )\
\
    ({Log 3, "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" )\
\
    ({Log 3, "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") )\
\
    ({Log 3, "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] > 15 and\
  [$SELF:cmd_nr] ne "7")\
\
    ({Log 3, "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] <= 6 and\
  [$SELF:cmd_nr] eq "7" )\
\
    ({Log 3, "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" )\
\
    ({Log 3, "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")}\
    )
attr Shaun_PV DbLogExclude .*
attr Shaun_PV DbLogInclude state,STATE,cmd.*,Device,Shaun_Status,wait_timer
attr Shaun_PV alias Shaun_PV
attr Shaun_PV cmdState Maximalzeit pro Tag überschritten|Eigenverbrauch gesperrt|Stop wait timer|Eigenverbrauch freigegeben|Shaun manuell ein|Shaun manuell aus|Shaun laed|Shaun aus|Eigenverbrauch gesperrt
attr Shaun_PV comment Version 2020.10.19 18:28
attr Shaun_PV do always
attr Shaun_PV group PV Eigenverbrauch-Steuerung
attr Shaun_PV icon scene_robo_lawnmower
attr Shaun_PV room Strom->Photovoltaik
attr Shaun_PV sortby 41
attr Shaun_PV stateFormat state : Shaun_Status
attr Shaun_PV verbose 0
attr Shaun_PV wait 0: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.

defmod shelly05 Shelly <IP-Address>
attr shelly05 DbLogExclude .*
attr shelly05 DbLogInclude relay.*,power.*,energy.*
attr shelly05 alias Brunnen und Shaun
attr shelly05 comment Version 2020.10.19 18:28
attr shelly05 event-on-change-reading relay.*,power.*,energy.*,state,network
attr shelly05 group PV Eigenverbrauch-Steuerung
attr shelly05 icon taster_ch_1
attr shelly05 interval 60
attr shelly05 mode relay
attr shelly05 model shelly2.5
attr shelly05 room Shelly,Strom->Photovoltaik
attr shelly05 sortby 32
attr shelly05 stateFormat {sprintf("\
<TABLE>\
\
<TR>\
  <TD VALIGN=\"TOP\" ALIGN=\"LEFT\" WIDTH=\"50\">\
    Status: %s<br>\
    WebLink: %s\
  </TD>\
\
  <TD VALIGN=\"TOP\" ALIGN=\"RIGHT\" WIDTH=\"100\">\
    Brunnen Gesamt 0: %08.2f KWh<br>\
    Shaun Gesamt 1: %08.2f KWh\
  </TD>\
\
  <TD VALIGN=\"TOP\" ALIGN=\"RIGHT\" WIDTH=\"70\">\
    Relais 0: %s %06.1f Watt<br>\
    Relais 1: %s %06.1f Watt<br>\
  </TD>\
</TR>\
\
</TABLE>\
" ,\
(ReadingsVal($name,"state","none") eq "OK") ? "<span style='color:#00FF00'>OK</span>":"<span style='color:#FF0000'>Error</span>",\
ReadingsVal($name,"WebLink","none"),\
ReadingsVal($name,"energy_0",0)/1000,\
ReadingsVal($name,"energy_1",0)/1000,\
(ReadingsVal($name,"relay_0","") eq "off") ? "<span style='color:#FF0000'>off</span>":"<span style='color:#00FF00'>on</span>",\
    ReadingsVal($name,"power_0",0),\
(ReadingsVal($name,"relay_1","") eq "off") ? "<span style='color:#FF0000'>off</span>":"<span style='color:#00FF00'>on</span>",\
    ReadingsVal($name,"power_1",0)\
)}
attr shelly05 userReadings WebLink:network { my $ip=ReadingsVal($NAME,"network","");; $ip =~ s/connected to //gs;; $ip =~ s/<[^>]*>//gs;; return("<html><a href='http://".$ip."/'>WEB</a></html>") }
attr shelly05 webCmd |

RAW Definition Shaun_Counter (HourCounter Modul)

defmod Shaun_Counter HourCounter shelly05:power_1:\s[0-9]{2,}(\.[0-9]{1,2})*$  shelly05:power_1:\s[0-9]{1}(\.[0-9]{1,2})*$
attr Shaun_Counter DbLogExclude .*
attr Shaun_Counter alias Shaun_Counter
attr Shaun_Counter comment Version 2020.10.19 18:28\
On und Off des Ladens werden direkt über den Shelly Stromverbrauch getriggert.
attr Shaun_Counter event-on-change-reading .*
attr Shaun_Counter group PV Eigenverbrauch-Steuerung
attr Shaun_Counter icon time_timer
attr Shaun_Counter interval 5
attr Shaun_Counter room Strom->Photovoltaik
attr Shaun_Counter sortby 43
attr Shaun_Counter verbose 0

Problemlösung

Hier könnt Ihr Eure Fragen loswerden.

  • Beitrag - Photovoltaik mit Eigenverbrauch Steuerung (Kostal Plenticore; KSEM; BYD HV)

Projekte der FHEM-Community

  • Beitrag - Photovoltaik mit Eigenverbrauch Steuerung (Kostal plenticore; EM410)
  • Beitrag - Kostal Plenticore Bilanz
  • Beitrag - Kostal Plenticore, EM410 (KSEM), BYD, Solarprognose, Forecast
  • Thema - Modul 93_DbRep - Reporting und Management von Datenbankinhalten (DbLog)
  • Thema - Neue Version von HTTPMOD mit neuen Features zum Testen

Links