1afcad25d3
Das Dokument beschrieb bisher nur die kuenftige Integration und liess offen, wie eine Installation heute ablaeuft. Neuer Abschnitt 1a: was install.sh abnimmt, die drei Eigenschaften, auf die es dabei ankommt (mehrfach ausfuehrbar, vorhandene Daten unangetastet, Sicherung der configuration.yaml), und eine Tabelle, was danach von Hand bleibt - jeweils mit der Spalte, was davon die Integration aufloest. Dazu zwei Stellen nachgezogen: die Erstinstallation der Integration laesst sich genauso automatisieren, das Skript schrumpft dann auf einen Kopiervorgang; und der erste Punkt der Reihenfolge (die drei Setup-Fehler) ist seit dem 13.08. erledigt. Ausserdem ein toter Verweis auf UMSETZUNGSPLAN.md korrigiert - die Datei liegt nur im Feature-Branch.
303 lines
16 KiB
Markdown
303 lines
16 KiB
Markdown
# DataMetric360 als native Home-Assistant-Integration
|
|
|
|
**Stand: 2026-08-13.** Vorhaben: Das HA-Backend von „pyscript-Skripte plus Dateien kopieren" auf
|
|
eine **eigene Integration mit Konfigurationsdialog** umstellen — Einrichtung vollständig über die
|
|
Oberfläche, kein YAML, Updates als Knopfdruck in HAs eigener Update-Liste.
|
|
|
|
Alle unten genannten Schnittstellen sind **an der laufenden Instanz geprüft** (Home Assistant
|
|
2026.8.1 im Container `audi_ha_test`), nicht aus der Dokumentation abgeschrieben. Die geprüften
|
|
Signaturen stehen jeweils dabei.
|
|
|
|
Verwandte Dokumente: `homeassistant/install.sh` (der heutige, automatisierte Weg — siehe §1a),
|
|
`homeassistant/INSTALL.md` (ausführliche Handanleitung), `REVIEW_main_2026-08-13.md` (die Fehler,
|
|
die hier konstruktionsbedingt verschwinden). Im Branch `umsetzung-datametric360` liegt zusätzlich
|
|
`UMSETZUNGSPLAN.md` mit dem Phasenplan der Companion-App.
|
|
|
|
---
|
|
|
|
## 1. Was heute nötig ist — und was davon bleibt
|
|
|
|
| Heute | Nach der Umstellung |
|
|
|---|---|
|
|
| HACS installieren, darüber pyscript | entfällt |
|
|
| drei Ordner nach `/config/` kopieren (`pyscript/`, `data/`, `www/`) | einmalig **ein** Ordner nach `custom_components/` |
|
|
| zwei Blöcke in `configuration.yaml` einfügen | entfällt vollständig |
|
|
| Entity-IDs zuordnen | bleibt, aber als HA-Konfigurationsdialog statt als selbstgebautes Menü |
|
|
| langlebiges Token in `audi_dashboard/ha_token.txt` ablegen | **entfällt ersatzlos** |
|
|
| `pip install pypdf` von Hand | entfällt (`manifest.json` → `requirements`) |
|
|
| Neustart | einmalig, wie bei jeder Integration |
|
|
| Updates über `update.ps1` per Samba | Knopfdruck in *Einstellungen → System → Updates* |
|
|
|
|
## 1a. Was heute schon geht: `install.sh`
|
|
|
|
Solange die Integration nicht steht, erledigt **`homeassistant/install.sh`** die linke Spalte der
|
|
Tabelle oben in einem Aufruf. Am einfachsten direkt auf der Instanz, im Add-on *Terminal & SSH*:
|
|
|
|
```bash
|
|
bash <(curl -fsSL https://gitea.nothaft.cloud/paul/audi-app/raw/branch/main/homeassistant/install.sh)
|
|
```
|
|
|
|
Bei privatem Repository mit Zugangstoken in der URL (`--repo https://<nutzer>:<token>@…`);
|
|
von einem Rechner aus alternativ `--ziel /Volumes/config --von ~/Development/audi-app`.
|
|
`--hilfe` zeigt alle Optionen.
|
|
|
|
**Was es abnimmt:** pyscript installieren (neueste Version von GitHub), `pyscript/` und `www/`
|
|
einspielen, den Belegleser mitliefern (er liegt in `data/`, ist aber Code), fehlende Datenbestände
|
|
aus der Vorlage anlegen, den Abschnitt in die `configuration.yaml` eintragen und `UPDATE_REPO_URL`
|
|
setzen — damit funktioniert „Update suchen" in der App, und weitere Updates sind ein Knopfdruck
|
|
statt eines Kopiervorgangs über Samba.
|
|
|
|
**Drei Eigenschaften, auf die es dabei ankommt:**
|
|
|
|
- **Mehrfach ausführbar.** Der Abschnitt in der `configuration.yaml` steht zwischen Markern und wird
|
|
beim zweiten Lauf ersetzt statt angehängt. Stehen `pyscript:` oder `panel_custom:` bereits
|
|
außerhalb dieses Abschnitts, weist das Skript darauf hin, statt einen doppelten Schlüssel zu
|
|
erzeugen.
|
|
- **Vorhandene Daten bleiben unangetastet.** Fahrzeugprofil, Fahrten, Tankvorgänge und ein bereits
|
|
hinterlegtes Token werden nie überschrieben, nur Fehlendes wird angelegt.
|
|
- **Die `configuration.yaml` wird vor jeder Änderung mit Zeitstempel gesichert.**
|
|
|
|
Geprüft gegen eine frische Home-Assistant-Instanz im Container: HA startet ohne
|
|
Konfigurationsfehler, alle neun pyscript-Entitäten werden veröffentlicht, das Panel ist als
|
|
„Mein Audi" mit `mdi:car-sports` unter `/audi-dashboard-panel` registriert und rendert mit fünf
|
|
Tabs. Ein zweiter Durchlauf ließ Profil und Token unverändert und den Konfigurationsabschnitt genau
|
|
einmal stehen.
|
|
|
|
**Was danach von Hand bleibt** — und genau das löst die Integration auf:
|
|
|
|
| Bleibt heute | Warum | Nach der Umstellung |
|
|
|---|---|---|
|
|
| Home Assistant neu starten | `configuration.yaml` hat sich geändert | einmalig beim Hinzufügen der Integration |
|
|
| Zugriffstoken hinterlegen | der Fahrtabschluss liest den Verlauf über die REST-API | entfällt (Recorder direkt, §4) |
|
|
| Sensoren zuordnen | — | bleibt, aber als HA-Konfigurationsdialog (§3) |
|
|
| Fahrzeugdaten eintragen | Profilinhalte, keine Installation | bleibt |
|
|
|
|
Das Skript wird durch die Integration also nicht überflüssig, sondern **schrumpft**: Übrig bliebe
|
|
das einmalige Kopieren eines Ordners nach `custom_components/`. Alles andere — YAML, pyscript,
|
|
Token, Datenordner — fällt weg.
|
|
|
|
## 2. Die Bausteine, die HA dafür mitbringt
|
|
|
|
Alle in 2026.8.1 vorhanden und geprüft:
|
|
|
|
| Baustein | Ersetzt |
|
|
|---|---|
|
|
| `EntitySelector` mit `domain`, `device_class`, `multiple`, `reorder`, `exclude_entities` | das gesamte Setup-Menü (`entitaeten.py` + ~800 Zeilen JS) |
|
|
| `panel_custom.async_register_panel(...)` | den `panel_custom`-Block in `configuration.yaml` |
|
|
| `hass.http.async_register_static_paths([StaticPathConfig(url_path, path, cache_headers)])` | Kopieren nach `/config/www/` samt Cache-Busting-Stub |
|
|
| `ConfigEntry.add_update_listener(...)` | den „wirkt erst nach Neustart"-Hinweis bei den drei triggergebundenen Feldern |
|
|
| `async_track_state_change_event` / `async_track_time_interval` / `async_track_time_change` | `@state_trigger` / `@time_trigger("period")` / `@time_trigger("cron")` |
|
|
| `recorder.history.get_significant_states(...)` | den HTTP-Aufruf auf `/api/history/period` **samt Token-Datei** |
|
|
| `hass.async_add_executor_job(...)` | `task.executor(io.open, ...)` |
|
|
| `Store` (`homeassistant.helpers.storage`) | handgeschriebenes JSON-I/O (optional, siehe §6) |
|
|
| `HomeAssistantView` / `websocket_api.async_register_command` | die 16-KB-Attributgrenze für Fahrten und Tankvorgänge |
|
|
| `UpdateEntity` (`INSTALL`, `PROGRESS`, `BACKUP`, `RELEASE_NOTES`) | `updateverwaltung.py` als sichtbare Update-Entität |
|
|
|
|
Der wichtigste Einzelfund: **`EntitySelector` kann `exclude_entities`.** Die Duplikat-Prüfung wäre
|
|
damit keine wegklickbare Warnung mehr, sondern schlicht nicht anwählbar.
|
|
|
|
## 3. Aufbau
|
|
|
|
```
|
|
custom_components/datametric360/
|
|
├── manifest.json Abhängigkeiten, "config_flow": true, Version
|
|
├── const.py Rollen-Tabelle (heute FELDER in entitaeten.py)
|
|
├── config_flow.py Ersteinrichtung + Optionen (die 17 Sensor-Rollen)
|
|
├── __init__.py Dienste, Horcher, Zeitgeber, Panel, statische Dateien
|
|
├── update.py UpdateEntity gegen das eigene Gitea
|
|
├── api.py HomeAssistantView für die Companion-App (später)
|
|
└── www/ audi-dashboard-app.js, .css, badges/ — mitgeliefert
|
|
```
|
|
|
|
### Konfigurationsdialog
|
|
|
|
Statt einer eigenen Katalogverwaltung reicht ein Schema. Skizze für zwei der 17 Rollen:
|
|
|
|
```python
|
|
vol.Schema({
|
|
vol.Required("zuendung_sensor"): EntitySelector(
|
|
EntitySelectorConfig(domain=["binary_sensor"])
|
|
),
|
|
vol.Optional("tuer_sensoren", default=[]): EntitySelector(
|
|
EntitySelectorConfig(domain=["binary_sensor"], device_class=["door"],
|
|
multiple=True, reorder=True)
|
|
),
|
|
})
|
|
```
|
|
|
|
Suche, Übersetzung, Gerätezuordnung und Mehrfachauswahl kommen von HA. Die vier Türpositionen
|
|
werden zu einer sortierbaren Mehrfachauswahl — die Reihenfolge ersetzt die heutigen festen
|
|
Positionsfelder.
|
|
|
|
### Panel und Dateien
|
|
|
|
```python
|
|
await hass.http.async_register_static_paths([
|
|
StaticPathConfig("/datametric360", hass.config.path("custom_components/datametric360/www"),
|
|
cache_headers=False),
|
|
])
|
|
panel_custom.async_register_panel(
|
|
hass, frontend_url_path="datametric360", webcomponent_name="audi-dashboard-panel",
|
|
sidebar_title="Mein Audi", sidebar_icon="mdi:car-sports",
|
|
module_url=f"/datametric360/audi-dashboard-app.js?v={VERSION}",
|
|
)
|
|
```
|
|
|
|
Die Versionsnummer in der URL macht den Lade-Stub `audi-dashboard-panel.js` und
|
|
`audi-dashboard-version.json` überflüssig — das Cache-Problem löst sich, weil die Integration ihre
|
|
eigene Version kennt.
|
|
|
|
**Das Frontend selbst bleibt unangetastet.** Alle 3.873 Zeilen `audi-dashboard-app.js` laufen
|
|
weiter; nur das Setup-Menü darin wird überflüssig.
|
|
|
|
## 4. Übersetzung der pyscript-Eigenheiten
|
|
|
|
Mechanisch, kein Neuentwurf. Die Fachlogik — Fahrterkennung, Tankerkennung, Beleg-Parser,
|
|
Reifenzähler, Prognose — geht nahezu unverändert mit.
|
|
|
|
| pyscript | Integration |
|
|
|---|---|
|
|
| `@service def audi_dashboard_x()` | `hass.services.async_register(DOMAIN, "x", handler, schema)` |
|
|
| `@state_trigger(f"{SENSOR}")` | `async_track_state_change_event(hass, [sensor], handler)` |
|
|
| `@time_trigger("period(now, 20 seconds)")` | `async_track_time_interval(hass, handler, timedelta(seconds=20))` |
|
|
| `@time_trigger("cron(0 4 * * *)")` | `async_track_time_change(hass, handler, hour=4, minute=0, second=0)` |
|
|
| `task.unique()` + `task.sleep()` | `asyncio.Task` merken und bei Bedarf `cancel()` |
|
|
| `task.executor(io.open, …)` | `hass.async_add_executor_job(...)` |
|
|
| `state.set("pyscript.x", …)` | eigene Entität oder `HomeAssistantView` (siehe §6) |
|
|
| `subprocess.run(shell_beleg_parser.py)` | `pypdf` direkt importieren, in einem Executor-Job |
|
|
| `urllib.request` auf `/api/history/period` | `recorder.history.get_significant_states(...)` |
|
|
|
|
Die letzte Zeile ist die wichtigste: Sie beseitigt **zwei** Befunde auf einmal — den blockierenden
|
|
Aufruf, den HA seit 2026.8 abbricht (siehe `REVIEW_main_2026-08-13.md`, und derselbe Fehler wurde
|
|
im Branch `umsetzung-datametric360` bereits behoben), **und** die Token-Datei im Klartext.
|
|
|
|
## 5. Installation und Updates
|
|
|
|
Hier liegt der eigentliche Knackpunkt, deshalb ausführlich.
|
|
|
|
### HACS scheidet aus
|
|
|
|
HACS spricht ausschließlich mit GitHub. Anfragen, auch GitLab, Gitea oder Codeberg zuzulassen, gibt
|
|
es seit Jahren; umgesetzt sind sie nicht. Das Repository liegt auf `gitea.nothaft.cloud` — HACS ist
|
|
damit kein Weg, außer man spiegelt nach GitHub.
|
|
|
|
### „App" ist der falsche Behälter
|
|
|
|
App-Repositories (früher Add-ons) akzeptieren **beliebige Git-URLs**, auch selbst gehostete; nötig
|
|
ist nur eine `repository.yaml` im Wurzelverzeichnis. Das ist die einzige Stelle, an der Gitea
|
|
nativ mitspielen würde.
|
|
|
|
Inhaltlich passt es trotzdem nicht: Apps sind **Docker-Container** neben Home Assistant. Eine
|
|
Integration muss *im* HA-Prozess laufen, um Entitäten anzulegen, Dienste zu registrieren und ein
|
|
Panel einzuhängen. Eine App kann das nicht.
|
|
|
|
*(Es gäbe den Trick, eine winzige App zu bauen, die nichts weiter tut, als die Integration nach
|
|
`custom_components/` zu schreiben — dann käme auch die Erstinstallation aus dem eigenen Gitea. Ein
|
|
Docker-Container für einen einmaligen Kopiervorgang ist das aus meiner Sicht nicht wert; der
|
|
Vollständigkeit halber notiert.)*
|
|
|
|
### Der native Weg: eigene UpdateEntity
|
|
|
|
`UpdateEntity` lässt die Integration ihre Updates selbst melden — sichtbar in **Einstellungen →
|
|
System → Updates**, derselben Liste wie HA-Kern und Apps. Geprüfte Möglichkeiten:
|
|
|
|
```
|
|
UpdateEntityFeature: INSTALL, SPECIFIC_VERSION, PROGRESS, BACKUP, RELEASE_NOTES
|
|
async_install(version: str | None, backup: bool, **kwargs) -> None
|
|
```
|
|
|
|
`BACKUP` gibt es genau für das, was `updateverwaltung.py` heute von Hand macht: vor dem Einspielen
|
|
den alten Code sichern.
|
|
|
|
**Die Logik ist bereits vorhanden.** `updateverwaltung.py` klont flach, vergleicht Versionen,
|
|
sichert nach `code_backups/` und ersetzt die Dateien. Das wandert nahezu unverändert in
|
|
`async_install()`. Neu sind nur zwei Eigenschaften:
|
|
|
|
```python
|
|
@property
|
|
def installed_version(self) -> str: return VERSION # aus manifest.json
|
|
@property
|
|
def latest_version(self) -> str | None: return self._neueste # aus Gitea
|
|
```
|
|
|
|
Für `latest_version` empfiehlt sich statt der heutigen Zeitstempel-Zahl aus
|
|
`audi-dashboard-version.json` ein **Git-Tag**. Gitea bietet eine GitHub-kompatible Schnittstelle:
|
|
|
|
```
|
|
GET https://gitea.nothaft.cloud/api/v1/repos/paul/audi-app/releases/latest
|
|
```
|
|
|
|
Ein einzelner HTTP-Aufruf, kein Klonen. In HA steht dann „1.4.0 → 1.5.0" statt einer nichtssagenden
|
|
Zahl, und die Release-Beschreibung lässt sich über `release_notes` direkt anzeigen.
|
|
|
|
### Was bleibt
|
|
|
|
**Die Erstinstallation bleibt ein einmaliges Kopieren** des Ordners nach `custom_components/`. Für
|
|
Integrationen aus Nicht-GitHub-Quellen gibt es keinen nativen Installationsweg. Jedes weitere
|
|
Update ist danach ein Knopfdruck.
|
|
|
|
Das lässt sich genauso automatisieren wie heute: `install.sh` (§1a) schrumpft dann auf genau diesen
|
|
einen Kopiervorgang zusammen — kein pyscript, kein YAML, kein Token, keine Datenordner. Wer den
|
|
Aufruf ohnehin schon einmal benutzt hat, merkt vom Wechsel bei der Installation also kaum etwas;
|
|
der Unterschied liegt danach, in der Einrichtung.
|
|
|
|
## 6. Datenhaltung — bewusst zu entscheiden
|
|
|
|
Zwei Möglichkeiten, beide vertretbar:
|
|
|
|
- **`Store`** (`.storage/datametric360`): atomar, versioniert, mit Migrationspfad. Nachteil: für
|
|
Menschen nicht mehr lesbar, und die heutige Sicherung über `backup.py` müsste neu gedacht werden.
|
|
- **JSON-Dateien behalten** unter `/config/audi_dashboard/`, gelesen über Executor-Jobs. Nachteil:
|
|
kein Migrationspfad. Vorteil: Fahrten und Tankvorgänge bleiben von Hand einsehbar und mit den
|
|
bestehenden Sicherungen kompatibel.
|
|
|
|
**Empfehlung:** Dateien behalten. Bei einem Fahrzeugprofil, einem Fahrtenbuch und einer
|
|
Tankhistorie ist Lesbarkeit mehr wert als Migrationskomfort — und die vorhandene Sicherungslogik
|
|
bleibt gültig.
|
|
|
|
Unabhängig davon: Fahrten und Tankvorgänge gehören **nicht** in Entitäts-Attribute (16-KB-Grenze,
|
|
siehe `frontend_veroeffentlichung.py`), sondern hinter einen `HomeAssistantView`. Das ist zugleich
|
|
die saubere Schnittstelle für die Companion-App, die heute Attribut-Blobs auslesen muss.
|
|
|
|
## 7. Was dabei konstruktionsbedingt verschwindet
|
|
|
|
Aus `REVIEW_main_2026-08-13.md`:
|
|
|
|
- **Befund 1** (Speichern mit leerem Katalog löscht alle Zuordnungen) — der Konfigurationsdialog
|
|
validiert serverseitig, ein leeres Formular kommt gar nicht durch.
|
|
- **Befunde 2 und 5** (Zurücksetzen wirkungslos bzw. zu stark) — die `setattr`-Override-Ebene
|
|
entfällt komplett; HA verwaltet Optionen selbst.
|
|
- **Befunde 3 und 4** (falsche Vorbelegung, beliebige Sensoren) — HAs Entity-Picker schlägt nichts
|
|
vor, der Nutzer wählt; `exclude_entities` verhindert Doppelbelegung.
|
|
- **Befund 7** (kaputte `entitaeten.json` legt das Panel lahm) — die Datei existiert nicht mehr.
|
|
- Der Neustart-Zwang bei den drei triggergebundenen Feldern — `add_update_listener` lädt die
|
|
Integration neu und registriert die Horcher dabei mit den neuen Entity-IDs.
|
|
|
|
Nicht gelöst werden dadurch: die Frontend-Befunde (Standort-Dauerfeuer, Barrierefreiheit,
|
|
`.navmarke`) — das Frontend bleibt ja unverändert.
|
|
|
|
## 8. Aufwand und Reihenfolge
|
|
|
|
Realistisch einige Tage, nicht Wochen — der Großteil ist Umschreiben von Dekoratoren.
|
|
|
|
1. ~~Zuerst die drei Setup-Fehler roh fixen.~~ **Erledigt am 2026-08-13** — siehe
|
|
`REVIEW_main_2026-08-13.md`. Die Instanz läuft bis zur Umstellung ohne den Datenverlust-Pfad,
|
|
der Zeitdruck ist damit raus.
|
|
2. **Gerüst aufsetzen:** `manifest.json`, `config_flow.py`, Panel- und Dateiregistrierung. Die
|
|
pyscript-Skripte laufen dabei zunächst unverändert weiter.
|
|
3. **Logik Datei für Datei herüberziehen**, beginnend mit `fahrtabschluss_logik.py` — dort
|
|
verschwinden Token und blockierender Aufruf zuerst.
|
|
4. **`update.py`** gegen das Gitea-Repository.
|
|
5. **`api.py`** für die Companion-App, sobald diese so weit ist.
|
|
|
|
### Was dabei zu beachten ist
|
|
|
|
- **Neuladen im laufenden Betrieb entfällt.** pyscript lädt bei jeder Dateiänderung neu; eine
|
|
Integration braucht einen Neustart oder ein Neuladen. Beim Entwickeln spürbar, im Betrieb egal.
|
|
- **Die Companion-App liest heute `pyscript.audi_dashboard_*`.** Werden daraus eigene Entitäten
|
|
oder ein eigener Endpunkt, ändert sich die Tabelle `ENTITAETEN` in
|
|
`companion-app/src/api/types.ts` — eine Datei, überschaubar.
|
|
- **`shell_beleg_parser.py` wandert mit in die Integration.** Heute liegt er unter
|
|
`/config/audi_dashboard/` und wird von keinem Update-Pfad ausgeliefert (siehe Review) — als Teil
|
|
des Integrationsordners löst sich das mit.
|