Ölwechsel/Inspektion-Anzeige repariert, Selbst-Update der Integration gebaut
Zwei getrennte Themen in einem Commit, beide in derselben Sitzung entstanden:
1. Ölwechsel/Inspektion wurden komplett ausgeblendet ("kein Eintrag im
Servicebuch"), sobald kein Servicebucheintrag vorlag - selbst wenn der
zugeordnete Sensor eine gültige Fälligkeit meldete. In beiden Frontends
prüfte die Anzeige nur den Servicebuch-Zweig, bevor sie die
Fahrzeugmeldung überhaupt las. Jetzt steht die Fahrzeugmeldung für sich;
fehlt zusätzlich ein Servicebucheintrag, übernimmt eine neue,
fahrtenlog-basierte Prognose (kmProTagAusFahrten()/meldungsPrognose())
die Hochrechnung statt der Servicebuch-Rate - deckelt auf die vom
Fahrzeug selbst gemeldete Zeitgrenze, falls zu wenig gefahren wird.
2. install.ps1 als Update-Weg wird von Windows Smart App Control blockiert,
ohne Umgehungsmöglichkeit. Die Integration lädt sich jetzt auf
Tastendruck selbst von Gitea (aktualisierung.py), verifiziert das
Manifest vor jedem Tausch und tauscht per os.rename mit automatischem
Rollback bei Fehlern - install.ps1 bleibt nur noch für die
Erstinstallation nötig. Zugangstoken über einen neuen OptionsFlow in
entry.options, nie in configuration.yaml.
Nebenbei: mehrere seit der HACS-Ausschluss-Entscheidung liegen gebliebene
falsche HACS-Referenzen in Code-Kommentaren und einem UI-Text korrigiert.
Details, Sicherheitsbegründung und Verifikationsstand in AGENTS.md,
Abschnitte I und J.
Co-Authored-By: Claude Sonnet 5 <[email protected]>
This commit is contained in:
@@ -38,9 +38,11 @@ export function Einstellungen({
|
||||
tankvorgaenge,
|
||||
rohprofil,
|
||||
otaBuendel,
|
||||
integrationUpdate,
|
||||
api,
|
||||
profilSpeichern,
|
||||
jetztAktualisieren,
|
||||
neuLaden,
|
||||
} = useDaten()
|
||||
const [theme, themeSetzen] = useTheme()
|
||||
const [entwurf, setzeEntwurf] = useState<EinstellungenWerte | null>(null)
|
||||
@@ -49,6 +51,8 @@ export function Einstellungen({
|
||||
const [importOffen, setzeImportOffen] = useState(false)
|
||||
const [otaLaeuft, setzeOtaLaeuft] = useState(false)
|
||||
const [otaFehler, setzeOtaFehler] = useState<string | null>(null)
|
||||
const [updatePruefenLaeuft, setzeUpdatePruefenLaeuft] = useState(false)
|
||||
const [updateInstallierenLaeuft, setzeUpdateInstallierenLaeuft] = useState(false)
|
||||
const dateiwahl = useRef<HTMLInputElement | null>(null)
|
||||
|
||||
const otaUpdateVerfuegbar = otaMoeglich() && buendelPasst(otaBuendel, eigeneVersion())
|
||||
@@ -72,6 +76,33 @@ export function Einstellungen({
|
||||
}
|
||||
}
|
||||
|
||||
// Selbst-Update der Integration (aktualisierung.py) - Ersatz für
|
||||
// install.ps1, das Windows Smart App Control blockiert. Beide Aktionen
|
||||
// laufen sofort (keine Warteschlange, siehe api/index.ts) und laden
|
||||
// danach über neuLaden() den neuen Stand von
|
||||
// sensor.audi_dashboard_app_version - der Fehlertext bei fehlendem Token
|
||||
// kommt von dort mit, eine eigene "ist ein Token eingetragen?"-Prüfung
|
||||
// gibt es bewusst nicht.
|
||||
const updatePruefenAusloesen = async () => {
|
||||
setzeUpdatePruefenLaeuft(true)
|
||||
try {
|
||||
await api.updatePruefen()
|
||||
await neuLaden()
|
||||
} finally {
|
||||
setzeUpdatePruefenLaeuft(false)
|
||||
}
|
||||
}
|
||||
|
||||
const updateInstallierenAusloesen = async () => {
|
||||
setzeUpdateInstallierenLaeuft(true)
|
||||
try {
|
||||
await api.updateInstallieren()
|
||||
await neuLaden()
|
||||
} finally {
|
||||
setzeUpdateInstallierenLaeuft(false)
|
||||
}
|
||||
}
|
||||
|
||||
if (!einstellungen || !fahrzeug) return null
|
||||
const werte = entwurf ?? einstellungen
|
||||
|
||||
@@ -410,6 +441,56 @@ export function Einstellungen({
|
||||
</Tile>
|
||||
)}
|
||||
|
||||
<Tile>
|
||||
<span className="ads-eyebrow">Integration-Update</span>
|
||||
{integrationUpdate?.installiert ? (
|
||||
<>
|
||||
<p className="dm-fussnote">
|
||||
Version {integrationUpdate.version} installiert. Home Assistant neu starten, damit
|
||||
die neue Fassung geladen wird — Einstellungen → System → Neu starten.
|
||||
</p>
|
||||
<div className="dm-knopfreihe">
|
||||
<ActionButton onClick={() => void updatePruefenAusloesen()} disabled={updatePruefenLaeuft}>
|
||||
{updatePruefenLaeuft ? "Prüfe …" : "Erneut prüfen"}
|
||||
</ActionButton>
|
||||
</div>
|
||||
</>
|
||||
) : integrationUpdate?.verfuegbar ? (
|
||||
<>
|
||||
<Werteliste
|
||||
kinder={<Wertzeile label="Verfügbare Version" wert={integrationUpdate.version ?? "?"} />}
|
||||
/>
|
||||
<p className="dm-fussnote">
|
||||
Lädt die neueste Fassung von Gitea und ersetzt den Integrationsordner. Home Assistant
|
||||
muss danach neu gestartet werden. Fahrzeugprofil, Fahrten und Tankvorgänge sind davon
|
||||
nicht betroffen.
|
||||
</p>
|
||||
<div className="dm-knopfreihe">
|
||||
<ActionButton
|
||||
onClick={() => void updateInstallierenAusloesen()}
|
||||
disabled={updateInstallierenLaeuft}
|
||||
>
|
||||
{updateInstallierenLaeuft ? "Installiere …" : "Update installieren"}
|
||||
</ActionButton>
|
||||
</div>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<p className="dm-fussnote">
|
||||
{integrationUpdate
|
||||
? `Integration ist aktuell (${integrationUpdate.geprueft_am ? datumZeit(integrationUpdate.geprueft_am) : ""}).`
|
||||
: "Noch nicht geprüft."}
|
||||
</p>
|
||||
<div className="dm-knopfreihe">
|
||||
<ActionButton onClick={() => void updatePruefenAusloesen()} disabled={updatePruefenLaeuft}>
|
||||
{updatePruefenLaeuft ? "Prüfe …" : "Auf Update prüfen"}
|
||||
</ActionButton>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
{integrationUpdate?.fehler && <p className="dm-fehler">{integrationUpdate.fehler}</p>}
|
||||
</Tile>
|
||||
|
||||
<Tile>
|
||||
<span className="ads-eyebrow">Zugang</span>
|
||||
<Werteliste
|
||||
|
||||
@@ -13,7 +13,13 @@ import { useState } from "react"
|
||||
import { ActionButton, Feld, Tile } from "@audi-dash/ui"
|
||||
|
||||
import { useDaten } from "../daten/DatenKontext"
|
||||
import { inspektionPrognose, letzterInspektion, letzterOelwechsel, oelwechselPrognose } from "../daten/service"
|
||||
import {
|
||||
inspektionPrognose,
|
||||
letzterInspektion,
|
||||
letzterOelwechsel,
|
||||
meldungsPrognose,
|
||||
oelwechselPrognose,
|
||||
} from "../daten/service"
|
||||
import { datum, de, isoTag } from "../format"
|
||||
import type { SeitenName } from "../navigation"
|
||||
import { SymbolInspektion, SymbolOelwechsel } from "../symbole"
|
||||
@@ -37,7 +43,7 @@ interface Autohaus {
|
||||
}
|
||||
|
||||
export function Service({ geheZu }: { geheZu: (name: SeitenName, id?: string) => void }) {
|
||||
const { fahrzeug } = useDaten()
|
||||
const { fahrzeug, fahrten } = useDaten()
|
||||
if (!fahrzeug) return null
|
||||
|
||||
const buch = (fahrzeug.service["buch"] ?? []) as ServicebuchEintrag[]
|
||||
@@ -62,6 +68,12 @@ export function Service({ geheZu }: { geheZu: (name: SeitenName, id?: string) =>
|
||||
: prognose
|
||||
? `${de(prognose.restKm)} km · ${datum(prognose.datum)}`
|
||||
: null
|
||||
// Ganz ohne Servicebucheintrag (prognose null) übernimmt die
|
||||
// Fahrzeugmeldung selbst die Rolle der Prognosequelle, hochgerechnet mit
|
||||
// der echten Fahrleistung aus dem Fahrtenlog statt einer Servicebuch-Rate.
|
||||
const oelMeldungProg = oelHatMeldung && !prognose
|
||||
? meldungsPrognose(fahrzeug.oelwechselFaelligTs, fahrzeug.oelwechselFaelligKm, fahrten)
|
||||
: null
|
||||
// Bei Herstellervorgabe rechnet die eigene Prognose ohnehin schon mit dem
|
||||
// Herstellerintervall - die Restkilometerzahl daneben wäre nur eine zweite,
|
||||
// verwirrende km-Angabe neben dem Fahrzeug-Wert oben. Nur beim eigenen
|
||||
@@ -70,7 +82,9 @@ export function Service({ geheZu }: { geheZu: (name: SeitenName, id?: string) =>
|
||||
? fahrzeug.oel.modus === "hersteller"
|
||||
? `voraussichtlich am ${datum(prognose.datum)}`
|
||||
: `${de(prognose.restKm)} km, voraussichtlich am ${datum(prognose.datum)}`
|
||||
: null
|
||||
: oelMeldungProg
|
||||
? `voraussichtlich am ${datum(oelMeldungProg.datum)}`
|
||||
: null
|
||||
// Der Ölwechsel folgt in "Einrichten" wahlweise der Herstellervorgabe oder
|
||||
// einem kürzeren eigenen Intervall (fahrzeug.oel.modus) - die Inspektion hat
|
||||
// kein einstellbares Intervall, rechnet also immer mit der Herstellervorgabe
|
||||
@@ -92,7 +106,14 @@ export function Service({ geheZu }: { geheZu: (name: SeitenName, id?: string) =>
|
||||
: inspektionsprognose
|
||||
? datum(inspektionsprognose.datum)
|
||||
: null
|
||||
const inspFussnote = inspektionsprognose ? `voraussichtlich am ${datum(inspektionsprognose.datum)}` : null
|
||||
const inspMeldungProg = inspHatMeldung && !inspektionsprognose
|
||||
? meldungsPrognose(fahrzeug.inspektionFaelligTs, fahrzeug.inspektionFaelligKm, fahrten)
|
||||
: null
|
||||
const inspFussnote = inspektionsprognose
|
||||
? `voraussichtlich am ${datum(inspektionsprognose.datum)}`
|
||||
: inspMeldungProg
|
||||
? `voraussichtlich am ${datum(inspMeldungProg.datum)}`
|
||||
: null
|
||||
|
||||
const huDatum = fahrzeug.hu ? new Date(fahrzeug.hu) : null
|
||||
const huGueltig = !!huDatum && !Number.isNaN(huDatum.getTime())
|
||||
@@ -107,7 +128,7 @@ export function Service({ geheZu }: { geheZu: (name: SeitenName, id?: string) =>
|
||||
<SymbolOelwechsel groesse={26} />
|
||||
<span>Ölwechsel</span>
|
||||
</div>
|
||||
{oelHatEintrag ? (
|
||||
{oelHatEintrag || oelHatMeldung ? (
|
||||
<>
|
||||
{oelHerstellervorgabe && <div className="dm-tgvorgabe">Herstellervorgabe</div>}
|
||||
<div className="dm-tgwert">{oelWert}</div>
|
||||
@@ -123,7 +144,7 @@ export function Service({ geheZu }: { geheZu: (name: SeitenName, id?: string) =>
|
||||
<SymbolInspektion groesse={20} />
|
||||
<span>Inspektion</span>
|
||||
</div>
|
||||
{inspHatEintrag ? (
|
||||
{inspHatEintrag || inspHatMeldung ? (
|
||||
<>
|
||||
<div className="dm-tgvorgabe">Herstellervorgabe</div>
|
||||
<div className="dm-tgwert">{inspWert}</div>
|
||||
|
||||
@@ -53,7 +53,7 @@ export function Uebersicht({ geheZu }: { geheZu: (name: SeitenName, id?: string)
|
||||
.trim()
|
||||
|
||||
const buch = (fahrzeug.service["buch"] ?? []) as ServicebuchEintrag[]
|
||||
const service = naechsterService(fahrzeug, buch)
|
||||
const service = naechsterService(fahrzeug, buch, fahrten)
|
||||
|
||||
const letzteFahrt = fahrten
|
||||
.slice()
|
||||
|
||||
Reference in New Issue
Block a user