Doku: Datenbanken und Einstellungsseite ergaenzt
datenbank.md: die drei Schemata, wer was schreibt, die drei Stufen der Verdichtung und welche Tabelle fuer welche Auswertung taugt. einstellungen.md: was jeder Reiter speichert, welche Regeln das Modell durchsetzt (feste Etagenkuerzel, Loeschsperren, Schluessel gehen nie zurueck) und was die Seite bewusst nicht kann. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -17,11 +17,12 @@ Dieses Verzeichnis ist zugleich der Web-Root der NAS
|
||||
Der Arbeitsordner *ist* die laufende Anwendung — jede gespeicherte Datei ist
|
||||
sofort live.
|
||||
|
||||
Für zwei Teile reicht diese Landkarte nicht, weil ihr Verhalten aus dem
|
||||
Zusammenspiel von Editor, Datenbank und Runner entsteht. Sie haben eigene,
|
||||
ausführliche Dokumente unter [`doku/`](doku/README.md):
|
||||
**[Automatiken](doku/automatiken.md)** und
|
||||
**[Zeitleiste](doku/zeitleiste.md)**.
|
||||
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)**,
|
||||
**[Datenbanken](doku/datenbank.md)** und
|
||||
**[Einstellungsseite](doku/einstellungen.md)**.
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -13,6 +13,8 @@ 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 |
|
||||
| [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,185 @@
|
||||
# Die Datenbanken
|
||||
|
||||
Drei Schemata auf derselben MariaDB der NAS (Port 3310). Die Trennung ist
|
||||
keine Ordnungsliebe, sondern Zuständigkeit:
|
||||
|
||||
| Schema | Inhalt | Kennzeichen |
|
||||
|---|---|---|
|
||||
| **`homeMesh`** | Geräte, Automatiken, Grundriss — **was gilt** | klein, viele Fremdschlüssel, wird von Hand gepflegt |
|
||||
| **`solarLog`** | Messreihen, Statistik, Preise — **was war** | groß, schreibt fast nur die NAS, wird verdichtet |
|
||||
| **`Logins`** | Passkeys und Einmal-Links | winzig, sicherheitsrelevant |
|
||||
|
||||
Wer von wo verbindet:
|
||||
|
||||
| Aufrufer | Funktion / Datei | Zugang |
|
||||
|---|---|---|
|
||||
| Weboberfläche | `meshDb()` (`restricted/meshdb.php`) | `homeMesh` |
|
||||
| Weboberfläche | `solarDb()` (`restricted/costs.php`) | `solarLog` |
|
||||
| Weboberfläche | `commandDb()` (`restricted/commands.php`) | `homeMesh`, für Kommandos |
|
||||
| Weboberfläche | `checkLogin()` (`helper.php`) | `Logins` |
|
||||
| NAS-Prozesse | `config.ini` im SolarManager | beide, eigene Benutzer |
|
||||
|
||||
Die Zugangsdaten stehen in `restricted/mysql.php` (Web) bzw. `config.ini`
|
||||
(NAS) — beides nicht im Git.
|
||||
|
||||
---
|
||||
|
||||
## 1. `homeMesh` — was gilt
|
||||
|
||||
```mermaid
|
||||
erDiagram
|
||||
floors ||--o{ rooms : "hat"
|
||||
rooms ||--o{ actors : "room_id"
|
||||
actors ||--o{ actor_states : "meldet"
|
||||
actors ||--o{ actor_commands : "kann"
|
||||
actor_commands ||--o{ command_parameters : "nimmt"
|
||||
state_types ||--o{ actor_states : "Datentyp"
|
||||
|
||||
automations ||--o{ automation_conditions : "wenn"
|
||||
automations ||--o{ automation_actions : "dann"
|
||||
automations ||--o{ automation_log : "Protokoll"
|
||||
automation_conditions }o--|| actor_states : "vergleicht"
|
||||
automation_actions }o--|| actor_commands : "schickt"
|
||||
automation_actions ||--o{ automation_action_params : "mit"
|
||||
floors ||--o{ automations : "Reiter"
|
||||
energiefluss }o--|| floors : "keine Beziehung, nur Abweichungen"
|
||||
```
|
||||
|
||||
### Geräte
|
||||
|
||||
| Tabelle | Inhalt | Geschrieben von |
|
||||
|---|---|---|
|
||||
| `actors` | ein Gerät je Zeile. `url` entscheidet den Weg: `mqtt://`, `http://`, `wled://`, `io://`/`rts://`/`internal://`/`ogp://` (Tahoma), `Logic`, `Automatik`. `room_id` verknüpft mit `rooms` | Gerätesuchlauf; `room_id` von Hand (Einstellungen → Geräte) |
|
||||
| `actor_states` | alles Lesbare. `url` ist **entweder** ein MQTT-Topic **oder** ein Feldname im HTTP-JSON, `value_path` der Schlüssel darin, `current_value` der letzte Wert | Suchlauf (Adressen), Runner (Werte) |
|
||||
| `actor_commands`, `command_parameters` | alles Schaltbare und die Parameter dazu (`url` = Stelle im Befehl) | Suchlauf |
|
||||
| `state_types` | Datentypen: `integer`, `float`, `bool`, `string`, `time`, `date`, `datetime`, `deltatime`, `elapsed` … | fest |
|
||||
|
||||
Zwei Dinge, die immer wieder überraschen:
|
||||
|
||||
* **`sensors`/`sensor_states` sind unbenutzt.** Der Suchlauf legt auch reine
|
||||
Messgeräte in `actors`/`actor_states` ab — ein Gerätebegriff für alles.
|
||||
* **Der Suchlauf löscht nie** (`clear_tables = false`). Er schreibt mit
|
||||
`ON DUPLICATE KEY UPDATE` auf den URLs. Ein `TRUNCATE` würde neue IDs
|
||||
vergeben und **alle Automatiken auf falsche Geräte zeigen lassen**.
|
||||
Karteileichen räumt man in den Einstellungen weg
|
||||
(→ [einstellungen.md](einstellungen.md#5-geräte)).
|
||||
|
||||
### Automatiken
|
||||
|
||||
Eigenes Dokument: [automatiken.md](automatiken.md#1-datenmodell). Kurz:
|
||||
`automations` (Rahmen + Laufzustand), `automation_conditions` (Auslöser,
|
||||
`group_no` = UND/ODER), `automation_actions` + `automation_action_params`
|
||||
(was geschickt wird), `automation_log` (30 Tage), `calendar_days` (Ferien
|
||||
und Feiertage, jährlich per `fetch_calendar.py`).
|
||||
|
||||
### Grundriss und Anzeige
|
||||
|
||||
| Tabelle | Inhalt | Besonderheit |
|
||||
|---|---|---|
|
||||
| `floors` | Etagen: `code` (fest — steht in URLs, SVG-Ids und `automations.floor`), Bezeichnung, Reihenfolge, Grundrissbild, Standard-Etage | Schema `homeMesh_grundriss.sql` |
|
||||
| `rooms` | feste Nummer `id`, Etage, Name, `kuerzel` (SVG-Ids), Kachelposition `x`/`y` (leer = keine Kachel), `thermostat` (MQTT-Zweig), `werte` (JSON, leer = Vorgabe) | die Nummer überlebt Umbenennen und Umziehen |
|
||||
| `energiefluss` | **nur Abweichungen** der Solar-Übersicht vom Katalog: `aktiv`, `name`, `x`/`y`, `optionen` | keine Zeile = Katalogwert; Schema `homeMesh_energiefluss.sql` |
|
||||
|
||||
Die drei `view_*`-Objekte sind Lesehilfen aus der Anfangszeit des
|
||||
Gerätesuchlaufs; die Anwendung benutzt sie nicht.
|
||||
|
||||
---
|
||||
|
||||
## 2. `solarLog` — was war
|
||||
|
||||
Stand heute rund 170 MB, verteilt auf wenige große Reihen:
|
||||
|
||||
| Tabelle | Zeilen | Inhalt | Geschrieben von |
|
||||
|---|---:|---|---|
|
||||
| `EnergyFlow` | ~108.000 | Momentanleistungen alle 5 min: PV, Netz, Batterie, Etagen, Heizstab, Wallbox | `solarManager.py` |
|
||||
| `EnergyFlow_hourly` | ~38.000 | Stundenarchiv in kWh — die Langzeitquelle | Rollup-Job |
|
||||
| `stats_daily` | ~1.500 | Tageswerte für die Jahresstatistik, inklusive vorberechneter Größen | Rollup-Job |
|
||||
| `Heater` | ~632.000 | Heizung und Heizstab | `solarManager.py` |
|
||||
| `puffertemp` | ~247.000 | Puffertemperaturen | `solarManager.py` |
|
||||
| `wasser`, `zisterne` | ~300.000 | Wasserzähler und Zisterne | `gatherWaterData.py` |
|
||||
| `windrad`, `kiga_windrad` | ~255.000 | Windräder | `solarManager.py` |
|
||||
| `weatherStation`, `weatherHours`, `weatherDays` | ~220.000 | eigene Wetterstation, Vorhersage | Wetterbrücke, Open-Meteo |
|
||||
| `daylight` | 663 | Sonnenauf- und -untergang je Tag | Vorhersage-Job |
|
||||
| `simPower` | ~82.000 | Ertragsprognose | Prognose-Job |
|
||||
| `byd`, `byd_zellen` | 1.559 / 492 | BYD-Speicher: alle 5 min Ladestand, SOH, Temperaturen; alle 15 min 128 Zellspannungen und 64 Temperaturen | `gatherBYDData.py` |
|
||||
| `skoda`, `skoda_raw`, `skoda_ladepunkte` | ~1.900 | Fahrzeugzustand, Rohantwort, Ladeverlauf im Minutentakt | `gatherSkodaData.py` |
|
||||
| `gridCosts`, `gasCosts`, `fuelCosts` | 13 | Preiszeitreihen | Einstellungsseite |
|
||||
| `car`, `Status`, `actors`, `sensors`, `autoActions*`, `WindradLog` | — | Altlasten aus der Vorgängerfassung | — |
|
||||
|
||||
### Drei Stufen der Verdichtung
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
A["EnergyFlow<br/><small>alle 5 min, Momentanleistung</small>"] -->|"nachts, Rollup"| B["EnergyFlow_hourly<br/><small>Stunden in kWh</small>"]
|
||||
A --> C["stats_daily<br/><small>Tageswerte + vorberechnete Größen</small>"]
|
||||
A -. "nach 12 Monaten ausgedünnt" .-> A
|
||||
B --> H2["Historie: Jahr, Jahrzehnt"]
|
||||
A --> H1["Historie: Monat<br/><small>der laufende Tag fehlt im Archiv</small>"]
|
||||
C --> S["Jahresstatistik<br/><small>ajax/getStats.php</small>"]
|
||||
```
|
||||
|
||||
Wichtig beim Auswerten:
|
||||
|
||||
* **Für einen Monat auf `EnergyFlow` rechnen**, sonst fehlt der laufende Tag
|
||||
(das Archiv wird nur nachts fortgeschrieben).
|
||||
* **Für Jahre auf `EnergyFlow_hourly`**, weil die Rohdaten nach zwölf Monaten
|
||||
ausgedünnt werden — wer dort rechnet, zeigt für alte Jahre zu wenig.
|
||||
* **Für Kennzahlen auf `stats_daily`.** Manches lässt sich aus Tagessummen
|
||||
gar nicht rekonstruieren, etwa der Eigenverbrauch oder der Solaranteil der
|
||||
Wallbox: dafür muss je Messwert eine Bedingung ausgewertet werden.
|
||||
* **Einspeisung kommt aus `gridPfeed`**, dem Zählerregister, nicht aus dem
|
||||
Saldo `gridP`. Innerhalb eines Fensters gibt es Bezug und Einspeisung
|
||||
gleichzeitig; der Saldo löscht kurze Bezüge weg.
|
||||
* **`pv_fehlbetrag_kwh`** gleicht aus, was fehlt, wenn ein Wechselrichter die
|
||||
Verbindung zur OpenDTU verliert: Senken sind dann vollständig gemessen, die
|
||||
Erzeugung nicht — ohne die Korrektur wird der Direktverbrauch negativ.
|
||||
|
||||
Die Preistabellen tragen **einen Stichtag und kein Enddatum**; das Ende holt
|
||||
sich die Auswertung mit `LEAD()` beim nächsten Eintrag. Weil ein Stichtag ein
|
||||
Datum ist, kann ein Tag nie zwei Tarife haben — die Verdichtung auf Tage
|
||||
verliert hier also nichts.
|
||||
|
||||
---
|
||||
|
||||
## 3. `Logins` — Zugang
|
||||
|
||||
| Tabelle | Inhalt |
|
||||
|---|---|
|
||||
| `users` | Passkeys, `authKey`, `lastAuth` |
|
||||
| `addUser` | Einmal-Links, mit denen ein neues Gerät einen Passkey anlegen darf |
|
||||
|
||||
`checkLogin()` in `helper.php` entscheidet in dieser Reihenfolge:
|
||||
|
||||
1. **Heimnetz** (`isLocal()`) — freier Zugang, ohne Anmeldung.
|
||||
2. Gültige Sitzung mit `authKey`, höchstens zwei Tage alt.
|
||||
3. Sonst: Anmeldeseite.
|
||||
|
||||
Daraus folgt die wichtigste Sicherheitsregel dieses Projekts: **Was im
|
||||
Heimnetz ohne Anmeldung sichtbar ist, darf keinen Schlüssel preisgeben.**
|
||||
Deshalb gehen API-Schlüssel nie an den Browser zurück
|
||||
(→ [einstellungen.md](einstellungen.md#6-fahrzeug)), und deshalb schaltet die
|
||||
Oberfläche Geräte über den Server statt direkt.
|
||||
|
||||
---
|
||||
|
||||
## 4. Pflege und Sicherung
|
||||
|
||||
| Aufgabe | Wer | Takt |
|
||||
|---|---|---|
|
||||
| Rohdaten ausdünnen, Stundenarchiv und Tageswerte fortschreiben | Rollup-Job auf der NAS (`solarlog-rollup.sh`) | nachts |
|
||||
| `automation_log` aufräumen | Runner selbst | täglich, 30 Tage Aufbewahrung |
|
||||
| `calendar_days` füllen | `fetch_calendar.py` | jährlich per Cron |
|
||||
| Datenbanksicherung | `backupDB.sh` | per Cron |
|
||||
| Schema neu aufsetzen | `homeMesh_DB-layout.sql`, `homeMesh_automations.sql`, `homeMesh_grundriss.sql`, `homeMesh_energiefluss.sql` in dieser Reihenfolge, danach die Nachrüstskripte aus `SolarManager/autoActions/` | einmalig |
|
||||
|
||||
---
|
||||
|
||||
## 5. Wo fange ich an, wenn ich …
|
||||
|
||||
| Vorhaben | Ort |
|
||||
|---|---|
|
||||
| … eine neue Messreihe aufzeichnen | Tabelle in `solarLog` anlegen, Schreiber im SolarManager, Leser als `ajax/*.php` |
|
||||
| … eine Kennzahl in die Jahresstatistik aufnehmen | Spalte in `stats_daily` (Rollup) **und** Eintrag in `ajax/getStats.php` |
|
||||
| … wissen, warum eine Zahl in zwei Ansichten abweicht | zuerst prüfen, aus welcher Stufe sie stammt (roh, Stunde, Tag) |
|
||||
| … ein Gerät endgültig loswerden | Einstellungen → Geräte; per SQL nur, wenn keine Automatik darauf zeigt |
|
||||
| … eine zweite Installation aufsetzen | Schemata einspielen, dann Einstellungen → Grundriss und → Übersicht; Inhalte stehen in der Datenbank, nicht im Code |
|
||||
@@ -0,0 +1,224 @@
|
||||
# Die Einstellungsseite
|
||||
|
||||
Alles, was das Haus beschreibt statt es zu programmieren: Preise, Etagen und
|
||||
Räume, die Solar-Übersicht, die Werte auf den Kacheln, die Zuordnung der
|
||||
Geräte und der Zugang zum Fahrzeug. **Was hier eingetragen wird, steht in der
|
||||
Datenbank — nicht im Quelltext.** Eine zweite Installation braucht deshalb
|
||||
keine Codeänderung, sondern einen Nachmittag auf dieser Seite.
|
||||
|
||||
| Reiter | Speichert nach | Modell (PHP) | Maske (JS) |
|
||||
|---|---|---|---|
|
||||
| Preise | `solarLog.gridCosts`, `gasCosts`, `fuelCosts` | `restricted/costs.php` | `settings.js` |
|
||||
| Grundriss | `homeMesh.floors`, `rooms` | `restricted/grundriss.php`, `rooms.php` | `grundriss.js` |
|
||||
| Übersicht | `homeMesh.energiefluss` | `ajax/energiefluss.php` | `energieflussEinstellungen.js` |
|
||||
| Home-Kacheln | `homeMesh.rooms.werte` (JSON) | `restricted/kacheln.php` | `settings.js` |
|
||||
| Geräte | `homeMesh.actors.room_id` | `restricted/automations.php` | `settings.js` |
|
||||
| Fahrzeug | `skoda.conf` (außerhalb des Web-Roots) | `restricted/skodaKeys.php` | `settings.js` |
|
||||
|
||||
Ein Endpunkt für alles: `ajax/settings.php?action=…`. Die Maske prüft nichts,
|
||||
was das Modell nicht auch prüfte — sie zeigt nur die Meldung, die von dort
|
||||
kommt.
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
UI["settings.php<br/><small>Reiter aus reiter.php</small>"] --> JS["settings.js · grundriss.js<br/>energieflussEinstellungen.js"]
|
||||
JS -->|"GET ?action=…"| EP["ajax/settings.php"]
|
||||
JS -->|"POST ?action=…"| EP
|
||||
EP --> M1["costs.php"] --> SL[("solarLog")]
|
||||
EP --> M2["grundriss.php / rooms.php"] --> HM[("homeMesh")]
|
||||
EP --> M3["kacheln.php"] --> HM
|
||||
EP --> M4["automations.php<br/><small>geraeteListe, geraetLoeschen</small>"] --> HM
|
||||
EP --> M5["skodaKeys.php"] --> CONF[["skoda.conf<br/><small>/volume1/homes/wagner/SolarManager</small>"]]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 1. Preise
|
||||
|
||||
Drei Tabellen desselben Zuschnitts: **je Zeile ein Stichtag, kein Enddatum.**
|
||||
Das Ende holt sich die Auswertung beim nächsten Eintrag (`LEAD()`), damit
|
||||
eine Preiserhöhung eine Zeile ist und nicht zwei Datumsfelder, die
|
||||
auseinanderlaufen können.
|
||||
|
||||
| Reihe | Felder | Wofür |
|
||||
|---|---|---|
|
||||
| Strom | Arbeitspreis, Einspeisung (ct/kWh), Grundpreis (€/Jahr) | Jahresstatistik |
|
||||
| Gas | Gaspreis (ct/l), Energieinhalt (kWh/l) | Ersparnis des Heizstabs |
|
||||
| Benzin | Preis (€/l), Verbrauch Verbrenner (l/100 km), Verbrauch Elektro (kWh/100 km) | „Benzin gespart“ |
|
||||
|
||||
Zwei Entscheidungen, die man beim Erweitern kennen sollte:
|
||||
|
||||
* **Gespeichert wird in Euro, eingegeben in Cent** (`faktor` je Feld). Auf
|
||||
der Rechnung stehen 31,63 ct/kWh — niemand tippt gern 0,3163.
|
||||
* **Nicht jedes Feld ist ein Preis.** Energieinhalt und Verbräuche stehen
|
||||
hier, weil sie am Brennstoff und am Fahrzeug hängen, nicht an der Physik.
|
||||
Beim Auto bewusst als zwei bekannte Verbräuche statt als „kWh je Liter“ —
|
||||
diese Zahl steht auf keinem Datenblatt.
|
||||
|
||||
Eine weitere Tabelle desselben Zuschnitts ist ein Eintrag in
|
||||
`preisReihen()`; Endpunkt, Maske und Speicherlogik bleiben unverändert.
|
||||
|
||||
---
|
||||
|
||||
## 2. Grundriss
|
||||
|
||||
Der Reiter pflegt `floors` und `rooms` — und damit gleichzeitig das Menü, die
|
||||
Startseite, die Kacheln, die MQTT-Abos und die Etagen-Auswahl im
|
||||
Automatik-Editor.
|
||||
|
||||
```mermaid
|
||||
flowchart TB
|
||||
E["Etage<br/><small>code, Bezeichnung, Reihenfolge, Bild, Standard</small>"] --> R["Raum<br/><small>Name, Kürzel, Thermostat-Zweig</small>"]
|
||||
R --> K["Kachel<br/><small>x/y auf der Zeichenfläche 400 × 300</small>"]
|
||||
R --> G["Geräte<br/><small>actors.room_id</small>"]
|
||||
E --> A["Automatiken<br/><small>automations.floor</small>"]
|
||||
```
|
||||
|
||||
Regeln, die das Modell durchsetzt:
|
||||
|
||||
* **Das Kürzel einer Etage ist nach dem Anlegen fest.** Es steht in URLs,
|
||||
SVG-Ids und `automations.floor`; bis zu vier Großbuchstaben oder Ziffern.
|
||||
* **Gelöscht wird nur, was leer ist.** Eine Etage mit Räumen oder Automatiken
|
||||
wird abgelehnt — mit der Liste dessen, was im Weg steht. Stilles
|
||||
Mitlöschen wäre Datenverlust.
|
||||
* **Die Raumnummer überlebt alles.** Umbenennen und Etagenwechsel ändern sie
|
||||
nicht, deshalb behalten Geräte, Regler und Automatiken ihren Raum.
|
||||
* **Kein `x`/`y` heißt: keine Kachel.** Die Position wird im Plan gezogen,
|
||||
Maße wie in `home.php` (Fläche 400 × 300, Kachel 24 × 19).
|
||||
* **Thermostat-Zweig** ist der MQTT-Pfad des Raums. Vorschläge kommen aus den
|
||||
Topics, die gerade auf `/Temp[degC]` enden.
|
||||
* **Grundrissbilder**: PNG, JPEG oder WebP, geprüft am Inhalt
|
||||
(`getimagesize`), nicht an der Endung. Hochgeladene landen unter
|
||||
`tiles/grundriss` — dem einen Verzeichnis, in das der Webserver schreiben
|
||||
darf, und bewusst außerhalb von Git: Ein Grundriss ist Inhalt dieses
|
||||
Hauses, nicht Teil der Anwendung. Der Dateiname trägt einen Teil der
|
||||
Prüfsumme, damit ein neues Bild eine neue Adresse bekommt und nicht im
|
||||
Browser-Zwischenspeicher hängen bleibt.
|
||||
|
||||
---
|
||||
|
||||
## 3. Übersicht (Energiefluss)
|
||||
|
||||
Die Solar-Übersicht hat einen **Katalog im Code**
|
||||
(`js/solar/energieflussKatalog.js`) — jeder Kreis genau einmal, mit Name,
|
||||
Symbol, Farbe, Elternkreis, Lage und Ringskala. In der Datenbank steht nur,
|
||||
**was davon abweicht**.
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
KAT["energieflussKatalog.js<br/><small>der Normalfall</small>"] --> MIX["setzeEinstellungen()"]
|
||||
DB[("homeMesh.energiefluss<br/><small>nur Abweichungen</small>")] --> MIX
|
||||
MIX --> BILD["gezeichnete Übersicht"]
|
||||
```
|
||||
|
||||
Änderbar sind: Kreis an/aus, Name, Lage (im Vorschaubild ziehen) und
|
||||
Optionen, etwa welche Einträge von `P_PVn` zu welcher PV-Fläche gehören.
|
||||
„Alles auf Katalog zurücksetzen“ löscht schlicht alle Zeilen.
|
||||
|
||||
Der Vorteil dieser Trennung: Eine neue Installation zeigt sofort etwas
|
||||
Sinnvolles, ohne dass jemand eine Tabelle füllt — und ein Update des Katalogs
|
||||
erreicht alle Installationen, ohne die Anpassungen zu überschreiben.
|
||||
|
||||
> Die Hochkant-Lage (`hochX`/`hochY`) steht nur im Katalog. Wer Kreise
|
||||
> verschiebt, ändert also die Queransicht;
|
||||
> siehe [README](../README.md) → Energiefluss.
|
||||
|
||||
---
|
||||
|
||||
## 4. Home-Kacheln
|
||||
|
||||
Welche Werte auf einer Kachel stehen, steht als JSON in `rooms.werte` —
|
||||
höchstens drei je Kachel. Ohne Eintrag gilt `kachelVorgabe()`: mit
|
||||
Thermostat Soll, Ist und Feuchte, sonst nichts.
|
||||
|
||||
Eine Wertdefinition hat überall dieselbe Form, damit zwischen Maske,
|
||||
Datenbank und Anzeige nichts übersetzt werden muss:
|
||||
|
||||
| Feld | Bedeutung |
|
||||
|---|---|
|
||||
| `topic` | MQTT-Topic, aus dem der Wert kommt |
|
||||
| `pfad` | Schlüssel in einer JSON-Nachricht (die Shelly-Zähler schicken mehrere Werte in einer) |
|
||||
| `wechselrichter`, `feld` | statt `topic`: Summe benannter Wechselrichter unter `solarManager/invertersN` |
|
||||
| `format` | `leistung` (Quelle meldet Watt) oder `leistung_kw` (Quelle meldet Kilowatt) — skaliert selbst zwischen W und kW |
|
||||
| `einheit`, `stellen` | sonst: Einheit dahinter, Nachkommastellen |
|
||||
| `negativ` | Vorzeichen drehen, für verkehrt herum eingebaute Zähler |
|
||||
| `gross` | die Zahl steht groß auf der Kachel |
|
||||
| `icon` | Name eines Bootstrap-Icons ohne `bi-` |
|
||||
|
||||
Gelesen wird das im Browser von `homeSVG.kachelWert()`. Warum die
|
||||
Wechselrichter über ihren **Namen** und nicht über die Nummer gesucht
|
||||
werden: `inverters0` ist eine Position in einer Liste und sagt nichts über
|
||||
das Dach — ändert sich die Reihenfolge, stünde sonst stillschweigend das
|
||||
falsche Dach auf der Kachel.
|
||||
|
||||
---
|
||||
|
||||
## 5. Geräte
|
||||
|
||||
Die Liste zeigt je Gerät: Beschriftung, Art, Raum, einen Raumvorschlag aus
|
||||
dem MQTT-Topic, die Zahl der Messwerte und Kommandos, wann zuletzt etwas
|
||||
gemeldet wurde — **und in welchen Automatiken es steckt**.
|
||||
|
||||
Genau deshalb liegt die Liste im Modell und nicht in der Maske: Wer über das
|
||||
Löschen entscheidet, muss die Verweise kennen.
|
||||
|
||||
| Handlung | Regel |
|
||||
|---|---|
|
||||
| Raum zuordnen | `actors.room_id`; ordnet die Geräteliste im Automatik-Editor und bestimmt, welche Messwerte bei einer Kachel oben stehen |
|
||||
| Gerät löschen | nur, wenn **keine** Automatik darauf zeigt. Sonst Ablehnung mit den Namen der Automatiken. Gelöscht wird von innen nach außen: Parameter, Kommandos, Messwerte, Gerät — in einer Transaktion |
|
||||
| Wieder da? | Steht das Gerät noch im Netz, findet es der nächste Suchlauf wieder. Das ist kein Fehler, sondern sein Zweck |
|
||||
|
||||
Der Suchlauf selbst löscht nie (→ [datenbank.md](datenbank.md#geräte)) —
|
||||
deshalb braucht es diesen Papierkorb überhaupt.
|
||||
|
||||
---
|
||||
|
||||
## 6. Fahrzeug
|
||||
|
||||
Hier werden die Schlüssel der MyŠkoda-API hinterlegt. Sie stehen in
|
||||
`skoda.conf` **außerhalb des Web-Roots**, weil sie auch `gatherSkodaData.py`
|
||||
im SolarManager liest.
|
||||
|
||||
**Ein Schlüssel geht nie an den Browser zurück.** Die Seite steht im
|
||||
Heimnetz ohne Anmeldung offen, und so ein Schlüssel steuert das Fahrzeug von
|
||||
überall auf der Welt — Laden, Klimatisierung, Standort. Die Maske sieht nur,
|
||||
*ob* einer hinterlegt ist und auf welche vier Zeichen er endet. Das genügt,
|
||||
um zwei auseinanderzuhalten, und taugt zu nichts sonst. Geschrieben wird nur
|
||||
in eine Richtung.
|
||||
|
||||
Dazu gehört die Aufteilung des Kontingents (20 Anfragen je Stunde für das
|
||||
ganze Konto, nicht je Schlüssel):
|
||||
|
||||
| Zweck | Anteil |
|
||||
|---|---|
|
||||
| Abruf des Fahrzeugzustands (`gatherSkodaData.py`) | 14 / Stunde |
|
||||
| Befehle aus der Oberfläche (`ajax/skodaCmd.php`) | 4 / Stunde |
|
||||
| Reserve | 2 / Stunde |
|
||||
|
||||
Ein eigener Befehlsschlüssel trennt deshalb nicht die Kontingente, sondern
|
||||
nur, wer womit fragt — er lässt sich einzeln widerrufen.
|
||||
|
||||
---
|
||||
|
||||
## 7. Was die Seite bewusst **nicht** kann
|
||||
|
||||
* **Geräte anlegen.** Das macht der Suchlauf
|
||||
(`restricted/deviceDiscovery/`); von Hand angelegte Geräte hätten keine
|
||||
Verbindung zur Wirklichkeit.
|
||||
* **Automatiken bearbeiten.** Dafür gibt es den Editor auf der Home-Seite
|
||||
(→ [automatiken.md](automatiken.md#2-der-editor)).
|
||||
* **Die Standheizung des Fahrzeugs starten.** Das verlangt die
|
||||
Sicherheits-PIN; sie dauerhaft auf der NAS zu hinterlegen ist eine andere
|
||||
Größenordnung als ein API-Schlüssel und soll bewusst entschieden werden.
|
||||
|
||||
---
|
||||
|
||||
## 8. Wo fange ich an, wenn ich …
|
||||
|
||||
| Vorhaben | Ort |
|
||||
|---|---|
|
||||
| … eine weitere Preistabelle | Eintrag in `preisReihen()` (`restricted/costs.php`) |
|
||||
| … ein neues Feld auf einer Kachel | `kachelWertPruefen()`/`kachelKatalog()` in `kacheln.php` und `homeSVG.kachelWert()` in `homeMQTT.js` |
|
||||
| … eine Etage, einen Raum, ein Grundrissbild | nur diese Seite — kein Code |
|
||||
| … einen weiteren Reiter | `restricted/settings.php` (Fläche über `reiter.php`), Endpunkt in `ajax/settings.php`, Modell in `restricted/`, Maske in `settings.js` |
|
||||
| … einen weiteren Zugangsschlüssel verwalten | `restricted/skodaKeys.php` als Vorbild: Modell kennt die Datei, Maske sieht nur den Stand |
|
||||
Reference in New Issue
Block a user