From 0fd73499d9e30762a25e24fc925d0a54106f94c3 Mon Sep 17 00:00:00 2001 From: "m0@nas" Date: Wed, 9 Sep 2026 00:28:42 +0200 Subject: [PATCH] README: Aufbau und Datenwege des Dashboards Das Repo hatte keine. Wer hier etwas suchte, musste sich von index.php aus durchhangeln - und die Frage "welches Skript laedt eigentlich welche Daten" war nur zu beantworten, indem man alle elf durchsah. Die Datei beschreibt den Weg einer Anfrage (Router, Anmeldung, Vorlage, Skripte), die Trennung der beiden Datenwege (laufende Werte ueber den Broker, alles mit Verlauf ueber ajax/), die Seitenkarte mit Skripten, Endpunkten und Topics je Seite, die JavaScript- und PHP-Schicht mit ihren Zustaendigkeiten, die drei Datenbanken und vier Bausteine im Detail: Home-Kacheln, Raum-Modal, Automatiken, Geraete-Erkennung. Dazu sieben Mermaid-Diagramme - Ablauf, Datenwege, Abhaengigkeiten, Include-Graph, Tabellen, Kachelaufloesung, Kommandoweg. Am Ende zwei Tabellen fuer den taeglichen Gebrauch: "wo fange ich an, wenn ich X aendern will" und die Pruefbefehle (php82 -l, node --check, readDB). Gegenstueck ist die README im SolarManager-Repo; beide verweisen aufeinander, damit man von jeder Seite aus zur anderen findet. Co-Authored-By: Claude Opus 5 --- README.md | 641 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 641 insertions(+) create mode 100644 README.md diff --git a/README.md b/README.md new file mode 100644 index 0000000..512a151 --- /dev/null +++ b/README.md @@ -0,0 +1,641 @@ +# Smart-Dashboard + +Die Anzeige und Bedienung des Hauses: Grundrisse mit Messwerten, Solar- und +Verbrauchsverläufe, das Fahrzeug, die Automatiken, die Einstellungen. Alles, +was einen offenen Browser braucht, liegt hier. + +Gegenstück ist das Repo **`admin/SolarManager`** unter +`/volume1/homes/wagner/SolarManager` — dort laufen die Hintergrundprozesse, +die Messwerte einsammeln und die Automatiken ausführen. Beide Seiten reden +über zwei Dinge miteinander und nie direkt: den **MQTT-Broker** für alles +Aktuelle, die **Datenbanken** für alles, was bleiben soll. Wer wissen will, +woher eine Zahl ursprünglich kommt, liest die README dort; hier steht, was +mit ihr im Browser passiert. + +Dieses Verzeichnis ist zugleich der Web-Root der NAS +(`/volume1/web/smart`, ausgeliefert unter `https://nas.el-wa.org/smart`). +Der Arbeitsordner *ist* die laufende Anwendung — jede gespeicherte Datei ist +sofort live. + +--- + +## Inhalt + +1. [Auf einen Blick](#auf-einen-blick) +2. [Wie eine Seite entsteht](#wie-eine-seite-entsteht) +3. [Zwei Wege für Daten](#zwei-wege-für-daten) +4. [Die Seitenkarte](#die-seitenkarte) +5. [Die JavaScript-Schicht](#die-javascript-schicht) +6. [Die PHP-Schicht](#die-php-schicht) +7. [Die Datenbanken](#die-datenbanken) +8. [Vier Bausteine im Detail](#vier-bausteine-im-detail) +9. [Wiederkehrende Bauteile und Konventionen](#wiederkehrende-bauteile-und-konventionen) +10. [Wo fange ich an, wenn ich … ändern will](#wo-fange-ich-an-wenn-ich--ändern-will) +11. [Entwickeln und Prüfen](#entwickeln-und-prüfen) + +--- + +## Auf einen Blick + +``` +smart/ +├── index.php Router: Anmeldung prüfen, Seite auswählen, drei Includes +├── helper.php Session, Heimnetzerkennung, checkLogin(), httpGetJson() +├── authServer.php Passkeys (WebAuthn): Registrierung und Anmeldung +├── addUser.php Einmal-Link zum Anlegen eines neuen Schlüssels +├── .htaccess sperrt .git, *.py, *.sql, *.ini … für den Browser +│ +├── restricted/ Vorlagen (was man sieht) und Modelle (was es weiß) +│ ├── header.php footer.php Rahmen jeder Seite, Menü, Skript-Einbindung +│ ├── home.php solar.php heat.php history.php +│ ├── skoda.php weather.php settings.php logs.php ← die acht Seitenvorlagen +│ ├── rooms.php Räume, Etagen, Kachelwerte, Symbole +│ ├── costs.php Preistabellen + solarDb() +│ ├── automations.php Automatiken, Gerätekatalog + meshDb() +│ ├── kacheln.php Anzeigewerte der Home-Kacheln (Modell zur Maske) +│ ├── roomControls.php Bedienelemente des Raum-Modals +│ ├── commands.php ein Kommando tatsächlich abschicken +│ ├── wallboxen.php beide Wallboxen über MQTT +│ ├── logdateien.php Logdateien lesen +│ ├── aussenplan.php Schrägbild des Außengeländes (SVG, gerechnet) +│ ├── reiter.php das eine Reiter-Bauteil der ganzen Seite +│ ├── config.php Zahlen, die an der Wirklichkeit hängen +│ ├── mysql.php Zugangsdaten — NICHT im Git (mysql.php.example) +│ └── deviceDiscovery/ Python: Geräte suchen und in homeMesh eintragen +│ +├── ajax/ Endpunkte. Verteiler, keine Logik (Ausnahmen unten) +├── js/solar/ der eigene JavaScript-Code +├── js/ Fremdbibliotheken, alle lokal (kein CDN) +├── css/solar.css das eigene Stylesheet (Theme + alle Bauteile) +├── assets/img/ Grundriss-Renderings OG/EG/UG/AG, Icons +├── tiles/ Kartenkacheln-Zwischenspeicher (nicht im Git) +└── *.sql Schema-Dateien zum Nachziehen von Hand +``` + +--- + +## Wie eine Seite entsteht + +```mermaid +sequenceDiagram + autonumber + participant B as Browser + participant I as index.php + participant H as helper.php + participant T as Vorlage + Modelle + participant F as footer.php + + B->>I: GET index.php?action=home&floor=OG + I->>H: checkLogin() + H-->>I: Heimnetz? sonst Passkey-Sitzung + Note over I: $pages[...] wählt Vorlage,
Bibliotheken und Seitenskripte + I->>T: include header.php + T-->>B: Kopf und Menü, gebaut aus $floors + I->>T: include restricted/(seite).php + T-->>B: Karten, SVG, eingebettete JSON-Daten + I->>F: include footer.php + F-->>B: Bootstrap, ggf. mqtt.js/Chart.js/Leaflet,
common.js, dann die Seitenskripte + B->>B: Skripte verbinden sich mit Broker und holen per fetch nach +``` + +**Anmeldung.** `helper.php` kennt zwei Wege. Aus dem Heimnetz +(`LOCAL_NETWORKS`, geprüft binär über `inet_pton`, ausschließlich anhand von +`REMOTE_ADDR` — Weiterleitungs-Header sind fälschbar und werden bewusst +ignoriert) ist man ohne Anmeldung drin. Von außen zählt ein Passkey: +`authServer.php` mit der WebAuthn-Bibliothek unter `restricted/WebAuthn`, +Schlüssel in der Datenbank `Logins`. Ein neuer Schlüssel entsteht nur über +einen Einmal-Link (`addUser.php`, Tabelle `addUser`, eine Minute gültig). + +**Die Seitentabelle** in `index.php` ist die einzige Stelle, an der steht, +welche Seite es gibt und was sie braucht: + +```php +"home" => ["template" => "home.php", "mqtt" => true, "charts" => false, + "meteogram" => false, "leaflet" => false, + "scripts" => ["js/solar/autoActionFuncs.js", "js/solar/homeMQTT.js"]], +``` + +`footer.php` lädt daraus die Bibliotheken — Chart.js nur wo gezeichnet wird, +mqtt.js nur wo Livewerte laufen, Leaflet nur auf der Fahrzeugseite. Danach +kommt immer `common.js` und dann die Seitenskripte, jeweils mit +`?v=`, damit ein Browser nie eine alte Fassung behält. + +**Vorlage und Modell sind getrennt.** Eine Seitenvorlage baut Markup und ruft +dafür Funktionen aus den Modelldateien; die Modelldateien enthalten kein +Markup außer dort, wo sie ein Bedienelement zeichnen (`roomControls.php`, +`reiter.php`). Die Endpunkte in `ajax/` benutzen dieselben Modellfunktionen — +so kann eine Maske nicht etwas anderes behaupten als der Endpunkt, der sie +speichert. + +--- + +## Zwei Wege für Daten + +```mermaid +flowchart LR + subgraph Browser + JS["js/solar/*.js"] + SVG["SVG-Grundriss
Diagramme
Masken"] + end + + BROKER{{"MQTT-Broker
wss://mqtt.nas.el-wa.org:443"}} + AJAX["ajax/*.php"] + MODELL["restricted/*.php
Modell-Schicht"] + SOLARLOG[("solarLog
Verlauf")] + HOMEMESH[("homeMesh
Geräte, Automatiken")] + GERAETE["Geräte
Tahoma · WLED · Shelly · Wallboxen"] + + BROKER -->|"laufende Werte"| JS + JS --> SVG + JS <-->|"fetch: Verlauf, Struktur, Masken"| AJAX + AJAX --> MODELL + MODELL --> SOLARLOG + MODELL --> HOMEMESH + MODELL -.->|"Kommandos"| BROKER + MODELL -.->|"HTTP · Tahoma · WLED"| GERAETE + + classDef speicher fill:#eaf5ee,stroke:#6fa981 + classDef code fill:#fff6e5,stroke:#d0a548 + class SOLARLOG,HOMEMESH,BROKER speicher + class JS,AJAX,MODELL code +``` + +Die Regel dahinter, und sie gilt ohne Ausnahme: + +| Was | Weg | Warum | +|---|---|---| +| Aktueller Messwert (Leistung, Temperatur, Ladestand) | **MQTT über `wss`, direkt vom Broker** | Er weiß es ohnehin, und er sagt es von selbst — kein Nachfragen im Takt | +| Verlauf, Statistik, Gerätelisten, Formulare | **`fetch` auf `ajax/*.php`** | Das weiß nur die Datenbank | +| Jeder Knopfdruck (schalten, speichern, Fahrzeugbefehl) | **immer über den Server** | Zugangsdaten dürfen die Seite nicht verlassen, und der Broker ist von außen nicht erreichbar | + +Der Browser abonniert je nach Seite `solarManager/#`, `weatherStation/#`, +`wattpilot/#`, `go-eCharger/#`, `Gartenwasser/#` und — auf der Home-Seite — +genau die Topics, die auf den Kacheln stehen (`tileTopics()` in `rooms.php`). + +--- + +## Die Seitenkarte + +| Seite (`?action=`) | Vorlage | Seitenskripte | holt per fetch | hört auf MQTT | +|---|---|---|---|---| +| `solar` (Vorgabe) | `solar.php` | `jahresstatistik.js`, `solarMQTT.js` | `getProdData`, `getConsData`, `getForecastData`, `getSunrise`, `getStats`, `carEG`, `carOG`, `heater`, `watering` | `solarManager/#`, `weatherStation/#`, `wattpilot/#`, `go-eCharger/#`, `Gartenwasser/#` | +| `home` | `home.php` | `autoActionFuncs.js`, `homeMQTT.js` | `room.php`, `AutoAction.php` | die Topics der Kacheln, `Raumtemp/#` | +| `heat` | `heat.php` | `heatMQTT.js` | `getHeaterData`, `getWaterData`, `getSunrise` | `solarManager/#`, `weatherStation/#`, Wallbox-Topics | +| `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 `