Support ESPHome BLE proxies via HA Bluetooth subsystem

Use async_ble_device_from_address() to resolve the BMS through whichever
adapter (local or ESPHome proxy) can reach it, instead of connecting by
raw MAC address directly. BleakClient now receives a BLEDevice object.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-04-11 19:33:04 +02:00
parent ea1e74c384
commit db45679c12
2 changed files with 27 additions and 6 deletions
@@ -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()
+18 -2
View File
@@ -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