# 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
```mermaid
flowchart LR
WR["Wechselrichter
GoodWe · OpenDTU · DTU-BI"]
EM3["Stromzähler
2× Shelly EM3"]
HEIZ["Heizung"]
GOE["go-eCharger"]
WPILOT["Wattpilot"]
SKODA["MySkoda-API"]
STATION["Wetterstation"]
METEO["Open-Meteo"]
VENTILE["Ventilsteuerungen
2× ESP32"]
HAUSGER["Thermostate · Shellys · Schalter
melden sich per
Home-Assistant-Discovery"]
MGR["solarManager.py
gatherModbusData · gatherOpenDTUData
gatherDTUBIData · gatherShellyEM3Data EG/UG
gatherHeaterData · gatherSkodaData
gatherWaterData · charger_goE"]
WPB["wattpilot_bruecke.py"]
WSB["wsMQTTbridge.py"]
RAIN["gatherRainData.py"]
BROKER{{"MQTT-Broker"}}
SOLARLOG[("solarLog")]
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/#
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
```mermaid
flowchart TB
BROKER{{"MQTT-Broker"}}
SOLARLOG[("solarLog
Verlauf")]
HOMEMESH[("homeMesh
Geräte · Automatiken")]
ALARM[("alarm
Weckzeiten")]
RUNNER["autoaction_runner.py
SolarManager"]
WECKER["wecker.py
SolarManager"]
KALENDER["fetch_calendar.py
SolarManager · Cronjob, jährlich"]
DISCOVERY["device_discovery.py
Web · von Hand gestartet"]
AJAX["ajax/*.php
Web"]
BROWSER["js/solar/*.js
Web · im Browser"]
HAUS["Rollläden · Licht · Schalter
Tahoma · WLED · Shelly"]
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](autoActions/README.md).