Fix broken map rendering, split GPS cleanup, receipt parser and design backlog

Leaflet's stylesheet was appended to document.head, so it never reached the
panel's shadow root: .leaflet-tile{position:absolute} never applied, tiles laid
out as in-flow images (~1040px inside a 190px box) and the marker pane ended up
far below the visible area. That is the real cause of the long-standing
"fragmented Leaflet rendering" finding and of the invisible vehicle pin - every
tile request had actually succeeded. The stylesheet now goes into the shadow
root and is awaited before the map is built.

Also in this round:
- Map tiles are always light (Google-Maps-style), no dark variant at night.
- Vehicle marker uses the real CI poi-car icons (poi-car-l >=34px, poi-car-s
  below), with a white halo so the outline stays readable on tiles.
- Removed the obsolete combined STANDORT_TRACKER field; only the split
  lat/lon sensors remain.
- Receipt upload: widened the try block so base64/save failures surface, and
  the frontend call site now reports a rejected service call.
- Generic receipt parser: total detection is line-based (letter-spaced
  headings, no more matching the SUMME-EUR column header, tax lines excluded),
  address heuristic handles 4-digit postcodes and single-line address blocks,
  and "Preis/L" matches without a spelled-out "Liter".
- Design backlog: red hairline frame on list rows (delete button bled through
  at fractional row heights) and select fields now use the grey background box.

