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 <noreply@anthropic.com>
This commit is contained in:
2026-09-03 20:00:39 +02:00
parent 8c96d9b78e
commit 4d4890034a
18 changed files with 999 additions and 30 deletions
@@ -1834,9 +1834,9 @@ function terminText(t) {
Updates vom Backend, siehe datenLaden()) selbständig hoch und schaltet
dabei die Einheit um - Sek. bis 60, dann Min. bis 60, dann Std. bis 24,
dann Tage. */
function standAlterText() {
if (LETZTES_UPDATE_TS == null) return "unbekannt";
const sekunden = Math.max(0, Math.floor((Date.now() - LETZTES_UPDATE_TS) / 1000));
function alterText(ts) {
if (ts == null || !Number.isFinite(ts)) return "unbekannt";
const sekunden = Math.max(0, Math.floor((Date.now() - ts) / 1000));
if (sekunden < 60) return `vor ${sekunden} Sek.`;
const minuten = Math.floor(sekunden / 60);
if (minuten < 60) return `vor ${minuten} Min.`;
@@ -1844,6 +1844,9 @@ function standAlterText() {
if (stunden < 24) return `vor ${stunden} Std.`;
return `vor ${Math.floor(stunden / 24)} Tage`;
}
function standAlterText() {
return alterText(LETZTES_UPDATE_TS);
}
function standAlterTicken() {
if (!ROOT || !CAR) return;
const sy = $("#sync span"); if (sy) sy.textContent = standAlterText();
@@ -3100,16 +3103,47 @@ function vEinst() {
</div>` : ""}
<div class="tile"><span class="label">Fahrt beenden</span>
<div class="schieber" style="margin-top:14px">
<input type="range" min="0" max="60" step="5" value="${CONFIG.pausenzeit}"
style="--fuell:${(CONFIG.pausenzeit / 60) * 100}%" data-pausenzeit
<input type="range" min="1" max="60" step="1" value="${CONFIG.pausenzeit}"
style="--fuell:${((CONFIG.pausenzeit - 1) / 59) * 100}%" data-pausenzeit
aria-label="Minuten bis zum automatischen Fahrtende">
<output>${CONFIG.pausenzeit === 0 ? "sofort" : CONFIG.pausenzeit + " Min."}</output>
<output>${CONFIG.pausenzeit} Min.</output>
</div>
<span class="label" style="margin-top:14px;color:var(--fg2);letter-spacing:0;
text-transform:none;font-size:12.5px;line-height:1.6">
Hier können Sie einstellen, wie viele Minuten nach Ausschalten der Zündung die
Fahrt automatisch beendet werden soll.</span>
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>
</div>
${DONGLE && DONGLE.aktiv ? `<div class="tile"><span class="label">Dongle</span>
${DONGLE.stand && DONGLE.stand.fehler
? `<span class="label" style="margin-top:8px;color:var(--bad);letter-spacing:0;
text-transform:none;font-size:12.5px;line-height:1.6">${esc(DONGLE.stand.fehler)}</span>`
: ""}
${DONGLE.stand && (DONGLE.stand.werte || []).length ? `<dl class="rows">
${DONGLE.stand.werte.map((w) => `<div class="row">
<dt>${esc(w.titel)} <small style="color:var(--fg3)">${esc(w.nummer)}</small></dt>
<dd${w.abweichung ? ` style="color:var(--warn)"` : ""}>${
w.geraet === null || w.geraet === undefined ? "" : esc(String(w.geraet))
}${w.einheit ? " " + esc(w.einheit) : ""}${
w.offen !== null && w.offen !== undefined
? ` <small>→ ${esc(String(w.offen))}, unterwegs</small>`
: w.abweichung ? ` <small>(wir: ${esc(String(w.unser))})</small>` : ""
}</dd>
</div>`).join("")}
</dl>` : ""}
<button class="aktion" data-dongle ${DONGLE_LAEUFT ? "disabled" : ""}>${
DONGLE_LAEUFT ? `<span class="lade-spinner"></span>Liest …` : "Jetzt lesen"
}</button>
<span class="label" style="margin-top:14px;color:var(--fg2);letter-spacing:0;
text-transform:none;font-size:12.5px;line-height:1.6">
${DONGLE.stand && DONGLE.stand.gelesen_am
? `Gelesen ${esc(alterText(Date.parse(DONGLE.stand.gelesen_am)))}. `
: "Noch nicht gelesen. "}Lesen und Schreiben gehen nur bei laufender Zündung
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>
</div>` : ""}
<div class="tile"><span class="label">Darstellung</span>
<div class="seg">
<button data-mode="nacht" class="${HOST.dataset.theme === "nacht" ? "on" : ""}">Nacht</button>
@@ -4158,6 +4192,13 @@ let INTEGRATION_UPDATE = null;
/* Nur "gesetzt/nicht gesetzt" je Dienst - der Token selbst kommt nie ueber
die Leitung, siehe zugaenge.py im Backend. */
let ZUGAENGE = [];
/* Was am Dongle steht, was noch aussteht - siehe flespi.py im Backend.
null, solange kein flespi-Token hinterlegt ist. */
let DONGLE = null;
// true, waehrend "Jetzt lesen" laeuft. Wie INTEGRATION_UPDATE_LAEUFT und aus
// demselben Grund: der Abruf geht ueber zwei Netze (wir -> flespi -> Geraet)
// und darf nicht wie ein toter Knopf aussehen.
let DONGLE_LAEUFT = false;
// "pruefen" | "installieren" | "neustart" | null - während einer dieser drei
// Aktionen läuft, siehe vEinst()'s Integration-Update-Kachel. Eigens statt
// über serviceRufen() (das feuert-und-vergisst), weil diese drei Aktionen
@@ -6001,6 +6042,18 @@ function ereignisseVerdrahten() {
// div ohne data-go, aber die Reihenfolge haelt das auch dann, wenn die
// Kachel spaeter einmal anders gebaut wird.
if (e.target.closest("[data-fahrtbeenden]")) { serviceRufen("fahrt_jetzt_beenden", {}); return; }
if (e.target.closest("[data-dongle]")) {
// Nicht ueber serviceRufen(): das feuert und vergisst, hier soll der Knopf
// aber zeigen, dass gerade zwei Netze befragt werden - und danach wieder
// freigeben. Das Ergebnis (auch ein Fehler) kommt ueber die veroeffentlichte
// Entitaet zurueck, siehe flespi.py; deshalb hier nur der Ladezustand.
if (DONGLE_LAEUFT) return;
DONGLE_LAEUFT = true; render();
HASS.callService(DOMAIN, "dongle_lesen", {})
.catch((f) => console.error("dongle_lesen", f))
.finally(() => { DONGLE_LAEUFT = false; render(); });
return;
}
const tokSpeichern = e.target.closest("[data-tokenspeichern]");
if (tokSpeichern) {
const dienst = tokSpeichern.dataset.tokenspeichern;
@@ -6391,6 +6444,9 @@ async function datenLaden(erzwingen) {
ZUGAENGE = versionState.attributes.daten
? versionState.attributes.daten.zugaenge ?? []
: [];
DONGLE = versionState.attributes.daten
? versionState.attributes.daten.dongle ?? null
: null;
}
if (entitaetenState && entitaetenState.attributes) ENTITAETEN = entitaetenState.attributes.daten;