diff --git a/custom_components/xiaoxiang_bms/bluetooth_handler.py b/custom_components/xiaoxiang_bms/bluetooth_handler.py index 2bcba99..c2aa36b 100644 --- a/custom_components/xiaoxiang_bms/bluetooth_handler.py +++ b/custom_components/xiaoxiang_bms/bluetooth_handler.py @@ -6,6 +6,7 @@ import logging import struct from bleak import BleakClient, BleakError +from bleak.backends.device import BLEDevice from .const import ( FRAME_END, @@ -42,13 +43,17 @@ class BmsBluetoothHandler: def is_connected(self) -> bool: return self._client is not None and self._client.is_connected - async def connect(self) -> None: - """Open BLE connection and start notifications.""" + async def connect(self, ble_device: BLEDevice) -> None: + """Open BLE connection and start notifications. + + Accepts a BLEDevice resolved by HA's Bluetooth subsystem so that + ESPHome BLE proxies are used transparently alongside local adapters. + """ if self.is_connected: return - _LOGGER.debug("Connecting to BMS at %s", self._address) + _LOGGER.debug("Connecting to BMS at %s (via %s)", self._address, ble_device.name) self._client = BleakClient( - self._address, + ble_device, disconnected_callback=self._on_disconnect, ) await self._client.connect() diff --git a/custom_components/xiaoxiang_bms/coordinator.py b/custom_components/xiaoxiang_bms/coordinator.py index 7179f29..972b366 100644 --- a/custom_components/xiaoxiang_bms/coordinator.py +++ b/custom_components/xiaoxiang_bms/coordinator.py @@ -4,6 +4,7 @@ from __future__ import annotations import logging from datetime import timedelta +from homeassistant.components.bluetooth import async_ble_device_from_address from homeassistant.core import HomeAssistant from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed @@ -35,9 +36,22 @@ class BmsCoordinator(DataUpdateCoordinator[dict]): # Lifecycle # ------------------------------------------------------------------ + def _get_ble_device(self): + """Resolve the best available BLE device via HA's Bluetooth subsystem. + + This automatically uses ESPHome BLE proxies if they can reach the BMS + and the local adapter cannot (or vice versa). + """ + device = async_ble_device_from_address(self.hass, self.address, connectable=True) + if device is None: + raise UpdateFailed( + f"BMS ({self.address}) not reachable by any Bluetooth adapter or proxy" + ) + return device + async def async_setup(self) -> None: """Connect to the BMS. Called once during config entry setup.""" - await self._handler.connect() + await self._handler.connect(self._get_ble_device()) async def async_teardown(self) -> None: """Disconnect cleanly. Called on entry unload.""" @@ -52,7 +66,9 @@ class BmsCoordinator(DataUpdateCoordinator[dict]): if not self._handler.is_connected: _LOGGER.debug("BMS not connected, attempting reconnect…") try: - await self._handler.connect() + await self._handler.connect(self._get_ble_device()) + except UpdateFailed: + raise except Exception as exc: raise UpdateFailed(f"BMS reconnect failed: {exc}") from exc