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# Imports 17# ----------------------------------------------------------------------------- 18from __future__ import annotations 19import struct 20from typing import List, Optional, Tuple, Union, cast 21 22from .company_ids import COMPANY_IDENTIFIERS 23 24 25# ----------------------------------------------------------------------------- 26# Constants 27# ----------------------------------------------------------------------------- 28# fmt: off 29 30BT_CENTRAL_ROLE = 0 31BT_PERIPHERAL_ROLE = 1 32 33BT_BR_EDR_TRANSPORT = 0 34BT_LE_TRANSPORT = 1 35 36 37# fmt: on 38 39 40# ----------------------------------------------------------------------------- 41# Utils 42# ----------------------------------------------------------------------------- 43def bit_flags_to_strings(bits, bit_flag_names): 44 names = [] 45 index = 0 46 while bits != 0: 47 if bits & 1: 48 name = bit_flag_names[index] if index < len(bit_flag_names) else f'#{index}' 49 names.append(name) 50 bits >>= 1 51 index += 1 52 53 return names 54 55 56def name_or_number(dictionary, number, width=2): 57 name = dictionary.get(number) 58 if name is not None: 59 return name 60 return f'[0x{number:0{width}X}]' 61 62 63def padded_bytes(buffer, size): 64 padding_size = max(size - len(buffer), 0) 65 return buffer + bytes(padding_size) 66 67 68def get_dict_key_by_value(dictionary, value): 69 for key, val in dictionary.items(): 70 if val == value: 71 return key 72 return None 73 74 75# ----------------------------------------------------------------------------- 76# Exceptions 77# ----------------------------------------------------------------------------- 78class BaseError(Exception): 79 """Base class for errors with an error code, error name and namespace""" 80 81 def __init__(self, error_code, error_namespace='', error_name='', details=''): 82 super().__init__() 83 self.error_code = error_code 84 self.error_namespace = error_namespace 85 self.error_name = error_name 86 self.details = details 87 88 def __str__(self): 89 if self.error_namespace: 90 namespace = f'{self.error_namespace}/' 91 else: 92 namespace = '' 93 if self.error_name: 94 name = f'{self.error_name} [0x{self.error_code:X}]' 95 else: 96 name = f'0x{self.error_code:X}' 97 98 return f'{type(self).__name__}({namespace}{name})' 99 100 101class ProtocolError(BaseError): 102 """Protocol Error""" 103 104 105class TimeoutError(Exception): # pylint: disable=redefined-builtin 106 """Timeout Error""" 107 108 109class CommandTimeoutError(Exception): 110 """Command Timeout Error""" 111 112 113class InvalidStateError(Exception): 114 """Invalid State Error""" 115 116 117class ConnectionError(BaseError): # pylint: disable=redefined-builtin 118 """Connection Error""" 119 120 FAILURE = 0x01 121 CONNECTION_REFUSED = 0x02 122 123 def __init__( 124 self, 125 error_code, 126 transport, 127 peer_address, 128 error_namespace='', 129 error_name='', 130 details='', 131 ): 132 super().__init__(error_code, error_namespace, error_name, details) 133 self.transport = transport 134 self.peer_address = peer_address 135 136 137# ----------------------------------------------------------------------------- 138# UUID 139# 140# NOTE: the internal byte representation is in little-endian byte order 141# 142# Base UUID: 00000000-0000-1000-8000- 00805F9B34FB 143# ----------------------------------------------------------------------------- 144class UUID: 145 ''' 146 See Bluetooth spec Vol 3, Part B - 2.5.1 UUID 147 148 Note that this class expects and works in little-endian byte-order throughout. 149 The exception is when interacting with strings, which are in big-endian byte-order. 150 ''' 151 152 BASE_UUID = bytes.fromhex('00001000800000805F9B34FB')[::-1] # little-endian 153 UUIDS: List[UUID] = [] # Registry of all instances created 154 155 def __init__(self, uuid_str_or_int, name=None): 156 if isinstance(uuid_str_or_int, int): 157 self.uuid_bytes = struct.pack('<H', uuid_str_or_int) 158 else: 159 if len(uuid_str_or_int) == 36: 160 if ( 161 uuid_str_or_int[8] != '-' 162 or uuid_str_or_int[13] != '-' 163 or uuid_str_or_int[18] != '-' 164 or uuid_str_or_int[23] != '-' 165 ): 166 raise ValueError('invalid UUID format') 167 uuid_str = uuid_str_or_int.replace('-', '') 168 else: 169 uuid_str = uuid_str_or_int 170 if len(uuid_str) != 32 and len(uuid_str) != 8 and len(uuid_str) != 4: 171 raise ValueError(f"invalid UUID format: {uuid_str}") 172 self.uuid_bytes = bytes(reversed(bytes.fromhex(uuid_str))) 173 self.name = name 174 175 def register(self): 176 # Register this object in the class registry, and update the entry's name if 177 # it wasn't set already 178 for uuid in self.UUIDS: 179 if self == uuid: 180 if uuid.name is None: 181 uuid.name = self.name 182 return uuid 183 184 self.UUIDS.append(self) 185 return self 186 187 @classmethod 188 def from_bytes(cls, uuid_bytes: bytes, name: Optional[str] = None) -> UUID: 189 if len(uuid_bytes) in (2, 4, 16): 190 self = cls.__new__(cls) 191 self.uuid_bytes = uuid_bytes 192 self.name = name 193 194 return self.register() 195 196 raise ValueError('only 2, 4 and 16 bytes are allowed') 197 198 @classmethod 199 def from_16_bits(cls, uuid_16, name=None): 200 return cls.from_bytes(struct.pack('<H', uuid_16), name) 201 202 @classmethod 203 def from_32_bits(cls, uuid_32, name=None): 204 return cls.from_bytes(struct.pack('<I', uuid_32), name) 205 206 @classmethod 207 def parse_uuid(cls, uuid_as_bytes, offset): 208 return len(uuid_as_bytes), cls.from_bytes(uuid_as_bytes[offset:]) 209 210 @classmethod 211 def parse_uuid_2(cls, uuid_as_bytes, offset): 212 return offset + 2, cls.from_bytes(uuid_as_bytes[offset : offset + 2]) 213 214 def to_bytes(self, force_128=False): 215 ''' 216 Serialize UUID in little-endian byte-order 217 ''' 218 if not force_128: 219 return self.uuid_bytes 220 221 if len(self.uuid_bytes) == 2: 222 return self.BASE_UUID + self.uuid_bytes + bytes([0, 0]) 223 elif len(self.uuid_bytes) == 4: 224 return self.BASE_UUID + self.uuid_bytes 225 elif len(self.uuid_bytes) == 16: 226 return self.uuid_bytes 227 else: 228 assert False, "unreachable" 229 230 def to_pdu_bytes(self): 231 ''' 232 Convert to bytes for use in an ATT PDU. 233 According to Vol 3, Part F - 3.2.1 Attribute Type: 234 "All 32-bit Attribute UUIDs shall be converted to 128-bit UUIDs when the 235 Attribute UUID is contained in an ATT PDU." 236 ''' 237 return self.to_bytes(force_128=(len(self.uuid_bytes) == 4)) 238 239 def to_hex_str(self) -> str: 240 if len(self.uuid_bytes) == 2 or len(self.uuid_bytes) == 4: 241 return bytes(reversed(self.uuid_bytes)).hex().upper() 242 243 return ''.join( 244 [ 245 bytes(reversed(self.uuid_bytes[12:16])).hex(), 246 bytes(reversed(self.uuid_bytes[10:12])).hex(), 247 bytes(reversed(self.uuid_bytes[8:10])).hex(), 248 bytes(reversed(self.uuid_bytes[6:8])).hex(), 249 bytes(reversed(self.uuid_bytes[0:6])).hex(), 250 ] 251 ).upper() 252 253 def __bytes__(self): 254 return self.to_bytes() 255 256 def __eq__(self, other): 257 if isinstance(other, UUID): 258 return self.to_bytes(force_128=True) == other.to_bytes(force_128=True) 259 260 if isinstance(other, str): 261 return UUID(other) == self 262 263 return False 264 265 def __hash__(self): 266 return hash(self.uuid_bytes) 267 268 def __str__(self): 269 if len(self.uuid_bytes) == 2: 270 uuid = struct.unpack('<H', self.uuid_bytes)[0] 271 result = f'UUID-16:{uuid:04X}' 272 elif len(self.uuid_bytes) == 4: 273 uuid = struct.unpack('<I', self.uuid_bytes)[0] 274 result = f'UUID-32:{uuid:08X}' 275 else: 276 result = '-'.join( 277 [ 278 bytes(reversed(self.uuid_bytes[12:16])).hex(), 279 bytes(reversed(self.uuid_bytes[10:12])).hex(), 280 bytes(reversed(self.uuid_bytes[8:10])).hex(), 281 bytes(reversed(self.uuid_bytes[6:8])).hex(), 282 bytes(reversed(self.uuid_bytes[0:6])).hex(), 283 ] 284 ).upper() 285 286 if self.name is not None: 287 return result + f' ({self.name})' 288 289 return result 290 291 def __repr__(self): 292 return str(self) 293 294 295# ----------------------------------------------------------------------------- 296# Common UUID constants 297# ----------------------------------------------------------------------------- 298# fmt: off 299# pylint: disable=line-too-long 300 301# Protocol Identifiers 302BT_SDP_PROTOCOL_ID = UUID.from_16_bits(0x0001, 'SDP') 303BT_UDP_PROTOCOL_ID = UUID.from_16_bits(0x0002, 'UDP') 304BT_RFCOMM_PROTOCOL_ID = UUID.from_16_bits(0x0003, 'RFCOMM') 305BT_TCP_PROTOCOL_ID = UUID.from_16_bits(0x0004, 'TCP') 306BT_TCS_BIN_PROTOCOL_ID = UUID.from_16_bits(0x0005, 'TCP-BIN') 307BT_TCS_AT_PROTOCOL_ID = UUID.from_16_bits(0x0006, 'TCS-AT') 308BT_ATT_PROTOCOL_ID = UUID.from_16_bits(0x0007, 'ATT') 309BT_OBEX_PROTOCOL_ID = UUID.from_16_bits(0x0008, 'OBEX') 310BT_IP_PROTOCOL_ID = UUID.from_16_bits(0x0009, 'IP') 311BT_FTP_PROTOCOL_ID = UUID.from_16_bits(0x000A, 'FTP') 312BT_HTTP_PROTOCOL_ID = UUID.from_16_bits(0x000C, 'HTTP') 313BT_WSP_PROTOCOL_ID = UUID.from_16_bits(0x000E, 'WSP') 314BT_BNEP_PROTOCOL_ID = UUID.from_16_bits(0x000F, 'BNEP') 315BT_UPNP_PROTOCOL_ID = UUID.from_16_bits(0x0010, 'UPNP') 316BT_HIDP_PROTOCOL_ID = UUID.from_16_bits(0x0011, 'HIDP') 317BT_HARDCOPY_CONTROL_CHANNEL_PROTOCOL_ID = UUID.from_16_bits(0x0012, 'HardcopyControlChannel') 318BT_HARDCOPY_DATA_CHANNEL_PROTOCOL_ID = UUID.from_16_bits(0x0014, 'HardcopyDataChannel') 319BT_HARDCOPY_NOTIFICATION_PROTOCOL_ID = UUID.from_16_bits(0x0016, 'HardcopyNotification') 320BT_AVTCP_PROTOCOL_ID = UUID.from_16_bits(0x0017, 'AVCTP') 321BT_AVDTP_PROTOCOL_ID = UUID.from_16_bits(0x0019, 'AVDTP') 322BT_CMTP_PROTOCOL_ID = UUID.from_16_bits(0x001B, 'CMTP') 323BT_MCAP_CONTROL_CHANNEL_PROTOCOL_ID = UUID.from_16_bits(0x001E, 'MCAPControlChannel') 324BT_MCAP_DATA_CHANNEL_PROTOCOL_ID = UUID.from_16_bits(0x001F, 'MCAPDataChannel') 325BT_L2CAP_PROTOCOL_ID = UUID.from_16_bits(0x0100, 'L2CAP') 326 327# Service Classes and Profiles 328BT_SERVICE_DISCOVERY_SERVER_SERVICE_CLASS_ID_SERVICE = UUID.from_16_bits(0x1000, 'ServiceDiscoveryServerServiceClassID') 329BT_BROWSE_GROUP_DESCRIPTOR_SERVICE_CLASS_ID_SERVICE = UUID.from_16_bits(0x1001, 'BrowseGroupDescriptorServiceClassID') 330BT_SERIAL_PORT_SERVICE = UUID.from_16_bits(0x1101, 'SerialPort') 331BT_LAN_ACCESS_USING_PPP_SERVICE = UUID.from_16_bits(0x1102, 'LANAccessUsingPPP') 332BT_DIALUP_NETWORKING_SERVICE = UUID.from_16_bits(0x1103, 'DialupNetworking') 333BT_IR_MCSYNC_SERVICE = UUID.from_16_bits(0x1104, 'IrMCSync') 334BT_OBEX_OBJECT_PUSH_SERVICE = UUID.from_16_bits(0x1105, 'OBEXObjectPush') 335BT_OBEX_FILE_TRANSFER_SERVICE = UUID.from_16_bits(0x1106, 'OBEXFileTransfer') 336BT_IR_MCSYNC_COMMAND_SERVICE = UUID.from_16_bits(0x1107, 'IrMCSyncCommand') 337BT_HEADSET_SERVICE = UUID.from_16_bits(0x1108, 'Headset') 338BT_CORDLESS_TELEPHONY_SERVICE = UUID.from_16_bits(0x1109, 'CordlessTelephony') 339BT_AUDIO_SOURCE_SERVICE = UUID.from_16_bits(0x110A, 'AudioSource') 340BT_AUDIO_SINK_SERVICE = UUID.from_16_bits(0x110B, 'AudioSink') 341BT_AV_REMOTE_CONTROL_TARGET_SERVICE = UUID.from_16_bits(0x110C, 'A/V_RemoteControlTarget') 342BT_ADVANCED_AUDIO_DISTRIBUTION_SERVICE = UUID.from_16_bits(0x110D, 'AdvancedAudioDistribution') 343BT_AV_REMOTE_CONTROL_SERVICE = UUID.from_16_bits(0x110E, 'A/V_RemoteControl') 344BT_AV_REMOTE_CONTROL_CONTROLLER_SERVICE = UUID.from_16_bits(0x110F, 'A/V_RemoteControlController') 345BT_INTERCOM_SERVICE = UUID.from_16_bits(0x1110, 'Intercom') 346BT_FAX_SERVICE = UUID.from_16_bits(0x1111, 'Fax') 347BT_HEADSET_AUDIO_GATEWAY_SERVICE = UUID.from_16_bits(0x1112, 'Headset - Audio Gateway') 348BT_WAP_SERVICE = UUID.from_16_bits(0x1113, 'WAP') 349BT_WAP_CLIENT_SERVICE = UUID.from_16_bits(0x1114, 'WAP_CLIENT') 350BT_PANU_SERVICE = UUID.from_16_bits(0x1115, 'PANU') 351BT_NAP_SERVICE = UUID.from_16_bits(0x1116, 'NAP') 352BT_GN_SERVICE = UUID.from_16_bits(0x1117, 'GN') 353BT_DIRECT_PRINTING_SERVICE = UUID.from_16_bits(0x1118, 'DirectPrinting') 354BT_REFERENCE_PRINTING_SERVICE = UUID.from_16_bits(0x1119, 'ReferencePrinting') 355BT_BASIC_IMAGING_PROFILE_SERVICE = UUID.from_16_bits(0x111A, 'Basic Imaging Profile') 356BT_IMAGING_RESPONDER_SERVICE = UUID.from_16_bits(0x111B, 'ImagingResponder') 357BT_IMAGING_AUTOMATIC_ARCHIVE_SERVICE = UUID.from_16_bits(0x111C, 'ImagingAutomaticArchive') 358BT_IMAGING_REFERENCED_OBJECTS_SERVICE = UUID.from_16_bits(0x111D, 'ImagingReferencedObjects') 359BT_HANDSFREE_SERVICE = UUID.from_16_bits(0x111E, 'Handsfree') 360BT_HANDSFREE_AUDIO_GATEWAY_SERVICE = UUID.from_16_bits(0x111F, 'HandsfreeAudioGateway') 361BT_DIRECT_PRINTING_REFERENCE_OBJECTS_SERVICE = UUID.from_16_bits(0x1120, 'DirectPrintingReferenceObjectsService') 362BT_REFLECTED_UI_SERVICE = UUID.from_16_bits(0x1121, 'ReflectedUI') 363BT_BASIC_PRINTING_SERVICE = UUID.from_16_bits(0x1122, 'BasicPrinting') 364BT_PRINTING_STATUS_SERVICE = UUID.from_16_bits(0x1123, 'PrintingStatus') 365BT_HUMAN_INTERFACE_DEVICE_SERVICE = UUID.from_16_bits(0x1124, 'HumanInterfaceDeviceService') 366BT_HARDCOPY_CABLE_REPLACEMENT_SERVICE = UUID.from_16_bits(0x1125, 'HardcopyCableReplacement') 367BT_HCR_PRINT_SERVICE = UUID.from_16_bits(0x1126, 'HCR_Print') 368BT_HCR_SCAN_SERVICE = UUID.from_16_bits(0x1127, 'HCR_Scan') 369BT_COMMON_ISDN_ACCESS_SERVICE = UUID.from_16_bits(0x1128, 'Common_ISDN_Access') 370BT_SIM_ACCESS_SERVICE = UUID.from_16_bits(0x112D, 'SIM_Access') 371BT_PHONEBOOK_ACCESS_PCE_SERVICE = UUID.from_16_bits(0x112E, 'Phonebook Access - PCE') 372BT_PHONEBOOK_ACCESS_PSE_SERVICE = UUID.from_16_bits(0x112F, 'Phonebook Access - PSE') 373BT_PHONEBOOK_ACCESS_SERVICE = UUID.from_16_bits(0x1130, 'Phonebook Access') 374BT_HEADSET_HS_SERVICE = UUID.from_16_bits(0x1131, 'Headset - HS') 375BT_MESSAGE_ACCESS_SERVER_SERVICE = UUID.from_16_bits(0x1132, 'Message Access Server') 376BT_MESSAGE_NOTIFICATION_SERVER_SERVICE = UUID.from_16_bits(0x1133, 'Message Notification Server') 377BT_MESSAGE_ACCESS_PROFILE_SERVICE = UUID.from_16_bits(0x1134, 'Message Access Profile') 378BT_GNSS_SERVICE = UUID.from_16_bits(0x1135, 'GNSS') 379BT_GNSS_SERVER_SERVICE = UUID.from_16_bits(0x1136, 'GNSS_Server') 380BT_3D_DISPLAY_SERVICE = UUID.from_16_bits(0x1137, '3D Display') 381BT_3D_GLASSES_SERVICE = UUID.from_16_bits(0x1138, '3D Glasses') 382BT_3D_SYNCHRONIZATION_SERVICE = UUID.from_16_bits(0x1139, '3D Synchronization') 383BT_MPS_PROFILE_SERVICE = UUID.from_16_bits(0x113A, 'MPS Profile') 384BT_MPS_SC_SERVICE = UUID.from_16_bits(0x113B, 'MPS SC') 385BT_ACCESS_SERVICE_SERVICE = UUID.from_16_bits(0x113C, 'CTN Access Service') 386BT_CTN_NOTIFICATION_SERVICE_SERVICE = UUID.from_16_bits(0x113D, 'CTN Notification Service') 387BT_CTN_PROFILE_SERVICE = UUID.from_16_bits(0x113E, 'CTN Profile') 388BT_PNP_INFORMATION_SERVICE = UUID.from_16_bits(0x1200, 'PnPInformation') 389BT_GENERIC_NETWORKING_SERVICE = UUID.from_16_bits(0x1201, 'GenericNetworking') 390BT_GENERIC_FILE_TRANSFER_SERVICE = UUID.from_16_bits(0x1202, 'GenericFileTransfer') 391BT_GENERIC_AUDIO_SERVICE = UUID.from_16_bits(0x1203, 'GenericAudio') 392BT_GENERIC_TELEPHONY_SERVICE = UUID.from_16_bits(0x1204, 'GenericTelephony') 393BT_UPNP_SERVICE = UUID.from_16_bits(0x1205, 'UPNP_Service') 394BT_UPNP_IP_SERVICE = UUID.from_16_bits(0x1206, 'UPNP_IP_Service') 395BT_ESDP_UPNP_IP_PAN_SERVICE = UUID.from_16_bits(0x1300, 'ESDP_UPNP_IP_PAN') 396BT_ESDP_UPNP_IP_LAP_SERVICE = UUID.from_16_bits(0x1301, 'ESDP_UPNP_IP_LAP') 397BT_ESDP_UPNP_L2CAP_SERVICE = UUID.from_16_bits(0x1302, 'ESDP_UPNP_L2CAP') 398BT_VIDEO_SOURCE_SERVICE = UUID.from_16_bits(0x1303, 'VideoSource') 399BT_VIDEO_SINK_SERVICE = UUID.from_16_bits(0x1304, 'VideoSink') 400BT_VIDEO_DISTRIBUTION_SERVICE = UUID.from_16_bits(0x1305, 'VideoDistribution') 401BT_HDP_SERVICE = UUID.from_16_bits(0x1400, 'HDP') 402BT_HDP_SOURCE_SERVICE = UUID.from_16_bits(0x1401, 'HDP Source') 403BT_HDP_SINK_SERVICE = UUID.from_16_bits(0x1402, 'HDP Sink') 404 405# fmt: on 406# pylint: enable=line-too-long 407 408 409# ----------------------------------------------------------------------------- 410# DeviceClass 411# ----------------------------------------------------------------------------- 412class DeviceClass: 413 # fmt: off 414 # pylint: disable=line-too-long 415 416 # Major Service Classes (flags combined with OR) 417 LIMITED_DISCOVERABLE_MODE_SERVICE_CLASS = (1 << 0) 418 LE_AUDIO_SERVICE_CLASS = (1 << 1) 419 RESERVED = (1 << 2) 420 POSITIONING_SERVICE_CLASS = (1 << 3) 421 NETWORKING_SERVICE_CLASS = (1 << 4) 422 RENDERING_SERVICE_CLASS = (1 << 5) 423 CAPTURING_SERVICE_CLASS = (1 << 6) 424 OBJECT_TRANSFER_SERVICE_CLASS = (1 << 7) 425 AUDIO_SERVICE_CLASS = (1 << 8) 426 TELEPHONY_SERVICE_CLASS = (1 << 9) 427 INFORMATION_SERVICE_CLASS = (1 << 10) 428 429 SERVICE_CLASS_LABELS = [ 430 'Limited Discoverable Mode', 431 'LE audio', 432 '(reserved)', 433 'Positioning', 434 'Networking', 435 'Rendering', 436 'Capturing', 437 'Object Transfer', 438 'Audio', 439 'Telephony', 440 'Information' 441 ] 442 443 # Major Device Classes 444 MISCELLANEOUS_MAJOR_DEVICE_CLASS = 0x00 445 COMPUTER_MAJOR_DEVICE_CLASS = 0x01 446 PHONE_MAJOR_DEVICE_CLASS = 0x02 447 LAN_NETWORK_ACCESS_POINT_MAJOR_DEVICE_CLASS = 0x03 448 AUDIO_VIDEO_MAJOR_DEVICE_CLASS = 0x04 449 PERIPHERAL_MAJOR_DEVICE_CLASS = 0x05 450 IMAGING_MAJOR_DEVICE_CLASS = 0x06 451 WEARABLE_MAJOR_DEVICE_CLASS = 0x07 452 TOY_MAJOR_DEVICE_CLASS = 0x08 453 HEALTH_MAJOR_DEVICE_CLASS = 0x09 454 UNCATEGORIZED_MAJOR_DEVICE_CLASS = 0x1F 455 456 MAJOR_DEVICE_CLASS_NAMES = { 457 MISCELLANEOUS_MAJOR_DEVICE_CLASS: 'Miscellaneous', 458 COMPUTER_MAJOR_DEVICE_CLASS: 'Computer', 459 PHONE_MAJOR_DEVICE_CLASS: 'Phone', 460 LAN_NETWORK_ACCESS_POINT_MAJOR_DEVICE_CLASS: 'LAN/Network Access Point', 461 AUDIO_VIDEO_MAJOR_DEVICE_CLASS: 'Audio/Video', 462 PERIPHERAL_MAJOR_DEVICE_CLASS: 'Peripheral', 463 IMAGING_MAJOR_DEVICE_CLASS: 'Imaging', 464 WEARABLE_MAJOR_DEVICE_CLASS: 'Wearable', 465 TOY_MAJOR_DEVICE_CLASS: 'Toy', 466 HEALTH_MAJOR_DEVICE_CLASS: 'Health', 467 UNCATEGORIZED_MAJOR_DEVICE_CLASS: 'Uncategorized' 468 } 469 470 COMPUTER_UNCATEGORIZED_MINOR_DEVICE_CLASS = 0x00 471 COMPUTER_DESKTOP_WORKSTATION_MINOR_DEVICE_CLASS = 0x01 472 COMPUTER_SERVER_CLASS_COMPUTER_MINOR_DEVICE_CLASS = 0x02 473 COMPUTER_LAPTOP_COMPUTER_MINOR_DEVICE_CLASS = 0x03 474 COMPUTER_HANDHELD_PC_PDA_MINOR_DEVICE_CLASS = 0x04 475 COMPUTER_PALM_SIZE_PC_PDA_MINOR_DEVICE_CLASS = 0x05 476 COMPUTER_WEARABLE_COMPUTER_MINOR_DEVICE_CLASS = 0x06 477 COMPUTER_TABLET_MINOR_DEVICE_CLASS = 0x07 478 479 COMPUTER_MINOR_DEVICE_CLASS_NAMES = { 480 COMPUTER_UNCATEGORIZED_MINOR_DEVICE_CLASS: 'Uncategorized', 481 COMPUTER_DESKTOP_WORKSTATION_MINOR_DEVICE_CLASS: 'Desktop workstation', 482 COMPUTER_SERVER_CLASS_COMPUTER_MINOR_DEVICE_CLASS: 'Server-class computer', 483 COMPUTER_LAPTOP_COMPUTER_MINOR_DEVICE_CLASS: 'Laptop', 484 COMPUTER_HANDHELD_PC_PDA_MINOR_DEVICE_CLASS: 'Handheld PC/PDA', 485 COMPUTER_PALM_SIZE_PC_PDA_MINOR_DEVICE_CLASS: 'Palm-size PC/PDA', 486 COMPUTER_WEARABLE_COMPUTER_MINOR_DEVICE_CLASS: 'Wearable computer', 487 COMPUTER_TABLET_MINOR_DEVICE_CLASS: 'Tablet' 488 } 489 490 PHONE_UNCATEGORIZED_MINOR_DEVICE_CLASS = 0x00 491 PHONE_CELLULAR_MINOR_DEVICE_CLASS = 0x01 492 PHONE_CORDLESS_MINOR_DEVICE_CLASS = 0x02 493 PHONE_SMARTPHONE_MINOR_DEVICE_CLASS = 0x03 494 PHONE_WIRED_MODEM_OR_VOICE_GATEWAY_MINOR_DEVICE_CLASS = 0x04 495 PHONE_COMMON_ISDN_MINOR_DEVICE_CLASS = 0x05 496 497 PHONE_MINOR_DEVICE_CLASS_NAMES = { 498 PHONE_UNCATEGORIZED_MINOR_DEVICE_CLASS: 'Uncategorized', 499 PHONE_CELLULAR_MINOR_DEVICE_CLASS: 'Cellular', 500 PHONE_CORDLESS_MINOR_DEVICE_CLASS: 'Cordless', 501 PHONE_SMARTPHONE_MINOR_DEVICE_CLASS: 'Smartphone', 502 PHONE_WIRED_MODEM_OR_VOICE_GATEWAY_MINOR_DEVICE_CLASS: 'Wired modem or voice gateway', 503 PHONE_COMMON_ISDN_MINOR_DEVICE_CLASS: 'Common ISDN access' 504 } 505 506 AUDIO_VIDEO_UNCATEGORIZED_MINOR_DEVICE_CLASS = 0x00 507 AUDIO_VIDEO_WEARABLE_HEADSET_DEVICE_MINOR_DEVICE_CLASS = 0x01 508 AUDIO_VIDEO_HANDS_FREE_DEVICE_MINOR_DEVICE_CLASS = 0x02 509 # (RESERVED) = 0x03 510 AUDIO_VIDEO_MICROPHONE_MINOR_DEVICE_CLASS = 0x04 511 AUDIO_VIDEO_LOUDSPEAKER_MINOR_DEVICE_CLASS = 0x05 512 AUDIO_VIDEO_HEADPHONES_MINOR_DEVICE_CLASS = 0x06 513 AUDIO_VIDEO_PORTABLE_AUDIO_MINOR_DEVICE_CLASS = 0x07 514 AUDIO_VIDEO_CAR_AUDIO_MINOR_DEVICE_CLASS = 0x08 515 AUDIO_VIDEO_SET_TOP_BOX_MINOR_DEVICE_CLASS = 0x09 516 AUDIO_VIDEO_HIFI_AUDIO_DEVICE_MINOR_DEVICE_CLASS = 0x0A 517 AUDIO_VIDEO_VCR_MINOR_DEVICE_CLASS = 0x0B 518 AUDIO_VIDEO_VIDEO_CAMERA_MINOR_DEVICE_CLASS = 0x0C 519 AUDIO_VIDEO_CAMCORDER_MINOR_DEVICE_CLASS = 0x0D 520 AUDIO_VIDEO_VIDEO_MONITOR_MINOR_DEVICE_CLASS = 0x0E 521 AUDIO_VIDEO_VIDEO_DISPLAY_AND_LOUDSPEAKER_MINOR_DEVICE_CLASS = 0x0F 522 AUDIO_VIDEO_VIDEO_CONFERENCING_MINOR_DEVICE_CLASS = 0x10 523 # (RESERVED) = 0x11 524 AUDIO_VIDEO_GAMING_OR_TOY_MINOR_DEVICE_CLASS = 0x12 525 526 AUDIO_VIDEO_MINOR_DEVICE_CLASS_NAMES = { 527 AUDIO_VIDEO_UNCATEGORIZED_MINOR_DEVICE_CLASS: 'Uncategorized', 528 AUDIO_VIDEO_WEARABLE_HEADSET_DEVICE_MINOR_DEVICE_CLASS: 'Wearable Headset Device', 529 AUDIO_VIDEO_HANDS_FREE_DEVICE_MINOR_DEVICE_CLASS: 'Hands-free Device', 530 AUDIO_VIDEO_MICROPHONE_MINOR_DEVICE_CLASS: 'Microphone', 531 AUDIO_VIDEO_LOUDSPEAKER_MINOR_DEVICE_CLASS: 'Loudspeaker', 532 AUDIO_VIDEO_HEADPHONES_MINOR_DEVICE_CLASS: 'Headphones', 533 AUDIO_VIDEO_PORTABLE_AUDIO_MINOR_DEVICE_CLASS: 'Portable Audio', 534 AUDIO_VIDEO_CAR_AUDIO_MINOR_DEVICE_CLASS: 'Car audio', 535 AUDIO_VIDEO_SET_TOP_BOX_MINOR_DEVICE_CLASS: 'Set-top box', 536 AUDIO_VIDEO_HIFI_AUDIO_DEVICE_MINOR_DEVICE_CLASS: 'HiFi Audio Device', 537 AUDIO_VIDEO_VCR_MINOR_DEVICE_CLASS: 'VCR', 538 AUDIO_VIDEO_VIDEO_CAMERA_MINOR_DEVICE_CLASS: 'Video Camera', 539 AUDIO_VIDEO_CAMCORDER_MINOR_DEVICE_CLASS: 'Camcorder', 540 AUDIO_VIDEO_VIDEO_MONITOR_MINOR_DEVICE_CLASS: 'Video Monitor', 541 AUDIO_VIDEO_VIDEO_DISPLAY_AND_LOUDSPEAKER_MINOR_DEVICE_CLASS: 'Video Display and Loudspeaker', 542 AUDIO_VIDEO_VIDEO_CONFERENCING_MINOR_DEVICE_CLASS: 'Video Conferencing', 543 AUDIO_VIDEO_GAMING_OR_TOY_MINOR_DEVICE_CLASS: 'Gaming/Toy' 544 } 545 546 PERIPHERAL_UNCATEGORIZED_MINOR_DEVICE_CLASS = 0x00 547 PERIPHERAL_KEYBOARD_MINOR_DEVICE_CLASS = 0x10 548 PERIPHERAL_POINTING_DEVICE_MINOR_DEVICE_CLASS = 0x20 549 PERIPHERAL_COMBO_KEYBOARD_POINTING_DEVICE_MINOR_DEVICE_CLASS = 0x30 550 PERIPHERAL_JOYSTICK_MINOR_DEVICE_CLASS = 0x01 551 PERIPHERAL_GAMEPAD_MINOR_DEVICE_CLASS = 0x02 552 PERIPHERAL_REMOTE_CONTROL_MINOR_DEVICE_CLASS = 0x03 553 PERIPHERAL_SENSING_DEVICE_MINOR_DEVICE_CLASS = 0x04 554 PERIPHERAL_DIGITIZER_TABLET_MINOR_DEVICE_CLASS = 0x05 555 PERIPHERAL_CARD_READER_MINOR_DEVICE_CLASS = 0x06 556 PERIPHERAL_DIGITAL_PEN_MINOR_DEVICE_CLASS = 0x07 557 PERIPHERAL_HANDHELD_SCANNER_MINOR_DEVICE_CLASS = 0x08 558 PERIPHERAL_HANDHELD_GESTURAL_INPUT_DEVICE_MINOR_DEVICE_CLASS = 0x09 559 560 PERIPHERAL_MINOR_DEVICE_CLASS_NAMES = { 561 PERIPHERAL_UNCATEGORIZED_MINOR_DEVICE_CLASS: 'Uncategorized', 562 PERIPHERAL_KEYBOARD_MINOR_DEVICE_CLASS: 'Keyboard', 563 PERIPHERAL_POINTING_DEVICE_MINOR_DEVICE_CLASS: 'Pointing device', 564 PERIPHERAL_COMBO_KEYBOARD_POINTING_DEVICE_MINOR_DEVICE_CLASS: 'Combo keyboard/pointing device', 565 PERIPHERAL_JOYSTICK_MINOR_DEVICE_CLASS: 'Joystick', 566 PERIPHERAL_GAMEPAD_MINOR_DEVICE_CLASS: 'Gamepad', 567 PERIPHERAL_REMOTE_CONTROL_MINOR_DEVICE_CLASS: 'Remote control', 568 PERIPHERAL_SENSING_DEVICE_MINOR_DEVICE_CLASS: 'Sensing device', 569 PERIPHERAL_DIGITIZER_TABLET_MINOR_DEVICE_CLASS: 'Digitizer tablet', 570 PERIPHERAL_CARD_READER_MINOR_DEVICE_CLASS: 'Card Reader', 571 PERIPHERAL_DIGITAL_PEN_MINOR_DEVICE_CLASS: 'Digital Pen', 572 PERIPHERAL_HANDHELD_SCANNER_MINOR_DEVICE_CLASS: 'Handheld scanner', 573 PERIPHERAL_HANDHELD_GESTURAL_INPUT_DEVICE_MINOR_DEVICE_CLASS: 'Handheld gestural input device' 574 } 575 576 MINOR_DEVICE_CLASS_NAMES = { 577 COMPUTER_MAJOR_DEVICE_CLASS: COMPUTER_MINOR_DEVICE_CLASS_NAMES, 578 PHONE_MAJOR_DEVICE_CLASS: PHONE_MINOR_DEVICE_CLASS_NAMES, 579 AUDIO_VIDEO_MAJOR_DEVICE_CLASS: AUDIO_VIDEO_MINOR_DEVICE_CLASS_NAMES, 580 PERIPHERAL_MAJOR_DEVICE_CLASS: PERIPHERAL_MINOR_DEVICE_CLASS_NAMES 581 } 582 583 # fmt: on 584 # pylint: enable=line-too-long 585 586 @staticmethod 587 def split_class_of_device(class_of_device): 588 # Split the bit fields of the composite class of device value into: 589 # (service_classes, major_device_class, minor_device_class) 590 return ( 591 (class_of_device >> 13 & 0x7FF), 592 (class_of_device >> 8 & 0x1F), 593 (class_of_device >> 2 & 0x3F), 594 ) 595 596 @staticmethod 597 def pack_class_of_device(service_classes, major_device_class, minor_device_class): 598 return service_classes << 13 | major_device_class << 8 | minor_device_class << 2 599 600 @staticmethod 601 def service_class_labels(service_class_flags): 602 return bit_flags_to_strings( 603 service_class_flags, DeviceClass.SERVICE_CLASS_LABELS 604 ) 605 606 @staticmethod 607 def major_device_class_name(device_class): 608 return name_or_number(DeviceClass.MAJOR_DEVICE_CLASS_NAMES, device_class) 609 610 @staticmethod 611 def minor_device_class_name(major_device_class, minor_device_class): 612 class_names = DeviceClass.MINOR_DEVICE_CLASS_NAMES.get(major_device_class) 613 if class_names is None: 614 return f'#{minor_device_class:02X}' 615 return name_or_number(class_names, minor_device_class) 616 617 618# ----------------------------------------------------------------------------- 619# Advertising Data 620# ----------------------------------------------------------------------------- 621AdvertisingObject = Union[ 622 List[UUID], Tuple[UUID, bytes], bytes, str, int, Tuple[int, int], Tuple[int, bytes] 623] 624 625 626class AdvertisingData: 627 # fmt: off 628 # pylint: disable=line-too-long 629 630 # This list is only partial, it still needs to be filled in from the spec 631 FLAGS = 0x01 632 INCOMPLETE_LIST_OF_16_BIT_SERVICE_CLASS_UUIDS = 0x02 633 COMPLETE_LIST_OF_16_BIT_SERVICE_CLASS_UUIDS = 0x03 634 INCOMPLETE_LIST_OF_32_BIT_SERVICE_CLASS_UUIDS = 0x04 635 COMPLETE_LIST_OF_32_BIT_SERVICE_CLASS_UUIDS = 0x05 636 INCOMPLETE_LIST_OF_128_BIT_SERVICE_CLASS_UUIDS = 0x06 637 COMPLETE_LIST_OF_128_BIT_SERVICE_CLASS_UUIDS = 0x07 638 SHORTENED_LOCAL_NAME = 0x08 639 COMPLETE_LOCAL_NAME = 0x09 640 TX_POWER_LEVEL = 0x0A 641 CLASS_OF_DEVICE = 0x0D 642 SIMPLE_PAIRING_HASH_C = 0x0E 643 SIMPLE_PAIRING_HASH_C_192 = 0x0E 644 SIMPLE_PAIRING_RANDOMIZER_R = 0x0F 645 SIMPLE_PAIRING_RANDOMIZER_R_192 = 0x0F 646 DEVICE_ID = 0x10 647 SECURITY_MANAGER_TK_VALUE = 0x10 648 SECURITY_MANAGER_OUT_OF_BAND_FLAGS = 0x11 649 PERIPHERAL_CONNECTION_INTERVAL_RANGE = 0x12 650 LIST_OF_16_BIT_SERVICE_SOLICITATION_UUIDS = 0x14 651 LIST_OF_128_BIT_SERVICE_SOLICITATION_UUIDS = 0x15 652 SERVICE_DATA = 0x16 653 SERVICE_DATA_16_BIT_UUID = 0x16 654 PUBLIC_TARGET_ADDRESS = 0x17 655 RANDOM_TARGET_ADDRESS = 0x18 656 APPEARANCE = 0x19 657 ADVERTISING_INTERVAL = 0x1A 658 LE_BLUETOOTH_DEVICE_ADDRESS = 0x1B 659 LE_ROLE = 0x1C 660 SIMPLE_PAIRING_HASH_C_256 = 0x1D 661 SIMPLE_PAIRING_RANDOMIZER_R_256 = 0x1E 662 LIST_OF_32_BIT_SERVICE_SOLICITATION_UUIDS = 0x1F 663 SERVICE_DATA_32_BIT_UUID = 0x20 664 SERVICE_DATA_128_BIT_UUID = 0x21 665 LE_SECURE_CONNECTIONS_CONFIRMATION_VALUE = 0x22 666 LE_SECURE_CONNECTIONS_RANDOM_VALUE = 0x23 667 URI = 0x24 668 INDOOR_POSITIONING = 0x25 669 TRANSPORT_DISCOVERY_DATA = 0x26 670 LE_SUPPORTED_FEATURES = 0x27 671 CHANNEL_MAP_UPDATE_INDICATION = 0x28 672 PB_ADV = 0x29 673 MESH_MESSAGE = 0x2A 674 MESH_BEACON = 0x2B 675 BIGINFO = 0x2C 676 BROADCAST_CODE = 0x2D 677 RESOLVABLE_SET_IDENTIFIER = 0x2E 678 ADVERTISING_INTERVAL_LONG = 0x2F 679 THREE_D_INFORMATION_DATA = 0x3D 680 MANUFACTURER_SPECIFIC_DATA = 0xFF 681 682 AD_TYPE_NAMES = { 683 FLAGS: 'FLAGS', 684 INCOMPLETE_LIST_OF_16_BIT_SERVICE_CLASS_UUIDS: 'INCOMPLETE_LIST_OF_16_BIT_SERVICE_CLASS_UUIDS', 685 COMPLETE_LIST_OF_16_BIT_SERVICE_CLASS_UUIDS: 'COMPLETE_LIST_OF_16_BIT_SERVICE_CLASS_UUIDS', 686 INCOMPLETE_LIST_OF_32_BIT_SERVICE_CLASS_UUIDS: 'INCOMPLETE_LIST_OF_32_BIT_SERVICE_CLASS_UUIDS', 687 COMPLETE_LIST_OF_32_BIT_SERVICE_CLASS_UUIDS: 'COMPLETE_LIST_OF_32_BIT_SERVICE_CLASS_UUIDS', 688 INCOMPLETE_LIST_OF_128_BIT_SERVICE_CLASS_UUIDS: 'INCOMPLETE_LIST_OF_128_BIT_SERVICE_CLASS_UUIDS', 689 COMPLETE_LIST_OF_128_BIT_SERVICE_CLASS_UUIDS: 'COMPLETE_LIST_OF_128_BIT_SERVICE_CLASS_UUIDS', 690 SHORTENED_LOCAL_NAME: 'SHORTENED_LOCAL_NAME', 691 COMPLETE_LOCAL_NAME: 'COMPLETE_LOCAL_NAME', 692 TX_POWER_LEVEL: 'TX_POWER_LEVEL', 693 CLASS_OF_DEVICE: 'CLASS_OF_DEVICE', 694 SIMPLE_PAIRING_HASH_C: 'SIMPLE_PAIRING_HASH_C', 695 SIMPLE_PAIRING_HASH_C_192: 'SIMPLE_PAIRING_HASH_C_192', 696 SIMPLE_PAIRING_RANDOMIZER_R: 'SIMPLE_PAIRING_RANDOMIZER_R', 697 SIMPLE_PAIRING_RANDOMIZER_R_192: 'SIMPLE_PAIRING_RANDOMIZER_R_192', 698 DEVICE_ID: 'DEVICE_ID', 699 SECURITY_MANAGER_TK_VALUE: 'SECURITY_MANAGER_TK_VALUE', 700 SECURITY_MANAGER_OUT_OF_BAND_FLAGS: 'SECURITY_MANAGER_OUT_OF_BAND_FLAGS', 701 PERIPHERAL_CONNECTION_INTERVAL_RANGE: 'PERIPHERAL_CONNECTION_INTERVAL_RANGE', 702 LIST_OF_16_BIT_SERVICE_SOLICITATION_UUIDS: 'LIST_OF_16_BIT_SERVICE_SOLICITATION_UUIDS', 703 LIST_OF_128_BIT_SERVICE_SOLICITATION_UUIDS: 'LIST_OF_128_BIT_SERVICE_SOLICITATION_UUIDS', 704 SERVICE_DATA: 'SERVICE_DATA', 705 SERVICE_DATA_16_BIT_UUID: 'SERVICE_DATA_16_BIT_UUID', 706 PUBLIC_TARGET_ADDRESS: 'PUBLIC_TARGET_ADDRESS', 707 RANDOM_TARGET_ADDRESS: 'RANDOM_TARGET_ADDRESS', 708 APPEARANCE: 'APPEARANCE', 709 ADVERTISING_INTERVAL: 'ADVERTISING_INTERVAL', 710 LE_BLUETOOTH_DEVICE_ADDRESS: 'LE_BLUETOOTH_DEVICE_ADDRESS', 711 LE_ROLE: 'LE_ROLE', 712 SIMPLE_PAIRING_HASH_C_256: 'SIMPLE_PAIRING_HASH_C_256', 713 SIMPLE_PAIRING_RANDOMIZER_R_256: 'SIMPLE_PAIRING_RANDOMIZER_R_256', 714 LIST_OF_32_BIT_SERVICE_SOLICITATION_UUIDS: 'LIST_OF_32_BIT_SERVICE_SOLICITATION_UUIDS', 715 SERVICE_DATA_32_BIT_UUID: 'SERVICE_DATA_32_BIT_UUID', 716 SERVICE_DATA_128_BIT_UUID: 'SERVICE_DATA_128_BIT_UUID', 717 LE_SECURE_CONNECTIONS_CONFIRMATION_VALUE: 'LE_SECURE_CONNECTIONS_CONFIRMATION_VALUE', 718 LE_SECURE_CONNECTIONS_RANDOM_VALUE: 'LE_SECURE_CONNECTIONS_RANDOM_VALUE', 719 URI: 'URI', 720 INDOOR_POSITIONING: 'INDOOR_POSITIONING', 721 TRANSPORT_DISCOVERY_DATA: 'TRANSPORT_DISCOVERY_DATA', 722 LE_SUPPORTED_FEATURES: 'LE_SUPPORTED_FEATURES', 723 CHANNEL_MAP_UPDATE_INDICATION: 'CHANNEL_MAP_UPDATE_INDICATION', 724 PB_ADV: 'PB_ADV', 725 MESH_MESSAGE: 'MESH_MESSAGE', 726 MESH_BEACON: 'MESH_BEACON', 727 BIGINFO: 'BIGINFO', 728 BROADCAST_CODE: 'BROADCAST_CODE', 729 RESOLVABLE_SET_IDENTIFIER: 'RESOLVABLE_SET_IDENTIFIER', 730 ADVERTISING_INTERVAL_LONG: 'ADVERTISING_INTERVAL_LONG', 731 THREE_D_INFORMATION_DATA: 'THREE_D_INFORMATION_DATA', 732 MANUFACTURER_SPECIFIC_DATA: 'MANUFACTURER_SPECIFIC_DATA' 733 } 734 735 LE_LIMITED_DISCOVERABLE_MODE_FLAG = 0x01 736 LE_GENERAL_DISCOVERABLE_MODE_FLAG = 0x02 737 BR_EDR_NOT_SUPPORTED_FLAG = 0x04 738 BR_EDR_CONTROLLER_FLAG = 0x08 739 BR_EDR_HOST_FLAG = 0x10 740 741 ad_structures: List[Tuple[int, bytes]] 742 743 # fmt: on 744 # pylint: enable=line-too-long 745 746 def __init__(self, ad_structures: Optional[List[Tuple[int, bytes]]] = None) -> None: 747 if ad_structures is None: 748 ad_structures = [] 749 self.ad_structures = ad_structures[:] 750 751 @staticmethod 752 def from_bytes(data): 753 instance = AdvertisingData() 754 instance.append(data) 755 return instance 756 757 @staticmethod 758 def flags_to_string(flags, short=False): 759 flag_names = ( 760 ['LE Limited', 'LE General', 'No BR/EDR', 'BR/EDR C', 'BR/EDR H'] 761 if short 762 else [ 763 'LE Limited Discoverable Mode', 764 'LE General Discoverable Mode', 765 'BR/EDR Not Supported', 766 'Simultaneous LE and BR/EDR (Controller)', 767 'Simultaneous LE and BR/EDR (Host)', 768 ] 769 ) 770 return ','.join(bit_flags_to_strings(flags, flag_names)) 771 772 @staticmethod 773 def uuid_list_to_objects(ad_data: bytes, uuid_size: int) -> List[UUID]: 774 uuids = [] 775 offset = 0 776 while (uuid_size * (offset + 1)) <= len(ad_data): 777 uuids.append(UUID.from_bytes(ad_data[offset : offset + uuid_size])) 778 offset += uuid_size 779 return uuids 780 781 @staticmethod 782 def uuid_list_to_string(ad_data, uuid_size): 783 return ', '.join( 784 [ 785 str(uuid) 786 for uuid in AdvertisingData.uuid_list_to_objects(ad_data, uuid_size) 787 ] 788 ) 789 790 @staticmethod 791 def ad_data_to_string(ad_type, ad_data): 792 if ad_type == AdvertisingData.FLAGS: 793 ad_type_str = 'Flags' 794 ad_data_str = AdvertisingData.flags_to_string(ad_data[0], short=True) 795 elif ad_type == AdvertisingData.COMPLETE_LIST_OF_16_BIT_SERVICE_CLASS_UUIDS: 796 ad_type_str = 'Complete List of 16-bit Service Class UUIDs' 797 ad_data_str = AdvertisingData.uuid_list_to_string(ad_data, 2) 798 elif ad_type == AdvertisingData.INCOMPLETE_LIST_OF_16_BIT_SERVICE_CLASS_UUIDS: 799 ad_type_str = 'Incomplete List of 16-bit Service Class UUIDs' 800 ad_data_str = AdvertisingData.uuid_list_to_string(ad_data, 2) 801 elif ad_type == AdvertisingData.COMPLETE_LIST_OF_32_BIT_SERVICE_CLASS_UUIDS: 802 ad_type_str = 'Complete List of 32-bit Service Class UUIDs' 803 ad_data_str = AdvertisingData.uuid_list_to_string(ad_data, 4) 804 elif ad_type == AdvertisingData.INCOMPLETE_LIST_OF_32_BIT_SERVICE_CLASS_UUIDS: 805 ad_type_str = 'Incomplete List of 32-bit Service Class UUIDs' 806 ad_data_str = AdvertisingData.uuid_list_to_string(ad_data, 4) 807 elif ad_type == AdvertisingData.COMPLETE_LIST_OF_128_BIT_SERVICE_CLASS_UUIDS: 808 ad_type_str = 'Complete List of 128-bit Service Class UUIDs' 809 ad_data_str = AdvertisingData.uuid_list_to_string(ad_data, 16) 810 elif ad_type == AdvertisingData.INCOMPLETE_LIST_OF_128_BIT_SERVICE_CLASS_UUIDS: 811 ad_type_str = 'Incomplete List of 128-bit Service Class UUIDs' 812 ad_data_str = AdvertisingData.uuid_list_to_string(ad_data, 16) 813 elif ad_type == AdvertisingData.SERVICE_DATA_16_BIT_UUID: 814 ad_type_str = 'Service Data' 815 uuid = UUID.from_bytes(ad_data[:2]) 816 ad_data_str = f'service={uuid}, data={ad_data[2:].hex()}' 817 elif ad_type == AdvertisingData.SERVICE_DATA_32_BIT_UUID: 818 ad_type_str = 'Service Data' 819 uuid = UUID.from_bytes(ad_data[:4]) 820 ad_data_str = f'service={uuid}, data={ad_data[4:].hex()}' 821 elif ad_type == AdvertisingData.SERVICE_DATA_128_BIT_UUID: 822 ad_type_str = 'Service Data' 823 uuid = UUID.from_bytes(ad_data[:16]) 824 ad_data_str = f'service={uuid}, data={ad_data[16:].hex()}' 825 elif ad_type == AdvertisingData.SHORTENED_LOCAL_NAME: 826 ad_type_str = 'Shortened Local Name' 827 ad_data_str = f'"{ad_data.decode("utf-8")}"' 828 elif ad_type == AdvertisingData.COMPLETE_LOCAL_NAME: 829 ad_type_str = 'Complete Local Name' 830 ad_data_str = f'"{ad_data.decode("utf-8")}"' 831 elif ad_type == AdvertisingData.TX_POWER_LEVEL: 832 ad_type_str = 'TX Power Level' 833 ad_data_str = str(ad_data[0]) 834 elif ad_type == AdvertisingData.MANUFACTURER_SPECIFIC_DATA: 835 ad_type_str = 'Manufacturer Specific Data' 836 company_id = struct.unpack_from('<H', ad_data, 0)[0] 837 company_name = COMPANY_IDENTIFIERS.get(company_id, f'0x{company_id:04X}') 838 ad_data_str = f'company={company_name}, data={ad_data[2:].hex()}' 839 elif ad_type == AdvertisingData.APPEARANCE: 840 ad_type_str = 'Appearance' 841 ad_data_str = ad_data.hex() 842 else: 843 ad_type_str = AdvertisingData.AD_TYPE_NAMES.get(ad_type, f'0x{ad_type:02X}') 844 ad_data_str = ad_data.hex() 845 846 return f'[{ad_type_str}]: {ad_data_str}' 847 848 # pylint: disable=too-many-return-statements 849 @staticmethod 850 def ad_data_to_object(ad_type: int, ad_data: bytes) -> AdvertisingObject: 851 if ad_type in ( 852 AdvertisingData.COMPLETE_LIST_OF_16_BIT_SERVICE_CLASS_UUIDS, 853 AdvertisingData.INCOMPLETE_LIST_OF_16_BIT_SERVICE_CLASS_UUIDS, 854 AdvertisingData.LIST_OF_16_BIT_SERVICE_SOLICITATION_UUIDS, 855 ): 856 return AdvertisingData.uuid_list_to_objects(ad_data, 2) 857 858 if ad_type in ( 859 AdvertisingData.COMPLETE_LIST_OF_32_BIT_SERVICE_CLASS_UUIDS, 860 AdvertisingData.INCOMPLETE_LIST_OF_32_BIT_SERVICE_CLASS_UUIDS, 861 AdvertisingData.LIST_OF_32_BIT_SERVICE_SOLICITATION_UUIDS, 862 ): 863 return AdvertisingData.uuid_list_to_objects(ad_data, 4) 864 865 if ad_type in ( 866 AdvertisingData.COMPLETE_LIST_OF_128_BIT_SERVICE_CLASS_UUIDS, 867 AdvertisingData.INCOMPLETE_LIST_OF_128_BIT_SERVICE_CLASS_UUIDS, 868 AdvertisingData.LIST_OF_128_BIT_SERVICE_SOLICITATION_UUIDS, 869 ): 870 return AdvertisingData.uuid_list_to_objects(ad_data, 16) 871 872 if ad_type == AdvertisingData.SERVICE_DATA_16_BIT_UUID: 873 return (UUID.from_bytes(ad_data[:2]), ad_data[2:]) 874 875 if ad_type == AdvertisingData.SERVICE_DATA_32_BIT_UUID: 876 return (UUID.from_bytes(ad_data[:4]), ad_data[4:]) 877 878 if ad_type == AdvertisingData.SERVICE_DATA_128_BIT_UUID: 879 return (UUID.from_bytes(ad_data[:16]), ad_data[16:]) 880 881 if ad_type in ( 882 AdvertisingData.SHORTENED_LOCAL_NAME, 883 AdvertisingData.COMPLETE_LOCAL_NAME, 884 AdvertisingData.URI, 885 ): 886 return ad_data.decode("utf-8") 887 888 if ad_type in (AdvertisingData.TX_POWER_LEVEL, AdvertisingData.FLAGS): 889 return cast(int, struct.unpack('B', ad_data)[0]) 890 891 if ad_type in ( 892 AdvertisingData.APPEARANCE, 893 AdvertisingData.ADVERTISING_INTERVAL, 894 ): 895 return cast(int, struct.unpack('<H', ad_data)[0]) 896 897 if ad_type == AdvertisingData.CLASS_OF_DEVICE: 898 return cast(int, struct.unpack('<I', bytes([*ad_data, 0]))[0]) 899 900 if ad_type == AdvertisingData.PERIPHERAL_CONNECTION_INTERVAL_RANGE: 901 return cast(Tuple[int, int], struct.unpack('<HH', ad_data)) 902 903 if ad_type == AdvertisingData.MANUFACTURER_SPECIFIC_DATA: 904 return (cast(int, struct.unpack_from('<H', ad_data, 0)[0]), ad_data[2:]) 905 906 return ad_data 907 908 def append(self, data): 909 offset = 0 910 while offset + 1 < len(data): 911 length = data[offset] 912 offset += 1 913 if length > 0: 914 ad_type = data[offset] 915 ad_data = data[offset + 1 : offset + length] 916 self.ad_structures.append((ad_type, ad_data)) 917 offset += length 918 919 def get_all(self, type_id: int, raw: bool = False) -> List[AdvertisingObject]: 920 ''' 921 Get Advertising Data Structure(s) with a given type 922 923 Returns a (possibly empty) list of matches. 924 ''' 925 926 def process_ad_data(ad_data: bytes) -> AdvertisingObject: 927 return ad_data if raw else self.ad_data_to_object(type_id, ad_data) 928 929 return [process_ad_data(ad[1]) for ad in self.ad_structures if ad[0] == type_id] 930 931 def get(self, type_id: int, raw: bool = False) -> Optional[AdvertisingObject]: 932 ''' 933 Get Advertising Data Structure(s) with a given type 934 935 Returns the first entry, or None if no structure matches. 936 ''' 937 938 all = self.get_all(type_id, raw=raw) 939 return all[0] if all else None 940 941 def __bytes__(self): 942 return b''.join( 943 [bytes([len(x[1]) + 1, x[0]]) + x[1] for x in self.ad_structures] 944 ) 945 946 def to_string(self, separator=', '): 947 return separator.join( 948 [AdvertisingData.ad_data_to_string(x[0], x[1]) for x in self.ad_structures] 949 ) 950 951 def __str__(self): 952 return self.to_string() 953 954 955# ----------------------------------------------------------------------------- 956# Connection Parameters 957# ----------------------------------------------------------------------------- 958class ConnectionParameters: 959 def __init__(self, connection_interval, peripheral_latency, supervision_timeout): 960 self.connection_interval = connection_interval 961 self.peripheral_latency = peripheral_latency 962 self.supervision_timeout = supervision_timeout 963 964 def __str__(self): 965 return ( 966 f'ConnectionParameters(connection_interval={self.connection_interval}, ' 967 f'peripheral_latency={self.peripheral_latency}, ' 968 f'supervision_timeout={self.supervision_timeout}' 969 ) 970 971 972# ----------------------------------------------------------------------------- 973# Connection PHY 974# ----------------------------------------------------------------------------- 975class ConnectionPHY: 976 def __init__(self, tx_phy, rx_phy): 977 self.tx_phy = tx_phy 978 self.rx_phy = rx_phy 979 980 def __str__(self): 981 return f'ConnectionPHY(tx_phy={self.tx_phy}, rx_phy={self.rx_phy})' 982