Files
audi-app/companion-app/src/api/index.ts
T
tobias e1d570992e Initialer Import: HA-Panel, Design-System, Companion-App
Drei zusammengehörige Teile in einem Repository:

- homeassistant/  Das fertige, im Einsatz befindliche Home-Assistant-Panel
  (panel_custom Custom Element + pyscript-Backend). Echte Fahrzeug- und
  Personendaten (fahrzeugprofil.json, fahrten.jsonl, tankvorgaenge.jsonl,
  Tankbelege) bleiben per .gitignore außen vor; die anonymisierte Vorlage
  fahrzeugprofil.example.json ist mit dabei.

- design-system/  Eigenständige React-Komponentenbibliothek (@audi-dash/ui),
  die die visuelle Sprache des Panels nachbildet - ohne Audi-Markenzeichen
  und ohne die lizenzierte Hausschrift. Dient als Grundlage für Claude
  Design. War bis hierher ein eigenes Repository und ist in dieses
  eingeschmolzen worden.

- companion-app/  Datenschicht der neuen App DataMetric360 (iOS/Android via
  Capacitor, zusätzlich als Iframe im HA-Dashboard). Noch ohne Oberfläche:
  REST- und WebSocket-Zugriff auf Home Assistant plus Warteschlange für
  Änderungen ohne Netz. Ersetzt das eingespritzte hass-Objekt, das nur
  innerhalb des HA-Frontends existiert.

Dazu die Projektdokumentation: SPECIFICATION.md (Ist-Stand des Panels),
COMPANION_APP_ARCHITECTURE.md (Architekturentscheidungen der neuen App),
AUDIT_2026-08-10.md, DESIGN_BRIEF_DATAMETRIC360.md und der ursprüngliche
Bauauftrag.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-11 00:20:05 +02:00

104 lines
3.5 KiB
TypeScript

/* ================================================================
Datenschicht DataMetric360 - Sammelexport
================================================================
Ein Zugang für die gesamte Oberfläche: REST für Lesen und Schreiben,
WebSocket für Push, Warteschlange fürs Funkloch. Welche Bildschirme es
gibt, ist hier bewusst unbekannt - diese Schicht überlebt jeden
Entwurfsdurchlauf unverändert. */
export * from "./types.ts";
export * from "./umgebung.ts";
export * from "./rest.ts";
export * from "./live.ts";
export * from "./warteschlange.ts";
import { ENTITAETEN } from "./types.ts";
import type { Fahrt, Fahrzeugstatus, Profil, Tankvorgang } from "./types.ts";
import { HassRest } from "./rest.ts";
import { HassLive } from "./live.ts";
import { Warteschlange } from "./warteschlange.ts";
/** Bündelt die drei Bausteine und bildet die Fachvorgänge ab, die das alte
Dashboard über hass.callService erledigt hat. */
export class DataMetricApi {
readonly rest: HassRest;
readonly live: HassLive;
readonly warteschlange: Warteschlange;
constructor() {
this.rest = new HassRest();
this.live = new HassLive();
this.warteschlange = new Warteschlange(this.rest);
/* Sobald die Verbindung wieder steht, wartende Änderungen nachliefern -
ohne dass die Oberfläche etwas anstoßen muss. */
this.live.aufVerbindung((zustand) => {
if (zustand === "verbunden") void this.warteschlange.abarbeiten();
});
}
/* ------------------------------------------------------------ Lesen */
profilLesen(): Promise<Profil> {
return this.rest.datenLesen<Profil>(ENTITAETEN.profil);
}
fahrtenLesen(): Promise<Fahrt[]> {
return this.rest.datenLesen<Fahrt[]>(ENTITAETEN.fahrten);
}
tankvorgaengeLesen(): Promise<Tankvorgang[]> {
return this.rest.datenLesen<Tankvorgang[]>(ENTITAETEN.tankvorgaenge);
}
fahrzeugstatusLesen(): Promise<Fahrzeugstatus> {
return this.rest.datenLesen<Fahrzeugstatus>(ENTITAETEN.fahrzeugstatus);
}
/* --------------------------------------------------------- Schreiben
Alle schreibenden Vorgänge laufen über die Warteschlange, nicht direkt
über rest.dienstAufrufen(). Damit verhält sich die App im Funkloch
genauso wie mit Netz - nur eben zeitversetzt. */
profilSchreiben(profil: Profil): Promise<unknown> {
return this.warteschlange.einreihen(
"pyscript",
"audi_dashboard_profil_schreiben",
{ profil_json: JSON.stringify(profil) },
"Fahrzeugdaten speichern",
);
}
belegHochladen(pdfBase64: string, dateiname: string, tankId?: string): Promise<unknown> {
return this.warteschlange.einreihen(
"pyscript",
"audi_dashboard_beleg_hochladen",
{ pdf_base64: pdfBase64, dateiname, ...(tankId ? { tank_id: tankId } : {}) },
`Beleg „${dateiname}" hochladen`,
);
}
fahrtLoeschen(tripId: string): Promise<unknown> {
return this.warteschlange.einreihen(
"pyscript",
"audi_dashboard_fahrt_loeschen",
{ trip_id: tripId },
"Fahrt löschen",
);
}
tankvorgangLoeschen(tankId: string): Promise<unknown> {
return this.warteschlange.einreihen(
"pyscript",
"audi_dashboard_tankvorgang_loeschen",
{ tank_id: tankId },
"Tankvorgang löschen",
);
}
/** Stößt eine sofortige Aktualisierung im Backend an (Pull-to-refresh). */
jetztAktualisieren(): Promise<unknown> {
return this.rest.dienstAufrufen("pyscript", "audi_dashboard_jetzt_aktualisieren");
}
}