diff --git a/bumble/gatt_client.py b/bumble/gatt_client.py index 35d8eb7b..a33039ed 100644 --- a/bumble/gatt_client.py +++ b/bumble/gatt_client.py @@ -63,6 +63,7 @@ from .gatt import ( GATT_PRIMARY_SERVICE_ATTRIBUTE_TYPE, GATT_REQUEST_TIMEOUT, GATT_SECONDARY_SERVICE_ATTRIBUTE_TYPE, + GATT_INCLUDE_ATTRIBUTE_TYPE, Characteristic, ClientCharacteristicConfigurationBits, ) @@ -109,6 +110,7 @@ class AttributeProxy(EventEmitter): class ServiceProxy(AttributeProxy): uuid: UUID characteristics: List[CharacteristicProxy] + included_services: List[ServiceProxy] @staticmethod def from_client(service_class, client, service_uuid): @@ -502,12 +504,69 @@ class Client: return services - async def discover_included_services(self, _service): + async def discover_included_services( + self, service: ServiceProxy + ) -> 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( + '