Setup-Menü für Sensor-Zuordnung + Umstellung von WLAN/VAG-Integration auf FMM003

Setup-Menü (Einstellungen -> Fahrzeug einrichten -> Setup): ordnet alle von
der App genutzten Sensor-Rollen echten HA-Entitäten zu, statt sie in
einstellungen.py von Hand einzutragen - durchsuchbares Dropdown je Feld,
Vorauswahl aus vorhandenen Entitäten, Schalter für "nur passende Sensoren".
Neues Modul entitaeten.py (Katalog + JSON-Override, zur Laufzeit über
setattr() auf einstellungen angewendet, kein Neustart nötig außer für die
drei trigger-gebundenen Felder).

Zusätzlich: Fahrzeug-WLAN-Erkennung und alle Entity-IDs der nicht mehr
genutzten TommiG1/HA_VAG-EU-Data-Act-Integration entfernt. Fahrterkennung
läuft jetzt über den Zündungs-/ACC-Status des neu angebundenen Teltonika
FMM003 (ersetzt die WLAN-Verbindungserkennung); Standort und 12V-
Batteriespannung kommen ebenfalls vom FMM003. Kilometerstand, Tankfüllstand,
Reichweite, Türen/Fenster/Schlösser sowie Ölwechsel-/Inspektionsdaten haben
dadurch vorerst keine Quelle mehr und zeigen "unbekannt" - die Funktionen
selbst bleiben erhalten und lassen sich über das neue Setup-Menü jederzeit
neu zuordnen.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
This commit is contained in:
2026-08-12 15:55:59 +02:00
co-authored by Claude Sonnet 5
parent 7e979ecfab
commit 9794193803
13 changed files with 848 additions and 197 deletions
+148 -24
View File
@@ -229,13 +229,20 @@ main#view::-webkit-scrollbar { width: 0; height: 0; }
transition: color .15s;
}
.tab.on { color: var(--fg); }
/* Die Pille ist ein eigenes Feld um das Symbol - so bleibt ihre Groesse
unabhaengig vom Symbol und der Wechsel laeuft sauber ueber background. */
.tabpille {
display: flex; align-items: center; justify-content: center;
width: 60px; height: 34px; border-radius: var(--r-pill);
transition: background .18s;
}
.tab.on .tabpille { background: var(--tile-2); }
.tab svg {
fill: none; stroke: currentColor; stroke-width: 1.6;
stroke-linecap: round; stroke-linejoin: round;
border-radius: var(--r-pill);
transition: box-shadow .18s, background .18s;
transition: transform .18s;
}
.tab.on svg { background: var(--tile-2); box-shadow: 0 0 0 11px var(--tile-2); }
.tab svg.voll { fill: currentColor; stroke: none; }
.tab:active svg { transform: scale(.9); }
.tab span { font-size: 11px; letter-spacing: .01em; }
.tabbar.ohne .tab span { display: none; }
@@ -664,11 +671,12 @@ button.tile, .tilebtn { transition: background .15s, transform .1s; }
}
.fahrzeug-pin-wrap { background: none; border: none; }
/* poi-car aus dem CI-Satz - rotes Symbol, weiss abgesetzt statt in einem
Kasten, damit die Spitze exakt auf der Position steht. */
.fahrzeug-pin {
width: 34px; height: 34px; border-radius: 10px;
background: var(--red); border: 2px solid #fff;
box-shadow: 0 3px 10px rgba(0,0,0,.35);
color: var(--red);
display: flex; align-items: center; justify-content: center;
filter: drop-shadow(0 0 1px #fff) drop-shadow(0 0 2px #fff) drop-shadow(0 2px 5px rgba(0,0,0,.45));
}
.user-pin-wrap { background: none; border: none; }
.user-pin {
@@ -688,45 +696,67 @@ button.tile, .tilebtn { transition: background .15s, transform .1s; }
.kartensteuerung {
position: absolute; z-index: 400; top: 16px; right: 16px;
display: flex; flex-direction: column; gap: 10px;
display: flex; flex-direction: column; gap: 12px;
}
.kartensteuerung button {
width: 42px; height: 42px; border-radius: 50%;
width: 48px; height: 48px; border-radius: 50%;
display: flex; align-items: center; justify-content: center;
background: var(--tile); color: var(--fg);
border: none; box-shadow: 0 3px 10px rgba(0,0,0,.25);
background: rgba(16, 20, 26, .84); color: #fff;
border: none; box-shadow: 0 4px 14px rgba(0,0,0,.32);
-webkit-backdrop-filter: blur(10px); backdrop-filter: blur(10px);
cursor: pointer; padding: 0;
}
.kartensteuerung button:active { background: var(--tile-2); transform: scale(.94); }
/* Tag-Theme: helle Kapsel mit dunklem Symbol - die CI-Symbole selbst tragen
fill="currentColor" und folgen der Textfarbe, es gibt keine zweite Datei. */
:host([data-theme="tag"]) .kartensteuerung button {
background: rgba(255,255,255,.92); color: #101418;
box-shadow: 0 4px 14px rgba(0,0,0,.18);
}
.kartensteuerung button:active { transform: scale(.94); }
:host([data-theme="tag"]) .kartensteuerung button:active { background: #fff; }
/* Griffzone/Zug-Amplitude (STANDORT_PEEK_PX im Frontend) - beide Werte
müssen zusammenpassen, siehe Kommentar dort. */
/* Sichtbarer Anteil im geschlossenen Zustand (STANDORT_PEEK_PX im Frontend) -
beide Werte müssen zusammenpassen, siehe Kommentar dort. Der Rest des
Blatts wird nach unten geschoben und mit einem Zug nach oben geöffnet. */
.standortmenu {
position: absolute; z-index: 410; left: 0; right: 0; bottom: 0;
background: var(--tile-2);
--standort-peek: 96px;
/* Deckend, nicht durchscheinend - unter dem Blatt liegt die Karte. */
background: var(--tile-deckend, var(--canvas));
border-radius: 20px 20px 0 0;
box-shadow: 0 -6px 24px rgba(0,0,0,.35);
padding: 0 20px max(18px, env(safe-area-inset-bottom));
transform: translateY(108px);
transform: translateY(calc(100% - var(--standort-peek)));
transition: transform .28s cubic-bezier(.22,.61,.36,1);
}
.standortmenu.offen { transform: translateY(0); }
.standortmenu-griffzone { padding: 10px 0 14px; touch-action: none; cursor: grab; position: relative; }
.standortmenu-griff { width: 36px; height: 4px; border-radius: 2px; background: var(--line-strong); margin: 0 auto; }
.standortmenu-schliessen {
position: absolute; top: 6px; right: 0; width: 30px; height: 30px; border-radius: 50%;
position: absolute; top: 14px; right: 0; width: 32px; height: 32px; border-radius: 50%;
display: flex; align-items: center; justify-content: center;
background: none; border: none; color: var(--fg3); cursor: pointer; padding: 0;
background: var(--tile); border: none; color: var(--fg2); cursor: pointer; padding: 0;
}
.standortmenu-kopf { margin-top: 6px; }
.standortmenu-distanz { font-size: 12px; color: var(--fg3); }
.standortmenu-name { font-size: 17px; color: var(--fg); margin-top: 2px; }
.standortmenu-adresse { font-size: 13.5px; color: var(--fg2); margin-top: 3px; }
.standortmenu-status { display: flex; align-items: center; gap: 9px; margin-top: 16px; font-size: 14px; color: var(--fg); }
.standortmenu-kopf { margin-top: 8px; padding-right: 44px; }
.standortmenu-name { font-size: 17px; color: var(--fg); }
.standortmenu-distanz { font-size: 12.5px; color: var(--fg3); margin-top: 4px; }
/* Abstand als leere Zeile zwischen Kopf und Details */
.standortmenu-adresse { font-size: 13.5px; color: var(--fg2); margin-top: 20px; }
.standortmenu-status { display: flex; align-items: center; gap: 9px; margin-top: 8px; font-size: 13.5px; color: var(--fg2); }
.standortmenu-status svg { color: var(--fg3); flex: 0 0 auto; }
.dot.neutral { background: var(--fg3); }
.standortmenu-werte { display: flex; gap: 30px; margin-top: 16px; }
.standortmenu-werte { display: flex; align-items: center; gap: 10px; margin-top: 8px; font-size: 13.5px; color: var(--fg2); }
.standortmenu-werte svg { color: var(--fg3); flex: 0 0 auto; }
.standortmenu-aktionen { display: flex; gap: 10px; margin-top: 18px; padding-bottom: 4px; }
.standortmenu-aktionen .aktion { flex: 1; margin-top: 0; }
.standort-pille {
display: inline-flex; align-items: center; justify-content: center; gap: 8px;
height: 40px; padding: 0 20px; border-radius: var(--r-pill);
border: none; background: var(--tile); color: var(--fg);
font-family: inherit; font-size: 14px; text-decoration: none; cursor: pointer;
}
.standort-pille.primaer { background: var(--line-strong); }
.standort-pille:active { transform: scale(.97); }
.standort-pille[disabled] { opacity: .4; pointer-events: none; }
/* ------------------------------------------------------- Bilderverwaltung */
.bildgrid { display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--sp-4); margin-top: var(--sp-4); }
@@ -811,6 +841,100 @@ button.tile, .tilebtn { transition: background .15s, transform .1s; }
}
.sheet-abbrechen:active { background: var(--shade); }
/* ------------------------------------------------------- Setup-Popup
Sensor-zu-Entität-Zuordnung (Einstellungen -> Fahrzeug einrichten ->
Setup). Gleiche Catcher/Karten-Bauweise wie .bildmenu/.sdpopup oben,
aber großformatig und scrollbar statt Bottom-Sheet-Größe; nutzt die
bereits vorhandenen sheet-fade/sheet-rein-Keyframes. z-index bewusst
unter .sheet (1200er), damit ein Fehlerhinweis (hinweis()) bei
fehlgeschlagenem Speichern über dem noch offenen Setup-Popup liegt. */
.setup-catcher {
position: absolute; inset: 0; z-index: 1000;
background: rgba(0,0,0,.45);
animation: sheet-fade .18s ease-out;
}
.setup-popup {
position: absolute; z-index: 1001;
left: 10px; right: 10px;
top: max(10px, env(safe-area-inset-top));
bottom: max(10px, env(safe-area-inset-bottom));
/* --tile-2 ist inzwischen transluzent (rgba(255,255,255,.1) im Nacht-
Theme) - fuer ein frei ueber dem Inhalt schwebendes Popup braucht es
einen blickdichten Hintergrund, gleiches Muster wie .standortmenu
oben. */
background: var(--tile-deckend, var(--canvas));
border-radius: 20px;
display: flex; flex-direction: column;
overflow: hidden;
animation: sheet-rein .24s cubic-bezier(.22,.61,.36,1);
}
.setup-kopf {
flex: 0 0 auto;
padding: 18px 20px 14px;
border-bottom: 1px solid var(--line);
display: flex; flex-direction: column; gap: 4px;
}
.setup-kopf strong { font-size: 17px; font-weight: 400; color: var(--fg); }
.setup-kopf span { font-size: 13px; color: var(--fg2); line-height: 1.45; }
.setup-kopf-schalter { display: flex; align-items: center; gap: 10px; margin-top: 10px; }
.setup-kopf-schalter span { font-size: 13.5px; color: var(--fg); }
.setup-koerper { flex: 1 1 0; min-height: 0; overflow-y: auto; padding: 4px 20px 20px; }
.setup-gruppe { margin-top: 18px; }
.setup-gruppe:first-child { margin-top: 6px; }
.setup-gruppe-titel { font-size: 11px; letter-spacing: .1em; text-transform: uppercase; color: var(--fg3); margin-bottom: 6px; }
.setup-feldzeile { padding: 12px 0; border-bottom: 1px solid var(--line); }
.setup-feldzeile:last-child { border-bottom: none; }
.setup-feld-kopf { display: flex; flex-direction: column; gap: 2px; margin-bottom: 8px; }
.setup-feld-label { font-size: 14.5px; color: var(--fg); }
.setup-feld-hinweis { font-size: 12.5px; color: var(--fg2); line-height: 1.4; }
.setup-feld-neustart { font-size: 12px; color: var(--warn); line-height: 1.4; }
.setup-unterfelder { display: flex; flex-direction: column; gap: 12px; }
.setup-unterfeld { display: flex; flex-direction: column; gap: 4px; }
.setup-unterfeld-label { font-size: 12px; color: var(--fg3); }
.entitaet-combo { position: relative; }
.entitaet-combo-icon {
position: absolute; left: 12px; top: 50%; transform: translateY(-50%);
color: var(--fg3); display: flex; pointer-events: none;
}
.entitaet-combo-input {
width: 100%; box-sizing: border-box;
background: var(--tile-deckend, var(--canvas)); border: 1px solid var(--line); color: var(--fg);
font-family: inherit; font-size: 16px; /* siehe .feld input weiter oben: >=16px gegen Safari-Autozoom */
padding: 11px 13px 11px 36px;
border-radius: 12px;
text-align: left;
-webkit-appearance: none; appearance: none;
}
.entitaet-combo-input:focus { outline: none; border-color: var(--red); }
.entitaet-liste {
position: absolute; z-index: 5; left: 0; right: 0; top: calc(100% + 4px);
max-height: 240px; overflow-y: auto;
background: var(--tile-deckend, var(--canvas)); border: 1px solid var(--line-strong);
border-radius: 12px;
box-shadow: 0 12px 28px rgba(0,0,0,.35);
}
.entitaet-eintrag {
display: flex; flex-direction: column; gap: 1px;
width: 100%; text-align: left;
background: none; border: none; border-bottom: 1px solid var(--line);
color: var(--fg); font-family: inherit;
padding: 9px 12px; cursor: pointer;
}
.entitaet-eintrag:last-child { border-bottom: none; }
.entitaet-eintrag:active, .entitaet-eintrag.aktiv { background: var(--shade); }
.entitaet-eintrag-name { font-size: 14px; color: var(--fg); }
.entitaet-eintrag-id { font-size: 11.5px; color: var(--fg3); font-family: ui-monospace, SFMono-Regular, Menlo, monospace; }
.entitaet-leer { padding: 14px 12px; font-size: 13px; color: var(--fg2); }
.setup-fuss {
flex: 0 0 auto;
display: flex; gap: 10px;
padding: 14px 20px max(14px, env(safe-area-inset-bottom));
border-top: 1px solid var(--line);
}
.setup-fuss .aktion { flex: 1; margin-top: 0; }
/* ------------------------------------------------- B3: Push/Pop
Seitenwechsel wie auf iOS: neue Ebene kommt von rechts, zurück geht sie
dorthin. */