# Node-RED Energiemanagement ## Tags #homeautomation #nodered #marstek #tibber #solcast #opendtu #proxmox --- ## Ziel Intelligente Ladesteuerung der Marstek Venus 3 Speicher unter Berücksichtigung von PV-Ertrag, Solcast-Forecast und Tibber-Preisen. Das CT002 übernimmt die Echtzeit-Regelung (Nulleinspeisung). Node-RED berechnet dynamisches Ladeziel und Ladeleistung und setzt diese über die HA-API. --- ## Hardware | Komponente | Details | |---|---| | Wechselrichter | Hoymiles (BKW ~2400 Wp) | | DTU | OpenDTU | | Speicher | 3× Marstek Venus 3 (je 13 kWh = 39 kWh gesamt) | | Speicherregler | Marstek CT002 | | Stromzähler | Inexogy (Smart Meter) | | Stromtarif | Tibber (dynamisch) | | Node-RED | LXC 106 auf Proxmox pve, IP 192.168.2.172, Port 1880 | | Home Assistant | VM haproxmox, IP 192.168.2.154, Port 8123 | | SQLite DB | /data/energiemanagement.db (im LXC 106) | --- ## Systemparameter | Parameter | Wert | |---|---| | Speicherkapazität gesamt | 39 kWh (3 × 13 kWh) | | Max. Ladeleistung je Speicher | 2.000 W | | Ziel-SOC (Tag) | 100 % | | Ziel-SOC (Nacht) | 80 % | | Mindest-SOC | 12 % | | Nacht-Definition | 21:00–06:00 Uhr | | Laden bei Tibber-Level | CHEAP + VERY_CHEAP | | PV aktiv Schwelle | > 50 W, letztes MQTT-Signal < 15 Min | --- ## Aufgabenteilung | Komponente | Aufgabe | |---|---| | **CT002** | Echtzeit-Regelung: Nulleinspeisung, Laden/Entladen nach Tibber | | **Node-RED** | Berechnet Ladeziel + Ladeleistung → setzt per HA-API alle 5 Minuten | | **SQLite** | Logging aller Messwerte und Entscheidungen | Node-RED greift **nicht** in die Echtzeit-Regelung ein. Kein direkter Modbus-Zugriff — Port 502 wird von der HA Marstek-Integration belegt. --- ## Programmablauf (umgangssprachlich) Der Flow läuft alle 5 Minuten durch — hier was dabei passiert: **1. PV-Leistung abhören (läuft dauerhaft parallel)** Der OpenDTU-Wechselrichter schickt seine aktuelle Leistung per MQTT. Node-RED hört das Topic `solar/dtu/ac/power` ständig ab und merkt sich den letzten Wert samt Uhrzeit. Nachts sendet der Wechselrichter nichts — deshalb wird der Zeitstempel geprüft: ist das letzte Signal älter als 15 Minuten oder unter 50W, gilt PV als inaktiv. **2. Alle 5 Minuten: Daten von Home Assistant holen** Node-RED fragt der Reihe nach folgende Sensoren bei HA ab: - Tibber: aktueller Preis und Preisniveau, plus Vorschau für die nächsten 4 Stunden - Solcast: wieviel PV heute noch zu erwarten ist, und wieviel morgen kommt - Marstek: aktueller Ladestand (SOC) aller 3 Batterien **3. Restverbrauch berechnen** Aus einem hinterlegten Lastprofil (stündliche Durchschnittswerte aus Inexogy-Daten) wird berechnet, wieviel Strom das Haus von jetzt bis 21:00 Uhr noch braucht. Nachts wird der Verbrauch bis 06:00 Uhr berechnet. **4. PV-Netto berechnen** Von der erwarteten PV-Leistung wird der Restverbrauch abgezogen. Was übrig bleibt ist der Anteil den die PV noch in die Batterien laden kann — ohne dass Netzstrom benötigt wird. **5. Ladeziel berechnen** Das Ladeziel wird so gesetzt dass die Batterien nicht voller geladen werden als nötig — der PV-Anteil soll noch reinpassen. Beispiel: PV kann noch 5 kWh liefern, das entspricht ~13% SOC → Ladeziel = 87% statt 100%. Nachts (21:00–06:00) ist das Maximum 80%, damit tagsüber noch Platz für PV bleibt. **6. Entscheidung treffen** Je nach Situation wird eine von fünf Entscheidungen getroffen: - **LADEN_NOTFALL** — SOC unter 12%: sofort mit voller Leistung laden, egal was - **PV_LADEN** — PV liefert gerade: Ladeziel auf 100% setzen, Ladeleistung = PV-Leistung geteilt durch 3 (je Batterie). CT002 regelt den Ausgleich zum Hausverbrauch - **LADEN** — Tibber ist gerade günstig (CHEAP/VERY_CHEAP) und Batterien noch nicht voll: mit 2000W je Batterie laden - **LADEN_VORSORGLICH** — Tibber wird in den nächsten 4 Stunden günstig: schon mal laden bevor das Fenster da ist - **NICHTS** — alles gut, CT002 macht weiter was es will **7. Steuerung ausführen (im Testmodus deaktiviert)** Node-RED setzt per HA-API das Ladeziel und die Ladeleistung für alle 3 Batterien gleichzeitig. Das CT002 hält sich dann an diese Vorgaben. **8. Alles in SQLite speichern** Jeder Durchlauf wird mit allen Messwerten und der getroffenen Entscheidung in die Datenbank geschrieben — für spätere Auswertung. --- ## Dateneingänge | Sensor | Kanal | Wert | |---|---|---| | `solar/dtu/ac/power` | MQTT | PV-Leistung aktuell (W) | | `sensor.daheim_aktuelles_preisniveau` | HA API | Tibber Level (CHEAP etc.) | | `sensor.daheim_aktueller_strompreis` | HA API | Tibber Preis (ct/kWh) | | `sensor.daheim_preis_nachste_1h` bis `_4h` | HA API | Tibber Vorschau 4 Stunden | | `sensor.solcast_pv_forecast_prognose_verbleibende_leistung_heute` | HA API | PV-Forecast verbleibend heute (kWh) | | `sensor.solcast_pv_forecast_prognose_heute` | HA API | PV-Forecast heute gesamt (kWh) | | `sensor.solcast_pv_forecast_prognose_morgen` | HA API | PV-Forecast morgen gesamt (kWh) | | `sensor.mtv01_soc_batterie` | HA API | SOC Batterie 1 (%) | | `sensor.mtv02_soc_batterie` | HA API | SOC Batterie 2 (%) | | `sensor.mtv03_soc_batterie` | HA API | SOC Batterie 3 (%) | ## Steuerausgänge | Entity | Wert | |---|---| | `number.marstek_venus_modbus_maximaler_soc` | Ladeziel B1 (%) | | `number.marstek_venus_modbus_maximaler_soc_2` | Ladeziel B2 (%) | | `number.marstek_venus_modbus_maximaler_soc_3` | Ladeziel B3 (%) | | `number.marstek_venus_modbus_ladeleistung_einstellen` | Ladeleistung B1 (W) | | `number.marstek_venus_modbus_ladeleistung_einstellen_2` | Ladeleistung B2 (W) | | `number.marstek_venus_modbus_ladeleistung_einstellen_3` | Ladeleistung B3 (W) | --- ## Forecast-Logik je Tageszeit | Uhrzeit | Forecast "heute" | Grund | |---|---|---| | 06:00–23:59 | `prognose_verbleibende_leistung_heute` | laufender Tag | | 00:00–05:59 | `prognose_heute` (Tagesgesamt) | Tag noch nicht begonnen | --- ## Lastprofil (Hausverbrauch, geglättet) ```javascript const lastprofil = [ 185, 182, 180, 178, 280, 380, 420, 480, // 00-07 Uhr 520, 580, 630, 690, 710, 700, 580, 500, // 08-15 Uhr 510, 730, 720, 650, 500, 380, 280, 200 // 16-23 Uhr ]; ``` Tagesverbrauch ca. 8–9 kWh. Basiert auf Inexogy-Lastprofil (geglättet). Später mit echten Daten feinjustieren. --- ## SQLite Logging ### Tabelle `entscheidungen` ```sql CREATE TABLE IF NOT EXISTS entscheidungen ( id INTEGER PRIMARY KEY AUTOINCREMENT, zeitstempel DATETIME, soc_b1 REAL, soc_b2 REAL, soc_b3 REAL, pv_aktuell REAL, tibber_preis REAL, tibber_level TEXT, forecast_heute REAL, forecast_morgen REAL, restverbrauch_kwh REAL, pv_netto_kwh REAL, ladeziel REAL, ladeleistung_je REAL, entscheidung TEXT ); ``` ### Tabelle neu erstellen (nach Schema-Änderung) ```bash sqlite3 /data/energiemanagement.db "DROP TABLE entscheidungen;" # Dann DB Init Node in Node-RED anklicken ``` ### Nützliche Abfragen ```bash # Letzte 20 Einträge sqlite3 /data/energiemanagement.db \ "SELECT zeitstempel, soc_b1, soc_b2, soc_b3, tibber_level, ladeziel, ladeleistung_je, entscheidung \ FROM entscheidungen ORDER BY id DESC LIMIT 20;" # Alle LADEN-Entscheidungen sqlite3 /data/energiemanagement.db \ "SELECT * FROM entscheidungen WHERE entscheidung LIKE 'LADEN%';" # Tagesübersicht sqlite3 /data/energiemanagement.db \ "SELECT DATE(zeitstempel), COUNT(*), entscheidung \ FROM entscheidungen GROUP BY DATE(zeitstempel), entscheidung;" # PV_LADEN Einträge sqlite3 /data/energiemanagement.db \ "SELECT zeitstempel, pv_aktuell, ladeleistung_je, soc_b1, soc_b2, soc_b3 \ FROM entscheidungen WHERE entscheidung='PV_LADEN';" ``` --- ## Flow-Versionen | Version | Änderungen | |---|---| | v1 | Erster Entwurf mit Modbus TCP direkt | | v2 | Modbus durch HA-API ersetzt (Port 502 belegt durch HA-Integration) | | v3 | Hausverbrauch und ENTLADEN-Logik entfernt (CT002 regelt selbst) | | v4 | PV_LADEN Entscheidung + pv_timestamp für Nacht-Erkennung | | v5 | Dynamische Ladeleistung: PV_LADEN = pv_aktuell/3, Netzladen = 2000W | Aktuelle Version: **v5** — `nodered_flow_v5.txt` --- ## Installation Node-RED LXC ```bash # Auf pve Host: bash -c "$(curl -fsSL https://community-scripts.org/scripts/node-red?id=node-red)" ``` LXC ID: 106, IP: 192.168.2.172, Port: 1880 ### Installierte Nodes - `node-red-contrib-home-assistant-websocket` - `node-red-contrib-modbus` (installiert, aktuell nicht genutzt) - `node-red-node-sqlite` - MQTT: eingebaut ### HA Long-Lived Token `Profil → Sicherheit → Langlebige Zugriffstoken → Token erstellen` ### Flow Backup ```bash cp /root/.node-red/flows.json /data/flows_backup_$(date +%Y%m%d).json ``` --- ## Testbetrieb - TESTMODUS = true → nur Logging, keine Steuerung - Zum Aktivieren: in `Entscheidungslogik` Node `var TESTMODUS = true;` auf `false` setzen - Empfehlung: mindestens 3–5 Tage beobachten bevor Steuerung aktiviert wird --- ## Offene Punkte - [ ] Tabelle neu erstellen (DROP + DB Init) wegen neuer Spalte `ladeleistung_je` - [ ] v5 importieren und testen - [ ] Testphase abwarten (min. 3-5 Tage) - [ ] Tibber-Level Mapping prüfen — liefert HA `CHEAP` oder deutsche Bezeichnung? - [ ] Logging auswerten — Entscheidungen plausibel? - [ ] Lastprofil mit echten Inexogy-Daten feinjustieren - [ ] TESTMODUS auf false setzen und Steuerung aktivieren --- [[Impressum|Impressum]] | [[Datenschutzerklärung|Datenschutz]]