diff --git a/README.md b/README.md new file mode 100644 index 0000000..ea51085 --- /dev/null +++ b/README.md @@ -0,0 +1,182 @@ +# 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).