Batteriespannungs-Messwertliste, Wisch-Löschen, Reifensatz-Archiv, CI-Icons

Fünf vom Nutzer angeforderte Erweiterungen in einer Runde:

- Batteriespannungs-Kachel bekommt eine Messwertliste (Datum, Uhrzeit,
  Spannung, optional Außentemperatur über den neuen AUSSENTEMP_SENSOR),
  erreichbar über ein neues list-s-Symbol in der Diagramm-Kachel. Werte
  über AGM_RUHE_MAX_V (12,8 V) sind keine Ruhespannung, sondern
  Lichtmaschinenspannung - die Zeile markiert das jetzt mit
  "Generatorspannung" statt es unkommentiert als Messwert auszugeben.
- Jede Zeile der neuen Liste wischbar zum Löschen (neuer Dienst
  batterieverlauf_loeschen), über dieselbe Wisch-Mechanik wie Fahrten/
  Tankvorgänge in beiden Oberflächen.
- Batteriespannungs-Diagramm im Panel war auf großen Bildschirmen (≥860px,
  Spaltenlayout) stark in die Breite gezogen (preserveAspectRatio="none"
  bei fester Höhe) - gedeckelt wie die dort bereits vorhandenen Popups.
- Statistik-Tab-Symbol in beiden Oberflächen durch das Audi-CI-Symbol
  polls-s ersetzt.
- Neue Möglichkeit, einen Reifensatz zu archivieren ("Neue Räder
  anlegen"): der aktuelle Stand (km, Marke, Modell, DOT, Maße, Solldruck,
  Kommentar) wandert in ein Archiv, der laufende Satz beginnt bei 0 km neu.
  Archiv als aufklappbare Übersicht mit voller Bearbeitungsmöglichkeit,
  über drei neue, bewusst nicht über profilSchreiben laufende Dienste
  (reifen_archivieren/-aktualisieren/-loeschen) - aus demselben Grund wie
  reifen_wechseln: kein im Browser gehaltener Stand darf einen
  zwischenzeitlich fortgeschriebenen km-Wert überschreiben.

Live im Testcontainer geprüft (erstmals über :18123 statt :8123 erreichbar)
und dabei zwei echte Layout-Fehler gefunden, die kein Compile-/Testlauf
sehen konnte: die Archiv-Eingabefelder waren durch ein fälschlich
verwendetes .mitEinheit (feste 96px-Breite, eigentlich für Zahl+Einheit
gedacht) abgeschnitten ("Continenta" statt "Continental"), und die
Kilometerzahl in der eingeklappten Archiv-Zeile war klein an das Datum
gequetscht statt wie der Satzname lesbar. Beides behoben, live erneut
bestätigt.

