Turn your BMW CarData stream into native Home Assistant entities. This integration subscribes to the BMW CarData MQTT stream (or an optional custom MQTT broker), keeps the token fresh automatically, and creates sensors/binary sensors for every descriptor that emits data.
Note: This entire plugin was generated with the assistance of AI to quickly solve issues with the legacy implementation. The code is intentionally open—to-modify, fork, or build a new integration from it. PRs are welcome unless otherwise noted in the future.
Tested Environment: Home Assistant 2025.3+ is required. Brand logos are included since HA 2026.3 via the
brand/directory.
Not required but appreciated :)
Please try to post only issues relevant to the integration itself on the Issues and keep all the outside discussion (problems with registration on BMWs side, asking for guidance, etc)
On the integration main page, there is now a "Configure" button. You can use it to:
- Refresh authentication tokens (will reload integration, might also need HA restart in some problem cases)
- Start device authorization again (redo the whole auth flow)
- MQTT Broker (switch stream source to a custom broker, including TLS mode and topic prefix)
- Reset telemetry container (delete and recreate the BMW telemetry container)
- Clean up orphaned entities (remove stale entities that no longer receive data)
- Settings (toggle optional features like Magic SOC, Charging History, Tyre Diagnosis, trip-end polling cooldown)
And manual API calls, these should be automatically called when needed, but if it seems that your device names aren't being updated, it might be worth it to run these manually.
- Initiate vehicles (API) (fetch all vehicle VINs on your account and create entities)
- Get basic vehicle information (API) (fetches vehicle details like model, series, etc. for all known VINs)
- Get telematics data (API) (fetches telematics data from the CarData API)
Note that every API call here counts towards your 50/24h quota!
The CarData web portal isn’t available everywhere (e.g., it’s disabled in Finland). You can still enable streaming by logging in by using supported region. It doesn't matter which language you select - all the generated Id and configuration is shared between all of them.
DO Steps 1-3 First before installing it in HACS
- https://www.bmw.co.uk/en-gb/mybmw/vehicle-overview (in English)
- https://www.bmw.de/de-de/mybmw/vehicle-overview (in German)
- https://mybmw.bmwusa.com/ (USA we need testers or temp access)
- https://www.mini.co.uk/en-gb/mymini/vehicle-overview (in English)
- https://www.mini.de/de-de/mymini/vehicle-overview (in German)
-
Select the vehicle you want to stream.
-
Choose BMW CarData or Mini CarData.
-
Generate a client ID as described here: https://bmw-cardata.bmwgroup.com/customer/public/api-documentation/Id-Technical-registration_Step-1
-
Under section CARDATA API, you see Client ID. Delete the original one and make a new one. Copy this new one to your clipboard because you will need it during Configuration Flow in Home Assistant. **Don't press the button Authenticate device (NEVER) **!!!!
-
Request access to CarData API first:
- Click "Request access to CarData API"
- ⏱️ Wait 60 seconds (BMW needs time to propagate permissions) Note, BMW portal seems to have some problems with scope selection. If you see an error on the top of the page, reload it, select one scope and wait for 120 seconds, then select another one and wait again.
-
Then request access to CarData Stream:
- Click "Request access to CarData Stream"
- ⏱️ Wait another 60 seconds
Why? BMW's backend needs time to activate permissions. Rushing causes 403 errors.
-
Scroll down to CARDATA STREAMING and press Configure data stream and on that new page, load all descriptors (keep clicking “Load more”).
-
Manually check every descriptor you want to stream or optionally to automate this, open the browser console (F12) and run:
document.querySelectorAll('label.chakra-checkbox:not([data-checked])').forEach(l => l.click());- If you want the "Predicted SOC" helper sensor to work, make sure your telematics container includes the descriptors
vehicle.drivetrain.batteryManagement.header,vehicle.drivetrain.batteryManagement.maxEnergy,vehicle.powertrain.electric.battery.charging.power, andvehicle.drivetrain.electricEngine.charging.status. Those fields let the integration reset the predicted state of charge and calculate the charging slope between stream updates. It seems like thevehicle.drivetrain.batteryManagement.maxEnergyalways gets sent even though it's not explicitly set, but check it anyway.
-
Save the selection.
-
Repeat for all the cars you want to support
-
In Home Assistant, install this integration via HACS (see below under Installation (HACS)) and still in Home Assistant, step trough the Configuration Flow also described here below.
-
During the Home Assistant config flow, paste the client ID, visit the provided verification URL, enter the code (if asked), and approve. Do not click Continue/Submit in Home Assistant until the BMW page confirms the approval; submitting early leaves the flow stuck and requires a restart.
-
If you get Error 500 during setup:
Immediate actions:
- ❌ Remove the integration from Home Assistant
- 🔄 Go to BMW portal → Delete current Client ID
- ⏱️ Wait 5 minutes (BMW backend needs to clear old session)
- ✅ Create new Client ID
- ⏱️ Wait another 2 minutes
- ✅ Try installation again
If error persists after 2-3 attempts:
- ⏱️ Wait 24 hours (you may have hit daily rate limit)
- Try during different time of day (BMW servers less loaded)
-
Wait for the car to send data—triggering an action via the MyBMW app (lock/unlock doors) usually produces updates immediately. (older cars might need a drive before sensors start popping up, idrive6)
Cause: Authentication credentials incorrect or permissions not activated
Solutions:
- ✅ Verify
clientidis from BMW portal (NOT your login email) - ✅ Ensure both "CarData API" AND "CarData Stream" are enabled
- ✅ Wait 2-3 minutes after enabling permissions before trying again
- ✅ Delete and regenerate Client ID if permissions were recently changed
- ✅ Check that your BMW account has an active ConnectedDrive subscription
Cause: BMW API temporary issue or rate limiting
Solutions:
- ⏱️ Wait 5-10 minutes before retrying
- 🔄 Delete integration, create new Client ID in BMW portal
- 🔄 Try setup during off-peak hours (early morning/late evening)
- ✅ Ensure you didn't click "Authenticate device" in BMW portal (skip this!)
- 📧 If persistent, contact BMW CarData support - may be account-specific issue -> bmwcardata-b2c-support@bmwgroup.com
Cause: Submitted HA config flow before BMW page confirmed approval
Solution:
- 🛑 Wait for BMW page to show: "Device authenticated successfully"
- ✅ Only then click "Submit" in Home Assistant
- If already stuck: Restart Home Assistant and start over
- Add this repo to HACS as a custom repository (type: Integration).
- Install "Bmw cardata" from the Custom section.
- Restart Home Assistant.
- Go to Settings → Devices & Services → Add Integration and pick Bmw cardata.
- Enter your CarData client ID (created in the BMW portal and seen under section CARDATA API and there copied to your clipboard).
- The flow displays a
verification_urlanduser_code. Open the link, enter the code, and approve the device. - Once the BMW portal confirms the approval, return to HA and click Submit. If you accidentally submit before finishing the BMW login, the flow will hang until the device-code exchange times out; cancel it and start over after completing the BMW login.
- If you remove the integration later, you can re-add it with the same client ID—the flow deletes the old entry automatically.
- Small tip, on newer cars with Idrive7, you can force the sensor creation by opening the BMW/Mini App and press lock doors; on older ones like idrive6, You have to start the car, maybe even drive it a little bit
If BMW rejects the token (e.g. because the portal revoked it), please use the Configure > Start Device Authorization Again tool
You can switch the live stream from BMW's MQTT endpoint to your own broker (for example via bmw-mqtt-bridge).
BMW authorization is still required; only stream transport changes.
Expected topic format is <topic_prefix><VIN> (default prefix bmw/) and payload JSON must include vin and data.
Configure it in Home Assistant via Settings -> Devices & Services -> BMW CarData -> Configure -> MQTT Broker.
- Each VIN becomes a device in HA (
VINpulled from CarData). - Sensors/binary sensors are auto-created and named from descriptors (e.g.
Cabin Door Row1 Driver Is Open). - The device tracker (location entity) is restored from the entity registry on restart, so it keeps its last known position even before MQTT data arrives.
- Additional attributes include the source timestamp.
- All numeric sensors declare
suggested_display_precision, so unit conversions (e.g. km to miles) display clean rounded values in standard HA cards and the built-in vehicle card. You can override the display unit per entity via the gear icon in the entity settings, or switch your HA unit system to imperial for a global change.
The integration automatically registers a built-in Lovelace vehicle card — no manual YAML or HACS frontend install needed.
In Home Assistant:
- Go to a dashboard → Edit dashboard → Add card
- Search for BMW CarData Vehicle
- In the card editor, select which vehicle to display
Available configuration options:
| Option | Default | Description |
|---|---|---|
device_id |
(required) | Device ID of the BMW vehicle (from the cardata integration) |
license_plate |
(empty) | License plate number, shown instead of VIN when set |
soc_source |
soc |
Battery level source for the bar: soc (BMW last known), predicted (charging prediction), or magic (driving prediction) |
show_indicators |
true |
Status indicator row (locks, doors, windows, alarm). Windows, tailgate, and hood only show red when the car is parked and locked with the item open (walked-away alert). Alarm indicator: green when armed, blue when unarmed, red when triggered. |
show_range |
true |
Battery / fuel level bar with range |
show_image |
true |
Vehicle image |
show_map |
true |
Inline location map |
show_buttons |
true |
Quick-info tiles (location, mileage, service) |
You can also add the card manually in YAML. The card type is custom:bmw-cardata-vehicle-card.
To find the device_id, go to Settings → Devices & Services → BMW CarData, click on a vehicle device, and copy the device ID from the URL (the hex string after /config/devices/device/).
Minimal example:
type: custom:bmw-cardata-vehicle-card
device_id: abcdef1234567890abcdef1234567890Full example with all options:
type: custom:bmw-cardata-vehicle-card
device_id: abcdef1234567890abcdef1234567890
license_plate: AB 123 CD
soc_source: predicted
show_indicators: true
show_range: true
show_image: true
show_map: true
show_buttons: trueTo hide the map and quick-info tiles:
type: custom:bmw-cardata-vehicle-card
device_id: abcdef1234567890abcdef1234567890
show_map: false
show_buttons: falseSet DEBUG_LOG = True in custom_components/cardata/const.py for detailed MQTT/auth logs (disabled by default). To reduce noise, change it to False and reload HA.
The integration includes a predicted SOC (State of Charge) sensor that estimates battery charge during charging sessions. This sensor uses real-time accumulated energy (trapezoidal integration of charging power minus auxiliary consumption) to calculate charging progress more frequently than BMW's native SOC updates. This handles varying power levels naturally (DC taper above 80%, cold-battery ramp-up, grid fluctuations).
Some older models (e.g. i3s, iDrive 6 cars) do not report charging power or voltage/current via MQTT. For these vehicles, the integration derives an implied charging power from BMW SOC changes: when an API poll delivers a higher SOC than the previous anchor and no real power data exists, the average power is back-calculated from the SOC delta, elapsed time, and default efficiency. The heartbeat then extrapolates between polls using this derived value, giving smooth SOC progression instead of staircase jumps every 30 minutes. Real power data (if it arrives later) overwrites the derived value automatically.
The predicted SOC sensor automatically learns your vehicle's charging efficiency:
- AC charging efficiency: Starts at 90%, learns per charging condition (phases, voltage, current)
- DC charging efficiency: Starts at 93%, learns from actual DC sessions
- Both AC and DC use the same efficiency matrix with per-condition outlier detection, history tracking, and trend analysis
- Uses Exponential Moving Average (EMA) with adaptive learning rate (converges fast initially, settles to 20%)
- Learning data persists across Home Assistant restarts
- Active charging sessions and pending sessions survive HA restarts (restored sessions skip learning to avoid polluted data from energy gaps)
- After HA restart, stale SOC data from previous sessions is detected and rejected using BMW-provided timestamps, preventing false re-anchoring of the predicted SOC
- Session-level SOC and energy tracking survives mid-session re-anchoring, so learning uses the full session data even when BMW SOC corrections occurred during charging
For learning to occur, a charging session must meet these criteria:
- Minimum 5% SOC gain during the session
- Calculated efficiency between 40% and 98% (outliers are rejected)
- Per-condition outlier detection (2-sigma after 5+ history entries for the same charging condition)
- Valid power data recorded throughout the session
Learning happens when a charging session ends:
- Target reached: If charging stops within 2% of the target SOC, learning happens immediately
- Charge interrupted: If stopped before target, waits for BMW SOC confirmation:
- DC charging: 5-minute grace period
- AC charging: 15-minute grace period
For Plug-in Hybrid Electric Vehicles (PHEVs), the predicted SOC has special handling:
- Automatic PHEV detection: Vehicles with both an HV battery and fuel system are detected as PHEVs, unless metadata (driveTrain/propulsionType) or the model name (e.g. i4, iX, i5) identifies them as a known BEV
- Sync down on battery depletion: If the actual BMW SOC is lower than the predicted value, the prediction syncs down immediately. This handles scenarios where the hybrid system depletes the battery (e.g., battery recovery mode, engine-priority driving)
- Header filtering during charging: When
charging.levelis available and fresh,batteryManagement.headeris only allowed through if it would sync UP (header above prediction). Stale header values frozen at the pre-charge level are always below the prediction and get blocked, while legitimate mid-charge header updates above the prediction are allowed through for re-anchoring - Charging level ignored for sync-down: During charging,
charging.level(BMW's own prediction) is ignored when lower than our energy-based prediction, which tracks the real battery more accurately - BEVs: For pure electric vehicles, the predicted SOC only syncs when not actively charging (standard behavior)
This ensures the predicted SOC stays accurate for PHEVs even when the hybrid system uses battery power in ways that don't register as "discharging" in the BMW API.
Charging prediction accuracy: On PHEVs with small batteries, you may notice a small step (typically 2-3 percentage points) at the end of charge when BMW's real SOC arrives and syncs up with the energy-based prediction. Small batteries amplify any efficiency estimation error because each kWh represents a larger percentage of total capacity. This is normal. Charging efficiency varies between sessions due to temperature, charge depth, and grid voltage fluctuations, so the learned EMA efficiency cannot perfectly predict each individual session. The step is largest during the first 5-10 sessions for a given charging configuration (phases, voltage, current) and shrinks as the EMA converges, but some residual variation is expected even after 20+ sessions. On BEVs with larger batteries the same absolute error translates to a much smaller percentage step. The prediction tracks the real battery closely throughout charging and the sync-up at the end ensures the final value is always accurate.
Each EV/PHEV vehicle gets button entities to reset learned data:
- Reset AC Learning: Clears all AC entries from the efficiency matrix (resets to default 90%)
- Reset DC Learning: Clears all DC entries from the efficiency matrix (resets to default 93%)
- Reset Magic Learning: Clears driving consumption learning (resets to model default). Only appears when Magic SOC is enabled.
These buttons appear in the vehicle's device page under Configuration entities.
Each EV/PHEV vehicle gets a Manual Battery Capacity number entity (disabled by default) under Configuration entities. When set to a value above 0, it overrides the automatic capacity detection (BMW maxEnergy / batterySizeMax) for both charging and driving SOC prediction. Useful when BMW reports incorrect capacity values. Set to 0 to return to automatic detection.
Vehicles with fuel data get a Manual Tank Capacity number entity (disabled by default) under Configuration entities. When set to a value above 0 (in litres), the vehicle card computes a fuel level percentage from the remaining fuel and displays a progress bar. Useful for vehicles where BMW does not send fuelSystem.level (the percentage descriptor) and only sends remainingFuel (absolute litres). Set to 0 to disable.
Magic SOC predicts battery drain during driving using real-time odometer distance and learned consumption rates. It provides a sub-integer SOC estimate that updates more frequently than BMW's native integer SOC. Disabled by default — enable via Settings.
- Enable via: Settings → Devices & Services → BMW CarData → Configure → Settings → Enable Magic SOC
- Sensor:
vehicle.magic_socper vehicle (BEV only; PHEVs get passthrough BMW SOC) - How it works: Anchors on BMW's reported SOC at trip start, then subtracts
distance × learned_consumption / capacityas the vehicle drives. Re-anchors when BMW sends a fresh SOC mid-drive — if the drift is < 0.5pp, keeps the sub-integer prediction for smoother display. - Consumption learning: Uses EMA (Exponential Moving Average) with adaptive rate. Default 0.21 kWh/km globally, with per-model defaults (e.g. i4 eDrive40 = 0.18 kWh/km). Learns from completed trips where both SOC drop and distance are available. Requires at least 5 trips before the learning rate settles to 20%.
- Trip detection: Combines BMW's
isMovingsignal, GPS-derived motion, and odometer changes. Handles MQTT bursts and GPS gaps gracefully. - Capacity: Uses live
maxEnergyfrom BMW (reflects real degradation), falls back tobatterySizeMax, then to per-model defaults. - Reset: A "Reset Magic Learning" button appears under Configuration entities for each BEV.
Limitations: This is experimental. Accuracy depends on BMW sending timely SOC and mileage data. Preheating, extended idle with accessories, and firmware glitches can cause temporary inaccuracy. PHEVs are excluded from prediction (hybrid powertrain makes distance-based estimation unreliable).
If you have a smart meter that reports the power delivered to the car, you can feed its readings into the SOC predictor and bypass BMW's V×A telemetry, which is refreshed only every ~5 hours on some models and returns sentinel zero values during fast EVSE flapping (see discussion #359). Disabled by default.
- Enable via: Settings → Devices & Services → BMW CarData → Configure → Settings → Use external power meter for charging
- Service:
cardata.update_charging_powerwith fieldsvin,power_kw, and optionalaux_power_kw. - Precedence: Freshness-based. While a local reading has arrived within the last 120 seconds, BMW-sourced V×A and
charging.powerupdates are suppressed. When local readings stop (car driven away, meter offline), the predictor falls back to BMW after the timeout — no manual switching needed. - Typical setup: A Home Assistant automation that triggers on your meter's power sensor changes (or on a 10–30 second interval) and calls the service with the current reading. Gate the automation on the car being home — otherwise the meter will happily push 0 W while the car is away at a public charger, holding the freshness gate and blocking BMW's V×A updates from the public session.
automation:
- trigger:
- platform: state
entity_id: sensor.my_ev_meter_power
condition:
- condition: state
entity_id: device_tracker.my_bmw
state: home
action:
- service: cardata.update_charging_power
data:
vin: "WBY31AW090FP15359"
power_kw: "{{ states('sensor.my_ev_meter_power') | float / 1000 }}"When the option is disabled, the service no-ops with a warning — leaving it always registered so automations do not break across reloads.
The integration can fetch your BMW charging session history from the past 30 days. This is disabled by default to conserve your API quota.
- Enable via: Settings → Devices & Services → BMW CarData → Configure → Settings → Enable Charging History
- API cost: 1 call per vehicle per day (from your 50-call daily quota)
- Sensor: Creates a diagnostic sensor per vehicle showing session count and last charge date
- Attributes: Summarised session data (start/end time, start/end SOC, energy consumed, duration, charging type). Full raw data available via the
cardata.fetch_charging_historyservice - Manual trigger: Use
cardata.fetch_charging_historyservice in Developer Tools
The integration can fetch tyre health and wear data from BMW's Smart Maintenance system. This is disabled by default to conserve your API quota.
- Enable via: Settings → Devices & Services → BMW CarData → Configure → Settings → Enable Tyre Diagnosis
- API cost: 1 call per vehicle per day (from your 50-call daily quota)
- Sensor: Creates a diagnostic sensor per vehicle showing aggregate tyre status
- Attributes: Per-wheel data including dimension, wear, season, manufacturer, defect status, and production date
- Manual trigger: Use
cardata.fetch_tyre_diagnosisservice in Developer Tools
Home Assistant's Developer Tools expose helper services for manual API checks:
cardata.fetch_telematic_datafetches the current contents of the configured telematics container for a VIN and logs the raw payload.cardata.fetch_vehicle_mappingscallsGET /customers/vehicles/mappingsand logs the mapping details (including PRIMARY or SECONDARY status). Only primary mappings return data; some vehicles do not support secondary users, in which case the mapped user is considered the primary one.cardata.fetch_basic_datacallsGET /customers/vehicles/{vin}/basicDatato retrieve static metadata (model name, series, etc.) for the specified VIN.cardata.fetch_charging_historyfetches the last 30 days of charging sessions for a VIN. Uses 1 API call per vehicle.cardata.fetch_tyre_diagnosisfetches tyre health and wear data for a VIN. Uses 1 API call per vehicle.cardata.fetch_vehicle_imagesmanually fetches vehicle images for all configured vehicles.cardata.clean_hv_containerslists or deletes high-voltage battery telemetry containers (actions:list,delete,delete_all,delete_all_matching).cardata.migrate_entity_idsmigrates entity IDs from old format to new format. Usedry_runto preview changes without applying them.cardata.update_charging_powerinjects a locally measured charging power reading (kW) into the SOC predictor. Requires the Use external power meter for charging option to be enabled under Settings. Intended for users with a smart meter who want to feed accurate real-time power into the prediction instead of relying on BMW's V×A — see the section below.
BMW imposes a 50 calls/day limit on the CarData API. This integration does not enforce the limit client-side — BMW's own 429 response is respected via backoff. API usage is minimized through MQTT freshness gating and rate limiting:
- MQTT Stream (real-time): The MQTT stream is unlimited and provides real-time updates for events like door locks, motion state, charging power, etc. GPS coordinates are paired using BMW payload timestamps (same GPS fix detection) with an arrival-time fallback, so location updates work even when latitude and longitude arrive in separate MQTT messages. In direct BMW mode, token refresh during MQTT reconnection is lock-free to avoid blocking the connection, and the MQTT connection is proactively reconnected with fresh credentials to prevent session expiry (~1 hour).
- Trip-end polling: When a vehicle stops moving (trip ends), the integration triggers an immediate API poll to capture post-trip battery state. This ensures SOC is updated even when the MQTT stream only delivers GPS/mileage but not SOC (common on some models). A configurable per-VIN cooldown (default 10 minutes) prevents GPS burst flapping from burning API quota. A 30-second grace period after door unlock prevents brief intermediate stops (e.g. picking up a passenger) from fragmenting a trip and wasting API calls. Trip-end polling can be disabled entirely or the cooldown can be increased via Settings → Devices & Services → BMW CarData → Configure → Settings. Disabling it is useful for vehicles making many short trips (e.g. delivery drivers, nurses) that would otherwise exhaust the daily API quota.
- Charge-end polling: When charging completes or stops, the integration triggers an immediate API poll to get the actual BMW SOC for learning calibration of the predicted SOC sensor, subject to the same per-VIN cooldown.
- Fallback polling: The integration polls periodically as a fallback in case MQTT stream fails or after Home Assistant restarts. VINs with fresh MQTT data are skipped individually, so in multi-car setups only stale VINs consume API calls.
- Daily optional features: When Charging History and/or Tyre Diagnosis are enabled, each makes exactly 1 API call per vehicle per day regardless of whether the call succeeds or fails (no retries). The polling interval automatically increases to compensate — e.g. with both features on 2 cars, polling stretches from 2h to 2.4h per VIN.
- Multi-VIN setups: All vehicles share the same 50 call/day limit. The poll interval scales with VIN count plus any enabled daily features. Each VIN is guaranteed at least 1 poll per day; BMW's 429 backoff handles actual quota enforcement.
- Rate limiting: If BMW returns a rate-limited response (HTTP 429 or HTTP 403 with
CU-429error code), the integration backs off automatically with exponential delay.
- BMW CarData account with streaming access (CarData API + CarData Streaming subscribed in the portal).
- Client ID created in the BMW portal (see "BMW Portal Setup").
- Home Assistant 2025.3+.
- TLS 1.3 capable SSL library (required for direct BMW MQTT mode): OpenSSL 1.1.1+, LibreSSL 3.2.0+, or equivalent.
- Familiarity with BMW's CarData documentation: https://bmw-cardata.bmwgroup.com/customer/public/api-documentation/Id-Introduction
!! Recommended setup with people on multiple BMWs (not required, it's working as is but you're limiting yourself in accuracy since the hardcoded 50 limits a day) !!
- Car 1 -> email_1
- Car 2 -> mail_2
- .....
- Use those separate accounts in the integration
- Use mail_x+1 which has all the cars merged for the BMW app
- As said, not needed but then you live with outdated data (Hour x amount of cars on single account).
The setup wizard, error messages, and options menu are translated into the following languages:
- English (en)
- German (de)
- French (fr)
- Italian (it)
- Dutch (nl)
- Spanish (es)
- Portuguese (pt)
Home Assistant automatically selects the translation matching your configured language. Entity names are not translated as they use BMW descriptor names with values and units.
The integration is organized into focused modules:
| Module | Purpose |
|---|---|
const.py |
Shared constants: descriptor paths, timeouts, domain identifiers |
__init__.py |
Thin entry point: delegates to lifecycle.py |
lifecycle.py |
Setup/unload orchestration, ghost device cleanup |
coordinator.py |
Central state management, message dispatch, entity signaling |
soc_wiring.py |
SOC/charging/driving prediction wiring between descriptors and prediction engines |
device_info.py |
Device metadata building, BEV detection, state restoration |
coordinator_housekeeping.py |
Diagnostics, stale VIN cleanup, old descriptor eviction, connection events |
soc_prediction.py |
Charging SOC: trapezoidal energy integration, session management |
soc_types.py |
Charging data types: LearnedEfficiency, PendingSession, ChargingSession |
soc_learning.py |
Charging efficiency EMA learning, session finalization, persistence |
magic_soc.py |
Driving SOC: distance-based consumption prediction, adaptive EMA learning |
stream.py |
MQTT connection management, credential hot-swap |
stream_circuit_breaker.py |
Circuit breaker for reconnection rate limiting |
stream_reconnect.py |
Reconnection, unauthorized handling, retry scheduling |
geo_utils.py |
Shared geographic utilities (Haversine distance) |
motion_detection.py |
GPS centroid movement detection, parking zone logic |
sensor_diagnostics.py |
Diagnostic sensors: connection, metadata, efficiency, charging history, tyre diagnosis |
sensor.py / binary_sensor.py / device_tracker.py |
Home Assistant entity platforms |
button.py |
Reset learning buttons (AC, DC, consumption) |
number.py |
Manual battery capacity and tank capacity input entities |
config_flow.py |
Setup and reauthorization UI flows |
options_flow.py |
Options menu: settings, MQTT broker, API actions, entity cleanup |
bootstrap.py |
VIN discovery, metadata fetch, container creation |
auth.py |
Token refresh loop, reauth flow, stream error handling |
runtime.py |
Per-entry runtime data, locks, session health |
telematics.py |
Scheduled API polling, trip-end/charge-end triggers |
container.py |
Telematic container CRUD, signature-based reuse |
services.py |
HA service handlers (API calls, migrations, container management) |
ratelimit.py |
Rate limiting: 429 backoff, unauthorized loop protection, container rate limiter |
http_retry.py |
HTTP retry with exponential backoff and jitter |
image.py |
Vehicle image fetching and caching |
frontend_cards.py |
Lovelace card backend: websocket API, resource registration |
migrations.py |
Entity ID format migration tool |
- Direct BMW MQTT has one-stream-per-GCID behavior: make sure no other direct clients are connected simultaneously. To share one upstream stream, use a bridge + custom MQTT broker mode.
- The CarData API is read-only; sending commands remains outside this integration.
- Premature Continue in auth flow: If you hit Continue before authorizing on BMW's site, the device-code flow gets stuck. Cancel the flow and restart the integration (or Home Assistant) once you've completed the BMW login.
- Older models (i3, i3s, iDrive 6 cars, older F-series): These vehicles send telemetry very infrequently — typically only when the car is stopped/turned off and when charging reaches 100%. There are no real-time MQTT updates while charging or driving, so most sensors will appear stale between events. The Predicted SOC sensor can help during charging, but accuracy depends on receiving at least an initial SOC value. This is a BMW platform limitation, not a bug in the integration.
- Neue Klasse (NK/NA5) vehicles: BMW's CarData API does not provide
batteryManagement.headerorcharging.levelfor NK vehicles. The integration usesstateOfCharge.displayedas the primary SOC source, withtrip...hvSoc(trip-end battery level) as a last resort.stateOfCharge.displayedupdates via API polls but may not refresh in real-time during driving. Charging prediction works viacharging.power.
Issues that remain inactive for 1 week receive an automated reminder. A second reminder follows after 2 weeks, and a final warning after 3 weeks. Issues with no response after 4 weeks are automatically closed. Any comment from a non-bot user resets the cycle. Issues labeled pinned or security are exempt.
This project is licensed under the BSD 2-Clause License - see the LICENSE file for details.
This software was created by Kris Van Biesen. Taken over since no response for original developper (https://github.com/JjyKsi/bmw-cardata-ha). Please keep this notice if you redistribute or modify the code.


