Repository navigation
v0.9.2
Trips grow up: every recorded drive can now be drawn on a map — a route line per
trip and a clustering map of where you actually go — and a trip's start and end
times finally describe the drive rather than the moment the detector noticed it.
Tires gain BMW's wear diagnosis next to pressure on a rebuilt tire card. And the
REST poller stops eating your quota: the daily container now carries the 41
fields the stream cannot, taking normal running from about 36 requests a day
down to 2 — while filling entities that until now could only ever sit empty.
Everything below accumulated across the 0.9.2 pre-releases; upgrading from 0.9.1
gets it all at once.
Added
- Each trip now shows its route on a map. Expanding a trip in the Trips card
view draws that drive on a small map — a single clean line with a start and an
end marker — for trips recorded with Record route on. No map data leaves
your browser to do it. - A destinations map (
view: map) on the dashboard card. A new card view
plots where your trips end as markers that cluster into counted bubbles
when zoomed out and split apart as you zoom in — a quick read on where you go
most, with an honest "times arrived here" count (a trip's start is the previous
trip's end, so plotting both would double-count). A time-window chip row (This
month / 3 months / All) filters it. It reuses Home Assistant's own map and
clustering, so there are no new dependencies, and it reads routes via
get_trips, so it spends no API quota. Destinations appear once Record
route (trip_track) is enabled; the map shows a hint until then. - Tire wear, on a rebuilt tire card (card 1.8.1). BMW's smart-maintenance
tyre diagnosis was being fetched and thrown away —fetch_tyre_diagnosis
logged it and nothing else. It now becomes five entities per car (Tyre
Condition plus one per wheel BMW reports), and the card is a tire card rather
than a tire-pressure card: titled Tires, it opens with the two things
that can actually be wrong with one, side by side — Pressure (the measured
spread across the set, with the shared target under it) and Wear (BMW's
verdict, with the mileage until the soonest wheel is due). Each wheel carries
its own size, tread pattern, season and fitting date beside it, because
staggered setups are normal: the i5 runs 245s at the front and 275s at the
rear, and a single shared line had to show the wrong size for half the car.
Wear outranks pressure in the wheel colour and the header badge — a tyre BMW
flags as worn reads "Check tires" even at perfect pressure. Note that BMW
reports remaining mileage, not tread depth;treadis the tread pattern
("EcoContact 6 Q"). Cars with no tyre service record on file get no tyre
entities and an unchanged card. - Sharper trip start and end on cars that stream the driver door (e.g. the
i5). The driver door brackets the drive: closing it (you got in) anchors where
a trip starts, and opening it again after the car has stopped (you got out)
ends the trip promptly instead of waiting out the 5-minute stationary timer.
Used only as an accelerator — cars that don't stream the door fall back to GPS
exactly as before. - Recorded routes now carry timing. With Record route (
trip_track) on,
each GPS fix in a trip's track is stored with the number of seconds since the
trip started (trackpoints become[lat, lon, t]), so a map can replay a
drive in real time, colour it by pace and show where the car stopped. Only
affects opted-in recording; routes captured before this update keep their
two-element[lat, lon]points and read back without timing (their times
can't be backfilled). - Trip-capture diagnostics (
Configure → Trips). An opt-in troubleshooting
toggle for improving trip detection. When on, the integration logs the raw
detector substrate under greppable tags — every GPS fix with its cadence,
latency, odometer and the close-timer countdown ([trip.gps]), the timer
lifecycle ([trip.timer]), full BMW segment batches ([trip.seg]), a
per-message descriptor firehose ([trip.raw]) and a per-trip post-mortem
([trip.post]) — and writes a replayablebavariandata_trip_capture.ndjson
capture to the config folder. Each[trip.gps]line also carries the GPS fix
state, satellite count and heading (to tell a stopped car from a lost fix), and
a[trip.watch]line surfaces catalogue signals that might drive a better
detector (speed, HV-system and connector state, the ignition trio, driver
door/lock, active navigation) whenever the car streams them. Independent of
Debug logging, off by default, and contains GPS/VIN, so it's meant to be
switched on for a test drive and back off. - Documented how to remove BavarianData completely. Troubleshooting & FAQ has
a new "Removing BavarianData completely" section: deleting the integration
already wipes your tokens, your charging/trip history and the statistics it
published, but the quota log, the cached vehicle image and any trip-capture
file are left behind — and a trip capture contains GPS coordinates, so it's
worth deleting. Also covers the one trap when testing a fresh install: long-term
statistics live in the recorder database, so a partial removal can leave
bavariandata:…series showing up in your Energy dashboard with nothing
installed.
Changed
-
The REST container is polled once a day instead of every 40 minutes, and now
carries everything the stream cannot. The container held 30 descriptors of
which 26 duplicated the stream, and was refetched every 40 minutes — about 36
of the 50 daily requests. It now holds the 41 non-streamable descriptors the
container endpoint can serve (service demands, Condition Based Servicing,
check-control messages, door-lock status, lifetime-consumption counters, state
of health) plus the battery keys for a fast first paint, and refreshes daily.Net effect: from ~36 requests a day to 2 (the container, plus the tyre
diagnosis on its own endpoint), leaving 48 free for manual fetches — while
filling entities that until now could only ever sit empty. Existing installs
migrate on the next token refresh: the old container is deleted and replaced,
rather than left to idle against BMW's 10-container limit. -
A trip's start and end times now describe the drive, not the detection. A
trip was stamped from the first position fix that registered movement to the
moment the five-minute stationary timer expired — so a drive from 10:51 to
10:57 was recorded as 10:54 → 11:00, and its duration overstated by minutes at
both ends. The start now falls back to when the car was last seen parked (on
cars that stream the driver door, bounded to five minutes before detection, so
sitting in the car before pulling away doesn't count as driving), and a
stationary or segment close ends the trip at the last fix that showed movement
instead of when the timer fired. A driver-door arrival still ends the trip
where it always did — the door opening is the arrival. Trips already
recorded keep their old timestamps. -
The tire-pressure band is no longer symmetric: low from 8% under target, high
only past 15% over. The old ±4% flagged every wheel of a perfectly healthy
car — BMW's target is the cold pressure and a tire you have just driven on
reads 8–10% high. Under-inflation is the condition worth an early hint;
over-inflation is only worth one past what warm-up explains. -
The dashboard card is now the "BavarianData Card". It was previously shown
as the "BMW CarData Card" (elementcustom:bmw-cardata-card); the card, its
element (custom:bavariandata-card) and its bundled file have been renamed to
match the integration's name. Existing dashboards keep working: the old
custom:bmw-cardata-cardelement is still registered as a hidden alias, so no
card needs to be re-added. The old name simply no longer appears in the card
picker. -
Refreshed BMW's API reference material from source.
docs/reference/now
carries Integration Guide v1.5 (09/01/2026) and the current Swagger files,
re-fetched from BMW. Notable spec changes: the charging-history session gained
energyDecreaseHvbKwhandenergyDischargedKwhand lost
chargingCostInformation;basicDatagainedreessNominalCapacityGross; the
device-code response no longer documentsverification_uri_complete; and the
streaming chapter now states MQTT QoS 0 only plus a per-IP connection-rate
policy over a one-minute window. No integration behaviour changes. The README
in that folder records the exact URLs to re-fetch from, andtools/README.md
now records the direct Telematics Data Catalogue download URL and spells out
that the catalogue — not the Swagger — is the stream's contract.
Fixed
-
Cards no longer break on a browser reload. Every BavarianData card turned
into a "Configuration error" box after pressing F5 — a hard refresh didn't
help, only closing and reopening the browser did. The card was published as a
frontend module, which Home Assistant renders into the page as a
fire-and-forgetimport(); the frontend's service worker can serve that page
from cache without it, so thebavariandata-cardelement was never defined
and every placed card fell back to the error box
(home-assistant/frontend#18728).
The card is now registered as a proper Lovelace dashboard resource, which is
loaded before any dashboard renders and is version-stamped on every update.
Installations whose dashboard resources are YAML-managed keep the old
behaviour and should add the resource by hand — see
Troubleshooting. -
Trip routes were drawn from mismatched latitude/longitude pairs, putting
every recorded point somewhere the car never was: a right-angle staircase where
each vertex took its latitude from the previous fix and its longitude from the
current one. BMW sends the two coordinates as separate messages, and the pairing
guard compared each coordinate only against its own previous value — so once a
single unpaired message slipped through (the first one after connect always
does, with nothing to compare against), the stale half counted as "advanced"
too and every later fix paired one message behind, permanently. Pairing now
waits for both halves of a fix to actually arrive, and drops BMW's duplicate
redelivery by the fix's own timestamp. The phantom right-angle points also
inflated the measured trip distance, which is now correct too. Routes recorded
before this fix keep their bad points; new drives are correct. -
Routes now start where the car was parked. A trip's track (and its start
place) began at the first point that registered as movement — often a block or
more past the actual start. It is now seeded from the last known parked
position, so the line connects from where the drive really began. -
The tire diagnosis now survives a restart. Wear, tread, size, season and
fitting date were held in memory only, so every Home Assistant restart blanked
the tire sensors and the wear half of the tire card until the next daily
refresh came due — up to 24 hours later — orfetch_tyre_diagnosiswas called
by hand. The last fetched diagnosis is now stored and restored at startup, at
no cost to the API quota. The sensors also carry afetched_atattribute now,
so a day-old reading is recognisable as one. -
The coverage report no longer reports gaps that can never close. BMW's
telematic catalogue marks each field with whether the MQTT stream can carry it
(246 of 295 can), but that column is drawn as a tick glyph rather than text, so
the catalogue generator read it as empty and the flag never existed. As a
result 35 fields BMW cannot stream — tyre diagnosis, vehicle image, state of
health, Condition Based Servicing, door-lock status, the lifetime-consumption
counters — were being requested on the stream and counted as expected by
get_coverage_report, which then listed them as missing on every car, forever.
That is exactly the false alarm the self-test exists to prevent.The flag is now parsed and carried through the pipeline, and non-streamable
fields are excluded from the cluster picker, the portal snippet,
activate_stream_fieldsand the coverage comparison. Entities are unaffected
— several of these fields arrive over REST via a container, so they keep their
entity and simply are not expected on the stream.telematics-fields.mdgained
a Stream column so you can see which is which per field.