e1d570992e
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>
650 lines
32 KiB
HTML
650 lines
32 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="de">
|
||
<head>
|
||
<meta charset="utf-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||
<title>Bauauftrag Fahrzeug-Dashboard</title>
|
||
<style>
|
||
:root {
|
||
--bg: #0E0E0E;
|
||
--panel: #161616;
|
||
--line: #2A2A2A;
|
||
--fg: #F2F2F2;
|
||
--muted: #9A9A9A;
|
||
--accent: #BB0A30;
|
||
--mono: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
|
||
}
|
||
* { box-sizing: border-box; }
|
||
html { scroll-behavior: smooth; }
|
||
body {
|
||
margin: 0;
|
||
background: var(--bg);
|
||
color: var(--fg);
|
||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif;
|
||
font-size: 16px;
|
||
line-height: 1.65;
|
||
-webkit-font-smoothing: antialiased;
|
||
}
|
||
.wrap {
|
||
display: grid;
|
||
grid-template-columns: 250px minmax(0, 1fr);
|
||
gap: 56px;
|
||
max-width: 1180px;
|
||
margin: 0 auto;
|
||
padding: 56px 32px 120px;
|
||
}
|
||
nav {
|
||
position: sticky;
|
||
top: 40px;
|
||
align-self: start;
|
||
max-height: calc(100vh - 80px);
|
||
overflow-y: auto;
|
||
border-left: 1px solid var(--line);
|
||
padding-left: 18px;
|
||
}
|
||
nav a {
|
||
display: block;
|
||
color: var(--muted);
|
||
text-decoration: none;
|
||
font-size: 13px;
|
||
letter-spacing: .01em;
|
||
padding: 5px 0;
|
||
border-left: 2px solid transparent;
|
||
margin-left: -20px;
|
||
padding-left: 18px;
|
||
}
|
||
nav a:hover { color: var(--fg); border-left-color: var(--accent); }
|
||
main { min-width: 0; }
|
||
h1 {
|
||
font-size: 34px;
|
||
font-weight: 300;
|
||
letter-spacing: -.02em;
|
||
line-height: 1.2;
|
||
margin: 0 0 28px;
|
||
padding-bottom: 24px;
|
||
border-bottom: 1px solid var(--line);
|
||
}
|
||
h2 {
|
||
font-size: 22px;
|
||
font-weight: 500;
|
||
letter-spacing: -.01em;
|
||
margin: 64px 0 18px;
|
||
padding-top: 20px;
|
||
border-top: 1px solid var(--line);
|
||
scroll-margin-top: 24px;
|
||
}
|
||
h3 { font-size: 16px; font-weight: 600; margin: 34px 0 12px; color: var(--fg); }
|
||
h1 + p strong { color: var(--accent); }
|
||
p { margin: 0 0 14px; }
|
||
a { color: var(--fg); }
|
||
strong { font-weight: 600; }
|
||
hr { display: none; }
|
||
ul, ol { padding-left: 22px; margin: 0 0 16px; }
|
||
li { margin: 5px 0; }
|
||
li.todo, li.done { list-style: none; position: relative; }
|
||
li.todo::before, li.done::before {
|
||
content: "";
|
||
position: absolute;
|
||
left: -22px; top: 7px;
|
||
width: 12px; height: 12px;
|
||
border: 1px solid var(--muted);
|
||
border-radius: 2px;
|
||
}
|
||
li.done::before { background: var(--accent); border-color: var(--accent); }
|
||
table {
|
||
width: 100%;
|
||
border-collapse: collapse;
|
||
margin: 18px 0 26px;
|
||
font-size: 14.5px;
|
||
display: block;
|
||
overflow-x: auto;
|
||
}
|
||
th, td {
|
||
text-align: left;
|
||
padding: 10px 14px;
|
||
border-bottom: 1px solid var(--line);
|
||
vertical-align: top;
|
||
}
|
||
th {
|
||
color: var(--muted);
|
||
font-weight: 500;
|
||
font-size: 12px;
|
||
text-transform: uppercase;
|
||
letter-spacing: .07em;
|
||
white-space: nowrap;
|
||
}
|
||
tr:hover td { background: var(--panel); }
|
||
code {
|
||
font-family: var(--mono);
|
||
font-size: .88em;
|
||
background: var(--panel);
|
||
border: 1px solid var(--line);
|
||
border-radius: 3px;
|
||
padding: 1px 5px;
|
||
}
|
||
pre {
|
||
background: var(--panel);
|
||
border: 1px solid var(--line);
|
||
border-left: 2px solid var(--accent);
|
||
border-radius: 4px;
|
||
padding: 18px 20px;
|
||
overflow-x: auto;
|
||
font-size: 13.5px;
|
||
line-height: 1.6;
|
||
}
|
||
pre code { background: none; border: 0; padding: 0; }
|
||
blockquote {
|
||
margin: 18px 0;
|
||
padding: 2px 0 2px 20px;
|
||
border-left: 2px solid var(--accent);
|
||
color: var(--fg);
|
||
}
|
||
@media (max-width: 900px) {
|
||
.wrap { grid-template-columns: 1fr; gap: 0; padding: 32px 20px 80px; }
|
||
nav { position: static; max-height: none; border-left: 0; border-bottom: 1px solid var(--line);
|
||
padding: 0 0 20px; margin-bottom: 28px; columns: 2; }
|
||
nav a { margin-left: 0; padding-left: 0; border-left: 0; }
|
||
nav a:hover { border-left: 0; }
|
||
h1 { font-size: 27px; }
|
||
}
|
||
@media print {
|
||
body { background: #fff; color: #111; }
|
||
nav { display: none; }
|
||
.wrap { display: block; max-width: none; padding: 0; }
|
||
a { color: #111; }
|
||
code, pre { background: #f4f4f4; border-color: #ddd; }
|
||
}
|
||
</style>
|
||
</head>
|
||
<body>
|
||
<div class="wrap">
|
||
<nav><a href="#1-der-auftrag-in-einem-satz">1. Der Auftrag in einem Satz</a>
|
||
<a href="#2-was-mitgeliefert-wird">2. Was mitgeliefert wird</a>
|
||
<a href="#3-systemumgebung">3. Systemumgebung</a>
|
||
<a href="#4-datenquellen">4. Datenquellen</a>
|
||
<a href="#5-aufbau-der-oberflache">5. Aufbau der Oberfläche</a>
|
||
<a href="#6-datenhaltung">6. Datenhaltung</a>
|
||
<a href="#7-kernlogik">7. Kernlogik</a>
|
||
<a href="#7a-bilder">7a. Bilder</a>
|
||
<a href="#8-gestaltung">8. Gestaltung</a>
|
||
<a href="#9-nicht-funktionale-anforderungen">9. Nicht-funktionale Anforderungen</a>
|
||
<a href="#10-vor-baubeginn-zu-klaren">10. Vor Baubeginn zu klären</a>
|
||
<a href="#11-abnahmekriterien">11. Abnahmekriterien</a>
|
||
<a href="#12-ausdrucklich-nicht-bestandteil">12. Ausdrücklich nicht Bestandteil</a>
|
||
<a href="#13-was-noch-zu-liefern-ist">13. Was noch zu liefern ist</a>
|
||
<a href="#14-zeitlicher-rahmen">14. Zeitlicher Rahmen</a></nav>
|
||
<main><h1 id="bauauftrag-fahrzeug-dashboard-in-home-assistant">Bauauftrag: Fahrzeug-Dashboard in Home Assistant</h1>
|
||
<p><strong>Stand 05.08.2026 · privates Projekt, keine Veröffentlichung</strong></p>
|
||
<p><em>Überarbeitung: §10 vollständig geklärt; Änderungen in §4.2, §5.6, §6.1, §7.1, §7.2, §7.4, §7.5, §7.7, §9, §10, §13, §14.</em></p>
|
||
<hr />
|
||
<h2 id="1-der-auftrag-in-einem-satz">1. Der Auftrag in einem Satz</h2>
|
||
<p>Zu bauen ist ein Fahrzeug-Dashboard in Home Assistant, das die Ende 2026 abgeschaltete App
|
||
<em>Audi connect plug & play</em> ersetzt: Fahrzeugstatus, Fahrtenbuch, Tankmonitor, Service, Kosten und
|
||
Versicherung für einen Audi RS 4 Avant competition — in einer Oberfläche, die sich an der Audi-App
|
||
orientiert.</p>
|
||
<hr />
|
||
<h2 id="2-was-mitgeliefert-wird">2. Was mitgeliefert wird</h2>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Datei</th>
|
||
<th>Inhalt</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td><code>dashboard-muster-ohne-bilder.html</code></td>
|
||
<td>Dieselbe Anwendung mit referenzierten statt eingebetteten Bildern, 184 statt 770 KB — die Fassung, mit der gearbeitet wird</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>bilder/</code></td>
|
||
<td>Die aufbereiteten Bilddateien, elf Stück, zusammen 435 KB</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>dashboard-muster.html</code></td>
|
||
<td><strong>Vollständig bedienbarer Prototyp.</strong> Eine einzelne HTML-Datei, alle Ansichten, alle Interaktionen, Beispieldaten. Das ist die verbindliche Vorlage für Aufbau, Verhalten und Gestaltung</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>audi-connect-nach-homeassistant.md</code></td>
|
||
<td>Projektbeschreibung mit allen Entscheidungen und ihren Begründungen</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>shell_beleg_parser.py</code></td>
|
||
<td>Getesteter Parser für Shell-Tankbelege, gegen zwei echte Belege geprüft</td>
|
||
</tr>
|
||
<tr>
|
||
<td>Schriften, Ringe, Typenschild, Fahrzeugbilder</td>
|
||
<td>im Prototyp eingebettet, separat verfügbar</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<p><strong>Der Prototyp ist keine Skizze.</strong> Er läuft, alle Wege sind begehbar. Wo dieser Auftrag und der
|
||
Prototyp voneinander abweichen, gilt der Prototyp — außer bei den unter §10 genannten Punkten.</p>
|
||
<hr />
|
||
<h2 id="3-systemumgebung">3. Systemumgebung</h2>
|
||
<ul>
|
||
<li><strong>Home Assistant</strong>, bestehende Installation</li>
|
||
<li><strong>Fahrzeugdaten:</strong> HACS-Integration <code>TommiG1/HA_VAG-EU-Data-Act</code>, bereits eingerichtet und liefernd</li>
|
||
<li><strong>Positionsdaten:</strong> Home Assistant Companion App auf einem iPhone</li>
|
||
<li><strong>Zugriff von außen:</strong> Tailscale, kein Portforwarding, kein Funnel</li>
|
||
<li><strong>Fahrzeug:</strong> Audi RS 4 Avant competition, FIN WUAZZZF48PA902804, Erstzulassung 08/2023</li>
|
||
</ul>
|
||
<hr />
|
||
<h2 id="4-datenquellen">4. Datenquellen</h2>
|
||
<h3 id="41-eu-data-act-portal-uber-die-hacs-integration">4.1 EU-Data-Act-Portal über die HACS-Integration</h3>
|
||
<p>Liefert Kilometerstand, Tankfüllstand, Reichweite, Serviceintervalle, Türen- und Fensterstatus,
|
||
Verriegelung. <strong>Kein GPS.</strong> Aktualisierung im 15-Minuten-Takt und nur, wenn das Fahrzeug in diesem
|
||
Fenster Daten hochgeladen hat. Leere Zyklen sind normal.</p>
|
||
<p><strong>Konsequenzen, die im Entwurf bereits berücksichtigt sind:</strong></p>
|
||
<ul>
|
||
<li>Keine Live-Telemetrie. Momentanverbrauch, Drehzahl, Geschwindigkeit gibt es nicht.</li>
|
||
<li>Der Kilometerstand steht nach dem Abstellen erst verzögert bereit → zweistufiger Fahrtabschluss (§7.2).</li>
|
||
<li>Der Sensor ist zeitweise <code>unavailable</code> → betrifft den Reifenzähler (§7.5).</li>
|
||
</ul>
|
||
<h3 id="42-iphone">4.2 iPhone</h3>
|
||
<p>Positionen über die Companion App. Fahrtstart und Fahrtende über die WLAN-Verbindung zum Fahrzeug
|
||
(Companion-App-Sensor für das verbundene Netz), <strong>nicht</strong> über CarPlay — CarPlay ist nicht immer
|
||
verbunden. Bluetooth wurde verworfen: iOS meldet Bluetooth-Verbindungswechsel im Hintergrund über
|
||
Region-Monitoring, das mehrere Minuten verzögern kann; die WLAN-Verbindung zum Fahrzeug baut sich
|
||
nach bisheriger Erfahrung zuverlässig sofort auf und ab. Der WLAN-Name (<code>Audi_MMI_2804_5GHz</code>) ist
|
||
fahrzeugspezifisch, liegt im Fahrzeugprofil (§6.1) und ist in den Einstellungen änderbar (§5.6).</p>
|
||
<h3 id="43-shell-tankbelege">4.3 Shell-Tankbelege</h3>
|
||
<p>PDF per Upload in der App (§5.5, §7.7) — kein Postfachzugriff. Parser liegt bei. Belegnummer ist
|
||
<strong>nicht</strong> eindeutig; Schlüssel ist der minutengenaue Zeitstempel aus der Belegkopfzeile.</p>
|
||
<h3 id="44-manuelle-eingaben">4.4 Manuelle Eingaben</h3>
|
||
<p>Stammdaten, Versicherung, Steuer, Reifendaten, Servicebuch, Werkstatt, Fahrten und Tankvorgänge
|
||
ohne Beleg.</p>
|
||
<hr />
|
||
<h2 id="5-aufbau-der-oberflache">5. Aufbau der Oberfläche</h2>
|
||
<p>Fünf Menüpunkte in einer festen Leiste unten, Symbole mit Beschriftung:</p>
|
||
<pre><code>Übersicht · Mein Audi · Fahrten · Statistik · Tanken
|
||
</code></pre>
|
||
<p>Einstellungen liegen hinter den Audi-Ringen oben rechts.</p>
|
||
<h3 id="51-ubersicht">5.1 Übersicht</h3>
|
||
<p>Fahrzeugbild (Frontansicht, fest), Typenschild mit Kennzeichen, Zustandszeile „Sicher abgestellt"
|
||
mit grünem oder rotem Punkt, Reichweite als große Zahl mit Balken und Prozentwert, Kilometerstand
|
||
und nächster Service als Kachelpaar, darunter letzte Fahrt und letzter Tankvorgang als Anrisse.</p>
|
||
<h3 id="52-mein-audi">5.2 Mein Audi</h3>
|
||
<p>Bildergalerie zum Durchtippen, Typenschild, dann Kacheln mit Sprung auf eigene Seiten:</p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Kachel</th>
|
||
<th>Unterseite</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td>Identität</td>
|
||
<td>Fahrzeugdaten, technische Daten, Ausstattung</td>
|
||
</tr>
|
||
<tr>
|
||
<td>Zustand</td>
|
||
<td>Kilometerstand, Tankfüllung, Batteriespannung</td>
|
||
</tr>
|
||
<tr>
|
||
<td>Service</td>
|
||
<td>Termine, Servicebuch, Autohaus, Terminvereinbarung</td>
|
||
</tr>
|
||
<tr>
|
||
<td>Kosten</td>
|
||
<td>Jahreskosten, Aufteilung, je Kilometer</td>
|
||
</tr>
|
||
<tr>
|
||
<td>Versicherung und Steuer</td>
|
||
<td>Beitrag, Vertrag, Notruf, Schutzbrief, Kfz-Steuer</td>
|
||
</tr>
|
||
<tr>
|
||
<td>Reifen</td>
|
||
<td>beide Sätze, Umschalter, Wechseltermin, Anzugsmoment</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<h3 id="53-fahrten">5.3 Fahrten</h3>
|
||
<p>Dreistufig aufklappbar: Jahr → Monat → Fahrt. Detailseite mit Karte, Start, Ziel, Start- und
|
||
Endkilometer, Distanz, Dauer, Ø-Geschwindigkeit, Kraftstoffkosten, Art der Fahrt. Erfassung von
|
||
Hand über eine Schaltfläche unter der Liste.</p>
|
||
<h3 id="54-statistik">5.4 Statistik</h3>
|
||
<p>Aufklappbare Kacheln: Distanz und Fahrzeit, getankte Menge und Kosten, Durchschnittsverbrauch,
|
||
Tag/Nacht, Art der Fahrten. Aufschlüsselung je Jahr, Monat, Woche, Tag.</p>
|
||
<h3 id="55-tanken">5.5 Tanken</h3>
|
||
<p>Jahr → Monat → Tankvorgang, mit mengengewichtetem Durchschnittspreis je Ebene. Detailseite mit
|
||
Karte der Tankstelle, Kosten, Preis je Liter, SmartDeal-Ersparnis, abgeleiteter Volltankung und
|
||
Verbrauch. Erfassung von Hand und Beleg-Upload.</p>
|
||
<h3 id="56-einstellungen">5.6 Einstellungen</h3>
|
||
<p>Tag- und Nachtmodus, Beschriftung der Menüleiste, Fahrbahn unter dem Fahrzeug, Übersichtsbild,
|
||
WLAN-Name des Fahrzeugs (Textfeld mit Übernehmen-Knopf für das aktuell verbundene Netz),
|
||
Fahrten-Pausenzeit, SmartDeal mit Ablaufdatum, Ölwechsel-Intervall, Fahrzeugprofil sichern und
|
||
laden, CSV-Ausgabe, Sicherung, Versionsangaben.</p>
|
||
<hr />
|
||
<h2 id="6-datenhaltung">6. Datenhaltung</h2>
|
||
<h3 id="61-drei-getrennte-bestande">6.1 Drei getrennte Bestände</h3>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Bestand</th>
|
||
<th>Form</th>
|
||
<th>Begründung</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td><strong>Fahrzeugprofil</strong></td>
|
||
<td>eine JSON-Datei</td>
|
||
<td>alles Fahrzeugspezifische an einem Ort, für Portierung austauschbar</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Fahrten</strong></td>
|
||
<td>JSON Lines, eine Zeile je Fahrt</td>
|
||
<td>wächst laufend, überlebt Purge und Neuinstallation</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Tankvorgänge</strong></td>
|
||
<td>JSON Lines</td>
|
||
<td>dito</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<p>Servicebuch, Versicherung, Steuer, Reifen und Werkstatt gehören ins <strong>Profil</strong>, nicht in die
|
||
Archive — sie beschreiben das Fahrzeug, nicht seine Bewegung. Der WLAN-Name des Fahrzeugs gehört
|
||
aus demselben Grund ebenfalls ins Profil.</p>
|
||
<h3 id="62-portierbarkeit-ist-anforderung-nicht-zugabe">6.2 Portierbarkeit ist Anforderung, nicht Zugabe</h3>
|
||
<p>Die Anwendung muss ohne Codeänderung für ein anderes Fahrzeug nutzbar sein. Das heißt: <strong>keine
|
||
fahrzeugbezogene Angabe außerhalb des Profils</strong>, auch keine Bezeichnungen in Texten. Bilder werden
|
||
referenziert, nicht eingebettet.</p>
|
||
<p>Drei Wege der Bearbeitung sind zu unterstützen: in der Oberfläche, über Sichern und Laden der
|
||
Datei, und direkt im Dateisystem.</p>
|
||
<h3 id="63-fahrten-schema">6.3 Fahrten-Schema</h3>
|
||
<p><code>trip_id</code>, <code>ts_start</code>, <code>ts_end</code>, <code>duration_s</code>, <code>distance_km</code>, <code>km_quelle</code> (<code>odometer</code> | <code>gps</code>),
|
||
<code>odo_start</code>, <code>odo_end</code>, <code>avg_speed_kmh</code>, <code>start_lat/lon</code>, <code>end_lat/lon</code>, <code>start_address</code>,
|
||
<code>end_address</code>, <code>art</code> (<code>privat</code> | <code>arbeitsweg</code>), <code>route</code> (GeoJSON oder <code>null</code>), <code>pausen</code>,
|
||
<code>source</code> (<code>ha</code> | <code>manual</code> | <code>audi_connect</code>), <code>status</code> (<code>offen</code> | <code>vollständig</code>).</p>
|
||
<h3 id="64-tankvorgang-schema">6.4 Tankvorgang-Schema</h3>
|
||
<p><code>tank_id</code>, <code>receipt_key</code> (Zeitstempel, minutengenau), <code>receipt_no</code>, <code>tse_beleg_nr</code>, <code>ts</code>,
|
||
<code>ts_payment</code>, <code>ts_tse</code>, <code>station_id</code>, <code>station_name</code>, <code>station_address</code>, <code>article_no</code>,
|
||
<code>product_name</code>, <code>fuel_type</code>, <code>liters</code>, <code>list_price_per_l</code>, <code>discount</code>, <code>discount_per_l</code>,
|
||
<code>fuel_total_eur</code>, <code>receipt_total_eur</code>, <code>price_per_l</code>, <code>net_eur</code>, <code>vat_eur</code>, <code>odometer_km</code>,
|
||
<code>level_before_pct</code>, <code>level_after_pct</code>, <code>full_tank</code>, <code>source</code>, <code>status</code>, <code>receipt_file</code>,
|
||
<code>edited_fields</code>.</p>
|
||
<hr />
|
||
<h2 id="7-kernlogik">7. Kernlogik</h2>
|
||
<p>Diese sieben Punkte sind der eigentliche Auftrag. Die Oberfläche ist im Prototyp gelöst; hier liegt
|
||
die Arbeit.</p>
|
||
<h3 id="71-fahrterkennung">7.1 Fahrterkennung</h3>
|
||
<ul>
|
||
<li><strong>Start:</strong> iPhone verbindet sich mit dem hinterlegten Fahrzeug-WLAN (Companion-App-Sensor,
|
||
Name in den Einstellungen hinterlegt, §5.6)</li>
|
||
<li><strong>Ende:</strong> WLAN-Verbindung bricht ab und kommt nicht zurück</li>
|
||
<li><strong>Pausenregel:</strong> Kommt die Verbindung binnen <em>n</em> Minuten zurück (Vorgabe 15, einstellbar), gilt
|
||
die Fahrt als fortgesetzt. Der Datensatz wird deshalb <strong>erst nach Ablauf der Wartezeit</strong>
|
||
geschrieben, nicht beim Abriss. Dieselbe Wartezeit löst danach auch das Kilometerstand-Screening
|
||
aus (§7.2).</li>
|
||
</ul>
|
||
<h3 id="72-zweistufiger-fahrtabschluss">7.2 Zweistufiger Fahrtabschluss</h3>
|
||
<ol>
|
||
<li>Bei Fahrtende (WLAN-Verbindung länger als die Pausenzeit weg, §7.1): Datensatz aus GPS und
|
||
Uhrzeit, Status <code>offen</code></li>
|
||
<li>Screening: In der Recorder-Historie des Kilometerstand-Sensors wird nach dem Wert gesucht,
|
||
dessen Zeitstempel am nächsten am Verbindungsabbruch liegt — auch aus während der Fahrt
|
||
gemeldeten Werten, nicht nur aus dem letzten vor Fahrtende. Wird ein passender Wert gefunden:
|
||
Start- und Endkilometer nachtragen, Status <code>vollständig</code></li>
|
||
<li>Wird keiner gefunden, bleibt der Status <code>offen</code>. Das Screening läuft erneut, sobald ein neuer
|
||
Kilometerstand eintrifft — auch wenn das erst mit der nächsten Fahrt geschieht, da der
|
||
Kilometerstand laut Datenquelle nicht sicher mit Fahrtende, sondern teils erst mit Beginn oder
|
||
während der folgenden Fahrt übermittelt wird</li>
|
||
</ol>
|
||
<p>Bleibt der Wert dauerhaft aus, greift ersatzweise die GPS-Strecke, kenntlich über <code>km_quelle</code>. Das
|
||
betrifft insbesondere Fahrten, die in kurzem Abstand aufeinanderfolgen, bevor ein neuer
|
||
Kilometerstand eintrifft: nur die erste und letzte Fahrt einer solchen Kette lassen sich über den
|
||
Kilometerstand abgrenzen, die dazwischenliegenden fallen auf GPS zurück.</p>
|
||
<p>Technische Voraussetzung: Zugriff auf die Recorder-Historie (nicht nur den aktuellen Zustand) und
|
||
eine ausreichende Aufbewahrungsdauer des Recorders.</p>
|
||
<h3 id="73-strecke-aus-dem-kilometerstand">7.3 Strecke aus dem Kilometerstand</h3>
|
||
<p><code>km = Odometer(nach) − Odometer(vor)</code>. Das GPS liefert Route und Adressen, <strong>nicht</strong> die Distanz.
|
||
Dadurch ist eine lückenhafte Positionsaufzeichnung unschädlich.</p>
|
||
<h3 id="74-volltankung-ableiten">7.4 Volltankung ableiten</h3>
|
||
<p>Nicht abfragen. Füllstand nach dem Tanken ≥ 97 % → Volltankung. Bei Teilbetankung wird <strong>kein</strong>
|
||
Verbrauch ausgewiesen. Der Tankgeber meldet den Füllstand bereits unterhalb des Ausdehnungsraums —
|
||
100 % sind also tatsächlich 100 %, die Schwelle braucht keine Korrektur dafür. Die Auflösung des
|
||
Sensors ist ganzzahlig.</p>
|
||
<h3 id="75-reifenzahler">7.5 Reifenzähler</h3>
|
||
<p>Kein <code>utility_meter</code> — stattdessen direkte Berechnung: Zähler = aktueller (absoluter)
|
||
Kilometerstand − Startwert des aktiven Satzes. Der Startwert je Satz ist als Stammdatum im
|
||
Fahrzeugprofil hinterlegt. Das vermeidet die Abhängigkeit von <code>total_increasing</code> und von
|
||
<code>utility_meter</code>-internem Zustand, der bei zeitweise <code>unavailable</code> Quellsensoren (§4.1) Strecke
|
||
verschlucken kann — die Subtraktion braucht nur den zuletzt bekannten Absolutwert, keinen
|
||
fortgeschriebenen Zählerstand.</p>
|
||
<h3 id="76-serviceprognose">7.6 Serviceprognose</h3>
|
||
<p>Termine werden <strong>nicht gespeichert, sondern berechnet</strong>: letzter passender Servicebuch-Eintrag plus
|
||
Intervall. Gespeichert wird nur der vereinbarte Werkstatttermin.</p>
|
||
<p>Die Fahrleistung mischt zwei Zeiträume: den gesamten bekannten Verlauf und den Zeitraum seit dem
|
||
letzten Service, gewichtet über etwa 180 Tage. Damit wird die Vorhersage genauer, je näher das Ziel
|
||
rückt, und startet nach jedem Service neu.</p>
|
||
<p>Ein eingestelltes Intervall darf die Herstellervorgabe nicht überschreiten.</p>
|
||
<h3 id="77-belegverarbeitung">7.7 Belegverarbeitung</h3>
|
||
<p>Beleg-Upload in der App (§5.5) → Parser → Datensatz. Ersetzt den in §4.3 ursprünglich vorgesehenen
|
||
Weg über ein Postfach — kein IMAP, keine Zugangsdaten nötig. Regeln:</p>
|
||
<ol>
|
||
<li>Schlüssel ist der Zeitstempel aus der Kopfzeile, minutengenau</li>
|
||
<li>Liter, Preis und Betrag kommen vom Beleg, der Kilometerstand vom Fahrzeug</li>
|
||
<li>Manuell geänderte Felder werden nie von einem Import überschrieben</li>
|
||
<li>Nicht zuordenbare Fahrzeugereignisse bleiben als <code>beleg_fehlt</code> stehen</li>
|
||
<li>Der Gesamtbetrag kann Shop-Käufe enthalten — Kraftstoffanteil separat rechnen</li>
|
||
<li>Ohne Beleg: manuelle Eingabe von Liter und Preis als Fallback, bereits als Erfassungsweg unter
|
||
„Tanken" vorgesehen</li>
|
||
</ol>
|
||
<hr />
|
||
<h2 id="7a-bilder">7a. Bilder</h2>
|
||
<p>Bilder werden <strong>nicht</strong> eingebettet, sondern referenziert. Sie kommen am Ende dazu; die Anwendung
|
||
muss ohne sie vollständig bedienbar sein.</p>
|
||
<p><strong>Ordner <code>bilder/</code>, feste Dateinamen:</strong></p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Datei</th>
|
||
<th>Verwendung</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td><code>seitenansicht.webp</code>, <code>front-schraeg.webp</code>, <code>frontansicht.webp</code>, <code>heckansicht.webp</code>, <code>scheinwerfer.webp</code>, <code>cockpit.webp</code>, <code>sitze.webp</code></td>
|
||
<td>Galerie in „Mein Audi", eine davon als Übersichtsbild</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>seitenansicht-winter.webp</code></td>
|
||
<td>Seitenansicht mit montierten Winterrädern</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>rad-sommer.webp</code>, <code>rad-winter.webp</code></td>
|
||
<td>Radbilder in der Reifenansicht</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>fahrbahn-schnee.webp</code></td>
|
||
<td>Untergrund bei montierten Winterrädern</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<p><strong>Drei Bedingungen, damit das Nachreichen ohne Nacharbeit gelingt:</strong></p>
|
||
<ol>
|
||
<li><strong>Die Flächen behalten ihre Maße.</strong> Bildfläche 206 px hoch, auf der Übersicht 165 px, Radbilder
|
||
78 × 78 px. Platzhalter belegen dieselben Maße. Andernfalls springt das Layout, sobald die Bilder
|
||
kommen — und zwar in jeder Ansicht gleichzeitig.</li>
|
||
<li><strong>Die Namen stehen vorher fest.</strong> Das Übersichtsbild wird über seinen Namen ausgewählt, nicht
|
||
über eine Position. Ein späterer Namenswechsel bricht die Konfiguration.</li>
|
||
<li><strong>Die Bildaufbereitung ist ein eigenes Arbeitspaket</strong>, kein Nebenprodukt. Sie umfasst:
|
||
Freistellen des weißen Hintergrunds über eine Flutfüllung vom Rand — nicht über eine
|
||
Helligkeitsschwelle, sonst verschwinden Kennzeichen und Tagfahrlicht —, Montage der Winterräder
|
||
in die Seitenansicht, kreisrunde Maskierung der Radbilder und Zuschnitt der Fahrbahn mit
|
||
ausgeblendeter Oberkante.</li>
|
||
</ol>
|
||
<p>Der beiliegende Prototyp enthält alle Bilder bereits aufbereitet; sie können übernommen werden.</p>
|
||
<h2 id="8-gestaltung">8. Gestaltung</h2>
|
||
<p>Verbindlich ist der Prototyp. Die Regeln dahinter:</p>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th></th>
|
||
<th></th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td>Fläche</td>
|
||
<td><code>#161b23</code>, Kacheln <code>#1f2733</code></td>
|
||
</tr>
|
||
<tr>
|
||
<td>Text</td>
|
||
<td><code>#FFFFFF</code>, sekundär <code>#9aa1ad</code>, Beschriftungen <code>#657081</code></td>
|
||
</tr>
|
||
<tr>
|
||
<td>Akzent</td>
|
||
<td>Progressive Red <code>#F50537</code>, sparsam</td>
|
||
</tr>
|
||
<tr>
|
||
<td>Signalfarben</td>
|
||
<td>Grün <code>#15da15</code>, Gelb <code>#ffaa00</code>, Rot <code>#fd2c4e</code> (Nachtmodus)</td>
|
||
</tr>
|
||
<tr>
|
||
<td>Radien</td>
|
||
<td>Kacheln 20 px, Schaltflächen vollrund — dazwischen nichts</td>
|
||
</tr>
|
||
<tr>
|
||
<td>Schrift</td>
|
||
<td>Audi Type: Normal für die Oberfläche, Wide Light für Kennzahlen, Extended Italic für den Fahrzeugtitel</td>
|
||
</tr>
|
||
<tr>
|
||
<td>Zahlen</td>
|
||
<td>durchgehend Tausenderpunkt, Tabellenziffern</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<p>Tag- und Nachtmodus sind umschaltbar, alle Farben liegen als Rollen vor. Keine Schatten, keine
|
||
Verläufe außer der Fahrbahn unter dem Fahrzeug.</p>
|
||
<p><strong>Rechtlich:</strong> Audi Type, Ringe und Typenschild sind lizenz- beziehungsweise markenrechtlich
|
||
geschützt und ausschließlich für diese private, nicht veröffentlichte Installation freigegeben.</p>
|
||
<hr />
|
||
<h2 id="9-nicht-funktionale-anforderungen">9. Nicht-funktionale Anforderungen</h2>
|
||
<ul>
|
||
<li><strong>Erreichbarkeit:</strong> über Tailscale. Kein Funnel, keine Subnetz-Routen, kein Exit Node. Das iPhone
|
||
darf ausschließlich Home Assistant auf Port 8123 erreichen. Schlüsselablauf für das HA-Gerät
|
||
deaktivieren — andernfalls fällt der Zugang nach Monaten still aus. Verbindungsaufbau über
|
||
„VPN On Demand" (ab Tailscale 1.48 für iOS), Regel „Only On" gebunden an das Fahrzeug-WLAN —
|
||
kein dauerhaft aktiver Tunnel, geringerer Akkuverbrauch, und der Tunnel steht genau dann, wenn
|
||
auch die Fahrterkennung anspringt. Die WLAN-Zuordnung liegt in der Tailscale-App selbst und muss
|
||
bei einer Änderung des WLAN-Namens (§5.6) dort separat nachgezogen werden.</li>
|
||
<li><strong>Ausfall der Datenquelle:</strong> Veraltete Werte müssen als solche erkennbar sein. Die Anzeige
|
||
„Sicher abgestellt" hat drei Zustände: grün, rot und <strong>unbekannt</strong> mit Zeitstempel. Kein grünes
|
||
Häkchen auf Basis alter Daten.</li>
|
||
<li><strong>Sicherung:</strong> Profil und Archive müssen gesichert und wiederhergestellt werden können,
|
||
automatisch und von Hand.</li>
|
||
<li><strong>Ausgabe:</strong> Fahrten, Tankvorgänge und Servicebuch als CSV mit Semikolon und deutschem
|
||
Zahlenformat.</li>
|
||
<li><strong>Kalender:</strong> Service- und Reifenwechseltermine als Eintrag in den iOS-Kalender, mit Vorwarnung.</li>
|
||
<li><strong>Karten:</strong> Leaflet mit dem tatsächlich aufgezeichneten Track, nicht mit einer berechneten Route.
|
||
Kachelquelle wechselt mit dem Modus.</li>
|
||
<li><strong>Notrufnummern</strong> müssen auch dann erreichbar sein, wenn die Anwendung nicht lädt.</li>
|
||
</ul>
|
||
<hr />
|
||
<h2 id="10-vor-baubeginn-zu-klaren">10. Vor Baubeginn zu klären</h2>
|
||
<p>Diese Punkte waren bewusst offen und betreffen die Umsetzung in Home Assistant. Sie sind
|
||
inzwischen geklärt; Fragestellung und Entscheidung stehen im Folgenden jeweils zusammen.</p>
|
||
<ol>
|
||
<li><strong>Einbindung der Oberfläche.</strong> Eigenes Panel, eingebettete Seite, eigene Lovelace-Karte oder
|
||
Add-on? Der Prototyp ist eine einzelne HTML-Datei — welche Form davon trägt im Dauerbetrieb, und
|
||
wie kommt die Oberfläche an Zustände und Dienste?<br>
|
||
<strong>Entscheidung:</strong> <code>panel_custom</code> — Home Assistants eingebaute Panel-Integration, per YAML
|
||
registriert, kein Add-on und keine eigene Integration nötig. Das JS-Custom-Element aus dem
|
||
Prototyp bekommt automatisch das <code>hass</code>-Objekt injiziert (Live-Zustände per WebSocket,
|
||
<code>callService()</code> für Aktionen); die bestehenden Render-Funktionen (<code>vHome()</code>, <code>vTrips()</code> usw.)
|
||
bleiben erhalten, nur die Datenquelle wechselt von den Beispielarrays auf <code>hass.states</code>.</li>
|
||
<li><strong>Zurückschreiben von Daten.</strong> Wie schreibt die Oberfläche Dateien im Home-Assistant-Dateisystem?
|
||
Eigene Integration, AppDaemon, pyscript, Skript im Add-on? Diese Entscheidung bestimmt den
|
||
gesamten Aufbau.<br>
|
||
<strong>Entscheidung:</strong> <code>pyscript</code> für die gesamte Logik — Fahrterkennung, Fahrtabschluss,
|
||
Belegverarbeitung, Dateizugriff. Läuft installationsunabhängig (Core, Container, OS,
|
||
Supervised), im HA-Python-Kontext, mit <code>@service</code> für aufrufbare Aktionen und
|
||
<code>@time_trigger</code>/<code>task.sleep</code> für die Wartezeit-Logik aus §7.1.</li>
|
||
<li><strong>Bluetooth- und WLAN-Erkennung unter iOS.</strong> Meldet die Companion App die Verbindung zum
|
||
Fahrzeug zuverlässig genug für die Fahrterkennung, oder braucht es Kurzbefehle? <strong>Das ist die
|
||
riskanteste Annahme des Entwurfs</strong> — trägt sie nicht, muss die Fahrterkennung anders gelöst
|
||
werden.<br>
|
||
<strong>Entscheidung:</strong> WLAN statt Bluetooth, Kurzbefehle entfallen. Die WLAN-Verbindung zum
|
||
Fahrzeug baut sich nach bisheriger Erfahrung zuverlässig sofort auf und ab; Bluetooth-Region-
|
||
Monitoring unter iOS wäre dagegen mit mehrminütiger Verzögerung riskant gewesen. Trigger ist der
|
||
Companion-App-Sensor für das verbundene WLAN (§4.2, §7.1).</li>
|
||
<li><strong>Verzögerung des Kilometerstands.</strong> Wie lange dauert es nach dem Abstellen tatsächlich, bis der
|
||
Wert nachkommt? Bestimmt die Wartezeit im zweistufigen Abschluss.<br>
|
||
<strong>Entscheidung:</strong> kein fester Wartewert — der Kilometerstand kommt nicht sicher mit
|
||
Fahrtende, sondern teils erst mit Beginn oder während der nächsten Fahrt. Der Fahrtabschluss
|
||
läuft deshalb ereignisgesteuert über ein Screening der Recorder-Historie statt über einen
|
||
Timeout (§7.2).</li>
|
||
<li><strong>Auflösung des Tankfüllstands.</strong> Prozentgenau oder gerastert? Bestimmt, ob die 97-Prozent-Regel
|
||
für die Volltankung trägt.<br>
|
||
<strong>Entscheidung:</strong> ganzzahlig. Der Tankgeber meldet den Stand bereits unterhalb des
|
||
Ausdehnungsraums, 100 % sind also tatsächlich 100 % — die 97-Prozent-Schwelle bleibt
|
||
unverändert (§7.4).</li>
|
||
<li><strong>Meldet die Integration den Kilometerstand als <code>total_increasing</code>?</strong> Falls nicht, ist ein
|
||
vorgeschalteter Template-Sensor nötig, sonst funktioniert der Reifenzähler nicht.<br>
|
||
<strong>Entscheidung:</strong> Frage erübrigt sich — kein <code>utility_meter</code>, kein Template-Sensor. Der
|
||
Reifenzähler rechnet direkt mit dem absoluten Kilometerstand gegen den im Profil hinterlegten
|
||
Startwert je Satz (§7.5).</li>
|
||
<li><strong>Abholung der Belege aus dem Postfach.</strong> Welcher Weg, und wo läuft der Parser?<br>
|
||
<strong>Entscheidung:</strong> kein Postfach. Belege werden als PDF direkt in der App hochgeladen (§4.3,
|
||
§5.5, §7.7); ohne Beleg ersatzweise manuelle Eingabe von Liter und Preis.</li>
|
||
<li><strong>Verhalten des Tailscale-Tunnels beim Losfahren.</strong> Steht er rechtzeitig für den Fahrtstart?<br>
|
||
<strong>Entscheidung:</strong> Tailscale „VPN On Demand" mit Regel „Only On", gebunden an das
|
||
Fahrzeug-WLAN (§9). Der Tunnel baut sich damit zeitgleich mit der Fahrterkennung auf, kein
|
||
dauerhaft aktiver Tunnel nötig.</li>
|
||
</ol>
|
||
<hr />
|
||
<h2 id="11-abnahmekriterien">11. Abnahmekriterien</h2>
|
||
<ol>
|
||
<li>Eine Fahrt wird ohne Zutun erfasst, mit korrekten Kilometern aus dem Fahrzeug, und erscheint
|
||
vollständig in der Liste.</li>
|
||
<li>Ein Tankstopp mitten in einer Fahrt erzeugt <strong>eine</strong> Fahrt, nicht zwei.</li>
|
||
<li>Ein hochgeladener Shell-Beleg erzeugt einen vollständigen Tankvorgang. Derselbe Beleg ein
|
||
zweites Mal hochgeladen erzeugt keinen zweiten.</li>
|
||
<li>Eine manuelle Korrektur überlebt den nächsten Import.</li>
|
||
<li>Ein Eintrag im Servicebuch verschiebt die zugehörige Fälligkeit.</li>
|
||
<li>Das Umschalten der Reifen leitet die Kilometer auf den anderen Satz um und verliert bei einem
|
||
Neustart nichts.</li>
|
||
<li>Fällt die Datenquelle aus, zeigt die Oberfläche das an, statt alte Werte als aktuell auszugeben.</li>
|
||
<li>Ein Fahrzeugprofil lässt sich ausgeben, in einem Texteditor ändern und wieder einlesen.</li>
|
||
<li>Die Oberfläche ist über Tailscale erreichbar, ohne dass Home Assistant öffentlich steht.</li>
|
||
</ol>
|
||
<hr />
|
||
<h2 id="12-ausdrucklich-nicht-bestandteil">12. Ausdrücklich nicht Bestandteil</h2>
|
||
<ul>
|
||
<li>Fernsteuerung des Fahrzeugs — der Data-Act-Zugang ist ausschließlich lesend</li>
|
||
<li>Fehlerspeicher und Live-Telemetrie über OBD</li>
|
||
<li>Fahrstil- oder Effizienzbewertung</li>
|
||
<li>Höchstgeschwindigkeit je Fahrt</li>
|
||
<li>Mehrbenutzerbetrieb</li>
|
||
<li>Veröffentlichung, Weitergabe oder Verwendung der Marken- und Schriftlizenzen außerhalb dieser
|
||
Installation</li>
|
||
</ul>
|
||
<hr />
|
||
<h2 id="13-was-noch-zu-liefern-ist">13. Was noch zu liefern ist</h2>
|
||
<p>Vom Auftraggeber, vor oder während des Baus:</p>
|
||
<ul>
|
||
<li>Bilder der Winterräder in höherer Auflösung</li>
|
||
<li>Fälligkeitsdatum der Kfz-Steuer</li>
|
||
<li>Reifendaten je Satz: Hersteller, Modell, DOT, bisherige Laufleistung</li>
|
||
</ul>
|
||
<p>Entfallen gegenüber der ursprünglichen Liste: Zugangsdaten und Postfach für die Belegabholung
|
||
(Beleg-Upload statt Mailanbindung, §7.7) und Bestätigung des Bluetooth-Gerätenamens des MMI
|
||
(Fahrterkennung läuft über WLAN, §7.1) — der WLAN-Name ist bereits bekannt
|
||
(<code>Audi_MMI_2804_5GHz</code>) und in den Einstellungen hinterlegt (§5.6).</p>
|
||
<hr />
|
||
<h2 id="14-zeitlicher-rahmen">14. Zeitlicher Rahmen</h2>
|
||
<p><strong>Eine Frist ist hart:</strong> Die Alt-App wird Ende 2026 abgeschaltet. Der Export des vorhandenen
|
||
Fahrtenbuchs muss vorher erfolgen — unabhängig vom Baufortschritt und als Erstes. Was danach
|
||
verloren ist, ist nicht wiederherstellbar.</p>
|
||
<p>Der <strong>Import</strong> der exportierten Daten in das neue System ist davon unabhängig und wird bewusst
|
||
zurückgestellt, bis die App sich im laufenden Betrieb bewährt hat. Die Frist betrifft nur die
|
||
Sicherung der Daten, nicht ihre Weiterverarbeitung.</p>
|
||
<p>Alles Übrige lässt sich auch nach der Abschaltung bauen.</p></main>
|
||
</div>
|
||
</body>
|
||
</html>
|