Das Discovery-Modul adressiert die neueren Shellys neu: ihre Messwerte stehen nicht mehr als Feldname zu einer HTTP-Adresse in actor_states, sondern als Topic mit dem Feld in value_path. Geschaltet werden sie weiter ueber HTTP - das Geraet bleibt also ein http://-Aktor, waehrend seine Werte ueber den Broker kommen. Der Runner teilte Messwerte bisher allein nach der Geraete-URL zu. Die umgestellten Werte waeren damit beim HTTP-Transport gelandet, der in der JSON-Antwort nach einem Feld namens "Power_EG/status/em:0" gesucht und nie gefunden haette: die Werte waeren still auf ihrem letzten Stand eingefroren. Zugeteilt wird deshalb je Messwert - was wie ein Topic aussieht (ist_topic()), liest der MQTT-Transport, alles andere geht den alten Weg. Fuer die aelteren Shellys aendert sich nichts, sie koennen kein MQTT und bleiben ganz bei HTTP. Zweitens haette der Runner den Wechsel gar nicht bemerkt. Sein Fingerabdruck fuers Neuladen zaehlt die Zeilen von actor_states, und ein Discovery-Lauf schreibt die Zeilen um, statt neue anzulegen - gleiche Zeilenzahl, keine Reaktion, bis zum naechsten Neustart. Jetzt gehen url und value_path als CRC-Summe mit ein, ebenso die command_url der Kommandos. Nach dem naechsten Suchlauf faellt damit das Polling fuer vier Geraete weg: 34 der 35 HTTP-Messwerte wandern zum Broker, gefragt wird nur noch der Handtuchtrockner. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
SolarManager
Die Hintergrundprozesse des Hauses: Messwerte einsammeln, Wallbox und Heizung
regeln, die Automatiken auswerten. Gegenstück ist das Web-Repo
admin/Smart-Dashboard unter /volume1/web/smart — dort liegt die Anzeige,
hier liegt alles, was ohne offenen Browser weiterlaufen muss.
Beide Seiten reden über zwei Dinge miteinander: den MQTT-Broker für alles Aktuelle und die Datenbanken für alles, was bleiben soll. Kein Prozess ruft einen anderen direkt auf.
Woher die Zahlen kommen
flowchart LR
WR["Wechselrichter<br/><small>GoodWe · OpenDTU · DTU-BI</small>"]
EM3["Stromzähler<br/><small>2× Shelly EM3</small>"]
HEIZ["Heizung"]
GOE["go-eCharger"]
WPILOT["Wattpilot"]
SKODA["MySkoda-API"]
STATION["Wetterstation"]
METEO["Open-Meteo"]
VENTILE["Ventilsteuerungen<br/><small>2× ESP32</small>"]
HAUSGER["Thermostate · Shellys · Schalter<br/><small>melden sich per<br/>Home-Assistant-Discovery</small>"]
MGR["<b>solarManager.py</b><br/><small>gatherModbusData · gatherOpenDTUData<br/>gatherDTUBIData · gatherShellyEM3Data EG/UG<br/>gatherHeaterData · gatherSkodaData<br/>gatherWaterData · charger_goE</small>"]
WPB["<b>wattpilot_bruecke.py</b>"]
WSB["<b>wsMQTTbridge.py</b>"]
RAIN["<b>gatherRainData.py</b>"]
BROKER{{"<b>MQTT-Broker</b>"}}
SOLARLOG[("<b>solarLog</b>")]
WR -->|Modbus · HTTP| MGR
EM3 -->|HTTP| MGR
HEIZ -->|HTTP| MGR
GOE -->|HTTP| MGR
WPILOT -->|WebSocket| MGR
SKODA -->|HTTPS| MGR
WPILOT -->|WebSocket| WPB
STATION -->|WebSocket| WSB
METEO -->|HTTPS| RAIN
SOLARLOG -->|zisterne| MGR
MGR -->|"EnergyFlow · skoda"| SOLARLOG
MGR -->|"solarManager/#"| BROKER
WPB -->|"wattpilot/#"| BROKER
WSB -->|"weatherStation/#"| BROKER
RAIN -->|"Wetter/Regen"| BROKER
GOE -->|"go-eCharger/#"| BROKER
VENTILE -->|"Gartenwasser/#"| BROKER
HAUSGER -->|"Raumtemp/# · Power_EG/#<br/>Power_UG/# · wasser/#"| BROKER
classDef quelle fill:#eef4fb,stroke:#7f9dc0
classDef skript fill:#fff6e5,stroke:#d0a548
classDef speicher fill:#eaf5ee,stroke:#6fa981
class WR,EM3,HEIZ,GOE,WPILOT,SKODA,STATION,METEO,VENTILE,HAUSGER quelle
class MGR,WPB,WSB,RAIN skript
class BROKER,SOLARLOG speicher
Vier Prozesse holen aktiv etwas ab. Alles andere meldet sich von selbst: die Ventilsteuerungen, die Wallbox und jedes Gerät, das Home-Assistant-Discovery spricht, schreiben ohne Umweg auf den Broker. Für deren Messwerte ist also kein Skript zuständig — wer sie sucht, sucht am Gerät, nicht im Quelltext.
solarManager.py ist der Sonderfall: ein Prozess, aber acht
Sammler-Module, jedes für eine Anlage. Sie laufen nicht einzeln, sondern
werden importiert; ihr gemeinsames Ergebnis geht als ein Baum nach
solarManager/#.
Die Zisterne fällt aus der Reihe — ihr Stand steht in solarLog, und
gatherWaterData liest ihn von dort. Die Daten laufen also durch die
Datenbank hindurch von einem Prozess zum nächsten.
Wer sie benutzt
flowchart TB
BROKER{{"<b>MQTT-Broker</b>"}}
SOLARLOG[("<b>solarLog</b><br/><small>Verlauf</small>")]
HOMEMESH[("<b>homeMesh</b><br/><small>Geräte · Automatiken</small>")]
ALARM[("<b>alarm</b><br/><small>Weckzeiten</small>")]
RUNNER["<b>autoaction_runner.py</b><br/><small>SolarManager</small>"]
WECKER["<b>wecker.py</b><br/><small>SolarManager</small>"]
KALENDER["<b>fetch_calendar.py</b><br/><small>SolarManager · Cronjob, jährlich</small>"]
DISCOVERY["<b>device_discovery.py</b><br/><small>Web · von Hand gestartet</small>"]
AJAX["<b>ajax/*.php</b><br/><small>Web</small>"]
BROWSER["<b>js/solar/*.js</b><br/><small>Web · im Browser</small>"]
HAUS["Rollläden · Licht · Schalter<br/><small>Tahoma · WLED · Shelly</small>"]
VENTILE["Ventilsteuerungen"]
FERIEN["openholidaysapi.org"]
BROKER <-->|"Messwerte ↓ Kommandos ↑"| RUNNER
HOMEMESH <-->|"Regelwerk ↓ Verlauf ↑"| RUNNER
RUNNER -.->|"HTTP · WLED · Tahoma"| HAUS
BROKER -.->|"Gartenwasser/…/set"| VENTILE
ALARM --> WECKER
FERIEN --> KALENDER
KALENDER -->|"calendar_days"| HOMEMESH
WECKER -.->|HTTP| HAUS
BROKER -->|"homeassistant/#"| DISCOVERY
HAUS -->|"mDNS · Tahoma"| DISCOVERY
DISCOVERY -->|"Geräte, Messwerte, Befehle"| HOMEMESH
SOLARLOG --> AJAX
HOMEMESH --> AJAX
AJAX <--> BROWSER
AJAX -.->|Kommandos| BROKER
BROKER -->|wss| BROWSER
classDef skript fill:#fff6e5,stroke:#d0a548
classDef speicher fill:#eaf5ee,stroke:#6fa981
classDef geraet fill:#eef4fb,stroke:#7f9dc0
class RUNNER,WECKER,DISCOVERY,AJAX,BROWSER,KALENDER skript
class BROKER,SOLARLOG,HOMEMESH,ALARM speicher
class HAUS,VENTILE,FERIEN geraet
Durchgezogen fließen Daten, gestrichelt gehen Befehle.
Der Browser bekommt seine laufenden Werte direkt vom Broker über
wss://mqtt.nas.el-wa.org, nicht über PHP. Über PHP läuft nur, was der
Broker nicht weiß: der Verlauf aus solarLog und alles aus homeMesh — und
umgekehrt jeder Knopfdruck, denn schalten darf nur der Server.
device_discovery.py ist kein Dauerläufer. Es sucht per mDNS nach Shellys
und WLEDs, hört die Discovery-Nachrichten auf homeassistant/# mit, fragt
die Tahoma-Box und legt daraus die Geräte in homeMesh an. Gestartet wird es
von Hand, wenn sich am Bestand etwas geändert hat.
Wem welche Daten gehören
| Daten | wird gefüllt von | wird gelesen von |
|---|---|---|
solarManager/# |
solarManager.py |
Browser, Runner |
wattpilot/# |
wattpilot_bruecke.py |
Browser, Runner |
weatherStation/# |
wsMQTTbridge.py |
Browser, Runner |
Wetter/Regen |
gatherRainData.py |
Runner, Browser |
go-eCharger/# |
die Wallbox selbst | Browser, Runner |
Gartenwasser/# |
die beiden ESP32 | Browser, Runner |
Raumtemp/#, Power_*/#, wasser/# |
die Geräte selbst | Browser, Runner |
homeassistant/# |
die Geräte selbst | device_discovery.py |
solarLog |
solarManager.py |
ajax/*.php, gatherWaterData |
homeMesh |
device_discovery.py, Runner, Web-Editor |
Runner, ajax/*.php |
alarm |
Web-Oberfläche | wecker.py |
Was wann startet
| Prozess | gestartet von |
|---|---|
solarManager.py |
startSolarServer.sh (Aufgabenplaner, beim Hochfahren) |
autoActions/autoaction_runner.py |
dito |
gatherRainData.py |
dito |
wsMQTTbridge.py |
startMQTTbridge.sh |
wattpilot_bruecke.py |
startWattpilotMQTT.sh |
wecker.py |
startWecker.sh |
autoActions/fetch_calendar.py |
Cronjob, einmal im Jahr |
device_discovery.py (Web-Repo) |
von Hand |
startSolarServer.sh beendet eine schon laufende Instanz, bevor es neu
startet — beim Runner ist das wichtig, zwei Instanzen würden jedes Kommando
doppelt schicken.
Konfiguration
Zugangsdaten und Standort stehen in config.ini (Vorlage:
config.ini.example), gelesen über konfig.py; der Runner hat seine eigene
unter autoActions/. Beide sind per .gitignore ausgenommen — nichts davon
gehört in den Quelltext.
Mehr zum Runner selbst, zum Aufbau einer Automatik und zu den Transporten steht in autoActions/README.md.