# 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]]