Files

866 lines
33 KiB
Markdown

# Belevo Energiemanagement fuer IP-Symcon
Stand: 2026-05-10
Diese Dokumentation ist als Arbeitsgrundlage fuer ChatGPT/Codex und fuer zukuenftige Software-Erweiterungen gedacht. Sie beschreibt die Funktion, Architektur, Modul-Schnittstellen und die wichtigsten Regeln, um neue Module sauber an den Energiemanager anzubinden.
## 1. Kurzueberblick
Das Repository ist eine IP-Symcon Modulbibliothek fuer ein Energiemanagementsystem der Belevo AG. Das System verteilt elektrische Leistung dynamisch auf steuerbare Verbraucher wie Ladestationen, Boiler, Pufferspeicher, Batterie, Waermepumpe und externe Schaltgruppen.
Die zentrale Idee:
1. Der `Manager` liest den aktuellen Netzbezug.
2. Der `Manager` entscheidet zwischen Solarladen und Peak-Shaving.
3. Jeder Verbraucher meldet seine aktuell moeglichen Leistungsstufen als `PowerSteps`.
4. Der `Manager` verteilt die verfuegbare oder zu reduzierende Leistung nach Prioritaet und Fairness.
5. Die Verbraucher setzen die zugeteilte Leistung in konkrete Schaltkontakte, API-Aufrufe oder Geraetemodi um.
Das System ist fuer Einzelhaeuser, ZEV/V-ZEV-Anlagen und groessere Lastmanagement-Strukturen ausgelegt. Fuer verteilte Anlagen gibt es neben dem lokalen `Manager` den `HauptManager`, der mehrere untergeordnete Manager ueber JSON-Datenpunkte zusammenfassen kann.
## 2. Repository-Struktur
Jedes IP-Symcon Modul liegt in einem eigenen Ordner und enthaelt typischerweise:
- `module.php`: PHP-Klasse mit der eigentlichen Logik.
- `module.json`: IP-Symcon Modul-Metadaten, GUID, Name, Typ, Hersteller, Prefix.
- `form.json`: Konfigurationsformular fuer IP-Symcon.
- `README.md`: Modulbeschreibung, aktuell oft noch Template oder veraltet.
Wichtige Dateien auf Root-Ebene:
- `library.json`: Modulbibliothek, aktuell Version `2.001`, IP-Symcon-Kompatibilitaet `8.0`.
- `README.md`: vorhandene Systembeschreibung des Energiemanagers.
- `DOKUMENTATION_CHATGPT.md`: diese technische Gesamtuebersicht.
## 3. Modulgruppen
| Modul | Rolle | Manager-Anbindung |
| --- | --- | --- |
| `Manager` | Lokaler Energiemanager, verteilt Leistung an Verbraucher | Zentrale Instanz |
| `HauptManager` | Aggregiert mehrere Manager/Unteranlagen ueber JSON | Uebergeordnete Instanz |
| `Ladestation_v2` | Moderne Ladestationsanbindung mit Fahrzeugerkennung, Mindeststrom, 1p/3p, Easee/ECarUp/Smart-Me/Go-E | Verbraucher |
| `Ladestation_Universal` | Aeltere/einfachere Ladestation | Verbraucher |
| `Batterie` | Batterie mit Lade-/Entladeleistungssteuerung fuer Goodwe, Solaredge, Sig Energy | Verbraucher, bidirektional |
| `Boiler_2_Stufig_Mit_Fueler` | Boiler mit Teil- und Volllastkontakt, Temperatur- und Legionellenlogik | Verbraucher |
| `Boiler_x_Stufig` | Boiler mit frei konfigurierbaren Leistungsstufen | Verbraucher |
| `Ansteuerung_Askoheat` | Stufenlose/mehrstufige Askoheat-Ansteuerung ueber externe Leistungsvariable | Verbraucher |
| `Pufferspeicher` | Puffer mit Teil-/Volllastkontakt und Aussentemperatur-Funktion | Verbraucher |
| `Puffer_Speicher` | Puffer mit frei konfigurierbaren Leistungsstufen | Verbraucher |
| `Verbraucher_1_Stufig` | Einfacher Ein/Aus-Verbraucher mit Mindestlaufzeit/Nachtlogik | Verbraucher |
| `Verbraucher_Sperrbar` | Verbraucher, der im Peak-Fall gesperrt werden kann | Verbraucher |
| `Verbraucher_extern` | Buendelt externe Schaltverbraucher zu Leistungskombinationen | Verbraucher/Adapter |
| `WP_Steuerung` | Waermepumpe mit Sperr-/Erhoehungskontakten, Wetter-/Sonnenlogik | Verbraucher |
| `CC100_HW` | Hardwarezugriff auf digitale Ein-/Ausgaenge und PT-Fuehler | Hilfs-/Hardwaremodul |
| `Shelly_Parser_MQTT` | MQTT-Parser fuer Shelly-Geraete, legt Variablen dynamisch an | Hilfs-/Integrationsmodul |
| `Belevo_Server_Kommunikation` | Wetterdaten und Influx/Server-Kommunikation | Monitoring/Kommunikation |
| `Energy_Pie` | Visualisierung von Produktion, Netz, Einspeisung, Hausverbrauch | Visualisierung |
| `Belevo_Bezahl_Modul` | Experimentelles Google-Pay/Stripe HTML-Modul | Nebenmodul, nicht produktionsreif |
## 4. Grundarchitektur
### 4.1 Schichten
Die Software hat logisch vier Schichten:
1. Datenquellen:
- Netzbezug, Batterieladezustand, aktuelle Ladeleistung, Temperaturen, Schaltzustand, Wetterdaten.
- Meist als IP-Symcon Variablen konfiguriert.
2. Energiemanagement:
- `Manager` fuer lokale Anlagen.
- `HauptManager` fuer uebergeordnete Verteilung ueber mehrere Manager.
3. Verbraucheradapter:
- Module, die den gemeinsamen Verbraucher-Vertrag umsetzen.
- Beispiele: `Ladestation_v2`, `Batterie`, `Boiler_x_Stufig`.
4. Aktoren und externe Systeme:
- Schaltkontakte, Wallbox HTTP APIs, Cloud APIs, Wechselrichtervariablen, Shelly MQTT, CC100 Linux-Dateisystem.
### 4.2 Normaler lokaler Ablauf
Ein typischer Zyklus laeuft so:
1. `Manager` wird ueber `Timer_DistributeEnergy` aufgerufen.
2. `Manager` liest `Netzbezug`, `Peakleistung`, `Ueberschussleistung` und `Verbraucher_Liste`.
3. Der Modus wird bestimmt:
- Solarladen, wenn der Netzbezug unterhalb des Umschaltbereichs liegt.
- Peak-Shaving, wenn der Netzbezug ueber dem Umschaltbereich liegt.
4. Fuer jeden Verbraucher werden die EMS-Variablen gelesen:
- `Power`
- `Bezogene_Energie`
- `PV_Prio`
- `Sperre_Prio`
- `Idle`
- `PowerSteps`
- `Leistung_Delta`
5. Der Manager fordert per `IPS_RequestAction($verbraucher, "GetCurrentData", $Is_Peak_Shaving)` neue Daten an.
6. Wenn ein Verbraucher nicht `Idle` ist oder sich der Modus geaendert hat, bleiben alle aktuellen Leistungen im aktuellen Zyklus stehen.
7. Sonst sortiert der Manager Verbraucher nach Prioritaet und verteilt die Leistung.
8. Die Zuteilung wird ueber `IPS_RequestAction($verbraucher, "SetAktuelle_Leistung", $leistung)` an die Verbraucher gegeben.
9. Die Verbraucher setzen diese Vorgabe in ihrem eigenen `Do_UserCalc`-Timer um.
Wichtig: Viele Verbraucher setzen die Leistung nicht direkt im `RequestAction("SetAktuelle_Leistung")`, sondern speichern den Wert zuerst in der Variable `Power`. Die reale Umsetzung erfolgt im naechsten `Do_UserCalc`.
### 4.3 Modusentscheidung
Der lokale `Manager` kennt zwei Betriebsarten:
- `Is_Peak_Shaving = false`: Solarladen / Nutzung verfuegbarer Leistung.
- `Is_Peak_Shaving = true`: Peak-Shaving / Reduktion oder Begrenzung der Leistung.
Wenn `UmschaltpunktStatisch = false`, nutzt der Manager die Mitte zwischen `Peakleistung` und `Ueberschussleistung`:
```text
Schwellwert = (Peakleistung + Ueberschussleistung) / 2
Netzbezug < Schwellwert -> Solarladen
Netzbezug >= Schwellwert -> Peak-Shaving
```
Wenn `UmschaltpunktStatisch = true`, werden `Umschalt_Solarladen` und `Umschalt_Peakshaving` als Hysterese verwendet. Zwischen den beiden Werten bleibt der bisherige Modus erhalten.
### 4.4 Leistungsverteilung
Der Manager arbeitet mit diskreten Leistungsstufen. Jeder Verbraucher liefert ein JSON-Array `PowerSteps`, zum Beispiel:
```json
[0, 1500, 3000, 6000]
```
oder bei bidirektionalen Verbrauchern:
```json
[-3000, -1000, 0, 1000, 3000]
```
Die Verteilung laeuft prioritaetsweise:
1. Prioritaetsschluessel waehlen:
- Solarladen: `PV_Prio`
- Peak-Shaving: `Sperre_Prio`
2. Verbraucher sortieren:
- zuerst nach Prioritaet, niedriger Wert bedeutet hoeher priorisiert.
- bei gleicher Prioritaet nach `Bezogene_Energie / 2000`, damit weniger versorgte Verbraucher bevorzugt werden.
3. Verbraucher gleicher Prioritaet werden gruppiert.
4. Innerhalb einer Gruppe werden alle moeglichen Schritte sortiert.
5. Der Manager nimmt nur Schritte, die in die verbleibende Leistung passen.
6. Verbraucher, die nicht `0` annehmen koennen, werden gesondert behandelt.
`Leistung_Delta` korrigiert Soll-/Ist-Abweichungen. Das ist besonders wichtig bei Ladestationen, weil die effektive Ladeleistung von der Vorgabe abweichen kann.
## 5. Der gemeinsame Verbraucher-Vertrag
Jedes neue Modul, das vom `Manager` gesteuert werden soll, muss mindestens die folgenden Variablen und Actions anbieten.
### 5.1 Pflichtvariablen
| Ident | Typ | Bedeutung |
| --- | --- | --- |
| `Sperre_Prio` | Integer | Prioritaet im Peak-Shaving. Niedriger = wichtiger. |
| `PV_Prio` | Integer | Prioritaet im Solarladen. Niedriger = wichtiger. |
| `Idle` | Boolean | `true`, wenn das Modul fuer eine neue Leistungszuteilung bereit ist. |
| `Aktuelle_Leistung` | Integer/Float | Aktuell umgesetzte oder zuletzt gesetzte Leistung in W. |
| `Bezogene_Energie` | Float | Aufsummierte Energie fuer Fairness. Aktuell in W * h gerechnet, faktisch Wh. |
| `PowerSteps` | String | JSON-Array moeglicher Leistungsstufen in W. |
| `Power` | Integer/Float | Vom Manager gewuenschte Leistung, oft als Zwischenspeicher. |
| `Is_Peak_Shaving` | Boolean | Letzter vom Manager uebergebener Modus. |
| `Leistung_Delta` | Integer/Float | Abweichung zwischen Soll und Ist, damit der Manager die reale Leistung beruecksichtigen kann. |
| `IdleCounter` | Integer | Hilfszaehler bis `Idle` wieder `true` wird. |
### 5.2 Pflicht-Actions
| Action | Parameter | Aufgabe |
| --- | --- | --- |
| `GetCurrentData` | Boolean `Peak` | Aktuellen Modus speichern und `PowerSteps` neu berechnen. |
| `SetAktuelle_Leistung` | Integer/Float `power` | Neue Leistungsvorgabe entgegennehmen oder direkt umsetzen. |
| `Do_UserCalc` | leer | Interner zyklischer Modulschritt, setzt `Power` um und aktualisiert `PowerSteps`. |
### 5.3 Bedeutung der Vorzeichen
Im bestehenden Code gilt implizit:
- Positive Leistung: Verbraucher bezieht oder laedt Energie.
- `0`: Verbraucher aus oder neutral.
- Negative Leistung: Einspeisen, Entladen oder Gegenrichtung, aktuell vor allem fuer `Batterie` relevant.
Neue Module sollten diese Vorzeichenregel konsequent einhalten. Wenn ein Geraet physikalisch anders arbeitet, sollte der Adapter intern umrechnen, aber gegenueber dem Manager diese Semantik behalten.
### 5.4 Idle-Regel
`Idle = false` bedeutet: Das Modul hat gerade eine Aenderung bekommen oder wartet auf Mindestlaufzeiten, Hardware-Reaktion oder Stabilisierung. Der Manager soll in dieser Zeit keine neue Verteilung erzwingen.
Typisches Muster:
1. Wenn neue Leistung ungleich alter Leistung:
- `Idle = false`
- `IdleCounter = IdleCounterMax`
2. Pro Zyklus:
- `IdleCounter` dekrementieren.
- Wenn `IdleCounter == 0`, dann `Idle = true`.
Dieses Muster verhindert schnelles Takten von Relais, Wallboxen und Wechselrichtervorgaben.
## 6. Manager im Detail
### 6.1 `Manager`
Datei: `Manager/module.php`
Wichtige Properties:
- `Peakleistung`: Sollwert bzw. Obergrenze fuer Peak-Shaving in W.
- `Ueberschussleistung`: Sollwert fuer Solarladen in W.
- `Netzbezug`: IP-Symcon Variable mit aktuellem Netzbezug.
- `Verbraucher_Liste`: Liste der zu steuernden Verbraucher-Instanzen.
- `UmschaltpunktStatisch`: aktiviert feste Umschaltpunkte.
- `Umschalt_Solarladen`: untere Schwelle bei statischer Hysterese.
- `Umschalt_Peakshaving`: obere Schwelle bei statischer Hysterese.
- `HauptmanagerAktiv`: aktiviert Kopplung an `HauptManager`.
- `ManagerID`: ID des Untermanagers fuer externe Strukturen.
- `DatenHoch`: String-Variable, in die lokale Daten fuer den Hauptmanager geschrieben werden.
- `DatenZuruck`: String-Variable, aus der Zuweisungen des Hauptmanagers gelesen werden.
- `Interval`: Zykluszeit in Sekunden.
Wichtige Funktionen:
- `DistributeEnergy()`: lokale Verteilung.
- `DistributeEnergy_Extern()`: JSON-Austausch mit Hauptmanager.
- `RequestAction("DistributeEnergy")`: Einstieg des Timers.
### 6.2 `HauptManager`
Datei: `HauptManager/module.php`
Der `HauptManager` verbindet mehrere lokale Manager oder Teilanlagen. Er liest pro Unteranlage:
- eine JSON-Variable mit User-/Verbraucherdaten (`User_Up`)
- eine JSON-Variable fuer Rueckgabe/Zuweisung (`User_Down`)
Er aggregiert alle Verbraucher, summiert den Netzbezug, entscheidet global zwischen Solarladen und Peak-Shaving und schreibt pro Untermanager ein JSON mit finalen `Set_Leistung`-Werten zurueck.
JSON-Erwartung vom Untermanager:
```json
{
"User": [
{
"InstanceID": 12345,
"Aktuelle_Leistung": 0,
"Bezogene_Energie": 0,
"PV_Prio": 1,
"Sperre_Prio": 1,
"Idle": true,
"PowerSteps": [0, 1500, 3000],
"Leistung_Delta": 0
}
],
"Netzbezug": 0,
"Timestamp": 1710000000
}
```
Rueckgabe an Untermanager:
```json
{
"Timestamp": 1710000000,
"Is_Peak_Shaving": false,
"User": [
{
"InstanceID": 12345,
"Set_Leistung": 1500
}
]
}
```
Wenn Daten aelter als 30 Sekunden sind, setzt der Hauptmanager fuer diese Unteranlage eine sichere Rueckgabe mit leerer User-Liste.
## 7. Verbraucher-Module
### 7.1 Ladestationen
#### `Ladestation_v2`
Das wichtigste Ladestationsmodul. Es kann fachlich bereits viele Elemente eines spaeteren OCPP-orientierten Moduls abbilden, aber aktuell noch mit herstellerspezifischer Logik direkt im Modul.
Unterstuetzte Typen laut Formular:
- Go-E Wallbox alte Version
- Go-E Gemini/Gemini Flex
- Smart-Me Pico
- Dummy Station
- Easee nur Solarladen
- Easee
Zentrale Eigenschaften:
- `IP_Adresse`, `ID`, `Seriennummer`, `Username`, `Password`
- `Token_Easee`, `Token_ECarUp`
- `Max_Current_abs`
- `Ein_Zeit`, `Aus_Zeit`
- `Mindestaldestrom` als Variable mit Action
Zentrale Betriebsvariablen:
- `Ladebereit`: Freigabe.
- `Solarladen`: PV-Lademodus.
- `Car_detected`: Fahrzeug erkannt.
- `Car_is_full`: Fahrzeug voll.
- `Is_1_ph`: erkannter 1-phasiger Betrieb.
- `Max_Current`: intern berechneter Maximalstrom.
- `Fahrzeugstatus`: herstellerspezifischer Status.
- `Ladeleistung_Effektiv`: gemessene Ladeleistung.
- `Pending_Counter`: Zaehler fuer ausbleibende Ist-Leistung.
- `IsTimerActive`, `IsTimerActive_Null_Timer`: Mindestlaufzeit-/Null-Timer.
- `Letzer_User`: Benutzerbezug fuer Easee/ECarUp-Logik.
Wichtige Funktionen:
- `Detect_Car()`: Fahrzeugerkennung je Stationstyp.
- `Get_Car_Status()`: Statusabfrage je Stationstyp.
- `Calc_Max_Current()`: dynamische Ermittlung des maximalen Stroms aus effektiver Ladeleistung.
- `Get_Current_From_Power()`: Umrechnung W -> A fuer 1p/3p.
- `Get_Array_From_Current()`: erzeugt moegliche Leistungsstufen aus Stromstufen.
- `SetAktuelle_Leistung()`: setzt EMS-Vorgabe und sendet Stromvorgabe an Station.
- `GetCurrentData()`: erzeugt `PowerSteps` je Modus, Freigabe, Fahrzeugzustand und Timer.
- `sendPowerToStation()`: setzt Strom ueber Go-E, Smart-Me, Easee oder Dummy.
- `Refresh_Token()`: Easee Login und Token-Pufferung.
#### `Ladestation_Universal`
Aelteres, einfacheres Ladestationsmodul. Es arbeitet staerker mit Leistungsgrenzen in W statt mit dynamischer Strom-/Fahrzeuglogik.
Zentrale Unterschiede zu `Ladestation_v2`:
- Konfiguriert `MinLeistung`, `MaxLeistung`, `MinLeistung_1ph`, `MaxLeistung_1ph`.
- Hat `Lademodus` statt `Is_1_ph`.
- Unterstuetzt Go-E, Smart-Me Pico und Dummy.
- Weniger ausgebaute Timer-/Pending-/Tokenlogik.
### 7.2 Batterie
Datei: `Batterie/module.php`
Das Batteriemodul ist bidirektional im Sinne des Managers: Es erzeugt positive und negative `PowerSteps`.
Eigenschaften:
- `MaxBatterieleistung`: Variable fuer maximale Entladeleistung.
- `MaxNachladen`: Variable fuer maximale Ladeleistung.
- `Batterieladezustand`: SoC-Variable.
- `AufdasNachladen`: Zielwert fuer Nachladen.
- `MinimumEntladen`: untere SoC-Grenze.
- `Batteriemanagement`: 1 = Wechselrichter, 2 = Symcon EMS.
- `Batterietyp`: 1 = Goodwe, 2 = Solaredge, 3 = Sig Energy.
- `Netzbezug`: Netzbezug fuer Sonderlogik.
Besonderheiten:
- `GeneratePowerSteps()` erzeugt ein Raster aus groben 250-W-Schritten und feinen 50-W-Schritten um die aktuelle Leistung.
- Im Wechselrichter-Modus (`Batteriemanagement = 1`) meldet das Modul nur `[0]`.
- Im EMS-Modus werden je nach SoC, Hysterese und Modus Lade-/Entladestufen freigegeben.
- `SetAktuelle_Leistung()` schreibt je Batterietyp andere Steuerwerte:
- Goodwe: `Goodwe_EntLadeleistung`, `Laden_Entladen`.
- Solaredge: `Ladeleistung`, `Entladeleistung`, `Laden_Entladen`.
- Sig Energy: Leistung in kW und andere Moduswerte.
### 7.3 Boiler
#### `Boiler_2_Stufig_Mit_Fueler`
Boiler mit zwei Schaltkontakten:
- `Kontakt_Teillast`
- `Kontakt_Volllast`
Leistungsstufen:
```json
[0, BoilerLeistungTeillast, BoilerLeistungVolllast]
```
Die `PowerSteps` werden aus Temperatur, Mindesttemperatur, Maximaltemperatur, Legionellenlogik, Zeitplan und Modus erzeugt.
#### `Boiler_x_Stufig`
Boiler mit frei konfigurierbarer Liste `LeistungsStufen`. Jede Stufe hat:
- `Stufe`
- `Leistung`
- `Schaltkontakt_Stufe`
Das Modul erzeugt daraus ein sortiertes Leistungsarray und schaltet genau die passende Stufe.
#### `Ansteuerung_Askoheat`
Askoheat-Modul mit 7-stufiger Leistungslogik:
- `BoilerLeistung` ist die Maximalleistung.
- `Calc_Seven_Steps()` erzeugt 8 Werte von 0 bis Volllast.
- `SetAktuelle_Leistung()` schreibt eine Stufe in `Variable_Leistung`.
- Temperatur kommt aus `Variable_Temperatur_Ist`.
### 7.4 Pufferspeicher
#### `Pufferspeicher`
Puffer mit zwei festen Leistungsstufen:
- `PufferTeilLeistung`
- `PufferLeistung`
Schaltkontakte:
- `Heizkontakt_Puffer_Teillast`
- `Heizkontakt_Puffer`
Die Zieltemperatur wird als lineare Funktion der Aussentemperatur berechnet:
```text
VT = f(AT)
```
mit den Parametern:
- `MinVT_Temp`
- `MaxVT_Temp`
- `MinAT_Temp`
- `MaxAT_Temp`
Im Peak-Modus meldet der Puffer normalerweise `[0]`. Im Solarlade-Modus gibt er Stufen frei, wenn die Puffertemperatur unter dem berechneten Zielwert liegt.
#### `Puffer_Speicher`
Variante mit frei konfigurierbaren Leistungsstufen analog zu `Boiler_x_Stufig`.
### 7.5 Einfache und externe Verbraucher
#### `Verbraucher_1_Stufig`
Ein Ein/Aus-Verbraucher mit:
- `BoilerLeistung`
- `Schaltkontakt1`
- `Mindesttlaufzeit`
- Nachtlogik 22:00 bis 07:00
- `DailyOnTime`
- Mindestzeit zwischen Zustandswechseln
Im Solarlade-Modus kann er `[0, BoilerLeistung]` melden. Bei Nacht und nicht erreichter Mindestlaufzeit kann er Leistung erzwingen.
#### `Verbraucher_Sperrbar`
Ein Verbraucher, der im Peak-Fall gesperrt werden kann.
Eigenschaften:
- `Leistung`: Variable mit aktueller Verbraucherleistung.
- `Schaltkontakt1`: Sperrkontakt.
- `Mindestsperrleistung`: Mindestwert, ab dem Sperre relevant wird.
- `MaxSperrZeit`: maximale Sperrzeit pro Tag.
Im Peak-Modus meldet das Modul je nach aktueller Leistung `[0, Letzte_Sperrleistung]`. Im Solarlade-Modus meldet es `[0]`.
#### `Verbraucher_extern`
Adapter fuer mehrere externe Verbraucher. Es liest eine Liste von Paaren:
- `Read_Var`: anfragende/aktive Variable.
- `Write_Var`: zu schaltende Variable.
- `P_Nenn`: Nennleistung.
Aus aktiven Einzelverbrauchern werden alle moeglichen Leistungskombinationen berechnet. Bei einer Manager-Zuweisung wird eine passende Kombination gesucht und die zugehoerigen `Write_Var` geschaltet.
### 7.6 Waermepumpe
Datei: `WP_Steuerung/module.php`
Die Waermepumpe nutzt:
- `Sperrkontakt`
- `Kontakt_Erhoeung`
- `WP_Leistung`
- Wetter-/Wolkenvariable
- Aussentemperatur
- Referenzzeit fuer Sonnenaufgang
- optionale Warmwasser-Schwellwertlogik
Sie hat interne Zustaende:
- `Zustand_WP = 1`: Normalbetrieb.
- `Zustand_WP = 2`: Sperre.
- `Zustand_WP = 3`: Erhoehung.
`GetCurrentData()` meldet normalerweise `[0, WP_Leistung]`, kann aber bei Mindestlaufzeit oder Warmwasser-Schwellwert auf einen festen Zustand begrenzen.
## 8. Hilfs- und Integrationsmodule
### 8.1 `CC100_HW`
Dieses Modul greift direkt auf Linux-Dateipfade eines CC100-Systems zu:
- Digitale Ausgaenge: `/sys/kernel/dout_drv/DOUT_DATA`
- Digitale Eingaenge: `/sys/devices/platform/soc/44009000.spi/spi_master/spi0/spi0.0/din`
- PT-Fuehler: `/sys/bus/iio/devices/iio:device2/...`
Es stellt bereit:
- `Bit1` bis `Bit4` als digitale Ausgaenge.
- `DI1` bis `DI8` als digitale Eingaenge.
- `PT1`, `PT2` als Temperaturwerte.
Dieses Modul kann als Hardware-Abstraktion fuer Schaltkontakte und Sensorwerte dienen.
### 8.2 `Shelly_Parser_MQTT`
Das Shelly-Modul verbindet sich mit der IP-Symcon MQTT-Server-Instanz und subscribed auf `#`.
Es verarbeitet:
- `<device>/online`
- `<device>/events/rpc`
Pro Shelly-Geraet wird ein Kategorieordner angelegt. Darin werden Variablen erzeugt:
- `Online`
- `Typ`
- `Output x`
- `Input x`
- `Temperatur`
Outputs erhalten ein gemeinsames Action-Script. Beim Schalten wird ein RPC `Switch.Set` an `<device>/rpc` publiziert.
Die Parserlogik liegt in `Shelly_Parser_MQTT/libs/ShellyParser.php`.
### 8.3 `Belevo_Server_Kommunikation`
Aufgaben:
- Wetterdaten von `https://brain.belevo.ch/v2wetter` abfragen.
- `Temperatur` und `Wolkenwarscheinlichkeit` setzen.
- Zusatzvariablen als JSON sammeln.
- Daten an `BaseURL`, standardmaessig `https://brain.belevo.ch/storedata`, senden.
Die Aufzeichnung wird ueber `InfluxJaNein` aktiviert. Der Timer laeuft dann alle 5 Minuten.
### 8.4 `Energy_Pie`
Visualisierungsmodul fuer Produktions-/Verbrauchsdaten.
Konfigurierte Quellvariablen:
- `VarProduction`
- `VarConsumption`
- `VarFeedIn`
- `VarGrid`
Das Modul liest geloggte Archivwerte, berechnet Differenzen fuer Tag, Woche, Monat, Jahr oder Total und schiebt ein JSON an das HTML-Tile `module.html`.
### 8.5 `Belevo_Bezahl_Modul`
Experimentelles Zahlungsmodul fuer Google Pay/Stripe. Es schreibt HTML in eine konfigurierte HTMLBox.
Aktueller Zustand:
- enthaelt einen Stripe Test-Publishable-Key.
- Backend-Pfad ist Platzhalter (`/path-to-your-backend.php`).
- keine produktive Zahlungslogik im Modul.
Dieses Modul sollte nicht als produktionsreif betrachtet werden.
## 9. Neue Module an den Energiemanager anbinden
### 9.1 Entscheidung: Verbraucher, Manager, Hilfsmodul oder Visualisierung?
Vor der Umsetzung klaeren:
- Muss das Modul vom Energiemanager Leistung zugeteilt bekommen? Dann ist es ein Verbraucher.
- Liefert es nur Daten oder Schaltkontakte? Dann ist es ein Hilfsmodul.
- Soll es mehrere Verbraucher aggregieren? Dann ist es ein Adapter oder Manager-nahes Modul.
- Zeigt es nur Daten an? Dann ist es eine Visualisierung.
Nur Verbraucher muessen den vollen Manager-Vertrag umsetzen.
### 9.2 Minimaler Verbraucher-Aufbau
Ein neues Verbraucher-Modul sollte in `Create()` mindestens registrieren:
```php
$this->RegisterVariableInteger("Sperre_Prio", "Sperre_Prio");
$this->RegisterVariableInteger("PV_Prio", "PV_Prio");
$this->RegisterVariableBoolean("Idle", "Idle", "", true);
$this->RegisterVariableInteger("Aktuelle_Leistung", "Aktuelle_Leistung", "", 0);
$this->RegisterVariableFloat("Bezogene_Energie", "Bezogene_Energie", "", 0);
$this->RegisterVariableString("PowerSteps", "PowerSteps");
$this->RegisterVariableInteger("Power", "Power", "", 0);
$this->RegisterVariableBoolean("Is_Peak_Shaving", "Is_Peak_Shaving", "", false);
$this->RegisterVariableInteger("Leistung_Delta", "Leistung_Delta", "", 0);
$this->RegisterPropertyInteger("IdleCounterMax", 2);
$this->RegisterVariableInteger("IdleCounter", "IdleCounter", "", 0);
$this->RegisterPropertyInteger("Interval", 5);
$this->RegisterTimer(
"Timer_Do_UserCalc",
$this->ReadPropertyInteger("Interval") * 1000,
"IPS_RequestAction(" . $this->InstanceID . ', "Do_UserCalc", "");'
);
```
`RequestAction()` sollte mindestens diese Faelle enthalten:
```php
public function RequestAction($Ident, $Value)
{
switch ($Ident) {
case "SetAktuelle_Leistung":
$this->SetValue("Power", (int)$Value);
break;
case "GetCurrentData":
$this->SetValue("Is_Peak_Shaving", (bool)$Value);
break;
case "Do_UserCalc":
$this->SetAktuelle_Leistung($this->GetValue("Power"));
$this->GetCurrentData($this->GetValue("Is_Peak_Shaving"));
break;
default:
throw new Exception("Invalid Ident");
}
}
```
### 9.3 `GetCurrentData()` richtig implementieren
`GetCurrentData(bool $Peak)` muss:
1. den aktuellen Betriebsmodus beruecksichtigen.
2. interne Sensoren/Statuswerte aktualisieren.
3. die erlaubten Leistungsstufen als JSON in `PowerSteps` schreiben.
4. `Leistung_Delta` setzen, falls Soll und Ist abweichen.
5. bei Mindestlaufzeiten oder Sperrzeiten nur die aktuelle Leistung melden.
Beispiel:
```php
public function GetCurrentData(bool $Peak)
{
$this->SetValue("Is_Peak_Shaving", $Peak);
if (!$this->GetValue("Idle")) {
$this->SetValue("PowerSteps", json_encode([$this->GetValue("Aktuelle_Leistung")]));
return;
}
if ($Peak) {
$steps = [0, 1000];
} else {
$steps = [0, 1000, 2000, 3000];
}
$this->SetValue("PowerSteps", json_encode($steps));
$this->SetValue("Leistung_Delta", 0);
}
```
### 9.4 `SetAktuelle_Leistung()` richtig implementieren
`SetAktuelle_Leistung()` muss:
1. Aenderung erkennen und `IdleCounter` setzen.
2. Die physische Ansteuerung durchfuehren.
3. `Aktuelle_Leistung` setzen.
4. `Bezogene_Energie` fortschreiben.
5. `IdleCounter` weiterverarbeiten.
Beispiel:
```php
public function SetAktuelle_Leistung(int $power)
{
$lastPower = $this->GetValue("Aktuelle_Leistung");
if ($lastPower !== $power) {
$this->SetValue("Idle", false);
$this->SetValue("IdleCounter", $this->ReadPropertyInteger("IdleCounterMax"));
}
// Hier Geraet ansteuern, z.B. Relais, HTTP API oder Wechselrichtervariable.
// SetValue($this->ReadPropertyInteger("Schaltkontakt"), $power > 0);
$this->SetValue("Aktuelle_Leistung", $power);
$this->SetValue(
"Bezogene_Energie",
$this->GetValue("Bezogene_Energie") + ($power * ($this->ReadPropertyInteger("Interval") / 3600))
);
$this->ProcessIdleCounter();
}
```
### 9.5 Checkliste fuer neue Verbraucher
- `module.json` mit neuer GUID, Name, Vendor und Prefix erstellen.
- `form.json` mit allen benoetigten Einstellungen erstellen.
- Alle Pflichtvariablen des Verbraucher-Vertrags registrieren.
- `RequestAction` mit `SetAktuelle_Leistung`, `GetCurrentData`, `Do_UserCalc` implementieren.
- `PowerSteps` immer als gueltiges JSON-Array schreiben.
- `PowerSteps` immer numerisch sortierbar halten.
- `0` als Stufe anbieten, wenn das Geraet wirklich ausgeschaltet/neutral sein darf.
- Negative Stufen nur verwenden, wenn das Modul Einspeisung/Entladung sauber unterstuetzt.
- `Idle` bei Schaltvorgaengen und Mindestlaufzeiten korrekt auf `false` setzen.
- Geraetefehler und Kommunikationsfehler abfangen.
- Bei Fehlern sichere `PowerSteps` setzen, meist `[0]` oder aktuelle Leistung.
- Im Manager unter `Verbraucher_Liste` eintragen.
## 10. Empfehlungen fuer zukuenftige Verbesserungen
### 10.1 Gemeinsame Basisklasse oder Trait fuer Verbraucher
Der gleiche EMS-Vertrag ist in fast allen Verbrauchern mehrfach implementiert. Sinnvoll waere eine gemeinsame Datei, zum Beispiel:
- `libs/EmsConsumerTrait.php`
- `libs/EmsPowerStepHelper.php`
Diese koennte bereitstellen:
- Registrierung der Standardvariablen.
- Einheitliche `IdleCounter`-Logik.
- Energiezaehlung.
- JSON-Validierung fuer `PowerSteps`.
- Hilfen fuer Sortierung und Minimalstufen.
### 10.2 Manager-Zyklus robuster machen
Im aktuellen `Manager::DistributeEnergy()` werden Verbraucherdaten gelesen und danach `GetCurrentData` aufgerufen. Dadurch kann der Manager im selben Zyklus noch alte `PowerSteps` verwenden, sofern die Verbraucher nicht ohnehin ueber ihren eigenen Timer aktuell sind.
Robuster waere:
1. Alle Verbraucher per `GetCurrentData($Is_Peak_Shaving)` aktualisieren.
2. Danach `PowerSteps`, `Idle`, `Leistung_Delta` neu lesen.
3. Dann verteilen.
Alternativ koennte `GetCurrentData()` synchron Daten zurueckgeben statt nur Variablen zu schreiben.
### 10.3 Validierung und Fail-Safe
Viele Properties sind Variable-IDs. Hauefig ist `0` Default. Vor `GetValue(0)` oder `SetValue(0)` sollte konsequent geprueft werden:
```php
$id = $this->ReadPropertyInteger("Netzbezug");
if ($id <= 0 || !IPS_VariableExists($id)) {
$this->SetStatus(201); // oder eigener Fehlerstatus
return;
}
```
Empfehlung:
- Konfigurationspruefung in `ApplyChanges()`.
- Statuscodes fuer unvollstaendige Konfiguration.
- sichere PowerSteps bei Kommunikationsfehlern.
### 10.4 Einheitliche Logging- und Diagnosevariablen
Aktuell gibt es viele `IPS_LogMessage()`-Eintraege, teils sehr haeufig in Schleifen. Besser waeren:
- `Letzter_Fehler` als String.
- `Letzte_Aktion` als String.
- `Letzte_Kommunikation` als Timestamp.
- `Kommunikation_OK` als Boolean.
- Debug-Logging nur optional per Property.
### 10.5 Ladestation v3 / OCPP-orientierte Architektur
`Ladestation_v2` enthaelt bereits viele Funktionen, die fuer ein OCPP-Modul relevant sind:
- Freigabe
- Solarladen
- Fahrzeugerkennung
- Statusabfrage
- effektive Ladeleistung
- Stromvorgabe
- Mindeststrom
- 1p/3p-Erkennung
- externe Leistungszuweisung
- PowerSteps
- Peak-Shaving-Einfluss
- Idle-/Pending-/Timer-Logik
- Token-/Login-Mechanismen
Fuer eine neue OCPP-orientierte Version sollte diese Logik fachlich erhalten bleiben, aber in Adapter aufgeteilt werden:
- `ChargePointModel`: internes Datenmodell.
- `ChargingSession`: Ladevorgang, Benutzer, Energie.
- `MeterValues`: Messwerte.
- `SmartCharging`: Sollstrom/Sollleistung.
- `ConnectorState`: Status pro Ladepunkt.
- `VendorAdapterInterface`: Herstelleradapter.
- `Ocpp16Adapter`, `Ocpp201Adapter`, `GoEAdapter`, `EaseeAdapter`, `SmartMeAdapter`, `ECarUpAdapter`.
Die EMS-Schnittstelle zum Manager sollte gleich bleiben: `PowerSteps`, `SetAktuelle_Leistung`, `GetCurrentData`, `Idle`.
### 10.6 Tests und Simulation
Das Projekt hat aktuell keine automatisierten Tests. Sinnvoll waere ein Simulationsmodus:
- Fake-Verbraucher mit frei definierbaren `PowerSteps`.
- Fake-Netzbezug.
- Testfaelle fuer Solarladen und Peak-Shaving.
- Testfaelle fuer Verbraucher mit und ohne `0`.
- Testfaelle fuer negative PowerSteps.
- Testfaelle fuer gleiche Prioritaet/Fairness.
- Testfaelle fuer Hauptmanager-JSON.
## 11. Bekannte technische Risiken und Auffaelligkeiten
Diese Punkte sind aus dem aktuellen Code abgeleitet und sollten vor groesseren Erweiterungen geprueft werden:
- Mehrere Dateien zeigen Encoding-Probleme in deutschen Texten. Das deutet auf UTF-8/ANSI-Mischung hin.
- `Manager::DistributeEnergy()` aktualisiert Verbraucher mit `GetCurrentData`, liest aber vorher bereits viele Verbraucherdaten. Das kann zu veralteten `PowerSteps` fuehren.
- Viele Module pruefen konfigurierte Variablen-IDs nicht konsequent vor `GetValue()` oder `SetValue()`.
- `Ansteuerung_Askoheat` registriert `Boilertemperatur` doppelt, einmal Float und spaeter Integer.
- `Boiler_x_Stufig` nutzt in der Zeitplanpruefung offenbar `$vollLeistung`, ohne diese Variable vorher eindeutig zu setzen.
- `Ladestation_v2::GetCurrentData()` verwendet in einem Zweig fuer `solarladen && Peak` die Variable `$is_1_ph`, die dort nicht definiert ist. Gemeint ist wahrscheinlich `$this->GetValue("Is_1_ph")`.
- `Pufferspeicher` prueft in der Glaettungslogik teilweise `GetIDForIdent("Boilertemperatur")`, obwohl die Variable `Puffertemperatur` heisst.
- `Batterie::CheckIdle()` verwendet `GetValue("Aktuelle_Leistung")` mit String statt Variablen-ID. Die Funktion scheint aktuell nicht zentral genutzt zu werden, sollte aber korrigiert werden.
- `Verbraucher_Sperrbar::ist_nachts()` nutzt `24:00` als Startzeit. Das ist fachlich ungewoehnlich und sollte geprueft werden.
- Mehrere Module setzen `date_default_timezone_set("Europe/Berlin")`; fuer Schweizer Anlagen waere `Europe/Zurich` konsistenter.
- `Belevo_Bezahl_Modul` ist nur Prototyp/Teststand und enthaelt noch Platzhalter.
- Logging in Schleifen kann im Dauerbetrieb sehr viele Meldungen erzeugen.
## 12. Arbeitsanweisung fuer ChatGPT/Codex bei zukuenftigen Aenderungen
Wenn ChatGPT/Codex spaeter an dieser Software weiterarbeitet, sollte es so vorgehen:
1. Zuerst `DOKUMENTATION_CHATGPT.md`, `README.md`, `Manager/module.php` und das betroffene Modul lesen.
2. Vor jeder Aenderung pruefen, ob das Modul ein Verbraucher ist und den EMS-Vertrag einhalten muss.
3. Bestehende Variablen-Idents nicht ohne Migrationsplan umbenennen, weil IP-Symcon Installationen davon abhaengen.
4. Neue Module mit eigenem Ordner, `module.php`, `module.json`, `form.json` anlegen.
5. Bei neuen Verbrauchern die Pflichtvariablen exakt gleich benennen.
6. `PowerSteps` und `Leistung_Delta` besonders sorgfaeltig testen.
7. Bei Hardware/API-Modulen immer Fehlerfaelle abfangen und sichere Werte setzen.
8. Keine bestehenden Benutzer- oder Anlagenkonfigurationen brechen.
9. Bei Ladestationen die EMS-Schnittstelle stabil halten, auch wenn intern OCPP oder Herstelleradapter eingefuehrt werden.
10. Nach Aenderungen mindestens PHP-Syntax pruefen und, wenn moeglich, mit einer simulierten Manager-Konfiguration testen.
## 13. Kurzreferenz fuer neue ChatGPT-Prompts
Wenn ein neues ChatGPT-Fenster mit dieser Software weiterarbeiten soll, kann folgender Kontext verwendet werden:
```text
Dies ist eine IP-Symcon Modulbibliothek fuer das Belevo Energiemanagement.
Der zentrale Manager verteilt Leistung an Verbraucher ueber einen gemeinsamen Vertrag:
Variablen: Sperre_Prio, PV_Prio, Idle, Aktuelle_Leistung, Bezogene_Energie,
PowerSteps, Power, Is_Peak_Shaving, Leistung_Delta, IdleCounter.
Actions: GetCurrentData(bool Peak), SetAktuelle_Leistung(power), Do_UserCalc.
PowerSteps ist ein JSON-Array moeglicher Leistungsstufen in Watt.
Positive Werte bedeuten Verbrauch/Laden, negative Werte Entladen/Einspeisen.
Der Manager entscheidet zwischen Solarladen und Peak-Shaving und verteilt nach Prioritaet.
Neue steuerbare Module muessen diesen Vertrag exakt umsetzen.
Wichtige Dateien: Manager/module.php, HauptManager/module.php,
Ladestation_v2/module.php, Batterie/module.php, DOKUMENTATION_CHATGPT.md.
```