Fehlerspeicher, Amber statt Gruen, Tankbalken rot ab 90 km

- Neue Sensorrolle FEHLERSPEICHER_SENSOR (number_of_diagnostic_trouble_codes
  vom CAN) und eine fuenfte Zeile auf der Sicherheitsseite: "Keine
  Fehlermeldungen". Bewusst NICHT im _sicherheitscheck(): "gesichert"
  rechnet ein UND ueber diese Liste, und ein Eintrag im Fehlerspeicher sagt
  nichts darueber, ob der Wagen verschlossen dasteht.
- "Zuendung an" ist Amber statt Gruen. Gruen las sich als "alles in Ordnung
  und sicher"; unterwegs ist kein guter Zustand, sondern ein anderer.
- Das Modell-Auswahlfeld bekam dm-eingabe statt dm-auswahl - nur letzteres
  setzt appearance:none, deshalb zeichnete der Browser seinen nativen Pfeil
  neben den des Design-Systems. Die Breitenregel heisst jetzt
  dm-feld--fahrzeug und gilt fuer input und select.
- "aktuell" erscheint erst nach einer Pruefung, und fuer beide Abschnitte
  gemeinsam. Der App-Abschnitt zeigte die Marke schon, sobald ein Buendel
  vorlag - also unmittelbar nach einem Update wieder.
