Skip to content

Commit

Permalink
Add support for bleak max_write_without_response_size and current ble…
Browse files Browse the repository at this point in the history
…ak MTU (#141)
  • Loading branch information
bdraco authored Aug 9, 2022
1 parent 1eec367 commit c7b42ca
Show file tree
Hide file tree
Showing 3 changed files with 52 additions and 14 deletions.
56 changes: 47 additions & 9 deletions aiohomekit/controller/ble/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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]:
Expand All @@ -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)
Expand All @@ -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)
Expand All @@ -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)

Expand All @@ -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,
):
Expand All @@ -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(
Expand Down Expand Up @@ -188,7 +226,7 @@ async def drive_pairing_state_machine(
client,
None,
None,
char.handle,
char,
iid,
body,
)
Expand Down
4 changes: 2 additions & 2 deletions aiohomekit/controller/ble/discovery.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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
Expand Down
6 changes: 3 additions & 3 deletions aiohomekit/controller/ble/pairing.py
Original file line number Diff line number Diff line change
Expand Up @@ -276,7 +276,7 @@ async def _async_request_under_lock(
self._encryption_key,
self._decryption_key,
opcode,
endpoint.handle,
endpoint,
char.iid,
data,
)
Expand Down Expand Up @@ -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:
Expand Down

0 comments on commit c7b42ca

Please sign in to comment.