Fahrt beenden auf Knopfdruck, Schieberegler, Zuendungspause (2026.9.3.5-.7)
Vier Themen aus einer Sitzung. 1. Die Fahrt endet wieder bei uns statt im Dongle (Abschnitt BW). Der Ignition-OFF-Timeout des FMM003 (900 s) und sein Schlaf-Timeout (ebenfalls 900 s) starten beide beim Zuendungs-Aus und fallen in derselben Sekunde - am 02.09. zweimal beobachtet, einmal ging das Trip-Ende verloren, einmal kam es eine Sekunde vor dem Schlaf an. Ausloeser ist jetzt die Zuendung, die Wartezeit laeuft in Home Assistant. ZUENDUNG_NACHLAUF_S = 180 wird in beiden Wegen abgezogen (ueber das Trip-Signal 1080 s, weil dessen Ende selbst an der verzoegerten Zuendung haengt). 2. Schieberegler, neu in beiden Codebasen. Schrittweite 5 Minuten; der Daumen war im Tagmodus weiss auf weiss und traegt jetzt einen Ring aus --line-strong - ein Token, das genau dort sichtbar ist, wo es gebraucht wird. 3. Knopf "Fahrt beenden" in der Zuletzt-Kachel (Abschnitt BX). Schliesst auf das letzte Lebenszeichen, nicht auf "jetzt", und kennzeichnet das Ende als vorlaeufig. Ein spaet eintreffendes Zuendungs-Aus zieht es nach - innerhalb von sechs Stunden, nur nach vorn, und nur bei einer vorlaeufigen Fahrt. ts_end wandert bewusst NICHT in edited_fields, sonst blockierte der Schutz fuer Handeingaben genau diese Korrektur. 4. Das OTA-Buendel kann auf dem Mac gar nicht entstehen (Abschnitt BV): npm run ota ist eine Windows-PowerShell-Datei, ios-signieren.sh baut ein frisches dist/ und fasst das Buendel nie an. Ausgeliefert war deshalb eine Oberflaeche ohne den Versionshinweis unter richtiger Nummer. Verifiziert: Backend 27/27 (5 Signalwechsel, 14 Zuendungspause, 8 Fahrtende), companion-app tsc sauber und 179/179, Panel als Modul geparst, audi_ha_test auf 2026.9.3.7 sauber gestartet. Live am laufenden Panel und ohne Rueckstand belegt: Wartezeit samt Abbruch, die 180-s-Rechnung, der Regler im Tagmodus und der Knopf von der laufenden Fahrt bis zum verworfenen Kurzvorgang - Fahrten vorher 16, nachher 16. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -1 +1 @@
|
||||
{"version":"2026.9.3.4","sha256":"54a1d06cd129ee5c8c28869a7191a40adbfb383b5090bd2feaed2ec9c8c1ffe9","bytes":267146,"gebaut":"2026-09-03T08:56:06Z"}
|
||||
{"version":"2026.9.3.7","sha256":"82f5fdbf1ab4281a51f0c2d31f27b7609b4bc88835c19641d071f9db4970bb00","bytes":268073,"gebaut":"2026-09-03T12:57:42Z"}
|
||||
Binary file not shown.
@@ -200,6 +200,11 @@ function profilZuConfig(p) {
|
||||
backupIntervall: p.einstellungen.backup_intervall || "aus",
|
||||
letztesBackup: p.einstellungen.letztes_backup || null,
|
||||
ausfuehrung: p.fahrzeug.ausfuehrung || "",
|
||||
// Der Wert hat die Zeit ueberlebt, in der die Einstellung nicht
|
||||
// angeboten wurde (31.08. bis 03.09.2026); der Standard greift nur bei
|
||||
// neuen Profilen. Siehe fahrterkennung._pausenzeit_s im Backend.
|
||||
pausenzeit: Number.isFinite(p.einstellungen.fahrten_pausenzeit_min)
|
||||
? p.einstellungen.fahrten_pausenzeit_min : 15,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -227,6 +232,7 @@ function profilZuCar(p, status) {
|
||||
sicherheitscheck: status.sicherheitscheck || [],
|
||||
zuendung: status.zuendung ?? null,
|
||||
geparktSeit: status.geparkt_seit ?? null,
|
||||
faehrtSeit: status.faehrt_seit ?? null,
|
||||
standortLat: status.standort_lat,
|
||||
standortLon: status.standort_lon,
|
||||
standortGenauigkeitM: status.standort_genauigkeit_m,
|
||||
@@ -284,6 +290,7 @@ function configCarZuProfil(vorherigesProfil) {
|
||||
p.fahrzeug.ausfuehrung = CONFIG.ausfuehrung;
|
||||
p.einstellungen.uebersichtsbild = CONFIG.startbild;
|
||||
p.einstellungen.backup_intervall = CONFIG.backupIntervall;
|
||||
p.einstellungen.fahrten_pausenzeit_min = CONFIG.pausenzeit;
|
||||
p.einstellungen.smartdeal = { aktiv: CAR.smartdeal.aktiv, laeuft_ab: CAR.smartdeal.laeuftAb };
|
||||
p.einstellungen.oelwechsel_intervall = {
|
||||
modus: CAR.oel.modus, km: CAR.oel.km, monate: CAR.oel.monate,
|
||||
@@ -959,9 +966,9 @@ function standortZustand() {
|
||||
};
|
||||
}
|
||||
/* Ab wann aus "steht gerade" ein "Parkplatz" wird. Hing bis 2026-08-31 an
|
||||
der Einstellung fahrten_pausenzeit_min - die ist mit der Pausenregel
|
||||
entfallen (der FMM003 bringt sie selbst mit), der Schwellwert hier hat
|
||||
damit aber nichts zu tun und bleibt als eigene Zahl stehen. */
|
||||
der Einstellung fahrten_pausenzeit_min. Die gibt es seit dem 03.09.2026
|
||||
wieder ("Fahrt beenden"), aber der Schwellwert hier hat nach wie vor nichts
|
||||
damit zu tun und bleibt als eigene Zahl stehen. */
|
||||
const PARKPLATZ_AB_MIN = 15;
|
||||
function dauerSeitText(ts) {
|
||||
if (ts == null) return "unbekannt";
|
||||
@@ -1608,9 +1615,15 @@ function teaser() {
|
||||
: null;
|
||||
const t = juengster(TRIPS, "ts_start"), f = juengster(FILLS, "ts");
|
||||
return `<div class="tile teaser"><span class="label">Zuletzt</span>
|
||||
${t ? `<button class="leaf" style="padding-left:0" data-go="trip:${t.trip_id}">
|
||||
${CAR.faehrtSeit ? `<div class="leaf" style="padding-left:0">
|
||||
${/* Kein Knopf und keine Wertespalte: solange die Fahrt laeuft, gibt es
|
||||
weder Strecke noch Verbrauch. Ein Strich behauptete, ein Wert fehle -
|
||||
richtig ist, dass es ihn noch nicht gibt. */""}
|
||||
<span class="k">Aktuelle Fahrt<small class="laeuft">läuft</small></span>
|
||||
<button class="zeilenknopf" data-fahrtbeenden>Fahrt beenden</button></div>`
|
||||
: t ? `<button class="leaf" style="padding-left:0" data-go="trip:${t.trip_id}">
|
||||
<span class="k">Letzte Fahrt<small>${strecke(t)}</small></span>
|
||||
<span class="v">${streckeText(t.distance_km, "—")}<small>${fahrtVerbrauch(t) != null ? de(fahrtVerbrauch(t), 1) + " l/100 km" : "—"}</small></span>
|
||||
<span class="v">${t.ende_vorlaeufig ? "~ " : ""}${streckeText(t.distance_km, "—")}<small${t.ende_vorlaeufig ? " class=\"vorlaeufig\"" : ""}>${t.ende_vorlaeufig ? "vorläufig" : fahrtVerbrauch(t) != null ? de(fahrtVerbrauch(t), 1) + " l/100 km" : "—"}</small></span>
|
||||
<svg class="chev" viewBox="0 0 6 10"><polyline points="1,1 5,5 1,9"/></svg></button>` : ""}
|
||||
${f ? `<button class="leaf" style="padding-left:0;border-bottom:0" data-go="fill:${f.tank_id}">
|
||||
<span class="k">Letzter Tankvorgang<small>${f.ts ? dedat(new Date(f.ts)) : ""}</small></span>
|
||||
@@ -3063,6 +3076,18 @@ function vEinst() {
|
||||
` : ""}
|
||||
<button class="aktion ${einrichtenOffen ? "primaer" : ""}" style="margin-top:16px" data-einrichten-auf>${einrichtenOffen ? "Fertig" : "Einrichten"}</button>
|
||||
</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
|
||||
aria-label="Minuten bis zum automatischen Fahrtende">
|
||||
<output>${CONFIG.pausenzeit === 0 ? "sofort" : 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>
|
||||
</div>
|
||||
<div class="tile"><span class="label">Darstellung</span>
|
||||
<div class="seg">
|
||||
<button data-mode="nacht" class="${HOST.dataset.theme === "nacht" ? "on" : ""}">Nacht</button>
|
||||
@@ -5946,11 +5971,29 @@ function ereignisseVerdrahten() {
|
||||
// Einzelbeleg: zurück auf die Tankstelle (manipuliert MAP direkt, kein
|
||||
// render() - das würde die Karte neu aufbauen und den Ausschnitt verlieren).
|
||||
if (e.target.closest("#belegZentrieren")) { belegAufTankstelleZentrieren(); return; }
|
||||
// Vor [data-go]: der Knopf sitzt in der Zuletzt-Kachel, deren Zeile sonst
|
||||
// in die Einzelfahrt navigiert. Die Zeile ist waehrend der Fahrt zwar ein
|
||||
// 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; }
|
||||
const g = e.target.closest("[data-go]");
|
||||
if (g) { const [n, i] = g.dataset.go.split(":"); go(n, i); }
|
||||
});
|
||||
|
||||
ROOT.addEventListener("input", (e) => {
|
||||
if (e.target.dataset.pausenzeit !== undefined) {
|
||||
// Beim Ziehen nur die Anzeige und die Fuellung nachziehen, NICHT
|
||||
// render() und nicht speichern: ein Re-Render mitten in der Geste
|
||||
// erzeugt den Regler neu und der Daumen springt aus der Hand.
|
||||
// Gespeichert wird beim Loslassen (change-Zweig weiter unten) -
|
||||
// dieselbe Trennung wie beim Suchfeld direkt darunter.
|
||||
const minuten = parseInt(e.target.value, 10);
|
||||
CONFIG.pausenzeit = minuten;
|
||||
e.target.style.setProperty("--fuell", (minuten / 60) * 100 + "%");
|
||||
const feld = e.target.parentElement.querySelector("output");
|
||||
if (feld) feld.textContent = minuten === 0 ? "sofort" : minuten + " Min.";
|
||||
return;
|
||||
}
|
||||
const sucheFeld = e.target.dataset.setupSuche;
|
||||
if (sucheFeld) {
|
||||
// Nur die Kandidatenliste patchen, nicht render() - ein voller
|
||||
@@ -6150,6 +6193,7 @@ function ereignisseVerdrahten() {
|
||||
if (e.target.dataset.oelm !== undefined) { CAR.oel.monate = parseInt(e.target.value, 10); profilSpeichern(); return; }
|
||||
if (e.target.dataset.setupNurpassend !== undefined) { setupNurPassend = e.target.checked; render(); return; }
|
||||
if (e.target.dataset.startbild !== undefined) { CONFIG.startbild = e.target.value; profilSpeichern(); render(); return; }
|
||||
if (e.target.dataset.pausenzeit !== undefined) { CONFIG.pausenzeit = parseInt(e.target.value, 10); profilSpeichern(); return; }
|
||||
if (e.target.dataset.svsel !== undefined) { svAuswahl = parseInt(e.target.value, 10); render(); return; }
|
||||
});
|
||||
}
|
||||
|
||||
@@ -798,6 +798,75 @@ img.carfix.klein { aspect-ratio: auto; height: auto; }
|
||||
.switch input:checked + .track { background: var(--fg); border-color: var(--fg); }
|
||||
.switch input:checked + .track::before { transform: translateX(19px); background: var(--canvas); }
|
||||
|
||||
/* Schieberegler - Zwilling von .ads-schieber im Design-System.
|
||||
|
||||
Ein echtes input[type=range], damit Tastatur und Vorlesefunktion ohne
|
||||
Zutun stimmen; gemalt wird selbst, sonst saehe er im Panel wie Chrome und
|
||||
in der App wie WebKit aus. Der gefuellte Teil kommt aus --fuell, weil ein
|
||||
Range-Feld seinen Wert nicht ans Stylesheet weitergibt und accent-color
|
||||
entfaellt, sobald man die Spur selbst gestaltet (gesetzt beim Rendern und
|
||||
im input-Zweig der Ereignisse).
|
||||
|
||||
Die beiden Spur-Regeln lassen sich NICHT zusammenfassen: einen unbekannten
|
||||
Selektor verwirft der Browser samt der ganzen Regel. */
|
||||
.schieber { display: flex; align-items: center; gap: 16px; min-width: 0; }
|
||||
/* 28 px hoch bei 4 px Spur: das ist die Trefferflaeche des Daumens. */
|
||||
.schieber input[type=range] {
|
||||
-webkit-appearance: none; appearance: none;
|
||||
flex: 1 1 auto; min-width: 0; height: 28px;
|
||||
margin: 0; padding: 0; background: transparent; cursor: pointer;
|
||||
}
|
||||
.schieber input[type=range]::-webkit-slider-runnable-track {
|
||||
height: 4px; border-radius: var(--r-pill);
|
||||
background: linear-gradient(to right, var(--fg) var(--fuell, 0%), var(--ios-fill) var(--fuell, 0%));
|
||||
}
|
||||
.schieber input[type=range]::-moz-range-track {
|
||||
height: 4px; border-radius: var(--r-pill);
|
||||
background: linear-gradient(to right, var(--fg) var(--fuell, 0%), var(--ios-fill) var(--fuell, 0%));
|
||||
}
|
||||
/* margin-top zentriert den Daumen auf der Spur: (4 - 28) / 2 = -12. */
|
||||
.schieber input[type=range]::-webkit-slider-thumb {
|
||||
-webkit-appearance: none; appearance: none; box-sizing: border-box;
|
||||
width: 28px; height: 28px; margin-top: -12px; border-radius: 50%;
|
||||
/* Der Ring ist der Trick fuer den Tagmodus: --line-strong ist dort
|
||||
rgba(0,0,0,.22) und zeichnet den weissen Daumen gegen die helle Kachel
|
||||
ab, im Nachtmodus rgba(255,255,255,.20) - auf Weiss also unsichtbar, wo
|
||||
er nicht gebraucht wird. Ein Token, zwei Themen. */
|
||||
border: 1px solid var(--line-strong);
|
||||
background: #fff; box-shadow: 0 1px 3px rgba(0,0,0,.22), 0 4px 10px rgba(0,0,0,.16);
|
||||
}
|
||||
.schieber input[type=range]::-moz-range-thumb {
|
||||
box-sizing: border-box; width: 28px; height: 28px; border-radius: 50%;
|
||||
border: 1px solid var(--line-strong);
|
||||
background: #fff; box-shadow: 0 1px 3px rgba(0,0,0,.22), 0 4px 10px rgba(0,0,0,.16);
|
||||
}
|
||||
.schieber input[type=range]:focus-visible { outline: none; }
|
||||
.schieber input[type=range]:focus-visible::-webkit-slider-thumb {
|
||||
box-shadow: 0 0 0 3px var(--line-strong), 0 3px 8px rgba(0,0,0,.16);
|
||||
}
|
||||
/* Feste Mindestbreite und Ziffern gleicher Laufweite: sonst ruckelt der
|
||||
Regler seitlich, sobald der Wert von 9 auf 10 springt. */
|
||||
/* Knopf in einer Listenzeile - heute nur "Fahrt beenden" in der
|
||||
Zuletzt-Kachel. 36 px sichtbar, 44 px Trefferhoehe ueber das
|
||||
Pseudoelement, damit die Zeile nicht hoeher wird als ihre Nachbarin. */
|
||||
.zeilenknopf {
|
||||
position: relative; flex: 0 0 auto;
|
||||
font-family: inherit; font-size: 13px; letter-spacing: .01em;
|
||||
color: var(--fg); background: none; border: 1px solid var(--line-strong);
|
||||
border-radius: var(--r-pill); padding: 8px 15px; min-height: 36px; cursor: pointer;
|
||||
}
|
||||
.zeilenknopf:hover { background: var(--shade); }
|
||||
.zeilenknopf:active { opacity: .55; }
|
||||
.zeilenknopf::before { content: ""; position: absolute; inset: -4px 0; }
|
||||
/* Gruen wie der Sicherheits-Haken: es laeuft gerade etwas. */
|
||||
.leaf .k small.laeuft { color: var(--ok); }
|
||||
.leaf .v small.vorlaeufig { color: var(--fg3); }
|
||||
|
||||
.schieber output {
|
||||
flex: 0 0 auto; min-width: 62px; text-align: right;
|
||||
font-size: 13px; font-variant-numeric: tabular-nums; color: var(--fg2);
|
||||
}
|
||||
|
||||
/* C3: gemischt geschrieben, 15 px, 48 px hoch. .primaer ist gefüllt,
|
||||
.aktion allein bleibt die Umriss-Sekundäraktion. */
|
||||
.aktion {
|
||||
|
||||
Reference in New Issue
Block a user