Bosch eBike Live Data Interface bridge as an ESPHome external component (BLE)

Hi all,

I have been working on an ESPHome external component that turns a plain ESP32 into a Bluetooth bridge for the Bosch smart system Live Data Interface that Bosch quietly published in May 2026. It surfaces all 13 live data fields (battery SoC, speed, cadence, rider power, odometer, light, lock state, charger connected, and the rest) as ESPHome sensors and binary_sensors. Repo and bilingual docs:

ha-bosch-ebike/esphome at main · Xunil99/ha-bosch-ebike · GitHub

Why this is interesting

Bosch's LDI spec (v1.0, May 2026) is public, Apache-2.0 licensed, with no registration and no keys: Live Data Interface: eBike-Echtzeitdaten via API nutzen. The catch is that smartphone support is explicitly out of scope, so we need a small stationary BLE accessory. An ESP32 in the garage or shed turned out to be the natural fit. Smart system control unit release v19 or newer is required on the bike side.

Easy install (no ESPHome dashboard needed)

For non-technical users I built a browser-based installer using esp-web-tools plus an automated GitHub Actions build:

Bosch eBike LDI Bridge - Web Installer

Plug an ESP32 via USB, open the page in Chrome or Edge, click Install. WiFi setup happens in the same browser session via Improv-Serial. After that, the device shows up in HA's auto-discovery and adopts into the ESPHome dashboard if available. The whole flashing flow takes about a minute, including WiFi.

DIY-style YAML for those who prefer to compile themselves is also in the repo under esphome/example-bridge.yaml. It pulls the component directly from the repo via external_components: source: github://..., so updates flow in automatically on the next compile.

A few technical highlights that may interest the ESPHome crowd

  • Inverse GAP / GATT roles: the eBike is GAP Central + GATT Server, the accessory must be GAP Peripheral + GATT Client. The ESP32 advertises with a Service Solicitation (AD type 0x15) for the LDI service UUID and then acts as GATT client on the resulting connection. This is unusual and not directly representable in NimBLE's ble_hs_adv_fields, so the AD payload is built by hand and passed to ble_gap_adv_set_data().
  • Mandatory bonding with LE Secure Connections, Just Works, stored persistently in NVS. NimBLE's ble_store_config_init() handles persistence cleanly across reboots and OTA.
  • Bosch firmware quirks: the spec acknowledges three known issues in v19. Two of them (DLE and MTU not being initiated by the eBike) are worked around by the bridge actively triggering both itself right after encryption setup.
  • Protobuf payload: the live data characteristic is a com.bosch.ebike.LiveData proto3 message. Hand-rolled decoder, around 120 lines, no nanopb build setup required. Forward-compatible by design, since unknown tags are skipped per proto3 rules.
  • Bond-resume race fix: NimBLE sometimes delivers BLE_GAP_EVENT_ENC_CHANGE to user space before BLE_GAP_EVENT_CONNECT on bonded reconnects. The handler reads conn_handle straight from the event struct rather than from cached state to avoid an ENOTCONN from ble_gattc_exchange_mtu.

Pairs with the cloud integration

This bridge complements an existing HACS integration in the same repo that pulls tour history from the Bosch Data Act cloud API. From v1.10.0, the cloud integration can optionally use the live BLE values as the source of truth for tour distance and battery consumption, replacing the snapshot-based cloud estimates.

Status

Tested with a Performance Line CX on smart system v19.54.0. Should work with any v19+ drive unit since the LDI is unit-agnostic, but confirmation from other variants (SX, Cargo, Performance Line) would be very welcome. ESP32-S3, C3 and C6 also untested so far, plain ESP32 dev boards work.

Issues, ideas, drive unit confirmations and PRs all appreciated.

Cheers,
Xunil99

5 Likes

Thanks for your effort.
Sounds very interesting.
Does the bike only send live data when it is turned on?
I understood that the ESP is stationary placed inside the garage, what benefit does it have besides the state of charge transmission?

Regards

Yes, the eBike sends live data only when it's turned on. However, it seems to wake up about all two hours, connects to BLE and sends data.

