An einem sonnigen Nachmittag macht mein Dach mehr Strom, als das Haus aufnehmen kann. Der Akku ist voll, der Wechselrichter würde den Rest am liebsten für fast nichts ins Netz geben, und unten im Keller steht ein 200-Liter-Tank mit kaltem Wasser. Der naheliegende Zug ist, diesen Überschuss in den Heizstab zu kippen und als Warmwasser zu speichern. Das Gerät dafür ist ein my-PV AC·THOR 9s — ein ohmscher PV-Überschuss-Diverter, der die Leistung in das Boiler-Heizelement moduliert, statt sie ins Netz abfließen zu lassen. Dieser Beitrag ist die Geschichte, wie ich ihn unter Home Assistant-Kontrolle gebracht habe — und die Wand, gegen die ich dabei lief.
Es ist Teil 03 davon, wie ich mein Haus selbst hoste. Teil 01 brachte Home Assistant auf einen Docker-Host, Teil 02 ergänzte HACS, und die Wechselrichter-Seite — ein Huawei SUN2000-8KTL-M1, über Modbus TCP per Huawei-SDongle ausgelesen, samt eines Akkus, dessen Laden und Entladen ich mitschreibe — war schon Vorarbeit. Überschuss ist für den AC·THOR schlicht das, was nach Hauslast und Akkuladung übrig bleibt. Das Schwere war also nicht die Energielogik. Es war die Steuerebene.
Der Plan, der hätte funktionieren sollen
Der AC·THOR spricht Modbus TCP, und mein ganzer Energie-Stack lebt schon auf Modbus, also schrieb sich das Design von selbst. Zwei Register machen alles: Register 5000 ist Heizung ein/aus (0 oder 1), und Register 5006 ist die Leistungsgrenze in Watt. Das Gerät hat zwei nützliche Modi — eine Eco-Einstellung mit 1500 W für reine Solarheizung und Full Power mit 3000 W, was zugleich sein Hardware-Maximum ist. Man kann live umschalten, während es heizt.
In Home Assistant habe ich das als kleine Steuerfläche modelliert: ein input_select für den Leistungsmodus, ein input_boolean für das Heizaktiv-Flag und eine Dashboard-Karte — AC·THOR Steuerung — mit schlichten Start- und Stopp-Knöpfen. Hinter den Knöpfen ein paar mbpoll-artige Writes: 5000 anstoßen zum Aktivieren, 5006 auf 1500 oder 3000 setzen. Ich testete erst die Reads, um Slave und Port sicher zu haben. Die Boiler-Temperatur kam sauber über Holding-Register 1001 (int16, in °C), alle 30 Sekunden abgefragt. Alles sah startklar aus.
Die Wand: Writes verweigert, Reads sauber
Dann schickte ich den ersten Write, und der AC·THOR schlug die Tür zu. Connection refused — kein Timeout, kein Fehler wegen falschem Wert, eine glatte Verweigerung auf Geräteebene. Ich prüfte Slave-ID, Register, Byte-Reihenfolge, Funktionscode. Nichts davon zählte. Das Ding nimmt schlicht keine Modbus-Write-Operationen an. Wirklich verwirrend war, dass dieselbe Verbindung bereitwillig Reads beantwortet: Register 1001 lieferte weiter alle 30 Sekunden die Boiler-Temperatur, am selben Host und Port, während jeder Write abprallte.
Ehrlich gesagt: das schmerzte. Ich hatte den ganzen Steuerpfad auf der Annahme gebaut, dass ein Gerät, das Modbus anbietet, es in beide Richtungen anbietet. Tut es hier nicht, und das ist eine bewusste Entscheidung von my-PV — die Register sind echt, nur über lokales Modbus eben nur lesbar. Mein stolzes, rein lokales Switch-Design war für die Steuerung also tot. Die Boiler-Temperatur-Überwachung überlebt unangetastet, weil das immer nur ein Read war.
# Modbus-Read funktioniert weiter — Boiler-Temp bleibt lokal; nur WRITES werden verweigert
- name: ac_thor
type: tcp
host: <LOCAL_IP> # geschwärzt — niemals die LAN-Adresse veröffentlichen
port: 502
delay: 1
timeout: 5
sensors:
- name: "Boiler-Temperatur"
slave: 1
address: 1001
input_type: holding
data_type: int16
unit_of_measurement: "°C"
scan_interval: 30Der Schwenk: lokal lesen, in der Cloud schreiben
Der Weg, der tatsächlich funktionierte, ist die my-PV Cloud-API. Jeder AC·THOR telefoniert nach Hause, und diese Cloud legt genau die Konfiguration offen, die die lokalen Writes berührt hätten: ein GET /data für Live-Monitoring und ein GET/PUT /setup zum Lesen und Ändern der Konfiguration. Der Split, den ich ausgeliefert habe, ist also lokal-lesen, Cloud-schreiben — die Boiler-Temperatur bleibt auf lokalem Modbus (Register 1001, alle 30 s, ohne Internet-Abhängigkeit), und jede Steuerung geht über die Cloud.
Es wurmte mich ein wenig. Von einer Hersteller-Cloud abhängig zu sein, nur um meinen eigenen Heizstab einzuschalten, ist genau die Abhängigkeit, der ich durch Selbsthosting entkommen will, und wenn ihre API ausfällt, hängt meine morgendliche Dusche an fremder Verfügbarkeit. Aber es ist der Split, der heute funktioniert, und er schaltet etwas frei, das das rein lokale Design nie konnte: echtes zeitgesteuertes Verhalten.
Ein Fallstrick ist einen Satz wert. Ein PUT auf /setup gibt sofort ok zurück, aber das Gerät wendet es erst nach weiteren 4 bis 6 Sekunden an. Wer sofort ein GET /setup zur Bestätigung hinterherschickt, liest die alten Werte und denkt, der Write sei ignoriert worden. Wurde er nicht — man muss nur warten und dann pollen.
Was die Cloud-Steuerung wirklich bringt: tagsüber Gratiswärme, morgens ein warmer Tank
Der interessante Teil ist nicht Ein/Aus — es sind zwei Temperaturziele und ein Zeitfenster. Der AC·THOR führt ein max. Solar-Ziel (ww1target) und ein niedrigeres Boost-Ziel (ww1boost). Tagsüber lasse ich den Überschuss den Tank bis zu einer hohen Solar-Obergrenze treiben — 65 °C — weil diese Wärme gratis ist; jedes Grad, das die Sonne einlagert, ist ein Grad, das ich nachts nicht kaufe. Temperaturen sind in Zehntelgrad codiert, 650 heißt also 65,0 °C und 400 heißt 40,0 °C, worüber man genau einmal stolpert.
Den Morgen erledigt der Sicherstellungsmodus der Cloud — Parameter bstmode. Ich setze ein Boost-Fenster von 01:00 bis 08:00 (bstton1=1, bsttof1=8) mit einem bescheidenen 40 °C-Bedarfsziel, sodass der Tank warm ist, bevor jemand duscht, egal ob die Nacht sonnig war. Ein zweites optionales Fenster gibt es, wenn man will. Tagsüber bleibt es solar-only bis zur hohen Obergrenze; die Nacht garantiert nur einen Boden.
# Lokale Modbus-Writes werden verweigert — Konfig stattdessen über die my-PV Cloud-API steuern.
# 40°C-Morgen-Boost-Fenster 01:00–08:00 setzen (Werte in Zehntelgrad: 400 = 40,0°C)
curl -X PUT "https://api.my-pv.com/api/v1/device/<SERIAL>/setup" \
-H "Authorization: <REDACTED_TOKEN>" \
-H "Content-Type: application/json" \
-d '{"bstmode": 1, "ww1boost": 400, "bstton1": 1, "bsttof1": 8}'
# Die API antwortet sofort "ok"; das Gerät wendet es ~4–6 s später an, also mit GET /setup nachpollen.Den Split sehen, der das Ganze rechtfertigt
Warum sich der Aufwand lohnt, zeigt sich in zwei Sensoren: Solar-Heizleistung und Netz-Heizleistung, getrennt gemeldet. Der ganze Sinn der Überschuss-Umleitung ist, dass die erste Zahl groß und die zweite null ist — Wärme aus Sonne, die ich sonst exportiert hätte, nicht vom Zähler. Daneben beobachte ich die aktuelle Leistungsgrenze und die Boiler-Temperatur, und die Modbus-Reads des Wechselrichters liefern PV-Ertrag, Netzexport und -import sowie das tägliche Laden und Entladen des Akkus. Mit all dem an einem Ort sehe ich in Echtzeit, wie Überschuss zu Warmwasser wird.
Und eine leise Sicherheitsnotiz, die mich dem Setup vertrauen ließ: welche Leistung oder welches Ziel ich auch befehle, der AC·THOR erzwingt ohnehin seine eigenen internen Thermo- und Sicherheitsgrenzen. Software fragt an, Hardware schützt. Genau diese Trennung will man für ein 3-kW-Element in einem Wassertank — meine Automatisierung darf falsch sein, ohne gefährlich zu sein.
Wohin das gehört
Der Warmwasser-Diverter ist eine Scheibe eines Gesamthaus-Energie-Setups, kein Spielzeug. Dieselbe Home-Assistant-Installation fährt raumweise Heizkörperthermostate, die auf ein Absenk-Preset fallen, sobald ein Fensterkontakt öffnet, und einen geplanten hausweiten Nachtmodus um 21:00 mit Tagmodus zurück um 05:00 über ein gutes Dutzend Zonen. Das Warmwasser ist nur zufällig die Ecke, in der der naheliegende lokale Weg eine Sackgasse war und ich eine Cloud-Abhängigkeit schlucken musste, um zu liefern.
Wenn ich die Lektion verdichten müsste: ein Gerät, das ein Protokoll spricht, verspricht nicht, jedes Verb davon zu sprechen. Read ging, Write nicht, und der Fix war nicht, gegen das Gerät zu kämpfen — es war, den billigen, lokalen, internetfreien Read dort zu lassen, wo er hingehört, und nur die Writes auf den einen Transport zu verlegen, den der Hersteller tatsächlich erlaubt. Nicht die Architektur, die ich wollte. Die, die morgens warmes Wasser im Tank hatte.



