diff --git a/aiohomekit/controller/ble/client.py b/aiohomekit/controller/ble/client.py index a4085072..7ea951ad 100644 --- a/aiohomekit/controller/ble/client.py +++ b/aiohomekit/controller/ble/client.py @@ -21,6 +21,7 @@ from typing import Any, Callable, TypeVar, cast from bleak import BleakClient +from bleak.backends.characteristic import BleakGATTCharacteristic from aiohomekit.controller.ble.key import DecryptionKey, EncryptionKey from aiohomekit.exceptions import EncryptionError @@ -76,7 +77,7 @@ async def ble_request( encryption_key: EncryptionKey | None, decryption_key: DecryptionKey | None, opcode: OpCode, - handle: int, + handle: BleakGATTCharacteristic, iid: int, data: bytes | None = None, ) -> tuple[PDUStatus, bytes]: @@ -85,19 +86,53 @@ async def ble_request( # We think there is a 3 byte overhead for ATT # https://github.com/jlusiardi/homekit_python/issues/211#issuecomment-996751939 # But we haven't confirmed that this isn't already taken into account + debug_enabled = logger.isEnabledFor(logging.DEBUG) + + # Newer bleak, not currently released + if max_write_without_response_size := getattr( + handle, "max_write_without_response_size", None + ): + if debug_enabled: + logger.debug( + "max_write_without_response_size: %s, mtu_size-3: %s", + max_write_without_response_size, + client.mtu_size - 3, + ) + fragment_size = max(max_write_without_response_size, client.mtu_size - 3) + # Bleak 0.15.1 and below + elif ( + (char_obj := getattr(handle, "obj", None)) + and isinstance(char_obj, dict) + and (char_mtu := char_obj.get("MTU")) + ): + if debug_enabled: + logger.debug( + "bleak obj MTU: %s, mtu_size-3: %s", + char_mtu, + client.mtu_size - 3, + ) + fragment_size = max(char_mtu - 3, client.mtu_size - 3) + else: + if debug_enabled: + logger.debug( + "no bleak obj MTU or max_write_without_response_size, using mtu_size-3: %s", + client.mtu_size - 3, + ) + fragment_size = client.mtu_size - 3 - fragment_size = client.mtu_size - 3 if encryption_key: # Secure session means an extra 16 bytes of overhead fragment_size -= 16 - logger.debug("Using fragment size: %s", fragment_size) + if debug_enabled: + logger.debug("Using fragment size: %s", fragment_size) # Wrap data in one or more PDU's split at fragment_size # And write each one to the target characterstic handle writes = [] for data in encode_pdu(opcode, tid, iid, data, fragment_size): - logger.debug("Queuing fragment for write: %s", data) + if debug_enabled: + logger.debug("Queuing fragment for write: %s", data) if encryption_key: data = encryption_key.encrypt(data) writes.append(data) @@ -110,7 +145,9 @@ async def ble_request( data = decryption_key.decrypt(data) if data is False: raise EncryptionError("Decryption failed") - logger.debug("Read fragment: %s", data) + + if debug_enabled: + logger.debug("Read fragment: %s", data) # Validate the PDU header status, expected_length, data = decode_pdu(tid, data) @@ -127,7 +164,8 @@ async def ble_request( next = decryption_key.decrypt(next) if next is False: raise EncryptionError("Decryption failed") - logger.debug("Read fragment: %s", next) + if debug_enabled: + logger.debug("Read fragment: %s", next) data += decode_pdu_continuation(tid, next) @@ -138,7 +176,7 @@ async def char_write( client: BleakClient, encryption_key: EncryptionKey | None, decryption_key: DecryptionKey | None, - handle: int, + handle: BleakGATTCharacteristic, iid: int, body: bytes, ): @@ -158,7 +196,7 @@ async def char_read( client: BleakClient, encryption_key: EncryptionKey | None, decryption_key: DecryptionKey | None, - handle: int, + handle: BleakGATTCharacteristic, iid: int, ): pdu_status, data = await ble_request( @@ -188,7 +226,7 @@ async def drive_pairing_state_machine( client, None, None, - char.handle, + char, iid, body, ) diff --git a/aiohomekit/controller/ble/discovery.py b/aiohomekit/controller/ble/discovery.py index 52a3c757..22bd38f7 100644 --- a/aiohomekit/controller/ble/discovery.py +++ b/aiohomekit/controller/ble/discovery.py @@ -111,7 +111,7 @@ async def _async_start_pairing(self, alias: str) -> tuple[bytearray, bytearray]: CharacteristicsTypes.PAIRING_FEATURES, ) ff_iid = await self.client.get_characteristic_iid(ff_char) - ff_raw = await char_read(self.client, None, None, ff_char.handle, ff_iid) + ff_raw = await char_read(self.client, None, None, ff_char, ff_iid) ff = FeatureFlags(ff_raw[0]) return await drive_pairing_state_machine( self.client, @@ -182,7 +182,7 @@ async def async_identify(self) -> None: ) iid = await self.client.get_characteristic_iid(char) - await char_write(self.client, None, None, char.handle, iid, b"\x01") + await char_write(self.client, None, None, char, iid, b"\x01") def _async_process_advertisement( self, device: BLEDevice, description: HomeKitAdvertisement diff --git a/aiohomekit/controller/ble/pairing.py b/aiohomekit/controller/ble/pairing.py index 4ca4a7e4..33368bc7 100644 --- a/aiohomekit/controller/ble/pairing.py +++ b/aiohomekit/controller/ble/pairing.py @@ -276,7 +276,7 @@ async def _async_request_under_lock( self._encryption_key, self._decryption_key, opcode, - endpoint.handle, + endpoint, char.iid, data, ) @@ -448,12 +448,12 @@ async def _async_fetch_gatt_database(self) -> Accessories: iid, ): await self.client.write_gatt_char( - char.handle, + char, data, "write-without-response" not in char.properties, ) - payload = await self.client.read_gatt_char(char.handle) + payload = await self.client.read_gatt_char(char) status, _, signature = decode_pdu(tid, payload) if status != PDUStatus.SUCCESS: