Phase 2: Doku an Codestand angleichen, profil_lesen haerten

Doku-Drift behoben: die Statistik-Seite rechnet laengst echt (Behauptung
stand an drei Stellen), INSTALL.md nannte in Schritt 4 vier Variablennamen,
die es nie gab, README erwaehnte die entfernte 97-Prozent-Volltankungsregel
und liess fuenf pyscript-Dateien in der Uebersicht aus. Obsoleter
TODO-Kommentar in belegverarbeitung.py entfernt.

profil_lesen() gibt bei fehlender oder beschaedigter Profildatei None
zurueck statt zu werfen; alle sieben Aufrufstellen fangen den Fall ab.
An der Testinstanz geprueft: Datei entfernt, es folgt eine verstaendliche
Fehlermeldung mit Verweis auf INSTALL.md statt eines Tracebacks pro
Trigger-Durchlauf, Home Assistant laeuft normal weiter.
This commit is contained in:
Paul Nothaft
2026-08-11 10:48:33 +02:00
parent 982bb04c1a
commit 8e268f044d
10 changed files with 64 additions and 16 deletions
+15 -6
View File
@@ -103,10 +103,18 @@ zeigt die Oberfläche „unbekannt" statt eines Werts — kein Absturz):
3. Ebenfalls in Entwicklerwerkzeuge → Zustände suchen:
- Reichweite → `RANGE_SENSOR`
- Türstatus (falls als ein Sammel-Sensor vorhanden) → `DOORS_SENSOR`
- Fensterstatus → `WINDOWS_SENSOR`
- Verriegelung (Domäne `lock.`) → `LOCK_ENTITY`
- Batteriespannung, falls überhaupt vorhanden → `BATTERY_VOLTAGE_SENSOR`
- Türstatus `TUER_SENSOREN` (Liste mit vier Einzelsensoren:
vorne links/rechts, hinten links/rechts)
- Fensterstatus → `FENSTER_SENSOREN` (ebenfalls vier Einzelsensoren)
- Verriegelung → `TUERSCHLOSS_SENSOREN` (vier Einzelsensoren; die
Integration liefert kein `lock.`-Entity)
- Heckklappe und Motorhaube → `HECKKLAPPE_SENSOR`,
`HECKKLAPPENSCHLOSS_SENSOR`, `HAUBE_SENSOR`, `HAUBENSCHLOSS_SENSOR`
- Service-Fälligkeit → `NAECHSTER_OELWECHSEL_SENSOR`,
`OELWECHSEL_STRECKE_SENSOR`, `NAECHSTE_INSPEKTION_SENSOR`,
`INSPEKTION_STRECKE_SENSOR`
- Batteriespannung, falls überhaupt vorhanden → `BATTERIE_SENSOR`
(bei der aktuellen Integration nicht vorhanden, bleibt leer)
4. Datei `pyscript/modules/einstellungen.py` öffnen (Samba: direkt im
Explorer, Studio Code Server: im Editor) und alle gefundenen Werte
@@ -201,8 +209,9 @@ Der `panel_custom`-Eintrag aus Schritt 3 zeigt auf
Prototyp. Das Gerät braucht dafür zusätzlich zur Tailscale-Verbindung
normalen Internetzugang — sonst bleibt die Karte auf der Fahrt- und
Tankvorgang-Detailseite leer.
- **Statistik-Seite** zeigt weiterhin Beispielzahlen aus dem Prototyp,
keine echte Auswertung der eigenen Fahrten — eigenes Arbeitspaket.
- **Statistik-Seite** wertet die eigenen Fahrten und Tankvorgänge echt aus
(Zeiträume, Verbrauch, Tag/Nacht, privat/Arbeitsweg). Sie bleibt nur so
lange leer, wie noch keine Fahrten und Tankungen erfasst sind.
## Schritt 10 — künftige Updates einspielen, ohne die App neu zu bauen
+10 -4
View File
@@ -96,8 +96,6 @@ Datendateien — nie App-Code oder -Konfiguration.
`fahrterkennung.py`.
- **Bilder fehlen** (§7a) — Ordner `www/bilder/` ist noch leer. Layout
springt nicht (Maße sind reserviert), aber die Flächen bleiben leer.
- **Statistik-Seite** zeigt weiterhin Beispielzahlen aus dem Prototyp, keine
echte Auswertung der erfassten Fahrten.
- **Leaflet lädt per CDN**, wie im Prototyp — braucht zusätzlich zu
Tailscale echten Internetzugang auf dem Gerät.
@@ -107,11 +105,14 @@ Datendateien — nie App-Code oder -Konfiguration.
- Zweistufiger Fahrtabschluss per Recorder-Historie-Screening, inklusive
Verkettung direkt anschließender Fahrten
- Reifenzähler per direkter Subtraktion, kein `utility_meter`
- Belegverarbeitung inklusive 97-%-Volltankungsregel und Duplikatserkennung
über `receipt_key`
- Belegverarbeitung mit Duplikatserkennung über `receipt_key` (die zunächst
geplante 97-%-Volltankungsregel wurde per Änderungswunsch entfernt, siehe
Kopfkommentar in `pyscript/belegverarbeitung.py`)
- Manuelle Fallback-Wege für Fahrten und Tankvorgänge ohne Beleg
- Frontend: Übersicht, Fahrtenliste, Navigation zwischen allen Ansichten,
Datenadapter Profil→Oberfläche, Schreibaktionen über `hass.callService`
- Statistik-Seite mit echter Auswertung aus Fahrten und Tankvorgängen
(Zeiträume, Verbrauch, Tag/Nacht, privat/Arbeitsweg)
## Dateiübersicht
@@ -133,8 +134,13 @@ homeassistant/
│ │ └── frontend_veroeffentlichung.py Zustände fürs Frontend (§10 Punkt 7 Ersatz)
│ ├── fahrterkennung.py §7.1
│ ├── fahrtabschluss.py §7.2 (Trigger-Registrierung)
│ ├── tankerkennung.py §7.4 (automatische Erkennung am Füllstandsanstieg)
│ ├── reifenzaehler.py §7.5
│ ├── belegverarbeitung.py §7.4, §7.7
│ ├── batterieverlauf.py 12-V-Spannung, Tagesminimum/-maximum
│ ├── bilderverwaltung.py Upload/Löschen der Fahrzeugfotos
│ ├── backup.py tägliche Sicherung und Wiederherstellung
│ ├── updateverwaltung.py Update suchen und installieren
│ └── frontend_api.py Lese-/Schreib-Anbindung fürs Frontend
├── www/
│ ├── audi-dashboard-panel.js Lade-Stub (zeigt configuration.yaml hierher), ändert sich kaum
+4
View File
@@ -44,6 +44,8 @@ def _backup_jetzt():
def _letztes_backup_vermerken():
p = profil.profil_lesen()
if p is None:
return
p.setdefault("einstellungen", {})["letztes_backup"] = datetime.datetime.now().isoformat()
profil.profil_schreiben(p)
frontend_veroeffentlichung.profil_veroeffentlichen()
@@ -60,6 +62,8 @@ def audi_dashboard_backup_jetzt():
@time_trigger("cron(0 4 * * *)")
def _automatisches_backup():
p = profil.profil_lesen()
if p is None:
return
intervall = p.get("einstellungen", {}).get("backup_intervall", "aus")
heute = datetime.date.today()
faellig = (
+1 -1
View File
@@ -38,7 +38,7 @@ import einstellungen
import frontend_veroeffentlichung
import profil
PARSER_SKRIPT = "/config/audi_dashboard/shell_beleg_parser.py" # TODO: Datei ablegen
PARSER_SKRIPT = "/config/audi_dashboard/shell_beleg_parser.py"
# --------------------------------------------------------------- Hilfsfunktionen
+4
View File
@@ -34,11 +34,15 @@ _fahrt_start_ts = None
def _profil_wlan_name():
p = profil.profil_lesen()
if p is None:
return None
return p.get("fahrzeug", {}).get("wlan_name")
def _pausenzeit_sekunden():
p = profil.profil_lesen()
if p is None:
return 15 * 60
return p.get("einstellungen", {}).get("fahrten_pausenzeit_min", 15) * 60
@@ -24,7 +24,10 @@ import profil
def profil_veroeffentlichen():
state.set("pyscript.audi_dashboard_profil", "aktuell", new_attributes={"daten": profil.profil_lesen()})
daten = profil.profil_lesen()
if daten is None:
return
state.set("pyscript.audi_dashboard_profil", "aktuell", new_attributes={"daten": daten})
def fahrten_veroeffentlichen():
+20 -1
View File
@@ -49,10 +49,29 @@ def ordner_sicherstellen():
# ----------------------------------------------------------- Fahrzeugprofil
def profil_lesen():
"""Liest das Fahrzeugprofil, oder None wenn es fehlt bzw. beschädigt ist.
Ohne diese Prüfung reißt eine fehlende Datei (Installation unvollständig,
siehe INSTALL.md Schritt 2) jeden Trigger und jeden Service mit, der das
Profil braucht bei laufenden Zeittriggern also im Minutentakt. Jeder
Aufrufer muss den None-Fall abfangen."""
if not os.path.exists(PROFIL_PFAD):
log.error(
f"audi_dashboard: {PROFIL_PFAD} fehlt. Siehe INSTALL.md Schritt 2 — "
"bis dahin bleiben alle Funktionen aus, die das Profil brauchen."
)
return None
f = task.executor(io.open, PROFIL_PFAD, "r")
inhalt = f.read()
f.close()
return json.loads(inhalt)
try:
return json.loads(inhalt)
except ValueError as fehler:
log.error(
f"audi_dashboard: {PROFIL_PFAD} ist kein gültiges JSON ({fehler}). "
"Letztes Backup aus audi_dashboard/backups/ zurückspielen."
)
return None
def profil_schreiben(profil):
+2
View File
@@ -60,6 +60,8 @@ def _fortschreiben_und_veroeffentlichen(neuer_km, neuer_aktiv=None):
Reihenfolge, damit beim Wechseln selbst keine km dem neuen Satz
zugerechnet werden, die eigentlich noch auf dem alten gefahren wurden."""
p = profil.profil_lesen()
if p is None:
return
reifen = p.setdefault("reifen", {})
saetze = reifen.setdefault("saetze", {})
aktiv = reifen.get("aktiv", "sommer")
+4 -1
View File
@@ -57,7 +57,10 @@ def _als_zahl(wert):
def _schwelle_prozent():
p = profil.profil_lesen()
tankvolumen = p.get("fahrzeug", {}).get("tankvolumen_liter") or STANDARD_TANKVOLUMEN_LITER
if p is None:
tankvolumen = STANDARD_TANKVOLUMEN_LITER
else:
tankvolumen = p.get("fahrzeug", {}).get("tankvolumen_liter") or STANDARD_TANKVOLUMEN_LITER
liter_als_prozent = (LITER_SCHWELLE / tankvolumen) * 100
return min(liter_als_prozent, PROZENT_SCHWELLE)
-2
View File
@@ -11,8 +11,6 @@
DOM-Anbindung (Shadow DOM statt document, siehe $ weiter unten).
Bekannte Lücken in diesem Stand:
- Statistik-Seite (vStat/ST) zeigt weiterhin Beispielzahlen, keine echte
Auswertung aus TRIPS/FILLS - eigenes Arbeitspaket.
- Kein Bild-Ordner vorhanden; Bilder fallen auf die im Prototyp bereits
vorgesehenen Platzhalter-Maße zurück (§7a), erscheinen aber als
gebrochene Bilder, bis bilder/ tatsächlich befüllt ist.