diff --git a/README.md b/README.md
index 72596fc..7b41695 100644
--- a/README.md
+++ b/README.md
@@ -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)**.
---
diff --git a/doku/README.md b/doku/README.md
index 64c7690..008fdff 100644
--- a/doku/README.md
+++ b/doku/README.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 |
---
diff --git a/doku/datenbank.md b/doku/datenbank.md
new file mode 100644
index 0000000..7d33d17
--- /dev/null
+++ b/doku/datenbank.md
@@ -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
alle 5 min, Momentanleistung"] -->|"nachts, Rollup"| B["EnergyFlow_hourly
Stunden in kWh"]
+ A --> C["stats_daily
Tageswerte + vorberechnete Größen"]
+ A -. "nach 12 Monaten ausgedünnt" .-> A
+ B --> H2["Historie: Jahr, Jahrzehnt"]
+ A --> H1["Historie: Monat
der laufende Tag fehlt im Archiv"]
+ C --> S["Jahresstatistik
ajax/getStats.php"]
+```
+
+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 |
diff --git a/doku/einstellungen.md b/doku/einstellungen.md
new file mode 100644
index 0000000..66efc34
--- /dev/null
+++ b/doku/einstellungen.md
@@ -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
Reiter aus reiter.php"] --> JS["settings.js · grundriss.js
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
geraeteListe, geraetLoeschen"] --> HM
+ EP --> M5["skodaKeys.php"] --> CONF[["skoda.conf
/volume1/homes/wagner/SolarManager"]]
+```
+
+---
+
+## 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
code, Bezeichnung, Reihenfolge, Bild, Standard"] --> R["Raum
Name, Kürzel, Thermostat-Zweig"]
+ R --> K["Kachel
x/y auf der Zeichenfläche 400 × 300"]
+ R --> G["Geräte
actors.room_id"]
+ E --> A["Automatiken
automations.floor"]
+```
+
+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
der Normalfall"] --> MIX["setzeEinstellungen()"]
+ DB[("homeMesh.energiefluss
nur Abweichungen")] --> 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 |