Phase 6: Datenanbindung, Ersteinrichtung und Offline-Anzeige

Die App spricht jetzt echt mit Home Assistant. Ein Datenkontext buendelt die
vier Datentoepfe, haengt sich an den WebSocket-Ereignisstrom und reicht
Verbindungszustand und Warteschlange nach aussen; die Screens kennen weder
REST noch WebSocket. Ersteinrichtung prueft die Zugangsdaten, bevor sie sie
speichert, und unterscheidet in der Fehlermeldung zwischen abgelehntem Token
und nicht erreichbarem Server - zwei Faelle, die voellig verschiedene
Reaktionen verlangen.

Zwei echte Fehler, die erst der Test gegen die laufende Instanz zutage
brachte:

1. Die Typen der Datenschicht deklarierten tank_prozent, sicher_abgestellt
   und sicherheit. Das Backend schreibt tankprozent, gesichert und
   sicherheitscheck - die Oberflaeche haette still ueberall undefined
   gelesen, ohne dass irgendetwas fehlgeschlagen waere.
2. Der Profil-Adapter reichte lebende Verweise ins Rohprofil durch. Ein
   Formular haette damit den Rohstand mitveraendert und die Zusage gebrochen,
   den vom Backend fortgeschriebenen Reifen-Kilometerstand nie zu
   ueberschreiben. Abschnitte werden jetzt kopiert.

Ausserdem zwei Parameter-Eigenschaften in der Datenschicht aufgeloest, weil
Node sie im Strip-Modus nicht uebersetzt und genau darueber die Rauchtests
laufen.

Belegt durch neun Pruefungen gegen die laufende Instanz (Lesen, Dienstaufruf,
vollstaendiger Warteschlangenumlauf, WebSocket-Anmeldung) und 21 Unit-Tests.
This commit is contained in:
Paul Nothaft
2026-08-11 11:02:44 +02:00
parent 5c0c2d1898
commit 0103508cea
13 changed files with 1358 additions and 25 deletions
+9 -5
View File
@@ -12,13 +12,17 @@ import type { DatenEntity, HassState } from "./types.ts";
import { zugangLesen, type Zugang } from "./umgebung.ts";
export class ApiFehler extends Error {
constructor(
message: string,
readonly status: number | null,
readonly ursache?: unknown,
) {
/* Eigene Felder statt Parameter-Eigenschaften: Node kann letztere im
Strip-Modus (--experimental-strip-types) nicht übersetzen, und darüber
laufen die Rauchtests dieses Pakets. */
readonly status: number | null;
readonly ursache: unknown;
constructor(message: string, status: number | null, ursache?: unknown) {
super(message);
this.name = "ApiFehler";
this.status = status;
this.ursache = ursache;
}
/** Fehlt oder taugt das Token nicht mehr - dann muss die Oberfläche zurück
+13 -3
View File
@@ -94,12 +94,22 @@ export interface SicherheitsPunkt {
ok: boolean | null;
}
/** Feldnamen an der laufenden Instanz abgelesen, nicht aus der Spezifikation
abgeleitet: frontend_veroeffentlichung.py schreibt "tankprozent",
"gesichert" und "sicherheitscheck" — ein früherer Entwurf dieser Datei
hatte "tank_prozent"/"sicher_abgestellt"/"sicherheit" angenommen, womit
die Oberfläche still überall undefined gelesen hätte. */
export interface Fahrzeugstatus {
km?: number | null;
tank_prozent?: number | null;
tankprozent?: number | null;
reichweite_km?: number | null;
sicher_abgestellt?: boolean | null;
sicherheit?: SicherheitsPunkt[];
batteriespannung?: number | null;
gesichert?: boolean | null;
sicherheitscheck?: SicherheitsPunkt[];
oelwechsel_faellig_ts?: string | null;
oelwechsel_faellig_km?: number | null;
inspektion_faellig_ts?: string | null;
inspektion_faellig_km?: number | null;
[weitere: string]: unknown;
}
+7 -1
View File
@@ -41,8 +41,14 @@ export class Warteschlange {
#geladen = false;
#laeuft = false;
#horcher = new Set<WarteschlangenHorcher>();
/* Bewusst als eigenes Feld statt als Parameter-Eigenschaft im Konstruktor:
Node kann Parameter-Eigenschaften im Strip-Modus (--experimental-strip-types)
nicht übersetzen, und genau darüber laufen die Rauchtests dieses Pakets. */
readonly rest: HassRest;
constructor(private readonly rest: HassRest) {}
constructor(rest: HassRest) {
this.rest = rest;
}
async laden(): Promise<void> {
if (this.#geladen) return;