mirror of
https://github.com/home-assistant/core.git
synced 2025-07-22 20:57:21 +00:00
Add support for esphome ble client connections v3 (#82815)
This commit is contained in:
parent
230b50d099
commit
fb98128b9f
@ -36,6 +36,13 @@ DISCONNECT_TIMEOUT = 5.0
|
||||
CONNECT_FREE_SLOT_TIMEOUT = 2.0
|
||||
GATT_READ_TIMEOUT = 30.0
|
||||
|
||||
# CCCD (Characteristic Client Config Descriptor)
|
||||
CCCD_UUID = "00002902-0000-1000-8000-00805f9b34fb"
|
||||
CCCD_NOTIFY_BYTES = b"\x01\x00"
|
||||
CCCD_INDICATE_BYTES = b"\x02\x00"
|
||||
|
||||
MIN_BLUETOOTH_PROXY_VERSION_HAS_CACHE = 3
|
||||
|
||||
DEFAULT_MAX_WRITE_WITHOUT_RESPONSE = DEFAULT_MTU - GATT_HEADER_SIZE
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
@ -137,6 +144,9 @@ class ESPHomeClient(BaseBleakClient):
|
||||
self._cancel_connection_state: CALLBACK_TYPE | None = None
|
||||
self._notify_cancels: dict[int, Callable[[], Coroutine[Any, Any, None]]] = {}
|
||||
self._disconnected_event: asyncio.Event | None = None
|
||||
device_info = self.entry_data.device_info
|
||||
assert device_info is not None
|
||||
self._connection_version = device_info.bluetooth_proxy_version
|
||||
|
||||
def __str__(self) -> str:
|
||||
"""Return the string representation of the client."""
|
||||
@ -206,7 +216,14 @@ class ESPHomeClient(BaseBleakClient):
|
||||
Boolean representing connection status.
|
||||
"""
|
||||
await self._wait_for_free_connection_slot(CONNECT_FREE_SLOT_TIMEOUT)
|
||||
|
||||
entry_data = self.entry_data
|
||||
self._mtu = entry_data.get_gatt_mtu_cache(self._address_as_int)
|
||||
has_cache = bool(
|
||||
dangerous_use_bleak_cache
|
||||
and self._connection_version >= MIN_BLUETOOTH_PROXY_VERSION_HAS_CACHE
|
||||
and entry_data.get_gatt_services_cache(self._address_as_int)
|
||||
and self._mtu
|
||||
)
|
||||
connected_future: asyncio.Future[bool] = asyncio.Future()
|
||||
|
||||
def _on_bluetooth_connection_state(
|
||||
@ -224,7 +241,9 @@ class ESPHomeClient(BaseBleakClient):
|
||||
)
|
||||
if connected:
|
||||
self._is_connected = True
|
||||
self._mtu = mtu
|
||||
if not self._mtu:
|
||||
self._mtu = mtu
|
||||
entry_data.set_gatt_mtu_cache(self._address_as_int, mtu)
|
||||
else:
|
||||
self._async_ble_device_disconnected()
|
||||
|
||||
@ -258,7 +277,7 @@ class ESPHomeClient(BaseBleakClient):
|
||||
self._ble_device.name,
|
||||
self._ble_device.address,
|
||||
)
|
||||
self.entry_data.disconnect_callbacks.append(self._async_esp_disconnected)
|
||||
entry_data.disconnect_callbacks.append(self._async_esp_disconnected)
|
||||
connected_future.set_result(connected)
|
||||
|
||||
timeout = kwargs.get("timeout", self._timeout)
|
||||
@ -271,6 +290,8 @@ class ESPHomeClient(BaseBleakClient):
|
||||
self._address_as_int,
|
||||
_on_bluetooth_connection_state,
|
||||
timeout=timeout,
|
||||
has_cache=has_cache,
|
||||
version=self._connection_version,
|
||||
)
|
||||
)
|
||||
except Exception: # pylint: disable=broad-except
|
||||
@ -344,9 +365,13 @@ class ESPHomeClient(BaseBleakClient):
|
||||
"""
|
||||
address_as_int = self._address_as_int
|
||||
entry_data = self.entry_data
|
||||
if dangerous_use_bleak_cache and (
|
||||
cached_services := entry_data.get_gatt_services_cache(address_as_int)
|
||||
):
|
||||
# If the connection version >= 3, we must use the cache
|
||||
# because the esp has already wiped the services list to
|
||||
# save memory.
|
||||
if (
|
||||
self._connection_version >= MIN_BLUETOOTH_PROXY_VERSION_HAS_CACHE
|
||||
or dangerous_use_bleak_cache
|
||||
) and (cached_services := entry_data.get_gatt_services_cache(address_as_int)):
|
||||
_LOGGER.debug(
|
||||
"%s: %s - %s: Cached services hit",
|
||||
self._source,
|
||||
@ -516,6 +541,14 @@ class ESPHomeClient(BaseBleakClient):
|
||||
f"characteristic:{characteristic.uuid} "
|
||||
f"handle:{ble_handle}"
|
||||
)
|
||||
if (
|
||||
"notify" not in characteristic.properties
|
||||
and "indicate" not in characteristic.properties
|
||||
):
|
||||
raise BleakError(
|
||||
f"Characteristic {characteristic.uuid} does not have notify or indicate property set."
|
||||
)
|
||||
|
||||
cancel_coro = await self._client.bluetooth_gatt_start_notify(
|
||||
self._address_as_int,
|
||||
ble_handle,
|
||||
@ -523,6 +556,37 @@ class ESPHomeClient(BaseBleakClient):
|
||||
)
|
||||
self._notify_cancels[ble_handle] = cancel_coro
|
||||
|
||||
if self._connection_version < MIN_BLUETOOTH_PROXY_VERSION_HAS_CACHE:
|
||||
return
|
||||
|
||||
# For connection v3 we are responsible for enabling notifications
|
||||
# on the cccd (characteristic client config descriptor) handle since
|
||||
# the esp32 will not have resolved the characteristic descriptors to
|
||||
# save memory since doing so can exhaust the memory and cause a soft
|
||||
# reset
|
||||
cccd_descriptor = characteristic.get_descriptor(CCCD_UUID)
|
||||
if not cccd_descriptor:
|
||||
raise BleakError(
|
||||
f"Characteristic {characteristic.uuid} does not have a "
|
||||
"characteristic client config descriptor."
|
||||
)
|
||||
|
||||
_LOGGER.debug(
|
||||
"%s: %s - %s: Writing to CCD descriptor %s for notifications with properties=%s",
|
||||
self._source,
|
||||
self._ble_device.name,
|
||||
self._ble_device.address,
|
||||
cccd_descriptor.handle,
|
||||
characteristic.properties,
|
||||
)
|
||||
supports_notify = "notify" in characteristic.properties
|
||||
await self._client.bluetooth_gatt_write_descriptor(
|
||||
self._address_as_int,
|
||||
cccd_descriptor.handle,
|
||||
CCCD_NOTIFY_BYTES if supports_notify else CCCD_INDICATE_BYTES,
|
||||
wait_for_response=False,
|
||||
)
|
||||
|
||||
@api_error_as_bleak_error
|
||||
async def stop_notify(
|
||||
self,
|
||||
|
@ -98,6 +98,9 @@ class RuntimeEntryData:
|
||||
_gatt_services_cache: MutableMapping[int, BleakGATTServiceCollection] = field(
|
||||
default_factory=lambda: LRU(MAX_CACHED_SERVICES) # type: ignore[no-any-return]
|
||||
)
|
||||
_gatt_mtu_cache: MutableMapping[int, int] = field(
|
||||
default_factory=lambda: LRU(MAX_CACHED_SERVICES) # type: ignore[no-any-return]
|
||||
)
|
||||
|
||||
@property
|
||||
def name(self) -> str:
|
||||
@ -116,6 +119,14 @@ class RuntimeEntryData:
|
||||
"""Set the BleakGATTServiceCollection for the given address."""
|
||||
self._gatt_services_cache[address] = services
|
||||
|
||||
def get_gatt_mtu_cache(self, address: int) -> int | None:
|
||||
"""Get the mtu cache for the given address."""
|
||||
return self._gatt_mtu_cache.get(address)
|
||||
|
||||
def set_gatt_mtu_cache(self, address: int, mtu: int) -> None:
|
||||
"""Set the mtu cache for the given address."""
|
||||
self._gatt_mtu_cache[address] = mtu
|
||||
|
||||
@callback
|
||||
def async_update_ble_connection_limits(self, free: int, limit: int) -> None:
|
||||
"""Update the BLE connection limits."""
|
||||
|
@ -3,7 +3,7 @@
|
||||
"name": "ESPHome",
|
||||
"config_flow": true,
|
||||
"documentation": "https://www.home-assistant.io/integrations/esphome",
|
||||
"requirements": ["aioesphomeapi==12.0.1"],
|
||||
"requirements": ["aioesphomeapi==12.1.0"],
|
||||
"zeroconf": ["_esphomelib._tcp.local."],
|
||||
"dhcp": [{ "registered_devices": true }],
|
||||
"codeowners": ["@OttoWinter", "@jesserockz"],
|
||||
|
@ -156,7 +156,7 @@ aioecowitt==2022.09.3
|
||||
aioemonitor==1.0.5
|
||||
|
||||
# homeassistant.components.esphome
|
||||
aioesphomeapi==12.0.1
|
||||
aioesphomeapi==12.1.0
|
||||
|
||||
# homeassistant.components.flo
|
||||
aioflo==2021.11.0
|
||||
|
@ -143,7 +143,7 @@ aioecowitt==2022.09.3
|
||||
aioemonitor==1.0.5
|
||||
|
||||
# homeassistant.components.esphome
|
||||
aioesphomeapi==12.0.1
|
||||
aioesphomeapi==12.1.0
|
||||
|
||||
# homeassistant.components.flo
|
||||
aioflo==2021.11.0
|
||||
|
Loading…
x
Reference in New Issue
Block a user