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