• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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