Doku: Benachrichtigungen beschrieben
Web Push ohne App, E-Mail, das gerechnete Geraet und der zweite Weg ueber MQTT - samt der Stolpersteine (sw.js gehoert ins Wurzelverzeichnis, iPhone erst ab Home-Bildschirm, Endpoint ist das Geheimnis, Schluessel nie neu erzeugen solange Abos daran haengen). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -21,6 +21,7 @@ Für einige Teile reicht diese Landkarte nicht, weil ihr Verhalten aus dem
|
||||
Zusammenspiel mehrerer Prozesse entsteht. Sie haben eigene, ausführliche
|
||||
Dokumente unter [`doku/`](doku/README.md):
|
||||
**[Automatiken](doku/automatiken.md)**, **[Zeitleiste](doku/zeitleiste.md)**,
|
||||
**[Benachrichtigungen](doku/benachrichtigungen.md)**,
|
||||
**[Solar-Übersicht](doku/uebersicht.md)**, **[Datenbanken](doku/datenbank.md)**
|
||||
und **[Einstellungsseite](doku/einstellungen.md)**.
|
||||
|
||||
@@ -196,7 +197,7 @@ genau die Topics, die auf den Kacheln stehen (`tileTopics()` in `rooms.php`).
|
||||
| `history` | `history.php` | `jahresstatistik.js`, `historyMQTT.js` | `energyHistory` (6 ×), `getStats` | — |
|
||||
| `skoda` | `skoda.php` | `skodaMQTT.js` | `skoda.php?was=…`, `skodaCmd.php`, `tile.php` | `solarManager/#` |
|
||||
| `weather` | `weather.php` | `weatherMQTT.js` | nichts — das Meteogramm ist ein fremdes Dokument im `<iframe>` (`js/meteogram.js`) | `weatherStation/#` |
|
||||
| `settings` | `settings.php` | `settings.js`, `grundriss.js`, `energieflussKatalog.js`, `energiefluss.js`, `energieflussEinstellungen.js` | `settings.php?action=…` | — |
|
||||
| `settings` | `settings.php` | `settings.js`, `grundriss.js`, `energieflussKatalog.js`, `energiefluss.js`, `energieflussEinstellungen.js`, `benachrichtigungen.js` | `settings.php?action=…`, `push.php?action=…` | — |
|
||||
| `logs` | `logs.php` | `logs.js` | `logs.php?action=…` | — |
|
||||
| `einfuehrung` | `einfuehrung.php` | `einfuehrung.js` | nichts — Folien und Bilder stehen in der Seite | — |
|
||||
| `anzeige` | `solar.php` im Anzeige-Modus | wie `solar`, dazu `anzeige.js` | wie `solar` | wie `solar` |
|
||||
|
||||
@@ -13,6 +13,7 @@ abzulesen sind.
|
||||
|---|---|
|
||||
| [automatiken.md](automatiken.md) | Die Automatiken (AutoActions): Datenmodell, Editor, Runner, wie eine Bedingung wirklich ausgewertet wird, Verkettung, Sperren, Fehlerbilder |
|
||||
| [zeitleiste.md](zeitleiste.md) | Die Zeitleiste der Automatiken: wie der Server den Tag ausrechnet und wie der Browser ihn zeichnet |
|
||||
| [benachrichtigungen.md](benachrichtigungen.md) | Meldungen aus Automatiken: Web Push ohne App, E-Mail, das Gerät „Benachrichtigungen“ |
|
||||
| [uebersicht.md](uebersicht.md) | Die Solar-Übersicht: Katalog und Renderer, wie aus Watt ein Ring, ein Fluss und ein Füllstand wird |
|
||||
| [datenbank.md](datenbank.md) | Die drei Schemata: wer was schreibt, wie Messreihen verdichtet werden, welche Tabelle man für welche Auswertung nimmt |
|
||||
| [einstellungen.md](einstellungen.md) | Die Einstellungsseite: was jeder Reiter speichert, welche Regeln das Modell durchsetzt und was die Seite bewusst nicht kann |
|
||||
|
||||
@@ -0,0 +1,149 @@
|
||||
# Benachrichtigungen
|
||||
|
||||
Automatiken können melden: als Nachricht auf dem Handy oder als E-Mail.
|
||||
Beides ohne eine eigene App — die Meldung aufs Handy kommt über **Web Push**
|
||||
aus dem Dashboard selbst.
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
A["Automatik<br/><small>Aktion: Benachrichtigungen</small>"] --> R
|
||||
P["Probe aus den Einstellungen"] -->|"MQTT benachrichtigung/#"| R
|
||||
R["autoaction_runner.py<br/><small>BenachrichtigungTransport</small>"] --> PUSH["Push-Dienst<br/><small>Google, Mozilla, Apple</small>"] --> H["Handy / Browser<br/><small>sw.js zeigt die Meldung</small>"]
|
||||
R --> MAIL["SMTP"] --> M["Postfach"]
|
||||
DB[("homeMesh.push_abos")] --> R
|
||||
KEY[["push_vapid.json<br/><small>Schlüsselpaar</small>"]] --> R
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 1. Das Gerät „Benachrichtigungen“
|
||||
|
||||
Ein gerechnetes Gerät wie „Zeitpunkt“: Es hängt an keinem Netzwerkanschluss,
|
||||
sondern bündelt Kommandos, die der Runner selbst ausführt. Angelegt wird es
|
||||
vom Gerätesuchlauf (`benachrichtigung_module.py`), Aktor-URL
|
||||
`Benachrichtigung`.
|
||||
|
||||
| Kommando | `command_url` | Parameter |
|
||||
|---|---|---|
|
||||
| Nachricht aufs Handy | `push` | Titel, Text |
|
||||
| E-Mail | `mail` | Betreff, Text |
|
||||
|
||||
**Warum als Gerät und nicht als eigener Aktionstyp?** Weil der Editor, die
|
||||
Übersicht und die Zeitleiste damit nichts Neues lernen müssen: Für sie ist es
|
||||
ein Gerät mit Kommandos wie jedes andere. Eine Meldung ist deshalb eine
|
||||
gewöhnliche Aktion — sie lässt sich mit anderen Aktionen mischen, im Rahmen
|
||||
begrenzen und verketten.
|
||||
|
||||
**Keine Empfängerliste je Automatik.** Wer eine Meldung bekommt, steht an
|
||||
einer Stelle: die Handys in `homeMesh.push_abos`, die Mailadressen in der
|
||||
`config.ini` des Runners. Ein Empfängerfeld je Aktion wäre eine zweite
|
||||
Liste, die mit der ersten auseinanderläuft.
|
||||
|
||||
---
|
||||
|
||||
## 2. Web Push — ohne App
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant B as Browser (Handy)
|
||||
participant W as ajax/push.php
|
||||
participant DB as homeMesh.push_abos
|
||||
participant R as Runner
|
||||
participant D as Push-Dienst
|
||||
|
||||
B->>B: sw.js registrieren, Erlaubnis holen
|
||||
B->>D: subscribe(VAPID-Public-Key)
|
||||
D-->>B: Abo (endpoint + 2 Schlüssel)
|
||||
B->>W: POST ?action=anmelden
|
||||
W->>DB: INSERT … ON DUPLICATE KEY UPDATE
|
||||
Note over R: Automatik löst aus
|
||||
R->>DB: Abos lesen
|
||||
R->>D: verschlüsseltes Paket, VAPID-signiert
|
||||
D->>B: push-Ereignis → showNotification()
|
||||
```
|
||||
|
||||
Drei Teile, die zusammengehören:
|
||||
|
||||
| Teil | Wo | Aufgabe |
|
||||
|---|---|---|
|
||||
| Service Worker | `sw.js` (Wurzelverzeichnis!) | nimmt das `push`-Ereignis an und zeigt die Meldung; ein Klick holt die Seite nach vorn |
|
||||
| Abo | `homeMesh.push_abos` | Endpoint plus zwei Schlüssel je Gerät |
|
||||
| Schlüsselpaar | `SolarManager/push_vapid.json` | der private Teil unterschreibt jede Meldung, der öffentliche steckt im Abo |
|
||||
|
||||
Wissenswertes:
|
||||
|
||||
* **`sw.js` muss im Wurzelverzeichnis liegen.** Ein Service Worker gilt nur
|
||||
für Adressen unterhalb seines eigenen Ortes.
|
||||
* **Er speichert nichts zwischen.** Das Dashboard lebt von aktuellen Werten;
|
||||
ein Cache wäre hier keine Beschleunigung, sondern eine Fehlerquelle.
|
||||
* **iPhone:** Safari erlaubt Web Push erst, wenn die Seite über
|
||||
„Teilen ▸ Zum Home-Bildschirm“ abgelegt und von dort geöffnet wurde.
|
||||
* **Das Abo gehört dem Browser, nicht dem Konto.** Jedes Gerät meldet sich
|
||||
einzeln an; in den Einstellungen stehen alle nebeneinander.
|
||||
* **Der Endpoint ist das Geheimnis.** Wer ihn hat, kann dem Gerät schreiben —
|
||||
deshalb geht er nie an den Browser zurück, auch nicht in der Liste.
|
||||
* **Tote Abos räumt der Runner selbst weg:** Antwortet der Dienst mit 404
|
||||
oder 410, gibt es das Gerät nicht mehr.
|
||||
* Der Schlüssel wird **nie neu erzeugt**, solange Abos daran hängen —
|
||||
`vapid_erzeugen.py` überschreibt eine vorhandene Datei nicht.
|
||||
|
||||
---
|
||||
|
||||
## 3. E-Mail
|
||||
|
||||
Ein gewöhnlicher SMTP-Versand aus dem Runner, eingetragen in dessen
|
||||
`config.ini`:
|
||||
|
||||
```ini
|
||||
[mail]
|
||||
server = smtp.example.net
|
||||
port = 587 ; 587 = STARTTLS, 465 = von Anfang an verschlüsselt
|
||||
benutzer =
|
||||
passwort =
|
||||
von =
|
||||
an = einer@example.net, zweiter@example.net
|
||||
```
|
||||
|
||||
Unverschlüsselten Versand gibt es nicht: Bei 465 wird die Verbindung
|
||||
verschlüsselt aufgebaut, sonst mit STARTTLS gewechselt. Fehlen Server oder
|
||||
Empfänger, meldet die Automatik einen Fehler ins Protokoll (`automation_log`),
|
||||
statt still nichts zu tun.
|
||||
|
||||
---
|
||||
|
||||
## 4. Der zweite Weg: MQTT
|
||||
|
||||
Der Runner hört zusätzlich auf `benachrichtigung/#`:
|
||||
|
||||
| Topic | Nutzlast | Wirkung |
|
||||
|---|---|---|
|
||||
| `benachrichtigung/push` | `{"titel": "...", "text": "..."}` | Meldung an alle Geräte |
|
||||
| `benachrichtigung/mail` | `{"betreff": "...", "text": "..."}` | Mail an die eingetragenen Empfänger |
|
||||
|
||||
Darüber schickt die **Probe** aus den Einstellungen — die Weboberfläche muss
|
||||
so nichts verschlüsseln und braucht den privaten Schlüssel nie. Denselben Weg
|
||||
kann jedes Skript auf der NAS nehmen.
|
||||
|
||||
Zugestellt wird im Haupttakt des Runners, nicht im MQTT-Faden: Eine
|
||||
Datenbankverbindung gehört einem Faden, und der Rückruf des Brokers läuft in
|
||||
einem eigenen.
|
||||
|
||||
---
|
||||
|
||||
## 5. Die Pakete
|
||||
|
||||
`pywebpush`, `py_vapid` und `http_ece` liegen wie alle übrigen Fremdpakete
|
||||
**im SolarManager-Ordner**, nicht in `site-packages`. Weil der Runner aus
|
||||
`autoActions/` startet, hängt `transports.py` diesen Ordner an den Suchpfad.
|
||||
|
||||
---
|
||||
|
||||
## 6. Wo fange ich an, wenn ich …
|
||||
|
||||
| Vorhaben | Ort |
|
||||
|---|---|
|
||||
| … einen weiteren Kanal (Telegram, Signal, WhatsApp) | ein Kommando in `benachrichtigung_module.py`, ein `elif` in `BenachrichtigungTransport.senden()` |
|
||||
| … den Text einer Meldung ändern | in der Automatik selbst — Titel und Text sind Parameter der Aktion |
|
||||
| … ein Handy loswerden | Einstellungen → Meldungen → Papierkorb; oder auf dem Gerät selbst abmelden |
|
||||
| … den Schlüssel erneuern | `push_vapid.json` löschen, `vapid_erzeugen.py` laufen lassen, danach müssen sich **alle** Geräte neu anmelden |
|
||||
| … nachsehen, warum nichts ankam | `autoActions.log` auf der NAS und die Spalten `zuletzt`/`fehler` in `push_abos` |
|
||||
Reference in New Issue
Block a user