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 dataclasses
20import enum
21import struct
22from typing import List, Optional, Tuple, Union, cast, Dict
23from typing_extensions import Self
24
25from bumble.company_ids import COMPANY_IDENTIFIERS
26from bumble.utils import OpenIntEnum
27
28
29# -----------------------------------------------------------------------------
30# Constants
31# -----------------------------------------------------------------------------
32# fmt: off
33
34BT_CENTRAL_ROLE    = 0
35BT_PERIPHERAL_ROLE = 1
36
37BT_BR_EDR_TRANSPORT = 0
38BT_LE_TRANSPORT     = 1
39
40
41# fmt: on
42
43
44# -----------------------------------------------------------------------------
45# Utils
46# -----------------------------------------------------------------------------
47def bit_flags_to_strings(bits, bit_flag_names):
48    names = []
49    index = 0
50    while bits != 0:
51        if bits & 1:
52            name = bit_flag_names[index] if index < len(bit_flag_names) else f'#{index}'
53            names.append(name)
54        bits >>= 1
55        index += 1
56
57    return names
58
59
60def name_or_number(dictionary: Dict[int, str], number: int, width: int = 2) -> str:
61    name = dictionary.get(number)
62    if name is not None:
63        return name
64    return f'[0x{number:0{width}X}]'
65
66
67def padded_bytes(buffer, size):
68    padding_size = max(size - len(buffer), 0)
69    return buffer + bytes(padding_size)
70
71
72def get_dict_key_by_value(dictionary, value):
73    for key, val in dictionary.items():
74        if val == value:
75            return key
76    return None
77
78
79# -----------------------------------------------------------------------------
80# Exceptions
81# -----------------------------------------------------------------------------
82
83
84class BaseBumbleError(Exception):
85    """Base Error raised by Bumble."""
86
87
88class BaseError(BaseBumbleError):
89    """Base class for errors with an error code, error name and namespace"""
90
91    def __init__(
92        self,
93        error_code: Optional[int],
94        error_namespace: str = '',
95        error_name: str = '',
96        details: str = '',
97    ):
98        super().__init__()
99        self.error_code = error_code
100        self.error_namespace = error_namespace
101        self.error_name = error_name
102        self.details = details
103
104    def __str__(self):
105        if self.error_namespace:
106            namespace = f'{self.error_namespace}/'
107        else:
108            namespace = ''
109        have_name = self.error_name != ''
110        have_code = self.error_code is not None
111        if have_name and have_code:
112            error_text = f'{self.error_name} [0x{self.error_code:X}]'
113        elif have_name and not have_code:
114            error_text = self.error_name
115        elif not have_name and have_code:
116            error_text = f'0x{self.error_code:X}'
117        else:
118            error_text = '<unspecified>'
119
120        return f'{type(self).__name__}({namespace}{error_text})'
121
122
123class ProtocolError(BaseError):
124    """Protocol Error"""
125
126
127class TimeoutError(BaseBumbleError):  # pylint: disable=redefined-builtin
128    """Timeout Error"""
129
130
131class CommandTimeoutError(BaseBumbleError):
132    """Command Timeout Error"""
133
134
135class InvalidStateError(BaseBumbleError):
136    """Invalid State Error"""
137
138
139class InvalidArgumentError(BaseBumbleError, ValueError):
140    """Invalid Argument Error"""
141
142
143class InvalidPacketError(BaseBumbleError, ValueError):
144    """Invalid Packet Error"""
145
146
147class InvalidOperationError(BaseBumbleError, RuntimeError):
148    """Invalid Operation Error"""
149
150
151class NotSupportedError(BaseBumbleError, RuntimeError):
152    """Not Supported"""
153
154
155class OutOfResourcesError(BaseBumbleError, RuntimeError):
156    """Out of Resources Error"""
157
158
159class UnreachableError(BaseBumbleError):
160    """The code path raising this error should be unreachable."""
161
162
163class ConnectionError(BaseError):  # pylint: disable=redefined-builtin
164    """Connection Error"""
165
166    FAILURE = 0x01
167    CONNECTION_REFUSED = 0x02
168
169    def __init__(
170        self,
171        error_code,
172        transport,
173        peer_address,
174        error_namespace='',
175        error_name='',
176        details='',
177    ):
178        super().__init__(error_code, error_namespace, error_name, details)
179        self.transport = transport
180        self.peer_address = peer_address
181
182
183class ConnectionParameterUpdateError(BaseError):
184    """Connection Parameter Update Error"""
185
186
187# -----------------------------------------------------------------------------
188# UUID
189#
190# NOTE: the internal byte representation is in little-endian byte order
191#
192# Base UUID: 00000000-0000-1000-8000- 00805F9B34FB
193# -----------------------------------------------------------------------------
194class UUID:
195    '''
196    See Bluetooth spec Vol 3, Part B - 2.5.1 UUID
197
198    Note that this class expects and works in little-endian byte-order throughout.
199    The exception is when interacting with strings, which are in big-endian byte-order.
200    '''
201
202    BASE_UUID = bytes.fromhex('00001000800000805F9B34FB')[::-1]  # little-endian
203    UUIDS: List[UUID] = []  # Registry of all instances created
204
205    uuid_bytes: bytes
206    name: Optional[str]
207
208    def __init__(
209        self, uuid_str_or_int: Union[str, int], name: Optional[str] = None
210    ) -> None:
211        if isinstance(uuid_str_or_int, int):
212            self.uuid_bytes = struct.pack('<H', uuid_str_or_int)
213        else:
214            if len(uuid_str_or_int) == 36:
215                if (
216                    uuid_str_or_int[8] != '-'
217                    or uuid_str_or_int[13] != '-'
218                    or uuid_str_or_int[18] != '-'
219                    or uuid_str_or_int[23] != '-'
220                ):
221                    raise InvalidArgumentError('invalid UUID format')
222                uuid_str = uuid_str_or_int.replace('-', '')
223            else:
224                uuid_str = uuid_str_or_int
225            if len(uuid_str) != 32 and len(uuid_str) != 8 and len(uuid_str) != 4:
226                raise InvalidArgumentError(f"invalid UUID format: {uuid_str}")
227            self.uuid_bytes = bytes(reversed(bytes.fromhex(uuid_str)))
228        self.name = name
229
230    def register(self) -> UUID:
231        # Register this object in the class registry, and update the entry's name if
232        # it wasn't set already
233        for uuid in self.UUIDS:
234            if self == uuid:
235                if uuid.name is None:
236                    uuid.name = self.name
237                return uuid
238
239        self.UUIDS.append(self)
240        return self
241
242    @classmethod
243    def from_bytes(cls, uuid_bytes: bytes, name: Optional[str] = None) -> UUID:
244        if len(uuid_bytes) in (2, 4, 16):
245            self = cls.__new__(cls)
246            self.uuid_bytes = uuid_bytes
247            self.name = name
248
249            return self.register()
250
251        raise InvalidArgumentError('only 2, 4 and 16 bytes are allowed')
252
253    @classmethod
254    def from_16_bits(cls, uuid_16: int, name: Optional[str] = None) -> UUID:
255        return cls.from_bytes(struct.pack('<H', uuid_16), name)
256
257    @classmethod
258    def from_32_bits(cls, uuid_32: int, name: Optional[str] = None) -> UUID:
259        return cls.from_bytes(struct.pack('<I', uuid_32), name)
260
261    @classmethod
262    def parse_uuid(cls, uuid_as_bytes: bytes, offset: int) -> Tuple[int, UUID]:
263        return len(uuid_as_bytes), cls.from_bytes(uuid_as_bytes[offset:])
264
265    @classmethod
266    def parse_uuid_2(cls, uuid_as_bytes: bytes, offset: int) -> Tuple[int, UUID]:
267        return offset + 2, cls.from_bytes(uuid_as_bytes[offset : offset + 2])
268
269    def to_bytes(self, force_128: bool = False) -> bytes:
270        '''
271        Serialize UUID in little-endian byte-order
272        '''
273        if not force_128:
274            return self.uuid_bytes
275
276        if len(self.uuid_bytes) == 2:
277            return self.BASE_UUID + self.uuid_bytes + bytes([0, 0])
278        elif len(self.uuid_bytes) == 4:
279            return self.BASE_UUID + self.uuid_bytes
280        elif len(self.uuid_bytes) == 16:
281            return self.uuid_bytes
282        else:
283            assert False, "unreachable"
284
285    def to_pdu_bytes(self) -> bytes:
286        '''
287        Convert to bytes for use in an ATT PDU.
288        According to Vol 3, Part F - 3.2.1 Attribute Type:
289        "All 32-bit Attribute UUIDs shall be converted to 128-bit UUIDs when the
290         Attribute UUID is contained in an ATT PDU."
291        '''
292        return self.to_bytes(force_128=(len(self.uuid_bytes) == 4))
293
294    def to_hex_str(self, separator: str = '') -> str:
295        if len(self.uuid_bytes) == 2 or len(self.uuid_bytes) == 4:
296            return bytes(reversed(self.uuid_bytes)).hex().upper()
297
298        return separator.join(
299            [
300                bytes(reversed(self.uuid_bytes[12:16])).hex(),
301                bytes(reversed(self.uuid_bytes[10:12])).hex(),
302                bytes(reversed(self.uuid_bytes[8:10])).hex(),
303                bytes(reversed(self.uuid_bytes[6:8])).hex(),
304                bytes(reversed(self.uuid_bytes[0:6])).hex(),
305            ]
306        ).upper()
307
308    def __bytes__(self) -> bytes:
309        return self.to_bytes()
310
311    def __eq__(self, other: object) -> bool:
312        if isinstance(other, UUID):
313            return self.to_bytes(force_128=True) == other.to_bytes(force_128=True)
314
315        if isinstance(other, str):
316            return UUID(other) == self
317
318        return False
319
320    def __hash__(self) -> int:
321        return hash(self.uuid_bytes)
322
323    def __str__(self) -> str:
324        result = self.to_hex_str(separator='-')
325        if len(self.uuid_bytes) == 2:
326            result = 'UUID-16:' + result
327        elif len(self.uuid_bytes) == 4:
328            result = 'UUID-32:' + result
329        if self.name is not None:
330            result += f' ({self.name})'
331        return result
332
333
334# -----------------------------------------------------------------------------
335# Common UUID constants
336# -----------------------------------------------------------------------------
337# fmt: off
338# pylint: disable=line-too-long
339
340# Protocol Identifiers
341BT_SDP_PROTOCOL_ID                      = UUID.from_16_bits(0x0001, 'SDP')
342BT_UDP_PROTOCOL_ID                      = UUID.from_16_bits(0x0002, 'UDP')
343BT_RFCOMM_PROTOCOL_ID                   = UUID.from_16_bits(0x0003, 'RFCOMM')
344BT_TCP_PROTOCOL_ID                      = UUID.from_16_bits(0x0004, 'TCP')
345BT_TCS_BIN_PROTOCOL_ID                  = UUID.from_16_bits(0x0005, 'TCP-BIN')
346BT_TCS_AT_PROTOCOL_ID                   = UUID.from_16_bits(0x0006, 'TCS-AT')
347BT_ATT_PROTOCOL_ID                      = UUID.from_16_bits(0x0007, 'ATT')
348BT_OBEX_PROTOCOL_ID                     = UUID.from_16_bits(0x0008, 'OBEX')
349BT_IP_PROTOCOL_ID                       = UUID.from_16_bits(0x0009, 'IP')
350BT_FTP_PROTOCOL_ID                      = UUID.from_16_bits(0x000A, 'FTP')
351BT_HTTP_PROTOCOL_ID                     = UUID.from_16_bits(0x000C, 'HTTP')
352BT_WSP_PROTOCOL_ID                      = UUID.from_16_bits(0x000E, 'WSP')
353BT_BNEP_PROTOCOL_ID                     = UUID.from_16_bits(0x000F, 'BNEP')
354BT_UPNP_PROTOCOL_ID                     = UUID.from_16_bits(0x0010, 'UPNP')
355BT_HIDP_PROTOCOL_ID                     = UUID.from_16_bits(0x0011, 'HIDP')
356BT_HARDCOPY_CONTROL_CHANNEL_PROTOCOL_ID = UUID.from_16_bits(0x0012, 'HardcopyControlChannel')
357BT_HARDCOPY_DATA_CHANNEL_PROTOCOL_ID    = UUID.from_16_bits(0x0014, 'HardcopyDataChannel')
358BT_HARDCOPY_NOTIFICATION_PROTOCOL_ID    = UUID.from_16_bits(0x0016, 'HardcopyNotification')
359BT_AVCTP_PROTOCOL_ID                    = UUID.from_16_bits(0x0017, 'AVCTP')
360BT_AVDTP_PROTOCOL_ID                    = UUID.from_16_bits(0x0019, 'AVDTP')
361BT_CMTP_PROTOCOL_ID                     = UUID.from_16_bits(0x001B, 'CMTP')
362BT_MCAP_CONTROL_CHANNEL_PROTOCOL_ID     = UUID.from_16_bits(0x001E, 'MCAPControlChannel')
363BT_MCAP_DATA_CHANNEL_PROTOCOL_ID        = UUID.from_16_bits(0x001F, 'MCAPDataChannel')
364BT_L2CAP_PROTOCOL_ID                    = UUID.from_16_bits(0x0100, 'L2CAP')
365
366# Service Classes and Profiles
367BT_SERVICE_DISCOVERY_SERVER_SERVICE_CLASS_ID_SERVICE = UUID.from_16_bits(0x1000, 'ServiceDiscoveryServerServiceClassID')
368BT_BROWSE_GROUP_DESCRIPTOR_SERVICE_CLASS_ID_SERVICE  = UUID.from_16_bits(0x1001, 'BrowseGroupDescriptorServiceClassID')
369BT_SERIAL_PORT_SERVICE                               = UUID.from_16_bits(0x1101, 'SerialPort')
370BT_LAN_ACCESS_USING_PPP_SERVICE                      = UUID.from_16_bits(0x1102, 'LANAccessUsingPPP')
371BT_DIALUP_NETWORKING_SERVICE                         = UUID.from_16_bits(0x1103, 'DialupNetworking')
372BT_IR_MCSYNC_SERVICE                                 = UUID.from_16_bits(0x1104, 'IrMCSync')
373BT_OBEX_OBJECT_PUSH_SERVICE                          = UUID.from_16_bits(0x1105, 'OBEXObjectPush')
374BT_OBEX_FILE_TRANSFER_SERVICE                        = UUID.from_16_bits(0x1106, 'OBEXFileTransfer')
375BT_IR_MCSYNC_COMMAND_SERVICE                         = UUID.from_16_bits(0x1107, 'IrMCSyncCommand')
376BT_HEADSET_SERVICE                                   = UUID.from_16_bits(0x1108, 'Headset')
377BT_CORDLESS_TELEPHONY_SERVICE                        = UUID.from_16_bits(0x1109, 'CordlessTelephony')
378BT_AUDIO_SOURCE_SERVICE                              = UUID.from_16_bits(0x110A, 'AudioSource')
379BT_AUDIO_SINK_SERVICE                                = UUID.from_16_bits(0x110B, 'AudioSink')
380BT_AV_REMOTE_CONTROL_TARGET_SERVICE                  = UUID.from_16_bits(0x110C, 'A/V_RemoteControlTarget')
381BT_ADVANCED_AUDIO_DISTRIBUTION_SERVICE               = UUID.from_16_bits(0x110D, 'AdvancedAudioDistribution')
382BT_AV_REMOTE_CONTROL_SERVICE                         = UUID.from_16_bits(0x110E, 'A/V_RemoteControl')
383BT_AV_REMOTE_CONTROL_CONTROLLER_SERVICE              = UUID.from_16_bits(0x110F, 'A/V_RemoteControlController')
384BT_INTERCOM_SERVICE                                  = UUID.from_16_bits(0x1110, 'Intercom')
385BT_FAX_SERVICE                                       = UUID.from_16_bits(0x1111, 'Fax')
386BT_HEADSET_AUDIO_GATEWAY_SERVICE                     = UUID.from_16_bits(0x1112, 'Headset - Audio Gateway')
387BT_WAP_SERVICE                                       = UUID.from_16_bits(0x1113, 'WAP')
388BT_WAP_CLIENT_SERVICE                                = UUID.from_16_bits(0x1114, 'WAP_CLIENT')
389BT_PANU_SERVICE                                      = UUID.from_16_bits(0x1115, 'PANU')
390BT_NAP_SERVICE                                       = UUID.from_16_bits(0x1116, 'NAP')
391BT_GN_SERVICE                                        = UUID.from_16_bits(0x1117, 'GN')
392BT_DIRECT_PRINTING_SERVICE                           = UUID.from_16_bits(0x1118, 'DirectPrinting')
393BT_REFERENCE_PRINTING_SERVICE                        = UUID.from_16_bits(0x1119, 'ReferencePrinting')
394BT_BASIC_IMAGING_PROFILE_SERVICE                     = UUID.from_16_bits(0x111A, 'Basic Imaging Profile')
395BT_IMAGING_RESPONDER_SERVICE                         = UUID.from_16_bits(0x111B, 'ImagingResponder')
396BT_IMAGING_AUTOMATIC_ARCHIVE_SERVICE                 = UUID.from_16_bits(0x111C, 'ImagingAutomaticArchive')
397BT_IMAGING_REFERENCED_OBJECTS_SERVICE                = UUID.from_16_bits(0x111D, 'ImagingReferencedObjects')
398BT_HANDSFREE_SERVICE                                 = UUID.from_16_bits(0x111E, 'Handsfree')
399BT_HANDSFREE_AUDIO_GATEWAY_SERVICE                   = UUID.from_16_bits(0x111F, 'HandsfreeAudioGateway')
400BT_DIRECT_PRINTING_REFERENCE_OBJECTS_SERVICE         = UUID.from_16_bits(0x1120, 'DirectPrintingReferenceObjectsService')
401BT_REFLECTED_UI_SERVICE                              = UUID.from_16_bits(0x1121, 'ReflectedUI')
402BT_BASIC_PRINTING_SERVICE                            = UUID.from_16_bits(0x1122, 'BasicPrinting')
403BT_PRINTING_STATUS_SERVICE                           = UUID.from_16_bits(0x1123, 'PrintingStatus')
404BT_HUMAN_INTERFACE_DEVICE_SERVICE                    = UUID.from_16_bits(0x1124, 'HumanInterfaceDeviceService')
405BT_HARDCOPY_CABLE_REPLACEMENT_SERVICE                = UUID.from_16_bits(0x1125, 'HardcopyCableReplacement')
406BT_HCR_PRINT_SERVICE                                 = UUID.from_16_bits(0x1126, 'HCR_Print')
407BT_HCR_SCAN_SERVICE                                  = UUID.from_16_bits(0x1127, 'HCR_Scan')
408BT_COMMON_ISDN_ACCESS_SERVICE                        = UUID.from_16_bits(0x1128, 'Common_ISDN_Access')
409BT_SIM_ACCESS_SERVICE                                = UUID.from_16_bits(0x112D, 'SIM_Access')
410BT_PHONEBOOK_ACCESS_PCE_SERVICE                      = UUID.from_16_bits(0x112E, 'Phonebook Access - PCE')
411BT_PHONEBOOK_ACCESS_PSE_SERVICE                      = UUID.from_16_bits(0x112F, 'Phonebook Access - PSE')
412BT_PHONEBOOK_ACCESS_SERVICE                          = UUID.from_16_bits(0x1130, 'Phonebook Access')
413BT_HEADSET_HS_SERVICE                                = UUID.from_16_bits(0x1131, 'Headset - HS')
414BT_MESSAGE_ACCESS_SERVER_SERVICE                     = UUID.from_16_bits(0x1132, 'Message Access Server')
415BT_MESSAGE_NOTIFICATION_SERVER_SERVICE               = UUID.from_16_bits(0x1133, 'Message Notification Server')
416BT_MESSAGE_ACCESS_PROFILE_SERVICE                    = UUID.from_16_bits(0x1134, 'Message Access Profile')
417BT_GNSS_SERVICE                                      = UUID.from_16_bits(0x1135, 'GNSS')
418BT_GNSS_SERVER_SERVICE                               = UUID.from_16_bits(0x1136, 'GNSS_Server')
419BT_3D_DISPLAY_SERVICE                                = UUID.from_16_bits(0x1137, '3D Display')
420BT_3D_GLASSES_SERVICE                                = UUID.from_16_bits(0x1138, '3D Glasses')
421BT_3D_SYNCHRONIZATION_SERVICE                        = UUID.from_16_bits(0x1139, '3D Synchronization')
422BT_MPS_PROFILE_SERVICE                               = UUID.from_16_bits(0x113A, 'MPS Profile')
423BT_MPS_SC_SERVICE                                    = UUID.from_16_bits(0x113B, 'MPS SC')
424BT_ACCESS_SERVICE_SERVICE                            = UUID.from_16_bits(0x113C, 'CTN Access Service')
425BT_CTN_NOTIFICATION_SERVICE_SERVICE                  = UUID.from_16_bits(0x113D, 'CTN Notification Service')
426BT_CTN_PROFILE_SERVICE                               = UUID.from_16_bits(0x113E, 'CTN Profile')
427BT_PNP_INFORMATION_SERVICE                           = UUID.from_16_bits(0x1200, 'PnPInformation')
428BT_GENERIC_NETWORKING_SERVICE                        = UUID.from_16_bits(0x1201, 'GenericNetworking')
429BT_GENERIC_FILE_TRANSFER_SERVICE                     = UUID.from_16_bits(0x1202, 'GenericFileTransfer')
430BT_GENERIC_AUDIO_SERVICE                             = UUID.from_16_bits(0x1203, 'GenericAudio')
431BT_GENERIC_TELEPHONY_SERVICE                         = UUID.from_16_bits(0x1204, 'GenericTelephony')
432BT_UPNP_SERVICE                                      = UUID.from_16_bits(0x1205, 'UPNP_Service')
433BT_UPNP_IP_SERVICE                                   = UUID.from_16_bits(0x1206, 'UPNP_IP_Service')
434BT_ESDP_UPNP_IP_PAN_SERVICE                          = UUID.from_16_bits(0x1300, 'ESDP_UPNP_IP_PAN')
435BT_ESDP_UPNP_IP_LAP_SERVICE                          = UUID.from_16_bits(0x1301, 'ESDP_UPNP_IP_LAP')
436BT_ESDP_UPNP_L2CAP_SERVICE                           = UUID.from_16_bits(0x1302, 'ESDP_UPNP_L2CAP')
437BT_VIDEO_SOURCE_SERVICE                              = UUID.from_16_bits(0x1303, 'VideoSource')
438BT_VIDEO_SINK_SERVICE                                = UUID.from_16_bits(0x1304, 'VideoSink')
439BT_VIDEO_DISTRIBUTION_SERVICE                        = UUID.from_16_bits(0x1305, 'VideoDistribution')
440BT_HDP_SERVICE                                       = UUID.from_16_bits(0x1400, 'HDP')
441BT_HDP_SOURCE_SERVICE                                = UUID.from_16_bits(0x1401, 'HDP Source')
442BT_HDP_SINK_SERVICE                                  = UUID.from_16_bits(0x1402, 'HDP Sink')
443
444# fmt: on
445# pylint: enable=line-too-long
446
447
448# -----------------------------------------------------------------------------
449# DeviceClass
450# -----------------------------------------------------------------------------
451class DeviceClass:
452    # fmt: off
453    # pylint: disable=line-too-long
454
455    # Major Service Classes (flags combined with OR)
456    LIMITED_DISCOVERABLE_MODE_SERVICE_CLASS = (1 << 0)
457    LE_AUDIO_SERVICE_CLASS                  = (1 << 1)
458    RESERVED                                = (1 << 2)
459    POSITIONING_SERVICE_CLASS               = (1 << 3)
460    NETWORKING_SERVICE_CLASS                = (1 << 4)
461    RENDERING_SERVICE_CLASS                 = (1 << 5)
462    CAPTURING_SERVICE_CLASS                 = (1 << 6)
463    OBJECT_TRANSFER_SERVICE_CLASS           = (1 << 7)
464    AUDIO_SERVICE_CLASS                     = (1 << 8)
465    TELEPHONY_SERVICE_CLASS                 = (1 << 9)
466    INFORMATION_SERVICE_CLASS               = (1 << 10)
467
468    SERVICE_CLASS_LABELS = [
469        'Limited Discoverable Mode',
470        'LE audio',
471        '(reserved)',
472        'Positioning',
473        'Networking',
474        'Rendering',
475        'Capturing',
476        'Object Transfer',
477        'Audio',
478        'Telephony',
479        'Information'
480    ]
481
482    # Major Device Classes
483    MISCELLANEOUS_MAJOR_DEVICE_CLASS            = 0x00
484    COMPUTER_MAJOR_DEVICE_CLASS                 = 0x01
485    PHONE_MAJOR_DEVICE_CLASS                    = 0x02
486    LAN_NETWORK_ACCESS_POINT_MAJOR_DEVICE_CLASS = 0x03
487    AUDIO_VIDEO_MAJOR_DEVICE_CLASS              = 0x04
488    PERIPHERAL_MAJOR_DEVICE_CLASS               = 0x05
489    IMAGING_MAJOR_DEVICE_CLASS                  = 0x06
490    WEARABLE_MAJOR_DEVICE_CLASS                 = 0x07
491    TOY_MAJOR_DEVICE_CLASS                      = 0x08
492    HEALTH_MAJOR_DEVICE_CLASS                   = 0x09
493    UNCATEGORIZED_MAJOR_DEVICE_CLASS            = 0x1F
494
495    MAJOR_DEVICE_CLASS_NAMES = {
496        MISCELLANEOUS_MAJOR_DEVICE_CLASS:            'Miscellaneous',
497        COMPUTER_MAJOR_DEVICE_CLASS:                 'Computer',
498        PHONE_MAJOR_DEVICE_CLASS:                    'Phone',
499        LAN_NETWORK_ACCESS_POINT_MAJOR_DEVICE_CLASS: 'LAN/Network Access Point',
500        AUDIO_VIDEO_MAJOR_DEVICE_CLASS:              'Audio/Video',
501        PERIPHERAL_MAJOR_DEVICE_CLASS:               'Peripheral',
502        IMAGING_MAJOR_DEVICE_CLASS:                  'Imaging',
503        WEARABLE_MAJOR_DEVICE_CLASS:                 'Wearable',
504        TOY_MAJOR_DEVICE_CLASS:                      'Toy',
505        HEALTH_MAJOR_DEVICE_CLASS:                   'Health',
506        UNCATEGORIZED_MAJOR_DEVICE_CLASS:            'Uncategorized'
507    }
508
509    COMPUTER_UNCATEGORIZED_MINOR_DEVICE_CLASS         = 0x00
510    COMPUTER_DESKTOP_WORKSTATION_MINOR_DEVICE_CLASS   = 0x01
511    COMPUTER_SERVER_CLASS_COMPUTER_MINOR_DEVICE_CLASS = 0x02
512    COMPUTER_LAPTOP_COMPUTER_MINOR_DEVICE_CLASS       = 0x03
513    COMPUTER_HANDHELD_PC_PDA_MINOR_DEVICE_CLASS       = 0x04
514    COMPUTER_PALM_SIZE_PC_PDA_MINOR_DEVICE_CLASS      = 0x05
515    COMPUTER_WEARABLE_COMPUTER_MINOR_DEVICE_CLASS     = 0x06
516    COMPUTER_TABLET_MINOR_DEVICE_CLASS                = 0x07
517
518    COMPUTER_MINOR_DEVICE_CLASS_NAMES = {
519        COMPUTER_UNCATEGORIZED_MINOR_DEVICE_CLASS:         'Uncategorized',
520        COMPUTER_DESKTOP_WORKSTATION_MINOR_DEVICE_CLASS:   'Desktop workstation',
521        COMPUTER_SERVER_CLASS_COMPUTER_MINOR_DEVICE_CLASS: 'Server-class computer',
522        COMPUTER_LAPTOP_COMPUTER_MINOR_DEVICE_CLASS:       'Laptop',
523        COMPUTER_HANDHELD_PC_PDA_MINOR_DEVICE_CLASS:       'Handheld PC/PDA',
524        COMPUTER_PALM_SIZE_PC_PDA_MINOR_DEVICE_CLASS:      'Palm-size PC/PDA',
525        COMPUTER_WEARABLE_COMPUTER_MINOR_DEVICE_CLASS:     'Wearable computer',
526        COMPUTER_TABLET_MINOR_DEVICE_CLASS:                'Tablet'
527    }
528
529    PHONE_UNCATEGORIZED_MINOR_DEVICE_CLASS                = 0x00
530    PHONE_CELLULAR_MINOR_DEVICE_CLASS                     = 0x01
531    PHONE_CORDLESS_MINOR_DEVICE_CLASS                     = 0x02
532    PHONE_SMARTPHONE_MINOR_DEVICE_CLASS                   = 0x03
533    PHONE_WIRED_MODEM_OR_VOICE_GATEWAY_MINOR_DEVICE_CLASS = 0x04
534    PHONE_COMMON_ISDN_MINOR_DEVICE_CLASS                  = 0x05
535
536    PHONE_MINOR_DEVICE_CLASS_NAMES = {
537        PHONE_UNCATEGORIZED_MINOR_DEVICE_CLASS:                'Uncategorized',
538        PHONE_CELLULAR_MINOR_DEVICE_CLASS:                     'Cellular',
539        PHONE_CORDLESS_MINOR_DEVICE_CLASS:                     'Cordless',
540        PHONE_SMARTPHONE_MINOR_DEVICE_CLASS:                   'Smartphone',
541        PHONE_WIRED_MODEM_OR_VOICE_GATEWAY_MINOR_DEVICE_CLASS: 'Wired modem or voice gateway',
542        PHONE_COMMON_ISDN_MINOR_DEVICE_CLASS:                  'Common ISDN access'
543    }
544
545    AUDIO_VIDEO_UNCATEGORIZED_MINOR_DEVICE_CLASS                 = 0x00
546    AUDIO_VIDEO_WEARABLE_HEADSET_DEVICE_MINOR_DEVICE_CLASS       = 0x01
547    AUDIO_VIDEO_HANDS_FREE_DEVICE_MINOR_DEVICE_CLASS             = 0x02
548    # (RESERVED)                                                 = 0x03
549    AUDIO_VIDEO_MICROPHONE_MINOR_DEVICE_CLASS                    = 0x04
550    AUDIO_VIDEO_LOUDSPEAKER_MINOR_DEVICE_CLASS                   = 0x05
551    AUDIO_VIDEO_HEADPHONES_MINOR_DEVICE_CLASS                    = 0x06
552    AUDIO_VIDEO_PORTABLE_AUDIO_MINOR_DEVICE_CLASS                = 0x07
553    AUDIO_VIDEO_CAR_AUDIO_MINOR_DEVICE_CLASS                     = 0x08
554    AUDIO_VIDEO_SET_TOP_BOX_MINOR_DEVICE_CLASS                   = 0x09
555    AUDIO_VIDEO_HIFI_AUDIO_DEVICE_MINOR_DEVICE_CLASS             = 0x0A
556    AUDIO_VIDEO_VCR_MINOR_DEVICE_CLASS                           = 0x0B
557    AUDIO_VIDEO_VIDEO_CAMERA_MINOR_DEVICE_CLASS                  = 0x0C
558    AUDIO_VIDEO_CAMCORDER_MINOR_DEVICE_CLASS                     = 0x0D
559    AUDIO_VIDEO_VIDEO_MONITOR_MINOR_DEVICE_CLASS                 = 0x0E
560    AUDIO_VIDEO_VIDEO_DISPLAY_AND_LOUDSPEAKER_MINOR_DEVICE_CLASS = 0x0F
561    AUDIO_VIDEO_VIDEO_CONFERENCING_MINOR_DEVICE_CLASS            = 0x10
562    # (RESERVED)                                                 = 0x11
563    AUDIO_VIDEO_GAMING_OR_TOY_MINOR_DEVICE_CLASS                 = 0x12
564
565    AUDIO_VIDEO_MINOR_DEVICE_CLASS_NAMES = {
566        AUDIO_VIDEO_UNCATEGORIZED_MINOR_DEVICE_CLASS:                 'Uncategorized',
567        AUDIO_VIDEO_WEARABLE_HEADSET_DEVICE_MINOR_DEVICE_CLASS:       'Wearable Headset Device',
568        AUDIO_VIDEO_HANDS_FREE_DEVICE_MINOR_DEVICE_CLASS:             'Hands-free Device',
569        AUDIO_VIDEO_MICROPHONE_MINOR_DEVICE_CLASS:                    'Microphone',
570        AUDIO_VIDEO_LOUDSPEAKER_MINOR_DEVICE_CLASS:                   'Loudspeaker',
571        AUDIO_VIDEO_HEADPHONES_MINOR_DEVICE_CLASS:                    'Headphones',
572        AUDIO_VIDEO_PORTABLE_AUDIO_MINOR_DEVICE_CLASS:                'Portable Audio',
573        AUDIO_VIDEO_CAR_AUDIO_MINOR_DEVICE_CLASS:                     'Car audio',
574        AUDIO_VIDEO_SET_TOP_BOX_MINOR_DEVICE_CLASS:                   'Set-top box',
575        AUDIO_VIDEO_HIFI_AUDIO_DEVICE_MINOR_DEVICE_CLASS:             'HiFi Audio Device',
576        AUDIO_VIDEO_VCR_MINOR_DEVICE_CLASS:                           'VCR',
577        AUDIO_VIDEO_VIDEO_CAMERA_MINOR_DEVICE_CLASS:                  'Video Camera',
578        AUDIO_VIDEO_CAMCORDER_MINOR_DEVICE_CLASS:                     'Camcorder',
579        AUDIO_VIDEO_VIDEO_MONITOR_MINOR_DEVICE_CLASS:                 'Video Monitor',
580        AUDIO_VIDEO_VIDEO_DISPLAY_AND_LOUDSPEAKER_MINOR_DEVICE_CLASS: 'Video Display and Loudspeaker',
581        AUDIO_VIDEO_VIDEO_CONFERENCING_MINOR_DEVICE_CLASS:            'Video Conferencing',
582        AUDIO_VIDEO_GAMING_OR_TOY_MINOR_DEVICE_CLASS:                 'Gaming/Toy'
583    }
584
585    PERIPHERAL_UNCATEGORIZED_MINOR_DEVICE_CLASS                  = 0x00
586    PERIPHERAL_KEYBOARD_MINOR_DEVICE_CLASS                       = 0x10
587    PERIPHERAL_POINTING_DEVICE_MINOR_DEVICE_CLASS                = 0x20
588    PERIPHERAL_COMBO_KEYBOARD_POINTING_DEVICE_MINOR_DEVICE_CLASS = 0x30
589    PERIPHERAL_JOYSTICK_MINOR_DEVICE_CLASS                       = 0x01
590    PERIPHERAL_GAMEPAD_MINOR_DEVICE_CLASS                        = 0x02
591    PERIPHERAL_REMOTE_CONTROL_MINOR_DEVICE_CLASS                 = 0x03
592    PERIPHERAL_SENSING_DEVICE_MINOR_DEVICE_CLASS                 = 0x04
593    PERIPHERAL_DIGITIZER_TABLET_MINOR_DEVICE_CLASS               = 0x05
594    PERIPHERAL_CARD_READER_MINOR_DEVICE_CLASS                    = 0x06
595    PERIPHERAL_DIGITAL_PEN_MINOR_DEVICE_CLASS                    = 0x07
596    PERIPHERAL_HANDHELD_SCANNER_MINOR_DEVICE_CLASS               = 0x08
597    PERIPHERAL_HANDHELD_GESTURAL_INPUT_DEVICE_MINOR_DEVICE_CLASS = 0x09
598
599    PERIPHERAL_MINOR_DEVICE_CLASS_NAMES = {
600        PERIPHERAL_UNCATEGORIZED_MINOR_DEVICE_CLASS:                  'Uncategorized',
601        PERIPHERAL_KEYBOARD_MINOR_DEVICE_CLASS:                       'Keyboard',
602        PERIPHERAL_POINTING_DEVICE_MINOR_DEVICE_CLASS:                'Pointing device',
603        PERIPHERAL_COMBO_KEYBOARD_POINTING_DEVICE_MINOR_DEVICE_CLASS: 'Combo keyboard/pointing device',
604        PERIPHERAL_JOYSTICK_MINOR_DEVICE_CLASS:                       'Joystick',
605        PERIPHERAL_GAMEPAD_MINOR_DEVICE_CLASS:                        'Gamepad',
606        PERIPHERAL_REMOTE_CONTROL_MINOR_DEVICE_CLASS:                 'Remote control',
607        PERIPHERAL_SENSING_DEVICE_MINOR_DEVICE_CLASS:                 'Sensing device',
608        PERIPHERAL_DIGITIZER_TABLET_MINOR_DEVICE_CLASS:               'Digitizer tablet',
609        PERIPHERAL_CARD_READER_MINOR_DEVICE_CLASS:                    'Card Reader',
610        PERIPHERAL_DIGITAL_PEN_MINOR_DEVICE_CLASS:                    'Digital Pen',
611        PERIPHERAL_HANDHELD_SCANNER_MINOR_DEVICE_CLASS:               'Handheld scanner',
612        PERIPHERAL_HANDHELD_GESTURAL_INPUT_DEVICE_MINOR_DEVICE_CLASS: 'Handheld gestural input device'
613    }
614
615    WEARABLE_UNCATEGORIZED_MINOR_DEVICE_CLASS = 0x00
616    WEARABLE_WRISTWATCH_MINOR_DEVICE_CLASS    = 0x01
617    WEARABLE_PAGER_MINOR_DEVICE_CLASS         = 0x02
618    WEARABLE_JACKET_MINOR_DEVICE_CLASS        = 0x03
619    WEARABLE_HELMET_MINOR_DEVICE_CLASS        = 0x04
620    WEARABLE_GLASSES_MINOR_DEVICE_CLASS       = 0x05
621
622    WEARABLE_MINOR_DEVICE_CLASS_NAMES = {
623        WEARABLE_UNCATEGORIZED_MINOR_DEVICE_CLASS: 'Uncategorized',
624        WEARABLE_WRISTWATCH_MINOR_DEVICE_CLASS:    'Wristwatch',
625        WEARABLE_PAGER_MINOR_DEVICE_CLASS:         'Pager',
626        WEARABLE_JACKET_MINOR_DEVICE_CLASS:        'Jacket',
627        WEARABLE_HELMET_MINOR_DEVICE_CLASS:        'Helmet',
628        WEARABLE_GLASSES_MINOR_DEVICE_CLASS:       'Glasses',
629    }
630
631    TOY_UNCATEGORIZED_MINOR_DEVICE_CLASS      = 0x00
632    TOY_ROBOT_MINOR_DEVICE_CLASS              = 0x01
633    TOY_VEHICLE_MINOR_DEVICE_CLASS            = 0x02
634    TOY_DOLL_ACTION_FIGURE_MINOR_DEVICE_CLASS = 0x03
635    TOY_CONTROLLER_MINOR_DEVICE_CLASS         = 0x04
636    TOY_GAME_MINOR_DEVICE_CLASS               = 0x05
637
638    TOY_MINOR_DEVICE_CLASS_NAMES = {
639        TOY_UNCATEGORIZED_MINOR_DEVICE_CLASS:      'Uncategorized',
640        TOY_ROBOT_MINOR_DEVICE_CLASS:              'Robot',
641        TOY_VEHICLE_MINOR_DEVICE_CLASS:            'Vehicle',
642        TOY_DOLL_ACTION_FIGURE_MINOR_DEVICE_CLASS: 'Doll/Action figure',
643        TOY_CONTROLLER_MINOR_DEVICE_CLASS:         'Controller',
644        TOY_GAME_MINOR_DEVICE_CLASS:               'Game',
645    }
646
647    HEALTH_UNDEFINED_MINOR_DEVICE_CLASS                 = 0x00
648    HEALTH_BLOOD_PRESSURE_MONITOR_MINOR_DEVICE_CLASS    = 0x01
649    HEALTH_THERMOMETER_MINOR_DEVICE_CLASS               = 0x02
650    HEALTH_WEIGHING_SCALE_MINOR_DEVICE_CLASS            = 0x03
651    HEALTH_GLUCOSE_METER_MINOR_DEVICE_CLASS             = 0x04
652    HEALTH_PULSE_OXIMETER_MINOR_DEVICE_CLASS            = 0x05
653    HEALTH_HEART_PULSE_RATE_MONITOR_MINOR_DEVICE_CLASS  = 0x06
654    HEALTH_HEALTH_DATA_DISPLAY_MINOR_DEVICE_CLASS       = 0x07
655    HEALTH_STEP_COUNTER_MINOR_DEVICE_CLASS              = 0x08
656    HEALTH_BODY_COMPOSITION_ANALYZER_MINOR_DEVICE_CLASS = 0x09
657    HEALTH_PEAK_FLOW_MONITOR_MINOR_DEVICE_CLASS         = 0x0A
658    HEALTH_MEDICATION_MONITOR_MINOR_DEVICE_CLASS        = 0x0B
659    HEALTH_KNEE_PROSTHESIS_MINOR_DEVICE_CLASS           = 0x0C
660    HEALTH_ANKLE_PROSTHESIS_MINOR_DEVICE_CLASS          = 0x0D
661    HEALTH_GENERIC_HEALTH_MANAGER_MINOR_DEVICE_CLASS    = 0x0E
662    HEALTH_PERSONAL_MOBILITY_DEVICE_MINOR_DEVICE_CLASS  = 0x0F
663
664    HEALTH_MINOR_DEVICE_CLASS_NAMES = {
665        HEALTH_UNDEFINED_MINOR_DEVICE_CLASS:                 'Undefined',
666        HEALTH_BLOOD_PRESSURE_MONITOR_MINOR_DEVICE_CLASS:    'Blood Pressure Monitor',
667        HEALTH_THERMOMETER_MINOR_DEVICE_CLASS:               'Thermometer',
668        HEALTH_WEIGHING_SCALE_MINOR_DEVICE_CLASS:            'Weighing Scale',
669        HEALTH_GLUCOSE_METER_MINOR_DEVICE_CLASS:             'Glucose Meter',
670        HEALTH_PULSE_OXIMETER_MINOR_DEVICE_CLASS:            'Pulse Oximeter',
671        HEALTH_HEART_PULSE_RATE_MONITOR_MINOR_DEVICE_CLASS:  'Heart/Pulse Rate Monitor',
672        HEALTH_HEALTH_DATA_DISPLAY_MINOR_DEVICE_CLASS:       'Health Data Display',
673        HEALTH_STEP_COUNTER_MINOR_DEVICE_CLASS:              'Step Counter',
674        HEALTH_BODY_COMPOSITION_ANALYZER_MINOR_DEVICE_CLASS: 'Body Composition Analyzer',
675        HEALTH_PEAK_FLOW_MONITOR_MINOR_DEVICE_CLASS:         'Peak Flow Monitor',
676        HEALTH_MEDICATION_MONITOR_MINOR_DEVICE_CLASS:        'Medication Monitor',
677        HEALTH_KNEE_PROSTHESIS_MINOR_DEVICE_CLASS:           'Knee Prosthesis',
678        HEALTH_ANKLE_PROSTHESIS_MINOR_DEVICE_CLASS:          'Ankle Prosthesis',
679        HEALTH_GENERIC_HEALTH_MANAGER_MINOR_DEVICE_CLASS:    'Generic Health Manager',
680        HEALTH_PERSONAL_MOBILITY_DEVICE_MINOR_DEVICE_CLASS:  'Personal Mobility Device',
681    }
682
683    MINOR_DEVICE_CLASS_NAMES = {
684        COMPUTER_MAJOR_DEVICE_CLASS:    COMPUTER_MINOR_DEVICE_CLASS_NAMES,
685        PHONE_MAJOR_DEVICE_CLASS:       PHONE_MINOR_DEVICE_CLASS_NAMES,
686        AUDIO_VIDEO_MAJOR_DEVICE_CLASS: AUDIO_VIDEO_MINOR_DEVICE_CLASS_NAMES,
687        PERIPHERAL_MAJOR_DEVICE_CLASS:  PERIPHERAL_MINOR_DEVICE_CLASS_NAMES,
688        WEARABLE_MAJOR_DEVICE_CLASS:    WEARABLE_MINOR_DEVICE_CLASS_NAMES,
689        TOY_MAJOR_DEVICE_CLASS:         TOY_MINOR_DEVICE_CLASS_NAMES,
690        HEALTH_MAJOR_DEVICE_CLASS:      HEALTH_MINOR_DEVICE_CLASS_NAMES,
691    }
692
693    # fmt: on
694    # pylint: enable=line-too-long
695
696    @staticmethod
697    def split_class_of_device(class_of_device):
698        # Split the bit fields of the composite class of device value into:
699        # (service_classes, major_device_class, minor_device_class)
700        return (
701            (class_of_device >> 13 & 0x7FF),
702            (class_of_device >> 8 & 0x1F),
703            (class_of_device >> 2 & 0x3F),
704        )
705
706    @staticmethod
707    def pack_class_of_device(service_classes, major_device_class, minor_device_class):
708        return service_classes << 13 | major_device_class << 8 | minor_device_class << 2
709
710    @staticmethod
711    def service_class_labels(service_class_flags):
712        return bit_flags_to_strings(
713            service_class_flags, DeviceClass.SERVICE_CLASS_LABELS
714        )
715
716    @staticmethod
717    def major_device_class_name(device_class):
718        return name_or_number(DeviceClass.MAJOR_DEVICE_CLASS_NAMES, device_class)
719
720    @staticmethod
721    def minor_device_class_name(major_device_class, minor_device_class):
722        class_names = DeviceClass.MINOR_DEVICE_CLASS_NAMES.get(major_device_class)
723        if class_names is None:
724            return f'#{minor_device_class:02X}'
725        return name_or_number(class_names, minor_device_class)
726
727
728# -----------------------------------------------------------------------------
729# Appearance
730# -----------------------------------------------------------------------------
731class Appearance:
732    class Category(OpenIntEnum):
733        UNKNOWN = 0x0000
734        PHONE = 0x0001
735        COMPUTER = 0x0002
736        WATCH = 0x0003
737        CLOCK = 0x0004
738        DISPLAY = 0x0005
739        REMOTE_CONTROL = 0x0006
740        EYE_GLASSES = 0x0007
741        TAG = 0x0008
742        KEYRING = 0x0009
743        MEDIA_PLAYER = 0x000A
744        BARCODE_SCANNER = 0x000B
745        THERMOMETER = 0x000C
746        HEART_RATE_SENSOR = 0x000D
747        BLOOD_PRESSURE = 0x000E
748        HUMAN_INTERFACE_DEVICE = 0x000F
749        GLUCOSE_METER = 0x0010
750        RUNNING_WALKING_SENSOR = 0x0011
751        CYCLING = 0x0012
752        CONTROL_DEVICE = 0x0013
753        NETWORK_DEVICE = 0x0014
754        SENSOR = 0x0015
755        LIGHT_FIXTURES = 0x0016
756        FAN = 0x0017
757        HVAC = 0x0018
758        AIR_CONDITIONING = 0x0019
759        HUMIDIFIER = 0x001A
760        HEATING = 0x001B
761        ACCESS_CONTROL = 0x001C
762        MOTORIZED_DEVICE = 0x001D
763        POWER_DEVICE = 0x001E
764        LIGHT_SOURCE = 0x001F
765        WINDOW_COVERING = 0x0020
766        AUDIO_SINK = 0x0021
767        AUDIO_SOURCE = 0x0022
768        MOTORIZED_VEHICLE = 0x0023
769        DOMESTIC_APPLIANCE = 0x0024
770        WEARABLE_AUDIO_DEVICE = 0x0025
771        AIRCRAFT = 0x0026
772        AV_EQUIPMENT = 0x0027
773        DISPLAY_EQUIPMENT = 0x0028
774        HEARING_AID = 0x0029
775        GAMING = 0x002A
776        SIGNAGE = 0x002B
777        PULSE_OXIMETER = 0x0031
778        WEIGHT_SCALE = 0x0032
779        PERSONAL_MOBILITY_DEVICE = 0x0033
780        CONTINUOUS_GLUCOSE_MONITOR = 0x0034
781        INSULIN_PUMP = 0x0035
782        MEDICATION_DELIVERY = 0x0036
783        SPIROMETER = 0x0037
784        OUTDOOR_SPORTS_ACTIVITY = 0x0051
785
786    class UnknownSubcategory(OpenIntEnum):
787        GENERIC_UNKNOWN = 0x00
788
789    class PhoneSubcategory(OpenIntEnum):
790        GENERIC_PHONE = 0x00
791
792    class ComputerSubcategory(OpenIntEnum):
793        GENERIC_COMPUTER = 0x00
794        DESKTOP_WORKSTATION = 0x01
795        SERVER_CLASS_COMPUTER = 0x02
796        LAPTOP = 0x03
797        HANDHELD_PC_PDA = 0x04
798        PALM_SIZE_PC_PDA = 0x05
799        WEARABLE_COMPUTER = 0x06
800        TABLET = 0x07
801        DOCKING_STATION = 0x08
802        ALL_IN_ONE = 0x09
803        BLADE_SERVER = 0x0A
804        CONVERTIBLE = 0x0B
805        DETACHABLE = 0x0C
806        IOT_GATEWAY = 0x0D
807        MINI_PC = 0x0E
808        STICK_PC = 0x0F
809
810    class WatchSubcategory(OpenIntEnum):
811        GENENERIC_WATCH = 0x00
812        SPORTS_WATCH = 0x01
813        SMARTWATCH = 0x02
814
815    class ClockSubcategory(OpenIntEnum):
816        GENERIC_CLOCK = 0x00
817
818    class DisplaySubcategory(OpenIntEnum):
819        GENERIC_DISPLAY = 0x00
820
821    class RemoteControlSubcategory(OpenIntEnum):
822        GENERIC_REMOTE_CONTROL = 0x00
823
824    class EyeglassesSubcategory(OpenIntEnum):
825        GENERIC_EYEGLASSES = 0x00
826
827    class TagSubcategory(OpenIntEnum):
828        GENERIC_TAG = 0x00
829
830    class KeyringSubcategory(OpenIntEnum):
831        GENERIC_KEYRING = 0x00
832
833    class MediaPlayerSubcategory(OpenIntEnum):
834        GENERIC_MEDIA_PLAYER = 0x00
835
836    class BarcodeScannerSubcategory(OpenIntEnum):
837        GENERIC_BARCODE_SCANNER = 0x00
838
839    class ThermometerSubcategory(OpenIntEnum):
840        GENERIC_THERMOMETER = 0x00
841        EAR_THERMOMETER = 0x01
842
843    class HeartRateSensorSubcategory(OpenIntEnum):
844        GENERIC_HEART_RATE_SENSOR = 0x00
845        HEART_RATE_BELT = 0x01
846
847    class BloodPressureSubcategory(OpenIntEnum):
848        GENERIC_BLOOD_PRESSURE = 0x00
849        ARM_BLOOD_PRESSURE = 0x01
850        WRIST_BLOOD_PRESSURE = 0x02
851
852    class HumanInterfaceDeviceSubcategory(OpenIntEnum):
853        GENERIC_HUMAN_INTERFACE_DEVICE = 0x00
854        KEYBOARD = 0x01
855        MOUSE = 0x02
856        JOYSTICK = 0x03
857        GAMEPAD = 0x04
858        DIGITIZER_TABLET = 0x05
859        CARD_READER = 0x06
860        DIGITAL_PEN = 0x07
861        BARCODE_SCANNER = 0x08
862        TOUCHPAD = 0x09
863        PRESENTATION_REMOTE = 0x0A
864
865    class GlucoseMeterSubcategory(OpenIntEnum):
866        GENERIC_GLUCOSE_METER = 0x00
867
868    class RunningWalkingSensorSubcategory(OpenIntEnum):
869        GENERIC_RUNNING_WALKING_SENSOR = 0x00
870        IN_SHOE_RUNNING_WALKING_SENSOR = 0x01
871        ON_SHOW_RUNNING_WALKING_SENSOR = 0x02
872        ON_HIP_RUNNING_WALKING_SENSOR = 0x03
873
874    class CyclingSubcategory(OpenIntEnum):
875        GENERIC_CYCLING = 0x00
876        CYCLING_COMPUTER = 0x01
877        SPEED_SENSOR = 0x02
878        CADENCE_SENSOR = 0x03
879        POWER_SENSOR = 0x04
880        SPEED_AND_CADENCE_SENSOR = 0x05
881
882    class ControlDeviceSubcategory(OpenIntEnum):
883        GENERIC_CONTROL_DEVICE = 0x00
884        SWITCH = 0x01
885        MULTI_SWITCH = 0x02
886        BUTTON = 0x03
887        SLIDER = 0x04
888        ROTARY_SWITCH = 0x05
889        TOUCH_PANEL = 0x06
890        SINGLE_SWITCH = 0x07
891        DOUBLE_SWITCH = 0x08
892        TRIPLE_SWITCH = 0x09
893        BATTERY_SWITCH = 0x0A
894        ENERGY_HARVESTING_SWITCH = 0x0B
895        PUSH_BUTTON = 0x0C
896
897    class NetworkDeviceSubcategory(OpenIntEnum):
898        GENERIC_NETWORK_DEVICE = 0x00
899        ACCESS_POINT = 0x01
900        MESH_DEVICE = 0x02
901        MESH_NETWORK_PROXY = 0x03
902
903    class SensorSubcategory(OpenIntEnum):
904        GENERIC_SENSOR = 0x00
905        MOTION_SENSOR = 0x01
906        AIR_QUALITY_SENSOR = 0x02
907        TEMPERATURE_SENSOR = 0x03
908        HUMIDITY_SENSOR = 0x04
909        LEAK_SENSOR = 0x05
910        SMOKE_SENSOR = 0x06
911        OCCUPANCY_SENSOR = 0x07
912        CONTACT_SENSOR = 0x08
913        CARBON_MONOXIDE_SENSOR = 0x09
914        CARBON_DIOXIDE_SENSOR = 0x0A
915        AMBIENT_LIGHT_SENSOR = 0x0B
916        ENERGY_SENSOR = 0x0C
917        COLOR_LIGHT_SENSOR = 0x0D
918        RAIN_SENSOR = 0x0E
919        FIRE_SENSOR = 0x0F
920        WIND_SENSOR = 0x10
921        PROXIMITY_SENSOR = 0x11
922        MULTI_SENSOR = 0x12
923        FLUSH_MOUNTED_SENSOR = 0x13
924        CEILING_MOUNTED_SENSOR = 0x14
925        WALL_MOUNTED_SENSOR = 0x15
926        MULTISENSOR = 0x16
927        ENERGY_METER = 0x17
928        FLAME_DETECTOR = 0x18
929        VEHICLE_TIRE_PRESSURE_SENSOR = 0x19
930
931    class LightFixturesSubcategory(OpenIntEnum):
932        GENERIC_LIGHT_FIXTURES = 0x00
933        WALL_LIGHT = 0x01
934        CEILING_LIGHT = 0x02
935        FLOOR_LIGHT = 0x03
936        CABINET_LIGHT = 0x04
937        DESK_LIGHT = 0x05
938        TROFFER_LIGHT = 0x06
939        PENDANT_LIGHT = 0x07
940        IN_GROUND_LIGHT = 0x08
941        FLOOD_LIGHT = 0x09
942        UNDERWATER_LIGHT = 0x0A
943        BOLLARD_WITH_LIGHT = 0x0B
944        PATHWAY_LIGHT = 0x0C
945        GARDEN_LIGHT = 0x0D
946        POLE_TOP_LIGHT = 0x0E
947        SPOTLIGHT = 0x0F
948        LINEAR_LIGHT = 0x10
949        STREET_LIGHT = 0x11
950        SHELVES_LIGHT = 0x12
951        BAY_LIGHT = 0x013
952        EMERGENCY_EXIT_LIGHT = 0x14
953        LIGHT_CONTROLLER = 0x15
954        LIGHT_DRIVER = 0x16
955        BULB = 0x17
956        LOW_BAY_LIGHT = 0x18
957        HIGH_BAY_LIGHT = 0x19
958
959    class FanSubcategory(OpenIntEnum):
960        GENERIC_FAN = 0x00
961        CEILING_FAN = 0x01
962        AXIAL_FAN = 0x02
963        EXHAUST_FAN = 0x03
964        PEDESTAL_FAN = 0x04
965        DESK_FAN = 0x05
966        WALL_FAN = 0x06
967
968    class HvacSubcategory(OpenIntEnum):
969        GENERIC_HVAC = 0x00
970        THERMOSTAT = 0x01
971        HUMIDIFIER = 0x02
972        DEHUMIDIFIER = 0x03
973        HEATER = 0x04
974        RADIATOR = 0x05
975        BOILER = 0x06
976        HEAT_PUMP = 0x07
977        INFRARED_HEATER = 0x08
978        RADIANT_PANEL_HEATER = 0x09
979        FAN_HEATER = 0x0A
980        AIR_CURTAIN = 0x0B
981
982    class AirConditioningSubcategory(OpenIntEnum):
983        GENERIC_AIR_CONDITIONING = 0x00
984
985    class HumidifierSubcategory(OpenIntEnum):
986        GENERIC_HUMIDIFIER = 0x00
987
988    class HeatingSubcategory(OpenIntEnum):
989        GENERIC_HEATING = 0x00
990        RADIATOR = 0x01
991        BOILER = 0x02
992        HEAT_PUMP = 0x03
993        INFRARED_HEATER = 0x04
994        RADIANT_PANEL_HEATER = 0x05
995        FAN_HEATER = 0x06
996        AIR_CURTAIN = 0x07
997
998    class AccessControlSubcategory(OpenIntEnum):
999        GENERIC_ACCESS_CONTROL = 0x00
1000        ACCESS_DOOR = 0x01
1001        GARAGE_DOOR = 0x02
1002        EMERGENCY_EXIT_DOOR = 0x03
1003        ACCESS_LOCK = 0x04
1004        ELEVATOR = 0x05
1005        WINDOW = 0x06
1006        ENTRANCE_GATE = 0x07
1007        DOOR_LOCK = 0x08
1008        LOCKER = 0x09
1009
1010    class MotorizedDeviceSubcategory(OpenIntEnum):
1011        GENERIC_MOTORIZED_DEVICE = 0x00
1012        MOTORIZED_GATE = 0x01
1013        AWNING = 0x02
1014        BLINDS_OR_SHADES = 0x03
1015        CURTAINS = 0x04
1016        SCREEN = 0x05
1017
1018    class PowerDeviceSubcategory(OpenIntEnum):
1019        GENERIC_POWER_DEVICE = 0x00
1020        POWER_OUTLET = 0x01
1021        POWER_STRIP = 0x02
1022        PLUG = 0x03
1023        POWER_SUPPLY = 0x04
1024        LED_DRIVER = 0x05
1025        FLUORESCENT_LAMP_GEAR = 0x06
1026        HID_LAMP_GEAR = 0x07
1027        CHARGE_CASE = 0x08
1028        POWER_BANK = 0x09
1029
1030    class LightSourceSubcategory(OpenIntEnum):
1031        GENERIC_LIGHT_SOURCE = 0x00
1032        INCANDESCENT_LIGHT_BULB = 0x01
1033        LED_LAMP = 0x02
1034        HID_LAMP = 0x03
1035        FLUORESCENT_LAMP = 0x04
1036        LED_ARRAY = 0x05
1037        MULTI_COLOR_LED_ARRAY = 0x06
1038        LOW_VOLTAGE_HALOGEN = 0x07
1039        ORGANIC_LIGHT_EMITTING_DIODE = 0x08
1040
1041    class WindowCoveringSubcategory(OpenIntEnum):
1042        GENERIC_WINDOW_COVERING = 0x00
1043        WINDOW_SHADES = 0x01
1044        WINDOW_BLINDS = 0x02
1045        WINDOW_AWNING = 0x03
1046        WINDOW_CURTAIN = 0x04
1047        EXTERIOR_SHUTTER = 0x05
1048        EXTERIOR_SCREEN = 0x06
1049
1050    class AudioSinkSubcategory(OpenIntEnum):
1051        GENERIC_AUDIO_SINK = 0x00
1052        STANDALONE_SPEAKER = 0x01
1053        SOUNDBAR = 0x02
1054        BOOKSHELF_SPEAKER = 0x03
1055        STANDMOUNTED_SPEAKER = 0x04
1056        SPEAKERPHONE = 0x05
1057
1058    class AudioSourceSubcategory(OpenIntEnum):
1059        GENERIC_AUDIO_SOURCE = 0x00
1060        MICROPHONE = 0x01
1061        ALARM = 0x02
1062        BELL = 0x03
1063        HORN = 0x04
1064        BROADCASTING_DEVICE = 0x05
1065        SERVICE_DESK = 0x06
1066        KIOSK = 0x07
1067        BROADCASTING_ROOM = 0x08
1068        AUDITORIUM = 0x09
1069
1070    class MotorizedVehicleSubcategory(OpenIntEnum):
1071        GENERIC_MOTORIZED_VEHICLE = 0x00
1072        CAR = 0x01
1073        LARGE_GOODS_VEHICLE = 0x02
1074        TWO_WHEELED_VEHICLE = 0x03
1075        MOTORBIKE = 0x04
1076        SCOOTER = 0x05
1077        MOPED = 0x06
1078        THREE_WHEELED_VEHICLE = 0x07
1079        LIGHT_VEHICLE = 0x08
1080        QUAD_BIKE = 0x09
1081        MINIBUS = 0x0A
1082        BUS = 0x0B
1083        TROLLEY = 0x0C
1084        AGRICULTURAL_VEHICLE = 0x0D
1085        CAMPER_CARAVAN = 0x0E
1086        RECREATIONAL_VEHICLE_MOTOR_HOME = 0x0F
1087
1088    class DomesticApplianceSubcategory(OpenIntEnum):
1089        GENERIC_DOMESTIC_APPLIANCE = 0x00
1090        REFRIGERATOR = 0x01
1091        FREEZER = 0x02
1092        OVEN = 0x03
1093        MICROWAVE = 0x04
1094        TOASTER = 0x05
1095        WASHING_MACHINE = 0x06
1096        DRYER = 0x07
1097        COFFEE_MAKER = 0x08
1098        CLOTHES_IRON = 0x09
1099        CURLING_IRON = 0x0A
1100        HAIR_DRYER = 0x0B
1101        VACUUM_CLEANER = 0x0C
1102        ROBOTIC_VACUUM_CLEANER = 0x0D
1103        RICE_COOKER = 0x0E
1104        CLOTHES_STEAMER = 0x0F
1105
1106    class WearableAudioDeviceSubcategory(OpenIntEnum):
1107        GENERIC_WEARABLE_AUDIO_DEVICE = 0x00
1108        EARBUD = 0x01
1109        HEADSET = 0x02
1110        HEADPHONES = 0x03
1111        NECK_BAND = 0x04
1112
1113    class AircraftSubcategory(OpenIntEnum):
1114        GENERIC_AIRCRAFT = 0x00
1115        LIGHT_AIRCRAFT = 0x01
1116        MICROLIGHT = 0x02
1117        PARAGLIDER = 0x03
1118        LARGE_PASSENGER_AIRCRAFT = 0x04
1119
1120    class AvEquipmentSubcategory(OpenIntEnum):
1121        GENERIC_AV_EQUIPMENT = 0x00
1122        AMPLIFIER = 0x01
1123        RECEIVER = 0x02
1124        RADIO = 0x03
1125        TUNER = 0x04
1126        TURNTABLE = 0x05
1127        CD_PLAYER = 0x06
1128        DVD_PLAYER = 0x07
1129        BLUERAY_PLAYER = 0x08
1130        OPTICAL_DISC_PLAYER = 0x09
1131        SET_TOP_BOX = 0x0A
1132
1133    class DisplayEquipmentSubcategory(OpenIntEnum):
1134        GENERIC_DISPLAY_EQUIPMENT = 0x00
1135        TELEVISION = 0x01
1136        MONITOR = 0x02
1137        PROJECTOR = 0x03
1138
1139    class HearingAidSubcategory(OpenIntEnum):
1140        GENERIC_HEARING_AID = 0x00
1141        IN_EAR_HEARING_AID = 0x01
1142        BEHIND_EAR_HEARING_AID = 0x02
1143        COCHLEAR_IMPLANT = 0x03
1144
1145    class GamingSubcategory(OpenIntEnum):
1146        GENERIC_GAMING = 0x00
1147        HOME_VIDEO_GAME_CONSOLE = 0x01
1148        PORTABLE_HANDHELD_CONSOLE = 0x02
1149
1150    class SignageSubcategory(OpenIntEnum):
1151        GENERIC_SIGNAGE = 0x00
1152        DIGITAL_SIGNAGE = 0x01
1153        ELECTRONIC_LABEL = 0x02
1154
1155    class PulseOximeterSubcategory(OpenIntEnum):
1156        GENERIC_PULSE_OXIMETER = 0x00
1157        FINGERTIP_PULSE_OXIMETER = 0x01
1158        WRIST_WORN_PULSE_OXIMETER = 0x02
1159
1160    class WeightScaleSubcategory(OpenIntEnum):
1161        GENERIC_WEIGHT_SCALE = 0x00
1162
1163    class PersonalMobilityDeviceSubcategory(OpenIntEnum):
1164        GENERIC_PERSONAL_MOBILITY_DEVICE = 0x00
1165        POWERED_WHEELCHAIR = 0x01
1166        MOBILITY_SCOOTER = 0x02
1167
1168    class ContinuousGlucoseMonitorSubcategory(OpenIntEnum):
1169        GENERIC_CONTINUOUS_GLUCOSE_MONITOR = 0x00
1170
1171    class InsulinPumpSubcategory(OpenIntEnum):
1172        GENERIC_INSULIN_PUMP = 0x00
1173        INSULIN_PUMP_DURABLE_PUMP = 0x01
1174        INSULIN_PUMP_PATCH_PUMP = 0x02
1175        INSULIN_PEN = 0x03
1176
1177    class MedicationDeliverySubcategory(OpenIntEnum):
1178        GENERIC_MEDICATION_DELIVERY = 0x00
1179
1180    class SpirometerSubcategory(OpenIntEnum):
1181        GENERIC_SPIROMETER = 0x00
1182        HANDHELD_SPIROMETER = 0x01
1183
1184    class OutdoorSportsActivitySubcategory(OpenIntEnum):
1185        GENERIC_OUTDOOR_SPORTS_ACTIVITY = 0x00
1186        LOCATION_DISPLAY = 0x01
1187        LOCATION_AND_NAVIGATION_DISPLAY = 0x02
1188        LOCATION_POD = 0x03
1189        LOCATION_AND_NAVIGATION_POD = 0x04
1190
1191    class _OpenSubcategory(OpenIntEnum):
1192        GENERIC = 0x00
1193
1194    SUBCATEGORY_CLASSES = {
1195        Category.UNKNOWN: UnknownSubcategory,
1196        Category.PHONE: PhoneSubcategory,
1197        Category.COMPUTER: ComputerSubcategory,
1198        Category.WATCH: WatchSubcategory,
1199        Category.CLOCK: ClockSubcategory,
1200        Category.DISPLAY: DisplaySubcategory,
1201        Category.REMOTE_CONTROL: RemoteControlSubcategory,
1202        Category.EYE_GLASSES: EyeglassesSubcategory,
1203        Category.TAG: TagSubcategory,
1204        Category.KEYRING: KeyringSubcategory,
1205        Category.MEDIA_PLAYER: MediaPlayerSubcategory,
1206        Category.BARCODE_SCANNER: BarcodeScannerSubcategory,
1207        Category.THERMOMETER: ThermometerSubcategory,
1208        Category.HEART_RATE_SENSOR: HeartRateSensorSubcategory,
1209        Category.BLOOD_PRESSURE: BloodPressureSubcategory,
1210        Category.HUMAN_INTERFACE_DEVICE: HumanInterfaceDeviceSubcategory,
1211        Category.GLUCOSE_METER: GlucoseMeterSubcategory,
1212        Category.RUNNING_WALKING_SENSOR: RunningWalkingSensorSubcategory,
1213        Category.CYCLING: CyclingSubcategory,
1214        Category.CONTROL_DEVICE: ControlDeviceSubcategory,
1215        Category.NETWORK_DEVICE: NetworkDeviceSubcategory,
1216        Category.SENSOR: SensorSubcategory,
1217        Category.LIGHT_FIXTURES: LightFixturesSubcategory,
1218        Category.FAN: FanSubcategory,
1219        Category.HVAC: HvacSubcategory,
1220        Category.AIR_CONDITIONING: AirConditioningSubcategory,
1221        Category.HUMIDIFIER: HumidifierSubcategory,
1222        Category.HEATING: HeatingSubcategory,
1223        Category.ACCESS_CONTROL: AccessControlSubcategory,
1224        Category.MOTORIZED_DEVICE: MotorizedDeviceSubcategory,
1225        Category.POWER_DEVICE: PowerDeviceSubcategory,
1226        Category.LIGHT_SOURCE: LightSourceSubcategory,
1227        Category.WINDOW_COVERING: WindowCoveringSubcategory,
1228        Category.AUDIO_SINK: AudioSinkSubcategory,
1229        Category.AUDIO_SOURCE: AudioSourceSubcategory,
1230        Category.MOTORIZED_VEHICLE: MotorizedVehicleSubcategory,
1231        Category.DOMESTIC_APPLIANCE: DomesticApplianceSubcategory,
1232        Category.WEARABLE_AUDIO_DEVICE: WearableAudioDeviceSubcategory,
1233        Category.AIRCRAFT: AircraftSubcategory,
1234        Category.AV_EQUIPMENT: AvEquipmentSubcategory,
1235        Category.DISPLAY_EQUIPMENT: DisplayEquipmentSubcategory,
1236        Category.HEARING_AID: HearingAidSubcategory,
1237        Category.GAMING: GamingSubcategory,
1238        Category.SIGNAGE: SignageSubcategory,
1239        Category.PULSE_OXIMETER: PulseOximeterSubcategory,
1240        Category.WEIGHT_SCALE: WeightScaleSubcategory,
1241        Category.PERSONAL_MOBILITY_DEVICE: PersonalMobilityDeviceSubcategory,
1242        Category.CONTINUOUS_GLUCOSE_MONITOR: ContinuousGlucoseMonitorSubcategory,
1243        Category.INSULIN_PUMP: InsulinPumpSubcategory,
1244        Category.MEDICATION_DELIVERY: MedicationDeliverySubcategory,
1245        Category.SPIROMETER: SpirometerSubcategory,
1246        Category.OUTDOOR_SPORTS_ACTIVITY: OutdoorSportsActivitySubcategory,
1247    }
1248
1249    category: Category
1250    subcategory: enum.IntEnum
1251
1252    @classmethod
1253    def from_int(cls, appearance: int) -> Self:
1254        category = cls.Category(appearance >> 6)
1255        return cls(category, appearance & 0x3F)
1256
1257    def __init__(self, category: Category, subcategory: int) -> None:
1258        self.category = category
1259        if subcategory_class := self.SUBCATEGORY_CLASSES.get(category):
1260            self.subcategory = subcategory_class(subcategory)
1261        else:
1262            self.subcategory = self._OpenSubcategory(subcategory)
1263
1264    def __int__(self) -> int:
1265        return self.category << 6 | self.subcategory
1266
1267    def __repr__(self) -> str:
1268        return (
1269            'Appearance('
1270            f'category={self.category.name}, '
1271            f'subcategory={self.subcategory.name}'
1272            ')'
1273        )
1274
1275    def __str__(self) -> str:
1276        return f'{self.category.name}/{self.subcategory.name}'
1277
1278
1279# -----------------------------------------------------------------------------
1280# Advertising Data
1281# -----------------------------------------------------------------------------
1282AdvertisingDataObject = Union[
1283    List[UUID],
1284    Tuple[UUID, bytes],
1285    bytes,
1286    str,
1287    int,
1288    Tuple[int, int],
1289    Tuple[int, bytes],
1290    Appearance,
1291]
1292
1293
1294class AdvertisingData:
1295    # fmt: off
1296    # pylint: disable=line-too-long
1297
1298    FLAGS                                            = 0x01
1299    INCOMPLETE_LIST_OF_16_BIT_SERVICE_CLASS_UUIDS    = 0x02
1300    COMPLETE_LIST_OF_16_BIT_SERVICE_CLASS_UUIDS      = 0x03
1301    INCOMPLETE_LIST_OF_32_BIT_SERVICE_CLASS_UUIDS    = 0x04
1302    COMPLETE_LIST_OF_32_BIT_SERVICE_CLASS_UUIDS      = 0x05
1303    INCOMPLETE_LIST_OF_128_BIT_SERVICE_CLASS_UUIDS   = 0x06
1304    COMPLETE_LIST_OF_128_BIT_SERVICE_CLASS_UUIDS     = 0x07
1305    SHORTENED_LOCAL_NAME                             = 0x08
1306    COMPLETE_LOCAL_NAME                              = 0x09
1307    TX_POWER_LEVEL                                   = 0x0A
1308    CLASS_OF_DEVICE                                  = 0x0D
1309    SIMPLE_PAIRING_HASH_C                            = 0x0E
1310    SIMPLE_PAIRING_HASH_C_192                        = 0x0E
1311    SIMPLE_PAIRING_RANDOMIZER_R                      = 0x0F
1312    SIMPLE_PAIRING_RANDOMIZER_R_192                  = 0x0F
1313    DEVICE_ID                                        = 0x10
1314    SECURITY_MANAGER_TK_VALUE                        = 0x10
1315    SECURITY_MANAGER_OUT_OF_BAND_FLAGS               = 0x11
1316    PERIPHERAL_CONNECTION_INTERVAL_RANGE             = 0x12
1317    LIST_OF_16_BIT_SERVICE_SOLICITATION_UUIDS        = 0x14
1318    LIST_OF_128_BIT_SERVICE_SOLICITATION_UUIDS       = 0x15
1319    SERVICE_DATA                                     = 0x16
1320    SERVICE_DATA_16_BIT_UUID                         = 0x16
1321    PUBLIC_TARGET_ADDRESS                            = 0x17
1322    RANDOM_TARGET_ADDRESS                            = 0x18
1323    APPEARANCE                                       = 0x19
1324    ADVERTISING_INTERVAL                             = 0x1A
1325    LE_BLUETOOTH_DEVICE_ADDRESS                      = 0x1B
1326    LE_ROLE                                          = 0x1C
1327    SIMPLE_PAIRING_HASH_C_256                        = 0x1D
1328    SIMPLE_PAIRING_RANDOMIZER_R_256                  = 0x1E
1329    LIST_OF_32_BIT_SERVICE_SOLICITATION_UUIDS        = 0x1F
1330    SERVICE_DATA_32_BIT_UUID                         = 0x20
1331    SERVICE_DATA_128_BIT_UUID                        = 0x21
1332    LE_SECURE_CONNECTIONS_CONFIRMATION_VALUE         = 0x22
1333    LE_SECURE_CONNECTIONS_RANDOM_VALUE               = 0x23
1334    URI                                              = 0x24
1335    INDOOR_POSITIONING                               = 0x25
1336    TRANSPORT_DISCOVERY_DATA                         = 0x26
1337    LE_SUPPORTED_FEATURES                            = 0x27
1338    CHANNEL_MAP_UPDATE_INDICATION                    = 0x28
1339    PB_ADV                                           = 0x29
1340    MESH_MESSAGE                                     = 0x2A
1341    MESH_BEACON                                      = 0x2B
1342    BIGINFO                                          = 0x2C
1343    BROADCAST_CODE                                   = 0x2D
1344    RESOLVABLE_SET_IDENTIFIER                        = 0x2E
1345    ADVERTISING_INTERVAL_LONG                        = 0x2F
1346    BROADCAST_NAME                                   = 0x30
1347    ENCRYPTED_ADVERTISING_DATA                       = 0X31
1348    PERIODIC_ADVERTISING_RESPONSE_TIMING_INFORMATION = 0X32
1349    ELECTRONIC_SHELF_LABEL                           = 0X34
1350    THREE_D_INFORMATION_DATA                         = 0x3D
1351    MANUFACTURER_SPECIFIC_DATA                       = 0xFF
1352
1353    AD_TYPE_NAMES = {
1354        FLAGS:                                            'FLAGS',
1355        INCOMPLETE_LIST_OF_16_BIT_SERVICE_CLASS_UUIDS:    'INCOMPLETE_LIST_OF_16_BIT_SERVICE_CLASS_UUIDS',
1356        COMPLETE_LIST_OF_16_BIT_SERVICE_CLASS_UUIDS:      'COMPLETE_LIST_OF_16_BIT_SERVICE_CLASS_UUIDS',
1357        INCOMPLETE_LIST_OF_32_BIT_SERVICE_CLASS_UUIDS:    'INCOMPLETE_LIST_OF_32_BIT_SERVICE_CLASS_UUIDS',
1358        COMPLETE_LIST_OF_32_BIT_SERVICE_CLASS_UUIDS:      'COMPLETE_LIST_OF_32_BIT_SERVICE_CLASS_UUIDS',
1359        INCOMPLETE_LIST_OF_128_BIT_SERVICE_CLASS_UUIDS:   'INCOMPLETE_LIST_OF_128_BIT_SERVICE_CLASS_UUIDS',
1360        COMPLETE_LIST_OF_128_BIT_SERVICE_CLASS_UUIDS:     'COMPLETE_LIST_OF_128_BIT_SERVICE_CLASS_UUIDS',
1361        SHORTENED_LOCAL_NAME:                             'SHORTENED_LOCAL_NAME',
1362        COMPLETE_LOCAL_NAME:                              'COMPLETE_LOCAL_NAME',
1363        TX_POWER_LEVEL:                                   'TX_POWER_LEVEL',
1364        CLASS_OF_DEVICE:                                  'CLASS_OF_DEVICE',
1365        SIMPLE_PAIRING_HASH_C:                            'SIMPLE_PAIRING_HASH_C',
1366        SIMPLE_PAIRING_HASH_C_192:                        'SIMPLE_PAIRING_HASH_C_192',
1367        SIMPLE_PAIRING_RANDOMIZER_R:                      'SIMPLE_PAIRING_RANDOMIZER_R',
1368        SIMPLE_PAIRING_RANDOMIZER_R_192:                  'SIMPLE_PAIRING_RANDOMIZER_R_192',
1369        DEVICE_ID:                                        'DEVICE_ID',
1370        SECURITY_MANAGER_TK_VALUE:                        'SECURITY_MANAGER_TK_VALUE',
1371        SECURITY_MANAGER_OUT_OF_BAND_FLAGS:               'SECURITY_MANAGER_OUT_OF_BAND_FLAGS',
1372        PERIPHERAL_CONNECTION_INTERVAL_RANGE:             'PERIPHERAL_CONNECTION_INTERVAL_RANGE',
1373        LIST_OF_16_BIT_SERVICE_SOLICITATION_UUIDS:        'LIST_OF_16_BIT_SERVICE_SOLICITATION_UUIDS',
1374        LIST_OF_128_BIT_SERVICE_SOLICITATION_UUIDS:       'LIST_OF_128_BIT_SERVICE_SOLICITATION_UUIDS',
1375        SERVICE_DATA_16_BIT_UUID:                         'SERVICE_DATA_16_BIT_UUID',
1376        PUBLIC_TARGET_ADDRESS:                            'PUBLIC_TARGET_ADDRESS',
1377        RANDOM_TARGET_ADDRESS:                            'RANDOM_TARGET_ADDRESS',
1378        APPEARANCE:                                       'APPEARANCE',
1379        ADVERTISING_INTERVAL:                             'ADVERTISING_INTERVAL',
1380        LE_BLUETOOTH_DEVICE_ADDRESS:                      'LE_BLUETOOTH_DEVICE_ADDRESS',
1381        LE_ROLE:                                          'LE_ROLE',
1382        SIMPLE_PAIRING_HASH_C_256:                        'SIMPLE_PAIRING_HASH_C_256',
1383        SIMPLE_PAIRING_RANDOMIZER_R_256:                  'SIMPLE_PAIRING_RANDOMIZER_R_256',
1384        LIST_OF_32_BIT_SERVICE_SOLICITATION_UUIDS:        'LIST_OF_32_BIT_SERVICE_SOLICITATION_UUIDS',
1385        SERVICE_DATA_32_BIT_UUID:                         'SERVICE_DATA_32_BIT_UUID',
1386        SERVICE_DATA_128_BIT_UUID:                        'SERVICE_DATA_128_BIT_UUID',
1387        LE_SECURE_CONNECTIONS_CONFIRMATION_VALUE:         'LE_SECURE_CONNECTIONS_CONFIRMATION_VALUE',
1388        LE_SECURE_CONNECTIONS_RANDOM_VALUE:               'LE_SECURE_CONNECTIONS_RANDOM_VALUE',
1389        URI:                                              'URI',
1390        INDOOR_POSITIONING:                               'INDOOR_POSITIONING',
1391        TRANSPORT_DISCOVERY_DATA:                         'TRANSPORT_DISCOVERY_DATA',
1392        LE_SUPPORTED_FEATURES:                            'LE_SUPPORTED_FEATURES',
1393        CHANNEL_MAP_UPDATE_INDICATION:                    'CHANNEL_MAP_UPDATE_INDICATION',
1394        PB_ADV:                                           'PB_ADV',
1395        MESH_MESSAGE:                                     'MESH_MESSAGE',
1396        MESH_BEACON:                                      'MESH_BEACON',
1397        BIGINFO:                                          'BIGINFO',
1398        BROADCAST_CODE:                                   'BROADCAST_CODE',
1399        RESOLVABLE_SET_IDENTIFIER:                        'RESOLVABLE_SET_IDENTIFIER',
1400        ADVERTISING_INTERVAL_LONG:                        'ADVERTISING_INTERVAL_LONG',
1401        BROADCAST_NAME:                                   'BROADCAST_NAME',
1402        ENCRYPTED_ADVERTISING_DATA:                       'ENCRYPTED_ADVERTISING_DATA',
1403        PERIODIC_ADVERTISING_RESPONSE_TIMING_INFORMATION: 'PERIODIC_ADVERTISING_RESPONSE_TIMING_INFORMATION',
1404        ELECTRONIC_SHELF_LABEL:                           'ELECTRONIC_SHELF_LABEL',
1405        THREE_D_INFORMATION_DATA:                         'THREE_D_INFORMATION_DATA',
1406        MANUFACTURER_SPECIFIC_DATA:                       'MANUFACTURER_SPECIFIC_DATA'
1407    }
1408
1409    LE_LIMITED_DISCOVERABLE_MODE_FLAG = 0x01
1410    LE_GENERAL_DISCOVERABLE_MODE_FLAG = 0x02
1411    BR_EDR_NOT_SUPPORTED_FLAG         = 0x04
1412    BR_EDR_CONTROLLER_FLAG            = 0x08
1413    BR_EDR_HOST_FLAG                  = 0x10
1414
1415    ad_structures: List[Tuple[int, bytes]]
1416
1417    # fmt: on
1418    # pylint: enable=line-too-long
1419
1420    def __init__(self, ad_structures: Optional[List[Tuple[int, bytes]]] = None) -> None:
1421        if ad_structures is None:
1422            ad_structures = []
1423        self.ad_structures = ad_structures[:]
1424
1425    @classmethod
1426    def from_bytes(cls, data: bytes) -> AdvertisingData:
1427        instance = AdvertisingData()
1428        instance.append(data)
1429        return instance
1430
1431    @staticmethod
1432    def flags_to_string(flags, short=False):
1433        flag_names = (
1434            ['LE Limited', 'LE General', 'No BR/EDR', 'BR/EDR C', 'BR/EDR H']
1435            if short
1436            else [
1437                'LE Limited Discoverable Mode',
1438                'LE General Discoverable Mode',
1439                'BR/EDR Not Supported',
1440                'Simultaneous LE and BR/EDR (Controller)',
1441                'Simultaneous LE and BR/EDR (Host)',
1442            ]
1443        )
1444        return ','.join(bit_flags_to_strings(flags, flag_names))
1445
1446    @staticmethod
1447    def uuid_list_to_objects(ad_data: bytes, uuid_size: int) -> List[UUID]:
1448        uuids = []
1449        offset = 0
1450        while (offset + uuid_size) <= len(ad_data):
1451            uuids.append(UUID.from_bytes(ad_data[offset : offset + uuid_size]))
1452            offset += uuid_size
1453        return uuids
1454
1455    @staticmethod
1456    def uuid_list_to_string(ad_data, uuid_size):
1457        return ', '.join(
1458            [
1459                str(uuid)
1460                for uuid in AdvertisingData.uuid_list_to_objects(ad_data, uuid_size)
1461            ]
1462        )
1463
1464    @staticmethod
1465    def ad_data_to_string(ad_type, ad_data):
1466        if ad_type == AdvertisingData.FLAGS:
1467            ad_type_str = 'Flags'
1468            ad_data_str = AdvertisingData.flags_to_string(ad_data[0], short=True)
1469        elif ad_type == AdvertisingData.COMPLETE_LIST_OF_16_BIT_SERVICE_CLASS_UUIDS:
1470            ad_type_str = 'Complete List of 16-bit Service Class UUIDs'
1471            ad_data_str = AdvertisingData.uuid_list_to_string(ad_data, 2)
1472        elif ad_type == AdvertisingData.INCOMPLETE_LIST_OF_16_BIT_SERVICE_CLASS_UUIDS:
1473            ad_type_str = 'Incomplete List of 16-bit Service Class UUIDs'
1474            ad_data_str = AdvertisingData.uuid_list_to_string(ad_data, 2)
1475        elif ad_type == AdvertisingData.COMPLETE_LIST_OF_32_BIT_SERVICE_CLASS_UUIDS:
1476            ad_type_str = 'Complete List of 32-bit Service Class UUIDs'
1477            ad_data_str = AdvertisingData.uuid_list_to_string(ad_data, 4)
1478        elif ad_type == AdvertisingData.INCOMPLETE_LIST_OF_32_BIT_SERVICE_CLASS_UUIDS:
1479            ad_type_str = 'Incomplete List of 32-bit Service Class UUIDs'
1480            ad_data_str = AdvertisingData.uuid_list_to_string(ad_data, 4)
1481        elif ad_type == AdvertisingData.COMPLETE_LIST_OF_128_BIT_SERVICE_CLASS_UUIDS:
1482            ad_type_str = 'Complete List of 128-bit Service Class UUIDs'
1483            ad_data_str = AdvertisingData.uuid_list_to_string(ad_data, 16)
1484        elif ad_type == AdvertisingData.INCOMPLETE_LIST_OF_128_BIT_SERVICE_CLASS_UUIDS:
1485            ad_type_str = 'Incomplete List of 128-bit Service Class UUIDs'
1486            ad_data_str = AdvertisingData.uuid_list_to_string(ad_data, 16)
1487        elif ad_type == AdvertisingData.SERVICE_DATA_16_BIT_UUID:
1488            ad_type_str = 'Service Data'
1489            uuid = UUID.from_bytes(ad_data[:2])
1490            ad_data_str = f'service={uuid}, data={ad_data[2:].hex()}'
1491        elif ad_type == AdvertisingData.SERVICE_DATA_32_BIT_UUID:
1492            ad_type_str = 'Service Data'
1493            uuid = UUID.from_bytes(ad_data[:4])
1494            ad_data_str = f'service={uuid}, data={ad_data[4:].hex()}'
1495        elif ad_type == AdvertisingData.SERVICE_DATA_128_BIT_UUID:
1496            ad_type_str = 'Service Data'
1497            uuid = UUID.from_bytes(ad_data[:16])
1498            ad_data_str = f'service={uuid}, data={ad_data[16:].hex()}'
1499        elif ad_type == AdvertisingData.SHORTENED_LOCAL_NAME:
1500            ad_type_str = 'Shortened Local Name'
1501            ad_data_str = f'"{ad_data.decode("utf-8")}"'
1502        elif ad_type == AdvertisingData.COMPLETE_LOCAL_NAME:
1503            ad_type_str = 'Complete Local Name'
1504            ad_data_str = f'"{ad_data.decode("utf-8")}"'
1505        elif ad_type == AdvertisingData.TX_POWER_LEVEL:
1506            ad_type_str = 'TX Power Level'
1507            ad_data_str = str(ad_data[0])
1508        elif ad_type == AdvertisingData.MANUFACTURER_SPECIFIC_DATA:
1509            ad_type_str = 'Manufacturer Specific Data'
1510            company_id = struct.unpack_from('<H', ad_data, 0)[0]
1511            company_name = COMPANY_IDENTIFIERS.get(company_id, f'0x{company_id:04X}')
1512            ad_data_str = f'company={company_name}, data={ad_data[2:].hex()}'
1513        elif ad_type == AdvertisingData.APPEARANCE:
1514            ad_type_str = 'Appearance'
1515            appearance = Appearance.from_int(struct.unpack_from('<H', ad_data, 0)[0])
1516            ad_data_str = str(appearance)
1517        elif ad_type == AdvertisingData.BROADCAST_NAME:
1518            ad_type_str = 'Broadcast Name'
1519            ad_data_str = ad_data.decode('utf-8')
1520        else:
1521            ad_type_str = AdvertisingData.AD_TYPE_NAMES.get(ad_type, f'0x{ad_type:02X}')
1522            ad_data_str = ad_data.hex()
1523
1524        return f'[{ad_type_str}]: {ad_data_str}'
1525
1526    # pylint: disable=too-many-return-statements
1527    @staticmethod
1528    def ad_data_to_object(ad_type: int, ad_data: bytes) -> AdvertisingDataObject:
1529        if ad_type in (
1530            AdvertisingData.COMPLETE_LIST_OF_16_BIT_SERVICE_CLASS_UUIDS,
1531            AdvertisingData.INCOMPLETE_LIST_OF_16_BIT_SERVICE_CLASS_UUIDS,
1532            AdvertisingData.LIST_OF_16_BIT_SERVICE_SOLICITATION_UUIDS,
1533        ):
1534            return AdvertisingData.uuid_list_to_objects(ad_data, 2)
1535
1536        if ad_type in (
1537            AdvertisingData.COMPLETE_LIST_OF_32_BIT_SERVICE_CLASS_UUIDS,
1538            AdvertisingData.INCOMPLETE_LIST_OF_32_BIT_SERVICE_CLASS_UUIDS,
1539            AdvertisingData.LIST_OF_32_BIT_SERVICE_SOLICITATION_UUIDS,
1540        ):
1541            return AdvertisingData.uuid_list_to_objects(ad_data, 4)
1542
1543        if ad_type in (
1544            AdvertisingData.COMPLETE_LIST_OF_128_BIT_SERVICE_CLASS_UUIDS,
1545            AdvertisingData.INCOMPLETE_LIST_OF_128_BIT_SERVICE_CLASS_UUIDS,
1546            AdvertisingData.LIST_OF_128_BIT_SERVICE_SOLICITATION_UUIDS,
1547        ):
1548            return AdvertisingData.uuid_list_to_objects(ad_data, 16)
1549
1550        if ad_type == AdvertisingData.SERVICE_DATA_16_BIT_UUID:
1551            return (UUID.from_bytes(ad_data[:2]), ad_data[2:])
1552
1553        if ad_type == AdvertisingData.SERVICE_DATA_32_BIT_UUID:
1554            return (UUID.from_bytes(ad_data[:4]), ad_data[4:])
1555
1556        if ad_type == AdvertisingData.SERVICE_DATA_128_BIT_UUID:
1557            return (UUID.from_bytes(ad_data[:16]), ad_data[16:])
1558
1559        if ad_type in (
1560            AdvertisingData.SHORTENED_LOCAL_NAME,
1561            AdvertisingData.COMPLETE_LOCAL_NAME,
1562            AdvertisingData.URI,
1563            AdvertisingData.BROADCAST_NAME,
1564        ):
1565            return ad_data.decode("utf-8")
1566
1567        if ad_type in (AdvertisingData.TX_POWER_LEVEL, AdvertisingData.FLAGS):
1568            return cast(int, struct.unpack('B', ad_data)[0])
1569
1570        if ad_type in (AdvertisingData.ADVERTISING_INTERVAL,):
1571            return cast(int, struct.unpack('<H', ad_data)[0])
1572
1573        if ad_type == AdvertisingData.CLASS_OF_DEVICE:
1574            return cast(int, struct.unpack('<I', bytes([*ad_data, 0]))[0])
1575
1576        if ad_type == AdvertisingData.PERIPHERAL_CONNECTION_INTERVAL_RANGE:
1577            return cast(Tuple[int, int], struct.unpack('<HH', ad_data))
1578
1579        if ad_type == AdvertisingData.MANUFACTURER_SPECIFIC_DATA:
1580            return (cast(int, struct.unpack_from('<H', ad_data, 0)[0]), ad_data[2:])
1581
1582        if ad_type == AdvertisingData.APPEARANCE:
1583            return Appearance.from_int(
1584                cast(int, struct.unpack_from('<H', ad_data, 0)[0])
1585            )
1586
1587        return ad_data
1588
1589    def append(self, data: bytes) -> None:
1590        offset = 0
1591        while offset + 1 < len(data):
1592            length = data[offset]
1593            offset += 1
1594            if length > 0:
1595                ad_type = data[offset]
1596                ad_data = data[offset + 1 : offset + length]
1597                self.ad_structures.append((ad_type, ad_data))
1598            offset += length
1599
1600    def get_all(self, type_id: int, raw: bool = False) -> List[AdvertisingDataObject]:
1601        '''
1602        Get Advertising Data Structure(s) with a given type
1603
1604        Returns a (possibly empty) list of matches.
1605        '''
1606
1607        def process_ad_data(ad_data: bytes) -> AdvertisingDataObject:
1608            return ad_data if raw else self.ad_data_to_object(type_id, ad_data)
1609
1610        return [process_ad_data(ad[1]) for ad in self.ad_structures if ad[0] == type_id]
1611
1612    def get(self, type_id: int, raw: bool = False) -> Optional[AdvertisingDataObject]:
1613        '''
1614        Get Advertising Data Structure(s) with a given type
1615
1616        Returns the first entry, or None if no structure matches.
1617        '''
1618
1619        all_objects = self.get_all(type_id, raw=raw)
1620        return all_objects[0] if all_objects else None
1621
1622    def __bytes__(self):
1623        return b''.join(
1624            [bytes([len(x[1]) + 1, x[0]]) + x[1] for x in self.ad_structures]
1625        )
1626
1627    def to_string(self, separator=', '):
1628        return separator.join(
1629            [AdvertisingData.ad_data_to_string(x[0], x[1]) for x in self.ad_structures]
1630        )
1631
1632    def __str__(self):
1633        return self.to_string()
1634
1635
1636# -----------------------------------------------------------------------------
1637# Connection Parameters
1638# -----------------------------------------------------------------------------
1639class ConnectionParameters:
1640    def __init__(self, connection_interval, peripheral_latency, supervision_timeout):
1641        self.connection_interval = connection_interval
1642        self.peripheral_latency = peripheral_latency
1643        self.supervision_timeout = supervision_timeout
1644
1645    def __str__(self):
1646        return (
1647            f'ConnectionParameters(connection_interval={self.connection_interval}, '
1648            f'peripheral_latency={self.peripheral_latency}, '
1649            f'supervision_timeout={self.supervision_timeout}'
1650        )
1651
1652
1653# -----------------------------------------------------------------------------
1654# Connection PHY
1655# -----------------------------------------------------------------------------
1656class ConnectionPHY:
1657    def __init__(self, tx_phy, rx_phy):
1658        self.tx_phy = tx_phy
1659        self.rx_phy = rx_phy
1660
1661    def __str__(self):
1662        return f'ConnectionPHY(tx_phy={self.tx_phy}, rx_phy={self.rx_phy})'
1663
1664
1665# -----------------------------------------------------------------------------
1666# LE Role
1667# -----------------------------------------------------------------------------
1668class LeRole(enum.IntEnum):
1669    PERIPHERAL_ONLY = 0x00
1670    CENTRAL_ONLY = 0x01
1671    BOTH_PERIPHERAL_PREFERRED = 0x02
1672    BOTH_CENTRAL_PREFERRED = 0x03
1673