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