Eine Zustandsmaschine für die Wärmepumpe
— zuletzt geändert am
942 Wörter, Lesezeit 6 min
Nach der Installation unserer LG Therma V im August 2024 habe ich mich monatelang mit der Anbindung an Home Assistant beschäftigt — die Wärmepumpe spricht Modbus, und alles, was über die Werkseinstellungen hinausgeht, muss man sich selbst zusammenbauen. Angefangen habe ich, wie das eben anfängt: ein paar Automationen in YAML, eine Heizkurve hier, eine Bedingung für den Pufferspeicher dort. Nach dem Umzug auf den Heimserver habe ich das noch einmal grundlegend umgebaut, diesmal als endlichen Automaten (finite state machine, FSM). Dieser Beitrag beschreibt, wie er aufgebaut ist.
Warum Automationen irgendwann nicht mehr reichen
Eine einzelne Home-Assistant-Automation ist schnell geschrieben: wenn Temperatur X unter Schwelle Y fällt, schalte Z. Das Problem fängt an, sobald mehrere solcher Regeln gleichzeitig auf dieselbe Hardware wirken. Bei mir waren das am Ende: eine Heizkurve mit PV-Korrektur, eine Mindestlaufzeit für den Verdichter, eine Mischerregelung für den Heizkreis, eine Zonenlogik für den Pufferspeicher und eine Vorrangsteuerung für Warmwasser und PV-Überschussladung — fünf Themen, die sich gegenseitig ausschließen oder bedingen. In YAML lässt sich das noch hinschreiben, aber nicht mehr lesen: Welche Automation hat gerade das letzte Wort, wenn PV-Ladung und Warmwasseranforderung gleichzeitig anliegen?
Eine Zustandsmaschine zwingt zu einer Antwort, die man sich sonst erspart: das System ist immer in genau einem von endlich vielen Zuständen, und für jeden Übergang zwischen zwei Zuständen steht eine einzige, benannte Bedingung fest. Kein »eigentlich sollte das nicht gleichzeitig passieren«, sondern eine Logik, die jede Kombination einmal durchdenkt.
Acht Zustände
Kurz zu jedem Zustand:
- idle. Nichts zu tun — Wärmepumpe und Heizkreispumpe aus, Mischer geschlossen.
- standby. Eine kurze Zwangspause nach dem Abschalten, damit der Verdichter nicht sofort wieder anspringt.
- heating. Regulärer Heizbetrieb nach Heizkurve, mit laufendem Mischerregler.
- heating_forced. Der Verdichter hat seine Mindestlaufzeit noch nicht erreicht, obwohl kein Wärmebedarf mehr besteht — die Wärmepumpe läuft weiter, aber mit absinkendem Sollwert.
- buffer_drain. Der Pufferspeicher ist warm genug, um das Haus direkt zu versorgen; die Wärmepumpe bleibt aus, nur Pumpe und Mischer laufen.
- buffer_charge. PV-Überschuss lädt den Pufferspeicher.
- buffer_charge_boost. Bei viel PV-Überschuss schaltet zusätzlich der Heizstab zu.
- hot_water. Warmwasserbereitung, mit Vorrang vor allem anderen — dieser Übergang ist aus jedem aktiven Zustand heraus möglich, deshalb steht er im Diagramm für sich.
Was zwischen den Zuständen entscheidet
Der Kern der ganzen Logik ist ein einziger Temperaturfühler: Puffer 1/2, auf Höhe des Vorlauf-Stutzens im Schichtspeicher. Fällt er unter die Heizkurven-Schwelle (plus Sicherheitsabstand), beginnt Heizbetrieb; steigt er darüber (plus Hysterese, damit es kein Takten gibt), übernimmt stattdessen der Puffer. Die Schwelle selbst ist kein fester Wert, sondern wird aus der Heizkurve berechnet — bei −5 °C Außentemperatur braucht der Puffer mehr Reserve als bei 10 °C.
Die Außentemperatur selbst geht nicht als Rohwert ein, sondern exponentiell geglättet über 24 h, damit ein einzelner kalter Vormittag nicht sofort die Heizung anwirft. Zum Abschalten genügt dagegen auch der ungeglättete 1-Stunden-Mittelwert — wird es warm, soll die Wärmepumpe zügig reagieren, nicht erst wenn die Glättung nachgezogen hat.
Bei der PV-Ladung war die interessantere Erkenntnis eine andere: Ich regle dabei nicht auf den Auslass der Wärmepumpe, sondern auf ihren Einlass. Der Auslass der Wärmepumpe erreicht die Zieltemperatur recht schnell. Fällt die Temperatur unten im Tank wieder (da wird ja das Wasser “abgesaugt”), beginnt das Takten. Der Einlass der Wärmepumpe ist dagegen der Rücklauf vom Speicherboden; erreicht der die Zieltemperatur, ist wirklich der ganze Puffer durchgeladen.
Ein Auszug aus der Übergangsliste zeigt, wie das im Code aussieht — jeder Übergang ist ein Tripel aus Start-, Zielzustand und einer Bedingung, dazu ein Klartext-Label, das im Home-Assistant-Logbuch landet:
Transition(
"heating", "buffer_drain",
lambda: self._need_buffer_drain() and self._compressor_on_since is None,
label="Puffer aufgeladen, Kompressor intern gestoppt - Puffer entladen",
),
Damit lässt sich im Nachhinein nachschauen, warum die Steuerung um 03:14 Uhr in einen anderen Zustand gewechselt ist, ohne im Code suchen zu müssen.
Auch der Mischer im Heizkreis läuft nicht mit einem klassischen PID-Regler, sondern mit einer Kombination aus Vorsteuerung und langsamem Trimmregler: Da alle drei beteiligten Temperaturen (Speicher, Rücklauf, gewünschter Vorlauf) bekannt sind, lässt sich die nötige Mischerstellung direkt ausrechnen, statt sie sich über einen Regelfehler zu erarbeiten. Der PI-Anteil korrigiert nur noch den Rest — Ventilkennlinie, Fühlerungenauigkeit — und darf deshalb sehr träge eingestellt sein.
Schutzmechanismen
Eine Wärmepumpe ist keine Testumgebung, deshalb steckt in der Basisklasse mehr Vorsicht als Regelungslogik:
- Not-Aus. Wechselt die Maschine zu oft in zu kurzer Zeit den Zustand, schaltet sie sich selbst in einen Trockenlauf-Modus und schickt eine Benachrichtigung — ein Zeichen, dass irgendwo zwei Bedingungen gegeneinander pendeln.
- Verdichter-Takt-Schutz. Zu viele Verdichterstarts in einer Stunde lösen dieselbe Bremse aus.
- Trockenlauf-Modus. Die gesamte Logik rechnet ganz normal mit, schreibt aber nichts an die Hardware — beim ersten Einsatz an einer echten Heizung unverzichtbar, und bis heute die erste Einstellung nach jeder größeren Änderung.
- Sensor-Fallback. Fällt ein Fühler aus, hält die Maschine den letzten gültigen Wert statt auf einen Standardwert zu springen; nur beim allerersten Start nach einem Neustart gibt es überhaupt einen Default, und der ist bewusst so gewählt, dass die Wärmepumpe dabei eher zu lange läuft als zu früh abschaltet.
- Start-Gnadenfrist. Nach jedem Neustart des Containers — unter anderem beim täglichen Backup nachts um zwei — sind einige Sensoren für ein paar Minuten nicht erreichbar. In dieser Zeit unterdrückt die Maschine jede Abschaltentscheidung, sonst hätte das nächtliche Backup schon zweimal für ein kaltes Haus gesorgt.
Der Code liegt offen unter ha-heatpump-fsm, inklusive der wiederverwendbaren Basisklasse für den Automaten selbst.
Fazit
Der eigentliche Wert der Zustandsmaschine zeigt sich nicht im Alltag, sondern in den Momenten, in denen etwas schiefgeht: Ein Blick ins Home-Assistant-Logbuch zeigt nicht nur, dass die Wärmepumpe von Heizen auf Puffer-Entladen gewechselt ist, sondern auch, welche der zwei möglichen Bedingungen dafür gefeuert hat. Mit verteilten YAML-Automationen wäre diese Frage eine Rekonstruktion aus mehreren Logdateien gewesen; jetzt steht die Antwort in einer Zeile.