Ein Update-Knopf, Modell als Auswahlfeld, abgestellt schliesst fahrend aus
- Die Update-Kachel hat genau einen Knopf am Ende. Er traegt in jedem Zustand den naechsten faelligen Schritt (suchen, installieren, Neustart, Neuladen); Beschriftung und Fuellung wechseln mit. Die Abschnitte darueber behalten ihre Werte und verlieren nur ihre Knoepfe. - Abstand unter der Trennlinie von 28 auf 16 px: das padding-top verhindert das Kollabieren des margin der Werteliste, also zaehlt jetzt nur noch das padding. - Modell ist in der App ein Auswahlfeld statt eines Textfelds, wie im Panel. Die Liste liegt als Zweitschrift in daten/fahrzeugmodelle.ts; fahrzeugmodelle.test.ts liest die Panel-Quelle ein und vergleicht sie Eintrag fuer Eintrag, damit die beiden nicht auseinanderlaufen. - Faehrt der Wagen, sagen Uebersicht und Sicherheitsseite das, statt "sicher abgestellt" zu behaupten. Die Zuendung meldet der Dongle in Sekunden, Tuer- und Schlossmeldungen kommen verzoegert - die schnellere Quelle hat Vorrang, weil sonst das Wort nicht stimmt. - tankerkennung: der Tiefststand wird vor dem Anlegen gesetzt. Ein gepufferter Schwung Datensaetze legte sonst denselben Tankvorgang zweimal an, weil der zweite Zustandswechsel waehrend des Wartens noch den alten Tiefststand las (04.09.2026, 29,8 l und 29,7 l). - flespi: nach dem Leeren des Zwischenspeichers wird nur abgeglichen, wenn ein Geraetewert vorliegt. Sonst ueberschrieb der Waechterfehler den frisch gelesenen Stand in der Kachel. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
# AGENTS.md — Project state, review findings, open items, and working rules
|
||||
|
||||
**Last updated: 2026-09-04** (Tankstelle zweizeilig und antippbar, die Belegkarte sagt bei
|
||||
**Last updated: 2026-09-05** (Tankstelle zweizeilig und antippbar, die Belegkarte sagt bei
|
||||
fehlender Position die Wahrheit statt einen toten Knopf zu zeigen, der Regler bekommt einen
|
||||
Speichern-Knopf, und die Share-Erweiterung trug eine andere Versionsnummer als die App;
|
||||
dazu die Kopfmarke statt der Firmierung des Betreibers und ein
|
||||
@@ -7633,6 +7633,143 @@ verwerfen - was sie fangen soll, liegt Jahrzehnte daneben.
|
||||
An acht Faellen geprueft: Epoche, beide ID-Verwechslungen und 366 Tage
|
||||
verworfen; jetzt, 2 Tage und 364 Tage angenommen.
|
||||
|
||||
## DA. Ein Knopf statt drei, abgestellt schließt fahrend aus, und zwei Tankvorgänge aus einem Datensatz (2026.9.5.35)
|
||||
|
||||
### Die Update-Kachel hat genau einen Knopf
|
||||
|
||||
Rückfrage des Eigentümers: *„update installieren ist ein separater button unter
|
||||
integration. warum nicht den update suchen button dafür verwenden und nur die
|
||||
optik und schrift zur installation anpassen?"* — berechtigt: über dem Suchknopf
|
||||
stand je nach Zustand ein zweiter, gleich aussehender Knopf.
|
||||
|
||||
Der eine Knopf am Kachelende trägt jetzt in jedem Zustand den **nächsten
|
||||
fälligen Schritt**, Beschriftung und Füllung wechseln mit:
|
||||
|
||||
| Zustand | Knopf |
|
||||
|---|---|
|
||||
| läuft gerade | Kreisel im Knopf, „Installiere …" / „Home Assistant startet neu …" / „Prüfe …" |
|
||||
| installiert, Neustart nötig | „Installation abschließen und neu starten" (gefüllt) |
|
||||
| installiert, nur Oberfläche (Panel) | „Seite neu laden" (gefüllt) |
|
||||
| Update verfügbar | „Update installieren" (gefüllt) |
|
||||
| sonst | „Auf Update prüfen" |
|
||||
|
||||
Die Reihenfolge ist eine **Rangfolge, keine Auswahl**: liegt ein Neustart an,
|
||||
ist das der nächste Schritt, egal was sonst bereitliegt. Ein gleichzeitiges
|
||||
Integrations- UND App-Update gibt es dabei praktisch nicht — `buendelPasst()`
|
||||
bietet ein Bündel erst an, wenn es zur laufenden Serverfassung passt, also nach
|
||||
der Integration.
|
||||
|
||||
Die Abschnitte darüber behalten ihre Werte- und Meldungszeilen und verlieren nur
|
||||
ihre Knöpfe. Der Abstand unter der Trennlinie war 28 px (16 px `padding` plus
|
||||
12 px `margin` der Werteliste, die wegen des `padding` **nicht** kollabieren) —
|
||||
`.dm-kachelabschnitt > :first-child { margin-top: 0 }` lässt nur noch das
|
||||
`padding` zählen.
|
||||
|
||||
### Modell ist auch in der App ein Auswahlfeld
|
||||
|
||||
Vom Eigentümer bemerkt: *„einrichten/modell ist in der app kein dropdown"*. Das
|
||||
Panel hatte seit jeher ein `<select>` aus `FAHRZEUG_MODELLE`, die App ein freies
|
||||
Textfeld — ein frei getippter Titel führt zu keinem oder zum falschen
|
||||
Typenschild, denn das Bild wird über den Modellnamen gesucht.
|
||||
|
||||
Die Liste steht jetzt in `companion-app/src/daten/fahrzeugmodelle.ts` als
|
||||
Zweitschrift. **Zwei Codebasen, eine Liste** — dass sie gleich bleiben, sichert
|
||||
`fahrzeugmodelle.test.ts`, der die Panel-Quelle einliest und Eintrag für Eintrag
|
||||
vergleicht; ein Auseinanderlaufen fällt damit im Testlauf auf, nicht erst beim
|
||||
Nutzer. Übernommen ist auch das Verhalten des Panels, einen gespeicherten, nicht
|
||||
mehr gelisteten Titel voranzustellen, statt die Auswahl still auf den ersten
|
||||
Eintrag zurückfallen zu lassen.
|
||||
|
||||
### Abgestellt und unterwegs schließen einander aus
|
||||
|
||||
Ansage des Eigentümers: *„es kann nicht sein dass das fahrzeug sicher abgestellt
|
||||
ist und zeitgleich fährt"* und *„genauso parken und fahren geht nicht
|
||||
gleichzeitig"*.
|
||||
|
||||
Die beiden Aussagen kommen aus verschiedenen Quellen: Tür-, Fenster- und
|
||||
Schlossmeldungen liefert der EU Data Act mit spürbarer Verzögerung (vom
|
||||
Eigentümer ausdrücklich hingenommen), die Zündung meldet der Dongle in Sekunden.
|
||||
Die schnellere Quelle hat Vorrang — **nicht weil die andere falsch wäre**
|
||||
(verriegelt ist der Wagen während der Fahrt tatsächlich), sondern weil das WORT
|
||||
„abgestellt" dann nicht stimmt.
|
||||
|
||||
Beide Oberflächen fragen dafür `standortZustand().faehrt`, also dieselbe
|
||||
Funktion, die das Standortmenü schon benutzt — und übernehmen deren Wortlaut
|
||||
„Zündung an" statt „fährt": einen Bewegungssensor gibt es nicht, die Zündung
|
||||
steht auch im Stand mit laufendem Motor auf an. Auf der Sicherheitsseite lautet
|
||||
die Sammelzeile „Zündung an - Fahrzeug in Betrieb", mit dem Punkt des
|
||||
Standortmenüs statt Haken, Kreuz oder Fragezeichen; die vier Gruppenzeilen
|
||||
darunter zeigen unverändert, was die Sensoren melden.
|
||||
|
||||
Die Parkanzeige war bereits richtig — `standortZustand()` unterdrückt „Geparkt
|
||||
seit" bei laufender Zündung seit jeher.
|
||||
|
||||
### Zwei Tankvorgänge aus einem gepufferten Schwung
|
||||
|
||||
Frage des Eigentümers zu zwei Einträgen vom 04.09.2026, 21:36:17, über 29,8 l
|
||||
und 29,7 l. In der Aufzeichnung nachgesehen: `can_fuel_volume` stand den ganzen
|
||||
Tag auf **7**, dann lieferte der Dongle um 21:36:50 einen gepufferten Schwung
|
||||
(`message_timestamp` in Zehnsekundenschritten über gut fünf Stunden) und meldete
|
||||
in derselben Sekunde **36,8** und **36,7**.
|
||||
|
||||
Kein echtes Tanken, sondern der Sprung vom Wert des Prüfstands auf den ersten
|
||||
echten CAN-Wert. Dass daraus **zwei** Einträge wurden, ist ein Wettlauf:
|
||||
`_automatisch_anlegen()` wartet auf Gerätezeit und Ablage, in dieser Zeit kommt
|
||||
der nächste Zustandswechsel dran und liest oben noch den ALTEN Tiefststand. Der
|
||||
Tiefststand wird jetzt **vor** dem Anlegen gesetzt (`tiefststand_liter_setzen()`
|
||||
belegt das Feld als erste Anweisung, also bevor es selbst wartet), in beiden
|
||||
Zweigen. Dieselbe Klasse Fehler wie der Signalwechsel-Wettlauf in Abschnitt BU.
|
||||
|
||||
**Die beiden Einträge bleiben stehen** — das sind die Daten des Eigentümers.
|
||||
|
||||
### Der Dongle steht auf Ultra Deep Sleep
|
||||
|
||||
`config_26.cfg` gegen `config_25.cfg` (beide gzip-gepackt, `gunzip` liest sie),
|
||||
feldweise verglichen: **genau zwei Unterschiede**.
|
||||
|
||||
| Parameter | 25 | 26 | Bedeutung |
|
||||
|---|---|---|---|
|
||||
| `102` | 2 | **4** | Sleep Mode: Deep Sleep zu Ultra Deep Sleep |
|
||||
| `19002` | 60 | **120** | Movement Stop Delay |
|
||||
|
||||
Die 120 s sind die richtige Konsequenz: in UDS weckt die Zündung nicht mehr,
|
||||
also muss das Aufwachen über den Beschleunigungssensor lange genug tragen, bis
|
||||
der Motor läuft und RPM kommt.
|
||||
|
||||
**Noch nicht bestätigt:** die Dongle-Kachel liest über flespi weiterhin
|
||||
`sleep_mode.mode.type = 2`, bestätigt vom Gerät am 05.09.2026 um 14:44:40 UTC,
|
||||
bei `timeout = 15` (passend zu `103`). Ob die USB-Übertragung vor oder nach
|
||||
diesem Zeitpunkt lag, ist von hier aus nicht feststellbar und beim Eigentümer zu
|
||||
klären.
|
||||
|
||||
**Zu `107` (Records saving/sending without TS): keine Änderung nötig.** Der
|
||||
Eigentümer sieht im Configurator „After Time Sync"; die Zeitquelle ist wegen
|
||||
`902:pool.ntp.org`/`903:time.nist.gov` das Mobilfunknetz, nicht GPS — ein
|
||||
fehlender Fix hält deshalb keine Pakete zurück. `Always` wäre ein Rückschritt:
|
||||
seit Abschnitt AY kommen Fahrtbeginn und -ende aus der Geräteuhr, und Datensätze
|
||||
mit ungestellter Uhr würden die Fahrtgrenzen verschieben.
|
||||
|
||||
**Merkposten aus derselben Frage:** ich hatte `107:2` aus der Reihenfolge dreier
|
||||
Beschriftungen in einem Binärabzug des Configurators als „Always" gelesen — die
|
||||
Reihenfolge dort ist alphabetisch, keine Enumeration. Eine Reihenfolge in einem
|
||||
Abzug, einem Verzeichnis oder einer Ausgabe ist keine Semantik.
|
||||
|
||||
### Verifiziert
|
||||
|
||||
Backend-Suite (aktualisierung, verlauf, fahrterkennung, belegparser) vollständig
|
||||
grün, App **303 Tests**, Panel als Modul geparst, `audi_ha_test` auf
|
||||
`2026.9.5.35` mit **0 Tracebacks**. Live im Panel: die Update-Kachel trägt genau
|
||||
einen Knopf („Auf Update prüfen"), Zündung auf „an" gesetzt zeigt die Übersicht
|
||||
„Zündung an" statt „Sicher abgestellt" und die Sicherheitsseite „Zündung an -
|
||||
Fahrzeug in Betrieb" mit Punkt statt Kreis; danach zurückgesetzt. „Jetzt lesen"
|
||||
in der Dongle-Kachel liefert wieder die Werte statt der Wächtermeldung — damit
|
||||
ist auch die Reihenfolge-Korrektur in `flespi.py` (Cache leeren, erst danach
|
||||
abgleichen, und nur bei bekanntem Wert) live belegt.
|
||||
|
||||
**Nicht live geprüft:** dieselben Änderungen in der App. Sie brauchen einen
|
||||
gespeicherten Zugang; belegt sind sie über `tsc`, die 303 Tests und den
|
||||
Listenvergleich gegen die Panel-Quelle.
|
||||
|
||||
## Working conventions (observed — keep them)
|
||||
|
||||
- German is the project language: identifiers, comments, commits, UI texts. Exceptions:
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
/**
|
||||
* Die Modellliste der App muss der des Panels entsprechen.
|
||||
*
|
||||
* Bis zum 05.09.2026 war Modell in der App ein freies Textfeld, im Panel ein
|
||||
* Auswahlfeld - vom Eigentümer bemerkt. Beim Nachziehen entstand eine
|
||||
* Zweitschrift der Liste; dieser Test schneidet die Panel-Fassung aus der
|
||||
* Quelle heraus und vergleicht sie, damit die beiden nicht auseinanderlaufen.
|
||||
*/
|
||||
|
||||
import { readFileSync } from "node:fs"
|
||||
|
||||
import { describe, expect, it } from "vitest"
|
||||
|
||||
import { FAHRZEUG_MODELLE, modellauswahl } from "./fahrzeugmodelle"
|
||||
|
||||
function panelListe(): string[] {
|
||||
const quelle = readFileSync(
|
||||
"../custom_components/audi_dashboard/frontend/audi-dashboard-app.js",
|
||||
"utf8",
|
||||
)
|
||||
const start = quelle.indexOf("const FAHRZEUG_MODELLE = [")
|
||||
if (start < 0) throw new Error("FAHRZEUG_MODELLE nicht in der Panel-Quelle gefunden")
|
||||
const ende = quelle.indexOf("];", start)
|
||||
const roh = quelle.slice(start, ende)
|
||||
return [...roh.matchAll(/"([^"]+)"/g)].map((m) => m[1] as string)
|
||||
}
|
||||
|
||||
describe("Modellliste", () => {
|
||||
it("stimmt mit der des Panels überein", () => {
|
||||
expect([...FAHRZEUG_MODELLE]).toEqual(panelListe())
|
||||
})
|
||||
|
||||
it("stellt einen unbekannten gespeicherten Titel voran", () => {
|
||||
const auswahl = modellauswahl("RS 6 Avant performance")
|
||||
expect(auswahl[0]).toBe("RS 6 Avant performance")
|
||||
expect(auswahl).toHaveLength(FAHRZEUG_MODELLE.length + 1)
|
||||
})
|
||||
|
||||
it("laesst die Liste unveraendert, wenn der Titel darin steht", () => {
|
||||
expect(modellauswahl("RS 4 Avant")).toEqual(FAHRZEUG_MODELLE)
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,38 @@
|
||||
/**
|
||||
* Die Modelle, zu denen ein echtes Badge vorliegt.
|
||||
*
|
||||
* Zweitschrift von `FAHRZEUG_MODELLE` im Panel
|
||||
* (custom_components/audi_dashboard/frontend/audi-dashboard-app.js). Zwei
|
||||
* Codebasen, eine Liste - dass sie gleich bleiben, sichert
|
||||
* `fahrzeugmodelle.test.ts`, der die Panel-Quelle einliest und Eintrag für
|
||||
* Eintrag vergleicht.
|
||||
*
|
||||
* Beschränkt auf Modelle mit Badge: für alles andere stünde ein falsches oder
|
||||
* gar kein Badge auf der Übersicht. Die Ausführung („competition",
|
||||
* „performance") gehört nicht hierher, sondern in das Feld „Ausführung" -
|
||||
* Entscheidung des Eigentümers vom 05.09.2026.
|
||||
*/
|
||||
export const FAHRZEUG_MODELLE = [
|
||||
"RS 3 Sportback",
|
||||
"RS 3 Limousine",
|
||||
"RS 4 Avant",
|
||||
"RS 5 Coupé",
|
||||
"RS 5 Sportback",
|
||||
"RS 6 Avant",
|
||||
"RS 7 Sportback",
|
||||
"S3 Sportback",
|
||||
"S3 Limousine",
|
||||
"SQ7",
|
||||
] as const
|
||||
|
||||
/** Die Liste, wie sie im Auswahlfeld stehen muss.
|
||||
*
|
||||
* Ein gespeicherter Titel, der nicht (mehr) in der Liste steht, wird
|
||||
* vorangestellt statt verschluckt - sonst fiele die Auswahl still auf den
|
||||
* ersten Eintrag zurück und änderte das Fahrzeug des Nutzers. Wortgleich mit
|
||||
* dem Panel. */
|
||||
export function modellauswahl(titel: string): readonly string[] {
|
||||
return FAHRZEUG_MODELLE.includes(titel as (typeof FAHRZEUG_MODELLE)[number])
|
||||
? FAHRZEUG_MODELLE
|
||||
: [titel, ...FAHRZEUG_MODELLE]
|
||||
}
|
||||
@@ -13,6 +13,7 @@ import type { LetzterDatensatz, Profil } from "../api"
|
||||
import { eigeneVersion, versionOrdnung } from "../daten/appVersion"
|
||||
import { Datumsfeld } from "./Datumsfeld"
|
||||
import { useDaten } from "../daten/DatenKontext"
|
||||
import { modellauswahl } from "../daten/fahrzeugmodelle"
|
||||
import { OtaFehler, buendelAnwenden, buendelPasst, otaMoeglich } from "../daten/ota"
|
||||
import type { Einstellungen as EinstellungenWerte } from "../daten/profilAdapter"
|
||||
import { datum, datumZeit, de, isoTag, standAlter } from "../format"
|
||||
@@ -403,13 +404,28 @@ export function Einstellungen({
|
||||
<span className="ads-eyebrow">Fahrzeug einrichten</span>
|
||||
{einrichtenOffen && (
|
||||
<>
|
||||
{/* Auswahlfeld, nicht Freitext - wie im Panel (<select id="einModell">).
|
||||
Bis zum 05.09.2026 stand hier ein einfaches Eingabefeld, vom
|
||||
Eigentümer bemerkt: „einrichten/modell ist in der app kein
|
||||
dropdown". Ein frei getippter Titel führt zu keinem oder zum
|
||||
falschen Typenschild, denn das Bild wird über den Modellnamen
|
||||
gesucht. Die Liste selbst steht in daten/fahrzeugmodelle.ts und
|
||||
wird von einem Test gegen die Panel-Fassung geprüft. */}
|
||||
<Feld label="Modell">
|
||||
<input
|
||||
<select
|
||||
className="dm-eingabe dm-eingabe--fahrzeug"
|
||||
value={werte.fahrzeugtitel}
|
||||
onChange={(e) => aendern({ fahrzeugtitel: e.target.value })}
|
||||
onBlur={() => sichern()}
|
||||
/>
|
||||
onChange={(e) => {
|
||||
aendern({ fahrzeugtitel: e.target.value })
|
||||
sichern({ fahrzeugtitel: e.target.value })
|
||||
}}
|
||||
>
|
||||
{modellauswahl(werte.fahrzeugtitel).map((m) => (
|
||||
<option key={m} value={m}>
|
||||
{m}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
</Feld>
|
||||
<Feld label="Kennzeichen">
|
||||
<input
|
||||
@@ -782,20 +798,10 @@ export function Einstellungen({
|
||||
zum nächsten nötigen Schritt (Neustart) statt den Nutzer selbst
|
||||
suchen zu lassen - ein reiner Reload reicht nicht (AGENTS.md
|
||||
Abschnitt J). */}
|
||||
{updateInstallierenLaeuft ? (
|
||||
<div className="dm-ladezeile">
|
||||
<span className="dm-spinner" aria-hidden="true" />
|
||||
<span className="dm-erklaerung" style={{ marginTop: 0 }}>
|
||||
Installiere …
|
||||
</span>
|
||||
</div>
|
||||
) : updateNeustartLaeuft ? (
|
||||
<div className="dm-ladezeile">
|
||||
<span className="dm-spinner" aria-hidden="true" />
|
||||
<span className="dm-erklaerung" style={{ marginTop: 0 }}>
|
||||
Home Assistant startet neu …
|
||||
</span>
|
||||
</div>
|
||||
{updateInstallierenLaeuft || updateNeustartLaeuft ? (
|
||||
/* Kein eigener Text mehr - was gerade läuft, steht im Knopf ganz
|
||||
unten, der dabei selbst den Fortschritt zeigt. */
|
||||
<></>
|
||||
) : integrationUpdate?.installiert && integrationUpdate.neustart_noetig === false ? (
|
||||
/* Gar keine Meldung. Geändert wurden Dateien unter frontend/, und das
|
||||
sind die des PANELS - die App lädt ihren eigenen Code aus
|
||||
@@ -813,11 +819,6 @@ export function Einstellungen({
|
||||
Version {integrationUpdate.version} installiert - die neue Fassung wird erst nach
|
||||
einem Neustart geladen.
|
||||
</p>
|
||||
<div className="dm-knopfreihe">
|
||||
<ActionButton onClick={() => void updateNeustartAusloesen()}>
|
||||
Installation abschließen und neu starten
|
||||
</ActionButton>
|
||||
</div>
|
||||
</>
|
||||
) : integrationUpdate?.verfuegbar ? (
|
||||
<>
|
||||
@@ -840,11 +841,6 @@ export function Einstellungen({
|
||||
/>
|
||||
}
|
||||
/>
|
||||
<div className="dm-knopfreihe">
|
||||
<ActionButton variant="primary" onClick={() => void updateInstallierenAusloesen()}>
|
||||
Update installieren
|
||||
</ActionButton>
|
||||
</div>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
@@ -877,11 +873,6 @@ export function Einstellungen({
|
||||
Lädt die Oberfläche neu und startet die App neu. Fahrzeugprofil, Fahrten und
|
||||
Tankvorgänge sind davon nicht betroffen — die liegen in Home Assistant.
|
||||
</p>
|
||||
<div className="dm-knopfreihe">
|
||||
<ActionButton onClick={() => void otaAusloesen()} disabled={otaLaeuft}>
|
||||
{otaLaeuft ? "Lade Update …" : "Update installieren"}
|
||||
</ActionButton>
|
||||
</div>
|
||||
{otaFehler && <p className="dm-fehler">{otaFehler}</p>}
|
||||
</>
|
||||
) : otaBuendel &&
|
||||
@@ -912,18 +903,59 @@ export function Einstellungen({
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Ein Suchknopf für die ganze Kachel, ganz unten: er steht damit unter
|
||||
beiden Abschnitten und legt nahe, was er tut - nach Neuem für
|
||||
Integration UND App sehen (Vorgabe des Eigentümers, 05.09.2026).
|
||||
{/* EIN Knopf für die ganze Kachel, ganz unten: er steht unter beiden
|
||||
Abschnitten und trägt in jedem Zustand den nächsten fälligen
|
||||
Schritt - suchen, installieren, Neustart. Bis zum 05.09.2026 stand
|
||||
darüber noch ein zweiter Knopf im jeweiligen Abschnitt; der
|
||||
Eigentümer: „warum nicht den update suchen button dafür verwenden
|
||||
und nur die optik und schrift zur installation anpassen?" - genau
|
||||
das passiert hier, Beschriftung und Füllung wechseln mit dem
|
||||
Zustand. Wortgleich mit dem Panel (updateKnopf()).
|
||||
|
||||
Er ist in JEDEM Zustand da. Vorher sass er nur im Zweig "ist
|
||||
aktuell", und daneben brauchte es ein zweites "Erneut prüfen", damit
|
||||
man eine womöglich wochenalte Zahl überhaupt auffrischen konnte,
|
||||
solange ein Update angeboten wurde. Das erübrigt sich hier. */}
|
||||
Die Reihenfolge ist eine Rangfolge, keine Auswahl: liegt ein
|
||||
Neustart an, ist das der nächste Schritt, egal was sonst noch
|
||||
bereitliegt. Integrations- UND App-Update gleichzeitig gibt es
|
||||
dabei praktisch nicht - das Bündel wird erst angeboten, wenn es zur
|
||||
laufenden Serverfassung passt (buendelPasst), also nach der
|
||||
Integration. */}
|
||||
<div className="dm-knopfreihe" style={{ marginTop: "var(--sp-4)" }}>
|
||||
<ActionButton onClick={() => void updatePruefenAusloesen()} disabled={updatePruefenLaeuft}>
|
||||
{updatePruefenLaeuft ? "Prüfe …" : "Auf Update prüfen"}
|
||||
</ActionButton>
|
||||
{(() => {
|
||||
const laufend = (text: string) => (
|
||||
<ActionButton disabled>
|
||||
<span className="dm-knopflader">
|
||||
<span className="dm-spinner" aria-hidden="true" />
|
||||
{text}
|
||||
</span>
|
||||
</ActionButton>
|
||||
)
|
||||
if (updateInstallierenLaeuft) return laufend("Installiere …")
|
||||
if (updateNeustartLaeuft) return laufend("Home Assistant startet neu …")
|
||||
if (updatePruefenLaeuft) return laufend("Prüfe …")
|
||||
if (otaLaeuft) return laufend("Lade Update …")
|
||||
if (integrationUpdate?.installiert && integrationUpdate.neustart_noetig !== false)
|
||||
return (
|
||||
<ActionButton variant="primary" onClick={() => void updateNeustartAusloesen()}>
|
||||
Installation abschließen und neu starten
|
||||
</ActionButton>
|
||||
)
|
||||
if (integrationUpdate?.verfuegbar)
|
||||
return (
|
||||
<ActionButton variant="primary" onClick={() => void updateInstallierenAusloesen()}>
|
||||
Update installieren
|
||||
</ActionButton>
|
||||
)
|
||||
if (otaUpdateVerfuegbar && otaBuendel)
|
||||
return (
|
||||
<ActionButton variant="primary" onClick={() => void otaAusloesen()}>
|
||||
Update installieren
|
||||
</ActionButton>
|
||||
)
|
||||
return (
|
||||
<ActionButton onClick={() => void updatePruefenAusloesen()}>
|
||||
Auf Update prüfen
|
||||
</ActionButton>
|
||||
)
|
||||
})()}
|
||||
</div>
|
||||
{/* Der Zeitpunkt gehört zum Suchvorgang, nicht zu einem der beiden
|
||||
Abschnitte - er stand bis zum 05.09.2026 an der grünen Marke der
|
||||
|
||||
@@ -17,6 +17,7 @@ import { useDaten } from "../daten/DatenKontext"
|
||||
import type { SeitenName } from "../navigation"
|
||||
import { gruppenErgebnis, Leerzustand, StatusKreis } from "./bausteine"
|
||||
import { Bild } from "./Bild"
|
||||
import { standortZustand } from "./Standort"
|
||||
|
||||
export function Fahrzeugstatus({ geheZu }: { geheZu: (name: SeitenName) => void }) {
|
||||
const { fahrzeug } = useDaten()
|
||||
@@ -37,8 +38,15 @@ export function Fahrzeugstatus({ geheZu }: { geheZu: (name: SeitenName) => void
|
||||
const fensterDach = gruppenErgebnis(punkte.filter((p) => p.gruppe === "fenster_dach"))
|
||||
const licht = gruppenErgebnis(punkte.filter((p) => p.gruppe === "licht"))
|
||||
const gesichert = fahrzeug.gesichert
|
||||
const sammelText =
|
||||
gesichert === null
|
||||
/* Dieselbe Regel wie auf der Übersicht: unterwegs ist der Wagen nicht
|
||||
abgestellt (Vorgabe des Eigentümers, 05.09.2026). Die vier Sammelzeilen
|
||||
darunter bleiben stehen und zeigen weiter, was die Tür-, Fenster- und
|
||||
Schlosssensoren melden — nur die Überschrift darf nicht „abgestellt"
|
||||
behaupten, während die Zündung an ist. */
|
||||
const faehrt = standortZustand(fahrzeug).faehrt
|
||||
const sammelText = faehrt
|
||||
? "Zündung an - Fahrzeug in Betrieb"
|
||||
: gesichert === null
|
||||
? "Zustand nicht vollständig bekannt"
|
||||
: gesichert
|
||||
? "Fahrzeug ist sicher abgestellt"
|
||||
@@ -57,7 +65,10 @@ export function Fahrzeugstatus({ geheZu }: { geheZu: (name: SeitenName) => void
|
||||
Kreis-Häkchen-Symbol wie die vier Zeilen unten (nicht der generische
|
||||
StatusRow-Punkt), keine Zeitangabe - das Panel zeigt dort keine. */}
|
||||
<Tile className="dm-sicherheitssammel">
|
||||
<StatusKreis ok={gesichert} />
|
||||
{/* 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} />}
|
||||
<span>{sammelText}</span>
|
||||
</Tile>
|
||||
|
||||
|
||||
@@ -124,7 +124,7 @@ const PARKPLATZ_AB_MIN = 15
|
||||
|
||||
Kein Rueckfall auf die Fahrtenliste: null heisst unbekannt, und das wird
|
||||
auch so angezeigt. Eine falsche Zahl ist schlechter als keine. */
|
||||
function standortZustand(
|
||||
export function standortZustand(
|
||||
fahrzeug: Fahrzeug,
|
||||
): { faehrt: boolean; unbekannt: boolean; seitTs: number | null; geparkt: boolean } {
|
||||
const roh = fahrzeug.geparktSeit ? new Date(fahrzeug.geparktSeit).getTime() : NaN
|
||||
|
||||
@@ -21,7 +21,7 @@ import { Blattzeile, Leerzustand } from "./bausteine"
|
||||
import { strecke } from "./Fahrten"
|
||||
import { Bild } from "./Bild"
|
||||
import type { ServicebuchEintrag } from "./Service"
|
||||
import { StandortVorschau } from "./Standort"
|
||||
import { StandortVorschau, standortZustand } from "./Standort"
|
||||
import { SymbolInspektion, SymbolOelwechsel } from "../symbole"
|
||||
import { Typenschild, schildErkennen } from "./Typenschild"
|
||||
|
||||
@@ -87,12 +87,32 @@ export function Uebersicht({ geheZu }: { geheZu: (name: SeitenName, id?: string)
|
||||
.slice()
|
||||
.sort((a, b) => new Date(b.ts).getTime() - new Date(a.ts).getTime())[0]
|
||||
|
||||
const sicherheitsstatus =
|
||||
fahrzeug.gesichert === true ? "ok" : fahrzeug.gesichert === false ? "bad" : "warn"
|
||||
// Wortlaut deckungsgleich mit vHome() im Panel (g === null ? "Status unbekannt" :
|
||||
// g ? "Sicher abgestellt" : "Nicht sicher abgestellt").
|
||||
const sicherheitstext =
|
||||
fahrzeug.gesichert === true
|
||||
/* Fährt der Wagen, kann er nicht gleichzeitig „sicher abgestellt" sein —
|
||||
abgestellt und unterwegs schließen einander aus (Vorgabe des Eigentümers,
|
||||
05.09.2026: „es kann nicht sein dass das fahrzeug sicher abgestellt ist und
|
||||
zeitgleich fährt").
|
||||
|
||||
Die beiden Aussagen kommen aus verschiedenen Quellen: Tür-, Fenster- und
|
||||
Schlossmeldungen liefert der EU Data Act mit spürbarer Verzögerung, die
|
||||
Zündung meldet der Dongle in Sekunden. Die schnellere Quelle hat hier
|
||||
Vorrang — nicht weil die andere falsch wäre (verriegelt ist der Wagen
|
||||
während der Fahrt tatsächlich), sondern weil das WORT „abgestellt" dann
|
||||
nicht stimmt.
|
||||
|
||||
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. */
|
||||
const faehrt = standortZustand(fahrzeug).faehrt
|
||||
const sicherheitsstatus = faehrt
|
||||
? "ok"
|
||||
: fahrzeug.gesichert === true
|
||||
? "ok"
|
||||
: fahrzeug.gesichert === false
|
||||
? "bad"
|
||||
: "warn"
|
||||
const sicherheitstext = faehrt
|
||||
? "Zündung an"
|
||||
: fahrzeug.gesichert === true
|
||||
? "Sicher abgestellt"
|
||||
: fahrzeug.gesichert === false
|
||||
? "Nicht sicher abgestellt"
|
||||
|
||||
@@ -1700,6 +1700,16 @@ button.dm-listenzeile {
|
||||
|
||||
.dm-kachelabschnitt + .dm-kachelabschnitt {
|
||||
margin-top: var(--sp-4);
|
||||
padding-top: var(--sp-4);
|
||||
border-top: 1px solid var(--line);
|
||||
}
|
||||
|
||||
/* Die Ueberschrift eines Abschnitts ist kein direktes Kind der Kachel, die
|
||||
Regel .ads-tile > .ads-eyebrow:first-child greift hier also nicht. */
|
||||
/* Der Strich zwischen zwei Abschnitten und die erste Zeile darunter standen
|
||||
28px auseinander: 16px padding-top der Regel oben PLUS die 12px margin-top
|
||||
der Werteliste - die kollabieren wegen des padding nicht miteinander. Vom
|
||||
Eigentümer bemerkt (05.09.2026: „abstand zwischen app und version und dem
|
||||
darüberliegendem strich ist zu groß"). Jetzt zählt nur noch das padding,
|
||||
in beiden Abschnitten gleich. */
|
||||
.dm-kachelabschnitt > :first-child {
|
||||
@@ -2363,3 +2373,13 @@ select.dm-auswahl {
|
||||
.ads-seg button {
|
||||
min-height: 44px;
|
||||
}
|
||||
|
||||
/* Der Schieberegler traegt seinen Daumen selbst; die Bahn ist duenn, das
|
||||
Element darum darf trotzdem 44px hoch treffen. */
|
||||
input[type="range"] {
|
||||
min-height: 44px;
|
||||
}
|
||||
|
||||
/* 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
|
||||
|
||||
@@ -678,4 +678,30 @@ async def jetzt_lesen(k: Koordinator) -> None:
|
||||
await k.flespi_stand_setzen(stand)
|
||||
# Ein frischer Stand kann eine Abweichung erst sichtbar machen - dann gehört
|
||||
# sie gleich behoben, statt bis zum nächsten Speichern zu warten.
|
||||
await wunsch_uebernehmen(k)
|
||||
#
|
||||
# ABER nur, wenn nach dem Leeren überhaupt schon ein Gerätewert
|
||||
# zurückgekommen ist. Schläft der Dongle, ist der Wert nicht abweichend,
|
||||
# sondern schlicht unbekannt - ein Schreibversuch scheitert dann am Schutz
|
||||
# in schlaf_timeout_schreiben() und ersetzte den eben gelesenen Stand durch
|
||||
# eine Fehlermeldung. Am 05.09.2026 genau so passiert: nach "Jetzt lesen"
|
||||
# stand in der Kachel nur noch "Der Schlafmodus des Geräts ist gerade
|
||||
# unbekannt", statt der Werte.
|
||||
if _schlafwert_bekannt(stand):
|
||||
await wunsch_uebernehmen(k)
|
||||
else:
|
||||
_LOGGER.debug(
|
||||
"Nach dem Leeren liegt noch kein Gerätewert vor - kein Abgleich,"
|
||||
" bis der Dongle sich meldet."
|
||||
)
|
||||
|
||||
|
||||
def _schlafwert_bekannt(stand: dict) -> bool:
|
||||
"""Ob der Anzeigestand einen Schlaf-Timeout vom Gerät kennt.
|
||||
|
||||
Ein ausstehender Wert (`offen`) zählt mit: er ist unterwegs, und das
|
||||
dazugehörige `mode`-Objekt liegt dann in flespi vor."""
|
||||
for zeile in stand.get("werte", []):
|
||||
if zeile.get("schluessel") != SCHLAF_SCHLUESSEL:
|
||||
continue
|
||||
return zeile.get("geraet") is not None or zeile.get("offen") is not None
|
||||
return False
|
||||
|
||||
@@ -1 +1 @@
|
||||
{"version":"2026.9.5.34","sha256":"0de4c2af3534ef74c4bda98de122419e2f4d4c2741a175e73722d757344a3f6b","bytes":324629,"gebaut":"2026-09-05T14:40:28Z"}
|
||||
{"version":"2026.9.5.35","sha256":"8873fddf9a976ff3317fb16cf4ed708ac19ef76dbf8faf4ff29189e2a5f83a75","bytes":324134,"gebaut":"2026-09-05T15:01:50Z"}
|
||||
Binary file not shown.
@@ -2300,8 +2300,26 @@ function standAlterTicken() {
|
||||
|
||||
function vHome() {
|
||||
const g = CAR.gesichert;
|
||||
const statusText = g === null ? "Status unbekannt" : g ? "Sicher abgestellt" : "Nicht sicher abgestellt";
|
||||
const statusDot = g === null ? "warn" : g ? "" : "bad";
|
||||
/* Faehrt der Wagen, kann er nicht gleichzeitig "sicher abgestellt" sein -
|
||||
abgestellt und unterwegs schliessen einander aus (Vorgabe des
|
||||
Eigentuemers, 05.09.2026: "es kann nicht sein dass das fahrzeug sicher
|
||||
abgestellt ist und zeitgleich faehrt").
|
||||
|
||||
Die beiden Aussagen kommen aus verschiedenen Quellen: Tuer-, Fenster- und
|
||||
Schlossmeldungen liefert der EU Data Act mit spuerbarer Verzoegerung, die
|
||||
Zuendung meldet der Dongle in Sekunden. Die schnellere Quelle hat hier
|
||||
Vorrang - nicht weil die andere falsch waere (verriegelt ist der Wagen
|
||||
waehrend der Fahrt tatsaechlich), sondern weil das WORT "abgestellt" dann
|
||||
nicht stimmt.
|
||||
|
||||
Wortlaut wie im Standortmenue: "Zuendung an", nicht "faehrt" - einen
|
||||
Bewegungssensor gibt es nicht, die Zuendung steht auch im Stand mit
|
||||
laufendem Motor auf an. */
|
||||
const faehrt = standortZustand().faehrt;
|
||||
const statusText = faehrt
|
||||
? "Zündung an"
|
||||
: g === null ? "Status unbekannt" : g ? "Sicher abgestellt" : "Nicht sicher abgestellt";
|
||||
const statusDot = faehrt ? "" : g === null ? "warn" : g ? "" : "bad";
|
||||
const pct = CAR.tankPct;
|
||||
const t = naechsterTermin();
|
||||
/* C2/C5/D1: Reihenfolge der myAudi-App - randloses Fahrzeugbild, Name und
|
||||
@@ -2424,7 +2442,14 @@ function vSicherheit() {
|
||||
const tuerenKlappen = sicherheitsGruppe("tueren_klappen");
|
||||
const fensterDach = sicherheitsGruppe("fenster_dach");
|
||||
const licht = sicherheitsGruppe("licht");
|
||||
const sammelText = g === null ? "Zustand nicht vollständig bekannt" : g ? "Fahrzeug ist sicher abgestellt" : "Bitte prüfen";
|
||||
/* Dieselbe Regel wie in vHome(): unterwegs ist der Wagen nicht abgestellt.
|
||||
Die vier Sammelzeilen darunter bleiben stehen und zeigen weiter, was die
|
||||
Tuer-, Fenster- und Schlosssensoren melden - nur die Ueberschrift darf
|
||||
nicht "abgestellt" behaupten, waehrend die Zuendung an ist. */
|
||||
const faehrt = standortZustand().faehrt;
|
||||
const sammelText = faehrt
|
||||
? "Zündung an - Fahrzeug in Betrieb"
|
||||
: g === null ? "Zustand nicht vollständig bekannt" : g ? "Fahrzeug ist sicher abgestellt" : "Bitte prüfen";
|
||||
const zeile = (label, ok, labelBad, labelWarn, route) => {
|
||||
const text = ok === null ? labelWarn : ok ? label : labelBad;
|
||||
const klickbar = !!route;
|
||||
@@ -2439,7 +2464,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">
|
||||
${statusKreis(g)}
|
||||
${faehrt ? '<span class="dot"></span>' : statusKreis(g)}
|
||||
<span style="font-size:15px;color:var(--fg)">${esc(sammelText)}</span>
|
||||
</div>
|
||||
<div class="tile sicherheitsliste">
|
||||
@@ -3559,6 +3584,36 @@ function dongleStand(w) {
|
||||
return tage >= 1 ? `bestätigt ${wann} (${tage} T. alt)` : `bestätigt ${wann}`;
|
||||
}
|
||||
|
||||
/* Der EINE Knopf der Update-Kachel. Er traegt in jedem Zustand den naechsten
|
||||
faelligen Schritt - suchen, installieren, Neustart, Neuladen - statt dass
|
||||
ueber dem Suchknopf noch ein zweiter Knopf im Abschnitt steht (Vorgabe des
|
||||
Eigentuemers, 05.09.2026: "warum nicht den update suchen button dafuer
|
||||
verwenden und nur die optik und schrift zur installation anpassen?").
|
||||
|
||||
Die Reihenfolge ist eine Rangfolge, keine Auswahl: liegt ein Neustart an,
|
||||
ist das der naechste Schritt, egal was sonst noch bereitliegt. Ein
|
||||
gleichzeitiges Integrations- UND App-Update gibt es dabei praktisch nicht -
|
||||
das Buendel wird erst angeboten, wenn es zur laufenden Serverfassung passt
|
||||
(buendelPasst), also nach der Integration. Das Panel kennt den App-Teil
|
||||
ohnehin nicht: es laedt seine Dateien bei jedem Aufruf frisch vom Server. */
|
||||
function updateKnopf() {
|
||||
const u = INTEGRATION_UPDATE;
|
||||
const laeuft = INTEGRATION_UPDATE_LAEUFT;
|
||||
const laufend = (text) => `<button class="aktion" style="margin-top:14px" disabled>
|
||||
<span style="display:inline-flex;align-items:center;gap:10px">
|
||||
<span class="lade-spinner" aria-hidden="true"></span>${esc(text)}</span></button>`;
|
||||
if (laeuft === "installieren") return laufend("Installiere …");
|
||||
if (laeuft === "neustart") return laufend("Home Assistant startet neu …");
|
||||
if (laeuft === "pruefen") return laufend("Prüfe …");
|
||||
const knopf = (text, aktion, primaer) =>
|
||||
`<button class="aktion${primaer ? " primaer" : ""}" style="margin-top:14px" data-update="${aktion}">${esc(text)}</button>`;
|
||||
if (u && u.installiert && u.neustart_noetig === false && String(CSS_VERSION) !== String(APP_VERSION))
|
||||
return knopf("Seite neu laden", "neuladen", true);
|
||||
if (u && u.installiert && u.neustart_noetig !== false)
|
||||
return knopf("Installation abschließen und neu starten", "neustart", true);
|
||||
if (u && u.verfuegbar) return knopf("Update installieren", "installieren", true);
|
||||
return knopf("Auf Update prüfen", "pruefen", false);
|
||||
}
|
||||
function vEinst() {
|
||||
return `
|
||||
<div class="tile"><span class="label">Fahrzeug einrichten</span>
|
||||
@@ -3761,12 +3816,12 @@ function vEinst() {
|
||||
if (u.neustart_noetig === false) {
|
||||
return `<span style="display:block;margin-top:8px;color:var(--fg2);font-size:13px;line-height:1.6">
|
||||
Version ${esc(u.version)} installiert - nur Oberflächendateien, kein Neustart nötig.</span>
|
||||
<button class="aktion primaer" style="margin-top:14px" data-update="neuladen">Seite neu laden</button>`;
|
||||
`;
|
||||
}
|
||||
return `<span style="display:block;margin-top:8px;color:var(--fg2);font-size:13px;line-height:1.6">
|
||||
Version ${esc(u.version)} installiert - die neue Fassung wird erst nach einem Neustart
|
||||
geladen.</span>
|
||||
<button class="aktion" style="margin-top:14px" data-update="neustart">Installation abschließen und neu starten</button>`;
|
||||
`;
|
||||
}
|
||||
if (u && u.verfuegbar) {
|
||||
/* Zeitstempel und ein zweiter Pruefknopf gehoeren hierher, nicht nur in
|
||||
@@ -3776,19 +3831,22 @@ function vEinst() {
|
||||
sie aufzufrischen, solange ueberhaupt ein Update angeboten wurde. */
|
||||
return `<dl class="rows"><div class="row" style="border-bottom:0"><dt>Verfügbare Version</dt>
|
||||
<dd>${esc(u.version)}${u.geprueft_am ? `<small>geprüft ${dezeitpunkt(new Date(u.geprueft_am))}</small>` : ""}</dd></div></dl>
|
||||
<button class="aktion primaer" style="margin-top:14px" data-update="installieren">Update installieren</button>`;
|
||||
`;
|
||||
}
|
||||
return u ? `<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>`
|
||||
: ""}
|
||||
<!-- Ein Suchknopf fuer die ganze Kachel, ganz unten - in JEDEM Zustand.
|
||||
Vorher sass er nur im Zweig "ist aktuell", und daneben brauchte es ein
|
||||
zweites "Erneut pruefen", damit sich eine womoeglich wochenalte Zahl
|
||||
ueberhaupt auffrischen liess, solange ein Update angeboten wurde. Das
|
||||
eruebrigt sich so. Wortgleich mit der App (Einstellungen.tsx). -->
|
||||
<button class="aktion" style="margin-top:14px" data-update="pruefen"${INTEGRATION_UPDATE_LAEUFT === "pruefen" ? " disabled" : ""}>${INTEGRATION_UPDATE_LAEUFT === "pruefen" ? "Prüfe …" : "Auf Update prüfen"}</button>
|
||||
<!-- EIN Knopf fuer die ganze Kachel, ganz unten - er ist in jedem
|
||||
Zustand da und traegt jedes Mal den naechsten faelligen Schritt:
|
||||
suchen, installieren, Neustart, Neuladen. Bis zum 05.09.2026 stand
|
||||
darueber noch ein zweiter Knopf im jeweiligen Abschnitt; der
|
||||
Eigentuemer: "warum nicht den update suchen button dafuer verwenden
|
||||
und nur die optik und schrift zur installation anpassen?" - genau das
|
||||
passiert hier, Beschriftung und Fuellung wechseln mit dem Zustand.
|
||||
Wortgleich mit der App (Einstellungen.tsx). -->
|
||||
${updateKnopf()}
|
||||
<!-- Der Zeitpunkt gehoert zum Suchvorgang, nicht zu einem Abschnitt -
|
||||
wortgleich mit der App. -->
|
||||
${INTEGRATION_UPDATE && INTEGRATION_UPDATE.geprueft_am
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"domain": "audi_dashboard",
|
||||
"name": "Audi Dashboard",
|
||||
"version": "2026.9.5.34",
|
||||
"version": "2026.9.5.35",
|
||||
"documentation": "https://gitea.nothaft.cloud/paul/audi-app/src/branch/main/README.md",
|
||||
"issue_tracker": "https://gitea.nothaft.cloud/paul/audi-app/issues",
|
||||
"codeowners": [
|
||||
|
||||
@@ -116,11 +116,14 @@ async def fuellstand_geaendert(
|
||||
# (siehe tankvolumen_geaendert) - genauer, weil ohne den Umweg über das im
|
||||
# Fahrzeugprofil hinterlegte Tankvolumen. Beide gleichzeitig auswerten
|
||||
# würde denselben Tankvorgang zweimal anlegen.
|
||||
# Reihenfolge wie bei tankvolumen_geaendert() - erst der Tiefststand, dann
|
||||
# das Anlegen, sonst legen zwei dicht aufeinanderfolgende Wechsel denselben
|
||||
# Tankvorgang zweimal an.
|
||||
await k.tiefststand_setzen(aktuell)
|
||||
if not k.zuordnung.werte.TANK_LITER_SENSOR:
|
||||
await _automatisch_anlegen(
|
||||
k, f"Füllstandsanstieg {round(anstieg, 1)} Prozentpunkte", ereigniszeit=ereigniszeit
|
||||
)
|
||||
await k.tiefststand_setzen(aktuell)
|
||||
|
||||
|
||||
async def tankvolumen_geaendert(
|
||||
@@ -144,13 +147,22 @@ async def tankvolumen_geaendert(
|
||||
if anstieg < LITER_SCHWELLE:
|
||||
return
|
||||
|
||||
# Der Tiefststand wird VOR dem Anlegen gesetzt, nicht danach.
|
||||
# _automatisch_anlegen() wartet auf Geraetezeit und Ablage; in dieser Zeit
|
||||
# kommt der naechste Zustandswechsel dran und liest oben noch den ALTEN
|
||||
# Tiefststand - beide legen denselben Tankvorgang an. Genau so entstanden
|
||||
# am 04.09.2026 um 21:37:04 zwei Eintraege ueber 29,8 l und 29,7 l, als der
|
||||
# Dongle einen gepufferten Schwung Datensaetze auf einmal ablieferte und
|
||||
# can_fuel_volume in derselben Sekunde 36,8 und 36,7 meldete.
|
||||
# tiefststand_liter_setzen() belegt das Feld als erste Anweisung, also
|
||||
# bevor es selbst wartet - danach sieht der zweite Wechsel den neuen Wert.
|
||||
await k.tiefststand_liter_setzen(aktuell)
|
||||
await _automatisch_anlegen(
|
||||
k,
|
||||
f"Tankvolumenanstieg {round(anstieg, 1)} l",
|
||||
liter_schaetzung=anstieg,
|
||||
ereigniszeit=ereigniszeit,
|
||||
)
|
||||
await k.tiefststand_liter_setzen(aktuell)
|
||||
|
||||
|
||||
async def distanz_seit_tankung(
|
||||
|
||||
Reference in New Issue
Block a user