The ESPHome is intended to be stationary, however, I'm just working on a small ESP32 C3 Module with a battery, that you can put in your bag and that saves all data. Should be able to save all data for about 8 to 10 hours. Not sure if anyone would use this, I'm just trying to find out, if that works because I'm interested :slight_smile:

1 Like

Can this bridge be installed on a Esp32 which is already used for 2 sensors? If that's possible, I'll combine the settings with my current Esp :slight_smile:

Great work again!

Should work, yes. If you could give me feedback, that would help other users :slight_smile:

Nice! I will try to make some time this week. I'll let you know if I succeed!

1 Like

I got it working :slight_smile:

It is running on my bedsensor board, which has 2 FSR bedsensors (FSR - the best bed occupancy sensor) and a DHT sensor as well.
Since our bedroom (1st floor) is located next to the driveway, it connected directly to the bike.

I just copied the parts from the example code on Github and sorted it between the original code of my sensor.

Thanks for the amazing work! Now I can turn my charger on and off more accurate on <25% and >80% without guessing! (using a Shelly plug S)

Thank you for your feedback!! Sounds great!

Great job @Xunil99 :grinning_face:
Did anyone try to flash a smart plug with this?
I was thinking of this one so I can charge to 80% by default and top it up to 100% on the same day doing a ride...

@ma_aue Funny timing - that is exactly what @henri98 just contributed. See PR #22, now merged: a standalone charge limiter on a Sonoff BASICR4 (which has an ESP32-C3 inside, so it runs the LDI bridge AND switches mains via its onboard relay). It reads the live battery SoC over BLE and cuts the relay at a configurable limit (default 80%), all on the device, so it works even without Home Assistant in the loop.

It is built for the BASICR4, but the logic (template number for the limit + a battery_soc.on_value that turns the switch off when SoC reaches it) ports to any ESPHome-controlled smart plug, like your Athom, as long as the bridge has the bike's SoC.

Your "80% by default, top up to 100% before a ride" idea goes one step further than the current example, which just stops at the limit. With an Athom plug in Home Assistant you could do the top-up part as an HA automation (raise the limit / re-enable charging on the morning of a ride). The charge limiter device gives you the reliable hard stop; HA handles the smarter scheduling on top.

Credit for the charge limiter goes to @henri98.

1 Like

For anyone who wants to take the bridge along on a ride and still get the data into Home Assistant at home, there is now a documented mobile setup.

The idea: the ESP runs off a powerbank in your frame bag, joins your phone's hotspot, and pushes its data home through a WireGuard tunnel that the ESP builds itself. You do not need a VPN app on the phone, it just provides the hotspot. It uses MQTT instead of the native API, because with MQTT the ESP pushes the data, so the constantly changing IP on the road no longer matters.

Guide and example config:

One thing worth knowing if you run your own WireGuard server with a separate tunnel subnet: ESPHome's WireGuard does not do policy routing, so the ESP's tunnel IP has to be in the same subnet as your MQTT broker (a FritzBox does this automatically), or you use full tunnel 0.0.0.0/0. There is a short troubleshooting section in MOBILE.md covering exactly that.

All values (WiFi, hotspot, MQTT, WireGuard) live in secrets.yaml, so nothing personal ends up in the config.

1 Like

Sounds great :grinning_face:
I was more thinking in the direction to let HA handle all the logic and just expose all entities from the smart plug running the Bosch ldi stuff.

Overall I'd like to have:

  • cool down timer when connecting the bike to the charger, like 60 min. Quite often I just plug it in after coming home from a ride.
  • default 80% charge limit
  • manual top up to 100% either toggled by HA or the button on the smart plug
  • LED indicating if top up mode is enabled on the smart plug
  • enabling top up mode should also cancel the #1 timer

There are probably different preferences for everyone so imho it is best to do most of it in HA and only add Bosch support on the smart plug.

I ordered these plugs yesterday, let's see when they arrive and how things work out then. Good to know that the platform is known as working. :slightly_smiling_face: