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:
@@ -6,6 +6,7 @@ import logging
|
|||||||
import struct
|
import struct
|
||||||
|
|
||||||
from bleak import BleakClient, BleakError
|
from bleak import BleakClient, BleakError
|
||||||
|
from bleak.backends.device import BLEDevice
|
||||||
|
|
||||||
from .const import (
|
from .const import (
|
||||||
FRAME_END,
|
FRAME_END,
|
||||||
@@ -42,13 +43,17 @@ class BmsBluetoothHandler:
|
|||||||
def is_connected(self) -> bool:
|
def is_connected(self) -> bool:
|
||||||
return self._client is not None and self._client.is_connected
|
return self._client is not None and self._client.is_connected
|
||||||
|
|
||||||
async def connect(self) -> None:
|
async def connect(self, ble_device: BLEDevice) -> None:
|
||||||
"""Open BLE connection and start notifications."""
|
"""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:
|
if self.is_connected:
|
||||||
return
|
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._client = BleakClient(
|
||||||
self._address,
|
ble_device,
|
||||||
disconnected_callback=self._on_disconnect,
|
disconnected_callback=self._on_disconnect,
|
||||||
)
|
)
|
||||||
await self._client.connect()
|
await self._client.connect()
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ from __future__ import annotations
|
|||||||
import logging
|
import logging
|
||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
|
|
||||||
|
from homeassistant.components.bluetooth import async_ble_device_from_address
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
|
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
|
||||||
|
|
||||||
@@ -35,9 +36,22 @@ class BmsCoordinator(DataUpdateCoordinator[dict]):
|
|||||||
# Lifecycle
|
# 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:
|
async def async_setup(self) -> None:
|
||||||
"""Connect to the BMS. Called once during config entry setup."""
|
"""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:
|
async def async_teardown(self) -> None:
|
||||||
"""Disconnect cleanly. Called on entry unload."""
|
"""Disconnect cleanly. Called on entry unload."""
|
||||||
@@ -52,7 +66,9 @@ class BmsCoordinator(DataUpdateCoordinator[dict]):
|
|||||||
if not self._handler.is_connected:
|
if not self._handler.is_connected:
|
||||||
_LOGGER.debug("BMS not connected, attempting reconnect…")
|
_LOGGER.debug("BMS not connected, attempting reconnect…")
|
||||||
try:
|
try:
|
||||||
await self._handler.connect()
|
await self._handler.connect(self._get_ble_device())
|
||||||
|
except UpdateFailed:
|
||||||
|
raise
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
raise UpdateFailed(f"BMS reconnect failed: {exc}") from exc
|
raise UpdateFailed(f"BMS reconnect failed: {exc}") from exc
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user