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