Vollaudit: sechs echte Fehler in Backend, Panel und companion-app behoben
- companion-app Batterie.tsx: Generatorspannung zeigte sich faelschlich als Ruhespannung/verzerrte den SOH-Trend, Panel-Filter (AGM_RUHE_MAX_V) fehlte - Manuell angelegte Fahrten/Tankvorgaenge trugen naive Zeitstempel und wurden von der Import-Dublettenpruefung stillschweigend uebersprungen; neue gemeinsame zeit_normalisiert() in verlauf.py schliesst die Luecke - Batteriespannungsverlauf fehlte im Backup (sicherung.py) - Tankvorgangs-Import verlor stillschweigend aeltere Tankvorgaenge vor Beginn der Litersensor-Historie; laeuft jetzt zweigleisig (Prozent + Liter) - Panel-Statistikseite behauptete faelschlich, der Verbrauch je Fahrt komme vom Fahrzeug (OBD) - ist eine Naeherung aus dem Tankfuellstand - companion-app Statistik.tsx: Arbeitsweg-Segment war noch rot statt neutral Version 2026.8.27.19, OTA-Buendel neu gebaut, im Testcontainer verifiziert. Co-Authored-By: Claude Sonnet 5 <[email protected]>
This commit is contained in:
@@ -3770,6 +3770,93 @@ verified, deployed fixes can vanish in one click.** (`git push` needed one retry
|
||||
already-documented Gitea Credential Manager flakiness - consistent with every other push this project has
|
||||
made.)
|
||||
|
||||
## Y. Full cross-codebase bug audit: six real defects found and fixed (2026.8.27.19)
|
||||
|
||||
Owner asked for a general audit of the app, not tied to a specific report. Ran three parallel reviews
|
||||
(backend Python, panel JS, companion-app) rather than one pass, each grounded in reading the actual current
|
||||
code rather than trusting this file's own history. Six real, concrete defects survived; three items the
|
||||
reviews raised as *possible* concerns were checked and confirmed correct as-is (documented below so they
|
||||
aren't re-flagged). No crash-class bugs (undefined-reference errors, the historical `bvRuhePunkte` class of
|
||||
bug) were found anywhere - confirmed via ESLint `no-undef` over the whole panel file plus manual review.
|
||||
|
||||
**1. (High) companion-app's `Batterie.tsx` showed generator voltage as "Ruhespannung" and skewed the SOH
|
||||
trend.** The panel's `bvPunkte` (the single, already-filtered `<= AGM_RUHE_MAX_V` array used for the
|
||||
headline, trend, and diagram alike - see section X's `.16`/`.17` history) was never mirrored here:
|
||||
companion-app's `messwerte` had no such filter, so the newest day's minimum reading - even a generator
|
||||
reading above 13.0 V - could show as the "Ruhespannung" headline, and the trend average wasn't excluding
|
||||
those days either. Fixed with the identical one-line filter the panel already applies:
|
||||
`verlauf.filter((t) => t.min != null && t.min <= AGM_RUHE_MAX_V)` in `Batterie.tsx` - this one array already
|
||||
drives the headline, the trend, and the diagram, so a single filter fixes all three. `BatterieListe.tsx`
|
||||
(the measurement list) is correctly left unfiltered, exactly like the panel's `vBatterieliste()` - it tags
|
||||
generator readings with a pill instead of hiding them, which is a different, already-correct concept.
|
||||
|
||||
**2. (High) Manually-entered trips/refuels could be silently duplicated by a later import.**
|
||||
`historienimport.py`'s own docstring promises every import-created record is checked for overlap against
|
||||
*all* existing ones, "egal ob live erkannt, von Hand angelegt oder aus einem früheren Import" - but the
|
||||
manual-entry paths (`fahrterkennung.manuell_anlegen()`/`aktualisieren()`, `belege.tankvorgang_manuell()`/
|
||||
`tankvorgang_aktualisieren()`) stored whatever timestamp the frontend sent verbatim via a raw
|
||||
`datetime.fromisoformat()`, while `historienimport.als_zeit()` normalizes the same kind of input (naive =
|
||||
local time, per its own comment) to a timezone-aware UTC datetime. `_ueberschneidet()`'s overlap check and
|
||||
the tank-vorgänge dedup window both explicitly skipped any record with a naive timestamp - so every
|
||||
manually-created trip or refuel was invisible to a later import's dedup, and could be re-created as a
|
||||
duplicate. Fixed by moving the normalization into a shared function, `zeit_normalisiert()` in `verlauf.py`
|
||||
(a leaf module with no local imports, chosen specifically to avoid a circular import between
|
||||
`fahrterkennung.py`/`belege.py` and `historienimport.py`) - `historienimport.als_zeit` is now an alias for
|
||||
it. Every write path (`manuell_anlegen`, `aktualisieren`, `tankvorgang_manuell`, `tankvorgang_aktualisieren`)
|
||||
now normalizes through it before storing. `_ueberschneidet()`/`_tankvorgaenge_importieren()`'s
|
||||
`bekannte_zeiten` collection were also changed to run existing stored timestamps through the same function
|
||||
instead of skipping naive ones - this also correctly protects any already-stored legacy record with a naive
|
||||
timestamp from before this fix, not just new writes.
|
||||
|
||||
**3. (Medium) Battery voltage history had no backup coverage.** `sicherung.py`'s `DATEIEN` tuple (used by
|
||||
"Backup jetzt erstellen") listed `fahrzeugprofil.json`/`fahrten.jsonl`/`tankvorgaenge.jsonl`/
|
||||
`entitaeten.json` but not `batteriespannung.jsonl` - even though `batterie.py`'s own docstring frames it as
|
||||
a deliberately multi-year dataset for detecting battery aging. Added it to `DATEIEN`; `_kopieren()` already
|
||||
skips any file that doesn't exist, so this is zero-risk for an install with no battery history yet.
|
||||
|
||||
**4. (Low/Medium) Tank-vorgang import was all-or-nothing per sensor once `TANK_LITER_SENSOR` was mapped.**
|
||||
`_tankvorgaenge_importieren()` used to read *only* the liter sensor's history once one was mapped, silently
|
||||
losing any refuel from before the liter sensor's own recorder history started (e.g. it was mapped
|
||||
mid-project, per section Q) even though the percent sensor's own history covered that period. Rewrote the
|
||||
function to run both passes when a liter sensor is mapped: the percent-based pass now covers the period
|
||||
strictly *before* the liter history's first point, and the liter-based pass (more accurate, since it's
|
||||
already in liters) covers everything from there on - both share the same `bekannte_zeiten`/`neue` state so
|
||||
the same physical refill can't be created twice if the two histories overlap. Unmapped-liter-sensor
|
||||
behavior (pure percent-based import) is unchanged.
|
||||
|
||||
**5. (Moderate) Panel's Statistik page mislabeled a tank-based estimate as vehicle-reported data.** The
|
||||
"Vom Fahrzeug gemeldet, je Zeitraum" tile and its caption claimed `t.verbrauch_l_100km` (per-trip fuel
|
||||
consumption) came from the vehicle's own OBD reporting - it doesn't; section V documented that this field
|
||||
is entirely a backend-computed approximation from `TANK_LITER_SENSOR` liter deltas, and the trip-detail
|
||||
page already correctly labels it "Näherung aus Tankfüllstand". Only the Statistik tile and a stale code
|
||||
comment (`fahrtVerbrauch`'s own header) still claimed OBD provenance. Fixed both to say what the value
|
||||
actually is - a text-only change, the underlying computation was already correct.
|
||||
|
||||
**6. (Low) companion-app's "Art der Fahrten" chart still colored the Arbeitsweg segment red.** The panel
|
||||
fixed this exact chart on 2026-08-13 (section C, the iOS-overlay red-semantics-inversion finding: red is
|
||||
reserved for destructive actions, this segment is a neutral data category) but companion-app's
|
||||
`Statistik.tsx` never received the port - its Arbeitsweg segment stayed `var(--red)` while its sibling
|
||||
Privat segment already used the neutral `var(--fg3)`. Changed to `var(--fg)`, matching the panel's own
|
||||
Arbeitsweg color exactly (not `--fg3`, which the panel reserves for the Privat segment).
|
||||
|
||||
**Checked and confirmed NOT a bug** (raised by one of the three reviews, verified against current code):
|
||||
`AGM_RUHE_MAX_V`, `NEBENWERT_MAX_ABSTAND_S=300`, `_letzte_gueltige_spannung`, the `ablage.py` temperature
|
||||
backfill branch, `historienimport.py`'s `aussentemp` fetch, and `batterie.py`'s freshness check - all
|
||||
already correct per sections T/W/X's history; the route/back-navigation tables (`ZURUECK` vs. the panel's
|
||||
dispatcher) - a full cross-check found every route has exactly one matching entry both ways; and the panel's
|
||||
`ereignisseVerdrahten()` listener wiring - guarded by the existing idempotency lock, no double-registration
|
||||
risk.
|
||||
|
||||
Verified: all five touched backend files (`verlauf.py`, `historienimport.py`, `fahrterkennung.py`,
|
||||
`belege.py`, `sicherung.py`) `py_compile` clean in `audi_ha_test`; panel `node --check` clean; companion-app
|
||||
`tsc --noEmit` clean and full suite green at 146/146. Manifest bumped to `2026.8.27.19` (panel text
|
||||
changed), `npm run ota` rerun (companion-app source changed, `Batterie.tsx`/`Statistik.tsx`), `audi_ha_test`
|
||||
restarted and confirmed clean via log (`Audi Dashboard 2026.8.27.19 eingerichtet`, no traceback; the only
|
||||
`ReferenceError: bvRuhePunkte` lines in the post-restart log predate the setup-complete line and come from a
|
||||
stale browser tab's cached pre-restart script, not a new regression). **Not yet committed/pushed** - per
|
||||
this file's own binding lesson from earlier the same day (section W), that should happen before any
|
||||
self-update/install run is suggested or allowed; ask the owner before running one.
|
||||
|
||||
---
|
||||
|
||||
## Working conventions (observed — keep them)
|
||||
|
||||
Reference in New Issue
Block a user