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