diff --git a/bumble/device.py b/bumble/device.py index 72fd755..258a43d 100644 --- a/bumble/device.py +++ b/bumble/device.py @@ -889,7 +889,7 @@ def host_event_handler(function): # List of host event handlers for the Device class. # (we define this list outside the class, because referencing a class in method # decorators is not straightforward) -device_host_event_handlers: list[str] = [] +device_host_event_handlers: List[str] = [] # ----------------------------------------------------------------------------- diff --git a/bumble/gatt.py b/bumble/gatt.py index e57f0a6..d99df81 100644 --- a/bumble/gatt.py +++ b/bumble/gatt.py @@ -205,8 +205,16 @@ class Service(Attribute): ''' uuid: UUID + characteristics: List[Characteristic] + included_services: List[Service] - def __init__(self, uuid, characteristics: list[Characteristic], primary=True): + def __init__( + self, + uuid, + characteristics: List[Characteristic], + included_services: List[Service] = [], + primary=True, + ): # Convert the uuid to a UUID object if it isn't already if isinstance(uuid, str): uuid = UUID(uuid) @@ -219,7 +227,7 @@ class Service(Attribute): uuid.to_pdu_bytes(), ) self.uuid = uuid - # self.included_services = [] + self.included_services = included_services[:] self.characteristics = characteristics[:] self.primary = primary @@ -253,6 +261,33 @@ class TemplateService(Service): super().__init__(self.UUID, characteristics, primary) +# ----------------------------------------------------------------------------- +class IncludedServiceDeclaration(Attribute): + ''' + See Vol 3, Part G - 3.2 INCLUDE DEFINITION + ''' + + service: Service + + def __init__(self, service): + declaration_bytes = struct.pack( + ' List[ServiceProxy]: ''' See Vol 3, Part G - 4.5.1 Find Included Services ''' - # TODO - return [] + + starting_handle = service.handle + ending_handle = service.end_group_handle + + included_services: List[ServiceProxy] = [] + while starting_handle <= ending_handle: + response = await self.send_request( + ATT_Read_By_Type_Request( + starting_handle=starting_handle, + ending_handle=ending_handle, + attribute_type=GATT_INCLUDE_ATTRIBUTE_TYPE, + ) + ) + if response is None: + # TODO raise appropriate exception + return [] + + # Check if we reached the end of the iteration + if response.op_code == ATT_ERROR_RESPONSE: + if response.error_code != ATT_ATTRIBUTE_NOT_FOUND_ERROR: + # Unexpected end + logger.warning( + '!!! unexpected error while discovering included services: ' + f'{HCI_Constant.error_name(response.error_code)}' + ) + raise ATT_Error( + error_code=response.error_code, + message='Unexpected error while discovering included services', + ) + break + + # Stop if for some reason the list was empty + if not response.attributes: + break + + # Process all included services returned in this iteration + for attribute_handle, attribute_value in response.attributes: + if attribute_handle < starting_handle: + # Something's not right + logger.warning(f'bogus handle value: {attribute_handle}') + return [] + + group_starting_handle, group_ending_handle = struct.unpack_from( + '