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
+79 -9
View File
@@ -4001,15 +4001,11 @@ Archiv button and "Montiert" share one Y-center exactly, with a visible backgrou
derived calculation, not read from any vehicle sensor. Owner pointed out a real FMM003 sensor,
`sensor.testzone_fmm003_calculated_distance_covered_after_the_latest_refueling` (device-side, presumably
more accurate/authoritative than our own subtraction), that reports this directly and "can be used instead
of the calculation" - not yet wired in, would mirror the `TANK_LITER_SENSOR` precedent (section Q): new
optional sensor role, preferred over the calculation when mapped, calculation stays as the fallback when
unmapped. Owner to confirm before this is built.
- **Item 9: done on the panel, see section AC below - companion-app port still pending** (parity rule).
- **Item 11**: Setup-Menü sensor role fields - replace the current description text under each field's
headline (e.g. "on = Fahrt läuft", "Optional, vom CAN...") with the live sensor's current output value and
unit inline next to the headline (`[on/off]`, `[%]`, `[l]`, `[km]`...), and show the matched entity's ID
suffix (the part after `fmm003_...`) as "the expected sensor" - not yet investigated in the code
(`entitaeten.py`'s `FELDER` catalog / the Setup popup's row-rendering in the panel).
of the calculation" - **done, see section AC below** (`TANK_DISTANZ_SENSOR`).
- **Item 9: done, both codebases** - panel in section AC, companion-app port in section AD.
- **Item 11: done, panel only (no companion-app equivalent, see section AF)** - Setup-Menü sensor role
fields now show the mapped sensor's ID suffix and live value next to the headline instead of a static
description.
## AC. Item 8 built (TANK_DISTANZ_SENSOR); Item 9 built on the panel: "Datensatz sichern/laden" (2026.8.28.3)
@@ -4147,6 +4143,80 @@ data" smoke test, which covers `Einstellungen.tsx`), `vite build` succeeds. **No
running companion-app session with backend auth this round, the same structural limitation noted repeatedly
elsewhere in this file for companion-app-only changes.
## AE. Wartungsplan schema mismatch between panel and companion-app fixed (2026-08-28)
The pre-existing schema mismatch flagged in section AD above (companion-app's `ServicebuchEintrag` naming
`betrieb`/`notiz` while the actual stored data — `profil["service"]["buch"]`, written by both the panel and
the backend — uses `werkstatt`/`kosten`/`arbeiten`, with no translation layer anywhere) is now fixed.
Decided `werkstatt`/`kosten`/`arbeiten` as canonical, per section AD's own reasoning: the panel's naming is
the older, already-established convention, and real installations' stored data already uses it.
`companion-app/src/screens/Service.tsx`: `ServicebuchEintrag` renamed to `werkstatt?: string` /
`kosten?: number | null` / `arbeiten?: string`. The add/edit form's "Betrieb"/"Notiz" fields became
"Werkstatt" / a new "Kosten" (€) field / "Durchgeführte Arbeiten" (matching the panel's own three-field
layout and its "Durchgeführte Arbeiten" label exactly, not just the field name) — companion-app previously
had no way to record cost on a Wartungsplan entry at all, this closes that gap too. The read-only entry
display gained matching `Wertzeile`s (`Werkstatt`, `Kosten` — "noch offen" when unset, matching the panel's
placeholder wording — `Durchgeführte Arbeiten`), replacing the old `Betrieb`/`Notiz` rows.
`DatensatzPopup.tsx`'s Wartungsplan CSV export (built the same day as the bug was found, section AC/AD) had
worked around the mismatch with an `as Record<string, unknown>` cast and an explanatory comment reading the
real field names directly; both are now redundant and removed — it reads `e.werkstatt`/`e.kosten` through
the corrected type like any other field.
`src/tests/beispieldaten.ts`'s two Wartungsplan fixture entries updated from `betrieb` to `werkstatt`
(no `kosten`/`arbeiten` needed adding — the existing tests don't assert on them). No dedicated
`Service.test.ts` exists; grepped the whole `src/` tree for other `betrieb`/`notiz` consumers before and
after — `Einstellungen.tsx`/`Uebersicht.tsx` only pass the raw `buch` array through as `ServicebuchEintrag[]`
without touching individual field names, so neither needed a change.
Verified: `tsc --noEmit` clean, full suite green at 146/146 (no new tests needed — a schema/naming
alignment, not new behavior), `vite build` succeeds. **Not live-verified against a real backend** — same
structural limitation as section AD immediately above (no running companion-app session with backend auth
this round). The underlying stored data was never wrong (the panel and backend always used
`werkstatt`/`kosten`/`arbeiten`); this was a companion-app-only frontend read/write bug, so no data migration
is needed — an existing Wartungsplan entry will display correctly the next time companion-app is rebuilt and
deployed with this fix.
## AF. Item 11: Setup-Menü zeigt jetzt den zugeordneten Sensor statt einer statischen Beschreibung (2026.8.28.4)
Panel only - the Setup menu (sensor-role mapping) is an HA-panel-only concept, no companion-app equivalent
exists to port this to (companion-app consumes the backend's already-resolved state, it has no Setup UI at
all - see the parity rule's own carve-out for this exact case).
Every field row in Setup used to show a static, developer-written description under the label ("on = Fahrt
läuft, erkennt Fahrtbeginn/-ende.", "Optional, vom CAN..."). Owner: replace it with something read from the
*actual* currently-mapped sensor instead - its technical ID (just the part after `fmm003_`, e.g.
`testintegratoin_engine_ignition_or_acc_status` instead of the full
`binary_sensor.testcar_b9_fmm003_testintegratoin_engine_ignition_or_acc_status`) and its live output value
with unit (`[on/off]`, `[%]`, `[l]`, `[km]`...), positioned next to the headline rather than stacked below it.
New `entitaetIdKurz(entityId)` (strips the domain prefix, then finds `"fmm003_"` case-insensitively and
returns everything after it - falls back to the domain-free ID if `"fmm003_"` isn't present, for other
brands/integrations) and `entitaetWertKurz(entityId)` (current `state` + `unit_of_measurement`, `null` for
`unavailable`/`unknown`/unmapped - the exact same zustand logic `entitaetZeilenMarkup()` already used for
the search-result list, just for one entity instead of a whole candidate list). `setupErwarteterSensor()`
combines both into one string (`"<id-suffix> · <value> <unit>"`), used in `setupFeldzeile()`'s header in
place of the old `feld.hinweis` read. List-type fields (four positions, e.g. door/window sensors) show
nothing in the shared header - there's no single "the" sensor to reference there, each position has its own
mapping and its own combo below.
`feld.hinweis` itself is untouched in the backend `FELDER` catalog (`einstellungen.py`) - still valid data,
simply no longer read at this one call site; not removed, since removing it wasn't asked and nothing else
depends on knowing it's now unused.
CSS: `.setup-feld-kopf` was `flex-direction: column` (label above the old description) - changed to a row
(`justify-content: space-between`) so the new content sits beside the label as requested, with
`text-overflow: ellipsis` on the right-hand span since a long ID-suffix-plus-value string won't always fit.
Verified live in `audi_ha_test` (not just read from source - this file's own section S lesson): four real
rows read via the shadow DOM after opening Setup for real (`ZUENDUNG_SENSOR`
`"testintegratoin_engine_ignition_or_acc_status · off"`, `KM_SENSOR` → `"...mileage_read_from_can · 21302
km"`, etc.) - label and the new span measured on the identical Y-coordinate (`sameRow: true`), confirming
the row layout actually changed, not just the text content. Confirmed a list field (`TUER_SENSOREN`,
"Türen") renders with no header span at all, as designed. `node --check` clean, manifest bumped to
`2026.8.28.4`, `audi_ha_test` restarted and confirmed clean via log.
---
## Working conventions (observed — keep them)