Backend: py_compile clean, services.yaml ergänzt. Panel: node --check
clean. companion-app: tsc/Testsuite (146/146)/Build/OTA-Bündel alle grün.
Manifest 2026.8.25.2 → .8, jeder Neustart im Testcontainer sauber.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-27 11:44:24 +02:00
parent 70253e83a5
commit 17149937a6
23 changed files with 1025 additions and 31 deletions
+144
View File
@@ -2942,6 +2942,150 @@ confirmed mechanism the pending Sicherheit plan already documented. `screening.p
`TankDetail.tsx` were deliberately left untouched: the estimate flows through existing display/edit paths
without needing either to change.
## R. Five owner-requested features in one round: battery measurement list, swipe-delete, diagram-width fix,
CI icon substitutions, wheel-set archiving (2026.8.25.3/.4)
**1. Battery measurement list.** The battery-voltage tile only ever showed a hand-drawn chart. Added a
"list" corner button (CI `list-s`) that opens a new list view — one row per day (the existing daily
min/max data, unchanged storage shape) showing date, time, voltage, and now also outside air temperature.
Backend: new optional `AUSSENTEMP_SENSOR` role (`einstellungen.py`); `batterie.py`'s 5-minute check now also
reads it and passes it to `ablage.batterieverlauf_tageswert_aktualisieren()`, which gained an `aussentemp`
parameter — stored as `min_temp_c`, **only updated when a measurement also sets that day's minimum voltage**
(the temperature is tied to that specific reading, not a daily average). Older entries and setups without
the sensor simply carry `min_temp_c: None` — no backfill, no migration. New screens: companion-app
`BatterieListe.tsx` (route `battliste`, reached from an `IconButton`/`SymbolListe` in `Batterie.tsx`'s
header) and panel `vBatterieliste()` (route `battliste`, reached via a new `.iconbtn`/`ciSVG(CI.listS)`
button in `vBatterieverlauf()`'s tile header).
**2. Swipe-to-delete for the new list, per entry.** Reused the existing pattern in both codebases rather
than inventing a new one: companion-app's `Zeilenmenue` (wipe + accessible menu button, see its own
docstring) and the panel's `swipeZeile()`/`eintragLoeschen()`/`LOESCH_TEXT`/`LOESCH_HINWEIS` machinery
already used for trips/tank vorgaenge, extended with a `"batt"` art. New backend service
`batterieverlauf_loeschen` (field: `datum`) → `ablage.batterieverlauf_eintrag_loeschen()` (new) →
`batterie.eintrag_loeschen()` (new, wired in `dienste.py`).
**3. Battery diagram "still stretched" on a large monitor — panel only (confirmed with the owner; the
diagram in question doesn't exist as a separate wide layout in companion-app).** Root cause found in
`audi-dashboard-ios.css`: past 860px viewport width the whole app switches to a wide two-column layout
(`@container (min-width:860px)`) where `main#view`'s content column is 772px wide (860 2×44px side
padding). The battery chart's SVG (`#bvSvg`, `audi-dashboard-app.js`) is drawn on a fixed `viewBox="0 0 320
170"` with `preserveAspectRatio="none"` and `width:100%` — at 772px wide against a native 320-unit design,
that's a ~2.4× horizontal stretch with the height staying fixed at 170px, turning the chart into an
unnaturally wide, flat band. Fixed the same way the existing popups in that file already solve the identical
problem one section up: `#bvChart{max-width:400px;margin-left:auto;margin-right:auto}` inside the same
`@container` block, no changes to the SVG's own drawing logic (`BV_W`/`BV_H`/`bvZeichnen()`), which section
O of this file already spent real effort getting right for a *different* stretch bug (the Y-axis
rescaling one) — deliberately left untouched here.
**4. CI icon substitutions — `list-l/-s`, `polls-l/-s`, `package-l/-s`, all requested by the owner
(SVG source supplied directly from their local Audi icon export folder, same as every other CI icon in this
codebase — see the recurring "vom Nutzer als SVG geliefert, verbatim uebernommen" comment convention).**
Added to the `CI` catalog in `audi-dashboard-app.js`. `polls-s` replaces the Statistik tab's hand-drawn bar
icon (`ICONS.stat`) in the panel's tab bar — needed adding `"stat"` to `ICONS_VOLL` (the existing mechanism
for filled-vs-outline icon rendering; CI paths carry their own `fill="currentColor"` but would otherwise
also inherit the tab bar's `stroke:currentColor` from `.tab svg`, doubling the line weight around the
shape). Companion-app's `symbole.tsx` had no filled-icon mechanism at all yet (its `SymbolAudi` etc. are
plain stroked outlines, unlike the panel's CI-icon-based tab bar) — added a `voll` prop to the shared
`Rahmen` wrapper (`fill`/`stroke` swap) as the equivalent mechanism, then `SymbolStatistik` (parity with
the panel), plus `SymbolListe`/`SymbolPaket` for the other two icon uses below. `list-s`/`package-s` are the
corner/inline buttons described in points 1 and 5; the `-l` (48-grid) variants are stored for parity with
every other CI icon pair in the catalog but aren't wired to a render path yet, same as the pre-existing
`editL`/`oilChangeL`/etc.
**5. Wheel-set archiving ("Neue Räder anlegen").** A `package-s` button sits between the wheel photo and
the editable Hersteller/Modell fields on the Reifen page in both codebases (panel: inline always-editable
`data-rf` inputs, matching that page's existing convention; companion-app: read-only `Wertzeile`s with a
separate edit form, matching *its* existing convention — no new interaction pattern introduced in either).
Pressing it asks "Neue Räder anlegen?" (panel: the existing `bestaetigen()` action sheet; companion-app: the
existing `window.confirm`-based `bestaetigen()` already used by `wechseln()` on the same page) before doing
anything. On confirmation, `reifen.py`'s new `archivieren(k, satz)`: snapshots the season's *entire* current
record (km, marke, modell, dot, mass, druck_vorne/hinten, kommentar, plus a generated id and today's date as
`ersetzt_am`) into `reifen.archiv` (newest first), then resets the live record — **km to 0 (explicit owner
decision) and marke/modell/dot/kommentar to blank** (a replacement tire hasn't been characterized yet),
**but mass/druck_vorne/druck_hinten/bild carried over** (size and target pressure don't usually change for
a same-fitment replacement — a judgment call, not something the owner specified; flag if wrong). Three new
services (`reifen_archivieren`, `reifen_archiv_aktualisieren`, `reifen_archiv_loeschen`), all deliberately
**bypassing `profilSchreiben`/`profilSpeichern`** for the same reason `reifen_wechseln`/`reifen_km_setzen`
already do (see `reifenzaehler.py`'s own docstring) — a full-profile round trip from a stale browser copy
could clobber a km value the backend fortgeschrieben in the meantime.
Overview UI: an "Archiv" tile at the bottom of the Reifen page, one row per archived set. Companion-app uses
the design system's `Accordion` (title = "Sommerräder"/"Winterräder", summary = "Ersetzt am ... · N km"),
expanding to a read-only `Werteliste` with a "Bearbeiten" button that swaps in an edit form (mirrors
`TankFormular`'s style) — plus a destructive "Löschen" button. The panel reuses its own existing
`acc-h`/`acc-b`/`data-acc` accordion mechanism (same as the trips/tankvorgaenge year/month lists) with
always-inline-editable fields (`data-rfa="id|feld"`, parallel to `data-rf`) that call
`reifen_archiv_aktualisieren` directly on change, plus a `data-reifenarchivloeschen` button.
`profilAdapter.ts` gained a `ReifenArchivEintrag` type and `Fahrzeug.reifen.archiv`; `profilZuFahrzeug()`
reads it straight off the profile (`?? []`), and `zusammenfuehren()` needed no change — it clones the raw
profile first and only ever touches `aktiv`/`anzugsmoment_nm`/`wechsel`/`saetze`, so `archiv` survives
untouched exactly like it already did for every other field this function doesn't know about.
Verified: `py_compile` clean on all six touched backend files (`einstellungen.py`, `ablage.py`,
`batterie.py`, `const.py`, `dienste.py`, `reifen.py`) plus `services.yaml` entries added for all four new
services; `node --check` clean on the panel JS; companion-app `tsc --noEmit` clean and full suite green at
**146/146** (one new test — the existing "every registered page renders with sample data" smoke test picked
up `battliste` automatically); `npm run build` and `npm run ota` both succeeded (OTA bundle rebuilt at
`2026.8.25.4`, sha256 `9d5ebc2a62da4bbddcd766a462726bb51a213ea4095e367e88a8e5d321a9f8e4`). Manifest bumped
`2026.8.25.2``.3` (backend) → `.4` (frontend + OTA), both restarts in `audi_ha_test` confirmed clean via
`"Audi Dashboard ... eingerichtet"` with no tracebacks, including the periodic battery-check log line
firing without error against the new `AUSSENTEMP_SENSOR` code path. **Not verified live in a browser**
the setup-menu entry for `AUSSENTEMP_SENSOR`, the new list screens, the icon substitutions' actual pixel
rendering, and the archive accordion/edit flow were none of them clicked through in this round (a
browser-tooling networking issue blocked reaching the test instance's UI, same blocker noted in section Q).
This entire round rests on static analysis, compiler/test-suite checks, and clean service-registration logs
— not on having seen any of it render.
## S. Live browser access to the test instance, a "Generatorspannung" remark, and two real bugs section R's
static-only verification missed (2026.8.25.5.8)
**The owner logged this session into the test instance's browser at `http://localhost:18123` (not the
`:8123` used earlier - a different, working port for this environment).** This unblocked live verification
for the first time since the FMM003 investigation's `/profile/security` refusal (section P) — worth noting
for future sessions: try `:18123` before assuming the browser tooling can't reach this project's test
container.
**Generator-voltage remark (2026.8.25.5):** `AGM_RUHE_MAX_V = 12.8` (the resting-vs-alternator-voltage
threshold, already used by the diagram's own point filter and the SOC estimate) was not applied to the new
measurement list from section R — a day whose minimum was never actually measured at rest still displayed
next to a date/time as if it were one. Companion-app: exported the constant from `Batterie.tsx`, added a
`Pill` reading "Generatorspannung" in `BatterieListe.tsx` when `min > AGM_RUHE_MAX_V`. Panel: same threshold
already in scope in `vBatterieliste()`, added a `.pill`-styled badge under the date in the `.v` column.
**Live verification then immediately found two real defects that every static check in section R had
missed** — `py_compile`/`tsc`/`node --check`/the test suite all passed because none of them render CSS:
1. **Archive edit fields visually truncated ("Continenta", "SportConta", "275/30 R2").** `vReifenArchiv()`'s
`feld()` helper unconditionally wrapped every input in `<span class="mitEinheit">` — a class meant for
short number+unit pairs (km, Nm, bar) that hard-codes `input{width:96px}`. None of the fields that
actually used this helper (marke/modell/mass/dot/kommentar) needed a unit at all, so the wrapper was pure
accident, not a deliberate width choice. Fixed by dropping `mitEinheit` from that helper entirely,
matching the plain `<input>` the live (non-archived) season fields already use one screen up. Confirmed
live: all three previously-clipped fields now render in full.
2. **Owner follow-up, also confirmed live before and after:** the archive accordion's collapsed summary
line squeezed `4.542 km` into a `<small>` immediately after the "Ersetzt am" date, and separately asked
for the Kommentar/Notiz field specifically to get more room. Both fixed the same way in both codebases:
Kommentar/Notiz now renders label-above-input at full width (`.feld--breit` in the panel CSS; a
bypassed-`Feld` block in companion-app) instead of sharing a row with its label like every other short
field — and the collapsed summary now shows the km figure at title size/color (`.acc-h .k`/
`.ads-acc-h__k`-equivalent styling) with the date dropped to its own smaller line below, instead of the
two being run together.
**Lesson for this project going forward, worth remembering deliberately:** a clean compile/test pass is not
evidence a layout is correct — none of the tooling in this repo renders CSS or checks visual width. Section
R shipped a real, user-visible truncation bug behind a fully green test suite. When browser access is
available (now confirmed reachable at `:18123`), prefer at least one live screenshot of any new or changed
input-heavy screen over relying on compiler output alone.
Verified: panel `node --check` clean at every step; companion-app `tsc --noEmit` clean, suite green at
146/146, `npm run build`/`npm run ota` succeeded. Manifest `2026.8.25.4``.5` (Generatorspannung) → `.6`
(mitEinheit fix) → `.7` (Kommentar width + archive header restyle, panel) → `.8` (same two, companion-app
parity + final OTA rebuild, sha256 `464c294c6c07dcb636a8ea3bd93c7e525b21722defb41f573e2b31fefade49ea`).
Every restart in `audi_ha_test` confirmed clean via log, **and this time also confirmed live in the browser**
at each step from `.6` onward — screenshots taken of the battery list (Generatorspannung pill rendering
correctly, including a real entry below the threshold correctly *not* tagged), the Statistik tab's `polls-s`
icon, and the Reifen archive section before and after both fixes.
---
## Working conventions (observed — keep them)