- Der Tankbalken wird rot ab 90 km Restreichweite, in Kilometern und nicht
  in Prozent: die Reichweite ist die Aussage, um die es geht, und das
  Fahrzeug rechnet die Fahrweise darin bereits ein. Farbe --red (Audi-CI,
  #F50537), nicht --bad-flaeche - letzteres ist fuer weisse Schrift darauf
  gewaehlt, und hier steht keine drauf.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-09-05 17:31:52 +02:00
parent 255a593b50
commit e76252505f
14 changed files with 258 additions and 20 deletions
+69
View File
@@ -7770,6 +7770,75 @@ abgleichen, und nur bei bekanntem Wert) live belegt.
gespeicherten Zugang; belegt sind sie über `tsc`, die 303 Tests und den
Listenvergleich gegen die Panel-Quelle.
### Nachtrag (2026.9.5.36.39): Fehlerspeicher, Amber, Reservebalken
**Der Doppelpfeil am Modellfeld war meiner.** Das neue `<select>` bekam
`dm-eingabe` statt `dm-auswahl` — und nur letzteres setzt `appearance: none`.
Der Aufklapp-Pfeil kommt als Hintergrundbild aus dem Design-System
(`.ads-feld select`), der Browser zeichnete seinen eigenen daneben. Behoben;
die Breitenregel heißt jetzt `dm-feld--fahrzeug` und gilt für `input` **und**
`select`. **Merkposten:** `.ads-feld select` im Design-System entfernt den
nativen Pfeil NICHT selbst, obwohl sein Kommentar das behauptet — die App
liefert den Reset über `.dm-auswahl` nach. Jedes neue `<select>` braucht diese
Klasse.
**„Zündung an" ist Amber, nicht Grün.** Ich hatte Grün gesetzt; der Eigentümer:
grün läse sich als „alles in Ordnung und sicher". Unterwegs ist kein guter
Zustand, sondern ein anderer — Übersicht und Sicherheitsseite tragen jetzt
`--warn`.
**Fehlerspeicher als fünfte Zeile.** Der Dongle liefert
`number_of_diagnostic_trouble_codes` direkt vom CAN (gemessen, 0 im Testwagen).
Neue Rolle `FEHLERSPEICHER_SENSOR`; die Zeile heißt „Keine Fehlermeldungen",
sonst „N Fehlermeldungen", ohne Sensor „Fehlerspeicher unbekannt".
**Bewusst NICHT im `_sicherheitscheck()`**, sondern als eigenes Feld
`fehlerspeicher`: `gesichert` rechnet ein UND über alle Punkte dieser Liste,
und ein Eintrag im Fehlerspeicher sagt nichts darüber, ob der Wagen
verschlossen dasteht. Live belegt: `sicherheitscheck` bleibt bei 13 Punkten,
`gesichert` unverändert `true`. WELCHER Fehler vorliegt, meldet das Gerät
nicht — ein Reserve- oder Warnsignal existiert ebenfalls nicht (alle 54
Entitäten geprüft).
**„aktuell" erst nach einer Prüfung, für beide Abschnitte.** Der App-Abschnitt
zeigte die grüne Marke, sobald ein Bündel vorlag — also unmittelbar nach einem
Update wieder, ohne dass jemand nachgesehen hätte. Beide Marken hängen jetzt an
`geprueft_am`; der eine Knopf prüft beides, also werden beide zusammen grün.
**Der Tankbalken wird rot ab 90 km Restreichweite.**
Erst hatte ich das in Prozent umrechnen wollen, mit dem Argument, eine
km-Schwelle löse je nach Fahrweise bei unterschiedlichen Füllständen aus. Der
Eigentümer hat das verworfen, und zu Recht: „90km sind absolute 90km", und
„falls der verbrauch sich verdoppelt wird der balken einfach früher schon rot"
— genau das ist der Zweck. Die Reichweite ist die Aussage, um die es geht, und
das Fahrzeug rechnet die Fahrweise darin bereits ein.
Zur Schwelle selbst, damit sie nicht als Messung gilt: **sie ist eine
Festlegung.** Belegt ist nur eine Untergrenze — das Bildschirmfoto der
myAudi-App zeigt 70 km rot; ein höherer Wert wurde nie im grauen Zustand
festgehalten. Aus unseren Daten ist die Schwelle der myAudi-App nicht
ableitbar. Gemessen wurde dabei immerhin die Umrechnung, aus 494 Wertepaaren
über 45 Tage: **10,46 km je Liter** (Median, 1,71 bis 12,53) und 0,613 l je
Prozentpunkt, also ein rechnerischer Tank von **61,3 l** — das Fahrzeugprofil
trägt 58 l.
Farbe: **`--red` (#F50537, Audi-CI-Rot)**, nicht `--bad-flaeche` (#D70015).
Letzteres ist Apples dunkles Rot und so gewählt, dass weiße Schrift darauf
lesbar bleibt; auf dem Balken steht keine Schrift, der Grund fällt weg. Vom
Eigentümer als „weniger dunkel" gemeldet. Rot als Fläche ist sonst
zerstörenden Aktionen vorbehalten — hier eine benannte Ausnahme, weil es kein
Bedienelement ist, sondern eine Warnung. Live gemessen: `rgb(245, 5, 55)` auf
`rgba(120,120,128,.16)`, 6 px, voll gerundet.
**Offen:** die Ziffern der Reichweite wirken in der myAudi-App dicker. Unsere
`.fig` steht auf Audi Type Wide **300** — und das ist der einzige Wide-Schnitt
im Repo (`audi-type-wide-300.woff2`; daneben nur `audi-type-400` und
`audi-type-extended-400-italic`). Ein dickerer Wide-Schnitt lässt sich damit
nicht setzen, und synthetisches Fetten verschmiert die Striche. Braucht
entweder die Schriftdatei vom Eigentümer oder die Entscheidung, für diese eine
Zahl auf „Audi Type" 400 zu wechseln.
## Working conventions (observed — keep them)
- German is the project language: identifiers, comments, commits, UI texts. Exceptions:
+3
View File
@@ -127,6 +127,9 @@ export interface Fahrzeugstatus {
batteriespannung?: number | null;
gesichert?: boolean | null;
sicherheitscheck?: SicherheitsPunkt[];
/** Diagnosecodes im Fehlerspeicher. Getrennt vom Sicherheitscheck, weil ein
Eintrag nichts darueber sagt, ob der Wagen verschlossen dasteht. */
fehlerspeicher?: { anzahl: number | null; ok: boolean | null } | null;
/** Zustand des Zündungssensors — ausdrücklich nicht „fährt": es gibt keinen
Bewegungssensor, und die Zündung meldet an auch bei Zubehörstellung oder
im Stand. `null`/fehlend heißt „kein Zündungssensor zugeordnet", also
+4
View File
@@ -66,6 +66,9 @@ export interface Fahrzeug {
batteriespannung: number | null
gesichert: boolean | null
sicherheitscheck: SicherheitsPunkt[]
/** Diagnosecodes im Fehlerspeicher; `null` heisst "kein Sensor zugeordnet
oder er meldet nichts" - nie als "keine Fehler" deuten. */
fehlerspeicher: { anzahl: number | null; ok: boolean | null } | null
/** Zustand des Zündungssensors — nicht „fährt": ohne Bewegungssensor sagt
an nur, dass die Zündung an ist. `null` heißt „kein Zündungssensor
zugeordnet", also unbekannt — nicht „steht". */
@@ -181,6 +184,7 @@ export function profilZuFahrzeug(profil: Profil, status: Fahrzeugstatus): Fahrze
batteriespannung: status.batteriespannung ?? null,
gesichert: status.gesichert ?? null,
sicherheitscheck: status.sicherheitscheck ?? [],
fehlerspeicher: status.fehlerspeicher ?? null,
zuendung: status.zuendung ?? null,
geparktSeit: (status.geparkt_seit as string | null) ?? null,
faehrtSeit: (status.faehrt_seit as string | null) ?? null,
+1
View File
@@ -26,6 +26,7 @@ function fahrzeug(odo: number, modus = "hersteller"): Fahrzeug {
batteriespannung: null,
gesichert: null,
sicherheitscheck: [],
fehlerspeicher: null,
zuendung: null,
geparktSeit: null,
faehrtSeit: null,
+26 -10
View File
@@ -412,8 +412,13 @@ export function Einstellungen({
gesucht. Die Liste selbst steht in daten/fahrzeugmodelle.ts und
wird von einem Test gegen die Panel-Fassung geprüft. */}
<Feld label="Modell">
{/* dm-auswahl, NICHT dm-eingabe: nur dort steht `appearance: none`.
Der Aufklapp-Pfeil kommt als Hintergrundbild aus dem
Design-System (.ads-feld select); ohne den Reset zeichnet der
Browser seinen eigenen daneben — vom Eigentümer am 05.09.2026
als „Doppelpfeil" gemeldet. */}
<select
className="dm-eingabe dm-eingabe--fahrzeug"
className="dm-auswahl dm-feld--fahrzeug"
value={werte.fahrzeugtitel}
onChange={(e) => {
aendern({ fahrzeugtitel: e.target.value })
@@ -429,7 +434,7 @@ export function Einstellungen({
</Feld>
<Feld label="Kennzeichen">
<input
className="dm-eingabe dm-eingabe--fahrzeug"
className="dm-eingabe dm-feld--fahrzeug"
value={werte.kennzeichen}
onChange={(e) => aendern({ kennzeichen: e.target.value })}
onBlur={() => sichern()}
@@ -437,7 +442,7 @@ export function Einstellungen({
</Feld>
<Feld label="Erstzulassung">
<input
className="dm-eingabe dm-eingabe--fahrzeug"
className="dm-eingabe dm-feld--fahrzeug"
placeholder="TT.MM.JJJJ"
value={erstzulassungWert}
onChange={(e) => setzeErstzulassung(e.target.value)}
@@ -446,7 +451,7 @@ export function Einstellungen({
</Feld>
<Feld label="Ausführung" last>
<input
className="dm-eingabe dm-eingabe--fahrzeug"
className="dm-eingabe dm-feld--fahrzeug"
value={werte.ausfuehrung}
onChange={(e) => aendern({ ausfuehrung: e.target.value })}
onBlur={() => sichern()}
@@ -844,7 +849,13 @@ export function Einstellungen({
</>
) : (
<>
{integrationUpdate && <span className="dm-standmarke">aktuell</span>}
{/* Nur nach einer Prüfung. „aktuell" ohne Prüfung wäre eine
Behauptung ohne Grundlage — dieselbe Bedingung trägt der
App-Abschnitt darunter, damit beide gemeinsam grün werden
(Vorgabe des Eigentümers, 05.09.2026). */}
{integrationUpdate?.geprueft_am && (
<span className="dm-standmarke">aktuell</span>
)}
</>
)}
{updateAktionFehler && <p className="dm-fehler">{updateAktionFehler}</p>}
@@ -890,11 +901,16 @@ export function Einstellungen({
nach dem Neustart etwas. */
<p className="dm-erklaerung">Update {otaBuendel.version} vorhanden</p>
) : otaBuendel ? (
/* Dieselbe grüne Marke wie bei der Integration, statt eines
Satzes - Vorgabe des Eigentümers, 05.09.2026. Ohne Zeitpunkt:
anders als bei der Integration wird hier nicht auf Knopfdruck
geprüft, das Bündel meldet der Server beim Laden mit. */
<span className="dm-standmarke">aktuell</span>
/* Dieselbe grüne Marke wie bei der Integration - und an
dieselbe Bedingung geknüpft: erst nach einer Prüfung. Das
ndel meldet der Server zwar bei jedem Laden mit, aber
„aktuell" stand damit schon unmittelbar nach einem Update
wieder da, ohne dass jemand nachgesehen hätte (vom Eigentümer
am 05.09.2026 bemerkt). Der eine Knopf unten prüft beides,
also werden auch beide zusammen grün. */
integrationUpdate?.geprueft_am ? (
<span className="dm-standmarke">aktuell</span>
) : null
) : (
<p className="dm-erklaerung">
Der Server hat gerade kein Update-Bündel hinterlegt.
+21 -1
View File
@@ -44,6 +44,17 @@ export function Fahrzeugstatus({ geheZu }: { geheZu: (name: SeitenName) => void
Schlosssensoren melden — nur die Überschrift darf nicht „abgestellt"
behaupten, während die Zündung an ist. */
const faehrt = standortZustand(fahrzeug).faehrt
const fehlerAnzahl =
typeof fahrzeug.fehlerspeicher?.anzahl === "number" ? fahrzeug.fehlerspeicher.anzahl : null
const fehlerOk = fehlerAnzahl === null ? null : fehlerAnzahl === 0
const fehlerText =
fehlerAnzahl === null
? "Fehlerspeicher unbekannt"
: fehlerAnzahl === 0
? "Keine Fehlermeldungen"
: fehlerAnzahl === 1
? "1 Fehlermeldung"
: `${fehlerAnzahl} Fehlermeldungen`
const sammelText = faehrt
? "Zündung an - Fahrzeug in Betrieb"
: gesichert === null
@@ -68,7 +79,7 @@ export function Fahrzeugstatus({ geheZu }: { geheZu: (name: SeitenName) => void
{/* Während der Fahrt weder Haken noch Kreuz noch Fragezeichen, sondern
derselbe Punkt wie im Standortmenü — der Zustand ist dann nicht
„geprüft", sondern schlicht ein anderer. */}
{faehrt ? <span className="ads-dot" /> : <StatusKreis ok={gesichert} />}
{faehrt ? <span className="ads-dot ads-dot--warn" /> : <StatusKreis ok={gesichert} />}
<span>{sammelText}</span>
</Tile>
@@ -105,6 +116,15 @@ export function Fahrzeugstatus({ geheZu }: { geheZu: (name: SeitenName) => void
{licht === null ? "Licht unbekannt" : licht ? "Licht ausgeschaltet" : "Licht eingeschaltet"}
</span>
</div>
{/* Der Fehlerspeicher des Fahrzeugs. Steht bewusst NICHT im
Sicherheitscheck und geht damit auch nicht in „sicher
abgestellt" ein — ein Eintrag sagt nichts darüber, ob der Wagen
verschlossen dasteht (siehe _fehlerspeicher() im Backend).
WELCHER Fehler vorliegt, meldet das Gerät nicht. */}
<div className="dm-sicherheitszeile">
<StatusKreis ok={fehlerOk} />
<span className="dm-sicherheitszeile__text">{fehlerText}</span>
</div>
</div>
</Tile>
</>
+28 -2
View File
@@ -29,6 +29,8 @@ import { Typenschild, schildErkennen } from "./Typenschild"
// links der Kennzahl - deckungsgleich mit ART_ICON im Panel (vHome()). Die
// Hauptuntersuchung hat kein Restkilometer-Ziel und zeigt stattdessen den
// Art-Namen selbst unter dem (gleich großen) Datum.
const RESERVE_KM = 90
const SERVICE_SYMBOL: Partial<Record<string, typeof SymbolOelwechsel>> = {
"Ölwechsel": SymbolOelwechsel,
"Inspektion": SymbolInspektion,
@@ -102,9 +104,30 @@ export function Uebersicht({ geheZu }: { geheZu: (name: SeitenName, id?: string)
Wortlaut wie im Standortmenü: „Zündung an", nicht „fährt" — einen
Bewegungssensor gibt es nicht, die Zündung steht auch im Stand mit
laufendem Motor auf an. Deckungsgleich mit vHome() im Panel. */
/* Ab wann der Tankbalken rot wird - in KILOMETERN Restreichweite, nicht in
Prozent. Die Reichweite ist die Aussage, um die es geht ("wie weit komme ich
noch"), und das Fahrzeug rechnet die Fahrweise darin bereits ein; eine
Prozentschwelle haette bei 1,71 bis 12,53 km je Liter (in 45 Tagen gemessen)
bei ganz unterschiedlichen Restreichweiten ausgeloest.
90 km ist die Zahl des Eigentuemers, an der myAudi-App beobachtet. Belegt ist
daraus nur eine Untergrenze: sein Bildschirmfoto zeigt 70 km rot, ein Wert
darueber wurde nie im grauen Zustand festgehalten. Der Dongle liefert kein
Reserve- oder Warnsignal (alle 54 Entitaeten geprueft), die Schwelle der
myAudi-App steht also in keinen Daten, die wir haben - dies ist eine
Festlegung, keine Messung.
Ohne bekannte Reichweite bleibt der Balken neutral: eine Warnung aus
fehlenden Daten waere geraten. */
const tankReserve =
typeof fahrzeug.reichweite === "number" && fahrzeug.reichweite <= RESERVE_KM
const faehrt = standortZustand(fahrzeug).faehrt
/* Amber, nicht grün: „Zündung an" ist kein guter Zustand, sondern ein
anderer — grün läse sich als „alles in Ordnung und sicher" (Vorgabe des
Eigentümers, 05.09.2026). */
const sicherheitsstatus = faehrt
? "ok"
? "warn"
: fahrzeug.gesichert === true
? "ok"
: fahrzeug.gesichert === false
@@ -190,7 +213,10 @@ export function Uebersicht({ geheZu }: { geheZu: (name: SeitenName, id?: string)
{fahrzeug.tankBekannt ? `${de(fahrzeug.tankPct)} %` : ""}
</span>
</div>
<ProgressBar percent={Math.max(0, Math.min(100, fahrzeug.tankPct))} />
<ProgressBar
percent={Math.max(0, Math.min(100, fahrzeug.tankPct))}
className={tankReserve ? "dm-balken--reserve" : ""}
/>
<span className="ads-eyebrow" style={{ marginTop: "var(--sp-3)" }}>
Reichweite
</span>
+16 -1
View File
@@ -37,7 +37,8 @@
Oberflächen: das längste Label ("Erstzulassung") lässt in der App nur
183px fürs Feld, im Panel 196px. 180 passt in beiden mit etwas Luft und
hält sie zugleich untereinander gleich. */
.ads-feld input.dm-eingabe--fahrzeug {
.ads-feld input.dm-feld--fahrzeug,
.ads-feld select.dm-feld--fahrzeug {
width: 180px;
flex: 0 0 auto;
max-width: none;
@@ -2383,3 +2384,17 @@ input[type="range"] {
/* Spinner UND Beschriftung in EINEM Knopf: seit dem 05.09.2026 trägt der
Knopf der Update-Kachel den laufenden Vorgang selbst, statt dass daneben
eine eigene Ladezeile steht (Vorgabe des Eigentümers: „warum nicht den
update suchen button dafür verwenden"). */
.dm-knopflader {
display: inline-flex;
align-items: center;
gap: 10px;
}
/* Tankbalken in der Reserve: das Audi-CI-Rot (--red, #F50537), nicht
--bad-flaeche (#D70015). Letzteres ist Apples dunkles Rot und ist so
gewaehlt, dass WEISSE SCHRIFT darauf lesbar bleibt - hier steht keine
Schrift auf der Flaeche, der Grund faellt also weg. Der Balken bildet die
myAudi-App nach, und deren Rot ist das der Marke (vom Eigentuemer am
05.09.2026 als "weniger dunkel" gemeldet).
@@ -163,6 +163,12 @@ class Sensorzuordnung:
# veroeffentlichung.py.
DACH_SENSOR: str = ""
LICHT_SENSOR: str = ""
# Anzahl der im Fehlerspeicher stehenden Diagnosecodes (OBD/CAN). 0 heisst
# "keine Fehlermeldungen"; jeder Wert darueber ist einer. Der Sensor sagt
# NICHT, welcher Fehler vorliegt - das steht nicht im Datensatz des
# Geraets. Zaehlt bewusst nicht in "sicher abgestellt" hinein: ein Auto
# mit einem Eintrag im Fehlerspeicher steht trotzdem verschlossen da.
FEHLERSPEICHER_SENSOR: str = ""
# Vom Fahrzeug selbst gemeldete Service-Fälligkeit (ergänzt die
# App-eigene, aus dem Servicebuch berechnete Prognose).
@@ -322,6 +328,11 @@ FELDER: list[dict] = [
"domains": ["binary_sensor"], "device_classes": ["light"], "units": [], "liste": False, "pflicht": False,
"beispiel": "parking_lights",
"stichworte": ["licht", "standlicht", "light", "parking lights"]},
{"key": "FEHLERSPEICHER_SENSOR", "label": "Fehlerspeicher", "gruppe": "sicherheit",
"hinweis": "Anzahl der Diagnosecodes (DTC). 0 = keine Fehlermeldungen.",
"domains": ["sensor"], "device_classes": [], "units": [], "liste": False, "pflicht": False,
"beispiel": "number_of_diagnostic_trouble_codes",
"stichworte": ["dtc", "fehler", "diagnostic", "trouble", "codes", "fault"]},
{"key": "NAECHSTER_OELWECHSEL_SENSOR", "label": "Nächster Ölwechsel", "gruppe": "uebersicht",
"hinweis": "Vom Fahrzeug selbst gemeldete Fälligkeit, ergänzt die App-eigene Servicebuch-Prognose.",
"domains": ["sensor"], "device_classes": ["date", "timestamp"], "units": [], "liste": False, "pflicht": False,
@@ -1 +1 @@
{"version":"2026.9.5.35","sha256":"8873fddf9a976ff3317fb16cf4ed708ac19ef76dbf8faf4ff29189e2a5f83a75","bytes":324134,"gebaut":"2026-09-05T15:01:50Z"}
{"version":"2026.9.5.39","sha256":"1a78c65f9a96298f7ed97a8595e6505cf91f00a71620b068b4afcfdd14020483","bytes":324341,"gebaut":"2026-09-05T15:29:32Z"}
@@ -232,6 +232,8 @@ function profilZuCar(p, status) {
reichweite: status.reichweite_km,
batteriespannung: status.batteriespannung,
gesichert: status.gesichert,
// Getrennt vom Sicherheitscheck, siehe _fehlerspeicher() im Backend.
fehlerspeicher: status.fehlerspeicher ?? null,
sicherheitscheck: status.sicherheitscheck || [],
zuendung: status.zuendung ?? null,
geparktSeit: status.geparkt_seit ?? null,
@@ -2298,6 +2300,25 @@ function standAlterTicken() {
const sy = $("#sync span"); if (sy) sy.textContent = standAlterText();
}
/* Ab wann der Tankbalken rot wird - in KILOMETERN Restreichweite, nicht in
Prozent. Die Reichweite ist die Aussage, um die es geht ("wie weit komme ich
noch"), und das Fahrzeug rechnet die Fahrweise darin bereits ein; eine
Prozentschwelle haette bei 1,71 bis 12,53 km je Liter (in 45 Tagen gemessen)
bei ganz unterschiedlichen Restreichweiten ausgeloest.
90 km ist die Zahl des Eigentuemers, an der myAudi-App beobachtet. Belegt ist
daraus nur eine Untergrenze: sein Bildschirmfoto zeigt 70 km rot, ein Wert
darueber wurde nie im grauen Zustand festgehalten. Der Dongle liefert kein
Reserve- oder Warnsignal (alle 54 Entitaeten geprueft), die Schwelle der
myAudi-App steht also in keinen Daten, die wir haben - dies ist eine
Festlegung, keine Messung.
Ohne bekannte Reichweite bleibt der Balken neutral: eine Warnung aus
fehlenden Daten waere geraten. */
const RESERVE_KM = 90;
function tankReserve() {
return typeof CAR.reichweite === "number" && CAR.reichweite <= RESERVE_KM;
}
function vHome() {
const g = CAR.gesichert;
/* Faehrt der Wagen, kann er nicht gleichzeitig "sicher abgestellt" sein -
@@ -2319,7 +2340,10 @@ function vHome() {
const statusText = faehrt
? "Zündung an"
: g === null ? "Status unbekannt" : g ? "Sicher abgestellt" : "Nicht sicher abgestellt";
const statusDot = faehrt ? "" : g === null ? "warn" : g ? "" : "bad";
/* Amber, nicht gruen: "Zuendung an" ist kein guter Zustand, sondern ein
anderer - gruen laese sich als "alles in Ordnung und sicher" (Vorgabe des
Eigentuemers, 05.09.2026). */
const statusDot = faehrt ? "warn" : g === null ? "warn" : g ? "" : "bad";
const pct = CAR.tankPct;
const t = naechsterTermin();
/* C2/C5/D1: Reihenfolge der myAudi-App - randloses Fahrzeugbild, Name und
@@ -2345,7 +2369,7 @@ function vHome() {
<div class="fig" style="font-size:60px">${CAR.reichweite != null ? de(CAR.reichweite) : ""}<span class="unit">km</span></div>
<span class="pct">${CAR.tankBekannt ? pct + " %" : ""}</span>
</div>
<div class="balken"><i style="width:${pct}%;background:var(--fg)"></i></div>
<div class="balken"><i style="width:${pct}%;background:var(${tankReserve() ? "--red" : "--fg"})"></i></div>
<span class="label" style="margin-top:12px">Reichweite</span>
</div>
${(() => {
@@ -2436,6 +2460,30 @@ function sicherheitsGruppe(gruppe) {
return { eintraege, ok };
}
/* Der Fehlerspeicher des Fahrzeugs, als eigene Zeile unter den vier
Sammelzeilen. Er steht bewusst NICHT im Sicherheitscheck und geht damit
auch nicht in "sicher abgestellt" ein: ein Eintrag im Fehlerspeicher sagt
nichts darueber, ob der Wagen verschlossen dasteht (siehe
_fehlerspeicher() im Backend).
Die Anzahl wird genannt, sobald sie groesser als null ist - "es liegt etwas
vor" ohne Zahl waere weniger, als der Sensor hergibt. WELCHER Fehler es
ist, steht nicht im Datensatz des Geraets und kann hier deshalb auch nicht
stehen. */
function fehlerzeile() {
const f = CAR.fehlerspeicher || {};
const anzahl = typeof f.anzahl === "number" ? f.anzahl : null;
const ok = anzahl === null ? null : anzahl === 0;
const text = anzahl === null
? "Fehlerspeicher unbekannt"
: anzahl === 0
? "Keine Fehlermeldungen"
: anzahl === 1
? "1 Fehlermeldung"
: `${anzahl} Fehlermeldungen`;
return `<div class="row sicherheitszeile">${statusKreis(ok)}
<span class="sicherheitszeile-text">${esc(text)}</span></div>`;
}
function vSicherheit() {
const g = CAR.gesichert;
const verriegelt = sicherheitsGruppe("verriegelt");
@@ -2464,7 +2512,7 @@ function vSicherheit() {
return `
<div class="szene bleed schmal"><div class="carbox">${bildMitPlatzhalter(bildAdresse("draufsicht.webp"), "draufsicht.webp", "Draufsicht", "carfix klein")}</div></div>
<div class="tile" style="display:flex;align-items:center;gap:12px;padding:16px 20px">
${faehrt ? '<span class="dot"></span>' : statusKreis(g)}
${faehrt ? '<span class="dot warn"></span>' : statusKreis(g)}
<span style="font-size:15px;color:var(--fg)">${esc(sammelText)}</span>
</div>
<div class="tile sicherheitsliste">
@@ -2472,6 +2520,7 @@ function vSicherheit() {
${zeile("Türen und Klappen geschlossen", tuerenKlappen.ok, "Türen oder Klappen offen", "Türen/Klappen unbekannt", "tuerenklappen")}
${zeile("Fenster und Dach geschlossen", fensterDach.ok, "Fenster oder Dach offen", "Fenster/Dach unbekannt")}
${zeile("Licht ausgeschaltet", licht.ok, "Licht eingeschaltet", "Licht unbekannt")}
${fehlerzeile()}
</div>`;
}
@@ -3833,7 +3882,9 @@ function vEinst() {
<dd>${esc(u.version)}${u.geprueft_am ? `<small>geprüft ${dezeitpunkt(new Date(u.geprueft_am))}</small>` : ""}</dd></div></dl>
`;
}
return u ? `<span class="standmarke">aktuell</span>` : "";
// Nur nach einer Pruefung - "aktuell" ohne Pruefung waere eine
// Behauptung ohne Grundlage. Wortgleich mit der App.
return u && u.geprueft_am ? `<span class="standmarke">aktuell</span>` : "";
})()}
${INTEGRATION_UPDATE && INTEGRATION_UPDATE.fehler && !INTEGRATION_UPDATE_LAEUFT
? `<span class="label" style="margin-top:10px;color:var(--red)">${esc(INTEGRATION_UPDATE.fehler)}</span>`
@@ -1,7 +1,7 @@
{
"domain": "audi_dashboard",
"name": "Audi Dashboard",
"version": "2026.9.5.35",
"version": "2026.9.5.39",
"documentation": "https://gitea.nothaft.cloud/paul/audi-app/src/branch/main/README.md",
"issue_tracker": "https://gitea.nothaft.cloud/paul/audi-app/issues",
"codeowners": [
@@ -190,6 +190,26 @@ def _sicherheitscheck(hass: HomeAssistant, werte: Sensorzuordnung) -> list[dict]
return eintraege
def _fehlerspeicher(hass: HomeAssistant, werte: Sensorzuordnung) -> dict:
"""Wie viele Diagnosecodes im Fehlerspeicher stehen.
BEWUSST NICHT Teil von _sicherheitscheck(): dort steht ausschliesslich,
was zu oder verriegelt ist, und daraus rechnet "gesichert" ein UND ueber
alle Punkte. Ein Fehlerspeichereintrag sagt nichts darueber, ob der Wagen
sicher abgestellt ist - er wuerde die Aussage still verfaelschen.
"anzahl" ist None, solange kein Sensor zugeordnet ist oder er nichts
meldet; die Oberflaechen zeigen das als "unbekannt" und raten nicht auf
"keine Fehler".
"""
roh = zustand_oder_none(hass, werte.FEHLERSPEICHER_SENSOR)
anzahl = _zu_zahl(roh)
if anzahl is None:
return {"anzahl": None, "ok": None}
ganz = int(anzahl)
return {"anzahl": ganz, "ok": ganz == 0}
def fahrzeugstatus(
hass: HomeAssistant,
werte: Sensorzuordnung,
@@ -238,6 +258,8 @@ def fahrzeugstatus(
"batteriespannung": spannung,
"gesichert": gesichert,
"sicherheitscheck": sicherheitscheck,
# Getrennt vom Sicherheitscheck, siehe _fehlerspeicher().
"fehlerspeicher": _fehlerspeicher(hass, werte),
# Fährt das Fahrzeug gerade? Kommt aus derselben Zündungs-Entität, die
# auch die Fahrterkennung als maßgebliches Signal für Fahrtbeginn und
# -ende nimmt - damit sagen Anzeige und Erfassung zwangsläufig