Regler stellt den Schlaf-Timeout des Dongles (2026.9.3.10)
Der Regler "Fahrt beenden" ist jetzt eine Zahl fuer zwei Dinge: unsere eigene
Wartezeit bis zum Fahrtende und der Schlaf-Timeout des FMM003 (103). So lange
bleibt der Dongle wach und wartet darauf, dass es gleich weitergeht; danach
schlaeft er. Die 180 s Ignition OFF Delay laufen davor und bleiben unsichtbar -
sie werden nur von der Fahrtdauer abgezogen. Der Regler geht deshalb von 1 bis
60 in Einerschritten; das Geraet kennt kein Timeout 0.
flespi.py liest und schreibt die Geraete-Konfiguration. Drei Annahmen sind an
der echten API gefallen und stehen als Messung im Modulkopf: der Wert liegt in
current (nicht value), die Einstellung heisst sleep_mode mit dem Wert eine Ebene
tiefer unter mode, und ein PUT braucht {"properties": ..., "address": ...}.
Die eigene Auftrags-Warteschlange ist wieder entfallen. Sie war fuer "das Geraet
schlaeft" gebaut - das gilt aber nur fuer das Geraet, nicht fuer die
Schnittstelle: die vollstaendige Konfiguration kam herein, waehrend das Fahrzeug
seit dem Vortag stand. flespi puffert selbst (pending + address=connection), ein
zweites Auftragsbuch waere eine zweite Warteschlange fuer dieselbe Aufgabe.
Gegengelesen wird nach jedem Schreiben; als Erfolg zaehlt current ODER pending -
letzteres ist bei stehendem Fahrzeug der Normalfall.
Live gegen den echten Account belegt: Token sieht genau ein Geraet, 127
Einstellungen bei parkendem Fahrzeug gelesen, Regler 15 geschrieben und als
pending bestaetigt (Nachbarfelder unveraendert), zweiter Neustart ohne erneute
Anfrage.
Co-Authored-By: Claude Opus 5 <[email protected]>
This commit is contained in:
@@ -351,6 +351,16 @@ export class DataMetricApi {
|
||||
return this.rest.dienstAufrufen(DIENST_DOMAIN, "zugang_setzen", { dienst, token });
|
||||
}
|
||||
|
||||
/** Liest die Konfiguration des Dongles - und schreibt dabei eine vorgemerkte
|
||||
Aenderung, falls eine offen ist (siehe flespi.py).
|
||||
|
||||
Wie zugangSetzen bewusst NICHT ueber die Warteschlange: der Abruf gelingt
|
||||
nur bei laufender Zuendung, und Stunden spaeter aus dem Funkloch nachgeholt
|
||||
traefe er das Geraet garantiert im Schlaf an. */
|
||||
dongleLesen(): Promise<unknown> {
|
||||
return this.rest.dienstAufrufen(DIENST_DOMAIN, "dongle_lesen", {});
|
||||
}
|
||||
|
||||
historieImportieren(start: string, ende: string): Promise<unknown> {
|
||||
return this.rest.dienstAufrufen(DIENST_DOMAIN, "historie_importieren", {
|
||||
start,
|
||||
|
||||
@@ -234,12 +234,50 @@ export interface ZugangAngabe {
|
||||
rest: string | null;
|
||||
}
|
||||
|
||||
/** Was am Dongle steht und was noch aussteht (flespi.py).
|
||||
|
||||
`aktiv` ist false, solange kein flespi-Token hinterlegt ist - dann gibt es
|
||||
nichts zu zeigen. `stand` traegt seinen eigenen Zeitstempel: eine Angabe von
|
||||
gestern soll nicht wie eine frische aussehen. Was noch zum Geraet unterwegs
|
||||
ist, steht je Wert in `offen` - flespi haelt es vor, wir nicht. */
|
||||
export interface DongleWert {
|
||||
schluessel: string;
|
||||
/** Die Nummer im flespi-Configurator - danach sucht man dort. */
|
||||
nummer: string;
|
||||
titel: string;
|
||||
einheit: string;
|
||||
geraet: unknown;
|
||||
/** Was noch zum Gerät unterwegs ist (flespis `pending`) - null, wenn nichts
|
||||
aussteht. */
|
||||
offen: unknown;
|
||||
/** Unser eigener Wert, sofern es einen zu vergleichen gibt. */
|
||||
unser: number | null;
|
||||
abweichung: boolean;
|
||||
}
|
||||
|
||||
export interface DongleStand {
|
||||
geraet?: number;
|
||||
gelesen_am: string;
|
||||
anzahl?: number;
|
||||
werte?: DongleWert[];
|
||||
abweichungen?: number;
|
||||
/** Wie viele der gezeigten Werte noch zum Gerät unterwegs sind. */
|
||||
offen?: number;
|
||||
fehler: string | null;
|
||||
}
|
||||
|
||||
export interface DongleAngabe {
|
||||
aktiv: boolean;
|
||||
stand: DongleStand | null;
|
||||
}
|
||||
|
||||
/** Nutzlast von sensor.audi_dashboard_app_version. */
|
||||
export interface AppVersionAngabe {
|
||||
app: string | null;
|
||||
buendel: Buendelangabe | null;
|
||||
integration_update: IntegrationUpdateAngabe | null;
|
||||
zugaenge?: ZugangAngabe[];
|
||||
dongle?: DongleAngabe;
|
||||
}
|
||||
|
||||
/* -------------------------------------------------- Entitäts-Verzeichnis */
|
||||
|
||||
@@ -26,6 +26,7 @@ import {
|
||||
type Fahrt,
|
||||
type Fahrzeugstatus,
|
||||
type IntegrationUpdateAngabe,
|
||||
type DongleAngabe,
|
||||
type ZugangAngabe,
|
||||
type Profil,
|
||||
type Tankvorgang,
|
||||
@@ -71,6 +72,9 @@ export interface DatenWert {
|
||||
Integration (aktualisierung.py), oder null vor der ersten Prüfung. */
|
||||
integrationUpdate: IntegrationUpdateAngabe | null
|
||||
zugaenge: ZugangAngabe[]
|
||||
/** Konfiguration des Dongles und eine ggf. vorgemerkte Aenderung
|
||||
(flespi.py). null, solange kein flespi-Token hinterlegt ist. */
|
||||
dongle: DongleAngabe | null
|
||||
|
||||
einstellungen: Einstellungen | null
|
||||
fahrzeug: Fahrzeug | null
|
||||
@@ -118,6 +122,7 @@ export function DatenAnbieter({
|
||||
const [serverVersion, setzeServerVersion] = useState<string | null>(null)
|
||||
const [otaBuendel, setzeOtaBuendel] = useState<Buendelangabe | null>(null)
|
||||
const [zugaenge, setzeZugaenge] = useState<ZugangAngabe[]>([])
|
||||
const [dongle, setzeDongle] = useState<DongleAngabe | null>(null)
|
||||
const [integrationUpdate, setzeIntegrationUpdate] = useState<IntegrationUpdateAngabe | null>(
|
||||
null,
|
||||
)
|
||||
@@ -162,6 +167,7 @@ export function DatenAnbieter({
|
||||
setzeOtaBuendel(versionsangabe?.buendel ?? null)
|
||||
setzeIntegrationUpdate(versionsangabe?.integration_update ?? null)
|
||||
setzeZugaenge(versionsangabe?.zugaenge ?? [])
|
||||
setzeDongle(versionsangabe?.dongle ?? null)
|
||||
return true
|
||||
} catch (fehler) {
|
||||
// Ein voruebergehender Ausfall ist kein Ladefehler, solange schon
|
||||
@@ -294,6 +300,7 @@ export function DatenAnbieter({
|
||||
otaBuendel,
|
||||
integrationUpdate,
|
||||
zugaenge,
|
||||
dongle,
|
||||
einstellungen,
|
||||
fahrzeug,
|
||||
fahrten,
|
||||
|
||||
@@ -15,7 +15,7 @@ import { Datumsfeld } from "./Datumsfeld"
|
||||
import { useDaten } from "../daten/DatenKontext"
|
||||
import { OtaFehler, buendelAnwenden, buendelPasst, otaMoeglich } from "../daten/ota"
|
||||
import type { Einstellungen as EinstellungenWerte } from "../daten/profilAdapter"
|
||||
import { datum, datumZeit, isoTag } from "../format"
|
||||
import { datum, datumZeit, isoTag, standAlter } from "../format"
|
||||
import { useTheme } from "../theme"
|
||||
import { BildMitMenue, Wertzeile, Werteliste, bestaetigen } from "./bausteine"
|
||||
import { BILDPLAETZE, SEITE_WINTER, markenlogoUrl } from "./bilder"
|
||||
@@ -43,6 +43,7 @@ export function Einstellungen({
|
||||
integrationUpdate,
|
||||
serverVersion,
|
||||
zugaenge,
|
||||
dongle,
|
||||
verbindung,
|
||||
api,
|
||||
profilSpeichern,
|
||||
@@ -231,6 +232,28 @@ export function Einstellungen({
|
||||
const [tokenLaeuft, setzeTokenLaeuft] = useState<string | null>(null)
|
||||
const [tokenFehler, setzeTokenFehler] = useState<string | null>(null)
|
||||
|
||||
// Wie tokenLaeuft daneben: der Abruf geht ueber zwei Netze (wir -> flespi ->
|
||||
// Geraet) und darf nicht wie ein toter Knopf aussehen. Das Ergebnis selbst
|
||||
// kommt ueber die veroeffentlichte Entitaet zurueck (flespi.py), hier steht
|
||||
// nur, was den Aufruf gar nicht erst erreicht hat.
|
||||
const [dongleLaeuft, setzeDongleLaeuft] = useState(false)
|
||||
const [dongleFehler, setzeDongleFehler] = useState<string | null>(null)
|
||||
|
||||
const dongleAbrufen = async () => {
|
||||
setzeDongleLaeuft(true)
|
||||
setzeDongleFehler(null)
|
||||
try {
|
||||
await api.dongleLesen()
|
||||
await neuLaden()
|
||||
} catch (fehler) {
|
||||
setzeDongleFehler(
|
||||
fehler instanceof Error ? fehler.message : "Der Abruf ist gescheitert.",
|
||||
)
|
||||
} finally {
|
||||
setzeDongleLaeuft(false)
|
||||
}
|
||||
}
|
||||
|
||||
const tokenSchreiben = async (dienst: string, wert: string) => {
|
||||
setzeTokenLaeuft(dienst)
|
||||
setzeTokenFehler(null)
|
||||
@@ -375,22 +398,61 @@ export function Einstellungen({
|
||||
<Tile>
|
||||
<span className="ads-eyebrow">Fahrt beenden</span>
|
||||
<Slider
|
||||
min={0}
|
||||
min={1}
|
||||
max={60}
|
||||
schritt={5}
|
||||
schritt={1}
|
||||
wert={werte.pausenzeitMin}
|
||||
onChange={(min) => aendern({ pausenzeitMin: min })}
|
||||
anzeige={werte.pausenzeitMin === 0 ? "sofort" : werte.pausenzeitMin + " Min."}
|
||||
anzeige={werte.pausenzeitMin + " Min."}
|
||||
aria-label="Minuten bis zum automatischen Fahrtende"
|
||||
/>
|
||||
{/* Fusszeile statt Info-Knopf: Apples eigenes Muster fuer erklaerenden
|
||||
Text unter einer Einstellung ist der Footer der Gruppe. */}
|
||||
<span className="ads-eyebrow" style={{ marginTop: "14px" }}>
|
||||
Hier können Sie einstellen, wie viele Minuten nach Ausschalten der Zündung die
|
||||
Fahrt automatisch beendet werden soll.
|
||||
Fahrt automatisch beendet werden soll. Derselbe Wert stellt den Schlaf-Timeout
|
||||
des Dongles: so lange bleibt er nach dem Abstellen wach und wartet darauf, dass
|
||||
es gleich weitergeht. Danach schläft er — das spart Strom, kostet beim Fortsetzen
|
||||
aber ein paar Sekunden fürs Aufwachen.
|
||||
</span>
|
||||
</Tile>
|
||||
|
||||
{dongle?.aktiv && (
|
||||
<Tile>
|
||||
<span className="ads-eyebrow">Dongle</span>
|
||||
{dongle.stand?.fehler && <p className="dm-fehler">{dongle.stand.fehler}</p>}
|
||||
{(dongle.stand?.werte ?? []).length > 0 && (
|
||||
<Werteliste
|
||||
kinder={(dongle.stand?.werte ?? []).map((w) => (
|
||||
<Wertzeile
|
||||
key={w.schluessel}
|
||||
label={`${w.titel} ${w.nummer}`}
|
||||
wert={`${w.geraet ?? "–"}${w.einheit ? " " + w.einheit : ""}`}
|
||||
zusatz={
|
||||
w.offen != null
|
||||
? `${w.offen} ist unterwegs zum Gerät`
|
||||
: w.abweichung
|
||||
? `wir rechnen mit ${w.unser}`
|
||||
: undefined
|
||||
}
|
||||
/>
|
||||
))}
|
||||
/>
|
||||
)}
|
||||
<ActionButton disabled={dongleLaeuft} onClick={() => void dongleAbrufen()}>
|
||||
{dongleLaeuft ? "Liest …" : "Jetzt lesen"}
|
||||
</ActionButton>
|
||||
{dongleFehler && <p className="dm-fehler">{dongleFehler}</p>}
|
||||
<span className="ads-eyebrow" style={{ marginTop: "14px" }}>
|
||||
{dongle.stand?.gelesen_am
|
||||
? `Gelesen ${standAlter(dongle.stand.gelesen_am)}. `
|
||||
: "Noch nicht gelesen. "}
|
||||
Der Regler oben schreibt den Schlaf-Timeout sofort hierher; bis das Gerät sich
|
||||
das nächste Mal meldet, steht die Änderung als „unterwegs" daneben.
|
||||
</span>
|
||||
</Tile>
|
||||
)}
|
||||
|
||||
<Tile>
|
||||
<span className="ads-eyebrow">Darstellung</span>
|
||||
<Seg
|
||||
|
||||
Reference in New Issue
Block a user