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# ----------------------------------------------------------------------------- 25import asyncio 26import types 27import logging 28from colors import color 29 30from .core import * 31from .hci import * 32from .att import * 33 34# ----------------------------------------------------------------------------- 35# Logging 36# ----------------------------------------------------------------------------- 37logger = logging.getLogger(__name__) 38 39# ----------------------------------------------------------------------------- 40# Constants 41# ----------------------------------------------------------------------------- 42GATT_REQUEST_TIMEOUT = 30 # seconds 43 44GATT_MAX_ATTRIBUTE_VALUE_SIZE = 512 45 46# Services 47GATT_GENERIC_ACCESS_SERVICE = UUID.from_16_bits(0x1800, 'Generic Access') 48GATT_GENERIC_ATTRIBUTE_SERVICE = UUID.from_16_bits(0x1801, 'Generic Attribute') 49GATT_IMMEDIATE_ALERT_SERVICE = UUID.from_16_bits(0x1802, 'Immediate Alert') 50GATT_LINK_LOSS_SERVICE = UUID.from_16_bits(0x1803, 'Link Loss') 51GATT_TX_POWER_SERVICE = UUID.from_16_bits(0x1804, 'TX Power') 52GATT_CURRENT_TIME_SERVICE = UUID.from_16_bits(0x1805, 'Current Time') 53GATT_REFERENCE_TIME_UPDATE_SERVICE = UUID.from_16_bits(0x1806, 'Reference Time Update') 54GATT_NEXT_DST_CHANGE_SERVICE = UUID.from_16_bits(0x1807, 'Next DST Change') 55GATT_GLUCOSE_SERVICE = UUID.from_16_bits(0x1808, 'Glucose') 56GATT_HEALTH_THERMOMETER_SERVICE = UUID.from_16_bits(0x1809, 'Health Thermometer') 57GATT_DEVICE_INFORMATION_SERVICE = UUID.from_16_bits(0x180A, 'Device Information') 58GATT_HEART_RATE_SERVICE = UUID.from_16_bits(0x180D, 'Heart Rate') 59GATT_PHONE_ALERT_STATUS_SERVICE = UUID.from_16_bits(0x180E, 'Phone Alert Status') 60GATT_BATTERY_SERVICE = UUID.from_16_bits(0x180F, 'Battery') 61GATT_BLOOD_PRESSURE_SERVICE = UUID.from_16_bits(0x1810, 'Blood Pressure') 62GATT_ALERT_NOTIFICATION_SERVICE = UUID.from_16_bits(0x1811, 'Alert Notification') 63GATT_HUMAN_INTERFACE_DEVICE_SERVICE = UUID.from_16_bits(0x1812, 'Human Interface Device') 64GATT_SCAN_PARAMETERS_SERVICE = UUID.from_16_bits(0x1813, 'Scan Parameters') 65GATT_RUNNING_SPEED_AND_CADENCE_SERVICE = UUID.from_16_bits(0x1814, 'Running Speed and Cadence') 66GATT_AUTOMATION_IO_SERVICE = UUID.from_16_bits(0x1815, 'Automation IO') 67GATT_CYCLING_SPEED_AND_CADENCE_SERVICE = UUID.from_16_bits(0x1816, 'Cycling Speed and Cadence') 68GATT_CYCLING_POWER_SERVICE = UUID.from_16_bits(0x1818, 'Cycling Power') 69GATT_LOCATION_AND_NAVIGATION_SERVICE = UUID.from_16_bits(0x1819, 'Location and Navigation') 70GATT_ENVIRONMENTAL_SENSING_SERVICE = UUID.from_16_bits(0x181A, 'Environmental Sensing') 71GATT_BODY_COMPOSITION_SERVICE = UUID.from_16_bits(0x181B, 'Body Composition') 72GATT_USER_DATA_SERVICE = UUID.from_16_bits(0x181C, 'User Data') 73GATT_WEIGHT_SCALE_SERVICE = UUID.from_16_bits(0x181D, 'Weight Scale') 74GATT_BOND_MANAGEMENT_SERVICE = UUID.from_16_bits(0x181E, 'Bond Management') 75GATT_CONTINUOUS_GLUCOSE_MONITORING_SERVICE = UUID.from_16_bits(0x181F, 'Continuous Glucose Monitoring') 76GATT_INTERNET_PROTOCOL_SUPPORT_SERVICE = UUID.from_16_bits(0x1820, 'Internet Protocol Support') 77GATT_INDOOR_POSITIONING_SERVICE = UUID.from_16_bits(0x1821, 'Indoor Positioning') 78GATT_PULSE_OXIMETER_SERVICE = UUID.from_16_bits(0x1822, 'Pulse Oximeter') 79GATT_HTTP_PROXY_SERVICE = UUID.from_16_bits(0x1823, 'HTTP Proxy') 80GATT_TRANSPORT_DISCOVERY_SERVICE = UUID.from_16_bits(0x1824, 'Transport Discovery') 81GATT_OBJECT_TRANSFER_SERVICE = UUID.from_16_bits(0x1825, 'Object Transfer') 82GATT_FITNESS_MACHINE_SERVICE = UUID.from_16_bits(0x1826, 'Fitness Machine') 83GATT_MESH_PROVISIONING_SERVICE = UUID.from_16_bits(0x1827, 'Mesh Provisioning') 84GATT_MESH_PROXY_SERVICE = UUID.from_16_bits(0x1828, 'Mesh Proxy') 85GATT_RECONNECTION_CONFIGURATION_SERVICE = UUID.from_16_bits(0x1829, 'Reconnection Configuration') 86GATT_INSULIN_DELIVERY_SERVICE = UUID.from_16_bits(0x183A, 'Insulin Delivery') 87GATT_BINARY_SENSOR_SERVICE = UUID.from_16_bits(0x183B, 'Binary Sensor') 88GATT_EMERGENCY_CONFIGURATION_SERVICE = UUID.from_16_bits(0x183C, 'Emergency Configuration') 89GATT_PHYSICAL_ACTIVITY_MONITOR_SERVICE = UUID.from_16_bits(0x183E, 'Physical Activity Monitor') 90GATT_AUDIO_INPUT_CONTROL_SERVICE = UUID.from_16_bits(0x1843, 'Audio Input Control') 91GATT_VOLUME_CONTROL_SERVICE = UUID.from_16_bits(0x1844, 'Volume Control') 92GATT_VOLUME_OFFSET_CONTROL_SERVICE = UUID.from_16_bits(0x1845, 'Volume Offset Control') 93GATT_COORDINATED_SET_IDENTIFICATION_SERVICE = UUID.from_16_bits(0x1846, 'Coordinated Set Identification Service') 94GATT_DEVICE_TIME_SERVICE = UUID.from_16_bits(0x1847, 'Device Time') 95GATT_MEDIA_CONTROL_SERVICE = UUID.from_16_bits(0x1848, 'Media Control Service') 96GATT_GENERIC_MEDIA_CONTROL_SERVICE = UUID.from_16_bits(0x1849, 'Generic Media Control Service') 97GATT_CONSTANT_TONE_EXTENSION_SERVICE = UUID.from_16_bits(0x184A, 'Constant Tone Extension') 98GATT_TELEPHONE_BEARER_SERVICE = UUID.from_16_bits(0x184B, 'Telephone Bearer Service') 99GATT_GENERIC_TELEPHONE_BEARER_SERVICE = UUID.from_16_bits(0x184C, 'Generic Telephone Bearer Service') 100GATT_MICROPHONE_CONTROL_SERVICE = UUID.from_16_bits(0x184D, 'Microphone Control') 101 102# Types 103GATT_PRIMARY_SERVICE_ATTRIBUTE_TYPE = UUID.from_16_bits(0x2800, 'Primary Service') 104GATT_SECONDARY_SERVICE_ATTRIBUTE_TYPE = UUID.from_16_bits(0x2801, 'Secondary Service') 105GATT_INCLUDE_ATTRIBUTE_TYPE = UUID.from_16_bits(0x2802, 'Include') 106GATT_CHARACTERISTIC_ATTRIBUTE_TYPE = UUID.from_16_bits(0x2803, 'Characteristic') 107 108# Descriptors 109GATT_CHARACTERISTIC_EXTENDED_PROPERTIES_DESCRIPTOR = UUID.from_16_bits(0x2900, 'Characteristic Extended Properties') 110GATT_CHARACTERISTIC_USER_DESCRIPTION_DESCRIPTOR = UUID.from_16_bits(0x2901, 'Characteristic User Description') 111GATT_CLIENT_CHARACTERISTIC_CONFIGURATION_DESCRIPTOR = UUID.from_16_bits(0x2902, 'Client Characteristic Configuration') 112GATT_SERVER_CHARACTERISTIC_CONFIGURATION_DESCRIPTOR = UUID.from_16_bits(0x2903, 'Server Characteristic Configuration') 113GATT_CHARACTERISTIC_PRESENTATION_FORMAT_DESCRIPTOR = UUID.from_16_bits(0x2904, 'Characteristic Format') 114GATT_CHARACTERISTIC_AGGREGATE_FORMAT_DESCRIPTOR = UUID.from_16_bits(0x2905, 'Characteristic Aggregate Format') 115GATT_VALID_RANGE_DESCRIPTOR = UUID.from_16_bits(0x2906, 'Valid Range') 116GATT_EXTERNAL_REPORT_DESCRIPTOR = UUID.from_16_bits(0x2907, 'External Report') 117GATT_REPORT_REFERENCE_DESCRIPTOR = UUID.from_16_bits(0x2908, 'Report Reference') 118GATT_NUMBER_OF_DIGITALS_DESCRIPTOR = UUID.from_16_bits(0x2909, 'Number of Digitals') 119GATT_VALUE_TRIGGER_SETTING_DESCRIPTOR = UUID.from_16_bits(0x290A, 'Value Trigger Setting') 120GATT_ENVIRONMENTAL_SENSING_CONFIGURATION_DESCRIPTOR = UUID.from_16_bits(0x290B, 'Environmental Sensing Configuration') 121GATT_ENVIRONMENTAL_SENSING_MEASUREMENT_DESCRIPTOR = UUID.from_16_bits(0x290C, 'Environmental Sensing Measurement') 122GATT_ENVIRONMENTAL_SENSING_TRIGGER_DESCRIPTOR = UUID.from_16_bits(0x290D, 'Environmental Sensing Trigger Setting') 123GATT_TIME_TRIGGER_DESCRIPTOR = UUID.from_16_bits(0x290E, 'Time Trigger Setting') 124GATT_COMPLETE_BR_EDR_TRANSPORT_BLOCK_DATA_DESCRIPTOR = UUID.from_16_bits(0x290F, 'Complete BR-EDR Transport Block Data') 125 126# Device Information Service 127GATT_SYSTEM_ID_CHARACTERISTIC = UUID.from_16_bits(0x2A23, 'System ID') 128GATT_MODEL_NUMBER_STRING_CHARACTERISTIC = UUID.from_16_bits(0x2A24, 'Model Number String') 129GATT_SERIAL_NUMBER_STRING_CHARACTERISTIC = UUID.from_16_bits(0x2A25, 'Serial Number String') 130GATT_FIRMWARE_REVISION_STRING_CHARACTERISTIC = UUID.from_16_bits(0x2A26, 'Firmware Revision String') 131GATT_HARDWARE_REVISION_STRING_CHARACTERISTIC = UUID.from_16_bits(0x2A27, 'Hardware Revision String') 132GATT_SOFTWARE_REVISION_STRING_CHARACTERISTIC = UUID.from_16_bits(0x2A28, 'Software Revision String') 133GATT_MANUFACTURER_NAME_STRING_CHARACTERISTIC = UUID.from_16_bits(0x2A29, 'Manufacturer Name String') 134GATT_REGULATORY_CERTIFICATION_DATA_LIST_CHARACTERISTIC = UUID.from_16_bits(0x2A2A, 'IEEE 11073-20601 Regulatory Certification Data List') 135GATT_PNP_ID_CHARACTERISTIC = UUID.from_16_bits(0x2A50, 'PnP ID') 136 137# Human Interface Device 138GATT_HID_INFORMATION_CHARACTERISTIC = UUID.from_16_bits(0x2A4A, 'HID Information') 139GATT_REPORT_MAP_CHARACTERISTIC = UUID.from_16_bits(0x2A4B, 'Report Map') 140GATT_HID_CONTROL_POINT_CHARACTERISTIC = UUID.from_16_bits(0x2A4C, 'HID Control Point') 141GATT_REPORT_CHARACTERISTIC = UUID.from_16_bits(0x2A4D, 'Report') 142GATT_PROTOCOL_MODE_CHARACTERISTIC = UUID.from_16_bits(0x2A4E, 'Protocol Mode') 143 144# Misc 145GATT_DEVICE_NAME_CHARACTERISTIC = UUID.from_16_bits(0x2A00, 'Device Name') 146GATT_APPEARANCE_CHARACTERISTIC = UUID.from_16_bits(0x2A01, 'Appearance') 147GATT_PERIPHERAL_PRIVACY_FLAG_CHARACTERISTIC = UUID.from_16_bits(0x2A02, 'Peripheral Privacy Flag') 148GATT_RECONNECTION_ADDRESS_CHARACTERISTIC = UUID.from_16_bits(0x2A03, 'Reconnection Address') 149GATT_PERIPHERAL_PREFERRED_CONNECTION_PARAMETERS_CHARACTERISTIC = UUID.from_16_bits(0x2A04, 'Peripheral Preferred Connection Parameters') 150GATT_SERVICE_CHANGED_CHARACTERISTIC = UUID.from_16_bits(0x2A05, 'Service Changed') 151GATT_ALERT_LEVEL_CHARACTERISTIC = UUID.from_16_bits(0x2A06, 'Alert Level') 152GATT_TX_POWER_LEVEL_CHARACTERISTIC = UUID.from_16_bits(0x2A07, 'Tx Power Level') 153GATT_BATTERY_LEVEL_CHARACTERISTIC = UUID.from_16_bits(0x2A19, 'Battery Level') 154GATT_BOOT_KEYBOARD_INPUT_REPORT_CHARACTERISTIC = UUID.from_16_bits(0x2A22, 'Boot Keyboard Input Report') 155GATT_CURRENT_TIME_CHARACTERISTIC = UUID.from_16_bits(0x2A2B, 'Current Time') 156GATT_BOOT_KEYBOARD_OUTPUT_REPORT_CHARACTERISTIC = UUID.from_16_bits(0x2A32, 'Boot Keyboard Output Report') 157GATT_CENTRAL_ADDRESS_RESOLUTION__CHARACTERISTIC = UUID.from_16_bits(0x2AA6, 'Central Address Resolution') 158 159 160# ----------------------------------------------------------------------------- 161# Utils 162# ----------------------------------------------------------------------------- 163 164def show_services(services): 165 for service in services: 166 print(color(str(service), 'cyan')) 167 168 for characteristic in service.characteristics: 169 print(color(' ' + str(characteristic), 'magenta')) 170 171 for descriptor in characteristic.descriptors: 172 print(color(' ' + str(descriptor), 'green')) 173 174 175# ----------------------------------------------------------------------------- 176class Service(Attribute): 177 ''' 178 See Vol 3, Part G - 3.1 SERVICE DEFINITION 179 ''' 180 181 def __init__(self, uuid, characteristics, primary=True): 182 # Convert the uuid to a UUID object if it isn't already 183 if type(uuid) is str: 184 uuid = UUID(uuid) 185 186 super().__init__( 187 GATT_PRIMARY_SERVICE_ATTRIBUTE_TYPE if primary else GATT_SECONDARY_SERVICE_ATTRIBUTE_TYPE, 188 Attribute.READABLE, 189 uuid.to_pdu_bytes() 190 ) 191 self.uuid = uuid 192 self.included_services = [] 193 self.characteristics = characteristics[:] 194 self.primary = primary 195 196 def __str__(self): 197 return f'Service(handle=0x{self.handle:04X}, end=0x{self.end_group_handle:04X}, uuid={self.uuid}){"" if self.primary else "*"}' 198 199 200# ----------------------------------------------------------------------------- 201class TemplateService(Service): 202 ''' 203 Convenience abstract class that can be used by profile-specific subclasses that want 204 to expose their UUID as a class property 205 ''' 206 UUID = None 207 208 def __init__(self, characteristics, primary=True): 209 super().__init__(self.UUID, characteristics, primary) 210 211 212# ----------------------------------------------------------------------------- 213class Characteristic(Attribute): 214 ''' 215 See Vol 3, Part G - 3.3 CHARACTERISTIC DEFINITION 216 ''' 217 218 # Property flags 219 BROADCAST = 0x01 220 READ = 0x02 221 WRITE_WITHOUT_RESPONSE = 0x04 222 WRITE = 0x08 223 NOTIFY = 0x10 224 INDICATE = 0X20 225 AUTHENTICATED_SIGNED_WRITES = 0X40 226 EXTENDED_PROPERTIES = 0X80 227 228 PROPERTY_NAMES = { 229 BROADCAST: 'BROADCAST', 230 READ: 'READ', 231 WRITE_WITHOUT_RESPONSE: 'WRITE_WITHOUT_RESPONSE', 232 WRITE: 'WRITE', 233 NOTIFY: 'NOTIFY', 234 INDICATE: 'INDICATE', 235 AUTHENTICATED_SIGNED_WRITES: 'AUTHENTICATED_SIGNED_WRITES', 236 EXTENDED_PROPERTIES: 'EXTENDED_PROPERTIES' 237 } 238 239 @staticmethod 240 def property_name(property): 241 return Characteristic.PROPERTY_NAMES.get(property, '') 242 243 @staticmethod 244 def properties_as_string(properties): 245 return ','.join([ 246 Characteristic.property_name(p) for p in Characteristic.PROPERTY_NAMES.keys() 247 if properties & p 248 ]) 249 250 def __init__(self, uuid, properties, permissions, value = b'', descriptors = []): 251 super().__init__(uuid, permissions, value) 252 self.uuid = self.type 253 self.properties = properties 254 self.descriptors = descriptors 255 256 def get_descriptor(self, descriptor_type): 257 for descriptor in self.descriptors: 258 if descriptor.uuid == descriptor_type: 259 return descriptor 260 261 def __str__(self): 262 return f'Characteristic(handle=0x{self.handle:04X}, end=0x{self.end_group_handle:04X}, uuid={self.uuid}, properties={Characteristic.properties_as_string(self.properties)})' 263 264 265# ----------------------------------------------------------------------------- 266class CharacteristicValue: 267 ''' 268 Characteristic value where reading and/or writing is delegated to functions 269 passed as arguments to the constructor. 270 ''' 271 def __init__(self, read=None, write=None): 272 self._read = read 273 self._write = write 274 275 def read(self, connection): 276 return self._read(connection) if self._read else b'' 277 278 def write(self, connection, value): 279 if self._write: 280 self._write(connection, value) 281 282 283# ----------------------------------------------------------------------------- 284class CharacteristicAdapter: 285 ''' 286 An adapter that can adapt any object with `read_value` and `write_value` 287 methods (like Characteristic and CharacteristicProxy objects) by wrapping 288 those methods with ones that return/accept encoded/decoded values. 289 Objects with async methods are considered proxies, so the adaptation is one 290 where the return value of `read_value` is decoded and the value passed to 291 `write_value` is encoded. Other objects are considered local characteristics 292 so the adaptation is one where the return value of `read_value` is encoded 293 and the value passed to `write_value` is decoded. 294 If the characteristic has a `subscribe` method, it is wrapped with one where 295 the values are decoded before being passed to the subscriber. 296 ''' 297 def __init__(self, characteristic): 298 self.wrapped_characteristic = characteristic 299 300 if ( 301 asyncio.iscoroutinefunction(characteristic.read_value) and 302 asyncio.iscoroutinefunction(characteristic.write_value) 303 ): 304 self.read_value = self.read_decoded_value 305 self.write_value = self.write_decoded_value 306 else: 307 self.read_value = self.read_encoded_value 308 self.write_value = self.write_encoded_value 309 310 if hasattr(self.wrapped_characteristic, 'subscribe'): 311 self.subscribe = self.wrapped_subscribe 312 313 def __getattr__(self, name): 314 return getattr(self.wrapped_characteristic, name) 315 316 def read_encoded_value(self, connection): 317 return self.encode_value(self.wrapped_characteristic.read_value(connection)) 318 319 def write_encoded_value(self, connection, value): 320 return self.wrapped_characteristic.write_value(connection, self.decode_value(value)) 321 322 async def read_decoded_value(self): 323 return self.decode_value(await self.wrapped_characteristic.read_value()) 324 325 async def write_decoded_value(self, value): 326 return await self.wrapped_characteristic.write_value(self.encode_value(value)) 327 328 def encode_value(self, value): 329 return value 330 331 def decode_value(self, value): 332 return value 333 334 def wrapped_subscribe(self, subscriber=None): 335 return self.wrapped_characteristic.subscribe( 336 None if subscriber is None else lambda value: subscriber(self.decode_value(value)) 337 ) 338 339 340# ----------------------------------------------------------------------------- 341class DelegatedCharacteristicAdapter(CharacteristicAdapter): 342 def __init__(self, characteristic, encode, decode): 343 super().__init__(characteristic) 344 self.encode = encode 345 self.decode = decode 346 347 def encode_value(self, value): 348 return self.encode(value) 349 350 def decode_value(self, value): 351 return self.decode(value) 352 353 354# ----------------------------------------------------------------------------- 355class PackedCharacteristicAdapter(CharacteristicAdapter): 356 ''' 357 Adapter that packs/unpacks characteristic values according to a standard 358 Python `struct` format. 359 For formats with a single value, the adapted `read_value` and `write_value` 360 methods return/accept single values. For formats with multiple values, 361 they return/accept a tuple with the same number of elements as is required for 362 the format. 363 ''' 364 def __init__(self, characteristic, format): 365 super().__init__(characteristic) 366 self.struct = struct.Struct(format) 367 368 def pack(self, *values): 369 return self.struct.pack(*values) 370 371 def unpack(self, buffer): 372 return self.struct.unpack(buffer) 373 374 def encode_value(self, value): 375 return self.pack(*value if type(value) is tuple else (value,)) 376 377 def decode_value(self, value): 378 unpacked = self.unpack(value) 379 return unpacked[0] if len(unpacked) == 1 else unpacked 380 381 382# ----------------------------------------------------------------------------- 383class MappedCharacteristicAdapter(PackedCharacteristicAdapter): 384 ''' 385 Adapter that packs/unpacks characteristic values according to a standard 386 Python `struct` format. 387 The adapted `read_value` and `write_value` methods return/accept aa dictionary which 388 is packed/unpacked according to format, with the arguments extracted from the dictionary 389 by key, in the same order as they occur in the `keys` parameter. 390 ''' 391 def __init__(self, characteristic, format, keys): 392 super().__init__(characteristic, format) 393 self.keys = keys 394 395 def pack(self, values): 396 return super().pack(*(values[key] for key in self.keys)) 397 398 def unpack(self, buffer): 399 return dict(zip(self.keys, super().unpack(buffer))) 400 401 402# ----------------------------------------------------------------------------- 403class UTF8CharacteristicAdapter(CharacteristicAdapter): 404 ''' 405 Adapter that converts strings to/from bytes using UTF-8 encoding 406 ''' 407 def encode_value(self, value): 408 return value.encode('utf-8') 409 410 def decode_value(self, value): 411 return value.decode('utf-8') 412 413 414# ----------------------------------------------------------------------------- 415class Descriptor(Attribute): 416 ''' 417 See Vol 3, Part G - 3.3.3 Characteristic Descriptor Declarations 418 ''' 419 420 def __init__(self, descriptor_type, permissions, value = b''): 421 super().__init__(descriptor_type, permissions, value) 422 423 def __str__(self): 424 return f'Descriptor(handle=0x{self.handle:04X}, type={self.type}, value={self.read_value(None).hex()})' 425