1# Copyright 2021-2022 Google LLC
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7#      https://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14
15# -----------------------------------------------------------------------------
16# GATT - Generic Attribute Profile
17#
18# See Bluetooth spec @ Vol 3, Part G
19#
20# -----------------------------------------------------------------------------
21
22# -----------------------------------------------------------------------------
23# Imports
24# -----------------------------------------------------------------------------
25from __future__ import annotations
26import enum
27import functools
28import logging
29import struct
30from typing import (
31    Callable,
32    Dict,
33    Iterable,
34    List,
35    Optional,
36    Sequence,
37    Union,
38    TYPE_CHECKING,
39)
40
41from bumble.colors import color
42from bumble.core import BaseBumbleError, UUID
43from bumble.att import Attribute, AttributeValue
44
45if TYPE_CHECKING:
46    from bumble.gatt_client import AttributeProxy
47    from bumble.device import Connection
48
49
50# -----------------------------------------------------------------------------
51# Logging
52# -----------------------------------------------------------------------------
53logger = logging.getLogger(__name__)
54
55# -----------------------------------------------------------------------------
56# Constants
57# -----------------------------------------------------------------------------
58# fmt: off
59# pylint: disable=line-too-long
60
61GATT_REQUEST_TIMEOUT = 30  # seconds
62
63GATT_MAX_ATTRIBUTE_VALUE_SIZE = 512
64
65# Services
66GATT_GENERIC_ACCESS_SERVICE                 = UUID.from_16_bits(0x1800, 'Generic Access')
67GATT_GENERIC_ATTRIBUTE_SERVICE              = UUID.from_16_bits(0x1801, 'Generic Attribute')
68GATT_IMMEDIATE_ALERT_SERVICE                = UUID.from_16_bits(0x1802, 'Immediate Alert')
69GATT_LINK_LOSS_SERVICE                      = UUID.from_16_bits(0x1803, 'Link Loss')
70GATT_TX_POWER_SERVICE                       = UUID.from_16_bits(0x1804, 'TX Power')
71GATT_CURRENT_TIME_SERVICE                   = UUID.from_16_bits(0x1805, 'Current Time')
72GATT_REFERENCE_TIME_UPDATE_SERVICE          = UUID.from_16_bits(0x1806, 'Reference Time Update')
73GATT_NEXT_DST_CHANGE_SERVICE                = UUID.from_16_bits(0x1807, 'Next DST Change')
74GATT_GLUCOSE_SERVICE                        = UUID.from_16_bits(0x1808, 'Glucose')
75GATT_HEALTH_THERMOMETER_SERVICE             = UUID.from_16_bits(0x1809, 'Health Thermometer')
76GATT_DEVICE_INFORMATION_SERVICE             = UUID.from_16_bits(0x180A, 'Device Information')
77GATT_HEART_RATE_SERVICE                     = UUID.from_16_bits(0x180D, 'Heart Rate')
78GATT_PHONE_ALERT_STATUS_SERVICE             = UUID.from_16_bits(0x180E, 'Phone Alert Status')
79GATT_BATTERY_SERVICE                        = UUID.from_16_bits(0x180F, 'Battery')
80GATT_BLOOD_PRESSURE_SERVICE                 = UUID.from_16_bits(0x1810, 'Blood Pressure')
81GATT_ALERT_NOTIFICATION_SERVICE             = UUID.from_16_bits(0x1811, 'Alert Notification')
82GATT_HUMAN_INTERFACE_DEVICE_SERVICE         = UUID.from_16_bits(0x1812, 'Human Interface Device')
83GATT_SCAN_PARAMETERS_SERVICE                = UUID.from_16_bits(0x1813, 'Scan Parameters')
84GATT_RUNNING_SPEED_AND_CADENCE_SERVICE      = UUID.from_16_bits(0x1814, 'Running Speed and Cadence')
85GATT_AUTOMATION_IO_SERVICE                  = UUID.from_16_bits(0x1815, 'Automation IO')
86GATT_CYCLING_SPEED_AND_CADENCE_SERVICE      = UUID.from_16_bits(0x1816, 'Cycling Speed and Cadence')
87GATT_CYCLING_POWER_SERVICE                  = UUID.from_16_bits(0x1818, 'Cycling Power')
88GATT_LOCATION_AND_NAVIGATION_SERVICE        = UUID.from_16_bits(0x1819, 'Location and Navigation')
89GATT_ENVIRONMENTAL_SENSING_SERVICE          = UUID.from_16_bits(0x181A, 'Environmental Sensing')
90GATT_BODY_COMPOSITION_SERVICE               = UUID.from_16_bits(0x181B, 'Body Composition')
91GATT_USER_DATA_SERVICE                      = UUID.from_16_bits(0x181C, 'User Data')
92GATT_WEIGHT_SCALE_SERVICE                   = UUID.from_16_bits(0x181D, 'Weight Scale')
93GATT_BOND_MANAGEMENT_SERVICE                = UUID.from_16_bits(0x181E, 'Bond Management')
94GATT_CONTINUOUS_GLUCOSE_MONITORING_SERVICE  = UUID.from_16_bits(0x181F, 'Continuous Glucose Monitoring')
95GATT_INTERNET_PROTOCOL_SUPPORT_SERVICE      = UUID.from_16_bits(0x1820, 'Internet Protocol Support')
96GATT_INDOOR_POSITIONING_SERVICE             = UUID.from_16_bits(0x1821, 'Indoor Positioning')
97GATT_PULSE_OXIMETER_SERVICE                 = UUID.from_16_bits(0x1822, 'Pulse Oximeter')
98GATT_HTTP_PROXY_SERVICE                     = UUID.from_16_bits(0x1823, 'HTTP Proxy')
99GATT_TRANSPORT_DISCOVERY_SERVICE            = UUID.from_16_bits(0x1824, 'Transport Discovery')
100GATT_OBJECT_TRANSFER_SERVICE                = UUID.from_16_bits(0x1825, 'Object Transfer')
101GATT_FITNESS_MACHINE_SERVICE                = UUID.from_16_bits(0x1826, 'Fitness Machine')
102GATT_MESH_PROVISIONING_SERVICE              = UUID.from_16_bits(0x1827, 'Mesh Provisioning')
103GATT_MESH_PROXY_SERVICE                     = UUID.from_16_bits(0x1828, 'Mesh Proxy')
104GATT_RECONNECTION_CONFIGURATION_SERVICE     = UUID.from_16_bits(0x1829, 'Reconnection Configuration')
105GATT_INSULIN_DELIVERY_SERVICE               = UUID.from_16_bits(0x183A, 'Insulin Delivery')
106GATT_BINARY_SENSOR_SERVICE                  = UUID.from_16_bits(0x183B, 'Binary Sensor')
107GATT_EMERGENCY_CONFIGURATION_SERVICE        = UUID.from_16_bits(0x183C, 'Emergency Configuration')
108GATT_AUTHORIZATION_CONTROL_SERVICE          = UUID.from_16_bits(0x183D, 'Authorization Control')
109GATT_PHYSICAL_ACTIVITY_MONITOR_SERVICE      = UUID.from_16_bits(0x183E, 'Physical Activity Monitor')
110GATT_ELAPSED_TIME_SERVICE                   = UUID.from_16_bits(0x183F, 'Elapsed Time')
111GATT_GENERIC_HEALTH_SENSOR_SERVICE          = UUID.from_16_bits(0x1840, 'Generic Health Sensor')
112GATT_AUDIO_INPUT_CONTROL_SERVICE            = UUID.from_16_bits(0x1843, 'Audio Input Control')
113GATT_VOLUME_CONTROL_SERVICE                 = UUID.from_16_bits(0x1844, 'Volume Control')
114GATT_VOLUME_OFFSET_CONTROL_SERVICE          = UUID.from_16_bits(0x1845, 'Volume Offset Control')
115GATT_COORDINATED_SET_IDENTIFICATION_SERVICE = UUID.from_16_bits(0x1846, 'Coordinated Set Identification')
116GATT_DEVICE_TIME_SERVICE                    = UUID.from_16_bits(0x1847, 'Device Time')
117GATT_MEDIA_CONTROL_SERVICE                  = UUID.from_16_bits(0x1848, 'Media Control')
118GATT_GENERIC_MEDIA_CONTROL_SERVICE          = UUID.from_16_bits(0x1849, 'Generic Media Control')
119GATT_CONSTANT_TONE_EXTENSION_SERVICE        = UUID.from_16_bits(0x184A, 'Constant Tone Extension')
120GATT_TELEPHONE_BEARER_SERVICE               = UUID.from_16_bits(0x184B, 'Telephone Bearer')
121GATT_GENERIC_TELEPHONE_BEARER_SERVICE       = UUID.from_16_bits(0x184C, 'Generic Telephone Bearer')
122GATT_MICROPHONE_CONTROL_SERVICE             = UUID.from_16_bits(0x184D, 'Microphone Control')
123GATT_AUDIO_STREAM_CONTROL_SERVICE           = UUID.from_16_bits(0x184E, 'Audio Stream Control')
124GATT_BROADCAST_AUDIO_SCAN_SERVICE           = UUID.from_16_bits(0x184F, 'Broadcast Audio Scan')
125GATT_PUBLISHED_AUDIO_CAPABILITIES_SERVICE   = UUID.from_16_bits(0x1850, 'Published Audio Capabilities')
126GATT_BASIC_AUDIO_ANNOUNCEMENT_SERVICE       = UUID.from_16_bits(0x1851, 'Basic Audio Announcement')
127GATT_BROADCAST_AUDIO_ANNOUNCEMENT_SERVICE   = UUID.from_16_bits(0x1852, 'Broadcast Audio Announcement')
128GATT_COMMON_AUDIO_SERVICE                   = UUID.from_16_bits(0x1853, 'Common Audio')
129GATT_HEARING_ACCESS_SERVICE                 = UUID.from_16_bits(0x1854, 'Hearing Access')
130GATT_TELEPHONY_AND_MEDIA_AUDIO_SERVICE      = UUID.from_16_bits(0x1855, 'Telephony and Media Audio')
131GATT_PUBLIC_BROADCAST_ANNOUNCEMENT_SERVICE  = UUID.from_16_bits(0x1856, 'Public Broadcast Announcement')
132GATT_ELECTRONIC_SHELF_LABEL_SERVICE         = UUID.from_16_bits(0X1857, 'Electronic Shelf Label')
133GATT_GAMING_AUDIO_SERVICE                   = UUID.from_16_bits(0x1858, 'Gaming Audio')
134GATT_MESH_PROXY_SOLICITATION_SERVICE        = UUID.from_16_bits(0x1859, 'Mesh Audio Solicitation')
135
136# Attribute Types
137GATT_PRIMARY_SERVICE_ATTRIBUTE_TYPE   = UUID.from_16_bits(0x2800, 'Primary Service')
138GATT_SECONDARY_SERVICE_ATTRIBUTE_TYPE = UUID.from_16_bits(0x2801, 'Secondary Service')
139GATT_INCLUDE_ATTRIBUTE_TYPE           = UUID.from_16_bits(0x2802, 'Include')
140GATT_CHARACTERISTIC_ATTRIBUTE_TYPE    = UUID.from_16_bits(0x2803, 'Characteristic')
141
142# Descriptors
143GATT_CHARACTERISTIC_EXTENDED_PROPERTIES_DESCRIPTOR   = UUID.from_16_bits(0x2900, 'Characteristic Extended Properties')
144GATT_CHARACTERISTIC_USER_DESCRIPTION_DESCRIPTOR      = UUID.from_16_bits(0x2901, 'Characteristic User Description')
145GATT_CLIENT_CHARACTERISTIC_CONFIGURATION_DESCRIPTOR  = UUID.from_16_bits(0x2902, 'Client Characteristic Configuration')
146GATT_SERVER_CHARACTERISTIC_CONFIGURATION_DESCRIPTOR  = UUID.from_16_bits(0x2903, 'Server Characteristic Configuration')
147GATT_CHARACTERISTIC_PRESENTATION_FORMAT_DESCRIPTOR   = UUID.from_16_bits(0x2904, 'Characteristic Format')
148GATT_CHARACTERISTIC_AGGREGATE_FORMAT_DESCRIPTOR      = UUID.from_16_bits(0x2905, 'Characteristic Aggregate Format')
149GATT_VALID_RANGE_DESCRIPTOR                          = UUID.from_16_bits(0x2906, 'Valid Range')
150GATT_EXTERNAL_REPORT_DESCRIPTOR                      = UUID.from_16_bits(0x2907, 'External Report')
151GATT_REPORT_REFERENCE_DESCRIPTOR                     = UUID.from_16_bits(0x2908, 'Report Reference')
152GATT_NUMBER_OF_DIGITALS_DESCRIPTOR                   = UUID.from_16_bits(0x2909, 'Number of Digitals')
153GATT_VALUE_TRIGGER_SETTING_DESCRIPTOR                = UUID.from_16_bits(0x290A, 'Value Trigger Setting')
154GATT_ENVIRONMENTAL_SENSING_CONFIGURATION_DESCRIPTOR  = UUID.from_16_bits(0x290B, 'Environmental Sensing Configuration')
155GATT_ENVIRONMENTAL_SENSING_MEASUREMENT_DESCRIPTOR    = UUID.from_16_bits(0x290C, 'Environmental Sensing Measurement')
156GATT_ENVIRONMENTAL_SENSING_TRIGGER_DESCRIPTOR        = UUID.from_16_bits(0x290D, 'Environmental Sensing Trigger Setting')
157GATT_TIME_TRIGGER_DESCRIPTOR                         = UUID.from_16_bits(0x290E, 'Time Trigger Setting')
158GATT_COMPLETE_BR_EDR_TRANSPORT_BLOCK_DATA_DESCRIPTOR = UUID.from_16_bits(0x290F, 'Complete BR-EDR Transport Block Data')
159GATT_OBSERVATION_SCHEDULE_DESCRIPTOR                 = UUID.from_16_bits(0x290F, 'Observation Schedule')
160GATT_VALID_RANGE_AND_ACCURACY_DESCRIPTOR             = UUID.from_16_bits(0x290F, 'Valid Range And Accuracy')
161
162# Device Information Service
163GATT_SYSTEM_ID_CHARACTERISTIC                          = UUID.from_16_bits(0x2A23, 'System ID')
164GATT_MODEL_NUMBER_STRING_CHARACTERISTIC                = UUID.from_16_bits(0x2A24, 'Model Number String')
165GATT_SERIAL_NUMBER_STRING_CHARACTERISTIC               = UUID.from_16_bits(0x2A25, 'Serial Number String')
166GATT_FIRMWARE_REVISION_STRING_CHARACTERISTIC           = UUID.from_16_bits(0x2A26, 'Firmware Revision String')
167GATT_HARDWARE_REVISION_STRING_CHARACTERISTIC           = UUID.from_16_bits(0x2A27, 'Hardware Revision String')
168GATT_SOFTWARE_REVISION_STRING_CHARACTERISTIC           = UUID.from_16_bits(0x2A28, 'Software Revision String')
169GATT_MANUFACTURER_NAME_STRING_CHARACTERISTIC           = UUID.from_16_bits(0x2A29, 'Manufacturer Name String')
170GATT_REGULATORY_CERTIFICATION_DATA_LIST_CHARACTERISTIC = UUID.from_16_bits(0x2A2A, 'IEEE 11073-20601 Regulatory Certification Data List')
171GATT_PNP_ID_CHARACTERISTIC                             = UUID.from_16_bits(0x2A50, 'PnP ID')
172
173# Human Interface Device Service
174GATT_HID_INFORMATION_CHARACTERISTIC   = UUID.from_16_bits(0x2A4A, 'HID Information')
175GATT_REPORT_MAP_CHARACTERISTIC        = UUID.from_16_bits(0x2A4B, 'Report Map')
176GATT_HID_CONTROL_POINT_CHARACTERISTIC = UUID.from_16_bits(0x2A4C, 'HID Control Point')
177GATT_REPORT_CHARACTERISTIC            = UUID.from_16_bits(0x2A4D, 'Report')
178GATT_PROTOCOL_MODE_CHARACTERISTIC     = UUID.from_16_bits(0x2A4E, 'Protocol Mode')
179
180# Heart Rate Service
181GATT_HEART_RATE_MEASUREMENT_CHARACTERISTIC   = UUID.from_16_bits(0x2A37, 'Heart Rate Measurement')
182GATT_BODY_SENSOR_LOCATION_CHARACTERISTIC     = UUID.from_16_bits(0x2A38, 'Body Sensor Location')
183GATT_HEART_RATE_CONTROL_POINT_CHARACTERISTIC = UUID.from_16_bits(0x2A39, 'Heart Rate Control Point')
184
185# Battery Service
186GATT_BATTERY_LEVEL_CHARACTERISTIC = UUID.from_16_bits(0x2A19, 'Battery Level')
187
188# Telephony And Media Audio Service (TMAS)
189GATT_TMAP_ROLE_CHARACTERISTIC = UUID.from_16_bits(0x2B51, 'TMAP Role')
190
191# Audio Input Control Service (AICS)
192GATT_AUDIO_INPUT_STATE_CHARACTERISTIC         = UUID.from_16_bits(0x2B77, 'Audio Input State')
193GATT_GAIN_SETTINGS_ATTRIBUTE_CHARACTERISTIC   = UUID.from_16_bits(0x2B78, 'Gain Settings Attribute')
194GATT_AUDIO_INPUT_TYPE_CHARACTERISTIC          = UUID.from_16_bits(0x2B79, 'Audio Input Type')
195GATT_AUDIO_INPUT_STATUS_CHARACTERISTIC        = UUID.from_16_bits(0x2B7A, 'Audio Input Status')
196GATT_AUDIO_INPUT_CONTROL_POINT_CHARACTERISTIC = UUID.from_16_bits(0x2B7B, 'Audio Input Control Point')
197GATT_AUDIO_INPUT_DESCRIPTION_CHARACTERISTIC   = UUID.from_16_bits(0x2B7C, 'Audio Input Description')
198
199# Volume Control Service (VCS)
200GATT_VOLUME_STATE_CHARACTERISTIC                = UUID.from_16_bits(0x2B7D, 'Volume State')
201GATT_VOLUME_CONTROL_POINT_CHARACTERISTIC        = UUID.from_16_bits(0x2B7E, 'Volume Control Point')
202GATT_VOLUME_FLAGS_CHARACTERISTIC                = UUID.from_16_bits(0x2B7F, 'Volume Flags')
203
204# Volume Offset Control Service (VOCS)
205GATT_VOLUME_OFFSET_STATE_CHARACTERISTIC         = UUID.from_16_bits(0x2B80, 'Volume Offset State')
206GATT_AUDIO_LOCATION_CHARACTERISTIC              = UUID.from_16_bits(0x2B81, 'Audio Location')
207GATT_VOLUME_OFFSET_CONTROL_POINT_CHARACTERISTIC = UUID.from_16_bits(0x2B82, 'Volume Offset Control Point')
208GATT_AUDIO_OUTPUT_DESCRIPTION_CHARACTERISTIC    = UUID.from_16_bits(0x2B83, 'Audio Output Description')
209
210# Coordinated Set Identification Service (CSIS)
211GATT_SET_IDENTITY_RESOLVING_KEY_CHARACTERISTIC  = UUID.from_16_bits(0x2B84, 'Set Identity Resolving Key')
212GATT_COORDINATED_SET_SIZE_CHARACTERISTIC        = UUID.from_16_bits(0x2B85, 'Coordinated Set Size')
213GATT_SET_MEMBER_LOCK_CHARACTERISTIC             = UUID.from_16_bits(0x2B86, 'Set Member Lock')
214GATT_SET_MEMBER_RANK_CHARACTERISTIC             = UUID.from_16_bits(0x2B87, 'Set Member Rank')
215
216# Media Control Service (MCS)
217GATT_MEDIA_PLAYER_NAME_CHARACTERISTIC                     = UUID.from_16_bits(0x2B93, 'Media Player Name')
218GATT_MEDIA_PLAYER_ICON_OBJECT_ID_CHARACTERISTIC           = UUID.from_16_bits(0x2B94, 'Media Player Icon Object ID')
219GATT_MEDIA_PLAYER_ICON_URL_CHARACTERISTIC                 = UUID.from_16_bits(0x2B95, 'Media Player Icon URL')
220GATT_TRACK_CHANGED_CHARACTERISTIC                         = UUID.from_16_bits(0x2B96, 'Track Changed')
221GATT_TRACK_TITLE_CHARACTERISTIC                           = UUID.from_16_bits(0x2B97, 'Track Title')
222GATT_TRACK_DURATION_CHARACTERISTIC                        = UUID.from_16_bits(0x2B98, 'Track Duration')
223GATT_TRACK_POSITION_CHARACTERISTIC                        = UUID.from_16_bits(0x2B99, 'Track Position')
224GATT_PLAYBACK_SPEED_CHARACTERISTIC                        = UUID.from_16_bits(0x2B9A, 'Playback Speed')
225GATT_SEEKING_SPEED_CHARACTERISTIC                         = UUID.from_16_bits(0x2B9B, 'Seeking Speed')
226GATT_CURRENT_TRACK_SEGMENTS_OBJECT_ID_CHARACTERISTIC      = UUID.from_16_bits(0x2B9C, 'Current Track Segments Object ID')
227GATT_CURRENT_TRACK_OBJECT_ID_CHARACTERISTIC               = UUID.from_16_bits(0x2B9D, 'Current Track Object ID')
228GATT_NEXT_TRACK_OBJECT_ID_CHARACTERISTIC                  = UUID.from_16_bits(0x2B9E, 'Next Track Object ID')
229GATT_PARENT_GROUP_OBJECT_ID_CHARACTERISTIC                = UUID.from_16_bits(0x2B9F, 'Parent Group Object ID')
230GATT_CURRENT_GROUP_OBJECT_ID_CHARACTERISTIC               = UUID.from_16_bits(0x2BA0, 'Current Group Object ID')
231GATT_PLAYING_ORDER_CHARACTERISTIC                         = UUID.from_16_bits(0x2BA1, 'Playing Order')
232GATT_PLAYING_ORDERS_SUPPORTED_CHARACTERISTIC              = UUID.from_16_bits(0x2BA2, 'Playing Orders Supported')
233GATT_MEDIA_STATE_CHARACTERISTIC                           = UUID.from_16_bits(0x2BA3, 'Media State')
234GATT_MEDIA_CONTROL_POINT_CHARACTERISTIC                   = UUID.from_16_bits(0x2BA4, 'Media Control Point')
235GATT_MEDIA_CONTROL_POINT_OPCODES_SUPPORTED_CHARACTERISTIC = UUID.from_16_bits(0x2BA5, 'Media Control Point Opcodes Supported')
236GATT_SEARCH_RESULTS_OBJECT_ID_CHARACTERISTIC              = UUID.from_16_bits(0x2BA6, 'Search Results Object ID')
237GATT_SEARCH_CONTROL_POINT_CHARACTERISTIC                  = UUID.from_16_bits(0x2BA7, 'Search Control Point')
238GATT_CONTENT_CONTROL_ID_CHARACTERISTIC                    = UUID.from_16_bits(0x2BBA, 'Content Control Id')
239
240# Telephone Bearer Service (TBS)
241GATT_BEARER_PROVIDER_NAME_CHARACTERISTIC                      = UUID.from_16_bits(0x2BB3, 'Bearer Provider Name')
242GATT_BEARER_UCI_CHARACTERISTIC                                = UUID.from_16_bits(0x2BB4, 'Bearer UCI')
243GATT_BEARER_TECHNOLOGY_CHARACTERISTIC                         = UUID.from_16_bits(0x2BB5, 'Bearer Technology')
244GATT_BEARER_URI_SCHEMES_SUPPORTED_LIST_CHARACTERISTIC         = UUID.from_16_bits(0x2BB6, 'Bearer URI Schemes Supported List')
245GATT_BEARER_SIGNAL_STRENGTH_CHARACTERISTIC                    = UUID.from_16_bits(0x2BB7, 'Bearer Signal Strength')
246GATT_BEARER_SIGNAL_STRENGTH_REPORTING_INTERVAL_CHARACTERISTIC = UUID.from_16_bits(0x2BB8, 'Bearer Signal Strength Reporting Interval')
247GATT_BEARER_LIST_CURRENT_CALLS_CHARACTERISTIC                 = UUID.from_16_bits(0x2BB9, 'Bearer List Current Calls')
248GATT_CONTENT_CONTROL_ID_CHARACTERISTIC                        = UUID.from_16_bits(0x2BBA, 'Content Control ID')
249GATT_STATUS_FLAGS_CHARACTERISTIC                              = UUID.from_16_bits(0x2BBB, 'Status Flags')
250GATT_INCOMING_CALL_TARGET_BEARER_URI_CHARACTERISTIC           = UUID.from_16_bits(0x2BBC, 'Incoming Call Target Bearer URI')
251GATT_CALL_STATE_CHARACTERISTIC                                = UUID.from_16_bits(0x2BBD, 'Call State')
252GATT_CALL_CONTROL_POINT_CHARACTERISTIC                        = UUID.from_16_bits(0x2BBE, 'Call Control Point')
253GATT_CALL_CONTROL_POINT_OPTIONAL_OPCODES_CHARACTERISTIC       = UUID.from_16_bits(0x2BBF, 'Call Control Point Optional Opcodes')
254GATT_TERMINATION_REASON_CHARACTERISTIC                        = UUID.from_16_bits(0x2BC0, 'Termination Reason')
255GATT_INCOMING_CALL_CHARACTERISTIC                             = UUID.from_16_bits(0x2BC1, 'Incoming Call')
256GATT_CALL_FRIENDLY_NAME_CHARACTERISTIC                        = UUID.from_16_bits(0x2BC2, 'Call Friendly Name')
257
258# Microphone Control Service (MICS)
259GATT_MUTE_CHARACTERISTIC = UUID.from_16_bits(0x2BC3, 'Mute')
260
261# Audio Stream Control Service (ASCS)
262GATT_SINK_ASE_CHARACTERISTIC                    = UUID.from_16_bits(0x2BC4, 'Sink ASE')
263GATT_SOURCE_ASE_CHARACTERISTIC                  = UUID.from_16_bits(0x2BC5, 'Source ASE')
264GATT_ASE_CONTROL_POINT_CHARACTERISTIC           = UUID.from_16_bits(0x2BC6, 'ASE Control Point')
265
266# Broadcast Audio Scan Service (BASS)
267GATT_BROADCAST_AUDIO_SCAN_CONTROL_POINT_CHARACTERISTIC = UUID.from_16_bits(0x2BC7, 'Broadcast Audio Scan Control Point')
268GATT_BROADCAST_RECEIVE_STATE_CHARACTERISTIC            = UUID.from_16_bits(0x2BC8, 'Broadcast Receive State')
269
270# Published Audio Capabilities Service (PACS)
271GATT_SINK_PAC_CHARACTERISTIC                    = UUID.from_16_bits(0x2BC9, 'Sink PAC')
272GATT_SINK_AUDIO_LOCATION_CHARACTERISTIC         = UUID.from_16_bits(0x2BCA, 'Sink Audio Location')
273GATT_SOURCE_PAC_CHARACTERISTIC                  = UUID.from_16_bits(0x2BCB, 'Source PAC')
274GATT_SOURCE_AUDIO_LOCATION_CHARACTERISTIC       = UUID.from_16_bits(0x2BCC, 'Source Audio Location')
275GATT_AVAILABLE_AUDIO_CONTEXTS_CHARACTERISTIC    = UUID.from_16_bits(0x2BCD, 'Available Audio Contexts')
276GATT_SUPPORTED_AUDIO_CONTEXTS_CHARACTERISTIC    = UUID.from_16_bits(0x2BCE, 'Supported Audio Contexts')
277
278# Hearing Access Service
279GATT_HEARING_AID_FEATURES_CHARACTERISTIC             = UUID.from_16_bits(0x2BDA, 'Hearing Aid Features')
280GATT_HEARING_AID_PRESET_CONTROL_POINT_CHARACTERISTIC = UUID.from_16_bits(0x2BDB, 'Hearing Aid Preset Control Point')
281GATT_ACTIVE_PRESET_INDEX_CHARACTERISTIC              = UUID.from_16_bits(0x2BDC, 'Active Preset Index')
282
283# ASHA Service
284GATT_ASHA_SERVICE                             = UUID.from_16_bits(0xFDF0, 'Audio Streaming for Hearing Aid')
285GATT_ASHA_READ_ONLY_PROPERTIES_CHARACTERISTIC = UUID('6333651e-c481-4a3e-9169-7c902aad37bb', 'ReadOnlyProperties')
286GATT_ASHA_AUDIO_CONTROL_POINT_CHARACTERISTIC  = UUID('f0d4de7e-4a88-476c-9d9f-1937b0996cc0', 'AudioControlPoint')
287GATT_ASHA_AUDIO_STATUS_CHARACTERISTIC         = UUID('38663f1a-e711-4cac-b641-326b56404837', 'AudioStatus')
288GATT_ASHA_VOLUME_CHARACTERISTIC               = UUID('00e4ca9e-ab14-41e4-8823-f9e70c7e91df', 'Volume')
289GATT_ASHA_LE_PSM_OUT_CHARACTERISTIC           = UUID('2d410339-82b6-42aa-b34e-e2e01df8cc1a', 'LE_PSM_OUT')
290
291# Misc
292GATT_DEVICE_NAME_CHARACTERISTIC                                = UUID.from_16_bits(0x2A00, 'Device Name')
293GATT_APPEARANCE_CHARACTERISTIC                                 = UUID.from_16_bits(0x2A01, 'Appearance')
294GATT_PERIPHERAL_PRIVACY_FLAG_CHARACTERISTIC                    = UUID.from_16_bits(0x2A02, 'Peripheral Privacy Flag')
295GATT_RECONNECTION_ADDRESS_CHARACTERISTIC                       = UUID.from_16_bits(0x2A03, 'Reconnection Address')
296GATT_PERIPHERAL_PREFERRED_CONNECTION_PARAMETERS_CHARACTERISTIC = UUID.from_16_bits(0x2A04, 'Peripheral Preferred Connection Parameters')
297GATT_SERVICE_CHANGED_CHARACTERISTIC                            = UUID.from_16_bits(0x2A05, 'Service Changed')
298GATT_ALERT_LEVEL_CHARACTERISTIC                                = UUID.from_16_bits(0x2A06, 'Alert Level')
299GATT_TX_POWER_LEVEL_CHARACTERISTIC                             = UUID.from_16_bits(0x2A07, 'Tx Power Level')
300GATT_BOOT_KEYBOARD_INPUT_REPORT_CHARACTERISTIC                 = UUID.from_16_bits(0x2A22, 'Boot Keyboard Input Report')
301GATT_CURRENT_TIME_CHARACTERISTIC                               = UUID.from_16_bits(0x2A2B, 'Current Time')
302GATT_BOOT_KEYBOARD_OUTPUT_REPORT_CHARACTERISTIC                = UUID.from_16_bits(0x2A32, 'Boot Keyboard Output Report')
303GATT_CENTRAL_ADDRESS_RESOLUTION__CHARACTERISTIC                = UUID.from_16_bits(0x2AA6, 'Central Address Resolution')
304GATT_CLIENT_SUPPORTED_FEATURES_CHARACTERISTIC                  = UUID.from_16_bits(0x2B29, 'Client Supported Features')
305GATT_DATABASE_HASH_CHARACTERISTIC                              = UUID.from_16_bits(0x2B2A, 'Database Hash')
306GATT_SERVER_SUPPORTED_FEATURES_CHARACTERISTIC                  = UUID.from_16_bits(0x2B3A, 'Server Supported Features')
307
308# fmt: on
309# pylint: enable=line-too-long
310
311
312# -----------------------------------------------------------------------------
313# Utils
314# -----------------------------------------------------------------------------
315
316
317def show_services(services: Iterable[Service]) -> None:
318    for service in services:
319        print(color(str(service), 'cyan'))
320
321        for characteristic in service.characteristics:
322            print(color('  ' + str(characteristic), 'magenta'))
323
324            for descriptor in characteristic.descriptors:
325                print(color('    ' + str(descriptor), 'green'))
326
327
328# -----------------------------------------------------------------------------
329class InvalidServiceError(BaseBumbleError):
330    """The service is not compliant with the spec/profile"""
331
332
333# -----------------------------------------------------------------------------
334class Service(Attribute):
335    '''
336    See Vol 3, Part G - 3.1 SERVICE DEFINITION
337    '''
338
339    uuid: UUID
340    characteristics: List[Characteristic]
341    included_services: List[Service]
342
343    def __init__(
344        self,
345        uuid: Union[str, UUID],
346        characteristics: List[Characteristic],
347        primary=True,
348        included_services: Iterable[Service] = (),
349    ) -> None:
350        # Convert the uuid to a UUID object if it isn't already
351        if isinstance(uuid, str):
352            uuid = UUID(uuid)
353
354        super().__init__(
355            (
356                GATT_PRIMARY_SERVICE_ATTRIBUTE_TYPE
357                if primary
358                else GATT_SECONDARY_SERVICE_ATTRIBUTE_TYPE
359            ),
360            Attribute.READABLE,
361            uuid.to_pdu_bytes(),
362        )
363        self.uuid = uuid
364        self.included_services = list(included_services)
365        self.characteristics = characteristics[:]
366        self.primary = primary
367
368    def get_advertising_data(self) -> Optional[bytes]:
369        """
370        Get Service specific advertising data
371        Defined by each Service, default value is empty
372        :return Service data for advertising
373        """
374        return None
375
376    def __str__(self) -> str:
377        return (
378            f'Service(handle=0x{self.handle:04X}, '
379            f'end=0x{self.end_group_handle:04X}, '
380            f'uuid={self.uuid})'
381            f'{"" if self.primary else "*"}'
382        )
383
384
385# -----------------------------------------------------------------------------
386class TemplateService(Service):
387    '''
388    Convenience abstract class that can be used by profile-specific subclasses that want
389    to expose their UUID as a class property
390    '''
391
392    UUID: UUID
393
394    def __init__(
395        self,
396        characteristics: List[Characteristic],
397        primary: bool = True,
398        included_services: Iterable[Service] = (),
399    ) -> None:
400        super().__init__(self.UUID, characteristics, primary, included_services)
401
402
403# -----------------------------------------------------------------------------
404class IncludedServiceDeclaration(Attribute):
405    '''
406    See Vol 3, Part G - 3.2 INCLUDE DEFINITION
407    '''
408
409    service: Service
410
411    def __init__(self, service: Service) -> None:
412        declaration_bytes = struct.pack(
413            '<HH2s', service.handle, service.end_group_handle, service.uuid.to_bytes()
414        )
415        super().__init__(
416            GATT_INCLUDE_ATTRIBUTE_TYPE, Attribute.READABLE, declaration_bytes
417        )
418        self.service = service
419
420    def __str__(self) -> str:
421        return (
422            f'IncludedServiceDefinition(handle=0x{self.handle:04X}, '
423            f'group_starting_handle=0x{self.service.handle:04X}, '
424            f'group_ending_handle=0x{self.service.end_group_handle:04X}, '
425            f'uuid={self.service.uuid})'
426        )
427
428
429# -----------------------------------------------------------------------------
430class Characteristic(Attribute):
431    '''
432    See Vol 3, Part G - 3.3 CHARACTERISTIC DEFINITION
433    '''
434
435    uuid: UUID
436    properties: Characteristic.Properties
437
438    class Properties(enum.IntFlag):
439        """Property flags"""
440
441        BROADCAST = 0x01
442        READ = 0x02
443        WRITE_WITHOUT_RESPONSE = 0x04
444        WRITE = 0x08
445        NOTIFY = 0x10
446        INDICATE = 0x20
447        AUTHENTICATED_SIGNED_WRITES = 0x40
448        EXTENDED_PROPERTIES = 0x80
449
450        @classmethod
451        def from_string(cls, properties_str: str) -> Characteristic.Properties:
452            try:
453                return functools.reduce(
454                    lambda x, y: x | cls[y],
455                    properties_str.replace("|", ",").split(","),
456                    Characteristic.Properties(0),
457                )
458            except (TypeError, KeyError):
459                # The check for `p.name is not None` here is needed because for InFlag
460                # enums, the .name property can be None, when the enum value is 0,
461                # so the type hint for .name is Optional[str].
462                enum_list: List[str] = [p.name for p in cls if p.name is not None]
463                enum_list_str = ",".join(enum_list)
464                raise TypeError(
465                    f"Characteristic.Properties::from_string() error:\nExpected a string containing any of the keys, separated by , or |: {enum_list_str}\nGot: {properties_str}"
466                )
467
468        def __str__(self) -> str:
469            # NOTE: we override this method to offer a consistent result between python
470            # versions: the value returned by IntFlag.__str__() changed in version 11.
471            return '|'.join(
472                flag.name
473                for flag in Characteristic.Properties
474                if self.value & flag.value and flag.name is not None
475            )
476
477    # For backwards compatibility these are defined here
478    # For new code, please use Characteristic.Properties.X
479    BROADCAST = Properties.BROADCAST
480    READ = Properties.READ
481    WRITE_WITHOUT_RESPONSE = Properties.WRITE_WITHOUT_RESPONSE
482    WRITE = Properties.WRITE
483    NOTIFY = Properties.NOTIFY
484    INDICATE = Properties.INDICATE
485    AUTHENTICATED_SIGNED_WRITES = Properties.AUTHENTICATED_SIGNED_WRITES
486    EXTENDED_PROPERTIES = Properties.EXTENDED_PROPERTIES
487
488    def __init__(
489        self,
490        uuid: Union[str, bytes, UUID],
491        properties: Characteristic.Properties,
492        permissions: Union[str, Attribute.Permissions],
493        value: Union[str, bytes, CharacteristicValue] = b'',
494        descriptors: Sequence[Descriptor] = (),
495    ):
496        super().__init__(uuid, permissions, value)
497        self.uuid = self.type
498        self.properties = properties
499        self.descriptors = descriptors
500
501    def get_descriptor(self, descriptor_type):
502        for descriptor in self.descriptors:
503            if descriptor.type == descriptor_type:
504                return descriptor
505
506        return None
507
508    def has_properties(self, properties: Characteristic.Properties) -> bool:
509        return self.properties & properties == properties
510
511    def __str__(self) -> str:
512        return (
513            f'Characteristic(handle=0x{self.handle:04X}, '
514            f'end=0x{self.end_group_handle:04X}, '
515            f'uuid={self.uuid}, '
516            f'{self.properties})'
517        )
518
519
520# -----------------------------------------------------------------------------
521class CharacteristicDeclaration(Attribute):
522    '''
523    See Vol 3, Part G - 3.3.1 CHARACTERISTIC DECLARATION
524    '''
525
526    characteristic: Characteristic
527
528    def __init__(self, characteristic: Characteristic, value_handle: int) -> None:
529        declaration_bytes = (
530            struct.pack('<BH', characteristic.properties, value_handle)
531            + characteristic.uuid.to_pdu_bytes()
532        )
533        super().__init__(
534            GATT_CHARACTERISTIC_ATTRIBUTE_TYPE, Attribute.READABLE, declaration_bytes
535        )
536        self.value_handle = value_handle
537        self.characteristic = characteristic
538
539    def __str__(self) -> str:
540        return (
541            f'CharacteristicDeclaration(handle=0x{self.handle:04X}, '
542            f'value_handle=0x{self.value_handle:04X}, '
543            f'uuid={self.characteristic.uuid}, '
544            f'{self.characteristic.properties})'
545        )
546
547
548# -----------------------------------------------------------------------------
549class CharacteristicValue(AttributeValue):
550    """Same as AttributeValue, for backward compatibility"""
551
552
553# -----------------------------------------------------------------------------
554class CharacteristicAdapter:
555    '''
556    An adapter that can adapt Characteristic and AttributeProxy objects
557    by wrapping their `read_value()` and `write_value()` methods with ones that
558    return/accept encoded/decoded values.
559
560    For proxies (i.e used by a GATT client), the adaptation is one where the return
561    value of `read_value()` is decoded and the value passed to `write_value()` is
562    encoded. The `subscribe()` method, is wrapped with one where the values are decoded
563    before being passed to the subscriber.
564
565    For local values (i.e hosted by a GATT server) the adaptation is one where the
566    return value of `read_value()` is encoded and the value passed to `write_value()`
567    is decoded.
568    '''
569
570    read_value: Callable
571    write_value: Callable
572
573    def __init__(self, characteristic: Union[Characteristic, AttributeProxy]):
574        self.wrapped_characteristic = characteristic
575        self.subscribers: Dict[Callable, Callable] = (
576            {}
577        )  # Map from subscriber to proxy subscriber
578
579        if isinstance(characteristic, Characteristic):
580            self.read_value = self.read_encoded_value
581            self.write_value = self.write_encoded_value
582        else:
583            self.read_value = self.read_decoded_value
584            self.write_value = self.write_decoded_value
585            self.subscribe = self.wrapped_subscribe
586            self.unsubscribe = self.wrapped_unsubscribe
587
588    def __getattr__(self, name):
589        return getattr(self.wrapped_characteristic, name)
590
591    def __setattr__(self, name, value):
592        if name in (
593            'wrapped_characteristic',
594            'subscribers',
595            'read_value',
596            'write_value',
597            'subscribe',
598            'unsubscribe',
599        ):
600            super().__setattr__(name, value)
601        else:
602            setattr(self.wrapped_characteristic, name, value)
603
604    async def read_encoded_value(self, connection):
605        return self.encode_value(
606            await self.wrapped_characteristic.read_value(connection)
607        )
608
609    async def write_encoded_value(self, connection, value):
610        return await self.wrapped_characteristic.write_value(
611            connection, self.decode_value(value)
612        )
613
614    async def read_decoded_value(self):
615        return self.decode_value(await self.wrapped_characteristic.read_value())
616
617    async def write_decoded_value(self, value, with_response=False):
618        return await self.wrapped_characteristic.write_value(
619            self.encode_value(value), with_response
620        )
621
622    def encode_value(self, value):
623        return value
624
625    def decode_value(self, value):
626        return value
627
628    def wrapped_subscribe(self, subscriber=None):
629        if subscriber is not None:
630            if subscriber in self.subscribers:
631                # We already have a proxy subscriber
632                subscriber = self.subscribers[subscriber]
633            else:
634                # Create and register a proxy that will decode the value
635                original_subscriber = subscriber
636
637                def on_change(value):
638                    original_subscriber(self.decode_value(value))
639
640                self.subscribers[subscriber] = on_change
641                subscriber = on_change
642
643        return self.wrapped_characteristic.subscribe(subscriber)
644
645    def wrapped_unsubscribe(self, subscriber=None):
646        if subscriber in self.subscribers:
647            subscriber = self.subscribers.pop(subscriber)
648
649        return self.wrapped_characteristic.unsubscribe(subscriber)
650
651    def __str__(self) -> str:
652        wrapped = str(self.wrapped_characteristic)
653        return f'{self.__class__.__name__}({wrapped})'
654
655
656# -----------------------------------------------------------------------------
657class DelegatedCharacteristicAdapter(CharacteristicAdapter):
658    '''
659    Adapter that converts bytes values using an encode and a decode function.
660    '''
661
662    def __init__(self, characteristic, encode=None, decode=None):
663        super().__init__(characteristic)
664        self.encode = encode
665        self.decode = decode
666
667    def encode_value(self, value):
668        return self.encode(value) if self.encode else value
669
670    def decode_value(self, value):
671        return self.decode(value) if self.decode else value
672
673
674# -----------------------------------------------------------------------------
675class PackedCharacteristicAdapter(CharacteristicAdapter):
676    '''
677    Adapter that packs/unpacks characteristic values according to a standard
678    Python `struct` format.
679    For formats with a single value, the adapted `read_value` and `write_value`
680    methods return/accept single values. For formats with multiple values,
681    they return/accept a tuple with the same number of elements as is required for
682    the format.
683    '''
684
685    def __init__(self, characteristic, pack_format):
686        super().__init__(characteristic)
687        self.struct = struct.Struct(pack_format)
688
689    def pack(self, *values):
690        return self.struct.pack(*values)
691
692    def unpack(self, buffer):
693        return self.struct.unpack(buffer)
694
695    def encode_value(self, value):
696        return self.pack(*value if isinstance(value, tuple) else (value,))
697
698    def decode_value(self, value):
699        unpacked = self.unpack(value)
700        return unpacked[0] if len(unpacked) == 1 else unpacked
701
702
703# -----------------------------------------------------------------------------
704class MappedCharacteristicAdapter(PackedCharacteristicAdapter):
705    '''
706    Adapter that packs/unpacks characteristic values according to a standard
707    Python `struct` format.
708    The adapted `read_value` and `write_value` methods return/accept aa dictionary which
709    is packed/unpacked according to format, with the arguments extracted from the
710    dictionary by key, in the same order as they occur in the `keys` parameter.
711    '''
712
713    def __init__(self, characteristic, pack_format, keys):
714        super().__init__(characteristic, pack_format)
715        self.keys = keys
716
717    # pylint: disable=arguments-differ
718    def pack(self, values):
719        return super().pack(*(values[key] for key in self.keys))
720
721    def unpack(self, buffer):
722        return dict(zip(self.keys, super().unpack(buffer)))
723
724
725# -----------------------------------------------------------------------------
726class UTF8CharacteristicAdapter(CharacteristicAdapter):
727    '''
728    Adapter that converts strings to/from bytes using UTF-8 encoding
729    '''
730
731    def encode_value(self, value: str) -> bytes:
732        return value.encode('utf-8')
733
734    def decode_value(self, value: bytes) -> str:
735        return value.decode('utf-8')
736
737
738# -----------------------------------------------------------------------------
739class Descriptor(Attribute):
740    '''
741    See Vol 3, Part G - 3.3.3 Characteristic Descriptor Declarations
742    '''
743
744    def __str__(self) -> str:
745        if isinstance(self.value, bytes):
746            value_str = self.value.hex()
747        elif isinstance(self.value, CharacteristicValue):
748            value = self.value.read(None)
749            if isinstance(value, bytes):
750                value_str = value.hex()
751            else:
752                value_str = '<async>'
753        else:
754            value_str = '<...>'
755        return (
756            f'Descriptor(handle=0x{self.handle:04X}, '
757            f'type={self.type}, '
758            f'value={value_str})'
759        )
760
761
762# -----------------------------------------------------------------------------
763class ClientCharacteristicConfigurationBits(enum.IntFlag):
764    '''
765    See Vol 3, Part G - 3.3.3.3 - Table 3.11 Client Characteristic Configuration bit
766    field definition
767    '''
768
769    DEFAULT = 0x0000
770    NOTIFICATION = 0x0001
771    INDICATION = 0x0002
772