diff --git a/aiohomekit/controller/ble/bleak.py b/aiohomekit/controller/ble/bleak.py index 29a09ef8..00112d73 100644 --- a/aiohomekit/controller/ble/bleak.py +++ b/aiohomekit/controller/ble/bleak.py @@ -2,9 +2,10 @@ import uuid -from bleak import BleakClient, BleakError +from bleak import BleakError from bleak.backends.characteristic import BleakGATTCharacteristic from bleak.backends.device import BLEDevice +from bleak_retry_connector import BleakClientWithServiceCache from .const import HAP_MIN_REQUIRED_MTU @@ -13,7 +14,7 @@ CHAR_DESCRIPTOR_UUID = uuid.UUID(CHAR_DESCRIPTOR_ID) -class AIOHomeKitBleakClient(BleakClient): +class AIOHomeKitBleakClient(BleakClientWithServiceCache): """Wrapper for bleak.BleakClient that auto discovers the max mtu.""" def __init__(self, address_or_ble_device: BLEDevice | str) -> None: diff --git a/aiohomekit/controller/ble/connection.py b/aiohomekit/controller/ble/connection.py index 5a02ee31..ba0473b0 100644 --- a/aiohomekit/controller/ble/connection.py +++ b/aiohomekit/controller/ble/connection.py @@ -19,6 +19,7 @@ from collections.abc import Callable from bleak.backends.device import BLEDevice +from bleak.backends.service import BleakGATTServiceCollection from bleak_retry_connector import ( BleakAbortedError, BleakConnectionError, @@ -39,6 +40,7 @@ async def establish_connection( name: str, disconnected_callback: Callable[[AIOHomeKitBleakClient], None], max_attempts: int = MAX_CONNECT_ATTEMPTS, + cached_services: BleakGATTServiceCollection | None = None, ) -> AIOHomeKitBleakClient: """Establish a connection to the accessory.""" try: @@ -48,6 +50,7 @@ async def establish_connection( name, disconnected_callback, max_attempts=max_attempts, + cached_services=cached_services, ) except (BleakAbortedError, BleakConnectionError) as ex: raise AccessoryDisconnectedError(ex) from ex diff --git a/aiohomekit/controller/ble/pairing.py b/aiohomekit/controller/ble/pairing.py index 33368bc7..2ddb1b13 100644 --- a/aiohomekit/controller/ble/pairing.py +++ b/aiohomekit/controller/ble/pairing.py @@ -26,7 +26,9 @@ from typing import TYPE_CHECKING, Any, TypeVar, cast from bleak.backends.device import BLEDevice +from bleak.backends.service import BleakGATTServiceCollection from bleak.exc import BleakError +from bleak_retry_connector import ble_device_has_changed from aiohomekit.exceptions import ( AccessoryDisconnectedError, @@ -123,6 +125,10 @@ def __init__( self.id = pairing_data["AccessoryPairingID"] self.device = device self.client = client + self._cached_services: BleakGATTServiceCollection | None = ( + client.services if client else None + ) + self.pairing_data = pairing_data self.description = description self.controller = controller @@ -200,7 +206,8 @@ def transport(self) -> Transport: def _async_ble_device_update(self, device: BLEDevice) -> None: """Update the BLE device.""" - if self.device and device.address != self.device.address: + if self.device and ble_device_has_changed(self.device, device): + self._cached_services = None logger.debug( "BLE address changed from %s to %s; closing connection", self.device.address, @@ -322,7 +329,9 @@ async def _ensure_connected(self): self.device, self.name, self._async_disconnected, + cached_services=self._cached_services, ) + self._cached_services = self.client.services logger.debug( "%s: Connected, processing subscriptions: %s", self.name, @@ -429,7 +438,9 @@ async def _async_fetch_gatt_database(self) -> Accessories: logger.debug("%s: Fetching GATT database", self.name) accessory = Accessory() accessory.aid = 1 - for service in self.client.services: + # Never use the cache when fetching the GATT database + services = await self.client.get_services() + for service in services: s = accessory.add_service(normalize_uuid(service.uuid)) for char in service.characteristics: diff --git a/poetry.lock b/poetry.lock index 8486be1a..76ea67c6 100644 --- a/poetry.lock +++ b/poetry.lock @@ -121,7 +121,7 @@ uvloop = ["uvloop (>=0.15.2)"] [[package]] name = "bleak" -version = "0.14.3" +version = "0.15.1" description = "Bluetooth Low Energy platform Agnostic Klient" category = "main" optional = false @@ -133,10 +133,11 @@ dbus-next = {version = "*", markers = "platform_system == \"Linux\""} pyobjc-core = {version = "*", markers = "platform_system == \"Darwin\""} pyobjc-framework-CoreBluetooth = {version = "*", markers = "platform_system == \"Darwin\""} pyobjc-framework-libdispatch = {version = "*", markers = "platform_system == \"Darwin\""} +typing-extensions = ">=4.2.0" [[package]] name = "bleak-retry-connector" -version = "1.5.0" +version = "1.7.0" description = "A connector for Bleak Clients that handles transient connection failures" category = "main" optional = false @@ -144,7 +145,7 @@ python-versions = ">=3.9,<4.0" [package.dependencies] async-timeout = ">=4.0.1" -bleak = ">=0.14.3" +bleak = ">=0.15.1" [package.extras] docs = ["sphinx-rtd-theme (>=1.0,<2.0)", "myst-parser (>=0.18,<0.19)", "Sphinx (>=5.0,<6.0)"] @@ -698,7 +699,7 @@ python-versions = ">=3.6,<4.0" name = "typing-extensions" version = "4.3.0" description = "Backported and Experimental Type Hints for Python 3.7+" -category = "dev" +category = "main" optional = false python-versions = ">=3.7" @@ -749,7 +750,7 @@ ifaddr = ">=0.1.7" [metadata] lock-version = "1.1" python-versions = "^3.9" -content-hash = "02d4e3f16b140ea6476f0ab44562a76534582960ec67fe3d551106844a4d3b33" +content-hash = "12be644545d6f7b626fbe121721d455d203d53c4a8755041c77f381174a51310" [metadata.files] aiocoap = [ @@ -879,12 +880,12 @@ black = [ {file = "black-22.6.0.tar.gz", hash = "sha256:6c6d39e28aed379aec40da1c65434c77d75e65bb59a1e1c283de545fb4e7c6c9"}, ] bleak = [ - {file = "bleak-0.14.3-py2.py3-none-any.whl", hash = "sha256:1740c09039e58a65023a3c7141609bff1300dff2b5f7ab103a3218063803e965"}, - {file = "bleak-0.14.3.tar.gz", hash = "sha256:760e5bb1e804087f762576b9b9563d63b47d521b1652988e28c3cb5e64b61990"}, + {file = "bleak-0.15.1-py2.py3-none-any.whl", hash = "sha256:af90ac301a723433460cb56215dd00f687281fa397395c2faadd9e59741ba3b4"}, + {file = "bleak-0.15.1.tar.gz", hash = "sha256:d8c8d88de0f22a15bd135ba056c4e5b2fb9f15119283def21b1ed7d43c00d590"}, ] bleak-retry-connector = [ - {file = "bleak-retry-connector-1.5.0.tar.gz", hash = "sha256:e12773df9b663149843d5ebf40cd808ccaa804939fdf5dfdc1c76d495849e5bb"}, - {file = "bleak_retry_connector-1.5.0-py3-none-any.whl", hash = "sha256:a66a5beb2a9b52592d5f9b3df90f073ba449699fda5271d44ad428f53edbe925"}, + {file = "bleak-retry-connector-1.7.0.tar.gz", hash = "sha256:8d2448ae8db42bab01d7ba510430dee60a2c63a3fb21df60d5e935d00f9da489"}, + {file = "bleak_retry_connector-1.7.0-py3-none-any.whl", hash = "sha256:b77ac37e62cab42ac299ce9d286de981547ca4dfb666b097a520356fa9a0e9ba"}, ] bleak-winrt = [ {file = "bleak-winrt-1.1.1.tar.gz", hash = "sha256:3e7765f98d71b5229d95bd3b197931994dead40f3144e7186de7b8f664e26df0"}, diff --git a/pyproject.toml b/pyproject.toml index b261546a..c1f9cf70 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -26,7 +26,7 @@ commentjson = "^0.9.0" aiocoap = ">=0.4.1" bleak = ">=0.14.2" chacha20poly1305-reuseable = ">=0.0.4" -bleak-retry-connector = ">=1.5.0" +bleak-retry-connector = ">=1.7.0" orjson = ">=3.7.8" [tool.poetry.dev-dependencies]