<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="de">
	<id>http://wiki.fhem.de/w/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Ph1959de</id>
	<title>FHEMWiki - Benutzerbeiträge [de]</title>
	<link rel="self" type="application/atom+xml" href="http://wiki.fhem.de/w/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Ph1959de"/>
	<link rel="alternate" type="text/html" href="http://wiki.fhem.de/wiki/Spezial:Beitr%C3%A4ge/Ph1959de"/>
	<updated>2026-04-17T20:39:34Z</updated>
	<subtitle>Benutzerbeiträge</subtitle>
	<generator>MediaWiki 1.43.6</generator>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=HourCounter&amp;diff=40910</id>
		<title>HourCounter</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=HourCounter&amp;diff=40910"/>
		<updated>2026-04-03T12:35:03Z</updated>

		<summary type="html">&lt;p&gt;Ph1959de: kleine Korrekturen und Formatierungsänderungen&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Infobox Modul&lt;br /&gt;
|ModPurpose=HourCounter dient zum Zählen von Ereignissen und zur Erfassung von Betriebs-/Ruhe-Zeiten&lt;br /&gt;
|ModType=h&lt;br /&gt;
|ModForumArea=MAX&lt;br /&gt;
|ModTechName=98_HourCounter.pm&lt;br /&gt;
|ModOwner=John ([http://forum.fhem.de/index.php?action=profile;u=806 Forum] / [[Benutzer Diskussion:John|Wiki]])}}&lt;br /&gt;
[[HourCounter]] ist ein Perl-Modul, das die Anzahl von Ereignissen erfasst. Bei bipolaren Ereignissen wird zusätzlich die Puls- sowie die Pausendauer ermittelt. Im vorliegenden Beispiel wird ein Betriebsstundenzähler mit einem MAX-Fensterkontakt realisiert.&lt;br /&gt;
&lt;br /&gt;
== Features ==&lt;br /&gt;
* Ermittlung von Betriebsstunden, Auslastung, Verbräuchen, Schalthäufigkeiten&lt;br /&gt;
* Ermittlung der Häufigkeit, Puls- Pausendauer pro Tag&lt;br /&gt;
* Ermittlung der Puls-/Pausendauer der letzten Schaltperiode&lt;br /&gt;
* Bereitstellung von Ereignissen zur Fortführung der Kumulation für Tages-, Wochen- und Monatswerte&lt;br /&gt;
* unterstützende Funktionen zur Ablage von Tages-, Wochen- und Monatswerten&lt;br /&gt;
&lt;br /&gt;
== Zielsetzung des WIKI-Artikels ==&lt;br /&gt;
Erläuterung der Funktionalität zur weiterführenden Diskussion im {{Link2Forum|Topic=12216|Message=72596|LinkText=Forum}}.&lt;br /&gt;
&lt;br /&gt;
== Aktuelle Version ==&lt;br /&gt;
Das Modul ist seit dem 24.10.2014 Bestandteil von FHEM.&lt;br /&gt;
&lt;br /&gt;
== Definition ==&lt;br /&gt;
=== Abstrakt ===&lt;br /&gt;
&amp;lt;pre&amp;gt;define &amp;lt;name&amp;gt; HourCounter &amp;lt;regexp_for_ON&amp;gt; [&amp;lt;regexp_for_Off&amp;gt;]&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;regexp_for_ON&amp;gt; ist ein regulärer Ausdruck der das Ereignis beschreibt.&lt;br /&gt;
&lt;br /&gt;
Wenn auch [&amp;lt;regexp_for_Off&amp;gt;] definiert ist, so sprechen wir von einem bipolaren Ereignis, das einen &lt;br /&gt;
EIN- sowie einen AUS-Zustand aufweist.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;regexp_for_ON&amp;gt; beschreibt in diesem Fall die positive Flanke,[&amp;lt;regexp_for_Off&amp;gt;] die negative Flanke. Die Struktur des regexp-Ausdruckes ist analog zu jener beim Notify aufgebaut.&lt;br /&gt;
&lt;br /&gt;
===Konkret===&lt;br /&gt;
&amp;lt;code&amp;gt;define CN.Test HourCounter SHUTTER.JOHN:onoff:.1 SHUTTER.JOHN:onoff:.0&amp;lt;/code&amp;gt;&lt;br /&gt;
Hier wird der HourCounter CN.TEST definiert.&lt;br /&gt;
Ein MAX-Fensterkontakt mit Namen SHUTTER.JOHN wird als Ereignis-Geber verwendet.&lt;br /&gt;
Das Reading &amp;quot;onoff&amp;quot; wird als Trigger für unserem Zähler genutzt.&lt;br /&gt;
Bei den Fensterkontakten sehen diese Ereignisse wie folgt aus:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
2013-11-15 23:19:12 MAX SHUTTER.JOHN onoff: 1&lt;br /&gt;
....&lt;br /&gt;
2013-11-15 23:19:24 MAX SHUTTER.JOHN onoff: 0&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Soll ein Dummy als Ereignisgeber verwendet werden, lautet die Definition wie folgt:&lt;br /&gt;
:&amp;lt;code&amp;gt;define CN.Test HourCounter myDummy:1 myDummy:0&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die neue Instanz weist folgende Struktur auf:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Text&amp;quot;&amp;gt;&lt;br /&gt;
Internals:&lt;br /&gt;
   CFGFN      &lt;br /&gt;
   DEF        SHUTTER.JOHN:onoff:.1 SHUTTER.JOHN:onoff:.0&lt;br /&gt;
   NAME       CN.Test&lt;br /&gt;
   NR         738&lt;br /&gt;
   NTFY_ORDER 50-CN.Test&lt;br /&gt;
   STATE      1&lt;br /&gt;
   TYPE       HourCounter&lt;br /&gt;
   Readings:&lt;br /&gt;
     2014-02-04 20:59:22   clearDate       2014-02-04 20:59:22&lt;br /&gt;
     2014-02-04 20:59:57   countsOverall   1&lt;br /&gt;
     2014-02-04 20:59:57   countsPerDay    1&lt;br /&gt;
     2014-02-04 20:59:57   pauseTimeIncrement 35&lt;br /&gt;
     2014-02-04 20:59:57   pauseTimeOverall 35&lt;br /&gt;
     2014-02-04 20:59:57   pauseTimePerDay 35&lt;br /&gt;
     2014-02-04 21:00:01   pulseTimeIncrement 4&lt;br /&gt;
     2014-02-04 21:00:01   pulseTimeOverall 4&lt;br /&gt;
     2014-02-04 21:00:01   pulseTimePerDay 4&lt;br /&gt;
     2014-02-04 20:59:57   state           1&lt;br /&gt;
     2014-02-04 21:00:00   tickHour        1&lt;br /&gt;
     2014-02-04 21:00:01   value           0&lt;br /&gt;
   Helper:&lt;br /&gt;
     OFF_Regexp SHUTTER.JOHN:onoff:.0&lt;br /&gt;
     ON_Regexp  SHUTTER.JOHN:onoff:.1&lt;br /&gt;
     calledByEvent &lt;br /&gt;
     changedTimestamp 2014-02-04 21:00:01&lt;br /&gt;
     forceClear &lt;br /&gt;
     forceDayChange &lt;br /&gt;
     forceHourChange &lt;br /&gt;
     forceMonthChange &lt;br /&gt;
     forceWeekChange &lt;br /&gt;
     isFirstRun &lt;br /&gt;
     sdRoundHourLast 1391544000&lt;br /&gt;
     value      0&lt;br /&gt;
     cmdQueue:&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Damit nicht zu viele Ereignisse in den Log-Dateien landen, kann man diese sinnvoll einschränken, so dass&lt;br /&gt;
nur Änderungen das Feuern von Events auslösen&lt;br /&gt;
:&amp;lt;code&amp;gt;attr CN.BRENNER event-on-change-reading .*&amp;lt;/code&amp;gt;&lt;br /&gt;
Wenn sich nun jedoch über Stunden und Tage nichts ändert, sieht man in den Charts keine Daten mehr.&lt;br /&gt;
Mit dieser Anweisung wird erreicht, dass alle Readings nach Aktualisierung spätesten nach 1 Stunden einen Event feuern, auch wenn sich der Wert nicht ändert.&lt;br /&gt;
Eine Ausnahme hiervon sollen machen die tick*-Readings, deren Events sollen immer sofort gefeuert werden, &lt;br /&gt;
wenn sie aktualisiert werden.&lt;br /&gt;
&amp;lt;code&amp;gt;attr CN.BRENNER event-min-interval tick.*:0,.*:3600&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Readings ==&lt;br /&gt;
{| class=&amp;quot;wikitable sortable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Reading !! class=&amp;quot;unsortable&amp;quot; |  Beschreibung &lt;br /&gt;
|-&lt;br /&gt;
| clearDate || Datum, zu dem alle kumulativen Readings  über set .. clear gelöscht wurden&lt;br /&gt;
|-&lt;br /&gt;
| countsOverall || Absolutzähler für das Auftreten des ON-Ereignisses&lt;br /&gt;
|-&lt;br /&gt;
| countsPerDay  || Tageszähler für das Auftreten des ON-Ereignisses&lt;br /&gt;
|-&lt;br /&gt;
| pauseTimeIncrement || Zeitdauer der Pausen-Phase in Sekunden ; wird zyklisch aktualisiert&lt;br /&gt;
|-&lt;br /&gt;
| pauseTimeEdge || Zeitdauer der letzten komplettierten Pausen-Phase&lt;br /&gt;
|-&lt;br /&gt;
| pauseTimeOverall || Zeitdauer in Sekunden über alle aufgetretenen Pause-Phasen&lt;br /&gt;
|-&lt;br /&gt;
| pauseTimePerDay || Zeitdauer in Sekunden über alle aufgetretenen Pause-Phasen des akt. Tages&lt;br /&gt;
|-&lt;br /&gt;
| pulseTimeIncrement ||  Zeitdauer der Puls-Phase in Sekunden ; wird zyklisch aktualisiert&lt;br /&gt;
|-&lt;br /&gt;
| pulseTimeEdge || Zeitdauer der letzten komplettierten Pulse-Phase&lt;br /&gt;
|-&lt;br /&gt;
| pulseTimeOverall || Zeitdauer in Sekunden über alle aufgetretenen Puls-Phasen&lt;br /&gt;
|-&lt;br /&gt;
| pulseTimePerDay || Zeitdauer in Sekunden über alle aufgetretenen Puls-Phasen des akt. Tages&lt;br /&gt;
|-&lt;br /&gt;
| value || Aktueller Schaltzustand gemäss ON/OFF Ereignis,&amp;lt;br /&amp;gt;&lt;br /&gt;
 mit 1=letztes Ereignis war ON-Ereignis &amp;lt;br /&amp;gt;&lt;br /&gt;
 und 0=letztes Ereignis war OFF-Ereignis&lt;br /&gt;
|-&lt;br /&gt;
| tickUpdated || Event wird gefeuert, wenn die operativen Readings beschrieben worden sind (nicht unbedingt gändert)&lt;br /&gt;
|-&lt;br /&gt;
| tickChanged || Event wird nach Änderung von value gefeuert&lt;br /&gt;
|-&lt;br /&gt;
| tickDay || Event wird nach Tageswechsel gefeuert bevor die Tageszähler resettiert werden&lt;br /&gt;
|-&lt;br /&gt;
| tickHour || Event wird nach Stundenwechsel gefeuert&lt;br /&gt;
|-&lt;br /&gt;
| tickWeek || Event wird nach Wochenwechsel gefeuert&lt;br /&gt;
|-&lt;br /&gt;
| tickMonth || Event wird nach Monatswechsel gefeuert&lt;br /&gt;
|-&lt;br /&gt;
| tickYear || Event wird nach Jahreswechsel gefeuert&lt;br /&gt;
|-&lt;br /&gt;
| forceHourChange || simuliert einen Stundenwechsel&lt;br /&gt;
|-&lt;br /&gt;
| forceDayChange || simuliert einen Tageswechsel&lt;br /&gt;
|-&lt;br /&gt;
| forceWeekChange || simuliert einen Wochenwechsel&lt;br /&gt;
|-&lt;br /&gt;
| forceMonthChange || simuliert einen Monatswechsel&lt;br /&gt;
|-&lt;br /&gt;
| forceYearChange || simuliert einen Jahreswechsel&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Web-Oberfläche ==&lt;br /&gt;
[[Datei:13_11_15_HourCounter_Face.png|HourCounter]]&lt;br /&gt;
&lt;br /&gt;
== Anwendung ==&lt;br /&gt;
Nachfolgende Darstellung zeigt das Einschaltverhalten eines Heizkessels zusammen mit den abgeleiteten Werten.&lt;br /&gt;
&lt;br /&gt;
[[Datei:13_11_15_HourCounter_Chart.png]]&lt;br /&gt;
* die Kurve &amp;quot;Brenner EIN&amp;quot; zeigt die Trigger-Signale des ON/OFF Filters, also das Ein-/Ausschalten des Brenners&lt;br /&gt;
* die Kurve &amp;quot;Brenner-Starts&amp;quot; zeigt die über den Tag aufgelaufenen Starts, also chronologisch das Anwachsen von Reading countsPerDay&lt;br /&gt;
* die Kurve &amp;quot;Betriebsstunden&amp;quot; zeigt die aufgelaufene Zeit aus dem Reading pulseTimePerDay umgerechnet zu Stunden&lt;br /&gt;
* die Kurve &amp;quot;Dauer&amp;quot; zeigt die Dauer des letzten Pulses in Sekunden&lt;br /&gt;
* die Kurve Auslastung zeigt das Verhältnis des Readings pulseTimePerDay zur seit Tagesbeginn vergangenen Zeit.&lt;br /&gt;
&lt;br /&gt;
=== Chart der Aktualwerte ===&lt;br /&gt;
Wir benötigen hierzu ein File-Archiv für die aufgelaufenen Daten.&lt;br /&gt;
:&amp;lt;code&amp;gt;define CN.Test.File FileLog ./log/CN.Test-%Y.log (CN\.Test:.*)&amp;lt;/code&amp;gt;&lt;br /&gt;
Man erhält nach den ersten Ereignissen Einträge in folgender Form:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Text&amp;quot;&amp;gt;&lt;br /&gt;
2013-11-16_12:45:40 CN.Test value: 1&lt;br /&gt;
2013-11-16_12:45:40 CN.Test 1&lt;br /&gt;
2013-11-16_12:46:21 CN.Test pulseTimeIncrement: 41    &lt;br /&gt;
2013-11-16_12:46:21 CN.Test pulseTimePerDay: 41&lt;br /&gt;
2013-11-16_12:46:21 CN.Test pulseTimeOverall: 41&lt;br /&gt;
2013-11-16_12:46:21 CN.Test value: 0&lt;br /&gt;
2013-11-16_12:50:38 CN.Test countsPerDay: 2&lt;br /&gt;
2013-11-16_12:50:38 CN.Test countsOverall: 2&lt;br /&gt;
2013-11-16_12:50:38 CN.Test pauseTimeIncrement: 257&lt;br /&gt;
2013-11-16_12:50:38 CN.Test pauseTimePerDay: 756&lt;br /&gt;
2013-11-16_12:50:38 CN.Test pauseTimeOverall: 756&lt;br /&gt;
2013-11-16_12:50:38 CN.Test value: 1&lt;br /&gt;
2013-11-16_12:50:38 CN.Test 2&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Nun kann man den Chart definieren:&lt;br /&gt;
[[Datei:13_11_16_HourCounter_ChartBuild_01.png]]&lt;br /&gt;
Für die Kurve &amp;quot;Brenner EIN&amp;quot; verwenden wir CN.Test.value. Damit diese als unterste Kurve dargestellt wird transformieren wir den Wert 1 auf -2 und alle anderen (also die 0) auf -21 mit folgender Funktion:&lt;br /&gt;
:&amp;lt;code&amp;gt;$fld[3]=~&amp;quot;1&amp;quot;?-2:-19&amp;lt;/code&amp;gt;&lt;br /&gt;
Die &amp;quot;Brenner Starts&amp;quot; können wir direkt von countsPerDay ableiten.&lt;br /&gt;
&lt;br /&gt;
Für die &amp;quot;Betriebsstunden&amp;quot; verwenden wir pulseTimePerDay. Da diese in Sekunden vorliegen teilen wir den Wert durch 3600, um Stunden zu erhalten.&lt;br /&gt;
:&amp;lt;code&amp;gt;$fld[3]/=3600&amp;lt;/code&amp;gt;&lt;br /&gt;
Als letzten versorgen wir noch die Kurve &amp;quot;Dauer&amp;quot; mit pulseTimeIncrement. Da wir diese in Minuten haben wollen, ist ebenfalls eine Umformung nötig.&lt;br /&gt;
:&amp;lt;code&amp;gt;$fld[3]/=60&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Somit sind die Basis-Kurven angelegt.&lt;br /&gt;
&lt;br /&gt;
=== Erweiterungen ===&lt;br /&gt;
Es wurden im Forum viele Wünsche formuliert, weitere Funktionalitäten für den HourCounter einzuführen.&lt;br /&gt;
&lt;br /&gt;
* Aggregation über bestimmte oder ganz freie Zeiträume&lt;br /&gt;
* komplexe Berechnungen, die zum Verbrauch führen&lt;br /&gt;
* Zuordnung von Verbräuchen zu unterschiedlichen Countern nach bestimmten Bedingungen&lt;br /&gt;
&lt;br /&gt;
Vor allem die Aggregation erfasster Werte in Stunden-, Tages-, Wochen- und Monatswerten ist eine sinnvolle&lt;br /&gt;
Erweiterung bei der Verbrauchserfassung.&lt;br /&gt;
&lt;br /&gt;
HourCounter bietet Schnittstellen an, die es ermöglichen, das Modul selbst mit neuen Eigenschaften zu erweitern.&lt;br /&gt;
&lt;br /&gt;
Die Referenz-Implementierung in 99_UtilsHourCounter.pm zeigt, wie dies skript-technisch zu realisieren ist.&lt;br /&gt;
&lt;br /&gt;
==== Installation ====&lt;br /&gt;
Die jeweils aktuelle Version von 99_UtilsHourCounter kann über diesen &lt;br /&gt;
[https://svn.fhem.de/trac/export/HEAD/trunk/fhem/contrib/99_UtilsHourCounter.pm Link] bezogen werden.&lt;br /&gt;
&lt;br /&gt;
Die Datei ist in das Unterverzeichnis FHEM vom FHEM-Homverzeichnis zu kopieren.&lt;br /&gt;
&lt;br /&gt;
z.B. bei Raspberry Pi: /opt/fhem/FHEM&lt;br /&gt;
&lt;br /&gt;
Nach dem Kopieren und einem Neustart von FHEM kann man überprüfen, ob FHEM diese Datei findet.&lt;br /&gt;
Wenn man das Menü &amp;quot;Edit Files &amp;quot; anwählt, wird auch 99_UtilsHourCounter angezeigt.&lt;br /&gt;
&lt;br /&gt;
==== Readings ====&lt;br /&gt;
99_UtilsHourCounter aus dem contrib-Verzeichnis der FHEM-Installation erweitert den HourCounter um folgende Funktionen:&lt;br /&gt;
{| class=&amp;quot;wikitable sortable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Reading !! class=&amp;quot;unsortable&amp;quot; |  Beschreibung&lt;br /&gt;
|-&lt;br /&gt;
| appCountsPerHour || Stundenzähler, wird bei Stundenwechsel aktualisiert&lt;br /&gt;
|-&lt;br /&gt;
| appCountsPerHourTemp || Arbeitszähler zu appCountsPerHour&lt;br /&gt;
|-&lt;br /&gt;
| appCountsPerDay || Tageszähler, wird bei Tageswechsel aktualisiert (Arbeitszähler ist countsPerDay)&lt;br /&gt;
|-&lt;br /&gt;
| appCountsPerWeek || Wochenzähler, wird bei Wochenwechsel aktualisiert&lt;br /&gt;
|-&lt;br /&gt;
| appCountsPerWeekTemp || Arbeitszähler zu appCountsPerWeek&lt;br /&gt;
|-&lt;br /&gt;
| appCountsPerMonth || Monatszähler, wird bei Monatswechsel aktualisiert&lt;br /&gt;
|-&lt;br /&gt;
| appCountsPerMonthTemp || Arbeitszähler zu appCountsPerMonth&lt;br /&gt;
|-&lt;br /&gt;
| appCountsPerYear || Jahreszähler, wird bei Jahreswechsel aktualisiert&lt;br /&gt;
|-&lt;br /&gt;
| appCountsPerYearTemp || Arbeitszähler zu appCountsPerYear&lt;br /&gt;
|-&lt;br /&gt;
| appOpHoursPerDay || Betriebsstunden des Tages&lt;br /&gt;
|-&lt;br /&gt;
| appOpHoursPerDayTemp || Arbeitszähler zu appOpHoursPerDay&lt;br /&gt;
|-&lt;br /&gt;
| appOpHoursPerWeek || Betriebsstunden der Woche&lt;br /&gt;
|-&lt;br /&gt;
| appOpHoursPerWeekTemp || Arbeitszähler zu appOpHoursPerWeek&lt;br /&gt;
|-&lt;br /&gt;
| appOpHoursPerMonth || Betriebsstunden des Monats&lt;br /&gt;
|-&lt;br /&gt;
| appOpHoursPerMonthTemp || Arbeitszähler appOpHoursPerMonth&lt;br /&gt;
|-&lt;br /&gt;
| appOpHoursPerYear || Betriebsstunden des Jahres&lt;br /&gt;
|-&lt;br /&gt;
| appOpHoursPerYearTemp || Arbeitszähler appOpHoursPerYear&lt;br /&gt;
|-&lt;br /&gt;
| appUtilization || Auslastung = pulseTimePerDay /(vergangene Sekunden seit Tagesbeginn) * 100&lt;br /&gt;
|-&lt;br /&gt;
| appUtilizationTemp || Arbeitsvariable zu appUtilization&lt;br /&gt;
|}&lt;br /&gt;
Beginn der Woche ist jeweils der Sonntag.&lt;br /&gt;
&lt;br /&gt;
Mit folgender Anweisung aktivieren wir die Erweiterungen: &lt;br /&gt;
:&amp;lt;code&amp;gt;define CN.EVENT notify CN\..*:tick.* { appHCNotify(&amp;quot;$NAME&amp;quot;,&amp;quot;$EVTPART0&amp;quot;,&amp;quot;$EVTPART1&amp;quot;);;}&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Spätestens nach einer steigenden und einer fallenden Flanke sind die zuvor genannten app*-Readings zu sehen.&lt;br /&gt;
{{Randnotiz|RNTyp=Info|RNText=Die gezeigten define-Anweisungen müssen jeweils in einer Zeile stehen (keine Zeilenumbrüche!).}}&lt;br /&gt;
&lt;br /&gt;
Die neuen Readings werden automatisch in den &amp;quot;Setter&amp;quot; der Web-Oberflächen aufgenommen. Dies gilt für alle Readings, die mit &amp;quot;app&amp;quot; beginnen.&lt;br /&gt;
&lt;br /&gt;
Somit können die neuen Readings beliebig manipuliert werden.&lt;br /&gt;
&lt;br /&gt;
===== Für Anfänger, die noch keine Erfahrungen mit Regular Expressions haben:=====&lt;br /&gt;
Benennt eure Hourcounter nach dem Muster CN.&amp;lt;euer Wunschname&amp;gt;. Dann wird der notify immer funktionieren.&lt;br /&gt;
&lt;br /&gt;
Beispiel: statt &amp;quot;PelletsCounter&amp;quot; wählt den Namen &amp;quot;CN.PelletsCounter&amp;quot;&lt;br /&gt;
&lt;br /&gt;
==== Archiv für Tages-/Wochen-/Monats-/Jahreswerte anlegen ====&lt;br /&gt;
Nun wollen wir die aggregierten Werte in eine eigene Datei speichern. Dies gelingt mit &lt;br /&gt;
:&amp;lt;code&amp;gt;define CN.Test.FileDay FileLog ./log/CN.Test-Day-%Y.log CN.Test:app\w*(Utilization|PerHour|PerDay|PerWeek|PerMonth|PerYear)(?!Temp).* &amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Im Klartext:&lt;br /&gt;
* verwende alle Werte des Counters CN.Test, deren Reading mit &amp;quot;app&amp;quot; beginnt&lt;br /&gt;
* und die einen der Terme appUtilization|PerHour|PerDay|PerWeek|PerMonth|PerYear beinhalten&lt;br /&gt;
* und die danach nicht dem Term &amp;quot;Temp&amp;quot; beinhalten&lt;br /&gt;
&lt;br /&gt;
== Fragen und Antworten ==&lt;br /&gt;
==== Betriebsstundenzähler über Leistungsmessung ableiten ====&lt;br /&gt;
&#039;&#039;&#039;Frage:&#039;&#039;&#039;&lt;br /&gt;
Ich würde gerne zählen, wenn ich mehr Strom als Standy verbrauche (also mehr als 2&amp;amp;nbsp;Watt)&lt;br /&gt;
und keine Betriebsstunden zählen, wenn der Verbrauch unter 2&amp;amp;nbsp;Watt ist. Ist das möglich?&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;Beispiel für die Events&amp;lt;/u&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
013-11-18_19:40:32 XXX power: 1.9&lt;br /&gt;
2013-11-18_19:40:32 XXX consumption: 2&lt;br /&gt;
2013-11-18_19:40:32 XXX consumptionTotal: 2&lt;br /&gt;
2013-11-18_19:40:36 XXX power: 27&lt;br /&gt;
2013-11-18_19:40:36 XXX consumption: 2&lt;br /&gt;
2013-11-18_19:40:36 XXX consumptionTotal: 2&lt;br /&gt;
2013-11-18_19:40:42 XXX power: 34.6&lt;br /&gt;
2013-11-18_19:40:42 XXX consumption: 2 &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Antwort&#039;&#039;&#039;&lt;br /&gt;
Die hier vorgestellte Lösung überprüft, ob der Wert des Events power eine oder zwei Ziffern vor dem Komma hat.&lt;br /&gt;
Deshalb wir hier erst gezählt, wenn die Schwelle von 10Watt überschritten wird. &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
define CN.Test HourCounter XXX:power:\s[0-9]{2,}(\.[0-9]{1,3})*$  XXX:power:\s[0-9]{1}(\.[0-9]{1,3})*$&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Erläuterung zu &amp;lt;code&amp;gt;&amp;lt;regexp_for_ON&amp;gt; = XXX:power:\s[0-9]{2,}(\.[0-9]{1,3})*$&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;quot;XXX&amp;quot;  bezeichnet das Device, der Term danach ist der regexp-Filte für das On-Ereignis&lt;br /&gt;
* &amp;quot;power:&amp;quot; das Ereignis muss mit diesem Term beginnen&lt;br /&gt;
* &amp;quot;\s&amp;quot; es muss ein Leerzeichen folgen&lt;br /&gt;
* &amp;quot;[0-9]{2,}&amp;quot; es müssen mindestens 2 Ziffern folgen&lt;br /&gt;
* &amp;quot;(\.[0-9]{1,3})*&amp;quot; wenn ein Punkt folgt, dann müssen auf diesen mindestens 1..3 Ziffern folgen&lt;br /&gt;
* &amp;quot;$&amp;quot; danach darf kein weiteres Zeichen mehr folgen&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Antwort Möglichkeit 2&#039;&#039;&#039;&lt;br /&gt;
In dieser Lösung bekommt das entsprechende Device, das mit HourCounter überwacht werden soll, ein [[userReadings]] &amp;quot;onoff&amp;quot;. Dieses Reading wird dann zum Schalten von Hour Counter verwendet:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
define GPIO4_DS18B20_Waermepumpe_Vorlauf GPIO4 28-000005956079&lt;br /&gt;
attr GPIO4_DS18B20_Waermepumpe_Vorlauf alias Wärmepumpe Vorlauf&lt;br /&gt;
attr GPIO4_DS18B20_Waermepumpe_Vorlauf model DS18B20&lt;br /&gt;
attr GPIO4_DS18B20_Waermepumpe_Vorlauf room GPIO4&lt;br /&gt;
attr GPIO4_DS18B20_Waermepumpe_Vorlauf userReadings onoff {(ReadingsVal(&amp;quot;GPIO4_DS18B20_Waermepumpe_Vorlauf&amp;quot;,&amp;quot;temperature&amp;quot;,0) &amp;gt;28)?1:0;;}&lt;br /&gt;
&lt;br /&gt;
define Waermepumpe_HourCounter HourCounter GPIO4_DS18B20_Waermepumpe_Vorlauf:onoff:.1  GPIO4_DS18B20_Waermepumpe_Vorlauf:onoff:.0&lt;br /&gt;
attr Waermepumpe_HourCounter room 2_Fussbodenheizung&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Erläuterungen zu dem Code: &amp;lt;code&amp;gt;{(ReadingsVal(&amp;quot;GPIO4_DS18B20_Waermepumpe_Vorlauf&amp;quot;,&amp;quot;temperature&amp;quot;,0) &amp;gt;28)?1:0;;}&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;(ReadingsVal(&amp;quot;GPIO4_DS18B20_Waermepumpe_Vorlauf&amp;quot;,&amp;quot;temperature&amp;quot;,0) &amp;gt;28)&amp;lt;/code&amp;gt; Diese Bedingung für das userReadings onoff prüft bei jedem Event, ob der Wert von temperature größer als 28 ist. &lt;br /&gt;
* &amp;lt;code&amp;gt;?1:0&amp;lt;/code&amp;gt; Ist dies der Fall wird das userReading onoff auf 1 gesetzt andernfalls auf 0.&lt;br /&gt;
Auf Basis deses UserReadings wird dann der HourCounter definiert:&lt;br /&gt;
* &amp;lt;code&amp;gt;GPIO4_DS18B20_Waermepumpe_Vorlauf:onoff:.1&amp;lt;/code&amp;gt; Einschaltbedingung für HourCounter&lt;br /&gt;
* &amp;lt;code&amp;gt;GPIO4_DS18B20_Waermepumpe_Vorlauf:onoff:.0&amp;lt;/code&amp;gt; Abschaltbedingung für Hour Counter&lt;br /&gt;
&lt;br /&gt;
==== Welche Anwendungsfälle sind denkbar ? ====&lt;br /&gt;
{{Link2Forum|Topic=12216|Message=175163|LinkText=Aus dem Forum}}&lt;br /&gt;
* Betriebsstundenzähler für meine &amp;quot;Fliegenkiller-Steckdose&amp;quot;&lt;br /&gt;
* Nutzungsdauer beschränken für TV,Internet oder Spielkonsolen für entnervte Eltern&lt;br /&gt;
* Nutzungsdauer ermitteln zur Energieeinsparung (Klimageräte, Ventilatoren, Dunstabzugshauben etc.)&lt;br /&gt;
* Lüftungsverhalten ermitteln (wie lange Fenster pro Tag geöffnet)&lt;br /&gt;
&lt;br /&gt;
{{Link2Forum|Topic=12216|Message=195358|LinkText=Brenner Starts/Verbrauch + akkumulierte Werte}}&lt;br /&gt;
&lt;br /&gt;
{{Link2Forum|Topic=12216|Message=196358|LinkText=Ölverbrauch+Solar-Ladung}}&lt;br /&gt;
&lt;br /&gt;
[http://voizchat.de/gaszaehler-verbrauch-erfassen-mit-fhem-und-raspberry-gpio/ Gaszähler mit HourCounter realisieren] (URL tot)&lt;br /&gt;
&lt;br /&gt;
==== Seltene Schaltvorgänge ====&lt;br /&gt;
Die Schaltvorgänge sind über den Tag sehr wenige. Die Aktualisierung erfolgt immer erst bei der negativen Flanke. Wie kann man eine häufigere Aktualisierung erreichen ?&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Antwort&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Ab Version 1.0.0.6 ist wurde das Attribut &amp;quot;interval&amp;quot; eingeführt; es ist auf 60 Minuten voreingestellt und kann von 5..60 im 5 Minuten-Raster festgelegt werden.&lt;br /&gt;
Es bestimmt, nach welcher Zeit Puls-/Pausendauer aktualisiert werden sollen, unabhängig vom Auftreten einer Schaltflanke.&lt;br /&gt;
&lt;br /&gt;
==== Korrekte Darstellung der akkumulierten Daten im Chart ====&lt;br /&gt;
&#039;&#039;&#039;Frage&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;quot;appCountsPerDay: 4&amp;quot; bezieht sich auf die Counts des Tages 2014-06-16, trägt aber selbst den Zeitstempel 2014-06-17 und wird demnach in einem Chart auch über den Tag  &amp;quot; 2014-06-17&amp;quot; dargestellt.&lt;br /&gt;
Das Problem betrifft alle akkumulierten Daten des HourCounters.&lt;br /&gt;
Wie erreicht man im Chart die korrekte Darstellung ?&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Antwort&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Das Thema wurde {{Link2Forum|Topic=12216|Message=239929|LinkText=hier}} diskutiert.&lt;br /&gt;
Eine Lösung findet man mit [[LogProxy]]. Damit läßt sich ein negativer Offset für die X-Achse definieren, so dass die Daten wieder korrekt dargestellt werden.&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:MAX]]&lt;br /&gt;
[[Kategorie:HOWTOS]]&lt;br /&gt;
[[Kategorie:Heizungssteuerung]]&lt;/div&gt;</summary>
		<author><name>Ph1959de</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=HourCounter&amp;diff=40898</id>
		<title>HourCounter</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=HourCounter&amp;diff=40898"/>
		<updated>2026-03-31T16:47:18Z</updated>

		<summary type="html">&lt;p&gt;Ph1959de: kleine Korrekturen&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Infobox Modul&lt;br /&gt;
|ModPurpose=HourCounter dient zum Zählen von Ereignissen und zur Erfassung von Betriebs-/Ruhe-Zeiten&lt;br /&gt;
|ModType=h&lt;br /&gt;
&amp;lt;!-- |ModCmdRef=LightScene -- nicht erforderlich, da Modultyp x --&amp;gt;&lt;br /&gt;
|ModForumArea=MAX&lt;br /&gt;
|ModTechName=98_HourCounter.pm&lt;br /&gt;
|ModOwner=John ([http://forum.fhem.de/index.php?action=profile;u=806 Forum] / [[Benutzer Diskussion:John|Wiki]])}}&lt;br /&gt;
&lt;br /&gt;
[[HourCounter]] ist ein Perl-Modul, das die Anzahl von Ereignissen erfasst. Bei bipolaren Ereignissen wird zusätzlich die Puls- sowie die Pausendauer ermittelt. Im vorliegenden Beispiel wird ein Betriebsstundenzähler mit einem MAX-Fensterkontakt realisiert.&lt;br /&gt;
&lt;br /&gt;
== Features ==&lt;br /&gt;
* Ermittlung von Betriebsstunden, Auslastung, Verbräuchen, Schalthäufigkeiten&lt;br /&gt;
* Ermittlung der Häufigkeit, Puls- Pausendauer pro Tag&lt;br /&gt;
* Ermittlung der Puls-/Pausendauer der letzten Schaltperiode&lt;br /&gt;
* Bereitstellung von Ereignissen zur Fortführung der Kumulation für Tages-, Wochen- und Monatswerte&lt;br /&gt;
* unterstützende Funktionen zur Ablage von Tages-, Wochen- und Monatswerten&lt;br /&gt;
&lt;br /&gt;
== Zielsetzung des WIKI-Artikels ==&lt;br /&gt;
Erläuterung der Funktionalität zur weiterführenden Diskussion im {{Link2Forum|Topic=12216|Message=72596|LinkText=Forum}}.&lt;br /&gt;
&lt;br /&gt;
== Aktuelle Version ==&lt;br /&gt;
Das Modul ist seit dem 24.10.2014 Bestandteil von FHEM.&lt;br /&gt;
&lt;br /&gt;
== Definition ==&lt;br /&gt;
=== Abstrakt ===&lt;br /&gt;
&amp;lt;pre&amp;gt;define &amp;lt;name&amp;gt; HourCounter &amp;lt;regexp_for_ON&amp;gt; [&amp;lt;regexp_for_Off&amp;gt;]&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;regexp_for_ON&amp;gt; ist ein regulärer Ausdruck der das Ereignis beschreibt.&lt;br /&gt;
&lt;br /&gt;
Wenn auch [&amp;lt;regexp_for_Off&amp;gt;] definiert ist, so sprechen wir von einem bipolarem Ereignis, das einen &lt;br /&gt;
EIN- sowie einen AUS-Zustand aufweist.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;regexp_for_ON&amp;gt; beschreibt in diesem Fall die positive Flanke,[&amp;lt;regexp_for_Off&amp;gt;] die negative Flanke. Die Struktur des regexp-Ausdruckes ist analog zu jener beim Notify aufgebaut.&lt;br /&gt;
&lt;br /&gt;
===Konkret===&lt;br /&gt;
&amp;lt;pre&amp;gt;define CN.Test HourCounter SHUTTER.JOHN:onoff:.1 SHUTTER.JOHN:onoff:.0&amp;lt;/pre&amp;gt;&lt;br /&gt;
Hier wird der HourCounter CN.TEST definiert.&lt;br /&gt;
Ein MAX-Fensterkontakt mit Namen SHUTTER.JOHN wird als Ereignis-Geber verwendet.&lt;br /&gt;
Das Reading &amp;quot;onoff&amp;quot; wird als Trigger für unserem Zähler genutzt.&lt;br /&gt;
Bei den Fensterkontakten sehen diese Ereignisse wie folgt aus:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
2013-11-15 23:19:12 MAX SHUTTER.JOHN onoff: 1&lt;br /&gt;
....&lt;br /&gt;
2013-11-15 23:19:24 MAX SHUTTER.JOHN onoff: 0&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Soll ein Dummy als Ereignisgeber verwendet werden, lautet die Definition wie folgt:&lt;br /&gt;
&amp;lt;pre&amp;gt; define CN.Test HourCounter myDummy:1 myDummy:0&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die neue Instanz weist folgende Struktur auf:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Internals:&lt;br /&gt;
   CFGFN      &lt;br /&gt;
   DEF        SHUTTER.JOHN:onoff:.1 SHUTTER.JOHN:onoff:.0&lt;br /&gt;
   NAME       CN.Test&lt;br /&gt;
   NR         738&lt;br /&gt;
   NTFY_ORDER 50-CN.Test&lt;br /&gt;
   STATE      1&lt;br /&gt;
   TYPE       HourCounter&lt;br /&gt;
   Readings:&lt;br /&gt;
     2014-02-04 20:59:22   clearDate       2014-02-04 20:59:22&lt;br /&gt;
     2014-02-04 20:59:57   countsOverall   1&lt;br /&gt;
     2014-02-04 20:59:57   countsPerDay    1&lt;br /&gt;
     2014-02-04 20:59:57   pauseTimeIncrement 35&lt;br /&gt;
     2014-02-04 20:59:57   pauseTimeOverall 35&lt;br /&gt;
     2014-02-04 20:59:57   pauseTimePerDay 35&lt;br /&gt;
     2014-02-04 21:00:01   pulseTimeIncrement 4&lt;br /&gt;
     2014-02-04 21:00:01   pulseTimeOverall 4&lt;br /&gt;
     2014-02-04 21:00:01   pulseTimePerDay 4&lt;br /&gt;
     2014-02-04 20:59:57   state           1&lt;br /&gt;
     2014-02-04 21:00:00   tickHour        1&lt;br /&gt;
     2014-02-04 21:00:01   value           0&lt;br /&gt;
   Helper:&lt;br /&gt;
     OFF_Regexp SHUTTER.JOHN:onoff:.0&lt;br /&gt;
     ON_Regexp  SHUTTER.JOHN:onoff:.1&lt;br /&gt;
     calledByEvent &lt;br /&gt;
     changedTimestamp 2014-02-04 21:00:01&lt;br /&gt;
     forceClear &lt;br /&gt;
     forceDayChange &lt;br /&gt;
     forceHourChange &lt;br /&gt;
     forceMonthChange &lt;br /&gt;
     forceWeekChange &lt;br /&gt;
     isFirstRun &lt;br /&gt;
     sdRoundHourLast 1391544000&lt;br /&gt;
     value      0&lt;br /&gt;
     cmdQueue:&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Damit nicht zu viele Ereignisse in den Log-Dateien landen, kann man diese sinnvoll einschränken, so daß&lt;br /&gt;
nur Änderungen das Feuern von Events auslösen&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
attr CN.BRENNER event-on-change-reading .*&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Wenn sich nun jedoch über Stunden und Tage nichts ändert, sieht man in den Charts keine Daten mehr.&lt;br /&gt;
Mit dieser Anweisung wird erreicht, daß alle Readings nach Aktualisierung spätesten nach 1 Stunden einen Event feuern, auch wenn sich der Wert nicht ändert.&lt;br /&gt;
Eine Ausnahme hiervon sollen machen die  tick*-Readings, deren Events sollen immer sofort gefeuert werden, &lt;br /&gt;
wenn sie aktualisiert werden.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
attr CN.BRENNER event-min-interval tick.*:0,.*:3600&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Readings ==&lt;br /&gt;
{| class=&amp;quot;wikitable sortable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Reading !! class=&amp;quot;unsortable&amp;quot; |  Beschreibung &lt;br /&gt;
|-&lt;br /&gt;
| clearDate || Datum, zu dem alle kumulativen Readings  über set .. clear gelöscht wurden&lt;br /&gt;
|-&lt;br /&gt;
| countsOverall || Absolutzähler für das Auftreten des ON-Ereignisses&lt;br /&gt;
|-&lt;br /&gt;
| countsPerDay  || Tageszähler für das Auftreten des ON-Ereignisses&lt;br /&gt;
|-&lt;br /&gt;
| pauseTimeIncrement || Zeitdauer der Pausen-Phase in Sekunden ; wird zyklisch aktualisiert&lt;br /&gt;
|-&lt;br /&gt;
| pauseTimeEdge || Zeitdauer der letzten komplettierten Pausen-Phase&lt;br /&gt;
|-&lt;br /&gt;
| pauseTimeOverall || Zeitdauer in Sekunden über alle aufgetretenen Pause-Phasen&lt;br /&gt;
|-&lt;br /&gt;
| pauseTimePerDay || Zeitdauer in Sekunden über alle aufgetretenen Pause-Phasen des akt. Tages&lt;br /&gt;
|-&lt;br /&gt;
| pulseTimeIncrement ||  Zeitdauer der Puls-Phase in Sekunden ; wird zyklisch aktualisiert&lt;br /&gt;
|-&lt;br /&gt;
| pulseTimeEdge || Zeitdauer der letzten komplettierten Pulse-Phase&lt;br /&gt;
|-&lt;br /&gt;
| pulseTimeOverall || Zeitdauer in Sekunden über alle aufgetretenen Puls-Phasen&lt;br /&gt;
|-&lt;br /&gt;
| pulseTimePerDay || Zeitdauer in Sekunden über alle aufgetretenen Puls-Phasen des akt. Tages&lt;br /&gt;
|-&lt;br /&gt;
| value || Aktueller Schaltzustand gemäss ON/OFF Ereignis,&amp;lt;br /&amp;gt;&lt;br /&gt;
 mit 1=letztes Ereignis war ON-Ereignis &amp;lt;br /&amp;gt;&lt;br /&gt;
 und 0=letztes Ereignis war OFF-Ereignis&lt;br /&gt;
|-&lt;br /&gt;
| tickUpdated || Event wird gefeuert, wenn die operativen Readings beschrieben worden sind (nicht unbedingt gändert)&lt;br /&gt;
|-&lt;br /&gt;
| tickChanged || Event wird nach Änderung von value gefeuert&lt;br /&gt;
|-&lt;br /&gt;
| tickDay || Event wird nach Tageswechsel gefeuert bevor die Tageszähler resettiert werden&lt;br /&gt;
|-&lt;br /&gt;
| tickHour || Event wird nach Stundenwechsel gefeuert&lt;br /&gt;
|-&lt;br /&gt;
| tickWeek || Event wird nach Wochenwechsel gefeuert&lt;br /&gt;
|-&lt;br /&gt;
| tickMonth || Event wird nach Monatswechsel gefeuert&lt;br /&gt;
|-&lt;br /&gt;
| tickYear || Event wird nach Jahreswechsel gefeuert&lt;br /&gt;
|-&lt;br /&gt;
| forceHourChange || simuliert einen Stundenwechsel&lt;br /&gt;
|-&lt;br /&gt;
| forceDayChange || simuliert einen Tageswechsel&lt;br /&gt;
|-&lt;br /&gt;
| forceWeekChange || simuliert einen Wochenwechsel&lt;br /&gt;
|-&lt;br /&gt;
| forceMonthChange || simuliert einen Monatswechsel&lt;br /&gt;
|-&lt;br /&gt;
| forceYearChange || simuliert einen Jahreswechsel&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Web-Oberfläche ==&lt;br /&gt;
[[Datei:13_11_15_HourCounter_Face.png|HourCounter]]&lt;br /&gt;
&lt;br /&gt;
== Anwendung ==&lt;br /&gt;
Nachfolgende Darstellung zeigt das Einschaltverhalten eines Heizkessels zusammen mit den &lt;br /&gt;
abgeleiteten Werten.&lt;br /&gt;
&lt;br /&gt;
[[Datei:13_11_15_HourCounter_Chart.png]]&lt;br /&gt;
&lt;br /&gt;
* die Kurve &amp;quot;Brenner EIN&amp;quot; zeigt die Trigger-Signale des ON/OFF Filters, also das Ein-/Ausschalten des Brenners&lt;br /&gt;
* die Kurve &amp;quot;Brenner-Starts&amp;quot; zeigt die über den Tag aufgelaufenen Starts, also chronologisch das Anwachsen von Reading countsPerDay&lt;br /&gt;
* die Kurve &amp;quot;Betriebsstunden&amp;quot; zeigt die aufgelaufene Zeit aus dem Reading pulseTimePerDay umgerechnet zu Stunden&lt;br /&gt;
* die Kurve &amp;quot;Dauer&amp;quot; zeigt die Dauer des letzten Pulses in Sekunden&lt;br /&gt;
* die Kurve Auslastung zeigt das Verhältnis des Readings pulseTimePerDay zur seit Tagesbeginn vergangenen Zeit.&lt;br /&gt;
&lt;br /&gt;
=== Chart der Aktualwerte ===&lt;br /&gt;
Wir benötigen hierzu ein File-Archiv für die aufgelaufenen Daten.&lt;br /&gt;
&amp;lt;pre&amp;gt;define CN.Test.File FileLog ./log/CN.Test-%Y.log (CN\.Test:.*)&amp;lt;/pre&amp;gt;&lt;br /&gt;
Man erhält nach den ersten Ereignissen Einträge in folgender Form:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
2013-11-16_12:45:40 CN.Test value: 1&lt;br /&gt;
2013-11-16_12:45:40 CN.Test 1&lt;br /&gt;
2013-11-16_12:46:21 CN.Test pulseTimeIncrement: 41    &lt;br /&gt;
2013-11-16_12:46:21 CN.Test pulseTimePerDay: 41&lt;br /&gt;
2013-11-16_12:46:21 CN.Test pulseTimeOverall: 41&lt;br /&gt;
2013-11-16_12:46:21 CN.Test value: 0&lt;br /&gt;
2013-11-16_12:50:38 CN.Test countsPerDay: 2&lt;br /&gt;
2013-11-16_12:50:38 CN.Test countsOverall: 2&lt;br /&gt;
2013-11-16_12:50:38 CN.Test pauseTimeIncrement: 257&lt;br /&gt;
2013-11-16_12:50:38 CN.Test pauseTimePerDay: 756&lt;br /&gt;
2013-11-16_12:50:38 CN.Test pauseTimeOverall: 756&lt;br /&gt;
2013-11-16_12:50:38 CN.Test value: 1&lt;br /&gt;
2013-11-16_12:50:38 CN.Test 2&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Nun kann man den Chart definieren:&lt;br /&gt;
[[Datei:13_11_16_HourCounter_ChartBuild_01.png]]&lt;br /&gt;
&lt;br /&gt;
Für die Kurve &amp;quot;Brenner EIN&amp;quot; verwenden wir CN.Test.value. Damit diese als unterste Kurve dargestellt wird transformieren wir den Wert 1 auf -2 und alle anderen (also die 0) auf -21 mit folgender Funktion:&lt;br /&gt;
&amp;lt;pre&amp;gt;$fld[3]=~&amp;quot;1&amp;quot;?-2:-19&amp;lt;/pre&amp;gt;&lt;br /&gt;
Die &amp;quot;Brenner Starts&amp;quot; können wir direkt von countsPerDay ableiten.&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
Für die &amp;quot;Betriebsstunden&amp;quot; verwenden wir pulseTimePerDay. Da diese in Sekunden vorliegen teilen wir den Wert durch 3600, um Stunden zu erhalten.&lt;br /&gt;
&amp;lt;pre&amp;gt;$fld[3]/=3600&amp;lt;/pre&amp;gt;&lt;br /&gt;
Als letzten versorgen wir noch die Kurve &amp;quot;Dauer&amp;quot; mit pulseTimeIncrement. Da wir diese in Minuten haben wollen ist ebenfalls eine Umformung nötig.&lt;br /&gt;
&amp;lt;pre&amp;gt;$fld[3]/=60&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Somit sind die Basis-Kurven angelegt.&lt;br /&gt;
&lt;br /&gt;
=== Erweiterungen ===&lt;br /&gt;
Es wurden im Forum viele Wünsche formuliert, weitere Funktionalitäten für den HourCounter einzuführen.&lt;br /&gt;
&lt;br /&gt;
* Aggregation über bestimmte oder ganz freie Zeiträume&lt;br /&gt;
* komplexe Berechnungen, die zum Verbrauch führen&lt;br /&gt;
* Zuordnung von Verbräuchen zu unterschiedlichen Countern nach bestimmten Bedingungen&lt;br /&gt;
&lt;br /&gt;
Vor allem die Aggregation erfasster Werte in Stunden-, Tages-, Wochen- und Monatswerten ist eine sinnvolle&lt;br /&gt;
Erweiterung bei der Verbrauchserfassung.&lt;br /&gt;
&lt;br /&gt;
HourCounter bietet Schnittstellen an, die es ermöglichen, das Modul selbst mit neuen Eigenschaften zu erweitern.&lt;br /&gt;
&lt;br /&gt;
Die Referenz-Implementierung in 99_UtilsHourCounter.pm zeigt, wie dies skript-technisch zu realisieren ist.&lt;br /&gt;
&lt;br /&gt;
==== Installation ====&lt;br /&gt;
Die jeweils aktuelle Version von 99_UtilsHourCounter kann über diesen &lt;br /&gt;
[https://svn.fhem.de/trac/export/HEAD/trunk/fhem/contrib/99_UtilsHourCounter.pm Link] bezogen werden.&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
Die Datei ist in das Unterverzeichnis FHEM vom FHEM-Homverzeichnis zu kopieren.&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
z.B. bei Raspberry Pi: /opt/fhem/FHEM&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
Nach dem Kopieren und einem Neustart von FHEM kann man überprüfen, ob FHEM diese Datei findet.&lt;br /&gt;
Wenn man das Menü &amp;quot;Edit Files &amp;quot; anwählt, wird auch 99_UtilsHourCounter angezeigt.&lt;br /&gt;
&lt;br /&gt;
==== Readings ====&lt;br /&gt;
99_UtilsHourCounter aus dem contrib-Verzeichnis der FHEM-Installation erweitert den HourCounter um folgende Funktionen:&lt;br /&gt;
{| class=&amp;quot;wikitable sortable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Reading !! class=&amp;quot;unsortable&amp;quot; |  Beschreibung&lt;br /&gt;
|-&lt;br /&gt;
| appCountsPerHour || Stundenzähler, wird bei Stundenwechsel aktualisiert&lt;br /&gt;
|-&lt;br /&gt;
| appCountsPerHourTemp || Arbeitszähler zu appCountsPerHour&lt;br /&gt;
|-&lt;br /&gt;
| appCountsPerDay || Tageszähler, wird bei Tageswechsel aktualisiert (Arbeitszähler ist countsPerDay)&lt;br /&gt;
|-&lt;br /&gt;
| appCountsPerWeek || Wochenzähler, wird bei Wochenwechsel aktualisiert&lt;br /&gt;
|-&lt;br /&gt;
| appCountsPerWeekTemp || Arbeitszähler zu appCountsPerWeek&lt;br /&gt;
|-&lt;br /&gt;
| appCountsPerMonth || Monatszähler, wird bei Monatswechsel aktualisiert&lt;br /&gt;
|-&lt;br /&gt;
| appCountsPerMonthTemp || Arbeitszähler zu appCountsPerMonth&lt;br /&gt;
|-&lt;br /&gt;
| appCountsPerYear || Jahreszähler, wird bei Jahreswechsel aktualisiert&lt;br /&gt;
|-&lt;br /&gt;
| appCountsPerYearTemp || Arbeitszähler zu appCountsPerYear&lt;br /&gt;
|-&lt;br /&gt;
| appOpHoursPerDay || Betriebsstunden des Tages&lt;br /&gt;
|-&lt;br /&gt;
| appOpHoursPerDayTemp || Arbeitszähler zu appOpHoursPerDay&lt;br /&gt;
|-&lt;br /&gt;
| appOpHoursPerWeek || Betriebsstunden der Woche&lt;br /&gt;
|-&lt;br /&gt;
| appOpHoursPerWeekTemp || Arbeitszähler zu appOpHoursPerWeek&lt;br /&gt;
|-&lt;br /&gt;
| appOpHoursPerMonth || Betriebsstunden des Monats&lt;br /&gt;
|-&lt;br /&gt;
| appOpHoursPerMonthTemp || Arbeitszähler appOpHoursPerMonth&lt;br /&gt;
|-&lt;br /&gt;
| appOpHoursPerYear || Betriebsstunden des Jahres&lt;br /&gt;
|-&lt;br /&gt;
| appOpHoursPerYearTemp || Arbeitszähler appOpHoursPerYear&lt;br /&gt;
|-&lt;br /&gt;
| appUtilization || Auslastung = pulseTimePerDay /(vergangene Sekunden seit Tagesbeginn) * 100&lt;br /&gt;
|-&lt;br /&gt;
| appUtilizationTemp || Arbeitsvariable zu appUtilization&lt;br /&gt;
|}&lt;br /&gt;
Beginn der Woche ist jeweils der Sonntag.&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Mit folgender Anweisung aktivieren wir die Erweiterungen: &lt;br /&gt;
:&amp;lt;code&amp;gt;define CN.EVENT notify CN\..*:tick.* { appHCNotify(&amp;quot;$NAME&amp;quot;,&amp;quot;$EVTPART0&amp;quot;,&amp;quot;$EVTPART1&amp;quot;);;}&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Spätestens nach einer steigenden und einer fallenden Flanke sind die zuvor genannten app*-Readings zu sehen.&lt;br /&gt;
{{Randnotiz|RNTyp=Info|RNText=Die gezeigten define-Anweisungen müssen jeweils in einer Zeile stehen (keine Zeilenumbrüche!).}}&lt;br /&gt;
&lt;br /&gt;
Die neuen Readings werden automatisch  in den &amp;quot;Setter&amp;quot; der Web-Oberflächen aufgenommen. Dies gilt für alle Readings, die mit &amp;quot;app&amp;quot; beginnen.&lt;br /&gt;
&lt;br /&gt;
Somit können die neuen Readings beliebig manipuliert werden.&lt;br /&gt;
&lt;br /&gt;
===== Für Anfänger, die noch keine Erfahrungen mit Regular Expressions haben:=====&lt;br /&gt;
Benennt eure Hourcounter nach dem Muster CN.&amp;lt;euer Wunschname&amp;gt;. Dann wird der notify immer funktionieren.&lt;br /&gt;
&lt;br /&gt;
Beispiel: statt &amp;quot;PelletsCounter&amp;quot; wählt den Namen &amp;quot;CN.PelletsCounter&amp;quot;&lt;br /&gt;
&lt;br /&gt;
==== Archiv für Tages-/Wochen-/Monats-/Jahreswerte anlegen ====&lt;br /&gt;
Nun wollen wir die aggregierten Werte in eine eigene Datei speichern. Dies gelingt mit &lt;br /&gt;
:&amp;lt;code&amp;gt;define CN.Test.FileDay FileLog ./log/CN.Test-Day-%Y.log CN.Test:app\w*(Utilization|PerHour|PerDay|PerWeek|PerMonth|PerYear)(?!Temp).* &amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Im Klartext:&lt;br /&gt;
* verwende alle Werte des Counters CN.Test, deren Reading mit &amp;quot;app&amp;quot; beginnt&lt;br /&gt;
* und die einen der Terme appUtilization|PerHour|PerDay|PerWeek|PerMonth|PerYear beinhalten&lt;br /&gt;
* und die danach nicht dem Term &amp;quot;Temp&amp;quot; beinhalten&lt;br /&gt;
&lt;br /&gt;
== Fragen und Antworten ==&lt;br /&gt;
==== Betriebsstundenzähler über Leistungsmessung ableiten ====&lt;br /&gt;
&#039;&#039;&#039;Frage:&#039;&#039;&#039;&lt;br /&gt;
Ich würde gerne zählen, wenn ich mehr Strom als Standy verbrauche (also mehr als 2&amp;amp;nbsp;Watt)&lt;br /&gt;
und keine Betriebsstunden zählen, wenn der Verbrauch unter 2&amp;amp;nbsp;Watt ist. Ist das möglich?&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;Beispiel für die Events&amp;lt;/u&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
013-11-18_19:40:32 XXX power: 1.9&lt;br /&gt;
2013-11-18_19:40:32 XXX consumption: 2&lt;br /&gt;
2013-11-18_19:40:32 XXX consumptionTotal: 2&lt;br /&gt;
2013-11-18_19:40:36 XXX power: 27&lt;br /&gt;
2013-11-18_19:40:36 XXX consumption: 2&lt;br /&gt;
2013-11-18_19:40:36 XXX consumptionTotal: 2&lt;br /&gt;
2013-11-18_19:40:42 XXX power: 34.6&lt;br /&gt;
2013-11-18_19:40:42 XXX consumption: 2 &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Antwort&#039;&#039;&#039;&lt;br /&gt;
Die hier vorgestellte Lösung überprüft, ob der Wert des Events power eine oder zwei Ziffern vor dem Komma hat.&lt;br /&gt;
Deshalb wir hier erst gezählt, wenn die Schwelle von 10Watt überschritten wird. &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
define CN.Test HourCounter XXX:power:\s[0-9]{2,}(\.[0-9]{1,3})*$  XXX:power:\s[0-9]{1}(\.[0-9]{1,3})*$&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Erläuterung zu &amp;lt;regexp_for_ON&amp;gt; = XXX:power:\s[0-9]{2,}(\.[0-9]{1,3})*$&lt;br /&gt;
* &amp;quot;XXX&amp;quot;  bezeichnet das Device, der Term danach ist der regexp-Filte für das On-Ereignis&lt;br /&gt;
* &amp;quot;power:&amp;quot; das Ereignis muss mit diesem Term beginnen&lt;br /&gt;
* &amp;quot;\s&amp;quot; es muss ein Leerzeichen folgen&lt;br /&gt;
* &amp;quot;[0-9]{2,}&amp;quot; es müssen mindestens 2 Ziffern folgen&lt;br /&gt;
* &amp;quot;(\.[0-9]{1,3})*&amp;quot; wenn ein Punkt folgt, dann müssen auf diesen mindestens 1..3 Ziffern folgen&lt;br /&gt;
* &amp;quot;$&amp;quot; danach darf kein weiteres Zeichen mehr folgen&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Antwort Möglichkeit 2&#039;&#039;&#039;&lt;br /&gt;
In dieser Lösung bekommt das entsprechende Device, das mit HourCounter überwacht werden soll, ein [[userReadings]] &amp;quot;onoff&amp;quot;. Dieses Reading wird dann zum Schalten von Hour Counter verwendet:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
define GPIO4_DS18B20_Waermepumpe_Vorlauf GPIO4 28-000005956079&lt;br /&gt;
attr GPIO4_DS18B20_Waermepumpe_Vorlauf alias Wärmepumpe Vorlauf&lt;br /&gt;
attr GPIO4_DS18B20_Waermepumpe_Vorlauf model DS18B20&lt;br /&gt;
attr GPIO4_DS18B20_Waermepumpe_Vorlauf room GPIO4&lt;br /&gt;
attr GPIO4_DS18B20_Waermepumpe_Vorlauf userReadings onoff {(ReadingsVal(&amp;quot;GPIO4_DS18B20_Waermepumpe_Vorlauf&amp;quot;,&amp;quot;temperature&amp;quot;,0) &amp;gt;28)?1:0;;}&lt;br /&gt;
&lt;br /&gt;
define Waermepumpe_HourCounter HourCounter GPIO4_DS18B20_Waermepumpe_Vorlauf:onoff:.1  GPIO4_DS18B20_Waermepumpe_Vorlauf:onoff:.0&lt;br /&gt;
attr Waermepumpe_HourCounter room 2_Fussbodenheizung&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Erläuterungen zu dem Code: &amp;quot;{(ReadingsVal(&amp;quot;GPIO4_DS18B20_Waermepumpe_Vorlauf&amp;quot;,&amp;quot;temperature&amp;quot;,0) &amp;gt;28)?1:0;;}&amp;quot;&lt;br /&gt;
* &amp;quot;(ReadingsVal(&amp;quot;GPIO4_DS18B20_Waermepumpe_Vorlauf&amp;quot;,&amp;quot;temperature&amp;quot;,0) &amp;gt;28)&amp;quot; Diese Bedingung für das userReadings onoff prüft bei jedem Event, ob der Wert von temperature größer als 28 ist. &lt;br /&gt;
* &amp;quot;?1:0&amp;quot; Ist dies der Fall wird das userReading onoff auf 1 gesetzt andernfalls auf 0.&lt;br /&gt;
Auf Basis deses UserReadings wird dann der HourCounter definiert:&lt;br /&gt;
* &amp;quot;GPIO4_DS18B20_Waermepumpe_Vorlauf:onoff:.1&amp;quot; Einschaltbedingung für HourCounter&lt;br /&gt;
* &amp;quot;GPIO4_DS18B20_Waermepumpe_Vorlauf:onoff:.0&amp;quot; Abschaltbedingung für Hour Counter&lt;br /&gt;
&lt;br /&gt;
==== Welche Anwendungsfälle sind denkbar ? ====&lt;br /&gt;
{{Link2Forum|Topic=12216|Message=175163|LinkText=Aus dem Forum}}&lt;br /&gt;
* Betriebsstundenzähler für meine &amp;quot;Fliegenkiller-Steckdose&amp;quot;&lt;br /&gt;
* Nutzungsdauer beschränken für TV,Internet oder Spielkonsolen für entnervte Eltern&lt;br /&gt;
* Nutzungsdauer ermitteln zur Energieeinsparung (Klimageräte, Ventilatoren, Dunstabzugshauben etc.)&lt;br /&gt;
* Lüftungsverhalten ermitteln (wie lange Fenster pro Tag geöffnet)&lt;br /&gt;
&lt;br /&gt;
{{Link2Forum|Topic=12216|Message=195358|LinkText=Brenner Starts/Verbrauch + akkumulierte Werte}}&lt;br /&gt;
&lt;br /&gt;
{{Link2Forum|Topic=12216|Message=196358|LinkText=Ölverbrauch+Solar-Ladung}}&lt;br /&gt;
&lt;br /&gt;
[http://voizchat.de/gaszaehler-verbrauch-erfassen-mit-fhem-und-raspberry-gpio/ Gaszähler mit HourCounter realisieren] (URL tot)&lt;br /&gt;
&lt;br /&gt;
==== Seltene Schaltvorgänge ====&lt;br /&gt;
Die Schaltvorgänge sind über den Tag sehr wenige. Die Aktualisierung erfolgt immer erst bei der negativen Flanke. Wie kann man eine häufigere Aktualisierung erreichen ?&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Antwort&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Ab Version 1.0.0.6 ist wurde das Attribut &amp;quot;interval&amp;quot; eingeführt; es ist auf 60 Minuten voreingestellt und kann von 5..60 im 5 Minuten-Raster festgelegt werden.&lt;br /&gt;
Es bestimmt, nach welcher Zeit Puls-/Pausendauer aktualisiert werden sollen, unabhängig vom Auftreten einer Schaltflanke.&lt;br /&gt;
&lt;br /&gt;
==== Korrekte Darstellung der akkumulierten Daten im Chart ====&lt;br /&gt;
&#039;&#039;&#039;Frage&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;quot;appCountsPerDay: 4&amp;quot; bezieht sich auf die Counts des Tages 2014-06-16, trägt aber selbst den Zeitstempel 2014-06-17 und wird demnach in einem Chart auch über den Tag  &amp;quot; 2014-06-17&amp;quot; dargestellt.&lt;br /&gt;
Das Problem betrifft alle akkumulierten Daten des HourCounters.&lt;br /&gt;
Wie erreicht man im Chart die korrekte Darstellung ?&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Antwort&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Das Thema wurde {{Link2Forum|Topic=12216|Message=239929|LinkText=hier}} disktuiert.&lt;br /&gt;
Eine Lösung findet man mit [[LogProxy]]. Damit läßt sich ein negativer Offset für die X-Achse definieren, so dass die Daten wieder korrekt dargestellt werden.&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:MAX]]&lt;br /&gt;
[[Kategorie:HOWTOS]]&lt;br /&gt;
[[Kategorie:Heizungssteuerung]]&lt;/div&gt;</summary>
		<author><name>Ph1959de</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=UserReadings&amp;diff=40897</id>
		<title>UserReadings</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=UserReadings&amp;diff=40897"/>
		<updated>2026-03-31T16:39:33Z</updated>

		<summary type="html">&lt;p&gt;Ph1959de: kleine Korrekturen&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{SEITENTITEL:userReadings}}  &amp;lt;!-- da richtige Schreibweise kleinen Anfangsbuchstaben hat --&amp;gt;&lt;br /&gt;
Über das Attribut [[userReadings]] können bei einem Device benutzerdefinierte [[reading|readings]] einschließlich der Anweisungen zum Befüllen derselben festgelegt werden. Das können zum Einen Formatänderungen (&amp;quot;sprintf&amp;quot;), oder aber auch durch die &#039;&#039;Modifier&#039;&#039;&lt;br /&gt;
* difference&lt;br /&gt;
* differential&lt;br /&gt;
* integral&lt;br /&gt;
* offset&lt;br /&gt;
* monotonic&lt;br /&gt;
gesteuerte Berechnungen sein.&lt;br /&gt;
&lt;br /&gt;
== Syntax ==&lt;br /&gt;
{{Randnotiz|RNTyp=y|RNText=&#039;&#039;&#039;Geändertes Verhalten - bitte beachten&#039;&#039;&#039;&lt;br /&gt;
Im April 2016 hat sich die Verarbeitung des &#039;&#039;Triggers&#039;&#039; dahingehend geändert, dass die Trigger-Spezifikation jetzt als [[Regulärer Ausdruck]] interpretiert wird, damit also z.B. ein &amp;lt;code&amp;gt;avgTemp:temperature&amp;lt;/code&amp;gt; geändert werden muss in &amp;lt;code&amp;gt;avgTemp:temperature.*&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Technische Details dazu wurden in {{Link2Forum|Topic=52165|LinkText=diesem Forenthread}} diskutiert.}}&lt;br /&gt;
Siehe {{Link2CmdRef|Anker=readingFnAttributes}}.&lt;br /&gt;
&lt;br /&gt;
* Bei Eingabe im Editor-Feld müssen mehrere Befehle mit einem &amp;quot;;&amp;quot; getrennt werden, bei Änderung in der Eingabezeile sind zwei &amp;quot;;&amp;quot; notwendig.&lt;br /&gt;
* Die Variable, dessen Wert man im Reading haben möchte, kann man einfach ans Ende stellen[https://perldoc.perl.org/functions/return.html]&lt;br /&gt;
* mehrere UserReadings werden durch Komma getrennt.&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;code&amp;gt;myreading {my $v = ReadingsVal($name,&amp;quot;actuation&amp;quot;,&amp;quot;error&amp;quot;)+62; fhem(&amp;quot;set PID desired $v&amp;quot;); $v},&amp;lt;/code&amp;gt;&lt;br /&gt;
:&amp;lt;code&amp;gt;myreading2 {my $v = ReadingsVal($name,&amp;quot;actuation&amp;quot;,&amp;quot;error&amp;quot;)+62; fhem(&amp;quot;set PID2 desired $v&amp;quot;); $v}&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Beispiele ==&lt;br /&gt;
{{Randnotiz|RNTyp=g|RNText=Die nachfolgenden Beispiele sind ohne Trigger notiert. In der Regel sollte jedoch geprüft werden, ob nicht ein solcher Trigger gesetzt werden kann. Dies ist insbesondere in den Fällen sinnvoll, in denen in einem Gerät sehr viele Readings vorhanden sind, die ggf. zu unterschiedlichen Zeitpunkten aktualisiert werden. Das erste Beispiel wäre daher besser so zu schreiben:&amp;lt;br&amp;gt;&amp;lt;code&amp;gt;attr ElbePegelSchoena userReadings Pegel:value.* { ReadingsVal(&amp;quot;ElbePegelSchoena&amp;quot;,&amp;quot;value&amp;quot;,0) }&amp;lt;/code&amp;gt; }}&lt;br /&gt;
==== Ein Reading soll einen anderen Namen bekommen ====&lt;br /&gt;
Das vorhandene Reading &amp;quot;value&amp;quot; des Devices &amp;quot;ElbePegelSchoena&amp;quot; soll künftig in &amp;quot;Pegel&amp;quot; umbenannt werden. Das geht nicht. Man kann aber ein neues Reading &amp;quot;Pegel&amp;quot; mit genau gleichem Wert erzeugen: &lt;br /&gt;
:&amp;lt;code&amp;gt;attr ElbePegelSchoena userReadings Pegel { ReadingsVal(&amp;quot;ElbePegelSchoena&amp;quot;,&amp;quot;value&amp;quot;,0) }&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Ausgabe eines Homematic 3-State Fenstersensor als Zahl für Visualisierung mit Icons ====&lt;br /&gt;
:&amp;lt;code&amp;gt;&amp;lt;nowiki&amp;gt;attr HM_XXXXXX userReadings Statenum {if(ReadingsVal(&amp;quot;HM_XXXXXX&amp;quot;,&amp;quot;state&amp;quot;,&amp;quot;&amp;quot;) eq &amp;quot;closed&amp;quot;) {return 0} elsif (ReadingsVal(&amp;quot;HM_XXXXXX&amp;quot;,&amp;quot;state&amp;quot;,&amp;quot;&amp;quot;) eq &amp;quot;tilted&amp;quot;) {return 1} elsif (ReadingsVal(&amp;quot;HM_XXXXXX&amp;quot;,&amp;quot;state&amp;quot;,&amp;quot;&amp;quot;) eq &amp;quot;open&amp;quot;) {return 2} else {return -1}}&amp;lt;/nowiki&amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Ausgabe als Moving Average und Formatierung mit sprintf ====&lt;br /&gt;
:&amp;lt;code&amp;gt;attr HZ_EINSTRAHLUNG_RAW userReadings Einstr_Mean.av {sprintf(&amp;quot;%.1f&amp;quot;,movingAverage(&amp;quot;HZ_EINSTRAHLUNG_RAW&amp;quot;,&amp;quot;reading&amp;quot;,1200))}&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Umrechnung der Temperatur (durch 10 geteilt) und Einheit [°C] angehängt ====&lt;br /&gt;
:&amp;lt;code&amp;gt;attr HZ_EINSTRAHLUNG_T userReadings SolarTemp {ReadingsVal(&amp;quot;HZ_EINSTRAHLUNG_T&amp;quot;,&amp;quot;reading&amp;quot;,0)/10  .&amp;quot; °C&amp;quot;}&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Batterieüberwachung durch Erweiterung mit notify und userReading ====&lt;br /&gt;
... ist im Detail auf der Seite [[Batterieüberwachung]] beschrieben.&lt;br /&gt;
&lt;br /&gt;
==== Integralfunktion - integral ====&lt;br /&gt;
Die Verwendung der Integralfunktion bei &#039;&#039;userReadings&#039;&#039; ist ausführlich im Forenbeitrag {{Link2Forum|Topic=26300|Message=193084|LinkText=Integralfunktion bei UserReadings}} erklärt.&lt;br /&gt;
&lt;br /&gt;
==== Trigger ====&lt;br /&gt;
Die Trigger-Regex muss ein Event inklusive Wert matchen. Hier soll &#039;&#039;countTotalSum&#039;&#039; um den Wert im Reading &#039;&#039;count&#039;&#039; erhöht werden, natürlich nur, wenn ein Event für das Reading &#039;&#039;count&#039;&#039; eintrifft, damit der Wert nur einmal hinzu addiert wird. (Alle anderen Events werden ignoriert.) Hier ist auf das Wortende zu achten &amp;quot;&#039;&#039;&#039;\b&#039;&#039;&#039;&amp;quot; in der Regex, damit z.B. Events für &#039;&#039;count1&#039;&#039;, &#039;&#039;count2&#039;&#039;, ... keinen Trigger auslösen. (Hinweis: Beim Bilden der Summe kann die Benutzung von event-on-change-reading zu falschen Ergebnissen führen.) &lt;br /&gt;
:&amp;lt;code&amp;gt;attr GasPulseCounter userReadings countTotalSum:count\b.* {my $val=ReadingsVal($name,&amp;quot;countTotalSum&amp;quot;,0)+ReadingsVal($name,&amp;quot;count&amp;quot;,0);;$val}&amp;lt;/code&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Hier wird die Anzahl der Events für &#039;&#039;count&#039;&#039; gezählt. Events für Readingnamen, welche lediglich mit &#039;&#039;count&#039;&#039; beginnen, werden ignoriert wegen &amp;quot;\b&amp;quot;.&lt;br /&gt;
:&amp;lt;code&amp;gt;attr GasPulseCounter userReadings inc:count\b.* {my $val=ReadingsVal($name,&amp;quot;inc&amp;quot;,0)+1;;$val}&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Zusätzlich kann der Trigger auf einen bestimmten Wert beschränkt werden. Hier wird jedes Auftreten des Wertes &amp;quot;5&amp;quot; gezählt.&lt;br /&gt;
:&amp;lt;code&amp;gt;attr GasPulseCounter userReadings inc:count:.5 {my $val=ReadingsVal($name,&amp;quot;inc&amp;quot;,0)+1;;$val}&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Links ==&lt;br /&gt;
* {{Link2CmdRef|Lang=de|Anker=userReadings}} - userReadings&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Attribut (allgemeingültig)]]&lt;/div&gt;</summary>
		<author><name>Ph1959de</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=Benutzer_Diskussion:0bones&amp;diff=40851</id>
		<title>Benutzer Diskussion:0bones</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=Benutzer_Diskussion:0bones&amp;diff=40851"/>
		<updated>2026-03-21T09:02:33Z</updated>

		<summary type="html">&lt;p&gt;Ph1959de: Willkommen&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Willkommen! ==&lt;br /&gt;
{| width=&amp;quot;100%&amp;quot; cellspacing=&amp;quot;0&amp;quot; cellpadding=&amp;quot;6&amp;quot; style=&amp;quot;line-height: 20px; background: #E0E0E0; border: 2px solid #1874CD;&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| colspan=&amp;quot;4&amp;quot; style=&amp;quot;background:#1874CD;&amp;quot; |&amp;lt;big&amp;gt;&amp;lt;span style=&amp;quot;color: #FAFAFA&amp;quot;&amp;gt;&#039;&#039;&#039;Hallo 0bones,&#039;&#039;&#039; willkommen im FHEM Wiki!&amp;lt;/span&amp;gt;&amp;lt;/big&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| colspan=&amp;quot;4&amp;quot; | Danke für dein Interesse an unserem Projekt, ich freue mich schon auf deine weiteren Beiträge. Die folgenden Seiten sollten dir die ersten Schritte erleichtern, bitte nimm dir daher etwas Zeit, sie zu lesen.&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&#039;&#039;&#039;FHEM-spezifische Informationen&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| width=&amp;quot;8%&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| width=&amp;quot;38%&amp;quot; | &#039;&#039;&#039;[[Systemübersicht]]&#039;&#039;&#039;&amp;lt;br /&amp;gt;&#039;&#039;FHEM Systemübersicht&#039;&#039;&lt;br /&gt;
| width=&amp;quot;8%&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| width=&amp;quot;38%&amp;quot; | &#039;&#039;&#039;[[FHEMWiki:Über FHEMWiki]]&#039;&#039;&#039;&amp;lt;br /&amp;gt;&#039;&#039;Informationen über dieses Wiki&#039;&#039;&lt;br /&gt;
&amp;lt;!-- Abschnitt auf Kommentar gesetzt&lt;br /&gt;
|-&lt;br /&gt;
| colspan=&amp;quot;4&amp;quot; |&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
{{Todo|FHEM-spezifische Anleitungen und Regeln.}}&lt;br /&gt;
&lt;br /&gt;
---- &lt;br /&gt;
 Ende von &#039;Abschnitt auf Kommentar gesetzt&#039; --&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| colspan=&amp;quot;4&amp;quot; | &lt;br /&gt;
----&lt;br /&gt;
&#039;&#039;&#039;Generelle Informationen über (Media)Wikis&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| width=&amp;quot;8%&amp;quot; | [[Datei:Crystal Clear app kedit.svg|rechts|30px|link=Hilfe:Bearbeiten]]&lt;br /&gt;
| width=&amp;quot;38%&amp;quot; | &#039;&#039;&#039;[[Hilfe:Bearbeiten]]&#039;&#039;&#039;&amp;lt;br /&amp;gt;&#039;&#039;Zugang zu allen wichtigen Informationen.&#039;&#039;&lt;br /&gt;
| width=&amp;quot;8%&amp;quot; | [[Datei:X-office-presentation.svg|rechts|30px|link=Wikipedia:Tutorial]]&lt;br /&gt;
| width=&amp;quot;38%&amp;quot; | &amp;lt;!-- &#039;&#039;&#039;[[Wikipedia:Tutorial]]&#039;&#039;&#039;--&amp;gt;&#039;&#039;&#039;[http://de.wikipedia.org/wiki/Wikipedia:Tutorial Wikipedia:Tutorial]&#039;&#039;&#039;&amp;lt;br /&amp;gt;&#039;&#039;Schritt-für-Schritt-Anleitung für Einsteiger.&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Applications-system.svg|rechts|30px|link=Wikipedia:Grundprinzipien]]&lt;br /&gt;
| &#039;&#039;&#039;&amp;lt;!--[[Wikipedia:Grundprinzipien]]--&amp;gt;[http://de.wikipedia.org/wiki/Wikipedia:Grundprinzipien Wikipedia:Grundprinzipien]&#039;&#039;&#039;&amp;lt;br /&amp;gt;&#039;&#039;Die grundlegende Philosophie unseres Projekts.&#039;&#039;&lt;br /&gt;
| [[Datei:MentorenProgrammLogo-7.svg|rechts|60px|link=Wikipedia:Mentorenprogramm]]&lt;br /&gt;
| &#039;&#039;&#039;&amp;lt;!--[[Wikipedia:Mentorenprogramm]]--&amp;gt;[http://de.wikipedia.org/wiki/Wikipedia:Mentorenprogramm Wikipedia:Mentorenprogramm]&#039;&#039;&#039;&amp;lt;br /&amp;gt;&#039;&#039;Persönliche Einführung in die Beteiligung bei Wikipedia.&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| colspan=&amp;quot;4&amp;quot; |&lt;br /&gt;
----&lt;br /&gt;
Bitte beachte, &amp;lt;!--[[Wikipedia:Was Wikipedia nicht ist|was Wikipedia nicht ist]]--&amp;gt;[http://de.wikipedia.org/wiki/Wikipedia:Was_Wikipedia_nicht_ist was Wikipedia nicht ist], und &amp;quot;unterschreibe&amp;quot; deine Diskussionsbeiträge durch Eingabe von &amp;lt;code&amp;gt;--&amp;lt;nowiki&amp;gt;~~~~&amp;lt;/nowiki&amp;gt;&amp;lt;/code&amp;gt; oder durch Drücken der Schaltfläche [[Datei:button_sig.png|Signaturknopf|20px|link=Hilfe:Signatur]] über dem Bearbeitungsfeld. Artikel werden jedoch nicht unterschrieben, und wofür die Zusammenfassungszeile da ist, erfährst du unter &amp;lt;!--[[wikipedia:Hilfe:Zusammenfassung und Quellen|Hilfe:Zusammenfassung und Quellen]]--&amp;gt;[http://de.wikipedia.org/wiki/Hilfe:Zusammenfassung_und_Quellen Zusammenfassung und Quellen]. &amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
[[Datei:Nuvola apps ksirc.png|25px|link=Benutzer Diskussion:Ph1959de]] &amp;amp;nbsp;&amp;amp;nbsp; &#039;&#039;&#039;Hast du Fragen an mich?&#039;&#039;&#039; Schreib mir auf [[Benutzer Diskussion:Ph1959de|&amp;lt;u&amp;gt;meiner&amp;lt;/u&amp;gt; Diskussionsseite]]! Viele Grüße, [[Benutzer:Ph1959de|Peter]] ([[Benutzer Diskussion:Ph1959de|Diskussion]]) 10:02, 21. Mär. 2026 (CET)&lt;br /&gt;
|}&lt;/div&gt;</summary>
		<author><name>Ph1959de</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=Mi_vacuum&amp;diff=40813</id>
		<title>Mi vacuum</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=Mi_vacuum&amp;diff=40813"/>
		<updated>2026-02-26T14:41:58Z</updated>

		<summary type="html">&lt;p&gt;Ph1959de: Sichtung der letzten Änderungen; kleinere Korrekturen&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Todo|Aktualiserung / Text weniger flapsig formulieren / Einbindung Info-Box. Information, die generell für das Modul [[XiaomiDevice]] gilt, sollte auf die Modulseite &amp;quot;verschoben&amp;quot; werden.}}&lt;br /&gt;
&lt;br /&gt;
=Mi Vacuum=&lt;br /&gt;
Wie der preiswerte Staubsaugerroboter auch auf FHEM hört...&lt;br /&gt;
&lt;br /&gt;
==Für wen ist die Anleitung gedacht?==&lt;br /&gt;
Ich gehe davon aus, dass ihr ein funktionierendes FHEM auf einem Linux Rechner (Debian) betreibt.&lt;br /&gt;
&lt;br /&gt;
Weiterhin denke ich, dass euer Hauptarbeitsrechner unter Windows läuft.&lt;br /&gt;
&lt;br /&gt;
Als Smartphone OS Android (wer Apple hat, kann gerne die Token-Auslese-Geschichte hier beschreiben).&lt;br /&gt;
&lt;br /&gt;
Um das Teil auch von Alexa aus ein und aus zu schalten, sollte auch die Installation von Alexa in FHEM fertig sein.&lt;br /&gt;
&lt;br /&gt;
==Die Einrichtung des Moduls==&lt;br /&gt;
===Vorarbeiten (Linux)===&lt;br /&gt;
Zunächst fangen wir mal auf unserem FHEM Rechner an. Mit Putty verbinden wir uns per SSH mit dem FHEM Computer (Todo: Das ist hier ist für Windows-Nutzer...)&lt;br /&gt;
&lt;br /&gt;
Folgende Befehle können benötigt werden (Debian):&lt;br /&gt;
* &amp;lt;code&amp;gt;sudo apt-get install libjson-perl&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;sudo apt-get install libdigest-md5-perl&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;sudo apt-get install libcrypt-cbc-perl&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;sudo apt install libcryptx-perl&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;sudo apt-get install libcrypt-ecb-perl&amp;lt;/code&amp;gt; (nur nötig, wenn ihr einen verschlüsselten Token mit 96 Zeichen habt)&lt;br /&gt;
* &amp;lt;code&amp;gt;sudo cpan Crypt::Rijndael_PP&amp;lt;/code&amp;gt; (nur nötig, wenn libcryptx-perl aus irgendwelchen Gründen nicht funktioniert)&lt;br /&gt;
* &amp;lt;code&amp;gt;sudo cpan Crypt::Cipher::AES&amp;lt;/code&amp;gt; (nur nötig, wenn Crypt::Rijndael_PP aus irgendwelchen Gründen nicht funktioniert)&lt;br /&gt;
&lt;br /&gt;
Wenn bei dem ein oder anderen Modul die Meldung kommt, dass es schon installiert ist, einfach mit dem nächsten Befehl weiter machen. Das CPAN Teil würde ich am Schluss machen, das braucht ziemlich lang zum installieren.&lt;br /&gt;
&lt;br /&gt;
===Vorarbeiten am Smartphone (Token)===&lt;br /&gt;
Das Problem ist folgendes:&lt;br /&gt;
&lt;br /&gt;
Wenn der Roboter mit dem WLAN verbunden wird, generiert er einen Token. Das ist ein Schlüssel, ohne den er sich nicht steuern lässt. Damit FHEM drauf zugreifen kann, benötigt man diesen Token.&lt;br /&gt;
&lt;br /&gt;
Mit den neueren Versionen der MiHome App klappt das Auslesen der Tokens leider nicht mehr.&lt;br /&gt;
&lt;br /&gt;
====Auslesen bei Android Endgeräten====&lt;br /&gt;
&lt;br /&gt;
Ich gehe davon aus, dass der Staubsauger mit der original MiHome App eingebunden ist und funktioniert. (Ich habe bei der Einrichtung des Servers =&amp;gt; &amp;quot;other&amp;quot; gewählt. Offenbar wollen neuere Staubsauger sich mit &amp;quot;China Mainland&amp;quot; nicht mehr verbinden. Tut hier an dieser Stelle zwar nichts zur Sache, aber vielleicht hilft es dem ein oder anderen. Wenn man die Region gewechselt hat, muss man auch den Staubsauger neu einbinden)&lt;br /&gt;
&lt;br /&gt;
* MiHome löschen&lt;br /&gt;
* alte MiHome suchen (APKMirror z.B., Version 5.0.19 hat bei mir funktioniert, unsichere Quellen müssen erlaubt sein)&lt;br /&gt;
* runtergeladene MiHome App öffnen und anmelden (sollte ganz normal den Sauger finden)&lt;br /&gt;
* USB-Debugging am Handy einschalten (Entwickleroptionen...)&lt;br /&gt;
* Handy mit USB Kabel und PC verbinden&lt;br /&gt;
* [https://github.com/ultrara1n/MiToolkit/releases MiToolkit] 1.6 runterladen und öffnen&lt;br /&gt;
* [https://github.com/ultrara1n/MiToolkit/releases MiToolkit] erzeugt nun am Handy ein Backup&lt;br /&gt;
* anschließend zeigt es den Token direkt am PC an&lt;br /&gt;
&lt;br /&gt;
====Wenn die Datenbank miio2.db kein Token enthält====&lt;br /&gt;
[https://github.com/merdok/homebridge-xiaomi-fan/blob/master/obtain_token.md github] homebridge-xiaomi-fan bietet eine Methode für root-freie Android SmartPhones über das Logfile.&lt;br /&gt;
&lt;br /&gt;
Diese Methode funktioniert nur, wenn Sie die Mi Home App version v5.4.54 nutzen.&lt;br /&gt;
&lt;br /&gt;
* installieren Sie Mi Home App Version 5.4.54&lt;br /&gt;
* loggen Sie sich mit Ihrem Xiaomi Account ein&lt;br /&gt;
* nutzen Sie den Dateiexplorer und gehen nach &#039;&#039;&#039;/sdcard/SmartHome/logs/Plug_Devicemanager/&#039;&#039;&#039;&lt;br /&gt;
* finden Sie dort eine aktuelle Datei im Format yyyy-mm-dd.txt und öffnen Sie sie&lt;br /&gt;
* ODER nutzen Sie adb pull  /sdcard/SmartHome/logs/Plug_Devicemanager/, um alles auf den PC herunter zu laden&lt;br /&gt;
* Suchen Sie eine Zeichenkette ähnlich der folgenden mit dem Gerätenamen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JSON&amp;quot;&amp;gt;&lt;br /&gt;
{&amp;quot;did&amp;quot;:&amp;quot;117383849&amp;quot;,&amp;quot;token&amp;quot;:&amp;quot;90557f1373xxxxxxx8314a74d547b5&amp;quot;,&amp;quot;longitude&amp;quot;:&amp;quot;x&amp;quot;,&amp;quot;latitude&amp;quot;:&amp;quot;y&amp;quot;,&amp;quot;name&amp;quot;:&amp;quot;Mi Robot Vacuum&amp;quot;,&amp;quot;pid&amp;quot;:&amp;quot;0&amp;quot;,&amp;quot;localip&amp;quot;:&amp;quot;192.168.88.68&amp;quot;,&amp;quot;mac&amp;quot;:&amp;quot;40:31:3C:AA:BB:CC&amp;quot;,&amp;quot;ssid&amp;quot;:&amp;quot;Your AP Name&amp;quot;,&amp;quot;bssid&amp;quot;:&amp;quot;E4:8D:8C:EE:FF:GG&amp;quot;,&amp;quot;parent_id&amp;quot;:&amp;quot;&amp;quot;,&amp;quot;parent_model&amp;quot;:&amp;quot;&amp;quot;,&amp;quot;show_mode&amp;quot;:1,&amp;quot;model&amp;quot;:&amp;quot;rockrobo.vacuum.v1&amp;quot;,&amp;quot;adminFlag&amp;quot;:1,&amp;quot;shareFlag&amp;quot;:0,&amp;quot;permitLevel&amp;quot;:16,&amp;quot;isOnline&amp;quot;:true,&amp;quot;desc&amp;quot;:&amp;quot;Zoned cleanup&amp;quot;,&amp;quot;extra&amp;quot;:{&amp;quot;isSetPincode&amp;quot;:0,&amp;quot;fw_version&amp;quot;:&amp;quot;3.3.9_003460&amp;quot;,&amp;quot;needVerifyCode&amp;quot;:0,&amp;quot;isPasswordEncrypt&amp;quot;:0},&amp;quot;event&amp;quot;:{&amp;quot;event.back_to_dock&amp;quot;:&amp;quot;{\&amp;quot;timestamp\&amp;quot;:1548817566,\&amp;quot;value\&amp;quot;:[0]}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Dort finden Sie &#039;&#039;token&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
====Auslesen bei iOS Endgeräten====&lt;br /&gt;
Zum Auslesen des Tokens mit iOS Endgeräten kann die Anleitung unter folgendem Link befolgt werden: [https://forum.smartapfel.de/forum/thread/370-xiaomi-token-auslesen/]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Auslesen mit fhem / fhempy&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
In fhem gibt es auch noch das [https://github.com/fhempy/fhempy/blob/master/README.md fhempy-System], dafür gibt es auch ein Modul zum Auslesen von Xiaomi-Token aus deren Cloud, u.a. auch das Token für unseren Mi-Vacuum.&lt;br /&gt;
&lt;br /&gt;
===Installation des inoffiziellen Moduls===&lt;br /&gt;
Das Modul ist seit 28.02.2018 ein offizielles Modul. Eine separates Installation ist nicht (mehr) notwendig.&lt;br /&gt;
&lt;br /&gt;
==Einrichten des Moduls [[XiaomiDevice]] ==&lt;br /&gt;
:&amp;lt;code&amp;gt;define Mi_Vacuum XiaomiDevice 192.168.222.77 55387753545937326a33396943557999&amp;lt;/code&amp;gt;&lt;br /&gt;
:&amp;lt;code&amp;gt;attr Mi_Vacuum subType VacuumCleaner&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Und schon (=etwa nach 10 Sekunden) sollte euer Roboter in FHEM mit ganz vielen Readings auftauchen.&lt;br /&gt;
&lt;br /&gt;
==Anwendung: Sprachsteuerung mit Alexa (einfach, ein und aus)==&lt;br /&gt;
Wie bindet man das jetzt schnell in Alexa ein?&lt;br /&gt;
&lt;br /&gt;
Wir legen uns in FHEM einen Dummy an. Dieser wird ein Switch (on off). Dazu noch ein DOIF oder ein notify, welches diesen Switch überwacht. Je nach Status des Dummys schalten wir den Roboter.&lt;br /&gt;
&lt;br /&gt;
Ein List meines Dummys:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 Internals: &lt;br /&gt;
   CFGFN &lt;br /&gt;
   NAME       Mi_Vacuum_Staubsauger &lt;br /&gt;
   NR         719473 &lt;br /&gt;
   STATE      on &lt;br /&gt;
   TYPE       dummy &lt;br /&gt;
   Readings: &lt;br /&gt;
     2017-09-18 21:10:12   state           on &lt;br /&gt;
 Attributes: &lt;br /&gt;
   alexaName  Mi_Vacuum_Staubsauger &lt;br /&gt;
   genericDeviceType switch &lt;br /&gt;
   room       alexa &lt;br /&gt;
   setList    on off &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Mein DOIF:&lt;br /&gt;
:&amp;lt;code&amp;gt;define di_Mi_Vacuum DOIF ([Mi_Vacuum_Staubsauger:&amp;quot;on&amp;quot;]) (set Mi_Vacuum start) DOELSE (set Mi_Vacuum charge)&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das Attribut do always nicht vergessen!&lt;br /&gt;
&lt;br /&gt;
Den Dummy setzen wir in den Raum, der unsere Alexa Geräte beinhaltet. Nun den Alexa FHEM Service neu starten. Anschließend sollte eine Suche nach neuen Geräten in Alexa unseren Dummy finden. Für diesen legen wir in Alexa einen neuen Raum an, z.B. Staubsauger.&lt;br /&gt;
&lt;br /&gt;
Fertig.&lt;br /&gt;
&lt;br /&gt;
Ein &amp;quot;Alexa schaltet den Staubsauger ein&amp;quot; lässt unseren Mi-Vacuum loslegen. &amp;quot;Alexa schalte den Staubsauger aus&amp;quot; schickt ihn wieder zurück zu seiner Station.&lt;br /&gt;
&lt;br /&gt;
==Zonenreinigung==&lt;br /&gt;
Um nur Teilbereiche einer Wohnung zu reinigen, ist es möglich über das Modul Zonen zu definieren. Hierzu ist es notwendig, die Koordinaten der gewünschten Zone zu ermitteln. Hierzu eignet sich die [https://xiaomi.flole.de/ FloleVac App] die entweder auf einem Android Device oder auf einem Android Emulator (z.B. [https://www.bluestacks.com/download.html BlueStacks]) verwendet werden kann. In der App kann man in der Karte eine Zone für die Reinigung markieren und durch langes Gedrückthalten des &amp;quot;Reinigen-Buttons&amp;quot; in der FloleVac App die Koordinaten dieser Zone in die Zwischenablage kopieren. &lt;br /&gt;
&lt;br /&gt;
Mit diesen Koordindaten im Format [16200,27250,31650,27650,1] kann man dann loslegen die Zonen in FHEM zu definieren:&lt;br /&gt;
&lt;br /&gt;
Entweder direkt über die Werte:&lt;br /&gt;
:&amp;lt;code&amp;gt;set vacuum zone 16200,27250,31650,27650,1 23700,23050,25200,24200,2&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Oder über die passenden Attribute erst ein Alias anlegen:&lt;br /&gt;
:&amp;lt;code&amp;gt;attr vacuum zone_names home:[16200,27250,31650,27650,1],[23700,23050,25200,24200,2] livingroom:[16200,26250,23000,30150,1]&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ein Alias kann dann wie folgt genutzt werden:&lt;br /&gt;
:&amp;lt;code&amp;gt;set vacuum zone home&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Probleme und Lösungen==&lt;br /&gt;
===Crypt::Cipher::AES or Crypt::Rijndael_PP is required!===&lt;br /&gt;
Die Meldung im STATE des Devices bedeutet, dass noch einige Debian/Perl-Module installiert werden müssen. Siehe [[#Vorarbeiten (Linux)]].&lt;br /&gt;
&lt;br /&gt;
Danach ist darauf zu achten, dass das Device wieder aktiviert wird.&lt;br /&gt;
 attr &amp;lt;devicename&amp;gt; disable 0&lt;br /&gt;
&lt;br /&gt;
==Quellen==&lt;br /&gt;
* {{Link2Forum|Topic=73052|LinkText=Forums-Thread}}&lt;br /&gt;
* {{Link2Forum|Topic=76940|LinkText=Diskussionsthread}}&lt;br /&gt;
* [http://miniweb.sourceforge.net/ MiniWeb], kleiner WebServer für Windows&lt;br /&gt;
* [https://xiaomi.flole.de/ Flole], alternative App für Android, die das Token nach GoogleDrive exportiert&lt;br /&gt;
* [http://www.roboter-forum.com/forumdisplay.php?130-Xiaomi Roboter-Forum] deutschsprachiges Forum, welches sich mit dem Mi Vacuum beschäftigt (auch vor dem Kauf des Roboters lohnt sich ein Besuch dort)&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Other Components]]&lt;/div&gt;</summary>
		<author><name>Ph1959de</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=SolarForecast_-_Solare_Prognose_(PV_Erzeugung)_und_Verbrauchersteuerung&amp;diff=40792</id>
		<title>SolarForecast - Solare Prognose (PV Erzeugung) und Verbrauchersteuerung</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=SolarForecast_-_Solare_Prognose_(PV_Erzeugung)_und_Verbrauchersteuerung&amp;diff=40792"/>
		<updated>2026-02-15T09:44:45Z</updated>

		<summary type="html">&lt;p&gt;Ph1959de: Zahlen bis 12 üblicherweise als Worte (nur stellenweise geändert); bei Plural kein Apostroph (hier: APIs)&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Baustelle}}&lt;br /&gt;
{{Hinweis|Die vorliegende Wiki-Seite befindet sich im Aufbau.}}&lt;br /&gt;
{{Infobox Modul&lt;br /&gt;
|ModPurpose=Solarprognose und Verbrauchersteuerung&lt;br /&gt;
|ModType=d&lt;br /&gt;
|ModCmdRef=76_SolarForecast&lt;br /&gt;
|ModFTopic=117864&lt;br /&gt;
|ModForumArea=Solaranlagen&lt;br /&gt;
|ModTechName=76_SolarForecast.pm&lt;br /&gt;
|ModOwner=DS_Starter ({{Link2FU|16933|Forum}}/[[Benutzer Diskussion:DS_Starter|Wiki]])&lt;br /&gt;
}}&lt;br /&gt;
[[SolarForecast - Solare Prognose (PV Erzeugung) und Verbrauchersteuerung|SolarForecast]] ist ein integratives Modul zur Gewinnung solarer Vorhersagedaten, deren Verarbeitung und grafischen Darstellung. Desweiteren bietet es die Möglichkeit, in FHEM definierte Verbraucher in einem SolarForecast-Device zu registrieren und eine PV Prognose basierte Steuerung der Verbraucher vom Modul übernehmen zu lassen.&lt;br /&gt;
&lt;br /&gt;
Das Modul ist insbesondere durch folgende Eigenschaften gekennzeichnet:&lt;br /&gt;
[[Datei:sfc1.png|right|thumb|300px|grafische Ansicht SolarForecast]]&lt;br /&gt;
&lt;br /&gt;
::* zur Gewinnung solarer Prognosewerte können die SolCast API, OpenMeteo API, Forecast.Solar API, Victron VRM Portal API oder alternativ DWD Stahlungswerte-Stationen verwendet werden&lt;br /&gt;
&lt;br /&gt;
::* optionale KI Unterstützung der PV-Prognose bei Verwendung von geeigneten Solar-APIs&lt;br /&gt;
&lt;br /&gt;
::* optionale KI Unterstützung der Prognose des Energieverbrauchs der kommenden Stunden&lt;br /&gt;
&lt;br /&gt;
::* es wird sowohl die Erzeugungsprognose als auch eine Verbrauchsprognose sowie SoC Prognose (bei Batterie-Integration) erstellt&lt;br /&gt;
&lt;br /&gt;
::* Wetterdaten werden über DWD Wetter-Stationen oder API integriert. Es können verschiedene APIs zur Gewinnung von Solar- und Wetterdaten kombiniert werden. &lt;br /&gt;
&lt;br /&gt;
::* Sprachensupport EN / DE&lt;br /&gt;
&lt;br /&gt;
::* die Prognosedaten, Wetterdaten, Verbraucherplanungen und die aktuellen Energieflüsse werden in umfangreich anpassbaren integrierten Grafiken dargestellt&lt;br /&gt;
&lt;br /&gt;
::* es wird keine externe SQL-Datenbank benötigt, die Datenhaltung erfolgt in einer Memory basierten Cachedatenbank inkl. einer Filesystempersistenz zur Datensicherung und Wiederherstellung beim FHEM-Restart&lt;br /&gt;
&lt;br /&gt;
::* die Integration von Geräten wie Wechselrichter, Energy Meter, Batterien, Wetterstationen oder Verbrauchern ist offen und universell gestaltet und bietet dem Anwender maximale Freiheiten bei der Einrichtung seines individuellen Solardatensystems.&lt;br /&gt;
&lt;br /&gt;
::* eine Integration von bis zu 4 Inverter und Smartloader (DC-DC Batterieladegeräte) ist möglich&lt;br /&gt;
&lt;br /&gt;
::* eine Integration von bis zu 3 nicht PV-Energieerzeugern wie BHKW, Windrädern oder Notstromaggregaten ist möglich&lt;br /&gt;
&lt;br /&gt;
::* eine integrierte und umfangreich anpassbare Verbrauchersteuerung vereint die PV Prognose basierte Einplanung der Verbraucher mit der Möglichkeit die Verbraucher durch das Modul schalten zu lassen und dadurch auf PV Erzeugungsschwankungen automatisch dynamisch zu reagieren&lt;br /&gt;
&lt;br /&gt;
::* Einbindung von bis zu 3 Batteriespeichersystemen&lt;br /&gt;
&lt;br /&gt;
::* Bereitstellung von Leitwerten zur optimalen Einstellung/Steuerung von Batteriespeichersystemen&lt;br /&gt;
&lt;br /&gt;
::* trotz der hohen Komplexität wird dem Anwender durch eine &amp;quot;Guided Procedure&amp;quot; bei der Gerätedefinition der Einstieg erleichtert und damit ein optimales Benutzererlebnis geboten&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Abgrenzung:&#039;&#039;&#039; Das Modul 76_SolarForecast ist nicht zu verwechseln mit der SQL-basierten (DbLog) Prognose-Lösung mit Kostal Plenticore Wechselrichtern, die auf der eigenen Seite &#039;&#039;&#039;[[Kostal_Plenticore_10_Plus]]&#039;&#039;&#039; behandelt wird. Diese Lösung beschreibt kein monolithisches Modul, sondern basiert auf einer orchestrierbaren Zusammenstellung individueller Perl Programmbausteine. &lt;br /&gt;
&lt;br /&gt;
Im vorliegenden Wiki-Beitrag wird ausschließlich das Modul 76_SolarForecast behandelt.&lt;br /&gt;
&lt;br /&gt;
== Rahmenbedingungen und Voraussetzungen ==&lt;br /&gt;
&lt;br /&gt;
Das Wiki gibt allgemeingültige und teilweise sehr spezifische bzw. tiefgehende Informationen und Handlungsempfehlungen zum SolarForecast Modul. Die dargestellten Informationen beziehen sich auf den zum Zeitpunkt der Wiki-Erstellung vorhandenen Entwicklungsstand des Moduls. Durch die stetige Weiterentwicklung können sich Abweichungen zum beschriebenen Inhalt ergeben oder zur Obsoleszenz dieser Inhalte führen. &lt;br /&gt;
&lt;br /&gt;
Vergleiche deshalb die Informationen immer mit der aktuellen Befehls-Referenz zum Modul!&lt;br /&gt;
&lt;br /&gt;
== Definition ==&lt;br /&gt;
Ein SolarForecast Device wird mit dem Befehl&lt;br /&gt;
&lt;br /&gt;
 define &amp;lt;Name&amp;gt; SolarForecast &lt;br /&gt;
&lt;br /&gt;
angelegt. Nach der Definition wird der User durch einen Dialog geführt, der einige unerlässliche Einstellungen abfragt und in den entsprechenden Attributen und Readings persistiert. Alle Attribute und Readings die eine Einstellung der Anlage bewirken, sind mit dem Präfix &amp;quot;setup&amp;quot; versehen. &lt;br /&gt;
&lt;br /&gt;
Die Verwendung von setup-Readings gestattet es die Anlagenparameter dynamisch im Betrieb zu verändern. So kann mit &lt;br /&gt;
&lt;br /&gt;
 set &amp;lt;Name&amp;gt; setupStringAzimuth   bzw.&lt;br /&gt;
 set &amp;lt;Name&amp;gt; setupStringDeclination &lt;br /&gt;
&lt;br /&gt;
die Ausrichtung und der Neigungswinkel der PV-Module ständig angepasst werden, was zum Beispiel bei einer dem Sonnenstand nachgeführten Anlage von Bedeutung ist.&lt;br /&gt;
&lt;br /&gt;
Die abgefragten Einstellungen während des Dialoges sind ausführlich in den entsprechenden Attributen bzw. Set-Kommandos erläutert. Die benötigten Informationen sind anhängig von der gewählten Prognose-API und können im Umfang variieren.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Screenshot 2024-09-21 213257.png|right|thumb|400px|Anlagenprüfung über Drucktaste in der Kopfgrafik]]&lt;br /&gt;
&lt;br /&gt;
Sobald alle verlangten Einstellungen vorgenommen sind, sollte eine Anlagenprüfung mit &lt;br /&gt;
&lt;br /&gt;
 set &amp;lt;Name&amp;gt; plantConfiguration check&lt;br /&gt;
&lt;br /&gt;
oder über die angebotene Drucktaste in der Grafik durchgeführt werden. Es wird eine Plausibilitätsprüfung vorgenommen und das Ergebnis sowie eventuelle Hinweise bzw. Fehler ausgegeben.&lt;br /&gt;
Die Prüfung kann beliebig wiederholt werden und sollte sich bei jeder Einstellungsänderung der Anlage durchgeführt werden. Dadurch wird die Wahrscheinlichkeit von fehlerhaften Einstellungen minimiert.&lt;br /&gt;
&lt;br /&gt;
Neben den grundlegenden Einstellungen können im Device weitere sekundäre Anlagenkomponenten wie Batteriesystem(e) (Attribut setupBatteryDev1 - setupBatteryDev3), weitere Wetter-Devices (Attribut setupWeatherDev1 - setupWeatherDev3) oder sonstige Erzeuger (z.B. BHKW, Winderzeugung, Notstromaggregat) integriert werden (Attribut setupOtherProducer01 - setupOtherProducer03).&lt;br /&gt;
&lt;br /&gt;
Die verlangten Parameter der Anlage werden in der Online-Hilfe umfassend beschrieben und deren Funktion erschließt sich zum größten Teil selbst. Dennoch soll nachfolgend auf einige Punkte im Zusammenhang der Einrichtung eingegangen werden.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
=== Hinweise zur Struktur der Attribute ===&lt;br /&gt;
&lt;br /&gt;
Durch die umfangreichen Funktionen des Moduls würde bei Nutzung der im FHEM herkömmlich vorhandenen Attributstruktur die Anzahl der benötigten Attribute extrem stark steigen und nicht sinnvoll genutzt werden können. Deshalb gibt es neben den &#039;&#039;gewöhnlichen&#039;&#039; Attributen mit einfachen Werteauswahlmöglichkeiten oder Eingaben (z.B. ctrlDebug, ctrlSpecialReadings) sogenannte &#039;&#039;zusammengesetzte&#039;&#039; Attribute, deren Inhalt durch &#039;&#039;&#039;Schlüssel=Wert Paare&#039;&#039;&#039; gebildet wird und dadurch deutlich mehr Funktionen und Informationen transportieren als ein gewöhnliches Attribut.&lt;br /&gt;
&lt;br /&gt;
Für zusammengesetzte Attribute besteht die zusätzliche Möglichkeit, gewünschte Änderungen der Schlüssel=Wert Paare selektiv vornehmen zu können, ohne das gesamte Attribut aufrufen und ändern zu müssen. Die Vorgang, der vor allem für programmtechnische Manipulationen gedacht ist, kann durch eine Set-Funktion ausgeführt werden:&lt;br /&gt;
&lt;br /&gt;
 set &amp;lt;Name&amp;gt; attrKeyVal &amp;lt;Attribut&amp;gt; [&amp;lt;Gerät&amp;gt;] &amp;lt;Schlüssel=Wert&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Beispiele für zusammengesetzte Attribute sind:&lt;br /&gt;
&lt;br /&gt;
* aiControl&lt;br /&gt;
* plantControl&lt;br /&gt;
* setup* Attribute&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Die Funktionen der Attribute werden durch einen Präfix geclustert und so inhaltlich, aber vor allem auch in der Attribute Drop-Down Liste, strukturiert dargestellt.&lt;br /&gt;
&lt;br /&gt;
Die Bedeutung der &#039;&#039;&#039;Präfixe&#039;&#039;&#039; sind:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;ai&#039;&#039;&#039; - Steuerungsfunktionen für KI (z.B. aiControl)&lt;br /&gt;
* &#039;&#039;&#039;consumer&#039;&#039;&#039; - Registrierung und Einstellungen für Verbraucher (z.B. consumer02, consumerControl)&lt;br /&gt;
* &#039;&#039;&#039;ctrl&#039;&#039;&#039; - Steuerungsfunktion allgemeiner Art (z.B. ctrlDebug, ctrlSpecialReadings, ctrlUserExitFn)&lt;br /&gt;
* &#039;&#039;&#039;flowGraphic&#039;&#039;&#039; - Einstellungen für die Energieflußgrafik (z.B. flowGraphicControl)&lt;br /&gt;
* &#039;&#039;&#039;graphic&#039;&#039;&#039; - Einstellung für die Grafik allgemein (z.B. graphicSelect, graphicControl)&lt;br /&gt;
* &#039;&#039;&#039;graphicBeam&#039;&#039;&#039; -  Einstellungen bestimmte Balkeneigenschaften in der Balkengrafik (z.B. graphicBeamHeightLevel1)&lt;br /&gt;
* &#039;&#039;&#039;plant&#039;&#039;&#039; - übergreifende Einstellungen betreffend der gesamten PV-Anlage bzw. des SolarForecast-Devices (z.B. plantControl)&lt;br /&gt;
* &#039;&#039;&#039;setup&#039;&#039;&#039; - Registrierung von Geräten in der Gesamtanlage und Einstellung deren Eigenschaften (Inverter, Zähler, Batterien usw.)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Einrichtung der Wechselrichter mit setupInverterDevXX ===&lt;br /&gt;
&lt;br /&gt;
Mit den Attributen setupInverterDev01 bis setupInverterDevXX werden die vorhandenen Wechselrichter der Anlage registriert.&lt;br /&gt;
Das Modul unterscheidet verschiedene Wechselrichter-Typen, die sich durch ihre Arbeitsweise innerhalb der Anlage unterscheiden. Die Schlüssel=Wert Paare in den Attributen können mehrzeilig organisiert werden. Das erleichtert die Übersicht wenn viele dieser Parameter eingegeben werden.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
;PV-Wechselrichter: Es ist der Standard Wechselrichter in einer PV-Anlage. An ihn sind die Solarzellen bzw. Strings angeschlossen. Die zugeordneten Strings können mit dem Schlüssel &#039;&#039;&#039;strings&#039;&#039;&#039; zugeordnet werden. Ohne diese Angabe werden diesem Gerät alle definierten Strings (siehe Attribut &#039;&#039;setupInverterStrings&#039;&#039;) zugeordnet. Der Wechselrichter liefert seine Energie in das Hausnetz. Alternativ kann mit der Angabe &#039;&#039;&#039;feed=grid&#039;&#039;&#039; die Funktion des Wechselrichters geändert werden. Diese Angabe sagt aus, dass dieser Wechselrichter ausschließlich in das öffentliche Netz einspeist.&lt;br /&gt;
&lt;br /&gt;
Hier ein Beispiel-Setup eines PV-Wechselrichters (SMA STP):&lt;br /&gt;
&lt;br /&gt;
 STP_5000 pvIn=string_1_pdc:kW pvOut=total_pac:kW etotal=etotal:kWh capacity=5000 strings=Süddach asynchron=1 limit=100&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
;Solar-Ladegeräte: Solar-Ladegeräte sind DC-DC Wandler und liefern die Energie der angeschlossenen Solarzellen nicht in das Wechselstromnetz, sondern laden direkt eine Batterie bzw. versorgen einen Batteriewechselrichter auf der Gleichstromseite. Die Funktion als Solar-Ladegerät wird mit &#039;&#039;&#039;feed=bat&#039;&#039;&#039; aktiviert, die relevanten Strings können ebenfalls mit dem Schlüssel &#039;&#039;&#039;strings&#039;&#039;&#039; zugeordnet werden. Ein Beispiel eines Solar-Ladegerätes ist ein Victron SmartSolar MPPT. &lt;br /&gt;
&lt;br /&gt;
Ein Beispiel für das Setup eines Solar-Ladegeräts (SmartSolar Charger MPPT):&lt;br /&gt;
&lt;br /&gt;
 MQTT2_cerboGX_c0619ab34e08_solarcharger_Common pv=Yield_Power_value:W etotal=Yield_System_value:kWh capacity=2080 strings=Schleppdach feed=bat synchron=0 limit=100&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
;Batterie-Wechselrichter: Dieses Gerät hat keine angeschlossenen Solarzellen und arbeitet als DC-AC bzw. AC-DC Wandler zwischen einer Batterie (=Gleichstromquelle) und dem Wechselstrom-Hausnetz. Die Funktion als Batterie-Wechselrichter wird mit der Angabe &#039;&#039;&#039;strings=none&#039;&#039;&#039; aktiviert. Die Schlüssel &#039;&#039;etotal&#039;&#039; und &#039;&#039;pv&#039;&#039; können bei einem Batterie-Wechselrichter ohne zugeordnete Solarzellen nicht gesetzt werden. Ein Beispiel eines Batterie-Wechselrichter ist der Victron MultiPlus II.&lt;br /&gt;
&lt;br /&gt;
Ein Setup-Beispiel für einen Batterie-Wechselrichter (Victron MultiPlus II):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
dc2ac=DC_IN:W&lt;br /&gt;
ac2dc=DC_OUT:W&lt;br /&gt;
capacity=7200&lt;br /&gt;
strings=none&lt;br /&gt;
icon=inverter@darkorange:inverter@grey&lt;br /&gt;
asynchron=0&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Die Eigenschaften des Wechselrichters werden durch eine Reihe von &amp;lt;Schlüssel=Wert&amp;gt; Paaren festgelegt. Auf einige dieser Paramter soll hier eingegangen werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
==== Ausgewählte Schlüssel=Wert Paare ====&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;pvIn=&amp;lt;Readingname&amp;gt;:&amp;lt;Einheit&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
	&lt;br /&gt;
Dem Schlüssel &#039;&#039;pvIn&#039;&#039; wird ein Reading und dessen Einheit (W,kW) des Wechselrichter-Device zugeordnet, welches die aktuell zugeführte PV-Leistung bereitstellt. Der Schlüssel ist optional und dient in erster Linie dazu den entsprechenden Input-Wert in der Flußgrafik anzuzeigen sofern man das Attribut flowGraphicControl-&amp;gt;showGenerators=1 setzt. Dadurch ist auch der Eigenverbrauch des Inverters bzw. dessen Wandlungsverlust visuell abschätzbar.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;pvOut=&amp;lt;Readingname&amp;gt;:&amp;lt;Einheit&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
	&lt;br /&gt;
Dem Schlüssel &#039;&#039;pvOut&#039;&#039; wird ein Reading und dessen Einheit (W,kW) des Wechselrichter-Device zugeordnet, welches die aktuell erzeugte Leistung als positiven Wert liefert. Für einen PV-Wechselrichter (DC-&amp;gt;AC) oder Solar-Ladegerät (DC-&amp;gt;DC) ist es die erzeugte PV-Leistung.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;ac2dc=&amp;lt;Readingname&amp;gt;:&amp;lt;Einheit&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Dieser Schlüssel ist nur für einen Batterie-Wechselrichter setzbar. Das Reading liefert die aktuelle Leistung, die der Wechselrichter vom Hausnetz in Richtung Batterie (AC-&amp;gt;DC) wandelt. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;dc2ac=&amp;lt;Readingname&amp;gt;:&amp;lt;Einheit&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Dieser Schlüssel ist nur für einen Batterie-Wechselrichter setzbar. Das Reading liefert die aktuelle Leistung, die der Wechselrichter aus einer Gleichstromquelle, d.h. einer Batterie oder einem Solar-Ladegerät, in Richtung des Hausnetzes (AC) wandelt . &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;capacity=&amp;lt;max. WR-Leistung&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
In dem Schlüssel &#039;&#039;capacity&#039;&#039; wird die maximal mögliche Leistung in Watt (ohne Einheit) angegeben, die der Wechselrichter lt. seinem Datenblatt in der Lage ist zu leisten. &lt;br /&gt;
&lt;br /&gt;
Die Angabe &#039;&#039;capacity&#039;&#039; hat in erster Linie die Aufgabe, die max. mögliche PV-Leistung für die Prognose zu begrenzen. Oft sind die installierten Peak-Leistungen der PV-Module höher als die angeschlossene Wechselrichter-Leistung bzw. kommt es vor, dass die KI / (korrigierte) API-Prognose über der maximal möglichen Wechselrichter-Leistung liegt. In diesen Fällen wird die Prognose auf die angegebene &#039;&#039;capacity&#039;&#039; begrenzt.&lt;br /&gt;
&lt;br /&gt;
Der Wert in diesem Schlüssel kann, wie andere Schlüssel auch, dynamisch zur Laufzeit mit dem Set-Befehl &amp;quot;[[#Hinweise zur Struktur der Attribute|set &amp;lt;Name&amp;gt; attrKeyVal ...]]&amp;quot; geändert werden. Ein Beispiel zur Anwendung ergibt sich aus folgendem Szenario.&lt;br /&gt;
&lt;br /&gt;
Gegeben sei eine Anlage die aus Solarzellen (1300 W Peak) mit einem Solar-Ladegerät mit max. 1800 W Leistung, einer Batterie und einem Batterie-Wechselrichter besteht. Die Anlage versorgt nur das Hausnetz. Eine Netzeinspeisung ist nicht vorgesehen oder nicht erlaubt.  &lt;br /&gt;
&lt;br /&gt;
Nehmen wir an, es ist ein Tag voller Sonnenschein prognostiziert und die Batterie ist zunächst entladen. Der Haushalt verbraucht 300 W. Dann würden die PV-Module Module ihre volle Leistung von 1300 W Peak leisten können, die sich auch in der Balken-Vorhersage niederschlägt. Die Leistung wird nicht begrenzt, weil:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* sie niedriger ist als die nominale und angegebene Wechselrichter Kapazität von &#039;&#039;capacity=1800&#039;&#039;&lt;br /&gt;
* die erzeugte Energie anteilig für die Verorgung des Hauses und die Aufladung der Batterie verwendet wird&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Anders verhält es sich wenn nach einer gewissen Zeit die Batterie voll ist und weiterhin volle PV Leistung zur Verfügung steht. In diesem Fall würde der Haushalt seine benötigte Leistung von 300 W abnehmen, aber das Solar-Ladegerät kann keine weitere Leistung mehr abgeben, da die Batterie bereits voll geladen ist, d.h. die Anlagensteuerung würde die PV-Anlage abregeln. In diesem Fall, wenn es einen längeren Zeitraum betrifft, muß auch die Prognose PV-Energie auf 300 Wh (bzw. den durchschnittlichen Energieverbrauch des Hauses pro Stunde) begrenzt werden, damit die PV-Prognose nach unten angepasst wird und so eine realitätsnahere Prognose ermöglicht wird. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;icon=&amp;lt;aktiv&amp;gt;[@&amp;lt;Farbe&amp;gt;][:&amp;lt;inaktiv&amp;gt;[@&amp;lt;Farbe&amp;gt;]]&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Jeder Wechselrichter wird im Standard durch ein Icon getrennt nach Tag und Nacht, d.h. je nach Aktiv-Status und Inaktiv-Status, symbolisiert.&lt;br /&gt;
&lt;br /&gt;
;Tag bzw. Aktivität: Für Wechselrichter mit angeschlossenen PV-Modulen wird ein entsprechend gefärbtes Sonnensymbol im Standard verwendet. Batterie-Wechselrichter verwenden ein gefärbtes Invertersymbol.&lt;br /&gt;
&lt;br /&gt;
;Nacht bzw. Inaktivität: Für Wechselrichter mit angeschlossenen PV-Modulen wird ein entsprechend gefärbtes Symbol der aktuellen Mondphasen im Standard verwendet. Batterie-Wechselrichter verwenden ein ausgegrautes Invertersymbol.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Wie angegeben, kann man für Tag und Nacht jeweils ein Wunsch-Icon, wenn gewünscht mit einer alternativen Färbung, auswählen.&lt;br /&gt;
&lt;br /&gt;
 icon=inverter@darkorange:inverter@gray&lt;br /&gt;
&lt;br /&gt;
Soll nur das Icon für die Nacht bzw. Inaktivät geändert werden, kann dies in dieser Form angegeben werden:&lt;br /&gt;
&lt;br /&gt;
 icon=:inverter@gray  &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
(beachte den führenden &#039;:&#039;)&lt;br /&gt;
&lt;br /&gt;
 &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
==== Definition von Hybrid-Wechselrichtern  ====&lt;br /&gt;
&lt;br /&gt;
Zur Zeit der Erstellung dieses Abschnittes ist es in der SolarForecast Version 1.58.x nicht möglich, einen Hybridwechselrichter nativ zu definieren. Als Workaround wird eine passende Kombination aus PV-Wechselrichter und Batterie-Device oder einer Kombination aus PV-Wechselrichter + Batterie-Wechselrichter und Batterie-Device erstellt.&lt;br /&gt;
&lt;br /&gt;
Bei allen benutzten Methoden ist es sehr wichtig!, dass die geforderten Inhalte der jeweilige Attributschlüssel beachtet und eingehalten werden. So ist zum Beispiel die Angabe von:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;pvOut&#039;&#039;&#039; - Ein Reading, welches die aktuelle Leistung aus PV-Erzeugung, die an das Hausnetz oder öffentliche Netz geliefert wird, bereitstellt. Es wird ein positiver numerischer Wert erwartet.  &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
bei einem PV-Wechselrichter ein Reading welches ausschließlich die Leistung liefert, die von den Solarzellen-Generatoren erzeugt wird. Elemente von Batterieleistungen oder Gridbestandteile dürfen hier nicht enthalten sein. Sind in den Gerätemodulen diese Readings in dieser Form nicht enthalten, bietet es sich an, userReadings zur Erstellung von zusätzlichen Readings in den Quellendevices zu nutzen um den nötigen Input für SolarForecast bereitzustellen. &lt;br /&gt;
&lt;br /&gt;
So wird zum Beispiel bei einem 	Batterie-Wechselrichter gefordert, dass die Readings &#039;&#039;&#039;ac2dc&#039;&#039;&#039; (AC-&amp;gt;DC-Leistung Hausnetz zur Batterie) und &#039;&#039;&#039;dc2ac&#039;&#039;&#039; (DC-&amp;gt;AC-Leistung (Batterie zum Hausnetz) &#039;&#039;&#039;jeweils als als positiver Wert&#039;&#039;&#039; anzugeben ist. Manche Batterie-Devices stellen allerdings nur ein Reading zur Verfügung, welches vorzeichenbehaftet die Leistungen in die Batterie bzw. aus der Batterie heraus liefert. Mit einem userReadings Attribut kann aus diesem Reading zwei neue Readings erzeugt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
attr &amp;lt;Name&amp;gt; userReadings  BatIn:DC_Power_value.* {&lt;br /&gt;
                          my $pwr = ReadingsVal ($name, &#039;DC_Power_value&#039;, 0);&lt;br /&gt;
                          $pwr    = $pwr &amp;gt; 0 ? $pwr : 0;&lt;br /&gt;
                          $pwr&lt;br /&gt;
                          },&lt;br /&gt;
                          BatOut:DC_Power_value.* {&lt;br /&gt;
                          my $pwr = ReadingsVal ($name, &#039;DC_Power_value&#039;, 0);&lt;br /&gt;
                          $pwr    = $pwr &amp;lt; 0 ? - $pwr : 0;&lt;br /&gt;
                          $pwr&lt;br /&gt;
                          },&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ist das Quellenreading &#039;&#039;DC_Power_value&#039;&#039; positiv, stellt der Wert AC-&amp;gt;DC-Leistung dar und wird als neues Reading BatIn bereitgestellt. Mit einem negativen Vorzeichen ist es eine DC-&amp;gt;AC-Leistung und wird in dem neuen Reading BatOut &#039;&#039;&#039;ebenfalls als positiver Wert&#039;&#039;&#039; bereitgestellt. Auch hier gilt der Grundsatz, dass zum Beispiel BatOut nur die von der Batterie gelieferte Energie und keinen Mix aus Batterie- und PV-Energie enthalten darf.&lt;br /&gt;
&lt;br /&gt;
 &lt;br /&gt;
===== Integration eines DEYE SUN-12K-SG04LP3-EU =====&lt;br /&gt;
&lt;br /&gt;
[[Datei:Screenshot 2025-09-23 203955.png|right|thumb|400px|DEYE SUN-12K-SG04LP3-EU Grundlegende Systemarchitektur]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Der DEYE vereint wie wohl alle bzw. die meisten Hybrid-Wechselrichter die Anschlüsse:&lt;br /&gt;
&lt;br /&gt;
* Eingang Solar-Strings&lt;br /&gt;
* Anschluß Batteriespeicher&lt;br /&gt;
* AC-Anschluß öffnetliches Netz&lt;br /&gt;
* AC-Anschluß Ersatzlast (wird bei Netzausfall weiter versorgt)&lt;br /&gt;
* AC-Anschluß Notstromgenerator bzw. netzgekoppelter anderer Wechselrichter (hier ist ein Growatt Micro-Wechselrichter angeschlossen)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Für die Integration werden die setup-Attribute durch das FHEM Device &#039;&#039;Deye_Inverter&#039;&#039; besetzt:&lt;br /&gt;
&lt;br /&gt;
* setupInverterStrings zur Definition der vorhandenen Solarstrings&lt;br /&gt;
* setupInverterDev01 zur Definition der Inverteranschlüsse und Eigenschaften&lt;br /&gt;
* setupBatteryDev01 integriert die Batterie&lt;br /&gt;
* setupMeterDev liefert die Daten für Netzbezug und Netzeinspeisung&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Das nachfolgend verwendete Device &#039;&#039;Deye_Inverter&#039;&#039; ist ein MQTT-Device. Es bekommt Daten vom Deye-Hybridwechselrichter per MQTT und sendet Einstellungen zum Deye, bspw. maximaler Lade/Entladestrom des Akkus, Laden des Akkus aus dem Netz, Ein- und Ausschalten des Micro-Inverter-Ports.&lt;br /&gt;
Das Reading &#039;&#039;Deye_Growatt_power&#039;&#039; im Schlüssel &#039;&#039;pvOut&#039;&#039; ist eine Addition aus den Daten des Deye WR und des angeschlossenen Growatt Micro-Wechselrichters.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
attr &amp;lt;Name&amp;gt; setupInverterStrings Sueddach,Garagendach&lt;br /&gt;
attr &amp;lt;Name&amp;gt; setupInverterDev01 Deye_Inverter pvOut=Deye_Growatt_power:W capacity=15200 etotal=total_pv_production:kWh&lt;br /&gt;
attr &amp;lt;Name&amp;gt; setupBatteryDev01 Deye_Inverter pin=-pout pout=battery_output_power:W intotal=total_charge_of_the_battery:kWh outtotal=total_discharge_of_the_battery:kWh cap=15200 charge=SOC_jkbms&lt;br /&gt;
attr &amp;lt;Name&amp;gt; setupMeterDev Deye_Inverter gcon=total_grid_power:W contotal=total_energy_bought:kWh gfeedin=-gcon feedtotal=total_energy_sold:kWh&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;setupInverterStrings&#039;&#039; benennt alle vorhandenen Solarstrings. Werden mehrere Inverter definiert, kann mit dem hier nicht verwendeten Schlüssel &#039;&#039;strings&#039;&#039; eine Zuordnung der Strings zum angeschlossenen Inverter vorgenommen werden.&lt;br /&gt;
&lt;br /&gt;
Im Attribut &#039;&#039;setupInverterDev01&#039;&#039; ist der allgemeine Schlüssel &#039;&#039;capacity&#039;&#039; zur Festlegung der Inverterleistung von 15200 Watt gesetzt. Die spezifischen Schlüssel &#039;&#039;pvOut&#039;&#039; und &#039;&#039;etotal&#039;&#039; teilen dem Modul die aktuell erzeugte PV-Leistung sowie die gesamte erzeugte PV-Energie mit. Letzteres dient unter anderem dazu die stündlich real erzeugte PV-Energie festzuhalten und mit der Prognose zu vergleichen. Die in den schlüsseln hinterlegten dürfen keine Battrie- oder Netzanteile enthalten.&lt;br /&gt;
&lt;br /&gt;
Das Attr &#039;&#039;setupBatteryDev01&#039;&#039; implementiert alle relevanten Batteriewerte. Das Reading &#039;&#039;battery_output_power&#039;&#039; im Schlüssel &#039;&#039;pout&#039;&#039; liefert den Batterie-Output als positiven Wert. Als Besonderheit kann der Wert &#039;&#039;pin&#039;&#039;, die Batterie-Ladeleistung, den Wert des Readings in &#039;&#039;pout&#039;&#039; übernehmen wenn dieser Wert ein negatives Vorzeichen hat. Die Schlüssel &#039;&#039;intotal&#039;&#039; und &#039;&#039;outtotal&#039;&#039; liefern die summierten Totalwerte für Batterie Ladung bzw. Entladung. Die Kapazität &#039;&#039;cap&#039;&#039; wird für verschiedene Aspekte der SoC- und Ladesteuerung verwendet. &lt;br /&gt;
&lt;br /&gt;
Abschließend liefern die angebenen Readings in den Schlüsseln des Attributes &#039;&#039;setupMeterDev&#039;&#039; alle notwendigen Werte des Netzbezugs und der Netzeinspeisung.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Nachfolgende Links liefern &#039;&#039;&#039;weitere Infos&#039;&#039;&#039; wie man Daten aus dem Deye WR ausliest und Einstellungen des Deye WR verändert:&lt;br /&gt;
&lt;br /&gt;
* https://github.com/klatremis/esphome-for-deye&lt;br /&gt;
* https://github.com/philipphenkel/esphome-config&lt;br /&gt;
* https://github.com/bagges/deye-esp32-bridge&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Für eine weiterführende Automation soll der Akku netzdienlich in der Mittagszeit geladen werden. Dazu wurde durch den Anwender folgendes userReadings &#039;&#039;MaxBattCharge_Request&#039;&#039; im Device &#039;&#039;Deye_Inverter&#039;&#039; für den maximalen Ladestrom definiert (bei 90% SOC und bei 100% SOC):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
MaxBattCharge_Request:SOC_jkbms.* {&lt;br /&gt;
                                    if ((ReadingsNum(&#039;mySolarForecast&#039;,&#039;RestOfDayPVforecast&#039;,&#039;&#039;)&lt;br /&gt;
                                         - ReadingsNum(&#039;mySolarForecast&#039;,&#039;special_todayConForecastTillSunset&#039;,&#039;&#039;)&lt;br /&gt;
                                         - (90-ReadingsNum(&#039;JK_BMS&#039;,&#039;capacity_remaining&#039;,&#039;&#039;))/100*310*51.2) &amp;lt; 400&lt;br /&gt;
                                         and ReadingsAge(&#039;JK_BMS&#039;,&#039;capacity_full_timestamp&#039;,&#039;&#039;) &amp;lt; 561600)&lt;br /&gt;
                                    {100}&lt;br /&gt;
                                    elsif ((ReadingsNum(&#039;mySolarForecast&#039;,&#039;RestOfDayPVforecast&#039;,&#039;&#039;)&lt;br /&gt;
                                            - ReadingsNum(&#039;mySolarForecast&#039;,&#039;special_todayConForecastTillSunset&#039;,&#039;&#039;)&lt;br /&gt;
                                            - (100-ReadingsNum(&#039;JK_BMS&#039;,&#039;capacity_remaining&#039;,&#039;&#039;))/100*310*51.2) &amp;lt; 400&lt;br /&gt;
                                            and ReadingsAge(&#039;JK_BMS&#039;,&#039;capacity_full_timestamp&#039;,&#039;&#039;) &amp;gt;= 561600)&lt;br /&gt;
                                    {100}&lt;br /&gt;
                                    elsif ((ReadingsNum(&#039;mySolarForecast&#039;,&#039;RestOfDayPVforecast&#039;,&#039;&#039;)&lt;br /&gt;
                                            - ReadingsNum(&#039;mySolarForecast&#039;,&#039;special_todayConForecastTillSunset&#039;,&#039;&#039;)&lt;br /&gt;
                                            - (90-ReadingsNum(&#039;JK_BMS&#039;,&#039;capacity_remaining&#039;,&#039;&#039;))/100*310*51.2) &amp;gt;= 400&lt;br /&gt;
                                            and ReadingsAge(&#039;JK_BMS&#039;,&#039;capacity_full_timestamp&#039;,&#039;&#039;) &amp;lt; 561600)&lt;br /&gt;
                                    {ceil ((90-ReadingsNum(&#039;JK_BMS&#039;,&#039;capacity_remaining&#039;,&#039;&#039;))/100*310/3.5)}&lt;br /&gt;
                                    elsif ((ReadingsNum(&#039;mySolarForecast&#039;,&#039;RestOfDayPVforecast&#039;,&#039;&#039;)&lt;br /&gt;
                                            - ReadingsNum(&#039;mySolarForecast&#039;,&#039;special_todayConForecastTillSunset&#039;,&#039;&#039;)&lt;br /&gt;
                                            - (100-ReadingsNum(&#039;JK_BMS&#039;,&#039;capacity_remaining&#039;,&#039;&#039;))/100*310*51.2) &amp;gt;= 400&lt;br /&gt;
                                            and ReadingsAge(&#039;JK_BMS&#039;,&#039;capacity_full_timestamp&#039;,&#039;&#039;) &amp;gt;= 561600)&lt;br /&gt;
                                    {ceil ((100-ReadingsNum(&#039;JK_BMS&#039;,&#039;capacity_remaining&#039;,&#039;&#039;))/100*310/3.0)}&lt;br /&gt;
                                  }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Das userReading wird in einer Automation benutzt, um den maximalen Ladestrom einzustellen; Ladebeginn bei ausreichend Sonne ist ab 11:30 eingestellt.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
===== Integration eines Fronius Symo GEN24 10.0 Plus mit (virtuellen) Batterie-Wechselrichter =====&lt;br /&gt;
&lt;br /&gt;
[[Datei:Screenshot 2025-10-04 162422.png|right|thumb|400px|Flußgrafik mit eingebauten Hybrid-Wechselrichter]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Die Integration des Wechselrichters Fronius Symo GEN24 10.0 Plus benötigt zunächst eine Anbindung in FHEM, was mit dem Modul 98_fronius.pm vorgenommen wurde.&lt;br /&gt;
&lt;br /&gt;
Das Ziel der Kurzdokumentation ist neben der Darstellung eines Hybridwechselrichters aufzuzeigen, wie man den Wandlungsverlust eines PV-Wechselrichters (pvIn – pvOut) in Näherung visualisieren kann.  &lt;br /&gt;
&#039;&#039;&#039;pvIn&#039;&#039;&#039; ist dabei die Summe der Produkte aus den DC Strom- und Spannungswerten der einzelnen Strings (hier 2109 W), &#039;&#039;&#039;pvOut&#039;&#039;&#039; (hier 2065 W) ist die an den Inverterknoten (hier 1105 W) plus die an die Batterie abgegebene Leistung (hier 960 W). Bei dieser Implementierung wird mit einem zusätzlichen (virtuellen) Batterie-Wechselrichter gearbeitet. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
In Vorbereitung werden die beiden anzugebenden Readings &#039;&#039;&amp;lt;pvIn&amp;gt;&#039;&#039; und &#039;&#039;&amp;lt;pvOut&amp;gt;&#039;&#039; zum Beispiel als userReadings erstellt, wobei &#039;&amp;lt;pvIn&amp;gt;&#039;&#039; und &#039;&#039;&amp;lt;pvOut&amp;gt;&#039;&#039; natürlich durch Readingnamen ersetzt werden müssen. Diese beiden Readings werden wie folgt berechnet / erstellt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;pvOut&amp;gt; = Inverter_Common_PAC_Value - PowerFlow_Site_P_Akku&lt;br /&gt;
&amp;lt;pvIn&amp;gt; = Inverter_Common_IDC_Value * Inverter_Common_UDC_Value + Inverter_Common_IDC_2_Value * Inverter_Common_UDC_2_Value&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der &#039;&#039;&#039;PV-Wechselrichter&#039;&#039;&#039; wird mit dem Attribut setupInverterDev01 definiert, wobei die angelegten Readingnamen in den Schlüsseln &#039;&#039;pvIn&#039;&#039; bzw. &#039;&#039;pvOut&#039;&#039; verwendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
SymGen24 icon=inverter@#ff8c00:inverter@grey capacity=10000 strings=suedwest,nordost etotal=User_Produced_PV:kWh pvOut=&amp;lt;pvOut&amp;gt;:W pvIn=&amp;lt;pvIn&amp;gt;:W&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dem PV-Wechselrichter sind die Strings &#039;&#039;suedwest&#039;&#039; und &#039;&#039;nordost&#039;&#039; zugewiesen.&lt;br /&gt;
&lt;br /&gt;
Der zusätzliche &#039;&#039;&#039;Batterie-Wechselrichter&#039;&#039;&#039; wird mit dem Attribut setupInverterDev02 hinzugefügt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
SymGen24 icon=inverter@#ff8c00:inverter@grey strings=none ac2dc=-PowerFlow_Site_P_Akku:W  dc2ac=PowerFlow_Site_P_Akku:W capacity=7680&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Bemerkungen:&#039;&#039;&#039; &amp;lt;br&amp;gt;&lt;br /&gt;
- strings=none sowie die Schlüssel ac2cd und dc2ac sind typisch für einen Batterie-WR bzw. kennzeichnen einen WR als Batterie-Wechselrichter &amp;lt;br&amp;gt;&lt;br /&gt;
- die an die Batterie abgegebene Leistung ist negativ&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===== Einbindung eines Fronius Symo GEN24 10.0 Plus mit BYD Batterie ohne (virtuellen) Batterie-Wechselrichter =====&lt;br /&gt;
Diee Implementierung ist komplett auf der Seite [[Solaranlage Komplettbeispiel Fronius BYD]] beschrieben.&lt;br /&gt;
&lt;br /&gt;
Zur Integration des &#039;&#039;Fronius Symo&#039;&#039; in FHEM wird das Modul &#039;&#039;fronius.pm&#039;&#039; verwendet und in dem definierten Device &#039;&#039;Fronius_Symo_Gen24&#039;&#039; diverse userReadings erzeugt. Diese Readings werden in den korrespondierenden Schlüsseln des Attributes &#039;&#039;setupInverterDev01&#039;&#039; angegeben:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
setupInverterDev01 Fronius_Symo_Gen24 etotal=User_Produced_PV_Energie:kWh pvOut=PowerFlow_Site_P_PV:W capacity=6000 strings=Dach_Ost,Dach_West&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Am Wechselrichter Fronius Symo Gen24 ist eine BYD Batterie angeschlossen. Das Device &#039;&#039;PV-Batterie&#039;&#039; wird per [[ModbusAttr]] angelegt und gemanaged. Die entsprechenden Readings werden im Attribut &#039;&#039;SetupBatteryDev01&#039;&#039; hinterlegt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
PV_Batterie&lt;br /&gt;
cap=10240&lt;br /&gt;
charge=BatteryChargePercent&lt;br /&gt;
icon=@dyn:@dyn:@dyn:@dyn&lt;br /&gt;
intotal=Summe_Ladung:Wh&lt;br /&gt;
outtotal=Summe_Entladung:Wh&lt;br /&gt;
pin=BatteryChargeWatt:W&lt;br /&gt;
pout=BatteryDischargeWatt:W&lt;br /&gt;
pinmax=10000&lt;br /&gt;
show=2&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===== Einbindung eines SMA - Hybrid Sunny Tripower Smart Energy  =====&lt;br /&gt;
[[Datei:Screenshot 2025-10-05 083530.png|right|thumb|400px|SMAInverter.pm]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Der genannte Wechselrichter wird über das Modul SMAInverter.pm in FHEM integriert. Die nebenstehende Abbildung zeigt und benennt die einzelnen Readings des Moduls welche in den entsprechenden Schlüsseln der SolarForecast Attribute &#039;&#039;setupInverterDevXX&#039;&#039; und &#039;&#039;setupBatteryDevXX&#039;&#039; zugeordnet werden. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Die Readings des SMAInverter Moduls können vor der Verwendung über geeignete Methoden (userReadings) in Readings mit sprechenden Namen überführt werden um sie mit diesen Namen in die genannten Attributschlüssel einzufügen. In der vorliegenden Lösung befinden sich die  Anwendungen SolarForecast und und die Inverter-Steuerung jeweils auf unterschiedlicher Hardware. Als Verbindungslayer wird MQTT genutzt, wobei dadurch bereits die dargestellten Readingnamen entstehen. &lt;br /&gt;
 &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Der integrierte SolarForecast Grafikbereich ==&lt;br /&gt;
Das SolarForecast Modul beinhaltet eine Grafikdarstellung die sich in mehrere Bereiche gliedert. Diese Bereiche können über Attribute ein- bzw. ausgeblendet sowie die Inhalte festgelegt werden.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Screenshot 2024-09-21 223545.png|right|thumb|400px|Bereiche der integrierten Grafik]]&lt;br /&gt;
&lt;br /&gt;
Der nebenstehende Screenshot gibt die Gliederung der Grafikbereiche wieder:&lt;br /&gt;
&lt;br /&gt;
 1 - der Kopfbereich (Graphic Header)&lt;br /&gt;
 2 - durch den Nutzer selbst definierbarer Inhalt (Graphic Header Own Specification) als Teil des Kopfbereiches&lt;br /&gt;
 3 - Bereich Verbraucherdarstellung (Consumer Legend)&lt;br /&gt;
 4 - Bereich Balkengrafik (Beam Graphic)&lt;br /&gt;
 5 - Energieflußgrafik (Flow Graphic)&lt;br /&gt;
&lt;br /&gt;
Jeder dieser Bereiche hat spezifische Steuerungsattribute zur Anpassung des Verhaltens und ggf. des Inhalts.&lt;br /&gt;
&lt;br /&gt;
=== 1 - Der Grafik Kopfbereich ===&lt;br /&gt;
Im Kopfbereich befinden sich fest verankerte Informationen zur verwendeten Vorhersage-API sowie deren Status, Informationen zum Sonnenaufgang und Sonnenuntergang, dem aktuellen Gesamtstatus des SolarForecast Devices, Abweichungskennzahlen der PV Vorhersage sowie weitere Kennzeichen zur Verfügung.&lt;br /&gt;
Über zwei Drucktasten kann eine Anlagenprüfung durchgeführt und in den Spportthread des Moduls im FHEM Forum abgesprungen werden.&lt;br /&gt;
&lt;br /&gt;
Mit dem Attribut &#039;&#039;&#039;graphicHeaderShow&#039;&#039;&#039; kann der gesamte Kopfbereich 1 und 2 (nutzereigener Teilbereich) ausgeblendet werden. Wird der Kopbereich ausgeblendet, ist es hilfreich mit dem Attribut &#039;&#039;&#039;plantControl-&amp;gt;showLink&#039;&#039;&#039; einen Link zur Detailgrafik einzufügen da der in der Kopfgrafik integrierte Link in diesem Fall nicht mehr zur Verfügung steht. &lt;br /&gt;
&lt;br /&gt;
Das Attribut &#039;&#039;&#039;graphicHeaderDetail&#039;&#039;&#039; definiert welche Bereiche des Grafikkopfes angezeigt werden sollen. Es kann die Anzeige des Energieverbrauches, der PV-Erzeugung und der vom Nutzer selbst definierte Inhalt miteinander frei kombiniert werden.&lt;br /&gt;
&lt;br /&gt;
=== 2 - Der Bereich mit selbst definierbarem Inhalt (Graphic Header Own Specification) ===&lt;br /&gt;
In diesem Bereich kann der User beliebige Readings, Set-Kommandos und Attribute des SolarForecast Devices oder anderer Devices, die sich im gleichen FHEM-System wie das SolarForecast Device befinden, anzeigen lassen.&lt;br /&gt;
&lt;br /&gt;
Dieser Bereich ist als Tabelle mit einer beliebigen Anzahl von Zeilen organisiert. Jede Zeile beinhaltet ein Kommentarfeld und vier Nutzfelder zur Darstellung ausgewähler Werte + Label. Der Bereich bzw. der Inhalt des Bereiches wird mit dem Attribut &#039;&#039;&#039;graphicHeaderOwnspec&#039;&#039;&#039; definiert.  &lt;br /&gt;
&lt;br /&gt;
Das nachfolgende Beispiel zeigt ein real gesetztes graphicHeaderOwnspec Attribut. Die Leerzeilen sind nicht notwendig, erhöhen aber die Lesbarkeit der Struktur.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#&lt;br /&gt;
Autarkierate:Current_AutarkyRate&lt;br /&gt;
PV&amp;amp;nbsp;übermorgen:special_dayAfterTomorrowPVforecast&lt;br /&gt;
Verbrauch&amp;amp;nbsp;bis&amp;lt;br&amp;gt;Sunset:special_todayConForecastTillSunset&lt;br /&gt;
Verbrauch&amp;amp;nbsp;bis&amp;lt;br&amp;gt;nächsten&amp;amp;nbsp;Sunrise:special_conForecastTillNextSunrise&lt;br /&gt;
&lt;br /&gt;
#Batterie&lt;br /&gt;
BatteryLive&amp;amp;nbsp;Status:userFn_BatLive_State&lt;br /&gt;
SoC&amp;amp;nbsp;Limit:Settings_CGwacs_BatteryLife_MinimumSocLimit_value@MQTT2_cerboGX_c0619ab34e08_settings&lt;br /&gt;
SoC&amp;amp;nbsp;Ist:SOC_value@MQTT2_cerboGX_c0619ab34e08_battery&lt;br /&gt;
SOC&amp;amp;nbsp;Optimum:Battery_OptimumTargetSoC&lt;br /&gt;
&lt;br /&gt;
#&lt;br /&gt;
aktuelle&amp;amp;nbsp;Ladung:userFn_Bat_EnergyState&lt;br /&gt;
Verfügbar:userFn_BatLive_UsableEnergy&lt;br /&gt;
benötigte&amp;amp;nbsp;Ladeenergie:userFn_Bat_EnergyRemain&lt;br /&gt;
Ladefreigabe:Battery_ChargeRecommended&lt;br /&gt;
&lt;br /&gt;
#&lt;br /&gt;
akt.&amp;amp;nbsp;DC&amp;amp;nbsp;Strom:DC_Current_value@MQTT2_cerboGX_c0619ab34e08_battery&lt;br /&gt;
Restladezeit&amp;amp;nbsp;Ziel:userFn_Bat_HoursUntilChargeFinish&lt;br /&gt;
MPII&amp;amp;nbsp;Ladestrom&amp;lt;br&amp;gt;Limit&amp;amp;nbsp;Soll:userFn_Bat_MaxChargeCurrent_set&lt;br /&gt;
:&lt;br /&gt;
&lt;br /&gt;
#Settings&lt;br /&gt;
Batterie&amp;amp;nbsp;Lademanagement:userFn_BatteryChargeManagement&lt;br /&gt;
SoC&amp;amp;nbsp;Management:userFn_BatterySoCManagement&lt;br /&gt;
MPII&amp;amp;nbsp;Ladestrom&amp;lt;br&amp;gt;Limit&amp;amp;nbsp;Ist:MaxChargeCurrent@MQTT2_cerboGX_c0619ab34e08_vebus&lt;br /&gt;
SmartSolar&amp;lt;br&amp;gt;Einspeisung:OvervoltageFeedIn@MQTT2_cerboGX_c0619ab34e08_settings&lt;br /&gt;
&lt;br /&gt;
MPII&amp;amp;nbsp;Limit&amp;lt;br&amp;gt;Einspeisung&amp;amp;nbsp;(W):MaxFeedInPower@MQTT2_cerboGX_c0619ab34e08_settings&lt;br /&gt;
SOC&amp;amp;nbsp;Limit&amp;amp;nbsp;Soll:MinimumSocLimit@MQTT2_cerboGX_c0619ab34e08_settings&lt;br /&gt;
Verbr.&amp;amp;nbsp;Neuplanung:consumerNewPlanning&lt;br /&gt;
Wetteranzeige:graphicShowWeather &lt;br /&gt;
&lt;br /&gt;
Anzeige&amp;amp;nbsp;Nachtstunden:graphicShowNight&lt;br /&gt;
hist.&amp;amp;nbsp;Stunden:graphicHistoryHour&lt;br /&gt;
Autokorrektur:pvCorrectionFactor_Auto&lt;br /&gt;
Victron&amp;amp;nbsp;BatteryLife&amp;lt;br&amp;gt;(1=an,10=aus):BatteryLife@MQTT2_cerboGX_c0619ab34e08_settings&lt;br /&gt;
&lt;br /&gt;
Grid&amp;amp;nbsp;Setpoint:GridSetpoint@MQTT2_cerboGX_c0619ab34e08_settings&lt;br /&gt;
Debug:ctrlDebu&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das Kommentarfeld am Beginn einer Zeile wird durch ein &#039;&#039;&#039;#&#039;&#039;&#039; gekennzeichnet. Folgt dem # ein String, wird dieser String als Kommentarangabe gewertet und in der Grafik ausgegeben. Ein leeres Kommentarfeld wird lediglich durch ein # gefolgt von einem Leerzeichen oder NL beschrieben:&lt;br /&gt;
&lt;br /&gt;
 #:&amp;lt;Text&amp;gt;  (bzw nur # für einen &#039;leeren&#039; Titel)&lt;br /&gt;
&lt;br /&gt;
Ein Netzfeld bzw. ein Datensatz wird durch die Angabe von einem Label und dem darzustellenden Wert getrennt durch &#039;:&#039; definiert:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;Label&amp;gt;:&amp;lt;Reading, Attribut oder Set-Kommando&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das System erkennt selbständig, ob es sich bei der Angabe um ein Reading, Attribut oder ein Set-Kommando handelt. Sollen Readings, Set-Kommandos und Attribute anderer Devices als dem SolarForecast-Device angezeigt werden, kann dies einfach durch Zusatz von&lt;br /&gt;
&lt;br /&gt;
 ....@&amp;lt;Device-Name&amp;gt;&lt;br /&gt;
&lt;br /&gt;
an den Parameter erreicht werden.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Screenshot 2024-12-05 203916.png|right|thumb|400px|Darstellung eigener Grafikbereich]]&lt;br /&gt;
Die Eingabe kann mehrzeilig erfolgen. Werte mit den Einheiten &amp;quot;Wh&amp;quot; bzw. &amp;quot;kWh&amp;quot; werden entsprechend der Einstellung des Attributs &#039;&#039;&#039;graphicEnergyUnit&#039;&#039;&#039; umgerechnet. Im Label sind Leerzeichen durch &amp;quot;&amp;amp;amp;nbsp;&amp;quot; einzufügen, ein Zeilenumbruch durch &amp;quot;&amp;amp;lt;br&amp;amp;gt;&amp;quot;. Ein leeres Feld in einer Zeile wird durch &amp;quot;:&amp;quot; ohne eine weitere Angabe erzeugt.&lt;br /&gt;
&lt;br /&gt;
Die nebenstehende Abbildung zeigt die reale Darstellung der oben angegebenen Struktur im Attribut graphicHeaderOwnspec.&lt;br /&gt;
&lt;br /&gt;
==== Formatierung der Inhalte im Bereich &amp;quot;Graphic Header Own Specification&amp;quot; ====&lt;br /&gt;
&lt;br /&gt;
Die angezeigten Inhalte, wie z.B. Readingwerte, des User eigenen Bereiches (Attribut graphicHeaderOwnspec) können durch Spezifikationen im Attribut &#039;&#039;&#039;graphicHeaderOwnspecValForm&#039;&#039;&#039; manipuliert werden.&lt;br /&gt;
&lt;br /&gt;
Die Online-Hilfe zum Attribut graphicHeaderOwnspecValForm erläutert die verfügbaren Möglichkeiten und Optionen.&lt;br /&gt;
Als Beispiel hier einige mögliche Notationen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
  &#039;userFn_Bat_HoursUntilChargeFinish&#039;                        =&amp;gt; &amp;quot;($VALUE).&#039; Stunden&#039;&amp;quot;,&lt;br /&gt;
  &#039;MQTT2_cerboGX_c0619ab34e08_battery.SOC_value&#039;             =&amp;gt; &amp;quot;%.1f %%&amp;quot;,&lt;br /&gt;
  &#039;Settings_CGwacs_BatteryLife_MinimumSocLimit_value&#039;        =&amp;gt; &amp;quot;%.1f %%&amp;quot;,&lt;br /&gt;
  &#039;Info_MaxChargeCurrent_value&#039;                              =&amp;gt; &amp;quot;($VALUE).&#039; A&#039;&amp;quot;,&lt;br /&gt;
  &#039;DC_Current_value&#039;                                         =&amp;gt; &amp;quot;%.0f A&amp;quot;,&lt;br /&gt;
  &#039;MQTT2_cerboGX_c0619ab34e08_solarcharger_Common.Regulated&#039; =&amp;gt; &amp;quot;($VALUE eq &#039;1&#039; ? &#039;Ja&#039; : &#039;Nein&#039;)&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Eine Besonderheit besteht bei darzustellenden Werten die die Einheiten &#039;Wh&#039; bzw. &#039;kWh&#039; besitzen. Diese Werte und Einheiten werden automatisch entsprechend des gesetzten Attributes graphicEnergyUnit umgerechnet und angezeigt.&lt;br /&gt;
&lt;br /&gt;
Soll in diesen Fällen eine automatische Formatierung umgangen werden, kann eine solche Notation verwendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    &#039;Today_PVforecast&#039; =&amp;gt; &amp;quot;(sprintf &#039;%.1f &amp;amp;amp;nbsp;kWh&#039;, ($VALUE / 1000))&amp;quot;,&lt;br /&gt;
} &lt;br /&gt;
&amp;lt;/pre&amp;gt;  &lt;br /&gt;
&lt;br /&gt;
Die Angabe von &amp;amp;amp;nbsp; vor &#039;kWh&#039; verhindert die Erkennung der Einheit und so auch die automatische Umsetzung durch das Modul. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== 3 - Bereich Verbraucherdarstellung (Consumer Legend) ===&lt;br /&gt;
Dieser Grafikbereich zeigt die im Modul registrierten Verbraucher sowie deren Status und weitere Informationen an. Sind die Voraussetzungen gegeben, d.h. entsprechende Schlüssel in der ConsumerXX-Attributen gesetzt, können die Verbraucher über das Paneel manuell geschaltet werden.&lt;br /&gt;
&lt;br /&gt;
Zur Anpassung der Darstellung sind verschiedene Attribute verfügbar:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
;consumerControl-&amp;gt;showLegend: Definiert die Lage bzw. Darstellungsweise der Verbraucherlegende sofern Verbraucher im SolarForecast Device registriert sind.&lt;br /&gt;
;consumerControl-&amp;gt;adviceIcon: Definiert die Art der Information über die geplanten Schaltzeiten eines Verbrauchers in der Verbraucherlegende. &lt;br /&gt;
;consumerControl-&amp;gt;detailLink: Wenn gesetzt, ist der jeweilige Verbraucher klickbar um direkt zur Detailansicht des jeweiligen Geräts auf einer neuen Browserseite zu gelangen. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Im Standard wird das Verbraucherpaneel zwischen dem Kopfbereich und der ersten Balkengrafik Ebene dargestellt. Mit dem Attribut &#039;&#039;&#039;consumerControl-&amp;gt;showLegend&#039;&#039;&#039; und Argument &#039;&#039;&#039;icon_bottom&#039;&#039;&#039; oder &#039;&#039;&#039;text_bottom&#039;&#039;&#039; kann das Verbraucherpaneel an das Ende des Grafikbereiches verschoben werden.&lt;br /&gt;
Die Verwendung von &#039;&#039;&#039;text_bottom&#039;&#039;&#039; oder &#039;&#039;&#039;text_top&#039;&#039;&#039; blendet die Icon-Spalte im Verbraucherpaneel aus.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;gallery&amp;gt;&lt;br /&gt;
File:Screenshot 2025-02-08 093239.png|Darstellung mit consumerControl showLegend=icon_top (default)&lt;br /&gt;
File:Screenshot 2025-02-08 093646.png|Darstellung mit consumerControl showLegend=icon_bottom&lt;br /&gt;
File:Screenshot 2025-02-08 152018.png|Darstellung mit consumerControl showLegend=text_top bzw. text_bottom&lt;br /&gt;
&amp;lt;/gallery&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Datei:Screenshot 2025-02-08 213634.png|right|thumb|400px|]]&lt;br /&gt;
Ein Mouse-Over über das Uhrensymbol zeigt nähere Informationen zum Status, den Planungsdaten, Modus und weitere Daten des Verbrauchers. Der Schalter &amp;quot;Aus/Ein&amp;quot; visualisiert den Schaltzustand des Verbrauchers und können auch zum manuellen Schalten des Verbrauchers dienen. Voraussetzung ist, dass im consumer-Attribut die Schlüssel &amp;quot;on&amp;quot; und &amp;quot;off&amp;quot; mit den Set-Kommandos zum Schalten des Verbrauchers defininiert sind.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Screenshot 2025-02-08 222210.png|right|thumb|300px|]]&lt;br /&gt;
Der Auto-Schieberegler zeigt einerseits den Freigabestatus zum Schalten des Verbrauchers durch das SolarForecast Modul und kann andererseits manuell betätigt werden. Der Status des Reglers korrespondiert mit dem im zugehörigen Consumer Attribut vorhandenen Schlüssel &#039;&#039;&#039;auto&#039;&#039;&#039; angegebenen Reading-Wert.&lt;br /&gt;
&lt;br /&gt;
Die Verbraucherbezeichnungen in dem Paneel können in der Standardkonfiguration angeklickt werden um direkt zur Detailansicht des Devices zu gelangen. Mit dem Attribut &#039;&#039;&#039;consumerControl-&amp;gt;detailLink&#039;&#039;&#039; kann der Link zur Detailansicht deaktiviert werden.&lt;br /&gt;
&lt;br /&gt;
Das Uhrensymbol kann mit dem Attribut &#039;&#039;&#039;consumerControl-&amp;gt;adviceIcon&#039;&#039;&#039; geändert und/oder dessen Farbe angepasst, sowie einen Informationstext statt eines Icons angezeigt werden. &lt;br /&gt;
&lt;br /&gt;
=== 4 - Bereich Balkengrafik (Beam Graphic) ===&lt;br /&gt;
&lt;br /&gt;
Die Balkengrafik des Moduls ist ein wesentliches Visualisierungshilfsmittel. Im Auslieferungszustand existiert eine Balkengrafikebene, die &#039;&#039;&#039;Ebene 1&#039;&#039;&#039;.&lt;br /&gt;
Jede Ebene kann zwei Balkendiagramme mit auswählbaren Inhalten, Farben und einstellbarem LayoutTyp darstellen. In Ebene 1 kann die Wettervorhersage eingebettet oder, bei Vorhandensein von Batteriegeräten, die zukünftigen Zeiten mit empfohlener Ladefreigabe und SoC-Prognose eingeblendet werden. &lt;br /&gt;
&lt;br /&gt;
Welche Daten in welchen Zeiträumen dargestellt werden sollen, die Bestimmung des Layouts, die Gestaltung der Balken und einiges mehr wird durch Attribute gesteuert. Ein Teil dieser Einstellungen wird über das zusammengesetzete Attribut &#039;&#039;&#039;graphicControl-&amp;gt;&#039;Schlüssel&#039;=&#039;Wert&#039;&#039;&#039;&#039; eingestellt:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
::*&#039;&#039;&#039;grundsätzliche Layout Definitionen&#039;&#039;&#039;&lt;br /&gt;
[[Datei:Screenshot 2025-01-12 204342.png|right|thumb|400px|Standardanzeige mit Wetter-Icons, gesetzen Attributen graphicBeam1Color=D4C177, graphicHistoryHour=11]]&lt;br /&gt;
&lt;br /&gt;
;graphicControl-&amp;gt;beamPaddingTop:Legt den Platz in px im Balkendiagramm fest, der zwischen dem oberen Rand der jeweiligen Balkengrafik Ebene und der ersten Text- oder Iconreihe dieser Ebene eingefügt wird.&lt;br /&gt;
;graphicControl-&amp;gt;beamPaddingBottom:Legt den Platz in px im Balkendiagramm fest, der zwischen der letzten Text- oder Iconreihe der jeweiligen Balkengrafik Ebene und dem unteren Rand dieser Ebene eingefügt wird. &lt;br /&gt;
;graphicControl-&amp;gt;layoutType:Layout-Optionen zur grundlegenden Gestaltung der Balkengrafik&lt;br /&gt;
;graphicControl-&amp;gt;hourStyle:Format der Zeitangabe in der Balkengrafik&lt;br /&gt;
;graphicControl-&amp;gt;hourCount:Anzahl der darzustellenden Balken/Stunden in der Balkengrafk&lt;br /&gt;
[[Datei:Screenshot Screenshot 2025-05-21 153613.png|right|thumb|400px|Erweiterung des oberen und unteren Abstandes mit den Attributen graphicControl-&amp;gt;beamPaddingTop und graphicControl-&amp;gt;beamPaddingBottom]]&lt;br /&gt;
;graphicControl-&amp;gt;energyUnit:definiert die Einheit zur Anzeige der elektrischen Leistung in der Grafik&lt;br /&gt;
;graphicHistoryHour:Anzahl der vorangegangenen Stunden die in der Balkengrafik dargestellt werden&lt;br /&gt;
;graphicControl-&amp;gt;beamWidth:Vorgabe Breite der Balken der Balkengrafik in px (sonst automatische Bestimmung) &lt;br /&gt;
;graphicBeamHeightLevel1:Multiplikator zur Festlegung der maximalen Balkenhöhe der jeweiligen Ebene (hier Ebene 1)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
::*&#039;&#039;&#039;Definition der Inhalte und Balkeneigenschaften&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[Datei:Screenshot 2025-01-12 204756.png|right|thumb|400px|Wie oben aber mit gesetzem Attribut graphicShowNight=1]]&lt;br /&gt;
;graphicBeam1Color:Farbauswahl des primären Balkens (Ebene 1)&lt;br /&gt;
;graphicBeam2Color:Farbauswahl des sekundären Balkens (Ebene 1)&lt;br /&gt;
;graphicBeam1Content:Legt den darzustellenden Inhalt des primären Balken fest (Ebene 1)&lt;br /&gt;
;graphicBeam2Content:Legt den darzustellenden Inhalt des sekundären Balken fest (Ebene 1)&lt;br /&gt;
;graphicBeam1FontColor:Auswahl der Schriftfarbe des primären Balkens (Ebene 1)&lt;br /&gt;
;graphicBeam2FontColor:Auswahl der Schriftfarbe des sekundären Balkens (Ebene 1)&lt;br /&gt;
[[Datei:Screenshot 2025-01-12 205024.png|right|thumb|400px|Balkengrafik mit aktivierter Ebene 2, gesetzem Attribut graphicShowNight=0 (ohne Zeitsynchronisation zwischen Ebene 1 und Ebene 2)]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
::*&#039;&#039;&#039;zusätzliche Optionen&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
;graphicShowNight:Anzeigen oder Verbergen der Nachtstunden in der Balkengrafik&lt;br /&gt;
;graphicShowWeather:Wettericons in der Balkengrafik anzeigen/verbergen (nur Ebene 1)&lt;br /&gt;
;graphicWeatherColor:Farbe der Wetter-Icons für die Tagesstunden&lt;br /&gt;
;graphicWeatherColorNight:Farbe der Wetter-Icons für die Nachtstunden&lt;br /&gt;
;graphicShowDiff:Zusätzliche Anzeige der Differenz &amp;quot;&amp;lt;primärer Balkeninhalt&amp;gt; - &amp;lt;sekundärer Balkeninhalt&amp;gt;&amp;quot; im Kopf- oder Fußbereich der Balkengrafik&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Die genaue Beschreibung und deren Optionen sind der aktuellen Online-Hilfe zu entnehmen.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Für alle Ebenen gilt:&#039;&#039;&#039; Ein Attribut mit einer ungeraden Balkennummer (z.B. graphicBeam1Content) bezieht sich immer auf den primären Balken, ein Attribut mit einer geraden Balkennummer (z.B. graphicBeam2Content) bezieht sich immer auf den sekundären Balken.&lt;br /&gt;
&lt;br /&gt;
Die Balken haben, wie die meisten Elemente in der Balkengrafik, eine Mouse-Over Eigenschaft welche relevante Informationen zu den dargestellten Werten anzeigt. &lt;br /&gt;
&lt;br /&gt;
Sind die Nachtunden mit &#039;&#039;&#039;graphicShowNight&#039;&#039;&#039; ausgeblendet, werden keine Balken angezeigt sofern es keine Werte anzuzeigen gibt. Sollten dennoch Werte ungleich 0 anzuzeigen sein, werden diese Werte auch trotz ausgeblendeter Nachstunden angezeigt. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Aktivierung der Balkengrafik Ebene 2 ====&lt;br /&gt;
[[Datei:Screenshot 2025-01-12 211741.png|right|thumb|400px|Balkengrafik mit aktivierter Ebene 2, gesetzem Attribut graphicShowNight=01 (mit Zeitsynchronisation zwischen Ebene 1 und Ebene 2)]]&lt;br /&gt;
&lt;br /&gt;
Um die &#039;&#039;&#039;Ebene 2&#039;&#039;&#039; der Balkengrafik zu aktivieren, setzt man das Attribut &#039;&#039;&#039;graphicBeam3Content&#039;&#039;&#039; und/oder &#039;&#039;&#039;graphicBeam4Content&#039;&#039;&#039; mit den gewünschten darzustellenden Inhalten. Die weiteren Darstellungsoptionen, wie graphicBeamCColor, können ebenfalls für den jeweiligen Balken selektiv definiert werden wenn ein Attribut mit der entsprechenden Nummer (z.B. 4 für Balken 4) vorhanden ist. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Hinweis:&#039;&#039;&#039; Die Wetter-Icons können nur in Ebene 1 integriert werden.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Werden in der Balkengrafik die Nachtstunden ausgeblendet (&#039;&#039;&#039;graphicShowNight=0&#039;&#039;&#039; bzw. default), findet zwischen der Ebene 1 und der Ebene 2 keine Synchronisation der Zeitebene statt. Falls es einen inhaltlichen Zusammenhang zwischen den Werten der Ebene 1 und Ebene 2 gibt, eine Zeitsynchronisation zwischen den Ebenen wünschenswert und sinnvoll. Durch Setzen des Attrbut &#039;&#039;&#039;graphicShowNight=01&#039;&#039;&#039; kann diese Synchronisation eingeschaltet werden. &lt;br /&gt;
&lt;br /&gt;
Dabei ist die Ebene 1 die führende Ebene, d.h. diese Ebene vererbt die exkludierten bzw. eventuell inkludierten Nachtstunden (wenn Werte in den Balken der Ebene 1 angezeigt werden sollen) an die nachfolgende Ebene.&lt;br /&gt;
 &lt;br /&gt;
&lt;br /&gt;
==== Aktivierung der Balkengrafik Ebene 3 ====&lt;br /&gt;
&lt;br /&gt;
Um die &#039;&#039;&#039;Ebene 3&#039;&#039;&#039; der Balkengrafik zu aktivieren, setzt man das Attribut &#039;&#039;&#039;graphicBeam5Content&#039;&#039;&#039; und/oder &#039;&#039;&#039;graphicBeam6Content&#039;&#039;&#039; mit den gewünschten darzustellenden Inhalten. Die weiteren Darstellungsoptionen, wie graphicBeamCColor, können ebenfalls für den jeweiligen Balken selektiv definiert werden wenn ein Attribut mit der entsprechenden Nummer (z.B. 5 für Balken 5) vorhanden ist. &lt;br /&gt;
&lt;br /&gt;
Werden in der Balkengrafik die Nachtstunden ausgeblendet (&#039;&#039;&#039;graphicShowNight=0&#039;&#039;&#039; bzw. default), findet zwischen der Ebene 1 und der Ebene 3 keine Synchronisation der Zeitebene statt. Falls es einen inhaltlichen Zusammenhang zwischen den Werten der Ebene 1 und Ebene 3 gibt, ist eine Zeitsynchronisation zwischen den Ebenen wünschenswert und sinnvoll. Durch Setzen des Attrbut &#039;&#039;&#039;graphicShowNight=01&#039;&#039;&#039; kann diese Synchronisation eingeschaltet werden. &lt;br /&gt;
&lt;br /&gt;
Dabei ist die Ebene 1 die führende Ebene, d.h. diese Ebene vererbt die exkludierten bzw. eventuell inkludierten Nachtstunden (wenn Werte in den Balken der Ebene 1 angezeigt werden sollen) an die nachfolgenden Ebenen, d.h. auch auf die Ebene 3.&lt;br /&gt;
&lt;br /&gt;
  &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 5 - Die Energieflußgrafik (Flow Graphic) ===&lt;br /&gt;
[[Datei:Screenshot 2025-02-13 213424.png|right|thumb|400px|Energieflußgrafik mit Verbrauchern in der Nachtdarstellung (Mondphasen) nach Sonnenuntergang]]&lt;br /&gt;
&lt;br /&gt;
Die Energieflußgrafik stellt die registrierten Erzeuger, Batterien, Netzzugänge und Verbraucher strukturiert in mehreren Ebenen dar:&lt;br /&gt;
&lt;br /&gt;
;Ebene 1: In der oberen Ebene befinden sich die PV-Generatoren, Smart-Loader sowie sonstigen Erzeuger &lt;br /&gt;
;Ebene 2: Die mittlere Ebene organisiert den Zugang zum öffentlichen Netz, dem Hausknoten, dem Batterieknoten sowie einem Dummy-Verbraucher (Lampensymbol) für den Energieverbrauch des Hauses der keinem der registrierten Verbraucher zugeordnet werden kann.&lt;br /&gt;
;Ebene 3: In der unteren Ebene werden die registrierten Verbraucher dargestellt. Unterhalb der Verbrauchersymbole werden der aktuelle Verbrauch (bzw. der On/Off-Zustand) sowie darunter die Restlaufzeit (Minuten) des Verbrauchers, sofern er durch das Modul geplant und gestartet wurde. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Die Position und Gestaltung der Grafikelemente ist umfangreich über das Attribut &#039;&#039;&#039;flowGraphicControl&#039;&#039;&#039; und den darin verfügbaren Schlüsseln einstellbar.&lt;br /&gt;
Die Bedeutung der Schlüssel ist in der Online-Hilfe zum Attribut mit einem Beispiel beschrieben. &lt;br /&gt;
&lt;br /&gt;
Darüber hinaus enthalten die Setup-Attribute &#039;&#039;&#039;setupInverterDevXX&#039;&#039;&#039;, &#039;&#039;&#039;setupOtherProducerXX&#039;&#039;&#039; und &#039;&#039;&#039;consumerXX&#039;&#039;&#039; weitere Einstellungsmöglichkeiten um deren Icons und Färbungen in der Flußgrafik individuell einstellen zu können. So sind zum Beispiel die Icons der PV-Inverter und deren Färbung mit dem Schlüssel &#039;&#039;&#039;icon&#039;&#039;&#039; der setupInverterDevXX-Attribute in Abhängigkeit von Tag und Nacht separat definierbar.&lt;br /&gt;
&lt;br /&gt;
In den Verbraucher-Attributen ist mit dem Schlüssel &#039;&#039;&#039;noshow&#039;&#039;&#039; die Sichtbarkeit in der Flußgrafik definierbar.&lt;br /&gt;
&lt;br /&gt;
Generell steuert das Attribut &#039;&#039;&#039;graphicSelect&#039;&#039;&#039; die Ein- bzw. Ausblendung der gesamten Flußgrafik.&lt;br /&gt;
&lt;br /&gt;
== Bestimmung von Sonnenaufgang und Sonnenuntergang ==&lt;br /&gt;
&lt;br /&gt;
Im Modul werden die Zeiten für den Sonnenaufgang und Sonnenuntergang aus alternativen Quellen verwendet. Es gelten dabei die folgenden Prioritäten.&lt;br /&gt;
&lt;br /&gt;
* sind die lokalen Koordinaten &#039;&#039;latitude, longitude, altitude&#039;&#039; im &#039;&#039;global&#039;&#039; Device gesetzt, erfolgt die Berechnung dieser Daten mit dem Parameter &#039;&#039;&#039;HORIZON=-0.833&#039;&#039;&#039; (vgl. CommandRef zu [https://fhem.de/commandref_DE.html#SUNRISE_EL SUNRISE_EL]). &lt;br /&gt;
&lt;br /&gt;
* sollten die lokalen Koordinaten nicht gesetzt sein, werden die Angaben aus dem in Attribut &#039;&#039;setupWeatherDev1&#039;&#039; angegebenen Wetter-Device entnommen.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Der Wert &#039;&#039;−0.833°&#039;&#039; entspricht dem international üblichen Standard für die Berechnung von Sonnenauf‑ und untergangszeiten. Er berücksichtigt sowohl die atmosphärische Refraktion als auch den halben Sonnendurchmesser und beschreibt damit den Moment, in dem der obere Sonnenrand scheinbar den Horizont berührt. Viele astronomische Algorithmen und [https://metacpan.org/release/JFORGET/DateTime-Event-Sunrise-0.0505/view/lib/DateTime/Event/Sunrise.pm Bibliotheken] verwenden diesen Wert als Default, weshalb er auch in SolarForecast eingesetzt wird.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Die Verbrauchsprognose ==&lt;br /&gt;
Im Gegensatz zu der PV Prognose gibt es keine API oder andere externen Datenquellen die für die Verbrauchsprognose herangezogen werden könnten.&lt;br /&gt;
&lt;br /&gt;
Zum Zweck der Verbrauchsermittlung verwendet das Modul die Meterdaten aus dem Attribut &#039;&#039;setupMeterDev&#039;&#039; sowie den weiteren setup-Attributen &#039;&#039;setupInverterDevXX&#039;&#039;. &lt;br /&gt;
Sind auch andere Erzeuger (BHKW, Windanlagen, etc.) bzw. eine Batterie vorhanden, sind ebenfalls die Attribute &#039;&#039;setupOtherProducerXX&#039;&#039; bzw. &#039;&#039;setupBatteryDev&#039;&#039; für die Berechnung des Hausverbrauchs relevant. In diesen Attributen gibt es die Schlüssel &#039;&#039;*total&#039;&#039; die teilweise optional sind. Für die Verbrauchsprognose bilden sie jedoch die Berechnungsgrundlage und sind deswegen für diese Funktionalität wichtig anzugeben.&lt;br /&gt;
&lt;br /&gt;
Die Verbrauchswerte werden stundengenau erfasst und im Datenspeicher &#039;&#039;pvHistory&#039;&#039; für 31 Tage, sowie im Datenspeicher &#039;&#039;pvCircular&#039;&#039; 210 Tage gespeichert. Die beiden Datenspeicher übernehmen unterschiedliche Aufgaben im Rahmen der Prognoseerstellung. Auf Grundlage der gespeicherten und somit historischen Verbrauchswerte erstellt das Modul eine Prognose für die kommende Zeit.&lt;br /&gt;
&lt;br /&gt;
Mit dem Kommando  &lt;br /&gt;
&lt;br /&gt;
    &amp;quot;get ... pvHistory [Tag]&amp;quot; &lt;br /&gt;
&lt;br /&gt;
wird der Inhalt aufgelistet. Die Schlüssel &#039;&#039;confc&#039;&#039;, &#039;&#039;con&#039;&#039; und &#039;&#039;gcon&#039;&#039; zeigen die Verbrauchsprognose, den Verbrauch und den Netzbezug der jeweiligen Stunde des gewählten Tages.&lt;br /&gt;
Die Stunde &amp;quot;99&amp;quot; zeigt die Summen dieser Schlüsssel für den Tag:&lt;br /&gt;
&lt;br /&gt;
      99 =&amp;gt; etotal: , pvfc: 782, pvrl: 0, rad1h: -&lt;br /&gt;
            confc: 14540, con: 13873, gcon: 13873, gfeedin: 0&lt;br /&gt;
            batintotal: , batin: 0, batouttotal: , batout: 0&lt;br /&gt;
            batmaxsoc: -, batsetsoc: -&lt;br /&gt;
            wid: , wcc: , wrp: , pvcorrf: , dayname: So&lt;br /&gt;
            cyclescsm01: 0&lt;br /&gt;
            cyclescsm02: 0&lt;br /&gt;
            cyclescsm03: 0&lt;br /&gt;
            cyclescsm04: 1, hourscsme04: 0.00&lt;br /&gt;
            cyclescsm05: 0&lt;br /&gt;
            cyclescsm06: 0&lt;br /&gt;
            cyclescsm07: 1, hourscsme07: 0.00&lt;br /&gt;
            cyclescsm08: 1, hourscsme08: 0.00&lt;br /&gt;
            cyclescsm09: 1, hourscsme09: 0.03 &lt;br /&gt;
&lt;br /&gt;
In dem Beispiel wurden für den Tag 14540 Wh Verbrauch prognostiziert, 13873 Wh tatsächlich verbraucht und davon 13873 Wh aus dem Netz bezogen. Leider war an diesem Tag keinerlei PV Ertrag vorhanden, was man auch an dem Schlüssel &#039;&#039;pvrl&#039;&#039; erkennen kann.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Den Inhalt des pvCircular-Speichers kann mit dem Befehl  &lt;br /&gt;
&lt;br /&gt;
     &amp;quot;get ... pvCircular [Tag]&amp;quot;&lt;br /&gt;
&lt;br /&gt;
angezeigt werden. Das nachfolgende Beispiel zeigt die Ausgabe für die Tagesstunde 23:&lt;br /&gt;
&lt;br /&gt;
 23 =&amp;gt; pvapifc: 0, pvaifc: -, pvfc: 0, aihit: 0, pvrl: 0&lt;br /&gt;
       batin01: 0, batin02: -, batin03: -&lt;br /&gt;
       batout01: 619, batout02: -, batout03: -&lt;br /&gt;
       confc: 650, gcon: 23, gfeedin: 0, wcc: 40, rr1c: 0.00&lt;br /&gt;
       temp: -6.00, wid: 100, wtxt: Bewölkungsentwicklung nicht beobachtet&lt;br /&gt;
       pprl01: -, pprl02: -, pprl03: -&lt;br /&gt;
       ... &lt;br /&gt;
       con_all =&amp;gt; Sa  @ 617 762&lt;br /&gt;
                  Di  @ 693 645&lt;br /&gt;
                  Mo  @ 620 679&lt;br /&gt;
                  So  @ 669 666 636&lt;br /&gt;
                  Mi  @ 701 626&lt;br /&gt;
                  Do  @ 612 642&lt;br /&gt;
                  Fr  @ 615 606&lt;br /&gt;
&lt;br /&gt;
Der Schüssel &#039;&#039;con_all&#039;&#039; enthält einen Hash aus Arrays die für jeden Wochentag (Mo..So) bis zu 30 Verbrauchswerte (in Wh) für die angezeigte Stunde speichern. Wie hier erkennbar ist, wurden an den drei vergangenen Sonntagen jweils 669 Wh, 666 Wh bzw. 636 Wh in der Stunde 23 (22:00-22:59) verbraucht.&lt;br /&gt;
Der Median aus diesen Werten geht in die Verbrauchsprognose ein. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Hinweis:&#039;&#039;&#039; Die Wochentagsnamen Mo..So sind von den installierten Locales auf Betriebsystemebene abhängig. Ist keine deutsche locale installiert, werden die englischen Wochentagsnamen Mon..Sun verwendet.    &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ausblick (ab V 2.0.0, aktuell in Entwicklung):&#039;&#039;&#039; &amp;lt;br&amp;gt;&lt;br /&gt;
Die Verbrauchsprognose kann zukünftig auf &#039;&#039;&#039;herkömmliche Weise (Legacy)&#039;&#039;&#039; erstellt oder &#039;&#039;&#039;durch eine KI&#039;&#039;&#039; erstellt werden. Es ist auch eine prozentual einstellbare Kombination aus beiden Ergebnissen möglich. Weiter unten werden beide Varianten beschrieben. Bitte beachten, dass beide Technologien ihre spezifischen Attribute zur Einstellung haben.&lt;br /&gt;
&lt;br /&gt;
Die KI-gestützte Prognose verwendet Langzeitdaten, die sich in einem speziellen Datenspeicher befinden und mit&lt;br /&gt;
&lt;br /&gt;
 get &amp;lt;Name&amp;gt; valDecTree aiRawData&lt;br /&gt;
&lt;br /&gt;
eingesehen werden können. Wie lange diese Daten aufbewahrt werden, ist mit dem Attribut&lt;br /&gt;
&lt;br /&gt;
 attr &amp;lt;Name&amp;gt; aiControl-&amp;gt;aiStorageDuration&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
einstellbar. Eine Pflege dieser Daten ist mit dem Befehl&lt;br /&gt;
&lt;br /&gt;
 set &amp;lt;Name&amp;gt; reset aiData ...&lt;br /&gt;
&lt;br /&gt;
möglich.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Wie wird der Verbrauch des Hauses ermittelt und gespeichert? ===&lt;br /&gt;
&lt;br /&gt;
Die Verbrauchsdaten des Hauses werden auf Stundenbasis ermittelt und gespeichert. Das &amp;quot;Haus&amp;quot; steht als Synonym aller im Kontext des Solarforecast Devices zusammengeführten Inverter (setupInverterDevXX), sonstigen Erzeuger (setupOtherProducerXX), Netzanschlüsse (setupMeterDev), Verbraucher (consumerXX) und Batterien (setupBatteryDev).&lt;br /&gt;
&lt;br /&gt;
Alle diese Daten werden durch das Modul mit jedem ausgeführten Zyklus (Attribut plantControl-&amp;gt;cycleInterval) und, wenn ein Event eines als &#039;asynchron&#039; definierten Devices auftritt, gesammelt und daraus permanent ein aktualisierter Verbrauch berechnet. Die Berechnung beschreibt folgende Formel: &lt;br /&gt;
&lt;br /&gt;
 PV-Erzeugung + sonstige Erzeugung - Netzeinspeisung + Netzbezug - Batterieladung + Batterieentladung - Verbrauch = 0&lt;br /&gt;
&lt;br /&gt;
bzw.&lt;br /&gt;
 &lt;br /&gt;
 Verbrauch (Wh) = PV-Erzeugung + sonstige Erzeugung - Netzeinspeisung + Netzbezug - Batterieladung + Batterieentladung&lt;br /&gt;
            con = PV           + PP                 - GridIn          + GridCon   - BatIn          + BatOut&lt;br /&gt;
&lt;br /&gt;
Die PV-Erzeugung beinhaltet die Summe aller durch die angegebnen Inverter (setupInverterDev01 .. setupInverterDevXX) erzeugten Energien. Die &amp;quot;sonstige Erzeugung&amp;quot; summiert sich aus den Werten aller angegebenen Producer (setupOtherProducer01 .. setupOtherProducerXX). Der Verbrauch wird durch die registrierten Geräte consumerXX teilweise direkt erfasst, ist aber grundsätzlich unvollständig da nicht alle Verbrauchswerte im &amp;quot;Haus&amp;quot; gemessen und durch das Modul erfasst werden können.&lt;br /&gt;
&lt;br /&gt;
Das folgende Beispiel zeigt das Ergebnis bei 300 Wh PV-Erzeugung und gleichzeitige 500 Wh Netzbezug:&lt;br /&gt;
&lt;br /&gt;
 Verbrauch = 300 + 0 - 0 + 500 - 0 + 0 = 800 Wh&lt;br /&gt;
&lt;br /&gt;
Bei 1300Wh PV-Erzeugung, 100 Wh Netzeinspeisung sowie 200 Wh Batterieladung ergibt sich der Verbrauch zu:&lt;br /&gt;
&lt;br /&gt;
 Verbrauch = 1300 + 0 - 100 + 0 - 200 + 0 = 1000 Wh&lt;br /&gt;
&lt;br /&gt;
Ohne Erzeugung, aber mit der Energielieferung von 500 Wh aus der Batterie, 800 Wh Netzbezug (mit einem Anteil Batterienachladung aus dem Netz von 200 Wh) sieht die Verbrauchsrechnung wie folgt aus: &lt;br /&gt;
&lt;br /&gt;
 Verbrauch = 0 + 0 - 0 + 800 - 200 + 500 = 1100 Wh&lt;br /&gt;
&lt;br /&gt;
Der so ermittelte Verbrauchsert wird mit jedem Zyklus in der pvHistory im Schlüssel &amp;quot;con&amp;quot; gespeichert. Man kann sich die gespeicherten Werte mit dem Befehl&lt;br /&gt;
&lt;br /&gt;
 get &amp;lt;name&amp;gt; pvHistory [&amp;lt;Tag&amp;gt;]&lt;br /&gt;
&lt;br /&gt;
angezeigt werden. Nachfolgendes Beispiel zeigt einen Datensatz für die Stunde 8 eines Tages:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
      08 =&amp;gt; pvfc: 0, pvrl: 0, pvrlvd: 1, rad1h: -&lt;br /&gt;
            etotali01: 62940734, etotali02: 2928860, etotali03: -&lt;br /&gt;
            pvrl01: 0, pvrl02: 0, pvrl03: -&lt;br /&gt;
            etotalp01: -, etotalp02: -, etotalp03: -&lt;br /&gt;
            pprl01: -, pprl02: -, pprl03: -&lt;br /&gt;
            confc: 630, con: 962, gcons: 962, conprice: 0.2958&lt;br /&gt;
            gfeedin: 0, feedprice: 0.1269&lt;br /&gt;
            DoN: 0, sunaz: 120, sunalt: -3&lt;br /&gt;
            batintotal: 3713815.07742543, batouttotal: 3604468.26993988, batin: 0, batout: 0&lt;br /&gt;
            wid: 161, wcc: 89, rr1c: 0.00, pvcorrf: 0.27/0.00 temp: 7.40, &lt;br /&gt;
            csmt01: 110996.12, csme01: 32.1399999999994, minutescsm01: 30&lt;br /&gt;
            minutescsm02: 0&lt;br /&gt;
            csmt03: 2513.67, csme03: 0, minutescsm03: 0&lt;br /&gt;
            csmt04: 1581293.6, csme04: 136.5, minutescsm04: 60&lt;br /&gt;
            csmt05: 1662.17, csme05: 0, minutescsm05: 0&lt;br /&gt;
            csmt06: 90.53, csme06: 0, minutescsm06: 0&lt;br /&gt;
            csmt07: 39.75, csme07: 0, minutescsm07: 0&lt;br /&gt;
            csmt08: 38050, csme08: 0, minutescsm08: 0&lt;br /&gt;
            csmt09: 131627, csme09: 33.2000000000116, minutescsm09: 39&lt;br /&gt;
            minutescsm10: 0&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Im Schlüssel &amp;quot;con&amp;quot; ist der gespeicherte Verbrauch von 962 Wh ersichtlich.&lt;br /&gt;
&lt;br /&gt;
Zur Laufzeit bekommt man mit dem Attribut &#039;&#039;ctrlDebug=collectData&#039;&#039; einen Einblick in die Verbrauchsermittlung bei jedem Zyklus und kann so die Eingangswerte und das daraus resultierende Ergebnis im FHEM Log nachverfolgen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
2024.11.28 11:38:20.080 1: SolCast DEBUG&amp;gt; EnergyConsumption input -&amp;gt; PV: 96, PP: 0, GridIn: 0, GridCon: 582, BatIn: 5, BatOut: 0&lt;br /&gt;
2024.11.28 11:38:20.081 1: SolCast DEBUG&amp;gt; EnergyConsumption result -&amp;gt; 673 Wh&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Die Legacy-Verbrauchsprognose einstellen ===&lt;br /&gt;
==== Wie wird die Legacy-Verbrauchsprognose erstellt? ====&lt;br /&gt;
&lt;br /&gt;
Aus den gespeicherten con-Werten (Verbrauchsdaten) der pvCircular (Schlüssel con_all) sowie Einträgen der pvHistory wird der Median der historischen Tage ermittelt und für den kommenden Tag bzw. die kommen Stunden zur Prognose angewendet. Dabei geht per default jeder verfügbare historische Tag gleichberechtigt in die Prognose ein. Mit dem Attribut &#039;&#039;&#039;plantControl-&amp;gt;consForecastIdentWeekdays=1&#039;&#039;&#039; kann festgelegt werden, dass nur gleiche Wochentage (Mo..So) für die Prognose verwendet werden. Hierbei spielt die Vermutung, dass das Verbrauchsverhalten eine gewisse Korrelation zum Wochentag hat, eine Rolle. &lt;br /&gt;
&lt;br /&gt;
Verwendet man die Einstellung consForecastIdentWeekdays=1, hat es sich als vorteilhaft erwiesen, das Attribut &#039;&#039;&#039;plantControl-&amp;gt;consForecastLastDays&#039;&#039;&#039; auf einen niedrigeren Wert als den Default zu setzen. Eine Einstellung auf &#039;8&#039; zum Beispiel verwendet den Median der gleichen Wochentage der vergangenenen 8 Wochen. Dadurch wird nicht zu weit in die Vergangenheit verzweigt und so eine langsame Veränderung des Verbrauchsmedian, zum Beispiel hervorgerufen durch den Übergang vom Winter in das Frühjahr hinein, abgebildet.&lt;br /&gt;
&lt;br /&gt;
Weiteren Einfluß auf die Verbrauchsprognose kann mit dem Schlüssel &#039;&#039;&#039;exconfc&#039;&#039;&#039; im Consumerattribut genommen werden. Insbesondere bei der unregelmäßigen Nutzung von Verbrauchern mit hoher Leistung kann die Projektion der aufgezeichneten historischen Werte in die Prognose zu unverhältnismäßig hohen Verbrauchswerwartunden führen. &lt;br /&gt;
&lt;br /&gt;
Möglicherweise führt in Fällen die Verwendung eine der nachfolgenden Optionen zu einem besseren Ergebnis:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;1 -&#039;&#039;&#039; die allgemeine Verbrauchsprognose wird um die gespeicherten Energieverbrauchsanteile reduziert.&lt;br /&gt;
* &#039;&#039;&#039;2 -&#039;&#039;&#039; wie bei &#039;1&#039;, jedoch gehen die Planungsdaten des Verbrauchers bei der Prognose der kommenden Stunden wieder mit ein.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Wird &#039;&#039;&#039;exconfc&#039;&#039;&#039; in einem der Consumer verwendet, sollte&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;plantControl-&amp;gt;consForecastIdentWeekdays=1&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;plantControl-&amp;gt;consForecastLastDays=4&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
eingestellt werden. Hintergrund ist, dass die Verbrauchsanteile der jeweiligen Verbraucher nur maximal 31 Tage in der pvHistory verfügbar sind. Für längere Zeiträume stehen nur Gesamtsummen des Verbrauchs in der pvCircular zu Verfügung.  &lt;br /&gt;
&lt;br /&gt;
Mit &#039;&#039;&#039;exconfc=1&#039;&#039;&#039; werden die in der Historie aufgezeichneten Verbrauchsbestandteile aus einer Prognose herausgerechnet. Konkret werden die in der pvHistory aufgezeichneten Verbrauchsbestandteile für die relevante Stunde ermittelt und daraus ein Durchschnitt gebildet der vom Prognosewert subtrahiert wird. Mit dem Attributwert plantControl-&amp;gt;consForecastIdentWeekdays=1 werden wiederum nur identische Wochentage (Mo .. So) zusammengefasst.&lt;br /&gt;
&lt;br /&gt;
Mit &#039;&#039;&#039;exconfc=2&#039;&#039;&#039; erfolgt ebenso die Eliminierung des Verbrauchsanteils, es wird jedoch auf die Planungsdaten für diesen Verbraucher zugegriffen und die geplanten Energieanteile wiederum der Verbrauchsprognose für die kommenden Stunden hinzugerechnet.&lt;br /&gt;
&lt;br /&gt;
In beiden Anwendungsfällen ist es notwendig, dass dem Modul durch die Angabe entsprechender Readings der Energieverbrauch im Setup des Consumers bekannt gemacht wird damit diese Energieanteile registriert werden können. Allgemein funktioniert die Verbrauchsprognose gut wenn Verbrauchsprozesse regelmäßig in einem wiederkehrenden Zeitraster ablaufen, der Verbrauch durch persönliche Rituale bestimmt ist. So zum Beispiel könnte am Dienstag und Samstag gewöhnlich die Waschmaschine laufen, das E-Auto täglich aufgeladen werden und gebügelt wird oft am Sonntag Nachmittag.&lt;br /&gt;
&lt;br /&gt;
Unregelmäßige Nutzung, vor allem vom Großverbrauchern, lässt sich nur sehr schwer und ungenügend auf zukünftige Verbrauchsprognosen projizieren.&lt;br /&gt;
&lt;br /&gt;
Das Ergebnis der Prognose wird im Kopf der Grafik in der Zeile &#039;&#039;Verbrauch&#039;&#039; sowie in diversen Readings wie &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;RestOfDayConsumptionForecast&#039;&#039;&#039; (Verbrauchsprognose für den Rest des Tages)&lt;br /&gt;
* &#039;&#039;&#039;NextHours_Sum04_ConsumptionForecast&#039;&#039;&#039; (Verbrauchsprognose für die nächsten 4 Stunden)&lt;br /&gt;
* &#039;&#039;&#039;Tomorrow_ConsumptionForecast&#039;&#039;&#039; (Verbrauchsprognose für den kommenden Tag)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
angezeigt.&lt;br /&gt;
&lt;br /&gt;
Weitere Aggregationen kann man sich über das Attribut &#039;&#039;&#039;ctrlStatisticReadings&#039;&#039;&#039; bei Bedarf zuschalten:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;conForecastTillNextSunrise&#039;&#039;&#039; - Verbrauchsprognose von aktueller Stunde bis zum kommenden Sonnenaufgang&lt;br /&gt;
* &#039;&#039;&#039;todayConForecastTillSunset&#039;&#039;&#039; - Verbrauchsprognose von aktueller Stunde bis Stunde vor Sonnenuntergang&lt;br /&gt;
* &#039;&#039;&#039;todayConsumptionForecast&#039;&#039;&#039; - Verbrauchsprognose aufgesplittet pro Stunde des aktuellen Tages (01-24) &lt;br /&gt;
 &lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
==== Debugging der Legacy-Verbrauchsprognose ====&lt;br /&gt;
&lt;br /&gt;
Um sich einen Einblick über die Erstellung der Verbrauchsprognose zu verschaffen, bietet sich die Einschaltung von:&lt;br /&gt;
&lt;br /&gt;
 attr &amp;lt;Name&amp;gt; ctrlDebug consumption_long&lt;br /&gt;
&lt;br /&gt;
an. Mit diesem gesetzten Attribut wird die Erstellung der Prognose im Log protokolliert:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
2025.02.27 19:52:00.063 1: SolCast DEBUG&amp;gt; ################### Start Consumption forecast ###################&lt;br /&gt;
2025.02.27 19:52:00.063 1: SolCast DEBUG&amp;gt; Basics - installed locale: de_DE.UTF-8, used scheme: DE&lt;br /&gt;
2025.02.27 19:52:00.064 1: SolCast DEBUG&amp;gt; process Today dayname: Do, Tomorrow dayname: Fr&lt;br /&gt;
2025.02.27 19:52:00.065 1: SolCast DEBUG&amp;gt; consumer &#039;03&#039; register for exclude day 13, hod: 01 - 74.43 Wh&lt;br /&gt;
2025.02.27 19:52:00.065 1: SolCast DEBUG&amp;gt; consumer &#039;03&#039; register for exclude day 13, hod: 10 - 290.77 Wh&lt;br /&gt;
2025.02.27 19:52:00.065 1: SolCast DEBUG&amp;gt; consumer &#039;03&#039; register for exclude day 13, hod: 24 - 660.70 Wh&lt;br /&gt;
2025.02.27 19:52:00.066 1: SolCast DEBUG&amp;gt; consumer &#039;03&#039; register for exclude day 20, hod: 07 - 459.93 Wh&lt;br /&gt;
2025.02.27 19:52:00.066 1: SolCast DEBUG&amp;gt; consumer &#039;07&#039; register for exclude day 20, hod: 02 - 0.33 Wh&lt;br /&gt;
2025.02.27 19:52:00.067 1: SolCast DEBUG&amp;gt; consumer &#039;03&#039; register for exclude day 30, hod: 08 - 264.78 Wh&lt;br /&gt;
2025.02.27 19:52:00.067 1: SolCast DEBUG&amp;gt; ################### Consumption forecast for the next Hours (new median) ###################&lt;br /&gt;
2025.02.27 19:52:00.067 1: SolCast DEBUG&amp;gt; excl. hist 74 Wh for Hour 01, Considered value numbers: 1&lt;br /&gt;
2025.02.27 19:52:00.067 1: SolCast DEBUG&amp;gt; estimated cons of Hour 01: 436 Wh, Considered value numbers: 4&lt;br /&gt;
2025.02.27 19:52:00.068 1: SolCast DEBUG&amp;gt; excl. hist 0 Wh for Hour 02, Considered value numbers: 1&lt;br /&gt;
2025.02.27 19:52:00.068 1: SolCast DEBUG&amp;gt; estimated cons of Hour 02: 428 Wh, Considered value numbers: 4&lt;br /&gt;
2025.02.27 19:52:00.068 1: SolCast DEBUG&amp;gt; estimated cons of Hour 03: 458 Wh, Considered value numbers: 4&lt;br /&gt;
2025.02.27 19:52:00.068 1: SolCast DEBUG&amp;gt; estimated cons of Hour 04: 503 Wh, Considered value numbers: 4&lt;br /&gt;
2025.02.27 19:52:00.069 1: SolCast DEBUG&amp;gt; estimated cons of Hour 05: 489 Wh, Considered value numbers: 4&lt;br /&gt;
2025.02.27 19:52:00.069 1: SolCast DEBUG&amp;gt; estimated cons of Hour 06: 482 Wh, Considered value numbers: 4&lt;br /&gt;
2025.02.27 19:52:00.069 1: SolCast DEBUG&amp;gt; excl. hist 460 Wh for Hour 07, Considered value numbers: 1&lt;br /&gt;
2025.02.27 19:52:00.069 1: SolCast DEBUG&amp;gt; estimated cons of Hour 07: 274 Wh, Considered value numbers: 4&lt;br /&gt;
2025.02.27 19:52:00.070 1: SolCast DEBUG&amp;gt; excl. hist 265 Wh for Hour 08, Considered value numbers: 1&lt;br /&gt;
2025.02.27 19:52:00.070 1: SolCast DEBUG&amp;gt; estimated cons of Hour 08: 441 Wh, Considered value numbers: 4&lt;br /&gt;
2025.02.27 19:52:00.070 1: SolCast DEBUG&amp;gt; estimated cons of Hour 09: 558 Wh, Considered value numbers: 4&lt;br /&gt;
2025.02.27 19:52:00.070 1: SolCast DEBUG&amp;gt; excl. hist 291 Wh for Hour 10, Considered value numbers: 1&lt;br /&gt;
2025.02.27 19:52:00.071 1: SolCast DEBUG&amp;gt; estimated cons of Hour 10: 443 Wh, Considered value numbers: 4&lt;br /&gt;
2025.02.27 19:52:00.071 1: SolCast DEBUG&amp;gt; estimated cons of Hour 11: 881 Wh, Considered value numbers: 4&lt;br /&gt;
2025.02.27 19:52:00.071 1: SolCast DEBUG&amp;gt; estimated cons of Hour 12: 794 Wh, Considered value numbers: 4&lt;br /&gt;
2025.02.27 19:52:00.072 1: SolCast DEBUG&amp;gt; estimated cons of Hour 13: 840 Wh, Considered value numbers: 4&lt;br /&gt;
2025.02.27 19:52:00.072 1: SolCast DEBUG&amp;gt; estimated cons of Hour 14: 660 Wh, Considered value numbers: 4&lt;br /&gt;
2025.02.27 19:52:00.072 1: SolCast DEBUG&amp;gt; estimated cons of Hour 15: 812 Wh, Considered value numbers: 4&lt;br /&gt;
2025.02.27 19:52:00.072 1: SolCast DEBUG&amp;gt; estimated cons of Hour 16: 752 Wh, Considered value numbers: 4&lt;br /&gt;
2025.02.27 19:52:00.073 1: SolCast DEBUG&amp;gt; estimated cons of Hour 17: 597 Wh, Considered value numbers: 4&lt;br /&gt;
2025.02.27 19:52:00.073 1: SolCast DEBUG&amp;gt; estimated cons of Hour 18: 652 Wh, Considered value numbers: 4&lt;br /&gt;
2025.02.27 19:52:00.073 1: SolCast DEBUG&amp;gt; estimated cons of Hour 19: 634 Wh, Considered value numbers: 4&lt;br /&gt;
2025.02.27 19:52:00.073 1: SolCast DEBUG&amp;gt; estimated cons of Hour 20: 628 Wh, Considered value numbers: 3&lt;br /&gt;
2025.02.27 19:52:00.074 1: SolCast DEBUG&amp;gt; estimated cons of Hour 21: 713 Wh, Considered value numbers: 3&lt;br /&gt;
2025.02.27 19:52:00.074 1: SolCast DEBUG&amp;gt; estimated cons of Hour 22: 596 Wh, Considered value numbers: 3&lt;br /&gt;
2025.02.27 19:52:00.074 1: SolCast DEBUG&amp;gt; estimated cons of Hour 23: 629 Wh, Considered value numbers: 3&lt;br /&gt;
2025.02.27 19:52:00.074 1: SolCast DEBUG&amp;gt; excl. hist 661 Wh for Hour 24, Considered value numbers: 1&lt;br /&gt;
2025.02.27 19:52:00.075 1: SolCast DEBUG&amp;gt; estimated cons of Hour 24: 660 Wh, Considered value numbers: 3&lt;br /&gt;
2025.02.27 19:52:00.075 1: SolCast DEBUG&amp;gt; ################### Consumption forecast for the next day (new median) ###################&lt;br /&gt;
2025.02.27 19:52:00.075 1: SolCast DEBUG&amp;gt; estimated cons Tomorrow: 16848 Wh, Individual hourly values considered: 72, exclude: 0 Wh (avg of 0 entities)&lt;br /&gt;
2025.02.27 19:52:00.075 1: SolCast DEBUG&amp;gt; ################### Store Consumption forecast values (new median) ###################&lt;br /&gt;
2025.02.27 19:52:00.076 1: SolCast DEBUG&amp;gt; store &#039;NextHour00&#039; hod &#039;20&#039; confc: 628, confcEx: 628&lt;br /&gt;
2025.02.27 19:52:00.076 1: SolCast DEBUG&amp;gt; store circular/history hod &#039;20&#039; confc: 628&lt;br /&gt;
2025.02.27 19:52:00.076 1: SolCast DEBUG&amp;gt; store &#039;NextHour01&#039; hod &#039;21&#039; confc: 713, confcEx: 713&lt;br /&gt;
2025.02.27 19:52:00.077 1: SolCast DEBUG&amp;gt; store circular/history hod &#039;21&#039; confc: 713&lt;br /&gt;
2025.02.27 19:52:00.077 1: SolCast DEBUG&amp;gt; store &#039;NextHour02&#039; hod &#039;22&#039; confc: 596, confcEx: 596&lt;br /&gt;
2025.02.27 19:52:00.077 1: SolCast DEBUG&amp;gt; store circular/history hod &#039;22&#039; confc: 596&lt;br /&gt;
2025.02.27 19:52:00.077 1: SolCast DEBUG&amp;gt; store &#039;NextHour03&#039; hod &#039;23&#039; confc: 629, confcEx: 629&lt;br /&gt;
2025.02.27 19:52:00.078 1: SolCast DEBUG&amp;gt; store circular/history hod &#039;23&#039; confc: 629&lt;br /&gt;
2025.02.27 19:52:00.078 1: SolCast DEBUG&amp;gt; store &#039;NextHour04&#039; hod &#039;24&#039; confc: 660, confcEx: 660&lt;br /&gt;
2025.02.27 19:52:00.078 1: SolCast DEBUG&amp;gt; store circular/history hod &#039;24&#039; confc: 660&lt;br /&gt;
2025.02.27 19:52:00.079 1: SolCast DEBUG&amp;gt; store &#039;NextHour05&#039; hod &#039;01&#039; confc: 436, confcEx: 436&lt;br /&gt;
2025.02.27 19:52:00.079 1: SolCast DEBUG&amp;gt; store &#039;NextHour06&#039; hod &#039;02&#039; confc: 428, confcEx: 428&lt;br /&gt;
2025.02.27 19:52:00.079 1: SolCast DEBUG&amp;gt; store &#039;NextHour07&#039; hod &#039;03&#039; confc: 458, confcEx: 458&lt;br /&gt;
2025.02.27 19:52:00.079 1: SolCast DEBUG&amp;gt; store &#039;NextHour08&#039; hod &#039;04&#039; confc: 503, confcEx: 503&lt;br /&gt;
2025.02.27 19:52:00.080 1: SolCast DEBUG&amp;gt; store &#039;NextHour09&#039; hod &#039;05&#039; confc: 489, confcEx: 489&lt;br /&gt;
2025.02.27 19:52:00.080 1: SolCast DEBUG&amp;gt; store &#039;NextHour10&#039; hod &#039;06&#039; confc: 482, confcEx: 482&lt;br /&gt;
2025.02.27 19:52:00.080 1: SolCast DEBUG&amp;gt; store &#039;NextHour11&#039; hod &#039;07&#039; confc: 274, confcEx: 274&lt;br /&gt;
2025.02.27 19:52:00.081 1: SolCast DEBUG&amp;gt; store &#039;NextHour12&#039; hod &#039;08&#039; confc: 441, confcEx: 441&lt;br /&gt;
2025.02.27 19:52:00.081 1: SolCast DEBUG&amp;gt; store &#039;NextHour13&#039; hod &#039;09&#039; confc: 558, confcEx: 558&lt;br /&gt;
2025.02.27 19:52:00.081 1: SolCast DEBUG&amp;gt; store &#039;NextHour14&#039; hod &#039;10&#039; confc: 443, confcEx: 443&lt;br /&gt;
2025.02.27 19:52:00.081 1: SolCast DEBUG&amp;gt; store &#039;NextHour15&#039; hod &#039;11&#039; confc: 881, confcEx: 881&lt;br /&gt;
2025.02.27 19:52:00.082 1: SolCast DEBUG&amp;gt; store &#039;NextHour16&#039; hod &#039;12&#039; confc: 794, confcEx: 794&lt;br /&gt;
2025.02.27 19:52:00.082 1: SolCast DEBUG&amp;gt; store &#039;NextHour17&#039; hod &#039;13&#039; confc: 840, confcEx: 840&lt;br /&gt;
2025.02.27 19:52:00.082 1: SolCast DEBUG&amp;gt; store &#039;NextHour18&#039; hod &#039;14&#039; confc: 660, confcEx: 660&lt;br /&gt;
2025.02.27 19:52:00.083 1: SolCast DEBUG&amp;gt; store &#039;NextHour19&#039; hod &#039;15&#039; confc: 812, confcEx: 812&lt;br /&gt;
2025.02.27 19:52:00.083 1: SolCast DEBUG&amp;gt; store &#039;NextHour20&#039; hod &#039;16&#039; confc: 752, confcEx: 752&lt;br /&gt;
2025.02.27 19:52:00.083 1: SolCast DEBUG&amp;gt; store &#039;NextHour21&#039; hod &#039;17&#039; confc: 597, confcEx: 597&lt;br /&gt;
2025.02.27 19:52:00.083 1: SolCast DEBUG&amp;gt; store &#039;NextHour22&#039; hod &#039;18&#039; confc: 652, confcEx: 652&lt;br /&gt;
2025.02.27 19:52:00.084 1: SolCast DEBUG&amp;gt; store &#039;NextHour23&#039; hod &#039;19&#039; confc: 634, confcEx: 634&lt;br /&gt;
2025.02.27 19:52:00.084 1: SolCast DEBUG&amp;gt; store &#039;NextHour24&#039; hod &#039;20&#039; confc: 628, confcEx: 628&lt;br /&gt;
2025.02.27 19:52:00.084 1: SolCast DEBUG&amp;gt; store &#039;NextHour25&#039; hod &#039;21&#039; confc: 713, confcEx: 713&lt;br /&gt;
2025.02.27 19:52:00.084 1: SolCast DEBUG&amp;gt; store &#039;NextHour26&#039; hod &#039;22&#039; confc: 596, confcEx: 596&lt;br /&gt;
2025.02.27 19:52:00.085 1: SolCast DEBUG&amp;gt; store &#039;NextHour27&#039; hod &#039;23&#039; confc: 629, confcEx: 629&lt;br /&gt;
2025.02.27 19:52:00.085 1: SolCast DEBUG&amp;gt; store &#039;NextHour28&#039; hod &#039;24&#039; confc: 660, confcEx: 660&lt;br /&gt;
2025.02.27 19:52:00.086 1: SolCast DEBUG&amp;gt; consumption calculated - day: 27, hod: 20, con: 574 Wh&lt;br /&gt;
2025.02.27 19:52:00.086 1: SolCast DEBUG&amp;gt; write pvCircular consumption - hod: 99, todayConsumption: 11210 Wh&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Die Ausgabe startet mit &#039;&#039;&amp;quot;Start Consumption forecast&#039;&#039;&amp;quot;, es werden die zu exkludierenden historischen Daten für jeden Consumer bzgl. jedem Tag (day) und Stunde des Tages (hod) getrennt registriert. Diese Werte werden durch die gespeicherte Einträge in der pvHistory (max. 31 historische Tage) geliefert. &lt;br /&gt;
&lt;br /&gt;
Im nächsten Abschnitt &#039;&#039;&amp;quot;Consumption forecast for the next Hours (new median)&amp;quot;&#039;&#039; werden die Prognosen für die nächsten Stunden kalkuliert und die zuvor registrierten Excludes angewendet. Die Prognose für den kommenden Tag ist im Abschnitt &#039;&#039;&amp;quot;Consumption forecast for the next day (new median)&amp;quot;&#039;&#039; protokolliert. Die Prognose für den kommenden Tag ist nicht einfach die Summe der Prognose der nächsten Stunden, denn in den kommenden Stunden werden auch die verfügbaren Planungen der Consumer einbezogen. Für den kommenden Tag können diese Werte unter Umständen nicht einbezogen werden da sie zum Zeitpunkt X noch nicht verfügbar sind.&lt;br /&gt;
&lt;br /&gt;
Im Abschnitt &#039;&#039;&amp;quot;Store Consumption forecast values (new median)&amp;quot;&#039;&#039; erscheinen die effektiv gespeicherten Werte. Diese sind die Grandlage für die grafische Darstellung sowie weiteren Kalkulationen.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Wie kann eine fehlerhafte Legacy-Verbrauchsprognose korrigiert werden? ====&lt;br /&gt;
Wie im vorherigen Abschnitt beschrieben, wird die Prognose aus den gespeicherten &amp;quot;con&amp;quot;-Werten der vergangenen Tage (bzw. Stunden) abgeleitet. &lt;br /&gt;
Grundsätzlich muß zunächst sichergestellt werden, dass die auszulesenden Readings der angegebenen Energiezähler sowie deren Einheiten! korrekt eingestellt sind. Das gilt ebenfalls für die registrierten Verbraucher im Modul.&lt;br /&gt;
&lt;br /&gt;
Für einen ersten Überblick bietet sich das Debug Attribut an zu setzen:&lt;br /&gt;
&lt;br /&gt;
 attr &amp;lt;Name&amp;gt; ctrlDebug consumption_long&lt;br /&gt;
&lt;br /&gt;
Beispielausgabe des Debug:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
2024.09.21 13:28:27.041 1: SolCast DEBUG&amp;gt; ################### Consumption forecast for the next day ###################&lt;br /&gt;
2024.09.21 13:28:27.042 1: SolCast DEBUG&amp;gt; History Consumption day &amp;gt;01&amp;lt; considering possible exclusions: 14082&lt;br /&gt;
2024.09.21 13:28:27.042 1: SolCast DEBUG&amp;gt; History Consumption day &amp;gt;08&amp;lt; considering possible exclusions: 16446&lt;br /&gt;
2024.09.21 13:28:27.042 1: SolCast DEBUG&amp;gt; History Consumption day &amp;gt;15&amp;lt; considering possible exclusions: 14568&lt;br /&gt;
2024.09.21 13:28:27.043 1: SolCast DEBUG&amp;gt; History Consumption day &amp;gt;25&amp;lt; considering possible exclusions: 15522&lt;br /&gt;
2024.09.21 13:28:27.043 1: SolCast DEBUG&amp;gt; estimated Consumption for tomorrow: 15154, days for avg: 4&lt;br /&gt;
2024.09.21 13:28:27.043 1: SolCast DEBUG&amp;gt; ################### Consumption forecast for the next hours ###################&lt;br /&gt;
2024.09.21 13:28:27.044 1: SolCast DEBUG&amp;gt;     historical Consumption added for Sa -&amp;gt; date: 07, hod: 14 -&amp;gt; 850 Wh&lt;br /&gt;
2024.09.21 13:28:27.044 1: SolCast DEBUG&amp;gt;     historical Consumption added for Sa -&amp;gt; date: 14, hod: 14 -&amp;gt; 643 Wh&lt;br /&gt;
2024.09.21 13:28:27.044 1: SolCast DEBUG&amp;gt;     historical Consumption added for Sa -&amp;gt; date: 24, hod: 14 -&amp;gt; 590 Wh&lt;br /&gt;
2024.09.21 13:28:27.045 1: SolCast DEBUG&amp;gt;     historical Consumption added for Sa -&amp;gt; date: 31, hod: 14 -&amp;gt; 522 Wh&lt;br /&gt;
2024.09.21 13:28:27.045 1: SolCast DEBUG&amp;gt; estimated Consumption for Sa -&amp;gt; starttime: 2024-09-21 13:00:00, confc: 651, days for avg: 4, hist. consumption registered consumers: 560.97&lt;br /&gt;
2024.09.21 13:28:27.046 1: SolCast DEBUG&amp;gt;     historical Consumption added for Sa -&amp;gt; date: 07, hod: 15 -&amp;gt; 720 Wh&lt;br /&gt;
2024.09.21 13:28:27.046 1: SolCast DEBUG&amp;gt;     historical Consumption added for Sa -&amp;gt; date: 14, hod: 15 -&amp;gt; 743 Wh&lt;br /&gt;
2024.09.21 13:28:27.046 1: SolCast DEBUG&amp;gt;     historical Consumption added for Sa -&amp;gt; date: 24, hod: 15 -&amp;gt; 897 Wh&lt;br /&gt;
2024.09.21 13:28:27.047 1: SolCast DEBUG&amp;gt;     historical Consumption added for Sa -&amp;gt; date: 31, hod: 15 -&amp;gt; 557 Wh&lt;br /&gt;
2024.09.21 13:28:27.047 1: SolCast DEBUG&amp;gt; estimated Consumption for Sa -&amp;gt; starttime: 2024-09-21 14:00:00, confc: 729, days for avg: 4, hist. consumption registered consumers: 620.97&lt;br /&gt;
2024.09.21 13:28:27.047 1: SolCast DEBUG&amp;gt;     historical Consumption added for Sa -&amp;gt; date: 07, hod: 16 -&amp;gt; 676 Wh&lt;br /&gt;
2024.09.21 13:28:27.048 1: SolCast DEBUG&amp;gt;     historical Consumption added for Sa -&amp;gt; date: 14, hod: 16 -&amp;gt; 790 Wh&lt;br /&gt;
2024.09.21 13:28:27.048 1: SolCast DEBUG&amp;gt;     historical Consumption added for Sa -&amp;gt; date: 24, hod: 16 -&amp;gt; 785 Wh&lt;br /&gt;
2024.09.21 13:28:27.048 1: SolCast DEBUG&amp;gt;     historical Consumption added for Sa -&amp;gt; date: 31, hod: 16 -&amp;gt; 581 Wh&lt;br /&gt;
2024.09.21 13:28:27.049 1: SolCast DEBUG&amp;gt; estimated Consumption for Sa -&amp;gt; starttime: 2024-09-21 15:00:00, confc: 708, days for avg: 4, hist. consumption registered consumers: 647.29&lt;br /&gt;
2024.09.21 13:28:27.049 1: SolCast DEBUG&amp;gt;     historical Consumption added for Sa -&amp;gt; date: 07, hod: 17 -&amp;gt; 622 Wh&lt;br /&gt;
2024.09.21 13:28:27.050 1: SolCast DEBUG&amp;gt;     historical Consumption added for Sa -&amp;gt; date: 14, hod: 17 -&amp;gt; 639 Wh&lt;br /&gt;
2024.09.21 13:28:27.050 1: SolCast DEBUG&amp;gt;     historical Consumption added for Sa -&amp;gt; date: 24, hod: 17 -&amp;gt; 646 Wh&lt;br /&gt;
2024.09.21 13:28:27.050 1: SolCast DEBUG&amp;gt;     historical Consumption added for Sa -&amp;gt; date: 31, hod: 17 -&amp;gt; 572 Wh&lt;br /&gt;
2024.09.21 13:28:27.051 1: SolCast DEBUG&amp;gt; estimated Consumption for Sa -&amp;gt; starttime: 2024-09-21 16:00:00, confc: 619, days for avg: 4, hist. consumption registered consumers: 618.30&lt;br /&gt;
2024.09.21 13:28:27.051 1: SolCast DEBUG&amp;gt;     historical Consumption added for Sa -&amp;gt; date: 07, hod: 18 -&amp;gt; 596 Wh&lt;br /&gt;
2024.09.21 13:28:27.051 1: SolCast DEBUG&amp;gt;     historical Consumption added for Sa -&amp;gt; date: 14, hod: 18 -&amp;gt; 614 Wh&lt;br /&gt;
2024.09.21 13:28:27.052 1: SolCast DEBUG&amp;gt;     historical Consumption added for Sa -&amp;gt; date: 24, hod: 18 -&amp;gt; 580 Wh&lt;br /&gt;
2024.09.21 13:28:27.052 1: SolCast DEBUG&amp;gt;     historical Consumption added for Sa -&amp;gt; date: 31, hod: 18 -&amp;gt; 480 Wh&lt;br /&gt;
2024.09.21 13:28:27.052 1: SolCast DEBUG&amp;gt; estimated Consumption for Sa -&amp;gt; starttime: 2024-09-21 17:00:00, confc: 567, days for avg: 4, hist. consumption registered consumers: 583.41&lt;br /&gt;
2024.09.21 13:28:27.053 1: SolCast DEBUG&amp;gt;     historical Consumption added for Sa -&amp;gt; date: 07, hod: 19 -&amp;gt; 615 Wh&lt;br /&gt;
2024.09.21 13:28:27.053 1: SolCast DEBUG&amp;gt;     historical Consumption added for Sa -&amp;gt; date: 14, hod: 19 -&amp;gt; 1659 Wh&lt;br /&gt;
2024.09.21 13:28:27.053 1: SolCast DEBUG&amp;gt;     historical Consumption added for Sa -&amp;gt; date: 24, hod: 19 -&amp;gt; 597 Wh&lt;br /&gt;
2024.09.21 13:28:27.054 1: SolCast DEBUG&amp;gt;     historical Consumption added for Sa -&amp;gt; date: 31, hod: 19 -&amp;gt; 618 Wh&lt;br /&gt;
2024.09.21 13:28:27.054 1: SolCast DEBUG&amp;gt; estimated Consumption for Sa -&amp;gt; starttime: 2024-09-21 18:00:00, confc: 872, days for avg: 4, hist. consumption registered consumers: 636.97&lt;br /&gt;
2024.09.21 13:28:27.055 1: SolCast DEBUG&amp;gt;     historical Consumption added for Sa -&amp;gt; date: 07, hod: 20 -&amp;gt; 611 Wh&lt;br /&gt;
2024.09.21 13:28:27.055 1: SolCast DEBUG&amp;gt;     historical Consumption added for Sa -&amp;gt; date: 14, hod: 20 -&amp;gt; 1124 Wh&lt;br /&gt;
2024.09.21 13:28:27.055 1: SolCast DEBUG&amp;gt;     historical Consumption added for Sa -&amp;gt; date: 24, hod: 20 -&amp;gt; 546 Wh&lt;br /&gt;
2024.09.21 13:28:27.056 1: SolCast DEBUG&amp;gt;     historical Consumption added for Sa -&amp;gt; date: 31, hod: 20 -&amp;gt; 479 Wh&lt;br /&gt;
2024.09.21 13:28:27.056 1: SolCast DEBUG&amp;gt; estimated Consumption for Sa -&amp;gt; starttime: 2024-09-21 19:00:00, confc: 690, days for avg: 4, hist. consumption registered consumers: 578.87&lt;br /&gt;
2024.09.21 13:28:27.056 1: SolCast DEBUG&amp;gt;     historical Consumption added for Sa -&amp;gt; date: 07, hod: 21 -&amp;gt; 1474 Wh&lt;br /&gt;
2024.09.21 13:28:27.057 1: SolCast DEBUG&amp;gt;     historical Consumption added for Sa -&amp;gt; date: 14, hod: 21 -&amp;gt; 596 Wh&lt;br /&gt;
2024.09.21 13:28:27.057 1: SolCast DEBUG&amp;gt;     historical Consumption added for Sa -&amp;gt; date: 24, hod: 21 -&amp;gt; 619 Wh&lt;br /&gt;
2024.09.21 13:28:27.057 1: SolCast DEBUG&amp;gt;     historical Consumption added for Sa -&amp;gt; date: 31, hod: 21 -&amp;gt; 433 Wh&lt;br /&gt;
2024.09.21 13:28:27.058 1: SolCast DEBUG&amp;gt; estimated Consumption for Sa -&amp;gt; starttime: 2024-09-21 20:00:00, confc: 780, days for avg: 4, hist. consumption registered consumers: 584.96&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Im ersten Teil wird die erwartete Consumption für den nächsten Tag unter Berücksichtigung möglicher Ausschlüsse von Verbrauchern ausgegeben. Ausschlüsse von Verbrauchern ergeben sich wenn in dem Verbraucherattribut der Schlüssel &#039;&#039;&#039;exconfc&#039;&#039;&#039; gesetzt ist. Er besagt, dass die Verbrauchswerte des Verbrauchers in eine zukünftige Prognose nicht eingehen sollen. Zum Beispiel wenn ein Verbraucher mit sehr hohen Verbrauchswerten nur sporadisch eingesetzt wird und dadurch die Prognose stark verfälschen würde. Es ist auch zu sehen, dass bedingt durch das Attribut &#039;&#039;&#039;affectConsForecastIdentWeekdays = 1&#039;&#039;&#039; nur die historischen Tage 01, 08, 15 und 25 berücksichtigt werden.&lt;br /&gt;
&lt;br /&gt;
Im zweiten Teil wird die berechnete Erwartung für die nächsten Stunden ausgegeben. &lt;br /&gt;
Zur Beschreibung der Fehlersuche gehen wir davon aus, dass die Prognose von 780 Wh am 2024-09-21 20:00:00 zu hoch erscheint weil am Tag &amp;quot;07&amp;quot; ein Wert von 1474 Wh gespeichert ist:&lt;br /&gt;
&lt;br /&gt;
 SolCast DEBUG&amp;gt; estimated Consumption for Sa -&amp;gt; starttime: 2024-09-21 20:00:00, confc: 780, days for avg: 4, hist. consumption registered consumers: 584.96&lt;br /&gt;
&lt;br /&gt;
Im nächsten Schritt geben wir die historischen Werte für den Tag 07 aus:&lt;br /&gt;
&lt;br /&gt;
 get &amp;lt;Name&amp;gt; pvHistory 07&lt;br /&gt;
&lt;br /&gt;
In dem Datensatz interessiert die Stunde 21. Sie resultiert aus der obigen Angabe &amp;quot;starttime: xxxx-xx-xx 20:00:00&amp;quot;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
      21 =&amp;gt; etotal: 64354660, pvfc: 0, pvrl: 0, pvrlvd: 1, rad1h: -&lt;br /&gt;
            etotalp01: -, etotalp02: -, etotalp03: -&lt;br /&gt;
            pprl01: -, pprl02: -, pprl03: -&lt;br /&gt;
            confc: 496, con: 1474, gcons: 23, conprice: 0.2958&lt;br /&gt;
            gfeedin: 5, feedprice: 0.1269&lt;br /&gt;
            DoN: 0, sunaz: 289, sunalt: -8&lt;br /&gt;
            batintotal: 3064096.92456668, batouttotal: 2958021.37946804, batin: 0, batout: 1460&lt;br /&gt;
            wid: 101, wcc: 20, rr1c: 0.00, pvcorrf: 1.00/-temp: 26.00, &lt;br /&gt;
            csmt01: 64262.43, csme01: 28.8099999999977, minutescsm01: 25&lt;br /&gt;
            minutescsm02: 0&lt;br /&gt;
            csmt03: 0, csme03: 0, minutescsm03: 0&lt;br /&gt;
            csmt04: 1389417.4, csme04: 94.5, minutescsm04: 60&lt;br /&gt;
            csmt05: 0, csme05: 0, minutescsm05: 0&lt;br /&gt;
            csmt06: 290.15, csme06: 6.66999999999996, minutescsm06: 54&lt;br /&gt;
            csmt07: 0, csme07: 0, minutescsm07: 0&lt;br /&gt;
            csmt08: 32800, csme08: 0, minutescsm08: 0&lt;br /&gt;
            csmt09: 103987.8, csme09: 38, minutescsm09: 43&lt;br /&gt;
            csmt10: 9151.24999999783, csme10: 3.99999999997999, minutescsm10: 60&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In dem Datensatz sind die Energieverbrauchsanteile der im Device registrierten Verbraucher mit dem Schlüssel &amp;quot;csmeXX&amp;quot; gespeichert. Dabei ist &amp;quot;XX&amp;quot; die Verbrauchernummer. In dem Beispiel existieren die Anteile csme01 und csme03 - csme10. Keiner dieser Verbraucher hat einen auffälligen Verbrauchswert gespeichert, d.h. die Messung der Verbraucher scheidet als Problemquelle aus. Wäre ein unverhältmismäßig hoher Verbrauch bei einem der Verbraucher feststellbar, müssten die Schlüssel &#039;&#039;&#039;pcurr&#039;&#039;&#039; und &#039;&#039;&#039;etotal&#039;&#039;&#039; im consumerXX-Attribut geprüft, sowie die Datenlieferung des Consumerdevices selbst geprüft werden.  &lt;br /&gt;
&lt;br /&gt;
Es wurde offensichtlich ein hoher Verbrauch von einem nicht registrierten Verbraucher verursacht oder durch die Übertragung eines Meßfehlers des Devices, angegeben im Attribut &#039;&#039;&#039;setupMeterDev&#039;&#039;&#039;, in das SolarForecast Device übertragen. Eine weitere Analyse ist an dieser Stelle nicht möglich.&lt;br /&gt;
&lt;br /&gt;
Zur Behebung des falschen Wertes kann ein Reset der gespeicherten Consumption der relevanten Stunde durchgeführt werden:&lt;br /&gt;
&lt;br /&gt;
 set &amp;lt;Name&amp;gt; reset consumptionHistory 07 21&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
=== Die KI‑Verbrauchsprognose mit AI::FANN aktivieren und einstellen ===&lt;br /&gt;
&lt;br /&gt;
FANN steht für &#039;&#039;&#039;Fast Artificial Neural Network&#039;&#039;&#039;.&lt;br /&gt;
Es handelt sich um eine sehr schnelle, leichtgewichtige Open‑Source‑Bibliothek zur Erstellung und zum Training künstlicher neuronaler Netze. Ein Manual dieser Library ist [https://fann.sourceforge.net/reference/index.html hier] verfügbar.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* FANN ist die &#039;&#039;&#039;technische Grundlage&#039;&#039;&#039; der KI‑Verbrauchsprognose.&lt;br /&gt;
* ws berechnet aus historischen Verbrauchsdaten ein Modell, das zukünftige Werte vorhersagen kann.&lt;br /&gt;
* es ist &#039;&#039;&#039;robust, schnell und ressourcenschonend&#039;&#039;&#039;, ideal für Systeme, die regelmäßig neu trainiert werden müssen.&lt;br /&gt;
* FANN arbeitet mit klassischen Feed‑Forward‑Netzen und Backpropagation‑Training – also bewährten, stabilen Methoden.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;AI::FANN&#039;&#039;&#039; ist ein Perl‑Modul, das als Schnittstelle (Binding) zwischen SolarForecast und der FANN‑Bibliothek dient.&lt;br /&gt;
&lt;br /&gt;
* AI::FANN ist die Brücke, über die unser Prognosesystem mit FANN kommuniziert.&lt;br /&gt;
* es ermöglicht dem System:&lt;br /&gt;
::* Trainingsdaten an FANN zu übergeben&lt;br /&gt;
::* ein neuronales Netz zu erzeugen&lt;br /&gt;
::* das Training zu steuern&lt;br /&gt;
::* das fertige Modell zu speichern und später für Prognosen zu nutzen&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Voraussetzungen ====&lt;br /&gt;
&lt;br /&gt;
===== Installation der FANN‑Library (Linux) ===== &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
sudo apt-get update &amp;amp;&amp;amp; sudo apt-get upgrade&lt;br /&gt;
sudo apt-get install gcc&lt;br /&gt;
sudo apt-get install libfann-dev libfann2&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
RPM‑basierte Systeme:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
sudo dnf install fann-devel&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
RPM‑basierte Systeme sind Linux‑Distributionen, die das Paketformat RPM (Red Hat Package Manager) verwenden.&lt;br /&gt;
&lt;br /&gt;
Typische RPM‑basierte Linux‑Distributionen:&lt;br /&gt;
* Red Hat Enterprise Linux (RHEL)&lt;br /&gt;
* CentOS / CentOS Stream&lt;br /&gt;
* Fedora&lt;br /&gt;
* openSUSE / SUSE Linux Enterprise&lt;br /&gt;
* Rocky Linux&lt;br /&gt;
* AlmaLinux&lt;br /&gt;
* Mageia&lt;br /&gt;
&lt;br /&gt;
===== Installation der FANN‑Library (Windows) ===== &lt;br /&gt;
&lt;br /&gt;
* der Installations- bzw. Erstellungsprozess der dll ist [https://fann.sourceforge.net/reference/x26.html hier] beschrieben &lt;br /&gt;
* fann.dll in PATH oder FHEM‑Verzeichnis kopieren  &lt;br /&gt;
* Perl‑Installation muss DLL laden können&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Installation des Perl‑Moduls AI::FANN ===== &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
cpan install AI::FANN&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Auf Debian Linux Systemen kann das Modul auch mit&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
sudo apt-get install libai-fann-perl &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
installiert werden.&lt;br /&gt;
&lt;br /&gt;
Alternativ kann natürlich auch cpanm oder der [https://wiki.fhem.de/wiki/Meta#Der_FHEM_Installer FHEM Installer] verwendet werden.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Bestandteile der Machine-Learning (ML)‑Pipeline ====&lt;br /&gt;
&lt;br /&gt;
===== Datenaufbereitung ===== &lt;br /&gt;
&lt;br /&gt;
Die Datenaufbereitung ist der wichtigste Schritt jeder ML‑Pipeline, da sie die Qualität der Eingabedaten sicherstellt.&lt;br /&gt;
Schlechte oder unskalierte Daten führen fast immer zu schlechten Modellen.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Normalisierung / Skalierung&#039;&#039;&#039;  &lt;br /&gt;
:- ML‑Modelle benötigen Eingabewerte in vergleichbaren Größenordnungen.&lt;br /&gt;
:- Beispiel: PV‑Leistung (0–10 000 W) vs. Uhrzeit (0–23).&lt;br /&gt;
:- Ohne Normalisierung würden große Werte die kleinen dominieren.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Feature‑Engineering&#039;&#039;&#039;  &lt;br /&gt;
: Aus Rohdaten werden zusätzliche Merkmale erzeugt, die Muster besser sichtbar machen:&lt;br /&gt;
::- Lags: Verbrauch der letzten Minuten/Stunden&lt;br /&gt;
::- Deltas: Änderungen gegenüber vorherigen Zeitpunkten&lt;br /&gt;
::- Zeitmerkmale: Stunde, Wochentag, Feiertag&lt;br /&gt;
::- PV‑Prognosen: erwartete Erzeugung als Einflussfaktor&lt;br /&gt;
::- Saisonale Merkmale: Sommer/Winter, Temperatur&lt;br /&gt;
::- Semantik (Erstellung von Interaktionen)&lt;br /&gt;
::- Rolling‑Statistics&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
📊 &#039;&#039;Rolling‑Statistics&#039;&#039; &amp;lt;br&amp;gt;&lt;br /&gt;
„Rolling“ bedeutet, dass die Kennzahlen nicht einmalig über den gesamten Datensatz berechnet werden, sondern gleitend über ein Zeitfenster (z. B. 24 h, 7 Tage, 100 Samples). So werden Trends, Drift, Modellverschlechterung oder saisonale Muster besser erkannt.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Integration zusätzlicher Verbraucher&#039;&#039;&#039;&lt;br /&gt;
: Wärmepumpen oder E‑Autos erzeugen starke, unregelmäßige Peaks. &lt;br /&gt;
: Diese können als zusätzliche Features einfließen.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Es wird umgesetzt:&#039;&#039;&#039;&lt;br /&gt;
* ✔ Normalisierung auf physikalische Grenzen (PV‑Maximalleistung, Hausverbrauchs‑Maxima)&lt;br /&gt;
* ✔ Feature‑Engineering wie Lags, Deltas, Rolling‑Statistics, Zeitmerkmale, PV/PV-Prognosen als Input und weitere Semantik&lt;br /&gt;
* ✔ Optionale Zusatzlasten (WP/EV)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===== Trainingsdaten‑Split ===== &lt;br /&gt;
&lt;br /&gt;
Der Split trennt Daten in Trainingsdaten (zum Lernen) und Validierungsdaten (zur Qualitätskontrolle).&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Training&#039;&#039;&#039;  &lt;br /&gt;
: Das Modell lernt Muster aus historischen Daten.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Validierung&#039;&#039;&#039;  &lt;br /&gt;
: Das Modell wird auf Daten getestet, die es nicht gesehen hat.&lt;br /&gt;
: Dadurch erkennt man Overfitting.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Shuffling&#039;&#039;&#039;  &lt;br /&gt;
: Durchmischen der Daten, um Reihenfolgeeffekte zu vermeiden.&lt;br /&gt;
: Besonders wichtig bei Zeitreihen.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Es wird umgesetzt:&#039;&#039;&#039;&lt;br /&gt;
* ✔ Training/Validierung‑Split&lt;br /&gt;
* ✔ Shuffling je nach Einstellung von aiControl-&amp;gt;aiConShuffleMode&lt;br /&gt;
* ✔ Shuffle‑Perioden während des Trainings&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===== Netzarchitektur ===== &lt;br /&gt;
&lt;br /&gt;
Die Architektur bestimmt, wie das neuronale Netz aufgebaut ist.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Inputs&#039;&#039;&#039;  &lt;br /&gt;
: Anzahl der Merkmale (Features), die das Modell erhält.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Hidden Layers&#039;&#039;&#039;  &lt;br /&gt;
: Anzahl und Größe der verdeckten Schichten bestimmen, wie komplexe Muster erkannt werden können.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Output Layer&#039;&#039;&#039;  &lt;br /&gt;
: bei SolarForecast: 1 Wert = Verbrauchsprognose (Wh).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Es wird umgesetzt:&#039;&#039;&#039;&lt;br /&gt;
* ✔ Dynamische Input‑Anzahl je nach aktivierten Features&lt;br /&gt;
* ✔ Konfigurierbare Hidden Layers (z. B. 80‑40‑20)&lt;br /&gt;
* ✔ 1 Output‑Neuron&lt;br /&gt;
* ✔ Konfigurierbare Aktivierungsfunktionen&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===== Training ===== &lt;br /&gt;
&lt;br /&gt;
Hier werden die Gewichte des Netzes angepasst, um Fehler zu minimieren.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Trainingsalgorithmus&#039;&#039;&#039;&lt;br /&gt;
: RPROP: stabil, robust&lt;br /&gt;
: INCREMENTAL: schneller, aber empfindlicher&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Lernrate (Learning Rate)&#039;&#039;&#039;  &lt;br /&gt;
: Wie stark Gewichte pro Schritt angepasst werden.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Momentum&#039;&#039;&#039;  &lt;br /&gt;
: Glättet die Lernbewegung, verhindert Zickzack‑Verhalten.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Steepness&#039;&#039;&#039;  &lt;br /&gt;
: Steuert die Empfindlichkeit der Aktivierungsfunktion.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Shuffle‑Perioden&#039;&#039;&#039;  &lt;br /&gt;
: Wie oft die Trainingsdaten neu gemischt werden.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Early‑Stopping&#039;&#039;&#039;  &lt;br /&gt;
: Training wird beendet, wenn Validierungsfehler nicht mehr sinkt.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Es wird umgesetzt:&#039;&#039;&#039;&lt;br /&gt;
* ✔ RPROP und INCREMENTAL&lt;br /&gt;
* ✔ Learning Rate, Momentum, Steepness konfigurierbar&lt;br /&gt;
* ✔ Shuffle‑Perioden&lt;br /&gt;
* ✔ Early‑Stopping über Validierungsfehler&lt;br /&gt;
* ✔ Mehrere Trainingsläufe (Runs) zur Modellselektion&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===== Modellselektion und Fehlerevaluierung ===== &lt;br /&gt;
&lt;br /&gt;
Hier wird entschieden, welches Modell am Ende verwendet wird. &lt;br /&gt;
Die Ergebnisse des Trainings werden während des Traininglaufs fortlaufend überwacht und die Einhaltung von intern vorgegebenen Kennzahlen von einem Retrain-Guard überprüft. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Evaluation&#039;&#039;&#039;&lt;br /&gt;
* MAE, MAPE, RMSE, R²&lt;br /&gt;
* BitFail&lt;br /&gt;
* Bias/Slope&lt;br /&gt;
* P95/P99&lt;br /&gt;
&lt;br /&gt;
📈 &#039;&#039;Fehlermaße: MAE, MAPE, RMSE, R²&#039;&#039;  &amp;lt;br&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;MAE&#039;&#039;&#039; – Mean Absolute Error (durchschnittlicher absoluter Fehler) &lt;br /&gt;
::: MAE=1/𝑛∑∣𝑦−𝑦&amp;lt;sup&amp;gt;^&amp;lt;/sup&amp;gt;∣&lt;br /&gt;
* &#039;&#039;&#039;MAPE&#039;&#039;&#039; – Mean Absolute Percentage Error (prozentualer Fehler)&lt;br /&gt;
::: MAPE=100/𝑛∑∣(𝑦−𝑦&amp;lt;sup&amp;gt;^&amp;lt;/sup&amp;gt;)/𝑦∣&lt;br /&gt;
::* Achtung: &#039;&#039;&#039;problematisch bei kleinen oder Null‑Werten&#039;&#039;&#039; (klassisch bei Verbrauchsdaten nachts).&lt;br /&gt;
* &#039;&#039;&#039;RMSE&#039;&#039;&#039; – Root Mean Squared Error (Quadratischer Fehler, stärker empfindlich für Ausreißer)&lt;br /&gt;
::* gut, wenn große Fehler besonders kritisch sind&lt;br /&gt;
* &#039;&#039;&#039;R²&#039;&#039;&#039; – Bestimmtheitsmaß (Anteil der Varianz, den das Modell erklärt)&lt;br /&gt;
::: 𝑅&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;=1−(SSE/SST)&lt;br /&gt;
::* 1.0 = perfekt, 0 = nicht besser als Mittelwert, &amp;lt;0 = schlechter als Mittelwert&lt;br /&gt;
* &#039;&#039;&#039;P95 / P99&#039;&#039;&#039; (Quantile) - Quantile beschreiben, wie groß der Fehler in den schlechtesten X % der Fälle ist.&lt;br /&gt;
::* &#039;&#039;&#039;P95:&#039;&#039;&#039; 95 % der Fehler liegen darunter → die schlechtesten 5 % sind größer&lt;br /&gt;
::* &#039;&#039;&#039;P99:&#039;&#039;&#039; 99 % der Fehler liegen darunter → zeigt Extremfehler&lt;br /&gt;
* &#039;&#039;&#039;Bias / Slope&#039;&#039;&#039; (Regression‑Diagnostik) - Diese beiden Werte stammen aus einer linearen Regression zwischen &#039;&#039;&#039;Vorhersage&#039;&#039;&#039; und &#039;&#039;&#039;Ist‑Wert&#039;&#039;&#039;.&lt;br /&gt;
::* Diese beiden Werte sind extrem hilfreich, um Modell‑Drift oder fehlerhafte Normalisierung zu erkennen.&lt;br /&gt;
::* Slope - Ideal: 1.0&lt;br /&gt;
:::* &amp;lt; 1 → Modell unterschätzt hohe Werte&lt;br /&gt;
:::* &amp;gt;= 1 → Modell überschätzt hohe Werte&lt;br /&gt;
:::* zeigt systematische Skalierungsfehler&lt;br /&gt;
::* Bias - Ideal: 0&lt;br /&gt;
:::* positiv → Modell sagt im Schnitt zu wenig vorher&lt;br /&gt;
:::* negativ → Modell sagt im Schnitt zu viel vorher&lt;br /&gt;
:::* zeigt systematische Verschiebung&lt;br /&gt;
* &#039;&#039;&#039;BitFail&#039;&#039;&#039;&lt;br /&gt;
::* FANN verwendet „BitFail“ als Zählwert, wie viele Ausgaben eines Samples „falsch“ sind. Ein Output gilt als falsch, wenn der Fehler größer ist als ein definierter BitFail‑Schwellwert (z. B. 0.35).&lt;br /&gt;
::* Bei Regressionen ist BitFail weniger aussagekräftig, aber:&lt;br /&gt;
:::- es zeigt, wie viele Samples „deutlich daneben“ liegen.&lt;br /&gt;
:::- gut zur schnellen Qualitätskontrolle während des Trainings.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Wird die geforderte Güte nicht erreicht, initiiert der Guard eine Wiederholung des Trainings bis zu einer maximalen Anzahl von Durchläufen.  &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Bestes Modell speichern&#039;&#039;&#039;  &lt;br /&gt;
: Das Modell mit dem niedrigsten Validierungsfehler wird ausgewählt.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Verwendung nur des besten Modells&#039;&#039;&#039;  &lt;br /&gt;
: Schlechtere Modelle werden verworfen.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Trainingsmodus ohne Prognose&#039;&#039;&#039;  &lt;br /&gt;
: Nützlich, wenn man nur trainieren, aber noch nicht prognostizieren möchte.&lt;br /&gt;
: einschalten mit aiControl -&amp;gt;aiConActivate=2&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Es wird umgesetzt:&#039;&#039;&#039;&lt;br /&gt;
* ✔ Monitoring mit Fehlerevaluierung&lt;br /&gt;
* ✔ bestes Modell wird automatisch gespeichert&lt;br /&gt;
* ✔ Prognosen basieren ausschließlich auf dem besten Modell&lt;br /&gt;
* ✔ Trainingsmodus only möglich (aiConActivate=2)&lt;br /&gt;
* ✔ Ausgabe detaillierter Trainingsmetriken&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Einstellbare Parameter über &#039;attr &amp;lt;Name&amp;gt; aiControl&#039; ====&lt;br /&gt;
&lt;br /&gt;
Die Einstellung von &#039;&#039;&#039;aiConActivate&#039;&#039;&#039;, &#039;&#039;&#039;aiConAlpha&#039;&#039;&#039; und &#039;&#039;&#039;aiConTrainStart&#039;&#039;&#039; wirken sofort, alle anderen Parameter werden erst beim nächsten Trainingslauf wirksam.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;u&amp;gt;aiConActivate&amp;lt;/u&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Wert !! Bedeutung&lt;br /&gt;
|-&lt;br /&gt;
| 0 || KI deaktiviert&lt;br /&gt;
|-&lt;br /&gt;
| 1 || KI aktiviert (Standard)&lt;br /&gt;
|-&lt;br /&gt;
| 2 || Trainingsmodus aktiv, die KI-Prognose ist deaktiviert. Es wird maximal eine Trainingswiederholung ausgeführt.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Best‑Practice:&#039;&#039;&#039; &amp;lt;br&amp;gt;&lt;br /&gt;
* 1 ab 2000–3000 verfügbaren Datensätzen (siehe get &amp;lt;Name&amp;gt; valDecTree aiRawData), bei WP/EV &amp;gt; 5000&lt;br /&gt;
* Der Trainingsmodus dient dazu verschiedene Einstellungen testen zu können. Zur Zeitersparnis wird maximal eine Trainingswiederholung ausgeführt, unabhängig von der Güte des Trainingsergebisses&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;u&amp;gt;aiConAlpha&amp;lt;/u&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Mit diesem Parameter kann ein Hybridergebnis aus klassischen (Legacy‑)Prognosen und durch die KI ermittelten Prognosewerten erzeugt werden. Das Hybridergebnis wird durch eine prozentuale Mischung beider Resultate erstellt.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Wert !! Bedeutung&lt;br /&gt;
|-&lt;br /&gt;
| 0 || die KI-Ergebnisse werden nicht verwendet, nur Legacy Werte gehen in das finale Ergebnis ein&lt;br /&gt;
|-&lt;br /&gt;
| 0.x || die KI-Ergebnisse gehen zu 0.x * 100%, die Legacy Werte zu (1-0.x) * 100% in die finale Prognose ein &lt;br /&gt;
|-&lt;br /&gt;
| 1 || es werden ausschließlich KI-Ergebnisse verwendet, Legacy Werte gehen nicht in das finale Ergebnis ein&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Best‑Practice:&#039;&#039;&#039; &amp;lt;br&amp;gt;&lt;br /&gt;
* 1 wenn die Genauigkeit der KI-Prognose überzeugend ist, ansonsten kann ein Mix aus KI und Legacy-Prognose hergestellt werden&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;u&amp;gt;aiConTrainStart&amp;lt;/u&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Legt fest, wann das Modul ein atomatisches Erst- bzw. Folgetraining durchführt. Im Standard erfolgt das Training alle 7 Tage.&lt;br /&gt;
&lt;br /&gt;
Format: `&amp;lt;Tage&amp;gt;:&amp;lt;Stunde&amp;gt;`, z. B. `7:3`.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Best‑Practice:&#039;&#039;&#039; &amp;lt;br&amp;gt; &lt;br /&gt;
* 7–14 Tage für normale Haushalte  &lt;br /&gt;
* 3–5 Tage bei WP/EV&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;u&amp;gt;aiConActFunc&amp;lt;/u&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Funktion !! Eigenschaften&lt;br /&gt;
|-&lt;br /&gt;
| SIGMOID || + bewährt, stabil, gut für kontinuierliche, monotone Zusammenhänge&lt;br /&gt;
– kann bei großen Inputs saturieren, aber bei Verbrauchsprognose meistens gut&lt;br /&gt;
|-&lt;br /&gt;
| GAUSSIAN || + gut für glatte Muster &lt;br /&gt;
– schlecht für harte Peaks (EV, Trockner), kann „vergessen“ und saturieren&lt;br /&gt;
|-&lt;br /&gt;
| ELLIOT || + schnellere, numerisch robustere Approximation von Sigmoid/Tanh, weniger Rechenaufwand &lt;br /&gt;
+ oft etwas „härter“ in der Reaktion, kann Peaks besser durchreichen &amp;lt;br&amp;gt;&lt;br /&gt;
– immer noch S‑förmig, also keine Wunderwaffe gegen komplett unmodellierte EV‑Ereignisse&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Best‑Practice:&#039;&#039;&#039; &amp;lt;br&amp;gt; &lt;br /&gt;
* SIGMOID oder SIGMOID_SYMMETRIC als Standard&lt;br /&gt;
* bei allen weiteren Aktivierungsfunktionen kommt es auf den Versuch an&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;u&amp;gt;aiConHiddenLayers&amp;lt;/u&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Typ !! Beispiel !! Eigenschaften&lt;br /&gt;
|-&lt;br /&gt;
| klein || 50‑25 || schnell, weniger genau&lt;br /&gt;
|-&lt;br /&gt;
| mittel || 64‑32 || guter Kompromiss&lt;br /&gt;
|-&lt;br /&gt;
| tief || 64‑32‑16 / 80‑40‑20 || erkennt komplexe Muster&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Best‑Practice:&#039;&#039;&#039; &amp;lt;br&amp;gt;&lt;br /&gt;
* &amp;lt; 3000 verfügbaren Datensätzen → `50-25`  &lt;br /&gt;
* 3000–8000 verfügbaren Datensätzen → `64-32`  &lt;br /&gt;
* &amp;gt; 8000 verfügbaren Datensätzen oder WP/EV → `80-40-20`&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;u&amp;gt;aiConLearnRate&amp;lt;/u&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Wert !! Bedeutung&lt;br /&gt;
|-&lt;br /&gt;
| 0.001 || sehr stabil, langsam&lt;br /&gt;
|-&lt;br /&gt;
| 0.005 || guter Kompromiss&lt;br /&gt;
|-&lt;br /&gt;
| 0.01–0.05 || schnell, aber instabil&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Best‑Practice:&#039;&#039;&#039; &amp;lt;br&amp;gt;&lt;br /&gt;
* 0.005, bei WP/EV 0.001&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;u&amp;gt;aiConBitFailLimit&amp;lt;/u&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Das Bit‑Fail‑Limit bestimmt, &#039;&#039;&#039;ab welcher Abweichung zwischen Prognose und echtem Wert&#039;&#039;&#039; ein Trainingspunkt als „Fehler“ gezählt wird.&lt;br /&gt;
Ein kleinerer Wert macht das Training &#039;&#039;&#039;strenger&#039;&#039;&#039;, ein größerer Wert macht es &#039;&#039;&#039;toleranter&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;kleineres Limit → Modell versucht Peaks stärker zu treffen&#039;&#039;&#039; → kann instabil werden, besonders mit INCREMENTAL&lt;br /&gt;
* &#039;&#039;&#039;größeres Limit → Modell glättet stärker&#039;&#039;&#039; → robuster, aber weniger peak‑sensitiv&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Wert !! Bedeutung&lt;br /&gt;
|-&lt;br /&gt;
| kleineres Limit (0.10–0.25) || Das Modell versucht Peaks und schnelle Änderungen genauer zu treffen, kann aber instabiler werden.&lt;br /&gt;
|-&lt;br /&gt;
| mittleres Limit (0.25–0.35) || Gute Balance zwischen Genauigkeit und Stabilität. Empfohlener Bereich.&lt;br /&gt;
|-&lt;br /&gt;
| größeres Limit (0.35–0.50) || Das Modell wird robuster gegen Ausreißer und Rauschen, reagiert aber weniger stark auf Spitzenlasten.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
🌪️ &#039;&#039;&#039;Was bedeutet „verrauschter Haushalt“?&#039;&#039;&#039; &amp;lt;br&amp;gt;&lt;br /&gt;
Ein Haushalt gilt als „verrauscht“, wenn der Stromverbrauch viele kleine, unregelmäßige Schwankungen hat, die nicht durch klare Muster (z. B. PV, Wärmepumpe, feste Tagesabläufe) erklärbar sind.&lt;br /&gt;
Dazu gehören spontane Gerätewechsel, impulsartige Lasten oder allgemein unruhige Verbrauchsprofile.&lt;br /&gt;
&lt;br /&gt;
Für solche Haushalte ist ein höheres Bit‑Fail‑Limit sinnvoll, weil das Modell dadurch robuster wird und nicht versucht, jede kleine Zufallsschwankung exakt vorherzusagen.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Best‑Practice:&#039;&#039;&#039; &amp;lt;br&amp;gt;&lt;br /&gt;
* versuche deinen Haushalt gemäß der oben beschriebenen Rauscheigenschaft einzuordnen und entscheide dann, ob ein Training unterhalb oder oberhalb des Defaultwertes (0.35) bei dir sinnvoll sein könnte. Bleibe dann bei der Einstellung die bei dir die besten Ergebnisse liefert. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;u&amp;gt;aiConMomentum&amp;lt;/u&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Bereich !! Bedeutung&lt;br /&gt;
|-&lt;br /&gt;
| 0.2 || reagiert direkt, schwankt stärker&lt;br /&gt;
|-&lt;br /&gt;
| 0.5–0.8 || stabil, guter Kompromiss&lt;br /&gt;
|-&lt;br /&gt;
| 0.9 || sehr stark geglättet&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Best‑Practice:&#039;&#039;&#039; &amp;lt;br&amp;gt;&lt;br /&gt;
* 0.5, bei WP/EV 0.7–0.8&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;u&amp;gt;aiConShuffleMode&amp;lt;/u&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Wert !! Bedeutung&lt;br /&gt;
|-&lt;br /&gt;
| 0 || chronologisch&lt;br /&gt;
|-&lt;br /&gt;
| 1 || chronologischer Split + Shuffle&lt;br /&gt;
|-&lt;br /&gt;
| 2 || vollständiges Shuffle&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Best‑Practice:&#039;&#039;&#039; &amp;lt;br&amp;gt; &lt;br /&gt;
* 2, bei saisonalen Mustern 1&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;u&amp;gt;aiConShufflePeriod&amp;lt;/u&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Wert !! Bedeutung&lt;br /&gt;
|-&lt;br /&gt;
| 10 || viel Variation&lt;br /&gt;
|-&lt;br /&gt;
| 15–20 || stabiler Kompromiss&lt;br /&gt;
|-&lt;br /&gt;
| &amp;gt;20 || Gefahr von Overfitting&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Best‑Practice:&#039;&#039;&#039; &amp;lt;br&amp;gt;&lt;br /&gt;
* 10, bei großen Datensätzen 15–20&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;u&amp;gt;aiConSteepness&amp;lt;/u&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Bereich !! Bedeutung&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;0.4 || starke Glättung&lt;br /&gt;
|-&lt;br /&gt;
| 0.5 || Standard&lt;br /&gt;
|-&lt;br /&gt;
| &amp;gt;0.7 || schärfere Muster&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Best‑Practice:&#039;&#039;&#039; &amp;lt;br&amp;gt; &lt;br /&gt;
* 0.9, bei glattem Verbrauch 0.5&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;u&amp;gt;aiConTrainAlgo&amp;lt;/u&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Algorithmus !! Eigenschaften&lt;br /&gt;
|-&lt;br /&gt;
| RPROP || stabil, wenig Parameter&lt;br /&gt;
|-&lt;br /&gt;
| INCREMENTAL || schneller, empfindlicher&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Best‑Practice:&#039;&#039;&#039; &amp;lt;br&amp;gt; &lt;br /&gt;
* RPROP bei WP/EV  &lt;br /&gt;
* INCREMENTAL bei einfachen Profilen&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;u&amp;gt;aiConProfile&amp;lt;/u&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Die folgenden Profile bestimmen, welche Verbrauchslogiken und Semantiken im Modell aktiviert werden. Sie beeinflussen, wie stark das Modell auf typische Haushaltsaktivitäten, PV‑Erzeugung und Wärmepumpenverhalten reagiert.&lt;br /&gt;
&lt;br /&gt;
Damit Nutzer das passende Profil wählen können, sind die Profile hier verständlich beschrieben.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Profil !! Eigenschaften&lt;br /&gt;
|-&lt;br /&gt;
| v1_common || Dieses Profil bildet den typischen menschlichen Tagesrhythmus ab:&lt;br /&gt;
* Morgenaktivität (Aufstehen, Geräte an)&lt;br /&gt;
* Mittagsaktivität (Kochen, Haushalt)&lt;br /&gt;
* Abendaktivität (Kochen, Licht, Geräte)&lt;br /&gt;
* Später Abend (Geräte gehen aus)&lt;br /&gt;
* Wochenendverhalten&lt;br /&gt;
* Tagesmuster vom Vortag&lt;br /&gt;
* Grundlast nachts&lt;br /&gt;
&lt;br /&gt;
Es nutzt Zeitstruktur, saisonale Muster, Wetter und Verbrauchsdynamik — aber &#039;&#039;&#039;keine PV‑ oder Wärmepumpenlogik&#039;&#039;&#039;. &amp;lt;br&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Empfohlen für:&#039;&#039;&#039; &lt;br /&gt;
Haushalte mit reinem Stromverbrauch ohne besondere Geräte und wenig PV Einfluß, z.B. mit kleiner Balkon PV-Anlage.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| v1_common_active || Für &#039;&#039;&#039;Haushalte mit stark ausgeprägten Aktivitätsmustern&#039;&#039;&#039; &amp;lt;br&amp;gt;&lt;br /&gt;
Wie &#039;&#039;v1_common&#039;&#039;, aber mit stärkerer Betonung auf:&lt;br /&gt;
* Verbrauchssprüngen&lt;br /&gt;
* Trendbrüchen&lt;br /&gt;
* Volatilität (z. B. Kochen, Gerätewechsel)&lt;br /&gt;
* Wiederkehrenden Peaks&lt;br /&gt;
&lt;br /&gt;
Dieses Profil reagiert sensibler auf &#039;&#039;&#039;kurzfristige Verbrauchsdynamik&#039;&#039;&#039;. &amp;lt;br&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Empfohlen für:&#039;&#039;&#039;  &lt;br /&gt;
Haushalte mit unregelmäßigen oder dynamischen Verbrauchsprofilen (z.B. viele Geräte, Homeoffice, Familien).&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| v1_common_pv || Für &#039;&#039;&#039;Haushalte mit dominanter PV‑Anlage&#039;&#039;&#039; &amp;lt;br&amp;gt;&lt;br /&gt;
Erweitert &#039;&#039;v1_common&#039;&#039; um PV‑Semantik:&lt;br /&gt;
* PV‑Erzeugung + Tageszeit&lt;br /&gt;
* PV‑Drop‑Erkennung (Wolke → Lastabfall)&lt;br /&gt;
* PV‑abhängige Verbrauchsverschiebung&lt;br /&gt;
* PV‑Mittagsspitzen‑Verstärker&lt;br /&gt;
* PV‑Temperatur‑Interaktionen (Heizung, Batterie, Geräteverhalten)&lt;br /&gt;
&lt;br /&gt;
Das Modell versteht, dass Haushalte &#039;&#039;&#039;mittags anders verbrauchen&#039;&#039;&#039;, wenn PV‑Strom verfügbar ist. &amp;lt;br&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Empfohlen für:&#039;&#039;&#039;  &lt;br /&gt;
Haushalte mit größerer PV‑Anlage, aber ohne Wärmepumpe.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| v1_common_active_pv || Für &#039;&#039;&#039;Haushalte mit PV und dynamischem Verbrauchsverhalten&#039;&#039;&#039; &amp;lt;br&amp;gt;&lt;br /&gt;
Kombiniert: &lt;br /&gt;
* Dynamik‑Semantik aus v1_common_active&lt;br /&gt;
* PV‑Semantik aus v1_common_pv&lt;br /&gt;
&lt;br /&gt;
Dieses Profil erkennt:&lt;br /&gt;
* spontane Peaks bei PV‑Überschuss&lt;br /&gt;
* Verbrauchssprünge + PV‑Erzeugung&lt;br /&gt;
* Trendverstärker mittags&lt;br /&gt;
* PV‑Drop‑Reaktionen&lt;br /&gt;
* Volatilität + PV‑Interaktionen&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Empfohlen für:&#039;&#039;&#039;  &lt;br /&gt;
PV‑Haushalte mit aktiven Verbrauchern (z.B. Kochen, Waschmaschine, Trockner usw.).&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| v1_heatpump || Für &#039;&#039;&#039;Haushalte mit Wärmepumpe (ohne dominante PV)&#039;&#039;&#039; &amp;lt;br&amp;gt;&lt;br /&gt;
Aktiviert die komplette Wärmepumpen‑Semantik:&lt;br /&gt;
* Heizmodus, Kühlmodus&lt;br /&gt;
* Warmwasser (morgens/abends, Zyklen, Kaltstart)&lt;br /&gt;
* Frostschutz&lt;br /&gt;
* Temperaturtrends&lt;br /&gt;
* Effizienzindikatoren (COP‑Proxy)&lt;br /&gt;
* Heiz‑/Kühlbedarf&lt;br /&gt;
* Temperaturdeltas und -historie&lt;br /&gt;
&lt;br /&gt;
Das Modell versteht typische WP‑Verbrauchsmuster:&lt;br /&gt;
* morgens und abends Heizen&lt;br /&gt;
* Warmwasserzyklen&lt;br /&gt;
* Frostschutz nachts&lt;br /&gt;
* Reaktion auf Temperaturstürze&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Empfohlen für:&#039;&#039;&#039;  &lt;br /&gt;
Haushalte mit Wärmepumpe, aber ohne dominante PV.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| v1_heatpump_pv || Für &#039;&#039;&#039;Wärmepumpen‑Haushalte mit dominanter PV&#039;&#039;&#039; &amp;lt;br&amp;gt;&lt;br /&gt;
Kombiniert:&lt;br /&gt;
* Wärmepumpen‑Semantik&lt;br /&gt;
* PV‑Semantik&lt;br /&gt;
* PV‑Warmwasser‑Boost&lt;br /&gt;
* PV‑Mittagsverschiebung&lt;br /&gt;
* PV‑abhängige Heiz-/WW‑Strategien&lt;br /&gt;
* Das Modell erkennt:&lt;br /&gt;
* PV‑Überschuss → Warmwasser&lt;br /&gt;
* PV‑Erzeugung → Heiz-/Kühlverhalten&lt;br /&gt;
* PV‑Drop → Lastabfall&lt;br /&gt;
* PV + Kälte → Heizspitzen&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Empfohlen für:&#039;&#039;&#039;  &lt;br /&gt;
Haushalte mit Wärmepumpe und dominanter PV‑Anlage.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| v1_heatpump_active_pv || Für &#039;&#039;&#039;Wärmepumpe + PV + dynamisches Verbrauchsverhalten&#039;&#039;&#039; &amp;lt;br&amp;gt;&lt;br /&gt;
Das umfassendste Profil. Es kombiniert:&lt;br /&gt;
* Wärmepumpen‑Semantik&lt;br /&gt;
* PV‑Semantik&lt;br /&gt;
* Dynamik‑Semantik (Trend, Volatilität, Peaks)&lt;br /&gt;
* Spezial‑Booster (z. B. heatpump_boost_special, pv_mittag_peak_boost_special)&lt;br /&gt;
&lt;br /&gt;
Dieses Profil erkennt:&lt;br /&gt;
* spontane Verbrauchssprünge&lt;br /&gt;
* WP‑Zyklen + PV‑Überschuss&lt;br /&gt;
* Trendbrüche + Temperaturdeltas&lt;br /&gt;
* dynamische Mittagspeaks&lt;br /&gt;
* komplexe Interaktionen zwischen PV, WP und menschlichem Verhalten&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Empfohlen für:&#039;&#039;&#039;  &lt;br /&gt;
Haushalte mit Wärmepumpe, PV und sehr aktivem Verbrauchsverhalten.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|  || &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Best‑Practice:&#039;&#039;&#039; &amp;lt;br&amp;gt;&lt;br /&gt;
Die oben angegebenen Profileigenschaften beschreiben die implementierten Verstärkungen bzw. auch Abschwächungen zur Unterstützung des Lernprozesses der künstlichen Intelligenz. Teste Trainings mit unterschiedlichen Profilen und wähle das Profil mit dem die besten Ergebnisse erzielt werden. Beginne mit dem Profil welches wahrscheinlich am Besten zu deinem Haushalt passen könnte.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Integration von Umwelt- und Kontextfaktoren in die Prognose ====&lt;br /&gt;
&lt;br /&gt;
Die Verbrauchscharakteristik eines Haushalts wird nicht nur durch die eingesetzten elektrischen Geräte bestimmt, sondern auch durch verschiedene systemische Einflussfaktoren, darunter:&lt;br /&gt;
&lt;br /&gt;
* Anwesenheit bzw. Abwesenheit der Bewohner&lt;br /&gt;
* Außentemperaturen&lt;br /&gt;
* Abweichungen vom üblichen Tagesmuster, z.B. durch Feiertage oder Urlaubszeiten&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Einbindung von Sensoren und Kontextdaten&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Außentemperatursensoren sowie Geräte zur Anwesenheits- oder Bewegungsdetektion werden im Attribut &#039;&#039;&#039;setupEnvironment&#039;&#039;&#039; hinterlegt. Die dafür erforderliche Syntax ist in der Online‑Hilfe des Moduls dokumentiert. SolarForecast nutzt diese Informationen, um Verbrauchsprognosen an reale Rahmenbedingungen anzupassen.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Feiertage und Urlaubszeiten&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Für die Berücksichtigung von Feiertagen und Urlaub wird das in FHEM etablierte Verfahren über [https://fhem.de/commandref_DE.html#holiday holiday]-Devices verwendet.&lt;br /&gt;
Dazu werden die entsprechenden holiday‑Devices im globalen Attribut &#039;&#039;holiday2we&#039;&#039; registriert. SolarForecast liest die dort eingetragenen holiday‑Devices aus und übernimmt die darin definierten Daten (z.B. gesetzliche Feiertage, benutzerdefinierte freie Tage oder Urlaubszeiträume).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
==== Das Training durchführen und produktiv setzen (Best Practice Ansatz) ====&lt;br /&gt;
&lt;br /&gt;
Sind alle [[SolarForecast_-_Solare_Prognose_(PV_Erzeugung)_und_Verbrauchersteuerung#Voraussetzungen|Voraussetzungen]] erfüllt, kann das Verbauchsprognose-Modell trainiert und – sofern das Ergebnis überzeugt – für den Produktivbetrieb aktiviert werden. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
1️⃣ &#039;&#039;&#039;Training‑Only‑Modus aktivieren&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Für das initiale Training wird FANN im &#039;&#039;Training‑Only‑Modus&#039;&#039; aktiviert:&lt;br /&gt;
&lt;br /&gt;
 attr &amp;lt;Name&amp;gt; aiControl aiConActivate=2&lt;br /&gt;
&lt;br /&gt;
In diesem Modus kann das Modell beliebig oft trainiert, bewertet und durch Anpassung der [[SolarForecast_-_Solare_Prognose_(PV_Erzeugung)_und_Verbrauchersteuerung#Einstellbare_Parameter_über_&#039;attr_&amp;lt;Name&amp;gt;_aiControl|Steuerungsparameter]] optimiert werden – ohne dass die produktive Prognose beeinflusst wird. Es kann jederzeit ein neues Training gestartet werden.&lt;br /&gt;
&lt;br /&gt;
Wer den Trainingsablauf und die internen Prüfungen live verfolgen möchte, aktiviert zusätzlich:&lt;br /&gt;
&lt;br /&gt;
 attr &amp;lt;Name&amp;gt; ctrlDebug aiProcess&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
2️⃣ &#039;&#039;&#039;Training starten&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Mit der FANN-Aktivierung durch aiControl aiConActivate=2 (oder 1) werden automatisch sinnvolle &#039;&#039;&#039;Standardparameter&#039;&#039;&#039; für FANN gesetzt. Für den Einstieg empfiehlt es sich, zunächst mit diesen Defaults zu trainieren und Änderungen an Parametern schrittweise vorzunehmen, neu zu trainieren und die Veränderungen im Ergebnis zu bewerten. Als Hilfestellung gibt es dazu einen separaten [[SolarForecast_-_Solare_Prognose_(PV_Erzeugung)_und_Verbrauchersteuerung#Auswertung_der_Trainingskennwerte|Abschnitt]]. &lt;br /&gt;
&lt;br /&gt;
Das Training wird manuell gestartet mit:&lt;br /&gt;
&lt;br /&gt;
 set &amp;lt;Name&amp;gt; aiDecTree runConTrain&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Hinweis:&#039;&#039;&#039; Ein bereits laufendes Training wird durch einen erneuten Start mit &#039;&#039;runConTrain&#039;&#039; automatisch abgebrochen.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Ein laufendes Training kann jederzeit manuell beendet werden:&lt;br /&gt;
&lt;br /&gt;
 set &amp;lt;Name&amp;gt; aiDecTree stopConTrain&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
3️⃣ &#039;&#039;&#039;Wie das Training intern abläuft&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Der Trainingsprozess wird vollständig durch die interne Steuerungslogik des Moduls geführt:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* Ein Trainingsjob umfasst &#039;&#039;&#039;bis zu 6 Trainingswiederholungen&#039;&#039;&#039;, jede mit &#039;&#039;&#039;maximal 15000&#039;&#039;&#039; Epochen.&lt;br /&gt;
* Ein &#039;&#039;&#039;Snapshot‑Guard&#039;&#039;&#039; überwacht kontinuierlich die Modellverbesserung und speichert bessere Modelle als Best Snapshot.&lt;br /&gt;
* Ein integrierter &#039;&#039;&#039;Early‑Stopping‑Mechanismus&#039;&#039;&#039; beendet Trainingsläufe, die sich nicht weiter verbessern.&lt;br /&gt;
* Ein interner &#039;&#039;&#039;Retrain‑Indikator&#039;&#039;&#039; bewertet den besten Snapshot. Ein &#039;&#039;&#039;Quality Gate&#039;&#039;&#039; entscheidet, ob ein weiterer Retrain durchgeführt wird.&lt;br /&gt;
* Die Anzahl der Retrains wird &#039;&#039;&#039;adaptiv&#039;&#039;&#039; anhand interner Kriterien bestimmt.&lt;br /&gt;
* Mit aktiviertem Debug‑Modus werden alle Entscheidungen im Log dokumentiert.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Der gesamte Prozess läuft in einem &#039;&#039;&#039;Nebenprozess&#039;&#039;&#039;, um FHEM nicht zu blockieren.&lt;br /&gt;
Je nach Hardware, Datenmenge und Parametern kann das Training sehr lange dauern – im Extremfall mehrere Stunden. Ein CPU‑Kern kann dabei dauerhaft zu 100 % ausgelastet sein.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
4️⃣ &#039;&#039;&#039;Ergebnis bewerten&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Nach Abschluss des Trainings sollte das Ergebnis &#039;&#039;&#039;[[SolarForecast_-_Solare_Prognose_(PV_Erzeugung)_und_Verbrauchersteuerung#Auswertung_der_Trainingskennwerte|manuell bewertet]]&#039;&#039;&#039; werden.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Wichtige Hinweise:&#039;&#039;&#039; &amp;lt;br&amp;gt;&lt;br /&gt;
* Auch bei &#039;&#039;&#039;nicht perfekten Kennzahlen&#039;&#039;&#039; lohnt sich eine visuelle Prüfung.&lt;br /&gt;
* Auch bei &#039;&#039;&#039;zu perfekten Kennzahlen&#039;&#039;&#039; ist Vorsicht geboten – dies kann auf Overfitting hindeuten.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
5️⃣ &#039;&#039;&#039;Modell produktiv setzen&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Zur visuellen Begutachtung und für den Produktivbetrieb wird das trainierte Modell aktiviert:&lt;br /&gt;
&lt;br /&gt;
 attr &amp;lt;Name&amp;gt; aiControl aiConActivate=1&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Im nächsten Zyklus des Moduls (siehe &#039;attr ... plantControl cycleInterval&#039;) erscheinen die KI‑Prognosen in der Balkengrafik, sofern diese Ansicht aktiviert ist. Wenn die Prognose zufriedenstellend ist, bleibt das Modell aktiv. Falls nicht, kann jederzeit wieder in den &#039;&#039;Training‑Only&#039;&#039;‑Modus gewechselt werden, um Parameter anzupassen und erneut zu trainieren.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
6️⃣ &#039;&#039;&#039;Automatisches (Re)Training&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Bei aktivem FANN‑Modus (aiConActivate=1 oder 2) wird automatisch alle &#039;&#039;&#039;7 Tage um 03:00 Uhr&#039;&#039;&#039; ein Retraining durchgeführt.&lt;br /&gt;
Der Zeitpunkt kann angepasst werden:&lt;br /&gt;
&lt;br /&gt;
 attr &amp;lt;Name&amp;gt; aiControl aiConTrainStart ...&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
7️⃣ &#039;&#039;&#039;Hybrid‑Modus&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Das Modul unterstützt einen &#039;&#039;&#039;Hybrid‑Modus&#039;&#039;&#039;, bei dem KI‑Prognosen mit den klassischen (Legacy‑)Prognosen gewichtet kombiniert werden. &lt;br /&gt;
Gesteuert wird dieser Modus über:&lt;br /&gt;
&lt;br /&gt;
 attr &amp;lt;Name&amp;gt; aiControl aiConAlpha &amp;lt;Wert&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Ein höherer Wert von [[SolarForecast_-_Solare_Prognose_(PV_Erzeugung)_und_Verbrauchersteuerung#Einstellbare_Parameter_über_&#039;attr_&amp;lt;Name&amp;gt;_aiControl&#039;|&#039;&#039;&#039;aiConAlpha&#039;&#039;&#039;]] bedeutet stärkere Gewichtung der KI‑Prognose.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
8️⃣ &#039;&#039;&#039;Debug‑Log&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Für eine detaillierte Nachvollziehbarkeit des Trainingsprozesses kann der Debug‑Modus aktiviert werden.&lt;br /&gt;
Im Debug‑Log protokolliert das Modul sämtliche Schritte der Trainingslogik, darunter:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* Start und Ende einzelner Trainingsläufe&lt;br /&gt;
* Verbesserungen der Modellgüte&lt;br /&gt;
* Snapshot‑Updates&lt;br /&gt;
* Early‑Stopping‑Ereignisse&lt;br /&gt;
* Entscheidungen des Quality Gates&lt;br /&gt;
* Auslösung von Retrains&lt;br /&gt;
* Abschlussbewertung des besten Modells&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Dies erleichtert sowohl die Diagnose ungewöhnlicher Trainingsverläufe als auch die Bewertung, ob Parameteranpassungen sinnvoll sind.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;mw-collapsible mw-collapsed&amp;quot;&amp;gt;&lt;br /&gt;
Ein &#039;&#039;&#039;auszugsweises Beispiel&#039;&#039;&#039; eines Trainingslogs -&amp;gt; &amp;lt;div class=&amp;quot;mw-collapsible-content&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
2026.01.01 15:40:06.690 1: SolCast DEBUG&amp;gt; First attempt 0 with Seed=407496&lt;br /&gt;
2026.01.01 15:40:06.696 1: SolCast DEBUG&amp;gt; AI FANN Training started with Params:&lt;br /&gt;
num input datasets=7931, &lt;br /&gt;
training algo=FANN_TRAIN_INCREMENTAL, &lt;br /&gt;
output AF=LINEAR, &lt;br /&gt;
hidden AF=SIGMOID, &lt;br /&gt;
hidden Neurons=80,40,20, &lt;br /&gt;
hidden steepness=0.9, &lt;br /&gt;
Epoches=15000, &lt;br /&gt;
mse_error=0.001, &lt;br /&gt;
learning rate=0.00500, &lt;br /&gt;
learning momentum=0.5, &lt;br /&gt;
Data sharing=split after shuffle of training data and use AI internal shuffle (Train=6344, Test=1586), &lt;br /&gt;
Data shuffle=2 (period=10)&lt;br /&gt;
2026.01.01 15:40:06.818 1: SolCast DEBUG&amp;gt; Epoche 1: Train MSE=0.001532, Val MSE=0.002521, Val MAE=0.028206, Val MedAE=0.020769, Bit_Fail=5 -&amp;gt; Snap saved (bit improved)&lt;br /&gt;
2026.01.01 15:40:07.534 1: SolCast DEBUG&amp;gt; Epoche 8: Train MSE=0.001472, Val MSE=0.002520, Val MAE=0.028199, Val MedAE=0.020764, Bit_Fail=5 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 15:40:13.541 1: SolCast DEBUG&amp;gt; Epoche 67: Train MSE=0.001477, Val MSE=0.002518, Val MAE=0.026941, Val MedAE=0.018749, Bit_Fail=5 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 15:40:13.748 1: SolCast DEBUG&amp;gt; Epoche 69: Train MSE=0.001476, Val MSE=0.002517, Val MAE=0.026928, Val MedAE=0.018728, Bit_Fail=5 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
...&lt;br /&gt;
2026.01.01 15:40:21.048 1: SolCast DEBUG&amp;gt; Epoche 140: Train MSE=0.001181, Val MSE=0.002253, Val MAE=0.023585, Val MedAE=0.012176, Bit_Fail=5 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 15:40:27.191 1: SolCast DEBUG&amp;gt; Epoche 200: Train MSE=0.001181, Val MSE=0.001860, Val MAE=0.022984, Val MedAE=0.012926, Bit_Fail=4&lt;br /&gt;
2026.01.01 15:40:27.704 1: SolCast DEBUG&amp;gt; Epoche 205: Train MSE=0.000982, Val MSE=0.001821, Val MAE=0.022794, Val MedAE=0.012760, Bit_Fail=4 -&amp;gt; Snap saved (bit improved)&lt;br /&gt;
2026.01.01 15:40:29.356 1: SolCast DEBUG&amp;gt; Epoche 221: Train MSE=0.000894, Val MSE=0.001654, Val MAE=0.021991, Val MedAE=0.012736, Bit_Fail=4 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
...&lt;br /&gt;
2026.01.01 15:40:30.508 1: SolCast DEBUG&amp;gt; Epoche 232: Train MSE=0.000806, Val MSE=0.001516, Val MAE=0.020531, Val MedAE=0.011495, Bit_Fail=4 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 15:40:30.615 1: SolCast DEBUG&amp;gt; Epoche 233: Train MSE=0.000797, Val MSE=0.001498, Val MAE=0.020435, Val MedAE=0.011480, Bit_Fail=2 -&amp;gt; Snap saved (bit improved)&lt;br /&gt;
2026.01.01 15:40:30.722 1: SolCast DEBUG&amp;gt; Epoche 234: Train MSE=0.000787, Val MSE=0.001479, Val MAE=0.020336, Val MedAE=0.011380, Bit_Fail=2 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 15:40:30.829 1: SolCast DEBUG&amp;gt; Epoche 235: Train MSE=0.000777, Val MSE=0.001460, Val MAE=0.020232, Val MedAE=0.011321, Bit_Fail=2 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
...&lt;br /&gt;
2026.01.01 15:40:34.103 1: SolCast DEBUG&amp;gt; Epoche 266: Train MSE=0.000347, Val MSE=0.000621, Val MAE=0.015564, Val MedAE=0.010886, Bit_Fail=1 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 15:40:34.209 1: SolCast DEBUG&amp;gt; Epoche 267: Train MSE=0.000336, Val MSE=0.000598, Val MAE=0.015326, Val MedAE=0.010661, Bit_Fail=0 -&amp;gt; Snap saved (bit improved)&lt;br /&gt;
2026.01.01 15:40:34.322 1: SolCast DEBUG&amp;gt; Epoche 268: Train MSE=0.000325, Val MSE=0.000576, Val MAE=0.015095, Val MedAE=0.010564, Bit_Fail=0 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 15:40:34.431 1: SolCast DEBUG&amp;gt; Epoche 269: Train MSE=0.000314, Val MSE=0.000555, Val MAE=0.014870, Val MedAE=0.010315, Bit_Fail=0 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
...&lt;br /&gt;
2026.01.01 15:45:53.259 1: SolCast DEBUG&amp;gt; Epoche 3300: Train MSE=0.000188, Val MSE=0.000286, Val MAE=0.011095, Val MedAE=0.007725, Bit_Fail=0&lt;br /&gt;
2026.01.01 15:46:00.439 1: SolCast DEBUG&amp;gt; Early stopping bei Epoche 3368 (no improvement since 2000 epochs)&lt;br /&gt;
2026.01.01 15:46:00.439 1: === Snapshot-Statistik ===&lt;br /&gt;
2026.01.01 15:46:00.440 1: Metric-Improvement Snapshots: 67 (letzte Epoche: 1368)&lt;br /&gt;
2026.01.01 15:46:00.440 1: Bit-Improvement Snapshots:    5 (letzte Epoche: 267)&lt;br /&gt;
2026.01.01 15:46:00.441 1: Bit-Tradeoff Snapshots:       0 (letzte Epoche: 0)&lt;br /&gt;
2026.01.01 15:46:00.443 1: SolCast DEBUG&amp;gt; Best Snapshot reloaded from Epoche 1368: Train MSE=0.000188, Val MSE=0.000286, Val MAE=0.010789, Val MedAE=0.006938, Bit_Fail=0,&lt;br /&gt;
2026.01.01 15:46:00.444 1: SolCast DEBUG&amp;gt; Run Validation Test with 20% of Input data ...&lt;br /&gt;
2026.01.01 15:46:00.475 1: SolCast DEBUG&amp;gt; Validation finished - Best Training MSE=0.000188, Validation MSE=0.000286, Validation Bit_Fail=0&lt;br /&gt;
2026.01.01 15:46:00.476 1: SolCast DEBUG&amp;gt; Retrain check -&amp;gt; &lt;br /&gt;
-- In Normalization Space: -- &lt;br /&gt;
Train MSE=0.000188 &lt;br /&gt;
Val MSE=0.000286 &lt;br /&gt;
Val Mean=0.0002908456 &lt;br /&gt;
VAL/TRAIN MSE Ratio=1.525656 (limit=2.5) &lt;br /&gt;
Diff=0.000099 (limit=0.005) &lt;br /&gt;
ValStd=0.0000052738 (limit=7.27113936211133e-05) &lt;br /&gt;
-- At Original Scale: -- &lt;br /&gt;
MAE=96.4264081498789 &lt;br /&gt;
RMSE/MAE=1.5680 (limit=1.5) &lt;br /&gt;
Slope=0.846171 (limit=0.7 .. 1.3) &lt;br /&gt;
Bias=104.79 (limit=+-48.2132040749395) &lt;br /&gt;
R2=0.886437100872621 &lt;br /&gt;
P95=283.1830 (limit=385.705632599516) &lt;br /&gt;
P99=538.2559 (limit=771.411265199031) &lt;br /&gt;
-- Robustness Indicators: -- &lt;br /&gt;
RMSE relative=24 (limit=20) &lt;br /&gt;
BitFail=0 (limit=5) &lt;br /&gt;
BitFailRate=0.0000 (limit=0.1) &lt;br /&gt;
-&amp;gt; Retrain&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;mw-collapsible mw-collapsed&amp;quot;&amp;gt;&lt;br /&gt;
Ein &#039;&#039;&#039;komplettes Beispiel mit 3 Wiederholungen&#039;&#039;&#039; eines Trainingslogs -&amp;gt; &amp;lt;div class=&amp;quot;mw-collapsible-content&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
2026.01.01 18:03:45.037 1: SolCast DEBUG&amp;gt; AI FANN Training for Consumption Forecast BlockingCall PID &amp;quot;813631&amp;quot; with Timeout 86400 s started&lt;br /&gt;
2026.01.01 18:03:46.046 1: SolCast DEBUG&amp;gt; AI FANN - There are 503 Records skipped due to incomplete or invalid data. Index: &lt;br /&gt;
2025013115, 2025020401, 2025020414, 2025020415, 2025020416, 2025021813, 2025021816, 2025021913, 2025021914, 2025021915, 2025022013, 2025022316, 2025030216, 2025030310, 2025030315, 2025030316, 2025030416, 2025030511, 2025030512, 2025030516, 2025030517, 2025030610, 2025030611, 2025030612, 2025030616, 2025030710, 2025030711, 2025030712, 2025030713, 2025030716, 2025030810, 2025030910, 2025030916, 2025030917, 2025031010, 2025031114, 2025031519, 2025031609, 2025031709, 2025031710, 2025031716, 2025031717, 2025031810, 2025031811, 2025031814, 2025031815, 2025031816, 2025031817, 2025031909, 2025031910, 2025031911, 2025031915, 2025031916, 2025031917, 2025032010, 2025032011, 2025032014, 2025032015, 2025032016, 2025032017, 2025032110, 2025032114, 2025032115, 2025032116, 2025032117, 2025032209, 2025032210, 2025032211, 2025032214, 2025032215, 2025032814, 2025032815, 2025032817, 2025033003, 2025040211, 2025040212, 2025040215, 2025040216, 2025040217, 2025040314, 2025040315, 2025040316, 2025040317, 2025040318, 2025040410, 2025040411, 2025040413, 2025040414, 2025040418, 2025040510, 2025040517, 2025040518, 2025040612, 2025040617, 2025040618, 2025040711, 2025040811, 2025040812, 2025040813, 2025041017, 2025041018, 2025041110, 2025041111, 2025041216, 2025041217, 2025041218, 2025041312, 2025041316, 2025041317, 2025041417, 2025041418, 2025041611, 2025041617, 2025041618, 2025042017, 2025042018, 2025042115, 2025042117, 2025042118, 2025042218, 2025042609, 2025042610, 2025042611, 2025042612, 2025042617, 2025042618, 2025042710, 2025042711, 2025042712, 2025042714, 2025042715, 2025042717, 2025042718, 2025042810, 2025042811, 2025042813, 2025042814, 2025042817, 2025042818, 2025042911, 2025042913, 2025042914, 2025042917, 2025042918, 2025050110, 2025050111, 2025050114, 2025050117, 2025050118, 2025050210, 2025050211, 2025050213, 2025050518, 2025050610, 2025050611, 2025050614, 2025050617, 2025050618, 2025050706, 2025050707, 2025050708, 2025050810, 2025050812, 2025050816, 2025050817, 2025050910, 2025050911, 2025050912, 2025050917, 2025050918, 2025051009, 2025051010, 2025051011, 2025051012, 2025051017, 2025051018, 2025051109, 2025051210, 2025051211, 2025051212, 2025051213, 2025051214, 2025051218, 2025051310, 2025051313, 2025051314, 2025051317, 2025051318, 2025051411, 2025051413, 2025051414, 2025051418, 2025051510, 2025051610, 2025051611, 2025051710, 2025051713, 2025051916, 2025052010, 2025052011, 2025052017, 2025052018, 2025052111, 2025052113, 2025052114, 2025052115, 2025052118, 2025052211, 2025052409, 2025052410, 2025052416, 2025052417, 2025052609, 2025052616, 2025052617, 2025052618, 2025052817, 2025052910, 2025052911, 2025052917, 2025053010, 2025053013, 2025053017, 2025053018, 2025053109, 2025053110, 2025053111, 2025053112, 2025053113, 2025053115, 2025053117, 2025060210, 2025060215, 2025060217, 2025060310, 2025060312, 2025060317, 2025060318, 2025060513, 2025060517, 2025060609, 2025060610, 2025060618, 2025060710, 2025060711, 2025060712, 2025060713, 2025060811, 2025060812, 2025060816, 2025060817, 2025060818, 2025060909, 2025060910, 2025060911, 2025060912, 2025060913, 2025060917, 2025060918, 2025061015, 2025061016, 2025061109, 2025061114, 2025061117, 2025061118, 2025061119, 2025061209, 2025061210, 2025061211, 2025061212, 2025061213, 2025061214, 2025061215, 2025061217, 2025061218, 2025061309, 2025061310, 2025061311, 2025061312, 2025061313, 2025061314, 2025061315, 2025061317, 2025061318, 2025061319, 2025061410, 2025061411, 2025061413, 2025061414, 2025061417, 2025061418, 2025061710, 2025061713, 2025061714, 2025061717, 2025061718, 2025061724, 2025062010, 2025062011, 2025062013, 2025062014, 2025062017, 2025062018, 2025062109, 2025062110, 2025062111, 2025062112, 2025062114, 2025062117, 2025062118, 2025062209, 2025062210, 2025062211, 2025062212, 2025062213, 2025062214, 2025062217, 2025062218, 2025062309, 2025062310, 2025062311, 2025062317, 2025062318, 2025062409, 2025062410, 2025062411, 2025062818, 2025062911, 2025062913, 2025062918, 2025063009, 2025063011, 2025063013, 2025063014, 2025063017, 2025063018, 2025070110, 2025070111, 2025070112, 2025070113, 2025070117, 2025070118, 2025070210, 2025070211, 2025070212, 2025070213, 2025070217, 2025070218, 2025070318, 2025070410, 2025070411, 2025070412, 2025070417, 2025070418, 2025070510, 2025070511, 2025070512, 2025070610, 2025070611, 2025070615, 2025070717, 2025070810, 2025071013, 2025071017, 2025071112, 2025071113, 2025071117, 2025071311, 2025071318, 2025071410, 2025071411, 2025071812, 2025071818, 2025071910, 2025071911, 2025071912, 2025071913, 2025071918, 2025072009, 2025072510, 2025072514, 2025073110, 2025073118, 2025080118, 2025080210, 2025080310, 2025080410, 2025080417, 2025080418, 2025080616, 2025080617, 2025080715, 2025080716, 2025080717, 2025080718, 2025080917, 2025080918, 2025081015, 2025081016, 2025081017, 2025081018, 2025081115, 2025081116, 2025081117, 2025081118, 2025081120, 2025081121, 2025081122, 2025081123, 2025081124, 2025081215, 2025081216, 2025081217, 2025081315, 2025081316, 2025081317, 2025081318, 2025081415, 2025081416, 2025081417, 2025081418, 2025081516, 2025081517, 2025081617, 2025081816, 2025081817, 2025081818, 2025081915, 2025081916, 2025081917, 2025082015, 2025082016, 2025082017, 2025082018, 2025082115, 2025082116, 2025082117, 2025082416, 2025082516, 2025082517, 2025082518, 2025082519, 2025082612, 2025082613, 2025082614, 2025082618, 2025082716, 2025082717, 2025083110, 2025083111, 2025083113, 2025083118, 2025090110, 2025090215, 2025090311, 2025090317, 2025090411, 2025090414, 2025090417, 2025090517, 2025090610, 2025090611, 2025090612, 2025090618, 2025090710, 2025090711, 2025090713, 2025090714, 2025090718, 2025090810, 2025090813, 2025090910, 2025090911, 2025091217, 2025091816, 2025091817, 2025091915, 2025091916, 2025091917, 2025092014, 2025092015, 2025092016, 2025092017, 2025092113, 2025092115, 2025092315, 2025092316, 2025092417, 2025092910, 2025092911, 2025092912, 2025092917, 2025093011, 2025093012, 2025093013, 2025093017, 2025100316, 2025100317, 2025110320, 2025122222, 2025122223, 2025122224, 2025122510&lt;br /&gt;
2026.01.01 18:03:46.940 1: SolCast - DBG F[7300]: lag1=0.001 lag2=0.001 lag24=0.001 d1p=0.000 d1n=0.000 rollstd=0.000 up=0 down=0 upS=0.000 downS=0.000 vol=0 pvX=0 break=0&lt;br /&gt;
2026.01.01 18:03:46.942 1: SolCast - DBG F[7301]: lag1=0.001 lag2=0.001 lag24=0.001 d1p=0.000 d1n=0.000 rollstd=0.000 up=0 down=0 upS=0.000 downS=0.000 vol=0 pvX=0 break=0&lt;br /&gt;
2026.01.01 18:03:46.944 1: SolCast - DBG F[7302]: lag1=0.001 lag2=0.001 lag24=0.001 d1p=0.000 d1n=0.000 rollstd=0.000 up=0 down=0 upS=0.000 downS=0.000 vol=0 pvX=0 break=0&lt;br /&gt;
2026.01.01 18:03:46.945 1: SolCast - DBG F[7303]: lag1=0.001 lag2=0.001 lag24=0.001 d1p=0.000 d1n=0.000 rollstd=0.000 up=0 down=0 upS=0.000 downS=0.000 vol=0 pvX=0 break=0&lt;br /&gt;
2026.01.01 18:03:46.947 1: SolCast - DBG F[7304]: lag1=0.001 lag2=0.001 lag24=0.001 d1p=0.000 d1n=0.000 rollstd=0.000 up=0 down=0 upS=0.000 downS=0.000 vol=0 pvX=0 break=0&lt;br /&gt;
2026.01.01 18:03:46.949 1: SolCast - DBG F[7305]: lag1=0.001 lag2=0.001 lag24=0.001 d1p=0.000 d1n=0.000 rollstd=0.000 up=0 down=0 upS=0.000 downS=0.000 vol=0 pvX=0 break=0&lt;br /&gt;
2026.01.01 18:03:46.951 1: SolCast - DBG F[7306]: lag1=0.001 lag2=0.001 lag24=0.001 d1p=0.000 d1n=0.000 rollstd=0.000 up=0 down=0 upS=0.000 downS=0.000 vol=0 pvX=0 break=0&lt;br /&gt;
2026.01.01 18:03:46.953 1: SolCast - DBG F[7307]: lag1=0.001 lag2=0.001 lag24=0.001 d1p=0.000 d1n=0.000 rollstd=0.000 up=0 down=0 upS=0.000 downS=0.000 vol=0 pvX=0 break=0&lt;br /&gt;
2026.01.01 18:03:46.954 1: SolCast - DBG F[7308]: lag1=0.001 lag2=0.001 lag24=0.001 d1p=0.000 d1n=0.000 rollstd=0.000 up=0 down=0 upS=0.000 downS=0.000 vol=0 pvX=0 break=0&lt;br /&gt;
2026.01.01 18:03:46.956 1: SolCast - DBG F[7309]: lag1=0.001 lag2=0.001 lag24=0.001 d1p=0.000 d1n=0.000 rollstd=0.000 up=0 down=0 upS=0.000 downS=0.000 vol=0 pvX=0 break=0&lt;br /&gt;
2026.01.01 18:03:46.958 1: SolCast - DBG F[7310]: lag1=0.002 lag2=0.001 lag24=0.002 d1p=0.036 d1n=0.000 rollstd=0.000 up=1 down=0 upS=0.036 downS=0.000 vol=0 pvX=0 break=0&lt;br /&gt;
2026.01.01 18:03:46.960 1: SolCast - DBG F[7311]: lag1=0.026 lag2=0.002 lag24=0.008 d1p=0.004 d1n=0.000 rollstd=0.033 up=0 down=0 upS=0.004 downS=0.000 vol=0 pvX=0 break=0&lt;br /&gt;
2026.01.01 18:03:46.962 1: SolCast - DBG F[7312]: lag1=0.029 lag2=0.026 lag24=0.012 d1p=0.000 d1n=0.008 rollstd=0.044 up=0 down=1 upS=0.000 downS=0.008 vol=0 pvX=0 break=0&lt;br /&gt;
2026.01.01 18:03:46.963 1: SolCast - DBG F[7313]: lag1=0.023 lag2=0.029 lag24=0.016 d1p=0.011 d1n=0.000 rollstd=0.045 up=1 down=0 upS=0.011 downS=0.000 vol=0 pvX=0 break=0&lt;br /&gt;
2026.01.01 18:03:46.965 1: SolCast - DBG F[7314]: lag1=0.031 lag2=0.023 lag24=0.018 d1p=0.000 d1n=0.003 rollstd=0.044 up=0 down=0 upS=0.000 downS=0.003 vol=0 pvX=0 break=0&lt;br /&gt;
2026.01.01 18:03:46.967 1: SolCast - DBG F[7315]: lag1=0.028 lag2=0.031 lag24=0.017 d1p=0.002 d1n=0.000 rollstd=0.036 up=0 down=0 upS=0.002 downS=0.000 vol=0 pvX=0 break=0&lt;br /&gt;
2026.01.01 18:03:46.968 1: SolCast - DBG F[7316]: lag1=0.030 lag2=0.028 lag24=0.008 d1p=0.000 d1n=0.002 rollstd=0.009 up=0 down=0 upS=0.000 downS=0.002 vol=0 pvX=0 break=0&lt;br /&gt;
2026.01.01 18:03:46.970 1: SolCast - DBG F[7317]: lag1=0.028 lag2=0.030 lag24=0.002 d1p=0.000 d1n=0.005 rollstd=0.009 up=0 down=1 upS=0.000 downS=0.005 vol=0 pvX=0 break=0&lt;br /&gt;
2026.01.01 18:03:46.974 1: SolCast - DBG F[7318]: lag1=0.025 lag2=0.028 lag24=0.002 d1p=0.005 d1n=0.000 rollstd=0.010 up=1 down=0 upS=0.005 downS=0.000 vol=0 pvX=0 break=0&lt;br /&gt;
2026.01.01 18:03:46.976 1: SolCast - DBG F[7319]: lag1=0.028 lag2=0.025 lag24=0.001 d1p=0.008 d1n=0.000 rollstd=0.007 up=1 down=0 upS=0.008 downS=0.000 vol=0 pvX=0 break=0&lt;br /&gt;
2026.01.01 18:03:47.013 1: SolCast DEBUG&amp;gt; First attempt 0 with Seed=439288&lt;br /&gt;
2026.01.01 18:03:47.024 1: SolCast DEBUG&amp;gt; AI FANN Training started with Params:&lt;br /&gt;
num input datasets=7314, &lt;br /&gt;
training algo=FANN_TRAIN_INCREMENTAL, &lt;br /&gt;
output AF=LINEAR, &lt;br /&gt;
hidden AF=SIGMOID, &lt;br /&gt;
hidden Neurons=80,40,20, &lt;br /&gt;
hidden steepness=0.9, &lt;br /&gt;
Epoches=15000, &lt;br /&gt;
mse_error=0.001, &lt;br /&gt;
learning rate=0.00500, &lt;br /&gt;
learning momentum=0.7, &lt;br /&gt;
Data sharing=split after shuffle of training data and use AI internal shuffle (Train=5851, Test=1462), &lt;br /&gt;
Data shuffle=2 (period=10)&lt;br /&gt;
2026.01.01 18:03:47.208 1: SolCast DEBUG&amp;gt; Epoche 1: Train MSE=0.002194, Val MSE=0.002834, Val MAE=0.031901, Val MedAE=0.027985, Bit_Fail=2 -&amp;gt; Snap saved (bit improved)&lt;br /&gt;
2026.01.01 18:03:47.528 1: SolCast DEBUG&amp;gt; Epoche 3: Train MSE=0.002192, Val MSE=0.002833, Val MAE=0.031892, Val MedAE=0.027975, Bit_Fail=2 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 18:03:54.175 1: SolCast DEBUG&amp;gt; Epoche 48: Train MSE=0.001736, Val MSE=0.002765, Val MAE=0.029706, Val MedAE=0.016374, Bit_Fail=2 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 18:04:02.843 1: SolCast DEBUG&amp;gt; Epoche 100: Train MSE=0.001736, Val MSE=0.001509, Val MAE=0.026824, Val MedAE=0.022443, Bit_Fail=1&lt;br /&gt;
2026.01.01 18:04:07.181 1: SolCast DEBUG&amp;gt; Epoche 127: Train MSE=0.000623, Val MSE=0.001033, Val MAE=0.022203, Val MedAE=0.017074, Bit_Fail=1 -&amp;gt; Snap saved (bit improved)&lt;br /&gt;
2026.01.01 18:04:07.341 1: SolCast DEBUG&amp;gt; Epoche 128: Train MSE=0.000611, Val MSE=0.001012, Val MAE=0.021994, Val MedAE=0.017013, Bit_Fail=1 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 18:04:07.519 1: SolCast DEBUG&amp;gt; Epoche 129: Train MSE=0.000599, Val MSE=0.000990, Val MAE=0.021783, Val MedAE=0.016793, Bit_Fail=1 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 18:04:07.994 1: SolCast DEBUG&amp;gt; Epoche 131: Train MSE=0.000577, Val MSE=0.000943, Val MAE=0.021456, Val MedAE=0.016655, Bit_Fail=1 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 18:04:08.185 1: SolCast DEBUG&amp;gt; Epoche 132: Train MSE=0.000566, Val MSE=0.000922, Val MAE=0.021240, Val MedAE=0.016463, Bit_Fail=1 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 18:04:08.344 1: SolCast DEBUG&amp;gt; Epoche 133: Train MSE=0.000554, Val MSE=0.000901, Val MAE=0.021025, Val MedAE=0.016355, Bit_Fail=1 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 18:04:08.508 1: SolCast DEBUG&amp;gt; Epoche 134: Train MSE=0.000543, Val MSE=0.000881, Val MAE=0.020809, Val MedAE=0.016328, Bit_Fail=1 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 18:04:08.684 1: SolCast DEBUG&amp;gt; Epoche 135: Train MSE=0.000532, Val MSE=0.000860, Val MAE=0.020593, Val MedAE=0.016227, Bit_Fail=1 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 18:04:08.840 1: SolCast DEBUG&amp;gt; Epoche 136: Train MSE=0.000521, Val MSE=0.000841, Val MAE=0.020381, Val MedAE=0.016031, Bit_Fail=1 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 18:04:08.989 1: SolCast DEBUG&amp;gt; Epoche 137: Train MSE=0.000511, Val MSE=0.000822, Val MAE=0.020175, Val MedAE=0.015652, Bit_Fail=1 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 18:04:09.144 1: SolCast DEBUG&amp;gt; Epoche 138: Train MSE=0.000501, Val MSE=0.000803, Val MAE=0.019972, Val MedAE=0.015495, Bit_Fail=1 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 18:04:09.297 1: SolCast DEBUG&amp;gt; Epoche 139: Train MSE=0.000492, Val MSE=0.000786, Val MAE=0.019773, Val MedAE=0.015409, Bit_Fail=1 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 18:04:09.458 1: SolCast DEBUG&amp;gt; Epoche 140: Train MSE=0.000481, Val MSE=0.000777, Val MAE=0.019495, Val MedAE=0.015158, Bit_Fail=1 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 18:04:09.615 1: SolCast DEBUG&amp;gt; Epoche 141: Train MSE=0.000473, Val MSE=0.000760, Val MAE=0.019325, Val MedAE=0.015145, Bit_Fail=1 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 18:04:09.767 1: SolCast DEBUG&amp;gt; Epoche 142: Train MSE=0.000465, Val MSE=0.000745, Val MAE=0.019163, Val MedAE=0.015044, Bit_Fail=1 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 18:04:09.919 1: SolCast DEBUG&amp;gt; Epoche 143: Train MSE=0.000457, Val MSE=0.000730, Val MAE=0.019008, Val MedAE=0.015017, Bit_Fail=1 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 18:04:10.080 1: SolCast DEBUG&amp;gt; Epoche 144: Train MSE=0.000449, Val MSE=0.000716, Val MAE=0.018857, Val MedAE=0.014983, Bit_Fail=1 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 18:04:10.245 1: SolCast DEBUG&amp;gt; Epoche 145: Train MSE=0.000442, Val MSE=0.000702, Val MAE=0.018714, Val MedAE=0.014883, Bit_Fail=1 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 18:04:10.400 1: SolCast DEBUG&amp;gt; Epoche 146: Train MSE=0.000436, Val MSE=0.000690, Val MAE=0.018579, Val MedAE=0.014777, Bit_Fail=1 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 18:04:10.705 1: SolCast DEBUG&amp;gt; Epoche 148: Train MSE=0.000424, Val MSE=0.000667, Val MAE=0.018328, Val MedAE=0.014732, Bit_Fail=1 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 18:04:10.858 1: SolCast DEBUG&amp;gt; Epoche 149: Train MSE=0.000418, Val MSE=0.000656, Val MAE=0.018213, Val MedAE=0.014659, Bit_Fail=1 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 18:04:11.447 1: SolCast DEBUG&amp;gt; Epoche 153: Train MSE=0.000398, Val MSE=0.000655, Val MAE=0.017918, Val MedAE=0.014430, Bit_Fail=0 -&amp;gt; Snap saved (bit improved)&lt;br /&gt;
2026.01.01 18:04:11.597 1: SolCast DEBUG&amp;gt; Epoche 154: Train MSE=0.000394, Val MSE=0.000648, Val MAE=0.017836, Val MedAE=0.014356, Bit_Fail=0 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 18:04:11.780 1: SolCast DEBUG&amp;gt; Epoche 155: Train MSE=0.000390, Val MSE=0.000641, Val MAE=0.017757, Val MedAE=0.014209, Bit_Fail=0 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 18:04:11.968 1: SolCast DEBUG&amp;gt; Epoche 156: Train MSE=0.000386, Val MSE=0.000635, Val MAE=0.017685, Val MedAE=0.014026, Bit_Fail=0 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 18:04:12.119 1: SolCast DEBUG&amp;gt; Epoche 157: Train MSE=0.000383, Val MSE=0.000629, Val MAE=0.017617, Val MedAE=0.013986, Bit_Fail=0 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 18:04:15.889 1: SolCast DEBUG&amp;gt; Epoche 180: Train MSE=0.000338, Val MSE=0.000530, Val MAE=0.017079, Val MedAE=0.013927, Bit_Fail=0 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 18:04:19.269 1: SolCast DEBUG&amp;gt; Epoche 200: Train MSE=0.000338, Val MSE=0.000535, Val MAE=0.016673, Val MedAE=0.013893, Bit_Fail=0&lt;br /&gt;
2026.01.01 18:04:35.878 1: SolCast DEBUG&amp;gt; Epoche 290: Train MSE=0.000288, Val MSE=0.000524, Val MAE=0.016388, Val MedAE=0.013535, Bit_Fail=0 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 18:04:36.582 1: SolCast DEBUG&amp;gt; Epoche 293: Train MSE=0.000287, Val MSE=0.000523, Val MAE=0.016367, Val MedAE=0.013507, Bit_Fail=0 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 18:04:37.370 1: SolCast DEBUG&amp;gt; Epoche 296: Train MSE=0.000287, Val MSE=0.000522, Val MAE=0.016353, Val MedAE=0.013413, Bit_Fail=0 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 18:04:38.343 1: SolCast DEBUG&amp;gt; Epoche 300: Train MSE=0.000287, Val MSE=0.000500, Val MAE=0.016520, Val MedAE=0.014059, Bit_Fail=0&lt;br /&gt;
2026.01.01 18:04:53.153 1: SolCast DEBUG&amp;gt; Epoche 360: Train MSE=0.000276, Val MSE=0.000498, Val MAE=0.016213, Val MedAE=0.013363, Bit_Fail=0 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 18:05:03.452 1: SolCast DEBUG&amp;gt; Epoche 400: Train MSE=0.000276, Val MSE=0.000492, Val MAE=0.016416, Val MedAE=0.013627, Bit_Fail=0&lt;br /&gt;
2026.01.01 18:05:29.137 1: SolCast DEBUG&amp;gt; Epoche 500: Train MSE=0.000276, Val MSE=0.000500, Val MAE=0.016208, Val MedAE=0.013216, Bit_Fail=0&lt;br /&gt;
2026.01.01 18:05:51.013 1: SolCast DEBUG&amp;gt; Epoche 600: Train MSE=0.000276, Val MSE=0.000500, Val MAE=0.016319, Val MedAE=0.012971, Bit_Fail=0&lt;br /&gt;
2026.01.01 18:06:14.772 1: SolCast DEBUG&amp;gt; Epoche 700: Train MSE=0.000276, Val MSE=0.000511, Val MAE=0.016383, Val MedAE=0.013241, Bit_Fail=0&lt;br /&gt;
2026.01.01 18:06:36.483 1: SolCast DEBUG&amp;gt; Epoche 800: Train MSE=0.000276, Val MSE=0.000509, Val MAE=0.016792, Val MedAE=0.013049, Bit_Fail=0&lt;br /&gt;
2026.01.01 18:06:52.867 1: SolCast DEBUG&amp;gt; Epoche 900: Train MSE=0.000276, Val MSE=0.000495, Val MAE=0.015972, Val MedAE=0.012938, Bit_Fail=0&lt;br /&gt;
2026.01.01 18:06:52.877 1: SolCast DEBUG&amp;gt; Epoche 900: Train MSE=0.000242, Val MSE=0.000495, Val MAE=0.015972, Val MedAE=0.012938, Bit_Fail=0 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 18:07:09.391 1: SolCast DEBUG&amp;gt; Epoche 1000: Train MSE=0.000242, Val MSE=0.000493, Val MAE=0.016187, Val MedAE=0.013015, Bit_Fail=0&lt;br /&gt;
2026.01.01 18:07:14.202 1: SolCast DEBUG&amp;gt; Epoche 1027: Train MSE=0.000237, Val MSE=0.000491, Val MAE=0.015971, Val MedAE=0.012928, Bit_Fail=0 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 18:07:18.001 1: SolCast DEBUG&amp;gt; Epoche 1050: Train MSE=0.000234, Val MSE=0.000486, Val MAE=0.015890, Val MedAE=0.012757, Bit_Fail=0 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 18:07:18.455 1: SolCast DEBUG&amp;gt; Epoche 1053: Train MSE=0.000234, Val MSE=0.000485, Val MAE=0.015881, Val MedAE=0.012678, Bit_Fail=0 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 18:07:25.760 1: SolCast DEBUG&amp;gt; Epoche 1100: Train MSE=0.000234, Val MSE=0.000480, Val MAE=0.015846, Val MedAE=0.012669, Bit_Fail=0&lt;br /&gt;
2026.01.01 18:07:25.766 1: SolCast DEBUG&amp;gt; Epoche 1100: Train MSE=0.000233, Val MSE=0.000480, Val MAE=0.015846, Val MedAE=0.012669, Bit_Fail=0 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 18:07:42.102 1: SolCast DEBUG&amp;gt; Epoche 1200: Train MSE=0.000233, Val MSE=0.000484, Val MAE=0.016233, Val MedAE=0.012505, Bit_Fail=0&lt;br /&gt;
2026.01.01 18:07:43.824 1: SolCast DEBUG&amp;gt; Epoche 1210: Train MSE=0.000227, Val MSE=0.000470, Val MAE=0.015552, Val MedAE=0.012390, Bit_Fail=0 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 18:07:57.828 1: SolCast DEBUG&amp;gt; Epoche 1300: Train MSE=0.000227, Val MSE=0.000520, Val MAE=0.017539, Val MedAE=0.013492, Bit_Fail=0&lt;br /&gt;
2026.01.01 18:08:14.430 1: SolCast DEBUG&amp;gt; Epoche 1400: Train MSE=0.000227, Val MSE=0.000460, Val MAE=0.016046, Val MedAE=0.012304, Bit_Fail=0&lt;br /&gt;
2026.01.01 18:08:16.132 1: SolCast DEBUG&amp;gt; Epoche 1410: Train MSE=0.000222, Val MSE=0.000452, Val MAE=0.015408, Val MedAE=0.011884, Bit_Fail=0 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 18:08:17.883 1: SolCast DEBUG&amp;gt; Epoche 1420: Train MSE=0.000220, Val MSE=0.000443, Val MAE=0.015258, Val MedAE=0.011811, Bit_Fail=0 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 18:08:29.901 1: SolCast DEBUG&amp;gt; Epoche 1490: Train MSE=0.000218, Val MSE=0.000439, Val MAE=0.014983, Val MedAE=0.011569, Bit_Fail=0 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 18:08:30.803 1: SolCast DEBUG&amp;gt; Epoche 1496: Train MSE=0.000218, Val MSE=0.000438, Val MAE=0.014960, Val MedAE=0.011509, Bit_Fail=0 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 18:08:31.440 1: SolCast DEBUG&amp;gt; Epoche 1500: Train MSE=0.000218, Val MSE=0.000439, Val MAE=0.015419, Val MedAE=0.011971, Bit_Fail=0&lt;br /&gt;
2026.01.01 18:08:47.448 1: SolCast DEBUG&amp;gt; Epoche 1600: Train MSE=0.000218, Val MSE=0.000454, Val MAE=0.016309, Val MedAE=0.012168, Bit_Fail=0&lt;br /&gt;
2026.01.01 18:08:50.500 1: SolCast DEBUG&amp;gt; Epoche 1620: Train MSE=0.000214, Val MSE=0.000431, Val MAE=0.014770, Val MedAE=0.011218, Bit_Fail=0 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 18:09:03.800 1: SolCast DEBUG&amp;gt; Epoche 1700: Train MSE=0.000214, Val MSE=0.000439, Val MAE=0.015947, Val MedAE=0.012095, Bit_Fail=0&lt;br /&gt;
2026.01.01 18:09:12.275 1: SolCast DEBUG&amp;gt; Epoche 1755: Train MSE=0.000210, Val MSE=0.000414, Val MAE=0.014646, Val MedAE=0.011210, Bit_Fail=0 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 18:09:19.335 1: SolCast DEBUG&amp;gt; Epoche 1800: Train MSE=0.000210, Val MSE=0.000427, Val MAE=0.015787, Val MedAE=0.011912, Bit_Fail=0&lt;br /&gt;
2026.01.01 18:09:36.229 1: SolCast DEBUG&amp;gt; Epoche 1900: Train MSE=0.000210, Val MSE=0.000417, Val MAE=0.015680, Val MedAE=0.011996, Bit_Fail=0&lt;br /&gt;
2026.01.01 18:09:45.972 1: SolCast DEBUG&amp;gt; Epoche 1960: Train MSE=0.000204, Val MSE=0.000388, Val MAE=0.014589, Val MedAE=0.011116, Bit_Fail=0 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 18:09:51.969 1: SolCast DEBUG&amp;gt; Epoche 2000: Train MSE=0.000204, Val MSE=0.000404, Val MAE=0.015606, Val MedAE=0.012356, Bit_Fail=0&lt;br /&gt;
2026.01.01 18:09:53.511 1: SolCast DEBUG&amp;gt; Epoche 2010: Train MSE=0.000202, Val MSE=0.000386, Val MAE=0.014313, Val MedAE=0.011006, Bit_Fail=0 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 18:09:55.063 1: SolCast DEBUG&amp;gt; Epoche 2020: Train MSE=0.000201, Val MSE=0.000383, Val MAE=0.014131, Val MedAE=0.010774, Bit_Fail=0 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 18:10:08.287 1: SolCast DEBUG&amp;gt; Epoche 2100: Train MSE=0.000201, Val MSE=0.000380, Val MAE=0.014528, Val MedAE=0.011353, Bit_Fail=0&lt;br /&gt;
2026.01.01 18:10:24.098 1: SolCast DEBUG&amp;gt; Epoche 2200: Train MSE=0.000201, Val MSE=0.000370, Val MAE=0.013946, Val MedAE=0.010416, Bit_Fail=0&lt;br /&gt;
2026.01.01 18:10:24.104 1: SolCast DEBUG&amp;gt; Epoche 2200: Train MSE=0.000197, Val MSE=0.000370, Val MAE=0.013946, Val MedAE=0.010416, Bit_Fail=0 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 18:10:39.646 1: SolCast DEBUG&amp;gt; Epoche 2300: Train MSE=0.000197, Val MSE=0.000374, Val MAE=0.014907, Val MedAE=0.011753, Bit_Fail=0&lt;br /&gt;
2026.01.01 18:10:56.682 1: SolCast DEBUG&amp;gt; Epoche 2400: Train MSE=0.000197, Val MSE=0.000356, Val MAE=0.013705, Val MedAE=0.010006, Bit_Fail=0&lt;br /&gt;
2026.01.01 18:10:56.693 1: SolCast DEBUG&amp;gt; Epoche 2400: Train MSE=0.000192, Val MSE=0.000356, Val MAE=0.013705, Val MedAE=0.010006, Bit_Fail=0 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 18:11:12.550 1: SolCast DEBUG&amp;gt; Epoche 2500: Train MSE=0.000192, Val MSE=0.000375, Val MAE=0.015257, Val MedAE=0.012743, Bit_Fail=0&lt;br /&gt;
2026.01.01 18:11:15.676 1: SolCast DEBUG&amp;gt; Epoche 2520: Train MSE=0.000191, Val MSE=0.000355, Val MAE=0.013591, Val MedAE=0.009674, Bit_Fail=0 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 18:11:17.824 1: SolCast DEBUG&amp;gt; Epoche 2531: Train MSE=0.000190, Val MSE=0.000346, Val MAE=0.013480, Val MedAE=0.009639, Bit_Fail=0 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 18:11:28.987 1: SolCast DEBUG&amp;gt; Epoche 2600: Train MSE=0.000190, Val MSE=0.000339, Val MAE=0.013649, Val MedAE=0.010530, Bit_Fail=0&lt;br /&gt;
2026.01.01 18:11:45.115 1: SolCast DEBUG&amp;gt; Epoche 2700: Train MSE=0.000190, Val MSE=0.000354, Val MAE=0.014651, Val MedAE=0.011877, Bit_Fail=0&lt;br /&gt;
2026.01.01 18:12:01.075 1: SolCast DEBUG&amp;gt; Epoche 2790: Train MSE=0.000186, Val MSE=0.000341, Val MAE=0.013362, Val MedAE=0.009487, Bit_Fail=0 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 18:12:02.671 1: SolCast DEBUG&amp;gt; Epoche 2800: Train MSE=0.000186, Val MSE=0.000333, Val MAE=0.013606, Val MedAE=0.010687, Bit_Fail=0&lt;br /&gt;
2026.01.01 18:12:18.422 1: SolCast DEBUG&amp;gt; Epoche 2900: Train MSE=0.000186, Val MSE=0.000335, Val MAE=0.013448, Val MedAE=0.010124, Bit_Fail=0&lt;br /&gt;
2026.01.01 18:12:35.464 1: SolCast DEBUG&amp;gt; Epoche 3000: Train MSE=0.000186, Val MSE=0.000327, Val MAE=0.013352, Val MedAE=0.010017, Bit_Fail=0&lt;br /&gt;
2026.01.01 18:12:56.411 1: SolCast DEBUG&amp;gt; Epoche 3100: Train MSE=0.000186, Val MSE=0.000333, Val MAE=0.013996, Val MedAE=0.011160, Bit_Fail=0&lt;br /&gt;
2026.01.01 18:13:10.994 1: SolCast DEBUG&amp;gt; Epoche 3162: Train MSE=0.000181, Val MSE=0.000328, Val MAE=0.013113, Val MedAE=0.009469, Bit_Fail=0 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 18:13:18.736 1: SolCast DEBUG&amp;gt; Epoche 3200: Train MSE=0.000181, Val MSE=0.000320, Val MAE=0.013393, Val MedAE=0.010591, Bit_Fail=0&lt;br /&gt;
2026.01.01 18:13:39.675 1: SolCast DEBUG&amp;gt; Epoche 3300: Train MSE=0.000181, Val MSE=0.000319, Val MAE=0.013325, Val MedAE=0.010389, Bit_Fail=0&lt;br /&gt;
2026.01.01 18:14:00.271 1: SolCast DEBUG&amp;gt; Epoche 3400: Train MSE=0.000181, Val MSE=0.000325, Val MAE=0.014006, Val MedAE=0.011521, Bit_Fail=0&lt;br /&gt;
2026.01.01 18:14:22.409 1: SolCast DEBUG&amp;gt; Epoche 3500: Train MSE=0.000181, Val MSE=0.000344, Val MAE=0.014666, Val MedAE=0.012138, Bit_Fail=0&lt;br /&gt;
2026.01.01 18:14:38.101 1: SolCast DEBUG&amp;gt; Epoche 3580: Train MSE=0.000177, Val MSE=0.000319, Val MAE=0.012991, Val MedAE=0.009430, Bit_Fail=0 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 18:14:42.467 1: SolCast DEBUG&amp;gt; Epoche 3600: Train MSE=0.000177, Val MSE=0.000314, Val MAE=0.013079, Val MedAE=0.010056, Bit_Fail=0&lt;br /&gt;
2026.01.01 18:15:03.838 1: SolCast DEBUG&amp;gt; Epoche 3700: Train MSE=0.000177, Val MSE=0.000333, Val MAE=0.014308, Val MedAE=0.011807, Bit_Fail=0&lt;br /&gt;
2026.01.01 18:15:16.053 2: HUEBridge: http request failed: read from http://192.168.2.7:80 timed out&lt;br /&gt;
2026.01.01 18:15:30.476 1: SolCast DEBUG&amp;gt; Epoche 3800: Train MSE=0.000177, Val MSE=0.000321, Val MAE=0.013020, Val MedAE=0.009413, Bit_Fail=0&lt;br /&gt;
2026.01.01 18:15:45.037 1: SolCast DEBUG&amp;gt; Epoche 3870: Train MSE=0.000174, Val MSE=0.000317, Val MAE=0.012911, Val MedAE=0.009292, Bit_Fail=0 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 18:15:52.131 1: SolCast DEBUG&amp;gt; Epoche 3900: Train MSE=0.000174, Val MSE=0.000312, Val MAE=0.013464, Val MedAE=0.010556, Bit_Fail=0&lt;br /&gt;
2026.01.01 18:16:12.390 1: SolCast DEBUG&amp;gt; Epoche 4000: Train MSE=0.000174, Val MSE=0.000320, Val MAE=0.013974, Val MedAE=0.011455, Bit_Fail=0&lt;br /&gt;
2026.01.01 18:16:29.091 1: SolCast DEBUG&amp;gt; Epoche 4100: Train MSE=0.000174, Val MSE=0.000312, Val MAE=0.013592, Val MedAE=0.010912, Bit_Fail=0&lt;br /&gt;
2026.01.01 18:16:52.505 1: SolCast DEBUG&amp;gt; Epoche 4200: Train MSE=0.000174, Val MSE=0.000302, Val MAE=0.012962, Val MedAE=0.010156, Bit_Fail=0&lt;br /&gt;
2026.01.01 18:17:16.099 1: SolCast DEBUG&amp;gt; Epoche 4300: Train MSE=0.000174, Val MSE=0.000303, Val MAE=0.013023, Val MedAE=0.010334, Bit_Fail=0&lt;br /&gt;
2026.01.01 18:17:31.066 1: openMeteo - Open-Meteo API server response: start_SSL https://api.open-meteo.com:443 timed out&lt;br /&gt;
2026.01.01 18:17:38.285 1: SolCast DEBUG&amp;gt; Epoche 4400: Train MSE=0.000174, Val MSE=0.000302, Val MAE=0.013197, Val MedAE=0.010550, Bit_Fail=0&lt;br /&gt;
2026.01.01 18:17:52.429 1: SolCast DEBUG&amp;gt; Epoche 4460: Train MSE=0.000170, Val MSE=0.000311, Val MAE=0.012837, Val MedAE=0.009137, Bit_Fail=0 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 18:17:58.628 1: SolCast DEBUG&amp;gt; Epoche 4490: Train MSE=0.000170, Val MSE=0.000305, Val MAE=0.012794, Val MedAE=0.009134, Bit_Fail=0 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 18:18:01.453 1: SolCast DEBUG&amp;gt; Epoche 4500: Train MSE=0.000170, Val MSE=0.000301, Val MAE=0.012898, Val MedAE=0.009802, Bit_Fail=0&lt;br /&gt;
2026.01.01 18:18:23.799 1: SolCast DEBUG&amp;gt; Epoche 4600: Train MSE=0.000170, Val MSE=0.000298, Val MAE=0.012957, Val MedAE=0.010201, Bit_Fail=0&lt;br /&gt;
2026.01.01 18:18:39.811 1: SolCast DEBUG&amp;gt; Epoche 4700: Train MSE=0.000170, Val MSE=0.000301, Val MAE=0.013527, Val MedAE=0.011413, Bit_Fail=0&lt;br /&gt;
2026.01.01 18:18:56.354 1: SolCast DEBUG&amp;gt; Epoche 4800: Train MSE=0.000170, Val MSE=0.000298, Val MAE=0.013255, Val MedAE=0.011018, Bit_Fail=0&lt;br /&gt;
2026.01.01 18:19:12.392 1: SolCast DEBUG&amp;gt; Epoche 4900: Train MSE=0.000170, Val MSE=0.000295, Val MAE=0.013216, Val MedAE=0.010940, Bit_Fail=0&lt;br /&gt;
2026.01.01 18:19:27.987 1: SolCast DEBUG&amp;gt; Epoche 5000: Train MSE=0.000170, Val MSE=0.000293, Val MAE=0.013073, Val MedAE=0.010728, Bit_Fail=0&lt;br /&gt;
2026.01.01 18:19:32.327 1: SolCast DEBUG&amp;gt; Epoche 5030: Train MSE=0.000167, Val MSE=0.000297, Val MAE=0.012698, Val MedAE=0.009102, Bit_Fail=0 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 18:19:43.670 1: SolCast DEBUG&amp;gt; Epoche 5100: Train MSE=0.000167, Val MSE=0.000298, Val MAE=0.013300, Val MedAE=0.010884, Bit_Fail=0&lt;br /&gt;
2026.01.01 18:19:59.190 1: SolCast DEBUG&amp;gt; Epoche 5200: Train MSE=0.000167, Val MSE=0.000294, Val MAE=0.013167, Val MedAE=0.010790, Bit_Fail=0&lt;br /&gt;
2026.01.01 18:20:10.606 1: SolCast DEBUG&amp;gt; Epoche 5262: Train MSE=0.000166, Val MSE=0.000295, Val MAE=0.012677, Val MedAE=0.009089, Bit_Fail=0 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 18:20:16.409 1: SolCast DEBUG&amp;gt; Epoche 5300: Train MSE=0.000166, Val MSE=0.000292, Val MAE=0.013247, Val MedAE=0.011177, Bit_Fail=0&lt;br /&gt;
2026.01.01 18:20:34.708 1: SolCast DEBUG&amp;gt; Epoche 5400: Train MSE=0.000166, Val MSE=0.000291, Val MAE=0.012964, Val MedAE=0.010392, Bit_Fail=0&lt;br /&gt;
2026.01.01 18:20:57.206 1: SolCast DEBUG&amp;gt; Epoche 5500: Train MSE=0.000166, Val MSE=0.000288, Val MAE=0.012996, Val MedAE=0.010552, Bit_Fail=0&lt;br /&gt;
2026.01.01 18:21:21.536 1: SolCast DEBUG&amp;gt; Epoche 5600: Train MSE=0.000166, Val MSE=0.000286, Val MAE=0.012927, Val MedAE=0.010610, Bit_Fail=0&lt;br /&gt;
2026.01.01 18:21:43.826 1: SolCast DEBUG&amp;gt; Epoche 5700: Train MSE=0.000166, Val MSE=0.000284, Val MAE=0.012811, Val MedAE=0.010111, Bit_Fail=0&lt;br /&gt;
2026.01.01 18:21:46.073 1: SolCast DEBUG&amp;gt; Epoche 5710: Train MSE=0.000164, Val MSE=0.000292, Val MAE=0.012617, Val MedAE=0.009052, Bit_Fail=0 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 18:22:05.636 1: SolCast DEBUG&amp;gt; Epoche 5800: Train MSE=0.000164, Val MSE=0.000311, Val MAE=0.013979, Val MedAE=0.012142, Bit_Fail=0&lt;br /&gt;
2026.01.01 18:22:28.019 1: SolCast DEBUG&amp;gt; Epoche 5900: Train MSE=0.000164, Val MSE=0.000284, Val MAE=0.012679, Val MedAE=0.009654, Bit_Fail=0&lt;br /&gt;
2026.01.01 18:22:44.531 1: SolCast DEBUG&amp;gt; Epoche 6000: Train MSE=0.000164, Val MSE=0.000333, Val MAE=0.014735, Val MedAE=0.013016, Bit_Fail=0&lt;br /&gt;
2026.01.01 18:23:00.239 1: SolCast DEBUG&amp;gt; Epoche 6100: Train MSE=0.000164, Val MSE=0.000286, Val MAE=0.012652, Val MedAE=0.009522, Bit_Fail=0&lt;br /&gt;
2026.01.01 18:23:16.145 1: SolCast DEBUG&amp;gt; Epoche 6200: Train MSE=0.000164, Val MSE=0.000283, Val MAE=0.012623, Val MedAE=0.009526, Bit_Fail=0&lt;br /&gt;
2026.01.01 18:23:31.970 1: SolCast DEBUG&amp;gt; Epoche 6300: Train MSE=0.000164, Val MSE=0.000294, Val MAE=0.013520, Val MedAE=0.011589, Bit_Fail=0&lt;br /&gt;
2026.01.01 18:23:48.995 1: SolCast DEBUG&amp;gt; Epoche 6400: Train MSE=0.000164, Val MSE=0.000290, Val MAE=0.013238, Val MedAE=0.011187, Bit_Fail=0&lt;br /&gt;
2026.01.01 18:24:04.958 1: SolCast DEBUG&amp;gt; Epoche 6500: Train MSE=0.000164, Val MSE=0.000283, Val MAE=0.013087, Val MedAE=0.011112, Bit_Fail=0&lt;br /&gt;
2026.01.01 18:24:20.473 1: SolCast DEBUG&amp;gt; Epoche 6600: Train MSE=0.000164, Val MSE=0.000291, Val MAE=0.012657, Val MedAE=0.009198, Bit_Fail=0&lt;br /&gt;
2026.01.01 18:24:35.890 1: SolCast DEBUG&amp;gt; Epoche 6700: Train MSE=0.000164, Val MSE=0.000290, Val MAE=0.013459, Val MedAE=0.011749, Bit_Fail=0&lt;br /&gt;
2026.01.01 18:24:52.891 1: SolCast DEBUG&amp;gt; Epoche 6800: Train MSE=0.000164, Val MSE=0.000289, Val MAE=0.013364, Val MedAE=0.011509, Bit_Fail=0&lt;br /&gt;
2026.01.01 18:25:08.802 1: SolCast DEBUG&amp;gt; Epoche 6900: Train MSE=0.000164, Val MSE=0.000283, Val MAE=0.013144, Val MedAE=0.011185, Bit_Fail=0&lt;br /&gt;
2026.01.01 18:25:24.371 1: SolCast DEBUG&amp;gt; Epoche 7000: Train MSE=0.000164, Val MSE=0.000278, Val MAE=0.012650, Val MedAE=0.009934, Bit_Fail=0&lt;br /&gt;
2026.01.01 18:25:39.968 1: SolCast DEBUG&amp;gt; Epoche 7100: Train MSE=0.000164, Val MSE=0.000320, Val MAE=0.014405, Val MedAE=0.012857, Bit_Fail=0&lt;br /&gt;
2026.01.01 18:25:56.650 1: SolCast DEBUG&amp;gt; Epoche 7200: Train MSE=0.000164, Val MSE=0.000281, Val MAE=0.013030, Val MedAE=0.011100, Bit_Fail=0&lt;br /&gt;
2026.01.01 18:26:12.530 1: SolCast DEBUG&amp;gt; Epoche 7300: Train MSE=0.000164, Val MSE=0.000277, Val MAE=0.012556, Val MedAE=0.009580, Bit_Fail=0&lt;br /&gt;
2026.01.01 18:26:28.387 1: SolCast DEBUG&amp;gt; Epoche 7400: Train MSE=0.000164, Val MSE=0.000275, Val MAE=0.012574, Val MedAE=0.009786, Bit_Fail=0&lt;br /&gt;
2026.01.01 18:26:44.426 1: SolCast DEBUG&amp;gt; Epoche 7500: Train MSE=0.000164, Val MSE=0.000282, Val MAE=0.013197, Val MedAE=0.011348, Bit_Fail=0&lt;br /&gt;
2026.01.01 18:27:00.108 1: SolCast DEBUG&amp;gt; Epoche 7600: Train MSE=0.000164, Val MSE=0.000279, Val MAE=0.012521, Val MedAE=0.009349, Bit_Fail=0&lt;br /&gt;
2026.01.01 18:27:17.631 1: SolCast DEBUG&amp;gt; Epoche 7700: Train MSE=0.000164, Val MSE=0.000334, Val MAE=0.014836, Val MedAE=0.013130, Bit_Fail=0&lt;br /&gt;
2026.01.01 18:27:19.201 1: SolCast DEBUG&amp;gt; Early stopping bei Epoche 7710 (no improvement since 2000 epochs)&lt;br /&gt;
2026.01.01 18:27:19.203 1: === Snapshot-Statistik ===&lt;br /&gt;
2026.01.01 18:27:19.206 1: Metric-Improvement Snapshots: 59 (letzte Epoche: 5710)&lt;br /&gt;
2026.01.01 18:27:19.208 1: Bit-Improvement Snapshots:    3 (letzte Epoche: 153)&lt;br /&gt;
2026.01.01 18:27:19.211 1: Bit-Tradeoff Snapshots:       0 (letzte Epoche: 0)&lt;br /&gt;
2026.01.01 18:27:19.222 1: SolCast DEBUG&amp;gt; Best Snapshot reloaded from Epoche 5710: Train MSE=0.000164, Val MSE=0.000292, Val MAE=0.012617, Val MedAE=0.009052, Bit_Fail=0,&lt;br /&gt;
2026.01.01 18:27:19.225 1: SolCast DEBUG&amp;gt; Run Validation Test with 20% of Input data ...&lt;br /&gt;
2026.01.01 18:27:19.269 1: SolCast DEBUG&amp;gt; Validation finished - Best Training MSE=0.000164, Validation MSE=0.000292, Validation Bit_Fail=0&lt;br /&gt;
2026.01.01 18:27:19.272 1: SolCast DEBUG&amp;gt; Retrain check -&amp;gt; &lt;br /&gt;
-- In Normalization Space: -- &lt;br /&gt;
Train MSE=0.000164 &lt;br /&gt;
Val MSE=0.000292 &lt;br /&gt;
Val Mean=0.0003006352 &lt;br /&gt;
VAL/TRAIN MSE Ratio=1.778317 (limit=2.5) &lt;br /&gt;
Diff=0.000128 (limit=0.005) &lt;br /&gt;
ValStd=0.0000223852 (limit=7.51588070934039e-05) &lt;br /&gt;
-- At Original Scale: -- &lt;br /&gt;
MAE=207.818584965433 &lt;br /&gt;
RMSE/MAE=1.3545 (limit=1.5) &lt;br /&gt;
Slope=0.930345 (limit=0.7 .. 1.3) &lt;br /&gt;
Bias=-1.81 (limit=+-103.909292482717) &lt;br /&gt;
R2=0.896675647742538 &lt;br /&gt;
P95=565.7317 (limit=831.274339861732) &lt;br /&gt;
P99=813.5925 (limit=1662.54867972346) &lt;br /&gt;
-- Robustness Indicators: -- &lt;br /&gt;
RMSE relative=60 (limit=20) &lt;br /&gt;
BitFail=0 (limit=5) &lt;br /&gt;
BitFailRate=0.0000 (limit=0.1) &lt;br /&gt;
-&amp;gt; Retrain&lt;br /&gt;
2026.01.01 18:27:19.280 1: SolCast DEBUG&amp;gt; Retry attempt 1 with Seed=16816002&lt;br /&gt;
2026.01.01 18:27:19.287 1: SolCast DEBUG&amp;gt; AI FANN Training started with Params:&lt;br /&gt;
num input datasets=7314, &lt;br /&gt;
training algo=FANN_TRAIN_INCREMENTAL, &lt;br /&gt;
output AF=LINEAR, &lt;br /&gt;
hidden AF=SIGMOID, &lt;br /&gt;
hidden Neurons=80,40,20, &lt;br /&gt;
hidden steepness=0.9, &lt;br /&gt;
Epoches=15000, &lt;br /&gt;
mse_error=0.001, &lt;br /&gt;
learning rate=0.00500, &lt;br /&gt;
learning momentum=0.7, &lt;br /&gt;
Data sharing=split after shuffle of training data and use AI internal shuffle (Train=5851, Test=1462), &lt;br /&gt;
Data shuffle=2 (period=10)&lt;br /&gt;
2026.01.01 18:27:19.448 1: SolCast DEBUG&amp;gt; Epoche 1: Train MSE=0.002204, Val MSE=0.002840, Val MAE=0.031967, Val MedAE=0.027950, Bit_Fail=2 -&amp;gt; Snap saved (bit improved)&lt;br /&gt;
2026.01.01 18:27:27.760 1: SolCast DEBUG&amp;gt; Epoche 55: Train MSE=0.001883, Val MSE=0.002825, Val MAE=0.030076, Val MedAE=0.017712, Bit_Fail=2 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 18:27:27.909 1: SolCast DEBUG&amp;gt; Epoche 56: Train MSE=0.001848, Val MSE=0.002787, Val MAE=0.029877, Val MedAE=0.017254, Bit_Fail=2 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 18:27:28.093 1: SolCast DEBUG&amp;gt; Epoche 57: Train MSE=0.001806, Val MSE=0.002742, Val MAE=0.029648, Val MedAE=0.016877, Bit_Fail=2 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 18:27:28.250 1: SolCast DEBUG&amp;gt; Epoche 58: Train MSE=0.001759, Val MSE=0.002690, Val MAE=0.029392, Val MedAE=0.016720, Bit_Fail=2 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 18:27:28.410 1: SolCast DEBUG&amp;gt; Epoche 59: Train MSE=0.001704, Val MSE=0.002632, Val MAE=0.029109, Val MedAE=0.016686, Bit_Fail=2 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 18:27:34.691 1: SolCast DEBUG&amp;gt; Epoche 100: Train MSE=0.001704, Val MSE=0.001681, Val MAE=0.026853, Val MedAE=0.019229, Bit_Fail=1&lt;br /&gt;
2026.01.01 18:27:39.980 1: SolCast DEBUG&amp;gt; Epoche 133: Train MSE=0.000733, Val MSE=0.001267, Val MAE=0.023793, Val MedAE=0.017436, Bit_Fail=1 -&amp;gt; Snap saved (bit improved)&lt;br /&gt;
2026.01.01 18:27:40.226 1: SolCast DEBUG&amp;gt; Epoche 134: Train MSE=0.000722, Val MSE=0.001247, Val MAE=0.023624, Val MedAE=0.017294, Bit_Fail=1 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 18:27:40.470 1: SolCast DEBUG&amp;gt; Epoche 135: Train MSE=0.000710, Val MSE=0.001225, Val MAE=0.023450, Val MedAE=0.017111, Bit_Fail=1 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 18:27:40.655 1: SolCast DEBUG&amp;gt; Epoche 136: Train MSE=0.000698, Val MSE=0.001204, Val MAE=0.023271, Val MedAE=0.017081, Bit_Fail=1 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 18:27:40.821 1: SolCast DEBUG&amp;gt; Epoche 137: Train MSE=0.000686, Val MSE=0.001182, Val MAE=0.023085, Val MedAE=0.017060, Bit_Fail=1 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 18:27:40.979 1: SolCast DEBUG&amp;gt; Epoche 138: Train MSE=0.000674, Val MSE=0.001160, Val MAE=0.022895, Val MedAE=0.016883, Bit_Fail=1 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 18:27:41.140 1: SolCast DEBUG&amp;gt; Epoche 139: Train MSE=0.000661, Val MSE=0.001138, Val MAE=0.022701, Val MedAE=0.016711, Bit_Fail=1 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 18:27:41.929 1: SolCast DEBUG&amp;gt; Epoche 144: Train MSE=0.000598, Val MSE=0.000999, Val MAE=0.021734, Val MedAE=0.016540, Bit_Fail=1 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 18:27:42.090 1: SolCast DEBUG&amp;gt; Epoche 145: Train MSE=0.000586, Val MSE=0.000978, Val MAE=0.021522, Val MedAE=0.016349, Bit_Fail=1 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 18:27:42.280 1: SolCast DEBUG&amp;gt; Epoche 146: Train MSE=0.000574, Val MSE=0.000958, Val MAE=0.021306, Val MedAE=0.016107, Bit_Fail=1 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 18:27:42.459 1: SolCast DEBUG&amp;gt; Epoche 147: Train MSE=0.000562, Val MSE=0.000937, Val MAE=0.021088, Val MedAE=0.015901, Bit_Fail=1 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 18:27:42.613 1: SolCast DEBUG&amp;gt; Epoche 148: Train MSE=0.000550, Val MSE=0.000918, Val MAE=0.020870, Val MedAE=0.015669, Bit_Fail=1 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 18:27:42.760 1: SolCast DEBUG&amp;gt; Epoche 149: Train MSE=0.000539, Val MSE=0.000898, Val MAE=0.020654, Val MedAE=0.015555, Bit_Fail=1 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 18:27:43.090 1: SolCast DEBUG&amp;gt; Epoche 151: Train MSE=0.000519, Val MSE=0.000853, Val MAE=0.020213, Val MedAE=0.015324, Bit_Fail=1 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 18:27:43.274 1: SolCast DEBUG&amp;gt; Epoche 152: Train MSE=0.000508, Val MSE=0.000834, Val MAE=0.019989, Val MedAE=0.015218, Bit_Fail=1 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 18:27:43.427 1: SolCast DEBUG&amp;gt; Epoche 153: Train MSE=0.000498, Val MSE=0.000815, Val MAE=0.019775, Val MedAE=0.015098, Bit_Fail=1 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 18:27:43.588 1: SolCast DEBUG&amp;gt; Epoche 154: Train MSE=0.000489, Val MSE=0.000798, Val MAE=0.019572, Val MedAE=0.015089, Bit_Fail=1 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 18:27:43.765 1: SolCast DEBUG&amp;gt; Epoche 155: Train MSE=0.000480, Val MSE=0.000781, Val MAE=0.019377, Val MedAE=0.014943, Bit_Fail=1 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 18:27:43.922 1: SolCast DEBUG&amp;gt; Epoche 156: Train MSE=0.000471, Val MSE=0.000765, Val MAE=0.019192, Val MedAE=0.014814, Bit_Fail=1 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 18:27:44.099 1: SolCast DEBUG&amp;gt; Epoche 157: Train MSE=0.000463, Val MSE=0.000750, Val MAE=0.019017, Val MedAE=0.014635, Bit_Fail=1 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 18:27:44.451 1: SolCast DEBUG&amp;gt; Epoche 159: Train MSE=0.000448, Val MSE=0.000722, Val MAE=0.018698, Val MedAE=0.014553, Bit_Fail=1 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 18:27:45.216 1: SolCast DEBUG&amp;gt; Epoche 164: Train MSE=0.000417, Val MSE=0.000649, Val MAE=0.018205, Val MedAE=0.014516, Bit_Fail=0 -&amp;gt; Snap saved (bit improved)&lt;br /&gt;
2026.01.01 18:27:45.399 1: SolCast DEBUG&amp;gt; Epoche 165: Train MSE=0.000412, Val MSE=0.000640, Val MAE=0.018099, Val MedAE=0.014421, Bit_Fail=0 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 18:27:45.558 1: SolCast DEBUG&amp;gt; Epoche 166: Train MSE=0.000407, Val MSE=0.000632, Val MAE=0.017999, Val MedAE=0.014286, Bit_Fail=0 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 18:27:45.726 1: SolCast DEBUG&amp;gt; Epoche 167: Train MSE=0.000402, Val MSE=0.000624, Val MAE=0.017906, Val MedAE=0.014177, Bit_Fail=0 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 18:27:45.885 1: SolCast DEBUG&amp;gt; Epoche 168: Train MSE=0.000398, Val MSE=0.000617, Val MAE=0.017820, Val MedAE=0.014094, Bit_Fail=0 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 18:27:46.051 1: SolCast DEBUG&amp;gt; Epoche 169: Train MSE=0.000394, Val MSE=0.000610, Val MAE=0.017740, Val MedAE=0.014051, Bit_Fail=0 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 18:27:50.636 1: SolCast DEBUG&amp;gt; Epoche 200: Train MSE=0.000394, Val MSE=0.000539, Val MAE=0.016987, Val MedAE=0.013961, Bit_Fail=0&lt;br /&gt;
2026.01.01 18:27:50.644 1: SolCast DEBUG&amp;gt; Epoche 200: Train MSE=0.000340, Val MSE=0.000539, Val MAE=0.016987, Val MedAE=0.013961, Bit_Fail=0 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 18:27:51.095 1: SolCast DEBUG&amp;gt; Epoche 203: Train MSE=0.000338, Val MSE=0.000538, Val MAE=0.016940, Val MedAE=0.013888, Bit_Fail=0 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 18:27:55.790 1: SolCast DEBUG&amp;gt; Epoche 233: Train MSE=0.000321, Val MSE=0.000535, Val MAE=0.016852, Val MedAE=0.013875, Bit_Fail=0 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 18:28:03.726 1: SolCast DEBUG&amp;gt; Epoche 281: Train MSE=0.000308, Val MSE=0.000524, Val MAE=0.016435, Val MedAE=0.013831, Bit_Fail=0 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 18:28:04.363 1: SolCast DEBUG&amp;gt; Epoche 285: Train MSE=0.000307, Val MSE=0.000523, Val MAE=0.016426, Val MedAE=0.013765, Bit_Fail=0 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 18:28:06.794 1: SolCast DEBUG&amp;gt; Epoche 300: Train MSE=0.000307, Val MSE=0.000550, Val MAE=0.017907, Val MedAE=0.015240, Bit_Fail=0&lt;br /&gt;
2026.01.01 18:28:08.309 1: SolCast DEBUG&amp;gt; Epoche 310: Train MSE=0.000306, Val MSE=0.000520, Val MAE=0.016274, Val MedAE=0.013632, Bit_Fail=0 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 18:28:08.614 1: SolCast DEBUG&amp;gt; Epoche 312: Train MSE=0.000305, Val MSE=0.000519, Val MAE=0.016257, Val MedAE=0.013585, Bit_Fail=0 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 18:28:09.360 1: SolCast DEBUG&amp;gt; Epoche 317: Train MSE=0.000304, Val MSE=0.000518, Val MAE=0.016235, Val MedAE=0.013539, Bit_Fail=0 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 18:28:18.741 1: SolCast DEBUG&amp;gt; Epoche 370: Train MSE=0.000293, Val MSE=0.000514, Val MAE=0.016166, Val MedAE=0.013205, Bit_Fail=0 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 18:28:23.398 1: SolCast DEBUG&amp;gt; Epoche 400: Train MSE=0.000293, Val MSE=0.000498, Val MAE=0.016166, Val MedAE=0.013530, Bit_Fail=0&lt;br /&gt;
2026.01.01 18:28:27.156 1: SolCast DEBUG&amp;gt; Epoche 420: Train MSE=0.000286, Val MSE=0.000497, Val MAE=0.015836, Val MedAE=0.012658, Bit_Fail=0 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 18:28:27.956 1: SolCast DEBUG&amp;gt; Epoche 424: Train MSE=0.000286, Val MSE=0.000496, Val MAE=0.015803, Val MedAE=0.012630, Bit_Fail=0 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 18:28:28.927 1: SolCast DEBUG&amp;gt; Epoche 429: Train MSE=0.000285, Val MSE=0.000495, Val MAE=0.015779, Val MedAE=0.012568, Bit_Fail=0 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 18:28:37.533 1: SolCast DEBUG&amp;gt; Epoche 470: Train MSE=0.000278, Val MSE=0.000490, Val MAE=0.015754, Val MedAE=0.012467, Bit_Fail=0 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 18:28:44.668 1: SolCast DEBUG&amp;gt; Epoche 500: Train MSE=0.000278, Val MSE=0.000512, Val MAE=0.015775, Val MedAE=0.011966, Bit_Fail=0&lt;br /&gt;
2026.01.01 18:28:49.391 1: SolCast DEBUG&amp;gt; Epoche 520: Train MSE=0.000270, Val MSE=0.000477, Val MAE=0.015651, Val MedAE=0.012339, Bit_Fail=0 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 18:28:53.585 1: SolCast DEBUG&amp;gt; Epoche 539: Train MSE=0.000270, Val MSE=0.000475, Val MAE=0.015648, Val MedAE=0.012191, Bit_Fail=0 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 18:29:05.842 1: SolCast DEBUG&amp;gt; Epoche 590: Train MSE=0.000265, Val MSE=0.000474, Val MAE=0.015526, Val MedAE=0.012056, Bit_Fail=0 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 18:29:08.065 1: SolCast DEBUG&amp;gt; Epoche 600: Train MSE=0.000265, Val MSE=0.000468, Val MAE=0.015894, Val MedAE=0.012751, Bit_Fail=0&lt;br /&gt;
2026.01.01 18:29:28.474 1: SolCast DEBUG&amp;gt; Epoche 690: Train MSE=0.000256, Val MSE=0.000470, Val MAE=0.015276, Val MedAE=0.011856, Bit_Fail=0 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 18:29:30.383 1: SolCast DEBUG&amp;gt; Epoche 699: Train MSE=0.000255, Val MSE=0.000467, Val MAE=0.015250, Val MedAE=0.011836, Bit_Fail=0 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 18:29:30.639 1: SolCast DEBUG&amp;gt; Epoche 700: Train MSE=0.000255, Val MSE=0.000478, Val MAE=0.016548, Val MedAE=0.013898, Bit_Fail=0&lt;br /&gt;
2026.01.01 18:29:52.830 1: SolCast DEBUG&amp;gt; Epoche 800: Train MSE=0.000255, Val MSE=0.000465, Val MAE=0.016306, Val MedAE=0.013391, Bit_Fail=0&lt;br /&gt;
2026.01.01 18:30:04.934 1: SolCast DEBUG&amp;gt; Epoche 850: Train MSE=0.000244, Val MSE=0.000461, Val MAE=0.015129, Val MedAE=0.011815, Bit_Fail=0 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 18:30:17.777 1: SolCast DEBUG&amp;gt; Epoche 900: Train MSE=0.000244, Val MSE=0.000448, Val MAE=0.015585, Val MedAE=0.012575, Bit_Fail=0&lt;br /&gt;
2026.01.01 18:30:40.850 1: SolCast DEBUG&amp;gt; Epoche 1000: Train MSE=0.000244, Val MSE=0.000450, Val MAE=0.015295, Val MedAE=0.012324, Bit_Fail=0&lt;br /&gt;
2026.01.01 18:30:56.457 1: SolCast DEBUG&amp;gt; Epoche 1100: Train MSE=0.000244, Val MSE=0.000443, Val MAE=0.015695, Val MedAE=0.012932, Bit_Fail=0&lt;br /&gt;
2026.01.01 18:31:07.100 1: SolCast DEBUG&amp;gt; Epoche 1160: Train MSE=0.000229, Val MSE=0.000432, Val MAE=0.014957, Val MedAE=0.011802, Bit_Fail=0 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 18:31:13.278 1: SolCast DEBUG&amp;gt; Epoche 1200: Train MSE=0.000229, Val MSE=0.000439, Val MAE=0.015822, Val MedAE=0.012868, Bit_Fail=0&lt;br /&gt;
2026.01.01 18:31:29.055 1: SolCast DEBUG&amp;gt; Epoche 1300: Train MSE=0.000229, Val MSE=0.000435, Val MAE=0.015613, Val MedAE=0.012747, Bit_Fail=0&lt;br /&gt;
2026.01.01 18:31:43.689 1: SolCast DEBUG&amp;gt; Epoche 1390: Train MSE=0.000219, Val MSE=0.000426, Val MAE=0.014936, Val MedAE=0.011470, Bit_Fail=0 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 18:31:45.625 1: SolCast DEBUG&amp;gt; Epoche 1400: Train MSE=0.000219, Val MSE=0.000414, Val MAE=0.014586, Val MedAE=0.011035, Bit_Fail=0&lt;br /&gt;
2026.01.01 18:31:45.635 1: SolCast DEBUG&amp;gt; Epoche 1400: Train MSE=0.000218, Val MSE=0.000414, Val MAE=0.014586, Val MedAE=0.011035, Bit_Fail=0 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 18:32:02.161 1: SolCast DEBUG&amp;gt; Epoche 1500: Train MSE=0.000218, Val MSE=0.000449, Val MAE=0.016432, Val MedAE=0.013464, Bit_Fail=0&lt;br /&gt;
2026.01.01 18:32:18.086 1: SolCast DEBUG&amp;gt; Epoche 1600: Train MSE=0.000218, Val MSE=0.000403, Val MAE=0.014934, Val MedAE=0.011695, Bit_Fail=0&lt;br /&gt;
2026.01.01 18:32:32.114 1: SolCast DEBUG&amp;gt; Epoche 1690: Train MSE=0.000209, Val MSE=0.000404, Val MAE=0.014320, Val MedAE=0.010320, Bit_Fail=0 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 18:32:33.832 1: SolCast DEBUG&amp;gt; Epoche 1700: Train MSE=0.000209, Val MSE=0.000428, Val MAE=0.016037, Val MedAE=0.012994, Bit_Fail=0&lt;br /&gt;
2026.01.01 18:32:49.865 1: SolCast DEBUG&amp;gt; Epoche 1800: Train MSE=0.000209, Val MSE=0.000410, Val MAE=0.015653, Val MedAE=0.012577, Bit_Fail=0&lt;br /&gt;
2026.01.01 18:33:02.182 1: SolCast DEBUG&amp;gt; Epoche 1870: Train MSE=0.000205, Val MSE=0.000383, Val MAE=0.014067, Val MedAE=0.010133, Bit_Fail=0 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 18:33:06.890 1: SolCast DEBUG&amp;gt; Epoche 1900: Train MSE=0.000205, Val MSE=0.000376, Val MAE=0.014446, Val MedAE=0.011115, Bit_Fail=0&lt;br /&gt;
2026.01.01 18:33:22.858 1: SolCast DEBUG&amp;gt; Epoche 2000: Train MSE=0.000205, Val MSE=0.000368, Val MAE=0.014258, Val MedAE=0.010752, Bit_Fail=0&lt;br /&gt;
2026.01.01 18:33:38.578 1: SolCast DEBUG&amp;gt; Epoche 2100: Train MSE=0.000205, Val MSE=0.000440, Val MAE=0.016680, Val MedAE=0.014515, Bit_Fail=0&lt;br /&gt;
2026.01.01 18:33:41.611 1: SolCast DEBUG&amp;gt; Epoche 2120: Train MSE=0.000198, Val MSE=0.000367, Val MAE=0.013801, Val MedAE=0.009661, Bit_Fail=0 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 18:33:54.354 1: SolCast DEBUG&amp;gt; Epoche 2200: Train MSE=0.000198, Val MSE=0.000417, Val MAE=0.016140, Val MedAE=0.013713, Bit_Fail=0&lt;br /&gt;
2026.01.01 18:34:11.686 1: SolCast DEBUG&amp;gt; Epoche 2300: Train MSE=0.000198, Val MSE=0.000362, Val MAE=0.014348, Val MedAE=0.010921, Bit_Fail=0&lt;br /&gt;
2026.01.01 18:34:18.148 1: SolCast DEBUG&amp;gt; Epoche 2340: Train MSE=0.000195, Val MSE=0.000356, Val MAE=0.013592, Val MedAE=0.009300, Bit_Fail=0 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 18:34:27.489 1: SolCast DEBUG&amp;gt; Epoche 2400: Train MSE=0.000195, Val MSE=0.000355, Val MAE=0.014051, Val MedAE=0.010411, Bit_Fail=0&lt;br /&gt;
2026.01.01 18:34:43.305 1: SolCast DEBUG&amp;gt; Epoche 2500: Train MSE=0.000195, Val MSE=0.000349, Val MAE=0.013577, Val MedAE=0.009666, Bit_Fail=0&lt;br /&gt;
2026.01.01 18:34:58.973 1: SolCast DEBUG&amp;gt; Epoche 2600: Train MSE=0.000195, Val MSE=0.000377, Val MAE=0.015170, Val MedAE=0.012456, Bit_Fail=0&lt;br /&gt;
2026.01.01 18:35:15.415 1: SolCast DEBUG&amp;gt; Epoche 2700: Train MSE=0.000195, Val MSE=0.000340, Val MAE=0.013970, Val MedAE=0.010876, Bit_Fail=0&lt;br /&gt;
2026.01.01 18:35:32.017 1: SolCast DEBUG&amp;gt; Epoche 2800: Train MSE=0.000195, Val MSE=0.000382, Val MAE=0.015341, Val MedAE=0.012854, Bit_Fail=0&lt;br /&gt;
2026.01.01 18:35:47.827 1: SolCast DEBUG&amp;gt; Epoche 2900: Train MSE=0.000195, Val MSE=0.000328, Val MAE=0.013411, Val MedAE=0.009790, Bit_Fail=0&lt;br /&gt;
2026.01.01 18:35:52.375 1: SolCast DEBUG&amp;gt; Epoche 2931: Train MSE=0.000186, Val MSE=0.000331, Val MAE=0.013276, Val MedAE=0.009297, Bit_Fail=0 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 18:36:04.885 1: SolCast DEBUG&amp;gt; Epoche 3000: Train MSE=0.000186, Val MSE=0.000333, Val MAE=0.013785, Val MedAE=0.010478, Bit_Fail=0&lt;br /&gt;
2026.01.01 18:36:20.725 1: SolCast DEBUG&amp;gt; Epoche 3100: Train MSE=0.000186, Val MSE=0.000329, Val MAE=0.013228, Val MedAE=0.009287, Bit_Fail=0&lt;br /&gt;
2026.01.01 18:36:20.735 1: SolCast DEBUG&amp;gt; Epoche 3100: Train MSE=0.000184, Val MSE=0.000329, Val MAE=0.013228, Val MedAE=0.009287, Bit_Fail=0 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 18:36:34.489 1: SolCast DEBUG&amp;gt; Epoche 3180: Train MSE=0.000182, Val MSE=0.000327, Val MAE=0.013125, Val MedAE=0.008939, Bit_Fail=0 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 18:36:38.029 1: SolCast DEBUG&amp;gt; Epoche 3200: Train MSE=0.000182, Val MSE=0.000320, Val MAE=0.013228, Val MedAE=0.009632, Bit_Fail=0&lt;br /&gt;
2026.01.01 18:36:45.842 1: SolCast DEBUG&amp;gt; Epoche 3250: Train MSE=0.000183, Val MSE=0.000323, Val MAE=0.013096, Val MedAE=0.008886, Bit_Fail=0 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 18:36:53.303 1: SolCast DEBUG&amp;gt; Epoche 3300: Train MSE=0.000183, Val MSE=0.000320, Val MAE=0.013318, Val MedAE=0.009760, Bit_Fail=0&lt;br /&gt;
2026.01.01 18:37:09.545 1: SolCast DEBUG&amp;gt; Epoche 3400: Train MSE=0.000183, Val MSE=0.000327, Val MAE=0.013922, Val MedAE=0.011272, Bit_Fail=0&lt;br /&gt;
2026.01.01 18:37:24.189 1: SolCast DEBUG&amp;gt; Epoche 3490: Train MSE=0.000180, Val MSE=0.000319, Val MAE=0.013041, Val MedAE=0.008820, Bit_Fail=0 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 18:37:25.723 1: SolCast DEBUG&amp;gt; Epoche 3500: Train MSE=0.000180, Val MSE=0.000323, Val MAE=0.013588, Val MedAE=0.010386, Bit_Fail=0&lt;br /&gt;
2026.01.01 18:37:47.590 1: SolCast DEBUG&amp;gt; Epoche 3600: Train MSE=0.000180, Val MSE=0.000323, Val MAE=0.013730, Val MedAE=0.010947, Bit_Fail=0&lt;br /&gt;
2026.01.01 18:37:53.258 1: SolCast DEBUG&amp;gt; Epoche 3630: Train MSE=0.000178, Val MSE=0.000318, Val MAE=0.012948, Val MedAE=0.008677, Bit_Fail=0 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 18:38:09.034 1: SolCast DEBUG&amp;gt; Epoche 3700: Train MSE=0.000178, Val MSE=0.000312, Val MAE=0.013378, Val MedAE=0.010497, Bit_Fail=0&lt;br /&gt;
2026.01.01 18:38:30.192 1: SolCast DEBUG&amp;gt; Epoche 3800: Train MSE=0.000178, Val MSE=0.000312, Val MAE=0.013379, Val MedAE=0.010674, Bit_Fail=0&lt;br /&gt;
2026.01.01 18:38:52.224 1: SolCast DEBUG&amp;gt; Epoche 3900: Train MSE=0.000178, Val MSE=0.000318, Val MAE=0.012933, Val MedAE=0.008471, Bit_Fail=0&lt;br /&gt;
2026.01.01 18:39:13.603 1: SolCast DEBUG&amp;gt; Epoche 4000: Train MSE=0.000178, Val MSE=0.000306, Val MAE=0.013228, Val MedAE=0.010395, Bit_Fail=0&lt;br /&gt;
2026.01.01 18:39:34.047 1: SolCast DEBUG&amp;gt; Epoche 4100: Train MSE=0.000178, Val MSE=0.000318, Val MAE=0.013793, Val MedAE=0.011333, Bit_Fail=0&lt;br /&gt;
2026.01.01 18:39:54.812 1: SolCast DEBUG&amp;gt; Epoche 4200: Train MSE=0.000178, Val MSE=0.000309, Val MAE=0.013388, Val MedAE=0.010831, Bit_Fail=0&lt;br /&gt;
2026.01.01 18:40:02.202 1: SolCast DEBUG&amp;gt; Epoche 4230: Train MSE=0.000175, Val MSE=0.000309, Val MAE=0.012845, Val MedAE=0.008584, Bit_Fail=0 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 18:40:16.021 1: SolCast DEBUG&amp;gt; Epoche 4300: Train MSE=0.000175, Val MSE=0.000303, Val MAE=0.013012, Val MedAE=0.009822, Bit_Fail=0&lt;br /&gt;
2026.01.01 18:40:34.355 1: SolCast DEBUG&amp;gt; Epoche 4400: Train MSE=0.000175, Val MSE=0.000310, Val MAE=0.012884, Val MedAE=0.008766, Bit_Fail=0&lt;br /&gt;
2026.01.01 18:40:57.218 1: SolCast DEBUG&amp;gt; Epoche 4500: Train MSE=0.000175, Val MSE=0.000302, Val MAE=0.013128, Val MedAE=0.010345, Bit_Fail=0&lt;br /&gt;
2026.01.01 18:41:22.220 1: SolCast DEBUG&amp;gt; Epoche 4600: Train MSE=0.000175, Val MSE=0.000302, Val MAE=0.013191, Val MedAE=0.010385, Bit_Fail=0&lt;br /&gt;
2026.01.01 18:41:43.986 1: SolCast DEBUG&amp;gt; Epoche 4700: Train MSE=0.000175, Val MSE=0.000301, Val MAE=0.013269, Val MedAE=0.010709, Bit_Fail=0&lt;br /&gt;
2026.01.01 18:42:05.591 1: SolCast DEBUG&amp;gt; Epoche 4800: Train MSE=0.000175, Val MSE=0.000327, Val MAE=0.012984, Val MedAE=0.008358, Bit_Fail=0&lt;br /&gt;
2026.01.01 18:42:28.817 1: SolCast DEBUG&amp;gt; Epoche 4900: Train MSE=0.000175, Val MSE=0.000300, Val MAE=0.013196, Val MedAE=0.010534, Bit_Fail=0&lt;br /&gt;
2026.01.01 18:42:51.092 1: SolCast DEBUG&amp;gt; Epoche 5000: Train MSE=0.000175, Val MSE=0.000298, Val MAE=0.013025, Val MedAE=0.010142, Bit_Fail=0&lt;br /&gt;
2026.01.01 18:43:13.597 1: SolCast DEBUG&amp;gt; Epoche 5100: Train MSE=0.000175, Val MSE=0.000296, Val MAE=0.012895, Val MedAE=0.009981, Bit_Fail=0&lt;br /&gt;
2026.01.01 18:43:36.900 1: SolCast DEBUG&amp;gt; Epoche 5200: Train MSE=0.000175, Val MSE=0.000298, Val MAE=0.012955, Val MedAE=0.009983, Bit_Fail=0&lt;br /&gt;
2026.01.01 18:43:58.243 1: SolCast DEBUG&amp;gt; Epoche 5300: Train MSE=0.000175, Val MSE=0.000305, Val MAE=0.013474, Val MedAE=0.010999, Bit_Fail=0&lt;br /&gt;
2026.01.01 18:44:20.399 1: SolCast DEBUG&amp;gt; Epoche 5400: Train MSE=0.000175, Val MSE=0.000296, Val MAE=0.012941, Val MedAE=0.009892, Bit_Fail=0&lt;br /&gt;
2026.01.01 18:44:37.326 1: SolCast DEBUG&amp;gt; Epoche 5500: Train MSE=0.000175, Val MSE=0.000301, Val MAE=0.013345, Val MedAE=0.010900, Bit_Fail=0&lt;br /&gt;
2026.01.01 18:44:53.049 1: SolCast DEBUG&amp;gt; Epoche 5600: Train MSE=0.000175, Val MSE=0.000297, Val MAE=0.012997, Val MedAE=0.010176, Bit_Fail=0&lt;br /&gt;
2026.01.01 18:45:10.274 1: SolCast DEBUG&amp;gt; Epoche 5700: Train MSE=0.000175, Val MSE=0.000303, Val MAE=0.013484, Val MedAE=0.011050, Bit_Fail=0&lt;br /&gt;
2026.01.01 18:45:27.329 1: SolCast DEBUG&amp;gt; Epoche 5800: Train MSE=0.000175, Val MSE=0.000300, Val MAE=0.013419, Val MedAE=0.011095, Bit_Fail=0&lt;br /&gt;
2026.01.01 18:45:42.673 1: SolCast DEBUG&amp;gt; Epoche 5900: Train MSE=0.000175, Val MSE=0.000295, Val MAE=0.013058, Val MedAE=0.010464, Bit_Fail=0&lt;br /&gt;
2026.01.01 18:45:59.007 1: SolCast DEBUG&amp;gt; Epoche 6000: Train MSE=0.000175, Val MSE=0.000299, Val MAE=0.012793, Val MedAE=0.009330, Bit_Fail=0&lt;br /&gt;
2026.01.01 18:46:14.775 1: SolCast DEBUG&amp;gt; Epoche 6100: Train MSE=0.000175, Val MSE=0.000294, Val MAE=0.013040, Val MedAE=0.010396, Bit_Fail=0&lt;br /&gt;
2026.01.01 18:46:29.912 1: SolCast DEBUG&amp;gt; Epoche 6200: Train MSE=0.000175, Val MSE=0.000305, Val MAE=0.013656, Val MedAE=0.011383, Bit_Fail=0&lt;br /&gt;
2026.01.01 18:46:34.543 1: SolCast DEBUG&amp;gt; Early stopping bei Epoche 6230 (no improvement since 2000 epochs)&lt;br /&gt;
2026.01.01 18:46:34.546 1: === Snapshot-Statistik ===&lt;br /&gt;
2026.01.01 18:46:34.547 1: Metric-Improvement Snapshots: 63 (letzte Epoche: 4230)&lt;br /&gt;
2026.01.01 18:46:34.549 1: Bit-Improvement Snapshots:    3 (letzte Epoche: 164)&lt;br /&gt;
2026.01.01 18:46:34.552 1: Bit-Tradeoff Snapshots:       0 (letzte Epoche: 0)&lt;br /&gt;
2026.01.01 18:46:34.559 1: SolCast DEBUG&amp;gt; Best Snapshot reloaded from Epoche 4230: Train MSE=0.000175, Val MSE=0.000309, Val MAE=0.012845, Val MedAE=0.008584, Bit_Fail=0,&lt;br /&gt;
2026.01.01 18:46:34.561 1: SolCast DEBUG&amp;gt; Run Validation Test with 20% of Input data ...&lt;br /&gt;
2026.01.01 18:46:34.602 1: SolCast DEBUG&amp;gt; Validation finished - Best Training MSE=0.000175, Validation MSE=0.000309, Validation Bit_Fail=0&lt;br /&gt;
2026.01.01 18:46:34.605 1: SolCast DEBUG&amp;gt; Retrain check -&amp;gt; &lt;br /&gt;
-- In Normalization Space: -- &lt;br /&gt;
Train MSE=0.000175 &lt;br /&gt;
Val MSE=0.000309 &lt;br /&gt;
Val Mean=0.0003042089 &lt;br /&gt;
VAL/TRAIN MSE Ratio=1.763249 (limit=2.5) &lt;br /&gt;
Diff=0.000134 (limit=0.005) &lt;br /&gt;
ValStd=0.0000076839 (limit=7.60522365479913e-05) &lt;br /&gt;
-- At Original Scale: -- &lt;br /&gt;
MAE=211.576764064665 &lt;br /&gt;
RMSE/MAE=1.3694 (limit=1.5) &lt;br /&gt;
Slope=0.935893 (limit=0.7 .. 1.3) &lt;br /&gt;
Bias=4.36 (limit=+-105.788382032332) &lt;br /&gt;
R2=0.890528595577006 &lt;br /&gt;
P95=585.8856 (limit=846.307056258658) &lt;br /&gt;
P99=968.7970 (limit=1692.61411251732) &lt;br /&gt;
-- Robustness Indicators: -- &lt;br /&gt;
RMSE relative=62 (limit=20) &lt;br /&gt;
BitFail=0 (limit=5) &lt;br /&gt;
BitFailRate=0.0000 (limit=0.1) &lt;br /&gt;
-&amp;gt; Retrain&lt;br /&gt;
2026.01.01 18:46:34.613 1: SolCast DEBUG&amp;gt; Retry attempt 2 with Seed=16325084&lt;br /&gt;
2026.01.01 18:46:34.619 1: SolCast DEBUG&amp;gt; AI FANN Training started with Params:&lt;br /&gt;
num input datasets=7314, &lt;br /&gt;
training algo=FANN_TRAIN_INCREMENTAL, &lt;br /&gt;
output AF=LINEAR, &lt;br /&gt;
hidden AF=SIGMOID, &lt;br /&gt;
hidden Neurons=80,40,20, &lt;br /&gt;
hidden steepness=0.9, &lt;br /&gt;
Epoches=15000, &lt;br /&gt;
mse_error=0.001, &lt;br /&gt;
learning rate=0.00500, &lt;br /&gt;
learning momentum=0.7, &lt;br /&gt;
Data sharing=split after shuffle of training data and use AI internal shuffle (Train=5851, Test=1462), &lt;br /&gt;
Data shuffle=2 (period=10)&lt;br /&gt;
2026.01.01 18:46:34.804 1: SolCast DEBUG&amp;gt; Epoche 1: Train MSE=0.002232, Val MSE=0.002836, Val MAE=0.031928, Val MedAE=0.027901, Bit_Fail=2 -&amp;gt; Snap saved (bit improved)&lt;br /&gt;
2026.01.01 18:46:35.250 1: SolCast DEBUG&amp;gt; Epoche 4: Train MSE=0.002191, Val MSE=0.002835, Val MAE=0.031923, Val MedAE=0.027895, Bit_Fail=2 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 18:46:35.703 1: SolCast DEBUG&amp;gt; Epoche 7: Train MSE=0.002187, Val MSE=0.002834, Val MAE=0.031919, Val MedAE=0.027892, Bit_Fail=2 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 18:46:42.846 1: SolCast DEBUG&amp;gt; Epoche 54: Train MSE=0.002012, Val MSE=0.002830, Val MAE=0.030056, Val MedAE=0.024398, Bit_Fail=2 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 18:46:42.997 1: SolCast DEBUG&amp;gt; Epoche 55: Train MSE=0.001999, Val MSE=0.002816, Val MAE=0.029980, Val MedAE=0.024096, Bit_Fail=2 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 18:46:43.151 1: SolCast DEBUG&amp;gt; Epoche 56: Train MSE=0.001984, Val MSE=0.002799, Val MAE=0.029892, Val MedAE=0.023790, Bit_Fail=2 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 18:46:43.302 1: SolCast DEBUG&amp;gt; Epoche 57: Train MSE=0.001966, Val MSE=0.002780, Val MAE=0.029791, Val MedAE=0.023427, Bit_Fail=2 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 18:46:43.459 1: SolCast DEBUG&amp;gt; Epoche 58: Train MSE=0.001945, Val MSE=0.002756, Val MAE=0.029676, Val MedAE=0.022958, Bit_Fail=2 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 18:46:43.623 1: SolCast DEBUG&amp;gt; Epoche 59: Train MSE=0.001920, Val MSE=0.002728, Val MAE=0.029540, Val MedAE=0.022374, Bit_Fail=2 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 18:46:44.667 1: SolCast DEBUG&amp;gt; Epoche 65: Train MSE=0.001639, Val MSE=0.002678, Val MAE=0.029156, Val MedAE=0.016122, Bit_Fail=2 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 18:46:50.165 1: SolCast DEBUG&amp;gt; Epoche 100: Train MSE=0.001639, Val MSE=0.001617, Val MAE=0.027389, Val MedAE=0.021329, Bit_Fail=1&lt;br /&gt;
2026.01.01 18:46:57.117 1: SolCast DEBUG&amp;gt; Epoche 140: Train MSE=0.000582, Val MSE=0.000982, Val MAE=0.021762, Val MedAE=0.016481, Bit_Fail=1 -&amp;gt; Snap saved (bit improved)&lt;br /&gt;
2026.01.01 18:46:57.374 1: SolCast DEBUG&amp;gt; Epoche 141: Train MSE=0.000571, Val MSE=0.000963, Val MAE=0.021553, Val MedAE=0.016314, Bit_Fail=1 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 18:46:57.625 1: SolCast DEBUG&amp;gt; Epoche 142: Train MSE=0.000559, Val MSE=0.000944, Val MAE=0.021340, Val MedAE=0.015998, Bit_Fail=1 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 18:46:57.814 1: SolCast DEBUG&amp;gt; Epoche 143: Train MSE=0.000548, Val MSE=0.000925, Val MAE=0.021128, Val MedAE=0.015847, Bit_Fail=1 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 18:46:58.048 1: SolCast DEBUG&amp;gt; Epoche 144: Train MSE=0.000537, Val MSE=0.000906, Val MAE=0.020921, Val MedAE=0.015525, Bit_Fail=1 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 18:46:58.217 1: SolCast DEBUG&amp;gt; Epoche 145: Train MSE=0.000526, Val MSE=0.000888, Val MAE=0.020716, Val MedAE=0.015427, Bit_Fail=1 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 18:46:58.387 1: SolCast DEBUG&amp;gt; Epoche 146: Train MSE=0.000516, Val MSE=0.000871, Val MAE=0.020515, Val MedAE=0.015357, Bit_Fail=1 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 18:46:58.797 1: SolCast DEBUG&amp;gt; Epoche 148: Train MSE=0.000497, Val MSE=0.000838, Val MAE=0.020132, Val MedAE=0.015318, Bit_Fail=1 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 18:46:58.986 1: SolCast DEBUG&amp;gt; Epoche 149: Train MSE=0.000488, Val MSE=0.000822, Val MAE=0.019953, Val MedAE=0.015200, Bit_Fail=1 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 18:46:59.141 1: SolCast DEBUG&amp;gt; Epoche 150: Train MSE=0.000482, Val MSE=0.000817, Val MAE=0.019792, Val MedAE=0.015107, Bit_Fail=1 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 18:46:59.316 1: SolCast DEBUG&amp;gt; Epoche 151: Train MSE=0.000474, Val MSE=0.000802, Val MAE=0.019630, Val MedAE=0.014972, Bit_Fail=1 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 18:46:59.471 1: SolCast DEBUG&amp;gt; Epoche 152: Train MSE=0.000466, Val MSE=0.000788, Val MAE=0.019477, Val MedAE=0.014905, Bit_Fail=1 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 18:46:59.637 1: SolCast DEBUG&amp;gt; Epoche 153: Train MSE=0.000459, Val MSE=0.000775, Val MAE=0.019332, Val MedAE=0.014836, Bit_Fail=1 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 18:46:59.795 1: SolCast DEBUG&amp;gt; Epoche 154: Train MSE=0.000452, Val MSE=0.000763, Val MAE=0.019196, Val MedAE=0.014833, Bit_Fail=1 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 18:46:59.953 1: SolCast DEBUG&amp;gt; Epoche 155: Train MSE=0.000446, Val MSE=0.000751, Val MAE=0.019068, Val MedAE=0.014766, Bit_Fail=1 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 18:47:00.128 1: SolCast DEBUG&amp;gt; Epoche 156: Train MSE=0.000440, Val MSE=0.000740, Val MAE=0.018949, Val MedAE=0.014751, Bit_Fail=1 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 18:47:00.423 1: SolCast DEBUG&amp;gt; Epoche 157: Train MSE=0.000434, Val MSE=0.000729, Val MAE=0.018837, Val MedAE=0.014571, Bit_Fail=1 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 18:47:00.580 1: SolCast DEBUG&amp;gt; Epoche 158: Train MSE=0.000429, Val MSE=0.000719, Val MAE=0.018732, Val MedAE=0.014399, Bit_Fail=1 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 18:47:00.738 1: SolCast DEBUG&amp;gt; Epoche 159: Train MSE=0.000423, Val MSE=0.000710, Val MAE=0.018632, Val MedAE=0.014296, Bit_Fail=0 -&amp;gt; Snap saved (bit improved)&lt;br /&gt;
2026.01.01 18:47:04.664 1: SolCast DEBUG&amp;gt; Epoche 185: Train MSE=0.000352, Val MSE=0.000570, Val MAE=0.017726, Val MedAE=0.014241, Bit_Fail=0 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 18:47:04.823 1: SolCast DEBUG&amp;gt; Epoche 186: Train MSE=0.000351, Val MSE=0.000568, Val MAE=0.017700, Val MedAE=0.014157, Bit_Fail=0 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 18:47:04.998 1: SolCast DEBUG&amp;gt; Epoche 187: Train MSE=0.000350, Val MSE=0.000567, Val MAE=0.017676, Val MedAE=0.014128, Bit_Fail=0 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 18:47:05.330 1: SolCast DEBUG&amp;gt; Epoche 189: Train MSE=0.000347, Val MSE=0.000565, Val MAE=0.017630, Val MedAE=0.014111, Bit_Fail=0 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 18:47:07.326 1: SolCast DEBUG&amp;gt; Epoche 200: Train MSE=0.000347, Val MSE=0.000560, Val MAE=0.017645, Val MedAE=0.014294, Bit_Fail=0&lt;br /&gt;
2026.01.01 18:47:08.430 1: SolCast DEBUG&amp;gt; Epoche 207: Train MSE=0.000336, Val MSE=0.000557, Val MAE=0.017581, Val MedAE=0.014098, Bit_Fail=0 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 18:47:08.925 1: SolCast DEBUG&amp;gt; Epoche 210: Train MSE=0.000332, Val MSE=0.000551, Val MAE=0.017149, Val MedAE=0.014015, Bit_Fail=0 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 18:47:09.251 1: SolCast DEBUG&amp;gt; Epoche 212: Train MSE=0.000331, Val MSE=0.000549, Val MAE=0.017106, Val MedAE=0.014013, Bit_Fail=0 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 18:47:09.543 1: SolCast DEBUG&amp;gt; Epoche 214: Train MSE=0.000330, Val MSE=0.000548, Val MAE=0.017079, Val MedAE=0.014009, Bit_Fail=0 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 18:47:19.704 1: SolCast DEBUG&amp;gt; Epoche 280: Train MSE=0.000312, Val MSE=0.000539, Val MAE=0.016444, Val MedAE=0.013594, Bit_Fail=0 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 18:47:22.914 1: SolCast DEBUG&amp;gt; Epoche 300: Train MSE=0.000312, Val MSE=0.000547, Val MAE=0.017776, Val MedAE=0.014873, Bit_Fail=0&lt;br /&gt;
2026.01.01 18:47:36.920 1: SolCast DEBUG&amp;gt; Epoche 390: Train MSE=0.000299, Val MSE=0.000534, Val MAE=0.016256, Val MedAE=0.013227, Bit_Fail=0 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 18:47:37.075 1: SolCast DEBUG&amp;gt; Epoche 391: Train MSE=0.000299, Val MSE=0.000533, Val MAE=0.016248, Val MedAE=0.013207, Bit_Fail=0 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 18:47:37.692 1: SolCast DEBUG&amp;gt; Epoche 395: Train MSE=0.000298, Val MSE=0.000532, Val MAE=0.016231, Val MedAE=0.013184, Bit_Fail=0 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 18:47:38.408 1: SolCast DEBUG&amp;gt; Epoche 400: Train MSE=0.000298, Val MSE=0.000533, Val MAE=0.016139, Val MedAE=0.013128, Bit_Fail=0&lt;br /&gt;
2026.01.01 18:47:43.011 1: SolCast DEBUG&amp;gt; Epoche 430: Train MSE=0.000294, Val MSE=0.000524, Val MAE=0.016053, Val MedAE=0.013160, Bit_Fail=0 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 18:47:53.755 1: SolCast DEBUG&amp;gt; Epoche 500: Train MSE=0.000294, Val MSE=0.000533, Val MAE=0.017601, Val MedAE=0.015372, Bit_Fail=0&lt;br /&gt;
2026.01.01 18:48:03.268 1: SolCast DEBUG&amp;gt; Epoche 560: Train MSE=0.000278, Val MSE=0.000499, Val MAE=0.015935, Val MedAE=0.013052, Bit_Fail=0 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 18:48:10.654 1: SolCast DEBUG&amp;gt; Epoche 600: Train MSE=0.000278, Val MSE=0.000497, Val MAE=0.016063, Val MedAE=0.013071, Bit_Fail=0&lt;br /&gt;
2026.01.01 18:48:17.120 1: SolCast DEBUG&amp;gt; Epoche 640: Train MSE=0.000270, Val MSE=0.000494, Val MAE=0.015925, Val MedAE=0.012746, Bit_Fail=0 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 18:48:26.254 1: SolCast DEBUG&amp;gt; Epoche 700: Train MSE=0.000270, Val MSE=0.000494, Val MAE=0.016121, Val MedAE=0.012865, Bit_Fail=0&lt;br /&gt;
2026.01.01 18:48:41.338 1: SolCast DEBUG&amp;gt; Epoche 800: Train MSE=0.000270, Val MSE=0.000496, Val MAE=0.016486, Val MedAE=0.013111, Bit_Fail=0&lt;br /&gt;
2026.01.01 18:48:50.270 1: SolCast DEBUG&amp;gt; Epoche 860: Train MSE=0.000251, Val MSE=0.000491, Val MAE=0.015786, Val MedAE=0.012241, Bit_Fail=0 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 18:48:56.298 1: SolCast DEBUG&amp;gt; Epoche 900: Train MSE=0.000251, Val MSE=0.000539, Val MAE=0.017781, Val MedAE=0.014813, Bit_Fail=0&lt;br /&gt;
2026.01.01 18:49:00.767 1: openMeteo - Open-Meteo API server response: start_SSL https://api.open-meteo.com:443 timed out&lt;br /&gt;
2026.01.01 18:49:11.666 1: SolCast DEBUG&amp;gt; Epoche 1000: Train MSE=0.000251, Val MSE=0.000487, Val MAE=0.015740, Val MedAE=0.012296, Bit_Fail=0&lt;br /&gt;
2026.01.01 18:49:28.665 1: SolCast DEBUG&amp;gt; Epoche 1100: Train MSE=0.000251, Val MSE=0.000491, Val MAE=0.015728, Val MedAE=0.012462, Bit_Fail=0&lt;br /&gt;
2026.01.01 18:49:39.207 1: SolCast DEBUG&amp;gt; Epoche 1168: Train MSE=0.000235, Val MSE=0.000479, Val MAE=0.015557, Val MedAE=0.012237, Bit_Fail=0 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 18:49:44.096 1: SolCast DEBUG&amp;gt; Epoche 1200: Train MSE=0.000235, Val MSE=0.000477, Val MAE=0.015613, Val MedAE=0.012266, Bit_Fail=0&lt;br /&gt;
2026.01.01 18:49:59.330 1: SolCast DEBUG&amp;gt; Epoche 1300: Train MSE=0.000235, Val MSE=0.000469, Val MAE=0.015970, Val MedAE=0.012473, Bit_Fail=0&lt;br /&gt;
2026.01.01 18:50:02.607 1: SolCast DEBUG&amp;gt; Epoche 1320: Train MSE=0.000230, Val MSE=0.000474, Val MAE=0.015490, Val MedAE=0.011887, Bit_Fail=0 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 18:50:15.063 1: SolCast DEBUG&amp;gt; Epoche 1400: Train MSE=0.000230, Val MSE=0.000469, Val MAE=0.016083, Val MedAE=0.012479, Bit_Fail=0&lt;br /&gt;
2026.01.01 18:50:31.357 1: SolCast DEBUG&amp;gt; Epoche 1500: Train MSE=0.000230, Val MSE=0.000461, Val MAE=0.015731, Val MedAE=0.012435, Bit_Fail=0&lt;br /&gt;
2026.01.01 18:50:43.405 1: SolCast DEBUG&amp;gt; Epoche 1570: Train MSE=0.000219, Val MSE=0.000452, Val MAE=0.015217, Val MedAE=0.011832, Bit_Fail=0 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 18:50:48.687 1: SolCast DEBUG&amp;gt; Epoche 1600: Train MSE=0.000219, Val MSE=0.000452, Val MAE=0.015676, Val MedAE=0.012380, Bit_Fail=0&lt;br /&gt;
2026.01.01 18:51:00.785 1: SolCast DEBUG&amp;gt; Epoche 1680: Train MSE=0.000216, Val MSE=0.000447, Val MAE=0.015153, Val MedAE=0.011761, Bit_Fail=0 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 18:51:02.263 1: SolCast DEBUG&amp;gt; Epoche 1690: Train MSE=0.000215, Val MSE=0.000446, Val MAE=0.015014, Val MedAE=0.011740, Bit_Fail=0 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 18:51:03.808 1: SolCast DEBUG&amp;gt; Epoche 1700: Train MSE=0.000215, Val MSE=0.000444, Val MAE=0.015548, Val MedAE=0.012346, Bit_Fail=0&lt;br /&gt;
2026.01.01 18:51:19.267 1: SolCast DEBUG&amp;gt; Epoche 1800: Train MSE=0.000215, Val MSE=0.000456, Val MAE=0.016255, Val MedAE=0.012272, Bit_Fail=0&lt;br /&gt;
2026.01.01 18:51:28.478 1: SolCast DEBUG&amp;gt; Epoche 1860: Train MSE=0.000211, Val MSE=0.000430, Val MAE=0.014967, Val MedAE=0.011675, Bit_Fail=0 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 18:51:32.128 1: SolCast DEBUG&amp;gt; Epoche 1885: Train MSE=0.000210, Val MSE=0.000429, Val MAE=0.014936, Val MedAE=0.011634, Bit_Fail=0 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 18:51:34.991 1: SolCast DEBUG&amp;gt; Epoche 1900: Train MSE=0.000210, Val MSE=0.000431, Val MAE=0.015557, Val MedAE=0.012006, Bit_Fail=0&lt;br /&gt;
2026.01.01 18:51:36.961 1: SolCast DEBUG&amp;gt; Epoche 1910: Train MSE=0.000209, Val MSE=0.000427, Val MAE=0.014927, Val MedAE=0.011633, Bit_Fail=0 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 18:51:44.135 1: SolCast DEBUG&amp;gt; Epoche 1950: Train MSE=0.000207, Val MSE=0.000425, Val MAE=0.014852, Val MedAE=0.011568, Bit_Fail=0 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 18:51:51.659 1: SolCast DEBUG&amp;gt; Epoche 2000: Train MSE=0.000207, Val MSE=0.000419, Val MAE=0.015028, Val MedAE=0.011705, Bit_Fail=0&lt;br /&gt;
2026.01.01 18:52:07.280 1: SolCast DEBUG&amp;gt; Epoche 2100: Train MSE=0.000207, Val MSE=0.000419, Val MAE=0.015127, Val MedAE=0.011801, Bit_Fail=0&lt;br /&gt;
2026.01.01 18:52:22.353 1: SolCast DEBUG&amp;gt; Epoche 2200: Train MSE=0.000207, Val MSE=0.000409, Val MAE=0.014933, Val MedAE=0.011578, Bit_Fail=0&lt;br /&gt;
2026.01.01 18:52:25.518 1: SolCast DEBUG&amp;gt; Epoche 2220: Train MSE=0.000201, Val MSE=0.000401, Val MAE=0.014505, Val MedAE=0.011123, Bit_Fail=0 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 18:52:38.140 1: SolCast DEBUG&amp;gt; Epoche 2280: Train MSE=0.000200, Val MSE=0.000398, Val MAE=0.014426, Val MedAE=0.010998, Bit_Fail=0 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 18:52:40.466 1: SolCast DEBUG&amp;gt; Epoche 2290: Train MSE=0.000199, Val MSE=0.000397, Val MAE=0.014408, Val MedAE=0.010918, Bit_Fail=0 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 18:52:42.754 1: SolCast DEBUG&amp;gt; Epoche 2300: Train MSE=0.000199, Val MSE=0.000395, Val MAE=0.014771, Val MedAE=0.011372, Bit_Fail=0&lt;br /&gt;
2026.01.01 18:53:06.020 1: SolCast DEBUG&amp;gt; Epoche 2400: Train MSE=0.000199, Val MSE=0.000386, Val MAE=0.014179, Val MedAE=0.010473, Bit_Fail=0&lt;br /&gt;
2026.01.01 18:53:06.031 1: SolCast DEBUG&amp;gt; Epoche 2400: Train MSE=0.000196, Val MSE=0.000386, Val MAE=0.014179, Val MedAE=0.010473, Bit_Fail=0 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 18:53:27.648 1: SolCast DEBUG&amp;gt; Epoche 2500: Train MSE=0.000196, Val MSE=0.000394, Val MAE=0.015055, Val MedAE=0.011719, Bit_Fail=0&lt;br /&gt;
2026.01.01 18:53:48.669 1: SolCast DEBUG&amp;gt; Epoche 2600: Train MSE=0.000196, Val MSE=0.000393, Val MAE=0.015223, Val MedAE=0.011833, Bit_Fail=0&lt;br /&gt;
2026.01.01 18:54:04.615 1: SolCast DEBUG&amp;gt; Epoche 2670: Train MSE=0.000190, Val MSE=0.000363, Val MAE=0.014023, Val MedAE=0.010343, Bit_Fail=0 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 18:54:10.539 1: SolCast DEBUG&amp;gt; Epoche 2700: Train MSE=0.000190, Val MSE=0.000399, Val MAE=0.015520, Val MedAE=0.012166, Bit_Fail=0&lt;br /&gt;
2026.01.01 18:54:22.630 1: SolCast DEBUG&amp;gt; Epoche 2762: Train MSE=0.000188, Val MSE=0.000359, Val MAE=0.014009, Val MedAE=0.010303, Bit_Fail=0 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 18:54:28.642 1: SolCast DEBUG&amp;gt; Epoche 2800: Train MSE=0.000188, Val MSE=0.000422, Val MAE=0.016216, Val MedAE=0.013541, Bit_Fail=0&lt;br /&gt;
2026.01.01 18:54:36.250 1: SolCast DEBUG&amp;gt; Epoche 2850: Train MSE=0.000187, Val MSE=0.000356, Val MAE=0.013892, Val MedAE=0.010014, Bit_Fail=0 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 18:54:37.817 1: SolCast DEBUG&amp;gt; Epoche 2860: Train MSE=0.000186, Val MSE=0.000353, Val MAE=0.013781, Val MedAE=0.009785, Bit_Fail=0 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 18:54:43.894 1: SolCast DEBUG&amp;gt; Epoche 2900: Train MSE=0.000186, Val MSE=0.000373, Val MAE=0.014968, Val MedAE=0.011754, Bit_Fail=0&lt;br /&gt;
2026.01.01 18:54:51.665 1: SolCast DEBUG&amp;gt; Epoche 2950: Train MSE=0.000184, Val MSE=0.000350, Val MAE=0.013709, Val MedAE=0.009744, Bit_Fail=0 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 18:54:56.355 1: SolCast DEBUG&amp;gt; Epoche 2980: Train MSE=0.000185, Val MSE=0.000344, Val MAE=0.013542, Val MedAE=0.009640, Bit_Fail=0 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 18:54:59.391 1: SolCast DEBUG&amp;gt; Epoche 3000: Train MSE=0.000185, Val MSE=0.000345, Val MAE=0.013771, Val MedAE=0.010299, Bit_Fail=0&lt;br /&gt;
2026.01.01 18:55:15.914 1: SolCast DEBUG&amp;gt; Epoche 3100: Train MSE=0.000185, Val MSE=0.000338, Val MAE=0.013390, Val MedAE=0.009239, Bit_Fail=0&lt;br /&gt;
2026.01.01 18:55:15.920 1: SolCast DEBUG&amp;gt; Epoche 3100: Train MSE=0.000183, Val MSE=0.000338, Val MAE=0.013390, Val MedAE=0.009239, Bit_Fail=0 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 18:55:31.223 1: SolCast DEBUG&amp;gt; Epoche 3200: Train MSE=0.000183, Val MSE=0.000374, Val MAE=0.015348, Val MedAE=0.013113, Bit_Fail=0&lt;br /&gt;
2026.01.01 18:55:46.650 1: SolCast DEBUG&amp;gt; Epoche 3300: Train MSE=0.000183, Val MSE=0.000395, Val MAE=0.015892, Val MedAE=0.013867, Bit_Fail=0&lt;br /&gt;
2026.01.01 18:55:55.663 1: SolCast DEBUG&amp;gt; Epoche 3360: Train MSE=0.000178, Val MSE=0.000336, Val MAE=0.013102, Val MedAE=0.009038, Bit_Fail=0 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 18:55:57.156 1: SolCast DEBUG&amp;gt; Epoche 3370: Train MSE=0.000178, Val MSE=0.000328, Val MAE=0.013027, Val MedAE=0.008576, Bit_Fail=0 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 18:56:01.837 1: SolCast DEBUG&amp;gt; Epoche 3400: Train MSE=0.000178, Val MSE=0.000331, Val MAE=0.013966, Val MedAE=0.010989, Bit_Fail=0&lt;br /&gt;
2026.01.01 18:56:18.127 1: SolCast DEBUG&amp;gt; Epoche 3500: Train MSE=0.000178, Val MSE=0.000317, Val MAE=0.013546, Val MedAE=0.010578, Bit_Fail=0&lt;br /&gt;
2026.01.01 18:56:33.705 1: SolCast DEBUG&amp;gt; Epoche 3600: Train MSE=0.000178, Val MSE=0.000326, Val MAE=0.013924, Val MedAE=0.011087, Bit_Fail=0&lt;br /&gt;
2026.01.01 18:56:49.380 1: SolCast DEBUG&amp;gt; Epoche 3700: Train MSE=0.000178, Val MSE=0.000313, Val MAE=0.013647, Val MedAE=0.011350, Bit_Fail=0&lt;br /&gt;
2026.01.01 18:57:04.360 1: SolCast DEBUG&amp;gt; Epoche 3800: Train MSE=0.000178, Val MSE=0.000315, Val MAE=0.013759, Val MedAE=0.011353, Bit_Fail=0&lt;br /&gt;
2026.01.01 18:57:20.079 1: SolCast DEBUG&amp;gt; Epoche 3900: Train MSE=0.000178, Val MSE=0.000317, Val MAE=0.013889, Val MedAE=0.011544, Bit_Fail=0&lt;br /&gt;
2026.01.01 18:57:36.568 1: SolCast DEBUG&amp;gt; Epoche 4000: Train MSE=0.000178, Val MSE=0.000303, Val MAE=0.013266, Val MedAE=0.010514, Bit_Fail=0&lt;br /&gt;
2026.01.01 18:57:51.815 1: SolCast DEBUG&amp;gt; Epoche 4100: Train MSE=0.000178, Val MSE=0.000297, Val MAE=0.013051, Val MedAE=0.010491, Bit_Fail=0&lt;br /&gt;
2026.01.01 18:58:07.665 1: SolCast DEBUG&amp;gt; Epoche 4200: Train MSE=0.000178, Val MSE=0.000298, Val MAE=0.013165, Val MedAE=0.010657, Bit_Fail=0&lt;br /&gt;
2026.01.01 18:58:22.874 1: SolCast DEBUG&amp;gt; Epoche 4300: Train MSE=0.000178, Val MSE=0.000299, Val MAE=0.012652, Val MedAE=0.009298, Bit_Fail=0&lt;br /&gt;
2026.01.01 18:58:39.872 1: SolCast DEBUG&amp;gt; Epoche 4400: Train MSE=0.000178, Val MSE=0.000299, Val MAE=0.013357, Val MedAE=0.010952, Bit_Fail=0&lt;br /&gt;
2026.01.01 18:58:55.060 1: SolCast DEBUG&amp;gt; Epoche 4500: Train MSE=0.000178, Val MSE=0.000293, Val MAE=0.012888, Val MedAE=0.010075, Bit_Fail=0&lt;br /&gt;
2026.01.01 18:59:10.645 1: SolCast DEBUG&amp;gt; Epoche 4600: Train MSE=0.000178, Val MSE=0.000296, Val MAE=0.013170, Val MedAE=0.010801, Bit_Fail=0&lt;br /&gt;
2026.01.01 18:59:26.299 1: SolCast DEBUG&amp;gt; Epoche 4700: Train MSE=0.000178, Val MSE=0.000289, Val MAE=0.012813, Val MedAE=0.010297, Bit_Fail=0&lt;br /&gt;
2026.01.01 18:59:41.390 1: SolCast DEBUG&amp;gt; Epoche 4800: Train MSE=0.000178, Val MSE=0.000288, Val MAE=0.012730, Val MedAE=0.010022, Bit_Fail=0&lt;br /&gt;
2026.01.01 18:59:58.018 1: SolCast DEBUG&amp;gt; Epoche 4900: Train MSE=0.000178, Val MSE=0.000288, Val MAE=0.012927, Val MedAE=0.010486, Bit_Fail=0&lt;br /&gt;
2026.01.01 19:00:16.580 1: SolCast DEBUG&amp;gt; Epoche 5000: Train MSE=0.000178, Val MSE=0.000300, Val MAE=0.013538, Val MedAE=0.011570, Bit_Fail=0&lt;br /&gt;
2026.01.01 19:00:22.430 1: VRMAPI DEBUG&amp;gt; start add AI raw data for hour: 19&lt;br /&gt;
2026.01.01 19:00:22.434 1: VRMAPI DEBUG&amp;gt; AI raw add - 1 entities added to raw data pool (set verbose 4 for output more detail)&lt;br /&gt;
2026.01.01 19:00:22.507 1: VRMAPI DEBUG&amp;gt; AI raw data saved into file: ./FHEM/FhemUtils/AIraw_SolarForecast_VRMAPI&lt;br /&gt;
2026.01.01 19:00:23.036 1: SolCast DEBUG&amp;gt; start add AI raw data for hour: 19&lt;br /&gt;
2026.01.01 19:00:23.041 1: SolCast DEBUG&amp;gt; AI raw add - 1 entities added to raw data pool (set verbose 4 for output more detail)&lt;br /&gt;
2026.01.01 19:00:23.490 1: SolCast DEBUG&amp;gt; AI raw data saved into file: ./FHEM/FhemUtils/AIraw_SolarForecast_SolCast&lt;br /&gt;
2026.01.01 19:00:25.468 1: openMeteo DEBUG&amp;gt; start add AI raw data for hour: 19&lt;br /&gt;
2026.01.01 19:00:25.471 1: openMeteo DEBUG&amp;gt; AI raw add - 1 entities added to raw data pool (set verbose 4 for output more detail)&lt;br /&gt;
2026.01.01 19:00:25.529 1: openMeteo DEBUG&amp;gt; AI raw data saved into file: ./FHEM/FhemUtils/AIraw_SolarForecast_openMeteo&lt;br /&gt;
2026.01.01 19:00:36.434 1: SolCast DEBUG&amp;gt; Epoche 5100: Train MSE=0.000178, Val MSE=0.000287, Val MAE=0.012656, Val MedAE=0.009749, Bit_Fail=0&lt;br /&gt;
2026.01.01 19:00:52.824 1: SolCast DEBUG&amp;gt; Epoche 5200: Train MSE=0.000178, Val MSE=0.000315, Val MAE=0.014053, Val MedAE=0.012264, Bit_Fail=0&lt;br /&gt;
2026.01.01 19:01:08.326 1: SolCast DEBUG&amp;gt; Epoche 5300: Train MSE=0.000178, Val MSE=0.000291, Val MAE=0.013064, Val MedAE=0.010484, Bit_Fail=0&lt;br /&gt;
2026.01.01 19:01:18.912 1: SolCast DEBUG&amp;gt; Early stopping bei Epoche 5370 (no improvement since 2000 epochs)&lt;br /&gt;
2026.01.01 19:01:18.914 1: === Snapshot-Statistik ===&lt;br /&gt;
2026.01.01 19:01:18.916 1: Metric-Improvement Snapshots: 64 (letzte Epoche: 3370)&lt;br /&gt;
2026.01.01 19:01:18.918 1: Bit-Improvement Snapshots:    3 (letzte Epoche: 159)&lt;br /&gt;
2026.01.01 19:01:18.919 1: Bit-Tradeoff Snapshots:       0 (letzte Epoche: 0)&lt;br /&gt;
2026.01.01 19:01:18.925 1: SolCast DEBUG&amp;gt; Best Snapshot reloaded from Epoche 3370: Train MSE=0.000178, Val MSE=0.000328, Val MAE=0.013027, Val MedAE=0.008576, Bit_Fail=0,&lt;br /&gt;
2026.01.01 19:01:18.927 1: SolCast DEBUG&amp;gt; Run Validation Test with 20% of Input data ...&lt;br /&gt;
2026.01.01 19:01:18.969 1: SolCast DEBUG&amp;gt; Validation finished - Best Training MSE=0.000178, Validation MSE=0.000328, Validation Bit_Fail=0&lt;br /&gt;
2026.01.01 19:01:18.971 1: SolCast DEBUG&amp;gt; Retrain check -&amp;gt; &lt;br /&gt;
-- In Normalization Space: -- &lt;br /&gt;
Train MSE=0.000178 &lt;br /&gt;
Val MSE=0.000328 &lt;br /&gt;
Val Mean=0.0002956156 &lt;br /&gt;
VAL/TRAIN MSE Ratio=1.837433 (limit=2.5) &lt;br /&gt;
Diff=0.000149 (limit=0.005) &lt;br /&gt;
ValStd=0.0000173294 (limit=7.39038913958514e-05) &lt;br /&gt;
-- At Original Scale: -- &lt;br /&gt;
MAE=214.560321844596 &lt;br /&gt;
RMSE/MAE=1.3893 (limit=1.5) &lt;br /&gt;
Slope=0.916647 (limit=0.7 .. 1.3) &lt;br /&gt;
Bias=2.25 (limit=+-107.280160922298) &lt;br /&gt;
R2=0.884124888835566 &lt;br /&gt;
P95=585.2166 (limit=858.241287378382) &lt;br /&gt;
P99=942.9805 (limit=1716.48257475676) &lt;br /&gt;
-- Robustness Indicators: -- &lt;br /&gt;
RMSE relative=64 (limit=20) &lt;br /&gt;
BitFail=0 (limit=5) &lt;br /&gt;
BitFailRate=0.0000 (limit=0.1) &lt;br /&gt;
-&amp;gt; Retrain&lt;br /&gt;
2026.01.01 19:01:18.979 1: SolCast DEBUG&amp;gt; Retry attempt 3 with Seed=16689959&lt;br /&gt;
2026.01.01 19:01:18.985 1: SolCast DEBUG&amp;gt; AI FANN Training started with Params:&lt;br /&gt;
num input datasets=7314, &lt;br /&gt;
training algo=FANN_TRAIN_INCREMENTAL, &lt;br /&gt;
output AF=LINEAR, &lt;br /&gt;
hidden AF=SIGMOID, &lt;br /&gt;
hidden Neurons=80,40,20, &lt;br /&gt;
hidden steepness=0.9, &lt;br /&gt;
Epoches=15000, &lt;br /&gt;
mse_error=0.001, &lt;br /&gt;
learning rate=0.00500, &lt;br /&gt;
learning momentum=0.7, &lt;br /&gt;
Data sharing=split after shuffle of training data and use AI internal shuffle (Train=5851, Test=1462), &lt;br /&gt;
Data shuffle=2 (period=10)&lt;br /&gt;
2026.01.01 19:01:19.145 1: SolCast DEBUG&amp;gt; Epoche 1: Train MSE=0.002202, Val MSE=0.002839, Val MAE=0.031937, Val MedAE=0.027914, Bit_Fail=2 -&amp;gt; Snap saved (bit improved)&lt;br /&gt;
2026.01.01 19:01:25.133 1: SolCast DEBUG&amp;gt; Epoche 40: Train MSE=0.002098, Val MSE=0.002825, Val MAE=0.031158, Val MedAE=0.027367, Bit_Fail=2 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 19:01:27.800 1: SolCast DEBUG&amp;gt; Epoche 57: Train MSE=0.001969, Val MSE=0.002810, Val MAE=0.029915, Val MedAE=0.021870, Bit_Fail=2 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 19:01:27.982 1: SolCast DEBUG&amp;gt; Epoche 58: Train MSE=0.001947, Val MSE=0.002786, Val MAE=0.029801, Val MedAE=0.021464, Bit_Fail=2 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 19:01:28.134 1: SolCast DEBUG&amp;gt; Epoche 59: Train MSE=0.001921, Val MSE=0.002758, Val MAE=0.029673, Val MedAE=0.020903, Bit_Fail=2 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 19:01:28.609 1: SolCast DEBUG&amp;gt; Epoche 62: Train MSE=0.001810, Val MSE=0.002716, Val MAE=0.029480, Val MedAE=0.017887, Bit_Fail=2 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 19:01:28.767 1: SolCast DEBUG&amp;gt; Epoche 63: Train MSE=0.001761, Val MSE=0.002660, Val MAE=0.029224, Val MedAE=0.017446, Bit_Fail=2 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 19:01:28.915 1: SolCast DEBUG&amp;gt; Epoche 64: Train MSE=0.001706, Val MSE=0.002597, Val MAE=0.028944, Val MedAE=0.016968, Bit_Fail=2 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 19:01:29.063 1: SolCast DEBUG&amp;gt; Epoche 65: Train MSE=0.001645, Val MSE=0.002528, Val MAE=0.028652, Val MedAE=0.016879, Bit_Fail=2 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 19:01:29.207 1: SolCast DEBUG&amp;gt; Epoche 66: Train MSE=0.001581, Val MSE=0.002455, Val MAE=0.028362, Val MedAE=0.016529, Bit_Fail=2 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 19:01:34.406 1: SolCast DEBUG&amp;gt; Epoche 100: Train MSE=0.001581, Val MSE=0.001674, Val MAE=0.026943, Val MedAE=0.019938, Bit_Fail=1&lt;br /&gt;
2026.01.01 19:01:41.897 1: SolCast DEBUG&amp;gt; Epoche 149: Train MSE=0.000574, Val MSE=0.000953, Val MAE=0.021557, Val MedAE=0.017213, Bit_Fail=1 -&amp;gt; Snap saved (bit improved)&lt;br /&gt;
2026.01.01 19:01:43.755 1: SolCast DEBUG&amp;gt; Epoche 160: Train MSE=0.000466, Val MSE=0.000806, Val MAE=0.019501, Val MedAE=0.014848, Bit_Fail=1 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 19:01:43.926 1: SolCast DEBUG&amp;gt; Epoche 161: Train MSE=0.000458, Val MSE=0.000792, Val MAE=0.019351, Val MedAE=0.014777, Bit_Fail=1 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 19:01:44.118 1: SolCast DEBUG&amp;gt; Epoche 162: Train MSE=0.000451, Val MSE=0.000778, Val MAE=0.019209, Val MedAE=0.014629, Bit_Fail=1 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 19:01:44.334 1: SolCast DEBUG&amp;gt; Epoche 163: Train MSE=0.000444, Val MSE=0.000764, Val MAE=0.019075, Val MedAE=0.014565, Bit_Fail=1 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 19:01:44.504 1: SolCast DEBUG&amp;gt; Epoche 164: Train MSE=0.000438, Val MSE=0.000752, Val MAE=0.018948, Val MedAE=0.014360, Bit_Fail=1 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 19:01:44.716 1: SolCast DEBUG&amp;gt; Epoche 165: Train MSE=0.000432, Val MSE=0.000740, Val MAE=0.018829, Val MedAE=0.014227, Bit_Fail=1 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 19:01:44.952 1: SolCast DEBUG&amp;gt; Epoche 166: Train MSE=0.000426, Val MSE=0.000729, Val MAE=0.018714, Val MedAE=0.014165, Bit_Fail=1 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 19:01:45.151 1: SolCast DEBUG&amp;gt; Epoche 167: Train MSE=0.000421, Val MSE=0.000718, Val MAE=0.018605, Val MedAE=0.014070, Bit_Fail=0 -&amp;gt; Snap saved (bit improved)&lt;br /&gt;
2026.01.01 19:01:45.311 1: SolCast DEBUG&amp;gt; Epoche 168: Train MSE=0.000416, Val MSE=0.000708, Val MAE=0.018500, Val MedAE=0.013993, Bit_Fail=0 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 19:01:45.475 1: SolCast DEBUG&amp;gt; Epoche 169: Train MSE=0.000411, Val MSE=0.000699, Val MAE=0.018400, Val MedAE=0.013928, Bit_Fail=0 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 19:01:50.312 1: SolCast DEBUG&amp;gt; Epoche 200: Train MSE=0.000411, Val MSE=0.000558, Val MAE=0.017252, Val MedAE=0.013991, Bit_Fail=0&lt;br /&gt;
2026.01.01 19:01:50.478 1: SolCast DEBUG&amp;gt; Epoche 201: Train MSE=0.000341, Val MSE=0.000558, Val MAE=0.017237, Val MedAE=0.013915, Bit_Fail=0 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 19:01:50.920 1: SolCast DEBUG&amp;gt; Epoche 204: Train MSE=0.000338, Val MSE=0.000557, Val MAE=0.017191, Val MedAE=0.013805, Bit_Fail=0 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 19:01:51.226 1: SolCast DEBUG&amp;gt; Epoche 206: Train MSE=0.000337, Val MSE=0.000556, Val MAE=0.017163, Val MedAE=0.013746, Bit_Fail=0 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 19:01:58.095 1: SolCast DEBUG&amp;gt; Epoche 250: Train MSE=0.000319, Val MSE=0.000554, Val MAE=0.016637, Val MedAE=0.013530, Bit_Fail=0 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 19:01:58.382 1: SolCast DEBUG&amp;gt; Epoche 252: Train MSE=0.000318, Val MSE=0.000553, Val MAE=0.016627, Val MedAE=0.013499, Bit_Fail=0 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 19:02:00.397 1: SolCast DEBUG&amp;gt; Epoche 266: Train MSE=0.000315, Val MSE=0.000552, Val MAE=0.016535, Val MedAE=0.013373, Bit_Fail=0 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 19:02:06.185 1: SolCast DEBUG&amp;gt; Epoche 300: Train MSE=0.000315, Val MSE=0.000521, Val MAE=0.016463, Val MedAE=0.014001, Bit_Fail=0&lt;br /&gt;
2026.01.01 19:02:17.223 1: SolCast DEBUG&amp;gt; Epoche 371: Train MSE=0.000296, Val MSE=0.000522, Val MAE=0.016217, Val MedAE=0.013366, Bit_Fail=0 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 19:02:21.515 1: SolCast DEBUG&amp;gt; Epoche 400: Train MSE=0.000296, Val MSE=0.000500, Val MAE=0.016347, Val MedAE=0.013895, Bit_Fail=0&lt;br /&gt;
2026.01.01 19:02:23.239 1: SolCast DEBUG&amp;gt; Epoche 410: Train MSE=0.000290, Val MSE=0.000518, Val MAE=0.016152, Val MedAE=0.013203, Bit_Fail=0 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 19:02:23.555 1: SolCast DEBUG&amp;gt; Epoche 412: Train MSE=0.000290, Val MSE=0.000517, Val MAE=0.016144, Val MedAE=0.013196, Bit_Fail=0 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 19:02:24.223 1: SolCast DEBUG&amp;gt; Epoche 416: Train MSE=0.000289, Val MSE=0.000516, Val MAE=0.016133, Val MedAE=0.013144, Bit_Fail=0 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 19:02:36.913 1: SolCast DEBUG&amp;gt; Epoche 500: Train MSE=0.000289, Val MSE=0.000513, Val MAE=0.016033, Val MedAE=0.012459, Bit_Fail=0&lt;br /&gt;
2026.01.01 19:02:36.920 1: SolCast DEBUG&amp;gt; Epoche 500: Train MSE=0.000274, Val MSE=0.000513, Val MAE=0.016033, Val MedAE=0.012459, Bit_Fail=0 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 19:02:37.941 1: SolCast DEBUG&amp;gt; Epoche 507: Train MSE=0.000273, Val MSE=0.000512, Val MAE=0.016016, Val MedAE=0.012419, Bit_Fail=0 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 19:02:52.279 1: SolCast DEBUG&amp;gt; Epoche 600: Train MSE=0.000273, Val MSE=0.000491, Val MAE=0.015793, Val MedAE=0.012659, Bit_Fail=0&lt;br /&gt;
2026.01.01 19:03:07.863 1: SolCast DEBUG&amp;gt; Epoche 700: Train MSE=0.000273, Val MSE=0.000531, Val MAE=0.017652, Val MedAE=0.014846, Bit_Fail=0&lt;br /&gt;
2026.01.01 19:03:22.755 1: SolCast DEBUG&amp;gt; Epoche 800: Train MSE=0.000273, Val MSE=0.000485, Val MAE=0.015777, Val MedAE=0.012388, Bit_Fail=0&lt;br /&gt;
2026.01.01 19:03:22.761 1: SolCast DEBUG&amp;gt; Epoche 800: Train MSE=0.000246, Val MSE=0.000485, Val MAE=0.015777, Val MedAE=0.012388, Bit_Fail=0 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 19:03:38.201 1: SolCast DEBUG&amp;gt; Epoche 900: Train MSE=0.000246, Val MSE=0.000489, Val MAE=0.015804, Val MedAE=0.012387, Bit_Fail=0&lt;br /&gt;
2026.01.01 19:03:53.934 1: SolCast DEBUG&amp;gt; Epoche 1000: Train MSE=0.000246, Val MSE=0.000491, Val MAE=0.016320, Val MedAE=0.012996, Bit_Fail=0&lt;br /&gt;
2026.01.01 19:04:12.553 1: SolCast DEBUG&amp;gt; Epoche 1100: Train MSE=0.000246, Val MSE=0.000500, Val MAE=0.016762, Val MedAE=0.013013, Bit_Fail=0&lt;br /&gt;
2026.01.01 19:04:29.863 1: SolCast DEBUG&amp;gt; Epoche 1200: Train MSE=0.000246, Val MSE=0.000472, Val MAE=0.015815, Val MedAE=0.012588, Bit_Fail=0&lt;br /&gt;
2026.01.01 19:04:39.395 1: SolCast DEBUG&amp;gt; Epoche 1240: Train MSE=0.000226, Val MSE=0.000476, Val MAE=0.015690, Val MedAE=0.012238, Bit_Fail=0 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 19:04:44.449 1: SolCast DEBUG&amp;gt; Epoche 1260: Train MSE=0.000226, Val MSE=0.000472, Val MAE=0.015617, Val MedAE=0.012095, Bit_Fail=0 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 19:04:54.273 1: SolCast DEBUG&amp;gt; Epoche 1300: Train MSE=0.000226, Val MSE=0.000469, Val MAE=0.015375, Val MedAE=0.012019, Bit_Fail=0&lt;br /&gt;
2026.01.01 19:04:54.283 1: SolCast DEBUG&amp;gt; Epoche 1300: Train MSE=0.000225, Val MSE=0.000469, Val MAE=0.015375, Val MedAE=0.012019, Bit_Fail=0 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 19:05:19.860 1: SolCast DEBUG&amp;gt; Epoche 1400: Train MSE=0.000225, Val MSE=0.000476, Val MAE=0.016232, Val MedAE=0.012583, Bit_Fail=0&lt;br /&gt;
2026.01.01 19:05:46.230 1: SolCast DEBUG&amp;gt; Epoche 1500: Train MSE=0.000225, Val MSE=0.000460, Val MAE=0.015477, Val MedAE=0.012145, Bit_Fail=0&lt;br /&gt;
2026.01.01 19:06:01.555 1: SolCast DEBUG&amp;gt; Epoche 1560: Train MSE=0.000215, Val MSE=0.000450, Val MAE=0.015175, Val MedAE=0.011698, Bit_Fail=0 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 19:06:11.656 1: SolCast DEBUG&amp;gt; Epoche 1600: Train MSE=0.000215, Val MSE=0.000446, Val MAE=0.015338, Val MedAE=0.011925, Bit_Fail=0&lt;br /&gt;
2026.01.01 19:06:19.086 1: SolCast DEBUG&amp;gt; Epoche 1630: Train MSE=0.000213, Val MSE=0.000443, Val MAE=0.015005, Val MedAE=0.011380, Bit_Fail=0 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 19:06:37.018 1: SolCast DEBUG&amp;gt; Epoche 1700: Train MSE=0.000213, Val MSE=0.000456, Val MAE=0.016104, Val MedAE=0.012245, Bit_Fail=0&lt;br /&gt;
2026.01.01 19:06:41.728 1: SolCast DEBUG&amp;gt; Epoche 1720: Train MSE=0.000209, Val MSE=0.000437, Val MAE=0.014979, Val MedAE=0.011226, Bit_Fail=0 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 19:06:54.098 1: SolCast DEBUG&amp;gt; Epoche 1770: Train MSE=0.000208, Val MSE=0.000428, Val MAE=0.014866, Val MedAE=0.011051, Bit_Fail=0 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 19:07:01.420 1: SolCast DEBUG&amp;gt; Epoche 1800: Train MSE=0.000208, Val MSE=0.000421, Val MAE=0.014909, Val MedAE=0.011181, Bit_Fail=0&lt;br /&gt;
2026.01.01 19:07:09.022 1: SolCast DEBUG&amp;gt; Epoche 1830: Train MSE=0.000206, Val MSE=0.000425, Val MAE=0.014835, Val MedAE=0.010975, Bit_Fail=0 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 19:07:18.661 1: SolCast DEBUG&amp;gt; Epoche 1870: Train MSE=0.000205, Val MSE=0.000422, Val MAE=0.014635, Val MedAE=0.010337, Bit_Fail=0 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 19:07:26.451 1: SolCast DEBUG&amp;gt; Epoche 1900: Train MSE=0.000205, Val MSE=0.000421, Val MAE=0.015039, Val MedAE=0.011242, Bit_Fail=0&lt;br /&gt;
2026.01.01 19:07:35.709 1: SolCast DEBUG&amp;gt; Epoche 1950: Train MSE=0.000204, Val MSE=0.000412, Val MAE=0.014563, Val MedAE=0.010315, Bit_Fail=0 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 19:07:43.401 1: SolCast DEBUG&amp;gt; Epoche 2000: Train MSE=0.000204, Val MSE=0.000478, Val MAE=0.017001, Val MedAE=0.013903, Bit_Fail=0&lt;br /&gt;
2026.01.01 19:07:59.225 1: SolCast DEBUG&amp;gt; Epoche 2100: Train MSE=0.000204, Val MSE=0.000400, Val MAE=0.014424, Val MedAE=0.010221, Bit_Fail=0&lt;br /&gt;
2026.01.01 19:07:59.231 1: SolCast DEBUG&amp;gt; Epoche 2100: Train MSE=0.000200, Val MSE=0.000400, Val MAE=0.014424, Val MedAE=0.010221, Bit_Fail=0 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 19:08:14.654 1: SolCast DEBUG&amp;gt; Epoche 2200: Train MSE=0.000200, Val MSE=0.000399, Val MAE=0.014978, Val MedAE=0.011324, Bit_Fail=0&lt;br /&gt;
2026.01.01 19:08:28.453 1: SolCast DEBUG&amp;gt; Epoche 2290: Train MSE=0.000196, Val MSE=0.000390, Val MAE=0.014290, Val MedAE=0.009855, Bit_Fail=0 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 19:08:29.937 1: SolCast DEBUG&amp;gt; Epoche 2300: Train MSE=0.000196, Val MSE=0.000391, Val MAE=0.014139, Val MedAE=0.009359, Bit_Fail=0&lt;br /&gt;
2026.01.01 19:08:46.992 1: SolCast DEBUG&amp;gt; Epoche 2400: Train MSE=0.000196, Val MSE=0.000429, Val MAE=0.016088, Val MedAE=0.013370, Bit_Fail=0&lt;br /&gt;
2026.01.01 19:08:59.193 1: SolCast DEBUG&amp;gt; Epoche 2480: Train MSE=0.000193, Val MSE=0.000378, Val MAE=0.014148, Val MedAE=0.009492, Bit_Fail=0 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 19:09:02.241 1: SolCast DEBUG&amp;gt; Epoche 2500: Train MSE=0.000193, Val MSE=0.000384, Val MAE=0.014816, Val MedAE=0.011437, Bit_Fail=0&lt;br /&gt;
2026.01.01 19:09:18.312 1: SolCast DEBUG&amp;gt; Epoche 2600: Train MSE=0.000193, Val MSE=0.000373, Val MAE=0.014435, Val MedAE=0.010892, Bit_Fail=0&lt;br /&gt;
2026.01.01 19:09:27.592 1: SolCast DEBUG&amp;gt; Epoche 2660: Train MSE=0.000190, Val MSE=0.000367, Val MAE=0.013899, Val MedAE=0.009407, Bit_Fail=0 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 19:09:33.631 1: SolCast DEBUG&amp;gt; Epoche 2700: Train MSE=0.000190, Val MSE=0.000364, Val MAE=0.014131, Val MedAE=0.010351, Bit_Fail=0&lt;br /&gt;
2026.01.01 19:09:50.479 1: SolCast DEBUG&amp;gt; Epoche 2800: Train MSE=0.000190, Val MSE=0.000374, Val MAE=0.014829, Val MedAE=0.011905, Bit_Fail=0&lt;br /&gt;
2026.01.01 19:10:06.433 1: SolCast DEBUG&amp;gt; Epoche 2900: Train MSE=0.000190, Val MSE=0.000358, Val MAE=0.013816, Val MedAE=0.009626, Bit_Fail=0&lt;br /&gt;
2026.01.01 19:10:17.130 1: SolCast DEBUG&amp;gt; Epoche 2970: Train MSE=0.000184, Val MSE=0.000354, Val MAE=0.013688, Val MedAE=0.009390, Bit_Fail=0 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 19:10:21.510 1: SolCast DEBUG&amp;gt; Epoche 3000: Train MSE=0.000184, Val MSE=0.000376, Val MAE=0.015092, Val MedAE=0.012492, Bit_Fail=0&lt;br /&gt;
2026.01.01 19:10:37.294 1: SolCast DEBUG&amp;gt; Epoche 3100: Train MSE=0.000184, Val MSE=0.000344, Val MAE=0.013896, Val MedAE=0.010700, Bit_Fail=0&lt;br /&gt;
2026.01.01 19:10:52.935 1: SolCast DEBUG&amp;gt; Epoche 3200: Train MSE=0.000184, Val MSE=0.000340, Val MAE=0.013841, Val MedAE=0.010725, Bit_Fail=0&lt;br /&gt;
2026.01.01 19:11:09.459 1: SolCast DEBUG&amp;gt; Epoche 3300: Train MSE=0.000184, Val MSE=0.000343, Val MAE=0.014114, Val MedAE=0.011228, Bit_Fail=0&lt;br /&gt;
2026.01.01 19:11:25.191 1: SolCast DEBUG&amp;gt; Epoche 3400: Train MSE=0.000184, Val MSE=0.000329, Val MAE=0.013537, Val MedAE=0.010090, Bit_Fail=0&lt;br /&gt;
2026.01.01 19:11:40.627 1: SolCast DEBUG&amp;gt; Epoche 3500: Train MSE=0.000184, Val MSE=0.000338, Val MAE=0.014317, Val MedAE=0.012038, Bit_Fail=0&lt;br /&gt;
2026.01.01 19:11:56.504 1: SolCast DEBUG&amp;gt; Epoche 3600: Train MSE=0.000184, Val MSE=0.000338, Val MAE=0.014292, Val MedAE=0.011860, Bit_Fail=0&lt;br /&gt;
2026.01.01 19:12:13.062 1: SolCast DEBUG&amp;gt; Epoche 3700: Train MSE=0.000184, Val MSE=0.000318, Val MAE=0.013443, Val MedAE=0.010655, Bit_Fail=0&lt;br /&gt;
2026.01.01 19:12:28.812 1: SolCast DEBUG&amp;gt; Epoche 3800: Train MSE=0.000184, Val MSE=0.000314, Val MAE=0.013299, Val MedAE=0.010337, Bit_Fail=0&lt;br /&gt;
2026.01.01 19:12:44.335 1: SolCast DEBUG&amp;gt; Epoche 3900: Train MSE=0.000184, Val MSE=0.000316, Val MAE=0.013177, Val MedAE=0.009737, Bit_Fail=0&lt;br /&gt;
2026.01.01 19:12:59.748 1: SolCast DEBUG&amp;gt; Epoche 4000: Train MSE=0.000184, Val MSE=0.000344, Val MAE=0.014781, Val MedAE=0.012876, Bit_Fail=0&lt;br /&gt;
2026.01.01 19:13:16.661 1: SolCast DEBUG&amp;gt; Epoche 4100: Train MSE=0.000184, Val MSE=0.000307, Val MAE=0.013173, Val MedAE=0.010441, Bit_Fail=0&lt;br /&gt;
2026.01.01 19:13:31.964 1: SolCast DEBUG&amp;gt; Epoche 4200: Train MSE=0.000184, Val MSE=0.000338, Val MAE=0.013368, Val MedAE=0.008535, Bit_Fail=0&lt;br /&gt;
2026.01.01 19:13:31.971 1: SolCast DEBUG&amp;gt; Epoche 4200: Train MSE=0.000173, Val MSE=0.000338, Val MAE=0.013368, Val MedAE=0.008535, Bit_Fail=0 -&amp;gt; Snap saved (metric improved)&lt;br /&gt;
2026.01.01 19:13:47.873 1: SolCast DEBUG&amp;gt; Epoche 4300: Train MSE=0.000173, Val MSE=0.000322, Val MAE=0.014152, Val MedAE=0.012068, Bit_Fail=0&lt;br /&gt;
2026.01.01 19:14:02.737 1: SolCast DEBUG&amp;gt; Epoche 4400: Train MSE=0.000173, Val MSE=0.000317, Val MAE=0.013926, Val MedAE=0.011788, Bit_Fail=0&lt;br /&gt;
2026.01.01 19:14:18.079 1: SolCast DEBUG&amp;gt; Epoche 4500: Train MSE=0.000173, Val MSE=0.000301, Val MAE=0.013120, Val MedAE=0.010500, Bit_Fail=0&lt;br /&gt;
2026.01.01 19:14:34.780 1: SolCast DEBUG&amp;gt; Epoche 4600: Train MSE=0.000173, Val MSE=0.000300, Val MAE=0.013251, Val MedAE=0.011131, Bit_Fail=0&lt;br /&gt;
2026.01.01 19:14:50.749 1: SolCast DEBUG&amp;gt; Epoche 4700: Train MSE=0.000173, Val MSE=0.000316, Val MAE=0.013979, Val MedAE=0.011920, Bit_Fail=0&lt;br /&gt;
2026.01.01 19:15:07.488 1: SolCast DEBUG&amp;gt; Epoche 4800: Train MSE=0.000173, Val MSE=0.000298, Val MAE=0.013297, Val MedAE=0.011209, Bit_Fail=0&lt;br /&gt;
2026.01.01 19:15:24.075 1: SolCast DEBUG&amp;gt; Epoche 4900: Train MSE=0.000173, Val MSE=0.000299, Val MAE=0.013338, Val MedAE=0.011219, Bit_Fail=0&lt;br /&gt;
2026.01.01 19:15:40.613 1: SolCast DEBUG&amp;gt; Epoche 5000: Train MSE=0.000173, Val MSE=0.000301, Val MAE=0.012879, Val MedAE=0.009926, Bit_Fail=0&lt;br /&gt;
2026.01.01 19:15:57.651 1: SolCast DEBUG&amp;gt; Epoche 5100: Train MSE=0.000173, Val MSE=0.000296, Val MAE=0.013288, Val MedAE=0.011301, Bit_Fail=0&lt;br /&gt;
2026.01.01 19:16:13.287 1: SolCast DEBUG&amp;gt; Epoche 5200: Train MSE=0.000173, Val MSE=0.000299, Val MAE=0.013429, Val MedAE=0.011297, Bit_Fail=0&lt;br /&gt;
2026.01.01 19:16:29.416 1: SolCast DEBUG&amp;gt; Epoche 5300: Train MSE=0.000173, Val MSE=0.000326, Val MAE=0.014362, Val MedAE=0.012558, Bit_Fail=0&lt;br /&gt;
2026.01.01 19:16:52.624 1: SolCast DEBUG&amp;gt; Epoche 5400: Train MSE=0.000173, Val MSE=0.000293, Val MAE=0.013008, Val MedAE=0.010932, Bit_Fail=0&lt;br /&gt;
2026.01.01 19:17:15.448 1: SolCast DEBUG&amp;gt; Epoche 5500: Train MSE=0.000173, Val MSE=0.000303, Val MAE=0.013566, Val MedAE=0.011497, Bit_Fail=0&lt;br /&gt;
2026.01.01 19:17:36.891 1: SolCast DEBUG&amp;gt; Epoche 5600: Train MSE=0.000173, Val MSE=0.000290, Val MAE=0.012844, Val MedAE=0.010516, Bit_Fail=0&lt;br /&gt;
2026.01.01 19:17:59.157 1: SolCast DEBUG&amp;gt; Epoche 5700: Train MSE=0.000173, Val MSE=0.000301, Val MAE=0.013589, Val MedAE=0.011589, Bit_Fail=0&lt;br /&gt;
2026.01.01 19:18:20.531 1: SolCast DEBUG&amp;gt; Epoche 5800: Train MSE=0.000173, Val MSE=0.000289, Val MAE=0.012913, Val MedAE=0.010712, Bit_Fail=0&lt;br /&gt;
2026.01.01 19:18:35.740 1: SolCast DEBUG&amp;gt; Epoche 5900: Train MSE=0.000173, Val MSE=0.000291, Val MAE=0.013056, Val MedAE=0.010867, Bit_Fail=0&lt;br /&gt;
2026.01.01 19:18:51.160 1: SolCast DEBUG&amp;gt; Epoche 6000: Train MSE=0.000173, Val MSE=0.000305, Val MAE=0.013648, Val MedAE=0.011687, Bit_Fail=0&lt;br /&gt;
2026.01.01 19:19:08.256 1: SolCast DEBUG&amp;gt; Epoche 6100: Train MSE=0.000173, Val MSE=0.000290, Val MAE=0.012878, Val MedAE=0.010646, Bit_Fail=0&lt;br /&gt;
2026.01.01 19:19:23.867 1: SolCast DEBUG&amp;gt; Epoche 6200: Train MSE=0.000173, Val MSE=0.000289, Val MAE=0.012900, Val MedAE=0.010677, Bit_Fail=0&lt;br /&gt;
2026.01.01 19:19:23.869 1: SolCast DEBUG&amp;gt; Early stopping bei Epoche 6200 (no improvement since 2000 epochs)&lt;br /&gt;
2026.01.01 19:19:23.871 1: === Snapshot-Statistik ===&lt;br /&gt;
2026.01.01 19:19:23.873 1: Metric-Improvement Snapshots: 47 (letzte Epoche: 4200)&lt;br /&gt;
2026.01.01 19:19:23.875 1: Bit-Improvement Snapshots:    3 (letzte Epoche: 167)&lt;br /&gt;
2026.01.01 19:19:23.877 1: Bit-Tradeoff Snapshots:       0 (letzte Epoche: 0)&lt;br /&gt;
2026.01.01 19:19:23.884 1: SolCast DEBUG&amp;gt; Best Snapshot reloaded from Epoche 4200: Train MSE=0.000173, Val MSE=0.000338, Val MAE=0.013368, Val MedAE=0.008535, Bit_Fail=0,&lt;br /&gt;
2026.01.01 19:19:23.886 1: SolCast DEBUG&amp;gt; Run Validation Test with 20% of Input data ...&lt;br /&gt;
2026.01.01 19:19:23.930 1: SolCast DEBUG&amp;gt; Validation finished - Best Training MSE=0.000173, Validation MSE=0.000338, Validation Bit_Fail=0&lt;br /&gt;
2026.01.01 19:19:23.933 1: SolCast DEBUG&amp;gt; Retrain check -&amp;gt; &lt;br /&gt;
-- In Normalization Space: -- &lt;br /&gt;
Train MSE=0.000173 &lt;br /&gt;
Val MSE=0.000338 &lt;br /&gt;
Val Mean=0.0002966460 &lt;br /&gt;
VAL/TRAIN MSE Ratio=1.953186 (limit=2.5) &lt;br /&gt;
Diff=0.000165 (limit=0.005) &lt;br /&gt;
ValStd=0.0000123896 (limit=7.41615117947988e-05) &lt;br /&gt;
-- At Original Scale: -- &lt;br /&gt;
MAE=220.184381409254 &lt;br /&gt;
RMSE/MAE=1.3759 (limit=1.5) &lt;br /&gt;
Slope=0.914758 (limit=0.7 .. 1.3) &lt;br /&gt;
Bias=-38.97 (limit=+-110.092190704627) &lt;br /&gt;
R2=0.880327221041415 &lt;br /&gt;
P95=605.3179 (limit=880.737525637016) &lt;br /&gt;
P99=868.5353 (limit=1761.47505127403) &lt;br /&gt;
-- Robustness Indicators: -- &lt;br /&gt;
RMSE relative=65 (limit=20) &lt;br /&gt;
BitFail=0 (limit=5) &lt;br /&gt;
BitFailRate=0.0000 (limit=0.1) &lt;br /&gt;
-&amp;gt; Retrain&lt;br /&gt;
2026.01.01 19:19:23.945 1: SolCast DEBUG&amp;gt; Best model after retries comes from Attempt=3 with: &lt;br /&gt;
Seed=16689959, &lt;br /&gt;
Model Slope=0.91, &lt;br /&gt;
Model Bias=-38.97, &lt;br /&gt;
VAL MedAE=140.59, &lt;br /&gt;
VAL MAE=220.18, &lt;br /&gt;
VAL RMSE=302.94, &lt;br /&gt;
VAL RMSE relative=65 %, &lt;br /&gt;
VAL RMSE_Rating=very bad, &lt;br /&gt;
VAL R2=0.88, &lt;br /&gt;
Val MSE=0.000338&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Auswertung der Trainingskennwerte ====&lt;br /&gt;
&lt;br /&gt;
Abrufbar über:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
get &amp;lt;Name&amp;gt; valDecTree aiNeuralNetConState&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===== Modellparameter ===== &lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Kennwert !! Bedeutung&lt;br /&gt;
|-&lt;br /&gt;
| Normierungsgrenzen || Skalierung der Eingabedaten&lt;br /&gt;
|-&lt;br /&gt;
| Trainingsdaten || Anzahl der Datensätze (Training/Validierung)&lt;br /&gt;
|-&lt;br /&gt;
| Architektur || Inputs, Hidden Layers, Outputs&lt;br /&gt;
|-&lt;br /&gt;
| Hyperparameter || Learning Rate, Momentum, BitFail‑Limit&lt;br /&gt;
|-&lt;br /&gt;
| Aktivierungen || Hidden/Output‑Aktivierungsfunktionen&lt;br /&gt;
|-&lt;br /&gt;
| Zufallsgenerator || Shuffle‑Mode und Shuffle‑Periode&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===== Trainingsmetriken ===== &lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Kennwert !! Interpretation&lt;br /&gt;
|-&lt;br /&gt;
| bestes Modell bei Epoche || Zeitpunkt minimalen Validierungsfehlers&lt;br /&gt;
|-&lt;br /&gt;
| Training MSE / Validation MSE || Fehlermaße; sollten niedrig und ähnlich sein&lt;br /&gt;
|-&lt;br /&gt;
| Validation Bit_Fail || Anzahl fehlerhafter Klassifikationen&lt;br /&gt;
|-&lt;br /&gt;
| bester Trainingslauf || Lauf mit bestem Ergebnis&lt;br /&gt;
|-&lt;br /&gt;
| Trainingsbewertung || Qualitätsindikator (Varianz, Mittelwert)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===== Fehlermaße der Prognosen ===== &lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Kennwert !! Bedeutung&lt;br /&gt;
|-&lt;br /&gt;
| MAE || mittlerer absoluter Fehler (Wh)&lt;br /&gt;
|-&lt;br /&gt;
| MedAE || medianer Fehler&lt;br /&gt;
|-&lt;br /&gt;
| RMSE || quadratischer Fehler, betont Ausreißer&lt;br /&gt;
|-&lt;br /&gt;
| MAPE || prozentualer Fehler&lt;br /&gt;
|-&lt;br /&gt;
| MdAPE || medianer prozentualer Fehler&lt;br /&gt;
|-&lt;br /&gt;
| R² || Gütemaß (0–1)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===== Interpretation des Beispielmodells ===== &lt;br /&gt;
&lt;br /&gt;
Die Modellkennzahlen können nach ausgführten Training mit&lt;br /&gt;
&lt;br /&gt;
 get &amp;lt;Name&amp;gt; valDecTree aiNeuralNetConState&lt;br /&gt;
&lt;br /&gt;
angezeigt werden. Eine Beispielausgabe sieht etwa so aus:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
letztes KI-Training: 25.12.2025 03:52:41 / Laufzeit in Sekunden: 1951&lt;br /&gt;
letzte KI-Ergebnis Generierungsdauer: 20.37 ms&lt;br /&gt;
&lt;br /&gt;
=== Modellparameter ===&lt;br /&gt;
&lt;br /&gt;
Normierungsgrenzen: PV=9276 W, Hausverbrauch: Min=0 W / Max=8937 W&lt;br /&gt;
Trainingsdaten: 7760 Datensätze (Training=6208, Validierung=1552)&lt;br /&gt;
Architektur: Inputs=41, Hidden Layers=80-40-20, Outputs=1&lt;br /&gt;
Hyperparameter: Learning Rate=0.005, Momentum=0.5, BitFail-Limit=0.35&lt;br /&gt;
Aktivierungen: Hidden=SIGMOID, Steilheit=0.9, Output=LINEAR&lt;br /&gt;
Zufallsgenerator: Mode=2, Periode=10&lt;br /&gt;
&lt;br /&gt;
=== Trainingsmetriken ===&lt;br /&gt;
&lt;br /&gt;
bestes Modell bei Epoche: 8690 (von max. 15000)&lt;br /&gt;
Training MSE: 0.000&lt;br /&gt;
Validation MSE: 0.000&lt;br /&gt;
Validation Bit_Fail: 0&lt;br /&gt;
bester Trainingslauf: 2&lt;br /&gt;
Trainingsbewertung: ok (Val MSE Standard Deviation=0.000000, Val MSE Avg=0.000003)&lt;br /&gt;
&lt;br /&gt;
=== Fehlermaße der Prognosen ===&lt;br /&gt;
&lt;br /&gt;
MAE: 122.74 Wh&lt;br /&gt;
MedAE: 69.28 Wh&lt;br /&gt;
RMSE: 0.002&lt;br /&gt;
MAPE: 17.526 %&lt;br /&gt;
MdAPE: 10.611 %&lt;br /&gt;
R²: 0.847&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Interpretation dieser Daten wird im angezeigten Popup mit ausgegeben und ist diesem Fall:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* 7760 Datensätze → sehr gute Basis  &lt;br /&gt;
* 80‑40‑20 Architektur → geeignet für komplexe Muster  &lt;br /&gt;
* MSE ≈ 0 → exzellente Modellanpassung  &lt;br /&gt;
* MAE 122 Wh → sehr präzise  &lt;br /&gt;
* R² = 0.847 → hohe Erklärungsqualität  &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Das Modell ist stabil, generalisiert gut und liefert sehr brauchbare Prognosen.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Modulinterne Datenstrukturen ==&lt;br /&gt;
Das Modul speichert die verschiedenen historischen und aktuellen Daten wie PV-Prognose, Witterungsdaten (Regen, Bewölkung), Strahlungsdaten, Einspeisung, Bezug, reale Erzeugungsdaten und vieles mehr zur Laufzeit im Arbeitsspeicher. Dadurch ist die Verarbeitung dieser Daten im Vergleich zu Lesevorgängen aus dem Filesystem bzw. einer Datenbank sehr performant. Allerdings würden diese Daten bei einem FHEM Absturz bzw. bei einem regulären Shutdown verlorengehen.&lt;br /&gt;
&lt;br /&gt;
Deswegen werden die relevanten Daten regelmäßig in Dateien gesichert und bei Bedarf wiederhergestellt. Diese Dateien befinden sich im Verzeichnis &#039;&#039;&#039;../fhem/FHEM/FhemUtils&#039;&#039;&#039; und heißen &#039;&#039;&#039;.*_SolarForecast_.*&#039;&#039;&#039;. &lt;br /&gt;
&lt;br /&gt;
Die Daten werden in jedem Zyklus der Zentralschleife (CentralTask) gesammelt bzw. berechnet. Der Zyklus wird mit dem Attribut &#039;&#039;&#039;plantControl-&amp;gt;cycleInterval&#039;&#039;&#039; eingestellt.&lt;br /&gt;
&lt;br /&gt;
Zur Laufzeit können diese Daten über get-Kommandos abgerufen werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
get ... pvHistory [Tag]&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der pvHistory Datenspeicher enthält die vergangenen Kennwerte der letzten 31 Tage mit einer Genauigkeit von einer Stunde. Es werden die Prognosewerte für Erzeugung und Verbrauch, die realen Werte für Erzeugung und Verbrauch sowie ebenso die Laufzeiten jedes einzelnen Verbrauchers (Consumer) festgehalten. Für jeden Tag gibt es die Stunde &amp;quot;99&amp;quot; die eine Summierung der Stundenwerte des Tages bzw. Sonderschlüssel enthält.&lt;br /&gt;
&lt;br /&gt;
Bei Bedarf lässt sich mit dem Befehl &amp;quot;set ... reset pvHistory&amp;quot; der gesamte Inhalt des Speichers löschen (nicht empfohlen). Selektiver funktioniert die Datenlöschung mit &amp;quot;set ... reset pvHistory &amp;lt;Tag&amp;gt;&amp;quot; (Daten eines bestimmten Tages löschen) bzw. &amp;quot;set ... reset pvHistory &amp;lt;Tag&amp;gt; &amp;lt;Stunde&amp;gt;&amp;quot; (Daten der Stunde  eines bestimmten Tages löschen). Die letzten beiden Kommandos müssen über die Kommandozeile im Browser ausgeführt werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
get ... pvCircular &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dieser Ringspeicher enthält Daten der letzten 24 Stunden sowie fortwährend berechnete Werte wie Korrekturfaktoren der PV-Erzeugung und Prognosequalität.&lt;br /&gt;
Neue Stundenwerte des Tages überschreiben die gespeicherten Schüssel des vorangegangenen Tages sofern es sich nicht um fortwährend berechnete Werte handelt. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
get ... nextHours&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der Speicher enthält sämtliche Prognosewerte der kommenden Stunden beginnend mit der aktuellen Stunde.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
get ... radiationApiData&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Abhängig von der ausgewählten Strahlungsdaten API enthält dieser Speicher die von der API gelieferten Rohdaten sowie evtl. weitere für die Funktion benötigte Daten wie API-Keys, Response Messages, Anzahl der API Abruf und weitere.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
get ... valCurrent&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Diese Struktur wird nicht regelmäßig im Filesystem gespeichert und enthält immer aktuell gesammelte oder berechnete/gelieferte Daten wie z.B. die Autarkierate, letze Laufzeit der Zentralschleife (CentralTask), Zeit des Sonnenauf- und untergangs sowie weitere Betriebsdaten. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
get ... valConsumerMaster [XX]&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Zeigt die Daten der aktuell im SolarForecast Device registrierten Verbraucher. Mit der Nummer &#039;&#039;&#039;XX&#039;&#039;&#039; kann ein bestimmter Verbraucher angesprungen werden. Die Nummer korrespondiert mit dem entsprechenden &#039;&#039;&#039;consumerXX&#039;&#039;&#039; Attribut.&lt;br /&gt;
Neben anderen Betriebs- und Energiedaten werden insbesondere folgende Daten ermittelt, die für die zukünftige Planung der Verbraucherschaltzeiten relevant sind:&lt;br /&gt;
&lt;br /&gt;
* epieces - prognostizierte Energiescheiben pro Betriebsstunde &lt;br /&gt;
&lt;br /&gt;
Ausgehend von gespeicherten historischen Verbrauchsdaten des Verbrauchers wird der Energieverbrauch des Verbrauchers in seiner ersten Betriebsstunde prognostiziert, was insbesondere für die Planung der optimalen Startzeit des Verbrauchers in Bezug zu der prognostizierten PV Energie und den Verbrauchswerten weiterer vorhandener bzw. zu planender Verbraucher Berücksichtigung findet. Weitere Informationen zur Verbrauchersteuerung in diesem  [[#Verbrauchersteuerung_-_Registrieren_und_visualisieren_von_Verbrauchern|Abschnitt]].&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Schnittstelle für Modulautoren bzw. eigenen Code ===&lt;br /&gt;
&lt;br /&gt;
Die im vorherigen Artikel beschriebenen internen Datenstrukturen können durch externen Code abgefragt und ausgewertet werden.&lt;br /&gt;
Dazu stehen die folgenden Funktionen zur Verfügung. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
::* [FHEM::SolarForecast::]BatteryVal (&#039;&amp;lt;Name&amp;gt;&#039;, &#039;&amp;lt;Bn&amp;gt;&#039;, &#039;&amp;lt;Key&amp;gt;&#039;, &amp;lt;default&amp;gt;) &amp;lt;br&amp;gt; entspricht der Abfrage mit &amp;quot;get ... valBattery&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
::* [FHEM::SolarForecast::]CurrentVal (&#039;&amp;lt;Name&amp;gt;&#039;, &#039;&amp;lt;Key&amp;gt;&#039;, &amp;lt;default&amp;gt;) &amp;lt;br&amp;gt; entspricht der Abfrage mit &amp;quot;get ... valCurrent&amp;quot;. &lt;br /&gt;
&lt;br /&gt;
::* [FHEM::SolarForecast::]InverterVal (&#039;&amp;lt;Name&amp;gt;&#039;, &#039;&amp;lt;Nr&amp;gt;&#039;, &amp;lt;Key&amp;gt;&#039;, &amp;lt;default&amp;gt;) &amp;lt;br&amp;gt; entspricht der Abfrage mit &amp;quot;get ... valInverter&amp;quot;. &lt;br /&gt;
&lt;br /&gt;
::* [FHEM::SolarForecast::]ProducerVal (&#039;&amp;lt;Name&amp;gt;&#039;, &#039;&amp;lt;Nr&amp;gt;&#039;, &amp;lt;Key&amp;gt;&#039;, &amp;lt;default&amp;gt;) &amp;lt;br&amp;gt; entspricht der Abfrage mit &amp;quot;get ... valProducer&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
::* [FHEM::SolarForecast::]HistoryVal (&#039;&amp;lt;Name&amp;gt;&#039;, &#039;&amp;lt;Day&amp;gt;&#039;, &#039;&amp;lt;HoD&amp;gt;&#039;, &#039;&amp;lt;Key&amp;gt;&#039;, &amp;lt;default&amp;gt;) &amp;lt;br&amp;gt; entspricht der Abfrage mit &amp;quot;get ... pvHistory&amp;quot;. &lt;br /&gt;
&lt;br /&gt;
::* [FHEM::SolarForecast::]CircularVal (&#039;&amp;lt;Name&amp;gt;&#039;, &#039;&amp;lt;HoD&amp;gt;&#039;, &#039;&amp;lt;Key&amp;gt;&#039;, &amp;lt;default&amp;gt;) &amp;lt;br&amp;gt; entspricht der Abfrage mit &amp;quot;get ... pvCircular&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
::* [FHEM::SolarForecast::]NexthoursVal (&#039;&amp;lt;Name&amp;gt;&#039;, &amp;lt;nhr&amp;gt;, &#039;&amp;lt;Key&amp;gt;&#039;, &amp;lt;default&amp;gt;) &amp;lt;br&amp;gt; entspricht der Abfrage mit &amp;quot;get ... nextHours&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
::* [FHEM::SolarForecast::]ConsumerVal (&#039;&amp;lt;Name&amp;gt;&#039;, &#039;&amp;lt;Nr&amp;gt;&#039;, &#039;&amp;lt;Key&amp;gt;&#039;, &amp;lt;default&amp;gt;) &amp;lt;br&amp;gt; entspricht der Abfrage mit &amp;quot;get ... valConsumerMaster&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
::* [FHEM::SolarForecast::]RadiationAPIVal (&#039;&amp;lt;Name&amp;gt;&#039;, &#039;&amp;lt;String&amp;gt;&#039;, &#039;&amp;lt;DateTime&amp;gt;&#039;, &#039;&amp;lt;Key&amp;gt;&#039;, &amp;lt;default&amp;gt;) &amp;lt;br&amp;gt; entspricht der Abfrage mit &amp;quot;get ... radiationApiData&amp;quot;. &lt;br /&gt;
&lt;br /&gt;
::* [FHEM::SolarForecast::]WeatherAPIVal (&#039;&amp;lt;Name&amp;gt;&#039;, &#039;&amp;lt;APIname&amp;gt;&#039;, &#039;&amp;lt;TimeCode&amp;gt;&#039;, &#039;&amp;lt;Key&amp;gt;&#039;, &amp;lt;default&amp;gt;) &amp;lt;br&amp;gt; entspricht der Abfrage mit &amp;quot;get ... weatherApiData&amp;quot;. &lt;br /&gt;
&lt;br /&gt;
::* [FHEM::SolarForecast::]StatusAPIVal (&#039;&amp;lt;Name&amp;gt;&#039;, &#039;&amp;lt;APIname&amp;gt;&#039;, &#039;&amp;lt;QTag&amp;gt;&#039;, &#039;&amp;lt;Key&amp;gt;&#039;, &amp;lt;default&amp;gt;) &amp;lt;br&amp;gt; entspricht der Abfrage mit &amp;quot;get ... statusApiData&amp;quot;. &lt;br /&gt;
&lt;br /&gt;
::* [FHEM::SolarForecast::]StringVal (&#039;&amp;lt;Name&amp;gt;&#039;, &#039;&amp;lt;String&amp;gt;&#039;, &#039;&amp;lt;Key&amp;gt;&#039;, &amp;lt;default&amp;gt;) &amp;lt;br&amp;gt; entspricht der Abfrage mit &amp;quot;get ... valStrings&amp;quot;. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Die Angabe des Packages (FHEM::SolarForecast::) ist nur notwendig wenn die Funktionen von außerhalb des SolarForecast-Devices aufgerufen werden. Innerhalb des Devices (Code-Ausführung im Attribut &#039;&#039;ctrlUserExitFn&#039;&#039;) kann darauf verzichtet werden.&lt;br /&gt;
&lt;br /&gt;
Die Bedeutung der Platzhalter sind:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
     &amp;lt;Name&amp;gt;     - Name des SolarForecast Devices&lt;br /&gt;
     &amp;lt;Day&amp;gt;      - abzufragender Tag (01 - 31)&lt;br /&gt;
     &amp;lt;Bn&amp;gt;       - Batterie Nummer (01, 02, ...)&lt;br /&gt;
     &amp;lt;DateTime&amp;gt; - Startzeit der Form YYYY-MM-DD hh:00:00&lt;br /&gt;
     &amp;lt;TimeCode&amp;gt; - Zeitwert der Form fcX_XX (z.B. fc1_19)&lt;br /&gt;
     &amp;lt;APIname&amp;gt;  - Hauptname der API gemäß setupWeatherDev1 (z.B. OpenMeteo)&lt;br /&gt;
     &amp;lt;String&amp;gt;   - Name des PV-Modulstrings wie im Attribut &#039;setupInverterStrings&#039;&lt;br /&gt;
     &amp;lt;QTag&amp;gt;     - Question Tag, z.B. &#039;?All&#039;&lt;br /&gt;
     &amp;lt;Nr&amp;gt;       - laufende Nummer des Verbrauchers / Gerätes (01, 02, 03, 04,...)&lt;br /&gt;
     &amp;lt;HoD&amp;gt;      - abzufragende Stunde (01 - 24, 99)&lt;br /&gt;
     &amp;lt;nhr&amp;gt;      - nächste Stunde (NextHour00, NextHour01,...)&lt;br /&gt;
     &amp;lt;Key&amp;gt;      - der abzufragende Schlüssel &lt;br /&gt;
     &amp;lt;default&amp;gt;  - def default-Rückgabewert&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
==== Beispiel Auslesen der Batterieladestrategie und Anlegen eines Readings ====&lt;br /&gt;
&lt;br /&gt;
Als Praxisbeispiel soll die aktuelle gesetzte Ladestrategie für Batterie &amp;quot;01&amp;quot; ausgelesen und mit dem Wert ein Reading im SolarForecast Device angelegt werden. &amp;lt;br&amp;gt;&lt;br /&gt;
Dazu wird der folgende Code im Attribut &#039;&#039;ctrlUserExitFn&#039;&#039; hinterlegt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
  my $wert = NexthoursVal ($name, &#039;NextHour00&#039;, &#039;strategybat01&#039;, &#039;loadRelease&#039;);&lt;br /&gt;
  storeReading (&#039;Ladestrategie&#039;, $wert);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bei jedem Zyklus des SF-Devices wird der aktuelle Wert von &#039;&#039;strategybat01&#039;&#039; gelesen und im Reading &#039;&#039;Ladestrategie&#039;&#039; gespeichert. Die Paket-Definition &#039;&#039;FHEM::SolarForecast::&#039;&#039; braucht nicht verwendet werden, da der Aufruf im Attribut ctrlUserExitFn innerhalb des SolarForecast-Paketes erfolgt.&lt;br /&gt;
&lt;br /&gt;
Für andere Batterienummern ist der NexthoursVal-Schlüssel &#039;&#039;strategybat01&#039;&#039; entsprechend anzupassen.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Auswerten von internen Arrays ===&lt;br /&gt;
&lt;br /&gt;
Die meisten der internen Datenstrukturen liefern bei der Abfrage einen einfachen String oder numerischen Wert der ausgewertet werden kann. Es gibt allerdings auch intern gespeicherte Wertelisten. Werden die Listenstrukturen mit den oben beschriebenen Befehlen abgefragt, wird eine Referenz auf die Liste geliefert sofern sie vorhanden ist.&lt;br /&gt;
&lt;br /&gt;
Um auf die Werte einer solchen Liste zuzugreifen, ist am Beispiel des Arrays &#039;&#039;surplusslidereg&#039;&#039; aus dem Current-Speicher (get ... valCurrent) dargestellt: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
my $splref = CurrentVal ($name, &#039;surplusslidereg&#039;, &#039;.&#039;);&lt;br /&gt;
my $spser  = ref $splref eq &#039;ARRAY&#039; ? join &#039; &#039;, @{$splref} : undef;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Variable $spser enthält bei Vorhandensein der Liste einen String, der die Listenelemente von &#039;surplusslidereg&#039; durch Leerzeichen getrennt enthält.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Backup und Wiederherstellung der Moduldaten ==&lt;br /&gt;
Neben der Datenhaltung in den Attributen und Readings des SolarForecast Devices, erzeugt jedes SolarForecast Device Dateien im Verzeichnis  &#039;&#039;&#039;../fhem/FHEM/FhemUtils&#039;&#039;&#039; mit folgenden Inhalten:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
PVH_SolarForecast_&amp;lt;name&amp;gt;        - PV History&lt;br /&gt;
PVC_SolarForecast_&amp;lt;name&amp;gt;        - PV Circular&lt;br /&gt;
PVCfg_SolarForecast_&amp;lt;name&amp;gt;      - PV Anlagenkonfiguration&lt;br /&gt;
PVCsm_SolarForecast_&amp;lt;name&amp;gt;      - Consumer Status&lt;br /&gt;
ScApi_SolarForecast_&amp;lt;name&amp;gt;      - Strahlungswerte aus verwendeter API&lt;br /&gt;
StatApi_SolarForecast_&amp;lt;name&amp;gt;    - Statusdaten der verwendeten APIs&lt;br /&gt;
WeatherApi_SolarForecast_&amp;lt;name&amp;gt; - Wetterdaten der gewählten Wetter-API (außer DWD-Device)&lt;br /&gt;
AItra_SolarForecast_&amp;lt;name&amp;gt;      - KI Entscheidungsquelle (trainierte Daten)&lt;br /&gt;
AIraw_SolarForecast_&amp;lt;name&amp;gt;      - KI Input Daten = Raw Trainigsdaten&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Je nach Ausprägung und eingesetzten Features des SolarForecast Devices sind alle oder nur ein Teil dieser Dateien vorhanden. Dabei ist &#039;&#039;&#039;&amp;lt;name&amp;gt;&#039;&#039;&#039; der Name des SolarForecast Devices.&lt;br /&gt;
&lt;br /&gt;
Bei einem Filebackup des FHEM-Servers ist darauf zu achten, dass diese Dateien in dem Backup enthalten sind. Der Inhalt der Dateien ist nicht statisch, sondern kann sich dynamisch aktualisieren. Um im Bedarfsfall einen aktuellen Datenstand zu haben, ist mindestens ein tägliches Backup dieser Dateien empfehlenswert. Eventuell auch öfter mit Hilfe einer Versionierung welche z.B. immer die letzten drei Versionen aufbewahrt.&lt;br /&gt;
&lt;br /&gt;
Beim Start von FHEM werden bestimmte Dateien automatisch gelesen und in das SolarForecast Device importiert. &lt;br /&gt;
Der Vorgang ist mit verbose=3 im Log protokolliert:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
2023.10.02 09:57:07.967 3: SolCast6 - cached data &amp;quot;pvHistory&amp;quot; restored&lt;br /&gt;
2023.10.02 09:57:07.969 3: SolCast6 - cached data &amp;quot;pvCircular&amp;quot; restored&lt;br /&gt;
2023.10.02 09:57:07.970 3: SolCast6 - cached data &amp;quot;consumerMaster&amp;quot; restored&lt;br /&gt;
2023.10.02 09:57:07.970 3: SolCast6 - cached data &amp;quot;radiationApiData&amp;quot; restored&lt;br /&gt;
2023.10.02 09:57:07.970 3: SolCast6 - cached data &amp;quot;statusApiData&amp;quot; restored&lt;br /&gt;
2023.10.02 09:57:07.970 3: SolCast6 - cached data &amp;quot;weatherApiData&amp;quot; restored&lt;br /&gt;
2023.10.02 09:57:07.974 3: SolCast6 - cached data &amp;quot;aiTrainedData&amp;quot; restored&lt;br /&gt;
2023.10.02 09:57:07.976 3: SolCast6 - cached data &amp;quot;aiRawData&amp;quot; restored&lt;br /&gt;
&amp;lt;/pre&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Bestimmte Betriebsdaten, wie z.B. die witterungsabhängigen Korrekturfaktoren (circular) oder die KI-Daten (aitrained/airaw), bauen sich über die Zeit immer granularer auf und stellen einen wertvollen Datenbestand dar.&lt;br /&gt;
&lt;br /&gt;
Wird das SolarForecast Device gelöscht und anschließend wieder neu mit dem gleichen Namen definiert, können die bisher verfügbaren Daten sehr einfach wiederhergestellt werden:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* 1. definieren des neuen SolarForecast Devices, d.h. ein einfaches &amp;quot;define &amp;lt;Name&amp;gt; SolarForecast&amp;quot;, und sichern der FHEM Konfiguration (der Konfigurationsdialog muß nicht durchgeführt werden)&lt;br /&gt;
* 2. FHEM stoppen&lt;br /&gt;
* 3. wiederherstellen der oben beschriebenen Dateien aus einem Backup in das Verzeichnis ../fhem/FHEM/FhemUtils (Überschreiben evtl. vorhandener Dateien)&lt;br /&gt;
* 4. Anpassen der Dateieigentümer und Berechtigungen: chown fhem:dialout /opt/fhem/FHEM/FhemUtils/*,  chmod 774 /opt/fhem/FHEM/FhemUtils/*&lt;br /&gt;
* 5. starten von FHEM (bestimmte Daten werden automatisch importiert)&lt;br /&gt;
* 6. mit dem Befehl &amp;quot;set &amp;lt;name&amp;gt; plantConfiguration restore&amp;quot; die Anlagenkonfiguration wiederherstellen&lt;br /&gt;
* 7. FHEM Konfiguration sichern und restarten&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Hinweis:&#039;&#039;&#039; &amp;lt;br&amp;gt;&lt;br /&gt;
Das zuvor beschriebene Verfahren kann auch angewendet werden um das SolarForcast Device von einer FHEM Installation in eine andere Installation umzuziehen. In diesem Fall ist aber besonders darauf zu achten, vorab alle in den SF-Attributen referenzierten Devices wie Inverter, Meter oder DWD-Devices mit den zutreffenden Namen in der neuen Installation anzulegen.&lt;br /&gt;
  &lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
Soll das neue SolarForecast Device mit einem anderen Namen oder weitere SolarForecast Devices erstellt werden, welche die Betriebsdaten des primären Devices verwenden sollen, ist wie folgt vorzugehen:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* 1. definieren des neuen SolarForecast Devices, d.h. ein einfaches &amp;quot;define &amp;lt;Name&amp;gt; SolarForecast&amp;quot;, und sichern der FHEM Konfiguration (der Konfigurationsdialog muß nicht durchgeführt werden)&lt;br /&gt;
* 2. FHEM stoppen&lt;br /&gt;
* 3. wiederherstellen der oben beschriebenen Dateien aus einem Backup in das Verzeichnis ../fhem/FHEM/FhemUtils (Überschreiben evtl. vorhandener Dateien)&lt;br /&gt;
* 4. umbenenen der Dateien, wobei &amp;lt;name&amp;gt; durch den Namen des neuen SolarForecast Devices ersetzt wird&lt;br /&gt;
* 5. Anpassen der Dateieigentümer und Berechtigungen: chown fhem:dialout /opt/fhem/FHEM/FhemUtils/*,  chmod 774 /opt/fhem/FHEM/FhemUtils/*&lt;br /&gt;
* 6. starten von FHEM (bestimmte Daten werden automatisch importiert)&lt;br /&gt;
* 7. mit dem Befehl &amp;quot;set &amp;lt;name&amp;gt; plantConfiguration restore&amp;quot; die Anlagenkonfiguration wiederherstellen&lt;br /&gt;
* 8. FHEM Konfiguration sichern und restarten&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Wenn sich die SolarForecast Devices hinsichtlich ihres Models oder der integrierten Consumer unterscheiden, sind die Dateien &amp;quot;PVCsm_SolarForecast_&amp;lt;name&amp;gt; &amp;quot; , &amp;quot;ScApi_SolarForecast_&amp;lt;name&amp;gt;&amp;quot; von dem Verfahren auszuschließen.&lt;br /&gt;
&lt;br /&gt;
== Batterieintegration und -steuerung ==&lt;br /&gt;
&lt;br /&gt;
=== Wie eine Batterie eingebunden wird ===&lt;br /&gt;
Wie immer muß die Batterieanlage zunächst mit einem für die Batterie spezifischen FHEM-Device in FHEM definiert sein.&lt;br /&gt;
In diesem Device werden Readings erwartet die folgende Werte bereitstellen:&lt;br /&gt;
&lt;br /&gt;
::* ein Reading welches die aktuelle Batterieladeleistung liefert&lt;br /&gt;
&lt;br /&gt;
::* ein Reading welches die aktuelle Batterieentladeleistung liefert&lt;br /&gt;
&lt;br /&gt;
::* ein Reading welches die installierte Batteriekapazität liefert (&#039;&#039;&#039;Hinweis:&#039;&#039;&#039; dieser Schlüssel ist für die Aktivierung des Batterie SOC-Managements und die grafische Anzeige des SOC wesentlich!)&lt;br /&gt;
&lt;br /&gt;
::* ein Reading welches die bis dato vorliegende Lade-Energiemenge als fortlaufenden Zähler liefert (optional)&lt;br /&gt;
&lt;br /&gt;
::* ein Reading welches die bis dato vorliegende Entlade-Energiemenge als fortlaufenden Zähler liefert (optional)&lt;br /&gt;
&lt;br /&gt;
::* Reading welches den aktuellen Ladezustand (SOC in Prozent) liefert (optional)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Die Batterie, bzw. das Batteriedevice wird mit dem Attribut &#039;&#039;&#039;setupBatteryDev&#039;&#039;&#039; im SolarForecast Device registriert. Die Syntax des Attributes ist in  der Online-Hilfe zum Attribut umfassend erläutert. &lt;br /&gt;
Auch wenn manche Readings optional sind, wird empfohlen alle Werte bereitzustellen um alle Batterie-Funktionen im Modul nutzen zu können. Durch die Weiterentwicklung des Moduls können optionale Readings später auch obligatorisch werden.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Visualisierung der Batterie in der Balkengrafik ====&lt;br /&gt;
&lt;br /&gt;
Das Attribut &#039;&#039;&#039;setupBatteryDevXX&#039;&#039;&#039; verfügt über den Schlüssel &#039;&#039;&#039;show&#039;&#039;&#039;, mit dem die Einblendung des bzw. der Batteriesymbole in den Ebenen der Balkengrafik gesteuert werden kann:&lt;br /&gt;
[[Datei:Screenshot 2025-02-23 192002.png|right|thumb|400px|Einblendung der Batterieleiste und Darstellung des Batterie-SoC als Balkencontent in der zweiten Ebene]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* 0 - keine Anzeige des Gerätes (default)&lt;br /&gt;
* 1 - Anzeige des Gerätes in der Balkengrafik Ebene 1&lt;br /&gt;
* 2 - Anzeige des Gerätes in der Balkengrafik Ebene 2 &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Die Symbole und die Farbgebung der Batterie(n) können mittels des Schlüssels &#039;&#039;&#039;icon&#039;&#039;&#039; verändert werden. Die Online-Hilfe gibt weitere Informationen zur Ausgestaltung. &lt;br /&gt;
&lt;br /&gt;
In der Batteriedarstellung wird das Symbol der aktuellen Stunde hervorgehoben. Ein Mouse-Over zeigt wesentliche Betriebszustände der Batterie. Die Batteriesymbole vergangener Stunden zeigen mit einem Mouse-Over die erreichten SoC (State of Charge) Stände. Die kommenden Stunden visualisieren die folgenden Prognosen:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* der prognostizierte SoC unter Berücksichtigung der PV-Prognose und der [[#Aktivierung_des_Batterie_SOC-Managements|Batteriesteuerung]] sofern sie im Modul aktiviert ist.&lt;br /&gt;
* eine Freigabe bzw. Empfehlung der Batterieladung zur [[#Unterstützung_eines_netzdienlichen_Verhaltens|Unterstützung eines netzdienlichen Verhaltens]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Neben der Einblendung der Batterieleiste über der Balkengrafik selbst können bestimmte Batteriekennwerte als Balkeninhalt dargestellt werden. Die Auswahl erfolgt über das Attribut &#039;&#039;&#039;graphicBeamXContent&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== Aktivierung des Batterie SOC- und Lade-Managements ===&lt;br /&gt;
Das integrierte SOC- und Lade-Management verfolgt insbesondere diese Ziele: &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* den Ladezustand der Batterie(n) zu optmimieren, was besonders in den Monaten mit geringerer PV-Erzeugung einen wertvollen Beitrag zur Erhöhung der Autarkierate und Batteriepflege leisten kann&lt;br /&gt;
 &lt;br /&gt;
* eine PV-Prognose und Verbrauch optimierte Beladungssteuerung, die in den Monaten mit hoher PV-Erzeugung hilft eine evtl. Abregelung der Anlage zu verhindern, die Batterie ausgewogen über den Tag zu laden und ein netzdienliches Verhalten zu unterstützen&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Diese Aspekte der Batteriesteuerung werden in den folgenden Abschnitten beschrieben. Sind mehrere Batterien installiert, kann die Steuerung getrennt für jede einzelne Batterie aktiviert und eingestellt werden. Dabei ist zu beachten, dass vom Modul nur Signale und Readings bereitgestellt werden, die durch eine entsprechende Routine ausgewertet werden können um spezifische Befehle zur Umsetzung der Steuerung an das Batteriesystem zu senden. &lt;br /&gt;
 &lt;br /&gt;
Für eine Aktivierung des Managements muß zunächst das Batterie-Device wie im vorigen Abschnitt im SolarForecast Device registriert werden.&lt;br /&gt;
Das Attribut &#039;&#039;&#039;ctrlBatSocManagementXX&#039;&#039;&#039; aktiviert die im Modul integrierte SOC- und Lade-Management Routine: &lt;br /&gt;
&lt;br /&gt;
 attr &amp;lt;Device&amp;gt; ctrlBatSocManagementXX lowSoc=&amp;lt;Wert&amp;gt; upSoC=&amp;lt;Wert&amp;gt; [maxSoC=&amp;lt;Wert&amp;gt;] [careCycle=&amp;lt;Wert&amp;gt;] [loadAbort=&amp;lt;SoC1&amp;gt;:&amp;lt;MinPwr&amp;gt;:&amp;lt;SoC2&amp;gt;] [safetyMargin=&amp;lt;Wert&amp;gt;[:&amp;lt;Wert&amp;gt;]] ...&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Die einzelnen Schlüssel werden der Kalkulationsroutine als Steuerungsparameter übergeben und bedeuten:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
;lowSoc:unterer Mindest-SoC, die Batterie wird nicht tiefer als dieser Wert entladen (muß &amp;gt; 0 sein). Dieser Wert kann zum Beispiel dazu dienen, stets eine Mindestenergiemenge für den Fall eines Stromausfalls im öffentlichen Netz in den Batterien vorzuhalten.&lt;br /&gt;
&lt;br /&gt;
;upSoC:oberer Mindest-SoC, Der übliche Wert des optimalen SoC bewegt sich in Perioden mit hohen PV-Überschuß tendenziell zwischen &#039;lowSoC&#039; und &#039;upSoC&#039;, in Perioden mit geringer PV-Ausbeute bzw. geringem PV Energieüberschuß bewegt sich der optimale SoC tendenziell zwischen &#039;upSoC&#039; und &#039;maxSoC&#039;. Die Einstellung von upSoC=50 für ein ausgewogenes Verhalten kann als guter Startwert dienen.&lt;br /&gt;
&lt;br /&gt;
;maxSoC:maximaler Mindest-SoC, d.h. der SoC Wert der mindestens im Abstand von &#039;careCycle&#039; Tagen erreicht werden muß um den Ladungsausgleich im Speicherverbund auszuführen. Die Angabe ist optional (muß &amp;lt;= 100 sein, default: 95)&lt;br /&gt;
&lt;br /&gt;
;stepSoC: Optionale Schrittweite zur optimalen SoC-Berechnung (Battery_OptimumTargetSoC_XX) in %. Mit der Angabe &#039;stepSoC=0&#039; wird das SoC-Management deaktiviert und Battery_OptimumTargetSoC_XX auf den Wert &#039;lowSoC&#039; gesetzt. &#039;&#039;&#039;Hinweis:&#039;&#039;&#039; Die Beziehung &#039;careCycle * stepSoC = 100&#039; sollte eingehalten werden! Wert: 0..5, default: 5 &lt;br /&gt;
&lt;br /&gt;
;careCycle:maximaler Abstand in Tagen, der zwischen zwei Ladungszuständen von mindestens &#039;maxSoC&#039; auftreten sollte. Anders formuliert legt careCycle fest, alle wieviel Tage mindestens einmal maxSoC erreicht werden sollte. Die Angabe ist optional. (default: 20)&lt;br /&gt;
&lt;br /&gt;
;lcSlot: Es wird ein tägliches Zeitfenster festgelegt, in dem die Ladesteuerung des Moduls für diese Batterie aktiv sein soll. Außerhalb des Zeitfensters wird die Batterieladung mit voller Leistung freigegeben. Das SoC-Management der Batterie ist davon nicht betroffen. Wert: &amp;lt;hh:mm&amp;gt;-&amp;lt;hh:mm&amp;gt;, default: ganztägig&lt;br /&gt;
&lt;br /&gt;
;loadAbort: definiert eine vom Anwender gewünnschte Bedingung für einen generellen Ladeabbruch und Wiederfreigabe der Lademöglichkeit. Die Abbruchbedingung ist erfüllt, wenn der angegebene SoC1 (%) erreicht bzw. überschritten ist &#039;&#039;&#039;UND&#039;&#039;&#039; die angegebene Ladeleistung &amp;lt;MinPwr&amp;gt; (W) unterschritten wurde -&amp;gt; Reading Battery_ChargeAbort_XX=1. Fällt der aktuelle SoC wieder unter den SoC2, wird Battery_ChargeAbort_XX=0 gesetzt. Ist SoC2 nicht angegeben, gilt SoC2=SoC1.  &lt;br /&gt;
&lt;br /&gt;
;loadStrategy: Abhängig von der gewählten Ladestrategie wird die Prognose der Batterieladung und ggf. die Generierung der Steuerreadings beeinflusst. Die Angabe ist optional. Wert: loadRelease | optPower | smartPower, default=loadRelease&lt;br /&gt;
	&lt;br /&gt;
;loadTarget: Optionaler Ziel-SoC in % für die Berechnung der Ladefreigabe bzw. der optimalen Ladeleistung. Der Zielwert ist eine kalkulatorische Rechengröße. Der reale SoC kann situativ in Grenzen über- oder unterschritten werden. Der höhere Wert aus Reading Battery_OptimumTargetSoC_XX und &#039;loadTarget&#039; hat für die Berechnung Vorrang. Wert: 0..100, default=100. &amp;lt;br&amp;gt; In loadTarget kann eine Zielzeit angegeben werden, wann der Ziel-SoC erreicht werden soll. Die optimale Ladeleistung wird entsprechend angepasst. Die Zeitangabe als positive Ganzzahl ist ein Absolutwert (Stunde), in der der Ziel-SoC erreicht sein soll. Ein negativer Integer (z.B. -3) besagt, dass drei Stunden vor dem [[SolarForecast_-_Solare_Prognose_(PV_Erzeugung)_und_Verbrauchersteuerung#Bestimmung_von_Sonnenaufgang_und_Sonnenuntergang|Sonnenuntergang]] des aktuellen Tages der Ziel-SoC erreicht sein soll. D.h. negative Zahlen sind Relativangaben. Generell wird der Ziel-SoC zur Wunschzeit nur dann erreicht sein, wenn genügend PV-Leistung verfügbar ist. &lt;br /&gt;
&lt;br /&gt;
;safetyMargin: Bei der Berechnung der Ladefreigabe und optimierten Ladeleistung werden Sicherheitszuschläge auf den prognostizierten Ladungsbedarf berücksichtigt. Abweichend vom Default können mit diesem Parameter individuelle Sicherheitszuschläge getrennt für die Berechnung der Ladefreigabe und optimierten Ladeleistung angegeben werden. Der erste Wert ist der Zuschlag bei der Berechnung der Ladefreigabe, der zweite Wert der Zuschlag bei der Berechnung der optimierten Ladeleistung. Beide Angaben sind Prozentwerte von 0..100.&lt;br /&gt;
&lt;br /&gt;
;weightOwnUse: Optionale Gewichtung der stündlichen Verbrauchsprognose als zusätzlich verwendbaren Anteil zur Batterieladung in %. Technisch wird der verfügbare PV-Überschuß zur Berechnung der optimierten Ladeleistung erhöht indem der kalkulierte Verbrauch um den angegebenen Prozentsatz gesenkt wird. Wert: 0..100 default: 0 &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Alle SoC-Werte sind ganze Zahlen in %. Dabei gilt: &lt;br /&gt;
&lt;br /&gt;
 lowSoc &amp;lt; upSoC &amp;lt; maxSoC&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
====  Erläuterung der Batterie-Steuerungsreadings ==== &lt;br /&gt;
&lt;br /&gt;
Für die Steuerung des Batteriesystems gibt es im SolarForecast einen Satz an Readings, die unterschiedliche Zustände signalisieren und für entsprechende Steuerungsaufgaben bezüglich der Batteriesysteme eingesetzt werden können. In den nachfolgenden Abschnitten werden diese Zusammenhänge noch dataillierter beschrieben.&lt;br /&gt;
&lt;br /&gt;
;Hinweis: SolarForecast stellt lediglich Steuerungsinformationen zur Verfügung. Die Umsetzung des Eingriffs in das Batteriesystem erfolgt durch den Anwender selbst über entsprechende Skripte. In den thematischen Abschnitten werden Beispiele für solche Steuerungsskripte vorgestellt.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Alle erstellten Readings gelten für ein Batteriesystem, es können mehrere Batteriesysteme in SolarForecast integriert sein. Die Endung &#039;&#039;XX&#039;&#039; der Readings beschreibt die Nummer des Batteriesystems und korrespondiert mit der Endung des entsprechenden Attributes &#039;&#039;ctrlBatSocManagementXX&#039;&#039; bzw. &#039;&#039;setupBatteryDevXX&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
;Battery_OptimumTargetSoC_XX: Mit den im Attribut &#039;&#039;ctrlBatSocManagementXX&#039;&#039; hinterlegten Parametern unter Berücksichtigung von PV- und Verbrauchsprognosen wird ein optimaler SOC-Wert errechnet. Dieser Wert dient zur Einstellung des unteren SOC des Batteriesystems.&lt;br /&gt;
&lt;br /&gt;
;Battery_ChargeRequest_XX: Dieses Reading signalisiert mit 1 eine Anforderung zur Zwangsladung der Batterie um die Batterie vor Schäden zu schützen (wenn der SOC unter ctrlBatSocManagementXX-&amp;gt;lowSoc gefallen ist) oder wenn der aktuelle SOC unter den berechneten Mindest-SOC gefallen ist.&lt;br /&gt;
&lt;br /&gt;
;Battery_ChargeUnrestricted_XX: Dieses Reading kann den Wert &#039;&#039;&#039;0&#039;&#039;&#039; oder &#039;&#039;&#039;1&#039;&#039;&#039; einnehmen. Ist der Wert 0, sollte die Batterie nur geladen werden, wenn der im Attribut &#039;&#039;plantControl-&amp;gt;feedinPowerLimit&#039;&#039; definierte Wert überschritten wird. Ist der Wert 1, sollte die Batterie mit der vollen zur Verfügung stehenden Ladeleistung angesteuert werden, um wie prognostiziert am Ende des Sonnentages die volle bzw. maximal mögliche Ladung zu erhalten. Der Fokus der Steuerung mit diesem Reading ist es, die Speicherkapazität der Batterie optimal zur Verhinderung der Überschreitung von Einspeiselimits einzusetzen und dennoch einen maximal möglichen SoC am Tagesende zu erreichen. (siehe das beschriebene [[SolarForecast_-_Solare_Prognose_(PV_Erzeugung)_und_Verbrauchersteuerung#PV-Prognose_und_Verbrauch_optimierte_Beladungssteuerung_unter_Berücksichtigung_einer_Wirkleistungsbegrenzung|Nutzungsbeispiel]])&lt;br /&gt;
&lt;br /&gt;
;Battery_ChargeOptTargetPower_XX: Dieses Reading enthält einen Richtwert für die optimierte Ladeleistung für jede Batterie. Die Aufladung wird prognosegeführt über den gesamten Tag verteilt, wobei der Fokus auf eine kontinuierliche, aber möglichst niederige Ladeleistung gelegt wird. (siehe diesen [[SolarForecast_-_Solare_Prognose_(PV_Erzeugung)_und_Verbrauchersteuerung#leistungsoptimierte_Beladungssteuerung_mit_dem_Fokus_geringe_Verlustleistung_und_Verschleiß|Abschnitt]])  &lt;br /&gt;
&lt;br /&gt;
;Battery_ChargeAbort_XX: Im Attribut &#039;&#039;ctrlBatSocManagementXX&#039;&#039; kann mit dem Schlüssel &#039;&#039;loadAbort&#039;&#039; eine Bedingung definiert werden, bei deren Eintritt die Ladung der Batterie abgebrochen werden soll. In diesem Fall wird &#039;&#039;Battery_ChargeAbort_XX=1&#039;&#039; gesetzt. Mit einem entsprechenden Befehl kann der Anwender das Batteriesystem anweisen den Ladevorgang abzubrechen. &lt;br /&gt;
&lt;br /&gt;
;Battery_TargetAchievable_XX: Diese Reading enthält einen Boolean Wert 0|1 und signalisiert ob der im Attribut &#039;&#039;&#039;ctrlBatSocManagementXX-&amp;gt;loadTarget&#039;&#039;&#039; gesetzte Ziel-SoC lt. Prognose am aktuellen Tag tatsächlich erreicht werden kann.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Nutzung des Batterie SOC-Management ====&lt;br /&gt;
&lt;br /&gt;
Oftmals wird durch die Anlagenhersteller ein hoher statischer SOC eingestellt bzw. empfohlen, der eine längere Verharrungszeit auf einem tiefen SOC-Wert verhindern soll. Allerdings wechseln sich in auch in den Herbst-, Frühlings- und Wintermonaten Zeiten mit wenig PV-Erzeugung mit Phasen stärkerer Solarstrahlung bei klarem Himmel und Sonnenschein ab.&lt;br /&gt;
&lt;br /&gt;
Ein statischer SOC würde dazu führen, dass in den erwähnten Phasen zuviel Energie in das öffentliche Netz eingespeist wird. Dies soll verhindert werden da die wertvolle Energie besser im eigenen Netz gespeichert und in Dunkelphasen verwendet werden kann. Andererseits soll die Batterie nicht zu lange auf einem tiefen SOC verbleiben und außerdem in gewissen Abständen auf einen so hohen SOC geladen werden der den internen Zellausgleich ermöglicht.&lt;br /&gt;
&lt;br /&gt;
Die SOC Kalkulationsroutine des Moduls errechnet unter Berücksichtigung des prognostizierten PV-Ertrages der kommenden Tage, des aktuellen SOC und der im Attribut &#039;&#039;&#039;ctrlBatSocManagementXX&#039;&#039;&#039; (nachfolgend beschrieben) angegebenen Steuerparameter den Vorschlag für eine optimale SOC-Einstellung. Dabei steht &#039;XX&#039; für die Nummer der Batterie, welche mit dem Attribut setupBatteryDevXX korrespondiert.&lt;br /&gt;
&lt;br /&gt;
Durch das Modul selbst findet keine Steuerung der Batterie statt. Es stellt entsprechende Readings zur Verfügung:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
::* das Reading &#039;&#039;&#039;Battery_OptimumTargetSoC_XX&#039;&#039;&#039; enthält den vom Modul berechneten optimalen SoC.&lt;br /&gt;
&lt;br /&gt;
::* das Reading &#039;&#039;&#039;Battery_ChargeRequest_XX&#039;&#039;&#039; wird auf &#039;1&#039; gesetzt, wenn der aktuelle SoC unter den optimalen SoC gefallen ist. In diesem Fall sollte die Batterie, unter Umständen mit Netzstrom, zwangsgeladen werden.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Die Readings können zur Steuerung des SoC (State of Charge) sowie zur Steuerung des verwendeten Ladestroms der Batterie verwendet werden.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Die Ermittlung des optimalen SOC erfolgt nach folgender Logik:&lt;br /&gt;
&lt;br /&gt;
# 	Ausgehend von &#039;lowSoc&#039; wird der Mindest-SoC kurz vor Sonnenuntergang um 5% inkrementiert sofern am laufenden Tag &#039;maxSoC&#039; nicht erreicht wurde und die PV-Prognose keinen hinreichenden Ertrag des kommenden Tages vorhersagt. &amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
# 	Wird &#039;maxSoC&#039; (wieder) erreicht, wird Mindest-SoC um 5%, aber nicht tiefer als &#039;lowSoc&#039;, verringert. Ist der berechnete Mindest-SoC größer als &#039;upSoc&#039;, wird der Mindest-SoC auf &#039;upSoc&#039; gesetzt. &amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
#   Mindest-SoC wird soweit verringert, dass die prognostizierte PV Energie des aktuellen bzw. des folgenden Tages von der Batterie aufgenommen werden kann. Mindest-SoC wird typisch auf &#039;upSoc&#039; und nicht tiefer als &#039;lowSoc&#039; verringert.  &amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
# 	Das Modul erfasst den letzten Zeitpunkt am &#039;maxSoC&#039;-Level, um eine Ladung auf &#039;maxSoC&#039; mindestens alle &#039;careCycle&#039; Tage zu realisieren. Zu diesem Zweck wird der optimierte SoC in Abhängigkeit der Resttage bis zum nächsten &#039;careCycle&#039; Zeitpunkt derart verändert, dass durch eine tägliche 5% SoC-Steigerung &#039;maxSoC&#039; am &#039;careCycle&#039; Zeitpunkt rechnerisch erreicht wird. Wird zwischenzeitlich &#039;maxSoC&#039; erreicht, beginnt der &#039;careCycle&#039; Zeitraum erneut. &amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
# 	Im fünften Schritt werden die Grenzen &#039;lowSoc&#039; und &#039;upSoc&#039; berücksichtigt. &amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
# 	Im letzten Schritt erfolgt Generierung des Readings &#039;&#039;&#039;Battery_ChargeRequest_XX&#039;&#039;&#039; zur Signalisierung einer eventuell empfohlenen Zwangsladung auf den berechneten Min-SoC. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Bei sehr wenig Erzeugungungsprognose, d.h. wenn sich der berechnete Min-SoC oberhalb von &#039;upSoC&#039; bewegt, wird &#039;upSoC&#039; eingestellt. Liegt der berechnete Min-SoC unterhalb von &#039;upSoC&#039;, ist jedoch größer als &#039;lowSoC&#039;, wird der berechnete SOC im Reading &#039;&#039;&#039;Battery_OptimumTargetSoC_XX&#039;&#039;&#039; eingestellt. Somit eignet sich die Angabe im Schlüssel &#039;upSoC&#039; um in Zeiten geringer Solarenergieerzeugung die Batterie für eine Notstromerzeugung auf mindestens diesem Wert zu halten. Sollte die PV-Prognose kurzfristig einen größeren Ertrag vorhersagen als die Batterie durch die Einhaltung von &#039;upSoC&#039; aufnehmen kann, wird der Min-SoC unter &#039;upSoC&#039; abgesenkt um möglichst allen PV-Überschuß in der Batterie zu speichern und somit dem Eigenverbrauch zuzuführen.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Der berechnete Mindest-SoC ist immer nur ein unterer Grenzwert. Die Batterie kann durch Ladevorgänge natürlich jederzeit einen höheren als den im Reading &#039;&#039;&#039;Battery_OptimumTargetSoC_XX&#039;&#039;&#039; dargestellten optimalen SoC haben. Der optimale SoC sollte nicht unterschritten werden um die oben genannten Zusammenhänge abbilden zu können.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Die dynamische SOC Steuerung mit Umsetzungsbeispiel =====&lt;br /&gt;
Das Modul bietet eine Unterstützung zur automatisierten Einstellung eines optimalen SoC Wertes.&lt;br /&gt;
&lt;br /&gt;
Ziel ist es, den Minimum SoC in Abhängigkeit der zu erwartenden Solarerträge des aktuellen und des folgenden Tages so einzustellen, dass der prognostizierte Solarertrag von der Batterie aufgenommen werden kann. Die Einspeisung in das öffentliche Netz soll minimiert werden.&lt;br /&gt;
&lt;br /&gt;
Weiterhin soll ein unterer SoC nicht unterschritten, sowie ein oberer Minimum SoC im Normalfall nicht überschritten werden. Der obere Grenzwert stellt sicher, dass die Batterie mindestens bis zu diesem Wert wieder entladen werden kann. Dadurch bleibt innerhalb der Batterie immer genügend Kapazität verfügbar um unvorhergesehene PV Energie aufnehmen zu können.&lt;br /&gt;
&lt;br /&gt;
Aktiviert wird die Unterstützung der Batteriesteuerung durch das Setzen des Attributes &#039;&#039;&#039;ctrlBatSocManagementXX&#039;&#039;&#039; z.B. mit diesen Angaben:&lt;br /&gt;
&lt;br /&gt;
 ctrlBatSocManagementXX lowSoc=x upSoC=x maxSoC=x careCycle=x &lt;br /&gt;
&lt;br /&gt;
so zum Beispiel:&lt;br /&gt;
&lt;br /&gt;
 ctrlBatSocManagement01 lowSoc=10 upSoC=50 maxSoC=98 careCycle=20&lt;br /&gt;
&lt;br /&gt;
Dabei steht &#039;XX&#039; für die Nummer der Batterie, welche mit dem Attribut setupBatteryDevXX korrespondiert. &lt;br /&gt;
Ist dieses Attribut gesetzt, werden Steuerungs-Readings erstellt, die der Nutzer auswerten und seine Batterieanlage z.B. via notify oder DOIF steuern kann. &amp;lt;bR&amp;gt;&lt;br /&gt;
Das Reading &#039;&#039;&#039;Battery_OptimumTargetSoC_XX&#039;&#039;&#039; enthält den vom Modul berechneten optimalen Mindest-SoC.&lt;br /&gt;
Das Reading &#039;&#039;&#039;Battery_ChargeRequest_XX&#039;&#039;&#039; wird auf &#039;1&#039; gesetzt, wenn der aktuelle SOC unter den optimalen SOC bzw. minimalen SOC gefallen ist.&lt;br /&gt;
&lt;br /&gt;
Die Schlüssel &#039;&#039;&#039;lowSoc&#039;&#039;&#039; und &#039;&#039;&#039;upSoC&#039;&#039;&#039; sind die unteren und oberen Grenzen, zwischen denen sich der Minimum SoC im normalen Betrieb bewegen soll.&lt;br /&gt;
Der Schlüssel &#039;&#039;&#039;maxSoC&#039;&#039;&#039; stellt eine Ladungsgrenze dar, die mindestens alle X (careCycle) Tage erreicht werden soll. Zu diesem Zweck errechnet das Modul die Anzahl der nötigen Erhöhungen der Minimum Batterieladung von 5% pro Tag um ausgehend vom aktuellen SoC-Level. Der resultierende Minimum SoC Wert wird entsprechend angepasst. Wurde der einstellte maxSoC Level erreicht, startet der Wartungszyklus (careCycle) erneut. &amp;lt;br&amp;gt;&lt;br /&gt;
Der maxSoC in Verbindung mit dem durch &#039;&#039;&#039;careCycle&#039;&#039;&#039; definierten Zyklus soll sicherstellen, dass der Batterieverbund in regelmäßigen Abständen einen Ladungsausgleich vollziehen kann. &lt;br /&gt;
&lt;br /&gt;
Durch die Modullogik erfolgt eine 5 protentige Anhebung des Minimum SoC wenn am Vortag maxSoC nicht erreicht wurde. Die Anhebung erfolgt aber nicht über upSoC. upSoC ist die obere Grenze des Minimum SoC. Wird am Vortag mindestens die maxSoC Ladung erreicht, verringert sich der Minimum SoC wieder schrittweise um 5%, aber nicht tiefer als lowSoc.&lt;br /&gt;
&lt;br /&gt;
Ergänzt wird die Logik noch um die PV Vorhersage. Dabei wird die obige Minimum SoC Berechnung um die Erwartung der PV Erwartung am aktuellen und folgenden Tag korrigiert. Das bedeutet, dass der Minimum SoC z.B. von heute 50% auf 10% abgesenkt und dadurch die gespeicherte Energie dem Haushalt zugeführt wird, wenn am heutigen oder kommenden Tag eine entsprechende PV Energie zu erwarten ist um die Batterie wieder vollständig zu zuladen.&lt;br /&gt;
Dadurch ist immer genügend &amp;quot;Freiplatz&amp;quot; in der Batterie und andererseits hat die Batterie in langen Dunkelphasen eine Reserve für einen eventuellen Stromausfall (falls der abgesichert werden soll) und wird vor einem dauerhaften tiefen SoC geschützt.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
===== Umsetzungsbeispiel =====&lt;br /&gt;
&lt;br /&gt;
Wie das Reading &#039;&#039;&#039;Battery_OptimumTargetSoC_XX&#039;&#039;&#039; genutzt werden kann, wird am Beispiel einer Victron Batterieanlage gezeigt. Die Anlage wird durch Cerbo GX Gerät gesteuert. Der Cerbo GX ist per MQTT2 in FHEM eingebunden. Umgesetzt ist die Steuerung der Batterie &#039;01&#039;, d.h. die Angabe &#039;XX&#039; ist durch &#039;01&#039; ersetzt.&lt;br /&gt;
&lt;br /&gt;
Die folgende kleine Logik ist in einem &#039;&#039;&#039;99_mySolarForecastUtils.pm&#039;&#039;&#039; File eingebaut und wird regelmäßig mit&lt;br /&gt;
&lt;br /&gt;
 batSocChargeMgmnt (&#039;&amp;lt;Name SolarForecast Device&amp;gt;&#039;);&lt;br /&gt;
&lt;br /&gt;
aufgerufen. Der Aufruf erfolgt durch Angabe der Routine im Attribut &#039;&#039;&#039;ctrlUserExitFn&#039;&#039;&#039; des SolarForecast Devices:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
{  &lt;br /&gt;
  # Hinweis: die Zeichen &#039;::&#039; vor der Routine sind durch das SolarForecast Package bedingt&lt;br /&gt;
  ::batSocChargeMgmnt ($name);&lt;br /&gt;
  return;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Routine wird dadurch automatisch am Ende jedes Zyklus (siehe Attribut plantControl-&amp;gt;cycleInterval) ausgeführt.&lt;br /&gt;
Um die SOC Managementfunktion nach Bedarf ein- bzw. ausschalten zu können wird zunächst im SolarForecast Device ein userattr zur Steuerung der SOC-Logik angelegt:&lt;br /&gt;
&lt;br /&gt;
 attr &amp;lt;Device&amp;gt; userattr userFn_BatterySoCManagement:ein,aus&lt;br /&gt;
&lt;br /&gt;
Die Perl Routine &#039;&#039;&#039;batSocChargeMgmnt&#039;&#039;&#039; ist wie folgt aufgebaut:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
###############################################################################&lt;br /&gt;
#&lt;br /&gt;
# Save this file as 99_mySolarForecastUtils.pm, and create your own functions&lt;br /&gt;
# in the new file. They are then available in every Perl expression.&lt;br /&gt;
#&lt;br /&gt;
###############################################################################&lt;br /&gt;
 package main;&lt;br /&gt;
 use strict;&lt;br /&gt;
 use warnings;&lt;br /&gt;
&lt;br /&gt;
 sub&lt;br /&gt;
 mySolarForecastUtils_Initialize($$)&lt;br /&gt;
 {&lt;br /&gt;
   my ($hash) = @_;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
# ab hier eigenen Code eintragen&lt;br /&gt;
###############################################################################&lt;br /&gt;
&lt;br /&gt;
############################################################################&lt;br /&gt;
#      Batterie SOC und Max. Ladestrom Management&lt;br /&gt;
############################################################################&lt;br /&gt;
sub batSocChargeMgmnt {&lt;br /&gt;
  my $name = shift;&lt;br /&gt;
  my $bn   = &#039;01&#039;;                                                                      # Batterienummer (evtl. im Aufruf mitgeben)&lt;br /&gt;
  my $hash = $defs{$name};&lt;br /&gt;
  &lt;br /&gt;
  my $vebus   = &#039;MQTT2_cerboGX_c0619ab34e08_vebus&#039;;                                     # Victron Vebus Device&lt;br /&gt;
  my $vicsets = &#039;MQTT2_cerboGX_c0619ab34e08_settings&#039;;                                  # Victron Einstellungen&lt;br /&gt;
&lt;br /&gt;
  my $maxcspc = 105;                                                                    # max. Ladestrom (A) für Victron MPII Verbund&lt;br /&gt;
  my $preduce = FHEM::SolarForecast::BatteryVal ($name, $bn, &#039;bpinreduced&#039;, $maxcspc);  # reduzierte Ladeleistung aus Bat-Konfig (W)&lt;br /&gt;
  my $actmcc  = ReadingsNum ($vicsets, &#039;MaxChargeCurrent&#039;, undef);                      # akt. Ladestromeinstellung&lt;br /&gt;
  my $load    = $actmcc // $maxcspc;                                                    # Soll-Ladestrom (A)  &lt;br /&gt;
&lt;br /&gt;
  ## Battery SoC Management&lt;br /&gt;
  ###########################&lt;br /&gt;
  my $ubsm = AttrVal ($name, &#039;userFn_BatterySoCManagement&#039;, &#039;aus&#039;); &lt;br /&gt;
  &lt;br /&gt;
  if ($ubsm eq &#039;ein&#039;) { &lt;br /&gt;
    my $bcrq = ReadingsNum ($name,    &#039;Battery_ChargeRequest_&#039;.$bn,     0);&lt;br /&gt;
    my $csoc = ReadingsNum ($vicsets, &#039;MinimumSocLimit&#039;,               10);             # akt. SoC&lt;br /&gt;
    my $osoc = ReadingsNum ($name,    &#039;Battery_OptimumTargetSoC_&#039;.$bn, 10);             # Soll-SoC&lt;br /&gt;
	my $surp = ReadingsNum ($name,    &#039;Current_Surplus&#039;,                0);             # aktueller PV-Überschuß&lt;br /&gt;
	&lt;br /&gt;
    if ($csoc != $osoc) {&lt;br /&gt;
      CommandSet (undef, &amp;quot;$vicsets MinimumSocLimit $osoc&amp;quot;);&lt;br /&gt;
      Log3 ($name, 3, qq{$name - userFn SoCMgmnt -&amp;gt; MinimumSocLimit in $vicsets set to $osoc %});&lt;br /&gt;
    }&lt;br /&gt;
	                                           &lt;br /&gt;
    if ($bcrq &amp;amp;&amp;amp; $surp &amp;lt; 1000) {                                                       # max. Ladestrom (A) b. Battery_ChargeRequest&lt;br /&gt;
      $load = $preduce / 48;                                                           # bei 48V Batteriesystem&lt;br /&gt;
    }&lt;br /&gt;
	else {&lt;br /&gt;
	  $load = $maxcspc;&lt;br /&gt;
	}&lt;br /&gt;
  }&lt;br /&gt;
  &lt;br /&gt;
  ## Einstellung MPII Verbund MaxChargeCurrent&lt;br /&gt;
  ##############################################&lt;br /&gt;
  if ($actmcc &amp;amp;&amp;amp; $load != $actmcc) {&lt;br /&gt;
    CommandSet (undef, &amp;quot;$vicsets MaxChargeCurrent $load&amp;quot;);&lt;br /&gt;
	&lt;br /&gt;
    Log3 ($name, 3, qq{$name - userFn ChargeMgmnt -&amp;gt; MaxChargeCurrent in $vicsets set }.&lt;br /&gt;
                    qq{from old $actmcc A to $load A});&lt;br /&gt;
  }&lt;br /&gt;
  &lt;br /&gt;
  readingsSingleUpdate ($hash, &#039;userFn_MPII_MaxChargeCurrent_set&#039;, $load, 0);&lt;br /&gt;
  &lt;br /&gt;
return;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
###############################################################################&lt;br /&gt;
# Ende eigener Code&lt;br /&gt;
&lt;br /&gt;
1;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ist im SolarForecast Device das Reading  &#039;&#039;&#039;Battery_ChargeRequest_01&#039;&#039;&#039; gesetzt, ist der aktuelle SOC kleiner als der optimale SOC und die Batterie wird (eventuell aus dem öffentlichen Netz) geladen. Das kann automatisch durch den Cerbo GX bzw. in anderen Anlagen durch einen entsprechenden Befehl erfolgen.&lt;br /&gt;
Damit die Netzbeladung nicht mit der vollen Ladestromstärke geschieht, wird der Ladestrom auf den gewünschten Wert (hier 21 A -&amp;gt; ca. 1000 W bei einem 48V System) reduziert sofern kein oder zu wenig PV-Überschuß vorhanden ist.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== weitere mögliche Aspekte der SOC- und Batterieladesteuerung ====&lt;br /&gt;
&lt;br /&gt;
SolarForecast liefert durch seine internen Logiken bereits sehr hilfreiche Signale zur Steuerung von Batteriesystemen in Form von Readings an. Abhängig von den individuellen Möglichkeiten der vorhandenen Anlage käme zum Beispiel zusätzlich zu einer Steurung des optimalen SOC oder der Zeitfenster für die Batterieaufladung auch eine Steuerung des Ladestroms bzw. der verwendeten Ladeleistung in Betracht.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Hintergründe:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* Das Laden eines Akkus mit möglichst gleichbleibender und geringer Leistung wirkt sich positiv auf dessen Langlebigkeit aus, was u.a. auch daran liegt, dass der Temperaturstreß im Akku bei langsamer Energiezuführung nur gering ist.&lt;br /&gt;
&lt;br /&gt;
* Ein geringer Ladestrom führt zu einem geringen Absacken der Akkuspannung beim (ggf, temporären) Abschalten des Ladestroms, was sich positiv auf die SOC-Schätzung auswirkt.&lt;br /&gt;
    &lt;br /&gt;
* Ein geringer Ladestrom führt, insb. bei Nutzung passiver Balancer (sind in den Akku-Systemen üblicherweise verbaut), zu einer geringen Differenz zwischen den einzelnen Zellspannungen und damit zu einer besseren Kapazitätsausnutzung eins Akkus.&lt;br /&gt;
&lt;br /&gt;
* Ein geringer Ladestrom kann zu einem zeitlich punktgenauen Zielfüllstand (im Winter 100%, ansonsten natürlich weniger) am Ende des Tages führen.&lt;br /&gt;
    &lt;br /&gt;
* Eine geringer Ladestrom führt auch zu geringen Leitungsverlusten, da der Strom quadratisch zu den Leitungsverlusten beiträgt.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Weiterhin kann auch die Verweildauer oder die Begrenzung des SOC, auf den die Batterie maximal geladen werden soll, Gegenstand von Optimierungen sein.&lt;br /&gt;
&lt;br /&gt;
Die häufig zu findende Empfehlung, Akkus nicht zu lange bei einem SOC von 100% verweilen zu lassen, liegt weniger an dem SOC als solchem, sondern daran, dass &#039;&#039;SOC=100%&#039;&#039; üblicherweise dadurch ermittelt wird, dass eine Zelle im Akku einen Spannungsschwellenwert überschreitet, der – würde dieser Schwellenwert zeitlich länger überschritten sein – zu einer Schädigung des Akkus führen könnte. Schädlich sind insbesondere häufige Nachladevorgänge bei Werten um die 100% SOC, da Zellen dann ja häufiger und damit - integral gesehen - auch länger den o.g. Schwellenwert überschreiten.&lt;br /&gt;
&lt;br /&gt;
Einige Akku-Hersteller, z.B. BYD, haben auf o.g, Grund in der (neueren) Firmware Ihrer Systeme einen SOC-Threshold (bei BYD liegt dieser bei 95%) eingebaut. Dieser Wert muss nach einem erreichten SOC von 100% unterschritten  werden, bevor überhaupt eine weitere Ladung erfolgt. Insbesondere in der dunklen Jahreszeit kann es daher sinnvoll sein, den Akku unter Verwendung von PV-Leistung so zu füllen, dass dieser möglichst nur einmal vor Einbruch der Dunkelheit auf &#039;&#039;SOC=100%&#039;&#039; geladen wird. Hierbei sollte darauf geachtet werden, dass der Ladeschlussstrom des jeweiligen Systems nicht unterschritten wird. &lt;br /&gt;
&lt;br /&gt;
SolarForecast unterstützt die oben betrachtete Batteriesteuerung durch die optionale Angabe von &#039;&#039;&#039;loadAbort&#039;&#039;&#039; im Attribut &#039;&#039;&#039;ctrlBatSocManagementXX&#039;&#039;&#039;. Das in diesem Kontext erstellte Reading &#039;&#039;&#039;Battery_ChargeAbort_XX&#039;&#039;&#039; stellt die auswertbaren Steuerungsinformationen zur Verfügung.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== PV-Prognose und Verbrauch optimierte Beladungssteuerung unter Berücksichtigung einer Wirkleistungsbegrenzung ====&lt;br /&gt;
&lt;br /&gt;
[[Datei:Screenshot 2025-04-25 143910.png|right|thumb|400px|Batterie Popup &amp;quot;nur laden wenn Einspeiselimit überschritten&amp;quot;]]&lt;br /&gt;
&lt;br /&gt;
Die in diesem Abschnitt beschriebene Ladestrategie &#039;&#039;&#039;loadRelease&#039;&#039;&#039; wird im Modul als &#039;&#039;&#039;Ladefreigabe&#039;&#039;&#039; bezeichnet.&lt;br /&gt;
&lt;br /&gt;
Üblicherweise wird die Batterie geladen. sobald PV-Überschuß vorhanden ist ohne eine Prognose zu berücksichtigen. Dadurch sind die Batterien im Sommer unter Umständen schon um 10-12 Uhr voll geladen und die Anlage liefert ihre mehr oder weniger volle Energie (je nach Ausrichtung) in das öffentliche Netz. Dann könnte es zu einer Begrenzung der PV-Leistung durch einen Schwellenwert (z.B. 70%-Regelung) oder eventuell Zwangsabregelung durch den Netzbetreiber kommen.&lt;br /&gt;
&lt;br /&gt;
Hintergrund dieser Erweiterung ist das Bestreben, eine optimale Unterstützung bei der Maximierung des Eigenverbrauchs zu geben und einen Beitrag zur Netzstabilität zu leisten.&lt;br /&gt;
Das Ziel der Beladungssteuerung der Batterie ist es, die Ladung der Batterie über den Tag verteilt so zu steuern, dass die Batterie erst dann geladen wird, wenn durch den PV-Überschuß eine Netzeinspeisung erfolgen würde bzw. ein Grenzwert erreicht werden würde, der zu einer Abregelung der PV-Anlage führen könnte.&lt;br /&gt;
Es kann auch zu einer Zwangsabregelung durch den Netzbetreiber führen, was vermieden werden soll.&lt;br /&gt;
 &lt;br /&gt;
Die interne Kalkulationslogik erstellt zu diesem Zweck ein Reading &#039;&#039;&#039;Battery_ChargeUnrestricted_XX&#039;&#039;&#039;.&lt;br /&gt;
Dieses Reading gibt dem User ein Signal, ob zu der aktuellen Stunde der Ladevorgang der Batterie freigegeben (1) oder nicht freigegeben werden sollte (0). Die Freigabe zur Ladung bedeutet dass:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Battery_ChargeUnrestricted_XX=1&#039;&#039;&#039; die Batterie mit ihrer vollen, oder der im Reading [[SolarForecast_-_Solare_Prognose_(PV_Erzeugung)_und_Verbrauchersteuerung#leistungsoptimierte_Beladungssteuerung_mit_dem_Fokus_geringe_Verlustleistung_und_Verschleiß|Battery_ChargeOptTargetPower_XX]] angegebenen Ladeleistung angesteuert werden sollte um wie prognostiziert am Ende des Sonnentages die volle bzw. maximal mögliche Ladung zu erhalten..&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Battery_ChargeUnrestricted_XX=0&#039;&#039;&#039; die Batterie nur geladen werden sollte wenn der im Attribut plantControl-&amp;gt;feedinPowerLimit definierte Wert überschritten wird&lt;br /&gt;
&lt;br /&gt;
 &lt;br /&gt;
Ist keine Freigabe zur Ladung vorhanden, kann bzw. sollte dennoch eine Ladung der Batterie vorgenommen werden, sofern eine über dem im &amp;lt;br&amp;gt;&lt;br /&gt;
Attribut &#039;&#039;&#039;plantControl-&amp;gt;feedinPowerLimit&#039;&#039;&#039; definierten Limit liegende Einspeiseleistung vorhanden ist. In diesem Status sollte die Batterie nur mit der Differenzleistung von vorhandener Einspeiseleistung und dem gesetzen Einspeiselimit geladen werden und damit das gesetzte Einspeiselimit einhalten ohne die Anlage abzuregeln. Ist die Batterie in einer Balkengrafikebene dargestellt, wird im Mouse-Over Popup die Battrie mit &#039;&#039;&amp;quot;nur laden wenn Einspeiselimit überschritten&amp;quot;&#039;&#039; gekennzeichnet.&lt;br /&gt;
&lt;br /&gt;
Die Möglichkeiten der Batteriesteuerung sind stark von der individuellen Anlage abhängig und können hier nicht beschrieben werden. Ein Umsetzungsbeispiel mit einer Victron-Anlage mit Pylontech Batterie ist weiter unten dargestellt.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
===== Aktivierung und Arbeitsweise =====&lt;br /&gt;
&lt;br /&gt;
Die implementierte Logik zur optimierten Batterie Beladungssteuerung wird aktiviert, wenn folgende Kriterien erfüllt sind: &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* der Schlüssel &#039;&#039;&#039;capacity&#039;&#039;&#039; in den setupInverterDevXX Atributen ist gesetzt&lt;br /&gt;
* das Attribut &#039;&#039;&#039;ctrlBatSocManagementXX&#039;&#039;&#039; für die jeweilige Batterie ist gesetzt&lt;br /&gt;
* der Schlüssel &#039;&#039;&#039;cap&#039;&#039;&#039; im Attribut setupBatteryDevXX der jeweiligen Batterie ist gesetzt&lt;br /&gt;
 &lt;br /&gt;
&lt;br /&gt;
Die Logik arbeitet wie folgt:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
;Der resultierende Schwellenwert der Wirkleistungsbegrenzung: der Gesamtanlage wird durch die Auswertung der in den Inverter-Attributen setupInverterDevXX  gesetzen Schlüssel limit (optional) und capacity ermittelt. Inverter mit gesetztem Schlüssel feed = grid (deren Energie wird ausschließlich in das öffentlich Netz eingespeist) werden von der Betrachtung in diesem Kontext ausgeschlossen.&lt;br /&gt;
&lt;br /&gt;
;Die benötigte Beladung bis zu maxSoC (im Attribut setupBatteryDevXX) : wird ermittelt und mit der zu erwartenden PV Tagesprognose in Beziehung gesetzt. Sofern die PV-Erzeugung abzgl. Verbrauch noch nicht das Wirkleistungbegrenzungs-Limit erreicht UND noch genügend PV-Überschuß für eine Ladung auf &#039;&#039;maxSoC&#039;&#039; im Laufe der kommenden Stunden des Tages zu erwarten ist, bleibt das Reading &#039;&#039;&#039;Battery_ChargeUnrestricted_XX = 0&#039;&#039;&#039;. Die erreichte Qualität der Steuerungslogik ist stark von der Prognosequaliät abhängig. Um die Steuerung resilienter zu gestalten, wird per default ein Sicherheitsaufschlag von 50% auf die prognostizierte benötigte Ladeenenergie auf Stundenbasis einkalkuliert. Dieser Wert kann mit dem Attribut &#039;&#039;&#039;ctrlBatSocManagementXX-&amp;gt;safetyMargin&#039;&#039;&#039; individuell angepasst werden.&lt;br /&gt;
&lt;br /&gt;
;Wird das Wirkleistungbegrenzungs-Limit erreicht/überschritten und/oder die PV-Überschußprognose: zzgl. eines Sicherheitspuffers unterschritten, wird &#039;&#039;&#039;Battery_ChargeUnrestricted_XX = 1&#039;&#039;&#039; gesetzt. Der User kann darauf reagieren und den Batterie Ladevorgang aktivieren. Die Möglichkeiten der Aktivierung sind natürlich von der installierten Anlage abhängig. Das SolarForecast Device greift nicht in die Batteriesteuerung direkt ein.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Anders ausgedrückt erfolgt immer dann eine Ladefreigabe, wenn einer der nachfolgenden Sachverhalte &#039;&#039;wahr&#039;&#039; ist:&lt;br /&gt;
&lt;br /&gt;
- die benötigte Ladeenergie (aller installierten Batterien) &amp;gt;= PV-Restüberschuß des Tages (Reading RestOfDayPVforecast) zzgl. Sicherheitsaufschlag&lt;br /&gt;
&lt;br /&gt;
ODER&lt;br /&gt;
&lt;br /&gt;
- der Zeitpunkt des PV-Erzeugungszenit des laufenden Tages überschritten ist (Reading Today_MaxPVforecastTime)&lt;br /&gt;
&lt;br /&gt;
ODER&lt;br /&gt;
&lt;br /&gt;
- wenn die aktuelle PV Leistung &amp;gt;= einer eventuellen Wechselrichter-Leistungsbegrenzung ist (Summe aller WR-Begrenzungen)&lt;br /&gt;
&lt;br /&gt;
ODER&lt;br /&gt;
&lt;br /&gt;
- wenn das BatSoc-Management _nicht_ aktiviert ist (nicht alle oben genannten Aktivierungskriterien erfüllt sind)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Sind mehrere Batterien installiert und durch das Management einer Batterie die Einhaltung der genannten Kriterien bzw. Sachverhalte erwirkt wird, bleiben die weiteren Batterien im Modus Ladefreigabe (Battery_ChargeUnrestricted_XX=1).  &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Umsetzungsbeispiel Batterie Ladesteuerung mit Victron GX Venus über den Grid Setpoint =====&lt;br /&gt;
&lt;br /&gt;
Ziel der Steuerung ist es abhängig vom Wert des Readings Battery_ChargeUnrestricted_XX (z.B. Battery_ChargeUnrestricted_01) die Ladung des angeschlossenen Batteriesystems zu aktivieren bzw. zu deaktivieren.&lt;br /&gt;
Das vorliegende Beispiel soll umzusetzen:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* Battery_ChargeUnrestricted_01 = 0 -&amp;gt; Batterie nicht aufladen bzw. nur laden wenn Einspeiselimit überschritten (Attribut plantControl-&amp;gt;feedinPowerLimit)&lt;br /&gt;
* Battery_ChargeUnrestricted_01 = 1 -&amp;gt; Batterie aufladen&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Der Grid Setpoint ist ein Parameter in den Victron Systemeinstellungen. Das Victron System wird den eingestellten Grid Setpoint versuchen einzuhalten und bei einem positiven Wert Energie aus dem Netz beziehen bzw. bei einem negativen Grid Setpoint Energie in das Netz einspeisen.&lt;br /&gt;
&lt;br /&gt;
Normalerweise wird das Victron System die erzeugte PV Energie zunächst zur Deckung des Hausverbrauchs verwenden, ist dann noch ein PV Überschuß vorhanden, die Batterien aufladen und bei weiter vohandenen PV Überschuß diese Energie in das öffentliche Netz einspeisen sofern dem System eine Einspeisung erlaubt ist. Anderenfalls werden die Wechselrichter oder MPPT Smartloader heruntergeregelt bzw. limitiert. &lt;br /&gt;
&lt;br /&gt;
In der Standardeinstellung ist beim Victron System ein Grid Setpoint von ca. 20 bis 50 Watt, d.h. das System strebt danach diese Vorgabe zu erfüllen zieht ständig Energie in diesem Bereich aus dem öffentlich Netz von einem gelegentlichen Überschwingen der Regelung abgesehen. Damit ein eventueller PV Überschuß nicht zum Laden der Batterie verwendet, sondern statt dessen in das öffentliche Netz eingespeist wird, muß der Grid Setpoint um diesen Betrag abgesenkt werden.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Screenshot 2025-02-24 114403.png|right|thumb|400px|Integration des Grid Setpoint-Managements in den Own_Spec-Bereich]]&lt;br /&gt;
&lt;br /&gt;
Damit man später per Attribut bzw. über den [[#Formatierung_der_Inhalte_im_Bereich_&amp;quot;Graphic_Header_Own_Specification&amp;quot;|Own_Spec-Bereich]] die automatische Ladesteuerung über den Grid Setpoint ein- bzw. ausschalten kann, legen wir ein userattr an:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
 userattr &amp;lt;Name&amp;gt; userFn_GridSetpointManagement:ein,aus&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Weiterhin sind einige Vorbereitungen im Device MQTT2_cerboGX_c0619ab34e08_settings vorzunehmen. Dieses Device ist im vorliegenden Beispiel ein in FHEM vorhandenes MQTT2_DEVICE, über das die Victron CerboGX Einstellungen zugreifbar sowie einstellbar sind. Die Vorgehensweise zur Erstellung des MQTT2_DEVICE ist hier nicht beschrieben und wird vorausgesetzt.&lt;br /&gt;
&lt;br /&gt;
Damit ein entsprechender Set-Befehl zur Einstellung des Grid Setpoints vorhanden ist, werden folgende Attribute gesetzt:&lt;br /&gt;
&lt;br /&gt;
 attr MQTT2_cerboGX_c0619ab34e08_settings jsonMap Settings_CGwacs_AcPowerSetPoint_value:GridSetpoint&lt;br /&gt;
 attr MQTT2_cerboGX_c0619ab34e08_settings setList GridSetpoint W/c0619ab34e08/settings/0/Settings/CGwacs/AcPowerSetPoint {&amp;quot;value&amp;quot;:$EVTPART1}&lt;br /&gt;
&lt;br /&gt;
Nach dieser Attributierung kann mit einem &amp;quot;&#039;&#039;set MQTT2_cerboGX_c0619ab34e08_settings GridSetpoint &amp;lt;Wert&amp;gt;&#039;&#039;&amp;quot; der Grid Setpoint eingestellt werden.&lt;br /&gt;
&lt;br /&gt;
Das Attribut userFn_GridSetpointManagement kann nun im Perl Code ausgewertet werden um das Grid Setpoint-Management ein- bzw. auszuschalten.&lt;br /&gt;
Der nachfolgende Code wird in die Datei &#039;&#039;&#039;99_mySolarForecastUtils.pm&#039;&#039;&#039; eingefügt. Ist diese Datei nicht vorhanden, legen wir sie vorab als Kopie der ausgelieferten 99_myUtils.pm an.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;mw-collapsible mw-collapsed&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Code Management Batterieladung über Victron Grid Setpoint -&amp;gt;&#039;&#039;&#039; &amp;lt;div class=&amp;quot;mw-collapsible-content&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
###############################################################################&lt;br /&gt;
#&lt;br /&gt;
# Save this file as 99_mySolarForecastUtils.pm, and create your own functions&lt;br /&gt;
# in the new file. They are then available in every Perl expression.&lt;br /&gt;
#&lt;br /&gt;
###############################################################################&lt;br /&gt;
 package main;&lt;br /&gt;
 use strict;&lt;br /&gt;
 use warnings;&lt;br /&gt;
&lt;br /&gt;
 sub&lt;br /&gt;
 mySolarForecastUtils_Initialize($$)&lt;br /&gt;
 {&lt;br /&gt;
   my ($hash) = @_;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
# ab hier eigenen Code eintragen&lt;br /&gt;
###############################################################################&lt;br /&gt;
&lt;br /&gt;
############################################################################&lt;br /&gt;
#   Steuerung Start/Stop der Batterieladung über den Victron Grid Setpoint&lt;br /&gt;
############################################################################&lt;br /&gt;
sub gridSetpointMgmt {&lt;br /&gt;
  my $name = shift;&lt;br /&gt;
  my $hash = $defs{$name};&lt;br /&gt;
  &lt;br /&gt;
  my $vicsets   = &#039;MQTT2_cerboGX_c0619ab34e08_settings&#039;;                               # Device Victron Einstellungen&lt;br /&gt;
  my $gspdef    = 20;                                                                  # Default Einstellung des Grid Setpoint&lt;br /&gt;
  my $targetgsp = $gspdef;                                                             # Voreinstellung Grid Setpoint&lt;br /&gt;
  my $neggspmax = FHEM::SolarForecast::CurrentVal ($name, &#039;feedinPowerLimit&#039;, 5000);   # max. gewünschte Einspeiseleistung in Watt&lt;br /&gt;
  $neggspmax    = $neggspmax * -1;&lt;br /&gt;
  my $gspm      = AttrVal ($name, &#039;userFn_GridSetpointManagement&#039;, &#039;aus&#039;);             # Freischaltung des Grid Setpoint-Managements&lt;br /&gt;
  my $curgsp    = ReadingsNum ($vicsets, &#039;GridSetpoint&#039;,         $gspdef);             # aktuelle Einstellung Grid Setpoint&lt;br /&gt;
  &lt;br /&gt;
  if ($gspm eq &#039;ein&#039;) { &lt;br /&gt;
    my $bcrq = ReadingsNum ($name, &#039;Battery_ChargeUnrestricted_01&#039;, 0);         # Ladefreigabe (1 -&amp;gt; Bat Ladefreigabe)&lt;br /&gt;
    my $surp = ReadingsNum ($name, &#039;Current_Surplus&#039;,               0);         # aktueller PV-Überschuß&lt;br /&gt;
	&lt;br /&gt;
    if (!$bcrq) {                                                               # Grid Setpoint absenken wenn keine .. &lt;br /&gt;
        $targetgsp = $gspdef - $surp;                                           # ..Batterie Ladefreigabe&lt;br /&gt;
        $targetgsp = $neggspmax if($targetgsp &amp;lt; $neggspmax);                    # Begrenzung der Einspeiseleistung&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
  &lt;br /&gt;
  if ($curgsp != $targetgsp) {&lt;br /&gt;
	  fhem (&amp;quot;set $vicsets GridSetpoint $targetgsp&amp;quot;);&lt;br /&gt;
      Log3 ($name, 3, qq{$name - userFn gridSetpointMgmt -&amp;gt; GridSetpoint in $vicsets set to $targetgsp});&lt;br /&gt;
  }&lt;br /&gt;
  &lt;br /&gt;
  readingsSingleUpdate ($hash, &#039;userFn_Victron_GridSetpoint_set&#039;, $targetgsp, 0);&lt;br /&gt;
  &lt;br /&gt;
return;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
###############################################################################&lt;br /&gt;
# Ende eigener Code&lt;br /&gt;
&lt;br /&gt;
1;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Der Vergleich des Setpoints mit der Vorgabe von &#039;&#039;$neggspmax&#039;&#039; dient dazu die maximale Einspeiseleistung in das öffentliche Netz zu begrenzen. Ist diese Grenze erreicht, wird der noch verbleibende Überschuß in die Batterie geladen. Dadurch wird eine Abregelung der Anlage vermieden wenn der Schwellenwert &#039;&#039;$neggspmax&#039;&#039;erreicht ist sofern die Batterie aufnahmefähig ist und nicht voll geladen ist. &lt;br /&gt;
&lt;br /&gt;
Damit die Routine mit jedem Zyklus im Modul ausgeführt wird, legen wir sie im Attribut &#039;&#039;&#039;ctrlUserExitFn&#039;&#039;&#039; an:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
attr &amp;lt;Name&amp;gt; ctrlUserExitFn  {  &lt;br /&gt;
                               ::gridSetpointMgmt  ($name);&lt;br /&gt;
                            }&lt;br /&gt;
&amp;lt;/pre&amp;gt; &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Zum Management des automatischen Grid Setpoint und der Anzeige des eingestellten Wertes wird das Attribut und die Readinganzeige in den Own_Spec-Bereich eingebunden, z.B.:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 attr &amp;lt;Name&amp;gt; graphicHeaderOwnspec  #Settings&lt;br /&gt;
                                   SetPoint&amp;amp;nbsp;Management:userFn_GridSetpointManagement&lt;br /&gt;
                                   Grid&amp;amp;nbsp;Setpoint:GridSetpoint@MQTT2_cerboGX_c0619ab34e08_settings&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== leistungsoptimierte Beladungssteuerung mit dem Fokus geringe Verlustleistung und Verschleiß ====&lt;br /&gt;
&lt;br /&gt;
Über das Attribut ctrlBatSocManagementXX kann mit dem Schlüssel loadStrategy die Ladestrategie &#039;&#039;&#039;optPower&#039;&#039;&#039; oder &#039;&#039;&#039;smartPower&#039;&#039;&#039; eingestellt werden.&lt;br /&gt;
Das SF-Modul bietet ein weiteres Reading &#039;&#039;&#039;Battery_ChargeOptTargetPower_XX&#039;&#039;&#039; zur Batteriesteuerung an. Dieses Reading liefert eine kalkulierte optimale Ladeleistung in Watt.&lt;br /&gt;
&lt;br /&gt;
Die in diesem Abschnitt beschriebene Ladestrategie &#039;&#039;&#039;optPower&#039;&#039;&#039; wird im Modul als &#039;&#039;&#039;optimierte Ladeleistung&#039;&#039;&#039;, die Ladestrategie &#039;&#039;&#039;smartPower&#039;&#039;&#039; als &#039;&#039;&#039;zieloptimierte Ladeleistung&#039;&#039;&#039; bezeichnet.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Beide Strategien kalkulieren die Ladeleistung kontinuierlich neu und berücksichtigen dabei:&lt;br /&gt;
&lt;br /&gt;
* die Prognose der PV-Erzeugung der nächsten Stunden und des (Rest-)Tages&lt;br /&gt;
* den prognostizierten Verbrauch auf Stunden- bzw. Tagesbasis&lt;br /&gt;
* eine eventuell mögliche Überschreitung des gesetzten Einspeiselimits (plantControl-&amp;gt;feedinPowerLimit)&lt;br /&gt;
* die gesetzten Leistungsparameter der Batterie &lt;br /&gt;
* das Ziel, am Tagesende einen möglichst maximalen SoC der Batterie erreicht zu haben bzw. den eingestellten Ziel-SoC &#039;&#039;&#039;loadTarget&#039;&#039;&#039;&lt;br /&gt;
* die Effizienz der Batterieanlage (Einstellung &#039;&#039;&#039;setupBatteryDevXX-&amp;gt;efficiency&#039;&#039;&#039;)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Der Fokus der implementierten Logik liegt dabei auf einer kontinuierlichen Ladeleistung, die aber unter den oben genannten Gesichtspunkten möglichst (relativ) gering sein soll um den Batterieverschleiß und die Verlustleistung zu minimieren. Die im Reading angezeigte Ladeleistung kann mit geeigneten Mitteln direkt in die Leistungssteuerung der Batterie eingebracht werden. Natürlich kann die Leistung auch vorab in einen optimalen Ladestrom umgerechnet werden.&lt;br /&gt;
&lt;br /&gt;
Da das Ergebnis der Kalkulation stark von den Prognosen abhängt, ist zur Erhöhung der Resilienz per Default ein Sicherheitszuschlag von 20% eingebaut. Dieser Wert kann mit dem Attribut &#039;&#039;&#039;ctrlBatSocManagementXX-&amp;gt;safetyMargin&#039;&#039;&#039; individuell angepasst werden. &lt;br /&gt;
&lt;br /&gt;
Ist kein PV-Überschuß (mehr) vorhanden / prognostiziert oder das Ladeziel erreicht, erfolgt ein Rückfall der Ladeleistung im Reading Battery_ChargeOptTargetPower_XX auf den  im Attribut &#039;&#039;&#039;setupBatteryDevXX-&amp;gt;pinmax&#039;&#039;&#039; angegebenen Wert. Ist dieser Wert nicht gesetzt, erfolgt der Rückfall auf &amp;quot;Unendlich&amp;quot; (9223372036854775807). &lt;br /&gt;
 &lt;br /&gt;
Im Reading Battery_ChargeOptTargetPower_XX wird der Wert des Parameters &#039;&#039;&#039;setupBatteryDevXX-&amp;gt;pinreduced&#039;&#039;&#039; gesetzt, wenn weniger Ladeleistung als pinreduced berechnet wurde (Einhaltung einer Mindestladeleistung) oder wenn der SoC der Batterie &amp;lt;= [[#Aktivierung_des_Batterie_SOC-_und_Lade-Managements|lowSoC]] beträgt.&lt;br /&gt;
Somit wird die Batterie bei einer Anforderungsladung bei Unterschreitung von lowSoC mit nur wenig Leistung aus dem Grid geladen falls es nötig sein sollte.&lt;br /&gt;
Es ist demzufolge ratsam den optionalen Parameter &#039;&#039;&#039;setupBatteryDevXX-&amp;gt;pinreduced&#039;&#039;&#039; zu setzen, da ansonsten ebenfalls ein Rückfall auf &#039;&#039;&#039;setupBatteryDevXX-&amp;gt;pinmax&#039;&#039;&#039; bzw. &amp;quot;Unendlich&amp;quot; erfolgt.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Beide Strategien, optPower und smartPower, setzen die zuvor beschriebene Arbeitsweise um.&lt;br /&gt;
Ergänzend wird bei der Ladestrategie &#039;&#039;&#039;smartPower&#039;&#039;&#039; die generelle Erreichbarkeit des Ladeziels bei der Festlegung der Ladeleistung berücksichtigt. Es führt dazu dass:&lt;br /&gt;
&lt;br /&gt;
* die Ladeleistung im Reading Battery_ChargeOptTargetPower_XX auf einen tendenziell höheren Wert als bei der optPower-Strategie gesetzt wird, wenn/solange das eingestellte Ladeziel als unerreichbar kalkuliert wird&lt;br /&gt;
* bei kalkulierter Erreichbarkeit des Ladeziels der Sicherheitsaufschlag linear absenkend proportional zum Kehrwert des verfügbaren PV-Überschusses auf die Ladeleistung angewendet wird&lt;br /&gt;
&lt;br /&gt;
Der nächste Abschnitt erläutert die smartPower Funktionen etwas eingehender.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
===== smartPower - die zieloptimierte Ladeleistung  =====&lt;br /&gt;
Die hinter smartPower liegende Anpassungslogik wird nachfolgend etwas näher beschrieben.&lt;br /&gt;
Ausgangspunkt ist das Ergbnis einer Ratio-Funktion aus dem (Rest)Tages-PV-Überschuß und benötigter Energie zur Erlangung des Batterie Ladeziels.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Lineare Interpolation zwischen pinmax und abgesicherter minpower&#039;&#039;&#039;  &amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Variablen&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;P&#039;&#039;&#039;: resultierende Leistung.&lt;br /&gt;
* &#039;&#039;&#039;pinmax&#039;&#039;&#039;: maximale Ladeleistung.&lt;br /&gt;
* &#039;&#039;&#039;limpower&#039;&#039;&#039;: Leistungslimit, d.h. Ladeleistungssollwert.&lt;br /&gt;
* &#039;&#039;&#039;r&#039;&#039;&#039;: Verhältnis in Prozent (r = spday·100 / whneed).&lt;br /&gt;
* &#039;&#039;&#039;otpMargin&#039;&#039;&#039;: Prozentwert der Bandbreite, über die die Absenkung linear verläuft (die Steilheit kann mit dem Attributwert ctrlBatSocManagementXX-&amp;gt;safetyMargin im Device eingestellt werden).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Die Kennlinie der für smartPower eingesetzten Funktion ist stückweise definiert über das Verhältnis Ratio &#039;&#039;𝑟 = spday x 100 / &amp;lt;benötigte Ladeenergie bis Ziel&amp;gt;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
* Für 𝑟 ≤ 100: &#039;&#039;𝑃 = pinmax&#039;&#039;, Interpretation: Es ist viel Bedarf vorhanden bzw. Überschuss ≤ 100 % des Bedarfs → maximal laden.&lt;br /&gt;
* Für 𝑟 ≥ 100 + otpMargin: &#039;&#039;𝑃 = limpower ⋅ (1 + otpMargin / 100 )&#039;&#039;&lt;br /&gt;
* Für 100 &amp;lt; 𝑟 &amp;lt; 100 + otpMargin: lineare Absenkung von &#039;&#039;&#039;P&#039;&#039;&#039; mit zunehmendem &#039;&#039;&#039;r&#039;&#039;&#039; für den Bereich 100 &amp;lt; 𝑟 &amp;lt; 100 + otpMargin. &#039;&#039;&#039;P&#039;&#039;&#039; wird bei 𝑟 = 100 gleich &#039;&#039;&#039;pinmax&#039;&#039;&#039; und bei 𝑟 = 100 + otpMargin gleich &#039;&#039;limpower ⋅ ( 1 + otpMargin/100 )&#039;&#039;, Interpretation: Sobald der Überschuss über den Bedarf hinausgeht (ratio &amp;gt; 100), wird das Ladeleistungslimit schrittweise von pinmax Richtung limpower reduziert; die Reduktion erfolgt gleichmäßig über die Margin-Spanne.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Lineare Interpolation und Steigung&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Die Differenz &#039;&#039;(pinmax − limpower)&#039;&#039; wird gleichmäßig auf die otpMargin-Prozentpunkte verteilt. Die Steigung der Geraden ist&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
slope = − (pinmax − limpower) / otpMargin&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
das heißt die Leistung sinkt um diesen Betrag pro Prozentpunkt von &#039;&#039;&#039;r&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Nach Umformung ist der Zusammenhang wie folgt darstellbar:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
𝑃 = pinmax − ( pinmax − limpower) ⋅ ((𝑟 − 100) / otpMargin)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
und ist äquivalent zu&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
𝑃 = pinmax + ( 𝑟 − 100 ) ⋅ slope&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Numerisches Beispiel&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;limpower&#039;&#039;&#039; = 1000 W, &#039;&#039;&#039;pinmax&#039;&#039;&#039; = 3000 W, &#039;&#039;&#039;otpMargin&#039;&#039;&#039; = 20&lt;br /&gt;
* Unterschied = 2000 W, Steigung = −2000/20 = −100 W/%&lt;br /&gt;
* Für &#039;&#039;&#039;r = 105&#039;&#039;&#039;: 𝑃 = 3000 − 2000 ⋅ ( 5 / 20 ) = 3000 − 500 = 2500 W&lt;br /&gt;
* Für &#039;&#039;&#039;r = 120&#039;&#039;&#039; ergibt die Formel den abgesicherten Endwert 1200 W&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Die Formel sorgt dafür, dass die Zielleistung stufenlos und vorhersehbar reduziert wird, wenn der relative Rest-Überschuss des Tages über 100% steigt, und erreicht bei Ende der otpMargin den geplanten abgesicherten Wert.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Ratio Kennlinie.png|right|thumb|200px|abfallend proportionale Anpassungsfunktion]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
In einem grafischen Diagramm stellt sich die abfallend proportionale Anpassungsfunktion der resultierenden Soll-Ladeleistung wie nebenstehend abgebildet dar. Dabei entspricht pow=100 dem jeweiligen pinmax Wert (hier 3000 W). So ist auch pow=0 mit dem abgesicherten Endwert (hier 1200 W) gleichzusetzen. Die Linie verläuft waagerecht bei pinmax solange das das Ratio  r ≤ 100 % und knickt an r == 100 mit steigenden r nach unten. Es verringert sich pow linear von pinmax zu limpower zzgl. otpMargin.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Durch diese Maßnahmen wird eine flexible Lademöglichkeit bis pinmax erreicht, die sich bei unvorhergesehenen Aufklarungen mit starker Sonneneintrahlung bei ansonsten sehr bedeckten Himmel positiv auswirkt. Andererseits wird die Erhöhung der Ladeleistung weiter begrenzt, wenn viel PV-Überschuß in Verbindung mit einer generellen Erreichbarkeit des Ladeziels zum Ende des Tages prognostiziert ist. Man könnte die Ladestrategie &#039;&#039;&#039;smartPower&#039;&#039;&#039; in ihrer Wirkung als einen Mix aus &#039;&#039;&#039;optPower&#039;&#039;&#039; und &#039;&#039;&#039;loadRelease&#039;&#039;&#039; ansehen.&lt;br /&gt;
&lt;br /&gt;
Zusammenfassend ist optPower vorwiegend für die Nutzergruppe geeignet, die eine tendenziell sparsamere Ladeleistung bevorzugen und dafür bereit sind eventuelle &amp;quot;Verluste&amp;quot; in Form von Einspeisung zu akzeptieren. &amp;lt;br&amp;gt;&lt;br /&gt;
Demgegenüber eignet sich das aggressivere smartPower für Anwender, die bei niedrigen PV-Erträgen bewusst das Maximum an Batterieladung anstreben und dafür in Kauf nehmen, dass Ladeleistungen weniger knapp kalkuliert sind. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Unterstützung des Modus &amp;quot;Eigennutzung&amp;quot; bei Hybrid-Wechselrichtern =====&lt;br /&gt;
Viele Hybrid-Wechselrichter verteilen die zur Verfügung stehende PV-Leistung nach folgendem Prioritäten, wenn der (übliche) Modus &amp;quot;Eigennutzung&amp;quot; aktiviert ist:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
# Nutzung der Leistung zur Deckung des Hausverbrauchs,&lt;br /&gt;
# Nutzung der Leistung zur Ladung des bzw. der angeschlossenen Speicher.&lt;br /&gt;
# Einspeisung der Leistung in das öffentliche Netz.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Da in aller Regel die Leistung, mit der ein angeschlossener Speicher geladen wird, limitiert werden kann, lassen sich die o.g. Priorität bzgl. des Speichers zugunsten einer Überschusseinspeisung aufweichen. Ein entstehender &amp;quot;Überschuss&amp;quot; steht also ggf. nur deshalb zur Verfügung, weil die Ladeleistung des Speichers begrenzt wurde.&lt;br /&gt;
&lt;br /&gt;
Ab Version 1.58.6 bietet SolarForecat die Möglichkeit, prognostizierte Verbräuche nicht mehr komplett bei der Bestimmung der &#039;&#039;optimalen Ladeleistung&#039;&#039; zu berücksichtigen, sondern nur noch zu einem prozentualen Anteil von 0..100 %. Hierzu muss der Attribut-Schlüssel &#039;&#039;&#039;ctrlBatSocManagementXX-&amp;gt;weightOwnUse=&amp;lt;Percentage&amp;gt;&#039;&#039;&#039; entsprechend gesetzt werden. &lt;br /&gt;
&lt;br /&gt;
So führt beispielsweise weightOwnUse=100 dazu, dass bei der Berechnung der optimalen Ladeleistung prognostizierte Verbräuche nicht mehr berücksichtigt werden indem sie bei der Bestimmung des stündlichen PV-Überschusses unberücksichtigt bleiben. Eine derartige Einstellung kann z.B. sinnvoll sein, wenn alle ansonsten täglich laufende Verbraucher zugunsten einer Batterieladung ausgeschaltet werden können.&lt;br /&gt;
&lt;br /&gt;
Ein Wert von zum Beispiel weightOwnUse=30 lässt 30% der stündlichen Verbrauchslast unberücksichtigt, sodass sich der kalkulierte PV-Überschuß pro Stunde um 30% des prognostizierten Verbrauchs erhöht. Da die Berechnung der optimierten Ladeleistung mit dem zur Verfügung stehenden PV-Überschuß gewichtet erfolgt, würde sich die kalkulierte Ladeleistung in den meisten Fällen ebenfalls um einen gewissen Betrag erhöhen sofern andere Faktoren dies nicht verhindern. Das kann beispielweise der Fall sein, wenn die kalkulierte Ladeleistung zu gering ist und automatisch aif mindestens &#039;&#039;&#039;setupBatteryDevXX-&amp;gt;pinreduced&#039;&#039;&#039; angehoben wurde.&lt;br /&gt;
  &lt;br /&gt;
  &lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Welche Ladestrategie soll ich wählen? - eine Möglichkeit zur Best-Practice Findung mit Codebeispiel ====&lt;br /&gt;
&lt;br /&gt;
In den vorangegangenen Abschnitten wurden die verschiedenen Möglichkeiten zur Steuerung der Batterieladung und dem SoC vorgestellt. Diese Steuerungsvarianten optimieren die Arbeitweise der Batterie, haben aber jeweils einen eigenen Fokus auch wenn es gewisse Überschneidungen gibt.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Die Steuerungsvarianten lassen sich folgendermaßen klassifizieren:&lt;br /&gt;
&lt;br /&gt;
# Steuerung der Batterieladung über eine Ladefreigabe, die durch bestimmte Indikatoren festgelegt wird. Diese Variante ist in [[SolarForecast_-_Solare_Prognose_(PV_Erzeugung)_und_Verbrauchersteuerung#PV-Prognose_und_Verbrauch_optimierte_Beladungssteuerung_unter_Berücksichtigung_einer_Wirkleistungsbegrenzung|diesem]] Abschnitt beschrieben.&lt;br /&gt;
# Steuerung der Batterieladung durch eine optimierte maximale Ladeleistung. Diese Variante ist in [[SolarForecast_-_Solare_Prognose_(PV_Erzeugung)_und_Verbrauchersteuerung#leistungsoptimierte_Beladungssteuerung_mit_dem_Fokus_geringe_Verlustleistung_und_Verschleiß|diesem]] Abschnitt dargelegt. Zu dieser Variante gehören die Strategien &#039;&#039;optPower&#039;&#039; sowie &#039;&#039;smartPower&#039;&#039;. Die Unterschiede beider Strategien sind ebenfalls in dem verlinkten Abschnitt beschrieben.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Die &#039;&#039;&#039;Variante 1.)&#039;&#039;&#039; gibt die Ladung der Batterie frei wenn der (verbleibende) PV-Überschuß des Tages kleiner oder gleich dem Ladungsbedarf der Batterie ist um einen SoC von 100% bzw. den maximal möglichen SoC am Ende des Tages zu erreichen. Dadurch wird die in der Batterie vorhandene freie Kapazität so lange wie möglich der Einhaltung von besonderen Grenzwerten, z.B. des Einspeiselimits (plantControl-&amp;gt;feedinPowerLimit), vorbehalten. Nebeneffekt ist, dass plötzlich und unerwartet auftretende hohe Erzeugungsleistungen in die Batterie geladen werden können und nicht unerwünscht in das öffentliche Netz eingespeist werden. &lt;br /&gt;
&lt;br /&gt;
Die &#039;&#039;&#039;Variante 2.)&#039;&#039;&#039; lässt die Batterie kontinuierlich laden, wobei die Ladeleistung auf ein optimiertes Minimum reduziert wird ohne das Ziel, einen maximalen SoC am Tagesende zu erreichen, zu ignorieren. Dadurch wird die Batterie schonend, mit möglichst wenig Verlustleistung und verschleißarm geladen. Die Vorhaltung der freien Kapaziäten der Batterie sind in dieser Variante nicht die Prämisse. Die Einhaltung des gesetzten Enispeiselimits wird auch in dieser Variante beachtet sofern es der Ladezustand der Batterie zulässt. Technisch bedingt kann diese Variante nur ungenügend auf unerwartete PV-Überschüsse reagieren, was zu einer Einspeisung in das öffentliche Netz oder im ungünstigsten Fall zur Abregelung der Anlage führen kann. Mit dem Attribut &#039;&#039;&#039;ctrlBatSocManagementXX-&amp;gt;safetyMargin&#039;&#039;&#039; kann man diesem negativen Aspekt in gewissem Maße entgegenwirken.  &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Ausgehend von diesen unterschiedlichen Optimierungszielen bietet sich die Variante 1.) vorwiegend in den Übergangs- und Wintermonaten an. Durch die beschriebenen Eigenschaften dieser Variante ist bei tendeziell weniger PV-Überschuß über den Tag die Ladung der Batterie komplett freigegeben, wodurch bei plötzlich auftretenden sonnige Abschnitten und Aufklarungen die Batterie sofort mit dem verfügbaren PV-Überschuß geladen wird. Auch eine Einspeisung über das Einspeiselimit hinaus kann verhindert werden. In der übrigen Zeit wird die Ladeleistung durch den verfügbaren PV-Überschuß begrenzt. &lt;br /&gt;
Weiterhin soll der SoC nicht dauerhaft tief absinken und ebenfalls nicht auf einem zu hohen Level verharren was auch einer maximal möglichen Energiespeicherung an sonnigen Tagen entgegenwirken würde.&lt;br /&gt;
In dieser Zeit ist die Nutzung der Kombination von [[SolarForecast_-_Solare_Prognose_(PV_Erzeugung)_und_Verbrauchersteuerung#Nutzung_des_Batterie_SOC-Management|SoC-Management]] und Variante 1.) wohl am Besten geeignet um die Ziele zu verwirklichen sowie Notreserven in den Batterien vorzuhalten. &lt;br /&gt;
&lt;br /&gt;
In den Sommermonaten hingegen ist üblicherweise sehr viel PV-Energie und Überschuß vorhanden. In dieser Periode würden die Batterien mit sehr hoher Leistung sehr zeitig am Tag vollgeladen. Die Variante 1.) ist im Prinzip ebenfalls gut geeignet den Zeitpunkt der Vollladung hinauszuzögern, jedoch erscheint die Varinte 2.) in dieser Zeit besser geeignet mit einer über den gesamten Tag verteilten moderaten Ladeleistung den Ziel-SoC zu erreichen. Auch diese Variante versucht eine Überschreitung des Einspeiselimits zu verhindern sofern der Ladezustand der Batterie dies ermöglicht.&lt;br /&gt;
&lt;br /&gt;
Das nachfolgende Codebeispiel bietet die Möglichkeit über ein Attribut die Ladevariante auszuwählen, über ein weiteres Attribut die SoC-Steuerung zu aktivieren/deaktivieren und integriert auch die Ladesteuerung für eine Batterie-Anforderungsladung bei Unterschreitung von ctrlBatSocManagementXX-&amp;gt;lowSoc. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Die gesteuerte Batterieanlage besteht aus:&lt;br /&gt;
&lt;br /&gt;
* 3 x Victron MultiPlusII 3000/48&lt;br /&gt;
* Steuergerät CerboGX (in FHEM über MQTT eingebunden)&lt;br /&gt;
* einem Batteriestack bestehend aus Pylontech US3000C (wird durch CerboxGX gesteuert)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Zur Umschaltung bzw. Auswahl der Ladestrategie und des SoC-Managements werden zwei User-Attribute im SolarForecast Device angelegt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
attr ... userattr userFn_BatterySoCManagement:Ein,Aus userFn_BatteryLoadManagement:Aus,loadRelease,optPower,smartPower&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Im Code werden diese Attribute ausgewertet. Das nachfolgende Script vereint folgende Funktionalitäten:&lt;br /&gt;
&lt;br /&gt;
* die Aktivierung/Deaktivierung des SoC-Managements via Attribut userFn_BatterySoCManagement&lt;br /&gt;
* die Auswahl der Ladestrategie oder Deaktivierung der Ladesteuerung via attribut userFn_BatteryLoadManagement&lt;br /&gt;
* Integration einer Anforderungsladung bei Signalisierung durch Reading Battery_ChargeRequest_XX&lt;br /&gt;
* Generierung von Readings userFn_Victron_GridSetpoint_set und userFn_MPII_MaxChargeCurrent_set zur Dokumentation der eingestellten Soll-Werte&lt;br /&gt;
&lt;br /&gt;
  &lt;br /&gt;
Bei der Nachnutzung des Scripts sind zunächst die &#039;&#039;&#039;Konstanten&#039;&#039;&#039; und das Device &#039;&#039;&#039;MQTT2_cerboGX_c0619ab34e08_setting&#039;&#039;&#039;s sowie dessen Readings im Block &#039;&#039;&#039;aktuelle Indikatoren&#039;&#039;&#039; anzupassen.&lt;br /&gt;
&lt;br /&gt;
Das Script wird in die Datei &#039;&#039;&#039;99_mySolarForecastUtils.pm&#039;&#039;&#039; eingetragen und im SolarForecast Attribut &#039;&#039;&#039;ctrlUserExitFn&#039;&#039;&#039; hinterlegt: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
attr ... ctrlUserExitFn {&lt;br /&gt;
                          ::batLoadMgmnt ($name, &#039;01&#039;);&lt;br /&gt;
                          # ::batLoadMgmnt ($name, &#039;02&#039;);   # optional für eine weitere Batterie &#039;02&#039;&lt;br /&gt;
                          # ::batLoadMgmnt ($name, &#039;0X&#039;);   # optional für weitere Batterien&lt;br /&gt;
                        }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Mit diesem Script ist der Nutzer in der Lage, das Batterie Lademanagement entsprechend seiner Motivation und gegebenenfalls abhängig von der Jahreszeit umzustellen und das Ergebnis bzw. die Zielerreichung zu testen.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Das Attribut &#039;&#039;&#039;userFn_BatteryLoadManagement&#039;&#039;&#039; selektiert die gewünschte Ladestrategie:&lt;br /&gt;
&lt;br /&gt;
;Aus: das Lademangement ist ausgeschaltet &lt;br /&gt;
&lt;br /&gt;
;loadRelease: das Lademangement Variante 1.) Ladestrategie Ladefreigabe ist aktiviert &lt;br /&gt;
&lt;br /&gt;
;optPower: das Lademangement Variante 2.) Ladestrategie optimierte Ladeleistung ist aktiviert &lt;br /&gt;
&lt;br /&gt;
;smartPower: das Lademangement Variante 2.) Ladestrategie zieloptimierte Ladeleistung ist aktiviert&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Die selektierte Ladestrategie wird im Script in das SolarForecast Device repliziert, d.h. der Attributschlüssel ctrlBatSocManagement01-&amp;gt;loadStrategy wird identisch zum Wert des Attributes userFn_BatteryLoadManagement gesetzt. Dazu wird das Set-Kommando &#039;&#039;attrKeyVal&#039;&#039; verwendet. Das globale Attribut &#039;&#039;autosave&#039;&#039; sollte aus diesem Grund NICHT explizit auf &amp;quot;0&amp;quot; gesetzt sein um eine reibungslose Funktion zu ermöglichen. &lt;br /&gt;
&lt;br /&gt;
Hat man die individuell passende Management-Strategie gefunden, bietet sich auch eine automatische (zum Beispiel durch einen Kalender oder Sonnenstand (Elevation) zur Mittagszeit) Umschaltung der Ladestrategie an. Die Unterschreitung der Elevation unter einen bestimmten Wert würde den Beginn des Winterhalbjahres und reverse Überschreitung den Beginn des Sommerhalbjahres kennzeichnen. Entsprechend würde dann die gewünschte Ladestrategie selektiert. Alternativ könnte auch die PV-Überschußprognose für den aktuellen Tag über das Reading &#039;&#039;Today_PVforecast&#039;&#039; für eine Entscheidung bzgl. der Ladestrategie herangezogen werden.&lt;br /&gt;
&lt;br /&gt;
Das SoC-Management kann über das gesamte Jahr aktiviert bleiben, hat jedoch nach bisherigen Erfahrungen nur im Winterhalbjahr eine starke Relevanz.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;mw-collapsible mw-collapsed&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Code Lademanagement -&amp;gt;&#039;&#039;&#039; &amp;lt;div class=&amp;quot;mw-collapsible-content&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
###############################################################################&lt;br /&gt;
#&lt;br /&gt;
# Save this file as 99_mySolarForecastUtils.pm, and create your own functions&lt;br /&gt;
# in the new file. They are then available in every Perl expression.&lt;br /&gt;
#&lt;br /&gt;
###############################################################################&lt;br /&gt;
 package main;&lt;br /&gt;
 use strict;&lt;br /&gt;
 use warnings;&lt;br /&gt;
&lt;br /&gt;
 sub&lt;br /&gt;
 mySolarForecastUtils_Initialize($$)&lt;br /&gt;
 {&lt;br /&gt;
   my ($hash) = @_;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
# ab hier eigenen Code eintragen&lt;br /&gt;
###############################################################################&lt;br /&gt;
&lt;br /&gt;
############################################################################&lt;br /&gt;
#      Komplettes Batterie Lademanagement&lt;br /&gt;
############################################################################&lt;br /&gt;
sub batLoadMgmnt {&lt;br /&gt;
  my $name = shift;&lt;br /&gt;
  my $bn   = shift // &#039;01&#039;;                                                             # Batterienummer&lt;br /&gt;
  my $hash = $defs{$name};&lt;br /&gt;
  &lt;br /&gt;
  ## Konstanten&lt;br /&gt;
  ###############  &lt;br /&gt;
  use constant {&lt;br /&gt;
    GSPDEF    =&amp;gt; 20,                                                                    # Einstellung des Grid Setpoint&lt;br /&gt;
    FINDEF    =&amp;gt; 5000,                                                                  # Default Einspeiselimit&lt;br /&gt;
    MINSOCDEF =&amp;gt; 10,                                                                    # Default Minimum SoC&lt;br /&gt;
    MAXPLDEF  =&amp;gt; 105,                                                                   # max. Ladestrom (A) Victron MPII &lt;br /&gt;
    SYSVOLTAG =&amp;gt; 48,                                                                    # Batterie Systemspannung&lt;br /&gt;
  };&lt;br /&gt;
  &lt;br /&gt;
  my $batsocmgmt  = AttrVal ($name, &#039;userFn_BatterySoCManagement&#039;,  &#039;Aus&#039;);             # SoC-Management Vorgabe&lt;br /&gt;
  my $batloadmgmt = AttrVal ($name, &#039;userFn_BatteryLoadManagement&#039;, &#039;Aus&#039;);             # Lademanagement Vorgabe&lt;br /&gt;
  my $vicsets     = &#039;MQTT2_cerboGX_c0619ab34e08_settings&#039;;                              # Victron CerboGX Einstellungen&lt;br /&gt;
&lt;br /&gt;
  ## Ladestrategie Ist-Einstellung und Soll-Abgleich&lt;br /&gt;
  ####################################################&lt;br /&gt;
  my $cgbt     = AttrVal ($name, &#039;ctrlBatSocManagement&#039;.$bn, undef);&lt;br /&gt;
  my $strategy = &#039;loadRelease&#039;; &lt;br /&gt;
&lt;br /&gt;
  if ($cgbt) {&lt;br /&gt;
    my $parsed = FHEM::SolarForecast::__parseAttrBatSoc ($name, $cgbt);                 # aktuell gesetzte Strategie ermitteln&lt;br /&gt;
    $strategy  = $parsed-&amp;gt;{loadStrategy} // $strategy;&lt;br /&gt;
  }&lt;br /&gt;
  &lt;br /&gt;
  if ($batloadmgmt ne &#039;Aus&#039; &amp;amp;&amp;amp; $strategy ne $batloadmgmt) {&lt;br /&gt;
    CommandSet (undef, &amp;quot;$name attrKeyVal ctrlBatSocManagement${bn} loadStrategy=$batloadmgmt&amp;quot;);              &lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  ## aktuelle Indikatoren&lt;br /&gt;
  ##########################&lt;br /&gt;
  my $surp    = ReadingsNum ($name, &#039;Current_Surplus&#039;,            0);                   # aktueller PV-Überschuß&lt;br /&gt;
  my $bcrq    = ReadingsNum ($name, &#039;Battery_ChargeRequest_&#039;.$bn, 0);                   # Notfallladung&lt;br /&gt;
  my $curgsp  = ReadingsNum ($vicsets, &#039;GridSetpoint&#039;, GSPDEF);                         # aktuelle Einstellung Grid Setpoint&lt;br /&gt;
  my $finplim = FHEM::SolarForecast::CurrentVal ($name, &#039;feedinPowerLimit&#039;,   FINDEF);  # Limit plantControl-&amp;gt;feedinPowerLimit&lt;br /&gt;
  my $preduce = FHEM::SolarForecast::BatteryVal ($name, $bn, &#039;bpinreduced&#039;, MAXPLDEF);  # reduzierte Ladeleistung Bat-Konfig&lt;br /&gt;
  my $actmcc  = ReadingsNum ($vicsets, &#039;MaxChargeCurrent&#039;,     0);                      # akt. maximale Ladestromeinstellung&lt;br /&gt;
  my $actmcp  = ReadingsNum ($vicsets, &#039;MaxChargePower&#039;,   undef);                      # akt. Ladeleistungseinstellung&lt;br /&gt;
  my $load    = MAXPLDEF;                                                               # initialer Soll-Ladestrom (A)&lt;br /&gt;
  &lt;br /&gt;
  my $targetgsp = GSPDEF;                                                               # Voreinstellung Grid Setpoint&lt;br /&gt;
  my $ctype     = &#039;default&#039;;                                                            # Voreinstellung Steuerungstyp  &lt;br /&gt;
&lt;br /&gt;
  ## Battery SoC Management&lt;br /&gt;
  ###########################  &lt;br /&gt;
  if ($batsocmgmt eq &#039;Ein&#039;) { &lt;br /&gt;
    my $csoc = ReadingsNum ($vicsets, &#039;MinimumSocLimit&#039;,               MINSOCDEF);     # akt. SoC&lt;br /&gt;
    my $osoc = ReadingsNum ($name,    &#039;Battery_OptimumTargetSoC_&#039;.$bn, MINSOCDEF);     # optimierter Mindest-SoC&lt;br /&gt;
	&lt;br /&gt;
    if ($csoc != $osoc) {&lt;br /&gt;
      CommandSet (undef, &amp;quot;$vicsets MinimumSocLimit $osoc&amp;quot;);&lt;br /&gt;
      Log3 ($name, 3, qq{$name - userFn SoCMgmnt -&amp;gt; MinimumSocLimit in $vicsets set to $osoc %});&lt;br /&gt;
    }                                       &lt;br /&gt;
  }&lt;br /&gt;
  &lt;br /&gt;
  ## Steuerung nach Ladungsfreigabe&lt;br /&gt;
  ###################################    &lt;br /&gt;
  if ($batloadmgmt eq &#039;loadRelease&#039;) {&lt;br /&gt;
    $ctype         = &#039;loadRelease&#039;;    &lt;br /&gt;
    my $unrestrict = ReadingsNum ($name, &#039;Battery_ChargeUnrestricted_&#039;.$bn, 0);         # Ladefreigabe (1 -&amp;gt; Ladefreigabe)&lt;br /&gt;
	&lt;br /&gt;
    if (!$unrestrict) {                                                                 # Grid Setpoint absenken wenn keine .. &lt;br /&gt;
        $targetgsp    = GSPDEF - $surp;                                                 # ..Batterie Ladefreigabe&lt;br /&gt;
        my $neggspmax = -1 * $finplim;&lt;br /&gt;
		$targetgsp    = $neggspmax if($targetgsp &amp;lt; $neggspmax);                         # Begrenzung der Einspeiseleistung&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
  &lt;br /&gt;
  if ($curgsp != $targetgsp) {                                                          # GridSetpoint setzen&lt;br /&gt;
	  fhem (&amp;quot;set $vicsets GridSetpoint $targetgsp&amp;quot;);&lt;br /&gt;
      Log3 ($name, 3, qq{$name - userFn ChargeMgmnt &#039;loadRelease&#039; -&amp;gt; GridSetpoint in $vicsets set to $targetgsp});&lt;br /&gt;
  }&lt;br /&gt;
  &lt;br /&gt;
  readingsSingleUpdate ($hash, &#039;userFn_Victron_GridSetpoint_set&#039;, $targetgsp, 0); &lt;br /&gt;
  &lt;br /&gt;
  ## Steuerung nach optimaler Ladeleistung&lt;br /&gt;
  ##########################################&lt;br /&gt;
  if ($batloadmgmt =~ /(?:opt|smart)Power/xs) {&lt;br /&gt;
    $ctype  = &#039;optPower&#039;; &lt;br /&gt;
    my $otp = ReadingsNum ($name, &#039;Battery_ChargeOptTargetPower_&#039;.$bn, $preduce);      # optimale Ladeleistung (W)&lt;br /&gt;
    $load   = sprintf &amp;quot;%.0f&amp;quot;, ($otp / SYSVOLTAG);&lt;br /&gt;
  }&lt;br /&gt;
  &lt;br /&gt;
  ## Anforderungsladung&lt;br /&gt;
  ####################### &lt;br /&gt;
  if ($bcrq) {                                                                         # max. Ladeleistung...&lt;br /&gt;
    $ctype = &#039;requestCharging&#039;;                                                        # bei Battery_ChargeRequest&lt;br /&gt;
    my $p;&lt;br /&gt;
    &lt;br /&gt;
	if ($surp &amp;lt; $preduce) {&lt;br /&gt;
	  $p = $preduce;&lt;br /&gt;
    }&lt;br /&gt;
    else {&lt;br /&gt;
      $p = ReadingsNum ($name, &#039;Battery_ChargeOptTargetPower_&#039;.$bn, $preduce);         # optimale Ladeleistung (W) &lt;br /&gt;
    }&lt;br /&gt;
	&lt;br /&gt;
    $load = sprintf &amp;quot;%.0f&amp;quot;, ($p / SYSVOLTAG);&lt;br /&gt;
  }&lt;br /&gt;
  &lt;br /&gt;
  ## Stromeinstellung umsetzen&lt;br /&gt;
  #############################  &lt;br /&gt;
  if ($load != $actmcc) {&lt;br /&gt;
    CommandSet (undef, &amp;quot;$vicsets MaxChargeCurrent $load&amp;quot;);&lt;br /&gt;
	&lt;br /&gt;
    Log3 ($name, 3, qq{$name - userFn ChargeMgmnt &#039;$ctype&#039; -&amp;gt; MaxChargeCurrent in $vicsets set }.&lt;br /&gt;
                    qq{from old $actmcc A to $load A});&lt;br /&gt;
  }&lt;br /&gt;
  &lt;br /&gt;
  readingsSingleUpdate ($hash, &#039;userFn_MPII_MaxChargeCurrent_set&#039;, $load, 0);&lt;br /&gt;
  &lt;br /&gt;
return;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
###############################################################################&lt;br /&gt;
# Ende eigener Code&lt;br /&gt;
&lt;br /&gt;
1;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== barrierSoC - der Parameter den nicht jeder braucht  ====&lt;br /&gt;
&lt;br /&gt;
Wie bereits hinlänglich beschrieben, aktiviert das Attribut &#039;&#039;&#039;ctrlBatSocManagementXX&#039;&#039;&#039; mit diversen Schlüsseln die Batterie SoC- und Ladesteuerung. Für die alltägliche Nutzung und um schnell sichtbare Ergebnisse zu erzielen, ist die die Verwendung der obligatorischen Schlüssel lowSoc, upSoC und vielleicht noch die Auswahl der gewünschten Ladestrategie zunächst völlig ausreichend. Das Modul stellt für die Batteriesteuerung aber noch einige weitere Schlüssel zu Verfügung, die besondere Anforderungen an die Batteriesteuerung unterstützen. Einer dieser Parameter ist der &#039;&#039;&#039;barrierSoC&#039;&#039;&#039; Schlüssel.&lt;br /&gt;
&lt;br /&gt;
Wird eine der beschriebenen Batterie Ladestrategien &#039;&#039;optPower&#039;&#039; oder &#039;&#039;smartPower&#039;&#039; eingesetzt, wird die Batterieladungsleistung vom aktuell vorhandenen SoC bis zum Ziel-SoC optmiert im Reading Battery_ChargeOptTargetPower_XX bereitgestellt. Dabei wird auch eine eventuell in &#039;&#039;ctrlBatSocManagementXX-&amp;gt;loadTarget&#039;&#039; eingestellte Zielzeit berücksichtigt. Möglicherweise besteht der Wunsch die Batterieladung bis zu einem bestimmten Mindest-SoC so schnell wie möglich aufzuladen um z.B. zügig eine Netzausfallreserve aufzubauen. &lt;br /&gt;
&lt;br /&gt;
Zu diesem Zweck kann der optionale Parameter &#039;&#039;ctrlBatSocManagementXX-&amp;gt;barrierSoC&#039;&#039; verwendet werden. In dem Ladebereich&lt;br /&gt;
&lt;br /&gt;
  lowSoC &amp;lt;= SoC &amp;lt; barrierSoC&lt;br /&gt;
&lt;br /&gt;
kann der User Einfluß auf die berechnete Ladeleistung nehmen und sie ggf. bis zur maximal möglichen Leistung abändern. Das bedeutet alle Manipulationen erfolgen ausschließlich diesem SoC-Bereich.&lt;br /&gt;
Dazu kann dem Parameter mit einer bestimmten Syntax das Änderungsverhalten vorgegeben werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
barrierSoC=XX:&amp;lt;Aktion&amp;gt;:&amp;lt;Wert&amp;gt; oder&lt;br /&gt;
barrierSoC=XX:&amp;lt;Aktion&amp;gt;:&amp;lt;Reading&amp;gt;:&amp;lt;default&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dabei bedeutet:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
XX - der Wert von barrierSoC in %. Es gilt lowSoC &amp;lt; XX &amp;lt; maxSoC&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Mit der &#039;&#039;&#039;&amp;lt;Aktion&amp;gt;&#039;&#039;&#039; wird eine der möglichen Manipulations-Varianten festgelegt:&lt;br /&gt;
&lt;br /&gt;
;max: - das Ladeleistungslimit wird auf setupBatteryDevXX-&amp;gt;pinmax festgelegt&lt;br /&gt;
;set: - das Ladeleistungslimit wird auf einen festen Wert oder Readingswert gesetzt&lt;br /&gt;
;inc: - das Ladeleistungslimit wird um einen festen Wert oder Readingswert erhöht&lt;br /&gt;
;dec: - das Ladeleistungslimit wird um einen festen Wert oder Readingswert verringert&lt;br /&gt;
;prc: - das Ladeleistungslimit wird um einen prozentualen Wert / Readingswert geändert&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Diese Aktion (außer &#039;&#039;max&#039;&#039;) wird auf den angegeben Wert bzw. den Wert des angegebenen Readings angewendet. Das Reading muß im SolarForecast Device enthalten sein. Bei Verwendung eines Readings wird mit &#039;&#039;&amp;lt;default&amp;gt;&#039;&#039; der Standardrückgabewert festgelegt falls das Reading keinen Wert liefert oder nicht vorhanden ist.&lt;br /&gt;
&lt;br /&gt;
Hier sind einige Beispiele für einen barrierSoC. Zur Vereinfachung wird ein barrierSoC von 40 % verwendet und kann natürlich auf einen anderen passenden Wert abgeändert werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
	barrierSoC=40:max:-                      -&amp;gt; das Ladeleistungslimit wird auf setupBatteryDevXX-&amp;gt;pinmax gesetzt&lt;br /&gt;
	barrierSoC=40:set:&amp;lt;Reading&amp;gt;:&amp;lt;default&amp;gt;    -&amp;gt; das Ladeleistungslimit auf den Wert &amp;lt;Reading&amp;gt; setzen&lt;br /&gt;
	barrierSoC=40:set:1350                   -&amp;gt; das Ladeleistungslimit wird auf 1350 W gesetzt&lt;br /&gt;
	barrierSoC=40:inc:&amp;lt;Reading&amp;gt;:&amp;lt;default&amp;gt;    -&amp;gt; das Ladeleistungslimit um den Wert &amp;lt;Reading&amp;gt; erhöhen&lt;br /&gt;
	barrierSoC=40:inc:200                    -&amp;gt; das Ladeleistungslimit wird um 200 W erhöht&lt;br /&gt;
	barrierSoC=40:dec:&amp;lt;Reading&amp;gt;:&amp;lt;default&amp;gt;    -&amp;gt; das Ladeleistungslimit um den Wert &amp;lt;Reading&amp;gt; verringern&lt;br /&gt;
	barrierSoC=40:dec:100                    -&amp;gt; das Ladeleistungslimit wird um 100 W verringert&lt;br /&gt;
	barrierSoC=40:prc:&amp;lt;Reading&amp;gt;:&amp;lt;default&amp;gt;    -&amp;gt; das Ladeleistungslimit um &amp;lt;Reading&amp;gt; Prozent ändern (+ erhöhen, - verringern)&lt;br /&gt;
	barrierSoC=40:prc:50                     -&amp;gt; das Ladeleistungslimit um 50% ändern (+ erhöhen, - verringern) &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== sequentielle Batterie-Ladung mehrerer Batterien mit lcSlot ====&lt;br /&gt;
&lt;br /&gt;
Der Parameter &#039;&#039;&#039;ctrlBatSocManagementXX-&amp;gt;lcSlot&#039;&#039;&#039; (&amp;quot;load control Slot&amp;quot;) definiert eine Zeitperiode am aktuellen Tag in der die Steuerungslogik für die Batteriesteuerung aktiviert ist. Ist dieser Parameter nicht gesetzt, ist das eingestellte Lademanagement über den gesamten Tag aktiert und wäre identisch mit der Einstellung:&lt;br /&gt;
&lt;br /&gt;
 lcSlot=00:00-23:59&lt;br /&gt;
&lt;br /&gt;
Somit ist die Ladesteuerung per default über den gesamten Tag aktiv.&lt;br /&gt;
&lt;br /&gt;
Durch das Setzen von lcSlot auf einen täglichen Slot wird die Ladesteuerung auf diese Periode eingegrenzt. In der übrigen Zeit ist die Ladung der Batterie freigegeben (Reading Battery_ChargeUnrestricted_XX) bzw. auf die maximal konfigurierte Ladeleistung eingestellt (Reading Battery_ChargeOptTargetPower_XX). Diese Readings können je nach verwendeter Ladestrategie ausgewertet und zur Steuerung der Batterie(n) verwendet werden.  &lt;br /&gt;
Die SoC-Steuerung wird durch die lcSlot-Angabe nicht beeinflusst.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Mit lcSlot kann zum Beispiel ein sequentielles Laden von mehreren Batterien umgesetzt werden. Nachfolgend wird ein solches Szenario mit drei Batterien  skizziert:&lt;br /&gt;
&lt;br /&gt;
1. die Batterie 1 soll mit voller Leistung geladen werden, die anderen Batterien nur bei weiterem Überschuß. Man würde die jeweiligen &lt;br /&gt;
ctrlBatSocManagementXX-&amp;gt;lcSlot Schlüssel definieren:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
set ... attrKeyVal ctrlBatSocManagement01 lcSlot=23:00-23:10&lt;br /&gt;
set ... attrKeyVal ctrlBatSocManagement02 lcSlot=00:00-23:59&lt;br /&gt;
set ... attrKeyVal ctrlBatSocManagement03 lcSlot=00:00-23:59&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;Wie ist der Ablauf: Die Batterie 1 unterliegt keiner Steuerung (nur von 23:00 bis 23:10 als Dummy-Periode). Das Reading Battery_ChargeUnrestricted_01 wird &amp;quot;1&amp;quot; gesetzt, d.h. die Batterie 1 wird zur Ladung uneingeschränkt freigegeben. Die anderen Batterien unterliegen der Steuerung -&amp;gt; Battery_ChargeUnrestricted_02 / 03 sind &amp;quot;0&amp;quot; und sollen nur geladen werden falls ein gesetztes Einspeiselimit überschritten wird.&lt;br /&gt;
&lt;br /&gt;
Ist das Ladeziel der Batterie 1 erreicht, setzt man um die Batterie 2 vollzuladen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
set ... attrKeyVal ctrlBatSocManagement01 lcSlot=00:00-23:59&lt;br /&gt;
set ... attrKeyVal ctrlBatSocManagement02 lcSlot=23:00-23:10&lt;br /&gt;
set ... attrKeyVal ctrlBatSocManagement03 lcSlot=00:00-23:59&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Batterien 1 und 3 unterliegen der Steuerung, d.h. Battery_ChargeUnrestricted_01 / 03 haben den Wert &amp;quot;0&amp;quot;. Demgegenüber ist Battery_ChargeUnrestricted_02=1 und soll uneingeschränkt geladen werden.&lt;br /&gt;
&lt;br /&gt;
Ist auch die Batterie 2 voll geladen, würde man das Laden der Batterie 3 aktivieren:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
set ... attrKeyVal ctrlBatSocManagement01 lcSlot=00:00-23:59&lt;br /&gt;
set ... attrKeyVal ctrlBatSocManagement02 lcSlot=00:00-23:59&lt;br /&gt;
set ... attrKeyVal ctrlBatSocManagement03 lcSlot=23:00-23:10&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Das Setzens via &#039;&#039;&#039;attrKeyVal&#039;&#039;&#039; hat den Vorteil, dass die Zeitfenster-Syntax (Anfangszeit kleiner Endezeit usw.) im Hintergrund peprüft und ggf. ein Fehler zurückgegeben wird, den der Nutzer in seinem Script auswerten kann. Weiterhin werden die Attribute implizit gespeichert sofern global-&amp;gt;autosave nicht explizit auf &amp;quot;0&amp;quot; gesetzt ist.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Unterstützung eines netzdienlichen Verhaltens ====&lt;br /&gt;
&lt;br /&gt;
Der Steuerungsprozess der Batterieanlage leistet insgesamt einen kleinen Beitrag zur Netzstabilität, indem die Speicherladung in den Zeitraum der meisten prognostizierten Überschußeinspeisung gelegt wird. Im Sommer kann sich dieser Zeitraum in die Zeit nach dem Maxmimum des prognostizierten PV-Überschusses verlagern, wenn auch danach noch genügend Überschußenergie zu Volladung der Batterie prognostiziert wird. In dem Zusammenhang sollte im Attribut &#039;&#039;&#039;plantControl-&amp;gt;feedinPowerLimit&#039;&#039;&#039; ein Einspeiselimit gesetzt werden. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Zur Verdeutlichung soll das Beispiel dienen:&lt;br /&gt;
&lt;br /&gt;
* die Batterie ist zu 60% geladen &lt;br /&gt;
* der User hat upSoC=40 eingestellt&lt;br /&gt;
* der User hat maxSoC=90 eingestellt&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Am Vormittag ist genügend Sonne vohanden, das Maximumum der Energie über die Zeit wird aber noch erwartet. &lt;br /&gt;
Zu diesem Zeitpunkt wird das Reading  &#039;&#039;&#039;Battery_ChargeUnrestricted_XX&#039;&#039;&#039; auf den Wert &amp;quot;0&amp;quot; gesetzt. Durch den Nutzer erfolgt daraufhin, zum Beipiel über ein Notify, ein anlagenspezifischer Befehl &amp;quot;Ladestop&amp;quot; an die Batterieanlage. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Ab diesem Moment wird:&lt;br /&gt;
&lt;br /&gt;
# die Batterie nicht geladen&lt;br /&gt;
# die erzeugte PV-Energie dem Haushalt zur Verfügung gestellt bzw. der verbleibende Überschuß eingespeist, sofern der Überschuß unterhalb des im Attribut &#039;&#039;&#039;plantControl-&amp;gt;feedinPowerLimit&#039;&#039;&#039; definierten Limits liegt.&lt;br /&gt;
# ein eventuell über dem Limit &#039;&#039;&#039;plantControl-&amp;gt;feedinPowerLimit&#039;&#039;&#039; vorhandener Überschuß in die Batterie geladen und so die Netzlast verringert sowie die Batterie schonend geladen. (dieser Punkt trifft in den Monaten mit wenig PV-Erzeugung eher nicht zu)  &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Gleichzeitig liefert die Batterie Energie an das Hausnetz und wird von 60% bis maximal einem SoC-Wert entladen, der durch die [[#Die_dynamische_SOC_Steuerung_mit_Umsetzungsbeispiel|dynamische SOC Steuerung]] bestimmt wird. Dazu muß diese Steuerung natürlich implementiert sein. Sofern der berechnete SoC größer als upSoC ist, wird der maximale Entladungs-SoC auf upSoC gesetzt. &lt;br /&gt;
&lt;br /&gt;
Zusätzlich überprüft das Modul permanent bei jedem Zyklus die Differenz des aktuell vorhandenen SoC zum eingestellten maxSoC.&lt;br /&gt;
Sie soll nicht größer sein um mit dem (noch) zu erwartenden PV-überschuß des Tages der eingestellte maxSoC bzw. dessen Nähe wahrscheinlich erreicht werden kann. &lt;br /&gt;
Sollte der Differenz-Grenzwert erreicht sein, wird &#039;&#039;&#039;Battery_ChargeUnrestricted_XX=1&#039;&#039;&#039; gesetzt und der Nutzer sollte dann über einen geeigneten Befehl seine Anlage in den Modus &amp;quot;Laden&amp;quot; umschalten.&lt;br /&gt;
Dieser Vorgang kann beliebig oft während des Tages alternieren.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Nutzung von batteryTrigger ===&lt;br /&gt;
Ergänzt werden die Möglichkeiten durch das set-Kommando: &lt;br /&gt;
&lt;br /&gt;
 batteryTrigger &amp;lt;1on&amp;gt;=&amp;lt;Wert&amp;gt; &amp;lt;1off&amp;gt;=&amp;lt;Wert&amp;gt; [&amp;lt;2on&amp;gt;=&amp;lt;Wert&amp;gt; &amp;lt;2off&amp;gt;=&amp;lt;Wert&amp;gt; ...] &lt;br /&gt;
&lt;br /&gt;
Das Kommando setzt Triggerpunkte bei Über- bzw. Unterschreitung bestimmter Batterieladungswerte (SoC in %).&lt;br /&gt;
&lt;br /&gt;
Der verwendete SoC wird als resultierender SoC als Summe der aktuellen Ladung aller registrierten Batterie-Geräte im Verhältnis zur installierten Gesamtkapazität gebildet. Das bedeutet, dass alle einzelnen Batterie-Geräte als ein gemeinsamer Cluster betrachtet werden. &lt;br /&gt;
&lt;br /&gt;
Es kann eine beliebige Anzahl von Triggerbedingungen angegeben werden. Xon/Xoff-Bedingungen müssen nicht zwingend paarweise definiert werden.&lt;br /&gt;
Überschreiten die letzten drei SoC-Messungen eine definierte Xon-Bedingung, wird das Reading &#039;&#039;&#039;batteryTrigger_X = on&#039;&#039;&#039; erstellt/gesetzt.&lt;br /&gt;
Unterschreiten die letzten drei SoC-Messungen eine definierte Xoff-Bedingung, wird das Reading &#039;&#039;&#039;batteryTrigger_X = off&#039;&#039;&#039; erstellt/gesetzt.&lt;br /&gt;
&lt;br /&gt;
Der Inhalt der Readings kann durch ein User-Programm oder, bei Erzeugung von entsprechenden Events, mittels notify-Device ausgewertet werden um bei Eintreten eines definierten Ereignisses entsprechende Reaktionen auszulösen. Das kann zum Beispiel die Freigabe eines Heizstabes oberhalb eines bestimmten SOC sein oder ein Entladeverbot der Batterie unterhalb eines SOC-Wertes um Reserven für einen eventuellen Stromausfall zu behalten.&lt;br /&gt;
&lt;br /&gt;
Damit die Batterietrigger dynamisch geändert werden können, ist die Einstellung als Set-Kommando und nicht als Attribut ausgeführt.   &lt;br /&gt;
&lt;br /&gt;
Alle Triggerpunkte können mit dem set-Befehl:&lt;br /&gt;
&lt;br /&gt;
 reset batteryTriggerSet&lt;br /&gt;
&lt;br /&gt;
wieder gelöscht werden.&lt;br /&gt;
&lt;br /&gt;
== Verbrauchersteuerung - Registrieren und visualisieren von Verbrauchern ==&lt;br /&gt;
&lt;br /&gt;
Das Modul gestattet es beliebige Verbraucher (Devices) über die Attribute &#039;&#039;&#039;consumerXX&#039;&#039;&#039; zu registrieren. Durch die Registrierung wird dem Modul der Namen des Devices sowie dessen Eingenschaften durch die Angabe von Schlüssel-Wert Paaren bekannt gemacht.&lt;br /&gt;
&lt;br /&gt;
Nach der Registrierung können die Verbraucher durch das Modul genutzt werden:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* es erfolgt eine Planung der Ein- und Ausschaltzeiten abhängig von der solaren Prognose in Bezug zu den Leistungsdaten des Verbrauchers sowie der anderen registrierten Consumer&lt;br /&gt;
* das Modul kann die Ein- und Ausschaltsteuerung der Consumer übernehmen (optional)&lt;br /&gt;
* die aktuellen Status (Verbrauchsdaten) werden in der Energiefußgrafik dargestellt&lt;br /&gt;
* das Modul lernt mit der Zeit das Verbrauchsverhalten der registrierten Verbraucher&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Um einen Verbaucher zu registrieren, wird ein freies &#039;&#039;&#039;consumerXX&#039;&#039;&#039; Attribut, zum Beispiel consumer01, gesetzt.&lt;br /&gt;
&lt;br /&gt;
Die Eigenschaften und das Schaltverhalten wird durch die Angabe der verfügbaren Schlüssel gesteuert. Die Möglichkeiten sind sehr umfangreich. Die nachfolgenden Abschnitte beschreiben die Verwendung am Beispiel oft vorkommender bzw. nachgefragter Sachverhalte.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Die Phasen der Verbrauchersteuerung ===&lt;br /&gt;
&lt;br /&gt;
Jeder Zyklus eines Verbrauchers durchläuft im Modul die folgenden Phasen:&lt;br /&gt;
&lt;br /&gt;
# &#039;&#039;&#039;Planung&#039;&#039;&#039; der Aktivzeiten des Verbrauchers, d.h. wann der Verbraucher geplant eingeschaltet sowie geplant ausgeschaltet wird und damit der Zyklus beendet wird.&lt;br /&gt;
# &#039;&#039;&#039;Start&#039;&#039;&#039; des Zyklus, d.h. Einschalten des Verbrauchers entsprechend der Planungsdaten sofern weitere, in den Consumer-Schlüsseln definierbare Rahmenbedingungen das Einschalten nicht verhindern (z.B. kein oder zu wenig PV-Energieerzeugung / PV-Überschuss)&lt;br /&gt;
# &#039;&#039;&#039;Permanente Überprüfung von&#039;&#039;&#039; eventuell vorhandenen &#039;&#039;&#039;Interruptbedingungen&#039;&#039;&#039; die den Verbraucher temporär ausschalten und, sofern die Interruptbedingung nicht mehr vorliegt, den Verbraucher wieder einschalten und so den gestarteten Zyklus fortsetzen.&lt;br /&gt;
#  &#039;&#039;&#039;Beendigung&#039;&#039;&#039; der Zyklus. Die Beendigung erfolgt entweder (normalerweise) wenn die geplante Endezeit der Zyklus erreicht ist oder eine optionale Endebedingung erfüllt ist.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Steuerung der Phasen ====&lt;br /&gt;
Der Ablauf der Phasen kann durch verschiedene Schlüssel-Wert Paare beeinflusst und gesteuert werden.&lt;br /&gt;
&lt;br /&gt;
Mit dem optionalen Schlüssel &#039;&#039;&#039;swoncond&#039;&#039;&#039; kann eine &#039;&#039;&#039;zusätzliche externe Bedingung&#039;&#039;&#039; definiert werden um den Einschaltvorgang des Consumers freizugeben. Ist die Bedingung (Regex) nicht erfüllt, erfolgt kein Einschalten des Verbrauchers auch wenn die sonstigen Voraussetzungen wie Zeitplanung, on-Schlüssel, auto-Mode und aktuelle PV-Leistung gegeben sind. Es erfolgt somit eine &#039;&#039;&#039;UND-Verknüpfung&#039;&#039;&#039; des Schlüssels swoncond mit den Planungsdaten und weiteren Einschaltbedingungen.&lt;br /&gt;
&lt;br /&gt;
Der optionale Schlüssel &#039;&#039;&#039;swoffcond&#039;&#039;&#039; definiert eine &#039;&#039;&#039;vorrangige Ausschaltbedingung&#039;&#039;&#039; (Regex). Sobald diese Bedingung erfüllt ist, wird der Consumer ausgeschaltet auch wenn die geplante Endezeit (consumerXX_planned_stop) noch nicht erreicht ist (&#039;&#039;&#039;ODER-Verknüpfung&#039;&#039;&#039;). Weitere Bedingungen wie off-Schlüssel und auto-Mode müssen zum automatischen Ausschalten erfüllt sein.&lt;br /&gt;
&lt;br /&gt;
Mit dem optionalen Schlüssel &#039;&#039;&#039;interruptable&#039;&#039;&#039; kann während der geplanten Einschaltzeit eine automatische Unterbrechung sowie Wiedereinschaltung des Verbrauchers vorgenommen werden. Der Verbraucher wird temporär ausgeschaltet (interrupted) und wieder eingeschaltet (continued) wenn die Interrupt-Bedingung nicht mehr vorliegt. Die verbleibende Laufzeit wird durch einen Interrupt nicht beeinflusst! &lt;br /&gt;
&lt;br /&gt;
Der Schlüssel &#039;&#039;&#039;power&#039;&#039;&#039; gibt die nominale Leistungsaufnahme des Verbrauchers gemäß seines Datenblattes an. Dieser Wert wird verwendet um die Schaltzeiten des Verbrauchers zu planen und das Schalten in Abhängigkeit des tatsächlichen PV-Überschusses zum Einplanungszeitpunkt zu steuern. Ist &#039;&#039;&#039;power=0&#039;&#039;&#039; gesetzt, wird der Verbraucher unabhängig von einem zum Zeitpunkt des Zyklus-Start eventuell nicht ausreichend vorhandenem PV-Überschuss wie geplant eingeschaltet und somit der Zyklus gestartet.&lt;br /&gt;
&lt;br /&gt;
Durch die Schlüssel &#039;&#039;&#039;notbefore&#039;&#039;&#039; und &#039;&#039;&#039;notafter&#039;&#039;&#039; kann der Startzeitpunkt des Verbrauchers so gesteuert werden, dass der Start des Zyklus nicht vor bzw. nach der angegebenen Zeit eingeplant wird. Diese Steuerung bezieht sich auf die &#039;&#039;Planung&#039;&#039;, d.h. der eigentliche Start des Verbrauchers kann sich unter Umständen durch vorhandene Rahmenbedingungen verzögern. &lt;br /&gt;
&lt;br /&gt;
Der Schlüssel &#039;&#039;&#039;mintime&#039;&#039;&#039; legt die geplanten Einschaltzeit, d.h. die Laufzeit des Zyklus fest. Der Name &#039;&#039;&#039;mintime&#039;&#039;&#039; ist nicht als MInimum-Zeit zu deuten, sondern beschreibt die angegebene Zeit in Minuten die der Zyklus dauern soll.&lt;br /&gt;
&lt;br /&gt;
Dieser Schlüssel ist optional, da sich die Standard-Laufzeit des Verbrauchers von dessen Typ abgeleitet wird. Diese Standard Laufzeiten sind:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|dryer&lt;br /&gt;
|90 Minten&lt;br /&gt;
|-&lt;br /&gt;
|dishwasher&lt;br /&gt;
|180 Minuten&lt;br /&gt;
|-&lt;br /&gt;
|washingmachine&lt;br /&gt;
|120 Minuten&lt;br /&gt;
|-&lt;br /&gt;
|heater&lt;br /&gt;
|240 Minuten&lt;br /&gt;
|-&lt;br /&gt;
|charger&lt;br /&gt;
|120 Minuten&lt;br /&gt;
|-&lt;br /&gt;
|other&lt;br /&gt;
|60 Minuten&lt;br /&gt;
|-&lt;br /&gt;
|heatpump&lt;br /&gt;
|Sonderfall&lt;br /&gt;
KI-Prognose (ab V2.0.0) beachten&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
=== Der Verbraucher wurde nicht gestartet obwohl die Startzeit der Einplanung erreicht wurde ===&lt;br /&gt;
&lt;br /&gt;
Wenn die Startzeit des Consumers entsprechend der Einplanung erreicht oder überschritten ist, wird der Consumer eingeschaltet wenn die weiteren Voraussetzungen wahr sind:&lt;br /&gt;
&lt;br /&gt;
* es ist aktuell ein PV-Überschuss vorhanden und der PV-Überschuss deckt die kalkulierten Leistungsaufnahme des Verbrauchers ab. Mit den Schlüsseln &#039;&#039;&#039;power&#039;&#039;&#039; und &#039;&#039;&#039;spignorecond&#039;&#039;&#039; kann die Einschaltung trotz fehlendem PV-Überschuss ermöglicht werden. &lt;br /&gt;
&lt;br /&gt;
* die Angabe im Schlüssel &#039;&#039;&#039;swoncond&#039;&#039;&#039; ist wahr (sofern gesetzt)&lt;br /&gt;
&lt;br /&gt;
* das Schalten ist über den Schlüssel &#039;&#039;&#039;auto&#039;&#039;&#039; freigegeben (sofern gesetzt)&lt;br /&gt;
&lt;br /&gt;
* im Schlüssel &#039;&#039;&#039;on&#039;&#039;&#039; ist ein valides Einschaltkommando hinterlegt&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Sind diese zusätzlichen Bedingungen nicht erfüllt, erfolgt kein Start des eingeplanten Zyklus. Der Verbraucher verbleibt in diesem Fall weiterhin im Status &#039;&#039;planned&#039;&#039; oder &#039;&#039;suspended&#039;&#039;. Die Startbedingungen werden regelmäßig reviewed und der Consumer gestartet sobald alle Startbedingungen wahr sind bzw. Start verhindernde Bedingungen entfallen sind. &lt;br /&gt;
&lt;br /&gt;
Dieser Vorgang erfolgt bis das geplante Ende des Verbaucherzyklus erreicht ist oder die Bedingung im Schlüssel &#039;&#039;&#039;swoffcond&#039;&#039;&#039; zutrifft. Sobald die Bedingung im Schlüssel &#039;&#039;&#039;swoffcond&#039;&#039;&#039; wahr ist, wird der gesamte Planungszyklus des Consumers beendet.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
=== Kann manuell / automatisiert in den Planungszyklus eines Verbrauchers eingegriffen werden? ===&lt;br /&gt;
&lt;br /&gt;
Die Einplanung und die darauf aufbauenden Schaltprozesse laufen gemäß den hinterlegten Schlüsseloptionen im Modul automatatisch ab ohne dass ein Eingriff durch den Anwender erfolgen muß.&lt;br /&gt;
&lt;br /&gt;
Dennoch kann es gewünscht sein eine Neuplanung oder eine Sofortplanung eines Consumers vorzunehmen weil der Bedarf dazu besteht. Zu diesem Zweck stehen die Set-Kommandos&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* consumerNewPlanning &lt;br /&gt;
&lt;br /&gt;
* consumerImmediatePlanning &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
zur Verfügung. &lt;br /&gt;
Beide Befehle initiieren die Consumer-Neuplanung, wobei mit dem Befehl &#039;&#039;consumerImmediatePlanning&#039;&#039; die eventuell im consumerXX Attribut gesetzten Schlüssel notbefore, notafter bzw. mode werden nicht beachtet werden. Dagenen beachtet der Befehl &#039;&#039;consumerNewPlanning&#039;&#039; alle definierten Schlüsseloptionen und dient gewöhnlich dazu einen abgeschlossenen Verbraucherzyklus wiederholt ausführen zu lassen weil die aktuelle Witterungslage dazu geeignet ist. &lt;br /&gt;
&lt;br /&gt;
Ein Anwendungsbeispiel dafür ist zum Beispiel die Ausführung von mehreren Waschmaschinen-Zyklen an einem langen, sonnigen Sommertag.&lt;br /&gt;
Diese beiden Set-Befehle können zum Beispiel über ein auf einen Schalter/Taster reagierendes Notify oder DOIF Device ausgeführt werden. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Registrierung eines Verbrauchers mit getrennten Geräten für Messung und Schalten ===&lt;br /&gt;
&lt;br /&gt;
Es existieren in FHEM Geräte, die über verschiedene Kanäle für unterschiedliche Aufgaben verfügen. Diese Kanäle werden jeweils in einem gesonderten Device abgebildet. HomeMatic oder readingsProxy sind Beispiele für solche Varianten.&lt;br /&gt;
&lt;br /&gt;
Um solche Verbraucher einzubinden gibt es im consumerXX Attribut den Schlüssel &#039;&#039;&#039;switchdev&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Bei der Registrierung gibt man als Consumerdevice das Gerät für die Messung an und im Schüssel switchdev das entsprechende FHEM-Device, welches für die Schaltvorgänge benutzt wird. &lt;br /&gt;
Ist switchdev angegeben, beziehen sich die weiteren Schlüssel on, off, swstate, auto und asynchron auf dieses Gerät.&lt;br /&gt;
&lt;br /&gt;
In dem nachfolgenden Beispiel (Homematic) ist &#039;&#039;eg.az.fridge_Pwr&#039;&#039; FHEM Device für die Energiemessung. Die Schlüssel &#039;&#039;pcurr&#039;&#039; und &#039;&#039;etotal&#039;&#039; geben Readings in diesem Device an damit SolarForecast den Verbrauch und die Energiemengen auslesen kann. Demgegenüber ist im Schlüssel &#039;&#039;switchdev&#039;&#039; das zugehörige Schalter-Device der Kombination angegeben.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
eg.az.fridge_Pwr&lt;br /&gt;
type=noSchedule switchdev=eg.az.fridge_Sw power=0 icon=fridge pcurr=power:W:5 etotal=energyCalc:Wh &lt;br /&gt;
swstate=state:on:off auto=automatic&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Registrierung von Verbrauchern ohne Zeitplanung und aktivem Schalten ===&lt;br /&gt;
Manchmal besteht der Wunsch, Verbraucher in SolarForecast zu integrieren um lediglich folgende Sachverhalte abzubilden:&lt;br /&gt;
&lt;br /&gt;
* Visualisierung des Schaltstatus in der Consumer Legende und/oder der Energieflußgrafik&lt;br /&gt;
* Darstellung des aktuellen Energieverbrauchs in der Energieflußgrafik&lt;br /&gt;
* Erfassung der (externen) Schaltzeiten, d.h. der Zeiten der Gerätenutzung sowie der Verbrauchsdaten um deren Einfluß bei den Planungszeiten anderer Geräte durch SolarForecast berücksichtigen zu lassen&lt;br /&gt;
&lt;br /&gt;
In solchen Fällen werden bei der Registrierung die Schlüssel &#039;&#039;swstate, pcurr&#039;&#039; und &#039;&#039;etotal&#039;&#039; zur Erfassung der Schaltzustände und der Energieverbrauchs angegeben, jedoch zur Verhinderung der Zeitplanung durch SolarForecast der Schlüssel &#039;&#039;&#039;type=noSchedule&#039;&#039;&#039; gesetzt sowie die Schlüssel &#039;&#039;on&#039;&#039; und &#039;&#039;off&#039;&#039; &#039;&#039;&#039;nicht&#039;&#039;&#039; gesetzt.&lt;br /&gt;
&lt;br /&gt;
Das nachfolgende Beispiel zeigt eine mögliche Registrierung eines Zwischensteckers (Shelly) zur Erfassung der Daten eines Gefrierschrankes:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Shelly.shellyplug4&lt;br /&gt;
type=noSchedule power=65 asynchron=1&lt;br /&gt;
icon=gefrierschrank &lt;br /&gt;
swstate=state:.*on.*:.*off.* &lt;br /&gt;
pcurr=relay_0_power:W:5 etotal=relay_0_energy_Wh:Wh&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Beispiel Sonderregistrierung (ab V2.0.0) einer Wärmepumpe ===&lt;br /&gt;
&lt;br /&gt;
Das nachfolgende Beispiel zeigt die besondere Möglichkeit zur Registrierung (ab V2.0.0.) einer Wärmepumpe - aber nur wenn &#039;&#039;&#039;aiConActivate=1&#039;&#039;&#039; =&amp;gt; KI-Consumerprognose &amp;lt;== gesetzt und genutzt wird!&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
EM_Waermepumpe &lt;br /&gt;
type=heatpump&lt;br /&gt;
power=3183&lt;br /&gt;
icon=sani_heating_heatpump@orange &lt;br /&gt;
pcurr=Bezug_Wirkleistung:W&lt;br /&gt;
etotal=Bezug_Wirkleistung_Zaehler:kWh&lt;br /&gt;
noshow=0&lt;br /&gt;
switchdev=MQTT_EMSwp   ### extra wg. swstate Reading auf MQTT_EMSwp&lt;br /&gt;
swstate=hpcompon:1:off&lt;br /&gt;
comforttemp=20&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Erläuterungen zu diesen &#039;besondern&#039; Einträgen&lt;br /&gt;
&lt;br /&gt;
Eintrag&#039;&#039;&#039; type=heatpump &#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Wenn das gemacht wird, aktiviert sich ein Feature-Set für Wärmepumpen. Dadurch ändern sich die Strukturen und das &#039;runConTrain&#039; muß erneuert gestartet werden. &lt;br /&gt;
&lt;br /&gt;
In dem Status des Consumer-KI-Status-Popup ist auch erkennbar ob die Wärmepumpe erkannt wird und welches Featureset verwendet wird. Es sollte aber dann z.B. beim &#039;&#039;&amp;lt;nowiki/&amp;gt;&#039;attr aiControl aiConProfile=v1_heatpump_active_pv&#039;&#039;&#039; gewählt und auch angezeigt werden.&lt;br /&gt;
&lt;br /&gt;
Eintrag&#039;&#039;&#039; power=3183&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Maximale Leistungsaufnahme der Wärmepumpe in W. Der Wert darf nicht! 0 sein. Die Angabe ist verpflichtend.&lt;br /&gt;
&lt;br /&gt;
Eintrag&#039;&#039;&#039; comforttemp=20&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Solltemperatur (Komforttemperatur) in den Wohnräumen in °C. Die Angabe ist verpflichtend.&lt;br /&gt;
&lt;br /&gt;
Eintrag&#039;&#039;&#039; Bezug_Wirkleistung_Zaehler:kWh&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Reading:Einheit (Wh/kWh) des Consumer Device, welches die Summe der verbrauchten Energie liefert. Die Angabe ist verpflichtend.&lt;br /&gt;
&lt;br /&gt;
Eintrag&#039;&#039;&#039;  swstate=hpcompon:1:off&#039;&#039;&#039;  &lt;br /&gt;
&lt;br /&gt;
in diesem Beispiel : Wert 1 = WP-Kompressor aktiv, Wert off= WP-Kompressor aus&lt;br /&gt;
&lt;br /&gt;
Reading:ON:OFF welches den Status der Consumers liefert. Die Angabe bei &#039;&#039;type=heatpump&#039;&#039; ist verpflichtend.&lt;br /&gt;
&lt;br /&gt;
Abweichend von anderen Consumern ist die Angabe verpflichtend, auch wenn der default verwendet werden soll. Durch Erstellung eines passenden userReadings (WP-komplett / WP-Heizen / WP-Kühlen / WP-WW-Aufbereitung etc.) kann damit gesteuert werden, ob man sowohl Laufzeiten für Heiz- und Kühlbetrieb, Warmwassererzeugung und Heizstabbetrieb (in einem Consumer) zusammenfassen will, oder ob man z.B. ausschließlich nur die Laufzeiten des Heiz- und Kühlbetriebs als Zeiten für die Heizung in dem jeweiligen consumerXX separieren möchte.&lt;br /&gt;
&lt;br /&gt;
Eintrag&#039;&#039;&#039;  pcurr=Bezug_Wirkleistung:W&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Reading:Einheit (W/kW) welches den aktuellen Energieverbrauch liefert. Die Angabe ist verpflichtend.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Beispiel Registrierung eines Shelly Devices ===&lt;br /&gt;
&lt;br /&gt;
Nachfolgendes Beispiel zeigt eine Registrierung eines Heizlüfters der an einem Shelly Zwischenstecker angeschlossen ist und durch das Modul gesteuert werden soll. Dabei soll die Steuerung nicht nur von der Solarprognose, sondern auch von der Raumtemperatur abhängig erfolgen. Im Beispiel wird das Attribut consumer03 verwendet.&lt;br /&gt;
&lt;br /&gt;
Zwingende Angaben sind&lt;br /&gt;
&lt;br /&gt;
 consumerXX &amp;lt;Device Name&amp;gt; type=&amp;lt;type&amp;gt; power=&amp;lt;power&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Alle Angaben können mehrzeilig eingegeben werden. Die Schlüssel-Werte Paare sind jeweils durch ein Leerzeichen getrennt.&lt;br /&gt;
&lt;br /&gt;
 consumerXX Shelly.shellyplug3 type=heater power=1700 &lt;br /&gt;
&lt;br /&gt;
In dem Beispiel ist Shelly.shellyplug3 der Devicename des Shellies in FHEM. Der Schlüssel &#039;&#039;&#039;type&#039;&#039;&#039; definiert die Art des Verbrauchers. In der Hilfe sind die möglichen Typen aufgeführt. Den richtigen Typ anzugeben hat Einfluß auf die spätere Einschätzung des Leistungsverhaltens über die Laufzeit. So wird von einem &#039;&#039;&#039;heater&#039;&#039;&#039; gleich nach dem Einschalten die angegebene Nominalleistung abgerufen und wird über die Zeit gleichbleiben. &lt;br /&gt;
Eine Waschmaschine oder ein Trockner rufen über ihre Laufzeit die Leistung nicht gleichbleibend ab und ändern die Leistungsaufnahme sich über die Einschaltdauer.&lt;br /&gt;
&lt;br /&gt;
Die Angabe von &#039;&#039;&#039;power&#039;&#039;&#039; definiert die Nominalleistung (Watt) die für den Verbraucher laut Typenschild vom Hersteller angegeben wird. Diese Angabe wird vom Modul bei der Planung der Einschaltzeiten verwendet indem die Nominalleistung in das Verhältnis zur Solarprognose bzw. Verbrauchsprognose des Netzes gesetzt wird. Weiterhin ist dieser Wert auch wichtig um später den tatsächlichen Einschaltzeitpunkt auszuführen wenn ein realer PV Überschuss festgestellt wird.&lt;br /&gt;
&lt;br /&gt;
Es ist möglich &#039;&#039;&#039;power=0&#039;&#039;&#039; zu setzen. Das führt dazu, dass die Planung und letztendlich auch der Schaltvorgang unabhängig von der Solarprognose bzw. einem realen PV Überschuss vorgenommen wird.  &lt;br /&gt;
&lt;br /&gt;
Es werden weitere Schlüsseleingaben vorgenommen:&lt;br /&gt;
&lt;br /&gt;
 Shelly.shellyplug3 type=heater power=1700 icon=vent_ventilation mode=can notbefore=09 mintime=SunPath:60:-60 on=on off=off&lt;br /&gt;
&lt;br /&gt;
Mit dem Schlüssel &#039;&#039;&#039;icon&#039;&#039;&#039; legt man ein Icon fest welches in der Grafik für den Verbraucher verwendet wird. Der &#039;&#039;&#039;mode&#039;&#039;&#039; definiert die Art und Weise der Einplanung:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
;can: Die Einplanung erfolgt zum Zeitpunkt mit wahrscheinlich genügend verfügbaren PV Überschuss. Der Start des Verbrauchers zum Planungszeitpunkt unterbleibt bei ungenügendem PV-Überschuss.&lt;br /&gt;
&lt;br /&gt;
;must: Der Verbaucher wird optimiert eingeplant auch wenn wahrscheinlich nicht genügend PV Überschuss vorhanden sein wird. Der Start des Verbrauchers erfolgt auch bei ungenügendem PV-Überschuss.  &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Mit &#039;&#039;&#039;notbefore&#039;&#039;&#039; wird festgelegt, dass die Einplanung nicht vor neun Uhr morgens erfolgen soll auch falls schon genügend PV Überschuss vorhanden wäre. Der Schlüssel &#039;&#039;&#039;mintime&#039;&#039;&#039; definiert die Einplanungsdauer des Verbrauchers in Minuten im einfachsten Fall. Die hier verwendete Angabe &#039;&#039;&#039;SunPath&#039;&#039;&#039; ist ein Spezialfall. Der Verbraucher soll von Sonnenaufgang bis Sonnenuntergang eingeschaltet werden, wobei die Angabe von 60 bzw. -60 ein relative Verschiebung bewirken. Dadurch wird das Einschalten 60 Mintuen nach Sonnenaufgang bis 60 Minuten vor Sonnenuntergang geplant.&lt;br /&gt;
&lt;br /&gt;
Die Schlüssel &#039;&#039;&#039;on&#039;&#039;&#039; und &#039;&#039;&#039;off&#039;&#039;&#039; teilen dem Modul die jeweiligen gültigen Ein- und Aus-Kommandos mit, mit dem das (Shelly)Device geschaltet werden kann. Werden diese Schlüssel nicht oder &amp;quot;leer&amp;quot; (on= off=) angegeben, erfolgt kein Schalten durch das Modul, nur die Planungsdaten werden erzeugt.&lt;br /&gt;
&lt;br /&gt;
Die Verbraucherregistrierung wird mit weiteren Angaben ergänzt um das gewünschte Verhalten zu erreichen:&lt;br /&gt;
&lt;br /&gt;
 Shelly.shellyplug3 type=heater power=1700 icon=vent_ventilation mode=can notbefore=09 mintime=SunPath:60:-60 on=on off=off etotal=relay_0_energy_Wh:Wh  &lt;br /&gt;
 pcurr=relay_0_power:W auto=automatic interruptable=og.bad.wandthermostat:diff-temp:[0-9]\.[0-9]:0.2&lt;br /&gt;
&lt;br /&gt;
In &#039;&#039;&#039;etotal&#039;&#039;&#039; wird der Readingname des Shelly Devices angegeben welches die Summe der verbrauchten Energie (Wh/kWh) des Consumer Device liefert. D.h. es muß ein sich stetig erhöhender Wert sein. Durch die Auswertung dieses Readings ermittelt das Modul die in bestimmten Zeiteinheiten verbrauchte Energie zur weiteren Verwendung. &lt;br /&gt;
&lt;br /&gt;
In dem Shelly Device ist per default ein solches Reading nicht vorhanden. Über userReadings kann in dem Device Shelly.shellyplug3 ein Reading für etotal erzeugt werden:&lt;br /&gt;
&lt;br /&gt;
 userReadings relay_0_energy_Wh:relay_0_energy.* monotonic { sprintf &amp;quot;%.0f&amp;quot;, ReadingsVal ($name, &#039;relay_0_energy&#039;, 0) / 60 } &lt;br /&gt;
&lt;br /&gt;
Der Schlüssel &#039;&#039;&#039;pcurr&#039;&#039;&#039; enthält das Reading in Shelly.shellyplug3 welches den aktuellen Energieverbrauch liefert. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;auto&#039;&#039;&#039; enthält das Reading in Shelly.shellyplug3 welches zur Freigabe/Sperrung der autmatischen Schaltung durch das Modul dienen soll. Ist das angegebene Reading (im Beispiel &amp;quot;automatic&amp;quot;) im Shelly.shellyplug3 nicht vorhanden, wird es vom Modul automatisch mit dem Wert &amp;quot;1&amp;quot; angelegt.&lt;br /&gt;
Dadurch ist per default das automatische Schalten von Shelly.shellyplug3 durch das Modul freigegeben. &lt;br /&gt;
Der User kann durch Setzen des Readings &#039;&#039;&#039;automatic=0&#039;&#039;&#039; das automatische Schalten durch das Modul sperren und mit &amp;quot;1&amp;quot; wieder freigeben. Dadurch kann man zu bestimmten Zeiten (Urlaub, Feiertage, etc.) die Schaltung temporär deaktivieren.&lt;br /&gt;
&lt;br /&gt;
Wie oben beschrieben, soll der Heizlüfter als weitere Schaltbedingung die Abhängigkeit von der Raumtemperatur beachten. Konkret soll der Heizlüfter mit einer Hysterese von 0.2 (Grad) bei Erreichen einer Soll-Raumtemperatur ausschalten und bei Unterschreiten einer bestimmten Temperatur einschalten.&lt;br /&gt;
Der Schlüssel &#039;&#039;&#039;interruptable&#039;&#039;&#039; übernimmt diese temporäre Schaltsequenzen.&lt;br /&gt;
&lt;br /&gt;
Zunächst wird in dem Sensordevice (In dem Beispiel ein Homatic Wandthermostat HM-TC-IT-WM-W-EU) ein Reading erstellt welches dann im Schlüssel &#039;&#039;&#039;interruptable&#039;&#039;&#039; angegeben wird. Dieses Reading soll einen Wert enthalten auf den der angegebene Regex ([0-9]\.[0-9]) matchen soll um Shelly.shellyplug3 temporär auszuschalten.&lt;br /&gt;
Im Wandthermostat wird dazu ein userReading angelegt:&lt;br /&gt;
&lt;br /&gt;
 userReadings diff-temp:desired-temp.* { &lt;br /&gt;
     sprintf &amp;quot;%.1f&amp;quot;, ReadingsVal ($name, &#039;measured-temp&#039;, 0) - ReadingsVal ($name, &#039;desired-temp&#039;, 0) &lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Das bedeutet, wenn die Raumtemperatur die Solltemperatur erreicht oder höher ist, wird diff-temp &amp;gt;= 0.&lt;br /&gt;
In diesem Fall matcht der angebene Regex in:&lt;br /&gt;
&lt;br /&gt;
 interruptable=og.bad.wandthermostat:diff-temp:&#039;&#039;&#039;[0-9]\.[0-9]&#039;&#039;&#039;:0.2&lt;br /&gt;
&lt;br /&gt;
und Shelly.shellyplug3 wird ausgeschaltet. &lt;br /&gt;
Unterschreitet der Wert von diff-temp 0, matcht der Regex nicht mehr und der Verbraucher wird wieder eingeschaltet. Dabei wird die angegebene Hysterese berücksichtigt, d.h der Verbraucher wird erst ausgeschaltet wenn &amp;quot;diff-temp - 0.2 &amp;gt;= 0&amp;quot; wahr ist.&lt;br /&gt;
&lt;br /&gt;
Für das Wiedereinschalten des Heizlüfters ist außerdem Voraussetzung, dass ein entsprechender PV-Überschuss vorliegt. Diese Bedingung wird durch die Angabe von &#039;&#039;&#039;power=1700&#039;&#039;&#039; bewirkt. Soll der zwangsweise PV Überschuss ignoriert werden, kann &#039;&#039;&#039;power=0&#039;&#039;&#039; angegeben werden. Alternativ kann die Berücksichtigung des zwangsweisen PV Überschuss mit dem Schlüssel &amp;quot;spignorecond&amp;quot; im Consumer-Attribut ausgesteuert werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== zusätzliche Readings bzw. Erzeugung von statistischen Readings ==&lt;br /&gt;
&lt;br /&gt;
Zusätzlich zu den zahlreichen im Modul per Standard erzeugten Readings kann der Anwender weitere Readings generieren lassen.&lt;br /&gt;
Diese zusätzlichen Readings geben entweder einen bestimmten Sachverhalt (Laufzeit, Status der API-Abfragen, etc.) oder einen statistischen Wert, z.B. die Batterieentladung des aktuellen Tages, wieder.&lt;br /&gt;
&lt;br /&gt;
Die zusätzlich erstellten Readings sind durch den Präfix &#039;&#039;&#039;special_&#039;&#039;&#039; im Device gekennzeichnet.&lt;br /&gt;
&lt;br /&gt;
Diese zusätzlichen Informationen können durch das Setzen einer oder mehrerer Kennzeichen im Attribut &#039;&#039;&#039;ctrlSpecialReadings&#039;&#039;&#039; erzeugt werden. Die mögliche Auswahl wird je nach Bedarf ausgebaut und umfasst zum aktuellen Zeitpunkt (07.02.2025) diese Möglichkeiten:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
;BatPowerIn_Sum: 	die Summe der momentanen Batterieladeleistung aller definierten Batterie Geräte&lt;br /&gt;
;BatPowerOut_Sum: 	die Summe der momentanen Batterieentladeleistung aller definierten Batterie Geräte&lt;br /&gt;
;allStringsFullfilled: 	Erfüllungsstatus der fehlerfreien Generierung aller Strings&lt;br /&gt;
;conForecastTillNextSunrise: 	Verbrauchsprognose von aktueller Stunde bis zum kommenden Sonnenaufgang&lt;br /&gt;
;currentAPIinterval: 	das aktuelle Abrufintervall der gewählten Strahlungsdaten-API in Sekunden&lt;br /&gt;
;currentRunMtsConsumer_XX: 	die Laufzeit (Minuten) des Verbrauchers &amp;quot;XX&amp;quot; seit dem letzten Einschalten. (letzter Laufzyklus)&lt;br /&gt;
;dayAfterTomorrowPVforecast: 	liefert die Vorhersage der PV Erzeugung für Übermorgen (sofern verfügbar) ohne Autokorrektur (Rohdaten).&lt;br /&gt;
;daysUntilBatteryCare_XX: 	Tage bis zur nächsten Batterie XX Pflege (Erreichen der Ladung &#039;maxSoC&#039; aus Attribut ctrlBatSocManagementXX)&lt;br /&gt;
;lastretrieval_time: 	der letzte Abrufzeitpunkt der gewählten Strahlungsdaten-API&lt;br /&gt;
;lastretrieval_timestamp: 	der Timestamp der letzen Abrufzeitpunkt der gewählten Strahlungsdaten-API&lt;br /&gt;
;response_message: 	die letzte Statusmeldung der gewählten Strahlungsdaten-API&lt;br /&gt;
;runTimeAvgDayConsumer_XX: 	die durchschnittliche Laufzeit (Minuten) des Verbrauchers &amp;quot;XX&amp;quot; an einem Tag&lt;br /&gt;
;runTimeCentralTask: 	die Laufzeit des letzten SolarForecast Intervalls (Gesamtprozess) in Sekunden&lt;br /&gt;
;runTimeTrainAI: 	die Laufzeit des letzten KI Trainingszyklus in Sekunden&lt;br /&gt;
;runTimeLastAPIAnswer: 	die letzte Antwortzeit des Strahlungsdaten-API Abrufs auf einen Request in Sekunden&lt;br /&gt;
;runTimeLastAPIProc: 	die letzte Prozesszeit zur Verarbeitung der empfangenen Strahlungsdaten-API Daten&lt;br /&gt;
;SunMinutes_Remain: 	die verbleibenden Minuten bis Sonnenuntergang des aktuellen Tages&lt;br /&gt;
;SunHours_Remain: 	die verbleibenden Stunden bis Sonnenuntergang des aktuellen Tages&lt;br /&gt;
;todayConsumptionForecast: 	Verbrauchsprognose pro Stunde des aktuellen Tages (01-24)&lt;br /&gt;
;todayConForecastTillSunset: 	Verbrauchsprognose von aktueller Stunde bis Stunde vor Sonnenuntergang&lt;br /&gt;
;todayDoneAPIcalls: 	die Anzahl der am aktuellen Tag ausgeführten Strahlungsdaten-API Calls&lt;br /&gt;
;todayDoneAPIrequests: 	die Anzahl der am aktuellen Tag ausgeführten Strahlungsdaten-API Requests&lt;br /&gt;
;todayConsumption: 	der gesamte Energieverbrauch (Wh) des Hauses am aktuellen Tag&lt;br /&gt;
;todayGridConsumption: 	die aus dem öffentlichen Netz bezogene Energie am aktuellen Tag&lt;br /&gt;
;todayGridFeedIn: 	die in das öffentliche Netz eingespeiste PV Energie am aktuellen Tag&lt;br /&gt;
;todayMaxAPIcalls: 	die maximal mögliche Anzahl Strahlungsdaten-API Calls. Ein Call kann mehrere API Requests enthalten.&lt;br /&gt;
;todayRemainingAPIcalls: 	die Anzahl der am aktuellen Tag noch möglichen Strahlungsdaten-API Calls&lt;br /&gt;
;todayRemainingAPIrequests: 	die Anzahl der am aktuellen Tag noch möglichen Strahlungsdaten-API Requests&lt;br /&gt;
;todayBatIn_XX: 	die am aktuellen Tag in die Batterie XX geladene Energie&lt;br /&gt;
;todayBatInSum: 	Summe der am aktuellen Tag in alle Batterien geladene Energie&lt;br /&gt;
;todayBatOut_XX: 	die am aktuellen Tag aus der Batterie XX entnommene Energie&lt;br /&gt;
;todayBatOutSum: 	Summe der am aktuellen Tag aus allen Batterien entnommene Energie&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Wichtig:&#039;&#039;&#039; Manche Angaben sind im Setup des Moduls optional, d.h. sie müssen nicht zwingend beim Setup angegeben werden. Allerdings sind diese Angaben für die Erstellung eines zusätzlichen Readings unter Umständen Voraussetzung. Es empfiehlt sich deshalb bei Setup so viele Informationen wie möglich zu übermitteln.&lt;br /&gt;
&lt;br /&gt;
So ist zum Beispiel für die sinnvolle Erstellung der Informationen &#039;&#039;&#039;todayBatInSum&#039;&#039;&#039; und &#039;&#039;&#039;todayBatOutSum&#039;&#039;&#039; (Readings special_todayBatInSum und special_todayBatOutSum) das Vorhandensein der Schlüssel &#039;&#039;intotal&#039;&#039; und &#039;&#039;outtotal&#039;&#039; im Attribut setupBatteryDevXX notwendig (Beispiel):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
attr &amp;lt;Name&amp;gt; setupBatteryDev01 MQTT2_cerboGX_c0619ab34e08_battery &lt;br /&gt;
                              pin=BatIn:W pout=BatOut:W charge=SOC_value intotal=BatInTotal:Wh outtotal=BatOutTotal:Wh &lt;br /&gt;
                              cap=InstalledCapacity_Wh:Wh asynchron=1 show=1&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Hinweise zur Fehlersuche ==&lt;br /&gt;
Das Modul SolarForecast ist sehr komplex und es die verschiedensten Datenquellen ausgewertet, zusammengeführt und daraus Ergebnisse berechnet und/oder visualisiert. Wird etwas nicht wie erwartet durch das Modul geliefert, können die Ursachen vielfältig sein. Natürlich kann ein Fehler im Modulcode vorliegen, oftmals gibt es aber andere Ursachen.&lt;br /&gt;
&lt;br /&gt;
Die nachfolgenden Hinweise sollen Hilfe zur Selbsthilfe geben und sind eine Zusammenfassung von realen Fällen die mit dem beschriebenen Wegen und Verfahren gelöst wurden.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Die erwartete erzeugte PV-Energie ist im Modul zu gering und wird im Balkendiagramm nicht angezeigt === &lt;br /&gt;
&lt;br /&gt;
Die erzeugte PV-Energie wird nicht richtig angezeigt: Am Vormittag sind keine Werte vorhanden und, wenn dann etwas kommt, ist die Anzeige zu gering. Für 13 und 14 Uhr wurden ca. 5.000Wh erwartet (im Vergleich mit den Daten aus einer anderen Auswertungsquelle). &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Lösungsweg:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Aus dem List des Devices wurden die für den Fall wesentlichen Readings herausgezogen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    2025-02-02 08:59:55  Today_Hour09_PVforecast 152 Wh&lt;br /&gt;
    2025-02-02 08:59:55  Today_Hour09_PVreal 0 Wh&lt;br /&gt;
&lt;br /&gt;
    2025-02-02 09:59:55  Today_Hour10_PVforecast 1400 Wh&lt;br /&gt;
    2025-02-02 09:59:55  Today_Hour10_PVreal 0 Wh&lt;br /&gt;
&lt;br /&gt;
    2025-02-02 10:59:55  Today_Hour11_PVforecast 2500 Wh&lt;br /&gt;
    2025-02-02 10:59:55  Today_Hour11_PVreal 0 Wh&lt;br /&gt;
&lt;br /&gt;
    2025-02-02 11:59:55  Today_Hour12_PVforecast 4392 Wh&lt;br /&gt;
    2025-02-02 11:59:55  Today_Hour12_PVreal 0 Wh&lt;br /&gt;
&lt;br /&gt;
    2025-02-02 12:59:54  Today_Hour13_PVforecast 4000 Wh&lt;br /&gt;
    2025-02-02 12:59:54  Today_Hour13_PVreal 300 Wh&lt;br /&gt;
&lt;br /&gt;
    2025-02-02 13:59:54  Today_Hour14_PVforecast 6415 Wh&lt;br /&gt;
    2025-02-02 13:59:54  Today_Hour14_PVreal 3200 Wh&lt;br /&gt;
&lt;br /&gt;
    2025-02-02 14:59:54  Today_Hour15_PVforecast 5629 Wh&lt;br /&gt;
    2025-02-02 14:59:54  Today_Hour15_PVreal 2700 Wh&lt;br /&gt;
&lt;br /&gt;
    2025-02-02 15:59:54  Today_Hour16_PVforecast 3958 Wh&lt;br /&gt;
    2025-02-02 15:59:54  Today_Hour16_PVreal 900 Wh&lt;br /&gt;
&lt;br /&gt;
    2025-02-02 16:31:24  Today_Hour17_PVforecast 945 Wh&lt;br /&gt;
    2025-02-02 16:31:24  Today_Hour17_PVreal 500 Wh&lt;br /&gt;
   &lt;br /&gt;
    2025-02-02 16:31:24  Today_Hour18_PVforecast 59 Wh&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Man sieht dass bis 12:00 keine erzeugte Energie vom WR gemeldet wird. Deswegen wird in der Balkengrafik nichts dargestellt. Auch danach bleibt die registrierte PV-Energie hinter den Erwartungen zurück. Zuständig für die Lieferung der PV-Energie an das Modul ist das Reading im Schlüssel:&lt;br /&gt;
&lt;br /&gt;
 setupInverterDev01 SH10rt pv=Total_DC_Power:W &amp;lt;mark&amp;gt;etotal=Total_Export_Energy_from_PV:kWh&amp;lt;/mark&amp;gt; capacity=10000 strings=Hauptdach,Flachdach&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Welche Änderungen es zwischen den Stunden gab, sieht man mit einem &amp;quot;get ... pvHistory XX&amp;quot; (&amp;quot;XX&amp;quot; steht für das Datum, d.h. den Tag, z.B. 02). &amp;lt;br&amp;gt;&lt;br /&gt;
Hier ein Beispiel für die Stunden 10 (9:00 - 9:59) und 11 (10:00 - 10:59). &amp;lt;br&amp;gt;&lt;br /&gt;
Wichtig ist hier der Key etotali01 für den ersten Inverter. Die Differenz zwischen den Werten beider Stunden ist die in der Stunde erzeugte Energie (hier 380 Wh). Diese 380 Wh findest du auch im Key pvrl01 als PVReal für diese Stunde. Das Modul rechnet intern immer mit Wh, kWh werden vorher in Wh umgerechnet.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
10 =&amp;gt; pvfc: 365, pvrl: 530, pvrlvd: 1, rad1h: -&lt;br /&gt;
&lt;br /&gt;
            &amp;lt;mark&amp;gt;etotali01: 63364714&amp;lt;/mark&amp;gt;, etotali02: 3044110, etotali03: -&lt;br /&gt;
&lt;br /&gt;
            &amp;lt;mark&amp;gt;pvrl01: 380&amp;lt;/mark&amp;gt;, pvrl02: 150, pvrl03: -&lt;br /&gt;
&lt;br /&gt;
            etotalp01: -, etotalp02: -, etotalp03: -&lt;br /&gt;
&lt;br /&gt;
            pprl01: -, pprl02: -, pprl03: -&lt;br /&gt;
&lt;br /&gt;
            confc: 574, con: 518, gcons: 22, conprice: 0.2958&lt;br /&gt;
&lt;br /&gt;
            gfeedin: 1, feedprice: 0.1269&lt;br /&gt;
&lt;br /&gt;
            DoN: 1, sunaz: 137, sunalt: 12&lt;br /&gt;
&lt;br /&gt;
....&lt;br /&gt;
&lt;br /&gt;
            &lt;br /&gt;
&lt;br /&gt;
      11 =&amp;gt; pvfc: 2966, pvrl: 1862, pvrlvd: 1, rad1h: -&lt;br /&gt;
&lt;br /&gt;
            &amp;lt;mark&amp;gt;etotali01: 63365094&amp;lt;/mark&amp;gt;, etotali02: 3044260, etotali03: -&lt;br /&gt;
&lt;br /&gt;
            pvrl01: 1392, pvrl02: 470, pvrl03: -&lt;br /&gt;
&lt;br /&gt;
            etotalp01: -, etotalp02: -, etotalp03: -&lt;br /&gt;
&lt;br /&gt;
            pprl01: -, pprl02: -, pprl03: -&lt;br /&gt;
&lt;br /&gt;
            confc: 774, con: 609, gcons: 25, conprice: 0.2958&lt;br /&gt;
&lt;br /&gt;
            gfeedin: 4, feedprice: 0.1269&lt;br /&gt;
           &lt;br /&gt;
....&lt;br /&gt;
&lt;br /&gt;
D.h. wenn zwischen den beiden Stunden keine Differenzen zu finden sind, liegt es sehr wahrscheinlich am Input des Readings:&lt;br /&gt;
&lt;br /&gt;
 etotal=Total_Export_Energy_from_PV:kWh&lt;br /&gt;
&lt;br /&gt;
welches sich in den ersten Stunden nicht ändert und danach auch nur wenig.&lt;br /&gt;
&lt;br /&gt;
Man kann mit dem Attribut:&lt;br /&gt;
&lt;br /&gt;
 ctrlDebug=collectData&lt;br /&gt;
&lt;br /&gt;
die Datensammlung verfolgen (leider entstehen viele Daten im Log). Man sieht welche Werte von dem Inverter/Reading geliefert werden:&lt;br /&gt;
 &lt;br /&gt;
 ....&lt;br /&gt;
 2025.02.02 19:10:10.558 1: SolCast DEBUG&amp;gt; collect Inverter 01 data - device: STP_5000, delivery: default =&amp;gt;&lt;br /&gt;
 2025.02.02 19:10:10.558 1: SolCast DEBUG&amp;gt; pv: 0 W, &amp;lt;mark&amp;gt;etotal: 63378810 Wh&amp;lt;/mark&amp;gt;&lt;br /&gt;
 ....&lt;br /&gt;
&lt;br /&gt;
Dieser Wert muß bei PV Erzeugung kontinuierlich hochzählen. Wenn nicht, muss das angegebene Reading, d.h. die Quelle überprüft werden.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ergebnis der Prüfung:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Tatsächlich wurde ein Reading benutzt, das ungeeignet war: Statt &amp;quot;Total_PV_Generation&amp;quot; wurde &amp;quot;Total_export_energy_from_PV&amp;quot; genutzt. In diesem Reading wurde immer die in die Batterie gespeicherte Energie von der gesamten erzeugten Energie abgezogen. Daher war auch vormittags alles 0, da in dieser Periode alle PV-Energie in die Batterie geladen wurde bzw. in weiteren Stunden ein Anteil der dann bei dem PV-Erzeugungswert fehlte.&lt;br /&gt;
&lt;br /&gt;
Nachdem die Ursache beseitigt war, zeigt SolarForecast wieder die erwarteten PV-Erzeugungswerte an.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
== Konfigurationsbeispiele ==&lt;br /&gt;
&lt;br /&gt;
=== Visualisierung solare Vorhersage und reale Erzeugung ===&lt;br /&gt;
&lt;br /&gt;
Zu Beginn jedes neuen Tages gegen 00:00 Uhr werden durch das SolarForecast Device Events für die initiale PV Prognose des kommenden Tages erstellt.&lt;br /&gt;
Der Readingteil dieser Events heißt&lt;br /&gt;
&lt;br /&gt;
 AllPVforecastsToEvent&lt;br /&gt;
&lt;br /&gt;
Die Uhrzeiten der Events sind so aufbereitet und können direkt geloggt werden um sie in einen SVG Plot zu integrieren.&lt;br /&gt;
Im SolarForecast Device ist dieses Reading nicht sichtbar, nur die Events werden erstellt.&lt;br /&gt;
&lt;br /&gt;
Die meteorologischen Bedingungen verändern sich über den Tag permanent. Je nach gewählter Strahlungsdatenquelle (eine API oder DWD Device) erfolgt eine mehr oder weniger dynamische Anpassung der Prognose an die sich verändernde Umwelt.&lt;br /&gt;
&lt;br /&gt;
Aktuelle Prognosedaten sowie die reale PV Erzeugung der vergangenen Stunde können über die Readings&lt;br /&gt;
&lt;br /&gt;
 LastHourPVforecast&lt;br /&gt;
 LastHourPVreal&lt;br /&gt;
&lt;br /&gt;
[[Datei:Screenshot 2024-10-20 134050.png|right|thumb|400px|Übersicht solare Vorhersage]]&lt;br /&gt;
geloggt werden. &lt;br /&gt;
&lt;br /&gt;
Werden alle drei Werte in einem SVG kombiniert, kann die Entwicklung der Prognose über den Tag und die Beziehung von Prognose zu realer PV Erzeugnung pro Stunde visualisiert werden.&lt;br /&gt;
&lt;br /&gt;
Für das rechts abgebildete Beispiel des Plots aus einer Datenbank &amp;quot;LogDBShort&amp;quot; sieht das gplot-File wie folgt aus:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# Created by FHEM/98_SVG.pm, 2024-03-08 15:05:31&lt;br /&gt;
set terminal png transparent size &amp;lt;SIZE&amp;gt; crop&lt;br /&gt;
set output &#039;&amp;lt;OUT&amp;gt;.png&#039;&lt;br /&gt;
set xdata time&lt;br /&gt;
set timefmt &amp;quot;%Y-%m-%d_%H:%M:%S&amp;quot;&lt;br /&gt;
set xlabel &amp;quot; &amp;quot;&lt;br /&gt;
set title &#039;&amp;lt;TL&amp;gt;&#039;&lt;br /&gt;
set ytics &lt;br /&gt;
set y2tics &lt;br /&gt;
set grid&lt;br /&gt;
set ylabel &amp;quot;Wh&amp;quot;&lt;br /&gt;
set y2label &amp;quot;Wh&amp;quot;&lt;br /&gt;
set yrange [0:7500]&lt;br /&gt;
set y2range [0:7500]&lt;br /&gt;
&lt;br /&gt;
#LogDBShort SolCast:LastHourPVforecast:::&lt;br /&gt;
#LogDBShort SolCast:LastHourPVreal:::&lt;br /&gt;
#LogDBShort SolCast:AllPVforecastsToEvent:::&lt;br /&gt;
&lt;br /&gt;
plot &amp;quot;&amp;lt;IN&amp;gt;&amp;quot; using 1:2 axes x1y2 title &#039;aktuelle PV Vorhersage&#039; ls l6fill lw 1 with lines,\&lt;br /&gt;
     &amp;quot;&amp;lt;IN&amp;gt;&amp;quot; using 1:2 axes x1y2 title &#039;reale PV Erzeugung&#039; ls l2fill lw 1 with lines,\&lt;br /&gt;
     &amp;quot;&amp;lt;IN&amp;gt;&amp;quot; using 1:2 axes x1y2 title &#039;initiale PV Vorhersage&#039; ls l4 lw 1 with lines&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Hinweis:&#039;&#039;&#039; In manchen Fällen werden die Vorhersagewerte im SVG-Plot nicht angezeigt wenn FileLog verwendet wird. Ein Setzen des Attribute &amp;quot;fixedrange=3days +1&amp;quot; im SVG löst das Problem. Die Definition des SVG-Devices:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
define SVG_LogDBShort_SolCast SVG LogDBShort:SVG_LogDBShort_SolCast:HISTORY&lt;br /&gt;
attr SVG_LogDBShort_SolCast fixedrange 3days +1&lt;br /&gt;
attr SVG_LogDBShort_SolCast room Energie-&amp;gt;SolarPrognose&lt;br /&gt;
attr SVG_LogDBShort_SolCast sortby 2&lt;br /&gt;
attr SVG_LogDBShort_SolCast title &amp;quot;Übersicht solare Vorhersage&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Technisch bedingt werden an einem Tag X kurz nach 00:00 Uhr die am Vortag für den Tag X erzeugten Events aktualisiert. Diese Daten erzeugen in der Datenbank einen zweiten Satz an Daten mit dem gleichen Timestamp was für die Anzeige unvorteilhaft ist. &lt;br /&gt;
Um nur die aktualisierten Events von &#039;&#039;&#039;AllPVforecastsToEvent&#039;&#039;&#039; in der Datenbank zu erhalten, bietet sich ein DbRep-Device an. Die nachfolgende Vorschlagsdefinition kann per at-Device jeden Tag kurz vor Mitternacht (z.B. 23:57:10) ausgeführt werden. Es werden immer die in der Vergangenheit geschriebenen (veralteten) Daten aus der Datenbank gelöscht, doppelte Datesätze vermieden und immer die aktuellste initiale Prognose gespeichert:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
define Rep.Del.AllPVforecastsToEvent DbRep LogDBShort&lt;br /&gt;
attr Rep.Del.AllPVforecastsToEvent alias Löschen Readings AllPVforecastsToEvent des Folgetages&lt;br /&gt;
attr Rep.Del.AllPVforecastsToEvent comment ermöglicht dass die Readings AllPVforecastsToEvent am Folgetag wieder neu und aktuell geschrieben werden können&lt;br /&gt;
attr Rep.Del.AllPVforecastsToEvent devStateIcon initialized:control_3dot_hor_s connected:10px-kreis-gelb .*disconnect:10px-kreis-rot .*done:10px-kreis-gruen&lt;br /&gt;
attr Rep.Del.AllPVforecastsToEvent disable 0&lt;br /&gt;
attr Rep.Del.AllPVforecastsToEvent event-on-update-reading state&lt;br /&gt;
attr Rep.Del.AllPVforecastsToEvent icon edit_delete&lt;br /&gt;
attr Rep.Del.AllPVforecastsToEvent room Datenbank-&amp;gt;Produktiv&lt;br /&gt;
attr Rep.Del.AllPVforecastsToEvent showproctime 1&lt;br /&gt;
attr Rep.Del.AllPVforecastsToEvent device TYPE=SolarForecast&lt;br /&gt;
attr Rep.Del.AllPVforecastsToEvent timestamp_begin next_day_begin&lt;br /&gt;
attr Rep.Del.AllPVforecastsToEvent timestamp_end next_day_end&lt;br /&gt;
attr Rep.Del.AllPVforecastsToEvent verbose 2&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das angegebene DbLog-Device &#039;&#039;&#039;LogDBShort&#039;&#039;&#039; ist natürlich anzupassen.&lt;br /&gt;
&lt;br /&gt;
Das at-Device zum Starten der Datenbankbereinigung sieht folgendermaßen aus:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
define At.Del.AllPVforecastsToEvent at *23:58:10 set Rep.Del.AllPVforecastsToEvent delEntries&lt;br /&gt;
attr At.Del.AllPVforecastsToEvent alias Start Löschen Readings AllPVforecastsToEventn LogDBShort&lt;br /&gt;
attr At.Del.AllPVforecastsToEvent icon clock&lt;br /&gt;
attr At.Del.AllPVforecastsToEvent room Datenbank-&amp;gt;Produktiv&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Natürlich sind auch hier die eigenen Devicenamen entsprechend anzupassen.&lt;br /&gt;
&lt;br /&gt;
Ab SolarForecast Version 1.51.7 kann die Eventerzeugung für bestimmte SVG Plot-Typen optimiert werden. &amp;lt;br&amp;gt; &lt;br /&gt;
Die Aktivierung dieser Optimierungen ist in der Online Hilfe zum Attribut &#039;&#039;&#039;plantControl-&amp;gt;genPVforecastsToEvent&#039;&#039;&#039; beschrieben. Bei Nutzung des Attributes plantControl-&amp;gt;genPVforecastsToEvent ist ebenfalls das Attribut &#039;&#039;&#039;event-on-update-reading=AllPVforecastsToEvent&#039;&#039;&#039; zu setzen.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Beispielkonfiguration 1 ===&lt;br /&gt;
Dies ist eine Konfiguration mit &lt;br /&gt;
&lt;br /&gt;
SummenDummy für 2 BatterieWR (Namen : SBS25 / SBS25_2)&lt;br /&gt;
&lt;br /&gt;
SummenDummy für 3 PV-Wechselrichter (Namen : SB25 / SB30 / SB40)&lt;br /&gt;
&lt;br /&gt;
            mit verschiedenen InverterStrings / ModulDirection / ModulTiltAngle, ModulPeakString&lt;br /&gt;
&lt;br /&gt;
und auch mit den notwendigen zugehörigen anderen Modul-Konfigs.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ACHTUNG:&#039;&#039;&#039; Zusätzlich enthalten ist bei dem Beispiel-Notify eine Sonderkonstellation für eine Brennstoffzelle &amp;quot;FCU&amp;quot; als weitere bzw. zusätzliche Stromerzeugungsquelle. Diese &amp;quot;FCU&amp;quot; wird dadurch mit in der Grafik mit deren Erzeugungsleistung Tag und Nacht in der Erzeugersumme (am Symbol = Sonne) berücksichtigt.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;(ReadingsVal(&amp;quot;FCU&amp;quot;,&amp;quot;FCU-Strom-aktuelle-Leistung&amp;quot;,0)/1000)&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
==== DWD ====&lt;br /&gt;
Bitte dabei diese DWD-Version aus dem [https://svn.fhem.de/trac/browser/trunk/fhem/contrib/DS_Starter Contrib] von DS_Starter dazu nutzen&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|55_DWD_OpenData.pm&lt;br /&gt;
|136.2 KB ​&lt;br /&gt;
|29260  &lt;br /&gt;
|DS_Starter&lt;br /&gt;
|55_DWD_OpenData: contrib 1.17.7&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
define DWD DWD_OpenData&lt;br /&gt;
attr DWD downloadTimeout 120&lt;br /&gt;
attr DWD comment Im DWD Wetterdevice verwendet Solarforecast : \&lt;br /&gt;
TTT,Neff,RR1c,ww,SunUp,SunRise,SunSet \&lt;br /&gt;
zusätzlich ist jedoch auch noch Rad1h notwendig \&lt;br /&gt;
wenn dieses DWD-Device als Strahlungsdevice genutzt wird.\&lt;br /&gt;
Die Station 10418 (Lüdenscheid) überträgt aktuell am 09.03.2025 (noch) Rad1h.\&lt;br /&gt;
attr DWD downloadTimeout 120&lt;br /&gt;
attr DWD forecastDays 7&lt;br /&gt;
attr DWD forecastProperties SunUp, SunRise, SunSet, Rad1h, R101, RR1c, TTT, Tx, Tn, Tg, DD, FX1, RR6c, R600, RRhc, Rh00, ww, wwd, Neff&lt;br /&gt;
attr DWD forecastRefresh 1&lt;br /&gt;
attr DWD forecastResolution 1&lt;br /&gt;
attr DWD forecastStation 10418&lt;br /&gt;
attr DWD forecastWW2Text 1&lt;br /&gt;
attr DWD group Umwelt&lt;br /&gt;
attr DWD icon rc_WEB&lt;br /&gt;
attr DWD room 021_DWD&lt;br /&gt;
attr DWD stateFormat Tomorrow Tmax fc1_Tx °C on fc1_date at fc_description  -(state fc_time)&lt;br /&gt;
attr DWD verbose 2&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== InverterDummy ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod InverterDummy dummy&lt;br /&gt;
attr InverterDummy event-on-change-reading .*&lt;br /&gt;
attr InverterDummy group Energy Meter&lt;br /&gt;
attr InverterDummy icon measure_photovoltaic_inst@green&lt;br /&gt;
attr InverterDummy room 020_PV,Energie&lt;br /&gt;
attr InverterDummy stateFormat {sprintf(&amp;quot;current %9.3f kW    Today_PVforecast  %9.3f kWh      Today_PV %9.3f kWh      Total_PV %9.3f kWh&amp;quot;,\&lt;br /&gt;
ReadingsVal($name,&amp;quot;total_pac&amp;quot;,0)/1,\&lt;br /&gt;
ReadingsNum(&amp;quot;Forecast&amp;quot;,&amp;quot;Today_PVforecast&amp;quot;,0)/1000,\&lt;br /&gt;
ReadingsVal($name,&amp;quot;etoday&amp;quot;,0)/1,\&lt;br /&gt;
ReadingsVal($name,&amp;quot;etotal&amp;quot;,0)/1,)}&lt;br /&gt;
attr InverterDummy verbose 2&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== SMA_Energymeter ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod SMA_Energymeter SMAEM&lt;br /&gt;
attr SMA_Energymeter DbLogExclude state&lt;br /&gt;
attr SMA_Energymeter diffAccept 50&lt;br /&gt;
attr SMA_Energymeter disable 0&lt;br /&gt;
attr SMA_Energymeter disableSernoInReading 1&lt;br /&gt;
attr SMA_Energymeter event-on-update-reading state,Saldo_Wirkleistung,Bezug_Wirkleistung,Einspeisung_Wirkleistung,Bezug_Wirkleistung_Zaehler,Einspeisung_Wirkleistung_Zaehler&lt;br /&gt;
attr SMA_Energymeter feedinPrice 0.08&lt;br /&gt;
attr SMA_Energymeter group Energy Meter&lt;br /&gt;
attr SMA_Energymeter icon measure_power@green&lt;br /&gt;
attr SMA_Energymeter interval 15&lt;br /&gt;
attr SMA_Energymeter powerCost 0.25&lt;br /&gt;
attr SMA_Energymeter room 015_Zaehler,020_PV,Energie&lt;br /&gt;
attr SMA_Energymeter serialNumber XXXXXXXXXX&lt;br /&gt;
attr SMA_Energymeter stateFormat state W (IN -) P1: L1_Saldo_Wirkleistung P2: L2_Saldo_Wirkleistung P3:L3_Saldo_Wirkleistung&lt;br /&gt;
attr SMA_Energymeter verbose 2&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== BatteryDummy ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod BatteryDummy dummy&lt;br /&gt;
attr BatteryDummy DbLogExclude .*&lt;br /&gt;
attr BatteryDummy event-on-change-reading .*&lt;br /&gt;
attr BatteryDummy group Energy Meter&lt;br /&gt;
attr BatteryDummy icon batterie@green&lt;br /&gt;
attr BatteryDummy room 020_PV,Energie&lt;br /&gt;
attr BatteryDummy stateFormat {ReadingsVal(&amp;quot;$name&amp;quot;,&amp;quot;total_pac&amp;quot;, undef).&amp;quot; kW &amp;quot;.\&lt;br /&gt;
&amp;quot; - total &amp;quot;.ReadingsVal(&amp;quot;$name&amp;quot;,&amp;quot;bat_loadtotal&amp;quot;, undef).&amp;quot; kWh (-in)&amp;quot;.\&lt;br /&gt;
&amp;quot; - &amp;quot;.ReadingsVal(&amp;quot;$name&amp;quot;,&amp;quot;bat_unloadtotal&amp;quot;, undef).&amp;quot; kWh (out)&amp;quot;.\&lt;br /&gt;
&amp;quot; - charged &amp;quot;.ReadingsVal(&amp;quot;$name&amp;quot;,&amp;quot;chargestatus&amp;quot;, undef).&amp;quot; % &amp;quot;}&lt;br /&gt;
attr BatteryDummy userReadings total_pac, power_out, power_in, chargestatus, bat_rated_capacity, bat_loadtotal, bat_unloadtotal&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== &amp;quot;Berechnungs&amp;quot;-Notify der Werte für Batterie- und InverterDummy ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod N.PV.TotalConsumption.Dum.Energy notify SMA_Energymeter:Saldo_Wirkleistung:.* {\&lt;br /&gt;
 # Batterie-Bezug -Batterieentnahme\&lt;br /&gt;
fhem &amp;quot;setreading Dum.Energy BattLoadIn &amp;quot;.sprintf(&amp;quot;%.1f&amp;quot;,(ReadingsVal(&amp;quot;SBS25&amp;quot;,&amp;quot;power_out&amp;quot;,0)));;\&lt;br /&gt;
 # Batterie-Beladung Batterie mit Strom füllen\&lt;br /&gt;
fhem &amp;quot;setreading Dum.Energy BattLoadOut &amp;quot;.sprintf(&amp;quot;%.1f&amp;quot;,(ReadingsVal(&amp;quot;SBS25&amp;quot;,&amp;quot;power_in&amp;quot;,0)));;\&lt;br /&gt;
 # Batteriestatus\&lt;br /&gt;
fhem &amp;quot;setreading Dum.Energy BattStatusP &amp;quot;.sprintf(&amp;quot;%.1f&amp;quot;,(ReadingsVal(&amp;quot;SBS25&amp;quot;,&amp;quot;chargestatus&amp;quot;,0)));;\&lt;br /&gt;
 # Batterie-Bezug -Batterieentnahme_2\&lt;br /&gt;
fhem &amp;quot;setreading Dum.Energy BattLoadIn_2 &amp;quot;.sprintf(&amp;quot;%.1f&amp;quot;,(ReadingsVal(&amp;quot;SBS25_2&amp;quot;,&amp;quot;power_out&amp;quot;,0)));;\&lt;br /&gt;
 # Batterie-Beladung_2 Batterie mit Strom füllen\&lt;br /&gt;
fhem &amp;quot;setreading Dum.Energy BattLoadOut_2 &amp;quot;.sprintf(&amp;quot;%.1f&amp;quot;,(ReadingsVal(&amp;quot;SBS25_2&amp;quot;,&amp;quot;power_in&amp;quot;,0)));;\&lt;br /&gt;
 # Batteriestatus_2\&lt;br /&gt;
fhem &amp;quot;setreading Dum.Energy BattStatusP_2 &amp;quot;.sprintf(&amp;quot;%.1f&amp;quot;,(ReadingsVal(&amp;quot;SBS25_2&amp;quot;,&amp;quot;chargestatus&amp;quot;,0)));;\&lt;br /&gt;
 # Forecast Invertererzeugung InverterDummy \&lt;br /&gt;
fhem &amp;quot;setreading InverterDummy Today_PVforecast &amp;quot;.sprintf(&amp;quot;%.3f&amp;quot;,(ReadingsNum(&amp;quot;Forecast&amp;quot;,&amp;quot;Today_PVforecast&amp;quot;,0)));;\&lt;br /&gt;
 # Invertererzeugung InverterDummy \&lt;br /&gt;
fhem &amp;quot;setreading InverterDummy etotal &amp;quot;.sprintf(&amp;quot;%.3f&amp;quot;,(ReadingsNum(&amp;quot;SB25&amp;quot;,&amp;quot;etotal&amp;quot;,0))+(ReadingsNum(&amp;quot;SB30&amp;quot;,&amp;quot;etotal&amp;quot;,0))+(ReadingsNum(&amp;quot;SB40&amp;quot;,&amp;quot;etotal&amp;quot;,0)));;\&lt;br /&gt;
 # Invertererzeugung InverterDummy \&lt;br /&gt;
 #fhem &amp;quot;setreading InverterDummy total_pac &amp;quot;.sprintf(&amp;quot;%.3f&amp;quot;,(ReadingsVal(&amp;quot;MB_USRW610_004&amp;quot;,&amp;quot;Power_Sum__W&amp;quot;,0)/1000)+(ReadingsNum(&amp;quot;SB25&amp;quot;,&amp;quot;total_pac&amp;quot;,0))+(ReadingsNum(&amp;quot;SB30&amp;quot;,&amp;quot;total_pac&amp;quot;,0))+(ReadingsNum(&amp;quot;SB40&amp;quot;,&amp;quot;total_pac&amp;quot;,0)));;\&lt;br /&gt;
fhem &amp;quot;setreading InverterDummy total_pac &amp;quot;.sprintf(&amp;quot;%.3f&amp;quot;,(ReadingsNum(&amp;quot;SB25&amp;quot;,&amp;quot;total_pac&amp;quot;,0))+(ReadingsNum(&amp;quot;SB30&amp;quot;,&amp;quot;total_pac&amp;quot;,0))+(ReadingsNum(&amp;quot;SB40&amp;quot;,&amp;quot;total_pac&amp;quot;,0)));;\&lt;br /&gt;
#fhem &amp;quot;setreading InverterDummy total_pac &amp;quot;.sprintf(&amp;quot;%.3f&amp;quot;,(ReadingsVal(&amp;quot;FCU&amp;quot;,&amp;quot;FCU-Strom-aktuelle-Leistung&amp;quot;,0)/1000)+(ReadingsNum(&amp;quot;SB25&amp;quot;,&amp;quot;total_pac&amp;quot;,0))+(ReadingsNum(&amp;quot;SB30&amp;quot;,&amp;quot;total_pac&amp;quot;,0))+(ReadingsNum(&amp;quot;SB40&amp;quot;,&amp;quot;total_pac&amp;quot;,0)));;\&lt;br /&gt;
 # Invertererzeugung InverterDummy \&lt;br /&gt;
my $wert1234 = &amp;quot;0&amp;quot; ;;\&lt;br /&gt;
$wert1234 = sprintf(&amp;quot;%.3f&amp;quot;,(ReadingsNum(&amp;quot;SB25&amp;quot;,&amp;quot;etoday&amp;quot;,0))+(ReadingsNum(&amp;quot;SB30&amp;quot;,&amp;quot;etoday&amp;quot;,0))+(ReadingsNum(&amp;quot;SB40&amp;quot;,&amp;quot;etoday&amp;quot;,0)));; \&lt;br /&gt;
fhem (&amp;quot;setreading InverterDummy etoday &amp;quot;.sprintf(&amp;quot;%.3f&amp;quot;,$wert1234));;\&lt;br /&gt;
 # Batterie-Bezug -Batterieentnahme InverterDummy\&lt;br /&gt;
fhem &amp;quot;setreading BatteryDummy power_out &amp;quot;.sprintf(&amp;quot;%.0f&amp;quot;,(ReadingsNum(&amp;quot;SBS25&amp;quot;,&amp;quot;power_out&amp;quot;,0))+(ReadingsNum(&amp;quot;SBS25_2&amp;quot;,&amp;quot;power_out&amp;quot;,0)));;\&lt;br /&gt;
 # Batterie-Beladung InverterDummyBatterie mit Strom füllen\&lt;br /&gt;
fhem &amp;quot;setreading BatteryDummy power_in &amp;quot;.sprintf(&amp;quot;%.0f&amp;quot;,(ReadingsNum(&amp;quot;SBS25&amp;quot;,&amp;quot;power_in&amp;quot;,0))+(ReadingsNum(&amp;quot;SBS25_2&amp;quot;,&amp;quot;power_in&amp;quot;,0)));;\&lt;br /&gt;
 # Batterie-Bezug -bat_loadtotal Batterieentnahme InverterDummy\&lt;br /&gt;
fhem &amp;quot;setreading BatteryDummy bat_unloadtotal &amp;quot;.sprintf(&amp;quot;%.3f&amp;quot;,(ReadingsNum(&amp;quot;SBS25&amp;quot;,&amp;quot;bat_unloadtotal&amp;quot;,0))+(ReadingsNum(&amp;quot;SBS25_2&amp;quot;,&amp;quot;bat_unloadtotal&amp;quot;,0)));;\&lt;br /&gt;
 # Batterie-Beladung bat_loadtotal InverterDummyBatterie mit Strom füllen\&lt;br /&gt;
fhem &amp;quot;setreading BatteryDummy bat_loadtotal &amp;quot;.sprintf(&amp;quot;%.3f&amp;quot;,(ReadingsNum(&amp;quot;SBS25&amp;quot;,&amp;quot;bat_loadtotal&amp;quot;,0))+(ReadingsNum(&amp;quot;SBS25_2&amp;quot;,&amp;quot;bat_loadtotal&amp;quot;,0)));;\&lt;br /&gt;
 # Batteriestatus InverterDummy\&lt;br /&gt;
my $wert5 = sprintf(&amp;quot;%.2f&amp;quot;,(((ReadingsNum(&amp;quot;SBS25&amp;quot;,&amp;quot;chargestatus&amp;quot;,0))/2) + ((ReadingsNum(&amp;quot;SBS25_2&amp;quot;,&amp;quot;chargestatus&amp;quot;,0))/2)));; \&lt;br /&gt;
fhem (&amp;quot;setreading BatteryDummy chargestatus &amp;quot;.sprintf(&amp;quot;%.2f&amp;quot;,$wert5));;\&lt;br /&gt;
 # Batterie-total_pac  InverterDummy\&lt;br /&gt;
my $wert6 = sprintf(&amp;quot;%.3f&amp;quot;,((ReadingsNum(&amp;quot;SBS25&amp;quot;,&amp;quot;total_pac&amp;quot;,0))+(ReadingsNum(&amp;quot;SBS25_2&amp;quot;,&amp;quot;total_pac&amp;quot;,0))));; \&lt;br /&gt;
fhem (&amp;quot;setreading BatteryDummy total_pac &amp;quot;.sprintf(&amp;quot;%.3f&amp;quot;,$wert6));;\&lt;br /&gt;
 # Batterie-total_pac  InverterDummy\&lt;br /&gt;
my $wert7 = sprintf(&amp;quot;%.3f&amp;quot;,((ReadingsNum(&amp;quot;SBS25&amp;quot;,&amp;quot;bat_rated_capacity&amp;quot;,0))+(ReadingsNum(&amp;quot;SBS25_2&amp;quot;,&amp;quot;bat_rated_capacity&amp;quot;,0))));; \&lt;br /&gt;
fhem (&amp;quot;setreading BatteryDummy bat_rated_capacity &amp;quot;.sprintf(&amp;quot;%.3f&amp;quot;,$wert7));;\&lt;br /&gt;
 # möglicher Aufruf einer Batterie-Bebladung-Routine....SMABatteryChargewithTibber();;\&lt;br /&gt;
}&lt;br /&gt;
attr N.PV.TotalConsumption.Dum.Energy DbLogExclude .*&lt;br /&gt;
attr N.PV.TotalConsumption.Dum.Energy room Energie&lt;br /&gt;
attr N.PV.TotalConsumption.Dum.Energy verbose 2&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== SolarForecast ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod Forecast SolarForecast&lt;br /&gt;
attr Forecast DbLogExclude .*&lt;br /&gt;
attr Forecast DbLogInclude Current_BatCharge999&lt;br /&gt;
attr Forecast affectBatteryPreferredCharge 30&lt;br /&gt;
attr Forecast affectConsForecastLastDays 31&lt;br /&gt;
attr Forecast affect70percentRule 0&lt;br /&gt;
attr Forecast comment &amp;quot;wget -qO ./FHEM/76_SolarForecast.pm https://svn.fhem.de/fhem/trunk/fhem/contrib/DS_Starter/76_SolarForecast.pm&amp;quot;\&lt;br /&gt;
&amp;quot;wget -qO ./FHEM/55_DWD_OpenData.pm https://svn.fhem.de/fhem/trunk/fhem/contrib/DS_Starter/55_DWD_OpenData.pm&amp;quot;\&lt;br /&gt;
&amp;quot;13.01.2025 mit der Version 76_SolarForecast.pm:v1.43.2-s29518/2025-01-12&amp;quot;&lt;br /&gt;
attr Forecast graphicHeaderOwnspec PV&amp;amp;nbsp;;Heute&amp;amp;nbsp;;real:Today_PVreal Verbrauch&amp;amp;nbsp;;bis&amp;amp;nbsp;;Sonnenaufgang&amp;amp;nbsp;;:special_conForecastTillNextSunrise PV&amp;amp;nbsp;;Morgen&amp;amp;nbsp;;erwartet:Tomorrow_PVforecast PV&amp;amp;nbsp;;Uebermorgen&amp;amp;nbsp;;erwartet:special_dayAfterTomorrowPVforecast  Batt.-Ladeanforderung&amp;amp;nbsp;;:Battery_ChargeRequest FCU-Erzeugung&amp;amp;nbsp;;:Current_PP01\&lt;br /&gt;
attr Forecast consumer01 FBDECT_fbahahttp_11657_0127183 icon=scene_washing_machine@orange type=washingmachine power=10 swstate:state notbefore=09 notafter=20 pcurr=power:W:3 etotal=energy:Wh interruptable=1 auto=solarforecast_auto surpmeth=default&lt;br /&gt;
attr Forecast consumer02 FBDECT_fbahahttp_E8_DF_70_07_3E_57 icon=light_floor_lamp@orange type=other power=15 swstate:state pcurr=power:W:10 etotal=energy:Wh interruptable=0 auto=solarforecast_auto surpmeth=default&lt;br /&gt;
attr Forecast consumer03 FBDECT_fbahahttp_E8_DF_70_07_42_0B icon=raspberrypi@orange type=other power=8 swstate:state pcurr=power:W:1 etotal=energy:Wh interruptable=0 auto=solarforecast_auto surpmeth=default&lt;br /&gt;
attr Forecast consumer04 FBDECT_fbahahttp_11657_0067275 icon=springbrunnen_icon@orange type=other power=50 swstate:state pcurr=power:W:10 etotal=energy:Wh interruptable=0 auto=solarforecast_auto surpmeth=default&lt;br /&gt;
attr Forecast consumer05 FBDECT_fbahahttp_34_31_C4_D4_31_37 icon=sani_domestic_waterworks@orange type=other power=10 swstate:state pcurr=power:W:3 etotal=energy:Wh auto=solarforecast_auto surpmeth=default&lt;br /&gt;
attr Forecast consumer06 tuya_local_bf5037060f450bdbd4rl0q icon=scene_clothes_dryer@orange type=dryer power=10 swstate:state pcurr=cur_power:W:3 etotal=energy:Wh auto=solarforecast_auto surpmeth=default&lt;br /&gt;
attr Forecast consumer07 tuya_local_bfac44fb487476efd1vhdu icon=weather_sunset@orange type=noSchedule power=5 swstate:state pcurr=cur_power:W:3 etotal=energy:Wh auto=solarforecast_auto surpmeth=default&lt;br /&gt;
attr Forecast consumerControl adviceIcon=light_light_dim_100@gold&lt;br /&gt;
attr Forecast consumerControl showLegend=icon_top&lt;br /&gt;
attr Forecast consumerControl detailLink=1&lt;br /&gt;
attr Forecast ctrlAIdataStorageDuration 1825&lt;br /&gt;
attr Forecast ctrlAIshiftTrainStart 2&lt;br /&gt;
attr Forecast ctrlBatSocManagement01 lowSoc=10 upSoC=30 maxSoC=99 careCycle=20&lt;br /&gt;
attr Forecast ctrlBatSocManagement02 lowSoc=10 upSoC=30 maxSoC=99 careCycle=20&lt;br /&gt;
attr Forecast ctrlDebug none&lt;br /&gt;
attr Forecast plantControl genPVdeviation=continuously&lt;br /&gt;
attr Forecast plantControl cycleInterval=15&lt;br /&gt;
attr Forecast ctrlNextDayForecastReadings 01,02,03,04,05,06,07,08,09,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24&lt;br /&gt;
attr Forecast ctrlNextHoursSoCForecastReadings 00,01,02,03,04,05,06,07,08,09,10,11,12,13,14,15,16,17,18,19,20,21,22,23&lt;br /&gt;
attr Forecast ctrlSpecialReadings BatPowerIn_Sum,BatPowerOut_Sum,SunHours_Remain,SunMinutes_Remain,allStringsFullfilled,conForecastTillNextSunrise,currentAPIinterval,currentRunMtsConsumer_01,currentRunMtsConsumer_02,currentRunMtsConsumer_03,currentRunMtsConsumer_04,currentRunMtsConsumer_05,currentRunMtsConsumer_06,currentRunMtsConsumer_07,dayAfterTomorrowPVforecast,daysUntilBatteryCare_01,daysUntilBatteryCare_02,lastretrieval_time,lastretrieval_timestamp,response_message,runTimeAvgDayConsumer_01,runTimeAvgDayConsumer_02,runTimeAvgDayConsumer_03,runTimeAvgDayConsumer_04,runTimeAvgDayConsumer_05,runTimeAvgDayConsumer_06,runTimeAvgDayConsumer_07,runTimeCentralTask,runTimeLastAPIAnswer,runTimeLastAPIProc,runTimeTrainAI,todayBatIn_01,todayBatIn_02,todayBatOut_01,todayBatOut_02,todayConForecastTillSunset,todayConsumptionForecast,todayDoneAPIcalls,todayDoneAPIrequests,todayGridConsumption,todayGridFeedIn,todayMaxAPIcalls,todayRemainingAPIcalls,todayRemainingAPIrequests&lt;br /&gt;
attr Forecast disable 0&lt;br /&gt;
attr Forecast event-min-interval .*:1800&lt;br /&gt;
attr Forecast event-on-change-reading .*&lt;br /&gt;
attr Forecast flowGraphicControl animate=1 consumerdist=80 h2consumerdist=50 shiftx=0  shifty=0 showconsumer=1 showconsumerdummy=1 showconsumerpower=1 showconsumerremaintime=0 size=400 strokewidth=12&lt;br /&gt;
attr Forecast graphicBeam1Color 3C14FF&lt;br /&gt;
attr Forecast graphicBeam1Content pvReal&lt;br /&gt;
attr Forecast graphicBeam2Color 19FF29&lt;br /&gt;
attr Forecast graphicBeam2Content pvForecast&lt;br /&gt;
attr Forecast graphicBeam3Color D60924&lt;br /&gt;
attr Forecast graphicBeam3Content batsocforecast_02&lt;br /&gt;
attr Forecast graphicBeam3FontColor FFFF0D&lt;br /&gt;
attr Forecast graphicBeam4Color FFFF1F&lt;br /&gt;
attr Forecast graphicBeam4Content batsocforecast_01&lt;br /&gt;
attr Forecast graphicBeam4FontColor 000000&lt;br /&gt;
attr Forecast graphicBeamHeightLevel1 200&lt;br /&gt;
attr Forecast graphicBeamHeightLevel2 200&lt;br /&gt;
attr Forecast graphicHeaderDetail all&lt;br /&gt;
attr Forecast graphicHeaderOwnspec #PV\&lt;br /&gt;
PV&amp;amp;nbsp;;Heute&amp;amp;nbsp;;real:Today_PVreal\&lt;br /&gt;
PV&amp;amp;nbsp;;Morgen&amp;amp;nbsp;;erwartet:Tomorrow_PVforecast\&lt;br /&gt;
PV&amp;amp;nbsp;;Uebermorgen&amp;amp;nbsp;;erwartet:special_dayAfterTomorrowPVforecast\&lt;br /&gt;
:\&lt;br /&gt;
#\&lt;br /&gt;
AutarkyRate:Current_AutarkyRate\&lt;br /&gt;
Überschuss:Current_Surplus\&lt;br /&gt;
aktueller&amp;amp;nbsp;;Netzbezug:Current_GridConsumption\&lt;br /&gt;
:\&lt;br /&gt;
#Verbrauch\&lt;br /&gt;
bis&amp;amp;nbsp;;Sonnenuntergang:special_todayConForecastTillSunset\&lt;br /&gt;
bis&amp;amp;nbsp;;Sonnenaufgang&amp;amp;nbsp;;:special_conForecastTillNextSunrise\&lt;br /&gt;
:\&lt;br /&gt;
:\&lt;br /&gt;
#Batterie01\&lt;br /&gt;
Batt.-Ladeanforderung&amp;amp;nbsp;;:Battery_ChargeRequest_01\&lt;br /&gt;
Batt.-Ladung&amp;amp;nbsp;;empfohlen:Battery_ChargeUnrestricted_01\&lt;br /&gt;
Ladung&amp;amp;nbsp;;heute:special_todayBatIn_01\&lt;br /&gt;
Entladung&amp;amp;nbsp;;heute:special_todayBatOut_01\&lt;br /&gt;
#Batterie01_tmp\&lt;br /&gt;
Current_PowerBatIn_01:Current_PowerBatIn_01\&lt;br /&gt;
Current_PowerBatOut_01:Current_PowerBatOut_01\&lt;br /&gt;
:\&lt;br /&gt;
:\&lt;br /&gt;
#Batterie02\&lt;br /&gt;
Batt.-Ladeanforderung&amp;amp;nbsp;;:Battery_ChargeRequest_02\&lt;br /&gt;
Batt.-Ladung&amp;amp;nbsp;;empfohlen:Battery_ChargeUnrestricted_02\&lt;br /&gt;
Ladung&amp;amp;nbsp;;heute:special_todayBatIn_02\&lt;br /&gt;
Entladung&amp;amp;nbsp;;heute:special_todayBatOut_02\&lt;br /&gt;
#Batterie02_tmp\&lt;br /&gt;
Current_PowerBatIn_02:Current_PowerBatIn_02\&lt;br /&gt;
Current_PowerBatOut_02:Current_PowerBatOut_02\&lt;br /&gt;
:\&lt;br /&gt;
:\&lt;br /&gt;
#Settings\&lt;br /&gt;
Autokorrektur:pvCorrectionFactor_Auto \&lt;br /&gt;
Wetter:graphicShowWeather\&lt;br /&gt;
History:graphicHistoryHour\&lt;br /&gt;
ShowNight:graphicShowNight\&lt;br /&gt;
Debug:ctrlDebug\&lt;br /&gt;
FCU-Modus:FCU-Betriebsmodus@FCU&lt;br /&gt;
attr Forecast graphicHistoryHour 4&lt;br /&gt;
attr Forecast graphicHourCount 24&lt;br /&gt;
attr Forecast graphicLayoutType double&lt;br /&gt;
attr Forecast graphicShowDiff top&lt;br /&gt;
attr Forecast graphicShowNight 1&lt;br /&gt;
attr Forecast graphicShowWeather 1&lt;br /&gt;
attr Forecast group Energy Meter&lt;br /&gt;
attr Forecast room 020_PV,Energie&lt;br /&gt;
attr Forecast setupBatteryDev01 SBS25 pin=-pout:kW pout=total_pac:kW intotal=bat_loadtotal:kWh outtotal=bat_unloadtotal:kWh charge=chargestatus cap=9800 show=2&lt;br /&gt;
attr Forecast setupBatteryDev02 SBS25_2 pin=-pout:kW pout=total_pac:kW intotal=bat_loadtotal:kWh outtotal=bat_unloadtotal:kWh charge=chargestatus cap=9800 show=2&lt;br /&gt;
attr Forecast setupInverterDev01 SB25 pv=total_pac:kW etotal=etotal:kWh capacity=2500 strings=GarageSE limit=70&lt;br /&gt;
attr Forecast setupInverterDev02 SB30 pv=total_pac:kW etotal=etotal:kWh capacity=3000 strings=GarageNW,HausNW limit=70&lt;br /&gt;
attr Forecast setupInverterDev03 SB40 pv=total_pac:kW etotal=etotal:kWh capacity=4000 strings=HausSE1,HausSE2,HausSW limit=70&lt;br /&gt;
attr Forecast setupInverterStrings GarageSE,GarageNW,HausNW,HausSW,HausSE1,HausSE2&lt;br /&gt;
attr Forecast setupMeterDev SMA_Energymeter gcon=Bezug_Wirkleistung:W contotal=Bezug_Wirkleistung_Zaehler:kWh gfeedin=Einspeisung_Wirkleistung:W feedtotal=Einspeisung_Wirkleistung_Zaehler:kWh conprice=0.25:€ feedprice=0.08123:€&lt;br /&gt;
attr Forecast setupOtherProducer01 icon=Heizung_FCU_green@red MB_USRW610_004 pcurr=Power_L1__W:W etotal=Energy_L1_import__kWh:kWh&lt;br /&gt;
attr Forecast setupRadiationAPI DWD&lt;br /&gt;
attr Forecast setupStringPeak GarageSE=2.75 GarageNW=3.200 HausNW=2.230 HausSW=2.230 HausSE1=2.200 HausSE2=2.200&lt;br /&gt;
attr Forecast setupWeatherDev1 DWD&lt;br /&gt;
attr Forecast stateFormat Current_PV&lt;br /&gt;
attr Forecast userReadings Current_BatCharge999 {((ReadingsNum(&amp;quot;SBS25&amp;quot;,&amp;quot;chargestatus&amp;quot;,0) * 10 * ReadingsNum(&amp;quot;SBS25&amp;quot;,&amp;quot;bat_rated_capacity&amp;quot;,0))  + (ReadingsNum(&amp;quot;SBS25_2&amp;quot;,&amp;quot;chargestatus&amp;quot;,0) * 10 * ReadingsNum(&amp;quot;SBS25&amp;quot;,&amp;quot;bat_rated_capacity&amp;quot;,0))) / ( (ReadingsNum(&amp;quot;SBS25&amp;quot;,&amp;quot;bat_rated_capacity&amp;quot;,0) * 1000)  + (ReadingsNum(&amp;quot;SBS25_2&amp;quot;,&amp;quot;bat_rated_capacity&amp;quot;,0)*1000))*100}&lt;br /&gt;
attr Forecast verbose 2&lt;br /&gt;
&lt;br /&gt;
setstate Forecast 0 W&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:55 .FCU_FCU-Betriebsmodus 2&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:55 .Forecast_ctrlDebug none&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:55 .Forecast_graphicHistoryHour 4&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:55 .Forecast_graphicShowNight 1&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:55 .Forecast_graphicShowWeather 1&lt;br /&gt;
setstate Forecast 2025-01-13 08:24:25 .associatedWith SMA_Energymeter FBDECT_fbahahttp_11657_0127183 FBDECT_fbahahttp_E8_DF_70_07_3E_57 FBDECT_fbahahttp_E8_DF_70_07_42_0B FBDECT_fbahahttp_11657_0067275 FBDECT_fbahahttp_34_31_C4_D4_31_37 tuya_local_bf5037060f450bdbd4rl0q tuya_local_bfac44fb487476efd1vhdu SBS25 SBS25_2 SB25 SB30 SB40 DWD MB_USRW610_004&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 .lastupdateForecastValues 1736790834&lt;br /&gt;
setstate Forecast 2025-01-13 01:00:05 .pvCorrectionFactor_01_apipercentil done&lt;br /&gt;
setstate Forecast 2025-01-13 01:00:05 .pvCorrectionFactor_01_cloudcover done&lt;br /&gt;
setstate Forecast 2025-01-13 02:00:04 .pvCorrectionFactor_02_apipercentil done&lt;br /&gt;
setstate Forecast 2025-01-13 02:00:04 .pvCorrectionFactor_02_cloudcover done&lt;br /&gt;
setstate Forecast 2025-01-13 03:00:04 .pvCorrectionFactor_03_apipercentil done&lt;br /&gt;
setstate Forecast 2025-01-13 03:00:04 .pvCorrectionFactor_03_cloudcover done&lt;br /&gt;
setstate Forecast 2025-01-13 04:00:05 .pvCorrectionFactor_04_apipercentil done&lt;br /&gt;
setstate Forecast 2025-01-13 04:00:05 .pvCorrectionFactor_04_cloudcover done&lt;br /&gt;
setstate Forecast 2025-01-13 05:00:05 .pvCorrectionFactor_05_apipercentil done&lt;br /&gt;
setstate Forecast 2025-01-13 05:00:05 .pvCorrectionFactor_05_cloudcover done&lt;br /&gt;
setstate Forecast 2025-01-13 06:00:06 .pvCorrectionFactor_06_apipercentil done&lt;br /&gt;
setstate Forecast 2025-01-13 06:00:06 .pvCorrectionFactor_06_cloudcover done&lt;br /&gt;
setstate Forecast 2025-01-13 07:00:04 .pvCorrectionFactor_07_apipercentil done&lt;br /&gt;
setstate Forecast 2025-01-13 07:00:04 .pvCorrectionFactor_07_cloudcover done&lt;br /&gt;
setstate Forecast 2025-01-13 08:00:03 .pvCorrectionFactor_08_apipercentil done&lt;br /&gt;
setstate Forecast 2025-01-13 08:00:03 .pvCorrectionFactor_08_cloudcover done&lt;br /&gt;
setstate Forecast 2025-01-13 09:00:04 .pvCorrectionFactor_09_apipercentil done&lt;br /&gt;
setstate Forecast 2025-01-13 09:00:04 .pvCorrectionFactor_09_cloudcover done&lt;br /&gt;
setstate Forecast 2025-01-13 10:00:05 .pvCorrectionFactor_10_apipercentil done&lt;br /&gt;
setstate Forecast 2025-01-13 10:00:05 .pvCorrectionFactor_10_cloudcover done&lt;br /&gt;
setstate Forecast 2025-01-13 11:00:05 .pvCorrectionFactor_11_apipercentil done&lt;br /&gt;
setstate Forecast 2025-01-13 11:00:05 .pvCorrectionFactor_11_cloudcover done&lt;br /&gt;
setstate Forecast 2025-01-13 12:00:02 .pvCorrectionFactor_12_apipercentil done&lt;br /&gt;
setstate Forecast 2025-01-13 12:00:02 .pvCorrectionFactor_12_cloudcover done&lt;br /&gt;
setstate Forecast 2025-01-13 13:00:02 .pvCorrectionFactor_13_apipercentil done&lt;br /&gt;
setstate Forecast 2025-01-13 13:00:02 .pvCorrectionFactor_13_cloudcover done&lt;br /&gt;
setstate Forecast 2025-01-13 14:00:03 .pvCorrectionFactor_14_apipercentil done&lt;br /&gt;
setstate Forecast 2025-01-13 14:00:03 .pvCorrectionFactor_14_cloudcover done&lt;br /&gt;
setstate Forecast 2025-01-13 15:00:05 .pvCorrectionFactor_15_apipercentil done&lt;br /&gt;
setstate Forecast 2025-01-13 15:00:05 .pvCorrectionFactor_15_cloudcover done&lt;br /&gt;
setstate Forecast 2025-01-13 16:00:06 .pvCorrectionFactor_16_apipercentil done&lt;br /&gt;
setstate Forecast 2025-01-13 16:00:06 .pvCorrectionFactor_16_cloudcover done&lt;br /&gt;
setstate Forecast 2025-01-13 17:00:05 .pvCorrectionFactor_17_apipercentil done&lt;br /&gt;
setstate Forecast 2025-01-13 17:00:05 .pvCorrectionFactor_17_cloudcover done&lt;br /&gt;
setstate Forecast 2025-01-13 18:00:04 .pvCorrectionFactor_18_apipercentil done&lt;br /&gt;
setstate Forecast 2025-01-13 18:00:04 .pvCorrectionFactor_18_cloudcover done&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 .pvCorrectionFactor_Auto_Soll on_complex_ai&lt;br /&gt;
setstate Forecast 2025-01-13 01:00:05 .signaldone_01 done&lt;br /&gt;
setstate Forecast 2025-01-13 02:00:04 .signaldone_02 done&lt;br /&gt;
setstate Forecast 2025-01-13 03:00:04 .signaldone_03 done&lt;br /&gt;
setstate Forecast 2025-01-13 04:00:05 .signaldone_04 done&lt;br /&gt;
setstate Forecast 2025-01-13 05:00:05 .signaldone_05 done&lt;br /&gt;
setstate Forecast 2025-01-13 06:00:06 .signaldone_06 done&lt;br /&gt;
setstate Forecast 2025-01-13 07:00:04 .signaldone_07 done&lt;br /&gt;
setstate Forecast 2025-01-13 08:00:03 .signaldone_08 done&lt;br /&gt;
setstate Forecast 2025-01-13 09:00:04 .signaldone_09 done&lt;br /&gt;
setstate Forecast 2025-01-13 10:00:05 .signaldone_10 done&lt;br /&gt;
setstate Forecast 2025-01-13 11:00:05 .signaldone_11 done&lt;br /&gt;
setstate Forecast 2025-01-13 12:00:02 .signaldone_12 done&lt;br /&gt;
setstate Forecast 2025-01-13 13:00:02 .signaldone_13 done&lt;br /&gt;
setstate Forecast 2025-01-13 14:00:03 .signaldone_14 done&lt;br /&gt;
setstate Forecast 2025-01-13 15:00:05 .signaldone_15 done&lt;br /&gt;
setstate Forecast 2025-01-13 16:00:06 .signaldone_16 done&lt;br /&gt;
setstate Forecast 2025-01-13 17:00:05 .signaldone_17 done&lt;br /&gt;
setstate Forecast 2025-01-13 18:00:04 .signaldone_18 done&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Battery_ChargeUnrestricted_01 1&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Battery_ChargeUnrestricted_02 1&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Battery_ChargeRequest_01 1&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Battery_ChargeRequest_02 1&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Battery_NextHour00_SoCforecast_01 10.0 %&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Battery_NextHour00_SoCforecast_02 10.0 %&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Battery_NextHour01_SoCforecast_01 10.0 %&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Battery_NextHour01_SoCforecast_02 10.0 %&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Battery_NextHour02_SoCforecast_01 10.0 %&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Battery_NextHour02_SoCforecast_02 10.0 %&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Battery_NextHour03_SoCforecast_01 10.0 %&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Battery_NextHour03_SoCforecast_02 10.0 %&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Battery_NextHour04_SoCforecast_01 10.0 %&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Battery_NextHour04_SoCforecast_02 10.0 %&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Battery_NextHour05_SoCforecast_01 10.0 %&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Battery_NextHour05_SoCforecast_02 10.0 %&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Battery_NextHour06_SoCforecast_01 10.0 %&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Battery_NextHour06_SoCforecast_02 10.0 %&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Battery_NextHour07_SoCforecast_01 10.0 %&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Battery_NextHour07_SoCforecast_02 10.0 %&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Battery_NextHour08_SoCforecast_01 10.0 %&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Battery_NextHour08_SoCforecast_02 10.0 %&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Battery_NextHour09_SoCforecast_01 10.0 %&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Battery_NextHour09_SoCforecast_02 10.0 %&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Battery_NextHour10_SoCforecast_01 10.0 %&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Battery_NextHour10_SoCforecast_02 10.0 %&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Battery_NextHour11_SoCforecast_01 10.0 %&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Battery_NextHour11_SoCforecast_02 10.0 %&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Battery_NextHour12_SoCforecast_01 10.0 %&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Battery_NextHour12_SoCforecast_02 10.0 %&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Battery_NextHour13_SoCforecast_01 10.0 %&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Battery_NextHour13_SoCforecast_02 10.0 %&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Battery_NextHour14_SoCforecast_01 10.0 %&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Battery_NextHour14_SoCforecast_02 10.0 %&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Battery_NextHour15_SoCforecast_01 10.0 %&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Battery_NextHour15_SoCforecast_02 10.0 %&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Battery_NextHour16_SoCforecast_01 10.0 %&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Battery_NextHour16_SoCforecast_02 10.0 %&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Battery_NextHour17_SoCforecast_01 10.0 %&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Battery_NextHour17_SoCforecast_02 10.0 %&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Battery_NextHour18_SoCforecast_01 40.0 %&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Battery_NextHour18_SoCforecast_02 40.0 %&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Battery_NextHour19_SoCforecast_01 40.0 %&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Battery_NextHour19_SoCforecast_02 40.0 %&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Battery_NextHour20_SoCforecast_01 42.6 %&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Battery_NextHour20_SoCforecast_02 42.6 %&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Battery_NextHour21_SoCforecast_01 40.0 %&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Battery_NextHour21_SoCforecast_02 40.0 %&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Battery_NextHour22_SoCforecast_01 40.0 %&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Battery_NextHour22_SoCforecast_02 40.0 %&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Battery_NextHour23_SoCforecast_01 40.0 %&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Battery_NextHour23_SoCforecast_02 40.0 %&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Battery_OptimumTargetSoC_01 40 %&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Battery_OptimumTargetSoC_02 40 %&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Current_AutarkyRate 83 %&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:55 Current_BatCharge999 10&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Current_BatCharge_01 12 %&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Current_BatCharge_02 8 %&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Current_Consumption 626 W&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Current_GridConsumption 2 W&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Current_GridFeedIn 0 W&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Current_PP_01 746.0 W&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Current_PV 0 W&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Current_PowerBatIn_01 0 W&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Current_PowerBatIn_02 132 W&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Current_PowerBatOut_01 10 W&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Current_PowerBatOut_02 0 W&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Current_SelfConsumption 0 W&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Current_SelfConsumptionRate 0 %&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Current_Surplus 120 W&lt;br /&gt;
setstate Forecast 2025-01-13 18:00:00 LastHourGridconsumptionReal 5 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 18:00:00 LastHourPVforecast 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 18:00:00 LastHourPVreal 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 NextHours_Sum01_PVforecast 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 NextHours_Sum02_PVforecast 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 NextHours_Sum03_PVforecast 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 NextHours_Sum04_ConsumptionForecast 2944 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 NextHours_Sum04_PVforecast 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 RestOfDayConsumptionForecast 3609 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 RestOfDayPVforecast 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 00:59:59 Today_Hour01_BatIn_01 272 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 00:59:59 Today_Hour01_BatIn_02 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 00:59:59 Today_Hour01_BatOut_01 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 00:59:59 Today_Hour01_BatOut_02 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 00:59:59 Today_Hour01_GridConsumption 1 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 00:59:59 Today_Hour01_GridFeedIn 1 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 00:59:59 Today_Hour01_PPreal_01 748 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 00:59:59 Today_Hour01_PVreal 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 01:59:58 Today_Hour02_BatIn_01 266 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 01:59:58 Today_Hour02_BatIn_02 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 01:59:58 Today_Hour02_BatOut_01 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 01:59:58 Today_Hour02_BatOut_02 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 01:59:58 Today_Hour02_GridConsumption 1 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 01:59:58 Today_Hour02_GridFeedIn 1 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 01:59:58 Today_Hour02_PPreal_01 787 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 01:59:58 Today_Hour02_PVreal 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 02:59:53 Today_Hour03_BatIn_01 271 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 02:59:53 Today_Hour03_BatIn_02 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 02:59:53 Today_Hour03_BatOut_01 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 02:59:53 Today_Hour03_BatOut_02 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 02:59:53 Today_Hour03_GridConsumption 2 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 02:59:53 Today_Hour03_GridFeedIn 2 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 02:59:53 Today_Hour03_PPreal_01 750 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 02:59:53 Today_Hour03_PVreal 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 03:59:52 Today_Hour04_BatIn_01 266 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 03:59:52 Today_Hour04_BatIn_02 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 03:59:52 Today_Hour04_BatOut_01 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 03:59:52 Today_Hour04_BatOut_02 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 03:59:52 Today_Hour04_GridConsumption 1 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 03:59:52 Today_Hour04_GridFeedIn 1 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 03:59:52 Today_Hour04_PPreal_01 745 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 03:59:52 Today_Hour04_PVreal 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 04:59:56 Today_Hour05_BatIn_01 267 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 04:59:56 Today_Hour05_BatIn_02 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 04:59:56 Today_Hour05_BatOut_01 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 04:59:56 Today_Hour05_BatOut_02 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 04:59:56 Today_Hour05_GridConsumption 2 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 04:59:56 Today_Hour05_GridFeedIn 2 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 04:59:56 Today_Hour05_PPreal_01 758 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 04:59:56 Today_Hour05_PVreal 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 05:59:59 Today_Hour06_BatIn_01 255 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 05:59:59 Today_Hour06_BatIn_02 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 05:59:59 Today_Hour06_BatOut_01 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 05:59:59 Today_Hour06_BatOut_02 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 05:59:59 Today_Hour06_GridConsumption 1 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 05:59:59 Today_Hour06_GridFeedIn 1 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 05:59:59 Today_Hour06_PPreal_01 752 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 05:59:59 Today_Hour06_PVreal 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 06:59:52 Today_Hour07_BatIn_01 226 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 06:59:52 Today_Hour07_BatIn_02 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 06:59:52 Today_Hour07_BatOut_01 50 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 06:59:52 Today_Hour07_BatOut_02 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 06:59:52 Today_Hour07_GridConsumption 2 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 06:59:52 Today_Hour07_GridFeedIn 2 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 06:59:52 Today_Hour07_PPreal_01 681 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 06:59:52 Today_Hour07_PVreal 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 07:59:50 Today_Hour08_BatIn_01 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 07:59:50 Today_Hour08_BatIn_02 233 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 07:59:50 Today_Hour08_BatOut_01 845 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 07:59:50 Today_Hour08_BatOut_02 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 07:59:50 Today_Hour08_GridConsumption 4 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 07:59:50 Today_Hour08_GridFeedIn 4 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 07:59:50 Today_Hour08_PPreal_01 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 07:59:50 Today_Hour08_PVreal 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 08:59:57 Today_Hour09_BatIn_01 37 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 08:59:57 Today_Hour09_BatIn_02 90 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 08:59:57 Today_Hour09_BatOut_01 351 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 08:59:57 Today_Hour09_BatOut_02 188 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 08:59:57 Today_Hour09_GridConsumption 447 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 08:59:57 Today_Hour09_GridFeedIn 4 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 08:59:57 Today_Hour09_PPreal_01 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 08:59:57 Today_Hour09_PVforecast 127 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 08:59:57 Today_Hour09_PVreal 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 09:59:58 Today_Hour10_BatIn_01 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 09:59:58 Today_Hour10_BatIn_02 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 09:59:58 Today_Hour10_BatOut_01 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 09:59:58 Today_Hour10_BatOut_02 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 09:59:58 Today_Hour10_GridConsumption 1650 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 09:59:58 Today_Hour10_GridFeedIn 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 09:59:58 Today_Hour10_PPreal_01 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 09:59:58 Today_Hour10_PVforecast 478 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 09:59:58 Today_Hour10_PVreal 26 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 10:59:58 Today_Hour11_BatIn_01 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 10:59:58 Today_Hour11_BatIn_02 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 10:59:58 Today_Hour11_BatOut_01 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 10:59:58 Today_Hour11_BatOut_02 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 10:59:58 Today_Hour11_GridConsumption 513 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 10:59:58 Today_Hour11_GridFeedIn 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 10:59:58 Today_Hour11_PPreal_01 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 10:59:58 Today_Hour11_PVforecast 868 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 10:59:58 Today_Hour11_PVreal 97 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 11:59:49 Today_Hour12_BatIn_01 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 11:59:49 Today_Hour12_BatIn_02 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 11:59:49 Today_Hour12_BatOut_01 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 11:59:49 Today_Hour12_BatOut_02 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 11:59:49 Today_Hour12_GridConsumption 576 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 11:59:49 Today_Hour12_GridFeedIn 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 11:59:49 Today_Hour12_PPreal_01 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 11:59:49 Today_Hour12_PVforecast 1157 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 11:59:49 Today_Hour12_PVreal 320 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 12:59:49 Today_Hour13_BatIn_01 8 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 12:59:49 Today_Hour13_BatIn_02 4 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 12:59:49 Today_Hour13_BatOut_01 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 12:59:49 Today_Hour13_BatOut_02 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 12:59:49 Today_Hour13_GridConsumption 1272 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 12:59:49 Today_Hour13_GridFeedIn 7 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 12:59:49 Today_Hour13_PPreal_01 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 12:59:49 Today_Hour13_PVforecast 1238 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 12:59:49 Today_Hour13_PVreal 945 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 13:59:49 Today_Hour14_BatIn_01 410 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 13:59:49 Today_Hour14_BatIn_02 53 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 13:59:49 Today_Hour14_BatOut_01 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 13:59:49 Today_Hour14_BatOut_02 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 13:59:49 Today_Hour14_GridConsumption 955 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 13:59:49 Today_Hour14_GridFeedIn 31 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 13:59:49 Today_Hour14_PPreal_01 562 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 13:59:49 Today_Hour14_PVforecast 1168 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 13:59:49 Today_Hour14_PVreal 1410 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 14:59:49 Today_Hour15_BatIn_01 703 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 14:59:49 Today_Hour15_BatIn_02 500 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 14:59:49 Today_Hour15_BatOut_01 22 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 14:59:49 Today_Hour15_BatOut_02 18 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 14:59:49 Today_Hour15_GridConsumption 32 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 14:59:49 Today_Hour15_GridFeedIn 71 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 14:59:49 Today_Hour15_PPreal_01 777 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 14:59:49 Today_Hour15_PVforecast 1630 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 14:59:49 Today_Hour15_PVreal 1416 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 15:59:50 Today_Hour16_BatIn_01 296 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 15:59:50 Today_Hour16_BatIn_02 70 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 15:59:50 Today_Hour16_BatOut_01 74 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 15:59:50 Today_Hour16_BatOut_02 258 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 15:59:50 Today_Hour16_GridConsumption 13 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 15:59:50 Today_Hour16_GridFeedIn 17 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 15:59:50 Today_Hour16_PPreal_01 732 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 15:59:50 Today_Hour16_PVforecast 880 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 15:59:50 Today_Hour16_PVreal 1051 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 16:59:49 Today_Hour17_BatIn_01 47 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 16:59:49 Today_Hour17_BatIn_02 126 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 16:59:49 Today_Hour17_BatOut_01 102 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 16:59:49 Today_Hour17_BatOut_02 75 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 16:59:49 Today_Hour17_GridConsumption 15 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 16:59:49 Today_Hour17_GridFeedIn 23 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 16:59:49 Today_Hour17_PPreal_01 771 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 16:59:49 Today_Hour17_PVforecast 117 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 16:59:49 Today_Hour17_PVreal 86 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 17:59:54 Today_Hour18_BatIn_01 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 17:59:54 Today_Hour18_BatIn_02 410 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 17:59:54 Today_Hour18_BatOut_01 758 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 17:59:54 Today_Hour18_BatOut_02 67 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 17:59:54 Today_Hour18_GridConsumption 5 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 17:59:54 Today_Hour18_GridFeedIn 5 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 17:59:54 Today_Hour18_PPreal_01 723 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 17:59:54 Today_Hour18_PVreal 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Today_Hour19_BatIn_01 349 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Today_Hour19_BatIn_02 124 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Today_Hour19_BatOut_01 73 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Today_Hour19_BatOut_02 450 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Today_Hour19_GridConsumption 8 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Today_Hour19_GridFeedIn 6 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Today_Hour19_PPreal_01 671 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Today_Hour19_PVreal 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Today_MaxPVforecast 1630 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Today_MaxPVforecastTime 2025-01-13 14:00:00&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Today_PVdeviation 30.17 %&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Today_PVforecast 7663 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Today_PVreal 5351 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Today_SunRise 08:28&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Today_SunSet 16:46&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Tomorrow_ConsumptionForecast 17043 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Tomorrow_Hour01_PVforecast 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Tomorrow_Hour02_PVforecast 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Tomorrow_Hour03_PVforecast 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Tomorrow_Hour04_PVforecast 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Tomorrow_Hour05_PVforecast 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Tomorrow_Hour06_PVforecast 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Tomorrow_Hour07_PVforecast 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Tomorrow_Hour08_PVforecast 10 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Tomorrow_Hour09_PVforecast 161 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Tomorrow_Hour10_PVforecast 275 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Tomorrow_Hour11_PVforecast 532 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Tomorrow_Hour12_PVforecast 730 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Tomorrow_Hour13_PVforecast 1252 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Tomorrow_Hour14_PVforecast 818 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Tomorrow_Hour15_PVforecast 1465 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Tomorrow_Hour16_PVforecast 303 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Tomorrow_Hour17_PVforecast 20 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Tomorrow_Hour18_PVforecast 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Tomorrow_Hour19_PVforecast 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Tomorrow_Hour20_PVforecast 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Tomorrow_Hour21_PVforecast 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Tomorrow_Hour22_PVforecast 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Tomorrow_Hour23_PVforecast 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Tomorrow_Hour24_PVforecast 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Tomorrow_PVforecast 5566 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Tomorrow_SunRise 08:27&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Tomorrow_SunSet 16:47&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 consumer01 name=&#039;_Waschmaschine&#039; state=&#039;off&#039; mode=&#039;can&#039; planningstate=&#039;planned&#039;&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 consumer01_currentPower 0 W&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 consumer01_planned_start 14.01.2025 09:00:00&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 consumer01_planned_stop 14.01.2025 11:00:00&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 consumer02 name=&#039;_Esszimmer&#039; state=&#039;on&#039; mode=&#039;can&#039; planningstate=&#039;planned&#039;&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 consumer02_currentPower 15.3 W&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 consumer02_planned_start 14.01.2025 08:00:00&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 consumer02_planned_stop 14.01.2025 09:00:00&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 consumer03 name=&#039;_FCU&#039; state=&#039;on&#039; mode=&#039;can&#039; planningstate=&#039;planned&#039;&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 consumer03_currentPower 7.15 W&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 consumer03_planned_start 14.01.2025 07:00:00&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 consumer03_planned_stop 14.01.2025 08:00:00&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 consumer04 name=&#039;_Brunnen&#039; state=&#039;off&#039; mode=&#039;can&#039; planningstate=&#039;planned&#039;&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 consumer04_currentPower 0 W&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 consumer04_planned_start 14.01.2025 08:00:00&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 consumer04_planned_stop 14.01.2025 09:00:00&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 consumer05 name=&#039;_Zisterne&#039; state=&#039;off&#039; mode=&#039;can&#039; planningstate=&#039;planned&#039;&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 consumer05_currentPower 0 W&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 consumer05_planned_start 14.01.2025 07:00:00&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 consumer05_planned_stop 14.01.2025 08:00:00&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 consumer06 name=&#039;SW Trockner&#039; state=&#039;unknown&#039; mode=&#039;can&#039; planningstate=&#039;planned&#039;&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 consumer06_currentPower 0 W&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 consumer06_planned_start 14.01.2025 07:00:00&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 consumer06_planned_stop 14.01.2025 08:30:00&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 consumer07 name=&#039;SW Urlaubslicht-Wohnzimmer&#039; state=&#039;on&#039; mode=&#039;can&#039; planningstate=&#039;noSchedule&#039; info=&#039;von extern umgeschaltet&#039;&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 consumer07_currentPower 0 W&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 nextCycletime 18:54:09&lt;br /&gt;
setstate Forecast 2025-01-13 10:00:05 pvCorrectionFactor_10 0.53 (automatic - old factor: 1.00, Sun Alt range: 5, Cloud range: 75, Days in range: 1)&lt;br /&gt;
setstate Forecast 2025-01-13 11:00:05 pvCorrectionFactor_11 0.56 (automatic - old factor: 1.00, Sun Alt range: 10, Cloud range: 70, Days in range: 1)&lt;br /&gt;
setstate Forecast 2025-01-13 12:00:02 pvCorrectionFactor_12 0.64 (automatic - old factor: 1.00, Sun Alt range: 15, Cloud range: 60, Days in range: 1)&lt;br /&gt;
setstate Forecast 2025-01-13 13:00:02 pvCorrectionFactor_13 0.88 (automatic - old factor: 1.00, Sun Alt range: 15, Cloud range: 60, Days in range: 1)&lt;br /&gt;
setstate Forecast 2025-01-13 14:00:03 pvCorrectionFactor_14 1.10 (automatic - old factor: 1.00, Sun Alt range: 15, Cloud range: 50, Days in range: 1)&lt;br /&gt;
setstate Forecast 2025-01-13 15:00:05 pvCorrectionFactor_15 1.01 (automatic - old factor: 1.06, Sun Alt range: 15, Cloud range: 45, Days in range: 2)&lt;br /&gt;
setstate Forecast 2025-01-13 16:00:06 pvCorrectionFactor_16 1.09 (automatic - old factor: 1.00, Sun Alt range: 10, Cloud range: 45, Days in range: 1)&lt;br /&gt;
setstate Forecast 2025-01-13 17:00:05 pvCorrectionFactor_17 0.28 (automatic - old factor: 0.52, Sun Alt range: 0, Cloud range: 45, Days in range: 2)&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 pvCorrectionFactor_Auto on_complex_ai&lt;br /&gt;
setstate Forecast 2024-10-30 19:42:19 setupStringAzimuth GarageSE=-55 GarageNW=125 HausNW=125 HausSW=35 HausSE1=-55 HausSE2=-55&lt;br /&gt;
setstate Forecast 2024-10-30 19:37:38 setupStringDeclination GarageSE=40 GarageNW=40 HausNW=50 HausSW=50 HausSE1=50 HausSE2=50&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 special_BatPowerIn_Sum 132 W&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 special_BatPowerOut_Sum 10 W&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 special_SunHours_Remain 0.00&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 special_SunMinutes_Remain 0&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 special_allStringsFullfilled 1&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 special_conForecastTillNextSunrise 9467 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 special_currentAPIinterval 0&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 special_currentRunMtsConsumer_01 132 min&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 special_currentRunMtsConsumer_02 184 min&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 special_currentRunMtsConsumer_03 329512 min&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 special_currentRunMtsConsumer_04 200 min&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 special_currentRunMtsConsumer_05 27 min&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 special_currentRunMtsConsumer_06 193 min&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 special_currentRunMtsConsumer_07 106 min&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 special_dayAfterTomorrowPVforecast 7344 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 special_daysUntilBatteryCare_01 12&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 special_daysUntilBatteryCare_02 12&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 special_lastretrieval_time 2025-01-13 18:53:54&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 special_lastretrieval_timestamp 1736790834&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 special_response_message success&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 special_runTimeAvgDayConsumer_01 545.94 min&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 special_runTimeAvgDayConsumer_02 382.10 min&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 special_runTimeAvgDayConsumer_03 1430.13 min&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 special_runTimeAvgDayConsumer_04 262.05 min&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 special_runTimeAvgDayConsumer_05 28.80 min&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 special_runTimeAvgDayConsumer_06 622.63 min&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 special_runTimeAvgDayConsumer_07 276.51 min&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 special_runTimeCentralTask 0.3744&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 special_runTimeLastAPIAnswer -&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 special_runTimeLastAPIProc -&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 special_runTimeTrainAI 1.2529&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 special_todayBatIn_01 3683.0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 special_todayBatIn_02 1617.0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 special_todayBatOut_01 2287.0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 special_todayBatOut_02 1057.0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 special_todayConForecastTillSunset 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 00:59:59 special_todayConsumptionForecast_01 509 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 01:59:58 special_todayConsumptionForecast_02 499 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 02:59:53 special_todayConsumptionForecast_03 501 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 03:59:52 special_todayConsumptionForecast_04 511 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 04:59:56 special_todayConsumptionForecast_05 503 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 05:59:59 special_todayConsumptionForecast_06 495 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 06:59:52 special_todayConsumptionForecast_07 504 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 07:59:50 special_todayConsumptionForecast_08 561 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 08:59:57 special_todayConsumptionForecast_09 768 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 09:59:58 special_todayConsumptionForecast_10 783 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 10:59:58 special_todayConsumptionForecast_11 692 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 11:59:49 special_todayConsumptionForecast_12 791 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 12:59:49 special_todayConsumptionForecast_13 910 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 13:59:49 special_todayConsumptionForecast_14 1022 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 14:59:49 special_todayConsumptionForecast_15 965 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 15:59:50 special_todayConsumptionForecast_16 812 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 16:59:49 special_todayConsumptionForecast_17 884 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 17:59:54 special_todayConsumptionForecast_18 928 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 special_todayConsumptionForecast_19 874 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 special_todayConsumptionForecast_20 763 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 special_todayConsumptionForecast_21 750 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 special_todayConsumptionForecast_22 765 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 special_todayConsumptionForecast_23 643 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 special_todayConsumptionForecast_24 601 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 special_todayDoneAPIcalls 0&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 special_todayDoneAPIrequests 4532&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 special_todayGridConsumption 5500.9 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 special_todayGridFeedIn 178.9 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 special_todayMaxAPIcalls n.a.&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 special_todayRemainingAPIcalls n.a.&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 special_todayRemainingAPIrequests n.a.&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:55 state updated&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
So sollte es dann als Ergebnis aussehen:&lt;br /&gt;
[[Datei:Bildschirmfoto 2025-01-13.png|zentriert|mini|Ergebnis nach kompletter Einrichtung]]&lt;br /&gt;
[[Datei:Bildschirmfoto 2025-01-13 Teil II.png|alternativtext=Unterer Teil der Anzeige|zentriert|mini]]&lt;br /&gt;
&lt;br /&gt;
== Praxisbeispiele und Lösungsansätze für Steuerungen ==&lt;br /&gt;
=== Fallstudie: Trockner darf pausiert werden, wenn nicht genug PV-Überschuss vorhanden ist, soll aber nach spätestens X Stunden fertig sein ===&lt;br /&gt;
Für diese Aufgabenstellung sind mehrere Schlüssel essentiell wichtig, &#039;&#039;&#039;power&#039;&#039;&#039;, &#039;&#039;&#039;mintime&#039;&#039;&#039;, &#039;&#039;&#039;mode&#039;&#039;&#039; und &#039;&#039;&#039;interruptable&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Die Bedingung, dass der Trockner nach spätestens X Stunden fertig sein soll, steht in einem Widerspruch zu der Möglichkeit den Trockner bei zu wenig PV-Überschuss unterbrechen zu können. Im Extremfall könnte nach dem Start die gesamte Planungszeit ungenügender Überschuss vorliegen und der Trockner nicht arbeiten. Der Schlüssel &#039;&#039;&#039;mintime&#039;&#039;&#039; (in Minuten) wird nun wie folgt angesetzt:&lt;br /&gt;
&lt;br /&gt;
 mintime = 2,5 x &amp;lt;gewöhnliche Betriebszeit für einen Trockengang (mneed, z.B. 120)&amp;gt; = 300 Minuten&lt;br /&gt;
&lt;br /&gt;
Das bedeutet, der Trockner wird nach 300 Minuten Laufzeit abgeschaltet werden bzw. vom Netz getrennt. Der Trockenvorgang wird natürlich bereits nach 120 Minuten beendet sein, sofern nach Start keine Unterbrechnung des Vorganges erfolgte. Wurde der Vorgang unterbrochen, soll der Trockner innerhalb &lt;br /&gt;
&lt;br /&gt;
 X+120 bis X+300 &lt;br /&gt;
&lt;br /&gt;
fertig sein. &lt;br /&gt;
&lt;br /&gt;
Demnach ist die ausgeführte Laufzeit des Trockners permanent zu überwachen, zu summieren (rtsum) und die notwendige Restzeit (mneed - rtsum) mit der noch verbleibenden Restzeit (mrest) bis zur geplanten Abschaltung des Consumers zu vergleichen. Ist der Consumer gestartet, wird die Laufzeit in der pvHistory für jede Stunde des Tages im Schlüssel &#039;&#039;minutescsmXX&#039;&#039; (XX = Consumernummer) aufgezeichnet und kann darüber ausgewertet werden. Somit ist:&lt;br /&gt;
&lt;br /&gt;
 msum = for (01 .. 24) {Summe von minutescsmXX} des aktuellen Tages&lt;br /&gt;
&lt;br /&gt;
Solange die Bedingung &lt;br /&gt;
&lt;br /&gt;
 mrest &amp;gt;= (mneed - msum)&lt;br /&gt;
&lt;br /&gt;
zutrifft, darf der Trockner unterbrochen werden, anderenfalls darf er nicht mehr unterbrochen werden da die Restzeit evtl. nicht ausreichen könnte um den Vorgang abzuschließen. Für die Planung des Trockners wird der Consumer wie gewöhnlich nach dem Bedarf im Modul registriert. Für die Steuerung der Unterbrechbarkeit wird noch ein Code erstellt, welcher im Trockner-Device &#039;&#039;Dryerdev&#039;&#039; das Reading &#039;&#039;SF_Int&#039;&#039; zur Unterbrechnungssteuerung verwaltet. Dieses Reading wird dem Schlüssel &#039;&#039;&#039;interruptable&#039;&#039;&#039; übergeben. Für das Beispiel wird der Consumer &#039;07&#039; verwendet, es kann natürlich jede andere freie Nummer verwendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 attr &amp;lt;Name&amp;gt; consumer07 Dryerdev:Trockner&lt;br /&gt;
                        icon=scene_cloves_dryer type=dryer power=2000 mode=must mintime=300 &lt;br /&gt;
                        on=on off=off etotal=energy_Wh:Wh pcurr=power:W&lt;br /&gt;
                        auto=automatic asynchron=1&lt;br /&gt;
                        interruptable=Dryerdev:SF_Int:1&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Kombination &#039;&#039;&#039;auto=automatic&#039;&#039;&#039; eröffnet die Möglickeit, in der Consumer-Legende den Trockner-Start zusätzlich manuell freizugeben oder zu verbieten. &lt;br /&gt;
&lt;br /&gt;
Im nachfolgenden Code werden modulinterne Subroutinen verwendet. Diese sind inklusive dem Paket &#039;&#039;FHEM::SolarForecast::&#039;&#039; anzugeben. Die erstellte Subroutine wird in die 99_mySolarForecastUtils.pm eingefügt. Die Datei 99_mySolarForecastUtils.pm wird vorher von 99_myUtils.pm kopiert und enthält für die Übersichtlichkeit nur Routinen für SolarForecast.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
############################################################################&lt;br /&gt;
#   Trocknersteuerung (einfügen in 99_mySolarForecastUtils.pm) &lt;br /&gt;
############################################################################&lt;br /&gt;
sub dryControl {&lt;br /&gt;
  my $name  = shift;&lt;br /&gt;
  my $c     = shift;                                                              # Nummer des Verbrauchers, z.B. 07&lt;br /&gt;
  my $mneed = shift;                                                              # gewöhlich benötigte Zeit für &lt;br /&gt;
                                                                                  # einen Trockengang, z.B. 120 (Minuten)&lt;br /&gt;
  &lt;br /&gt;
  $c            = sprintf &amp;quot;%02d&amp;quot;, $c;                                             # falls führende 0 vergessen wird&lt;br /&gt;
  my $dryer     = FHEM::SolarForecast::ConsumerVal ($name, $c, &#039;name&#039;, &#039;&#039;);       # Devicename des Trockners&lt;br /&gt;
  my $plstate   = FHEM::SolarForecast::ConsumerVal ($name, $c, &#039;planstate&#039;, &#039;&#039;); &lt;br /&gt;
  my $simpCstat = FHEM::SolarForecast::simplifyCstate ($plstate);                 # akt. Status des Consumers&lt;br /&gt;
&lt;br /&gt;
  if ($simpCstat =~ /started|interrupt|continu/xs) {                              # Vorgang ist gestartet&lt;br /&gt;
      my $t       = time;&lt;br /&gt;
      my $startts = FHEM::SolarForecast::ConsumerVal ($name, $c, &#039;planswitchon&#039;,  &#039;&#039;);&lt;br /&gt;
      my $stopts  = FHEM::SolarForecast::ConsumerVal ($name, $c, &#039;planswitchoff&#039;, &#039;&#039;);&lt;br /&gt;
      return if(!$startts || !$stopts);&lt;br /&gt;
      &lt;br /&gt;
      my $mrest = sprintf &#039;%.0f&#039;, (($stopts - $t) / 60);                         # Restlaufzeit (Minuten)      &lt;br /&gt;
      my $dt     = FHEM::SolarForecast::timestringsFromOffset ($startts, 0);&lt;br /&gt;
      my $day    = $dt-&amp;gt;{day};&lt;br /&gt;
      my $hstart = int $dt-&amp;gt;{hour} + 1;                                          # lfd. Stunde bei Trockner-Start &lt;br /&gt;
      my $msum   = 0;&lt;br /&gt;
	  &lt;br /&gt;
      for my $hod (1..24) {                                                      # bisherige Laufzeit des Trockners&lt;br /&gt;
          next if($hod &amp;lt; $hstart);&lt;br /&gt;
          $hod = sprintf &amp;quot;%02d&amp;quot;, $hod;&lt;br /&gt;
          $msum += FHEM::SolarForecast::HistoryVal ($name, $day, $hod, &amp;quot;minutescsm${c}&amp;quot;, 0);&lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
      my $dhash = $defs{$dryer};&lt;br /&gt;
&lt;br /&gt;
      if ($mrest &amp;gt;= ($mneed - $msum)) {&lt;br /&gt;
          readingsSingleUpdate ($dhash, &#039;SF_Int&#039;, 1, 0);                        # Interrupt-Freigabe&lt;br /&gt;
      }&lt;br /&gt;
      else {&lt;br /&gt;
          readingsSingleUpdate ($dhash, &#039;SF_Int&#039;, 0, 0);                        # keine Interrupt-Freigabe&lt;br /&gt;
  &lt;br /&gt;
      }&lt;br /&gt;
  }&lt;br /&gt;
  &lt;br /&gt;
return;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Damit die Routine mit jedem Zyklus im Modul ausgeführt wird, legen wir sie im Attribut &#039;&#039;&#039;ctrlUserExitFn&#039;&#039;&#039; an. Es werden der Name des SolarForecast-Devices, die Verbrauchernummer (07) und die gewöhnliche Laufzeit des Trockners für einen Trockenvorgang (120 Minuten) übergeben:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
attr &amp;lt;Name&amp;gt; ctrlUserExitFn  {  &lt;br /&gt;
                               ::dryControl ($name, &#039;07&#039;, 120);&lt;br /&gt;
                            }&lt;br /&gt;
&amp;lt;/pre&amp;gt; &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
=== Fallstudie: Umwälzpumpe Pool soll X Stunden am Tag laufen. Ist kein PV-Überschuß vorhanden, müssen die X Stunden/Tag trotzdem erreicht werden ===&lt;br /&gt;
&lt;br /&gt;
Die Vorgehensweise ähnelt der Fallstudie zuvor. Verändernd kommt hinzu, dass der Planungszeitraum bewusst sehr lang angesetzt wird mit der Option, den Zyklus des Consumers vorfristig zu beenden sobald die Laufzeit von X Stunden am Tag erreicht ist. &lt;br /&gt;
Im folgenden Beispiel soll die Pumpe 5 Stunden am Tag (300 Minuten) laufen und dabei mögliche PV-Überschüsse ausnutzen.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 attr &amp;lt;Name&amp;gt; consumer07 Pump:Zirkulationspumpe&lt;br /&gt;
                        icon=sani_pump type=other power=50 mode=must mintime=780 notafter=09&lt;br /&gt;
                        on=on off=off etotal=energy_Wh:Wh pcurr=power:W&lt;br /&gt;
                        auto=automatic &lt;br /&gt;
                        interruptable=1 swoffcond=Pump:SF_Abort:1&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Mit dieser Registrierung wird die Pumpe ca. 09 Uhr täglich starten. Wenn kein bzw. ungenügender PV-Überschuß vorhanden ist, erfolgt eine Unterbrechung nach dem Start. Ist genügender PV-Überschuß (wieder) vorhanden, erfolgt keine Unterbrechnung (mehr). Zusätzlich wird über den Schlüssel &#039;&#039;&#039;swoffcond&#039;&#039;&#039; eine vorfristige Beendigung veranlasst, sobald die gewünschte Tageslaufzeit erreicht ist.&lt;br /&gt;
&lt;br /&gt;
Zur Verwaltung des Schlüssels &#039;&#039;interruptable&#039;&#039; und des Readings &#039;&#039;SF_Abort&#039;&#039; verwenden wir eine leicht veränderte Subroutine aus dem vorhergehenden Fallbeispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
############################################################################&lt;br /&gt;
#   Pumpensteuerung (einfügen in 99_mySolarForecastUtils.pm) &lt;br /&gt;
############################################################################&lt;br /&gt;
sub pumpControl {&lt;br /&gt;
  my $name  = shift;&lt;br /&gt;
  my $c     = shift;                                                              # Nummer des Verbrauchers, z.B. 07&lt;br /&gt;
  my $mneed = shift;                                                              # Soll-Pumpenzeit, z.B. 300 (Minuten)&lt;br /&gt;
  &lt;br /&gt;
  $c            = sprintf &amp;quot;%02d&amp;quot;, $c;                                             # falls führende 0 vergessen wird&lt;br /&gt;
  my $pump      = FHEM::SolarForecast::ConsumerVal ($name, $c, &#039;name&#039;, &#039;&#039;);       # Devicename der Pumpe&lt;br /&gt;
  my $plstate   = FHEM::SolarForecast::ConsumerVal ($name, $c, &#039;planstate&#039;, &#039;&#039;);&lt;br /&gt;
  my $intbl     = FHEM::SolarForecast::ConsumerVal ($name, $c, &#039;interruptable&#039;, 1); &lt;br /&gt;
  my $simpCstat = FHEM::SolarForecast::simplifyCstate ($plstate);                 # akt. Status des Consumers&lt;br /&gt;
  my $dhash     = $defs{$pump};&lt;br /&gt;
&lt;br /&gt;
  readingsSingleUpdate ($dhash, &#039;SF_Abort&#039;, 0, 0);                                # default keine Zyklusbeendigung&lt;br /&gt;
&lt;br /&gt;
  if ($simpCstat =~ /started|interrupt|continu/xs) {                              # Vorgang ist gestartet&lt;br /&gt;
      my $t       = time;&lt;br /&gt;
      my $startts = FHEM::SolarForecast::ConsumerVal ($name, $c, &#039;planswitchon&#039;,  &#039;&#039;);&lt;br /&gt;
      my $stopts  = FHEM::SolarForecast::ConsumerVal ($name, $c, &#039;planswitchoff&#039;, &#039;&#039;);&lt;br /&gt;
      return if(!$startts || !$stopts);&lt;br /&gt;
      &lt;br /&gt;
      my $mrest = sprintf &#039;%.0f&#039;, (($stopts - $t) / 60);                         # Restlaufzeit (Minuten)      &lt;br /&gt;
      my $dt     = FHEM::SolarForecast::timestringsFromOffset ($startts, 0);&lt;br /&gt;
      my $day    = $dt-&amp;gt;{day};&lt;br /&gt;
      my $hstart = int $dt-&amp;gt;{hour} + 1;                                          # lfd. Stunde bei Pumpen Start &lt;br /&gt;
      my $msum   = 0;&lt;br /&gt;
	  &lt;br /&gt;
      for my $hod (1..24) {                                                      # bisherige Laufzeit der Pumpe&lt;br /&gt;
          next if($hod &amp;lt; $hstart);&lt;br /&gt;
          $hod = sprintf &amp;quot;%02d&amp;quot;, $hod;&lt;br /&gt;
          $msum += FHEM::SolarForecast::HistoryVal ($name, $day, $hod, &amp;quot;minutescsm${c}&amp;quot;, 0);&lt;br /&gt;
      }&lt;br /&gt;
     &lt;br /&gt;
      if ($msum &amp;gt;= $mneed) {&lt;br /&gt;
          readingsSingleUpdate ($dhash, &#039;SF_Abort&#039;, 1, 0);                       # vorfristige Zyklusbeendigung&lt;br /&gt;
          return;&lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
      if ($mrest &amp;gt;= ($mneed - $msum)) {                        &lt;br /&gt;
          fhem (&amp;quot;set $name attrKeyVal consumer$c interruptable=1&amp;quot;) if(!$intbl);  # Interrupt-Freigabe&lt;br /&gt;
      }&lt;br /&gt;
      else {                        &lt;br /&gt;
          fhem (&amp;quot;set $name attrKeyVal consumer$c interruptable=0&amp;quot;) if($intbl);   # keine Interrupt-Freigabe&lt;br /&gt;
      }&lt;br /&gt;
  }&lt;br /&gt;
  &lt;br /&gt;
return;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der Befehl &#039;&#039;attrKeyVal&#039;&#039; setzt den Schlüssel &#039;&#039;interruptable&#039;&#039; im Attribut &#039;&#039;consumer07&#039;&#039; dynamisch. Diese Strukturänderung wird automatisch im System gespeichert, sofern &#039;&#039;global-&amp;gt;autosave=1&#039;&#039; gesetzt ist. Diese Einstellung ist der FHEM default.  &lt;br /&gt;
&lt;br /&gt;
Damit die Routine mit jedem Zyklus im Modul ausgeführt wird, legen wir sie im Attribut &#039;&#039;&#039;ctrlUserExitFn&#039;&#039;&#039; an. Es werden der Name des SolarForecast-Devices, die Verbrauchernummer (07) und die Mindestlaufzeit der Umwälzpumpe (300 Minuten) übergeben:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
attr &amp;lt;Name&amp;gt; ctrlUserExitFn  {  &lt;br /&gt;
                               ::pumpControl ($name, &#039;07&#039;, 300);&lt;br /&gt;
                            }&lt;br /&gt;
&amp;lt;/pre&amp;gt; &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Dynamische Ladestromsteuerung eines Victron MultiPlus II Chargers mit Pylontech Batterie ===&lt;br /&gt;
Die nachfolgend beschriebene Lösung soll als Anregung für eigene Optimierungen verstanden werden und ist weiter ausbaufähig bzw. erweiterbar um zum Beipiel jahreszeitliche Anpassungen des Verfahrens.&lt;br /&gt;
&lt;br /&gt;
Die MultiPlus II Batteriewechselrichter von Victron sollten die Batterien nicht mit dem maximal möglichen Ladestrom beladen, da sich die Effizienz deutlich verschlechtert wenn sich die Ströme den jeweiligen Grenzwerten nähern ([https://www.victronenergy.com/upload/documents/Output-rating-operating-temperature-and-efficiency.pdf Lesestoff]). &lt;br /&gt;
Andererseits sollen die Ladeleistungen so gewählt werden, dass die verfügbare Solarenergie eine volle Aufladung der Akkus ermöglicht.&lt;br /&gt;
&lt;br /&gt;
Das Victron System ermöglicht das Auslesen und die Steuerung der Anlage via MQTT. Die Einbindung in FHEM über MQTT soll hier nicht beschrieben werden.&lt;br /&gt;
Aber wenn dies erfolgt ist, kann der Ladestrom z.B. mit dem Kommando:&lt;br /&gt;
&lt;br /&gt;
 set &amp;lt;Device&amp;gt; MaxChargeCurrent &amp;lt;Wert&amp;gt;&lt;br /&gt;
&lt;br /&gt;
eingestellt bzw. variiert werden.&lt;br /&gt;
&lt;br /&gt;
Das Modul bietet dem Nutzer die Möglichkeit in dem Attribut &#039;&#039;ctrlUserExitFn&#039;&#039; eigenen Code zur Auführung zu bringen. Der in diesem Attribut enthaltene Code wird am Ende jedes Zyklus (siehe Attribut &#039;&#039;plantControl-&amp;gt;cycleInterval&#039;&#039;) ausgeführt.&lt;br /&gt;
In dem Attribut wird dieser Code eingetragen dessen Funktion und Zusammenspiel nachfolgend erläutert wird:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
{ &lt;br /&gt;
  ## Vars&lt;br /&gt;
  ########&lt;br /&gt;
  my $batdev  = (split &amp;quot; &amp;quot;, ReadingsVal ($name, &#039;currentBatteryDev&#039;, &#039;&#039;))[0];  # Batteriedevice&lt;br /&gt;
  my $vebus   = &#039;MQTT2_cerboGX_c0619ab34e08_vebus&#039;;                            # Victron Vebus Device &lt;br /&gt;
  my $mppt1   = &#039;MQTT2_cerboGX_c0619ab34e08_solarcharger_Common&#039;;              # SmartLoader Device&lt;br /&gt;
  my $maxcspc = 105;                        # max. Ladesollstrom (A) nominal&lt;br /&gt;
  my $spcorr  = 0.9;                        # Korrekturfaktor Überschuss 90%&lt;br /&gt;
  my $sch     = 0.25;                       # Verkürzungsfaktor Zeit bis Vollladung vor Sunset             &lt;br /&gt;
  &lt;br /&gt;
  my $cclvl0  = 15;                         # Ladestrom Level 0 max. 5  A (240 W) pro WR  (720  W)&lt;br /&gt;
  my $cclvl1  = 27;                         # Ladestrom Level 1 max. 9  A (432 W) pro WR  (1296 W)&lt;br /&gt;
  my $cclvl2  = 54;                         # Ladestrom Level 2 max. 18 A (864 W) pro WR  (2592 W)&lt;br /&gt;
  my $cclvl3  = 81;                         # Ladestrom Level 3 max. 27 A (1296 W) pro WR (3888 W)&lt;br /&gt;
  ###############&lt;br /&gt;
  &lt;br /&gt;
  my $cofc   = ReadingsNum ($name,  &#039;special_todayConForecastTillSunset&#039;, 0);&lt;br /&gt;
  my $pvfc   = ReadingsNum ($name,  &#039;RestOfDayPVforecast&#039;, 0);&lt;br /&gt;
  my $cpv    = ReadingsNum ($name,  &#039;Current_PV&#039;,          0);&lt;br /&gt;
  my $mppt1c = ReadingsNum ($mppt1, &#039;DC_0_Current_value&#039;,  0);             # Load I MPPT1 (max. 44 A)&lt;br /&gt;
  my $ssts   = CurrentVal  ($hash,  &#039;sunsetTodayTs&#039;,        0);&lt;br /&gt;
  my $srts   = CurrentVal  ($hash,  &#039;sunriseTodayTs&#039;,       0);&lt;br /&gt;
  my $sdiff  = ($ssts - $srts) / 3600;                                     # Sonnengang heute in h &lt;br /&gt;
  &lt;br /&gt;
  my $solh   = ReadingsNum ($name, &#039;special_SunHours_Remain&#039;, 0);          # h bis SunSet&lt;br /&gt;
  $solh      = $solh - ($sdiff * $sch);                                    # h bis SunSet - X&lt;br /&gt;
  $solh      = $solh &amp;lt; 0 ? 0 : $solh;&lt;br /&gt;
   &lt;br /&gt;
  my $ahrem  = ReadingsNum ($batdev, &#039;EnergyRemain&#039;, 0) / 48;              # Ah Bat bis SOC&lt;br /&gt;
  $ahrem     = sprintf &amp;quot;%.0f&amp;quot;, $ahrem;&lt;br /&gt;
  &lt;br /&gt;
  my $fcdiff = ($pvfc - $cofc) * $spcorr;     # Korrektur d. noch prognostizierten Überschussenergie &lt;br /&gt;
  $fcdiff    = $fcdiff &amp;lt; 0 ? 0 : $fcdiff;     # Wh&lt;br /&gt;
  &lt;br /&gt;
  my $loadcur = $maxcspc;&lt;br /&gt;
  &lt;br /&gt;
  if ($cpv &amp;amp;&amp;amp; $solh &amp;amp;&amp;amp; $ahrem &amp;amp;&amp;amp; $fcdiff &amp;gt; $ahrem) {  # Ladeanforderung und Überschuss übersteigt &lt;br /&gt;
                                                      # Lademenge&lt;br /&gt;
      my $cspc = sprintf &amp;quot;%.2f&amp;quot;, ($ahrem / $solh);    # A = Ah / h -&amp;gt; A Soll Schätzung&lt;br /&gt;
&lt;br /&gt;
      Log3 ($name, 3, qq{$name - userFn -&amp;gt; Battery charging process should be completed }.&lt;br /&gt;
                      qq{in &amp;gt;$solh&amp;lt; hours});&lt;br /&gt;
      Log3 ($name, 3, qq{$name - userFn -&amp;gt; raw Target charging current: $cspc});&lt;br /&gt;
	  &lt;br /&gt;
      $cspc = sprintf &amp;quot;%.2f&amp;quot;, ($cspc - $mppt1c);      # SmartLoader IST berücksichtigen&lt;br /&gt;
	  &lt;br /&gt;
      Log3 ($name, 3, qq{$name - userFn -&amp;gt; Charge Current minus MPPT load Current &amp;gt;$mppt1c&amp;lt;: $cspc});&lt;br /&gt;
	  &lt;br /&gt;
      $loadcur = $cspc &amp;lt;= 0       ? 0       :     # nur MPPT Ladung&lt;br /&gt;
                 $cspc &amp;lt;= $cclvl0 ? $cclvl0 :     # Übergangsbereich&lt;br /&gt;
                 $cspc &amp;lt;= $cclvl1 ? $cclvl1 :     # Ladung mit Level 1 Leistung&lt;br /&gt;
                 $cspc &amp;lt;= $cclvl2 ? $cclvl2 :     # Ladung mit Level 2 Leistung&lt;br /&gt;
                 $cspc &amp;lt;= $cclvl3 ? $cclvl3 :     # Ladung mit Level 3 Leistung&lt;br /&gt;
                 $maxcspc;                        # max. Ladesollstrom&lt;br /&gt;
	  &lt;br /&gt;
	  Log3 ($name, 3, qq{$name - userFn -&amp;gt; MaxChargeCurrent calculated: $loadcur});&lt;br /&gt;
  }&lt;br /&gt;
  &lt;br /&gt;
  readingsSingleUpdate ($hash, &#039;userFn_Bat_MaxChargeCurrent&#039;,       $loadcur.&#039; A&#039;,          1);&lt;br /&gt;
  readingsSingleUpdate ($hash, &#039;userFn_Bat_HoursUntilChargeFinish&#039;, sprintf (&amp;quot;%.2f&amp;quot;,$solh), 1);&lt;br /&gt;
  CommandSet           (undef, &amp;quot;$vebus MaxChargeCurrent $loadcur&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Datei:Screenshot 2023-05-03 201457.png|right|thumb|300px|Vorhersage PV Erzeugung und Energieverbrauch des (restlichen) Tages]]&lt;br /&gt;
Zunächst wird der am aktuellen Tag zu erwartende Totalüberschuss solarer Energie ermittelt. Das erfolgt aus der Differenz von &#039;&#039;RestOfDayPVforecast&#039;&#039; und &#039;&#039;RestOfDayConsumptionForecast&#039;&#039; unter Berücksichtigung eines Sicherheitsabschlages. Das Ergebnis liegt in Wh vor und wird bezogen auf 48 V (die Spannung des Batteriesystems) in Ah umgerechnet. &lt;br /&gt;
&lt;br /&gt;
Die noch benötigte Ladung der Batterie zur Erreichung des Soll SOC-Wertes ermittelt das ReadingsNum &#039;&#039;special_SunHours_Remain&#039;&#039; aus dem Batteriedevice, welches vom Modulreading &#039;&#039;currentBatteryDev&#039;&#039; ausgelesen wird.&lt;br /&gt;
&lt;br /&gt;
Sofern der so ermittelte solare Überschuss des (Rest)Tages die benötigte Lademenge der Batterie übersteigt, wird der DC Ladestrom so verringert, dass er sich in einem effizienten Bereich des MultiPlus II bewegt und dennoch rechnerisch ausreicht auf den Soll SOC-Wert zu laden.  &lt;br /&gt;
&lt;br /&gt;
Zu diesem Zweck wird im Modul über die Auswahl von &#039;&#039;SunHours_Remain&#039;&#039; im Attribut &#039;&#039;ctrlStatisticReadings&#039;&#039; das zusätzliche Reading &#039;&#039;special_SunHours_Remain&#039;&#039; erzeugt. Aus dem (noch) erforderlichen Ladungswert &#039;&#039;&#039;EnergyRemain&#039;&#039;&#039; (Ah) und der um eine Sicherheitszeit (&#039;&#039;Verkürzungsfaktor Zeit bis Vollladung vor Sunset&#039;&#039;) reduzierte Zeit bis zum Sonnenuntergang wird die rechnerisch erforderliche mindest Ladestromstärke ermittelt.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Dieser Wert wird auf die zu Anfang des Codeblocks definierten Ladelevel (&#039;&#039;Ladestrom Level X&#039;&#039;) abstrahiert und ein passender &#039;&#039;&#039;effizienter&#039;&#039;&#039; Ladestrom-Bereich ausgewählt.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Screenshot 2023-05-03 201719.png|right|thumb|300px|Vorhersage PV Erzeugung und Energieverbrauch des (restlichen) Tages]]&lt;br /&gt;
&lt;br /&gt;
In allen anderen Fällen, wenn z.B. rechnerisch nicht genügend oder keine Solarenergie erzeugt wird (Nachts) oder der Speicher gefüllt ist, wird der maximale (Sollwert) des Ladestroms &#039;&#039;$maxcspc = 35&#039;&#039; eingestellt. Dieser Wert entspricht dem maximalen Ladestrom gemäß Herstellerunterlagen Pylontech.&lt;br /&gt;
&lt;br /&gt;
Die Einstellung erfolgt per Set-Kommando im Victron vebus MQTT Device &#039;&#039;MQTT2_cerboGX_c0619ab34e08_vebus&#039;&#039;. Dieses Device wurde auch während der MQTT Intergration des Victron Systems angelegt und wird hier nicht näher diskutiert.&lt;br /&gt;
Zur Kontrolle des kalkulierten Wertes werden noch die Readings &#039;&#039;SolCast_userFn_MaxChargeCurrent&#039;&#039; im Batteriedevice und &#039;&#039;userFn_Bat_MaxChargeCurrent&#039;&#039; im SolarForecast Device generiert.&lt;br /&gt;
&lt;br /&gt;
Neben der Effizienzoptimierung hat dieses Verfahren auch den Vorteil einer möglichst schonenden Batterieaufladung was der Lebensdauer der Komponenten zugute kommen sollte.&lt;br /&gt;
&lt;br /&gt;
=== Poolheizung in Abhängigkeit von vorhandenem Photovoltaik-Überschuss aktivieren/deaktivieren (Eigenverbrauch optimieren) ===&lt;br /&gt;
Eine (Whirl-)Poolheizung soll bei ausreichend überschüssiger Energie aus einer Photovoltaikanlage dynamisch gesteuert werden (Eigenverbrauchsoptimierung). Im folgenden Beispiel ist ein Device mit der Bezeichnung &#039;&#039;Forecast&#039;&#039; des Typs &#039;&#039;SolarForecast bereits&#039;&#039; angelegt und konfiguriert. Ein freies Consumer-Attribut &#039;&#039;consumer01&#039;&#039; wird beispielhaft wie folgt definiert:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
attr Forecast consumer01 MQTT2_layzspa type=other power=1950 mode=can on=&amp;quot;heater on&amp;quot; off=&amp;quot;heater off&amp;quot; pcurr=WATT interruptable=1 swstate=heaterstate:1:0 auto=Automatiksteuerung mintime=SunPath spignorecond=EVCharger22:LadungMitPVUeberschussActive:1 icon=scene_pool notbefore=8 notafter=20&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Poolheizung ist innerhalb FHEM bereits im Device &#039;&#039;MQTT2_layzspa&#039;&#039; integriert und lässt sich normalerweise per FHEM-Befehl &amp;quot;set &#039;&#039;MQTT2_layzspa heater on&amp;quot;&#039;&#039; oder &#039;&#039;&amp;quot;set MQTT2_layzspa heater off&amp;quot;&#039;&#039; steuern. Entsprechend werden die Schlüssel-Wert-Paare &#039;&#039;&amp;quot;on=&amp;quot;&#039;&#039; und &#039;&#039;&amp;quot;off=&amp;quot;&#039;&#039; definiert. &lt;br /&gt;
&lt;br /&gt;
Der Stromverbrauch einer aktiven Poolheizung ist uns bekannt und wird im Schlüssel &#039;&#039;&amp;quot;power=1950&amp;quot;&#039;&#039; definiert. Die Heizfunktion soll nur bei ausreichend Überschuss aktiviert werden dürfen und muss bei Unterschreitung sofort abgeschaltet werden. Diese Eigenschaften werden über die Schlüssel &#039;&#039;&amp;quot;mode=can&amp;quot;&#039;&#039; und &#039;&#039;&amp;quot;interruptable=&amp;quot;1&amp;quot;&#039;&#039; festgelegt. Zuletzt soll grundsätzlich nur in der Zeit zwischen 08:00 Uhr bis 20:00 Uhr geheizt werden dürfen. &lt;br /&gt;
&lt;br /&gt;
Die Schlüssel &#039;&#039;&amp;quot;notbefore=8&amp;quot;&#039;&#039; und &#039;&#039;&amp;quot;notafter=20&amp;quot;&#039;&#039; legen diese Eigenschaften fest. Zur Anzeige des Schaltzustands der Heizung wurde der Schlüssel &#039;&#039;&amp;quot;swstate=heaterstate:1:0&amp;quot;&#039;&#039; hinzugefügt (Wert 1 = Heizung aktiv, Wert 0= Heizung aus). &lt;br /&gt;
&lt;br /&gt;
==== Priorisierung vor externen PV-Überschuss gesteuerten Verbrauchern ====&lt;br /&gt;
Der Schlüssel-Wert &#039;&#039;&#039;&amp;quot;spignorecond=&amp;quot;&#039;&#039;&#039; bietet optional die Möglichkeit, einen Consumer auch ohne ausreichend PV-Überschuss einzuschalten. Bei erfüllter Bedingung wird der Verbraucher entsprechend der Planung eingeschaltet auch wenn zu dem Zeitpunkt kein PV Überschuss vorliegt. Im obigen Beispiel existiert in der Haus-Elektroinstallation eine Elektroauto-Ladestation mit eigener (externer) dynamischer PV-gesteuerter Fahrzeugladung ohne Integration in &#039;&#039;SolarForecast&#039;&#039;. &lt;br /&gt;
&lt;br /&gt;
In Abhängigkeit von PV-Generatorleistung und Sonneneinstrahlung besteht manchmal eine Möglichkeit, dass der gesamte PV-Überschuss zur Fahrzeugladung genutzt wird. Das Modul SolarForecast würde entsprechend keinen (oder zu wenig) PV-Überschuss erkennen und den Consumer &#039;&#039;MQTT2_layzspa&#039;&#039; nicht aktivieren. &lt;br /&gt;
&lt;br /&gt;
Durch Verwendung von &#039;&#039;spignorecond=EVCharger22:LadungMitPVUeberschussActive:1&#039;&#039; wird die Poolheizung gegenüber der Ladestation jedoch priorisiert mit PV-Überschuss versorgt. In diesem Beispiel wird Consumer1 aktiviert, wenn die Ladestation &amp;quot;&#039;&#039;EVCharger22&#039;&#039;&amp;quot; sich im PV-Überschuss-Modus befindet &#039;&#039;&#039;und&#039;&#039;&#039; momentan ein Fahrzeug lädt. In der Folge würde der Hausstromverbrauch um ca. 1950 Watt steigen, die externe Fahrzeugladestation diese Änderung registrieren und die Fahrzeugladeleistung entsprechend verringern oder ganz abbrechen.&lt;br /&gt;
&lt;br /&gt;
=== Luftentfeuchter PV-Überschuss gesteuert mit externer Zwangs-Einschaltbedingung ===&lt;br /&gt;
Im folgenden Beispiel wird ein Luftentfeuchter normalerweise nur bei ausreichend PV-Überschuss Energie eingeschaltet (Eigenverbrauchsoptimierung), soll jedoch bei hoher Luftfeuchtigkeit zusätzlich aktiviert werden.&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
attr Forecast SolarForecast ShellyPlug1 type=other power=300 mode=can on=on off=off pcurr=power:W interruptable=1 swstate=state:on:off mintime=SunPath locktime=900 notbefore=07 notafter=22 spignorecond=ESPEasy_ESP_Easy1_am2302_sensor:humidity:high icon=Ventilator_fett&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Das in FHEM bereits angelegte Device namens &#039;&#039;ShellyPlug1&#039;&#039; dient als Zwischenstecker zur Steuerung des Luftentfeuchters. Der Schlüsselwert &amp;quot;&#039;&#039;mintime=SunPath&#039;&#039;&amp;quot; definiert die zeitliche Einplanung von Sonnenauf- bis untergang. Jedoch soll das Gerät unter keinen Umständen in der Nacht eingeschaltet werden, was über die Schlüssel &#039;&#039;&amp;quot;notbefore=07&amp;quot;&#039;&#039; bzw. &#039;&#039;&amp;quot;notafter=22&amp;quot;&#039;&#039; festgelegt ist. Weiterhin wurde im Schlüssel &#039;&#039;&amp;quot;locktime=900&amp;quot;&#039;&#039; festgelegt, dass zwischen (möglichen) Ein- und Ausschaltintervallen wenigstens 15 Minuten Wartezeit vergehen soll.&lt;br /&gt;
&lt;br /&gt;
Der Schlüssel-Wert &#039;&#039;&#039;&amp;quot;spignorecond=&amp;quot;&#039;&#039;&#039; bietet optional die Möglichkeit, ein Consumer bei erfüllter Bedingung auch ohne ausreichend PV-Überschuss einzuschalten. In diesem Beispiel wird ein Raumluftsensor abgefragt. Bei Vorhandensein der Einschaltschwelle &amp;quot;high&amp;quot; aktiviert sich der Consumer zur geplanten Laufzeit zwischen 07:00 bis 22:00 Uhr solange, bis der Raumluftsensor einen abweichenden Wert meldet.&lt;br /&gt;
&lt;br /&gt;
=== PV-Vorhersage an evcc übertragen (PV-Überschussladen von E-Autos über steuerbare Wallboxen) ===&lt;br /&gt;
[https://evcc.io evcc] ist ein Energie-Management-System, mit dem u.a. Überschussladen von Elektroautos möglich ist. Hier ist beschreiben, wie die SolarForecast-Vorhersagedaten per API von evcc abgerufen werden.&lt;br /&gt;
Lege im SolarForecast-Device ein User-Reading mit dem Namen forecast_json an. Es wandelt die Forecast-Werte in JSON und ins UTC-Zeitformat um, so dass evcc die Daten einlesen kann. Der Code wird nur ausgeführt, wenn das Reading nextCycletime ein Event erzeugt.&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
	&lt;br /&gt;
forecast_json:nextCycletime.* {&lt;br /&gt;
&lt;br /&gt;
    use strict;&lt;br /&gt;
    use warnings;&lt;br /&gt;
    use JSON;&lt;br /&gt;
    use DateTime;&lt;br /&gt;
    use DateTime::Format::Strptime;&lt;br /&gt;
&lt;br /&gt;
    my $hour = 0;&lt;br /&gt;
    my $last_start_ts;    &lt;br /&gt;
    my @output;&lt;br /&gt;
    &lt;br /&gt;
    # Parser für Datum mit CET/CEST&lt;br /&gt;
    my $parser = DateTime::Format::Strptime-&amp;gt;new(&lt;br /&gt;
        pattern   =&amp;gt; &amp;quot;%Y-%m-%d %H:%M:%S&amp;quot;,&lt;br /&gt;
        time_zone =&amp;gt; &amp;quot;Europe/Berlin&amp;quot;,&lt;br /&gt;
    );&lt;br /&gt;
    my $start_ts;&lt;br /&gt;
    &lt;br /&gt;
    # Alle NextHour-Daten durchsuchen (als Fallback zur Vermeidung einer Endlosschleife auf max. 100 Std. begrenzen)&lt;br /&gt;
    while ($hour &amp;lt; 100) {&lt;br /&gt;
        my $hour_str = sprintf(&amp;quot;NextHour%02d&amp;quot;, $hour);&lt;br /&gt;
        my $start_str = FHEM::SolarForecast::NexthoursVal(&amp;quot;$NAME&amp;quot;, $hour_str, &amp;quot;starttime&amp;quot;, &amp;quot;na&amp;quot;);&lt;br /&gt;
        my $pvfc = FHEM::SolarForecast::NexthoursVal(&amp;quot;$NAME&amp;quot;, $hour_str, &amp;quot;pvfc&amp;quot;, &amp;quot;na&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
        # Schleife beenden, wenn keine Vorhersage-Werte mehr vorhanden sind&lt;br /&gt;
        last if $start_str eq &amp;quot;na&amp;quot; or $pvfc eq &amp;quot;na&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
        $start_ts = $parser-&amp;gt;parse_datetime($start_str);  # Zeitzone und Sommer/Winterzeit berücksichtigen&lt;br /&gt;
&lt;br /&gt;
        # Zeit nach UTC und ISO 8601 konvertieren und Vorhersagewert ergänzen&lt;br /&gt;
        push @output, {&lt;br /&gt;
            start =&amp;gt; $start_ts-&amp;gt;set_time_zone(&amp;quot;UTC&amp;quot;)-&amp;gt;iso8601() . &amp;quot;Z&amp;quot;,&lt;br /&gt;
            end   =&amp;gt; $start_ts-&amp;gt;add(hours =&amp;gt; 1)-&amp;gt;set_time_zone(&amp;quot;UTC&amp;quot;)-&amp;gt;iso8601() . &amp;quot;Z&amp;quot;,&lt;br /&gt;
            value =&amp;gt; 0 + $pvfc,&lt;br /&gt;
        };&lt;br /&gt;
        $hour++;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    # Prüfen, ob der letzte Wert 23 Uhr war -&amp;gt; zusätzliche Stunde anhängen, damit der Tag&lt;br /&gt;
    # bei evcc als vollständig angezeigt wird (siehe https://github.com/evcc-io/evcc/issues/22979)&lt;br /&gt;
    if ($start_ts-&amp;gt;hour == 23) {&lt;br /&gt;
&lt;br /&gt;
        # Zeit nach UTC und ISO 8601 konvertieren und Vorhersagewert ergänzen&lt;br /&gt;
        push @output, {&lt;br /&gt;
            start =&amp;gt; $start_ts-&amp;gt;set_time_zone(&amp;quot;UTC&amp;quot;)-&amp;gt;iso8601() . &amp;quot;Z&amp;quot;,&lt;br /&gt;
            end   =&amp;gt; $start_ts-&amp;gt;add(hours =&amp;gt; 1)-&amp;gt;set_time_zone(&amp;quot;UTC&amp;quot;)-&amp;gt;iso8601() . &amp;quot;Z&amp;quot;,&lt;br /&gt;
            value =&amp;gt; 0,&lt;br /&gt;
        };      &lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    # Ausgabe als JSON&lt;br /&gt;
    my $json = JSON-&amp;gt;new-&amp;gt;utf8-&amp;gt;pretty-&amp;gt;encode(\@output);&lt;br /&gt;
    return $json;&lt;br /&gt;
},&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
[[Datei:Screenshot 2025-08-04 104835.png|right|thumb|300px|Darstellung der PV-Vorhersage in evcc auf Basis der SolarForecast-Daten]]&lt;br /&gt;
Die Kommunikation läuft über den integrierten FHEM-Webserver, der die JSON-Daten aus dem Reading forecast_json veröffentlicht. Dazu wird in FHEM eine separate Webinstanz auf einem neuen Port erstellt, die ohne [[CsrfToken-HowTo|CSFR-Token]] (Achtung, Auswirkungen auf die Sicherheit beachten!) und nur von localhost aufrufbar ist (Voraussetzung: evcc läuft auf derselben Maschine wie FHEM):&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
define WEBapi FHEMWEB 8089&lt;br /&gt;
attr WEBapi csrfToken none&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;In der [https://docs.evcc.io/docs/tariffs#pv-vorhersage evcc-Konfig unter tariffs] steht die Quelle für die Solar-Vorhersage. Der FHEM-Befehl {ReadingsVal(&#039;solarforecast_dev&#039;,&#039;forecast_json&#039;,&#039;[]&#039;)} wird encoded und in die uri eingetragen. Trage statt solarforecast_dev den passenden Namen deines SolarForecast-Device ein. evcc holt die Daten aus dem Reading forecast_json dann regelmäßig ab.&amp;lt;syntaxhighlight lang=&amp;quot;yaml&amp;quot;&amp;gt;&lt;br /&gt;
solar:&lt;br /&gt;
  type: custom&lt;br /&gt;
  interval: 20m    &lt;br /&gt;
  forecast:&lt;br /&gt;
    source: http&lt;br /&gt;
    uri: http://localhost:8089/fhem?cmd=%7BReadingsVal%28%27solarforecast_dev%27%2C%27forecast_json%27%2C%27%5B%5D%27%29%7D&amp;amp;XHR=1&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Zur Verbesserung der Sicherheit kann das neue WEBapi Device auch ein [[CsrfToken-HowTo#csrfToken festlegen|festes CSFR-Token]] verwenden. Dieses Token muss dann in der evcc-Konfiguration an die uri mittels &amp;amp;fwcsrf=&amp;lt;festes token&amp;gt; angehängt werden.&lt;br /&gt;
&lt;br /&gt;
=== Poolsteuerung inkl. Eigenverbrauchsoptimierung und zusätzlichem Verbraucher, um Einspeiselimit zu vermeiden + manuelle Steuerung durch Sonderprogramme ===&lt;br /&gt;
&#039;&#039;&#039;Vorhandene Hardware:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* Poolpumpe, die über mehrere Stufen verfügt. Hier werden zwei verwendet.&lt;br /&gt;
** Pump Low: Energiesparprogramm um den Pool umzuwälzen, auch wenn nicht genug PV Leistung zur Verfügung steht. -&amp;gt; ca. 250W&lt;br /&gt;
** Pump High: Normalbetrieb wenn genug PV Überschuss zur Verfügung steht. -&amp;gt; ca. 800W zusätzlich&lt;br /&gt;
* Poolheizung: Wärmepumpe -&amp;gt; ca. 1,5kW elektrisch&lt;br /&gt;
* Nachbars Pool: Pumpe + Salzanlage +pH-Regler -&amp;gt; ca. 650W&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Anforderungen:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* Pool muss täglich mindestens 1x umgewälzt werden.&lt;br /&gt;
* Im Idealfall wird der Pool &amp;gt; 2x umgewälzt.&lt;br /&gt;
* Bei genügend Überschuss soll der Pool geheizt werden. Es soll nur Sonnenstrom für die Poolheizung verwendet werden.&lt;br /&gt;
* Um das Einspeiselimit zu vermeiden, soll auch geheizt werden, wenn der Pool schon warm genug ist. (&amp;gt;28,5 °C)&lt;br /&gt;
* Sollte trotzdem noch das Einspeiselimit erreicht werden, soll auch der Pool des Nachbarn versorgt werden.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Herausforderungen:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* Verbraucher dürfen nur in einer gewissen Reihenfolge geschaltet werden, auch wenn der Überschuss eine andere Reihenfolge ergeben würde.&lt;br /&gt;
* Pumpe mit mehreren Stufen muss als zwei Consumer abgebildet werden, um die Schaltlogik in SolarForecast abzubilden. Würde die Trennung außerhalb von Solarforecast realisiert, könnte Solarforecast den Verbraucher schlechter &amp;quot;lernen&amp;quot;, denn dann wäre es ein Consumer mit ständig wechselndem Verbrauch.&lt;br /&gt;
* Pool des Nachbarn darf nicht laut der benötigten Leistung geschaltet werden, sondern in Abhängigkeit der Einspeisebegrenzung.&lt;br /&gt;
&lt;br /&gt;
In diesem Solarforcast-Device sind 16 Verbraucher definiert. Im Weiteren werden jedoch nur die verwendeten betrachtet. Da diese Reglung mittlerweile recht komplex ist, werden auch viele Dinge, die nicht SolarForcast betreffen, aber trotzdem dazu gehören, gezeigt. Es trägt zum Verständnis bei und man kann sich hier sicher Ideen holen.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ergebnis:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
In den folgenden beiden Bildern sieht man das Regelverhalten recht gut.&lt;br /&gt;
[[Datei:Mustertag ohne Wolken.jpg|zentriert|mini|823x823px|Wolkenloser Tag]]&lt;br /&gt;
Hier erkennt man gut das Einschalten der jeweiligen Verbraucher (Pump Low, Pump High, Wärmepumpe und Nachbar Pool). Die Wärempumpe schaltet erst bei 5kW Überschuss ein, da der Pool eigentlich schon warm genug ist. Sie dient &amp;quot;nur&amp;quot; dazu, das EInspeiselimit zu verhindern.Auch das Auschalten ist wieder deutlich zu erkennen. Die Regleung folgt dem Überschuss recht gut. (Die Leistungsspitzen auf dem Plateau sind normale Großverbraucher im Haus.)&lt;br /&gt;
[[Datei:Wechselhaft.jpg|zentriert|mini|818x818px|Wechselhafter Tag.]]&lt;br /&gt;
In diesem Bild sieht man das &amp;quot;verzweifelte&amp;quot; Regeln um das wechselhafte Wetter auszugleichen. Hier gibt es keine korrekte Reaktion und es wird durch die Sperrzeiten und andere Delays versucht, ein zu häufiges hin- und herschalten möglichst zu vermeiden. In Summe aber auch bei so einem Wetter ein akzeptables Verhalten.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Definition der Consumer:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Allgemeine Erklärungen zu den Definitionen:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;asynchron=0&#039;&#039;&#039;: Da hier die Werte des Überschusses für das Timing der Schaltvorgänge verwendet werden, darf hier keine Eventsteuerung verwendet werden. Sonst kann man nicht mehr sagen, über welchen Zeitraum der Mittelwert oder der Median gebildet wurde. Dafür wurde die Zykluszeit des SolarForecast-Device auf 60 Sekunden geändert, um die Überlegungen zu vereinfachen. Bei Event-Steuerung könnte man nicht sagen ob die letzten 10 Events in den letzten 5 Sekunden oder in den letzten 10 Minuten aufgetreten sind.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;power=xxx&#039;&#039;&#039;: Wurde teilweise etwas höher als der tatsächliche Wert gewählt, um nach dem Einschalten sicherzustellen, dass nicht sofort der gesamte Überschuss &amp;quot;verbraucht&amp;quot; wird und somit gleich wieder abgeschaltet wird.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;auto=xxx&#039;&#039;&#039;: Jeder Verbraucher benötigt sein eigenes auto-Reading, da die Automatik für die Regelung in verschiedenen Stufen ein- und ausgeschaltet werden muss.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;dum_valve&#039;&#039;&#039;: Dummy-Device in dem die Zustände der Regelung abgebildet werden. Notifys lesen bzw. befüllen diesen Dummy und steuern dann die Hardware bzw. wird der Dummy durch Werte aus der Hardware passend befüllt.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;surpmeth=median_xx:&#039;&#039;&#039; Durch das asynchron=0 in allen Consumern und beim Generator wird von Solarforcast genau alle 60 Sekunden ein Zyklus ausgeführt. Das führt dazu, dass der Median genau über xx Minuten gebildet wird.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;locktime=xx:xx&#039;&#039;&#039;: Sicherheitsvorkehrung, dass nicht zu oft geschaltet werden kann. Schont die Hardware und verhindert ein Schwingen. Abhänging vom geschalteten Gerät. z.B. soll die Wärmepumpe möglichst wenig takten, um die Lebensdauer zu verlängern.&lt;br /&gt;
&lt;br /&gt;
Es gibt dann auch noch einige weitere Sicherheitsvorkehrungen, um diese Regelung möglichst stabil und hardwareschonend zu gestalten. Die Schwierigkeit sind nicht die Bilderbuchtage mit einem glatten Verlauf der PV-Leistung, sondern Tage mit ständig wechselndem Überschuss in der Nähe der Schaltgrenzen.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;swoncond= dum_valve:sf_true:{main::xxx_On}:&#039;&#039;&#039; Hier werden verschiedene Zustände geprüft, um ein Gerät nur dann einzuschalten, wenn es sinnvoll und sicher ist.&lt;br /&gt;
&lt;br /&gt;
Details unter &#039;&#039;&#039;99_mySolarForecastUtils.pm&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;swoffcond= dum_valve:sf_true:{main::xxx_Off}&#039;&#039;&#039;: Hier werden verschiedene Zustände geprüft, um ein Gerät nur dann auszuschalten, wenn es sinnvoll und sicher ist.&lt;br /&gt;
&lt;br /&gt;
Details unter &#039;&#039;&#039;99_mySolarForecastUtils.pm&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Pump Low: (consumer01)&#039;&#039;&#039;&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
dum_valve:Pool+Low&lt;br /&gt;
aliasshort=Low&lt;br /&gt;
asynchron=0&lt;br /&gt;
auto=pump_low_auto&lt;br /&gt;
icon=scene_pool&lt;br /&gt;
interruptable=0&lt;br /&gt;
mintime=SunPath:0:180&lt;br /&gt;
mode=must&lt;br /&gt;
noshow=0&lt;br /&gt;
notafter=08:00&lt;br /&gt;
off=&amp;quot;pump_low off&amp;quot;&lt;br /&gt;
on=&amp;quot;pump_low on&amp;quot;&lt;br /&gt;
pcurr=pump_low_power&lt;br /&gt;
power=250&lt;br /&gt;
surpmeth=median_13&lt;br /&gt;
switchdev=dum_valve&lt;br /&gt;
swoffcond=dum_valve:sf_true:{main::Check_Pump_Low_Off}&lt;br /&gt;
swoncond=dum_valve:sf_true:{main::Check_Pump_Low_On}&lt;br /&gt;
swstate=pump_low:on:off&lt;br /&gt;
type=other&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Erläuterungen:&lt;br /&gt;
&lt;br /&gt;
* Die Pumpe soll ab Sonnenaufgang, bis 180 Minuten nach Sonnenuntergang (mintime=SunPath:0:180), bei mindestens 250W Überschuss (power=250) gestartet werden.&lt;br /&gt;
* Die Pumpe muss spätestens um 8:00 eingeplant werden (notafter=08:00), um das Umwälzen auch bei Schlechtwetter zu garantieren.&lt;br /&gt;
* Die Pumpe muss gestartet werden (mode=must) sobald sie eingeplant ist.&lt;br /&gt;
* swoncond sorgt dafür, dass sie vor 8:00 nur bei genügend Überschuss gestartet wird, oder es 8:00 ist. swondcond ist mit der eigentlichen Einschaltbedingung (eingeplant und mode=must) UND verknüpft. -&amp;gt; siehe &#039;&#039;&#039;99_mySolarForecastUtils.pm&#039;&#039;&#039;&lt;br /&gt;
* Überschuss wird auch in swoncond mit der gewählten Methode geprüft.&lt;br /&gt;
* Die Pumpe darf, wenn sie gestartet wurde, auch bei fehlendem Überschuss nicht mehr unterbrochen werden. (interruptable=0)&lt;br /&gt;
* Die Pumpe wird am Ende der Einplanung ausgeschaltet oder wenn swoffcond erfüllt ist. swoffcond wird true, wenn eine gewisse Wassermenge umgewälzt wurde oder die Sicherheitsbedingungen nicht (mehr) erfüllt sind. swoffcond ist mit der eigentlichen Ausschaltbedingung (Planung endet) ODER verknüpft. -&amp;gt; siehe &#039;&#039;&#039;99_mySolarForecastUtils.pm&#039;&#039;&#039;&lt;br /&gt;
* {&#039;&#039;&#039;main::&#039;&#039;&#039;xxx} dient dazu, um die Funktion xxx in der 99_mySolarForecastUtils.pm aus Solarforecast erreichbar zu machen.&lt;br /&gt;
* dum_valve:sf_true ist ein Reading, das fix 1 ist und als Vergleichswert für den in Klammern folgenden Perlcode dient. Wenn dieses Reading und der Perlcode gleich sind (1==1), ist die swoncond bzw swoffcond erfüllt.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Pump High: (consumer03)&#039;&#039;&#039;&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
dum_valve:Pool+High&lt;br /&gt;
aliasshort=High&lt;br /&gt;
type=other power=1000 asynchron=0&lt;br /&gt;
auto=pump_high_auto&lt;br /&gt;
icon=scene_pool&lt;br /&gt;
pcurr=pump_high_power&lt;br /&gt;
switchdev=dum_valve&lt;br /&gt;
swstate=pump_high:on:off&lt;br /&gt;
mode=can&lt;br /&gt;
mintime=SunPath&lt;br /&gt;
on=&amp;quot;pump_high on&amp;quot;&lt;br /&gt;
off=&amp;quot;pump_high off&amp;quot;&lt;br /&gt;
surpmeth=median_5&lt;br /&gt;
noshow=0&lt;br /&gt;
locktime=180:300&lt;br /&gt;
interruptable=1&lt;br /&gt;
swoncond=dum_valve:sf_true:{main::Check_Pump_High_On}&lt;br /&gt;
swoffcond=dum_valve:sf_true:{main::Check_Pump_High_Off}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Erläuterungen:&lt;br /&gt;
&lt;br /&gt;
* Pump HIgh ist die selbe Pumpe, aber eine höhere Durchflussmenge&lt;br /&gt;
* Der Mehrverbrauch beträgt eigentlich 800W wird hier aber mit 1000W angegeben, um eine Hysterese von ca. 200W zu erreichen und damit ein schnelles Abschalten nach dem Einschalten zu verhindern.&lt;br /&gt;
* Diese Stufe wird nur bei genügend Überschuss aktiviert. (mode=can)&lt;br /&gt;
* Diese Stufe wird bei fehlendem Überschuss unterbrochen. (interruptable=1)&lt;br /&gt;
* Diese Stufe wird nur während Sonnenschein eingeplant. (mintime=SunPath)&lt;br /&gt;
* locktime sorgt dafür, dass bei schnell wechselnden Bedingungen, nicht ständig umgeschaltet wird. -&amp;gt; Schonung der Pumpe.&lt;br /&gt;
* swoncond und swoffcond -&amp;gt; siehe &#039;&#039;&#039;99_mySolarForecastUtils.pm&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Heatpump: (consumer05)&#039;&#039;&#039;&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
Pool_Strom_Heizung:Poolheizung&lt;br /&gt;
aliasshort=WP&lt;br /&gt;
type=other power=1800 asynchron=0&lt;br /&gt;
icon=sani_heating_heatpump&lt;br /&gt;
auto=heatpump_auto&lt;br /&gt;
pcurr=Pool_Pin32_monotonic_count_PowerCurrent:W&lt;br /&gt;
etotal=Pool_Pin32_monotonic_count_EnergyMeter:kWh&lt;br /&gt;
switchdev=dum_valve&lt;br /&gt;
swstate=heatpump:on:off&lt;br /&gt;
mode=can&lt;br /&gt;
locktime=600:600&lt;br /&gt;
mintime=SunPath&lt;br /&gt;
on=&amp;quot;heatpump on&amp;quot;&lt;br /&gt;
off=&amp;quot;heatpump off&amp;quot;&lt;br /&gt;
surpmeth=median_13&lt;br /&gt;
noshow=0&lt;br /&gt;
swoncond=dum_valve:sf_true:{main::CheckWPOn}&lt;br /&gt;
interruptable=1&lt;br /&gt;
swoffcond=dum_valve:sf_true:{main::CheckWPOff}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Erläuterungen:&lt;br /&gt;
&lt;br /&gt;
* Der Verbrauch ist hier wieder etwas zu hoch angegeben (1800W statt realen 1500W), um eine Hysterese zu erreichen.&lt;br /&gt;
* Da es sich hier um eine Wärmepumpe handelt, die nicht getaktet werden sollte, ist die locktime in beide Schaltrichtungen mit 10 Minuten relativ hoch.&lt;br /&gt;
* Die Ermittlung des Medians ist mit 13 Minuten recht lang, um den Überschuss über lange Zeit zu glätten, damit möglichst selten geschaltet wird. Diese 13 Minuten in Kombination mit der Locktime und der Hysterese führten bei dieser Anlage bisher dazu, dass höchstens 5 Zyklen am Tag geschaltet wurden. -&amp;gt; Hier kann das individuelle Schaltverhalten optimiert werden. Besseres Verfolgen des Überschusses vs. weniger Taktungen der Wärmepumpe.&lt;br /&gt;
* swoncond und swoffcond -&amp;gt; siehe &#039;&#039;&#039;99_mySolarForecastUtils.pm&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Nachbars Pool: (consumer07)&#039;&#039;&#039;&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
Shelly_Plug_S_1:Kurz+Pool&lt;br /&gt;
aliasshort=Kurz&lt;br /&gt;
type=other power=650 asynchron=0&lt;br /&gt;
icon=scene_pool&lt;br /&gt;
auto=pool_kurz_auto&lt;br /&gt;
pcurr=power:W&lt;br /&gt;
switchdev=dum_valve&lt;br /&gt;
swstate=pool_kurz:on:off&lt;br /&gt;
mode=can&lt;br /&gt;
mintime=SunPath&lt;br /&gt;
on=&amp;quot;pool_kurz on&amp;quot;&lt;br /&gt;
off=&amp;quot;pool_kurz off&amp;quot;&lt;br /&gt;
surpmeth=median_13&lt;br /&gt;
noshow=0&lt;br /&gt;
interruptable=1&lt;br /&gt;
locktime=600:300&lt;br /&gt;
swoncond=dum_valve:sf_true:{main::Check_Pool_Kurz_On}&lt;br /&gt;
swoffcond=dum_valve:sf_true:{main::Check_Pool_Kurz_Off}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Erläuterungen:&lt;br /&gt;
&lt;br /&gt;
* Dieser Consumer dient als Einspeisebegrenzungsschutz und wird aktiviert, sobald sich der Überschuss der Einspeisegrenze nähert.  -&amp;gt; siehe &#039;&#039;&#039;99_mySolarForecastUtils.pm&#039;&#039;&#039;&lt;br /&gt;
* Verbraucher kann recht träge geschaltet werden. (surpmeth=median_13 und locktime=600:300)&lt;br /&gt;
* Consumer wird ausgeschaltet wenn ein gewisser Überschuss unterschritten wird und somit die Einspeisegrenze wieder weit genug weg ist. -&amp;gt; siehe &#039;&#039;&#039;99_mySolarForecastUtils.pm&#039;&#039;&#039; &lt;br /&gt;
&#039;&#039;&#039;99_mySolarForecastUtils.pm&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Hier sind die Funktionen, die für diese Regelung verwendet werden, der Übersichtlichkeit halber in einzelnen Blöcken mit Erklärungen dargestellt.&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
package main;&lt;br /&gt;
use strict;&lt;br /&gt;
use warnings;&lt;br /&gt;
&lt;br /&gt;
my $pool_dummy = &#039;dum_valve&#039;;&lt;br /&gt;
my $pool_temperature = &#039;Pool_Temp&#039;;&lt;br /&gt;
my $flow_sensor = &#039;Pool_Pin_A0&#039;;&lt;br /&gt;
my $water_counter_l1 = &#039;pump_l1_hc&#039;;&lt;br /&gt;
my $water_counter_l2 = &#039;pump_l2_hc&#039;;&lt;br /&gt;
my $target_temp = 28.4; # maximum temperature for heating, without infeed limit&lt;br /&gt;
my $pool_kurz_sw_hofb = &#039;PwSw_Schalter&#039;; # Homematic switch at pool neighbour&lt;br /&gt;
my $pool_kurz_sw_kurz = &#039;Shelly_Plug_S_1&#039;; # Shelly switch for pool Kurz at home&lt;br /&gt;
&lt;br /&gt;
my $pump_low_id			= sprintf &amp;quot;%02d&amp;quot;, 1;&lt;br /&gt;
my $pump_high_id		= sprintf &amp;quot;%02d&amp;quot;, 3;&lt;br /&gt;
my $heatpump_id			= sprintf &amp;quot;%02d&amp;quot;, 5;&lt;br /&gt;
my $pool_kurz_id		= sprintf &amp;quot;%02d&amp;quot;, 7;&lt;br /&gt;
&lt;br /&gt;
my $switch_delay = 160; # just under three minutes&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
sub&lt;br /&gt;
SolarForecastUtils_Initialize($$)&lt;br /&gt;
{&lt;br /&gt;
  my ($hash) = @_;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Hier werden die IDs der Geräte definiert, um über sprechende Namen darauf zugreifen zu können.&lt;br /&gt;
* Einige Devices und Parameter, die immer wieder gebraucht werden sind hier angegeben.&lt;br /&gt;
* sprintf wird verwendet um sicherzustellen, dass die IDs zweistellig sind. z.B. 3 -&amp;gt; 03&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Pump Low&#039;&#039;&#039;&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
###################&lt;br /&gt;
### Pumpe - LOW ###&lt;br /&gt;
###################&lt;br /&gt;
&lt;br /&gt;
sub&lt;br /&gt;
Check_Pump_Low_On{&lt;br /&gt;
my $name = &#039;energy_mgmt&#039;;&lt;br /&gt;
my $surplus	= FHEM::SolarForecast::ConsumerVal ($name,$pump_low_id,&#039;surpmethResult&#039;,&#039;&#039;);&lt;br /&gt;
my $nom_power	= FHEM::SolarForecast::ConsumerVal ($name,$pump_low_id,&#039;power&#039;,&#039;&#039;);&lt;br /&gt;
my ($min, $hour) = (localtime())[1,2];&lt;br /&gt;
my $timer = 0;&lt;br /&gt;
if($hour &amp;gt; 8 || ($hour == 8 &amp;amp;&amp;amp; $min &amp;gt;= 0)){$timer=1;}&lt;br /&gt;
&lt;br /&gt;
if(ReadingsVal($pool_dummy,&amp;quot;valve_position&amp;quot;,&amp;quot;&amp;quot;) eq &amp;quot;normal&amp;quot;&lt;br /&gt;
	&amp;amp;&amp;amp; ReadingsVal($pool_dummy,&amp;quot;special_function&amp;quot;,&amp;quot;&amp;quot;) eq &amp;quot;none&amp;quot;&lt;br /&gt;
	&amp;amp;&amp;amp; ($surplus &amp;gt; $nom_power&lt;br /&gt;
		|| $timer&lt;br /&gt;
	)&lt;br /&gt;
)&lt;br /&gt;
	{&lt;br /&gt;
	return 1;}&lt;br /&gt;
else&lt;br /&gt;
	{&lt;br /&gt;
	return 0;}&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
sub&lt;br /&gt;
Check_Pump_Low_Off{&lt;br /&gt;
my $bath_mode = ReadingsVal($pool_dummy,&amp;quot;bath_mode&amp;quot;,0);&lt;br /&gt;
my $m3_l1 = ReadingsVal($water_counter_l1,&amp;quot;waterAmount&amp;quot;,0);&lt;br /&gt;
my $m3_l2 = ReadingsVal($water_counter_l2,&amp;quot;waterAmount&amp;quot;,0);&lt;br /&gt;
my $water_volume = AttrVal($pool_dummy,&amp;quot;water_volume&amp;quot;,42); #42m³&lt;br /&gt;
my $desired_cf_l1 = AttrVal($pool_dummy,&amp;quot;desired_cf_l1&amp;quot;,0); #circulation_factor_level1&lt;br /&gt;
my $desired_cf_l2 = AttrVal($pool_dummy,&amp;quot;desired_cf_l2&amp;quot;,0); #circulation_factor_level2&lt;br /&gt;
my $bathmode_factor = AttrVal($pool_dummy,&amp;quot;bathmode_factor&amp;quot;,1);&lt;br /&gt;
my $desired_amount_l1;&lt;br /&gt;
my $desired_amount_l2;&lt;br /&gt;
my $amount = $m3_l1 + $m3_l2;&lt;br /&gt;
if($bath_mode == 1){&lt;br /&gt;
	$desired_amount_l1 = $desired_cf_l1 * $water_volume;&lt;br /&gt;
	$desired_amount_l2 = $desired_cf_l2 * $water_volume;&lt;br /&gt;
}else{&lt;br /&gt;
	$desired_amount_l1 = $desired_cf_l1 * $water_volume/$bathmode_factor;&lt;br /&gt;
	$desired_amount_l2 = $desired_cf_l2 * $water_volume/$bathmode_factor;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
if(($amount &amp;gt; $desired_amount_l1)&lt;br /&gt;
	&amp;amp;&amp;amp; (ReadingsVal($pool_dummy,&amp;quot;pump_high&amp;quot;,&amp;quot;on&amp;quot;) eq &amp;quot;off&amp;quot;  &amp;amp;&amp;amp; ReadingsAge($pool_dummy,&amp;quot;pump_high&amp;quot;,0) &amp;gt;160)&lt;br /&gt;
	&amp;amp;&amp;amp; ReadingsVal($pool_dummy,&amp;quot;special_function&amp;quot;,&amp;quot;&amp;quot;) eq &amp;quot;none&amp;quot;&lt;br /&gt;
	&amp;amp;&amp;amp; ReadingsVal($pool_dummy,&amp;quot;valve_position&amp;quot;,&amp;quot;&amp;quot;) eq &amp;quot;normal&amp;quot;&lt;br /&gt;
)&lt;br /&gt;
	{&lt;br /&gt;
		return 1;&lt;br /&gt;
	}else{&lt;br /&gt;
		return 0;&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Einschalten:&lt;br /&gt;
&lt;br /&gt;
* Es wird der errechnete Wert für den Überschuss von SolarForecast verwendet und auch der benötigte Überschuss aus dem Consumer ausgelesen.&lt;br /&gt;
* Das Einschalten wird erst durch erfolgreiche Prüfung der Hardwarevoraussetzungen erlaubt. -&amp;gt; Das Motorventil steht richtig und es ist kein Sonderbetrieb (Reinigung, Massage, Winter, o.ä.) aktiv.&lt;br /&gt;
* Es muss entweder genügend Überschuss vorhanden sein oder es ist 8:00.&lt;br /&gt;
&lt;br /&gt;
Ausschalten:&lt;br /&gt;
&lt;br /&gt;
* Wenn der Badebetrieb aktiv ist (bath_mode==1, [bei Außentemperatur &amp;gt; 24°C]) wird nach 90m³ Gesamtumwälzung ausgeschaltet.&lt;br /&gt;
* Wenn der Badebetrieb nicht aktiv ist (bath_mode==0, [bei Außentemperatur &amp;lt; 20°C]) wird bereits nach 42m³ Gesamtumwälzung ausgeschaltet.&lt;br /&gt;
* Prüfung, ob Pump High schon aus ist. Sollte durch die Logik gar nicht möglich sein, aber es könnte manuell oder durch einen (Logik-)Fehler trotzdem eingeschaltet sein.&lt;br /&gt;
* Prüfung, ob alle Hardware-Funktionen in einem für die Automatik geeigneten Zustand sind.&lt;br /&gt;
* Es wird auch geprüft, ob Pump High seit mindestens 3 Minuten aus ist, um zu verhindern, dass wegen fehlendem Überschuss gleich mehrere Geräte ausgeschaltet werden.&lt;br /&gt;
* Da die Zykluszeit 60 Sekunden ist, führen die 160 Sekunden dazu, dass nach drei Minuten(180 Sekunden) ein definierter Wert gilt. Mit 180 Sekunden könnte es manchaml eine Minute mehr sein.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Pump High&#039;&#039;&#039;&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
####################&lt;br /&gt;
### Pumpe - HIGH ###&lt;br /&gt;
####################&lt;br /&gt;
&lt;br /&gt;
sub&lt;br /&gt;
Check_Pump_High_On{&lt;br /&gt;
my $bath_mode = ReadingsVal($pool_dummy,&amp;quot;bath_mode&amp;quot;,0);&lt;br /&gt;
my $m3_l1 = ReadingsVal($water_counter_l1,&amp;quot;waterAmount&amp;quot;,0);&lt;br /&gt;
my $m3_l2 = ReadingsVal($water_counter_l2,&amp;quot;waterAmount&amp;quot;,0);&lt;br /&gt;
my $water_volume = AttrVal($pool_dummy,&amp;quot;water_volume&amp;quot;,42); #42m³&lt;br /&gt;
my $desired_cf_l1 = AttrVal($pool_dummy,&amp;quot;desired_cf_l1&amp;quot;,0); #circulation_factor_level1&lt;br /&gt;
my $desired_cf_l2 = AttrVal($pool_dummy,&amp;quot;desired_cf_l2&amp;quot;,0); #circulation_factor_level2&lt;br /&gt;
my $bathmode_factor = AttrVal($pool_dummy,&amp;quot;bathmode_factor&amp;quot;,1);&lt;br /&gt;
my $desired_amount_l1;&lt;br /&gt;
my $desired_amount_l2;&lt;br /&gt;
my $amount = $m3_l1 + $m3_l2;&lt;br /&gt;
if($bath_mode == 1){&lt;br /&gt;
	$desired_amount_l1 = $desired_cf_l1 * $water_volume;&lt;br /&gt;
	$desired_amount_l2 = $desired_cf_l2 * $water_volume;&lt;br /&gt;
}else{&lt;br /&gt;
	$desired_amount_l1 = $desired_cf_l1 * $water_volume/$bathmode_factor;&lt;br /&gt;
	$desired_amount_l2 = $desired_cf_l2 * $water_volume/$bathmode_factor;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
if(($amount &amp;lt; $desired_amount_l2)&lt;br /&gt;
	&amp;amp;&amp;amp; ReadingsVal($pool_dummy,&amp;quot;special_function&amp;quot;,&amp;quot;&amp;quot;) eq &amp;quot;none&amp;quot;&lt;br /&gt;
	&amp;amp;&amp;amp; ReadingsVal($pool_dummy,&amp;quot;valve_position&amp;quot;,&amp;quot;&amp;quot;) eq &amp;quot;normal&amp;quot;&lt;br /&gt;
	&amp;amp;&amp;amp; (ReadingsVal($pool_dummy,&amp;quot;pump_low&amp;quot;,&amp;quot;off&amp;quot;) eq &amp;quot;on&amp;quot;  &amp;amp;&amp;amp; ReadingsAge($pool_dummy,&amp;quot;pump_low&amp;quot;,0) &amp;gt; $switch_delay)&lt;br /&gt;
)&lt;br /&gt;
	{return 1}&lt;br /&gt;
else&lt;br /&gt;
	{return 0}&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
sub&lt;br /&gt;
Check_Pump_High_Off{&lt;br /&gt;
my $bath_mode = ReadingsVal($pool_dummy,&amp;quot;bath_mode&amp;quot;,0);&lt;br /&gt;
my $m3_l1 = ReadingsVal($water_counter_l1,&amp;quot;waterAmount&amp;quot;,0);&lt;br /&gt;
my $m3_l2 = ReadingsVal($water_counter_l2,&amp;quot;waterAmount&amp;quot;,0);&lt;br /&gt;
my $water_volume = AttrVal($pool_dummy,&amp;quot;water_volume&amp;quot;,42); #42m³&lt;br /&gt;
my $desired_cf_l1 = AttrVal($pool_dummy,&amp;quot;desired_cf_l1&amp;quot;,0); #circulation_factor_level1&lt;br /&gt;
my $desired_cf_l2 = AttrVal($pool_dummy,&amp;quot;desired_cf_l2&amp;quot;,0); #circulation_factor_level2&lt;br /&gt;
my $bathmode_factor = AttrVal($pool_dummy,&amp;quot;bathmode_factor&amp;quot;,1);&lt;br /&gt;
my $desired_amount_l1;&lt;br /&gt;
my $desired_amount_l2;&lt;br /&gt;
my $amount = $m3_l1 + $m3_l2;&lt;br /&gt;
&lt;br /&gt;
if($bath_mode == 1){&lt;br /&gt;
	$desired_amount_l1 = $desired_cf_l1 * $water_volume;&lt;br /&gt;
	$desired_amount_l2 = $desired_cf_l2 * $water_volume;&lt;br /&gt;
}else{&lt;br /&gt;
	$desired_amount_l1 = $desired_cf_l1 * $water_volume/$bathmode_factor;&lt;br /&gt;
	$desired_amount_l2 = $desired_cf_l2 * $water_volume/$bathmode_factor;&lt;br /&gt;
}&lt;br /&gt;
if(($amount &amp;gt; $desired_amount_l2)&lt;br /&gt;
	&amp;amp;&amp;amp; (ReadingsVal($pool_dummy,&amp;quot;heatpump&amp;quot;,&amp;quot;on&amp;quot;) eq &amp;quot;off&amp;quot;  &amp;amp;&amp;amp; ReadingsAge($pool_dummy,&amp;quot;heatpump&amp;quot;,0) &amp;gt;160)&lt;br /&gt;
	&amp;amp;&amp;amp; ReadingsVal($pool_dummy,&amp;quot;special_function&amp;quot;,&amp;quot;&amp;quot;) eq &amp;quot;none&amp;quot;&lt;br /&gt;
	&amp;amp;&amp;amp; ReadingsVal($pool_dummy,&amp;quot;valve_position&amp;quot;,&amp;quot;&amp;quot;) eq &amp;quot;normal&amp;quot;&lt;br /&gt;
)&lt;br /&gt;
	{return 1}&lt;br /&gt;
else&lt;br /&gt;
	{return 0}&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Einschalten:&lt;br /&gt;
&lt;br /&gt;
* Prüfung, ob die gewünschte Umwälzung bezüglich des Badebetriebs noch nicht erreicht wurde.&lt;br /&gt;
* Prüfung der Hardwarevoraussetzungen.&lt;br /&gt;
* Prüfung, ob Pump Low schon seit mindestens drei Minuten aktiv ist. (sollte eigentlich immer der Fall sein, wenn die Automatik für Pump High aktiv ist)&lt;br /&gt;
&lt;br /&gt;
Ausschalten:&lt;br /&gt;
&lt;br /&gt;
* Prüfung, ob die gewünschte Umwälzung bezüglich des Badebetriebs bereits erreicht wurde.&lt;br /&gt;
* Prüfung der Hardwarevoraussetzungen.&lt;br /&gt;
* Prüfung, ob die Wärmepumpe schon seit mindestens drei Minuten aus ist. Ermöglicht drei Minuten lang, den Median für den Überschuss zu aktualisieren.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Wärmepumpe&#039;&#039;&#039;&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
##################&lt;br /&gt;
### Wärempumpe ###&lt;br /&gt;
##################&lt;br /&gt;
sub&lt;br /&gt;
CheckWPOn(){&lt;br /&gt;
my $name = &#039;energy_mgmt&#039;;&lt;br /&gt;
my $surplus_on = 5000; # minimum surplus to avoid infeed limit with heatpump&lt;br /&gt;
my $surplus_restart = 3000; # minimum surplus to re-enable heatpump, when avoidung infeed limit and a big consumer turned the hp off&lt;br /&gt;
&lt;br /&gt;
my $planstate = FHEM::SolarForecast::ConsumerVal ($name,$heatpump_id,&#039;planstate&#039;,&#039;&#039;);&lt;br /&gt;
my $surplus	= FHEM::SolarForecast::ConsumerVal ($name,$heatpump_id,&#039;surpmethResult&#039;,&#039;&#039;);&lt;br /&gt;
if ($planstate =~ /^([^:]+)/) {&lt;br /&gt;
	$planstate = $1;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
if(ReadingsVal($flow_sensor,&amp;quot;reading&amp;quot;,&amp;quot;off&amp;quot;) eq &amp;quot;on&amp;quot;&lt;br /&gt;
	&amp;amp;&amp;amp; (ReadingsVal($pool_dummy,&amp;quot;special_function&amp;quot;,&amp;quot;&amp;quot;) eq &amp;quot;none&amp;quot;&lt;br /&gt;
		|| ReadingsVal($pool_dummy,&amp;quot;special_function&amp;quot;,&amp;quot;&amp;quot;) eq &amp;quot;party&amp;quot;&lt;br /&gt;
	)&lt;br /&gt;
	&amp;amp;&amp;amp; ReadingsVal($pool_dummy,&#039;heat_protection&#039;,&#039;on&#039;) eq &amp;quot;off&amp;quot;&lt;br /&gt;
	&amp;amp;&amp;amp; ReadingsVal($pool_dummy,&amp;quot;valve_position&amp;quot;,&amp;quot;&amp;quot;) eq &amp;quot;normal&amp;quot;&lt;br /&gt;
	&amp;amp;&amp;amp; (ReadingsVal($pool_dummy,&amp;quot;pump_high&amp;quot;,&amp;quot;off&amp;quot;) eq &amp;quot;on&amp;quot;  &amp;amp;&amp;amp; ReadingsAge($pool_dummy,&amp;quot;pump_high&amp;quot;,0) &amp;gt; $switch_delay)&lt;br /&gt;
	&amp;amp;&amp;amp; (ReadingsVal($pool_temperature,&amp;quot;temperature&amp;quot;,30) &amp;lt; $target_temp &lt;br /&gt;
		|| (ReadingsVal($pool_temperature,&amp;quot;temperature&amp;quot;,15) &amp;gt;= $target_temp&lt;br /&gt;
			&amp;amp;&amp;amp; ( $surplus &amp;gt; $surplus_on&lt;br /&gt;
				|| (($planstate eq &amp;quot;replanned&amp;quot;) &amp;amp;&amp;amp; ($surplus &amp;gt; $surplus_restart))&lt;br /&gt;
			)&lt;br /&gt;
		)&lt;br /&gt;
	)&lt;br /&gt;
)&lt;br /&gt;
	{return 1}&lt;br /&gt;
else&lt;br /&gt;
	{return 0}&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
sub&lt;br /&gt;
CheckWPOff{&lt;br /&gt;
my $name = &#039;energy_mgmt&#039;;&lt;br /&gt;
my $surplus_off = 1000; #minimum surplus to keep heatpump on, when avoiding infeed limit&lt;br /&gt;
&lt;br /&gt;
my $surplus	= FHEM::SolarForecast::ConsumerVal ($name,$heatpump_id,&#039;surpmethResult&#039;,&#039;&#039;);&lt;br /&gt;
if((ReadingsVal($flow_sensor,&amp;quot;reading&amp;quot;,&amp;quot;on&amp;quot;) eq &amp;quot;off&amp;quot;&lt;br /&gt;
	|| !((ReadingsVal($pool_dummy,&amp;quot;special_function&amp;quot;,&amp;quot;&amp;quot;) eq &amp;quot;none&amp;quot;) &lt;br /&gt;
		|| (ReadingsVal($pool_dummy,&amp;quot;special_function&amp;quot;,&amp;quot;&amp;quot;) eq &amp;quot;party&amp;quot;)&lt;br /&gt;
	)&lt;br /&gt;
	|| ReadingsVal($pool_dummy,&#039;heat_protection&#039;,&#039;off&#039;) eq &amp;quot;on&amp;quot;&lt;br /&gt;
	|| ReadingsVal($pool_dummy,&amp;quot;valve_position&amp;quot;,&amp;quot;&amp;quot;) ne &amp;quot;normal&amp;quot;&lt;br /&gt;
	|| ((ReadingsVal($pool_temperature,&amp;quot;temperature&amp;quot;,15) &amp;gt; $target_temp)&lt;br /&gt;
		&amp;amp;&amp;amp; ($surplus &amp;lt; $surplus_off)&lt;br /&gt;
		)&lt;br /&gt;
	)&lt;br /&gt;
	&amp;amp;&amp;amp; (ReadingsVal($pool_dummy,&amp;quot;heatpump&amp;quot;,&amp;quot;off&amp;quot;) eq &amp;quot;on&amp;quot;  &amp;amp;&amp;amp; ReadingsAge($pool_dummy,&amp;quot;heatpump&amp;quot;,0) &amp;gt; $switch_delay)&lt;br /&gt;
)&lt;br /&gt;
	{return 1}&lt;br /&gt;
else&lt;br /&gt;
	{return 0}&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Einschalten:&lt;br /&gt;
&lt;br /&gt;
* Prüfung, ob Flussensor ein. (Pool_pin_A0) -&amp;gt; Wasser fließt durch die Wärmepumpe.&lt;br /&gt;
* Prüfung, ob Pool in Normal- oder Partybetrieb ist und das Ventil in Normalposition ist. In beiden Fällen läuft Pump High.&lt;br /&gt;
* Einschalten wenn genug Überschuss da ist und die Temperatur &amp;lt;28.4°C ist oder wenn über 5000W Überschuss (Einspeisebegrenzung bei 5900W) da ist, egal wie warm der Pool ist.&lt;br /&gt;
* Sonderfall: Wenn die Wärmepumpe durch einen/mehrere Großverbrucher &amp;quot;kurz&amp;quot; ausgeschaltet wurde (swoffcond) wird sie ausgeplant. Ein Notify plant sie wieder ein. Wenn das passiert, soll die Wärmepumpe aber wieder bei 3000W Überschuss gestartet werden, um die Energie noch weiter zu nutzen bis der Überschuss unter 1000W fällt. (Hysterese von 500W, da die WP nur 1500W braucht)&lt;br /&gt;
&lt;br /&gt;
Ausschalten:&lt;br /&gt;
&lt;br /&gt;
* Ausschalten sobald hardwaremäßig irgendetwas faul ist. (Betriebsart, Ventilstellung, Durchfluss)&lt;br /&gt;
* Ausschalten wenn kein Überschuss. (interruptable=1)&lt;br /&gt;
* Wenn Wassertemperatur über 28,5°C und der Überschuss kleiner als 1000W ist.&lt;br /&gt;
* Nur schalten, wenn Wärmepumpe läuft und seit mindestens drei Minuten läuft (Sicherheit). Das verhindert das erneute Triggern des Notifys, wenn die Pumpe schon aus ist. Sollte nicht passieren, aber sicher ist sicher.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Nachbar Pool&#039;&#039;&#039;&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
###################&lt;br /&gt;
### Pool - KURZ ###&lt;br /&gt;
###################&lt;br /&gt;
sub&lt;br /&gt;
Check_Pool_Kurz_On{&lt;br /&gt;
my $name = &#039;energy_mgmt&#039;;&lt;br /&gt;
my $surplus	= FHEM::SolarForecast::ConsumerVal ($name,$pool_kurz_id,&#039;surpmethResult&#039;,&#039;&#039;);&lt;br /&gt;
if(($surplus &amp;gt; 3000)&lt;br /&gt;
	&amp;amp;&amp;amp; ((ReadingsVal($pool_dummy,&amp;quot;heatpump&amp;quot;,&amp;quot;off&amp;quot;) eq &amp;quot;on&amp;quot;  &amp;amp;&amp;amp; ReadingsAge($pool_dummy,&amp;quot;heatpump&amp;quot;,0) &amp;gt;160) || ReadingsVal($pool_dummy,&#039;heat_protection&#039;,&#039;off&#039;) eq &#039;on&#039; ))&lt;br /&gt;
	{return 1}&lt;br /&gt;
else&lt;br /&gt;
	{return 0}&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
sub&lt;br /&gt;
Check_Pool_Kurz_Off{&lt;br /&gt;
my $name = &#039;energy_mgmt&#039;;&lt;br /&gt;
my $surplus	= FHEM::SolarForecast::ConsumerVal ($name,$pool_kurz_id,&#039;surpmethResult&#039;,&#039;&#039;);&lt;br /&gt;
if(($surplus &amp;lt; 1000 )&lt;br /&gt;
	&amp;amp;&amp;amp; (ReadingsVal($pool_dummy,&amp;quot;pool_kurz&amp;quot;,&amp;quot;off&amp;quot;) eq &amp;quot;on&amp;quot;  &amp;amp;&amp;amp; ReadingsAge($pool_dummy,&amp;quot;pool_kurz&amp;quot;,0) &amp;gt;160))&lt;br /&gt;
	{return 1}&lt;br /&gt;
else&lt;br /&gt;
	{return 0}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Einschalten:&lt;br /&gt;
&lt;br /&gt;
* Überschuss ist nahe der Einspeisegrenze (5900W) und Wärmepumpe läuft seit mindestens drei Minuten. Es wird schon bei 3000W eingeschaltet, um dem Nachbarn etwas Gutes zu tun ;-)&lt;br /&gt;
&lt;br /&gt;
Ausschalten:&lt;br /&gt;
&lt;br /&gt;
* Überschuss ist unter 1000W. -&amp;gt; Um noch Reserven für den eigenen Verbrauch zu haben.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Hilfsfunktionen&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ctrlUserExitFn&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Hier erfolgt nach jedem Zyklus die Festlegung der verschiedenen Automatikmodi.&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
#############################################################&lt;br /&gt;
##################### Exit Function #########################&lt;br /&gt;
#############################################################&lt;br /&gt;
my $ctrl_dummy	= &amp;quot;dum_valve&amp;quot;; # dummy device for pool-control&lt;br /&gt;
my $automatic	= ReadingsNum($ctrl_dummy,&amp;quot;sf_automatic&amp;quot;,1);&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
if($automatic){&lt;br /&gt;
&lt;br /&gt;
	my $pump_low_state = ReadingsVal($ctrl_dummy,&#039;pump_low&#039;,&#039;on&#039;);&lt;br /&gt;
	my $pump_low_auto	= FHEM::SolarForecast::ConsumerVal ($name, $pump_low_id, &#039;auto&#039;, &#039;&#039;);	# Automatikmodus der Pumpe&lt;br /&gt;
&lt;br /&gt;
	my $pump_high_state = ReadingsVal($ctrl_dummy,&#039;pump_high&#039;,&#039;on&#039;);&lt;br /&gt;
	my $pump_high_auto	= FHEM::SolarForecast::ConsumerVal ($name, $pump_high_id, &#039;auto&#039;, &#039;&#039;);	# Automatikmodus der Pumpe&lt;br /&gt;
&lt;br /&gt;
	my $heatpump_state = ReadingsVal($ctrl_dummy,&#039;heatpump&#039;,&#039;on&#039;);&lt;br /&gt;
	my $heatpump_auto	= FHEM::SolarForecast::ConsumerVal ($name, $heatpump_id, &#039;auto&#039;, &#039;&#039;);	# Automatikmodus der Wärmepumpe&lt;br /&gt;
	my $pool_heat_protection = ReadingsVal($ctrl_dummy,&#039;heat_protection&#039;,&#039;on&#039;);&lt;br /&gt;
&lt;br /&gt;
	my $pool_kurz_state = ReadingsVal($ctrl_dummy,&#039;pool_kurz&#039;,&#039;on&#039;);&lt;br /&gt;
	my $pool_kurz_auto	= FHEM::SolarForecast::ConsumerVal ($name, $pool_kurz_id, &#039;auto&#039;, &#039;&#039;);	# Automatikmodus des Pools&lt;br /&gt;
&lt;br /&gt;
	my $pump_low_delay	= FHEM::SolarForecast::ConsumerVal ($name, $pump_low_id, &#039;surpmeth&#039;, &#039;&#039;);&lt;br /&gt;
	if ($pump_low_delay =~ /_(\d+)/) {&lt;br /&gt;
    	$pump_low_delay = int(($1/2 + 1) * 60);&lt;br /&gt;
		Log3 (&amp;quot;SolarForecast&amp;quot;, 1, &amp;quot;SolarForecast: pump_low_delay: $pump_low_delay&amp;quot;);&lt;br /&gt;
	} else {&lt;br /&gt;
    	$pump_low_delay = 220;&lt;br /&gt;
		Log3 (&amp;quot;SolarForecast&amp;quot;, 1, &amp;quot;SolarForecast: warning - surpmeth for pump_low is: $pump_low_delay, but must be given as method_number, using fallback value&amp;quot;);&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	my $pump_high_delay	= FHEM::SolarForecast::ConsumerVal ($name, $pump_high_id, &#039;surpmeth&#039;, &#039;&#039;);&lt;br /&gt;
	if ($pump_high_delay =~ /_(\d+)/) {&lt;br /&gt;
    	$pump_high_delay = int(($1/2 + 1) * 60);&lt;br /&gt;
	} else {&lt;br /&gt;
    	$pump_high_delay = 160;&lt;br /&gt;
		Log3 (&amp;quot;SolarForecast&amp;quot;, 1, &amp;quot;SolarForecast: warning - surpmeth for pump_high is: $pump_high_delay, but must be given as method_number, using fallback value&amp;quot;);&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	my $heatpump_delay	= FHEM::SolarForecast::ConsumerVal ($name, $heatpump_id, &#039;surpmeth&#039;, &#039;&#039;);&lt;br /&gt;
	if ($heatpump_delay =~ /_(\d+)/) {&lt;br /&gt;
    	$heatpump_delay = int(($1/2 + 1) * 60);&lt;br /&gt;
	} else {&lt;br /&gt;
    	$heatpump_delay = 290;&lt;br /&gt;
		Log3 (&amp;quot;SolarForecast&amp;quot;, 1, &amp;quot;SolarForecast: warning - surpmeth for heatpump is: $heatpump_delay, but must be given as method_number, using fallback value&amp;quot;);&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	my $pool_kurz_delay	= FHEM::SolarForecast::ConsumerVal ($name, $pool_kurz_id, &#039;surpmeth&#039;, &#039;&#039;);&lt;br /&gt;
	if ($pool_kurz_delay =~ /_(\d+)/) {&lt;br /&gt;
    	$pool_kurz_delay = int(($1/2 + 1) * 60);&lt;br /&gt;
	} else {&lt;br /&gt;
    	$pool_kurz_delay = 160;&lt;br /&gt;
		Log3 (&amp;quot;SolarForecast&amp;quot;, 1, &amp;quot;SolarForecast: warning - surpmeth for pool_kurz is: $pool_kurz_delay, but must be given as method_number&amp;quot;);&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
############################&lt;br /&gt;
# Überhitzungsschutz aktiv #&lt;br /&gt;
############################&lt;br /&gt;
&lt;br /&gt;
if($pool_heat_protection eq &#039;on&#039;){&lt;br /&gt;
	if($heatpump_auto == 1){&lt;br /&gt;
		fhem(&amp;quot;set $ctrl_dummy heatpump_auto 0&amp;quot;);&lt;br /&gt;
	}&lt;br /&gt;
	if($heatpump_state eq &amp;quot;on&amp;quot;){&lt;br /&gt;
			fhem(&amp;quot;set $ctrl_dummy heatpump off&amp;quot;)&lt;br /&gt;
	}&lt;br /&gt;
	#################&lt;br /&gt;
	#### on-chain ###&lt;br /&gt;
	#################&lt;br /&gt;
	if($pump_low_state eq &amp;quot;on&amp;quot; &amp;amp;&amp;amp; ReadingsAge($ctrl_dummy,&#039;pump_low&#039;,0) &amp;gt; $pump_high_delay &amp;amp;&amp;amp; $pump_high_auto == 0  &amp;amp;&amp;amp; $pool_kurz_auto == 0){&lt;br /&gt;
		fhem(&amp;quot;set $ctrl_dummy pump_high_auto 1&amp;quot;);&lt;br /&gt;
	}elsif($pump_high_state eq &amp;quot;on&amp;quot; &amp;amp;&amp;amp; ReadingsAge($ctrl_dummy,&#039;pump_high&#039;,0) &amp;gt; $pool_kurz_delay &amp;amp;&amp;amp; $pool_kurz_auto == 0 &amp;amp;&amp;amp; $pump_high_auto == 1){&lt;br /&gt;
		fhem(&amp;quot;set $ctrl_dummy pool_kurz_auto 1&amp;quot;);&lt;br /&gt;
	}elsif($pool_kurz_state eq &amp;quot;on&amp;quot; &amp;amp;&amp;amp; $pool_kurz_auto == 1 &amp;amp;&amp;amp; $pump_high_auto == 1){&lt;br /&gt;
		fhem(&amp;quot;set $ctrl_dummy pump_high_auto 0&amp;quot;);&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	#################&lt;br /&gt;
	### off-chain ###&lt;br /&gt;
	#################&lt;br /&gt;
	if($pool_kurz_state eq &amp;quot;off&amp;quot; &amp;amp;&amp;amp; ReadingsAge($ctrl_dummy,&#039;pool_kurz&#039;,0) &amp;gt; $pump_high_delay &amp;amp;&amp;amp; $pool_kurz_auto == 1 &amp;amp;&amp;amp; $pump_high_auto == 0){&lt;br /&gt;
		fhem(&amp;quot;set $ctrl_dummy pump_high_auto 1&amp;quot;);&lt;br /&gt;
	}elsif($pump_high_state eq &amp;quot;off&amp;quot; &amp;amp;&amp;amp; $pump_high_auto == 1 &amp;amp;&amp;amp; $pool_kurz_auto == 1){&lt;br /&gt;
		fhem(&amp;quot;set $ctrl_dummy pool_kurz_auto 0&amp;quot;);&lt;br /&gt;
	}elsif($pump_low_state eq &amp;quot;off&amp;quot; &amp;amp;&amp;amp; $pump_high_state eq &amp;quot;off&amp;quot; &amp;amp;&amp;amp; $pump_high_auto == 1 &amp;amp;&amp;amp; $pool_kurz_auto == 0){&lt;br /&gt;
		fhem(&amp;quot;set $ctrl_dummy pump_high_auto 0&amp;quot;);&lt;br /&gt;
	}&lt;br /&gt;
##################################&lt;br /&gt;
# Überhitzungsschutz nicht aktiv #&lt;br /&gt;
##################################&lt;br /&gt;
}else{&lt;br /&gt;
	if($heatpump_auto == 0 &amp;amp;&amp;amp; $heatpump_state eq &amp;quot;off&amp;quot;){&lt;br /&gt;
		if($pool_kurz_state eq &amp;quot;on&amp;quot;){&lt;br /&gt;
			fhem(&amp;quot;set $ctrl_dummy pool_kurz off&amp;quot;);&lt;br /&gt;
		}	&lt;br /&gt;
		if($pool_kurz_auto == 1){&lt;br /&gt;
			fhem(&amp;quot;set $ctrl_dummy pool_kurz_auto 0&amp;quot;);&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
	#################&lt;br /&gt;
	#### on-chain ###&lt;br /&gt;
	#################&lt;br /&gt;
	if($pump_low_state eq &amp;quot;on&amp;quot; &amp;amp;&amp;amp; ReadingsAge($ctrl_dummy,&#039;pump_low&#039;,0) &amp;gt; $pump_high_delay &amp;amp;&amp;amp; $pump_high_auto == 0 &amp;amp;&amp;amp; $heatpump_auto == 0 &amp;amp;&amp;amp; $pool_kurz_auto == 0){&lt;br /&gt;
		fhem(&amp;quot;set $ctrl_dummy pump_high_auto 1&amp;quot;);&lt;br /&gt;
	}elsif($pump_high_state eq &amp;quot;on&amp;quot; &amp;amp;&amp;amp; ReadingsAge($ctrl_dummy,&#039;pump_high&#039;,0) &amp;gt; $heatpump_delay &amp;amp;&amp;amp; $heatpump_auto == 0 &amp;amp;&amp;amp; $pool_kurz_auto == 0 &amp;amp;&amp;amp; $pump_high_auto == 1){&lt;br /&gt;
		fhem(&amp;quot;set $ctrl_dummy heatpump_auto 1&amp;quot;);&lt;br /&gt;
	}elsif($heatpump_state eq &amp;quot;on&amp;quot; &amp;amp;&amp;amp; ReadingsAge($ctrl_dummy,&#039;heatpump&#039;,0) &amp;gt; $pool_kurz_delay &amp;amp;&amp;amp; $pump_high_state eq &amp;quot;on&amp;quot; &amp;amp;&amp;amp; $pool_kurz_auto == 0 &amp;amp;&amp;amp; $pump_high_auto == 1 &amp;amp;&amp;amp; $heatpump_auto == 1){&lt;br /&gt;
		# pump_high muss on sein, da sonst die automatik ausgeschaltet wird wenn die pumpe gerade interrupted ist.&lt;br /&gt;
		# Bei unterschiedlichen Medianlängen ist es möglich, dass WP ein ist und pump_high abschaltet wegen surplus == 0&lt;br /&gt;
		# Nachbarpool würde auf EIN warten, aber pump_high dürfte nicht wieder einschalten, obwohl genügend Überschuss da ist.&lt;br /&gt;
		fhem(&amp;quot;set $ctrl_dummy pump_high_auto 0&amp;quot;);&lt;br /&gt;
		fhem(&amp;quot;set $ctrl_dummy pool_kurz_auto 1&amp;quot;);&lt;br /&gt;
	}elsif($pool_kurz_state eq &amp;quot;on&amp;quot; &amp;amp;&amp;amp; $pool_kurz_auto == 1 &amp;amp;&amp;amp; $heatpump_auto == 1 &amp;amp;&amp;amp; $pump_high_auto == 0){&lt;br /&gt;
		fhem(&amp;quot;set $ctrl_dummy heatpump_auto 0&amp;quot;);&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	#################&lt;br /&gt;
	### off-chain ###&lt;br /&gt;
	#################&lt;br /&gt;
	if($pool_kurz_state eq &amp;quot;off&amp;quot; &amp;amp;&amp;amp; ReadingsAge($ctrl_dummy,&#039;pool_kurz&#039;,0) &amp;gt; $heatpump_delay &amp;amp;&amp;amp; $pool_kurz_auto == 1 &amp;amp;&amp;amp; $heatpump_auto == 0 &amp;amp;&amp;amp; $pump_high_auto == 0){&lt;br /&gt;
		fhem(&amp;quot;set $ctrl_dummy heatpump_auto 1&amp;quot;);&lt;br /&gt;
	}elsif($heatpump_state eq &amp;quot;off&amp;quot; &amp;amp;&amp;amp; ReadingsAge($ctrl_dummy,&#039;heatpump&#039;,0) &amp;gt; $pump_high_delay &amp;amp;&amp;amp; $heatpump_auto == 1 &amp;amp;&amp;amp; $pump_high_auto == 0 &amp;amp;&amp;amp; $pool_kurz_auto == 1){&lt;br /&gt;
		fhem(&amp;quot;set $ctrl_dummy pool_kurz_auto 0&amp;quot;);&lt;br /&gt;
		fhem(&amp;quot;set $ctrl_dummy pump_high_auto 1&amp;quot;);&lt;br /&gt;
	}elsif($pump_high_state eq &amp;quot;off&amp;quot; &amp;amp;&amp;amp; $heatpump_state eq &amp;quot;off&amp;quot; &amp;amp;&amp;amp; $pump_high_auto == 1 &amp;amp;&amp;amp; $heatpump_auto == 1 &amp;amp;&amp;amp; $pool_kurz_auto == 0){&lt;br /&gt;
#	heatpump muss aus sein, da sonst vor Ablauf der Locktime die Automatik ausgeschaltet wird und die WP nicht mehr ausschalten kann.&lt;br /&gt;
		fhem(&amp;quot;set $ctrl_dummy heatpump_auto 0&amp;quot;);&lt;br /&gt;
	}elsif($pump_low_state eq &amp;quot;off&amp;quot; &amp;amp;&amp;amp; $pump_high_state eq &amp;quot;off&amp;quot; &amp;amp;&amp;amp; $pump_high_auto == 1 &amp;amp;&amp;amp; $heatpump_auto == 0){&lt;br /&gt;
		fhem(&amp;quot;set $ctrl_dummy pump_high_auto 0&amp;quot;);&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
}&lt;br /&gt;
#################################################&lt;br /&gt;
# Check hardware-state vs. reading-state&lt;br /&gt;
# re-trigger notify&lt;br /&gt;
#################################################&lt;br /&gt;
my $delay_retrigger = 100; #delay time to wait until re-trigger the notify&lt;br /&gt;
&lt;br /&gt;
if(ReadingsVal($ctrl_dummy,&#039;pool_kurz&#039;,&#039;on&#039;) eq &#039;off&#039; &amp;amp;&amp;amp; ReadingsAge($ctrl_dummy,&#039;pool_kurz&#039;,0) &amp;gt; $delay_retrigger &amp;amp;&amp;amp; ReadingsVal($pool_kurz_sw_kurz,&#039;state&#039;,&#039;off&#039;) eq &#039;on&#039;){&lt;br /&gt;
	# re-trigger notify&lt;br /&gt;
	fhem(&amp;quot;trigger dum_valve pool_kurz: off&amp;quot;);&lt;br /&gt;
	Log3 (&amp;quot;SolarForecast&amp;quot;, 1, &amp;quot;SolarForecast: Pool-Kurz-off must be re-triggered&amp;quot;);&lt;br /&gt;
	DebianMail(&#039;clemens@familie-hofbauer.at&#039;,&#039;Notify Pool Kurz&#039;,&amp;quot;Notify hat nicht ausgeschaltet -&amp;gt; re-trigger&amp;quot;);&lt;br /&gt;
}elsif(ReadingsVal($ctrl_dummy,&#039;pool_kurz&#039;,&#039;off&#039;) eq &#039;on&#039; &amp;amp;&amp;amp; ReadingsAge($ctrl_dummy,&#039;pool_kurz&#039;,0) &amp;gt; $delay_retrigger &amp;amp;&amp;amp; ReadingsVal($pool_kurz_sw_kurz,&#039;state&#039;,&#039;on&#039;) eq &#039;off&#039;){&lt;br /&gt;
	# re-trigger notify&lt;br /&gt;
	fhem(&amp;quot;trigger dum_valve pool_kurz: on&amp;quot;);&lt;br /&gt;
	Log3 (&amp;quot;SolarForecast&amp;quot;, 1, &amp;quot;SolarForecast: Pool-Kurz-on must be re-triggered&amp;quot;);&lt;br /&gt;
	DebianMail(&#039;clemens@familie-hofbauer.at&#039;,&#039;Notify Pool Kurz&#039;,&amp;quot;Notify hat nicht eingeschaltet -&amp;gt; re-trigger&amp;quot;);&lt;br /&gt;
}elsif((ReadingsVal($pool_kurz_sw_kurz,&#039;state&#039;,&#039;off&#039;) eq &#039;set_on&#039; || ReadingsVal($pool_kurz_sw_kurz,&#039;state&#039;,&#039;off&#039;) eq &#039;set_off&#039;) &amp;amp;&amp;amp; ReadingsAge($pool_kurz_sw_kurz,&#039;state&#039;,0) &amp;gt; $delay_retrigger){&lt;br /&gt;
	# state not clear&lt;br /&gt;
	Log3 (&amp;quot;SolarForecast&amp;quot;, 1, &amp;quot;SolarForecast: Pool-Kurz state unclear&amp;quot;);&lt;br /&gt;
	DebianMail(&#039;clemens@familie-hofbauer.at&#039;,&#039;Status Pool Kurz unklar&#039;,&amp;quot;Status HM-Schalter set_xx&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
1;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Erläuterungen:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Verzögerungen der einzelnen Stufen:&#039;&#039;&#039;&lt;br /&gt;
** Die delays der einzelnen Stufen werden aus den surpmethods berechnet. Das dient dazu, dass der Median mit den Werten nach dem ein-/ausschalten des vorheriegen Verbrauchers bis zur Hälfte + einen Wert gefüllt wird und somit sicher das Umschalten des vorherigen Verbrauchers den Median nicht mehr beeinflusst.&lt;br /&gt;
* &#039;&#039;&#039;Überhitzungsschutz:&#039;&#039;&#039;&lt;br /&gt;
** Da durch die Wärmepumpe Überschuss &amp;quot;verheizt&amp;quot; wird, der durch das Einspeiselimit gar nicht produziert würde, kann es vorkommen, dass der Pool unangenehm warm wird.&lt;br /&gt;
** Um das zu vermeiden gibt es ein Reading, welches gesetzt wird, wenn der Pool eine gewisse Temperatur überschreitet. Dieses Setzen und Zurücksetzen erfolgt mit einer Hysterese von 0.2°C, um ein Takten der Wärmepumpe zu verhindern. Diese 0.4°C sind bei dieser Wassermenge einige Stunden.&lt;br /&gt;
** Wenn der Überhitzungsschutz aktiv ist, wird die Wärmepumpe in der Kette übersprungen.&lt;br /&gt;
** Wenn sich der Hitzeschutz während des Betriebs ändert, wird die Wärmepumpe bzw. der Pool vom Nachbarn passend ausgeschaltet und die Modi entsprechend gesetzt.&lt;br /&gt;
* &#039;&#039;&#039;On-chain:&#039;&#039;&#039;&lt;br /&gt;
** Wenn am Morgen Pump Low seit $pump_low_delay aktiv ist und alle folgenden Consumer noch nicht, dann wird die nächste Stufe freigegeben. -&amp;gt; Pump High&lt;br /&gt;
** Wenn Pump High seit $pump_high_delay aktiv ist und alle folgenden Consumer noch nicht, wird die Wärmepumpe freigegeben. -&amp;gt; Heatpump&lt;br /&gt;
** Wenn die Wärmepumpe seit $heatpump_delay aktiv ist und der letzte Consumer noch nicht, wird der letzte Verbraucher freigegeben. -&amp;gt; Pool Kurz Gleichzeitig wird die Automatik für Pump High ausgeschaltet, da dieser Verbraucher erst unterbrochen werden darf wenn die Wärmepumpe aus ist!&lt;br /&gt;
** Wenn der letzte Verbraucher aktiviert ist, wird die Automatik der Wärmepumpe deaktiviert, da ja zuerst der letzte Verbraucher (Pool Kurz) wieder ausgeschaltet werden muss, bevor der vorletzte (Wärmepumpe) wieder ausgeschaltet werden darf. Da es keinen nachfolgenden Verbraucher mehr gibt, kann hier auf ein delay verzichtet werden, da es nicht passieren kann, dass zwei Verbraucher wegen sehr hohem Überschuss unmittelbar hintereinander geschaltet werden können.&lt;br /&gt;
[[Datei:Poolsteuerung mit unterschiedlichen Automatikmodi.jpg|alternativtext=Poolsteuerung|mini|Consumer der Poolsteuerung|500x500px]]&lt;br /&gt;
&#039;&#039;&#039;Wichtig:&#039;&#039;&#039; Es dürfen immer nur maximal zwei unterbrechbare Consumer im Automatikmodus sein. Der &amp;quot;aktuelle&amp;quot;, der ja aufgrund von fehlendem Überschuss jederzeit unterbrochen werden kann und der &amp;quot;folgende&amp;quot; Consumer, der auf das Einschalten wartet. Pump Low ist immer im Automatikmodus, da diese nie unterbrochen wird. (mode=must, interruptable=0)&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Off-chain:&#039;&#039;&#039;&lt;br /&gt;
** Wenn der letzte Verbraucher seit mindestens der delay-Zeit aufgrund von fehlendem Überschuss inaktiv war, im Automatikmodus ist und der vorherige Verbraucher nicht im Automatikmodus ist, wird der vorherige Verbraucher in den Automatikmodus geschaltet und wartet jetzt auch auf das Ausschalten. Wenn der Überschuss steigt, springt man wieder in die on-chain und der letzte Verbraucher wird eingeschaltet und die Automatik des vorletzten Verbrauchers deaktiviert.&lt;br /&gt;
** Wenn die Wärmepumpe seit mindestens der delay-Zeit aus ist, sich im Automatikmodus befind und Pump High noch nicht im Automatikmodus ist, wird die Automatik für den Nachbarpool ausgeschaltet, da er ja jetzt nicht mehr einschalten darf, da zuerst die Wärmepumpe ein sein müsste. Die Automatik von Pump High wird wieder aktiviert, da diese jetzt auf das Ausschalten wartet. Steigt der Überschuss wieder, wird in den entsprechenden On-chain Zweig gesprungen.&lt;br /&gt;
** Wenn Pump High mindestens die delay-Zeit aus ist, wird die Automatik der Wärmepumpe deaktiviert, da diese ja erst einschalten darf, wenn Pump High wieder läuft. Steigender Überschuss führt wieder zum aktivieren von Pump High und zum Sprung in den entsprechenden On-chain Zweig.&lt;br /&gt;
** Wenn Pump Low ausgeschaltet wird, wird sofort die Automatik für Pump High ausgeschaltet, da durch das spätere Ausschalten von (der evtl. wieder eingeschalteten) Pump High, hardwaremäßig wieder Pump Low aktiviert wird, aber durch SolarForecast nie ausgeschaltet wird, da Pump Low vor ein paar Minuten ja schon logisch ausgeschaltet wurde.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Wichtig:&#039;&#039;&#039; Es dürfen immer nur maximal zwei unterbrechbare Consumer im Automatikmodus sein. Der &amp;quot;aktuelle&amp;quot;, der ja aufgrund von neuem Überschuss jederzeit wieder gestartet werden kann und der &amp;quot;vorherige&amp;quot; Consumer, der auf das Ausschalten wartet.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Synchronisation Hardware vs. Reading:&#039;&#039;&#039;Manchmal passiert es, dass durch die etwas aufwendigen Umschaltprozesse die Hardware dem Reading nicht folgt/folgen kann. Evtl. Gründe sind Funkunterbrechung, verzögerte Umschaltung usw. Daher wird bei jedem Zyklus der Status geprüft und evtl. das event nochmal getriggert, um die Umschaltung erneut auszuführen.&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Hilfs-Notifys für die Automatik&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Da durch das Verwenden von swoffcond die Verbraucher beim Ausschalten auch ausgeplant werden, aber eigentlich &amp;quot;nur&amp;quot; unterbrochen werden sollen, müssen sie wieder neu eingeplant werden. Dazu sind notifys nötig. Wichtig: IDs müssen zweistellig angegeben werden!&lt;br /&gt;
&lt;br /&gt;
Wärmepumpe:&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
dum_valve:heatpump:.off sleep 10; set energy_mgmt consumerNewPlanning 05&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Nachbar Pool:&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
dum_valve:pool_kurz:.off sleep 10; set energy_mgmt consumerNewPlanning 07&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Eine Pumpe mit einem Energiezähler als zwei Consumer verwenden&#039;&#039;&#039;  &lt;br /&gt;
&lt;br /&gt;
Die verwendete Pumpe hat vier Betriebsarten mit unterschiedlichen Leistungen. Da sie als zwei Consumer abgebildet wird, führt das dazu, dass die Leistung fälschlicherweise von SolarForecast auch zweimal gezählt wird. Die Lösung sind Dummy-Readings, welche die Leistungsdaten passend auseinander rechnen.&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
Pool_Strom_Pumpe:Pool_Pin31_monotonic_count_PowerCurrent:.* {&lt;br /&gt;
my $power = $EVTPART1;&lt;br /&gt;
my $pump_low = ReadingsVal(&#039;dum_valve&#039;,&#039;pump_low&#039;,&#039;off&#039;);&lt;br /&gt;
my $pump_high = ReadingsVal(&#039;dum_valve&#039;,&#039;pump_high&#039;,&#039;off&#039;);&lt;br /&gt;
my $power_low_sim = 250;&lt;br /&gt;
&lt;br /&gt;
if($pump_low eq &amp;quot;on&amp;quot; &amp;amp;&amp;amp; $pump_high eq &amp;quot;on&amp;quot;){	&lt;br /&gt;
	my $power_low = $power_low_sim; #fiktiver Verbrauch der niedrigen Stufe&lt;br /&gt;
	my $power_high = $power - $power_low_sim;&lt;br /&gt;
	fhem(&amp;quot;set dum_valve pump_low_power $power_low&amp;quot;);&lt;br /&gt;
	fhem(&amp;quot;set dum_valve pump_high_power $power_high&amp;quot;);&lt;br /&gt;
	}&lt;br /&gt;
elsif($pump_low eq &amp;quot;on&amp;quot; &amp;amp;&amp;amp; $pump_high eq &amp;quot;off&amp;quot;){&lt;br /&gt;
		fhem(&amp;quot;set dum_valve pump_low_power $power&amp;quot;);&lt;br /&gt;
		fhem(&amp;quot;set dum_valve pump_high_power 0&amp;quot;);&lt;br /&gt;
	}&lt;br /&gt;
elsif($pump_low eq &amp;quot;off&amp;quot; &amp;amp;&amp;amp; $pump_high eq &amp;quot;off&amp;quot;){&lt;br /&gt;
		fhem(&amp;quot;set dum_valve pump_low_power 0&amp;quot;);&lt;br /&gt;
		fhem(&amp;quot;set dum_valve pump_high_power 0&amp;quot;);&lt;br /&gt;
}elsif($pump_low eq &amp;quot;off&amp;quot; &amp;amp;&amp;amp; $pump_high eq &amp;quot;on&amp;quot;){&lt;br /&gt;
		fhem(&amp;quot;set dum_valve pump_low_power 0&amp;quot;);&lt;br /&gt;
		fhem(&amp;quot;set dum_valve pump_high_power $power&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Erläuterungen:&lt;br /&gt;
&lt;br /&gt;
* Es sind alle vier Möglichkeiten der zwei Pumpenlevel abgebildet.&lt;br /&gt;
* Wenn die Automatik deaktiviert ist und die Level 1-4 von extern geschaltet werden, zählen sie dadurch korrekterweise nicht zu diesen beiden Consumern.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Notifys für das hardwaremäßige Schalten der Consumer&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Es werden einige Notifys verwendet, die die Umsetzung vom Dummy-Device auf die Hardware erledigen, da ein logisches Ein teilweise hardwaremäßig einige Dinge erfordert.&lt;br /&gt;
&lt;br /&gt;
Notify für Pump Low:&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
dum_valve:pump_low:.* {&lt;br /&gt;
	if($EVTPART1 eq &amp;quot;on&amp;quot;){&lt;br /&gt;
		fhem(&amp;quot;set Pool_Pin26 on; sleep 60; set Pool_Pin25 on; sleep 1; set Pool_Pin26 off&amp;quot;);&lt;br /&gt;
	}elsif($EVTPART1 eq &amp;quot;off&amp;quot;){&lt;br /&gt;
		fhem(&amp;quot;set Pool_Pin14 off; sleep 5; set Pool_Pin12 off; sleep 15; set Pool_Pin25 off&amp;quot;);&lt;br /&gt;
	}else{&lt;br /&gt;
	Log3 (&amp;quot;Pool&amp;quot;, 4, qq{Notify Level1 - parameter is not on or off});&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Erläuterungen:&lt;br /&gt;
&lt;br /&gt;
* Beim Einschalten ist es nötig für eine Minute Pump High zu aktivieren, um Luft aus der Elektrolysezelle zu spülen, bevor Pump Low aktiv sein darf. (Pin26 und Pin25 sind die Steuerpins auf der Interfacekarte der Pumpe für die beiden Level.)&lt;br /&gt;
* Beim Ausschalten werden zuerst die Salzanlage (Pin14) und die pH-Anlage (Pin12) deaktiviert, damit noch 15 Sekunden lang die Elektrolysezelle mit frischem Wasser gespült wird, um das verbleibende Chlor auszuspülen.&amp;lt;br /&amp;gt;&lt;br /&gt;
Notify für Pump High:&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
dum_valve:pump_high:.* {&lt;br /&gt;
	if($EVTPART1 eq &amp;quot;on&amp;quot;){&lt;br /&gt;
		fhem(&amp;quot;set Pool_Pin26 on; sleep 1; set Pool_Pin25 off&amp;quot;);&lt;br /&gt;
	}elsif($EVTPART1 eq &amp;quot;off&amp;quot;){&lt;br /&gt;
		fhem(&amp;quot;set Pool_Pin25 on; sleep 1; set Pool_Pin26 off&amp;quot;);&lt;br /&gt;
	}else{&lt;br /&gt;
	Log3 (&amp;quot;Pool&amp;quot;, 4, qq{Notify Level 2- parameter is not on or off});&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Erläuterung:&lt;br /&gt;
&lt;br /&gt;
* Da die Interfacekarte immer auf den höchsten Level reagiert, wird beim Umschalten zuerst Pump High aktiviert und nach einer Sekunde Pump Low deaktiviert, um ein &amp;quot;stufenloses&amp;quot; (ohne Ausschalten) Umschalten zu ermöglichen. Beim Ausschalten genauso.&lt;br /&gt;
&lt;br /&gt;
Notify für die Wärmepumpe:&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
dum_valve:heatpump:.* {&lt;br /&gt;
	if($EVTPART1 eq &amp;quot;on&amp;quot;){&lt;br /&gt;
		fhem(&amp;quot;set Pool_Pin6 on&amp;quot;);&lt;br /&gt;
	}elsif($EVTPART1 eq &amp;quot;off&amp;quot;){&lt;br /&gt;
		fhem(&amp;quot;set Pool_Pin6 off&amp;quot;);&lt;br /&gt;
	}else{&lt;br /&gt;
	Log3 (&amp;quot;Pool&amp;quot;, 4, qq{Notify heatpump - parameter is not on or off});&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Erläuterung:&lt;br /&gt;
&lt;br /&gt;
* Hier reicht ein dirketes Übernehmen aus dem Dummy.&lt;br /&gt;
&lt;br /&gt;
Notify für den Nachbar-Pool:&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
dum_valve:pool_kurz:.* {&lt;br /&gt;
	if($EVTPART1 eq &amp;quot;on&amp;quot; &amp;amp;&amp;amp; ReadingsVal(&amp;quot;Shelly_Plug_S_1&amp;quot;,&amp;quot;state&amp;quot;,&amp;quot;on&amp;quot;) eq &amp;quot;off&amp;quot;){&lt;br /&gt;
		fhem(&amp;quot;set PwSw_Schalter off; sleep 3;{if(ReadingsAge(\&amp;quot;PwSw_Schalter\&amp;quot;,\&amp;quot;state\&amp;quot;,0)&amp;lt;4 &amp;amp;&amp;amp; ReadingsVal(\&amp;quot;PwSw_Schalter\&amp;quot;,\&amp;quot;state\&amp;quot;,\&amp;quot;on\&amp;quot;) eq \&amp;quot;off\&amp;quot;){fhem(\&amp;quot;set Shelly_Plug_S_1 on\&amp;quot;)}}; sleep 5; set PwSw_Schalter on&amp;quot;);&lt;br /&gt;
	}elsif($EVTPART1 eq &amp;quot;off&amp;quot; &amp;amp;&amp;amp; ReadingsVal(&amp;quot;Shelly_Plug_S_1&amp;quot;,&amp;quot;state&amp;quot;,&amp;quot;off&amp;quot;) eq &amp;quot;on&amp;quot;){&lt;br /&gt;
		fhem(&amp;quot;set PwSw_Schalter off; sleep 3;{if(ReadingsAge(\&amp;quot;PwSw_Schalter\&amp;quot;,\&amp;quot;state\&amp;quot;,0)&amp;lt;4 &amp;amp;&amp;amp; ReadingsVal(\&amp;quot;PwSw_Schalter\&amp;quot;,\&amp;quot;state\&amp;quot;,\&amp;quot;on\&amp;quot;) eq \&amp;quot;off\&amp;quot;){fhem(\&amp;quot;set Shelly_Plug_S_1 off\&amp;quot;)}}; sleep 5; set PwSw_Schalter on&amp;quot;);&lt;br /&gt;
	}else{&lt;br /&gt;
	Log3 (&amp;quot;Pool&amp;quot;, 4, qq{Notify Pool Kurz - parameter is not on or off});&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Erläuterung&lt;br /&gt;
&lt;br /&gt;
* Da hier eine Pumpe durch einen 3-poligen Umschalter zwischen zwei Hausanspeisungen umgeschaltet wird, ist es wichtig, dass die Pumpe dazwischen &#039;&#039;&#039;gesichert&#039;&#039;&#039; zum Stehen gebracht wird! Sonst führt die rotierende Pumpe als Generator zu einem gewaltigen Kurzschluss mit der &amp;quot;fremden&amp;quot; Phase. War nicht schön ...&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Sonderprogramme für den Poolbetrieb&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Hier ist das Aus- und Einschalten der Solarforcast-Automatik zu sehen. Es werden alle Stati gespeichert. Danach kann von beliebigen &amp;quot;Sonderfunktionen&amp;quot; (Reinigung des Filters, Massagefunktion, usw.) auf die Hardware zugegriffen werden. Wenn wieder in den Automatikmodus zurückgewechselt wird, wird hardwaremäßig wieder der Zustand der Automatik von vorher hergestellt, die Stati wieder zurück geschrieben und die Automatik wieder aktiviert.&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
##############################################&lt;br /&gt;
# Status speichern und Automatik ausschalten #&lt;br /&gt;
##############################################&lt;br /&gt;
sub PoolAutoOff{&lt;br /&gt;
	my $pump_low_id			= sprintf &amp;quot;%02d&amp;quot;, 1;&lt;br /&gt;
	my $pump_high_id		= sprintf &amp;quot;%02d&amp;quot;, 3;&lt;br /&gt;
	my $heatpump_id			= sprintf &amp;quot;%02d&amp;quot;, 5;&lt;br /&gt;
	my $pool_kurz_id		= sprintf &amp;quot;%02d&amp;quot;, 7;&lt;br /&gt;
	my $sf				= &#039;energy_mgmt&#039;;&lt;br /&gt;
&lt;br /&gt;
		&lt;br /&gt;
#	my $pump_low_state = ReadingsVal($valve_dummy,&#039;pump_low&#039;,&#039;on&#039;);&lt;br /&gt;
	my $pump_low_auto	= FHEM::SolarForecast::ConsumerVal ($sf, $pump_low_id, &#039;auto&#039;, &#039;&#039;);	# Automatikmodus der Pumpe&lt;br /&gt;
#	my $pump_high_state = ReadingsVal($valve_dummy,&#039;pump_high&#039;,&#039;on&#039;);&lt;br /&gt;
	my $pump_high_auto	= FHEM::SolarForecast::ConsumerVal ($sf, $pump_high_id, &#039;auto&#039;, &#039;&#039;);	# Automatikmodus der Pumpe&lt;br /&gt;
#	my $heatpump_state = ReadingsVal($valve_dummy,&#039;heatpump&#039;,&#039;on&#039;);&lt;br /&gt;
	my $heatpump_auto	= FHEM::SolarForecast::ConsumerVal ($sf, $heatpump_id, &#039;auto&#039;, &#039;&#039;);	# Automatikmodus der Wärmepumpe&lt;br /&gt;
#	my $pool_kurz_state = ReadingsVal($valve_dummy,&#039;pool_kurz&#039;,&#039;on&#039;);&lt;br /&gt;
	my $pool_kurz_auto	= FHEM::SolarForecast::ConsumerVal ($sf, $pool_kurz_id, &#039;auto&#039;, &#039;&#039;);	# Automatikmodus des Pools&lt;br /&gt;
	fhem(&amp;quot;set $valve_dummy pump_low_auto 0&amp;quot;);&lt;br /&gt;
	fhem(&amp;quot;set $valve_dummy pump_high_auto 0&amp;quot;);&lt;br /&gt;
	fhem(&amp;quot;set $valve_dummy heatpump_auto 0&amp;quot;);&lt;br /&gt;
	fhem(&amp;quot;set $valve_dummy pool_kurz_auto 0&amp;quot;);&lt;br /&gt;
	fhem(&amp;quot;setreading $valve_dummy sf_automatic 0&amp;quot;); &lt;br /&gt;
	fhem(&amp;quot;setreading $valve_dummy last_auto_pump_low $pump_low_auto&amp;quot;);&lt;br /&gt;
	fhem(&amp;quot;setreading $valve_dummy last_auto_pump_high $pump_high_auto&amp;quot;);&lt;br /&gt;
	fhem(&amp;quot;setreading $valve_dummy last_auto_heatpump $heatpump_auto&amp;quot;);&lt;br /&gt;
	fhem(&amp;quot;setreading $valve_dummy last_auto_pool_kurz $pool_kurz_auto&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
	my $last_pump_level_1 = ReadingsVal($valve_dummy,&amp;quot;pump_low&amp;quot;,&amp;quot;off&amp;quot;);&lt;br /&gt;
	my $last_pump_level_2 = ReadingsVal($valve_dummy,&amp;quot;pump_high&amp;quot;,&amp;quot;off&amp;quot;);&lt;br /&gt;
	my $last_pump_level_3 = ReadingsVal($pump_level_3,&amp;quot;value&amp;quot;,&amp;quot;off&amp;quot;);&lt;br /&gt;
	my $last_pump_level_4 = ReadingsVal($pump_level_4,&amp;quot;value&amp;quot;,&amp;quot;off&amp;quot;);&lt;br /&gt;
	my $last_heatpump = ReadingsVal($valve_dummy,&amp;quot;heatpump&amp;quot;,&amp;quot;off&amp;quot;);&lt;br /&gt;
	fhem(&amp;quot;setreading $valve_dummy last_pump_level_1 $last_pump_level_1&amp;quot;); &lt;br /&gt;
	fhem(&amp;quot;setreading $valve_dummy last_pump_level_2 $last_pump_level_2&amp;quot;); &lt;br /&gt;
	fhem(&amp;quot;setreading $valve_dummy last_pump_level_3 $last_pump_level_3&amp;quot;); &lt;br /&gt;
	fhem(&amp;quot;setreading $valve_dummy last_pump_level_4 $last_pump_level_4&amp;quot;); &lt;br /&gt;
	fhem(&amp;quot;setreading $valve_dummy last_heatpump $last_heatpump&amp;quot;); &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
##########################################&lt;br /&gt;
# Status laden und Automatik einschalten #&lt;br /&gt;
##########################################&lt;br /&gt;
sub PoolAutoOn{&lt;br /&gt;
	my $last_pump_level_4 = ReadingsVal($valve_dummy,&amp;quot;last_pump_level_4&amp;quot;,&amp;quot;off&amp;quot;);&lt;br /&gt;
	my $last_pump_level_3 = ReadingsVal($valve_dummy,&amp;quot;last_pump_level_3&amp;quot;,&amp;quot;off&amp;quot;);&lt;br /&gt;
	my $last_pump_level_2 = ReadingsVal($valve_dummy,&amp;quot;last_pump_level_2&amp;quot;,&amp;quot;off&amp;quot;);&lt;br /&gt;
	my $last_pump_level_1 = ReadingsVal($valve_dummy,&amp;quot;last_pump_level_1&amp;quot;,&amp;quot;off&amp;quot;);&lt;br /&gt;
	my $last_heatpump = ReadingsVal($valve_dummy,&amp;quot;last_heatpump&amp;quot;,&amp;quot;off&amp;quot;);&lt;br /&gt;
	fhem(&amp;quot;set $pump_level_4 $last_pump_level_4&amp;quot;); &lt;br /&gt;
	fhem(&amp;quot;set $pump_level_3 $last_pump_level_3&amp;quot;);&lt;br /&gt;
	fhem(&amp;quot;set $pump_level_2 $last_pump_level_2&amp;quot;);&lt;br /&gt;
	if($last_pump_level_1 eq &amp;quot;on&amp;quot; &amp;amp;&amp;amp; $last_pump_level_2 eq &amp;quot;off&amp;quot; &amp;amp;&amp;amp; $last_pump_level_3 eq &amp;quot;off&amp;quot; &amp;amp;&amp;amp; $last_pump_level_4 eq &amp;quot;off&amp;quot;){&lt;br /&gt;
		fhem(&amp;quot;set $pump_level_2 on; sleep 1; set $pump_level_1 $last_pump_level_1; sleep 28; set $pump_level_2 off; set $valve_dummy sf_automatic 1&amp;quot;);&lt;br /&gt;
	}elsif($last_pump_level_1 eq &amp;quot;on&amp;quot; &amp;amp;&amp;amp; $last_pump_level_2 eq &amp;quot;on&amp;quot; &amp;amp;&amp;amp; $last_pump_level_3 eq &amp;quot;off&amp;quot; &amp;amp;&amp;amp; $last_pump_level_4 eq &amp;quot;off&amp;quot;){&lt;br /&gt;
		fhem(&amp;quot;set $pump_level_1 off; set $valve_dummy sf_automatic 1&amp;quot;); &lt;br /&gt;
	}elsif($last_pump_level_1 eq &amp;quot;off&amp;quot; &amp;amp;&amp;amp; $last_pump_level_2 eq &amp;quot;off&amp;quot; &amp;amp;&amp;amp; $last_pump_level_3 eq &amp;quot;off&amp;quot; &amp;amp;&amp;amp; $last_pump_level_4 eq &amp;quot;off&amp;quot;){&lt;br /&gt;
		fhem(&amp;quot;set $pump_level_1 off; set $valve_dummy sf_automatic 1&amp;quot;); &lt;br /&gt;
	}&lt;br /&gt;
	fhem(&amp;quot;sleep 10; set $heatpump $last_heatpump&amp;quot;); &lt;br /&gt;
	fhem(&amp;quot;setreading $valve_dummy last_pump_level_1 none&amp;quot;); &lt;br /&gt;
	fhem(&amp;quot;setreading $valve_dummy last_pump_level_2 none&amp;quot;); &lt;br /&gt;
	fhem(&amp;quot;setreading $valve_dummy last_pump_level_3 none&amp;quot;); &lt;br /&gt;
	fhem(&amp;quot;setreading $valve_dummy last_pump_level_4 none&amp;quot;); &lt;br /&gt;
	fhem(&amp;quot;setreading $valve_dummy last_heatpump none&amp;quot;); &lt;br /&gt;
	fhem(&amp;quot;setreading $valve_dummy valve_position normal&amp;quot;);&lt;br /&gt;
	my $last_auto_pump_low = ReadingsVal($valve_dummy,&amp;quot;last_auto_pump_low&amp;quot;,0);&lt;br /&gt;
	my $last_auto_pump_high = ReadingsVal($valve_dummy,&amp;quot;last_auto_pump_high&amp;quot;,0);&lt;br /&gt;
	my $last_auto_heatpump = ReadingsVal($valve_dummy,&amp;quot;last_auto_heatpump&amp;quot;,0);&lt;br /&gt;
	my $last_auto_pool_kurz = ReadingsVal($valve_dummy,&amp;quot;last_auto_pool_kurz&amp;quot;,0);&lt;br /&gt;
	fhem(&amp;quot;set $valve_dummy pump_low_auto $last_auto_pump_low&amp;quot;);&lt;br /&gt;
	fhem(&amp;quot;set $valve_dummy pump_high_auto $last_auto_pump_high&amp;quot;);&lt;br /&gt;
	fhem(&amp;quot;set $valve_dummy heatpump_auto $last_auto_heatpump&amp;quot;);&lt;br /&gt;
	fhem(&amp;quot;set $valve_dummy pool_kurz_auto $last_auto_pool_kurz&amp;quot;);&lt;br /&gt;
	fhem(&amp;quot;setreading $valve_dummy last_auto_pump_low none&amp;quot;);&lt;br /&gt;
	fhem(&amp;quot;setreading $valve_dummy last_auto_pump_high none&amp;quot;);&lt;br /&gt;
	fhem(&amp;quot;setreading $valve_dummy last_auto_heatpump none&amp;quot;);&lt;br /&gt;
	fhem(&amp;quot;setreading $valve_dummy last_auto_pool_kurz none&amp;quot;);&lt;br /&gt;
}		&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Erläuterungen:&lt;br /&gt;
&lt;br /&gt;
* pump_level_1 ist Pump Low, pump_level_2 ist Pump High, pump_level_3 und pump_level_4 sind Sonderfunktionen (Filterreinigung, Massage)&lt;br /&gt;
* Beim Wiedereinschalten von Pump Low ist es wieder nötig kurz Pump High zu aktivieren, um Luft auszuspülen.&lt;br /&gt;
* Es wird der Zustand von Solarforecast gespeichert und danach auf die Hardware geschrieben. -&amp;gt; Stellt sicher, dass nach dem Einschalten der Automatik der logical switchstate zum physical switchstate passt.&lt;br /&gt;
&lt;br /&gt;
== Tips &amp;amp; Tricks ==&lt;br /&gt;
* Interruptable verhält sich völlig anders, wenn der Perlcode 1 oder 0 liefert, als wenn man 1 oder 0 in der Config hinterlegt. -&amp;gt; siehe commandref&lt;br /&gt;
* Rückgabewert bei swoncond und swoffcond wird mit dem Readingswert verglichen und nicht dirket verwendet. Bsp. wenn das Reading 100 enthält muss auch der Rückgabewert des Perlcodes 100 liefern, um true zu ergeben.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== weiterführende Links ==&lt;br /&gt;
* Forenthema &amp;quot;{{Link2Forum|Topic=137058|LinkText=76_SolarForecast - Informationen/Ideen zu Weiterentwicklung und Support}}&amp;quot;&lt;br /&gt;
* Solcast API Toolkit: https://toolkit.solcast.com.au&lt;br /&gt;
* Kostal Plenticore 10 Plus mit SQL-Datenbank Integration ([[Kostal Plenticore 10 Plus]])&lt;br /&gt;
* Photovoltaik Ingenieurbüro Junge: https://www.ing-büro-junge.de/html/photovoltaik.html&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Energieerzeugungsmessung]]&lt;/div&gt;</summary>
		<author><name>Ph1959de</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=BOSE_SoundTouch_de-clouding&amp;diff=40788</id>
		<title>BOSE SoundTouch de-clouding</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=BOSE_SoundTouch_de-clouding&amp;diff=40788"/>
		<updated>2026-02-12T17:28:47Z</updated>

		<summary type="html">&lt;p&gt;Ph1959de: Typo entfernt&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Zielgruppe ==&lt;br /&gt;
Benutzer von BOSE SoundTouch-Systemen&lt;br /&gt;
&lt;br /&gt;
== Lösungsübersicht ==&lt;br /&gt;
Cloudfreier Betrieb der BOSE-Boxen, De-bricking nach fehlerhaften Installationen&lt;br /&gt;
&lt;br /&gt;
== Ausgangssituation ==&lt;br /&gt;
BOSE wird spätestens im Mai 2026 seine Server aus dem Netz nehmen. Damit wird die Funktionalität der BOSE-Boxen drastisch eingeschränkt, das Abspielen von netzbasierten Radiostreams ist ohne Modifikation der Boxen nicht möglich.&lt;br /&gt;
&lt;br /&gt;
== De-clouding ==&lt;br /&gt;
Um einen root-Zugang zum Linux-Betriebssystem der Boxen zu erhalten, wird benötigt:&lt;br /&gt;
* USB-Stick, formatiert FAT32, mit einer leeren Datei &#039;remote_services&#039;. Achtung: Das bootable-Flag des Dateisystems muss gesetzt sein.&lt;br /&gt;
* Für BOSE ST10 und ST300 zusätzlich ein USB-OTG-Adapter. Das ist ein Adapter, der eine Buchse vom Typ USB A (in den wird der o.a. Stick gesteckt) mit einem Stecker vom Type USB Micro (Typ Micro B, trapezförmig) verbindet. Darin ist offenbar das 5. (ID-)Pin des Micro USB-Steckers mit GND verbunden. Diese Adapter gibt es für ca. 8 € im Handel, sie werden gerne benutzt, um Smartphones in den USB-Host-Modus zu bringen. &lt;br /&gt;
===Vorbereitung allgemein===&lt;br /&gt;
In der Regel werden für den cloudfreien Betrieb der Boxen sowohl die Device-ID, als auch die ID des BOSE-Accounts benötigt. Dazu greift man mit einem Browser auf das API der Box zu, und ruft die Adresse&lt;br /&gt;
 http://&amp;lt;IP-Adresse der Box&amp;gt;:8090/info &lt;br /&gt;
auf. Die Rückgabe sieht etwa so aus:&lt;br /&gt;
 &amp;lt;info deviceID=&amp;quot;{Device-ID}&amp;quot;&amp;gt;&lt;br /&gt;
 &amp;lt;name&amp;gt;{Name des Geräts in der BOSE Apps}&amp;lt;/name&amp;gt;&lt;br /&gt;
 &amp;lt;type&amp;gt;SoundTouch 300&amp;lt;/type&amp;gt;&lt;br /&gt;
 &amp;lt;margeAccountUUID&amp;gt;{ID des BOSE-Accounts}&amp;lt;/margeAccountUUID&amp;gt;&lt;br /&gt;
 ...&lt;br /&gt;
Diese beiden Codes bitte notieren. Außerdem die drei Dateien&lt;br /&gt;
 http://&amp;lt;IP-Adresse der Box&amp;gt;:8090/info &lt;br /&gt;
 http://&amp;lt;IP-Adresse der Box&amp;gt;:8090/recents &lt;br /&gt;
 http://&amp;lt;IP-Adresse der Box&amp;gt;:8090/preset&lt;br /&gt;
lokal abspeichern mit den Namen DeviceInfo.xml, Recents.xml, Presets.xml (Achtung, XML-Header nicht vergessen).&lt;br /&gt;
&lt;br /&gt;
===Vorbereitung ST10===&lt;br /&gt;
Achtung: Vorher unbedingt die Einbindung der ST10 in ein Stereopaar aufheben! Sonst ist das Gerät nach der Änderung der internen Dateien nur noch ein teurer Ziegelstein (&#039;Brick&#039;) und man muss erst die gesamte Firmware neu installieren, siehe unten.&lt;br /&gt;
*USB-Stick durch den OTG-Adapter mit dem USB-Port der ST10 verbinden.&lt;br /&gt;
*Stromversorgungskabel abziehen.&lt;br /&gt;
*Stromversorgungskabel wieder einstecken&lt;br /&gt;
*Die ST10 bootet jetzt vom USB-Stick, ggf. zeigt dessen Signal-LED den Zugriff an.&lt;br /&gt;
&lt;br /&gt;
===Vorbereitung ST20===&lt;br /&gt;
*USB-Stick mit dem USB-Port der ST20 verbinden.&lt;br /&gt;
*Stromversorgungskabel abziehen.&lt;br /&gt;
*Stromversorgungskabel wieder einstecken&lt;br /&gt;
*Die ST20 bootet jetzt vom USB-Stick, ggf. zeigt dessen Signal-LED den Zugriff an.&lt;br /&gt;
&lt;br /&gt;
===Vorbereitung ST300===&lt;br /&gt;
*USB-Stick durch den OTG-Adapter mit dem USB-Port der ST300 verbinden.&lt;br /&gt;
*Stromversorgungskabel abziehen.&lt;br /&gt;
*Infrarot-Fernbedienung der ST300 auf das Gerät richten, den &#039;&#039;SoundTouch&#039;&#039;-Button drücken und &#039;&#039;&#039;halten&#039;&#039;&#039; (2. Button in der 2. Reihe)&lt;br /&gt;
*Währenddessen das Stromversorgungskabel wieder einstecken&lt;br /&gt;
*Die ST300 bootet jetzt vom USB-Stick, ggf. zeigt dessen Signal-LED den Zugriff an.&lt;br /&gt;
*LEDs der ST300 sollten jetzt gelb blinken, die Taste kann losgelassen werden.&lt;br /&gt;
&lt;br /&gt;
===Zugang===&lt;br /&gt;
Der Zugang ist jetzt möglich mit &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Bash&amp;quot;&amp;gt;&lt;br /&gt;
 telnet &amp;lt;IP-Adresse der Box&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Auf die Rückgabe &#039;rhino login&#039; einfach &#039;root&#039; eingeben, ein Passwort ist nicht erforderlich.&lt;br /&gt;
Alternativ kann man auch verwenden&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Bash&amp;quot;&amp;gt;&lt;br /&gt;
 ssh -o HostKeyAlgorithms=+ssh-rsa -o PubkeyAcceptedAlgorithms=+ssh-rsa  root@&amp;lt;IP-Adresse der Box&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Möchte man diesen Zugang permanent haben, um ggf. weitere Änderungen einfacher auszuführen, muss man eine leere Datei &#039;remote_services&#039; erzeugen mit dem Kommando&lt;br /&gt;
 touch /mnt/nv/remote_services&lt;br /&gt;
Während das für die Umstellung auf cloudfreien Betrieb ganz komfortabel ist, sollte man diese Datei nach erfolgreich getester Umstellung wieder löschen, denn sonst ist die Box komplett ungesichert!&lt;br /&gt;
&lt;br /&gt;
===Holen weiterer Daten===&lt;br /&gt;
Nach einem Wechsel in das korrekte Verzeichnis&lt;br /&gt;
 cd /mnt/nv/BoseApp-Persistence/1/&lt;br /&gt;
findet man dort auch die schon oben erwähnten Dateien Recents.xml und Presets.xml, sie können auch von hier auf den USB-Stick herunterkopiert werden.&lt;br /&gt;
&lt;br /&gt;
Ebenfalls in diesem Verzeichnis liegt die Datei Sources.xml, dies muss ebenfalls entweder kopiert werden. Entweder lokal auf den USB-Stick oder, z.B. mit dem Befehl&lt;br /&gt;
 scp Sources.xml {username}@{irgendein computer}&lt;br /&gt;
via Netz übertragen werden.&lt;br /&gt;
&lt;br /&gt;
===Umbiegen===&lt;br /&gt;
Die Konfiguration der BOSE-Server kann jetzt geändert werden, dazu muss man das richtige Verzeichnis auf der Box kennen, das Dateisystem beschreibbar machen und die entsprechende Datei ändern&lt;br /&gt;
 cd /opt/Bose/etc/&lt;br /&gt;
 rw&lt;br /&gt;
 cp SoundTouchSdkPrivateCfg.xml STSPC.backup&lt;br /&gt;
 vi SoundTouchSdkPrivateCfg.xml&lt;br /&gt;
Für Nicht-Linux-Nutzer ist der vi Editor etwas gewöhnungsbedürftig, hier findet man eine [https://sits.de/vi.html Mini-Anleitung].&lt;br /&gt;
&lt;br /&gt;
Die Datei enthält im BOSE-Urzustand folgende Informationen&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;XML&amp;quot;&amp;gt;&lt;br /&gt;
 &amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;utf-8&amp;quot;?&amp;gt;&lt;br /&gt;
 &amp;lt;SoundTouchSdkPrivateCfg&amp;gt;&lt;br /&gt;
 &amp;lt;margeServerUrl&amp;gt;https://streaming.bose.com&amp;lt;/margeServerUrl&amp;gt;&lt;br /&gt;
 &amp;lt;statsServerUrl&amp;gt;https://events.api.bosecm.com&amp;lt;/statsServerUrl&amp;gt;&lt;br /&gt;
 &amp;lt;swUpdateUrl&amp;gt;https://worldwide.bose.com/updates/soundtouch&amp;lt;/swUpdateUrl&amp;gt;&lt;br /&gt;
 &amp;lt;usePandoraProductionServer&amp;gt;true&amp;lt;/usePandoraProductionServer&amp;gt;&lt;br /&gt;
 &amp;lt;isZeroconfEnabled&amp;gt;true&amp;lt;/isZeroconfEnabled&amp;gt;&lt;br /&gt;
 &amp;lt;saveMargeCustomerReport&amp;gt;false&amp;lt;/saveMargeCustomerReport&amp;gt;&lt;br /&gt;
 &amp;lt;bmxRegistryUrl&amp;gt;https://content.api.bose.io/bmx/registry/v1/services&amp;lt;/bmxRegistryUrl&amp;gt;&lt;br /&gt;
 &amp;lt;/SoundTouchSdkPrivateCfg&amp;gt;&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Um die Box auf einen anderen Server umzubiegen, der unter dem Namen oder der IP-adresse &amp;lt;code&amp;gt;&#039;{NeuerServer}:{Port}&#039;&amp;lt;/code&amp;gt; erreichbar ist, bitte den Inhalt wie folgt ändern:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;XML&amp;quot;&amp;gt;&lt;br /&gt;
 &amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;utf-8&amp;quot;?&amp;gt;&lt;br /&gt;
 &amp;lt;SoundTouchSdkPrivateCfg&amp;gt;&lt;br /&gt;
  &amp;lt;margeServerUrl&amp;gt;http://{NeuerServer}:{Port}/marge&amp;lt;/margeServerUrl&amp;gt;&lt;br /&gt;
  &amp;lt;statsServerUrl&amp;gt;http://{NeuerServer}:{Port}&amp;lt;/statsServerUrl&amp;gt;&lt;br /&gt;
  &amp;lt;swUpdateUrl&amp;gt;http://{NeuerServer}:{Port}/updates/soundtouch&amp;lt;/swUpdateUrl&amp;gt;&lt;br /&gt;
  &amp;lt;usePandoraProductionServer&amp;gt;true&amp;lt;/usePandoraProductionServer&amp;gt;&lt;br /&gt;
  &amp;lt;isZeroconfEnabled&amp;gt;true&amp;lt;/isZeroconfEnabled&amp;gt;&lt;br /&gt;
  &amp;lt;saveMargeCustomerReport&amp;gt;false&amp;lt;/saveMargeCustomerReport&amp;gt;&lt;br /&gt;
  &amp;lt;bmxRegistryUrl&amp;gt;http://{NeuerServer}:{Port}/bmx/registry/v1/services&amp;lt;/bmxRegistryUrl&amp;gt;&lt;br /&gt;
 &amp;lt;/SoundTouchSdkPrivateCfg&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== De-bricking ==&lt;br /&gt;
Für den Fall, dass die BOSE Box nach irgendwelchen Änderungen nicht mehr ins lokale WLAN findet, aber auch keinen eigenen Access Point anbietet, kann man das Gerät dennoch retten. Dazu muss die Firmware komplett neu installiert werden. Zunächst die passende Firmware suchen&lt;br /&gt;
&lt;br /&gt;
[https://archive.org/download/bose-soundtouch-software-and-firmware/ Archiv mit BOSE Firmware]&lt;br /&gt;
&lt;br /&gt;
Die Zip-Datei muss entpackt werden die darin enthaltene Datei mit der Endung &#039;&#039;.stu&#039;&#039; muss auf einen bootfähigen USB-Stick geschrieben werden, siehe oben.&lt;br /&gt;
&lt;br /&gt;
TODO: Unterabschnitte für die anderen Systeme schreiben, da nicht bekannt ist, wie lange [https://downloads.bose.com/ced/soundtouch/soundtouch_usb/index.html?l=de BOSE die Informationen noch zur Verfügung stellt].&lt;br /&gt;
&lt;br /&gt;
===ST10===&lt;br /&gt;
*USB-Stick durch den OTG-Adapter mit dem USB-Port der ST10 verbinden.&lt;br /&gt;
*Stromversorgungskabel abziehen.&lt;br /&gt;
*Auf dem Tastenfeld die Taste „4“ und die „Lautstärke leiser“-Taste drücken und &#039;&#039;&#039;halten&#039;&#039;&#039;&lt;br /&gt;
*Stromversorgungskabel wieder einstecken&lt;br /&gt;
*Die ST10 bootet jetzt vom USB-Stick, ggf. zeigt dessen Signal-LED den Zugriff an. Die Buttons können nun losgelassen werden.&lt;br /&gt;
Die Installation dauert ca. 5 Minuten.&lt;br /&gt;
&lt;br /&gt;
== ST10 zum Stereopaar verbinden ==&lt;br /&gt;
Zwei ST10-Boxen können auch ohne BOSE App und BOSE Server zu einem Stereopaar verbunden werden.&lt;br /&gt;
*Beide ST10 für den telnet-Zugang vorbereiten, so wie oben beschrieben&lt;br /&gt;
*Auswählen, welche Box künftig den LINKEN Stereokanal wiedergeben soll, einloggen als root&lt;br /&gt;
**Auf dieser Box in das korrekte Verzeichnis wechseln&lt;br /&gt;
 cd /mnt/nv/BoseApp-Persistence/1/&lt;br /&gt;
** Hier liegt eine Datei &#039;&#039;GroupService.xml&#039;&#039;. Im normalen (ungepaarten) Zustand hat diese den Inhalt&lt;br /&gt;
 &amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot; ?&amp;gt;&lt;br /&gt;
 &amp;lt;group /&amp;gt;&lt;br /&gt;
**Den Inhalt dieser Datei ersetzen durch&lt;br /&gt;
 &amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot; ?&amp;gt;&lt;br /&gt;
 &amp;lt;group id=&amp;quot;1234567&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;name&amp;gt;{NAME DES PAARS, z.B. &amp;quot;ST10-1 und ST10-2&amp;quot;}&amp;lt;/name&amp;gt;&lt;br /&gt;
    &amp;lt;masterDeviceId&amp;gt;{BOSE-ID des Master-Devices, z.B. 50338B343905}&amp;lt;/masterDeviceId&amp;gt;&lt;br /&gt;
    &amp;lt;roles&amp;gt;&lt;br /&gt;
        &amp;lt;groupRole&amp;gt;&lt;br /&gt;
            &amp;lt;deviceId&amp;gt;{BOSE-ID des Master-Devices, z.B. 50338B343905}&amp;lt;/deviceId&amp;gt;&lt;br /&gt;
            &amp;lt;role&amp;gt;LEFT&amp;lt;/role&amp;gt;&lt;br /&gt;
            &amp;lt;ipAddress&amp;gt;{IP-Adresse des Master-Devices}&amp;lt;/ipAddress&amp;gt;&lt;br /&gt;
        &amp;lt;/groupRole&amp;gt;&lt;br /&gt;
        &amp;lt;groupRole&amp;gt;&lt;br /&gt;
            &amp;lt;deviceId&amp;gt;{BOSE-ID des Slave-Devices, z.B. 458790343905}&amp;lt;/deviceId&amp;gt;&lt;br /&gt;
            &amp;lt;role&amp;gt;RIGHT&amp;lt;/role&amp;gt;&lt;br /&gt;
            &amp;lt;ipAddress&amp;gt;{IP-Adresse des Slave-Devices}&amp;lt;/ipAddress&amp;gt;&lt;br /&gt;
        &amp;lt;/groupRole&amp;gt;&lt;br /&gt;
    &amp;lt;/roles&amp;gt;&lt;br /&gt;
    &amp;lt;senderIPAddress&amp;gt;{IP-Adresse des Master-Devices}&amp;lt;/senderIPAddress&amp;gt;&lt;br /&gt;
 &amp;lt;/group&amp;gt;&lt;br /&gt;
** Datei sichern und die Box neu booten&lt;br /&gt;
*Auswählen, welche Box künftig den RECHTEN Stereokanal wiedergeben soll, einloggen als root&lt;br /&gt;
**Auf dieser Box in das korrekte Verzeichnis wechseln&lt;br /&gt;
 cd /mnt/nv/BoseApp-Persistence/1/&lt;br /&gt;
** Hier liegt eine Datei &#039;&#039;GroupService.xml&#039;&#039;. Im normalen (ungepaarten) Zustand hat diese den Inhalt&lt;br /&gt;
 &amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot; ?&amp;gt;&lt;br /&gt;
 &amp;lt;group /&amp;gt;&lt;br /&gt;
**Den Inhalt dieser Datei ersetzen durch&lt;br /&gt;
 &amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot; ?&amp;gt;&lt;br /&gt;
 &amp;lt;group id=&amp;quot;1234567&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;name&amp;gt;{NAME DES PAARS, z.B. &amp;quot;ST10-1 und ST10-2&amp;quot;}&amp;lt;/name&amp;gt;&lt;br /&gt;
    &amp;lt;masterDeviceId&amp;gt;{BOSE-ID des Master-Devices, z.B. 50338B343905}&amp;lt;/masterDeviceId&amp;gt;&lt;br /&gt;
    &amp;lt;roles&amp;gt;&lt;br /&gt;
        &amp;lt;groupRole&amp;gt;&lt;br /&gt;
            &amp;lt;deviceId&amp;gt;{BOSE-ID des Master-Devices, z.B. 50338B343905}&amp;lt;/deviceId&amp;gt;&lt;br /&gt;
            &amp;lt;role&amp;gt;LEFT&amp;lt;/role&amp;gt;&lt;br /&gt;
            &amp;lt;ipAddress&amp;gt;{IP-Adresse des Master-Devices}&amp;lt;/ipAddress&amp;gt;&lt;br /&gt;
        &amp;lt;/groupRole&amp;gt;&lt;br /&gt;
        &amp;lt;groupRole&amp;gt;&lt;br /&gt;
            &amp;lt;deviceId&amp;gt;{BOSE-ID des Slave-Devices, z.B. 458790343905}&amp;lt;/deviceId&amp;gt;&lt;br /&gt;
            &amp;lt;role&amp;gt;RIGHT&amp;lt;/role&amp;gt;&lt;br /&gt;
            &amp;lt;ipAddress&amp;gt;{IP-Adresse des Slave-Devices}&amp;lt;/ipAddress&amp;gt;&lt;br /&gt;
        &amp;lt;/groupRole&amp;gt;&lt;br /&gt;
    &amp;lt;/roles&amp;gt;&lt;br /&gt;
    &amp;lt;senderIPAddress&amp;gt;{IP-Adresse des Master-Devices}&amp;lt;/senderIPAddress&amp;gt;&lt;br /&gt;
    &amp;lt;status&amp;gt;GROUP_OK&amp;lt;/status&amp;gt;&lt;br /&gt;
 &amp;lt;/group&amp;gt;&lt;br /&gt;
** Achtung: Der einzige Unterschied zur Datei auf der LINKEN Box ist die vorletzte Zeile mit dem &#039;&#039;status&#039;&#039;&lt;br /&gt;
**Datei sichern und die Box neu booten&lt;br /&gt;
Abspielbefehle werden dann nur an die Master-Box gesendet, diese synchronisiert sich automatisch mit der anderen Box&lt;br /&gt;
[[Kategorie:HOWTOS]]&lt;br /&gt;
[[Kategorie:BOSE]]&lt;/div&gt;</summary>
		<author><name>Ph1959de</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=Vitoconnect&amp;diff=40787</id>
		<title>Vitoconnect</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=Vitoconnect&amp;diff=40787"/>
		<updated>2026-02-12T17:27:44Z</updated>

		<summary type="html">&lt;p&gt;Ph1959de: URL für API-Key etc. angepasst; Formatierung überarbeitet&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{SEITENTITEL:vitoconnect}}{{Infobox Modul&lt;br /&gt;
|ModPurpose=Steuerung von Viessmann Heizungen&lt;br /&gt;
|ModCategory=Heizungssteuerung/Raumklima&lt;br /&gt;
|ModCmdRef=vitoconnect&lt;br /&gt;
|ModFTopic=140073&lt;br /&gt;
|ModType=d&lt;br /&gt;
|ModForumArea=Heizungssteuerung/Raumklima&lt;br /&gt;
|ModTechName=98_vitoconnect.pm&lt;br /&gt;
|ModOwner=stefanru ({{Link2FU|24242|Forum}}/[[Benutzer Diskussion:stefanru|Wiki]])}}&lt;br /&gt;
[https://www.viessmann.de/de/viessmann-apps/vitoconnect.html Vitoconnect] ist die Schnittstelle zwischen Viessmann Gerät (Heizkessel, Wärmepumpe,...) und der App ViCare zum Bedienen von Viessmann Heizungsanlagen. &lt;br /&gt;
&lt;br /&gt;
Mit dem FHEM Modul [[vitoconnect]] können die Daten, die über die Internetschnittstelle an den Herstellerserver gemeldet und von diesem über eine API zur Verfügung gestellt werden, in FHEM in Form von Readings angezeigt werden. Auch eine Steuerung der Heizung ist mit diesem Modul möglich.&lt;br /&gt;
&lt;br /&gt;
Wen stört, dass die Daten in der Herstellercloud liegen, sei das Modul [[Vitotronic 200 (Viessmann Heizungssteuerung)]] empfohlen.&lt;br /&gt;
&lt;br /&gt;
Die Idee zu dem Modul enstand aus dem Thread {{Link2Forum|Topic=86073|LinkText=&amp;quot;Viessmann VitoConnect API&amp;quot;}}. Es basiert auf dem PHP Modul von [https://github.com/thetrueavatar/Viessmann-Api thetrueavatar].&lt;br /&gt;
&lt;br /&gt;
== Einrichtung ==&lt;br /&gt;
=== Vorbereitung ===&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;{{Randnotiz|RNTyp=y|RNText=Disclaimer zur Ursprungsversion des Moduls&lt;br /&gt;
&lt;br /&gt;
Das Modul wurde nach bestem Wissen und Gewissen programmiert. Da Änderungen an der Heizung nur über die Viessmann API erfolgen, sollte die Nutzung risikoarm sein. Allerdings kann ich keine Garantie übernehmen, dass es nicht doch zu irgendwelchen unerwünschten Nebeneffekten kommt. Nutzung auf eigene Gefahr!&lt;br /&gt;
&lt;br /&gt;
Diskussionen über Fehler, Verbesserungen usw. finden im Forum im Thread {{Link2Forum|Topic=93664|LinkText=&amp;quot;Neues Modul: vitoconnect&amp;quot;}} statt.}}&lt;br /&gt;
Zur Nutzung des Moduls muss vorhanden sein:&lt;br /&gt;
* Eine aktuelle FHEM Installation &lt;br /&gt;
* Internet-Schnittstelle Vitoconnect (Typ OPTO1 oder OPTO2) oder E3 One Base&lt;br /&gt;
* Ein Viessmann ViCare Account, wie er im Rahmen der Nutzung der ViCare App erstellt wird.&lt;br /&gt;
* Seit 15. Juli 2021 auch einen persönlichen API Key (=Client-ID), diesen gibts hier: &amp;lt;nowiki&amp;gt;https://developer.viessmann-climatesolutions.com/&amp;lt;/nowiki&amp;gt; &lt;br /&gt;
* Das Modul benötigt mindestens folgende Perl Libraries: Path::Tiny, JSON und DateTime. Diese können mit sudo apt install libtypes-path-tiny-perl libjson-perl libdatetime-perl oder via cpan. Sollten Libraries fehlen erhält man folgende Fehlermeldung &amp;quot;cannot load module vitoconnect&amp;quot;. Dann bitte ins Logfile schauen, welche Lib fehlt.&lt;br /&gt;
&lt;br /&gt;
=== Define des Devices ===&lt;br /&gt;
{{Randnotiz|RNTyp=|RNText=Aktuelle Entwicklung des Moduls&lt;br /&gt;
&lt;br /&gt;
Das &amp;quot;offizielle&amp;quot; Modul im SVN war &amp;quot;verwaist&amp;quot;, der Maintainer (mcp) nicht mehr aktiv:&lt;br /&gt;
* Ursprüngliches Forenthema ({{Link2Forum|Topic=86073|LinkText=86073}}): geschlossen; enthält &amp;quot;inoffizielle&amp;quot; Modulupdates von verschiedenen Autoren in verschiedenen Beiträgen&lt;br /&gt;
* Neues Forenthema ({{Link2Forum|Topic=140073|LinkText=Vitoconnect - Verbesserte Version}}), Benutzer {{Link2FU|24242|stefanru}}; dieses Forenthema wird der neue &amp;quot;Entwicklungsthread&amp;quot; zum Modul. &lt;br /&gt;
Die neue Version des Moduls ist seit dem 29.01.2025 im SVN verfügbar.&lt;br /&gt;
Das neue Modul ist abwärtskompatibel, es wird aber empfohlen auf Raw Readings umzustellen (vitoconnect_raw_readings = 1), siehe Wiki.&lt;br /&gt;
}}&lt;br /&gt;
Dann kann das Gerät angelegt werden: &lt;br /&gt;
:&amp;lt;code&amp;gt;define &amp;lt;name&amp;gt; vitoconnect &amp;lt;user&amp;gt; &amp;lt;password&amp;gt; &amp;lt;interval&amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
:z.B.: &amp;lt;code&amp;gt;define vitoconnect vitoconnect user@mail.xx geheim 60&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
User und Passwort sind das gleiche wie in der ViCare App.&lt;br /&gt;
&lt;br /&gt;
Da die Parameter, die beim Definieren des Devices angelegt werden im Klartext in der Detailansicht lesbar sind, kann man beim Definieren einfach ein falsches Passwort eingeben und das richtige später mit einem &amp;quot;set&amp;quot;-Kommando setzen. Also zum Beispiel:&lt;br /&gt;
:&amp;lt;code&amp;gt;define vitoconnect vitoconnect user@mail.xx fakePassword 60&amp;lt;/code&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
:&amp;lt;code&amp;gt;set vitoconnect password correctPassword 60&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Zusätzlich muss der apiKey gesetzt werden.&lt;br /&gt;
&lt;br /&gt;
Dazu muss eine Client-ID unter &amp;lt;nowiki&amp;gt;https://developer.viessmann-climatesolutions.com/&amp;lt;/nowiki&amp;gt; erstellt werden. &lt;br /&gt;
Nach dem Erstellen eines Kontos muss ein neuer Client (ClientID sollte (muss?) dem angegebenen &amp;quot;user@mail.xx&amp;quot; entsprechen) mit folgenden Einstellungen angelegt werden:&lt;br /&gt;
* Google reCAPTCHA deaktiviert &lt;br /&gt;
* Redirect URI = &amp;lt;nowiki&amp;gt;http://localhost:4200/&amp;lt;/nowiki&amp;gt; &lt;br /&gt;
* Nun die Client-ID kopieren und am FHEM Device setzen:&lt;br /&gt;
* &amp;lt;code&amp;gt;set vitoconnect apiKey &amp;lt;Client-ID&amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Sollte die Anlage mehrere Viessmann Geräte oder mehrere Gateways mit mehreren Geräten oder mehrere Standorte mit mehreren Gateways und mehreren Geräten besitzen, wird es eine Meldung im Status des FHEM Devices geben. Bitte mit&lt;br /&gt;
:&amp;lt;code&amp;gt;set vitoconnect selectDevice &amp;lt;Devices&amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
das Device auswählen, das FHEM abfragen soll.&lt;br /&gt;
&lt;br /&gt;
Sollen mehrere Devices abgefragt werden, bitte mehrere FHEM vitoconnect Devices anlegen. Hier bitte das API Limit von oben beachten.&lt;br /&gt;
&lt;br /&gt;
Zur nachträgliches Änderung des Intervalls kann das DEF aufgerufen und dort die Parameter korrigiert werden (z.B. von 60 Sek. auf 180 Sek.) - dies gilt natürlich ebenso für nachträgliches Ändern des Usernamens oder Passwortes.&lt;br /&gt;
&lt;br /&gt;
=== Betrieb ===&lt;br /&gt;
Der Zugriff auf die API von Viessmann ist aktuell auf 1450 pro Tag in der Gratisversion beschränkt, daraus ergibt sich ein Maximum von ca. einer Abfrage pro Minute. Die Anzahl der Zugriffe ist im Entwicklerportal einsehbar: https://developer.viessmann.com/de/clients/history (funktioniert Stand 02/2026 nicht mehr, eine &amp;quot;neue&amp;quot; Seite um die Anzahl der Zugriffe abzufragen ist (mir) nicht bekannt).&lt;br /&gt;
&lt;br /&gt;
== Readings ==&lt;br /&gt;
=== Mapping ===&lt;br /&gt;
Im neuen Modul kann man per Attribut auswählen, wie die Readings dargestellt werden sollen. Es gibt drei Möglichkeiten:&lt;br /&gt;
# Raw Readings: Die Readings werden im API-Format abgelegt.&lt;br /&gt;
# Altes SVN Mapping: Die Readings werden wie im alten Modul auf Namen gemappt.&lt;br /&gt;
# &amp;quot;Roger Mapping&amp;quot;: Die Readings werden wie in den Zwischenversionen von {{Link2FU|185|Roger}} in {{Link2Forum|Topic=93664|Message=1292441|LinkText=diesem Forenbeitrag}} gemappt.&lt;br /&gt;
&lt;br /&gt;
Die Mappings sind passend zur aktuellen API. Ein Mapping lässt jedoch immer Raum für falsche Namen oder ein falsches Mapping, sollte Viessmann einen Namen in der API ändern.&lt;br /&gt;
&lt;br /&gt;
Das Mapping der Readings hat auch einen Einfluss beim Setzen von Werten der Anlage. Das heißt, sie müssen korrekt zurückgemappt werden.&lt;br /&gt;
&lt;br /&gt;
Deshalb empfehle ich, die Möglichkeit 1 zu nutzen. Bei Raw Readings findet kein Mapping statt; es werden die originalen API-Namen von Viessmann verwendet. Man kann mit diesen Namen auch im Viessmann-Forum anfragen oder sogar bei anderen SmartHome-Plattformen. Sollte sich ein Wert der Viessmann API ändern, wird er bei Raw Readings direkt als neues Reading auftauchen. Auch beim Setzen von Werten der Anlage können direkt diese Raw Readings verwendet werden.&lt;br /&gt;
&lt;br /&gt;
Aus Kompatibilitätsgründen verwendet das Modul jedoch standardmäßig das SVN Mapping.&lt;br /&gt;
&lt;br /&gt;
Um dies zu ändern, gibt es folgende Attribute am Modul:&lt;br /&gt;
* &amp;lt;code&amp;gt;vitoconnect_mapping_roger&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;vitoconnect_raw_readings&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Setzt man keines der Attribute, wird das SVN Mapping verwendet. Setzt man eines der beiden Attribute auf 1, wird dieses Mapping verwendet.&lt;br /&gt;
&lt;br /&gt;
Wie oben erklärt, ist die empfohlene Einstellung &lt;br /&gt;
:&amp;lt;code&amp;gt;vitoconnect_raw_readings = 1&amp;lt;/code&amp;gt;&lt;br /&gt;
Weiter unten die Beschreibung des SVN Mappings. Zum &amp;quot;Roger Mapping&amp;quot; gibt es keine Aufstellung, es ähnelt aber stark dem SVN Mapping.&lt;br /&gt;
&lt;br /&gt;
Raw Readings müssen nicht erklärt werden; die Beschreibung der Datenpunkte kann hier bei Viessmann gefunden werden: https://api.viessmann-climatesolutions.com/documentation/data-points&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;mw-collapsible mw-collapsed&amp;quot;&amp;gt;&lt;br /&gt;
=== Altes SVN Mapping ===&lt;br /&gt;
&amp;lt;div class=&amp;quot;mw-collapsible-content&amp;quot;&amp;gt;&lt;br /&gt;
Die Readings werden so ausgegeben, wie sie von der API geliefert werden! Alle Reading für HK1 sind auch für HK2 und HK3 verfügbar sofern die Heizung solche Readings liefert.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
!Name!!Datentyp/&amp;lt;BR /&amp;gt;Wertebereich!!Bedeutung&lt;br /&gt;
|-&lt;br /&gt;
|Aktive_Heizkreise||0,1,2&lt;br /&gt;
|Liste der aktiven Heizkreise. 0 entspricht HK1&lt;br /&gt;
|-&lt;br /&gt;
| || ||&lt;br /&gt;
|-&lt;br /&gt;
|Aussen_Status||connected/notConnected||Ist ein Außensensor angeschlossen?&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;Aussen_StatusWired&#039;&#039;||connected/notConnected||Ist ein Außensensor per Kabel angeschlossen? (wird seit 16.12.18 nicht mehr geliefert?)&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;Aussen_StatusWireless&#039;&#039;||connected/notConnected||Ist ein kabelloser Außensensor angeschlossen? (wird seit 16.12.18 nicht mehr geliefert?)&lt;br /&gt;
|-&lt;br /&gt;
|Aussentemperatur||2.4||Außentemperatur in °C&lt;br /&gt;
|-&lt;br /&gt;
| || ||&lt;br /&gt;
|-&lt;br /&gt;
|Brenner_Betriebsstunden||934.336944444444||Brenner Betriebsstunden &lt;br /&gt;
|-&lt;br /&gt;
|Brenner_Fehlercode||0||Brenner Fehlercode&lt;br /&gt;
|-&lt;br /&gt;
|Brenner_Modulation||11||Brenner Modulation in %&lt;br /&gt;
|-&lt;br /&gt;
|Brenner_Starts||2717||Brenner Starts&lt;br /&gt;
|-&lt;br /&gt;
|Brenner_Status||ok||Brenner Status&lt;br /&gt;
|-&lt;br /&gt;
|Brenner_aktiv||0,1||Brenneraktiv&lt;br /&gt;
|-&lt;br /&gt;
| || ||&lt;br /&gt;
|-&lt;br /&gt;
|Controller_Seriennummer||1234567890123456||Controller Seriennummer&lt;br /&gt;
|-&lt;br /&gt;
|Device_Time_Offset||61|| ???&lt;br /&gt;
|-&lt;br /&gt;
|Fehlereinträge_Historie||ErrorListChanges||??? (noch nicht implementiert)&lt;br /&gt;
|-&lt;br /&gt;
|Fehlereinträge_aktive||ErrorListChanges||???  (noch nicht implementiert)&lt;br /&gt;
|-&lt;br /&gt;
| || ||&lt;br /&gt;
|-&lt;br /&gt;
|Gasverbrauch_Heizung/Jahr||3293,0,0||Liste der jährlichen Gasverbräuche Heizung in kWH (aktuelles Jahr und die letzten beiden) &#039;&#039;&#039;(bis zum 09.12.2018 lieferte die API Werte, die noch durch 8 geteilt werden mussten, um auf kWh zu kommen!&#039;&#039;&#039;&lt;br /&gt;
Außerdem liefert Viessmann für manche Heizungstypen (noch) &#039;&#039;&#039;keine getrennten Werte für Heizung und WW&#039;&#039;&#039;. D.h. in den Reading für WW und Heizung steht der gleiche Wert (vermutlich der Gesamtverbrauch)!&lt;br /&gt;
|-&lt;br /&gt;
|Gasverbrauch_Heizung/Monat||1078,1384,590,241,0,0,0,0,0,0,0,0,0||Liste der monatlichen Gasverbräuche Heizung in kWH&lt;br /&gt;
|-&lt;br /&gt;
|Gasverbrauch_Heizung/Tag||76,104,113,99,76,65,60,70||Liste der täglichen Gasverbräuche Heizung in kWH (aktueller und die letzten 7 Tage) &lt;br /&gt;
|-&lt;br /&gt;
|Gasverbrauch_Heizung/Woche||555,409,416,472,289,196,254,209,24,46,&lt;br /&gt;
183,192,49,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,&lt;br /&gt;
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 &lt;br /&gt;
||Liste der wöchentlichen Gasverbräuche Heizung in kWH&lt;br /&gt;
|-&lt;br /&gt;
|Gasverbrauch_WW/Jahr||235,705,0||Liste der jährlichen Gasverbräuche Warmwasser in kWH&lt;br /&gt;
|-&lt;br /&gt;
|Gasverbrauch_WW/Monat||27,208,213,193,215,84,0,0,0,0,0,0,0||Liste der monatlichen Gasverbräuche Warmwasser in kWH&lt;br /&gt;
|-&lt;br /&gt;
|Gasverbrauch_WW/Tag||2,6,0,0,0,2,9,0||Liste der täglichen Gasverbräuche Warmwasser in kWH&lt;br /&gt;
|-&lt;br /&gt;
|Gasverbrauch_WW/Woche||17,0,37,43,51,50,41,53,52,47,41,52,44,49,41,45,52,47,45,49,51,33,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0||Liste der wöchentlichen Gasverbräuche Warmwasser in kWH&lt;br /&gt;
|-&lt;br /&gt;
| || ||&lt;br /&gt;
|-&lt;br /&gt;
|HK1-Betriebsart||standby, dwh, dhwAndHeating, forcedReduced, forcedNormal||Aus, Nur Warmwasser aktiv	, Heizung und Warmwasser aktiv, Dauernd Reduziert, Dauernd Tagbetrieb&lt;br /&gt;
|-&lt;br /&gt;
| || ||&lt;br /&gt;
|-&lt;br /&gt;
|HK1-External_Temperatur||0||Temperatur Raumsensor?&lt;br /&gt;
|-&lt;br /&gt;
|HK1-External_aktiv||0||Raumsensor aktiv?&lt;br /&gt;
|-&lt;br /&gt;
| || ||&lt;br /&gt;
|-&lt;br /&gt;
|HK1-Frostschutz_Status||on, off||Frostschutz Status&lt;br /&gt;
|-&lt;br /&gt;
| || ||&lt;br /&gt;
|-&lt;br /&gt;
|HK1-Heizkurve-Niveau||0||Heizkurve-Niveau&lt;br /&gt;
|-&lt;br /&gt;
|HK1-Heizkurve-Steigung||1.4||Heizkurve-Steigung&lt;br /&gt;
|-&lt;br /&gt;
| || ||&lt;br /&gt;
|-&lt;br /&gt;
|HK1-Programmstatus||standby, reduced, normal, forcedReduced, forcedNormal|| Programmstatus&lt;br /&gt;
|-&lt;br /&gt;
|HK1-Raum_Status||error||Raumsensor?&lt;br /&gt;
|-&lt;br /&gt;
|HK1-Raum_Temperatur||20||Temperatur Raumsensor? Nur vorhanden, wenn HK1-Raum_Status &amp;quot;ok&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
|HK1-Reduzierte_Temperatur_erzwungen||0,1||xxx&lt;br /&gt;
|-&lt;br /&gt;
|HK1-Solltemperatur_aktiv||0,1||Solltemperatur aktiv&lt;br /&gt;
|-&lt;br /&gt;
|HK1-Solltemperatur_comfort||22||Comfort Solltemeratur&lt;br /&gt;
|-&lt;br /&gt;
|HK1-Solltemperatur_comfort_aktiv||0,1||Comfort aktiv&lt;br /&gt;
|-&lt;br /&gt;
|HK1-Solltemperatur_eco||24||Eco Solltemperatur&lt;br /&gt;
|-&lt;br /&gt;
|HK1-Solltemperatur_eco_aktiv||0,1||Eco aktiv&lt;br /&gt;
|-&lt;br /&gt;
|HK1-Solltemperatur_erzwungen||0,1||Solltemperatur erzwungen&lt;br /&gt;
|-&lt;br /&gt;
|HK1-Solltemperatur_normal||24||Solltemperatur normal&lt;br /&gt;
|-&lt;br /&gt;
|HK1-Solltemperatur_reduziert||16||Solltemperatur reduziert&lt;br /&gt;
|-&lt;br /&gt;
| HK1-Solltemperatur_reduziert_aktiv||0,1||Reduziert aktiv&lt;br /&gt;
|-&lt;br /&gt;
|HK1-Standby_aktiv||0,1||Standby aktiv&lt;br /&gt;
|-&lt;br /&gt;
| || ||&lt;br /&gt;
|-&lt;br /&gt;
|HK1-Urlaub_Ende&lt;br /&gt;
|2019-02-16T00:00:00.000Z||Urlaubsabsenkung Ende&lt;br /&gt;
|-&lt;br /&gt;
|HK1-Urlaub_Start||2019-02-02T23:59:59.000Z||Urlaubsabsenkung Start&lt;br /&gt;
|-&lt;br /&gt;
|HK1-Urlaub_aktiv||0,1||Urlaubsabsenkung aktiv&lt;br /&gt;
|-&lt;br /&gt;
| || ||&lt;br /&gt;
|-&lt;br /&gt;
|HK1-Vorlauftemperatur||81||Vorlauftemperatir&lt;br /&gt;
|-&lt;br /&gt;
|HK1-Vorlauftemperatur_aktiv||connected||Vorlauftemperatursensor verbunden&lt;br /&gt;
|-&lt;br /&gt;
| || ||&lt;br /&gt;
|-&lt;br /&gt;
|HK1-WW_aktiv||0,1||Modus WW (dhw) aktiv&lt;br /&gt;
|-&lt;br /&gt;
|HK1-WW_und_Heizen_aktiv&lt;br /&gt;
|0,1||Modus WW und Heizen (dhwAndHeating) aktiv&lt;br /&gt;
|-&lt;br /&gt;
|HK1-Zeitsteuerung_Heizung||&lt;br /&gt;
wed start:05:00 mode:normal end:08:00 position:0, mode:normal end:23:00 position:1 start:16:00, sat position:0 end:23:00 mode:normal start:06:00,&lt;br /&gt;
thu position:0 mode:normal end:08:00 start:05:00, start:16:00 end:23:00 mode:normal position:1, sun start:06:00 position:0 end:23:00 mode:normal,&lt;br /&gt;
mon start:05:00 position:0 end:08:00 mode:normal, start:16:00 position:1 mode:normal end:23:00,&lt;br /&gt;
fri start:05:00 position:0 end:08:00 mode:normal, position:1 mode:normal end:23:00 start:16:00,&lt;br /&gt;
tue position:0 mode:normal end:08:00 start:05:00, mode:normal end:23:00 position:1 start:16:00, &lt;br /&gt;
||Zeitplan für Heizung&lt;br /&gt;
|-&lt;br /&gt;
|HK1-Zeitsteuerung_Heizung_aktiv||0,1||Zeitplan Heizung aktiv&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;HK1-Zeitsteuerung_Zirkulation&#039;&#039;||0,1||Zeitplan für Zirkulationspumpe. Ist irgendwann nach WW gewandert?&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;HK1-Zeitsteuerung_Zirkulation_aktiv&#039;&#039;||0,1||Zirkulationspumpe zeitgesteuert&lt;br /&gt;
|-&lt;br /&gt;
|HK1-Zirkulationspumpe||on,off||Zirkulationspumpe aktiv (neu seit 16.12.18?) &lt;br /&gt;
|-&lt;br /&gt;
|HK1-aktiv||1||HK1 aktiv&lt;br /&gt;
|-&lt;br /&gt;
| || ||&lt;br /&gt;
|-&lt;br /&gt;
|Kessel_Common_Supply||error|| ??? (neu seit 16.12.18?)&lt;br /&gt;
|-&lt;br /&gt;
|Kessel_Status&lt;br /&gt;
&lt;br /&gt;
|connected||xxx&lt;br /&gt;
|-&lt;br /&gt;
|Kesseltemperatur||53||xxx&lt;br /&gt;
|-&lt;br /&gt;
|Kesseltemperatur_exact||53||??? (neu seit 16.12.18?) &lt;br /&gt;
|-&lt;br /&gt;
|Mehrfamilienhaus_aktiv||0,1||xxx&lt;br /&gt;
|-&lt;br /&gt;
| || ||&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;Service_Betriebsstunden_seit_letzten&#039;&#039;||0||(wird seit 16.12.18 nicht mehr geliefert?)&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;Service_Intervall_Betriebsstunden&#039;&#039;||0||(wird seit 16.12.18 nicht mehr geliefert?)&lt;br /&gt;
|-&lt;br /&gt;
|Service_Intervall_Monate||0|| ???&lt;br /&gt;
|-&lt;br /&gt;
|Service_Letzter||1970-01-01T00:00:00.000Z|| ???&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;Service_Letzter_brennerbasiert&#039;&#039;||1970-01-01T00:00:00.000Z||(wird seit 16.12.18 nicht mehr geliefert?)&lt;br /&gt;
|-&lt;br /&gt;
|Service_Monate_aktiv_seit_letzten_Service||0|| ???&lt;br /&gt;
|-&lt;br /&gt;
|Service_fällig||0,1||Service fällig?&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;Service_fällig_brennerbasiert&#039;&#039;|| ||(wird seit 16.12.18 nicht mehr geliefert?)&lt;br /&gt;
|-&lt;br /&gt;
| || ||&lt;br /&gt;
|-&lt;br /&gt;
|Stromverbrauch/Jahr||35573,66052,0 ||Stromverbrauch im Jahr in Wh(?) (analog Gasverbrauch) (neu seit 15.02.19?) &lt;br /&gt;
|-&lt;br /&gt;
|Stromverbrauch/Monat||11860,23713,22851,19264,17924,6013,0,0,0,0,0,0,0||Stromverbrauch im Monat(analog Gasverbrauch) (neu seit 15.02.19?) &lt;br /&gt;
|-&lt;br /&gt;
|Stromverbrauch/Tag||409,828,720,733,731,727,708,682||Stromverbrauch im Tag (analog Gasverbrauch) (neu seit 15.02.19?) &lt;br /&gt;
|-&lt;br /&gt;
|Stromverbrauch/Woche||4418,5058,5514,5802,5249,5109,5156,5702,5179,5547,4286,4710,5087,4236,&lt;br /&gt;
4075,4633,4307,3675,3721,4148,4594,1419,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 &lt;br /&gt;
||Stromverbrauch im Woche (analog Gasverbrauch) (neu seit 15.02.19?) &lt;br /&gt;
|-&lt;br /&gt;
| || ||&lt;br /&gt;
|-&lt;br /&gt;
|WW-Aufladung||0,1||WW Vorrat wird erhitzt (neu seit 16.12.18?) &lt;br /&gt;
|-&lt;br /&gt;
|WW-Haupttemperatur||54|| ?? (neu seit 16.12.18?) &lt;br /&gt;
|-&lt;br /&gt;
|WW-Isttemperatur||56||WW Ist-Temperatur&lt;br /&gt;
|-&lt;br /&gt;
|WW-Sensoren_Auslauf_Status||error|| ??? (neu seit 16.12.18?) &lt;br /&gt;
|-&lt;br /&gt;
|WW-Solltemperatur||53||WW Solltemperatur&lt;br /&gt;
|-&lt;br /&gt;
|WW-Temperatur_aktiv||connected||WW aktiv&lt;br /&gt;
|-&lt;br /&gt;
|WW-Zeitplan||&lt;br /&gt;
fri position:0 mode:on end:22:30 start:06:30, mon start:06:30 mode:on end:22:30 position:0, &lt;br /&gt;
sat start:06:30 position:0 mode:on end:22:30, wed end:22:30 mode:on position:0 start:06:30,&lt;br /&gt;
thu start:06:30 position:0 mode:on end:22:30,&lt;br /&gt;
tue position:0 mode:on end:22:30 start:06:30, sun mode:on end:22:30 position:0 start:06:30,&lt;br /&gt;
||WW Zeitplan&lt;br /&gt;
|-&lt;br /&gt;
|WW-Zirkulationspumpe_Zeitsteuerung_aktiv||0,1||Zeitsteuerung Zirkulationspumpe aktiv(neu seit 16.12.18?) &lt;br /&gt;
|-&lt;br /&gt;
|WW-Zirkulationspumpe_Status||on||Status Zirkulationspumpe(neu seit 16.12.18?)&lt;br /&gt;
|-&lt;br /&gt;
|WW-Zirkulationspumpe_Zeitplan||&lt;br /&gt;
wed start:04:30 mode:on end:22:30 position:0, mon start:04:30 position:0 end:22:30 mode:on,&lt;br /&gt;
sun start:04:30 position:0 mode:on end:22:30, sat start:04:30 position:0 end:22:30 mode:on, thu start:04:30 end:22:30 mode:on position:0,&lt;br /&gt;
tue start:04:30 mode:on end:22:30 position:0, fri position:0 end:22:30 mode:on start:04:30,&lt;br /&gt;
||Zeitplan für die Zirkulationspumpe (neu seit 16.12.18?) &lt;br /&gt;
|-&lt;br /&gt;
|WW-Zirkulationspumpe_primaer||off||(neu seit 16.12.18?)&lt;br /&gt;
|-&lt;br /&gt;
|WW-aktiv||0,1||WW aktiv&lt;br /&gt;
|-&lt;br /&gt;
|WW-onTimeCharge_aktiv||0,1||einmaliges WW Aufladen aktiv&lt;br /&gt;
|-&lt;br /&gt;
|WW-zeitgesteuert_aktiv||0,1||WW zeitgesteuert&lt;br /&gt;
|-&lt;br /&gt;
| || ||&lt;br /&gt;
|-&lt;br /&gt;
|state || ok, Login failure||Status&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Settings==&lt;br /&gt;
&lt;br /&gt;
===Set Befehle===&lt;br /&gt;
&#039;&#039;&#039;Allgemein&#039;&#039;&#039;&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
!Name!!Datentyp/&amp;lt;BR /&amp;gt;Wertebereich !!Beschreibung&lt;br /&gt;
|-&lt;br /&gt;
|update|| ||löst ein Update der Reading zwischen manuell aus&lt;br /&gt;
|-&lt;br /&gt;
|clearReadings|| ||clear all readings immeadiatlely&lt;br /&gt;
|-&lt;br /&gt;
|clearMappedErrors&lt;br /&gt;
|&lt;br /&gt;
|clear all mapped errors immediately&lt;br /&gt;
|-&lt;br /&gt;
|password &amp;lt;passwd&amp;gt;||String||store password in key store&lt;br /&gt;
|-&lt;br /&gt;
|logResponseOnce|| ||dumps the json response of Viessmann server to entities.json, gw.json, actions.json in FHEM log directory&lt;br /&gt;
|-&lt;br /&gt;
|apiKey &amp;lt;clientID&amp;gt;&lt;br /&gt;
|String&lt;br /&gt;
|set the viessmann clientID, see define device&lt;br /&gt;
|-&lt;br /&gt;
|selectDevice&lt;br /&gt;
|&lt;br /&gt;
|in case of more then one viessmann devic, select the device to be used&lt;br /&gt;
|}&#039;&#039;&#039;Benutzung der Set-Befehle auf eigenes Risiko!&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Die set Befehle stehen je nach Mapping und Anlagen Konfiguration zur Verfügung.&lt;br /&gt;
&lt;br /&gt;
Bei vitoconnect_raw_readings haben die set Befehle die selben Namen wie die Readings und sind somit selbst erklärend oder über die Viessmann Doku nachzuschauen: [http://documentation.viessmann.com/static/iot/data-points. http://documentation.viessmann.com/static/iot/data-points.]&lt;br /&gt;
&lt;br /&gt;
Bei SVN oder Roger Mapping sind die Namen gemappt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;mw-collapsible mw-collapsed&amp;quot;&amp;gt;&lt;br /&gt;
=== Liste einiger SVN set Befehl ===&lt;br /&gt;
&amp;lt;div class=&amp;quot;mw-collapsible-content&amp;quot;&amp;gt;&lt;br /&gt;
Das Modul prüft nicht, ob ein Befehl oder die Parameter für die Heizung sinnvoll und möglich sind. Die API scheint dies mit einem Fehler  zu quittieren.&lt;br /&gt;
&lt;br /&gt;
Befehle für HK1 sind auch für HK2 und HK3 verfügbar. &lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
!Name!!Datentyp/&amp;lt;BR /&amp;gt;Wertebereich !!Beschreibung&lt;br /&gt;
|-&lt;br /&gt;
|HK1-Heizkurve-Niveau &amp;lt;shift&amp;gt;||-13 bis 40||set shift of heating curve&lt;br /&gt;
|-&lt;br /&gt;
|HK1-Heizkurve-Steigung &amp;lt;slope&amp;gt;||0.2 bis 3.5||set slope of heating curve&lt;br /&gt;
|-&lt;br /&gt;
|HK1-Betriebsart &amp;lt;mode&amp;gt;||standby,dhw,dhwAndHeating,forcedReduced or forcedNormal||set HK1-Betriebsart&lt;br /&gt;
|-&lt;br /&gt;
|HK1-Solltemperatur_comfort_aktiv &amp;lt;activate,deactivate&amp;gt;|| ||activate/deactivate comfort temperature&lt;br /&gt;
|-&lt;br /&gt;
|HK1-Solltemperatur_comfort &amp;lt;targetTemperature&amp;gt;|| ||set comfort target temperatur&lt;br /&gt;
|-&lt;br /&gt;
|HK1-Solltemperatur_eco_aktiv activate,deactivate|| ||activate/deactivate eco temperature&lt;br /&gt;
|-&lt;br /&gt;
|HK1-Urlaub_Start &amp;lt;start&amp;gt;||2019-02-02 (früher war das Format mal 2019-02-02T23:59:59.000Z)||set holiday start time&lt;br /&gt;
|-&lt;br /&gt;
|HK1-Urlaub_Ende &amp;lt;end&amp;gt;||2019-02-16 (früher war das Format mal 2019-02-16T00:00:00.000Z)||set holiday end time (geht nur, wenn schon eine Startzeit gesetzt ist)&lt;br /&gt;
|-&lt;br /&gt;
|HK1-Urlaub_unschedule|| ||remove holiday start and end time&lt;br /&gt;
|-&lt;br /&gt;
| HK1-Solltemperatur_normal &amp;lt;targetTemperature&amp;gt;||3 and 37||sets the normale target temperature&lt;br /&gt;
|-&lt;br /&gt;
|HK1-Solltemperatur_reduziert &amp;lt;targetTemperature&amp;gt;||3 and 37||sets the reduced target temperature&lt;br /&gt;
|-&lt;br /&gt;
| || ||&lt;br /&gt;
|-&lt;br /&gt;
|WW-einmaliges_Aufladen activate,deactivate|| ||activate or deactivate one time charge for hot water&lt;br /&gt;
|-&lt;br /&gt;
|WW-Zirkulationspumpe_Zeitplan  &amp;lt;schedule&amp;gt;|| ||not implemented&lt;br /&gt;
|-&lt;br /&gt;
|WW-Zeitplan &amp;lt;schedule&amp;gt;|| ||not implemented&lt;br /&gt;
|-&lt;br /&gt;
|WW-Haupttemperatur &amp;lt;targetTemperature&amp;gt;||10 and 60||sets hot water main temperature to targetTemperature&lt;br /&gt;
|-&lt;br /&gt;
|WW-Solltemperatur &amp;lt;targetTemperature&amp;gt;||10 and 60||sets hot water temperature to targetTemperature&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Get==&lt;br /&gt;
Derzeit gibt es keine &amp;quot;get&amp;quot;-Kommandos.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
!Name!!Beschreibung&lt;br /&gt;
|-&lt;br /&gt;
| ||&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==Attribute==&lt;br /&gt;
===Attribute ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
!Name!!Datentyp/&amp;lt;BR /&amp;gt;Wertebereich !!Default-Wert&lt;br /&gt;
!Beschreibung&lt;br /&gt;
|-&lt;br /&gt;
|disable||0,1|| ||Disable updates&lt;br /&gt;
|-&lt;br /&gt;
|verbose||0,1,2,3,4,5|| ||Loglevel&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|vitoconnect_raw_readings||0,1|| ||Lege Reading mit dem JSON Namen wie &#039;heating.circuits.0.heating.curve.slope&#039; statt der Deutschen Bezeichnungen an&lt;br /&gt;
|-&lt;br /&gt;
|vitoconnect_actions_active||0,1|| ||Erzeuge Readings for Befehle wie &#039;heating.circuits.0.heating.curve.setCurve&#039;&lt;br /&gt;
|-&lt;br /&gt;
|vitoconnect_mapping_roger&lt;br /&gt;
|0,1&lt;br /&gt;
|&lt;br /&gt;
|Verwenden das Mapping von Roger vom 8. November (&amp;lt;nowiki&amp;gt;https://forum.fhem.de/index.php?msg=1292441&amp;lt;/nowiki&amp;gt;) anstelle der SVN-Zuordnung&lt;br /&gt;
|-&lt;br /&gt;
|vitoconnect_installationID&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|Dieses Attribut wird bei der Initialisierung des FHEM-Geräts befüllt. Muss etwas getan werden gibt es eine Benachrichtigung und der Befehl set selectDevice muss ausgeführt werden.&lt;br /&gt;
Sie müssen dieses Attribut nicht manuell setzen. &lt;br /&gt;
|-&lt;br /&gt;
| vitoconnect_serial&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|Dieses Attribut wird bei der Initialisierung des FHEM-Geräts befüllt. Muss etwas getan werden gibt es eine Benachrichtigung und der Befehl set selectDevice muss ausgeführt werden.&lt;br /&gt;
Sie müssen dieses Attribut nicht manuell setzen &lt;br /&gt;
|-&lt;br /&gt;
|vitoconnect_gw_readings&lt;br /&gt;
|0,1&lt;br /&gt;
|&lt;br /&gt;
|Erstellt ein Reading für Gateway Informationen&lt;br /&gt;
|-&lt;br /&gt;
|vitoconnect_disable_raw_readings&lt;br /&gt;
|0,1&lt;br /&gt;
|&lt;br /&gt;
|Diese Einstellung deaktiviert die zusätzliche Generierung von raw Readings. Das bedeutet, nur die Readings, im gewählten Mapping werden erzeugt. Diese Einstellung ist nicht aktiv, wenn vitoconnect_raw_readings = 1 gesetzt ist. &lt;br /&gt;
|-&lt;br /&gt;
|vitoconnect_mappings&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|Eigene Zuordnung von Schlüssel-Wert-Paaren anstelle der eingebauten.&lt;br /&gt;
|-&lt;br /&gt;
|vitoconnect_translations&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|Eigene Übersetzung; sie wird jedes Wort Teil für Teil übersetzen.&lt;br /&gt;
|-&lt;br /&gt;
|vitoconnect_device&lt;br /&gt;
|0,1&lt;br /&gt;
|&lt;br /&gt;
|Das Viessmann Gerät kann auf 0 oder 1 gesetzt werden. Standard 0 sollte immer passen. Sollte es einen Fall geben wo dies nicht passt bitte beim Entwickler melden.&lt;br /&gt;
|-&lt;br /&gt;
|vitoconnect_timeout&lt;br /&gt;
|10-30&lt;br /&gt;
|&lt;br /&gt;
|Setzt ein Timeout für den API-Aufruf.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Hilfsmittel ==&lt;br /&gt;
=== Attribut zum Speichern der wichtigsten Werte mittels DbLog ===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;attr vitoconnect DbLogInclude Gasverbrauch_gestern,Gasverbrauch_Heizung/Jahr,Gasverbrauch_aktuelle_Woche,Gasverbrauch_aktueller_Monat,Gasverbrauch_heute,WW-aktiv,&lt;br /&gt;
HK1-Frostschutz_Status,HK1-WW_und_Heizen_aktiv,HK1-WW_aktiv,HK1-Betriebsart,HK1-Programmstatus,Aussentemperatur,HK1-Solltemperatur_normal,Brenner_aktiv,&lt;br /&gt;
HK1-Vorlauftemperatur,Kesseltemperatur,WW-Isttemperatur,WW-Solltemperatur&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Userreadings um Reading für Einzelwerte von Gasverbräuche zu erzeugen===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
attr vitoconnect userReadings&lt;br /&gt;
Gasverbrauch_heute:Gasverbrauch_Heizung/Tag.* { (split /,/, ReadingsVal(&amp;quot;vitoconnect&amp;quot;, &amp;quot;Gasverbrauch_Heizung/Tag&amp;quot;,0))[0] },&lt;br /&gt;
Gasverbrauch_gestern:Gasverbrauch_Heizung/Tag.* { (split /,/, ReadingsVal(&amp;quot;vitoconnect&amp;quot;,&amp;quot;Gasverbrauch_Heizung/Tag&amp;quot;,0))[1] },&lt;br /&gt;
Gasverbrauch_aktuelle_Woche:Gasverbrauch_Heizung/Woche.* { (split /,/, ReadingsVal(&amp;quot;vitoconnect&amp;quot;,&amp;quot;Gasverbrauch_Heizung/Woche&amp;quot;,0))[0] },&lt;br /&gt;
Gasverbrauch_letzte_Woche:Gasverbrauch_Heizung/Woche.* { (split /,/, ReadingsVal(&amp;quot;vitoconnect&amp;quot;,&amp;quot;Gasverbrauch_Heizung/Woche&amp;quot;,0))[1] },&lt;br /&gt;
Gasverbrauch_aktueller_Monat:Gasverbrauch_Heizung/Monat.* { (split /,/, ReadingsVal(&amp;quot;vitoconnect&amp;quot;,&amp;quot;Gasverbrauch_Heizung/Monat&amp;quot;,0))[0] },&lt;br /&gt;
Gasverbrauch_letzter_Monat:Gasverbrauch_Heizung/Monat.* { (split /,/, ReadingsVal(&amp;quot;vitoconnect&amp;quot;,&amp;quot;Gasverbrauch_Heizung/Monat&amp;quot;,0))[1] },&lt;br /&gt;
Gasverbrauch_aktuelles_Jahr:Gasverbrauch_Heizung/Jahr.* { (split /,/, ReadingsVal(&amp;quot;vitoconnect&amp;quot;,&amp;quot;Gasverbrauch_Heizung/Jahr&amp;quot;,0))[0] },&lt;br /&gt;
Gasverbrauch_letztes_Jahr:Gasverbrauch_Heizung/Jahr.* { (split /,/, ReadingsVal(&amp;quot;vitoconnect&amp;quot;,&amp;quot;Gasverbrauch_Heizung/Jahr&amp;quot;,0))[1] },&lt;br /&gt;
&lt;br /&gt;
Gasverbrauch_WW_heute:Gasverbrauch_WW/Tag.* { (split /,/, ReadingsVal(&amp;quot;vitoconnect&amp;quot;, &amp;quot;Gasverbrauch_WW/Tag&amp;quot;,0))[0] },&lt;br /&gt;
Gasverbrauch_WW_gestern:Gasverbrauch_WW/Tag.* { (split /,/, ReadingsVal(&amp;quot;vitoconnect&amp;quot;,&amp;quot;Gasverbrauch_WW/Tag&amp;quot;,0))[1] },&lt;br /&gt;
Gasverbrauch_WW_aktuelle_Woche:Gasverbrauch_WW/Woche.* { (split /,/, ReadingsVal(&amp;quot;vitoconnect&amp;quot;,&amp;quot;Gasverbrauch_WW/Woche&amp;quot;,0))[0] },&lt;br /&gt;
Gasverbrauch_WW_letzte_Woche:Gasverbrauch_WW/Woche.* { (split /,/, ReadingsVal(&amp;quot;vitoconnect&amp;quot;,&amp;quot;Gasverbrauch_WW/Woche&amp;quot;,0))[1] },&lt;br /&gt;
Gasverbrauch_WW_aktueller_Monat:Gasverbrauch_WW/Monat.* { (split /,/, ReadingsVal(&amp;quot;vitoconnect&amp;quot;,&amp;quot;Gasverbrauch_WW/Monat&amp;quot;,0))[0] },&lt;br /&gt;
Gasverbrauch_WW_letzter_Monat:Gasverbrauch_WW/Monat.* { (split /,/, ReadingsVal(&amp;quot;vitoconnect&amp;quot;,&amp;quot;Gasverbrauch_WW/Monat&amp;quot;,0))[1] },&lt;br /&gt;
Gasverbrauch_WW_aktuelles_Jahr:Gasverbrauch_WW/Jahr.* { (split /,/, ReadingsVal(&amp;quot;vitoconnect&amp;quot;,&amp;quot;Gasverbrauch_WW/Jahr&amp;quot;,0))[0] },&lt;br /&gt;
Gasverbrauch_WW_letztes_Jahr:Gasverbrauch_WW/Jahr.* { (split /,/, ReadingsVal(&amp;quot;vitoconnect&amp;quot;, &amp;quot;Gasverbrauch_WW/Jahr&amp;quot;,0))[1] },&lt;br /&gt;
&lt;br /&gt;
Stromverbrauch_heute:Stromverbrauch/Tag.* { (split /,/, ReadingsVal(&amp;quot;vitoconnect&amp;quot;, &amp;quot;Stromverbrauch/Tag&amp;quot;,0))[0] },&lt;br /&gt;
Stromverbrauch_gestern:Stromverbrauch/Tag.* { (split /,/, ReadingsVal(&amp;quot;vitoconnect&amp;quot;, &amp;quot;Stromverbrauch/Tag&amp;quot;,0))[1] },&lt;br /&gt;
Stromverbrauch_aktuelle_Woche:Stromverbrauch/Woche.* { (split /,/, ReadingsVal(&amp;quot;vitoconnect&amp;quot;, &amp;quot;Stromverbrauch/Woche&amp;quot;,0))[0] },&lt;br /&gt;
Stromverbrauch_letzte_Woche:Stromverbrauch/Woche.* { (split /,/, ReadingsVal(&amp;quot;vitoconnect&amp;quot;, &amp;quot;Stromverbrauch/Woche&amp;quot;,0))[1] },&lt;br /&gt;
Stromverbrauch_aktueller_Monat:Stromverbrauch/Monat.* { (split /,/, ReadingsVal(&amp;quot;vitoconnect&amp;quot;, &amp;quot;Stromverbrauch/Monat&amp;quot;,0))[0] },&lt;br /&gt;
Stromverbrauch_letzter_Monat:Stromverbrauch/Monat.* { (split /,/, ReadingsVal(&amp;quot;vitoconnect&amp;quot;, &amp;quot;Stromverbrauch/Monat&amp;quot;,0))[1] },&lt;br /&gt;
Stromverbrauch_aktuelles_Jahr:Stromverbrauch/Jahr.* { (split /,/, ReadingsVal(&amp;quot;vitoconnect&amp;quot;, &amp;quot;Stromverbrauch/Jahr&amp;quot;,0))[0] },&lt;br /&gt;
Stromverbrauch_letztes_Jahr:Stromverbrauch/Jahr.* { (split /,/, ReadingsVal(&amp;quot;vitoconnect&amp;quot;, &amp;quot;Stromverbrauch/Jahr&amp;quot;,0))[1] }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===ReadingsGroup für eine kompakte Darstellung===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot; style=&amp;quot;width:90%;&amp;quot;&amp;gt;&lt;br /&gt;
defmod vitoconnect_rg readingsGroup&lt;br /&gt;
vitoconnect:Aussentemperatur&lt;br /&gt;
&amp;lt;Brenner&amp;gt;&lt;br /&gt;
vitoconnect:Brenner_Betriebsstunden&lt;br /&gt;
vitoconnect:Brenner_Fehlercode&lt;br /&gt;
vitoconnect:Brenner_Modulation&lt;br /&gt;
vitoconnect:Brenner_Starts&lt;br /&gt;
vitoconnect:Brenner_Status&lt;br /&gt;
vitoconnect:Brenner_aktiv&lt;br /&gt;
&amp;lt;HK1&amp;gt;&lt;br /&gt;
vitoconnect:HK1-aktiv&lt;br /&gt;
vitoconnect:HK1-Betriebsart&lt;br /&gt;
&lt;br /&gt;
vitoconnect:HK1-Frostschutz_Status&lt;br /&gt;
&lt;br /&gt;
vitoconnect:HK1-Heizkurve-Niveau&lt;br /&gt;
vitoconnect:HK1-Heizkurve-Steigung&lt;br /&gt;
&lt;br /&gt;
vitoconnect:HK1-Programmstatus&lt;br /&gt;
vitoconnect:HK1-Raum_Status&lt;br /&gt;
vitoconnect:HK1-Reduzierte_Temperatur_erzwungen&lt;br /&gt;
vitoconnect:HK1-Solltemperatur_aktiv&lt;br /&gt;
vitoconnect:HK1-Solltemperatur_erzwungen&lt;br /&gt;
vitoconnect:HK1-Solltemperatur_normal&lt;br /&gt;
vitoconnect:HK1-Solltemperatur_reduziert&lt;br /&gt;
vitoconnect:HK1-Solltemperatur_reduziert_aktiv&lt;br /&gt;
vitoconnect:HK1-Standby_aktiv&lt;br /&gt;
&lt;br /&gt;
vitoconnect:HK1-Urlaub_Ende&lt;br /&gt;
vitoconnect:HK1-Urlaub_Start&lt;br /&gt;
vitoconnect:HK1-Urlaub_aktiv&lt;br /&gt;
&lt;br /&gt;
vitoconnect:HK1-Vorlauftemperatur&lt;br /&gt;
&lt;br /&gt;
vitoconnect:HK1-WW_aktiv&lt;br /&gt;
vitoconnect:HK1-WW_und_Heizen_aktiv&lt;br /&gt;
&lt;br /&gt;
vitoconnect:HK1-Zeitsteuerung_Heizung_aktiv&lt;br /&gt;
vitoconnect:HK1-Zeitsteuerung_Zirkulation_aktiv&lt;br /&gt;
&lt;br /&gt;
vitoconnect:HK1-Zirkulationspumpe&lt;br /&gt;
&amp;lt;Kessel&amp;gt;&lt;br /&gt;
vitoconnect:Kesseltemperatur&lt;br /&gt;
vitoconnect:Kesseltemperatur_exact&lt;br /&gt;
&amp;lt;WW&amp;gt;&lt;br /&gt;
vitoconnect:WW-Aufladung&lt;br /&gt;
vitoconnect:WW-Haupttemperatur&lt;br /&gt;
vitoconnect:WW-Isttemperatur&lt;br /&gt;
vitoconnect:WW-Solltemperatur&lt;br /&gt;
vitoconnect:WW-Zirklationspumpe_Zeitsteuerung_aktiv&lt;br /&gt;
vitoconnect:WW-Zirkulationspumpe_Status&lt;br /&gt;
vitoconnect:WW-Zirkulationspumpe_primaer&lt;br /&gt;
vitoconnect:WW-aktiv&lt;br /&gt;
vitoconnect:WW-onTimeCharge_aktiv&lt;br /&gt;
vitoconnect:WW-zeitgesteuert_aktiv&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot; style=&amp;quot;width:90%;&amp;quot;&amp;gt;&lt;br /&gt;
attr vitoconnect_rg alias Heizung&lt;br /&gt;
attr vitoconnect_rg cellStyle { &lt;br /&gt;
&amp;quot;c:0&amp;quot;=&amp;gt;&#039;style=&amp;quot;text-align:left&amp;quot;&#039;, &lt;br /&gt;
&amp;quot;c:1&amp;quot;=&amp;gt;&#039;style=&amp;quot;text-align:left&amp;quot;&#039;, &lt;br /&gt;
&amp;quot;r:2&amp;quot;=&amp;gt;&#039;style=&amp;quot;text-align:right;;;;font-weight:bold&amp;quot;&#039;&lt;br /&gt;
}&lt;br /&gt;
attr vitoconnect_rg mapping {&lt;br /&gt;
&amp;quot;Aussentemperatur&amp;quot; =&amp;gt; &amp;quot;Aussentemperatur&amp;quot;,&lt;br /&gt;
&lt;br /&gt;
&amp;quot;Brenner_Betriebsstunden&amp;quot; =&amp;gt; &amp;quot;Betriebsstunden&amp;quot;,&lt;br /&gt;
&amp;quot;Brenner_Fehlercode&amp;quot; =&amp;gt; &amp;quot;Fehlercode&amp;quot;,&lt;br /&gt;
&amp;quot;Brenner_Modulation&amp;quot; =&amp;gt; &amp;quot;Modulation&amp;quot;,&lt;br /&gt;
&amp;quot;Brenner_Starts&amp;quot; =&amp;gt; &amp;quot;Starts&amp;quot;,&lt;br /&gt;
&amp;quot;Brenner_Status&amp;quot; =&amp;gt; &amp;quot;Status&amp;quot;,&lt;br /&gt;
&amp;quot;Brenner_aktiv&amp;quot; =&amp;gt; &amp;quot;aktiv&amp;quot;,&lt;br /&gt;
&lt;br /&gt;
&amp;quot;HK1-aktiv&amp;quot; =&amp;gt; &amp;quot;aktiv&amp;quot;,&lt;br /&gt;
&amp;quot;HK1-Betriebsart&amp;quot; =&amp;gt; &amp;quot;Betriebsart&amp;quot;,&lt;br /&gt;
&amp;quot;HK1-Frostschutz_Status&amp;quot; =&amp;gt; &amp;quot;Frostschutz_Status&amp;quot;,&lt;br /&gt;
&amp;quot;HK1-Heizkurve-Niveau&amp;quot; =&amp;gt; &amp;quot;Heizkurve-Niveau&amp;quot;,&lt;br /&gt;
&amp;quot;HK1-Heizkurve-Steigung&amp;quot; =&amp;gt; &amp;quot;Heizkurve-Steigung&amp;quot;,&lt;br /&gt;
&amp;quot;HK1-Programmstatus&amp;quot; =&amp;gt; &amp;quot;Programmstatus&amp;quot;,&lt;br /&gt;
&amp;quot;HK1-Raum_Status&amp;quot; =&amp;gt; &amp;quot;Raum Status&amp;quot;,&lt;br /&gt;
&amp;quot;HK1-Reduzierte_Temperatur_erzwungen&amp;quot; =&amp;gt; &amp;quot;Reduzierte Temperatur erzwungen&amp;quot;,&lt;br /&gt;
&amp;quot;HK1-Solltemperatur_aktiv&amp;quot; =&amp;gt; &amp;quot;Solltemperatur aktiv&amp;quot;,&lt;br /&gt;
&amp;quot;HK1-Solltemperatur_erzwungen&amp;quot; =&amp;gt; &amp;quot;Solltemperatur erzwungen&amp;quot;,&lt;br /&gt;
&amp;quot;HK1-Solltemperatur_normal&amp;quot; =&amp;gt; &amp;quot;Solltemperatur normal&amp;quot;,&lt;br /&gt;
&amp;quot;HK1-Solltemperatur_reduziert&amp;quot; =&amp;gt; &amp;quot;Solltemperatur reduziert&amp;quot;,&lt;br /&gt;
&amp;quot;HK1-Solltemperatur_reduziert_aktiv&amp;quot; =&amp;gt; &amp;quot;Solltemperatur reduziert_aktiv&amp;quot;,&lt;br /&gt;
&amp;quot;HK1-Standby_aktiv&amp;quot; =&amp;gt; &amp;quot;Standby aktiv&amp;quot;,&lt;br /&gt;
&amp;quot;HK1-Urlaub_Ende&amp;quot; =&amp;gt; &amp;quot;Urlaub Ende&amp;quot;,&lt;br /&gt;
&amp;quot;HK1-Urlaub_Start&amp;quot; =&amp;gt; &amp;quot;Urlaub Start&amp;quot;,&lt;br /&gt;
&amp;quot;HK1-Urlaub_aktiv&amp;quot; =&amp;gt; &amp;quot;Urlaub aktiv&amp;quot;,&lt;br /&gt;
&amp;quot;HK1-Vorlauftemperatur&amp;quot; =&amp;gt; &amp;quot;Vorlauftemperatur&amp;quot;,&lt;br /&gt;
&amp;quot;HK1-WW_aktiv&amp;quot; =&amp;gt; &amp;quot;WW aktiv&amp;quot;,&lt;br /&gt;
&amp;quot;HK1-WW_und_Heizen_aktiv&amp;quot; =&amp;gt; &amp;quot;WW und Heizen aktiv&amp;quot;,&lt;br /&gt;
&amp;quot;HK1-Zeitsteuerung_Heizung_aktiv&amp;quot; =&amp;gt; &amp;quot;Zeitsteuerung Heizung aktiv&amp;quot;,&lt;br /&gt;
&amp;quot;HK1-Zirkulationspumpe&amp;quot; =&amp;gt; &amp;quot;Zirkulationspumpe&amp;quot;,&lt;br /&gt;
&lt;br /&gt;
&amp;quot;Kesseltemperatur&amp;quot; =&amp;gt; &amp;quot;Kesseltemperatur&amp;quot;,&lt;br /&gt;
&amp;quot;Kesseltemperatur_exact&amp;quot; =&amp;gt; &amp;quot;Kesseltemperatur exact&amp;quot;,&lt;br /&gt;
&lt;br /&gt;
&amp;quot;WW-Aufladung&amp;quot; =&amp;gt; &amp;quot;Aufladung&amp;quot;,&lt;br /&gt;
&amp;quot;WW-Haupttemperatur&amp;quot; =&amp;gt; &amp;quot;Haupttemperatur&amp;quot;,&lt;br /&gt;
&amp;quot;WW-Isttemperatur&amp;quot; =&amp;gt; &amp;quot;Isttemperatur&amp;quot;,&lt;br /&gt;
&amp;quot;WW-Solltemperatur&amp;quot; =&amp;gt; &amp;quot;Solltemperatur&amp;quot;,&lt;br /&gt;
&amp;quot;WW-Zirklationspumpe_Zeitsteuerung_aktiv&amp;quot; =&amp;gt; &amp;quot;Zirkulationspumpe Zeitsteuerung aktiv&amp;quot;,&lt;br /&gt;
&amp;quot;WW-Zirkulationspumpe_Status&amp;quot; =&amp;gt; &amp;quot;Zirkulationspumpe Status&amp;quot;,&lt;br /&gt;
&amp;quot;WW-Zirkulationspumpe_primaer&amp;quot; =&amp;gt; &amp;quot;Zirkulationspumpe primaer&amp;quot;,&lt;br /&gt;
&amp;quot;WW-aktiv&amp;quot; =&amp;gt; &amp;quot;aktiv&amp;quot;,&lt;br /&gt;
&amp;quot;WW-onTimeCharge_aktiv&amp;quot; =&amp;gt; &amp;quot;onTimeCharge aktiv&amp;quot;,&lt;br /&gt;
&amp;quot;WW-zeitgesteuert_aktiv&amp;quot; =&amp;gt; &amp;quot;zeitgesteuert aktiv&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
attr vitoconnect_rg nostate 1&lt;br /&gt;
attr vitoconnect_rg notime 1&lt;br /&gt;
attr vitoconnect_rg room Heizung&lt;br /&gt;
attr vitoconnect_rg sortby 1&lt;br /&gt;
attr vitoconnect_rg valueFormat { &amp;quot;Brenner_Betriebsstunden&amp;quot; =&amp;gt; &amp;quot;%2d&amp;quot; }&lt;br /&gt;
attr vitoconnect_rg valueIcon {&#039;Brenner_aktiv.0&#039; =&amp;gt; &#039;1px-spacer&#039;, &lt;br /&gt;
&#039;Brenner_aktiv.1&#039; =&amp;gt; &#039;icoHEIZUNG&#039;,&lt;br /&gt;
&#039;HK1-Solltemperatur_reduziert_aktiv.0&#039; =&amp;gt; &#039;10px-kreis-rot&#039;,&lt;br /&gt;
&#039;HK1-Solltemperatur_reduziert_aktiv.1&#039; =&amp;gt; &#039;10px-kreis-gruen&#039;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
attr vitoconnect_rg valueSuffix {&lt;br /&gt;
Aussentemperatur =&amp;gt; &amp;quot;°C&amp;quot;, &lt;br /&gt;
&lt;br /&gt;
Brenner_Betriebsstunden =&amp;gt; &amp;quot;h&amp;quot;,&lt;br /&gt;
Brenner_Modulation =&amp;gt; &amp;quot;%&amp;quot;,&lt;br /&gt;
&lt;br /&gt;
&#039;HK1-Solltemperatur_normal&#039; =&amp;gt; &amp;quot;°C&amp;quot;,&lt;br /&gt;
&#039;HK1-Solltemperatur_reduziert&#039; =&amp;gt; &amp;quot;°C&amp;quot;,&lt;br /&gt;
&#039;HK1-Vorlauftemperatur&#039; =&amp;gt; &amp;quot; °C&amp;quot;,&lt;br /&gt;
&lt;br /&gt;
Kesseltemperatur =&amp;gt; &amp;quot;°C&amp;quot;,&lt;br /&gt;
Kesseltemperatur_exact =&amp;gt; &amp;quot;°C&amp;quot;,&lt;br /&gt;
&lt;br /&gt;
&#039;WW-Solltemperatur&#039; =&amp;gt; &amp;quot; °C&amp;quot;,&lt;br /&gt;
&#039;WW-Isttemperatur&#039; =&amp;gt; &amp;quot; °C&amp;quot;,&lt;br /&gt;
&lt;br /&gt;
&#039;WW-Solltemperatur&#039; =&amp;gt; &amp;quot;°C&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Datei:ReadingsGroup für Vitoconnect Modul 3.jpg|200px]]&lt;br /&gt;
===ReadingsGroup für eine kompakte Darstellung der Verbräuche===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot; style=&amp;quot;width:100%;&amp;quot;&amp;gt;&lt;br /&gt;
defmod Heizung_Verbrauch readingsGroup &amp;lt;&amp;gt;,&amp;lt;Periode&amp;gt;,&amp;lt;Heizung&amp;gt;,&amp;lt;Wasser&amp;gt;,&amp;lt;Strom&amp;gt;&lt;br /&gt;
vitoconnect:,&amp;lt;heute&amp;gt;,Gasverbrauch_heute,Gasverbrauch_WW_heute,Stromverbrauch_heute&lt;br /&gt;
vitoconnect:,&amp;lt;gestern&amp;gt;,Gasverbrauch_gestern,Gasverbrauch_WW_gestern,Stromverbrauch_gestern&lt;br /&gt;
vitoconnect:,&amp;lt;Woche&amp;gt;,Gasverbrauch_aktuelle_Woche,Gasverbrauch_WW_aktuelle_Woche,Stromverbrauch_aktuelle_Woche&lt;br /&gt;
vitoconnect:,&amp;lt;letzte&amp;gt;,Gasverbrauch_letzte_Woche,Gasverbrauch_WW_letzte_Woche,Stromverbrauch_letzte_Woche&lt;br /&gt;
vitoconnect:,&amp;lt;Monat&amp;gt;,Gasverbrauch_aktueller_Monat,Gasverbrauch_WW_aktueller_Monat,Stromverbrauch_aktueller_Monat&lt;br /&gt;
vitoconnect:,&amp;lt;letzter&amp;gt;,Gasverbrauch_letzter_Monat,Gasverbrauch_WW_letzter_Monat,Stromverbrauch_letzter_Monat&lt;br /&gt;
vitoconnect:,&amp;lt;Jahr&amp;gt;,Gasverbrauch_aktuelles_Jahr,Gasverbrauch_WW_aktuelles_Jahr,Stromverbrauch_aktuelles_Jahr&lt;br /&gt;
vitoconnect:,&amp;lt;letztes&amp;gt;,Gasverbrauch_letztes_Jahr,Gasverbrauch_WW_letztes_Jahr,Stromverbrauch_letztes_Jahr&lt;br /&gt;
&lt;br /&gt;
attr Heizung_Verbrauch cellStyle { &lt;br /&gt;
&amp;quot;r:1&amp;quot;=&amp;gt;&#039;style=&amp;quot;text-align:left;;;;font-weight:bold&amp;quot;&#039;,&lt;br /&gt;
&amp;quot;c:1&amp;quot;=&amp;gt;&#039;style=&amp;quot;text-align:right;;;;font-weight:bold&amp;quot;&#039;&lt;br /&gt;
}&lt;br /&gt;
attr Heizung_Verbrauch mapping &amp;amp;nbsp;;&lt;br /&gt;
attr Heizung_Verbrauch nostate 1&lt;br /&gt;
attr Heizung_Verbrauch room Heizung&lt;br /&gt;
attr Heizung_Verbrauch style style=&amp;quot;text-align:right;;;;font-size:18px&amp;quot;&lt;br /&gt;
attr Heizung_Verbrauch valueFormat {&lt;br /&gt;
Gasverbrauch_heute				=&amp;gt; &amp;quot;%.0f kWh&amp;quot;,&lt;br /&gt;
Gasverbrauch_WW_heute			=&amp;gt; &amp;quot;%.0f kWh&amp;quot;,&lt;br /&gt;
Stromverbrauch_heute			=&amp;gt; &amp;quot;%.0f kWh&amp;quot;,&lt;br /&gt;
Gasverbrauch_gestern			=&amp;gt; &amp;quot;%.0f kWh&amp;quot;,&lt;br /&gt;
Gasverbrauch_WW_gestern			=&amp;gt; &amp;quot;%.0f kWh&amp;quot;,&lt;br /&gt;
Stromverbrauch_gestern			=&amp;gt; &amp;quot;%.0f kWh&amp;quot;,&lt;br /&gt;
Gasverbrauch_aktuelle_Woche		=&amp;gt; &amp;quot;%.0f kWh&amp;quot;,&lt;br /&gt;
Gasverbrauch_WW_aktuelle_Woche	=&amp;gt; &amp;quot;%.0f kWh&amp;quot;,&lt;br /&gt;
Stromverbrauch_aktuelle_Woche	=&amp;gt; &amp;quot;%.0f kWh&amp;quot;,&lt;br /&gt;
Gasverbrauch_letzte_Woche		=&amp;gt; &amp;quot;%.0f kWh&amp;quot;,&lt;br /&gt;
Gasverbrauch_WW_letzte_Woche	=&amp;gt; &amp;quot;%.0f kWh&amp;quot;,&lt;br /&gt;
Stromverbrauch_letzte_Woche		=&amp;gt; &amp;quot;%.0f kWh&amp;quot;,&lt;br /&gt;
Gasverbrauch_aktueller_Monat	=&amp;gt; &amp;quot;%.0f kWh&amp;quot;,&lt;br /&gt;
Gasverbrauch_WW_aktueller_Monat	=&amp;gt; &amp;quot;%.0f kWh&amp;quot;,&lt;br /&gt;
Stromverbrauch_aktueller_Monat	=&amp;gt; &amp;quot;%.0f kWh&amp;quot;,&lt;br /&gt;
Gasverbrauch_letzter_Monat		=&amp;gt; &amp;quot;%.0f kWh&amp;quot;,&lt;br /&gt;
Gasverbrauch_WW_letzter_Monat	=&amp;gt; &amp;quot;%.0f kWh&amp;quot;,&lt;br /&gt;
Stromverbrauch_letzter_Monat	=&amp;gt; &amp;quot;%.0f kWh&amp;quot;,&lt;br /&gt;
Gasverbrauch_aktuelles_Jahr		=&amp;gt; &amp;quot;%.0f kWh&amp;quot;,&lt;br /&gt;
Gasverbrauch_WW_aktuelles_Jahr	=&amp;gt; &amp;quot;%.0f kWh&amp;quot;,&lt;br /&gt;
Stromverbrauch_aktuelles_Jahr	=&amp;gt; &amp;quot;%.0f kWh&amp;quot;,&lt;br /&gt;
Gasverbrauch_letztes_Jahr		=&amp;gt; &amp;quot;%.0f kWh&amp;quot;,&lt;br /&gt;
Gasverbrauch_WW_letztes_Jahr	=&amp;gt; &amp;quot;%.0f kWh&amp;quot;,&lt;br /&gt;
Stromverbrauch_letztes_Jahr		=&amp;gt; &amp;quot;%.0f kWh&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
[[Datei:Readuingsgroup Verbräuche aus Vitoconnect 2.jpg|200px]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== SVG Graphen ===&lt;br /&gt;
==== Gas- und Stromverbrauch ====&lt;br /&gt;
&lt;br /&gt;
[[Datei:SVG Gasverbrauch für vitoconnect Modul.jpg|800px]]&lt;br /&gt;
&lt;br /&gt;
[[Datei:Verbräuche aus Vitoconnect.jpg|800px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
==== Weitere Graphen ====&lt;br /&gt;
[[Datei:Beispiel für Grafiken aus vitoconnect Daten.jpg|800px]]&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Speichern der wichtigsten Werte mittels FileLog und Erzeugung der entspr. SVG Plots ===&lt;br /&gt;
==== FileLog erstellen====&lt;br /&gt;
Zunächst wird das FileLog mit ausgewählten Parametern erstellt. Dies ist die Voraussetzung für die SVG Plots.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
define FileLog_VitoConnect FileLog ./log/VitoConnect-%Y-%m.log vitoconnect:Aussentemperatur|vitoconnect:Brenner_Modulation|vitoconnect:Brenner_aktiv|vitoconnect:Gasverbrauch_Heizung/Jahr|vitoconnect:Gasverbrauch_aktuelle_Woche|vitoconnect:Gasverbrauch_aktueller_Monat|vitoconnect:Gasverbrauch_gestern|vitoconnect:Gasverbrauch_heute|vitoconnect:HK1-Betriebsart|vitoconnect:HK1-Frostschutz_Status|vitoconnect:HK1-Programmstatus|vitoconnect:HK1-Solltemperatur_aktiv|vitoconnect:HK1-Solltemperatur_normal|vitoconnect:HK1-Solltemperatur_reduziert|vitoconnect:HK1-Solltemperatur_reduziert_aktiv|vitoconnect:HK1-Vorlauftemperatur|vitoconnect:HK1-WW_aktiv|vitoconnect:HK1-WW_und_Heizen_aktiv|vitoconnect:HK1-Zirkulationspumpe|vitoconnect:Kessel_Common_Supply_Temperatur|vitoconnect:Kesseltemperatur_exact|vitoconnect:WW-Isttemperatur|vitoconnect:WW-Sensoren_Auslauf_Wert|vitoconnect:WW-Solltemperatur|vitoconnect:WW-Zirkulationspumpe_Status|vitoconnect:WW-zeitgesteuert_aktiv&lt;br /&gt;
attr FileLog_VitoConnect room Log&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== SVG Graphen aus FileLog erstellen ====&lt;br /&gt;
Nachdem das FileLog erstellt wurde, kann aus dem Log nun der Plot von ausgewählten Werten erstellt werden.&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== SVG Plot für Heizkreis1 =====&lt;br /&gt;
Im folgenden Beispiel werden hier verschiedene Graphen für Heizkreis1 (HK1) genommen.&lt;br /&gt;
Einige der Logwerte speichern &amp;quot;on&amp;quot;- oder &amp;quot;off&amp;quot;-Werte, diese müssten beispielsweise mit der Formel &amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;$fld[3]=~&amp;quot;on&amp;quot;?1.5:0&amp;lt;/syntaxhighlight&amp;gt; erst in Werte (on=1.5, off=0) gewandelt werden.&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
SVG Plot mit Plot Editor:&amp;lt;br&amp;gt;&lt;br /&gt;
[[Datei:SVG FileLog VitoConnect 1 HK1 Temps.png|800px]]&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
…  mit dem entsprechenden &amp;lt;b&amp;gt;Code&amp;lt;/b&amp;gt;:&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod SVG_FileLog_VitoConnect_1 SVG FileLog_VitoConnect:SVG_FileLog_VitoConnect_1:CURRENT&lt;br /&gt;
attr SVG_FileLog_VitoConnect_1 room Heizung&lt;br /&gt;
&lt;br /&gt;
setstate SVG_FileLog_VitoConnect_1 initialized&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;SVG_FileLog_VitoConnect_1.gplot&amp;lt;/b&amp;gt;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
# Created by FHEM/98_SVG.pm, 2019-11-04 20:35:38&lt;br /&gt;
set terminal png transparent size &amp;lt;SIZE&amp;gt; crop&lt;br /&gt;
set output &#039;&amp;lt;OUT&amp;gt;.png&#039;&lt;br /&gt;
set xdata time&lt;br /&gt;
set timefmt &amp;quot;%Y-%m-%d_%H:%M:%S&amp;quot;&lt;br /&gt;
set xlabel &amp;quot; &amp;quot;&lt;br /&gt;
set title &#039;HK1 Temps&#039;&lt;br /&gt;
set ytics &lt;br /&gt;
set y2tics &lt;br /&gt;
set grid&lt;br /&gt;
set ylabel &amp;quot;°C&amp;quot;&lt;br /&gt;
set y2label &amp;quot;boolean&amp;quot;&lt;br /&gt;
set y2range [0:4]&lt;br /&gt;
&lt;br /&gt;
#FileLog_VitoConnect 4:vitoconnect.Aussentemperatur\x3a::&lt;br /&gt;
#FileLog_VitoConnect 4:vitoconnect.HK1-Solltemperatur_normal\x3a::&lt;br /&gt;
#FileLog_VitoConnect 4:vitoconnect.HK1-Solltemperatur_reduziert\x3a::&lt;br /&gt;
#FileLog_VitoConnect 4:vitoconnect.HK1-Solltemperatur_aktiv\x3a::&lt;br /&gt;
#FileLog_VitoConnect 4:vitoconnect.HK1-Solltemperatur_reduziert_aktiv\x3a::&lt;br /&gt;
#FileLog_VitoConnect 4:vitoconnect.HK1-Vorlauftemperatur\x3a::&lt;br /&gt;
#FileLog_VitoConnect 4:vitoconnect.Kesseltemperatur_exact\x3a::&lt;br /&gt;
#FileLog_VitoConnect 4:vitoconnect.HK1-Zirkulationspumpe\x3a::$fld[3]=~&amp;quot;on&amp;quot;?1.5:0&lt;br /&gt;
&lt;br /&gt;
plot &amp;quot;&amp;lt;IN&amp;gt;&amp;quot; using 1:2 axes x1y1 title &#039;Außentemperatur&#039; ls l7 lw 1 with lines,\&lt;br /&gt;
     &amp;quot;&amp;lt;IN&amp;gt;&amp;quot; using 1:2 axes x1y1 title &#039;Soll normal&#039; ls l0 lw 1 with lines,\&lt;br /&gt;
     &amp;quot;&amp;lt;IN&amp;gt;&amp;quot; using 1:2 axes x1y1 title &#039;Soll reduziert&#039; ls l1 lw 1 with lines,\&lt;br /&gt;
     &amp;quot;&amp;lt;IN&amp;gt;&amp;quot; using 1:2 axes x1y2 title &#039;normal-prg&#039; ls l0fill lw 2 with lines,\&lt;br /&gt;
     &amp;quot;&amp;lt;IN&amp;gt;&amp;quot; using 1:2 axes x1y2 title &#039;reduziert-prg&#039; ls l1fill lw 2 with lines,\&lt;br /&gt;
     &amp;quot;&amp;lt;IN&amp;gt;&amp;quot; using 1:2 axes x1y1 title &#039;Vorlauf&#039; ls l2 lw 1 with lines,\&lt;br /&gt;
     &amp;quot;&amp;lt;IN&amp;gt;&amp;quot; using 1:2 axes x1y1 title &#039;Kesseltemp.&#039; ls l5 lw 1 with lines&lt;br /&gt;
     &amp;quot;&amp;lt;IN&amp;gt;&amp;quot; using 1:2 axes x1y2 title &#039;Zirk.pumpe&#039; ls l8fill lw 2 with lines,\&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== SVG Plot für Warmwasser =====&lt;br /&gt;
Im folgenden Beispiel werden hier verschiedene Graphen für Warmwasser (WW) genommen.&lt;br /&gt;
Einige der Logwerte speichern &amp;quot;on&amp;quot;- oder &amp;quot;off&amp;quot;-Werte, diese müssten beispielsweise mit der Formel &amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;$fld[3]=~&amp;quot;on&amp;quot;?1.5:0&amp;lt;/syntaxhighlight&amp;gt; erst in Werte (on=1.5, off=0) gewandelt werden.&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
SVG Plot mit Plot Editor:&amp;lt;br&amp;gt;&lt;br /&gt;
[[Datei:SVG FileLog VitoConnect 2 WW Temps.png|800px]]&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
…  mit dem entsprechenden &amp;lt;b&amp;gt;Code&amp;lt;/b&amp;gt;:&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod SVG_FileLog_VitoConnect_2 SVG FileLog_VitoConnect:SVG_FileLog_VitoConnect_2:CURRENT&lt;br /&gt;
attr SVG_FileLog_VitoConnect_2 room Heizung&lt;br /&gt;
&lt;br /&gt;
setstate SVG_FileLog_VitoConnect_2 initialized&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;SVG_FileLog_VitoConnect_1.gplot&amp;lt;/b&amp;gt;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
# Created by FHEM/98_SVG.pm, 2019-11-04 21:08:37&lt;br /&gt;
set terminal png transparent size &amp;lt;SIZE&amp;gt; crop&lt;br /&gt;
set output &#039;&amp;lt;OUT&amp;gt;.png&#039;&lt;br /&gt;
set xdata time&lt;br /&gt;
set timefmt &amp;quot;%Y-%m-%d_%H:%M:%S&amp;quot;&lt;br /&gt;
set xlabel &amp;quot; &amp;quot;&lt;br /&gt;
set title &#039;WW Temps&#039;&lt;br /&gt;
set ytics &lt;br /&gt;
set y2tics &lt;br /&gt;
set grid&lt;br /&gt;
set ylabel &amp;quot;°C&amp;quot;&lt;br /&gt;
set y2label &amp;quot;boolean&amp;quot;&lt;br /&gt;
set y2range [0:4]&lt;br /&gt;
&lt;br /&gt;
#FileLog_VitoConnect 4:vitoconnect.Brenner_aktiv\x3a::&lt;br /&gt;
#FileLog_VitoConnect 4:vitoconnect.WW-Solltemperatur\x3a::&lt;br /&gt;
#FileLog_VitoConnect 4:vitoconnect.WW-Isttemperatur\x3a::&lt;br /&gt;
#FileLog_VitoConnect 4:vitoconnect.WW-Sensoren_Auslauf_Wert\x3a::&lt;br /&gt;
#FileLog_VitoConnect 4:vitoconnect.Kesseltemperatur\x3a::&lt;br /&gt;
#FileLog_VitoConnect 4:vitoconnect.WW-Zirkulationspumpe_Status\x3a::$fld[3]=~&amp;quot;on&amp;quot;?1.5:0&lt;br /&gt;
&lt;br /&gt;
plot &amp;quot;&amp;lt;IN&amp;gt;&amp;quot; using 1:2 axes x1y2 title &#039;Brenner&#039; ls l0fill lw 2 with lines,\&lt;br /&gt;
     &amp;quot;&amp;lt;IN&amp;gt;&amp;quot; using 1:2 axes x1y1 title &#039;WW soll&#039; ls l1 lw 1 with lines,\&lt;br /&gt;
     &amp;quot;&amp;lt;IN&amp;gt;&amp;quot; using 1:2 axes x1y1 title &#039;WW ist&#039; ls l2 lw 1 with lines,\&lt;br /&gt;
     &amp;quot;&amp;lt;IN&amp;gt;&amp;quot; using 1:2 axes x1y1 title &#039;WW Auslaufwert&#039; ls l3 lw 1 with lines,\&lt;br /&gt;
     &amp;quot;&amp;lt;IN&amp;gt;&amp;quot; using 1:2 axes x1y1 title &#039;Kesseltemp.&#039; ls l4 lw 1 with lines,\&lt;br /&gt;
     &amp;quot;&amp;lt;IN&amp;gt;&amp;quot; using 1:2 axes x1y2 title &#039;Zirk.pumpe&#039; ls l8fill lw 2 with lines,\&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Weblinks ==&lt;br /&gt;
* {{Link2Forum|Topic=93664|LinkText=Thread zum Modul im Forum}}&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Heizungssteuerung]]&lt;/div&gt;</summary>
		<author><name>Ph1959de</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=Vorlage:EnOceanSubTypeTable&amp;diff=40785</id>
		<title>Vorlage:EnOceanSubTypeTable</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=Vorlage:EnOceanSubTypeTable&amp;diff=40785"/>
		<updated>2026-02-10T11:12:35Z</updated>

		<summary type="html">&lt;p&gt;Ph1959de: Schreibweise Micropelt iTRV korrigiert&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{|&lt;br /&gt;
{{News|&#039;&#039;&#039;subType&#039;&#039;&#039;        |&#039;&#039;&#039;Wiki-Seite&#039;&#039;&#039;}}&lt;br /&gt;
{{News|tempSensor.XX  | }}&lt;br /&gt;
{{News|roomSensorControl.01 | }}&lt;br /&gt;
{{News|tempHumiSensor.02 |[[EnOcean-FBH65TFB-Funk-Bewegungs- Helligkeits- Temperatur- und Feuchte-Sensor]] }}&lt;br /&gt;
{{News|lightSensor.XX |[[EnOcean-FAH60-Au%C3%9Fen-Helligkeitssensor]] }}&lt;br /&gt;
{{News|occupSensor.XX | }}&lt;br /&gt;
{{News|lightTempOccupSensor.XX |[[EnOcean-FBH65S-Funk-Bewegungs- Helligkeitssensor]] }}&lt;br /&gt;
{{News||[[EnOcean-FBH65TFB-Funk-Bewegungs- Helligkeits- Temperatur- und Feuchte-Sensor]] }}&lt;br /&gt;
{{News|COSensor.01 | }}&lt;br /&gt;
{{News|tempHumiCO2Sensor.XX | }}&lt;br /&gt;
{{News|vocSensor.01 | }}&lt;br /&gt;
{{News|radonSensor.01 | }}&lt;br /&gt;
{{News|particlesSensor.01 | }}&lt;br /&gt;
{{News|roomSensorControl.XX  | }}&lt;br /&gt;
{{News|tempCtrlState.01 | }}&lt;br /&gt;
{{News|shutterCtrlState.01 |[[EnOcean-D-452-FU-EBIM-JR-Aktor-Beschattungselemente-Rollladen]]}}&lt;br /&gt;
{{News|blindsCtrl.00 |[[EnOcean-UPJ230_12-Jalousieaktor]]}}&lt;br /&gt;
{{News|lightCtrlState.XX |[[EnOcean-D-452-FU-EBIM-Aktor-2fach]] }}&lt;br /&gt;
{{News|autoMeterReading.XX |[[EnOcean-FSR61VA-10A-Stromstoß-Schaltrelais mit Strommessung]] }}&lt;br /&gt;
{{News|environmentApp |[[EnOcean-FWS61-24V-DC-Funk-Wetterdaten-Sendemodul]] }}&lt;br /&gt;
{{News|multiFuncSensor |[[EnOcean-Roto-Com-Tec-Basic-Fenstersensor]]}}&lt;br /&gt;
{{News|hvac.XX |[[EnOcean-MD15-Kleinstellantrieb]] }}&lt;br /&gt;
{{News||[[Micropelt iTRV Kleinstellantrieb]] }}&lt;br /&gt;
{{News|digitalInput.XX | }}&lt;br /&gt;
{{News|gateway; gwCmd switching|[[EnOcean-FSR14-4x-RS485-Bus-Schaltaktor-4-Kanal-Stromstoß-Schaltrelais]] }}&lt;br /&gt;
{{News|gateway; gwCmd dimming|[[EnOcean-FUD12NPN-RS485-Bus-Universal-Dimmaktor]] }}&lt;br /&gt;
{{News||[[EnOcean-FUD14-RS485-Bus-Universal-Dimmaktor]] }}&lt;br /&gt;
{{News||[[EnOcean-FUD61NPN-Funk-Universal-Dimmaktor]] }}&lt;br /&gt;
{{News||[[EnOcean-FUD61NPN-Funk-Universal-Dimmaktor-unidirektional]] }}&lt;br /&gt;
{{News||{{Link2Forum|Topic=58832|LinkText=Osram Touch Dim RC}} }}&lt;br /&gt;
{{News|gateway; gwCmd blindCmd|[[EnOcean-D-452-FU-EBIM-JR-Aktor-Beschattungselemente-Rollladen]] }}&lt;br /&gt;
{{News||[[EnOcean-D-452-FU-EBI-JR-Aktor-Beschattungselemente-Rollladen]] }}&lt;br /&gt;
{{News|manufProfile |[[EnOcean-FSB12-RS485-Bus-Schaltaktor-2-Kanal-Beschattungselemente-Rollladen]] }}&lt;br /&gt;
{{News||[[EnOcean-FSB14-RS485-Bus-Schaltaktor-2-Kanal-Beschattungselemente-Rollladen]]}}&lt;br /&gt;
{{News||[[EnOcean-FSB61-Aktor-Beschattungselemente-Rollladen]]}}&lt;br /&gt;
{{News|actuator.01 |[[EnOcean-D-452-FU-EBIM-Aktor-2fach]] }}&lt;br /&gt;
{{News||[[EnOcean-PSC234-Zwischenstecker]] }}&lt;br /&gt;
{{News|switch.XX |[[EnOcean-PTM-210-Taster]] }}&lt;br /&gt;
{{News||[[EnOcean-FBH65TFB-Funk-Bewegungs- Helligkeits- Temperatur- und Feuchte-Sensor]]}}&lt;br /&gt;
{{News||[[EnOcean-FSR12-4x-RS485-Bus-Schaltaktor-4-Kanal-Stromstoß-Schaltrelais]]}}&lt;br /&gt;
{{News||[[EnOcean-FMS61NP-2-Kanal-Multifunktions-Stromstoßschalter]]}}&lt;br /&gt;
{{News||[[EnOcean-FSR61VA-10A-Stromstoß-Schaltrelais mit Strommessung]]}}&lt;br /&gt;
{{News||[[EnOcean-SecuSignal-Fenstergriff]]}}&lt;br /&gt;
{{News|roomCtrlPanel.00 | }}&lt;br /&gt;
{{News|contact |[[EnOcean_Starter_Guide#Kontakte]] }}&lt;br /&gt;
{{News||[[EnOcean-STM-250-Fenster-Türkontakt]]}}&lt;br /&gt;
{{News|keycard.xx | }}&lt;br /&gt;
{{News|windowHandle.XX |[[EnOcean-SecuSignal-Fenstergriff]]}}&lt;br /&gt;
{{News|sensor | }}&lt;br /&gt;
{{News|smokeDetector.02 |[[EnOcean-FRW-Rauchmelder]]}}&lt;br /&gt;
{{News|PM101|[[EnOcean_Starter_Guide#Profilloses_4BS-Teach-In]] }}&lt;br /&gt;
{{News|raw | }}&lt;br /&gt;
{{News|doorContact|[[EiMSIG Universalsensor mit EnOcean]] }}&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;Die Endung .XX bei einem subType bedeutet, dass es mehrere subType-Untervarianten gibt, die sich aber nur in Kleinigkeiten -zumeist Skalierung- unterscheiden.&amp;lt;/code&amp;gt;&lt;br /&gt;
&amp;lt;noinclude&amp;gt;[[Kategorie:Vorlage]]&amp;lt;/noinclude&amp;gt;&lt;/div&gt;</summary>
		<author><name>Ph1959de</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=Micropelt_iTRV_Kleinstellantrieb&amp;diff=40784</id>
		<title>Micropelt iTRV Kleinstellantrieb</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=Micropelt_iTRV_Kleinstellantrieb&amp;diff=40784"/>
		<updated>2026-02-10T11:10:28Z</updated>

		<summary type="html">&lt;p&gt;Ph1959de: Ph1959de verschob die Seite Micropelt iRTV Kleinstellantrieb nach Micropelt iTRV Kleinstellantrieb, ohne dabei eine Weiterleitung anzulegen: Falsch geschriebener Name&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Infobox Hardware&lt;br /&gt;
| Bild = ITRV.jpg&lt;br /&gt;
| Bildbeschreibung = Batterieloser Heizungssteller&lt;br /&gt;
| HWProtocol = EnOcean&lt;br /&gt;
| HWType = Aktor&lt;br /&gt;
| HWCategory = EnOcean&lt;br /&gt;
| HWComm = EnOcean Funk, 868Mhz&lt;br /&gt;
| HWProtokoll = EnOcean Protokoll EEP A5-20-01 (Ventil Position in %)&lt;br /&gt;
| HWChannels = 1 (bidirektional)&lt;br /&gt;
| HWVoltage = 3.2 V, LiFePo-Akku&lt;br /&gt;
| HWPowerConsumption = unbekannt&lt;br /&gt;
| HWPoweredBy = TEG (Thermoelektrischer Generator;autark)&lt;br /&gt;
| HWSize = 59 × 64 × 59 [mm] (Breite × Höhe × Tiefe), Gewicht 260 g&lt;br /&gt;
neuere, runde Modelle: 55 x 95 [mm] (Ø x T), 260 g&lt;br /&gt;
| HWDeviceFHEM = [http://fhem.de/commandref.html#EnOcean EnOcean]&lt;br /&gt;
| HWManufacturer = Micropelt GmbH&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;EnOcean-Micropelt iTRV-Kleinstellantrieb&#039;&#039;&#039; ist ein batterieloser Kleinstellantrieb für Raumtemperaturregelung, der seine Energie per Energy Harvesting aus der Wärme des Vorlaufes zieht. Hier wird vorrangig das Modell MVA-002 beschrieben. &lt;br /&gt;
Das Modell MVA-004 ist rund und hat einen eigenen PID-Controller, das Modell MVA-005 (ebenfalls rund) hat zusätzlich eine Handverstellung in Ein-Grad-Schritten (bis +/- 5°). Beide Modelle sind noch leiser und sollen laut Hersteller bei schwacher Funkversorgung die Verbindung besser halten. Das Modell MVA-005 hat das EEP A5-20-06, das seit 21.1.2019 von FHEM unterstützt wird.&lt;br /&gt;
&lt;br /&gt;
Aktuell in der Auslieferung sind die Modelle MVA-008 (wie 004) und MVA-009 (wie 005) mit einer USB-Buchse zum Nachladen, falls der eingebaute Speicher leergelaufen sein sollte. &lt;br /&gt;
&lt;br /&gt;
Neben der hier beschriebenen EnOcean-Variante gibt es auch Modelle für LoRaWan.&lt;br /&gt;
&lt;br /&gt;
== Features ==&lt;br /&gt;
Kleinstellantrieb, der ohne Batterien auskommt, da er über einen thermolektrischen Generator seine Energie aus der Temperaturdifferenz des Heizungsvorlaufes und der Raumtemperatur bezieht.&lt;br /&gt;
&lt;br /&gt;
== Hinweise zum Betrieb mit FHEM ==&lt;br /&gt;
&lt;br /&gt;
=== Definition/Anlernvorgang ===&lt;br /&gt;
4BS-Bidirektionales-Teach-In:&lt;br /&gt;
&lt;br /&gt;
#falls vorhanden, alle bisherigen FHEM Devices des Aktors löschen und nach Speichern der geänderten Konfiguration FHEM neu starten&lt;br /&gt;
#FHEM in Lernmodus versetzen: &amp;lt;code&amp;gt;&amp;lt;nowiki&amp;gt;set &amp;lt;IODev&amp;gt; teach &amp;lt;time [s]&amp;gt;&amp;lt;/nowiki&amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
#Taster am iTRV zweimal drücken (Montageposition = 0%). Der erfolgreiche Anlernvorgang wird durch einmaliges Aufleuchten der Status-LED quittiert. (Fehlanzeige: 6mal Blinken)&lt;br /&gt;
#Nach Montage Taster einmal drücken - das Ventil fährt die von FHEM gewünschte Position an.&lt;br /&gt;
#Aktor-Device wird in FHEM automatisch mit allen notwendigen Parametern angelegt.&lt;br /&gt;
&lt;br /&gt;
Standardmäßig nutzt FHEM den eigenen PID-Regler des FHEM-Moduls, der auf die Eingabe einer Temperaturvorgabe wartet:&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;lt;nowiki&amp;gt;set &amp;lt;name&amp;gt; desired-temp &amp;lt;wert°C&amp;gt;&amp;lt;/nowiki&amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Solange kein Referenz-Device mit &amp;lt;code&amp;gt;&amp;lt;nowiki&amp;gt;set &amp;lt;name&amp;gt; temperatureRefDev &amp;lt;Temperaturdevice mit einem Reading temperature&amp;gt;&amp;lt;/nowiki&amp;gt;&amp;lt;/code&amp;gt; angegeben wurde, benutzt das Gerät einen internen Temperaturfühler, der bauartbedingt nicht (MVA-005:bedingt) geeignet für eine Raumregelung ist.&lt;br /&gt;
&lt;br /&gt;
Zur Sicherheit des Heizkörperventils gegen Festsetzen durch Rost oder Kalk, wird im Sommermodus pro Tag eine Servicefahrt – einmal auf und zu – des Ventils durchgeführt. Zusätzlich wird dreimal am Tag die Verbindung mit FHEM aufgerufen. Das dient zur Kontrolle, dass alle bislang verbundenen Devices noch vorhanden sind und ordnungsgemäß miteinander kommunizieren.&lt;br /&gt;
&lt;br /&gt;
Wenn mehr als dreimal hintereinander die Funkkommunikation mit FHEM fehlschlug (was mehr als 30 Minuten im Normalbetrieb bedeutet), sendet der Aktor nur noch alle Stunde und - falls das Ventil zu weniger als 50 Prozent geöffent ist, wird es auf 50% aufgefahren bzw. bei MVA-004 und MVA-005 ff. im internen PID-Betrieb auf 21° gesteuert.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Durch einmaliges Drücken des Tasters&#039;&#039;&#039; kann man im Alltagsbetrieb eine sofortige Funkkommunikation auslösen.&lt;br /&gt;
&lt;br /&gt;
{{Hinweis|Zum Energiesparen verarbeitet der Aktor nur alle 586 oder 587 Sekunden (Sommermodus: alle 8 Stunden) Telegramme von FHEM. Darum zeigen FHEM-Befehle keine sofortige Aktorreaktion, sondern erst nach der genannten Zeitspanne. Die Rückmeldung des Aktors an FHEM über die erfolgte Befehlsausführung erfolgt wiederum erst nach erneutem Ablauf der Zeitspanne.&lt;br /&gt;
Falls keine Temperaturdaten geliefert werden, geht das Device in einen Stromsparmodus und fragt nur noch alle 2 Stunden an.&lt;br /&gt;
Zur Vermeidung dieses Stromsparmodus, in dem eben auch nicht nachgeregelt wird, sollte bei Referenzgeräte wie einem  Raumthermometer das attr event-on-update &#039;&#039;&#039;nicht&#039;&#039;&#039; benutzt werden. Falls die Temperatur sich 1 Stunde lang nicht ändert, würde entsprechend auch eine Stunde lang kein Referenzwert gesendet, der Stelltrieb in den Sparmodus gehen und für die nächsten 2 Stunden nun kommende Änderungen nicht verarbeiten.&lt;br /&gt;
In der Praxis mit einem TCM 310 zeigt sich, dass bei einem RSSI&amp;lt;-80 die Funkkommunikation nicht ausreichend stabil ist. Wenn dreimal hintereinander keine Funktbestätigung erhalten wurde, geht der Aktor in den Notbetrieb und öffnet das Ventil auf 50% oder lässt es auf einem gesetzten größeren Wert bis zur nächsten erfolgreichen bisdirektionalen Kommunikation offen. Das Anpassen der Antennenausrichtung ist hier sehr wichtig.}}&lt;br /&gt;
&lt;br /&gt;
=== Settings ===&lt;br /&gt;
{| class=&amp;quot;wikitable sortable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! set !! Wertebereich/Default !! Beschreibung&lt;br /&gt;
|-&lt;br /&gt;
|desired-temp||10...40||Soll-Temperatur für den FHEM-PID-Controler: der PID-Controler des FHEM-Moduls wird &#039;&#039;&#039;ein&#039;&#039;&#039;geschaltet&lt;br /&gt;
|-&lt;br /&gt;
|liftSet||||??&lt;br /&gt;
|-&lt;br /&gt;
|runInit||||beim nächsten Funkkontakt wird ein Kalibrierungslauf angestoßen&lt;br /&gt;
|-&lt;br /&gt;
|setpoint||0...100||Setze Aktorposition auf X%; der PID-Controler des FHEM-Moduls wird &#039;&#039;&#039;ein&#039;&#039;&#039;geschaltet, wenn das attr pidCtrl on ist&lt;br /&gt;
|-&lt;br /&gt;
|setpointTemp||10...40||Soll-Temperatur für den FHEM-PID-Controler, der PID-Controler des FHEM-Moduls wird &#039;&#039;&#039;ein&#039;&#039;&#039;geschaltet; ein runInit wird ausgelöst wenn das attr pidCtrl on ist&lt;br /&gt;
|-&lt;br /&gt;
|ValveCloses||||Ventil auf 0% setzen; der PID-Controler des FHEM-Moduls wird &#039;&#039;&#039;aus&#039;&#039;&#039;geschaltet&lt;br /&gt;
|-&lt;br /&gt;
|ValveOpens||||Ventil auf 100% setzen; der PID-Controler des FHEM-Moduls wird &#039;&#039;&#039;aus&#039;&#039;&#039;geschaltet&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Readings ===&lt;br /&gt;
{| class=&amp;quot;wikitable sortable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Reading !! Beispiel !! Wertebereich/Default !! Beschreibung&lt;br /&gt;
|-&lt;br /&gt;
|actuatorState||ok||||wenn &#039;obstructed&#039;, dann ist der metallene Kühlring des Gehäuses z.b. von einem Vorhang bedeckt und der Thermogenerator kann nicht richtig arbeiten&lt;br /&gt;
|-&lt;br /&gt;
|alarm||no_response_from_actuator||||nur fallweise: wenn Kommunikation ausgefallen; Aktor blinkt dreimal&lt;br /&gt;
|-&lt;br /&gt;
|battery||ok ||&#039;&#039;&#039;ok&#039;&#039;&#039; / low||&lt;br /&gt;
|-&lt;br /&gt;
|cover||closed||||wird vom MVA-002 nicht gesendet&lt;br /&gt;
|-&lt;br /&gt;
|delta||-9.75||||&lt;br /&gt;
|-&lt;br /&gt;
|feedTemp&lt;br /&gt;
|32.5&lt;br /&gt;
|&lt;br /&gt;
|Die Modelle ab 004 haben einen zweiten Thermofühler, der verlässlich die Vorlauftemperatur am Ventilsitz misst und zur Steuerung des eingebauten PID-Controlers herangezogen wird.&lt;br /&gt;
|-&lt;br /&gt;
|energyInput||enabled||&#039;&#039;&#039;enabled&#039;&#039;&#039; / disabled||enabled zeigt ausreichende Temperaturdifferenz zur Speisung des Akkus an&lt;br /&gt;
|-&lt;br /&gt;
|energyStorage||charged||&#039;&#039;&#039;charged&#039;&#039;&#039; / empty ||&lt;br /&gt;
|-&lt;br /&gt;
|maintenanceMode||off||&#039;&#039;&#039;off&#039;&#039;&#039; / runinit||&lt;br /&gt;
|-&lt;br /&gt;
|operationMode||setpointTemp||&#039;&#039;&#039;setpointTemp&#039;&#039;&#039; / summerMode / setpointSet||&#039;&#039;&#039;Betrieb mit Stellwert-Vorgabe&#039;&#039;&#039; / im Sommer-Modus / mit Temperatursollwert-Vorgabe &lt;br /&gt;
|-&lt;br /&gt;
|operationModeRestore||setpointTemp||||nur fallweise: Modus, der beim Beenden des summermode gesetzt wird&lt;br /&gt;
|-&lt;br /&gt;
|pidAlarm||||actuator_in_errorPos / dead_sensor / no_temperature_value / setpoint_device_missing||&lt;br /&gt;
|-&lt;br /&gt;
|pidState|||| processing |alarm / idle / &#039;&#039;&#039;processing&#039;&#039;&#039; / start / stop||&lt;br /&gt;
|-&lt;br /&gt;
|p_d||0||||&lt;br /&gt;
|-&lt;br /&gt;
|p_i||100||||&lt;br /&gt;
|-&lt;br /&gt;
|p_p||-487.5||||&lt;br /&gt;
|-&lt;br /&gt;
|roomTemp||22.0||||interne Temperatur: wenn attr setpointRefDev nicht gesetzt wird diese Temperatur benutzt - ist für Regelung nicht brauchbar wegen Verfälschung durch Vorlauftemperatur, höhere Temperaturen sind Indiz für Wärmeangebot vom Vorlauf, das den Energiespeicher lädt&lt;br /&gt;
|-&lt;br /&gt;
|selfCtrl||off||||MVA-002 hat keinen eigenen PID-Controler&lt;br /&gt;
|-&lt;br /&gt;
|setpoint||0||||beim letzten Funkkontakt gemeldete Ist-Position vom Aktuator in %&lt;br /&gt;
|-&lt;br /&gt;
|setpointSet||15||||beim nächsten Funkkontakt gesendete Aktuatorstellung in %&lt;br /&gt;
|-&lt;br /&gt;
|setpointTemp||15.0||||Soll-Temperatur. Kann gesetzt werden über &amp;lt;code&amp;gt;&amp;lt;nowiki&amp;gt;set &amp;lt;IODev&amp;gt; desired-temp &amp;lt;°C&amp;gt;&amp;lt;/nowiki&amp;gt;&amp;lt;/code&amp;gt; oder auch durch Module wie Heating_Control oder WeekdayTimer&lt;br /&gt;
|-&lt;br /&gt;
|setpointTempRestore||16.0 ||||nur fallweise: setpointTemp, die bei Beenden des summermode gesetzt wird&lt;br /&gt;
|-&lt;br /&gt;
|state||T: 23.4 SPT: 15.0 SP: 0||||T:=Raumtemperatur vom Referenzsensor, SPT=setpointTemp, SP=setpoint&lt;br /&gt;
|-&lt;br /&gt;
|teach||4BS teach-in accepted EEP A5-20-01 Manufacturer: Micropelt GmbH||||Modelle &amp;gt;004: A5-20-06&lt;br /&gt;
|-&lt;br /&gt;
|temperature||24.75||||zuletzt gemeldete Raumtemperatur vom Referenz-Sensor (siehe Attribut temperatureRefDev)&lt;br /&gt;
|-&lt;br /&gt;
|waitingCmds||runInit|||noch nicht gesendetes Kommando, hier Kalibrierungslauf|&lt;br /&gt;
|-&lt;br /&gt;
|wakeUpCycle||240|||das Gerät meldet sich alle 4 Minuten|&lt;br /&gt;
|-&lt;br /&gt;
|window||closed||open / &#039;&#039;&#039;closed&#039;&#039;&#039;||wird vom MVA-002 nicht gesendet&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Attribute ===&lt;br /&gt;
{| class=&amp;quot;wikitable sortable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Attribut !! Beispiel !! Wertebereich/Default !! Beschreibung&lt;br /&gt;
|-&lt;br /&gt;
|actualTemp&lt;br /&gt;
|biDir&lt;br /&gt;
|t/°C&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|comMode||biDir||||&lt;br /&gt;
|-&lt;br /&gt;
|destinationID||unicast||||&lt;br /&gt;
|-&lt;br /&gt;
|eep||A5-20-01||||MVA-002 bis MVA-004; beim MVA-005 A5-20-06&lt;br /&gt;
|-&lt;br /&gt;
|manufID||049||||&lt;br /&gt;
|-&lt;br /&gt;
|pidActorErrorPos||20||||MVA-002 ignoriert diesen Wert und benutzt &#039;&#039;&#039;50%&#039;&#039;&#039; oder den zuvor gesendeten höheren Stellwert&lt;br /&gt;
|-&lt;br /&gt;
|pidActorLimitLower||1||||bei 0% Öffnung wird eine Initialisierung ausgelöst?&lt;br /&gt;
|-&lt;br /&gt;
|pidCtrl||on||||bei on wird der FHEM Pid-Controler benutzt, bei off der im Actor (Modell MVA-002 hat keinen internen PID-Controler, wohl aber MVA-004 und MVA-005)&lt;br /&gt;
|-&lt;br /&gt;
|pidFactor_I||0.2||||&lt;br /&gt;
|-&lt;br /&gt;
|pidFactor_P||50||||&lt;br /&gt;
|-&lt;br /&gt;
|pidSensorTimeout||3600|||| Zahl der Sekunden, in denen der Sensor auf einen Wert vom temperatureRefDev auf einen Wert wartet, sonst Alarm&lt;br /&gt;
|-&lt;br /&gt;
|pidDeltaTreshold&lt;br /&gt;
|2&lt;br /&gt;
|1&lt;br /&gt;
|Minimale Veränderung der Aktorstellung in Prozent&lt;br /&gt;
|-&lt;br /&gt;
|rcvRespAction||||||Hier kann eine Aktion eingetragen werden, die bei Rückmeldung des Aktors durchgeführt wird. Details in der FHEM-Referenz&lt;br /&gt;
|-&lt;br /&gt;
|setpointRefDev||||||Das Device, das die Ventilöffnung (Aktuator) vorgibt, z.B. ein Raumregler, FHEM-PID20. Typischerweise &#039;&#039;&#039;nicht gesetzt, es wird der modul-eigene PID-Controler benutzt&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
|setpointSummerMode||2||||Ventilöffnung, die im Sommer gelten soll&lt;br /&gt;
|-&lt;br /&gt;
|setpointTempRefDev||CC.Kueche||||Eintragen, wenn ein Device, die Solltemperatur vorgibt, z.B. Heating_Control oder ein physischer Raumthermostat, der in FHEM ausgelesen wird.&lt;br /&gt;
|-&lt;br /&gt;
|subDef||********||||Sender-ID&lt;br /&gt;
|-&lt;br /&gt;
|subType||hvac.01||||bei MVA-002 bis MVA-004: hvac.01; ab MVA-005: hvac.06&lt;br /&gt;
|-&lt;br /&gt;
|summerMode||off||||&lt;br /&gt;
|-&lt;br /&gt;
|teachMethod||4BS||||&lt;br /&gt;
|-&lt;br /&gt;
|temperatureRefDev||T.Kueche||||Das Device, das mindestens einmal pro Stunde ein Event mit Namen temperature (aktuelle Raumtemperatur) meldet&lt;br /&gt;
|-&lt;br /&gt;
|webCmd||setpointTemp||||&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Tipps &amp;amp; Tricks ==&lt;br /&gt;
Fehlermeldungen wie Sensor &#039;&#039;&#039;dead&#039;&#039;&#039; oder &#039;&#039;&#039;no Response from actuator&#039;&#039;&#039; kann man im Betrieb mit einem externen Sensor verhindern, indem man mindestens alle 5 Minuten einen Temperaturwert sendet (gegebenenfalls mit attr event-min-interval im sendenden Temperatur-Device einstellen). Alternativ kann man auch über ein DOIF alle 24 Stunden den Wunschwert mit &amp;lt;Code&amp;gt;set &amp;lt;Devicename&amp;gt; desired-temp 20&amp;lt;/Code&amp;gt; wiederholen, wobei 20 beispielhaft eine Wunschtemperatur ist.&lt;br /&gt;
&lt;br /&gt;
Für eine stabile Funkkommunikation ist ein RSSI von besser als -80 anzustreben. Eine Verbesserungsmöglichkeit bietet das versuchsweise Drehen des Micropelt am Heizkörper um 90°. Normalerweise ist der geprägte Schriftzug Micropelt waagerecht wie auch die innen liegende Antenne. Nach der Drehung steht diese dann aufrecht. Das passt in der Praxis dann manchmal besser zu einer waagerecht angeordneten Senderantenne, deren Leistung sich dann gezielter im Haus verbreitet. Ein RSSI von -70 ist besser als -80.&lt;br /&gt;
Die Empfangsqualität aller EnOcean-Geräte lässt sich schnell mit &amp;lt;Code&amp;gt;list .* TCM_ESP3_0_RSSI&amp;lt;/Code&amp;gt; abfragen, wobei TCM_ESP3_0 der Name des EnOcean-Gateways ist.&lt;br /&gt;
&lt;br /&gt;
Die neueren Modelle beginnen nach einer sehr langen Sommerpause bei leerem Akku schon nach wenigen Minuten mit Vorlauftemperaturen um 30 Grad wieder ihren Betrieb alleine mit dem Strom, den der kleine Thermo-Generator erzeugt.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== ToDos ==&lt;br /&gt;
Optimierungstipps, um möglichst wenig Energie im Aktor zu verbrauchen: Sommerbetrieb und Filtern von Sollwert-Verstellungen.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
event-on-NN beim Sensor, wakeupCycle&lt;br /&gt;
&lt;br /&gt;
Sorgt Setpoint 0 für eine Initialisierung?&lt;br /&gt;
&lt;br /&gt;
Optimierung von p_i,p_d,p_p in der Praxis&lt;br /&gt;
&lt;br /&gt;
Bedeutung von set &amp;lt;name&amp;gt; liftSet&lt;br /&gt;
&lt;br /&gt;
Abgrenztung ActualTemp gegenüber temperatureRefDev&lt;br /&gt;
&lt;br /&gt;
== Links ==&lt;br /&gt;
* {{Link2Forum|Topic=34314|Message=290203|LinkText=Forenbeitrag}}&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:EnOcean Components]]&lt;br /&gt;
[[Kategorie:Heizungsventile]]&lt;/div&gt;</summary>
		<author><name>Ph1959de</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=Apache_Authentication_Proxy&amp;diff=40779</id>
		<title>Apache Authentication Proxy</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=Apache_Authentication_Proxy&amp;diff=40779"/>
		<updated>2026-01-31T11:06:01Z</updated>

		<summary type="html">&lt;p&gt;Ph1959de: &amp;quot;hr&amp;quot; eingefügt&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Randnotiz|RNText=Anstatt die unten angegeben einfache BasicAuth zu nutzen kann folgende Erweiterung für eine browserbasierte passwortlose Authentifizierung per QR-Code (Magic-Link) oder Login mit persistentem Cookie verwendet werden. Falls ein gültiges Authentifizierungs-cookie vorliegt wird man transparent zur Applikation weitergeleitet. Andernfalls muss man sich einmal anmelden um das Cookie zu setzen. Anschließend wird man zur Applikation weitergeleitet.&lt;br /&gt;
Voraussetzungen ist der Betrieb und Konfiguration eines eigenen Apache2 Webservers in dieser Anleitung. Das AddOn funktioniert u.a. in iOs und dem Passwortmanager und ist [https://github.com/tobiasfaust/SimpleWebAuth hier dokumentiert]&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
Eine sicherere Variante eines Apache Authentication Proxy zusätzlich mit Zertifikaten ist [https://gist.github.com/gbirke/8608543 hier dokumentiert]}}&lt;br /&gt;
== Konfiguration ==&lt;br /&gt;
Um den Zugriff auf [[FHEMWEB]] etwas sicherer zu machen, kann man den Webzugriff über einen [https://httpd.apache.org/ Apache Webserver] laufen lassen. Dies ist ein kurzes Rezept, um Zugriffe auf FHEMWEB über einen Apachen authentifizieren zu lassen. Erstellt wurde es auf [https://www.debian.org Debian Squeeze]], sollte aber auch mit [https://www.ubuntu.com/ Ubuntu] funktionieren.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
apt-get install apache2 libapache2-mod-proxy-html&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Step 1) FHEMWEB sollte nur noch auf Anfragen vom selben Rechner lauschen, also kein &#039;global&#039; Attribut in der Definition in fhem.cfg&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
define WEBS FHEMWEB 8084&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Step 2) die folgenden Apache2 Module müssen aktiviert sein: [https://httpd.apache.org/docs/current/mod/mod_proxy.html mod_proxy] + [http://httpd.apache.org/docs/2.4/mod/mod_proxy_http.html mod_proxy_http]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ini&amp;quot;&amp;gt;&lt;br /&gt;
a2enmod proxy&lt;br /&gt;
a2enmod proxy_http&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Step 3) neue Datei &#039;&#039;&#039;&#039;&#039;/etc/apache2/conf.d/fhem&#039;&#039;&#039;&#039;&#039; anlegen:&lt;br /&gt;
&lt;br /&gt;
Diese Konfiguration sorgt dafür, dass alle Anfragen unter /fhem weiter nach &#039;&#039;&#039;http://localhost/fhem&#039;&#039;&#039; geleitet werden. Zusätzlich wird eine Basic-Authentifizierung eingeschaltet. Die Benutzerdatenbank ist dann in /etc/fhem-htpasswd zu finden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ini&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;Location /fhem&amp;gt;&lt;br /&gt;
  # ProxyPass/ProxyPassReverse leitet HTTP requests auf eine andere URL um&lt;br /&gt;
  ProxyPass http://localhost:8084/fhem&lt;br /&gt;
  ProxyPassReverse http://localhost:8084/fhem&lt;br /&gt;
  ProxyHTMLEnable On&lt;br /&gt;
  # ProxyHTMLURLMap passt Links im HTML/JavaScript Source an&lt;br /&gt;
  ProxyHTMLURLMap /        /fhem/&lt;br /&gt;
  ProxyHTMLURLMap /fhem/     /fhem/&lt;br /&gt;
  AuthType Basic&lt;br /&gt;
  AuthName &amp;amp;quot;Password Required&amp;amp;quot;&lt;br /&gt;
  AuthUserFile /etc/fhem-htpasswd&lt;br /&gt;
  Require valid-user&lt;br /&gt;
  Order deny,allow&lt;br /&gt;
  Allow from all&lt;br /&gt;
&amp;lt;/Location&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Step 4) Benutzer-Datenbank in &#039;&#039;&#039;&#039;&#039;/etc/fhem-htpasswd&#039;&#039;&#039;&#039;&#039; anlegen&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
# -c -&amp;gt; create file&lt;br /&gt;
# -s SHA encryption&lt;br /&gt;
htpasswd -c -s /etc/fhem-htpasswd &amp;lt;username&amp;gt;&lt;br /&gt;
# add more users with&lt;br /&gt;
htpasswd -s /etc/fhem-htpasswd &amp;lt;username&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Step 5) Apache neu starten&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;invoke-rc.d apache2 reload&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
bzw.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;service apache2 reload&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Fertig. FHEM ist jetzt über &#039;&#039;&#039;&#039;&#039;http://server/fhem&#039;&#039;&#039;&#039;&#039; erreichbar. Alle Zugriffe müssen aber erst mit Benutzername + Passwort freigeschaltet werden.&lt;br /&gt;
&lt;br /&gt;
=== SSL-Konfiguration ===&lt;br /&gt;
&lt;br /&gt;
Um nicht die Passwörter unverschlüsselt übers Netz zu schicken, ist die Konfiguration eines SSL-Reverse-Proxys noch empfehlenswerter. Die untenstehende Konfiguration zeigt die Verwendung mittels Zertifikaten, die von [https://letsencrypt.org Let&#039;s Encrypt] ausgestellt wurden. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ini&amp;quot;&amp;gt;&lt;br /&gt;
	&amp;lt;VirtualHost *:80&amp;gt;&lt;br /&gt;
  	   ServerName           $host&lt;br /&gt;
  	   RedirectPermanent    / https://$host/&lt;br /&gt;
	&amp;lt;/VirtualHost&amp;gt;&lt;br /&gt;
&lt;br /&gt;
	&amp;lt;VirtualHost *:443&amp;gt;&lt;br /&gt;
	    ServerName          $host&lt;br /&gt;
&lt;br /&gt;
	    SSLEngine           on&lt;br /&gt;
	    SSLProtocol         all -SSLv2 -SSLv3&lt;br /&gt;
	    SSLCipherSuite      ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:AES:CAMELLIA:DES-CBC3-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!aECDH:!EDH-DSS-DES-CBC3-SHA:!EDH-RSA-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA&lt;br /&gt;
	    SSLHonorCipherOrder on&lt;br /&gt;
	    SSLCompression      off&lt;br /&gt;
	    SSLOptions          +StrictRequire&lt;br /&gt;
	&lt;br /&gt;
	    SSLCertificateFile  /etc/letsencrypt/live/$host/fullchain.pem&lt;br /&gt;
	    SSLCertificateKeyFile /etc/letsencrypt/live/$host/privkey.pem&lt;br /&gt;
&lt;br /&gt;
	    Use RootDir $dir  # hier sollte ein Verzeichnis unter /var/www stehen (kann leer sein, muss aber existieren)&lt;br /&gt;
	&lt;br /&gt;
            ProxyPass /.well-known !&lt;br /&gt;
            Alias /.well-known &amp;quot;/var/www/proxy/.well-known&amp;quot;&lt;br /&gt;
            &amp;lt;Directory &amp;quot;$dir/.well-known&amp;quot;&amp;gt;&lt;br /&gt;
               order allow,deny&lt;br /&gt;
               allow from all&lt;br /&gt;
               AllowOverride All&lt;br /&gt;
               AddDefaultCharset Off&lt;br /&gt;
            &amp;lt;/Directory&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
	    ProxyRequests       Off&lt;br /&gt;
	    ProxyVia 		Off&lt;br /&gt;
	    ProxyPreserveHost   On&lt;br /&gt;
	    ProxyPass 		/fhem http://$fhemhost:$port/fhem&lt;br /&gt;
	    ProxyPassReverse 	/fhem http://$fhemhost:$port/fhem&lt;br /&gt;
&lt;br /&gt;
   	    Header always set Strict-Transport-Security &amp;quot;max-age=31536000&amp;quot;&lt;br /&gt;
&lt;br /&gt;
	    &amp;lt;Proxy *&amp;gt;&lt;br /&gt;
		Order deny,allow&lt;br /&gt;
		Allow from all&lt;br /&gt;
                AuthType Basic&lt;br /&gt;
                AuthName &amp;amp;quot;Password Required&amp;amp;quot;&lt;br /&gt;
                AuthUserFile /etc/fhem-htpasswd&lt;br /&gt;
                Require valid-user&lt;br /&gt;
	    &amp;lt;/Proxy&amp;gt;&lt;br /&gt;
	&amp;lt;/VirtualHost&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Proxy-Konfiguration ist so gewählt, dass auch automatische Zertifikats-Aktualisierung per webroot klappt (&amp;lt;code&amp;gt;letsencrypt certonly --renew-by-default --webroot -w $dir -d $host&amp;lt;/code&amp;gt;). Initial muss man natürlich ohne Zertifikat einen normalen non-SSL-VHost aufmachen.&lt;br /&gt;
&lt;br /&gt;
== Websockets-Unterstützung ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ini&amp;quot;&amp;gt;&lt;br /&gt;
			RewriteEngine On&lt;br /&gt;
			RewriteCond %{HTTP:Upgrade} =websocket [NC]&lt;br /&gt;
			RewriteRule /fhem(.*)           ws://$url$1 [P,L]&lt;br /&gt;
			RewriteCond %{HTTP:Upgrade} !=websocket [NC]&lt;br /&gt;
			RewriteRule /fhem(.*)           http://$url$1 [P,L]&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Hinweis zu Apache 2.4 ==&lt;br /&gt;
Es muss zusätzlich das Modul &#039;&#039;&#039;proxy_html&#039;&#039;&#039; aktiviert werden (&amp;lt;code&amp;gt;a2enmod proxy_html&amp;lt;/code&amp;gt;).&lt;br /&gt;
Und die zu erstellende Config-Datei muss jetzt nach &#039;&#039;/etc/apache2/conf-available/fhem.conf&#039;&#039; (statt /etc/apache2/conf.d/fhem).&lt;br /&gt;
&lt;br /&gt;
== Mögliche Probleme ==&lt;br /&gt;
&lt;br /&gt;
=== Invalid command &#039;ProxyHTMLURLMap&#039; ===&lt;br /&gt;
Falls nach dem Neustart des Apache folgende Fehlermeldung kommt:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;Invalid command &#039;ProxyHTMLURLMap&#039;, perhaps misspelled or defined by a module not included in the server configuration&lt;br /&gt;
Action &#039;configtest&#039; failed.&lt;br /&gt;
The Apache error log may have more information.&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
fehlt das Paket libapache2-mod-proxy-html. Einfach mit &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;apt-get install libapache2-mod-proxy-html&amp;lt;/syntaxhighlight&amp;gt; &lt;br /&gt;
&lt;br /&gt;
nachinstallieren.&lt;br /&gt;
&lt;br /&gt;
=== ProxyHTMLURLMap funktioniert nicht ===&lt;br /&gt;
&lt;br /&gt;
Nicht bei allen Distributionen scheint die Konfigurationsdatei proxy_html.conf mitgeliefert zu werden. Allerdings ist seit einigen Versionen aus mod_proxy_ssl alle Information über zu ersetzende Links etc. entfernt worden. proxy_html.conf wird daher zwingend benötigt. Sie sollte in /etc/apache2/mods-available liegen und bei der Aktivierung des mods nach /etc/apache2/mods-enabled verlinkt werden. Wenn das nicht der Fall ist, kann man das nachholen.&lt;br /&gt;
&lt;br /&gt;
Eine Beispielkonfiguration gibt es [http://apache.webthing.com/svn/apache/filters/proxy_html/proxy_html.conf hier beim ursprünglichen Autor des mods]. Abspeichern unter /etc/apache2/mods-available/proxy_html.conf und dann verlinken:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;ln -s /etc/apache2/mods-available/proxy_html.conf /etc/apache2/mods-enabled/proxy_html.conf&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dann Apache neu starten/laden.&lt;br /&gt;
&lt;br /&gt;
=== Content Encoding Fehler ===&lt;br /&gt;
&lt;br /&gt;
Wenn Firefox und Chromium sich nach der Konfigurationsänderung über Content Encoding Fehler beschweren, könnte Folgendes helfen:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;pre&amp;gt;SetOutputFilter INFLATE;proxy-html;DEFLATE&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Evtl. kann auch folgender Eintrag noch notwendig sein:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;pre&amp;gt;&lt;br /&gt;
        # Enables outgoing compression for specific file types&lt;br /&gt;
        &amp;lt;IfModule mod_deflate.c&amp;gt;&lt;br /&gt;
                &amp;lt;FilesMatch &amp;quot;.*\.(html|htm|shtml|php|css|js|xml|log|txt|bmp|ttf|otf|eot|svg)$&amp;quot;&amp;gt;&lt;br /&gt;
                   SetOutputFilter DEFLATE&lt;br /&gt;
                &amp;lt;/FilesMatch&amp;gt;&lt;br /&gt;
            AddOutputFilterByType DEFLATE text/html text/plain text/css application/json&lt;br /&gt;
            AddOutputFilterByType DEFLATE application/rss+xml&lt;br /&gt;
            AddOutputFilterByType DEFLATE text/xml application/xml application/xhtml+xml text/x-com$&lt;br /&gt;
            AddOutputFilterByType DEFLATE text/javascript application/javascript application/x-java$&lt;br /&gt;
        &amp;lt;/IfModule&amp;gt;&lt;br /&gt;
 &amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Links==&lt;br /&gt;
*[[HTTPS-Absicherung &amp;amp; Authentifizierung via nginx Webserver]]&lt;br /&gt;
*[https://github.com/tobiasfaust/SimpleWebAuth A simple apache/php web authenticator with longlife JWT tokens for a permanently signin]&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:HOWTOS]]&lt;/div&gt;</summary>
		<author><name>Ph1959de</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=Apache_Authentication_Proxy&amp;diff=40778</id>
		<title>Apache Authentication Proxy</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=Apache_Authentication_Proxy&amp;diff=40778"/>
		<updated>2026-01-31T11:05:01Z</updated>

		<summary type="html">&lt;p&gt;Ph1959de: Randnotizen aus Formatierungsgründen zusammengefasst&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Randnotiz|RNText=Anstatt die unten angegeben einfache BasicAuth zu nutzen kann folgende Erweiterung für eine browserbasierte passwortlose Authentifizierung per QR-Code (Magic-Link) oder Login mit persistentem Cookie verwendet werden. Falls ein gültiges Authentifizierungs-cookie vorliegt wird man transparent zur Applikation weitergeleitet. Andernfalls muss man sich einmal anmelden um das Cookie zu setzen. Anschließend wird man zur Applikation weitergeleitet.&lt;br /&gt;
Voraussetzungen ist der Betrieb und Konfiguration eines eigenen Apache2 Webservers in dieser Anleitung. Das AddOn funktioniert u.a. in iOs und dem Passwortmanager und ist [https://github.com/tobiasfaust/SimpleWebAuth hier dokumentiert]&lt;br /&gt;
&lt;br /&gt;
Eine sicherere Variante eines Apache Authentication Proxy zusätzlich mit Zertifikaten ist [https://gist.github.com/gbirke/8608543 hier dokumentiert]}}&lt;br /&gt;
== Konfiguration ==&lt;br /&gt;
Um den Zugriff auf [[FHEMWEB]] etwas sicherer zu machen, kann man den Webzugriff über einen [https://httpd.apache.org/ Apache Webserver] laufen lassen. Dies ist ein kurzes Rezept, um Zugriffe auf FHEMWEB über einen Apachen authentifizieren zu lassen. Erstellt wurde es auf [https://www.debian.org Debian Squeeze]], sollte aber auch mit [https://www.ubuntu.com/ Ubuntu] funktionieren.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
apt-get install apache2 libapache2-mod-proxy-html&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Step 1) FHEMWEB sollte nur noch auf Anfragen vom selben Rechner lauschen, also kein &#039;global&#039; Attribut in der Definition in fhem.cfg&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
define WEBS FHEMWEB 8084&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Step 2) die folgenden Apache2 Module müssen aktiviert sein: [https://httpd.apache.org/docs/current/mod/mod_proxy.html mod_proxy] + [http://httpd.apache.org/docs/2.4/mod/mod_proxy_http.html mod_proxy_http]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ini&amp;quot;&amp;gt;&lt;br /&gt;
a2enmod proxy&lt;br /&gt;
a2enmod proxy_http&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Step 3) neue Datei &#039;&#039;&#039;&#039;&#039;/etc/apache2/conf.d/fhem&#039;&#039;&#039;&#039;&#039; anlegen:&lt;br /&gt;
&lt;br /&gt;
Diese Konfiguration sorgt dafür, dass alle Anfragen unter /fhem weiter nach &#039;&#039;&#039;http://localhost/fhem&#039;&#039;&#039; geleitet werden. Zusätzlich wird eine Basic-Authentifizierung eingeschaltet. Die Benutzerdatenbank ist dann in /etc/fhem-htpasswd zu finden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ini&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;Location /fhem&amp;gt;&lt;br /&gt;
  # ProxyPass/ProxyPassReverse leitet HTTP requests auf eine andere URL um&lt;br /&gt;
  ProxyPass http://localhost:8084/fhem&lt;br /&gt;
  ProxyPassReverse http://localhost:8084/fhem&lt;br /&gt;
  ProxyHTMLEnable On&lt;br /&gt;
  # ProxyHTMLURLMap passt Links im HTML/JavaScript Source an&lt;br /&gt;
  ProxyHTMLURLMap /        /fhem/&lt;br /&gt;
  ProxyHTMLURLMap /fhem/     /fhem/&lt;br /&gt;
  AuthType Basic&lt;br /&gt;
  AuthName &amp;amp;quot;Password Required&amp;amp;quot;&lt;br /&gt;
  AuthUserFile /etc/fhem-htpasswd&lt;br /&gt;
  Require valid-user&lt;br /&gt;
  Order deny,allow&lt;br /&gt;
  Allow from all&lt;br /&gt;
&amp;lt;/Location&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Step 4) Benutzer-Datenbank in &#039;&#039;&#039;&#039;&#039;/etc/fhem-htpasswd&#039;&#039;&#039;&#039;&#039; anlegen&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
# -c -&amp;gt; create file&lt;br /&gt;
# -s SHA encryption&lt;br /&gt;
htpasswd -c -s /etc/fhem-htpasswd &amp;lt;username&amp;gt;&lt;br /&gt;
# add more users with&lt;br /&gt;
htpasswd -s /etc/fhem-htpasswd &amp;lt;username&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Step 5) Apache neu starten&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;invoke-rc.d apache2 reload&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
bzw.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;service apache2 reload&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Fertig. FHEM ist jetzt über &#039;&#039;&#039;&#039;&#039;http://server/fhem&#039;&#039;&#039;&#039;&#039; erreichbar. Alle Zugriffe müssen aber erst mit Benutzername + Passwort freigeschaltet werden.&lt;br /&gt;
&lt;br /&gt;
=== SSL-Konfiguration ===&lt;br /&gt;
&lt;br /&gt;
Um nicht die Passwörter unverschlüsselt übers Netz zu schicken, ist die Konfiguration eines SSL-Reverse-Proxys noch empfehlenswerter. Die untenstehende Konfiguration zeigt die Verwendung mittels Zertifikaten, die von [https://letsencrypt.org Let&#039;s Encrypt] ausgestellt wurden. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ini&amp;quot;&amp;gt;&lt;br /&gt;
	&amp;lt;VirtualHost *:80&amp;gt;&lt;br /&gt;
  	   ServerName           $host&lt;br /&gt;
  	   RedirectPermanent    / https://$host/&lt;br /&gt;
	&amp;lt;/VirtualHost&amp;gt;&lt;br /&gt;
&lt;br /&gt;
	&amp;lt;VirtualHost *:443&amp;gt;&lt;br /&gt;
	    ServerName          $host&lt;br /&gt;
&lt;br /&gt;
	    SSLEngine           on&lt;br /&gt;
	    SSLProtocol         all -SSLv2 -SSLv3&lt;br /&gt;
	    SSLCipherSuite      ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:AES:CAMELLIA:DES-CBC3-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!aECDH:!EDH-DSS-DES-CBC3-SHA:!EDH-RSA-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA&lt;br /&gt;
	    SSLHonorCipherOrder on&lt;br /&gt;
	    SSLCompression      off&lt;br /&gt;
	    SSLOptions          +StrictRequire&lt;br /&gt;
	&lt;br /&gt;
	    SSLCertificateFile  /etc/letsencrypt/live/$host/fullchain.pem&lt;br /&gt;
	    SSLCertificateKeyFile /etc/letsencrypt/live/$host/privkey.pem&lt;br /&gt;
&lt;br /&gt;
	    Use RootDir $dir  # hier sollte ein Verzeichnis unter /var/www stehen (kann leer sein, muss aber existieren)&lt;br /&gt;
	&lt;br /&gt;
            ProxyPass /.well-known !&lt;br /&gt;
            Alias /.well-known &amp;quot;/var/www/proxy/.well-known&amp;quot;&lt;br /&gt;
            &amp;lt;Directory &amp;quot;$dir/.well-known&amp;quot;&amp;gt;&lt;br /&gt;
               order allow,deny&lt;br /&gt;
               allow from all&lt;br /&gt;
               AllowOverride All&lt;br /&gt;
               AddDefaultCharset Off&lt;br /&gt;
            &amp;lt;/Directory&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
	    ProxyRequests       Off&lt;br /&gt;
	    ProxyVia 		Off&lt;br /&gt;
	    ProxyPreserveHost   On&lt;br /&gt;
	    ProxyPass 		/fhem http://$fhemhost:$port/fhem&lt;br /&gt;
	    ProxyPassReverse 	/fhem http://$fhemhost:$port/fhem&lt;br /&gt;
&lt;br /&gt;
   	    Header always set Strict-Transport-Security &amp;quot;max-age=31536000&amp;quot;&lt;br /&gt;
&lt;br /&gt;
	    &amp;lt;Proxy *&amp;gt;&lt;br /&gt;
		Order deny,allow&lt;br /&gt;
		Allow from all&lt;br /&gt;
                AuthType Basic&lt;br /&gt;
                AuthName &amp;amp;quot;Password Required&amp;amp;quot;&lt;br /&gt;
                AuthUserFile /etc/fhem-htpasswd&lt;br /&gt;
                Require valid-user&lt;br /&gt;
	    &amp;lt;/Proxy&amp;gt;&lt;br /&gt;
	&amp;lt;/VirtualHost&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Proxy-Konfiguration ist so gewählt, dass auch automatische Zertifikats-Aktualisierung per webroot klappt (&amp;lt;code&amp;gt;letsencrypt certonly --renew-by-default --webroot -w $dir -d $host&amp;lt;/code&amp;gt;). Initial muss man natürlich ohne Zertifikat einen normalen non-SSL-VHost aufmachen.&lt;br /&gt;
&lt;br /&gt;
== Websockets-Unterstützung ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ini&amp;quot;&amp;gt;&lt;br /&gt;
			RewriteEngine On&lt;br /&gt;
			RewriteCond %{HTTP:Upgrade} =websocket [NC]&lt;br /&gt;
			RewriteRule /fhem(.*)           ws://$url$1 [P,L]&lt;br /&gt;
			RewriteCond %{HTTP:Upgrade} !=websocket [NC]&lt;br /&gt;
			RewriteRule /fhem(.*)           http://$url$1 [P,L]&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Hinweis zu Apache 2.4 ==&lt;br /&gt;
Es muss zusätzlich das Modul &#039;&#039;&#039;proxy_html&#039;&#039;&#039; aktiviert werden (&amp;lt;code&amp;gt;a2enmod proxy_html&amp;lt;/code&amp;gt;).&lt;br /&gt;
Und die zu erstellende Config-Datei muss jetzt nach &#039;&#039;/etc/apache2/conf-available/fhem.conf&#039;&#039; (statt /etc/apache2/conf.d/fhem).&lt;br /&gt;
&lt;br /&gt;
== Mögliche Probleme ==&lt;br /&gt;
&lt;br /&gt;
=== Invalid command &#039;ProxyHTMLURLMap&#039; ===&lt;br /&gt;
Falls nach dem Neustart des Apache folgende Fehlermeldung kommt:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;Invalid command &#039;ProxyHTMLURLMap&#039;, perhaps misspelled or defined by a module not included in the server configuration&lt;br /&gt;
Action &#039;configtest&#039; failed.&lt;br /&gt;
The Apache error log may have more information.&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
fehlt das Paket libapache2-mod-proxy-html. Einfach mit &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;apt-get install libapache2-mod-proxy-html&amp;lt;/syntaxhighlight&amp;gt; &lt;br /&gt;
&lt;br /&gt;
nachinstallieren.&lt;br /&gt;
&lt;br /&gt;
=== ProxyHTMLURLMap funktioniert nicht ===&lt;br /&gt;
&lt;br /&gt;
Nicht bei allen Distributionen scheint die Konfigurationsdatei proxy_html.conf mitgeliefert zu werden. Allerdings ist seit einigen Versionen aus mod_proxy_ssl alle Information über zu ersetzende Links etc. entfernt worden. proxy_html.conf wird daher zwingend benötigt. Sie sollte in /etc/apache2/mods-available liegen und bei der Aktivierung des mods nach /etc/apache2/mods-enabled verlinkt werden. Wenn das nicht der Fall ist, kann man das nachholen.&lt;br /&gt;
&lt;br /&gt;
Eine Beispielkonfiguration gibt es [http://apache.webthing.com/svn/apache/filters/proxy_html/proxy_html.conf hier beim ursprünglichen Autor des mods]. Abspeichern unter /etc/apache2/mods-available/proxy_html.conf und dann verlinken:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;ln -s /etc/apache2/mods-available/proxy_html.conf /etc/apache2/mods-enabled/proxy_html.conf&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dann Apache neu starten/laden.&lt;br /&gt;
&lt;br /&gt;
=== Content Encoding Fehler ===&lt;br /&gt;
&lt;br /&gt;
Wenn Firefox und Chromium sich nach der Konfigurationsänderung über Content Encoding Fehler beschweren, könnte Folgendes helfen:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;pre&amp;gt;SetOutputFilter INFLATE;proxy-html;DEFLATE&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Evtl. kann auch folgender Eintrag noch notwendig sein:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;pre&amp;gt;&lt;br /&gt;
        # Enables outgoing compression for specific file types&lt;br /&gt;
        &amp;lt;IfModule mod_deflate.c&amp;gt;&lt;br /&gt;
                &amp;lt;FilesMatch &amp;quot;.*\.(html|htm|shtml|php|css|js|xml|log|txt|bmp|ttf|otf|eot|svg)$&amp;quot;&amp;gt;&lt;br /&gt;
                   SetOutputFilter DEFLATE&lt;br /&gt;
                &amp;lt;/FilesMatch&amp;gt;&lt;br /&gt;
            AddOutputFilterByType DEFLATE text/html text/plain text/css application/json&lt;br /&gt;
            AddOutputFilterByType DEFLATE application/rss+xml&lt;br /&gt;
            AddOutputFilterByType DEFLATE text/xml application/xml application/xhtml+xml text/x-com$&lt;br /&gt;
            AddOutputFilterByType DEFLATE text/javascript application/javascript application/x-java$&lt;br /&gt;
        &amp;lt;/IfModule&amp;gt;&lt;br /&gt;
 &amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Links==&lt;br /&gt;
*[[HTTPS-Absicherung &amp;amp; Authentifizierung via nginx Webserver]]&lt;br /&gt;
*[https://github.com/tobiasfaust/SimpleWebAuth A simple apache/php web authenticator with longlife JWT tokens for a permanently signin]&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:HOWTOS]]&lt;/div&gt;</summary>
		<author><name>Ph1959de</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=BOSE_SoundTouch_de-clouding&amp;diff=40770</id>
		<title>BOSE SoundTouch de-clouding</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=BOSE_SoundTouch_de-clouding&amp;diff=40770"/>
		<updated>2026-01-27T18:00:57Z</updated>

		<summary type="html">&lt;p&gt;Ph1959de: Sichtung der letzten Änderungen; kleinere Korrekturen&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Zielgruppe ==&lt;br /&gt;
Benutzer von BOSE SoundTouch-Systemen&lt;br /&gt;
&lt;br /&gt;
== Lösungsübersicht ==&lt;br /&gt;
Cloudfreier Betrieb der BOSE-Boxen, De-bricking nach fehlerhaften Installationen&lt;br /&gt;
&lt;br /&gt;
== Ausgangssituation ==&lt;br /&gt;
BOSE wird im Februar 2026 seine Server aus dem Netz nehmen. Damit wird die Funktionalität der BOSE-Boxen drastisch eingeschränkt, das Abspielen von netzbasierten Radiostreams ist ohne Modifikation der Boxen nicht möglich.&lt;br /&gt;
&lt;br /&gt;
== De-clouding ==&lt;br /&gt;
Um einen root-Zugang zum Linux-Betriebssystem der Boxen zu erhalten, wird benötigt:&lt;br /&gt;
* USB-Stick, formatiert FAT32, mit einer leeren Datei &#039;remote_services&#039;. Achtung: Das bootable-Flag des Dateisystems muss gesetzt sein.&lt;br /&gt;
* Für BOSE ST10 und ST300 zusätzlich ein USB-OTG-Adapter. Das ist ein Adapter, der eine Buchse vom Typ USB A (in den wird der o.a. Stick gesteckt) mit einem Stecker vom Type USB Micro (Typ Micro B, trapezförmig) verbindet. Darin ist offenbar das 5. (ID-)Pin des Micro USB-Steckers mit GND verbunden. Diese Adapter gibt es für ca. 8 € im Handel, sie werden gerne benutzt, um Smartphones in den USB-Host-Modus zu bringen. &lt;br /&gt;
===Vorbereitung allgemein===&lt;br /&gt;
In der Regel werden für den cloudfreien Betrieb der Boxen sowohl die Device-ID, als auch die ID des BOSE-Accounts benötigt. Dazu greift man mit einem Browser auf das API der Box zu, und ruft die Adresse&lt;br /&gt;
 http://&amp;lt;IP-Adresse der Box&amp;gt;:8090/info &lt;br /&gt;
auf. Die Rückgabe sieht etwa so aus:&lt;br /&gt;
 &amp;lt;info deviceID=&amp;quot;{Device-ID}&amp;quot;&amp;gt;&lt;br /&gt;
 &amp;lt;name&amp;gt;{Name des Geräts in der BOSE Apps}&amp;lt;/name&amp;gt;&lt;br /&gt;
 &amp;lt;type&amp;gt;SoundTouch 300&amp;lt;/type&amp;gt;&lt;br /&gt;
 &amp;lt;margeAccountUUID&amp;gt;{ID des BOSE-Accounts}&amp;lt;/margeAccountUUID&amp;gt;&lt;br /&gt;
 ...&lt;br /&gt;
Diese beiden Codes bitte notieren. Außerdem die drei Dateien&lt;br /&gt;
 http://&amp;lt;IP-Adresse der Box&amp;gt;:8090/info &lt;br /&gt;
 http://&amp;lt;IP-Adresse der Box&amp;gt;:8090/recents &lt;br /&gt;
 http://&amp;lt;IP-Adresse der Box&amp;gt;:8090/preset&lt;br /&gt;
lokal abspeichern mit den Namen DeviceInfo.xml, Recents.xml, Presets.xml (Achtung, XML-Header nicht vergessen).&lt;br /&gt;
&lt;br /&gt;
===Vorbereitung ST10===&lt;br /&gt;
Achtung: Vorher unbedingt die Einbindung der ST10 in ein Stereopaar aufheben! Sonst ist das Gerät nach der Änderung der internen Dateien nur noch ein teurer Ziegelstein (&#039;Brick&#039;) und man muss erst die gesamte Firmware neu installieren, siehe unten.&lt;br /&gt;
*USB-Stick durch den OTG-Adapter mit dem USB-Port der ST10 verbinden.&lt;br /&gt;
*Stromversorgungskabel abziehen.&lt;br /&gt;
*Stromversorgungskabel wieder einstecken&lt;br /&gt;
*Die ST10 bootet jetzt vom USB-Stick, ggf. zeigt dessen Signal-LED den Zugriff an.&lt;br /&gt;
&lt;br /&gt;
===Vorbereitung ST20===&lt;br /&gt;
*USB-Stick mit dem USB-Port der ST20 verbinden.&lt;br /&gt;
*Stromversorgungskabel abziehen.&lt;br /&gt;
*Stromversorgungskabel wieder einstecken&lt;br /&gt;
*Die ST20 bootet jetzt vom USB-Stick, ggf. zeigt dessen Signal-LED den Zugriff an.&lt;br /&gt;
&lt;br /&gt;
===Vorbereitung ST300===&lt;br /&gt;
*USB-Stick durch den OTG-Adapter mit dem USB-Port der ST300 verbinden.&lt;br /&gt;
*Stromversorgungskabel abziehen.&lt;br /&gt;
*Infrarot-Fernbedienung der ST300 auf das Gerät richten, den &#039;&#039;SoundTouch&#039;&#039;-Button drücken und &#039;&#039;&#039;halten&#039;&#039;&#039; (2. Button in der 2. Reihe)&lt;br /&gt;
*Währenddessen das Stromversorgungskabel wieder einstecken&lt;br /&gt;
*Die ST300 bootet jetzt vom USB-Stick, ggf. zeigt dessen Signal-LED den Zugriff an.&lt;br /&gt;
*LEDs der ST300 sollten jetzt gelb blinken&lt;br /&gt;
&lt;br /&gt;
===Zugang===&lt;br /&gt;
Der Zugang ist jetzt möglich mit &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Bash&amp;quot;&amp;gt;&lt;br /&gt;
 telnet &amp;lt;IP-Adresse der Box&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Auf die Rückgabe &#039;rhino login&#039; einfach &#039;root&#039; eingeben, ein Passwort ist nicht erforderlich.&lt;br /&gt;
Alternativ kann man auch verwenden&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Bash&amp;quot;&amp;gt;&lt;br /&gt;
 ssh -o HostKeyAlgorithms=+ssh-rsa -o PubkeyAcceptedAlgorithms=+ssh-rsa  root@&amp;lt;IP-Adresse der Box&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Möchte man diesen Zugang permanent haben, um ggf. weitere Änderungen einfacher auszuführen, muss man eine leere Datei &#039;remote_services&#039; erzeugen mit dem Kommando&lt;br /&gt;
 touch /mnt/nv/remote_services&lt;br /&gt;
Während das für die Umstellung auf cloudfreien Betrieb ganz komfortabel ist, sollte man diese Datei nach erfolgreich getester Umstellung wieder löschen, denn sonst ist die Box komplett ungesichert!&lt;br /&gt;
&lt;br /&gt;
===Holen weiterer Daten===&lt;br /&gt;
Nach einem Wechsel in das korrekte Verzeichnis&lt;br /&gt;
 cd /mnt/nv/BoseApp-Persistence/1/&lt;br /&gt;
findet man dort auch die schon oben erwähnten Dateien Recents.xml und Presets.xml, sie können auch von hier auf den USB-Stick herunterkopiert werden.&lt;br /&gt;
&lt;br /&gt;
Ebenfalls in diesem Verzeichnis liegt die Datei Sources.xml, dies muss ebenfalls entweder kopiert werden. Entweder local auf den USB-Stick oder, z.B. mit dem Befehl&lt;br /&gt;
 scp Sources.xml {username}@{irgendein computer}&lt;br /&gt;
Via Netz übertragen werden.&lt;br /&gt;
===Umbiegen===&lt;br /&gt;
Die Konfiguration der BOSE-Server kann jetzt geändert werden, dazu muss man das richtige Verzeichnis auf der Box kennen, das Dateisystem beschreibbar machen und die entsprechende Datei ändern&lt;br /&gt;
 cd /opt/Bose/etc/&lt;br /&gt;
 rw&lt;br /&gt;
 vi SoundTouchSdkPrivateCfg.xml&lt;br /&gt;
Für Nicht-Linux-Nutzer ist der vi Editor etwas gewöhnungsbedürftig, hier findet man eine [https://sits.de/vi.html Mini-Anleitung].&lt;br /&gt;
&lt;br /&gt;
Die Datei enthält im BOSE-Urzustand folgende Informationen&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;XML&amp;quot;&amp;gt;&lt;br /&gt;
 &amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;utf-8&amp;quot;?&amp;gt;&lt;br /&gt;
 &amp;lt;SoundTouchSdkPrivateCfg&amp;gt;&lt;br /&gt;
 &amp;lt;margeServerUrl&amp;gt;https://streaming.bose.com&amp;lt;/margeServerUrl&amp;gt;&lt;br /&gt;
 &amp;lt;statsServerUrl&amp;gt;https://events.api.bosecm.com&amp;lt;/statsServerUrl&amp;gt;&lt;br /&gt;
 &amp;lt;swUpdateUrl&amp;gt;https://worldwide.bose.com/updates/soundtouch&amp;lt;/swUpdateUrl&amp;gt;&lt;br /&gt;
 &amp;lt;usePandoraProductionServer&amp;gt;true&amp;lt;/usePandoraProductionServer&amp;gt;&lt;br /&gt;
 &amp;lt;isZeroconfEnabled&amp;gt;true&amp;lt;/isZeroconfEnabled&amp;gt;&lt;br /&gt;
 &amp;lt;saveMargeCustomerReport&amp;gt;false&amp;lt;/saveMargeCustomerReport&amp;gt;&lt;br /&gt;
 &amp;lt;bmxRegistryUrl&amp;gt;https://content.api.bose.io/bmx/registry/v1/services&amp;lt;/bmxRegistryUrl&amp;gt;&lt;br /&gt;
 &amp;lt;/SoundTouchSdkPrivateCfg&amp;gt;&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Um die Box auf einen anderen Server umzubiegen, der unter dem Namen oder der IP-adresse &amp;lt;code&amp;gt;&#039;{NeuerServer}:{Port}&#039;&amp;lt;/code&amp;gt; erreichbar ist, bitte den Inhalt wie folgt ändern:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;XML&amp;quot;&amp;gt;&lt;br /&gt;
 &amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;utf-8&amp;quot;?&amp;gt;&lt;br /&gt;
 &amp;lt;SoundTouchSdkPrivateCfg&amp;gt;&lt;br /&gt;
  &amp;lt;margeServerUrl&amp;gt;http://{NeuerServer}:{Port}/marge&amp;lt;/margeServerUrl&amp;gt;&lt;br /&gt;
  &amp;lt;statsServerUrl&amp;gt;http://{NeuerServer}:{Port}&amp;lt;/statsServerUrl&amp;gt;&lt;br /&gt;
  &amp;lt;swUpdateUrl&amp;gt;http://{NeuerServer}:{Port}/updates/soundtouch&amp;lt;/swUpdateUrl&amp;gt;&lt;br /&gt;
  &amp;lt;usePandoraProductionServer&amp;gt;true&amp;lt;/usePandoraProductionServer&amp;gt;&lt;br /&gt;
  &amp;lt;isZeroconfEnabled&amp;gt;true&amp;lt;/isZeroconfEnabled&amp;gt;&lt;br /&gt;
  &amp;lt;saveMargeCustomerReport&amp;gt;false&amp;lt;/saveMargeCustomerReport&amp;gt;&lt;br /&gt;
  &amp;lt;bmxRegistryUrl&amp;gt;http://{NeuerServer}:{Port}/bmx/registry/v1/services&amp;lt;/bmxRegistryUrl&amp;gt;&lt;br /&gt;
 &amp;lt;/SoundTouchSdkPrivateCfg&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== De-bricking ==&lt;br /&gt;
Für den Fall, dass die BOSE Box nach irgendwelchen Änderungen nicht mehr ins lokale WLAN findet, aber auch keinen eigenen Access Point anbietet, kann man das Gerät dennoch retten. Dazu muss die Firmware komplett neu installiert werden. Zunächst die passende Firmware suchen&lt;br /&gt;
&lt;br /&gt;
[https://archive.org/download/bose-soundtouch-software-and-firmware/ Archiv mit BOSE Firmware]&lt;br /&gt;
&lt;br /&gt;
Die Zip-Datei muss entpackt werden die darin enthaltene Datei mit der Endung &#039;&#039;.stu&#039;&#039; muss auf einen bootfähigen USB-Stick geschrieben werden, siehe oben.&lt;br /&gt;
&lt;br /&gt;
TODO: Unterabschnitte für die anderen Systeme schreiben, da nicht bekannt ist, wie lange [https://downloads.bose.com/ced/soundtouch/soundtouch_usb/index.html?l=de BOSE die Informationen noch zur Verfügung stellt].&lt;br /&gt;
&lt;br /&gt;
===ST10===&lt;br /&gt;
*USB-Stick durch den OTG-Adapter mit dem USB-Port der ST10 verbinden.&lt;br /&gt;
*Stromversorgungskabel abziehen.&lt;br /&gt;
*Auf dem Tastenfeld die Taste „4“ und die „Lautstärke leiser“-Taste drücken und &#039;&#039;&#039;halten&#039;&#039;&#039;&lt;br /&gt;
*Stromversorgungskabel wieder einstecken&lt;br /&gt;
*Die ST10 bootet jetzt vom USB-Stick, ggf. zeigt dessen Signal-LED den Zugriff an. Die Buttons können nun losgelassen werden.&lt;br /&gt;
Die Installation dauert ca. 5 Minuten.&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:HOWTOS]]&lt;br /&gt;
[[Kategorie:BOSE]]&lt;/div&gt;</summary>
		<author><name>Ph1959de</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=Modul_Shelly&amp;diff=40735</id>
		<title>Modul Shelly</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=Modul_Shelly&amp;diff=40735"/>
		<updated>2026-01-14T18:56:16Z</updated>

		<summary type="html">&lt;p&gt;Ph1959de: Hinweis auf Umbau / Umstrukturierung der Seiten&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Baustelle}}{{Todo|&#039;&#039;&#039;Achtung: Diese Seite ist teilweise veraltet, insbesondere unterstützt das Modul weitere Aktoren. Bitte Commandref lesen - diese Seite ist in Überarbeitung&#039;&#039;&#039;}}{{Todo|Umstrukturierung läuft gerade, weitere betroffene Seiten sind [[Shelly]], [[Shelly (Modul)]], [[ShellyMonitor]], weitere können und werden folgen... --[[Benutzer:Ph1959de|Peter]] ([[Benutzer Diskussion:Ph1959de|Diskussion]]) 19:56, 14. Jan. 2026 (CET)}}{{Infobox Modul&lt;br /&gt;
|ModPurpose=Das Modul stellt ein Interface zur Bedienung von Shelly Devices zur Verfügung&lt;br /&gt;
|ModType=d&lt;br /&gt;
|ModCmdRef=Shelly&lt;br /&gt;
|ModForumArea=Sonstige Systeme&lt;br /&gt;
|ModFTopic=118446&lt;br /&gt;
|ModTechName=36_Shelly.pm&lt;br /&gt;
|ModOwner=Starkstrombastler ({{Link2FU|3884|Forum}}/[[Benutzer Diskussion:Starkstrombastler|Wiki]])&lt;br /&gt;
}}&lt;br /&gt;
Auf dieser Seite werden die Aktoren des bulgarischen Herstellers Allterco Robotics beschrieben (Markenname Shelly) sowie deren Ansteuerung mit FHEM und aufgetretene Probleme. &lt;br /&gt;
{{Randnotiz|RNTyp=r|RNText=&#039;&#039;&#039;Achtung&#039;&#039;&#039;: Einige der auf dieser Seite erwähnten Geräte und Funktionen sind derzeit nur mit {{Link2Forum|Topic=111905|Message=1285498|LinkText=dieser Testversion}} verfügbar, die manuell installiert werden muss!&lt;br /&gt;
* Für die Weiterentwicklung des Moduls wurde im Forum ein neues Thema &#039;&#039;&#039;({{Link2Forum|Topic=137222|LinkText=Entwicklungs-Thread Modul 36_Shelly.pm}})&#039;&#039;&#039; aufgemacht.}}&lt;br /&gt;
Bei den Shelly-Geräten handelt es sich um IP-basierte Schalt- und Dimmaktoren, die auf verschiedene Weise angesteuert werden können &lt;br /&gt;
*über die Web-Oberfläche des eingebauten Mikro-Webservers,&lt;br /&gt;
*über eine proprietäre App des Herstellers (Achtung, Cloud!),&lt;br /&gt;
*über das hier beschriebene FHEM-Modul 36_Shelly.pm&lt;br /&gt;
*über MQTT&lt;br /&gt;
Ein Teil der Aktoren verfügt über eine eingebaute Leistungsmessung (siehe Spalte Messkanäle in unten stehender Tabelle).&lt;br /&gt;
&lt;br /&gt;
Einen Überblick über die (mittlerweile - Frühjahr 2025 - vier) Gerätegenerationen (Gen1 bis Gen4) bietet die Supportseite [https://support.shelly.cloud/de/support/solutions/articles/103000316073-vergleich-von-shelly-gen-1-gen-2-gen-3-gen4-ger%C3%A4ten Vergleich von Shelly gen1...gen4 Geräten]. Die untenstehende Auflistung muss angesichts der mittlerweile vier Geräteversionen überarbeitet werden.&lt;br /&gt;
&lt;br /&gt;
==Geräteübersicht==&lt;br /&gt;
Übersicht der IP-basierten Produktreihen&lt;br /&gt;
{| class=&amp;quot;wikitable mw:datatable&amp;quot;&lt;br /&gt;
! style=&amp;quot;width:15px&amp;quot; |ID&lt;br /&gt;
! style=&amp;quot;width:100px&amp;quot; | Produktreihe&lt;br /&gt;
! style=&amp;quot;width:225px&amp;quot; |gemeinsame Merkmale&lt;br /&gt;
|-&lt;br /&gt;
|SH&lt;br /&gt;
|erste Generation&lt;br /&gt;
|COIOT (Nutzung mit Shelly-Monitor), kein Bluetooth&lt;br /&gt;
|-&lt;br /&gt;
|SN&lt;br /&gt;
|Shelly Plus&lt;br /&gt;
Shelly Plus mini&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|SP&lt;br /&gt;
|Shelly Pro&lt;br /&gt;
|Montage auf Hutschiene, zusätzlicher Ethernet-Port (RJ45)&lt;br /&gt;
|-&lt;br /&gt;
|S3&lt;br /&gt;
|Shelly Gen3&lt;br /&gt;
Shelly Gen3 mini&lt;br /&gt;
|proprietärer Prozessor&lt;br /&gt;
|-&lt;br /&gt;
|SA&lt;br /&gt;
|Control Panel&lt;br /&gt;
|Android-System&lt;br /&gt;
|}&lt;br /&gt;
ID: erste beiden Stellen der Modell-ID &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Liste der aktuell unterstützten Geräte &lt;br /&gt;
(hier nicht aufgeführte Geräte der ersten Generation können zusammen mit dem Shelly-Monitor genutzt werden):&lt;br /&gt;
{| class=&amp;quot;wikitable sortable&amp;quot;&lt;br /&gt;
! data-sort-type=&amp;quot;text&amp;quot; style=&amp;quot;width:150px&amp;quot; |Modell&lt;br /&gt;
! data-sort-type=&amp;quot;text&amp;quot; style=&amp;quot;width:75px&amp;quot; |Typ&lt;br /&gt;
! data-sort-type=&amp;quot;number&amp;quot; style=&amp;quot;width:20px;text-align:center;&amp;quot; |Schalt- kanäle&lt;br /&gt;
! data-sort-type=&amp;quot;number&amp;quot; style=&amp;quot;width:20px;text-align:center;&amp;quot; |Dimm- kanäle&lt;br /&gt;
! data-sort-type=&amp;quot;number&amp;quot; style=&amp;quot;width:20px;text-align:center;&amp;quot; |Mess- kanäle&lt;br /&gt;
! data-sort-type=&amp;quot;number&amp;quot; style=&amp;quot;width:20px;text-align:center;&amp;quot; |Digital Eingänge&lt;br /&gt;
! |Bemerkungen&lt;br /&gt;
|-&lt;br /&gt;
! colspan=&amp;quot;7&amp;quot; |&#039;&#039;&#039;Gen 1&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
|Shelly 1&lt;br /&gt;
|Schalter&lt;br /&gt;
|1&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|1&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|Shelly 1PM&lt;br /&gt;
|Schalter&lt;br /&gt;
|1&lt;br /&gt;
|&lt;br /&gt;
|1&lt;br /&gt;
|1&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|Shelly 1L&lt;br /&gt;
|Schalter&lt;br /&gt;
| 1&lt;br /&gt;
|&lt;br /&gt;
|1&lt;br /&gt;
| 1&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot; |Shelly 2&lt;br /&gt;
| Schalter&lt;br /&gt;
|2&lt;br /&gt;
|&lt;br /&gt;
|1&lt;br /&gt;
|2&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|Rollladenaktor&lt;br /&gt;
|1&lt;br /&gt;
|&lt;br /&gt;
|1&lt;br /&gt;
|2&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot; |Shelly 2.5&lt;br /&gt;
|Schalter&lt;br /&gt;
| 2&lt;br /&gt;
|&lt;br /&gt;
|2&lt;br /&gt;
|2&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|Rollladenaktor&lt;br /&gt;
|1&lt;br /&gt;
|&lt;br /&gt;
|1&lt;br /&gt;
|2&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|Shelly 4Pro&lt;br /&gt;
|Schalter&lt;br /&gt;
|4&lt;br /&gt;
|&lt;br /&gt;
|4&lt;br /&gt;
|4&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|Shelly i3&lt;br /&gt;
| Digitale Eingänge&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|3&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|Shelly EM&lt;br /&gt;
|Leistungsmessung&lt;br /&gt;
|1&lt;br /&gt;
|&lt;br /&gt;
|2&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;[[Shelly 3EM]]&#039;&#039;&#039;&lt;br /&gt;
|Leistungsmessung&lt;br /&gt;
|1&lt;br /&gt;
|&lt;br /&gt;
|3&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;[[Shelly Uni]]&#039;&#039;&#039;&lt;br /&gt;
|Universal&lt;br /&gt;
|2&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|*)&lt;br /&gt;
|1-Wire, 2 potentialfreie Relaisausgänge&lt;br /&gt;
Analogeingänge&lt;br /&gt;
|-&lt;br /&gt;
|Shelly Plug&lt;br /&gt;
Shelly Plug S &lt;br /&gt;
|Schaltsteckdose&lt;br /&gt;
|1&lt;br /&gt;
|&lt;br /&gt;
|1&lt;br /&gt;
|&lt;br /&gt;
|1 Taster&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot; |Shelly RGBW2&lt;br /&gt;
|Dimmer&lt;br /&gt;
|&lt;br /&gt;
|4&lt;br /&gt;
|4&lt;br /&gt;
|1&lt;br /&gt;
|4-fach Aktor&lt;br /&gt;
|-&lt;br /&gt;
|Dimmer&lt;br /&gt;
|&lt;br /&gt;
|1&lt;br /&gt;
| 1&lt;br /&gt;
|1&lt;br /&gt;
|RGBW Controller&lt;br /&gt;
|-&lt;br /&gt;
|Shelly Dimmer2&lt;br /&gt;
|Dimmer&lt;br /&gt;
|&lt;br /&gt;
| 1&lt;br /&gt;
|1&lt;br /&gt;
|2&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|Shelly Duo&lt;br /&gt;
|Leuchte&lt;br /&gt;
|&lt;br /&gt;
|1&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|E27 oder GU10 Fassung&lt;br /&gt;
|-&lt;br /&gt;
|Shelly Vintage&lt;br /&gt;
|Leuchte&lt;br /&gt;
|&lt;br /&gt;
|1&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|Shelly Bulb&lt;br /&gt;
|Leuchte&lt;br /&gt;
|&lt;br /&gt;
|1&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|Modi: weiß oder farbe&lt;br /&gt;
|-&lt;br /&gt;
! colspan=&amp;quot;7&amp;quot; |&#039;&#039;&#039;Shelly Plus&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
|Shelly Plus 1&lt;br /&gt;
&lt;br /&gt;
|Schalter &lt;br /&gt;
|1&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|1&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;[[Shelly Plus 1PM]]&#039;&#039;&#039;&lt;br /&gt;
|Schalter&lt;br /&gt;
|1&lt;br /&gt;
|&lt;br /&gt;
|1&lt;br /&gt;
|1&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot; |&#039;&#039;&#039;[[Shelly Plus 2PM]]&#039;&#039;&#039;&lt;br /&gt;
| Schalter&lt;br /&gt;
|2&lt;br /&gt;
|&lt;br /&gt;
|2&lt;br /&gt;
|2&lt;br /&gt;
|-&lt;br /&gt;
|Rollladenaktor&lt;br /&gt;
|1&lt;br /&gt;
|&lt;br /&gt;
| 1&lt;br /&gt;
|2&lt;br /&gt;
|-&lt;br /&gt;
|Shelly Plus i4&lt;br /&gt;
|Digitale Eingänge&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
| 4&lt;br /&gt;
|AC und DC - Variante&lt;br /&gt;
|-&lt;br /&gt;
| Shelly Plus Plug S&lt;br /&gt;
Shelly Plus Plug IT&lt;br /&gt;
Shelly Plus Plug UK&lt;br /&gt;
Shelly Plus Plug US&lt;br /&gt;
|Schaltsteckdose&lt;br /&gt;
&lt;br /&gt;
|1&lt;br /&gt;
|&lt;br /&gt;
|1&lt;br /&gt;
|&lt;br /&gt;
|1 Taster;&lt;br /&gt;
Varianten V1, V2&lt;br /&gt;
|-&lt;br /&gt;
|Shelly Plus Uni&lt;br /&gt;
|Universal&lt;br /&gt;
| 2&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|*)&lt;br /&gt;
|1-Wire, 2 potentialfreie Relaisausgänge &lt;br /&gt;
Analogeingänge; Neu in 2024&lt;br /&gt;
|-&lt;br /&gt;
|Shelly Plus 0-10V Dimmer&lt;br /&gt;
|Dimmer &lt;br /&gt;
|&lt;br /&gt;
|1&lt;br /&gt;
|1&lt;br /&gt;
|2&lt;br /&gt;
|0-10 V DC Ausgang&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot; |Shelly Plus RGBW&lt;br /&gt;
|Dimmer&lt;br /&gt;
|&lt;br /&gt;
|4&lt;br /&gt;
|4&lt;br /&gt;
|4&lt;br /&gt;
|4-fach Aktor&lt;br /&gt;
|-&lt;br /&gt;
|Dimmer&lt;br /&gt;
|&lt;br /&gt;
|1&lt;br /&gt;
|1&lt;br /&gt;
|1&lt;br /&gt;
|RGBW Controller&lt;br /&gt;
|-&lt;br /&gt;
! colspan=&amp;quot;7&amp;quot; | &#039;&#039;&#039;Shelly Plus Mini&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
|Shelly Plus 1 Mini&lt;br /&gt;
|Schalter&lt;br /&gt;
|1&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|1&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|Shelly Plus 1PM Mini&lt;br /&gt;
|Schalter&lt;br /&gt;
|1&lt;br /&gt;
|&lt;br /&gt;
|1&lt;br /&gt;
|1&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|Shelly PM Mini&lt;br /&gt;
|Leistungsmessung&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
| 1&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
! colspan=&amp;quot;7&amp;quot; |&#039;&#039;&#039;Shelly Pro&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
|Shelly Pro 1&lt;br /&gt;
|Schalter&lt;br /&gt;
|1&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|2&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|Shelly Pro 1PM&lt;br /&gt;
|Schalter&lt;br /&gt;
|1&lt;br /&gt;
|&lt;br /&gt;
|1&lt;br /&gt;
|2&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|Shelly Pro 2&lt;br /&gt;
| Schalter&lt;br /&gt;
|2&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
| 2&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|Shelly Pro 2PM&lt;br /&gt;
|Schalter&lt;br /&gt;
|2&lt;br /&gt;
|&lt;br /&gt;
|2&lt;br /&gt;
|2&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|Shelly Pro Dual&lt;br /&gt;
|Rollladenaktor&lt;br /&gt;
|4&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|4&lt;br /&gt;
|2 Rolladenaktoren&lt;br /&gt;
|-&lt;br /&gt;
|Shelly Pro Dimmer 1PM &lt;br /&gt;
|Dimmer&lt;br /&gt;
|&lt;br /&gt;
|1&lt;br /&gt;
|1&lt;br /&gt;
|2&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|Shelly Pro Dimmer 2PM&lt;br /&gt;
|Dimmer&lt;br /&gt;
|&lt;br /&gt;
| 2&lt;br /&gt;
|2&lt;br /&gt;
|4&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|Shelly Pro 3&lt;br /&gt;
|Schalter&lt;br /&gt;
|3&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|3&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|Shelly Pro 3EM&lt;br /&gt;
|Leistungsmessung&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|3&lt;br /&gt;
|&lt;br /&gt;
|1 Schaltkanal mit Addon&lt;br /&gt;
|-&lt;br /&gt;
|Shelly Pro EM50 &lt;br /&gt;
|Leistungsmessung&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|Shelly Pro 4PM&lt;br /&gt;
|Schalter&lt;br /&gt;
|4&lt;br /&gt;
|&lt;br /&gt;
|4&lt;br /&gt;
|4&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
! colspan=&amp;quot;7&amp;quot; |&#039;&#039;&#039;Shelly Gen3&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| Shelly 1 Gen3&lt;br /&gt;
|Schalter&lt;br /&gt;
|1&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|1&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|Shelly 1PM Gen3&lt;br /&gt;
|Schalter&lt;br /&gt;
|1&lt;br /&gt;
|&lt;br /&gt;
|1&lt;br /&gt;
|1&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|Shelly i4 Gen3&lt;br /&gt;
|Digitale Eingänge&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
| 4&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|Shelly Dimmer 0/1-10V Gen3&lt;br /&gt;
|Dimmer&lt;br /&gt;
|&lt;br /&gt;
| 1&lt;br /&gt;
| 1&lt;br /&gt;
|2&lt;br /&gt;
|0-10V DC oder&lt;br /&gt;
1-10V DC Ausgang&lt;br /&gt;
|-&lt;br /&gt;
! colspan=&amp;quot;7&amp;quot; |&#039;&#039;&#039;Shelly Gen3 Mini&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
|Shelly 1 Mini Gen3&lt;br /&gt;
|Schalter&lt;br /&gt;
|1&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|1&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|Shelly 1PM Mini Gen3 &lt;br /&gt;
|Schalter&lt;br /&gt;
|1&lt;br /&gt;
|&lt;br /&gt;
|1&lt;br /&gt;
|1&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|Shelly PM Mini Gen3&lt;br /&gt;
|Leistungsmessung&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|1&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
! colspan=&amp;quot;7&amp;quot; | &#039;&#039;&#039;Control Panels&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
|Shelly Wall Display&lt;br /&gt;
| Control Panel&lt;br /&gt;
| 1&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
| 1&lt;br /&gt;
|&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==Einbindung in FHEM==&lt;br /&gt;
Vorgehensweise zur Einbindung eines Shelly-Gerätes in FHEM:&lt;br /&gt;
*Aktor nach Vorschrift anschließen&lt;br /&gt;
*mit einem WLAN-fähigen Gerät (Laptop, Smartphone, Tablet...; im Folgenden als &#039;&#039;&#039;Laptop&#039;&#039;&#039; bezeichnet) nach dem internen Access Point suchen, der durch das Shelly-Gerät erzeugt wird; typischerweise hat es eine SSID ähnlich wie&lt;br /&gt;
:&amp;lt;code&amp;gt;shelly1-..., shellyswitch-..., shelly4pro-..., &amp;lt;/code&amp;gt; &lt;br /&gt;
*&#039;&#039;&#039;Laptop&#039;&#039;&#039; mit diesem Access Point verbinden; typischerweise bekommt das Gerät dabei die IP-Adresse 192.168.33.2 zugewiesen.&lt;br /&gt;
*im Browser des &#039;&#039;&#039;Laptops&#039;&#039;&#039; die IP-Adresse 192.168.33.1 aufrufen - das ist der Shelly selbst; in der damit angezeigten Weboberfläche kann das Shelly-Gerät konfiguriert werden&lt;br /&gt;
**Shelly ins häusliche WLAN anmelden (mit fester IP-Adresse &amp;lt;shelly-ip&amp;gt; natürlich...)&lt;br /&gt;
**Internen Access Point abschalten (kann auch nach dem nächsten Schritt oder noch später erfolgen)&lt;br /&gt;
**Testen: &#039;&#039;&#039;Laptop&#039;&#039;&#039; wieder mit dem häuslichen WLAN verbinden, und im Browser die Adresse &amp;lt;shelly-ip&amp;gt; aufrufen&lt;br /&gt;
*In FHEM definieren &lt;br /&gt;
:&amp;lt;code&amp;gt;define myShelly Shelly &amp;lt;shelly-ip&amp;gt;&amp;lt;/code&amp;gt; &lt;br /&gt;
*Das Modul setzt bei bekannten Geräten das Attribut &amp;lt;code&amp;gt;model&amp;lt;/code&amp;gt; automatisch. Bei nicht unterstützten Geräten wird das Attribut auf den Wert &amp;lt;code&amp;gt;generic&amp;lt;/code&amp;gt; gesetzt. In diesen Fällen kann das Attribut &amp;lt;code&amp;gt;model&amp;lt;/code&amp;gt; auf der Detailseite des Devices manuell gesetzt werden:&lt;br /&gt;
:&amp;lt;code&amp;gt; attr myShelly model shellyrgbw|shellydimmer|shelly2.5|shelly2|shellyem|shelly3em|shelly4|shellyplug|shelly1|shellybulb|shelly1pm|shellyuni|generic&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Falls es sich um einen Shelly2 oder 2.5 handelt, muss ferner das Attribut &amp;lt;code&amp;gt;mode&amp;lt;/code&amp;gt; auf &amp;quot;roller&amp;quot; oder &amp;quot;relay&amp;quot; gesetzt werden. Mit diesem Modul können alle Daten übertragen und (prinzipiell) alle Konfigurationsänderungen durchgeführt werden, außerdem ist es auf einfachste Weise zu installieren. Das Modul pollt im per Attribut &amp;lt;code&amp;gt;interval&amp;lt;/code&amp;gt; einstellbaren Abstand zyklisch den Aktor auf Statusänderungen (Wert 0 =&amp;gt; kein Polling). Damit der Aktor im Stande ist, irgendwelche Zustandsänderungen &#039;&#039;von sich aus&#039;&#039; an FHEM zu melden, müssen diese als REST-Befehle (also URL-Aufrufe) in der Konfigurationsoberfläche des Shelly-Aktors eingetragen werden. Siehe CommandRef.&lt;br /&gt;
&lt;br /&gt;
Zum Betrieb ist ferner noch zu bemerken, dass das Modul zwar meldet, ob ein Firmware-Update nötig ist, ausgelöst werden muss dieses aber über die Web-Oberfläche des Shelly selber.&lt;br /&gt;
&lt;br /&gt;
===Actions/Webhooks (nur Testversion)===&lt;br /&gt;
Ab Shelly Firmware 1.5.0 werden Actions unterstützt. Damit besteht die Möglichkeit, dass ein Shelly bei Eintreten bestimmter Ereignisse von sich aus Meldungen an andere Shellies und/oder übergeordnete Systeme wie FHEM absetzt. Dies ist nützlich, um Statusänderungen, die z.B. durch lokal betätigte Tasten entstehen, direkt an FHEM zu übermitteln.&lt;br /&gt;
&lt;br /&gt;
Nachfolgende Beispiele zeigen den Code, der im Shelly unter URL einzutragen ist:&lt;br /&gt;
&lt;br /&gt;
Ausgang (Relais) eines Shelly1 schaltet ein:      &lt;br /&gt;
:&amp;lt;code&amp;gt;http://&amp;lt;FHEM-IP&amp;gt;:&amp;lt;Port&amp;gt;/fhem?cmd=set%20&amp;lt;name&amp;gt;%20out_on&amp;lt;/code&amp;gt;&lt;br /&gt;
hierbei sind: &lt;br /&gt;
:&amp;lt;code&amp;gt;&amp;lt;FHEM-IP&amp;gt;&amp;lt;/code&amp;gt; die IP-Adresse des Servers auf dem FHEM läuft&lt;br /&gt;
:&amp;lt;code&amp;gt;&amp;lt;Port&amp;gt;&amp;lt;/code&amp;gt; die Port-Nummer&lt;br /&gt;
:&amp;lt;code&amp;gt;&amp;lt;name&amp;gt;&amp;lt;/code&amp;gt; der Name des FHEM-Devices&lt;br /&gt;
:&amp;lt;code&amp;gt;%20&amp;lt;/code&amp;gt;    stellt ein Leerzeichen dar&lt;br /&gt;
&lt;br /&gt;
Beispiel 2: Eingang eines Shelly2 wird betätigt:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot;&amp;gt;http://&amp;lt;FHEM-IP&amp;gt;:&amp;lt;Port&amp;gt;/fhem?cmd=set%20&amp;lt;name&amp;gt;%20input_on%20&amp;lt;ch&amp;gt;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
:&amp;lt;code&amp;gt;&amp;lt;ch&amp;gt;&amp;lt;/code&amp;gt;  die Nummer des Schaltkanals (Nummer des Eingangs), z.B. &amp;lt;code&amp;gt;0&amp;lt;/code&amp;gt; oder &amp;lt;code&amp;gt;1&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Beispiel 3: Eingang1 eines ShellyDimmers wird betätigt:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot;&amp;gt;http://&amp;amp;#x3C;FHEM-IP&amp;amp;#x3E;:&amp;amp;#x3C;Port&amp;amp;#x3E;/fhem?cmd=set%20&amp;amp;#x3C;name&amp;amp;#x3E;%20short_push&amp;lt;nowiki/&amp;gt;%20&amp;lt;inp&amp;gt;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
:&amp;lt;code&amp;gt;&amp;lt;nowiki&amp;gt;&amp;lt;inp&amp;gt;&amp;lt;/nowiki&amp;gt;&amp;lt;/code&amp;gt;  Nummer des Eingangs, 0 oder 1 (ShellyDimmer verfügen je Schaltkanal über zwei Eingänge)&lt;br /&gt;
&lt;br /&gt;
Beispiel 4: Wirkleistung eines ShellyPro3EM:  &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot;&amp;gt;http://&amp;lt;FHEM-IP&amp;gt;:&amp;lt;Port&amp;gt;/fhem?fwcsrf=csrf_368985985592099&amp;amp;cmd=set%20Y173%20Active_Power_$phase%20$active_power&amp;lt;/syntaxhighlight&amp;gt; &lt;br /&gt;
:&amp;lt;code&amp;gt;fwcsrf=csrf_368985985592099&amp;lt;/code&amp;gt;    das CSRF-Token (FHEMWeb)&lt;br /&gt;
:&amp;lt;code&amp;gt;$phase&amp;lt;/code&amp;gt;   wird vom Shelly durch a, b oder c ersetzt&lt;br /&gt;
: &amp;lt;code&amp;gt;$active_power&amp;lt;/code&amp;gt;  wird vom Shelly durch die aktuelle Wirkleistung ersetzt&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Endpoints&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
In vorstehenden Beispielen stellt der Teil &amp;lt;code&amp;gt;set%20&amp;lt;name&amp;gt;%20&amp;lt;cmd&amp;gt;&amp;lt;/code&amp;gt; den Endpoint dar, d.h. dies ist der Befehl, der vom Shelly-Device in FHEM verarbeitet werden muss.&lt;br /&gt;
&lt;br /&gt;
=====Liste der Befehle der Set-Endpoints:=====&lt;br /&gt;
{| class=&amp;quot;wikitable sortable mw-collapsible&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
!&amp;lt;cmd&amp;gt;&lt;br /&gt;
!Wert&lt;br /&gt;
!Reading&lt;br /&gt;
!Erläuterung &lt;br /&gt;
!Geräte&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;out_on&amp;lt;/code&amp;gt;&lt;br /&gt;
|&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot; |&amp;lt;code&amp;gt;relay_&amp;lt;ch&amp;gt;&amp;lt;/code&amp;gt;&amp;lt;code&amp;gt;state&amp;lt;/code&amp;gt;&lt;br /&gt;
|Ausgang ein&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot; |alle Shelly mit Relaisausgang&lt;br /&gt;
ShellyBulb&lt;br /&gt;
ShellyRGBW&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;out_off&amp;lt;/code&amp;gt;&lt;br /&gt;
|&lt;br /&gt;
|Ausgang aus&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;button_on&amp;lt;/code&amp;gt;&lt;br /&gt;
|&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot; |&amp;lt;code&amp;gt;button_&amp;lt;ch&amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
|Eingang ein&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot; |ShellyPlug&lt;br /&gt;
ShellyPlugS&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;button_off&amp;lt;/code&amp;gt;&lt;br /&gt;
|&lt;br /&gt;
|Eingang aus&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;input_on&amp;lt;/code&amp;gt;&lt;br /&gt;
|&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot; |&amp;lt;code&amp;gt;input_&amp;lt;ch&amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
|Eingang ein&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot; |alle Shelly mit HW-Eingang, aber nicht Shelly-I-Geräte&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;input_off&amp;lt;/code&amp;gt;&lt;br /&gt;
|&lt;br /&gt;
|Eingang aus&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;input_on&amp;lt;/code&amp;gt;&lt;br /&gt;
|&lt;br /&gt;
| rowspan=&amp;quot;4&amp;quot; |&amp;lt;code&amp;gt;input_&amp;lt;inp&amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
|Eingang ein&lt;br /&gt;
| rowspan=&amp;quot;4&amp;quot; |ShellyDimmer&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;input_off&amp;lt;/code&amp;gt;&lt;br /&gt;
|&lt;br /&gt;
|Eingang aus&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;short_push&amp;lt;/code&amp;gt;&lt;br /&gt;
|&lt;br /&gt;
|kurzer Tastendruck&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;long_push&amp;lt;/code&amp;gt;&lt;br /&gt;
|&lt;br /&gt;
|langer Tastendruck&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;single_push&amp;lt;/code&amp;gt;&lt;br /&gt;
|&lt;br /&gt;
| rowspan=&amp;quot;6&amp;quot; |&amp;lt;code&amp;gt;input_&amp;lt;ch&amp;gt;&amp;lt;/code&amp;gt;&amp;lt;code&amp;gt;input_&amp;lt;ch&amp;gt;_action&amp;lt;/code&amp;gt;&lt;br /&gt;
|kurzer Tastendruck&lt;br /&gt;
| rowspan=&amp;quot;4&amp;quot; |ShellyI3&lt;br /&gt;
ShellyI4 &lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;long_push&amp;lt;/code&amp;gt;&lt;br /&gt;
|&lt;br /&gt;
|langer Tastendruck&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;double_push&amp;lt;/code&amp;gt;&lt;br /&gt;
|&lt;br /&gt;
|zweifacher Tastendruck&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;triple_push&amp;lt;/code&amp;gt;&lt;br /&gt;
|&lt;br /&gt;
|dreifacher Tastendruck&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;short_long_push&amp;lt;/code&amp;gt;&lt;br /&gt;
|&lt;br /&gt;
|Tastersequenz lang-kurz&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot; |ShellyI3&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;long_short_push&amp;lt;/code&amp;gt;&lt;br /&gt;
|&lt;br /&gt;
|Tastersequenz kurz-lang&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;stopped&amp;lt;/code&amp;gt;&lt;br /&gt;
|&lt;br /&gt;
| rowspan=&amp;quot;5&amp;quot; |&amp;lt;code&amp;gt;state&amp;lt;/code&amp;gt;&lt;br /&gt;
|Rollo angehalten&lt;br /&gt;
| rowspan=&amp;quot;5&amp;quot; | Shelly2/2.5/Plus2/Pro2 mode=roller&lt;br /&gt;
&amp;lt;nowiki&amp;gt;*&amp;lt;/nowiki&amp;gt;) nur für Shelly Plus2/Pro2&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;opening&amp;lt;/code&amp;gt;&lt;br /&gt;
|&lt;br /&gt;
|Rollo wird geöffnet&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;closing&amp;lt;/code&amp;gt;&lt;br /&gt;
|&lt;br /&gt;
|Rollo wird geschlossen&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;is_open   *)&amp;lt;/code&amp;gt;&lt;br /&gt;
|&lt;br /&gt;
|Rollo offen (in oberer Endlage)&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;is_closed   *)&amp;lt;/code&amp;gt;&lt;br /&gt;
|&lt;br /&gt;
|Rollo geschlossen (in unterer Endlage)&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;temperature_over&amp;lt;/code&amp;gt;&lt;br /&gt;
|&lt;br /&gt;
|&amp;lt;code&amp;gt;temperature_&amp;lt;ch&amp;gt;_range&amp;lt;/code&amp;gt;&lt;br /&gt;
|Temperatur überschreitet eingestellten Grenzwert&lt;br /&gt;
| rowspan=&amp;quot;5&amp;quot; |ShellyAddOn&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;temperature_under&amp;lt;/code&amp;gt;&lt;br /&gt;
|&lt;br /&gt;
|&amp;lt;code&amp;gt;temperature_&amp;lt;ch&amp;gt;_range&amp;lt;/code&amp;gt;&lt;br /&gt;
|Temperatur unterschreitet eingestellten Grenzwert&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;humidity_over&amp;lt;/code&amp;gt;&lt;br /&gt;
|&lt;br /&gt;
|&amp;lt;code&amp;gt;humidity_&amp;lt;ch&amp;gt;_range&amp;lt;/code&amp;gt;&lt;br /&gt;
|Luftfeuchtigkeit überschreitet eingestellten Grenzwert&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;humidity_under&amp;lt;/code&amp;gt;&lt;br /&gt;
|&lt;br /&gt;
|&amp;lt;code&amp;gt;humidity_&amp;lt;ch&amp;gt;_range&amp;lt;/code&amp;gt;&lt;br /&gt;
|Luftfeuchtigkeit unterschreitet eingestellten Grenzwert&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;tempC&amp;lt;/code&amp;gt;&lt;br /&gt;
|&amp;lt;code&amp;gt;$temperature&amp;lt;/code&amp;gt; &lt;br /&gt;
|&amp;lt;code&amp;gt;temperature&amp;lt;/code&amp;gt;&lt;br /&gt;
|Temperatur in °C&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;voltage_over&amp;lt;/code&amp;gt;&lt;br /&gt;
|&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot; |&amp;lt;code&amp;gt;voltage_range&amp;lt;/code&amp;gt;&lt;br /&gt;
|Spannung überschreitet eingestellten Grenzwert&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot; |ShellyUni&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;voltage_under&amp;lt;/code&amp;gt;&lt;br /&gt;
|&lt;br /&gt;
|Spannung unterschreitet eingestellten Grenzwert&lt;br /&gt;
|-&lt;br /&gt;
| Active_Power_$phase&lt;br /&gt;
|&amp;lt;code&amp;gt;$active_power&amp;lt;/code&amp;gt;&lt;br /&gt;
|&amp;lt;code&amp;gt;Active_Power_&amp;lt;ph&amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
|Änderung Wirkleistung&lt;br /&gt;
| rowspan=&amp;quot;3&amp;quot; |ShellyPro3EM&lt;br /&gt;
|-&lt;br /&gt;
|Voltage_$phase&lt;br /&gt;
|&amp;lt;code&amp;gt;$voltage&amp;lt;/code&amp;gt;&lt;br /&gt;
|&amp;lt;code&amp;gt;Voltage_&amp;lt;ph&amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
| Änderung Spannung&lt;br /&gt;
|-&lt;br /&gt;
|Current_$phase&lt;br /&gt;
|&amp;lt;code&amp;gt;$current&amp;lt;/code&amp;gt;&lt;br /&gt;
|&amp;lt;code&amp;gt;Current_&amp;lt;ph&amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
|Änderung Strom&lt;br /&gt;
|}&lt;br /&gt;
Bei Eintreffen eines Set-Endpoints wird im Shelly-Device das zugeordnete Reading entsprechend gesetzt. Damit kann das Shelly-Ereignis z.B. mit einem &amp;lt;code&amp;gt;notify&amp;lt;/code&amp;gt; ausgewertet werden. Im Anschluss daran holt sich das Modul die aktuellen Daten vom Shelly und setzt das Intervall zurück.&lt;br /&gt;
&lt;br /&gt;
Anmerkung zum ShellyPro3EM: Die Action wird erst bei einer gewissen Änderung des jeweiligen Wertes ausgelöst. Bei kleinen Schwankungen kommen also keine Webhooks in FHEM an. &lt;br /&gt;
&lt;br /&gt;
===== Get-Endpoint =====&lt;br /&gt;
Eine besondere Form stellt der Get-Endpoint dar, mit dem das Shelly-Device in FHEM aufgefordert wird, den Status des Shelly zu holen. Beispiel: &lt;br /&gt;
:&amp;lt;code&amp;gt;http://&amp;lt;FHEM-IP&amp;gt;:&amp;lt;Port&amp;gt;/fhem?cmd=get%20&amp;lt;name&amp;gt;%20status&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Attribut webhook (derzeit nur Gen2)====&lt;br /&gt;
Durch Setzten des Attributes &amp;lt;code&amp;gt;webhook&amp;lt;/code&amp;gt; werden auf dem Shelly die verfügbaren Webhooks automatisiert angelegt (auf der Shelly Oberfläche unter Actions zu sehen). Als Attribut-Wert muss das empfangende FHEMWeb-Device ausgewählt werden. Wird das FHEMWeb-Device mit einem csrf-Token abgesichert, wird der Token in den Webhook eingebunden. Bei Änderungen des Tokens (z.B. bei Neustart von FHEM) werden die entsprechenden Webhooks mit angepasst. &lt;br /&gt;
&lt;br /&gt;
Nach dem Setzen des Attributes müssen die Webhooks mit &amp;lt;code&amp;gt;set myShellyDevice actions create all&amp;lt;/code&amp;gt; angelegt werden.&lt;br /&gt;
&lt;br /&gt;
Die vom Modul angelegten Webhooks erhalten im Shelly einen Namen, beginnend mit einem Unterstrich (&amp;lt;code&amp;gt;_&amp;lt;/code&amp;gt;). Wird das Attribut geändert oder gelöscht, dann werden auch zugehörige Actions geändert bzw. gelöscht. Durch Entfernen des Unterstrichs im Namen der Action kann dieser Mechanismus unterbunden werden.&lt;br /&gt;
&lt;br /&gt;
Das Reading &amp;lt;code&amp;gt;webhook_cnt&amp;lt;/code&amp;gt; zeigt die Anzahl aller auf dem Shelly hinterlegten Webhooks und &amp;lt;code&amp;gt;webhooks_ver&amp;lt;/code&amp;gt; den Versionszähler des Shelly.&lt;br /&gt;
&lt;br /&gt;
Eine Übersicht aller Actions/Webhooks eines Shelly bekommt man für Gen2-Geräte mit:  &lt;br /&gt;
:&amp;lt;code&amp;gt;http://&amp;lt;ip-des-Shelly&amp;gt;/rpc/Webhook.List&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===MQTT===&lt;br /&gt;
MQTT (Message Queue Telemetry Transport) ist ein nachrichtenbasiertes Protokoll, bei dem Geräte (Devices) nicht direkt miteinander, sondern mit einem zentralen MQTT-Server (in alter Nomenklatur &#039;&#039;Broker&#039;&#039; genannt) kommunizieren. Eine kurze Einführung in MQTT findet man auf der Seite [[MQTT Einführung]]. Mit entsprechend gesetzten Attributen lassen sich die Shelly-Aktoren auch steuern ([[MQTT2-Module - Praxisbeispiele#Shelly|Praxisbeispiele zu den MQTT2-Modulen]]), für Anfänger ist das allerdings nicht unbedingt zu empfehlen.&lt;br /&gt;
&lt;br /&gt;
==Links==&lt;br /&gt;
*{{Link2Forum|Topic=118446|LinkText=Support Thread}} zu diesem Modul&lt;br /&gt;
*{{Link2Forum|Topic=137222|LinkText=Entwicklungs Thread}} zur Weiterentwicklung des Moduls, ab Februar 2024&lt;br /&gt;
*[http://www.shelly.com Website des Herstellers der Geräte]&lt;br /&gt;
*[https://community.shelly.cloud Forum des Herstellers (englischsprachig]&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Energieverbrauchsmessung]]&lt;br /&gt;
[[Kategorie:MQTT]]&lt;br /&gt;
[[Kategorie:Bluetooth]]&lt;br /&gt;
[[Kategorie:Schalter (Empfänger)]]&lt;br /&gt;
[[Kategorie:Shelly]]&lt;/div&gt;</summary>
		<author><name>Ph1959de</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=Shelly&amp;diff=40734</id>
		<title>Shelly</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=Shelly&amp;diff=40734"/>
		<updated>2026-01-14T18:54:35Z</updated>

		<summary type="html">&lt;p&gt;Ph1959de: Erste Version der Seite&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Baustelle}}{{Todo|Information zum gesamten Shelly &amp;quot;System&amp;quot; wird gerade umstrukturiert, konsolidiert, erweitert}}&lt;br /&gt;
[[Shelly]] bezeichnet die IoT-Geräte der bulgarischen [https://de.wikipedia.org/wiki/Shelly_Group Shelly Group]. Diese Geräte werden in unterschiedlicher Weise von FHEM unterstützt.&lt;br /&gt;
&lt;br /&gt;
== Überblick ==&lt;br /&gt;
Die generellen Eigenschaften von Shelly Geräten werden in den folgenden Abschnitten kurz erläutert, eine umfangreiche tabellarische Liste ist im Abschnitt [[Shelly#Geräteübersicht|Geräteübersicht]] zusammengestellt. Diese Liste erhebt keinen Anspruch auf Vollständigkeit.&lt;br /&gt;
&lt;br /&gt;
=== Gerätetypen ===&lt;br /&gt;
Arten von verfügbaren Shelly Geräten sind derzeit&lt;br /&gt;
* (Unterputz-)Relais (Aktoren)&lt;br /&gt;
* Energiezähler &lt;br /&gt;
* Sensoren für Temperatur, Bewegung, Feuchtigkeit, Türen/Fenster&lt;br /&gt;
* Smarte Steckdosen / Zwischenstecker&lt;br /&gt;
* Thermostate und Heizkörperventile &lt;br /&gt;
&lt;br /&gt;
=== Kommunikation ===&lt;br /&gt;
Die Kommunikation mit Shelly Geräten erfolgt über WLAN (Access Point und/oder Einbindung in Netzwerk) und/oder Bluetooth (BLE), wobei &lt;br /&gt;
unterschiedliche Kommunikationsprotokolle ([[MQTT]], Modbus, RPC über UDP) zum Einsatz kommen können. Die Geräte können in der Regel über eine Web-Oberfläche angesprochen werden oder auch über eine proprietäre App, wobei die Geräte damit in die Hersteller-Cloud eingebunden werden.&lt;br /&gt;
&lt;br /&gt;
Mit der Einführung der Gen4 (Geräte der 4. Generation) kann auch zwischen einer Firmware mit [[Matter]]- oder [[ZigBee]]-Unterstützung gewählt werden.&lt;br /&gt;
&lt;br /&gt;
=== Gerätegenerationen ===&lt;br /&gt;
Mittlerweile (2024) gibt es die vierte Gerätegeneration, einen detaillierten Überblick über Funktionalität und Unterschiede gibt die Supportseite [https://support.shelly.cloud/de/support/solutions/articles/103000316073-vergleich-von-shelly-gen-1-gen-2-gen-3-gen4-ger%C3%A4ten Vergleich von Shelly gen1...gen4 Geräten].&lt;br /&gt;
&lt;br /&gt;
== Einbindung von Shelly Geräten in FHEM ==&lt;br /&gt;
=== Spezifisches FHEM-Modul ===&lt;br /&gt;
Viele Shelly-Geräte werden durch das Modul [[Shelly (Modul)|36_Shelly.pm]] unterstützt, modulspezifische Details dazu finden sich auf der genannten Modul-Seite.&lt;br /&gt;
&lt;br /&gt;
=== MQTT ===&lt;br /&gt;
Die Einbindung von Shelly Geräten über MQTT ist auf der Seite [[MQTT2-Module_-_Praxisbeispiele#Shelly|MQTT Praxisbeispiele: Shelly]] beschrieben.&lt;br /&gt;
&lt;br /&gt;
=== ... ===&lt;br /&gt;
&lt;br /&gt;
== Geräteübersicht ==&lt;br /&gt;
Liste der aktuell durch das [[Shelly (Modul)|Shelly Modul]] unterstützten Geräte &lt;br /&gt;
{{Todo|falls das wirklich die &#039;&#039;unterstützten&#039;&#039; Geräte sind, sollte die Liste auf die 36_Shelly.pm Seite &amp;quot;wandern&amp;quot;}}&lt;br /&gt;
{{Todo|&#039;&#039;Shelly-Monitor&#039;&#039; muss erläutert / verlinkt werden}}&lt;br /&gt;
(hier nicht aufgeführte Geräte der ersten Generation können zusammen mit dem [[ShellyMonitor]] genutzt werden):&lt;br /&gt;
{| class=&amp;quot;wikitable sortable&amp;quot;&lt;br /&gt;
! data-sort-type=&amp;quot;text&amp;quot; style=&amp;quot;width:150px&amp;quot; |Modell&lt;br /&gt;
! data-sort-type=&amp;quot;text&amp;quot; style=&amp;quot;width:75px&amp;quot; |Typ&lt;br /&gt;
! data-sort-type=&amp;quot;number&amp;quot; style=&amp;quot;width:20px;text-align:center;&amp;quot; |Schalt- kanäle&lt;br /&gt;
! data-sort-type=&amp;quot;number&amp;quot; style=&amp;quot;width:20px;text-align:center;&amp;quot; |Dimm- kanäle&lt;br /&gt;
! data-sort-type=&amp;quot;number&amp;quot; style=&amp;quot;width:20px;text-align:center;&amp;quot; |Mess- kanäle&lt;br /&gt;
! data-sort-type=&amp;quot;number&amp;quot; style=&amp;quot;width:20px;text-align:center;&amp;quot; |Digital Eingänge&lt;br /&gt;
! |Bemerkungen&lt;br /&gt;
|-&lt;br /&gt;
! colspan=&amp;quot;7&amp;quot; |&#039;&#039;&#039;Gen 1&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
|Shelly 1&lt;br /&gt;
|Schalter&lt;br /&gt;
|1&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|1&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|Shelly 1PM&lt;br /&gt;
|Schalter&lt;br /&gt;
|1&lt;br /&gt;
|&lt;br /&gt;
|1&lt;br /&gt;
|1&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|Shelly 1L&lt;br /&gt;
|Schalter&lt;br /&gt;
| 1&lt;br /&gt;
|&lt;br /&gt;
|1&lt;br /&gt;
| 1&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot; |Shelly 2&lt;br /&gt;
| Schalter&lt;br /&gt;
|2&lt;br /&gt;
|&lt;br /&gt;
|1&lt;br /&gt;
|2&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|Rollladenaktor&lt;br /&gt;
|1&lt;br /&gt;
|&lt;br /&gt;
|1&lt;br /&gt;
|2&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot; |Shelly 2.5&lt;br /&gt;
|Schalter&lt;br /&gt;
| 2&lt;br /&gt;
|&lt;br /&gt;
|2&lt;br /&gt;
|2&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|Rollladenaktor&lt;br /&gt;
|1&lt;br /&gt;
|&lt;br /&gt;
|1&lt;br /&gt;
|2&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|Shelly 4Pro&lt;br /&gt;
|Schalter&lt;br /&gt;
|4&lt;br /&gt;
|&lt;br /&gt;
|4&lt;br /&gt;
|4&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|Shelly i3&lt;br /&gt;
| Digitale Eingänge&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|3&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|Shelly EM&lt;br /&gt;
|Leistungsmessung&lt;br /&gt;
|1&lt;br /&gt;
|&lt;br /&gt;
|2&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;[[Shelly 3EM]]&#039;&#039;&#039;&lt;br /&gt;
|Leistungsmessung&lt;br /&gt;
|1&lt;br /&gt;
|&lt;br /&gt;
|3&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;[[Shelly Uni]]&#039;&#039;&#039;&lt;br /&gt;
|Universal&lt;br /&gt;
|2&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|*)&lt;br /&gt;
|1-Wire, 2 potentialfreie Relaisausgänge&lt;br /&gt;
Analogeingänge&lt;br /&gt;
|-&lt;br /&gt;
|Shelly Plug&lt;br /&gt;
Shelly Plug S &lt;br /&gt;
|Schaltsteckdose&lt;br /&gt;
|1&lt;br /&gt;
|&lt;br /&gt;
|1&lt;br /&gt;
|&lt;br /&gt;
|1 Taster&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot; |Shelly RGBW2&lt;br /&gt;
|Dimmer&lt;br /&gt;
|&lt;br /&gt;
|4&lt;br /&gt;
|4&lt;br /&gt;
|1&lt;br /&gt;
|4-fach Aktor&lt;br /&gt;
|-&lt;br /&gt;
|Dimmer&lt;br /&gt;
|&lt;br /&gt;
|1&lt;br /&gt;
| 1&lt;br /&gt;
|1&lt;br /&gt;
|RGBW Controller&lt;br /&gt;
|-&lt;br /&gt;
|Shelly Dimmer2&lt;br /&gt;
|Dimmer&lt;br /&gt;
|&lt;br /&gt;
| 1&lt;br /&gt;
|1&lt;br /&gt;
|2&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|Shelly Duo&lt;br /&gt;
|Leuchte&lt;br /&gt;
|&lt;br /&gt;
|1&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|E27 oder GU10 Fassung&lt;br /&gt;
|-&lt;br /&gt;
|Shelly Vintage&lt;br /&gt;
|Leuchte&lt;br /&gt;
|&lt;br /&gt;
|1&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|Shelly Bulb&lt;br /&gt;
|Leuchte&lt;br /&gt;
|&lt;br /&gt;
|1&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|Modi: weiß oder farbe&lt;br /&gt;
|-&lt;br /&gt;
! colspan=&amp;quot;7&amp;quot; |&#039;&#039;&#039;Shelly Plus&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
|Shelly Plus 1&lt;br /&gt;
&lt;br /&gt;
|Schalter &lt;br /&gt;
|1&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|1&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;[[Shelly Plus 1PM]]&#039;&#039;&#039;&lt;br /&gt;
|Schalter&lt;br /&gt;
|1&lt;br /&gt;
|&lt;br /&gt;
|1&lt;br /&gt;
|1&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot; |&#039;&#039;&#039;[[Shelly Plus 2PM]]&#039;&#039;&#039;&lt;br /&gt;
| Schalter&lt;br /&gt;
|2&lt;br /&gt;
|&lt;br /&gt;
|2&lt;br /&gt;
|2&lt;br /&gt;
|-&lt;br /&gt;
|Rollladenaktor&lt;br /&gt;
|1&lt;br /&gt;
|&lt;br /&gt;
| 1&lt;br /&gt;
|2&lt;br /&gt;
|-&lt;br /&gt;
|Shelly Plus i4&lt;br /&gt;
|Digitale Eingänge&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
| 4&lt;br /&gt;
|AC und DC - Variante&lt;br /&gt;
|-&lt;br /&gt;
| Shelly Plus Plug S&lt;br /&gt;
Shelly Plus Plug IT&lt;br /&gt;
Shelly Plus Plug UK&lt;br /&gt;
Shelly Plus Plug US&lt;br /&gt;
|Schaltsteckdose&lt;br /&gt;
&lt;br /&gt;
|1&lt;br /&gt;
|&lt;br /&gt;
|1&lt;br /&gt;
|&lt;br /&gt;
|1 Taster;&lt;br /&gt;
Varianten V1, V2&lt;br /&gt;
|-&lt;br /&gt;
|Shelly Plus Uni&lt;br /&gt;
|Universal&lt;br /&gt;
| 2&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|*)&lt;br /&gt;
|1-Wire, 2 potentialfreie Relaisausgänge &lt;br /&gt;
Analogeingänge; Neu in 2024&lt;br /&gt;
|-&lt;br /&gt;
|Shelly Plus 0-10V Dimmer&lt;br /&gt;
|Dimmer &lt;br /&gt;
|&lt;br /&gt;
|1&lt;br /&gt;
|1&lt;br /&gt;
|2&lt;br /&gt;
|0-10 V DC Ausgang&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot; |Shelly Plus RGBW&lt;br /&gt;
|Dimmer&lt;br /&gt;
|&lt;br /&gt;
|4&lt;br /&gt;
|4&lt;br /&gt;
|4&lt;br /&gt;
|4-fach Aktor&lt;br /&gt;
|-&lt;br /&gt;
|Dimmer&lt;br /&gt;
|&lt;br /&gt;
|1&lt;br /&gt;
|1&lt;br /&gt;
|1&lt;br /&gt;
|RGBW Controller&lt;br /&gt;
|-&lt;br /&gt;
! colspan=&amp;quot;7&amp;quot; | &#039;&#039;&#039;Shelly Plus Mini&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
|Shelly Plus 1 Mini&lt;br /&gt;
|Schalter&lt;br /&gt;
|1&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|1&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|Shelly Plus 1PM Mini&lt;br /&gt;
|Schalter&lt;br /&gt;
|1&lt;br /&gt;
|&lt;br /&gt;
|1&lt;br /&gt;
|1&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|Shelly PM Mini&lt;br /&gt;
|Leistungsmessung&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
| 1&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
! colspan=&amp;quot;7&amp;quot; |&#039;&#039;&#039;Shelly Pro&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
|Shelly Pro 1&lt;br /&gt;
|Schalter&lt;br /&gt;
|1&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|2&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|Shelly Pro 1PM&lt;br /&gt;
|Schalter&lt;br /&gt;
|1&lt;br /&gt;
|&lt;br /&gt;
|1&lt;br /&gt;
|2&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|Shelly Pro 2&lt;br /&gt;
| Schalter&lt;br /&gt;
|2&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
| 2&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|Shelly Pro 2PM&lt;br /&gt;
|Schalter&lt;br /&gt;
|2&lt;br /&gt;
|&lt;br /&gt;
|2&lt;br /&gt;
|2&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|Shelly Pro Dual&lt;br /&gt;
|Rollladenaktor&lt;br /&gt;
|4&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|4&lt;br /&gt;
|2 Rolladenaktoren&lt;br /&gt;
|-&lt;br /&gt;
|Shelly Pro Dimmer 1PM &lt;br /&gt;
|Dimmer&lt;br /&gt;
|&lt;br /&gt;
|1&lt;br /&gt;
|1&lt;br /&gt;
|2&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|Shelly Pro Dimmer 2PM&lt;br /&gt;
|Dimmer&lt;br /&gt;
|&lt;br /&gt;
| 2&lt;br /&gt;
|2&lt;br /&gt;
|4&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|Shelly Pro 3&lt;br /&gt;
|Schalter&lt;br /&gt;
|3&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|3&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|Shelly Pro 3EM&lt;br /&gt;
|Leistungsmessung&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|3&lt;br /&gt;
|&lt;br /&gt;
|1 Schaltkanal mit Addon&lt;br /&gt;
|-&lt;br /&gt;
|Shelly Pro EM50 &lt;br /&gt;
|Leistungsmessung&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|Shelly Pro 4PM&lt;br /&gt;
|Schalter&lt;br /&gt;
|4&lt;br /&gt;
|&lt;br /&gt;
|4&lt;br /&gt;
|4&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
! colspan=&amp;quot;7&amp;quot; |&#039;&#039;&#039;Shelly Gen3&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| Shelly 1 Gen3&lt;br /&gt;
|Schalter&lt;br /&gt;
|1&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|1&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|Shelly 1PM Gen3&lt;br /&gt;
|Schalter&lt;br /&gt;
|1&lt;br /&gt;
|&lt;br /&gt;
|1&lt;br /&gt;
|1&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|Shelly i4 Gen3&lt;br /&gt;
|Digitale Eingänge&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
| 4&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|Shelly Dimmer 0/1-10V Gen3&lt;br /&gt;
|Dimmer&lt;br /&gt;
|&lt;br /&gt;
| 1&lt;br /&gt;
| 1&lt;br /&gt;
|2&lt;br /&gt;
|0-10V DC oder&lt;br /&gt;
1-10V DC Ausgang&lt;br /&gt;
|-&lt;br /&gt;
! colspan=&amp;quot;7&amp;quot; |&#039;&#039;&#039;Shelly Gen3 Mini&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
|Shelly 1 Mini Gen3&lt;br /&gt;
|Schalter&lt;br /&gt;
|1&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|1&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|Shelly 1PM Mini Gen3 &lt;br /&gt;
|Schalter&lt;br /&gt;
|1&lt;br /&gt;
|&lt;br /&gt;
|1&lt;br /&gt;
|1&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|Shelly PM Mini Gen3&lt;br /&gt;
|Leistungsmessung&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|1&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
! colspan=&amp;quot;7&amp;quot; | &#039;&#039;&#039;Control Panels&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
|Shelly Wall Display&lt;br /&gt;
| Control Panel&lt;br /&gt;
| 1&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
| 1&lt;br /&gt;
|&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Links ==&lt;br /&gt;
* [https://support.shelly.cloud/de/support/solutions/articles/103000316073-vergleich-von-shelly-gen-1-gen-2-gen-3-gen4-ger%C3%A4ten Vergleich von Shelly gen1...gen4 Geräten]&lt;br /&gt;
* [https://shelly-api-docs.shelly.cloud/gen2/changelog/ Shelly Firmware Change Log]&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Shelly]]&lt;/div&gt;</summary>
		<author><name>Ph1959de</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=ShellyMonitor&amp;diff=40733</id>
		<title>ShellyMonitor</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=ShellyMonitor&amp;diff=40733"/>
		<updated>2026-01-14T18:54:27Z</updated>

		<summary type="html">&lt;p&gt;Ph1959de: Erste Version der Seite&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Infobox Modul&lt;br /&gt;
|ModPurpose=... aktualisiert die Readings von [[Shelly]]-Geräten, die ihre Daten im CoIoT-&amp;quot;Standard&amp;quot; im Netzwerk versenden&lt;br /&gt;
|ModType=d&lt;br /&gt;
|ModCmdRef=ShellyMonitor&lt;br /&gt;
|ModForumArea=Sonstige Systeme&lt;br /&gt;
|ModFTopic=117805&lt;br /&gt;
|ModTechName=36_ShellyMonitor.pm&lt;br /&gt;
|ModOwner=gvzdus ({{Link2FU|23999|Forum}})&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
{{Todo|Seite ist bisher nur &amp;quot;Stub&amp;quot;, muss noch in allen Belangen ergänzt werden!}}&lt;br /&gt;
&lt;br /&gt;
Unstrukturierte Sammlung von Informationen:&lt;br /&gt;
* Der Monitor geht nur mit Gen1 (ohne Plus) und im Shelly muss CoIot aktiviert sein.&lt;br /&gt;
* Damit wertet der Monitor die Meldungen aus, die der Shelly per UDP ins LAN schickt. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Links ==&lt;br /&gt;
* [[Shelly]] - allgemeine Informationen zum Shelly &amp;quot;System&amp;quot;&lt;br /&gt;
* [[Shelly (Modul)]] - das Modul 36_Shelly.pm&lt;br /&gt;
* {{Link2Forum|Topic=117805|LinkText=Modul-Thread}} im Forum&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Shelly]]&lt;/div&gt;</summary>
		<author><name>Ph1959de</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=Shelly_(Modul)&amp;diff=40732</id>
		<title>Shelly (Modul)</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=Shelly_(Modul)&amp;diff=40732"/>
		<updated>2026-01-14T18:54:14Z</updated>

		<summary type="html">&lt;p&gt;Ph1959de: Erste Version der Seite&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Infobox Modul&lt;br /&gt;
|ModPurpose=Das Modul stellt ein Interface zur Bedienung von Shelly Devices zur Verfügung&lt;br /&gt;
|ModType=d&lt;br /&gt;
|ModCmdRef=Shelly&lt;br /&gt;
|ModForumArea=Sonstige Systeme&lt;br /&gt;
|ModFTopic=118446&lt;br /&gt;
|ModTechName=36_Shelly.pm&lt;br /&gt;
|ModOwner=Starkstrombastler ({{Link2FU|3884|Forum}}/[[Benutzer Diskussion:Starkstrombastler|Wiki]])&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
== Definition - Anlegen eines Gerätes ==&lt;br /&gt;
* Gerät nach Vorschrift anschließen&lt;br /&gt;
* mit einem WLAN-fähigen Gerät (Laptop, Smartphone, Tablet...; im Folgenden als &#039;&#039;&#039;Laptop&#039;&#039;&#039; bezeichnet) nach dem internen Access Point suchen, der durch das Shelly-Gerät erzeugt wird; typischerweise hat es eine SSID ähnlich wie&lt;br /&gt;
:&amp;lt;code&amp;gt;shelly1-..., shellyswitch-..., shelly4pro-..., &amp;lt;/code&amp;gt; &lt;br /&gt;
* &#039;&#039;&#039;Laptop&#039;&#039;&#039; mit diesem Access Point verbinden; typischerweise bekommt das Gerät dabei die IP-Adresse 192.168.33.2 zugewiesen.&lt;br /&gt;
* im Browser des &#039;&#039;&#039;Laptops&#039;&#039;&#039; die IP-Adresse 192.168.33.1 aufrufen - das ist der Shelly selbst; in der damit angezeigten Weboberfläche kann das Shelly-Gerät konfiguriert werden&lt;br /&gt;
** Shelly ins häusliche WLAN anmelden (mit fester IP-Adresse &amp;lt;shelly-ip&amp;gt; natürlich...)&lt;br /&gt;
** Internen Access Point abschalten (kann auch nach dem nächsten Schritt oder noch später erfolgen)&lt;br /&gt;
** Testen: &#039;&#039;&#039;Laptop&#039;&#039;&#039; wieder mit dem häuslichen WLAN verbinden, und im Browser die Adresse &amp;lt;shelly-ip&amp;gt; aufrufen&lt;br /&gt;
* In FHEM definieren &lt;br /&gt;
:&amp;lt;code&amp;gt;define myShelly Shelly &amp;lt;shelly-ip&amp;gt;&amp;lt;/code&amp;gt; &lt;br /&gt;
* Das Modul setzt bei bekannten Geräten das Attribut &amp;lt;code&amp;gt;model&amp;lt;/code&amp;gt; automatisch. Bei nicht unterstützten Geräten wird das Attribut auf den Wert &amp;lt;code&amp;gt;generic&amp;lt;/code&amp;gt; gesetzt. In diesen Fällen kann das Attribut &amp;lt;code&amp;gt;model&amp;lt;/code&amp;gt; auf der Detailseite des Devices manuell gesetzt werden:&lt;br /&gt;
::&amp;lt;code&amp;gt; attr myShelly model &amp;lt;model&amp;gt; &amp;lt;/code&amp;gt;&lt;br /&gt;
: unterstützte Modelle (ab Datum/Modulversion):&lt;br /&gt;
:* shellyrgbw&lt;br /&gt;
:* shellydimmer&lt;br /&gt;
:* shelly2.5&lt;br /&gt;
:* shelly2&lt;br /&gt;
:* shellyem&lt;br /&gt;
:* shelly3em&lt;br /&gt;
:* shelly4&lt;br /&gt;
:* shellyplug&lt;br /&gt;
:* shelly1&lt;br /&gt;
:* shellybulb&lt;br /&gt;
:* shelly1pm&lt;br /&gt;
:* shellyuni&lt;br /&gt;
:* generic&lt;br /&gt;
&lt;br /&gt;
Falls es sich um einen Shelly2 oder Shelly2.5 handelt, muss ferner das Attribut &amp;lt;code&amp;gt;mode&amp;lt;/code&amp;gt; auf &amp;quot;roller&amp;quot; oder &amp;quot;relay&amp;quot; gesetzt werden. &lt;br /&gt;
&lt;br /&gt;
Mit diesem Modul können alle Daten übertragen und (prinzipiell) alle Konfigurationsänderungen durchgeführt werden, außerdem ist es auf einfachste Weise zu installieren. Das Modul pollt im per Attribut &amp;lt;code&amp;gt;interval&amp;lt;/code&amp;gt; einstellbaren Abstand zyklisch den Aktor auf Statusänderungen (Wert 0 =&amp;gt; kein Polling). Damit der Aktor im Stande ist, irgendwelche Zustandsänderungen &#039;&#039;von sich aus&#039;&#039; an FHEM zu melden, müssen diese als REST-Befehle (also URL-Aufrufe) in der Konfigurationsoberfläche des Shelly-Aktors eingetragen werden. Siehe CommandRef.&lt;br /&gt;
&lt;br /&gt;
Zum Betrieb ist ferner noch zu bemerken, dass das Modul zwar meldet, ob ein Firmware-Update nötig ist, ausgelöst werden muss dieses aber über die Web-Oberfläche des Shelly selber.&lt;br /&gt;
&lt;br /&gt;
== Aktionen / Webhooks ==&lt;br /&gt;
Ab Shelly Firmware 1.5.0 werden Actions unterstützt. Damit besteht die Möglichkeit, dass ein Shelly bei Eintreten bestimmter Ereignisse von sich aus Meldungen an andere Shellies und/oder übergeordnete Systeme wie FHEM absetzt. Dies ist nützlich, um Statusänderungen, die z.B. durch lokal betätigte Tasten entstehen, direkt an FHEM zu übermitteln.&lt;br /&gt;
&lt;br /&gt;
Nachfolgende Beispiele zeigen den Code, der im Shelly unter URL einzutragen ist:&lt;br /&gt;
&lt;br /&gt;
Ausgang (Relais) eines Shelly1 schaltet ein:      &lt;br /&gt;
:&amp;lt;code&amp;gt;http://&amp;lt;FHEM-IP&amp;gt;:&amp;lt;Port&amp;gt;/fhem?cmd=set%20&amp;lt;name&amp;gt;%20out_on&amp;lt;/code&amp;gt;&lt;br /&gt;
hierbei sind: &lt;br /&gt;
:&amp;lt;code&amp;gt;&amp;lt;FHEM-IP&amp;gt;&amp;lt;/code&amp;gt; die IP-Adresse des Servers auf dem FHEM läuft&lt;br /&gt;
:&amp;lt;code&amp;gt;&amp;lt;Port&amp;gt;&amp;lt;/code&amp;gt; die Port-Nummer&lt;br /&gt;
:&amp;lt;code&amp;gt;&amp;lt;name&amp;gt;&amp;lt;/code&amp;gt; der Name des FHEM-Devices&lt;br /&gt;
:&amp;lt;code&amp;gt;%20&amp;lt;/code&amp;gt;    stellt ein Leerzeichen dar&lt;br /&gt;
&lt;br /&gt;
Beispiel 2: Eingang eines Shelly2 wird betätigt:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot;&amp;gt;http://&amp;lt;FHEM-IP&amp;gt;:&amp;lt;Port&amp;gt;/fhem?cmd=set%20&amp;lt;name&amp;gt;%20input_on%20&amp;lt;ch&amp;gt;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
:&amp;lt;code&amp;gt;&amp;lt;ch&amp;gt;&amp;lt;/code&amp;gt;  die Nummer des Schaltkanals (Nummer des Eingangs), z.B. &amp;lt;code&amp;gt;0&amp;lt;/code&amp;gt; oder &amp;lt;code&amp;gt;1&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Beispiel 3: Eingang1 eines ShellyDimmers wird betätigt:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot;&amp;gt;http://&amp;amp;#x3C;FHEM-IP&amp;amp;#x3E;:&amp;amp;#x3C;Port&amp;amp;#x3E;/fhem?cmd=set%20&amp;amp;#x3C;name&amp;amp;#x3E;%20short_push&amp;lt;nowiki/&amp;gt;%20&amp;lt;inp&amp;gt;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
:&amp;lt;code&amp;gt;&amp;lt;nowiki&amp;gt;&amp;lt;inp&amp;gt;&amp;lt;/nowiki&amp;gt;&amp;lt;/code&amp;gt;  Nummer des Eingangs, 0 oder 1 (ShellyDimmer verfügen je Schaltkanal über zwei Eingänge)&lt;br /&gt;
&lt;br /&gt;
Beispiel 4: Wirkleistung eines ShellyPro3EM:  &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot;&amp;gt;http://&amp;lt;FHEM-IP&amp;gt;:&amp;lt;Port&amp;gt;/fhem?fwcsrf=csrf_368985985592099&amp;amp;cmd=set%20Y173%20Active_Power_$phase%20$active_power&amp;lt;/syntaxhighlight&amp;gt; &lt;br /&gt;
:&amp;lt;code&amp;gt;fwcsrf=csrf_368985985592099&amp;lt;/code&amp;gt;    das CSRF-Token (FHEMWeb)&lt;br /&gt;
:&amp;lt;code&amp;gt;$phase&amp;lt;/code&amp;gt;   wird vom Shelly durch a, b oder c ersetzt&lt;br /&gt;
: &amp;lt;code&amp;gt;$active_power&amp;lt;/code&amp;gt;  wird vom Shelly durch die aktuelle Wirkleistung ersetzt&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Endpoints&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
In vorstehenden Beispielen stellt der Teil &amp;lt;code&amp;gt;set%20&amp;lt;name&amp;gt;%20&amp;lt;cmd&amp;gt;&amp;lt;/code&amp;gt; den Endpoint dar, d.h. dies ist der Befehl, der vom Shelly-Device in FHEM verarbeitet werden muss.&lt;br /&gt;
== Bekannte Probleme ==&lt;br /&gt;
&lt;br /&gt;
== Links ==&lt;br /&gt;
* {{Link2Forum|Topic=137222|LinkText=Entwicklungs-Thread}} im Forum&lt;br /&gt;
* {{Link2Forum|Topic=118446|LinkText=Support-Thread}} im Forum &lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Shelly]]&lt;/div&gt;</summary>
		<author><name>Ph1959de</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=Stromb%C3%B6rse&amp;diff=40730</id>
		<title>Strombörse</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=Stromb%C3%B6rse&amp;diff=40730"/>
		<updated>2026-01-13T10:51:50Z</updated>

		<summary type="html">&lt;p&gt;Ph1959de: Tippfehler korrigiert&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Todo|Diese Wiki Seite ist noch in Bearbeitung! &amp;lt;br&amp;gt;Siehe auch {{Link2Forum|Topic=142165|LinkText=HTTPMOD: Aktueller Strompreis EEX Strombörse Leipzig abfragen}}}}&lt;br /&gt;
[[Bild:Screenshot 2024-01-23 122115.png|mini|400px|rechts|EVU_Tibber_connect]]&lt;br /&gt;
Diese Seite beinhaltet Informationen für die Anbindung von Energieversorgungsunternehmen (EVU), die ihre Preise nach der Strombörse z.B. im Stundenrhythmus anbieten.&lt;br /&gt;
&lt;br /&gt;
Als Beispiel wären da:&lt;br /&gt;
# {{Link2Forum|Topic=130407|LinkText=Tibber - im Forum}}&lt;br /&gt;
# {{Link2Forum|Topic=135906|LinkText=aWATTar - im Forum}}&lt;br /&gt;
# {{Link2Forum|Topic=142165|LinkText=EPEX_Spot}}&lt;br /&gt;
&lt;br /&gt;
== 1. Tibber mit Tibber Pulse ==&lt;br /&gt;
Eine momentan verbreitete Methode zur Anbindung an Tibber ist ein Lesekopf auf dem EVU Zähler, der als &amp;quot;Tibber Pulse&amp;quot; vertrieben wird. Damit wird ein bestehender digitaler EVU Zähler ausgelesen und die Werte per LAN zu Tibber gesendet. Über eine API bei Tibber können dann die Verbrauchswerte und Kosten sowie ein Live Stream (WebSocket) abgefragt werden.&lt;br /&gt;
&lt;br /&gt;
Die im Folgenden beschriebenen Beispieldevices bieten die oben genannte Abfrage der API mit WebSocket und [[HTTPMOD]]. Das EVU_Tibber Device dient der Darstellung und Steuerung.&lt;br /&gt;
&lt;br /&gt;
=== Voraussetzungen ===&lt;br /&gt;
* FHEM mit einer installierten MySQL DbLog (FileLog wird nicht unterstützt)&lt;br /&gt;
* KeyValue() (wird hier beschrieben)&lt;br /&gt;
* homeID und personal Token/Access Token/Connect Token&lt;br /&gt;
* Tibber Pulse Lesekopf auf dem digitalen EVU Zähler&lt;br /&gt;
* Man sollte bereits Tibber Kunde sein, da ansonsten nur Demo Daten abgerufen werden können&lt;br /&gt;
&lt;br /&gt;
=== Token ===&lt;br /&gt;
Der Token wird leider unterschiedlich bezeichnet, aber egal ob er personal Token, Access Token oder Connect Token heisst, es bezieht sich auf den gleichen. &lt;br /&gt;
&lt;br /&gt;
Erhältlich ist dieser aktuell (Oktober 24) unter https://developer.tibber.com/ &lt;br /&gt;
&lt;br /&gt;
Dort bitte mit deinen Zugangsdaten einloggen (&amp;quot;Sign In&amp;quot;, oben rechts) und man bekommt den Access Token direkt angezeigt.&lt;br /&gt;
&lt;br /&gt;
=== homeID ===&lt;br /&gt;
Um eine HomeID zu erhalten, muss zuerst der Pulse mit dem Internet bzw. tibber verbunden werden. Wenn die App glücklich ist, gehts hier weiter: &lt;br /&gt;
&lt;br /&gt;
Man loggt sich wieder ein unter https://developer.tibber.com/. Dort geht man dann zum API Explorer. &lt;br /&gt;
&lt;br /&gt;
Leider ist keine der Demo-Abfragen geeignet, die homeID zu erfahren. Dafür gibt man ins linke fenster folgenden Code ein: &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
  viewer {&lt;br /&gt;
    homes {&lt;br /&gt;
      id&lt;br /&gt;
      address {&lt;br /&gt;
        address1&lt;br /&gt;
        postalCode&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Dann startet man mit dem Play-Pfeil und bekommt auf der rechten Seite etwa folgende Ausgabe: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
  &amp;quot;data&amp;quot;: {&lt;br /&gt;
    &amp;quot;viewer&amp;quot;: {&lt;br /&gt;
      &amp;quot;homes&amp;quot;: [&lt;br /&gt;
        {&lt;br /&gt;
          &amp;quot;id&amp;quot;: &amp;quot;1234abcd-12ab-34cd-56ef-1234abcd12ab&lt;br /&gt;
          &amp;quot;address&amp;quot;: {&lt;br /&gt;
            &amp;quot;address1&amp;quot;: &amp;quot;Musterstr.1&amp;quot;,&lt;br /&gt;
            &amp;quot;postalCode&amp;quot;: &amp;quot;12345&amp;quot;&lt;br /&gt;
          }&lt;br /&gt;
        }&lt;br /&gt;
      ]&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Diese ID ist die homeID und kann unten verwendet werden.&lt;br /&gt;
&lt;br /&gt;
=== EVU_Tibber_connect ===&lt;br /&gt;
Das EVU_Tibber_connect fragt mit HTTPMOD Statistiken auf der Tibber API Webseite ab. Zusätzlich wurde in den userReadings die WebSocket Verbindung für die Live Daten integriert.&lt;br /&gt;
* Die WebSocket Verbindung wurde leicht angepasst &#039;&#039;&#039;[[Websocket|von hier]]&#039;&#039;&#039; übernommen&lt;br /&gt;
* Im Forum wurde dies &#039;&#039;&#039;{{Link2Forum|Topic=130407|Message=1298863|LinkText=ab hier}}&#039;&#039;&#039; bearbeitet&lt;br /&gt;
&lt;br /&gt;
==== Verwendete Module ====&lt;br /&gt;
* HTTPMOD&lt;br /&gt;
* DbLog&lt;br /&gt;
* DbRep&lt;br /&gt;
* DOIF&lt;br /&gt;
* KeyValue()&lt;br /&gt;
&lt;br /&gt;
==== Ablage der homeID und des Tokens ====&lt;br /&gt;
Eine Verbindung zur Tibber API mit den &#039;&#039;&#039;tatsächlichen Preisen&#039;&#039;&#039; bekommen nur Kunden, ansonsten kann man eine Demo mit Kosten in Schwedischen Kronen testweise abfragen.&lt;br /&gt;
&lt;br /&gt;
In diesem Beispiel Device kann man seine homeID und sein Token entweder als Attribut ablegen oder man macht einen Eintrag im KeyStore von FHEM, wodurch man nicht so leicht versehentlich seine Daten im Forum postet.&lt;br /&gt;
&lt;br /&gt;
Es dient lediglich der Ablage von Keys, damit sie nicht direkt offen im Datensystem oder im Konfigurationsfile zu finden sind.&lt;br /&gt;
Die Funktion befindet sich in der 99_myUtils.pm .&lt;br /&gt;
&lt;br /&gt;
===== Beispiel KeyValue() =====&lt;br /&gt;
&#039;&#039;&#039;Achtung, der KeyValue ist kein Schutz für Passwort oder Keys!&#039;&#039;&#039;&lt;br /&gt;
Es dient lediglich der Ablage von Keys, damit sie nicht direkt offen im FHEM Device oder im Konfigurationsfile zu finden sind.&lt;br /&gt;
Die Funktion befindet sich in der 99_myUtils .&lt;br /&gt;
&lt;br /&gt;
====== KeyValue() Funktion in der ~/FHEM/99_myUtils.pm ======&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
sub KeyValue {&lt;br /&gt;
    my ($step, $index, $value) = @_;&lt;br /&gt;
    my $key = getUniqueId().$index;&lt;br /&gt;
    my $e_value = &amp;quot;&amp;quot;;&lt;br /&gt;
    my $error;&lt;br /&gt;
&lt;br /&gt;
    if (eval &amp;quot;use Digest::MD5;1&amp;quot;) {&lt;br /&gt;
      $key    = Digest::MD5::md5_hex(unpack &amp;quot;H*&amp;quot;, $key);&lt;br /&gt;
      $key   .= Digest::MD5::md5_hex($key);&lt;br /&gt;
    }&lt;br /&gt;
   &lt;br /&gt;
    if ($step eq &amp;quot;read&amp;quot;) {&lt;br /&gt;
      ($error, $value) = getKeyValue($index);&lt;br /&gt;
&lt;br /&gt;
      if ( defined($error) ) {&lt;br /&gt;
        Log3 $index,3, &amp;quot;$index, can&#039;t read key from FhemUtils/uniqueID: $error&amp;quot;;&lt;br /&gt;
        return undef;&lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
      if ( defined($value) ) {&lt;br /&gt;
        my $dec_value = &#039;&#039;;&lt;br /&gt;
&lt;br /&gt;
        for my $char (map { pack(&#039;C&#039;, hex($_)) } ($value =~ /(..)/g)) {&lt;br /&gt;
          my $decode  = chop($key);&lt;br /&gt;
          $dec_value .= chr(ord($char)^ord($decode));&lt;br /&gt;
          $key        = $decode.$key;&lt;br /&gt;
        }&lt;br /&gt;
        return $dec_value;&lt;br /&gt;
      }&lt;br /&gt;
      else {&lt;br /&gt;
        Log3 $index,3,&amp;quot;$index, no key found in FhemUtils/uniqueID&amp;quot;;&lt;br /&gt;
        return undef;&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if ($step eq &amp;quot;store&amp;quot;) {&lt;br /&gt;
      for my $char (split //, $value) {&lt;br /&gt;
        my $encode = chop($key);&lt;br /&gt;
        $e_value  .= sprintf(&amp;quot;%.2x&amp;quot;,ord($char)^ord($encode));&lt;br /&gt;
        $key       = $encode.$key;&lt;br /&gt;
      }&lt;br /&gt;
      $error = setKeyValue($index, $e_value);&lt;br /&gt;
      return &amp;quot;error while saving key : $error&amp;quot; if(defined($error));&lt;br /&gt;
      return &amp;quot;Key successfully saved in FhemUtils/uniqueID Key $index&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====== Ablegen der homeID und des Token ======&lt;br /&gt;
In der FHEM Kommandozeile legt man seinen Daten wie folgt im KeyValue ab. Hier wären als Beispiel der Demo Zugang von Tibber.&lt;br /&gt;
Das &amp;quot;read&amp;quot; ist dann auch direkt der erste Test, bevor man weiter macht.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
{KeyValue(&amp;quot;store&amp;quot;,&amp;quot;EVU_Tibber_connect_token&amp;quot;,&amp;quot;5K4MVS-OjfWhK_4yrjOlFe1F6kJXPVf7eQYggo8ebAE&amp;quot;)}&lt;br /&gt;
{KeyValue(&amp;quot;store&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;,&amp;quot;96a14971-525a-4420-aae9-e5aedaa129ff&amp;quot;)}&lt;br /&gt;
&lt;br /&gt;
{KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_token&amp;quot;)}&lt;br /&gt;
{KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;)}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Beispiel Attribut für homeID und Token =====&lt;br /&gt;
Oder, sobald das EVU_Tibber_connect Device erstellt wurde, als Attribut.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
attr EVU_Tibber_connect ws_homeId 96a14971-525a-4420-aae9-e5aedaa129ff&lt;br /&gt;
attr EVU_Tibber_connect ws_myId 96a14971-525a-4420-aae9-e5aedaa129ff          &amp;lt;&amp;lt;&amp;lt;&amp;lt; Dies kann wohl eine frei gewählte ID sein, was hier der Einfachheithalber gleich der homeId gesetzt wurde.&lt;br /&gt;
attr EVU_Tibber_connect ws_token 5K4MVS-OjfWhK_4yrjOlFe1F6kJXPVf7eQYggo8ebAE&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== EVU_Tibber_connect RAW Device====&lt;br /&gt;
Das Device EVU_Tibber_connect dient der Verbindung zur Tibber API. Zusätzlich wird für die Abfrage der Live Daten innerhalb der userReadings eine WebSocket Verbindung zu Tibber aufgebaut. Tibber stellt jeweils gegen 14:00 Uhr die Daten für den nächsten Tag bereit, wodurch von 00:00 - 14:00 Uhr diese Information im EVU_Tibber_connect noch nicht angezeigt werden kann.&lt;br /&gt;
Das Device arbeitet über die userReadings mit einem MySQL DbRep Device, was somit eine MySQL DbLog Verwendung im FHEM voraussetzt. &#039;&#039;&#039;Die Verwendung von FileLog ist nicht unterstützt!&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod EVU_Tibber_connect HTTPMOD https://api.tibber.com/v1-beta/gql 0&lt;br /&gt;
attr EVU_Tibber_connect userattr ws_homeId ws_minInterval ws_myId ws_token ws_websocketURL&lt;br /&gt;
attr EVU_Tibber_connect DbLogExclude .*&lt;br /&gt;
attr EVU_Tibber_connect DbLogInclude total_cost_.*,fc0_trigger.*&lt;br /&gt;
attr EVU_Tibber_connect comment Version 2024.01.22 14:00 \&lt;br /&gt;
https://developer.tibber.com/explorer\&lt;br /&gt;
\&lt;br /&gt;
In der FHEM Kommandozeile könnt Ihr das token und die homeID im KeyStore ablegen.\&lt;br /&gt;
Bitte lest dazu das Thema KeyStore im Wiki und legt die Funktion in Eure 99_myUtils.\&lt;br /&gt;
\&lt;br /&gt;
{KeyValue(&amp;quot;store&amp;quot;,&amp;quot;EVU_Tibber_connect_token&amp;quot;,&amp;quot;5K4MVS-OjfWhK_4yrjOlFe1F6kJXPVf7eQYggo8ebAE&amp;quot;)}\&lt;br /&gt;
{KeyValue(&amp;quot;store&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;,&amp;quot;96a14971-525a-4420-aae9-e5aedaa129ff&amp;quot;)}\&lt;br /&gt;
\&lt;br /&gt;
Hierduch kann man einen wirtschaftlicheren trigger_price berechnen lassen:\&lt;br /&gt;
setreading EVU_Tibber compensation_grid &amp;lt;Eure Einspeisevergütung&amp;gt;\&lt;br /&gt;
\&lt;br /&gt;
# Berechnung eines Default Schwellwertes als täglicher Niedrigpreis\&lt;br /&gt;
    (fc_avg - fc_min)  /2 + fc_min\&lt;br /&gt;
\&lt;br /&gt;
# Abschätzung von Wirtschaftlichkeit beim Speicher Laden, falls Tibber zu teuer wird\&lt;br /&gt;
# compensation_grid kann auch auf einen für Euch passenden Wert gesetzt werden\&lt;br /&gt;
    (fc_avg - compensation_grid) *0.85&lt;br /&gt;
attr EVU_Tibber_connect disable 0&lt;br /&gt;
attr EVU_Tibber_connect enableControlSet 1&lt;br /&gt;
attr EVU_Tibber_connect get01-1Name current_currency&lt;br /&gt;
attr EVU_Tibber_connect get01-2Name current_level&lt;br /&gt;
attr EVU_Tibber_connect get01-3Name current_date&lt;br /&gt;
attr EVU_Tibber_connect get01-3OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get01-4Name current_price&lt;br /&gt;
attr EVU_Tibber_connect get01-4OExpr $val *100&lt;br /&gt;
attr EVU_Tibber_connect get01Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {currentSubscription {priceInfo {current {total startsAt currency level}}}}}}&amp;quot; }&lt;br /&gt;
attr EVU_Tibber_connect get01Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get01Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get01JSON data_viewer_home_currentSubscription_priceInfo_current&lt;br /&gt;
attr EVU_Tibber_connect get01Name 01_priceInfo&lt;br /&gt;
attr EVU_Tibber_connect get01URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get02-10Name fc0_03_total&lt;br /&gt;
attr EVU_Tibber_connect get02-11Name fc0_04_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-11OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-12Name fc0_04_total&lt;br /&gt;
attr EVU_Tibber_connect get02-13Name fc0_05_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-13OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-14Name fc0_05_total&lt;br /&gt;
attr EVU_Tibber_connect get02-15Name fc0_06_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-15OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-16Name fc0_06_total&lt;br /&gt;
attr EVU_Tibber_connect get02-17Name fc0_07_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-17OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-18Name fc0_07_total&lt;br /&gt;
attr EVU_Tibber_connect get02-19Name fc0_08_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-19OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-1Name current_date&lt;br /&gt;
attr EVU_Tibber_connect get02-1OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-20Name fc0_08_total&lt;br /&gt;
attr EVU_Tibber_connect get02-21Name fc0_09_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-21OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-22Name fc0_09_total&lt;br /&gt;
attr EVU_Tibber_connect get02-23Name fc0_10_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-23OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-24Name fc0_10_total&lt;br /&gt;
attr EVU_Tibber_connect get02-25Name fc0_11_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-25OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-26Name fc0_11_total&lt;br /&gt;
attr EVU_Tibber_connect get02-27Name fc0_12_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-27OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-28Name fc0_12_total&lt;br /&gt;
attr EVU_Tibber_connect get02-29Name fc0_13_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-29OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-2Name current_price&lt;br /&gt;
attr EVU_Tibber_connect get02-2OExpr $val *100&lt;br /&gt;
attr EVU_Tibber_connect get02-30Name fc0_13_total&lt;br /&gt;
attr EVU_Tibber_connect get02-31Name fc0_14_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-31OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-32Name fc0_14_total&lt;br /&gt;
attr EVU_Tibber_connect get02-33Name fc0_15_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-33OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-34Name fc0_15_total&lt;br /&gt;
attr EVU_Tibber_connect get02-35Name fc0_16_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-35OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-36Name fc0_16_total&lt;br /&gt;
attr EVU_Tibber_connect get02-37Name fc0_17_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-37OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-38Name fc0_17_total&lt;br /&gt;
attr EVU_Tibber_connect get02-39Name fc0_18_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-39OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-3Name fc0_00_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-3OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-40Name fc0_18_total&lt;br /&gt;
attr EVU_Tibber_connect get02-41Name fc0_19_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-41OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-42Name fc0_19_total&lt;br /&gt;
attr EVU_Tibber_connect get02-43Name fc0_20_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-43OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-44Name fc0_20_total&lt;br /&gt;
attr EVU_Tibber_connect get02-45Name fc0_21_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-45OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-46Name fc0_21_total&lt;br /&gt;
attr EVU_Tibber_connect get02-47Name fc0_22_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-47OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-48Name fc0_22_total&lt;br /&gt;
attr EVU_Tibber_connect get02-49Name fc0_23_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-49OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-4Name fc0_00_total&lt;br /&gt;
attr EVU_Tibber_connect get02-50Name fc0_23_total&lt;br /&gt;
attr EVU_Tibber_connect get02-51Name fc1_00_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-51OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-52Name fc1_00_total&lt;br /&gt;
attr EVU_Tibber_connect get02-53Name fc1_01_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-53OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-54Name fc1_01_total&lt;br /&gt;
attr EVU_Tibber_connect get02-55Name fc1_02_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-55OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-56Name fc1_02_total&lt;br /&gt;
attr EVU_Tibber_connect get02-57Name fc1_03_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-57OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-58Name fc1_03_total&lt;br /&gt;
attr EVU_Tibber_connect get02-59Name fc1_04_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-59OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-5Name fc0_01_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-5OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-60Name fc1_04_total&lt;br /&gt;
attr EVU_Tibber_connect get02-61Name fc1_05_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-61OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-62Name fc1_05_total&lt;br /&gt;
attr EVU_Tibber_connect get02-63Name fc1_06_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-63OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-64Name fc1_06_total&lt;br /&gt;
attr EVU_Tibber_connect get02-65Name fc1_07_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-65OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-66Name fc1_07_total&lt;br /&gt;
attr EVU_Tibber_connect get02-67Name fc1_08_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-67OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-68Name fc1_08_total&lt;br /&gt;
attr EVU_Tibber_connect get02-69Name fc1_09_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-69OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-6Name fc0_01_total&lt;br /&gt;
attr EVU_Tibber_connect get02-70Name fc1_09_total&lt;br /&gt;
attr EVU_Tibber_connect get02-71Name fc1_10_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-71OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-72Name fc1_10_total&lt;br /&gt;
attr EVU_Tibber_connect get02-73Name fc1_11_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-73OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-74Name fc1_11_total&lt;br /&gt;
attr EVU_Tibber_connect get02-75Name fc1_12_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-75OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-76Name fc1_12_total&lt;br /&gt;
attr EVU_Tibber_connect get02-77Name fc1_13_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-77OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-78Name fc1_13_total&lt;br /&gt;
attr EVU_Tibber_connect get02-79Name fc1_14_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-79OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-7Name fc0_02_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-7OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-80Name fc1_14_total&lt;br /&gt;
attr EVU_Tibber_connect get02-81Name fc1_15_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-81OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-82Name fc1_15_total&lt;br /&gt;
attr EVU_Tibber_connect get02-83Name fc1_16_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-83OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-84Name fc1_16_total&lt;br /&gt;
attr EVU_Tibber_connect get02-85Name fc1_17_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-85OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-86Name fc1_17_total&lt;br /&gt;
attr EVU_Tibber_connect get02-87Name fc1_18_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-87OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-88Name fc1_18_total&lt;br /&gt;
attr EVU_Tibber_connect get02-89Name fc1_19_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-89OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-8Name fc0_02_total&lt;br /&gt;
attr EVU_Tibber_connect get02-90Name fc1_19_total&lt;br /&gt;
attr EVU_Tibber_connect get02-91Name fc1_20_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-91OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-92Name fc1_20_total&lt;br /&gt;
attr EVU_Tibber_connect get02-93Name fc1_21_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-93OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-94Name fc1_21_total&lt;br /&gt;
attr EVU_Tibber_connect get02-95Name fc1_22_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-95OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-96Name fc1_22_total&lt;br /&gt;
attr EVU_Tibber_connect get02-97Name fc1_23_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-97OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-98Name fc1_23_total&lt;br /&gt;
attr EVU_Tibber_connect get02-9Name fc0_03_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-9OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {currentSubscription {priceInfo {current {total startsAt} today {total startsAt} tomorrow {total startsAt}}}}}}&amp;quot; }&lt;br /&gt;
attr EVU_Tibber_connect get02Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get02Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get02JSON data_viewer_home_currentSubscription_priceInfo&lt;br /&gt;
attr EVU_Tibber_connect get02Name 02_priceAll&lt;br /&gt;
attr EVU_Tibber_connect get02URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get03-1Name nodes_00_00_from&lt;br /&gt;
attr EVU_Tibber_connect get03-1OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get03-2Name nodes_00_00_cost&lt;br /&gt;
attr EVU_Tibber_connect get03-2OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get03-3Name nodes_00_00_consumption&lt;br /&gt;
attr EVU_Tibber_connect get03-4Name nodes_00_01_from&lt;br /&gt;
attr EVU_Tibber_connect get03-4OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get03-50Name nodes_00_01_cost&lt;br /&gt;
attr EVU_Tibber_connect get03-50OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get03-5Name nodes_00_01_cost&lt;br /&gt;
attr EVU_Tibber_connect get03-5OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get03-6Name nodes_00_01_consumption&lt;br /&gt;
attr EVU_Tibber_connect get03-7Name nodes_00_02_from&lt;br /&gt;
attr EVU_Tibber_connect get03-7OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get03-8Name nodes_00_02_cost&lt;br /&gt;
attr EVU_Tibber_connect get03-8OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get03-9Name nodes_00_02_consumption&lt;br /&gt;
attr EVU_Tibber_connect get03Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {consumption(resolution: HOURLY, last: 3) {nodes {from cost consumption }}}}}&amp;quot;}&lt;br /&gt;
attr EVU_Tibber_connect get03Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get03Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get03Name 03_consumption_hour&lt;br /&gt;
attr EVU_Tibber_connect get03RegOpt g&lt;br /&gt;
attr EVU_Tibber_connect get03Regex \{&amp;quot;from&amp;quot;:&amp;quot;([\d+-]+T[\d+:]+\.000[+-][\d+:]+)&amp;quot;,&amp;quot;cost&amp;quot;:(null|\d+\.\d+|0),&amp;quot;consumption&amp;quot;:(null|\d+\.\d+|0)\}&lt;br /&gt;
attr EVU_Tibber_connect get03URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get04-10Name nodes_24_03_from&lt;br /&gt;
attr EVU_Tibber_connect get04-10OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-11Name nodes_24_03_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-11OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-12Name nodes_24_03_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-13Name nodes_24_04_from&lt;br /&gt;
attr EVU_Tibber_connect get04-13OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-14Name nodes_24_04_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-14OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-15Name nodes_24_04_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-16Name nodes_24_05_from&lt;br /&gt;
attr EVU_Tibber_connect get04-16OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-17Name nodes_24_05_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-17OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-18Name nodes_24_05_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-19Name nodes_24_06_from&lt;br /&gt;
attr EVU_Tibber_connect get04-19OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-1Name nodes_24_00_from&lt;br /&gt;
attr EVU_Tibber_connect get04-1OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-20Name nodes_24_06_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-20OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-21Name nodes_24_06_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-22Name nodes_24_07_from&lt;br /&gt;
attr EVU_Tibber_connect get04-22OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-23Name nodes_24_07_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-23OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-24Name nodes_24_07_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-25Name nodes_24_08_from&lt;br /&gt;
attr EVU_Tibber_connect get04-25OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-26Name nodes_24_08_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-26OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-27Name nodes_24_08_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-28Name nodes_24_09_from&lt;br /&gt;
attr EVU_Tibber_connect get04-28OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-29Name nodes_24_09_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-29OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-2Name nodes_24_00_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-2OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-30Name nodes_24_09_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-31Name nodes_24_10_from&lt;br /&gt;
attr EVU_Tibber_connect get04-31OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-32Name nodes_24_10_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-32OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-33Name nodes_24_10_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-34Name nodes_24_11_from&lt;br /&gt;
attr EVU_Tibber_connect get04-34OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-35Name nodes_24_11_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-35OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-36Name nodes_24_11_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-37Name nodes_24_12_from&lt;br /&gt;
attr EVU_Tibber_connect get04-37OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-38Name nodes_24_12_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-38OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-39Name nodes_24_12_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-3Name nodes_24_00_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-40Name nodes_24_13_from&lt;br /&gt;
attr EVU_Tibber_connect get04-40OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-41Name nodes_24_13_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-41OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-42Name nodes_24_13_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-43Name nodes_24_14_from&lt;br /&gt;
attr EVU_Tibber_connect get04-43OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-44Name nodes_24_14_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-44OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-45Name nodes_24_14_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-46Name nodes_24_15_from&lt;br /&gt;
attr EVU_Tibber_connect get04-46OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-47Name nodes_24_15_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-47OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-48Name nodes_24_15_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-49Name nodes_24_16_from&lt;br /&gt;
attr EVU_Tibber_connect get04-49OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-4Name nodes_24_01_from&lt;br /&gt;
attr EVU_Tibber_connect get04-4OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-50Name nodes_24_16_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-50OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-51Name nodes_24_16_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-52Name nodes_24_17_from&lt;br /&gt;
attr EVU_Tibber_connect get04-52OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-53Name nodes_24_17_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-53OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-54Name nodes_24_17_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-55Name nodes_24_18_from&lt;br /&gt;
attr EVU_Tibber_connect get04-55OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-56Name nodes_24_18_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-56OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-57Name nodes_24_18_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-58Name nodes_24_19_from&lt;br /&gt;
attr EVU_Tibber_connect get04-58OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-59Name nodes_24_19_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-59OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-5Name nodes_24_01_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-5OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-60Name nodes_24_19_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-61Name nodes_24_20_from&lt;br /&gt;
attr EVU_Tibber_connect get04-61OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-62Name nodes_24_20_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-62OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-63Name nodes_24_20_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-64Name nodes_24_21_from&lt;br /&gt;
attr EVU_Tibber_connect get04-64OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-65Name nodes_24_21_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-65OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-66Name nodes_24_21_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-67Name nodes_24_22_from&lt;br /&gt;
attr EVU_Tibber_connect get04-67OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-68Name nodes_24_22_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-68OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-69Name nodes_24_22_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-6Name nodes_24_01_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-70Name nodes_24_23_from&lt;br /&gt;
attr EVU_Tibber_connect get04-70OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-71Name nodes_24_23_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-71OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-72Name nodes_24_23_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-7Name nodes_24_02_from&lt;br /&gt;
attr EVU_Tibber_connect get04-7OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-8Name nodes_24_02_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-8OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-9Name nodes_24_02_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {consumption(resolution: HOURLY, last: 24) {nodes {from cost consumption}}}}}&amp;quot;}&lt;br /&gt;
attr EVU_Tibber_connect get04Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get04Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get04Name 04_consumption_hour_24&lt;br /&gt;
attr EVU_Tibber_connect get04RegOpt g&lt;br /&gt;
attr EVU_Tibber_connect get04Regex \{&amp;quot;from&amp;quot;:&amp;quot;([\d+-]+T[\d+:]+\.000[+-][\d+:]+)&amp;quot;,&amp;quot;cost&amp;quot;:(null|\d+\.\d+|0),&amp;quot;consumption&amp;quot;:(null|\d+\.\d+|0)\}&lt;br /&gt;
attr EVU_Tibber_connect get04URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get05AutoNumLen 4&lt;br /&gt;
attr EVU_Tibber_connect get05Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {consumption(resolution: HOURLY, last: 100) {nodes {from cost consumption}}}}}&amp;quot;}&lt;br /&gt;
attr EVU_Tibber_connect get05Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get05Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get05JSON data_viewer_home&lt;br /&gt;
attr EVU_Tibber_connect get05Name 05_consumption_hourly_100&lt;br /&gt;
attr EVU_Tibber_connect get05URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get06-10Name Adresse_07_Telefon&lt;br /&gt;
attr EVU_Tibber_connect get06-11Name Adresse_01_Vorname&lt;br /&gt;
attr EVU_Tibber_connect get06-12Name Adresse_02_Nachname&lt;br /&gt;
attr EVU_Tibber_connect get06-1Name Adresse_03_Strasse&lt;br /&gt;
attr EVU_Tibber_connect get06-2Name Adresse_05_Ort&lt;br /&gt;
attr EVU_Tibber_connect get06-3Name Adresse_06_Land&lt;br /&gt;
attr EVU_Tibber_connect get06-4Name Adresse_05_Ort&lt;br /&gt;
attr EVU_Tibber_connect get06-5Name Adresse_06_Land&lt;br /&gt;
attr EVU_Tibber_connect get06-6Name Adresse_09_latitude&lt;br /&gt;
attr EVU_Tibber_connect get06-7Name Adresse_10_longitude&lt;br /&gt;
attr EVU_Tibber_connect get06-8Name Adresse_04_Plz&lt;br /&gt;
attr EVU_Tibber_connect get06-9Name Adresse_08_eMail&lt;br /&gt;
attr EVU_Tibber_connect get06Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {address {address1 address2 address3 postalCode city country latitude longitude} owner {firstName lastName contactInfo {email mobile}}}}}&amp;quot; }&lt;br /&gt;
attr EVU_Tibber_connect get06Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get06Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get06JSON data_viewer_home&lt;br /&gt;
attr EVU_Tibber_connect get06Name 06_address&lt;br /&gt;
attr EVU_Tibber_connect get06URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get07-10Name nodes_24_03_from&lt;br /&gt;
attr EVU_Tibber_connect get07-10OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-11Name nodes_24_03_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-11OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-12Name nodes_24_03_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-13Name nodes_24_04_from&lt;br /&gt;
attr EVU_Tibber_connect get07-13OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-14Name nodes_24_04_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-14OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-15Name nodes_24_04_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-16Name nodes_24_05_from&lt;br /&gt;
attr EVU_Tibber_connect get07-16OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-17Name nodes_24_05_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-17OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-18Name nodes_24_05_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-19Name nodes_24_06_from&lt;br /&gt;
attr EVU_Tibber_connect get07-19OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-1Name features_realTimeConsumptionEnabled&lt;br /&gt;
attr EVU_Tibber_connect get07-1OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-20Name nodes_24_06_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-20OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-21Name nodes_24_06_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-22Name nodes_24_07_from&lt;br /&gt;
attr EVU_Tibber_connect get07-22OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-23Name nodes_24_07_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-23OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-24Name nodes_24_07_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-25Name nodes_24_08_from&lt;br /&gt;
attr EVU_Tibber_connect get07-25OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-26Name nodes_24_08_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-26OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-27Name nodes_24_08_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-28Name nodes_24_09_from&lt;br /&gt;
attr EVU_Tibber_connect get07-28OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-29Name nodes_24_09_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-29OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-2Name features_id&lt;br /&gt;
attr EVU_Tibber_connect get07-2OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-30Name nodes_24_09_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-31Name nodes_24_10_from&lt;br /&gt;
attr EVU_Tibber_connect get07-31OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-32Name nodes_24_10_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-32OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-33Name nodes_24_10_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-34Name nodes_24_11_from&lt;br /&gt;
attr EVU_Tibber_connect get07-34OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-35Name nodes_24_11_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-35OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-36Name nodes_24_11_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-37Name nodes_24_12_from&lt;br /&gt;
attr EVU_Tibber_connect get07-37OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-38Name nodes_24_12_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-38OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-39Name nodes_24_12_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-3Name nodes_24_00_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-40Name nodes_24_13_from&lt;br /&gt;
attr EVU_Tibber_connect get07-40OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-41Name nodes_24_13_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-41OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-42Name nodes_24_13_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-43Name nodes_24_14_from&lt;br /&gt;
attr EVU_Tibber_connect get07-43OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-44Name nodes_24_14_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-44OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-45Name nodes_24_14_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-46Name nodes_24_15_from&lt;br /&gt;
attr EVU_Tibber_connect get07-46OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-47Name nodes_24_15_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-47OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-48Name nodes_24_15_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-49Name nodes_24_16_from&lt;br /&gt;
attr EVU_Tibber_connect get07-49OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-4Name nodes_24_01_from&lt;br /&gt;
attr EVU_Tibber_connect get07-4OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-50Name nodes_24_16_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-50OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-51Name nodes_24_16_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-52Name nodes_24_17_from&lt;br /&gt;
attr EVU_Tibber_connect get07-52OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-53Name nodes_24_17_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-53OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-54Name nodes_24_17_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-55Name nodes_24_18_from&lt;br /&gt;
attr EVU_Tibber_connect get07-55OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-56Name nodes_24_18_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-56OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-57Name nodes_24_18_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-58Name nodes_24_19_from&lt;br /&gt;
attr EVU_Tibber_connect get07-58OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-59Name nodes_24_19_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-59OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-5Name nodes_24_01_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-5OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-60Name nodes_24_19_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-61Name nodes_24_20_from&lt;br /&gt;
attr EVU_Tibber_connect get07-61OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-62Name nodes_24_20_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-62OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-63Name nodes_24_20_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-64Name nodes_24_21_from&lt;br /&gt;
attr EVU_Tibber_connect get07-64OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-65Name nodes_24_21_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-65OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-66Name nodes_24_21_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-67Name nodes_24_22_from&lt;br /&gt;
attr EVU_Tibber_connect get07-67OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-68Name nodes_24_22_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-68OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-69Name nodes_24_22_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-6Name nodes_24_01_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-70Name nodes_24_23_from&lt;br /&gt;
attr EVU_Tibber_connect get07-70OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-71Name nodes_24_23_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-71OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-72Name nodes_24_23_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-7Name nodes_24_02_from&lt;br /&gt;
attr EVU_Tibber_connect get07-7OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-8Name nodes_24_02_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-8OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-9Name nodes_24_02_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {id features{realTimeConsumptionEnabled} } } }&amp;quot; }&lt;br /&gt;
attr EVU_Tibber_connect get07Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get07Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get07JSON data_viewer_home&lt;br /&gt;
attr EVU_Tibber_connect get07Name 07_realTimeConsumptionEnabled&lt;br /&gt;
attr EVU_Tibber_connect get07RegOpt g&lt;br /&gt;
attr EVU_Tibber_connect get07Regex \{&amp;quot;from&amp;quot;:&amp;quot;([\d+-]+T[\d+:]+\.000[+-][\d+:]+)&amp;quot;,&amp;quot;cost&amp;quot;:(null|\d+\.\d+|0),&amp;quot;consumption&amp;quot;:(null|\d+\.\d+|0)\}&lt;br /&gt;
attr EVU_Tibber_connect get07URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect group PV Steuerung EVU&lt;br /&gt;
attr EVU_Tibber_connect icon stromzaehler_icon&lt;br /&gt;
attr EVU_Tibber_connect replacement01Mode expression&lt;br /&gt;
attr EVU_Tibber_connect replacement01Regex %%token%%&lt;br /&gt;
attr EVU_Tibber_connect replacement01Value {KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_token&amp;quot;)}&lt;br /&gt;
attr EVU_Tibber_connect replacement02Mode expression&lt;br /&gt;
attr EVU_Tibber_connect replacement02Regex %%homeID%%&lt;br /&gt;
attr EVU_Tibber_connect replacement02Value {KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;)}&lt;br /&gt;
attr EVU_Tibber_connect requestData { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {currentSubscription {priceInfo {current {total energy tax startsAt }}}}}}&amp;quot; }&lt;br /&gt;
attr EVU_Tibber_connect requestHeader1 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect requestHeader2 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect room Strom-&amp;gt;Boerse&lt;br /&gt;
attr EVU_Tibber_connect showBody 1&lt;br /&gt;
attr EVU_Tibber_connect showError 1&lt;br /&gt;
attr EVU_Tibber_connect sortby 313&lt;br /&gt;
attr EVU_Tibber_connect timeout 30&lt;br /&gt;
attr EVU_Tibber_connect userReadings ws_connect:ws_cmd:.connect {\&lt;br /&gt;
    my $hash = $defs{$name};;\&lt;br /&gt;
    my $devState = DevIo_IsOpen($hash);;\&lt;br /&gt;
    return &amp;quot;Device already open&amp;quot; if (defined($devState));;\&lt;br /&gt;
    \&lt;br /&gt;
    # establish connection to websocket\&lt;br /&gt;
    # format must also include portnumber if a path is to be specified\&lt;br /&gt;
    $hash-&amp;gt;{DeviceName} = AttrVal($name, &amp;quot;ws_websocketURL&amp;quot;, &amp;quot;wss:echo.websocket.org:443&amp;quot;);;\&lt;br /&gt;
    \&lt;br /&gt;
    # special headers needed for Tibber, see also Developer Tools in Browser\&lt;br /&gt;
    $hash-&amp;gt;{header}{&#039;Sec-WebSocket-Protocol&#039;} = &#039;graphql-transport-ws&#039;;;\&lt;br /&gt;
    $hash-&amp;gt;{header}{&#039;Host&#039;} = &#039;websocket-api.tibber.com&#039;;;\&lt;br /&gt;
    $hash-&amp;gt;{header}{&#039;Origin&#039;} = &#039;https://developer.tibber.com&#039;;;\&lt;br /&gt;
    \&lt;br /&gt;
    # callback function when &amp;quot;select()&amp;quot; signals data for us\&lt;br /&gt;
    # websocket Ping/Pongs are treated in DevIo but still call this function\&lt;br /&gt;
    $hash-&amp;gt;{directReadFn} = sub () {\&lt;br /&gt;
        my $hash = $defs{$name};;\&lt;br /&gt;
        \&lt;br /&gt;
        # we can read without closing the DevIo, because select() signalled data\&lt;br /&gt;
        my $buf = DevIo_SimpleRead($hash);;\&lt;br /&gt;
        \&lt;br /&gt;
        # if read fails, close device\&lt;br /&gt;
        if(!defined($buf)) {\&lt;br /&gt;
            DevIo_CloseDev($hash);;\&lt;br /&gt;
            $buf = &amp;quot;not_connected&amp;quot;;;\&lt;br /&gt;
        }\&lt;br /&gt;
        \&lt;br /&gt;
        #Log(3, &amp;quot;$name:$reading: websocket data: &amp;gt;&amp;gt;&amp;gt;$buf&amp;lt;&amp;lt;&amp;lt;&amp;quot;);;\&lt;br /&gt;
        \&lt;br /&gt;
        # only update our reading if buffer is not empty and if last update is older than minInterval\&lt;br /&gt;
        if ($buf ne &amp;quot;&amp;quot;) {\&lt;br /&gt;
            my $websocketDataAge = ReadingsAge($name, &amp;quot;ws_websocketData&amp;quot;, 3600);;\&lt;br /&gt;
            my $minInterval = AttrVal($name, &amp;quot;ws_minInterval&amp;quot;, 0);;\&lt;br /&gt;
            my $isNext = ($buf =~ /.*id.*type.*next.*payload.*data.*liveMeasurement.*/s);;\&lt;br /&gt;
            \&lt;br /&gt;
            readingsBeginUpdate($hash);;\&lt;br /&gt;
            readingsBulkUpdate($hash, &amp;quot;ws_websocketData&amp;quot;, &amp;quot;$buf&amp;quot;) if ($isNext &amp;amp;&amp;amp; $websocketDataAge &amp;gt; $minInterval);;\&lt;br /&gt;
            readingsBulkUpdate($hash, &amp;quot;ws_websocketData&amp;quot;, &amp;quot;$buf&amp;quot;) if (!$isNext);;\&lt;br /&gt;
            readingsEndUpdate($hash, 1);;\&lt;br /&gt;
            #Log(3, &amp;quot;$name:$reading: websocket data written to reading&amp;quot;);;\&lt;br /&gt;
        }\&lt;br /&gt;
    };;\&lt;br /&gt;
    \&lt;br /&gt;
    # open DevIo websocket\&lt;br /&gt;
    DevIo_OpenDev($hash, 0, undef, sub(){\&lt;br /&gt;
        my ($hash, $error) = @_;;\&lt;br /&gt;
        return &amp;quot;$error&amp;quot; if ($error);;\&lt;br /&gt;
        \&lt;br /&gt;
        my $token = AttrVal($name, &amp;quot;ws_token&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
           $token = KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_token&amp;quot;) if ($token eq &amp;quot;???&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
        DevIo_SimpleWrite($hash, &#039;{&amp;quot;type&amp;quot;:&amp;quot;connection_init&amp;quot;,&amp;quot;payload&amp;quot;:{&amp;quot;token&amp;quot;:&amp;quot;&#039;.$token.&#039;&amp;quot;}}&#039;, 2);;\&lt;br /&gt;
    });;\&lt;br /&gt;
    readingsBulkUpdate($hash, &amp;quot;ws_websocketData&amp;quot;, &amp;quot;&amp;quot;);;\&lt;br /&gt;
    #Log(3, &amp;quot;$name:$reading: websocket data cleared in reading&amp;quot;);;\&lt;br /&gt;
      \&lt;br /&gt;
    return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
ws_disconnect:ws_cmd:.disconnect {\&lt;br /&gt;
    Log(3, &amp;quot;$name: disconnect&amp;quot;);;\&lt;br /&gt;
    my $hash = $defs{$name};;\&lt;br /&gt;
    RemoveInternalTimer($hash);;\&lt;br /&gt;
    DevIo_SimpleRead($hash);;\&lt;br /&gt;
    DevIo_CloseDev($hash);;\&lt;br /&gt;
\&lt;br /&gt;
    return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
ws_onDisconnect {\&lt;br /&gt;
    my $myState = ReadingsVal($name, &amp;quot;state&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
    my $myData = ReadingsVal($name, &amp;quot;ws_websocketData&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
    return if ($myState ne &amp;quot;disconnected&amp;quot; and $myData ne &amp;quot;not_connected&amp;quot;);;\&lt;br /&gt;
    \&lt;br /&gt;
	## timer callback function, called after a few seconds to initiate a reconnect\&lt;br /&gt;
	my $timerFunction = sub() {\&lt;br /&gt;
		my ($arg) = @_;;\&lt;br /&gt;
		my $hash = $defs{$name};;\&lt;br /&gt;
		my $devState = DevIo_IsOpen($hash);;\&lt;br /&gt;
		\&lt;br /&gt;
		# only re-connect if device is not connected\&lt;br /&gt;
		readingsSingleUpdate($hash, &amp;quot;ws_cmd&amp;quot;, &amp;quot;connect&amp;quot;, 1) if (!defined($devState));;\&lt;br /&gt;
	};;\&lt;br /&gt;
	RemoveInternalTimer($name.$reading.&#039;Timer&#039;);;\&lt;br /&gt;
	\&lt;br /&gt;
	# wait a random time before reconnect (exponential backoff TBD):\&lt;br /&gt;
	my $rwait = int(rand(200)) + 30;;\&lt;br /&gt;
	InternalTimer(gettimeofday() + $rwait, $timerFunction, $name.$reading.&#039;Timer&#039;);;\&lt;br /&gt;
	\&lt;br /&gt;
	#set cmd to a new value, informs user and allows to retrigger when timer expires\&lt;br /&gt;
	my $hash = $defs{$name};;\&lt;br /&gt;
	readingsBulkUpdate($hash, &amp;quot;ws_cmd&amp;quot;, &amp;quot;reconnect attempt in $rwait seconds&amp;quot;);;\&lt;br /&gt;
	\&lt;br /&gt;
	return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
ws_onTimeout:ws_websocketData:.* {\&lt;br /&gt;
	#re-establish websocket connection if no data received in the past ten minutes\&lt;br /&gt;
	#but only if our reading &amp;quot;ws_cmd&amp;quot; was not set to the value &amp;quot;disconnect&amp;quot;\&lt;br /&gt;
\&lt;br /&gt;
        #Log(3, &amp;quot;$name:$reading: websocket data triggert ws_onTimeout&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
	#timeout in seconds when the connection is considered dead\&lt;br /&gt;
	my $timeoutTime = 600;;\&lt;br /&gt;
	\&lt;br /&gt;
	# function to execute when timeout expired\&lt;br /&gt;
	# defining the function here in the userReading, allows us to insert variables directly\&lt;br /&gt;
	my $timerFunction = sub() {\&lt;br /&gt;
		my ($arg) = @_;;\&lt;br /&gt;
		my $hash = $defs{$name};;\&lt;br /&gt;
		my $rCmd = ReadingsVal($name, &amp;quot;ws_cmd&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
		my $age  = ReadingsAge($name, &amp;quot;ws_websocketData&amp;quot;, 0);;\&lt;br /&gt;
		\&lt;br /&gt;
		Log(3, &amp;quot;$name: onTimeoutTimer triggered &amp;gt;&amp;gt;$arg&amp;lt;&amp;lt;&amp;quot;);;\&lt;br /&gt;
		\&lt;br /&gt;
		#do not do anything further if disconnect is on purpose\&lt;br /&gt;
		if ( $rCmd eq &amp;quot;disconnect&amp;quot; ) {\&lt;br /&gt;
			Log(3, &amp;quot;$name: ws_cmd was set to disconnect&amp;quot;);;\&lt;br /&gt;
			return;;\&lt;br /&gt;
		}\&lt;br /&gt;
		\&lt;br /&gt;
		# for whatever reason, we triggered to soon (80%)\&lt;br /&gt;
		if ( $age &amp;lt; $timeoutTime*0.8 ) {\&lt;br /&gt;
			Log(3, &amp;quot;$name: ws_websocketData is not outdated&amp;quot;);;\&lt;br /&gt;
			return;;\&lt;br /&gt;
		}\&lt;br /&gt;
		\&lt;br /&gt;
		DevIo_CloseDev($hash);;\&lt;br /&gt;
		Log(3, &amp;quot;$name: onTimeoutTimer closed DevIo...&amp;quot;);;\&lt;br /&gt;
		\&lt;br /&gt;
		readingsSingleUpdate($hash, &amp;quot;ws_cmd&amp;quot;, &amp;quot;connect&amp;quot;, 1);;\&lt;br /&gt;
		Log(3, &amp;quot;$name: onTimeoutTimer set ws_cmd to value &#039;connect&#039;&amp;quot;);;\&lt;br /&gt;
	};;\&lt;br /&gt;
\&lt;br /&gt;
        #Log(3, &amp;quot;$name:$reading: onTimeout function defined&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
	#remove/cancel previous timers, because we got fresh data and countdown starts again\&lt;br /&gt;
	RemoveInternalTimer($name.$reading.&#039;Timer&#039;);;\&lt;br /&gt;
	\&lt;br /&gt;
	#set timer to expire and execute function defined above, give special arg as identifier\&lt;br /&gt;
	InternalTimer(gettimeofday() + $timeoutTime, $timerFunction, $name.$reading.&#039;Timer&#039;);;\&lt;br /&gt;
	\&lt;br /&gt;
	return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
ws_onConnectionAck:ws_websocketData:.*connection_ack.* {\&lt;br /&gt;
    #ws_websocketData contains the string &amp;quot;connection_ack&amp;quot;\&lt;br /&gt;
    Log(3, &amp;quot;$name:$reading: got connection ack&amp;quot;);;\&lt;br /&gt;
    \&lt;br /&gt;
    # do not proceed if connection is lost\&lt;br /&gt;
    my $hash = $defs{$name};;\&lt;br /&gt;
    my $devState = DevIo_IsOpen($hash);;\&lt;br /&gt;
    return &amp;quot;Device not open&amp;quot; if (!defined($devState));;\&lt;br /&gt;
\&lt;br /&gt;
    readingsBulkUpdate($hash, &amp;quot;ws_cmd&amp;quot;, &amp;quot;got connection ack&amp;quot;);;\&lt;br /&gt;
    \&lt;br /&gt;
    my $homeId = AttrVal($name, &amp;quot;ws_homeId&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
       $homeId = KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;) if ($homeId eq &amp;quot;???&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
    my $myId = AttrVal($name, &amp;quot;ws_myId&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
       $myId = KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;) if ($myId eq &amp;quot;???&amp;quot;);;\&lt;br /&gt;
    \&lt;br /&gt;
    # build the query, do it in pieces, the comma at the end caused perl errors\&lt;br /&gt;
    # so we put it together in this not very elegant way\&lt;br /&gt;
    my $json = &#039;{ &amp;quot;id&amp;quot;:&amp;quot;&#039;. $myId .&#039;&amp;quot;, &amp;quot;type&amp;quot;:&amp;quot;subscribe&amp;quot;&#039;.&amp;quot;, &amp;quot;;;\&lt;br /&gt;
    $json .= &#039;&amp;quot;payload&amp;quot;:{&#039;;;\&lt;br /&gt;
    $json .= &#039;&amp;quot;variables&amp;quot;:{}&#039;.&amp;quot;, &amp;quot;;;\&lt;br /&gt;
    $json .= &#039;&amp;quot;extensions&amp;quot;:{}&#039;.&amp;quot;, &amp;quot;;;\&lt;br /&gt;
    $json .= &#039;&amp;quot;query&amp;quot;:&amp;quot;subscription { liveMeasurement( homeId: \&amp;quot;&#039;.$homeId.&#039;\&amp;quot; ) &#039;;;\&lt;br /&gt;
    #$json .= &#039;{ timestamp power accumulatedConsumption accumulatedCost currency minPower averagePower maxPower signalStrength }}&amp;quot;&#039;;;\&lt;br /&gt;
    $json .= &#039;{ timestamp power lastMeterConsumption accumulatedConsumption accumulatedProduction &#039;;;\&lt;br /&gt;
    $json .= &#039;accumulatedProductionLastHour accumulatedCost accumulatedReward currency minPower averagePower maxPower &#039;;;\&lt;br /&gt;
    $json .= &#039;powerProduction powerReactive powerProductionReactive minPowerProduction maxPowerProduction lastMeterProduction &#039;;;\&lt;br /&gt;
    $json .= &#039;powerFactor voltagePhase1 voltagePhase2 voltagePhase3 signalStrength }}&amp;quot;&#039;;;\&lt;br /&gt;
    $json .= &#039;}}&#039;;;\&lt;br /&gt;
    \&lt;br /&gt;
    #send the string via websocket as ASCII\&lt;br /&gt;
    Log(3, &amp;quot;$name:$reading: sending JSON: &amp;gt;&amp;gt;&amp;gt;$json&amp;lt;&amp;lt;&amp;lt;&amp;quot;);;\&lt;br /&gt;
    DevIo_SimpleWrite($hash, $json, 2);;\&lt;br /&gt;
        \&lt;br /&gt;
    return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
ws_onNextLiveMeasurement:ws_websocketData:.*next.*payload.*data.*liveMeasurement.* {\&lt;br /&gt;
    #websocketData contains next-live-measurement-data\&lt;br /&gt;
    my $val = ReadingsVal($name, &amp;quot;ws_websocketData&amp;quot;, &amp;quot;{}&amp;quot;);;\&lt;br /&gt;
    my %res = %{json2nameValue($val, undef, undef, &amp;quot;payload_data_liveMeasurement.*&amp;quot;)};;\&lt;br /&gt;
    \&lt;br /&gt;
    my $ret = &amp;quot;got values for:\n&amp;quot;;;\&lt;br /&gt;
    foreach my $k (sort keys %res) {\&lt;br /&gt;
        $ret .= &amp;quot;$k\n&amp;quot;;;\&lt;br /&gt;
        readingsBulkUpdate($hash, makeReadingName($k), $res{$k});;\&lt;br /&gt;
    }\&lt;br /&gt;
    return $ret;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_TIMESTAMP:nodes_00_00_cost.* {\&lt;br /&gt;
my ($timestamp,$value) = 2x0;;\&lt;br /&gt;
my $tmp = 0;;\&lt;br /&gt;
\&lt;br /&gt;
for (my $loop_last = 0;; $loop_last &amp;lt;= 2;; $loop_last++) {\&lt;br /&gt;
  $timestamp = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_00_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_from&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_00_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_cost&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $value ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    # Eintragen der Kosten für die Stunde\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_cost&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
    # Eintragen des Verbrauchs für die Stunde\&lt;br /&gt;
    $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_00_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_consumption&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_consumption&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
  } else {\&lt;br /&gt;
    $tmp = &amp;quot;null&amp;quot;;;\&lt;br /&gt;
  }\&lt;br /&gt;
} # end for\&lt;br /&gt;
if ($tmp eq &amp;quot;null&amp;quot;) {\&lt;br /&gt;
  $timestamp = $tmp\&lt;br /&gt;
}\&lt;br /&gt;
$timestamp;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_TIMESTAMP:nodes_24_00_cost.* {\&lt;br /&gt;
my ($timestamp,$value) = 2x0;;\&lt;br /&gt;
my $tmp = 0;;\&lt;br /&gt;
\&lt;br /&gt;
for (my $loop_last = 0;; $loop_last &amp;lt;= 23;; $loop_last++) {\&lt;br /&gt;
  $timestamp = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_24_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_from&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_24_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_cost&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $value ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    # Eintragen der Kosten für die Stunde\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_cost&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
    # Eintragen des Verbrauchs für die Stunde\&lt;br /&gt;
    $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_24_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_consumption&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_consumption&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
  } else {\&lt;br /&gt;
    $tmp = &amp;quot;null&amp;quot;;;\&lt;br /&gt;
  }\&lt;br /&gt;
} # end for\&lt;br /&gt;
if ($tmp eq &amp;quot;null&amp;quot;) {\&lt;br /&gt;
  $timestamp = $tmp\&lt;br /&gt;
}\&lt;br /&gt;
$timestamp;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_TIMESTAMP:05_consumption_hourly_100-0001.* {\&lt;br /&gt;
my ($timestamp,$value) = 2x0;;\&lt;br /&gt;
\&lt;br /&gt;
for (my $loop_last = 1;; $loop_last &amp;lt;= 300;; $loop_last += 3) {\&lt;br /&gt;
\&lt;br /&gt;
  $timestamp =  ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;05_consumption_hourly_100-&amp;quot;.sprintf(&amp;quot;%04d&amp;quot;,$loop_last+2),&amp;quot;null&amp;quot;);; # timestamp\&lt;br /&gt;
  $timestamp =~ s/T/ /g ;;\&lt;br /&gt;
  $timestamp =  substr($timestamp,0,19);;\&lt;br /&gt;
  $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;05_consumption_hourly_100-&amp;quot;.sprintf(&amp;quot;%04d&amp;quot;,$loop_last+1),&amp;quot;null&amp;quot;);;  # cost\&lt;br /&gt;
\&lt;br /&gt;
  if ( $value ne &amp;quot;&amp;quot; ) {\&lt;br /&gt;
    $value = round($value,4);;\&lt;br /&gt;
# print $timestamp.&amp;quot; &amp;quot;.$value.&amp;quot;\n&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
    # Eintragen der Kosten für die Stunde\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_cost&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
    # Eintragen des Verbrauchs für die Stunde\&lt;br /&gt;
    $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;05_consumption_hourly_100-&amp;quot;.sprintf(&amp;quot;%04d&amp;quot;,$loop_last),&amp;quot;null&amp;quot;);;  # consumption\&lt;br /&gt;
    $value = round($value,4);;\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_consumption&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
  }\&lt;br /&gt;
} # end for\&lt;br /&gt;
\&lt;br /&gt;
$timestamp;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_cost_avg:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Tages Wertes\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_TIMESTAMP&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(avg(VALUE)*100 AS DECIMAL(4,2)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND TIMESTAMP &amp;gt;= curdate() ;;&amp;quot;) ;;\&lt;br /&gt;
  } else {\&lt;br /&gt;
    ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_cost_avg&amp;quot;,&amp;quot;null&amp;quot;)\&lt;br /&gt;
  }\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_cost_min:nodes_TIMESTAMP.* {\&lt;br /&gt;
##  Ermittlung des minimal Wertes\&lt;br /&gt;
if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_TIMESTAMP&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
  ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(min(VALUE)*100 AS DECIMAL(4,2)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND TIMESTAMP &amp;gt;= curdate() ;;&amp;quot;) ;;\&lt;br /&gt;
} else {\&lt;br /&gt;
  ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_cost_min&amp;quot;,&amp;quot;null&amp;quot;)\&lt;br /&gt;
}\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_cost_max:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Ermittlung des maximal Wertes\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_TIMESTAMP&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(max(VALUE)*100 AS DECIMAL(4,2)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND TIMESTAMP &amp;gt;= curdate() ;;&amp;quot;) ;;\&lt;br /&gt;
  } else {\&lt;br /&gt;
    ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_cost_max&amp;quot;,&amp;quot;null&amp;quot;)\&lt;br /&gt;
  }\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_consumption_day:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Tages Verbrauches\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(sum(VALUE) AS DECIMAL(10,4)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_consumption&#039;\&lt;br /&gt;
             AND date(TIMESTAMP) = curdate() ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_consumption_month:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Monats Verbrauches\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(sum(VALUE) AS DECIMAL(10,4)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_consumption&#039;\&lt;br /&gt;
             AND YEAR(TIMESTAMP) = YEAR(curdate())\&lt;br /&gt;
             AND MONTH(TIMESTAMP) = MONTH(curdate()) ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_consumption_year:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Jahres Verbrauches\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(sum(VALUE) AS DECIMAL(10,4)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_consumption&#039;\&lt;br /&gt;
             AND YEAR(TIMESTAMP) = YEAR(curdate()) ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_avg:current_price.* {\&lt;br /&gt;
## Berechnung des durchschnitt Wertes\&lt;br /&gt;
  my $fc_avg = 0;;\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
\&lt;br /&gt;
  if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {       ## Der nächste Tag ist bereits da\&lt;br /&gt;
    if (AttrVal(&amp;quot;$NAME&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
      Log 3, &amp;quot;$NAME cmd_1  : Tibber Daten für fc1 sind bereits da&amp;quot;;;\&lt;br /&gt;
    }\&lt;br /&gt;
    $fc = 1;;\&lt;br /&gt;
  } # end if\&lt;br /&gt;
\&lt;br /&gt;
    for (my $j=0;;$j&amp;lt;=$fc;;$j++){\&lt;br /&gt;
      for (my $k=0;;$k&amp;lt;=23;;$k++) {                                          ## Summe  berechnen\&lt;br /&gt;
        $fc_avg += ReadingsVal(&amp;quot;$NAME&amp;quot;,sprintf(&amp;quot;fc%d_%02d_total&amp;quot;,$j,$k),0);;\&lt;br /&gt;
        } # end $k\&lt;br /&gt;
    } # end $j\&lt;br /&gt;
\&lt;br /&gt;
    return round($fc_avg / (24 *(1+$fc)) *100 ,2);;                     ## Durchschnitt berechnen\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_med:current_price.* {\&lt;br /&gt;
## Berechnung des median Wertes\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT\&lt;br /&gt;
             cast( (   (SUBSTRING_INDEX(SUBSTRING_INDEX(group_concat(VALUE order by VALUE), &#039;,&#039;, floor(1+((count(VALUE)-1) / 2)))  , &#039;,&#039;, -1))\&lt;br /&gt;
                       + (SUBSTRING_INDEX(SUBSTRING_INDEX(group_concat(VALUE order by VALUE), &#039;,&#039;, ceiling(1+((count(VALUE)-1) / 2))), &#039;,&#039;, -1))\&lt;br /&gt;
                     )/2 *100\&lt;br /&gt;
             AS DECIMAL(4,2))\&lt;br /&gt;
           FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND (   READING=&#039;fc0_total&#039; AND TIMESTAMP &amp;gt;= DATE_FORMAT(NOW(), &#039;%Y-%m-%d 00:00:00&#039;)\&lt;br /&gt;
                     OR READING=&#039;fc1_total&#039; AND TIMESTAMP &amp;gt;= DATE_FORMAT(NOW() + INTERVAL 1 DAY, &#039;%Y-%m-%d 00:00:00&#039;) ) ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_min:current_price.* {\&lt;br /&gt;
##  Ermittlung des minimal Wertes\&lt;br /&gt;
  my $fc_min = 0;;\&lt;br /&gt;
  my $fc_tmp = 0;;\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
\&lt;br /&gt;
  if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {       ## Der nächste Tag ist bereits da\&lt;br /&gt;
    $fc = 1;;\&lt;br /&gt;
  } # end if\&lt;br /&gt;
\&lt;br /&gt;
    for (my $j=0;;$j&amp;lt;=$fc;;$j++){\&lt;br /&gt;
      for (my $k=0;;$k&amp;lt;=23;;$k++) {\&lt;br /&gt;
        $fc_tmp = ReadingsVal(&amp;quot;$NAME&amp;quot;,sprintf(&amp;quot;fc%d_%02d_total&amp;quot;,$j,$k),0);;\&lt;br /&gt;
  \&lt;br /&gt;
        if (($fc_tmp &amp;lt; $fc_min) or ($j == 0 and $k == 0)) {\&lt;br /&gt;
          $fc_min = $fc_tmp;;\&lt;br /&gt;
        }\&lt;br /&gt;
      } # end $k\&lt;br /&gt;
    } # end $j\&lt;br /&gt;
\&lt;br /&gt;
    return round($fc_min *100 ,2);;                             ## Von Euro auf Cent umrechnen\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_max:current_price.* {\&lt;br /&gt;
##  Ermittlung des minimal Wertes\&lt;br /&gt;
  my $fc_max = 0;;\&lt;br /&gt;
  my $fc_tmp = 0;;\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
\&lt;br /&gt;
  if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {       ## Der nächste Tag ist bereits da\&lt;br /&gt;
    $fc = 1;;\&lt;br /&gt;
  } # end if\&lt;br /&gt;
\&lt;br /&gt;
    for (my $j=0;;$j&amp;lt;=$fc;;$j++){\&lt;br /&gt;
      for (my $k=0;;$k&amp;lt;=23;;$k++) {\&lt;br /&gt;
        $fc_tmp = ReadingsVal(&amp;quot;$NAME&amp;quot;,sprintf(&amp;quot;fc%d_%02d_total&amp;quot;,$j,$k),0);;\&lt;br /&gt;
  \&lt;br /&gt;
        if (($fc_tmp &amp;gt; $fc_max) or ($j == 0 and $k == 0)) {\&lt;br /&gt;
          $fc_max = $fc_tmp;;\&lt;br /&gt;
        }\&lt;br /&gt;
      } # end $k\&lt;br /&gt;
    } # end $j\&lt;br /&gt;
\&lt;br /&gt;
    return round($fc_max *100 ,2);;                             ## Von Euro auf Cent umrechnen\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_trigger_price:fc_avg.* {\&lt;br /&gt;
## fc_trigger_price:[fc_avg|compensation_grid].* {\&lt;br /&gt;
  my $fc_avg = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_avg&amp;quot;,0);;\&lt;br /&gt;
  my $fc_min = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_min&amp;quot;,0);;\&lt;br /&gt;
\&lt;br /&gt;
  # Berechnung eines Default Schwellwertes als täglicher Niedrigpreis\&lt;br /&gt;
  my $price_level = round( ($fc_avg - $fc_min)/2 + $fc_min , 1);;\&lt;br /&gt;
  \&lt;br /&gt;
  # Abschätzung von Wirtschaftlichkeit beim Speicher Laden, falls Tibber zu teuer wird\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;compensation_grid&amp;quot;,0) != 0 ) {\&lt;br /&gt;
    my $price_level_battery = round( ($fc_avg - ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;compensation_grid&amp;quot;,0)) *0.85 , 1) ;;\&lt;br /&gt;
    if ( $price_level &amp;gt; $price_level_battery ) {\&lt;br /&gt;
      $price_level = $price_level_battery;;\&lt;br /&gt;
    }\&lt;br /&gt;
  }\&lt;br /&gt;
$price_level;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_start:fc_trigger_price.* {\&lt;br /&gt;
  my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
  my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for (my $loop_hour = $hour;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour)) ;;\&lt;br /&gt;
      }\&lt;br /&gt;
    } # end  for loop_hour\&lt;br /&gt;
\&lt;br /&gt;
  return(&amp;quot;null&amp;quot;);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_stop:fc0_trigger_start.* {\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
  my $loop_hour = 0;;\&lt;br /&gt;
  my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my $fc_trigger_start = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_trigger_start&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  my $fc_trigger_stop = $fc_trigger_start;;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $fc_trigger_start ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    $fc_trigger_start =~ /(\d\d):/;; $fc_trigger_start = $1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for ($loop_hour = $fc_trigger_start;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$fc.&amp;quot;_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        $fc_trigger_stop = sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour) ;;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour));;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
     # wechsel zum nächsten Tag\&lt;br /&gt;
     if ( $loop_hour == 23 and $fc == 0 ) {\&lt;br /&gt;
       $fc = 1;;\&lt;br /&gt;
       $loop_hour = -1;;\&lt;br /&gt;
     }\&lt;br /&gt;
\&lt;br /&gt;
    } # end for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
 \&lt;br /&gt;
  return($fc_trigger_stop);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger:fc0_trigger_stop.* {\&lt;br /&gt;
\&lt;br /&gt;
  # Setzen des Triggers für die aktuelle Stunde\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;current_price&amp;quot;,100)  &amp;lt; ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) ) {\&lt;br /&gt;
    return(&amp;quot;on&amp;quot;)\&lt;br /&gt;
  } else {\&lt;br /&gt;
    return(&amp;quot;off&amp;quot;)\&lt;br /&gt;
  }\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc1_trigger_start:fc_trigger_price.* {\&lt;br /&gt;
  if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {\&lt;br /&gt;
    my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
    # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
    my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for (my $loop_hour = 0;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour)) ;;\&lt;br /&gt;
      }\&lt;br /&gt;
    } # end  for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
  return(&amp;quot;null&amp;quot;);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc1_trigger_stop:fc0_trigger_start.* {\&lt;br /&gt;
  my $loop_hour = 0;;\&lt;br /&gt;
  my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my $fc_trigger_start = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_trigger_start&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  my $fc_trigger_stop = $fc_trigger_start;;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $fc_trigger_start ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    $fc_trigger_start =~ /(\d\d):/;; $fc_trigger_start = $1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for ($loop_hour = $fc_trigger_start;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        $fc_trigger_stop = sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour) ;;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour));;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
    } # end for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
 \&lt;br /&gt;
  return($fc_trigger_stop);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_trigger_max:fc_avg.* {\&lt;br /&gt;
  my $fc_avg = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_avg&amp;quot;,0);;\&lt;br /&gt;
  my $fc_max = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_max&amp;quot;,0);;\&lt;br /&gt;
\&lt;br /&gt;
  # Berechnung eines Schwellwertes als täglichen Maximalpreises\&lt;br /&gt;
  my $price_level = round( ($fc_max - $fc_avg)/2 + $fc_avg , 0);;\&lt;br /&gt;
  \&lt;br /&gt;
$price_level;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_max_start:fc_trigger_max.* {\&lt;br /&gt;
  my $fc_trigger_max = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_max&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger_max Fensters\&lt;br /&gt;
  my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
  my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for (my $loop_hour = $hour;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;gt; $fc_trigger_max ) {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour)) ;;\&lt;br /&gt;
      }\&lt;br /&gt;
    } # end  for loop_hour\&lt;br /&gt;
\&lt;br /&gt;
  return(&amp;quot;null&amp;quot;);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_max_stop:fc0_trigger_max_start.* {\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
  my $loop_hour = 0;;\&lt;br /&gt;
  my $fc_trigger_max = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_max&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my $fc_trigger_max_start = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_trigger_max_start&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  my $fc_trigger_max_stop = $fc_trigger_max_start;;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $fc_trigger_max_start ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    $fc_trigger_max_start =~ /(\d\d):/;; $fc_trigger_max_start = $1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for ($loop_hour = $fc_trigger_max_start;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$fc.&amp;quot;_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;gt; $fc_trigger_max ) {\&lt;br /&gt;
        $fc_trigger_max_stop = sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour) ;;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour));;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
     # wechsel zum nächsten Tag\&lt;br /&gt;
     if ( $loop_hour == 23 and $fc == 0 ) {\&lt;br /&gt;
       $fc = 1;;\&lt;br /&gt;
       $loop_hour = -1;;\&lt;br /&gt;
     }\&lt;br /&gt;
\&lt;br /&gt;
    } # end for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
 \&lt;br /&gt;
  return($fc_trigger_max_stop);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_max:fc0_trigger_max_stop.* {\&lt;br /&gt;
\&lt;br /&gt;
  # Setzen des maximum Triggers für die aktuelle Stunde\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;current_price&amp;quot;,0)  &amp;gt; ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_max&amp;quot;,0) ) {\&lt;br /&gt;
    return(&amp;quot;on&amp;quot;)\&lt;br /&gt;
  } else {\&lt;br /&gt;
    return(&amp;quot;off&amp;quot;)\&lt;br /&gt;
  }\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc1_trigger_max_start:fc_trigger_max.* {\&lt;br /&gt;
  if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {\&lt;br /&gt;
    my $fc_trigger_max = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_max&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
    # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
    my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for (my $loop_hour = 0;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;gt; $fc_trigger_max ) {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour)) ;;\&lt;br /&gt;
      }\&lt;br /&gt;
    } # end  for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
  return(&amp;quot;null&amp;quot;);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc1_trigger_max_stop:fc0_trigger_max_start.* {\&lt;br /&gt;
  my $loop_hour = 0;;\&lt;br /&gt;
  my $fc_trigger_max = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_max&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my $fc_trigger_max_start = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_trigger_max_start&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  my $fc_trigger_max_stop = $fc_trigger_max_start;;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $fc_trigger_max_start ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    $fc_trigger_max_start =~ /(\d\d):/;; $fc_trigger_max_start = $1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for ($loop_hour = $fc_trigger_max_start;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;gt; $fc_trigger_max ) {\&lt;br /&gt;
        $fc_trigger_max_stop = sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour) ;;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour));;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
    } # end for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
 \&lt;br /&gt;
  return($fc_trigger_max_stop);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
total_cost_day:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Tages Wertes\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT CAST(SUM(VALUE) AS DECIMAL(8,2)) AS VALUE\&lt;br /&gt;
           FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND date(TIMESTAMP) = curdate() ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
total_cost_month:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des monats Wertes\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT CAST(SUM(VALUE) AS DECIMAL(8,2)) AS VALUE\&lt;br /&gt;
           FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND YEAR(TIMESTAMP) = YEAR(curdate())\&lt;br /&gt;
             AND MONTH(TIMESTAMP) = MONTH(curdate()) ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
total_cost_year:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Jahres Wertes\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT CAST(SUM(VALUE) AS DECIMAL(8,2)) AS VALUE\&lt;br /&gt;
           FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND YEAR(TIMESTAMP) = YEAR(curdate()) ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_DbLog:fc0_00_total.* {\&lt;br /&gt;
my ($timestamp,$date,$hour,$value,$loop_fc_next) = 5x0;;\&lt;br /&gt;
\&lt;br /&gt;
for (my $loop_fc = 0;; $loop_fc &amp;lt;= 1;; $loop_fc++) {\&lt;br /&gt;
  $loop_fc_next = $loop_fc +1;;\&lt;br /&gt;
  $date = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$loop_fc.&amp;quot;_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  if ($date ne &amp;quot;null&amp;quot;) {\&lt;br /&gt;
    $date =~ /([\d+-]+)/;; $date = $1 ;;\&lt;br /&gt;
    for (my $loop_hour = 0;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $hour = sprintf(&amp;quot;%02d&amp;quot;,$loop_hour);;\&lt;br /&gt;
      $timestamp = $date.&amp;quot; &amp;quot;.$hour.&amp;quot;:00:00&amp;quot;;;\&lt;br /&gt;
      $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$loop_fc.&amp;quot;_&amp;quot;.$hour.&amp;quot;_total&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
      ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
                        INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                          VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;fc&amp;quot;.$loop_fc.&amp;quot;_total&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
                        ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                          VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
    }\&lt;br /&gt;
    if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$loop_fc.&amp;quot;_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) eq ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$loop_fc_next.&amp;quot;_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;)) {\&lt;br /&gt;
      fhem(&amp;quot;deletereading $NAME fc&amp;quot;.$loop_fc_next.&amp;quot;_.*&amp;quot;);;\&lt;br /&gt;
    }\&lt;br /&gt;
  } else {\&lt;br /&gt;
      fhem(&amp;quot;deletereading $NAME fc1_.*&amp;quot;);;\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
ReadingsTimestamp(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
}&lt;br /&gt;
attr EVU_Tibber_connect verbose 0&lt;br /&gt;
attr EVU_Tibber_connect ws_minInterval 54&lt;br /&gt;
attr EVU_Tibber_connect ws_websocketURL wss:websocket-api.tibber.com:443/v1-beta/gql/subscriptions&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== EVU_Tibber RAW Device ====&lt;br /&gt;
Das Device dient der Anzeige und Steuerung des EVU_Tibber_connect Devices.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod EVU_Tibber DOIF ## Startup Befehle für den WebSocket vom EVU_Tibber_connect\&lt;br /&gt;
init\&lt;br /&gt;
{ \&lt;br /&gt;
  Log(0, &amp;quot;$SELF 0   init       : ▶️  EVU_Tibber init&amp;quot;);;\&lt;br /&gt;
  fhem(&amp;quot;setreading EVU_Tibber_connect ws_cmd connect&amp;quot;);; \&lt;br /&gt;
  if (AttrVal(&amp;quot;$SELF&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
      Log 3, &amp;quot;$SELF 0   WebSocket  : ▶️  EVU_Tibber_connect start Websocket&amp;quot;;;\&lt;br /&gt;
  }\&lt;br /&gt;
  Log(0, &amp;quot;$SELF 0   init       : 🏁 EVU_Tibber init done&amp;quot;);;\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
################################################################################################################\&lt;br /&gt;
## EVU_Tibber_connect start/stop WebSocket\&lt;br /&gt;
EVU_Tibber_connect_ws\&lt;br /&gt;
{if( !([$SELF:state] eq &amp;quot;off&amp;quot;)                                           ## DOIF enabled\&lt;br /&gt;
#    or  [$SELF:ui_command_2]                                             ## Hier wird das uiTable select ausgewertet\&lt;br /&gt;
    and [$SELF:ui_command_2] ne &amp;quot;---&amp;quot;\&lt;br /&gt;
   ) {\&lt;br /&gt;
\&lt;br /&gt;
    fhem(&amp;quot;setreading EVU_Tibber_connect ws_cmd &amp;quot;.[?$SELF:ui_command_2]);; \&lt;br /&gt;
\&lt;br /&gt;
    set_Reading(&amp;quot;ui_command_2&amp;quot;,&amp;quot;---&amp;quot;);;                                   ## Hier wird das uiTable select wieder zurückgesetzt, ansonsten\&lt;br /&gt;
                                                                         ## kann das Kommando nicht sofort wiederholt werden\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
################################################################################################################\&lt;br /&gt;
## 1 Scheduling für das Abholen von Tibber Daten\&lt;br /&gt;
1_EVU_Tibber_PriceInfo\&lt;br /&gt;
{if( !([$SELF:state] eq &amp;quot;off&amp;quot;)                                           ## DOIF enabled\&lt;br /&gt;
    and\&lt;br /&gt;
    (     [:03]                                                          ## Kurz nach jeder vollen Stunde\&lt;br /&gt;
    )\&lt;br /&gt;
    or [$SELF:ui_command_1] eq &amp;quot;1_EVU_Tibber_PriceInfo&amp;quot;                  ## Hier wird das uiTable select ausgewertet\&lt;br /&gt;
   ) {\&lt;br /&gt;
\&lt;br /&gt;
  ::CommandGet(undef, &amp;quot;EVU_Tibber_connect 01_priceInfo&amp;quot;);;                ## Preis für die aktuelle Stunde\&lt;br /&gt;
  ::CommandGet(undef, &amp;quot;EVU_Tibber_connect 03_consumption_hour&amp;quot;);;         ## Kosten der letzen drei Stunden\&lt;br /&gt;
  if (AttrVal(&amp;quot;$SELF&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
      Log 3, &amp;quot;$SELF 1   PriceInfo  : Abfrage von Tibber&amp;quot;;;\&lt;br /&gt;
    }\&lt;br /&gt;
\&lt;br /&gt;
    set_Reading(&amp;quot;ui_command_1&amp;quot;,&amp;quot;---&amp;quot;);;                                   ## Hier wird das uiTable select wieder zurückgesetzt, ansonsten\&lt;br /&gt;
                                                                         ## kann das Kommando nicht sofort wiederholt werden\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
################################################################################################################\&lt;br /&gt;
## 2 Scheduling für das Abholen der Tibber Preise\&lt;br /&gt;
2_EVU_Tibber_PriceAll\&lt;br /&gt;
{if( !([$SELF:state] eq &amp;quot;off&amp;quot;)                                           ## DOIF enabled\&lt;br /&gt;
    and\&lt;br /&gt;
    ([00:03] or [14:03]                                                  ## Ab 14:00 Uhr gibt es die Preise für den nächsten Tag\&lt;br /&gt;
    )\&lt;br /&gt;
    or [$SELF:ui_command_1] eq &amp;quot;2_EVU_Tibber_PriceAll&amp;quot;                   ## Hier wird das uiTable select ausgewertet\&lt;br /&gt;
   ) {\&lt;br /&gt;
\&lt;br /&gt;
  ::CommandGet(undef, &amp;quot;EVU_Tibber_connect 02_priceAll&amp;quot;);;\&lt;br /&gt;
  if (AttrVal(&amp;quot;$SELF&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
      Log 3, &amp;quot;$SELF 2   priceAll   : Abfrage von Tibber für den nächsten Tag&amp;quot;;;\&lt;br /&gt;
    }\&lt;br /&gt;
\&lt;br /&gt;
    set_Reading(&amp;quot;ui_command_1&amp;quot;,&amp;quot;---&amp;quot;);;                                   ## Hier wird das uiTable select wieder zurückgesetzt, ansonsten\&lt;br /&gt;
                                                                         ## kann das Kommando nicht sofort wiederholt werden\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
################################################################################################################\&lt;br /&gt;
## 2 Erstellen des Diagramms im uiTable\&lt;br /&gt;
3_EVU_Tibber_Diagramm\&lt;br /&gt;
{if( !([$SELF:state] eq &amp;quot;off&amp;quot;)                                           ## DOIF enabled\&lt;br /&gt;
    and\&lt;br /&gt;
    (     [00:05] or [14:05]                                             ## Kurz nach Mitternacht\&lt;br /&gt;
    )\&lt;br /&gt;
    or [$SELF:ui_command_1] eq &amp;quot;3_EVU_Tibber_Diagramm&amp;quot;                   ## Hier wird das uiTable select ausgewertet\&lt;br /&gt;
   ) {\&lt;br /&gt;
\&lt;br /&gt;
  my (@out) = (&amp;quot;&amp;quot;) x 2;;\&lt;br /&gt;
  my $timestamp;;\&lt;br /&gt;
\&lt;br /&gt;
  for (my $j=0;;$j&amp;lt;=1;;$j++){\&lt;br /&gt;
\&lt;br /&gt;
    if (ReadingsVal(&amp;quot;EVU_Tibber_connect&amp;quot;,sprintf(&amp;quot;fc%d_00_startsAt&amp;quot;,$j),&amp;quot;null&amp;quot;) eq &amp;quot;null&amp;quot;) {       ## Der nächste Tag ist noch nicht da\&lt;br /&gt;
      if (AttrVal(&amp;quot;$SELF&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
        Log 3, &amp;quot;$SELF 3   Diagramm   : Tibber Daten für fc&amp;quot;.$j.&amp;quot; sind noch nicht da&amp;quot;;;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
      $timestamp = POSIX::strftime(&amp;quot;%Y-%m-%d&amp;quot;,localtime(time+86400));;    ## Setze das Datum auf morgen\&lt;br /&gt;
      for (my $k=0;;$k&amp;lt;=24;;$k++) {\&lt;br /&gt;
        $out[$j] .= sprintf(&amp;quot;%s_%02d:00:00 0.0\n&amp;quot;, $timestamp, $k);;      ## Die Daten mit 0 ergänzen\&lt;br /&gt;
      }\&lt;br /&gt;
      $j = 2;;                                                            ## Aus der Schleife springen\&lt;br /&gt;
\&lt;br /&gt;
    } else {\&lt;br /&gt;
\&lt;br /&gt;
      for (my $i=0;;$i&amp;lt;=23;;$i++){\&lt;br /&gt;
        $timestamp = ReadingsVal(&amp;quot;EVU_Tibber_connect&amp;quot;,sprintf(&amp;quot;fc%d_%02d_startsAt&amp;quot;,$j,$i),&amp;quot;&amp;quot;);;\&lt;br /&gt;
        $timestamp =~ s/ /_/g;;\&lt;br /&gt;
        $out[$j] .=$timestamp.&amp;quot; &amp;quot;.::round(ReadingsVal(&amp;quot;EVU_Tibber_connect&amp;quot;,sprintf(&amp;quot;fc%d_%02d_total&amp;quot;,$j, $i),0)*100,1).&amp;quot;\n&amp;quot;;;\&lt;br /&gt;
      } # End $i\&lt;br /&gt;
\&lt;br /&gt;
    } # End if\&lt;br /&gt;
  } # End $j\&lt;br /&gt;
  \&lt;br /&gt;
  if (AttrVal(&amp;quot;$SELF&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
      Log 3, &amp;quot;$SELF 3   Diagramm   : Werte für das Diagramm&amp;quot;;;\&lt;br /&gt;
      print($out[0]);;\&lt;br /&gt;
      print($out[1]);;\&lt;br /&gt;
    }\&lt;br /&gt;
  ## Die readings current_price und current_level dienen hier nur als Trigger Events, und müssen für\&lt;br /&gt;
  ## jedes Diagramm unterschiedlich sein, die Daten werden über $out[] bereit gestellt !!\&lt;br /&gt;
  ::DOIF_modify_card_data(&amp;quot;EVU_Tibber&amp;quot;,&amp;quot;EVU_Tibber_connect&amp;quot;,&amp;quot;current_price&amp;quot;,&amp;quot;bar1day&amp;quot;,0,$out[0]);;\&lt;br /&gt;
  ::DOIF_modify_card_data(&amp;quot;EVU_Tibber&amp;quot;,&amp;quot;EVU_Tibber_connect&amp;quot;,&amp;quot;current_level&amp;quot;,&amp;quot;bar1day&amp;quot;,-86400,$out[1]);;\&lt;br /&gt;
  ## Mit der Abfrage 03_consumption_hour werden die Trigger erneut ausgelöst\&lt;br /&gt;
  ::CommandGet(undef, &amp;quot;EVU_Tibber_connect 01_priceInfo&amp;quot;);;                ## Kosten der letzen drei Stunden\&lt;br /&gt;
##  fhem(&amp;quot;get EVU_Tibber_connect 01_priceInfo&amp;quot;);;                           ## Kosten der letzen drei Stunden\&lt;br /&gt;
\&lt;br /&gt;
    set_Reading(&amp;quot;ui_command_1&amp;quot;,&amp;quot;---&amp;quot;);;                                   ## Hier wird das uiTable select wieder zurückgesetzt, ansonsten\&lt;br /&gt;
                                                                         ## kann das Kommando nicht sofort wiederholt werden\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
&lt;br /&gt;
attr EVU_Tibber DbLogExclude .*&lt;br /&gt;
attr EVU_Tibber comment Version 2023.12.06 13:00 \&lt;br /&gt;
Dieses Device benötigt EVU_Tibber_connect als Verbindung zu Tibber.&lt;br /&gt;
attr EVU_Tibber disable 0&lt;br /&gt;
attr EVU_Tibber group PV Steuerung EVU&lt;br /&gt;
attr EVU_Tibber icon stromzaehler_icon&lt;br /&gt;
attr EVU_Tibber room Strom-&amp;gt;Boerse&lt;br /&gt;
attr EVU_Tibber sortby 315&lt;br /&gt;
attr EVU_Tibber uiState {\&lt;br /&gt;
package ui_Table;;\&lt;br /&gt;
  $TABLE = &amp;quot;style=&#039;width:100%;;&#039;&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
  $TD{0..6}{0} = &amp;quot;style=&#039;border-top-style:solid;;border-bottom-style:solid;;border-left-style:solid;;border-left-width:2px;;border-right-style:solid;;border-color:darkgreen;;border-top-width:2px;;border-bottom-width:2px;;border-right-width:1px;;width:36%;;font-weight:bold;;&#039;&amp;quot;;;\&lt;br /&gt;
  $TD{0..6}{1..4} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
  $TD{0..6}{5} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
&amp;quot;Kommando&amp;lt;dd&amp;gt;Auswahl&amp;lt;/dd&amp;gt;&amp;quot; |widget([$SELF:ui_command_1],&amp;quot;uzsuDropDown,---,1_EVU_Tibber_PriceInfo,2_EVU_Tibber_PriceAll,3_EVU_Tibber_Diagramm&amp;quot;) |&amp;quot;LiveMessurement &amp;lt;br&amp;gt;&amp;quot;.widget([$SELF:ui_command_2],&amp;quot;uzsuDropDown,---,connect,disconnect&amp;quot;)|&amp;quot;&amp;quot;|[EVU_Tibber_connect:ws_cmd]\&lt;br /&gt;
\&lt;br /&gt;
&amp;quot;Strompreis&amp;lt;br&amp;gt;&amp;quot;.card([EVU_Tibber_connect:current_price:bar1day],undef,undef,0,60,90,0,&amp;quot;fc0  &amp;quot;.::ReadingsVal(Device(),&amp;quot;current_currency&amp;quot;,&amp;quot;&amp;quot;),undef,&amp;quot;1&amp;quot;,&amp;quot;130,,,,,,220&amp;quot;).card([EVU_Tibber_connect:current_level:bar1day],undef,undef,0,60,90,0,&amp;quot;fc1  &amp;quot;.::ReadingsVal(Device(),&amp;quot;current_currency&amp;quot;,&amp;quot;&amp;quot;),undef,&amp;quot;1&amp;quot;,&amp;quot;130,,,,,,220&amp;quot;)|\&lt;br /&gt;
&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;nächste 3h&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;quot;.Price(1).&amp;quot;&amp;lt;br&amp;gt;&amp;quot;.Price(2).&amp;quot;&amp;lt;br&amp;gt;&amp;quot;.Price(3)|&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;Statistik fc0&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;quot;.::ReadingsVal(Device(),&amp;quot;fc_min&amp;quot;,0).&amp;quot; min&amp;lt;br&amp;gt;&amp;quot;.::ReadingsVal(Device(),&amp;quot;fc_avg&amp;quot;,0).&amp;quot; avg&amp;lt;br&amp;gt;&amp;quot;.::ReadingsVal(Device(),&amp;quot;fc_med&amp;quot;,0).&amp;quot; med&amp;lt;br&amp;gt;&amp;quot;.::ReadingsVal(Device(),&amp;quot;fc_max&amp;quot;,0).&amp;quot; max&amp;quot;|\&lt;br /&gt;
&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;Trigger fc0&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;Basis &amp;quot;.widget([EVU_Tibber_connect:compensation_grid],&amp;quot;selectnumbers,0,0.1,12,1,lin&amp;quot;).&amp;quot;&amp;lt;br&amp;gt;&amp;quot;.Format(&amp;quot;trigger_0&amp;quot;)|\&lt;br /&gt;
&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;Trigger fc1&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;quot;.Format(&amp;quot;trigger_1&amp;quot;)&lt;br /&gt;
attr EVU_Tibber uiTable {\&lt;br /&gt;
package ui_Table;;\&lt;br /&gt;
  $TABLE = &amp;quot;style=&#039;width:100%;;&#039;&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
   $TD{0..18}{0} = &amp;quot;style=&#039;border-top-style:solid;;border-bottom-style:solid;;border-left-style:solid;;border-left-width:2px;;border-right-style:solid;;border-color:darkgreen;;border-top-width:2px;;border-bottom-width:2px;;border-right-width:1px;;width:36%;;font-weight:bold;;&#039;&amp;quot;;;\&lt;br /&gt;
  $TD{0..18}{1..5} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
  $TD{0..18}{6} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
sub FUNC_Status {\&lt;br /&gt;
    my($value, $min, $colorMin,  $statusMin,  $colorMiddel, $statusMiddle, $max, $colorMax, $statusMax)=@_;;\&lt;br /&gt;
    my $ret = ($value &amp;lt; $min)? &#039;&amp;lt;span style=&amp;quot;color:&#039;.$colorMin.&#039;&amp;quot;&amp;gt;&#039;.$statusMin.&#039;&amp;lt;/span&amp;gt;&#039; : ($value &amp;gt; $max)? &#039;&amp;lt;span style=&amp;quot;color:&#039;.$colorMax.&#039;&amp;quot;&amp;gt;&#039;.$statusMax.&#039;&amp;lt;/span&amp;gt;&#039; : &#039;&amp;lt;span style=&amp;quot;color:&#039;.$colorMiddel.&#039;&amp;quot;&amp;gt;&#039;.$statusMiddle.&#039;&amp;lt;/span&amp;gt;&#039;;;\&lt;br /&gt;
    return $ret;;\&lt;br /&gt;
  }\&lt;br /&gt;
\&lt;br /&gt;
sub Device {\&lt;br /&gt;
    return &amp;quot;$SELF&amp;quot;.&amp;quot;_connect&amp;quot;;;\&lt;br /&gt;
  }\&lt;br /&gt;
\&lt;br /&gt;
sub Cost {\&lt;br /&gt;
  my($i)=@_;;\&lt;br /&gt;
  my $currency = (::ReadingsVal(Device(),&amp;quot;current_currency&amp;quot;,&amp;quot;&amp;quot;) eq &amp;quot;EUR&amp;quot;)? &amp;quot; €&amp;quot; : &amp;quot;&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
  return ::ReadingsVal(Device(),&amp;quot;total_cost_&amp;quot;.$i,0).$currency;;\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
sub Price {\&lt;br /&gt;
  my($i)=@_;;\&lt;br /&gt;
  my $j;;\&lt;br /&gt;
  my $value;;\&lt;br /&gt;
  my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
\&lt;br /&gt;
  if ($i == 0) {\&lt;br /&gt;
    $value = ::ReadingsVal(Device(),&amp;quot;current_price&amp;quot;,0);;\&lt;br /&gt;
    $value = ( ::ReadingsVal(Device(),&amp;quot;fc0_trigger&amp;quot;,&amp;quot;off&amp;quot;) eq &amp;quot;on&amp;quot; ) ?\&lt;br /&gt;
         &amp;quot;&amp;lt;span style=&#039;color:green&#039;&amp;gt;&amp;quot;.$value.&amp;quot;&amp;lt;/span&amp;gt;&amp;quot; :\&lt;br /&gt;
         &amp;quot;&amp;lt;span style=&#039;color:red&#039;&amp;gt;&amp;quot;.$value.&amp;quot;&amp;lt;/span&amp;gt;&amp;quot; ;;\&lt;br /&gt;
    $value .= &amp;quot; ct/kWh&amp;quot;;;\&lt;br /&gt;
  } else {\&lt;br /&gt;
    $j       = $i+$hour;;\&lt;br /&gt;
    if ($j &amp;lt; 24) {\&lt;br /&gt;
      $value = ::round(::ReadingsVal(Device(),&amp;quot;fc0_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$j).&amp;quot;_total&amp;quot;,0)*100,1);;\&lt;br /&gt;
    } else {\&lt;br /&gt;
      $j = $j - 24;;\&lt;br /&gt;
      $value = ::round(::ReadingsVal(Device(),&amp;quot;fc1_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$j).&amp;quot;_total&amp;quot;,0)*100,1);;\&lt;br /&gt;
    }\&lt;br /&gt;
    $value = ( ::ReadingsVal(Device(),&amp;quot;fc_trigger_price&amp;quot;,&amp;quot;0&amp;quot;) &amp;gt; $value ) ?\&lt;br /&gt;
         &amp;quot;&amp;lt;span style=&#039;color:green&#039;&amp;gt;&amp;quot;.$value.&amp;quot;&amp;lt;/span&amp;gt;&amp;quot; :\&lt;br /&gt;
         &amp;quot;&amp;lt;span style=&#039;color:red&#039;&amp;gt;&amp;quot;.$value.&amp;quot;&amp;lt;/span&amp;gt;&amp;quot; ;;\&lt;br /&gt;
    $value = sprintf(&amp;quot;%02d&amp;quot;,$j).&amp;quot; :  &amp;quot;.$value ;;\&lt;br /&gt;
  }\&lt;br /&gt;
  return  $value;;\&lt;br /&gt;
 }\&lt;br /&gt;
\&lt;br /&gt;
sub Format {\&lt;br /&gt;
  my($i)=@_;;\&lt;br /&gt;
\&lt;br /&gt;
  my $MonthBefore   = &amp;quot;LogDBRep_Statistic_previous_Month&amp;quot;;;\&lt;br /&gt;
  my $MonthPrevious = ::ReadingsTimestamp(&amp;quot;$MonthBefore&amp;quot;,&amp;quot;EVU_Tibber_Pulse_nodes_consumption_month&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
       $MonthPrevious = ($MonthPrevious ne &amp;quot;null&amp;quot;) ?    POSIX::strftime(&amp;quot;%Y&amp;quot;,localtime(::time_str2num(::ReadingsTimestamp(&amp;quot;$MonthBefore&amp;quot;,&amp;quot;EVU_Tibber_Pulse_nodes_consumption_month&amp;quot;,&amp;quot;null&amp;quot;)))) : &amp;quot;null&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
  my $YearBefore   = &amp;quot;LogDBRep_Statistic_previous_Year&amp;quot;;;\&lt;br /&gt;
  my $YearPrevious = ::ReadingsTimestamp(&amp;quot;$YearBefore&amp;quot;,Device().&amp;quot;_nodes_consumption_year&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
       $YearPrevious = ($YearPrevious ne &amp;quot;null&amp;quot;) ? POSIX::strftime(&amp;quot;%Y&amp;quot;,localtime(::time_str2num(::ReadingsTimestamp(&amp;quot;$YearBefore&amp;quot;,Device().&amp;quot;_nodes_consumption_year&amp;quot;,&amp;quot;null&amp;quot;)))) : &amp;quot;null&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
  if ($i eq &amp;quot;day&amp;quot;) {\&lt;br /&gt;
      return sprintf(&amp;quot;%04d&amp;quot;,::ReadingsVal(Device(),&amp;quot;nodes_consumption_day&amp;quot;,0));;\&lt;br /&gt;
    } elsif ($i eq &amp;quot;month&amp;quot;) {\&lt;br /&gt;
      my $evu_em = sprintf(&amp;quot;%04d&amp;quot;,::ReadingsVal(Device(),&amp;quot;nodes_consumption_month&amp;quot;,0));;\&lt;br /&gt;
      $evu_em .= ($MonthPrevious ne &amp;quot;null&amp;quot;) ? sprintf(&amp;quot; / %04d&amp;quot;, ::ReadingsVal(&amp;quot;$MonthBefore&amp;quot;,&amp;quot;EVU_Tibber_Pulse_nodes_consumption_month&amp;quot;,0) ) : &amp;quot;&amp;quot;;;\&lt;br /&gt;
      return $evu_em;;\&lt;br /&gt;
    } elsif ($i eq &amp;quot;year&amp;quot;) {\&lt;br /&gt;
      my $evu_ey = sprintf(&amp;quot;%04d&amp;quot;,::ReadingsVal(Device(),&amp;quot;nodes_consumption_year&amp;quot;,0));;\&lt;br /&gt;
      $evu_ey .= ($YearPrevious ne &amp;quot;null&amp;quot;) ? sprintf(&amp;quot; / %04d&amp;quot;, ::ReadingsVal(&amp;quot;$YearBefore&amp;quot;,&amp;quot;EVU_Tibber_Pulse_nodes_consumption_month&amp;quot;,0) ) : &amp;quot;&amp;quot;;;\&lt;br /&gt;
      return $evu_ey;;\&lt;br /&gt;
    } elsif ($i eq &amp;quot;trigger_0&amp;quot;) {\&lt;br /&gt;
      return ((::ReadingsVal(Device(),&amp;quot;fc0_trigger_start&amp;quot;,&amp;quot;&amp;quot;) eq &amp;quot;null&amp;quot; ) ?\&lt;br /&gt;
            &amp;quot;&amp;lt;span style=&#039;color:red&#039;&amp;gt;Heute kein Trigger &amp;lt;br&amp;gt;mehr unter &amp;quot;.\&lt;br /&gt;
                 ::ReadingsVal(Device(),&amp;quot;fc_trigger_price&amp;quot;,&amp;quot;null&amp;quot;).&amp;quot; ct&amp;lt;/span&amp;gt;&amp;quot; :\&lt;br /&gt;
            &amp;quot;&amp;lt;span style=&#039;color:green&#039;&amp;gt;Trigger von&amp;lt;br&amp;gt;&amp;quot;.\&lt;br /&gt;
                 ::ReadingsVal(Device(),&amp;quot;fc0_trigger_start&amp;quot;,&amp;quot;00:00&amp;quot;).&amp;quot; bis &amp;quot;.::ReadingsVal(Device(),&amp;quot;fc0_trigger_stop&amp;quot;,&amp;quot;00:00&amp;quot;).&amp;quot;&amp;lt;br&amp;gt;unter &amp;quot;.\&lt;br /&gt;
                 ::ReadingsVal(Device(),&amp;quot;fc_trigger_price&amp;quot;,&amp;quot;null&amp;quot;).&amp;quot; ct&amp;lt;/span&amp;gt;&amp;quot; );;\&lt;br /&gt;
    } elsif ($i eq &amp;quot;trigger_1&amp;quot;) {\&lt;br /&gt;
      if (::ReadingsVal(Device(),&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {\&lt;br /&gt;
        return ((::ReadingsVal(Device(),&amp;quot;fc1_trigger_start&amp;quot;,&amp;quot;null&amp;quot;) eq &amp;quot;null&amp;quot; ) ?\&lt;br /&gt;
              &amp;quot;&amp;lt;span style=&#039;color:red&#039;&amp;gt;Morgen kein Trigger &amp;lt;br&amp;gt;mehr unter &amp;quot;.\&lt;br /&gt;
                   ::ReadingsVal(Device(),&amp;quot;fc_trigger_price&amp;quot;,&amp;quot;null&amp;quot;).&amp;quot; ct&amp;lt;/span&amp;gt;&amp;quot; :\&lt;br /&gt;
              &amp;quot;&amp;lt;span style=&#039;color:green&#039;&amp;gt;Morgen ein Trigger von&amp;lt;br&amp;gt;&amp;quot;.\&lt;br /&gt;
                   ::ReadingsVal(Device(),&amp;quot;fc1_trigger_start&amp;quot;,&amp;quot;00:00&amp;quot;).&amp;quot; bis &amp;quot;.::ReadingsVal(Device(),&amp;quot;fc1_trigger_stop&amp;quot;,&amp;quot;00:00&amp;quot;).&amp;quot;&amp;lt;br&amp;gt;unter &amp;quot;.\&lt;br /&gt;
                   ::ReadingsVal(Device(),&amp;quot;fc_trigger_price&amp;quot;,&amp;quot;null&amp;quot;).&amp;quot; ct&amp;lt;/span&amp;gt;&amp;quot; );;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return &amp;quot;Morgen noch kein Trigger vorhanden&amp;quot;;;\&lt;br /&gt;
      }\&lt;br /&gt;
    }\&lt;br /&gt;
  return &amp;quot;null&amp;quot;;;\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
&amp;quot;Statistiken &amp;quot;.::ReadingsVal(Device(),&amp;quot;current_date&amp;quot;,0).&amp;quot; in kWh&amp;quot;|&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;aktuell&amp;lt;/span&amp;gt;&amp;quot;|&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;heute&amp;lt;/span&amp;gt;&amp;quot;|&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;Monat&amp;lt;/span&amp;gt;&amp;quot;|&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;Jahr&amp;lt;/span&amp;gt;&amp;quot;\&lt;br /&gt;
\&lt;br /&gt;
&amp;quot;Strom&amp;lt;dd&amp;gt;Preis / Kosten&amp;lt;/dd&amp;gt;&amp;quot;|Price(0)|Cost(&amp;quot;day&amp;quot;)|Cost(&amp;quot;month&amp;quot;)|Cost(&amp;quot;year&amp;quot;)\&lt;br /&gt;
\&lt;br /&gt;
## Wenn man das liveMessurment von Tibber verwendet\&lt;br /&gt;
&amp;quot;Bezug vom Netz&amp;quot;|\&lt;br /&gt;
sprintf(&amp;quot;%04d W&amp;quot;,([EVU_Tibber_connect:payload_data_liveMeasurement_power]  &amp;gt;= 0 ? ::ReadingsVal(&amp;quot;EVU_Tibber_connect&amp;quot;,&amp;quot;payload_data_liveMeasurement_power&amp;quot;,0) : 0))|\&lt;br /&gt;
Format(&amp;quot;day&amp;quot;)|Format(&amp;quot;month&amp;quot;)|Format(&amp;quot;year&amp;quot;)\&lt;br /&gt;
\&lt;br /&gt;
&amp;quot;Einspeisung ins Netz&amp;quot;|\&lt;br /&gt;
sprintf(&amp;quot;%04d W&amp;quot;,([EVU_Tibber_connect:payload_data_liveMeasurement_powerProduction]  &amp;gt;= 0 ? ::ReadingsVal(&amp;quot;EVU_Tibber_connect&amp;quot;,&amp;quot;payload_data_liveMeasurement_powerProduction&amp;quot;,0) : 0))|\&lt;br /&gt;
&amp;quot;&amp;quot;|&amp;quot;&amp;quot;|&amp;quot;&amp;quot;\&lt;br /&gt;
\&lt;br /&gt;
## Wenn man einen eigenes SmartMeter verwendet\&lt;br /&gt;
## &amp;quot;Bezug vom Netz&amp;quot;|sprintf(&amp;quot;%04d W&amp;quot;,([WR_0_KSEM:M_AC_Power] &amp;gt;= 0 ? ::round(::ReadingsVal(&amp;quot;WR_0_KSEM&amp;quot;,&amp;quot;M_AC_Power&amp;quot;,0),0) : 0) )|Format(&amp;quot;day&amp;quot;)|Format(&amp;quot;month&amp;quot;)|Format(&amp;quot;year&amp;quot;)\&lt;br /&gt;
## &amp;quot;Einspeisung ins Netz&amp;quot;|sprintf(&amp;quot;%04d W&amp;quot;,([WR_0_KSEM:M_AC_Power] &amp;lt;= 0 ? abs(::round(::ReadingsVal(&amp;quot;WR_0_KSEM&amp;quot;,&amp;quot;M_AC_Power&amp;quot;,0),0)) :  0) )|&amp;quot;&amp;quot;|&amp;quot;&amp;quot;|&amp;quot;&amp;quot;&lt;br /&gt;
attr EVU_Tibber verbose 3&lt;br /&gt;
&lt;br /&gt;
setstate EVU_Tibber 2024-01-23 10:03:00 ui_command_1 ---&lt;br /&gt;
setstate EVU_Tibber 2024-01-20 17:40:53 ui_command_2 ---&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== LogDBRep_EVU_Tibber_connect_SQL RAW Device ====&lt;br /&gt;
Dieses DbRep Device dient der Verbindung zur MySQL DbLog. Die SQL SELECT Statements werden im EVU_Tibber_connect Device generiert und über dieses Device in der MySQL DbLog ausgeführt. Der Name des Devices wird ebenfalls generiert und liegt somit fest.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod LogDBRep_EVU_Tibber_connect_SQL DbRep LogDB&lt;br /&gt;
attr LogDBRep_EVU_Tibber_connect_SQL DbLogExclude .*&lt;br /&gt;
attr LogDBRep_EVU_Tibber_connect_SQL room System&lt;br /&gt;
attr LogDBRep_EVU_Tibber_connect_SQL sqlCmdHistoryLength 5&lt;br /&gt;
attr LogDBRep_EVU_Tibber_connect_SQL timeout 20&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 2. aWATTar ==&lt;br /&gt;
=== Voraussetzungen ===&lt;br /&gt;
* FHEM mit einer installierten MySQL DbLog (FileLog wird nicht unterstützt)&lt;br /&gt;
* Für die Berechnung der tatsächlichen Kosten benötigt man seine lokalen Fixkosten, die je nach Region unterschiedlich sind.&lt;br /&gt;
* Die bisherige Berechnung mit Stand 20240123 wäre wie folgt: round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
&lt;br /&gt;
=== EVU_aWATTar_connect ===&lt;br /&gt;
Das EVU_aWATTar_connect fragt mit HTTPMOD die aWATTar API Webseite ab. Es werden keine Live Verbrauchdaten geliefert.&lt;br /&gt;
&lt;br /&gt;
==== Verwendete Module ====&lt;br /&gt;
* HTTPMOD&lt;br /&gt;
* DbLog&lt;br /&gt;
* DbRep&lt;br /&gt;
&lt;br /&gt;
==== EVU_aWATTar_connect  RAW Device ====&lt;br /&gt;
Momentan fragt das EVU_aWattar_connect lediglich die aktuellen Preise des Tages ab. Die readings sind an die des EVU_Tibber_connect angelehnt, um im weiteren Verlauf eventuell ein gemeinsames Device für die Anzeige und Steuerung zu erarbeiten.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod EVU_aWATTar_connect HTTPMOD https://api.awattar.de/v1/marketdata/current.yaml 0&lt;br /&gt;
attr EVU_aWATTar_connect DbLogExclude .*&lt;br /&gt;
attr EVU_aWATTar_connect comment Version 2024.01.23 13:00\&lt;br /&gt;
\&lt;br /&gt;
Achtung, momentan werden nur die Börsenpreise ohne die fix Kosten dargestellt.\&lt;br /&gt;
\&lt;br /&gt;
https://api.awattar.de/v1/marketdata&lt;br /&gt;
attr EVU_aWATTar_connect enableControlSet 1&lt;br /&gt;
attr EVU_aWATTar_connect get01-15Name fc_max&lt;br /&gt;
attr EVU_aWATTar_connect get01-15OExpr round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
attr EVU_aWATTar_connect get01-1Name current_date&lt;br /&gt;
attr EVU_aWATTar_connect get01-22Name fc_med&lt;br /&gt;
attr EVU_aWATTar_connect get01-22OExpr round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
attr EVU_aWATTar_connect get01-29Name fc_avg&lt;br /&gt;
attr EVU_aWATTar_connect get01-29OExpr round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
attr EVU_aWATTar_connect get01-36Name current_price&lt;br /&gt;
attr EVU_aWATTar_connect get01-36OExpr round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
attr EVU_aWATTar_connect get01-8Name fc_min&lt;br /&gt;
attr EVU_aWATTar_connect get01-8OExpr round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
attr EVU_aWATTar_connect get01Name 01_priceInfo&lt;br /&gt;
attr EVU_aWATTar_connect get01RegOpt g&lt;br /&gt;
attr EVU_aWATTar_connect get01Regex date_now: (\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2})|price_low: ([0-9]+[0-9\.]+)|price_high: ([0-9]+[0-9\.]+)|price_median: ([0-9]+[0-9\.]+)|price_average: ([0-9]+[0-9\.]+)|price_current: ([0-9]+[0-9\.]+)&lt;br /&gt;
attr EVU_aWATTar_connect get01URL https://api.awattar.de/v1/marketdata/current.yaml&lt;br /&gt;
attr EVU_aWATTar_connect get02-10Name fc0_09_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-10OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-11Name fc0_10_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-11OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-12Name fc0_11_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-12OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-13Name fc0_12_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-13OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-14Name fc0_13_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-14OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-15Name fc0_14_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-15OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-16Name fc0_15_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-16OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-17Name fc0_16_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-17OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-18Name fc0_17_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-18OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-19Name fc0_18_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-19OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-1Name fc0_00_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-1OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-20Name fc0_19_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-20OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-21Name fc0_20_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-21OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-22Name fc0_21_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-22OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-23Name fc0_22_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-23OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-24Name fc0_23_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-24OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-2Name fc0_01_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-2OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-3Name fc0_02_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-3OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-4Name fc0_03_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-4OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-5Name fc0_04_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-5OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-6Name fc0_05_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-6OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-7Name fc0_06_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-7OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-8Name fc0_07_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-8OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-9Name fc0_08_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-9OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02FollowGet 01_priceInfo&lt;br /&gt;
attr EVU_aWATTar_connect get02Name 02_priceDay&lt;br /&gt;
attr EVU_aWATTar_connect get02RegOpt g&lt;br /&gt;
attr EVU_aWATTar_connect get02Regex (?&amp;lt;=abs_[0-9]{2}_amount: )([0-9]+[0-9\.]+)&lt;br /&gt;
attr EVU_aWATTar_connect get02URL https://api.awattar.de/v1/marketdata/current.yaml&lt;br /&gt;
attr EVU_aWATTar_connect get03-1Name fc3_00_startsAt&lt;br /&gt;
attr EVU_aWATTar_connect get03-1OExpr POSIX::strftime(&amp;quot;%Y-%m-%d %H:%M:%S&amp;quot;,localtime(substr($val,0,10)))&lt;br /&gt;
attr EVU_aWATTar_connect get03-4Name fc3_00_total&lt;br /&gt;
attr EVU_aWATTar_connect get03-4OExpr round($val/1000,4)&lt;br /&gt;
attr EVU_aWATTar_connect get03Name 02_priceAll&lt;br /&gt;
attr EVU_aWATTar_connect get03RegOpt g&lt;br /&gt;
attr EVU_aWATTar_connect get03Regex &amp;quot;start_timestamp&amp;quot;: (\d{10})|&amp;quot;marketprice&amp;quot;: (\d*\.\d*)&lt;br /&gt;
attr EVU_aWATTar_connect get03URL https://api.awattar.de/v1/marketdata?start=1701990000000&amp;amp;end=1702159200000&lt;br /&gt;
attr EVU_aWATTar_connect group PV Steuerung EVU&lt;br /&gt;
attr EVU_aWATTar_connect icon sani_pump&lt;br /&gt;
attr EVU_aWATTar_connect room Strom-&amp;gt;Boerse&lt;br /&gt;
attr EVU_aWATTar_connect sortby 213&lt;br /&gt;
attr EVU_aWATTar_connect userReadings fc_trigger_price:fc_avg.* {\&lt;br /&gt;
## fc_trigger_price:[fc_avg|compensation_grid].* {\\&lt;br /&gt;
  my $fc_avg = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_avg&amp;quot;,0);;\&lt;br /&gt;
  my $fc_min = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_min&amp;quot;,0);;\&lt;br /&gt;
\&lt;br /&gt;
  # Berechnung eines Default Schwellwertes als täglicher Niedrigpreis\&lt;br /&gt;
  my $price_level = round( ($fc_avg - $fc_min)/2 + $fc_min , 1);;\&lt;br /&gt;
 \&lt;br /&gt;
  # Abschätzung von Wirtschaftlichkeit beim Speicher Laden, falls Tibber zu teuer wird\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;compensation_grid&amp;quot;,0) != 0 ) {\&lt;br /&gt;
    my $price_level_battery = round( ($fc_avg - ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;compensation_grid&amp;quot;,0)) *0.85 , 1) ;;\&lt;br /&gt;
    if ( $price_level &amp;gt; $price_level_battery ) {\&lt;br /&gt;
      $price_level = $price_level_battery;;\&lt;br /&gt;
    }\&lt;br /&gt;
  }\&lt;br /&gt;
  $price_level;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_start:fc_trigger_price.* {\&lt;br /&gt;
  my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
  my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for (my $loop_hour = $hour;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour)) ;;\&lt;br /&gt;
      }\&lt;br /&gt;
    } # end  for loop_hour\&lt;br /&gt;
\&lt;br /&gt;
  return(&amp;quot;null&amp;quot;);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_stop:fc0_trigger_start.* {\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
  my $loop_hour = 0;;\&lt;br /&gt;
  my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my $fc_trigger_start = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_trigger_start&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  my $fc_trigger_stop = $fc_trigger_start;;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $fc_trigger_start ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    $fc_trigger_start =~ /(\d\d):/;; $fc_trigger_start = $1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for ($loop_hour = $fc_trigger_start;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$fc.&amp;quot;_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        $fc_trigger_stop = sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour) ;;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour));;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
     # wechsel zum nächsten Tag\&lt;br /&gt;
     if ( $loop_hour == 23 and $fc == 0 ) {\&lt;br /&gt;
       $fc = 1;;\&lt;br /&gt;
       $loop_hour = -1;;\&lt;br /&gt;
     }\&lt;br /&gt;
\&lt;br /&gt;
    } # end for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
 \&lt;br /&gt;
  return($fc_trigger_stop);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger:fc0_trigger_stop.* {\&lt;br /&gt;
\&lt;br /&gt;
  # Setzen des Triggers für die aktuelle Stunde\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;current_price&amp;quot;,100)  &amp;lt; ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) ) {\&lt;br /&gt;
    return(&amp;quot;on&amp;quot;)\&lt;br /&gt;
  } else {\&lt;br /&gt;
    return(&amp;quot;off&amp;quot;)\&lt;br /&gt;
  }\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_DbLog:fc0_00_total.* {\&lt;br /&gt;
my ($timestamp,$date,$hour,$value,$loop_fc_next) = 5x0;;\&lt;br /&gt;
\&lt;br /&gt;
for (my $loop_fc = 0;; $loop_fc &amp;lt;= 1;; $loop_fc++) {\&lt;br /&gt;
  $loop_fc_next = $loop_fc +1;;\&lt;br /&gt;
  $date = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;current_date&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  if ($date ne &amp;quot;null&amp;quot;) {\&lt;br /&gt;
    $date =~ /([\d+-]+)/;; $date = $1 ;;\&lt;br /&gt;
    for (my $loop_hour = 0;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $hour = sprintf(&amp;quot;%02d&amp;quot;,$loop_hour);;\&lt;br /&gt;
      $timestamp = $date.&amp;quot; &amp;quot;.$hour.&amp;quot;:00:00&amp;quot;;;\&lt;br /&gt;
      $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$loop_fc.&amp;quot;_&amp;quot;.$hour.&amp;quot;_total&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
      ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
                        INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                          VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;fc&amp;quot;.$loop_fc.&amp;quot;_total&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
                        ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                          VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
    }\&lt;br /&gt;
  } else {\&lt;br /&gt;
      fhem(&amp;quot;deletereading $NAME fc1_.*&amp;quot;);;\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;current_date&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
}&lt;br /&gt;
attr EVU_aWATTar_connect verbose 0&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== LogDBRep_EVU_aWATTar_connect_SQL RAW Device ====&lt;br /&gt;
Dieses DbRep Device dient der Verbindung zur MySQL DbLog. Die SQL SELECT Statements werden im EVU_Tibber_connect Device generiert und über dieses Device in der MySQL DbLog ausgeführt. Der Name des Devices wird ebenfalls generiert und liegt somit fest.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod LogDBRep_EVU_aWATTar_connect_SQL DbRep LogDB&lt;br /&gt;
attr LogDBRep_EVU_aWATTar_connect_SQL DbLogExclude .*&lt;br /&gt;
attr LogDBRep_EVU_aWATTar_connect_SQL room System&lt;br /&gt;
attr LogDBRep_EVU_aWATTar_connect_SQL sqlCmdHistoryLength 5&lt;br /&gt;
attr LogDBRep_EVU_aWATTar_connect_SQL timeout 20&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 3. EPEX_Spot ==&lt;br /&gt;
=== Voraussetzungen ===&lt;br /&gt;
httpmod&lt;br /&gt;
&lt;br /&gt;
==== EPEX_Spot  RAW Device ====&lt;br /&gt;
Mit diesem device erhält man die viertelstündlichen Börsenpreise für heute und morgen (&#039;EPEX_prices&#039; und zugehörige &#039;EPEX_timetags&#039; als Listen ) und den aktuellen Preis (&#039;EPEX_price&#039;). &amp;lt;br&amp;gt;&lt;br /&gt;
Die &#039;dayahead&#039; Preise für morgen werden gegen 14:00 am Vortag veröffentlicht. Bis dahin enthalten die Listen &#039;EPEX_prices&#039; und &#039;EPEX_timetags&#039; 96 Elemente, danach sind es 192 Elemente. &lt;br /&gt;
Die timetags sind die Unix-timestamps der Viertelstunde, ab welcher der jeweilige Preis gilt.&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Code für device &#039;&#039;EPEX_Boersenpreise&#039;&#039;:&amp;lt;div style=&amp;quot;max-height:150px; overflow:auto; padding:4px;&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
defmod EPEX_Boersenpreise HTTPMOD URL 900&lt;br /&gt;
attr EPEX_Boersenpreise alignTime 00:00&lt;br /&gt;
attr EPEX_Boersenpreise comment This device queries €/MWh prices for electrical energy from https://api.energy-charts.info/price?bzn=DE-LU.\&lt;br /&gt;
\&lt;br /&gt;
A reread every 15 min extracts the EPEX-price of the next quarter hour from the data lists in readings\&lt;br /&gt;
EPEX_prices and EPEX_timetags, using the code in reading02RecombineExpr. \&lt;br /&gt;
Within reading02RecombineExpr, &amp;quot;get $name EPEX_prices&amp;quot; is issued at the beginning of every second odd \&lt;br /&gt;
hour in order to keep EPEX_prices and EPEX_timetags up-to-date. \&lt;br /&gt;
Note that the reread itself does not perform an explicit request to api.energy-charts.info, except \&lt;br /&gt;
for a dummy request to localhost. This above strategy minimizes the number of external requests, \&lt;br /&gt;
in order to avoid occasional timeouts as much as possible.\&lt;br /&gt;
\&lt;br /&gt;
no_of_data, the length of the data lists in EPEX_prices and EPEX_timetags is 96 after midnight, and it \&lt;br /&gt;
changes to 192 in the afternoon when the dayahead prices are available.\&lt;br /&gt;
\&lt;br /&gt;
An event-on-change-reading for no_of_data can be used by a FileLog device to save the new data.&lt;br /&gt;
&lt;br /&gt;
attr EPEX_Boersenpreise disable 0&lt;br /&gt;
attr EPEX_Boersenpreise event-on-update-reading EPEX_price.*&lt;br /&gt;
attr EPEX_Boersenpreise get01-1Name EPEX_timetags&lt;br /&gt;
attr EPEX_Boersenpreise get01-2Name EPEX_prices&lt;br /&gt;
attr EPEX_Boersenpreise get01Name EPEX_prices&lt;br /&gt;
attr EPEX_Boersenpreise get01Regex unix_seconds&amp;quot;:\[(.*?)\],&amp;quot;price&amp;quot;:\[(.*?)\]&lt;br /&gt;
attr EPEX_Boersenpreise get01Replacement02Value {my ($s,$m,$h,$D,$M,$Y)=localtime();;my $start=int(fhemTimeLocal(0,0,0,$D,$M,$Y));;&amp;quot;https://api.energy-charts.info/price?bzn=DE-LU&amp;amp;start=&amp;quot;.sprintf(&amp;quot;%d&amp;quot;,$start).&amp;quot;&amp;amp;end=&amp;quot;.sprintf(&amp;quot;%d&amp;quot;,$start+86400+86399)}&lt;br /&gt;
attr EPEX_Boersenpreise group EPEX&lt;br /&gt;
attr EPEX_Boersenpreise reading02Name EPEX_price&lt;br /&gt;
attr EPEX_Boersenpreise reading02RecombineExpr {my ($s,$m,$h,$D,$M,$Y)=localtime();; my $time=fhemTimeLocal(0,0,0,$D,$M,$Y);;\&lt;br /&gt;
my $price = &amp;quot;error&amp;quot;;;\&lt;br /&gt;
if (time_str2num(ReadingsTimestamp($name, &amp;quot;EPEX_prices&amp;quot;,0)) &amp;gt; $time-86400) {\&lt;br /&gt;
	$time=fhemTimeLocal(0,15*int($m/15),$h,$D,$M,$Y);;\&lt;br /&gt;
	my @times  = split(/,/,ReadingsVal($name,&amp;quot;EPEX_timetags&amp;quot;,&amp;quot;-1&amp;quot;));;\&lt;br /&gt;
	my @prices = split(/,/,ReadingsVal($name,&amp;quot;EPEX_prices&amp;quot;,&amp;quot;-1&amp;quot;));;\&lt;br /&gt;
	my %map;; @map{@times} = @prices;; \&lt;br /&gt;
	if (exists $map{$time}) {$price = $map{$time}};;\&lt;br /&gt;
	};;\&lt;br /&gt;
if ($h % 2 == 0 and $m == 0) {fhem(&amp;quot;get $name EPEX_prices&amp;quot;)};;\&lt;br /&gt;
$price}&lt;br /&gt;
attr EPEX_Boersenpreise reading02Regex .&lt;br /&gt;
attr EPEX_Boersenpreise replacement02Mode expression&lt;br /&gt;
attr EPEX_Boersenpreise replacement02Regex URL&lt;br /&gt;
attr EPEX_Boersenpreise replacement02Value {&amp;quot;http://localhost/&amp;quot;}&lt;br /&gt;
attr EPEX_Boersenpreise room Energie&lt;br /&gt;
attr EPEX_Boersenpreise showError 1&lt;br /&gt;
attr EPEX_Boersenpreise stateFormat EPEX_price €/MWh&lt;br /&gt;
attr EPEX_Boersenpreise timeout 5&lt;br /&gt;
attr EPEX_Boersenpreise userReadings no_of_data:EPEX_prices.* {split(/,/,ReadingsVal(&amp;quot;EPEX_Boersenpreise&amp;quot;,&amp;quot;EPEX_prices&amp;quot;,&amp;quot;&amp;quot;))}&lt;br /&gt;
    &amp;lt;/pre&amp;gt;&lt;br /&gt;
  &amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Code für device &#039;&#039;FileLog_EPEX_Boersenpreise&#039;&#039;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
define FileLog_EPEX_Boersenpreise FileLog ./log/EPEX_Boersenpreise.log EPEX_Boersenpreise:no_of_data:.192.*&lt;br /&gt;
attr FileLog_EPEX_Boersenpreise outputFormat {my @prices=split(/,/,ReadingsVal(&amp;quot;EPEX_Boersenpreise&amp;quot;,&amp;quot;EPEX_prices&amp;quot;,&amp;quot;-1&amp;quot;));;my @times=split(/,/,ReadingsVal(&amp;quot;EPEX_Boersenpreise&amp;quot;,&amp;quot;EPEX_timetags&amp;quot;,&amp;quot;-1&amp;quot;));;return join &amp;quot;&amp;quot;, map {strftime(&amp;quot;%Y-%m-%d_%H:%M:%S&amp;quot;, localtime($times[$_])).&amp;quot; &amp;quot;.&amp;quot;EPEX_Boersenpreise&amp;quot;.&amp;quot; EPEX_price $prices[$_]\n&amp;quot;} $#times - 95 .. $#times}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Mit einem at-device kann man die Länge der Daten im Log-File begrenzen, in diesem Beispiel auf drei Tage:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
define trim_EPEX_Logfile at *00:02:12 {`tail -n 288 ./log/EPEX_Boersenpreise.log &amp;gt; ./log/file.tmp &amp;amp;&amp;amp; mv ./log/file.tmp ./log/EPEX_Boersenpreise.log`;;fhem(&amp;quot;set FileLog_EPEX_Boersenpreise reopen&amp;quot;)}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Anwendungsbeispiel===&lt;br /&gt;
Im Beispiel sind der viertelstündliche Strombezug (falls mitgeloggt) und der viertelstündliche EPEX-Preis am 11.1.2026 (vormittags) dargestellt:&lt;br /&gt;
[[Datei:Bezug-EPEX-Preis.png|600px|rahmenlos|links]]&lt;br /&gt;
&amp;lt;br clear=&amp;quot;all&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Links ==&lt;br /&gt;
* Website (deutsch) des Anbieters [https://tibber.com/de Tibber]&lt;br /&gt;
* Website (deutsch) des Anbieters [https://www.awattar.de/ aWATTar]&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Strompreis]]&lt;/div&gt;</summary>
		<author><name>Ph1959de</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=HttpUtils&amp;diff=40729</id>
		<title>HttpUtils</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=HttpUtils&amp;diff=40729"/>
		<updated>2026-01-12T19:16:19Z</updated>

		<summary type="html">&lt;p&gt;Ph1959de: (Ober)Kategorie entfernt&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Infobox Modul&lt;br /&gt;
|ModPurpose=Hilfsfunktionen für HTTP-Zugriffe&lt;br /&gt;
|ModType=u&lt;br /&gt;
|ModForumArea=Automatisierung&lt;br /&gt;
|ModTechName=HttpUtils.pm&lt;br /&gt;
|ModOwner=rudolfkoenig ({{Link2FU|8|Forum}} / [[Benutzer Diskussion:Rudolfkoenig|Wiki]])&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Das Modul [[HttpUtils]](.pm) ist sowohl für Modulentwickler als auch für Endanwender gedacht, um Daten via HTTP auszutauschen. Es stellt dabei eine Reihe von Funktionen zur Verfügung und wird beispielsweise vom Modul [[HTTPMOD]] intensiv genutzt.&lt;br /&gt;
&lt;br /&gt;
== Primäre Funktionen (HTTP) ==&lt;br /&gt;
Es ist zu beachten, dass bei den Funktionen&lt;br /&gt;
* &amp;lt;code&amp;gt;GetHttpFile()&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;GetFileFromURL&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;GetFileFromURLQuiet&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;HttpUtils_BlockingGet&amp;lt;/code&amp;gt;&lt;br /&gt;
ein sogenannter &amp;quot;blockierender&amp;quot; Aufruf durchgeführt wird. Das bedeutet, dass FHEM bei einem Aufruf einer dieser Funktionen solange wartet und dabei absolut nichts macht, bis die Antwort vom HTTP-Server eintrifft und die Funktion damit beendet ist. Das kann bei Verbindungsproblemen evtl. dazu führen, dass FHEM für die gesamte Wartezeit (Timeout) steht und nichts verarbeitet. Problematisch ist das gerade bei Anwendungen oder Hardware, die eine zeitnahe Reaktion von FHEM erwarten (z.B. HomeMatic-Geräte). In der Zeit, in der auf eine HTTP Antwort gewartet wird, steht FHEM dabei komplett. &lt;br /&gt;
&lt;br /&gt;
Es wird daher empfohlen, die Funktionen so sparsam wie möglich zu verwenden und die Timeouts so niedrig wie möglich zu halten, um ein längeres Einfrieren von FHEM möglichst zu vermeiden.&lt;br /&gt;
&lt;br /&gt;
Um eine Blockierung zu vermeiden wird generell die Verwendung von&lt;br /&gt;
:&amp;lt;code&amp;gt;HttpUtils_NonblockingGet&amp;lt;/code&amp;gt;&lt;br /&gt;
empfohlen. Diese führt den HTTP-Request asynchron durch, wodurch ein Blockieren von FHEM verhindert wird. Wie das genau funktioniert, wird in dem entsprechenden Kapitel beschrieben.&lt;br /&gt;
&lt;br /&gt;
=== GetHttpFile ===&lt;br /&gt;
Die Funktion GetHttpFile ist die denkbar einfachste Variante um eine URL aufzurufen. &lt;br /&gt;
:&amp;lt;code&amp;gt;GetHttpFile($server, $file)&amp;lt;/code&amp;gt;&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Parameter!! Bedeutung&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$server&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;mandatory&#039;&#039;&lt;br /&gt;
|| Der DNS-Name oder die IP-Adresse des HTTP-Servers&amp;lt;br&amp;gt;&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&amp;lt;nowiki&amp;gt;www.myhost.com&amp;lt;/nowiki&amp;gt;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&amp;lt;nowiki&amp;gt;192.168.0.10&amp;lt;/nowiki&amp;gt;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$file&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;mandatory&#039;&#039;&lt;br /&gt;
|| Die Datei, welche auf dem HTTP-Server aufgerufen werden soll.&amp;lt;br&amp;gt;&lt;br /&gt;
Beispiel:&lt;br /&gt;
* &#039;&#039;/&#039;&#039;&lt;br /&gt;
* &#039;&#039;/index.html&#039;&#039;&lt;br /&gt;
* &#039;&#039;/directory/image.jpg&#039;&#039;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Funktionsergebnis ist der Inhalt der aufgerufenen Seite in Form einer Zeichenkette.&lt;br /&gt;
&lt;br /&gt;
=== GetFileFromURL ===&lt;br /&gt;
Die Funktion GetFileFromURL ruft die HTTP-URL auf und gibt als Funktionsergebnis den Seiteninhalt zurück. Im Gegensatz zu GetHttpFile beinhaltet GetFileFromURL einige Zusatzoptionen in Form von Funktionsparametern.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Aufruf: &amp;lt;code&amp;gt;GetFileFromURL($url, &#039;&#039;[$timeout], [$data], [$noshutdown], [$loglevel]&#039;&#039;)&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Parameter!! Bedeutung&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$url&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;mandatory&#039;&#039;&lt;br /&gt;
|| Die HTTP-URL, welche aufgerufen werden soll. Diese kann optional Usernamen, Passwort und einen Port enthalten. Sowohl HTTP als auch HTTPS wird hierbei unterstützt.&amp;lt;br&amp;gt;&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&amp;lt;nowiki&amp;gt;http://www.myhost.com/directory/&amp;lt;/nowiki&amp;gt;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&amp;lt;nowiki&amp;gt;https://www.myhost.com/&amp;lt;/nowiki&amp;gt;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&amp;lt;nowiki&amp;gt;http://www.myhost.com:8080/&amp;lt;/nowiki&amp;gt;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&amp;lt;nowiki&amp;gt;http://user:password@www.myhost.com/&amp;lt;/nowiki&amp;gt;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;WICHTIG:&#039;&#039;&#039; Falls ein Username sowie Passwort übergeben werden, so müssen diese vorher jeweils mittels [[#urlEncode|urlEncode()]] in URL-kompatible Form umgewandelt werden um Probleme mit evtl. enthaltenen Sonderzeichen zu vermeiden. &lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$timeout&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;optional&#039;&#039;&lt;br /&gt;
|| Die maximale Dauer in Sekunden für die HTTP-Anfrage&lt;br /&gt;
&lt;br /&gt;
Beispiel: 5 &#039;&#039;(Sekunden)&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Standardwert: 4 Sekunden&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$data&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;optional&#039;&#039;&lt;br /&gt;
|| Wenn man Daten via HTTP-POST übertragen möchte, so kann man die Nutzdaten über $data übergeben. Die Daten werden dabei als Formulardaten übertragen. Wenn man den Content-Type beeinflussen oder mehrere Formular-Felder senden möchte, sollte man zur Funktion HttpUtils_BlockingGet oder HttpUtils_NonblockingGet greifen.&lt;br /&gt;
&lt;br /&gt;
Standardwert: &#039;&#039;[leer]&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$noshutdown&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;optional&#039;&#039;&lt;br /&gt;
|| Wenn $noshutdown auf 1 gesetzt ist, wird dem HTTP-Server nicht implizit mitgeteilt, dass die Verbindung nach dem Request geschlossen werden soll. Viele Webserver schließen in solch einem Fall die Verbindung, bevor sie die Antwort senden. Bei 0 wird dem Webserver mitgeteilt, dass der Sendevorgang beendet ist und nun die Antwort abgewartet wird.&lt;br /&gt;
&lt;br /&gt;
Standardwert: 1&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$loglevel&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;optional&#039;&#039;&lt;br /&gt;
|| Das [[verbose|Loglevel]], in dem sämtliche Logmeldungen zu dieser HTTP-Abfrage erzeugt werden sollen.&lt;br /&gt;
&lt;br /&gt;
Standardwert: 4&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Funktionsergebnis ist der Inhalt der aufgerufenen Seite in Form eines Strings.&lt;br /&gt;
&lt;br /&gt;
=== GetFileFromURLQuiet ===&lt;br /&gt;
Diese Funktion funktioniert ähnlich wie GetFileFromURL. Allerdings wird die tatsächliche URL in allen erzeugten Log-Meldungen unkenntlich gemacht um z.B. Zugangsdaten nicht preiszugeben. Die aufgerufene Seite wird ebenfalls als Funktionsergebnis zurückgegeben.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Aufruf: &amp;lt;code&amp;gt;GetFileFromURLQuiet($url, &#039;&#039;[$timeout], [$data], [$noshutdown], [$loglevel]&#039;&#039;)&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Parameter!! Bedeutung&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$url&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;mandatory&#039;&#039;&lt;br /&gt;
|| Die HTTP-URL, welche aufgerufen werden soll. Diese kann optional Usernamen, Passwort und einen Port enthalten. Sowohl HTTP als auch HTTPS wird hierbei unterstützt.&amp;lt;br&amp;gt;&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&amp;lt;nowiki&amp;gt;http://www.myhost.com/directory/&amp;lt;/nowiki&amp;gt;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&amp;lt;nowiki&amp;gt;https://www.myhost.com/&amp;lt;/nowiki&amp;gt;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&amp;lt;nowiki&amp;gt;http://www.myhost.com:8080/&amp;lt;/nowiki&amp;gt;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&amp;lt;nowiki&amp;gt;http://user:password@www.myhost.com/&amp;lt;/nowiki&amp;gt;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;WICHTIG:&#039;&#039;&#039; Falls ein Username sowie Passwort übergeben werden, so müssen diese vorher jeweils mittels [[#urlEncode|urlEncode()]] in URL-kompatible Form umgewandelt werden um Probleme mit evtl. enthaltenen Sonderzeichen zu vermeiden. &lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$timeout&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;optional&#039;&#039;&lt;br /&gt;
|| Die maximale Dauer in Sekunden für die HTTP-Anfrage&lt;br /&gt;
&lt;br /&gt;
Beispiel: 5 &#039;&#039;(Sekunden)&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Standardwert: 4 Sekunden&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$data&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;optional&#039;&#039;&lt;br /&gt;
|| Wenn man Daten via HTTP-POST übertragen möchte, so kann man die Nutzdaten über &amp;lt;code&amp;gt;$data&amp;lt;/code&amp;gt; übergeben. Die Daten werden dabei als Formulardaten übertragen. Wenn man den Content-Type beeinflussen möchte, oder mehrere Formular-Felder senden möchte, sollte man zur Funktion HttpUtils_BlockingGet oder HttpUtils_NonblockingGet greifen.&lt;br /&gt;
&lt;br /&gt;
Standardwert: &#039;&#039;[leer]&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$noshutdown&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;optional&#039;&#039;&lt;br /&gt;
|| Wenn $noshutdown auf 1 gesetzt ist, wird dem HTTP-Server nicht implizit mitgeteilt, dass die Verbindung nach dem Request geschlossen werden soll. Viele Webserver schließen in solch einem Fall die Verbindung bevor, sie die Antwort senden. Bei 0 wird dem Webserver mitgeteilt, dass der Sendevorgang beendet ist und nun die Antwort abgewartet wird.&lt;br /&gt;
&lt;br /&gt;
Standardwert: 1&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$loglevel&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;optional&#039;&#039;&lt;br /&gt;
|| Das Loglevel, in dem sämtliche Logmeldungen zu dieser HTTP-Abfrage erzeugt werden sollen.&lt;br /&gt;
&lt;br /&gt;
Standardwert: 4&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Funktionsergebnis ist der Inhalt der aufgerufenen Seite in Form eines Strings.&lt;br /&gt;
&lt;br /&gt;
=== HttpUtils_BlockingGet ===&lt;br /&gt;
Wenn die bisher genannten Funktionen nicht ausreichen um die gewünschte Abfrage durchzuführen, so kann man diese Funktion verwenden. Aufgrund zahlreicher Parameter ermöglicht sie viele Anpassungsmöglichkeiten. Diese Funktion hat dabei nicht wie üblich eine Liste an Funktionsparametern, sondern lediglich einen Parameter, welcher eine Hashreferenz mit allen Funktionsparametern darstellt. Dieser Hash enthält sämtliche Parameter inkl. Werten. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Aufruf: &amp;lt;code&amp;gt;HttpUtils_BlockingGet($param)&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Der Parameter $param ist eine Referenz auf eine Hash-Struktur, welche die einzelnen Parameter enthält. Der Hash $param kann folgende Optionen beinhalten:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;width:175px&amp;quot; | Parameter !! style=&amp;quot;width:auto&amp;quot; | Bedeutung&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$param-&amp;gt;{url}&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;mandatory&#039;&#039;&lt;br /&gt;
 || Die HTTP-URL, welche aufgerufen werden soll. Diese kann optional Usernamen, Passwort und einen Port enthalten. Sowohl HTTP als auch HTTPS wird hierbei unterstützt.&amp;lt;br&amp;gt;&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&amp;lt;nowiki&amp;gt;http://www.myhost.com/directory/&amp;lt;/nowiki&amp;gt;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&amp;lt;nowiki&amp;gt;https://www.myhost.com/&amp;lt;/nowiki&amp;gt;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&amp;lt;nowiki&amp;gt;http://www.myhost.com:8080/&amp;lt;/nowiki&amp;gt;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&amp;lt;nowiki&amp;gt;http://user:password@www.myhost.com/&amp;lt;/nowiki&amp;gt;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;WICHTIG:&#039;&#039;&#039; Alternativ können die Zugangsdaten auch in &amp;lt;code&amp;gt;$param-&amp;gt;{user}&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;$param-&amp;gt;{pwd}&amp;lt;/code&amp;gt; übergeben werden. Falls  Username und Passwort in der URL übergeben werden, so müssen diese vorher jeweils mittels [[#urlEncode|urlEncode()]] in URL-kompatible Form umgewandelt werden um Probleme mit evtl. enthaltenen Sonderzeichen (z.B. &amp;quot;:&amp;quot; und &amp;quot;@&amp;quot;) zu vermeiden, da sonst die URL nicht mehr einer validen Syntax entspricht. &lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top&amp;quot; |&#039;&#039;&#039;&amp;lt;code&amp;gt;$param-&amp;gt;{timeout}&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;optional&#039;&#039;&lt;br /&gt;
|| Die maximale Dauer in Sekunden bis der Server eine Antwort liefern muss. Andernfalls wird der Request mit einer Fehlermeldung abgebrochen.&lt;br /&gt;
&lt;br /&gt;
Beispiel: 5 &#039;&#039;(Sekunden)&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Standardwert: 4 Sekunden&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top&amp;quot; |&#039;&#039;&#039;&amp;lt;code&amp;gt;$param-&amp;gt;{data}&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;optional&#039;&#039;&lt;br /&gt;
|| Wenn man Daten via HTTP-POST übertragen möchte, so kann man die Nutzdaten über &amp;lt;code&amp;gt;$param-&amp;gt;{data}&amp;lt;/code&amp;gt; übergeben. Die Daten werden dabei als Formulardaten übertragen. Die Daten können dabei auf zwei Arten übergeben werden:&lt;br /&gt;
&lt;br /&gt;
1. Daten als Zeichenkette:&lt;br /&gt;
:* Die Daten werden komplett als gesamte Zeichenkette in &amp;lt;code&amp;gt;$param-&amp;gt;{data}&amp;lt;/code&amp;gt; abgelegt (z.B.: &amp;lt;code&amp;gt;$param-&amp;gt;{data} = &amp;quot;Jede Menge tolle Daten&amp;quot;&amp;lt;/code&amp;gt;).&lt;br /&gt;
2. Daten als Hash-Struktur:&lt;br /&gt;
:* Die Daten werden als Hash mit Key-Value-Pairs übergeben (z.B.: &amp;lt;code&amp;gt;$param-&amp;gt;{data}{field1} = &amp;quot;value1&amp;quot;, $param{data}{field2} = &amp;quot;value2&amp;quot;, ...&amp;lt;/code&amp;gt; ). Die Daten werden dann als Formulardaten mit mehrfachen Datenfeldern übertragen.&lt;br /&gt;
&lt;br /&gt;
Standardwert: &#039;&#039;[leer]&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$param-&amp;gt;{user}&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;optional&#039;&#039;&lt;br /&gt;
||Im Falle einer notwendigen HTTP-Authentifizierung, der zu benutzende Username um sich gegenüber dem Server zu identifizieren. Alternativ kann der Username und das Passwort auch direkt in der URL mitgegeben werden (siehe jedoch &amp;lt;code&amp;gt;$param-&amp;gt;{hideurl}&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
Standardwert: &#039;&#039;[leer]&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$param-&amp;gt;{pwd}&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;optional&#039;&#039;&lt;br /&gt;
||Im Falle einer notwendigen HTTP-Authentifizierung, das zu benutzende Passwort um sich gegenüber dem Server zu identifizieren. Alternativ kann der Username und das Passwort auch direkt in der URL mitgegeben werden (siehe jedoch &amp;lt;code&amp;gt;$param-&amp;gt;{hideurl}&amp;lt;/code&amp;gt;).&lt;br /&gt;
Standardwert: &#039;&#039;[leer]&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$param-&amp;gt;{noshutdown}&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;optional&#039;&#039;&lt;br /&gt;
||Wenn &amp;lt;code&amp;gt;$param-&amp;gt;{noshutdown}&amp;lt;/code&amp;gt; auf 1 gesetzt ist, wird dem HTTP-Server nicht implizit mitgeteilt, dass die Verbindung nach dem Request geschlossen werden soll. Viele Webserver schließen in solch einem Fall die Verbindung, bevor sie die Antwort senden. Bei 0 wird dem Webserver mitgeteilt, dass der Sendevorgang beendet ist und nun die Antwort abgewartet wird.&lt;br /&gt;
&lt;br /&gt;
Standardwert: 1&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$param-&amp;gt;{loglevel}&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;optional&#039;&#039;&lt;br /&gt;
|| Das Loglevel, in dem sämtliche Logmeldungen zu dieser HTTP-Abfrage erzeugt werden sollen.&lt;br /&gt;
&lt;br /&gt;
Standardwert: 4&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$param-&amp;gt;{hideurl}&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;optional&#039;&#039;&lt;br /&gt;
|| Wenn dieser Parameter den Wert 1 trägt, wird die gesamte URL in sämtlichen Log-Ausgaben unkenntlich gemacht. Dies notwendig, wenn z.B. Zugangsdaten direkt in der URL angegeben wurden, oder die URL ein geheimes Token oder andere schützenswerte Informationen enthält.&lt;br /&gt;
&lt;br /&gt;
Standardwert: 0&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$param-&amp;gt;{ignoreredirects}&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;optional&#039;&#039;&lt;br /&gt;
|| Wenn dieser Parameter den Wert 1 trägt, werden Umleitungen durch den Server ignoriert und der Request beendet. Dies kann erforderlich sein um evtl. Cookies aus der Antwort, welche eine Umleitung enthält aus dem HTTP Header zu extrahieren um diese im nächsten Request weiterzuverwenden.&lt;br /&gt;
&lt;br /&gt;
Standardwert: 0&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$param-&amp;gt;{method}&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;optional&#039;&#039;&lt;br /&gt;
||Die HTTP-Methode, welche zur Abfrage verwendet werden soll. Sofern keine Daten übertragen werden ist dies standardmäßig &amp;quot;GET&amp;quot;, ansonsten &amp;quot;POST&amp;quot;. Es können aber auch andere Methoden verwendet werden.&lt;br /&gt;
&lt;br /&gt;
Standardwert: &amp;quot;GET&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$param-&amp;gt;{keepalive}&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;optional&#039;&#039;&lt;br /&gt;
||Wenn dieser Parameter auf 1 gesetzt ist, wird dem Server der Wunsch mitgeteilt, die Verbindung offen zu lassen für weitere Anfragen. Sobald die Antwort auf den jeweiligen Request eintrifft, bleibt die TCP-Verbindung bestehen (sofern der Server dies unterstützt). Anschließend kann man den Parameter-Hash mit einer neuen URL und Optionen füllen. Der Parameter &amp;quot;keepalive&amp;quot; sollte dabei weiterhin gesetzt bleiben, sofern die Verbindung auch weiterhin möglichst erhalten bleiben soll. &lt;br /&gt;
&lt;br /&gt;
Um eine offene Verbindung endgültig zu schließen, muss die Funktion [[HttpUtils#HttpUtils_Close|HttpUtils_Close]] aufgerufen werden.&lt;br /&gt;
&lt;br /&gt;
Standardwert: 0&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$param-&amp;gt;{header}&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;optional&#039;&#039;&lt;br /&gt;
|| Eigene HTTP-Header-Zeilen können über diesen Parameter eingebracht werden. Er kann dazu genutzt werden um z.B. den Content-Type festzulegen, oder einfach nur zusätzliche Header-Felder zu setzen. Es gibt zwei Möglichkeiten, diesen Parameter zu befüllen.&lt;br /&gt;
&lt;br /&gt;
# Als &#039;&#039;&#039;Zeichenkette&#039;&#039;&#039;. Mehrere Header-Zeilen müssen dabei mit &amp;quot;\r\n&amp;quot; getrennt werden.&lt;br /&gt;
# Als &#039;&#039;&#039;Hash-Struktur&#039;&#039;&#039;. Hierbei findet eine Zuordnung von Header-Bezeichnung zum Wert als Key-Value-Pair statt.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
* &amp;lt;code&amp;gt;&amp;quot;User-Agent: Mozilla/1.22&#039;&#039;&#039;&amp;lt;u&amp;gt;\r\n&amp;lt;/u&amp;gt;&#039;&#039;&#039;Content-Type: application/xml&amp;quot;&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;&amp;quot;Content-Type: application/xml&amp;quot;&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;{ &amp;quot;User-Agent&amp;quot; =&amp;gt;  &amp;quot;Mozilla/1.22&amp;quot;, &amp;quot;Content-Type&amp;quot; =&amp;gt; &amp;quot;application/xml&amp;quot; }&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;{ &amp;quot;Content-Type&amp;quot; =&amp;gt; &amp;quot;application/xml&amp;quot; }&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Standardwert: &#039;&#039;[leer]&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$param-&amp;gt;{sslargs}&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;optional&#039;&#039;&lt;br /&gt;
|| Eigene SSL-Optionen können über diesen Parameter eingebracht werden. Er kann dazu genutzt werden um z.B. die SSL-Zertifikats Verifikation abzuschalten. Die SSL-Optionen müssen als eigene Hash-Referenz übergeben werden. Eine Liste aller möglichen Optionen findet man in der Perl-Dokumentation zu [http://search.cpan.org/~sullr/IO-Socket-SSL-2.016/lib/IO/Socket/SSL.pod#Description_Of_Methods IO::Socket::SSL].&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
* &amp;lt;code&amp;gt;$param-&amp;gt;{sslargs} = { SSL_verify_mode =&amp;gt; 0, sslOpt2 =&amp;gt; &#039;sslVal2&#039; }&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Standardwert: &#039;&#039;{ }&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$param-&amp;gt;{httpversion}&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;optional&#039;&#039;&lt;br /&gt;
|| Die HTTP-Version, welche zur Abfrage verwendet werden soll. Standardmäßig werden alle Abfragen mit HTTP/1.0 durchgeführt. Falls es jedoch notwendig ist HTTP/1.1 zu verwenden, so sollte &amp;lt;code&amp;gt;$param-&amp;gt;{httpversion}&amp;lt;/code&amp;gt; auf &amp;quot;1.1&amp;quot; gesetzt werden. Bei Version 1.1 wird automatisch der Header &amp;quot;&amp;lt;code&amp;gt;Connection: close&amp;lt;/code&amp;gt;&amp;quot; implizit mitgesendet.&lt;br /&gt;
&lt;br /&gt;
Standardwert: &amp;quot;1.0&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$param-&amp;gt;{digest}&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;optional&#039;&#039;&lt;br /&gt;
|| Wenn dieser Parameter den Wert 1 trägt, wird bei vorhandenen Authentifizierungsdaten (Username+Passwort) eine Authentifizierung via Digest-Verfahren nach RFC 2617&amp;lt;ref name=&amp;quot;rfc2617&amp;quot;&amp;gt;RFC 2617 - HTTP Authentication: Basic and Digest Access Authentication&amp;lt;/ref&amp;gt; erwartet. Die Anmeldedaten werden dann nur verwendet, wenn dieser explizit eine HTTP Digest Authentifizierung einleitet. Ist dieser Parameter nicht gesetzt (Wert: 0), wird bei vorhandenen Authentifizierungsdaten immer eine HTTP Basic&amp;lt;ref name=&amp;quot;rfc2617&amp;quot; /&amp;gt; Authentifizierung im Request mitgeschickt.&lt;br /&gt;
&lt;br /&gt;
Standardwert: 0&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$param-&amp;gt;{compress}&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;optional&#039;&#039;&lt;br /&gt;
|| Wenn dieser Parameter den Wert 1 trägt, wird dem Server die Verwendung von Komprimierung in Form GZIP oder alternativ Deflate&amp;lt;ref&amp;gt;RFC 1951 - DEFLATE Compressed Data Format Specification version 1.3&amp;lt;/ref&amp;gt; zur Übertragung der Antwort ermöglicht. Die komprimierte Antwort wird dabei direkt durch HttpUtils.pm wieder dekomprimiert. Wird dieser Parameter auf den Wert 0 gesetzt, so wird keine Komrpimierung ermöglicht.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;WICHTIG&#039;&#039;&#039;: Die Verwendung von Komprimierung in HttpUtils.pm kann mit dem globalen Attribut &amp;quot;httpcompress&amp;quot; (Standardwert: 1) durch setzen auf 0 für die gesamte FHEM Installation durch den Nutzer deaktiviert werden. In diesem Fall ist der Parameter &amp;lt;code&amp;gt;$param-&amp;gt;{compress} = 1&amp;lt;/code&amp;gt; wirkungslos.&lt;br /&gt;
&lt;br /&gt;
Standardwert: 1&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Als Rückgabewert von HttpUtils_BlockingGet wird ein Array mit zwei Rückgabewerten zurückgegeben:&lt;br /&gt;
:&amp;lt;code&amp;gt;($err, $data) = HttpUtils_BlockingGet( … )&amp;lt;/code&amp;gt;&lt;br /&gt;
Diese zwei Rückgabewerte haben folgende Bedeutung:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Rückgabewert !! Bedeutung&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$err&amp;lt;/code&amp;gt;&#039;&#039;&#039;|| Falls beim Aufruf der URL ein Fehler aufgetreten ist (z.B. Server nicht erreichbar oder Verbindungstimeout), dann ist dieser Wert mit einer Fehlermeldung gefüllt. &lt;br /&gt;
&lt;br /&gt;
Wenn kein Fehler aufgetreten ist, ist dieser Wert mit einem Leerstring gefüllt (&amp;lt;code&amp;gt;$err = &amp;quot;&amp;quot;&amp;lt;/code&amp;gt;)&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$data&amp;lt;/code&amp;gt;&#039;&#039;&#039;|| Die Ergebnisdaten, welche der HTTP-Server zurückgeliefert hat. Die Daten werden als Klartext in Form eines gesamten Strings zurückgegeben.&lt;br /&gt;
&lt;br /&gt;
Falls ein Fehler aufgetreten ist, ist dieser Wert mit einem Leersting gefüllt (&amp;lt;code&amp;gt;$data = &amp;quot;&amp;quot;&amp;lt;/code&amp;gt;)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== HttpUtils_NonblockingGet ===&lt;br /&gt;
{{Randnotiz|RNText=Die Funktion HttpUtils_NonblockingGet ist nicht komplett durchgehend &amp;quot;non-blocking&amp;quot;. DNS-Abfragen sind weiterhin blockierend. Insbesondere wenn der DNS-Name nicht aufgelöst werden kann.}}&lt;br /&gt;
Diese Funktion arbeitet ähnlich wie HttpUtils_BlockingGet. Allerdings wird das Ergebnis nicht als Funktionsergebnis zurückgegeben. Die Funktion HttpUtils_NonblockingGet initiiert den Verbindungsaufbau und übergibt alles weitere an FHEM interne Routinen. Sobald eine Antwort vom HTTP-Server eintrifft, wird eine Callback-Funktion mit verschiedenen Parametern (unter anderem auch das Ergebnis) aufgerufen, um die Antwort entgegenzunehmen und weiter zu verarbeiten.&lt;br /&gt;
&lt;br /&gt;
Der Aufruf ist daher ähnlich zu HttpUtils_BlockingGet mit nur einem Parameter-Hash:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Aufruf: &amp;lt;code&amp;gt;HttpUtils_NonblockingGet($param)&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;width:175px&amp;quot; | Parameter !! style=&amp;quot;width:auto&amp;quot; | Bedeutung&lt;br /&gt;
|-&lt;br /&gt;
| colspan=&amp;quot;2&amp;quot; style=&amp;quot;text-align:center&amp;quot; | &#039;&#039;&#039;&#039;&#039;&amp;lt;br&amp;gt;Alle Hash-Parameter, welche für HttpUtils_BlockingGet gelten, sind auch für HttpUtils_NonblockingGet gültig&#039;&#039;&#039;&#039;&#039;&amp;lt;br&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$param-&amp;gt;{callback}&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;mandatory&#039;&#039;&lt;br /&gt;
|| Eine Funktion (oder eine Referenz auf eine Funktion), welche die Ergebnisdaten entgegennimmt und die Antwort entsprechend weiterverarbeitet. Die Callback-Funktion muss dabei 3 Parameter erwarten. Die Funktionsparameter der Callback-Funktion werden im nachfolgenden Abschnitt näher erläutert.&lt;br /&gt;
&lt;br /&gt;
Beispiel: &lt;br /&gt;
* &amp;lt;code&amp;gt;$param-&amp;gt;{callback} = \&amp;amp;MyCallbackFn&amp;lt;/code&amp;gt; — &#039;&#039;(Referenzzeiger auf Funktionsname)&#039;&#039;&lt;br /&gt;
* &amp;lt;code&amp;gt;$param-&amp;gt;{callback} = sub(){ … }&amp;lt;/code&amp;gt; —  &#039;&#039;(direkte Funktionsdefinition)&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top;white-space:nowrap;&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$param-&amp;gt;{incrementalTimeout}&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;optional&#039;&#039;&lt;br /&gt;
|| Sofern gesetzt (Wert: &amp;lt;code&amp;gt;1&amp;lt;/code&amp;gt;), wird der Timeout nach jedem Empfang von Teil-Daten wieder zurückgesetzt. So können größere Datenmengen von langsamen Sendern empfangen werden, ohne den Parameter &amp;lt;code&amp;gt;timeout&amp;lt;/code&amp;gt; zu erhöhen und dadurch tatsächliche Verbindungsprobleme erst spät zu erkennen.&lt;br /&gt;
&lt;br /&gt;
Standardwert: 0&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;Benutzerdefinierte Parameter&#039;&#039; &lt;br /&gt;
|| Es können im Hash weitere benutzerdefinierte Parameter gesetzt werden, welche evtl. in der Callback-Funktion benötigt werden, um die Antwort korrekt zu verarbeiten.&lt;br /&gt;
&lt;br /&gt;
Zum Beispiel im Rahmen der Modul-Programmierung wäre das &amp;lt;code&amp;gt;$hash&amp;lt;/code&amp;gt; der aktuellen Definition. Alle gesetzten Parameter sind in der Callback-Funktion wieder über &amp;lt;code&amp;gt;$param&amp;lt;/code&amp;gt; direkt abrufbar und können ausgewertet werden.&lt;br /&gt;
&lt;br /&gt;
Beispiel: &lt;br /&gt;
* &amp;lt;code&amp;gt;$param-&amp;gt;{hash} = $hash&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;$param-&amp;gt;{command} = &amp;quot;on&amp;quot;&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
|} &lt;br /&gt;
&lt;br /&gt;
Ein Funktionsrückgabewert von HttpUtils_NonblockingGet existiert nicht, da die eigentliche Rückgabe der Daten über die Callback-Funktion erfolgt. Die Callback-Funktion wird aufgerufen, sobdald der HTTP-Request abgeschlossen ist, oder ein Fehler aufgetreten ist. Der Funktionsaufruf erfolgt mit den folgenden Parametern:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt; &#039;&#039;MyCallbackFn&#039;&#039; ( $param, $err, $data )&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Diese 3 Parameter haben dabei folgende Bedeutung:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Parameter !! Bedeutung&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$param&amp;lt;/code&amp;gt;&#039;&#039;&#039; || Der Parameter-Hash, mit allen Argumenten die beim Aufruf der Funktion übergeben worden sind.&lt;br /&gt;
&lt;br /&gt;
Es ist möglich, dass der Parameter-Hash nach erfolgter Abfrage zusätzliche oder veränderte Elemente enthält:&lt;br /&gt;
* &#039;&#039;&#039;&amp;lt;code&amp;gt;$param-&amp;gt;{redirects}&amp;lt;/code&amp;gt;&#039;&#039;&#039; - Die Anzahl an Umleitungen die erfolgte, bis die Anfrage beantwortet wurde.&lt;br /&gt;
* &#039;&#039;&#039;&amp;lt;code&amp;gt;$param-&amp;gt;{url}&amp;lt;/code&amp;gt;&#039;&#039;&#039; - Die URL, die nach erfolgter Umleitung die Anfrage beantwortet hat.&lt;br /&gt;
* &#039;&#039;&#039;&amp;lt;code&amp;gt;$param-&amp;gt;{protocol}&amp;lt;/code&amp;gt;&#039;&#039;&#039; - Das verwendete Protokoll, welches zur Abfrage verwendet wurde (&amp;quot;http&amp;quot; oder &amp;quot;https&amp;quot;).&lt;br /&gt;
* &#039;&#039;&#039;&amp;lt;code&amp;gt;$param-&amp;gt;{path}&amp;lt;/code&amp;gt;&#039;&#039;&#039; - Der Pfad, welcher auf dem HTTP-Server angefragt wurde.&lt;br /&gt;
* &#039;&#039;&#039;&amp;lt;code&amp;gt;$param-&amp;gt;{host}&amp;lt;/code&amp;gt;&#039;&#039;&#039; - Der Name oder die IP-Adresse des HTTP-Servers.&lt;br /&gt;
* &#039;&#039;&#039;&amp;lt;code&amp;gt;$param-&amp;gt;{httpheader}&amp;lt;/code&amp;gt;&#039;&#039;&#039; - Der gesamte HTTP Header, welcher der Server bei der letzten Antwort zurücklieferte.&lt;br /&gt;
* &#039;&#039;&#039;&amp;lt;code&amp;gt;$param-&amp;gt;{code}&amp;lt;/code&amp;gt;&#039;&#039;&#039; - Der HTTP-Statuscode, mit dem die Anfrage vom Server beantwortet wurde.&lt;br /&gt;
* &#039;&#039;&#039;&amp;lt;code&amp;gt;$param-&amp;gt;{addr}&amp;lt;/code&amp;gt;&#039;&#039;&#039; - Die HTTP-URL ohne Pfad und evtl. Authentifizerungsinformationen des HTTP-Servers (z.B. &amp;quot;&amp;lt;nowiki&amp;gt;http://myserver.com:8080&amp;lt;/nowiki&amp;gt;&amp;quot;).&lt;br /&gt;
* &#039;&#039;&#039;&amp;lt;code&amp;gt;$param-&amp;gt;{auth}&amp;lt;/code&amp;gt;&#039;&#039;&#039; - Ein Flag (0/1) ob Zugangsdaten für den Request zur Verfügung standen (durch Angabe in der URL oder direkt in &amp;lt;code&amp;gt;$param&amp;lt;/code&amp;gt;).&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$err&amp;lt;/code&amp;gt;&#039;&#039;&#039;|| Falls beim Aufruf der URL ein Fehler aufgetreten ist (z.B. Server nicht erreichbar oder Verbindungstimeout), dann ist dieser Wert mit einer Fehlermeldung gefüllt. &lt;br /&gt;
&lt;br /&gt;
Wenn kein Fehler aufgetreten ist, ist dieser Wert mit einem Leerstring gefüllt (&amp;lt;code&amp;gt;$err = &amp;quot;&amp;quot;&amp;lt;/code&amp;gt;)&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$data&amp;lt;/code&amp;gt;&#039;&#039;&#039;|| Die Ergebnisdaten, welche der HTTP-Server zurückgeliefert hat. Die Daten werden als Klartext in Form eines gesamten Strings zurückgegeben. Es handelt sich hierbei ausschließlich um den HTTP-Body. &lt;br /&gt;
&lt;br /&gt;
Falls ein Fehler aufgetreten ist, ist dieser Wert mit einem Leersting gefüllt (&amp;lt;code&amp;gt;$data = &amp;quot;&amp;quot;&amp;lt;/code&amp;gt;)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Die Callback-Funktion kann nun die Daten aus der HTTP-Antwort verarbeiten oder bei Fehlern entsprechende Log-Meldungen ausgeben.&lt;br /&gt;
&lt;br /&gt;
==== Beispiel #1 für HttpUtils_NonblockingGet() für Modulprogrammierer ====&lt;br /&gt;
Das folgende Beispiel soll eine Hilfestellung für eigene Anwendungen geben&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;use HttpUtils;&lt;br /&gt;
sub X_PerformHttpRequest($)&lt;br /&gt;
{&lt;br /&gt;
    my ($hash, $def) = @_;&lt;br /&gt;
    my $name = $hash-&amp;gt;{NAME};&lt;br /&gt;
    my $param = {&lt;br /&gt;
                    url        =&amp;gt; &amp;quot;http://www.foo.de/&amp;quot;,&lt;br /&gt;
                    timeout    =&amp;gt; 5,&lt;br /&gt;
                    hash       =&amp;gt; $hash,                                                                                 # Muss gesetzt werden, damit die Callback funktion wieder $hash hat&lt;br /&gt;
                    method     =&amp;gt; &amp;quot;GET&amp;quot;,                                                                                 # Lesen von Inhalten&lt;br /&gt;
                    header     =&amp;gt; &amp;quot;User-Agent: TeleHeater/2.2.3\r\nAccept: application/json&amp;quot;,                            # Den Header gemäß abzufragender Daten ändern&lt;br /&gt;
                    callback   =&amp;gt; \&amp;amp;X_ParseHttpResponse                                                                  # Diese Funktion soll das Ergebnis dieser HTTP Anfrage bearbeiten&lt;br /&gt;
                };&lt;br /&gt;
&lt;br /&gt;
    HttpUtils_NonblockingGet($param);                                                                                    # Starten der HTTP Abfrage. Es gibt keinen Return-Code. &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
sub X_ParseHttpResponse($)&lt;br /&gt;
{&lt;br /&gt;
    my ($param, $err, $data) = @_;&lt;br /&gt;
    my $hash = $param-&amp;gt;{hash};&lt;br /&gt;
    my $name = $hash-&amp;gt;{NAME};&lt;br /&gt;
&lt;br /&gt;
    if($err ne &amp;quot;&amp;quot;)                                                                                                      # wenn ein Fehler bei der HTTP Abfrage aufgetreten ist&lt;br /&gt;
    {&lt;br /&gt;
        Log3 $name, 3, &amp;quot;error while requesting &amp;quot;.$param-&amp;gt;{url}.&amp;quot; - $err&amp;quot;;                                               # Eintrag fürs Log&lt;br /&gt;
        readingsSingleUpdate($hash, &amp;quot;fullResponse&amp;quot;, &amp;quot;ERROR&amp;quot;, 0);                                                        # Readings erzeugen&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    elsif($data ne &amp;quot;&amp;quot;)                                                                                                  # wenn die Abfrage erfolgreich war ($data enthält die Ergebnisdaten des HTTP Aufrufes)&lt;br /&gt;
    {&lt;br /&gt;
        Log3 $name, 3, &amp;quot;url &amp;quot;.$param-&amp;gt;{url}.&amp;quot; returned: $data&amp;quot;;                                                         # Eintrag fürs Log&lt;br /&gt;
&lt;br /&gt;
        # An dieser Stelle die Antwort parsen / verarbeiten mit $data&lt;br /&gt;
&lt;br /&gt;
        readingsSingleUpdate($hash, &amp;quot;fullResponse&amp;quot;, $data, 0);                                                          # Readings erzeugen&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    # Damit ist die Abfrage zuende.&lt;br /&gt;
    # Evtl. einen InternalTimer neu schedulen&lt;br /&gt;
}&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Beispiel #2 eines Snippets um eine Datei herunterzuladen und lokal zu speichern ====&lt;br /&gt;
Dieses vollständige Beispiel eines Device verwendet &amp;lt;code&amp;gt;HttpUtils_NonblockingGet()&amp;lt;/code&amp;gt; um eine Datei herunterzuladen und im lokalem Dateisystem mit den Rechten des FHEM Nutzers zu speichern.&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
defmod MyDownloader dummy&lt;br /&gt;
attr MyDownloader userattr FilePath&lt;br /&gt;
attr MyDownloader comment &#039;\&lt;br /&gt;
Beispiel:\&lt;br /&gt;
set MyDownloader job Filepath=&amp;quot;/opt/fhem/www/Dateiname.bin&amp;quot; https://example.com/datei.bin\&lt;br /&gt;
&#039;&lt;br /&gt;
attr MyDownloader readingList job&lt;br /&gt;
attr MyDownloader setList job&lt;br /&gt;
attr MyDownloader userReadings result:job:.* {\&lt;br /&gt;
	my $hash = $defs{$name};;\&lt;br /&gt;
	my $job = ReadingsVal($name, &amp;quot;job&amp;quot;, &#039;https://fhem.de/www/images/default/fhemicon.png&#039;);;\&lt;br /&gt;
	my $path = AttrVal($name, &amp;quot;Filepath&amp;quot;, &amp;quot;/opt/fhem/www/download.bin&amp;quot;);;\&lt;br /&gt;
	\&lt;br /&gt;
	if ($job =~ s/(?:Filepath|Dateipfad)=&amp;quot;(.*?)&amp;quot;//) {\&lt;br /&gt;
		$path = $1;;\&lt;br /&gt;
	}\&lt;br /&gt;
	\&lt;br /&gt;
	my $fnFct = sub {\&lt;br /&gt;
		my ($param, $err, $data) = @_;;\&lt;br /&gt;
		\&lt;br /&gt;
		my $len = $data ? length($data) : 0;;\&lt;br /&gt;
		my $error = $err ? $err : FileWrite($path, $data);;\&lt;br /&gt;
		\&lt;br /&gt;
		readingsBeginUpdate($hash);;\&lt;br /&gt;
		readingsBulkUpdate($hash, &amp;quot;busy&amp;quot;, 0, 1);;\&lt;br /&gt;
		readingsBulkUpdate($hash, &amp;quot;result&amp;quot;, &amp;quot;saved $len Bytes&amp;quot;, 1) if !$error;;\&lt;br /&gt;
		readingsBulkUpdate($hash, &amp;quot;result&amp;quot;, &amp;quot;error ($error, $err)&amp;quot;, 1) if $error;;\&lt;br /&gt;
		readingsEndUpdate($hash, 1);;\&lt;br /&gt;
	};;\&lt;br /&gt;
	\&lt;br /&gt;
	if ( ReadingsVal($name, &amp;quot;busy&amp;quot;, 0) == 0 ) {\&lt;br /&gt;
		readingsBulkUpdate($hash, &amp;quot;busy&amp;quot;, 1, 1);;\&lt;br /&gt;
		$job =~ s/^\s+|\s+$//g;;\&lt;br /&gt;
		my $param = {\&lt;br /&gt;
                    url        =&amp;gt; $job,\&lt;br /&gt;
                    timeout    =&amp;gt; 10,\&lt;br /&gt;
                    hash       =&amp;gt; $hash,\&lt;br /&gt;
                    method     =&amp;gt; &amp;quot;GET&amp;quot;,\&lt;br /&gt;
                    callback   =&amp;gt; $fnFct\&lt;br /&gt;
                };;\&lt;br /&gt;
		HttpUtils_NonblockingGet($param);;\&lt;br /&gt;
		return &amp;quot;download job started&amp;quot;;;\&lt;br /&gt;
	}\&lt;br /&gt;
	\&lt;br /&gt;
	return;;\&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Zugehöriger Forumspost: {{Link2Forum|Topic=143117|LinkText=Datei herunterladen, Downloader, HttpUtils_NonblockingGet}}&lt;br /&gt;
&lt;br /&gt;
=== HttpUtils_Close ===&lt;br /&gt;
Für den Abbruch von offenen Verbindungen und noch laufenden NonBockingGet-Aufrufen gibt es die Funktion HttpUtils_Close. Diese kann z.B. beim Löschen eines Devices oder Herunterfahren des Servers aufgerufen werden, um bestehende Verbindungen zu schliessen.&lt;br /&gt;
&lt;br /&gt;
Wenn man den Parameter &amp;quot;keepalive&amp;quot; beim Aufruf von HttpUtils_NonBlockingGet/HttpUtils_BlockingGet gesetzt hat, muss man HttpUtils_Close aufrufen um die Verbindung tatsächlich zu schließen, da diese noch durch den Server offen gehalten werden kann.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Aufruf: &amp;lt;code&amp;gt;HttpUtils_Close($param)&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;width:175px&amp;quot; | Parameter !! style=&amp;quot;width:auto&amp;quot; | Bedeutung&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$param&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;mandatory&#039;&#039;&lt;br /&gt;
|| Der Hash, der beim vorherigen Aufruf an HTTPUtils Funktionen (HttpUtils_NonblockingGet oder HttpUtils_BlockingGet) übergeben wurde&lt;br /&gt;
&lt;br /&gt;
|} &lt;br /&gt;
&lt;br /&gt;
== Hilfsfunktionen ==&lt;br /&gt;
&lt;br /&gt;
=== urlEncode ===&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
$encoded = urlEncode($string);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Die Funktion urlEncode() wandelt die übergebene Zeichenkette &amp;lt;code&amp;gt;$string&amp;lt;/code&amp;gt; in eine URL-kompatible&amp;lt;ref name=&amp;quot;rfc3986-2.1&amp;quot;&amp;gt;[https://tools.ietf.org/html/rfc3986#section-2.1 RFC 3989] - Abschnitt 2.1 &amp;quot;Percent-Encoding&amp;quot;&amp;lt;/ref&amp;gt; Zeichenkette um. Der Rückgabewert &amp;lt;code&amp;gt;$encoded&amp;lt;/code&amp;gt; ist eine Zeichenkette welche innerhalb einer URL verwendet werden kann, bspw. als Wert eines GET-Parameters.&lt;br /&gt;
&lt;br /&gt;
Parameter:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Parameter !! Bedeutung&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$string&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;mandatory&#039;&#039;&lt;br /&gt;
|| Die Zeichenkette, welche in eine URL-kompatible Form umgewandelt werden soll.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Rückgabewerte:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Rückgabewert !! Bedeutung&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$encoded &amp;lt;/code&amp;gt;&#039;&#039;&#039; || Die URL-kompatible Version der übergebenen Zeichenkette. Problematische Zeichen sind in diesem Wert durch ein Prozentzeichen gefolgt von der Hexadezimaldarstellung des Zeichens dargestellt.&lt;br /&gt;
&lt;br /&gt;
Bsp:&lt;br /&gt;
* &amp;quot;a: b&amp;quot; =&amp;gt; &amp;quot;a%3a%20b&amp;quot;&lt;br /&gt;
* &amp;quot;a/b&amp;quot; =&amp;gt; &amp;quot;a%2fb&amp;quot;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== urlDecode ===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
$string = urlDecode($encoded);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Die Funktion urlDecode () normalisiert die übergebene Zeichenkette &amp;lt;code&amp;gt;$encoded&amp;lt;/code&amp;gt; und ersetzt dabei sämtliche Hexadezimaldarstellungen&amp;lt;ref name=&amp;quot;rfc3986-2.1&amp;quot; /&amp;gt; (eingeleitet mit einem Prozentzeichen) durch die entsprechenden Zeichen. Der Rückgabewert &amp;lt;code&amp;gt;$string&amp;lt;/code&amp;gt; ist eine Zeichenkette welche keinerlei Hexadezimaldarstellungen mehr aufweist.&lt;br /&gt;
&lt;br /&gt;
Parameter:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Parameter !! Bedeutung&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$encoded&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;mandatory&#039;&#039;&lt;br /&gt;
|| Eine Zeichenkette, welche URL-kombatible Hexadezimaldarstellungen enthält.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Rückgabewerte:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Rückgabewert !! Bedeutung&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$string&amp;lt;/code&amp;gt;&#039;&#039;&#039; || Die normale Version der übergebenen Zeichenkette. Sämtliche Hexadezimalddarstellungen sind in ihre entsprechenden Zeichen umgewandelt.&lt;br /&gt;
&lt;br /&gt;
Bsp:&lt;br /&gt;
* &amp;quot;a%3a%20b&amp;quot; =&amp;gt; &amp;quot;a: b&amp;quot;&lt;br /&gt;
* &amp;quot;a%2fb&amp;quot; =&amp;gt; &amp;quot;a/b&amp;quot;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Referenzen ==&lt;br /&gt;
&amp;lt;references /&amp;gt;&lt;/div&gt;</summary>
		<author><name>Ph1959de</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=FritzBoxUtils&amp;diff=40728</id>
		<title>FritzBoxUtils</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=FritzBoxUtils&amp;diff=40728"/>
		<updated>2026-01-12T19:14:57Z</updated>

		<summary type="html">&lt;p&gt;Ph1959de: (Ober)Kategorie entfernt&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Infobox Modul&lt;br /&gt;
|ModPurpose=Hilfsfunktionen zur Unterstützung von Fritz!Box Geräten in FHEM&lt;br /&gt;
|ModType=u&lt;br /&gt;
|ModForumArea=FRITZ!Box&lt;br /&gt;
|ModTechName=FritzBoxUtils.pm&lt;br /&gt;
|ModOwner=rudolfkoenig ({{Link2FU|8|Forum}} / [[Benutzer Diskussion:Rudolfkoenig|Wiki]])&lt;br /&gt;
}}&lt;br /&gt;
Das &amp;quot;Modul&amp;quot; [[FritzBoxUtils]](.pm) stellt eine Reihe von Funktionen zur Verfügung, die für die Anbindung von Fritz!Box Geräten des Herstellers AVM an FHEM benötigt werden bzw. benutzt werden können.&lt;br /&gt;
&lt;br /&gt;
== Verwendung ==&lt;br /&gt;
Funktionen aus FritzBoxUtils werden z.B. verwendet oder beschrieben in:&lt;br /&gt;
* [[Fritzbox: WLAN ein/ausschalten]]&lt;br /&gt;
* [[Callmonitor mit Anruferliste und Zusatzfunktionen]]&lt;br /&gt;
* [[E-Mail senden]]&lt;br /&gt;
&lt;br /&gt;
== Links ==&lt;br /&gt;
* ...&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:FritzBox]]&lt;/div&gt;</summary>
		<author><name>Ph1959de</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=LandroidUtils&amp;diff=40727</id>
		<title>LandroidUtils</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=LandroidUtils&amp;diff=40727"/>
		<updated>2026-01-12T19:11:15Z</updated>

		<summary type="html">&lt;p&gt;Ph1959de: Kategorie &amp;quot;Development&amp;quot; entfernt, da nicht zutreffend&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Infobox Modul&lt;br /&gt;
|ModPurpose=Hilfsfunktionen für Mähroboter&lt;br /&gt;
|ModType=u&lt;br /&gt;
|ModForumArea=MQTT&lt;br /&gt;
|ModTechName=LandroidUtils.pm&lt;br /&gt;
|ModOwner=rudolfkoenig ({{Link2FU|8|Forum}} / [[Benutzer Diskussion:Rudolfkoenig|Wiki]])&lt;br /&gt;
frober  ({{Link2FU|8261|Forum}} / [[Benutzer Diskussion:frober|Wiki]])&lt;br /&gt;
}}&lt;br /&gt;
Das &amp;quot;Modul&amp;quot; [[LandroidUtils]](.pm) stellt eine Reihe von Funktionen zur Verfügung, die für die Anbindung bestimmter Mähroboter an FHEM benötigt werden.&lt;br /&gt;
&lt;br /&gt;
== Verwendung ==&lt;br /&gt;
Die Verwendung der verfügbaren Funktionen ist (z.B.) beschrieben in:&lt;br /&gt;
* [[Mähroboter: Worx Landroid, Kress, Landxcape]]&lt;br /&gt;
&lt;br /&gt;
== Links ==&lt;br /&gt;
* Forenthema {{Link2Forum|Topic=111959|LinkText=MQTT2 für Worx Landroid Mähroboter}}&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Mähroboter]]&lt;/div&gt;</summary>
		<author><name>Ph1959de</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=Solaranlage_Komplettbeispiel_Fronius_BYD&amp;diff=40726</id>
		<title>Solaranlage Komplettbeispiel Fronius BYD</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=Solaranlage_Komplettbeispiel_Fronius_BYD&amp;diff=40726"/>
		<updated>2026-01-12T18:09:02Z</updated>

		<summary type="html">&lt;p&gt;Ph1959de: Typo&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==Einbindung eines Fronius-Wechselrichters, BYD-Speicher, SolarForecast, evcc-Autoladen und FTUI 2 Darstellung in FHEM==&lt;br /&gt;
[[Datei:Screenshot 2024-11-14 164746.jpg|mini|Übersicht]]&lt;br /&gt;
Dies Wiki beschreibt die beispielhafte Einbindung einer Solaranlage in FHEM. (Stand 02.01.2026)&lt;br /&gt;
&lt;br /&gt;
Alle Module zur Steuerung der Komponenten sind bereits in FHEM vorhanden bzw. werden gerade weiterentwickelt.&lt;br /&gt;
&lt;br /&gt;
Ein Wechselrichter der Fa Fronius aus Österreich sowie der China-Speicher von BYD sind oft die Grundeinheiten einer Einfamilienhauslösung.&lt;br /&gt;
&lt;br /&gt;
Alle notwendigen Readings werden über die vorhandenen Module speziell für dies Paket über Userreadings erzeugt, so dass auch andere Erzeuger oder Speicher in das Grundkonstrukt übernommen werden können. Es gibt auch einen weiteren Grund für die Userreadings: Der Wechselrichter der Frima Fronius erzeugt einige Daten in relativ langen Zeitabständen (&amp;gt;10 Min) so dass diese z.B. nicht für Intergrale zur Leistungsanzeige benutzt werden können.&lt;br /&gt;
&lt;br /&gt;
Diese Beispielanlage besteht aus 4 Strings. Zwei sind an den Fronius WR und zwei weitere an zwei Growatt 2KW WR angschlossen. Weiterhin gibt es einen zusätzlichen Fronius Prower Meter (1 Phasig) für die beiden Growatt WR. &lt;br /&gt;
[[Datei:Screenshot 20250612 192318 Fully Kiosk Browser.jpg|mini|ftui2 Übersicht]]&lt;br /&gt;
&lt;br /&gt;
=== Vorbedingungen ===&lt;br /&gt;
FHEM läuft und hat genügend Platz für Logdaten (dBLog &amp;gt;5GB/Jahr wenn wirklich alles geloggt wird)&lt;br /&gt;
&lt;br /&gt;
Der Wechselrichter sowie der Speicher sind über Lan oder eine sehr stabile(!) Wlan Verbindung erreichbar.&lt;br /&gt;
&lt;br /&gt;
Von Wlan ist abzuraten, weil diese Verbindungen oftmals zu lange Zeitverzögerungen oder auch kurzzeitige Unterbrechungen haben. Es läuft mit Wlan-- aber oft nicht stabil genug. Der Aufbau eines Lan zu weiter entfernten Einheiten ist u.U. aufwändig, lohnt aber immer.&lt;br /&gt;
&lt;br /&gt;
Alle Daten werden über [[DbLog]] gespeichert und die Graphen daraus gewonnen.&lt;br /&gt;
&lt;br /&gt;
Um die Kommunikation z.B. über Modbus zu ermöglichen, reicht es, wenn der Installateur die Modbus-Verbindung freischaltet . Man benötigt keineswegs das Technician-Passwort des WR.&lt;br /&gt;
&lt;br /&gt;
Wenn Fahrzeugladen über eine Wallbox mit eingebunden werden soll, können geeignete Wallboxen das natürlich selbst steuern. Hier ist ein Beispiel für die Integration der Software EVCC die das Laden eines Fahrzeugs komfortabel steuert vorgestellt. Für diese SW (sie kann paralel z.B. mit FHEM auf einem PI laufen) ist für die meisten Wallboxen ein sog. Token erforderlich, der über Github pro Monat für 1$ gekauft werden muss.&lt;br /&gt;
&lt;br /&gt;
=== Grundstruktur ===&lt;br /&gt;
* Der Wechselrichter wird über das Modul [[fronius]] ausgelesen, dabei werden diverse [[userReadings]] erzeugt.&lt;br /&gt;
* Zusätzlich werden über [[HTTPMOD]] weitere Daten ausgelesen, diese sind aber nicht für die Berechnungen notwendig.&lt;br /&gt;
* Der Speicher ist über den Wechselrichter per [[ModbusAttr]] erreichbar sowie auch direkt über das Modul [[BYDBox]].&lt;br /&gt;
* Daraus ergeben sich zusätzliche Darstellungen des Batteriezustands.&lt;br /&gt;
* Bezug, Einspeisung, Erzeugung und Hausverbrauch werden über vier [[ElectricityCalculator|ElectricityCalculator]] Devices erzeugt,&lt;br /&gt;
* diese Module erzeugen auch die gesammten Statistikdaten. (Heute, Gestern, Monat, Vormonat, Jahr)&lt;br /&gt;
* Zwei weitere Dummys plus Berechnungsroutinen erhalten die Daten Eigenverbrauchsquote und Autarkiegrad.&lt;br /&gt;
* Das sehr umfangreiche [[SolarForecast - Solare Prognose (PV Erzeugung) und Verbrauchersteuerung|Modul SolarForecast]] wird integriert und es können viele Graphen dargestellt werden.&lt;br /&gt;
* Eine größere [[readingsGroup]] stellt die Gesamtstatistik der Anlage dar.&lt;br /&gt;
* Das optinonale Programm EVCC kann integriert und auch gesteuert werden.&lt;br /&gt;
* Einige FTUI2 Beispiele runden die Darstellung auf einem Tablet ab.&lt;br /&gt;
&lt;br /&gt;
==Fronius_Symo in FHEM==&lt;br /&gt;
In FHEM wird das Modul fronius angelegt und darin diverse userReadings erzeugt, ebenso werden damit auch die Daten der zusätzlichen WR ausgelesen und addiert. &#039;&#039;&#039;Wichtig&#039;&#039;&#039;: Es ist ein Autolade_Calculator in den userReadings vorhanden. Wenn der Abschnitt mit Autoladen nicht verwendet wird, bitte die Einträge entfernen. Die dienen nur der korrekten Berechnung und Anzeige des Hausverbrauchs (ohne Autoladen!).&lt;br /&gt;
&lt;br /&gt;
Über Integrale werden einige Strommengen als laufend erhöhende Zähler erzeugt. Dies ist für die weitere Verarbeitung in den Modulen ElectricityCalculator (v.Sailor) erforderlich. Die Zähler sind auch im Fronius Datensatz enthalten, sie werden aber in Abständen &amp;gt;= 10-&amp;gt;15 Minuten dort aktualisiert und sind somit für eine Wandanzeige ungeeignet.&lt;br /&gt;
&amp;lt;div class=&amp;quot;mw-collapsible mw-collapsed&amp;quot;&amp;gt;&lt;br /&gt;
Code:&amp;lt;div class=&amp;quot;mw-collapsible-content&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod Fronius_Symo fronius 192.168.xxx.xxx&lt;br /&gt;
attr Fronius_Symo DbLogExclude .*&lt;br /&gt;
attr Fronius_Symo DbLogInclude User_Consumed_E,Einspeisung,Bezug,Akku_Laden,Akku_Entladen,PowerFlow_Site_P_Grid,PowerFlow_Site_P_Load,Meter_1_PowerReal_P_Sum&lt;br /&gt;
attr Fronius_Symo IntervalRealtimeData 60&lt;br /&gt;
attr Fronius_Symo comment Auf dem Dach sind 15.6kWp installiert\&lt;br /&gt;
Str1: 7640W Süd+Carport\&lt;br /&gt;
Str2: 3480W Ost\&lt;br /&gt;
Grow1: 2200W West\&lt;br /&gt;
Grow2: 2320W GH\&lt;br /&gt;
userReadings User_Produced_FPV nur für SolarForecast&lt;br /&gt;
attr Fronius_Symo group Fronius&lt;br /&gt;
attr Fronius_Symo icon inverter&lt;br /&gt;
attr Fronius_Symo room Energie-Strom&lt;br /&gt;
attr Fronius_Symo stateFormat {return &amp;quot;Status: &amp;quot;.ReadingsVal($name,&amp;quot;state&amp;quot;,&amp;quot;&amp;quot;).&amp;quot;&amp;lt;br&amp;gt;\\&lt;br /&gt;
PV-aktuell: &amp;quot;.sprintf(&amp;quot;%.0f&amp;quot;,ReadingsVal($name,&amp;quot;PowerFlow_Site_P_PV&amp;quot;,0)).&amp;quot; W&amp;lt;br&amp;gt;\\&lt;br /&gt;
PV Einspeisung: &amp;quot;.sprintf(&amp;quot;%.0f&amp;quot;,ReadingsVal($name,&amp;quot;Einspeisung&amp;quot;,0)).&amp;quot; W&amp;lt;br&amp;gt;\\&lt;br /&gt;
Netzbezug: &amp;quot;.sprintf(&amp;quot;%.0f&amp;quot;,ReadingsVal($name,&amp;quot;Bezug&amp;quot;,0)).&amp;quot; W&amp;lt;br&amp;gt;\\&lt;br /&gt;
Ladestand Akku: &amp;quot;.sprintf(&amp;quot;%.0f&amp;quot;,ReadingsVal($name,&amp;quot;Storage_0_Controller_StateOfCharge_Relative&amp;quot;,0)).&amp;quot; %&amp;lt;br&amp;gt;\\&lt;br /&gt;
PV gesamt: &amp;quot;.sprintf(&amp;quot;%.2f&amp;quot;,ReadingsVal($name,&amp;quot;PowerFlow_Site_E_Total&amp;quot;,0)/1000).&amp;quot; kWh&amp;quot;;;;;;;;;}&lt;br /&gt;
attr Fronius_Symo userReadings Einspeisung:PowerFlow_Site_P_Grid.* {ReadingsVal($name,&amp;quot;PowerFlow_Site_P_Grid&amp;quot;,&amp;quot;&amp;quot;)&amp;lt;0?ReadingsVal($name,&amp;quot;PowerFlow_Site_P_Grid&amp;quot;,&amp;quot;&amp;quot;)*-1:0},\&lt;br /&gt;
Neg_Einspeisung:PowerFlow_Site_P_Grid.* {ReadingsVal($name,&amp;quot;PowerFlow_Site_P_Grid&amp;quot;,&amp;quot;&amp;quot;)&amp;lt;0?ReadingsVal($name,&amp;quot;PowerFlow_Site_P_Grid&amp;quot;,&amp;quot;&amp;quot;):0},\&lt;br /&gt;
Bezug:PowerFlow_Site_P_Grid.* {ReadingsVal($name,&amp;quot;PowerFlow_Site_P_Grid&amp;quot;,&amp;quot;&amp;quot;)&amp;gt;0?ReadingsVal($name,&amp;quot;PowerFlow_Site_P_Grid&amp;quot;,&amp;quot;&amp;quot;):0},\&lt;br /&gt;
Akku_Laden:PowerFlow_Site_P_Akku.* {ReadingsVal($name,&amp;quot;PowerFlow_Site_P_Akku&amp;quot;,&amp;quot;&amp;quot;)&amp;lt;0?ReadingsVal($name,&amp;quot;PowerFlow_Site_P_Akku&amp;quot;,&amp;quot;&amp;quot;)*-1:0},\&lt;br /&gt;
Akku_Entladen:PowerFlow_Site_P_Akku.* {ReadingsVal($name,&amp;quot;PowerFlow_Site_P_Akku&amp;quot;,&amp;quot;&amp;quot;)&amp;gt;0?ReadingsVal($name,&amp;quot;PowerFlow_Site_P_Akku&amp;quot;,&amp;quot;&amp;quot;):0},\&lt;br /&gt;
User_Produced_PV:PowerFlow_Site_P_PV.* integral {((ReadingsVal(&amp;quot;$name&amp;quot;,&amp;quot;PowerFlow_Site_P_PV&amp;quot;,&amp;quot;0&amp;quot;))+(ReadingsVal($name,&amp;quot;Meter_1_PowerReal_P_Sum&amp;quot;,&amp;quot;0&amp;quot;)))/3600000},\&lt;br /&gt;
User_Produced_FPV:PowerFlow_Site_P_PV.* integral {(ReadingsVal(&amp;quot;$name&amp;quot;,&amp;quot;PowerFlow_Site_P_PV&amp;quot;,&amp;quot;0&amp;quot;))/3600000},\&lt;br /&gt;
User_Consumed_EN:PowerFlow_Site_P_PV.* {ReadingsVal($name,&amp;quot;PowerFlow_Site_P_PV&amp;quot;,&amp;quot;&amp;quot;)+ReadingsVal($name,&amp;quot;Meter_1_PowerReal_P_Sum&amp;quot;,&amp;quot;&amp;quot;)-(ReadingsVal(&amp;quot;MQTT2_evcc1&amp;quot;,&amp;quot;Ladeleistung&amp;quot;,&amp;quot;&amp;quot;)*1000)+ReadingsVal($name,&amp;quot;PowerFlow_Site_P_Grid&amp;quot;,&amp;quot;&amp;quot;)+ReadingsVal($name,&amp;quot;PowerFlow_Site_P_Akku&amp;quot;,&amp;quot;&amp;quot;)},\&lt;br /&gt;
User_Consumed_E:PowerFlow_Site_P_PV.* {ReadingsVal($name,&amp;quot;User_Consumed_EN&amp;quot;,&amp;quot;&amp;quot;)&amp;gt;0?ReadingsVal($name,&amp;quot;User_Consumed_EN&amp;quot;,&amp;quot;&amp;quot;):0},\&lt;br /&gt;
User_Consumed_Energie_C:User_Consumed_E.* integral {ReadingsVal(&amp;quot;$name&amp;quot;,&amp;quot;User_Consumed_E&amp;quot;,&amp;quot;0&amp;quot;)/3600000},&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==FroniusPowerFlow==&lt;br /&gt;
Über HTTPMOD können zusätzlich aus dem Fronius &#039;&#039;&#039;Device String Werte&#039;&#039;&#039; ausgelesen (für FTUI Anzeigen z.B.)&lt;br /&gt;
&amp;lt;div class=&amp;quot;mw-collapsible mw-collapsed&amp;quot;&amp;gt;&lt;br /&gt;
Code:&amp;lt;div class=&amp;quot;mw-collapsible-content&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
defmod FroniusPowerFlow HTTPMOD http://192.168.xxx.xxx/solar_api/v1/GetPowerFlowRealtimeData.fcgi 10&lt;br /&gt;
attr FroniusPowerFlow userattr reading1Name reading1JSON&lt;br /&gt;
attr FroniusPowerFlow DbLogExclude .*&lt;br /&gt;
attr FroniusPowerFlow extractAllJSON 1&lt;br /&gt;
attr FroniusPowerFlow group Fronius&lt;br /&gt;
attr FroniusPowerFlow icon inverter&lt;br /&gt;
attr FroniusPowerFlow room Energie-Strom&lt;br /&gt;
attr FroniusPowerFlow stateFormat Leistung: PV_Produktion W&lt;br /&gt;
attr FroniusPowerFlow userReadings Neg_Einspeisung:Body_Data_Site_P_Grid.* {ReadingsVal($name,&amp;quot;Body_Data_Site_P_Grid&amp;quot;,&amp;quot;&amp;quot;)&amp;lt;0?ReadingsVal($name,&amp;quot;Body_Data_Site_P_Grid&amp;quot;,&amp;quot;&amp;quot;):0},\&lt;br /&gt;
Bezug:Body_Data_Site_P_Grid.* {ReadingsVal($name,&amp;quot;Body_Data_Site_P_Grid&amp;quot;,&amp;quot;&amp;quot;)&amp;gt;0?ReadingsVal($name,&amp;quot;Body_Data_Site_P_Grid&amp;quot;,&amp;quot;&amp;quot;):0},\&lt;br /&gt;
Akku_Laden:Body_Data_Site_P_Akku.* {ReadingsVal($name,&amp;quot;Body_Data_Site_P_Akku&amp;quot;,&amp;quot;&amp;quot;)&amp;lt;0?ReadingsVal($name,&amp;quot;Body_Data_Site_P_Akku&amp;quot;,&amp;quot;&amp;quot;)*-1:0},\&lt;br /&gt;
Akku_Entladen:Body_Data_Site_P_Akku.* {ReadingsVal($name,&amp;quot;Body_Data_Site_P_Akku&amp;quot;,&amp;quot;&amp;quot;)&amp;gt;0?ReadingsVal($name,&amp;quot;Body_Data_Site_P_Akku&amp;quot;,&amp;quot;&amp;quot;):0},\&lt;br /&gt;
PV_Produktion:Body_Data_Site_P_Grid.* {((ReadingsVal(&amp;quot;$name&amp;quot;,&amp;quot;Body_Data_Site_P_PV&amp;quot;,&amp;quot;0&amp;quot;))+(ReadingsVal(&amp;quot;Fronius_Symo&amp;quot;,&amp;quot;Meter_1_PowerReal_P_Sum&amp;quot;,&amp;quot;0&amp;quot;)))}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== PV_Batterie ==&lt;br /&gt;
Per [[ModbusAttr]] werden Daten der Batterie aus dem Fronius gelesen und es können auch Einstellungen daran vorgenommen werden&lt;br /&gt;
&lt;br /&gt;
min SOC Winter/Sommer oder auch das Verhindern einer Sofortentladung bei Anschluss eines Fahrzeugs zur Aufladung....&lt;br /&gt;
&amp;lt;div class=&amp;quot;mw-collapsible mw-collapsed&amp;quot;&amp;gt;&lt;br /&gt;
Code:&amp;lt;div class=&amp;quot;mw-collapsible-content&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
defmod PV_Batterie ModbusAttr 1 10 192.168.xxx.xxx:502 TCP&lt;br /&gt;
attr PV_Batterie DbLogExclude .*&lt;br /&gt;
attr PV_Batterie DbLogInclude DCPowerMPPT1,DCPowerMPPT2&lt;br /&gt;
attr PV_Batterie dev-h-combine 125&lt;br /&gt;
attr PV_Batterie dev-h-defFormat %.1f&lt;br /&gt;
attr PV_Batterie dev-h-defLen 2&lt;br /&gt;
attr PV_Batterie dev-h-defPoll 1&lt;br /&gt;
attr PV_Batterie dev-h-defUnpack f&amp;gt;&lt;br /&gt;
attr PV_Batterie devStateStyle style=&amp;quot;text-align:right&amp;quot;&lt;br /&gt;
attr PV_Batterie disable 0&lt;br /&gt;
attr PV_Batterie event-min-interval ACActEnergy:7200,ACPower:7200,Battery.*:7200&lt;br /&gt;
attr PV_Batterie event-on-change-reading .*Energy:0.1,ACPower:1,DCPowerMPPT.*:1,status,Battery.*charge.*:1,BatteryState&lt;br /&gt;
attr PV_Batterie group Fronius&lt;br /&gt;
attr PV_Batterie icon measure_battery_100&lt;br /&gt;
attr PV_Batterie obj-h40073-reading ACCurrentPhaseA&lt;br /&gt;
attr PV_Batterie obj-h40075-reading ACCurrentPhaseB&lt;br /&gt;
attr PV_Batterie obj-h40077-reading ACCurrentPhaseC&lt;br /&gt;
attr PV_Batterie obj-h40085-reading ACVoltagePhaseA&lt;br /&gt;
attr PV_Batterie obj-h40087-reading ACVoltagePhaseB&lt;br /&gt;
attr PV_Batterie obj-h40089-reading ACVoltagePhaseC&lt;br /&gt;
attr PV_Batterie obj-h40091-format %.0f&lt;br /&gt;
attr PV_Batterie obj-h40091-reading ACPower&lt;br /&gt;
attr PV_Batterie obj-h40093-reading ACFrequency&lt;br /&gt;
attr PV_Batterie obj-h40109-reading CabinetTemperature&lt;br /&gt;
attr PV_Batterie obj-h40117-format %s&lt;br /&gt;
attr PV_Batterie obj-h40117-len 1&lt;br /&gt;
attr PV_Batterie obj-h40117-map 1:off,2:sleeping,3:starting,4:active,5:throttled,6:shutdown,7:fault,8:standby&lt;br /&gt;
attr PV_Batterie obj-h40117-reading status&lt;br /&gt;
attr PV_Batterie obj-h40117-unpack n&lt;br /&gt;
attr PV_Batterie obj-h40196-expr $val / 1000&lt;br /&gt;
attr PV_Batterie obj-h40196-format %.2f&lt;br /&gt;
attr PV_Batterie obj-h40196-len 4&lt;br /&gt;
attr PV_Batterie obj-h40196-reading ACActEnergy&lt;br /&gt;
attr PV_Batterie obj-h40196-unpack Q&amp;gt;&lt;br /&gt;
attr PV_Batterie obj-h40267-format %d&lt;br /&gt;
attr PV_Batterie obj-h40267-group 1-1&lt;br /&gt;
attr PV_Batterie obj-h40267-len 1&lt;br /&gt;
attr PV_Batterie obj-h40267-reading DCPowerScale&lt;br /&gt;
attr PV_Batterie obj-h40267-unpack s&amp;gt;&lt;br /&gt;
attr PV_Batterie obj-h40284-expr $val * 10 ** ReadingsVal($name, &#039;DCPowerScale&#039;, 1)&lt;br /&gt;
attr PV_Batterie obj-h40284-group 1-2&lt;br /&gt;
attr PV_Batterie obj-h40284-len 1&lt;br /&gt;
attr PV_Batterie obj-h40284-reading DCPowerMPPT1&lt;br /&gt;
attr PV_Batterie obj-h40284-unpack n&lt;br /&gt;
attr PV_Batterie obj-h40304-expr $val * 10 ** ReadingsVal($name, &#039;DCPowerScale&#039;, 1)&lt;br /&gt;
attr PV_Batterie obj-h40304-group 1-3&lt;br /&gt;
attr PV_Batterie obj-h40304-len 1&lt;br /&gt;
attr PV_Batterie obj-h40304-reading DCPowerMPPT2&lt;br /&gt;
attr PV_Batterie obj-h40304-unpack n&lt;br /&gt;
attr PV_Batterie obj-h40324-expr $val * 10 ** ReadingsVal($name, &#039;DCPowerScale&#039;, 1)&lt;br /&gt;
attr PV_Batterie obj-h40324-group 1-4&lt;br /&gt;
attr PV_Batterie obj-h40324-len 1&lt;br /&gt;
attr PV_Batterie obj-h40324-reading BatteryChargeWatt&lt;br /&gt;
attr PV_Batterie obj-h40324-unpack n&lt;br /&gt;
attr PV_Batterie obj-h40325-expr $val/1000000&lt;br /&gt;
attr PV_Batterie obj-h40325-ignoreExpr $val &amp;lt; 100&lt;br /&gt;
attr PV_Batterie obj-h40325-len 2&lt;br /&gt;
attr PV_Batterie obj-h40325-poll 300&lt;br /&gt;
attr PV_Batterie obj-h40325-reading Summe_Ladung&lt;br /&gt;
attr PV_Batterie obj-h40325-unpack N&lt;br /&gt;
attr PV_Batterie obj-h40344-expr $val * 10 ** ReadingsVal($name, &#039;DCPowerScale&#039;, 1)&lt;br /&gt;
attr PV_Batterie obj-h40344-group 1-5&lt;br /&gt;
attr PV_Batterie obj-h40344-len 1&lt;br /&gt;
attr PV_Batterie obj-h40344-reading BatteryDischargeWatt&lt;br /&gt;
attr PV_Batterie obj-h40344-unpack n&lt;br /&gt;
attr PV_Batterie obj-h40345-expr $val/1000000&lt;br /&gt;
attr PV_Batterie obj-h40345-ignoreExpr $val &amp;lt; 100&lt;br /&gt;
attr PV_Batterie obj-h40345-len 2&lt;br /&gt;
attr PV_Batterie obj-h40345-poll 300&lt;br /&gt;
attr PV_Batterie obj-h40345-reading Summe_Entladung&lt;br /&gt;
attr PV_Batterie obj-h40345-unpack N&lt;br /&gt;
attr PV_Batterie obj-h40355-len 1&lt;br /&gt;
attr PV_Batterie obj-h40355-reading BatConfigMaxReferenceWatt&lt;br /&gt;
attr PV_Batterie obj-h40355-unpack n&lt;br /&gt;
attr PV_Batterie obj-h40358-format %s&lt;br /&gt;
attr PV_Batterie obj-h40358-len 1&lt;br /&gt;
attr PV_Batterie obj-h40358-map 0:none,1:chargeMax,2:dischrMax,3:bothMax&lt;br /&gt;
attr PV_Batterie obj-h40358-reading BatConfigMaxEnabled&lt;br /&gt;
attr PV_Batterie obj-h40358-set 1&lt;br /&gt;
attr PV_Batterie obj-h40358-unpack n&lt;br /&gt;
attr PV_Batterie obj-h40360-expr $val / 100&lt;br /&gt;
attr PV_Batterie obj-h40360-format %.0f&lt;br /&gt;
attr PV_Batterie obj-h40360-len 1&lt;br /&gt;
attr PV_Batterie obj-h40360-poll 60&lt;br /&gt;
attr PV_Batterie obj-h40360-reading BatConfigReserve&lt;br /&gt;
attr PV_Batterie obj-h40360-set 1&lt;br /&gt;
attr PV_Batterie obj-h40360-setexpr $val * 100&lt;br /&gt;
attr PV_Batterie obj-h40360-unpack n&lt;br /&gt;
attr PV_Batterie obj-h40361-expr $val / 100&lt;br /&gt;
attr PV_Batterie obj-h40361-len 1&lt;br /&gt;
attr PV_Batterie obj-h40361-reading BatteryChargePercent&lt;br /&gt;
attr PV_Batterie obj-h40361-unpack n&lt;br /&gt;
attr PV_Batterie obj-h40364-format %s&lt;br /&gt;
attr PV_Batterie obj-h40364-len 1&lt;br /&gt;
attr PV_Batterie obj-h40364-map 1:off,2:empty,3:discharging,4:charging,5:full,6:holding,7:testing&lt;br /&gt;
attr PV_Batterie obj-h40364-reading BatteryState&lt;br /&gt;
attr PV_Batterie obj-h40364-unpack n&lt;br /&gt;
attr PV_Batterie obj-h40365-expr $val / 10000 * ReadingsVal($name, &#039;BatConfigMaxReferenceWatt&#039;, 1)&lt;br /&gt;
attr PV_Batterie obj-h40365-len 1&lt;br /&gt;
attr PV_Batterie obj-h40365-max ReadingsVal($name, &#039;BatConfigMaxReferenceWatt&#039;, 1)&lt;br /&gt;
attr PV_Batterie obj-h40365-min -ReadingsVal($name, &#039;BatConfigMaxReferenceWatt&#039;, 1)&lt;br /&gt;
attr PV_Batterie obj-h40365-reading BatConfigMaxDischargeWatt&lt;br /&gt;
attr PV_Batterie obj-h40365-set 1&lt;br /&gt;
attr PV_Batterie obj-h40365-setexpr $val / ReadingsVal($name, &#039;BatConfigMaxReferenceWatt&#039;, 1) * 10000&lt;br /&gt;
attr PV_Batterie obj-h40365-unpack s&amp;gt;&lt;br /&gt;
attr PV_Batterie obj-h40366-expr $val / 10000 * ReadingsVal($name, &#039;BatConfigMaxReferenceWatt&#039;, 1)&lt;br /&gt;
attr PV_Batterie obj-h40366-len 1&lt;br /&gt;
attr PV_Batterie obj-h40366-max ReadingsVal($name, &#039;BatConfigMaxReferenceWatt&#039;, 1)&lt;br /&gt;
attr PV_Batterie obj-h40366-min -ReadingsVal($name, &#039;BatConfigMaxReferenceWatt&#039;, 1)&lt;br /&gt;
attr PV_Batterie obj-h40366-reading BatConfigMaxChargeWatt&lt;br /&gt;
attr PV_Batterie obj-h40366-set 1&lt;br /&gt;
attr PV_Batterie obj-h40366-setexpr $val / ReadingsVal($name, &#039;BatConfigMaxReferenceWatt&#039;, 1) * 10000&lt;br /&gt;
attr PV_Batterie obj-h40366-unpack s&amp;gt;&lt;br /&gt;
attr PV_Batterie room Energie-Strom&lt;br /&gt;
attr PV_Batterie stateFormat Status: BatteryState &amp;lt;br/&amp;gt;\&lt;br /&gt;
Ladung: BatteryChargePercent % | Reserve: BatConfigReserve %&amp;lt;br/&amp;gt;\&lt;br /&gt;
Summe Entladung: Summe_Entladung kWh | Summe Ladung: Summe_Ladung kWh&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==myBYDBox==&lt;br /&gt;
Das Modul BYDBox ermöglicht es, direkt aus der Batterie einzelne Zelldaten auszulesen. Das geht natürlich nur, wenn der Speicher direkt über LAN am Netzwerk angebunden ist. Um ein den Kontakt mit dem Hersteller in China zu unterbinden, kann diese Verbindung Problemlos im Hausnetz z.B. mittels einer Kindersicherung versehen werden. Die Kombi Fronius -&amp;gt; BYD-Box funktioniert damit einwandfrei weil deren nötige Kommunikation mit dem Wechselrichter über Modbus-Kabel erfolgt.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Achtung&#039;&#039;&#039;: IP der BYD-Box nehmen, nicht die vom Fronius!&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;define myBYDBox BYDBox 192.168.xxx.xxx 60&lt;br /&gt;
attr myBYDBox DbLogExclude .*&lt;br /&gt;
attr myBYDBox detail-level 1&lt;br /&gt;
attr myBYDBox disable 0&lt;br /&gt;
attr myBYDBox room Energie-Strom&lt;br /&gt;
attr myBYDBox verbose 0&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== BYD.Box_view ==&lt;br /&gt;
[[Datei:Screenshot 2024-11-14 164959.jpg|mini|BYD.Box_view]]Runde Darstellung der BYD Speicherwerte&lt;br /&gt;
&lt;br /&gt;
Darstellung der Batterie über ein DOIF aus myBYDBox die ui_Table dazu genutzt&amp;lt;div class=&amp;quot;mw-collapsible mw-collapsed&amp;quot;&amp;gt;&lt;br /&gt;
Code:&amp;lt;div class=&amp;quot;mw-collapsible-content&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
define BYD.Box_viev DOIF ##&lt;br /&gt;
attr BYD.Box_viev alias BYD.Box_viev&lt;br /&gt;
attr BYD.Box_viev room Energie-Strom&lt;br /&gt;
attr BYD.Box_viev uiTable {package ui_Table;;}\&lt;br /&gt;
&amp;quot;BYDB-Box&amp;quot;| ring2([myBYDBox:BatteryPower],-6000,6000,120,0,&amp;quot;W&amp;quot;,200,[(0,120,500,90,6000,60)],&amp;quot;1,font-weight:normal&amp;quot;,[myBYDBox:Battery_1_SOC],0,100,0,120,&amp;quot;%&amp;quot;,undef,&amp;quot;1,font-weight:normal&amp;quot;,undef,undef,&amp;quot;0,,1,0,1&amp;quot;) |\&lt;br /&gt;
ring2([myBYDBox:BatteryCurrent],-20,20,120,0,&amp;quot;A&amp;quot;,200,undef,&amp;quot;1,font-weight:normal&amp;quot;,[myBYDBox:BatteryOutVoltage],300,400,0,120,&amp;quot;V&amp;quot;,undef,&amp;quot;1,font-weight:normal&amp;quot;,undef,undef,&amp;quot;0,,,0,1&amp;quot;) |\&lt;br /&gt;
ring2([myBYDBox:Battery_1_MaxmVolt],2800,3500,120,0,&amp;quot;mV&amp;quot;,200,undef,&amp;quot;1,font-weight:normal&amp;quot;,[myBYDBox:Battery_1_MinmVolt],2800,3500,0,120,&amp;quot;mV&amp;quot;,undef,&amp;quot;1,font-weight:normal&amp;quot;) |\&lt;br /&gt;
ring2([myBYDBox:BatteryMaxTemp],10,30,120,0,&amp;quot;°C&amp;quot;,200,undef,&amp;quot;1,font-weight:normal&amp;quot;,[myBYDBox:BatteryMinTemp],10,30,0,120,&amp;quot;°C&amp;quot;,undef,&amp;quot;1,font-weight:normal&amp;quot;)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== BYD_Cells ==&lt;br /&gt;
[[Datei:Screenshot 2024-11-14 165025.jpg|mini|BYD_Cells nach dem Laden]]&lt;br /&gt;
[[Datei:BYD-Cels.jpg|mini|Balancing nahezu abgeschlossen]]&lt;br /&gt;
Ladezustand der einzelnen Zellen einer BYD-Box über ein DOIF aus myBYDBox. Es sind vier oder fünf Zellengruppen definiert, ggf anpassen. Weiterhin ist das Cell Balancing des BMS zu erkennen. Erst einige Zeit nach Ende des Ladevorgangs ist die Energie nahezu gleich auf alle Zellen verteilt.&amp;lt;div class=&amp;quot;mw-collapsible mw-collapsed&amp;quot;&amp;gt;&lt;br /&gt;
Code:&amp;lt;div class=&amp;quot;mw-collapsible-content&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
define BYD_Cells DOIF ##&lt;br /&gt;
attr BYD_Cells alias BYD_Cells&lt;br /&gt;
attr BYD_Cells room Energie-Strom&lt;br /&gt;
attr BYD_Cells uiTable {package ui_Table;;\&lt;br /&gt;
\&lt;br /&gt;
sub floor_round {\&lt;br /&gt;
my ($zahl)=@_;;\&lt;br /&gt;
return(POSIX::floor($zahl / 10) * 10) - 10;;\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
sub ceil_round {\&lt;br /&gt;
my ($zahl)=@_;;\&lt;br /&gt;
return(POSIX::ceil($zahl / 10) * 10) + 10;;\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
sub colorBYD {\&lt;br /&gt;
my ($zahl)=@_;;\&lt;br /&gt;
my $min = 2800;;\&lt;br /&gt;
my $max = 3550;;\&lt;br /&gt;
my $mid = 3000;;\&lt;br /&gt;
my $mid2 = 3400;;\&lt;br /&gt;
my $color_green = 120;;\&lt;br /&gt;
\&lt;br /&gt;
my $num = 0;;\&lt;br /&gt;
\&lt;br /&gt;
if($zahl &amp;gt;= $mid2 &amp;amp;&amp;amp; $zahl &amp;lt; $max)\&lt;br /&gt;
{\&lt;br /&gt;
	$num = $color_green - (($zahl-$mid2)/($max-$mid2) * $color_green);;\&lt;br /&gt;
}\&lt;br /&gt;
elsif($zahl &amp;lt; $mid)\&lt;br /&gt;
{\&lt;br /&gt;
	$num = (($zahl-$min)/($mid-$min) * $color_green);;\&lt;br /&gt;
}\&lt;br /&gt;
elsif($zahl &amp;gt;= $mid)\&lt;br /&gt;
{\&lt;br /&gt;
    $num = $color_green;;\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
return(POSIX::ceil($num));;\&lt;br /&gt;
}\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
cylinder_bars(&amp;quot;BYD Modul 1&amp;quot;,floor_round([myBYDBox:Battery_1_MinmVolt]),ceil_round([myBYDBox:Battery_1_MaxmVolt]),&amp;quot;mV&amp;quot;,250,undef,undef,0,\&lt;br /&gt;
[myBYDBox:Battery_1_VoltsperCell_000],colorBYD([myBYDBox:Battery_1_VoltsperCell_000]),&amp;quot;0&amp;quot;,\&lt;br /&gt;
[myBYDBox:Battery_1_VoltsperCell_001],colorBYD([myBYDBox:Battery_1_VoltsperCell_001]),&amp;quot;1&amp;quot;,\&lt;br /&gt;
[myBYDBox:Battery_1_VoltsperCell_002],colorBYD([myBYDBox:Battery_1_VoltsperCell_002]),&amp;quot;2&amp;quot;,\&lt;br /&gt;
[myBYDBox:Battery_1_VoltsperCell_003],colorBYD([myBYDBox:Battery_1_VoltsperCell_003]),&amp;quot;3&amp;quot;,\&lt;br /&gt;
[myBYDBox:Battery_1_VoltsperCell_004],colorBYD([myBYDBox:Battery_1_VoltsperCell_004]),&amp;quot;4&amp;quot;,\&lt;br /&gt;
[myBYDBox:Battery_1_VoltsperCell_005],colorBYD([myBYDBox:Battery_1_VoltsperCell_005]),&amp;quot;5&amp;quot;,\&lt;br /&gt;
[myBYDBox:Battery_1_VoltsperCell_006],colorBYD([myBYDBox:Battery_1_VoltsperCell_006]),&amp;quot;6&amp;quot;,\&lt;br /&gt;
[myBYDBox:Battery_1_VoltsperCell_007],colorBYD([myBYDBox:Battery_1_VoltsperCell_007]),&amp;quot;7&amp;quot;,\&lt;br /&gt;
[myBYDBox:Battery_1_VoltsperCell_008],colorBYD([myBYDBox:Battery_1_VoltsperCell_008]),&amp;quot;8&amp;quot;,\&lt;br /&gt;
[myBYDBox:Battery_1_VoltsperCell_009],colorBYD([myBYDBox:Battery_1_VoltsperCell_009]),&amp;quot;9&amp;quot;,\&lt;br /&gt;
[myBYDBox:Battery_1_VoltsperCell_010],colorBYD([myBYDBox:Battery_1_VoltsperCell_010]),&amp;quot;10&amp;quot;,\&lt;br /&gt;
[myBYDBox:Battery_1_VoltsperCell_011],colorBYD([myBYDBox:Battery_1_VoltsperCell_011]),&amp;quot;11&amp;quot;,\&lt;br /&gt;
[myBYDBox:Battery_1_VoltsperCell_012],colorBYD([myBYDBox:Battery_1_VoltsperCell_012]),&amp;quot;12&amp;quot;,\&lt;br /&gt;
[myBYDBox:Battery_1_VoltsperCell_013],colorBYD([myBYDBox:Battery_1_VoltsperCell_013]),&amp;quot;13&amp;quot;,\&lt;br /&gt;
[myBYDBox:Battery_1_VoltsperCell_014],colorBYD([myBYDBox:Battery_1_VoltsperCell_014]),&amp;quot;14&amp;quot;,\&lt;br /&gt;
[myBYDBox:Battery_1_VoltsperCell_015],colorBYD([myBYDBox:Battery_1_VoltsperCell_015]),&amp;quot;15&amp;quot;) |\&lt;br /&gt;
\&lt;br /&gt;
cylinder_bars(&amp;quot;BYD Modul 2&amp;quot;,floor_round([myBYDBox:Battery_1_MinmVolt]),ceil_round([myBYDBox:Battery_1_MaxmVolt]),&amp;quot;mV&amp;quot;,250,undef,undef,0,\&lt;br /&gt;
[myBYDBox:Battery_1_VoltsperCell_016],colorBYD([myBYDBox:Battery_1_VoltsperCell_016]),&amp;quot;0&amp;quot;,\&lt;br /&gt;
[myBYDBox:Battery_1_VoltsperCell_017],colorBYD([myBYDBox:Battery_1_VoltsperCell_017]),&amp;quot;1&amp;quot;,\&lt;br /&gt;
[myBYDBox:Battery_1_VoltsperCell_018],colorBYD([myBYDBox:Battery_1_VoltsperCell_018]),&amp;quot;2&amp;quot;,\&lt;br /&gt;
[myBYDBox:Battery_1_VoltsperCell_019],colorBYD([myBYDBox:Battery_1_VoltsperCell_019]),&amp;quot;3&amp;quot;,\&lt;br /&gt;
[myBYDBox:Battery_1_VoltsperCell_020],colorBYD([myBYDBox:Battery_1_VoltsperCell_020]),&amp;quot;4&amp;quot;,\&lt;br /&gt;
[myBYDBox:Battery_1_VoltsperCell_021],colorBYD([myBYDBox:Battery_1_VoltsperCell_021]),&amp;quot;5&amp;quot;,\&lt;br /&gt;
[myBYDBox:Battery_1_VoltsperCell_022],colorBYD([myBYDBox:Battery_1_VoltsperCell_022]),&amp;quot;6&amp;quot;,\&lt;br /&gt;
[myBYDBox:Battery_1_VoltsperCell_023],colorBYD([myBYDBox:Battery_1_VoltsperCell_023]),&amp;quot;7&amp;quot;,\&lt;br /&gt;
[myBYDBox:Battery_1_VoltsperCell_024],colorBYD([myBYDBox:Battery_1_VoltsperCell_024]),&amp;quot;8&amp;quot;,\&lt;br /&gt;
[myBYDBox:Battery_1_VoltsperCell_025],colorBYD([myBYDBox:Battery_1_VoltsperCell_025]),&amp;quot;9&amp;quot;,\&lt;br /&gt;
[myBYDBox:Battery_1_VoltsperCell_026],colorBYD([myBYDBox:Battery_1_VoltsperCell_026]),&amp;quot;10&amp;quot;,\&lt;br /&gt;
[myBYDBox:Battery_1_VoltsperCell_027],colorBYD([myBYDBox:Battery_1_VoltsperCell_027]),&amp;quot;11&amp;quot;,\&lt;br /&gt;
[myBYDBox:Battery_1_VoltsperCell_028],colorBYD([myBYDBox:Battery_1_VoltsperCell_028]),&amp;quot;12&amp;quot;,\&lt;br /&gt;
[myBYDBox:Battery_1_VoltsperCell_029],colorBYD([myBYDBox:Battery_1_VoltsperCell_029]),&amp;quot;13&amp;quot;,\&lt;br /&gt;
[myBYDBox:Battery_1_VoltsperCell_030],colorBYD([myBYDBox:Battery_1_VoltsperCell_030]),&amp;quot;14&amp;quot;,\&lt;br /&gt;
[myBYDBox:Battery_1_VoltsperCell_031],colorBYD([myBYDBox:Battery_1_VoltsperCell_031]),&amp;quot;15&amp;quot;) |\&lt;br /&gt;
\&lt;br /&gt;
cylinder_bars(&amp;quot;BYD Modul 3&amp;quot;,floor_round([myBYDBox:Battery_1_MinmVolt]),ceil_round([myBYDBox:Battery_1_MaxmVolt]),&amp;quot;mV&amp;quot;,250,undef,undef,0,\&lt;br /&gt;
[myBYDBox:Battery_1_VoltsperCell_032],colorBYD([myBYDBox:Battery_1_VoltsperCell_032]),&amp;quot;0&amp;quot;,\&lt;br /&gt;
[myBYDBox:Battery_1_VoltsperCell_033],colorBYD([myBYDBox:Battery_1_VoltsperCell_033]),&amp;quot;1&amp;quot;,\&lt;br /&gt;
[myBYDBox:Battery_1_VoltsperCell_034],colorBYD([myBYDBox:Battery_1_VoltsperCell_034]),&amp;quot;2&amp;quot;,\&lt;br /&gt;
[myBYDBox:Battery_1_VoltsperCell_035],colorBYD([myBYDBox:Battery_1_VoltsperCell_035]),&amp;quot;3&amp;quot;,\&lt;br /&gt;
[myBYDBox:Battery_1_VoltsperCell_036],colorBYD([myBYDBox:Battery_1_VoltsperCell_036]),&amp;quot;4&amp;quot;,\&lt;br /&gt;
[myBYDBox:Battery_1_VoltsperCell_037],colorBYD([myBYDBox:Battery_1_VoltsperCell_037]),&amp;quot;5&amp;quot;,\&lt;br /&gt;
[myBYDBox:Battery_1_VoltsperCell_038],colorBYD([myBYDBox:Battery_1_VoltsperCell_038]),&amp;quot;6&amp;quot;,\&lt;br /&gt;
[myBYDBox:Battery_1_VoltsperCell_039],colorBYD([myBYDBox:Battery_1_VoltsperCell_039]),&amp;quot;7&amp;quot;,\&lt;br /&gt;
[myBYDBox:Battery_1_VoltsperCell_040],colorBYD([myBYDBox:Battery_1_VoltsperCell_040]),&amp;quot;8&amp;quot;,\&lt;br /&gt;
[myBYDBox:Battery_1_VoltsperCell_041],colorBYD([myBYDBox:Battery_1_VoltsperCell_041]),&amp;quot;9&amp;quot;,\&lt;br /&gt;
[myBYDBox:Battery_1_VoltsperCell_042],colorBYD([myBYDBox:Battery_1_VoltsperCell_042]),&amp;quot;10&amp;quot;,\&lt;br /&gt;
[myBYDBox:Battery_1_VoltsperCell_043],colorBYD([myBYDBox:Battery_1_VoltsperCell_043]),&amp;quot;11&amp;quot;,\&lt;br /&gt;
[myBYDBox:Battery_1_VoltsperCell_044],colorBYD([myBYDBox:Battery_1_VoltsperCell_044]),&amp;quot;12&amp;quot;,\&lt;br /&gt;
[myBYDBox:Battery_1_VoltsperCell_045],colorBYD([myBYDBox:Battery_1_VoltsperCell_045]),&amp;quot;13&amp;quot;,\&lt;br /&gt;
[myBYDBox:Battery_1_VoltsperCell_046],colorBYD([myBYDBox:Battery_1_VoltsperCell_046]),&amp;quot;14&amp;quot;,\&lt;br /&gt;
[myBYDBox:Battery_1_VoltsperCell_047],colorBYD([myBYDBox:Battery_1_VoltsperCell_047]),&amp;quot;15&amp;quot;) |\&lt;br /&gt;
\&lt;br /&gt;
cylinder_bars(&amp;quot;BYD Modul 4&amp;quot;,floor_round([myBYDBox:Battery_1_MinmVolt]),ceil_round([myBYDBox:Battery_1_MaxmVolt]),&amp;quot;mV&amp;quot;,250,undef,undef,0,\&lt;br /&gt;
[myBYDBox:Battery_1_VoltsperCell_048],colorBYD([myBYDBox:Battery_1_VoltsperCell_048]),&amp;quot;0&amp;quot;,\&lt;br /&gt;
[myBYDBox:Battery_1_VoltsperCell_049],colorBYD([myBYDBox:Battery_1_VoltsperCell_049]),&amp;quot;1&amp;quot;,\&lt;br /&gt;
[myBYDBox:Battery_1_VoltsperCell_050],colorBYD([myBYDBox:Battery_1_VoltsperCell_050]),&amp;quot;2&amp;quot;,\&lt;br /&gt;
[myBYDBox:Battery_1_VoltsperCell_051],colorBYD([myBYDBox:Battery_1_VoltsperCell_051]),&amp;quot;3&amp;quot;,\&lt;br /&gt;
[myBYDBox:Battery_1_VoltsperCell_052],colorBYD([myBYDBox:Battery_1_VoltsperCell_052]),&amp;quot;4&amp;quot;,\&lt;br /&gt;
[myBYDBox:Battery_1_VoltsperCell_053],colorBYD([myBYDBox:Battery_1_VoltsperCell_053]),&amp;quot;5&amp;quot;,\&lt;br /&gt;
[myBYDBox:Battery_1_VoltsperCell_054],colorBYD([myBYDBox:Battery_1_VoltsperCell_054]),&amp;quot;6&amp;quot;,\&lt;br /&gt;
[myBYDBox:Battery_1_VoltsperCell_055],colorBYD([myBYDBox:Battery_1_VoltsperCell_055]),&amp;quot;7&amp;quot;,\&lt;br /&gt;
[myBYDBox:Battery_1_VoltsperCell_056],colorBYD([myBYDBox:Battery_1_VoltsperCell_056]),&amp;quot;8&amp;quot;,\&lt;br /&gt;
[myBYDBox:Battery_1_VoltsperCell_057],colorBYD([myBYDBox:Battery_1_VoltsperCell_057]),&amp;quot;9&amp;quot;,\&lt;br /&gt;
[myBYDBox:Battery_1_VoltsperCell_058],colorBYD([myBYDBox:Battery_1_VoltsperCell_058]),&amp;quot;10&amp;quot;,\&lt;br /&gt;
[myBYDBox:Battery_1_VoltsperCell_059],colorBYD([myBYDBox:Battery_1_VoltsperCell_059]),&amp;quot;11&amp;quot;,\&lt;br /&gt;
[myBYDBox:Battery_1_VoltsperCell_060],colorBYD([myBYDBox:Battery_1_VoltsperCell_060]),&amp;quot;12&amp;quot;,\&lt;br /&gt;
[myBYDBox:Battery_1_VoltsperCell_061],colorBYD([myBYDBox:Battery_1_VoltsperCell_061]),&amp;quot;13&amp;quot;,\&lt;br /&gt;
[myBYDBox:Battery_1_VoltsperCell_062],colorBYD([myBYDBox:Battery_1_VoltsperCell_062]),&amp;quot;14&amp;quot;,\&lt;br /&gt;
[myBYDBox:Battery_1_VoltsperCell_063],colorBYD([myBYDBox:Battery_1_VoltsperCell_063]),&amp;quot;15&amp;quot;) |\&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Anlegen der ElectricityCalculator für Bezug, Einspeisung, Erzeugung und Hausverbrauch ==&lt;br /&gt;
vier Module ElectricityCalculator (v.Sailor) werden angelegt um die gesammten Statistikdaten&lt;br /&gt;
&lt;br /&gt;
(Heute, Gestern, Monat, Vormonat, Jahr, Vorjahr) zu erzeugen. Das geht auch mit dem Modul Statistics.&amp;lt;div class=&amp;quot;mw-collapsible mw-collapsed&amp;quot;&amp;gt;&lt;br /&gt;
Code:&amp;lt;div class=&amp;quot;mw-collapsible-content&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
define Hausverbrauch ElectricityCalculator Fronius_Symo:User_Consumed_Energie_C.*&lt;br /&gt;
attr Hausverbrauch BasicPricePerAnnum 0&lt;br /&gt;
attr Hausverbrauch Currency &amp;amp;#8364;;&lt;br /&gt;
attr Hausverbrauch DbLogExclude .*&lt;br /&gt;
attr Hausverbrauch DbLogInclude Fronius_Symo_User_Consumed_Energie_C_EnergyDay&lt;br /&gt;
attr Hausverbrauch DecimalPlace 3&lt;br /&gt;
attr Hausverbrauch ElectricityCounterOffset 0&lt;br /&gt;
attr Hausverbrauch ElectricityKwhPerCounts 1&lt;br /&gt;
attr Hausverbrauch ElectricityPricePerKWh 0.2567&lt;br /&gt;
attr Hausverbrauch MonthOfAnnualReading 1&lt;br /&gt;
attr Hausverbrauch MonthlyPayment 0&lt;br /&gt;
attr Hausverbrauch ReadingDestination CalculatorDevice&lt;br /&gt;
attr Hausverbrauch SiPrefixPower W&lt;br /&gt;
attr Hausverbrauch room Energie-Strom&lt;br /&gt;
attr Hausverbrauch stateFormat Fronius_Symo_User_Consumed_Energie_C_EnergyDay kWh&lt;br /&gt;
attr Hausverbrauch verbose 0&lt;br /&gt;
&lt;br /&gt;
define Fronius_Erzeugung ElectricityCalculator Fronius_Symo:User_Produced_PV.*&lt;br /&gt;
attr Fronius_Erzeugung BasicPricePerAnnum 0&lt;br /&gt;
attr Fronius_Erzeugung Currency &amp;amp;#8364;;&lt;br /&gt;
attr Fronius_Erzeugung DbLogExclude .*&lt;br /&gt;
attr Fronius_Erzeugung DbLogInclude Fronius_Symo_User_Produced_PV_EnergyDay&lt;br /&gt;
attr Fronius_Erzeugung DecimalPlace 3&lt;br /&gt;
attr Fronius_Erzeugung ElectricityCounterOffset -0.148&lt;br /&gt;
attr Fronius_Erzeugung ElectricityKwhPerCounts 1&lt;br /&gt;
attr Fronius_Erzeugung ElectricityPricePerKWh 0.3305&lt;br /&gt;
attr Fronius_Erzeugung MonthOfAnnualReading 1&lt;br /&gt;
attr Fronius_Erzeugung MonthlyPayment 0&lt;br /&gt;
attr Fronius_Erzeugung ReadingDestination CalculatorDevice&lt;br /&gt;
attr Fronius_Erzeugung SiPrefixPower W&lt;br /&gt;
attr Fronius_Erzeugung alias Erzeugung&lt;br /&gt;
attr Fronius_Erzeugung room Energie-Strom&lt;br /&gt;
attr Fronius_Erzeugung stateFormat Fronius_Symo_User_Produced_PV_EnergyDay kWh&lt;br /&gt;
attr Fronius_Erzeugung verbose 0&lt;br /&gt;
&lt;br /&gt;
define Fronius_Einspeisung ElectricityCalculator Fronius_Symo:Meter_0_EnergyReal_WAC_Minus_Absolute.*&lt;br /&gt;
attr Fronius_Einspeisung BasicPricePerAnnum 0&lt;br /&gt;
attr Fronius_Einspeisung Currency &amp;amp;#8364;;&lt;br /&gt;
attr Fronius_Einspeisung DbLogExclude .*&lt;br /&gt;
attr Fronius_Einspeisung DbLogInclude Fronius_Symo_Meter_0_EnergyReal_WAC_Minus_Absolute_PowerCurrent,Fronius_Symo_Meter_0_EnergyReal_WAC_Minus_Absolute_EnergyDay&lt;br /&gt;
attr Fronius_Einspeisung DecimalPlace 3&lt;br /&gt;
attr Fronius_Einspeisung ElectricityCounterOffset 0&lt;br /&gt;
attr Fronius_Einspeisung ElectricityKwhPerCounts 0.001&lt;br /&gt;
attr Fronius_Einspeisung ElectricityPricePerKWh 0.083&lt;br /&gt;
attr Fronius_Einspeisung MonthOfAnnualReading 1&lt;br /&gt;
attr Fronius_Einspeisung MonthlyPayment 0&lt;br /&gt;
attr Fronius_Einspeisung ReadingDestination CalculatorDevice&lt;br /&gt;
attr Fronius_Einspeisung SiPrefixPower W&lt;br /&gt;
attr Fronius_Einspeisung alias Einspeisung&lt;br /&gt;
attr Fronius_Einspeisung room Energie-Strom&lt;br /&gt;
attr Fronius_Einspeisung stateFormat Fronius_Symo_Meter_0_EnergyReal_WAC_Minus_Absolute_EnergyDay kWh&lt;br /&gt;
attr Fronius_Einspeisung verbose 0&lt;br /&gt;
&lt;br /&gt;
define Fronius_Bezug ElectricityCalculator Fronius_Symo:Meter_0_EnergyReal_WAC_Plus_Absolute.*&lt;br /&gt;
attr Fronius_Bezug BasicPricePerAnnum 0&lt;br /&gt;
attr Fronius_Bezug Currency &amp;amp;#8364;;&lt;br /&gt;
attr Fronius_Bezug DbLogExclude .*&lt;br /&gt;
attr Fronius_Bezug DbLogInclude Fronius_Symo_Meter_0_EnergyReal_WAC_Plus_Absolute_EnergyDay,Fronius_Symo_Meter_0_EnergyReal_WAC_Plus_Absolute_PowerCurrent&lt;br /&gt;
attr Fronius_Bezug DecimalPlace 3&lt;br /&gt;
attr Fronius_Bezug ElectricityCounterOffset -36.075&lt;br /&gt;
attr Fronius_Bezug ElectricityKwhPerCounts 0.001&lt;br /&gt;
attr Fronius_Bezug ElectricityPricePerKWh 0.3305&lt;br /&gt;
attr Fronius_Bezug MonthOfAnnualReading 1&lt;br /&gt;
attr Fronius_Bezug MonthlyPayment 0&lt;br /&gt;
attr Fronius_Bezug ReadingDestination CalculatorDevice&lt;br /&gt;
attr Fronius_Bezug SiPrefixPower W&lt;br /&gt;
attr Fronius_Bezug alias Bezug&lt;br /&gt;
attr Fronius_Bezug room Energie-Strom&lt;br /&gt;
attr Fronius_Bezug stateFormat Fronius_Symo_Meter_0_EnergyReal_WAC_Plus_Absolute_EnergyDay kWh&lt;br /&gt;
attr Fronius_Bezug verbose 0&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Autarkie, Eigenverbrauch ==&lt;br /&gt;
Zwei Dummys mit dazugehörigen Füllroutinen werden angelegt&lt;br /&gt;
&amp;lt;div class=&amp;quot;mw-collapsible mw-collapsed&amp;quot;&amp;gt;&lt;br /&gt;
Code:&amp;lt;div class=&amp;quot;mw-collapsible-content&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
define AutarkieQuote dummy&lt;br /&gt;
attr AutarkieQuote DbLogExclude .*&lt;br /&gt;
attr AutarkieQuote alias Autarkie&lt;br /&gt;
attr AutarkieQuote group Fronius&lt;br /&gt;
attr AutarkieQuote room Energie-Strom&lt;br /&gt;
attr AutarkieQuote stateFormat Heute&lt;br /&gt;
attr AutarkieQuote userReadings Heute {get_autarkie ( ReadingsVal(&amp;quot;Fronius_Erzeugung&amp;quot;,&amp;quot;Fronius_Symo_User_Produced_PV_EnergyDay&amp;quot;,0) , ReadingsVal(&amp;quot;Fronius_Einspeisung&amp;quot;,&amp;quot;Fronius_Symo_Meter_0_EnergyReal_WAC_Minus_Absolute_EnergyDay&amp;quot;,0) , ReadingsVal(&amp;quot;Fronius_Bezug&amp;quot;,&amp;quot;Fronius_Symo_Meter_0_EnergyReal_WAC_Plus_Absolute_EnergyDay&amp;quot;,0) )},\&lt;br /&gt;
Gestern {get_autarkie ( ReadingsVal(&amp;quot;Fronius_Erzeugung&amp;quot;,&amp;quot;Fronius_Symo_User_Produced_PV_EnergyDayLast&amp;quot;,0) , ReadingsVal(&amp;quot;Fronius_Einspeisung&amp;quot;,&amp;quot;Fronius_Symo_Meter_0_EnergyReal_WAC_Minus_Absolute_EnergyDayLast&amp;quot;,0) , ReadingsVal(&amp;quot;Fronius_Bezug&amp;quot;,&amp;quot;Fronius_Symo_Meter_0_EnergyReal_WAC_Plus_Absolute_EnergyDayLast&amp;quot;,0) )},\&lt;br /&gt;
Monat {get_autarkie ( ReadingsVal(&amp;quot;Fronius_Erzeugung&amp;quot;,&amp;quot;Fronius_Symo_User_Produced_PV_EnergyMonth&amp;quot;,0) , ReadingsVal(&amp;quot;Fronius_Einspeisung&amp;quot;,&amp;quot;Fronius_Symo_Meter_0_EnergyReal_WAC_Minus_Absolute_EnergyMonth&amp;quot;,0) , ReadingsVal(&amp;quot;Fronius_Bezug&amp;quot;,&amp;quot;Fronius_Symo_Meter_0_EnergyReal_WAC_Plus_Absolute_EnergyMonth&amp;quot;,0) )},\&lt;br /&gt;
Vormonat {get_autarkie ( ReadingsVal(&amp;quot;Fronius_Erzeugung&amp;quot;,&amp;quot;Fronius_Symo_User_Produced_PV_EnergyMonthLast&amp;quot;,0) , ReadingsVal(&amp;quot;Fronius_Einspeisung&amp;quot;,&amp;quot;Fronius_Symo_Meter_0_EnergyReal_WAC_Minus_Absolute_EnergyMonthLast&amp;quot;,0) , ReadingsVal(&amp;quot;Fronius_Bezug&amp;quot;,&amp;quot;Fronius_Symo_Meter_0_EnergyReal_WAC_Plus_Absolute_EnergyMonthLast&amp;quot;,0) )},\&lt;br /&gt;
Jahr {get_autarkie ( ReadingsVal(&amp;quot;Fronius_Erzeugung&amp;quot;,&amp;quot;Fronius_Symo_User_Produced_PV_EnergyYear&amp;quot;,0) , ReadingsVal(&amp;quot;Fronius_Einspeisung&amp;quot;,&amp;quot;Fronius_Symo_Meter_0_EnergyReal_WAC_Minus_Absolute_EnergyYear&amp;quot;,0) , ReadingsVal(&amp;quot;Fronius_Bezug&amp;quot;,&amp;quot;Fronius_Symo_Meter_0_EnergyReal_WAC_Plus_Absolute_EnergyYear&amp;quot;,0) )}&lt;br /&gt;
&lt;br /&gt;
define EigenverbrQuote dummy&lt;br /&gt;
attr EigenverbrQuote DbLogExclude .*&lt;br /&gt;
attr EigenverbrQuote alias Eigenverbr.&lt;br /&gt;
attr EigenverbrQuote group Fronius&lt;br /&gt;
attr EigenverbrQuote room Energie-Strom&lt;br /&gt;
attr EigenverbrQuote stateFormat Heute&lt;br /&gt;
attr EigenverbrQuote userReadings Heute {get_eigenverbrauch ( ReadingsVal(&amp;quot;Fronius_Erzeugung&amp;quot;,&amp;quot;Fronius_Symo_User_Produced_PV_EnergyDay&amp;quot;,0) , ReadingsVal(&amp;quot;Fronius_Einspeisung&amp;quot;,&amp;quot;Fronius_Symo_Meter_0_EnergyReal_WAC_Minus_Absolute_EnergyDay&amp;quot;,0) )},\&lt;br /&gt;
Gestern {get_eigenverbrauch ( ReadingsVal(&amp;quot;Fronius_Erzeugung&amp;quot;,&amp;quot;Fronius_Symo_User_Produced_PV_EnergyDayLast&amp;quot;,0) , ReadingsVal(&amp;quot;Fronius_Einspeisung&amp;quot;,&amp;quot;Fronius_Symo_Meter_0_EnergyReal_WAC_Minus_Absolute_EnergyDayLast&amp;quot;,0) )},\&lt;br /&gt;
Monat {get_eigenverbrauch ( ReadingsVal(&amp;quot;Fronius_Erzeugung&amp;quot;,&amp;quot;Fronius_Symo_User_Produced_PV_EnergyMonth&amp;quot;,0) , ReadingsVal(&amp;quot;Fronius_Einspeisung&amp;quot;,&amp;quot;Fronius_Symo_Meter_0_EnergyReal_WAC_Minus_Absolute_EnergyMonth&amp;quot;,0) )},\&lt;br /&gt;
Vormonat {get_eigenverbrauch ( ReadingsVal(&amp;quot;Fronius_Erzeugung&amp;quot;,&amp;quot;Fronius_Symo_User_Produced_PV_EnergyMonthLast&amp;quot;,0) , ReadingsVal(&amp;quot;Fronius_Einspeisung&amp;quot;,&amp;quot;Fronius_Symo_Meter_0_EnergyReal_WAC_Minus_Absolute_EnergyMonthLast&amp;quot;,0) )},\&lt;br /&gt;
Jahr {get_eigenverbrauch ( ReadingsVal(&amp;quot;Fronius_Erzeugung&amp;quot;,&amp;quot;Fronius_Symo_User_Produced_PV_EnergyYear&amp;quot;,0) , ReadingsVal(&amp;quot;Fronius_Einspeisung&amp;quot;,&amp;quot;Fronius_Symo_Meter_0_EnergyReal_WAC_Minus_Absolute_EnergyYear&amp;quot;,0) )}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;In die Datei 99_myUtils.pm die Berechnungsroutinen eintragen:&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
sub get_eigenverbrauch &lt;br /&gt;
{&lt;br /&gt;
  my($erzeugung,$einspeisung) = @_;&lt;br /&gt;
	return(100) if ($erzeugung)&amp;lt;=0;&lt;br /&gt;
my($eigenverbrauch)=(($erzeugung)-($einspeisung))/($erzeugung)*100;&lt;br /&gt;
    return($eigenverbrauch);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
sub get_autarkie&lt;br /&gt;
{&lt;br /&gt;
  my($erzeugung,$einspeisung,$bezug) = @_;&lt;br /&gt;
  my($autarkie)=(($erzeugung)-($einspeisung))/(($erzeugung)-($einspeisung)+($bezug))*100;&lt;br /&gt;
  return($autarkie);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Zusätzliche Routinen in 99_myUtils.pm um zu kleine oder auch zu große Werte auszusondern. Das &amp;quot;verschönert&amp;quot; die Graphen; kann auch weggelassen werden. Hintergrund: Diffential/Integralberechnungen unter/übersteuern beim Start.&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
sub reduce_max_value&lt;br /&gt;
{&lt;br /&gt;
  my($device, $reading, $max_value)=@_;&lt;br /&gt;
  # Begrenzung der Ausgabewerte für Graphen&lt;br /&gt;
  my($Grenzwert) = ReadingsVal($device,$reading, 0);&lt;br /&gt;
   return($max_value) if (ReadingsVal($device, $reading,0) &amp;gt; $max_value );&lt;br /&gt;
  return($Grenzwert); &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
sub reduce_min_value&lt;br /&gt;
{&lt;br /&gt;
  my($device, $reading, $min_value)=@_;&lt;br /&gt;
  # Begrenzung der Ausgabewerte für Graphen&lt;br /&gt;
  my($Grenzwert) = ReadingsVal($device,$reading, 0);&lt;br /&gt;
   return(0) if (ReadingsVal($device, $reading,0) &amp;lt;= $min_value );&lt;br /&gt;
  return($Grenzwert); &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Alle 15 Min erneute Berechnung der Werte (oder eine längere Zeit setzen...)&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
define Autarkie_Timer at +*00:15:00 {fhem &amp;quot;setreading AutarkieQuote Heute 1&amp;quot;}&lt;br /&gt;
attr Autarkie_Timer DbLogExclude .*&lt;br /&gt;
attr Autarkie_Timer room Energie-Strom&lt;br /&gt;
&lt;br /&gt;
define Eigenverbr_Timer at +*00:15:00 {fhem &amp;quot;setreading EigenverbrQuote Heute 1&amp;quot;}&lt;br /&gt;
attr Eigenverbr_Timer DbLogExclude .*&lt;br /&gt;
attr Eigenverbr_Timer room Energie-Strom&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== mySolarStat ==&lt;br /&gt;
[[Datei:Screenshot 2024-11-14 164746.jpg|mini|ReadingsGroup]]&lt;br /&gt;
Eine größere readingsGroup als Übersichtstabelle, passt auch gut in ftui2 aufs Tablet&lt;br /&gt;
&amp;lt;div class=&amp;quot;mw-collapsible mw-collapsed&amp;quot;&amp;gt;&lt;br /&gt;
Code:&amp;lt;div class=&amp;quot;mw-collapsible-content&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
define mySolarStat readingsGroup &amp;lt;  &amp;gt;,&amp;lt;Heute&amp;gt;,&amp;lt;Gestern&amp;gt;,&amp;lt;Monat&amp;gt;,&amp;lt;Vormonat&amp;gt;,&amp;lt;Jahr&amp;gt; \&lt;br /&gt;
Fronius_Erzeugung:Fronius_Symo_User_Produced_PV_EnergyDay,Fronius_Symo_User_Produced_PV_EnergyDayLast,Fronius_Symo_User_Produced_PV_EnergyMonth,Fronius_Symo_User_Produced_PV_EnergyMonthLast,Fronius_Symo_User_Produced_PV_EnergyYear \&lt;br /&gt;
Fronius_Einspeisung:Fronius_Symo_Meter_0_EnergyReal_WAC_Minus_Absolute_EnergyDay,Fronius_Symo_Meter_0_EnergyReal_WAC_Minus_Absolute_EnergyDayLast,Fronius_Symo_Meter_0_EnergyReal_WAC_Minus_Absolute_EnergyMonth,Fronius_Symo_Meter_0_EnergyReal_WAC_Minus_Absolute_EnergyMonthLast,Fronius_Symo_Meter_0_EnergyReal_WAC_Minus_Absolute_EnergyYear \&lt;br /&gt;
Fronius_Bezug:Fronius_Symo_Meter_0_EnergyReal_WAC_Plus_Absolute_EnergyDay,Fronius_Symo_Meter_0_EnergyReal_WAC_Plus_Absolute_EnergyDayLast,Fronius_Symo_Meter_0_EnergyReal_WAC_Plus_Absolute_EnergyMonth,Fronius_Symo_Meter_0_EnergyReal_WAC_Plus_Absolute_EnergyMonthLast,Fronius_Symo_Meter_0_EnergyReal_WAC_Plus_Absolute_EnergyYear \&lt;br /&gt;
Hausverbrauch:Fronius_Symo_User_Consumed_Energie_C_EnergyDay,Fronius_Symo_User_Consumed_Energie_C_EnergyDayLast,Fronius_Symo_User_Consumed_Energie_C_EnergyMonth,Fronius_Symo_User_Consumed_Energie_C_EnergyMonth,Fronius_Symo_User_Consumed_Energie_C_EnergyYear\&lt;br /&gt;
Autolade_Calculator:C_STROM_GAS_counters.A_EnergyDay,C_STROM_GAS_counters.A_EnergyDayLast,C_STROM_GAS_counters.A_EnergyMonth,C_STROM_GAS_counters.A_EnergyMonthLast,C_STROM_GAS_counters.A_EnergyYear \&lt;br /&gt;
AutarkieQuote:Heute,Gestern,Monat,Vormonat,Jahr \&lt;br /&gt;
EigenverbrQuote:Heute,Gestern,Monat,Vormonat,Jahr&lt;br /&gt;
attr mySolarStat alias Solarstromstatistik kWh / %&lt;br /&gt;
attr mySolarStat room Energie-Auto,Energie-Strom,Statistik&lt;br /&gt;
attr mySolarStat valueFormat %.1f&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Grafiken ==&lt;br /&gt;
[[Datei:Screenshot 2024-11-14 164925.jpg|mini|Erzeugung]]&lt;br /&gt;
Strombezug, Stromeinspeisung, Stromerzeugung, Stromverbrauch, Solare Vorhersage&lt;br /&gt;
hier &amp;quot;nur&amp;quot; ein Beispiel Stromerzeugung, die anderen Graphen dementsprechend anlegen&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;mw-collapsible mw-collapsed&amp;quot;&amp;gt;&lt;br /&gt;
Code:&amp;lt;div class=&amp;quot;mw-collapsible-content&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
defmod SVG_myDbLog_1 SVG myDbLog:SVG_myDbLog_1:HISTORY&lt;br /&gt;
attr SVG_myDbLog_1 alias Stromerzeugung&lt;br /&gt;
attr SVG_myDbLog_1 fp_Energieerzeugung 415,204,1,SVG_myDbLog_1,&lt;br /&gt;
attr SVG_myDbLog_1 fp_Energieverbrauch 204,164,1,SVG_myDbLog_1,&lt;br /&gt;
attr SVG_myDbLog_1 label $data{currval1}::$data{currval2}::$data{currval3}::$data{currval4}::$data{currval5}::&lt;br /&gt;
attr SVG_myDbLog_1 plotReplace L1={sprintf(&amp;quot;%.1f&amp;quot;, $data{currval1})} L2={sprintf(&amp;quot;%.1f&amp;quot;, $data{currval2})}&lt;br /&gt;
attr SVG_myDbLog_1 room Energie-Strom&lt;br /&gt;
attr SVG_myDbLog_1 title &amp;quot;Stromerzeugung&amp;quot;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;und dazu die .gplot Datei:&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
# Created by FHEM/98_SVG.pm, 2024-10-31 18:49:58&lt;br /&gt;
set terminal png transparent size &amp;lt;SIZE&amp;gt; crop&lt;br /&gt;
set output &#039;&amp;lt;OUT&amp;gt;.png&#039;&lt;br /&gt;
set xdata time&lt;br /&gt;
set timefmt &amp;quot;%Y-%m-%d_%H:%M:%S&amp;quot;&lt;br /&gt;
set xlabel &amp;quot; &amp;quot;&lt;br /&gt;
set title &#039;&amp;lt;TL&amp;gt; &amp;lt;L1&amp;gt; W &amp;lt;L2&amp;gt;kWh&#039;&lt;br /&gt;
set ytics &lt;br /&gt;
set y2tics &lt;br /&gt;
set grid y2tics&lt;br /&gt;
set ylabel &amp;quot;Momentanleistung [W]&amp;quot;&lt;br /&gt;
set y2label &amp;quot;Tageserzeugung [kWh]&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#myDbLog Fronius_Symo:PowerFlow_Site_P_PV:::&lt;br /&gt;
#myDbLog Fronius_Erzeugung:Fronius_Symo_User_Produced_PV_EnergyDay:::&lt;br /&gt;
&lt;br /&gt;
plot &amp;quot;&amp;lt;IN&amp;gt;&amp;quot; using 1:2 axes x1y1 title &#039;Momentanleistung &amp;lt;L1&amp;gt; [W]&#039; ls l3 lw 2 with lines,\&lt;br /&gt;
     &amp;quot;&amp;lt;IN&amp;gt;&amp;quot; using 1:2 axes x1y2 title &#039;Tageserzeugung &amp;lt;L2&amp;gt; [kWh]&#039; ls l2fill lw 1 with lines&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== SolarForecast ==&lt;br /&gt;
[[Datei:Screenshot 2024-11-15 173408.jpg|mini|SolarForecast Darstellung]]&lt;br /&gt;
Es müssen vorher unter global die attr longitude und latitude für den Standort der Anlage eingegeben werden, damit die einzelnen Wettermodelle funktionieren. Forecast hat ein prima selbsterklärendes Startmenü und man sieht auch gleich die evt. vorhandenen Einstellungsfehler.&lt;br /&gt;
&lt;br /&gt;
Weiteres dazu im umfangreichen [[SolarForecast - Solare Prognose (PV Erzeugung) und Verbrauchersteuerung|Wiki Solarforecast]].&lt;br /&gt;
&lt;br /&gt;
Die dort beschriebene Grafik &amp;quot;Solare Vorhersage&amp;quot; zeigt die Differenzen zwischen der berechneten Vorhersage und der tatsächlichen PV-Erzeugung eines Tages. Die Werte, die in der Anlagenübersicht angezeigt werden, sollten identisch zu denen in dem u.A. ftui2 Widget sein (Gegenkontrolle).&lt;br /&gt;
&lt;br /&gt;
In diesem Beispiel ist ein Interner consumer01 (Autoladen) eingestellt, dessen Werte nicht mit in den Forecast einbezogen werden.&lt;br /&gt;
&amp;lt;div class=&amp;quot;mw-collapsible mw-collapsed&amp;quot;&amp;gt;&lt;br /&gt;
Code:&amp;lt;div class=&amp;quot;mw-collapsible-content&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
defmod Forecast SolarForecast&lt;br /&gt;
attr Forecast DbLogExclude .*&lt;br /&gt;
attr Forecast DbLogInclude LastHourPVforecast,LastHourPVreal,AllPVforecastsToEvent&lt;br /&gt;
attr Forecast consumer01 MQTT2_evcc1 type=charger power=0 exconfc=1 icon=electric_car_icon pcurr=loadpoints_1_chargePower :W&lt;br /&gt;
attr Forecast ctrlLanguage DE&lt;br /&gt;
attr Forecast ctrlNextHoursSoCForecastReadings 07,08,09,10,11,12,13,14,15,16,17,18,19,20,21&lt;br /&gt;
attr Forecast ctrlSpecialReadings BatPowerIn_Sum,BatPowerOut_Sum,conForecastTillNextSunrise,dayAfterTomorrowPVforecast,todayConsumption,todayConsumptionForecast,todayConsumptionForecastDay,todayGridConsumption,todayGridFeedIn,tomorrowConsumptionForecast&lt;br /&gt;
attr Forecast event-on-change-reading .*&lt;br /&gt;
attr Forecast graphicControl energyUnit=kWh headerDetail=all,pv&lt;br /&gt;
attr Forecast graphicHistoryHour 8&lt;br /&gt;
attr Forecast graphicSelect forecast&lt;br /&gt;
attr Forecast graphicShowNight 01&lt;br /&gt;
attr Forecast plantControl showLink=1&lt;br /&gt;
attr Forecast room Energie-Strom&lt;br /&gt;
attr Forecast setupBatteryDev01 PV_Batterie pin=BatteryChargeWatt:W pout=BatteryDischargeWatt:W intotal=Summe_Ladung:kWh outtotal=Summe_Entladung:kWh show=1 cap=12800 charge=BatteryChargePercent&lt;br /&gt;
attr Forecast setupInverterDev01 Fronius_Symo pvOut=PowerFlow_Site_P_PV:W etotal=User_Produced_FPV:kWh capacity=11000 strings:Ostseite,Suedseite&lt;br /&gt;
attr Forecast setupInverterDev02 Fronius_Symo pvOut=Meter_1_PowerReal_P_Sum:W etotal=Meter_1_EnergyReal_WAC_Sum_Produced:Wh capacity=4000 strings:Westseite,GH_Ost,GH_West&lt;br /&gt;
attr Forecast setupInverterStrings Suedseite,Westseite,Ostseite,GH_West,GH_Ost&lt;br /&gt;
attr Forecast setupMeterDev Fronius_Symo gcon=Bezug:W contotal=Meter_0_EnergyReal_WAC_Plus_Absolute:Wh gfeedin=Einspeisung feedtotal=Meter_0_EnergyReal_WAC_Plus_Absolute:Wh conprice=powerCost:€ feedprice=0.083:€&lt;br /&gt;
attr Forecast setupRadiationAPI OpenMeteoDWD_D2-API&lt;br /&gt;
attr Forecast setupStringAzimuth Suedseite=0 Westseite=90 Ostseite=-90 GH_Ost=-90 GH_West=90&lt;br /&gt;
attr Forecast setupStringDeclination Suedseite=35 Westseite=60 Ostseite=45 GH_West=18 GH_Ost=18&lt;br /&gt;
attr Forecast setupStringPeak Suedseite=7.66 Westseite=2.2 Ostseite=3.48 GH_Ost=1.74 GH_West=0.58&lt;br /&gt;
attr Forecast setupWeatherDev1 OpenMeteoDWD_D2-API&lt;br /&gt;
attr Forecast verbose 0&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
[[Datei:Screenshot 2024-11-14 165143.jpg|mini|SolarForecast Diff. Grafik]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Die Unterschiede vom Forecast zur reellen Erzeugung können in einer Grafik dargestellt werden.&lt;br /&gt;
&lt;br /&gt;
Code siehe weiter unten.&lt;br /&gt;
&lt;br /&gt;
== Autoladen über evcc ==&lt;br /&gt;
Integration und Steuerung der Wallbox, Wechselrichter, Batterie sowie zu ladendes Fahrzeug kann über das Programm evcc gemacht werden.&lt;br /&gt;
&lt;br /&gt;
Die Software evcc läuft problemlos parallel auf dem FHEM Systemrechner und kann über MQTT2 eingebunden werden.&lt;br /&gt;
&lt;br /&gt;
Weitere Infos dazu in https://evcc.io/&lt;br /&gt;
&lt;br /&gt;
Der Nachteil dieser (externen) Software ist der Preis. Es wird mit Open-Source-Software geworben, jedoch die meisten Module z.B. zum Steuern eines Fahrzeugs oder einer Wallbox gehen nur über einen sogenannten Sponsortoken, den man für 1$ pro Monat erwerben kann. Aber solange ein solches Modul mit ähnlich umfangreichen Möglichkeiten bei FHEM nicht zur Verfügung steht, hier die Möglichkeit der Integration in FHEM.&lt;br /&gt;
&lt;br /&gt;
Der Clou ist hiebei ist die Hausbatterie entsprechend der evcc Möglichkeiten zu schalten und auch das Programm zu steuern&lt;br /&gt;
&lt;br /&gt;
auf welche Art das Fz geladen werden soll: Sofort, nur PV Überschuss, Minimal + PV oder Aus&lt;br /&gt;
&lt;br /&gt;
Zusätzlich ein weiterer ElectricityCalculator für die Autoladestatistik der Wallbox.&lt;br /&gt;
&lt;br /&gt;
Dieser übergibt seine Autoladungswerte über mqttPublish zurück an fhem.&lt;br /&gt;
&amp;lt;div class=&amp;quot;mw-collapsible mw-collapsed&amp;quot;&amp;gt;&lt;br /&gt;
Code:&amp;lt;div class=&amp;quot;mw-collapsible-content&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
defmod MQTT2_evcc1 MQTT2_DEVICE evcc1&lt;br /&gt;
attr MQTT2_evcc1 DbLogExclude .*&lt;br /&gt;
attr MQTT2_evcc1 DbLogInclude loadpoints_1_chargePower&lt;br /&gt;
attr MQTT2_evcc1 alias Wally&lt;br /&gt;
attr MQTT2_evcc1 autocreate 1&lt;br /&gt;
attr MQTT2_evcc1 comment Achtung: EVCC greift per Modbus auf Fronius zu Port 502 Adr. 200&lt;br /&gt;
attr MQTT2_evcc1 event-on-change-reading .*&lt;br /&gt;
attr MQTT2_evcc1 event-on-update-reading .*&lt;br /&gt;
attr MQTT2_evcc1 icon wallbox&lt;br /&gt;
attr MQTT2_evcc1 room Energie-Auto&lt;br /&gt;
attr MQTT2_evcc1 setList ChargeMode:Now,Min+PV,PV,Stop { my %h=(Now=&amp;gt;&#039;now&#039;,&#039;Min+PV&#039;=&amp;gt;&#039;minpv&#039;,PV=&amp;gt;&#039;pv&#039;,Stop=&amp;gt;&#039;off&#039;);; qq(evcc/loadpoints/1/mode/set $h{$EVTPART1});; } \&lt;br /&gt;
PvPriority:Home,Car,FillCar { my %h=(Home=&amp;gt;&#039;95&#039;,&#039;Car&#039;=&amp;gt;&#039;35&#039;,&#039;FillCar&#039;=&amp;gt;&#039;15&#039;);; qq(evcc/site/prioritySoc/set $h{$EVTPART1});; }\&lt;br /&gt;
MaxCurrent:11,14,16,20,24,32 { qq(evcc/loadpoints/1/maxCurrent/set $EVTPART1);; }&lt;br /&gt;
attr MQTT2_evcc1 stateFormat Auto_Status | loadpoints_1_mode | loadpoints_1_chargePower W&lt;br /&gt;
attr MQTT2_evcc1 userReadings Auto_Status { (ReadingsVal($name,&#039;loadpoints_1_connected&#039;,&#039;&#039;) eq &#039;true&#039; ? 2 : 0) },\&lt;br /&gt;
Reichweite { ((ReadingsVal($name,&#039;loadpoints_1_vehicleSoc&#039;,&#039;&#039;)-10)*0.67) },\&lt;br /&gt;
Ladeleistung { (ReadingsVal($name,&#039;loadpoints_1_chargePower&#039;,&#039;&#039;)/1000) }&lt;br /&gt;
&lt;br /&gt;
Und nun noch ein ElectricityCalculator, zur Berechnung der Strommenge:&lt;br /&gt;
Die Stromwerte kommen aus einem 1-Wire Counter der einen extra Zähler über den SO Port zählt.&lt;br /&gt;
&lt;br /&gt;
Wenn die Wallbox einen Stromzähler hat, geht das natürlich einfacher; nur die Statistikwerte dieses Zählers werden benötigt.&lt;br /&gt;
&lt;br /&gt;
defmod Autolade_Calculator ElectricityCalculator MQTT2_evcc1:loadpoints_1_chargeTotalImport.*&lt;br /&gt;
attr Autolade_Calculator BasicPricePerAnnum 132.84&lt;br /&gt;
attr Autolade_Calculator Currency €&lt;br /&gt;
attr Autolade_Calculator DbLogExclude .*&lt;br /&gt;
attr Autolade_Calculator DbLogInclude MQTT2_evcc1_loadpoints_1_chargeTotalImport_EnergyDay&lt;br /&gt;
attr Autolade_Calculator DecimalPlace 3&lt;br /&gt;
attr Autolade_Calculator ElectricityCounterOffset 0&lt;br /&gt;
attr Autolade_Calculator ElectricityKwhPerCounts 1&lt;br /&gt;
attr Autolade_Calculator ElectricityPricePerKWh 0.3305&lt;br /&gt;
attr Autolade_Calculator MonthOfAnnualReading 1&lt;br /&gt;
attr Autolade_Calculator MonthlyPayment 121&lt;br /&gt;
attr Autolade_Calculator ReadingDestination CalculatorDevice&lt;br /&gt;
attr Autolade_Calculator SiPrefixPower kW&lt;br /&gt;
attr Autolade_Calculator alias Autoladen&lt;br /&gt;
attr Autolade_Calculator room Energie-Auto,Steuerung&lt;br /&gt;
attr Autolade_Calculator stateFormat Heute: MQTT2_evcc1_loadpoints_1_chargeTotalImport_EnergyDay kWh&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Datei:Solaranlage-ftui.jpg|mini|Solaranlagenübersicht]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==ftui2 Widgets zur Darstellung auf einem Tablet==&lt;br /&gt;
Widget pvvis und angepasste Daten zur Darstellung&lt;br /&gt;
&lt;br /&gt;
Einige Werte werden intern berechnet, das {{Link2Forum|Topic=119440|Message=1320903|LinkText=Widget}} selbst kann im Forum geladen werden.&lt;br /&gt;
&lt;br /&gt;
Widget&lt;br /&gt;
&amp;lt;div class=&amp;quot;mw-collapsible mw-collapsed&amp;quot;&amp;gt;&lt;br /&gt;
Code:&amp;lt;div class=&amp;quot;mw-collapsible-content&amp;quot;&amp;gt;&amp;lt;pre&amp;gt;&lt;br /&gt;
		&amp;lt;header&amp;gt;Solaranlage und Batterie&amp;lt;/header&amp;gt;&lt;br /&gt;
&lt;br /&gt;
					&amp;lt;div data-type=&amp;quot;pvvis&amp;quot; data-device=&amp;quot;Fronius_Symo&amp;quot;&lt;br /&gt;
						data-get=&amp;quot;Fronius_Symo:Bezug&amp;quot;&lt;br /&gt;
						data-feed=&amp;quot;Fronius_Symo:Neg_Einspeisung&amp;quot;&lt;br /&gt;
						data-lp1=&amp;quot;MQTT2_evcc1:Auto_Status&amp;quot;&lt;br /&gt;
						data-lp1pow=&amp;quot;Autolade_Calculator:Auto_reduce&amp;quot;&lt;br /&gt;
						data-carsoc=&amp;quot;MQTT2_evcc1:loadpoints_1_vehicleSoc&amp;quot;&lt;br /&gt;
						data-carvol=&amp;quot;Autolade_Calculator:C_STROM_GAS_counters.A_EnergyDay&amp;quot;&lt;br /&gt;
						data-carrange=&amp;quot;MQTT2_evcc1:Reichweite&amp;quot;&lt;br /&gt;
						data-charge=&amp;quot;Fronius_Symo:Akku_Laden&amp;quot;&lt;br /&gt;
						data-discharge=&amp;quot;Fronius_Symo:Akku_Entladen&amp;quot;&lt;br /&gt;
						data-produce=&amp;quot;Fronius_Symo:PowerFlow_Site_P_PV&amp;quot;&lt;br /&gt;
						data-sumproduceday=&amp;quot;Fronius_Erzeugung:Fronius_Symo_User_Produced_PV_EnergyDay&amp;quot;&lt;br /&gt;
						data-sumgridday=&amp;quot;Fronius_Bezug:Fronius_Symo_Meter_0_EnergyReal_WAC_Plus_Absolute_EnergyDay&amp;quot;&lt;br /&gt;
						data-sumfeedday=&amp;quot;Fronius_Einspeisung:Fronius_Symo_Meter_0_EnergyReal_WAC_Minus_Absolute_EnergyDay&amp;quot;&lt;br /&gt;
						data-soc=&amp;quot;Fronius_Symo:PowerFlow_Inverters_1_SOC&amp;quot;&lt;br /&gt;
						data-chargedischarge=&amp;quot;Fronius_Symo:PowerFlow_Site_P_Akku&amp;quot;&lt;br /&gt;
						data-pv-max=&amp;quot;10000&amp;quot;&lt;br /&gt;
						data-batt-max=&amp;quot;10240&amp;quot;&lt;br /&gt;
						data-autarky=&amp;quot;AutarkieQuote:Heute&amp;quot;&lt;br /&gt;
						data-pvhome=&amp;quot;Fronius_Symo:PowerFlow_Site_P_Load&amp;quot;&lt;br /&gt;
						data-width=&amp;quot;450&amp;quot; data-height=&amp;quot;450&amp;quot; class=&amp;quot;centered&amp;quot;&amp;gt;&lt;br /&gt;
					&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== [[SolarForecast FTUI Widget|Widget SolarForecast]] für die Vorschau auf ftui2, darunter die Differenz Vorhersage/reale Erzeugung: ==&lt;br /&gt;
&amp;lt;div class=&amp;quot;mw-collapsible mw-collapsed&amp;quot;&amp;gt;&lt;br /&gt;
Code:&amp;lt;div class=&amp;quot;mw-collapsible-content&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;div class=&amp;quot;page&amp;quot; id=&amp;quot;content_forecast&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;div class=&amp;quot;gridster&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;ul&amp;gt;&lt;br /&gt;
&lt;br /&gt;
		 &amp;lt;li data-row=&amp;quot;1&amp;quot; data-col=&amp;quot;1&amp;quot; data-sizex=&amp;quot;9&amp;quot; data-sizey=&amp;quot;9&amp;quot;&amp;gt;&lt;br /&gt;
				&amp;lt;header&amp;gt;Solar Vorhersage&amp;lt;/header&amp;gt;&lt;br /&gt;
				&amp;lt;div class=&amp;quot;cell&amp;quot;&amp;gt;&lt;br /&gt;
					&amp;lt;div data-type=&amp;quot;smaportalspg&amp;quot; data-device=&amp;quot;Forecast&amp;quot; data-get=&amp;quot;state&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;  &lt;br /&gt;
				&amp;lt;/div&amp;gt;&lt;br /&gt;
		&amp;lt;/li&amp;gt; &lt;br /&gt;
		&amp;lt;li data-row=&amp;quot;9&amp;quot; data-col=&amp;quot;1&amp;quot; data-sizex=&amp;quot;9&amp;quot; data-sizey=&amp;quot;4&amp;quot;&amp;gt;&lt;br /&gt;
		&amp;lt;header&amp;gt;Vorhersage&amp;lt;/header&amp;gt;&lt;br /&gt;
			&amp;lt;div data-type=&amp;quot;chart&amp;quot;&lt;br /&gt;
			data-device=&amp;quot;Hausverbrauch&amp;quot;&lt;br /&gt;
			data-logdevice=&#039;[&amp;quot;myDbLog&amp;quot;]&#039;&lt;br /&gt;
			data-logfile=&#039;[&amp;quot;HISTORY&amp;quot;]&#039;&lt;br /&gt;
			data-columnspec=&#039;[&amp;quot;Forecast:LastHourPVreal&amp;quot;,&amp;quot;Forecast:LastHourPVforecast&amp;quot;]&#039;&lt;br /&gt;
			data-style=&#039;[&amp;quot;ftui l6fill&amp;quot;,&amp;quot;ftui l1fill&amp;quot;]&#039;&lt;br /&gt;
			data-uaxis=&#039;[&amp;quot;primary&amp;quot;,&amp;quot;secondary&amp;quot;]&#039;&lt;br /&gt;
			data-legend=&#039;[&amp;quot;Akt.Erzeugung&amp;quot;,&amp;quot;Akt.Vorhersage&amp;quot;]&#039;&lt;br /&gt;
			data-yunit=&amp;quot;°kWh&amp;quot;&lt;br /&gt;
			data-ytext=&amp;quot;Menge&amp;quot;&lt;br /&gt;
				data-width=&amp;quot;100%&amp;quot;&lt;br /&gt;
				data-height=&amp;quot;100%&amp;quot;&lt;br /&gt;
				data-yticks=&amp;quot;auto&amp;quot;&lt;br /&gt;
				data-minvalue=&amp;quot;auto&amp;quot;&lt;br /&gt;
				data-maxvalue=&amp;quot;auto&amp;quot;&lt;br /&gt;
				data-nofulldays=&amp;quot;true&amp;quot;&lt;br /&gt;
				data-daysago_start=&amp;quot;-6H&amp;quot;&lt;br /&gt;
				data-daysago_end=&amp;quot;-22H&amp;quot;&lt;br /&gt;
				data-xticks=&amp;quot;auto&amp;quot;&lt;br /&gt;
			&amp;lt;/div&amp;gt;&lt;br /&gt;
		&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/ul&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;(Nur der der Vollständigkeit wegen)&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&amp;lt;!-- Fortsetzung folgt --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Wechselrichter]]&lt;br /&gt;
[[Kategorie:Energieerzeugungsmessung]]&lt;/div&gt;</summary>
		<author><name>Ph1959de</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=Diskussion:Solaranlage_Komplettbeispiel_Fronius_BYD&amp;diff=40725</id>
		<title>Diskussion:Solaranlage Komplettbeispiel Fronius BYD</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=Diskussion:Solaranlage_Komplettbeispiel_Fronius_BYD&amp;diff=40725"/>
		<updated>2026-01-12T18:04:10Z</updated>

		<summary type="html">&lt;p&gt;Ph1959de: Neuer Abschnitt /* Datein / Screenshots - Verwendung */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;So Leute, die Grundstruktur steht erst mal.&lt;br /&gt;
Soll der Titel so bleiben, oder geändert werden?&lt;br /&gt;
:Der Titel kann so bleiben - was Du noch machen solltest: an geeigneten Stellen auf diese Beispiel verweisen (kannst Dich vielleicht an [[Spezial:Linkliste/SolarForecast_-_Solare_Prognose_(PV_Erzeugung)_und_Verbrauchersteuerung]] orientieren), damit der Beitrag nicht nur &amp;quot;zufällig&amp;quot; oder über einen Verweis aus den Forum gefunden wird. --[[Benutzer:Ph1959de|Peter]] ([[Benutzer Diskussion:Ph1959de|Diskussion]]) 11:34, 5. Nov. 2025 (CET)&lt;br /&gt;
&lt;br /&gt;
== Abhängigkeit von Autolade_Calculator ==&lt;br /&gt;
Wenn man den Code im Abschnitt &amp;quot;Fronius_Symo in FHEM&amp;quot; 1:1 übernimmt, führt eine Abhängigkeit von &amp;quot;Autolade_Calculator&amp;quot; in userReadings zu Einträgen im logfile, wenn kein device &amp;quot;Autolade_Calculator&amp;quot; definiert ist:&lt;br /&gt;
&lt;br /&gt;
&amp;quot;User_Consumed_EN:PowerFlow_Site_P_PV.* {ReadingsVal($name,&amp;quot;PowerFlow_Site_P_PV&amp;quot;,&amp;quot;&amp;quot;)-ReadingsVal(&amp;quot;Autolade_Calculator&amp;quot;,&amp;quot;Auto_Ladung_av&amp;quot;,&amp;quot;&amp;quot;)+ReadingsVal($name,&amp;quot;PowerFlow_Site_P_Grid&amp;quot;,&amp;quot;&amp;quot;)+ReadingsVal($name,&amp;quot;PowerFlow_Site_P_Akku&amp;quot;,&amp;quot;&amp;quot;)},&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Ich schlage vor, die Zeile zu entfernen (oder zu erläutern, welches device da verlangt wird).&lt;br /&gt;
&lt;br /&gt;
(Anmerkung: Bei mir irritierte zusätzlich, dass die log-Einträge selbst mit verbose=0 auftraten)&lt;br /&gt;
&lt;br /&gt;
== Linkliste ==&lt;br /&gt;
&lt;br /&gt;
Habe die Linkliste geprüft, alles soweit ok.&lt;br /&gt;
Änderungen in anderen WIKI zu Links hierauf werde ich noch nicht erstellen.&lt;br /&gt;
Muss das System WIKI erst mal verstehen.....&lt;br /&gt;
&lt;br /&gt;
== Datein / Screenshots - Verwendung ==&lt;br /&gt;
&lt;br /&gt;
Hallo Wds1957,&lt;br /&gt;
Du hast die Verlinkung von &#039;&#039;Screenshot 2024-09-25 191422.jpg&#039;&#039; im Artikel ersetzt durch &#039;&#039;Screenshot 20250612 192318 Fully Kiosk Browser.jpg&#039;&#039;. Dadurch wird erstere Datei nicht mehr verwendet. Falls sie gelöscht werden soll, bitte durch Kennzeichnung als &#039;&#039;&amp;lt;nowiki&amp;gt;{{Löschkandidat|Grund für Löschung}}&amp;lt;/nowiki&amp;gt;&#039;&#039; markieren, anderenfalls bitte an passender Stelle neu verwenden. --[[Benutzer:Ph1959de|Peter]] ([[Benutzer Diskussion:Ph1959de|Diskussion]]) 19:04, 12. Jan. 2026 (CET)&lt;/div&gt;</summary>
		<author><name>Ph1959de</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=Wattpilot&amp;diff=40723</id>
		<title>Wattpilot</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=Wattpilot&amp;diff=40723"/>
		<updated>2026-01-12T12:23:01Z</updated>

		<summary type="html">&lt;p&gt;Ph1959de: Hinweis auf Websocket-Einbindung&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Infobox Modul&lt;br /&gt;
|ModPurpose=Überwachung und Steuerung von Fronius Wattpilot Wallboxen&lt;br /&gt;
|ModType=x&lt;br /&gt;
|ModCmdRef=&lt;br /&gt;
|ModForumArea=Wallboxen und E-Fahrzeuge&lt;br /&gt;
|ModFTopic=110282&lt;br /&gt;
|ModTechName=72_Wattpilot.pm&lt;br /&gt;
|ModOwner=Blablubblaber ({{Link2FU|14511|Forum}})&lt;br /&gt;
}}&lt;br /&gt;
Das Modul [[Wattpilot]] dient zur Überwachung und Steuerung von Fronius Wattpilot Wallboxen.&lt;br /&gt;
&lt;br /&gt;
Alternativ kann auch die auf [[Websocket#Fronius_Wattpilot|dieser Seite]] beschriebene Anbindung mit den gezeigten Code Snippets über Websocket verwendet werden.&lt;br /&gt;
&lt;br /&gt;
== Quelle und Dokumentation ==&lt;br /&gt;
Das Modul ist über die [https://github.com/DennisGrame/FHEM_Modul_Fronius_Wattpilot Github Seite des Autors] verfügbar.&lt;br /&gt;
&lt;br /&gt;
== Links ==&lt;br /&gt;
* [https://github.com/DennisGrame/FHEM_Modul_Fronius_Wattpilot Github Seite zum Modul/Projekt]&lt;br /&gt;
* Website des Wallbox-Herstellers: [https://www.fronius.com/de-de/germany/solarenergie/eigenheim/produkte-und-loesungen/e-mobilitaet Fronius]&lt;br /&gt;
 &lt;br /&gt;
[[Kategorie:Wallboxen]]&lt;/div&gt;</summary>
		<author><name>Ph1959de</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=Wallboxen_%C3%9Cbersicht&amp;diff=40722</id>
		<title>Wallboxen Übersicht</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=Wallboxen_%C3%9Cbersicht&amp;diff=40722"/>
		<updated>2026-01-12T12:20:04Z</updated>

		<summary type="html">&lt;p&gt;Ph1959de: Fronius-Anbindung präzisiert&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Diese Seite gibt eine tabellarische Übersicht über die Eckdaten verschiedener Wallboxen.&lt;br /&gt;
&lt;br /&gt;
{|  class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
!rowspan=&amp;quot;2&amp;quot;|Hersteller!!rowspan=&amp;quot;2&amp;quot;|Typ!!rowspan=&amp;quot;2&amp;quot;|Preis in € ca.!!colspan=&amp;quot;2&amp;quot;|Ladeleistung kW!!rowspan=&amp;quot;2&amp;quot;|FI intern!!rowspan=&amp;quot;2&amp;quot;|Zähler!!rowspan=&amp;quot;2&amp;quot;|Kabel o. Dose!!rowspan=&amp;quot;2&amp;quot;|Netzwerk!!rowspan=&amp;quot;2&amp;quot;|Anbindung FHEM!!rowspan=&amp;quot;2&amp;quot;|Betrieb!!rowspan=&amp;quot;2&amp;quot;|Zugang Kontrolle!!rowspan=&amp;quot;2&amp;quot;|Datum&lt;br /&gt;
|- &lt;br /&gt;
!1-phasig!!3-phasig&lt;br /&gt;
|-&lt;br /&gt;
|rowspan=&amp;quot;2&amp;quot;|ABL       ||eMH1               ||  200,00       ||    || 11 ||rowspan=&amp;quot;2&amp;quot;| DC+AC ||rowspan=&amp;quot;2&amp;quot;| MID   || Kabel    ||    ||        ||&lt;br /&gt;
|-&lt;br /&gt;
|eMH2               ||  900,00       ||    || 22  || opt. Kabel   ||    ||        ||&lt;br /&gt;
|-&lt;br /&gt;
|cfos-emobility || Power Brain Wallbox || 549,00 || 3,7 || 11 || DC || ja || Kabel || WLAN || Modbus TCP || max 45°C || App oder RFID || Dez. 2023 {{Link2Forum|Topic=136113|Message=1298136|LinkText=(F)}}&lt;br /&gt;
|-&lt;br /&gt;
|E.ON     || Drive vBox smart 11|| 490,00    ||3,7 ||1,4-11|| DC  ||nein    ||opt. Kabel 4,5m||WLAN und LAN, opt. RS485||Modbus TCP  ||max 55°C||RFID ||Dez. 2023&lt;br /&gt;
|-&lt;br /&gt;
|E.ON     || Drive vBox pro 22|| 329,00    ||7,4 ||bis 22|| DC  ||MID    ||mit Kabel 6,5m||WLAN und LAN, LTE||Modbus TCP  ||max 55°C||RFID ||März. 2025&lt;br /&gt;
|-&lt;br /&gt;
|Fronius  || Wattpilot Home 11J || 770,00    ||3,7||11    || DC  ||nein    ||Dose           ||WLAN                    || WebSocket ([[Websocket#Fronius_Wattpilot|Beispielcode]]), Modul ([[Wattpilot]]) ||max 40°C|| App||Dez. 2023&lt;br /&gt;
|-&lt;br /&gt;
|go-e     || eCharger Gemini    ||  550,00   ||3,7*  ||11    ||DC  ||MID      ||Dose           || WLAN                   ||REST, MQTT, Modbus TCP, [[GoE Charger|HTTPMOD]], [[GoECharger|Modul 46_GoECharger.pm]] (veraltetes API V1)  ||max. 40°C||RFID,App||Dez. 2023&lt;br /&gt;
|-&lt;br /&gt;
|Hardy Barth||eCHARGE cPµ2 PRO  || 750,00    ||    ||1,4-11||ja  ||MID extern||Kabel 5m       ||    ||        || || ||Dez. 2023&lt;br /&gt;
|-&lt;br /&gt;
|Heidelberg|| Energy Control 11kW || 250,00 zuzgl. wbec Modul 100,00    ||3,7 *||11    ||DC ||ja||Kabel  5m         ||WLAN+LAN mit wbec-Modul                 || REST, MQTT, Modbus TCP  mit wbec-Modul, Lademanagement         || || Browser ||Dez. 2023 {{Link2Forum|Topic=136113|Message=1298136|LinkText=(F)}}&lt;br /&gt;
|-&lt;br /&gt;
|rowspan=&amp;quot;2&amp;quot;|Elli = VW || ID.Charger Connect||   800,00     ||rowspan=&amp;quot;2&amp;quot;|7,4 ||rowspan=&amp;quot;2&amp;quot;|11   ||rowspan=&amp;quot;2&amp;quot;|ja  ||nein    ||rowspan=&amp;quot;2&amp;quot;|Kabel ab 4,5m ||rowspan=&amp;quot;2&amp;quot;|WLAN, LAN, LTE, opt. RS485   ||rowspan=&amp;quot;2&amp;quot;|Software evcc auf externem Server bildet EEBUS auf MQTT ab. EEBUS-Interface für FHEM wäre wünschenswert   ||rowspan=&amp;quot;2&amp;quot;|max. 40°C||rowspan=&amp;quot;2&amp;quot;|RFID,App||rowspan=&amp;quot;2&amp;quot;|Dez. 2023&lt;br /&gt;
|-&lt;br /&gt;
|ID.Charger Pro    || 1050,00     ||ja MID &lt;br /&gt;
|-&lt;br /&gt;
|KEBA     ||KeContact P30 a-series||  200,00    ||    ||1,4-11||ja  ||nein   ||Kabel 4m       ||LAN ||Modbus TCP || || ||Dez. 2023&lt;br /&gt;
|-&lt;br /&gt;
|rowspan=&amp;quot;2&amp;quot;|OpenWB   ||OpenWB series2 Standard || 1100,00  ||rowspan=&amp;quot;2&amp;quot;|    7,4|| rowspan=&amp;quot;2&amp;quot; |11 oder 22||rowspan=&amp;quot;2&amp;quot;|DC  ||  ja|| rowspan=&amp;quot;2&amp;quot; |Kabel ab 3m, optional mit Buchse (custom) || rowspan=&amp;quot;2&amp;quot; |LAN, WLAN || rowspan=&amp;quot;2&amp;quot; |REST, MQTT, Modbus via Adapter&lt;br /&gt;
Lademanagement umfangreich, siehe [[OpenWB#Komplexe Anbindung]]&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot; |max 45°C ||        RFID (opt)&lt;br /&gt;
PIN (opt, bei display)&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot; |Dez. 2023&lt;br /&gt;
|-&lt;br /&gt;
|OpenWB PRO            || 2100,00  &lt;br /&gt;
|-&lt;br /&gt;
|Vestel   || EVC04-E11-W-C      || colspan=&amp;quot;11&amp;quot; |Baugleich mit E.ON Drive vBox smart 11&lt;br /&gt;
|-&lt;br /&gt;
|Wallbox  || Pulsar Plus        || 700,00    ||    ||11    ||DC  ||ja, nicht MID|| || WLAN, Bluetooth|| || ||RFID, App       ||Dez.2023&lt;br /&gt;
|- &lt;br /&gt;
! colspan=&amp;quot;13&amp;quot; style=&amp;quot;text-align:left&amp;quot; | &lt;br /&gt;
Bemerkungen: &lt;br /&gt;
* Ein * in der Spalte &amp;quot;Ladeleistung 1-phasig&amp;quot; bedeutet, dass die Phasenumschaltung automatisch erfolgt.&lt;br /&gt;
* FI = Fehlerstromschutzschalter für Gleichstrom (DC) bzw. Typ A für Wechselstrom (AC).&lt;br /&gt;
* Ein interner Zähler kann geeicht sein (nötig nach deutschem Recht für Abrechungen), &amp;quot;nur&amp;quot; MID-konform (internationaler Standard) oder irgendwas...&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Wallboxen]]&lt;/div&gt;</summary>
		<author><name>Ph1959de</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=Wallboxen_%C3%9Cbersicht&amp;diff=40721</id>
		<title>Wallboxen Übersicht</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=Wallboxen_%C3%9Cbersicht&amp;diff=40721"/>
		<updated>2026-01-12T12:15:09Z</updated>

		<summary type="html">&lt;p&gt;Ph1959de: Legende ans Tabellenende verschoben; einige Module / Beschreibungen verlinkt&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Diese Seite gibt eine tabellarische Übersicht über die Eckdaten verschiedener Wallboxen.&lt;br /&gt;
&lt;br /&gt;
{|  class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
!rowspan=&amp;quot;2&amp;quot;|Hersteller!!rowspan=&amp;quot;2&amp;quot;|Typ!!rowspan=&amp;quot;2&amp;quot;|Preis in € ca.!!colspan=&amp;quot;2&amp;quot;|Ladeleistung kW!!rowspan=&amp;quot;2&amp;quot;|FI intern!!rowspan=&amp;quot;2&amp;quot;|Zähler!!rowspan=&amp;quot;2&amp;quot;|Kabel o. Dose!!rowspan=&amp;quot;2&amp;quot;|Netzwerk!!rowspan=&amp;quot;2&amp;quot;|Anbindung FHEM!!rowspan=&amp;quot;2&amp;quot;|Betrieb!!rowspan=&amp;quot;2&amp;quot;|Zugang Kontrolle!!rowspan=&amp;quot;2&amp;quot;|Datum&lt;br /&gt;
|- &lt;br /&gt;
!1-phasig!!3-phasig&lt;br /&gt;
|-&lt;br /&gt;
|rowspan=&amp;quot;2&amp;quot;|ABL       ||eMH1               ||  200,00       ||    || 11 ||rowspan=&amp;quot;2&amp;quot;| DC+AC ||rowspan=&amp;quot;2&amp;quot;| MID   || Kabel    ||    ||        ||&lt;br /&gt;
|-&lt;br /&gt;
|eMH2               ||  900,00       ||    || 22  || opt. Kabel   ||    ||        ||&lt;br /&gt;
|-&lt;br /&gt;
|cfos-emobility || Power Brain Wallbox || 549,00 || 3,7 || 11 || DC || ja || Kabel || WLAN || Modbus TCP || max 45°C || App oder RFID || Dez. 2023 {{Link2Forum|Topic=136113|Message=1298136|LinkText=(F)}}&lt;br /&gt;
|-&lt;br /&gt;
|E.ON     || Drive vBox smart 11|| 490,00    ||3,7 ||1,4-11|| DC  ||nein    ||opt. Kabel 4,5m||WLAN und LAN, opt. RS485||Modbus TCP  ||max 55°C||RFID ||Dez. 2023&lt;br /&gt;
|-&lt;br /&gt;
|E.ON     || Drive vBox pro 22|| 329,00    ||7,4 ||bis 22|| DC  ||MID    ||mit Kabel 6,5m||WLAN und LAN, LTE||Modbus TCP  ||max 55°C||RFID ||März. 2025&lt;br /&gt;
|-&lt;br /&gt;
|Fronius  || Wattpilot Home 11J || 770,00    ||3,7||11    || DC  ||nein    ||Dose           ||WLAN                    || WebSocket (Modul [[Wattpilot]]) ||max 40°C|| App||Dez. 2023&lt;br /&gt;
|-&lt;br /&gt;
|go-e     || eCharger Gemini    ||  550,00   ||3,7*  ||11    ||DC  ||MID      ||Dose           || WLAN                   ||REST, MQTT, Modbus TCP, [[GoE Charger|HTTPMOD]], [[GoECharger|Modul 46_GoECharger.pm]] (veraltetes API V1)  ||max. 40°C||RFID,App||Dez. 2023&lt;br /&gt;
|-&lt;br /&gt;
|Hardy Barth||eCHARGE cPµ2 PRO  || 750,00    ||    ||1,4-11||ja  ||MID extern||Kabel 5m       ||    ||        || || ||Dez. 2023&lt;br /&gt;
|-&lt;br /&gt;
|Heidelberg|| Energy Control 11kW || 250,00 zuzgl. wbec Modul 100,00    ||3,7 *||11    ||DC ||ja||Kabel  5m         ||WLAN+LAN mit wbec-Modul                 || REST, MQTT, Modbus TCP  mit wbec-Modul, Lademanagement         || || Browser ||Dez. 2023 {{Link2Forum|Topic=136113|Message=1298136|LinkText=(F)}}&lt;br /&gt;
|-&lt;br /&gt;
|rowspan=&amp;quot;2&amp;quot;|Elli = VW || ID.Charger Connect||   800,00     ||rowspan=&amp;quot;2&amp;quot;|7,4 ||rowspan=&amp;quot;2&amp;quot;|11   ||rowspan=&amp;quot;2&amp;quot;|ja  ||nein    ||rowspan=&amp;quot;2&amp;quot;|Kabel ab 4,5m ||rowspan=&amp;quot;2&amp;quot;|WLAN, LAN, LTE, opt. RS485   ||rowspan=&amp;quot;2&amp;quot;|Software evcc auf externem Server bildet EEBUS auf MQTT ab. EEBUS-Interface für FHEM wäre wünschenswert   ||rowspan=&amp;quot;2&amp;quot;|max. 40°C||rowspan=&amp;quot;2&amp;quot;|RFID,App||rowspan=&amp;quot;2&amp;quot;|Dez. 2023&lt;br /&gt;
|-&lt;br /&gt;
|ID.Charger Pro    || 1050,00     ||ja MID &lt;br /&gt;
|-&lt;br /&gt;
|KEBA     ||KeContact P30 a-series||  200,00    ||    ||1,4-11||ja  ||nein   ||Kabel 4m       ||LAN ||Modbus TCP || || ||Dez. 2023&lt;br /&gt;
|-&lt;br /&gt;
|rowspan=&amp;quot;2&amp;quot;|OpenWB   ||OpenWB series2 Standard || 1100,00  ||rowspan=&amp;quot;2&amp;quot;|    7,4|| rowspan=&amp;quot;2&amp;quot; |11 oder 22||rowspan=&amp;quot;2&amp;quot;|DC  ||  ja|| rowspan=&amp;quot;2&amp;quot; |Kabel ab 3m, optional mit Buchse (custom) || rowspan=&amp;quot;2&amp;quot; |LAN, WLAN || rowspan=&amp;quot;2&amp;quot; |REST, MQTT, Modbus via Adapter&lt;br /&gt;
Lademanagement umfangreich, siehe [[OpenWB#Komplexe Anbindung]]&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot; |max 45°C ||        RFID (opt)&lt;br /&gt;
PIN (opt, bei display)&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot; |Dez. 2023&lt;br /&gt;
|-&lt;br /&gt;
|OpenWB PRO            || 2100,00  &lt;br /&gt;
|-&lt;br /&gt;
|Vestel   || EVC04-E11-W-C      || colspan=&amp;quot;11&amp;quot; |Baugleich mit E.ON Drive vBox smart 11&lt;br /&gt;
|-&lt;br /&gt;
|Wallbox  || Pulsar Plus        || 700,00    ||    ||11    ||DC  ||ja, nicht MID|| || WLAN, Bluetooth|| || ||RFID, App       ||Dez.2023&lt;br /&gt;
|- &lt;br /&gt;
! colspan=&amp;quot;13&amp;quot; style=&amp;quot;text-align:left&amp;quot; | &lt;br /&gt;
Bemerkungen: &lt;br /&gt;
* Ein * in der Spalte &amp;quot;Ladeleistung 1-phasig&amp;quot; bedeutet, dass die Phasenumschaltung automatisch erfolgt.&lt;br /&gt;
* FI = Fehlerstromschutzschalter für Gleichstrom (DC) bzw. Typ A für Wechselstrom (AC).&lt;br /&gt;
* Ein interner Zähler kann geeicht sein (nötig nach deutschem Recht für Abrechungen), &amp;quot;nur&amp;quot; MID-konform (internationaler Standard) oder irgendwas...&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Wallboxen]]&lt;/div&gt;</summary>
		<author><name>Ph1959de</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=Wattpilot&amp;diff=40720</id>
		<title>Wattpilot</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=Wattpilot&amp;diff=40720"/>
		<updated>2026-01-12T12:13:49Z</updated>

		<summary type="html">&lt;p&gt;Ph1959de: Modul&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Infobox Modul&lt;br /&gt;
|ModPurpose=Überwachung und Steuerung von Fronius Wattpilot Wallboxen&lt;br /&gt;
|ModType=x&lt;br /&gt;
|ModCmdRef=&lt;br /&gt;
|ModForumArea=Wallboxen und E-Fahrzeuge&lt;br /&gt;
|ModFTopic=110282&lt;br /&gt;
|ModTechName=72_Wattpilot.pm&lt;br /&gt;
|ModOwner=Blablubblaber ({{Link2FU|14511|Forum}})&lt;br /&gt;
}}&lt;br /&gt;
Das Modul [[Wattpilot]] dient zur Überwachung und Steuerung von Fronius Wattpilot Wallboxen.&lt;br /&gt;
&lt;br /&gt;
== Quelle und Dokumentation ==&lt;br /&gt;
Das Modul ist über die [https://github.com/DennisGrame/FHEM_Modul_Fronius_Wattpilot Github Seite des Autors] verfügbar.&lt;br /&gt;
&lt;br /&gt;
== Links ==&lt;br /&gt;
* [https://github.com/DennisGrame/FHEM_Modul_Fronius_Wattpilot Github Seite zum Modul/Projekt]&lt;br /&gt;
* Website des Wallbox-Herstellers: [https://www.fronius.com/de-de/germany/solarenergie/eigenheim/produkte-und-loesungen/e-mobilitaet Fronius]&lt;br /&gt;
 &lt;br /&gt;
[[Kategorie:Wallboxen]]&lt;/div&gt;</summary>
		<author><name>Ph1959de</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=GoECharger&amp;diff=40719</id>
		<title>GoECharger</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=GoECharger&amp;diff=40719"/>
		<updated>2026-01-12T12:13:36Z</updated>

		<summary type="html">&lt;p&gt;Ph1959de: Modulseite angelegt&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Infobox Modul&lt;br /&gt;
|ModPurpose=Überwachung und Steuerung von Wallboxen des Herstellers [https://go-e.com/de-de go-e]&lt;br /&gt;
|ModType=x&lt;br /&gt;
|ModCmdRef=&lt;br /&gt;
|ModForumArea=Wallboxen und E-Fahrzeuge&lt;br /&gt;
|ModFTopic=143571&lt;br /&gt;
|ModTechName=72_Wattpilot.pm&lt;br /&gt;
|ModOwner=LR66 ({{Link2FU|4213|Forum}})&lt;br /&gt;
}}&lt;br /&gt;
Das Modul [[GoECharger]] dient zur Überwachung und Steuerung von go-e Wallboxen.&lt;br /&gt;
&lt;br /&gt;
== Status == &lt;br /&gt;
Das Modul ist (Stand Januar 2026) veraltet und wird allem Anschein nach nicht mehr gepflegt.&lt;br /&gt;
&lt;br /&gt;
Alternativ gibt es die Beschreibung [[GoE Charger]] zur Einbindung der Wallbox in FHEM über [[HTTPMOD]]. &lt;br /&gt;
&lt;br /&gt;
== Quelle und Dokumentation ==&lt;br /&gt;
Das Modul ist über die [https://github.com/LuRhe/fhem-46_GoECharger Github Seite des Autors] verfügbar.&lt;br /&gt;
&lt;br /&gt;
== Links ==&lt;br /&gt;
* [https://github.com/LuRhe/fhem-46_GoECharger Github Seite zum Modul/Projekt]&lt;br /&gt;
* Website des Wallbox-Herstellers: [https://go-e.com/de-de go-e]&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Wallboxen]]&lt;/div&gt;</summary>
		<author><name>Ph1959de</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=Vorlage:Infobox_Modul&amp;diff=40658</id>
		<title>Vorlage:Infobox Modul</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=Vorlage:Infobox_Modul&amp;diff=40658"/>
		<updated>2026-01-05T15:24:07Z</updated>

		<summary type="html">&lt;p&gt;Ph1959de: ... give it a fine border&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;onlyinclude&amp;gt;&lt;br /&gt;
{| class=&amp;quot;float-right infobox toccolours&amp;quot; style=&amp;quot;margin: 0 0 1em 1em; float:right; border-style: solid; border-width: 1px&amp;quot; width=&amp;quot;300&amp;quot;&lt;br /&gt;
! colspan=&amp;quot;2&amp;quot; style=&amp;quot;background-color:#EFEFFF&amp;quot; | {{#if: {{{ModCmdRef|}}}|{{{ModCmdRef}}}|{{PAGENAME}} }}&lt;br /&gt;
|- style=&amp;quot;background-color:#EFEFFF&amp;quot;&lt;br /&gt;
! colspan=&amp;quot;2&amp;quot; | Zweck / Funktion&lt;br /&gt;
|- style=&amp;quot;background-color:#FFFFFF&amp;quot;&lt;br /&gt;
| colspan=&amp;quot;2&amp;quot; | {{{ModPurpose}}}&lt;br /&gt;
|- style=&amp;quot;background-color:#EFEFFF&amp;quot;&lt;br /&gt;
! colspan=&amp;quot;2&amp;quot; | Allgemein&lt;br /&gt;
|- bgcolor=&amp;quot;#FFFFFF&amp;quot;&lt;br /&gt;
&amp;lt;!-- | Typ || {{{ModType}}} .................................. --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Vordefinierte Modultypen / &amp;quot;AutoKategorie&amp;quot; .............. --&amp;gt;&lt;br /&gt;
| Typ &lt;br /&gt;
| {{#switch: &lt;br /&gt;
{{{ModType}}}           &amp;lt;!-- &lt;br /&gt;
--&amp;gt;| cmd = Befehl        [[Kategorie:FHEM Befehl]]           &amp;lt;!--&lt;br /&gt;
--&amp;gt;| u = Utilities       [[Kategorie:FHEM Utilities]]        &amp;lt;!--&lt;br /&gt;
--&amp;gt;| contrib = [https://svn.fhem.de/trac/browser/trunk/fhem/contrib Contrib]   [[Kategorie:Modul (Contrib)]]      &amp;lt;!--&lt;br /&gt;
--&amp;gt;| d = Gerätemodul     [[Kategorie:Gerätemodul]]          &amp;lt;!--&lt;br /&gt;
--&amp;gt;| h = Hilfsmodul      [[Kategorie:Hilfsmodul]]           &amp;lt;!--&lt;br /&gt;
--&amp;gt;| x = Inoffiziell     [[Kategorie:Modul (Inoffiziell)]]  &amp;lt;!--&lt;br /&gt;
--&amp;gt;| #default = undefiniert }}&lt;br /&gt;
&amp;lt;!--           Ende Modultypen / &amp;quot;AutoKategorie&amp;quot; ............. --&amp;gt;&lt;br /&gt;
|- style=&amp;quot;background-color:#EFEFFF&amp;quot;&lt;br /&gt;
! colspan=&amp;quot;2&amp;quot; | Details&lt;br /&gt;
|- bgcolor=&amp;quot;#FFFFFF&amp;quot;&lt;br /&gt;
&amp;lt;!--           Dokumentation  ................................ --&amp;gt;&lt;br /&gt;
| Dokumentation &lt;br /&gt;
| {{#switch:&lt;br /&gt;
{{{ModType}}}           &amp;lt;!-- &lt;br /&gt;
--&amp;gt;| x|u = {{#if: {{{ModFTopic|}}}          |    &amp;lt;!-- ModFTopic gesetzt, dann&lt;br /&gt;
--&amp;gt; {{Link2Forum|Topic={{{ModFTopic}}}}}  |    &amp;lt;!-- Link auf Forentopic generieren&lt;br /&gt;
--&amp;gt; siehe Forum }}                         |   &amp;lt;!-- nicht gesetzt; default text &lt;br /&gt;
--&amp;gt; contrib = {{#if: {{{ModFTopic|}}}     |    &amp;lt;!--&lt;br /&gt;
--&amp;gt; {{Link2Forum|Topic={{{ModFTopic}}}}}  |    &amp;lt;!--&lt;br /&gt;
--&amp;gt; siehe Forum   }}                       |   &amp;lt;!-- &lt;br /&gt;
--&amp;gt; cmd|d|h =  {{#if: {{{ModCmdRef|}}}   &amp;lt;!-- &lt;br /&gt;
ModName=y --&amp;gt;| {{Link2CmdRef|Anker={{{ModCmdRef}}}|Label=EN}} / {{Link2CmdRef|Anker={{{ModCmdRef}}}|Label=DE|Lang=de}} &amp;lt;!-- &lt;br /&gt;
ModName=p --&amp;gt;| {{Link2CmdRef|Anker={{PAGENAME}}|Label=EN}} / {{Link2CmdRef|Anker={{PAGENAME}}|Label=DE|Lang=de}} &amp;lt;!-- endif &#039;Modulname angegeben&#039; &lt;br /&gt;
--&amp;gt;}}{{#if: {{{ModFTopic|}}} | &amp;lt;br&amp;gt;{{Link2Forum|Topic={{{ModFTopic}}}}}|}}         |                          &amp;lt;!-- end_if CmdRef angegeben &lt;br /&gt;
--&amp;gt;| #default = ModUndef }}&lt;br /&gt;
&amp;lt;!--           ForumArea ..................................... --&amp;gt;&lt;br /&gt;
|- bgcolor=&amp;quot;#FFFFFF&amp;quot;&lt;br /&gt;
{{#if: {{{ModForumArea|}}} |&lt;br /&gt;
{{!}} Support (Forum) {{!!}} {{Link2Forum|Area={{{ModForumArea|}}}}}&lt;br /&gt;
}}&lt;br /&gt;
&amp;lt;!--           Modul - technischer Name (nn_naMe.pm) ......... --&amp;gt;&lt;br /&gt;
|- bgcolor=&amp;quot;#FFFFFF&amp;quot;&lt;br /&gt;
| Modulname || {{{ModTechName}}}&lt;br /&gt;
&amp;lt;!--           Ersteller (Maintainer) ........................ --&amp;gt;&lt;br /&gt;
|- bgcolor=&amp;quot;#FFFFFF&amp;quot;&lt;br /&gt;
| [http://fhem.de/MAINTAINER.txt Ersteller] || {{{ModOwner}}}&lt;br /&gt;
&amp;lt;!-- commandref disclaimer --&amp;gt;&lt;br /&gt;
|- style=&amp;quot;background-color:#7FFFD4&amp;quot;&lt;br /&gt;
| colspan=&amp;quot;2&amp;quot; | &#039;&#039;&#039;Wichtig&#039;&#039;&#039;: sofern vorhanden, gilt im Zweifel &#039;&#039;&#039;immer&#039;&#039;&#039; die (englische) Beschreibung in der &#039;&#039;{{Link2CmdRef|Anker=#|Label=commandref}}&#039;&#039;!&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/onlyinclude&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;noinclude&amp;gt;&lt;br /&gt;
__NOTOC__&lt;br /&gt;
&lt;br /&gt;
== Dokumentation ==&lt;br /&gt;
Benutzung dieser Vorlage: &amp;lt;nowiki&amp;gt;{{Infobox Modul|...}}&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Diese Vorlage dient zur Zusammenfassung von Schlüsseldaten zu einem Modul und sollte auf der Beschreibungsseite des Moduls verwendet werden.&lt;br /&gt;
&lt;br /&gt;
=== Aufrufparameter ===&lt;br /&gt;
;ModPurpose&lt;br /&gt;
:Kurzbeschreibung der Funktion / des Zwecks dieses Moduls&lt;br /&gt;
;ModType&lt;br /&gt;
:Modultyp, ausgehend von der Einteilung in der commandref: &amp;lt;br /&amp;gt;&#039;&#039;&#039;cmd&#039;&#039;&#039;=&amp;quot;fhem commands&amp;quot; (FHEM-Befehle) &amp;lt;br /&amp;gt;&#039;&#039;&#039;contrib&#039;&#039;&#039;=&amp;quot;Contrib&amp;quot; Inoffizielles Modul, das aus dem Contrib-Unterverzeichnis des FHEM-Repositories geladen werden muss; externer Link auf das Verzeichnis wird automatisch generiert. &amp;lt;br /&amp;gt;&#039;&#039;&#039;d&#039;&#039;&#039;=&amp;quot;devices&amp;quot; (Geräte) &amp;lt;br /&amp;gt;&#039;&#039;&#039;h&#039;&#039;&#039;=&amp;quot;helper modules&amp;quot; (Hilfs(Erweiterungs-)module) &amp;lt;br /&amp;gt;&#039;&#039;&#039;u&#039;&#039;&#039;=&amp;quot;Utilities&amp;quot; (Sammlung von Hilfsfunktionen für bestimmte Einsatzbereiche) &amp;lt;br /&amp;gt;&#039;&#039;&#039;x&#039;&#039;&#039;=&amp;quot;experimentell&amp;quot; (Inoffiziell; noch nicht Teil des Standard-FHEM, aber auch nicht im &amp;quot;contrib&amp;quot; Unterverzeichnis bei sourceforge abgelegt; derartige Module sind in der Regel über den zugehörigen Diskussionsfaden im [forum.fhem.de FHEM Forum] zu beziehen).  &amp;lt;br /&amp;gt;Abhängig vom gewählten Modultyp wird ein Kategorieeintrag für &#039;&#039;FHEM Befehl&#039;&#039;, &#039;&#039;FHEM Utilities&#039;&#039;, &#039;&#039;Gerätemodul&#039;&#039;, &#039;&#039;Hilfsmodul&#039;&#039;, &#039;&#039;Modul (Contrib)&#039;&#039; oder &#039;&#039;Modul (Inoffiziell)&#039;&#039; erzeugt. &lt;br /&gt;
{{Randnotiz|RNTyp=y|RNText=Entspricht der Seitentitel nicht dem Modulnamen, müssen die folgenden zusätzlichen Maßnahmen ergriffen werden:&lt;br /&gt;
* An den Beginn der Modul-Seite &#039;&#039;&#039;muss&#039;&#039;&#039; die Anweisung &amp;lt;nowiki&amp;gt;{{SEITENTITEL:naMeInRichtigerSchreibweise}}&amp;lt;/nowiki&amp;gt; eingefügt werden&lt;br /&gt;
* Der Parameter &#039;&#039;ModCmdRef=naMeInRichtigerSchreibweise&#039;&#039; &#039;&#039;&#039;muss&#039;&#039;&#039; für die &#039;&#039;Infobox Modul&#039;&#039; spezifiziert werden.&lt;br /&gt;
}}&lt;br /&gt;
;ModCmdRef&lt;br /&gt;
:optionaler Parameter (für die Bestimmung des Wertes für die Zeile &#039;&#039;Dokumentation&#039;&#039;). Falls nicht angegeben, wird aus dem Seitentitel ein Link auf den entsprechenden Abschnitt der commandref generiert. Weicht der Seitentitel von der Bezeichnung des Abschnitts in der commandref ab (z.&amp;amp;nbsp;B., weil der Modulname &amp;quot;eigentlich&amp;quot; mit einem Kleinbuchstaben beginnt), kann hier der Name in der korrekten Schreibweise spezifiziert werden. &amp;lt;br&amp;gt;Für ModType=&#039;&#039;x&#039;&#039; und &#039;&#039;contrib&#039;&#039; wird kein Link auf die commandref erzeugt (da der entsprechende Abschnitt noch nicht existieren dürfte), sondern ein schlichter Verweis auf das FHEM Forum. Wenn Parameter &#039;&#039;&#039;&#039;&#039;ModFTopic&#039;&#039;&#039;&#039;&#039; spezifiziert ist, wird ein Link auf diesen Forenthread generiert. &amp;lt;br&amp;gt;Ist &#039;&#039;&#039;&#039;&#039;ModFTopic&#039;&#039;&#039;&#039;&#039; für &amp;quot;offizielle&amp;quot; Module spezifiziert, wird zusätzlich der Link auf den entsprechenden Forenthread generiert.&lt;br /&gt;
;ModFTopic&lt;br /&gt;
:optionaler Parameter. Forendiskussion zu diesem Modul; numerischer Wert, der zu einem Verweis auf den ersten Beitrag des Diskussionsfadens (Threads, Topics) führt.&lt;br /&gt;
{{Randnotiz|RNTyp=y|RNText=Beispiel für das &#039;&#039;ModOwner&#039;&#039;-Feld:&lt;br /&gt;
:&amp;lt;code&amp;gt;Peter/ph1959de (&amp;lt;nowiki&amp;gt;{{Link2FU|73|Forum}}/[[Benutzer Diskussion:ph1959de|Wiki]]&amp;lt;/nowiki&amp;gt;)&amp;lt;/code&amp;gt;&lt;br /&gt;
sieht dann so aus:&lt;br /&gt;
:Peter/ph1959de ({{Link2FU|73|Forum}}/[[Benutzer Diskussion:ph1959de|Wiki]])&lt;br /&gt;
}}&lt;br /&gt;
; ModForumArea&lt;br /&gt;
: Name des Forenbereichs, in dem Supportanfragen zu dem Modul gestellt werden können bzw. sollen. Die Liste der Zuordnung von Modul zu Forenbereich ist in der [http://fhem.de/MAINTAINER.txt Liste der Maintainer] enthalten. Der Text wird automatisch in einen Link auf den Forenbereich umgesetzt.&lt;br /&gt;
;ModTechName&lt;br /&gt;
:Technischer Name des Moduls (also z.&amp;amp;nbsp;B. &amp;lt;code&amp;gt;01_FHEMWEB.pm&amp;lt;/code&amp;gt;; siehe [http://fhem.de/MAINTAINER.txt Liste der Maintainer])&lt;br /&gt;
;ModOwner&lt;br /&gt;
:Name des Modulerstellers bzw. -betreuers (siehe [http://fhem.de/MAINTAINER.txt Liste der Maintainer])&lt;br /&gt;
&lt;br /&gt;
=== Felder der Infobox ===&lt;br /&gt;
;Titelzeile&lt;br /&gt;
:Im Regelfall Seitenname=Modulname=Titelzeile. &amp;lt;br&amp;gt;Stimmt der Modulname nicht mit dem Seitentitel überein, muss der Parameter &#039;&#039;ModCmdRef&#039;&#039; angegeben sein und wird hier verwendet.&lt;br /&gt;
;Zweck / Funktion&lt;br /&gt;
:Dieses Feld wird direkt aus dem Wert des Parameters &#039;&#039;ModPurpose&#039;&#039; gefüllt.&lt;br /&gt;
;Typ&lt;br /&gt;
:Modultyp, abgeleitet aus dem Parameter &#039;&#039;ModType&#039;&#039;.&lt;br /&gt;
;Dokumentation&lt;br /&gt;
:Verweis auf die Dokumentation zu diesem Modul; das besteht &lt;br /&gt;
:*bei &amp;quot;offiziellen&amp;quot; Modulen aus den automatisch generierten Links auf den entsprechenden Abschnitt der commandref und, optional/zusätzlich, wenn der Parameter &#039;&#039;ModFTopic&#039;&#039; angegeben ist, einem Link auf den spezifizierten Forenthread&lt;br /&gt;
:*bei &amp;quot;inoffiziellen&amp;quot; oder &amp;quot;contrib&amp;quot; Modulen aus einem Link auf einen Forenthread (sofern &#039;&#039;ModFTopic&#039;&#039; angegeben ist) oder dem schlichten Hinweis auf das Forum.&lt;br /&gt;
;Support (Forum)&lt;br /&gt;
:Ein Link auf den Forenbereich, in dem Fragen und Fehlerberichte zu diesem Modul eingestellt werden sollen, das jedoch nur, wenn &#039;&#039;ModForumArea&#039;&#039; angegeben ist.&lt;br /&gt;
;Modulname&lt;br /&gt;
:Der &amp;quot;technische&amp;quot; Name des Moduls, also z.B. &#039;&#039;01_FHEMWEB.pm&#039;&#039; &lt;br /&gt;
;Ersteller&lt;br /&gt;
:Der Name (Forum/Wiki/...) des Modulverantwortlichen.&lt;br /&gt;
&lt;br /&gt;
=== Kopiervorlage ===&lt;br /&gt;
Die folgende (generelle) Schablone wird an den Anfang der Zielseite kopiert und ausgefüllt. Weitere, modultypspezifische Beispiele, sind weiter unten aufgeführt.&lt;br /&gt;
&amp;lt;pre style=&amp;quot;width:50%;&amp;quot;&amp;gt;&lt;br /&gt;
{{Infobox Modul&lt;br /&gt;
|ModPurpose=&lt;br /&gt;
|ModType=[cmd|contrib|d|h|x]&lt;br /&gt;
|ModCmdRef= &lt;br /&gt;
|ModForumArea=&lt;br /&gt;
|ModFTopic=&lt;br /&gt;
|ModTechName=&lt;br /&gt;
|ModOwner=&lt;br /&gt;
}}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Weitere kopierfähige Beispiele ==&lt;br /&gt;
=== Modultyp &amp;quot;cmd&amp;quot;, &amp;quot;d&amp;quot; oder &amp;quot;h&amp;quot; ===&lt;br /&gt;
&amp;lt;!-- &amp;lt;br clear=all&amp;gt;  Testarea  Typ d                          --&amp;gt;&lt;br /&gt;
{{Infobox Modul&lt;br /&gt;
|ModPurpose=Sinn und Zweck dieses Moduls (ein Satz)&lt;br /&gt;
|ModType=d&lt;br /&gt;
|ModForumArea=Forenbereich&lt;br /&gt;
|ModTechName=77_Device.pm&lt;br /&gt;
|ModOwner=Modulersteller&lt;br /&gt;
}}&lt;br /&gt;
Infobox - mögliche Werte für die Felder bei einem Geräte-(Device-)Modul. Stimmt der Seitenname nicht mit dem Modulnamen (wie in der commandref vorgegeben) überein, muss zusätzlich der Parameter &#039;&#039;ModCmdRef&#039;&#039; angegeben werden.&lt;br /&gt;
&amp;lt;pre style=&amp;quot;width:50%;&amp;quot;&amp;gt;&amp;lt;nowiki&amp;gt;&lt;br /&gt;
{{Infobox Modul&lt;br /&gt;
|ModPurpose=Sinn und Zweck dieses Moduls (ein Satz)&lt;br /&gt;
|ModType=d&lt;br /&gt;
|ModForumArea=Forenbereich&lt;br /&gt;
|ModTechName=77_Device.pm&lt;br /&gt;
|ModOwner=Modulersteller&lt;br /&gt;
}}&lt;br /&gt;
&amp;lt;/nowiki&amp;gt;&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
=== Modultyp &amp;quot;x&amp;quot; oder &amp;quot;contrib&amp;quot; ===&lt;br /&gt;
&amp;lt;!--    Testarea  Typ contrib                          --&amp;gt;&lt;br /&gt;
{{Infobox Modul&lt;br /&gt;
|ModPurpose=Ein Modul des Typs &amp;quot;contrib&amp;quot;&lt;br /&gt;
|ModType=contrib&lt;br /&gt;
|ModFTopic=12345&lt;br /&gt;
|ModCmdRef=ContribModul&lt;br /&gt;
|ModForumArea=Contrib&lt;br /&gt;
|ModTechName=66_Contrib.pm&lt;br /&gt;
|ModOwner=Contrib-Owner&lt;br /&gt;
}}&lt;br /&gt;
Infobox - mögliche/empfohlene Werte für die Felder bei einem inoffiziellen Modul. Stimmt der Seitenname nicht mit dem Modulnamen (wie in der commandref vorgegeben) überein, muss zusätzlich der Parameter &#039;&#039;ModCmdRef&#039;&#039; angegeben werden.&lt;br /&gt;
&amp;lt;pre style=&amp;quot;width:50%;&amp;quot;&amp;gt;&amp;lt;nowiki&amp;gt;&lt;br /&gt;
{{Infobox Modul&lt;br /&gt;
|ModPurpose=Ein Modul des Typs &amp;quot;contrib&amp;quot;&lt;br /&gt;
|ModType=contrib&lt;br /&gt;
|ModFTopic=12345&lt;br /&gt;
|ModCmdRef=ContribModul&lt;br /&gt;
|ModForumArea=Contrib&lt;br /&gt;
|ModTechName=66_Contrib.pm&lt;br /&gt;
|ModOwner=Contrib-Owner&lt;br /&gt;
}}&lt;br /&gt;
&amp;lt;/nowiki&amp;gt;&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Komplette Liste möglicher Parameter ===&lt;br /&gt;
Die folgende Kopiervorlage enthält alle Schlüsselworte ohne Angabe von Werten:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre style=&amp;quot;width:550px;&amp;quot;&amp;gt;&amp;lt;nowiki&amp;gt;&lt;br /&gt;
{{SEITENTITEL:...}}  &amp;lt;!-- Nur angeben, wenn wirklich erforderlich! --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{{Infobox Modul&lt;br /&gt;
|ModPurpose=...&lt;br /&gt;
|ModType=...&lt;br /&gt;
|ModCmdRef=...&lt;br /&gt;
|ModFTopic=...&lt;br /&gt;
|ModForumArea=...&lt;br /&gt;
|ModTechName=...&lt;br /&gt;
|ModOwner=...&lt;br /&gt;
}}&lt;br /&gt;
&amp;lt;/nowiki&amp;gt;&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;templatedata&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
	&amp;quot;params&amp;quot;: {&lt;br /&gt;
		&amp;quot;ModCmdRef&amp;quot;: {&lt;br /&gt;
			&amp;quot;label&amp;quot;: &amp;quot;Modulname&amp;quot;,&lt;br /&gt;
			&amp;quot;description&amp;quot;: &amp;quot;Sofern der Modulname vom Seitennamen abweicht (bspw. Groß-/Kleinschreibung) kann hier der korrekte Modulname angegeben werden, so wie er in der Commandref zu finden ist. Falls nicht angegeben wird der Wiki-Seitentitel als Modulname verwendet&amp;quot;,&lt;br /&gt;
			&amp;quot;type&amp;quot;: &amp;quot;string&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		&amp;quot;ModPurpose&amp;quot;: {&lt;br /&gt;
			&amp;quot;label&amp;quot;: &amp;quot;Kurzbeschreibung&amp;quot;,&lt;br /&gt;
			&amp;quot;description&amp;quot;: &amp;quot;Eine kurze Beschreibung der Funktion / Zweck des Moduls.&amp;quot;,&lt;br /&gt;
			&amp;quot;type&amp;quot;: &amp;quot;string&amp;quot;,&lt;br /&gt;
			&amp;quot;required&amp;quot;: true,&lt;br /&gt;
			&amp;quot;suggested&amp;quot;: true&lt;br /&gt;
		},&lt;br /&gt;
		&amp;quot;ModType&amp;quot;: {&lt;br /&gt;
			&amp;quot;label&amp;quot;: &amp;quot;Modul-Typ&amp;quot;,&lt;br /&gt;
			&amp;quot;description&amp;quot;: &amp;quot;Typ des Moduls (einzelner Wert), ausgehend von der Einteilung in der commandref: (\&amp;quot;d\&amp;quot; =&amp;gt; Gerätemodul, \&amp;quot;h\&amp;quot; =&amp;gt; Hilfsmodul, \&amp;quot;cmd\&amp;quot; =&amp;gt; Befehlsmodul, \&amp;quot;u\&amp;quot; =&amp;gt; Utilities, \&amp;quot;contrib\&amp;quot; =&amp;gt; Inoffizielles Modul im contrib-Verzeichnis, \&amp;quot;x\&amp;quot; =&amp;gt; experimentelles Modul)&amp;quot;,&lt;br /&gt;
			&amp;quot;example&amp;quot;: &amp;quot;d&amp;quot;,&lt;br /&gt;
			&amp;quot;required&amp;quot;: true,&lt;br /&gt;
			&amp;quot;suggested&amp;quot;: true&lt;br /&gt;
		},&lt;br /&gt;
		&amp;quot;ModFTopic&amp;quot;: {&lt;br /&gt;
			&amp;quot;label&amp;quot;: &amp;quot;Forumsbeitrag&amp;quot;,&lt;br /&gt;
			&amp;quot;description&amp;quot;: &amp;quot;Die Topic-ID zu einer Diskussion im Forum um weitere Informationen zu erhalten. Hierbei nur die Topic-ID aus dem Link angeben&amp;quot;,&lt;br /&gt;
			&amp;quot;type&amp;quot;: &amp;quot;number&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		&amp;quot;ModForumArea&amp;quot;: {&lt;br /&gt;
			&amp;quot;label&amp;quot;: &amp;quot;Forumsbereich&amp;quot;,&lt;br /&gt;
			&amp;quot;description&amp;quot;: &amp;quot;Der Name des Forumsbereich, in dem Fragen zu diesem Modul diskutiert werden sollten.&amp;quot;,&lt;br /&gt;
			&amp;quot;type&amp;quot;: &amp;quot;string&amp;quot;,&lt;br /&gt;
			&amp;quot;required&amp;quot;: true&lt;br /&gt;
		},&lt;br /&gt;
		&amp;quot;ModTechName&amp;quot;: {&lt;br /&gt;
			&amp;quot;label&amp;quot;: &amp;quot;Dateiname&amp;quot;,&lt;br /&gt;
			&amp;quot;description&amp;quot;: &amp;quot;Der vollständige Dateiname des Moduls&amp;quot;,&lt;br /&gt;
			&amp;quot;example&amp;quot;: &amp;quot;01_FHEMWEB.pm&amp;quot;,&lt;br /&gt;
			&amp;quot;type&amp;quot;: &amp;quot;string&amp;quot;,&lt;br /&gt;
			&amp;quot;required&amp;quot;: true,&lt;br /&gt;
			&amp;quot;suggested&amp;quot;: true&lt;br /&gt;
		},&lt;br /&gt;
		&amp;quot;ModOwner&amp;quot;: {&lt;br /&gt;
			&amp;quot;label&amp;quot;: &amp;quot;Modulersteller&amp;quot;,&lt;br /&gt;
			&amp;quot;description&amp;quot;: &amp;quot;Name des Moulerstellers bzw. -betreuers&amp;quot;,&lt;br /&gt;
			&amp;quot;type&amp;quot;: &amp;quot;content&amp;quot;&lt;br /&gt;
		}&lt;br /&gt;
	},&lt;br /&gt;
	&amp;quot;description&amp;quot;: &amp;quot;Eine Infobox um die wichtigsten Daten zu einem FHEM-Modul darzustellen.&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/templatedata&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Vorschlag für die Struktur/Gliederung einer Modulseite  ==&lt;br /&gt;
Eine Seite, die die Vorlage &#039;&#039;Infobox Modul&#039;&#039; verwendet, sollte sich an der folgenden Gliederung orientieren (wobei der Schwerpunkt auf Anwendungsbeispiele gelegt werden sollte; Zweck ist es nicht, die commandref zu kopieren oder zu ersetzen):&lt;br /&gt;
&amp;lt;pre&amp;gt;&amp;lt;nowiki&amp;gt;&lt;br /&gt;
{{Infobox Modul&lt;br /&gt;
...&lt;br /&gt;
}}&lt;br /&gt;
[[Modulname]] ist ... und macht ... (allgemeine Beschreibung, etwas mehr, &lt;br /&gt;
als unter &amp;quot;ModPurpose&amp;quot; angegeben)&lt;br /&gt;
&lt;br /&gt;
== Voraussetzungen ==&lt;br /&gt;
Um Modul xyz benutzen zu können, muss außerdem ...&lt;br /&gt;
&lt;br /&gt;
== Anwendung ==&lt;br /&gt;
=== Define ===&lt;br /&gt;
&lt;br /&gt;
=== Attribute ===&lt;br /&gt;
&lt;br /&gt;
== Anwendungsbeispiele ==&lt;br /&gt;
&lt;br /&gt;
== Links ==&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:xyz]] &lt;br /&gt;
&amp;lt;!-- (Modulkategorie wird automatisch gesetzt) --&amp;gt;&lt;br /&gt;
&amp;lt;/nowiki&amp;gt;&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Vorlage &#039;&#039;Infobox Modul&#039;&#039; wird derzeit verwendet auf den [[Special:Linkliste/Vorlage:Infobox Modul|hier]] gelisteten Seiten.&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Vorlage]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/noinclude&amp;gt;&lt;/div&gt;</summary>
		<author><name>Ph1959de</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=Vorlage:Infobox_Hardware&amp;diff=40657</id>
		<title>Vorlage:Infobox Hardware</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=Vorlage:Infobox_Hardware&amp;diff=40657"/>
		<updated>2026-01-05T15:24:05Z</updated>

		<summary type="html">&lt;p&gt;Ph1959de: ... give it a fine border&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;onlyinclude&amp;gt;&lt;br /&gt;
{| class=&amp;quot;float-right infobox toccolours&amp;quot; style=&amp;quot;margin: 0 0 1em 1em; float:right; border-style: solid; border-width: 1px&amp;quot; width=&amp;quot;300&amp;quot;&lt;br /&gt;
! colspan=&amp;quot;2&amp;quot; style=&amp;quot;background-color:#EFEFFF&amp;quot; | {{#if: {{{Name|}}}|{{{Name}}}|{{PAGENAME}} }}&lt;br /&gt;
|- bgcolor=&amp;quot;#FFFFFF&amp;quot;&lt;br /&gt;
| align=&amp;quot;center&amp;quot; colspan=&amp;quot;2&amp;quot; | [[Datei:{{{Bild}}}|280px|mini|frameless{{#if:{{{Bildbeschreibung|}}}|{{!}}{{{Bildbeschreibung}}}|}}]]&lt;br /&gt;
&amp;lt;!-- auskommentiert ...&lt;br /&gt;
|- style=&amp;quot;background-color:#EFEFFF&amp;quot;&lt;br /&gt;
! colspan=&amp;quot;2&amp;quot; | Die Vorlage ist noch in Bearbeitung.&amp;lt;br /&amp;gt;Bitte noch nicht benutzen!&lt;br /&gt;
...--&amp;gt;&lt;br /&gt;
|- style=&amp;quot;background-color:#EFEFFF&amp;quot;&lt;br /&gt;
! colspan=&amp;quot;2&amp;quot; | Allgemein&lt;br /&gt;
|- bgcolor=&amp;quot;#FFFFFF&amp;quot;&lt;br /&gt;
| Protokoll || {{{HWProtocol}}}&lt;br /&gt;
|- bgcolor=&amp;quot;#FFFFFF&amp;quot;&lt;br /&gt;
| Typ || {{{HWType}}}&lt;br /&gt;
|- bgcolor=&amp;quot;#FFFFFF&amp;quot;&lt;br /&gt;
| Kategorie || {{{HWCategory}}} &lt;br /&gt;
|- style=&amp;quot;background-color:#EFEFFF&amp;quot;&lt;br /&gt;
! colspan=&amp;quot;2&amp;quot; | Technische Details&lt;br /&gt;
|- bgcolor=&amp;quot;#FFFFFF&amp;quot;&lt;br /&gt;
| Kommunikation || {{{HWComm}}}&lt;br /&gt;
|- bgcolor=&amp;quot;#FFFFFF&amp;quot;&lt;br /&gt;
| Kanäle || {{{HWChannels}}}&lt;br /&gt;
|- bgcolor=&amp;quot;#FFFFFF&amp;quot;&lt;br /&gt;
| Betriebsspannung || {{{HWVoltage}}}&lt;br /&gt;
|- bgcolor=&amp;quot;#FFFFFF&amp;quot;&lt;br /&gt;
| Leistungsaufnahme || {{{HWPowerConsumption}}}&lt;br /&gt;
|- bgcolor=&amp;quot;#FFFFFF&amp;quot;&lt;br /&gt;
| Versorgung || {{{HWPoweredBy}}}&lt;br /&gt;
|- bgcolor=&amp;quot;#FFFFFF&amp;quot;&lt;br /&gt;
| Abmessungen || {{{HWSize}}}&lt;br /&gt;
|- style=&amp;quot;background-color:#EFEFFF&amp;quot;&lt;br /&gt;
! colspan=&amp;quot;2&amp;quot; | Sonstiges&lt;br /&gt;
|- bgcolor=&amp;quot;#FFFFFF&amp;quot;&lt;br /&gt;
| Modulname || {{{HWDeviceFHEM}}}&lt;br /&gt;
|- bgcolor=&amp;quot;#FFFFFF&amp;quot;&lt;br /&gt;
{{#if: {{{ModOwner|}}} |&lt;br /&gt;
{{!}} Ersteller {{!!}} {{{ModOwner|}}}&lt;br /&gt;
}}&lt;br /&gt;
|- bgcolor=&amp;quot;#FFFFFF&amp;quot;&lt;br /&gt;
| Hersteller || {{{HWManufacturer}}}&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/onlyinclude&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;noinclude&amp;gt;&lt;br /&gt;
__NOTOC__&lt;br /&gt;
&lt;br /&gt;
== Dokumentation ==&lt;br /&gt;
Diese Vorlage dient zur Zusammenfassung von Daten zu einer bestimmten, von [[FHEM]] unterstützten Hardwarekomponente.&lt;br /&gt;
&lt;br /&gt;
=== Kopiervorlage(n) ===&lt;br /&gt;
&amp;lt;pre style=&amp;quot;width:400px;&amp;quot;&amp;gt;&lt;br /&gt;
{{Infobox Hardware&lt;br /&gt;
|Bild=PlatzHalter.png&lt;br /&gt;
|Bildbeschreibung=&lt;br /&gt;
|HWProtocol= &lt;br /&gt;
|HWType=&lt;br /&gt;
|HWCategory=&lt;br /&gt;
|HWComm=&lt;br /&gt;
|HWChannels=&lt;br /&gt;
|HWVoltage=&lt;br /&gt;
|HWPowerConsumption=&lt;br /&gt;
|HWPoweredBy=&lt;br /&gt;
|HWSize=&lt;br /&gt;
|HWDeviceFHEM=&lt;br /&gt;
&amp;lt;!-- |ModOwner=  --&amp;gt;&lt;br /&gt;
|HWManufacturer=&lt;br /&gt;
}}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Erläuterung der Parameter ===&lt;br /&gt;
{{Infobox Hardware&lt;br /&gt;
|Bild=PlatzHalter.png&lt;br /&gt;
|Bildbeschreibung=Beschreibung zum Bild&lt;br /&gt;
|HWProtocol=FS20 / FHT / HMS / HomeMatic (BidCos) / EnOcean / 1-Wire&lt;br /&gt;
|HWType=Server / Sensor / Aktor / Sender / Empfänger / Gateway&lt;br /&gt;
|HWCategory=FS20 / Homematic / ...&lt;br /&gt;
|HWComm=868&amp;amp;nbsp;MHz / 1-Wire / LAN / RS485 / ...&lt;br /&gt;
|HWChannels=n/a / 1 / ... Anzahl unterstützter Kanäle&lt;br /&gt;
|HWVoltage=3V / 230V / ...&lt;br /&gt;
|HWPowerConsumption=0,5W / 2 Jahre&lt;br /&gt;
|HWPoweredBy=Batterie 2xLR6 / Netz / ...&lt;br /&gt;
|HWSize=Abmessungen LxBxH &lt;br /&gt;
|HWDeviceFHEM=see commandref, Devices&lt;br /&gt;
|ModOwner=Modulersteller&lt;br /&gt;
|HWManufacturer=ELV / eQ-3 / EnOcean ...&lt;br /&gt;
}}&lt;br /&gt;
Das nebenstehende Beispiel zeigt eine Auswahl der für die einzelnen Zeilen vorgesehenen Werte.&lt;br /&gt;
&lt;br /&gt;
; Bild&lt;br /&gt;
: Dateiname der zugehörigen Bilddatei (bitte &#039;&#039;PlatzHalter.png&#039;&#039; verwenden, wenn kein eigenes Bild vorhanden ist)&lt;br /&gt;
; Bildbeschreibung&lt;br /&gt;
: Erläuterungstext zum Bild &lt;br /&gt;
; HWProtocol &lt;br /&gt;
: Geräteprotokoll (FS20, HomeMatic, etc.)&lt;br /&gt;
; HWType &lt;br /&gt;
: Gerätetyp (Sensor, Sender, ...); bei HomeMatic Geräten der &#039;&#039;HomeMatic Type ...&#039;&#039;, formatiert als &amp;lt;nowiki&amp;gt;[[HomeMatic Type xyz|xyz]]&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
; HWCategory &lt;br /&gt;
: Gerätekategorie (Max, FHT, HMS, HomeMatic, ...)&lt;br /&gt;
; HWComm &lt;br /&gt;
: Kommunikation (Funk 868MHz, 1-Wire, RS485, ...)&lt;br /&gt;
; HWChannels&lt;br /&gt;
: Anzahl unterstützter Kanäle&lt;br /&gt;
; HWVoltage&lt;br /&gt;
: Betriebsspannung&lt;br /&gt;
; HWPowerConsumption&lt;br /&gt;
: Leistungsaufnahme (Ruhe / Betrieb in W, Batterielebensdauer, ...) &lt;br /&gt;
; HWPoweredBy&lt;br /&gt;
: Versorgungsart (Batterie, Netz)&lt;br /&gt;
; HWSize&lt;br /&gt;
: Abmessungen&lt;br /&gt;
; HWDeviceFHEM&lt;br /&gt;
: Welches FHEM-Modul unterstützt das Gerät; gibt es eine eigene Modulseite, sollte hier &amp;lt;nowiki&amp;gt;[[ModulName]]&amp;lt;/nowiki&amp;gt; (in der korrekten Schreibweise der Modulseite) angegeben werden.&lt;br /&gt;
; ModOwner&lt;br /&gt;
: (optional) Modulersteller / -betreuer&lt;br /&gt;
; HWManufacturer&lt;br /&gt;
: Hersteller des Geräts&lt;br /&gt;
&lt;br /&gt;
&amp;lt;templatedata&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
	&amp;quot;params&amp;quot;: {&lt;br /&gt;
		&amp;quot;Name&amp;quot;: {&lt;br /&gt;
			&amp;quot;label&amp;quot;: &amp;quot;Name&amp;quot;,&lt;br /&gt;
			&amp;quot;description&amp;quot;: &amp;quot;Sofern der Gerätename von dem Namen der Wiki-Seite abweicht kann hiermit ein alternativer Name in der Infobox gewählt werden&amp;quot;,&lt;br /&gt;
			&amp;quot;type&amp;quot;: &amp;quot;string&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		&amp;quot;Bild&amp;quot;: {&lt;br /&gt;
			&amp;quot;label&amp;quot;: &amp;quot;Bild&amp;quot;,&lt;br /&gt;
			&amp;quot;description&amp;quot;: &amp;quot;Die Bezeichnung des Bildes welches im Wiki dazu hochgeladen ist.&amp;quot;,&lt;br /&gt;
			&amp;quot;type&amp;quot;: &amp;quot;wiki-file-name&amp;quot;,&lt;br /&gt;
			&amp;quot;required&amp;quot;: true,&lt;br /&gt;
			&amp;quot;suggested&amp;quot;: true&lt;br /&gt;
		},&lt;br /&gt;
		&amp;quot;Bildbeschreibung&amp;quot;: {&lt;br /&gt;
			&amp;quot;label&amp;quot;: &amp;quot;Bildbeschreibung&amp;quot;,&lt;br /&gt;
			&amp;quot;description&amp;quot;: &amp;quot;Kurzer Erläuterungstext zum Bild. Erscheint direkt unter dem Bild.&amp;quot;,&lt;br /&gt;
			&amp;quot;type&amp;quot;: &amp;quot;string&amp;quot;,&lt;br /&gt;
			&amp;quot;required&amp;quot;: true&lt;br /&gt;
		},&lt;br /&gt;
		&amp;quot;HWProtocol&amp;quot;: {&lt;br /&gt;
			&amp;quot;label&amp;quot;: &amp;quot;Protokoll&amp;quot;,&lt;br /&gt;
			&amp;quot;description&amp;quot;: &amp;quot;Name des Übertragunsprotokolls (z.B. FS20, HomeMatic, etc.)&amp;quot;,&lt;br /&gt;
			&amp;quot;example&amp;quot;: &amp;quot;HomeMatic&amp;quot;,&lt;br /&gt;
			&amp;quot;type&amp;quot;: &amp;quot;string&amp;quot;,&lt;br /&gt;
			&amp;quot;required&amp;quot;: true&lt;br /&gt;
		},&lt;br /&gt;
		&amp;quot;HWType&amp;quot;: {&lt;br /&gt;
			&amp;quot;label&amp;quot;: &amp;quot;Typ&amp;quot;,&lt;br /&gt;
			&amp;quot;description&amp;quot;: &amp;quot;Gerätetyp (Sensor, Sender, ...); bei HomeMatic Geräten der HomeMatic Type ..., formatiert als [[HomeMatic Type xyz|xyz]]&amp;quot;,&lt;br /&gt;
			&amp;quot;example&amp;quot;: &amp;quot;Sensor&amp;quot;,&lt;br /&gt;
			&amp;quot;type&amp;quot;: &amp;quot;content&amp;quot;,&lt;br /&gt;
			&amp;quot;required&amp;quot;: true&lt;br /&gt;
		},&lt;br /&gt;
		&amp;quot;HWCategory&amp;quot;: {&lt;br /&gt;
			&amp;quot;label&amp;quot;: &amp;quot;Gerätekategorie&amp;quot;,&lt;br /&gt;
			&amp;quot;description&amp;quot;: &amp;quot;Gerätekategorie (Max, FHT, HMS, HomeMatic, ...)&amp;quot;,&lt;br /&gt;
			&amp;quot;example&amp;quot;: &amp;quot;HomeMatic&amp;quot;,&lt;br /&gt;
			&amp;quot;required&amp;quot;: true&lt;br /&gt;
		},&lt;br /&gt;
		&amp;quot;HWComm&amp;quot;: {&lt;br /&gt;
			&amp;quot;label&amp;quot;: &amp;quot;Kommunikationsart&amp;quot;,&lt;br /&gt;
			&amp;quot;description&amp;quot;: &amp;quot;Art der Kommunikation (z.B. Funk 868MHz, 1-Wire, RS485, ...)&amp;quot;,&lt;br /&gt;
			&amp;quot;example&amp;quot;: &amp;quot;Funk 868MHz&amp;quot;,&lt;br /&gt;
			&amp;quot;required&amp;quot;: true&lt;br /&gt;
		},&lt;br /&gt;
		&amp;quot;HWChannels&amp;quot;: {&lt;br /&gt;
			&amp;quot;label&amp;quot;: &amp;quot;Unterstützte Kanäle&amp;quot;,&lt;br /&gt;
			&amp;quot;description&amp;quot;: &amp;quot;Anzahl unterstützter Kanäle&amp;quot;,&lt;br /&gt;
			&amp;quot;type&amp;quot;: &amp;quot;string&amp;quot;,&lt;br /&gt;
			&amp;quot;required&amp;quot;: true&lt;br /&gt;
		},&lt;br /&gt;
		&amp;quot;HWVoltage&amp;quot;: {&lt;br /&gt;
			&amp;quot;label&amp;quot;: &amp;quot;Betriebsspannung (V)&amp;quot;,&lt;br /&gt;
			&amp;quot;description&amp;quot;: &amp;quot;Die unterstützte Betriebsspannung(-en) des Geräts&amp;quot;,&lt;br /&gt;
			&amp;quot;example&amp;quot;: &amp;quot;230V&amp;quot;,&lt;br /&gt;
			&amp;quot;type&amp;quot;: &amp;quot;string&amp;quot;,&lt;br /&gt;
			&amp;quot;required&amp;quot;: true,&lt;br /&gt;
			&amp;quot;suggested&amp;quot;: true&lt;br /&gt;
		},&lt;br /&gt;
		&amp;quot;HWPowerConsumption&amp;quot;: {&lt;br /&gt;
			&amp;quot;label&amp;quot;: &amp;quot;Leistungsaufnahme (W)&amp;quot;,&lt;br /&gt;
			&amp;quot;description&amp;quot;: &amp;quot;Die durchschnittliche Leistungsaufnahme (Watt) im Betriebszustand.&amp;quot;,&lt;br /&gt;
			&amp;quot;example&amp;quot;: &amp;quot;ca. 1W&amp;quot;,&lt;br /&gt;
			&amp;quot;type&amp;quot;: &amp;quot;string&amp;quot;,&lt;br /&gt;
			&amp;quot;required&amp;quot;: true,&lt;br /&gt;
			&amp;quot;suggested&amp;quot;: true&lt;br /&gt;
		},&lt;br /&gt;
		&amp;quot;HWPoweredBy&amp;quot;: {&lt;br /&gt;
			&amp;quot;label&amp;quot;: &amp;quot;Stromversorgung&amp;quot;,&lt;br /&gt;
			&amp;quot;description&amp;quot;: &amp;quot;Art der Stromversorgung&amp;quot;,&lt;br /&gt;
			&amp;quot;example&amp;quot;: &amp;quot;Batterie (2x AA Mignon)&amp;quot;,&lt;br /&gt;
			&amp;quot;type&amp;quot;: &amp;quot;string&amp;quot;,&lt;br /&gt;
			&amp;quot;required&amp;quot;: true&lt;br /&gt;
		},&lt;br /&gt;
		&amp;quot;HWSize&amp;quot;: {&lt;br /&gt;
			&amp;quot;label&amp;quot;: &amp;quot;Abmessungen&amp;quot;,&lt;br /&gt;
			&amp;quot;description&amp;quot;: &amp;quot;Die Abmessungen des Geräts (LxBxH)&amp;quot;,&lt;br /&gt;
			&amp;quot;type&amp;quot;: &amp;quot;string&amp;quot;,&lt;br /&gt;
			&amp;quot;required&amp;quot;: true&lt;br /&gt;
		},&lt;br /&gt;
		&amp;quot;HWDeviceFHEM&amp;quot;: {&lt;br /&gt;
			&amp;quot;label&amp;quot;: &amp;quot;FHEM-Modul&amp;quot;,&lt;br /&gt;
			&amp;quot;description&amp;quot;: &amp;quot;Das zugehörige FHEM-Modul, welches diese Hardware steuern kann.&amp;quot;,&lt;br /&gt;
			&amp;quot;example&amp;quot;: &amp;quot;CUL_HM&amp;quot;,&lt;br /&gt;
			&amp;quot;required&amp;quot;: true&lt;br /&gt;
		},&lt;br /&gt;
		&amp;quot;ModOwner&amp;quot;: {&lt;br /&gt;
			&amp;quot;label&amp;quot;: &amp;quot;Modulersteller&amp;quot;,&lt;br /&gt;
			&amp;quot;description&amp;quot;: &amp;quot;Der Ersteller / Maintainer des zugehörigen FHEM-Moduls.&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		&amp;quot;HWManufacturer&amp;quot;: {&lt;br /&gt;
			&amp;quot;label&amp;quot;: &amp;quot;Hersteller&amp;quot;,&lt;br /&gt;
			&amp;quot;description&amp;quot;: &amp;quot;Der Name des Geräteherstellers&amp;quot;,&lt;br /&gt;
			&amp;quot;example&amp;quot;: &amp;quot;ELV / eQ3&amp;quot;,&lt;br /&gt;
			&amp;quot;type&amp;quot;: &amp;quot;string&amp;quot;,&lt;br /&gt;
			&amp;quot;required&amp;quot;: true,&lt;br /&gt;
			&amp;quot;suggested&amp;quot;: true&lt;br /&gt;
		}&lt;br /&gt;
	},&lt;br /&gt;
	&amp;quot;description&amp;quot;: &amp;quot;Zusammenstellung aller wichtigen Daten/Fakten zu einem Hardware-Gerät.&amp;quot;,&lt;br /&gt;
	&amp;quot;paramOrder&amp;quot;: [&lt;br /&gt;
		&amp;quot;Bild&amp;quot;,&lt;br /&gt;
		&amp;quot;Bildbeschreibung&amp;quot;,&lt;br /&gt;
		&amp;quot;HWProtocol&amp;quot;,&lt;br /&gt;
		&amp;quot;HWType&amp;quot;,&lt;br /&gt;
		&amp;quot;HWCategory&amp;quot;,&lt;br /&gt;
		&amp;quot;HWComm&amp;quot;,&lt;br /&gt;
		&amp;quot;HWChannels&amp;quot;,&lt;br /&gt;
		&amp;quot;HWVoltage&amp;quot;,&lt;br /&gt;
		&amp;quot;HWPowerConsumption&amp;quot;,&lt;br /&gt;
		&amp;quot;HWPoweredBy&amp;quot;,&lt;br /&gt;
		&amp;quot;HWSize&amp;quot;,&lt;br /&gt;
		&amp;quot;HWDeviceFHEM&amp;quot;,&lt;br /&gt;
		&amp;quot;ModOwner&amp;quot;,&lt;br /&gt;
		&amp;quot;HWManufacturer&amp;quot;,&lt;br /&gt;
		&amp;quot;Name&amp;quot;&lt;br /&gt;
	],&lt;br /&gt;
	&amp;quot;format&amp;quot;: &amp;quot;block&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/templatedata&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Beispiele ===&lt;br /&gt;
Kopiervorlagen für diverse Anwendungssituationen:&lt;br /&gt;
&lt;br /&gt;
==== HomeMatic, batteriebetrieben ====&lt;br /&gt;
Beispielvorlage für batteriebetriebenes HomeMatic Gerät:&lt;br /&gt;
&amp;lt;pre style=&amp;quot;width:400px;&amp;quot;&amp;gt;&lt;br /&gt;
{{Infobox Hardware&lt;br /&gt;
|Bild=PlatzHalter.png&lt;br /&gt;
|Bildbeschreibung=Vorderseite mit Beschriftungen&lt;br /&gt;
|HWProtocol=HomeMatic&lt;br /&gt;
|HWType=[[HomeMatic Type ...|...]]&lt;br /&gt;
|HWCategory=HomeMatic&lt;br /&gt;
|HWComm=868MHz&lt;br /&gt;
|HWChannels=6&lt;br /&gt;
|HWVoltage=3V&lt;br /&gt;
|HWPowerConsumption=ca. 5 Jahre&lt;br /&gt;
|HWPoweredBy=2x1,5V LR03/Micro AAA&lt;br /&gt;
|HWSize=55x55x20mm&lt;br /&gt;
|HWDeviceFHEM=[[CUL_HM]]&lt;br /&gt;
|HWManufacturer=ELV / eQ-3 &lt;br /&gt;
}}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Vorschlag für die Seitenstruktur ==&lt;br /&gt;
Eine Seite, die die Vorlage &#039;&#039;Infobox Hardware&#039;&#039; verwendet, sollte sich an der folgenden Gliederung orientieren. Je nach Ausführlichkeit und Bedarf können natürliche weitere Abschnitte hinzugefügt werden.&lt;br /&gt;
&amp;lt;pre style=&amp;quot;width:650px;&amp;quot;&amp;gt;&amp;lt;nowiki&amp;gt;&lt;br /&gt;
{{Infobox Hardware&lt;br /&gt;
...&lt;br /&gt;
}}&lt;br /&gt;
[[GerätebezeichnungXyz]] ist ... und macht ... (allgemeine Beschreibung, etwas mehr, &lt;br /&gt;
als unter &amp;quot;BildBeschreibung&amp;quot; angegeben)&lt;br /&gt;
&lt;br /&gt;
== Features ==&lt;br /&gt;
Gerät Xyz hat folgende Funktionen...&lt;br /&gt;
* Senden&lt;br /&gt;
* Empfangen ...&lt;br /&gt;
&lt;br /&gt;
== Hinweise zum Betrieb mit FHEM ==&lt;br /&gt;
&lt;br /&gt;
=== Definition ===&lt;br /&gt;
&lt;br /&gt;
=== Logbeispiel ===&lt;br /&gt;
&lt;br /&gt;
== Bekannte Probleme ==&lt;br /&gt;
&lt;br /&gt;
== ... ==&lt;br /&gt;
&lt;br /&gt;
== Links ==&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:xyz]] &lt;br /&gt;
&lt;br /&gt;
&amp;lt;/nowiki&amp;gt;&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Diese Vorlage wird derzeit verwendet auf: [[Special:Linkliste/Vorlage:Infobox Hardware|(Seitenliste)]].&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Vorlage]]&lt;br /&gt;
&amp;lt;/noinclude&amp;gt;&lt;/div&gt;</summary>
		<author><name>Ph1959de</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=Vorlage:Randnotiz&amp;diff=40656</id>
		<title>Vorlage:Randnotiz</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=Vorlage:Randnotiz&amp;diff=40656"/>
		<updated>2026-01-05T14:23:07Z</updated>

		<summary type="html">&lt;p&gt;Ph1959de: Mediawiki-Update: Folgeaktion(en)&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;onlyinclude&amp;gt;&amp;lt;div class=&amp;quot;thumb tright&amp;quot; style=&amp;quot;width:40%;font-size:100%;padding:1em;float:right;background-color:#{{#switch: {{{RNTyp}}}&lt;br /&gt;
| g | Info = E0F8E0&lt;br /&gt;
| y | Warn = F2F5A9&lt;br /&gt;
| r | Fehl = F6CEE3&lt;br /&gt;
| #default = E0F8E0}};border:3px solid #{{#switch:{{{RNTyp}}}&lt;br /&gt;
| g | Info = 008000&lt;br /&gt;
| y | Warn = 868A08&lt;br /&gt;
| r | Fehl = FF0000&lt;br /&gt;
| #default = 008000}};&amp;quot;&amp;gt;[[File:{{#switch: {{{RNTyp}}}&lt;br /&gt;
| g | Info = Info_green.png&lt;br /&gt;
| y | Warn = Emblem-question-yellow.svg&lt;br /&gt;
| r | Fehl = X mark.svg&lt;br /&gt;
| #default = Info_green.png}}|20px]]{{{RNText}}}&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/onlyinclude&amp;gt;&lt;br /&gt;
&amp;lt;!--&lt;br /&gt;
 &lt;br /&gt;
    Dokumentation und Beispiele&lt;br /&gt;
&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&amp;lt;noinclude&amp;gt;&lt;br /&gt;
== Dokumentation für diese Vorlage ==&lt;br /&gt;
Diese Vorlage fügt einen rechtsbündigen, 40% der Seitenbreite einnehmenden Rahmen mit dem angegebenen Text ein. Rahmen- und Hintergrundfarbe sind wählbar und bestimmen das verwendete &amp;quot;Info-Icon&amp;quot; mit. Syntax für die Verwendung ist: &lt;br /&gt;
:&amp;lt;code&amp;gt;&amp;lt;nowiki&amp;gt;{{Randnotiz|RNTyp=Info|RNText=Text text text ...}}&amp;lt;/nowiki&amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
Unterstützte Werte für RNTyp:&lt;br /&gt;
* Info (oder g): grüne Box mit Info-Icon &lt;br /&gt;
* Warn (oder y): gelbe Box mit Fragezeichen &lt;br /&gt;
* Fehl (oder r): rote Box mit Warnschild&lt;br /&gt;
&lt;br /&gt;
Ist ein Hinweistext in Seitenbreite gewünscht, kann dafür die Vorlage [[Vorlage:Hinweis|Hinweis]] verwendet werden.&lt;br /&gt;
&lt;br /&gt;
== Beispiele ==&lt;br /&gt;
{{Randnotiz|RNTyp=Fehl|RNText=Rote Box mit rotem Icon}}&lt;br /&gt;
&amp;lt;pre style=&amp;quot;width:50%;&amp;quot;&amp;gt;&amp;lt;nowiki&amp;gt;&lt;br /&gt;
{{Randnotiz|RNTyp=Fehl|RNText=Rote Box mit rotem Icon}}&lt;br /&gt;
&amp;lt;/nowiki&amp;gt;&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;br clear=all&amp;gt; &lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- Beispiel2: Gelb mit Liste --&amp;gt;&lt;br /&gt;
{{Randnotiz|RNTyp=Warn|RNText=Gelbe ListenBox mit gelbem Icon und&lt;br /&gt;
* text&lt;br /&gt;
# text &lt;br /&gt;
... Verwendung von Aufzählungszeichen!&lt;br /&gt;
}}&lt;br /&gt;
&amp;lt;pre style=&amp;quot;width:50%;&amp;quot;&amp;gt;&amp;lt;nowiki&amp;gt;&lt;br /&gt;
{{Randnotiz|RNTyp=y|RNText=Gelbe ListenBox mit gelbem Icon und&lt;br /&gt;
* text&lt;br /&gt;
# text &lt;br /&gt;
... Verwendung von Aufzählungszeichen!&lt;br /&gt;
}}&lt;br /&gt;
&amp;lt;/nowiki&amp;gt;&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;br clear=all&amp;gt; &lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- Beispiel3: Grün --&amp;gt;&lt;br /&gt;
{{Randnotiz|RNTyp=Info|RNText=Grüne (Default) Box mit Info-Icon}}&lt;br /&gt;
&amp;lt;pre style=&amp;quot;width:50%;&amp;quot;&amp;gt;&amp;lt;nowiki&amp;gt;&lt;br /&gt;
{{Randnotiz|RNText=Grüne Box mit Info-Icon}}&lt;br /&gt;
&amp;lt;/nowiki&amp;gt;&amp;lt;/pre&amp;gt;&lt;br /&gt;
[[Kategorie:Vorlage]]&lt;br /&gt;
&amp;lt;/noinclude&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- Wikimedia &amp;quot;Standard&amp;quot; Icons z.B.: http://www.mediawiki.org/w/index.php?title=Special:MostLinkedFiles&lt;br /&gt;
Icon Alternativen:&lt;br /&gt;
FAQ icon.svg&lt;br /&gt;
Talk page icon crystal.png&lt;br /&gt;
Emblem-question-yellow.svg&lt;br /&gt;
Ambox important.svg&lt;br /&gt;
Symbol OK.svg&lt;br /&gt;
X mark.svg&lt;br /&gt;
&lt;br /&gt;
[[Datei:FAQ icon.svg|20px]] - [[Datei:Ambox important.svg|20px]] - [[Datei:Emblem-question-yellow.svg|20px]] - [[Datei:X mark.svg|20px]] - [[Datei:Symbol OK.svg|20px]] - [[Datei:Talk page icon crystal.png|20px]]&lt;br /&gt;
--&amp;gt;&lt;/div&gt;</summary>
		<author><name>Ph1959de</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=Victron&amp;diff=40603</id>
		<title>Victron</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=Victron&amp;diff=40603"/>
		<updated>2025-12-28T21:37:48Z</updated>

		<summary type="html">&lt;p&gt;Ph1959de: Diverse Korrekturen (u.a. syntaxhighlight)&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==Übersicht==&lt;br /&gt;
Die Familie der Solarlade- und -speichergeräte von Victron verwenden verschiedene Bussysteme, um untereinander zu kommunizieren. Sehr angenehm ist, dass die zentrale Steuerung namens Venus (Victron ENergy Unix System :-) frei verfügbar ist, und gern auf einem RaspberryPi läuft. Integriert ist gleich ein [[MQTT]] Server (mit und ohne SSL), der die Meßwerte der angeschlossenen Geräte - und das sind viele - per MQTT anbietet.&lt;br /&gt;
&lt;br /&gt;
Victron ist generell gut dokumentiert: https://www.victronenergy.com/support-and-downloads/technical-information&lt;br /&gt;
&lt;br /&gt;
==Einbindung in FHEM als MQTT2_DEVICE==&lt;br /&gt;
Über MQTT2_CLIENT wird zunächst die Verbindung zum Broker auf dem Victron Venus Gerät angelegt (hier ist die IP noch anzupasssen).&lt;br /&gt;
 defmod venus MQTT2_CLIENT 10.1.x.x:1883&lt;br /&gt;
 attr venus autocreate complex&lt;br /&gt;
 attr venus clientOrder MQTT_GENERIC_BRIDGE MQTT2_DEVICE&lt;br /&gt;
 attr venus room MQTT2_DEVICE&lt;br /&gt;
 attr venus verbose 4&lt;br /&gt;
&lt;br /&gt;
Der Wechselrichter wird dann als MQTT2_DEVICE angelegt: &lt;br /&gt;
 defmod MQTT2_mp2 MQTT2_DEVICE venus&lt;br /&gt;
 attr MQTT2_mp2 IODev venus&lt;br /&gt;
&lt;br /&gt;
===== Aufbau der Topics von Victron =====&lt;br /&gt;
Jede Installation &#039;&#039;Venus&#039;&#039; gibt sich eine vrm_ID, die initial aus der Hardwareadresse abgeleitet wird. Über diese ID wird die Installation auf dem [https:///vrm.victronenergy.com Victron VRM Portal] erkannt. In den Beispielen unten taucht sie als &amp;lt;vrm_ID&amp;gt; auf. Die einzelnen Geräte einer Installation erhalten Nummern, die eine Unterscheidung auch mehrerer Wechselrichter (etwa im 3-Phasenbetrieb) oder mehrerer Solarlader ermöglichen. &lt;br /&gt;
&lt;br /&gt;
Nach Verbindung erscheint eine umfangreiche readingList, aus der hier nur einige Elemente verwendet werden. &lt;br /&gt;
* system - Konfiguration des Gesamtsystems bzw. konsolidierte Werte&lt;br /&gt;
* solarcharger - MPPT-DC-Lader, Anschluß VE-Direct (seriell, baut auf RS-485), Wartung per Bluetooth und App&lt;br /&gt;
* grid - [https://www.victronenergy.com/accessories/energy-meter Stromzähler], Anschluß über RS-485 / wahlweise Ethernet &lt;br /&gt;
* vebus - Wechselrichter, Anschluß über Cat5, serielles Protokoll&lt;br /&gt;
&lt;br /&gt;
 N/&amp;lt;vrm_ID&amp;gt;/system/0/Batteries:.* { json2nameValue($EVENT, &#039;Batteries_&#039;, $JSONMAP) }&lt;br /&gt;
 N/&amp;lt;vrm_ID&amp;gt;/solarcharger/290/Yield/Power:.* { json2nameValue($EVENT, &#039;PVPower_&#039;, $JSONMAP) }&lt;br /&gt;
 N/&amp;lt;vrm_ID&amp;gt;/grid/30/Ac/Power:.* { json2nameValue($EVENT, &#039;GridPower_&#039;, $JSONMAP) }&lt;br /&gt;
 N/&amp;lt;vrm_ID&amp;gt;/vebus/288/Ac/Out/P:.* { json2nameValue($EVENT, &#039;P_&#039;, $JSONMAP) }&lt;br /&gt;
 N/&amp;lt;vrm_ID&amp;gt;/system/0/SystemState/State:.* { json2nameValue($EVENT, &#039;State_&#039;, $JSONMAP) }&lt;br /&gt;
 N/&amp;lt;vrm_ID&amp;gt;/vebus/288/Mode:.* { json2nameValue($EVENT, &#039;Mode_&#039;, $JSONMAP) }&lt;br /&gt;
&lt;br /&gt;
===== Topics zum Schreiben =====&lt;br /&gt;
Einige Einstellungen sind schreibbar. Die entsprechenden Topics beginnen mit einem W. Eine setList kann beispielsweise so aussehen und erlaubt dann &lt;br /&gt;
* An- und Abschalten&lt;br /&gt;
* Inverter- und Charger Mode&lt;br /&gt;
* Mode: Betriebszustand&lt;br /&gt;
* CurrentLimit: max. Strom aus/in das Netz&lt;br /&gt;
* MinimumSocLimit: Entladegranze im ESS (Speicher-)Betrieb, solange das Netz anliegt&lt;br /&gt;
* Relay[0|1]: Schalte interne Relais&lt;br /&gt;
Eine einfache Setlist:&lt;br /&gt;
&lt;br /&gt;
 off W/&amp;lt;vrm_ID&amp;gt;/vebus/288/Mode {&amp;quot;value&amp;quot;:4}&lt;br /&gt;
 on W/&amp;lt;vrm_ID&amp;gt;/vebus/288/Mode {&amp;quot;value&amp;quot;:3}&lt;br /&gt;
 inv W/&amp;lt;vrm_ID&amp;gt;/vebus/288/Mode {&amp;quot;value&amp;quot;:2}&lt;br /&gt;
 chg W/&amp;lt;vrm_ID&amp;gt;/vebus/288/Mode {&amp;quot;value&amp;quot;:1}&lt;br /&gt;
 Mode W/&amp;lt;vrm_ID&amp;gt;/vebus/288/Mode {&amp;quot;value&amp;quot;:$EVTPART1}&lt;br /&gt;
 GridCurrentLimit W/&amp;lt;vrm_ID&amp;gt;/vebus/288/Ac/In/1/CurrentLimit {&amp;quot;value&amp;quot;:$EVTPART1}&lt;br /&gt;
 MinSOC W/&amp;lt;vrm_ID&amp;gt;/settings/0/Settings/CGwacs/BatteryLife/MinimumSocLimit {&amp;quot;value&amp;quot;:$EVTPART1}&lt;br /&gt;
 Relay0 W/&amp;lt;vrm_ID&amp;gt;/system/0/Relay/0/State:.* {&amp;quot;value&amp;quot;:$EVTPART1}&lt;br /&gt;
 Relay1 W/&amp;lt;vrm_ID&amp;gt;/system/0/Relay/1/State:.* {&amp;quot;value&amp;quot;:$EVTPART1}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Einbindung über ModbusAttr== &lt;br /&gt;
Die verschiedenen Devices werden intern über verschieden Modbusadressen abgefragt. &lt;br /&gt;
Dies kann man dem Excel &amp;quot;Modbus-TCP Register List unter Link 1 entnehmen. &lt;br /&gt;
&lt;br /&gt;
Hier ist der Code für ein MultiPlus II-5000 Device (die Unit ID 227 verweist auf den VE.Bus-Port an ttyS4, an welchem ausschließlich jener Multiplus angeschlossen ist). Leider kann ich es mangels Geräten nicht testen, aber ich vermute, dass wenn der Multiplus an einen anderen Port angeschlossen wird, lediglich die Modbus-ID zu ändern ist.&lt;br /&gt;
&lt;br /&gt;
=== Multiplus II ===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod MultiPlusII_227 ModbusAttr 227 5 192.168.243.38:502 TCP&lt;br /&gt;
attr MultiPlusII_227 dev-h-defPoll 1&lt;br /&gt;
attr MultiPlusII_227 obj-h0003-expr $val / 10&lt;br /&gt;
attr MultiPlusII_227 obj-h0003-len 2&lt;br /&gt;
attr MultiPlusII_227 obj-h0003-reading AC_Input_Voltage_L1&lt;br /&gt;
attr MultiPlusII_227 obj-h0003-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0004-expr $val / 10&lt;br /&gt;
attr MultiPlusII_227 obj-h0004-len 2&lt;br /&gt;
attr MultiPlusII_227 obj-h0004-reading AC_Input_Voltage_L2&lt;br /&gt;
attr MultiPlusII_227 obj-h0004-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0005-expr $val / 10&lt;br /&gt;
attr MultiPlusII_227 obj-h0005-reading AC_Input_Voltage_L3&lt;br /&gt;
attr MultiPlusII_227 obj-h0005-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0006-expr $val / 10&lt;br /&gt;
attr MultiPlusII_227 obj-h0006-reading AC_Input_Current_L1&lt;br /&gt;
attr MultiPlusII_227 obj-h0006-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0007-expr $val / 10&lt;br /&gt;
attr MultiPlusII_227 obj-h0007-reading AC_Input_Current_L2&lt;br /&gt;
attr MultiPlusII_227 obj-h0007-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0008-expr $val / 10&lt;br /&gt;
attr MultiPlusII_227 obj-h0008-reading AC_Input_Current_L3&lt;br /&gt;
attr MultiPlusII_227 obj-h0008-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0009-expr $val / 100&lt;br /&gt;
attr MultiPlusII_227 obj-h0009-reading AC_Input_Frequency_L1&lt;br /&gt;
attr MultiPlusII_227 obj-h0009-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0010-expr $val / 100&lt;br /&gt;
attr MultiPlusII_227 obj-h0010-reading AC_Input_Current_L2&lt;br /&gt;
attr MultiPlusII_227 obj-h0010-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0011-expr $val / 100&lt;br /&gt;
attr MultiPlusII_227 obj-h0011-reading AC_Input_Frequency_L3&lt;br /&gt;
attr MultiPlusII_227 obj-h0011-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0012-expr $val * 10&lt;br /&gt;
attr MultiPlusII_227 obj-h0012-reading AC_Input_Power_L1&lt;br /&gt;
attr MultiPlusII_227 obj-h0012-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0013-expr $val * 10&lt;br /&gt;
attr MultiPlusII_227 obj-h0013-reading AC_Input_Power_L2&lt;br /&gt;
attr MultiPlusII_227 obj-h0013-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0014-expr $val * 10&lt;br /&gt;
attr MultiPlusII_227 obj-h0014-reading AC_Input_Power_L3&lt;br /&gt;
attr MultiPlusII_227 obj-h0014-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0015-expr $val / 10&lt;br /&gt;
attr MultiPlusII_227 obj-h0015-reading AC_Output_Voltage_L1&lt;br /&gt;
attr MultiPlusII_227 obj-h0015-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0016-expr $val / 10&lt;br /&gt;
attr MultiPlusII_227 obj-h0016-reading AC_Output_Voltage_L2&lt;br /&gt;
attr MultiPlusII_227 obj-h0016-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0017-expr $val / 10&lt;br /&gt;
attr MultiPlusII_227 obj-h0017-reading AC_Output_Voltage_L3&lt;br /&gt;
attr MultiPlusII_227 obj-h0017-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0018-expr $val / 10&lt;br /&gt;
attr MultiPlusII_227 obj-h0018-reading AC_Output_Current_L1&lt;br /&gt;
attr MultiPlusII_227 obj-h0018-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0019-expr $val / 10&lt;br /&gt;
attr MultiPlusII_227 obj-h0019-reading AC_Output_Current_L2&lt;br /&gt;
attr MultiPlusII_227 obj-h0019-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0020-expr $val / 10&lt;br /&gt;
attr MultiPlusII_227 obj-h0020-reading AC_Output_Current_L3&lt;br /&gt;
attr MultiPlusII_227 obj-h0020-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0021-expr $val / 100&lt;br /&gt;
attr MultiPlusII_227 obj-h0021-reading AC_Output_Frequency&lt;br /&gt;
attr MultiPlusII_227 obj-h0021-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0022-expr $val / 10&lt;br /&gt;
attr MultiPlusII_227 obj-h0022-reading Active_Input_current_Limit&lt;br /&gt;
attr MultiPlusII_227 obj-h0022-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0023-expr $val * 10&lt;br /&gt;
attr MultiPlusII_227 obj-h0023-reading AC_Output_Power_L1&lt;br /&gt;
attr MultiPlusII_227 obj-h0023-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0024-expr $val * 10&lt;br /&gt;
attr MultiPlusII_227 obj-h0024-reading AC_Output_Power_L2&lt;br /&gt;
attr MultiPlusII_227 obj-h0024-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0025-expr $val * 10&lt;br /&gt;
attr MultiPlusII_227 obj-h0025-reading AC_Output_Power_L3&lt;br /&gt;
attr MultiPlusII_227 obj-h0025-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0026-expr $val / 100&lt;br /&gt;
attr MultiPlusII_227 obj-h0026-reading Battery_Voltage&lt;br /&gt;
attr MultiPlusII_227 obj-h0026-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0027-expr $val / 10&lt;br /&gt;
attr MultiPlusII_227 obj-h0027-reading Battery_Current&lt;br /&gt;
attr MultiPlusII_227 obj-h0027-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0028-reading Phase_Count&lt;br /&gt;
attr MultiPlusII_227 obj-h0028-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0029-map 0:AC Input 1,1:AC Input 2,240:Disconnected&lt;br /&gt;
attr MultiPlusII_227 obj-h0029-reading Active_Input&lt;br /&gt;
attr MultiPlusII_227 obj-h0029-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0030-expr $val / 10&lt;br /&gt;
attr MultiPlusII_227 obj-h0030-reading vebus_soc&lt;br /&gt;
attr MultiPlusII_227 obj-h0030-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0031-map 0:Off,1:Low Power,2:Fault,3:Bulk,4:Absorption,5:Float,6:Storage,7:Equalize,8:Passthru,9:Inverting,10:Power assist,11:Power supply,244:Sustain,252:External controL&lt;br /&gt;
attr MultiPlusII_227 obj-h0031-reading vebus_state&lt;br /&gt;
attr MultiPlusII_227 obj-h0031-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0032-map 0:No error,1:VE.Bus Error Device is switched off because one of the other phases in the system has switched off,2:VE.Bus Error New and old types MK2 are mixed in the system,3:VE.Bus Error Not all- or more than- the expected devices were found in the system,4:VE.Bus Error No other device whatsoever detected,5:VE.Bus Error Overvoltage on AC-out,6:VE.Bus Error  Error in DDC Program,7:VE.Bus BMS connected- which requires an Assistant- but no assistant found,10:VE.Bus Error System time synchronisation problem occurred,14:VE.Bus Error Device cannot transmit data,16:VE.Bus Error Dongle missing,17:VE.Bus Error One of the devices assumed master status because the original master failed,18:VE.Bus Error AC Overvoltage on the output of a slave has occurred while already switched off,22:VE.Bus Error This device cannot function as slave,24:VE.Bus Error Switch-over system protection initiated,25:VE.Bus Error Firmware incompatibility. The firmware of one of the connected device is not sufficiently up to date to operate in conjunction with this device,26:VE.Bus Error Internal error&lt;br /&gt;
attr MultiPlusII_227 obj-h0032-reading vebus_error&lt;br /&gt;
attr MultiPlusII_227 obj-h0032-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0033-map 1:Charger Only,2:Inverter Only,3:Charger and Inverter,4:Off&lt;br /&gt;
attr MultiPlusII_227 obj-h0033-reading Device_Status&lt;br /&gt;
attr MultiPlusII_227 obj-h0033-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0033-set 1&lt;br /&gt;
attr MultiPlusII_227 obj-h0034-map 0:Ok,1:Warning,2:Alarm&lt;br /&gt;
attr MultiPlusII_227 obj-h0034-reading temperature_alarm&lt;br /&gt;
attr MultiPlusII_227 obj-h0034-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0035-map 0:Ok,1:Warning,2:Alarm&lt;br /&gt;
attr MultiPlusII_227 obj-h0035-reading low_battery_alarm&lt;br /&gt;
attr MultiPlusII_227 obj-h0035-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0036-map 0:Ok,1:Warning,2:Alarm&lt;br /&gt;
attr MultiPlusII_227 obj-h0036-reading overload_alarm&lt;br /&gt;
attr MultiPlusII_227 obj-h0036-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0037-reading ESS_Power_setpoint_L1&lt;br /&gt;
attr MultiPlusII_227 obj-h0037-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0038-map 0:Charge allowed,1:Charge disabled&lt;br /&gt;
attr MultiPlusII_227 obj-h0038-reading ESS_disable_charge_flag_phase&lt;br /&gt;
attr MultiPlusII_227 obj-h0038-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0039-map 0:Feed in allowed,1:Feed in disabled&lt;br /&gt;
attr MultiPlusII_227 obj-h0039-reading ESS_disable_feedback_flag_phase&lt;br /&gt;
attr MultiPlusII_227 obj-h0039-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0040-reading ESS_Power_setpoint_L2&lt;br /&gt;
attr MultiPlusII_227 obj-h0040-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0041-reading ESS_Power_setpoint_L3&lt;br /&gt;
attr MultiPlusII_227 obj-h0041-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0042-map 0:Ok,1:Warning,2:Alarm&lt;br /&gt;
attr MultiPlusII_227 obj-h0042-reading temperature_sensor_alarm&lt;br /&gt;
attr MultiPlusII_227 obj-h0042-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0043-map 0:Ok,1:Warning,2:Alarm&lt;br /&gt;
attr MultiPlusII_227 obj-h0043-reading voltage_sensor_alarm&lt;br /&gt;
attr MultiPlusII_227 obj-h0043-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0044-map 0:Ok,1:Warning,2:Alarm&lt;br /&gt;
attr MultiPlusII_227 obj-h0044-reading temperature_alarm_L1&lt;br /&gt;
attr MultiPlusII_227 obj-h0044-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0045-map 0:Ok,1:Warning,2:Alarm&lt;br /&gt;
attr MultiPlusII_227 obj-h0045-reading low_battery_alarm_L1&lt;br /&gt;
attr MultiPlusII_227 obj-h0045-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0046-map 0:Ok,1:Warning,2:Alarm&lt;br /&gt;
attr MultiPlusII_227 obj-h0046-reading overload_alarm_L1&lt;br /&gt;
attr MultiPlusII_227 obj-h0046-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0047-map 0:Ok,1:Warning,2:Alarm&lt;br /&gt;
attr MultiPlusII_227 obj-h0047-reading ripple_alarm_L1&lt;br /&gt;
attr MultiPlusII_227 obj-h0047-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0048-map 0:Ok,1:Warning,2:Alarm&lt;br /&gt;
attr MultiPlusII_227 obj-h0048-reading temperature_alarm_L2&lt;br /&gt;
attr MultiPlusII_227 obj-h0048-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0049-map 0:Ok,1:Warning,2:Alarm&lt;br /&gt;
attr MultiPlusII_227 obj-h0049-reading low_battery_alarm_L2&lt;br /&gt;
attr MultiPlusII_227 obj-h0049-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0050-map 0:Ok,1:Warning,2:Alarm&lt;br /&gt;
attr MultiPlusII_227 obj-h0050-reading overload_alarm_L2&lt;br /&gt;
attr MultiPlusII_227 obj-h0050-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0051-map 0:Ok,1:Warning,2:Alarm&lt;br /&gt;
attr MultiPlusII_227 obj-h0051-reading ripple_alarm_L2&lt;br /&gt;
attr MultiPlusII_227 obj-h0051-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0052-map 0:Ok,1:Warning,2:Alarm&lt;br /&gt;
attr MultiPlusII_227 obj-h0052-reading temperature_alarm_L3&lt;br /&gt;
attr MultiPlusII_227 obj-h0052-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0053-map 0:Ok,1:Warning,2:Alarm&lt;br /&gt;
attr MultiPlusII_227 obj-h0053-reading low_battery_alarm_L3&lt;br /&gt;
attr MultiPlusII_227 obj-h0053-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0054-map 0:Ok,1:Warning,2:Alarm&lt;br /&gt;
attr MultiPlusII_227 obj-h0054-reading overload_alarm_L3&lt;br /&gt;
attr MultiPlusII_227 obj-h0054-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0055-map 0:Ok,1:Warning,2:Alarm&lt;br /&gt;
attr MultiPlusII_227 obj-h0055-reading ripple_alarm_L3&lt;br /&gt;
attr MultiPlusII_227 obj-h0055-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0056-map 0:PV enabled,1:PV disabled&lt;br /&gt;
attr MultiPlusII_227 obj-h0056-reading disable_pv_inverter&lt;br /&gt;
attr MultiPlusII_227 obj-h0056-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0057-map 0:No,1:Yes&lt;br /&gt;
attr MultiPlusII_227 obj-h0057-reading bms_allow_charge&lt;br /&gt;
attr MultiPlusII_227 obj-h0057-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0058-map 0:No,1:Yes&lt;br /&gt;
attr MultiPlusII_227 obj-h0058-reading bms_allow_discharge&lt;br /&gt;
attr MultiPlusII_227 obj-h0058-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0059-map 0:No,1:Yes&lt;br /&gt;
attr MultiPlusII_227 obj-h0059-reading bms_expected&lt;br /&gt;
attr MultiPlusII_227 obj-h0059-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0060-map 0:No,1:Yes&lt;br /&gt;
attr MultiPlusII_227 obj-h0060-reading bms_error&lt;br /&gt;
attr MultiPlusII_227 obj-h0060-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0061-expr $val / 10&lt;br /&gt;
attr MultiPlusII_227 obj-h0061-reading temperature_battery&lt;br /&gt;
attr MultiPlusII_227 obj-h0061-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0062-map 1:VE.Bus reset&lt;br /&gt;
attr MultiPlusII_227 obj-h0062-reading vebus_reset&lt;br /&gt;
attr MultiPlusII_227 obj-h0062-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0063-map 0:Ok,1:Warning&lt;br /&gt;
attr MultiPlusII_227 obj-h0063-reading phase_rotation_warning&lt;br /&gt;
attr MultiPlusII_227 obj-h0063-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0064-map 0:Ok,2:Alarm&lt;br /&gt;
attr MultiPlusII_227 obj-h0064-reading grid_lost_alarm&lt;br /&gt;
attr MultiPlusII_227 obj-h0064-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0065-map 0:Feed in overvoltage,1:Do not feed in overvoltage&lt;br /&gt;
attr MultiPlusII_227 obj-h0065-reading feed_dc_overvoltage_into_grid&lt;br /&gt;
attr MultiPlusII_227 obj-h0065-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0066-expr $val * 100&lt;br /&gt;
attr MultiPlusII_227 obj-h0066-reading maximum_overvoltage_feedin_power_L1&lt;br /&gt;
attr MultiPlusII_227 obj-h0066-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0067-expr $val * 100&lt;br /&gt;
attr MultiPlusII_227 obj-h0067-reading maximum_overvoltage_feedin_power_L2&lt;br /&gt;
attr MultiPlusII_227 obj-h0067-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0068-expr $val * 100&lt;br /&gt;
attr MultiPlusII_227 obj-h0068-reading maximum_overvoltage_feedin_power_L3&lt;br /&gt;
attr MultiPlusII_227 obj-h0068-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0069-map 0:AC input not ignored,1:AC input ignored&lt;br /&gt;
attr MultiPlusII_227 obj-h0069-reading ac_input_1_ignored&lt;br /&gt;
attr MultiPlusII_227 obj-h0069-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0070-map 0:AC input not ignored,1:AC input ignored&lt;br /&gt;
attr MultiPlusII_227 obj-h0070-reading ac_input_2_ignored&lt;br /&gt;
attr MultiPlusII_227 obj-h0070-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0071-map 0:AcPowerSetpoint interpreted normally, 1:AcPowerSetpoint is OvervoltageFeedIn limit&lt;br /&gt;
attr MultiPlusII_227 obj-h0071-reading ac_power_setpoint_acts_as_feedin_limit&lt;br /&gt;
attr MultiPlusII_227 obj-h0071-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0072-map 0:OvervoltageFeedIn uses 1V offset, 1:OvervoltageFeedIn uses 0.1V offset&lt;br /&gt;
attr MultiPlusII_227 obj-h0072-reading solar_offset_voltage&lt;br /&gt;
attr MultiPlusII_227 obj-h0072-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0073-map 0:Sustain inactive, 1:Sustain active&lt;br /&gt;
attr MultiPlusII_227 obj-h0073-reading sustain_active&lt;br /&gt;
attr MultiPlusII_227 obj-h0073-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0074-expr $val / 100&lt;br /&gt;
attr MultiPlusII_227 obj-h0074-len 2&lt;br /&gt;
attr MultiPlusII_227 obj-h0074-reading energy_from_acIn1_to_acOut&lt;br /&gt;
attr MultiPlusII_227 obj-h0074-unpack N&lt;br /&gt;
attr MultiPlusII_227 obj-h0076-expr $val / 100&lt;br /&gt;
attr MultiPlusII_227 obj-h0076-len 2&lt;br /&gt;
attr MultiPlusII_227 obj-h0076-reading energy_from_acIn1_to_battery&lt;br /&gt;
attr MultiPlusII_227 obj-h0076-unpack N&lt;br /&gt;
attr MultiPlusII_227 obj-h0078-expr $val / 100&lt;br /&gt;
attr MultiPlusII_227 obj-h0078-len 2&lt;br /&gt;
attr MultiPlusII_227 obj-h0078-reading energy_from_acIn2_to_acOut&lt;br /&gt;
attr MultiPlusII_227 obj-h0078-unpack N&lt;br /&gt;
attr MultiPlusII_227 obj-h0080-expr $val / 100&lt;br /&gt;
attr MultiPlusII_227 obj-h0080-len 2&lt;br /&gt;
attr MultiPlusII_227 obj-h0080-reading energy_from_acIn2_to_battery&lt;br /&gt;
attr MultiPlusII_227 obj-h0080-unpack N&lt;br /&gt;
attr MultiPlusII_227 obj-h0082-expr $val / 100&lt;br /&gt;
attr MultiPlusII_227 obj-h0082-len 2&lt;br /&gt;
attr MultiPlusII_227 obj-h0082-reading energy_from_acOut_to_acIn1&lt;br /&gt;
attr MultiPlusII_227 obj-h0082-unpack N&lt;br /&gt;
attr MultiPlusII_227 obj-h0084-expr $val / 100&lt;br /&gt;
attr MultiPlusII_227 obj-h0084-len 2&lt;br /&gt;
attr MultiPlusII_227 obj-h0084-reading energy_from_acOut_to_acIn2&lt;br /&gt;
attr MultiPlusII_227 obj-h0084-unpack f&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0086-expr $val / 100&lt;br /&gt;
attr MultiPlusII_227 obj-h0086-len 2&lt;br /&gt;
attr MultiPlusII_227 obj-h0086-reading energy_from_battery_to_AcIn1&lt;br /&gt;
attr MultiPlusII_227 obj-h0086-unpack f&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0088-expr $val / 100&lt;br /&gt;
attr MultiPlusII_227 obj-h0088-len 2&lt;br /&gt;
attr MultiPlusII_227 obj-h0088-reading energy_from_battery_to_AcIn2&lt;br /&gt;
attr MultiPlusII_227 obj-h0088-unpack N&lt;br /&gt;
attr MultiPlusII_227 obj-h0090-expr $val / 100&lt;br /&gt;
attr MultiPlusII_227 obj-h0090-len 2&lt;br /&gt;
attr MultiPlusII_227 obj-h0090-reading energy_from_battery_to_AcOut&lt;br /&gt;
attr MultiPlusII_227 obj-h0090-unpack f&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0092-expr $val / 100&lt;br /&gt;
attr MultiPlusII_227 obj-h0092-len 2&lt;br /&gt;
attr MultiPlusII_227 obj-h0092-reading energy_from_AcOut_to_battery&lt;br /&gt;
attr MultiPlusII_227 obj-h0092-unpack N&lt;br /&gt;
attr MultiPlusII_227 obj-h0094-map 0:OK,1:Warning&lt;br /&gt;
attr MultiPlusII_227 obj-h0094-reading low_cell_voltage_imminent&lt;br /&gt;
attr MultiPlusII_227 obj-h0094-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0095-map 0:Initialising,1:Bulk,2:Absorption,3:Float,4:Storage,5:Absorb repeat,6:Forced absorb,7:Equalise,8:Bulk stopped,9:Unknown&lt;br /&gt;
attr MultiPlusII_227 obj-h0095-reading charge_state&lt;br /&gt;
attr MultiPlusII_227 obj-h0095-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0096-reading ESS_Power_setpoint_L1&lt;br /&gt;
attr MultiPlusII_227 obj-h0096-unpack f&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0098-len 2&lt;br /&gt;
attr MultiPlusII_227 obj-h0098-reading ESS_Power_setpoint_L2&lt;br /&gt;
attr MultiPlusII_227 obj-h0098-unpack f&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0100-len 2&lt;br /&gt;
attr MultiPlusII_227 obj-h0100-reading ESS_Power_setpoint_L2&lt;br /&gt;
attr MultiPlusII_227 obj-h0100-unpack f&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0102-map 0:Renewable energy not preferred,1:Renewable energy preferred&lt;br /&gt;
attr MultiPlusII_227 obj-h0102-reading prefer_renewable_energy&lt;br /&gt;
attr MultiPlusII_227 obj-h0102-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0103-map 0:Generator not selected,1:Generator selected&lt;br /&gt;
attr MultiPlusII_227 obj-h0103-reading select_remote_generator&lt;br /&gt;
attr MultiPlusII_227 obj-h0103-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0104-map 0:Generator not selected,1:Generator selected&lt;br /&gt;
attr MultiPlusII_227 obj-h0104-reading remote_generator_selected&lt;br /&gt;
attr MultiPlusII_227 obj-h0104-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0105-map 0:No Action, 1:Redetect System&lt;br /&gt;
attr MultiPlusII_227 obj-h0105-reading redetect_vebus_system&lt;br /&gt;
attr MultiPlusII_227 obj-h0105-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0106-expr $val / 100&lt;br /&gt;
attr MultiPlusII_227 obj-h0106-reading powerAssist_boost_factor&lt;br /&gt;
attr MultiPlusII_227 obj-h0106-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0107-expr $val / 100&lt;br /&gt;
attr MultiPlusII_227 obj-h0107-reading configured_output_voltage&lt;br /&gt;
attr MultiPlusII_227 obj-h0107-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0108-map 0:disabled, 1:enabled&lt;br /&gt;
attr MultiPlusII_227 obj-h0108-reading powerAssist_enabled&lt;br /&gt;
attr MultiPlusII_227 obj-h0108-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0109-map 0:disabled, 1:enabled&lt;br /&gt;
attr MultiPlusII_227 obj-h0109-reading ups_function_enabled&lt;br /&gt;
attr MultiPlusII_227 obj-h0109-unpack n&lt;br /&gt;
attr MultiPlusII_227 room Solar-&amp;gt;Victron&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Cerbo GX mit Unit ID 100 ===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod Cerbo_100 ModbusAttr 100 5 192.168.243.38:502 TCP&lt;br /&gt;
attr Cerbo_100 dev-h-defPoll 1&lt;br /&gt;
attr Cerbo_100 obj-h0800-len 6&lt;br /&gt;
attr Cerbo_100 obj-h0800-reading system_serial&lt;br /&gt;
attr Cerbo_100 obj-h0800-unpack Z12&lt;br /&gt;
attr Cerbo_100 obj-h0806-map 0:Open,1:Closed&lt;br /&gt;
attr Cerbo_100 obj-h0806-reading CCGX_relay_state_1&lt;br /&gt;
attr Cerbo_100 obj-h0806-unpack n&lt;br /&gt;
attr Cerbo_100 obj-h0807-map 0:Open,1:Closed&lt;br /&gt;
attr Cerbo_100 obj-h0807-reading CCGX_relay_state_2&lt;br /&gt;
attr Cerbo_100 obj-h0807-unpack n&lt;br /&gt;
attr Cerbo_100 obj-h0808-reading PV_AC_Coupled_output_L1&lt;br /&gt;
attr Cerbo_100 obj-h0808-unpack n&lt;br /&gt;
attr Cerbo_100 obj-h0809-reading PV_AC_Coupled_output_L2&lt;br /&gt;
attr Cerbo_100 obj-h0809-unpack n&lt;br /&gt;
attr Cerbo_100 obj-h0810-reading PV_AC_Coupled_output_L3&lt;br /&gt;
attr Cerbo_100 obj-h0810-unpack n&lt;br /&gt;
attr Cerbo_100 obj-h0811-reading PV_AC_Coupled_input_L1&lt;br /&gt;
attr Cerbo_100 obj-h0811-unpack n&lt;br /&gt;
attr Cerbo_100 obj-h0812-reading PV_AC_Coupled_input_L2&lt;br /&gt;
attr Cerbo_100 obj-h0812-unpack n&lt;br /&gt;
attr Cerbo_100 obj-h0813-reading PV_AC_Coupled_input_L3&lt;br /&gt;
attr Cerbo_100 obj-h0813-unpack n&lt;br /&gt;
attr Cerbo_100 obj-h0814-reading PV_AC_Coupled_generator_L1&lt;br /&gt;
attr Cerbo_100 obj-h0814-unpack n&lt;br /&gt;
attr Cerbo_100 obj-h0815-reading PV_AC_Coupled_generator_L2&lt;br /&gt;
attr Cerbo_100 obj-h0815-unpack n&lt;br /&gt;
attr Cerbo_100 obj-h0816-reading PV_AC_Coupled_generator_L3&lt;br /&gt;
attr Cerbo_100 obj-h0816-unpack n&lt;br /&gt;
attr Cerbo_100 obj-h0817-reading AC_consumption_L1&lt;br /&gt;
attr Cerbo_100 obj-h0817-unpack n&lt;br /&gt;
attr Cerbo_100 obj-h0818-reading AC_consumption_L2&lt;br /&gt;
attr Cerbo_100 obj-h0818-unpack n&lt;br /&gt;
attr Cerbo_100 obj-h0819-reading AC_consumption_L3&lt;br /&gt;
attr Cerbo_100 obj-h0819-unpack n&lt;br /&gt;
attr Cerbo_100 obj-h0820-reading grid_L1&lt;br /&gt;
attr Cerbo_100 obj-h0820-unpack s&amp;gt;&lt;br /&gt;
attr Cerbo_100 obj-h0821-reading grid_L2&lt;br /&gt;
attr Cerbo_100 obj-h0821-unpack s&amp;gt;&lt;br /&gt;
attr Cerbo_100 obj-h0822-reading grid_L3&lt;br /&gt;
attr Cerbo_100 obj-h0822-unpack s&amp;gt;&lt;br /&gt;
attr Cerbo_100 obj-h0823-reading genset_L1&lt;br /&gt;
attr Cerbo_100 obj-h0823-unpack s&amp;gt;&lt;br /&gt;
attr Cerbo_100 obj-h0824-reading genset_L2&lt;br /&gt;
attr Cerbo_100 obj-h0824-unpack s&amp;gt;&lt;br /&gt;
attr Cerbo_100 obj-h0825-reading genset_L3&lt;br /&gt;
attr Cerbo_100 obj-h0825-unpack s&amp;gt;&lt;br /&gt;
attr Cerbo_100 obj-h0826-map 0:Unknown,1:Grid,2:Generator,3:Shore power,240:Not connected&lt;br /&gt;
attr Cerbo_100 obj-h0826-reading active_input_source&lt;br /&gt;
attr Cerbo_100 obj-h0826-unpack s&amp;gt;&lt;br /&gt;
attr Cerbo_100 obj-h0830-len 4&lt;br /&gt;
attr Cerbo_100 obj-h0830-reading system_UTC_time&lt;br /&gt;
attr Cerbo_100 obj-h0830-unpack Q&lt;br /&gt;
attr Cerbo_100 obj-h0840-expr $val / 10&lt;br /&gt;
attr Cerbo_100 obj-h0840-reading battery_voltage_system&lt;br /&gt;
attr Cerbo_100 obj-h0840-unpack n&lt;br /&gt;
attr Cerbo_100 obj-h0841-expr $val / 10&lt;br /&gt;
attr Cerbo_100 obj-h0841-reading battery_current_system&lt;br /&gt;
attr Cerbo_100 obj-h0841-unpack s&amp;gt;&lt;br /&gt;
attr Cerbo_100 obj-h0842-reading battery_power_system&lt;br /&gt;
attr Cerbo_100 obj-h0842-unpack s&amp;gt;&lt;br /&gt;
attr Cerbo_100 obj-h0843-reading battery_soc_system&lt;br /&gt;
attr Cerbo_100 obj-h0843-unpack n&lt;br /&gt;
attr Cerbo_100 obj-h0844-map 0:idle,1:charging,2:discharging&lt;br /&gt;
attr Cerbo_100 obj-h0844-reading battery_state_system&lt;br /&gt;
attr Cerbo_100 obj-h0844-unpack n&lt;br /&gt;
attr Cerbo_100 obj-h0845-expr $val /-10&lt;br /&gt;
attr Cerbo_100 obj-h0845-reading battery_consumed_amphours_system&lt;br /&gt;
attr Cerbo_100 obj-h0845-unpack n&lt;br /&gt;
attr Cerbo_100 obj-h0846-expr $val * 100&lt;br /&gt;
attr Cerbo_100 obj-h0846-reading battery_timetogo_system&lt;br /&gt;
attr Cerbo_100 obj-h0846-unpack n&lt;br /&gt;
attr Cerbo_100 obj-h0850-reading PV_DC_Coupled_power&lt;br /&gt;
attr Cerbo_100 obj-h0850-unpack n&lt;br /&gt;
attr Cerbo_100 obj-h0851-expr $val /10&lt;br /&gt;
attr Cerbo_100 obj-h0851-reading PV_DC_Coupled_current&lt;br /&gt;
attr Cerbo_100 obj-h0851-unpack s&amp;gt;&lt;br /&gt;
attr Cerbo_100 obj-h0855-reading charger_power&lt;br /&gt;
attr Cerbo_100 obj-h0855-unpack n&lt;br /&gt;
attr Cerbo_100 obj-h0860-reading dc_system_power&lt;br /&gt;
attr Cerbo_100 obj-h0860-unpack s&amp;gt;&lt;br /&gt;
attr Cerbo_100 obj-h0865-expr $val /10&lt;br /&gt;
attr Cerbo_100 obj-h0865-reading vebus_charge_current_system&lt;br /&gt;
attr Cerbo_100 obj-h0865-unpack s&amp;gt;&lt;br /&gt;
attr Cerbo_100 obj-h0866-reading vebus_charge_power_system&lt;br /&gt;
attr Cerbo_100 obj-h0866-unpack s&amp;gt;&lt;br /&gt;
attr Cerbo_100 obj-h0867-reading RESERVED&lt;br /&gt;
attr Cerbo_100 obj-h0867-unpack n&lt;br /&gt;
attr Cerbo_100 obj-h0868-expr $val /10&lt;br /&gt;
attr Cerbo_100 obj-h0868-reading inverter_charger_current&lt;br /&gt;
attr Cerbo_100 obj-h0868-unpack f&amp;gt;&lt;br /&gt;
attr Cerbo_100 obj-h0870-len 2&lt;br /&gt;
attr Cerbo_100 obj-h0870-reading inverter_charger_power&lt;br /&gt;
attr Cerbo_100 obj-h0870-unpack f&amp;gt;&lt;br /&gt;
attr Cerbo_100 obj-h0872-len 2&lt;br /&gt;
attr Cerbo_100 obj-h0872-reading power_between_meter_and_charger_L1&lt;br /&gt;
attr Cerbo_100 obj-h0872-unpack f&amp;gt;&lt;br /&gt;
attr Cerbo_100 obj-h0874-len 2&lt;br /&gt;
attr Cerbo_100 obj-h0874-reading power_between_meter_and_charger_L2&lt;br /&gt;
attr Cerbo_100 obj-h0874-unpack f&amp;gt;&lt;br /&gt;
attr Cerbo_100 obj-h0876-len 2&lt;br /&gt;
attr Cerbo_100 obj-h0876-reading power_between_meter_and_charger_L3&lt;br /&gt;
attr Cerbo_100 obj-h0876-unpack f&amp;gt;&lt;br /&gt;
attr Cerbo_100 obj-h0878-len 2&lt;br /&gt;
attr Cerbo_100 obj-h0878-reading power_output_inverter_charger_L1&lt;br /&gt;
attr Cerbo_100 obj-h0878-unpack f&amp;gt;&lt;br /&gt;
attr Cerbo_100 obj-h0880-len 2&lt;br /&gt;
attr Cerbo_100 obj-h0880-reading power_output_inverter_charger_L2&lt;br /&gt;
attr Cerbo_100 obj-h0880-unpack f&amp;gt;&lt;br /&gt;
attr Cerbo_100 obj-h0882-len 2&lt;br /&gt;
attr Cerbo_100 obj-h0882-reading power_output_inverter_charger_L3&lt;br /&gt;
attr Cerbo_100 obj-h0882-unpack f&amp;gt;&lt;br /&gt;
attr Cerbo_100 room Solar-&amp;gt;Victron&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Visualisierung==&lt;br /&gt;
&#039;&#039;&#039;stateFormat&#039;&#039;&#039; kann so aussehen:&lt;br /&gt;
 SSOC Batteries_value_1_soc, &lt;br /&gt;
 SOC: Batteries_value_1_soc % (MinimumSocLimit_value), &lt;br /&gt;
 Bat: Batteries_value_1_power W,&lt;br /&gt;
 PV: PVPower_value W,&lt;br /&gt;
 ACin: GridPower_value W, &lt;br /&gt;
 ACout2: P_value W,&lt;br /&gt;
 State: State_value,&lt;br /&gt;
 Mode: Mode_value&lt;br /&gt;
SSOC wird später für das devStateIcon genutzt.&lt;br /&gt;
&lt;br /&gt;
Das &#039;&#039;&#039;[[devStateIcon]]&#039;&#039;&#039; bietet einen ersten Blick auf den Ladestand:&lt;br /&gt;
 SSOC\s[2|3|4].+:measure_battery_25&lt;br /&gt;
 SSOC\s[5|6].+:measure_battery_50&lt;br /&gt;
 SSOC\s[7|8].+:measure_battery_75&lt;br /&gt;
 SSOC\s[9].+:measure_battery_100&lt;br /&gt;
&lt;br /&gt;
Derzeit existiert noch kein Template, aber das kann sich ja ändern.&lt;br /&gt;
&lt;br /&gt;
==Externe Links==&lt;br /&gt;
*[https://www.victronenergy.com/support-and-downloads/technical-information Victron Technical Info]&lt;br /&gt;
*[https://vrm-api-docs.victronenergy.com/#/ VRm API]&lt;br /&gt;
*[https://github.com/victronenergy/dbus-mqtt Venus MQTT Broker]&lt;/div&gt;</summary>
		<author><name>Ph1959de</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=LightScene&amp;diff=40602</id>
		<title>LightScene</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=LightScene&amp;diff=40602"/>
		<updated>2025-12-28T21:28:56Z</updated>

		<summary type="html">&lt;p&gt;Ph1959de: Syntaxhighlight korrigiert&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Infobox Modul&lt;br /&gt;
|ModPurpose=Speicherung und Wiederherstellung des Zustands von einer Gruppe von Geräten.&lt;br /&gt;
|ModType=h&lt;br /&gt;
&amp;lt;!-- |ModCategory=?? --&amp;gt;&lt;br /&gt;
&amp;lt;!-- |ModCmdRef=http://fhem.de/commandref.html#LightScene --&amp;gt;&lt;br /&gt;
|ModTechName=31_LightScene.pm&lt;br /&gt;
|ModOwner=justme1968&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;LightScene&#039;&#039;&#039; ist ein Hilfsmodul, das es erlaubt, Zustände einer Gruppe von Geräten abzuspeichern und wiederherzustellen.&lt;br /&gt;
&lt;br /&gt;
== Anwendung ==&lt;br /&gt;
=== Define ===&lt;br /&gt;
:&amp;lt;code&amp;gt;define &amp;lt;name&amp;gt; LightScene [&amp;lt;dev1&amp;gt;] [&amp;lt;dev2&amp;gt;] [&amp;lt;dev3&amp;gt;] ... &amp;lt;/code&amp;gt;&lt;br /&gt;
:Beispiele:&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
define light_group LightScene Lampe1 Lampe2 Dimmer1&lt;br /&gt;
define kino_group LightScene LampeDecke LampeFernseher Fernseher Verstaerker&lt;br /&gt;
define Wohnzimmer LightScene Leinwand Beamer TV Leselampe Deckenlampe&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Die Gerätedetailansicht zeigt eine HTML-Übersicht über den aktuellen Zustand aller eingebundenen Geräte und aller konfigurierten Szenen mit den jeweiligen Gerätezuständen. Die Spaltenüberschrift mit den Gerätenamen ist anklickbar, um zur Detailansicht dieses Geräts zu gelangen. Die erste Zeile, die den aktuellen Gerätezustand anzeigt, ist anklickbar und sollte wie ein Klick auf das Gerätesymbol in einer Raumübersicht reagieren. Damit kann interaktiv eine neue Szene konfiguriert und mit dem Befehlsmenü der Detailansicht gespeichert werden. Die erste Spalte der Tabelle mit den Szenennamen ist anklickbar, um die Szene zu aktivieren.&lt;br /&gt;
:&#039;&#039;&#039;Achtung&#039;&#039;&#039;: nach einer Änderung von Szenen muss die FHEM-Konfiguration gespeichert werden, auch wenn nicht wie üblich mit einem roten Fragezeichen angezeigt wird, dass die Konfiguration geändert hat.&lt;br /&gt;
:Ein Weblink mit einer Szenenübersicht, der in jeden Raum oder Grundriss eingebunden werden kann, lässt sich erstellen mit:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
define wlScene weblink htmlCode {LightScene_2html(&amp;quot;LightSceneName&amp;quot;)}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Set ===&lt;br /&gt;
&lt;br /&gt;
=== Get ===&lt;br /&gt;
&lt;br /&gt;
=== Attributes ===&lt;br /&gt;
&lt;br /&gt;
== Funktionen ==&lt;br /&gt;
&lt;br /&gt;
== Anwendungsbeispiele ==&lt;br /&gt;
* [[XBMC#Lichtsteuerung_durch_KODI_oder_PLEX|Lichtsteuerung durch KODI oder PLEX]]&lt;br /&gt;
* [[Zuhause-Status|Umsetzung Zuhause-Status mit LightScene]]&lt;br /&gt;
* [[ReadingsGroup#LightScene_DropDown-Men.C3.BC_f.C3.BCr_smallscreen_Styles_oder_Floorplan|LightScene-DropDown-Menü für Smallscreen-Styles oder Floorplan]]&lt;br /&gt;
&lt;br /&gt;
== Links ==&lt;br /&gt;
* weitere Dokumentation&lt;br /&gt;
* ...&lt;/div&gt;</summary>
		<author><name>Ph1959de</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=Modul_FBTAM&amp;diff=40601</id>
		<title>Modul FBTAM</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=Modul_FBTAM&amp;diff=40601"/>
		<updated>2025-12-28T18:45:58Z</updated>

		<summary type="html">&lt;p&gt;Ph1959de: Typografie; Codefragmente mit Syntaxhighlight&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Infobox Modul&lt;br /&gt;
|ModPurpose=Das Modul stellt eine komfortable Oberfläche bereit, um den Anrufbeantworter der FritzBox abzufragen und zu steuern&lt;br /&gt;
|ModType=h&lt;br /&gt;
|ModCmdRef=FBTAM&lt;br /&gt;
|ModForumArea=Fritz!Box&lt;br /&gt;
|ModTechName=72_FBTAM.pm&lt;br /&gt;
|ModOwner=Prof. Dr. Peter A. Henning&lt;br /&gt;
}}&lt;br /&gt;
Für Supportanfragen bitte &#039;&#039;{{Link2Forum|Topic=142205.0|LinkText=diesen Forenthread}}&#039;&#039; verwenden.&lt;br /&gt;
&lt;br /&gt;
=Allgemeines=&lt;br /&gt;
Das Modul &#039;&#039;72_FBTAM.pm&#039;&#039; stellt eine komfortable Oberfläche bereit, um den Anrufbeantworter der FritzBox abzufragen und zu steuern.&lt;br /&gt;
&lt;br /&gt;
Zur Einrichtung muss das Modul &#039;&#039;72_FBTAM.pm&#039;&#039; im Modulpfad installiert werden, ebenso die JavaScript-Datei &#039;&#039;fbtam.js&#039;&#039; in www/pgm2.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Achtung&#039;&#039;&#039;: Derzeit geht das Modul davon aus, dass lokal (d.h. in derselben FHEM-Installation) ein Device vom Typ FRITZBOX existiert. Ggf. wird dies in Kürze noch vereinfacht, so dass nur im lokalen Subnetz eine FritzBox existieren muss.&lt;br /&gt;
==Definition==&lt;br /&gt;
Das FHEM-Device wird in der folgenden Dokumentation mit dem Namen &amp;quot;FritzBoxTAM&amp;quot; verwendet und definiert als&lt;br /&gt;
 define FritzBoxTAM FBTAM &amp;lt;Name des FRITZBOX-Devices&amp;gt; &amp;lt;Nummer des verwalteten Anrufbeantworters 1..4&amp;gt;&lt;br /&gt;
Das Device verfügt dann über die folgenden Attribute&lt;br /&gt;
* &#039;&#039;username&#039;&#039; ist ein Benutzername der FritzBox, welcher das Recht zu Abfragen des Anrufbeantworters haben muss&lt;br /&gt;
* &#039;&#039;interval&#039;&#039; ist das Abfrageintervall bei der FritzBox, Default = 60 (Sekunden)&lt;br /&gt;
* &#039;&#039;targetdir&#039;&#039; ist das Speicherverzeichnis für heruntergeladene Nachrichten, Default = /opt/fhem/www/audio&lt;br /&gt;
* &#039;&#039;MailFun&#039;&#039; ist eine mit Perl (nicht FHEM!) ausführbare Funktion zum Versand von Mails. In diesem Funktionstemplate wird beim Versand der String REC durch die Mailadresse des jeweiligen Empfängers, der String META durch die Metadaten der Nachricht und der String FILE durch den Dateinamen der gespeicherten Nachrichtendatei ersetzt.&lt;br /&gt;
Beispiel für diesen Attributwert:&lt;br /&gt;
 attr FritzBoxTAM MailFun sendMail(&amp;quot;REC&amp;quot;,&amp;quot;Anrufbeantworter&amp;quot;,&amp;quot;META&amp;quot;,&amp;quot;FILE&amp;quot;)&lt;br /&gt;
* &#039;&#039;MailRecList&#039;&#039; ist eine durch Leerzeichen getrennte Liste von Mailadressen, an welche ggf. Emails betreffend die Nachrichten versandt werden sollen. &#039;&#039;&#039;Achtung&#039;&#039;&#039;: Das Zeichen &amp;quot;@&amp;quot; muss in dieser Liste stets als &amp;quot;\@&amp;quot; geschrieben werden.&lt;br /&gt;
* &#039;&#039;MsgrType&#039;&#039; ist eine Bezeichnung für den Instant Messenger, mit welchem Messages zu den Nachrichten versendet werden sollen.&lt;br /&gt;
Beispiel für diesen Attributwert:&lt;br /&gt;
 attr FritzBoxTAM MsgrType Telegram&lt;br /&gt;
* &#039;&#039;MsgrFun&#039;&#039; ist eine mit Perl (nicht FHEM!) ausführbare Funktion zum Versand von Messages. In diesem Funktionstemplate wird beim Versand der String REC durch die Adresse des jeweiligen Empfängers, der String META durch die Metadaten der Nachricht und der String FILE durch den Dateinamen der gespeicherten Nachrichtendatei ersetzt. &lt;br /&gt;
Beispiel für diesen Attributwert:&lt;br /&gt;
 attr FritzBoxTAM MsgrFun fhem(&#039;set TelegramBot sendMedia REC FILE&#039;)&lt;br /&gt;
* &#039;&#039;MsgrRecList&#039;&#039; ist eine durch Leerzeichen getrennte Liste von Messenger-Adressen, an welche ggf. Kurznachrichten betreffend die Nachrichten versandt werden sollen. &#039;&#039;&#039;Achtung&#039;&#039;&#039;: Das Zeichen &amp;quot;@&amp;quot; muss in dieser Liste stets als &amp;quot;\@&amp;quot; geschrieben werden.&lt;br /&gt;
* &#039;&#039;Wav2MP3Fun&#039;&#039; ist eine mit Perl (nicht FHEM!) ausführbare Funktion zum Umwandeln von WAV-Dateien in MP3-Dateien. In diesem Funktionstemplate wird der String INPUT durch den Dateinamen der WAV-Datei, OUTPUT durch den Namen der zu erstellenden MP3-Datei ersetzt. &#039;&#039;&#039;Achtung&#039;&#039;&#039;: Oftmals wird für eine solche Konversion das Paket ffmpeg verwendet. Das kann ziemlich mächtig sein, obwohl für den speziellen Zweck nur ein Bruchteil davon benötigt wird. In dem Wiki-Artikel [[Minimales_ffmpeg]] wird erläutert, wie man eine weniger als 2 MByte große Minimalversion des Konversionsprogramms erzeugen kann.&lt;br /&gt;
Beispiel für diesen Attributwert:&lt;br /&gt;
 attr FritzBoxTAM Wav2MP3Fun system(&#039;ffmpeg -loglevel error -y -i INPUT -metadata title=\&amp;quot;META\&amp;quot; OUTPUT&#039;)&lt;br /&gt;
Hinweis: Die Option &amp;quot;-loglevel error&amp;quot; sorgt dafür dass das FHEM Logfile nicht mit Daten des Konversionsprogramms ffmpeg geflutet wird, die Option &amp;quot;-y&amp;quot; sorgt dafür, dass existierende MP3-Dateien ohne Rückfrage überschrieben werden.&lt;br /&gt;
&lt;br /&gt;
== Bedienung ==&lt;br /&gt;
Nach der Einrichtung wird regelmäßig die FritzBox nach den gespeicherten Nachrichten befragt. Falls Nachrichten vorhanden sind, erfolgt die Anzeige in einer Tabelle.&lt;br /&gt;
* 1.Spalte: Index der Nachricht in der FritzBox = laufende Nummer beginnend bei 0. Ein &amp;quot;*&amp;quot; bei diesem Index zeigt an, dass die Nachricht &amp;quot;neu&amp;quot; ist und noch nicht abgehört wurde.&lt;br /&gt;
* 2.Spalte: Datum, Uhrzeit und Dauer der Nachricht&lt;br /&gt;
* 3.Spalte: Anrufername und Telefonnummer. Falls der Name nicht bekannt ist, wird die Nummer als Name verwendet&lt;br /&gt;
* 4.Spalte: Aktionsbuttons für &lt;br /&gt;
** den Versand per Instant Messenger (nur, wenn das Attribut &#039;&#039;MsgrFun&#039;&#039; gesetzt ist) an alle Empfänger in der &#039;&#039;MsgrRecList&#039;&#039;&lt;br /&gt;
** den Versand per Email (nur, wenn das Attribut &#039;&#039;MailFun&#039;&#039; gesetzt ist) an alle Empfänger in der &#039;&#039;MailRecList&#039;&#039;&lt;br /&gt;
** das Löschen der Nachricht&lt;br /&gt;
** den Download der Nachricht in das durch das Attribut &#039;&#039;targetdir&#039;&#039; bestimmte Verzeichnis&lt;br /&gt;
* &#039;&#039;&#039;Achtung&#039;&#039;&#039;: Die korrekte Funktion der Buttons ist nur sichergestellt, wenn die Datei &#039;&#039;fbtam.js&#039;&#039; im Verzeichnis /opt/fhem/www/pgm2 vorhanden ist.&lt;br /&gt;
&lt;br /&gt;
Kommandozeilenbefehle (bzw. über den Button &amp;quot;set FritzBoxTAM ...&amp;quot; auswählbar sind&lt;br /&gt;
* &#039;&#039;update&#039;&#039; für sofotiges manuelles Update der Anzeige&lt;br /&gt;
* &#039;&#039;deleteMsg &amp;lt;Index der Nachricht&amp;gt;&#039;&#039; für das Löschen&lt;br /&gt;
* &#039;&#039;downloadMsg &amp;lt;Index der Nachricht&amp;gt;&#039;&#039; für den Download&lt;br /&gt;
* &#039;&#039;sendMessengerMsg &amp;lt;Index der Nachricht&amp;gt; [&amp;lt;Empfänger&amp;gt;]&lt;br /&gt;
* &#039;&#039;sendEmailMsg &amp;lt;Index der Nachricht&amp;gt; [&amp;lt;Empfänger&amp;gt;]&lt;br /&gt;
* &#039;&#039;on&#039;&#039; Einschalten, &#039;&#039;off&#039;&#039; Ausschalten des Anrufbeantworters&lt;br /&gt;
* &#039;&#039; username &amp;lt;Username&amp;gt;&#039;&#039; und &#039;&#039;password &amp;lt;Passwort&amp;gt;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== Verwendung mit Telegram ==&lt;br /&gt;
Mit den nachfolgend beschriebenen Codefragmenten kann der Anrufbeantworter über den Instant Messenger Telegram gesteuert und abgehört werden. Das sieht dann ungefähr so aus.&lt;br /&gt;
[[File:Fbtamtg1.png|800px]]&lt;br /&gt;
&lt;br /&gt;
Das Modul 72_FBTAM.pm stellt eine interne Funktion &#039;&#039;FBTAM_renderTgTable&#039;&#039;, bereit, die als einziges Argument den Geräte-Hash des FBTAM-Devices benötigt. Von außen kann man diese Funktion also aufrufen als &lt;br /&gt;
 {FBTAM_renderTgTable($defs{&#039;FritzBoxTAM&#039;})&lt;br /&gt;
und erhält als Rückgabewert etwas von der Form&lt;br /&gt;
  ([2*] Peter, 24.08.25:tam_line 0_2) ([1*] Paul, 23.08.25:tam_line 0_1) ([0] Mary, 22.08.25:tam_line 0_0) ENDMENU Anrufbeantworter=on, 2 neue / 1 alte Nachrichten &lt;br /&gt;
Speichert man diesen Rückgabewert z.B. im Reading &#039;&#039;fbtamSource&#039;&#039; des Telegram-Bots, kann man mit dem nachfolgenden Codefragment eine Übersicht der gespeicherten Nachrichten als Telegram Inline-Keyboard erzeugen:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
    my $cmd = ReadingsVal(&amp;quot;TelegramBot&amp;quot;,&amp;quot;fbtamSource&amp;quot;,&amp;quot;&amp;quot;);&lt;br /&gt;
    $cmd =~ s/ENDMENU/\(Kommunikation\) \(Hauptmenü\)/;&lt;br /&gt;
    if( $cmd =~ /=on/ ){&lt;br /&gt;
      $cmd = &amp;quot;(Ausschalten:tam_switch off) $cmd&amp;quot;;&lt;br /&gt;
    }else{&lt;br /&gt;
      $cmd = &amp;quot;(Einschalten:tam_switch on) $cmd&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
    $cmd = &amp;quot;set TelegramBot queryEditInline $menuMsgId \@$queryPeer $cmd&amp;quot;;&lt;br /&gt;
    fhem(&amp;quot;$cmd&amp;quot;); &lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Dabei wird ENDMENU durch Menüeinträge des Inline-Keyboards ersetzt, die beim Anklicken auf eine höhere Ebene (z.B. &amp;quot;Kommunikation&amp;quot; oder &amp;quot;Hauptmenü&amp;quot; führen sollen.&lt;br /&gt;
Außerdem wird an erster Stelle des erzeugten Telegram Keyboards ein Menüeintrag erzeut, mit dem man den Anrufbeantworter an- bzw. Ausschalten kann.&lt;br /&gt;
&lt;br /&gt;
Klickt man nun in Telegram den Button für An- oder Ausschalten an, wird im Bot eine Message mit Nachrichteninhalt &#039;&#039;tam_switch &amp;lt;on/off&amp;gt;&#039;&#039; registriert. Klickt man nun in Telegram eine der angezeigten Einzelnachrichten an, wird im Bot eine Message mit Nachrichteninhalt &#039;&#039;tam_line &amp;lt;n&amp;gt;_&amp;lt;m&amp;gt;&#039;&#039; empfangen. Darin ist &amp;lt;n&amp;gt; der Index des Anrufbeantworters (0 für AB 1, 1 für AB2, ...) und &amp;lt;m&amp;gt; der Index der Nachricht. Diese Message kann man wiederum abfangen. Angenommen, das erste Wort des Nachrichteninhalt sein in $cb1 gespeichert, das zweite in $cb2, so kann das Codefragment&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
  ...&lt;br /&gt;
  #-- Level 4 - Hauptmenü-&amp;gt;Kommunikation-&amp;gt;Anrufbeantworter-&amp;gt;An/ausschalten&lt;br /&gt;
  }elsif( $cb1 eq &amp;quot;tam_switch&amp;quot;){&lt;br /&gt;
    Log 1,&amp;quot;[telegramRecognition] Level 4 - Hauptmenü-&amp;gt;Kommunikation-&amp;gt;Anrufbeantworter-&amp;gt;An/Ausschalten&amp;quot;&lt;br /&gt;
      if( $debug);&lt;br /&gt;
    fhem(&amp;quot;set FritzBoxTAM $cb2&amp;quot;);&lt;br /&gt;
    ... (hier weitere Aktionen, z.B. re-display der Daten)&lt;br /&gt;
    return;  &lt;br /&gt;
    &lt;br /&gt;
  #-- Level 4 - Hauptmenü-&amp;gt;Kommunikation-&amp;gt;Anrufbeantworter-&amp;gt;Einzelnachricht&lt;br /&gt;
  }elsif( $cb1 =~ /tam_line/){&lt;br /&gt;
    Log 1,&amp;quot;[telegramRecognition] Level 4 - Hauptmenü-&amp;gt;Kommunikation-&amp;gt;Anrufbeantworter-&amp;gt;Nachricht&amp;quot;&lt;br /&gt;
      if( $debug);&lt;br /&gt;
     #-- create array from raw list&lt;br /&gt;
     my $src   = ReadingsVal(&amp;quot;TelegramBot&amp;quot;,&amp;quot;fbtamSource&amp;quot;,&amp;quot;&amp;quot;);&lt;br /&gt;
     $src =~ s/^\(//;&lt;br /&gt;
     $src =~ s/\)\s\w.*$//;&lt;br /&gt;
     my @srces = split /\)\s*\(/,$src;&lt;br /&gt;
     #-- look up msgIndex in this array&lt;br /&gt;
     $cb2 =~ /(\d)_(\d+)/;&lt;br /&gt;
     my $tamIndex=$1;&lt;br /&gt;
     my $msgIndex=$2;&lt;br /&gt;
     my $found=0;&lt;br /&gt;
     my ($si,$sname,$sdate);&lt;br /&gt;
     for( my $i=0;$i&amp;lt; int(@srces);$i++){&lt;br /&gt;
       my $msg=$srces[$i];&lt;br /&gt;
       $msg =~/\[(\d+)\*?\] (\w.*)\, ([\d\.]*)\:.*/;&lt;br /&gt;
       $si=$1;&lt;br /&gt;
       $sname=$2;&lt;br /&gt;
       $sdate=$3;&lt;br /&gt;
       if( $si == $msgIndex){&lt;br /&gt;
         $found=1;&lt;br /&gt;
         last;&lt;br /&gt;
       }&lt;br /&gt;
     }&lt;br /&gt;
     if( $found ){    &lt;br /&gt;
       fhem(&amp;quot;set TelegramBot queryEditInline $menuMsgId \@$queryPeer (Abhören:tam_read $cb2|Löschen:tam_delete $cb2) (Anrufbeantworter:tam_main) (Kommunikation) (Hauptmenü) Nachricht $msgIndex von $sname am $sdate;&amp;quot;);&lt;br /&gt;
     }else{&lt;br /&gt;
       fhem(&amp;quot;set TelegramBot queryEditInline $menuMsgId \@$queryPeer (Anrufbeantworter:tam_main) (Hauptmenü) Kommunikation;&amp;quot;);&lt;br /&gt;
     }&lt;br /&gt;
     return;      &lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
zunächst den Anrufbeantworter an- oder ausschalten oder eine Einzelnachricht anzeigen. Mit dem nachfolgenden Codefragment kann eine Nachricht dann gelöscht oder als MP3-Datei an den Client gesendet werden&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
 #-- Level 5 - Hauptmenü-&amp;gt;Kommunikation-&amp;gt;Anrufbeantworter-&amp;gt;Einzelnachricht-&amp;gt;Abhören &lt;br /&gt;
  }elsif( $cb1 eq &amp;quot;tam_read&amp;quot;){&lt;br /&gt;
    Log 1,&amp;quot;[telegramRecognition] Level 5 - Hauptmenü-&amp;gt;Kommunikation-&amp;gt;Anrufbeantworter-&amp;gt;Einzelnachricht-&amp;gt;Abhören&amp;quot;&lt;br /&gt;
      if( $debug);&lt;br /&gt;
    $cb2 =~ /(\d)_(\d+)/;&lt;br /&gt;
    my $tamIndex=$1;&lt;br /&gt;
    my $msgIndex=$2;&lt;br /&gt;
    fhem(&amp;quot;set FritzBoxTAM sendMessengerMsg $msgIndex&amp;quot;);&lt;br /&gt;
    return;&lt;br /&gt;
    &lt;br /&gt;
  #-- Level 5 - Hauptmenü-&amp;gt;Kommunikation-&amp;gt;Anrufbeantworter-&amp;gt;Einzelnachricht-&amp;gt;Löschen&lt;br /&gt;
  }elsif( $cb1 eq &amp;quot;tam_delete&amp;quot;){&lt;br /&gt;
    Log 1,&amp;quot;[telegramRecognition] Level 5 - Hauptmenü-&amp;gt;Kommunikation-&amp;gt;Anrufbeantworter-&amp;gt;Einzelnachricht-&amp;gt;Löschen&amp;quot;&lt;br /&gt;
      if( $debug);&lt;br /&gt;
    $cb2 =~ /(\d)_(\d+)/;&lt;br /&gt;
    my $tamIndex=$1;&lt;br /&gt;
    my $msgIndex=$2;&lt;br /&gt;
    fhem(&amp;quot;set FritzBoxTAM deleteMsg $msgIndex&amp;quot;);&lt;br /&gt;
    ... (weitere Aktionen, z.B. re-display)&lt;br /&gt;
    return&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:FritzBox]]&lt;/div&gt;</summary>
		<author><name>Ph1959de</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=Stromb%C3%B6rse&amp;diff=40552</id>
		<title>Strombörse</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=Stromb%C3%B6rse&amp;diff=40552"/>
		<updated>2025-12-19T16:01:29Z</updated>

		<summary type="html">&lt;p&gt;Ph1959de: Kategorie und Baustellenmarkierung eingefügt&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Todo|Diese Wiki Seite ist noch in Bearbeitung! &amp;lt;br&amp;gt;Siehe auch {{Link2Forum|Topic=142165|LinkText=HTTPMOD: Aktueller Strompreis EEX Strombörse Leipzig abfragen}}}}&lt;br /&gt;
[[Bild:Screenshot 2024-01-23 122115.png|mini|400px|rechts|EVU_Tibber_connect]]&lt;br /&gt;
Diese Seite beinhaltet Informationen für die Anbindung von Energieversorgungsunternehmen (EVU), die ihre Preise nach der Strombörse z.B. im Stundenrhythmus anbieten.&lt;br /&gt;
&lt;br /&gt;
Als Beispiel wären da:&lt;br /&gt;
# {{Link2Forum|Topic=130407|LinkText=Tibber - im Forum}}&lt;br /&gt;
# {{Link2Forum|Topic=135906|LinkText=aWATTar - im Forum}}&lt;br /&gt;
# {{Link2Forum|Topic=135906|LinkText=EPEX_Spot}}&lt;br /&gt;
&lt;br /&gt;
== 1. Tibber mit Tibber Pulse ==&lt;br /&gt;
Eine momentan verbreitete Methode zur Anbindung an Tibber ist ein Lesekopf auf dem EVU Zähler, der als &amp;quot;Tibber Pulse&amp;quot; vertrieben wird. Damit wird ein bestehender digitaler EVU Zähler ausgelesen und die Werte per LAN zu Tibber gesendet. Über eine API bei Tibber können dann die Verbrauchswerte und Kosten sowie ein Live Stream (WebSocket) abgefragt werden.&lt;br /&gt;
&lt;br /&gt;
Die im Folgenden beschriebenen Beispieldevices bieten die oben genannte Abfrage der API mit WebSocket und [[HTTPMOD]]. Das EVU_Tibber Device dient der Darstellung und Steuerung.&lt;br /&gt;
&lt;br /&gt;
=== Vorausetzungen ===&lt;br /&gt;
* FHEM mit einer installierten MySQL DbLog (FileLog wird nicht unterstützt)&lt;br /&gt;
* KeyValue() (wird hier beschrieben)&lt;br /&gt;
* homeID und personal Token/Access Token/Connect Token&lt;br /&gt;
* Tibber Pulse Lesekopf auf dem digitalen EVU Zähler&lt;br /&gt;
* Man sollte bereits Tibber Kunde sein, da ansonsten nur Demo Daten abgerufen werden können&lt;br /&gt;
&lt;br /&gt;
=== Token ===&lt;br /&gt;
Der Token wird leider unterschiedlich bezeichnet, aber egal ob er personal Token, Access Token oder Connect Token heisst, es bezieht sich auf den gleichen. &lt;br /&gt;
&lt;br /&gt;
Erhältlich ist dieser aktuell (Oktober 24) unter https://developer.tibber.com/ &lt;br /&gt;
&lt;br /&gt;
Dort bitte mit deinen Zugangsdaten einloggen (&amp;quot;Sign In&amp;quot;, oben rechts) und man bekommt den Access Token direkt angezeigt.&lt;br /&gt;
&lt;br /&gt;
=== homeID ===&lt;br /&gt;
Um eine HomeID zu erhalten, muss zuerst der Pulse mit dem Internet bzw. tibber verbunden werden. Wenn die App glücklich ist, gehts hier weiter: &lt;br /&gt;
&lt;br /&gt;
Man loggt sich wieder ein unter https://developer.tibber.com/. Dort geht man dann zum API Explorer. &lt;br /&gt;
&lt;br /&gt;
Leider ist keine der Demo-Abfragen geeignet, die homeID zu erfahren. Dafür gibt man ins linke fenster folgenden Code ein: &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
  viewer {&lt;br /&gt;
    homes {&lt;br /&gt;
      id&lt;br /&gt;
      address {&lt;br /&gt;
        address1&lt;br /&gt;
        postalCode&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Dann startet man mit dem Play-Pfeil und bekommt auf der rechten Seite etwa folgende Ausgabe: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
  &amp;quot;data&amp;quot;: {&lt;br /&gt;
    &amp;quot;viewer&amp;quot;: {&lt;br /&gt;
      &amp;quot;homes&amp;quot;: [&lt;br /&gt;
        {&lt;br /&gt;
          &amp;quot;id&amp;quot;: &amp;quot;1234abcd-12ab-34cd-56ef-1234abcd12ab&lt;br /&gt;
          &amp;quot;address&amp;quot;: {&lt;br /&gt;
            &amp;quot;address1&amp;quot;: &amp;quot;Musterstr.1&amp;quot;,&lt;br /&gt;
            &amp;quot;postalCode&amp;quot;: &amp;quot;12345&amp;quot;&lt;br /&gt;
          }&lt;br /&gt;
        }&lt;br /&gt;
      ]&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Diese ID ist die homeID und kann unten verwendet werden.&lt;br /&gt;
&lt;br /&gt;
=== EVU_Tibber_connect ===&lt;br /&gt;
Das EVU_Tibber_connect fragt mit HTTPMOD Statistiken auf der Tibber API Webseite ab. Zusätzlich wurde in den userReadings die WebSocket Verbindung für die Live Daten integriert.&lt;br /&gt;
* Die WebSocket Verbindung wurde leicht angepasst &#039;&#039;&#039;[[Websocket|von hier]]&#039;&#039;&#039; übernommen&lt;br /&gt;
* Im Forum wurde dies &#039;&#039;&#039;{{Link2Forum|Topic=130407|Message=1298863|LinkText=ab hier}}&#039;&#039;&#039; bearbeitet&lt;br /&gt;
&lt;br /&gt;
==== Verwendete Module ====&lt;br /&gt;
* HTTPMOD&lt;br /&gt;
* DbLog&lt;br /&gt;
* DbRep&lt;br /&gt;
* DOIF&lt;br /&gt;
* KeyValue()&lt;br /&gt;
&lt;br /&gt;
==== Ablage der homeID und des Tokens ====&lt;br /&gt;
Eine Verbindung zur Tibber API mit den &#039;&#039;&#039;tatsächlichen Preisen&#039;&#039;&#039; bekommen nur Kunden, ansonsten kann man eine Demo mit Kosten in Schwedischen Kronen testweise abfragen.&lt;br /&gt;
&lt;br /&gt;
In diesem Beispiel Device kann man seine homeID und sein Token entweder als Attribut ablegen oder man macht einen Eintrag im KeyStore von FHEM, wodurch man nicht so leicht versehentlich seine Daten im Forum postet.&lt;br /&gt;
&lt;br /&gt;
Es dient lediglich der Ablage von Keys, damit sie nicht direkt offen im Datensystem oder im Konfigurationsfile zu finden sind.&lt;br /&gt;
Die Funktion befindet sich in der 99_myUtils.pm .&lt;br /&gt;
&lt;br /&gt;
===== Beispiel KeyValue() =====&lt;br /&gt;
&#039;&#039;&#039;Achtung, der KeyValue ist kein Schutz für Passwort oder Keys!&#039;&#039;&#039;&lt;br /&gt;
Es dient lediglich der Ablage von Keys, damit sie nicht direkt offen im FHEM Device oder im Konfigurationsfile zu finden sind.&lt;br /&gt;
Die Funktion befindet sich in der 99_myUtils .&lt;br /&gt;
&lt;br /&gt;
====== KeyValue() Funktion in der ~/FHEM/99_myUtils.pm ======&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
sub KeyValue {&lt;br /&gt;
    my ($step, $index, $value) = @_;&lt;br /&gt;
    my $key = getUniqueId().$index;&lt;br /&gt;
    my $e_value = &amp;quot;&amp;quot;;&lt;br /&gt;
    my $error;&lt;br /&gt;
&lt;br /&gt;
    if (eval &amp;quot;use Digest::MD5;1&amp;quot;) {&lt;br /&gt;
      $key    = Digest::MD5::md5_hex(unpack &amp;quot;H*&amp;quot;, $key);&lt;br /&gt;
      $key   .= Digest::MD5::md5_hex($key);&lt;br /&gt;
    }&lt;br /&gt;
   &lt;br /&gt;
    if ($step eq &amp;quot;read&amp;quot;) {&lt;br /&gt;
      ($error, $value) = getKeyValue($index);&lt;br /&gt;
&lt;br /&gt;
      if ( defined($error) ) {&lt;br /&gt;
        Log3 $index,3, &amp;quot;$index, can&#039;t read key from FhemUtils/uniqueID: $error&amp;quot;;&lt;br /&gt;
        return undef;&lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
      if ( defined($value) ) {&lt;br /&gt;
        my $dec_value = &#039;&#039;;&lt;br /&gt;
&lt;br /&gt;
        for my $char (map { pack(&#039;C&#039;, hex($_)) } ($value =~ /(..)/g)) {&lt;br /&gt;
          my $decode  = chop($key);&lt;br /&gt;
          $dec_value .= chr(ord($char)^ord($decode));&lt;br /&gt;
          $key        = $decode.$key;&lt;br /&gt;
        }&lt;br /&gt;
        return $dec_value;&lt;br /&gt;
      }&lt;br /&gt;
      else {&lt;br /&gt;
        Log3 $index,3,&amp;quot;$index, no key found in FhemUtils/uniqueID&amp;quot;;&lt;br /&gt;
        return undef;&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if ($step eq &amp;quot;store&amp;quot;) {&lt;br /&gt;
      for my $char (split //, $value) {&lt;br /&gt;
        my $encode = chop($key);&lt;br /&gt;
        $e_value  .= sprintf(&amp;quot;%.2x&amp;quot;,ord($char)^ord($encode));&lt;br /&gt;
        $key       = $encode.$key;&lt;br /&gt;
      }&lt;br /&gt;
      $error = setKeyValue($index, $e_value);&lt;br /&gt;
      return &amp;quot;error while saving key : $error&amp;quot; if(defined($error));&lt;br /&gt;
      return &amp;quot;Key successfully saved in FhemUtils/uniqueID Key $index&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====== Ablegen der homeID und des Token ======&lt;br /&gt;
In der FHEM Kommandozeile legt man seinen Daten wie folgt im KeyValue ab. Hier wären als Beispiel der Demo Zugang von Tibber.&lt;br /&gt;
Das &amp;quot;read&amp;quot; ist dann auch direkt der erste Test, bevor man weiter macht.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
{KeyValue(&amp;quot;store&amp;quot;,&amp;quot;EVU_Tibber_connect_token&amp;quot;,&amp;quot;5K4MVS-OjfWhK_4yrjOlFe1F6kJXPVf7eQYggo8ebAE&amp;quot;)}&lt;br /&gt;
{KeyValue(&amp;quot;store&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;,&amp;quot;96a14971-525a-4420-aae9-e5aedaa129ff&amp;quot;)}&lt;br /&gt;
&lt;br /&gt;
{KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_token&amp;quot;)}&lt;br /&gt;
{KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;)}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Beispiel Attribut für homeID und Token =====&lt;br /&gt;
Oder, sobald das EVU_Tibber_connect Device erstellt wurde, als Attribut.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
attr EVU_Tibber_connect ws_homeId 96a14971-525a-4420-aae9-e5aedaa129ff&lt;br /&gt;
attr EVU_Tibber_connect ws_myId 96a14971-525a-4420-aae9-e5aedaa129ff          &amp;lt;&amp;lt;&amp;lt;&amp;lt; Dies kann wohl eine frei gewählte ID sein, was hier der Einfachheithalber gleich der homeId gesetzt wurde.&lt;br /&gt;
attr EVU_Tibber_connect ws_token 5K4MVS-OjfWhK_4yrjOlFe1F6kJXPVf7eQYggo8ebAE&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== EVU_Tibber_connect RAW Device====&lt;br /&gt;
Das Device EVU_Tibber_connect dient der Verbindung zur Tibber API. Zusätzlich wird für die Abfrage der Live Daten innerhalb der userReadings eine WebSocket Verbindung zu Tibber aufgebaut. Tibber stellt jeweils gegen 14:00 Uhr die Daten für den nächsten Tag bereit, wodurch von 00:00 - 14:00 Uhr diese Information im EVU_Tibber_connect noch nicht angezeigt werden kann.&lt;br /&gt;
Das Device arbeitet über die userReadings mit einem MySQL DbRep Device, was somit eine MySQL DbLog Verwendung im FHEM voraussetzt. &#039;&#039;&#039;Die Verwendung von FileLog ist nicht unterstützt!&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod EVU_Tibber_connect HTTPMOD https://api.tibber.com/v1-beta/gql 0&lt;br /&gt;
attr EVU_Tibber_connect userattr ws_homeId ws_minInterval ws_myId ws_token ws_websocketURL&lt;br /&gt;
attr EVU_Tibber_connect DbLogExclude .*&lt;br /&gt;
attr EVU_Tibber_connect DbLogInclude total_cost_.*,fc0_trigger.*&lt;br /&gt;
attr EVU_Tibber_connect comment Version 2024.01.22 14:00 \&lt;br /&gt;
https://developer.tibber.com/explorer\&lt;br /&gt;
\&lt;br /&gt;
In der FHEM Kommandozeile könnt Ihr das token und die homeID im KeyStore ablegen.\&lt;br /&gt;
Bitte lest dazu das Thema KeyStore im Wiki und legt die Funktion in Eure 99_myUtils.\&lt;br /&gt;
\&lt;br /&gt;
{KeyValue(&amp;quot;store&amp;quot;,&amp;quot;EVU_Tibber_connect_token&amp;quot;,&amp;quot;5K4MVS-OjfWhK_4yrjOlFe1F6kJXPVf7eQYggo8ebAE&amp;quot;)}\&lt;br /&gt;
{KeyValue(&amp;quot;store&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;,&amp;quot;96a14971-525a-4420-aae9-e5aedaa129ff&amp;quot;)}\&lt;br /&gt;
\&lt;br /&gt;
Hierduch kann man einen wirtschaftlicheren trigger_price berechnen lassen:\&lt;br /&gt;
setreading EVU_Tibber compensation_grid &amp;lt;Eure Einspeisevergütung&amp;gt;\&lt;br /&gt;
\&lt;br /&gt;
# Berechnung eines Default Schwellwertes als täglicher Niedrigpreis\&lt;br /&gt;
    (fc_avg - fc_min)  /2 + fc_min\&lt;br /&gt;
\&lt;br /&gt;
# Abschätzung von Wirtschaftlichkeit beim Speicher Laden, falls Tibber zu teuer wird\&lt;br /&gt;
# compensation_grid kann auch auf einen für Euch passenden Wert gesetzt werden\&lt;br /&gt;
    (fc_avg - compensation_grid) *0.85&lt;br /&gt;
attr EVU_Tibber_connect disable 0&lt;br /&gt;
attr EVU_Tibber_connect enableControlSet 1&lt;br /&gt;
attr EVU_Tibber_connect get01-1Name current_currency&lt;br /&gt;
attr EVU_Tibber_connect get01-2Name current_level&lt;br /&gt;
attr EVU_Tibber_connect get01-3Name current_date&lt;br /&gt;
attr EVU_Tibber_connect get01-3OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get01-4Name current_price&lt;br /&gt;
attr EVU_Tibber_connect get01-4OExpr $val *100&lt;br /&gt;
attr EVU_Tibber_connect get01Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {currentSubscription {priceInfo {current {total startsAt currency level}}}}}}&amp;quot; }&lt;br /&gt;
attr EVU_Tibber_connect get01Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get01Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get01JSON data_viewer_home_currentSubscription_priceInfo_current&lt;br /&gt;
attr EVU_Tibber_connect get01Name 01_priceInfo&lt;br /&gt;
attr EVU_Tibber_connect get01URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get02-10Name fc0_03_total&lt;br /&gt;
attr EVU_Tibber_connect get02-11Name fc0_04_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-11OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-12Name fc0_04_total&lt;br /&gt;
attr EVU_Tibber_connect get02-13Name fc0_05_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-13OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-14Name fc0_05_total&lt;br /&gt;
attr EVU_Tibber_connect get02-15Name fc0_06_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-15OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-16Name fc0_06_total&lt;br /&gt;
attr EVU_Tibber_connect get02-17Name fc0_07_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-17OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-18Name fc0_07_total&lt;br /&gt;
attr EVU_Tibber_connect get02-19Name fc0_08_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-19OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-1Name current_date&lt;br /&gt;
attr EVU_Tibber_connect get02-1OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-20Name fc0_08_total&lt;br /&gt;
attr EVU_Tibber_connect get02-21Name fc0_09_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-21OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-22Name fc0_09_total&lt;br /&gt;
attr EVU_Tibber_connect get02-23Name fc0_10_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-23OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-24Name fc0_10_total&lt;br /&gt;
attr EVU_Tibber_connect get02-25Name fc0_11_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-25OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-26Name fc0_11_total&lt;br /&gt;
attr EVU_Tibber_connect get02-27Name fc0_12_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-27OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-28Name fc0_12_total&lt;br /&gt;
attr EVU_Tibber_connect get02-29Name fc0_13_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-29OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-2Name current_price&lt;br /&gt;
attr EVU_Tibber_connect get02-2OExpr $val *100&lt;br /&gt;
attr EVU_Tibber_connect get02-30Name fc0_13_total&lt;br /&gt;
attr EVU_Tibber_connect get02-31Name fc0_14_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-31OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-32Name fc0_14_total&lt;br /&gt;
attr EVU_Tibber_connect get02-33Name fc0_15_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-33OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-34Name fc0_15_total&lt;br /&gt;
attr EVU_Tibber_connect get02-35Name fc0_16_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-35OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-36Name fc0_16_total&lt;br /&gt;
attr EVU_Tibber_connect get02-37Name fc0_17_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-37OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-38Name fc0_17_total&lt;br /&gt;
attr EVU_Tibber_connect get02-39Name fc0_18_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-39OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-3Name fc0_00_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-3OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-40Name fc0_18_total&lt;br /&gt;
attr EVU_Tibber_connect get02-41Name fc0_19_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-41OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-42Name fc0_19_total&lt;br /&gt;
attr EVU_Tibber_connect get02-43Name fc0_20_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-43OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-44Name fc0_20_total&lt;br /&gt;
attr EVU_Tibber_connect get02-45Name fc0_21_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-45OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-46Name fc0_21_total&lt;br /&gt;
attr EVU_Tibber_connect get02-47Name fc0_22_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-47OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-48Name fc0_22_total&lt;br /&gt;
attr EVU_Tibber_connect get02-49Name fc0_23_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-49OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-4Name fc0_00_total&lt;br /&gt;
attr EVU_Tibber_connect get02-50Name fc0_23_total&lt;br /&gt;
attr EVU_Tibber_connect get02-51Name fc1_00_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-51OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-52Name fc1_00_total&lt;br /&gt;
attr EVU_Tibber_connect get02-53Name fc1_01_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-53OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-54Name fc1_01_total&lt;br /&gt;
attr EVU_Tibber_connect get02-55Name fc1_02_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-55OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-56Name fc1_02_total&lt;br /&gt;
attr EVU_Tibber_connect get02-57Name fc1_03_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-57OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-58Name fc1_03_total&lt;br /&gt;
attr EVU_Tibber_connect get02-59Name fc1_04_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-59OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-5Name fc0_01_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-5OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-60Name fc1_04_total&lt;br /&gt;
attr EVU_Tibber_connect get02-61Name fc1_05_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-61OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-62Name fc1_05_total&lt;br /&gt;
attr EVU_Tibber_connect get02-63Name fc1_06_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-63OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-64Name fc1_06_total&lt;br /&gt;
attr EVU_Tibber_connect get02-65Name fc1_07_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-65OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-66Name fc1_07_total&lt;br /&gt;
attr EVU_Tibber_connect get02-67Name fc1_08_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-67OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-68Name fc1_08_total&lt;br /&gt;
attr EVU_Tibber_connect get02-69Name fc1_09_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-69OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-6Name fc0_01_total&lt;br /&gt;
attr EVU_Tibber_connect get02-70Name fc1_09_total&lt;br /&gt;
attr EVU_Tibber_connect get02-71Name fc1_10_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-71OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-72Name fc1_10_total&lt;br /&gt;
attr EVU_Tibber_connect get02-73Name fc1_11_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-73OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-74Name fc1_11_total&lt;br /&gt;
attr EVU_Tibber_connect get02-75Name fc1_12_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-75OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-76Name fc1_12_total&lt;br /&gt;
attr EVU_Tibber_connect get02-77Name fc1_13_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-77OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-78Name fc1_13_total&lt;br /&gt;
attr EVU_Tibber_connect get02-79Name fc1_14_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-79OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-7Name fc0_02_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-7OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-80Name fc1_14_total&lt;br /&gt;
attr EVU_Tibber_connect get02-81Name fc1_15_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-81OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-82Name fc1_15_total&lt;br /&gt;
attr EVU_Tibber_connect get02-83Name fc1_16_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-83OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-84Name fc1_16_total&lt;br /&gt;
attr EVU_Tibber_connect get02-85Name fc1_17_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-85OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-86Name fc1_17_total&lt;br /&gt;
attr EVU_Tibber_connect get02-87Name fc1_18_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-87OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-88Name fc1_18_total&lt;br /&gt;
attr EVU_Tibber_connect get02-89Name fc1_19_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-89OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-8Name fc0_02_total&lt;br /&gt;
attr EVU_Tibber_connect get02-90Name fc1_19_total&lt;br /&gt;
attr EVU_Tibber_connect get02-91Name fc1_20_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-91OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-92Name fc1_20_total&lt;br /&gt;
attr EVU_Tibber_connect get02-93Name fc1_21_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-93OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-94Name fc1_21_total&lt;br /&gt;
attr EVU_Tibber_connect get02-95Name fc1_22_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-95OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-96Name fc1_22_total&lt;br /&gt;
attr EVU_Tibber_connect get02-97Name fc1_23_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-97OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-98Name fc1_23_total&lt;br /&gt;
attr EVU_Tibber_connect get02-9Name fc0_03_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-9OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {currentSubscription {priceInfo {current {total startsAt} today {total startsAt} tomorrow {total startsAt}}}}}}&amp;quot; }&lt;br /&gt;
attr EVU_Tibber_connect get02Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get02Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get02JSON data_viewer_home_currentSubscription_priceInfo&lt;br /&gt;
attr EVU_Tibber_connect get02Name 02_priceAll&lt;br /&gt;
attr EVU_Tibber_connect get02URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get03-1Name nodes_00_00_from&lt;br /&gt;
attr EVU_Tibber_connect get03-1OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get03-2Name nodes_00_00_cost&lt;br /&gt;
attr EVU_Tibber_connect get03-2OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get03-3Name nodes_00_00_consumption&lt;br /&gt;
attr EVU_Tibber_connect get03-4Name nodes_00_01_from&lt;br /&gt;
attr EVU_Tibber_connect get03-4OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get03-50Name nodes_00_01_cost&lt;br /&gt;
attr EVU_Tibber_connect get03-50OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get03-5Name nodes_00_01_cost&lt;br /&gt;
attr EVU_Tibber_connect get03-5OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get03-6Name nodes_00_01_consumption&lt;br /&gt;
attr EVU_Tibber_connect get03-7Name nodes_00_02_from&lt;br /&gt;
attr EVU_Tibber_connect get03-7OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get03-8Name nodes_00_02_cost&lt;br /&gt;
attr EVU_Tibber_connect get03-8OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get03-9Name nodes_00_02_consumption&lt;br /&gt;
attr EVU_Tibber_connect get03Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {consumption(resolution: HOURLY, last: 3) {nodes {from cost consumption }}}}}&amp;quot;}&lt;br /&gt;
attr EVU_Tibber_connect get03Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get03Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get03Name 03_consumption_hour&lt;br /&gt;
attr EVU_Tibber_connect get03RegOpt g&lt;br /&gt;
attr EVU_Tibber_connect get03Regex \{&amp;quot;from&amp;quot;:&amp;quot;([\d+-]+T[\d+:]+\.000[+-][\d+:]+)&amp;quot;,&amp;quot;cost&amp;quot;:(null|\d+\.\d+|0),&amp;quot;consumption&amp;quot;:(null|\d+\.\d+|0)\}&lt;br /&gt;
attr EVU_Tibber_connect get03URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get04-10Name nodes_24_03_from&lt;br /&gt;
attr EVU_Tibber_connect get04-10OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-11Name nodes_24_03_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-11OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-12Name nodes_24_03_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-13Name nodes_24_04_from&lt;br /&gt;
attr EVU_Tibber_connect get04-13OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-14Name nodes_24_04_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-14OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-15Name nodes_24_04_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-16Name nodes_24_05_from&lt;br /&gt;
attr EVU_Tibber_connect get04-16OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-17Name nodes_24_05_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-17OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-18Name nodes_24_05_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-19Name nodes_24_06_from&lt;br /&gt;
attr EVU_Tibber_connect get04-19OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-1Name nodes_24_00_from&lt;br /&gt;
attr EVU_Tibber_connect get04-1OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-20Name nodes_24_06_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-20OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-21Name nodes_24_06_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-22Name nodes_24_07_from&lt;br /&gt;
attr EVU_Tibber_connect get04-22OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-23Name nodes_24_07_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-23OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-24Name nodes_24_07_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-25Name nodes_24_08_from&lt;br /&gt;
attr EVU_Tibber_connect get04-25OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-26Name nodes_24_08_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-26OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-27Name nodes_24_08_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-28Name nodes_24_09_from&lt;br /&gt;
attr EVU_Tibber_connect get04-28OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-29Name nodes_24_09_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-29OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-2Name nodes_24_00_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-2OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-30Name nodes_24_09_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-31Name nodes_24_10_from&lt;br /&gt;
attr EVU_Tibber_connect get04-31OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-32Name nodes_24_10_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-32OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-33Name nodes_24_10_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-34Name nodes_24_11_from&lt;br /&gt;
attr EVU_Tibber_connect get04-34OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-35Name nodes_24_11_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-35OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-36Name nodes_24_11_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-37Name nodes_24_12_from&lt;br /&gt;
attr EVU_Tibber_connect get04-37OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-38Name nodes_24_12_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-38OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-39Name nodes_24_12_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-3Name nodes_24_00_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-40Name nodes_24_13_from&lt;br /&gt;
attr EVU_Tibber_connect get04-40OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-41Name nodes_24_13_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-41OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-42Name nodes_24_13_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-43Name nodes_24_14_from&lt;br /&gt;
attr EVU_Tibber_connect get04-43OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-44Name nodes_24_14_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-44OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-45Name nodes_24_14_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-46Name nodes_24_15_from&lt;br /&gt;
attr EVU_Tibber_connect get04-46OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-47Name nodes_24_15_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-47OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-48Name nodes_24_15_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-49Name nodes_24_16_from&lt;br /&gt;
attr EVU_Tibber_connect get04-49OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-4Name nodes_24_01_from&lt;br /&gt;
attr EVU_Tibber_connect get04-4OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-50Name nodes_24_16_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-50OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-51Name nodes_24_16_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-52Name nodes_24_17_from&lt;br /&gt;
attr EVU_Tibber_connect get04-52OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-53Name nodes_24_17_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-53OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-54Name nodes_24_17_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-55Name nodes_24_18_from&lt;br /&gt;
attr EVU_Tibber_connect get04-55OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-56Name nodes_24_18_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-56OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-57Name nodes_24_18_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-58Name nodes_24_19_from&lt;br /&gt;
attr EVU_Tibber_connect get04-58OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-59Name nodes_24_19_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-59OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-5Name nodes_24_01_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-5OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-60Name nodes_24_19_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-61Name nodes_24_20_from&lt;br /&gt;
attr EVU_Tibber_connect get04-61OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-62Name nodes_24_20_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-62OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-63Name nodes_24_20_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-64Name nodes_24_21_from&lt;br /&gt;
attr EVU_Tibber_connect get04-64OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-65Name nodes_24_21_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-65OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-66Name nodes_24_21_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-67Name nodes_24_22_from&lt;br /&gt;
attr EVU_Tibber_connect get04-67OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-68Name nodes_24_22_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-68OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-69Name nodes_24_22_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-6Name nodes_24_01_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-70Name nodes_24_23_from&lt;br /&gt;
attr EVU_Tibber_connect get04-70OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-71Name nodes_24_23_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-71OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-72Name nodes_24_23_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-7Name nodes_24_02_from&lt;br /&gt;
attr EVU_Tibber_connect get04-7OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-8Name nodes_24_02_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-8OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-9Name nodes_24_02_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {consumption(resolution: HOURLY, last: 24) {nodes {from cost consumption}}}}}&amp;quot;}&lt;br /&gt;
attr EVU_Tibber_connect get04Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get04Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get04Name 04_consumption_hour_24&lt;br /&gt;
attr EVU_Tibber_connect get04RegOpt g&lt;br /&gt;
attr EVU_Tibber_connect get04Regex \{&amp;quot;from&amp;quot;:&amp;quot;([\d+-]+T[\d+:]+\.000[+-][\d+:]+)&amp;quot;,&amp;quot;cost&amp;quot;:(null|\d+\.\d+|0),&amp;quot;consumption&amp;quot;:(null|\d+\.\d+|0)\}&lt;br /&gt;
attr EVU_Tibber_connect get04URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get05AutoNumLen 4&lt;br /&gt;
attr EVU_Tibber_connect get05Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {consumption(resolution: HOURLY, last: 100) {nodes {from cost consumption}}}}}&amp;quot;}&lt;br /&gt;
attr EVU_Tibber_connect get05Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get05Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get05JSON data_viewer_home&lt;br /&gt;
attr EVU_Tibber_connect get05Name 05_consumption_hourly_100&lt;br /&gt;
attr EVU_Tibber_connect get05URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get06-10Name Adresse_07_Telefon&lt;br /&gt;
attr EVU_Tibber_connect get06-11Name Adresse_01_Vorname&lt;br /&gt;
attr EVU_Tibber_connect get06-12Name Adresse_02_Nachname&lt;br /&gt;
attr EVU_Tibber_connect get06-1Name Adresse_03_Strasse&lt;br /&gt;
attr EVU_Tibber_connect get06-2Name Adresse_05_Ort&lt;br /&gt;
attr EVU_Tibber_connect get06-3Name Adresse_06_Land&lt;br /&gt;
attr EVU_Tibber_connect get06-4Name Adresse_05_Ort&lt;br /&gt;
attr EVU_Tibber_connect get06-5Name Adresse_06_Land&lt;br /&gt;
attr EVU_Tibber_connect get06-6Name Adresse_09_latitude&lt;br /&gt;
attr EVU_Tibber_connect get06-7Name Adresse_10_longitude&lt;br /&gt;
attr EVU_Tibber_connect get06-8Name Adresse_04_Plz&lt;br /&gt;
attr EVU_Tibber_connect get06-9Name Adresse_08_eMail&lt;br /&gt;
attr EVU_Tibber_connect get06Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {address {address1 address2 address3 postalCode city country latitude longitude} owner {firstName lastName contactInfo {email mobile}}}}}&amp;quot; }&lt;br /&gt;
attr EVU_Tibber_connect get06Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get06Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get06JSON data_viewer_home&lt;br /&gt;
attr EVU_Tibber_connect get06Name 06_address&lt;br /&gt;
attr EVU_Tibber_connect get06URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get07-10Name nodes_24_03_from&lt;br /&gt;
attr EVU_Tibber_connect get07-10OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-11Name nodes_24_03_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-11OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-12Name nodes_24_03_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-13Name nodes_24_04_from&lt;br /&gt;
attr EVU_Tibber_connect get07-13OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-14Name nodes_24_04_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-14OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-15Name nodes_24_04_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-16Name nodes_24_05_from&lt;br /&gt;
attr EVU_Tibber_connect get07-16OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-17Name nodes_24_05_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-17OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-18Name nodes_24_05_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-19Name nodes_24_06_from&lt;br /&gt;
attr EVU_Tibber_connect get07-19OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-1Name features_realTimeConsumptionEnabled&lt;br /&gt;
attr EVU_Tibber_connect get07-1OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-20Name nodes_24_06_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-20OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-21Name nodes_24_06_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-22Name nodes_24_07_from&lt;br /&gt;
attr EVU_Tibber_connect get07-22OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-23Name nodes_24_07_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-23OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-24Name nodes_24_07_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-25Name nodes_24_08_from&lt;br /&gt;
attr EVU_Tibber_connect get07-25OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-26Name nodes_24_08_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-26OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-27Name nodes_24_08_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-28Name nodes_24_09_from&lt;br /&gt;
attr EVU_Tibber_connect get07-28OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-29Name nodes_24_09_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-29OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-2Name features_id&lt;br /&gt;
attr EVU_Tibber_connect get07-2OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-30Name nodes_24_09_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-31Name nodes_24_10_from&lt;br /&gt;
attr EVU_Tibber_connect get07-31OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-32Name nodes_24_10_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-32OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-33Name nodes_24_10_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-34Name nodes_24_11_from&lt;br /&gt;
attr EVU_Tibber_connect get07-34OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-35Name nodes_24_11_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-35OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-36Name nodes_24_11_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-37Name nodes_24_12_from&lt;br /&gt;
attr EVU_Tibber_connect get07-37OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-38Name nodes_24_12_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-38OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-39Name nodes_24_12_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-3Name nodes_24_00_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-40Name nodes_24_13_from&lt;br /&gt;
attr EVU_Tibber_connect get07-40OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-41Name nodes_24_13_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-41OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-42Name nodes_24_13_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-43Name nodes_24_14_from&lt;br /&gt;
attr EVU_Tibber_connect get07-43OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-44Name nodes_24_14_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-44OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-45Name nodes_24_14_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-46Name nodes_24_15_from&lt;br /&gt;
attr EVU_Tibber_connect get07-46OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-47Name nodes_24_15_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-47OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-48Name nodes_24_15_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-49Name nodes_24_16_from&lt;br /&gt;
attr EVU_Tibber_connect get07-49OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-4Name nodes_24_01_from&lt;br /&gt;
attr EVU_Tibber_connect get07-4OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-50Name nodes_24_16_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-50OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-51Name nodes_24_16_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-52Name nodes_24_17_from&lt;br /&gt;
attr EVU_Tibber_connect get07-52OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-53Name nodes_24_17_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-53OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-54Name nodes_24_17_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-55Name nodes_24_18_from&lt;br /&gt;
attr EVU_Tibber_connect get07-55OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-56Name nodes_24_18_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-56OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-57Name nodes_24_18_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-58Name nodes_24_19_from&lt;br /&gt;
attr EVU_Tibber_connect get07-58OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-59Name nodes_24_19_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-59OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-5Name nodes_24_01_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-5OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-60Name nodes_24_19_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-61Name nodes_24_20_from&lt;br /&gt;
attr EVU_Tibber_connect get07-61OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-62Name nodes_24_20_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-62OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-63Name nodes_24_20_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-64Name nodes_24_21_from&lt;br /&gt;
attr EVU_Tibber_connect get07-64OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-65Name nodes_24_21_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-65OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-66Name nodes_24_21_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-67Name nodes_24_22_from&lt;br /&gt;
attr EVU_Tibber_connect get07-67OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-68Name nodes_24_22_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-68OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-69Name nodes_24_22_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-6Name nodes_24_01_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-70Name nodes_24_23_from&lt;br /&gt;
attr EVU_Tibber_connect get07-70OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-71Name nodes_24_23_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-71OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-72Name nodes_24_23_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-7Name nodes_24_02_from&lt;br /&gt;
attr EVU_Tibber_connect get07-7OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-8Name nodes_24_02_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-8OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-9Name nodes_24_02_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {id features{realTimeConsumptionEnabled} } } }&amp;quot; }&lt;br /&gt;
attr EVU_Tibber_connect get07Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get07Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get07JSON data_viewer_home&lt;br /&gt;
attr EVU_Tibber_connect get07Name 07_realTimeConsumptionEnabled&lt;br /&gt;
attr EVU_Tibber_connect get07RegOpt g&lt;br /&gt;
attr EVU_Tibber_connect get07Regex \{&amp;quot;from&amp;quot;:&amp;quot;([\d+-]+T[\d+:]+\.000[+-][\d+:]+)&amp;quot;,&amp;quot;cost&amp;quot;:(null|\d+\.\d+|0),&amp;quot;consumption&amp;quot;:(null|\d+\.\d+|0)\}&lt;br /&gt;
attr EVU_Tibber_connect get07URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect group PV Steuerung EVU&lt;br /&gt;
attr EVU_Tibber_connect icon stromzaehler_icon&lt;br /&gt;
attr EVU_Tibber_connect replacement01Mode expression&lt;br /&gt;
attr EVU_Tibber_connect replacement01Regex %%token%%&lt;br /&gt;
attr EVU_Tibber_connect replacement01Value {KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_token&amp;quot;)}&lt;br /&gt;
attr EVU_Tibber_connect replacement02Mode expression&lt;br /&gt;
attr EVU_Tibber_connect replacement02Regex %%homeID%%&lt;br /&gt;
attr EVU_Tibber_connect replacement02Value {KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;)}&lt;br /&gt;
attr EVU_Tibber_connect requestData { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {currentSubscription {priceInfo {current {total energy tax startsAt }}}}}}&amp;quot; }&lt;br /&gt;
attr EVU_Tibber_connect requestHeader1 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect requestHeader2 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect room Strom-&amp;gt;Boerse&lt;br /&gt;
attr EVU_Tibber_connect showBody 1&lt;br /&gt;
attr EVU_Tibber_connect showError 1&lt;br /&gt;
attr EVU_Tibber_connect sortby 313&lt;br /&gt;
attr EVU_Tibber_connect timeout 30&lt;br /&gt;
attr EVU_Tibber_connect userReadings ws_connect:ws_cmd:.connect {\&lt;br /&gt;
    my $hash = $defs{$name};;\&lt;br /&gt;
    my $devState = DevIo_IsOpen($hash);;\&lt;br /&gt;
    return &amp;quot;Device already open&amp;quot; if (defined($devState));;\&lt;br /&gt;
    \&lt;br /&gt;
    # establish connection to websocket\&lt;br /&gt;
    # format must also include portnumber if a path is to be specified\&lt;br /&gt;
    $hash-&amp;gt;{DeviceName} = AttrVal($name, &amp;quot;ws_websocketURL&amp;quot;, &amp;quot;wss:echo.websocket.org:443&amp;quot;);;\&lt;br /&gt;
    \&lt;br /&gt;
    # special headers needed for Tibber, see also Developer Tools in Browser\&lt;br /&gt;
    $hash-&amp;gt;{header}{&#039;Sec-WebSocket-Protocol&#039;} = &#039;graphql-transport-ws&#039;;;\&lt;br /&gt;
    $hash-&amp;gt;{header}{&#039;Host&#039;} = &#039;websocket-api.tibber.com&#039;;;\&lt;br /&gt;
    $hash-&amp;gt;{header}{&#039;Origin&#039;} = &#039;https://developer.tibber.com&#039;;;\&lt;br /&gt;
    \&lt;br /&gt;
    # callback function when &amp;quot;select()&amp;quot; signals data for us\&lt;br /&gt;
    # websocket Ping/Pongs are treated in DevIo but still call this function\&lt;br /&gt;
    $hash-&amp;gt;{directReadFn} = sub () {\&lt;br /&gt;
        my $hash = $defs{$name};;\&lt;br /&gt;
        \&lt;br /&gt;
        # we can read without closing the DevIo, because select() signalled data\&lt;br /&gt;
        my $buf = DevIo_SimpleRead($hash);;\&lt;br /&gt;
        \&lt;br /&gt;
        # if read fails, close device\&lt;br /&gt;
        if(!defined($buf)) {\&lt;br /&gt;
            DevIo_CloseDev($hash);;\&lt;br /&gt;
            $buf = &amp;quot;not_connected&amp;quot;;;\&lt;br /&gt;
        }\&lt;br /&gt;
        \&lt;br /&gt;
        #Log(3, &amp;quot;$name:$reading: websocket data: &amp;gt;&amp;gt;&amp;gt;$buf&amp;lt;&amp;lt;&amp;lt;&amp;quot;);;\&lt;br /&gt;
        \&lt;br /&gt;
        # only update our reading if buffer is not empty and if last update is older than minInterval\&lt;br /&gt;
        if ($buf ne &amp;quot;&amp;quot;) {\&lt;br /&gt;
            my $websocketDataAge = ReadingsAge($name, &amp;quot;ws_websocketData&amp;quot;, 3600);;\&lt;br /&gt;
            my $minInterval = AttrVal($name, &amp;quot;ws_minInterval&amp;quot;, 0);;\&lt;br /&gt;
            my $isNext = ($buf =~ /.*id.*type.*next.*payload.*data.*liveMeasurement.*/s);;\&lt;br /&gt;
            \&lt;br /&gt;
            readingsBeginUpdate($hash);;\&lt;br /&gt;
            readingsBulkUpdate($hash, &amp;quot;ws_websocketData&amp;quot;, &amp;quot;$buf&amp;quot;) if ($isNext &amp;amp;&amp;amp; $websocketDataAge &amp;gt; $minInterval);;\&lt;br /&gt;
            readingsBulkUpdate($hash, &amp;quot;ws_websocketData&amp;quot;, &amp;quot;$buf&amp;quot;) if (!$isNext);;\&lt;br /&gt;
            readingsEndUpdate($hash, 1);;\&lt;br /&gt;
            #Log(3, &amp;quot;$name:$reading: websocket data written to reading&amp;quot;);;\&lt;br /&gt;
        }\&lt;br /&gt;
    };;\&lt;br /&gt;
    \&lt;br /&gt;
    # open DevIo websocket\&lt;br /&gt;
    DevIo_OpenDev($hash, 0, undef, sub(){\&lt;br /&gt;
        my ($hash, $error) = @_;;\&lt;br /&gt;
        return &amp;quot;$error&amp;quot; if ($error);;\&lt;br /&gt;
        \&lt;br /&gt;
        my $token = AttrVal($name, &amp;quot;ws_token&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
           $token = KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_token&amp;quot;) if ($token eq &amp;quot;???&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
        DevIo_SimpleWrite($hash, &#039;{&amp;quot;type&amp;quot;:&amp;quot;connection_init&amp;quot;,&amp;quot;payload&amp;quot;:{&amp;quot;token&amp;quot;:&amp;quot;&#039;.$token.&#039;&amp;quot;}}&#039;, 2);;\&lt;br /&gt;
    });;\&lt;br /&gt;
    readingsBulkUpdate($hash, &amp;quot;ws_websocketData&amp;quot;, &amp;quot;&amp;quot;);;\&lt;br /&gt;
    #Log(3, &amp;quot;$name:$reading: websocket data cleared in reading&amp;quot;);;\&lt;br /&gt;
      \&lt;br /&gt;
    return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
ws_disconnect:ws_cmd:.disconnect {\&lt;br /&gt;
    Log(3, &amp;quot;$name: disconnect&amp;quot;);;\&lt;br /&gt;
    my $hash = $defs{$name};;\&lt;br /&gt;
    RemoveInternalTimer($hash);;\&lt;br /&gt;
    DevIo_SimpleRead($hash);;\&lt;br /&gt;
    DevIo_CloseDev($hash);;\&lt;br /&gt;
\&lt;br /&gt;
    return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
ws_onDisconnect {\&lt;br /&gt;
    my $myState = ReadingsVal($name, &amp;quot;state&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
    my $myData = ReadingsVal($name, &amp;quot;ws_websocketData&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
    return if ($myState ne &amp;quot;disconnected&amp;quot; and $myData ne &amp;quot;not_connected&amp;quot;);;\&lt;br /&gt;
    \&lt;br /&gt;
	## timer callback function, called after a few seconds to initiate a reconnect\&lt;br /&gt;
	my $timerFunction = sub() {\&lt;br /&gt;
		my ($arg) = @_;;\&lt;br /&gt;
		my $hash = $defs{$name};;\&lt;br /&gt;
		my $devState = DevIo_IsOpen($hash);;\&lt;br /&gt;
		\&lt;br /&gt;
		# only re-connect if device is not connected\&lt;br /&gt;
		readingsSingleUpdate($hash, &amp;quot;ws_cmd&amp;quot;, &amp;quot;connect&amp;quot;, 1) if (!defined($devState));;\&lt;br /&gt;
	};;\&lt;br /&gt;
	RemoveInternalTimer($name.$reading.&#039;Timer&#039;);;\&lt;br /&gt;
	\&lt;br /&gt;
	# wait a random time before reconnect (exponential backoff TBD):\&lt;br /&gt;
	my $rwait = int(rand(200)) + 30;;\&lt;br /&gt;
	InternalTimer(gettimeofday() + $rwait, $timerFunction, $name.$reading.&#039;Timer&#039;);;\&lt;br /&gt;
	\&lt;br /&gt;
	#set cmd to a new value, informs user and allows to retrigger when timer expires\&lt;br /&gt;
	my $hash = $defs{$name};;\&lt;br /&gt;
	readingsBulkUpdate($hash, &amp;quot;ws_cmd&amp;quot;, &amp;quot;reconnect attempt in $rwait seconds&amp;quot;);;\&lt;br /&gt;
	\&lt;br /&gt;
	return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
ws_onTimeout:ws_websocketData:.* {\&lt;br /&gt;
	#re-establish websocket connection if no data received in the past ten minutes\&lt;br /&gt;
	#but only if our reading &amp;quot;ws_cmd&amp;quot; was not set to the value &amp;quot;disconnect&amp;quot;\&lt;br /&gt;
\&lt;br /&gt;
        #Log(3, &amp;quot;$name:$reading: websocket data triggert ws_onTimeout&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
	#timeout in seconds when the connection is considered dead\&lt;br /&gt;
	my $timeoutTime = 600;;\&lt;br /&gt;
	\&lt;br /&gt;
	# function to execute when timeout expired\&lt;br /&gt;
	# defining the function here in the userReading, allows us to insert variables directly\&lt;br /&gt;
	my $timerFunction = sub() {\&lt;br /&gt;
		my ($arg) = @_;;\&lt;br /&gt;
		my $hash = $defs{$name};;\&lt;br /&gt;
		my $rCmd = ReadingsVal($name, &amp;quot;ws_cmd&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
		my $age  = ReadingsAge($name, &amp;quot;ws_websocketData&amp;quot;, 0);;\&lt;br /&gt;
		\&lt;br /&gt;
		Log(3, &amp;quot;$name: onTimeoutTimer triggered &amp;gt;&amp;gt;$arg&amp;lt;&amp;lt;&amp;quot;);;\&lt;br /&gt;
		\&lt;br /&gt;
		#do not do anything further if disconnect is on purpose\&lt;br /&gt;
		if ( $rCmd eq &amp;quot;disconnect&amp;quot; ) {\&lt;br /&gt;
			Log(3, &amp;quot;$name: ws_cmd was set to disconnect&amp;quot;);;\&lt;br /&gt;
			return;;\&lt;br /&gt;
		}\&lt;br /&gt;
		\&lt;br /&gt;
		# for whatever reason, we triggered to soon (80%)\&lt;br /&gt;
		if ( $age &amp;lt; $timeoutTime*0.8 ) {\&lt;br /&gt;
			Log(3, &amp;quot;$name: ws_websocketData is not outdated&amp;quot;);;\&lt;br /&gt;
			return;;\&lt;br /&gt;
		}\&lt;br /&gt;
		\&lt;br /&gt;
		DevIo_CloseDev($hash);;\&lt;br /&gt;
		Log(3, &amp;quot;$name: onTimeoutTimer closed DevIo...&amp;quot;);;\&lt;br /&gt;
		\&lt;br /&gt;
		readingsSingleUpdate($hash, &amp;quot;ws_cmd&amp;quot;, &amp;quot;connect&amp;quot;, 1);;\&lt;br /&gt;
		Log(3, &amp;quot;$name: onTimeoutTimer set ws_cmd to value &#039;connect&#039;&amp;quot;);;\&lt;br /&gt;
	};;\&lt;br /&gt;
\&lt;br /&gt;
        #Log(3, &amp;quot;$name:$reading: onTimeout function defined&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
	#remove/cancel previous timers, because we got fresh data and countdown starts again\&lt;br /&gt;
	RemoveInternalTimer($name.$reading.&#039;Timer&#039;);;\&lt;br /&gt;
	\&lt;br /&gt;
	#set timer to expire and execute function defined above, give special arg as identifier\&lt;br /&gt;
	InternalTimer(gettimeofday() + $timeoutTime, $timerFunction, $name.$reading.&#039;Timer&#039;);;\&lt;br /&gt;
	\&lt;br /&gt;
	return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
ws_onConnectionAck:ws_websocketData:.*connection_ack.* {\&lt;br /&gt;
    #ws_websocketData contains the string &amp;quot;connection_ack&amp;quot;\&lt;br /&gt;
    Log(3, &amp;quot;$name:$reading: got connection ack&amp;quot;);;\&lt;br /&gt;
    \&lt;br /&gt;
    # do not proceed if connection is lost\&lt;br /&gt;
    my $hash = $defs{$name};;\&lt;br /&gt;
    my $devState = DevIo_IsOpen($hash);;\&lt;br /&gt;
    return &amp;quot;Device not open&amp;quot; if (!defined($devState));;\&lt;br /&gt;
\&lt;br /&gt;
    readingsBulkUpdate($hash, &amp;quot;ws_cmd&amp;quot;, &amp;quot;got connection ack&amp;quot;);;\&lt;br /&gt;
    \&lt;br /&gt;
    my $homeId = AttrVal($name, &amp;quot;ws_homeId&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
       $homeId = KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;) if ($homeId eq &amp;quot;???&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
    my $myId = AttrVal($name, &amp;quot;ws_myId&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
       $myId = KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;) if ($myId eq &amp;quot;???&amp;quot;);;\&lt;br /&gt;
    \&lt;br /&gt;
    # build the query, do it in pieces, the comma at the end caused perl errors\&lt;br /&gt;
    # so we put it together in this not very elegant way\&lt;br /&gt;
    my $json = &#039;{ &amp;quot;id&amp;quot;:&amp;quot;&#039;. $myId .&#039;&amp;quot;, &amp;quot;type&amp;quot;:&amp;quot;subscribe&amp;quot;&#039;.&amp;quot;, &amp;quot;;;\&lt;br /&gt;
    $json .= &#039;&amp;quot;payload&amp;quot;:{&#039;;;\&lt;br /&gt;
    $json .= &#039;&amp;quot;variables&amp;quot;:{}&#039;.&amp;quot;, &amp;quot;;;\&lt;br /&gt;
    $json .= &#039;&amp;quot;extensions&amp;quot;:{}&#039;.&amp;quot;, &amp;quot;;;\&lt;br /&gt;
    $json .= &#039;&amp;quot;query&amp;quot;:&amp;quot;subscription { liveMeasurement( homeId: \&amp;quot;&#039;.$homeId.&#039;\&amp;quot; ) &#039;;;\&lt;br /&gt;
    #$json .= &#039;{ timestamp power accumulatedConsumption accumulatedCost currency minPower averagePower maxPower signalStrength }}&amp;quot;&#039;;;\&lt;br /&gt;
    $json .= &#039;{ timestamp power lastMeterConsumption accumulatedConsumption accumulatedProduction &#039;;;\&lt;br /&gt;
    $json .= &#039;accumulatedProductionLastHour accumulatedCost accumulatedReward currency minPower averagePower maxPower &#039;;;\&lt;br /&gt;
    $json .= &#039;powerProduction powerReactive powerProductionReactive minPowerProduction maxPowerProduction lastMeterProduction &#039;;;\&lt;br /&gt;
    $json .= &#039;powerFactor voltagePhase1 voltagePhase2 voltagePhase3 signalStrength }}&amp;quot;&#039;;;\&lt;br /&gt;
    $json .= &#039;}}&#039;;;\&lt;br /&gt;
    \&lt;br /&gt;
    #send the string via websocket as ASCII\&lt;br /&gt;
    Log(3, &amp;quot;$name:$reading: sending JSON: &amp;gt;&amp;gt;&amp;gt;$json&amp;lt;&amp;lt;&amp;lt;&amp;quot;);;\&lt;br /&gt;
    DevIo_SimpleWrite($hash, $json, 2);;\&lt;br /&gt;
        \&lt;br /&gt;
    return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
ws_onNextLiveMeasurement:ws_websocketData:.*next.*payload.*data.*liveMeasurement.* {\&lt;br /&gt;
    #websocketData contains next-live-measurement-data\&lt;br /&gt;
    my $val = ReadingsVal($name, &amp;quot;ws_websocketData&amp;quot;, &amp;quot;{}&amp;quot;);;\&lt;br /&gt;
    my %res = %{json2nameValue($val, undef, undef, &amp;quot;payload_data_liveMeasurement.*&amp;quot;)};;\&lt;br /&gt;
    \&lt;br /&gt;
    my $ret = &amp;quot;got values for:\n&amp;quot;;;\&lt;br /&gt;
    foreach my $k (sort keys %res) {\&lt;br /&gt;
        $ret .= &amp;quot;$k\n&amp;quot;;;\&lt;br /&gt;
        readingsBulkUpdate($hash, makeReadingName($k), $res{$k});;\&lt;br /&gt;
    }\&lt;br /&gt;
    return $ret;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_TIMESTAMP:nodes_00_00_cost.* {\&lt;br /&gt;
my ($timestamp,$value) = 2x0;;\&lt;br /&gt;
my $tmp = 0;;\&lt;br /&gt;
\&lt;br /&gt;
for (my $loop_last = 0;; $loop_last &amp;lt;= 2;; $loop_last++) {\&lt;br /&gt;
  $timestamp = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_00_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_from&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_00_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_cost&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $value ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    # Eintragen der Kosten für die Stunde\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_cost&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
    # Eintragen des Verbrauchs für die Stunde\&lt;br /&gt;
    $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_00_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_consumption&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_consumption&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
  } else {\&lt;br /&gt;
    $tmp = &amp;quot;null&amp;quot;;;\&lt;br /&gt;
  }\&lt;br /&gt;
} # end for\&lt;br /&gt;
if ($tmp eq &amp;quot;null&amp;quot;) {\&lt;br /&gt;
  $timestamp = $tmp\&lt;br /&gt;
}\&lt;br /&gt;
$timestamp;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_TIMESTAMP:nodes_24_00_cost.* {\&lt;br /&gt;
my ($timestamp,$value) = 2x0;;\&lt;br /&gt;
my $tmp = 0;;\&lt;br /&gt;
\&lt;br /&gt;
for (my $loop_last = 0;; $loop_last &amp;lt;= 23;; $loop_last++) {\&lt;br /&gt;
  $timestamp = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_24_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_from&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_24_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_cost&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $value ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    # Eintragen der Kosten für die Stunde\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_cost&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
    # Eintragen des Verbrauchs für die Stunde\&lt;br /&gt;
    $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_24_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_consumption&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_consumption&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
  } else {\&lt;br /&gt;
    $tmp = &amp;quot;null&amp;quot;;;\&lt;br /&gt;
  }\&lt;br /&gt;
} # end for\&lt;br /&gt;
if ($tmp eq &amp;quot;null&amp;quot;) {\&lt;br /&gt;
  $timestamp = $tmp\&lt;br /&gt;
}\&lt;br /&gt;
$timestamp;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_TIMESTAMP:05_consumption_hourly_100-0001.* {\&lt;br /&gt;
my ($timestamp,$value) = 2x0;;\&lt;br /&gt;
\&lt;br /&gt;
for (my $loop_last = 1;; $loop_last &amp;lt;= 300;; $loop_last += 3) {\&lt;br /&gt;
\&lt;br /&gt;
  $timestamp =  ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;05_consumption_hourly_100-&amp;quot;.sprintf(&amp;quot;%04d&amp;quot;,$loop_last+2),&amp;quot;null&amp;quot;);; # timestamp\&lt;br /&gt;
  $timestamp =~ s/T/ /g ;;\&lt;br /&gt;
  $timestamp =  substr($timestamp,0,19);;\&lt;br /&gt;
  $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;05_consumption_hourly_100-&amp;quot;.sprintf(&amp;quot;%04d&amp;quot;,$loop_last+1),&amp;quot;null&amp;quot;);;  # cost\&lt;br /&gt;
\&lt;br /&gt;
  if ( $value ne &amp;quot;&amp;quot; ) {\&lt;br /&gt;
    $value = round($value,4);;\&lt;br /&gt;
# print $timestamp.&amp;quot; &amp;quot;.$value.&amp;quot;\n&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
    # Eintragen der Kosten für die Stunde\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_cost&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
    # Eintragen des Verbrauchs für die Stunde\&lt;br /&gt;
    $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;05_consumption_hourly_100-&amp;quot;.sprintf(&amp;quot;%04d&amp;quot;,$loop_last),&amp;quot;null&amp;quot;);;  # consumption\&lt;br /&gt;
    $value = round($value,4);;\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_consumption&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
  }\&lt;br /&gt;
} # end for\&lt;br /&gt;
\&lt;br /&gt;
$timestamp;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_cost_avg:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Tages Wertes\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_TIMESTAMP&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(avg(VALUE)*100 AS DECIMAL(4,2)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND TIMESTAMP &amp;gt;= curdate() ;;&amp;quot;) ;;\&lt;br /&gt;
  } else {\&lt;br /&gt;
    ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_cost_avg&amp;quot;,&amp;quot;null&amp;quot;)\&lt;br /&gt;
  }\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_cost_min:nodes_TIMESTAMP.* {\&lt;br /&gt;
##  Ermittlung des minimal Wertes\&lt;br /&gt;
if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_TIMESTAMP&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
  ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(min(VALUE)*100 AS DECIMAL(4,2)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND TIMESTAMP &amp;gt;= curdate() ;;&amp;quot;) ;;\&lt;br /&gt;
} else {\&lt;br /&gt;
  ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_cost_min&amp;quot;,&amp;quot;null&amp;quot;)\&lt;br /&gt;
}\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_cost_max:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Ermittlung des maximal Wertes\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_TIMESTAMP&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(max(VALUE)*100 AS DECIMAL(4,2)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND TIMESTAMP &amp;gt;= curdate() ;;&amp;quot;) ;;\&lt;br /&gt;
  } else {\&lt;br /&gt;
    ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_cost_max&amp;quot;,&amp;quot;null&amp;quot;)\&lt;br /&gt;
  }\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_consumption_day:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Tages Verbrauches\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(sum(VALUE) AS DECIMAL(10,4)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_consumption&#039;\&lt;br /&gt;
             AND date(TIMESTAMP) = curdate() ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_consumption_month:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Monats Verbrauches\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(sum(VALUE) AS DECIMAL(10,4)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_consumption&#039;\&lt;br /&gt;
             AND YEAR(TIMESTAMP) = YEAR(curdate())\&lt;br /&gt;
             AND MONTH(TIMESTAMP) = MONTH(curdate()) ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_consumption_year:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Jahres Verbrauches\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(sum(VALUE) AS DECIMAL(10,4)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_consumption&#039;\&lt;br /&gt;
             AND YEAR(TIMESTAMP) = YEAR(curdate()) ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_avg:current_price.* {\&lt;br /&gt;
## Berechnung des durchschnitt Wertes\&lt;br /&gt;
  my $fc_avg = 0;;\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
\&lt;br /&gt;
  if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {       ## Der nächste Tag ist bereits da\&lt;br /&gt;
    if (AttrVal(&amp;quot;$NAME&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
      Log 3, &amp;quot;$NAME cmd_1  : Tibber Daten für fc1 sind bereits da&amp;quot;;;\&lt;br /&gt;
    }\&lt;br /&gt;
    $fc = 1;;\&lt;br /&gt;
  } # end if\&lt;br /&gt;
\&lt;br /&gt;
    for (my $j=0;;$j&amp;lt;=$fc;;$j++){\&lt;br /&gt;
      for (my $k=0;;$k&amp;lt;=23;;$k++) {                                          ## Summe  berechnen\&lt;br /&gt;
        $fc_avg += ReadingsVal(&amp;quot;$NAME&amp;quot;,sprintf(&amp;quot;fc%d_%02d_total&amp;quot;,$j,$k),0);;\&lt;br /&gt;
        } # end $k\&lt;br /&gt;
    } # end $j\&lt;br /&gt;
\&lt;br /&gt;
    return round($fc_avg / (24 *(1+$fc)) *100 ,2);;                     ## Durchschnitt berechnen\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_med:current_price.* {\&lt;br /&gt;
## Berechnung des median Wertes\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT\&lt;br /&gt;
             cast( (   (SUBSTRING_INDEX(SUBSTRING_INDEX(group_concat(VALUE order by VALUE), &#039;,&#039;, floor(1+((count(VALUE)-1) / 2)))  , &#039;,&#039;, -1))\&lt;br /&gt;
                       + (SUBSTRING_INDEX(SUBSTRING_INDEX(group_concat(VALUE order by VALUE), &#039;,&#039;, ceiling(1+((count(VALUE)-1) / 2))), &#039;,&#039;, -1))\&lt;br /&gt;
                     )/2 *100\&lt;br /&gt;
             AS DECIMAL(4,2))\&lt;br /&gt;
           FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND (   READING=&#039;fc0_total&#039; AND TIMESTAMP &amp;gt;= DATE_FORMAT(NOW(), &#039;%Y-%m-%d 00:00:00&#039;)\&lt;br /&gt;
                     OR READING=&#039;fc1_total&#039; AND TIMESTAMP &amp;gt;= DATE_FORMAT(NOW() + INTERVAL 1 DAY, &#039;%Y-%m-%d 00:00:00&#039;) ) ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_min:current_price.* {\&lt;br /&gt;
##  Ermittlung des minimal Wertes\&lt;br /&gt;
  my $fc_min = 0;;\&lt;br /&gt;
  my $fc_tmp = 0;;\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
\&lt;br /&gt;
  if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {       ## Der nächste Tag ist bereits da\&lt;br /&gt;
    $fc = 1;;\&lt;br /&gt;
  } # end if\&lt;br /&gt;
\&lt;br /&gt;
    for (my $j=0;;$j&amp;lt;=$fc;;$j++){\&lt;br /&gt;
      for (my $k=0;;$k&amp;lt;=23;;$k++) {\&lt;br /&gt;
        $fc_tmp = ReadingsVal(&amp;quot;$NAME&amp;quot;,sprintf(&amp;quot;fc%d_%02d_total&amp;quot;,$j,$k),0);;\&lt;br /&gt;
  \&lt;br /&gt;
        if (($fc_tmp &amp;lt; $fc_min) or ($j == 0 and $k == 0)) {\&lt;br /&gt;
          $fc_min = $fc_tmp;;\&lt;br /&gt;
        }\&lt;br /&gt;
      } # end $k\&lt;br /&gt;
    } # end $j\&lt;br /&gt;
\&lt;br /&gt;
    return round($fc_min *100 ,2);;                             ## Von Euro auf Cent umrechnen\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_max:current_price.* {\&lt;br /&gt;
##  Ermittlung des minimal Wertes\&lt;br /&gt;
  my $fc_max = 0;;\&lt;br /&gt;
  my $fc_tmp = 0;;\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
\&lt;br /&gt;
  if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {       ## Der nächste Tag ist bereits da\&lt;br /&gt;
    $fc = 1;;\&lt;br /&gt;
  } # end if\&lt;br /&gt;
\&lt;br /&gt;
    for (my $j=0;;$j&amp;lt;=$fc;;$j++){\&lt;br /&gt;
      for (my $k=0;;$k&amp;lt;=23;;$k++) {\&lt;br /&gt;
        $fc_tmp = ReadingsVal(&amp;quot;$NAME&amp;quot;,sprintf(&amp;quot;fc%d_%02d_total&amp;quot;,$j,$k),0);;\&lt;br /&gt;
  \&lt;br /&gt;
        if (($fc_tmp &amp;gt; $fc_max) or ($j == 0 and $k == 0)) {\&lt;br /&gt;
          $fc_max = $fc_tmp;;\&lt;br /&gt;
        }\&lt;br /&gt;
      } # end $k\&lt;br /&gt;
    } # end $j\&lt;br /&gt;
\&lt;br /&gt;
    return round($fc_max *100 ,2);;                             ## Von Euro auf Cent umrechnen\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_trigger_price:fc_avg.* {\&lt;br /&gt;
## fc_trigger_price:[fc_avg|compensation_grid].* {\&lt;br /&gt;
  my $fc_avg = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_avg&amp;quot;,0);;\&lt;br /&gt;
  my $fc_min = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_min&amp;quot;,0);;\&lt;br /&gt;
\&lt;br /&gt;
  # Berechnung eines Default Schwellwertes als täglicher Niedrigpreis\&lt;br /&gt;
  my $price_level = round( ($fc_avg - $fc_min)/2 + $fc_min , 1);;\&lt;br /&gt;
  \&lt;br /&gt;
  # Abschätzung von Wirtschaftlichkeit beim Speicher Laden, falls Tibber zu teuer wird\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;compensation_grid&amp;quot;,0) != 0 ) {\&lt;br /&gt;
    my $price_level_battery = round( ($fc_avg - ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;compensation_grid&amp;quot;,0)) *0.85 , 1) ;;\&lt;br /&gt;
    if ( $price_level &amp;gt; $price_level_battery ) {\&lt;br /&gt;
      $price_level = $price_level_battery;;\&lt;br /&gt;
    }\&lt;br /&gt;
  }\&lt;br /&gt;
$price_level;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_start:fc_trigger_price.* {\&lt;br /&gt;
  my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
  my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for (my $loop_hour = $hour;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour)) ;;\&lt;br /&gt;
      }\&lt;br /&gt;
    } # end  for loop_hour\&lt;br /&gt;
\&lt;br /&gt;
  return(&amp;quot;null&amp;quot;);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_stop:fc0_trigger_start.* {\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
  my $loop_hour = 0;;\&lt;br /&gt;
  my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my $fc_trigger_start = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_trigger_start&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  my $fc_trigger_stop = $fc_trigger_start;;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $fc_trigger_start ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    $fc_trigger_start =~ /(\d\d):/;; $fc_trigger_start = $1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for ($loop_hour = $fc_trigger_start;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$fc.&amp;quot;_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        $fc_trigger_stop = sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour) ;;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour));;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
     # wechsel zum nächsten Tag\&lt;br /&gt;
     if ( $loop_hour == 23 and $fc == 0 ) {\&lt;br /&gt;
       $fc = 1;;\&lt;br /&gt;
       $loop_hour = -1;;\&lt;br /&gt;
     }\&lt;br /&gt;
\&lt;br /&gt;
    } # end for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
 \&lt;br /&gt;
  return($fc_trigger_stop);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger:fc0_trigger_stop.* {\&lt;br /&gt;
\&lt;br /&gt;
  # Setzen des Triggers für die aktuelle Stunde\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;current_price&amp;quot;,100)  &amp;lt; ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) ) {\&lt;br /&gt;
    return(&amp;quot;on&amp;quot;)\&lt;br /&gt;
  } else {\&lt;br /&gt;
    return(&amp;quot;off&amp;quot;)\&lt;br /&gt;
  }\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc1_trigger_start:fc_trigger_price.* {\&lt;br /&gt;
  if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {\&lt;br /&gt;
    my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
    # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
    my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for (my $loop_hour = 0;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour)) ;;\&lt;br /&gt;
      }\&lt;br /&gt;
    } # end  for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
  return(&amp;quot;null&amp;quot;);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc1_trigger_stop:fc0_trigger_start.* {\&lt;br /&gt;
  my $loop_hour = 0;;\&lt;br /&gt;
  my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my $fc_trigger_start = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_trigger_start&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  my $fc_trigger_stop = $fc_trigger_start;;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $fc_trigger_start ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    $fc_trigger_start =~ /(\d\d):/;; $fc_trigger_start = $1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for ($loop_hour = $fc_trigger_start;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        $fc_trigger_stop = sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour) ;;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour));;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
    } # end for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
 \&lt;br /&gt;
  return($fc_trigger_stop);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_trigger_max:fc_avg.* {\&lt;br /&gt;
  my $fc_avg = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_avg&amp;quot;,0);;\&lt;br /&gt;
  my $fc_max = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_max&amp;quot;,0);;\&lt;br /&gt;
\&lt;br /&gt;
  # Berechnung eines Schwellwertes als täglichen Maximalpreises\&lt;br /&gt;
  my $price_level = round( ($fc_max - $fc_avg)/2 + $fc_avg , 0);;\&lt;br /&gt;
  \&lt;br /&gt;
$price_level;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_max_start:fc_trigger_max.* {\&lt;br /&gt;
  my $fc_trigger_max = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_max&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger_max Fensters\&lt;br /&gt;
  my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
  my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for (my $loop_hour = $hour;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;gt; $fc_trigger_max ) {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour)) ;;\&lt;br /&gt;
      }\&lt;br /&gt;
    } # end  for loop_hour\&lt;br /&gt;
\&lt;br /&gt;
  return(&amp;quot;null&amp;quot;);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_max_stop:fc0_trigger_max_start.* {\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
  my $loop_hour = 0;;\&lt;br /&gt;
  my $fc_trigger_max = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_max&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my $fc_trigger_max_start = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_trigger_max_start&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  my $fc_trigger_max_stop = $fc_trigger_max_start;;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $fc_trigger_max_start ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    $fc_trigger_max_start =~ /(\d\d):/;; $fc_trigger_max_start = $1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for ($loop_hour = $fc_trigger_max_start;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$fc.&amp;quot;_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;gt; $fc_trigger_max ) {\&lt;br /&gt;
        $fc_trigger_max_stop = sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour) ;;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour));;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
     # wechsel zum nächsten Tag\&lt;br /&gt;
     if ( $loop_hour == 23 and $fc == 0 ) {\&lt;br /&gt;
       $fc = 1;;\&lt;br /&gt;
       $loop_hour = -1;;\&lt;br /&gt;
     }\&lt;br /&gt;
\&lt;br /&gt;
    } # end for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
 \&lt;br /&gt;
  return($fc_trigger_max_stop);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_max:fc0_trigger_max_stop.* {\&lt;br /&gt;
\&lt;br /&gt;
  # Setzen des maximum Triggers für die aktuelle Stunde\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;current_price&amp;quot;,0)  &amp;gt; ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_max&amp;quot;,0) ) {\&lt;br /&gt;
    return(&amp;quot;on&amp;quot;)\&lt;br /&gt;
  } else {\&lt;br /&gt;
    return(&amp;quot;off&amp;quot;)\&lt;br /&gt;
  }\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc1_trigger_max_start:fc_trigger_max.* {\&lt;br /&gt;
  if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {\&lt;br /&gt;
    my $fc_trigger_max = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_max&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
    # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
    my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for (my $loop_hour = 0;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;gt; $fc_trigger_max ) {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour)) ;;\&lt;br /&gt;
      }\&lt;br /&gt;
    } # end  for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
  return(&amp;quot;null&amp;quot;);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc1_trigger_max_stop:fc0_trigger_max_start.* {\&lt;br /&gt;
  my $loop_hour = 0;;\&lt;br /&gt;
  my $fc_trigger_max = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_max&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my $fc_trigger_max_start = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_trigger_max_start&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  my $fc_trigger_max_stop = $fc_trigger_max_start;;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $fc_trigger_max_start ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    $fc_trigger_max_start =~ /(\d\d):/;; $fc_trigger_max_start = $1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for ($loop_hour = $fc_trigger_max_start;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;gt; $fc_trigger_max ) {\&lt;br /&gt;
        $fc_trigger_max_stop = sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour) ;;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour));;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
    } # end for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
 \&lt;br /&gt;
  return($fc_trigger_max_stop);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
total_cost_day:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Tages Wertes\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT CAST(SUM(VALUE) AS DECIMAL(8,2)) AS VALUE\&lt;br /&gt;
           FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND date(TIMESTAMP) = curdate() ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
total_cost_month:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des monats Wertes\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT CAST(SUM(VALUE) AS DECIMAL(8,2)) AS VALUE\&lt;br /&gt;
           FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND YEAR(TIMESTAMP) = YEAR(curdate())\&lt;br /&gt;
             AND MONTH(TIMESTAMP) = MONTH(curdate()) ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
total_cost_year:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Jahres Wertes\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT CAST(SUM(VALUE) AS DECIMAL(8,2)) AS VALUE\&lt;br /&gt;
           FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND YEAR(TIMESTAMP) = YEAR(curdate()) ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_DbLog:fc0_00_total.* {\&lt;br /&gt;
my ($timestamp,$date,$hour,$value,$loop_fc_next) = 5x0;;\&lt;br /&gt;
\&lt;br /&gt;
for (my $loop_fc = 0;; $loop_fc &amp;lt;= 1;; $loop_fc++) {\&lt;br /&gt;
  $loop_fc_next = $loop_fc +1;;\&lt;br /&gt;
  $date = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$loop_fc.&amp;quot;_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  if ($date ne &amp;quot;null&amp;quot;) {\&lt;br /&gt;
    $date =~ /([\d+-]+)/;; $date = $1 ;;\&lt;br /&gt;
    for (my $loop_hour = 0;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $hour = sprintf(&amp;quot;%02d&amp;quot;,$loop_hour);;\&lt;br /&gt;
      $timestamp = $date.&amp;quot; &amp;quot;.$hour.&amp;quot;:00:00&amp;quot;;;\&lt;br /&gt;
      $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$loop_fc.&amp;quot;_&amp;quot;.$hour.&amp;quot;_total&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
      ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
                        INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                          VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;fc&amp;quot;.$loop_fc.&amp;quot;_total&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
                        ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                          VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
    }\&lt;br /&gt;
    if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$loop_fc.&amp;quot;_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) eq ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$loop_fc_next.&amp;quot;_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;)) {\&lt;br /&gt;
      fhem(&amp;quot;deletereading $NAME fc&amp;quot;.$loop_fc_next.&amp;quot;_.*&amp;quot;);;\&lt;br /&gt;
    }\&lt;br /&gt;
  } else {\&lt;br /&gt;
      fhem(&amp;quot;deletereading $NAME fc1_.*&amp;quot;);;\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
ReadingsTimestamp(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
}&lt;br /&gt;
attr EVU_Tibber_connect verbose 0&lt;br /&gt;
attr EVU_Tibber_connect ws_minInterval 54&lt;br /&gt;
attr EVU_Tibber_connect ws_websocketURL wss:websocket-api.tibber.com:443/v1-beta/gql/subscriptions&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== EVU_Tibber RAW Device ====&lt;br /&gt;
Das Device dient der Anzeige und Steuerung des EVU_Tibber_connect Devices.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod EVU_Tibber DOIF ## Startup Befehle für den WebSocket vom EVU_Tibber_connect\&lt;br /&gt;
init\&lt;br /&gt;
{ \&lt;br /&gt;
  Log(0, &amp;quot;$SELF 0   init       : ▶️  EVU_Tibber init&amp;quot;);;\&lt;br /&gt;
  fhem(&amp;quot;setreading EVU_Tibber_connect ws_cmd connect&amp;quot;);; \&lt;br /&gt;
  if (AttrVal(&amp;quot;$SELF&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
      Log 3, &amp;quot;$SELF 0   WebSocket  : ▶️  EVU_Tibber_connect start Websocket&amp;quot;;;\&lt;br /&gt;
  }\&lt;br /&gt;
  Log(0, &amp;quot;$SELF 0   init       : 🏁 EVU_Tibber init done&amp;quot;);;\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
################################################################################################################\&lt;br /&gt;
## EVU_Tibber_connect start/stop WebSocket\&lt;br /&gt;
EVU_Tibber_connect_ws\&lt;br /&gt;
{if( !([$SELF:state] eq &amp;quot;off&amp;quot;)                                           ## DOIF enabled\&lt;br /&gt;
#    or  [$SELF:ui_command_2]                                             ## Hier wird das uiTable select ausgewertet\&lt;br /&gt;
    and [$SELF:ui_command_2] ne &amp;quot;---&amp;quot;\&lt;br /&gt;
   ) {\&lt;br /&gt;
\&lt;br /&gt;
    fhem(&amp;quot;setreading EVU_Tibber_connect ws_cmd &amp;quot;.[?$SELF:ui_command_2]);; \&lt;br /&gt;
\&lt;br /&gt;
    set_Reading(&amp;quot;ui_command_2&amp;quot;,&amp;quot;---&amp;quot;);;                                   ## Hier wird das uiTable select wieder zurückgesetzt, ansonsten\&lt;br /&gt;
                                                                         ## kann das Kommando nicht sofort wiederholt werden\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
################################################################################################################\&lt;br /&gt;
## 1 Scheduling für das Abholen von Tibber Daten\&lt;br /&gt;
1_EVU_Tibber_PriceInfo\&lt;br /&gt;
{if( !([$SELF:state] eq &amp;quot;off&amp;quot;)                                           ## DOIF enabled\&lt;br /&gt;
    and\&lt;br /&gt;
    (     [:03]                                                          ## Kurz nach jeder vollen Stunde\&lt;br /&gt;
    )\&lt;br /&gt;
    or [$SELF:ui_command_1] eq &amp;quot;1_EVU_Tibber_PriceInfo&amp;quot;                  ## Hier wird das uiTable select ausgewertet\&lt;br /&gt;
   ) {\&lt;br /&gt;
\&lt;br /&gt;
  ::CommandGet(undef, &amp;quot;EVU_Tibber_connect 01_priceInfo&amp;quot;);;                ## Preis für die aktuelle Stunde\&lt;br /&gt;
  ::CommandGet(undef, &amp;quot;EVU_Tibber_connect 03_consumption_hour&amp;quot;);;         ## Kosten der letzen drei Stunden\&lt;br /&gt;
  if (AttrVal(&amp;quot;$SELF&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
      Log 3, &amp;quot;$SELF 1   PriceInfo  : Abfrage von Tibber&amp;quot;;;\&lt;br /&gt;
    }\&lt;br /&gt;
\&lt;br /&gt;
    set_Reading(&amp;quot;ui_command_1&amp;quot;,&amp;quot;---&amp;quot;);;                                   ## Hier wird das uiTable select wieder zurückgesetzt, ansonsten\&lt;br /&gt;
                                                                         ## kann das Kommando nicht sofort wiederholt werden\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
################################################################################################################\&lt;br /&gt;
## 2 Scheduling für das Abholen der Tibber Preise\&lt;br /&gt;
2_EVU_Tibber_PriceAll\&lt;br /&gt;
{if( !([$SELF:state] eq &amp;quot;off&amp;quot;)                                           ## DOIF enabled\&lt;br /&gt;
    and\&lt;br /&gt;
    ([00:03] or [14:03]                                                  ## Ab 14:00 Uhr gibt es die Preise für den nächsten Tag\&lt;br /&gt;
    )\&lt;br /&gt;
    or [$SELF:ui_command_1] eq &amp;quot;2_EVU_Tibber_PriceAll&amp;quot;                   ## Hier wird das uiTable select ausgewertet\&lt;br /&gt;
   ) {\&lt;br /&gt;
\&lt;br /&gt;
  ::CommandGet(undef, &amp;quot;EVU_Tibber_connect 02_priceAll&amp;quot;);;\&lt;br /&gt;
  if (AttrVal(&amp;quot;$SELF&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
      Log 3, &amp;quot;$SELF 2   priceAll   : Abfrage von Tibber für den nächsten Tag&amp;quot;;;\&lt;br /&gt;
    }\&lt;br /&gt;
\&lt;br /&gt;
    set_Reading(&amp;quot;ui_command_1&amp;quot;,&amp;quot;---&amp;quot;);;                                   ## Hier wird das uiTable select wieder zurückgesetzt, ansonsten\&lt;br /&gt;
                                                                         ## kann das Kommando nicht sofort wiederholt werden\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
################################################################################################################\&lt;br /&gt;
## 2 Erstellen des Diagramms im uiTable\&lt;br /&gt;
3_EVU_Tibber_Diagramm\&lt;br /&gt;
{if( !([$SELF:state] eq &amp;quot;off&amp;quot;)                                           ## DOIF enabled\&lt;br /&gt;
    and\&lt;br /&gt;
    (     [00:05] or [14:05]                                             ## Kurz nach Mitternacht\&lt;br /&gt;
    )\&lt;br /&gt;
    or [$SELF:ui_command_1] eq &amp;quot;3_EVU_Tibber_Diagramm&amp;quot;                   ## Hier wird das uiTable select ausgewertet\&lt;br /&gt;
   ) {\&lt;br /&gt;
\&lt;br /&gt;
  my (@out) = (&amp;quot;&amp;quot;) x 2;;\&lt;br /&gt;
  my $timestamp;;\&lt;br /&gt;
\&lt;br /&gt;
  for (my $j=0;;$j&amp;lt;=1;;$j++){\&lt;br /&gt;
\&lt;br /&gt;
    if (ReadingsVal(&amp;quot;EVU_Tibber_connect&amp;quot;,sprintf(&amp;quot;fc%d_00_startsAt&amp;quot;,$j),&amp;quot;null&amp;quot;) eq &amp;quot;null&amp;quot;) {       ## Der nächste Tag ist noch nicht da\&lt;br /&gt;
      if (AttrVal(&amp;quot;$SELF&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
        Log 3, &amp;quot;$SELF 3   Diagramm   : Tibber Daten für fc&amp;quot;.$j.&amp;quot; sind noch nicht da&amp;quot;;;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
      $timestamp = POSIX::strftime(&amp;quot;%Y-%m-%d&amp;quot;,localtime(time+86400));;    ## Setze das Datum auf morgen\&lt;br /&gt;
      for (my $k=0;;$k&amp;lt;=24;;$k++) {\&lt;br /&gt;
        $out[$j] .= sprintf(&amp;quot;%s_%02d:00:00 0.0\n&amp;quot;, $timestamp, $k);;      ## Die Daten mit 0 ergänzen\&lt;br /&gt;
      }\&lt;br /&gt;
      $j = 2;;                                                            ## Aus der Schleife springen\&lt;br /&gt;
\&lt;br /&gt;
    } else {\&lt;br /&gt;
\&lt;br /&gt;
      for (my $i=0;;$i&amp;lt;=23;;$i++){\&lt;br /&gt;
        $timestamp = ReadingsVal(&amp;quot;EVU_Tibber_connect&amp;quot;,sprintf(&amp;quot;fc%d_%02d_startsAt&amp;quot;,$j,$i),&amp;quot;&amp;quot;);;\&lt;br /&gt;
        $timestamp =~ s/ /_/g;;\&lt;br /&gt;
        $out[$j] .=$timestamp.&amp;quot; &amp;quot;.::round(ReadingsVal(&amp;quot;EVU_Tibber_connect&amp;quot;,sprintf(&amp;quot;fc%d_%02d_total&amp;quot;,$j, $i),0)*100,1).&amp;quot;\n&amp;quot;;;\&lt;br /&gt;
      } # End $i\&lt;br /&gt;
\&lt;br /&gt;
    } # End if\&lt;br /&gt;
  } # End $j\&lt;br /&gt;
  \&lt;br /&gt;
  if (AttrVal(&amp;quot;$SELF&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
      Log 3, &amp;quot;$SELF 3   Diagramm   : Werte für das Diagramm&amp;quot;;;\&lt;br /&gt;
      print($out[0]);;\&lt;br /&gt;
      print($out[1]);;\&lt;br /&gt;
    }\&lt;br /&gt;
  ## Die readings current_price und current_level dienen hier nur als Trigger Events, und müssen für\&lt;br /&gt;
  ## jedes Diagramm unterschiedlich sein, die Daten werden über $out[] bereit gestellt !!\&lt;br /&gt;
  ::DOIF_modify_card_data(&amp;quot;EVU_Tibber&amp;quot;,&amp;quot;EVU_Tibber_connect&amp;quot;,&amp;quot;current_price&amp;quot;,&amp;quot;bar1day&amp;quot;,0,$out[0]);;\&lt;br /&gt;
  ::DOIF_modify_card_data(&amp;quot;EVU_Tibber&amp;quot;,&amp;quot;EVU_Tibber_connect&amp;quot;,&amp;quot;current_level&amp;quot;,&amp;quot;bar1day&amp;quot;,-86400,$out[1]);;\&lt;br /&gt;
  ## Mit der Abfrage 03_consumption_hour werden die Trigger erneut ausgelöst\&lt;br /&gt;
  ::CommandGet(undef, &amp;quot;EVU_Tibber_connect 01_priceInfo&amp;quot;);;                ## Kosten der letzen drei Stunden\&lt;br /&gt;
##  fhem(&amp;quot;get EVU_Tibber_connect 01_priceInfo&amp;quot;);;                           ## Kosten der letzen drei Stunden\&lt;br /&gt;
\&lt;br /&gt;
    set_Reading(&amp;quot;ui_command_1&amp;quot;,&amp;quot;---&amp;quot;);;                                   ## Hier wird das uiTable select wieder zurückgesetzt, ansonsten\&lt;br /&gt;
                                                                         ## kann das Kommando nicht sofort wiederholt werden\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
&lt;br /&gt;
attr EVU_Tibber DbLogExclude .*&lt;br /&gt;
attr EVU_Tibber comment Version 2023.12.06 13:00 \&lt;br /&gt;
Dieses Device benötigt EVU_Tibber_connect als Verbindung zu Tibber.&lt;br /&gt;
attr EVU_Tibber disable 0&lt;br /&gt;
attr EVU_Tibber group PV Steuerung EVU&lt;br /&gt;
attr EVU_Tibber icon stromzaehler_icon&lt;br /&gt;
attr EVU_Tibber room Strom-&amp;gt;Boerse&lt;br /&gt;
attr EVU_Tibber sortby 315&lt;br /&gt;
attr EVU_Tibber uiState {\&lt;br /&gt;
package ui_Table;;\&lt;br /&gt;
  $TABLE = &amp;quot;style=&#039;width:100%;;&#039;&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
  $TD{0..6}{0} = &amp;quot;style=&#039;border-top-style:solid;;border-bottom-style:solid;;border-left-style:solid;;border-left-width:2px;;border-right-style:solid;;border-color:darkgreen;;border-top-width:2px;;border-bottom-width:2px;;border-right-width:1px;;width:36%;;font-weight:bold;;&#039;&amp;quot;;;\&lt;br /&gt;
  $TD{0..6}{1..4} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
  $TD{0..6}{5} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
&amp;quot;Kommando&amp;lt;dd&amp;gt;Auswahl&amp;lt;/dd&amp;gt;&amp;quot; |widget([$SELF:ui_command_1],&amp;quot;uzsuDropDown,---,1_EVU_Tibber_PriceInfo,2_EVU_Tibber_PriceAll,3_EVU_Tibber_Diagramm&amp;quot;) |&amp;quot;LiveMessurement &amp;lt;br&amp;gt;&amp;quot;.widget([$SELF:ui_command_2],&amp;quot;uzsuDropDown,---,connect,disconnect&amp;quot;)|&amp;quot;&amp;quot;|[EVU_Tibber_connect:ws_cmd]\&lt;br /&gt;
\&lt;br /&gt;
&amp;quot;Strompreis&amp;lt;br&amp;gt;&amp;quot;.card([EVU_Tibber_connect:current_price:bar1day],undef,undef,0,60,90,0,&amp;quot;fc0  &amp;quot;.::ReadingsVal(Device(),&amp;quot;current_currency&amp;quot;,&amp;quot;&amp;quot;),undef,&amp;quot;1&amp;quot;,&amp;quot;130,,,,,,220&amp;quot;).card([EVU_Tibber_connect:current_level:bar1day],undef,undef,0,60,90,0,&amp;quot;fc1  &amp;quot;.::ReadingsVal(Device(),&amp;quot;current_currency&amp;quot;,&amp;quot;&amp;quot;),undef,&amp;quot;1&amp;quot;,&amp;quot;130,,,,,,220&amp;quot;)|\&lt;br /&gt;
&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;nächste 3h&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;quot;.Price(1).&amp;quot;&amp;lt;br&amp;gt;&amp;quot;.Price(2).&amp;quot;&amp;lt;br&amp;gt;&amp;quot;.Price(3)|&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;Statistik fc0&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;quot;.::ReadingsVal(Device(),&amp;quot;fc_min&amp;quot;,0).&amp;quot; min&amp;lt;br&amp;gt;&amp;quot;.::ReadingsVal(Device(),&amp;quot;fc_avg&amp;quot;,0).&amp;quot; avg&amp;lt;br&amp;gt;&amp;quot;.::ReadingsVal(Device(),&amp;quot;fc_med&amp;quot;,0).&amp;quot; med&amp;lt;br&amp;gt;&amp;quot;.::ReadingsVal(Device(),&amp;quot;fc_max&amp;quot;,0).&amp;quot; max&amp;quot;|\&lt;br /&gt;
&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;Trigger fc0&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;Basis &amp;quot;.widget([EVU_Tibber_connect:compensation_grid],&amp;quot;selectnumbers,0,0.1,12,1,lin&amp;quot;).&amp;quot;&amp;lt;br&amp;gt;&amp;quot;.Format(&amp;quot;trigger_0&amp;quot;)|\&lt;br /&gt;
&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;Trigger fc1&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;quot;.Format(&amp;quot;trigger_1&amp;quot;)&lt;br /&gt;
attr EVU_Tibber uiTable {\&lt;br /&gt;
package ui_Table;;\&lt;br /&gt;
  $TABLE = &amp;quot;style=&#039;width:100%;;&#039;&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
   $TD{0..18}{0} = &amp;quot;style=&#039;border-top-style:solid;;border-bottom-style:solid;;border-left-style:solid;;border-left-width:2px;;border-right-style:solid;;border-color:darkgreen;;border-top-width:2px;;border-bottom-width:2px;;border-right-width:1px;;width:36%;;font-weight:bold;;&#039;&amp;quot;;;\&lt;br /&gt;
  $TD{0..18}{1..5} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
  $TD{0..18}{6} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
sub FUNC_Status {\&lt;br /&gt;
    my($value, $min, $colorMin,  $statusMin,  $colorMiddel, $statusMiddle, $max, $colorMax, $statusMax)=@_;;\&lt;br /&gt;
    my $ret = ($value &amp;lt; $min)? &#039;&amp;lt;span style=&amp;quot;color:&#039;.$colorMin.&#039;&amp;quot;&amp;gt;&#039;.$statusMin.&#039;&amp;lt;/span&amp;gt;&#039; : ($value &amp;gt; $max)? &#039;&amp;lt;span style=&amp;quot;color:&#039;.$colorMax.&#039;&amp;quot;&amp;gt;&#039;.$statusMax.&#039;&amp;lt;/span&amp;gt;&#039; : &#039;&amp;lt;span style=&amp;quot;color:&#039;.$colorMiddel.&#039;&amp;quot;&amp;gt;&#039;.$statusMiddle.&#039;&amp;lt;/span&amp;gt;&#039;;;\&lt;br /&gt;
    return $ret;;\&lt;br /&gt;
  }\&lt;br /&gt;
\&lt;br /&gt;
sub Device {\&lt;br /&gt;
    return &amp;quot;$SELF&amp;quot;.&amp;quot;_connect&amp;quot;;;\&lt;br /&gt;
  }\&lt;br /&gt;
\&lt;br /&gt;
sub Cost {\&lt;br /&gt;
  my($i)=@_;;\&lt;br /&gt;
  my $currency = (::ReadingsVal(Device(),&amp;quot;current_currency&amp;quot;,&amp;quot;&amp;quot;) eq &amp;quot;EUR&amp;quot;)? &amp;quot; €&amp;quot; : &amp;quot;&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
  return ::ReadingsVal(Device(),&amp;quot;total_cost_&amp;quot;.$i,0).$currency;;\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
sub Price {\&lt;br /&gt;
  my($i)=@_;;\&lt;br /&gt;
  my $j;;\&lt;br /&gt;
  my $value;;\&lt;br /&gt;
  my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
\&lt;br /&gt;
  if ($i == 0) {\&lt;br /&gt;
    $value = ::ReadingsVal(Device(),&amp;quot;current_price&amp;quot;,0);;\&lt;br /&gt;
    $value = ( ::ReadingsVal(Device(),&amp;quot;fc0_trigger&amp;quot;,&amp;quot;off&amp;quot;) eq &amp;quot;on&amp;quot; ) ?\&lt;br /&gt;
         &amp;quot;&amp;lt;span style=&#039;color:green&#039;&amp;gt;&amp;quot;.$value.&amp;quot;&amp;lt;/span&amp;gt;&amp;quot; :\&lt;br /&gt;
         &amp;quot;&amp;lt;span style=&#039;color:red&#039;&amp;gt;&amp;quot;.$value.&amp;quot;&amp;lt;/span&amp;gt;&amp;quot; ;;\&lt;br /&gt;
    $value .= &amp;quot; ct/kWh&amp;quot;;;\&lt;br /&gt;
  } else {\&lt;br /&gt;
    $j       = $i+$hour;;\&lt;br /&gt;
    if ($j &amp;lt; 24) {\&lt;br /&gt;
      $value = ::round(::ReadingsVal(Device(),&amp;quot;fc0_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$j).&amp;quot;_total&amp;quot;,0)*100,1);;\&lt;br /&gt;
    } else {\&lt;br /&gt;
      $j = $j - 24;;\&lt;br /&gt;
      $value = ::round(::ReadingsVal(Device(),&amp;quot;fc1_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$j).&amp;quot;_total&amp;quot;,0)*100,1);;\&lt;br /&gt;
    }\&lt;br /&gt;
    $value = ( ::ReadingsVal(Device(),&amp;quot;fc_trigger_price&amp;quot;,&amp;quot;0&amp;quot;) &amp;gt; $value ) ?\&lt;br /&gt;
         &amp;quot;&amp;lt;span style=&#039;color:green&#039;&amp;gt;&amp;quot;.$value.&amp;quot;&amp;lt;/span&amp;gt;&amp;quot; :\&lt;br /&gt;
         &amp;quot;&amp;lt;span style=&#039;color:red&#039;&amp;gt;&amp;quot;.$value.&amp;quot;&amp;lt;/span&amp;gt;&amp;quot; ;;\&lt;br /&gt;
    $value = sprintf(&amp;quot;%02d&amp;quot;,$j).&amp;quot; :  &amp;quot;.$value ;;\&lt;br /&gt;
  }\&lt;br /&gt;
  return  $value;;\&lt;br /&gt;
 }\&lt;br /&gt;
\&lt;br /&gt;
sub Format {\&lt;br /&gt;
  my($i)=@_;;\&lt;br /&gt;
\&lt;br /&gt;
  my $MonthBefore   = &amp;quot;LogDBRep_Statistic_previous_Month&amp;quot;;;\&lt;br /&gt;
  my $MonthPrevious = ::ReadingsTimestamp(&amp;quot;$MonthBefore&amp;quot;,&amp;quot;EVU_Tibber_Pulse_nodes_consumption_month&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
       $MonthPrevious = ($MonthPrevious ne &amp;quot;null&amp;quot;) ?    POSIX::strftime(&amp;quot;%Y&amp;quot;,localtime(::time_str2num(::ReadingsTimestamp(&amp;quot;$MonthBefore&amp;quot;,&amp;quot;EVU_Tibber_Pulse_nodes_consumption_month&amp;quot;,&amp;quot;null&amp;quot;)))) : &amp;quot;null&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
  my $YearBefore   = &amp;quot;LogDBRep_Statistic_previous_Year&amp;quot;;;\&lt;br /&gt;
  my $YearPrevious = ::ReadingsTimestamp(&amp;quot;$YearBefore&amp;quot;,Device().&amp;quot;_nodes_consumption_year&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
       $YearPrevious = ($YearPrevious ne &amp;quot;null&amp;quot;) ? POSIX::strftime(&amp;quot;%Y&amp;quot;,localtime(::time_str2num(::ReadingsTimestamp(&amp;quot;$YearBefore&amp;quot;,Device().&amp;quot;_nodes_consumption_year&amp;quot;,&amp;quot;null&amp;quot;)))) : &amp;quot;null&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
  if ($i eq &amp;quot;day&amp;quot;) {\&lt;br /&gt;
      return sprintf(&amp;quot;%04d&amp;quot;,::ReadingsVal(Device(),&amp;quot;nodes_consumption_day&amp;quot;,0));;\&lt;br /&gt;
    } elsif ($i eq &amp;quot;month&amp;quot;) {\&lt;br /&gt;
      my $evu_em = sprintf(&amp;quot;%04d&amp;quot;,::ReadingsVal(Device(),&amp;quot;nodes_consumption_month&amp;quot;,0));;\&lt;br /&gt;
      $evu_em .= ($MonthPrevious ne &amp;quot;null&amp;quot;) ? sprintf(&amp;quot; / %04d&amp;quot;, ::ReadingsVal(&amp;quot;$MonthBefore&amp;quot;,&amp;quot;EVU_Tibber_Pulse_nodes_consumption_month&amp;quot;,0) ) : &amp;quot;&amp;quot;;;\&lt;br /&gt;
      return $evu_em;;\&lt;br /&gt;
    } elsif ($i eq &amp;quot;year&amp;quot;) {\&lt;br /&gt;
      my $evu_ey = sprintf(&amp;quot;%04d&amp;quot;,::ReadingsVal(Device(),&amp;quot;nodes_consumption_year&amp;quot;,0));;\&lt;br /&gt;
      $evu_ey .= ($YearPrevious ne &amp;quot;null&amp;quot;) ? sprintf(&amp;quot; / %04d&amp;quot;, ::ReadingsVal(&amp;quot;$YearBefore&amp;quot;,&amp;quot;EVU_Tibber_Pulse_nodes_consumption_month&amp;quot;,0) ) : &amp;quot;&amp;quot;;;\&lt;br /&gt;
      return $evu_ey;;\&lt;br /&gt;
    } elsif ($i eq &amp;quot;trigger_0&amp;quot;) {\&lt;br /&gt;
      return ((::ReadingsVal(Device(),&amp;quot;fc0_trigger_start&amp;quot;,&amp;quot;&amp;quot;) eq &amp;quot;null&amp;quot; ) ?\&lt;br /&gt;
            &amp;quot;&amp;lt;span style=&#039;color:red&#039;&amp;gt;Heute kein Trigger &amp;lt;br&amp;gt;mehr unter &amp;quot;.\&lt;br /&gt;
                 ::ReadingsVal(Device(),&amp;quot;fc_trigger_price&amp;quot;,&amp;quot;null&amp;quot;).&amp;quot; ct&amp;lt;/span&amp;gt;&amp;quot; :\&lt;br /&gt;
            &amp;quot;&amp;lt;span style=&#039;color:green&#039;&amp;gt;Trigger von&amp;lt;br&amp;gt;&amp;quot;.\&lt;br /&gt;
                 ::ReadingsVal(Device(),&amp;quot;fc0_trigger_start&amp;quot;,&amp;quot;00:00&amp;quot;).&amp;quot; bis &amp;quot;.::ReadingsVal(Device(),&amp;quot;fc0_trigger_stop&amp;quot;,&amp;quot;00:00&amp;quot;).&amp;quot;&amp;lt;br&amp;gt;unter &amp;quot;.\&lt;br /&gt;
                 ::ReadingsVal(Device(),&amp;quot;fc_trigger_price&amp;quot;,&amp;quot;null&amp;quot;).&amp;quot; ct&amp;lt;/span&amp;gt;&amp;quot; );;\&lt;br /&gt;
    } elsif ($i eq &amp;quot;trigger_1&amp;quot;) {\&lt;br /&gt;
      if (::ReadingsVal(Device(),&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {\&lt;br /&gt;
        return ((::ReadingsVal(Device(),&amp;quot;fc1_trigger_start&amp;quot;,&amp;quot;null&amp;quot;) eq &amp;quot;null&amp;quot; ) ?\&lt;br /&gt;
              &amp;quot;&amp;lt;span style=&#039;color:red&#039;&amp;gt;Morgen kein Trigger &amp;lt;br&amp;gt;mehr unter &amp;quot;.\&lt;br /&gt;
                   ::ReadingsVal(Device(),&amp;quot;fc_trigger_price&amp;quot;,&amp;quot;null&amp;quot;).&amp;quot; ct&amp;lt;/span&amp;gt;&amp;quot; :\&lt;br /&gt;
              &amp;quot;&amp;lt;span style=&#039;color:green&#039;&amp;gt;Morgen ein Trigger von&amp;lt;br&amp;gt;&amp;quot;.\&lt;br /&gt;
                   ::ReadingsVal(Device(),&amp;quot;fc1_trigger_start&amp;quot;,&amp;quot;00:00&amp;quot;).&amp;quot; bis &amp;quot;.::ReadingsVal(Device(),&amp;quot;fc1_trigger_stop&amp;quot;,&amp;quot;00:00&amp;quot;).&amp;quot;&amp;lt;br&amp;gt;unter &amp;quot;.\&lt;br /&gt;
                   ::ReadingsVal(Device(),&amp;quot;fc_trigger_price&amp;quot;,&amp;quot;null&amp;quot;).&amp;quot; ct&amp;lt;/span&amp;gt;&amp;quot; );;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return &amp;quot;Morgen noch kein Trigger vorhanden&amp;quot;;;\&lt;br /&gt;
      }\&lt;br /&gt;
    }\&lt;br /&gt;
  return &amp;quot;null&amp;quot;;;\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
&amp;quot;Statistiken &amp;quot;.::ReadingsVal(Device(),&amp;quot;current_date&amp;quot;,0).&amp;quot; in kWh&amp;quot;|&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;aktuell&amp;lt;/span&amp;gt;&amp;quot;|&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;heute&amp;lt;/span&amp;gt;&amp;quot;|&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;Monat&amp;lt;/span&amp;gt;&amp;quot;|&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;Jahr&amp;lt;/span&amp;gt;&amp;quot;\&lt;br /&gt;
\&lt;br /&gt;
&amp;quot;Strom&amp;lt;dd&amp;gt;Preis / Kosten&amp;lt;/dd&amp;gt;&amp;quot;|Price(0)|Cost(&amp;quot;day&amp;quot;)|Cost(&amp;quot;month&amp;quot;)|Cost(&amp;quot;year&amp;quot;)\&lt;br /&gt;
\&lt;br /&gt;
## Wenn man das liveMessurment von Tibber verwendet\&lt;br /&gt;
&amp;quot;Bezug vom Netz&amp;quot;|\&lt;br /&gt;
sprintf(&amp;quot;%04d W&amp;quot;,([EVU_Tibber_connect:payload_data_liveMeasurement_power]  &amp;gt;= 0 ? ::ReadingsVal(&amp;quot;EVU_Tibber_connect&amp;quot;,&amp;quot;payload_data_liveMeasurement_power&amp;quot;,0) : 0))|\&lt;br /&gt;
Format(&amp;quot;day&amp;quot;)|Format(&amp;quot;month&amp;quot;)|Format(&amp;quot;year&amp;quot;)\&lt;br /&gt;
\&lt;br /&gt;
&amp;quot;Einspeisung ins Netz&amp;quot;|\&lt;br /&gt;
sprintf(&amp;quot;%04d W&amp;quot;,([EVU_Tibber_connect:payload_data_liveMeasurement_powerProduction]  &amp;gt;= 0 ? ::ReadingsVal(&amp;quot;EVU_Tibber_connect&amp;quot;,&amp;quot;payload_data_liveMeasurement_powerProduction&amp;quot;,0) : 0))|\&lt;br /&gt;
&amp;quot;&amp;quot;|&amp;quot;&amp;quot;|&amp;quot;&amp;quot;\&lt;br /&gt;
\&lt;br /&gt;
## Wenn man einen eigenes SmartMeter verwendet\&lt;br /&gt;
## &amp;quot;Bezug vom Netz&amp;quot;|sprintf(&amp;quot;%04d W&amp;quot;,([WR_0_KSEM:M_AC_Power] &amp;gt;= 0 ? ::round(::ReadingsVal(&amp;quot;WR_0_KSEM&amp;quot;,&amp;quot;M_AC_Power&amp;quot;,0),0) : 0) )|Format(&amp;quot;day&amp;quot;)|Format(&amp;quot;month&amp;quot;)|Format(&amp;quot;year&amp;quot;)\&lt;br /&gt;
## &amp;quot;Einspeisung ins Netz&amp;quot;|sprintf(&amp;quot;%04d W&amp;quot;,([WR_0_KSEM:M_AC_Power] &amp;lt;= 0 ? abs(::round(::ReadingsVal(&amp;quot;WR_0_KSEM&amp;quot;,&amp;quot;M_AC_Power&amp;quot;,0),0)) :  0) )|&amp;quot;&amp;quot;|&amp;quot;&amp;quot;|&amp;quot;&amp;quot;&lt;br /&gt;
attr EVU_Tibber verbose 3&lt;br /&gt;
&lt;br /&gt;
setstate EVU_Tibber 2024-01-23 10:03:00 ui_command_1 ---&lt;br /&gt;
setstate EVU_Tibber 2024-01-20 17:40:53 ui_command_2 ---&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== LogDBRep_EVU_Tibber_connect_SQL RAW Device ====&lt;br /&gt;
Dieses DbRep Device dient der Verbindung zur MySQL DbLog. Die SQL SELECT Statements werden im EVU_Tibber_connect Device generiert und über dieses Device in der MySQL DbLog ausgeführt. Der Name des Devices wird ebenfalls generiert und liegt somit fest.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod LogDBRep_EVU_Tibber_connect_SQL DbRep LogDB&lt;br /&gt;
attr LogDBRep_EVU_Tibber_connect_SQL DbLogExclude .*&lt;br /&gt;
attr LogDBRep_EVU_Tibber_connect_SQL room System&lt;br /&gt;
attr LogDBRep_EVU_Tibber_connect_SQL sqlCmdHistoryLength 5&lt;br /&gt;
attr LogDBRep_EVU_Tibber_connect_SQL timeout 20&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 2. aWATTar ==&lt;br /&gt;
=== Vorausetzungen ===&lt;br /&gt;
* FHEM mit einer installierten MySQL DbLog (FileLog wird nicht unterstützt)&lt;br /&gt;
* Für die Berechnung der tatsächlichen Kosten benötigt man seine lokalen Fixkosten, die je nach Region unterschiedlich sind.&lt;br /&gt;
* Die bisherige Berechnung mit Stand 20240123 wäre wie folgt: round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
&lt;br /&gt;
=== EVU_aWATTar_connect ===&lt;br /&gt;
Das EVU_aWATTar_connect fragt mit HTTPMOD die aWATTar API Webseite ab. Es werden keine Live Verbrauchdaten geliefert.&lt;br /&gt;
&lt;br /&gt;
==== Verwendete Module ====&lt;br /&gt;
* HTTPMOD&lt;br /&gt;
* DbLog&lt;br /&gt;
* DbRep&lt;br /&gt;
&lt;br /&gt;
==== EVU_aWATTar_connect  RAW Device ====&lt;br /&gt;
Momentan fragt das EVU_aWattar_connect lediglich die aktuellen Preise des Tages ab. Die readings sind an die des EVU_Tibber_connect angelehnt, um im weiteren Verlauf eventuell ein gemeinsames Device für die Anzeige und Steuerung zu erarbeiten.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod EVU_aWATTar_connect HTTPMOD https://api.awattar.de/v1/marketdata/current.yaml 0&lt;br /&gt;
attr EVU_aWATTar_connect DbLogExclude .*&lt;br /&gt;
attr EVU_aWATTar_connect comment Version 2024.01.23 13:00\&lt;br /&gt;
\&lt;br /&gt;
Achtung, momentan werden nur die Börsenpreise ohne die fix Kosten dargestellt.\&lt;br /&gt;
\&lt;br /&gt;
https://api.awattar.de/v1/marketdata&lt;br /&gt;
attr EVU_aWATTar_connect enableControlSet 1&lt;br /&gt;
attr EVU_aWATTar_connect get01-15Name fc_max&lt;br /&gt;
attr EVU_aWATTar_connect get01-15OExpr round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
attr EVU_aWATTar_connect get01-1Name current_date&lt;br /&gt;
attr EVU_aWATTar_connect get01-22Name fc_med&lt;br /&gt;
attr EVU_aWATTar_connect get01-22OExpr round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
attr EVU_aWATTar_connect get01-29Name fc_avg&lt;br /&gt;
attr EVU_aWATTar_connect get01-29OExpr round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
attr EVU_aWATTar_connect get01-36Name current_price&lt;br /&gt;
attr EVU_aWATTar_connect get01-36OExpr round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
attr EVU_aWATTar_connect get01-8Name fc_min&lt;br /&gt;
attr EVU_aWATTar_connect get01-8OExpr round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
attr EVU_aWATTar_connect get01Name 01_priceInfo&lt;br /&gt;
attr EVU_aWATTar_connect get01RegOpt g&lt;br /&gt;
attr EVU_aWATTar_connect get01Regex date_now: (\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2})|price_low: ([0-9]+[0-9\.]+)|price_high: ([0-9]+[0-9\.]+)|price_median: ([0-9]+[0-9\.]+)|price_average: ([0-9]+[0-9\.]+)|price_current: ([0-9]+[0-9\.]+)&lt;br /&gt;
attr EVU_aWATTar_connect get01URL https://api.awattar.de/v1/marketdata/current.yaml&lt;br /&gt;
attr EVU_aWATTar_connect get02-10Name fc0_09_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-10OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-11Name fc0_10_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-11OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-12Name fc0_11_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-12OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-13Name fc0_12_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-13OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-14Name fc0_13_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-14OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-15Name fc0_14_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-15OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-16Name fc0_15_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-16OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-17Name fc0_16_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-17OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-18Name fc0_17_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-18OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-19Name fc0_18_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-19OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-1Name fc0_00_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-1OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-20Name fc0_19_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-20OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-21Name fc0_20_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-21OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-22Name fc0_21_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-22OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-23Name fc0_22_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-23OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-24Name fc0_23_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-24OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-2Name fc0_01_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-2OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-3Name fc0_02_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-3OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-4Name fc0_03_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-4OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-5Name fc0_04_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-5OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-6Name fc0_05_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-6OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-7Name fc0_06_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-7OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-8Name fc0_07_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-8OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-9Name fc0_08_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-9OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02FollowGet 01_priceInfo&lt;br /&gt;
attr EVU_aWATTar_connect get02Name 02_priceDay&lt;br /&gt;
attr EVU_aWATTar_connect get02RegOpt g&lt;br /&gt;
attr EVU_aWATTar_connect get02Regex (?&amp;lt;=abs_[0-9]{2}_amount: )([0-9]+[0-9\.]+)&lt;br /&gt;
attr EVU_aWATTar_connect get02URL https://api.awattar.de/v1/marketdata/current.yaml&lt;br /&gt;
attr EVU_aWATTar_connect get03-1Name fc3_00_startsAt&lt;br /&gt;
attr EVU_aWATTar_connect get03-1OExpr POSIX::strftime(&amp;quot;%Y-%m-%d %H:%M:%S&amp;quot;,localtime(substr($val,0,10)))&lt;br /&gt;
attr EVU_aWATTar_connect get03-4Name fc3_00_total&lt;br /&gt;
attr EVU_aWATTar_connect get03-4OExpr round($val/1000,4)&lt;br /&gt;
attr EVU_aWATTar_connect get03Name 02_priceAll&lt;br /&gt;
attr EVU_aWATTar_connect get03RegOpt g&lt;br /&gt;
attr EVU_aWATTar_connect get03Regex &amp;quot;start_timestamp&amp;quot;: (\d{10})|&amp;quot;marketprice&amp;quot;: (\d*\.\d*)&lt;br /&gt;
attr EVU_aWATTar_connect get03URL https://api.awattar.de/v1/marketdata?start=1701990000000&amp;amp;end=1702159200000&lt;br /&gt;
attr EVU_aWATTar_connect group PV Steuerung EVU&lt;br /&gt;
attr EVU_aWATTar_connect icon sani_pump&lt;br /&gt;
attr EVU_aWATTar_connect room Strom-&amp;gt;Boerse&lt;br /&gt;
attr EVU_aWATTar_connect sortby 213&lt;br /&gt;
attr EVU_aWATTar_connect userReadings fc_trigger_price:fc_avg.* {\&lt;br /&gt;
## fc_trigger_price:[fc_avg|compensation_grid].* {\\&lt;br /&gt;
  my $fc_avg = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_avg&amp;quot;,0);;\&lt;br /&gt;
  my $fc_min = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_min&amp;quot;,0);;\&lt;br /&gt;
\&lt;br /&gt;
  # Berechnung eines Default Schwellwertes als täglicher Niedrigpreis\&lt;br /&gt;
  my $price_level = round( ($fc_avg - $fc_min)/2 + $fc_min , 1);;\&lt;br /&gt;
 \&lt;br /&gt;
  # Abschätzung von Wirtschaftlichkeit beim Speicher Laden, falls Tibber zu teuer wird\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;compensation_grid&amp;quot;,0) != 0 ) {\&lt;br /&gt;
    my $price_level_battery = round( ($fc_avg - ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;compensation_grid&amp;quot;,0)) *0.85 , 1) ;;\&lt;br /&gt;
    if ( $price_level &amp;gt; $price_level_battery ) {\&lt;br /&gt;
      $price_level = $price_level_battery;;\&lt;br /&gt;
    }\&lt;br /&gt;
  }\&lt;br /&gt;
  $price_level;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_start:fc_trigger_price.* {\&lt;br /&gt;
  my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
  my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for (my $loop_hour = $hour;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour)) ;;\&lt;br /&gt;
      }\&lt;br /&gt;
    } # end  for loop_hour\&lt;br /&gt;
\&lt;br /&gt;
  return(&amp;quot;null&amp;quot;);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_stop:fc0_trigger_start.* {\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
  my $loop_hour = 0;;\&lt;br /&gt;
  my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my $fc_trigger_start = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_trigger_start&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  my $fc_trigger_stop = $fc_trigger_start;;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $fc_trigger_start ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    $fc_trigger_start =~ /(\d\d):/;; $fc_trigger_start = $1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for ($loop_hour = $fc_trigger_start;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$fc.&amp;quot;_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        $fc_trigger_stop = sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour) ;;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour));;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
     # wechsel zum nächsten Tag\&lt;br /&gt;
     if ( $loop_hour == 23 and $fc == 0 ) {\&lt;br /&gt;
       $fc = 1;;\&lt;br /&gt;
       $loop_hour = -1;;\&lt;br /&gt;
     }\&lt;br /&gt;
\&lt;br /&gt;
    } # end for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
 \&lt;br /&gt;
  return($fc_trigger_stop);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger:fc0_trigger_stop.* {\&lt;br /&gt;
\&lt;br /&gt;
  # Setzen des Triggers für die aktuelle Stunde\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;current_price&amp;quot;,100)  &amp;lt; ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) ) {\&lt;br /&gt;
    return(&amp;quot;on&amp;quot;)\&lt;br /&gt;
  } else {\&lt;br /&gt;
    return(&amp;quot;off&amp;quot;)\&lt;br /&gt;
  }\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_DbLog:fc0_00_total.* {\&lt;br /&gt;
my ($timestamp,$date,$hour,$value,$loop_fc_next) = 5x0;;\&lt;br /&gt;
\&lt;br /&gt;
for (my $loop_fc = 0;; $loop_fc &amp;lt;= 1;; $loop_fc++) {\&lt;br /&gt;
  $loop_fc_next = $loop_fc +1;;\&lt;br /&gt;
  $date = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;current_date&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  if ($date ne &amp;quot;null&amp;quot;) {\&lt;br /&gt;
    $date =~ /([\d+-]+)/;; $date = $1 ;;\&lt;br /&gt;
    for (my $loop_hour = 0;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $hour = sprintf(&amp;quot;%02d&amp;quot;,$loop_hour);;\&lt;br /&gt;
      $timestamp = $date.&amp;quot; &amp;quot;.$hour.&amp;quot;:00:00&amp;quot;;;\&lt;br /&gt;
      $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$loop_fc.&amp;quot;_&amp;quot;.$hour.&amp;quot;_total&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
      ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
                        INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                          VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;fc&amp;quot;.$loop_fc.&amp;quot;_total&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
                        ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                          VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
    }\&lt;br /&gt;
  } else {\&lt;br /&gt;
      fhem(&amp;quot;deletereading $NAME fc1_.*&amp;quot;);;\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;current_date&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
}&lt;br /&gt;
attr EVU_aWATTar_connect verbose 0&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== LogDBRep_EVU_aWATTar_connect_SQL RAW Device ====&lt;br /&gt;
Dieses DbRep Device dient der Verbindung zur MySQL DbLog. Die SQL SELECT Statements werden im EVU_Tibber_connect Device generiert und über dieses Device in der MySQL DbLog ausgeführt. Der Name des Devices wird ebenfalls generiert und liegt somit fest.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod LogDBRep_EVU_aWATTar_connect_SQL DbRep LogDB&lt;br /&gt;
attr LogDBRep_EVU_aWATTar_connect_SQL DbLogExclude .*&lt;br /&gt;
attr LogDBRep_EVU_aWATTar_connect_SQL room System&lt;br /&gt;
attr LogDBRep_EVU_aWATTar_connect_SQL sqlCmdHistoryLength 5&lt;br /&gt;
attr LogDBRep_EVU_aWATTar_connect_SQL timeout 20&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 3. EPEX_Spot ==&lt;br /&gt;
=== Vorausetzungen ===&lt;br /&gt;
httpmod&lt;br /&gt;
&lt;br /&gt;
==== EPEX_Spot  RAW Device ====&lt;br /&gt;
Mit diesem device erhält man die viertelstündlichen Börsenpreise für heute und morgen (&#039;EPEX_prices&#039; und zugehörige &#039;EPEX_timetags&#039; als Listen ) und den aktuellen Preis (&#039;EPEX_price&#039;). &amp;lt;br&amp;gt;&lt;br /&gt;
Die &#039;dayahead&#039; Preise für morgen werden gegen 14:00 am Vortag veröffentlicht. Bis dahin enthalten die Listen &#039;EPEX_prices&#039; und &#039;EPEX_timetags&#039; 96 Elemente, danach sind es 192 Elemente.&amp;lt;br&amp;gt;&lt;br /&gt;
Die timetags sind die Unix-timestamps der Viertelstunde, ab welcher der jeweilige Preis gilt.&lt;br /&gt;
&amp;lt;div class=&amp;quot;mw-collapsible mw-collapsed&amp;quot;&amp;gt;&lt;br /&gt;
Code für device &#039;&#039;EPEX_Boersenpreise&#039;&#039;:&amp;lt;div class=&amp;quot;mw-collapsible-content&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
defmod EPEX_Boersenpreise HTTPMOD URL 900&lt;br /&gt;
attr EPEX_Boersenpreise alignTime 00:00&lt;br /&gt;
attr EPEX_Boersenpreise comment This device queries prices for electrical energy from https://api.energy-charts.info/price?bzn=DE-LU. All prices are in €/MWh&lt;br /&gt;
attr EPEX_Boersenpreise disable 0&lt;br /&gt;
attr EPEX_Boersenpreise event-on-update-reading EPEX_price.*&lt;br /&gt;
attr EPEX_Boersenpreise get01-1Name EPEX_timetags&lt;br /&gt;
attr EPEX_Boersenpreise get01-2Name EPEX_prices&lt;br /&gt;
attr EPEX_Boersenpreise get01Name EPEX_prices&lt;br /&gt;
attr EPEX_Boersenpreise get01Regex unix_seconds&amp;quot;:\[(.*?)\],&amp;quot;price&amp;quot;:\[(.*?)\]&lt;br /&gt;
attr EPEX_Boersenpreise get01Replacement02Value {my ($s,$m,$h,$D,$M,$Y)=localtime();;my $start=int(fhemTimeLocal(0,0,0,$D,$M,$Y));;&amp;quot;https://api.energy-charts.info/price?bzn=DE-LU&amp;amp;start=&amp;quot;.sprintf(&amp;quot;%d&amp;quot;,$start).&amp;quot;&amp;amp;end=&amp;quot;.sprintf(&amp;quot;%d&amp;quot;,$start+86400+86399)}&lt;br /&gt;
attr EPEX_Boersenpreise group EPEX&lt;br /&gt;
attr EPEX_Boersenpreise reading02Name EPEX_price&lt;br /&gt;
attr EPEX_Boersenpreise reading02RecombineExpr {my ($s,$m,$h,$D,$M,$Y)=localtime();; my $time=fhemTimeLocal(0,0,0,$D,$M,$Y);;\&lt;br /&gt;
my $price = &amp;quot;error&amp;quot;;;\&lt;br /&gt;
if (time_str2num(ReadingsTimestamp($name, &amp;quot;EPEX_prices&amp;quot;,0)) &amp;gt; $time-86400) {\&lt;br /&gt;
	$time=fhemTimeLocal(0,15*int($m/15),$h,$D,$M,$Y);;\&lt;br /&gt;
	my @times  = split(/,/,ReadingsVal($name,&amp;quot;EPEX_timetags&amp;quot;,&amp;quot;-1&amp;quot;));;\&lt;br /&gt;
	my @prices = split(/,/,ReadingsVal($name,&amp;quot;EPEX_prices&amp;quot;,&amp;quot;-1&amp;quot;));;\&lt;br /&gt;
	my %map;; @map{@times} = @prices;; \&lt;br /&gt;
	if (exists $map{$time}) {$price = $map{$time}};;\&lt;br /&gt;
	};;\&lt;br /&gt;
if ($h % 2 == 0 and $m == 0) {fhem(&amp;quot;get $name EPEX_prices&amp;quot;)};;\&lt;br /&gt;
$price}&lt;br /&gt;
attr EPEX_Boersenpreise reading02Regex .&lt;br /&gt;
attr EPEX_Boersenpreise replacement02Mode expression&lt;br /&gt;
attr EPEX_Boersenpreise replacement02Regex URL&lt;br /&gt;
attr EPEX_Boersenpreise replacement02Value {&amp;quot;http://localhost/&amp;quot;}&lt;br /&gt;
attr EPEX_Boersenpreise room Energie&lt;br /&gt;
attr EPEX_Boersenpreise showError 1&lt;br /&gt;
attr EPEX_Boersenpreise stateFormat EPEX_price €/MWh&lt;br /&gt;
attr EPEX_Boersenpreise timeout 5&lt;br /&gt;
attr EPEX_Boersenpreise userReadings no_of_data:EPEX_prices.* {split(/,/,ReadingsVal(&amp;quot;EPEX_Boersenpreise&amp;quot;,&amp;quot;EPEX_prices&amp;quot;,&amp;quot;&amp;quot;))}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
Weder get02Poll noch get03Poll sollten auf 1 gesetzt sein, da es dann zu gelegentlichen timeout-Fehlern kommt.&lt;br /&gt;
&lt;br /&gt;
== Links ==&lt;br /&gt;
* Website (deutsch) des Anbieters [https://tibber.com/de Tibber]&lt;br /&gt;
* Website (deutsch) des Anbieters [https://www.awattar.de/ aWATTar]&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Strompreis]]&lt;/div&gt;</summary>
		<author><name>Ph1959de</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=KNX&amp;diff=40536</id>
		<title>KNX</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=KNX&amp;diff=40536"/>
		<updated>2025-12-09T19:35:01Z</updated>

		<summary type="html">&lt;p&gt;Ph1959de: Sichtung der letzten Änderungen; kleinere Korrekturen&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Infobox Modul&lt;br /&gt;
|ModPurpose=Unterstützung des KNX Feldbus in FHEM&lt;br /&gt;
|ModType=d&lt;br /&gt;
|ModForumArea=KNX/EIB&lt;br /&gt;
|ModFTopic=122582&lt;br /&gt;
|ModTechName=10_KNX.pm&lt;br /&gt;
|ModOwner=Erwin ({{Link2FU|115|Forum}}/[[Benutzer Diskussion:Erwin111|Wiki]])&lt;br /&gt;
}}&lt;br /&gt;
Das Modul [[KNX]] implementiert die Unterstützung für den Gebäudeautomations-Feldbus [https://de.wikipedia.org/wiki/KNX-Standard KNX] (eine Weiterentwicklung von EIB) innerhalb von FHEM.&lt;br /&gt;
&lt;br /&gt;
== Voraussetzungen ==&lt;br /&gt;
{{Randnotiz|RNText=Hinweis: Das EIB-Modul ist seit 2018 deprecated und wird auch nicht mehr gewartet. Es ist daher dringend empfohlen, auf das KNX-Modul umzusteigen!&lt;br /&gt;
Unterstützung gibt es auf der Wiki Seite [[KNX#Umstellung_von_EIB_auf_KNX_Modul|Umstellung von EIB auf KNX]] oder in {{Link2Forum|Topic=126994|LinkText=diesem Thread im Forum}}|RNTyp=Warn|style=}}&lt;br /&gt;
&lt;br /&gt;
Der KNX support ist in FHEM als 2stufiges Modul konzipiert, braucht daher zusätzlich zu diesem Modul ein sog. IO-Modul, das mit der &amp;quot;Aussenwelt&amp;quot; spricht. Das IO-Modul: [[KNXIO|&#039;&#039;&#039;KNXIO&#039;&#039;&#039;]] ersetzt die bisher verfügbaren Module &#039;&#039;&#039;TUL&#039;&#039;&#039; und &#039;&#039;&#039;KNXTUL&#039;&#039;&#039;, es unterstützt die (meisten) Funktionen der bisherigen IO-Module und bietet zusätzlich weitere Kommunikationsoptionen.&lt;br /&gt;
&lt;br /&gt;
Weiters wird eine Schnittstellen-Hardware gebraucht zwischen dem KNX-Bus und der LAN- oder USB- oder Seriell-Schnittstelle, das kann ein serielles-&amp;gt;KNX-Bus Modul sein (Bsp: siehe Fa. Busware TUL,...) oder auch ein Hardware Gateway oder Router.&lt;br /&gt;
&lt;br /&gt;
Als Middleware kommt meistens knxd zum Einsatz. [https://github.com/knxd/knxd/wiki knxd wiki] &lt;br /&gt;
&lt;br /&gt;
== Anwendung ==&lt;br /&gt;
=== Define ===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
define &amp;lt;name&amp;gt; KNX &amp;lt;group&amp;gt;:&amp;lt;dpt&amp;gt;[:[gadName]:[set|get|listenonly]:[nosuffix]] [&amp;lt;group&amp;gt;:&amp;lt;dpt&amp;gt; ..] [&amp;lt;group&amp;gt;:&amp;lt;dpt&amp;gt; ..]&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Wie in FHEM üblich, alles was hier zwischen &amp;lt;...&amp;gt; dargestellt ist, sind mandatory Angaben! Optionales wird zwischen [...] dargestellt. &lt;br /&gt;
&lt;br /&gt;
Mehrfache &amp;lt;group&amp;gt;&amp;lt;dpt&amp;gt;....  sind in einer Definition zulässig. Damit kann man Geräte mit komplexen Funktionen in einem FHEM Gerät abbilden. (siehe Beispiele) &lt;br /&gt;
&lt;br /&gt;
==== Definitions-Felder im Detail ====&lt;br /&gt;
* &#039;&#039;&#039;group&#039;&#039;&#039; - Das ist die GruppenAdresse (GA) des jeweiligen KNX-Geräts. Diese wird (meist) mit der ETS in das KNX-Gerät programmiert. Ein KNX Aktor reagiert nur auf diese Adresse, wenn es eine seiner eigenen ist. Format: &amp;lt;0-31&amp;gt;/&amp;lt;0-7&amp;gt;/&amp;lt;0-255&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;dpt&#039;&#039;&#039; - Das ist die Definition (Data Point Type), wie FHEM und auch das KNX-Gerät den gesendeten/empfangenen Wert interpretieren! Die Werte (am Bus) können 1bit, 2bit,....4byte lang sein, oder auch 14Char.Text, oder Zeit und Datum. Falls der dpt in FHEM nicht mit dem im KNX-Gerät übereinstimmt, gibts falsche od. gar keine Werte! Eine vollständige Liste der unterstützen dpt-Types: {{Link2CmdRef|Anker=KNX-dpt}}. &lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;gadName&#039;&#039;&#039; - GruppenAdressName bzw. GA-Alias. Angabe ist optional, sinnvoll ist ein &amp;quot;sprechender Name&amp;quot;, z.B. &amp;quot;EinAus&amp;quot; od. &amp;quot;steuern&amp;quot; für eine GA die zum senden eines Befehls dient, oder z.B. &amp;quot;status&amp;quot; für die Rückmeldung vom Gerät. Ist kein gadName definiert, wird g1...g9 als gadName verwendet. Der gadName beeinflusst auch die entsprechenden Reading-Namen! Die Wahl eines  gadName ist fast völlig frei - (Einschränkungen sind in der cmdref dokumentiert), allerdings ist eine Überlegung, den gadNamen so zu wählen, das der daraus abgeleitete readingName und auch set-cmd  mit einem externen System (z.B. Sprachsteuerung) kompatibel ist. Z.B.: für Soll-Temperatur-&amp;gt;desired-temp, Luftfeuchte-&amp;gt;humidity, Luftdruck-&amp;gt;pressure, Temperatur-&amp;gt;temperature, usw... Damit erspart man sich mühsame mappings bei der Umsetzung der Sprachsteuerung.  Beispiel: &#039;&#039;&amp;quot;Axxx wie ist die Solltemperatur im Wohnzimmer&amp;quot;&#039;&#039; - wird mit &amp;lt;code&amp;gt;get Wohnzimmer desired-temperature&amp;lt;/code&amp;gt; übersetzt....&lt;br /&gt;
* &#039;&#039;&#039;set | get | listenonly&#039;&#039;&#039; - alternative optionen, optional, (nur eine Angabe möglich) zur Beeinflussung was FHEM (oder der User) mit dieser Definition machen darf. set bzw get steht für: Fhem darf senden bzw. aktiv ein Gerät abfragen, listenonly bedeutet dass weder was gesendet noch was abgefragt werden darf, es soll nur ankommende Messages verarbeitet werden. Sinnvoll z.B. bei einem Windsensor der in Intervallen die Windgeschwindigkeit auf den Bus sendet.&lt;br /&gt;
* &#039;&#039;&#039;nosuffix&#039;&#039;&#039; - optional, beeinflusst den readingNamen. Ohne diesen Parameter lauten Reading-Namen z.b. &amp;quot;EinAus-set&amp;quot; u. &amp;quot;EinAus-get&amp;quot; bzw. &amp;quot;setG1&amp;quot; u. &amp;quot;getG1&amp;quot;, mit nosuffix fällt das -set bzw. -get weg, alle gesendeten- und empfangenen-Messages landen im reading EinAus.&lt;br /&gt;
&lt;br /&gt;
Siehe {{Link2CmdRef|Anker=KNX}}.&lt;br /&gt;
&lt;br /&gt;
==== Define mittels autocreate ====&lt;br /&gt;
Autocreate wird unterstützt. Ankommende Messages erhalten DeviceNamen nach dem Schema: &#039;&#039;&#039;KNX_nnmmooo&#039;&#039;&#039;. &amp;quot;nn&amp;quot; bezeichnet die Hauptlinie, &amp;quot;mm&amp;quot; die Bereichsline und &amp;quot;ooo&amp;quot; das KNX-Gerät (= die GA_Addresse). Als dpt-Typ wird dptRAW gesetzt, weil autocreate keinen richtigen dpt vergeben kann. Wichtig ist, einen korrekten dpt-Typ zu definieren,  damit valides en-/de-coding von Messages möglich ist.  Es wird auch keine FileLog- oder SVG-Definition erstellt. Sinnvollerweise ändert man auch den DeviceName auf etwas &amp;quot;sprechendes&amp;quot; (z.B. Licht_Wohnzimmer). Man kann das definieren durch autocreate beeinflussen, mit den Attributen: &#039;&#039;&#039;autocreateThreshold&#039;&#039;&#039; und &#039;&#039;&#039;ignoreTypes&#039;&#039;&#039;  im globalen autocreate-device. &lt;br /&gt;
&lt;br /&gt;
Falls man einen FileLog für ALLE KNX-Geräte haben will, die mittels Autocreate angelegt wurden - oder künftig werden, könnte das so aussehen: &amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
define KNX_default_Fl FileLog %L/KNX_default-%Y-W%W.log KNX_.*&lt;br /&gt;
attr KNX_default_Fl logtype text&lt;br /&gt;
attr KNX_default_Fl nrarchive 2&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== ESF-import oder: Wie bekomme ich alle GA&#039;s aus der ETS in FHEM definiert? ====&lt;br /&gt;
Dazu gibts ein XLSX {{Link2Forum|Topic=128532|Message=1231776|LinkText=im Forum}}, welches automatisiert FHEM Definitionen erstellt. Danke an JooNey, der dafür auch Unterstützung im Forum leistet!&lt;br /&gt;
&lt;br /&gt;
=== Attribute ===&lt;br /&gt;
Zusätzlich zu den FHEM-standard Attributen sind folgende Attribute verfügbar:&lt;br /&gt;
* &#039;&#039;&#039;disable&#039;&#039;&#039; - ident zum FHEM Standard - kein Senden oder Empfangen möglich!&lt;br /&gt;
* &#039;&#039;&#039;format&#039;&#039;&#039; - hängt den Inhalt dieses Attr. an den Wert an, - im state Reading!  Diese Attribut ist abgekündigt, nicht mehr verwenden. Besser stateCmd, stateRegex oder stateFormat verwenden.&lt;br /&gt;
* &#039;&#039;&#039;stateRegex&#039;&#039;&#039; - beeinflusst, welcher Wert in das Reading state gesetzt wird, und zwar durch string-substitution. Siehe auch Beispiele.&lt;br /&gt;
* &#039;&#039;&#039;stateCmd&#039;&#039;&#039; - beeinflusst, welcher Wert in das Reading state gesetzt wird, mittels perl-funktion. Siehe auch Beispiele.&lt;br /&gt;
* &#039;&#039;&#039;putCmd&#039;&#039;&#039; - Falls dieses Attr gesetzt ist, wird ein read-request vom KNX Gerät beantwortet. Der Rückgabe-Wert wird mit einer perl Funktion bestimmt.&lt;br /&gt;
* &#039;&#039;&#039;KNX_toggle&#039;&#039;&#039; - Bei einem &amp;quot;toggle-cmd&amp;quot; muss das Device den aktuellen Status des Gerätes wissen. Mit diesem Attr. kann das &amp;quot;Status Device&amp;quot; definiert werden.&lt;br /&gt;
* &#039;&#039;&#039;readingNmap&#039;&#039;&#039; - ändert reading Name(n) mittels regex. Beispiele in der cmd-ref. Dieses Attribut wird vor allen anderen Attributen verarbeitet, daher müssen readingNamen die in z.B. stateCmd,..., notifies... verwendet werden, angepasst werden.&lt;br /&gt;
* &#039;&#039;&#039;IODev&#039;&#039;&#039; - Auf Grund von Änderungen im IO-Handling durch FHEM (-core) ist dieses Attribut in den allermeisten Fällen nicht mehr nötig und evtl. sogar kontraproduktiv! Die Ausnahme ist: Falls mehrere KNX-IO (TUL,KNXTUL,KNXIO) devices definiert sind. Hier ist jedoch extreme Vorsicht geboten, es kann zu endlosen msg-loops kommen! &lt;br /&gt;
&lt;br /&gt;
Siehe {{Link2CmdRef|Anker=KNX-attr}}.&lt;br /&gt;
&lt;br /&gt;
=== Set Command ===&lt;br /&gt;
Ein Set command ist nur erlaubt, wenn die option im define nicht get oder listenonly ist!&lt;br /&gt;
&lt;br /&gt;
Die Syntax:&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
set &amp;lt;device&amp;gt; &amp;lt;gadName&amp;gt; &amp;lt;wert&amp;gt; &lt;br /&gt;
set &amp;lt;device&amp;gt; &amp;lt;wert&amp;gt;&lt;br /&gt;
set &amp;lt;device&amp;gt; g1 &amp;lt;wert&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;In der 2ten Zeile fehlt der gadName, das ist dennoch ein gültiges Set, es wird die erste Gruppenadresse (g1) zum senden verwendet.&lt;br /&gt;
&lt;br /&gt;
In der 3ten Zeile wird explizit die erste Gruppenadresse verwendet, allerdings darf dann kein gadName definiert sein!&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Welche Werte sind im Set Command erlaubt ?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Die erlaubten Werte sind vom dpt abhängig!&lt;br /&gt;
* Alle Werte / Wertbereiche die für diesen dpt vorgesehen sind - Referenz: {{Link2CmdRef|Anker=KNX-dpt}}&lt;br /&gt;
* Für dpt1 und dpt1.001 zusätzlich: toggle, blink &amp;lt;Anzahl&amp;gt; &amp;lt;Dauer&amp;gt;, (on|off)-for-timer &amp;lt;Sekunden&amp;gt;,  (on|off)-until &amp;lt;hh:mm:ss&amp;gt;&lt;br /&gt;
* Für dpt10, dpt11, dpt19 (Datum/Zeit) zusätzlich: now&lt;br /&gt;
* Für dpt16 (14 Char Text) zusätzlich: &amp;gt;CLR&amp;lt; -löscht gesamten Text&lt;br /&gt;
&lt;br /&gt;
=== Get Command ===&lt;br /&gt;
Ein Get command ist nur erlaubt, wenn die option im define nicht set oder listenonly ist! Die Syntax ist analog zum Set command (ohne &amp;lt;wert&amp;gt;). Kommt eine Antwort vom KNX-Gerät wird dieser Wert im entsprechenden Reading gespeichert.&lt;br /&gt;
&lt;br /&gt;
== Umstellung von EIB auf KNX Modul ==&lt;br /&gt;
Nachdem das EIB-Modul seit März 2018 nicht mehr aktualisiert wurde und abgekündigt ist, möchte ich dazu motivieren, auf das aktuelle KNX-Modul umzustellen.&lt;br /&gt;
&lt;br /&gt;
Ausführliche Erklärungen und Beispiele sind in {{Link2Forum|Topic=126994|LinkText=diesem Forenthema}} zu finden.&lt;br /&gt;
&lt;br /&gt;
== Anwendungsbeispiele ==&lt;br /&gt;
Eine umfangreiche Sammlung von Anwendungsbeispielen ist auf der Seite &#039;&#039;&#039;[[KNX Device Definition - Beispiele]]&#039;&#039;&#039; zusammengestellt.&lt;br /&gt;
&lt;br /&gt;
== Bekannte Probleme ==&lt;br /&gt;
&lt;br /&gt;
== Links ==&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:EIB/KNX]]&lt;/div&gt;</summary>
		<author><name>Ph1959de</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=FHEMWEB/MiniChart&amp;diff=40534</id>
		<title>FHEMWEB/MiniChart</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=FHEMWEB/MiniChart&amp;diff=40534"/>
		<updated>2025-12-08T15:49:36Z</updated>

		<summary type="html">&lt;p&gt;Ph1959de: Seite Kategorisiert und wikifiziert, Code-Beispiele kompakter dargestellt, teilweise mit syntax-highlighting, Gliederungsebenen bereinigt&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Datei:Minichart flex mobile.png|Mini|rechts]]&lt;br /&gt;
== MiniChart – kompaktes Chart-Widget (Line &amp;amp; Bar) ==&lt;br /&gt;
Das MiniChart-Widget visualisiert Messwertverläufe als kompaktes Liniendiagramm oder Balkendiagramm für die Darstellung im [[FHEMWEB]] ([[PGM2]]) Frontend.&lt;br /&gt;
Zusätzlich zeigt es drei aktuelle Werte, die aus Label, Reading und Einheit bestehen. Zum Einrichten sind drei Schritte notwendig.&lt;br /&gt;
&lt;br /&gt;
=== Hilfe ===&lt;br /&gt;
* Forenthread zum {{Link2Forum|Topic=141857|LinkText=MiniChart Widget}}&lt;br /&gt;
&lt;br /&gt;
== 1️⃣ 99_myUtils.pm – Hilfsfunktion zum Speichern von historischen Werten ==&lt;br /&gt;
Zuerst wird eine Subroutine in [[99_myUtils_anlegen|99_myUtils.pm]] angelegt, die neue Werte an ein Fixed-Array anhängt und alte Werte entfernt, falls die maximale Größe überschritten wird.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
 sub AppendToFixedArray {&lt;br /&gt;
     my ($dev, $reading, $newVal, $maxSize) = @_;&lt;br /&gt;
     $maxSize //= 250;  # Standard: 250 Werte&lt;br /&gt;
     my $current = ReadingsVal($dev, $reading, &#039;&#039;);&lt;br /&gt;
     my @values = split(&#039;,&#039;, $current);&lt;br /&gt;
     if (@values &amp;gt;= $maxSize) {&lt;br /&gt;
         @values = @values[-($maxSize - 1) .. -1];  # Älteste Werte entfernen&lt;br /&gt;
     }&lt;br /&gt;
     push @values, $newVal;&lt;br /&gt;
     return join(&#039;,&#039;, @values);&lt;br /&gt;
 }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 2️⃣ at-Timer zum regelmäßigen Aktualisieren ==&lt;br /&gt;
Mit einem `[[at]]`-Timer werden die Werte der Messgeräte regelmäßig in die `chartArray`-Readings geschrieben.  &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
 defmod chartUpdate at +*00:05 {\&lt;br /&gt;
   my $val = ReadingsVal(&amp;quot;Smartmeter_2E1F50&amp;quot;, &amp;quot;APOX_Power&amp;quot;, 0);;\&lt;br /&gt;
   my $new = AppendToFixedArray(&amp;quot;Smartmeter_2E1F50&amp;quot;, &amp;quot;chartArray&amp;quot;, $val);;\&lt;br /&gt;
   fhem(&amp;quot;setreading Smartmeter_2E1F50 chartArray $new&amp;quot;);;\&lt;br /&gt;
 \&lt;br /&gt;
   my $val1 = ReadingsVal(&amp;quot;OpenDTU&amp;quot;, &amp;quot;power&amp;quot;, 0);;\&lt;br /&gt;
   my $new1 = AppendToFixedArray(&amp;quot;OpenDTU&amp;quot;, &amp;quot;chartArray&amp;quot;, $val1);;\&lt;br /&gt;
   fhem(&amp;quot;setreading OpenDTU chartArray $new1&amp;quot;);;\&lt;br /&gt;
 \&lt;br /&gt;
   my $val2 = ReadingsVal(&amp;quot;Kuehlschrank&amp;quot;, &amp;quot;power&amp;quot;, 0);;\&lt;br /&gt;
   my $new2 = AppendToFixedArray(&amp;quot;Kuehlschrank&amp;quot;, &amp;quot;chartArray&amp;quot;, $val2);;\&lt;br /&gt;
   fhem(&amp;quot;setreading Kuehlschrank chartArray $new2&amp;quot;);;\&lt;br /&gt;
 \&lt;br /&gt;
   my $val3 = ReadingsVal(&amp;quot;Luftentfeuchter&amp;quot;, &amp;quot;ENERGY_Power&amp;quot;, 0);;\&lt;br /&gt;
   my $new3 = AppendToFixedArray(&amp;quot;Luftentfeuchter&amp;quot;, &amp;quot;chartArray&amp;quot;, $val3);;\&lt;br /&gt;
   fhem(&amp;quot;setreading Luftentfeuchter chartArray $new3&amp;quot;);;\&lt;br /&gt;
 \&lt;br /&gt;
   my $val4 = ReadingsVal(&amp;quot;Geschirrspueler&amp;quot;, &amp;quot;power&amp;quot;, 0);;\&lt;br /&gt;
   my $new4 = AppendToFixedArray(&amp;quot;Geschirrspueler&amp;quot;, &amp;quot;chartArray&amp;quot;, $val4);;\&lt;br /&gt;
   fhem(&amp;quot;setreading Geschirrspueler chartArray $new4&amp;quot;);;\&lt;br /&gt;
 \&lt;br /&gt;
   my $val5 = ReadingsVal(&amp;quot;Trockner&amp;quot;, &amp;quot;power&amp;quot;, 0);;\&lt;br /&gt;
   my $new5 = AppendToFixedArray(&amp;quot;Trockner&amp;quot;, &amp;quot;chartArray&amp;quot;, $val5);;\&lt;br /&gt;
   fhem(&amp;quot;setreading Trockner chartArray $new5&amp;quot;);;\&lt;br /&gt;
 \&lt;br /&gt;
   my $val6 = ReadingsVal(&amp;quot;Waschmaschine&amp;quot;, &amp;quot;power&amp;quot;, 0);;\&lt;br /&gt;
   my $new6 = AppendToFixedArray(&amp;quot;Waschmaschine&amp;quot;, &amp;quot;chartArray&amp;quot;, $val6);;\&lt;br /&gt;
   fhem(&amp;quot;setreading Waschmaschine chartArray $new6&amp;quot;);;\&lt;br /&gt;
 \&lt;br /&gt;
   my $val7 = ReadingsVal(&amp;quot;Pumpe_FBH_Pwr&amp;quot;, &amp;quot;power&amp;quot;, 0);;\&lt;br /&gt;
   my $new7 = AppendToFixedArray(&amp;quot;Pumpe_FBH_Pwr&amp;quot;, &amp;quot;chartArray&amp;quot;, $val7);;\&lt;br /&gt;
   fhem(&amp;quot;setreading Pumpe_FBH_Pwr chartArray $new7&amp;quot;);;\&lt;br /&gt;
 \&lt;br /&gt;
   my $val8 = ReadingsVal(&amp;quot;Heizstab_BW_Pwr&amp;quot;, &amp;quot;power&amp;quot;, 0);;\&lt;br /&gt;
   my $new8 = AppendToFixedArray(&amp;quot;Heizstab_BW_Pwr&amp;quot;, &amp;quot;chartArray&amp;quot;, $val8);;\&lt;br /&gt;
   fhem(&amp;quot;setreading Heizstab_BW_Pwr chartArray $new8&amp;quot;);;\&lt;br /&gt;
 }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 3️⃣ Minichart ==&lt;br /&gt;
=== 1. Widget-Datei aus contrib laden ===&lt;br /&gt;
Bevor das MiniChart-Widget genutzt werden kann, muss die JS-Datei ins FHEM-Webverzeichnis kopiert werden.&lt;br /&gt;
Dies kann mit der Perlfunktion &amp;lt;code&amp;gt;{ Svn_GetFile(&#039;from SVN Path&#039;, &#039;to local Path&#039;) }&amp;lt;/code&amp;gt; direkt in der FHEM Kommandozeile erfolgen.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
{ Svn_GetFile(&#039;contrib/fhemweb_minichart.js&#039;, &#039;www/pgm2/fhemweb_minichart.js&#039;) } &lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 2. Widget im Device mit widgetOverride einrichten ===&lt;br /&gt;
=== Werteanzeige ===&lt;br /&gt;
Drei Textzeilen können angezeigt werden, jeweils im Format:&lt;br /&gt;
:&amp;lt;code&amp;gt;Label@Reading@Einheit&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
:&amp;lt;code&amp;gt;Jetzt@power@W,Heute@yieldday@kWh,Gesamt@yieldtotal@kWh&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Ausblenden einzelner Zeilen ====&lt;br /&gt;
Jede der drei Zeilen kann vollständig oder teilweise ausgeblendet werden, indem das jeweilige Element leer gelassen wird. Das Widget setzt intern automatisch ein geschütztes Leerzeichen (&amp;amp;nbsp;).&lt;br /&gt;
&lt;br /&gt;
Beispiele:&lt;br /&gt;
* Nur Label ausblenden:&lt;br /&gt;
:&amp;lt;code&amp;gt;@reading@W&amp;lt;/code&amp;gt;&lt;br /&gt;
* Nur Reading ausblenden:&lt;br /&gt;
:&amp;lt;code&amp;gt;Label@@W&amp;lt;/code&amp;gt;&lt;br /&gt;
* Nur Einheit ausblenden:&lt;br /&gt;
:&amp;lt;code&amp;gt;Label@reading@&amp;lt;/code&amp;gt;&lt;br /&gt;
* Ganze Zeile ausblenden:&lt;br /&gt;
:&amp;lt;code&amp;gt;&amp;amp;nbsp;&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Diagrammtypen ===&lt;br /&gt;
MiniChart unterstützt zwei Diagrammtypen:&lt;br /&gt;
* line – geglättetes Liniendiagramm mit Farbverlauf  &lt;br /&gt;
* bar  – Balkendiagramm mit positiver/negativer Farbgebung&lt;br /&gt;
&lt;br /&gt;
Das Chart-Reading benötigt eine kommagetrennte Zahlenliste, z. B.:&lt;br /&gt;
:&amp;lt;code&amp;gt;10,20,15,18,25&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Chartoptionen:&lt;br /&gt;
:&amp;lt;code&amp;gt;chartType@colorPositive@colorNegative&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Beispiele:&lt;br /&gt;
:&amp;lt;code&amp;gt;line@#3b82f6@#ff0000&amp;lt;/code&amp;gt;&lt;br /&gt;
:&amp;lt;code&amp;gt;bar@#00cc88@#cc0044&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Fallback (nur ChartReading angegeben):&lt;br /&gt;
:&amp;lt;code&amp;gt;dann wird automatisch line@#3b82f6@#3b82f6 gesetzt&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Einrichtung im Device ===&lt;br /&gt;
Syntax:&lt;br /&gt;
:&amp;lt;code&amp;gt;&amp;lt;webCmd&amp;gt;:minichart,Label1@Reading1@Einheit,Label2@Reading2@Einheit,Label3@Reading3@Einheit,ChartReading,chartType@colorPos@colorNeg&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Beispiel Liniendiagramm:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot; line&amp;gt;&lt;br /&gt;
attr &amp;lt;device&amp;gt; webCmd minichart&lt;br /&gt;
attr &amp;lt;device&amp;gt; widgetOverride minichart:minichart,Jetzt@power@W,Heute@yieldday@kWh,Gesamt@yieldtotal@kWh,chartArray,line@#3b82f6@#ff0000&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Beispiel Balkendiagramm:&lt;br /&gt;
:&amp;lt;code&amp;gt;...,chartArray,bar@#00cc88@#cc0044&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Fallback (Standard line + Standardfarbe):&lt;br /&gt;
:&amp;lt;code&amp;gt;...,chartArray&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Parameterübersicht ===&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Abschnitt&lt;br /&gt;
! Bedeutung&lt;br /&gt;
! Beschreibung&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;&amp;amp;lt;beliebigerName&amp;amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
| webCmd-Zuweisung&lt;br /&gt;
| Definiert die Instanz des Widgets.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;minichart&amp;lt;/code&amp;gt;&lt;br /&gt;
| Widget-Typ&lt;br /&gt;
| Aktiviert das MiniChart-Widget.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;Label1@Reading1@Einheit1&amp;lt;/code&amp;gt;&lt;br /&gt;
| Anzeigezeilen&lt;br /&gt;
| Drei frei definierbare Werte-/Statusfelder; mit &amp;lt;code&amp;gt;&amp;amp;nbsp;&amp;lt;/code&amp;gt; komplett ausblendbar.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;Label2@Reading2@Einheit2&amp;lt;/code&amp;gt;&lt;br /&gt;
| Anzeigezeilen&lt;br /&gt;
| Drei frei definierbare Werte-/Statusfelder; mit &amp;lt;code&amp;gt;&amp;amp;nbsp;&amp;lt;/code&amp;gt; komplett ausblendbar.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;Label3@Reading3@Einheit3&amp;lt;/code&amp;gt;&lt;br /&gt;
| Anzeigezeilen&lt;br /&gt;
| Drei frei definierbare Werte-/Statusfelder; mit &amp;lt;code&amp;gt;&amp;amp;nbsp;&amp;lt;/code&amp;gt; komplett ausblendbar.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;ChartReading&amp;lt;/code&amp;gt;&lt;br /&gt;
| Datenquelle &lt;br /&gt;
| Liefert Array-Werte für das Diagramm .&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;chartType@colorPos@colorNeg&amp;lt;/code&amp;gt;&lt;br /&gt;
| Chart-Optionen&lt;br /&gt;
| line oder bar; Farben optional.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Code Snippets]]&lt;br /&gt;
[[Kategorie:FHEM Frontends]]&lt;/div&gt;</summary>
		<author><name>Ph1959de</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=KNX&amp;diff=40527</id>
		<title>KNX</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=KNX&amp;diff=40527"/>
		<updated>2025-12-07T16:12:54Z</updated>

		<summary type="html">&lt;p&gt;Ph1959de: Tippfehler bereinigt&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Infobox Modul&lt;br /&gt;
|ModPurpose=Unterstützung des KNX Feldbus in FHEM&lt;br /&gt;
|ModType=d&lt;br /&gt;
|ModForumArea=KNX/EIB&lt;br /&gt;
|ModFTopic=122582&lt;br /&gt;
|ModTechName=10_KNX.pm&lt;br /&gt;
|ModOwner=Erwin ({{Link2FU|115|Forum}}/[[Benutzer Diskussion:Erwin111|Wiki]])&lt;br /&gt;
}}&lt;br /&gt;
Das Modul [[KNX]] implementiert die Unterstützung für den Gebäudeautomations-Feldbus [https://de.wikipedia.org/wiki/KNX-Standard KNX] (eine Weiterentwicklung von EIB) innerhalb von FHEM.&lt;br /&gt;
&lt;br /&gt;
== Voraussetzungen ==&lt;br /&gt;
{{Randnotiz|RNText=Hinweis: Das EIB-Modul ist seit 2018 deprecated und wird auch nicht mehr gewartet. Es ist daher dringend empfohlen, auf das KNX-Modul umzusteigen!&lt;br /&gt;
Unterstützung gibst hier: https://wiki.fhem.de/wiki/KNX#Umstellung_von_EIB_auf_KNX_Modul oder diesem Thread:  https://forum.fhem.de/index.php/topic,126994.0.html im Forum|RNTyp=Warn|style=}}&lt;br /&gt;
&lt;br /&gt;
Der KNX support ist in FHEM als 2stufiges Modul konzipiert, braucht daher zusätzlich zu diesem Modul ein sog. IO-Modul, das mit der &amp;quot;Aussenwelt&amp;quot; spricht. Das IO-Modul: [[KNXIO|&#039;&#039;&#039;KNXIO&#039;&#039;&#039;]] ersetzt die bisher verfügbaren Module &#039;&#039;&#039;TUL&#039;&#039;&#039; und &#039;&#039;&#039;KNXTUL&#039;&#039;&#039;, es unterstützt die (meisten) Funktionen der bisherigen IO-Module und bietet zusätzlich weitere Kommunikationsoptionen.&lt;br /&gt;
&lt;br /&gt;
Weiters wird eine Schnittstellen-Hardware gebraucht zwischen dem KNX-Bus und der LAN- oder USB- oder Seriell-Schnittstelle, das kann ein serielles-&amp;gt;KNX-Bus Modul sein (Bsp: siehe Fa. Busware TUL,...) oder auch ein Hardware Gateway oder Router.&lt;br /&gt;
&lt;br /&gt;
Als Middleware kommt meistens knxd zum Einsatz. [https://github.com/knxd/knxd/wiki knxd wiki] &lt;br /&gt;
&lt;br /&gt;
== Anwendung ==&lt;br /&gt;
=== Define ===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
define &amp;lt;name&amp;gt; KNX &amp;lt;group&amp;gt;:&amp;lt;dpt&amp;gt;[:[gadName]:[set|get|listenonly]:[nosuffix]] [&amp;lt;group&amp;gt;:&amp;lt;dpt&amp;gt; ..] [&amp;lt;group&amp;gt;:&amp;lt;dpt&amp;gt; ..]&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Wie in FHEM üblich, alles was hier zwischen &amp;lt;...&amp;gt; dargestellt ist, sind mandatory Angaben! Optionales wird zwischen [...] dargestellt. &lt;br /&gt;
&lt;br /&gt;
Mehrfache &amp;lt;group&amp;gt;&amp;lt;dpt&amp;gt;....  sind in einer Definition zulässig. Damit kann man Geräte mit komplexen Funktionen in einem FHEM Gerät abbilden. (siehe Beispiele) &lt;br /&gt;
&lt;br /&gt;
==== Definitions-Felder im Detail ====&lt;br /&gt;
* &#039;&#039;&#039;group&#039;&#039;&#039; - Das ist die GruppenAdresse (GA) des jeweiligen KNX-Geräts. Diese wird (meist) mit der ETS in das KNX-Gerät programmiert. Ein KNX Aktor reagiert nur auf diese Adresse, wenn es eine seiner eigenen ist. Format: &amp;lt;0-31&amp;gt;/&amp;lt;0-7&amp;gt;/&amp;lt;0-255&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;dpt&#039;&#039;&#039; - Das ist die Definition (Data Point Type), wie FHEM und auch das KNX-Gerät den gesendeten/empfangenen Wert interpretieren! Die Werte (am Bus) können 1bit, 2bit,....4byte lang sein, oder auch 14Char.Text, oder Zeit und Datum. Falls der dpt in FHEM nicht mit dem im KNX-Gerät übereinstimmt, gibts falsche od. gar keine Werte! Eine vollständige Liste der unterstützen dpt-Types: {{Link2CmdRef|Anker=KNX-dpt}}. &lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;gadName&#039;&#039;&#039; - GruppenAdressName bzw. GA-Alias. Angabe ist optional, sinnvoll ist ein &amp;quot;sprechender Name&amp;quot;, z.B. &amp;quot;EinAus&amp;quot; od. &amp;quot;steuern&amp;quot; für eine GA die zum senden eines Befehls dient, oder z.B. &amp;quot;status&amp;quot; für die Rückmeldung vom Gerät. Ist kein gadName definiert, wird g1...g9 als gadName verwendet. Der gadName beeinflusst auch die entsprechenden Reading-Namen! Die Wahl eines  gadName ist fast völlig frei - (Einschränkungen sind in der cmdref dokumentiert), allerdings ist eine Überlegung, den gadNamen so zu wählen, das der daraus abgeleitete readingName und auch set-cmd  mit einem externen System (z.B. Sprachsteuerung) kompatibel ist. Z.B.: für Soll-Temperatur-&amp;gt;desired-temp, Luftfeuchte-&amp;gt;humidity, Luftdruck-&amp;gt;pressure, Temperatur-&amp;gt;temperature, usw... Damit erspart man sich mühsame mappings bei der Umsetzung der Sprachsteuerung.  Beispiel: &#039;&#039;&amp;quot;Axxx wie ist die Solltemperatur im Wohnzimmer&amp;quot;&#039;&#039; - wird mit &amp;lt;code&amp;gt;get Wohnzimmer desired-temperature&amp;lt;/code&amp;gt; übersetzt....&lt;br /&gt;
* &#039;&#039;&#039;set | get | listenonly&#039;&#039;&#039; - alternative optionen, optional, (nur eine Angabe möglich) zur Beeinflussung was FHEM (oder der User) mit dieser Definition machen darf. set bzw get steht für: Fhem darf senden bzw. aktiv ein Gerät abfragen, listenonly bedeutet dass weder was gesendet noch was abgefragt werden darf, es soll nur ankommende Messages verarbeitet werden. Sinnvoll z.B. bei einem Windsensor der in Intervallen die Windgeschwindigkeit auf den Bus sendet.&lt;br /&gt;
* &#039;&#039;&#039;nosuffix&#039;&#039;&#039; - optional, beeinflusst den readingNamen. Ohne diesen Parameter lauten Reading-Namen z.b. &amp;quot;EinAus-set&amp;quot; u. &amp;quot;EinAus-get&amp;quot; bzw. &amp;quot;setG1&amp;quot; u. &amp;quot;getG1&amp;quot;, mit nosuffix fällt das -set bzw. -get weg, alle gesendeten- und empfangenen-Messages landen im reading EinAus.&lt;br /&gt;
&lt;br /&gt;
Siehe {{Link2CmdRef|Anker=KNX}}.&lt;br /&gt;
&lt;br /&gt;
==== Define mittels autocreate ====&lt;br /&gt;
Autocreate wird unterstützt. Ankommende Messages erhalten DeviceNamen nach dem Schema: &#039;&#039;&#039;KNX_nnmmooo&#039;&#039;&#039;. &amp;quot;nn&amp;quot; bezeichnet die Hauptlinie, &amp;quot;mm&amp;quot; die Bereichsline und &amp;quot;ooo&amp;quot; das KNX-Gerät (= die GA_Addresse). Allerdings wird automatisch das  disable Attribut gesetzt, weil autocreate keinen dpt vergeben kann und damit en- / de-coding von Messages nicht möglich ist.  Es wird auch keine FileLog- oder SVG-Definition erstellt. Um das disable attribut zu löschen, muß man vorher einen gültigen dpt in die Definition einfügen. Sinnvollerweise ändert man auch den DeviceName auf etwas &amp;quot;sprechendes&amp;quot; (z.B. Licht_Wohnzimmer). Man kann das definieren durch autocreate beeinflussen, mit dem Attributen: &#039;&#039;&#039;autocreateThreshold&#039;&#039;&#039; und &#039;&#039;&#039;ignoreTypes&#039;&#039;&#039;  im globalen autocreate-device. &lt;br /&gt;
&lt;br /&gt;
Falls man einen FileLog für ALLE KNX-Geräte haben will, die mittels Autocreate angelegt wurden - oder künftig werden, könnte das so aussehen: &amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
define KNX_default_Fl FileLog %L/KNX_default-%Y-W%W.log KNX_.*&lt;br /&gt;
attr KNX_default_Fl logtype text&lt;br /&gt;
attr KNX_default_Fl nrarchive 2&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== ESF-import oder: Wie bekomme ich alle GA&#039;s aus der ETS in FHEM definiert? ====&lt;br /&gt;
Dazu gibts ein XLSX [https://forum.fhem.de/index.php/topic,128532.msg1231776.html#msg1231776 im Forum] , welches automatisiert FHEM definitionen erstellt. Danke an JooNey, der dafür auch Unterstützung im Forum leistet!&lt;br /&gt;
&lt;br /&gt;
=== Attribute ===&lt;br /&gt;
Zusätzlich zu den FHEM-standard Attributen sind folgende Attribute verfügbar:&lt;br /&gt;
* &#039;&#039;&#039;disable&#039;&#039;&#039; - ident zum FHEM Standard - kein Senden oder Empfangen möglich!&lt;br /&gt;
* &#039;&#039;&#039;format&#039;&#039;&#039; - hängt den Inhalt dieses Attr. an den Wert an, - im state Reading!  Diese Attribut ist abgekündigt, nicht mehr verwenden. Besser stateCmd, stateRegex oder stateFormat verwenden.&lt;br /&gt;
* &#039;&#039;&#039;stateRegex&#039;&#039;&#039; - beeinflusst, welcher Wert in das Reading state gesetzt wird, und zwar durch string-substitution. Siehe auch Beispiele.&lt;br /&gt;
* &#039;&#039;&#039;stateCmd&#039;&#039;&#039; - beeinflusst, welcher Wert in das Reading state gesetzt wird, mittels perl-funktion. Siehe auch Beispiele.&lt;br /&gt;
* &#039;&#039;&#039;putCmd&#039;&#039;&#039; - Falls dieses Attr gesetzt ist, wird ein read-request vom KNX Gerät beantwortet. Der Rückgabe-Wert wird mit einer perl Funktion bestimmt.&lt;br /&gt;
* &#039;&#039;&#039;KNX_toggle&#039;&#039;&#039; - Bei einem &amp;quot;toggle-cmd&amp;quot; muss das Device den aktuellen Status des Gerätes wissen. Mit diesem Attr. kann das &amp;quot;Status Device&amp;quot; definiert werden.&lt;br /&gt;
* &#039;&#039;&#039;readingNmap&#039;&#039;&#039; - ändert reading Name(n) mittels regex. Beispiele in der cmd-ref. Dieses Attribut wird vor allen anderen Attributen verarbeitet, daher müssen readingNamen die in z.B. stateCmd,..., notifies... verwendet werden, angepasst werden.&lt;br /&gt;
* &#039;&#039;&#039;IODev&#039;&#039;&#039; - Auf Grund von Änderungen im IO-Handling durch FHEM (-core) ist dieses Attribut in den allermeisten Fällen nicht mehr nötig und evtl. sogar kontraproduktiv! Die Ausnahme ist: Falls mehrere KNX-IO (TUL,KNXTUL,KNXIO) devices definiert sind. Hier ist jedoch extreme Vorsicht geboten, es kann zu endlosen msg-loops kommen! &lt;br /&gt;
&lt;br /&gt;
Siehe {{Link2CmdRef|Anker=KNX-attr}}.&lt;br /&gt;
&lt;br /&gt;
=== Set Command ===&lt;br /&gt;
Ein Set command ist nur erlaubt, wenn die option im define nicht get oder listenonly ist!&lt;br /&gt;
&lt;br /&gt;
Die Syntax:&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
set &amp;lt;device&amp;gt; &amp;lt;gadName&amp;gt; &amp;lt;wert&amp;gt; &lt;br /&gt;
set &amp;lt;device&amp;gt; &amp;lt;wert&amp;gt;&lt;br /&gt;
set &amp;lt;device&amp;gt; g1 &amp;lt;wert&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;In der 2ten Zeile fehlt der gadName, das ist dennoch ein gültiges Set, es wird die erste Gruppenadresse (g1) zum senden verwendet.&lt;br /&gt;
&lt;br /&gt;
In der 3ten Zeile wird explizit die erste Gruppenadresse verwendet, allerdings darf dann kein gadName definiert sein!&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Welche Werte sind im Set Command erlaubt ?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Die erlaubten Werte sind vom dpt abhängig!&lt;br /&gt;
* Alle Werte / Wertbereiche die für diesen dpt vorgesehen sind - Referenz: {{Link2CmdRef|Anker=KNX-dpt}}&lt;br /&gt;
* Für dpt1 und dpt1.001 zusätzlich: toggle, blink &amp;lt;Anzahl&amp;gt; &amp;lt;Dauer&amp;gt;, (on|off)-for-timer &amp;lt;Sekunden&amp;gt;,  (on|off)-until &amp;lt;hh:mm:ss&amp;gt;&lt;br /&gt;
* Für dpt10, dpt11, dpt19 (Datum/Zeit) zusätzlich: now&lt;br /&gt;
* Für dpt16 (14 Char Text) zusätzlich: &amp;gt;CLR&amp;lt; -löscht gesamten Text&lt;br /&gt;
&lt;br /&gt;
=== Get Command ===&lt;br /&gt;
Ein Get command ist nur erlaubt, wenn die option im define nicht set oder listenonly ist! Die Syntax ist analog zum Set command (ohne &amp;lt;wert&amp;gt;). Kommt eine Antwort vom KNX-Gerät wird dieser Wert im entsprechenden Reading gespeichert.&lt;br /&gt;
&lt;br /&gt;
== Umstellung von EIB auf KNX Modul ==&lt;br /&gt;
Nachdem das EIB-Modul seit März 2018 nicht mehr aktualisiert wurde und abgekündigt ist, möchte ich dazu motivieren, auf das aktuelle KNX-Modul umzustellen.&lt;br /&gt;
&lt;br /&gt;
Ausführliche Erklärungen und Beispiele sind in {{Link2Forum|Topic=126994|LinkText=diesem Forenthema}} zu finden.&lt;br /&gt;
&lt;br /&gt;
== Anwendungsbeispiele ==&lt;br /&gt;
Eine umfangreiche Sammlung von Anwendungsbeispielen ist auf der Seite &#039;&#039;&#039;[[KNX Device Definition - Beispiele]]&#039;&#039;&#039; zusammengestellt.&lt;br /&gt;
&lt;br /&gt;
== Bekannte Probleme ==&lt;br /&gt;
&lt;br /&gt;
== Links ==&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:EIB/KNX]]&lt;/div&gt;</summary>
		<author><name>Ph1959de</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=FHEMWEB/Widgets&amp;diff=40512</id>
		<title>FHEMWEB/Widgets</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=FHEMWEB/Widgets&amp;diff=40512"/>
		<updated>2025-12-05T16:58:04Z</updated>

		<summary type="html">&lt;p&gt;Ph1959de: Foren- und Intra-Wiki Links umformatiert (cmdref links sollten auch auf Link2CmdRef umgestellt werden)&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Widgets sind Frontendelemente, die der Dateneingabe und -anzeige dienen. Die hier gezeigten Frontendelemente werden durch [[FHEMWEB]] ([[PGM2]]) bereitgestellt.&lt;br /&gt;
&lt;br /&gt;
== Syntax ==&lt;br /&gt;
Die Syntax der Widgets ist unter dem Attribut [https://fhem.de/commandref_DE.html#widgetOverride widgetOverride] in der Befehlreferenz beschrieben.&lt;br /&gt;
&lt;br /&gt;
== Verwendung ==&lt;br /&gt;
Viele Module (u.a. [[DOIF]], [[dummy]], [[MQTT_DEVICE]], [[MQTT2_DEVICE]], [[MYSENSORS_DEVICE]], [[readingsGroup]]) bieten die Möglichkeit eine Benutzerschnittstelle zu erstellen.&lt;br /&gt;
&lt;br /&gt;
Widgets werden über &amp;lt;code&amp;gt;&amp;lt;name&amp;gt;:&amp;lt;modifier&amp;gt;&amp;lt;/code&amp;gt;-Paare an [[Readings]] oder [[Attribute]] gebunden, dabei ist:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;&amp;lt;name&amp;gt;&amp;lt;/code&amp;gt; ein Readingsname, ein Attribut oder ein Mapping-Argument.&lt;br /&gt;
* &amp;lt;code&amp;gt;&amp;lt;modifier&amp;gt;&amp;lt;/code&amp;gt; ein Widget einschliesslich Parameter.&lt;br /&gt;
&lt;br /&gt;
Bei der Erstellung spielen verschiedene Attribute zusammen:&lt;br /&gt;
&lt;br /&gt;
* Mit readingList (bzw. bei Modulen, die dies nicht kennen: widgetOverride) ist es möglich den Set-Befehl auf [[Readings]] zu erweitern, siehe [https://fhem.de/commandref_DE.html#readingList readingList].&lt;br /&gt;
* Mit [[setList]] (bzw. widgetOverride) werden die Widgets einem Reading zugeordnet und parametrisiert, siehe [https://fhem.de/commandref_DE.html#setList setList].&lt;br /&gt;
* Mit [[webCmd]] werden die Widgets angezeigt, siehe [https://fhem.de/commandref_DE.html#setList webCmd].&lt;br /&gt;
* Mit webCmdLabel werden die Widgets beschriftet und tabellarisch angeordnet, siehe [https://fhem.de/commandref_DE.html#webCmdLabel webCmdLabel].&lt;br /&gt;
* Mit widgetOverride ist es möglich, die im Modul festgelegten Widgets zu überschreiben, siehe [https://fhem.de/commandref_DE.html#widgetOverride widgetOverride].&lt;br /&gt;
* Mit [[eventMap]] ist es möglich Widgets an Mapping-Argumente zu binden, siehe [https://fhem.de/commandref_DE.html#eventMap eventMap] und {{Link2Forum|Topic=76755|LinkText=eventMap und Widget-Anbindung}}.&lt;br /&gt;
Das Zusammenspiel dieser Attribute ist u.A. im Artikel [[DeviceOverview anpassen#webCmd .26 Co|DeviceOverview anpassen]] näher erläutert.&lt;br /&gt;
&lt;br /&gt;
== Zusammenstellung der Widgets ==&lt;br /&gt;
[[Datei:fhemweb_widgets_1.png|mini|rechts]]&lt;br /&gt;
Die Bildschirmkopie zeigt das Benutzerinterface des Dummys mit den Widgets&lt;br /&gt;
&lt;br /&gt;
=== weitere Konfiguration ===&lt;br /&gt;
* &#039;&#039;&#039;knob&#039;&#039;&#039;, siehe http://anthonyterrien.com/knob/&lt;br /&gt;
* &#039;&#039;&#039;colorpicker&#039;&#039;&#039;, siehe [[Color]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
== Das Beispiel für [[DOIF/Import_von_Code_Snippets|Raw definition]] zum Ausprobieren ==&lt;br /&gt;
In der vorstehenden Abbildung fehlt das Look and Feel der Widgets. Mit dem folgenden Code-Snippet zum Ausprobieren, wird dies ergänzt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
defmod widgets dummy&lt;br /&gt;
attr widgets readingList 00select 01select 02selectnumbers 03selectnumbers 04textField 05textFieldNL 06textField-long 07textFieldNL-long 08slider 09multiple 10multiple-strict 11knob 12sortable 13sortable-strict 14sortable-given 15uzsuToggle 16uzsuSelect 17uzsuSelectRadio 18uzsuDropDown 19colorpicker_RGB 19colorpicker_HSV 19colorpicker_CT 19colorpicker_HUE 19colorpicker_BRI 20time 21iconRadio 21iconRadio_use4icon 22iconSwitch 23iconLabel 24iconButtons 24iconButtons_use4icon 25bitfield 26widgetList&lt;br /&gt;
attr widgets room 0_Test&lt;br /&gt;
attr widgets setList 00select:1,2,3,4,5,0,a,b,c,def,hik \&lt;br /&gt;
01select:select,1,2,3,4,5,0,a,b,c,def,hik \&lt;br /&gt;
02selectnumbers:selectnumbers,-30,3,33,1,lin \&lt;br /&gt;
03selectnumbers:selectnumbers,1,0.0625,140000,0,log10 \&lt;br /&gt;
04textField:textField \&lt;br /&gt;
05textFieldNL:textFieldNL\&lt;br /&gt;
06textField-long:textField-long,87 \&lt;br /&gt;
07textFieldNL-long:textFieldNL-long,87 \&lt;br /&gt;
08slider:slider,7,0.5,30,1 \&lt;br /&gt;
09multiple:multiple,shirt,shoes,skirt,trowsers \&lt;br /&gt;
10multiple-strict:multiple-strict,red,green,blue,yellow \&lt;br /&gt;
11knob:knob,min:0,max:360,width:50,height:50,step:1,lineCap:round,angleOffset:180,cursor:3,thickness:.3 \&lt;br /&gt;
12sortable:sortable,blue,shirt,white,shoes,brown,skirt,black,jeans\&lt;br /&gt;
13sortable-strict:sortable-strict,blue,shirt,white,shoes,brown,skirt,black,jeans\&lt;br /&gt;
14sortable-given:sortable-given,blue,shirt,white,shoes,brown,skirt,black,jeans\&lt;br /&gt;
15uzsuToggle:uzsuToggle,ON,OFF\&lt;br /&gt;
16uzsuSelect:uzsuSelect,blue,shirt,white,shoes,brown,skirt,black,jeans\&lt;br /&gt;
17uzsuSelectRadio:uzsuSelectRadio,MW,KW,UKW\&lt;br /&gt;
18uzsuDropDown:uzsuDropDown,red,green,blue,yellow \&lt;br /&gt;
19colorpicker_RGB:colorpicker,RGB \&lt;br /&gt;
19colorpicker_HSV:colorpicker,HSV \&lt;br /&gt;
19colorpicker_CT:colorpicker,CT,2000,10,6500 \&lt;br /&gt;
19colorpicker_HUE:colorpicker,HUE,0,1,359 \&lt;br /&gt;
19colorpicker_BRI:colorpicker,BRI,0,1,100 \&lt;br /&gt;
20time:time \&lt;br /&gt;
21iconRadio:iconRadio,#808080,10,fts_shutter_10@FFA500,20,fts_shutter_20@orange,30,fts_shutter_30@orange,40,fts_shutter_40@orange,50,fts_shutter_50@orange,60,fts_shutter_60@orange,70,fts_shutter_70@orange,80,fts_shutter_80@orange,90,fts_shutter_90@orange,100,fts_shutter_100@orange \&lt;br /&gt;
21iconRadio_use4icon:iconRadio,use4icon@808080,10,fts_shutter_10@FFA500,20,fts_shutter_20@orange,30,fts_shutter_30@orange,40,fts_shutter_40@orange,50,fts_shutter_50@orange,60,fts_shutter_60@orange,70,fts_shutter_70@orange,80,fts_shutter_80@orange,90,fts_shutter_90@orange,100,fts_shutter_100@orange \&lt;br /&gt;
22iconSwitch:iconSwitch,wide#open,@red,half#open,fts_shutter_50@%23cd2906,totally#closed,fts_shutter_100@magenta,half#closed, \&lt;br /&gt;
23iconLabel:iconLabel,wide#open,@red,half#open,fts_shutter_50@%23cd2906,totally#closed,fts_shutter_100@magenta,half#closed,\&lt;br /&gt;
24iconButtons:iconButtons,orange,Bad,sani_heating@green,Wohnzimmer_1,sani_heating@green,Wohnzimmer_2,sani_heating@green,Esszimmer,sani_heating@green,Diele,sani_heating@green,Gäste_WC,sani_heating@green,Büro,sani_heating@green,Wintergarten,sani_heating@green,Werkstatt,sani_heating@green,Schlafzimmer,sani_heating@green \&lt;br /&gt;
24iconButtons_use4icon:iconButtons,use4icon@red,Bad,sani_heating@0000FF,Wohnzimmer_1,sani_heating@blue,Wohnzimmer_2,sani_heating@blue,Esszimmer,sani_heating@blue,Diele,sani_heating@blue,Gäste_WC,sani_heating@blue,Büro,sani_heating@blue,Wintergarten,sani_heating@blue,Werkstatt,sani_heating@blue,Schlafzimmer,sani_heating@0000FF\&lt;br /&gt;
25bitfield:bitfield\&lt;br /&gt;
26widgetList:widgetList,4,select,Bad,Küche,Wohnzimmer,6,selectnumbers,7,1,30,0,lin&lt;br /&gt;
attr widgets userReadings 23iconLabel:22iconSwitch.* {ReadingsVal($name,&amp;quot;22iconSwitch&amp;quot;,&amp;quot;wideopen&amp;quot;)}&lt;br /&gt;
attr widgets webCmd 00select:01select:02selectnumbers:03selectnumbers:04textField:05textFieldNL:06textField-long:07textFieldNL-long:08slider:20time:09multiple:10multiple-strict:11knob:12sortable:13sortable-strict:14sortable-given:15uzsuToggle:16uzsuSelect:17uzsuSelectRadio:18uzsuDropDown:19colorpicker_RGB:19colorpicker_HSV:19colorpicker_CT:19colorpicker_HUE:19colorpicker_BRI:21iconRadio:21iconRadio_use4icon:24iconButtons:24iconButtons_use4icon:22iconSwitch:23iconLabel:25bitfield:26widgetList&lt;br /&gt;
attr widgets webCmdLabel select (default)\&lt;br /&gt;
:select (explicit)\&lt;br /&gt;
:selectnumbers (linear)\&lt;br /&gt;
:selectnumbers (logarithmic)\&lt;br /&gt;
:textField\&lt;br /&gt;
:textFieldNL (no label)\&lt;br /&gt;
:textField-long (multi-line)\&lt;br /&gt;
:textFieldNL-long (multi-line,no label)\&lt;br /&gt;
:slider\&lt;br /&gt;
:time\&lt;br /&gt;
:multiple\&lt;br /&gt;
:multiple-strict\&lt;br /&gt;
:knob\&lt;br /&gt;
:sortable\&lt;br /&gt;
:sortable-strict\&lt;br /&gt;
:sortable-given\&lt;br /&gt;
:uzsuToggle\&lt;br /&gt;
:uzsuSelect\&lt;br /&gt;
:uzsuSelectRadio\&lt;br /&gt;
:uzsuDropDown\&lt;br /&gt;
:colorpicker RGB\&lt;br /&gt;
:colorpicker HSV\&lt;br /&gt;
:colorpicker Farbtemperatur\&lt;br /&gt;
:colorpicker Farbton\&lt;br /&gt;
:colorpicker Helligkeit\&lt;br /&gt;
:iconRadio\&lt;br /&gt;
:iconRadio prefix use4icon\&lt;br /&gt;
:iconButtons\&lt;br /&gt;
:iconButtons prefix use4icon\&lt;br /&gt;
:iconSwitch\&lt;br /&gt;
:iconLabel\&lt;br /&gt;
:bitfield\&lt;br /&gt;
:widgetList (Raum Temperatur)&lt;br /&gt;
attr widgets widgetOverride readingList|setList|webCmd|webCmdLabel|widgetOverride:textField-long,86&lt;br /&gt;
&lt;br /&gt;
setstate widgets iconSwitch wide#open&lt;br /&gt;
setstate widgets 2024-03-19 20:18:38 00select 1&lt;br /&gt;
setstate widgets 2021-09-27 18:52:48 01select 0&lt;br /&gt;
setstate widgets 2021-09-27 18:52:33 02selectnumbers 0.0&lt;br /&gt;
setstate widgets 2021-09-27 12:13:35 03selectnumbers 64938&lt;br /&gt;
setstate widgets 2023-01-03 14:32:14 04textField Hello&lt;br /&gt;
setstate widgets 2017-08-24 17:39:22 05textField Hallo&lt;br /&gt;
setstate widgets 2023-01-03 14:47:53 05textFieldNL Hello&lt;br /&gt;
setstate widgets 2023-01-08 19:25:46 06textField-long Hello&lt;br /&gt;
setstate widgets 2022-11-28 18:23:30 07textFieldNL-long Hello&lt;br /&gt;
setstate widgets 2017-08-24 18:22:07 08slider 17.0&lt;br /&gt;
setstate widgets 2017-08-24 18:22:40 09multiple shirt,shoes&lt;br /&gt;
setstate widgets 2017-08-24 18:22:57 10multiple-strict red,green&lt;br /&gt;
setstate widgets 2021-09-27 03:19:35 11knob 207&lt;br /&gt;
setstate widgets 2017-08-24 18:21:09 12sortable blue,shirt,brown,skirt,white,shoes,black,jeans&lt;br /&gt;
setstate widgets 2017-08-24 18:48:17 13sortable-strict blue,white,shirt,shoes,brown,skirt,black,jeans&lt;br /&gt;
setstate widgets 2017-08-24 18:49:34 14sortable-given blue,shirt,white,shoes,brown,skirt,black,jeans&lt;br /&gt;
setstate widgets 2024-03-19 20:11:06 15uzsuToggle OFF&lt;br /&gt;
setstate widgets 2024-03-19 20:10:53 16uzsuSelect brown&lt;br /&gt;
setstate widgets 2024-03-19 20:11:16 17uzsuSelectRadio UKW&lt;br /&gt;
setstate widgets 2024-03-19 20:11:09 18uzsuDropDown blue&lt;br /&gt;
setstate widgets 2017-10-07 07:56:43 19colorpicker_BRI 54&lt;br /&gt;
setstate widgets 2017-10-07 07:56:31 19colorpicker_CT 5100&lt;br /&gt;
setstate widgets 2017-10-07 07:59:26 19colorpicker_HSV 208c20&lt;br /&gt;
setstate widgets 2017-10-07 07:56:29 19colorpicker_HUE 94&lt;br /&gt;
setstate widgets 2017-10-07 07:53:01 19colorpicker_RGB c95918&lt;br /&gt;
setstate widgets 2017-08-24 19:03:20 20time 12:00&lt;br /&gt;
setstate widgets 2021-09-27 18:54:56 21iconRadio 90&lt;br /&gt;
setstate widgets 2021-09-27 03:19:18 21iconRadio_use4icon 40&lt;br /&gt;
setstate widgets 2024-03-20 18:55:18 22iconSwitch half closed&lt;br /&gt;
setstate widgets 2024-03-20 18:55:18 23iconLabel half closed&lt;br /&gt;
setstate widgets 2021-09-27 18:57:53 24iconButtons Büro,Schlafzimmer&lt;br /&gt;
setstate widgets 2017-10-25 08:20:05 24iconButtons_use4icon Bad,Wohnzimmer_2,Diele,Büro,Werkstatt&lt;br /&gt;
setstate widgets 2024-03-19 20:07:03 25bitfield 25&lt;br /&gt;
setstate widgets 2024-04-11 19:25:52 26widgetList Küche,23&lt;br /&gt;
setstate widgets 2021-09-27 19:02:25 state iconSwitch wide#open&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Beigesteuerte Widgets ==&lt;br /&gt;
[[Datei:datetimepicker.png|mini|rechts]]&lt;br /&gt;
* Forenthread {{Link2Forum|Topic=35736|LinkText=neues DateTimePicker Widget}}&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;[[Datei:Controlminidash.png|mini|rechts]]&lt;br /&gt;
* Wiki Seite [[FHEMWEB/ControlMiniDash|neues ControlMiniDash Widget]]&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Code Snippets]]&lt;br /&gt;
[[Kategorie:Glossary]]&lt;br /&gt;
[[Kategorie:HOWTOS]]&lt;/div&gt;</summary>
		<author><name>Ph1959de</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=Modul_Shelly&amp;diff=40503</id>
		<title>Modul Shelly</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=Modul_Shelly&amp;diff=40503"/>
		<updated>2025-11-30T11:28:16Z</updated>

		<summary type="html">&lt;p&gt;Ph1959de: Hinweis auf Gen1-4 Übersichtsseite&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Todo|&#039;&#039;&#039;Achtung: Diese Seite ist teilweise veraltet, insbesondere unterstützt das Modul weitere Aktoren. Bitte Commandref lesen - diese Seite ist in Überarbeitung&#039;&#039;&#039;}}&lt;br /&gt;
{{Infobox Modul&lt;br /&gt;
|ModPurpose=Das Modul stellt ein Interface zur Bedienung von Shelly Devices zur Verfügung&lt;br /&gt;
|ModType=d&lt;br /&gt;
|ModCmdRef=Shelly&lt;br /&gt;
|ModForumArea=Sonstige Systeme&lt;br /&gt;
|ModFTopic=118446&lt;br /&gt;
|ModTechName=36_Shelly.pm&lt;br /&gt;
|ModOwner=Starkstrombastler ({{Link2FU|3884|Forum}}/[[Benutzer Diskussion:Starkstrombastler|Wiki]])&lt;br /&gt;
}}&lt;br /&gt;
Auf dieser Seite werden die Aktoren des bulgarischen Herstellers Allterco Robotics beschrieben (Markenname Shelly) sowie deren Ansteuerung mit FHEM und aufgetretene Probleme. &lt;br /&gt;
{{Randnotiz|RNTyp=r|RNText=&#039;&#039;&#039;Achtung&#039;&#039;&#039;: Einige der auf dieser Seite erwähnten Geräte und Funktionen sind derzeit nur mit {{Link2Forum|Topic=111905|Message=1285498|LinkText=dieser Testversion}} verfügbar, die manuell installiert werden muss!&lt;br /&gt;
* Für die Weiterentwicklung des Moduls wurde im Forum ein neues Thema &#039;&#039;&#039;({{Link2Forum|Topic=137222|LinkText=Entwicklungs-Thread Modul 36_Shelly.pm}})&#039;&#039;&#039; aufgemacht.}}&lt;br /&gt;
Bei den Shelly-Geräten handelt es sich um IP-basierte Schalt- und Dimmaktoren, die auf verschiedene Weise angesteuert werden können &lt;br /&gt;
*über die Web-Oberfläche des eingebauten Mikro-Webservers,&lt;br /&gt;
*über eine proprietäre App des Herstellers (Achtung, Cloud!),&lt;br /&gt;
*über das hier beschriebene FHEM-Modul 36_Shelly.pm&lt;br /&gt;
*über MQTT&lt;br /&gt;
Ein Teil der Aktoren verfügt über eine eingebaute Leistungsmessung (siehe Spalte Messkanäle in unten stehender Tabelle).&lt;br /&gt;
&lt;br /&gt;
Einen Überblick über die (mittlerweile - Frühjahr 2025 - vier) Gerätegenerationen (Gen1 bis Gen4) bietet die Supportseite [https://support.shelly.cloud/de/support/solutions/articles/103000316073-vergleich-von-shelly-gen-1-gen-2-gen-3-gen4-ger%C3%A4ten Vergleich von Shelly gen1...gen4 Geräten]. Die untenstehende Auflistungmuss angesichts der mittlerweile vier Geräteversionen überarbeitet werden.&lt;br /&gt;
&lt;br /&gt;
==Geräteübersicht==&lt;br /&gt;
Übersicht der IP-basierten Produktreihen&lt;br /&gt;
{| class=&amp;quot;wikitable mw:datatable&amp;quot;&lt;br /&gt;
! style=&amp;quot;width:15px&amp;quot; |ID&lt;br /&gt;
! style=&amp;quot;width:100px&amp;quot; | Produktreihe&lt;br /&gt;
! style=&amp;quot;width:225px&amp;quot; |gemeinsame Merkmale&lt;br /&gt;
|-&lt;br /&gt;
|SH&lt;br /&gt;
|erste Generation&lt;br /&gt;
|COIOT (Nutzung mit Shelly-Monitor), kein Bluetooth&lt;br /&gt;
|-&lt;br /&gt;
|SN&lt;br /&gt;
|Shelly Plus&lt;br /&gt;
Shelly Plus mini&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|SP&lt;br /&gt;
|Shelly Pro&lt;br /&gt;
|Montage auf Hutschiene, zusätzlicher Ethernet-Port (RJ45)&lt;br /&gt;
|-&lt;br /&gt;
|S3&lt;br /&gt;
|Shelly Gen3&lt;br /&gt;
Shelly Gen3 mini&lt;br /&gt;
|proprietärer Prozessor&lt;br /&gt;
|-&lt;br /&gt;
|SA&lt;br /&gt;
|Control Panel&lt;br /&gt;
|Android-System&lt;br /&gt;
|}&lt;br /&gt;
ID: erste beiden Stellen der Modell-ID &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Liste der aktuell unterstützten Geräte &lt;br /&gt;
(hier nicht aufgeführte Geräte der ersten Generation können zusammen mit dem Shelly-Monitor genutzt werden):&lt;br /&gt;
{| class=&amp;quot;wikitable sortable&amp;quot;&lt;br /&gt;
! data-sort-type=&amp;quot;text&amp;quot; style=&amp;quot;width:150px&amp;quot; |Modell&lt;br /&gt;
! data-sort-type=&amp;quot;text&amp;quot; style=&amp;quot;width:75px&amp;quot; |Typ&lt;br /&gt;
! data-sort-type=&amp;quot;number&amp;quot; style=&amp;quot;width:20px;text-align:center;&amp;quot; |Schalt- kanäle&lt;br /&gt;
! data-sort-type=&amp;quot;number&amp;quot; style=&amp;quot;width:20px;text-align:center;&amp;quot; |Dimm- kanäle&lt;br /&gt;
! data-sort-type=&amp;quot;number&amp;quot; style=&amp;quot;width:20px;text-align:center;&amp;quot; |Mess- kanäle&lt;br /&gt;
! data-sort-type=&amp;quot;number&amp;quot; style=&amp;quot;width:20px;text-align:center;&amp;quot; |Digital Eingänge&lt;br /&gt;
! |Bemerkungen&lt;br /&gt;
|-&lt;br /&gt;
! colspan=&amp;quot;7&amp;quot; |&#039;&#039;&#039;Gen 1&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
|Shelly 1&lt;br /&gt;
|Schalter&lt;br /&gt;
|1&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|1&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|Shelly 1PM&lt;br /&gt;
|Schalter&lt;br /&gt;
|1&lt;br /&gt;
|&lt;br /&gt;
|1&lt;br /&gt;
|1&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|Shelly 1L&lt;br /&gt;
|Schalter&lt;br /&gt;
| 1&lt;br /&gt;
|&lt;br /&gt;
|1&lt;br /&gt;
| 1&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot; |Shelly 2&lt;br /&gt;
| Schalter&lt;br /&gt;
|2&lt;br /&gt;
|&lt;br /&gt;
|1&lt;br /&gt;
|2&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|Rollladenaktor&lt;br /&gt;
|1&lt;br /&gt;
|&lt;br /&gt;
|1&lt;br /&gt;
|2&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot; |Shelly 2.5&lt;br /&gt;
|Schalter&lt;br /&gt;
| 2&lt;br /&gt;
|&lt;br /&gt;
|2&lt;br /&gt;
|2&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|Rollladenaktor&lt;br /&gt;
|1&lt;br /&gt;
|&lt;br /&gt;
|1&lt;br /&gt;
|2&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|Shelly 4Pro&lt;br /&gt;
|Schalter&lt;br /&gt;
|4&lt;br /&gt;
|&lt;br /&gt;
|4&lt;br /&gt;
|4&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|Shelly i3&lt;br /&gt;
| Digitale Eingänge&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|3&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|Shelly EM&lt;br /&gt;
|Leistungsmessung&lt;br /&gt;
|1&lt;br /&gt;
|&lt;br /&gt;
|2&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;[[Shelly 3EM]]&#039;&#039;&#039;&lt;br /&gt;
|Leistungsmessung&lt;br /&gt;
|1&lt;br /&gt;
|&lt;br /&gt;
|3&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;[[Shelly Uni]]&#039;&#039;&#039;&lt;br /&gt;
|Universal&lt;br /&gt;
|2&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|*)&lt;br /&gt;
|1-Wire, 2 potentialfreie Relaisausgänge&lt;br /&gt;
Analogeingänge&lt;br /&gt;
|-&lt;br /&gt;
|Shelly Plug&lt;br /&gt;
Shelly Plug S &lt;br /&gt;
|Schaltsteckdose&lt;br /&gt;
|1&lt;br /&gt;
|&lt;br /&gt;
|1&lt;br /&gt;
|&lt;br /&gt;
|1 Taster&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot; |Shelly RGBW2&lt;br /&gt;
|Dimmer&lt;br /&gt;
|&lt;br /&gt;
|4&lt;br /&gt;
|4&lt;br /&gt;
|1&lt;br /&gt;
|4-fach Aktor&lt;br /&gt;
|-&lt;br /&gt;
|Dimmer&lt;br /&gt;
|&lt;br /&gt;
|1&lt;br /&gt;
| 1&lt;br /&gt;
|1&lt;br /&gt;
|RGBW Controller&lt;br /&gt;
|-&lt;br /&gt;
|Shelly Dimmer2&lt;br /&gt;
|Dimmer&lt;br /&gt;
|&lt;br /&gt;
| 1&lt;br /&gt;
|1&lt;br /&gt;
|2&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|Shelly Duo&lt;br /&gt;
|Leuchte&lt;br /&gt;
|&lt;br /&gt;
|1&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|E27 oder GU10 Fassung&lt;br /&gt;
|-&lt;br /&gt;
|Shelly Vintage&lt;br /&gt;
|Leuchte&lt;br /&gt;
|&lt;br /&gt;
|1&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|Shelly Bulb&lt;br /&gt;
|Leuchte&lt;br /&gt;
|&lt;br /&gt;
|1&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|Modi: weiß oder farbe&lt;br /&gt;
|-&lt;br /&gt;
! colspan=&amp;quot;7&amp;quot; |&#039;&#039;&#039;Shelly Plus&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
|Shelly Plus 1&lt;br /&gt;
&lt;br /&gt;
|Schalter &lt;br /&gt;
|1&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|1&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;[[Shelly Plus 1PM]]&#039;&#039;&#039;&lt;br /&gt;
|Schalter&lt;br /&gt;
|1&lt;br /&gt;
|&lt;br /&gt;
|1&lt;br /&gt;
|1&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot; |&#039;&#039;&#039;[[Shelly Plus 2PM]]&#039;&#039;&#039;&lt;br /&gt;
| Schalter&lt;br /&gt;
|2&lt;br /&gt;
|&lt;br /&gt;
|2&lt;br /&gt;
|2&lt;br /&gt;
|-&lt;br /&gt;
|Rollladenaktor&lt;br /&gt;
|1&lt;br /&gt;
|&lt;br /&gt;
| 1&lt;br /&gt;
|2&lt;br /&gt;
|-&lt;br /&gt;
|Shelly Plus i4&lt;br /&gt;
|Digitale Eingänge&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
| 4&lt;br /&gt;
|AC und DC - Variante&lt;br /&gt;
|-&lt;br /&gt;
| Shelly Plus Plug S&lt;br /&gt;
Shelly Plus Plug IT&lt;br /&gt;
Shelly Plus Plug UK&lt;br /&gt;
Shelly Plus Plug US&lt;br /&gt;
|Schaltsteckdose&lt;br /&gt;
&lt;br /&gt;
|1&lt;br /&gt;
|&lt;br /&gt;
|1&lt;br /&gt;
|&lt;br /&gt;
|1 Taster;&lt;br /&gt;
Varianten V1, V2&lt;br /&gt;
|-&lt;br /&gt;
|Shelly Plus Uni&lt;br /&gt;
|Universal&lt;br /&gt;
| 2&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|*)&lt;br /&gt;
|1-Wire, 2 potentialfreie Relaisausgänge &lt;br /&gt;
Analogeingänge; Neu in 2024&lt;br /&gt;
|-&lt;br /&gt;
|Shelly Plus 0-10V Dimmer&lt;br /&gt;
|Dimmer &lt;br /&gt;
|&lt;br /&gt;
|1&lt;br /&gt;
|1&lt;br /&gt;
|2&lt;br /&gt;
|0-10 V DC Ausgang&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot; |Shelly Plus RGBW&lt;br /&gt;
|Dimmer&lt;br /&gt;
|&lt;br /&gt;
|4&lt;br /&gt;
|4&lt;br /&gt;
|4&lt;br /&gt;
|4-fach Aktor&lt;br /&gt;
|-&lt;br /&gt;
|Dimmer&lt;br /&gt;
|&lt;br /&gt;
|1&lt;br /&gt;
|1&lt;br /&gt;
|1&lt;br /&gt;
|RGBW Controller&lt;br /&gt;
|-&lt;br /&gt;
! colspan=&amp;quot;7&amp;quot; | &#039;&#039;&#039;Shelly Plus Mini&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
|Shelly Plus 1 Mini&lt;br /&gt;
|Schalter&lt;br /&gt;
|1&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|1&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|Shelly Plus 1PM Mini&lt;br /&gt;
|Schalter&lt;br /&gt;
|1&lt;br /&gt;
|&lt;br /&gt;
|1&lt;br /&gt;
|1&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|Shelly PM Mini&lt;br /&gt;
|Leistungsmessung&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
| 1&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
! colspan=&amp;quot;7&amp;quot; |&#039;&#039;&#039;Shelly Pro&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
|Shelly Pro 1&lt;br /&gt;
|Schalter&lt;br /&gt;
|1&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|2&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|Shelly Pro 1PM&lt;br /&gt;
|Schalter&lt;br /&gt;
|1&lt;br /&gt;
|&lt;br /&gt;
|1&lt;br /&gt;
|2&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|Shelly Pro 2&lt;br /&gt;
| Schalter&lt;br /&gt;
|2&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
| 2&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|Shelly Pro 2PM&lt;br /&gt;
|Schalter&lt;br /&gt;
|2&lt;br /&gt;
|&lt;br /&gt;
|2&lt;br /&gt;
|2&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|Shelly Pro Dual&lt;br /&gt;
|Rollladenaktor&lt;br /&gt;
|4&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|4&lt;br /&gt;
|2 Rolladenaktoren&lt;br /&gt;
|-&lt;br /&gt;
|Shelly Pro Dimmer 1PM &lt;br /&gt;
|Dimmer&lt;br /&gt;
|&lt;br /&gt;
|1&lt;br /&gt;
|1&lt;br /&gt;
|2&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|Shelly Pro Dimmer 2PM&lt;br /&gt;
|Dimmer&lt;br /&gt;
|&lt;br /&gt;
| 2&lt;br /&gt;
|2&lt;br /&gt;
|4&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|Shelly Pro 3&lt;br /&gt;
|Schalter&lt;br /&gt;
|3&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|3&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|Shelly Pro 3EM&lt;br /&gt;
|Leistungsmessung&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|3&lt;br /&gt;
|&lt;br /&gt;
|1 Schaltkanal mit Addon&lt;br /&gt;
|-&lt;br /&gt;
|Shelly Pro EM50 &lt;br /&gt;
|Leistungsmessung&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|Shelly Pro 4PM&lt;br /&gt;
|Schalter&lt;br /&gt;
|4&lt;br /&gt;
|&lt;br /&gt;
|4&lt;br /&gt;
|4&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
! colspan=&amp;quot;7&amp;quot; |&#039;&#039;&#039;Shelly Gen3&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| Shelly 1 Gen3&lt;br /&gt;
|Schalter&lt;br /&gt;
|1&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|1&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|Shelly 1PM Gen3&lt;br /&gt;
|Schalter&lt;br /&gt;
|1&lt;br /&gt;
|&lt;br /&gt;
|1&lt;br /&gt;
|1&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|Shelly i4 Gen3&lt;br /&gt;
|Digitale Eingänge&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
| 4&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|Shelly Dimmer 0/1-10V Gen3&lt;br /&gt;
|Dimmer&lt;br /&gt;
|&lt;br /&gt;
| 1&lt;br /&gt;
| 1&lt;br /&gt;
|2&lt;br /&gt;
|0-10V DC oder&lt;br /&gt;
1-10V DC Ausgang&lt;br /&gt;
|-&lt;br /&gt;
! colspan=&amp;quot;7&amp;quot; |&#039;&#039;&#039;Shelly Gen3 Mini&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
|Shelly 1 Mini Gen3&lt;br /&gt;
|Schalter&lt;br /&gt;
|1&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|1&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|Shelly 1PM Mini Gen3 &lt;br /&gt;
|Schalter&lt;br /&gt;
|1&lt;br /&gt;
|&lt;br /&gt;
|1&lt;br /&gt;
|1&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|Shelly PM Mini Gen3&lt;br /&gt;
|Leistungsmessung&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|1&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
! colspan=&amp;quot;7&amp;quot; | &#039;&#039;&#039;Control Panels&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
|Shelly Wall Display&lt;br /&gt;
| Control Panel&lt;br /&gt;
| 1&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
| 1&lt;br /&gt;
|&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==Einbindung in FHEM==&lt;br /&gt;
Vorgehensweise zur Einbindung eines Shelly-Gerätes in FHEM:&lt;br /&gt;
*Aktor nach Vorschrift anschließen&lt;br /&gt;
*mit einem WLAN-fähigen Gerät (Laptop, Smartphone, Tablet...; im Folgenden als &#039;&#039;&#039;Laptop&#039;&#039;&#039; bezeichnet) nach dem internen Access Point suchen, der durch das Shelly-Gerät erzeugt wird; typischerweise hat es eine SSID ähnlich wie&lt;br /&gt;
:&amp;lt;code&amp;gt;shelly1-..., shellyswitch-..., shelly4pro-..., &amp;lt;/code&amp;gt; &lt;br /&gt;
*&#039;&#039;&#039;Laptop&#039;&#039;&#039; mit diesem Access Point verbinden; typischerweise bekommt das Gerät dabei die IP-Adresse 192.168.33.2 zugewiesen.&lt;br /&gt;
*im Browser des &#039;&#039;&#039;Laptops&#039;&#039;&#039; die IP-Adresse 192.168.33.1 aufrufen - das ist der Shelly selbst; in der damit angezeigten Weboberfläche kann das Shelly-Gerät konfiguriert werden&lt;br /&gt;
**Shelly ins häusliche WLAN anmelden (mit fester IP-Adresse &amp;lt;shelly-ip&amp;gt; natürlich...)&lt;br /&gt;
**Internen Access Point abschalten (kann auch nach dem nächsten Schritt oder noch später erfolgen)&lt;br /&gt;
**Testen: &#039;&#039;&#039;Laptop&#039;&#039;&#039; wieder mit dem häuslichen WLAN verbinden, und im Browser die Adresse &amp;lt;shelly-ip&amp;gt; aufrufen&lt;br /&gt;
*In FHEM definieren &lt;br /&gt;
:&amp;lt;code&amp;gt;define myShelly Shelly &amp;lt;shelly-ip&amp;gt;&amp;lt;/code&amp;gt; &lt;br /&gt;
*Das Modul setzt bei bekannten Geräten das Attribut &amp;lt;code&amp;gt;model&amp;lt;/code&amp;gt; automatisch. Bei nicht unterstützten Geräten wird das Attribut auf den Wert &amp;lt;code&amp;gt;generic&amp;lt;/code&amp;gt; gesetzt. In diesen Fällen kann das Attribut &amp;lt;code&amp;gt;model&amp;lt;/code&amp;gt; auf der Detailseite des Devices manuell gesetzt werden:&lt;br /&gt;
:&amp;lt;code&amp;gt; attr myShelly model shellyrgbw|shellydimmer|shelly2.5|shelly2|shellyem|shelly3em|shelly4|shellyplug|shelly1|shellybulb|shelly1pm|shellyuni|generic&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Falls es sich um einen Shelly2 oder 2.5 handelt, muss ferner das Attribut &amp;lt;code&amp;gt;mode&amp;lt;/code&amp;gt; auf &amp;quot;roller&amp;quot; oder &amp;quot;relay&amp;quot; gesetzt werden. Mit diesem Modul können alle Daten übertragen und (prinzipiell) alle Konfigurationsänderungen durchgeführt werden, außerdem ist es auf einfachste Weise zu installieren. Das Modul pollt im per Attribut &amp;lt;code&amp;gt;interval&amp;lt;/code&amp;gt; einstellbaren Abstand zyklisch den Aktor auf Statusänderungen (Wert 0 =&amp;gt; kein Polling). Damit der Aktor im Stande ist, irgendwelche Zustandsänderungen &#039;&#039;von sich aus&#039;&#039; an FHEM zu melden, müssen diese als REST-Befehle (also URL-Aufrufe) in der Konfigurationsoberfläche des Shelly-Aktors eingetragen werden. Siehe CommandRef.&lt;br /&gt;
&lt;br /&gt;
Zum Betrieb ist ferner noch zu bemerken, dass das Modul zwar meldet, ob ein Firmware-Update nötig ist, ausgelöst werden muss dieses aber über die Web-Oberfläche des Shelly selber.&lt;br /&gt;
&lt;br /&gt;
===Actions/Webhooks (nur Testversion)===&lt;br /&gt;
Ab Shelly Firmware 1.5.0 werden Actions unterstützt. Damit besteht die Möglichkeit, dass ein Shelly bei Eintreten bestimmter Ereignisse von sich aus Meldungen an andere Shellies und/oder übergeordnete Systeme wie FHEM absetzt. Dies ist nützlich, um Statusänderungen, die z.B. durch lokal betätigte Tasten entstehen, direkt an FHEM zu übermitteln.&lt;br /&gt;
&lt;br /&gt;
Nachfolgende Beispiele zeigen den Code, der im Shelly unter URL einzutragen ist:&lt;br /&gt;
&lt;br /&gt;
Ausgang (Relais) eines Shelly1 schaltet ein:      &lt;br /&gt;
:&amp;lt;code&amp;gt;http://&amp;lt;FHEM-IP&amp;gt;:&amp;lt;Port&amp;gt;/fhem?cmd=set%20&amp;lt;name&amp;gt;%20out_on&amp;lt;/code&amp;gt;&lt;br /&gt;
hierbei sind: &lt;br /&gt;
:&amp;lt;code&amp;gt;&amp;lt;FHEM-IP&amp;gt;&amp;lt;/code&amp;gt; die IP-Adresse des Servers auf dem FHEM läuft&lt;br /&gt;
:&amp;lt;code&amp;gt;&amp;lt;Port&amp;gt;&amp;lt;/code&amp;gt; die Port-Nummer&lt;br /&gt;
:&amp;lt;code&amp;gt;&amp;lt;name&amp;gt;&amp;lt;/code&amp;gt; der Name des FHEM-Devices&lt;br /&gt;
:&amp;lt;code&amp;gt;%20&amp;lt;/code&amp;gt;    stellt ein Leerzeichen dar&lt;br /&gt;
&lt;br /&gt;
Beispiel 2: Eingang eines Shelly2 wird betätigt:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot;&amp;gt;http://&amp;lt;FHEM-IP&amp;gt;:&amp;lt;Port&amp;gt;/fhem?cmd=set%20&amp;lt;name&amp;gt;%20input_on%20&amp;lt;ch&amp;gt;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
:&amp;lt;code&amp;gt;&amp;lt;ch&amp;gt;&amp;lt;/code&amp;gt;  die Nummer des Schaltkanals (Nummer des Eingangs), z.B. &amp;lt;code&amp;gt;0&amp;lt;/code&amp;gt; oder &amp;lt;code&amp;gt;1&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Beispiel 3: Eingang1 eines ShellyDimmers wird betätigt:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot;&amp;gt;http://&amp;amp;#x3C;FHEM-IP&amp;amp;#x3E;:&amp;amp;#x3C;Port&amp;amp;#x3E;/fhem?cmd=set%20&amp;amp;#x3C;name&amp;amp;#x3E;%20short_push&amp;lt;nowiki/&amp;gt;%20&amp;lt;inp&amp;gt;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
:&amp;lt;code&amp;gt;&amp;lt;nowiki&amp;gt;&amp;lt;inp&amp;gt;&amp;lt;/nowiki&amp;gt;&amp;lt;/code&amp;gt;  Nummer des Eingangs, 0 oder 1 (ShellyDimmer verfügen je Schaltkanal über zwei Eingänge)&lt;br /&gt;
&lt;br /&gt;
Beispiel 4: Wirkleistung eines ShellyPro3EM:  &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot;&amp;gt;http://&amp;lt;FHEM-IP&amp;gt;:&amp;lt;Port&amp;gt;/fhem?fwcsrf=csrf_368985985592099&amp;amp;cmd=set%20Y173%20Active_Power_$phase%20$active_power&amp;lt;/syntaxhighlight&amp;gt; &lt;br /&gt;
:&amp;lt;code&amp;gt;fwcsrf=csrf_368985985592099&amp;lt;/code&amp;gt;    das CSRF-Token (FHEMWeb)&lt;br /&gt;
:&amp;lt;code&amp;gt;$phase&amp;lt;/code&amp;gt;   wird vom Shelly durch a, b oder c ersetzt&lt;br /&gt;
: &amp;lt;code&amp;gt;$active_power&amp;lt;/code&amp;gt;  wird vom Shelly durch die aktuelle Wirkleistung ersetzt&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Endpoints&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
In vorstehenden Beispielen stellt der Teil &amp;lt;code&amp;gt;set%20&amp;lt;name&amp;gt;%20&amp;lt;cmd&amp;gt;&amp;lt;/code&amp;gt; den Endpoint dar, d.h. dies ist der Befehl, der vom Shelly-Device in FHEM verarbeitet werden muss.&lt;br /&gt;
&lt;br /&gt;
=====Liste der Befehle der Set-Endpoints:=====&lt;br /&gt;
{| class=&amp;quot;wikitable sortable mw-collapsible&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
!&amp;lt;cmd&amp;gt;&lt;br /&gt;
!Wert&lt;br /&gt;
!Reading&lt;br /&gt;
!Erläuterung &lt;br /&gt;
!Geräte&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;out_on&amp;lt;/code&amp;gt;&lt;br /&gt;
|&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot; |&amp;lt;code&amp;gt;relay_&amp;lt;ch&amp;gt;&amp;lt;/code&amp;gt;&amp;lt;code&amp;gt;state&amp;lt;/code&amp;gt;&lt;br /&gt;
|Ausgang ein&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot; |alle Shelly mit Relaisausgang&lt;br /&gt;
ShellyBulb&lt;br /&gt;
ShellyRGBW&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;out_off&amp;lt;/code&amp;gt;&lt;br /&gt;
|&lt;br /&gt;
|Ausgang aus&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;button_on&amp;lt;/code&amp;gt;&lt;br /&gt;
|&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot; |&amp;lt;code&amp;gt;button_&amp;lt;ch&amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
|Eingang ein&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot; |ShellyPlug&lt;br /&gt;
ShellyPlugS&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;button_off&amp;lt;/code&amp;gt;&lt;br /&gt;
|&lt;br /&gt;
|Eingang aus&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;input_on&amp;lt;/code&amp;gt;&lt;br /&gt;
|&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot; |&amp;lt;code&amp;gt;input_&amp;lt;ch&amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
|Eingang ein&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot; |alle Shelly mit HW-Eingang, aber nicht Shelly-I-Geräte&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;input_off&amp;lt;/code&amp;gt;&lt;br /&gt;
|&lt;br /&gt;
|Eingang aus&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;input_on&amp;lt;/code&amp;gt;&lt;br /&gt;
|&lt;br /&gt;
| rowspan=&amp;quot;4&amp;quot; |&amp;lt;code&amp;gt;input_&amp;lt;inp&amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
|Eingang ein&lt;br /&gt;
| rowspan=&amp;quot;4&amp;quot; |ShellyDimmer&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;input_off&amp;lt;/code&amp;gt;&lt;br /&gt;
|&lt;br /&gt;
|Eingang aus&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;short_push&amp;lt;/code&amp;gt;&lt;br /&gt;
|&lt;br /&gt;
|kurzer Tastendruck&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;long_push&amp;lt;/code&amp;gt;&lt;br /&gt;
|&lt;br /&gt;
|langer Tastendruck&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;single_push&amp;lt;/code&amp;gt;&lt;br /&gt;
|&lt;br /&gt;
| rowspan=&amp;quot;6&amp;quot; |&amp;lt;code&amp;gt;input_&amp;lt;ch&amp;gt;&amp;lt;/code&amp;gt;&amp;lt;code&amp;gt;input_&amp;lt;ch&amp;gt;_action&amp;lt;/code&amp;gt;&lt;br /&gt;
|kurzer Tastendruck&lt;br /&gt;
| rowspan=&amp;quot;4&amp;quot; |ShellyI3&lt;br /&gt;
ShellyI4 &lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;long_push&amp;lt;/code&amp;gt;&lt;br /&gt;
|&lt;br /&gt;
|langer Tastendruck&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;double_push&amp;lt;/code&amp;gt;&lt;br /&gt;
|&lt;br /&gt;
|zweifacher Tastendruck&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;triple_push&amp;lt;/code&amp;gt;&lt;br /&gt;
|&lt;br /&gt;
|dreifacher Tastendruck&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;short_long_push&amp;lt;/code&amp;gt;&lt;br /&gt;
|&lt;br /&gt;
|Tastersequenz lang-kurz&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot; |ShellyI3&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;long_short_push&amp;lt;/code&amp;gt;&lt;br /&gt;
|&lt;br /&gt;
|Tastersequenz kurz-lang&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;stopped&amp;lt;/code&amp;gt;&lt;br /&gt;
|&lt;br /&gt;
| rowspan=&amp;quot;5&amp;quot; |&amp;lt;code&amp;gt;state&amp;lt;/code&amp;gt;&lt;br /&gt;
|Rollo angehalten&lt;br /&gt;
| rowspan=&amp;quot;5&amp;quot; | Shelly2/2.5/Plus2/Pro2 mode=roller&lt;br /&gt;
&amp;lt;nowiki&amp;gt;*&amp;lt;/nowiki&amp;gt;) nur für Shelly Plus2/Pro2&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;opening&amp;lt;/code&amp;gt;&lt;br /&gt;
|&lt;br /&gt;
|Rollo wird geöffnet&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;closing&amp;lt;/code&amp;gt;&lt;br /&gt;
|&lt;br /&gt;
|Rollo wird geschlossen&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;is_open   *)&amp;lt;/code&amp;gt;&lt;br /&gt;
|&lt;br /&gt;
|Rollo offen (in oberer Endlage)&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;is_closed   *)&amp;lt;/code&amp;gt;&lt;br /&gt;
|&lt;br /&gt;
|Rollo geschlossen (in unterer Endlage)&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;temperature_over&amp;lt;/code&amp;gt;&lt;br /&gt;
|&lt;br /&gt;
|&amp;lt;code&amp;gt;temperature_&amp;lt;ch&amp;gt;_range&amp;lt;/code&amp;gt;&lt;br /&gt;
|Temperatur überschreitet eingestellten Grenzwert&lt;br /&gt;
| rowspan=&amp;quot;5&amp;quot; |ShellyAddOn&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;temperature_under&amp;lt;/code&amp;gt;&lt;br /&gt;
|&lt;br /&gt;
|&amp;lt;code&amp;gt;temperature_&amp;lt;ch&amp;gt;_range&amp;lt;/code&amp;gt;&lt;br /&gt;
|Temperatur unterschreitet eingestellten Grenzwert&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;humidity_over&amp;lt;/code&amp;gt;&lt;br /&gt;
|&lt;br /&gt;
|&amp;lt;code&amp;gt;humidity_&amp;lt;ch&amp;gt;_range&amp;lt;/code&amp;gt;&lt;br /&gt;
|Luftfeuchtigkeit überschreitet eingestellten Grenzwert&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;humidity_under&amp;lt;/code&amp;gt;&lt;br /&gt;
|&lt;br /&gt;
|&amp;lt;code&amp;gt;humidity_&amp;lt;ch&amp;gt;_range&amp;lt;/code&amp;gt;&lt;br /&gt;
|Luftfeuchtigkeit unterschreitet eingestellten Grenzwert&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;tempC&amp;lt;/code&amp;gt;&lt;br /&gt;
|&amp;lt;code&amp;gt;$temperature&amp;lt;/code&amp;gt; &lt;br /&gt;
|&amp;lt;code&amp;gt;temperature&amp;lt;/code&amp;gt;&lt;br /&gt;
|Temperatur in °C&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;voltage_over&amp;lt;/code&amp;gt;&lt;br /&gt;
|&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot; |&amp;lt;code&amp;gt;voltage_range&amp;lt;/code&amp;gt;&lt;br /&gt;
|Spannung überschreitet eingestellten Grenzwert&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot; |ShellyUni&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;voltage_under&amp;lt;/code&amp;gt;&lt;br /&gt;
|&lt;br /&gt;
|Spannung unterschreitet eingestellten Grenzwert&lt;br /&gt;
|-&lt;br /&gt;
| Active_Power_$phase&lt;br /&gt;
|&amp;lt;code&amp;gt;$active_power&amp;lt;/code&amp;gt;&lt;br /&gt;
|&amp;lt;code&amp;gt;Active_Power_&amp;lt;ph&amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
|Änderung Wirkleistung&lt;br /&gt;
| rowspan=&amp;quot;3&amp;quot; |ShellyPro3EM&lt;br /&gt;
|-&lt;br /&gt;
|Voltage_$phase&lt;br /&gt;
|&amp;lt;code&amp;gt;$voltage&amp;lt;/code&amp;gt;&lt;br /&gt;
|&amp;lt;code&amp;gt;Voltage_&amp;lt;ph&amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
| Änderung Spannung&lt;br /&gt;
|-&lt;br /&gt;
|Current_$phase&lt;br /&gt;
|&amp;lt;code&amp;gt;$current&amp;lt;/code&amp;gt;&lt;br /&gt;
|&amp;lt;code&amp;gt;Current_&amp;lt;ph&amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
|Änderung Strom&lt;br /&gt;
|}&lt;br /&gt;
Bei Eintreffen eines Set-Endpoints wird im Shelly-Device das zugeordnete Reading entsprechend gesetzt. Damit kann das Shelly-Ereignis z.B. mit einem &amp;lt;code&amp;gt;notify&amp;lt;/code&amp;gt; ausgewertet werden. Im Anschluss daran holt sich das Modul die aktuellen Daten vom Shelly und setzt das Intervall zurück.&lt;br /&gt;
&lt;br /&gt;
Anmerkung zum ShellyPro3EM: Die Action wird erst bei einer gewissen Änderung des jeweiligen Wertes ausgelöst. Bei kleinen Schwankungen kommen also keine Webhooks in FHEM an. &lt;br /&gt;
&lt;br /&gt;
===== Get-Endpoint =====&lt;br /&gt;
Eine besondere Form stellt der Get-Endpoint dar, mit dem das Shelly-Device in FHEM aufgefordert wird, den Status des Shelly zu holen. Beispiel: &lt;br /&gt;
:&amp;lt;code&amp;gt;http://&amp;lt;FHEM-IP&amp;gt;:&amp;lt;Port&amp;gt;/fhem?cmd=get%20&amp;lt;name&amp;gt;%20status&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Attribut webhook (derzeit nur Gen2)====&lt;br /&gt;
Durch Setzten des Attributes &amp;lt;code&amp;gt;webhook&amp;lt;/code&amp;gt; werden auf dem Shelly die verfügbaren Webhooks automatisiert angelegt (auf der Shelly Oberfläche unter Actions zu sehen). Als Attribut-Wert muss das empfangende FHEMWeb-Device ausgewählt werden. Wird das FHEMWeb-Device mit einem csrf-Token abgesichert, wird der Token in den Webhook eingebunden. Bei Änderungen des Tokens (z.B. bei Neustart von FHEM) werden die entsprechenden Webhooks mit angepasst. &lt;br /&gt;
&lt;br /&gt;
Nach dem Setzen des Attributes müssen die Webhooks mit &amp;lt;code&amp;gt;set myShellyDevice actions create all&amp;lt;/code&amp;gt; angelegt werden.&lt;br /&gt;
&lt;br /&gt;
Die vom Modul angelegten Webhooks erhalten im Shelly einen Namen, beginnend mit einem Unterstrich (&amp;lt;code&amp;gt;_&amp;lt;/code&amp;gt;). Wird das Attribut geändert oder gelöscht, dann werden auch zugehörige Actions geändert bzw. gelöscht. Durch Entfernen des Unterstrichs im Namen der Action kann dieser Mechanismus unterbunden werden.&lt;br /&gt;
&lt;br /&gt;
Das Reading &amp;lt;code&amp;gt;webhook_cnt&amp;lt;/code&amp;gt; zeigt die Anzahl aller auf dem Shelly hinterlegten Webhooks und &amp;lt;code&amp;gt;webhooks_ver&amp;lt;/code&amp;gt; den Versionszähler des Shelly.&lt;br /&gt;
&lt;br /&gt;
Eine Übersicht aller Actions/Webhooks eines Shelly bekommt man für Gen2-Geräte mit:  &lt;br /&gt;
:&amp;lt;code&amp;gt;http://&amp;lt;ip-des-Shelly&amp;gt;/rpc/Webhook.List&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===MQTT===&lt;br /&gt;
MQTT (Message Queue Telemetry Transport) ist ein nachrichtenbasiertes Protokoll, bei dem Geräte (Devices) nicht direkt miteinander, sondern mit einem zentralen MQTT-Server (in alter Nomenklatur &#039;&#039;Broker&#039;&#039; genannt) kommunizieren. Eine kurze Einführung in MQTT findet man auf der Seite [[MQTT Einführung]]. Mit entsprechend gesetzten Attributen lassen sich die Shelly-Aktoren auch steuern ([[MQTT2-Module - Praxisbeispiele#Shelly|Praxisbeispiele zu den MQTT2-Modulen]]), für Anfänger ist das allerdings nicht unbedingt zu empfehlen.&lt;br /&gt;
&lt;br /&gt;
==Links==&lt;br /&gt;
*{{Link2Forum|Topic=118446|LinkText=Support Thread}} zu diesem Modul&lt;br /&gt;
*{{Link2Forum|Topic=137222|LinkText=Entwicklungs Thread}} zur Weiterentwicklung des Moduls, ab Februar 2024&lt;br /&gt;
*[http://www.shelly.com Website des Herstellers der Geräte]&lt;br /&gt;
*[https://community.shelly.cloud Forum des Herstellers (englischsprachig]&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Energieverbrauchsmessung]]&lt;br /&gt;
[[Kategorie:MQTT]]&lt;br /&gt;
[[Kategorie:Bluetooth]]&lt;br /&gt;
[[Kategorie:Schalter (Empfänger)]]&lt;br /&gt;
[[Kategorie:Shelly]]&lt;/div&gt;</summary>
		<author><name>Ph1959de</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=Benutzer_Diskussion:Schwatter&amp;diff=40502</id>
		<title>Benutzer Diskussion:Schwatter</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=Benutzer_Diskussion:Schwatter&amp;diff=40502"/>
		<updated>2025-11-30T10:35:24Z</updated>

		<summary type="html">&lt;p&gt;Ph1959de: Willkommen&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Willkommen! ==&lt;br /&gt;
{| width=&amp;quot;100%&amp;quot; cellspacing=&amp;quot;0&amp;quot; cellpadding=&amp;quot;6&amp;quot; style=&amp;quot;line-height: 20px; background: #E0E0E0; border: 2px solid #1874CD;&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| colspan=&amp;quot;4&amp;quot; style=&amp;quot;background:#1874CD;&amp;quot; |&amp;lt;big&amp;gt;&amp;lt;span style=&amp;quot;color: #FAFAFA&amp;quot;&amp;gt;&#039;&#039;&#039;Hallo Schwatter,&#039;&#039;&#039; willkommen im FHEM Wiki!&amp;lt;/span&amp;gt;&amp;lt;/big&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| colspan=&amp;quot;4&amp;quot; | Danke für dein Interesse an unserem Projekt, ich freue mich schon auf deine weiteren Beiträge. Die folgenden Seiten sollten dir die ersten Schritte erleichtern, bitte nimm dir daher etwas Zeit, sie zu lesen.&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&#039;&#039;&#039;FHEM-spezifische Informationen&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| width=&amp;quot;8%&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| width=&amp;quot;38%&amp;quot; | &#039;&#039;&#039;[[Systemübersicht]]&#039;&#039;&#039;&amp;lt;br /&amp;gt;&#039;&#039;FHEM Systemübersicht&#039;&#039;&lt;br /&gt;
| width=&amp;quot;8%&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| width=&amp;quot;38%&amp;quot; | &#039;&#039;&#039;[[FHEMWiki:Über FHEMWiki]]&#039;&#039;&#039;&amp;lt;br /&amp;gt;&#039;&#039;Informationen über dieses Wiki&#039;&#039;&lt;br /&gt;
&amp;lt;!-- Abschnitt auf Kommentar gesetzt&lt;br /&gt;
|-&lt;br /&gt;
| colspan=&amp;quot;4&amp;quot; |&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
{{Todo|FHEM-spezifische Anleitungen und Regeln.}}&lt;br /&gt;
&lt;br /&gt;
---- &lt;br /&gt;
 Ende von &#039;Abschnitt auf Kommentar gesetzt&#039; --&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| colspan=&amp;quot;4&amp;quot; | &lt;br /&gt;
----&lt;br /&gt;
&#039;&#039;&#039;Generelle Informationen über (Media)Wikis&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| width=&amp;quot;8%&amp;quot; | [[Datei:Crystal Clear app kedit.svg|rechts|30px|link=Hilfe:Bearbeiten]]&lt;br /&gt;
| width=&amp;quot;38%&amp;quot; | &#039;&#039;&#039;[[Hilfe:Bearbeiten]]&#039;&#039;&#039;&amp;lt;br /&amp;gt;&#039;&#039;Zugang zu allen wichtigen Informationen.&#039;&#039;&lt;br /&gt;
| width=&amp;quot;8%&amp;quot; | [[Datei:X-office-presentation.svg|rechts|30px|link=Wikipedia:Tutorial]]&lt;br /&gt;
| width=&amp;quot;38%&amp;quot; | &amp;lt;!-- &#039;&#039;&#039;[[Wikipedia:Tutorial]]&#039;&#039;&#039;--&amp;gt;&#039;&#039;&#039;[http://de.wikipedia.org/wiki/Wikipedia:Tutorial Wikipedia:Tutorial]&#039;&#039;&#039;&amp;lt;br /&amp;gt;&#039;&#039;Schritt-für-Schritt-Anleitung für Einsteiger.&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Applications-system.svg|rechts|30px|link=Wikipedia:Grundprinzipien]]&lt;br /&gt;
| &#039;&#039;&#039;&amp;lt;!--[[Wikipedia:Grundprinzipien]]--&amp;gt;[http://de.wikipedia.org/wiki/Wikipedia:Grundprinzipien Wikipedia:Grundprinzipien]&#039;&#039;&#039;&amp;lt;br /&amp;gt;&#039;&#039;Die grundlegende Philosophie unseres Projekts.&#039;&#039;&lt;br /&gt;
| [[Datei:MentorenProgrammLogo-7.svg|rechts|60px|link=Wikipedia:Mentorenprogramm]]&lt;br /&gt;
| &#039;&#039;&#039;&amp;lt;!--[[Wikipedia:Mentorenprogramm]]--&amp;gt;[http://de.wikipedia.org/wiki/Wikipedia:Mentorenprogramm Wikipedia:Mentorenprogramm]&#039;&#039;&#039;&amp;lt;br /&amp;gt;&#039;&#039;Persönliche Einführung in die Beteiligung bei Wikipedia.&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| colspan=&amp;quot;4&amp;quot; |&lt;br /&gt;
----&lt;br /&gt;
Bitte beachte, &amp;lt;!--[[Wikipedia:Was Wikipedia nicht ist|was Wikipedia nicht ist]]--&amp;gt;[http://de.wikipedia.org/wiki/Wikipedia:Was_Wikipedia_nicht_ist was Wikipedia nicht ist], und &amp;quot;unterschreibe&amp;quot; deine Diskussionsbeiträge durch Eingabe von &amp;lt;code&amp;gt;--&amp;lt;nowiki&amp;gt;~~~~&amp;lt;/nowiki&amp;gt;&amp;lt;/code&amp;gt; oder durch Drücken der Schaltfläche [[Datei:button_sig.png|Signaturknopf|20px|link=Hilfe:Signatur]] über dem Bearbeitungsfeld. Artikel werden jedoch nicht unterschrieben, und wofür die Zusammenfassungszeile da ist, erfährst du unter &amp;lt;!--[[wikipedia:Hilfe:Zusammenfassung und Quellen|Hilfe:Zusammenfassung und Quellen]]--&amp;gt;[http://de.wikipedia.org/wiki/Hilfe:Zusammenfassung_und_Quellen Zusammenfassung und Quellen]. &amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
[[Datei:Nuvola apps ksirc.png|25px|link=Benutzer Diskussion:Ph1959de]] &amp;amp;nbsp;&amp;amp;nbsp; &#039;&#039;&#039;Hast du Fragen an mich?&#039;&#039;&#039; Schreib mir auf [[Benutzer Diskussion:Ph1959de|&amp;lt;u&amp;gt;meiner&amp;lt;/u&amp;gt; Diskussionsseite]]! Viele Grüße, [[Benutzer:Ph1959de|Peter]] ([[Benutzer Diskussion:Ph1959de|Diskussion]]) 11:34, 30. Nov. 2025 (CET)&lt;br /&gt;
|}&lt;/div&gt;</summary>
		<author><name>Ph1959de</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=HttpUtils&amp;diff=40501</id>
		<title>HttpUtils</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=HttpUtils&amp;diff=40501"/>
		<updated>2025-11-24T09:34:48Z</updated>

		<summary type="html">&lt;p&gt;Ph1959de: Link zu Forenbeitrag über Vorlage&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Infobox Modul&lt;br /&gt;
|ModPurpose=Hilfsfunktionen für HTTP-Zugriffe&lt;br /&gt;
|ModType=u&lt;br /&gt;
|ModForumArea=Automatisierung&lt;br /&gt;
|ModTechName=HttpUtils.pm&lt;br /&gt;
|ModOwner=rudolfkoenig ({{Link2FU|8|Forum}} / [[Benutzer Diskussion:Rudolfkoenig|Wiki]])&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Das Modul [[HttpUtils]](.pm) ist sowohl für Modulentwickler als auch für Endanwender gedacht, um Daten via HTTP auszutauschen. Es stellt dabei eine Reihe von Funktionen zur Verfügung und wird beispielsweise vom Modul [[HTTPMOD]] intensiv genutzt.&lt;br /&gt;
&lt;br /&gt;
== Primäre Funktionen (HTTP) ==&lt;br /&gt;
Es ist zu beachten, dass bei den Funktionen&lt;br /&gt;
* &amp;lt;code&amp;gt;GetHttpFile()&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;GetFileFromURL&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;GetFileFromURLQuiet&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;HttpUtils_BlockingGet&amp;lt;/code&amp;gt;&lt;br /&gt;
ein sogenannter &amp;quot;blockierender&amp;quot; Aufruf durchgeführt wird. Das bedeutet, dass FHEM bei einem Aufruf einer dieser Funktionen solange wartet und dabei absolut nichts macht, bis die Antwort vom HTTP-Server eintrifft und die Funktion damit beendet ist. Das kann bei Verbindungsproblemen evtl. dazu führen, dass FHEM für die gesamte Wartezeit (Timeout) steht und nichts verarbeitet. Problematisch ist das gerade bei Anwendungen oder Hardware, die eine zeitnahe Reaktion von FHEM erwarten (z.B. HomeMatic-Geräte). In der Zeit, in der auf eine HTTP Antwort gewartet wird, steht FHEM dabei komplett. &lt;br /&gt;
&lt;br /&gt;
Es wird daher empfohlen, die Funktionen so sparsam wie möglich zu verwenden und die Timeouts so niedrig wie möglich zu halten, um ein längeres Einfrieren von FHEM möglichst zu vermeiden.&lt;br /&gt;
&lt;br /&gt;
Um eine Blockierung zu vermeiden wird generell die Verwendung von&lt;br /&gt;
:&amp;lt;code&amp;gt;HttpUtils_NonblockingGet&amp;lt;/code&amp;gt;&lt;br /&gt;
empfohlen. Diese führt den HTTP-Request asynchron durch, wodurch ein Blockieren von FHEM verhindert wird. Wie das genau funktioniert, wird in dem entsprechenden Kapitel beschrieben.&lt;br /&gt;
&lt;br /&gt;
=== GetHttpFile ===&lt;br /&gt;
Die Funktion GetHttpFile ist die denkbar einfachste Variante um eine URL aufzurufen. &lt;br /&gt;
:&amp;lt;code&amp;gt;GetHttpFile($server, $file)&amp;lt;/code&amp;gt;&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Parameter!! Bedeutung&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$server&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;mandatory&#039;&#039;&lt;br /&gt;
|| Der DNS-Name oder die IP-Adresse des HTTP-Servers&amp;lt;br&amp;gt;&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&amp;lt;nowiki&amp;gt;www.myhost.com&amp;lt;/nowiki&amp;gt;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&amp;lt;nowiki&amp;gt;192.168.0.10&amp;lt;/nowiki&amp;gt;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$file&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;mandatory&#039;&#039;&lt;br /&gt;
|| Die Datei, welche auf dem HTTP-Server aufgerufen werden soll.&amp;lt;br&amp;gt;&lt;br /&gt;
Beispiel:&lt;br /&gt;
* &#039;&#039;/&#039;&#039;&lt;br /&gt;
* &#039;&#039;/index.html&#039;&#039;&lt;br /&gt;
* &#039;&#039;/directory/image.jpg&#039;&#039;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Funktionsergebnis ist der Inhalt der aufgerufenen Seite in Form einer Zeichenkette.&lt;br /&gt;
&lt;br /&gt;
=== GetFileFromURL ===&lt;br /&gt;
Die Funktion GetFileFromURL ruft die HTTP-URL auf und gibt als Funktionsergebnis den Seiteninhalt zurück. Im Gegensatz zu GetHttpFile beinhaltet GetFileFromURL einige Zusatzoptionen in Form von Funktionsparametern.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Aufruf: &amp;lt;code&amp;gt;GetFileFromURL($url, &#039;&#039;[$timeout], [$data], [$noshutdown], [$loglevel]&#039;&#039;)&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Parameter!! Bedeutung&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$url&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;mandatory&#039;&#039;&lt;br /&gt;
|| Die HTTP-URL, welche aufgerufen werden soll. Diese kann optional Usernamen, Passwort und einen Port enthalten. Sowohl HTTP als auch HTTPS wird hierbei unterstützt.&amp;lt;br&amp;gt;&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&amp;lt;nowiki&amp;gt;http://www.myhost.com/directory/&amp;lt;/nowiki&amp;gt;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&amp;lt;nowiki&amp;gt;https://www.myhost.com/&amp;lt;/nowiki&amp;gt;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&amp;lt;nowiki&amp;gt;http://www.myhost.com:8080/&amp;lt;/nowiki&amp;gt;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&amp;lt;nowiki&amp;gt;http://user:password@www.myhost.com/&amp;lt;/nowiki&amp;gt;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;WICHTIG:&#039;&#039;&#039; Falls ein Username sowie Passwort übergeben werden, so müssen diese vorher jeweils mittels [[#urlEncode|urlEncode()]] in URL-kompatible Form umgewandelt werden um Probleme mit evtl. enthaltenen Sonderzeichen zu vermeiden. &lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$timeout&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;optional&#039;&#039;&lt;br /&gt;
|| Die maximale Dauer in Sekunden für die HTTP-Anfrage&lt;br /&gt;
&lt;br /&gt;
Beispiel: 5 &#039;&#039;(Sekunden)&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Standardwert: 4 Sekunden&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$data&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;optional&#039;&#039;&lt;br /&gt;
|| Wenn man Daten via HTTP-POST übertragen möchte, so kann man die Nutzdaten über $data übergeben. Die Daten werden dabei als Formulardaten übertragen. Wenn man den Content-Type beeinflussen oder mehrere Formular-Felder senden möchte, sollte man zur Funktion HttpUtils_BlockingGet oder HttpUtils_NonblockingGet greifen.&lt;br /&gt;
&lt;br /&gt;
Standardwert: &#039;&#039;[leer]&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$noshutdown&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;optional&#039;&#039;&lt;br /&gt;
|| Wenn $noshutdown auf 1 gesetzt ist, wird dem HTTP-Server nicht implizit mitgeteilt, dass die Verbindung nach dem Request geschlossen werden soll. Viele Webserver schließen in solch einem Fall die Verbindung, bevor sie die Antwort senden. Bei 0 wird dem Webserver mitgeteilt, dass der Sendevorgang beendet ist und nun die Antwort abgewartet wird.&lt;br /&gt;
&lt;br /&gt;
Standardwert: 1&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$loglevel&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;optional&#039;&#039;&lt;br /&gt;
|| Das [[verbose|Loglevel]], in dem sämtliche Logmeldungen zu dieser HTTP-Abfrage erzeugt werden sollen.&lt;br /&gt;
&lt;br /&gt;
Standardwert: 4&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Funktionsergebnis ist der Inhalt der aufgerufenen Seite in Form eines Strings.&lt;br /&gt;
&lt;br /&gt;
=== GetFileFromURLQuiet ===&lt;br /&gt;
Diese Funktion funktioniert ähnlich wie GetFileFromURL. Allerdings wird die tatsächliche URL in allen erzeugten Log-Meldungen unkenntlich gemacht um z.B. Zugangsdaten nicht preiszugeben. Die aufgerufene Seite wird ebenfalls als Funktionsergebnis zurückgegeben.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Aufruf: &amp;lt;code&amp;gt;GetFileFromURLQuiet($url, &#039;&#039;[$timeout], [$data], [$noshutdown], [$loglevel]&#039;&#039;)&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Parameter!! Bedeutung&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$url&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;mandatory&#039;&#039;&lt;br /&gt;
|| Die HTTP-URL, welche aufgerufen werden soll. Diese kann optional Usernamen, Passwort und einen Port enthalten. Sowohl HTTP als auch HTTPS wird hierbei unterstützt.&amp;lt;br&amp;gt;&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&amp;lt;nowiki&amp;gt;http://www.myhost.com/directory/&amp;lt;/nowiki&amp;gt;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&amp;lt;nowiki&amp;gt;https://www.myhost.com/&amp;lt;/nowiki&amp;gt;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&amp;lt;nowiki&amp;gt;http://www.myhost.com:8080/&amp;lt;/nowiki&amp;gt;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&amp;lt;nowiki&amp;gt;http://user:password@www.myhost.com/&amp;lt;/nowiki&amp;gt;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;WICHTIG:&#039;&#039;&#039; Falls ein Username sowie Passwort übergeben werden, so müssen diese vorher jeweils mittels [[#urlEncode|urlEncode()]] in URL-kompatible Form umgewandelt werden um Probleme mit evtl. enthaltenen Sonderzeichen zu vermeiden. &lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$timeout&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;optional&#039;&#039;&lt;br /&gt;
|| Die maximale Dauer in Sekunden für die HTTP-Anfrage&lt;br /&gt;
&lt;br /&gt;
Beispiel: 5 &#039;&#039;(Sekunden)&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Standardwert: 4 Sekunden&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$data&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;optional&#039;&#039;&lt;br /&gt;
|| Wenn man Daten via HTTP-POST übertragen möchte, so kann man die Nutzdaten über &amp;lt;code&amp;gt;$data&amp;lt;/code&amp;gt; übergeben. Die Daten werden dabei als Formulardaten übertragen. Wenn man den Content-Type beeinflussen möchte, oder mehrere Formular-Felder senden möchte, sollte man zur Funktion HttpUtils_BlockingGet oder HttpUtils_NonblockingGet greifen.&lt;br /&gt;
&lt;br /&gt;
Standardwert: &#039;&#039;[leer]&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$noshutdown&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;optional&#039;&#039;&lt;br /&gt;
|| Wenn $noshutdown auf 1 gesetzt ist, wird dem HTTP-Server nicht implizit mitgeteilt, dass die Verbindung nach dem Request geschlossen werden soll. Viele Webserver schließen in solch einem Fall die Verbindung bevor, sie die Antwort senden. Bei 0 wird dem Webserver mitgeteilt, dass der Sendevorgang beendet ist und nun die Antwort abgewartet wird.&lt;br /&gt;
&lt;br /&gt;
Standardwert: 1&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$loglevel&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;optional&#039;&#039;&lt;br /&gt;
|| Das Loglevel, in dem sämtliche Logmeldungen zu dieser HTTP-Abfrage erzeugt werden sollen.&lt;br /&gt;
&lt;br /&gt;
Standardwert: 4&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Funktionsergebnis ist der Inhalt der aufgerufenen Seite in Form eines Strings.&lt;br /&gt;
&lt;br /&gt;
=== HttpUtils_BlockingGet ===&lt;br /&gt;
Wenn die bisher genannten Funktionen nicht ausreichen um die gewünschte Abfrage durchzuführen, so kann man diese Funktion verwenden. Aufgrund zahlreicher Parameter ermöglicht sie viele Anpassungsmöglichkeiten. Diese Funktion hat dabei nicht wie üblich eine Liste an Funktionsparametern, sondern lediglich einen Parameter, welcher eine Hashreferenz mit allen Funktionsparametern darstellt. Dieser Hash enthält sämtliche Parameter inkl. Werten. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Aufruf: &amp;lt;code&amp;gt;HttpUtils_BlockingGet($param)&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Der Parameter $param ist eine Referenz auf eine Hash-Struktur, welche die einzelnen Parameter enthält. Der Hash $param kann folgende Optionen beinhalten:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;width:175px&amp;quot; | Parameter !! style=&amp;quot;width:auto&amp;quot; | Bedeutung&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$param-&amp;gt;{url}&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;mandatory&#039;&#039;&lt;br /&gt;
 || Die HTTP-URL, welche aufgerufen werden soll. Diese kann optional Usernamen, Passwort und einen Port enthalten. Sowohl HTTP als auch HTTPS wird hierbei unterstützt.&amp;lt;br&amp;gt;&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&amp;lt;nowiki&amp;gt;http://www.myhost.com/directory/&amp;lt;/nowiki&amp;gt;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&amp;lt;nowiki&amp;gt;https://www.myhost.com/&amp;lt;/nowiki&amp;gt;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&amp;lt;nowiki&amp;gt;http://www.myhost.com:8080/&amp;lt;/nowiki&amp;gt;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&amp;lt;nowiki&amp;gt;http://user:password@www.myhost.com/&amp;lt;/nowiki&amp;gt;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;WICHTIG:&#039;&#039;&#039; Alternativ können die Zugangsdaten auch in &amp;lt;code&amp;gt;$param-&amp;gt;{user}&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;$param-&amp;gt;{pwd}&amp;lt;/code&amp;gt; übergeben werden. Falls  Username und Passwort in der URL übergeben werden, so müssen diese vorher jeweils mittels [[#urlEncode|urlEncode()]] in URL-kompatible Form umgewandelt werden um Probleme mit evtl. enthaltenen Sonderzeichen (z.B. &amp;quot;:&amp;quot; und &amp;quot;@&amp;quot;) zu vermeiden, da sonst die URL nicht mehr einer validen Syntax entspricht. &lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top&amp;quot; |&#039;&#039;&#039;&amp;lt;code&amp;gt;$param-&amp;gt;{timeout}&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;optional&#039;&#039;&lt;br /&gt;
|| Die maximale Dauer in Sekunden bis der Server eine Antwort liefern muss. Andernfalls wird der Request mit einer Fehlermeldung abgebrochen.&lt;br /&gt;
&lt;br /&gt;
Beispiel: 5 &#039;&#039;(Sekunden)&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Standardwert: 4 Sekunden&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top&amp;quot; |&#039;&#039;&#039;&amp;lt;code&amp;gt;$param-&amp;gt;{data}&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;optional&#039;&#039;&lt;br /&gt;
|| Wenn man Daten via HTTP-POST übertragen möchte, so kann man die Nutzdaten über &amp;lt;code&amp;gt;$param-&amp;gt;{data}&amp;lt;/code&amp;gt; übergeben. Die Daten werden dabei als Formulardaten übertragen. Die Daten können dabei auf zwei Arten übergeben werden:&lt;br /&gt;
&lt;br /&gt;
1. Daten als Zeichenkette:&lt;br /&gt;
:* Die Daten werden komplett als gesamte Zeichenkette in &amp;lt;code&amp;gt;$param-&amp;gt;{data}&amp;lt;/code&amp;gt; abgelegt (z.B.: &amp;lt;code&amp;gt;$param-&amp;gt;{data} = &amp;quot;Jede Menge tolle Daten&amp;quot;&amp;lt;/code&amp;gt;).&lt;br /&gt;
2. Daten als Hash-Struktur:&lt;br /&gt;
:* Die Daten werden als Hash mit Key-Value-Pairs übergeben (z.B.: &amp;lt;code&amp;gt;$param-&amp;gt;{data}{field1} = &amp;quot;value1&amp;quot;, $param{data}{field2} = &amp;quot;value2&amp;quot;, ...&amp;lt;/code&amp;gt; ). Die Daten werden dann als Formulardaten mit mehrfachen Datenfeldern übertragen.&lt;br /&gt;
&lt;br /&gt;
Standardwert: &#039;&#039;[leer]&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$param-&amp;gt;{user}&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;optional&#039;&#039;&lt;br /&gt;
||Im Falle einer notwendigen HTTP-Authentifizierung, der zu benutzende Username um sich gegenüber dem Server zu identifizieren. Alternativ kann der Username und das Passwort auch direkt in der URL mitgegeben werden (siehe jedoch &amp;lt;code&amp;gt;$param-&amp;gt;{hideurl}&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
Standardwert: &#039;&#039;[leer]&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$param-&amp;gt;{pwd}&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;optional&#039;&#039;&lt;br /&gt;
||Im Falle einer notwendigen HTTP-Authentifizierung, das zu benutzende Passwort um sich gegenüber dem Server zu identifizieren. Alternativ kann der Username und das Passwort auch direkt in der URL mitgegeben werden (siehe jedoch &amp;lt;code&amp;gt;$param-&amp;gt;{hideurl}&amp;lt;/code&amp;gt;).&lt;br /&gt;
Standardwert: &#039;&#039;[leer]&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$param-&amp;gt;{noshutdown}&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;optional&#039;&#039;&lt;br /&gt;
||Wenn &amp;lt;code&amp;gt;$param-&amp;gt;{noshutdown}&amp;lt;/code&amp;gt; auf 1 gesetzt ist, wird dem HTTP-Server nicht implizit mitgeteilt, dass die Verbindung nach dem Request geschlossen werden soll. Viele Webserver schließen in solch einem Fall die Verbindung, bevor sie die Antwort senden. Bei 0 wird dem Webserver mitgeteilt, dass der Sendevorgang beendet ist und nun die Antwort abgewartet wird.&lt;br /&gt;
&lt;br /&gt;
Standardwert: 1&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$param-&amp;gt;{loglevel}&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;optional&#039;&#039;&lt;br /&gt;
|| Das Loglevel, in dem sämtliche Logmeldungen zu dieser HTTP-Abfrage erzeugt werden sollen.&lt;br /&gt;
&lt;br /&gt;
Standardwert: 4&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$param-&amp;gt;{hideurl}&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;optional&#039;&#039;&lt;br /&gt;
|| Wenn dieser Parameter den Wert 1 trägt, wird die gesamte URL in sämtlichen Log-Ausgaben unkenntlich gemacht. Dies notwendig, wenn z.B. Zugangsdaten direkt in der URL angegeben wurden, oder die URL ein geheimes Token oder andere schützenswerte Informationen enthält.&lt;br /&gt;
&lt;br /&gt;
Standardwert: 0&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$param-&amp;gt;{ignoreredirects}&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;optional&#039;&#039;&lt;br /&gt;
|| Wenn dieser Parameter den Wert 1 trägt, werden Umleitungen durch den Server ignoriert und der Request beendet. Dies kann erforderlich sein um evtl. Cookies aus der Antwort, welche eine Umleitung enthält aus dem HTTP Header zu extrahieren um diese im nächsten Request weiterzuverwenden.&lt;br /&gt;
&lt;br /&gt;
Standardwert: 0&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$param-&amp;gt;{method}&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;optional&#039;&#039;&lt;br /&gt;
||Die HTTP-Methode, welche zur Abfrage verwendet werden soll. Sofern keine Daten übertragen werden ist dies standardmäßig &amp;quot;GET&amp;quot;, ansonsten &amp;quot;POST&amp;quot;. Es können aber auch andere Methoden verwendet werden.&lt;br /&gt;
&lt;br /&gt;
Standardwert: &amp;quot;GET&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$param-&amp;gt;{keepalive}&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;optional&#039;&#039;&lt;br /&gt;
||Wenn dieser Parameter auf 1 gesetzt ist, wird dem Server der Wunsch mitgeteilt, die Verbindung offen zu lassen für weitere Anfragen. Sobald die Antwort auf den jeweiligen Request eintrifft, bleibt die TCP-Verbindung bestehen (sofern der Server dies unterstützt). Anschließend kann man den Parameter-Hash mit einer neuen URL und Optionen füllen. Der Parameter &amp;quot;keepalive&amp;quot; sollte dabei weiterhin gesetzt bleiben, sofern die Verbindung auch weiterhin möglichst erhalten bleiben soll. &lt;br /&gt;
&lt;br /&gt;
Um eine offene Verbindung endgültig zu schließen, muss die Funktion [[HttpUtils#HttpUtils_Close|HttpUtils_Close]] aufgerufen werden.&lt;br /&gt;
&lt;br /&gt;
Standardwert: 0&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$param-&amp;gt;{header}&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;optional&#039;&#039;&lt;br /&gt;
|| Eigene HTTP-Header-Zeilen können über diesen Parameter eingebracht werden. Er kann dazu genutzt werden um z.B. den Content-Type festzulegen, oder einfach nur zusätzliche Header-Felder zu setzen. Es gibt zwei Möglichkeiten, diesen Parameter zu befüllen.&lt;br /&gt;
&lt;br /&gt;
# Als &#039;&#039;&#039;Zeichenkette&#039;&#039;&#039;. Mehrere Header-Zeilen müssen dabei mit &amp;quot;\r\n&amp;quot; getrennt werden.&lt;br /&gt;
# Als &#039;&#039;&#039;Hash-Struktur&#039;&#039;&#039;. Hierbei findet eine Zuordnung von Header-Bezeichnung zum Wert als Key-Value-Pair statt.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
* &amp;lt;code&amp;gt;&amp;quot;User-Agent: Mozilla/1.22&#039;&#039;&#039;&amp;lt;u&amp;gt;\r\n&amp;lt;/u&amp;gt;&#039;&#039;&#039;Content-Type: application/xml&amp;quot;&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;&amp;quot;Content-Type: application/xml&amp;quot;&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;{ &amp;quot;User-Agent&amp;quot; =&amp;gt;  &amp;quot;Mozilla/1.22&amp;quot;, &amp;quot;Content-Type&amp;quot; =&amp;gt; &amp;quot;application/xml&amp;quot; }&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;{ &amp;quot;Content-Type&amp;quot; =&amp;gt; &amp;quot;application/xml&amp;quot; }&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Standardwert: &#039;&#039;[leer]&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$param-&amp;gt;{sslargs}&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;optional&#039;&#039;&lt;br /&gt;
|| Eigene SSL-Optionen können über diesen Parameter eingebracht werden. Er kann dazu genutzt werden um z.B. die SSL-Zertifikats Verifikation abzuschalten. Die SSL-Optionen müssen als eigene Hash-Referenz übergeben werden. Eine Liste aller möglichen Optionen findet man in der Perl-Dokumentation zu [http://search.cpan.org/~sullr/IO-Socket-SSL-2.016/lib/IO/Socket/SSL.pod#Description_Of_Methods IO::Socket::SSL].&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
* &amp;lt;code&amp;gt;$param-&amp;gt;{sslargs} = { SSL_verify_mode =&amp;gt; 0, sslOpt2 =&amp;gt; &#039;sslVal2&#039; }&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Standardwert: &#039;&#039;{ }&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$param-&amp;gt;{httpversion}&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;optional&#039;&#039;&lt;br /&gt;
|| Die HTTP-Version, welche zur Abfrage verwendet werden soll. Standardmäßig werden alle Abfragen mit HTTP/1.0 durchgeführt. Falls es jedoch notwendig ist HTTP/1.1 zu verwenden, so sollte &amp;lt;code&amp;gt;$param-&amp;gt;{httpversion}&amp;lt;/code&amp;gt; auf &amp;quot;1.1&amp;quot; gesetzt werden. Bei Version 1.1 wird automatisch der Header &amp;quot;&amp;lt;code&amp;gt;Connection: close&amp;lt;/code&amp;gt;&amp;quot; implizit mitgesendet.&lt;br /&gt;
&lt;br /&gt;
Standardwert: &amp;quot;1.0&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$param-&amp;gt;{digest}&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;optional&#039;&#039;&lt;br /&gt;
|| Wenn dieser Parameter den Wert 1 trägt, wird bei vorhandenen Authentifizierungsdaten (Username+Passwort) eine Authentifizierung via Digest-Verfahren nach RFC 2617&amp;lt;ref name=&amp;quot;rfc2617&amp;quot;&amp;gt;RFC 2617 - HTTP Authentication: Basic and Digest Access Authentication&amp;lt;/ref&amp;gt; erwartet. Die Anmeldedaten werden dann nur verwendet, wenn dieser explizit eine HTTP Digest Authentifizierung einleitet. Ist dieser Parameter nicht gesetzt (Wert: 0), wird bei vorhandenen Authentifizierungsdaten immer eine HTTP Basic&amp;lt;ref name=&amp;quot;rfc2617&amp;quot; /&amp;gt; Authentifizierung im Request mitgeschickt.&lt;br /&gt;
&lt;br /&gt;
Standardwert: 0&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$param-&amp;gt;{compress}&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;optional&#039;&#039;&lt;br /&gt;
|| Wenn dieser Parameter den Wert 1 trägt, wird dem Server die Verwendung von Komprimierung in Form GZIP oder alternativ Deflate&amp;lt;ref&amp;gt;RFC 1951 - DEFLATE Compressed Data Format Specification version 1.3&amp;lt;/ref&amp;gt; zur Übertragung der Antwort ermöglicht. Die komprimierte Antwort wird dabei direkt durch HttpUtils.pm wieder dekomprimiert. Wird dieser Parameter auf den Wert 0 gesetzt, so wird keine Komrpimierung ermöglicht.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;WICHTIG&#039;&#039;&#039;: Die Verwendung von Komprimierung in HttpUtils.pm kann mit dem globalen Attribut &amp;quot;httpcompress&amp;quot; (Standardwert: 1) durch setzen auf 0 für die gesamte FHEM Installation durch den Nutzer deaktiviert werden. In diesem Fall ist der Parameter &amp;lt;code&amp;gt;$param-&amp;gt;{compress} = 1&amp;lt;/code&amp;gt; wirkungslos.&lt;br /&gt;
&lt;br /&gt;
Standardwert: 1&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Als Rückgabewert von HttpUtils_BlockingGet wird ein Array mit zwei Rückgabewerten zurückgegeben:&lt;br /&gt;
:&amp;lt;code&amp;gt;($err, $data) = HttpUtils_BlockingGet( … )&amp;lt;/code&amp;gt;&lt;br /&gt;
Diese zwei Rückgabewerte haben folgende Bedeutung:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Rückgabewert !! Bedeutung&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$err&amp;lt;/code&amp;gt;&#039;&#039;&#039;|| Falls beim Aufruf der URL ein Fehler aufgetreten ist (z.B. Server nicht erreichbar oder Verbindungstimeout), dann ist dieser Wert mit einer Fehlermeldung gefüllt. &lt;br /&gt;
&lt;br /&gt;
Wenn kein Fehler aufgetreten ist, ist dieser Wert mit einem Leerstring gefüllt (&amp;lt;code&amp;gt;$err = &amp;quot;&amp;quot;&amp;lt;/code&amp;gt;)&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$data&amp;lt;/code&amp;gt;&#039;&#039;&#039;|| Die Ergebnisdaten, welche der HTTP-Server zurückgeliefert hat. Die Daten werden als Klartext in Form eines gesamten Strings zurückgegeben.&lt;br /&gt;
&lt;br /&gt;
Falls ein Fehler aufgetreten ist, ist dieser Wert mit einem Leersting gefüllt (&amp;lt;code&amp;gt;$data = &amp;quot;&amp;quot;&amp;lt;/code&amp;gt;)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== HttpUtils_NonblockingGet ===&lt;br /&gt;
{{Randnotiz|RNText=Die Funktion HttpUtils_NonblockingGet ist nicht komplett durchgehend &amp;quot;non-blocking&amp;quot;. DNS-Abfragen sind weiterhin blockierend. Insbesondere wenn der DNS-Name nicht aufgelöst werden kann.}}&lt;br /&gt;
Diese Funktion arbeitet ähnlich wie HttpUtils_BlockingGet. Allerdings wird das Ergebnis nicht als Funktionsergebnis zurückgegeben. Die Funktion HttpUtils_NonblockingGet initiiert den Verbindungsaufbau und übergibt alles weitere an FHEM interne Routinen. Sobald eine Antwort vom HTTP-Server eintrifft, wird eine Callback-Funktion mit verschiedenen Parametern (unter anderem auch das Ergebnis) aufgerufen, um die Antwort entgegenzunehmen und weiter zu verarbeiten.&lt;br /&gt;
&lt;br /&gt;
Der Aufruf ist daher ähnlich zu HttpUtils_BlockingGet mit nur einem Parameter-Hash:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Aufruf: &amp;lt;code&amp;gt;HttpUtils_NonblockingGet($param)&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;width:175px&amp;quot; | Parameter !! style=&amp;quot;width:auto&amp;quot; | Bedeutung&lt;br /&gt;
|-&lt;br /&gt;
| colspan=&amp;quot;2&amp;quot; style=&amp;quot;text-align:center&amp;quot; | &#039;&#039;&#039;&#039;&#039;&amp;lt;br&amp;gt;Alle Hash-Parameter, welche für HttpUtils_BlockingGet gelten, sind auch für HttpUtils_NonblockingGet gültig&#039;&#039;&#039;&#039;&#039;&amp;lt;br&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$param-&amp;gt;{callback}&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;mandatory&#039;&#039;&lt;br /&gt;
|| Eine Funktion (oder eine Referenz auf eine Funktion), welche die Ergebnisdaten entgegennimmt und die Antwort entsprechend weiterverarbeitet. Die Callback-Funktion muss dabei 3 Parameter erwarten. Die Funktionsparameter der Callback-Funktion werden im nachfolgenden Abschnitt näher erläutert.&lt;br /&gt;
&lt;br /&gt;
Beispiel: &lt;br /&gt;
* &amp;lt;code&amp;gt;$param-&amp;gt;{callback} = \&amp;amp;MyCallbackFn&amp;lt;/code&amp;gt; — &#039;&#039;(Referenzzeiger auf Funktionsname)&#039;&#039;&lt;br /&gt;
* &amp;lt;code&amp;gt;$param-&amp;gt;{callback} = sub(){ … }&amp;lt;/code&amp;gt; —  &#039;&#039;(direkte Funktionsdefinition)&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top;white-space:nowrap;&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$param-&amp;gt;{incrementalTimeout}&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;optional&#039;&#039;&lt;br /&gt;
|| Sofern gesetzt (Wert: &amp;lt;code&amp;gt;1&amp;lt;/code&amp;gt;), wird der Timeout nach jedem Empfang von Teil-Daten wieder zurückgesetzt. So können größere Datenmengen von langsamen Sendern empfangen werden, ohne den Parameter &amp;lt;code&amp;gt;timeout&amp;lt;/code&amp;gt; zu erhöhen und dadurch tatsächliche Verbindungsprobleme erst spät zu erkennen.&lt;br /&gt;
&lt;br /&gt;
Standardwert: 0&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;Benutzerdefinierte Parameter&#039;&#039; &lt;br /&gt;
|| Es können im Hash weitere benutzerdefinierte Parameter gesetzt werden, welche evtl. in der Callback-Funktion benötigt werden, um die Antwort korrekt zu verarbeiten.&lt;br /&gt;
&lt;br /&gt;
Zum Beispiel im Rahmen der Modul-Programmierung wäre das &amp;lt;code&amp;gt;$hash&amp;lt;/code&amp;gt; der aktuellen Definition. Alle gesetzten Parameter sind in der Callback-Funktion wieder über &amp;lt;code&amp;gt;$param&amp;lt;/code&amp;gt; direkt abrufbar und können ausgewertet werden.&lt;br /&gt;
&lt;br /&gt;
Beispiel: &lt;br /&gt;
* &amp;lt;code&amp;gt;$param-&amp;gt;{hash} = $hash&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;$param-&amp;gt;{command} = &amp;quot;on&amp;quot;&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
|} &lt;br /&gt;
&lt;br /&gt;
Ein Funktionsrückgabewert von HttpUtils_NonblockingGet existiert nicht, da die eigentliche Rückgabe der Daten über die Callback-Funktion erfolgt. Die Callback-Funktion wird aufgerufen, sobdald der HTTP-Request abgeschlossen ist, oder ein Fehler aufgetreten ist. Der Funktionsaufruf erfolgt mit den folgenden Parametern:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt; &#039;&#039;MyCallbackFn&#039;&#039; ( $param, $err, $data )&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Diese 3 Parameter haben dabei folgende Bedeutung:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Parameter !! Bedeutung&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$param&amp;lt;/code&amp;gt;&#039;&#039;&#039; || Der Parameter-Hash, mit allen Argumenten die beim Aufruf der Funktion übergeben worden sind.&lt;br /&gt;
&lt;br /&gt;
Es ist möglich, dass der Parameter-Hash nach erfolgter Abfrage zusätzliche oder veränderte Elemente enthält:&lt;br /&gt;
* &#039;&#039;&#039;&amp;lt;code&amp;gt;$param-&amp;gt;{redirects}&amp;lt;/code&amp;gt;&#039;&#039;&#039; - Die Anzahl an Umleitungen die erfolgte, bis die Anfrage beantwortet wurde.&lt;br /&gt;
* &#039;&#039;&#039;&amp;lt;code&amp;gt;$param-&amp;gt;{url}&amp;lt;/code&amp;gt;&#039;&#039;&#039; - Die URL, die nach erfolgter Umleitung die Anfrage beantwortet hat.&lt;br /&gt;
* &#039;&#039;&#039;&amp;lt;code&amp;gt;$param-&amp;gt;{protocol}&amp;lt;/code&amp;gt;&#039;&#039;&#039; - Das verwendete Protokoll, welches zur Abfrage verwendet wurde (&amp;quot;http&amp;quot; oder &amp;quot;https&amp;quot;).&lt;br /&gt;
* &#039;&#039;&#039;&amp;lt;code&amp;gt;$param-&amp;gt;{path}&amp;lt;/code&amp;gt;&#039;&#039;&#039; - Der Pfad, welcher auf dem HTTP-Server angefragt wurde.&lt;br /&gt;
* &#039;&#039;&#039;&amp;lt;code&amp;gt;$param-&amp;gt;{host}&amp;lt;/code&amp;gt;&#039;&#039;&#039; - Der Name oder die IP-Adresse des HTTP-Servers.&lt;br /&gt;
* &#039;&#039;&#039;&amp;lt;code&amp;gt;$param-&amp;gt;{httpheader}&amp;lt;/code&amp;gt;&#039;&#039;&#039; - Der gesamte HTTP Header, welcher der Server bei der letzten Antwort zurücklieferte.&lt;br /&gt;
* &#039;&#039;&#039;&amp;lt;code&amp;gt;$param-&amp;gt;{code}&amp;lt;/code&amp;gt;&#039;&#039;&#039; - Der HTTP-Statuscode, mit dem die Anfrage vom Server beantwortet wurde.&lt;br /&gt;
* &#039;&#039;&#039;&amp;lt;code&amp;gt;$param-&amp;gt;{addr}&amp;lt;/code&amp;gt;&#039;&#039;&#039; - Die HTTP-URL ohne Pfad und evtl. Authentifizerungsinformationen des HTTP-Servers (z.B. &amp;quot;&amp;lt;nowiki&amp;gt;http://myserver.com:8080&amp;lt;/nowiki&amp;gt;&amp;quot;).&lt;br /&gt;
* &#039;&#039;&#039;&amp;lt;code&amp;gt;$param-&amp;gt;{auth}&amp;lt;/code&amp;gt;&#039;&#039;&#039; - Ein Flag (0/1) ob Zugangsdaten für den Request zur Verfügung standen (durch Angabe in der URL oder direkt in &amp;lt;code&amp;gt;$param&amp;lt;/code&amp;gt;).&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$err&amp;lt;/code&amp;gt;&#039;&#039;&#039;|| Falls beim Aufruf der URL ein Fehler aufgetreten ist (z.B. Server nicht erreichbar oder Verbindungstimeout), dann ist dieser Wert mit einer Fehlermeldung gefüllt. &lt;br /&gt;
&lt;br /&gt;
Wenn kein Fehler aufgetreten ist, ist dieser Wert mit einem Leerstring gefüllt (&amp;lt;code&amp;gt;$err = &amp;quot;&amp;quot;&amp;lt;/code&amp;gt;)&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$data&amp;lt;/code&amp;gt;&#039;&#039;&#039;|| Die Ergebnisdaten, welche der HTTP-Server zurückgeliefert hat. Die Daten werden als Klartext in Form eines gesamten Strings zurückgegeben. Es handelt sich hierbei ausschließlich um den HTTP-Body. &lt;br /&gt;
&lt;br /&gt;
Falls ein Fehler aufgetreten ist, ist dieser Wert mit einem Leersting gefüllt (&amp;lt;code&amp;gt;$data = &amp;quot;&amp;quot;&amp;lt;/code&amp;gt;)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Die Callback-Funktion kann nun die Daten aus der HTTP-Antwort verarbeiten oder bei Fehlern entsprechende Log-Meldungen ausgeben.&lt;br /&gt;
&lt;br /&gt;
==== Beispiel #1 für HttpUtils_NonblockingGet() für Modulprogrammierer ====&lt;br /&gt;
Das folgende Beispiel soll eine Hilfestellung für eigene Anwendungen geben&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;use HttpUtils;&lt;br /&gt;
sub X_PerformHttpRequest($)&lt;br /&gt;
{&lt;br /&gt;
    my ($hash, $def) = @_;&lt;br /&gt;
    my $name = $hash-&amp;gt;{NAME};&lt;br /&gt;
    my $param = {&lt;br /&gt;
                    url        =&amp;gt; &amp;quot;http://www.foo.de/&amp;quot;,&lt;br /&gt;
                    timeout    =&amp;gt; 5,&lt;br /&gt;
                    hash       =&amp;gt; $hash,                                                                                 # Muss gesetzt werden, damit die Callback funktion wieder $hash hat&lt;br /&gt;
                    method     =&amp;gt; &amp;quot;GET&amp;quot;,                                                                                 # Lesen von Inhalten&lt;br /&gt;
                    header     =&amp;gt; &amp;quot;User-Agent: TeleHeater/2.2.3\r\nAccept: application/json&amp;quot;,                            # Den Header gemäß abzufragender Daten ändern&lt;br /&gt;
                    callback   =&amp;gt; \&amp;amp;X_ParseHttpResponse                                                                  # Diese Funktion soll das Ergebnis dieser HTTP Anfrage bearbeiten&lt;br /&gt;
                };&lt;br /&gt;
&lt;br /&gt;
    HttpUtils_NonblockingGet($param);                                                                                    # Starten der HTTP Abfrage. Es gibt keinen Return-Code. &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
sub X_ParseHttpResponse($)&lt;br /&gt;
{&lt;br /&gt;
    my ($param, $err, $data) = @_;&lt;br /&gt;
    my $hash = $param-&amp;gt;{hash};&lt;br /&gt;
    my $name = $hash-&amp;gt;{NAME};&lt;br /&gt;
&lt;br /&gt;
    if($err ne &amp;quot;&amp;quot;)                                                                                                      # wenn ein Fehler bei der HTTP Abfrage aufgetreten ist&lt;br /&gt;
    {&lt;br /&gt;
        Log3 $name, 3, &amp;quot;error while requesting &amp;quot;.$param-&amp;gt;{url}.&amp;quot; - $err&amp;quot;;                                               # Eintrag fürs Log&lt;br /&gt;
        readingsSingleUpdate($hash, &amp;quot;fullResponse&amp;quot;, &amp;quot;ERROR&amp;quot;, 0);                                                        # Readings erzeugen&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    elsif($data ne &amp;quot;&amp;quot;)                                                                                                  # wenn die Abfrage erfolgreich war ($data enthält die Ergebnisdaten des HTTP Aufrufes)&lt;br /&gt;
    {&lt;br /&gt;
        Log3 $name, 3, &amp;quot;url &amp;quot;.$param-&amp;gt;{url}.&amp;quot; returned: $data&amp;quot;;                                                         # Eintrag fürs Log&lt;br /&gt;
&lt;br /&gt;
        # An dieser Stelle die Antwort parsen / verarbeiten mit $data&lt;br /&gt;
&lt;br /&gt;
        readingsSingleUpdate($hash, &amp;quot;fullResponse&amp;quot;, $data, 0);                                                          # Readings erzeugen&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    # Damit ist die Abfrage zuende.&lt;br /&gt;
    # Evtl. einen InternalTimer neu schedulen&lt;br /&gt;
}&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Beispiel #2 eines Snippets um eine Datei herunterzuladen und lokal zu speichern ====&lt;br /&gt;
Dieses vollständige Beispiel eines Device verwendet &amp;lt;code&amp;gt;HttpUtils_NonblockingGet()&amp;lt;/code&amp;gt; um eine Datei herunterzuladen und im lokalem Dateisystem mit den Rechten des FHEM Nutzers zu speichern.&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
defmod MyDownloader dummy&lt;br /&gt;
attr MyDownloader userattr FilePath&lt;br /&gt;
attr MyDownloader comment &#039;\&lt;br /&gt;
Beispiel:\&lt;br /&gt;
set MyDownloader job Filepath=&amp;quot;/opt/fhem/www/Dateiname.bin&amp;quot; https://example.com/datei.bin\&lt;br /&gt;
&#039;&lt;br /&gt;
attr MyDownloader readingList job&lt;br /&gt;
attr MyDownloader setList job&lt;br /&gt;
attr MyDownloader userReadings result:job:.* {\&lt;br /&gt;
	my $hash = $defs{$name};;\&lt;br /&gt;
	my $job = ReadingsVal($name, &amp;quot;job&amp;quot;, &#039;https://fhem.de/www/images/default/fhemicon.png&#039;);;\&lt;br /&gt;
	my $path = AttrVal($name, &amp;quot;Filepath&amp;quot;, &amp;quot;/opt/fhem/www/download.bin&amp;quot;);;\&lt;br /&gt;
	\&lt;br /&gt;
	if ($job =~ s/(?:Filepath|Dateipfad)=&amp;quot;(.*?)&amp;quot;//) {\&lt;br /&gt;
		$path = $1;;\&lt;br /&gt;
	}\&lt;br /&gt;
	\&lt;br /&gt;
	my $fnFct = sub {\&lt;br /&gt;
		my ($param, $err, $data) = @_;;\&lt;br /&gt;
		\&lt;br /&gt;
		my $len = $data ? length($data) : 0;;\&lt;br /&gt;
		my $error = $err ? $err : FileWrite($path, $data);;\&lt;br /&gt;
		\&lt;br /&gt;
		readingsBeginUpdate($hash);;\&lt;br /&gt;
		readingsBulkUpdate($hash, &amp;quot;busy&amp;quot;, 0, 1);;\&lt;br /&gt;
		readingsBulkUpdate($hash, &amp;quot;result&amp;quot;, &amp;quot;saved $len Bytes&amp;quot;, 1) if !$error;;\&lt;br /&gt;
		readingsBulkUpdate($hash, &amp;quot;result&amp;quot;, &amp;quot;error ($error, $err)&amp;quot;, 1) if $error;;\&lt;br /&gt;
		readingsEndUpdate($hash, 1);;\&lt;br /&gt;
	};;\&lt;br /&gt;
	\&lt;br /&gt;
	if ( ReadingsVal($name, &amp;quot;busy&amp;quot;, 0) == 0 ) {\&lt;br /&gt;
		readingsBulkUpdate($hash, &amp;quot;busy&amp;quot;, 1, 1);;\&lt;br /&gt;
		$job =~ s/^\s+|\s+$//g;;\&lt;br /&gt;
		my $param = {\&lt;br /&gt;
                    url        =&amp;gt; $job,\&lt;br /&gt;
                    timeout    =&amp;gt; 10,\&lt;br /&gt;
                    hash       =&amp;gt; $hash,\&lt;br /&gt;
                    method     =&amp;gt; &amp;quot;GET&amp;quot;,\&lt;br /&gt;
                    callback   =&amp;gt; $fnFct\&lt;br /&gt;
                };;\&lt;br /&gt;
		HttpUtils_NonblockingGet($param);;\&lt;br /&gt;
		return &amp;quot;download job started&amp;quot;;;\&lt;br /&gt;
	}\&lt;br /&gt;
	\&lt;br /&gt;
	return;;\&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Zugehöriger Forumspost: {{Link2Forum|Topic=143117|LinkText=Datei herunterladen, Downloader, HttpUtils_NonblockingGet}}&lt;br /&gt;
&lt;br /&gt;
=== HttpUtils_Close ===&lt;br /&gt;
Für den Abbruch von offenen Verbindungen und noch laufenden NonBockingGet-Aufrufen gibt es die Funktion HttpUtils_Close. Diese kann z.B. beim Löschen eines Devices oder Herunterfahren des Servers aufgerufen werden, um bestehende Verbindungen zu schliessen.&lt;br /&gt;
&lt;br /&gt;
Wenn man den Parameter &amp;quot;keepalive&amp;quot; beim Aufruf von HttpUtils_NonBlockingGet/HttpUtils_BlockingGet gesetzt hat, muss man HttpUtils_Close aufrufen um die Verbindung tatsächlich zu schließen, da diese noch durch den Server offen gehalten werden kann.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Aufruf: &amp;lt;code&amp;gt;HttpUtils_Close($param)&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;width:175px&amp;quot; | Parameter !! style=&amp;quot;width:auto&amp;quot; | Bedeutung&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$param&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;mandatory&#039;&#039;&lt;br /&gt;
|| Der Hash, der beim vorherigen Aufruf an HTTPUtils Funktionen (HttpUtils_NonblockingGet oder HttpUtils_BlockingGet) übergeben wurde&lt;br /&gt;
&lt;br /&gt;
|} &lt;br /&gt;
&lt;br /&gt;
== Hilfsfunktionen ==&lt;br /&gt;
&lt;br /&gt;
=== urlEncode ===&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
$encoded = urlEncode($string);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Die Funktion urlEncode() wandelt die übergebene Zeichenkette &amp;lt;code&amp;gt;$string&amp;lt;/code&amp;gt; in eine URL-kompatible&amp;lt;ref name=&amp;quot;rfc3986-2.1&amp;quot;&amp;gt;[https://tools.ietf.org/html/rfc3986#section-2.1 RFC 3989] - Abschnitt 2.1 &amp;quot;Percent-Encoding&amp;quot;&amp;lt;/ref&amp;gt; Zeichenkette um. Der Rückgabewert &amp;lt;code&amp;gt;$encoded&amp;lt;/code&amp;gt; ist eine Zeichenkette welche innerhalb einer URL verwendet werden kann, bspw. als Wert eines GET-Parameters.&lt;br /&gt;
&lt;br /&gt;
Parameter:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Parameter !! Bedeutung&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$string&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;mandatory&#039;&#039;&lt;br /&gt;
|| Die Zeichenkette, welche in eine URL-kompatible Form umgewandelt werden soll.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Rückgabewerte:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Rückgabewert !! Bedeutung&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$encoded &amp;lt;/code&amp;gt;&#039;&#039;&#039; || Die URL-kompatible Version der übergebenen Zeichenkette. Problematische Zeichen sind in diesem Wert durch ein Prozentzeichen gefolgt von der Hexadezimaldarstellung des Zeichens dargestellt.&lt;br /&gt;
&lt;br /&gt;
Bsp:&lt;br /&gt;
* &amp;quot;a: b&amp;quot; =&amp;gt; &amp;quot;a%3a%20b&amp;quot;&lt;br /&gt;
* &amp;quot;a/b&amp;quot; =&amp;gt; &amp;quot;a%2fb&amp;quot;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== urlDecode ===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
$string = urlDecode($encoded);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Die Funktion urlDecode () normalisiert die übergebene Zeichenkette &amp;lt;code&amp;gt;$encoded&amp;lt;/code&amp;gt; und ersetzt dabei sämtliche Hexadezimaldarstellungen&amp;lt;ref name=&amp;quot;rfc3986-2.1&amp;quot; /&amp;gt; (eingeleitet mit einem Prozentzeichen) durch die entsprechenden Zeichen. Der Rückgabewert &amp;lt;code&amp;gt;$string&amp;lt;/code&amp;gt; ist eine Zeichenkette welche keinerlei Hexadezimaldarstellungen mehr aufweist.&lt;br /&gt;
&lt;br /&gt;
Parameter:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Parameter !! Bedeutung&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$encoded&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;mandatory&#039;&#039;&lt;br /&gt;
|| Eine Zeichenkette, welche URL-kombatible Hexadezimaldarstellungen enthält.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Rückgabewerte:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Rückgabewert !! Bedeutung&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$string&amp;lt;/code&amp;gt;&#039;&#039;&#039; || Die normale Version der übergebenen Zeichenkette. Sämtliche Hexadezimalddarstellungen sind in ihre entsprechenden Zeichen umgewandelt.&lt;br /&gt;
&lt;br /&gt;
Bsp:&lt;br /&gt;
* &amp;quot;a%3a%20b&amp;quot; =&amp;gt; &amp;quot;a: b&amp;quot;&lt;br /&gt;
* &amp;quot;a%2fb&amp;quot; =&amp;gt; &amp;quot;a/b&amp;quot;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Referenzen ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;references /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Development]]&lt;/div&gt;</summary>
		<author><name>Ph1959de</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=SolarForecast_-_Solare_Prognose_(PV_Erzeugung)_und_Verbrauchersteuerung&amp;diff=40493</id>
		<title>SolarForecast - Solare Prognose (PV Erzeugung) und Verbrauchersteuerung</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=SolarForecast_-_Solare_Prognose_(PV_Erzeugung)_und_Verbrauchersteuerung&amp;diff=40493"/>
		<updated>2025-11-11T10:42:40Z</updated>

		<summary type="html">&lt;p&gt;Ph1959de: Verweise auf Wiki-Seiten auf ... umgestellt&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Baustelle}}&lt;br /&gt;
{{Hinweis|Die vorliegende Wiki-Seite befindet sich im Aufbau.}}&lt;br /&gt;
{{Infobox Modul&lt;br /&gt;
|ModPurpose=Solarprognose und Verbrauchersteuerung&lt;br /&gt;
|ModType=d&lt;br /&gt;
|ModCmdRef=76_SolarForecast&lt;br /&gt;
|ModFTopic=117864&lt;br /&gt;
|ModForumArea=Solaranlagen&lt;br /&gt;
|ModTechName=76_SolarForecast.pm&lt;br /&gt;
|ModOwner=DS_Starter ({{Link2FU|16933|Forum}}/[[Benutzer Diskussion:DS_Starter|Wiki]])&lt;br /&gt;
}}&lt;br /&gt;
[[SolarForecast - Solare Prognose (PV Erzeugung) und Verbrauchersteuerung|SolarForecast]] ist ein integratives Modul zur Gewinnung solarer Vorhersagedaten, deren Verarbeitung und grafischen Darstellung. Desweiteren bietet es die Möglichkeit, in FHEM definierte Verbraucher in einem SolarForecast-Device zu registrieren und eine PV Prognose basierte Steuerung der Verbraucher vom Modul übernehmen zu lassen.&lt;br /&gt;
&lt;br /&gt;
Das Modul ist insbesondere durch folgende Eigenschaften gekennzeichnet:&lt;br /&gt;
[[Datei:sfc1.png|right|thumb|300px|grafische Ansicht SolarForecast]]&lt;br /&gt;
&lt;br /&gt;
::* zur Gewinnung solarer Prognosewerte können die SolCast API, OpenMeteo API, Forecast.Solar API, Victron VRM Portal API oder alternativ DWD Stahlungswerte-Stationen verwendet werden&lt;br /&gt;
&lt;br /&gt;
::* optionale KI Unterstützung der PV-Prognose bei Verwendung von geeigneten Solar-API&#039;s&lt;br /&gt;
&lt;br /&gt;
::* es wird sowohl die Erzeugungsprognose als auch eine Verbrauchsprognose sowie SoC Prognose (bei Batterie-Integration) erstellt&lt;br /&gt;
&lt;br /&gt;
::* Wetterdaten werden über DWD Wetter-Stationen oder verschiedene API integriert. Es können verschiedene API zur Gewinnung von Solar- und Wetterdaten kombiniert werden. &lt;br /&gt;
&lt;br /&gt;
::* Sprachensupport EN / DE&lt;br /&gt;
&lt;br /&gt;
::* die Prognosedaten, Wetterdaten, Verbraucherplanungen und die aktuellen Energieflüsse werden in umfangreich anpassbaren integrierten Grafiken dargestellt&lt;br /&gt;
&lt;br /&gt;
::* es wird keine externe SQL-Datenbank benötigt, die Datenhaltung erfolgt in einer Memory basierten Cachedatenbank inkl. einer Filesystempersistenz zur Datensicherung und Wiederherstellung beim FHEM-Restart&lt;br /&gt;
&lt;br /&gt;
::* die Integration von Geräten wie Wechselrichter, Energy Meter, Batterien, Wetterstationen oder Verbrauchern ist offen und universell gestaltet und bietet dem Anwender maximale Freiheiten bei der Einrichtung seines individuellen Solardatensystems.&lt;br /&gt;
&lt;br /&gt;
::* eine Integration von bis zu 4 Inverter und Smartloader (DC-DC Batterieladegeräte) ist möglich&lt;br /&gt;
&lt;br /&gt;
::* eine Integration von bis zu 3 nicht PV-Energieerzeugern wie BHKW, Windrädern oder Notstromaggregaten ist möglich&lt;br /&gt;
&lt;br /&gt;
::* eine integrierte und umfangreich anpassbare Verbrauchersteuerung vereint die PV Prognose basierte Einplanung der Verbraucher mit der Möglichkeit die Verbraucher durch das Modul schalten zu lassen und dadurch auf PV Erzeugungsschwankungen automatisch dynamisch zu reagieren&lt;br /&gt;
&lt;br /&gt;
::* Einbindung von bis zu drei Batteriespeichersystemen&lt;br /&gt;
&lt;br /&gt;
::* Bereitstellung von Leitwerten zur optimalen Einstellung/Steuerung von Batteriespeichersystemen&lt;br /&gt;
&lt;br /&gt;
::* trotz der hohen Komplexität wird dem Anwender durch eine &amp;quot;Guided Procedure&amp;quot; bei der Gerätedefinition der Einstieg erleichtert und damit ein optimales Benutzererlebnis geboten&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Abgrenzung:&#039;&#039;&#039; Das Modul 76_SolarForecast ist nicht zu verwechseln mit der SQL-basierten (DbLog) Prognose-Lösung mit Kostal Plenticore Wechselrichtern, die auf der eigenen Seite &#039;&#039;&#039;[[Kostal_Plenticore_10_Plus]]&#039;&#039;&#039; behandelt wird. Diese Lösung beschreibt kein monolithisches Modul, sondern basiert auf einer orchestrierbaren Zusammenstellung individueller Perl Programmbausteine. &lt;br /&gt;
&lt;br /&gt;
Im vorliegenden Wiki-Beitrag wird ausschließlich das Modul 76_SolarForecast behandelt.&lt;br /&gt;
&lt;br /&gt;
== Rahmenbedingungen und Voraussetzungen ==&lt;br /&gt;
&lt;br /&gt;
Das Wiki gibt allgemeingültige und teilweise sehr spezifische bzw. tiefgehende Informationen und Handlungsempfehlungen zum SolarForecast Modul. Die dargestellten Informationen beziehen sich auf den zum Zeitpunkt der Wiki-Erstellung vorhandenen Entwicklungsstand des Moduls. Durch die stetige Weiterentwicklung können sich Abweichungen zum beschriebenen Inhalt ergeben oder zur Obsoleszenz dieser Inhalte führen. &lt;br /&gt;
&lt;br /&gt;
Vergleiche deshalb die Informationen immer mit der aktuellen Befehls-Referenz zum Modul!&lt;br /&gt;
&lt;br /&gt;
== Definition ==&lt;br /&gt;
Ein SolarForecast Device wird mit dem Befehl&lt;br /&gt;
&lt;br /&gt;
 define &amp;lt;Name&amp;gt; SolarForecast &lt;br /&gt;
&lt;br /&gt;
angelegt. Nach der Definition wird der User durch einen Dialog geführt, der einige unerlässliche Einstellungen abfragt und in den entsprechenden Attributen und Readings persistiert. Alle Attribute und Readings die eine Einstellung der Anlage bewirken, sind mit dem Präfix &amp;quot;setup&amp;quot; versehen. &lt;br /&gt;
&lt;br /&gt;
Die Verwendung von setup-Readings gestattet es die Anlagenparameter dynamisch im Betrieb zu verändern. So kann mit &lt;br /&gt;
&lt;br /&gt;
 set &amp;lt;Name&amp;gt; setupStringAzimuth   bzw.&lt;br /&gt;
 set &amp;lt;Name&amp;gt; setupStringDeclination &lt;br /&gt;
&lt;br /&gt;
die Ausrichtung und der Neigungswinkel der PV-Module ständig angepasst werden, was zum Beispiel bei einer dem Sonnenstand nachgeführten Anlage von Bedeutung ist.&lt;br /&gt;
&lt;br /&gt;
Die abgefragten Einstellungen während des Dialoges sind ausführlich in den entsprechenden Attributen bzw. Set-Kommandos erläutert. Die benötigten Informationen sind anhängig von der gewählten Prognose-API und können im Umfang variieren.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Screenshot 2024-09-21 213257.png|right|thumb|400px|Anlagenprüfung über Drucktaste in der Kopfgrafik]]&lt;br /&gt;
&lt;br /&gt;
Sobald alle verlangten Einstellungen vorgenommen sind, sollte eine Anlagenprüfung mit &lt;br /&gt;
&lt;br /&gt;
 set &amp;lt;Name&amp;gt; plantConfiguration check&lt;br /&gt;
&lt;br /&gt;
oder über die angebotene Drucktaste in der Grafik durchgeführt werden. Es wird eine Plausibilitätsprüfung vorgenommen und das Ergebnis sowie eventuelle Hinweise bzw. Fehler ausgegeben.&lt;br /&gt;
Die Prüfung kann beliebig wiederholt werden und sollte sich bei jeder Einstellungsänderung der Anlage durchgeführt werden. Dadurch wird die Wahrscheinlichkeit von fehlerhaften Einstellungen minimiert.&lt;br /&gt;
&lt;br /&gt;
Neben den grundlegenden Einstellungen können im Device weitere sekundäre Anlagenkomponenten wie Batteriesystem(e) (Attribut setupBatteryDev1 - setupBatteryDev3), weitere Wetter-Devices (Attribut setupWeatherDev1 - setupWeatherDev3) oder sonstige Erzeuger (z.B. BHKW, Winderzeugung, Notstromaggregat) integriert werden (Attribut setupOtherProducer01 - setupOtherProducer03).&lt;br /&gt;
&lt;br /&gt;
Die verlangten Parameter der Anlage werden in der Online-Hilfe umfassend beschrieben und deren Funktion erschließt sich zum größten Teil selbst. Dennoch soll nachfolgend auf einige Punkte im Zusammenhang der Einrichtung eingegangen werden.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
=== Hinweise zur Struktur der Attribute ===&lt;br /&gt;
&lt;br /&gt;
Durch die umfangreichen Funktionen des Moduls würde bei Nutzung der im FHEM herkömmlich vorhandenen Attributstruktur die Anzahl der benötigten Attribute extrem stark steigen und nicht sinnvoll genutzt werden können. Deshalb gibt es neben den &#039;&#039;gewöhnlichen&#039;&#039; Attributen mit einfachen Werteauswahlmöglichkeiten oder Eingaben (z.B. ctrlDebug, ctrlSpecialReadings) sogenannte &#039;&#039;zusammengesetzte&#039;&#039; Attribute, deren Inhalt durch &#039;&#039;&#039;Schlüssel=Wert Paare&#039;&#039;&#039; gebildet wird und dadurch deutlich mehr Funktionen und Informationen transportieren als ein gewöhnliches Attribut.&lt;br /&gt;
&lt;br /&gt;
Für zusammengesetzte Attribute besteht die zusätzliche Möglichkeit, gewünschte Änderungen der Schlüssel=Wert Paare selektiv vornehmen zu können, ohne das gesamte Attribut aufrufen und ändern zu müssen. Die Vorgang, der vor allem für programmtechnische Manipulationen gedacht ist, kann durch eine Set-Funktion ausgeführt werden:&lt;br /&gt;
&lt;br /&gt;
 set &amp;lt;Name&amp;gt; attrKeyVal &amp;lt;Attribut&amp;gt; [&amp;lt;Gerät&amp;gt;] &amp;lt;Schlüssel=Wert&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Beispiele für zusammengesetzte Attribute sind:&lt;br /&gt;
&lt;br /&gt;
* aiControl&lt;br /&gt;
* plantControl&lt;br /&gt;
* setup* Attribute&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Die Funktionen der Attribute werden durch einen Präfix geclustert und so inhaltlich, aber vor allem auch in der Attribute Drop-Down Liste, strukturiert dargestellt.&lt;br /&gt;
&lt;br /&gt;
Die Bedeutung der &#039;&#039;&#039;Präfixe&#039;&#039;&#039; sind:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;ai&#039;&#039;&#039; - Steuerungsfunktionen für KI (z.B. aiControl)&lt;br /&gt;
* &#039;&#039;&#039;consumer&#039;&#039;&#039; - Registrierung und Einstellungen für Verbraucher (z.B. consumer02, consumerControl)&lt;br /&gt;
* &#039;&#039;&#039;ctrl&#039;&#039;&#039; - Steuerungsfunktion allgemeiner Art (z.B. ctrlDebug, ctrlSpecialReadings, ctrlUserExitFn)&lt;br /&gt;
* &#039;&#039;&#039;flowGraphic&#039;&#039;&#039; - Einstellungen für die Energieflußgrafik (z.B. flowGraphicControl)&lt;br /&gt;
* &#039;&#039;&#039;graphic&#039;&#039;&#039; - Einstellung für die Grafik allgemein (z.B. graphicSelect, graphicControl)&lt;br /&gt;
* &#039;&#039;&#039;graphicBeam&#039;&#039;&#039; -  Einstellungen bestimmte Balkeneigenschaften in der Balkengrafik (z.B. graphicBeamHeightLevel1)&lt;br /&gt;
* &#039;&#039;&#039;plant&#039;&#039;&#039; - übergreifende Einstellungen betreffend der gesamten PV-Anlage bzw. des SolarForecast-Devices (z.B. plantControl)&lt;br /&gt;
* &#039;&#039;&#039;setup&#039;&#039;&#039; - Registrierung von Geräten in der Gesamtanlage und Einstellung deren Eigenschaften (Inverter, Zähler, Batterien usw.)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Einrichtung der Wechselrichter mit setupInverterDevXX ===&lt;br /&gt;
&lt;br /&gt;
Mit den Attributen setupInverterDev01 bis setupInverterDevXX werden die vorhandenen Wechselrichter der Anlage registriert.&lt;br /&gt;
Das Modul unterscheidet verschiedene Wechselrichter-Typen, die sich durch ihre Arbeitsweise innerhalb der Anlage unterscheiden. Die Schlüssel=Wert Paare in den Attributen können mehrzeilig organisiert werden. Das erleichtert die Übersicht wenn viele dieser Parameter eingegeben werden.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
;PV-Wechselrichter: Es ist der Standard Wechselrichter in einer PV-Anlage. An ihn sind die Solarzellen bzw. Strings angeschlossen. Die zugeordneten Strings können mit dem Schlüssel &#039;&#039;&#039;strings&#039;&#039;&#039; zugeordnet werden. Ohne diese Angabe werden diesem Gerät alle definierten Strings (siehe Attribut &#039;&#039;setupInverterStrings&#039;&#039;) zugeordnet. Der Wechselrichter liefert seine Energie in das Hausnetz. Alternativ kann mit der Angabe &#039;&#039;&#039;feed=grid&#039;&#039;&#039; die Funktion des Wechselrichters geändert werden. Diese Angabe sagt aus, dass dieser Wechselrichter ausschließlich in das öffentliche Netz einspeist.&lt;br /&gt;
&lt;br /&gt;
Hier ein Beispiel-Setup eines PV-Wechselrichters (SMA STP):&lt;br /&gt;
&lt;br /&gt;
 STP_5000 pvIn=string_1_pdc:kW pvOut=total_pac:kW etotal=etotal:kWh capacity=5000 strings=Süddach asynchron=1 limit=100&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
;Solar-Ladegeräte: Solar-Ladegeräte sind DC-DC Wandler und liefern die Energie der angeschlossenen Solarzellen nicht in das Wechselstromnetz, sondern laden direkt eine Batterie bzw. versorgen einen Batteriewechselrichter auf der Gleichstromseite. Die Funktion als Solar-Ladegerät wird mit &#039;&#039;&#039;feed=bat&#039;&#039;&#039; aktiviert, die relevanten Strings können ebenfalls mit dem Schlüssel &#039;&#039;&#039;strings&#039;&#039;&#039; zugeordnet werden. Ein Beispiel eines Solar-Ladegerätes ist ein Victron SmartSolar MPPT. &lt;br /&gt;
&lt;br /&gt;
Ein Beispiel für das Setup eines Solar-Ladegeräts (SmartSolar Charger MPPT):&lt;br /&gt;
&lt;br /&gt;
 MQTT2_cerboGX_c0619ab34e08_solarcharger_Common pv=Yield_Power_value:W etotal=Yield_System_value:kWh capacity=2080 strings=Schleppdach feed=bat synchron=0 limit=100&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
;Batterie-Wechselrichter: Dieses Gerät hat keine angeschlossenen Solarzellen und arbeitet als DC-AC bzw. AC-DC Wandler zwischen einer Batterie (=Gleichstromquelle) und dem Wechselstrom-Hausnetz. Die Funktion als Batterie-Wechselrichter wird mit der Angabe &#039;&#039;&#039;strings=none&#039;&#039;&#039; aktiviert. Die Schlüssel &#039;&#039;etotal&#039;&#039; und &#039;&#039;pv&#039;&#039; können bei einem Batterie-Wechselrichter ohne zugeordnete Solarzellen nicht gesetzt werden. Ein Beispiel eines Batterie-Wechselrichter ist der Victron MultiPlus II.&lt;br /&gt;
&lt;br /&gt;
Ein Setup-Beispiel für einen Batterie-Wechselrichter (Victron MultiPlus II):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
dc2ac=DC_IN:W&lt;br /&gt;
ac2dc=DC_OUT:W&lt;br /&gt;
capacity=7200&lt;br /&gt;
strings=none&lt;br /&gt;
icon=inverter@darkorange:inverter@grey&lt;br /&gt;
asynchron=0&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Die Eigenschaften des Wechselrichters werden durch eine Reihe von &amp;lt;Schlüssel=Wert&amp;gt; Paaren festgelegt. Auf einige dieser Paramter soll hier eingegangen werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
==== Ausgewählte Schlüssel=Wert Paare ====&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;pvIn=&amp;lt;Readingname&amp;gt;:&amp;lt;Einheit&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
	&lt;br /&gt;
Dem Schlüssel &#039;&#039;pvIn&#039;&#039; wird ein Reading und dessen Einheit (W,kW) des Wechselrichter-Device zugeordnet, welches die aktuell zugeführte PV-Leistung bereitstellt. Der Schlüssel ist optional und dient in erster Linie dazu den entsprechenden Input-Wert in der Flußgrafik anzuzeigen sofern man das Attribut flowGraphicControl-&amp;gt;showGenerators=1 setzt. Dadurch ist auch der Eigenverbrauch des Inverters bzw. dessen Wandlungsverlust visuell abschätzbar.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;pvOut=&amp;lt;Readingname&amp;gt;:&amp;lt;Einheit&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
	&lt;br /&gt;
Dem Schlüssel &#039;&#039;pvOut&#039;&#039; wird ein Reading und dessen Einheit (W,kW) des Wechselrichter-Device zugeordnet, welches die aktuell erzeugte Leistung als positiven Wert liefert. Für einen PV-Wechselrichter (DC-&amp;gt;AC) oder Solar-Ladegerät (DC-&amp;gt;DC) ist es die erzeugte PV-Leistung.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;ac2dc=&amp;lt;Readingname&amp;gt;:&amp;lt;Einheit&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Dieser Schlüssel ist nur für einen Batterie-Wechselrichter setzbar. Das Reading liefert die aktuelle Leistung, die der Wechselrichter vom Hausnetz in Richtung Batterie (AC-&amp;gt;DC) wandelt. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;dc2ac=&amp;lt;Readingname&amp;gt;:&amp;lt;Einheit&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Dieser Schlüssel ist nur für einen Batterie-Wechselrichter setzbar. Das Reading liefert die aktuelle Leistung, die der Wechselrichter aus einer Gleichstromquelle, d.h. einer Batterie oder einem Solar-Ladegerät, in Richtung des Hausnetzes (AC) wandelt . &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;capacity=&amp;lt;max. WR-Leistung&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
In dem Schlüssel &#039;&#039;capacity&#039;&#039; wird die maximal mögliche Leistung in Watt (ohne Einheit) angegeben, die der Wechselrichter lt. seinem Datenblatt in der Lage ist zu leisten. &lt;br /&gt;
&lt;br /&gt;
Die Angabe &#039;&#039;capacity&#039;&#039; hat in erster Linie die Aufgabe, die max. mögliche PV-Leistung für die Prognose zu begrenzen. Oft sind die installierten Peak-Leistungen der PV-Module höher als die angeschlossene Wechselrichter-Leistung bzw. kommt es vor, dass die KI / (korrigierte) API-Prognose über der maximal möglichen Wechselrichter-Leistung liegt. In diesen Fällen wird die Prognose auf die angegebene &#039;&#039;capacity&#039;&#039; begrenzt.&lt;br /&gt;
&lt;br /&gt;
Der Wert in diesem Schlüssel kann, wie andere Schlüssel auch, dynamisch zur Laufzeit mit dem Set-Befehl &amp;quot;[[#Hinweise zur Struktur der Attribute|set &amp;lt;Name&amp;gt; attrKeyVal ...]]&amp;quot; geändert werden. Ein Beispiel zur Anwendung ergibt sich aus folgendem Szenario.&lt;br /&gt;
&lt;br /&gt;
Gegeben sei eine Anlage die aus Solarzellen (1300 W Peak) mit einem Solar-Ladegerät mit max. 1800 W Leistung, einer Batterie und einem Batterie-Wechselrichter besteht. Die Anlage versorgt nur das Hausnetz. Eine Netzeinspeisung ist nicht vorgesehen oder nicht erlaubt.  &lt;br /&gt;
&lt;br /&gt;
Nehmen wir an, es ist ein Tag voller Sonnenschein prognostiziert und die Batterie ist zunächst entladen. Der Haushalt verbraucht 300 W. Dann würden die PV-Module Module ihre volle Leistung von 1300 W Peak leisten können, die sich auch in der Balken-Vorhersage niederschlägt. Die Leistung wird nicht begrenzt, weil:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* sie niedriger ist als die nominale und angegebene Wechselrichter Kapazität von &#039;&#039;capacity=1800&#039;&#039;&lt;br /&gt;
* die erzeugte Energie anteilig für die Verorgung des Hauses und die Aufladung der Batterie verwendet wird&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Anders verhält es sich wenn nach einer gewissen Zeit die Batterie voll ist und weiterhin volle PV Leistung zur Verfügung steht. In diesem Fall würde der Haushalt seine benötigte Leistung von 300 W abnehmen, aber das Solar-Ladegerät kann keine weitere Leistung mehr abgeben, da die Batterie bereits voll geladen ist, d.h. die Anlagensteuerung würde die PV-Anlage abregeln. In diesem Fall, wenn es einen längeren Zeitraum betrifft, muß auch die Prognose PV-Energie auf 300 Wh (bzw. den durchschnittlichen Energieverbrauch des Hauses pro Stunde) begrenzt werden, damit die PV-Prognose nach unten angepasst wird und so eine realitätsnahere Prognose ermöglicht wird. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;icon=&amp;lt;aktiv&amp;gt;[@&amp;lt;Farbe&amp;gt;][:&amp;lt;inaktiv&amp;gt;[@&amp;lt;Farbe&amp;gt;]]&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Jeder Wechselrichter wird im Standard durch ein Icon getrennt nach Tag und Nacht, d.h. je nach Aktiv-Status und Inaktiv-Status, symbolisiert.&lt;br /&gt;
&lt;br /&gt;
;Tag bzw. Aktivität: Für Wechselrichter mit angeschlossenen PV-Modulen wird ein entsprechend gefärbtes Sonnensymbol im Standard verwendet. Batterie-Wechselrichter verwenden ein gefärbtes Invertersymbol.&lt;br /&gt;
&lt;br /&gt;
;Nacht bzw. Inaktivität: Für Wechselrichter mit angeschlossenen PV-Modulen wird ein entsprechend gefärbtes Symbol der aktuellen Mondphasen im Standard verwendet. Batterie-Wechselrichter verwenden ein ausgegrautes Invertersymbol.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Wie angegeben, kann man für Tag und Nacht jeweils ein Wunsch-Icon, wenn gewünscht mit einer alternativen Färbung, auswählen.&lt;br /&gt;
&lt;br /&gt;
 icon=inverter@darkorange:inverter@gray&lt;br /&gt;
&lt;br /&gt;
Soll nur das Icon für die Nacht bzw. Inaktivät geändert werden, kann dies in dieser Form angegeben werden:&lt;br /&gt;
&lt;br /&gt;
 icon=:inverter@gray  &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
(beachte den führenden &#039;:&#039;)&lt;br /&gt;
&lt;br /&gt;
 &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
==== Definition von Hybrid-Wechselrichtern  ====&lt;br /&gt;
&lt;br /&gt;
Zur Zeit der Erstellung dieses Abschnittes ist es in der SolarForecast Version 1.58.x nicht möglich, einen Hybridwechselrichter nativ zu definieren. Als Workaround wird eine passende Kombination aus PV-Wechselrichter und Batterie-Device oder einer Kombination aus PV-Wechselrichter + Batterie-Wechselrichter und Batterie-Device erstellt.&lt;br /&gt;
&lt;br /&gt;
Bei allen benutzten Methoden ist es sehr wichtig!, dass die geforderten Inhalte der jeweilige Attributschlüssel beachtet und eingehalten werden. So ist zum Beispiel die Angabe von:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;pvOut&#039;&#039;&#039; - Ein Reading, welches die aktuelle Leistung aus PV-Erzeugung, die an das Hausnetz oder öffentliche Netz geliefert wird, bereitstellt. Es wird ein positiver numerischer Wert erwartet.  &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
bei einem PV-Wechselrichter ein Reading welches ausschließlich die Leistung liefert, die von den Solarzellen-Generatoren erzeugt wird. Elemente von Batterieleistungen oder Gridbestandteile dürfen hier nicht enthalten sein. Sind in den Gerätemodulen diese Readings in dieser Form nicht enthalten, bietet es sich an, userReadings zur Erstellung von zusätzlichen Readings in den Quellendevices zu nutzen um den nötigen Input für SolarForecast bereitzustellen. &lt;br /&gt;
&lt;br /&gt;
So wird zum Beispiel bei einem 	Batterie-Wechselrichter gefordert, dass die Readings &#039;&#039;&#039;ac2dc&#039;&#039;&#039; (AC-&amp;gt;DC-Leistung Hausnetz zur Batterie) und &#039;&#039;&#039;dc2ac&#039;&#039;&#039; (DC-&amp;gt;AC-Leistung (Batterie zum Hausnetz) &#039;&#039;&#039;jeweils als als positiver Wert&#039;&#039;&#039; anzugeben ist. Manche Batterie-Devices stellen allerdings nur ein Reading zur Verfügung, welches vorzeichenbehaftet die Leistungen in die Batterie bzw. aus der Batterie heraus liefert. Mit einem userReadings Attribut kann aus diesem Reading zwei neue Readings erzeugt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
attr &amp;lt;Name&amp;gt; userReadings  BatIn:DC_Power_value.* {&lt;br /&gt;
                          my $pwr = ReadingsVal ($name, &#039;DC_Power_value&#039;, 0);&lt;br /&gt;
                          $pwr    = $pwr &amp;gt; 0 ? $pwr : 0;&lt;br /&gt;
                          $pwr&lt;br /&gt;
                          },&lt;br /&gt;
                          BatOut:DC_Power_value.* {&lt;br /&gt;
                          my $pwr = ReadingsVal ($name, &#039;DC_Power_value&#039;, 0);&lt;br /&gt;
                          $pwr    = $pwr &amp;lt; 0 ? - $pwr : 0;&lt;br /&gt;
                          $pwr&lt;br /&gt;
                          },&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ist das Quellenreading &#039;&#039;DC_Power_value&#039;&#039; positiv, stellt der Wert AC-&amp;gt;DC-Leistung dar und wird als neues Reading BatIn bereitgestellt. Mit einem negativen Vorzeichen ist es eine DC-&amp;gt;AC-Leistung und wird in dem neuen Reading BatOut &#039;&#039;&#039;ebenfalls als positiver Wert&#039;&#039;&#039; bereitgestellt. Auch hier gilt der Grundsatz, dass zum Beispiel BatOut nur die von der Batterie gelieferte Energie und keinen Mix aus Batterie- und PV-Energie enthalten darf.&lt;br /&gt;
&lt;br /&gt;
 &lt;br /&gt;
===== Integration eines DEYE SUN-12K-SG04LP3-EU =====&lt;br /&gt;
&lt;br /&gt;
[[Datei:Screenshot 2025-09-23 203955.png|right|thumb|400px|DEYE SUN-12K-SG04LP3-EU Grundlegende Systemarchitektur]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Der DEYE vereint wie wohl alle bzw. die meisten Hybrid-Wechselrichter die Anschlüsse:&lt;br /&gt;
&lt;br /&gt;
* Eingang Solar-Strings&lt;br /&gt;
* Anschluß Batteriespeicher&lt;br /&gt;
* AC-Anschluß öffnetliches Netz&lt;br /&gt;
* AC-Anschluß Ersatzlast (wird bei Netzausfall weiter versorgt)&lt;br /&gt;
* AC-Anschluß Notstromgenerator bzw. netzgekoppelter anderer Wechselrichter (hier ist ein Growatt Micro-Wechselrichter angeschlossen)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Für die Integration werden die setup-Attribute durch das FHEM Device &#039;&#039;Deye_Inverter&#039;&#039; besetzt:&lt;br /&gt;
&lt;br /&gt;
* setupInverterStrings zur Definition der vorhandenen Solarstrings&lt;br /&gt;
* setupInverterDev01 zur Definition der Inverteranschlüsse und Eigenschaften&lt;br /&gt;
* setupBatteryDev01 integriert die Batterie&lt;br /&gt;
* setupMeterDev liefert die Daten für Netzbezug und Netzeinspeisung&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Das nachfolgend verwendete Device &#039;&#039;Deye_Inverter&#039;&#039; ist ein MQTT-Device. Es bekommt Daten vom Deye-Hybridwechselrichter per MQTT und sendet Einstellungen zum Deye, bspw. maximaler Lade/Entladestrom des Akkus, Laden des Akkus aus dem Netz, Ein- und Ausschalten des Micro-Inverter-Ports.&lt;br /&gt;
Das Reading &#039;&#039;Deye_Growatt_power&#039;&#039; im Schlüssel &#039;&#039;pvOut&#039;&#039; ist eine Addition aus den Daten des Deye WR und des angeschlossenen Growatt Micro-Wechselrichters.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
attr &amp;lt;Name&amp;gt; setupInverterStrings Sueddach,Garagendach&lt;br /&gt;
attr &amp;lt;Name&amp;gt; setupInverterDev01 Deye_Inverter pvOut=Deye_Growatt_power:W capacity=15200 etotal=total_pv_production:kWh&lt;br /&gt;
attr &amp;lt;Name&amp;gt; setupBatteryDev01 Deye_Inverter pin=-pout pout=battery_output_power:W intotal=total_charge_of_the_battery:kWh outtotal=total_discharge_of_the_battery:kWh cap=15200 charge=SOC_jkbms&lt;br /&gt;
attr &amp;lt;Name&amp;gt; setupMeterDev Deye_Inverter gcon=total_grid_power:W contotal=total_energy_bought:kWh gfeedin=-gcon feedtotal=total_energy_sold:kWh&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;setupInverterStrings&#039;&#039; benennt alle vorhandenen Solarstrings. Werden mehrere Inverter definiert, kann mit dem hier nicht verwendeten Schlüssel &#039;&#039;strings&#039;&#039; eine Zuordnung der Strings zum angeschlossenen Inverter vorgenommen werden.&lt;br /&gt;
&lt;br /&gt;
Im Attribut &#039;&#039;setupInverterDev01&#039;&#039; ist der allgemeine Schlüssel &#039;&#039;capacity&#039;&#039; zur Festlegung der Inverterleistung von 15200 Watt gesetzt. Die spezifischen Schlüssel &#039;&#039;pvOut&#039;&#039; und &#039;&#039;etotal&#039;&#039; teilen dem Modul die aktuell erzeugte PV-Leistung sowie die gesamte erzeugte PV-Energie mit. Letzteres dient unter anderem dazu die stündlich real erzeugte PV-Energie festzuhalten und mit der Prognose zu vergleichen. Die in den schlüsseln hinterlegten dürfen keine Battrie- oder Netzanteile enthalten.&lt;br /&gt;
&lt;br /&gt;
Das Attr &#039;&#039;setupBatteryDev01&#039;&#039; implementiert alle relevanten Batteriewerte. Das Reading &#039;&#039;battery_output_power&#039;&#039; im Schlüssel &#039;&#039;pout&#039;&#039; liefert den Batterie-Output als positiven Wert. Als Besonderheit kann der Wert &#039;&#039;pin&#039;&#039;, die Batterie-Ladeleistung, den Wert des Readings in &#039;&#039;pout&#039;&#039; übernehmen wenn dieser Wert ein negatives Vorzeichen hat. Die Schlüssel &#039;&#039;intotal&#039;&#039; und &#039;&#039;outtotal&#039;&#039; liefern die summierten Totalwerte für Batterie Ladung bzw. Entladung. Die Kapazität &#039;&#039;cap&#039;&#039; wird für verschiedene Aspekte der SoC- und Ladesteuerung verwendet. &lt;br /&gt;
&lt;br /&gt;
Abschließend liefern die angebenen Readings in den Schlüsseln des Attributes &#039;&#039;setupMeterDev&#039;&#039; alle notwendigen Werte des Netzbezugs und der Netzeinspeisung.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Nachfolgende Links liefern &#039;&#039;&#039;weitere Infos&#039;&#039;&#039; wie man Daten aus dem Deye WR ausliest und Einstellungen des Deye WR verändert:&lt;br /&gt;
&lt;br /&gt;
* https://github.com/klatremis/esphome-for-deye&lt;br /&gt;
* https://github.com/philipphenkel/esphome-config&lt;br /&gt;
* https://github.com/bagges/deye-esp32-bridge&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Für eine weiterführende Automation soll der Akku netzdienlich in der Mittagszeit geladen werden. Dazu wurde durch den Anwender folgendes userReadings &#039;&#039;MaxBattCharge_Request&#039;&#039; im Device &#039;&#039;Deye_Inverter&#039;&#039; für den maximalen Ladestrom definiert (bei 90% SOC und bei 100% SOC):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
MaxBattCharge_Request:SOC_jkbms.* {&lt;br /&gt;
                                    if ((ReadingsNum(&#039;mySolarForecast&#039;,&#039;RestOfDayPVforecast&#039;,&#039;&#039;)&lt;br /&gt;
                                         - ReadingsNum(&#039;mySolarForecast&#039;,&#039;special_todayConForecastTillSunset&#039;,&#039;&#039;)&lt;br /&gt;
                                         - (90-ReadingsNum(&#039;JK_BMS&#039;,&#039;capacity_remaining&#039;,&#039;&#039;))/100*310*51.2) &amp;lt; 400&lt;br /&gt;
                                         and ReadingsAge(&#039;JK_BMS&#039;,&#039;capacity_full_timestamp&#039;,&#039;&#039;) &amp;lt; 561600)&lt;br /&gt;
                                    {100}&lt;br /&gt;
                                    elsif ((ReadingsNum(&#039;mySolarForecast&#039;,&#039;RestOfDayPVforecast&#039;,&#039;&#039;)&lt;br /&gt;
                                            - ReadingsNum(&#039;mySolarForecast&#039;,&#039;special_todayConForecastTillSunset&#039;,&#039;&#039;)&lt;br /&gt;
                                            - (100-ReadingsNum(&#039;JK_BMS&#039;,&#039;capacity_remaining&#039;,&#039;&#039;))/100*310*51.2) &amp;lt; 400&lt;br /&gt;
                                            and ReadingsAge(&#039;JK_BMS&#039;,&#039;capacity_full_timestamp&#039;,&#039;&#039;) &amp;gt;= 561600)&lt;br /&gt;
                                    {100}&lt;br /&gt;
                                    elsif ((ReadingsNum(&#039;mySolarForecast&#039;,&#039;RestOfDayPVforecast&#039;,&#039;&#039;)&lt;br /&gt;
                                            - ReadingsNum(&#039;mySolarForecast&#039;,&#039;special_todayConForecastTillSunset&#039;,&#039;&#039;)&lt;br /&gt;
                                            - (90-ReadingsNum(&#039;JK_BMS&#039;,&#039;capacity_remaining&#039;,&#039;&#039;))/100*310*51.2) &amp;gt;= 400&lt;br /&gt;
                                            and ReadingsAge(&#039;JK_BMS&#039;,&#039;capacity_full_timestamp&#039;,&#039;&#039;) &amp;lt; 561600)&lt;br /&gt;
                                    {ceil ((90-ReadingsNum(&#039;JK_BMS&#039;,&#039;capacity_remaining&#039;,&#039;&#039;))/100*310/3.5)}&lt;br /&gt;
                                    elsif ((ReadingsNum(&#039;mySolarForecast&#039;,&#039;RestOfDayPVforecast&#039;,&#039;&#039;)&lt;br /&gt;
                                            - ReadingsNum(&#039;mySolarForecast&#039;,&#039;special_todayConForecastTillSunset&#039;,&#039;&#039;)&lt;br /&gt;
                                            - (100-ReadingsNum(&#039;JK_BMS&#039;,&#039;capacity_remaining&#039;,&#039;&#039;))/100*310*51.2) &amp;gt;= 400&lt;br /&gt;
                                            and ReadingsAge(&#039;JK_BMS&#039;,&#039;capacity_full_timestamp&#039;,&#039;&#039;) &amp;gt;= 561600)&lt;br /&gt;
                                    {ceil ((100-ReadingsNum(&#039;JK_BMS&#039;,&#039;capacity_remaining&#039;,&#039;&#039;))/100*310/3.0)}&lt;br /&gt;
                                  }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Das userReading wird in einer Automation benutzt, um den maximalen Ladestrom einzustellen; Ladebeginn bei ausreichend Sonne ist ab 11:30 eingestellt.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
===== Integration eines Fronius Symo GEN24 10.0 Plus mit (virtuellen) Batterie-Wechselrichter =====&lt;br /&gt;
&lt;br /&gt;
[[Datei:Screenshot 2025-10-04 162422.png|right|thumb|400px|Flußgrafik mit eingebauten Hybrid-Wechselrichter]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Die Integration des Wechselrichters Fronius Symo GEN24 10.0 Plus benötigt zunächst eine Anbindung in FHEM, was mit dem Modul 98_fronius.pm vorgenommen wurde.&lt;br /&gt;
&lt;br /&gt;
Das Ziel der Kurzdokumentation ist neben der Darstellung eines Hybridwechselrichters aufzuzeigen, wie man den Wandlungsverlust eines PV-Wechselrichters (pvIn – pvOut) in Näherung visualisieren kann.  &lt;br /&gt;
&#039;&#039;&#039;pvIn&#039;&#039;&#039; ist dabei die Summe der Produkte aus den DC Strom- und Spannungswerten der einzelnen Strings (hier 2109 W), &#039;&#039;&#039;pvOut&#039;&#039;&#039; (hier 2065 W) ist die an den Inverterknoten (hier 1105 W) plus die an die Batterie abgegebene Leistung (hier 960 W). Bei dieser Implementierung wird mit einem zusätzlichen (virtuellen) Batterie-Wechselrichter gearbeitet. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
In Vorbereitung werden die beiden anzugebenden Readings &#039;&#039;&amp;lt;pvIn&amp;gt;&#039;&#039; und &#039;&#039;&amp;lt;pvOut&amp;gt;&#039;&#039; zum Beispiel als userReadings erstellt, wobei &#039;&amp;lt;pvIn&amp;gt;&#039;&#039; und &#039;&#039;&amp;lt;pvOut&amp;gt;&#039;&#039; natürlich durch Readingnamen ersetzt werden müssen. Diese beiden Readings werden wie folgt berechnet / erstellt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;pvOut&amp;gt; = Inverter_Common_PAC_Value - PowerFlow_Site_P_Akku&lt;br /&gt;
&amp;lt;pvIn&amp;gt; = Inverter_Common_IDC_Value * Inverter_Common_UDC_Value + Inverter_Common_IDC_2_Value * Inverter_Common_UDC_2_Value&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der &#039;&#039;&#039;PV-Wechselrichter&#039;&#039;&#039; wird mit dem Attribut setupInverterDev01 definiert, wobei die angelegten Readingnamen in den Schlüsseln &#039;&#039;pvIn&#039;&#039; bzw. &#039;&#039;pvOut&#039;&#039; verwendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
SymGen24 icon=inverter@#ff8c00:inverter@grey capacity=10000 strings=suedwest,nordost etotal=User_Produced_PV:kWh pvOut=&amp;lt;pvOut&amp;gt;:W pvIn=&amp;lt;pvIn&amp;gt;:W&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dem PV-Wechselrichter sind die Strings &#039;&#039;suedwest&#039;&#039; und &#039;&#039;nordost&#039;&#039; zugewiesen.&lt;br /&gt;
&lt;br /&gt;
Der zusätzliche &#039;&#039;&#039;Batterie-Wechselrichter&#039;&#039;&#039; wird mit dem Attribut setupInverterDev02 hinzugefügt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
SymGen24 icon=inverter@#ff8c00:inverter@grey strings=none ac2dc=-PowerFlow_Site_P_Akku:W  dc2ac=PowerFlow_Site_P_Akku:W capacity=7680&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Bemerkungen:&#039;&#039;&#039; &amp;lt;br&amp;gt;&lt;br /&gt;
- strings=none sowie die Schlüssel ac2cd und dc2ac sind typisch für einen Batterie-WR bzw. kennzeichnen einen WR als Batterie-Wechselrichter &amp;lt;br&amp;gt;&lt;br /&gt;
- die an die Batterie abgegebene Leistung ist negativ&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===== Einbindung eines Fronius Symo GEN24 10.0 Plus mit BYD Batterie ohne (virtuellen) Batterie-Wechselrichter =====&lt;br /&gt;
Diee Implementierung ist komplett auf der Seite [[Solaranlage Komplettbeispiel Fronius BYD]] beschrieben.&lt;br /&gt;
&lt;br /&gt;
Zur Integration des &#039;&#039;Fronius Symo&#039;&#039; in FHEM wird das Modul &#039;&#039;fronius.pm&#039;&#039; verwendet und in dem definierten Device &#039;&#039;Fronius_Symo_Gen24&#039;&#039; diverse userReadings erzeugt. Diese Readings werden in den korrespondierenden Schlüsseln des Attributes &#039;&#039;setupInverterDev01&#039;&#039; angegeben:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
setupInverterDev01 Fronius_Symo_Gen24 etotal=User_Produced_PV_Energie:kWh pvOut=PowerFlow_Site_P_PV:W capacity=6000 strings=Dach_Ost,Dach_West&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Am Wechselrichter Fronius Symo Gen24 ist eine BYD Batterie angeschlossen. Das Device &#039;&#039;PV-Batterie&#039;&#039; wird per [[ModbusAttr]] angelegt und gemanaged. Die entsprechenden Readings werden im Attribut &#039;&#039;SetupBatteryDev01&#039;&#039; hinterlegt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
PV_Batterie&lt;br /&gt;
cap=10240&lt;br /&gt;
charge=BatteryChargePercent&lt;br /&gt;
icon=@dyn:@dyn:@dyn:@dyn&lt;br /&gt;
intotal=Summe_Ladung:Wh&lt;br /&gt;
outtotal=Summe_Entladung:Wh&lt;br /&gt;
pin=BatteryChargeWatt:W&lt;br /&gt;
pout=BatteryDischargeWatt:W&lt;br /&gt;
pinmax=10000&lt;br /&gt;
show=2&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===== Einbindung eines SMA - Hybrid Sunny Tripower Smart Energy  =====&lt;br /&gt;
[[Datei:Screenshot 2025-10-05 083530.png|right|thumb|400px|SMAInverter.pm]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Der genannte Wechselrichter wird über das Modul SMAInverter.pm in FHEM integriert. Die nebenstehende Abbildung zeigt und benennt die einzelnen Readings des Moduls welche in den entsprechenden Schlüsseln der SolarForecast Attribute &#039;&#039;setupInverterDevXX&#039;&#039; und &#039;&#039;setupBatteryDevXX&#039;&#039; zugeordnet werden. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Die Readings des SMAInverter Moduls können vor der Verwendung über geeignete Methoden (userReadings) in Readings mit sprechenden Namen überführt werden um sie mit diesen Namen in die genannten Attributschlüssel einzufügen. In der vorliegenden Lösung befinden sich die  Anwendungen SolarForecast und und die Inverter-Steuerung jeweils auf unterschiedlicher Hardware. Als Verbindungslayer wird MQTT genutzt, wobei dadurch bereits die dargestellten Readingnamen entstehen. &lt;br /&gt;
 &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Der integrierte SolarForecast Grafikbereich ==&lt;br /&gt;
Das SolarForecast Modul beinhaltet eine Grafikdarstellung die sich in mehrere Bereiche gliedert. Diese Bereiche können über Attribute ein- bzw. ausgeblendet sowie die Inhalte festgelegt werden.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Screenshot 2024-09-21 223545.png|right|thumb|400px|Bereiche der integrierten Grafik]]&lt;br /&gt;
&lt;br /&gt;
Der nebenstehende Screenshot gibt die Gliederung der Grafikbereiche wieder:&lt;br /&gt;
&lt;br /&gt;
 1 - der Kopfbereich (Graphic Header)&lt;br /&gt;
 2 - durch den Nutzer selbst definierbarer Inhalt (Graphic Header Own Specification) als Teil des Kopfbereiches&lt;br /&gt;
 3 - Bereich Verbraucherdarstellung (Consumer Legend)&lt;br /&gt;
 4 - Bereich Balkengrafik (Beam Graphic)&lt;br /&gt;
 5 - Energieflußgrafik (Flow Graphic)&lt;br /&gt;
&lt;br /&gt;
Jeder dieser Bereiche hat spezifische Steuerungsattribute zur Anpassung des Verhaltens und ggf. des Inhalts.&lt;br /&gt;
&lt;br /&gt;
=== 1 - Der Grafik Kopfbereich ===&lt;br /&gt;
Im Kopfbereich befinden sich fest verankerte Informationen zur verwendeten Vorhersage-API sowie deren Status, Informationen zum Sonnenaufgang und Sonnenuntergang, dem aktuellen Gesamtstatus des SolarForecast Devices, Abweichungskennzahlen der PV Vorhersage sowie weitere Kennzeichen zur Verfügung.&lt;br /&gt;
Über zwei Drucktasten kann eine Anlagenprüfung durchgeführt und in den Spportthread des Moduls im FHEM Forum abgesprungen werden.&lt;br /&gt;
&lt;br /&gt;
Mit dem Attribut &#039;&#039;&#039;graphicHeaderShow&#039;&#039;&#039; kann der gesamte Kopfbereich 1 und 2 (nutzereigener Teilbereich) ausgeblendet werden. Wird der Kopbereich ausgeblendet, ist es hilfreich mit dem Attribut &#039;&#039;&#039;plantControl-&amp;gt;showLink&#039;&#039;&#039; einen Link zur Detailgrafik einzufügen da der in der Kopfgrafik integrierte Link in diesem Fall nicht mehr zur Verfügung steht. &lt;br /&gt;
&lt;br /&gt;
Das Attribut &#039;&#039;&#039;graphicHeaderDetail&#039;&#039;&#039; definiert welche Bereiche des Grafikkopfes angezeigt werden sollen. Es kann die Anzeige des Energieverbrauches, der PV-Erzeugung und der vom Nutzer selbst definierte Inhalt miteinander frei kombiniert werden.&lt;br /&gt;
&lt;br /&gt;
=== 2 - Der Bereich mit selbst definierbarem Inhalt (Graphic Header Own Specification) ===&lt;br /&gt;
In diesem Bereich kann der User beliebige Readings, Set-Kommandos und Attribute des SolarForecast Devices oder anderer Devices, die sich im gleichen FHEM-System wie das SolarForecast Device befinden, anzeigen lassen.&lt;br /&gt;
&lt;br /&gt;
Dieser Bereich ist als Tabelle mit einer beliebigen Anzahl von Zeilen organisiert. Jede Zeile beinhaltet ein Kommentarfeld und vier Nutzfelder zur Darstellung ausgewähler Werte + Label. Der Bereich bzw. der Inhalt des Bereiches wird mit dem Attribut &#039;&#039;&#039;graphicHeaderOwnspec&#039;&#039;&#039; definiert.  &lt;br /&gt;
&lt;br /&gt;
Das nachfolgende Beispiel zeigt ein real gesetztes graphicHeaderOwnspec Attribut. Die Leerzeilen sind nicht notwendig, erhöhen aber die Lesbarkeit der Struktur.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#&lt;br /&gt;
Autarkierate:Current_AutarkyRate&lt;br /&gt;
PV&amp;amp;nbsp;übermorgen:special_dayAfterTomorrowPVforecast&lt;br /&gt;
Verbrauch&amp;amp;nbsp;bis&amp;lt;br&amp;gt;Sunset:special_todayConForecastTillSunset&lt;br /&gt;
Verbrauch&amp;amp;nbsp;bis&amp;lt;br&amp;gt;nächsten&amp;amp;nbsp;Sunrise:special_conForecastTillNextSunrise&lt;br /&gt;
&lt;br /&gt;
#Batterie&lt;br /&gt;
BatteryLive&amp;amp;nbsp;Status:userFn_BatLive_State&lt;br /&gt;
SoC&amp;amp;nbsp;Limit:Settings_CGwacs_BatteryLife_MinimumSocLimit_value@MQTT2_cerboGX_c0619ab34e08_settings&lt;br /&gt;
SoC&amp;amp;nbsp;Ist:SOC_value@MQTT2_cerboGX_c0619ab34e08_battery&lt;br /&gt;
SOC&amp;amp;nbsp;Optimum:Battery_OptimumTargetSoC&lt;br /&gt;
&lt;br /&gt;
#&lt;br /&gt;
aktuelle&amp;amp;nbsp;Ladung:userFn_Bat_EnergyState&lt;br /&gt;
Verfügbar:userFn_BatLive_UsableEnergy&lt;br /&gt;
benötigte&amp;amp;nbsp;Ladeenergie:userFn_Bat_EnergyRemain&lt;br /&gt;
Ladefreigabe:Battery_ChargeRecommended&lt;br /&gt;
&lt;br /&gt;
#&lt;br /&gt;
akt.&amp;amp;nbsp;DC&amp;amp;nbsp;Strom:DC_Current_value@MQTT2_cerboGX_c0619ab34e08_battery&lt;br /&gt;
Restladezeit&amp;amp;nbsp;Ziel:userFn_Bat_HoursUntilChargeFinish&lt;br /&gt;
MPII&amp;amp;nbsp;Ladestrom&amp;lt;br&amp;gt;Limit&amp;amp;nbsp;Soll:userFn_Bat_MaxChargeCurrent_set&lt;br /&gt;
:&lt;br /&gt;
&lt;br /&gt;
#Settings&lt;br /&gt;
Batterie&amp;amp;nbsp;Lademanagement:userFn_BatteryChargeManagement&lt;br /&gt;
SoC&amp;amp;nbsp;Management:userFn_BatterySoCManagement&lt;br /&gt;
MPII&amp;amp;nbsp;Ladestrom&amp;lt;br&amp;gt;Limit&amp;amp;nbsp;Ist:MaxChargeCurrent@MQTT2_cerboGX_c0619ab34e08_vebus&lt;br /&gt;
SmartSolar&amp;lt;br&amp;gt;Einspeisung:OvervoltageFeedIn@MQTT2_cerboGX_c0619ab34e08_settings&lt;br /&gt;
&lt;br /&gt;
MPII&amp;amp;nbsp;Limit&amp;lt;br&amp;gt;Einspeisung&amp;amp;nbsp;(W):MaxFeedInPower@MQTT2_cerboGX_c0619ab34e08_settings&lt;br /&gt;
SOC&amp;amp;nbsp;Limit&amp;amp;nbsp;Soll:MinimumSocLimit@MQTT2_cerboGX_c0619ab34e08_settings&lt;br /&gt;
Verbr.&amp;amp;nbsp;Neuplanung:consumerNewPlanning&lt;br /&gt;
Wetteranzeige:graphicShowWeather &lt;br /&gt;
&lt;br /&gt;
Anzeige&amp;amp;nbsp;Nachtstunden:graphicShowNight&lt;br /&gt;
hist.&amp;amp;nbsp;Stunden:graphicHistoryHour&lt;br /&gt;
Autokorrektur:pvCorrectionFactor_Auto&lt;br /&gt;
Victron&amp;amp;nbsp;BatteryLife&amp;lt;br&amp;gt;(1=an,10=aus):BatteryLife@MQTT2_cerboGX_c0619ab34e08_settings&lt;br /&gt;
&lt;br /&gt;
Grid&amp;amp;nbsp;Setpoint:GridSetpoint@MQTT2_cerboGX_c0619ab34e08_settings&lt;br /&gt;
Debug:ctrlDebu&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das Kommentarfeld am Beginn einer Zeile wird durch ein &#039;&#039;&#039;#&#039;&#039;&#039; gekennzeichnet. Folgt dem # ein String, wird dieser String als Kommentarangabe gewertet und in der Grafik ausgegeben. Ein leeres Kommentarfeld wird lediglich durch ein # gefolgt von einem Leerzeichen oder NL beschrieben:&lt;br /&gt;
&lt;br /&gt;
 #:&amp;lt;Text&amp;gt;  (bzw nur # für einen &#039;leeren&#039; Titel)&lt;br /&gt;
&lt;br /&gt;
Ein Netzfeld bzw. ein Datensatz wird durch die Angabe von einem Label und dem darzustellenden Wert getrennt durch &#039;:&#039; definiert:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;Label&amp;gt;:&amp;lt;Reading, Attribut oder Set-Kommando&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das System erkennt selbständig, ob es sich bei der Angabe um ein Reading, Attribut oder ein Set-Kommando handelt. Sollen Readings, Set-Kommandos und Attribute anderer Devices als dem SolarForecast-Device angezeigt werden, kann dies einfach durch Zusatz von&lt;br /&gt;
&lt;br /&gt;
 ....@&amp;lt;Device-Name&amp;gt;&lt;br /&gt;
&lt;br /&gt;
an den Parameter erreicht werden.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Screenshot 2024-12-05 203916.png|right|thumb|400px|Darstellung eigener Grafikbereich]]&lt;br /&gt;
Die Eingabe kann mehrzeilig erfolgen. Werte mit den Einheiten &amp;quot;Wh&amp;quot; bzw. &amp;quot;kWh&amp;quot; werden entsprechend der Einstellung des Attributs &#039;&#039;&#039;graphicEnergyUnit&#039;&#039;&#039; umgerechnet. Im Label sind Leerzeichen durch &amp;quot;&amp;amp;amp;nbsp;&amp;quot; einzufügen, ein Zeilenumbruch durch &amp;quot;&amp;amp;lt;br&amp;amp;gt;&amp;quot;. Ein leeres Feld in einer Zeile wird durch &amp;quot;:&amp;quot; ohne eine weitere Angabe erzeugt.&lt;br /&gt;
&lt;br /&gt;
Die nebenstehende Abbildung zeigt die reale Darstellung der oben angegebenen Struktur im Attribut graphicHeaderOwnspec.&lt;br /&gt;
&lt;br /&gt;
==== Formatierung der Inhalte im Bereich &amp;quot;Graphic Header Own Specification&amp;quot; ====&lt;br /&gt;
&lt;br /&gt;
Die angezeigten Inhalte, wie z.B. Readingwerte, des User eigenen Bereiches (Attribut graphicHeaderOwnspec) können durch Spezifikationen im Attribut &#039;&#039;&#039;graphicHeaderOwnspecValForm&#039;&#039;&#039; manipuliert werden.&lt;br /&gt;
&lt;br /&gt;
Die Online-Hilfe zum Attribut graphicHeaderOwnspecValForm erläutert die verfügbaren Möglichkeiten und Optionen.&lt;br /&gt;
Als Beispiel hier einige mögliche Notationen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
  &#039;userFn_Bat_HoursUntilChargeFinish&#039;                        =&amp;gt; &amp;quot;($VALUE).&#039; Stunden&#039;&amp;quot;,&lt;br /&gt;
  &#039;MQTT2_cerboGX_c0619ab34e08_battery.SOC_value&#039;             =&amp;gt; &amp;quot;%.1f %%&amp;quot;,&lt;br /&gt;
  &#039;Settings_CGwacs_BatteryLife_MinimumSocLimit_value&#039;        =&amp;gt; &amp;quot;%.1f %%&amp;quot;,&lt;br /&gt;
  &#039;Info_MaxChargeCurrent_value&#039;                              =&amp;gt; &amp;quot;($VALUE).&#039; A&#039;&amp;quot;,&lt;br /&gt;
  &#039;DC_Current_value&#039;                                         =&amp;gt; &amp;quot;%.0f A&amp;quot;,&lt;br /&gt;
  &#039;MQTT2_cerboGX_c0619ab34e08_solarcharger_Common.Regulated&#039; =&amp;gt; &amp;quot;($VALUE eq &#039;1&#039; ? &#039;Ja&#039; : &#039;Nein&#039;)&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Eine Besonderheit besteht bei darzustellenden Werten die die Einheiten &#039;Wh&#039; bzw. &#039;kWh&#039; besitzen. Diese Werte und Einheiten werden automatisch entsprechend des gesetzten Attributes graphicEnergyUnit umgerechnet und angezeigt.&lt;br /&gt;
&lt;br /&gt;
Soll in diesen Fällen eine automatische Formatierung umgangen werden, kann eine solche Notation verwendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    &#039;Today_PVforecast&#039; =&amp;gt; &amp;quot;(sprintf &#039;%.1f &amp;amp;amp;nbsp;kWh&#039;, ($VALUE / 1000))&amp;quot;,&lt;br /&gt;
} &lt;br /&gt;
&amp;lt;/pre&amp;gt;  &lt;br /&gt;
&lt;br /&gt;
Die Angabe von &amp;amp;amp;nbsp; vor &#039;kWh&#039; verhindert die Erkennung der Einheit und so auch die automatische Umsetzung durch das Modul. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== 3 - Bereich Verbraucherdarstellung (Consumer Legend) ===&lt;br /&gt;
Dieser Grafikbereich zeigt die im Modul registrierten Verbraucher sowie deren Status und weitere Informationen an. Sind die Voraussetzungen gegeben, d.h. entsprechende Schlüssel in der ConsumerXX-Attributen gesetzt, können die Verbraucher über das Paneel manuell geschaltet werden.&lt;br /&gt;
&lt;br /&gt;
Zur Anpassung der Darstellung sind verschiedene Attribute verfügbar:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
;consumerControl-&amp;gt;showLegend: Definiert die Lage bzw. Darstellungsweise der Verbraucherlegende sofern Verbraucher im SolarForecast Device registriert sind.&lt;br /&gt;
;consumerControl-&amp;gt;adviceIcon: Definiert die Art der Information über die geplanten Schaltzeiten eines Verbrauchers in der Verbraucherlegende. &lt;br /&gt;
;consumerControl-&amp;gt;detailLink: Wenn gesetzt, ist der jeweilige Verbraucher klickbar um direkt zur Detailansicht des jeweiligen Geräts auf einer neuen Browserseite zu gelangen. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Im Standard wird das Verbraucherpaneel zwischen dem Kopfbereich und der ersten Balkengrafik Ebene dargestellt. Mit dem Attribut &#039;&#039;&#039;consumerControl-&amp;gt;showLegend&#039;&#039;&#039; und Argument &#039;&#039;&#039;icon_bottom&#039;&#039;&#039; oder &#039;&#039;&#039;text_bottom&#039;&#039;&#039; kann das Verbraucherpaneel an das Ende des Grafikbereiches verschoben werden.&lt;br /&gt;
Die Verwendung von &#039;&#039;&#039;text_bottom&#039;&#039;&#039; oder &#039;&#039;&#039;text_top&#039;&#039;&#039; blendet die Icon-Spalte im Verbraucherpaneel aus.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;gallery&amp;gt;&lt;br /&gt;
File:Screenshot 2025-02-08 093239.png|Darstellung mit consumerControl showLegend=icon_top (default)&lt;br /&gt;
File:Screenshot 2025-02-08 093646.png|Darstellung mit consumerControl showLegend=icon_bottom&lt;br /&gt;
File:Screenshot 2025-02-08 152018.png|Darstellung mit consumerControl showLegend=text_top bzw. text_bottom&lt;br /&gt;
&amp;lt;/gallery&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Datei:Screenshot 2025-02-08 213634.png|right|thumb|400px|]]&lt;br /&gt;
Ein Mouse-Over über das Uhrensymbol zeigt nähere Informationen zum Status, den Planungsdaten, Modus und weitere Daten des Verbrauchers. Der Schalter &amp;quot;Aus/Ein&amp;quot; visualisiert den Schaltzustand des Verbrauchers und können auch zum manuellen Schalten des Verbrauchers dienen. Voraussetzung ist, dass im consumer-Attribut die Schlüssel &amp;quot;on&amp;quot; und &amp;quot;off&amp;quot; mit den Set-Kommandos zum Schalten des Verbrauchers defininiert sind.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Screenshot 2025-02-08 222210.png|right|thumb|300px|]]&lt;br /&gt;
Der Auto-Schieberegler zeigt einerseits den Freigabestatus zum Schalten des Verbrauchers durch das SolarForecast Modul und kann andererseits manuell betätigt werden. Der Status des Reglers korrespondiert mit dem im zugehörigen Consumer Attribut vorhandenen Schlüssel &#039;&#039;&#039;auto&#039;&#039;&#039; angegebenen Reading-Wert.&lt;br /&gt;
&lt;br /&gt;
Die Verbraucherbezeichnungen in dem Paneel können in der Standardkonfiguration angeklickt werden um direkt zur Detailansicht des Devices zu gelangen. Mit dem Attribut &#039;&#039;&#039;consumerControl-&amp;gt;detailLink&#039;&#039;&#039; kann der Link zur Detailansicht deaktiviert werden.&lt;br /&gt;
&lt;br /&gt;
Das Uhrensymbol kann mit dem Attribut &#039;&#039;&#039;consumerControl-&amp;gt;adviceIcon&#039;&#039;&#039; geändert und/oder dessen Farbe angepasst, sowie einen Informationstext statt eines Icons angezeigt werden. &lt;br /&gt;
&lt;br /&gt;
=== 4 - Bereich Balkengrafik (Beam Graphic) ===&lt;br /&gt;
&lt;br /&gt;
Die Balkengrafik des Moduls ist ein wesentliches Visualisierungshilfsmittel. Im Auslieferungszustand existiert eine Balkengrafikebene, die &#039;&#039;&#039;Ebene 1&#039;&#039;&#039;.&lt;br /&gt;
Jede Ebene kann zwei Balkendiagramme mit auswählbaren Inhalten, Farben und einstellbarem LayoutTyp darstellen. In Ebene 1 kann die Wettervorhersage eingebettet oder, bei Vorhandensein von Batteriegeräten, die zukünftigen Zeiten mit empfohlener Ladefreigabe und SoC-Prognose eingeblendet werden. &lt;br /&gt;
&lt;br /&gt;
Welche Daten in welchen Zeiträumen dargestellt werden sollen, die Bestimmung des Layouts, die Gestaltung der Balken und einiges mehr wird durch Attribute gesteuert. Ein Teil dieser Einstellungen wird über das zusammengesetzete Attribut &#039;&#039;&#039;graphicControl-&amp;gt;&#039;Schlüssel&#039;=&#039;Wert&#039;&#039;&#039;&#039; eingestellt:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
::*&#039;&#039;&#039;grundsätzliche Layout Definitionen&#039;&#039;&#039;&lt;br /&gt;
[[Datei:Screenshot 2025-01-12 204342.png|right|thumb|400px|Standardanzeige mit Wetter-Icons, gesetzen Attributen graphicBeam1Color=D4C177, graphicHistoryHour=11]]&lt;br /&gt;
&lt;br /&gt;
;graphicControl-&amp;gt;beamPaddingTop:Legt den Platz in px im Balkendiagramm fest, der zwischen dem oberen Rand der jeweiligen Balkengrafik Ebene und der ersten Text- oder Iconreihe dieser Ebene eingefügt wird.&lt;br /&gt;
;graphicControl-&amp;gt;beamPaddingBottom:Legt den Platz in px im Balkendiagramm fest, der zwischen der letzten Text- oder Iconreihe der jeweiligen Balkengrafik Ebene und dem unteren Rand dieser Ebene eingefügt wird. &lt;br /&gt;
;graphicControl-&amp;gt;layoutType:Layout-Optionen zur grundlegenden Gestaltung der Balkengrafik&lt;br /&gt;
;graphicControl-&amp;gt;hourStyle:Format der Zeitangabe in der Balkengrafik&lt;br /&gt;
;graphicControl-&amp;gt;hourCount:Anzahl der darzustellenden Balken/Stunden in der Balkengrafk&lt;br /&gt;
[[Datei:Screenshot Screenshot 2025-05-21 153613.png|right|thumb|400px|Erweiterung des oberen und unteren Abstandes mit den Attributen graphicControl-&amp;gt;beamPaddingTop und graphicControl-&amp;gt;beamPaddingBottom]]&lt;br /&gt;
;graphicControl-&amp;gt;energyUnit:definiert die Einheit zur Anzeige der elektrischen Leistung in der Grafik&lt;br /&gt;
;graphicHistoryHour:Anzahl der vorangegangenen Stunden die in der Balkengrafik dargestellt werden&lt;br /&gt;
;graphicControl-&amp;gt;beamWidth:Vorgabe Breite der Balken der Balkengrafik in px (sonst automatische Bestimmung) &lt;br /&gt;
;graphicBeamHeightLevel1:Multiplikator zur Festlegung der maximalen Balkenhöhe der jeweiligen Ebene (hier Ebene 1)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
::*&#039;&#039;&#039;Definition der Inhalte und Balkeneigenschaften&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[Datei:Screenshot 2025-01-12 204756.png|right|thumb|400px|Wie oben aber mit gesetzem Attribut graphicShowNight=1]]&lt;br /&gt;
;graphicBeam1Color:Farbauswahl des primären Balkens (Ebene 1)&lt;br /&gt;
;graphicBeam2Color:Farbauswahl des sekundären Balkens (Ebene 1)&lt;br /&gt;
;graphicBeam1Content:Legt den darzustellenden Inhalt des primären Balken fest (Ebene 1)&lt;br /&gt;
;graphicBeam2Content:Legt den darzustellenden Inhalt des sekundären Balken fest (Ebene 1)&lt;br /&gt;
;graphicBeam1FontColor:Auswahl der Schriftfarbe des primären Balkens (Ebene 1)&lt;br /&gt;
;graphicBeam2FontColor:Auswahl der Schriftfarbe des sekundären Balkens (Ebene 1)&lt;br /&gt;
[[Datei:Screenshot 2025-01-12 205024.png|right|thumb|400px|Balkengrafik mit aktivierter Ebene 2, gesetzem Attribut graphicShowNight=0 (ohne Zeitsynchronisation zwischen Ebene 1 und Ebene 2)]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
::*&#039;&#039;&#039;zusätzliche Optionen&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
;graphicShowNight:Anzeigen oder Verbergen der Nachtstunden in der Balkengrafik&lt;br /&gt;
;graphicShowWeather:Wettericons in der Balkengrafik anzeigen/verbergen (nur Ebene 1)&lt;br /&gt;
;graphicWeatherColor:Farbe der Wetter-Icons für die Tagesstunden&lt;br /&gt;
;graphicWeatherColorNight:Farbe der Wetter-Icons für die Nachtstunden&lt;br /&gt;
;graphicShowDiff:Zusätzliche Anzeige der Differenz &amp;quot;&amp;lt;primärer Balkeninhalt&amp;gt; - &amp;lt;sekundärer Balkeninhalt&amp;gt;&amp;quot; im Kopf- oder Fußbereich der Balkengrafik&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Die genaue Beschreibung und deren Optionen sind der aktuellen Online-Hilfe zu entnehmen.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Für alle Ebenen gilt:&#039;&#039;&#039; Ein Attribut mit einer ungeraden Balkennummer (z.B. graphicBeam1Content) bezieht sich immer auf den primären Balken, ein Attribut mit einer geraden Balkennummer (z.B. graphicBeam2Content) bezieht sich immer auf den sekundären Balken.&lt;br /&gt;
&lt;br /&gt;
Die Balken haben, wie die meisten Elemente in der Balkengrafik, eine Mouse-Over Eigenschaft welche relevante Informationen zu den dargestellten Werten anzeigt. &lt;br /&gt;
&lt;br /&gt;
Sind die Nachtunden mit &#039;&#039;&#039;graphicShowNight&#039;&#039;&#039; ausgeblendet, werden keine Balken angezeigt sofern es keine Werte anzuzeigen gibt. Sollten dennoch Werte ungleich 0 anzuzeigen sein, werden diese Werte auch trotz ausgeblendeter Nachstunden angezeigt. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Aktivierung der Balkengrafik Ebene 2 ====&lt;br /&gt;
[[Datei:Screenshot 2025-01-12 211741.png|right|thumb|400px|Balkengrafik mit aktivierter Ebene 2, gesetzem Attribut graphicShowNight=01 (mit Zeitsynchronisation zwischen Ebene 1 und Ebene 2)]]&lt;br /&gt;
&lt;br /&gt;
Um die &#039;&#039;&#039;Ebene 2&#039;&#039;&#039; der Balkengrafik zu aktivieren, setzt man das Attribut &#039;&#039;&#039;graphicBeam3Content&#039;&#039;&#039; und/oder &#039;&#039;&#039;graphicBeam4Content&#039;&#039;&#039; mit den gewünschten darzustellenden Inhalten. Die weiteren Darstellungsoptionen, wie graphicBeamCColor, können ebenfalls für den jeweiligen Balken selektiv definiert werden wenn ein Attribut mit der entsprechenden Nummer (z.B. 4 für Balken 4) vorhanden ist. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Hinweis:&#039;&#039;&#039; Die Wetter-Icons können nur in Ebene 1 integriert werden.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Werden in der Balkengrafik die Nachtstunden ausgeblendet (&#039;&#039;&#039;graphicShowNight=0&#039;&#039;&#039; bzw. default), findet zwischen der Ebene 1 und der Ebene 2 keine Synchronisation der Zeitebene statt. Falls es einen inhaltlichen Zusammenhang zwischen den Werten der Ebene 1 und Ebene 2 gibt, eine Zeitsynchronisation zwischen den Ebenen wünschenswert und sinnvoll. Durch Setzen des Attrbut &#039;&#039;&#039;graphicShowNight=01&#039;&#039;&#039; kann diese Synchronisation eingeschaltet werden. &lt;br /&gt;
&lt;br /&gt;
Dabei ist die Ebene 1 die führende Ebene, d.h. diese Ebene vererbt die exkludierten bzw. eventuell inkludierten Nachtstunden (wenn Werte in den Balken der Ebene 1 angezeigt werden sollen) an die nachfolgende Ebene.&lt;br /&gt;
 &lt;br /&gt;
&lt;br /&gt;
==== Aktivierung der Balkengrafik Ebene 3 ====&lt;br /&gt;
&lt;br /&gt;
Um die &#039;&#039;&#039;Ebene 3&#039;&#039;&#039; der Balkengrafik zu aktivieren, setzt man das Attribut &#039;&#039;&#039;graphicBeam5Content&#039;&#039;&#039; und/oder &#039;&#039;&#039;graphicBeam6Content&#039;&#039;&#039; mit den gewünschten darzustellenden Inhalten. Die weiteren Darstellungsoptionen, wie graphicBeamCColor, können ebenfalls für den jeweiligen Balken selektiv definiert werden wenn ein Attribut mit der entsprechenden Nummer (z.B. 5 für Balken 5) vorhanden ist. &lt;br /&gt;
&lt;br /&gt;
Werden in der Balkengrafik die Nachtstunden ausgeblendet (&#039;&#039;&#039;graphicShowNight=0&#039;&#039;&#039; bzw. default), findet zwischen der Ebene 1 und der Ebene 3 keine Synchronisation der Zeitebene statt. Falls es einen inhaltlichen Zusammenhang zwischen den Werten der Ebene 1 und Ebene 3 gibt, ist eine Zeitsynchronisation zwischen den Ebenen wünschenswert und sinnvoll. Durch Setzen des Attrbut &#039;&#039;&#039;graphicShowNight=01&#039;&#039;&#039; kann diese Synchronisation eingeschaltet werden. &lt;br /&gt;
&lt;br /&gt;
Dabei ist die Ebene 1 die führende Ebene, d.h. diese Ebene vererbt die exkludierten bzw. eventuell inkludierten Nachtstunden (wenn Werte in den Balken der Ebene 1 angezeigt werden sollen) an die nachfolgenden Ebenen, d.h. auch auf die Ebene 3.&lt;br /&gt;
&lt;br /&gt;
  &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 5 - Die Energieflußgrafik (Flow Graphic) ===&lt;br /&gt;
[[Datei:Screenshot 2025-02-13 213424.png|right|thumb|400px|Energieflußgrafik mit Verbrauchern in der Nachtdarstellung (Mondphasen) nach Sonnenuntergang]]&lt;br /&gt;
&lt;br /&gt;
Die Energieflußgrafik stellt die registrierten Erzeuger, Batterien, Netzzugänge und Verbraucher strukturiert in mehreren Ebenen dar:&lt;br /&gt;
&lt;br /&gt;
;Ebene 1: In der oberen Ebene befinden sich die PV-Generatoren, Smart-Loader sowie sonstigen Erzeuger &lt;br /&gt;
;Ebene 2: Die mittlere Ebene organisiert den Zugang zum öffentlichen Netz, dem Hausknoten, dem Batterieknoten sowie einem Dummy-Verbraucher (Lampensymbol) für den Energieverbrauch des Hauses der keinem der registrierten Verbraucher zugeordnet werden kann.&lt;br /&gt;
;Ebene 3: In der unteren Ebene werden die registrierten Verbraucher dargestellt. Unterhalb der Verbrauchersymbole werden der aktuelle Verbrauch (bzw. der On/Off-Zustand) sowie darunter die Restlaufzeit (Minuten) des Verbrauchers, sofern er durch das Modul geplant und gestartet wurde. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Die Position und Gestaltung der Grafikelemente ist umfangreich über das Attribut &#039;&#039;&#039;flowGraphicControl&#039;&#039;&#039; und den darin verfügbaren Schlüsseln einstellbar.&lt;br /&gt;
Die Bedeutung der Schlüssel ist in der Online-Hilfe zum Attribut mit einem Beispiel beschrieben. &lt;br /&gt;
&lt;br /&gt;
Darüber hinaus enthalten die Setup-Attribute &#039;&#039;&#039;setupInverterDevXX&#039;&#039;&#039;, &#039;&#039;&#039;setupOtherProducerXX&#039;&#039;&#039; und &#039;&#039;&#039;consumerXX&#039;&#039;&#039; weitere Einstellungsmöglichkeiten um deren Icons und Färbungen in der Flußgrafik individuell einstellen zu können. So sind zum Beispiel die Icons der PV-Inverter und deren Färbung mit dem Schlüssel &#039;&#039;&#039;icon&#039;&#039;&#039; der setupInverterDevXX-Attribute in Abhängigkeit von Tag und Nacht separat definierbar.&lt;br /&gt;
&lt;br /&gt;
In den Verbraucher-Attributen ist mit dem Schlüssel &#039;&#039;&#039;noshow&#039;&#039;&#039; die Sichtbarkeit in der Flußgrafik definierbar.&lt;br /&gt;
&lt;br /&gt;
Generell steuert das Attribut &#039;&#039;&#039;graphicSelect&#039;&#039;&#039; die Ein- bzw. Ausblendung der gesamten Flußgrafik.&lt;br /&gt;
&lt;br /&gt;
== Die Verbrauchsprognose ==&lt;br /&gt;
Im Gegensatz zu der PV Prognose gibt es keine API oder andere externen Datenquellen die für die Verbrauchsprognose herangezogen werden könnten.&lt;br /&gt;
&lt;br /&gt;
Zum Zweck der Verbrauchsermittlung verwendet das Modul die Meterdaten aus dem Attribut &#039;&#039;setupMeterDev&#039;&#039; sowie den weiteren setup-Attributen &#039;&#039;setupInverterDevXX&#039;&#039;. &lt;br /&gt;
Sind auch andere Erzeuger (BHKW, Windanlagen, etc.) bzw. eine Batterie vorhanden, sind ebenfalls die Attribute &#039;&#039;setupOtherProducerXX&#039;&#039; bzw. &#039;&#039;setupBatteryDev&#039;&#039; für die Berechnung des Hausverbrauchs relevant. In diesen Attributen gibt es die Schlüssel &#039;&#039;*total&#039;&#039; die teilweise optional sind. Für die Verbrauchsprognose bilden sie jedoch die Berechnungsgrundlage und sind deswegen für diese Funktionalität wichtig anzugeben.&lt;br /&gt;
&lt;br /&gt;
Die Verbrauchswerte werden stundengenau erfasst und im Datenspeicher &#039;&#039;pvHistory&#039;&#039; für 31 Tage, sowie im Datenspeicher &#039;&#039;pvCircular&#039;&#039; 210 Tage gespeichert. Die beiden Datenspeicher übernehmen unterschiedliche Aufgaben im Rahmen der Prognoseerstellung. Auf Grundlage der gespeicherten und somit historischen Verbrauchswerte erstellt das Modul eine Prognose für die kommende Zeit.&lt;br /&gt;
&lt;br /&gt;
Mit dem Kommando &lt;br /&gt;
&lt;br /&gt;
   &amp;quot;get ... pvHistory [Tag]&amp;quot; &lt;br /&gt;
&lt;br /&gt;
wird der Inhalt aufgelistet. Die Schlüssel &#039;&#039;confc&#039;&#039;, &#039;&#039;con&#039;&#039; und &#039;&#039;gcon&#039;&#039; zeigen die Verbrauchsprognose, den Verbrauch und den Netzbezug der jeweiligen Stunde des gewählten Tages.&lt;br /&gt;
Die Stunde &amp;quot;99&amp;quot; zeigt die Summen dieser Schlüsssel für den Tag:&lt;br /&gt;
&lt;br /&gt;
      99 =&amp;gt; etotal: , pvfc: 782, pvrl: 0, rad1h: -&lt;br /&gt;
            confc: 14540, con: 13873, gcon: 13873, gfeedin: 0&lt;br /&gt;
            batintotal: , batin: 0, batouttotal: , batout: 0&lt;br /&gt;
            batmaxsoc: -, batsetsoc: -&lt;br /&gt;
            wid: , wcc: , wrp: , pvcorrf: , dayname: So&lt;br /&gt;
            cyclescsm01: 0&lt;br /&gt;
            cyclescsm02: 0&lt;br /&gt;
            cyclescsm03: 0&lt;br /&gt;
            cyclescsm04: 1, hourscsme04: 0.00&lt;br /&gt;
            cyclescsm05: 0&lt;br /&gt;
            cyclescsm06: 0&lt;br /&gt;
            cyclescsm07: 1, hourscsme07: 0.00&lt;br /&gt;
            cyclescsm08: 1, hourscsme08: 0.00&lt;br /&gt;
            cyclescsm09: 1, hourscsme09: 0.03 &lt;br /&gt;
&lt;br /&gt;
In dem Beispiel wurden für den Tag 14540 Wh Verbrauch prognostiziert, 13873 Wh tatsächlich verbraucht und davon 13873 Wh aus dem Netz bezogen. Leider war an diesem Tag keinerlei PV Ertrag vorhanden, was man auch an dem Schlüssel &#039;&#039;pvrl&#039;&#039; erkennen kann.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Den Inhalt des pvCircular-Speichers kann mit dem Befehl  &lt;br /&gt;
&lt;br /&gt;
     &amp;quot;get ... pvCircular [Tag]&amp;quot;&lt;br /&gt;
&lt;br /&gt;
angezeigt werden. Das nachfolgende Beispiel zeigt die Ausgabe für die Tagesstunde 23:&lt;br /&gt;
&lt;br /&gt;
 23 =&amp;gt; pvapifc: 0, pvaifc: -, pvfc: 0, aihit: 0, pvrl: 0&lt;br /&gt;
       batin01: 0, batin02: -, batin03: -&lt;br /&gt;
       batout01: 619, batout02: -, batout03: -&lt;br /&gt;
       confc: 650, gcon: 23, gfeedin: 0, wcc: 40, rr1c: 0.00&lt;br /&gt;
       temp: -6.00, wid: 100, wtxt: Bewölkungsentwicklung nicht beobachtet&lt;br /&gt;
       pprl01: -, pprl02: -, pprl03: -&lt;br /&gt;
       ... &lt;br /&gt;
       con_all =&amp;gt; Sa  @ 617 762&lt;br /&gt;
                  Di  @ 693 645&lt;br /&gt;
                  Mo  @ 620 679&lt;br /&gt;
                  So  @ 669 666 636&lt;br /&gt;
                  Mi  @ 701 626&lt;br /&gt;
                  Do  @ 612 642&lt;br /&gt;
                  Fr  @ 615 606&lt;br /&gt;
&lt;br /&gt;
Der Schüssel &#039;&#039;con_all&#039;&#039; enthält einen Hash aus Arrays die für jeden Wochentag (Mo..So) bis zu 30 Verbrauchswerte (in Wh) für die angezeigte Stunde speichern. Wie hier erkennbar ist, wurden an den drei vergangenen Sonntagen jweils 669 Wh, 666 Wh bzw. 636 Wh in der Stunde 23 (22:00-22:59) verbraucht.&lt;br /&gt;
Der Median aus diesen Werten geht in die Verbrauchsprognose ein. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Hinweis:&#039;&#039;&#039; Die Wochentagsnamen Mo..So sind von den installierten Locales auf Betriebsystemebene abhängig. Ist keine deutsche locale installiert, werden die englischen Wochentagsnamen Mon..Sun verwendet. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Wie wird der Verbrauch des Hauses ermittelt und gespeichert? ===&lt;br /&gt;
Die Verbrauchsdaten des Hauses werden auf Stundenbasis ermittelt und gespeichert. Das &amp;quot;Haus&amp;quot; steht als Synonym aller im Kontext des Solarforecast Devices zusammengeführten Inverter (setupInverterDevXX), sonstigen Erzeuger (setupOtherProducerXX), Netzanschlüsse (setupMeterDev), Verbraucher (consumerXX) und Batterien (setupBatteryDev).&lt;br /&gt;
&lt;br /&gt;
Alle diese Daten werden durch das Modul mit jedem ausgeführten Zyklus (Attribut plantControl-&amp;gt;cycleInterval) und, wenn ein Event eines als &#039;asynchron&#039; definierten Devices auftritt, gesammelt und daraus permanent ein aktualisierter Verbrauch berechnet. Die Berechnung beschreibt folgende Formel:&lt;br /&gt;
&lt;br /&gt;
 PV-Erzeugung + sonstige Erzeugung - Netzeinspeisung + Netzbezug - Batterieladung + Batterieentladung - Verbrauch = 0&lt;br /&gt;
&lt;br /&gt;
bzw.&lt;br /&gt;
 &lt;br /&gt;
 Verbrauch (Wh) = PV-Erzeugung + sonstige Erzeugung - Netzeinspeisung + Netzbezug - Batterieladung + Batterieentladung&lt;br /&gt;
            con = PV           + PP                 - GridIn          + GridCon   - BatIn          + BatOut&lt;br /&gt;
&lt;br /&gt;
Die PV-Erzeugung beinhaltet die Summe aller durch die angegebnen Inverter (setupInverterDev01 .. setupInverterDevXX) erzeugten Energien. Die &amp;quot;sonstige Erzeugung&amp;quot; summiert sich aus den Werten aller angegebenen Producer (setupOtherProducer01 .. setupOtherProducerXX). Der Verbrauch wird durch die registrierten Geräte consumerXX teilweise direkt erfasst, ist aber grundsätzlich unvollständig da nicht alle Verbrauchswerte im &amp;quot;Haus&amp;quot; gemessen und durch das Modul erfasst werden können.&lt;br /&gt;
&lt;br /&gt;
Das folgende Beispiel zeigt das Ergebnis bei 300 Wh PV-Erzeugung und gleichzeitige 500 Wh Netzbezug:&lt;br /&gt;
&lt;br /&gt;
 Verbrauch = 300 + 0 - 0 + 500 - 0 + 0 = 800 Wh&lt;br /&gt;
&lt;br /&gt;
Bei 1300Wh PV-Erzeugung, 100 Wh Netzeinspeisung sowie 200 Wh Batterieladung ergibt sich der Verbrauch zu:&lt;br /&gt;
&lt;br /&gt;
 Verbrauch = 1300 + 0 - 100 + 0 - 200 + 0 = 1000 Wh&lt;br /&gt;
&lt;br /&gt;
Ohne Erzeugung, aber mit der Energielieferung von 500 Wh aus der Batterie, 800 Wh Netzbezug (mit einem Anteil Batterienachladung aus dem Netz von 200 Wh) sieht die Verbrauchsrechnung wie folgt aus: &lt;br /&gt;
&lt;br /&gt;
 Verbrauch = 0 + 0 - 0 + 800 - 200 + 500 = 1100 Wh&lt;br /&gt;
&lt;br /&gt;
Der so ermittelte Verbrauchsert wird mit jedem Zyklus in der pvHistory im Schlüssel &amp;quot;con&amp;quot; gespeichert. Man kann sich die gespeicherten Werte mit dem Befehl&lt;br /&gt;
&lt;br /&gt;
 get &amp;lt;name&amp;gt; pvHistory [&amp;lt;Tag&amp;gt;]&lt;br /&gt;
&lt;br /&gt;
angezeigt werden. Nachfolgendes Beispiel zeigt einen Datensatz für die Stunde 8 eines Tages:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
      08 =&amp;gt; pvfc: 0, pvrl: 0, pvrlvd: 1, rad1h: -&lt;br /&gt;
            etotali01: 62940734, etotali02: 2928860, etotali03: -&lt;br /&gt;
            pvrl01: 0, pvrl02: 0, pvrl03: -&lt;br /&gt;
            etotalp01: -, etotalp02: -, etotalp03: -&lt;br /&gt;
            pprl01: -, pprl02: -, pprl03: -&lt;br /&gt;
            confc: 630, con: 962, gcons: 962, conprice: 0.2958&lt;br /&gt;
            gfeedin: 0, feedprice: 0.1269&lt;br /&gt;
            DoN: 0, sunaz: 120, sunalt: -3&lt;br /&gt;
            batintotal: 3713815.07742543, batouttotal: 3604468.26993988, batin: 0, batout: 0&lt;br /&gt;
            wid: 161, wcc: 89, rr1c: 0.00, pvcorrf: 0.27/0.00 temp: 7.40, &lt;br /&gt;
            csmt01: 110996.12, csme01: 32.1399999999994, minutescsm01: 30&lt;br /&gt;
            minutescsm02: 0&lt;br /&gt;
            csmt03: 2513.67, csme03: 0, minutescsm03: 0&lt;br /&gt;
            csmt04: 1581293.6, csme04: 136.5, minutescsm04: 60&lt;br /&gt;
            csmt05: 1662.17, csme05: 0, minutescsm05: 0&lt;br /&gt;
            csmt06: 90.53, csme06: 0, minutescsm06: 0&lt;br /&gt;
            csmt07: 39.75, csme07: 0, minutescsm07: 0&lt;br /&gt;
            csmt08: 38050, csme08: 0, minutescsm08: 0&lt;br /&gt;
            csmt09: 131627, csme09: 33.2000000000116, minutescsm09: 39&lt;br /&gt;
            minutescsm10: 0&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Im Schlüssel &amp;quot;con&amp;quot; ist der gespeicherte Verbrauch von 962 Wh ersichtlich.&lt;br /&gt;
&lt;br /&gt;
Zur Laufzeit bekommt man mit dem Attribut &#039;&#039;ctrlDebug=collectData&#039;&#039; einen Einblick in die Verbrauchsermittlung bei jedem Zyklus und kann so die Eingangswerte und das daraus resultierende Ergebnis im FHEM Log nachverfolgen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
2024.11.28 11:38:20.080 1: SolCast DEBUG&amp;gt; EnergyConsumption input -&amp;gt; PV: 96, PP: 0, GridIn: 0, GridCon: 582, BatIn: 5, BatOut: 0&lt;br /&gt;
2024.11.28 11:38:20.081 1: SolCast DEBUG&amp;gt; EnergyConsumption result -&amp;gt; 673 Wh&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Wie wird die Verbrauchsprognose erstellt? ===&lt;br /&gt;
&lt;br /&gt;
Aus den gespeicherten con-Werten (Verbrauchsdaten) der pvCircular (Schlüssel con_all) sowie Einträgen der pvHistory wird der Median der historischen Tage ermittelt und für den kommenden Tag bzw. die kommen Stunden zur Prognose angewendet. Dabei geht per default jeder verfügbare historische Tag gleichberechtigt in die Prognose ein. Mit dem Attribut &#039;&#039;&#039;plantControl-&amp;gt;consForecastIdentWeekdays=1&#039;&#039;&#039; kann festgelegt werden, dass nur gleiche Wochentage (Mo..So) für die Prognose verwendet werden. Hierbei spielt die Vermutung, dass das Verbrauchsverhalten eine gewisse Korrelation zum Wochentag hat, eine Rolle. &lt;br /&gt;
&lt;br /&gt;
Verwendet man die Einstellung consForecastIdentWeekdays=1, hat es sich als vorteilhaft erwiesen, das Attribut &#039;&#039;&#039;plantControl-&amp;gt;consForecastLastDays&#039;&#039;&#039; auf einen niedrigeren Wert als den Default zu setzen. Eine Einstellung auf &#039;8&#039; zum Beispiel verwendet den Median der gleichen Wochentage der vergangenenen 8 Wochen. Dadurch wird nicht zu weit in die Vergangenheit verzweigt und so eine langsame Veränderung des Verbrauchsmedian, zum Beispiel hervorgerufen durch den Übergang vom Winter in das Frühjahr hinein, abgebildet.&lt;br /&gt;
&lt;br /&gt;
Weiteren Einfluß auf die Verbrauchsprognose kann mit dem Schlüssel &#039;&#039;&#039;exconfc&#039;&#039;&#039; im Consumerattribut genommen werden. Insbesondere bei der unregelmäßigen Nutzung von Verbrauchern mit hoher Leistung kann die Projektion der aufgezeichneten historischen Werte in die Prognose zu unverhältnismäßig hohen Verbrauchswerwartunden führen. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Möglicherweise führt in Fällen die Verwendung eine der nachfolgenden Optionen zu einem besseren Ergebnis:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;1 -&#039;&#039;&#039; die allgemeine Verbrauchsprognose wird um die gespeicherten Energieverbrauchsanteile reduziert.&lt;br /&gt;
* &#039;&#039;&#039;2 -&#039;&#039;&#039; wie bei &#039;1&#039;, jedoch gehen die Planungsdaten des Verbrauchers bei der Prognose der kommenden Stunden wieder mit ein.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Wird &#039;&#039;&#039;exconfc&#039;&#039;&#039; in einem der Consumer verwendet, sollte&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;plantControl-&amp;gt;consForecastIdentWeekdays=1&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;plantControl-&amp;gt;consForecastLastDays=4&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
eingestellt werden. Hintergrund ist, dass die Verbrauchsanteile der jeweiligen Verbraucher nur maximal 31 Tage in der pvHistory verfügbar sind. Für längere Zeiträume stehen nur Gesamtsummen des Verbrauchs in der pvCircular zu Verfügung.  &lt;br /&gt;
&lt;br /&gt;
Mit &#039;&#039;&#039;exconfc=1&#039;&#039;&#039; werden die in der Historie aufgezeichneten Verbrauchsbestandteile aus einer Prognose herausgerechnet. Konkret werden die in der pvHistory aufgezeichneten Verbrauchsbestandteile für die relevante Stunde ermittelt und daraus ein Durchschnitt gebildet der vom Prognosewert subtrahiert wird. Mit dem Attributwert plantControl-&amp;gt;consForecastIdentWeekdays=1 werden wiederum nur identische Wochentage (Mo .. So) zusammengefasst.&lt;br /&gt;
&lt;br /&gt;
Mit &#039;&#039;&#039;exconfc=2&#039;&#039;&#039; erfolgt ebenso die Eliminierung des Verbrauchsanteils, es wird jedoch auf die Planungsdaten für diesen Verbraucher zugegriffen und die geplanten Energieanteile wiederum der Verbrauchsprognose für die kommenden Stunden hinzugerechnet.&lt;br /&gt;
&lt;br /&gt;
In beiden Anwendungsfällen ist es notwendig, dass dem Modul durch die Angabe entsprechender Readings der Energieverbrauch im Setup des Consumers bekannt gemacht wird damit diese Energieanteile registriert werden können. Allgemein funktioniert die Verbrauchsprognose gut wenn Verbrauchsprozesse regelmäßig in einem wiederkehrenden Zeitraster ablaufen, der Verbrauch durch persönliche Rituale bestimmt ist. So zum Beispiel könnte am Dienstag und Samstag gewöhnlich die Waschmaschine laufen, das E-Auto täglich aufgeladen werden und gebügelt wird oft am Sonntag Nachmittag.&lt;br /&gt;
&lt;br /&gt;
Unregelmäßige Nutzung, vor allem vom Großverbrauchern, lässt sich nur sehr schwer und ungenügend auf zukünftige Verbrauchsprognosen projizieren.&lt;br /&gt;
&lt;br /&gt;
Das Ergebnis der Prognose wird im Kopf der Grafik in der Zeile &#039;&#039;Verbrauch&#039;&#039; sowie in diversen Readings wie &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;RestOfDayConsumptionForecast&#039;&#039;&#039; (Verbrauchsprognose für den Rest des Tages)&lt;br /&gt;
* &#039;&#039;&#039;NextHours_Sum04_ConsumptionForecast&#039;&#039;&#039; (Verbrauchsprognose für die nächsten 4 Stunden)&lt;br /&gt;
* &#039;&#039;&#039;Tomorrow_ConsumptionForecast&#039;&#039;&#039; (Verbrauchsprognose für den kommenden Tag)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
angezeigt.&lt;br /&gt;
&lt;br /&gt;
Weitere Aggregationen kann man sich über das Attribut &#039;&#039;&#039;ctrlStatisticReadings&#039;&#039;&#039; bei Bedarf zuschalten:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;conForecastTillNextSunrise&#039;&#039;&#039; - Verbrauchsprognose von aktueller Stunde bis zum kommenden Sonnenaufgang&lt;br /&gt;
* &#039;&#039;&#039;todayConForecastTillSunset&#039;&#039;&#039; - Verbrauchsprognose von aktueller Stunde bis Stunde vor Sonnenuntergang&lt;br /&gt;
* &#039;&#039;&#039;todayConsumptionForecast&#039;&#039;&#039; - Verbrauchsprognose aufgesplittet pro Stunde des aktuellen Tages (01-24) &lt;br /&gt;
 &lt;br /&gt;
==== Debugging der Verbrauchsprognose ====&lt;br /&gt;
Um sich einen Einblick über die Erstellung der Verbrauchsprognose zu verschaffen, bietet sich die Einschaltung von:&lt;br /&gt;
&lt;br /&gt;
 attr &amp;lt;Name&amp;gt; ctrlDebug consumption_long&lt;br /&gt;
&lt;br /&gt;
an. Mit diesem gesetzten Attribut wird die Erstellung der Prognose im Log protokolliert:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
2025.02.27 19:52:00.063 1: SolCast DEBUG&amp;gt; ################### Start Consumption forecast ###################&lt;br /&gt;
2025.02.27 19:52:00.063 1: SolCast DEBUG&amp;gt; Basics - installed locale: de_DE.UTF-8, used scheme: DE&lt;br /&gt;
2025.02.27 19:52:00.064 1: SolCast DEBUG&amp;gt; process Today dayname: Do, Tomorrow dayname: Fr&lt;br /&gt;
2025.02.27 19:52:00.065 1: SolCast DEBUG&amp;gt; consumer &#039;03&#039; register for exclude day 13, hod: 01 - 74.43 Wh&lt;br /&gt;
2025.02.27 19:52:00.065 1: SolCast DEBUG&amp;gt; consumer &#039;03&#039; register for exclude day 13, hod: 10 - 290.77 Wh&lt;br /&gt;
2025.02.27 19:52:00.065 1: SolCast DEBUG&amp;gt; consumer &#039;03&#039; register for exclude day 13, hod: 24 - 660.70 Wh&lt;br /&gt;
2025.02.27 19:52:00.066 1: SolCast DEBUG&amp;gt; consumer &#039;03&#039; register for exclude day 20, hod: 07 - 459.93 Wh&lt;br /&gt;
2025.02.27 19:52:00.066 1: SolCast DEBUG&amp;gt; consumer &#039;07&#039; register for exclude day 20, hod: 02 - 0.33 Wh&lt;br /&gt;
2025.02.27 19:52:00.067 1: SolCast DEBUG&amp;gt; consumer &#039;03&#039; register for exclude day 30, hod: 08 - 264.78 Wh&lt;br /&gt;
2025.02.27 19:52:00.067 1: SolCast DEBUG&amp;gt; ################### Consumption forecast for the next Hours (new median) ###################&lt;br /&gt;
2025.02.27 19:52:00.067 1: SolCast DEBUG&amp;gt; excl. hist 74 Wh for Hour 01, Considered value numbers: 1&lt;br /&gt;
2025.02.27 19:52:00.067 1: SolCast DEBUG&amp;gt; estimated cons of Hour 01: 436 Wh, Considered value numbers: 4&lt;br /&gt;
2025.02.27 19:52:00.068 1: SolCast DEBUG&amp;gt; excl. hist 0 Wh for Hour 02, Considered value numbers: 1&lt;br /&gt;
2025.02.27 19:52:00.068 1: SolCast DEBUG&amp;gt; estimated cons of Hour 02: 428 Wh, Considered value numbers: 4&lt;br /&gt;
2025.02.27 19:52:00.068 1: SolCast DEBUG&amp;gt; estimated cons of Hour 03: 458 Wh, Considered value numbers: 4&lt;br /&gt;
2025.02.27 19:52:00.068 1: SolCast DEBUG&amp;gt; estimated cons of Hour 04: 503 Wh, Considered value numbers: 4&lt;br /&gt;
2025.02.27 19:52:00.069 1: SolCast DEBUG&amp;gt; estimated cons of Hour 05: 489 Wh, Considered value numbers: 4&lt;br /&gt;
2025.02.27 19:52:00.069 1: SolCast DEBUG&amp;gt; estimated cons of Hour 06: 482 Wh, Considered value numbers: 4&lt;br /&gt;
2025.02.27 19:52:00.069 1: SolCast DEBUG&amp;gt; excl. hist 460 Wh for Hour 07, Considered value numbers: 1&lt;br /&gt;
2025.02.27 19:52:00.069 1: SolCast DEBUG&amp;gt; estimated cons of Hour 07: 274 Wh, Considered value numbers: 4&lt;br /&gt;
2025.02.27 19:52:00.070 1: SolCast DEBUG&amp;gt; excl. hist 265 Wh for Hour 08, Considered value numbers: 1&lt;br /&gt;
2025.02.27 19:52:00.070 1: SolCast DEBUG&amp;gt; estimated cons of Hour 08: 441 Wh, Considered value numbers: 4&lt;br /&gt;
2025.02.27 19:52:00.070 1: SolCast DEBUG&amp;gt; estimated cons of Hour 09: 558 Wh, Considered value numbers: 4&lt;br /&gt;
2025.02.27 19:52:00.070 1: SolCast DEBUG&amp;gt; excl. hist 291 Wh for Hour 10, Considered value numbers: 1&lt;br /&gt;
2025.02.27 19:52:00.071 1: SolCast DEBUG&amp;gt; estimated cons of Hour 10: 443 Wh, Considered value numbers: 4&lt;br /&gt;
2025.02.27 19:52:00.071 1: SolCast DEBUG&amp;gt; estimated cons of Hour 11: 881 Wh, Considered value numbers: 4&lt;br /&gt;
2025.02.27 19:52:00.071 1: SolCast DEBUG&amp;gt; estimated cons of Hour 12: 794 Wh, Considered value numbers: 4&lt;br /&gt;
2025.02.27 19:52:00.072 1: SolCast DEBUG&amp;gt; estimated cons of Hour 13: 840 Wh, Considered value numbers: 4&lt;br /&gt;
2025.02.27 19:52:00.072 1: SolCast DEBUG&amp;gt; estimated cons of Hour 14: 660 Wh, Considered value numbers: 4&lt;br /&gt;
2025.02.27 19:52:00.072 1: SolCast DEBUG&amp;gt; estimated cons of Hour 15: 812 Wh, Considered value numbers: 4&lt;br /&gt;
2025.02.27 19:52:00.072 1: SolCast DEBUG&amp;gt; estimated cons of Hour 16: 752 Wh, Considered value numbers: 4&lt;br /&gt;
2025.02.27 19:52:00.073 1: SolCast DEBUG&amp;gt; estimated cons of Hour 17: 597 Wh, Considered value numbers: 4&lt;br /&gt;
2025.02.27 19:52:00.073 1: SolCast DEBUG&amp;gt; estimated cons of Hour 18: 652 Wh, Considered value numbers: 4&lt;br /&gt;
2025.02.27 19:52:00.073 1: SolCast DEBUG&amp;gt; estimated cons of Hour 19: 634 Wh, Considered value numbers: 4&lt;br /&gt;
2025.02.27 19:52:00.073 1: SolCast DEBUG&amp;gt; estimated cons of Hour 20: 628 Wh, Considered value numbers: 3&lt;br /&gt;
2025.02.27 19:52:00.074 1: SolCast DEBUG&amp;gt; estimated cons of Hour 21: 713 Wh, Considered value numbers: 3&lt;br /&gt;
2025.02.27 19:52:00.074 1: SolCast DEBUG&amp;gt; estimated cons of Hour 22: 596 Wh, Considered value numbers: 3&lt;br /&gt;
2025.02.27 19:52:00.074 1: SolCast DEBUG&amp;gt; estimated cons of Hour 23: 629 Wh, Considered value numbers: 3&lt;br /&gt;
2025.02.27 19:52:00.074 1: SolCast DEBUG&amp;gt; excl. hist 661 Wh for Hour 24, Considered value numbers: 1&lt;br /&gt;
2025.02.27 19:52:00.075 1: SolCast DEBUG&amp;gt; estimated cons of Hour 24: 660 Wh, Considered value numbers: 3&lt;br /&gt;
2025.02.27 19:52:00.075 1: SolCast DEBUG&amp;gt; ################### Consumption forecast for the next day (new median) ###################&lt;br /&gt;
2025.02.27 19:52:00.075 1: SolCast DEBUG&amp;gt; estimated cons Tomorrow: 16848 Wh, Individual hourly values considered: 72, exclude: 0 Wh (avg of 0 entities)&lt;br /&gt;
2025.02.27 19:52:00.075 1: SolCast DEBUG&amp;gt; ################### Store Consumption forecast values (new median) ###################&lt;br /&gt;
2025.02.27 19:52:00.076 1: SolCast DEBUG&amp;gt; store &#039;NextHour00&#039; hod &#039;20&#039; confc: 628, confcEx: 628&lt;br /&gt;
2025.02.27 19:52:00.076 1: SolCast DEBUG&amp;gt; store circular/history hod &#039;20&#039; confc: 628&lt;br /&gt;
2025.02.27 19:52:00.076 1: SolCast DEBUG&amp;gt; store &#039;NextHour01&#039; hod &#039;21&#039; confc: 713, confcEx: 713&lt;br /&gt;
2025.02.27 19:52:00.077 1: SolCast DEBUG&amp;gt; store circular/history hod &#039;21&#039; confc: 713&lt;br /&gt;
2025.02.27 19:52:00.077 1: SolCast DEBUG&amp;gt; store &#039;NextHour02&#039; hod &#039;22&#039; confc: 596, confcEx: 596&lt;br /&gt;
2025.02.27 19:52:00.077 1: SolCast DEBUG&amp;gt; store circular/history hod &#039;22&#039; confc: 596&lt;br /&gt;
2025.02.27 19:52:00.077 1: SolCast DEBUG&amp;gt; store &#039;NextHour03&#039; hod &#039;23&#039; confc: 629, confcEx: 629&lt;br /&gt;
2025.02.27 19:52:00.078 1: SolCast DEBUG&amp;gt; store circular/history hod &#039;23&#039; confc: 629&lt;br /&gt;
2025.02.27 19:52:00.078 1: SolCast DEBUG&amp;gt; store &#039;NextHour04&#039; hod &#039;24&#039; confc: 660, confcEx: 660&lt;br /&gt;
2025.02.27 19:52:00.078 1: SolCast DEBUG&amp;gt; store circular/history hod &#039;24&#039; confc: 660&lt;br /&gt;
2025.02.27 19:52:00.079 1: SolCast DEBUG&amp;gt; store &#039;NextHour05&#039; hod &#039;01&#039; confc: 436, confcEx: 436&lt;br /&gt;
2025.02.27 19:52:00.079 1: SolCast DEBUG&amp;gt; store &#039;NextHour06&#039; hod &#039;02&#039; confc: 428, confcEx: 428&lt;br /&gt;
2025.02.27 19:52:00.079 1: SolCast DEBUG&amp;gt; store &#039;NextHour07&#039; hod &#039;03&#039; confc: 458, confcEx: 458&lt;br /&gt;
2025.02.27 19:52:00.079 1: SolCast DEBUG&amp;gt; store &#039;NextHour08&#039; hod &#039;04&#039; confc: 503, confcEx: 503&lt;br /&gt;
2025.02.27 19:52:00.080 1: SolCast DEBUG&amp;gt; store &#039;NextHour09&#039; hod &#039;05&#039; confc: 489, confcEx: 489&lt;br /&gt;
2025.02.27 19:52:00.080 1: SolCast DEBUG&amp;gt; store &#039;NextHour10&#039; hod &#039;06&#039; confc: 482, confcEx: 482&lt;br /&gt;
2025.02.27 19:52:00.080 1: SolCast DEBUG&amp;gt; store &#039;NextHour11&#039; hod &#039;07&#039; confc: 274, confcEx: 274&lt;br /&gt;
2025.02.27 19:52:00.081 1: SolCast DEBUG&amp;gt; store &#039;NextHour12&#039; hod &#039;08&#039; confc: 441, confcEx: 441&lt;br /&gt;
2025.02.27 19:52:00.081 1: SolCast DEBUG&amp;gt; store &#039;NextHour13&#039; hod &#039;09&#039; confc: 558, confcEx: 558&lt;br /&gt;
2025.02.27 19:52:00.081 1: SolCast DEBUG&amp;gt; store &#039;NextHour14&#039; hod &#039;10&#039; confc: 443, confcEx: 443&lt;br /&gt;
2025.02.27 19:52:00.081 1: SolCast DEBUG&amp;gt; store &#039;NextHour15&#039; hod &#039;11&#039; confc: 881, confcEx: 881&lt;br /&gt;
2025.02.27 19:52:00.082 1: SolCast DEBUG&amp;gt; store &#039;NextHour16&#039; hod &#039;12&#039; confc: 794, confcEx: 794&lt;br /&gt;
2025.02.27 19:52:00.082 1: SolCast DEBUG&amp;gt; store &#039;NextHour17&#039; hod &#039;13&#039; confc: 840, confcEx: 840&lt;br /&gt;
2025.02.27 19:52:00.082 1: SolCast DEBUG&amp;gt; store &#039;NextHour18&#039; hod &#039;14&#039; confc: 660, confcEx: 660&lt;br /&gt;
2025.02.27 19:52:00.083 1: SolCast DEBUG&amp;gt; store &#039;NextHour19&#039; hod &#039;15&#039; confc: 812, confcEx: 812&lt;br /&gt;
2025.02.27 19:52:00.083 1: SolCast DEBUG&amp;gt; store &#039;NextHour20&#039; hod &#039;16&#039; confc: 752, confcEx: 752&lt;br /&gt;
2025.02.27 19:52:00.083 1: SolCast DEBUG&amp;gt; store &#039;NextHour21&#039; hod &#039;17&#039; confc: 597, confcEx: 597&lt;br /&gt;
2025.02.27 19:52:00.083 1: SolCast DEBUG&amp;gt; store &#039;NextHour22&#039; hod &#039;18&#039; confc: 652, confcEx: 652&lt;br /&gt;
2025.02.27 19:52:00.084 1: SolCast DEBUG&amp;gt; store &#039;NextHour23&#039; hod &#039;19&#039; confc: 634, confcEx: 634&lt;br /&gt;
2025.02.27 19:52:00.084 1: SolCast DEBUG&amp;gt; store &#039;NextHour24&#039; hod &#039;20&#039; confc: 628, confcEx: 628&lt;br /&gt;
2025.02.27 19:52:00.084 1: SolCast DEBUG&amp;gt; store &#039;NextHour25&#039; hod &#039;21&#039; confc: 713, confcEx: 713&lt;br /&gt;
2025.02.27 19:52:00.084 1: SolCast DEBUG&amp;gt; store &#039;NextHour26&#039; hod &#039;22&#039; confc: 596, confcEx: 596&lt;br /&gt;
2025.02.27 19:52:00.085 1: SolCast DEBUG&amp;gt; store &#039;NextHour27&#039; hod &#039;23&#039; confc: 629, confcEx: 629&lt;br /&gt;
2025.02.27 19:52:00.085 1: SolCast DEBUG&amp;gt; store &#039;NextHour28&#039; hod &#039;24&#039; confc: 660, confcEx: 660&lt;br /&gt;
2025.02.27 19:52:00.086 1: SolCast DEBUG&amp;gt; consumption calculated - day: 27, hod: 20, con: 574 Wh&lt;br /&gt;
2025.02.27 19:52:00.086 1: SolCast DEBUG&amp;gt; write pvCircular consumption - hod: 99, todayConsumption: 11210 Wh&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Die Ausgabe startet mit &#039;&#039;&amp;quot;Start Consumption forecast&#039;&#039;&amp;quot;, es werden die zu exkludierenden historischen Daten für jeden Consumer bzgl. jedem Tag (day) und Stunde des Tages (hod) getrennt registriert. Diese Werte werden durch die gespeicherte Einträge in der pvHistory (max. 31 historische Tage) geliefert. &lt;br /&gt;
&lt;br /&gt;
Im nächsten Abschnitt &#039;&#039;&amp;quot;Consumption forecast for the next Hours (new median)&amp;quot;&#039;&#039; werden die Prognosen für die nächsten Stunden kalkuliert und die zuvor registrierten Excludes angewendet. Die Prognose für den kommenden Tag ist im Abschnitt &#039;&#039;&amp;quot;Consumption forecast for the next day (new median)&amp;quot;&#039;&#039; protokolliert. Die Prognose für den kommenden Tag ist nicht einfach die Summe der Prognose der nächsten Stunden, denn in den kommenden Stunden werden auch die verfügbaren Planungen der Consumer einbezogen. Für den kommenden Tag können diese Werte unter Umständen nicht einbezogen werden da sie zum Zeitpunkt X noch nicht verfügbar sind.&lt;br /&gt;
&lt;br /&gt;
Im Abschnitt &#039;&#039;&amp;quot;Store Consumption forecast values (new median)&amp;quot;&#039;&#039; erscheinen die effektiv gespeicherten Werte. Diese sind die Grandlage für die grafische Darstellung sowie weiteren Kalkulationen.&lt;br /&gt;
&lt;br /&gt;
=== Wie kann eine fehlerhafte Verbrauchsprognose korrigiert werden? ===&lt;br /&gt;
Wie im vorherigen Abschnitt beschrieben, wird die Prognose aus den gespeicherten &amp;quot;con&amp;quot;-Werten der vergangenen Tage (bzw. Stunden) abgeleitet. &lt;br /&gt;
Grundsätzlich muß zunächst sichergestellt werden, dass die auszulesenden Readings der angegebenen Energiezähler sowie deren Einheiten! korrekt eingestellt sind. Das gilt ebenfalls für die registrierten Verbraucher im Modul.&lt;br /&gt;
&lt;br /&gt;
Für einen ersten Überblick bietet sich das Debug Attribut an zu setzen:&lt;br /&gt;
&lt;br /&gt;
 attr &amp;lt;Name&amp;gt; ctrlDebug consumption_long&lt;br /&gt;
&lt;br /&gt;
Beispielausgabe des Debug:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
2024.09.21 13:28:27.041 1: SolCast DEBUG&amp;gt; ################### Consumption forecast for the next day ###################&lt;br /&gt;
2024.09.21 13:28:27.042 1: SolCast DEBUG&amp;gt; History Consumption day &amp;gt;01&amp;lt; considering possible exclusions: 14082&lt;br /&gt;
2024.09.21 13:28:27.042 1: SolCast DEBUG&amp;gt; History Consumption day &amp;gt;08&amp;lt; considering possible exclusions: 16446&lt;br /&gt;
2024.09.21 13:28:27.042 1: SolCast DEBUG&amp;gt; History Consumption day &amp;gt;15&amp;lt; considering possible exclusions: 14568&lt;br /&gt;
2024.09.21 13:28:27.043 1: SolCast DEBUG&amp;gt; History Consumption day &amp;gt;25&amp;lt; considering possible exclusions: 15522&lt;br /&gt;
2024.09.21 13:28:27.043 1: SolCast DEBUG&amp;gt; estimated Consumption for tomorrow: 15154, days for avg: 4&lt;br /&gt;
2024.09.21 13:28:27.043 1: SolCast DEBUG&amp;gt; ################### Consumption forecast for the next hours ###################&lt;br /&gt;
2024.09.21 13:28:27.044 1: SolCast DEBUG&amp;gt;     historical Consumption added for Sa -&amp;gt; date: 07, hod: 14 -&amp;gt; 850 Wh&lt;br /&gt;
2024.09.21 13:28:27.044 1: SolCast DEBUG&amp;gt;     historical Consumption added for Sa -&amp;gt; date: 14, hod: 14 -&amp;gt; 643 Wh&lt;br /&gt;
2024.09.21 13:28:27.044 1: SolCast DEBUG&amp;gt;     historical Consumption added for Sa -&amp;gt; date: 24, hod: 14 -&amp;gt; 590 Wh&lt;br /&gt;
2024.09.21 13:28:27.045 1: SolCast DEBUG&amp;gt;     historical Consumption added for Sa -&amp;gt; date: 31, hod: 14 -&amp;gt; 522 Wh&lt;br /&gt;
2024.09.21 13:28:27.045 1: SolCast DEBUG&amp;gt; estimated Consumption for Sa -&amp;gt; starttime: 2024-09-21 13:00:00, confc: 651, days for avg: 4, hist. consumption registered consumers: 560.97&lt;br /&gt;
2024.09.21 13:28:27.046 1: SolCast DEBUG&amp;gt;     historical Consumption added for Sa -&amp;gt; date: 07, hod: 15 -&amp;gt; 720 Wh&lt;br /&gt;
2024.09.21 13:28:27.046 1: SolCast DEBUG&amp;gt;     historical Consumption added for Sa -&amp;gt; date: 14, hod: 15 -&amp;gt; 743 Wh&lt;br /&gt;
2024.09.21 13:28:27.046 1: SolCast DEBUG&amp;gt;     historical Consumption added for Sa -&amp;gt; date: 24, hod: 15 -&amp;gt; 897 Wh&lt;br /&gt;
2024.09.21 13:28:27.047 1: SolCast DEBUG&amp;gt;     historical Consumption added for Sa -&amp;gt; date: 31, hod: 15 -&amp;gt; 557 Wh&lt;br /&gt;
2024.09.21 13:28:27.047 1: SolCast DEBUG&amp;gt; estimated Consumption for Sa -&amp;gt; starttime: 2024-09-21 14:00:00, confc: 729, days for avg: 4, hist. consumption registered consumers: 620.97&lt;br /&gt;
2024.09.21 13:28:27.047 1: SolCast DEBUG&amp;gt;     historical Consumption added for Sa -&amp;gt; date: 07, hod: 16 -&amp;gt; 676 Wh&lt;br /&gt;
2024.09.21 13:28:27.048 1: SolCast DEBUG&amp;gt;     historical Consumption added for Sa -&amp;gt; date: 14, hod: 16 -&amp;gt; 790 Wh&lt;br /&gt;
2024.09.21 13:28:27.048 1: SolCast DEBUG&amp;gt;     historical Consumption added for Sa -&amp;gt; date: 24, hod: 16 -&amp;gt; 785 Wh&lt;br /&gt;
2024.09.21 13:28:27.048 1: SolCast DEBUG&amp;gt;     historical Consumption added for Sa -&amp;gt; date: 31, hod: 16 -&amp;gt; 581 Wh&lt;br /&gt;
2024.09.21 13:28:27.049 1: SolCast DEBUG&amp;gt; estimated Consumption for Sa -&amp;gt; starttime: 2024-09-21 15:00:00, confc: 708, days for avg: 4, hist. consumption registered consumers: 647.29&lt;br /&gt;
2024.09.21 13:28:27.049 1: SolCast DEBUG&amp;gt;     historical Consumption added for Sa -&amp;gt; date: 07, hod: 17 -&amp;gt; 622 Wh&lt;br /&gt;
2024.09.21 13:28:27.050 1: SolCast DEBUG&amp;gt;     historical Consumption added for Sa -&amp;gt; date: 14, hod: 17 -&amp;gt; 639 Wh&lt;br /&gt;
2024.09.21 13:28:27.050 1: SolCast DEBUG&amp;gt;     historical Consumption added for Sa -&amp;gt; date: 24, hod: 17 -&amp;gt; 646 Wh&lt;br /&gt;
2024.09.21 13:28:27.050 1: SolCast DEBUG&amp;gt;     historical Consumption added for Sa -&amp;gt; date: 31, hod: 17 -&amp;gt; 572 Wh&lt;br /&gt;
2024.09.21 13:28:27.051 1: SolCast DEBUG&amp;gt; estimated Consumption for Sa -&amp;gt; starttime: 2024-09-21 16:00:00, confc: 619, days for avg: 4, hist. consumption registered consumers: 618.30&lt;br /&gt;
2024.09.21 13:28:27.051 1: SolCast DEBUG&amp;gt;     historical Consumption added for Sa -&amp;gt; date: 07, hod: 18 -&amp;gt; 596 Wh&lt;br /&gt;
2024.09.21 13:28:27.051 1: SolCast DEBUG&amp;gt;     historical Consumption added for Sa -&amp;gt; date: 14, hod: 18 -&amp;gt; 614 Wh&lt;br /&gt;
2024.09.21 13:28:27.052 1: SolCast DEBUG&amp;gt;     historical Consumption added for Sa -&amp;gt; date: 24, hod: 18 -&amp;gt; 580 Wh&lt;br /&gt;
2024.09.21 13:28:27.052 1: SolCast DEBUG&amp;gt;     historical Consumption added for Sa -&amp;gt; date: 31, hod: 18 -&amp;gt; 480 Wh&lt;br /&gt;
2024.09.21 13:28:27.052 1: SolCast DEBUG&amp;gt; estimated Consumption for Sa -&amp;gt; starttime: 2024-09-21 17:00:00, confc: 567, days for avg: 4, hist. consumption registered consumers: 583.41&lt;br /&gt;
2024.09.21 13:28:27.053 1: SolCast DEBUG&amp;gt;     historical Consumption added for Sa -&amp;gt; date: 07, hod: 19 -&amp;gt; 615 Wh&lt;br /&gt;
2024.09.21 13:28:27.053 1: SolCast DEBUG&amp;gt;     historical Consumption added for Sa -&amp;gt; date: 14, hod: 19 -&amp;gt; 1659 Wh&lt;br /&gt;
2024.09.21 13:28:27.053 1: SolCast DEBUG&amp;gt;     historical Consumption added for Sa -&amp;gt; date: 24, hod: 19 -&amp;gt; 597 Wh&lt;br /&gt;
2024.09.21 13:28:27.054 1: SolCast DEBUG&amp;gt;     historical Consumption added for Sa -&amp;gt; date: 31, hod: 19 -&amp;gt; 618 Wh&lt;br /&gt;
2024.09.21 13:28:27.054 1: SolCast DEBUG&amp;gt; estimated Consumption for Sa -&amp;gt; starttime: 2024-09-21 18:00:00, confc: 872, days for avg: 4, hist. consumption registered consumers: 636.97&lt;br /&gt;
2024.09.21 13:28:27.055 1: SolCast DEBUG&amp;gt;     historical Consumption added for Sa -&amp;gt; date: 07, hod: 20 -&amp;gt; 611 Wh&lt;br /&gt;
2024.09.21 13:28:27.055 1: SolCast DEBUG&amp;gt;     historical Consumption added for Sa -&amp;gt; date: 14, hod: 20 -&amp;gt; 1124 Wh&lt;br /&gt;
2024.09.21 13:28:27.055 1: SolCast DEBUG&amp;gt;     historical Consumption added for Sa -&amp;gt; date: 24, hod: 20 -&amp;gt; 546 Wh&lt;br /&gt;
2024.09.21 13:28:27.056 1: SolCast DEBUG&amp;gt;     historical Consumption added for Sa -&amp;gt; date: 31, hod: 20 -&amp;gt; 479 Wh&lt;br /&gt;
2024.09.21 13:28:27.056 1: SolCast DEBUG&amp;gt; estimated Consumption for Sa -&amp;gt; starttime: 2024-09-21 19:00:00, confc: 690, days for avg: 4, hist. consumption registered consumers: 578.87&lt;br /&gt;
2024.09.21 13:28:27.056 1: SolCast DEBUG&amp;gt;     historical Consumption added for Sa -&amp;gt; date: 07, hod: 21 -&amp;gt; 1474 Wh&lt;br /&gt;
2024.09.21 13:28:27.057 1: SolCast DEBUG&amp;gt;     historical Consumption added for Sa -&amp;gt; date: 14, hod: 21 -&amp;gt; 596 Wh&lt;br /&gt;
2024.09.21 13:28:27.057 1: SolCast DEBUG&amp;gt;     historical Consumption added for Sa -&amp;gt; date: 24, hod: 21 -&amp;gt; 619 Wh&lt;br /&gt;
2024.09.21 13:28:27.057 1: SolCast DEBUG&amp;gt;     historical Consumption added for Sa -&amp;gt; date: 31, hod: 21 -&amp;gt; 433 Wh&lt;br /&gt;
2024.09.21 13:28:27.058 1: SolCast DEBUG&amp;gt; estimated Consumption for Sa -&amp;gt; starttime: 2024-09-21 20:00:00, confc: 780, days for avg: 4, hist. consumption registered consumers: 584.96&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Im ersten Teil wird die erwartete Consumption für den nächsten Tag unter Berücksichtigung möglicher Ausschlüsse von Verbrauchern ausgegeben. Ausschlüsse von Verbrauchern ergeben sich wenn in dem Verbraucherattribut der Schlüssel &#039;&#039;&#039;exconfc&#039;&#039;&#039; gesetzt ist. Er besagt, dass die Verbrauchswerte des Verbrauchers in eine zukünftige Prognose nicht eingehen sollen. Zum Beispiel wenn ein Verbraucher mit sehr hohen Verbrauchswerten nur sporadisch eingesetzt wird und dadurch die Prognose stark verfälschen würde. Es ist auch zu sehen, dass bedingt durch das Attribut &#039;&#039;&#039;affectConsForecastIdentWeekdays = 1&#039;&#039;&#039; nur die historischen Tage 01, 08, 15 und 25 berücksichtigt werden.&lt;br /&gt;
&lt;br /&gt;
Im zweiten Teil wird die berechnete Erwartung für die nächsten Stunden ausgegeben. &lt;br /&gt;
Zur Beschreibung der Fehlersuche gehen wir davon aus, dass die Prognose von 780 Wh am 2024-09-21 20:00:00 zu hoch erscheint weil am Tag &amp;quot;07&amp;quot; ein Wert von 1474 Wh gespeichert ist:&lt;br /&gt;
&lt;br /&gt;
 SolCast DEBUG&amp;gt; estimated Consumption for Sa -&amp;gt; starttime: 2024-09-21 20:00:00, confc: 780, days for avg: 4, hist. consumption registered consumers: 584.96&lt;br /&gt;
&lt;br /&gt;
Im nächsten Schritt geben wir die historischen Werte für den Tag 07 aus:&lt;br /&gt;
&lt;br /&gt;
 get &amp;lt;Name&amp;gt; pvHistory 07&lt;br /&gt;
&lt;br /&gt;
In dem Datensatz interessiert die Stunde 21. Sie resultiert aus der obigen Angabe &amp;quot;starttime: xxxx-xx-xx 20:00:00&amp;quot;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
      21 =&amp;gt; etotal: 64354660, pvfc: 0, pvrl: 0, pvrlvd: 1, rad1h: -&lt;br /&gt;
            etotalp01: -, etotalp02: -, etotalp03: -&lt;br /&gt;
            pprl01: -, pprl02: -, pprl03: -&lt;br /&gt;
            confc: 496, con: 1474, gcons: 23, conprice: 0.2958&lt;br /&gt;
            gfeedin: 5, feedprice: 0.1269&lt;br /&gt;
            DoN: 0, sunaz: 289, sunalt: -8&lt;br /&gt;
            batintotal: 3064096.92456668, batouttotal: 2958021.37946804, batin: 0, batout: 1460&lt;br /&gt;
            wid: 101, wcc: 20, rr1c: 0.00, pvcorrf: 1.00/-temp: 26.00, &lt;br /&gt;
            csmt01: 64262.43, csme01: 28.8099999999977, minutescsm01: 25&lt;br /&gt;
            minutescsm02: 0&lt;br /&gt;
            csmt03: 0, csme03: 0, minutescsm03: 0&lt;br /&gt;
            csmt04: 1389417.4, csme04: 94.5, minutescsm04: 60&lt;br /&gt;
            csmt05: 0, csme05: 0, minutescsm05: 0&lt;br /&gt;
            csmt06: 290.15, csme06: 6.66999999999996, minutescsm06: 54&lt;br /&gt;
            csmt07: 0, csme07: 0, minutescsm07: 0&lt;br /&gt;
            csmt08: 32800, csme08: 0, minutescsm08: 0&lt;br /&gt;
            csmt09: 103987.8, csme09: 38, minutescsm09: 43&lt;br /&gt;
            csmt10: 9151.24999999783, csme10: 3.99999999997999, minutescsm10: 60&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In dem Datensatz sind die Energieverbrauchsanteile der im Device registrierten Verbraucher mit dem Schlüssel &amp;quot;csmeXX&amp;quot; gespeichert. Dabei ist &amp;quot;XX&amp;quot; die Verbrauchernummer. In dem Beispiel existieren die Anteile csme01 und csme03 - csme10. Keiner dieser Verbraucher hat einen auffälligen Verbrauchswert gespeichert, d.h. die Messung der Verbraucher scheidet als Problemquelle aus. Wäre ein unverhältmismäßig hoher Verbrauch bei einem der Verbraucher feststellbar, müssten die Schlüssel &#039;&#039;&#039;pcurr&#039;&#039;&#039; und &#039;&#039;&#039;etotal&#039;&#039;&#039; im consumerXX-Attribut geprüft, sowie die Datenlieferung des Consumerdevices selbst geprüft werden.  &lt;br /&gt;
&lt;br /&gt;
Es wurde offensichtlich ein hoher Verbrauch von einem nicht registrierten Verbraucher verursacht oder durch die Übertragung eines Meßfehlers des Devices, angegeben im Attribut &#039;&#039;&#039;setupMeterDev&#039;&#039;&#039;, in das SolarForecast Device übertragen. Eine weitere Analyse ist an dieser Stelle nicht möglich.&lt;br /&gt;
&lt;br /&gt;
Zur Behebung des falschen Wertes kann ein Reset der gespeicherten Consumption der relevanten Stunde durchgeführt werden:&lt;br /&gt;
&lt;br /&gt;
 set &amp;lt;Name&amp;gt; reset consumptionHistory 07 21&lt;br /&gt;
&lt;br /&gt;
== Modulinterne Datenstrukturen ==&lt;br /&gt;
Das Modul speichert die verschiedenen historischen und aktuellen Daten wie PV-Prognose, Witterungsdaten (Regen, Bewölkung), Strahlungsdaten, Einspeisung, Bezug, reale Erzeugungsdaten und vieles mehr zur Laufzeit im Arbeitsspeicher. Dadurch ist die Verarbeitung dieser Daten im Vergleich zu Lesevorgängen aus dem Filesystem bzw. einer Datenbank sehr performant. Allerdings würden diese Daten bei einem FHEM Absturz bzw. bei einem regulären Shutdown verlorengehen.&lt;br /&gt;
&lt;br /&gt;
Deswegen werden die relevanten Daten regelmäßig in Dateien gesichert und bei Bedarf wiederhergestellt. Diese Dateien befinden sich im Verzeichnis &#039;&#039;&#039;../fhem/FHEM/FhemUtils&#039;&#039;&#039; und heißen &#039;&#039;&#039;.*_SolarForecast_.*&#039;&#039;&#039;. &lt;br /&gt;
&lt;br /&gt;
Die Daten werden in jedem Zyklus der Zentralschleife (CentralTask) gesammelt bzw. berechnet. Der Zyklus wird mit dem Attribut &#039;&#039;&#039;plantControl-&amp;gt;cycleInterval&#039;&#039;&#039; eingestellt.&lt;br /&gt;
&lt;br /&gt;
Zur Laufzeit können diese Daten über get-Kommandos abgerufen werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
get ... pvHistory [Tag]&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der pvHistory Datenspeicher enthält die vergangenen Kennwerte der letzten 31 Tage mit einer Genauigkeit von einer Stunde. Es werden die Prognosewerte für Erzeugung und Verbrauch, die realen Werte für Erzeugung und Verbrauch sowie ebenso die Laufzeiten jedes einzelnen Verbrauchers (Consumer) festgehalten. Für jeden Tag gibt es die Stunde &amp;quot;99&amp;quot; die eine Summierung der Stundenwerte des Tages bzw. Sonderschlüssel enthält.&lt;br /&gt;
&lt;br /&gt;
Bei Bedarf lässt sich mit dem Befehl &amp;quot;set ... reset pvHistory&amp;quot; der gesamte Inhalt des Speichers löschen (nicht empfohlen). Selektiver funktioniert die Datenlöschung mit &amp;quot;set ... reset pvHistory &amp;lt;Tag&amp;gt;&amp;quot; (Daten eines bestimmten Tages löschen) bzw. &amp;quot;set ... reset pvHistory &amp;lt;Tag&amp;gt; &amp;lt;Stunde&amp;gt;&amp;quot; (Daten der Stunde  eines bestimmten Tages löschen). Die letzten beiden Kommandos müssen über die Kommandozeile im Browser ausgeführt werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
get ... pvCircular &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dieser Ringspeicher enthält Daten der letzten 24 Stunden sowie fortwährend berechnete Werte wie Korrekturfaktoren der PV-Erzeugung und Prognosequalität.&lt;br /&gt;
Neue Stundenwerte des Tages überschreiben die gespeicherten Schüssel des vorangegangenen Tages sofern es sich nicht um fortwährend berechnete Werte handelt. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
get ... nextHours&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der Speicher enthält sämtliche Prognosewerte der kommenden Stunden beginnend mit der aktuellen Stunde.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
get ... radiationApiData&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Abhängig von der ausgewählten Strahlungsdaten API enthält dieser Speicher die von der API gelieferten Rohdaten sowie evtl. weitere für die Funktion benötigte Daten wie API-Keys, Response Messages, Anzahl der API Abruf und weitere.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
get ... valCurrent&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Diese Struktur wird nicht regelmäßig im Filesystem gespeichert und enthält immer aktuell gesammelte oder berechnete/gelieferte Daten wie z.B. die Autarkierate, letze Laufzeit der Zentralschleife (CentralTask), Zeit des Sonnenauf- und untergangs sowie weitere Betriebsdaten. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
get ... valConsumerMaster [XX]&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Zeigt die Daten der aktuell im SolarForecast Device registrierten Verbraucher. Mit der Nummer &#039;&#039;&#039;XX&#039;&#039;&#039; kann ein bestimmter Verbraucher angesprungen werden. Die Nummer korrespondiert mit dem entsprechenden &#039;&#039;&#039;consumerXX&#039;&#039;&#039; Attribut.&lt;br /&gt;
Neben anderen Betriebs- und Energiedaten werden insbesondere folgende Daten ermittelt, die für die zukünftige Planung der Verbraucherschaltzeiten relevant sind:&lt;br /&gt;
&lt;br /&gt;
* epieces - prognostizierte Energiescheiben pro Betriebsstunde &lt;br /&gt;
&lt;br /&gt;
Ausgehend von gespeicherten historischen Verbrauchsdaten des Verbrauchers wird der Energieverbrauch des Verbrauchers in seiner ersten Betriebsstunde prognostiziert, was insbesondere für die Planung der optimalen Startzeit des Verbrauchers in Bezug zu der prognostizierten PV Energie und den Verbrauchswerten weiterer vorhandener bzw. zu planender Verbraucher Berücksichtigung findet. Weitere Informationen zur Verbrauchersteuerung in diesem  [[#Verbrauchersteuerung_-_Registrieren_und_visualisieren_von_Verbrauchern|Abschnitt]].&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Schnittstelle für Modulautoren bzw. eigenen Code ===&lt;br /&gt;
&lt;br /&gt;
Die im vorherigen Artikel beschriebenen internen Datenstrukturen können durch externen Code abgefragt und ausgewertet werden.&lt;br /&gt;
Dazu stehen die folgenden Funktionen zur Verfügung. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
::* [FHEM::SolarForecast::]BatteryVal (&#039;&amp;lt;Name&amp;gt;&#039;, &#039;&amp;lt;Bn&amp;gt;&#039;, &#039;&amp;lt;Key&amp;gt;&#039;, &amp;lt;default&amp;gt;) &amp;lt;br&amp;gt; entspricht der Abfrage mit &amp;quot;get ... valBattery&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
::* [FHEM::SolarForecast::]CurrentVal (&#039;&amp;lt;Name&amp;gt;&#039;, &#039;&amp;lt;Key&amp;gt;&#039;, &amp;lt;default&amp;gt;) &amp;lt;br&amp;gt; entspricht der Abfrage mit &amp;quot;get ... valCurrent&amp;quot;. &lt;br /&gt;
&lt;br /&gt;
::* [FHEM::SolarForecast::]InverterVal (&#039;&amp;lt;Name&amp;gt;&#039;, &#039;&amp;lt;Nr&amp;gt;&#039;, &amp;lt;Key&amp;gt;&#039;, &amp;lt;default&amp;gt;) &amp;lt;br&amp;gt; entspricht der Abfrage mit &amp;quot;get ... valInverter&amp;quot;. &lt;br /&gt;
&lt;br /&gt;
::* [FHEM::SolarForecast::]ProducerVal (&#039;&amp;lt;Name&amp;gt;&#039;, &#039;&amp;lt;Nr&amp;gt;&#039;, &amp;lt;Key&amp;gt;&#039;, &amp;lt;default&amp;gt;) &amp;lt;br&amp;gt; entspricht der Abfrage mit &amp;quot;get ... valProducer&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
::* [FHEM::SolarForecast::]HistoryVal (&#039;&amp;lt;Name&amp;gt;&#039;, &#039;&amp;lt;Day&amp;gt;&#039;, &#039;&amp;lt;HoD&amp;gt;&#039;, &#039;&amp;lt;Key&amp;gt;&#039;, &amp;lt;default&amp;gt;) &amp;lt;br&amp;gt; entspricht der Abfrage mit &amp;quot;get ... pvHistory&amp;quot;. &lt;br /&gt;
&lt;br /&gt;
::* [FHEM::SolarForecast::]CircularVal (&#039;&amp;lt;Name&amp;gt;&#039;, &#039;&amp;lt;HoD&amp;gt;&#039;, &#039;&amp;lt;Key&amp;gt;&#039;, &amp;lt;default&amp;gt;) &amp;lt;br&amp;gt; entspricht der Abfrage mit &amp;quot;get ... pvCircular&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
::* [FHEM::SolarForecast::]NexthoursVal (&#039;&amp;lt;Name&amp;gt;&#039;, &amp;lt;nhr&amp;gt;, &#039;&amp;lt;Key&amp;gt;&#039;, &amp;lt;default&amp;gt;) &amp;lt;br&amp;gt; entspricht der Abfrage mit &amp;quot;get ... nextHours&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
::* [FHEM::SolarForecast::]ConsumerVal (&#039;&amp;lt;Name&amp;gt;&#039;, &#039;&amp;lt;Nr&amp;gt;&#039;, &#039;&amp;lt;Key&amp;gt;&#039;, &amp;lt;default&amp;gt;) &amp;lt;br&amp;gt; entspricht der Abfrage mit &amp;quot;get ... valConsumerMaster&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
::* [FHEM::SolarForecast::]RadiationAPIVal (&#039;&amp;lt;Name&amp;gt;&#039;, &#039;&amp;lt;String&amp;gt;&#039;, &#039;&amp;lt;DateTime&amp;gt;&#039;, &#039;&amp;lt;Key&amp;gt;&#039;, &amp;lt;default&amp;gt;) &amp;lt;br&amp;gt; entspricht der Abfrage mit &amp;quot;get ... radiationApiData&amp;quot;. &lt;br /&gt;
&lt;br /&gt;
::* [FHEM::SolarForecast::]WeatherAPIVal (&#039;&amp;lt;Name&amp;gt;&#039;, &#039;&amp;lt;APIname&amp;gt;&#039;, &#039;&amp;lt;TimeCode&amp;gt;&#039;, &#039;&amp;lt;Key&amp;gt;&#039;, &amp;lt;default&amp;gt;) &amp;lt;br&amp;gt; entspricht der Abfrage mit &amp;quot;get ... weatherApiData&amp;quot;. &lt;br /&gt;
&lt;br /&gt;
::* [FHEM::SolarForecast::]StatusAPIVal (&#039;&amp;lt;Name&amp;gt;&#039;, &#039;&amp;lt;APIname&amp;gt;&#039;, &#039;&amp;lt;QTag&amp;gt;&#039;, &#039;&amp;lt;Key&amp;gt;&#039;, &amp;lt;default&amp;gt;) &amp;lt;br&amp;gt; entspricht der Abfrage mit &amp;quot;get ... statusApiData&amp;quot;. &lt;br /&gt;
&lt;br /&gt;
::* [FHEM::SolarForecast::]StringVal (&#039;&amp;lt;Name&amp;gt;&#039;, &#039;&amp;lt;String&amp;gt;&#039;, &#039;&amp;lt;Key&amp;gt;&#039;, &amp;lt;default&amp;gt;) &amp;lt;br&amp;gt; entspricht der Abfrage mit &amp;quot;get ... valStrings&amp;quot;. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Die Angabe des Packages (FHEM::SolarForecast::) ist nur notwendig wenn die Funktionen von außerhalb des SolarForecast-Devices aufgerufen werden. Innerhalb des Devices (Code-Ausführung im Attribut &#039;&#039;ctrlUserExitFn&#039;&#039;) kann darauf verzichtet werden.&lt;br /&gt;
&lt;br /&gt;
Die Bedeutung der Platzhalter sind:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
     &amp;lt;Name&amp;gt;     - Name des SolarForecast Devices&lt;br /&gt;
     &amp;lt;Day&amp;gt;      - abzufragender Tag (01 - 31)&lt;br /&gt;
     &amp;lt;Bn&amp;gt;       - Batterie Nummer (01, 02, ...)&lt;br /&gt;
     &amp;lt;DateTime&amp;gt; - Startzeit der Form YYYY-MM-DD hh:00:00&lt;br /&gt;
     &amp;lt;TimeCode&amp;gt; - Zeitwert der Form fcX_XX (z.B. fc1_19)&lt;br /&gt;
     &amp;lt;APIname&amp;gt;  - Hauptname der API gemäß setupWeatherDev1 (z.B. OpenMeteo)&lt;br /&gt;
     &amp;lt;String&amp;gt;   - Name des PV-Modulstrings wie im Attribut &#039;setupInverterStrings&#039;&lt;br /&gt;
     &amp;lt;QTag&amp;gt;     - Question Tag, z.B. &#039;?All&#039;&lt;br /&gt;
     &amp;lt;Nr&amp;gt;       - laufende Nummer des Verbrauchers / Gerätes (01, 02, 03, 04,...)&lt;br /&gt;
     &amp;lt;HoD&amp;gt;      - abzufragende Stunde (01 - 24, 99)&lt;br /&gt;
     &amp;lt;nhr&amp;gt;      - nächste Stunde (NextHour00, NextHour01,...)&lt;br /&gt;
     &amp;lt;Key&amp;gt;      - der abzufragende Schlüssel &lt;br /&gt;
     &amp;lt;default&amp;gt;  - def default-Rückgabewert&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
==== Beispiel Auslesen der Batterieladestrategie und Anlegen eines Readings ====&lt;br /&gt;
&lt;br /&gt;
Als Praxisbeispiel soll die aktuelle gesetzte Ladestrategie für Batterie &amp;quot;01&amp;quot; ausgelesen und mit dem Wert ein Reading im SolarForecast Device angelegt werden. &amp;lt;br&amp;gt;&lt;br /&gt;
Dazu wird der folgende Code im Attribut &#039;&#039;ctrlUserExitFn&#039;&#039; hinterlegt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
  my $wert = NexthoursVal ($name, &#039;NextHour00&#039;, &#039;strategybat01&#039;, &#039;loadRelease&#039;);&lt;br /&gt;
  storeReading (&#039;Ladestrategie&#039;, $wert);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bei jedem Zyklus des SF-Devices wird der aktuelle Wert von &#039;&#039;strategybat01&#039;&#039; gelesen und im Reading &#039;&#039;Ladestrategie&#039;&#039; gespeichert. Die Paket-Definition &#039;&#039;FHEM::SolarForecast::&#039;&#039; braucht nicht verwendet werden, da der Aufruf im Attribut ctrlUserExitFn innerhalb des SolarForecast-Paketes erfolgt.&lt;br /&gt;
&lt;br /&gt;
Für andere Batterienummern ist der NexthoursVal-Schlüssel &#039;&#039;strategybat01&#039;&#039; entsprechend anzupassen.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Auswerten von internen Arrays ===&lt;br /&gt;
&lt;br /&gt;
Die meisten der internen Datenstrukturen liefern bei der Abfrage einen einfachen String oder numerischen Wert der ausgewertet werden kann. Es gibt allerdings auch intern gespeicherte Wertelisten. Werden die Listenstrukturen mit den oben beschriebenen Befehlen abgefragt, wird eine Referenz auf die Liste geliefert sofern sie vorhanden ist.&lt;br /&gt;
&lt;br /&gt;
Um auf die Werte einer solchen Liste zuzugreifen, ist am Beispiel des Arrays &#039;&#039;surplusslidereg&#039;&#039; aus dem Current-Speicher (get ... valCurrent) dargestellt: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
my $splref = CurrentVal ($name, &#039;surplusslidereg&#039;, &#039;.&#039;);&lt;br /&gt;
my $spser  = ref $splref eq &#039;ARRAY&#039; ? join &#039; &#039;, @{$splref} : undef;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Variable $spser enthält bei Vorhandensein der Liste einen String, der die Listenelemente von &#039;surplusslidereg&#039; durch Leerzeichen getrennt enthält.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Backup und Wiederherstellung der Moduldaten ==&lt;br /&gt;
Neben der Datenhaltung in den Attributen und Readings des SolarForecast Devices, erzeugt jedes SolarForecast Device Dateien im Verzeichnis  &#039;&#039;&#039;../fhem/FHEM/FhemUtils&#039;&#039;&#039; mit folgenden Inhalten:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
PVH_SolarForecast_&amp;lt;name&amp;gt;        - PV History&lt;br /&gt;
PVC_SolarForecast_&amp;lt;name&amp;gt;        - PV Circular&lt;br /&gt;
PVCfg_SolarForecast_&amp;lt;name&amp;gt;      - PV Anlagenkonfiguration&lt;br /&gt;
PVCsm_SolarForecast_&amp;lt;name&amp;gt;      - Consumer Status&lt;br /&gt;
ScApi_SolarForecast_&amp;lt;name&amp;gt;      - Strahlungswerte aus verwendeter API&lt;br /&gt;
StatApi_SolarForecast_&amp;lt;name&amp;gt;    - Statusdaten der verwendeten API&#039;s&lt;br /&gt;
WeatherApi_SolarForecast_&amp;lt;name&amp;gt; - Wetterdaten der gewählten Wetter-API (außer DWD-Device)&lt;br /&gt;
AItra_SolarForecast_&amp;lt;name&amp;gt;      - KI Entscheidungsquelle (trainierte Daten)&lt;br /&gt;
AIraw_SolarForecast_&amp;lt;name&amp;gt;      - KI Input Daten = Raw Trainigsdaten&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Je nach Ausprägung und eingesetzten Features des SolarForecast Devices sind alle oder nur ein Teil dieser Dateien vorhanden. Dabei ist &#039;&#039;&#039;&amp;lt;name&amp;gt;&#039;&#039;&#039; der Name des SolarForecast Devices.&lt;br /&gt;
&lt;br /&gt;
Bei einem Filebackup des FHEM-Servers ist darauf zu achten, dass diese Dateien in dem Backup enthalten sind. Der Inhalt der Dateien ist nicht statisch, sondern kann sich dynamisch aktualisieren. Um im Bedarfsfall einen aktuellen Datenstand zu haben, ist mindestens ein tägliches Backup dieser Dateien empfehlenswert. Eventuell auch öfter mit Hilfe einer Versionierung welche z.B. immer die letzten 3 Versionen aufbewahrt.&lt;br /&gt;
&lt;br /&gt;
Beim Start von FHEM werden bestimmte Dateien automatisch gelesen und in das SolarForecast Device importiert. &lt;br /&gt;
Der Vorgang ist mit verbose=3 im Log protokolliert:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
2023.10.02 09:57:07.967 3: SolCast6 - cached data &amp;quot;pvHistory&amp;quot; restored&lt;br /&gt;
2023.10.02 09:57:07.969 3: SolCast6 - cached data &amp;quot;pvCircular&amp;quot; restored&lt;br /&gt;
2023.10.02 09:57:07.970 3: SolCast6 - cached data &amp;quot;consumerMaster&amp;quot; restored&lt;br /&gt;
2023.10.02 09:57:07.970 3: SolCast6 - cached data &amp;quot;radiationApiData&amp;quot; restored&lt;br /&gt;
2023.10.02 09:57:07.970 3: SolCast6 - cached data &amp;quot;statusApiData&amp;quot; restored&lt;br /&gt;
2023.10.02 09:57:07.970 3: SolCast6 - cached data &amp;quot;weatherApiData&amp;quot; restored&lt;br /&gt;
2023.10.02 09:57:07.974 3: SolCast6 - cached data &amp;quot;aiTrainedData&amp;quot; restored&lt;br /&gt;
2023.10.02 09:57:07.976 3: SolCast6 - cached data &amp;quot;aiRawData&amp;quot; restored&lt;br /&gt;
&amp;lt;/pre&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Bestimmte Betriebsdaten, wie z.B. die witterungsabhängigen Korrekturfaktoren (circular) oder die KI-Daten (aitrained/airaw), bauen sich über die Zeit immer granularer auf und stellen einen wertvollen Datenbestand dar.&lt;br /&gt;
&lt;br /&gt;
Wird das SolarForecast Device gelöscht und anschließend wieder neu mit dem gleichen Namen definiert, können die bisher verfügbaren Daten sehr einfach wiederhergestellt werden:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* 1. definieren des neuen SolarForecast Devices, d.h. ein einfaches &amp;quot;define &amp;lt;Name&amp;gt; SolarForecast&amp;quot;, und sichern der FHEM Konfiguration (der Konfigurationsdialog muß nicht durchgeführt werden)&lt;br /&gt;
* 2. FHEM stoppen&lt;br /&gt;
* 3. wiederherstellen der oben beschriebenen Dateien aus einem Backup in das Verzeichnis ../fhem/FHEM/FhemUtils (Überschreiben evtl. vorhandener Dateien)&lt;br /&gt;
* 4. Anpassen der Dateieigentümer und Berechtigungen: chown fhem:dialout /opt/fhem/FHEM/FhemUtils/*,  chmod 774 /opt/fhem/FHEM/FhemUtils/*&lt;br /&gt;
* 5. starten von FHEM (bestimmte Daten werden automatisch importiert)&lt;br /&gt;
* 6. mit dem Befehl &amp;quot;set &amp;lt;name&amp;gt; plantConfiguration restore&amp;quot; die Anlagenkonfiguration wiederherstellen&lt;br /&gt;
* 7. FHEM Konfiguration sichern und restarten&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Hinweis:&#039;&#039;&#039; &amp;lt;br&amp;gt;&lt;br /&gt;
Das zuvor beschriebene Verfahren kann auch angewendet werden um das SolarForcast Device von einer FHEM Installation in eine andere Installation umzuziehen. In diesem Fall ist aber besonders darauf zu achten, vorab alle in den SF-Attributen referenzierten Devices wie Inverter, Meter oder DWD-Devices mit den zutreffenden Namen in der neuen Installation anzulegen.&lt;br /&gt;
  &lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
Soll das neue SolarForecast Device mit einem anderen Namen oder weitere SolarForecast Devices erstellt werden, welche die Betriebsdaten des primären Devices verwenden sollen, ist wie folgt vorzugehen:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* 1. definieren des neuen SolarForecast Devices, d.h. ein einfaches &amp;quot;define &amp;lt;Name&amp;gt; SolarForecast&amp;quot;, und sichern der FHEM Konfiguration (der Konfigurationsdialog muß nicht durchgeführt werden)&lt;br /&gt;
* 2. FHEM stoppen&lt;br /&gt;
* 3. wiederherstellen der oben beschriebenen Dateien aus einem Backup in das Verzeichnis ../fhem/FHEM/FhemUtils (Überschreiben evtl. vorhandener Dateien)&lt;br /&gt;
* 4. umbenenen der Dateien, wobei &amp;lt;name&amp;gt; durch den Namen des neuen SolarForecast Devices ersetzt wird&lt;br /&gt;
* 5. Anpassen der Dateieigentümer und Berechtigungen: chown fhem:dialout /opt/fhem/FHEM/FhemUtils/*,  chmod 774 /opt/fhem/FHEM/FhemUtils/*&lt;br /&gt;
* 6. starten von FHEM (bestimmte Daten werden automatisch importiert)&lt;br /&gt;
* 7. mit dem Befehl &amp;quot;set &amp;lt;name&amp;gt; plantConfiguration restore&amp;quot; die Anlagenkonfiguration wiederherstellen&lt;br /&gt;
* 8. FHEM Konfiguration sichern und restarten&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Wenn sich die SolarForecast Devices hinsichtlich ihres Models oder der integrierten Consumer unterscheiden, sind die Dateien &amp;quot;PVCsm_SolarForecast_&amp;lt;name&amp;gt; &amp;quot; , &amp;quot;ScApi_SolarForecast_&amp;lt;name&amp;gt;&amp;quot; von dem Verfahren auszuschließen.&lt;br /&gt;
&lt;br /&gt;
== Batterieintegration und -steuerung ==&lt;br /&gt;
&lt;br /&gt;
=== Wie eine Batterie eingebunden wird ===&lt;br /&gt;
Wie immer muß die Batterieanlage zunächst mit einem für die Batterie spezifischen FHEM-Device in FHEM definiert sein.&lt;br /&gt;
In diesem Device werden Readings erwartet die folgende Werte bereitstellen:&lt;br /&gt;
&lt;br /&gt;
::* ein Reading welches die aktuelle Batterieladeleistung liefert&lt;br /&gt;
&lt;br /&gt;
::* ein Reading welches die aktuelle Batterieentladeleistung liefert&lt;br /&gt;
&lt;br /&gt;
::* ein Reading welches die installierte Batteriekapazität liefert (&#039;&#039;&#039;Hinweis:&#039;&#039;&#039; dieser Schlüssel ist für die Aktivierung des Batterie SOC-Managements und die grafische Anzeige des SOC wesentlich!)&lt;br /&gt;
&lt;br /&gt;
::* ein Reading welches die bis dato vorliegende Lade-Energiemenge als fortlaufenden Zähler liefert (optional)&lt;br /&gt;
&lt;br /&gt;
::* ein Reading welches die bis dato vorliegende Entlade-Energiemenge als fortlaufenden Zähler liefert (optional)&lt;br /&gt;
&lt;br /&gt;
::* Reading welches den aktuellen Ladezustand (SOC in Prozent) liefert (optional)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Die Batterie, bzw. das Batteriedevice wird mit dem Attribut &#039;&#039;&#039;setupBatteryDev&#039;&#039;&#039; im SolarForecast Device registriert. Die Syntax des Attributes ist in  der Online-Hilfe zum Attribut umfassend erläutert. &lt;br /&gt;
Auch wenn manche Readings optional sind, wird empfohlen alle Werte bereitzustellen um alle Batterie-Funktionen im Modul nutzen zu können. Durch die Weiterentwicklung des Moduls können optionale Readings später auch obligatorisch werden.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Visualisierung der Batterie in der Balkengrafik ====&lt;br /&gt;
&lt;br /&gt;
Das Attribut &#039;&#039;&#039;setupBatteryDevXX&#039;&#039;&#039; verfügt über den Schlüssel &#039;&#039;&#039;show&#039;&#039;&#039;, mit dem die Einblendung des bzw. der Batteriesymbole in den Ebenen der Balkengrafik gesteuert werden kann:&lt;br /&gt;
[[Datei:Screenshot 2025-02-23 192002.png|right|thumb|400px|Einblendung der Batterieleiste und Darstellung des Batterie-SoC als Balkencontent in der zweiten Ebene]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* 0 - keine Anzeige des Gerätes (default)&lt;br /&gt;
* 1 - Anzeige des Gerätes in der Balkengrafik Ebene 1&lt;br /&gt;
* 2 - Anzeige des Gerätes in der Balkengrafik Ebene 2 &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Die Symbole und die Farbgebung der Batterie(n) können mittels des Schlüssels &#039;&#039;&#039;icon&#039;&#039;&#039; verändert werden. Die Online-Hilfe gibt weitere Informationen zur Ausgestaltung. &lt;br /&gt;
&lt;br /&gt;
In der Batteriedarstellung wird das Symbol der aktuellen Stunde hervorgehoben. Ein Mouse-Over zeigt wesentliche Betriebszustände der Batterie. Die Batteriesymbole vergangener Stunden zeigen mit einem Mouse-Over die erreichten SoC (State of Charge) Stände. Die kommenden Stunden visualisieren die folgenden Prognosen:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* der prognostizierte SoC unter Berücksichtigung der PV-Prognose und der [[#Aktivierung_des_Batterie_SOC-Managements|Batteriesteuerung]] sofern sie im Modul aktiviert ist.&lt;br /&gt;
* eine Freigabe bzw. Empfehlung der Batterieladung zur [[#Unterstützung_eines_netzdienlichen_Verhaltens|Unterstützung eines netzdienlichen Verhaltens]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Neben der Einblendung der Batterieleiste über der Balkengrafik selbst können bestimmte Batteriekennwerte als Balkeninhalt dargestellt werden. Die Auswahl erfolgt über das Attribut &#039;&#039;&#039;graphicBeamXContent&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== Aktivierung des Batterie SOC- und Lade-Managements ===&lt;br /&gt;
Das integrierte SOC- und Lade-Management verfolgt insbesondere diese Ziele: &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* den Ladezustand der Batterie(n) zu optmimieren, was besonders in den Monaten mit geringerer PV-Erzeugung einen wertvollen Beitrag zur Erhöhung der Autarkierate und Batteriepflege leisten kann&lt;br /&gt;
 &lt;br /&gt;
* eine PV-Prognose und Verbrauch optimierte Beladungssteuerung, die in den Monaten mit hoher PV-Erzeugung hilft eine evtl. Abregelung der Anlage zu verhindern, die Batterie ausgewogen über den Tag zu laden und ein netzdienliches Verhalten zu unterstützen&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Diese Aspekte der Batteriesteuerung werden in den folgenden Abschnitten beschrieben. Sind mehrere Batterien installiert, kann die Steuerung getrennt für jede einzelne Batterie aktiviert und eingestellt werden. Dabei ist zu beachten, dass vom Modul nur Signale und Readings bereitgestellt werden, die durch eine entsprechende Routine ausgewertet werden können um spezifische Befehle zur Umsetzung der Steuerung an das Batteriesystem zu senden. &lt;br /&gt;
 &lt;br /&gt;
Für eine Aktivierung des Managements muß zunächst das Batterie-Device wie im vorigen Abschnitt im SolarForecast Device registriert werden.&lt;br /&gt;
Das Attribut &#039;&#039;&#039;ctrlBatSocManagementXX&#039;&#039;&#039; aktiviert die im Modul integrierte SOC- und Lade-Management Routine: &lt;br /&gt;
&lt;br /&gt;
 attr &amp;lt;Device&amp;gt; ctrlBatSocManagementXX lowSoc=&amp;lt;Wert&amp;gt; upSoC=&amp;lt;Wert&amp;gt; [maxSoC=&amp;lt;Wert&amp;gt;] [careCycle=&amp;lt;Wert&amp;gt;] [loadAbort=&amp;lt;SoC1&amp;gt;:&amp;lt;MinPwr&amp;gt;:&amp;lt;SoC2&amp;gt;] [safetyMargin=&amp;lt;Wert&amp;gt;[:&amp;lt;Wert&amp;gt;]] ...&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Die einzelnen Schlüssel werden der Kalkulationsroutine als Steuerungsparameter übergeben und bedeuten:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
;lowSoc:unterer Mindest-SoC, die Batterie wird nicht tiefer als dieser Wert entladen (muß &amp;gt; 0 sein). Dieser Wert kann zum Beispiel dazu dienen, stets eine Mindestenergiemenge für den Fall eines Stromausfalls im öffentlichen Netz in den Batterien vorzuhalten.&lt;br /&gt;
&lt;br /&gt;
;upSoC:oberer Mindest-SoC, Der übliche Wert des optimalen SoC bewegt sich in Perioden mit hohen PV-Überschuß tendenziell zwischen &#039;lowSoC&#039; und &#039;upSoC&#039;, in Perioden mit geringer PV-Ausbeute bzw. geringem PV Energieüberschuß bewegt sich der optimale SoC tendenziell zwischen &#039;upSoC&#039; und &#039;maxSoC&#039;. Die Einstellung von upSoC=50 für ein ausgewogenes Verhalten kann als guter Startwert dienen.&lt;br /&gt;
&lt;br /&gt;
;maxSoC:maximaler Mindest-SoC, d.h. der SoC Wert der mindestens im Abstand von &#039;careCycle&#039; Tagen erreicht werden muß um den Ladungsausgleich im Speicherverbund auszuführen. Die Angabe ist optional (muß &amp;lt;= 100 sein, default: 95)&lt;br /&gt;
&lt;br /&gt;
;stepSoC: Optionale Schrittweite zur optimalen SoC-Berechnung (Battery_OptimumTargetSoC_XX) in %. Mit der Angabe &#039;stepSoC=0&#039; wird das SoC-Management deaktiviert und Battery_OptimumTargetSoC_XX auf den Wert &#039;lowSoC&#039; gesetzt. &#039;&#039;&#039;Hinweis:&#039;&#039;&#039; Die Beziehung &#039;careCycle * stepSoC = 100&#039; sollte eingehalten werden! Wert: 0..5, default: 5 &lt;br /&gt;
&lt;br /&gt;
;careCycle:maximaler Abstand in Tagen, der zwischen zwei Ladungszuständen von mindestens &#039;maxSoC&#039; auftreten darf. Die Angabe ist optional. (default: 20)&lt;br /&gt;
&lt;br /&gt;
;lcSlot: Es wird ein tägliches Zeitfenster festgelegt, in dem die Ladesteuerung des Moduls für diese Batterie aktiv sein soll. Außerhalb des Zeitfensters wird die Batterieladung mit voller Leistung freigegeben. Das SoC-Management der Batterie ist davon nicht betroffen. Wert: &amp;lt;hh:mm&amp;gt;-&amp;lt;hh:mm&amp;gt;, default: ganztägig&lt;br /&gt;
&lt;br /&gt;
;loadAbort: definiert eine vom Anwender gewünnschte Bedingung für einen generellen Ladeabbruch und Wiederfreigabe der Lademöglichkeit. Die Abbruchbedingung ist erfüllt, wenn der angegebene SoC1 (%) erreicht bzw. überschritten ist &#039;&#039;&#039;UND&#039;&#039;&#039; die angegebene Ladeleistung &amp;lt;MinPwr&amp;gt; (W) unterschritten wurde -&amp;gt; Reading Battery_ChargeAbort_XX=1. Fällt der aktuelle SoC wieder unter den SoC2, wird Battery_ChargeAbort_XX=0 gesetzt. Ist SoC2 nicht angegeben, gilt SoC2=SoC1.  &lt;br /&gt;
&lt;br /&gt;
;loadStrategy: Abhängig von der gewählten Ladestrategie wird die Prognose der Batterieladung und ggf. die Generierung der Steuerreadings beeinflusst. Die Angabe ist optional. Wert: loadRelease | optPower | smartPower, default: loadRelease&lt;br /&gt;
	&lt;br /&gt;
;loadTarget: Optionaler Ziel-SoC in % für die Berechnung der Ladefreigabe bzw. der optimalen Ladeleistung. Der Zielwert ist eine kalkulatorische Rechengröße. Der reale SoC kann situativ in Grenzen über- oder unterschritten werden. Der höhere Wert aus Reading Battery_OptimumTargetSoC_XX und &#039;loadTarget&#039; hat für die Berechnung Vorrang. Wert: 0..100, default: 100 &lt;br /&gt;
&lt;br /&gt;
;safetyMargin: Bei der Berechnung der Ladefreigabe und optimierten Ladeleistung werden Sicherheitszuschläge auf den prognostizierten Ladungsbedarf berücksichtigt. Abweichend vom Default können mit diesem Parameter individuelle Sicherheitszuschläge getrennt für die Berechnung der Ladefreigabe und optimierten Ladeleistung angegeben werden. Der erste Wert ist der Zuschlag bei der Berechnung der Ladefreigabe, der zweite Wert der Zuschlag bei der Berechnung der optimierten Ladeleistung. Beide Angaben sind Prozentwerte von 0..100.&lt;br /&gt;
&lt;br /&gt;
;weightOwnUse: Optionale Gewichtung der stündlichen Verbrauchsprognose als zusätzlich verwendbaren Anteil zur Batterieladung in %. Technisch wird der verfügbare PV-Überschuß zur Berechnung der optimierten Ladeleistung erhöht indem der kalkulierte Verbrauch um den angegebenen Prozentsatz gesenkt wird. Wert: 0..100 default: 0 &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Alle SoC-Werte sind ganze Zahlen in %. Dabei gilt: &lt;br /&gt;
&lt;br /&gt;
 lowSoc &amp;lt; upSoC &amp;lt; maxSoC&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
====  Erläuterung der Batterie-Steuerungsreadings ==== &lt;br /&gt;
&lt;br /&gt;
Für die Steuerung des Batteriesystems gibt es im SolarForecast einen Satz an Readings, die unterschiedliche Zustände signalisieren und für entsprechende Steuerungsaufgaben bezüglich der Batteriesysteme eingesetzt werden können. In den nachfolgenden Abschnitten werden diese Zusammenhänge noch dataillierter beschrieben.&lt;br /&gt;
&lt;br /&gt;
;Hinweis: SolarForecast stellt lediglich Steuerungsinformationen zur Verfügung. Die Umsetzung des Eingriffs in das Batteriesystem erfolgt durch den Anwender selbst über entsprechende Skripte. In den thematischen Abschnitten werden Beispiele für solche Steuerungsskripte vorgestellt.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Alle erstellten Readings gelten für ein Batteriesystem, es können mehrere Batteriesysteme in SolarForecast integriert sein. Die Endung &#039;&#039;XX&#039;&#039; der Readings beschreibt die Nummer des Batteriesystems und korrespondiert mit der Endung des entsprechenden Attributes &#039;&#039;ctrlBatSocManagementXX&#039;&#039; bzw. &#039;&#039;setupBatteryDevXX&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
;Battery_OptimumTargetSoC_XX: Mit den im Attribut &#039;&#039;ctrlBatSocManagementXX&#039;&#039; hinterlegten Parametern unter Berücksichtigung von PV- und Verbrauchsprognosen wird ein optimaler SOC-Wert errechnet. Dieser Wert dient zur Einstellung des unteren SOC des Batteriesystems.&lt;br /&gt;
&lt;br /&gt;
;Battery_ChargeRequest_XX: Dieses Reading signalisiert mit 1 eine Anforderung zur Zwangsladung der Batterie um die Batterie vor Schäden zu schützen (wenn der SOC unter ctrlBatSocManagementXX-&amp;gt;lowSoc gefallen ist) oder wenn der aktuelle SOC unter den berechneten Mindest-SOC gefallen ist.&lt;br /&gt;
&lt;br /&gt;
;Battery_ChargeUnrestricted_XX: Dieses Reading kann den Wert &#039;&#039;&#039;0&#039;&#039;&#039; oder &#039;&#039;&#039;1&#039;&#039;&#039; einnehmen. Ist der Wert 0, sollte die Batterie nur geladen werden, wenn der im Attribut &#039;&#039;plantControl-&amp;gt;feedinPowerLimit&#039;&#039; definierte Wert überschritten wird. Ist der Wert 1, sollte die Batterie mit der vollen zur Verfügung stehenden Ladeleistung angesteuert werden, um wie prognostiziert am Ende des Sonnentages die volle bzw. maximal mögliche Ladung zu erhalten. Der Fokus der Steuerung mit diesem Reading ist es, die Speicherkapazität der Batterie optimal zur Verhinderung der Überschreitung von Einspeiselimits einzusetzen und dennoch einen maximal möglichen SoC am Tagesende zu erreichen. (siehe das beschriebene [[SolarForecast_-_Solare_Prognose_(PV_Erzeugung)_und_Verbrauchersteuerung#PV-Prognose_und_Verbrauch_optimierte_Beladungssteuerung_unter_Berücksichtigung_einer_Wirkleistungsbegrenzung|Nutzungsbeispiel]])&lt;br /&gt;
&lt;br /&gt;
;Battery_ChargeOptTargetPower_XX: Dieses Reading enthält einen Richtwert für die optimierte Ladeleistung für jede Batterie. Die Aufladung wird prognosegeführt über den gesamten Tag verteilt, wobei der Fokus auf eine kontinuierliche, aber möglichst niederige Ladeleistung gelegt wird. (siehe diesen [[SolarForecast_-_Solare_Prognose_(PV_Erzeugung)_und_Verbrauchersteuerung#leistungsoptimierte_Beladungssteuerung_mit_dem_Fokus_geringe_Verlustleistung_und_Verschleiß|Abschnitt]])  &lt;br /&gt;
&lt;br /&gt;
;Battery_ChargeAbort_XX: Im Attribut &#039;&#039;ctrlBatSocManagementXX&#039;&#039; kann mit dem Schlüssel &#039;&#039;loadAbort&#039;&#039; eine Bedingung definiert werden, bei deren Eintritt die Ladung der Batterie abgebrochen werden soll. In diesem Fall wird &#039;&#039;Battery_ChargeAbort_XX=1&#039;&#039; gesetzt. Mit einem entsprechenden Befehl kann der Anwender das Batteriesystem anweisen den Ladevorgang abzubrechen. &lt;br /&gt;
&lt;br /&gt;
;Battery_TargetAchievable_XX: Diese Reading enthält einen Boolean Wert 0|1 und signalisiert ob der im Attribut &#039;&#039;&#039;ctrlBatSocManagementXX-&amp;gt;loadTarget&#039;&#039;&#039; gesetzte Ziel-SoC lt. Prognose am aktuellen Tag tatsächlich erreicht werden kann.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Nutzung des Batterie SOC-Management ====&lt;br /&gt;
&lt;br /&gt;
Oftmals wird durch die Anlagenhersteller ein hoher statischer SOC eingestellt bzw. empfohlen, der eine längere Verharrungszeit auf einem tiefen SOC-Wert verhindern soll. Allerdings wechseln sich in auch in den Herbst-, Frühlings- und Wintermonaten Zeiten mit wenig PV-Erzeugung mit Phasen stärkerer Solarstrahlung bei klarem Himmel und Sonnenschein ab.&lt;br /&gt;
&lt;br /&gt;
Ein statischer SOC würde dazu führen, dass in den erwähnten Phasen zuviel Energie in das öffentliche Netz eingespeist wird. Dies soll verhindert werden da die wertvolle Energie besser im eigenen Netz gespeichert und in Dunkelphasen verwendet werden kann. Andererseits soll die Batterie nicht zu lange auf einem tiefen SOC verbleiben und außerdem in gewissen Abständen auf einen so hohen SOC geladen werden der den internen Zellausgleich ermöglicht.&lt;br /&gt;
&lt;br /&gt;
Die SOC Kalkulationsroutine des Moduls errechnet unter Berücksichtigung des prognostizierten PV-Ertrages der kommenden Tage, des aktuellen SOC und der im Attribut &#039;&#039;&#039;ctrlBatSocManagementXX&#039;&#039;&#039; (nachfolgend beschrieben) angegebenen Steuerparameter den Vorschlag für eine optimale SOC-Einstellung. Dabei steht &#039;XX&#039; für die Nummer der Batterie, welche mit dem Attribut setupBatteryDevXX korrespondiert.&lt;br /&gt;
&lt;br /&gt;
Durch das Modul selbst findet keine Steuerung der Batterie statt. Es stellt entsprechende Readings zur Verfügung:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
::* das Reading &#039;&#039;&#039;Battery_OptimumTargetSoC_XX&#039;&#039;&#039; enthält den vom Modul berechneten optimalen SoC.&lt;br /&gt;
&lt;br /&gt;
::* das Reading &#039;&#039;&#039;Battery_ChargeRequest_XX&#039;&#039;&#039; wird auf &#039;1&#039; gesetzt, wenn der aktuelle SoC unter den optimalen SoC gefallen ist. In diesem Fall sollte die Batterie, unter Umständen mit Netzstrom, zwangsgeladen werden.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Die Readings können zur Steuerung des SoC (State of Charge) sowie zur Steuerung des verwendeten Ladestroms der Batterie verwendet werden.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Die Ermittlung des optimalen SOC erfolgt nach folgender Logik:&lt;br /&gt;
&lt;br /&gt;
# 	Ausgehend von &#039;lowSoc&#039; wird der Mindest-SoC kurz vor Sonnenuntergang um 5% inkrementiert sofern am laufenden Tag &#039;maxSoC&#039; nicht erreicht wurde und die PV-Prognose keinen hinreichenden Ertrag des kommenden Tages vorhersagt. &amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
# 	Wird &#039;maxSoC&#039; (wieder) erreicht, wird Mindest-SoC um 5%, aber nicht tiefer als &#039;lowSoc&#039;, verringert. Ist der berechnete Mindest-SoC größer als &#039;upSoc&#039;, wird der Mindest-SoC auf &#039;upSoc&#039; gesetzt. &amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
#   Mindest-SoC wird soweit verringert, dass die prognostizierte PV Energie des aktuellen bzw. des folgenden Tages von der Batterie aufgenommen werden kann. Mindest-SoC wird typisch auf &#039;upSoc&#039; und nicht tiefer als &#039;lowSoc&#039; verringert.  &amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
# 	Das Modul erfasst den letzten Zeitpunkt am &#039;maxSoC&#039;-Level, um eine Ladung auf &#039;maxSoC&#039; mindestens alle &#039;careCycle&#039; Tage zu realisieren. Zu diesem Zweck wird der optimierte SoC in Abhängigkeit der Resttage bis zum nächsten &#039;careCycle&#039; Zeitpunkt derart verändert, dass durch eine tägliche 5% SoC-Steigerung &#039;maxSoC&#039; am &#039;careCycle&#039; Zeitpunkt rechnerisch erreicht wird. Wird zwischenzeitlich &#039;maxSoC&#039; erreicht, beginnt der &#039;careCycle&#039; Zeitraum erneut. &amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
# 	Im fünften Schritt werden die Grenzen &#039;lowSoc&#039; und &#039;upSoc&#039; berücksichtigt. &amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
# 	Im letzten Schritt erfolgt Generierung des Readings &#039;&#039;&#039;Battery_ChargeRequest_XX&#039;&#039;&#039; zur Signalisierung einer eventuell empfohlenen Zwangsladung auf den berechneten Min-SoC. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Bei sehr wenig Erzeugungungsprognose, d.h. wenn sich der berechnete Min-SoC oberhalb von &#039;upSoC&#039; bewegt, wird &#039;upSoC&#039; eingestellt. Liegt der berechnete Min-SoC unterhalb von &#039;upSoC&#039;, ist jedoch größer als &#039;lowSoC&#039;, wird der berechnete SOC im Reading &#039;&#039;&#039;Battery_OptimumTargetSoC_XX&#039;&#039;&#039; eingestellt. Somit eignet sich die Angabe im Schlüssel &#039;upSoC&#039; um in Zeiten geringer Solarenergieerzeugung die Batterie für eine Notstromerzeugung auf mindestens diesem Wert zu halten. Sollte die PV-Prognose kurzfristig einen größeren Ertrag vorhersagen als die Batterie durch die Einhaltung von &#039;upSoC&#039; aufnehmen kann, wird der Min-SoC unter &#039;upSoC&#039; abgesenkt um möglichst allen PV-Überschuß in der Batterie zu speichern und somit dem Eigenverbrauch zuzuführen.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Der berechnete Mindest-SoC ist immer nur ein unterer Grenzwert. Die Batterie kann durch Ladevorgänge natürlich jederzeit einen höheren als den im Reading &#039;&#039;&#039;Battery_OptimumTargetSoC_XX&#039;&#039;&#039; dargestellten optimalen SoC haben. Der optimale SoC sollte nicht unterschritten werden um die oben genannten Zusammenhänge abbilden zu können.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Die dynamische SOC Steuerung mit Umsetzungsbeispiel =====&lt;br /&gt;
Das Modul bietet eine Unterstützung zur automatisierten Einstellung eines optimalen SoC Wertes.&lt;br /&gt;
&lt;br /&gt;
Ziel ist es, den Minimum SoC in Abhängigkeit der zu erwartenden Solarerträge des aktuellen und des folgenden Tages so einzustellen, dass der prognostizierte Solarertrag von der Batterie aufgenommen werden kann. Die Einspeisung in das öffentliche Netz soll minimiert werden.&lt;br /&gt;
&lt;br /&gt;
Weiterhin soll ein unterer SoC nicht unterschritten, sowie ein oberer Minimum SoC im Normalfall nicht überschritten werden. Der obere Grenzwert stellt sicher, dass die Batterie mindestens bis zu diesem Wert wieder entladen werden kann. Dadurch bleibt innerhalb der Batterie immer genügend Kapazität verfügbar um unvorhergesehene PV Energie aufnehmen zu können.&lt;br /&gt;
&lt;br /&gt;
Aktiviert wird die Unterstützung der Batteriesteuerung durch das Setzen des Attributes &#039;&#039;&#039;ctrlBatSocManagementXX&#039;&#039;&#039; z.B. mit diesen Angaben:&lt;br /&gt;
&lt;br /&gt;
 ctrlBatSocManagementXX lowSoc=x upSoC=x maxSoC=x careCycle=x &lt;br /&gt;
&lt;br /&gt;
so zum Beispiel:&lt;br /&gt;
&lt;br /&gt;
 ctrlBatSocManagement01 lowSoc=10 upSoC=50 maxSoC=98 careCycle=20&lt;br /&gt;
&lt;br /&gt;
Dabei steht &#039;XX&#039; für die Nummer der Batterie, welche mit dem Attribut setupBatteryDevXX korrespondiert. &lt;br /&gt;
Ist dieses Attribut gesetzt, werden Steuerungs-Readings erstellt, die der Nutzer auswerten und seine Batterieanlage z.B. via notify oder DOIF steuern kann. &amp;lt;bR&amp;gt;&lt;br /&gt;
Das Reading &#039;&#039;&#039;Battery_OptimumTargetSoC_XX&#039;&#039;&#039; enthält den vom Modul berechneten optimalen Mindest-SoC.&lt;br /&gt;
Das Reading &#039;&#039;&#039;Battery_ChargeRequest_XX&#039;&#039;&#039; wird auf &#039;1&#039; gesetzt, wenn der aktuelle SOC unter den optimalen SOC bzw. minimalen SOC gefallen ist.&lt;br /&gt;
&lt;br /&gt;
Die Schlüssel &#039;&#039;&#039;lowSoc&#039;&#039;&#039; und &#039;&#039;&#039;upSoC&#039;&#039;&#039; sind die unteren und oberen Grenzen, zwischen denen sich der Minimum SoC im normalen Betrieb bewegen soll.&lt;br /&gt;
Der Schlüssel &#039;&#039;&#039;maxSoC&#039;&#039;&#039; stellt eine Ladungsgrenze dar, die mindestens alle X (careCycle) Tage erreicht werden soll. Zu diesem Zweck errechnet das Modul die Anzahl der nötigen Erhöhungen der Minimum Batterieladung von 5% pro Tag um ausgehend vom aktuellen SoC-Level. Der resultierende Minimum SoC Wert wird entsprechend angepasst. Wurde der einstellte maxSoC Level erreicht, startet der Wartungszyklus (careCycle) erneut. &amp;lt;br&amp;gt;&lt;br /&gt;
Der maxSoC in Verbindung mit dem durch &#039;&#039;&#039;careCycle&#039;&#039;&#039; definierten Zyklus soll sicherstellen, dass der Batterieverbund in regelmäßigen Abständen einen Ladungsausgleich vollziehen kann. &lt;br /&gt;
&lt;br /&gt;
Durch die Modullogik erfolgt eine 5 protentige Anhebung des Minimum SoC wenn am Vortag maxSoC nicht erreicht wurde. Die Anhebung erfolgt aber nicht über upSoC. upSoC ist die obere Grenze des Minimum SoC. Wird am Vortag mindestens die maxSoC Ladung erreicht, verringert sich der Minimum SoC wieder schrittweise um 5%, aber nicht tiefer als lowSoc.&lt;br /&gt;
&lt;br /&gt;
Ergänzt wird die Logik noch um die PV Vorhersage. Dabei wird die obige Minimum SoC Berechnung um die Erwartung der PV Erwartung am aktuellen und folgenden Tag korrigiert. Das bedeutet, dass der Minimum SoC z.B. von heute 50% auf 10% abgesenkt und dadurch die gespeicherte Energie dem Haushalt zugeführt wird, wenn am heutigen oder kommenden Tag eine entsprechende PV Energie zu erwarten ist um die Batterie wieder vollständig zu zuladen.&lt;br /&gt;
Dadurch ist immer genügend &amp;quot;Freiplatz&amp;quot; in der Batterie und andererseits hat die Batterie in langen Dunkelphasen eine Reserve für einen eventuellen Stromausfall (falls der abgesichert werden soll) und wird vor einem dauerhaften tiefen SoC geschützt.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
===== Umsetzungsbeispiel =====&lt;br /&gt;
&lt;br /&gt;
Wie das Reading &#039;&#039;&#039;Battery_OptimumTargetSoC_XX&#039;&#039;&#039; genutzt werden kann, wird am Beispiel einer Victron Batterieanlage gezeigt. Die Anlage wird durch Cerbo GX Gerät gesteuert. Der Cerbo GX ist per MQTT2 in FHEM eingebunden. Umgesetzt ist die Steuerung der Batterie &#039;01&#039;, d.h. die Angabe &#039;XX&#039; ist durch &#039;01&#039; ersetzt.&lt;br /&gt;
&lt;br /&gt;
Die folgende kleine Logik ist in einem &#039;&#039;&#039;99_mySolarForecastUtils.pm&#039;&#039;&#039; File eingebaut und wird regelmäßig mit&lt;br /&gt;
&lt;br /&gt;
 batSocChargeMgmnt (&#039;&amp;lt;Name SolarForecast Device&amp;gt;&#039;);&lt;br /&gt;
&lt;br /&gt;
aufgerufen. Der Aufruf erfolgt durch Angabe der Routine im Attribut &#039;&#039;&#039;ctrlUserExitFn&#039;&#039;&#039; des SolarForecast Devices:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
{  &lt;br /&gt;
  # Hinweis: die Zeichen &#039;::&#039; vor der Routine sind durch das SolarForecast Package bedingt&lt;br /&gt;
  ::batSocChargeMgmnt ($name);&lt;br /&gt;
  return;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Routine wird dadurch automatisch am Ende jedes Zyklus (siehe Attribut plantControl-&amp;gt;cycleInterval) ausgeführt.&lt;br /&gt;
Um die SOC Managementfunktion nach Bedarf ein- bzw. ausschalten zu können wird zunächst im SolarForecast Device ein userattr zur Steuerung der SOC-Logik angelegt:&lt;br /&gt;
&lt;br /&gt;
 attr &amp;lt;Device&amp;gt; userattr userFn_BatterySoCManagement:ein,aus&lt;br /&gt;
&lt;br /&gt;
Die Perl Routine &#039;&#039;&#039;batSocChargeMgmnt&#039;&#039;&#039; ist wie folgt aufgebaut:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
###############################################################################&lt;br /&gt;
#&lt;br /&gt;
# Save this file as 99_mySolarForecastUtils.pm, and create your own functions&lt;br /&gt;
# in the new file. They are then available in every Perl expression.&lt;br /&gt;
#&lt;br /&gt;
###############################################################################&lt;br /&gt;
 package main;&lt;br /&gt;
 use strict;&lt;br /&gt;
 use warnings;&lt;br /&gt;
&lt;br /&gt;
 sub&lt;br /&gt;
 mySolarForecastUtils_Initialize($$)&lt;br /&gt;
 {&lt;br /&gt;
   my ($hash) = @_;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
# ab hier eigenen Code eintragen&lt;br /&gt;
###############################################################################&lt;br /&gt;
&lt;br /&gt;
############################################################################&lt;br /&gt;
#      Batterie SOC und Max. Ladestrom Management&lt;br /&gt;
############################################################################&lt;br /&gt;
sub batSocChargeMgmnt {&lt;br /&gt;
  my $name = shift;&lt;br /&gt;
  my $bn   = &#039;01&#039;;                                                                      # Batterienummer (evtl. im Aufruf mitgeben)&lt;br /&gt;
  my $hash = $defs{$name};&lt;br /&gt;
  &lt;br /&gt;
  my $vebus   = &#039;MQTT2_cerboGX_c0619ab34e08_vebus&#039;;                                     # Victron Vebus Device&lt;br /&gt;
  my $vicsets = &#039;MQTT2_cerboGX_c0619ab34e08_settings&#039;;                                  # Victron Einstellungen&lt;br /&gt;
&lt;br /&gt;
  my $maxcspc = 105;                                                                    # max. Ladestrom (A) für Victron MPII Verbund&lt;br /&gt;
  my $preduce = FHEM::SolarForecast::BatteryVal ($name, $bn, &#039;bpinreduced&#039;, $maxcspc);  # reduzierte Ladeleistung aus Bat-Konfig (W)&lt;br /&gt;
  my $actmcc  = ReadingsNum ($vicsets, &#039;MaxChargeCurrent&#039;, undef);                      # akt. Ladestromeinstellung&lt;br /&gt;
  my $load    = $actmcc // $maxcspc;                                                    # Soll-Ladestrom (A)  &lt;br /&gt;
&lt;br /&gt;
  ## Battery SoC Management&lt;br /&gt;
  ###########################&lt;br /&gt;
  my $ubsm = AttrVal ($name, &#039;userFn_BatterySoCManagement&#039;, &#039;aus&#039;); &lt;br /&gt;
  &lt;br /&gt;
  if ($ubsm eq &#039;ein&#039;) { &lt;br /&gt;
    my $bcrq = ReadingsNum ($name,    &#039;Battery_ChargeRequest_&#039;.$bn,     0);&lt;br /&gt;
    my $csoc = ReadingsNum ($vicsets, &#039;MinimumSocLimit&#039;,               10);             # akt. SoC&lt;br /&gt;
    my $osoc = ReadingsNum ($name,    &#039;Battery_OptimumTargetSoC_&#039;.$bn, 10);             # Soll-SoC&lt;br /&gt;
	my $surp = ReadingsNum ($name,    &#039;Current_Surplus&#039;,                0);             # aktueller PV-Überschuß&lt;br /&gt;
	&lt;br /&gt;
    if ($csoc != $osoc) {&lt;br /&gt;
      CommandSet (undef, &amp;quot;$vicsets MinimumSocLimit $osoc&amp;quot;);&lt;br /&gt;
      Log3 ($name, 3, qq{$name - userFn SoCMgmnt -&amp;gt; MinimumSocLimit in $vicsets set to $osoc %});&lt;br /&gt;
    }&lt;br /&gt;
	                                           &lt;br /&gt;
    if ($bcrq &amp;amp;&amp;amp; $surp &amp;lt; 1000) {                                                       # max. Ladestrom (A) b. Battery_ChargeRequest&lt;br /&gt;
      $load = $preduce / 48;                                                           # bei 48V Batteriesystem&lt;br /&gt;
    }&lt;br /&gt;
	else {&lt;br /&gt;
	  $load = $maxcspc;&lt;br /&gt;
	}&lt;br /&gt;
  }&lt;br /&gt;
  &lt;br /&gt;
  ## Einstellung MPII Verbund MaxChargeCurrent&lt;br /&gt;
  ##############################################&lt;br /&gt;
  if ($actmcc &amp;amp;&amp;amp; $load != $actmcc) {&lt;br /&gt;
    CommandSet (undef, &amp;quot;$vicsets MaxChargeCurrent $load&amp;quot;);&lt;br /&gt;
	&lt;br /&gt;
    Log3 ($name, 3, qq{$name - userFn ChargeMgmnt -&amp;gt; MaxChargeCurrent in $vicsets set }.&lt;br /&gt;
                    qq{from old $actmcc A to $load A});&lt;br /&gt;
  }&lt;br /&gt;
  &lt;br /&gt;
  readingsSingleUpdate ($hash, &#039;userFn_MPII_MaxChargeCurrent_set&#039;, $load, 0);&lt;br /&gt;
  &lt;br /&gt;
return;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
###############################################################################&lt;br /&gt;
# Ende eigener Code&lt;br /&gt;
&lt;br /&gt;
1;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ist im SolarForecast Device das Reading  &#039;&#039;&#039;Battery_ChargeRequest_01&#039;&#039;&#039; gesetzt, ist der aktuelle SOC kleiner als der optimale SOC und die Batterie wird (eventuell aus dem öffentlichen Netz) geladen. Das kann automatisch durch den Cerbo GX bzw. in anderen Anlagen durch einen entsprechenden Befehl erfolgen.&lt;br /&gt;
Damit die Netzbeladung nicht mit der vollen Ladestromstärke geschieht, wird der Ladestrom auf den gewünschten Wert (hier 21 A -&amp;gt; ca. 1000 W bei einem 48V System) reduziert sofern kein oder zu wenig PV-Überschuß vorhanden ist.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== weitere mögliche Aspekte der SOC- und Batterieladesteuerung ====&lt;br /&gt;
&lt;br /&gt;
SolarForecast liefert durch seine internen Logiken bereits sehr hilfreiche Signale zur Steuerung von Batteriesystemen in Form von Readings an. Abhängig von den individuellen Möglichkeiten der vorhandenen Anlage käme zum Beispiel zusätzlich zu einer Steurung des optimalen SOC oder der Zeitfenster für die Batterieaufladung auch eine Steuerung des Ladestroms bzw. der verwendeten Ladeleistung in Betracht.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Hintergründe:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* Das Laden eines Akkus mit möglichst gleichbleibender und geringer Leistung wirkt sich positiv auf dessen Langlebigkeit aus, was u.a. auch daran liegt, dass der Temperaturstreß im Akku bei langsamer Energiezuführung nur gering ist.&lt;br /&gt;
&lt;br /&gt;
* Ein geringer Ladestrom führt zu einem geringen Absacken der Akkuspannung beim (ggf, temporären) Abschalten des Ladestroms, was sich positiv auf die SOC-Schätzung auswirkt.&lt;br /&gt;
    &lt;br /&gt;
* Ein geringer Ladestrom führt, insb. bei Nutzung passiver Balancer (sind in den Akku-Systemen üblicherweise verbaut), zu einer geringen Differenz zwischen den einzelnen Zellspannungen und damit zu einer besseren Kapazitätsausnutzung eins Akkus.&lt;br /&gt;
&lt;br /&gt;
* Ein geringer Ladestrom kann zu einem zeitlich punktgenauen Zielfüllstand (im Winter 100%, ansonsten natürlich weniger) am Ende des Tages führen.&lt;br /&gt;
    &lt;br /&gt;
* Eine geringer Ladestrom führt auch zu geringen Leitungsverlusten, da der Strom quadratisch zu den Leitungsverlusten beiträgt.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Weiterhin kann auch die Verweildauer oder die Begrenzung des SOC, auf den die Batterie maximal geladen werden soll, Gegenstand von Optimierungen sein.&lt;br /&gt;
&lt;br /&gt;
Die häufig zu findende Empfehlung, Akkus nicht zu lange bei einem SOC von 100% verweilen zu lassen, liegt weniger an dem SOC als solchem, sondern daran, dass &#039;&#039;SOC=100%&#039;&#039; üblicherweise dadurch ermittelt wird, dass eine Zelle im Akku einen Spannungsschwellenwert überschreitet, der – würde dieser Schwellenwert zeitlich länger überschritten sein – zu einer Schädigung des Akkus führen könnte. Schädlich sind insbesondere häufige Nachladevorgänge bei Werten um die 100% SOC, da Zellen dann ja häufiger und damit - integral gesehen - auch länger den o.g. Schwellenwert überschreiten.&lt;br /&gt;
&lt;br /&gt;
Einige Akku-Hersteller, z.B. BYD, haben auf o.g, Grund in der (neueren) Firmware Ihrer Systeme einen SOC-Threshold (bei BYD liegt dieser bei 95%) eingebaut. Dieser Wert muss nach einem erreichten SOC von 100% unterschritten  werden, bevor überhaupt eine weitere Ladung erfolgt. Insbesondere in der dunklen Jahreszeit kann es daher sinnvoll sein, den Akku unter Verwendung von PV-Leistung so zu füllen, dass dieser möglichst nur einmal vor Einbruch der Dunkelheit auf &#039;&#039;SOC=100%&#039;&#039; geladen wird. Hierbei sollte darauf geachtet werden, dass der Ladeschlussstrom des jeweiligen Systems nicht unterschritten wird. &lt;br /&gt;
&lt;br /&gt;
SolarForecast unterstützt die oben betrachtete Batteriesteuerung durch die optionale Angabe von &#039;&#039;&#039;loadAbort&#039;&#039;&#039; im Attribut &#039;&#039;&#039;ctrlBatSocManagementXX&#039;&#039;&#039;. Das in diesem Kontext erstellte Reading &#039;&#039;&#039;Battery_ChargeAbort_XX&#039;&#039;&#039; stellt die auswertbaren Steuerungsinformationen zur Verfügung.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== PV-Prognose und Verbrauch optimierte Beladungssteuerung unter Berücksichtigung einer Wirkleistungsbegrenzung ====&lt;br /&gt;
&lt;br /&gt;
[[Datei:Screenshot 2025-04-25 143910.png|right|thumb|400px|Batterie Popup &amp;quot;nur laden wenn Einspeiselimit überschritten&amp;quot;]]&lt;br /&gt;
&lt;br /&gt;
Die in diesem Abschnitt beschriebene Ladestrategie &#039;&#039;&#039;loadRelease&#039;&#039;&#039; wird im Modul als &#039;&#039;&#039;Ladefreigabe&#039;&#039;&#039; bezeichnet.&lt;br /&gt;
&lt;br /&gt;
Üblicherweise wird die Batterie geladen. sobald PV-Überschuß vorhanden ist ohne eine Prognose zu berücksichtigen. Dadurch sind die Batterien im Sommer unter Umständen schon um 10-12 Uhr voll geladen und die Anlage liefert ihre mehr oder weniger volle Energie (je nach Ausrichtung) in das öffentliche Netz. Dann könnte es zu einer Begrenzung der PV-Leistung durch einen Schwellenwert (z.B. 70%-Regelung) oder eventuell Zwangsabregelung durch den Netzbetreiber kommen.&lt;br /&gt;
&lt;br /&gt;
Hintergrund dieser Erweiterung ist das Bestreben, eine optimale Unterstützung bei der Maximierung des Eigenverbrauchs zu geben und einen Beitrag zur Netzstabilität zu leisten.&lt;br /&gt;
Das Ziel der Beladungssteuerung der Batterie ist es, die Ladung der Batterie über den Tag verteilt so zu steuern, dass die Batterie erst dann geladen wird, wenn durch den PV-Überschuß eine Netzeinspeisung erfolgen würde bzw. ein Grenzwert erreicht werden würde, der zu einer Abregelung der PV-Anlage führen könnte.&lt;br /&gt;
Es kann auch zu einer Zwangsabregelung durch den Netzbetreiber führen, was vermieden werden soll.&lt;br /&gt;
 &lt;br /&gt;
Die interne Kalkulationslogik erstellt zu diesem Zweck ein Reading &#039;&#039;&#039;Battery_ChargeUnrestricted_XX&#039;&#039;&#039;.&lt;br /&gt;
Dieses Reading gibt dem User ein Signal, ob zu der aktuellen Stunde der Ladevorgang der Batterie freigegeben (1) oder nicht freigegeben werden sollte (0). Die Freigabe zur Ladung bedeutet dass:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Battery_ChargeUnrestricted_XX=1&#039;&#039;&#039; die Batterie mit ihrer vollen, oder der im Reading [[SolarForecast_-_Solare_Prognose_(PV_Erzeugung)_und_Verbrauchersteuerung#leistungsoptimierte_Beladungssteuerung_mit_dem_Fokus_geringe_Verlustleistung_und_Verschleiß|Battery_ChargeOptTargetPower_XX]] angegebenen Ladeleistung angesteuert werden sollte um wie prognostiziert am Ende des Sonnentages die volle bzw. maximal mögliche Ladung zu erhalten..&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Battery_ChargeUnrestricted_XX=0&#039;&#039;&#039; die Batterie nur geladen werden sollte wenn der im Attribut plantControl-&amp;gt;feedinPowerLimit definierte Wert überschritten wird&lt;br /&gt;
&lt;br /&gt;
 &lt;br /&gt;
Ist keine Freigabe zur Ladung vorhanden, kann bzw. sollte dennoch eine Ladung der Batterie vorgenommen werden, sofern eine über dem im &amp;lt;br&amp;gt;&lt;br /&gt;
Attribut &#039;&#039;&#039;plantControl-&amp;gt;feedinPowerLimit&#039;&#039;&#039; definierten Limit liegende Einspeiseleistung vorhanden ist. In diesem Status sollte die Batterie nur mit der Differenzleistung von vorhandener Einspeiseleistung und dem gesetzen Einspeiselimit geladen werden und damit das gesetzte Einspeiselimit einhalten ohne die Anlage abzuregeln. Ist die Batterie in einer Balkengrafikebene dargestellt, wird im Mouse-Over Popup die Battrie mit &#039;&#039;&amp;quot;nur laden wenn Einspeiselimit überschritten&amp;quot;&#039;&#039; gekennzeichnet.&lt;br /&gt;
&lt;br /&gt;
Die Möglichkeiten der Batteriesteuerung sind stark von der individuellen Anlage abhängig und können hier nicht beschrieben werden. Ein Umsetzungsbeispiel mit einer Victron-Anlage mit Pylontech Batterie ist weiter unten dargestellt.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
===== Aktivierung und Arbeitsweise =====&lt;br /&gt;
&lt;br /&gt;
Die implementierte Logik zur optimierten Batterie Beladungssteuerung wird aktiviert, wenn folgende Kriterien erfüllt sind: &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* der Schlüssel &#039;&#039;&#039;capacity&#039;&#039;&#039; in den setupInverterDevXX Atributen ist gesetzt&lt;br /&gt;
* das Attribut &#039;&#039;&#039;ctrlBatSocManagementXX&#039;&#039;&#039; für die jeweilige Batterie ist gesetzt&lt;br /&gt;
* der Schlüssel &#039;&#039;&#039;cap&#039;&#039;&#039; im Attribut setupBatteryDevXX der jeweiligen Batterie ist gesetzt&lt;br /&gt;
 &lt;br /&gt;
&lt;br /&gt;
Die Logik arbeitet wie folgt:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
;Der resultierende Schwellenwert der Wirkleistungsbegrenzung: der Gesamtanlage wird durch die Auswertung der in den Inverter-Attributen setupInverterDevXX  gesetzen Schlüssel limit (optional) und capacity ermittelt. Inverter mit gesetztem Schlüssel feed = grid (deren Energie wird ausschließlich in das öffentlich Netz eingespeist) werden von der Betrachtung in diesem Kontext ausgeschlossen.&lt;br /&gt;
&lt;br /&gt;
;Die benötigte Beladung bis zu maxSoC (im Attribut setupBatteryDevXX) : wird ermittelt und mit der zu erwartenden PV Tagesprognose in Beziehung gesetzt. Sofern die PV-Erzeugung abzgl. Verbrauch noch nicht das Wirkleistungbegrenzungs-Limit erreicht UND noch genügend PV-Überschuß für eine Ladung auf &#039;&#039;maxSoC&#039;&#039; im Laufe der kommenden Stunden des Tages zu erwarten ist, bleibt das Reading &#039;&#039;&#039;Battery_ChargeUnrestricted_XX = 0&#039;&#039;&#039;. Die erreichte Qualität der Steuerungslogik ist stark von der Prognosequaliät abhängig. Um die Steuerung resilienter zu gestalten, wird per default ein Sicherheitsaufschlag von 50% auf die prognostizierte benötigte Ladeenenergie auf Stundenbasis einkalkuliert. Dieser Wert kann mit dem Attribut &#039;&#039;&#039;ctrlBatSocManagementXX-&amp;gt;safetyMargin&#039;&#039;&#039; individuell angepasst werden.&lt;br /&gt;
&lt;br /&gt;
;Wird das Wirkleistungbegrenzungs-Limit erreicht/überschritten und/oder die PV-Überschußprognose: zzgl. eines Sicherheitspuffers unterschritten, wird &#039;&#039;&#039;Battery_ChargeUnrestricted_XX = 1&#039;&#039;&#039; gesetzt. Der User kann darauf reagieren und den Batterie Ladevorgang aktivieren. Die Möglichkeiten der Aktivierung sind natürlich von der installierten Anlage abhängig. Das SolarForecast Device greift nicht in die Batteriesteuerung direkt ein.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Anders ausgedrückt erfolgt immer dann eine Ladefreigabe, wenn einer der nachfolgenden Sachverhalte &#039;&#039;wahr&#039;&#039; ist:&lt;br /&gt;
&lt;br /&gt;
- die benötigte Ladeenergie (aller installierten Batterien) &amp;gt;= PV-Restüberschuß des Tages (Reading RestOfDayPVforecast) zzgl. Sicherheitsaufschlag&lt;br /&gt;
&lt;br /&gt;
ODER&lt;br /&gt;
&lt;br /&gt;
- der Zeitpunkt des PV-Erzeugungszenit des laufenden Tages überschritten ist (Reading Today_MaxPVforecastTime)&lt;br /&gt;
&lt;br /&gt;
ODER&lt;br /&gt;
&lt;br /&gt;
- wenn die aktuelle PV Leistung &amp;gt;= einer eventuellen Wechselrichter-Leistungsbegrenzung ist (Summe aller WR-Begrenzungen)&lt;br /&gt;
&lt;br /&gt;
ODER&lt;br /&gt;
&lt;br /&gt;
- wenn das BatSoc-Management _nicht_ aktiviert ist (nicht alle oben genannten Aktivierungskriterien erfüllt sind)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Sind mehrere Batterien installiert und durch das Management einer Batterie die Einhaltung der genannten Kriterien bzw. Sachverhalte erwirkt wird, bleiben die weiteren Batterien im Modus Ladefreigabe (Battery_ChargeUnrestricted_XX=1).  &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Umsetzungsbeispiel Batterie Ladesteuerung mit Victron GX Venus über den Grid Setpoint =====&lt;br /&gt;
&lt;br /&gt;
Ziel der Steuerung ist es abhängig vom Wert des Readings Battery_ChargeUnrestricted_XX (z.B. Battery_ChargeUnrestricted_01) die Ladung des angeschlossenen Batteriesystems zu aktivieren bzw. zu deaktivieren.&lt;br /&gt;
Das vorliegende Beispiel soll umzusetzen:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* Battery_ChargeUnrestricted_01 = 0 -&amp;gt; Batterie nicht aufladen bzw. nur laden wenn Einspeiselimit überschritten (Attribut plantControl-&amp;gt;feedinPowerLimit)&lt;br /&gt;
* Battery_ChargeUnrestricted_01 = 1 -&amp;gt; Batterie aufladen&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Der Grid Setpoint ist ein Parameter in den Victron Systemeinstellungen. Das Victron System wird den eingestellten Grid Setpoint versuchen einzuhalten und bei einem positiven Wert Energie aus dem Netz beziehen bzw. bei einem negativen Grid Setpoint Energie in das Netz einspeisen.&lt;br /&gt;
&lt;br /&gt;
Normalerweise wird das Victron System die erzeugte PV Energie zunächst zur Deckung des Hausverbrauchs verwenden, ist dann noch ein PV Überschuß vorhanden, die Batterien aufladen und bei weiter vohandenen PV Überschuß diese Energie in das öffentliche Netz einspeisen sofern dem System eine Einspeisung erlaubt ist. Anderenfalls werden die Wechselrichter oder MPPT Smartloader heruntergeregelt bzw. limitiert. &lt;br /&gt;
&lt;br /&gt;
In der Standardeinstellung ist beim Victron System ein Grid Setpoint von ca. 20 bis 50 Watt, d.h. das System strebt danach diese Vorgabe zu erfüllen zieht ständig Energie in diesem Bereich aus dem öffentlich Netz von einem gelegentlichen Überschwingen der Regelung abgesehen. Damit ein eventueller PV Überschuß nicht zum Laden der Batterie verwendet, sondern statt dessen in das öffentliche Netz eingespeist wird, muß der Grid Setpoint um diesen Betrag abgesenkt werden.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Screenshot 2025-02-24 114403.png|right|thumb|400px|Integration des Grid Setpoint-Managements in den Own_Spec-Bereich]]&lt;br /&gt;
&lt;br /&gt;
Damit man später per Attribut bzw. über den [[#Formatierung_der_Inhalte_im_Bereich_&amp;quot;Graphic_Header_Own_Specification&amp;quot;|Own_Spec-Bereich]] die automatische Ladesteuerung über den Grid Setpoint ein- bzw. ausschalten kann, legen wir ein userattr an:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
 userattr &amp;lt;Name&amp;gt; userFn_GridSetpointManagement:ein,aus&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Weiterhin sind einige Vorbereitungen im Device MQTT2_cerboGX_c0619ab34e08_settings vorzunehmen. Dieses Device ist im vorliegenden Beispiel ein in FHEM vorhandenes MQTT2_DEVICE, über das die Victron CerboGX Einstellungen zugreifbar sowie einstellbar sind. Die Vorgehensweise zur Erstellung des MQTT2_DEVICE ist hier nicht beschrieben und wird vorausgesetzt.&lt;br /&gt;
&lt;br /&gt;
Damit ein entsprechender Set-Befehl zur Einstellung des Grid Setpoints vorhanden ist, werden folgende Attribute gesetzt:&lt;br /&gt;
&lt;br /&gt;
 attr MQTT2_cerboGX_c0619ab34e08_settings jsonMap Settings_CGwacs_AcPowerSetPoint_value:GridSetpoint&lt;br /&gt;
 attr MQTT2_cerboGX_c0619ab34e08_settings setList GridSetpoint W/c0619ab34e08/settings/0/Settings/CGwacs/AcPowerSetPoint {&amp;quot;value&amp;quot;:$EVTPART1}&lt;br /&gt;
&lt;br /&gt;
Nach dieser Attributierung kann mit einem &amp;quot;&#039;&#039;set MQTT2_cerboGX_c0619ab34e08_settings GridSetpoint &amp;lt;Wert&amp;gt;&#039;&#039;&amp;quot; der Grid Setpoint eingestellt werden.&lt;br /&gt;
&lt;br /&gt;
Das Attribut userFn_GridSetpointManagement kann nun im Perl Code ausgewertet werden um das Grid Setpoint-Management ein- bzw. auszuschalten.&lt;br /&gt;
Der nachfolgende Code wird in die Datei &#039;&#039;&#039;99_mySolarForecastUtils.pm&#039;&#039;&#039; eingefügt. Ist diese Datei nicht vorhanden, legen wir sie vorab als Kopie der ausgelieferten 99_myUtils.pm an.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;mw-collapsible mw-collapsed&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Code Management Batterieladung über Victron Grid Setpoint -&amp;gt;&#039;&#039;&#039; &amp;lt;div class=&amp;quot;mw-collapsible-content&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
###############################################################################&lt;br /&gt;
#&lt;br /&gt;
# Save this file as 99_mySolarForecastUtils.pm, and create your own functions&lt;br /&gt;
# in the new file. They are then available in every Perl expression.&lt;br /&gt;
#&lt;br /&gt;
###############################################################################&lt;br /&gt;
 package main;&lt;br /&gt;
 use strict;&lt;br /&gt;
 use warnings;&lt;br /&gt;
&lt;br /&gt;
 sub&lt;br /&gt;
 mySolarForecastUtils_Initialize($$)&lt;br /&gt;
 {&lt;br /&gt;
   my ($hash) = @_;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
# ab hier eigenen Code eintragen&lt;br /&gt;
###############################################################################&lt;br /&gt;
&lt;br /&gt;
############################################################################&lt;br /&gt;
#   Steuerung Start/Stop der Batterieladung über den Victron Grid Setpoint&lt;br /&gt;
############################################################################&lt;br /&gt;
sub gridSetpointMgmt {&lt;br /&gt;
  my $name = shift;&lt;br /&gt;
  my $hash = $defs{$name};&lt;br /&gt;
  &lt;br /&gt;
  my $vicsets   = &#039;MQTT2_cerboGX_c0619ab34e08_settings&#039;;                               # Device Victron Einstellungen&lt;br /&gt;
  my $gspdef    = 20;                                                                  # Default Einstellung des Grid Setpoint&lt;br /&gt;
  my $targetgsp = $gspdef;                                                             # Voreinstellung Grid Setpoint&lt;br /&gt;
  my $neggspmax = FHEM::SolarForecast::CurrentVal ($name, &#039;feedinPowerLimit&#039;, 5000);   # max. gewünschte Einspeiseleistung in Watt&lt;br /&gt;
  $neggspmax    = $neggspmax * -1;&lt;br /&gt;
  my $gspm      = AttrVal ($name, &#039;userFn_GridSetpointManagement&#039;, &#039;aus&#039;);             # Freischaltung des Grid Setpoint-Managements&lt;br /&gt;
  my $curgsp    = ReadingsNum ($vicsets, &#039;GridSetpoint&#039;,         $gspdef);             # aktuelle Einstellung Grid Setpoint&lt;br /&gt;
  &lt;br /&gt;
  if ($gspm eq &#039;ein&#039;) { &lt;br /&gt;
    my $bcrq = ReadingsNum ($name, &#039;Battery_ChargeUnrestricted_01&#039;, 0);         # Ladefreigabe (1 -&amp;gt; Bat Ladefreigabe)&lt;br /&gt;
    my $surp = ReadingsNum ($name, &#039;Current_Surplus&#039;,               0);         # aktueller PV-Überschuß&lt;br /&gt;
	&lt;br /&gt;
    if (!$bcrq) {                                                               # Grid Setpoint absenken wenn keine .. &lt;br /&gt;
        $targetgsp = $gspdef - $surp;                                           # ..Batterie Ladefreigabe&lt;br /&gt;
        $targetgsp = $neggspmax if($targetgsp &amp;lt; $neggspmax);                    # Begrenzung der Einspeiseleistung&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
  &lt;br /&gt;
  if ($curgsp != $targetgsp) {&lt;br /&gt;
	  fhem (&amp;quot;set $vicsets GridSetpoint $targetgsp&amp;quot;);&lt;br /&gt;
      Log3 ($name, 3, qq{$name - userFn gridSetpointMgmt -&amp;gt; GridSetpoint in $vicsets set to $targetgsp});&lt;br /&gt;
  }&lt;br /&gt;
  &lt;br /&gt;
  readingsSingleUpdate ($hash, &#039;userFn_Victron_GridSetpoint_set&#039;, $targetgsp, 0);&lt;br /&gt;
  &lt;br /&gt;
return;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
###############################################################################&lt;br /&gt;
# Ende eigener Code&lt;br /&gt;
&lt;br /&gt;
1;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Der Vergleich des Setpoints mit der Vorgabe von &#039;&#039;$neggspmax&#039;&#039; dient dazu die maximale Einspeiseleistung in das öffentliche Netz zu begrenzen. Ist diese Grenze erreicht, wird der noch verbleibende Überschuß in die Batterie geladen. Dadurch wird eine Abregelung der Anlage vermieden wenn der Schwellenwert &#039;&#039;$neggspmax&#039;&#039;erreicht ist sofern die Batterie aufnahmefähig ist und nicht voll geladen ist. &lt;br /&gt;
&lt;br /&gt;
Damit die Routine mit jedem Zyklus im Modul ausgeführt wird, legen wir sie im Attribut &#039;&#039;&#039;ctrlUserExitFn&#039;&#039;&#039; an:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
attr &amp;lt;Name&amp;gt; ctrlUserExitFn  {  &lt;br /&gt;
                               ::gridSetpointMgmt  ($name);&lt;br /&gt;
                            }&lt;br /&gt;
&amp;lt;/pre&amp;gt; &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Zum Management des automatischen Grid Setpoint und der Anzeige des eingestellten Wertes wird das Attribut und die Readinganzeige in den Own_Spec-Bereich eingebunden, z.B.:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 attr &amp;lt;Name&amp;gt; graphicHeaderOwnspec  #Settings&lt;br /&gt;
                                   SetPoint&amp;amp;nbsp;Management:userFn_GridSetpointManagement&lt;br /&gt;
                                   Grid&amp;amp;nbsp;Setpoint:GridSetpoint@MQTT2_cerboGX_c0619ab34e08_settings&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== leistungsoptimierte Beladungssteuerung mit dem Fokus geringe Verlustleistung und Verschleiß ====&lt;br /&gt;
&lt;br /&gt;
Über das Attribut ctrlBatSocManagementXX kann mit dem Schlüssel loadStrategy die Ladestrategie &#039;&#039;&#039;optPower&#039;&#039;&#039; oder &#039;&#039;&#039;smartPower&#039;&#039;&#039; eingestellt werden.&lt;br /&gt;
Das SF-Modul bietet ein weiteres Reading &#039;&#039;&#039;Battery_ChargeOptTargetPower_XX&#039;&#039;&#039; zur Batteriesteuerung an. Dieses Reading liefert eine kalkulierte optimale Ladeleistung in Watt.&lt;br /&gt;
&lt;br /&gt;
Die in diesem Abschnitt beschriebene Ladestrategie &#039;&#039;&#039;optPower&#039;&#039;&#039; wird im Modul als &#039;&#039;&#039;optimierte Ladeleistung&#039;&#039;&#039;, die Ladestrategie &#039;&#039;&#039;smartPower&#039;&#039;&#039; als &#039;&#039;&#039;zieloptimierte Ladeleistung&#039;&#039;&#039; bezeichnet.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Beide Strategien kalkulieren die Ladeleistung kontinuierlich neu und berücksichtigen dabei:&lt;br /&gt;
&lt;br /&gt;
* die Prognose der PV-Erzeugung der nächsten Stunden und des (Rest-)Tages&lt;br /&gt;
* den prognostizierten Verbrauch auf Stunden- bzw. Tagesbasis&lt;br /&gt;
* eine eventuell mögliche Überschreitung des gesetzten Einspeiselimits (plantControl-&amp;gt;feedinPowerLimit)&lt;br /&gt;
* die gesetzten Leistungsparameter der Batterie &lt;br /&gt;
* das Ziel, am Tagesende einen möglichst maximalen SoC der Batterie erreicht zu haben bzw. den eingestellten Ziel-SoC &#039;&#039;&#039;loadTarget&#039;&#039;&#039;&lt;br /&gt;
* die Effizienz der Batterieanlage (Einstellung &#039;&#039;&#039;setupBatteryDevXX-&amp;gt;efficiency&#039;&#039;&#039;)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Der Fokus der implementierten Logik liegt dabei auf einer kontinuierlichen Ladeleistung, die aber unter den oben genannten Gesichtspunkten möglichst (relativ) gering sein soll um den Batterieverschleiß und die Verlustleistung zu minimieren. Die im Reading angezeigte Ladeleistung kann mit geeigneten Mitteln direkt in die Leistungssteuerung der Batterie eingebracht werden. Natürlich kann die Leistung auch vorab in einen optimalen Ladestrom umgerechnet werden.&lt;br /&gt;
&lt;br /&gt;
Da das Ergebnis der Kalkulation stark von den Prognosen abhängt, ist zur Erhöhung der Resilienz per Default ein Sicherheitszuschlag von 20% eingebaut. Dieser Wert kann mit dem Attribut &#039;&#039;&#039;ctrlBatSocManagementXX-&amp;gt;safetyMargin&#039;&#039;&#039; individuell angepasst werden. &lt;br /&gt;
&lt;br /&gt;
Ist kein PV-Überschuß (mehr) vorhanden / prognostiziert oder das Ladeziel erreicht, erfolgt ein Rückfall der Ladeleistung im Reading Battery_ChargeOptTargetPower_XX auf den  im Attribut &#039;&#039;&#039;setupBatteryDevXX-&amp;gt;pinmax&#039;&#039;&#039; angegebenen Wert. Ist dieser Wert nicht gesetzt, erfolgt der Rückfall auf &amp;quot;Unendlich&amp;quot; (9223372036854775807). &lt;br /&gt;
 &lt;br /&gt;
Im Reading Battery_ChargeOptTargetPower_XX wird der Wert des Parameters &#039;&#039;&#039;setupBatteryDevXX-&amp;gt;pinreduced&#039;&#039;&#039; gesetzt, wenn weniger Ladeleistung als pinreduced berechnet wurde (Einhaltung einer Mindestladeleistung) oder wenn der SoC der Batterie &amp;lt;= [[#Aktivierung_des_Batterie_SOC-_und_Lade-Managements|lowSoC]] beträgt.&lt;br /&gt;
Somit wird die Batterie bei einer Anforderungsladung bei Unterschreitung von lowSoC mit nur wenig Leistung aus dem Grid geladen falls es nötig sein sollte.&lt;br /&gt;
Es ist demzufolge ratsam den optionalen Parameter &#039;&#039;&#039;setupBatteryDevXX-&amp;gt;pinreduced&#039;&#039;&#039; zu setzen, da ansonsten ebenfalls ein Rückfall auf &#039;&#039;&#039;setupBatteryDevXX-&amp;gt;pinmax&#039;&#039;&#039; bzw. &amp;quot;Unendlich&amp;quot; erfolgt.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Beide Strategien, optPower und smartPower, setzen die zuvor beschriebene Arbeitsweise um.&lt;br /&gt;
Ergänzend wird bei der Ladestrategie &#039;&#039;&#039;smartPower&#039;&#039;&#039; die generelle Erreichbarkeit des Ladeziels bei der Festlegung der Ladeleistung berücksichtigt. Es führt dazu dass:&lt;br /&gt;
&lt;br /&gt;
* die Ladeleistung im Reading Battery_ChargeOptTargetPower_XX auf einen tendenziell höheren Wert als bei der optPower-Strategie gesetzt wird, wenn/solange das eingestellte Ladeziel als unerreichbar kalkuliert wird&lt;br /&gt;
* bei kalkulierter Erreichbarkeit des Ladeziels der Sicherheitsaufschlag linear absenkend proportional zum Kehrwert des verfügbaren PV-Überschusses auf die Ladeleistung angewendet wird&lt;br /&gt;
&lt;br /&gt;
Der nächste Abschnitt erläutert die smartPower Funktionen etwas eingehender.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
===== smartPower - die zieloptimierte Ladeleistung  =====&lt;br /&gt;
Die hinter smartPower liegende Anpassungslogik wird nachfolgend etwas näher beschrieben.&lt;br /&gt;
Ausgangspunkt ist das Ergbnis einer Ratio-Funktion aus dem (Rest)Tages-PV-Überschuß und benötigter Energie zur Erlangung des Batterie Ladeziels.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Lineare Interpolation zwischen pinmax und abgesicherter minpower&#039;&#039;&#039;  &amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Variablen&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;P&#039;&#039;&#039;: resultierende Leistung.&lt;br /&gt;
* &#039;&#039;&#039;pinmax&#039;&#039;&#039;: maximale Ladeleistung.&lt;br /&gt;
* &#039;&#039;&#039;limpower&#039;&#039;&#039;: Leistungslimit, d.h. Ladeleistungssollwert.&lt;br /&gt;
* &#039;&#039;&#039;r&#039;&#039;&#039;: Verhältnis in Prozent (r = spday·100 / whneed).&lt;br /&gt;
* &#039;&#039;&#039;otpMargin&#039;&#039;&#039;: Prozentwert der Bandbreite, über die die Absenkung linear verläuft (die Steilheit kann mit dem Attributwert ctrlBatSocManagementXX-&amp;gt;safetyMargin im Device eingestellt werden).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Die Kennlinie der für smartPower eingesetzten Funktion ist stückweise definiert über das Verhältnis Ratio &#039;&#039;𝑟 = spday x 100 / &amp;lt;benötigte Ladeenergie bis Ziel&amp;gt;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
* Für 𝑟 ≤ 100: &#039;&#039;𝑃 = pinmax&#039;&#039;, Interpretation: Es ist viel Bedarf vorhanden bzw. Überschuss ≤ 100 % des Bedarfs → maximal laden.&lt;br /&gt;
* Für 𝑟 ≥ 100 + otpMargin: &#039;&#039;𝑃 = limpower ⋅ (1 + otpMargin / 100 )&#039;&#039;&lt;br /&gt;
* Für 100 &amp;lt; 𝑟 &amp;lt; 100 + otpMargin: lineare Absenkung von &#039;&#039;&#039;P&#039;&#039;&#039; mit zunehmendem &#039;&#039;&#039;r&#039;&#039;&#039; für den Bereich 100 &amp;lt; 𝑟 &amp;lt; 100 + otpMargin. &#039;&#039;&#039;P&#039;&#039;&#039; wird bei 𝑟 = 100 gleich &#039;&#039;&#039;pinmax&#039;&#039;&#039; und bei 𝑟 = 100 + otpMargin gleich &#039;&#039;limpower ⋅ ( 1 + otpMargin/100 )&#039;&#039;, Interpretation: Sobald der Überschuss über den Bedarf hinausgeht (ratio &amp;gt; 100), wird das Ladeleistungslimit schrittweise von pinmax Richtung limpower reduziert; die Reduktion erfolgt gleichmäßig über die Margin-Spanne.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Lineare Interpolation und Steigung&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Die Differenz &#039;&#039;(pinmax − limpower)&#039;&#039; wird gleichmäßig auf die otpMargin-Prozentpunkte verteilt. Die Steigung der Geraden ist&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
slope = − (pinmax − limpower) / otpMargin&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
das heißt die Leistung sinkt um diesen Betrag pro Prozentpunkt von &#039;&#039;&#039;r&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Nach Umformung ist der Zusammenhang wie folgt darstellbar:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
𝑃 = pinmax − ( pinmax − limpower) ⋅ ((𝑟 − 100) / otpMargin)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
und ist äquivalent zu&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
𝑃 = pinmax + ( 𝑟 − 100 ) ⋅ slope&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Numerisches Beispiel&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;limpower&#039;&#039;&#039; = 1000 W, &#039;&#039;&#039;pinmax&#039;&#039;&#039; = 3000 W, &#039;&#039;&#039;otpMargin&#039;&#039;&#039; = 20&lt;br /&gt;
* Unterschied = 2000 W, Steigung = −2000/20 = −100 W/%&lt;br /&gt;
* Für &#039;&#039;&#039;r = 105&#039;&#039;&#039;: 𝑃 = 3000 − 2000 ⋅ ( 5 / 20 ) = 3000 − 500 = 2500 W&lt;br /&gt;
* Für &#039;&#039;&#039;r = 120&#039;&#039;&#039; ergibt die Formel den abgesicherten Endwert 1200 W&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Die Formel sorgt dafür, dass die Zielleistung stufenlos und vorhersehbar reduziert wird, wenn der relative Rest-Überschuss des Tages über 100% steigt, und erreicht bei Ende der otpMargin den geplanten abgesicherten Wert.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Ratio Kennlinie.png|right|thumb|200px|abfallend proportionale Anpassungsfunktion]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
In einem grafischen Diagramm stellt sich die abfallend proportionale Anpassungsfunktion der resultierenden Soll-Ladeleistung wie nebenstehend abgebildet dar. Dabei entspricht pow=100 dem jeweiligen pinmax Wert (hier 3000 W). So ist auch pow=0 mit dem abgesicherten Endwert (hier 1200 W) gleichzusetzen. Die Linie verläuft waagerecht bei pinmax solange das das Ratio  r ≤ 100 % und knickt an r == 100 mit steigenden r nach unten. Es verringert sich pow linear von pinmax zu limpower zzgl. otpMargin.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Durch diese Maßnahmen wird eine flexible Lademöglichkeit bis pinmax erreicht, die sich bei unvorhergesehenen Aufklarungen mit starker Sonneneintrahlung bei ansonsten sehr bedeckten Himmel positiv auswirkt. Andererseits wird die Erhöhung der Ladeleistung weiter begrenzt, wenn viel PV-Überschuß in Verbindung mit einer generellen Erreichbarkeit des Ladeziels zum Ende des Tages prognostiziert ist. Man könnte die Ladestrategie &#039;&#039;&#039;smartPower&#039;&#039;&#039; in ihrer Wirkung als einen Mix aus &#039;&#039;&#039;optPower&#039;&#039;&#039; und &#039;&#039;&#039;loadRelease&#039;&#039;&#039; ansehen.&lt;br /&gt;
&lt;br /&gt;
Zusammenfassend ist optPower vorwiegend für die Nutzergruppe geeignet, die eine tendenziell sparsamere Ladeleistung bevorzugen und dafür bereit sind eventuelle &amp;quot;Verluste&amp;quot; in Form von Einspeisung zu akzeptieren. &amp;lt;br&amp;gt;&lt;br /&gt;
Demgegenüber eignet sich das aggressivere smartPower für Anwender, die bei niedrigen PV-Erträgen bewusst das Maximum an Batterieladung anstreben und dafür in Kauf nehmen, dass Ladeleistungen weniger knapp kalkuliert sind. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Unterstützung des Modus &amp;quot;Eigennutzung&amp;quot; bei Hybrid-Wechselrichtern =====&lt;br /&gt;
Viele Hybrid-Wechselrichter verteilen die zur Verfügung stehende PV-Leistung nach folgendem Prioritäten, wenn der (übliche) Modus &amp;quot;Eigennutzung&amp;quot; aktiviert ist:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
# Nutzung der Leistung zur Deckung des Hausverbrauchs,&lt;br /&gt;
# Nutzung der Leistung zur Ladung des bzw. der angeschlossenen Speicher.&lt;br /&gt;
# Einspeisung der Leistung in das öffentliche Netz.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Da in aller Regel die Leistung, mit der ein angeschlossener Speicher geladen wird, limitiert werden kann, lassen sich die o.g. Priorität bzgl. des Speichers zugunsten einer Überschusseinspeisung aufweichen. Ein entstehender &amp;quot;Überschuss&amp;quot; steht also ggf. nur deshalb zur Verfügung, weil die Ladeleistung des Speichers begrenzt wurde.&lt;br /&gt;
&lt;br /&gt;
Ab Version 1.58.6 bietet SolarForecat die Möglichkeit, prognostizierte Verbräuche nicht mehr komplett bei der Bestimmung der &#039;&#039;optimalen Ladeleistung&#039;&#039; zu berücksichtigen, sondern nur noch zu einem prozentualen Anteil von 0..100 %. Hierzu muss der Attribut-Schlüssel &#039;&#039;&#039;ctrlBatSocManagementXX-&amp;gt;weightOwnUse=&amp;lt;Percentage&amp;gt;&#039;&#039;&#039; entsprechend gesetzt werden. &lt;br /&gt;
&lt;br /&gt;
So führt beispielsweise weightOwnUse=100 dazu, dass bei der Berechnung der optimalen Ladeleistung prognostizierte Verbräuche nicht mehr berücksichtigt werden indem sie bei der Bestimmung des stündlichen PV-Überschusses unberücksichtigt bleiben. Eine derartige Einstellung kann z.B. sinnvoll sein, wenn alle ansonsten täglich laufende Verbraucher zugunsten einer Batterieladung ausgeschaltet werden können.&lt;br /&gt;
&lt;br /&gt;
Ein Wert von zum Beispiel weightOwnUse=30 lässt 30% der stündlichen Verbrauchslast unberücksichtigt, sodass sich der kalkulierte PV-Überschuß pro Stunde um 30% des prognostizierten Verbrauchs erhöht. Da die Berechnung der optimierten Ladeleistung mit dem zur Verfügung stehenden PV-Überschuß gewichtet erfolgt, würde sich die kalkulierte Ladeleistung in den meisten Fällen ebenfalls um einen gewissen Betrag erhöhen sofern andere Faktoren dies nicht verhindern. Das kann beispielweise der Fall sein, wenn die kalkulierte Ladeleistung zu gering ist und automatisch aif mindestens &#039;&#039;&#039;setupBatteryDevXX-&amp;gt;pinreduced&#039;&#039;&#039; angehoben wurde.&lt;br /&gt;
  &lt;br /&gt;
  &lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Welche Ladestrategie soll ich wählen? - eine Möglichkeit zur Best-Practice Findung mit Codebeispiel ====&lt;br /&gt;
&lt;br /&gt;
In den vorangegangenen Abschnitten wurden die verschiedenen Möglichkeiten zur Steuerung der Batterieladung und dem SoC vorgestellt. Diese Steuerungsvarianten optimieren die Arbeitweise der Batterie, haben aber jeweils einen eigenen Fokus auch wenn es gewisse Überschneidungen gibt.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Die Steuerungsvarianten lassen sich folgendermaßen klassifizieren:&lt;br /&gt;
&lt;br /&gt;
# Steuerung der Batterieladung über eine Ladefreigabe, die durch bestimmte Indikatoren festgelegt wird. Diese Variante ist in [[SolarForecast_-_Solare_Prognose_(PV_Erzeugung)_und_Verbrauchersteuerung#PV-Prognose_und_Verbrauch_optimierte_Beladungssteuerung_unter_Berücksichtigung_einer_Wirkleistungsbegrenzung|diesem]] Abschnitt beschrieben.&lt;br /&gt;
# Steuerung der Batterieladung durch eine optimierte maximale Ladeleistung. Diese Variante ist in [[SolarForecast_-_Solare_Prognose_(PV_Erzeugung)_und_Verbrauchersteuerung#leistungsoptimierte_Beladungssteuerung_mit_dem_Fokus_geringe_Verlustleistung_und_Verschleiß|diesem]] Abschnitt dargelegt. Zu dieser Variante gehören die Strategien &#039;&#039;optPower&#039;&#039; sowie &#039;&#039;smartPower&#039;&#039;. Die Unterschiede beider Strategien sind ebenfalls in dem verlinkten Abschnitt beschrieben.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Die &#039;&#039;&#039;Variante 1.)&#039;&#039;&#039; gibt die Ladung der Batterie frei wenn der (verbleibende) PV-Überschuß des Tages kleiner oder gleich dem Ladungsbedarf der Batterie ist um einen SoC von 100% bzw. den maximal möglichen SoC am Ende des Tages zu erreichen. Dadurch wird die in der Batterie vorhandene freie Kapazität so lange wie möglich der Einhaltung von besonderen Grenzwerten, z.B. des Einspeiselimits (plantControl-&amp;gt;feedinPowerLimit), vorbehalten. Nebeneffekt ist, dass plötzlich und unerwartet auftretende hohe Erzeugungsleistungen in die Batterie geladen werden können und nicht unerwünscht in das öffentliche Netz eingespeist werden. &lt;br /&gt;
&lt;br /&gt;
Die &#039;&#039;&#039;Variante 2.)&#039;&#039;&#039; lässt die Batterie kontinuierlich laden, wobei die Ladeleistung auf ein optimiertes Minimum reduziert wird ohne das Ziel, einen maximalen SoC am Tagesende zu erreichen, zu ignorieren. Dadurch wird die Batterie schonend, mit möglichst wenig Verlustleistung und verschleißarm geladen. Die Vorhaltung der freien Kapaziäten der Batterie sind in dieser Variante nicht die Prämisse. Die Einhaltung des gesetzten Enispeiselimits wird auch in dieser Variante beachtet sofern es der Ladezustand der Batterie zulässt. Technisch bedingt kann diese Variante nur ungenügend auf unerwartete PV-Überschüsse reagieren, was zu einer Einspeisung in das öffentliche Netz oder im ungünstigsten Fall zur Abregelung der Anlage führen kann. Mit dem Attribut &#039;&#039;&#039;ctrlBatSocManagementXX-&amp;gt;safetyMargin&#039;&#039;&#039; kann man diesem negativen Aspekt in gewissem Maße entgegenwirken.  &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Ausgehend von diesen unterschiedlichen Optimierungszielen bietet sich die Variante 1.) vorwiegend in den Übergangs- und Wintermonaten an. Durch die beschriebenen Eigenschaften dieser Variante ist bei tendeziell weniger PV-Überschuß über den Tag die Ladung der Batterie komplett freigegeben, wodurch bei plötzlich auftretenden sonnige Abschnitten und Aufklarungen die Batterie sofort mit dem verfügbaren PV-Überschuß geladen wird. Auch eine Einspeisung über das Einspeiselimit hinaus kann verhindert werden. In der übrigen Zeit wird die Ladeleistung durch den verfügbaren PV-Überschuß begrenzt. &lt;br /&gt;
Weiterhin soll der SoC nicht dauerhaft tief absinken und ebenfalls nicht auf einem zu hohen Level verharren was auch einer maximal möglichen Energiespeicherung an sonnigen Tagen entgegenwirken würde.&lt;br /&gt;
In dieser Zeit ist die Nutzung der Kombination von [[SolarForecast_-_Solare_Prognose_(PV_Erzeugung)_und_Verbrauchersteuerung#Nutzung_des_Batterie_SOC-Management|SoC-Management]] und Variante 1.) wohl am Besten geeignet um die Ziele zu verwirklichen sowie Notreserven in den Batterien vorzuhalten. &lt;br /&gt;
&lt;br /&gt;
In den Sommermonaten hingegen ist üblicherweise sehr viel PV-Energie und Überschuß vorhanden. In dieser Periode würden die Batterien mit sehr hoher Leistung sehr zeitig am Tag vollgeladen. Die Variante 1.) ist im Prinzip ebenfalls gut geeignet den Zeitpunkt der Vollladung hinauszuzögern, jedoch erscheint die Varinte 2.) in dieser Zeit besser geeignet mit einer über den gesamten Tag verteilten moderaten Ladeleistung den Ziel-SoC zu erreichen. Auch diese Variante versucht eine Überschreitung des Einspeiselimits zu verhindern sofern der Ladezustand der Batterie dies ermöglicht.&lt;br /&gt;
&lt;br /&gt;
Das nachfolgende Codebeispiel bietet die Möglichkeit über ein Attribut die Ladevariante auszuwählen, über ein weiteres Attribut die SoC-Steuerung zu aktivieren/deaktivieren und integriert auch die Ladesteuerung für eine Batterie-Anforderungsladung bei Unterschreitung von ctrlBatSocManagementXX-&amp;gt;lowSoc. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Die gesteuerte Batterieanlage besteht aus:&lt;br /&gt;
&lt;br /&gt;
* 3 x Victron MultiPlusII 3000/48&lt;br /&gt;
* Steuergerät CerboGX (in FHEM über MQTT eingebunden)&lt;br /&gt;
* einem Batteriestack bestehend aus Pylontech US3000C (wird durch CerboxGX gesteuert)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Zur Umschaltung bzw. Auswahl der Ladestrategie und des SoC-Managements werden zwei User-Attribute im SolarForecast Device angelegt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
attr ... userattr userFn_BatterySoCManagement:Ein,Aus userFn_BatteryLoadManagement:Aus,loadRelease,optPower,smartPower&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Im Code werden diese Attribute ausgewertet. Das nachfolgende Script vereint folgende Funktionalitäten:&lt;br /&gt;
&lt;br /&gt;
* die Aktivierung/Deaktivierung des SoC-Managements via Attribut userFn_BatterySoCManagement&lt;br /&gt;
* die Auswahl der Ladestrategie oder Deaktivierung der Ladesteuerung via attribut userFn_BatteryLoadManagement&lt;br /&gt;
* Integration einer Anforderungsladung bei Signalisierung durch Reading Battery_ChargeRequest_XX&lt;br /&gt;
* Generierung von Readings userFn_Victron_GridSetpoint_set und userFn_MPII_MaxChargeCurrent_set zur Dokumentation der eingestellten Soll-Werte&lt;br /&gt;
&lt;br /&gt;
  &lt;br /&gt;
Bei der Nachnutzung des Scripts sind zunächst die &#039;&#039;&#039;Konstanten&#039;&#039;&#039; und das Device &#039;&#039;&#039;MQTT2_cerboGX_c0619ab34e08_setting&#039;&#039;&#039;s sowie dessen Readings im Block &#039;&#039;&#039;aktuelle Indikatoren&#039;&#039;&#039; anzupassen.&lt;br /&gt;
&lt;br /&gt;
Das Script wird in die Datei &#039;&#039;&#039;99_mySolarForecastUtils.pm&#039;&#039;&#039; eingetragen und im SolarForecast Attribut &#039;&#039;&#039;ctrlUserExitFn&#039;&#039;&#039; hinterlegt: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
attr ... ctrlUserExitFn {&lt;br /&gt;
                          ::batLoadMgmnt ($name, &#039;01&#039;);&lt;br /&gt;
                          # ::batLoadMgmnt ($name, &#039;02&#039;);   # optional für eine weitere Batterie &#039;02&#039;&lt;br /&gt;
                          # ::batLoadMgmnt ($name, &#039;0X&#039;);   # optional für weitere Batterien&lt;br /&gt;
                        }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Mit diesem Script ist der Nutzer in der Lage, das Batterie Lademanagement entsprechend seiner Motivation und gegebenenfalls abhängig von der Jahreszeit umzustellen und das Ergebnis bzw. die Zielerreichung zu testen.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Das Attribut &#039;&#039;&#039;userFn_BatteryLoadManagement&#039;&#039;&#039; selektiert die gewünschte Ladestrategie:&lt;br /&gt;
&lt;br /&gt;
;Aus: das Lademangement ist ausgeschaltet &lt;br /&gt;
&lt;br /&gt;
;loadRelease: das Lademangement Variante 1.) Ladestrategie Ladefreigabe ist aktiviert &lt;br /&gt;
&lt;br /&gt;
;optPower: das Lademangement Variante 2.) Ladestrategie optimierte Ladeleistung ist aktiviert &lt;br /&gt;
&lt;br /&gt;
;smartPower: das Lademangement Variante 2.) Ladestrategie zieloptimierte Ladeleistung ist aktiviert&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Die selektierte Ladestrategie wird im Script in das SolarForecast Device repliziert, d.h. der Attributschlüssel ctrlBatSocManagement01-&amp;gt;loadStrategy wird identisch zum Wert des Attributes userFn_BatteryLoadManagement gesetzt. Dazu wird das Set-Kommando &#039;&#039;attrKeyVal&#039;&#039; verwendet. Das globale Attribut &#039;&#039;autosave&#039;&#039; sollte aus diesem Grund NICHT explizit auf &amp;quot;0&amp;quot; gesetzt sein um eine reibungslose Funktion zu ermöglichen. &lt;br /&gt;
&lt;br /&gt;
Hat man die individuell passende Management-Strategie gefunden, bietet sich auch eine automatische (zum Beispiel durch einen Kalender oder Sonnenstand (Elevation) zur Mittagszeit) Umschaltung der Ladestrategie an. Die Unterschreitung der Elevation unter einen bestimmten Wert würde den Beginn des Winterhalbjahres und reverse Überschreitung den Beginn des Sommerhalbjahres kennzeichnen. Entsprechend würde dann die gewünschte Ladestrategie selektiert. Alternativ könnte auch die PV-Überschußprognose für den aktuellen Tag über das Reading &#039;&#039;Today_PVforecast&#039;&#039; für eine Entscheidung bzgl. der Ladestrategie herangezogen werden.&lt;br /&gt;
&lt;br /&gt;
Das SoC-Management kann über das gesamte Jahr aktiviert bleiben, hat jedoch nach bisherigen Erfahrungen nur im Winterhalbjahr eine starke Relevanz.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;mw-collapsible mw-collapsed&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Code Lademanagement -&amp;gt;&#039;&#039;&#039; &amp;lt;div class=&amp;quot;mw-collapsible-content&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
###############################################################################&lt;br /&gt;
#&lt;br /&gt;
# Save this file as 99_mySolarForecastUtils.pm, and create your own functions&lt;br /&gt;
# in the new file. They are then available in every Perl expression.&lt;br /&gt;
#&lt;br /&gt;
###############################################################################&lt;br /&gt;
 package main;&lt;br /&gt;
 use strict;&lt;br /&gt;
 use warnings;&lt;br /&gt;
&lt;br /&gt;
 sub&lt;br /&gt;
 mySolarForecastUtils_Initialize($$)&lt;br /&gt;
 {&lt;br /&gt;
   my ($hash) = @_;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
# ab hier eigenen Code eintragen&lt;br /&gt;
###############################################################################&lt;br /&gt;
&lt;br /&gt;
############################################################################&lt;br /&gt;
#      Komplettes Batterie Lademanagement&lt;br /&gt;
############################################################################&lt;br /&gt;
sub batLoadMgmnt {&lt;br /&gt;
  my $name = shift;&lt;br /&gt;
  my $bn   = shift // &#039;01&#039;;                                                             # Batterienummer&lt;br /&gt;
  my $hash = $defs{$name};&lt;br /&gt;
  &lt;br /&gt;
  ## Konstanten&lt;br /&gt;
  ###############  &lt;br /&gt;
  use constant {&lt;br /&gt;
    GSPDEF    =&amp;gt; 20,                                                                    # Einstellung des Grid Setpoint&lt;br /&gt;
    FINDEF    =&amp;gt; 5000,                                                                  # Default Einspeiselimit&lt;br /&gt;
    MINSOCDEF =&amp;gt; 10,                                                                    # Default Minimum SoC&lt;br /&gt;
    MAXPLDEF  =&amp;gt; 105,                                                                   # max. Ladestrom (A) Victron MPII &lt;br /&gt;
    SYSVOLTAG =&amp;gt; 48,                                                                    # Batterie Systemspannung&lt;br /&gt;
  };&lt;br /&gt;
  &lt;br /&gt;
  my $batsocmgmt  = AttrVal ($name, &#039;userFn_BatterySoCManagement&#039;,  &#039;Aus&#039;);             # SoC-Management Vorgabe&lt;br /&gt;
  my $batloadmgmt = AttrVal ($name, &#039;userFn_BatteryLoadManagement&#039;, &#039;Aus&#039;);             # Lademanagement Vorgabe&lt;br /&gt;
  my $vicsets     = &#039;MQTT2_cerboGX_c0619ab34e08_settings&#039;;                              # Victron CerboGX Einstellungen&lt;br /&gt;
&lt;br /&gt;
  ## Ladestrategie Ist-Einstellung und Soll-Abgleich&lt;br /&gt;
  ####################################################&lt;br /&gt;
  my $cgbt     = AttrVal ($name, &#039;ctrlBatSocManagement&#039;.$bn, undef);&lt;br /&gt;
  my $strategy = &#039;loadRelease&#039;; &lt;br /&gt;
&lt;br /&gt;
  if ($cgbt) {&lt;br /&gt;
    my $parsed = FHEM::SolarForecast::__parseAttrBatSoc ($name, $cgbt);                 # aktuell gesetzte Strategie ermitteln&lt;br /&gt;
    $strategy  = $parsed-&amp;gt;{loadStrategy} // $strategy;&lt;br /&gt;
  }&lt;br /&gt;
  &lt;br /&gt;
  if ($batloadmgmt ne &#039;Aus&#039; &amp;amp;&amp;amp; $strategy ne $batloadmgmt) {&lt;br /&gt;
    CommandSet (undef, &amp;quot;$name attrKeyVal ctrlBatSocManagement${bn} loadStrategy=$batloadmgmt&amp;quot;);              &lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  ## aktuelle Indikatoren&lt;br /&gt;
  ##########################&lt;br /&gt;
  my $surp    = ReadingsNum ($name, &#039;Current_Surplus&#039;,            0);                   # aktueller PV-Überschuß&lt;br /&gt;
  my $bcrq    = ReadingsNum ($name, &#039;Battery_ChargeRequest_&#039;.$bn, 0);                   # Notfallladung&lt;br /&gt;
  my $curgsp  = ReadingsNum ($vicsets, &#039;GridSetpoint&#039;, GSPDEF);                         # aktuelle Einstellung Grid Setpoint&lt;br /&gt;
  my $finplim = FHEM::SolarForecast::CurrentVal ($name, &#039;feedinPowerLimit&#039;,   FINDEF);  # Limit plantControl-&amp;gt;feedinPowerLimit&lt;br /&gt;
  my $preduce = FHEM::SolarForecast::BatteryVal ($name, $bn, &#039;bpinreduced&#039;, MAXPLDEF);  # reduzierte Ladeleistung Bat-Konfig&lt;br /&gt;
  my $actmcc  = ReadingsNum ($vicsets, &#039;MaxChargeCurrent&#039;,     0);                      # akt. maximale Ladestromeinstellung&lt;br /&gt;
  my $actmcp  = ReadingsNum ($vicsets, &#039;MaxChargePower&#039;,   undef);                      # akt. Ladeleistungseinstellung&lt;br /&gt;
  my $load    = MAXPLDEF;                                                               # initialer Soll-Ladestrom (A)&lt;br /&gt;
  &lt;br /&gt;
  my $targetgsp = GSPDEF;                                                               # Voreinstellung Grid Setpoint&lt;br /&gt;
  my $ctype     = &#039;default&#039;;                                                            # Voreinstellung Steuerungstyp  &lt;br /&gt;
&lt;br /&gt;
  ## Battery SoC Management&lt;br /&gt;
  ###########################  &lt;br /&gt;
  if ($batsocmgmt eq &#039;Ein&#039;) { &lt;br /&gt;
    my $csoc = ReadingsNum ($vicsets, &#039;MinimumSocLimit&#039;,               MINSOCDEF);     # akt. SoC&lt;br /&gt;
    my $osoc = ReadingsNum ($name,    &#039;Battery_OptimumTargetSoC_&#039;.$bn, MINSOCDEF);     # optimierter Mindest-SoC&lt;br /&gt;
	&lt;br /&gt;
    if ($csoc != $osoc) {&lt;br /&gt;
      CommandSet (undef, &amp;quot;$vicsets MinimumSocLimit $osoc&amp;quot;);&lt;br /&gt;
      Log3 ($name, 3, qq{$name - userFn SoCMgmnt -&amp;gt; MinimumSocLimit in $vicsets set to $osoc %});&lt;br /&gt;
    }                                       &lt;br /&gt;
  }&lt;br /&gt;
  &lt;br /&gt;
  ## Steuerung nach Ladungsfreigabe&lt;br /&gt;
  ###################################    &lt;br /&gt;
  if ($batloadmgmt eq &#039;loadRelease&#039;) {&lt;br /&gt;
    $ctype         = &#039;loadRelease&#039;;    &lt;br /&gt;
    my $unrestrict = ReadingsNum ($name, &#039;Battery_ChargeUnrestricted_&#039;.$bn, 0);         # Ladefreigabe (1 -&amp;gt; Ladefreigabe)&lt;br /&gt;
	&lt;br /&gt;
    if (!$unrestrict) {                                                                 # Grid Setpoint absenken wenn keine .. &lt;br /&gt;
        $targetgsp    = GSPDEF - $surp;                                                 # ..Batterie Ladefreigabe&lt;br /&gt;
        my $neggspmax = -1 * $finplim;&lt;br /&gt;
		$targetgsp    = $neggspmax if($targetgsp &amp;lt; $neggspmax);                         # Begrenzung der Einspeiseleistung&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
  &lt;br /&gt;
  if ($curgsp != $targetgsp) {                                                          # GridSetpoint setzen&lt;br /&gt;
	  fhem (&amp;quot;set $vicsets GridSetpoint $targetgsp&amp;quot;);&lt;br /&gt;
      Log3 ($name, 3, qq{$name - userFn ChargeMgmnt &#039;loadRelease&#039; -&amp;gt; GridSetpoint in $vicsets set to $targetgsp});&lt;br /&gt;
  }&lt;br /&gt;
  &lt;br /&gt;
  readingsSingleUpdate ($hash, &#039;userFn_Victron_GridSetpoint_set&#039;, $targetgsp, 0); &lt;br /&gt;
  &lt;br /&gt;
  ## Steuerung nach optimaler Ladeleistung&lt;br /&gt;
  ##########################################&lt;br /&gt;
  if ($batloadmgmt =~ /(?:opt|smart)Power/xs) {&lt;br /&gt;
    $ctype  = &#039;optPower&#039;; &lt;br /&gt;
    my $otp = ReadingsNum ($name, &#039;Battery_ChargeOptTargetPower_&#039;.$bn, $preduce);      # optimale Ladeleistung (W)&lt;br /&gt;
    $load   = sprintf &amp;quot;%.0f&amp;quot;, ($otp / SYSVOLTAG);&lt;br /&gt;
  }&lt;br /&gt;
  &lt;br /&gt;
  ## Anforderungsladung&lt;br /&gt;
  ####################### &lt;br /&gt;
  if ($bcrq) {                                                                         # max. Ladeleistung...&lt;br /&gt;
    $ctype = &#039;requestCharging&#039;;                                                        # bei Battery_ChargeRequest&lt;br /&gt;
    my $p;&lt;br /&gt;
    &lt;br /&gt;
	if ($surp &amp;lt; $preduce) {&lt;br /&gt;
	  $p = $preduce;&lt;br /&gt;
    }&lt;br /&gt;
    else {&lt;br /&gt;
      $p = ReadingsNum ($name, &#039;Battery_ChargeOptTargetPower_&#039;.$bn, $preduce);         # optimale Ladeleistung (W) &lt;br /&gt;
    }&lt;br /&gt;
	&lt;br /&gt;
    $load = sprintf &amp;quot;%.0f&amp;quot;, ($p / SYSVOLTAG);&lt;br /&gt;
  }&lt;br /&gt;
  &lt;br /&gt;
  ## Stromeinstellung umsetzen&lt;br /&gt;
  #############################  &lt;br /&gt;
  if ($load != $actmcc) {&lt;br /&gt;
    CommandSet (undef, &amp;quot;$vicsets MaxChargeCurrent $load&amp;quot;);&lt;br /&gt;
	&lt;br /&gt;
    Log3 ($name, 3, qq{$name - userFn ChargeMgmnt &#039;$ctype&#039; -&amp;gt; MaxChargeCurrent in $vicsets set }.&lt;br /&gt;
                    qq{from old $actmcc A to $load A});&lt;br /&gt;
  }&lt;br /&gt;
  &lt;br /&gt;
  readingsSingleUpdate ($hash, &#039;userFn_MPII_MaxChargeCurrent_set&#039;, $load, 0);&lt;br /&gt;
  &lt;br /&gt;
return;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
###############################################################################&lt;br /&gt;
# Ende eigener Code&lt;br /&gt;
&lt;br /&gt;
1;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== sequentielle Batterie-Ladung mehrerer Batterien mit lcSlot ====&lt;br /&gt;
&lt;br /&gt;
Der Parameter &#039;&#039;&#039;ctrlBatSocManagementXX-&amp;gt;lcSlot&#039;&#039;&#039; (&amp;quot;load control Slot&amp;quot;) definiert eine Zeitperiode am aktuellen Tag in der die Steuerungslogik für die Batteriesteuerung aktiviert ist. Ist dieser Parameter nicht gesetzt, ist das eingestellte Lademanagement über den gesamten Tag aktiert und wäre identisch mit der Einstellung:&lt;br /&gt;
&lt;br /&gt;
 lcSlot=00:00-23:59&lt;br /&gt;
&lt;br /&gt;
Somit ist die Ladesteuerung per default über den gesamten Tag aktiv.&lt;br /&gt;
&lt;br /&gt;
Durch das Setzen von lcSlot auf einen täglichen Slot wird die Ladesteuerung auf diese Periode eingegrenzt. In der übrigen Zeit ist die Ladung der Batterie freigegeben (Reading Battery_ChargeUnrestricted_XX) bzw. auf die maximal konfigurierte Ladeleistung eingestellt (Reading Battery_ChargeOptTargetPower_XX). Diese Readings können je nach verwendeter Ladestrategie ausgewertet und zur Steuerung der Batterie(n) verwendet werden.  &lt;br /&gt;
Die SoC-Steuerung wird durch die lcSlot-Angabe nicht beeinflusst.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Mit lcSlot kann zum Beispiel ein sequentielles Laden von mehreren Batterien umgesetzt werden. Nachfolgend wird ein solches Szenario mit drei Batterien  skizziert:&lt;br /&gt;
&lt;br /&gt;
1. die Batterie 1 soll mit voller Leistung geladen werden, die anderen Batterien nur bei weiterem Überschuß. Man würde die jeweiligen &lt;br /&gt;
ctrlBatSocManagementXX-&amp;gt;lcSlot Schlüssel definieren:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
set ... attrKeyVal ctrlBatSocManagement01 lcSlot=23:00-23:10&lt;br /&gt;
set ... attrKeyVal ctrlBatSocManagement02 lcSlot=00:00-23:59&lt;br /&gt;
set ... attrKeyVal ctrlBatSocManagement03 lcSlot=00:00-23:59&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;Wie ist der Ablauf: Die Batterie 1 unterliegt keiner Steuerung (nur von 23:00 bis 23:10 als Dummy-Periode). Das Reading Battery_ChargeUnrestricted_01 wird &amp;quot;1&amp;quot; gesetzt, d.h. die Batterie 1 wird zur Ladung uneingeschränkt freigegeben. Die anderen Batterien unterliegen der Steuerung -&amp;gt; Battery_ChargeUnrestricted_02 / 03 sind &amp;quot;0&amp;quot; und sollen nur geladen werden falls ein gesetztes Einspeiselimit überschritten wird.&lt;br /&gt;
&lt;br /&gt;
Ist das Ladeziel der Batterie 1 erreicht, setzt man um die Batterie 2 vollzuladen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
set ... attrKeyVal ctrlBatSocManagement01 lcSlot=00:00-23:59&lt;br /&gt;
set ... attrKeyVal ctrlBatSocManagement02 lcSlot=23:00-23:10&lt;br /&gt;
set ... attrKeyVal ctrlBatSocManagement03 lcSlot=00:00-23:59&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Batterien 1 und 3 unterliegen der Steuerung, d.h. Battery_ChargeUnrestricted_01 / 03 haben den Wert &amp;quot;0&amp;quot;. Demgegenüber ist Battery_ChargeUnrestricted_02=1 und soll uneingeschränkt geladen werden.&lt;br /&gt;
&lt;br /&gt;
Ist auch die Batterie 2 voll geladen, würde man das Laden der Batterie 3 aktivieren:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
set ... attrKeyVal ctrlBatSocManagement01 lcSlot=00:00-23:59&lt;br /&gt;
set ... attrKeyVal ctrlBatSocManagement02 lcSlot=00:00-23:59&lt;br /&gt;
set ... attrKeyVal ctrlBatSocManagement03 lcSlot=23:00-23:10&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Das Setzens via &#039;&#039;&#039;attrKeyVal&#039;&#039;&#039; hat den Vorteil, dass die Zeitfenster-Syntax (Anfangszeit kleiner Endezeit usw.) im Hintergrund peprüft und ggf. ein Fehler zurückgegeben wird, den der Nutzer in seinem Script auswerten kann. Weiterhin werden die Attribute implizit gespeichert sofern global-&amp;gt;autosave nicht explizit auf &amp;quot;0&amp;quot; gesetzt ist.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Unterstützung eines netzdienlichen Verhaltens ====&lt;br /&gt;
&lt;br /&gt;
Der Steuerungsprozess der Batterieanlage leistet insgesamt einen kleinen Beitrag zur Netzstabilität, indem die Speicherladung in den Zeitraum der meisten prognostizierten Überschußeinspeisung gelegt wird. Im Sommer kann sich dieser Zeitraum in die Zeit nach dem Maxmimum des prognostizierten PV-Überschusses verlagern, wenn auch danach noch genügend Überschußenergie zu Volladung der Batterie prognostiziert wird. In dem Zusammenhang sollte im Attribut &#039;&#039;&#039;plantControl-&amp;gt;feedinPowerLimit&#039;&#039;&#039; ein Einspeiselimit gesetzt werden. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Zur Verdeutlichung soll das Beispiel dienen:&lt;br /&gt;
&lt;br /&gt;
* die Batterie ist zu 60% geladen &lt;br /&gt;
* der User hat upSoC=40 eingestellt&lt;br /&gt;
* der User hat maxSoC=90 eingestellt&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Am Vormittag ist genügend Sonne vohanden, das Maximumum der Energie über die Zeit wird aber noch erwartet. &lt;br /&gt;
Zu diesem Zeitpunkt wird das Reading  &#039;&#039;&#039;Battery_ChargeUnrestricted_XX&#039;&#039;&#039; auf den Wert &amp;quot;0&amp;quot; gesetzt. Durch den Nutzer erfolgt daraufhin, zum Beipiel über ein Notify, ein anlagenspezifischer Befehl &amp;quot;Ladestop&amp;quot; an die Batterieanlage. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Ab diesem Moment wird:&lt;br /&gt;
&lt;br /&gt;
# die Batterie nicht geladen&lt;br /&gt;
# die erzeugte PV-Energie dem Haushalt zur Verfügung gestellt bzw. der verbleibende Überschuß eingespeist, sofern der Überschuß unterhalb des im Attribut &#039;&#039;&#039;plantControl-&amp;gt;feedinPowerLimit&#039;&#039;&#039; definierten Limits liegt.&lt;br /&gt;
# ein eventuell über dem Limit &#039;&#039;&#039;plantControl-&amp;gt;feedinPowerLimit&#039;&#039;&#039; vorhandener Überschuß in die Batterie geladen und so die Netzlast verringert sowie die Batterie schonend geladen. (dieser Punkt trifft in den Monaten mit wenig PV-Erzeugung eher nicht zu)  &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Gleichzeitig liefert die Batterie Energie an das Hausnetz und wird von 60% bis maximal einem SoC-Wert entladen, der durch die [[#Die_dynamische_SOC_Steuerung_mit_Umsetzungsbeispiel|dynamische SOC Steuerung]] bestimmt wird. Dazu muß diese Steuerung natürlich implementiert sein. Sofern der berechnete SoC größer als upSoC ist, wird der maximale Entladungs-SoC auf upSoC gesetzt. &lt;br /&gt;
&lt;br /&gt;
Zusätzlich überprüft das Modul permanent bei jedem Zyklus die Differenz des aktuell vorhandenen SoC zum eingestellten maxSoC.&lt;br /&gt;
Sie soll nicht größer sein um mit dem (noch) zu erwartenden PV-überschuß des Tages der eingestellte maxSoC bzw. dessen Nähe wahrscheinlich erreicht werden kann. &lt;br /&gt;
Sollte der Differenz-Grenzwert erreicht sein, wird &#039;&#039;&#039;Battery_ChargeUnrestricted_XX=1&#039;&#039;&#039; gesetzt und der Nutzer sollte dann über einen geeigneten Befehl seine Anlage in den Modus &amp;quot;Laden&amp;quot; umschalten.&lt;br /&gt;
Dieser Vorgang kann beliebig oft während des Tages alternieren.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Nutzung von batteryTrigger ===&lt;br /&gt;
Ergänzt werden die Möglichkeiten durch das set-Kommando: &lt;br /&gt;
&lt;br /&gt;
 batteryTrigger &amp;lt;1on&amp;gt;=&amp;lt;Wert&amp;gt; &amp;lt;1off&amp;gt;=&amp;lt;Wert&amp;gt; [&amp;lt;2on&amp;gt;=&amp;lt;Wert&amp;gt; &amp;lt;2off&amp;gt;=&amp;lt;Wert&amp;gt; ...] &lt;br /&gt;
&lt;br /&gt;
Das Kommando setzt Triggerpunkte bei Über- bzw. Unterschreitung bestimmter Batterieladungswerte (SoC in %).&lt;br /&gt;
&lt;br /&gt;
Der verwendete SoC wird als resultierender SoC als Summe der aktuellen Ladung aller registrierten Batterie-Geräte im Verhältnis zur installierten Gesamtkapazität gebildet. Das bedeutet, dass alle einzelnen Batterie-Geräte als ein gemeinsamer Cluster betrachtet werden. &lt;br /&gt;
&lt;br /&gt;
Es kann eine beliebige Anzahl von Triggerbedingungen angegeben werden. Xon/Xoff-Bedingungen müssen nicht zwingend paarweise definiert werden.&lt;br /&gt;
Überschreiten die letzten drei SoC-Messungen eine definierte Xon-Bedingung, wird das Reading &#039;&#039;&#039;batteryTrigger_X = on&#039;&#039;&#039; erstellt/gesetzt.&lt;br /&gt;
Unterschreiten die letzten drei SoC-Messungen eine definierte Xoff-Bedingung, wird das Reading &#039;&#039;&#039;batteryTrigger_X = off&#039;&#039;&#039; erstellt/gesetzt.&lt;br /&gt;
&lt;br /&gt;
Der Inhalt der Readings kann durch ein User-Programm oder, bei Erzeugung von entsprechenden Events, mittels notify-Device ausgewertet werden um bei Eintreten eines definierten Ereignisses entsprechende Reaktionen auszulösen. Das kann zum Beispiel die Freigabe eines Heizstabes oberhalb eines bestimmten SOC sein oder ein Entladeverbot der Batterie unterhalb eines SOC-Wertes um Reserven für einen eventuellen Stromausfall zu behalten.&lt;br /&gt;
&lt;br /&gt;
Damit die Batterietrigger dynamisch geändert werden können, ist die Einstellung als Set-Kommando und nicht als Attribut ausgeführt.   &lt;br /&gt;
&lt;br /&gt;
Alle Triggerpunkte können mit dem set-Befehl:&lt;br /&gt;
&lt;br /&gt;
 reset batteryTriggerSet&lt;br /&gt;
&lt;br /&gt;
wieder gelöscht werden.&lt;br /&gt;
&lt;br /&gt;
== Verbrauchersteuerung - Registrieren und visualisieren von Verbrauchern ==&lt;br /&gt;
&lt;br /&gt;
Das Modul gestattet es beliebige Verbraucher (Devices) über die Attribute &#039;&#039;&#039;consumerXX&#039;&#039;&#039; zu registrieren. Durch die Registrierung wird dem Modul der Namen des Devices sowie dessen Eingenschaften durch die Angabe von Schlüssel-Wert Paaren bekannt gemacht.&lt;br /&gt;
&lt;br /&gt;
Nach der Registrierung können die Verbraucher durch das Modul genutzt werden:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* es erfolgt eine Planung der Ein- und Ausschaltzeiten abhängig von der solaren Prognose in Bezug zu den Leistungsdaten des Verbrauchers sowie der anderen registrierten Consumer&lt;br /&gt;
* das Modul kann die Ein- und Ausschaltsteuerung der Consumer übernehmen (optional)&lt;br /&gt;
* die aktuellen Status (Verbrauchsdaten) werden in der Energiefußgrafik dargestellt&lt;br /&gt;
* das Modul lernt mit der Zeit das Verbrauchsverhalten der registrierten Verbraucher&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Um einen Verbaucher zu registrieren, wird ein freies &#039;&#039;&#039;consumerXX&#039;&#039;&#039; Attribut, zum Beispiel consumer01, gesetzt.&lt;br /&gt;
&lt;br /&gt;
Die Eigenschaften und das Schaltverhalten wird durch die Angabe der verfügbaren Schlüssel gesteuert. Die Möglichkeiten sind sehr umfangreich. Die nachfolgenden Abschnitte beschreiben die Verwendung am Beispiel oft vorkommender bzw. nachgefragter Sachverhalte.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Die Phasen der Verbrauchersteuerung ===&lt;br /&gt;
&lt;br /&gt;
Jeder Zyklus eines Verbrauchers durchläuft im Modul die folgenden Phasen:&lt;br /&gt;
&lt;br /&gt;
# &#039;&#039;&#039;Planung&#039;&#039;&#039; der Aktivzeiten des Verbrauchers, d.h. wann der Verbraucher geplant eingeschaltet sowie geplant ausgeschaltet wird und damit der Zyklus beendet wird.&lt;br /&gt;
# &#039;&#039;&#039;Start&#039;&#039;&#039; des Zyklus, d.h. Einschalten des Verbrauchers entsprechend der Planungsdaten sofern weitere, in den Consumer-Schlüsseln definierbare Rahmenbedingungen das Einschalten nicht verhindern (z.B. kein oder zu wenig PV-Energieerzeugung / PV-Überschuss)&lt;br /&gt;
# &#039;&#039;&#039;Permanente Überprüfung von&#039;&#039;&#039; eventuell vorhandenen &#039;&#039;&#039;Interruptbedingungen&#039;&#039;&#039; die den Verbraucher temporär ausschalten und, sofern die Interruptbedingung nicht mehr vorliegt, den Verbraucher wieder einschalten und so den gestarteten Zyklus fortsetzen.&lt;br /&gt;
#  &#039;&#039;&#039;Beendigung&#039;&#039;&#039; der Zyklus. Die Beendigung erfolgt entweder (normalerweise) wenn die geplante Endezeit der Zyklus erreicht ist oder eine optionale Endebedingung erfüllt ist.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Steuerung der Phasen ====&lt;br /&gt;
Der Ablauf der Phasen kann durch verschiedene Schlüssel-Wert Paare beeinflusst und gesteuert werden.&lt;br /&gt;
&lt;br /&gt;
Mit dem optionalen Schlüssel &#039;&#039;&#039;swoncond&#039;&#039;&#039; kann eine &#039;&#039;&#039;zusätzliche externe Bedingung&#039;&#039;&#039; definiert werden um den Einschaltvorgang des Consumers freizugeben. Ist die Bedingung (Regex) nicht erfüllt, erfolgt kein Einschalten des Verbrauchers auch wenn die sonstigen Voraussetzungen wie Zeitplanung, on-Schlüssel, auto-Mode und aktuelle PV-Leistung gegeben sind. Es erfolgt somit eine &#039;&#039;&#039;UND-Verknüpfung&#039;&#039;&#039; des Schlüssels swoncond mit den Planungsdaten und weiteren Einschaltbedingungen.&lt;br /&gt;
&lt;br /&gt;
Der optionale Schlüssel &#039;&#039;&#039;swoffcond&#039;&#039;&#039; definiert eine &#039;&#039;&#039;vorrangige Ausschaltbedingung&#039;&#039;&#039; (Regex). Sobald diese Bedingung erfüllt ist, wird der Consumer ausgeschaltet auch wenn die geplante Endezeit (consumerXX_planned_stop) noch nicht erreicht ist (&#039;&#039;&#039;ODER-Verknüpfung&#039;&#039;&#039;). Weitere Bedingungen wie off-Schlüssel und auto-Mode müssen zum automatischen Ausschalten erfüllt sein.&lt;br /&gt;
&lt;br /&gt;
Mit dem optionalen Schlüssel &#039;&#039;&#039;interruptable&#039;&#039;&#039; kann während der geplanten Einschaltzeit eine automatische Unterbrechung sowie Wiedereinschaltung des Verbrauchers vorgenommen werden. Der Verbraucher wird temporär ausgeschaltet (interrupted) und wieder eingeschaltet (continued) wenn die Interrupt-Bedingung nicht mehr vorliegt. Die verbleibende Laufzeit wird durch einen Interrupt nicht beeinflusst! &lt;br /&gt;
&lt;br /&gt;
Der Schlüssel &#039;&#039;&#039;power&#039;&#039;&#039; gibt die nominale Leistungsaufnahme des Verbrauchers gemäß seines Datenblattes an. Dieser Wert wird verwendet um die Schaltzeiten des Verbrauchers zu planen und das Schalten in Abhängigkeit des tatsächlichen PV-Überschusses zum Einplanungszeitpunkt zu steuern. Ist &#039;&#039;&#039;power=0&#039;&#039;&#039; gesetzt, wird der Verbraucher unabhängig von einem zum Zeitpunkt des Zyklus-Start eventuell nicht ausreichend vorhandenem PV-Überschuss wie geplant eingeschaltet und somit der Zyklus gestartet.&lt;br /&gt;
&lt;br /&gt;
Durch die Schlüssel &#039;&#039;&#039;notbefore&#039;&#039;&#039; und &#039;&#039;&#039;notafter&#039;&#039;&#039; kann der Startzeitpunkt des Verbrauchers so gesteuert werden, dass der Start des Zyklus nicht vor bzw. nach der angegebenen Zeit eingeplant wird. Diese Steuerung bezieht sich auf die &#039;&#039;Planung&#039;&#039;, d.h. der eigentliche Start des Verbrauchers kann sich unter Umständen durch vorhandene Rahmenbedingungen verzögern. &lt;br /&gt;
&lt;br /&gt;
Der Schlüssel &#039;&#039;&#039;mintime&#039;&#039;&#039; legt die geplanten Einschaltzeit, d.h. die Laufzeit des Zyklus fest. Der Name &#039;&#039;&#039;mintime&#039;&#039;&#039; ist nicht als MInimum-Zeit zu deuten, sondern beschreibt die angegebene Zeit in Minuten die der Zyklus dauern soll.&lt;br /&gt;
&lt;br /&gt;
Dieser Schlüssel ist optional, da sich die Standard-Laufzeit des Verbrauchers von dessen Typ abgeleitet wird. Diese Standard Laufzeiten sind:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|dryer&lt;br /&gt;
|90 Minten&lt;br /&gt;
|-&lt;br /&gt;
|dishwasher&lt;br /&gt;
|180 Minuten&lt;br /&gt;
|-&lt;br /&gt;
|washingmachine&lt;br /&gt;
|120 Minuten&lt;br /&gt;
|-&lt;br /&gt;
|heater&lt;br /&gt;
|240 Minuten&lt;br /&gt;
|-&lt;br /&gt;
|charger&lt;br /&gt;
|120 Minuten&lt;br /&gt;
|-&lt;br /&gt;
|other&lt;br /&gt;
|60 Minuten&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
=== Der Verbraucher wurde nicht gestartet obwohl die Startzeit der Einplanung erreicht wurde ===&lt;br /&gt;
&lt;br /&gt;
Wenn die Startzeit des Consumers entsprechend der Einplanung erreicht oder überschritten ist, wird der Consumer eingeschaltet wenn die weiteren Voraussetzungen wahr sind:&lt;br /&gt;
&lt;br /&gt;
* es ist aktuell ein PV-Überschuss vorhanden und der PV-Überschuss deckt die kalkulierten Leistungsaufnahme des Verbrauchers ab. Mit den Schlüsseln &#039;&#039;&#039;power&#039;&#039;&#039; und &#039;&#039;&#039;spignorecond&#039;&#039;&#039; kann die Einschaltung trotz fehlendem PV-Überschuss ermöglicht werden. &lt;br /&gt;
&lt;br /&gt;
* die Angabe im Schlüssel &#039;&#039;&#039;swoncond&#039;&#039;&#039; ist wahr (sofern gesetzt)&lt;br /&gt;
&lt;br /&gt;
* das Schalten ist über den Schlüssel &#039;&#039;&#039;auto&#039;&#039;&#039; freigegeben (sofern gesetzt)&lt;br /&gt;
&lt;br /&gt;
* im Schlüssel &#039;&#039;&#039;on&#039;&#039;&#039; ist ein valides Einschaltkommando hinterlegt&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Sind diese zusätzlichen Bedingungen nicht erfüllt, erfolgt kein Start des eingeplanten Zyklus. Der Verbraucher verbleibt in diesem Fall weiterhin im Status &#039;&#039;planned&#039;&#039; oder &#039;&#039;suspended&#039;&#039;. Die Startbedingungen werden regelmäßig reviewed und der Consumer gestartet sobald alle Startbedingungen wahr sind bzw. Start verhindernde Bedingungen entfallen sind. &lt;br /&gt;
&lt;br /&gt;
Dieser Vorgang erfolgt bis das geplante Ende des Verbaucherzyklus erreicht ist oder die Bedingung im Schlüssel &#039;&#039;&#039;swoffcond&#039;&#039;&#039; zutrifft. Sobald die Bedingung im Schlüssel &#039;&#039;&#039;swoffcond&#039;&#039;&#039; wahr ist, wird der gesamte Planungszyklus des Consumers beendet.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
=== Kann manuell / automatisiert in den Planungszyklus eines Verbrauchers eingegriffen werden? ===&lt;br /&gt;
&lt;br /&gt;
Die Einplanung und die darauf aufbauenden Schaltprozesse laufen gemäß den hinterlegten Schlüsseloptionen im Modul automatatisch ab ohne dass ein Eingriff durch den Anwender erfolgen muß.&lt;br /&gt;
&lt;br /&gt;
Dennoch kann es gewünscht sein eine Neuplanung oder eine Sofortplanung eines Consumers vorzunehmen weil der Bedarf dazu besteht. Zu diesem Zweck stehen die Set-Kommandos&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* consumerNewPlanning &lt;br /&gt;
&lt;br /&gt;
* consumerImmediatePlanning &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
zur Verfügung. &lt;br /&gt;
Beide Befehle initiieren die Consumer-Neuplanung, wobei mit dem Befehl &#039;&#039;consumerImmediatePlanning&#039;&#039; die eventuell im consumerXX Attribut gesetzten Schlüssel notbefore, notafter bzw. mode werden nicht beachtet werden. Dagenen beachtet der Befehl &#039;&#039;consumerNewPlanning&#039;&#039; alle definierten Schlüsseloptionen und dient gewöhnlich dazu einen abgeschlossenen Verbraucherzyklus wiederholt ausführen zu lassen weil die aktuelle Witterungslage dazu geeignet ist. &lt;br /&gt;
&lt;br /&gt;
Ein Anwendungsbeispiel dafür ist zum Beispiel die Ausführung von mehreren Waschmaschinen-Zyklen an einem langen, sonnigen Sommertag.&lt;br /&gt;
Diese beiden Set-Befehle können zum Beispiel über ein auf einen Schalter/Taster reagierendes Notify oder DOIF Device ausgeführt werden. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Registrierung eines Verbrauchers mit getrennten Geräten für Messung und Schalten ===&lt;br /&gt;
&lt;br /&gt;
Es existieren in FHEM Geräte, die über verschiedene Kanäle für unterschiedliche Aufgaben verfügen. Diese Kanäle werden jeweils in einem gesonderten Device abgebildet. HomeMatic oder readingsProxy sind Beispiele für solche Varianten.&lt;br /&gt;
&lt;br /&gt;
Um solche Verbraucher einzubinden gibt es im consumerXX Attribut den Schlüssel &#039;&#039;&#039;switchdev&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Bei der Registrierung gibt man als Consumerdevice das Gerät für die Messung an und im Schüssel switchdev das entsprechende FHEM-Device, welches für die Schaltvorgänge benutzt wird. &lt;br /&gt;
Ist switchdev angegeben, beziehen sich die weiteren Schlüssel on, off, swstate, auto und asynchron auf dieses Gerät.&lt;br /&gt;
&lt;br /&gt;
In dem nachfolgenden Beispiel (Homematic) ist &#039;&#039;eg.az.fridge_Pwr&#039;&#039; FHEM Device für die Energiemessung. Die Schlüssel &#039;&#039;pcurr&#039;&#039; und &#039;&#039;etotal&#039;&#039; geben Readings in diesem Device an damit SolarForecast den Verbrauch und die Energiemengen auslesen kann. Demgegenüber ist im Schlüssel &#039;&#039;switchdev&#039;&#039; das zugehörige Schalter-Device der Kombination angegeben.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
eg.az.fridge_Pwr&lt;br /&gt;
type=noSchedule switchdev=eg.az.fridge_Sw power=0 icon=fridge pcurr=power:W:5 etotal=energyCalc:Wh &lt;br /&gt;
swstate=state:on:off auto=automatic&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Registrierung von Verbrauchern ohne Zeitplanung und aktivem Schalten ===&lt;br /&gt;
Manchmal besteht der Wunsch, Verbraucher in SolarForecast zu integrieren um lediglich folgende Sachverhalte abzubilden:&lt;br /&gt;
&lt;br /&gt;
* Visualisierung des Schaltstatus in der Consumer Legende und/oder der Energieflußgrafik&lt;br /&gt;
* Darstellung des aktuellen Energieverbrauchs in der Energieflußgrafik&lt;br /&gt;
* Erfassung der (externen) Schaltzeiten, d.h. der Zeiten der Gerätenutzung sowie der Verbrauchsdaten um deren Einfluß bei den Planungszeiten anderer Geräte durch SolarForecast berücksichtigen zu lassen&lt;br /&gt;
&lt;br /&gt;
In solchen Fällen werden bei der Registrierung die Schlüssel &#039;&#039;swstate, pcurr&#039;&#039; und &#039;&#039;etotal&#039;&#039; zur Erfassung der Schaltzustände und der Energieverbrauchs angegeben, jedoch zur Verhinderung der Zeitplanung durch SolarForecast der Schlüssel &#039;&#039;&#039;type=noSchedule&#039;&#039;&#039; gesetzt sowie die Schlüssel &#039;&#039;on&#039;&#039; und &#039;&#039;off&#039;&#039; &#039;&#039;&#039;nicht&#039;&#039;&#039; gesetzt.&lt;br /&gt;
&lt;br /&gt;
Das nachfolgende Beispiel zeigt eine mögliche Registrierung eines Zwischensteckers (Shelly) zur Erfassung der Daten eines Gefrierschrankes:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Shelly.shellyplug4&lt;br /&gt;
type=noSchedule power=65 asynchron=1&lt;br /&gt;
icon=gefrierschrank &lt;br /&gt;
swstate=state:.*on.*:.*off.* &lt;br /&gt;
pcurr=relay_0_power:W:5 etotal=relay_0_energy_Wh:Wh&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Beispiel Registrierung eines Shelly Devices ===&lt;br /&gt;
&lt;br /&gt;
Nachfolgendes Beispiel zeigt eine Registrierung eines Heizlüfters der an einem Shelly Zwischenstecker angeschlossen ist und durch das Modul gesteuert werden soll. Dabei soll die Steuerung nicht nur von der Solarprognose, sondern auch von der Raumtemperatur abhängig erfolgen. Im Beispiel wird das Attribut consumer03 verwendet.&lt;br /&gt;
&lt;br /&gt;
Zwingende Angaben sind&lt;br /&gt;
&lt;br /&gt;
 consumerXX &amp;lt;Device Name&amp;gt; type=&amp;lt;type&amp;gt; power=&amp;lt;power&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Alle Angaben können mehrzeilig eingegeben werden. Die Schlüssel-Werte Paare sind jeweils durch ein Leerzeichen getrennt.&lt;br /&gt;
&lt;br /&gt;
 consumerXX Shelly.shellyplug3 type=heater power=1700 &lt;br /&gt;
&lt;br /&gt;
In dem Beispiel ist Shelly.shellyplug3 der Devicename des Shellies in FHEM. Der Schlüssel &#039;&#039;&#039;type&#039;&#039;&#039; definiert die Art des Verbrauchers. In der Hilfe sind die möglichen Typen aufgeführt. Den richtigen Typ anzugeben hat Einfluß auf die spätere Einschätzung des Leistungsverhaltens über die Laufzeit. So wird von einem &#039;&#039;&#039;heater&#039;&#039;&#039; gleich nach dem Einschalten die angegebene Nominalleistung abgerufen und wird über die Zeit gleichbleiben. &lt;br /&gt;
Eine Waschmaschine oder ein Trockner rufen über ihre Laufzeit die Leistung nicht gleichbleibend ab und ändern die Leistungsaufnahme sich über die Einschaltdauer.&lt;br /&gt;
&lt;br /&gt;
Die Angabe von &#039;&#039;&#039;power&#039;&#039;&#039; definiert die Nominalleistung (Watt) die für den Verbraucher laut Typenschild vom Hersteller angegeben wird. Diese Angabe wird vom Modul bei der Planung der Einschaltzeiten verwendet indem die Nominalleistung in das Verhältnis zur Solarprognose bzw. Verbrauchsprognose des Netzes gesetzt wird. Weiterhin ist dieser Wert auch wichtig um später den tatsächlichen Einschaltzeitpunkt auszuführen wenn ein realer PV Überschuss festgestellt wird.&lt;br /&gt;
&lt;br /&gt;
Es ist möglich &#039;&#039;&#039;power=0&#039;&#039;&#039; zu setzen. Das führt dazu, dass die Planung und letztendlich auch der Schaltvorgang unabhängig von der Solarprognose bzw. einem realen PV Überschuss vorgenommen wird.  &lt;br /&gt;
&lt;br /&gt;
Es werden weitere Schlüsseleingaben vorgenommen:&lt;br /&gt;
&lt;br /&gt;
 Shelly.shellyplug3 type=heater power=1700 icon=vent_ventilation mode=can notbefore=09 mintime=SunPath:60:-60 on=on off=off&lt;br /&gt;
&lt;br /&gt;
Mit dem Schlüssel &#039;&#039;&#039;icon&#039;&#039;&#039; legt man ein Icon fest welches in der Grafik für den Verbraucher verwendet wird. Der &#039;&#039;&#039;mode&#039;&#039;&#039; definiert die Art und Weise der Einplanung:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
;can: Die Einplanung erfolgt zum Zeitpunkt mit wahrscheinlich genügend verfügbaren PV Überschuss. Der Start des Verbrauchers zum Planungszeitpunkt unterbleibt bei ungenügendem PV-Überschuss.&lt;br /&gt;
&lt;br /&gt;
;must: Der Verbaucher wird optimiert eingeplant auch wenn wahrscheinlich nicht genügend PV Überschuss vorhanden sein wird. Der Start des Verbrauchers erfolgt auch bei ungenügendem PV-Überschuss.  &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Mit &#039;&#039;&#039;notbefore&#039;&#039;&#039; wird festgelegt, dass die Einplanung nicht vor neun Uhr morgens erfolgen soll auch falls schon genügend PV Überschuss vorhanden wäre. Der Schlüssel &#039;&#039;&#039;mintime&#039;&#039;&#039; definiert die Einplanungsdauer des Verbrauchers in Minuten im einfachsten Fall. Die hier verwendete Angabe &#039;&#039;&#039;SunPath&#039;&#039;&#039; ist ein Spezialfall. Der Verbraucher soll von Sonnenaufgang bis Sonnenuntergang eingeschaltet werden, wobei die Angabe von 60 bzw. -60 ein relative Verschiebung bewirken. Dadurch wird das Einschalten 60 Mintuen nach Sonnenaufgang bis 60 Minuten vor Sonnenuntergang geplant.&lt;br /&gt;
&lt;br /&gt;
Die Schlüssel &#039;&#039;&#039;on&#039;&#039;&#039; und &#039;&#039;&#039;off&#039;&#039;&#039; teilen dem Modul die jeweiligen gültigen Ein- und Aus-Kommandos mit, mit dem das (Shelly)Device geschaltet werden kann. Werden diese Schlüssel nicht oder &amp;quot;leer&amp;quot; (on= off=) angegeben, erfolgt kein Schalten durch das Modul, nur die Planungsdaten werden erzeugt.&lt;br /&gt;
&lt;br /&gt;
Die Verbraucherregistrierung wird mit weiteren Angaben ergänzt um das gewünschte Verhalten zu erreichen:&lt;br /&gt;
&lt;br /&gt;
 Shelly.shellyplug3 type=heater power=1700 icon=vent_ventilation mode=can notbefore=09 mintime=SunPath:60:-60 on=on off=off etotal=relay_0_energy_Wh:Wh  &lt;br /&gt;
 pcurr=relay_0_power:W auto=automatic interruptable=og.bad.wandthermostat:diff-temp:[0-9]\.[0-9]:0.2&lt;br /&gt;
&lt;br /&gt;
In &#039;&#039;&#039;etotal&#039;&#039;&#039; wird der Readingname des Shelly Devices angegeben welches die Summe der verbrauchten Energie (Wh/kWh) des Consumer Device liefert. D.h. es muß ein sich stetig erhöhender Wert sein. Durch die Auswertung dieses Readings ermittelt das Modul die in bestimmten Zeiteinheiten verbrauchte Energie zur weiteren Verwendung. &lt;br /&gt;
&lt;br /&gt;
In dem Shelly Device ist per default ein solches Reading nicht vorhanden. Über userReadings kann in dem Device Shelly.shellyplug3 ein Reading für etotal erzeugt werden:&lt;br /&gt;
&lt;br /&gt;
 userReadings relay_0_energy_Wh:relay_0_energy.* monotonic { sprintf &amp;quot;%.0f&amp;quot;, ReadingsVal ($name, &#039;relay_0_energy&#039;, 0) / 60 } &lt;br /&gt;
&lt;br /&gt;
Der Schlüssel &#039;&#039;&#039;pcurr&#039;&#039;&#039; enthält das Reading in Shelly.shellyplug3 welches den aktuellen Energieverbrauch liefert. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;auto&#039;&#039;&#039; enthält das Reading in Shelly.shellyplug3 welches zur Freigabe/Sperrung der autmatischen Schaltung durch das Modul dienen soll. Ist das angegebene Reading (im Beispiel &amp;quot;automatic&amp;quot;) im Shelly.shellyplug3 nicht vorhanden, wird es vom Modul automatisch mit dem Wert &amp;quot;1&amp;quot; angelegt.&lt;br /&gt;
Dadurch ist per default das automatische Schalten von Shelly.shellyplug3 durch das Modul freigegeben. &lt;br /&gt;
Der User kann durch Setzen des Readings &#039;&#039;&#039;automatic=0&#039;&#039;&#039; das automatische Schalten durch das Modul sperren und mit &amp;quot;1&amp;quot; wieder freigeben. Dadurch kann man zu bestimmten Zeiten (Urlaub, Feiertage, etc.) die Schaltung temporär deaktivieren.&lt;br /&gt;
&lt;br /&gt;
Wie oben beschrieben, soll der Heizlüfter als weitere Schaltbedingung die Abhängigkeit von der Raumtemperatur beachten. Konkret soll der Heizlüfter mit einer Hysterese von 0.2 (Grad) bei Erreichen einer Soll-Raumtemperatur ausschalten und bei Unterschreiten einer bestimmten Temperatur einschalten.&lt;br /&gt;
Der Schlüssel &#039;&#039;&#039;interruptable&#039;&#039;&#039; übernimmt diese temporäre Schaltsequenzen.&lt;br /&gt;
&lt;br /&gt;
Zunächst wird in dem Sensordevice (In dem Beispiel ein Homatic Wandthermostat HM-TC-IT-WM-W-EU) ein Reading erstellt welches dann im Schlüssel &#039;&#039;&#039;interruptable&#039;&#039;&#039; angegeben wird. Dieses Reading soll einen Wert enthalten auf den der angegebene Regex ([0-9]\.[0-9]) matchen soll um Shelly.shellyplug3 temporär auszuschalten.&lt;br /&gt;
Im Wandthermostat wird dazu ein userReading angelegt:&lt;br /&gt;
&lt;br /&gt;
 userReadings diff-temp:desired-temp.* { &lt;br /&gt;
     sprintf &amp;quot;%.1f&amp;quot;, ReadingsVal ($name, &#039;measured-temp&#039;, 0) - ReadingsVal ($name, &#039;desired-temp&#039;, 0) &lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Das bedeutet, wenn die Raumtemperatur die Solltemperatur erreicht oder höher ist, wird diff-temp &amp;gt;= 0.&lt;br /&gt;
In diesem Fall matcht der angebene Regex in:&lt;br /&gt;
&lt;br /&gt;
 interruptable=og.bad.wandthermostat:diff-temp:&#039;&#039;&#039;[0-9]\.[0-9]&#039;&#039;&#039;:0.2&lt;br /&gt;
&lt;br /&gt;
und Shelly.shellyplug3 wird ausgeschaltet. &lt;br /&gt;
Unterschreitet der Wert von diff-temp 0, matcht der Regex nicht mehr und der Verbraucher wird wieder eingeschaltet. Dabei wird die angegebene Hysterese berücksichtigt, d.h der Verbraucher wird erst ausgeschaltet wenn &amp;quot;diff-temp - 0.2 &amp;gt;= 0&amp;quot; wahr ist.&lt;br /&gt;
&lt;br /&gt;
Für das Wiedereinschalten des Heizlüfters ist außerdem Voraussetzung, dass ein entsprechender PV-Überschuss vorliegt. Diese Bedingung wird durch die Angabe von &#039;&#039;&#039;power=1700&#039;&#039;&#039; bewirkt. Soll der zwangsweise PV Überschuss ignoriert werden, kann &#039;&#039;&#039;power=0&#039;&#039;&#039; angegeben werden. Alternativ kann die Berücksichtigung des zwangsweisen PV Überschuss mit dem Schlüssel &amp;quot;spignorecond&amp;quot; im Consumer-Attribut ausgesteuert werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== zusätzliche Readings bzw. Erzeugung von statistischen Readings ==&lt;br /&gt;
&lt;br /&gt;
Zusätzlich zu den zahlreichen im Modul per Standard erzeugten Readings kann der Anwender weitere Readings generieren lassen.&lt;br /&gt;
Diese zusätzlichen Readings geben entweder einen bestimmten Sachverhalt (Laufzeit, Status der API-Abfragen, etc.) oder einen statistischen Wert, z.B. die Batterieentladung des aktuellen Tages, wieder.&lt;br /&gt;
&lt;br /&gt;
Die zusätzlich erstellten Readings sind durch den Präfix &#039;&#039;&#039;special_&#039;&#039;&#039; im Device gekennzeichnet.&lt;br /&gt;
&lt;br /&gt;
Diese zusätzlichen Informationen können durch das Setzen einer oder mehrerer Kennzeichen im Attribut &#039;&#039;&#039;ctrlSpecialReadings&#039;&#039;&#039; erzeugt werden. Die mögliche Auswahl wird je nach Bedarf ausgebaut und umfasst zum aktuellen Zeitpunkt (07.02.2025) diese Möglichkeiten:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
;BatPowerIn_Sum: 	die Summe der momentanen Batterieladeleistung aller definierten Batterie Geräte&lt;br /&gt;
;BatPowerOut_Sum: 	die Summe der momentanen Batterieentladeleistung aller definierten Batterie Geräte&lt;br /&gt;
;allStringsFullfilled: 	Erfüllungsstatus der fehlerfreien Generierung aller Strings&lt;br /&gt;
;conForecastTillNextSunrise: 	Verbrauchsprognose von aktueller Stunde bis zum kommenden Sonnenaufgang&lt;br /&gt;
;currentAPIinterval: 	das aktuelle Abrufintervall der gewählten Strahlungsdaten-API in Sekunden&lt;br /&gt;
;currentRunMtsConsumer_XX: 	die Laufzeit (Minuten) des Verbrauchers &amp;quot;XX&amp;quot; seit dem letzten Einschalten. (letzter Laufzyklus)&lt;br /&gt;
;dayAfterTomorrowPVforecast: 	liefert die Vorhersage der PV Erzeugung für Übermorgen (sofern verfügbar) ohne Autokorrektur (Rohdaten).&lt;br /&gt;
;daysUntilBatteryCare_XX: 	Tage bis zur nächsten Batterie XX Pflege (Erreichen der Ladung &#039;maxSoC&#039; aus Attribut ctrlBatSocManagementXX)&lt;br /&gt;
;lastretrieval_time: 	der letzte Abrufzeitpunkt der gewählten Strahlungsdaten-API&lt;br /&gt;
;lastretrieval_timestamp: 	der Timestamp der letzen Abrufzeitpunkt der gewählten Strahlungsdaten-API&lt;br /&gt;
;response_message: 	die letzte Statusmeldung der gewählten Strahlungsdaten-API&lt;br /&gt;
;runTimeAvgDayConsumer_XX: 	die durchschnittliche Laufzeit (Minuten) des Verbrauchers &amp;quot;XX&amp;quot; an einem Tag&lt;br /&gt;
;runTimeCentralTask: 	die Laufzeit des letzten SolarForecast Intervalls (Gesamtprozess) in Sekunden&lt;br /&gt;
;runTimeTrainAI: 	die Laufzeit des letzten KI Trainingszyklus in Sekunden&lt;br /&gt;
;runTimeLastAPIAnswer: 	die letzte Antwortzeit des Strahlungsdaten-API Abrufs auf einen Request in Sekunden&lt;br /&gt;
;runTimeLastAPIProc: 	die letzte Prozesszeit zur Verarbeitung der empfangenen Strahlungsdaten-API Daten&lt;br /&gt;
;SunMinutes_Remain: 	die verbleibenden Minuten bis Sonnenuntergang des aktuellen Tages&lt;br /&gt;
;SunHours_Remain: 	die verbleibenden Stunden bis Sonnenuntergang des aktuellen Tages&lt;br /&gt;
;todayConsumptionForecast: 	Verbrauchsprognose pro Stunde des aktuellen Tages (01-24)&lt;br /&gt;
;todayConForecastTillSunset: 	Verbrauchsprognose von aktueller Stunde bis Stunde vor Sonnenuntergang&lt;br /&gt;
;todayDoneAPIcalls: 	die Anzahl der am aktuellen Tag ausgeführten Strahlungsdaten-API Calls&lt;br /&gt;
;todayDoneAPIrequests: 	die Anzahl der am aktuellen Tag ausgeführten Strahlungsdaten-API Requests&lt;br /&gt;
;todayConsumption: 	der gesamte Energieverbrauch (Wh) des Hauses am aktuellen Tag&lt;br /&gt;
;todayGridConsumption: 	die aus dem öffentlichen Netz bezogene Energie am aktuellen Tag&lt;br /&gt;
;todayGridFeedIn: 	die in das öffentliche Netz eingespeiste PV Energie am aktuellen Tag&lt;br /&gt;
;todayMaxAPIcalls: 	die maximal mögliche Anzahl Strahlungsdaten-API Calls. Ein Call kann mehrere API Requests enthalten.&lt;br /&gt;
;todayRemainingAPIcalls: 	die Anzahl der am aktuellen Tag noch möglichen Strahlungsdaten-API Calls&lt;br /&gt;
;todayRemainingAPIrequests: 	die Anzahl der am aktuellen Tag noch möglichen Strahlungsdaten-API Requests&lt;br /&gt;
;todayBatIn_XX: 	die am aktuellen Tag in die Batterie XX geladene Energie&lt;br /&gt;
;todayBatInSum: 	Summe der am aktuellen Tag in alle Batterien geladene Energie&lt;br /&gt;
;todayBatOut_XX: 	die am aktuellen Tag aus der Batterie XX entnommene Energie&lt;br /&gt;
;todayBatOutSum: 	Summe der am aktuellen Tag aus allen Batterien entnommene Energie&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Wichtig:&#039;&#039;&#039; Manche Angaben sind im Setup des Moduls optional, d.h. sie müssen nicht zwingend beim Setup angegeben werden. Allerdings sind diese Angaben für die Erstellung eines zusätzlichen Readings unter Umständen Voraussetzung. Es empfiehlt sich deshalb bei Setup so viele Informationen wie möglich zu übermitteln.&lt;br /&gt;
&lt;br /&gt;
So ist zum Beispiel für die sinnvolle Erstellung der Informationen &#039;&#039;&#039;todayBatInSum&#039;&#039;&#039; und &#039;&#039;&#039;todayBatOutSum&#039;&#039;&#039; (Readings special_todayBatInSum und special_todayBatOutSum) das Vorhandensein der Schlüssel &#039;&#039;intotal&#039;&#039; und &#039;&#039;outtotal&#039;&#039; im Attribut setupBatteryDevXX notwendig (Beispiel):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
attr &amp;lt;Name&amp;gt; setupBatteryDev01 MQTT2_cerboGX_c0619ab34e08_battery &lt;br /&gt;
                              pin=BatIn:W pout=BatOut:W charge=SOC_value intotal=BatInTotal:Wh outtotal=BatOutTotal:Wh &lt;br /&gt;
                              cap=InstalledCapacity_Wh:Wh asynchron=1 show=1&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Hinweise zur Fehlersuche ==&lt;br /&gt;
Das Modul SolarForecast ist sehr komplex und es die verschiedensten Datenquellen ausgewertet, zusammengeführt und daraus Ergebnisse berechnet und/oder visualisiert. Wird etwas nicht wie erwartet durch das Modul geliefert, können die Ursachen vielfältig sein. Natürlich kann ein Fehler im Modulcode vorliegen, oftmals gibt es aber andere Ursachen.&lt;br /&gt;
&lt;br /&gt;
Die nachfolgenden Hinweise sollen Hilfe zur Selbsthilfe geben und sind eine Zusammenfassung von realen Fällen die mit dem beschriebenen Wegen und Verfahren gelöst wurden.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Die erwartete erzeugte PV-Energie ist im Modul zu gering und wird im Balkendiagramm nicht angezeigt === &lt;br /&gt;
&lt;br /&gt;
Die erzeugte PV-Energie wird nicht richtig angezeigt: Am Vormittag sind keine Werte vorhanden und, wenn dann etwas kommt, ist die Anzeige zu gering. Für 13 und 14 Uhr wurden ca. 5.000Wh erwartet (im Vergleich mit den Daten aus einer anderen Auswertungsquelle). &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Lösungsweg:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Aus dem List des Devices wurden die für den Fall wesentlichen Readings herausgezogen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    2025-02-02 08:59:55  Today_Hour09_PVforecast 152 Wh&lt;br /&gt;
    2025-02-02 08:59:55  Today_Hour09_PVreal 0 Wh&lt;br /&gt;
&lt;br /&gt;
    2025-02-02 09:59:55  Today_Hour10_PVforecast 1400 Wh&lt;br /&gt;
    2025-02-02 09:59:55  Today_Hour10_PVreal 0 Wh&lt;br /&gt;
&lt;br /&gt;
    2025-02-02 10:59:55  Today_Hour11_PVforecast 2500 Wh&lt;br /&gt;
    2025-02-02 10:59:55  Today_Hour11_PVreal 0 Wh&lt;br /&gt;
&lt;br /&gt;
    2025-02-02 11:59:55  Today_Hour12_PVforecast 4392 Wh&lt;br /&gt;
    2025-02-02 11:59:55  Today_Hour12_PVreal 0 Wh&lt;br /&gt;
&lt;br /&gt;
    2025-02-02 12:59:54  Today_Hour13_PVforecast 4000 Wh&lt;br /&gt;
    2025-02-02 12:59:54  Today_Hour13_PVreal 300 Wh&lt;br /&gt;
&lt;br /&gt;
    2025-02-02 13:59:54  Today_Hour14_PVforecast 6415 Wh&lt;br /&gt;
    2025-02-02 13:59:54  Today_Hour14_PVreal 3200 Wh&lt;br /&gt;
&lt;br /&gt;
    2025-02-02 14:59:54  Today_Hour15_PVforecast 5629 Wh&lt;br /&gt;
    2025-02-02 14:59:54  Today_Hour15_PVreal 2700 Wh&lt;br /&gt;
&lt;br /&gt;
    2025-02-02 15:59:54  Today_Hour16_PVforecast 3958 Wh&lt;br /&gt;
    2025-02-02 15:59:54  Today_Hour16_PVreal 900 Wh&lt;br /&gt;
&lt;br /&gt;
    2025-02-02 16:31:24  Today_Hour17_PVforecast 945 Wh&lt;br /&gt;
    2025-02-02 16:31:24  Today_Hour17_PVreal 500 Wh&lt;br /&gt;
   &lt;br /&gt;
    2025-02-02 16:31:24  Today_Hour18_PVforecast 59 Wh&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Man sieht dass bis 12:00 keine erzeugte Energie vom WR gemeldet wird. Deswegen wird in der Balkengrafik nichts dargestellt. Auch danach bleibt die registrierte PV-Energie hinter den Erwartungen zurück. Zuständig für die Lieferung der PV-Energie an das Modul ist das Reading im Schlüssel:&lt;br /&gt;
&lt;br /&gt;
 setupInverterDev01 SH10rt pv=Total_DC_Power:W &amp;lt;mark&amp;gt;etotal=Total_Export_Energy_from_PV:kWh&amp;lt;/mark&amp;gt; capacity=10000 strings=Hauptdach,Flachdach&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Welche Änderungen es zwischen den Stunden gab, sieht man mit einem &amp;quot;get ... pvHistory XX&amp;quot; (&amp;quot;XX&amp;quot; steht für das Datum, d.h. den Tag, z.B. 02). &amp;lt;br&amp;gt;&lt;br /&gt;
Hier ein Beispiel für die Stunden 10 (9:00 - 9:59) und 11 (10:00 - 10:59). &amp;lt;br&amp;gt;&lt;br /&gt;
Wichtig ist hier der Key etotali01 für den ersten Inverter. Die Differenz zwischen den Werten beider Stunden ist die in der Stunde erzeugte Energie (hier 380 Wh). Diese 380 Wh findest du auch im Key pvrl01 als PVReal für diese Stunde. Das Modul rechnet intern immer mit Wh, kWh werden vorher in Wh umgerechnet.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
10 =&amp;gt; pvfc: 365, pvrl: 530, pvrlvd: 1, rad1h: -&lt;br /&gt;
&lt;br /&gt;
            &amp;lt;mark&amp;gt;etotali01: 63364714&amp;lt;/mark&amp;gt;, etotali02: 3044110, etotali03: -&lt;br /&gt;
&lt;br /&gt;
            &amp;lt;mark&amp;gt;pvrl01: 380&amp;lt;/mark&amp;gt;, pvrl02: 150, pvrl03: -&lt;br /&gt;
&lt;br /&gt;
            etotalp01: -, etotalp02: -, etotalp03: -&lt;br /&gt;
&lt;br /&gt;
            pprl01: -, pprl02: -, pprl03: -&lt;br /&gt;
&lt;br /&gt;
            confc: 574, con: 518, gcons: 22, conprice: 0.2958&lt;br /&gt;
&lt;br /&gt;
            gfeedin: 1, feedprice: 0.1269&lt;br /&gt;
&lt;br /&gt;
            DoN: 1, sunaz: 137, sunalt: 12&lt;br /&gt;
&lt;br /&gt;
....&lt;br /&gt;
&lt;br /&gt;
            &lt;br /&gt;
&lt;br /&gt;
      11 =&amp;gt; pvfc: 2966, pvrl: 1862, pvrlvd: 1, rad1h: -&lt;br /&gt;
&lt;br /&gt;
            &amp;lt;mark&amp;gt;etotali01: 63365094&amp;lt;/mark&amp;gt;, etotali02: 3044260, etotali03: -&lt;br /&gt;
&lt;br /&gt;
            pvrl01: 1392, pvrl02: 470, pvrl03: -&lt;br /&gt;
&lt;br /&gt;
            etotalp01: -, etotalp02: -, etotalp03: -&lt;br /&gt;
&lt;br /&gt;
            pprl01: -, pprl02: -, pprl03: -&lt;br /&gt;
&lt;br /&gt;
            confc: 774, con: 609, gcons: 25, conprice: 0.2958&lt;br /&gt;
&lt;br /&gt;
            gfeedin: 4, feedprice: 0.1269&lt;br /&gt;
           &lt;br /&gt;
....&lt;br /&gt;
&lt;br /&gt;
D.h. wenn zwischen den beiden Stunden keine Differenzen zu finden sind, liegt es sehr wahrscheinlich am Input des Readings:&lt;br /&gt;
&lt;br /&gt;
 etotal=Total_Export_Energy_from_PV:kWh&lt;br /&gt;
&lt;br /&gt;
welches sich in den ersten Stunden nicht ändert und danach auch nur wenig.&lt;br /&gt;
&lt;br /&gt;
Man kann mit dem Attribut:&lt;br /&gt;
&lt;br /&gt;
 ctrlDebug=collectData&lt;br /&gt;
&lt;br /&gt;
die Datensammlung verfolgen (leider entstehen viele Daten im Log). Man sieht welche Werte von dem Inverter/Reading geliefert werden:&lt;br /&gt;
 &lt;br /&gt;
 ....&lt;br /&gt;
 2025.02.02 19:10:10.558 1: SolCast DEBUG&amp;gt; collect Inverter 01 data - device: STP_5000, delivery: default =&amp;gt;&lt;br /&gt;
 2025.02.02 19:10:10.558 1: SolCast DEBUG&amp;gt; pv: 0 W, &amp;lt;mark&amp;gt;etotal: 63378810 Wh&amp;lt;/mark&amp;gt;&lt;br /&gt;
 ....&lt;br /&gt;
&lt;br /&gt;
Dieser Wert muß bei PV Erzeugung kontinuierlich hochzählen. Wenn nicht, muss das angegebene Reading, d.h. die Quelle überprüft werden.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ergebnis der Prüfung:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Tatsächlich wurde ein Reading benutzt, das ungeeignet war: Statt &amp;quot;Total_PV_Generation&amp;quot; wurde &amp;quot;Total_export_energy_from_PV&amp;quot; genutzt. In diesem Reading wurde immer die in die Batterie gespeicherte Energie von der gesamten erzeugten Energie abgezogen. Daher war auch vormittags alles 0, da in dieser Periode alle PV-Energie in die Batterie geladen wurde bzw. in weiteren Stunden ein Anteil der dann bei dem PV-Erzeugungswert fehlte.&lt;br /&gt;
&lt;br /&gt;
Nachdem die Ursache beseitigt war, zeigt SolarForecast wieder die erwarteten PV-Erzeugungswerte an.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
== Konfigurationsbeispiele ==&lt;br /&gt;
&lt;br /&gt;
=== Visualisierung solare Vorhersage und reale Erzeugung ===&lt;br /&gt;
&lt;br /&gt;
Zu Beginn jedes neuen Tages gegen 00:00 Uhr werden durch das SolarForecast Device Events für die initiale PV Prognose des kommenden Tages erstellt.&lt;br /&gt;
Der Readingteil dieser Events heißt&lt;br /&gt;
&lt;br /&gt;
 AllPVforecastsToEvent&lt;br /&gt;
&lt;br /&gt;
Die Uhrzeiten der Events sind so aufbereitet und können direkt geloggt werden um sie in einen SVG Plot zu integrieren.&lt;br /&gt;
Im SolarForecast Device ist dieses Reading nicht sichtbar, nur die Events werden erstellt.&lt;br /&gt;
&lt;br /&gt;
Die meteorologischen Bedingungen verändern sich über den Tag permanent. Je nach gewählter Strahlungsdatenquelle (eine API oder DWD Device) erfolgt eine mehr oder weniger dynamische Anpassung der Prognose an die sich verändernde Umwelt.&lt;br /&gt;
&lt;br /&gt;
Aktuelle Prognosedaten sowie die reale PV Erzeugung der vergangenen Stunde können über die Readings&lt;br /&gt;
&lt;br /&gt;
 LastHourPVforecast&lt;br /&gt;
 LastHourPVreal&lt;br /&gt;
&lt;br /&gt;
[[Datei:Screenshot 2024-10-20 134050.png|right|thumb|400px|Übersicht solare Vorhersage]]&lt;br /&gt;
geloggt werden. &lt;br /&gt;
&lt;br /&gt;
Werden alle drei Werte in einem SVG kombiniert, kann die Entwicklung der Prognose über den Tag und die Beziehung von Prognose zu realer PV Erzeugnung pro Stunde visualisiert werden.&lt;br /&gt;
&lt;br /&gt;
Für das rechts abgebildete Beispiel des Plots aus einer Datenbank &amp;quot;LogDBShort&amp;quot; sieht das gplot-File wie folgt aus:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# Created by FHEM/98_SVG.pm, 2024-03-08 15:05:31&lt;br /&gt;
set terminal png transparent size &amp;lt;SIZE&amp;gt; crop&lt;br /&gt;
set output &#039;&amp;lt;OUT&amp;gt;.png&#039;&lt;br /&gt;
set xdata time&lt;br /&gt;
set timefmt &amp;quot;%Y-%m-%d_%H:%M:%S&amp;quot;&lt;br /&gt;
set xlabel &amp;quot; &amp;quot;&lt;br /&gt;
set title &#039;&amp;lt;TL&amp;gt;&#039;&lt;br /&gt;
set ytics &lt;br /&gt;
set y2tics &lt;br /&gt;
set grid&lt;br /&gt;
set ylabel &amp;quot;Wh&amp;quot;&lt;br /&gt;
set y2label &amp;quot;Wh&amp;quot;&lt;br /&gt;
set yrange [0:7500]&lt;br /&gt;
set y2range [0:7500]&lt;br /&gt;
&lt;br /&gt;
#LogDBShort SolCast:LastHourPVforecast:::&lt;br /&gt;
#LogDBShort SolCast:LastHourPVreal:::&lt;br /&gt;
#LogDBShort SolCast:AllPVforecastsToEvent:::&lt;br /&gt;
&lt;br /&gt;
plot &amp;quot;&amp;lt;IN&amp;gt;&amp;quot; using 1:2 axes x1y2 title &#039;aktuelle PV Vorhersage&#039; ls l6fill lw 1 with lines,\&lt;br /&gt;
     &amp;quot;&amp;lt;IN&amp;gt;&amp;quot; using 1:2 axes x1y2 title &#039;reale PV Erzeugung&#039; ls l2fill lw 1 with lines,\&lt;br /&gt;
     &amp;quot;&amp;lt;IN&amp;gt;&amp;quot; using 1:2 axes x1y2 title &#039;initiale PV Vorhersage&#039; ls l4 lw 1 with lines&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Hinweis:&#039;&#039;&#039; In manchen Fällen werden die Vorhersagewerte im SVG-Plot nicht angezeigt wenn FileLog verwendet wird. Ein Setzen des Attribute &amp;quot;fixedrange=3days +1&amp;quot; im SVG löst das Problem. Die Definition des SVG-Devices:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
define SVG_LogDBShort_SolCast SVG LogDBShort:SVG_LogDBShort_SolCast:HISTORY&lt;br /&gt;
attr SVG_LogDBShort_SolCast fixedrange 3days +1&lt;br /&gt;
attr SVG_LogDBShort_SolCast room Energie-&amp;gt;SolarPrognose&lt;br /&gt;
attr SVG_LogDBShort_SolCast sortby 2&lt;br /&gt;
attr SVG_LogDBShort_SolCast title &amp;quot;Übersicht solare Vorhersage&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Technisch bedingt werden an einem Tag X kurz nach 00:00 Uhr die am Vortag für den Tag X erzeugten Events aktualisiert. Diese Daten erzeugen in der Datenbank einen zweiten Satz an Daten mit dem gleichen Timestamp was für die Anzeige unvorteilhaft ist. &lt;br /&gt;
Um nur die aktualisierten Events von &#039;&#039;&#039;AllPVforecastsToEvent&#039;&#039;&#039; in der Datenbank zu erhalten, bietet sich ein DbRep-Device an. Die nachfolgende Vorschlagsdefinition kann per at-Device jeden Tag kurz vor Mitternacht (z.B. 23:57:10) ausgeführt werden. Es werden immer die in der Vergangenheit geschriebenen (veralteten) Daten aus der Datenbank gelöscht, doppelte Datesätze vermieden und immer die aktuellste initiale Prognose gespeichert:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
define Rep.Del.AllPVforecastsToEvent DbRep LogDBShort&lt;br /&gt;
attr Rep.Del.AllPVforecastsToEvent alias Löschen Readings AllPVforecastsToEvent des Folgetages&lt;br /&gt;
attr Rep.Del.AllPVforecastsToEvent comment ermöglicht dass die Readings AllPVforecastsToEvent am Folgetag wieder neu und aktuell geschrieben werden können&lt;br /&gt;
attr Rep.Del.AllPVforecastsToEvent devStateIcon initialized:control_3dot_hor_s connected:10px-kreis-gelb .*disconnect:10px-kreis-rot .*done:10px-kreis-gruen&lt;br /&gt;
attr Rep.Del.AllPVforecastsToEvent disable 0&lt;br /&gt;
attr Rep.Del.AllPVforecastsToEvent event-on-update-reading state&lt;br /&gt;
attr Rep.Del.AllPVforecastsToEvent icon edit_delete&lt;br /&gt;
attr Rep.Del.AllPVforecastsToEvent room Datenbank-&amp;gt;Produktiv&lt;br /&gt;
attr Rep.Del.AllPVforecastsToEvent showproctime 1&lt;br /&gt;
attr Rep.Del.AllPVforecastsToEvent device TYPE=SolarForecast&lt;br /&gt;
attr Rep.Del.AllPVforecastsToEvent timestamp_begin next_day_begin&lt;br /&gt;
attr Rep.Del.AllPVforecastsToEvent timestamp_end next_day_end&lt;br /&gt;
attr Rep.Del.AllPVforecastsToEvent verbose 2&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das angegebene DbLog-Device &#039;&#039;&#039;LogDBShort&#039;&#039;&#039; ist natürlich anzupassen.&lt;br /&gt;
&lt;br /&gt;
Das at-Device zum Starten der Datenbankbereinigung sieht folgendermaßen aus:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
define At.Del.AllPVforecastsToEvent at *23:58:10 set Rep.Del.AllPVforecastsToEvent delEntries&lt;br /&gt;
attr At.Del.AllPVforecastsToEvent alias Start Löschen Readings AllPVforecastsToEventn LogDBShort&lt;br /&gt;
attr At.Del.AllPVforecastsToEvent icon clock&lt;br /&gt;
attr At.Del.AllPVforecastsToEvent room Datenbank-&amp;gt;Produktiv&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Natürlich sind auch hier die eigenen Devicenamen entsprechend anzupassen.&lt;br /&gt;
&lt;br /&gt;
Ab SolarForecast Version 1.51.7 kann die Eventerzeugung für bestimmte SVG Plot-Typen optimiert werden. &amp;lt;br&amp;gt; &lt;br /&gt;
Die Aktivierung dieser Optimierungen ist in der Online Hilfe zum Attribut &#039;&#039;&#039;plantControl-&amp;gt;genPVforecastsToEvent&#039;&#039;&#039; beschrieben. Bei Nutzung des Attributes plantControl-&amp;gt;genPVforecastsToEvent ist ebenfalls das Attribut &#039;&#039;&#039;event-on-update-reading=AllPVforecastsToEvent&#039;&#039;&#039; zu setzen.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Beispielkonfiguration 1 ===&lt;br /&gt;
Dies ist eine Konfiguration mit &lt;br /&gt;
&lt;br /&gt;
SummenDummy für 2 BatterieWR (Namen : SBS25 / SBS25_2)&lt;br /&gt;
&lt;br /&gt;
SummenDummy für 3 PV-Wechselrichter (Namen : SB25 / SB30 / SB40)&lt;br /&gt;
&lt;br /&gt;
            mit verschiedenen InverterStrings / ModulDirection / ModulTiltAngle, ModulPeakString&lt;br /&gt;
&lt;br /&gt;
und auch mit den notwendigen zugehörigen anderen Modul-Konfigs.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ACHTUNG:&#039;&#039;&#039; Zusätzlich enthalten ist bei dem Beispiel-Notify eine Sonderkonstellation für eine Brennstoffzelle &amp;quot;FCU&amp;quot; als weitere bzw. zusätzliche Stromerzeugungsquelle. Diese &amp;quot;FCU&amp;quot; wird dadurch mit in der Grafik mit deren Erzeugungsleistung Tag und Nacht in der Erzeugersumme (am Symbol = Sonne) berücksichtigt.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;(ReadingsVal(&amp;quot;FCU&amp;quot;,&amp;quot;FCU-Strom-aktuelle-Leistung&amp;quot;,0)/1000)&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
==== DWD ====&lt;br /&gt;
Bitte dabei diese DWD-Version aus dem [https://svn.fhem.de/trac/browser/trunk/fhem/contrib/DS_Starter Contrib] von DS_Starter dazu nutzen&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|55_DWD_OpenData.pm&lt;br /&gt;
|136.2 KB ​&lt;br /&gt;
|29260  &lt;br /&gt;
|DS_Starter&lt;br /&gt;
|55_DWD_OpenData: contrib 1.17.7&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
define DWD DWD_OpenData&lt;br /&gt;
attr DWD downloadTimeout 120&lt;br /&gt;
attr DWD comment Im DWD Wetterdevice verwendet Solarforecast : \&lt;br /&gt;
TTT,Neff,RR1c,ww,SunUp,SunRise,SunSet \&lt;br /&gt;
zusätzlich ist jedoch auch noch Rad1h notwendig \&lt;br /&gt;
wenn dieses DWD-Device als Strahlungsdevice genutzt wird.\&lt;br /&gt;
Die Station 10418 (Lüdenscheid) überträgt aktuell am 09.03.2025 (noch) Rad1h.\&lt;br /&gt;
attr DWD downloadTimeout 120&lt;br /&gt;
attr DWD forecastDays 7&lt;br /&gt;
attr DWD forecastProperties SunUp, SunRise, SunSet, Rad1h, R101, RR1c, TTT, Tx, Tn, Tg, DD, FX1, RR6c, R600, RRhc, Rh00, ww, wwd, Neff&lt;br /&gt;
attr DWD forecastRefresh 1&lt;br /&gt;
attr DWD forecastResolution 1&lt;br /&gt;
attr DWD forecastStation 10418&lt;br /&gt;
attr DWD forecastWW2Text 1&lt;br /&gt;
attr DWD group Umwelt&lt;br /&gt;
attr DWD icon rc_WEB&lt;br /&gt;
attr DWD room 021_DWD&lt;br /&gt;
attr DWD stateFormat Tomorrow Tmax fc1_Tx °C on fc1_date at fc_description  -(state fc_time)&lt;br /&gt;
attr DWD verbose 2&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== InverterDummy ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod InverterDummy dummy&lt;br /&gt;
attr InverterDummy event-on-change-reading .*&lt;br /&gt;
attr InverterDummy group Energy Meter&lt;br /&gt;
attr InverterDummy icon measure_photovoltaic_inst@green&lt;br /&gt;
attr InverterDummy room 020_PV,Energie&lt;br /&gt;
attr InverterDummy stateFormat {sprintf(&amp;quot;current %9.3f kW    Today_PVforecast  %9.3f kWh      Today_PV %9.3f kWh      Total_PV %9.3f kWh&amp;quot;,\&lt;br /&gt;
ReadingsVal($name,&amp;quot;total_pac&amp;quot;,0)/1,\&lt;br /&gt;
ReadingsNum(&amp;quot;Forecast&amp;quot;,&amp;quot;Today_PVforecast&amp;quot;,0)/1000,\&lt;br /&gt;
ReadingsVal($name,&amp;quot;etoday&amp;quot;,0)/1,\&lt;br /&gt;
ReadingsVal($name,&amp;quot;etotal&amp;quot;,0)/1,)}&lt;br /&gt;
attr InverterDummy verbose 2&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== SMA_Energymeter ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod SMA_Energymeter SMAEM&lt;br /&gt;
attr SMA_Energymeter DbLogExclude state&lt;br /&gt;
attr SMA_Energymeter diffAccept 50&lt;br /&gt;
attr SMA_Energymeter disable 0&lt;br /&gt;
attr SMA_Energymeter disableSernoInReading 1&lt;br /&gt;
attr SMA_Energymeter event-on-update-reading state,Saldo_Wirkleistung,Bezug_Wirkleistung,Einspeisung_Wirkleistung,Bezug_Wirkleistung_Zaehler,Einspeisung_Wirkleistung_Zaehler&lt;br /&gt;
attr SMA_Energymeter feedinPrice 0.08&lt;br /&gt;
attr SMA_Energymeter group Energy Meter&lt;br /&gt;
attr SMA_Energymeter icon measure_power@green&lt;br /&gt;
attr SMA_Energymeter interval 15&lt;br /&gt;
attr SMA_Energymeter powerCost 0.25&lt;br /&gt;
attr SMA_Energymeter room 015_Zaehler,020_PV,Energie&lt;br /&gt;
attr SMA_Energymeter serialNumber XXXXXXXXXX&lt;br /&gt;
attr SMA_Energymeter stateFormat state W (IN -) P1: L1_Saldo_Wirkleistung P2: L2_Saldo_Wirkleistung P3:L3_Saldo_Wirkleistung&lt;br /&gt;
attr SMA_Energymeter verbose 2&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== BatteryDummy ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod BatteryDummy dummy&lt;br /&gt;
attr BatteryDummy DbLogExclude .*&lt;br /&gt;
attr BatteryDummy event-on-change-reading .*&lt;br /&gt;
attr BatteryDummy group Energy Meter&lt;br /&gt;
attr BatteryDummy icon batterie@green&lt;br /&gt;
attr BatteryDummy room 020_PV,Energie&lt;br /&gt;
attr BatteryDummy stateFormat {ReadingsVal(&amp;quot;$name&amp;quot;,&amp;quot;total_pac&amp;quot;, undef).&amp;quot; kW &amp;quot;.\&lt;br /&gt;
&amp;quot; - total &amp;quot;.ReadingsVal(&amp;quot;$name&amp;quot;,&amp;quot;bat_loadtotal&amp;quot;, undef).&amp;quot; kWh (-in)&amp;quot;.\&lt;br /&gt;
&amp;quot; - &amp;quot;.ReadingsVal(&amp;quot;$name&amp;quot;,&amp;quot;bat_unloadtotal&amp;quot;, undef).&amp;quot; kWh (out)&amp;quot;.\&lt;br /&gt;
&amp;quot; - charged &amp;quot;.ReadingsVal(&amp;quot;$name&amp;quot;,&amp;quot;chargestatus&amp;quot;, undef).&amp;quot; % &amp;quot;}&lt;br /&gt;
attr BatteryDummy userReadings total_pac, power_out, power_in, chargestatus, bat_rated_capacity, bat_loadtotal, bat_unloadtotal&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== &amp;quot;Berechnungs&amp;quot;-Notify der Werte für Batterie- und InverterDummy ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod N.PV.TotalConsumption.Dum.Energy notify SMA_Energymeter:Saldo_Wirkleistung:.* {\&lt;br /&gt;
 # Batterie-Bezug -Batterieentnahme\&lt;br /&gt;
fhem &amp;quot;setreading Dum.Energy BattLoadIn &amp;quot;.sprintf(&amp;quot;%.1f&amp;quot;,(ReadingsVal(&amp;quot;SBS25&amp;quot;,&amp;quot;power_out&amp;quot;,0)));;\&lt;br /&gt;
 # Batterie-Beladung Batterie mit Strom füllen\&lt;br /&gt;
fhem &amp;quot;setreading Dum.Energy BattLoadOut &amp;quot;.sprintf(&amp;quot;%.1f&amp;quot;,(ReadingsVal(&amp;quot;SBS25&amp;quot;,&amp;quot;power_in&amp;quot;,0)));;\&lt;br /&gt;
 # Batteriestatus\&lt;br /&gt;
fhem &amp;quot;setreading Dum.Energy BattStatusP &amp;quot;.sprintf(&amp;quot;%.1f&amp;quot;,(ReadingsVal(&amp;quot;SBS25&amp;quot;,&amp;quot;chargestatus&amp;quot;,0)));;\&lt;br /&gt;
 # Batterie-Bezug -Batterieentnahme_2\&lt;br /&gt;
fhem &amp;quot;setreading Dum.Energy BattLoadIn_2 &amp;quot;.sprintf(&amp;quot;%.1f&amp;quot;,(ReadingsVal(&amp;quot;SBS25_2&amp;quot;,&amp;quot;power_out&amp;quot;,0)));;\&lt;br /&gt;
 # Batterie-Beladung_2 Batterie mit Strom füllen\&lt;br /&gt;
fhem &amp;quot;setreading Dum.Energy BattLoadOut_2 &amp;quot;.sprintf(&amp;quot;%.1f&amp;quot;,(ReadingsVal(&amp;quot;SBS25_2&amp;quot;,&amp;quot;power_in&amp;quot;,0)));;\&lt;br /&gt;
 # Batteriestatus_2\&lt;br /&gt;
fhem &amp;quot;setreading Dum.Energy BattStatusP_2 &amp;quot;.sprintf(&amp;quot;%.1f&amp;quot;,(ReadingsVal(&amp;quot;SBS25_2&amp;quot;,&amp;quot;chargestatus&amp;quot;,0)));;\&lt;br /&gt;
 # Forecast Invertererzeugung InverterDummy \&lt;br /&gt;
fhem &amp;quot;setreading InverterDummy Today_PVforecast &amp;quot;.sprintf(&amp;quot;%.3f&amp;quot;,(ReadingsNum(&amp;quot;Forecast&amp;quot;,&amp;quot;Today_PVforecast&amp;quot;,0)));;\&lt;br /&gt;
 # Invertererzeugung InverterDummy \&lt;br /&gt;
fhem &amp;quot;setreading InverterDummy etotal &amp;quot;.sprintf(&amp;quot;%.3f&amp;quot;,(ReadingsNum(&amp;quot;SB25&amp;quot;,&amp;quot;etotal&amp;quot;,0))+(ReadingsNum(&amp;quot;SB30&amp;quot;,&amp;quot;etotal&amp;quot;,0))+(ReadingsNum(&amp;quot;SB40&amp;quot;,&amp;quot;etotal&amp;quot;,0)));;\&lt;br /&gt;
 # Invertererzeugung InverterDummy \&lt;br /&gt;
 #fhem &amp;quot;setreading InverterDummy total_pac &amp;quot;.sprintf(&amp;quot;%.3f&amp;quot;,(ReadingsVal(&amp;quot;MB_USRW610_004&amp;quot;,&amp;quot;Power_Sum__W&amp;quot;,0)/1000)+(ReadingsNum(&amp;quot;SB25&amp;quot;,&amp;quot;total_pac&amp;quot;,0))+(ReadingsNum(&amp;quot;SB30&amp;quot;,&amp;quot;total_pac&amp;quot;,0))+(ReadingsNum(&amp;quot;SB40&amp;quot;,&amp;quot;total_pac&amp;quot;,0)));;\&lt;br /&gt;
fhem &amp;quot;setreading InverterDummy total_pac &amp;quot;.sprintf(&amp;quot;%.3f&amp;quot;,(ReadingsNum(&amp;quot;SB25&amp;quot;,&amp;quot;total_pac&amp;quot;,0))+(ReadingsNum(&amp;quot;SB30&amp;quot;,&amp;quot;total_pac&amp;quot;,0))+(ReadingsNum(&amp;quot;SB40&amp;quot;,&amp;quot;total_pac&amp;quot;,0)));;\&lt;br /&gt;
#fhem &amp;quot;setreading InverterDummy total_pac &amp;quot;.sprintf(&amp;quot;%.3f&amp;quot;,(ReadingsVal(&amp;quot;FCU&amp;quot;,&amp;quot;FCU-Strom-aktuelle-Leistung&amp;quot;,0)/1000)+(ReadingsNum(&amp;quot;SB25&amp;quot;,&amp;quot;total_pac&amp;quot;,0))+(ReadingsNum(&amp;quot;SB30&amp;quot;,&amp;quot;total_pac&amp;quot;,0))+(ReadingsNum(&amp;quot;SB40&amp;quot;,&amp;quot;total_pac&amp;quot;,0)));;\&lt;br /&gt;
 # Invertererzeugung InverterDummy \&lt;br /&gt;
my $wert1234 = &amp;quot;0&amp;quot; ;;\&lt;br /&gt;
$wert1234 = sprintf(&amp;quot;%.3f&amp;quot;,(ReadingsNum(&amp;quot;SB25&amp;quot;,&amp;quot;etoday&amp;quot;,0))+(ReadingsNum(&amp;quot;SB30&amp;quot;,&amp;quot;etoday&amp;quot;,0))+(ReadingsNum(&amp;quot;SB40&amp;quot;,&amp;quot;etoday&amp;quot;,0)));; \&lt;br /&gt;
fhem (&amp;quot;setreading InverterDummy etoday &amp;quot;.sprintf(&amp;quot;%.3f&amp;quot;,$wert1234));;\&lt;br /&gt;
 # Batterie-Bezug -Batterieentnahme InverterDummy\&lt;br /&gt;
fhem &amp;quot;setreading BatteryDummy power_out &amp;quot;.sprintf(&amp;quot;%.0f&amp;quot;,(ReadingsNum(&amp;quot;SBS25&amp;quot;,&amp;quot;power_out&amp;quot;,0))+(ReadingsNum(&amp;quot;SBS25_2&amp;quot;,&amp;quot;power_out&amp;quot;,0)));;\&lt;br /&gt;
 # Batterie-Beladung InverterDummyBatterie mit Strom füllen\&lt;br /&gt;
fhem &amp;quot;setreading BatteryDummy power_in &amp;quot;.sprintf(&amp;quot;%.0f&amp;quot;,(ReadingsNum(&amp;quot;SBS25&amp;quot;,&amp;quot;power_in&amp;quot;,0))+(ReadingsNum(&amp;quot;SBS25_2&amp;quot;,&amp;quot;power_in&amp;quot;,0)));;\&lt;br /&gt;
 # Batterie-Bezug -bat_loadtotal Batterieentnahme InverterDummy\&lt;br /&gt;
fhem &amp;quot;setreading BatteryDummy bat_unloadtotal &amp;quot;.sprintf(&amp;quot;%.3f&amp;quot;,(ReadingsNum(&amp;quot;SBS25&amp;quot;,&amp;quot;bat_unloadtotal&amp;quot;,0))+(ReadingsNum(&amp;quot;SBS25_2&amp;quot;,&amp;quot;bat_unloadtotal&amp;quot;,0)));;\&lt;br /&gt;
 # Batterie-Beladung bat_loadtotal InverterDummyBatterie mit Strom füllen\&lt;br /&gt;
fhem &amp;quot;setreading BatteryDummy bat_loadtotal &amp;quot;.sprintf(&amp;quot;%.3f&amp;quot;,(ReadingsNum(&amp;quot;SBS25&amp;quot;,&amp;quot;bat_loadtotal&amp;quot;,0))+(ReadingsNum(&amp;quot;SBS25_2&amp;quot;,&amp;quot;bat_loadtotal&amp;quot;,0)));;\&lt;br /&gt;
 # Batteriestatus InverterDummy\&lt;br /&gt;
my $wert5 = sprintf(&amp;quot;%.2f&amp;quot;,(((ReadingsNum(&amp;quot;SBS25&amp;quot;,&amp;quot;chargestatus&amp;quot;,0))/2) + ((ReadingsNum(&amp;quot;SBS25_2&amp;quot;,&amp;quot;chargestatus&amp;quot;,0))/2)));; \&lt;br /&gt;
fhem (&amp;quot;setreading BatteryDummy chargestatus &amp;quot;.sprintf(&amp;quot;%.2f&amp;quot;,$wert5));;\&lt;br /&gt;
 # Batterie-total_pac  InverterDummy\&lt;br /&gt;
my $wert6 = sprintf(&amp;quot;%.3f&amp;quot;,((ReadingsNum(&amp;quot;SBS25&amp;quot;,&amp;quot;total_pac&amp;quot;,0))+(ReadingsNum(&amp;quot;SBS25_2&amp;quot;,&amp;quot;total_pac&amp;quot;,0))));; \&lt;br /&gt;
fhem (&amp;quot;setreading BatteryDummy total_pac &amp;quot;.sprintf(&amp;quot;%.3f&amp;quot;,$wert6));;\&lt;br /&gt;
 # Batterie-total_pac  InverterDummy\&lt;br /&gt;
my $wert7 = sprintf(&amp;quot;%.3f&amp;quot;,((ReadingsNum(&amp;quot;SBS25&amp;quot;,&amp;quot;bat_rated_capacity&amp;quot;,0))+(ReadingsNum(&amp;quot;SBS25_2&amp;quot;,&amp;quot;bat_rated_capacity&amp;quot;,0))));; \&lt;br /&gt;
fhem (&amp;quot;setreading BatteryDummy bat_rated_capacity &amp;quot;.sprintf(&amp;quot;%.3f&amp;quot;,$wert7));;\&lt;br /&gt;
 # möglicher Aufruf einer Batterie-Bebladung-Routine....SMABatteryChargewithTibber();;\&lt;br /&gt;
}&lt;br /&gt;
attr N.PV.TotalConsumption.Dum.Energy DbLogExclude .*&lt;br /&gt;
attr N.PV.TotalConsumption.Dum.Energy room Energie&lt;br /&gt;
attr N.PV.TotalConsumption.Dum.Energy verbose 2&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== SolarForecast ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod Forecast SolarForecast&lt;br /&gt;
attr Forecast DbLogExclude .*&lt;br /&gt;
attr Forecast DbLogInclude Current_BatCharge999&lt;br /&gt;
attr Forecast affectBatteryPreferredCharge 30&lt;br /&gt;
attr Forecast affectConsForecastLastDays 31&lt;br /&gt;
attr Forecast affect70percentRule 0&lt;br /&gt;
attr Forecast comment &amp;quot;wget -qO ./FHEM/76_SolarForecast.pm https://svn.fhem.de/fhem/trunk/fhem/contrib/DS_Starter/76_SolarForecast.pm&amp;quot;\&lt;br /&gt;
&amp;quot;wget -qO ./FHEM/55_DWD_OpenData.pm https://svn.fhem.de/fhem/trunk/fhem/contrib/DS_Starter/55_DWD_OpenData.pm&amp;quot;\&lt;br /&gt;
&amp;quot;13.01.2025 mit der Version 76_SolarForecast.pm:v1.43.2-s29518/2025-01-12&amp;quot;&lt;br /&gt;
attr Forecast graphicHeaderOwnspec PV&amp;amp;nbsp;;Heute&amp;amp;nbsp;;real:Today_PVreal Verbrauch&amp;amp;nbsp;;bis&amp;amp;nbsp;;Sonnenaufgang&amp;amp;nbsp;;:special_conForecastTillNextSunrise PV&amp;amp;nbsp;;Morgen&amp;amp;nbsp;;erwartet:Tomorrow_PVforecast PV&amp;amp;nbsp;;Uebermorgen&amp;amp;nbsp;;erwartet:special_dayAfterTomorrowPVforecast  Batt.-Ladeanforderung&amp;amp;nbsp;;:Battery_ChargeRequest FCU-Erzeugung&amp;amp;nbsp;;:Current_PP01\&lt;br /&gt;
attr Forecast consumer01 FBDECT_fbahahttp_11657_0127183 icon=scene_washing_machine@orange type=washingmachine power=10 swstate:state notbefore=09 notafter=20 pcurr=power:W:3 etotal=energy:Wh interruptable=1 auto=solarforecast_auto surpmeth=default&lt;br /&gt;
attr Forecast consumer02 FBDECT_fbahahttp_E8_DF_70_07_3E_57 icon=light_floor_lamp@orange type=other power=15 swstate:state pcurr=power:W:10 etotal=energy:Wh interruptable=0 auto=solarforecast_auto surpmeth=default&lt;br /&gt;
attr Forecast consumer03 FBDECT_fbahahttp_E8_DF_70_07_42_0B icon=raspberrypi@orange type=other power=8 swstate:state pcurr=power:W:1 etotal=energy:Wh interruptable=0 auto=solarforecast_auto surpmeth=default&lt;br /&gt;
attr Forecast consumer04 FBDECT_fbahahttp_11657_0067275 icon=springbrunnen_icon@orange type=other power=50 swstate:state pcurr=power:W:10 etotal=energy:Wh interruptable=0 auto=solarforecast_auto surpmeth=default&lt;br /&gt;
attr Forecast consumer05 FBDECT_fbahahttp_34_31_C4_D4_31_37 icon=sani_domestic_waterworks@orange type=other power=10 swstate:state pcurr=power:W:3 etotal=energy:Wh auto=solarforecast_auto surpmeth=default&lt;br /&gt;
attr Forecast consumer06 tuya_local_bf5037060f450bdbd4rl0q icon=scene_clothes_dryer@orange type=dryer power=10 swstate:state pcurr=cur_power:W:3 etotal=energy:Wh auto=solarforecast_auto surpmeth=default&lt;br /&gt;
attr Forecast consumer07 tuya_local_bfac44fb487476efd1vhdu icon=weather_sunset@orange type=noSchedule power=5 swstate:state pcurr=cur_power:W:3 etotal=energy:Wh auto=solarforecast_auto surpmeth=default&lt;br /&gt;
attr Forecast consumerControl adviceIcon=light_light_dim_100@gold&lt;br /&gt;
attr Forecast consumerControl showLegend=icon_top&lt;br /&gt;
attr Forecast consumerControl detailLink=1&lt;br /&gt;
attr Forecast ctrlAIdataStorageDuration 1825&lt;br /&gt;
attr Forecast ctrlAIshiftTrainStart 2&lt;br /&gt;
attr Forecast ctrlBatSocManagement01 lowSoc=10 upSoC=30 maxSoC=99 careCycle=20&lt;br /&gt;
attr Forecast ctrlBatSocManagement02 lowSoc=10 upSoC=30 maxSoC=99 careCycle=20&lt;br /&gt;
attr Forecast ctrlDebug none&lt;br /&gt;
attr Forecast plantControl genPVdeviation=continuously&lt;br /&gt;
attr Forecast plantControl cycleInterval=15&lt;br /&gt;
attr Forecast ctrlNextDayForecastReadings 01,02,03,04,05,06,07,08,09,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24&lt;br /&gt;
attr Forecast ctrlNextHoursSoCForecastReadings 00,01,02,03,04,05,06,07,08,09,10,11,12,13,14,15,16,17,18,19,20,21,22,23&lt;br /&gt;
attr Forecast ctrlSpecialReadings BatPowerIn_Sum,BatPowerOut_Sum,SunHours_Remain,SunMinutes_Remain,allStringsFullfilled,conForecastTillNextSunrise,currentAPIinterval,currentRunMtsConsumer_01,currentRunMtsConsumer_02,currentRunMtsConsumer_03,currentRunMtsConsumer_04,currentRunMtsConsumer_05,currentRunMtsConsumer_06,currentRunMtsConsumer_07,dayAfterTomorrowPVforecast,daysUntilBatteryCare_01,daysUntilBatteryCare_02,lastretrieval_time,lastretrieval_timestamp,response_message,runTimeAvgDayConsumer_01,runTimeAvgDayConsumer_02,runTimeAvgDayConsumer_03,runTimeAvgDayConsumer_04,runTimeAvgDayConsumer_05,runTimeAvgDayConsumer_06,runTimeAvgDayConsumer_07,runTimeCentralTask,runTimeLastAPIAnswer,runTimeLastAPIProc,runTimeTrainAI,todayBatIn_01,todayBatIn_02,todayBatOut_01,todayBatOut_02,todayConForecastTillSunset,todayConsumptionForecast,todayDoneAPIcalls,todayDoneAPIrequests,todayGridConsumption,todayGridFeedIn,todayMaxAPIcalls,todayRemainingAPIcalls,todayRemainingAPIrequests&lt;br /&gt;
attr Forecast disable 0&lt;br /&gt;
attr Forecast event-min-interval .*:1800&lt;br /&gt;
attr Forecast event-on-change-reading .*&lt;br /&gt;
attr Forecast flowGraphicControl animate=1 consumerdist=80 h2consumerdist=50 shiftx=0  shifty=0 showconsumer=1 showconsumerdummy=1 showconsumerpower=1 showconsumerremaintime=0 size=400 strokewidth=12&lt;br /&gt;
attr Forecast graphicBeam1Color 3C14FF&lt;br /&gt;
attr Forecast graphicBeam1Content pvReal&lt;br /&gt;
attr Forecast graphicBeam2Color 19FF29&lt;br /&gt;
attr Forecast graphicBeam2Content pvForecast&lt;br /&gt;
attr Forecast graphicBeam3Color D60924&lt;br /&gt;
attr Forecast graphicBeam3Content batsocforecast_02&lt;br /&gt;
attr Forecast graphicBeam3FontColor FFFF0D&lt;br /&gt;
attr Forecast graphicBeam4Color FFFF1F&lt;br /&gt;
attr Forecast graphicBeam4Content batsocforecast_01&lt;br /&gt;
attr Forecast graphicBeam4FontColor 000000&lt;br /&gt;
attr Forecast graphicBeamHeightLevel1 200&lt;br /&gt;
attr Forecast graphicBeamHeightLevel2 200&lt;br /&gt;
attr Forecast graphicHeaderDetail all&lt;br /&gt;
attr Forecast graphicHeaderOwnspec #PV\&lt;br /&gt;
PV&amp;amp;nbsp;;Heute&amp;amp;nbsp;;real:Today_PVreal\&lt;br /&gt;
PV&amp;amp;nbsp;;Morgen&amp;amp;nbsp;;erwartet:Tomorrow_PVforecast\&lt;br /&gt;
PV&amp;amp;nbsp;;Uebermorgen&amp;amp;nbsp;;erwartet:special_dayAfterTomorrowPVforecast\&lt;br /&gt;
:\&lt;br /&gt;
#\&lt;br /&gt;
AutarkyRate:Current_AutarkyRate\&lt;br /&gt;
Überschuss:Current_Surplus\&lt;br /&gt;
aktueller&amp;amp;nbsp;;Netzbezug:Current_GridConsumption\&lt;br /&gt;
:\&lt;br /&gt;
#Verbrauch\&lt;br /&gt;
bis&amp;amp;nbsp;;Sonnenuntergang:special_todayConForecastTillSunset\&lt;br /&gt;
bis&amp;amp;nbsp;;Sonnenaufgang&amp;amp;nbsp;;:special_conForecastTillNextSunrise\&lt;br /&gt;
:\&lt;br /&gt;
:\&lt;br /&gt;
#Batterie01\&lt;br /&gt;
Batt.-Ladeanforderung&amp;amp;nbsp;;:Battery_ChargeRequest_01\&lt;br /&gt;
Batt.-Ladung&amp;amp;nbsp;;empfohlen:Battery_ChargeUnrestricted_01\&lt;br /&gt;
Ladung&amp;amp;nbsp;;heute:special_todayBatIn_01\&lt;br /&gt;
Entladung&amp;amp;nbsp;;heute:special_todayBatOut_01\&lt;br /&gt;
#Batterie01_tmp\&lt;br /&gt;
Current_PowerBatIn_01:Current_PowerBatIn_01\&lt;br /&gt;
Current_PowerBatOut_01:Current_PowerBatOut_01\&lt;br /&gt;
:\&lt;br /&gt;
:\&lt;br /&gt;
#Batterie02\&lt;br /&gt;
Batt.-Ladeanforderung&amp;amp;nbsp;;:Battery_ChargeRequest_02\&lt;br /&gt;
Batt.-Ladung&amp;amp;nbsp;;empfohlen:Battery_ChargeUnrestricted_02\&lt;br /&gt;
Ladung&amp;amp;nbsp;;heute:special_todayBatIn_02\&lt;br /&gt;
Entladung&amp;amp;nbsp;;heute:special_todayBatOut_02\&lt;br /&gt;
#Batterie02_tmp\&lt;br /&gt;
Current_PowerBatIn_02:Current_PowerBatIn_02\&lt;br /&gt;
Current_PowerBatOut_02:Current_PowerBatOut_02\&lt;br /&gt;
:\&lt;br /&gt;
:\&lt;br /&gt;
#Settings\&lt;br /&gt;
Autokorrektur:pvCorrectionFactor_Auto \&lt;br /&gt;
Wetter:graphicShowWeather\&lt;br /&gt;
History:graphicHistoryHour\&lt;br /&gt;
ShowNight:graphicShowNight\&lt;br /&gt;
Debug:ctrlDebug\&lt;br /&gt;
FCU-Modus:FCU-Betriebsmodus@FCU&lt;br /&gt;
attr Forecast graphicHistoryHour 4&lt;br /&gt;
attr Forecast graphicHourCount 24&lt;br /&gt;
attr Forecast graphicLayoutType double&lt;br /&gt;
attr Forecast graphicShowDiff top&lt;br /&gt;
attr Forecast graphicShowNight 1&lt;br /&gt;
attr Forecast graphicShowWeather 1&lt;br /&gt;
attr Forecast group Energy Meter&lt;br /&gt;
attr Forecast room 020_PV,Energie&lt;br /&gt;
attr Forecast setupBatteryDev01 SBS25 pin=-pout:kW pout=total_pac:kW intotal=bat_loadtotal:kWh outtotal=bat_unloadtotal:kWh charge=chargestatus cap=9800 show=2&lt;br /&gt;
attr Forecast setupBatteryDev02 SBS25_2 pin=-pout:kW pout=total_pac:kW intotal=bat_loadtotal:kWh outtotal=bat_unloadtotal:kWh charge=chargestatus cap=9800 show=2&lt;br /&gt;
attr Forecast setupInverterDev01 SB25 pv=total_pac:kW etotal=etotal:kWh capacity=2500 strings=GarageSE limit=70&lt;br /&gt;
attr Forecast setupInverterDev02 SB30 pv=total_pac:kW etotal=etotal:kWh capacity=3000 strings=GarageNW,HausNW limit=70&lt;br /&gt;
attr Forecast setupInverterDev03 SB40 pv=total_pac:kW etotal=etotal:kWh capacity=4000 strings=HausSE1,HausSE2,HausSW limit=70&lt;br /&gt;
attr Forecast setupInverterStrings GarageSE,GarageNW,HausNW,HausSW,HausSE1,HausSE2&lt;br /&gt;
attr Forecast setupMeterDev SMA_Energymeter gcon=Bezug_Wirkleistung:W contotal=Bezug_Wirkleistung_Zaehler:kWh gfeedin=Einspeisung_Wirkleistung:W feedtotal=Einspeisung_Wirkleistung_Zaehler:kWh conprice=0.25:€ feedprice=0.08123:€&lt;br /&gt;
attr Forecast setupOtherProducer01 icon=Heizung_FCU_green@red MB_USRW610_004 pcurr=Power_L1__W:W etotal=Energy_L1_import__kWh:kWh&lt;br /&gt;
attr Forecast setupRadiationAPI DWD&lt;br /&gt;
attr Forecast setupStringPeak GarageSE=2.75 GarageNW=3.200 HausNW=2.230 HausSW=2.230 HausSE1=2.200 HausSE2=2.200&lt;br /&gt;
attr Forecast setupWeatherDev1 DWD&lt;br /&gt;
attr Forecast stateFormat Current_PV&lt;br /&gt;
attr Forecast userReadings Current_BatCharge999 {((ReadingsNum(&amp;quot;SBS25&amp;quot;,&amp;quot;chargestatus&amp;quot;,0) * 10 * ReadingsNum(&amp;quot;SBS25&amp;quot;,&amp;quot;bat_rated_capacity&amp;quot;,0))  + (ReadingsNum(&amp;quot;SBS25_2&amp;quot;,&amp;quot;chargestatus&amp;quot;,0) * 10 * ReadingsNum(&amp;quot;SBS25&amp;quot;,&amp;quot;bat_rated_capacity&amp;quot;,0))) / ( (ReadingsNum(&amp;quot;SBS25&amp;quot;,&amp;quot;bat_rated_capacity&amp;quot;,0) * 1000)  + (ReadingsNum(&amp;quot;SBS25_2&amp;quot;,&amp;quot;bat_rated_capacity&amp;quot;,0)*1000))*100}&lt;br /&gt;
attr Forecast verbose 2&lt;br /&gt;
&lt;br /&gt;
setstate Forecast 0 W&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:55 .FCU_FCU-Betriebsmodus 2&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:55 .Forecast_ctrlDebug none&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:55 .Forecast_graphicHistoryHour 4&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:55 .Forecast_graphicShowNight 1&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:55 .Forecast_graphicShowWeather 1&lt;br /&gt;
setstate Forecast 2025-01-13 08:24:25 .associatedWith SMA_Energymeter FBDECT_fbahahttp_11657_0127183 FBDECT_fbahahttp_E8_DF_70_07_3E_57 FBDECT_fbahahttp_E8_DF_70_07_42_0B FBDECT_fbahahttp_11657_0067275 FBDECT_fbahahttp_34_31_C4_D4_31_37 tuya_local_bf5037060f450bdbd4rl0q tuya_local_bfac44fb487476efd1vhdu SBS25 SBS25_2 SB25 SB30 SB40 DWD MB_USRW610_004&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 .lastupdateForecastValues 1736790834&lt;br /&gt;
setstate Forecast 2025-01-13 01:00:05 .pvCorrectionFactor_01_apipercentil done&lt;br /&gt;
setstate Forecast 2025-01-13 01:00:05 .pvCorrectionFactor_01_cloudcover done&lt;br /&gt;
setstate Forecast 2025-01-13 02:00:04 .pvCorrectionFactor_02_apipercentil done&lt;br /&gt;
setstate Forecast 2025-01-13 02:00:04 .pvCorrectionFactor_02_cloudcover done&lt;br /&gt;
setstate Forecast 2025-01-13 03:00:04 .pvCorrectionFactor_03_apipercentil done&lt;br /&gt;
setstate Forecast 2025-01-13 03:00:04 .pvCorrectionFactor_03_cloudcover done&lt;br /&gt;
setstate Forecast 2025-01-13 04:00:05 .pvCorrectionFactor_04_apipercentil done&lt;br /&gt;
setstate Forecast 2025-01-13 04:00:05 .pvCorrectionFactor_04_cloudcover done&lt;br /&gt;
setstate Forecast 2025-01-13 05:00:05 .pvCorrectionFactor_05_apipercentil done&lt;br /&gt;
setstate Forecast 2025-01-13 05:00:05 .pvCorrectionFactor_05_cloudcover done&lt;br /&gt;
setstate Forecast 2025-01-13 06:00:06 .pvCorrectionFactor_06_apipercentil done&lt;br /&gt;
setstate Forecast 2025-01-13 06:00:06 .pvCorrectionFactor_06_cloudcover done&lt;br /&gt;
setstate Forecast 2025-01-13 07:00:04 .pvCorrectionFactor_07_apipercentil done&lt;br /&gt;
setstate Forecast 2025-01-13 07:00:04 .pvCorrectionFactor_07_cloudcover done&lt;br /&gt;
setstate Forecast 2025-01-13 08:00:03 .pvCorrectionFactor_08_apipercentil done&lt;br /&gt;
setstate Forecast 2025-01-13 08:00:03 .pvCorrectionFactor_08_cloudcover done&lt;br /&gt;
setstate Forecast 2025-01-13 09:00:04 .pvCorrectionFactor_09_apipercentil done&lt;br /&gt;
setstate Forecast 2025-01-13 09:00:04 .pvCorrectionFactor_09_cloudcover done&lt;br /&gt;
setstate Forecast 2025-01-13 10:00:05 .pvCorrectionFactor_10_apipercentil done&lt;br /&gt;
setstate Forecast 2025-01-13 10:00:05 .pvCorrectionFactor_10_cloudcover done&lt;br /&gt;
setstate Forecast 2025-01-13 11:00:05 .pvCorrectionFactor_11_apipercentil done&lt;br /&gt;
setstate Forecast 2025-01-13 11:00:05 .pvCorrectionFactor_11_cloudcover done&lt;br /&gt;
setstate Forecast 2025-01-13 12:00:02 .pvCorrectionFactor_12_apipercentil done&lt;br /&gt;
setstate Forecast 2025-01-13 12:00:02 .pvCorrectionFactor_12_cloudcover done&lt;br /&gt;
setstate Forecast 2025-01-13 13:00:02 .pvCorrectionFactor_13_apipercentil done&lt;br /&gt;
setstate Forecast 2025-01-13 13:00:02 .pvCorrectionFactor_13_cloudcover done&lt;br /&gt;
setstate Forecast 2025-01-13 14:00:03 .pvCorrectionFactor_14_apipercentil done&lt;br /&gt;
setstate Forecast 2025-01-13 14:00:03 .pvCorrectionFactor_14_cloudcover done&lt;br /&gt;
setstate Forecast 2025-01-13 15:00:05 .pvCorrectionFactor_15_apipercentil done&lt;br /&gt;
setstate Forecast 2025-01-13 15:00:05 .pvCorrectionFactor_15_cloudcover done&lt;br /&gt;
setstate Forecast 2025-01-13 16:00:06 .pvCorrectionFactor_16_apipercentil done&lt;br /&gt;
setstate Forecast 2025-01-13 16:00:06 .pvCorrectionFactor_16_cloudcover done&lt;br /&gt;
setstate Forecast 2025-01-13 17:00:05 .pvCorrectionFactor_17_apipercentil done&lt;br /&gt;
setstate Forecast 2025-01-13 17:00:05 .pvCorrectionFactor_17_cloudcover done&lt;br /&gt;
setstate Forecast 2025-01-13 18:00:04 .pvCorrectionFactor_18_apipercentil done&lt;br /&gt;
setstate Forecast 2025-01-13 18:00:04 .pvCorrectionFactor_18_cloudcover done&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 .pvCorrectionFactor_Auto_Soll on_complex_ai&lt;br /&gt;
setstate Forecast 2025-01-13 01:00:05 .signaldone_01 done&lt;br /&gt;
setstate Forecast 2025-01-13 02:00:04 .signaldone_02 done&lt;br /&gt;
setstate Forecast 2025-01-13 03:00:04 .signaldone_03 done&lt;br /&gt;
setstate Forecast 2025-01-13 04:00:05 .signaldone_04 done&lt;br /&gt;
setstate Forecast 2025-01-13 05:00:05 .signaldone_05 done&lt;br /&gt;
setstate Forecast 2025-01-13 06:00:06 .signaldone_06 done&lt;br /&gt;
setstate Forecast 2025-01-13 07:00:04 .signaldone_07 done&lt;br /&gt;
setstate Forecast 2025-01-13 08:00:03 .signaldone_08 done&lt;br /&gt;
setstate Forecast 2025-01-13 09:00:04 .signaldone_09 done&lt;br /&gt;
setstate Forecast 2025-01-13 10:00:05 .signaldone_10 done&lt;br /&gt;
setstate Forecast 2025-01-13 11:00:05 .signaldone_11 done&lt;br /&gt;
setstate Forecast 2025-01-13 12:00:02 .signaldone_12 done&lt;br /&gt;
setstate Forecast 2025-01-13 13:00:02 .signaldone_13 done&lt;br /&gt;
setstate Forecast 2025-01-13 14:00:03 .signaldone_14 done&lt;br /&gt;
setstate Forecast 2025-01-13 15:00:05 .signaldone_15 done&lt;br /&gt;
setstate Forecast 2025-01-13 16:00:06 .signaldone_16 done&lt;br /&gt;
setstate Forecast 2025-01-13 17:00:05 .signaldone_17 done&lt;br /&gt;
setstate Forecast 2025-01-13 18:00:04 .signaldone_18 done&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Battery_ChargeUnrestricted_01 1&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Battery_ChargeUnrestricted_02 1&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Battery_ChargeRequest_01 1&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Battery_ChargeRequest_02 1&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Battery_NextHour00_SoCforecast_01 10.0 %&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Battery_NextHour00_SoCforecast_02 10.0 %&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Battery_NextHour01_SoCforecast_01 10.0 %&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Battery_NextHour01_SoCforecast_02 10.0 %&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Battery_NextHour02_SoCforecast_01 10.0 %&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Battery_NextHour02_SoCforecast_02 10.0 %&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Battery_NextHour03_SoCforecast_01 10.0 %&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Battery_NextHour03_SoCforecast_02 10.0 %&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Battery_NextHour04_SoCforecast_01 10.0 %&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Battery_NextHour04_SoCforecast_02 10.0 %&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Battery_NextHour05_SoCforecast_01 10.0 %&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Battery_NextHour05_SoCforecast_02 10.0 %&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Battery_NextHour06_SoCforecast_01 10.0 %&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Battery_NextHour06_SoCforecast_02 10.0 %&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Battery_NextHour07_SoCforecast_01 10.0 %&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Battery_NextHour07_SoCforecast_02 10.0 %&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Battery_NextHour08_SoCforecast_01 10.0 %&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Battery_NextHour08_SoCforecast_02 10.0 %&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Battery_NextHour09_SoCforecast_01 10.0 %&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Battery_NextHour09_SoCforecast_02 10.0 %&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Battery_NextHour10_SoCforecast_01 10.0 %&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Battery_NextHour10_SoCforecast_02 10.0 %&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Battery_NextHour11_SoCforecast_01 10.0 %&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Battery_NextHour11_SoCforecast_02 10.0 %&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Battery_NextHour12_SoCforecast_01 10.0 %&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Battery_NextHour12_SoCforecast_02 10.0 %&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Battery_NextHour13_SoCforecast_01 10.0 %&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Battery_NextHour13_SoCforecast_02 10.0 %&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Battery_NextHour14_SoCforecast_01 10.0 %&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Battery_NextHour14_SoCforecast_02 10.0 %&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Battery_NextHour15_SoCforecast_01 10.0 %&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Battery_NextHour15_SoCforecast_02 10.0 %&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Battery_NextHour16_SoCforecast_01 10.0 %&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Battery_NextHour16_SoCforecast_02 10.0 %&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Battery_NextHour17_SoCforecast_01 10.0 %&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Battery_NextHour17_SoCforecast_02 10.0 %&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Battery_NextHour18_SoCforecast_01 40.0 %&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Battery_NextHour18_SoCforecast_02 40.0 %&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Battery_NextHour19_SoCforecast_01 40.0 %&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Battery_NextHour19_SoCforecast_02 40.0 %&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Battery_NextHour20_SoCforecast_01 42.6 %&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Battery_NextHour20_SoCforecast_02 42.6 %&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Battery_NextHour21_SoCforecast_01 40.0 %&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Battery_NextHour21_SoCforecast_02 40.0 %&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Battery_NextHour22_SoCforecast_01 40.0 %&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Battery_NextHour22_SoCforecast_02 40.0 %&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Battery_NextHour23_SoCforecast_01 40.0 %&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Battery_NextHour23_SoCforecast_02 40.0 %&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Battery_OptimumTargetSoC_01 40 %&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Battery_OptimumTargetSoC_02 40 %&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Current_AutarkyRate 83 %&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:55 Current_BatCharge999 10&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Current_BatCharge_01 12 %&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Current_BatCharge_02 8 %&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Current_Consumption 626 W&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Current_GridConsumption 2 W&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Current_GridFeedIn 0 W&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Current_PP_01 746.0 W&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Current_PV 0 W&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Current_PowerBatIn_01 0 W&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Current_PowerBatIn_02 132 W&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Current_PowerBatOut_01 10 W&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Current_PowerBatOut_02 0 W&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Current_SelfConsumption 0 W&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Current_SelfConsumptionRate 0 %&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Current_Surplus 120 W&lt;br /&gt;
setstate Forecast 2025-01-13 18:00:00 LastHourGridconsumptionReal 5 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 18:00:00 LastHourPVforecast 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 18:00:00 LastHourPVreal 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 NextHours_Sum01_PVforecast 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 NextHours_Sum02_PVforecast 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 NextHours_Sum03_PVforecast 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 NextHours_Sum04_ConsumptionForecast 2944 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 NextHours_Sum04_PVforecast 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 RestOfDayConsumptionForecast 3609 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 RestOfDayPVforecast 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 00:59:59 Today_Hour01_BatIn_01 272 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 00:59:59 Today_Hour01_BatIn_02 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 00:59:59 Today_Hour01_BatOut_01 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 00:59:59 Today_Hour01_BatOut_02 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 00:59:59 Today_Hour01_GridConsumption 1 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 00:59:59 Today_Hour01_GridFeedIn 1 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 00:59:59 Today_Hour01_PPreal_01 748 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 00:59:59 Today_Hour01_PVreal 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 01:59:58 Today_Hour02_BatIn_01 266 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 01:59:58 Today_Hour02_BatIn_02 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 01:59:58 Today_Hour02_BatOut_01 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 01:59:58 Today_Hour02_BatOut_02 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 01:59:58 Today_Hour02_GridConsumption 1 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 01:59:58 Today_Hour02_GridFeedIn 1 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 01:59:58 Today_Hour02_PPreal_01 787 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 01:59:58 Today_Hour02_PVreal 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 02:59:53 Today_Hour03_BatIn_01 271 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 02:59:53 Today_Hour03_BatIn_02 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 02:59:53 Today_Hour03_BatOut_01 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 02:59:53 Today_Hour03_BatOut_02 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 02:59:53 Today_Hour03_GridConsumption 2 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 02:59:53 Today_Hour03_GridFeedIn 2 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 02:59:53 Today_Hour03_PPreal_01 750 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 02:59:53 Today_Hour03_PVreal 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 03:59:52 Today_Hour04_BatIn_01 266 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 03:59:52 Today_Hour04_BatIn_02 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 03:59:52 Today_Hour04_BatOut_01 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 03:59:52 Today_Hour04_BatOut_02 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 03:59:52 Today_Hour04_GridConsumption 1 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 03:59:52 Today_Hour04_GridFeedIn 1 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 03:59:52 Today_Hour04_PPreal_01 745 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 03:59:52 Today_Hour04_PVreal 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 04:59:56 Today_Hour05_BatIn_01 267 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 04:59:56 Today_Hour05_BatIn_02 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 04:59:56 Today_Hour05_BatOut_01 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 04:59:56 Today_Hour05_BatOut_02 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 04:59:56 Today_Hour05_GridConsumption 2 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 04:59:56 Today_Hour05_GridFeedIn 2 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 04:59:56 Today_Hour05_PPreal_01 758 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 04:59:56 Today_Hour05_PVreal 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 05:59:59 Today_Hour06_BatIn_01 255 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 05:59:59 Today_Hour06_BatIn_02 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 05:59:59 Today_Hour06_BatOut_01 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 05:59:59 Today_Hour06_BatOut_02 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 05:59:59 Today_Hour06_GridConsumption 1 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 05:59:59 Today_Hour06_GridFeedIn 1 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 05:59:59 Today_Hour06_PPreal_01 752 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 05:59:59 Today_Hour06_PVreal 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 06:59:52 Today_Hour07_BatIn_01 226 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 06:59:52 Today_Hour07_BatIn_02 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 06:59:52 Today_Hour07_BatOut_01 50 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 06:59:52 Today_Hour07_BatOut_02 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 06:59:52 Today_Hour07_GridConsumption 2 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 06:59:52 Today_Hour07_GridFeedIn 2 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 06:59:52 Today_Hour07_PPreal_01 681 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 06:59:52 Today_Hour07_PVreal 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 07:59:50 Today_Hour08_BatIn_01 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 07:59:50 Today_Hour08_BatIn_02 233 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 07:59:50 Today_Hour08_BatOut_01 845 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 07:59:50 Today_Hour08_BatOut_02 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 07:59:50 Today_Hour08_GridConsumption 4 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 07:59:50 Today_Hour08_GridFeedIn 4 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 07:59:50 Today_Hour08_PPreal_01 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 07:59:50 Today_Hour08_PVreal 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 08:59:57 Today_Hour09_BatIn_01 37 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 08:59:57 Today_Hour09_BatIn_02 90 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 08:59:57 Today_Hour09_BatOut_01 351 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 08:59:57 Today_Hour09_BatOut_02 188 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 08:59:57 Today_Hour09_GridConsumption 447 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 08:59:57 Today_Hour09_GridFeedIn 4 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 08:59:57 Today_Hour09_PPreal_01 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 08:59:57 Today_Hour09_PVforecast 127 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 08:59:57 Today_Hour09_PVreal 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 09:59:58 Today_Hour10_BatIn_01 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 09:59:58 Today_Hour10_BatIn_02 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 09:59:58 Today_Hour10_BatOut_01 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 09:59:58 Today_Hour10_BatOut_02 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 09:59:58 Today_Hour10_GridConsumption 1650 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 09:59:58 Today_Hour10_GridFeedIn 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 09:59:58 Today_Hour10_PPreal_01 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 09:59:58 Today_Hour10_PVforecast 478 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 09:59:58 Today_Hour10_PVreal 26 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 10:59:58 Today_Hour11_BatIn_01 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 10:59:58 Today_Hour11_BatIn_02 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 10:59:58 Today_Hour11_BatOut_01 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 10:59:58 Today_Hour11_BatOut_02 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 10:59:58 Today_Hour11_GridConsumption 513 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 10:59:58 Today_Hour11_GridFeedIn 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 10:59:58 Today_Hour11_PPreal_01 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 10:59:58 Today_Hour11_PVforecast 868 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 10:59:58 Today_Hour11_PVreal 97 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 11:59:49 Today_Hour12_BatIn_01 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 11:59:49 Today_Hour12_BatIn_02 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 11:59:49 Today_Hour12_BatOut_01 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 11:59:49 Today_Hour12_BatOut_02 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 11:59:49 Today_Hour12_GridConsumption 576 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 11:59:49 Today_Hour12_GridFeedIn 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 11:59:49 Today_Hour12_PPreal_01 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 11:59:49 Today_Hour12_PVforecast 1157 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 11:59:49 Today_Hour12_PVreal 320 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 12:59:49 Today_Hour13_BatIn_01 8 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 12:59:49 Today_Hour13_BatIn_02 4 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 12:59:49 Today_Hour13_BatOut_01 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 12:59:49 Today_Hour13_BatOut_02 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 12:59:49 Today_Hour13_GridConsumption 1272 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 12:59:49 Today_Hour13_GridFeedIn 7 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 12:59:49 Today_Hour13_PPreal_01 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 12:59:49 Today_Hour13_PVforecast 1238 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 12:59:49 Today_Hour13_PVreal 945 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 13:59:49 Today_Hour14_BatIn_01 410 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 13:59:49 Today_Hour14_BatIn_02 53 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 13:59:49 Today_Hour14_BatOut_01 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 13:59:49 Today_Hour14_BatOut_02 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 13:59:49 Today_Hour14_GridConsumption 955 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 13:59:49 Today_Hour14_GridFeedIn 31 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 13:59:49 Today_Hour14_PPreal_01 562 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 13:59:49 Today_Hour14_PVforecast 1168 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 13:59:49 Today_Hour14_PVreal 1410 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 14:59:49 Today_Hour15_BatIn_01 703 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 14:59:49 Today_Hour15_BatIn_02 500 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 14:59:49 Today_Hour15_BatOut_01 22 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 14:59:49 Today_Hour15_BatOut_02 18 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 14:59:49 Today_Hour15_GridConsumption 32 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 14:59:49 Today_Hour15_GridFeedIn 71 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 14:59:49 Today_Hour15_PPreal_01 777 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 14:59:49 Today_Hour15_PVforecast 1630 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 14:59:49 Today_Hour15_PVreal 1416 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 15:59:50 Today_Hour16_BatIn_01 296 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 15:59:50 Today_Hour16_BatIn_02 70 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 15:59:50 Today_Hour16_BatOut_01 74 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 15:59:50 Today_Hour16_BatOut_02 258 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 15:59:50 Today_Hour16_GridConsumption 13 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 15:59:50 Today_Hour16_GridFeedIn 17 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 15:59:50 Today_Hour16_PPreal_01 732 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 15:59:50 Today_Hour16_PVforecast 880 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 15:59:50 Today_Hour16_PVreal 1051 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 16:59:49 Today_Hour17_BatIn_01 47 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 16:59:49 Today_Hour17_BatIn_02 126 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 16:59:49 Today_Hour17_BatOut_01 102 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 16:59:49 Today_Hour17_BatOut_02 75 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 16:59:49 Today_Hour17_GridConsumption 15 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 16:59:49 Today_Hour17_GridFeedIn 23 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 16:59:49 Today_Hour17_PPreal_01 771 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 16:59:49 Today_Hour17_PVforecast 117 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 16:59:49 Today_Hour17_PVreal 86 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 17:59:54 Today_Hour18_BatIn_01 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 17:59:54 Today_Hour18_BatIn_02 410 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 17:59:54 Today_Hour18_BatOut_01 758 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 17:59:54 Today_Hour18_BatOut_02 67 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 17:59:54 Today_Hour18_GridConsumption 5 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 17:59:54 Today_Hour18_GridFeedIn 5 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 17:59:54 Today_Hour18_PPreal_01 723 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 17:59:54 Today_Hour18_PVreal 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Today_Hour19_BatIn_01 349 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Today_Hour19_BatIn_02 124 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Today_Hour19_BatOut_01 73 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Today_Hour19_BatOut_02 450 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Today_Hour19_GridConsumption 8 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Today_Hour19_GridFeedIn 6 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Today_Hour19_PPreal_01 671 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Today_Hour19_PVreal 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Today_MaxPVforecast 1630 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Today_MaxPVforecastTime 2025-01-13 14:00:00&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Today_PVdeviation 30.17 %&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Today_PVforecast 7663 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Today_PVreal 5351 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Today_SunRise 08:28&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Today_SunSet 16:46&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Tomorrow_ConsumptionForecast 17043 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Tomorrow_Hour01_PVforecast 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Tomorrow_Hour02_PVforecast 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Tomorrow_Hour03_PVforecast 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Tomorrow_Hour04_PVforecast 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Tomorrow_Hour05_PVforecast 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Tomorrow_Hour06_PVforecast 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Tomorrow_Hour07_PVforecast 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Tomorrow_Hour08_PVforecast 10 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Tomorrow_Hour09_PVforecast 161 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Tomorrow_Hour10_PVforecast 275 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Tomorrow_Hour11_PVforecast 532 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Tomorrow_Hour12_PVforecast 730 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Tomorrow_Hour13_PVforecast 1252 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Tomorrow_Hour14_PVforecast 818 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Tomorrow_Hour15_PVforecast 1465 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Tomorrow_Hour16_PVforecast 303 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Tomorrow_Hour17_PVforecast 20 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Tomorrow_Hour18_PVforecast 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Tomorrow_Hour19_PVforecast 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Tomorrow_Hour20_PVforecast 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Tomorrow_Hour21_PVforecast 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Tomorrow_Hour22_PVforecast 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Tomorrow_Hour23_PVforecast 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Tomorrow_Hour24_PVforecast 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Tomorrow_PVforecast 5566 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Tomorrow_SunRise 08:27&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Tomorrow_SunSet 16:47&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 consumer01 name=&#039;_Waschmaschine&#039; state=&#039;off&#039; mode=&#039;can&#039; planningstate=&#039;planned&#039;&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 consumer01_currentPower 0 W&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 consumer01_planned_start 14.01.2025 09:00:00&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 consumer01_planned_stop 14.01.2025 11:00:00&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 consumer02 name=&#039;_Esszimmer&#039; state=&#039;on&#039; mode=&#039;can&#039; planningstate=&#039;planned&#039;&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 consumer02_currentPower 15.3 W&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 consumer02_planned_start 14.01.2025 08:00:00&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 consumer02_planned_stop 14.01.2025 09:00:00&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 consumer03 name=&#039;_FCU&#039; state=&#039;on&#039; mode=&#039;can&#039; planningstate=&#039;planned&#039;&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 consumer03_currentPower 7.15 W&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 consumer03_planned_start 14.01.2025 07:00:00&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 consumer03_planned_stop 14.01.2025 08:00:00&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 consumer04 name=&#039;_Brunnen&#039; state=&#039;off&#039; mode=&#039;can&#039; planningstate=&#039;planned&#039;&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 consumer04_currentPower 0 W&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 consumer04_planned_start 14.01.2025 08:00:00&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 consumer04_planned_stop 14.01.2025 09:00:00&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 consumer05 name=&#039;_Zisterne&#039; state=&#039;off&#039; mode=&#039;can&#039; planningstate=&#039;planned&#039;&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 consumer05_currentPower 0 W&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 consumer05_planned_start 14.01.2025 07:00:00&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 consumer05_planned_stop 14.01.2025 08:00:00&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 consumer06 name=&#039;SW Trockner&#039; state=&#039;unknown&#039; mode=&#039;can&#039; planningstate=&#039;planned&#039;&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 consumer06_currentPower 0 W&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 consumer06_planned_start 14.01.2025 07:00:00&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 consumer06_planned_stop 14.01.2025 08:30:00&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 consumer07 name=&#039;SW Urlaubslicht-Wohnzimmer&#039; state=&#039;on&#039; mode=&#039;can&#039; planningstate=&#039;noSchedule&#039; info=&#039;von extern umgeschaltet&#039;&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 consumer07_currentPower 0 W&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 nextCycletime 18:54:09&lt;br /&gt;
setstate Forecast 2025-01-13 10:00:05 pvCorrectionFactor_10 0.53 (automatic - old factor: 1.00, Sun Alt range: 5, Cloud range: 75, Days in range: 1)&lt;br /&gt;
setstate Forecast 2025-01-13 11:00:05 pvCorrectionFactor_11 0.56 (automatic - old factor: 1.00, Sun Alt range: 10, Cloud range: 70, Days in range: 1)&lt;br /&gt;
setstate Forecast 2025-01-13 12:00:02 pvCorrectionFactor_12 0.64 (automatic - old factor: 1.00, Sun Alt range: 15, Cloud range: 60, Days in range: 1)&lt;br /&gt;
setstate Forecast 2025-01-13 13:00:02 pvCorrectionFactor_13 0.88 (automatic - old factor: 1.00, Sun Alt range: 15, Cloud range: 60, Days in range: 1)&lt;br /&gt;
setstate Forecast 2025-01-13 14:00:03 pvCorrectionFactor_14 1.10 (automatic - old factor: 1.00, Sun Alt range: 15, Cloud range: 50, Days in range: 1)&lt;br /&gt;
setstate Forecast 2025-01-13 15:00:05 pvCorrectionFactor_15 1.01 (automatic - old factor: 1.06, Sun Alt range: 15, Cloud range: 45, Days in range: 2)&lt;br /&gt;
setstate Forecast 2025-01-13 16:00:06 pvCorrectionFactor_16 1.09 (automatic - old factor: 1.00, Sun Alt range: 10, Cloud range: 45, Days in range: 1)&lt;br /&gt;
setstate Forecast 2025-01-13 17:00:05 pvCorrectionFactor_17 0.28 (automatic - old factor: 0.52, Sun Alt range: 0, Cloud range: 45, Days in range: 2)&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 pvCorrectionFactor_Auto on_complex_ai&lt;br /&gt;
setstate Forecast 2024-10-30 19:42:19 setupStringAzimuth GarageSE=-55 GarageNW=125 HausNW=125 HausSW=35 HausSE1=-55 HausSE2=-55&lt;br /&gt;
setstate Forecast 2024-10-30 19:37:38 setupStringDeclination GarageSE=40 GarageNW=40 HausNW=50 HausSW=50 HausSE1=50 HausSE2=50&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 special_BatPowerIn_Sum 132 W&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 special_BatPowerOut_Sum 10 W&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 special_SunHours_Remain 0.00&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 special_SunMinutes_Remain 0&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 special_allStringsFullfilled 1&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 special_conForecastTillNextSunrise 9467 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 special_currentAPIinterval 0&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 special_currentRunMtsConsumer_01 132 min&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 special_currentRunMtsConsumer_02 184 min&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 special_currentRunMtsConsumer_03 329512 min&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 special_currentRunMtsConsumer_04 200 min&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 special_currentRunMtsConsumer_05 27 min&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 special_currentRunMtsConsumer_06 193 min&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 special_currentRunMtsConsumer_07 106 min&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 special_dayAfterTomorrowPVforecast 7344 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 special_daysUntilBatteryCare_01 12&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 special_daysUntilBatteryCare_02 12&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 special_lastretrieval_time 2025-01-13 18:53:54&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 special_lastretrieval_timestamp 1736790834&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 special_response_message success&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 special_runTimeAvgDayConsumer_01 545.94 min&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 special_runTimeAvgDayConsumer_02 382.10 min&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 special_runTimeAvgDayConsumer_03 1430.13 min&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 special_runTimeAvgDayConsumer_04 262.05 min&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 special_runTimeAvgDayConsumer_05 28.80 min&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 special_runTimeAvgDayConsumer_06 622.63 min&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 special_runTimeAvgDayConsumer_07 276.51 min&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 special_runTimeCentralTask 0.3744&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 special_runTimeLastAPIAnswer -&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 special_runTimeLastAPIProc -&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 special_runTimeTrainAI 1.2529&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 special_todayBatIn_01 3683.0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 special_todayBatIn_02 1617.0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 special_todayBatOut_01 2287.0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 special_todayBatOut_02 1057.0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 special_todayConForecastTillSunset 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 00:59:59 special_todayConsumptionForecast_01 509 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 01:59:58 special_todayConsumptionForecast_02 499 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 02:59:53 special_todayConsumptionForecast_03 501 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 03:59:52 special_todayConsumptionForecast_04 511 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 04:59:56 special_todayConsumptionForecast_05 503 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 05:59:59 special_todayConsumptionForecast_06 495 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 06:59:52 special_todayConsumptionForecast_07 504 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 07:59:50 special_todayConsumptionForecast_08 561 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 08:59:57 special_todayConsumptionForecast_09 768 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 09:59:58 special_todayConsumptionForecast_10 783 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 10:59:58 special_todayConsumptionForecast_11 692 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 11:59:49 special_todayConsumptionForecast_12 791 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 12:59:49 special_todayConsumptionForecast_13 910 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 13:59:49 special_todayConsumptionForecast_14 1022 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 14:59:49 special_todayConsumptionForecast_15 965 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 15:59:50 special_todayConsumptionForecast_16 812 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 16:59:49 special_todayConsumptionForecast_17 884 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 17:59:54 special_todayConsumptionForecast_18 928 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 special_todayConsumptionForecast_19 874 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 special_todayConsumptionForecast_20 763 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 special_todayConsumptionForecast_21 750 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 special_todayConsumptionForecast_22 765 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 special_todayConsumptionForecast_23 643 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 special_todayConsumptionForecast_24 601 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 special_todayDoneAPIcalls 0&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 special_todayDoneAPIrequests 4532&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 special_todayGridConsumption 5500.9 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 special_todayGridFeedIn 178.9 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 special_todayMaxAPIcalls n.a.&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 special_todayRemainingAPIcalls n.a.&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 special_todayRemainingAPIrequests n.a.&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:55 state updated&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
So sollte es dann als Ergebnis aussehen:&lt;br /&gt;
[[Datei:Bildschirmfoto 2025-01-13.png|zentriert|mini|Ergebnis nach kompletter Einrichtung]]&lt;br /&gt;
[[Datei:Bildschirmfoto 2025-01-13 Teil II.png|alternativtext=Unterer Teil der Anzeige|zentriert|mini]]&lt;br /&gt;
&lt;br /&gt;
== Praxisbeispiele und Lösungsansätze für Steuerungen ==&lt;br /&gt;
=== Fallstudie: Trockner darf pausiert werden, wenn nicht genug PV-Überschuss vorhanden ist, soll aber nach spätestens X Stunden fertig sein ===&lt;br /&gt;
Für diese Aufgabenstellung sind mehrere Schlüssel essentiell wichtig, &#039;&#039;&#039;power&#039;&#039;&#039;, &#039;&#039;&#039;mintime&#039;&#039;&#039;, &#039;&#039;&#039;mode&#039;&#039;&#039; und &#039;&#039;&#039;interruptable&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Die Bedingung, dass der Trockner nach spätestens X Stunden fertig sein soll, steht in einem Widerspruch zu der Möglichkeit den Trockner bei zu wenig PV-Überschuss unterbrechen zu können. Im Extremfall könnte nach dem Start die gesamte Planungszeit ungenügender Überschuss vorliegen und der Trockner nicht arbeiten. Der Schlüssel &#039;&#039;&#039;mintime&#039;&#039;&#039; (in Minuten) wird nun wie folgt angesetzt:&lt;br /&gt;
&lt;br /&gt;
 mintime = 2,5 x &amp;lt;gewöhnliche Betriebszeit für einen Trockengang (mneed, z.B. 120)&amp;gt; = 300 Minuten&lt;br /&gt;
&lt;br /&gt;
Das bedeutet, der Trockner wird nach 300 Minuten Laufzeit abgeschaltet werden bzw. vom Netz getrennt. Der Trockenvorgang wird natürlich bereits nach 120 Minuten beendet sein, sofern nach Start keine Unterbrechnung des Vorganges erfolgte. Wurde der Vorgang unterbrochen, soll der Trockner innerhalb &lt;br /&gt;
&lt;br /&gt;
 X+120 bis X+300 &lt;br /&gt;
&lt;br /&gt;
fertig sein. &lt;br /&gt;
&lt;br /&gt;
Demnach ist die ausgeführte Laufzeit des Trockners permanent zu überwachen, zu summieren (rtsum) und die notwendige Restzeit (mneed - rtsum) mit der noch verbleibenden Restzeit (mrest) bis zur geplanten Abschaltung des Consumers zu vergleichen. Ist der Consumer gestartet, wird die Laufzeit in der pvHistory für jede Stunde des Tages im Schlüssel &#039;&#039;minutescsmXX&#039;&#039; (XX = Consumernummer) aufgezeichnet und kann darüber ausgewertet werden. Somit ist:&lt;br /&gt;
&lt;br /&gt;
 msum = for (01 .. 24) {Summe von minutescsmXX} des aktuellen Tages&lt;br /&gt;
&lt;br /&gt;
Solange die Bedingung &lt;br /&gt;
&lt;br /&gt;
 mrest &amp;gt;= (mneed - msum)&lt;br /&gt;
&lt;br /&gt;
zutrifft, darf der Trockner unterbrochen werden, anderenfalls darf er nicht mehr unterbrochen werden da die Restzeit evtl. nicht ausreichen könnte um den Vorgang abzuschließen. Für die Planung des Trockners wird der Consumer wie gewöhnlich nach dem Bedarf im Modul registriert. Für die Steuerung der Unterbrechbarkeit wird noch ein Code erstellt, welcher im Trockner-Device &#039;&#039;Dryerdev&#039;&#039; das Reading &#039;&#039;SF_Int&#039;&#039; zur Unterbrechnungssteuerung verwaltet. Dieses Reading wird dem Schlüssel &#039;&#039;&#039;interruptable&#039;&#039;&#039; übergeben. Für das Beispiel wird der Consumer &#039;07&#039; verwendet, es kann natürlich jede andere freie Nummer verwendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 attr &amp;lt;Name&amp;gt; consumer07 Dryerdev:Trockner&lt;br /&gt;
                        icon=scene_cloves_dryer type=dryer power=2000 mode=must mintime=300 &lt;br /&gt;
                        on=on off=off etotal=energy_Wh:Wh pcurr=power:W&lt;br /&gt;
                        auto=automatic asynchron=1&lt;br /&gt;
                        interruptable=Dryerdev:SF_Int:1&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Kombination &#039;&#039;&#039;auto=automatic&#039;&#039;&#039; eröffnet die Möglickeit, in der Consumer-Legende den Trockner-Start zusätzlich manuell freizugeben oder zu verbieten. &lt;br /&gt;
&lt;br /&gt;
Im nachfolgenden Code werden modulinterne Subroutinen verwendet. Diese sind inklusive dem Paket &#039;&#039;FHEM::SolarForecast::&#039;&#039; anzugeben. Die erstellte Subroutine wird in die 99_mySolarForecastUtils.pm eingefügt. Die Datei 99_mySolarForecastUtils.pm wird vorher von 99_myUtils.pm kopiert und enthält für die Übersichtlichkeit nur Routinen für SolarForecast.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
############################################################################&lt;br /&gt;
#   Trocknersteuerung (einfügen in 99_mySolarForecastUtils.pm) &lt;br /&gt;
############################################################################&lt;br /&gt;
sub dryControl {&lt;br /&gt;
  my $name  = shift;&lt;br /&gt;
  my $c     = shift;                                                              # Nummer des Verbrauchers, z.B. 07&lt;br /&gt;
  my $mneed = shift;                                                              # gewöhlich benötigte Zeit für &lt;br /&gt;
                                                                                  # einen Trockengang, z.B. 120 (Minuten)&lt;br /&gt;
  &lt;br /&gt;
  $c            = sprintf &amp;quot;%02d&amp;quot;, $c;                                             # falls führende 0 vergessen wird&lt;br /&gt;
  my $dryer     = FHEM::SolarForecast::ConsumerVal ($name, $c, &#039;name&#039;, &#039;&#039;);       # Devicename des Trockners&lt;br /&gt;
  my $plstate   = FHEM::SolarForecast::ConsumerVal ($name, $c, &#039;planstate&#039;, &#039;&#039;); &lt;br /&gt;
  my $simpCstat = FHEM::SolarForecast::simplifyCstate ($plstate);                 # akt. Status des Consumers&lt;br /&gt;
&lt;br /&gt;
  if ($simpCstat =~ /started|interrupt|continu/xs) {                              # Vorgang ist gestartet&lt;br /&gt;
      my $t       = time;&lt;br /&gt;
      my $startts = FHEM::SolarForecast::ConsumerVal ($name, $c, &#039;planswitchon&#039;,  &#039;&#039;);&lt;br /&gt;
      my $stopts  = FHEM::SolarForecast::ConsumerVal ($name, $c, &#039;planswitchoff&#039;, &#039;&#039;);&lt;br /&gt;
      return if(!$startts || !$stopts);&lt;br /&gt;
      &lt;br /&gt;
      my $mrest = sprintf &#039;%.0f&#039;, (($stopts - $t) / 60);                         # Restlaufzeit (Minuten)      &lt;br /&gt;
      my $dt     = FHEM::SolarForecast::timestringsFromOffset ($startts, 0);&lt;br /&gt;
      my $day    = $dt-&amp;gt;{day};&lt;br /&gt;
      my $hstart = int $dt-&amp;gt;{hour} + 1;                                          # lfd. Stunde bei Trockner-Start &lt;br /&gt;
      my $msum   = 0;&lt;br /&gt;
	  &lt;br /&gt;
      for my $hod (1..24) {                                                      # bisherige Laufzeit des Trockners&lt;br /&gt;
          next if($hod &amp;lt; $hstart);&lt;br /&gt;
          $hod = sprintf &amp;quot;%02d&amp;quot;, $hod;&lt;br /&gt;
          $msum += FHEM::SolarForecast::HistoryVal ($name, $day, $hod, &amp;quot;minutescsm${c}&amp;quot;, 0);&lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
      my $dhash = $defs{$dryer};&lt;br /&gt;
&lt;br /&gt;
      if ($mrest &amp;gt;= ($mneed - $msum)) {&lt;br /&gt;
          readingsSingleUpdate ($dhash, &#039;SF_Int&#039;, 1, 0);                        # Interrupt-Freigabe&lt;br /&gt;
      }&lt;br /&gt;
      else {&lt;br /&gt;
          readingsSingleUpdate ($dhash, &#039;SF_Int&#039;, 0, 0);                        # keine Interrupt-Freigabe&lt;br /&gt;
  &lt;br /&gt;
      }&lt;br /&gt;
  }&lt;br /&gt;
  &lt;br /&gt;
return;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Damit die Routine mit jedem Zyklus im Modul ausgeführt wird, legen wir sie im Attribut &#039;&#039;&#039;ctrlUserExitFn&#039;&#039;&#039; an. Es werden der Name des SolarForecast-Devices, die Verbrauchernummer (07) und die gewöhnliche Laufzeit des Trockners für einen Trockenvorgang (120 Minuten) übergeben:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
attr &amp;lt;Name&amp;gt; ctrlUserExitFn  {  &lt;br /&gt;
                               ::dryControl ($name, &#039;07&#039;, 120);&lt;br /&gt;
                            }&lt;br /&gt;
&amp;lt;/pre&amp;gt; &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
=== Fallstudie: Umwälzpumpe Pool soll X Stunden am Tag laufen. Ist kein PV-Überschuß vorhanden, müssen die X Stunden/Tag trotzdem erreicht werden ===&lt;br /&gt;
&lt;br /&gt;
Die Vorgehensweise ähnelt der Fallstudie zuvor. Verändernd kommt hinzu, dass der Planungszeitraum bewusst sehr lang angesetzt wird mit der Option, den Zyklus des Consumers vorfristig zu beenden sobald die Laufzeit von X Stunden am Tag erreicht ist. &lt;br /&gt;
Im folgenden Beispiel soll die Pumpe 5 Stunden am Tag (300 Minuten) laufen und dabei mögliche PV-Überschüsse ausnutzen.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 attr &amp;lt;Name&amp;gt; consumer07 Pump:Zirkulationspumpe&lt;br /&gt;
                        icon=sani_pump type=other power=50 mode=must mintime=780 notafter=09&lt;br /&gt;
                        on=on off=off etotal=energy_Wh:Wh pcurr=power:W&lt;br /&gt;
                        auto=automatic &lt;br /&gt;
                        interruptable=1 swoffcond=Pump:SF_Abort:1&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Mit dieser Registrierung wird die Pumpe ca. 09 Uhr täglich starten. Wenn kein bzw. ungenügender PV-Überschuß vorhanden ist, erfolgt eine Unterbrechung nach dem Start. Ist genügender PV-Überschuß (wieder) vorhanden, erfolgt keine Unterbrechnung (mehr). Zusätzlich wird über den Schlüssel &#039;&#039;&#039;swoffcond&#039;&#039;&#039; eine vorfristige Beendigung veranlasst, sobald die gewünschte Tageslaufzeit erreicht ist.&lt;br /&gt;
&lt;br /&gt;
Zur Verwaltung des Schlüssels &#039;&#039;interruptable&#039;&#039; und des Readings &#039;&#039;SF_Abort&#039;&#039; verwenden wir eine leicht veränderte Subroutine aus dem vorhergehenden Fallbeispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
############################################################################&lt;br /&gt;
#   Pumpensteuerung (einfügen in 99_mySolarForecastUtils.pm) &lt;br /&gt;
############################################################################&lt;br /&gt;
sub pumpControl {&lt;br /&gt;
  my $name  = shift;&lt;br /&gt;
  my $c     = shift;                                                              # Nummer des Verbrauchers, z.B. 07&lt;br /&gt;
  my $mneed = shift;                                                              # Soll-Pumpenzeit, z.B. 300 (Minuten)&lt;br /&gt;
  &lt;br /&gt;
  $c            = sprintf &amp;quot;%02d&amp;quot;, $c;                                             # falls führende 0 vergessen wird&lt;br /&gt;
  my $pump      = FHEM::SolarForecast::ConsumerVal ($name, $c, &#039;name&#039;, &#039;&#039;);       # Devicename der Pumpe&lt;br /&gt;
  my $plstate   = FHEM::SolarForecast::ConsumerVal ($name, $c, &#039;planstate&#039;, &#039;&#039;);&lt;br /&gt;
  my $intbl     = FHEM::SolarForecast::ConsumerVal ($name, $c, &#039;interruptable&#039;, 1); &lt;br /&gt;
  my $simpCstat = FHEM::SolarForecast::simplifyCstate ($plstate);                 # akt. Status des Consumers&lt;br /&gt;
  my $dhash     = $defs{$pump};&lt;br /&gt;
&lt;br /&gt;
  readingsSingleUpdate ($dhash, &#039;SF_Abort&#039;, 0, 0);                                # default keine Zyklusbeendigung&lt;br /&gt;
&lt;br /&gt;
  if ($simpCstat =~ /started|interrupt|continu/xs) {                              # Vorgang ist gestartet&lt;br /&gt;
      my $t       = time;&lt;br /&gt;
      my $startts = FHEM::SolarForecast::ConsumerVal ($name, $c, &#039;planswitchon&#039;,  &#039;&#039;);&lt;br /&gt;
      my $stopts  = FHEM::SolarForecast::ConsumerVal ($name, $c, &#039;planswitchoff&#039;, &#039;&#039;);&lt;br /&gt;
      return if(!$startts || !$stopts);&lt;br /&gt;
      &lt;br /&gt;
      my $mrest = sprintf &#039;%.0f&#039;, (($stopts - $t) / 60);                         # Restlaufzeit (Minuten)      &lt;br /&gt;
      my $dt     = FHEM::SolarForecast::timestringsFromOffset ($startts, 0);&lt;br /&gt;
      my $day    = $dt-&amp;gt;{day};&lt;br /&gt;
      my $hstart = int $dt-&amp;gt;{hour} + 1;                                          # lfd. Stunde bei Pumpen Start &lt;br /&gt;
      my $msum   = 0;&lt;br /&gt;
	  &lt;br /&gt;
      for my $hod (1..24) {                                                      # bisherige Laufzeit der Pumpe&lt;br /&gt;
          next if($hod &amp;lt; $hstart);&lt;br /&gt;
          $hod = sprintf &amp;quot;%02d&amp;quot;, $hod;&lt;br /&gt;
          $msum += FHEM::SolarForecast::HistoryVal ($name, $day, $hod, &amp;quot;minutescsm${c}&amp;quot;, 0);&lt;br /&gt;
      }&lt;br /&gt;
     &lt;br /&gt;
      if ($msum &amp;gt;= $mneed) {&lt;br /&gt;
          readingsSingleUpdate ($dhash, &#039;SF_Abort&#039;, 1, 0);                       # vorfristige Zyklusbeendigung&lt;br /&gt;
          return;&lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
      if ($mrest &amp;gt;= ($mneed - $msum)) {                        &lt;br /&gt;
          fhem (&amp;quot;set $name attrKeyVal consumer$c interruptable=1&amp;quot;) if(!$intbl);  # Interrupt-Freigabe&lt;br /&gt;
      }&lt;br /&gt;
      else {                        &lt;br /&gt;
          fhem (&amp;quot;set $name attrKeyVal consumer$c interruptable=0&amp;quot;) if($intbl);   # keine Interrupt-Freigabe&lt;br /&gt;
      }&lt;br /&gt;
  }&lt;br /&gt;
  &lt;br /&gt;
return;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der Befehl &#039;&#039;attrKeyVal&#039;&#039; setzt den Schlüssel &#039;&#039;interruptable&#039;&#039; im Attribut &#039;&#039;consumer07&#039;&#039; dynamisch. Diese Strukturänderung wird automatisch im System gespeichert, sofern &#039;&#039;global-&amp;gt;autosave=1&#039;&#039; gesetzt ist. Diese Einstellung ist der FHEM default.  &lt;br /&gt;
&lt;br /&gt;
Damit die Routine mit jedem Zyklus im Modul ausgeführt wird, legen wir sie im Attribut &#039;&#039;&#039;ctrlUserExitFn&#039;&#039;&#039; an. Es werden der Name des SolarForecast-Devices, die Verbrauchernummer (07) und die Mindestlaufzeit der Umwälzpumpe (300 Minuten) übergeben:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
attr &amp;lt;Name&amp;gt; ctrlUserExitFn  {  &lt;br /&gt;
                               ::pumpControl ($name, &#039;07&#039;, 300);&lt;br /&gt;
                            }&lt;br /&gt;
&amp;lt;/pre&amp;gt; &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Dynamische Ladestromsteuerung eines Victron MultiPlus II Chargers mit Pylontech Batterie ===&lt;br /&gt;
Die nachfolgend beschriebene Lösung soll als Anregung für eigene Optimierungen verstanden werden und ist weiter ausbaufähig bzw. erweiterbar um zum Beipiel jahreszeitliche Anpassungen des Verfahrens.&lt;br /&gt;
&lt;br /&gt;
Die MultiPlus II Batteriewechselrichter von Victron sollten die Batterien nicht mit dem maximal möglichen Ladestrom beladen, da sich die Effizienz deutlich verschlechtert wenn sich die Ströme den jeweiligen Grenzwerten nähern ([https://www.victronenergy.com/upload/documents/Output-rating-operating-temperature-and-efficiency.pdf Lesestoff]). &lt;br /&gt;
Andererseits sollen die Ladeleistungen so gewählt werden, dass die verfügbare Solarenergie eine volle Aufladung der Akkus ermöglicht.&lt;br /&gt;
&lt;br /&gt;
Das Victron System ermöglicht das Auslesen und die Steuerung der Anlage via MQTT. Die Einbindung in FHEM über MQTT soll hier nicht beschrieben werden.&lt;br /&gt;
Aber wenn dies erfolgt ist, kann der Ladestrom z.B. mit dem Kommando:&lt;br /&gt;
&lt;br /&gt;
 set &amp;lt;Device&amp;gt; MaxChargeCurrent &amp;lt;Wert&amp;gt;&lt;br /&gt;
&lt;br /&gt;
eingestellt bzw. variiert werden.&lt;br /&gt;
&lt;br /&gt;
Das Modul bietet dem Nutzer die Möglichkeit in dem Attribut &#039;&#039;ctrlUserExitFn&#039;&#039; eigenen Code zur Auführung zu bringen. Der in diesem Attribut enthaltene Code wird am Ende jedes Zyklus (siehe Attribut &#039;&#039;plantControl-&amp;gt;cycleInterval&#039;&#039;) ausgeführt.&lt;br /&gt;
In dem Attribut wird dieser Code eingetragen dessen Funktion und Zusammenspiel nachfolgend erläutert wird:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
{ &lt;br /&gt;
  ## Vars&lt;br /&gt;
  ########&lt;br /&gt;
  my $batdev  = (split &amp;quot; &amp;quot;, ReadingsVal ($name, &#039;currentBatteryDev&#039;, &#039;&#039;))[0];  # Batteriedevice&lt;br /&gt;
  my $vebus   = &#039;MQTT2_cerboGX_c0619ab34e08_vebus&#039;;                            # Victron Vebus Device &lt;br /&gt;
  my $mppt1   = &#039;MQTT2_cerboGX_c0619ab34e08_solarcharger_Common&#039;;              # SmartLoader Device&lt;br /&gt;
  my $maxcspc = 105;                        # max. Ladesollstrom (A) nominal&lt;br /&gt;
  my $spcorr  = 0.9;                        # Korrekturfaktor Überschuss 90%&lt;br /&gt;
  my $sch     = 0.25;                       # Verkürzungsfaktor Zeit bis Vollladung vor Sunset             &lt;br /&gt;
  &lt;br /&gt;
  my $cclvl0  = 15;                         # Ladestrom Level 0 max. 5  A (240 W) pro WR  (720  W)&lt;br /&gt;
  my $cclvl1  = 27;                         # Ladestrom Level 1 max. 9  A (432 W) pro WR  (1296 W)&lt;br /&gt;
  my $cclvl2  = 54;                         # Ladestrom Level 2 max. 18 A (864 W) pro WR  (2592 W)&lt;br /&gt;
  my $cclvl3  = 81;                         # Ladestrom Level 3 max. 27 A (1296 W) pro WR (3888 W)&lt;br /&gt;
  ###############&lt;br /&gt;
  &lt;br /&gt;
  my $cofc   = ReadingsNum ($name,  &#039;special_todayConForecastTillSunset&#039;, 0);&lt;br /&gt;
  my $pvfc   = ReadingsNum ($name,  &#039;RestOfDayPVforecast&#039;, 0);&lt;br /&gt;
  my $cpv    = ReadingsNum ($name,  &#039;Current_PV&#039;,          0);&lt;br /&gt;
  my $mppt1c = ReadingsNum ($mppt1, &#039;DC_0_Current_value&#039;,  0);             # Load I MPPT1 (max. 44 A)&lt;br /&gt;
  my $ssts   = CurrentVal  ($hash,  &#039;sunsetTodayTs&#039;,        0);&lt;br /&gt;
  my $srts   = CurrentVal  ($hash,  &#039;sunriseTodayTs&#039;,       0);&lt;br /&gt;
  my $sdiff  = ($ssts - $srts) / 3600;                                     # Sonnengang heute in h &lt;br /&gt;
  &lt;br /&gt;
  my $solh   = ReadingsNum ($name, &#039;special_SunHours_Remain&#039;, 0);          # h bis SunSet&lt;br /&gt;
  $solh      = $solh - ($sdiff * $sch);                                    # h bis SunSet - X&lt;br /&gt;
  $solh      = $solh &amp;lt; 0 ? 0 : $solh;&lt;br /&gt;
   &lt;br /&gt;
  my $ahrem  = ReadingsNum ($batdev, &#039;EnergyRemain&#039;, 0) / 48;              # Ah Bat bis SOC&lt;br /&gt;
  $ahrem     = sprintf &amp;quot;%.0f&amp;quot;, $ahrem;&lt;br /&gt;
  &lt;br /&gt;
  my $fcdiff = ($pvfc - $cofc) * $spcorr;     # Korrektur d. noch prognostizierten Überschussenergie &lt;br /&gt;
  $fcdiff    = $fcdiff &amp;lt; 0 ? 0 : $fcdiff;     # Wh&lt;br /&gt;
  &lt;br /&gt;
  my $loadcur = $maxcspc;&lt;br /&gt;
  &lt;br /&gt;
  if ($cpv &amp;amp;&amp;amp; $solh &amp;amp;&amp;amp; $ahrem &amp;amp;&amp;amp; $fcdiff &amp;gt; $ahrem) {  # Ladeanforderung und Überschuss übersteigt &lt;br /&gt;
                                                      # Lademenge&lt;br /&gt;
      my $cspc = sprintf &amp;quot;%.2f&amp;quot;, ($ahrem / $solh);    # A = Ah / h -&amp;gt; A Soll Schätzung&lt;br /&gt;
&lt;br /&gt;
      Log3 ($name, 3, qq{$name - userFn -&amp;gt; Battery charging process should be completed }.&lt;br /&gt;
                      qq{in &amp;gt;$solh&amp;lt; hours});&lt;br /&gt;
      Log3 ($name, 3, qq{$name - userFn -&amp;gt; raw Target charging current: $cspc});&lt;br /&gt;
	  &lt;br /&gt;
      $cspc = sprintf &amp;quot;%.2f&amp;quot;, ($cspc - $mppt1c);      # SmartLoader IST berücksichtigen&lt;br /&gt;
	  &lt;br /&gt;
      Log3 ($name, 3, qq{$name - userFn -&amp;gt; Charge Current minus MPPT load Current &amp;gt;$mppt1c&amp;lt;: $cspc});&lt;br /&gt;
	  &lt;br /&gt;
      $loadcur = $cspc &amp;lt;= 0       ? 0       :     # nur MPPT Ladung&lt;br /&gt;
                 $cspc &amp;lt;= $cclvl0 ? $cclvl0 :     # Übergangsbereich&lt;br /&gt;
                 $cspc &amp;lt;= $cclvl1 ? $cclvl1 :     # Ladung mit Level 1 Leistung&lt;br /&gt;
                 $cspc &amp;lt;= $cclvl2 ? $cclvl2 :     # Ladung mit Level 2 Leistung&lt;br /&gt;
                 $cspc &amp;lt;= $cclvl3 ? $cclvl3 :     # Ladung mit Level 3 Leistung&lt;br /&gt;
                 $maxcspc;                        # max. Ladesollstrom&lt;br /&gt;
	  &lt;br /&gt;
	  Log3 ($name, 3, qq{$name - userFn -&amp;gt; MaxChargeCurrent calculated: $loadcur});&lt;br /&gt;
  }&lt;br /&gt;
  &lt;br /&gt;
  readingsSingleUpdate ($hash, &#039;userFn_Bat_MaxChargeCurrent&#039;,       $loadcur.&#039; A&#039;,          1);&lt;br /&gt;
  readingsSingleUpdate ($hash, &#039;userFn_Bat_HoursUntilChargeFinish&#039;, sprintf (&amp;quot;%.2f&amp;quot;,$solh), 1);&lt;br /&gt;
  CommandSet           (undef, &amp;quot;$vebus MaxChargeCurrent $loadcur&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Datei:Screenshot 2023-05-03 201457.png|right|thumb|300px|Vorhersage PV Erzeugung und Energieverbrauch des (restlichen) Tages]]&lt;br /&gt;
Zunächst wird der am aktuellen Tag zu erwartende Totalüberschuss solarer Energie ermittelt. Das erfolgt aus der Differenz von &#039;&#039;RestOfDayPVforecast&#039;&#039; und &#039;&#039;RestOfDayConsumptionForecast&#039;&#039; unter Berücksichtigung eines Sicherheitsabschlages. Das Ergebnis liegt in Wh vor und wird bezogen auf 48 V (die Spannung des Batteriesystems) in Ah umgerechnet. &lt;br /&gt;
&lt;br /&gt;
Die noch benötigte Ladung der Batterie zur Erreichung des Soll SOC-Wertes ermittelt das ReadingsNum &#039;&#039;special_SunHours_Remain&#039;&#039; aus dem Batteriedevice, welches vom Modulreading &#039;&#039;currentBatteryDev&#039;&#039; ausgelesen wird.&lt;br /&gt;
&lt;br /&gt;
Sofern der so ermittelte solare Überschuss des (Rest)Tages die benötigte Lademenge der Batterie übersteigt, wird der DC Ladestrom so verringert, dass er sich in einem effizienten Bereich des MultiPlus II bewegt und dennoch rechnerisch ausreicht auf den Soll SOC-Wert zu laden.  &lt;br /&gt;
&lt;br /&gt;
Zu diesem Zweck wird im Modul über die Auswahl von &#039;&#039;SunHours_Remain&#039;&#039; im Attribut &#039;&#039;ctrlStatisticReadings&#039;&#039; das zusätzliche Reading &#039;&#039;special_SunHours_Remain&#039;&#039; erzeugt. Aus dem (noch) erforderlichen Ladungswert &#039;&#039;&#039;EnergyRemain&#039;&#039;&#039; (Ah) und der um eine Sicherheitszeit (&#039;&#039;Verkürzungsfaktor Zeit bis Vollladung vor Sunset&#039;&#039;) reduzierte Zeit bis zum Sonnenuntergang wird die rechnerisch erforderliche mindest Ladestromstärke ermittelt.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Dieser Wert wird auf die zu Anfang des Codeblocks definierten Ladelevel (&#039;&#039;Ladestrom Level X&#039;&#039;) abstrahiert und ein passender &#039;&#039;&#039;effizienter&#039;&#039;&#039; Ladestrom-Bereich ausgewählt.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Screenshot 2023-05-03 201719.png|right|thumb|300px|Vorhersage PV Erzeugung und Energieverbrauch des (restlichen) Tages]]&lt;br /&gt;
&lt;br /&gt;
In allen anderen Fällen, wenn z.B. rechnerisch nicht genügend oder keine Solarenergie erzeugt wird (Nachts) oder der Speicher gefüllt ist, wird der maximale (Sollwert) des Ladestroms &#039;&#039;$maxcspc = 35&#039;&#039; eingestellt. Dieser Wert entspricht dem maximalen Ladestrom gemäß Herstellerunterlagen Pylontech.&lt;br /&gt;
&lt;br /&gt;
Die Einstellung erfolgt per Set-Kommando im Victron vebus MQTT Device &#039;&#039;MQTT2_cerboGX_c0619ab34e08_vebus&#039;&#039;. Dieses Device wurde auch während der MQTT Intergration des Victron Systems angelegt und wird hier nicht näher diskutiert.&lt;br /&gt;
Zur Kontrolle des kalkulierten Wertes werden noch die Readings &#039;&#039;SolCast_userFn_MaxChargeCurrent&#039;&#039; im Batteriedevice und &#039;&#039;userFn_Bat_MaxChargeCurrent&#039;&#039; im SolarForecast Device generiert.&lt;br /&gt;
&lt;br /&gt;
Neben der Effizienzoptimierung hat dieses Verfahren auch den Vorteil einer möglichst schonenden Batterieaufladung was der Lebensdauer der Komponenten zugute kommen sollte.&lt;br /&gt;
&lt;br /&gt;
=== Poolheizung in Abhängigkeit von vorhandenem Photovoltaik-Überschuss aktivieren/deaktivieren (Eigenverbrauch optimieren) ===&lt;br /&gt;
Eine (Whirl-)Poolheizung soll bei ausreichend überschüssiger Energie aus einer Photovoltaikanlage dynamisch gesteuert werden (Eigenverbrauchsoptimierung). Im folgenden Beispiel ist ein Device mit der Bezeichnung &#039;&#039;Forecast&#039;&#039; des Typs &#039;&#039;SolarForecast bereits&#039;&#039; angelegt und konfiguriert. Ein freies Consumer-Attribut &#039;&#039;consumer01&#039;&#039; wird beispielhaft wie folgt definiert:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
attr Forecast consumer01 MQTT2_layzspa type=other power=1950 mode=can on=&amp;quot;heater on&amp;quot; off=&amp;quot;heater off&amp;quot; pcurr=WATT interruptable=1 swstate=heaterstate:1:0 auto=Automatiksteuerung mintime=SunPath spignorecond=EVCharger22:LadungMitPVUeberschussActive:1 icon=scene_pool notbefore=8 notafter=20&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Poolheizung ist innerhalb FHEM bereits im Device &#039;&#039;MQTT2_layzspa&#039;&#039; integriert und lässt sich normalerweise per FHEM-Befehl &amp;quot;set &#039;&#039;MQTT2_layzspa heater on&amp;quot;&#039;&#039; oder &#039;&#039;&amp;quot;set MQTT2_layzspa heater off&amp;quot;&#039;&#039; steuern. Entsprechend werden die Schlüssel-Wert-Paare &#039;&#039;&amp;quot;on=&amp;quot;&#039;&#039; und &#039;&#039;&amp;quot;off=&amp;quot;&#039;&#039; definiert. &lt;br /&gt;
&lt;br /&gt;
Der Stromverbrauch einer aktiven Poolheizung ist uns bekannt und wird im Schlüssel &#039;&#039;&amp;quot;power=1950&amp;quot;&#039;&#039; definiert. Die Heizfunktion soll nur bei ausreichend Überschuss aktiviert werden dürfen und muss bei Unterschreitung sofort abgeschaltet werden. Diese Eigenschaften werden über die Schlüssel &#039;&#039;&amp;quot;mode=can&amp;quot;&#039;&#039; und &#039;&#039;&amp;quot;interruptable=&amp;quot;1&amp;quot;&#039;&#039; festgelegt. Zuletzt soll grundsätzlich nur in der Zeit zwischen 08:00 Uhr bis 20:00 Uhr geheizt werden dürfen. &lt;br /&gt;
&lt;br /&gt;
Die Schlüssel &#039;&#039;&amp;quot;notbefore=8&amp;quot;&#039;&#039; und &#039;&#039;&amp;quot;notafter=20&amp;quot;&#039;&#039; legen diese Eigenschaften fest. Zur Anzeige des Schaltzustands der Heizung wurde der Schlüssel &#039;&#039;&amp;quot;swstate=heaterstate:1:0&amp;quot;&#039;&#039; hinzugefügt (Wert 1 = Heizung aktiv, Wert 0= Heizung aus). &lt;br /&gt;
&lt;br /&gt;
==== Priorisierung vor externen PV-Überschuss gesteuerten Verbrauchern ====&lt;br /&gt;
Der Schlüssel-Wert &#039;&#039;&#039;&amp;quot;spignorecond=&amp;quot;&#039;&#039;&#039; bietet optional die Möglichkeit, einen Consumer auch ohne ausreichend PV-Überschuss einzuschalten. Bei erfüllter Bedingung wird der Verbraucher entsprechend der Planung eingeschaltet auch wenn zu dem Zeitpunkt kein PV Überschuss vorliegt. Im obigen Beispiel existiert in der Haus-Elektroinstallation eine Elektroauto-Ladestation mit eigener (externer) dynamischer PV-gesteuerter Fahrzeugladung ohne Integration in &#039;&#039;SolarForecast&#039;&#039;. &lt;br /&gt;
&lt;br /&gt;
In Abhängigkeit von PV-Generatorleistung und Sonneneinstrahlung besteht manchmal eine Möglichkeit, dass der gesamte PV-Überschuss zur Fahrzeugladung genutzt wird. Das Modul SolarForecast würde entsprechend keinen (oder zu wenig) PV-Überschuss erkennen und den Consumer &#039;&#039;MQTT2_layzspa&#039;&#039; nicht aktivieren. &lt;br /&gt;
&lt;br /&gt;
Durch Verwendung von &#039;&#039;spignorecond=EVCharger22:LadungMitPVUeberschussActive:1&#039;&#039; wird die Poolheizung gegenüber der Ladestation jedoch priorisiert mit PV-Überschuss versorgt. In diesem Beispiel wird Consumer1 aktiviert, wenn die Ladestation &amp;quot;&#039;&#039;EVCharger22&#039;&#039;&amp;quot; sich im PV-Überschuss-Modus befindet &#039;&#039;&#039;und&#039;&#039;&#039; momentan ein Fahrzeug lädt. In der Folge würde der Hausstromverbrauch um ca. 1950 Watt steigen, die externe Fahrzeugladestation diese Änderung registrieren und die Fahrzeugladeleistung entsprechend verringern oder ganz abbrechen.&lt;br /&gt;
&lt;br /&gt;
=== Luftentfeuchter PV-Überschuss gesteuert mit externer Zwangs-Einschaltbedingung ===&lt;br /&gt;
Im folgenden Beispiel wird ein Luftentfeuchter normalerweise nur bei ausreichend PV-Überschuss Energie eingeschaltet (Eigenverbrauchsoptimierung), soll jedoch bei hoher Luftfeuchtigkeit zusätzlich aktiviert werden.&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
attr Forecast SolarForecast ShellyPlug1 type=other power=300 mode=can on=on off=off pcurr=power:W interruptable=1 swstate=state:on:off mintime=SunPath locktime=900 notbefore=07 notafter=22 spignorecond=ESPEasy_ESP_Easy1_am2302_sensor:humidity:high icon=Ventilator_fett&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Das in FHEM bereits angelegte Device namens &#039;&#039;ShellyPlug1&#039;&#039; dient als Zwischenstecker zur Steuerung des Luftentfeuchters. Der Schlüsselwert &amp;quot;&#039;&#039;mintime=SunPath&#039;&#039;&amp;quot; definiert die zeitliche Einplanung von Sonnenauf- bis untergang. Jedoch soll das Gerät unter keinen Umständen in der Nacht eingeschaltet werden, was über die Schlüssel &#039;&#039;&amp;quot;notbefore=07&amp;quot;&#039;&#039; bzw. &#039;&#039;&amp;quot;notafter=22&amp;quot;&#039;&#039; festgelegt ist. Weiterhin wurde im Schlüssel &#039;&#039;&amp;quot;locktime=900&amp;quot;&#039;&#039; festgelegt, dass zwischen (möglichen) Ein- und Ausschaltintervallen wenigstens 15 Minuten Wartezeit vergehen soll.&lt;br /&gt;
&lt;br /&gt;
Der Schlüssel-Wert &#039;&#039;&#039;&amp;quot;spignorecond=&amp;quot;&#039;&#039;&#039; bietet optional die Möglichkeit, ein Consumer bei erfüllter Bedingung auch ohne ausreichend PV-Überschuss einzuschalten. In diesem Beispiel wird ein Raumluftsensor abgefragt. Bei Vorhandensein der Einschaltschwelle &amp;quot;high&amp;quot; aktiviert sich der Consumer zur geplanten Laufzeit zwischen 07:00 bis 22:00 Uhr solange, bis der Raumluftsensor einen abweichenden Wert meldet.&lt;br /&gt;
&lt;br /&gt;
=== PV-Vorhersage an evcc übertragen (PV-Überschussladen von E-Autos über steuerbare Wallboxen) ===&lt;br /&gt;
[https://evcc.io evcc] ist ein Energie-Management-System, mit dem u.a. Überschussladen von Elektroautos möglich ist. Hier ist beschreiben, wie die SolarForecast-Vorhersagedaten per API von evcc abgerufen werden.&lt;br /&gt;
Lege im SolarForecast-Device ein User-Reading mit dem Namen forecast_json an. Es wandelt die Forecast-Werte in JSON und ins UTC-Zeitformat um, so dass evcc die Daten einlesen kann. Der Code wird nur ausgeführt, wenn das Reading nextCycletime ein Event erzeugt.&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
	&lt;br /&gt;
forecast_json:nextCycletime.* {&lt;br /&gt;
&lt;br /&gt;
    use strict;&lt;br /&gt;
    use warnings;&lt;br /&gt;
    use JSON;&lt;br /&gt;
    use DateTime;&lt;br /&gt;
    use DateTime::Format::Strptime;&lt;br /&gt;
&lt;br /&gt;
    my $hour = 0;&lt;br /&gt;
    my $last_start_ts;    &lt;br /&gt;
    my @output;&lt;br /&gt;
    &lt;br /&gt;
    # Parser für Datum mit CET/CEST&lt;br /&gt;
    my $parser = DateTime::Format::Strptime-&amp;gt;new(&lt;br /&gt;
        pattern   =&amp;gt; &amp;quot;%Y-%m-%d %H:%M:%S&amp;quot;,&lt;br /&gt;
        time_zone =&amp;gt; &amp;quot;Europe/Berlin&amp;quot;,&lt;br /&gt;
    );&lt;br /&gt;
    my $start_ts;&lt;br /&gt;
    &lt;br /&gt;
    # Alle NextHour-Daten durchsuchen (als Fallback zur Vermeidung einer Endlosschleife auf max. 100 Std. begrenzen)&lt;br /&gt;
    while ($hour &amp;lt; 100) {&lt;br /&gt;
        my $hour_str = sprintf(&amp;quot;NextHour%02d&amp;quot;, $hour);&lt;br /&gt;
        my $start_str = FHEM::SolarForecast::NexthoursVal(&amp;quot;$NAME&amp;quot;, $hour_str, &amp;quot;starttime&amp;quot;, &amp;quot;na&amp;quot;);&lt;br /&gt;
        my $pvfc = FHEM::SolarForecast::NexthoursVal(&amp;quot;$NAME&amp;quot;, $hour_str, &amp;quot;pvfc&amp;quot;, &amp;quot;na&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
        # Schleife beenden, wenn keine Vorhersage-Werte mehr vorhanden sind&lt;br /&gt;
        last if $start_str eq &amp;quot;na&amp;quot; or $pvfc eq &amp;quot;na&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
        $start_ts = $parser-&amp;gt;parse_datetime($start_str);  # Zeitzone und Sommer/Winterzeit berücksichtigen&lt;br /&gt;
&lt;br /&gt;
        # Zeit nach UTC und ISO 8601 konvertieren und Vorhersagewert ergänzen&lt;br /&gt;
        push @output, {&lt;br /&gt;
            start =&amp;gt; $start_ts-&amp;gt;set_time_zone(&amp;quot;UTC&amp;quot;)-&amp;gt;iso8601() . &amp;quot;Z&amp;quot;,&lt;br /&gt;
            end   =&amp;gt; $start_ts-&amp;gt;add(hours =&amp;gt; 1)-&amp;gt;set_time_zone(&amp;quot;UTC&amp;quot;)-&amp;gt;iso8601() . &amp;quot;Z&amp;quot;,&lt;br /&gt;
            value =&amp;gt; 0 + $pvfc,&lt;br /&gt;
        };&lt;br /&gt;
        $hour++;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    # Prüfen, ob der letzte Wert 23 Uhr war -&amp;gt; zusätzliche Stunde anhängen, damit der Tag&lt;br /&gt;
    # bei evcc als vollständig angezeigt wird (siehe https://github.com/evcc-io/evcc/issues/22979)&lt;br /&gt;
    if ($start_ts-&amp;gt;hour == 23) {&lt;br /&gt;
&lt;br /&gt;
        # Zeit nach UTC und ISO 8601 konvertieren und Vorhersagewert ergänzen&lt;br /&gt;
        push @output, {&lt;br /&gt;
            start =&amp;gt; $start_ts-&amp;gt;set_time_zone(&amp;quot;UTC&amp;quot;)-&amp;gt;iso8601() . &amp;quot;Z&amp;quot;,&lt;br /&gt;
            end   =&amp;gt; $start_ts-&amp;gt;add(hours =&amp;gt; 1)-&amp;gt;set_time_zone(&amp;quot;UTC&amp;quot;)-&amp;gt;iso8601() . &amp;quot;Z&amp;quot;,&lt;br /&gt;
            value =&amp;gt; 0,&lt;br /&gt;
        };      &lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    # Ausgabe als JSON&lt;br /&gt;
    my $json = JSON-&amp;gt;new-&amp;gt;utf8-&amp;gt;pretty-&amp;gt;encode(\@output);&lt;br /&gt;
    return $json;&lt;br /&gt;
},&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
[[Datei:Screenshot 2025-08-04 104835.png|right|thumb|300px|Darstellung der PV-Vorhersage in evcc auf Basis der SolarForecast-Daten]]&lt;br /&gt;
Die Kommunikation läuft über den integrierten FHEM-Webserver, der die JSON-Daten aus dem Reading forecast_json veröffentlicht. Dazu wird in FHEM eine separate Webinstanz auf einem neuen Port erstellt, die ohne [[CsrfToken-HowTo|CSFR-Token]] (Achtung, Auswirkungen auf die Sicherheit beachten!) und nur von localhost aufrufbar ist (Voraussetzung: evcc läuft auf derselben Maschine wie FHEM):&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
define WEBapi FHEMWEB 8089&lt;br /&gt;
attr WEBapi csrfToken none&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;In der [https://docs.evcc.io/docs/tariffs#pv-vorhersage evcc-Konfig unter tariffs] steht die Quelle für die Solar-Vorhersage. Der FHEM-Befehl {ReadingsVal(&#039;solarforecast_dev&#039;,&#039;forecast_json&#039;,&#039;[]&#039;)} wird encoded und in die uri eingetragen. Trage statt solarforecast_dev den passenden Namen deines SolarForecast-Device ein. evcc holt die Daten aus dem Reading forecast_json dann regelmäßig ab.&amp;lt;syntaxhighlight lang=&amp;quot;yaml&amp;quot;&amp;gt;&lt;br /&gt;
solar:&lt;br /&gt;
  type: custom&lt;br /&gt;
  interval: 20m    &lt;br /&gt;
  forecast:&lt;br /&gt;
    source: http&lt;br /&gt;
    uri: http://localhost:8089/fhem?cmd=%7BReadingsVal%28%27solarforecast_dev%27%2C%27forecast_json%27%2C%27%5B%5D%27%29%7D&amp;amp;XHR=1&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Zur Verbesserung der Sicherheit kann das neue WEBapi Device auch ein [[CsrfToken-HowTo#csrfToken festlegen|festes CSFR-Token]] verwenden. Dieses Token muss dann in der evcc-Konfiguration an die uri mittels &amp;amp;fwcsrf=&amp;lt;festes token&amp;gt; angehängt werden.&lt;br /&gt;
&lt;br /&gt;
=== Poolsteuerung inkl. Eigenverbrauchsoptimierung und zusätzlichem Verbraucher, um Einspeiselimit zu vermeiden + manuelle Steuerung durch Sonderprogramme ===&lt;br /&gt;
&#039;&#039;&#039;Vorhandene Hardware:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* Poolpumpe, die über mehrere Stufen verfügt. Hier werden zwei verwendet.&lt;br /&gt;
** Pump Low: Energiesparprogramm um den Pool umzuwälzen, auch wenn nicht genug PV Leistung zur Verfügung steht. -&amp;gt; ca. 250W&lt;br /&gt;
** Pump High: Normalbetrieb wenn genug PV Überschuss zur Verfügung steht. -&amp;gt; ca. 800W zusätzlich&lt;br /&gt;
* Poolheizung: Wärmepumpe -&amp;gt; ca. 1,5kW elektrisch&lt;br /&gt;
* Nachbars Pool: Pumpe + Salzanlage +pH-Regler -&amp;gt; ca. 650W&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Anforderungen:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* Pool muss täglich mindestens 1x umgewälzt werden.&lt;br /&gt;
* Im Idealfall wird der Pool &amp;gt; 2x umgewälzt.&lt;br /&gt;
* Bei genügend Überschuss soll der Pool geheizt werden. Es soll nur Sonnenstrom für die Poolheizung verwendet werden.&lt;br /&gt;
* Um das Einspeiselimit zu vermeiden, soll auch geheizt werden, wenn der Pool schon warm genug ist. (&amp;gt;28,5 °C)&lt;br /&gt;
* Sollte trotzdem noch das Einspeiselimit erreicht werden, soll auch der Pool des Nachbarn versorgt werden.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Herausforderungen:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* Verbraucher dürfen nur in einer gewissen Reihenfolge geschaltet werden, auch wenn der Überschuss eine andere Reihenfolge ergeben würde.&lt;br /&gt;
* Pumpe mit mehreren Stufen muss als zwei Consumer abgebildet werden, um die Schaltlogik in SolarForecast abzubilden. Würde die Trennung außerhalb von Solarforecast realisiert, könnte Solarforecast den Verbraucher schlechter &amp;quot;lernen&amp;quot;, denn dann wäre es ein Consumer mit ständig wechselndem Verbrauch.&lt;br /&gt;
* Pool des Nachbarn darf nicht laut der benötigten Leistung geschaltet werden, sondern in Abhängigkeit der Einspeisebegrenzung.&lt;br /&gt;
&lt;br /&gt;
In diesem Solarforcast-Device sind 16 Verbraucher definiert. Im Weiteren werden jedoch nur die verwendeten betrachtet. Da diese Reglung mittlerweile recht komplex ist, werden auch viele Dinge, die nicht SolarForcast betreffen, aber trotzdem dazu gehören, gezeigt. Es trägt zum Verständnis bei und man kann sich hier sicher Ideen holen.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ergebnis:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
In den folgenden beiden Bildern sieht man das Regelverhalten recht gut.&lt;br /&gt;
[[Datei:Mustertag ohne Wolken.jpg|zentriert|mini|823x823px|Wolkenloser Tag]]&lt;br /&gt;
Hier erkennt man gut das Einschalten der jeweiligen Verbraucher (Pump Low, Pump High, Wärmepumpe und Nachbar Pool). Die Wärempumpe schaltet erst bei 5kW Überschuss ein, da der Pool eigentlich schon warm genug ist. Sie dient &amp;quot;nur&amp;quot; dazu, das EInspeiselimit zu verhindern.Auch das Auschalten ist wieder deutlich zu erkennen. Die Regleung folgt dem Überschuss recht gut. (Die Leistungsspitzen auf dem Plateau sind normale Großverbraucher im Haus.)&lt;br /&gt;
[[Datei:Wechselhaft.jpg|zentriert|mini|818x818px|Wechselhafter Tag.]]&lt;br /&gt;
In diesem Bild sieht man das &amp;quot;verzweifelte&amp;quot; Regeln um das wechselhafte Wetter auszugleichen. Hier gibt es keine korrekte Reaktion und es wird durch die Sperrzeiten und andere Delays versucht, ein zu häufiges hin- und herschalten möglichst zu vermeiden. In Summe aber auch bei so einem Wetter ein akzeptables Verhalten.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Definition der Consumer:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Allgemeine Erklärungen zu den Definitionen:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;asynchron=0&#039;&#039;&#039;: Da hier die Werte des Überschusses für das Timing der Schaltvorgänge verwendet werden, darf hier keine Eventsteuerung verwendet werden. Sonst kann man nicht mehr sagen, über welchen Zeitraum der Mittelwert oder der Median gebildet wurde. Dafür wurde die Zykluszeit des SolarForecast-Device auf 60 Sekunden geändert, um die Überlegungen zu vereinfachen. Bei Event-Steuerung könnte man nicht sagen ob die letzten 10 Events in den letzten 5 Sekunden oder in den letzten 10 Minuten aufgetreten sind.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;power=xxx&#039;&#039;&#039;: Wurde teilweise etwas höher als der tatsächliche Wert gewählt, um nach dem Einschalten sicherzustellen, dass nicht sofort der gesamte Überschuss &amp;quot;verbraucht&amp;quot; wird und somit gleich wieder abgeschaltet wird.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;auto=xxx&#039;&#039;&#039;: Jeder Verbraucher benötigt sein eigenes auto-Reading, da die Automatik für die Regelung in verschiedenen Stufen ein- und ausgeschaltet werden muss.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;dum_valve&#039;&#039;&#039;: Dummy-Device in dem die Zustände der Regelung abgebildet werden. Notifys lesen bzw. befüllen diesen Dummy und steuern dann die Hardware bzw. wird der Dummy durch Werte aus der Hardware passend befüllt.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;surpmeth=median_xx:&#039;&#039;&#039; Durch das asynchron=0 in allen Consumern und beim Generator wird von Solarforcast genau alle 60 Sekunden ein Zyklus ausgeführt. Das führt dazu, dass der Median genau über xx Minuten gebildet wird.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;locktime=xx:xx&#039;&#039;&#039;: Sicherheitsvorkehrung, dass nicht zu oft geschaltet werden kann. Schont die Hardware und verhindert ein Schwingen. Abhänging vom geschalteten Gerät. z.B. soll die Wärmepumpe möglichst wenig takten, um die Lebensdauer zu verlängern.&lt;br /&gt;
&lt;br /&gt;
Es gibt dann auch noch einige weitere Sicherheitsvorkehrungen, um diese Regelung möglichst stabil und hardwareschonend zu gestalten. Die Schwierigkeit sind nicht die Bilderbuchtage mit einem glatten Verlauf der PV-Leistung, sondern Tage mit ständig wechselndem Überschuss in der Nähe der Schaltgrenzen.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;swoncond= dum_valve:sf_true:{main::xxx_On}:&#039;&#039;&#039; Hier werden verschiedene Zustände geprüft, um ein Gerät nur dann einzuschalten, wenn es sinnvoll und sicher ist.&lt;br /&gt;
&lt;br /&gt;
Details unter &#039;&#039;&#039;99_mySolarForecastUtils.pm&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;swoffcond= dum_valve:sf_true:{main::xxx_Off}&#039;&#039;&#039;: Hier werden verschiedene Zustände geprüft, um ein Gerät nur dann auszuschalten, wenn es sinnvoll und sicher ist.&lt;br /&gt;
&lt;br /&gt;
Details unter &#039;&#039;&#039;99_mySolarForecastUtils.pm&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Pump Low: (consumer01)&#039;&#039;&#039;&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
dum_valve:Pool+Low&lt;br /&gt;
aliasshort=Low&lt;br /&gt;
asynchron=0&lt;br /&gt;
auto=pump_low_auto&lt;br /&gt;
icon=scene_pool&lt;br /&gt;
interruptable=0&lt;br /&gt;
mintime=SunPath:0:180&lt;br /&gt;
mode=must&lt;br /&gt;
noshow=0&lt;br /&gt;
notafter=08:00&lt;br /&gt;
off=&amp;quot;pump_low off&amp;quot;&lt;br /&gt;
on=&amp;quot;pump_low on&amp;quot;&lt;br /&gt;
pcurr=pump_low_power&lt;br /&gt;
power=250&lt;br /&gt;
surpmeth=median_13&lt;br /&gt;
switchdev=dum_valve&lt;br /&gt;
swoffcond=dum_valve:sf_true:{main::Check_Pump_Low_Off}&lt;br /&gt;
swoncond=dum_valve:sf_true:{main::Check_Pump_Low_On}&lt;br /&gt;
swstate=pump_low:on:off&lt;br /&gt;
type=other&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Erläuterungen:&lt;br /&gt;
&lt;br /&gt;
* Die Pumpe soll ab Sonnenaufgang, bis 180 Minuten nach Sonnenuntergang (mintime=SunPath:0:180), bei mindestens 250W Überschuss (power=250) gestartet werden.&lt;br /&gt;
* Die Pumpe muss spätestens um 8:00 eingeplant werden (notafter=08:00), um das Umwälzen auch bei Schlechtwetter zu garantieren.&lt;br /&gt;
* Die Pumpe muss gestartet werden (mode=must) sobald sie eingeplant ist.&lt;br /&gt;
* swoncond sorgt dafür, dass sie vor 8:00 nur bei genügend Überschuss gestartet wird, oder es 8:00 ist. swondcond ist mit der eigentlichen Einschaltbedingung (eingeplant und mode=must) UND verknüpft. -&amp;gt; siehe &#039;&#039;&#039;99_mySolarForecastUtils.pm&#039;&#039;&#039;&lt;br /&gt;
* Überschuss wird auch in swoncond mit der gewählten Methode geprüft.&lt;br /&gt;
* Die Pumpe darf, wenn sie gestartet wurde, auch bei fehlendem Überschuss nicht mehr unterbrochen werden. (interruptable=0)&lt;br /&gt;
* Die Pumpe wird am Ende der Einplanung ausgeschaltet oder wenn swoffcond erfüllt ist. swoffcond wird true, wenn eine gewisse Wassermenge umgewälzt wurde oder die Sicherheitsbedingungen nicht (mehr) erfüllt sind. swoffcond ist mit der eigentlichen Ausschaltbedingung (Planung endet) ODER verknüpft. -&amp;gt; siehe &#039;&#039;&#039;99_mySolarForecastUtils.pm&#039;&#039;&#039;&lt;br /&gt;
* {&#039;&#039;&#039;main::&#039;&#039;&#039;xxx} dient dazu, um die Funktion xxx in der 99_mySolarForecastUtils.pm aus Solarforecast erreichbar zu machen.&lt;br /&gt;
* dum_valve:sf_true ist ein Reading, das fix 1 ist und als Vergleichswert für den in Klammern folgenden Perlcode dient. Wenn dieses Reading und der Perlcode gleich sind (1==1), ist die swoncond bzw swoffcond erfüllt.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Pump High: (consumer03)&#039;&#039;&#039;&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
dum_valve:Pool+High&lt;br /&gt;
aliasshort=High&lt;br /&gt;
type=other power=1000 asynchron=0&lt;br /&gt;
auto=pump_high_auto&lt;br /&gt;
icon=scene_pool&lt;br /&gt;
pcurr=pump_high_power&lt;br /&gt;
switchdev=dum_valve&lt;br /&gt;
swstate=pump_high:on:off&lt;br /&gt;
mode=can&lt;br /&gt;
mintime=SunPath&lt;br /&gt;
on=&amp;quot;pump_high on&amp;quot;&lt;br /&gt;
off=&amp;quot;pump_high off&amp;quot;&lt;br /&gt;
surpmeth=median_5&lt;br /&gt;
noshow=0&lt;br /&gt;
locktime=180:300&lt;br /&gt;
interruptable=1&lt;br /&gt;
swoncond=dum_valve:sf_true:{main::Check_Pump_High_On}&lt;br /&gt;
swoffcond=dum_valve:sf_true:{main::Check_Pump_High_Off}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Erläuterungen:&lt;br /&gt;
&lt;br /&gt;
* Pump HIgh ist die selbe Pumpe, aber eine höhere Durchflussmenge&lt;br /&gt;
* Der Mehrverbrauch beträgt eigentlich 800W wird hier aber mit 1000W angegeben, um eine Hysterese von ca. 200W zu erreichen und damit ein schnelles Abschalten nach dem Einschalten zu verhindern.&lt;br /&gt;
* Diese Stufe wird nur bei genügend Überschuss aktiviert. (mode=can)&lt;br /&gt;
* Diese Stufe wird bei fehlendem Überschuss unterbrochen. (interruptable=1)&lt;br /&gt;
* Diese Stufe wird nur während Sonnenschein eingeplant. (mintime=SunPath)&lt;br /&gt;
* locktime sorgt dafür, dass bei schnell wechselnden Bedingungen, nicht ständig umgeschaltet wird. -&amp;gt; Schonung der Pumpe.&lt;br /&gt;
* swoncond und swoffcond -&amp;gt; siehe &#039;&#039;&#039;99_mySolarForecastUtils.pm&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Heatpump: (consumer05)&#039;&#039;&#039;&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
Pool_Strom_Heizung:Poolheizung&lt;br /&gt;
aliasshort=WP&lt;br /&gt;
type=other power=1800 asynchron=0&lt;br /&gt;
icon=sani_heating_heatpump&lt;br /&gt;
auto=heatpump_auto&lt;br /&gt;
pcurr=Pool_Pin32_monotonic_count_PowerCurrent:W&lt;br /&gt;
etotal=Pool_Pin32_monotonic_count_EnergyMeter:kWh&lt;br /&gt;
switchdev=dum_valve&lt;br /&gt;
swstate=heatpump:on:off&lt;br /&gt;
mode=can&lt;br /&gt;
locktime=600:600&lt;br /&gt;
mintime=SunPath&lt;br /&gt;
on=&amp;quot;heatpump on&amp;quot;&lt;br /&gt;
off=&amp;quot;heatpump off&amp;quot;&lt;br /&gt;
surpmeth=median_13&lt;br /&gt;
noshow=0&lt;br /&gt;
swoncond=dum_valve:sf_true:{main::CheckWPOn}&lt;br /&gt;
interruptable=1&lt;br /&gt;
swoffcond=dum_valve:sf_true:{main::CheckWPOff}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Erläuterungen:&lt;br /&gt;
&lt;br /&gt;
* Der Verbrauch ist hier wieder etwas zu hoch angegeben (1800W statt realen 1500W), um eine Hysterese zu erreichen.&lt;br /&gt;
* Da es sich hier um eine Wärmepumpe handelt, die nicht getaktet werden sollte, ist die locktime in beide Schaltrichtungen mit 10 Minuten relativ hoch.&lt;br /&gt;
* Die Ermittlung des Medians ist mit 13 Minuten recht lang, um den Überschuss über lange Zeit zu glätten, damit möglichst selten geschaltet wird. Diese 13 Minuten in Kombination mit der Locktime und der Hysterese führten bei dieser Anlage bisher dazu, dass höchstens 5 Zyklen am Tag geschaltet wurden. -&amp;gt; Hier kann das individuelle Schaltverhalten optimiert werden. Besseres Verfolgen des Überschusses vs. weniger Taktungen der Wärmepumpe.&lt;br /&gt;
* swoncond und swoffcond -&amp;gt; siehe &#039;&#039;&#039;99_mySolarForecastUtils.pm&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Nachbars Pool: (consumer07)&#039;&#039;&#039;&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
Shelly_Plug_S_1:Kurz+Pool&lt;br /&gt;
aliasshort=Kurz&lt;br /&gt;
type=other power=650 asynchron=0&lt;br /&gt;
icon=scene_pool&lt;br /&gt;
auto=pool_kurz_auto&lt;br /&gt;
pcurr=power:W&lt;br /&gt;
switchdev=dum_valve&lt;br /&gt;
swstate=pool_kurz:on:off&lt;br /&gt;
mode=can&lt;br /&gt;
mintime=SunPath&lt;br /&gt;
on=&amp;quot;pool_kurz on&amp;quot;&lt;br /&gt;
off=&amp;quot;pool_kurz off&amp;quot;&lt;br /&gt;
surpmeth=median_13&lt;br /&gt;
noshow=0&lt;br /&gt;
interruptable=1&lt;br /&gt;
locktime=600:300&lt;br /&gt;
swoncond=dum_valve:sf_true:{main::Check_Pool_Kurz_On}&lt;br /&gt;
swoffcond=dum_valve:sf_true:{main::Check_Pool_Kurz_Off}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Erläuterungen:&lt;br /&gt;
&lt;br /&gt;
* Dieser Consumer dient als Einspeisebegrenzungsschutz und wird aktiviert, sobald sich der Überschuss der Einspeisegrenze nähert.  -&amp;gt; siehe &#039;&#039;&#039;99_mySolarForecastUtils.pm&#039;&#039;&#039;&lt;br /&gt;
* Verbraucher kann recht träge geschaltet werden. (surpmeth=median_13 und locktime=600:300)&lt;br /&gt;
* Consumer wird ausgeschaltet wenn ein gewisser Überschuss unterschritten wird und somit die Einspeisegrenze wieder weit genug weg ist. -&amp;gt; siehe &#039;&#039;&#039;99_mySolarForecastUtils.pm&#039;&#039;&#039; &lt;br /&gt;
&#039;&#039;&#039;99_mySolarForecastUtils.pm&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Hier sind die Funktionen, die für diese Regelung verwendet werden, der Übersichtlichkeit halber in einzelnen Blöcken mit Erklärungen dargestellt.&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
package main;&lt;br /&gt;
use strict;&lt;br /&gt;
use warnings;&lt;br /&gt;
&lt;br /&gt;
my $pool_dummy = &#039;dum_valve&#039;;&lt;br /&gt;
my $pool_temperature = &#039;Pool_Temp&#039;;&lt;br /&gt;
my $flow_sensor = &#039;Pool_Pin_A0&#039;;&lt;br /&gt;
my $water_counter_l1 = &#039;pump_l1_hc&#039;;&lt;br /&gt;
my $water_counter_l2 = &#039;pump_l2_hc&#039;;&lt;br /&gt;
my $target_temp = 28.4; # maximum temperature for heating, without infeed limit&lt;br /&gt;
my $pool_kurz_sw_hofb = &#039;PwSw_Schalter&#039;; # Homematic switch at pool neighbour&lt;br /&gt;
my $pool_kurz_sw_kurz = &#039;Shelly_Plug_S_1&#039;; # Shelly switch for pool Kurz at home&lt;br /&gt;
&lt;br /&gt;
my $pump_low_id			= sprintf &amp;quot;%02d&amp;quot;, 1;&lt;br /&gt;
my $pump_high_id		= sprintf &amp;quot;%02d&amp;quot;, 3;&lt;br /&gt;
my $heatpump_id			= sprintf &amp;quot;%02d&amp;quot;, 5;&lt;br /&gt;
my $pool_kurz_id		= sprintf &amp;quot;%02d&amp;quot;, 7;&lt;br /&gt;
&lt;br /&gt;
my $switch_delay = 160; # just under three minutes&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
sub&lt;br /&gt;
SolarForecastUtils_Initialize($$)&lt;br /&gt;
{&lt;br /&gt;
  my ($hash) = @_;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Hier werden die IDs der Geräte definiert, um über sprechende Namen darauf zugreifen zu können.&lt;br /&gt;
* Einige Devices und Parameter, die immer wieder gebraucht werden sind hier angegeben.&lt;br /&gt;
* sprintf wird verwendet um sicherzustellen, dass die IDs zweistellig sind. z.B. 3 -&amp;gt; 03&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Pump Low&#039;&#039;&#039;&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
###################&lt;br /&gt;
### Pumpe - LOW ###&lt;br /&gt;
###################&lt;br /&gt;
&lt;br /&gt;
sub&lt;br /&gt;
Check_Pump_Low_On{&lt;br /&gt;
my $name = &#039;energy_mgmt&#039;;&lt;br /&gt;
my $surplus	= FHEM::SolarForecast::ConsumerVal ($name,$pump_low_id,&#039;surpmethResult&#039;,&#039;&#039;);&lt;br /&gt;
my $nom_power	= FHEM::SolarForecast::ConsumerVal ($name,$pump_low_id,&#039;power&#039;,&#039;&#039;);&lt;br /&gt;
my ($min, $hour) = (localtime())[1,2];&lt;br /&gt;
my $timer = 0;&lt;br /&gt;
if($hour &amp;gt; 8 || ($hour == 8 &amp;amp;&amp;amp; $min &amp;gt;= 0)){$timer=1;}&lt;br /&gt;
&lt;br /&gt;
if(ReadingsVal($pool_dummy,&amp;quot;valve_position&amp;quot;,&amp;quot;&amp;quot;) eq &amp;quot;normal&amp;quot;&lt;br /&gt;
	&amp;amp;&amp;amp; ReadingsVal($pool_dummy,&amp;quot;special_function&amp;quot;,&amp;quot;&amp;quot;) eq &amp;quot;none&amp;quot;&lt;br /&gt;
	&amp;amp;&amp;amp; ($surplus &amp;gt; $nom_power&lt;br /&gt;
		|| $timer&lt;br /&gt;
	)&lt;br /&gt;
)&lt;br /&gt;
	{&lt;br /&gt;
	return 1;}&lt;br /&gt;
else&lt;br /&gt;
	{&lt;br /&gt;
	return 0;}&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
sub&lt;br /&gt;
Check_Pump_Low_Off{&lt;br /&gt;
my $bath_mode = ReadingsVal($pool_dummy,&amp;quot;bath_mode&amp;quot;,0);&lt;br /&gt;
my $m3_l1 = ReadingsVal($water_counter_l1,&amp;quot;waterAmount&amp;quot;,0);&lt;br /&gt;
my $m3_l2 = ReadingsVal($water_counter_l2,&amp;quot;waterAmount&amp;quot;,0);&lt;br /&gt;
my $water_volume = AttrVal($pool_dummy,&amp;quot;water_volume&amp;quot;,42); #42m³&lt;br /&gt;
my $desired_cf_l1 = AttrVal($pool_dummy,&amp;quot;desired_cf_l1&amp;quot;,0); #circulation_factor_level1&lt;br /&gt;
my $desired_cf_l2 = AttrVal($pool_dummy,&amp;quot;desired_cf_l2&amp;quot;,0); #circulation_factor_level2&lt;br /&gt;
my $bathmode_factor = AttrVal($pool_dummy,&amp;quot;bathmode_factor&amp;quot;,1);&lt;br /&gt;
my $desired_amount_l1;&lt;br /&gt;
my $desired_amount_l2;&lt;br /&gt;
my $amount = $m3_l1 + $m3_l2;&lt;br /&gt;
if($bath_mode == 1){&lt;br /&gt;
	$desired_amount_l1 = $desired_cf_l1 * $water_volume;&lt;br /&gt;
	$desired_amount_l2 = $desired_cf_l2 * $water_volume;&lt;br /&gt;
}else{&lt;br /&gt;
	$desired_amount_l1 = $desired_cf_l1 * $water_volume/$bathmode_factor;&lt;br /&gt;
	$desired_amount_l2 = $desired_cf_l2 * $water_volume/$bathmode_factor;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
if(($amount &amp;gt; $desired_amount_l1)&lt;br /&gt;
	&amp;amp;&amp;amp; (ReadingsVal($pool_dummy,&amp;quot;pump_high&amp;quot;,&amp;quot;on&amp;quot;) eq &amp;quot;off&amp;quot;  &amp;amp;&amp;amp; ReadingsAge($pool_dummy,&amp;quot;pump_high&amp;quot;,0) &amp;gt;160)&lt;br /&gt;
	&amp;amp;&amp;amp; ReadingsVal($pool_dummy,&amp;quot;special_function&amp;quot;,&amp;quot;&amp;quot;) eq &amp;quot;none&amp;quot;&lt;br /&gt;
	&amp;amp;&amp;amp; ReadingsVal($pool_dummy,&amp;quot;valve_position&amp;quot;,&amp;quot;&amp;quot;) eq &amp;quot;normal&amp;quot;&lt;br /&gt;
)&lt;br /&gt;
	{&lt;br /&gt;
		return 1;&lt;br /&gt;
	}else{&lt;br /&gt;
		return 0;&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Einschalten:&lt;br /&gt;
&lt;br /&gt;
* Es wird der errechnete Wert für den Überschuss von SolarForecast verwendet und auch der benötigte Überschuss aus dem Consumer ausgelesen.&lt;br /&gt;
* Das Einschalten wird erst durch erfolgreiche Prüfung der Hardwarevoraussetzungen erlaubt. -&amp;gt; Das Motorventil steht richtig und es ist kein Sonderbetrieb (Reinigung, Massage, Winter, o.ä.) aktiv.&lt;br /&gt;
* Es muss entweder genügend Überschuss vorhanden sein oder es ist 8:00.&lt;br /&gt;
&lt;br /&gt;
Ausschalten:&lt;br /&gt;
&lt;br /&gt;
* Wenn der Badebetrieb aktiv ist (bath_mode==1, [bei Außentemperatur &amp;gt; 24°C]) wird nach 90m³ Gesamtumwälzung ausgeschaltet.&lt;br /&gt;
* Wenn der Badebetrieb nicht aktiv ist (bath_mode==0, [bei Außentemperatur &amp;lt; 20°C]) wird bereits nach 42m³ Gesamtumwälzung ausgeschaltet.&lt;br /&gt;
* Prüfung, ob Pump High schon aus ist. Sollte durch die Logik gar nicht möglich sein, aber es könnte manuell oder durch einen (Logik-)Fehler trotzdem eingeschaltet sein.&lt;br /&gt;
* Prüfung, ob alle Hardware-Funktionen in einem für die Automatik geeigneten Zustand sind.&lt;br /&gt;
* Es wird auch geprüft, ob Pump High seit mindestens 3 Minuten aus ist, um zu verhindern, dass wegen fehlendem Überschuss gleich mehrere Geräte ausgeschaltet werden.&lt;br /&gt;
* Da die Zykluszeit 60 Sekunden ist, führen die 160 Sekunden dazu, dass nach drei Minuten(180 Sekunden) ein definierter Wert gilt. Mit 180 Sekunden könnte es manchaml eine Minute mehr sein.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Pump High&#039;&#039;&#039;&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
####################&lt;br /&gt;
### Pumpe - HIGH ###&lt;br /&gt;
####################&lt;br /&gt;
&lt;br /&gt;
sub&lt;br /&gt;
Check_Pump_High_On{&lt;br /&gt;
my $bath_mode = ReadingsVal($pool_dummy,&amp;quot;bath_mode&amp;quot;,0);&lt;br /&gt;
my $m3_l1 = ReadingsVal($water_counter_l1,&amp;quot;waterAmount&amp;quot;,0);&lt;br /&gt;
my $m3_l2 = ReadingsVal($water_counter_l2,&amp;quot;waterAmount&amp;quot;,0);&lt;br /&gt;
my $water_volume = AttrVal($pool_dummy,&amp;quot;water_volume&amp;quot;,42); #42m³&lt;br /&gt;
my $desired_cf_l1 = AttrVal($pool_dummy,&amp;quot;desired_cf_l1&amp;quot;,0); #circulation_factor_level1&lt;br /&gt;
my $desired_cf_l2 = AttrVal($pool_dummy,&amp;quot;desired_cf_l2&amp;quot;,0); #circulation_factor_level2&lt;br /&gt;
my $bathmode_factor = AttrVal($pool_dummy,&amp;quot;bathmode_factor&amp;quot;,1);&lt;br /&gt;
my $desired_amount_l1;&lt;br /&gt;
my $desired_amount_l2;&lt;br /&gt;
my $amount = $m3_l1 + $m3_l2;&lt;br /&gt;
if($bath_mode == 1){&lt;br /&gt;
	$desired_amount_l1 = $desired_cf_l1 * $water_volume;&lt;br /&gt;
	$desired_amount_l2 = $desired_cf_l2 * $water_volume;&lt;br /&gt;
}else{&lt;br /&gt;
	$desired_amount_l1 = $desired_cf_l1 * $water_volume/$bathmode_factor;&lt;br /&gt;
	$desired_amount_l2 = $desired_cf_l2 * $water_volume/$bathmode_factor;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
if(($amount &amp;lt; $desired_amount_l2)&lt;br /&gt;
	&amp;amp;&amp;amp; ReadingsVal($pool_dummy,&amp;quot;special_function&amp;quot;,&amp;quot;&amp;quot;) eq &amp;quot;none&amp;quot;&lt;br /&gt;
	&amp;amp;&amp;amp; ReadingsVal($pool_dummy,&amp;quot;valve_position&amp;quot;,&amp;quot;&amp;quot;) eq &amp;quot;normal&amp;quot;&lt;br /&gt;
	&amp;amp;&amp;amp; (ReadingsVal($pool_dummy,&amp;quot;pump_low&amp;quot;,&amp;quot;off&amp;quot;) eq &amp;quot;on&amp;quot;  &amp;amp;&amp;amp; ReadingsAge($pool_dummy,&amp;quot;pump_low&amp;quot;,0) &amp;gt; $switch_delay)&lt;br /&gt;
)&lt;br /&gt;
	{return 1}&lt;br /&gt;
else&lt;br /&gt;
	{return 0}&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
sub&lt;br /&gt;
Check_Pump_High_Off{&lt;br /&gt;
my $bath_mode = ReadingsVal($pool_dummy,&amp;quot;bath_mode&amp;quot;,0);&lt;br /&gt;
my $m3_l1 = ReadingsVal($water_counter_l1,&amp;quot;waterAmount&amp;quot;,0);&lt;br /&gt;
my $m3_l2 = ReadingsVal($water_counter_l2,&amp;quot;waterAmount&amp;quot;,0);&lt;br /&gt;
my $water_volume = AttrVal($pool_dummy,&amp;quot;water_volume&amp;quot;,42); #42m³&lt;br /&gt;
my $desired_cf_l1 = AttrVal($pool_dummy,&amp;quot;desired_cf_l1&amp;quot;,0); #circulation_factor_level1&lt;br /&gt;
my $desired_cf_l2 = AttrVal($pool_dummy,&amp;quot;desired_cf_l2&amp;quot;,0); #circulation_factor_level2&lt;br /&gt;
my $bathmode_factor = AttrVal($pool_dummy,&amp;quot;bathmode_factor&amp;quot;,1);&lt;br /&gt;
my $desired_amount_l1;&lt;br /&gt;
my $desired_amount_l2;&lt;br /&gt;
my $amount = $m3_l1 + $m3_l2;&lt;br /&gt;
&lt;br /&gt;
if($bath_mode == 1){&lt;br /&gt;
	$desired_amount_l1 = $desired_cf_l1 * $water_volume;&lt;br /&gt;
	$desired_amount_l2 = $desired_cf_l2 * $water_volume;&lt;br /&gt;
}else{&lt;br /&gt;
	$desired_amount_l1 = $desired_cf_l1 * $water_volume/$bathmode_factor;&lt;br /&gt;
	$desired_amount_l2 = $desired_cf_l2 * $water_volume/$bathmode_factor;&lt;br /&gt;
}&lt;br /&gt;
if(($amount &amp;gt; $desired_amount_l2)&lt;br /&gt;
	&amp;amp;&amp;amp; (ReadingsVal($pool_dummy,&amp;quot;heatpump&amp;quot;,&amp;quot;on&amp;quot;) eq &amp;quot;off&amp;quot;  &amp;amp;&amp;amp; ReadingsAge($pool_dummy,&amp;quot;heatpump&amp;quot;,0) &amp;gt;160)&lt;br /&gt;
	&amp;amp;&amp;amp; ReadingsVal($pool_dummy,&amp;quot;special_function&amp;quot;,&amp;quot;&amp;quot;) eq &amp;quot;none&amp;quot;&lt;br /&gt;
	&amp;amp;&amp;amp; ReadingsVal($pool_dummy,&amp;quot;valve_position&amp;quot;,&amp;quot;&amp;quot;) eq &amp;quot;normal&amp;quot;&lt;br /&gt;
)&lt;br /&gt;
	{return 1}&lt;br /&gt;
else&lt;br /&gt;
	{return 0}&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Einschalten:&lt;br /&gt;
&lt;br /&gt;
* Prüfung, ob die gewünschte Umwälzung bezüglich des Badebetriebs noch nicht erreicht wurde.&lt;br /&gt;
* Prüfung der Hardwarevoraussetzungen.&lt;br /&gt;
* Prüfung, ob Pump Low schon seit mindestens 3 Minuten aktiv ist. (sollte eigentlich immer der Fall sein, wenn die Automatik für Pump High aktiv ist)&lt;br /&gt;
&lt;br /&gt;
Ausschalten:&lt;br /&gt;
&lt;br /&gt;
* Prüfung, ob die gewünschte Umwälzung bezüglich des Badebetriebs bereits erreicht wurde.&lt;br /&gt;
* Prüfung der Hardwarevoraussetzungen.&lt;br /&gt;
* Prüfung, ob die Wärmepumpe schon seit mindestens 3 Minuten aus ist. Ermöglicht drei Minuten lang, den Median für den Überschuss zu aktualisieren.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Wärmepumpe&#039;&#039;&#039;&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
##################&lt;br /&gt;
### Wärempumpe ###&lt;br /&gt;
##################&lt;br /&gt;
sub&lt;br /&gt;
CheckWPOn(){&lt;br /&gt;
my $name = &#039;energy_mgmt&#039;;&lt;br /&gt;
my $surplus_on = 5000; # minimum surplus to avoid infeed limit with heatpump&lt;br /&gt;
my $surplus_restart = 3000; # minimum surplus to re-enable heatpump, when avoidung infeed limit and a big consumer turned the hp off&lt;br /&gt;
&lt;br /&gt;
my $planstate = FHEM::SolarForecast::ConsumerVal ($name,$heatpump_id,&#039;planstate&#039;,&#039;&#039;);&lt;br /&gt;
my $surplus	= FHEM::SolarForecast::ConsumerVal ($name,$heatpump_id,&#039;surpmethResult&#039;,&#039;&#039;);&lt;br /&gt;
if ($planstate =~ /^([^:]+)/) {&lt;br /&gt;
	$planstate = $1;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
if(ReadingsVal($flow_sensor,&amp;quot;reading&amp;quot;,&amp;quot;off&amp;quot;) eq &amp;quot;on&amp;quot;&lt;br /&gt;
	&amp;amp;&amp;amp; (ReadingsVal($pool_dummy,&amp;quot;special_function&amp;quot;,&amp;quot;&amp;quot;) eq &amp;quot;none&amp;quot;&lt;br /&gt;
		|| ReadingsVal($pool_dummy,&amp;quot;special_function&amp;quot;,&amp;quot;&amp;quot;) eq &amp;quot;party&amp;quot;&lt;br /&gt;
	)&lt;br /&gt;
	&amp;amp;&amp;amp; ReadingsVal($pool_dummy,&#039;heat_protection&#039;,&#039;on&#039;) eq &amp;quot;off&amp;quot;&lt;br /&gt;
	&amp;amp;&amp;amp; ReadingsVal($pool_dummy,&amp;quot;valve_position&amp;quot;,&amp;quot;&amp;quot;) eq &amp;quot;normal&amp;quot;&lt;br /&gt;
	&amp;amp;&amp;amp; (ReadingsVal($pool_dummy,&amp;quot;pump_high&amp;quot;,&amp;quot;off&amp;quot;) eq &amp;quot;on&amp;quot;  &amp;amp;&amp;amp; ReadingsAge($pool_dummy,&amp;quot;pump_high&amp;quot;,0) &amp;gt; $switch_delay)&lt;br /&gt;
	&amp;amp;&amp;amp; (ReadingsVal($pool_temperature,&amp;quot;temperature&amp;quot;,30) &amp;lt; $target_temp &lt;br /&gt;
		|| (ReadingsVal($pool_temperature,&amp;quot;temperature&amp;quot;,15) &amp;gt;= $target_temp&lt;br /&gt;
			&amp;amp;&amp;amp; ( $surplus &amp;gt; $surplus_on&lt;br /&gt;
				|| (($planstate eq &amp;quot;replanned&amp;quot;) &amp;amp;&amp;amp; ($surplus &amp;gt; $surplus_restart))&lt;br /&gt;
			)&lt;br /&gt;
		)&lt;br /&gt;
	)&lt;br /&gt;
)&lt;br /&gt;
	{return 1}&lt;br /&gt;
else&lt;br /&gt;
	{return 0}&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
sub&lt;br /&gt;
CheckWPOff{&lt;br /&gt;
my $name = &#039;energy_mgmt&#039;;&lt;br /&gt;
my $surplus_off = 1000; #minimum surplus to keep heatpump on, when avoiding infeed limit&lt;br /&gt;
&lt;br /&gt;
my $surplus	= FHEM::SolarForecast::ConsumerVal ($name,$heatpump_id,&#039;surpmethResult&#039;,&#039;&#039;);&lt;br /&gt;
if((ReadingsVal($flow_sensor,&amp;quot;reading&amp;quot;,&amp;quot;on&amp;quot;) eq &amp;quot;off&amp;quot;&lt;br /&gt;
	|| !((ReadingsVal($pool_dummy,&amp;quot;special_function&amp;quot;,&amp;quot;&amp;quot;) eq &amp;quot;none&amp;quot;) &lt;br /&gt;
		|| (ReadingsVal($pool_dummy,&amp;quot;special_function&amp;quot;,&amp;quot;&amp;quot;) eq &amp;quot;party&amp;quot;)&lt;br /&gt;
	)&lt;br /&gt;
	|| ReadingsVal($pool_dummy,&#039;heat_protection&#039;,&#039;off&#039;) eq &amp;quot;on&amp;quot;&lt;br /&gt;
	|| ReadingsVal($pool_dummy,&amp;quot;valve_position&amp;quot;,&amp;quot;&amp;quot;) ne &amp;quot;normal&amp;quot;&lt;br /&gt;
	|| ((ReadingsVal($pool_temperature,&amp;quot;temperature&amp;quot;,15) &amp;gt; $target_temp)&lt;br /&gt;
		&amp;amp;&amp;amp; ($surplus &amp;lt; $surplus_off)&lt;br /&gt;
		)&lt;br /&gt;
	)&lt;br /&gt;
	&amp;amp;&amp;amp; (ReadingsVal($pool_dummy,&amp;quot;heatpump&amp;quot;,&amp;quot;off&amp;quot;) eq &amp;quot;on&amp;quot;  &amp;amp;&amp;amp; ReadingsAge($pool_dummy,&amp;quot;heatpump&amp;quot;,0) &amp;gt; $switch_delay)&lt;br /&gt;
)&lt;br /&gt;
	{return 1}&lt;br /&gt;
else&lt;br /&gt;
	{return 0}&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Einschalten:&lt;br /&gt;
&lt;br /&gt;
* Prüfung, ob Flussensor ein. (Pool_pin_A0) -&amp;gt; Wasser fließt durch die Wärmepumpe.&lt;br /&gt;
* Prüfung, ob Pool in Normal- oder Partybetrieb ist und das Ventil in Normalposition ist. In beiden Fällen läuft Pump High.&lt;br /&gt;
* Einschalten wenn genug Überschuss da ist und die Temperatur &amp;lt;28.4°C ist oder wenn über 5000W Überschuss (Einspeisebegrenzung bei 5900W) da ist, egal wie warm der Pool ist.&lt;br /&gt;
* Sonderfall: Wenn die Wärmepumpe durch einen/mehrere Großverbrucher &amp;quot;kurz&amp;quot; ausgeschaltet wurde (swoffcond) wird sie ausgeplant. Ein Notify plant sie wieder ein. Wenn das passiert, soll die Wärmepumpe aber wieder bei 3000W Überschuss gestartet werden, um die Energie noch weiter zu nutzen bis der Überschuss unter 1000W fällt. (Hysterese von 500W, da die WP nur 1500W braucht)&lt;br /&gt;
&lt;br /&gt;
Ausschalten:&lt;br /&gt;
&lt;br /&gt;
* Ausschalten sobald hardwaremäßig irgendetwas faul ist. (Betriebsart, Ventilstellung, Durchfluss)&lt;br /&gt;
* Ausschalten wenn kein Überschuss. (interruptable=1)&lt;br /&gt;
* Wenn Wassertemperatur über 28,5°C und der Überschuss kleiner als 1000W ist.&lt;br /&gt;
* Nur schalten, wenn Wärmepumpe läuft und seit mindestens 3 Minuten läuft (Sicherheit). Das verhindert das erneute Triggern des Notifys, wenn die Pumpe schon aus ist. Sollte nicht passieren, aber sicher ist sicher.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Nachbar Pool&#039;&#039;&#039;&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
###################&lt;br /&gt;
### Pool - KURZ ###&lt;br /&gt;
###################&lt;br /&gt;
sub&lt;br /&gt;
Check_Pool_Kurz_On{&lt;br /&gt;
my $name = &#039;energy_mgmt&#039;;&lt;br /&gt;
my $surplus	= FHEM::SolarForecast::ConsumerVal ($name,$pool_kurz_id,&#039;surpmethResult&#039;,&#039;&#039;);&lt;br /&gt;
if(($surplus &amp;gt; 3000)&lt;br /&gt;
	&amp;amp;&amp;amp; ((ReadingsVal($pool_dummy,&amp;quot;heatpump&amp;quot;,&amp;quot;off&amp;quot;) eq &amp;quot;on&amp;quot;  &amp;amp;&amp;amp; ReadingsAge($pool_dummy,&amp;quot;heatpump&amp;quot;,0) &amp;gt;160) || ReadingsVal($pool_dummy,&#039;heat_protection&#039;,&#039;off&#039;) eq &#039;on&#039; ))&lt;br /&gt;
	{return 1}&lt;br /&gt;
else&lt;br /&gt;
	{return 0}&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
sub&lt;br /&gt;
Check_Pool_Kurz_Off{&lt;br /&gt;
my $name = &#039;energy_mgmt&#039;;&lt;br /&gt;
my $surplus	= FHEM::SolarForecast::ConsumerVal ($name,$pool_kurz_id,&#039;surpmethResult&#039;,&#039;&#039;);&lt;br /&gt;
if(($surplus &amp;lt; 1000 )&lt;br /&gt;
	&amp;amp;&amp;amp; (ReadingsVal($pool_dummy,&amp;quot;pool_kurz&amp;quot;,&amp;quot;off&amp;quot;) eq &amp;quot;on&amp;quot;  &amp;amp;&amp;amp; ReadingsAge($pool_dummy,&amp;quot;pool_kurz&amp;quot;,0) &amp;gt;160))&lt;br /&gt;
	{return 1}&lt;br /&gt;
else&lt;br /&gt;
	{return 0}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Einschalten:&lt;br /&gt;
&lt;br /&gt;
* Überschuss ist nahe der Einspeisegrenze (5900W) und Wärmepumpe läuft seit mindestens 3 Minuten. Es wird schon bei 3000W eingeschaltet, um dem Nachbarn etwas Gutes zu tun ;-)&lt;br /&gt;
&lt;br /&gt;
Ausschalten:&lt;br /&gt;
&lt;br /&gt;
* Überschuss ist unter 1000W. -&amp;gt; Um noch Reserven für den eigenen Verbrauch zu haben.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Hilfsfunktionen&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ctrlUserExitFn&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Hier erfolgt nach jedem Zyklus die Festlegung der verschiedenen Automatikmodi.&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
#############################################################&lt;br /&gt;
##################### Exit Function #########################&lt;br /&gt;
#############################################################&lt;br /&gt;
my $ctrl_dummy	= &amp;quot;dum_valve&amp;quot;; # dummy device for pool-control&lt;br /&gt;
my $automatic	= ReadingsNum($ctrl_dummy,&amp;quot;sf_automatic&amp;quot;,1);&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
if($automatic){&lt;br /&gt;
&lt;br /&gt;
	my $pump_low_state = ReadingsVal($ctrl_dummy,&#039;pump_low&#039;,&#039;on&#039;);&lt;br /&gt;
	my $pump_low_auto	= FHEM::SolarForecast::ConsumerVal ($name, $pump_low_id, &#039;auto&#039;, &#039;&#039;);	# Automatikmodus der Pumpe&lt;br /&gt;
&lt;br /&gt;
	my $pump_high_state = ReadingsVal($ctrl_dummy,&#039;pump_high&#039;,&#039;on&#039;);&lt;br /&gt;
	my $pump_high_auto	= FHEM::SolarForecast::ConsumerVal ($name, $pump_high_id, &#039;auto&#039;, &#039;&#039;);	# Automatikmodus der Pumpe&lt;br /&gt;
&lt;br /&gt;
	my $heatpump_state = ReadingsVal($ctrl_dummy,&#039;heatpump&#039;,&#039;on&#039;);&lt;br /&gt;
	my $heatpump_auto	= FHEM::SolarForecast::ConsumerVal ($name, $heatpump_id, &#039;auto&#039;, &#039;&#039;);	# Automatikmodus der Wärmepumpe&lt;br /&gt;
	my $pool_heat_protection = ReadingsVal($ctrl_dummy,&#039;heat_protection&#039;,&#039;on&#039;);&lt;br /&gt;
&lt;br /&gt;
	my $pool_kurz_state = ReadingsVal($ctrl_dummy,&#039;pool_kurz&#039;,&#039;on&#039;);&lt;br /&gt;
	my $pool_kurz_auto	= FHEM::SolarForecast::ConsumerVal ($name, $pool_kurz_id, &#039;auto&#039;, &#039;&#039;);	# Automatikmodus des Pools&lt;br /&gt;
&lt;br /&gt;
	my $pump_low_delay	= FHEM::SolarForecast::ConsumerVal ($name, $pump_low_id, &#039;surpmeth&#039;, &#039;&#039;);&lt;br /&gt;
	if ($pump_low_delay =~ /_(\d+)/) {&lt;br /&gt;
    	$pump_low_delay = int(($1/2 + 1) * 60);&lt;br /&gt;
		Log3 (&amp;quot;SolarForecast&amp;quot;, 1, &amp;quot;SolarForecast: pump_low_delay: $pump_low_delay&amp;quot;);&lt;br /&gt;
	} else {&lt;br /&gt;
    	$pump_low_delay = 220;&lt;br /&gt;
		Log3 (&amp;quot;SolarForecast&amp;quot;, 1, &amp;quot;SolarForecast: warning - surpmeth for pump_low is: $pump_low_delay, but must be given as method_number, using fallback value&amp;quot;);&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	my $pump_high_delay	= FHEM::SolarForecast::ConsumerVal ($name, $pump_high_id, &#039;surpmeth&#039;, &#039;&#039;);&lt;br /&gt;
	if ($pump_high_delay =~ /_(\d+)/) {&lt;br /&gt;
    	$pump_high_delay = int(($1/2 + 1) * 60);&lt;br /&gt;
	} else {&lt;br /&gt;
    	$pump_high_delay = 160;&lt;br /&gt;
		Log3 (&amp;quot;SolarForecast&amp;quot;, 1, &amp;quot;SolarForecast: warning - surpmeth for pump_high is: $pump_high_delay, but must be given as method_number, using fallback value&amp;quot;);&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	my $heatpump_delay	= FHEM::SolarForecast::ConsumerVal ($name, $heatpump_id, &#039;surpmeth&#039;, &#039;&#039;);&lt;br /&gt;
	if ($heatpump_delay =~ /_(\d+)/) {&lt;br /&gt;
    	$heatpump_delay = int(($1/2 + 1) * 60);&lt;br /&gt;
	} else {&lt;br /&gt;
    	$heatpump_delay = 290;&lt;br /&gt;
		Log3 (&amp;quot;SolarForecast&amp;quot;, 1, &amp;quot;SolarForecast: warning - surpmeth for heatpump is: $heatpump_delay, but must be given as method_number, using fallback value&amp;quot;);&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	my $pool_kurz_delay	= FHEM::SolarForecast::ConsumerVal ($name, $pool_kurz_id, &#039;surpmeth&#039;, &#039;&#039;);&lt;br /&gt;
	if ($pool_kurz_delay =~ /_(\d+)/) {&lt;br /&gt;
    	$pool_kurz_delay = int(($1/2 + 1) * 60);&lt;br /&gt;
	} else {&lt;br /&gt;
    	$pool_kurz_delay = 160;&lt;br /&gt;
		Log3 (&amp;quot;SolarForecast&amp;quot;, 1, &amp;quot;SolarForecast: warning - surpmeth for pool_kurz is: $pool_kurz_delay, but must be given as method_number&amp;quot;);&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
############################&lt;br /&gt;
# Überhitzungsschutz aktiv #&lt;br /&gt;
############################&lt;br /&gt;
&lt;br /&gt;
if($pool_heat_protection eq &#039;on&#039;){&lt;br /&gt;
	if($heatpump_auto == 1){&lt;br /&gt;
		fhem(&amp;quot;set $ctrl_dummy heatpump_auto 0&amp;quot;);&lt;br /&gt;
	}&lt;br /&gt;
	if($heatpump_state eq &amp;quot;on&amp;quot;){&lt;br /&gt;
			fhem(&amp;quot;set $ctrl_dummy heatpump off&amp;quot;)&lt;br /&gt;
	}&lt;br /&gt;
	#################&lt;br /&gt;
	#### on-chain ###&lt;br /&gt;
	#################&lt;br /&gt;
	if($pump_low_state eq &amp;quot;on&amp;quot; &amp;amp;&amp;amp; ReadingsAge($ctrl_dummy,&#039;pump_low&#039;,0) &amp;gt; $pump_high_delay &amp;amp;&amp;amp; $pump_high_auto == 0  &amp;amp;&amp;amp; $pool_kurz_auto == 0){&lt;br /&gt;
		fhem(&amp;quot;set $ctrl_dummy pump_high_auto 1&amp;quot;);&lt;br /&gt;
	}elsif($pump_high_state eq &amp;quot;on&amp;quot; &amp;amp;&amp;amp; ReadingsAge($ctrl_dummy,&#039;pump_high&#039;,0) &amp;gt; $pool_kurz_delay &amp;amp;&amp;amp; $pool_kurz_auto == 0 &amp;amp;&amp;amp; $pump_high_auto == 1){&lt;br /&gt;
		fhem(&amp;quot;set $ctrl_dummy pool_kurz_auto 1&amp;quot;);&lt;br /&gt;
	}elsif($pool_kurz_state eq &amp;quot;on&amp;quot; &amp;amp;&amp;amp; $pool_kurz_auto == 1 &amp;amp;&amp;amp; $pump_high_auto == 1){&lt;br /&gt;
		fhem(&amp;quot;set $ctrl_dummy pump_high_auto 0&amp;quot;);&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	#################&lt;br /&gt;
	### off-chain ###&lt;br /&gt;
	#################&lt;br /&gt;
	if($pool_kurz_state eq &amp;quot;off&amp;quot; &amp;amp;&amp;amp; ReadingsAge($ctrl_dummy,&#039;pool_kurz&#039;,0) &amp;gt; $pump_high_delay &amp;amp;&amp;amp; $pool_kurz_auto == 1 &amp;amp;&amp;amp; $pump_high_auto == 0){&lt;br /&gt;
		fhem(&amp;quot;set $ctrl_dummy pump_high_auto 1&amp;quot;);&lt;br /&gt;
	}elsif($pump_high_state eq &amp;quot;off&amp;quot; &amp;amp;&amp;amp; $pump_high_auto == 1 &amp;amp;&amp;amp; $pool_kurz_auto == 1){&lt;br /&gt;
		fhem(&amp;quot;set $ctrl_dummy pool_kurz_auto 0&amp;quot;);&lt;br /&gt;
	}elsif($pump_low_state eq &amp;quot;off&amp;quot; &amp;amp;&amp;amp; $pump_high_state eq &amp;quot;off&amp;quot; &amp;amp;&amp;amp; $pump_high_auto == 1 &amp;amp;&amp;amp; $pool_kurz_auto == 0){&lt;br /&gt;
		fhem(&amp;quot;set $ctrl_dummy pump_high_auto 0&amp;quot;);&lt;br /&gt;
	}&lt;br /&gt;
##################################&lt;br /&gt;
# Überhitzungsschutz nicht aktiv #&lt;br /&gt;
##################################&lt;br /&gt;
}else{&lt;br /&gt;
	if($heatpump_auto == 0 &amp;amp;&amp;amp; $heatpump_state eq &amp;quot;off&amp;quot;){&lt;br /&gt;
		if($pool_kurz_state eq &amp;quot;on&amp;quot;){&lt;br /&gt;
			fhem(&amp;quot;set $ctrl_dummy pool_kurz off&amp;quot;);&lt;br /&gt;
		}	&lt;br /&gt;
		if($pool_kurz_auto == 1){&lt;br /&gt;
			fhem(&amp;quot;set $ctrl_dummy pool_kurz_auto 0&amp;quot;);&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
	#################&lt;br /&gt;
	#### on-chain ###&lt;br /&gt;
	#################&lt;br /&gt;
	if($pump_low_state eq &amp;quot;on&amp;quot; &amp;amp;&amp;amp; ReadingsAge($ctrl_dummy,&#039;pump_low&#039;,0) &amp;gt; $pump_high_delay &amp;amp;&amp;amp; $pump_high_auto == 0 &amp;amp;&amp;amp; $heatpump_auto == 0 &amp;amp;&amp;amp; $pool_kurz_auto == 0){&lt;br /&gt;
		fhem(&amp;quot;set $ctrl_dummy pump_high_auto 1&amp;quot;);&lt;br /&gt;
	}elsif($pump_high_state eq &amp;quot;on&amp;quot; &amp;amp;&amp;amp; ReadingsAge($ctrl_dummy,&#039;pump_high&#039;,0) &amp;gt; $heatpump_delay &amp;amp;&amp;amp; $heatpump_auto == 0 &amp;amp;&amp;amp; $pool_kurz_auto == 0 &amp;amp;&amp;amp; $pump_high_auto == 1){&lt;br /&gt;
		fhem(&amp;quot;set $ctrl_dummy heatpump_auto 1&amp;quot;);&lt;br /&gt;
	}elsif($heatpump_state eq &amp;quot;on&amp;quot; &amp;amp;&amp;amp; ReadingsAge($ctrl_dummy,&#039;heatpump&#039;,0) &amp;gt; $pool_kurz_delay &amp;amp;&amp;amp; $pump_high_state eq &amp;quot;on&amp;quot; &amp;amp;&amp;amp; $pool_kurz_auto == 0 &amp;amp;&amp;amp; $pump_high_auto == 1 &amp;amp;&amp;amp; $heatpump_auto == 1){&lt;br /&gt;
		# pump_high muss on sein, da sonst die automatik ausgeschaltet wird wenn die pumpe gerade interrupted ist.&lt;br /&gt;
		# Bei unterschiedlichen Medianlängen ist es möglich, dass WP ein ist und pump_high abschaltet wegen surplus == 0&lt;br /&gt;
		# Nachbarpool würde auf EIN warten, aber pump_high dürfte nicht wieder einschalten, obwohl genügend Überschuss da ist.&lt;br /&gt;
		fhem(&amp;quot;set $ctrl_dummy pump_high_auto 0&amp;quot;);&lt;br /&gt;
		fhem(&amp;quot;set $ctrl_dummy pool_kurz_auto 1&amp;quot;);&lt;br /&gt;
	}elsif($pool_kurz_state eq &amp;quot;on&amp;quot; &amp;amp;&amp;amp; $pool_kurz_auto == 1 &amp;amp;&amp;amp; $heatpump_auto == 1 &amp;amp;&amp;amp; $pump_high_auto == 0){&lt;br /&gt;
		fhem(&amp;quot;set $ctrl_dummy heatpump_auto 0&amp;quot;);&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	#################&lt;br /&gt;
	### off-chain ###&lt;br /&gt;
	#################&lt;br /&gt;
	if($pool_kurz_state eq &amp;quot;off&amp;quot; &amp;amp;&amp;amp; ReadingsAge($ctrl_dummy,&#039;pool_kurz&#039;,0) &amp;gt; $heatpump_delay &amp;amp;&amp;amp; $pool_kurz_auto == 1 &amp;amp;&amp;amp; $heatpump_auto == 0 &amp;amp;&amp;amp; $pump_high_auto == 0){&lt;br /&gt;
		fhem(&amp;quot;set $ctrl_dummy heatpump_auto 1&amp;quot;);&lt;br /&gt;
	}elsif($heatpump_state eq &amp;quot;off&amp;quot; &amp;amp;&amp;amp; ReadingsAge($ctrl_dummy,&#039;heatpump&#039;,0) &amp;gt; $pump_high_delay &amp;amp;&amp;amp; $heatpump_auto == 1 &amp;amp;&amp;amp; $pump_high_auto == 0 &amp;amp;&amp;amp; $pool_kurz_auto == 1){&lt;br /&gt;
		fhem(&amp;quot;set $ctrl_dummy pool_kurz_auto 0&amp;quot;);&lt;br /&gt;
		fhem(&amp;quot;set $ctrl_dummy pump_high_auto 1&amp;quot;);&lt;br /&gt;
	}elsif($pump_high_state eq &amp;quot;off&amp;quot; &amp;amp;&amp;amp; $heatpump_state eq &amp;quot;off&amp;quot; &amp;amp;&amp;amp; $pump_high_auto == 1 &amp;amp;&amp;amp; $heatpump_auto == 1 &amp;amp;&amp;amp; $pool_kurz_auto == 0){&lt;br /&gt;
#	heatpump muss aus sein, da sonst vor Ablauf der Locktime die Automatik ausgeschaltet wird und die WP nicht mehr ausschalten kann.&lt;br /&gt;
		fhem(&amp;quot;set $ctrl_dummy heatpump_auto 0&amp;quot;);&lt;br /&gt;
	}elsif($pump_low_state eq &amp;quot;off&amp;quot; &amp;amp;&amp;amp; $pump_high_state eq &amp;quot;off&amp;quot; &amp;amp;&amp;amp; $pump_high_auto == 1 &amp;amp;&amp;amp; $heatpump_auto == 0){&lt;br /&gt;
		fhem(&amp;quot;set $ctrl_dummy pump_high_auto 0&amp;quot;);&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
}&lt;br /&gt;
#################################################&lt;br /&gt;
# Check hardware-state vs. reading-state&lt;br /&gt;
# re-trigger notify&lt;br /&gt;
#################################################&lt;br /&gt;
my $delay_retrigger = 100; #delay time to wait until re-trigger the notify&lt;br /&gt;
&lt;br /&gt;
if(ReadingsVal($ctrl_dummy,&#039;pool_kurz&#039;,&#039;on&#039;) eq &#039;off&#039; &amp;amp;&amp;amp; ReadingsAge($ctrl_dummy,&#039;pool_kurz&#039;,0) &amp;gt; $delay_retrigger &amp;amp;&amp;amp; ReadingsVal($pool_kurz_sw_kurz,&#039;state&#039;,&#039;off&#039;) eq &#039;on&#039;){&lt;br /&gt;
	# re-trigger notify&lt;br /&gt;
	fhem(&amp;quot;trigger dum_valve pool_kurz: off&amp;quot;);&lt;br /&gt;
	Log3 (&amp;quot;SolarForecast&amp;quot;, 1, &amp;quot;SolarForecast: Pool-Kurz-off must be re-triggered&amp;quot;);&lt;br /&gt;
	DebianMail(&#039;clemens@familie-hofbauer.at&#039;,&#039;Notify Pool Kurz&#039;,&amp;quot;Notify hat nicht ausgeschaltet -&amp;gt; re-trigger&amp;quot;);&lt;br /&gt;
}elsif(ReadingsVal($ctrl_dummy,&#039;pool_kurz&#039;,&#039;off&#039;) eq &#039;on&#039; &amp;amp;&amp;amp; ReadingsAge($ctrl_dummy,&#039;pool_kurz&#039;,0) &amp;gt; $delay_retrigger &amp;amp;&amp;amp; ReadingsVal($pool_kurz_sw_kurz,&#039;state&#039;,&#039;on&#039;) eq &#039;off&#039;){&lt;br /&gt;
	# re-trigger notify&lt;br /&gt;
	fhem(&amp;quot;trigger dum_valve pool_kurz: on&amp;quot;);&lt;br /&gt;
	Log3 (&amp;quot;SolarForecast&amp;quot;, 1, &amp;quot;SolarForecast: Pool-Kurz-on must be re-triggered&amp;quot;);&lt;br /&gt;
	DebianMail(&#039;clemens@familie-hofbauer.at&#039;,&#039;Notify Pool Kurz&#039;,&amp;quot;Notify hat nicht eingeschaltet -&amp;gt; re-trigger&amp;quot;);&lt;br /&gt;
}elsif((ReadingsVal($pool_kurz_sw_kurz,&#039;state&#039;,&#039;off&#039;) eq &#039;set_on&#039; || ReadingsVal($pool_kurz_sw_kurz,&#039;state&#039;,&#039;off&#039;) eq &#039;set_off&#039;) &amp;amp;&amp;amp; ReadingsAge($pool_kurz_sw_kurz,&#039;state&#039;,0) &amp;gt; $delay_retrigger){&lt;br /&gt;
	# state not clear&lt;br /&gt;
	Log3 (&amp;quot;SolarForecast&amp;quot;, 1, &amp;quot;SolarForecast: Pool-Kurz state unclear&amp;quot;);&lt;br /&gt;
	DebianMail(&#039;clemens@familie-hofbauer.at&#039;,&#039;Status Pool Kurz unklar&#039;,&amp;quot;Status HM-Schalter set_xx&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
1;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Erläuterungen:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Verzögerungen der einzelnen Stufen:&#039;&#039;&#039;&lt;br /&gt;
** Die delays der einzelnen Stufen werden aus den surpmethods berechnet. Das dient dazu, dass der Median mit den Werten nach dem ein-/ausschalten des vorheriegen Verbrauchers bis zur Hälfte + einen Wert gefüllt wird und somit sicher das Umschalten des vorherigen Verbrauchers den Median nicht mehr beeinflusst.&lt;br /&gt;
* &#039;&#039;&#039;Überhitzungsschutz:&#039;&#039;&#039;&lt;br /&gt;
** Da durch die Wärmepumpe Überschuss &amp;quot;verheizt&amp;quot; wird, der durch das Einspeiselimit gar nicht produziert würde, kann es vorkommen, dass der Pool unangenehm warm wird.&lt;br /&gt;
** Um das zu vermeiden gibt es ein Reading, welches gesetzt wird, wenn der Pool eine gewisse Temperatur überschreitet. Dieses Setzen und Zurücksetzen erfolgt mit einer Hysterese von 0.2°C, um ein Takten der Wärmepumpe zu verhindern. Diese 0.4°C sind bei dieser Wassermenge einige Stunden.&lt;br /&gt;
** Wenn der Überhitzungsschutz aktiv ist, wird die Wärmepumpe in der Kette übersprungen.&lt;br /&gt;
** Wenn sich der Hitzeschutz während des Betriebs ändert, wird die Wärmepumpe bzw. der Pool vom Nachbarn passend ausgeschaltet und die Modi entsprechend gesetzt.&lt;br /&gt;
* &#039;&#039;&#039;On-chain:&#039;&#039;&#039;&lt;br /&gt;
** Wenn am Morgen Pump Low seit $pump_low_delay aktiv ist und alle folgenden Consumer noch nicht, dann wird die nächste Stufe freigegeben. -&amp;gt; Pump High&lt;br /&gt;
** Wenn Pump High seit $pump_high_delay aktiv ist und alle folgenden Consumer noch nicht, wird die Wärmepumpe freigegeben. -&amp;gt; Heatpump&lt;br /&gt;
** Wenn die Wärmepumpe seit $heatpump_delay aktiv ist und der letzte Consumer noch nicht, wird der letzte Verbraucher freigegeben. -&amp;gt; Pool Kurz Gleichzeitig wird die Automatik für Pump High ausgeschaltet, da dieser Verbraucher erst unterbrochen werden darf wenn die Wärmepumpe aus ist!&lt;br /&gt;
** Wenn der letzte Verbraucher aktiviert ist, wird die Automatik der Wärmepumpe deaktiviert, da ja zuerst der letzte Verbraucher (Pool Kurz) wieder ausgeschaltet werden muss, bevor der vorletzte (Wärmepumpe) wieder ausgeschaltet werden darf. Da es keinen nachfolgenden Verbraucher mehr gibt, kann hier auf ein delay verzichtet werden, da es nicht passieren kann, dass zwei Verbraucher wegen sehr hohem Überschuss unmittelbar hintereinander geschaltet werden können.&lt;br /&gt;
[[Datei:Poolsteuerung mit unterschiedlichen Automatikmodi.jpg|alternativtext=Poolsteuerung|mini|Consumer der Poolsteuerung|500x500px]]&lt;br /&gt;
&#039;&#039;&#039;Wichtig:&#039;&#039;&#039; Es dürfen immer nur maximal zwei unterbrechbare Consumer im Automatikmodus sein. Der &amp;quot;aktuelle&amp;quot;, der ja aufgrund von fehlendem Überschuss jederzeit unterbrochen werden kann und der &amp;quot;folgende&amp;quot; Consumer, der auf das Einschalten wartet. Pump Low ist immer im Automatikmodus, da diese nie unterbrochen wird. (mode=must, interruptable=0)&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Off-chain:&#039;&#039;&#039;&lt;br /&gt;
** Wenn der letzte Verbraucher seit mindestens der delay-Zeit aufgrund von fehlendem Überschuss inaktiv war, im Automatikmodus ist und der vorherige Verbraucher nicht im Automatikmodus ist, wird der vorherige Verbraucher in den Automatikmodus geschaltet und wartet jetzt auch auf das Ausschalten. Wenn der Überschuss steigt, springt man wieder in die on-chain und der letzte Verbraucher wird eingeschaltet und die Automatik des vorletzten Verbrauchers deaktiviert.&lt;br /&gt;
** Wenn die Wärmepumpe seit mindestens der delay-Zeit aus ist, sich im Automatikmodus befind und Pump High noch nicht im Automatikmodus ist, wird die Automatik für den Nachbarpool ausgeschaltet, da er ja jetzt nicht mehr einschalten darf, da zuerst die Wärmepumpe ein sein müsste. Die Automatik von Pump High wird wieder aktiviert, da diese jetzt auf das Ausschalten wartet. Steigt der Überschuss wieder, wird in den entsprechenden On-chain Zweig gesprungen.&lt;br /&gt;
** Wenn Pump High mindestens die delay-Zeit aus ist, wird die Automatik der Wärmepumpe deaktiviert, da diese ja erst einschalten darf, wenn Pump High wieder läuft. Steigender Überschuss führt wieder zum aktivieren von Pump High und zum Sprung in den entsprechenden On-chain Zweig.&lt;br /&gt;
** Wenn Pump Low ausgeschaltet wird, wird sofort die Automatik für Pump High ausgeschaltet, da durch das spätere Ausschalten von (der evtl. wieder eingeschalteten) Pump High, hardwaremäßig wieder Pump Low aktiviert wird, aber durch SolarForecast nie ausgeschaltet wird, da Pump Low vor ein paar Minuten ja schon logisch ausgeschaltet wurde.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Wichtig:&#039;&#039;&#039; Es dürfen immer nur maximal zwei unterbrechbare Consumer im Automatikmodus sein. Der &amp;quot;aktuelle&amp;quot;, der ja aufgrund von neuem Überschuss jederzeit wieder gestartet werden kann und der &amp;quot;vorherige&amp;quot; Consumer, der auf das Ausschalten wartet.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Synchronisation Hardware vs. Reading:&#039;&#039;&#039;Manchmal passiert es, dass durch die etwas aufwendigen Umschaltprozesse die Hardware dem Reading nicht folgt/folgen kann. Evtl. Gründe sind Funkunterbrechung, verzögerte Umschaltung usw. Daher wird bei jedem Zyklus der Status geprüft und evtl. das event nochmal getriggert, um die Umschaltung erneut auszuführen.&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Hilfs-Notifys für die Automatik&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Da durch das Verwenden von swoffcond die Verbraucher beim Ausschalten auch ausgeplant werden, aber eigentlich &amp;quot;nur&amp;quot; unterbrochen werden sollen, müssen sie wieder neu eingeplant werden. Dazu sind notifys nötig. Wichtig: IDs müssen zweistellig angegeben werden!&lt;br /&gt;
&lt;br /&gt;
Wärmepumpe:&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
dum_valve:heatpump:.off sleep 10; set energy_mgmt consumerNewPlanning 05&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Nachbar Pool:&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
dum_valve:pool_kurz:.off sleep 10; set energy_mgmt consumerNewPlanning 07&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Eine Pumpe mit einem Energiezähler als zwei Consumer verwenden&#039;&#039;&#039;  &lt;br /&gt;
&lt;br /&gt;
Die verwendete Pumpe hat vier Betriebsarten mit unterschiedlichen Leistungen. Da sie als zwei Consumer abgebildet wird, führt das dazu, dass die Leistung fälschlicherweise von SolarForecast auch zweimal gezählt wird. Die Lösung sind Dummy-Readings, welche die Leistungsdaten passend auseinander rechnen.&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
Pool_Strom_Pumpe:Pool_Pin31_monotonic_count_PowerCurrent:.* {&lt;br /&gt;
my $power = $EVTPART1;&lt;br /&gt;
my $pump_low = ReadingsVal(&#039;dum_valve&#039;,&#039;pump_low&#039;,&#039;off&#039;);&lt;br /&gt;
my $pump_high = ReadingsVal(&#039;dum_valve&#039;,&#039;pump_high&#039;,&#039;off&#039;);&lt;br /&gt;
my $power_low_sim = 250;&lt;br /&gt;
&lt;br /&gt;
if($pump_low eq &amp;quot;on&amp;quot; &amp;amp;&amp;amp; $pump_high eq &amp;quot;on&amp;quot;){	&lt;br /&gt;
	my $power_low = $power_low_sim; #fiktiver Verbrauch der niedrigen Stufe&lt;br /&gt;
	my $power_high = $power - $power_low_sim;&lt;br /&gt;
	fhem(&amp;quot;set dum_valve pump_low_power $power_low&amp;quot;);&lt;br /&gt;
	fhem(&amp;quot;set dum_valve pump_high_power $power_high&amp;quot;);&lt;br /&gt;
	}&lt;br /&gt;
elsif($pump_low eq &amp;quot;on&amp;quot; &amp;amp;&amp;amp; $pump_high eq &amp;quot;off&amp;quot;){&lt;br /&gt;
		fhem(&amp;quot;set dum_valve pump_low_power $power&amp;quot;);&lt;br /&gt;
		fhem(&amp;quot;set dum_valve pump_high_power 0&amp;quot;);&lt;br /&gt;
	}&lt;br /&gt;
elsif($pump_low eq &amp;quot;off&amp;quot; &amp;amp;&amp;amp; $pump_high eq &amp;quot;off&amp;quot;){&lt;br /&gt;
		fhem(&amp;quot;set dum_valve pump_low_power 0&amp;quot;);&lt;br /&gt;
		fhem(&amp;quot;set dum_valve pump_high_power 0&amp;quot;);&lt;br /&gt;
}elsif($pump_low eq &amp;quot;off&amp;quot; &amp;amp;&amp;amp; $pump_high eq &amp;quot;on&amp;quot;){&lt;br /&gt;
		fhem(&amp;quot;set dum_valve pump_low_power 0&amp;quot;);&lt;br /&gt;
		fhem(&amp;quot;set dum_valve pump_high_power $power&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Erläuterungen:&lt;br /&gt;
&lt;br /&gt;
* Es sind alle vier Möglichkeiten der zwei Pumpenlevel abgebildet.&lt;br /&gt;
* Wenn die Automatik deaktiviert ist und die Level 1-4 von extern geschaltet werden, zählen sie dadurch korrekterweise nicht zu diesen beiden Consumern.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Notifys für das hardwaremäßige Schalten der Consumer&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Es werden einige Notifys verwendet, die die Umsetzung vom Dummy-Device auf die Hardware erledigen, da ein logisches Ein teilweise hardwaremäßig einige Dinge erfordert.&lt;br /&gt;
&lt;br /&gt;
Notify für Pump Low:&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
dum_valve:pump_low:.* {&lt;br /&gt;
	if($EVTPART1 eq &amp;quot;on&amp;quot;){&lt;br /&gt;
		fhem(&amp;quot;set Pool_Pin26 on; sleep 60; set Pool_Pin25 on; sleep 1; set Pool_Pin26 off&amp;quot;);&lt;br /&gt;
	}elsif($EVTPART1 eq &amp;quot;off&amp;quot;){&lt;br /&gt;
		fhem(&amp;quot;set Pool_Pin14 off; sleep 5; set Pool_Pin12 off; sleep 15; set Pool_Pin25 off&amp;quot;);&lt;br /&gt;
	}else{&lt;br /&gt;
	Log3 (&amp;quot;Pool&amp;quot;, 4, qq{Notify Level1 - parameter is not on or off});&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Erläuterungen:&lt;br /&gt;
&lt;br /&gt;
* Beim Einschalten ist es nötig für eine Minute Pump High zu aktivieren, um Luft aus der Elektrolysezelle zu spülen, bevor Pump Low aktiv sein darf. (Pin26 und Pin25 sind die Steuerpins auf der Interfacekarte der Pumpe für die beiden Level.)&lt;br /&gt;
* Beim Ausschalten werden zuerst die Salzanlage (Pin14) und die pH-Anlage (Pin12) deaktiviert, damit noch 15 Sekunden lang die Elektrolysezelle mit frischem Wasser gespült wird, um das verbleibende Chlor auszuspülen.&amp;lt;br /&amp;gt;&lt;br /&gt;
Notify für Pump High:&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
dum_valve:pump_high:.* {&lt;br /&gt;
	if($EVTPART1 eq &amp;quot;on&amp;quot;){&lt;br /&gt;
		fhem(&amp;quot;set Pool_Pin26 on; sleep 1; set Pool_Pin25 off&amp;quot;);&lt;br /&gt;
	}elsif($EVTPART1 eq &amp;quot;off&amp;quot;){&lt;br /&gt;
		fhem(&amp;quot;set Pool_Pin25 on; sleep 1; set Pool_Pin26 off&amp;quot;);&lt;br /&gt;
	}else{&lt;br /&gt;
	Log3 (&amp;quot;Pool&amp;quot;, 4, qq{Notify Level 2- parameter is not on or off});&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Erläuterung:&lt;br /&gt;
&lt;br /&gt;
* Da die Interfacekarte immer auf den höchsten Level reagiert, wird beim Umschalten zuerst Pump High aktiviert und nach einer Sekunde Pump Low deaktiviert, um ein &amp;quot;stufenloses&amp;quot; (ohne Ausschalten) Umschalten zu ermöglichen. Beim Ausschalten genauso.&lt;br /&gt;
&lt;br /&gt;
Notify für die Wärmepumpe:&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
dum_valve:heatpump:.* {&lt;br /&gt;
	if($EVTPART1 eq &amp;quot;on&amp;quot;){&lt;br /&gt;
		fhem(&amp;quot;set Pool_Pin6 on&amp;quot;);&lt;br /&gt;
	}elsif($EVTPART1 eq &amp;quot;off&amp;quot;){&lt;br /&gt;
		fhem(&amp;quot;set Pool_Pin6 off&amp;quot;);&lt;br /&gt;
	}else{&lt;br /&gt;
	Log3 (&amp;quot;Pool&amp;quot;, 4, qq{Notify heatpump - parameter is not on or off});&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Erläuterung:&lt;br /&gt;
&lt;br /&gt;
* Hier reicht ein dirketes Übernehmen aus dem Dummy.&lt;br /&gt;
&lt;br /&gt;
Notify für den Nachbar-Pool:&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
dum_valve:pool_kurz:.* {&lt;br /&gt;
	if($EVTPART1 eq &amp;quot;on&amp;quot; &amp;amp;&amp;amp; ReadingsVal(&amp;quot;Shelly_Plug_S_1&amp;quot;,&amp;quot;state&amp;quot;,&amp;quot;on&amp;quot;) eq &amp;quot;off&amp;quot;){&lt;br /&gt;
		fhem(&amp;quot;set PwSw_Schalter off; sleep 3;{if(ReadingsAge(\&amp;quot;PwSw_Schalter\&amp;quot;,\&amp;quot;state\&amp;quot;,0)&amp;lt;4 &amp;amp;&amp;amp; ReadingsVal(\&amp;quot;PwSw_Schalter\&amp;quot;,\&amp;quot;state\&amp;quot;,\&amp;quot;on\&amp;quot;) eq \&amp;quot;off\&amp;quot;){fhem(\&amp;quot;set Shelly_Plug_S_1 on\&amp;quot;)}}; sleep 5; set PwSw_Schalter on&amp;quot;);&lt;br /&gt;
	}elsif($EVTPART1 eq &amp;quot;off&amp;quot; &amp;amp;&amp;amp; ReadingsVal(&amp;quot;Shelly_Plug_S_1&amp;quot;,&amp;quot;state&amp;quot;,&amp;quot;off&amp;quot;) eq &amp;quot;on&amp;quot;){&lt;br /&gt;
		fhem(&amp;quot;set PwSw_Schalter off; sleep 3;{if(ReadingsAge(\&amp;quot;PwSw_Schalter\&amp;quot;,\&amp;quot;state\&amp;quot;,0)&amp;lt;4 &amp;amp;&amp;amp; ReadingsVal(\&amp;quot;PwSw_Schalter\&amp;quot;,\&amp;quot;state\&amp;quot;,\&amp;quot;on\&amp;quot;) eq \&amp;quot;off\&amp;quot;){fhem(\&amp;quot;set Shelly_Plug_S_1 off\&amp;quot;)}}; sleep 5; set PwSw_Schalter on&amp;quot;);&lt;br /&gt;
	}else{&lt;br /&gt;
	Log3 (&amp;quot;Pool&amp;quot;, 4, qq{Notify Pool Kurz - parameter is not on or off});&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Erläuterung&lt;br /&gt;
&lt;br /&gt;
* Da hier eine Pumpe durch einen 3-poligen Umschalter zwischen zwei Hausanspeisungen umgeschaltet wird, ist es wichtig, dass die Pumpe dazwischen &#039;&#039;&#039;gesichert&#039;&#039;&#039; zum Stehen gebracht wird! Sonst führt die rotierende Pumpe als Generator zu einem gewaltigen Kurzschluss mit der &amp;quot;fremden&amp;quot; Phase. War nicht schön ...&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Sonderprogramme für den Poolbetrieb&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Hier ist das Aus- und Einschalten der Solarforcast-Automatik zu sehen. Es werden alle Stati gespeichert. Danach kann von beliebigen &amp;quot;Sonderfunktionen&amp;quot; (Reinigung des Filters, Massagefunktion, usw.) auf die Hardware zugegriffen werden. Wenn wieder in den Automatikmodus zurückgewechselt wird, wird hardwaremäßig wieder der Zustand der Automatik von vorher hergestellt, die Stati wieder zurück geschrieben und die Automatik wieder aktiviert.&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
##############################################&lt;br /&gt;
# Status speichern und Automatik ausschalten #&lt;br /&gt;
##############################################&lt;br /&gt;
sub PoolAutoOff{&lt;br /&gt;
	my $pump_low_id			= sprintf &amp;quot;%02d&amp;quot;, 1;&lt;br /&gt;
	my $pump_high_id		= sprintf &amp;quot;%02d&amp;quot;, 3;&lt;br /&gt;
	my $heatpump_id			= sprintf &amp;quot;%02d&amp;quot;, 5;&lt;br /&gt;
	my $pool_kurz_id		= sprintf &amp;quot;%02d&amp;quot;, 7;&lt;br /&gt;
	my $sf				= &#039;energy_mgmt&#039;;&lt;br /&gt;
&lt;br /&gt;
		&lt;br /&gt;
#	my $pump_low_state = ReadingsVal($valve_dummy,&#039;pump_low&#039;,&#039;on&#039;);&lt;br /&gt;
	my $pump_low_auto	= FHEM::SolarForecast::ConsumerVal ($sf, $pump_low_id, &#039;auto&#039;, &#039;&#039;);	# Automatikmodus der Pumpe&lt;br /&gt;
#	my $pump_high_state = ReadingsVal($valve_dummy,&#039;pump_high&#039;,&#039;on&#039;);&lt;br /&gt;
	my $pump_high_auto	= FHEM::SolarForecast::ConsumerVal ($sf, $pump_high_id, &#039;auto&#039;, &#039;&#039;);	# Automatikmodus der Pumpe&lt;br /&gt;
#	my $heatpump_state = ReadingsVal($valve_dummy,&#039;heatpump&#039;,&#039;on&#039;);&lt;br /&gt;
	my $heatpump_auto	= FHEM::SolarForecast::ConsumerVal ($sf, $heatpump_id, &#039;auto&#039;, &#039;&#039;);	# Automatikmodus der Wärmepumpe&lt;br /&gt;
#	my $pool_kurz_state = ReadingsVal($valve_dummy,&#039;pool_kurz&#039;,&#039;on&#039;);&lt;br /&gt;
	my $pool_kurz_auto	= FHEM::SolarForecast::ConsumerVal ($sf, $pool_kurz_id, &#039;auto&#039;, &#039;&#039;);	# Automatikmodus des Pools&lt;br /&gt;
	fhem(&amp;quot;set $valve_dummy pump_low_auto 0&amp;quot;);&lt;br /&gt;
	fhem(&amp;quot;set $valve_dummy pump_high_auto 0&amp;quot;);&lt;br /&gt;
	fhem(&amp;quot;set $valve_dummy heatpump_auto 0&amp;quot;);&lt;br /&gt;
	fhem(&amp;quot;set $valve_dummy pool_kurz_auto 0&amp;quot;);&lt;br /&gt;
	fhem(&amp;quot;setreading $valve_dummy sf_automatic 0&amp;quot;); &lt;br /&gt;
	fhem(&amp;quot;setreading $valve_dummy last_auto_pump_low $pump_low_auto&amp;quot;);&lt;br /&gt;
	fhem(&amp;quot;setreading $valve_dummy last_auto_pump_high $pump_high_auto&amp;quot;);&lt;br /&gt;
	fhem(&amp;quot;setreading $valve_dummy last_auto_heatpump $heatpump_auto&amp;quot;);&lt;br /&gt;
	fhem(&amp;quot;setreading $valve_dummy last_auto_pool_kurz $pool_kurz_auto&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
	my $last_pump_level_1 = ReadingsVal($valve_dummy,&amp;quot;pump_low&amp;quot;,&amp;quot;off&amp;quot;);&lt;br /&gt;
	my $last_pump_level_2 = ReadingsVal($valve_dummy,&amp;quot;pump_high&amp;quot;,&amp;quot;off&amp;quot;);&lt;br /&gt;
	my $last_pump_level_3 = ReadingsVal($pump_level_3,&amp;quot;value&amp;quot;,&amp;quot;off&amp;quot;);&lt;br /&gt;
	my $last_pump_level_4 = ReadingsVal($pump_level_4,&amp;quot;value&amp;quot;,&amp;quot;off&amp;quot;);&lt;br /&gt;
	my $last_heatpump = ReadingsVal($valve_dummy,&amp;quot;heatpump&amp;quot;,&amp;quot;off&amp;quot;);&lt;br /&gt;
	fhem(&amp;quot;setreading $valve_dummy last_pump_level_1 $last_pump_level_1&amp;quot;); &lt;br /&gt;
	fhem(&amp;quot;setreading $valve_dummy last_pump_level_2 $last_pump_level_2&amp;quot;); &lt;br /&gt;
	fhem(&amp;quot;setreading $valve_dummy last_pump_level_3 $last_pump_level_3&amp;quot;); &lt;br /&gt;
	fhem(&amp;quot;setreading $valve_dummy last_pump_level_4 $last_pump_level_4&amp;quot;); &lt;br /&gt;
	fhem(&amp;quot;setreading $valve_dummy last_heatpump $last_heatpump&amp;quot;); &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
##########################################&lt;br /&gt;
# Status laden und Automatik einschalten #&lt;br /&gt;
##########################################&lt;br /&gt;
sub PoolAutoOn{&lt;br /&gt;
	my $last_pump_level_4 = ReadingsVal($valve_dummy,&amp;quot;last_pump_level_4&amp;quot;,&amp;quot;off&amp;quot;);&lt;br /&gt;
	my $last_pump_level_3 = ReadingsVal($valve_dummy,&amp;quot;last_pump_level_3&amp;quot;,&amp;quot;off&amp;quot;);&lt;br /&gt;
	my $last_pump_level_2 = ReadingsVal($valve_dummy,&amp;quot;last_pump_level_2&amp;quot;,&amp;quot;off&amp;quot;);&lt;br /&gt;
	my $last_pump_level_1 = ReadingsVal($valve_dummy,&amp;quot;last_pump_level_1&amp;quot;,&amp;quot;off&amp;quot;);&lt;br /&gt;
	my $last_heatpump = ReadingsVal($valve_dummy,&amp;quot;last_heatpump&amp;quot;,&amp;quot;off&amp;quot;);&lt;br /&gt;
	fhem(&amp;quot;set $pump_level_4 $last_pump_level_4&amp;quot;); &lt;br /&gt;
	fhem(&amp;quot;set $pump_level_3 $last_pump_level_3&amp;quot;);&lt;br /&gt;
	fhem(&amp;quot;set $pump_level_2 $last_pump_level_2&amp;quot;);&lt;br /&gt;
	if($last_pump_level_1 eq &amp;quot;on&amp;quot; &amp;amp;&amp;amp; $last_pump_level_2 eq &amp;quot;off&amp;quot; &amp;amp;&amp;amp; $last_pump_level_3 eq &amp;quot;off&amp;quot; &amp;amp;&amp;amp; $last_pump_level_4 eq &amp;quot;off&amp;quot;){&lt;br /&gt;
		fhem(&amp;quot;set $pump_level_2 on; sleep 1; set $pump_level_1 $last_pump_level_1; sleep 28; set $pump_level_2 off; set $valve_dummy sf_automatic 1&amp;quot;);&lt;br /&gt;
	}elsif($last_pump_level_1 eq &amp;quot;on&amp;quot; &amp;amp;&amp;amp; $last_pump_level_2 eq &amp;quot;on&amp;quot; &amp;amp;&amp;amp; $last_pump_level_3 eq &amp;quot;off&amp;quot; &amp;amp;&amp;amp; $last_pump_level_4 eq &amp;quot;off&amp;quot;){&lt;br /&gt;
		fhem(&amp;quot;set $pump_level_1 off; set $valve_dummy sf_automatic 1&amp;quot;); &lt;br /&gt;
	}elsif($last_pump_level_1 eq &amp;quot;off&amp;quot; &amp;amp;&amp;amp; $last_pump_level_2 eq &amp;quot;off&amp;quot; &amp;amp;&amp;amp; $last_pump_level_3 eq &amp;quot;off&amp;quot; &amp;amp;&amp;amp; $last_pump_level_4 eq &amp;quot;off&amp;quot;){&lt;br /&gt;
		fhem(&amp;quot;set $pump_level_1 off; set $valve_dummy sf_automatic 1&amp;quot;); &lt;br /&gt;
	}&lt;br /&gt;
	fhem(&amp;quot;sleep 10; set $heatpump $last_heatpump&amp;quot;); &lt;br /&gt;
	fhem(&amp;quot;setreading $valve_dummy last_pump_level_1 none&amp;quot;); &lt;br /&gt;
	fhem(&amp;quot;setreading $valve_dummy last_pump_level_2 none&amp;quot;); &lt;br /&gt;
	fhem(&amp;quot;setreading $valve_dummy last_pump_level_3 none&amp;quot;); &lt;br /&gt;
	fhem(&amp;quot;setreading $valve_dummy last_pump_level_4 none&amp;quot;); &lt;br /&gt;
	fhem(&amp;quot;setreading $valve_dummy last_heatpump none&amp;quot;); &lt;br /&gt;
	fhem(&amp;quot;setreading $valve_dummy valve_position normal&amp;quot;);&lt;br /&gt;
	my $last_auto_pump_low = ReadingsVal($valve_dummy,&amp;quot;last_auto_pump_low&amp;quot;,0);&lt;br /&gt;
	my $last_auto_pump_high = ReadingsVal($valve_dummy,&amp;quot;last_auto_pump_high&amp;quot;,0);&lt;br /&gt;
	my $last_auto_heatpump = ReadingsVal($valve_dummy,&amp;quot;last_auto_heatpump&amp;quot;,0);&lt;br /&gt;
	my $last_auto_pool_kurz = ReadingsVal($valve_dummy,&amp;quot;last_auto_pool_kurz&amp;quot;,0);&lt;br /&gt;
	fhem(&amp;quot;set $valve_dummy pump_low_auto $last_auto_pump_low&amp;quot;);&lt;br /&gt;
	fhem(&amp;quot;set $valve_dummy pump_high_auto $last_auto_pump_high&amp;quot;);&lt;br /&gt;
	fhem(&amp;quot;set $valve_dummy heatpump_auto $last_auto_heatpump&amp;quot;);&lt;br /&gt;
	fhem(&amp;quot;set $valve_dummy pool_kurz_auto $last_auto_pool_kurz&amp;quot;);&lt;br /&gt;
	fhem(&amp;quot;setreading $valve_dummy last_auto_pump_low none&amp;quot;);&lt;br /&gt;
	fhem(&amp;quot;setreading $valve_dummy last_auto_pump_high none&amp;quot;);&lt;br /&gt;
	fhem(&amp;quot;setreading $valve_dummy last_auto_heatpump none&amp;quot;);&lt;br /&gt;
	fhem(&amp;quot;setreading $valve_dummy last_auto_pool_kurz none&amp;quot;);&lt;br /&gt;
}		&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Erläuterungen:&lt;br /&gt;
&lt;br /&gt;
* pump_level_1 ist Pump Low, pump_level_2 ist Pump High, pump_level_3 und pump_level_4 sind Sonderfunktionen (Filterreinigung, Massage)&lt;br /&gt;
* Beim Wiedereinschalten von Pump Low ist es wieder nötig kurz Pump High zu aktivieren, um Luft auszuspülen.&lt;br /&gt;
* Es wird der Zustand von Solarforecast gespeichert und danach auf die Hardware geschrieben. -&amp;gt; Stellt sicher, dass nach dem Einschalten der Automatik der logical switchstate zum physical switchstate passt.&lt;br /&gt;
&lt;br /&gt;
== Tips &amp;amp; Tricks ==&lt;br /&gt;
* Interruptable verhält sich völlig anders, wenn der Perlcode 1 oder 0 liefert, als wenn man 1 oder 0 in der Config hinterlegt. -&amp;gt; siehe commandref&lt;br /&gt;
* Rückgabewert bei swoncond und swoffcond wird mit dem Readingswert verglichen und nicht dirket verwendet. Bsp. wenn das Reading 100 enthält muss auch der Rückgabewert des Perlcodes 100 liefern, um true zu ergeben.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== weiterführende Links ==&lt;br /&gt;
* Forenthema &amp;quot;{{Link2Forum|Topic=137058|LinkText=76_SolarForecast - Informationen/Ideen zu Weiterentwicklung und Support}}&amp;quot;&lt;br /&gt;
* Solcast API Toolkit: https://toolkit.solcast.com.au&lt;br /&gt;
* Kostal Plenticore 10 Plus mit SQL-Datenbank Integration ([[Kostal Plenticore 10 Plus]])&lt;br /&gt;
* Photovoltaik Ingenieurbüro Junge: https://www.ing-büro-junge.de/html/photovoltaik.html&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Energieerzeugungsmessung]]&lt;/div&gt;</summary>
		<author><name>Ph1959de</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=Diskussion:Solaranlage_Komplettbeispiel_Fronius_BYD&amp;diff=40481</id>
		<title>Diskussion:Solaranlage Komplettbeispiel Fronius BYD</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=Diskussion:Solaranlage_Komplettbeispiel_Fronius_BYD&amp;diff=40481"/>
		<updated>2025-11-05T10:34:53Z</updated>

		<summary type="html">&lt;p&gt;Ph1959de: Antwort auf &amp;quot;Titel ok?&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;So Leute, die Grundstruktur steht erst mal.&lt;br /&gt;
Soll der Titel so bleiben, oder geändert werden?&lt;br /&gt;
:Der Titel kann so bleiben - was Du noch machen solltest: an geeigneten Stellen auf diese Beispiel verweisen (kannst Dich vielleicht an [[Spezial:Linkliste/SolarForecast_-_Solare_Prognose_(PV_Erzeugung)_und_Verbrauchersteuerung]] orientieren), damit der Beitrag nicht nur &amp;quot;zufällig&amp;quot; oder über einen Verweis aus den Forum gefunden wird. --[[Benutzer:Ph1959de|Peter]] ([[Benutzer Diskussion:Ph1959de|Diskussion]]) 11:34, 5. Nov. 2025 (CET)&lt;br /&gt;
&lt;br /&gt;
== Abhängigkeit von Autolade_Calculator ==&lt;br /&gt;
Wenn man den Code im Abschnitt &amp;quot;Fronius_Symo in FHEM&amp;quot; 1:1 übernimmt, führt eine Abhängigkeit von &amp;quot;Autolade_Calculator&amp;quot; in userReadings zu Einträgen im logfile, wenn kein device &amp;quot;Autolade_Calculator&amp;quot; definiert ist:&lt;br /&gt;
&lt;br /&gt;
&amp;quot;User_Consumed_EN:PowerFlow_Site_P_PV.* {ReadingsVal($name,&amp;quot;PowerFlow_Site_P_PV&amp;quot;,&amp;quot;&amp;quot;)-ReadingsVal(&amp;quot;Autolade_Calculator&amp;quot;,&amp;quot;Auto_Ladung_av&amp;quot;,&amp;quot;&amp;quot;)+ReadingsVal($name,&amp;quot;PowerFlow_Site_P_Grid&amp;quot;,&amp;quot;&amp;quot;)+ReadingsVal($name,&amp;quot;PowerFlow_Site_P_Akku&amp;quot;,&amp;quot;&amp;quot;)},&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Ich schlage vor, die Zeile zu entfernen (oder zu erläutern, welches device da verlangt wird).&lt;br /&gt;
&lt;br /&gt;
(Anmerkung: Bei mir irritierte zusätzlich, dass die log-Einträge selbst mit verbose=0 auftraten)&lt;/div&gt;</summary>
		<author><name>Ph1959de</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=Stromb%C3%B6rse&amp;diff=40458</id>
		<title>Strombörse</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=Stromb%C3%B6rse&amp;diff=40458"/>
		<updated>2025-10-25T16:11:56Z</updated>

		<summary type="html">&lt;p&gt;Ph1959de: Tippfehler korrigiert&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Bild:Screenshot 2024-01-23 122115.png|mini|400px|rechts|EVU_Tibber_connect]]&lt;br /&gt;
Diese Seite beinhaltet Informationen für die Anbindung von Energieversorgungsunternehmen (EVU), die ihre Preise nach der Strombörse z.B. im Stundenrhythmus anbieten.&lt;br /&gt;
&lt;br /&gt;
Als Beispiel wären da:&lt;br /&gt;
# {{Link2Forum|Topic=130407|LinkText=Tibber - im Forum}}&lt;br /&gt;
# {{Link2Forum|Topic=135906|LinkText=aWATTar - im Forum}}&lt;br /&gt;
# {{Link2Forum|Topic=135906|LinkText=EPEX_Spot}}&lt;br /&gt;
&lt;br /&gt;
Diese Wiki Seite ist noch in Bearbeitung!!!&lt;br /&gt;
&lt;br /&gt;
== 1. Tibber mit Tibber Pulse ==&lt;br /&gt;
Eine momentan verbreitete Methode zur Anbindung an Tibber ist ein Lesekopf auf dem EVU Zähler, der als &amp;quot;Tibber Pulse&amp;quot; vertrieben wird. Damit wird ein bestehender digitaler EVU Zähler ausgelesen und die Werte per LAN zu Tibber gesendet. Über eine API bei Tibber können dann die Verbrauchswerte und Kosten sowie ein Live Stream (WebSocket) abgefragt werden.&lt;br /&gt;
&lt;br /&gt;
Die im Folgenden beschriebenen Beispieldevices bieten die oben genannte Abfrage der API mit WebSocket und [[HTTPMOD]]. Das EVU_Tibber Device dient der Darstellung und Steuerung.&lt;br /&gt;
&lt;br /&gt;
=== Vorausetzungen ===&lt;br /&gt;
* FHEM mit einer installierten MySQL DbLog (FileLog wird nicht unterstützt)&lt;br /&gt;
* KeyValue() (wird hier beschrieben)&lt;br /&gt;
* homeID und personal Token/Access Token/Connect Token&lt;br /&gt;
* Tibber Pulse Lesekopf auf dem digitalen EVU Zähler&lt;br /&gt;
* Man sollte bereits Tibber Kunde sein, da ansonsten nur Demo Daten abgerufen werden können&lt;br /&gt;
&lt;br /&gt;
=== Token ===&lt;br /&gt;
Der Token wird leider unterschiedlich bezeichnet, aber egal ob er personal Token, Access Token oder Connect Token heisst, es bezieht sich auf den gleichen. &lt;br /&gt;
&lt;br /&gt;
Erhältlich ist dieser aktuell (Oktober 24) unter https://developer.tibber.com/ &lt;br /&gt;
&lt;br /&gt;
Dort bitte mit deinen Zugangsdaten einloggen (&amp;quot;Sign In&amp;quot;, oben rechts) und man bekommt den Access Token direkt angezeigt.&lt;br /&gt;
&lt;br /&gt;
=== homeID ===&lt;br /&gt;
Um eine HomeID zu erhalten, muss zuerst der Pulse mit dem Internet bzw. tibber verbunden werden. Wenn die App glücklich ist, gehts hier weiter: &lt;br /&gt;
&lt;br /&gt;
Man loggt sich wieder ein unter https://developer.tibber.com/. Dort geht man dann zum API Explorer. &lt;br /&gt;
&lt;br /&gt;
Leider ist keine der Demo-Abfragen geeignet, die homeID zu erfahren. Dafür gibt man ins linke fenster folgenden Code ein: &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
  viewer {&lt;br /&gt;
    homes {&lt;br /&gt;
      id&lt;br /&gt;
      address {&lt;br /&gt;
        address1&lt;br /&gt;
        postalCode&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Dann startet man mit dem Play-Pfeil und bekommt auf der rechten Seite etwa folgende Ausgabe: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
  &amp;quot;data&amp;quot;: {&lt;br /&gt;
    &amp;quot;viewer&amp;quot;: {&lt;br /&gt;
      &amp;quot;homes&amp;quot;: [&lt;br /&gt;
        {&lt;br /&gt;
          &amp;quot;id&amp;quot;: &amp;quot;1234abcd-12ab-34cd-56ef-1234abcd12ab&lt;br /&gt;
          &amp;quot;address&amp;quot;: {&lt;br /&gt;
            &amp;quot;address1&amp;quot;: &amp;quot;Musterstr.1&amp;quot;,&lt;br /&gt;
            &amp;quot;postalCode&amp;quot;: &amp;quot;12345&amp;quot;&lt;br /&gt;
          }&lt;br /&gt;
        }&lt;br /&gt;
      ]&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Diese ID ist die homeID und kann unten verwendet werden.&lt;br /&gt;
&lt;br /&gt;
=== EVU_Tibber_connect ===&lt;br /&gt;
Das EVU_Tibber_connect fragt mit HTTPMOD Statistiken auf der Tibber API Webseite ab. Zusätzlich wurde in den userReadings die WebSocket Verbindung für die Live Daten integriert.&lt;br /&gt;
* Die WebSocket Verbindung wurde leicht angepasst &#039;&#039;&#039;[[Websocket|von hier]]&#039;&#039;&#039; übernommen&lt;br /&gt;
* Im Forum wurde dies &#039;&#039;&#039;{{Link2Forum|Topic=130407|Message=1298863|LinkText=ab hier}}&#039;&#039;&#039; bearbeitet&lt;br /&gt;
&lt;br /&gt;
==== Verwendete Module ====&lt;br /&gt;
* HTTPMOD&lt;br /&gt;
* DbLog&lt;br /&gt;
* DbRep&lt;br /&gt;
* DOIF&lt;br /&gt;
* KeyValue()&lt;br /&gt;
&lt;br /&gt;
==== Ablage der homeID und des Tokens ====&lt;br /&gt;
Eine Verbindung zur Tibber API mit den &#039;&#039;&#039;tatsächlichen Preisen&#039;&#039;&#039; bekommen nur Kunden, ansonsten kann man eine Demo mit Kosten in Schwedischen Kronen testweise abfragen.&lt;br /&gt;
&lt;br /&gt;
In diesem Beispiel Device kann man seine homeID und sein Token entweder als Attribut ablegen oder man macht einen Eintrag im KeyStore von FHEM, wodurch man nicht so leicht versehentlich seine Daten im Forum postet.&lt;br /&gt;
&lt;br /&gt;
Es dient lediglich der Ablage von Keys, damit sie nicht direkt offen im Datensystem oder im Konfigurationsfile zu finden sind.&lt;br /&gt;
Die Funktion befindet sich in der 99_myUtils.pm .&lt;br /&gt;
&lt;br /&gt;
===== Beispiel KeyValue() =====&lt;br /&gt;
&#039;&#039;&#039;Achtung, der KeyValue ist kein Schutz für Passwort oder Keys!&#039;&#039;&#039;&lt;br /&gt;
Es dient lediglich der Ablage von Keys, damit sie nicht direkt offen im FHEM Device oder im Konfigurationsfile zu finden sind.&lt;br /&gt;
Die Funktion befindet sich in der 99_myUtils .&lt;br /&gt;
&lt;br /&gt;
====== KeyValue() Funktion in der ~/FHEM/99_myUtils.pm ======&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
sub KeyValue {&lt;br /&gt;
    my ($step, $index, $value) = @_;&lt;br /&gt;
    my $key = getUniqueId().$index;&lt;br /&gt;
    my $e_value = &amp;quot;&amp;quot;;&lt;br /&gt;
    my $error;&lt;br /&gt;
&lt;br /&gt;
    if (eval &amp;quot;use Digest::MD5;1&amp;quot;) {&lt;br /&gt;
      $key    = Digest::MD5::md5_hex(unpack &amp;quot;H*&amp;quot;, $key);&lt;br /&gt;
      $key   .= Digest::MD5::md5_hex($key);&lt;br /&gt;
    }&lt;br /&gt;
   &lt;br /&gt;
    if ($step eq &amp;quot;read&amp;quot;) {&lt;br /&gt;
      ($error, $value) = getKeyValue($index);&lt;br /&gt;
&lt;br /&gt;
      if ( defined($error) ) {&lt;br /&gt;
        Log3 $index,3, &amp;quot;$index, can&#039;t read key from FhemUtils/uniqueID: $error&amp;quot;;&lt;br /&gt;
        return undef;&lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
      if ( defined($value) ) {&lt;br /&gt;
        my $dec_value = &#039;&#039;;&lt;br /&gt;
&lt;br /&gt;
        for my $char (map { pack(&#039;C&#039;, hex($_)) } ($value =~ /(..)/g)) {&lt;br /&gt;
          my $decode  = chop($key);&lt;br /&gt;
          $dec_value .= chr(ord($char)^ord($decode));&lt;br /&gt;
          $key        = $decode.$key;&lt;br /&gt;
        }&lt;br /&gt;
        return $dec_value;&lt;br /&gt;
      }&lt;br /&gt;
      else {&lt;br /&gt;
        Log3 $index,3,&amp;quot;$index, no key found in FhemUtils/uniqueID&amp;quot;;&lt;br /&gt;
        return undef;&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if ($step eq &amp;quot;store&amp;quot;) {&lt;br /&gt;
      for my $char (split //, $value) {&lt;br /&gt;
        my $encode = chop($key);&lt;br /&gt;
        $e_value  .= sprintf(&amp;quot;%.2x&amp;quot;,ord($char)^ord($encode));&lt;br /&gt;
        $key       = $encode.$key;&lt;br /&gt;
      }&lt;br /&gt;
      $error = setKeyValue($index, $e_value);&lt;br /&gt;
      return &amp;quot;error while saving key : $error&amp;quot; if(defined($error));&lt;br /&gt;
      return &amp;quot;Key successfully saved in FhemUtils/uniqueID Key $index&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====== Ablegen der homeID und des Token ======&lt;br /&gt;
In der FHEM Kommandozeile legt man seinen Daten wie folgt im KeyValue ab. Hier wären als Beispiel der Demo Zugang von Tibber.&lt;br /&gt;
Das &amp;quot;read&amp;quot; ist dann auch direkt der erste Test, bevor man weiter macht.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
{KeyValue(&amp;quot;store&amp;quot;,&amp;quot;EVU_Tibber_connect_token&amp;quot;,&amp;quot;5K4MVS-OjfWhK_4yrjOlFe1F6kJXPVf7eQYggo8ebAE&amp;quot;)}&lt;br /&gt;
{KeyValue(&amp;quot;store&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;,&amp;quot;96a14971-525a-4420-aae9-e5aedaa129ff&amp;quot;)}&lt;br /&gt;
&lt;br /&gt;
{KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_token&amp;quot;)}&lt;br /&gt;
{KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;)}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Beispiel Attribut für homeID und Token =====&lt;br /&gt;
Oder, sobald das EVU_Tibber_connect Device erstellt wurde, als Attribut.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
attr EVU_Tibber_connect ws_homeId 96a14971-525a-4420-aae9-e5aedaa129ff&lt;br /&gt;
attr EVU_Tibber_connect ws_myId 96a14971-525a-4420-aae9-e5aedaa129ff          &amp;lt;&amp;lt;&amp;lt;&amp;lt; Dies kann wohl eine frei gewählte ID sein, was hier der Einfachheithalber gleich der homeId gesetzt wurde.&lt;br /&gt;
attr EVU_Tibber_connect ws_token 5K4MVS-OjfWhK_4yrjOlFe1F6kJXPVf7eQYggo8ebAE&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== EVU_Tibber_connect RAW Device====&lt;br /&gt;
Das Device EVU_Tibber_connect dient der Verbindung zur Tibber API. Zusätzlich wird für die Abfrage der Live Daten innerhalb der userReadings eine WebSocket Verbindung zu Tibber aufgebaut. Tibber stellt jeweils gegen 14:00 Uhr die Daten für den nächsten Tag bereit, wodurch von 00:00 - 14:00 Uhr diese Information im EVU_Tibber_connect noch nicht angezeigt werden kann.&lt;br /&gt;
Das Device arbeitet über die userReadings mit einem MySQL DbRep Device, was somit eine MySQL DbLog Verwendung im FHEM voraussetzt. &#039;&#039;&#039;Die Verwendung von FileLog ist nicht unterstützt!&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod EVU_Tibber_connect HTTPMOD https://api.tibber.com/v1-beta/gql 0&lt;br /&gt;
attr EVU_Tibber_connect userattr ws_homeId ws_minInterval ws_myId ws_token ws_websocketURL&lt;br /&gt;
attr EVU_Tibber_connect DbLogExclude .*&lt;br /&gt;
attr EVU_Tibber_connect DbLogInclude total_cost_.*,fc0_trigger.*&lt;br /&gt;
attr EVU_Tibber_connect comment Version 2024.01.22 14:00 \&lt;br /&gt;
https://developer.tibber.com/explorer\&lt;br /&gt;
\&lt;br /&gt;
In der FHEM Kommandozeile könnt Ihr das token und die homeID im KeyStore ablegen.\&lt;br /&gt;
Bitte lest dazu das Thema KeyStore im Wiki und legt die Funktion in Eure 99_myUtils.\&lt;br /&gt;
\&lt;br /&gt;
{KeyValue(&amp;quot;store&amp;quot;,&amp;quot;EVU_Tibber_connect_token&amp;quot;,&amp;quot;5K4MVS-OjfWhK_4yrjOlFe1F6kJXPVf7eQYggo8ebAE&amp;quot;)}\&lt;br /&gt;
{KeyValue(&amp;quot;store&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;,&amp;quot;96a14971-525a-4420-aae9-e5aedaa129ff&amp;quot;)}\&lt;br /&gt;
\&lt;br /&gt;
Hierduch kann man einen wirtschaftlicheren trigger_price berechnen lassen:\&lt;br /&gt;
setreading EVU_Tibber compensation_grid &amp;lt;Eure Einspeisevergütung&amp;gt;\&lt;br /&gt;
\&lt;br /&gt;
# Berechnung eines Default Schwellwertes als täglicher Niedrigpreis\&lt;br /&gt;
    (fc_avg - fc_min)  /2 + fc_min\&lt;br /&gt;
\&lt;br /&gt;
# Abschätzung von Wirtschaftlichkeit beim Speicher Laden, falls Tibber zu teuer wird\&lt;br /&gt;
# compensation_grid kann auch auf einen für Euch passenden Wert gesetzt werden\&lt;br /&gt;
    (fc_avg - compensation_grid) *0.85&lt;br /&gt;
attr EVU_Tibber_connect disable 0&lt;br /&gt;
attr EVU_Tibber_connect enableControlSet 1&lt;br /&gt;
attr EVU_Tibber_connect get01-1Name current_currency&lt;br /&gt;
attr EVU_Tibber_connect get01-2Name current_level&lt;br /&gt;
attr EVU_Tibber_connect get01-3Name current_date&lt;br /&gt;
attr EVU_Tibber_connect get01-3OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get01-4Name current_price&lt;br /&gt;
attr EVU_Tibber_connect get01-4OExpr $val *100&lt;br /&gt;
attr EVU_Tibber_connect get01Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {currentSubscription {priceInfo {current {total startsAt currency level}}}}}}&amp;quot; }&lt;br /&gt;
attr EVU_Tibber_connect get01Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get01Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get01JSON data_viewer_home_currentSubscription_priceInfo_current&lt;br /&gt;
attr EVU_Tibber_connect get01Name 01_priceInfo&lt;br /&gt;
attr EVU_Tibber_connect get01URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get02-10Name fc0_03_total&lt;br /&gt;
attr EVU_Tibber_connect get02-11Name fc0_04_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-11OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-12Name fc0_04_total&lt;br /&gt;
attr EVU_Tibber_connect get02-13Name fc0_05_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-13OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-14Name fc0_05_total&lt;br /&gt;
attr EVU_Tibber_connect get02-15Name fc0_06_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-15OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-16Name fc0_06_total&lt;br /&gt;
attr EVU_Tibber_connect get02-17Name fc0_07_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-17OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-18Name fc0_07_total&lt;br /&gt;
attr EVU_Tibber_connect get02-19Name fc0_08_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-19OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-1Name current_date&lt;br /&gt;
attr EVU_Tibber_connect get02-1OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-20Name fc0_08_total&lt;br /&gt;
attr EVU_Tibber_connect get02-21Name fc0_09_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-21OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-22Name fc0_09_total&lt;br /&gt;
attr EVU_Tibber_connect get02-23Name fc0_10_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-23OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-24Name fc0_10_total&lt;br /&gt;
attr EVU_Tibber_connect get02-25Name fc0_11_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-25OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-26Name fc0_11_total&lt;br /&gt;
attr EVU_Tibber_connect get02-27Name fc0_12_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-27OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-28Name fc0_12_total&lt;br /&gt;
attr EVU_Tibber_connect get02-29Name fc0_13_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-29OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-2Name current_price&lt;br /&gt;
attr EVU_Tibber_connect get02-2OExpr $val *100&lt;br /&gt;
attr EVU_Tibber_connect get02-30Name fc0_13_total&lt;br /&gt;
attr EVU_Tibber_connect get02-31Name fc0_14_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-31OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-32Name fc0_14_total&lt;br /&gt;
attr EVU_Tibber_connect get02-33Name fc0_15_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-33OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-34Name fc0_15_total&lt;br /&gt;
attr EVU_Tibber_connect get02-35Name fc0_16_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-35OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-36Name fc0_16_total&lt;br /&gt;
attr EVU_Tibber_connect get02-37Name fc0_17_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-37OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-38Name fc0_17_total&lt;br /&gt;
attr EVU_Tibber_connect get02-39Name fc0_18_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-39OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-3Name fc0_00_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-3OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-40Name fc0_18_total&lt;br /&gt;
attr EVU_Tibber_connect get02-41Name fc0_19_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-41OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-42Name fc0_19_total&lt;br /&gt;
attr EVU_Tibber_connect get02-43Name fc0_20_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-43OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-44Name fc0_20_total&lt;br /&gt;
attr EVU_Tibber_connect get02-45Name fc0_21_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-45OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-46Name fc0_21_total&lt;br /&gt;
attr EVU_Tibber_connect get02-47Name fc0_22_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-47OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-48Name fc0_22_total&lt;br /&gt;
attr EVU_Tibber_connect get02-49Name fc0_23_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-49OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-4Name fc0_00_total&lt;br /&gt;
attr EVU_Tibber_connect get02-50Name fc0_23_total&lt;br /&gt;
attr EVU_Tibber_connect get02-51Name fc1_00_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-51OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-52Name fc1_00_total&lt;br /&gt;
attr EVU_Tibber_connect get02-53Name fc1_01_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-53OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-54Name fc1_01_total&lt;br /&gt;
attr EVU_Tibber_connect get02-55Name fc1_02_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-55OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-56Name fc1_02_total&lt;br /&gt;
attr EVU_Tibber_connect get02-57Name fc1_03_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-57OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-58Name fc1_03_total&lt;br /&gt;
attr EVU_Tibber_connect get02-59Name fc1_04_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-59OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-5Name fc0_01_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-5OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-60Name fc1_04_total&lt;br /&gt;
attr EVU_Tibber_connect get02-61Name fc1_05_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-61OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-62Name fc1_05_total&lt;br /&gt;
attr EVU_Tibber_connect get02-63Name fc1_06_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-63OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-64Name fc1_06_total&lt;br /&gt;
attr EVU_Tibber_connect get02-65Name fc1_07_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-65OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-66Name fc1_07_total&lt;br /&gt;
attr EVU_Tibber_connect get02-67Name fc1_08_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-67OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-68Name fc1_08_total&lt;br /&gt;
attr EVU_Tibber_connect get02-69Name fc1_09_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-69OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-6Name fc0_01_total&lt;br /&gt;
attr EVU_Tibber_connect get02-70Name fc1_09_total&lt;br /&gt;
attr EVU_Tibber_connect get02-71Name fc1_10_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-71OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-72Name fc1_10_total&lt;br /&gt;
attr EVU_Tibber_connect get02-73Name fc1_11_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-73OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-74Name fc1_11_total&lt;br /&gt;
attr EVU_Tibber_connect get02-75Name fc1_12_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-75OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-76Name fc1_12_total&lt;br /&gt;
attr EVU_Tibber_connect get02-77Name fc1_13_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-77OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-78Name fc1_13_total&lt;br /&gt;
attr EVU_Tibber_connect get02-79Name fc1_14_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-79OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-7Name fc0_02_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-7OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-80Name fc1_14_total&lt;br /&gt;
attr EVU_Tibber_connect get02-81Name fc1_15_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-81OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-82Name fc1_15_total&lt;br /&gt;
attr EVU_Tibber_connect get02-83Name fc1_16_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-83OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-84Name fc1_16_total&lt;br /&gt;
attr EVU_Tibber_connect get02-85Name fc1_17_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-85OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-86Name fc1_17_total&lt;br /&gt;
attr EVU_Tibber_connect get02-87Name fc1_18_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-87OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-88Name fc1_18_total&lt;br /&gt;
attr EVU_Tibber_connect get02-89Name fc1_19_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-89OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-8Name fc0_02_total&lt;br /&gt;
attr EVU_Tibber_connect get02-90Name fc1_19_total&lt;br /&gt;
attr EVU_Tibber_connect get02-91Name fc1_20_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-91OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-92Name fc1_20_total&lt;br /&gt;
attr EVU_Tibber_connect get02-93Name fc1_21_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-93OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-94Name fc1_21_total&lt;br /&gt;
attr EVU_Tibber_connect get02-95Name fc1_22_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-95OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-96Name fc1_22_total&lt;br /&gt;
attr EVU_Tibber_connect get02-97Name fc1_23_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-97OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-98Name fc1_23_total&lt;br /&gt;
attr EVU_Tibber_connect get02-9Name fc0_03_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-9OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {currentSubscription {priceInfo {current {total startsAt} today {total startsAt} tomorrow {total startsAt}}}}}}&amp;quot; }&lt;br /&gt;
attr EVU_Tibber_connect get02Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get02Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get02JSON data_viewer_home_currentSubscription_priceInfo&lt;br /&gt;
attr EVU_Tibber_connect get02Name 02_priceAll&lt;br /&gt;
attr EVU_Tibber_connect get02URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get03-1Name nodes_00_00_from&lt;br /&gt;
attr EVU_Tibber_connect get03-1OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get03-2Name nodes_00_00_cost&lt;br /&gt;
attr EVU_Tibber_connect get03-2OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get03-3Name nodes_00_00_consumption&lt;br /&gt;
attr EVU_Tibber_connect get03-4Name nodes_00_01_from&lt;br /&gt;
attr EVU_Tibber_connect get03-4OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get03-50Name nodes_00_01_cost&lt;br /&gt;
attr EVU_Tibber_connect get03-50OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get03-5Name nodes_00_01_cost&lt;br /&gt;
attr EVU_Tibber_connect get03-5OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get03-6Name nodes_00_01_consumption&lt;br /&gt;
attr EVU_Tibber_connect get03-7Name nodes_00_02_from&lt;br /&gt;
attr EVU_Tibber_connect get03-7OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get03-8Name nodes_00_02_cost&lt;br /&gt;
attr EVU_Tibber_connect get03-8OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get03-9Name nodes_00_02_consumption&lt;br /&gt;
attr EVU_Tibber_connect get03Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {consumption(resolution: HOURLY, last: 3) {nodes {from cost consumption }}}}}&amp;quot;}&lt;br /&gt;
attr EVU_Tibber_connect get03Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get03Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get03Name 03_consumption_hour&lt;br /&gt;
attr EVU_Tibber_connect get03RegOpt g&lt;br /&gt;
attr EVU_Tibber_connect get03Regex \{&amp;quot;from&amp;quot;:&amp;quot;([\d+-]+T[\d+:]+\.000[+-][\d+:]+)&amp;quot;,&amp;quot;cost&amp;quot;:(null|\d+\.\d+|0),&amp;quot;consumption&amp;quot;:(null|\d+\.\d+|0)\}&lt;br /&gt;
attr EVU_Tibber_connect get03URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get04-10Name nodes_24_03_from&lt;br /&gt;
attr EVU_Tibber_connect get04-10OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-11Name nodes_24_03_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-11OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-12Name nodes_24_03_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-13Name nodes_24_04_from&lt;br /&gt;
attr EVU_Tibber_connect get04-13OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-14Name nodes_24_04_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-14OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-15Name nodes_24_04_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-16Name nodes_24_05_from&lt;br /&gt;
attr EVU_Tibber_connect get04-16OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-17Name nodes_24_05_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-17OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-18Name nodes_24_05_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-19Name nodes_24_06_from&lt;br /&gt;
attr EVU_Tibber_connect get04-19OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-1Name nodes_24_00_from&lt;br /&gt;
attr EVU_Tibber_connect get04-1OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-20Name nodes_24_06_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-20OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-21Name nodes_24_06_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-22Name nodes_24_07_from&lt;br /&gt;
attr EVU_Tibber_connect get04-22OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-23Name nodes_24_07_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-23OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-24Name nodes_24_07_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-25Name nodes_24_08_from&lt;br /&gt;
attr EVU_Tibber_connect get04-25OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-26Name nodes_24_08_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-26OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-27Name nodes_24_08_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-28Name nodes_24_09_from&lt;br /&gt;
attr EVU_Tibber_connect get04-28OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-29Name nodes_24_09_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-29OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-2Name nodes_24_00_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-2OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-30Name nodes_24_09_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-31Name nodes_24_10_from&lt;br /&gt;
attr EVU_Tibber_connect get04-31OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-32Name nodes_24_10_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-32OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-33Name nodes_24_10_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-34Name nodes_24_11_from&lt;br /&gt;
attr EVU_Tibber_connect get04-34OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-35Name nodes_24_11_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-35OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-36Name nodes_24_11_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-37Name nodes_24_12_from&lt;br /&gt;
attr EVU_Tibber_connect get04-37OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-38Name nodes_24_12_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-38OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-39Name nodes_24_12_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-3Name nodes_24_00_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-40Name nodes_24_13_from&lt;br /&gt;
attr EVU_Tibber_connect get04-40OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-41Name nodes_24_13_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-41OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-42Name nodes_24_13_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-43Name nodes_24_14_from&lt;br /&gt;
attr EVU_Tibber_connect get04-43OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-44Name nodes_24_14_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-44OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-45Name nodes_24_14_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-46Name nodes_24_15_from&lt;br /&gt;
attr EVU_Tibber_connect get04-46OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-47Name nodes_24_15_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-47OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-48Name nodes_24_15_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-49Name nodes_24_16_from&lt;br /&gt;
attr EVU_Tibber_connect get04-49OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-4Name nodes_24_01_from&lt;br /&gt;
attr EVU_Tibber_connect get04-4OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-50Name nodes_24_16_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-50OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-51Name nodes_24_16_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-52Name nodes_24_17_from&lt;br /&gt;
attr EVU_Tibber_connect get04-52OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-53Name nodes_24_17_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-53OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-54Name nodes_24_17_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-55Name nodes_24_18_from&lt;br /&gt;
attr EVU_Tibber_connect get04-55OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-56Name nodes_24_18_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-56OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-57Name nodes_24_18_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-58Name nodes_24_19_from&lt;br /&gt;
attr EVU_Tibber_connect get04-58OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-59Name nodes_24_19_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-59OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-5Name nodes_24_01_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-5OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-60Name nodes_24_19_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-61Name nodes_24_20_from&lt;br /&gt;
attr EVU_Tibber_connect get04-61OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-62Name nodes_24_20_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-62OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-63Name nodes_24_20_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-64Name nodes_24_21_from&lt;br /&gt;
attr EVU_Tibber_connect get04-64OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-65Name nodes_24_21_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-65OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-66Name nodes_24_21_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-67Name nodes_24_22_from&lt;br /&gt;
attr EVU_Tibber_connect get04-67OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-68Name nodes_24_22_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-68OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-69Name nodes_24_22_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-6Name nodes_24_01_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-70Name nodes_24_23_from&lt;br /&gt;
attr EVU_Tibber_connect get04-70OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-71Name nodes_24_23_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-71OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-72Name nodes_24_23_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-7Name nodes_24_02_from&lt;br /&gt;
attr EVU_Tibber_connect get04-7OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-8Name nodes_24_02_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-8OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-9Name nodes_24_02_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {consumption(resolution: HOURLY, last: 24) {nodes {from cost consumption}}}}}&amp;quot;}&lt;br /&gt;
attr EVU_Tibber_connect get04Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get04Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get04Name 04_consumption_hour_24&lt;br /&gt;
attr EVU_Tibber_connect get04RegOpt g&lt;br /&gt;
attr EVU_Tibber_connect get04Regex \{&amp;quot;from&amp;quot;:&amp;quot;([\d+-]+T[\d+:]+\.000[+-][\d+:]+)&amp;quot;,&amp;quot;cost&amp;quot;:(null|\d+\.\d+|0),&amp;quot;consumption&amp;quot;:(null|\d+\.\d+|0)\}&lt;br /&gt;
attr EVU_Tibber_connect get04URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get05AutoNumLen 4&lt;br /&gt;
attr EVU_Tibber_connect get05Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {consumption(resolution: HOURLY, last: 100) {nodes {from cost consumption}}}}}&amp;quot;}&lt;br /&gt;
attr EVU_Tibber_connect get05Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get05Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get05JSON data_viewer_home&lt;br /&gt;
attr EVU_Tibber_connect get05Name 05_consumption_hourly_100&lt;br /&gt;
attr EVU_Tibber_connect get05URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get06-10Name Adresse_07_Telefon&lt;br /&gt;
attr EVU_Tibber_connect get06-11Name Adresse_01_Vorname&lt;br /&gt;
attr EVU_Tibber_connect get06-12Name Adresse_02_Nachname&lt;br /&gt;
attr EVU_Tibber_connect get06-1Name Adresse_03_Strasse&lt;br /&gt;
attr EVU_Tibber_connect get06-2Name Adresse_05_Ort&lt;br /&gt;
attr EVU_Tibber_connect get06-3Name Adresse_06_Land&lt;br /&gt;
attr EVU_Tibber_connect get06-4Name Adresse_05_Ort&lt;br /&gt;
attr EVU_Tibber_connect get06-5Name Adresse_06_Land&lt;br /&gt;
attr EVU_Tibber_connect get06-6Name Adresse_09_latitude&lt;br /&gt;
attr EVU_Tibber_connect get06-7Name Adresse_10_longitude&lt;br /&gt;
attr EVU_Tibber_connect get06-8Name Adresse_04_Plz&lt;br /&gt;
attr EVU_Tibber_connect get06-9Name Adresse_08_eMail&lt;br /&gt;
attr EVU_Tibber_connect get06Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {address {address1 address2 address3 postalCode city country latitude longitude} owner {firstName lastName contactInfo {email mobile}}}}}&amp;quot; }&lt;br /&gt;
attr EVU_Tibber_connect get06Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get06Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get06JSON data_viewer_home&lt;br /&gt;
attr EVU_Tibber_connect get06Name 06_address&lt;br /&gt;
attr EVU_Tibber_connect get06URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get07-10Name nodes_24_03_from&lt;br /&gt;
attr EVU_Tibber_connect get07-10OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-11Name nodes_24_03_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-11OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-12Name nodes_24_03_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-13Name nodes_24_04_from&lt;br /&gt;
attr EVU_Tibber_connect get07-13OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-14Name nodes_24_04_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-14OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-15Name nodes_24_04_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-16Name nodes_24_05_from&lt;br /&gt;
attr EVU_Tibber_connect get07-16OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-17Name nodes_24_05_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-17OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-18Name nodes_24_05_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-19Name nodes_24_06_from&lt;br /&gt;
attr EVU_Tibber_connect get07-19OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-1Name features_realTimeConsumptionEnabled&lt;br /&gt;
attr EVU_Tibber_connect get07-1OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-20Name nodes_24_06_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-20OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-21Name nodes_24_06_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-22Name nodes_24_07_from&lt;br /&gt;
attr EVU_Tibber_connect get07-22OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-23Name nodes_24_07_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-23OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-24Name nodes_24_07_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-25Name nodes_24_08_from&lt;br /&gt;
attr EVU_Tibber_connect get07-25OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-26Name nodes_24_08_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-26OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-27Name nodes_24_08_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-28Name nodes_24_09_from&lt;br /&gt;
attr EVU_Tibber_connect get07-28OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-29Name nodes_24_09_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-29OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-2Name features_id&lt;br /&gt;
attr EVU_Tibber_connect get07-2OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-30Name nodes_24_09_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-31Name nodes_24_10_from&lt;br /&gt;
attr EVU_Tibber_connect get07-31OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-32Name nodes_24_10_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-32OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-33Name nodes_24_10_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-34Name nodes_24_11_from&lt;br /&gt;
attr EVU_Tibber_connect get07-34OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-35Name nodes_24_11_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-35OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-36Name nodes_24_11_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-37Name nodes_24_12_from&lt;br /&gt;
attr EVU_Tibber_connect get07-37OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-38Name nodes_24_12_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-38OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-39Name nodes_24_12_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-3Name nodes_24_00_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-40Name nodes_24_13_from&lt;br /&gt;
attr EVU_Tibber_connect get07-40OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-41Name nodes_24_13_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-41OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-42Name nodes_24_13_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-43Name nodes_24_14_from&lt;br /&gt;
attr EVU_Tibber_connect get07-43OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-44Name nodes_24_14_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-44OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-45Name nodes_24_14_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-46Name nodes_24_15_from&lt;br /&gt;
attr EVU_Tibber_connect get07-46OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-47Name nodes_24_15_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-47OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-48Name nodes_24_15_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-49Name nodes_24_16_from&lt;br /&gt;
attr EVU_Tibber_connect get07-49OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-4Name nodes_24_01_from&lt;br /&gt;
attr EVU_Tibber_connect get07-4OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-50Name nodes_24_16_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-50OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-51Name nodes_24_16_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-52Name nodes_24_17_from&lt;br /&gt;
attr EVU_Tibber_connect get07-52OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-53Name nodes_24_17_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-53OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-54Name nodes_24_17_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-55Name nodes_24_18_from&lt;br /&gt;
attr EVU_Tibber_connect get07-55OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-56Name nodes_24_18_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-56OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-57Name nodes_24_18_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-58Name nodes_24_19_from&lt;br /&gt;
attr EVU_Tibber_connect get07-58OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-59Name nodes_24_19_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-59OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-5Name nodes_24_01_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-5OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-60Name nodes_24_19_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-61Name nodes_24_20_from&lt;br /&gt;
attr EVU_Tibber_connect get07-61OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-62Name nodes_24_20_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-62OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-63Name nodes_24_20_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-64Name nodes_24_21_from&lt;br /&gt;
attr EVU_Tibber_connect get07-64OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-65Name nodes_24_21_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-65OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-66Name nodes_24_21_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-67Name nodes_24_22_from&lt;br /&gt;
attr EVU_Tibber_connect get07-67OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-68Name nodes_24_22_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-68OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-69Name nodes_24_22_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-6Name nodes_24_01_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-70Name nodes_24_23_from&lt;br /&gt;
attr EVU_Tibber_connect get07-70OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-71Name nodes_24_23_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-71OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-72Name nodes_24_23_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-7Name nodes_24_02_from&lt;br /&gt;
attr EVU_Tibber_connect get07-7OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-8Name nodes_24_02_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-8OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-9Name nodes_24_02_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {id features{realTimeConsumptionEnabled} } } }&amp;quot; }&lt;br /&gt;
attr EVU_Tibber_connect get07Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get07Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get07JSON data_viewer_home&lt;br /&gt;
attr EVU_Tibber_connect get07Name 07_realTimeConsumptionEnabled&lt;br /&gt;
attr EVU_Tibber_connect get07RegOpt g&lt;br /&gt;
attr EVU_Tibber_connect get07Regex \{&amp;quot;from&amp;quot;:&amp;quot;([\d+-]+T[\d+:]+\.000[+-][\d+:]+)&amp;quot;,&amp;quot;cost&amp;quot;:(null|\d+\.\d+|0),&amp;quot;consumption&amp;quot;:(null|\d+\.\d+|0)\}&lt;br /&gt;
attr EVU_Tibber_connect get07URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect group PV Steuerung EVU&lt;br /&gt;
attr EVU_Tibber_connect icon stromzaehler_icon&lt;br /&gt;
attr EVU_Tibber_connect replacement01Mode expression&lt;br /&gt;
attr EVU_Tibber_connect replacement01Regex %%token%%&lt;br /&gt;
attr EVU_Tibber_connect replacement01Value {KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_token&amp;quot;)}&lt;br /&gt;
attr EVU_Tibber_connect replacement02Mode expression&lt;br /&gt;
attr EVU_Tibber_connect replacement02Regex %%homeID%%&lt;br /&gt;
attr EVU_Tibber_connect replacement02Value {KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;)}&lt;br /&gt;
attr EVU_Tibber_connect requestData { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {currentSubscription {priceInfo {current {total energy tax startsAt }}}}}}&amp;quot; }&lt;br /&gt;
attr EVU_Tibber_connect requestHeader1 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect requestHeader2 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect room Strom-&amp;gt;Boerse&lt;br /&gt;
attr EVU_Tibber_connect showBody 1&lt;br /&gt;
attr EVU_Tibber_connect showError 1&lt;br /&gt;
attr EVU_Tibber_connect sortby 313&lt;br /&gt;
attr EVU_Tibber_connect timeout 30&lt;br /&gt;
attr EVU_Tibber_connect userReadings ws_connect:ws_cmd:.connect {\&lt;br /&gt;
    my $hash = $defs{$name};;\&lt;br /&gt;
    my $devState = DevIo_IsOpen($hash);;\&lt;br /&gt;
    return &amp;quot;Device already open&amp;quot; if (defined($devState));;\&lt;br /&gt;
    \&lt;br /&gt;
    # establish connection to websocket\&lt;br /&gt;
    # format must also include portnumber if a path is to be specified\&lt;br /&gt;
    $hash-&amp;gt;{DeviceName} = AttrVal($name, &amp;quot;ws_websocketURL&amp;quot;, &amp;quot;wss:echo.websocket.org:443&amp;quot;);;\&lt;br /&gt;
    \&lt;br /&gt;
    # special headers needed for Tibber, see also Developer Tools in Browser\&lt;br /&gt;
    $hash-&amp;gt;{header}{&#039;Sec-WebSocket-Protocol&#039;} = &#039;graphql-transport-ws&#039;;;\&lt;br /&gt;
    $hash-&amp;gt;{header}{&#039;Host&#039;} = &#039;websocket-api.tibber.com&#039;;;\&lt;br /&gt;
    $hash-&amp;gt;{header}{&#039;Origin&#039;} = &#039;https://developer.tibber.com&#039;;;\&lt;br /&gt;
    \&lt;br /&gt;
    # callback function when &amp;quot;select()&amp;quot; signals data for us\&lt;br /&gt;
    # websocket Ping/Pongs are treated in DevIo but still call this function\&lt;br /&gt;
    $hash-&amp;gt;{directReadFn} = sub () {\&lt;br /&gt;
        my $hash = $defs{$name};;\&lt;br /&gt;
        \&lt;br /&gt;
        # we can read without closing the DevIo, because select() signalled data\&lt;br /&gt;
        my $buf = DevIo_SimpleRead($hash);;\&lt;br /&gt;
        \&lt;br /&gt;
        # if read fails, close device\&lt;br /&gt;
        if(!defined($buf)) {\&lt;br /&gt;
            DevIo_CloseDev($hash);;\&lt;br /&gt;
            $buf = &amp;quot;not_connected&amp;quot;;;\&lt;br /&gt;
        }\&lt;br /&gt;
        \&lt;br /&gt;
        #Log(3, &amp;quot;$name:$reading: websocket data: &amp;gt;&amp;gt;&amp;gt;$buf&amp;lt;&amp;lt;&amp;lt;&amp;quot;);;\&lt;br /&gt;
        \&lt;br /&gt;
        # only update our reading if buffer is not empty and if last update is older than minInterval\&lt;br /&gt;
        if ($buf ne &amp;quot;&amp;quot;) {\&lt;br /&gt;
            my $websocketDataAge = ReadingsAge($name, &amp;quot;ws_websocketData&amp;quot;, 3600);;\&lt;br /&gt;
            my $minInterval = AttrVal($name, &amp;quot;ws_minInterval&amp;quot;, 0);;\&lt;br /&gt;
            my $isNext = ($buf =~ /.*id.*type.*next.*payload.*data.*liveMeasurement.*/s);;\&lt;br /&gt;
            \&lt;br /&gt;
            readingsBeginUpdate($hash);;\&lt;br /&gt;
            readingsBulkUpdate($hash, &amp;quot;ws_websocketData&amp;quot;, &amp;quot;$buf&amp;quot;) if ($isNext &amp;amp;&amp;amp; $websocketDataAge &amp;gt; $minInterval);;\&lt;br /&gt;
            readingsBulkUpdate($hash, &amp;quot;ws_websocketData&amp;quot;, &amp;quot;$buf&amp;quot;) if (!$isNext);;\&lt;br /&gt;
            readingsEndUpdate($hash, 1);;\&lt;br /&gt;
            #Log(3, &amp;quot;$name:$reading: websocket data written to reading&amp;quot;);;\&lt;br /&gt;
        }\&lt;br /&gt;
    };;\&lt;br /&gt;
    \&lt;br /&gt;
    # open DevIo websocket\&lt;br /&gt;
    DevIo_OpenDev($hash, 0, undef, sub(){\&lt;br /&gt;
        my ($hash, $error) = @_;;\&lt;br /&gt;
        return &amp;quot;$error&amp;quot; if ($error);;\&lt;br /&gt;
        \&lt;br /&gt;
        my $token = AttrVal($name, &amp;quot;ws_token&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
           $token = KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_token&amp;quot;) if ($token eq &amp;quot;???&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
        DevIo_SimpleWrite($hash, &#039;{&amp;quot;type&amp;quot;:&amp;quot;connection_init&amp;quot;,&amp;quot;payload&amp;quot;:{&amp;quot;token&amp;quot;:&amp;quot;&#039;.$token.&#039;&amp;quot;}}&#039;, 2);;\&lt;br /&gt;
    });;\&lt;br /&gt;
    readingsBulkUpdate($hash, &amp;quot;ws_websocketData&amp;quot;, &amp;quot;&amp;quot;);;\&lt;br /&gt;
    #Log(3, &amp;quot;$name:$reading: websocket data cleared in reading&amp;quot;);;\&lt;br /&gt;
      \&lt;br /&gt;
    return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
ws_disconnect:ws_cmd:.disconnect {\&lt;br /&gt;
    Log(3, &amp;quot;$name: disconnect&amp;quot;);;\&lt;br /&gt;
    my $hash = $defs{$name};;\&lt;br /&gt;
    RemoveInternalTimer($hash);;\&lt;br /&gt;
    DevIo_SimpleRead($hash);;\&lt;br /&gt;
    DevIo_CloseDev($hash);;\&lt;br /&gt;
\&lt;br /&gt;
    return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
ws_onDisconnect {\&lt;br /&gt;
    my $myState = ReadingsVal($name, &amp;quot;state&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
    my $myData = ReadingsVal($name, &amp;quot;ws_websocketData&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
    return if ($myState ne &amp;quot;disconnected&amp;quot; and $myData ne &amp;quot;not_connected&amp;quot;);;\&lt;br /&gt;
    \&lt;br /&gt;
	## timer callback function, called after a few seconds to initiate a reconnect\&lt;br /&gt;
	my $timerFunction = sub() {\&lt;br /&gt;
		my ($arg) = @_;;\&lt;br /&gt;
		my $hash = $defs{$name};;\&lt;br /&gt;
		my $devState = DevIo_IsOpen($hash);;\&lt;br /&gt;
		\&lt;br /&gt;
		# only re-connect if device is not connected\&lt;br /&gt;
		readingsSingleUpdate($hash, &amp;quot;ws_cmd&amp;quot;, &amp;quot;connect&amp;quot;, 1) if (!defined($devState));;\&lt;br /&gt;
	};;\&lt;br /&gt;
	RemoveInternalTimer($name.$reading.&#039;Timer&#039;);;\&lt;br /&gt;
	\&lt;br /&gt;
	# wait a random time before reconnect (exponential backoff TBD):\&lt;br /&gt;
	my $rwait = int(rand(200)) + 30;;\&lt;br /&gt;
	InternalTimer(gettimeofday() + $rwait, $timerFunction, $name.$reading.&#039;Timer&#039;);;\&lt;br /&gt;
	\&lt;br /&gt;
	#set cmd to a new value, informs user and allows to retrigger when timer expires\&lt;br /&gt;
	my $hash = $defs{$name};;\&lt;br /&gt;
	readingsBulkUpdate($hash, &amp;quot;ws_cmd&amp;quot;, &amp;quot;reconnect attempt in $rwait seconds&amp;quot;);;\&lt;br /&gt;
	\&lt;br /&gt;
	return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
ws_onTimeout:ws_websocketData:.* {\&lt;br /&gt;
	#re-establish websocket connection if no data received in the past ten minutes\&lt;br /&gt;
	#but only if our reading &amp;quot;ws_cmd&amp;quot; was not set to the value &amp;quot;disconnect&amp;quot;\&lt;br /&gt;
\&lt;br /&gt;
        #Log(3, &amp;quot;$name:$reading: websocket data triggert ws_onTimeout&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
	#timeout in seconds when the connection is considered dead\&lt;br /&gt;
	my $timeoutTime = 600;;\&lt;br /&gt;
	\&lt;br /&gt;
	# function to execute when timeout expired\&lt;br /&gt;
	# defining the function here in the userReading, allows us to insert variables directly\&lt;br /&gt;
	my $timerFunction = sub() {\&lt;br /&gt;
		my ($arg) = @_;;\&lt;br /&gt;
		my $hash = $defs{$name};;\&lt;br /&gt;
		my $rCmd = ReadingsVal($name, &amp;quot;ws_cmd&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
		my $age  = ReadingsAge($name, &amp;quot;ws_websocketData&amp;quot;, 0);;\&lt;br /&gt;
		\&lt;br /&gt;
		Log(3, &amp;quot;$name: onTimeoutTimer triggered &amp;gt;&amp;gt;$arg&amp;lt;&amp;lt;&amp;quot;);;\&lt;br /&gt;
		\&lt;br /&gt;
		#do not do anything further if disconnect is on purpose\&lt;br /&gt;
		if ( $rCmd eq &amp;quot;disconnect&amp;quot; ) {\&lt;br /&gt;
			Log(3, &amp;quot;$name: ws_cmd was set to disconnect&amp;quot;);;\&lt;br /&gt;
			return;;\&lt;br /&gt;
		}\&lt;br /&gt;
		\&lt;br /&gt;
		# for whatever reason, we triggered to soon (80%)\&lt;br /&gt;
		if ( $age &amp;lt; $timeoutTime*0.8 ) {\&lt;br /&gt;
			Log(3, &amp;quot;$name: ws_websocketData is not outdated&amp;quot;);;\&lt;br /&gt;
			return;;\&lt;br /&gt;
		}\&lt;br /&gt;
		\&lt;br /&gt;
		DevIo_CloseDev($hash);;\&lt;br /&gt;
		Log(3, &amp;quot;$name: onTimeoutTimer closed DevIo...&amp;quot;);;\&lt;br /&gt;
		\&lt;br /&gt;
		readingsSingleUpdate($hash, &amp;quot;ws_cmd&amp;quot;, &amp;quot;connect&amp;quot;, 1);;\&lt;br /&gt;
		Log(3, &amp;quot;$name: onTimeoutTimer set ws_cmd to value &#039;connect&#039;&amp;quot;);;\&lt;br /&gt;
	};;\&lt;br /&gt;
\&lt;br /&gt;
        #Log(3, &amp;quot;$name:$reading: onTimeout function defined&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
	#remove/cancel previous timers, because we got fresh data and countdown starts again\&lt;br /&gt;
	RemoveInternalTimer($name.$reading.&#039;Timer&#039;);;\&lt;br /&gt;
	\&lt;br /&gt;
	#set timer to expire and execute function defined above, give special arg as identifier\&lt;br /&gt;
	InternalTimer(gettimeofday() + $timeoutTime, $timerFunction, $name.$reading.&#039;Timer&#039;);;\&lt;br /&gt;
	\&lt;br /&gt;
	return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
ws_onConnectionAck:ws_websocketData:.*connection_ack.* {\&lt;br /&gt;
    #ws_websocketData contains the string &amp;quot;connection_ack&amp;quot;\&lt;br /&gt;
    Log(3, &amp;quot;$name:$reading: got connection ack&amp;quot;);;\&lt;br /&gt;
    \&lt;br /&gt;
    # do not proceed if connection is lost\&lt;br /&gt;
    my $hash = $defs{$name};;\&lt;br /&gt;
    my $devState = DevIo_IsOpen($hash);;\&lt;br /&gt;
    return &amp;quot;Device not open&amp;quot; if (!defined($devState));;\&lt;br /&gt;
\&lt;br /&gt;
    readingsBulkUpdate($hash, &amp;quot;ws_cmd&amp;quot;, &amp;quot;got connection ack&amp;quot;);;\&lt;br /&gt;
    \&lt;br /&gt;
    my $homeId = AttrVal($name, &amp;quot;ws_homeId&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
       $homeId = KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;) if ($homeId eq &amp;quot;???&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
    my $myId = AttrVal($name, &amp;quot;ws_myId&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
       $myId = KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;) if ($myId eq &amp;quot;???&amp;quot;);;\&lt;br /&gt;
    \&lt;br /&gt;
    # build the query, do it in pieces, the comma at the end caused perl errors\&lt;br /&gt;
    # so we put it together in this not very elegant way\&lt;br /&gt;
    my $json = &#039;{ &amp;quot;id&amp;quot;:&amp;quot;&#039;. $myId .&#039;&amp;quot;, &amp;quot;type&amp;quot;:&amp;quot;subscribe&amp;quot;&#039;.&amp;quot;, &amp;quot;;;\&lt;br /&gt;
    $json .= &#039;&amp;quot;payload&amp;quot;:{&#039;;;\&lt;br /&gt;
    $json .= &#039;&amp;quot;variables&amp;quot;:{}&#039;.&amp;quot;, &amp;quot;;;\&lt;br /&gt;
    $json .= &#039;&amp;quot;extensions&amp;quot;:{}&#039;.&amp;quot;, &amp;quot;;;\&lt;br /&gt;
    $json .= &#039;&amp;quot;query&amp;quot;:&amp;quot;subscription { liveMeasurement( homeId: \&amp;quot;&#039;.$homeId.&#039;\&amp;quot; ) &#039;;;\&lt;br /&gt;
    #$json .= &#039;{ timestamp power accumulatedConsumption accumulatedCost currency minPower averagePower maxPower signalStrength }}&amp;quot;&#039;;;\&lt;br /&gt;
    $json .= &#039;{ timestamp power lastMeterConsumption accumulatedConsumption accumulatedProduction &#039;;;\&lt;br /&gt;
    $json .= &#039;accumulatedProductionLastHour accumulatedCost accumulatedReward currency minPower averagePower maxPower &#039;;;\&lt;br /&gt;
    $json .= &#039;powerProduction powerReactive powerProductionReactive minPowerProduction maxPowerProduction lastMeterProduction &#039;;;\&lt;br /&gt;
    $json .= &#039;powerFactor voltagePhase1 voltagePhase2 voltagePhase3 signalStrength }}&amp;quot;&#039;;;\&lt;br /&gt;
    $json .= &#039;}}&#039;;;\&lt;br /&gt;
    \&lt;br /&gt;
    #send the string via websocket as ASCII\&lt;br /&gt;
    Log(3, &amp;quot;$name:$reading: sending JSON: &amp;gt;&amp;gt;&amp;gt;$json&amp;lt;&amp;lt;&amp;lt;&amp;quot;);;\&lt;br /&gt;
    DevIo_SimpleWrite($hash, $json, 2);;\&lt;br /&gt;
        \&lt;br /&gt;
    return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
ws_onNextLiveMeasurement:ws_websocketData:.*next.*payload.*data.*liveMeasurement.* {\&lt;br /&gt;
    #websocketData contains next-live-measurement-data\&lt;br /&gt;
    my $val = ReadingsVal($name, &amp;quot;ws_websocketData&amp;quot;, &amp;quot;{}&amp;quot;);;\&lt;br /&gt;
    my %res = %{json2nameValue($val, undef, undef, &amp;quot;payload_data_liveMeasurement.*&amp;quot;)};;\&lt;br /&gt;
    \&lt;br /&gt;
    my $ret = &amp;quot;got values for:\n&amp;quot;;;\&lt;br /&gt;
    foreach my $k (sort keys %res) {\&lt;br /&gt;
        $ret .= &amp;quot;$k\n&amp;quot;;;\&lt;br /&gt;
        readingsBulkUpdate($hash, makeReadingName($k), $res{$k});;\&lt;br /&gt;
    }\&lt;br /&gt;
    return $ret;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_TIMESTAMP:nodes_00_00_cost.* {\&lt;br /&gt;
my ($timestamp,$value) = 2x0;;\&lt;br /&gt;
my $tmp = 0;;\&lt;br /&gt;
\&lt;br /&gt;
for (my $loop_last = 0;; $loop_last &amp;lt;= 2;; $loop_last++) {\&lt;br /&gt;
  $timestamp = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_00_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_from&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_00_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_cost&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $value ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    # Eintragen der Kosten für die Stunde\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_cost&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
    # Eintragen des Verbrauchs für die Stunde\&lt;br /&gt;
    $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_00_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_consumption&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_consumption&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
  } else {\&lt;br /&gt;
    $tmp = &amp;quot;null&amp;quot;;;\&lt;br /&gt;
  }\&lt;br /&gt;
} # end for\&lt;br /&gt;
if ($tmp eq &amp;quot;null&amp;quot;) {\&lt;br /&gt;
  $timestamp = $tmp\&lt;br /&gt;
}\&lt;br /&gt;
$timestamp;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_TIMESTAMP:nodes_24_00_cost.* {\&lt;br /&gt;
my ($timestamp,$value) = 2x0;;\&lt;br /&gt;
my $tmp = 0;;\&lt;br /&gt;
\&lt;br /&gt;
for (my $loop_last = 0;; $loop_last &amp;lt;= 23;; $loop_last++) {\&lt;br /&gt;
  $timestamp = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_24_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_from&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_24_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_cost&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $value ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    # Eintragen der Kosten für die Stunde\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_cost&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
    # Eintragen des Verbrauchs für die Stunde\&lt;br /&gt;
    $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_24_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_consumption&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_consumption&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
  } else {\&lt;br /&gt;
    $tmp = &amp;quot;null&amp;quot;;;\&lt;br /&gt;
  }\&lt;br /&gt;
} # end for\&lt;br /&gt;
if ($tmp eq &amp;quot;null&amp;quot;) {\&lt;br /&gt;
  $timestamp = $tmp\&lt;br /&gt;
}\&lt;br /&gt;
$timestamp;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_TIMESTAMP:05_consumption_hourly_100-0001.* {\&lt;br /&gt;
my ($timestamp,$value) = 2x0;;\&lt;br /&gt;
\&lt;br /&gt;
for (my $loop_last = 1;; $loop_last &amp;lt;= 300;; $loop_last += 3) {\&lt;br /&gt;
\&lt;br /&gt;
  $timestamp =  ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;05_consumption_hourly_100-&amp;quot;.sprintf(&amp;quot;%04d&amp;quot;,$loop_last+2),&amp;quot;null&amp;quot;);; # timestamp\&lt;br /&gt;
  $timestamp =~ s/T/ /g ;;\&lt;br /&gt;
  $timestamp =  substr($timestamp,0,19);;\&lt;br /&gt;
  $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;05_consumption_hourly_100-&amp;quot;.sprintf(&amp;quot;%04d&amp;quot;,$loop_last+1),&amp;quot;null&amp;quot;);;  # cost\&lt;br /&gt;
\&lt;br /&gt;
  if ( $value ne &amp;quot;&amp;quot; ) {\&lt;br /&gt;
    $value = round($value,4);;\&lt;br /&gt;
# print $timestamp.&amp;quot; &amp;quot;.$value.&amp;quot;\n&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
    # Eintragen der Kosten für die Stunde\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_cost&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
    # Eintragen des Verbrauchs für die Stunde\&lt;br /&gt;
    $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;05_consumption_hourly_100-&amp;quot;.sprintf(&amp;quot;%04d&amp;quot;,$loop_last),&amp;quot;null&amp;quot;);;  # consumption\&lt;br /&gt;
    $value = round($value,4);;\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_consumption&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
  }\&lt;br /&gt;
} # end for\&lt;br /&gt;
\&lt;br /&gt;
$timestamp;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_cost_avg:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Tages Wertes\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_TIMESTAMP&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(avg(VALUE)*100 AS DECIMAL(4,2)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND TIMESTAMP &amp;gt;= curdate() ;;&amp;quot;) ;;\&lt;br /&gt;
  } else {\&lt;br /&gt;
    ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_cost_avg&amp;quot;,&amp;quot;null&amp;quot;)\&lt;br /&gt;
  }\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_cost_min:nodes_TIMESTAMP.* {\&lt;br /&gt;
##  Ermittlung des minimal Wertes\&lt;br /&gt;
if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_TIMESTAMP&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
  ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(min(VALUE)*100 AS DECIMAL(4,2)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND TIMESTAMP &amp;gt;= curdate() ;;&amp;quot;) ;;\&lt;br /&gt;
} else {\&lt;br /&gt;
  ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_cost_min&amp;quot;,&amp;quot;null&amp;quot;)\&lt;br /&gt;
}\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_cost_max:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Ermittlung des maximal Wertes\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_TIMESTAMP&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(max(VALUE)*100 AS DECIMAL(4,2)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND TIMESTAMP &amp;gt;= curdate() ;;&amp;quot;) ;;\&lt;br /&gt;
  } else {\&lt;br /&gt;
    ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_cost_max&amp;quot;,&amp;quot;null&amp;quot;)\&lt;br /&gt;
  }\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_consumption_day:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Tages Verbrauches\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(sum(VALUE) AS DECIMAL(10,4)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_consumption&#039;\&lt;br /&gt;
             AND date(TIMESTAMP) = curdate() ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_consumption_month:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Monats Verbrauches\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(sum(VALUE) AS DECIMAL(10,4)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_consumption&#039;\&lt;br /&gt;
             AND YEAR(TIMESTAMP) = YEAR(curdate())\&lt;br /&gt;
             AND MONTH(TIMESTAMP) = MONTH(curdate()) ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_consumption_year:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Jahres Verbrauches\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(sum(VALUE) AS DECIMAL(10,4)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_consumption&#039;\&lt;br /&gt;
             AND YEAR(TIMESTAMP) = YEAR(curdate()) ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_avg:current_price.* {\&lt;br /&gt;
## Berechnung des durchschnitt Wertes\&lt;br /&gt;
  my $fc_avg = 0;;\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
\&lt;br /&gt;
  if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {       ## Der nächste Tag ist bereits da\&lt;br /&gt;
    if (AttrVal(&amp;quot;$NAME&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
      Log 3, &amp;quot;$NAME cmd_1  : Tibber Daten für fc1 sind bereits da&amp;quot;;;\&lt;br /&gt;
    }\&lt;br /&gt;
    $fc = 1;;\&lt;br /&gt;
  } # end if\&lt;br /&gt;
\&lt;br /&gt;
    for (my $j=0;;$j&amp;lt;=$fc;;$j++){\&lt;br /&gt;
      for (my $k=0;;$k&amp;lt;=23;;$k++) {                                          ## Summe  berechnen\&lt;br /&gt;
        $fc_avg += ReadingsVal(&amp;quot;$NAME&amp;quot;,sprintf(&amp;quot;fc%d_%02d_total&amp;quot;,$j,$k),0);;\&lt;br /&gt;
        } # end $k\&lt;br /&gt;
    } # end $j\&lt;br /&gt;
\&lt;br /&gt;
    return round($fc_avg / (24 *(1+$fc)) *100 ,2);;                     ## Durchschnitt berechnen\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_med:current_price.* {\&lt;br /&gt;
## Berechnung des median Wertes\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT\&lt;br /&gt;
             cast( (   (SUBSTRING_INDEX(SUBSTRING_INDEX(group_concat(VALUE order by VALUE), &#039;,&#039;, floor(1+((count(VALUE)-1) / 2)))  , &#039;,&#039;, -1))\&lt;br /&gt;
                       + (SUBSTRING_INDEX(SUBSTRING_INDEX(group_concat(VALUE order by VALUE), &#039;,&#039;, ceiling(1+((count(VALUE)-1) / 2))), &#039;,&#039;, -1))\&lt;br /&gt;
                     )/2 *100\&lt;br /&gt;
             AS DECIMAL(4,2))\&lt;br /&gt;
           FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND (   READING=&#039;fc0_total&#039; AND TIMESTAMP &amp;gt;= DATE_FORMAT(NOW(), &#039;%Y-%m-%d 00:00:00&#039;)\&lt;br /&gt;
                     OR READING=&#039;fc1_total&#039; AND TIMESTAMP &amp;gt;= DATE_FORMAT(NOW() + INTERVAL 1 DAY, &#039;%Y-%m-%d 00:00:00&#039;) ) ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_min:current_price.* {\&lt;br /&gt;
##  Ermittlung des minimal Wertes\&lt;br /&gt;
  my $fc_min = 0;;\&lt;br /&gt;
  my $fc_tmp = 0;;\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
\&lt;br /&gt;
  if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {       ## Der nächste Tag ist bereits da\&lt;br /&gt;
    $fc = 1;;\&lt;br /&gt;
  } # end if\&lt;br /&gt;
\&lt;br /&gt;
    for (my $j=0;;$j&amp;lt;=$fc;;$j++){\&lt;br /&gt;
      for (my $k=0;;$k&amp;lt;=23;;$k++) {\&lt;br /&gt;
        $fc_tmp = ReadingsVal(&amp;quot;$NAME&amp;quot;,sprintf(&amp;quot;fc%d_%02d_total&amp;quot;,$j,$k),0);;\&lt;br /&gt;
  \&lt;br /&gt;
        if (($fc_tmp &amp;lt; $fc_min) or ($j == 0 and $k == 0)) {\&lt;br /&gt;
          $fc_min = $fc_tmp;;\&lt;br /&gt;
        }\&lt;br /&gt;
      } # end $k\&lt;br /&gt;
    } # end $j\&lt;br /&gt;
\&lt;br /&gt;
    return round($fc_min *100 ,2);;                             ## Von Euro auf Cent umrechnen\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_max:current_price.* {\&lt;br /&gt;
##  Ermittlung des minimal Wertes\&lt;br /&gt;
  my $fc_max = 0;;\&lt;br /&gt;
  my $fc_tmp = 0;;\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
\&lt;br /&gt;
  if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {       ## Der nächste Tag ist bereits da\&lt;br /&gt;
    $fc = 1;;\&lt;br /&gt;
  } # end if\&lt;br /&gt;
\&lt;br /&gt;
    for (my $j=0;;$j&amp;lt;=$fc;;$j++){\&lt;br /&gt;
      for (my $k=0;;$k&amp;lt;=23;;$k++) {\&lt;br /&gt;
        $fc_tmp = ReadingsVal(&amp;quot;$NAME&amp;quot;,sprintf(&amp;quot;fc%d_%02d_total&amp;quot;,$j,$k),0);;\&lt;br /&gt;
  \&lt;br /&gt;
        if (($fc_tmp &amp;gt; $fc_max) or ($j == 0 and $k == 0)) {\&lt;br /&gt;
          $fc_max = $fc_tmp;;\&lt;br /&gt;
        }\&lt;br /&gt;
      } # end $k\&lt;br /&gt;
    } # end $j\&lt;br /&gt;
\&lt;br /&gt;
    return round($fc_max *100 ,2);;                             ## Von Euro auf Cent umrechnen\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_trigger_price:fc_avg.* {\&lt;br /&gt;
## fc_trigger_price:[fc_avg|compensation_grid].* {\&lt;br /&gt;
  my $fc_avg = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_avg&amp;quot;,0);;\&lt;br /&gt;
  my $fc_min = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_min&amp;quot;,0);;\&lt;br /&gt;
\&lt;br /&gt;
  # Berechnung eines Default Schwellwertes als täglicher Niedrigpreis\&lt;br /&gt;
  my $price_level = round( ($fc_avg - $fc_min)/2 + $fc_min , 1);;\&lt;br /&gt;
  \&lt;br /&gt;
  # Abschätzung von Wirtschaftlichkeit beim Speicher Laden, falls Tibber zu teuer wird\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;compensation_grid&amp;quot;,0) != 0 ) {\&lt;br /&gt;
    my $price_level_battery = round( ($fc_avg - ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;compensation_grid&amp;quot;,0)) *0.85 , 1) ;;\&lt;br /&gt;
    if ( $price_level &amp;gt; $price_level_battery ) {\&lt;br /&gt;
      $price_level = $price_level_battery;;\&lt;br /&gt;
    }\&lt;br /&gt;
  }\&lt;br /&gt;
$price_level;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_start:fc_trigger_price.* {\&lt;br /&gt;
  my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
  my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for (my $loop_hour = $hour;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour)) ;;\&lt;br /&gt;
      }\&lt;br /&gt;
    } # end  for loop_hour\&lt;br /&gt;
\&lt;br /&gt;
  return(&amp;quot;null&amp;quot;);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_stop:fc0_trigger_start.* {\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
  my $loop_hour = 0;;\&lt;br /&gt;
  my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my $fc_trigger_start = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_trigger_start&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  my $fc_trigger_stop = $fc_trigger_start;;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $fc_trigger_start ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    $fc_trigger_start =~ /(\d\d):/;; $fc_trigger_start = $1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for ($loop_hour = $fc_trigger_start;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$fc.&amp;quot;_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        $fc_trigger_stop = sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour) ;;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour));;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
     # wechsel zum nächsten Tag\&lt;br /&gt;
     if ( $loop_hour == 23 and $fc == 0 ) {\&lt;br /&gt;
       $fc = 1;;\&lt;br /&gt;
       $loop_hour = -1;;\&lt;br /&gt;
     }\&lt;br /&gt;
\&lt;br /&gt;
    } # end for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
 \&lt;br /&gt;
  return($fc_trigger_stop);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger:fc0_trigger_stop.* {\&lt;br /&gt;
\&lt;br /&gt;
  # Setzen des Triggers für die aktuelle Stunde\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;current_price&amp;quot;,100)  &amp;lt; ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) ) {\&lt;br /&gt;
    return(&amp;quot;on&amp;quot;)\&lt;br /&gt;
  } else {\&lt;br /&gt;
    return(&amp;quot;off&amp;quot;)\&lt;br /&gt;
  }\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc1_trigger_start:fc_trigger_price.* {\&lt;br /&gt;
  if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {\&lt;br /&gt;
    my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
    # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
    my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for (my $loop_hour = 0;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour)) ;;\&lt;br /&gt;
      }\&lt;br /&gt;
    } # end  for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
  return(&amp;quot;null&amp;quot;);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc1_trigger_stop:fc0_trigger_start.* {\&lt;br /&gt;
  my $loop_hour = 0;;\&lt;br /&gt;
  my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my $fc_trigger_start = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_trigger_start&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  my $fc_trigger_stop = $fc_trigger_start;;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $fc_trigger_start ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    $fc_trigger_start =~ /(\d\d):/;; $fc_trigger_start = $1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for ($loop_hour = $fc_trigger_start;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        $fc_trigger_stop = sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour) ;;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour));;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
    } # end for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
 \&lt;br /&gt;
  return($fc_trigger_stop);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_trigger_max:fc_avg.* {\&lt;br /&gt;
  my $fc_avg = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_avg&amp;quot;,0);;\&lt;br /&gt;
  my $fc_max = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_max&amp;quot;,0);;\&lt;br /&gt;
\&lt;br /&gt;
  # Berechnung eines Schwellwertes als täglichen Maximalpreises\&lt;br /&gt;
  my $price_level = round( ($fc_max - $fc_avg)/2 + $fc_avg , 0);;\&lt;br /&gt;
  \&lt;br /&gt;
$price_level;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_max_start:fc_trigger_max.* {\&lt;br /&gt;
  my $fc_trigger_max = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_max&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger_max Fensters\&lt;br /&gt;
  my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
  my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for (my $loop_hour = $hour;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;gt; $fc_trigger_max ) {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour)) ;;\&lt;br /&gt;
      }\&lt;br /&gt;
    } # end  for loop_hour\&lt;br /&gt;
\&lt;br /&gt;
  return(&amp;quot;null&amp;quot;);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_max_stop:fc0_trigger_max_start.* {\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
  my $loop_hour = 0;;\&lt;br /&gt;
  my $fc_trigger_max = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_max&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my $fc_trigger_max_start = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_trigger_max_start&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  my $fc_trigger_max_stop = $fc_trigger_max_start;;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $fc_trigger_max_start ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    $fc_trigger_max_start =~ /(\d\d):/;; $fc_trigger_max_start = $1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for ($loop_hour = $fc_trigger_max_start;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$fc.&amp;quot;_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;gt; $fc_trigger_max ) {\&lt;br /&gt;
        $fc_trigger_max_stop = sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour) ;;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour));;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
     # wechsel zum nächsten Tag\&lt;br /&gt;
     if ( $loop_hour == 23 and $fc == 0 ) {\&lt;br /&gt;
       $fc = 1;;\&lt;br /&gt;
       $loop_hour = -1;;\&lt;br /&gt;
     }\&lt;br /&gt;
\&lt;br /&gt;
    } # end for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
 \&lt;br /&gt;
  return($fc_trigger_max_stop);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_max:fc0_trigger_max_stop.* {\&lt;br /&gt;
\&lt;br /&gt;
  # Setzen des maximum Triggers für die aktuelle Stunde\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;current_price&amp;quot;,0)  &amp;gt; ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_max&amp;quot;,0) ) {\&lt;br /&gt;
    return(&amp;quot;on&amp;quot;)\&lt;br /&gt;
  } else {\&lt;br /&gt;
    return(&amp;quot;off&amp;quot;)\&lt;br /&gt;
  }\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc1_trigger_max_start:fc_trigger_max.* {\&lt;br /&gt;
  if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {\&lt;br /&gt;
    my $fc_trigger_max = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_max&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
    # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
    my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for (my $loop_hour = 0;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;gt; $fc_trigger_max ) {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour)) ;;\&lt;br /&gt;
      }\&lt;br /&gt;
    } # end  for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
  return(&amp;quot;null&amp;quot;);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc1_trigger_max_stop:fc0_trigger_max_start.* {\&lt;br /&gt;
  my $loop_hour = 0;;\&lt;br /&gt;
  my $fc_trigger_max = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_max&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my $fc_trigger_max_start = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_trigger_max_start&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  my $fc_trigger_max_stop = $fc_trigger_max_start;;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $fc_trigger_max_start ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    $fc_trigger_max_start =~ /(\d\d):/;; $fc_trigger_max_start = $1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for ($loop_hour = $fc_trigger_max_start;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;gt; $fc_trigger_max ) {\&lt;br /&gt;
        $fc_trigger_max_stop = sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour) ;;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour));;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
    } # end for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
 \&lt;br /&gt;
  return($fc_trigger_max_stop);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
total_cost_day:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Tages Wertes\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT CAST(SUM(VALUE) AS DECIMAL(8,2)) AS VALUE\&lt;br /&gt;
           FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND date(TIMESTAMP) = curdate() ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
total_cost_month:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des monats Wertes\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT CAST(SUM(VALUE) AS DECIMAL(8,2)) AS VALUE\&lt;br /&gt;
           FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND YEAR(TIMESTAMP) = YEAR(curdate())\&lt;br /&gt;
             AND MONTH(TIMESTAMP) = MONTH(curdate()) ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
total_cost_year:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Jahres Wertes\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT CAST(SUM(VALUE) AS DECIMAL(8,2)) AS VALUE\&lt;br /&gt;
           FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND YEAR(TIMESTAMP) = YEAR(curdate()) ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_DbLog:fc0_00_total.* {\&lt;br /&gt;
my ($timestamp,$date,$hour,$value,$loop_fc_next) = 5x0;;\&lt;br /&gt;
\&lt;br /&gt;
for (my $loop_fc = 0;; $loop_fc &amp;lt;= 1;; $loop_fc++) {\&lt;br /&gt;
  $loop_fc_next = $loop_fc +1;;\&lt;br /&gt;
  $date = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$loop_fc.&amp;quot;_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  if ($date ne &amp;quot;null&amp;quot;) {\&lt;br /&gt;
    $date =~ /([\d+-]+)/;; $date = $1 ;;\&lt;br /&gt;
    for (my $loop_hour = 0;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $hour = sprintf(&amp;quot;%02d&amp;quot;,$loop_hour);;\&lt;br /&gt;
      $timestamp = $date.&amp;quot; &amp;quot;.$hour.&amp;quot;:00:00&amp;quot;;;\&lt;br /&gt;
      $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$loop_fc.&amp;quot;_&amp;quot;.$hour.&amp;quot;_total&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
      ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
                        INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                          VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;fc&amp;quot;.$loop_fc.&amp;quot;_total&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
                        ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                          VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
    }\&lt;br /&gt;
    if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$loop_fc.&amp;quot;_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) eq ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$loop_fc_next.&amp;quot;_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;)) {\&lt;br /&gt;
      fhem(&amp;quot;deletereading $NAME fc&amp;quot;.$loop_fc_next.&amp;quot;_.*&amp;quot;);;\&lt;br /&gt;
    }\&lt;br /&gt;
  } else {\&lt;br /&gt;
      fhem(&amp;quot;deletereading $NAME fc1_.*&amp;quot;);;\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
ReadingsTimestamp(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
}&lt;br /&gt;
attr EVU_Tibber_connect verbose 0&lt;br /&gt;
attr EVU_Tibber_connect ws_minInterval 54&lt;br /&gt;
attr EVU_Tibber_connect ws_websocketURL wss:websocket-api.tibber.com:443/v1-beta/gql/subscriptions&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== EVU_Tibber RAW Device ====&lt;br /&gt;
Das Device dient der Anzeige und Steuerung des EVU_Tibber_connect Devices.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod EVU_Tibber DOIF ## Startup Befehle für den WebSocket vom EVU_Tibber_connect\&lt;br /&gt;
init\&lt;br /&gt;
{ \&lt;br /&gt;
  Log(0, &amp;quot;$SELF 0   init       : ▶️  EVU_Tibber init&amp;quot;);;\&lt;br /&gt;
  fhem(&amp;quot;setreading EVU_Tibber_connect ws_cmd connect&amp;quot;);; \&lt;br /&gt;
  if (AttrVal(&amp;quot;$SELF&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
      Log 3, &amp;quot;$SELF 0   WebSocket  : ▶️  EVU_Tibber_connect start Websocket&amp;quot;;;\&lt;br /&gt;
  }\&lt;br /&gt;
  Log(0, &amp;quot;$SELF 0   init       : 🏁 EVU_Tibber init done&amp;quot;);;\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
################################################################################################################\&lt;br /&gt;
## EVU_Tibber_connect start/stop WebSocket\&lt;br /&gt;
EVU_Tibber_connect_ws\&lt;br /&gt;
{if( !([$SELF:state] eq &amp;quot;off&amp;quot;)                                           ## DOIF enabled\&lt;br /&gt;
#    or  [$SELF:ui_command_2]                                             ## Hier wird das uiTable select ausgewertet\&lt;br /&gt;
    and [$SELF:ui_command_2] ne &amp;quot;---&amp;quot;\&lt;br /&gt;
   ) {\&lt;br /&gt;
\&lt;br /&gt;
    fhem(&amp;quot;setreading EVU_Tibber_connect ws_cmd &amp;quot;.[?$SELF:ui_command_2]);; \&lt;br /&gt;
\&lt;br /&gt;
    set_Reading(&amp;quot;ui_command_2&amp;quot;,&amp;quot;---&amp;quot;);;                                   ## Hier wird das uiTable select wieder zurückgesetzt, ansonsten\&lt;br /&gt;
                                                                         ## kann das Kommando nicht sofort wiederholt werden\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
################################################################################################################\&lt;br /&gt;
## 1 Scheduling für das Abholen von Tibber Daten\&lt;br /&gt;
1_EVU_Tibber_PriceInfo\&lt;br /&gt;
{if( !([$SELF:state] eq &amp;quot;off&amp;quot;)                                           ## DOIF enabled\&lt;br /&gt;
    and\&lt;br /&gt;
    (     [:03]                                                          ## Kurz nach jeder vollen Stunde\&lt;br /&gt;
    )\&lt;br /&gt;
    or [$SELF:ui_command_1] eq &amp;quot;1_EVU_Tibber_PriceInfo&amp;quot;                  ## Hier wird das uiTable select ausgewertet\&lt;br /&gt;
   ) {\&lt;br /&gt;
\&lt;br /&gt;
  ::CommandGet(undef, &amp;quot;EVU_Tibber_connect 01_priceInfo&amp;quot;);;                ## Preis für die aktuelle Stunde\&lt;br /&gt;
  ::CommandGet(undef, &amp;quot;EVU_Tibber_connect 03_consumption_hour&amp;quot;);;         ## Kosten der letzen drei Stunden\&lt;br /&gt;
  if (AttrVal(&amp;quot;$SELF&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
      Log 3, &amp;quot;$SELF 1   PriceInfo  : Abfrage von Tibber&amp;quot;;;\&lt;br /&gt;
    }\&lt;br /&gt;
\&lt;br /&gt;
    set_Reading(&amp;quot;ui_command_1&amp;quot;,&amp;quot;---&amp;quot;);;                                   ## Hier wird das uiTable select wieder zurückgesetzt, ansonsten\&lt;br /&gt;
                                                                         ## kann das Kommando nicht sofort wiederholt werden\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
################################################################################################################\&lt;br /&gt;
## 2 Scheduling für das Abholen der Tibber Preise\&lt;br /&gt;
2_EVU_Tibber_PriceAll\&lt;br /&gt;
{if( !([$SELF:state] eq &amp;quot;off&amp;quot;)                                           ## DOIF enabled\&lt;br /&gt;
    and\&lt;br /&gt;
    ([00:03] or [14:03]                                                  ## Ab 14:00 Uhr gibt es die Preise für den nächsten Tag\&lt;br /&gt;
    )\&lt;br /&gt;
    or [$SELF:ui_command_1] eq &amp;quot;2_EVU_Tibber_PriceAll&amp;quot;                   ## Hier wird das uiTable select ausgewertet\&lt;br /&gt;
   ) {\&lt;br /&gt;
\&lt;br /&gt;
  ::CommandGet(undef, &amp;quot;EVU_Tibber_connect 02_priceAll&amp;quot;);;\&lt;br /&gt;
  if (AttrVal(&amp;quot;$SELF&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
      Log 3, &amp;quot;$SELF 2   priceAll   : Abfrage von Tibber für den nächsten Tag&amp;quot;;;\&lt;br /&gt;
    }\&lt;br /&gt;
\&lt;br /&gt;
    set_Reading(&amp;quot;ui_command_1&amp;quot;,&amp;quot;---&amp;quot;);;                                   ## Hier wird das uiTable select wieder zurückgesetzt, ansonsten\&lt;br /&gt;
                                                                         ## kann das Kommando nicht sofort wiederholt werden\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
################################################################################################################\&lt;br /&gt;
## 2 Erstellen des Diagramms im uiTable\&lt;br /&gt;
3_EVU_Tibber_Diagramm\&lt;br /&gt;
{if( !([$SELF:state] eq &amp;quot;off&amp;quot;)                                           ## DOIF enabled\&lt;br /&gt;
    and\&lt;br /&gt;
    (     [00:05] or [14:05]                                             ## Kurz nach Mitternacht\&lt;br /&gt;
    )\&lt;br /&gt;
    or [$SELF:ui_command_1] eq &amp;quot;3_EVU_Tibber_Diagramm&amp;quot;                   ## Hier wird das uiTable select ausgewertet\&lt;br /&gt;
   ) {\&lt;br /&gt;
\&lt;br /&gt;
  my (@out) = (&amp;quot;&amp;quot;) x 2;;\&lt;br /&gt;
  my $timestamp;;\&lt;br /&gt;
\&lt;br /&gt;
  for (my $j=0;;$j&amp;lt;=1;;$j++){\&lt;br /&gt;
\&lt;br /&gt;
    if (ReadingsVal(&amp;quot;EVU_Tibber_connect&amp;quot;,sprintf(&amp;quot;fc%d_00_startsAt&amp;quot;,$j),&amp;quot;null&amp;quot;) eq &amp;quot;null&amp;quot;) {       ## Der nächste Tag ist noch nicht da\&lt;br /&gt;
      if (AttrVal(&amp;quot;$SELF&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
        Log 3, &amp;quot;$SELF 3   Diagramm   : Tibber Daten für fc&amp;quot;.$j.&amp;quot; sind noch nicht da&amp;quot;;;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
      $timestamp = POSIX::strftime(&amp;quot;%Y-%m-%d&amp;quot;,localtime(time+86400));;    ## Setze das Datum auf morgen\&lt;br /&gt;
      for (my $k=0;;$k&amp;lt;=24;;$k++) {\&lt;br /&gt;
        $out[$j] .= sprintf(&amp;quot;%s_%02d:00:00 0.0\n&amp;quot;, $timestamp, $k);;      ## Die Daten mit 0 ergänzen\&lt;br /&gt;
      }\&lt;br /&gt;
      $j = 2;;                                                            ## Aus der Schleife springen\&lt;br /&gt;
\&lt;br /&gt;
    } else {\&lt;br /&gt;
\&lt;br /&gt;
      for (my $i=0;;$i&amp;lt;=23;;$i++){\&lt;br /&gt;
        $timestamp = ReadingsVal(&amp;quot;EVU_Tibber_connect&amp;quot;,sprintf(&amp;quot;fc%d_%02d_startsAt&amp;quot;,$j,$i),&amp;quot;&amp;quot;);;\&lt;br /&gt;
        $timestamp =~ s/ /_/g;;\&lt;br /&gt;
        $out[$j] .=$timestamp.&amp;quot; &amp;quot;.::round(ReadingsVal(&amp;quot;EVU_Tibber_connect&amp;quot;,sprintf(&amp;quot;fc%d_%02d_total&amp;quot;,$j, $i),0)*100,1).&amp;quot;\n&amp;quot;;;\&lt;br /&gt;
      } # End $i\&lt;br /&gt;
\&lt;br /&gt;
    } # End if\&lt;br /&gt;
  } # End $j\&lt;br /&gt;
  \&lt;br /&gt;
  if (AttrVal(&amp;quot;$SELF&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
      Log 3, &amp;quot;$SELF 3   Diagramm   : Werte für das Diagramm&amp;quot;;;\&lt;br /&gt;
      print($out[0]);;\&lt;br /&gt;
      print($out[1]);;\&lt;br /&gt;
    }\&lt;br /&gt;
  ## Die readings current_price und current_level dienen hier nur als Trigger Events, und müssen für\&lt;br /&gt;
  ## jedes Diagramm unterschiedlich sein, die Daten werden über $out[] bereit gestellt !!\&lt;br /&gt;
  ::DOIF_modify_card_data(&amp;quot;EVU_Tibber&amp;quot;,&amp;quot;EVU_Tibber_connect&amp;quot;,&amp;quot;current_price&amp;quot;,&amp;quot;bar1day&amp;quot;,0,$out[0]);;\&lt;br /&gt;
  ::DOIF_modify_card_data(&amp;quot;EVU_Tibber&amp;quot;,&amp;quot;EVU_Tibber_connect&amp;quot;,&amp;quot;current_level&amp;quot;,&amp;quot;bar1day&amp;quot;,-86400,$out[1]);;\&lt;br /&gt;
  ## Mit der Abfrage 03_consumption_hour werden die Trigger erneut ausgelöst\&lt;br /&gt;
  ::CommandGet(undef, &amp;quot;EVU_Tibber_connect 01_priceInfo&amp;quot;);;                ## Kosten der letzen drei Stunden\&lt;br /&gt;
##  fhem(&amp;quot;get EVU_Tibber_connect 01_priceInfo&amp;quot;);;                           ## Kosten der letzen drei Stunden\&lt;br /&gt;
\&lt;br /&gt;
    set_Reading(&amp;quot;ui_command_1&amp;quot;,&amp;quot;---&amp;quot;);;                                   ## Hier wird das uiTable select wieder zurückgesetzt, ansonsten\&lt;br /&gt;
                                                                         ## kann das Kommando nicht sofort wiederholt werden\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
&lt;br /&gt;
attr EVU_Tibber DbLogExclude .*&lt;br /&gt;
attr EVU_Tibber comment Version 2023.12.06 13:00 \&lt;br /&gt;
Dieses Device benötigt EVU_Tibber_connect als Verbindung zu Tibber.&lt;br /&gt;
attr EVU_Tibber disable 0&lt;br /&gt;
attr EVU_Tibber group PV Steuerung EVU&lt;br /&gt;
attr EVU_Tibber icon stromzaehler_icon&lt;br /&gt;
attr EVU_Tibber room Strom-&amp;gt;Boerse&lt;br /&gt;
attr EVU_Tibber sortby 315&lt;br /&gt;
attr EVU_Tibber uiState {\&lt;br /&gt;
package ui_Table;;\&lt;br /&gt;
  $TABLE = &amp;quot;style=&#039;width:100%;;&#039;&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
  $TD{0..6}{0} = &amp;quot;style=&#039;border-top-style:solid;;border-bottom-style:solid;;border-left-style:solid;;border-left-width:2px;;border-right-style:solid;;border-color:darkgreen;;border-top-width:2px;;border-bottom-width:2px;;border-right-width:1px;;width:36%;;font-weight:bold;;&#039;&amp;quot;;;\&lt;br /&gt;
  $TD{0..6}{1..4} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
  $TD{0..6}{5} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
&amp;quot;Kommando&amp;lt;dd&amp;gt;Auswahl&amp;lt;/dd&amp;gt;&amp;quot; |widget([$SELF:ui_command_1],&amp;quot;uzsuDropDown,---,1_EVU_Tibber_PriceInfo,2_EVU_Tibber_PriceAll,3_EVU_Tibber_Diagramm&amp;quot;) |&amp;quot;LiveMessurement &amp;lt;br&amp;gt;&amp;quot;.widget([$SELF:ui_command_2],&amp;quot;uzsuDropDown,---,connect,disconnect&amp;quot;)|&amp;quot;&amp;quot;|[EVU_Tibber_connect:ws_cmd]\&lt;br /&gt;
\&lt;br /&gt;
&amp;quot;Strompreis&amp;lt;br&amp;gt;&amp;quot;.card([EVU_Tibber_connect:current_price:bar1day],undef,undef,0,60,90,0,&amp;quot;fc0  &amp;quot;.::ReadingsVal(Device(),&amp;quot;current_currency&amp;quot;,&amp;quot;&amp;quot;),undef,&amp;quot;1&amp;quot;,&amp;quot;130,,,,,,220&amp;quot;).card([EVU_Tibber_connect:current_level:bar1day],undef,undef,0,60,90,0,&amp;quot;fc1  &amp;quot;.::ReadingsVal(Device(),&amp;quot;current_currency&amp;quot;,&amp;quot;&amp;quot;),undef,&amp;quot;1&amp;quot;,&amp;quot;130,,,,,,220&amp;quot;)|\&lt;br /&gt;
&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;nächste 3h&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;quot;.Price(1).&amp;quot;&amp;lt;br&amp;gt;&amp;quot;.Price(2).&amp;quot;&amp;lt;br&amp;gt;&amp;quot;.Price(3)|&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;Statistik fc0&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;quot;.::ReadingsVal(Device(),&amp;quot;fc_min&amp;quot;,0).&amp;quot; min&amp;lt;br&amp;gt;&amp;quot;.::ReadingsVal(Device(),&amp;quot;fc_avg&amp;quot;,0).&amp;quot; avg&amp;lt;br&amp;gt;&amp;quot;.::ReadingsVal(Device(),&amp;quot;fc_med&amp;quot;,0).&amp;quot; med&amp;lt;br&amp;gt;&amp;quot;.::ReadingsVal(Device(),&amp;quot;fc_max&amp;quot;,0).&amp;quot; max&amp;quot;|\&lt;br /&gt;
&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;Trigger fc0&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;Basis &amp;quot;.widget([EVU_Tibber_connect:compensation_grid],&amp;quot;selectnumbers,0,0.1,12,1,lin&amp;quot;).&amp;quot;&amp;lt;br&amp;gt;&amp;quot;.Format(&amp;quot;trigger_0&amp;quot;)|\&lt;br /&gt;
&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;Trigger fc1&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;quot;.Format(&amp;quot;trigger_1&amp;quot;)&lt;br /&gt;
attr EVU_Tibber uiTable {\&lt;br /&gt;
package ui_Table;;\&lt;br /&gt;
  $TABLE = &amp;quot;style=&#039;width:100%;;&#039;&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
   $TD{0..18}{0} = &amp;quot;style=&#039;border-top-style:solid;;border-bottom-style:solid;;border-left-style:solid;;border-left-width:2px;;border-right-style:solid;;border-color:darkgreen;;border-top-width:2px;;border-bottom-width:2px;;border-right-width:1px;;width:36%;;font-weight:bold;;&#039;&amp;quot;;;\&lt;br /&gt;
  $TD{0..18}{1..5} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
  $TD{0..18}{6} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
sub FUNC_Status {\&lt;br /&gt;
    my($value, $min, $colorMin,  $statusMin,  $colorMiddel, $statusMiddle, $max, $colorMax, $statusMax)=@_;;\&lt;br /&gt;
    my $ret = ($value &amp;lt; $min)? &#039;&amp;lt;span style=&amp;quot;color:&#039;.$colorMin.&#039;&amp;quot;&amp;gt;&#039;.$statusMin.&#039;&amp;lt;/span&amp;gt;&#039; : ($value &amp;gt; $max)? &#039;&amp;lt;span style=&amp;quot;color:&#039;.$colorMax.&#039;&amp;quot;&amp;gt;&#039;.$statusMax.&#039;&amp;lt;/span&amp;gt;&#039; : &#039;&amp;lt;span style=&amp;quot;color:&#039;.$colorMiddel.&#039;&amp;quot;&amp;gt;&#039;.$statusMiddle.&#039;&amp;lt;/span&amp;gt;&#039;;;\&lt;br /&gt;
    return $ret;;\&lt;br /&gt;
  }\&lt;br /&gt;
\&lt;br /&gt;
sub Device {\&lt;br /&gt;
    return &amp;quot;$SELF&amp;quot;.&amp;quot;_connect&amp;quot;;;\&lt;br /&gt;
  }\&lt;br /&gt;
\&lt;br /&gt;
sub Cost {\&lt;br /&gt;
  my($i)=@_;;\&lt;br /&gt;
  my $currency = (::ReadingsVal(Device(),&amp;quot;current_currency&amp;quot;,&amp;quot;&amp;quot;) eq &amp;quot;EUR&amp;quot;)? &amp;quot; €&amp;quot; : &amp;quot;&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
  return ::ReadingsVal(Device(),&amp;quot;total_cost_&amp;quot;.$i,0).$currency;;\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
sub Price {\&lt;br /&gt;
  my($i)=@_;;\&lt;br /&gt;
  my $j;;\&lt;br /&gt;
  my $value;;\&lt;br /&gt;
  my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
\&lt;br /&gt;
  if ($i == 0) {\&lt;br /&gt;
    $value = ::ReadingsVal(Device(),&amp;quot;current_price&amp;quot;,0);;\&lt;br /&gt;
    $value = ( ::ReadingsVal(Device(),&amp;quot;fc0_trigger&amp;quot;,&amp;quot;off&amp;quot;) eq &amp;quot;on&amp;quot; ) ?\&lt;br /&gt;
         &amp;quot;&amp;lt;span style=&#039;color:green&#039;&amp;gt;&amp;quot;.$value.&amp;quot;&amp;lt;/span&amp;gt;&amp;quot; :\&lt;br /&gt;
         &amp;quot;&amp;lt;span style=&#039;color:red&#039;&amp;gt;&amp;quot;.$value.&amp;quot;&amp;lt;/span&amp;gt;&amp;quot; ;;\&lt;br /&gt;
    $value .= &amp;quot; ct/kWh&amp;quot;;;\&lt;br /&gt;
  } else {\&lt;br /&gt;
    $j       = $i+$hour;;\&lt;br /&gt;
    if ($j &amp;lt; 24) {\&lt;br /&gt;
      $value = ::round(::ReadingsVal(Device(),&amp;quot;fc0_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$j).&amp;quot;_total&amp;quot;,0)*100,1);;\&lt;br /&gt;
    } else {\&lt;br /&gt;
      $j = $j - 24;;\&lt;br /&gt;
      $value = ::round(::ReadingsVal(Device(),&amp;quot;fc1_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$j).&amp;quot;_total&amp;quot;,0)*100,1);;\&lt;br /&gt;
    }\&lt;br /&gt;
    $value = ( ::ReadingsVal(Device(),&amp;quot;fc_trigger_price&amp;quot;,&amp;quot;0&amp;quot;) &amp;gt; $value ) ?\&lt;br /&gt;
         &amp;quot;&amp;lt;span style=&#039;color:green&#039;&amp;gt;&amp;quot;.$value.&amp;quot;&amp;lt;/span&amp;gt;&amp;quot; :\&lt;br /&gt;
         &amp;quot;&amp;lt;span style=&#039;color:red&#039;&amp;gt;&amp;quot;.$value.&amp;quot;&amp;lt;/span&amp;gt;&amp;quot; ;;\&lt;br /&gt;
    $value = sprintf(&amp;quot;%02d&amp;quot;,$j).&amp;quot; :  &amp;quot;.$value ;;\&lt;br /&gt;
  }\&lt;br /&gt;
  return  $value;;\&lt;br /&gt;
 }\&lt;br /&gt;
\&lt;br /&gt;
sub Format {\&lt;br /&gt;
  my($i)=@_;;\&lt;br /&gt;
\&lt;br /&gt;
  my $MonthBefore   = &amp;quot;LogDBRep_Statistic_previous_Month&amp;quot;;;\&lt;br /&gt;
  my $MonthPrevious = ::ReadingsTimestamp(&amp;quot;$MonthBefore&amp;quot;,&amp;quot;EVU_Tibber_Pulse_nodes_consumption_month&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
       $MonthPrevious = ($MonthPrevious ne &amp;quot;null&amp;quot;) ?    POSIX::strftime(&amp;quot;%Y&amp;quot;,localtime(::time_str2num(::ReadingsTimestamp(&amp;quot;$MonthBefore&amp;quot;,&amp;quot;EVU_Tibber_Pulse_nodes_consumption_month&amp;quot;,&amp;quot;null&amp;quot;)))) : &amp;quot;null&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
  my $YearBefore   = &amp;quot;LogDBRep_Statistic_previous_Year&amp;quot;;;\&lt;br /&gt;
  my $YearPrevious = ::ReadingsTimestamp(&amp;quot;$YearBefore&amp;quot;,Device().&amp;quot;_nodes_consumption_year&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
       $YearPrevious = ($YearPrevious ne &amp;quot;null&amp;quot;) ? POSIX::strftime(&amp;quot;%Y&amp;quot;,localtime(::time_str2num(::ReadingsTimestamp(&amp;quot;$YearBefore&amp;quot;,Device().&amp;quot;_nodes_consumption_year&amp;quot;,&amp;quot;null&amp;quot;)))) : &amp;quot;null&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
  if ($i eq &amp;quot;day&amp;quot;) {\&lt;br /&gt;
      return sprintf(&amp;quot;%04d&amp;quot;,::ReadingsVal(Device(),&amp;quot;nodes_consumption_day&amp;quot;,0));;\&lt;br /&gt;
    } elsif ($i eq &amp;quot;month&amp;quot;) {\&lt;br /&gt;
      my $evu_em = sprintf(&amp;quot;%04d&amp;quot;,::ReadingsVal(Device(),&amp;quot;nodes_consumption_month&amp;quot;,0));;\&lt;br /&gt;
      $evu_em .= ($MonthPrevious ne &amp;quot;null&amp;quot;) ? sprintf(&amp;quot; / %04d&amp;quot;, ::ReadingsVal(&amp;quot;$MonthBefore&amp;quot;,&amp;quot;EVU_Tibber_Pulse_nodes_consumption_month&amp;quot;,0) ) : &amp;quot;&amp;quot;;;\&lt;br /&gt;
      return $evu_em;;\&lt;br /&gt;
    } elsif ($i eq &amp;quot;year&amp;quot;) {\&lt;br /&gt;
      my $evu_ey = sprintf(&amp;quot;%04d&amp;quot;,::ReadingsVal(Device(),&amp;quot;nodes_consumption_year&amp;quot;,0));;\&lt;br /&gt;
      $evu_ey .= ($YearPrevious ne &amp;quot;null&amp;quot;) ? sprintf(&amp;quot; / %04d&amp;quot;, ::ReadingsVal(&amp;quot;$YearBefore&amp;quot;,&amp;quot;EVU_Tibber_Pulse_nodes_consumption_month&amp;quot;,0) ) : &amp;quot;&amp;quot;;;\&lt;br /&gt;
      return $evu_ey;;\&lt;br /&gt;
    } elsif ($i eq &amp;quot;trigger_0&amp;quot;) {\&lt;br /&gt;
      return ((::ReadingsVal(Device(),&amp;quot;fc0_trigger_start&amp;quot;,&amp;quot;&amp;quot;) eq &amp;quot;null&amp;quot; ) ?\&lt;br /&gt;
            &amp;quot;&amp;lt;span style=&#039;color:red&#039;&amp;gt;Heute kein Trigger &amp;lt;br&amp;gt;mehr unter &amp;quot;.\&lt;br /&gt;
                 ::ReadingsVal(Device(),&amp;quot;fc_trigger_price&amp;quot;,&amp;quot;null&amp;quot;).&amp;quot; ct&amp;lt;/span&amp;gt;&amp;quot; :\&lt;br /&gt;
            &amp;quot;&amp;lt;span style=&#039;color:green&#039;&amp;gt;Trigger von&amp;lt;br&amp;gt;&amp;quot;.\&lt;br /&gt;
                 ::ReadingsVal(Device(),&amp;quot;fc0_trigger_start&amp;quot;,&amp;quot;00:00&amp;quot;).&amp;quot; bis &amp;quot;.::ReadingsVal(Device(),&amp;quot;fc0_trigger_stop&amp;quot;,&amp;quot;00:00&amp;quot;).&amp;quot;&amp;lt;br&amp;gt;unter &amp;quot;.\&lt;br /&gt;
                 ::ReadingsVal(Device(),&amp;quot;fc_trigger_price&amp;quot;,&amp;quot;null&amp;quot;).&amp;quot; ct&amp;lt;/span&amp;gt;&amp;quot; );;\&lt;br /&gt;
    } elsif ($i eq &amp;quot;trigger_1&amp;quot;) {\&lt;br /&gt;
      if (::ReadingsVal(Device(),&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {\&lt;br /&gt;
        return ((::ReadingsVal(Device(),&amp;quot;fc1_trigger_start&amp;quot;,&amp;quot;null&amp;quot;) eq &amp;quot;null&amp;quot; ) ?\&lt;br /&gt;
              &amp;quot;&amp;lt;span style=&#039;color:red&#039;&amp;gt;Morgen kein Trigger &amp;lt;br&amp;gt;mehr unter &amp;quot;.\&lt;br /&gt;
                   ::ReadingsVal(Device(),&amp;quot;fc_trigger_price&amp;quot;,&amp;quot;null&amp;quot;).&amp;quot; ct&amp;lt;/span&amp;gt;&amp;quot; :\&lt;br /&gt;
              &amp;quot;&amp;lt;span style=&#039;color:green&#039;&amp;gt;Morgen ein Trigger von&amp;lt;br&amp;gt;&amp;quot;.\&lt;br /&gt;
                   ::ReadingsVal(Device(),&amp;quot;fc1_trigger_start&amp;quot;,&amp;quot;00:00&amp;quot;).&amp;quot; bis &amp;quot;.::ReadingsVal(Device(),&amp;quot;fc1_trigger_stop&amp;quot;,&amp;quot;00:00&amp;quot;).&amp;quot;&amp;lt;br&amp;gt;unter &amp;quot;.\&lt;br /&gt;
                   ::ReadingsVal(Device(),&amp;quot;fc_trigger_price&amp;quot;,&amp;quot;null&amp;quot;).&amp;quot; ct&amp;lt;/span&amp;gt;&amp;quot; );;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return &amp;quot;Morgen noch kein Trigger vorhanden&amp;quot;;;\&lt;br /&gt;
      }\&lt;br /&gt;
    }\&lt;br /&gt;
  return &amp;quot;null&amp;quot;;;\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
&amp;quot;Statistiken &amp;quot;.::ReadingsVal(Device(),&amp;quot;current_date&amp;quot;,0).&amp;quot; in kWh&amp;quot;|&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;aktuell&amp;lt;/span&amp;gt;&amp;quot;|&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;heute&amp;lt;/span&amp;gt;&amp;quot;|&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;Monat&amp;lt;/span&amp;gt;&amp;quot;|&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;Jahr&amp;lt;/span&amp;gt;&amp;quot;\&lt;br /&gt;
\&lt;br /&gt;
&amp;quot;Strom&amp;lt;dd&amp;gt;Preis / Kosten&amp;lt;/dd&amp;gt;&amp;quot;|Price(0)|Cost(&amp;quot;day&amp;quot;)|Cost(&amp;quot;month&amp;quot;)|Cost(&amp;quot;year&amp;quot;)\&lt;br /&gt;
\&lt;br /&gt;
## Wenn man das liveMessurment von Tibber verwendet\&lt;br /&gt;
&amp;quot;Bezug vom Netz&amp;quot;|\&lt;br /&gt;
sprintf(&amp;quot;%04d W&amp;quot;,([EVU_Tibber_connect:payload_data_liveMeasurement_power]  &amp;gt;= 0 ? ::ReadingsVal(&amp;quot;EVU_Tibber_connect&amp;quot;,&amp;quot;payload_data_liveMeasurement_power&amp;quot;,0) : 0))|\&lt;br /&gt;
Format(&amp;quot;day&amp;quot;)|Format(&amp;quot;month&amp;quot;)|Format(&amp;quot;year&amp;quot;)\&lt;br /&gt;
\&lt;br /&gt;
&amp;quot;Einspeisung ins Netz&amp;quot;|\&lt;br /&gt;
sprintf(&amp;quot;%04d W&amp;quot;,([EVU_Tibber_connect:payload_data_liveMeasurement_powerProduction]  &amp;gt;= 0 ? ::ReadingsVal(&amp;quot;EVU_Tibber_connect&amp;quot;,&amp;quot;payload_data_liveMeasurement_powerProduction&amp;quot;,0) : 0))|\&lt;br /&gt;
&amp;quot;&amp;quot;|&amp;quot;&amp;quot;|&amp;quot;&amp;quot;\&lt;br /&gt;
\&lt;br /&gt;
## Wenn man einen eigenes SmartMeter verwendet\&lt;br /&gt;
## &amp;quot;Bezug vom Netz&amp;quot;|sprintf(&amp;quot;%04d W&amp;quot;,([WR_0_KSEM:M_AC_Power] &amp;gt;= 0 ? ::round(::ReadingsVal(&amp;quot;WR_0_KSEM&amp;quot;,&amp;quot;M_AC_Power&amp;quot;,0),0) : 0) )|Format(&amp;quot;day&amp;quot;)|Format(&amp;quot;month&amp;quot;)|Format(&amp;quot;year&amp;quot;)\&lt;br /&gt;
## &amp;quot;Einspeisung ins Netz&amp;quot;|sprintf(&amp;quot;%04d W&amp;quot;,([WR_0_KSEM:M_AC_Power] &amp;lt;= 0 ? abs(::round(::ReadingsVal(&amp;quot;WR_0_KSEM&amp;quot;,&amp;quot;M_AC_Power&amp;quot;,0),0)) :  0) )|&amp;quot;&amp;quot;|&amp;quot;&amp;quot;|&amp;quot;&amp;quot;&lt;br /&gt;
attr EVU_Tibber verbose 3&lt;br /&gt;
&lt;br /&gt;
setstate EVU_Tibber 2024-01-23 10:03:00 ui_command_1 ---&lt;br /&gt;
setstate EVU_Tibber 2024-01-20 17:40:53 ui_command_2 ---&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== LogDBRep_EVU_Tibber_connect_SQL RAW Device ====&lt;br /&gt;
Dieses DbRep Device dient der Verbindung zur MySQL DbLog. Die SQL SELECT Statements werden im EVU_Tibber_connect Device generiert und über dieses Device in der MySQL DbLog ausgeführt. Der Name des Devices wird ebenfalls generiert und liegt somit fest.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod LogDBRep_EVU_Tibber_connect_SQL DbRep LogDB&lt;br /&gt;
attr LogDBRep_EVU_Tibber_connect_SQL DbLogExclude .*&lt;br /&gt;
attr LogDBRep_EVU_Tibber_connect_SQL room System&lt;br /&gt;
attr LogDBRep_EVU_Tibber_connect_SQL sqlCmdHistoryLength 5&lt;br /&gt;
attr LogDBRep_EVU_Tibber_connect_SQL timeout 20&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 2. aWATTar ==&lt;br /&gt;
=== Vorausetzungen ===&lt;br /&gt;
* FHEM mit einer installierten MySQL DbLog (FileLog wird nicht unterstützt)&lt;br /&gt;
* Für die Berechnung der tatsächlichen Kosten benötigt man seine lokalen Fixkosten, die je nach Region unterschiedlich sind.&lt;br /&gt;
* Die bisherige Berechnung mit Stand 20240123 wäre wie folgt: round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
&lt;br /&gt;
=== EVU_aWATTar_connect ===&lt;br /&gt;
Das EVU_aWATTar_connect fragt mit HTTPMOD die aWATTar API Webseite ab. Es werden keine Live Verbrauchdaten geliefert.&lt;br /&gt;
&lt;br /&gt;
==== Verwendete Module ====&lt;br /&gt;
* HTTPMOD&lt;br /&gt;
* DbLog&lt;br /&gt;
* DbRep&lt;br /&gt;
&lt;br /&gt;
==== EVU_aWATTar_connect  RAW Device ====&lt;br /&gt;
Momentan fragt das EVU_aWattar_connect lediglich die aktuellen Preise des Tages ab. Die readings sind an die des EVU_Tibber_connect angelehnt, um im weiteren Verlauf eventuell ein gemeinsames Device für die Anzeige und Steuerung zu erarbeiten.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod EVU_aWATTar_connect HTTPMOD https://api.awattar.de/v1/marketdata/current.yaml 0&lt;br /&gt;
attr EVU_aWATTar_connect DbLogExclude .*&lt;br /&gt;
attr EVU_aWATTar_connect comment Version 2024.01.23 13:00\&lt;br /&gt;
\&lt;br /&gt;
Achtung, momentan werden nur die Börsenpreise ohne die fix Kosten dargestellt.\&lt;br /&gt;
\&lt;br /&gt;
https://api.awattar.de/v1/marketdata&lt;br /&gt;
attr EVU_aWATTar_connect enableControlSet 1&lt;br /&gt;
attr EVU_aWATTar_connect get01-15Name fc_max&lt;br /&gt;
attr EVU_aWATTar_connect get01-15OExpr round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
attr EVU_aWATTar_connect get01-1Name current_date&lt;br /&gt;
attr EVU_aWATTar_connect get01-22Name fc_med&lt;br /&gt;
attr EVU_aWATTar_connect get01-22OExpr round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
attr EVU_aWATTar_connect get01-29Name fc_avg&lt;br /&gt;
attr EVU_aWATTar_connect get01-29OExpr round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
attr EVU_aWATTar_connect get01-36Name current_price&lt;br /&gt;
attr EVU_aWATTar_connect get01-36OExpr round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
attr EVU_aWATTar_connect get01-8Name fc_min&lt;br /&gt;
attr EVU_aWATTar_connect get01-8OExpr round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
attr EVU_aWATTar_connect get01Name 01_priceInfo&lt;br /&gt;
attr EVU_aWATTar_connect get01RegOpt g&lt;br /&gt;
attr EVU_aWATTar_connect get01Regex date_now: (\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2})|price_low: ([0-9]+[0-9\.]+)|price_high: ([0-9]+[0-9\.]+)|price_median: ([0-9]+[0-9\.]+)|price_average: ([0-9]+[0-9\.]+)|price_current: ([0-9]+[0-9\.]+)&lt;br /&gt;
attr EVU_aWATTar_connect get01URL https://api.awattar.de/v1/marketdata/current.yaml&lt;br /&gt;
attr EVU_aWATTar_connect get02-10Name fc0_09_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-10OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-11Name fc0_10_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-11OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-12Name fc0_11_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-12OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-13Name fc0_12_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-13OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-14Name fc0_13_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-14OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-15Name fc0_14_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-15OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-16Name fc0_15_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-16OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-17Name fc0_16_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-17OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-18Name fc0_17_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-18OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-19Name fc0_18_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-19OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-1Name fc0_00_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-1OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-20Name fc0_19_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-20OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-21Name fc0_20_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-21OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-22Name fc0_21_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-22OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-23Name fc0_22_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-23OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-24Name fc0_23_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-24OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-2Name fc0_01_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-2OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-3Name fc0_02_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-3OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-4Name fc0_03_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-4OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-5Name fc0_04_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-5OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-6Name fc0_05_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-6OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-7Name fc0_06_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-7OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-8Name fc0_07_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-8OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-9Name fc0_08_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-9OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02FollowGet 01_priceInfo&lt;br /&gt;
attr EVU_aWATTar_connect get02Name 02_priceDay&lt;br /&gt;
attr EVU_aWATTar_connect get02RegOpt g&lt;br /&gt;
attr EVU_aWATTar_connect get02Regex (?&amp;lt;=abs_[0-9]{2}_amount: )([0-9]+[0-9\.]+)&lt;br /&gt;
attr EVU_aWATTar_connect get02URL https://api.awattar.de/v1/marketdata/current.yaml&lt;br /&gt;
attr EVU_aWATTar_connect get03-1Name fc3_00_startsAt&lt;br /&gt;
attr EVU_aWATTar_connect get03-1OExpr POSIX::strftime(&amp;quot;%Y-%m-%d %H:%M:%S&amp;quot;,localtime(substr($val,0,10)))&lt;br /&gt;
attr EVU_aWATTar_connect get03-4Name fc3_00_total&lt;br /&gt;
attr EVU_aWATTar_connect get03-4OExpr round($val/1000,4)&lt;br /&gt;
attr EVU_aWATTar_connect get03Name 02_priceAll&lt;br /&gt;
attr EVU_aWATTar_connect get03RegOpt g&lt;br /&gt;
attr EVU_aWATTar_connect get03Regex &amp;quot;start_timestamp&amp;quot;: (\d{10})|&amp;quot;marketprice&amp;quot;: (\d*\.\d*)&lt;br /&gt;
attr EVU_aWATTar_connect get03URL https://api.awattar.de/v1/marketdata?start=1701990000000&amp;amp;end=1702159200000&lt;br /&gt;
attr EVU_aWATTar_connect group PV Steuerung EVU&lt;br /&gt;
attr EVU_aWATTar_connect icon sani_pump&lt;br /&gt;
attr EVU_aWATTar_connect room Strom-&amp;gt;Boerse&lt;br /&gt;
attr EVU_aWATTar_connect sortby 213&lt;br /&gt;
attr EVU_aWATTar_connect userReadings fc_trigger_price:fc_avg.* {\&lt;br /&gt;
## fc_trigger_price:[fc_avg|compensation_grid].* {\\&lt;br /&gt;
  my $fc_avg = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_avg&amp;quot;,0);;\&lt;br /&gt;
  my $fc_min = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_min&amp;quot;,0);;\&lt;br /&gt;
\&lt;br /&gt;
  # Berechnung eines Default Schwellwertes als täglicher Niedrigpreis\&lt;br /&gt;
  my $price_level = round( ($fc_avg - $fc_min)/2 + $fc_min , 1);;\&lt;br /&gt;
 \&lt;br /&gt;
  # Abschätzung von Wirtschaftlichkeit beim Speicher Laden, falls Tibber zu teuer wird\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;compensation_grid&amp;quot;,0) != 0 ) {\&lt;br /&gt;
    my $price_level_battery = round( ($fc_avg - ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;compensation_grid&amp;quot;,0)) *0.85 , 1) ;;\&lt;br /&gt;
    if ( $price_level &amp;gt; $price_level_battery ) {\&lt;br /&gt;
      $price_level = $price_level_battery;;\&lt;br /&gt;
    }\&lt;br /&gt;
  }\&lt;br /&gt;
  $price_level;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_start:fc_trigger_price.* {\&lt;br /&gt;
  my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
  my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for (my $loop_hour = $hour;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour)) ;;\&lt;br /&gt;
      }\&lt;br /&gt;
    } # end  for loop_hour\&lt;br /&gt;
\&lt;br /&gt;
  return(&amp;quot;null&amp;quot;);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_stop:fc0_trigger_start.* {\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
  my $loop_hour = 0;;\&lt;br /&gt;
  my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my $fc_trigger_start = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_trigger_start&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  my $fc_trigger_stop = $fc_trigger_start;;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $fc_trigger_start ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    $fc_trigger_start =~ /(\d\d):/;; $fc_trigger_start = $1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for ($loop_hour = $fc_trigger_start;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$fc.&amp;quot;_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        $fc_trigger_stop = sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour) ;;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour));;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
     # wechsel zum nächsten Tag\&lt;br /&gt;
     if ( $loop_hour == 23 and $fc == 0 ) {\&lt;br /&gt;
       $fc = 1;;\&lt;br /&gt;
       $loop_hour = -1;;\&lt;br /&gt;
     }\&lt;br /&gt;
\&lt;br /&gt;
    } # end for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
 \&lt;br /&gt;
  return($fc_trigger_stop);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger:fc0_trigger_stop.* {\&lt;br /&gt;
\&lt;br /&gt;
  # Setzen des Triggers für die aktuelle Stunde\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;current_price&amp;quot;,100)  &amp;lt; ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) ) {\&lt;br /&gt;
    return(&amp;quot;on&amp;quot;)\&lt;br /&gt;
  } else {\&lt;br /&gt;
    return(&amp;quot;off&amp;quot;)\&lt;br /&gt;
  }\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_DbLog:fc0_00_total.* {\&lt;br /&gt;
my ($timestamp,$date,$hour,$value,$loop_fc_next) = 5x0;;\&lt;br /&gt;
\&lt;br /&gt;
for (my $loop_fc = 0;; $loop_fc &amp;lt;= 1;; $loop_fc++) {\&lt;br /&gt;
  $loop_fc_next = $loop_fc +1;;\&lt;br /&gt;
  $date = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;current_date&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  if ($date ne &amp;quot;null&amp;quot;) {\&lt;br /&gt;
    $date =~ /([\d+-]+)/;; $date = $1 ;;\&lt;br /&gt;
    for (my $loop_hour = 0;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $hour = sprintf(&amp;quot;%02d&amp;quot;,$loop_hour);;\&lt;br /&gt;
      $timestamp = $date.&amp;quot; &amp;quot;.$hour.&amp;quot;:00:00&amp;quot;;;\&lt;br /&gt;
      $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$loop_fc.&amp;quot;_&amp;quot;.$hour.&amp;quot;_total&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
      ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
                        INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                          VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;fc&amp;quot;.$loop_fc.&amp;quot;_total&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
                        ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                          VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
    }\&lt;br /&gt;
  } else {\&lt;br /&gt;
      fhem(&amp;quot;deletereading $NAME fc1_.*&amp;quot;);;\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;current_date&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
}&lt;br /&gt;
attr EVU_aWATTar_connect verbose 0&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== LogDBRep_EVU_aWATTar_connect_SQL RAW Device ====&lt;br /&gt;
Dieses DbRep Device dient der Verbindung zur MySQL DbLog. Die SQL SELECT Statements werden im EVU_Tibber_connect Device generiert und über dieses Device in der MySQL DbLog ausgeführt. Der Name des Devices wird ebenfalls generiert und liegt somit fest.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod LogDBRep_EVU_aWATTar_connect_SQL DbRep LogDB&lt;br /&gt;
attr LogDBRep_EVU_aWATTar_connect_SQL DbLogExclude .*&lt;br /&gt;
attr LogDBRep_EVU_aWATTar_connect_SQL room System&lt;br /&gt;
attr LogDBRep_EVU_aWATTar_connect_SQL sqlCmdHistoryLength 5&lt;br /&gt;
attr LogDBRep_EVU_aWATTar_connect_SQL timeout 20&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 3. EPEX_Spot ==&lt;br /&gt;
=== Vorausetzungen ===&lt;br /&gt;
httpmod&lt;br /&gt;
&lt;br /&gt;
==== EPEX_Spot  RAW Device ====&lt;br /&gt;
Mit diesem device erhält man die viertelstündlichen Börsenpreise für heute (&#039;EPEX_prices_day&#039;), für morgen (&#039;EPEX_prices_dayahead&#039;) und den aktuellen Preis (&#039;EPEX_price&#039;). &lt;br /&gt;
Die dayahead Preise werden in der Regel gegen 14:00 am Vortag veröffentlicht.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod EPEX_Boersenpreise HTTPMOD https://api.energy-charts.info/price?bzn=DE-LUtimerange 900&lt;br /&gt;
attr EPEX_Boersenpreise alignTime 00:00:30&lt;br /&gt;
attr EPEX_Boersenpreise comment This device queries prices for electrical energy from https://api.energy-charts.info/price?bzn=DE-LU.\&lt;br /&gt;
The quarter-hour day and dayahead prices are given as lists of 96 elements each, the first price element being valid for the first quarter-hour 00:00 to 00:15, and so on.\&lt;br /&gt;
All prices are in €/MWh&lt;br /&gt;
attr EPEX_Boersenpreise get02JSON price&lt;br /&gt;
attr EPEX_Boersenpreise get02Name EPEX_prices_day&lt;br /&gt;
attr EPEX_Boersenpreise get02RecombineExpr join &amp;quot;,&amp;quot;, @matchlist&lt;br /&gt;
attr EPEX_Boersenpreise get02Replacement01Value {&amp;quot;&amp;quot;}&lt;br /&gt;
attr EPEX_Boersenpreise get03JSON price&lt;br /&gt;
attr EPEX_Boersenpreise get03Name EPEX_prices_dayahead&lt;br /&gt;
attr EPEX_Boersenpreise get03RecombineExpr join &amp;quot;,&amp;quot;, @matchlist&lt;br /&gt;
attr EPEX_Boersenpreise get03Replacement01Value {fhem(&amp;quot;setreading $name EPEX_prices_dayahead not yet published&amp;quot;);;;;my ($sec, $min, $hour, $mday, $month, $year) = localtime();;;;my $start = 86400+int(fhemTimeLocal(0,0,0, $mday, $month, $year));;;;&amp;quot;&amp;amp;start=&amp;quot;.$start.&amp;quot;&amp;amp;end=&amp;quot;.($start+86399)}&lt;br /&gt;
attr EPEX_Boersenpreise reading01JSON price&lt;br /&gt;
attr EPEX_Boersenpreise reading01Name EPEX_price&lt;br /&gt;
attr EPEX_Boersenpreise replacement01Mode expression&lt;br /&gt;
attr EPEX_Boersenpreise replacement01Regex timerange&lt;br /&gt;
attr EPEX_Boersenpreise replacement01Value {my ($sec, $min, $hour, $mday, $month, $year) = localtime();;;;&amp;quot;&amp;amp;start=&amp;quot;.900 * (int(fhemTimeLocal(0, $min, $hour, $mday, $month, $year) / 900))}&lt;br /&gt;
attr EPEX_Boersenpreise stateFormat EPEX_price €/MWh&lt;br /&gt;
attr EPEX_Boersenpreise verbose 2&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Weder get02Poll noch get03Poll sollten auf 1 gesetzt sein, da es dann zu gelegentlichen timeout-Fehlern kommt. Davon abgesehen ist es soowieso überflüssig, die sich nur einmal täglich ändernden &#039;prices_day&#039; und &#039;prices_dayahead&#039; Wert zu oft auszulesen.&lt;br /&gt;
Folgendes &#039;at&#039; sorgt dafür, dass beide Preis_Listen nur 1x pro Tag gelesen werden:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
define get_EPEX_prices at *00:03:00 get EPEX_Boersenpreise EPEX_prices_day;;setreading EPEX_Boersenpreise EPEX_prices_dayahead not yet published;;define get_EPEX_prices_dayahead at 15:03 get EPEX_Boersenpreise EPEX_prices_dayahead&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Dieses &#039;at&#039; liest täglich kurz nach Mitternacht die &#039;day&#039; Preise und erzeugt ein einmal-&#039;at&#039;, das kurz nach 15:00 die &#039;dayahead&#039; Preise liest.&lt;br /&gt;
&lt;br /&gt;
== Links ==&lt;br /&gt;
* Website (deutsch) des Anbieters [https://tibber.com/de Tibber]&lt;br /&gt;
* Website (deutsch) des Anbieters [https://www.awattar.de/ aWATTar]&lt;/div&gt;</summary>
		<author><name>Ph1959de</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=Benutzer_Diskussion:Gary&amp;diff=40435</id>
		<title>Benutzer Diskussion:Gary</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=Benutzer_Diskussion:Gary&amp;diff=40435"/>
		<updated>2025-10-07T15:47:09Z</updated>

		<summary type="html">&lt;p&gt;Ph1959de: Willkommen&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Willkommen! ==&lt;br /&gt;
{| width=&amp;quot;100%&amp;quot; cellspacing=&amp;quot;0&amp;quot; cellpadding=&amp;quot;6&amp;quot; style=&amp;quot;line-height: 20px; background: #E0E0E0; border: 2px solid #1874CD;&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| colspan=&amp;quot;4&amp;quot; style=&amp;quot;background:#1874CD;&amp;quot; |&amp;lt;big&amp;gt;&amp;lt;span style=&amp;quot;color: #FAFAFA&amp;quot;&amp;gt;&#039;&#039;&#039;Hallo Gary,&#039;&#039;&#039; willkommen im FHEM Wiki!&amp;lt;/span&amp;gt;&amp;lt;/big&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| colspan=&amp;quot;4&amp;quot; | Danke für dein Interesse an unserem Projekt, ich freue mich schon auf deine weiteren Beiträge. Die folgenden Seiten sollten dir die ersten Schritte erleichtern, bitte nimm dir daher etwas Zeit, sie zu lesen.&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&#039;&#039;&#039;FHEM-spezifische Informationen&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| width=&amp;quot;8%&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| width=&amp;quot;38%&amp;quot; | &#039;&#039;&#039;[[Systemübersicht]]&#039;&#039;&#039;&amp;lt;br /&amp;gt;&#039;&#039;FHEM Systemübersicht&#039;&#039;&lt;br /&gt;
| width=&amp;quot;8%&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| width=&amp;quot;38%&amp;quot; | &#039;&#039;&#039;[[FHEMWiki:Über FHEMWiki]]&#039;&#039;&#039;&amp;lt;br /&amp;gt;&#039;&#039;Informationen über dieses Wiki&#039;&#039;&lt;br /&gt;
&amp;lt;!-- Abschnitt auf Kommentar gesetzt&lt;br /&gt;
|-&lt;br /&gt;
| colspan=&amp;quot;4&amp;quot; |&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
{{Todo|FHEM-spezifische Anleitungen und Regeln.}}&lt;br /&gt;
&lt;br /&gt;
---- &lt;br /&gt;
 Ende von &#039;Abschnitt auf Kommentar gesetzt&#039; --&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| colspan=&amp;quot;4&amp;quot; | &lt;br /&gt;
----&lt;br /&gt;
&#039;&#039;&#039;Generelle Informationen über (Media)Wikis&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| width=&amp;quot;8%&amp;quot; | [[Datei:Crystal Clear app kedit.svg|rechts|30px|link=Hilfe:Bearbeiten]]&lt;br /&gt;
| width=&amp;quot;38%&amp;quot; | &#039;&#039;&#039;[[Hilfe:Bearbeiten]]&#039;&#039;&#039;&amp;lt;br /&amp;gt;&#039;&#039;Zugang zu allen wichtigen Informationen.&#039;&#039;&lt;br /&gt;
| width=&amp;quot;8%&amp;quot; | [[Datei:X-office-presentation.svg|rechts|30px|link=Wikipedia:Tutorial]]&lt;br /&gt;
| width=&amp;quot;38%&amp;quot; | &amp;lt;!-- &#039;&#039;&#039;[[Wikipedia:Tutorial]]&#039;&#039;&#039;--&amp;gt;&#039;&#039;&#039;[http://de.wikipedia.org/wiki/Wikipedia:Tutorial Wikipedia:Tutorial]&#039;&#039;&#039;&amp;lt;br /&amp;gt;&#039;&#039;Schritt-für-Schritt-Anleitung für Einsteiger.&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Applications-system.svg|rechts|30px|link=Wikipedia:Grundprinzipien]]&lt;br /&gt;
| &#039;&#039;&#039;&amp;lt;!--[[Wikipedia:Grundprinzipien]]--&amp;gt;[http://de.wikipedia.org/wiki/Wikipedia:Grundprinzipien Wikipedia:Grundprinzipien]&#039;&#039;&#039;&amp;lt;br /&amp;gt;&#039;&#039;Die grundlegende Philosophie unseres Projekts.&#039;&#039;&lt;br /&gt;
| [[Datei:MentorenProgrammLogo-7.svg|rechts|60px|link=Wikipedia:Mentorenprogramm]]&lt;br /&gt;
| &#039;&#039;&#039;&amp;lt;!--[[Wikipedia:Mentorenprogramm]]--&amp;gt;[http://de.wikipedia.org/wiki/Wikipedia:Mentorenprogramm Wikipedia:Mentorenprogramm]&#039;&#039;&#039;&amp;lt;br /&amp;gt;&#039;&#039;Persönliche Einführung in die Beteiligung bei Wikipedia.&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| colspan=&amp;quot;4&amp;quot; |&lt;br /&gt;
----&lt;br /&gt;
Bitte beachte, &amp;lt;!--[[Wikipedia:Was Wikipedia nicht ist|was Wikipedia nicht ist]]--&amp;gt;[http://de.wikipedia.org/wiki/Wikipedia:Was_Wikipedia_nicht_ist was Wikipedia nicht ist], und &amp;quot;unterschreibe&amp;quot; deine Diskussionsbeiträge durch Eingabe von &amp;lt;code&amp;gt;--&amp;lt;nowiki&amp;gt;~~~~&amp;lt;/nowiki&amp;gt;&amp;lt;/code&amp;gt; oder durch Drücken der Schaltfläche [[Datei:button_sig.png|Signaturknopf|20px|link=Hilfe:Signatur]] über dem Bearbeitungsfeld. Artikel werden jedoch nicht unterschrieben, und wofür die Zusammenfassungszeile da ist, erfährst du unter &amp;lt;!--[[wikipedia:Hilfe:Zusammenfassung und Quellen|Hilfe:Zusammenfassung und Quellen]]--&amp;gt;[http://de.wikipedia.org/wiki/Hilfe:Zusammenfassung_und_Quellen Zusammenfassung und Quellen]. &amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
[[Datei:Nuvola apps ksirc.png|25px|link=Benutzer Diskussion:Ph1959de]] &amp;amp;nbsp;&amp;amp;nbsp; &#039;&#039;&#039;Hast du Fragen an mich?&#039;&#039;&#039; Schreib mir auf [[Benutzer Diskussion:Ph1959de|&amp;lt;u&amp;gt;meiner&amp;lt;/u&amp;gt; Diskussionsseite]]! Viele Grüße, [[Benutzer:Ph1959de|Peter]] ([[Benutzer Diskussion:Ph1959de|Diskussion]]) 17:46, 7. Okt. 2025 (CEST)&lt;br /&gt;
|}&lt;/div&gt;</summary>
		<author><name>Ph1959de</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=FHEMWiki:Interna&amp;diff=40391</id>
		<title>FHEMWiki:Interna</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=FHEMWiki:Interna&amp;diff=40391"/>
		<updated>2025-09-21T17:44:20Z</updated>

		<summary type="html">&lt;p&gt;Ph1959de: Update MediaWiki&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Randnotiz|RNTyp=info|RNText=&amp;quot;Infobox Interna&amp;quot; zu FHEM Wiki&lt;br /&gt;
* [[Spezial:Version|Versionsinfo]]&lt;br /&gt;
* [[Spezial:Statistik|Statistisches]]&lt;br /&gt;
}}&lt;br /&gt;
[[FHEMWiki:Interna]] wie Informationen über geplante Wartungsarbeiten, Probleme, Erweiterungswünsche etc.&lt;br /&gt;
&lt;br /&gt;
== Anfragen, Ideen, Todo ==&lt;br /&gt;
* Suche im Wiki &amp;quot;case-&#039;&#039;&#039;in&#039;&#039;&#039;-sensitive machen ({{Link2Forum|Topic=123428}})&amp;lt;/br&amp;gt;MediaWiki Links:&lt;br /&gt;
** https://www.mediawiki.org/wiki/Extension:MixedNamespaceSearchSuggestions&lt;br /&gt;
** https://www.mediawiki.org/wiki/Manual:Enabling_autocomplete_in_search_box&lt;br /&gt;
* ...&lt;br /&gt;
&lt;br /&gt;
== Aktuell ==&lt;br /&gt;
&lt;br /&gt;
== September 2025 ==&lt;br /&gt;
* MediaWiki auf Version 1.39.13 angehoben&lt;br /&gt;
&lt;br /&gt;
== Dezember 2024 ==&lt;br /&gt;
* MediaWiki auf Version 1.39.10 angehoben&lt;br /&gt;
&lt;br /&gt;
== April 2024 ==&lt;br /&gt;
Umstellung des FHEMWiki-Standardskin (&amp;quot;Style&amp;quot;) von &#039;Vector alt (2010)&#039; auf &#039;Vector (2022)&#039; um eine bessere Benutzbarkeit auf mobilen Geräten bei nicht registrierten Benutzern zu erreichen. Registrierte Benutzer können unter Einstellungen/Aussehen den Skin selbst ändern. {{Link2Forum|Topic=137399|Diskussion dazu im Forum}}&lt;br /&gt;
== März 2023 ==&lt;br /&gt;
* Umzug auf neuen Server steht an&lt;br /&gt;
* dabei wird auch MediaWiki auf die aktuelle Version angehoben&lt;br /&gt;
&lt;br /&gt;
== April 2021 ==&lt;br /&gt;
* 5.4.: MediaWiki wurde auf Version 1.35.1 aktualisiert&lt;br /&gt;
:* als Vorbereitung dafür: Upgrade von PHP 7.3.11 auf PHP 7.3.27&lt;br /&gt;
:* Extension &amp;quot;UserMerge&amp;quot; installiert (relevant nur für Wiki Admins)&lt;br /&gt;
&lt;br /&gt;
== September 2018 ==&lt;br /&gt;
* 9.9.: MediaWiki wurde auf Version 1.31.0 aktualisiert&lt;br /&gt;
&lt;br /&gt;
== Januar 2017 ==&lt;br /&gt;
* MediaWiki wurde auf Version 1.28.0 aktualisiert&lt;br /&gt;
&lt;br /&gt;
== Dezember 2016 ==&lt;br /&gt;
* Wiki wurde auf einen anderen Server und gleichzeitig auf die Domain wiki.fhem.de umgezogen&lt;br /&gt;
* MediaWiki wurde vorerst auf Version 1.24.1 belassen; Update auf aktuelle Version ist vorgesehen, sobald sich das Wiki in der neuen Umgebung eingespielt hat&lt;br /&gt;
&lt;br /&gt;
== Umstellung vom 6.11.2015 ==&lt;br /&gt;
* Einige &amp;quot;Standarddateien&amp;quot; (aus wikimedia:common) scheinen verschwunden zu sein; dadurch fehlen aktuell einige Bilder, insbesondere in den Vorlagen &amp;quot;Hallo&amp;quot; und &amp;quot;Randnotiz&amp;quot;. Arno wurde per Mail informiert.&lt;br /&gt;
&lt;br /&gt;
== Umstellung vom 30.10.2015 ==&lt;br /&gt;
* [[Syntax Highlighting]] wurde auf mehrfachen Wunsch eingerichtet.&lt;br /&gt;
&lt;br /&gt;
== Umstellung vom 10.2.2015 ==&lt;br /&gt;
Am 10.2.2015 wurde der Wiki-Server auf eine leistungsfähigere Plattform umgezogen. &lt;br /&gt;
&lt;br /&gt;
Gleichzeitig wurde die MediaWiki Version auf 1.24.1 aktualisiert.&lt;br /&gt;
&lt;br /&gt;
=== Offene Probleme ===&lt;br /&gt;
Probleme sind mittlerweile abgearbeitet, Wünsche bearbeitet (abgelehnt oder erfüllt)&lt;br /&gt;
&lt;br /&gt;
=== Erledigte Probleme ===&lt;br /&gt;
* Kopfbereich aller Wiki-Seiten hat (noch) die falsche Hintergrundfarbe&lt;br /&gt;
** erledigt: [[Benutzer:Akw|Akw]] ([[Benutzer Diskussion:Akw|Diskussion]]) 17:27, 24. Feb. 2015 (CET)&lt;br /&gt;
* &#039;&#039;wikitable sortable&#039;&#039; funktioniert nicht (mehr)&lt;br /&gt;
** erledigt: --[[Benutzer:Akw|Akw]] ([[Benutzer Diskussion:Akw|Diskussion]]) 22:37, 19. Apr. 2015 (CEST)&lt;br /&gt;
* Bearbeitungs- und erweiterte Bearbeitungswerkzeugleiste sind trotz Auswahl in Benutzereinstellung nicht vorhanden (getestete Browser: Firefox, IE)&lt;br /&gt;
** erledigt: --[[Benutzer:Akw|Akw]] ([[Benutzer Diskussion:Akw|Diskussion]]) 22:37, 19. Apr. 2015 (CEST)&lt;br /&gt;
* Suchvorschlagsliste bei Eingabe des Suchwortes im Suchfeld nicht mehr vorhanden&lt;br /&gt;
** erledigt: --[[Benutzer:Krikan|Christian]] ([[Benutzer Diskussion:Krikan|Diskussion]]) 20:23, 22. Apr. 2015 (CEST)&lt;br /&gt;
* Darstellung ist &amp;quot;anders&amp;quot; (z.B.: Überschriften auf Level 2 (==) jetzt Serifenschrift)&lt;br /&gt;
* (Wunsch:) Wäre es möglich, die Extension [http://www.mediawiki.org/wiki/Extension:DynamicWikiSitemap DynamicWikiSitemap] zu installieren?&lt;br /&gt;
** Die funktioniert nicht gut mit dem nginx-webserver. Ich habe statische sitemaps für die Google Webmaster Tools erstellt: http://wiki.fhem.de/w/sitemap/sitemap-index-c1fhemwiki.xml [[Benutzer:Akw|Akw]] ([[Benutzer Diskussion:Akw|Diskussion]]) 17:27, 24. Feb. 2015 (CET)&lt;br /&gt;
* Benutzereinstellungen (Spezial:Einstellungen): die Seite bietet keine &amp;quot;Themes&amp;quot; (mehr?) an; es ist &amp;quot;nur noch&amp;quot; &#039;&#039;&#039;Benutzeroberfläche&#039;&#039;&#039; &#039;&#039;Vector&#039;&#039; installiert.&lt;br /&gt;
:: Das ist so gewollt. ([[Benutzer Diskussion:Akw|Diskussion]]) 17:27, 24. Feb. 2015 (CET)&lt;br /&gt;
* Bug? Wenn man direkt ein Kapitel einer Seite bearbeitet, bleibt die Schaltfläche &amp;quot;nur Kleinigkeiten wurden verändert&amp;quot; ohne Wirkung. &lt;br /&gt;
&lt;br /&gt;
=== Diskussion === &lt;br /&gt;
:: Es gibt aber allem Anschein nach noch ein offenes Problem mit diesem einzigen verbliebenen Skin: ich bekomme auf allen FHEM-Wiki Seiten (sichtbar &amp;quot;nur&amp;quot; in Firebug) die Fehlermeldung &#039;&#039;&amp;lt;nowiki&amp;gt;&amp;quot;NetworkError: 502 Bad Gateway - http://wiki.fhem.de/w/load.php?debug=false&amp;amp;lang=de&amp;amp;modules=startup&amp;amp;only=scripts&amp;amp;skin=vector&amp;amp;*&amp;quot;&amp;lt;/nowiki&amp;gt;&#039;&#039;. Nach einer Info, die ich an anderer Stelle gefunden habe, könnte auch die fehlende Werkzeugleiste damit zusammenhängen. &lt;br /&gt;
:: Falls die Frage erlaubt ist: was ist der Grund dafür, dass nur noch ein Skin &amp;quot;gewollt&amp;quot; ist? --[[Benutzer:Ph1959de|Peter]] ([[Benutzer Diskussion:Ph1959de|Diskussion]]) 10:56, 2. Apr. 2015 (CEST)&lt;br /&gt;
:: &amp;lt;hr /&amp;gt;&lt;br /&gt;
:: Ich hab den Bug jetzt im Griff, denke ich. Es paar Puffergrößen mussten angepasst werden. --[[Benutzer:Akw|Akw]] ([[Benutzer Diskussion:Akw|Diskussion]]) 22:35, 19. Apr. 2015 (CEST)&lt;br /&gt;
:: @Peter: Wenn es zwingend gewünscht wird, lasse ich mich mit den Skins breitschlagen, aber Vector ist doch ganz hübsch oder? Und der Wartungsaufwand wird minimiert.&lt;br /&gt;
[[Kategorie:FHEM Wiki]]&lt;br /&gt;
::: Ich brauch jedenfalls keine Skins.[[Benutzer:Soulman|Soulman]] ([[Benutzer Diskussion:Soulman|Diskussion]]) 03:47, 26. Apr. 2015 (CEST)&lt;br /&gt;
:: &amp;lt;hr /&amp;gt;&lt;br /&gt;
:: Ich bin neu hier und daher hoffentlich an der richtigen Stelle: Unterstützt die MediaWiki-Software eine Umschaltung der Inhaltssprache? Idealerweise gäbe es zu jedem Artikel ein englisches Äquivalent, das angezeigt wird, wenn der Nutzer englisch eingestellt hat. Aber geht das im FHEMWiki und wenn ja, wie? [[Benutzer:Rollo Wikinger|Rollo Wikinger]] ([[Benutzer Diskussion:Rollo Wikinger|Diskussion]]) 21:35, 5. Jan. 2020 (CET)&lt;br /&gt;
::: &amp;lt;hr /&amp;gt;&lt;br /&gt;
::: Hallo &amp;quot;Rollo&amp;quot;, &lt;br /&gt;
::: nein, die Mehrsprachigkeit ist nicht implementiert / installiert (bin mir nicht sicher, was dafür alles erforderlich wäre. Ist bisher bei einigen wenigen Seiten &amp;quot;manuell&amp;quot; gelöst, indem an den Seitentitel ein &amp;quot;/en&amp;quot; (siehe z.B. [[Quick-Start/en]]) angehängt ist. Am Anfang der Seite dann jeweils ein Verweis auf die andere(n) Sprachversionen. --[[Benutzer:Ph1959de|Peter]] ([[Benutzer Diskussion:Ph1959de|Diskussion]]) 18:54, 27. Dez. 2020 (CET)&lt;/div&gt;</summary>
		<author><name>Ph1959de</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=Kategorie:Akustische_Ausgabe&amp;diff=40357</id>
		<title>Kategorie:Akustische Ausgabe</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=Kategorie:Akustische_Ausgabe&amp;diff=40357"/>
		<updated>2025-09-05T17:04:19Z</updated>

		<summary type="html">&lt;p&gt;Ph1959de: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Kategorie:Kommunikationswege]]&lt;/div&gt;</summary>
		<author><name>Ph1959de</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=Benutzer_Diskussion:Parallix&amp;diff=40336</id>
		<title>Benutzer Diskussion:Parallix</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=Benutzer_Diskussion:Parallix&amp;diff=40336"/>
		<updated>2025-08-21T08:11:30Z</updated>

		<summary type="html">&lt;p&gt;Ph1959de: Willkommen&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Willkommen! ==&lt;br /&gt;
{| width=&amp;quot;100%&amp;quot; cellspacing=&amp;quot;0&amp;quot; cellpadding=&amp;quot;6&amp;quot; style=&amp;quot;line-height: 20px; background: #E0E0E0; border: 2px solid #1874CD;&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| colspan=&amp;quot;4&amp;quot; style=&amp;quot;background:#1874CD;&amp;quot; |&amp;lt;big&amp;gt;&amp;lt;span style=&amp;quot;color: #FAFAFA&amp;quot;&amp;gt;&#039;&#039;&#039;Hallo Parallix,&#039;&#039;&#039; willkommen im FHEM Wiki!&amp;lt;/span&amp;gt;&amp;lt;/big&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| colspan=&amp;quot;4&amp;quot; | Danke für dein Interesse an unserem Projekt, ich freue mich schon auf deine weiteren Beiträge. Die folgenden Seiten sollten dir die ersten Schritte erleichtern, bitte nimm dir daher etwas Zeit, sie zu lesen.&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&#039;&#039;&#039;FHEM-spezifische Informationen&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| width=&amp;quot;8%&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| width=&amp;quot;38%&amp;quot; | &#039;&#039;&#039;[[Systemübersicht]]&#039;&#039;&#039;&amp;lt;br /&amp;gt;&#039;&#039;FHEM Systemübersicht&#039;&#039;&lt;br /&gt;
| width=&amp;quot;8%&amp;quot; | &amp;amp;nbsp;&lt;br /&gt;
| width=&amp;quot;38%&amp;quot; | &#039;&#039;&#039;[[FHEMWiki:Über FHEMWiki]]&#039;&#039;&#039;&amp;lt;br /&amp;gt;&#039;&#039;Informationen über dieses Wiki&#039;&#039;&lt;br /&gt;
&amp;lt;!-- Abschnitt auf Kommentar gesetzt&lt;br /&gt;
|-&lt;br /&gt;
| colspan=&amp;quot;4&amp;quot; |&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
{{Todo|FHEM-spezifische Anleitungen und Regeln.}}&lt;br /&gt;
&lt;br /&gt;
---- &lt;br /&gt;
 Ende von &#039;Abschnitt auf Kommentar gesetzt&#039; --&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| colspan=&amp;quot;4&amp;quot; | &lt;br /&gt;
----&lt;br /&gt;
&#039;&#039;&#039;Generelle Informationen über (Media)Wikis&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| width=&amp;quot;8%&amp;quot; | [[Datei:Crystal Clear app kedit.svg|rechts|30px|link=Hilfe:Bearbeiten]]&lt;br /&gt;
| width=&amp;quot;38%&amp;quot; | &#039;&#039;&#039;[[Hilfe:Bearbeiten]]&#039;&#039;&#039;&amp;lt;br /&amp;gt;&#039;&#039;Zugang zu allen wichtigen Informationen.&#039;&#039;&lt;br /&gt;
| width=&amp;quot;8%&amp;quot; | [[Datei:X-office-presentation.svg|rechts|30px|link=Wikipedia:Tutorial]]&lt;br /&gt;
| width=&amp;quot;38%&amp;quot; | &amp;lt;!-- &#039;&#039;&#039;[[Wikipedia:Tutorial]]&#039;&#039;&#039;--&amp;gt;&#039;&#039;&#039;[http://de.wikipedia.org/wiki/Wikipedia:Tutorial Wikipedia:Tutorial]&#039;&#039;&#039;&amp;lt;br /&amp;gt;&#039;&#039;Schritt-für-Schritt-Anleitung für Einsteiger.&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Applications-system.svg|rechts|30px|link=Wikipedia:Grundprinzipien]]&lt;br /&gt;
| &#039;&#039;&#039;&amp;lt;!--[[Wikipedia:Grundprinzipien]]--&amp;gt;[http://de.wikipedia.org/wiki/Wikipedia:Grundprinzipien Wikipedia:Grundprinzipien]&#039;&#039;&#039;&amp;lt;br /&amp;gt;&#039;&#039;Die grundlegende Philosophie unseres Projekts.&#039;&#039;&lt;br /&gt;
| [[Datei:MentorenProgrammLogo-7.svg|rechts|60px|link=Wikipedia:Mentorenprogramm]]&lt;br /&gt;
| &#039;&#039;&#039;&amp;lt;!--[[Wikipedia:Mentorenprogramm]]--&amp;gt;[http://de.wikipedia.org/wiki/Wikipedia:Mentorenprogramm Wikipedia:Mentorenprogramm]&#039;&#039;&#039;&amp;lt;br /&amp;gt;&#039;&#039;Persönliche Einführung in die Beteiligung bei Wikipedia.&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| colspan=&amp;quot;4&amp;quot; |&lt;br /&gt;
----&lt;br /&gt;
Bitte beachte, &amp;lt;!--[[Wikipedia:Was Wikipedia nicht ist|was Wikipedia nicht ist]]--&amp;gt;[http://de.wikipedia.org/wiki/Wikipedia:Was_Wikipedia_nicht_ist was Wikipedia nicht ist], und &amp;quot;unterschreibe&amp;quot; deine Diskussionsbeiträge durch Eingabe von &amp;lt;code&amp;gt;--&amp;lt;nowiki&amp;gt;~~~~&amp;lt;/nowiki&amp;gt;&amp;lt;/code&amp;gt; oder durch Drücken der Schaltfläche [[Datei:button_sig.png|Signaturknopf|20px|link=Hilfe:Signatur]] über dem Bearbeitungsfeld. Artikel werden jedoch nicht unterschrieben, und wofür die Zusammenfassungszeile da ist, erfährst du unter &amp;lt;!--[[wikipedia:Hilfe:Zusammenfassung und Quellen|Hilfe:Zusammenfassung und Quellen]]--&amp;gt;[http://de.wikipedia.org/wiki/Hilfe:Zusammenfassung_und_Quellen Zusammenfassung und Quellen]. &amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
[[Datei:Nuvola apps ksirc.png|25px|link=Benutzer Diskussion:Ph1959de]] &amp;amp;nbsp;&amp;amp;nbsp; &#039;&#039;&#039;Hast du Fragen an mich?&#039;&#039;&#039; Schreib mir auf [[Benutzer Diskussion:Ph1959de|&amp;lt;u&amp;gt;meiner&amp;lt;/u&amp;gt; Diskussionsseite]]! Viele Grüße, [[Benutzer:Ph1959de|Peter]] ([[Benutzer Diskussion:Ph1959de|Diskussion]]) 10:11, 21. Aug. 2025 (CEST)&lt;br /&gt;
|}&lt;/div&gt;</summary>
		<author><name>Ph1959de</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=Diskussion:SmartVISU/lichtSzene&amp;diff=40236</id>
		<title>Diskussion:SmartVISU/lichtSzene</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=Diskussion:SmartVISU/lichtSzene&amp;diff=40236"/>
		<updated>2025-06-04T17:29:24Z</updated>

		<summary type="html">&lt;p&gt;Ph1959de: Die Seite wurde neu angelegt: „@wvhn: ich schlage vor, die HTML-Beispiele mit &amp;lt;nowiki&amp;gt;&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot;&amp;gt;&amp;lt;/nowiki&amp;gt; zu formatieren - das von Dir verwendete nowiki erscheint ziemlich aufwändig und fehleranfällig. Falls es &amp;quot;nur&amp;quot; darum ging, Zeilenumbrüche zu vermeiden / unterdrücken, lassen sich möglicherweise auch noch andere Wege finden. --~~~~“&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;@wvhn: ich schlage vor, die HTML-Beispiele mit &amp;lt;nowiki&amp;gt;&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot;&amp;gt;&amp;lt;/nowiki&amp;gt; zu formatieren - das von Dir verwendete nowiki erscheint ziemlich aufwändig und fehleranfällig. Falls es &amp;quot;nur&amp;quot; darum ging, Zeilenumbrüche zu vermeiden / unterdrücken, lassen sich möglicherweise auch noch andere Wege finden.&lt;br /&gt;
--[[Benutzer:Ph1959de|Peter]] ([[Benutzer Diskussion:Ph1959de|Diskussion]]) 19:29, 4. Jun. 2025 (CEST)&lt;/div&gt;</summary>
		<author><name>Ph1959de</name></author>
	</entry>
</feed>