diff --git a/bumble/device.py b/bumble/device.py index 4b3fa431..97503bba 100644 --- a/bumble/device.py +++ b/bumble/device.py @@ -294,8 +294,8 @@ async def discover_descriptors(self, characteristic = None, start_handle = None, async def discover_attributes(self): return await self.gatt_client.discover_attributes() - async def subscribe(self, characteristic, subscriber=None): - return await self.gatt_client.subscribe(characteristic, subscriber) + async def subscribe(self, characteristic, subscriber=None, prefer_notify=True): + return await self.gatt_client.subscribe(characteristic, subscriber, prefer_notify) async def unsubscribe(self, characteristic, subscriber=None): return await self.gatt_client.unsubscribe(characteristic, subscriber) diff --git a/bumble/gatt_client.py b/bumble/gatt_client.py index bce0a327..5ea75b84 100644 --- a/bumble/gatt_client.py +++ b/bumble/gatt_client.py @@ -26,20 +26,17 @@ import asyncio import logging import struct + from colors import color -from .core import ProtocolError, TimeoutError -from .hci import * from .att import * -from .gatt import ( - GATT_CLIENT_CHARACTERISTIC_CONFIGURATION_DESCRIPTOR, - GATT_REQUEST_TIMEOUT, - GATT_PRIMARY_SERVICE_ATTRIBUTE_TYPE, - GATT_SECONDARY_SERVICE_ATTRIBUTE_TYPE, - GATT_CHARACTERISTIC_ATTRIBUTE_TYPE, - Characteristic, - ClientCharacteristicConfigurationBits -) +from .core import InvalidStateError, ProtocolError, TimeoutError +from .gatt import (GATT_CHARACTERISTIC_ATTRIBUTE_TYPE, + GATT_CLIENT_CHARACTERISTIC_CONFIGURATION_DESCRIPTOR, + GATT_PRIMARY_SERVICE_ATTRIBUTE_TYPE, GATT_REQUEST_TIMEOUT, + GATT_SECONDARY_SERVICE_ATTRIBUTE_TYPE, Characteristic, + ClientCharacteristicConfigurationBits) +from .hci import * # ----------------------------------------------------------------------------- # Logging @@ -115,7 +112,7 @@ def get_descriptor(self, descriptor_type): async def discover_descriptors(self): return await self.client.discover_descriptors(self) - async def subscribe(self, subscriber=None): + async def subscribe(self, subscriber=None, prefer_notify=True): if subscriber is not None: if subscriber in self.subscribers: # We already have a proxy subscriber @@ -129,7 +126,7 @@ def on_change(value): self.subscribers[subscriber] = on_change subscriber = on_change - return await self.client.subscribe(self, subscriber) + return await self.client.subscribe(self, subscriber, prefer_notify) async def unsubscribe(self, subscriber=None): if subscriber in self.subscribers: @@ -547,7 +544,7 @@ async def discover_attributes(self): return attributes - async def subscribe(self, characteristic, subscriber=None): + async def subscribe(self, characteristic, subscriber=None, prefer_notify=True): # If we haven't already discovered the descriptors for this characteristic, do it now if not characteristic.descriptors_discovered: await self.discover_descriptors(characteristic) @@ -558,23 +555,31 @@ async def subscribe(self, characteristic, subscriber=None): logger.warning('subscribing to characteristic with no CCCD descriptor') return - # Set the subscription bits and select the subscriber set - bits = ClientCharacteristicConfigurationBits.DEFAULT - subscriber_sets = [] - if characteristic.properties & Characteristic.NOTIFY: - bits |= ClientCharacteristicConfigurationBits.NOTIFICATION - subscriber_sets.append(self.notification_subscribers.setdefault(characteristic.handle, set())) - if characteristic.properties & Characteristic.INDICATE: - bits |= ClientCharacteristicConfigurationBits.INDICATION - subscriber_sets.append(self.indication_subscribers.setdefault(characteristic.handle, set())) + def characteristic_is_notify(characteristic, prefer_notify): + if ( + characteristic.properties & (Characteristic.NOTIFY | Characteristic.INDICATE) + ) == (Characteristic.NOTIFY | Characteristic.INDICATE): + return True if prefer_notify else False + elif characteristic.properties & Characteristic.NOTIFY: + return True + elif characteristic.properties & Characteristic.INDICATE: + return False + else: + raise InvalidStateError('characteristic is not notify or indicate') + + bits, subscribers = ( + (ClientCharacteristicConfigurationBits.NOTIFICATION, self.notification_subscribers) + if characteristic_is_notify(characteristic, prefer_notify) + else (ClientCharacteristicConfigurationBits.INDICATION, self.indication_subscribers) + ) # Add subscribers to the sets - for subscriber_set in subscriber_sets: - if subscriber is not None: - subscriber_set.add(subscriber) - # Add the characteristic as a subscriber, which will result in the characteristic - # emitting an 'update' event when a notification or indication is received - subscriber_set.add(characteristic) + subscriber_set = subscribers.setdefault(characteristic.handle, set()) + if subscriber is not None: + subscriber_set.add(subscriber) + # Add the characteristic as a subscriber, which will result in the characteristic + # emitting an 'update' event when a notification or indication is received + subscriber_set.add(characteristic) await self.write_value(cccd, struct.pack('