# 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
Aktion: Benachrichtigungen"] --> R
P["Probe aus den Einstellungen"] -->|"MQTT benachrichtigung/#"| R
R["autoaction_runner.py
BenachrichtigungTransport"] --> PUSH["Push-Dienst
Google, Mozilla, Apple"] --> H["Handy / Browser
sw.js zeigt die Meldung"]
R --> MAIL["SMTP"] --> M["Postfach"]
DB[("homeMesh.push_abos")] --> R
KEY[["push_vapid.json
Schlüsselpaar"]] --> 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` |