Shell 10-receipt regression suite still passes; all changes verified live in
the audi_ha_test container.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-16 12:46:48 +02:00
parent 25d1cebd12
commit 39da68cd87
11 changed files with 328 additions and 133 deletions
+112 -3
View File
@@ -6,8 +6,14 @@ oversized toggle switches, and fixed a desktop-layout audit (settings button / r
overflowing past the capped content column) — see `DESIGN_AUDIT_2026-08-13.md`; 2026-08-16: fixed
five user-reported bugs — removed Türschloss/Haubenschloss checks, fixed a translucent confirm-
sheet, added a flespi-style split lat/lon location source, hardened the "Lädt …" bootstrap race,
and added a generic (station-independent) fuel-receipt parser fallback — see section C). This file
is the entry point for every new agent
and added a generic (station-independent) fuel-receipt parser fallback; later the same day: removed
the now-unused combined `STANDORT_TRACKER` field entirely (flespi never provides it), closed a real
silent-failure gap in the receipt-upload error path, and replaced the vehicle's map marker with a
two-tone pin+car icon — see section C; a third round the same day found and fixed the long-standing
"fragmented Leaflet rendering" defect at its root — the Leaflet stylesheet was loaded into
`document.head` and therefore never reached the panel's shadow root — plus light-only map tiles,
the CI `poi-car` marker, the red frame on list rows, the select-field grey box, and two more
generic-receipt-parser gaps). This file is the entry point for every new agent
session: what this repo is, what is finished, what is missing, and how to work here. Detail lives in
the linked documents — this file points, it does not duplicate.
@@ -150,7 +156,9 @@ the `design/` export's RS-6 error extends into the technical sample data (73 l t
FIN series 4G). Four browser-verified rendering defects: switches stretched by
`.feld label{flex:1 1 auto}` (state display becomes ambiguous), double "offen" in the trip list,
empty oil-change tile when the service book is empty, and fragmented Leaflet rendering in all
three maps (verify on a real device). Positives: the 300/400 font-weight rule and de-DE
three maps (**fixed 2026-08-16** — root cause was Leaflet's stylesheet being loaded into
`document.head`, which never reaches the panel's shadow root; see the third-round entry in
"Open items"). Positives: the 300/400 font-weight rule and de-DE
formatting hold everywhere; `design-system/` is verifiably brand-free.
### HA panel — known gaps (most also in SPECIFICATION.md §7)
@@ -666,6 +674,107 @@ wraps the web app for iPhone; a PWA home-screen install is the accepted intermed
card by deliberately triggering it (this container's limited entity set causes a real
duplicate auto-suggestion); pyscript reload clean, no new console errors beyond the
pre-existing placeholder-image 404s. Synced to `installationspaket/`.
- [x] Follow-up round the same day (2026-08-16), three more items:
(1) **Removed `STANDORT_TRACKER` entirely** — user confirmed the combined device_tracker
field is no longer needed (flespi only ever provides the split lat/lon sensors added earlier
that day). Deleted the field from `einstellungen.py` and `entitaeten.py`'s Setup catalog, and
simplified `_standort()` in `frontend_veroeffentlichung.py` down to the single split-sensor
path (no more two-branch fallback). `overrides_schreiben()`'s existing "only known keys
survive" behavior (see its docstring) meant no explicit migration was needed for stale
`STANDORT_TRACKER` entries already sitting in `data/entitaeten.json` — confirmed live: this
container's own leftover override file still had `STANDORT_TRACKER` from earlier testing
(plus stale `TUERSCHLOSS_SENSOREN`/`HAUBENSCHLOSS_SENSOR` from before finding (1) of the
previous entry), all silently dropped on the next Setup save, which is exactly why the GPS
tile was still showing "Kein GPS-Signal" after the user's own remap attempt earlier — that
attempt happened to save while the old combined field was still in the catalog, so nothing
ever actually persisted `STANDORT_LAT_SENSOR`/`STANDORT_LON_SENSOR`. Redid the Setup save
live (mapped both to `sensor.testcar_b9_fmm003_testintegratoin_lat/longitude_coordinate_value`)
and confirmed the Übersicht/fullscreen Standort map switched from the "no signal" placeholder
to a real rendered map at the correct coordinates (South Tyrol terrain, "Burgleralm" label
visible).
(2) **Fuel-receipt "silently does nothing", root cause found** — reproduced the exact
real upload flow (button click → real `<input type=file>` → synthetic `File`/`DataTransfer`
dispatch → `FileReader``hass.callService`) via browser instrumentation: the happy path
works completely (backend parses, publishes `pyscript.audi_dashboard_beleg_ergebnis`, frontend
fills the form) — `belegverarbeitung.py`'s existing `try/except` around `_parser_aufrufen()`
already publishes a visible `fehler` for parser failures specifically. The actual gap:
`base64.b64decode()` and `_pdf_speichern()` in `audi_dashboard_beleg_hochladen()` were
*outside* that try block — any failure there (corrupted upload, disk full, ...) raised an
unhandled exception with zero UI feedback, since `serviceRufen()`'s generic `.catch()` only
does `console.error()`. Widened the try block to cover all three steps. Also stopped using
`serviceRufen()` for this one call site specifically (`audi-dashboard-app.js`, the
`eingabe.onchange` handler) — it now calls `HASS.callService()` directly with its own
`.catch()` that sets `belegFehler` and re-renders, so a rejected service call (network,
timeout — anything that bypasses the backend's own state-publish) is visible too, not just
logged. Verified against the real 10-receipt Shell suite (still passing) both before and
after this change.
(3) **Vehicle map marker replaced with a two-tone pin+car icon** — added `CI.pinCar` (a
balloon/pin path + white badge circle + a simplified car-front glyph built from rects, not
circles, after an isolated-DOM-injection test round showed a circles-for-headlights version
read as a face/animal rather than a car) to `audi-dashboard-app.js`, replacing the single-color
`poiCar` teardrop previously used by `fahrzeugMarkerSVG()`. Updated both marker call sites'
`iconAnchor` to the new pin's actual tip position and simplified `.fahrzeug-pin` in
`audi-dashboard.css` (no more `color:var(--red)`/white-halo-filter hack, since the new icon
carries its own fixed colors). Verified the icon markup renders correctly via an isolated
DOM-injection test (bypassing Leaflet); could **not** get a final on-map screenshot in
`audi_ha_test` — the Standort map tiles themselves only ever partially load in this container
(reproduced on a fully fresh reload, unrelated to this change), consistent with a previously
documented network limitation of this specific sandbox (see the Standort-Kachel entry above:
reverse-geocoding via Nominatim had the same kind of container-specific network issue).
Structurally verified only; needs a look on a real device/network to confirm final visual
placement.
All three deployed to `audi_ha_test` (version `1786758000`) and synced to `installationspaket/`.
- [x] Third round the same day (2026-08-16) — **the "fragmented Leaflet rendering" defect is solved
at its root**, plus four smaller items:
(1) **Leaflet CSS never reached the shadow root.** `leafletLaden()` appended
`leaflet.min.css` to `document.head`, but the panel renders inside `this.shadowRoot` — and
document stylesheets do not cross a shadow boundary. Inside the panel, `.leaflet-tile
{position:absolute}` and friends therefore never applied: the tiles laid themselves out as
ordinary in-flow `<img>` elements (two per row, ~1040 px of stacked height inside a 190 px
box), so only a thin strip of map was ever visible, and the marker pane ended up ~1009 px
below the visible area — which is exactly why the new vehicle pin appeared "missing". This
also explains the earlier misdiagnosis as a container/network limitation: every tile request
succeeded (`complete:true`, `naturalWidth:512`), the geometry was the problem. Fixed by
injecting the stylesheet into `ROOT` and awaiting it before the map is built; the script tag
stays in `document.head` (it must, `window.L` is global). The CSS injection is deliberately
re-checked on every `leafletLaden()` call rather than guarded by the `window.L` check, because
a HA panel remount produces a fresh shadow root while `window.L` is already set. Verified live:
both the Übersicht preview map and the fullscreen Standort map now fill their containers and
show the pin at the vehicle position.
(2) **Maps are now always light.** `TILES[theme]` switched to CARTO `dark_all` in Nacht mode;
replaced by a single themeless `TILE_URL` (`light_all`), matching how Google Maps & co. keep
the standard road map light regardless of app theme.
(3) **Vehicle marker now uses the real CI `poi-car` icons** (`poi-car-l.svg` / `poi-car-s.svg`
from the delivered icon set) instead of the hand-built two-tone pin from the previous round:
`CI.poiCarL` (48-grid) at ≥34 px, the existing `CI.poiCar` (24-grid) below, anchors recomputed
per variant. Since these are single-color outline forms in `currentColor`, `.fahrzeug-pin`
regained a stacked white drop-shadow halo so the outline stays readable on map tiles.
(4) **Red partial frame on Fahrten/Tanken list rows** (open since the 2026-08-13 design audit,
finding 3) — reproduced and confirmed by recoloring the delete button live. Row heights are
fractional (66.28 px), so `.swipe-content`'s edges miss the device-pixel grid and the red
`.swipe-delete` behind it bled through as a hairline around every row. Fixed by only painting
the delete button while a swipe is actually happening: `.swipe-delete{visibility:hidden}` plus
a `wischt` class set on the first movement of the gesture (and the existing `swiped` class for
the open row). Verified the full gesture still works — button appears at the first pixel of
drag and the row settles open at 84 px.
(5) **Select fields now show the grey background box** (design audit finding 1) — replaced
`.feld select{color:var(--ios-tint)}` with `background:var(--ios-fill);padding:8px 10px`,
matching `.mitEinheit input` ("Pause bis [15] Minuten"), as the user asked. Verified on
Modell / Ölwechsel-Intervall / Bildposition; free text fields stay plain, so the box now
genuinely marks "there is a choice here".
(6) **Generic receipt parser: total and station name** — a real Austrian non-Shell receipt
returned the right litres but 6,00 € and no station. Two causes, both fixed: the free-running
total pattern matched the *column heading* `SUMME-EUR` and read the next line's article number
as the amount, and bon printers letter-space headings (`G E S A M T BETRAG EUR: 92,60`), which
the keyword never matched. Total detection is now line-based — keyword checked against the
whitespace-stripped line, amount must be a real money value (`_GELD`, decimals required), tax
lines (`MWST`/`UST`/`VAT`/`STEUER`/`NETTO`) excluded, largest remaining candidate wins. The
address heuristic no longer demands three fixed lines with a 5-digit postcode (AT uses 4
digits, and this receipt carries `SHELL TANKSTELLE, 6450 SÖLDEN` on one line): it finds the
first postcode line in the header and takes the name from the line above. `Preis/L` without a
spelled-out "Liter" now matches too. Result on that receipt: 42,13 l · 92,60 € · 2,198 €/l ·
no discount · `AUTO B. FRISCHMANN GMBH`. The 10-receipt Shell regression suite still passes.
All deployed to `audi_ha_test` (version `1786765000`) and synced to `installationspaket/`.
- [ ] Fix remaining documentation drift (statistics claim, README gaps, obsolete TODO comment) —
text-only changes; INSTALL.md's WLAN/TommiG1 drift and stale variable names were fixed
2026-08-12 (see section B); `DESIGN_REVIEW_2026-08-13.md` and `REVIEW_main_2026-08-13.md`