Phase 10 Schritt 7 ist damit erledigt. auslieferung/App.ipa, 2,4 MB, Ad-hoc signiert mit "Apple Distribution: Paul Nothaft", Profil gueltig bis 29.08.2027, genau ein eingetragenes Geraet. Zwei Annahmen von heute frueh waren falsch und sind korrigiert: die bezahlte Mitgliedschaft stuft das bestehende Team hoch, statt ein neues anzulegen (die Kennung bleibt RMACS9VLS4), und der Export als Ad-hoc funktioniert einwandfrei. Neue Falle festgehalten: beim ersten Signieren fragt der Schluesselbund per Dialog um Erlaubnis. Bleibt der unbeantwortet, haengt xcodebuild wortlos und endet mit errSecInternalComponent. Die Diagnose steht in AGENTS.md, weil das Symptom von sich aus nirgendwohin zeigt. Neu: scripts/ios-luftweg.sh erzeugt manifest.plist, Installationsseite und Symbole fuer die Uebertragung ueber die Luft. Es verweigert eine Basis-Adresse ohne https, weil iOS sonst erst auf dem Telefon still scheitert. Offen bleibt der Host, der die Dateien ausliefert.
42 KiB
DataMetric360 — Umsetzungsplan
Stand: 2026-08-11. Dieser Plan führt von heute (fertiges HA-Panel, fertige Datenschicht,
halbfertiger Design-Entwurf) bis zur fertigen DataMetric360-App inklusive aller offenen Punkte aus
AGENTS.md. Er ist bewusst kleinteilig geschrieben, damit jede Phase auch von einem schwächeren
KI-Modell oder in einer frischen Session ohne Vorwissen abgearbeitet werden kann.
Beschlossene Rahmenbedingungen (nicht neu diskutieren):
- Alle Daten liegen in Home Assistant. Es gibt kein eigenes Backend — die App spricht direkt
mit der HA-REST-/WebSocket-API und ruft die vorhandenen
pyscript.audi_dashboard_*-Services auf. - Kein Electron. Fürs iPhone wird die Web-App mit Capacitor verpackt (nur Sideload, nie App Store). Optionaler Zwischenschritt: als PWA auf den Homescreen (Phase 10).
- Die App ersetzt später das HA-Panel; bis dahin bleibt das Panel unangetastet in Betrieb.
Für das ausführende Modell: Arbeitsregeln
- Lies zuerst:
AGENTS.md(Regeln + Stand), dann die Phase, die du bearbeitest — nichts anderes. Hole dir Detailwissen erst, wenn ein Schritt es verlangt (die Schritte nennen die Quelldatei). - Eine Phase pro Session. Arbeite die Schritte in Reihenfolge ab. Jeder Schritt hat eine Prüfung („✅ Fertig wenn"). Erfülle sie, bevor du weitergehst. Wenn eine Prüfung fehlschlägt, behebe das, bevor du den nächsten Schritt beginnst.
- Nach jeder abgeschlossenen Phase: Haken in diesem Plan setzen, betroffene Checkboxen in
AGENTS.mdabhaken, „Last updated" inAGENTS.mdaktualisieren (englisch!), committen mit deutscher Commit-Message. - Harte Verbote:
- Audi-Assets (Schriften, Ringe, Typenschilder) niemals nach
design-system/kopieren oder in irgendetwas einbauen, das veröffentlicht/hochgeladen wird. Quelle für die App:homeassistant/www/unddesign/uploads/. - Keine Schriftgewichte ≥ 600, keine Schatten, keine Verläufe. Nur Gewichte 300/400.
homeassistant/nicht umbauen (nur die explizit genannten Pflege-Schritte in Phase 2).- Keine zusätzlichen npm-Pakete einführen, die der Plan nicht nennt, ohne es zu begründen und
in
AGENTS.mdzu dokumentieren. - Home Assistant niemals direkt ins Internet stellen. Nur die in Phase 12 beschriebenen Wege.
- Audi-Assets (Schriften, Ringe, Typenschilder) niemals nach
- Sprache: Code-Bezeichner, Kommentare, UI-Texte, Commits auf Deutsch (Ausnahme:
AGENTS.mdenglisch;design-system/behält englische Props). - Zahlenformat immer
de-DE(1.234,5). Übernimm die Helferde()/eur()aus dem alten Panel (homeassistant/www/audi-dashboard-app.js), statt eigene zu erfinden.
Phasenübersicht und Abhängigkeiten
| Phase | Inhalt | Hängt ab von | Braucht Hardware/Extern? |
|---|---|---|---|
| 1 | Arbeitsumgebung herstellen und verifizieren | — | nein |
| 2 | Pflege HA-Panel (Doku-Drift, Härtung) | — | nein |
| 3 | Design-Entwurf vervollständigen | — | Claude Design (Besitzer) |
| 4 | Workspace + React-Gerüst in companion-app/ |
1 | nein |
| 5 | App-Shell: Theme, Router, responsives Layout | 4 | nein |
| 6 | Onboarding + Datenanbindung + Offline-UX | 5 | nein |
| 7 | Alle Screens umsetzen | 3, 6 | nein |
| 8 | Audi-Assets einbauen | 7 | nein |
| 9 | Tests (authentifiziert + Komponenten) | 6 | nein |
| 10 | PWA + Capacitor/iOS-Sideload | 7 | erledigt bis auf das Signieren aufs Geraet |
| 11 | HA-Einbettung, Ablösung + Archivierung des Panels | 7–10 | HA-Produktivinstanz |
| 12 | Externer Zugriff: Cloudflare Tunnel + Reverse Proxy | teils unabhängig | Domain/DNS, Besitzer |
| 13 | FMM003: MQTT, Zertifikate, Mapping, Fahrterkennung | Hardware | ja: FMM003 verbaut |
Phasen 2, 3 und 12 (Teilschritte) sind unabhängig und können vorgezogen werden. Phase 13 ist die einzige, die zwingend auf Hardware wartet.
Phase 1 — Arbeitsumgebung herstellen und verifizieren ✅ ERLEDIGT (2026-08-11)
Ergebnis: design-system baut und besteht 21/21, companion-app typecheckt und besteht 7/7. Die verlorene Testinstanz wurde als reproduzierbares Skript neu aufgebaut — siehe
testumgebung/(Abweichung vom Plan, bewusst: das Original ging genau deshalb verloren, weil es nur als Anleitung existierte).
Ziel: Beide npm-Pakete bauen, beide Smoke-Tests laufen, die HA-Testinstanz ist erreichbar.
- Node-Version prüfen:
node --version— muss ≥ 20 sein (companion-app nutzt--experimental-strip-types, ab Node 22 stabil; wenn < 20: mit nvm/Volta Node 22 installieren). cd design-system && npm install && npm run build✅ Fertig wenn:design-system/dist/index.jsunddist/styles.cssexistieren, Build ohne Fehler.npm run smoke(im selben Ordner) ✅ Fertig wenn: 21/21 Fälle „ok" melden.cd ../companion-app && npm install && npm run typecheck✅ Fertig wenn:tsc --noEmitfehlerfrei durchläuft.- HA-Testinstanz prüfen:
curl -s -o /dev/null -w '%{http_code}' http://localhost:18123/api/— erwartet:401(API da, Token fehlt — das ist richtig so). Wenn keine Antwort: Der Docker-Container heißtaudi_ha_test(docker start audi_ha_test). Existiert er nicht, siehehomeassistant/README.md(Testbericht-Abschnitt) für den Aufbau; notfalls Phase-1-Schritt 6 überspringen und in Phase 9 nachholen. npm run smokeincompanion-app/✅ Fertig wenn: 7/7 Prüfungen grün.
Abschluss Phase 1: nichts committen (nur node_modules/dist, beides gitignored).
Phase 2 — Pflege des bestehenden HA-Panels ✅ ERLEDIGT (2026-08-11)
Doku-Drift behoben,
profil_lesen()gehärtet und an der Testinstanz belegt. Offen bleibt nur, was Betriebsdaten sind: Fahrzeugfotos undsteuer.faellig.
Ziel: Doku stimmt wieder mit dem Code überein; ein Crash-Risiko ist beseitigt. Reine Pflege — keine Funktionsänderungen.
- Statistik-Behauptung korrigieren (3 Stellen, gleiche Falschaussage „zeigt Beispielzahlen"):
homeassistant/www/audi-dashboard-app.jsKopfkommentar Zeilen ~13–15homeassistant/README.mdZeile ~99homeassistant/INSTALL.mdZeile ~204 Neue Aussage sinngemäß: „Die Statistik-Seite berechnet echte Werte aus Fahrten und Tankvorgängen (Zeiträume, Verbrauch, Tag/Nacht, privat/Arbeitsweg)."
- INSTALL.md Schritt 4 (Zeilen ~106–109): falsche Variablennamen ersetzen.
Falsch:
DOORS_SENSOR,WINDOWS_SENSOR,LOCK_ENTITY,BATTERY_VOLTAGE_SENSOR. Richtig (siehehomeassistant/pyscript/modules/einstellungen.py):TUER_SENSOREN,FENSTER_SENSOREN,TUERSCHLOSS_SENSOREN(je 4er-Listen) undBATTERIE_SENSOR(einzeln). - README.md: Erwähnung der „97-%-Volltankungsregel" (~Zeile 110) streichen (Regel wurde
entfernt, siehe Kommentar
pyscript/belegverarbeitung.py:18-20); in der Dateiübersicht die fehlenden Skripte ergänzen:tankerkennung.py,batterieverlauf.py,bilderverwaltung.py,backup.py,updateverwaltung.py. - Obsoleten Kommentar entfernen:
pyscript/belegverarbeitung.py:41— den Zusatz „TODO: Datei ablegen" streichen (Datei existiert längst), Konstante selbst unverändert lassen. profil_lesen()härten (pyscript/modules/profil.py, ~Zeile 51–55): fehlende oder nicht parsebarefahrzeugprofil.jsonabfangen. Verhalten: Fehler ins Log (log.errorist in pyscript global verfügbar), RückgabeNone; Aufrufer prüfen. Vorher alle Aufrufer suchen (grep -rn "profil_lesen" homeassistant/pyscript/) und sicherstellen, dass jeder mitNoneumgehen kann — wo nicht, dort eine frühe Rückkehr einbauen. pyscript-Eigenheit beachten: Datei-I/O nur übertask.executor(io.open, …)-Muster wie im Bestand, kein nacktesopen(). ✅ Fertig wenn:python3 -c "import ast; ast.parse(open('homeassistant/pyscript/modules/profil.py').read())"fehlerfrei ist und jeder Aufrufer denNone-Fall behandelt.- Bewusst NICHT tun: Swipe-Delete-Fallback, Popup-Tastaturzugang, Leaflet-Selbsthosting (Audit-Reste) — Entscheidung laut Audit: kommt in DataMetric360, nicht ins alte Panel. RAM-only-Zustände (Fahrtstart, Tank-Tiefststand) ebenfalls nicht anfassen — wird mit dem FMM003-Umstieg hinfällig.
- Committen: „Doku an Codestand angleichen und profil_lesen gegen fehlende Datei härten".
AGENTS.md: die erledigten Punkte in Block C abhaken.
Offen bleiben in Block C nur: Fahrzeugfotos hochladen + steuer.faellig setzen (macht der
Besitzer in der App/UI, kein Code).
Phase 3 — Design-Entwurf vervollständigen ⏭️ ÜBERSPRUNGEN (2026-08-11)
Entscheidung des Besitzers: Die Screens wurden direkt aus den Views des alten Panels abgeleitet, statt auf den Claude-Design-Entwurf zu warten. Dieser Abschnitt bleibt stehen, falls der Entwurf später auf den Stand der Umsetzung gebracht werden soll.
Ziel: Der Entwurf zeigt das richtige Fahrzeug und alle Seiten, die das alte Panel hat. Diese Phase läuft im Claude-Design-Projekt (https://claude.ai/design/p/c28a8d4d-ec4e-4178-9e49-ab5b90c02097) — der Besitzer stößt sie an; ein Agent kann den Auftragstext vorbereiten und den Export danach einpflegen.
- Korrektur Modell: überall „Audi RS 4 Avant competition" statt „RS 6 Avant"; Typenschild
rs4(negative/positive) stattrs6. - Fehlende Seiten ergänzen (16 Stück; Vorlage ist jeweils die View im alten Panel —
Funktionsnamen aus
homeassistant/www/audi-dashboard-app.js, Beschreibung inSPECIFICATION.md§3 „Views"):# Seite Vorlage (alte View) 1 Fahrzeugstatus-Detail (12-Punkte Türen/Fenster/Schlösser) vSicherheit()2 Fahrzeugdaten/Technik/Ausstattung vIdent()3 Batterieverlauf (SVG-Kurve, SoC/SoH) vBatterieverlauf()4 Service (Termine, Terminanfrage, Servicebuch-Liste) vService()5 Werkstatt bearbeiten vWerkstatt()6 Servicebuch-Eintrag (ansehen/bearbeiten/neu) vSbuch(i)7 Versicherung & Steuer (Hub) vVers()8 Beitrag bearbeiten vBeitrag()9 Vertragsdetails vVertragsdetails()10 Schutzbrief (nur lesen) vSchutz()11 Notrufnummern bearbeiten vNotrufBearbeiten()12 Kfz-Steuer (Ansicht + Bearbeiten) vSteuer()/vSteuerBearbeiten()13 Reifen (Sätze, km-Zähler, Wechsel, Drehmoment) vReifen()14 Fahrt-Detail (Karte, Daten, Bearbeiten) vTrip(id)15 Tankvorgang-Detail (inkl. Beleg-Upload/-Ersetzen) vFill(id)16 Einstellungen in voller Tiefe (Fahrzeug-Setup, Fotos, SmartDeal, Pausenzeit, Export/Import, Backup, Version/Update) vEinst()Für jede Seite beide Layoutvarianten (schmal mit Tab-Leiste unten, breit ≥1000px mit Seitenleiste) — wie im Brief DESIGN_BRIEF_DATAMETRIC360.mdgefordert. - Export einpflegen: kompletten Ordner
design/durch den neuen Export ersetzen (design/README.mdsagt selbst: „wird beim nächsten Export komplett ersetzt"), README-Abschnitt „Stand dieses Exports" aktualisieren. ✅ Fertig wenn:grep -ci "rs 6\|rs6" design/DM360.dc.html= 0 und alle 16 Seiten im Export auffindbar sind. - Committen;
AGENTS.mdBlock A Punkt 1 abhaken.
Wichtig: Phase 7 kann pro Screen schon vor Abschluss dieser Phase beginnen — die 8 vorhandenen Hauptscreens sind entworfen; die 16 Unterseiten folgen dann dem gleichen Muster.
Phase 4 — Workspace und React-Gerüst ✅ ERLEDIGT (2026-08-11)
Ziel: companion-app/ ist ein lauffähiges Vite+React+TS-Projekt, das @audi-dash/ui als
echte Dependency nutzt. Die bestehende Datenschicht (src/api/) bleibt unverändert liegen.
- npm-Workspace-Root anlegen — neue Datei
package.jsonim Repo-Root:Dazu Repo-Root-{ "name": "audi-app", "private": true, "workspaces": ["design-system", "companion-app"] }.gitignoreumnode_modules/ergänzen (falls nicht abgedeckt). - Dependencies in
companion-app/package.jsonergänzen (bestehende Felder beibehalten!):dependencies:react ^18,react-dom ^18,@audi-dash/ui(Versionsangabe*— kommt über den Workspace);devDependencieszusätzlich:vite,@vitejs/plugin-react,@types/react,@types/react-dom. Scripts ergänzen:"dev": "vite","build": "vite build","preview": "vite preview". npm installim Repo-Root ausführen (verlinkt design-system in die companion-app). Danach einmalnpm run build --workspace design-system(die App importiert ausdist/).- Vite-Konfiguration
companion-app/vite.config.ts:import { defineConfig } from "vite" import react from "@vitejs/plugin-react" export default defineConfig({ plugins: [react()], server: { port: 5173 }, build: { outDir: "dist", target: "es2022" }, }) - tsconfig anpassen: Das bestehende
companion-app/tsconfig.jsonist auf die reine Datenschicht zugeschnitten (noEmit, sehr streng — so lassen). Ergänzen:"jsx": "react-jsx","lib": ["ES2022", "DOM", "DOM.Iterable"]und"moduleResolution": "bundler", falls nicht gesetzt. Die strengen Flags (exactOptionalPropertyTypesusw.) NICHT abschwächen. - Einstieg anlegen:
companion-app/index.html— Minimal-HTML mit<div id="wurzel"></div>und<script type="module" src="/src/main.tsx"></script>;<html lang="de">;<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">.companion-app/src/main.tsx:import { createRoot } from "react-dom/client" import "@audi-dash/ui/styles.css" import { App } from "./App" createRoot(document.getElementById("wurzel")!).render(<App />)companion-app/src/App.tsx— vorerst nur:export function App() { return <div className="ads-root" data-theme="nacht" style={{ minHeight: "100vh" }}>DataMetric360</div> }
className="ads-root"unddata-theme— ohne diese Wurzelklasse greifen die Design-Tokens nicht (Regel ausdesign-system/.design-sync/conventions.md). - ✅ Fertig wenn:
npm run dev --workspace companion-appstartet, http://localhost:5173 zeigt dunklen Hintergrund (#161b23) mit Text;npm run typecheck --workspace companion-appundnpm run build --workspace companion-applaufen fehlerfrei. - Committen: „companion-app: Vite+React-Gerüst und Workspace-Anbindung an @audi-dash/ui".
AGENTS.mdBlock A Punkt 2 abhaken, Statustabelle aktualisieren.
Phase 5 — App-Shell: Theme, Navigation, responsives Layout ✅ ERLEDIGT (2026-08-11)
Ziel: Navigationsgerüst, das beide Layouts beherrscht (schmal: Tab-Leiste unten; breit ≥1000px: Seitenleiste), mit Tag/Nacht-Theme und Zurück-Navigation. Noch ohne echte Daten.
Vorbild für Verhalten: das alte Panel (audi-dashboard-app.js) — flacher Routenzustand,
ZURUECK-Tabelle. Vorbild fürs Aussehen: design/DM360.dc.html (dort existiert der
wide-Umschalter mit 240px-Seitenleiste bereits).
- Kein Router-Paket installieren. Eigener flacher Zustand wie im alten Panel:
src/navigation.tsmittype Route = { name: RouteName; id?: string }, React-State im App-Root,geheZu(name, id?), plusZURUECK-Tabelle (aus dem alten Panel übernehmen und um die neuen Routen ergänzen; 3-stufige Verschachtelungschutz → vertragsdetails → versbeachten). - Theme:
src/theme.ts— Zustand"nacht" | "tag", Persistenz inlocalStorage(Schlüsseldm360.theme), Initialwert: gespeicherter Wert, sonstprefers-color-scheme, Standardnacht. Gesetzt wird ausschließlichdata-themeauf demads-root-Element.localStorage-Zugriffe in try/catch (Muster aus dem alten Panel, Private-Mode-Browser). - Layout:
src/Shell.tsx— CSS-Grid/Flex mit Breakpoint bei 1000px (window.matchMedia("(min-width: 1000px)")+ Listener):- schmal: Inhalt +
TabBar-Komponente (aus@audi-dash/ui) unten,env(safe-area-inset-*)als Padding (iPhone-Notch). - breit: 240px-Seitenleiste links (Navigationspunkte + Ringe-Platzhalter oben), Inhalt rechts; Listen-Detail nebeneinander wo der Entwurf es vorsieht. Die 5 Hauptbereiche: Übersicht, Mein Audi, Fahrten, Statistik, Tanken. Einstellungen über Zahnrad oben rechts (nicht in der Tab-Leiste) — wie im alten Panel und im Entwurf.
- schmal: Inhalt +
- Platzhalter-Screens: je Hauptbereich eine Datei unter
src/screens/(z. B.Uebersicht.tsx), die vorerst nur Titel perads-eyebrow/Tilezeigt. - ✅ Fertig wenn: Im Browser (a) bei < 1000px die Tab-Leiste unten erscheint und alle 5 Bereiche wechselbar sind, (b) bei ≥ 1000px stattdessen die Seitenleiste erscheint, (c) der Theme-Wechsel sichtbar Tag/Nacht umschaltet und einen Reload überlebt.
- Committen: „companion-app: App-Shell mit responsivem Layout, Navigation und Theme".
Phase 6 — Onboarding, Datenanbindung, Offline-UX ✅ ERLEDIGT (2026-08-11)
Ziel: Die App verbindet sich echt mit Home Assistant über die vorhandene Datenschicht.
Die Datenschicht ist fertig — nur benutzen, nicht neu bauen. Einstieg:
companion-app/src/api/index.ts (DataMetricApi). Sie liefert: REST-Reads
(profilLesen, fahrtenLesen, tankvorgaengeLesen, fahrzeugstatusLesen), Live-Updates
(HassLive mit Reconnect), Schreibzugriffe über die Offline-Queue (profilSchreiben,
belegHochladen, fahrtLoeschen, tankvorgangLoeschen, jetztAktualisieren) und
Zugangsdaten-Verwaltung (umgebung.ts).
- Ersteinrichtungs-Screen (
src/screens/Einrichtung.tsx) nach dem Entwurf indesign/DM360.dc.html(dort derisSetup-Block): Felder Server-Adresse + Zugangs-Token, Knopf „Verbinden" ruftverbindungPruefen()aus der Datenschicht; Fehler unterscheiden nachApiFehler.istAnmeldeproblem(Token falsch) vs.istNetzproblem(Adresse/Netz) und in verständlichem Deutsch anzeigen. QR-Knopf: vorerst ausblenden oder deaktiviert mit Hinweis (kommt mit Capacitor, Phase 10). Bei Erfolg Zugangsdaten über dieAblageder Datenschicht speichern und in die App wechseln. - Datenkontext:
src/datenkontext.tsx— ein React-Context, der beim StartDataMetricApiinstanziiert, initial alle vier Reads lädt, sich beiaufZustand()für Live-Updates registriert und{profil, fahrten, tankvorgaenge, status, verbindung}bereitstellt. Übernahme-Pflicht aus dem alten Panel: die UmrechnungenprofilZuConfig()/profilZuCar()(inkl. SmartDeal-Gültigkeit: aktiv wennlaeuft_abfehlt oder ≥ heute, Ablaufdatum einschließlich) ausaudi-dashboard-app.jsnach TypeScript portieren — Logik exakt beibehalten. - Offline-UX (Entwurf: Offline-Marker in der Kopfzeile):
- Verbindungszustand aus
aufVerbindung()der Datenschicht → dezenter „Offline"-Hinweis im Kopf, Daten bleiben sichtbar. - Warteschlange aus
aufAenderung()→ sichtbare Zahl wartender Änderungen („2 Änderungen warten auf Übertragung"), wie im Design-Brief gefordert.
- Verbindungszustand aus
- Stale-Anzeige:
last_updateddes Fahrzeugstatus mitführen (Muster: Staleness-Indikator des alten Panels). - ✅ Fertig wenn: Gegen
audi_ha_test(Token siehe Phase 9 Schritt 1): Einrichtung mit falschem Token zeigt Anmeldefehler, mit richtigem Token lädt die App Profil + Status; HA-Container stoppen → Offline-Marker erscheint; Container starten → verschwindet wieder (Reconnect testet die Backoff-Logik der Datenschicht). - Committen;
AGENTS.mdBlock A Punkte Onboarding/Offline teilweise abhaken (QR bleibt offen).
Phase 7 — Alle Screens umsetzen ✅ ERLEDIGT (2026-08-11)
Alle 21 Seiten stehen. Die Entwurfsvorlage kam für die 16 Unterseiten aus den Views des alten Panels statt aus Claude Design (Entscheidung des Besitzers, 2026-08-11) — Phase 3 ist damit gegenstandslos, solange der Entwurf nicht nachgezogen werden soll.
Ziel: Funktionsgleichheit mit dem alten Panel plus die neuen Live-Ansichten. Pro Screen:
Entwurf aus design/ nachbauen (mit @audi-dash/ui-Komponenten), Logik aus der alten View
portieren, an den Datenkontext anschließen.
Reihenfolge und Zuordnung (Logik-Vorlage = Funktion in audi-dashboard-app.js;
Datenquelle = Feld im Datenkontext):
| Screen | Logik-Vorlage | Datenquelle / Services | Hinweise |
|---|---|---|---|
| Übersicht | vHome() |
status, profil, fahrten, tankvorgaenge | Foto, Status „sicher abgestellt", Reichweite, km, nächster Service, Teaser letzte Fahrt/Tankung; Pull-to-Refresh → jetztAktualisieren() |
| Fahrten-Liste | vTrips() |
fahrten | Jahr→Monat-Akkordeon (Accordion); Lösch-Aktion: nicht nur Swipe — zusätzlich „…"-Menü pro Zeile (behebt Audit-Finding barrierefreies Löschen); manuelles Anlegen (vTripsFormular()) über Service audi_dashboard_fahrt_manuell_anlegen |
| Fahrt-Detail | vTrip(id) |
fahrten | Karte: Leaflet lokal bündeln (npm i leaflet, Audit-Finding CDN), Tile-Layer nach Theme; kein fakeTrack() portieren — ohne echte Route nur Start/Ende-Marker zeigen, mit ehrlichem Leerhinweis |
| Tanken-Liste | vFuel() |
tankvorgaenge | volumengewichteter Durchschnittspreis; Lösch-„…"-Menü wie bei Fahrten |
| Tankvorgang-Detail | vFill(id) |
tankvorgaenge | Beleg-Upload: Datei → Base64 → belegHochladen(); Ergebnis kommt asynchron über die Entität pyscript.audi_dashboard_beleg_ergebnis (Muster im alten Panel: belegErgebnisVerarbeiten()) |
| Statistik | vStat() |
fahrten, tankvorgaenge | Rechenfunktionen (fahrtenSeit, tankSeit, verbrauch, istNachtZeit) 1:1 portieren |
| Mein Audi (Hub) | vAudi() |
profil | Fotogalerie + Kachel-Links auf die Unterseiten |
| Fahrzeugstatus-Detail | vSicherheit() |
status | 16-Punkte-Liste; ok === null heißt „unbekannt", nie grün raten |
| Fahrzeugdaten | vIdent() |
profil (technik, ausstattung) | statisch aus dem Profil |
| Batterieverlauf | vBatterieverlauf() |
eigener Read der Entität pyscript.audi_dashboard_batterieverlauf |
SVG-Chart portieren (Pinch/Pan/Tap); Leerzustand prominent — Sensor ist derzeit unbesetzt |
| Service + Werkstatt + Servicebuch | vService(), vWerkstatt(), vSbuch() |
profil.service | Öl-/Service-Prognose (oelwechselPrognose(), intervalle(), termine()) exakt portieren; ICS-Export (ics()-Helfer) übernehmen |
| Versicherung/Steuer-Gruppe | vVers(), vBeitrag(), vVertragsdetails(), vSchutz(), vNotrufBearbeiten(), vSteuer() |
profil.versicherung, profil.steuer | reine Formular-/Anzeige-Seiten; Schreiben immer als ganzes Profil über profilSchreiben() (es gibt keinen Feld-einzeln-Service!) |
| Reifen | vReifen() |
profil.reifen + Entitäten pyscript.reifen_* |
Wechsel über Service audi_dashboard_reifen_wechseln (nur "sommer"/"winter"); Wechseldatum → ICS |
| Einstellungen | vEinst() |
profil, alle | volle Tiefe: Fahrzeug-Setup, Theme, Fotos (Upload über audi_dashboard_bild_hochladen, nur die 10 erlaubten Dateinamen!), SmartDeal, Pausenzeit, Profil-Export/Import, Backup, CSV-Export, Version |
| Neu: Live-Fahrt | — (neu) | status (künftig FMM003-Felder) | Entwurf screen === 'live': Karte, Geschwindigkeit groß, Strecke/Dauer; bis Phase 13 hinter einem Feature-Schalter LIVE_VERFUEGBAR = false in einer zentralen Konfigurationsdatei — Screen bauen, Schalter erst mit echten Daten umlegen |
| Leerzustände | — | — | für Fahrten/Tanken/Statistik je einen freundlichen „noch keine Daten"-Zustand (Design-Brief), wird real gebraucht: beide JSONL sind aktuell leer |
Regeln für alle Screens:
- Nur
@audi-dash/ui-Komponenten + Tokens (var(--…)), keine neuen Hex-Farben, keine neuen Komponenten ohne sie zuerst indesign-system/anzulegen (dann dort mit CSS+Preview, danach hier verwenden). - Destruktive Aktionen immer mit Bestätigung; Fehler sichtbar in rot inline (Muster altes Panel).
- Jede Zahl durch
de()/eur(). - Nach jedem Screen:
npm run typecheck+ Sichtprüfung schmal UND breit.
✅ Fertig wenn: jeder Screen der Tabelle existiert, mit Testdaten befüllbar ist und in beiden Layouts bedienbar aussieht wie der Entwurf. Committen pro Screen-Gruppe (z. B. „Fahrten-Screens"), nicht ein Riesencommit.
Phase 8 — Audi-Assets einbauen ✅ ERLEDIGT (2026-08-11)
Ziel: Die App sieht aus wie das Original — Schriften, Ringe, Typenschilder.
- Quellen: Schriften aus
design/uploads/*.ttf(oder die woff2-Base64-Blöcke aushomeassistant/www/audi-dashboard.css), Ringedesign/assets/audi-rings-{white,black}.svg, Typenschilder aushomeassistant/www/badges/(alle 14). - Ablage:
companion-app/src/assets/audi/+@font-face-Deklarationen in einer eigenensrc/audi-schrift.css(Familien exakt wie im Bestand:"Audi Type"400,"Audi Type Wide"300/400,"Audi Type Extended"italic). Der--font-stackwird per CSS-Variable auf demads-rootüberschrieben —design-system/-Dateien bleiben unberührt. - Typenschild-Auswahl nach Modell aus dem Profil (Logik aus
vEinst()/Badge-Mapping des alten Panels übernehmen; Theme-abhängig positive/negative-Variante). - Kontrolle:
git log --statdes Commits zeigt KEINE Änderung unterdesign-system/; Gewichte ≥ 600 kommen nirgends vor (grep -rn "font-weight" companion-app/src | grep -vE "300|400"liefert nichts). - Committen;
AGENTS.mdBlock A Assets-Punkt abhaken. Hinweis im Commit: Repo bleibt privat (Lizenz).
Phase 9 — Tests ✅ ERLEDIGT (2026-08-11)
90 Unit- und Rendertests, dazu 9 Prüfungen gegen die laufende Instanz.
Ziel: Die ungeteste Hälfte der Datenschicht ist abgedeckt; die Kern-Rechenlogik hat Unit-Tests.
- Token für die Testinstanz erzeugen:
audi_ha_testim Browser öffnen (http://localhost:18123), anmelden, Profil → Sicherheit → langlebiges Zugriffstoken erstellen. Token NUR incompanion-app/.env.localablegen (VITE_TEST_TOKEN=…), Datei in.gitignoreaufnehmen. Niemals committen. - Authentifizierter Smoke (
scripts/smoke-auth.ts, Aufbau wiescripts/smoke.ts): mit Token (a)zustandLesen("pyscript.audi_dashboard_profil")liefert Daten, (b)dienstAufrufenmit einem harmlosen Service (audi_dashboard_jetzt_aktualisieren) gibt 200, (c) Queue-Round-Trip: Eintrag einstellen bei gestopptem Container, Container starten, Queue leert sich. Script inpackage.jsonalssmoke:auth; ohne gesetztes Token bricht es mit klarer Meldung ab (Exit-Code 0, „übersprungen"). - Unit-Tests mit
vitest(einzige neue Dev-Dependency): portierte Rechenlogik testen — Statistik (verbrauch,istNachtZeit, Zeiträume), Service-Prognose (oelwechselPrognose: Neustart nach Ölwechsel, 180-Tage-Gewichtung, Kappung), SmartDeal-Gültigkeit (Ablauftag einschließlich!),de()/eur()-Formatierung. Testdaten als Fixtures aus realistischen JSONL-Zeilen (Schema:SPECIFICATION.md§5). - ✅ Fertig wenn:
npm run smoke:auth3/3 grün (mit Token),npx vitest rungrün, und beide incompanion-app/README.mddokumentiert sind. - Committen;
AGENTS.mdBlock A Test-Punkt abhaken.
Phase 10 — PWA und Capacitor ✅ ERLEDIGT (2026-08-11)
PWA fertig. Native Hülle gebaut und in Betrieb: Capacitor 8 für iOS und Android, Build im Simulator (iPhone 17 Pro, iOS 26.4) erfolgreich, App zeigt echte Daten.
CapacitorHttpumgeht die CORS-Beschränkung der WebView. Offen bleibt allein das Signieren aufs eigene Gerät — das braucht das angeschlossene iPhone und die Apple-ID des Besitzers.2026-08-29: Schritt 7 erledigt. Der Besitzer hat das bezahlte Entwicklerkonto gelöst und das iPhone im Portal registriert; die Team-Kennung blieb dabei
RMACS9VLS4(die bezahlte Mitgliedschaft stuft das bestehende Team hoch, sie legt kein neues an). Ergebnis:companion-app/auslieferung/App.ipa, 2,4 MB, Ad-hoc signiert mitApple Distribution: Paul Nothaft, Profil gültig bis 29.08.2027. Zwei Skripte tragen das:scripts/ios-signieren.sh(bauen und signieren) undscripts/ios-luftweg.sh(Manifest und Installationsseite für die Übertragung über die Luft). Offen bleibt allein ein HTTPS-Host, der die Dateien ausliefert — siehe Schritt 8. Fallstricke und Diagnosen inAGENTS.mdAbschnitt AI.
Ziel: Die App läuft auf dem iPhone. Zwei Stufen — erst PWA (sofort nutzbar), dann Capacitor (Keychain + QR-Scan).
Stufe 1 — PWA (kein neues Werkzeug):
companion-app/public/manifest.webmanifest(Name „DataMetric360",display: "standalone", Themenfarbe#161b23, neutrale Icons 192/512px — NICHT die Audi-Ringe als App-Icon, der Homescreen ist „außen"; neutrales DM360-Monogramm erzeugen) + Verweis imindex.html.- Erreichbarkeit fürs Handy: solange Phase 12 nicht fertig ist, über Tailscale
(
npm run build, Ausgabe z. B. über HA/local/odervite preview --hostim LAN testen). - ✅ Fertig wenn: „Zum Home-Bildschirm" auf dem iPhone die App randlos startet und Login + Datenanzeige funktionieren.
Stufe 2 — Capacitor:
4. npm i -D @capacitor/cli && npm i @capacitor/core @capacitor/ios @capacitor/android
(im Workspace für companion-app), npx cap init DataMetric360 app.datametric360 --web-dir dist,
npx cap add ios, npx cap add android.
5. Sichere Token-Ablage: Capacitor-Secure-Storage-Plugin (z. B.
@aparajita/capacitor-secure-storage) als neue Ablage-Implementierung; über den vorhandenen
Hook ablageSetzen() der Datenschicht einhängen, wenn umgebungErkennen() „capacitor" meldet.
Browser-Fall bleibt unverändert BrowserAblage.
6. QR-Scan: Barcode-Plugin (@capacitor-mlkit/barcode-scanning) + Kamera-Berechtigung;
Gegenstück: kleine statische Seite unter HA /local/dm360-qr.html, die den Token clientseitig
(Offline-JS-QR-Bibliothek, keine Netzabfrage) als QR anzeigt. Inhalt des QR: JSON
{"url": "...", "token": "..."}. Wenn das zusammen > 1 Tag Aufwand wird: weglassen —
manuelles Einfügen ist die beschlossene, ausreichende Lösung.
7. ✅ iOS-Sideload (erledigt 2026-08-29): bash companion-app/scripts/ios-signieren.sh
baut Webbündel, synchronisiert die Hülle, archiviert signiert und exportiert die .ipa
nach companion-app/auslieferung/. Die Team-Kennung steht im Skript, weil ios/ gitignored
ist und jede in Xcode geklickte Einstellung beim nächsten npx cap add ios verschwinden würde.
Der Entscheidungspunkt Kostenlos-vs-Bezahlt ist entschieden: bezahltes Konto, damit
Signatur ein Jahr gültig statt sieben Tage, und Geräteregistrierung über das Portal ohne
angeschlossenes Telefon.
⚠️ Falle, die viel Zeit kostet: beim ersten Signieren fragt der Schlüsselbund per Dialog
um Erlaubnis. Wird der nicht beantwortet, hängt xcodebuild wortlos minutenlang und endet
mit errSecInternalComponent. Im Dialog „Immer erlauben" wählen — ein Archiv signiert
über 25 Binärdateien und würde sonst jedes Mal erneut fragen.
8. Übertragung über die Luft (statt Kabel): bash companion-app/scripts/ios-luftweg.sh https://<host> erzeugt manifest.plist, Installationsseite und Symbole in
ios/build/luftweg/. Der Ordner muss über HTTPS mit öffentlich vertrauenswürdigem
Zertifikat ausgeliefert werden — iOS lehnt einfaches HTTP und selbstsignierte Zertifikate
ab, Home Assistant unter /local/ genügt also nicht. Passend: tailscale serve --bg <ordner> (echtes Let's-Encrypt-Zertifikat auf *.ts.net, kein offener Port, iPhone ohnehin
im Tailnet); der Cloudflare-Tunnel aus Phase 12 täte es später ebenso. Link auf dem iPhone in
Safari öffnen — andere Browser reichen itms-services:// nicht ans System weiter.
⏳ Offen: dieser Mac ist bei Tailscale abgemeldet, es läuft also noch kein Host.
9. ✅ Fertig wenn: App startet nativ auf dem iPhone, Token liegt im Keychain (Test: App löschen und
neu installieren → Token weg; Backup/Restore-Verhalten notieren), QR-Einrichtung funktioniert
oder ist dokumentiert entfallen.
9. Committen; AGENTS.md Block A Punkte QR/Secure-Storage abhaken.
Phase 11 — HA-Einbettung und Ablösung des alten Panels
Ziel: Die neue App läuft im HA-Seitenmenü; das alte Panel ist archiviert.
- Build in HA ausliefern:
npm run build, Ausgabe nach/config/www/dm360/der Produktivinstanz (Weg analogupdate.ps1; das Script um diesen Ordner erweitern). configuration.yaml:panel_iframe-Eintrag (Titel „Mein Audi",mdi:car-sports, URL/local/dm360/index.html). Derpanel_custom-Block des alten Panels bleibt zunächst parallel bestehen (zweiter Menüpunkt „Mein Audi (alt)").- Parallelbetrieb mindestens 2 Wochen: beide Panels zeigen dieselben Daten (gleiche Entitäten/Services — Abweichungen sind Portierungsfehler; prüfen: Übersichtszahlen, eine Fahrt anlegen/löschen, ein Beleg-Upload).
- Ablösung: alten
panel_custom-Block entfernen; Dateien NICHT löschen, sondern verschieben:homeassistant/www/audi-dashboard-app.js→homeassistant/archiv/(plus Panel-Stub und CSS), README-Hinweis dort ablegen. Beschlossene Regel: archivieren, nie löschen (COMPANION_APP_ARCHITECTURE.md§1). - ✅ Fertig wenn: Neues Panel im HA-Menü, altes entfernt aber archiviert,
SPECIFICATION.mdbekommt eine Kopfnotiz „beschreibt das archivierte Panel; aktuell ist companion-app/". - Committen;
AGENTS.mdBlock D abhaken, Statustabelle umstellen.
Phase 12 — Externer Zugriff: Cloudflare Tunnel + Reverse Proxy 🟡 VORBEREITET
2026-08-28 aktualisiert/teilweise überholt. Die Schritte unten stammen aus der frühen Planungsphase (2026-08-11) und enthalten inzwischen überholte Annahmen: zwei getrennte Hostnamen (App-Domain + API-Subdomain — entfällt, die App wird nie als Web-Build ausgeliefert, siehe
COMPANION_APP_ARCHITECTURE.md§5 Punkt 4) und pyscript-Entitäts-/Dienstnamen (seit der Integrations-Umstellung 2026-08-23sensor.audi_dashboard_*/audi_dashboard.<name>, sieheAGENTS.mdAbschnitt H). Reverse-Proxy-Wahl (Nginx Proxy Manager) und die vollständige, aktuelle Freigabeliste stehen bereits fest. Maßgeblich für die tatsächliche Einrichtung sindhomeassistant/INTERNET_ZUGRIFF_EINRICHTEN.md(Schritt-für-Schritt-Anleitung) undhomeassistant/REVERSE_PROXY.md(die Freigabeliste selbst) - die Liste unten bleibt nur als Entscheidungsprotokoll stehen, nicht als aktuelle Anleitung.
Ziel: Die App funktioniert von unterwegs (Mobilfunk), ohne dass HA selbst erreichbar ist.
Referenz: COMPANION_APP_ARCHITECTURE.md §4/§5. Teilweise Besitzer-Aufgaben (Konten/DNS).
- [Besitzer] Nameserver umstellen:
datametric360.debei all-inkl auf die Cloudflare-Nameserver zeigen lassen (Cloudflare-Konto → Site hinzufügen → „Full setup"; Domain trägt sonst nichts, Umstellung ist folgenlos). ✅ wenndig NS datametric360.dedie Cloudflare-Server liefert. Hostnamen festlegen (App-Domain + getrennter API-Hostname)— überholt, siehe Hinweis oben: ein einziger Hostname (https://datametric360.de) genügt, da die App nativ/sideload-only bleibt und nie als eigener Web-Build ausgeliefert wird.- Cloudflared-Add-on in HA installieren, Tunnel erstellen, ein öffentlicher Hostname
(
datametric360.de→ Reverse-Proxy-Port). Kein Router-Port wird geöffnet. Genaue Klicks:INTERNET_ZUGRIFF_EINRICHTEN.mdSchritt 3/5. - Reverse Proxy: Nginx Proxy Manager-Add-on (entschieden, nicht Traefik). Die Allowlist ist
umfangreicher als unten ursprünglich skizziert (aktueller Entitäts-/Dienststand plus das
OTA-Bündel unter
/audi_dashboard_static/app/*) - wortwörtlich inREVERSE_PROXY.mdgepflegt, nicht hier dupliziert. ⚠️ Ehrliche Einschränkung bleibt bestehen:/api/websocketlässt sich nicht pfadgenau beschneiden — nachauth_oksind darüber alle States lesbar. Schutzschicht bleibt der LLAT (ohne Token keine Verbindung). - Prüfen von außen (Mobilfunk, VPN aus): die Prüfbefehle stehen in
REVERSE_PROXY.md("Nach der Einrichtung prüfen"). HA-Port 8123 ist von außen nicht erreichbar. - ✅ Fertig wenn: Schritt 5 vollständig besteht und die Server-Adresse in der App eingetragen ist
(
INTERNET_ZUGRIFF_EINRICHTEN.mdSchritt 7). InAGENTS.mdBlock B abhaken.
Schritte 3–4 können gegen die bestehende HA-API erledigt werden (die FMM003-Hardware läuft seit 2026-08-13 bereits über flespi, siehe Phase 13 unten — diese Phase hängt nicht mehr daran).
Phase 13 — FMM003-Inbetriebnahme ✅ ÜBERHOLT — Hardware lief bereits über flespi
Diese Phase ist gegenstandslos. Die FMM003-Hardware ist seit 2026-08-13 in Betrieb, aber über den in
AGENTS.md(§ FMM003/Datenpfad) beschriebenen Weg: flespi (natives Codec8/TCP, IMEI- Auth), nicht MQTT/Mosquitto. Die Schritte unten (eigene CA, Mosquitto-Add-on, Zertifikate,mosquitto_sub-Mitschnitt) beschreiben die zuerst geplante, dann verworfene Variante — stehen nur noch als Entscheidungsprotokoll.homeassistant/FMM003_MAPPING.md(unten referenziert) wurde beim Merge 2026-08-13 deshalb bewusst nicht übernommen. Maßgeblich ist ausschließlichAGENTS.md.
Ziel (nicht mehr aktuell): Live-Daten vom Fahrzeug fließen nach HA; Fahrterkennung läuft über
die Zündung. Referenz: COMPANION_APP_ARCHITECTURE.md §2b. Nichts hiervon vorab raten oder
simulieren.
- Mosquitto-Add-on in HA installieren, Benutzer für das Gerät anlegen.
- Zertifikate: kleine eigene CA (drei
openssl-Befehle: CA-Schlüssel+Zertifikat, Server-Zertifikat für den Broker, Client-Zertifikat fürs Gerät); Broker auf 8883/TLS; CA-/Client-Dateien im Teltonika Configurator unter Security hinterlegen. Ohne Zertifikate verweigert der FMM003 MQTT. - [Besitzer] Broker-Erreichbarkeit entscheiden: Portfreigabe 8883 (einfach; TLS+Login
davor) oder VPS-Broker mit Mosquitto-Bridge über Tailscale (keine Freigabe, mehr Aufwand).
Entscheidung + Begründung in
COMPANION_APP_ARCHITECTURE.md§5.2a undAGENTS.mdnachtragen. - Gerät konfigurieren: Firmware-Version notieren (Codec JSON ist firmwareabhängig!); System → Data Protocol → Codec JSON; GPRS → MQTT mit Broker-IP/Port/Login.
- Erste echte Nachricht mitschneiden:
mosquitto_sub -h <broker> -p 8883 --cafile ca.crt -u <nutzer> -P <pw> -t '#' -v > mitschnitt.txt— mit Zündung an/aus, kurzer Fahrt. Aus dem Mitschnitt die Feldzuordnungstabelle bauen (JSON-Feld → Bedeutung → Einheit) und alshomeassistant/FMM003_MAPPING.mdeinchecken (ohne echte Positionsdaten im Beispiel!). - HA-MQTT-Integration: aus der Tabelle
mqtt:-Sensoren/device_trackerin der HA-Konfiguration definieren (Zündung, Geschwindigkeit, Drehzahl, Tankfüllstand, Position, km). - Fahrterkennung umstellen: neues pyscript
fahrterkennung_fmm003.pynach dem Muster vonfahrterkennung.py— Trigger ist die Zündungs-Entität (1→0 mit Pausentoleranz viatask.unique()/task.sleep(), Ende-Zeitpunkt auf den echten Zündung-aus-Moment rückdatieren;fahrten_pausenzeit_minaus dem Profil weiterverwenden). Erst parallel laufen lassen (schreibt in eine Test-Datei), vergleichen, dann scharf schalten undsensor.iphone_wifi_connectionauseinstellungen.pyentfernen. - Live-Screen aktivieren: Feature-Schalter aus Phase 7 umlegen, Live-Felder anbinden;
GPS-Route der laufenden Fahrt aufzeichnen und im Fahrt-Detail echte Tracks statt der
Start/Ende-Marker zeigen (damit stirbt der letzte Rest von
fakeTrack()endgültig). - ✅ Fertig wenn: eine echte Fahrt automatisch erkannt, mit km und Route gespeichert und in der
App (Liste, Detail mit echter Route, Statistik) korrekt angezeigt wird; VAG-Integration und
WLAN-Trigger sind entfernt;
AGENTS.mdBlock B komplett abgehakt.
Anhang A — Nachschlagereferenz für das ausführende Modell
Entitäten (lesen): pyscript.audi_dashboard_profil, …_fahrten, …_tankvorgaenge,
…_fahrzeugstatus, …_batterieverlauf, …_beleg_ergebnis, …_update_status,
pyscript.reifen_sommer_km, …_winter_km, …_aktiver_satz. Nutzdaten stets im Attribut daten.
Services (schreiben, Domain pyscript): audi_dashboard_profil_schreiben,
…_beleg_hochladen, …_tankvorgang_manuell, …_tankvorgang_aktualisieren,
…_tankvorgang_loeschen, …_fahrt_manuell_anlegen, …_fahrt_loeschen, …_bild_hochladen,
…_bild_loeschen, …_reifen_wechseln, …_backup_jetzt, …_backup_wiederherstellen,
…_jetzt_aktualisieren, …_screening_jetzt, …_update_pruefen, …_update_installieren.
Signaturen: SPECIFICATION.md §5. Service-Aufrufe geben keine Werte zurück — Ergebnisse
kommen asynchron über Entitäten (Muster: Beleg-Upload).
Stolperfallen:
- Profil wird immer als Ganzes geschrieben — nie Teil-Updates erfinden.
edited_fieldseines Datensatzes schützt Felder vor automatischen Überschreibungen — beim Portieren von Bearbeiten-Formularen beibehalten.- Sensorwerte
"unknown"/"unavailable"=Nonebehandeln, nie raten (Musterzustand_oder_none()). - pyscript: kein nacktes
open()/with—task.executor-Muster ausmodules/profil.py. - HA-Attribute haben ~16 KB-Grenze (
frontend_veroeffentlichung.py) — keine großen Blobs in Entitäten stopfen. fahrten.jsonl/tankvorgaenge.jsonlsind aktuell leer — Leerzustände sind der erste Eindruck der App, nicht ein Randfall.
Anhang B — Definition of Done (gesamt)
Die App gilt als fertig, wenn: alle Screens aus Phase 7 in beiden Layouts funktionieren; Onboarding,
Offline-Queue und Beleg-Upload gegen die Produktiv-HA laufen; die App als PWA und (falls Capacitor
umgesetzt) nativ auf dem iPhone installiert ist; der externe Zugriff über datametric360.de
funktioniert, ohne dass HA exponiert ist; das alte Panel archiviert ist; und AGENTS.md den
Endstand widerspiegelt (alle Blöcke A–D abgehakt oder begründet gestrichen).