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
@@ -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