Wartungsplan-Schema companion-app<->Panel angeglichen; Setup-Feldkopf zeigt zugeordneten Sensor

companion-app nannte Servicebuch-Feldnamen betrieb/notiz, waehrend Panel und
Backend (profil["service"]["buch"]) werkstatt/kosten/arbeiten verwenden -
ohne Uebersetzung dazwischen zeigten in einer Oberflaeche angelegte
Eintraege in der anderen leere Werkstatt-/Kosten-Werte. werkstatt/kosten/
arbeiten als kanonisch uebernommen (aeltere, bereits etablierte Panel-
Konvention); ServicebuchEintrag, das Bearbeitungsformular (inkl. neuem
Kosten-Feld) und die Ansicht in Service.tsx angepasst, den Workaround-Cast
im CSV-Export (DatensatzPopup.tsx) sowie die Testfixture entsprechend
bereinigt.

Setup-Popup (Panel, Item 11 aus der letzten Sammel-Rueckmeldung): die
statische Beschreibung unter jedem Feldnamen ist jetzt die Kennung und der
aktuelle Wert des tatsaechlich zugeordneten Sensors.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-28 10:38:27 +02:00
parent 2be6b99667
commit b1cd03be51
7 changed files with 166 additions and 43 deletions
@@ -4061,6 +4061,42 @@ function sheetMarkup() {
/* ------------------------------------------------------------ Setup-Popup */
const LUPE_SVG = `<svg viewBox="0 0 20 20" width="16" height="16" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round"><circle cx="8.5" cy="8.5" r="6"/><path d="m17 17-4.3-4.3"/></svg>`;
/* Nur der Teil der Entity-ID nach "fmm003_" - die tatsächliche, technische
Kennung des zugeordneten Sensors, kürzer und aussagekräftiger als der
volle "binary_sensor.testcar_b9_fmm003_..."-Pfad. Ohne "fmm003_" im Namen
(z. B. eine andere Marke/Integration) bleibt der Domain-freie Rest
stehen, statt nichts anzuzeigen. */
function entitaetIdKurz(entityId) {
if (!entityId) return null;
const ohneDomain = entityId.includes(".") ? entityId.split(".").slice(1).join(".") : entityId;
const idx = ohneDomain.toLowerCase().indexOf("fmm003_");
return idx >= 0 ? ohneDomain.slice(idx + "fmm003_".length) : ohneDomain;
}
/* Aktueller Wert + Einheit einer Entität, oder null ohne brauchbaren
Zustand (nicht zugeordnet, unavailable/unknown) - dieselbe Zustandslogik
wie in entitaetZeilenMarkup() unten, hier für eine einzelne Entität statt
einer ganzen Kandidatenliste. */
function entitaetWertKurz(entityId) {
if (!entityId || !HASS.states[entityId]) return null;
const zustand = HASS.states[entityId].state;
if (zustand === "unavailable" || zustand === "unknown") return null;
const einheit = HASS.states[entityId].attributes && HASS.states[entityId].attributes.unit_of_measurement;
return `${zustand}${einheit ? " " + einheit : ""}`;
}
/* Ersetzt den bisherigen statischen Beschreibungstext im Setup ("on = Fahrt
läuft", "Optional, vom CAN...") - Nutzerwunsch: statt einer generischen
Erklärung zeigt die Kopfzeile jetzt die Kennung UND die aktuelle Meldung
des tatsächlich zugeordneten Sensors, direkt neben der Feld-Überschrift.
Ohne Zuordnung (leer) gibt es nichts zu zeigen. */
function setupErwarteterSensor(entityId) {
const kurz = entitaetIdKurz(entityId);
if (!kurz) return "";
const wert = entitaetWertKurz(entityId);
return wert != null ? `${kurz} · ${wert}` : kurz;
}
function entitaetZeilenMarkup(feldId, key, suchtext, aktuellerWert) {
const kandidaten = entitaetKandidaten(key, suchtext, aktuellerWert);
if (!kandidaten.length) return `<div class="entitaet-leer">Keine passende Entität gefunden</div>`;
@@ -4096,9 +4132,13 @@ function entitaetCombo(key, idx) {
}
function setupFeldzeile(feld) {
// Bei einem Listenfeld (vier Positionen wie Tür-/Fenstersensoren) gibt es
// keinen EINEN zugeordneten Sensor für die Kopfzeile - jede Position hat
// ihre eigene Zuordnung, siehe die Unterfelder unten.
const erwartet = feld.liste ? "" : setupErwarteterSensor(setupZuordnung[feld.key]);
const kopf = `<div class="setup-feld-kopf">
<span class="setup-feld-label">${esc(feld.label)}</span>
<span class="setup-feld-hinweis">${esc(feld.hinweis)}</span>
${erwartet ? `<span class="setup-feld-hinweis">${esc(erwartet)}</span>` : ""}
</div>`;
if (feld.liste) {
return `<div class="setup-feldzeile">${kopf}
@@ -1255,9 +1255,16 @@ button.leaf:active { background: var(--tile-2); }
.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; }
/* Nutzerwunsch: der zugeordnete Sensor (Kennung + aktueller Wert, siehe
setupErwarteterSensor() in audi-dashboard-app.js) steht NEBEN der
Feld-Überschrift, nicht mehr wie die frühere statische Beschreibung
darunter - deshalb Zeile statt Spalte. */
.setup-feld-kopf { display: flex; align-items: baseline; justify-content: space-between; gap: 10px; margin-bottom: 8px; }
.setup-feld-label { font-size: 14.5px; color: var(--fg); flex: 0 1 auto; }
.setup-feld-hinweis {
font-size: 12.5px; color: var(--fg2); line-height: 1.4; text-align: right;
white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 55%;
}
.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); }
@@ -1,7 +1,7 @@
{
"domain": "audi_dashboard",
"name": "Audi Dashboard",
"version": "2026.8.28.3",
"version": "2026.8.28.4",
"documentation": "https://gitea.nothaft.cloud/paul/audi-app/src/branch/main/README.md",
"issue_tracker": "https://gitea.nothaft.cloud/paul/audi-app/issues",
"codeowners": ["@paul"],