bumble 0.0.204__py3-none-any.whl → 0.0.208__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. bumble/_version.py +9 -4
  2. bumble/apps/auracast.py +631 -98
  3. bumble/apps/bench.py +238 -157
  4. bumble/apps/console.py +19 -12
  5. bumble/apps/controller_info.py +23 -7
  6. bumble/apps/device_info.py +50 -4
  7. bumble/apps/gg_bridge.py +1 -1
  8. bumble/apps/lea_unicast/app.py +61 -201
  9. bumble/att.py +51 -37
  10. bumble/audio/__init__.py +17 -0
  11. bumble/audio/io.py +553 -0
  12. bumble/controller.py +24 -9
  13. bumble/core.py +305 -156
  14. bumble/device.py +1090 -99
  15. bumble/gatt.py +36 -226
  16. bumble/gatt_adapters.py +374 -0
  17. bumble/gatt_client.py +52 -33
  18. bumble/gatt_server.py +5 -5
  19. bumble/hci.py +812 -14
  20. bumble/host.py +367 -65
  21. bumble/l2cap.py +3 -16
  22. bumble/pairing.py +5 -5
  23. bumble/pandora/host.py +7 -12
  24. bumble/profiles/aics.py +48 -57
  25. bumble/profiles/ascs.py +8 -19
  26. bumble/profiles/asha.py +16 -14
  27. bumble/profiles/bass.py +16 -22
  28. bumble/profiles/battery_service.py +13 -3
  29. bumble/profiles/device_information_service.py +16 -14
  30. bumble/profiles/gap.py +12 -8
  31. bumble/profiles/gatt_service.py +167 -0
  32. bumble/profiles/gmap.py +198 -0
  33. bumble/profiles/hap.py +8 -6
  34. bumble/profiles/heart_rate_service.py +20 -4
  35. bumble/profiles/le_audio.py +87 -4
  36. bumble/profiles/mcp.py +11 -9
  37. bumble/profiles/pacs.py +61 -16
  38. bumble/profiles/tmap.py +8 -12
  39. bumble/profiles/{vcp.py → vcs.py} +35 -29
  40. bumble/profiles/vocs.py +62 -85
  41. bumble/sdp.py +223 -93
  42. bumble/smp.py +1 -1
  43. bumble/utils.py +12 -2
  44. bumble/vendor/android/hci.py +1 -1
  45. {bumble-0.0.204.dist-info → bumble-0.0.208.dist-info}/METADATA +13 -11
  46. {bumble-0.0.204.dist-info → bumble-0.0.208.dist-info}/RECORD +50 -46
  47. {bumble-0.0.204.dist-info → bumble-0.0.208.dist-info}/WHEEL +1 -1
  48. {bumble-0.0.204.dist-info → bumble-0.0.208.dist-info}/entry_points.txt +1 -0
  49. bumble/apps/lea_unicast/liblc3.wasm +0 -0
  50. {bumble-0.0.204.dist-info → bumble-0.0.208.dist-info}/LICENSE +0 -0
  51. {bumble-0.0.204.dist-info → bumble-0.0.208.dist-info}/top_level.txt +0 -0
bumble/core.py CHANGED
@@ -1,4 +1,4 @@
1
- # Copyright 2021-2022 Google LLC
1
+ # Copyright 2021-2025 Google LLC
2
2
  #
3
3
  # Licensed under the Apache License, Version 2.0 (the "License");
4
4
  # you may not use this file except in compliance with the License.
@@ -16,10 +16,10 @@
16
16
  # Imports
17
17
  # -----------------------------------------------------------------------------
18
18
  from __future__ import annotations
19
- import dataclasses
19
+
20
20
  import enum
21
21
  import struct
22
- from typing import List, Optional, Tuple, Union, cast, Dict
22
+ from typing import cast, overload, Literal, Union, Optional
23
23
  from typing_extensions import Self
24
24
 
25
25
  from bumble.company_ids import COMPANY_IDENTIFIERS
@@ -57,7 +57,7 @@ def bit_flags_to_strings(bits, bit_flag_names):
57
57
  return names
58
58
 
59
59
 
60
- def name_or_number(dictionary: Dict[int, str], number: int, width: int = 2) -> str:
60
+ def name_or_number(dictionary: dict[int, str], number: int, width: int = 2) -> str:
61
61
  name = dictionary.get(number)
62
62
  if name is not None:
63
63
  return name
@@ -200,7 +200,7 @@ class UUID:
200
200
  '''
201
201
 
202
202
  BASE_UUID = bytes.fromhex('00001000800000805F9B34FB')[::-1] # little-endian
203
- UUIDS: List[UUID] = [] # Registry of all instances created
203
+ UUIDS: list[UUID] = [] # Registry of all instances created
204
204
 
205
205
  uuid_bytes: bytes
206
206
  name: Optional[str]
@@ -259,11 +259,11 @@ class UUID:
259
259
  return cls.from_bytes(struct.pack('<I', uuid_32), name)
260
260
 
261
261
  @classmethod
262
- def parse_uuid(cls, uuid_as_bytes: bytes, offset: int) -> Tuple[int, UUID]:
262
+ def parse_uuid(cls, uuid_as_bytes: bytes, offset: int) -> tuple[int, UUID]:
263
263
  return len(uuid_as_bytes), cls.from_bytes(uuid_as_bytes[offset:])
264
264
 
265
265
  @classmethod
266
- def parse_uuid_2(cls, uuid_as_bytes: bytes, offset: int) -> Tuple[int, UUID]:
266
+ def parse_uuid_2(cls, uuid_as_bytes: bytes, offset: int) -> tuple[int, UUID]:
267
267
  return offset + 2, cls.from_bytes(uuid_as_bytes[offset : offset + 2])
268
268
 
269
269
  def to_bytes(self, force_128: bool = False) -> bytes:
@@ -1280,13 +1280,13 @@ class Appearance:
1280
1280
  # Advertising Data
1281
1281
  # -----------------------------------------------------------------------------
1282
1282
  AdvertisingDataObject = Union[
1283
- List[UUID],
1284
- Tuple[UUID, bytes],
1283
+ list[UUID],
1284
+ tuple[UUID, bytes],
1285
1285
  bytes,
1286
1286
  str,
1287
1287
  int,
1288
- Tuple[int, int],
1289
- Tuple[int, bytes],
1288
+ tuple[int, int],
1289
+ tuple[int, bytes],
1290
1290
  Appearance,
1291
1291
  ]
1292
1292
 
@@ -1295,116 +1295,116 @@ class AdvertisingData:
1295
1295
  # fmt: off
1296
1296
  # pylint: disable=line-too-long
1297
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
- }
1298
+ class Type(OpenIntEnum):
1299
+ FLAGS = 0x01
1300
+ INCOMPLETE_LIST_OF_16_BIT_SERVICE_CLASS_UUIDS = 0x02
1301
+ COMPLETE_LIST_OF_16_BIT_SERVICE_CLASS_UUIDS = 0x03
1302
+ INCOMPLETE_LIST_OF_32_BIT_SERVICE_CLASS_UUIDS = 0x04
1303
+ COMPLETE_LIST_OF_32_BIT_SERVICE_CLASS_UUIDS = 0x05
1304
+ INCOMPLETE_LIST_OF_128_BIT_SERVICE_CLASS_UUIDS = 0x06
1305
+ COMPLETE_LIST_OF_128_BIT_SERVICE_CLASS_UUIDS = 0x07
1306
+ SHORTENED_LOCAL_NAME = 0x08
1307
+ COMPLETE_LOCAL_NAME = 0x09
1308
+ TX_POWER_LEVEL = 0x0A
1309
+ CLASS_OF_DEVICE = 0x0D
1310
+ SIMPLE_PAIRING_HASH_C = 0x0E
1311
+ SIMPLE_PAIRING_HASH_C_192 = 0x0E
1312
+ SIMPLE_PAIRING_RANDOMIZER_R = 0x0F
1313
+ SIMPLE_PAIRING_RANDOMIZER_R_192 = 0x0F
1314
+ DEVICE_ID = 0x10
1315
+ SECURITY_MANAGER_TK_VALUE = 0x10
1316
+ SECURITY_MANAGER_OUT_OF_BAND_FLAGS = 0x11
1317
+ PERIPHERAL_CONNECTION_INTERVAL_RANGE = 0x12
1318
+ LIST_OF_16_BIT_SERVICE_SOLICITATION_UUIDS = 0x14
1319
+ LIST_OF_128_BIT_SERVICE_SOLICITATION_UUIDS = 0x15
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
+ # For backward-compatibility
1354
+ FLAGS = Type.FLAGS
1355
+ INCOMPLETE_LIST_OF_16_BIT_SERVICE_CLASS_UUIDS = Type.INCOMPLETE_LIST_OF_16_BIT_SERVICE_CLASS_UUIDS
1356
+ COMPLETE_LIST_OF_16_BIT_SERVICE_CLASS_UUIDS = Type.COMPLETE_LIST_OF_16_BIT_SERVICE_CLASS_UUIDS
1357
+ INCOMPLETE_LIST_OF_32_BIT_SERVICE_CLASS_UUIDS = Type.INCOMPLETE_LIST_OF_32_BIT_SERVICE_CLASS_UUIDS
1358
+ COMPLETE_LIST_OF_32_BIT_SERVICE_CLASS_UUIDS = Type.COMPLETE_LIST_OF_32_BIT_SERVICE_CLASS_UUIDS
1359
+ INCOMPLETE_LIST_OF_128_BIT_SERVICE_CLASS_UUIDS = Type.INCOMPLETE_LIST_OF_128_BIT_SERVICE_CLASS_UUIDS
1360
+ COMPLETE_LIST_OF_128_BIT_SERVICE_CLASS_UUIDS = Type.COMPLETE_LIST_OF_128_BIT_SERVICE_CLASS_UUIDS
1361
+ SHORTENED_LOCAL_NAME = Type.SHORTENED_LOCAL_NAME
1362
+ COMPLETE_LOCAL_NAME = Type.COMPLETE_LOCAL_NAME
1363
+ TX_POWER_LEVEL = Type.TX_POWER_LEVEL
1364
+ CLASS_OF_DEVICE = Type.CLASS_OF_DEVICE
1365
+ SIMPLE_PAIRING_HASH_C = Type.SIMPLE_PAIRING_HASH_C
1366
+ SIMPLE_PAIRING_HASH_C_192 = Type.SIMPLE_PAIRING_HASH_C_192
1367
+ SIMPLE_PAIRING_RANDOMIZER_R = Type.SIMPLE_PAIRING_RANDOMIZER_R
1368
+ SIMPLE_PAIRING_RANDOMIZER_R_192 = Type.SIMPLE_PAIRING_RANDOMIZER_R_192
1369
+ DEVICE_ID = Type.DEVICE_ID
1370
+ SECURITY_MANAGER_TK_VALUE = Type.SECURITY_MANAGER_TK_VALUE
1371
+ SECURITY_MANAGER_OUT_OF_BAND_FLAGS = Type.SECURITY_MANAGER_OUT_OF_BAND_FLAGS
1372
+ PERIPHERAL_CONNECTION_INTERVAL_RANGE = Type.PERIPHERAL_CONNECTION_INTERVAL_RANGE
1373
+ LIST_OF_16_BIT_SERVICE_SOLICITATION_UUIDS = Type.LIST_OF_16_BIT_SERVICE_SOLICITATION_UUIDS
1374
+ LIST_OF_128_BIT_SERVICE_SOLICITATION_UUIDS = Type.LIST_OF_128_BIT_SERVICE_SOLICITATION_UUIDS
1375
+ SERVICE_DATA = Type.SERVICE_DATA_16_BIT_UUID
1376
+ SERVICE_DATA_16_BIT_UUID = Type.SERVICE_DATA_16_BIT_UUID
1377
+ PUBLIC_TARGET_ADDRESS = Type.PUBLIC_TARGET_ADDRESS
1378
+ RANDOM_TARGET_ADDRESS = Type.RANDOM_TARGET_ADDRESS
1379
+ APPEARANCE = Type.APPEARANCE
1380
+ ADVERTISING_INTERVAL = Type.ADVERTISING_INTERVAL
1381
+ LE_BLUETOOTH_DEVICE_ADDRESS = Type.LE_BLUETOOTH_DEVICE_ADDRESS
1382
+ LE_ROLE = Type.LE_ROLE
1383
+ SIMPLE_PAIRING_HASH_C_256 = Type.SIMPLE_PAIRING_HASH_C_256
1384
+ SIMPLE_PAIRING_RANDOMIZER_R_256 = Type.SIMPLE_PAIRING_RANDOMIZER_R_256
1385
+ LIST_OF_32_BIT_SERVICE_SOLICITATION_UUIDS = Type.LIST_OF_32_BIT_SERVICE_SOLICITATION_UUIDS
1386
+ SERVICE_DATA_32_BIT_UUID = Type.SERVICE_DATA_32_BIT_UUID
1387
+ SERVICE_DATA_128_BIT_UUID = Type.SERVICE_DATA_128_BIT_UUID
1388
+ LE_SECURE_CONNECTIONS_CONFIRMATION_VALUE = Type.LE_SECURE_CONNECTIONS_CONFIRMATION_VALUE
1389
+ LE_SECURE_CONNECTIONS_RANDOM_VALUE = Type.LE_SECURE_CONNECTIONS_RANDOM_VALUE
1390
+ URI = Type.URI
1391
+ INDOOR_POSITIONING = Type.INDOOR_POSITIONING
1392
+ TRANSPORT_DISCOVERY_DATA = Type.TRANSPORT_DISCOVERY_DATA
1393
+ LE_SUPPORTED_FEATURES = Type.LE_SUPPORTED_FEATURES
1394
+ CHANNEL_MAP_UPDATE_INDICATION = Type.CHANNEL_MAP_UPDATE_INDICATION
1395
+ PB_ADV = Type.PB_ADV
1396
+ MESH_MESSAGE = Type.MESH_MESSAGE
1397
+ MESH_BEACON = Type.MESH_BEACON
1398
+ BIGINFO = Type.BIGINFO
1399
+ BROADCAST_CODE = Type.BROADCAST_CODE
1400
+ RESOLVABLE_SET_IDENTIFIER = Type.RESOLVABLE_SET_IDENTIFIER
1401
+ ADVERTISING_INTERVAL_LONG = Type.ADVERTISING_INTERVAL_LONG
1402
+ BROADCAST_NAME = Type.BROADCAST_NAME
1403
+ ENCRYPTED_ADVERTISING_DATA = Type.ENCRYPTED_ADVERTISING_DATA
1404
+ PERIODIC_ADVERTISING_RESPONSE_TIMING_INFORMATION = Type.PERIODIC_ADVERTISING_RESPONSE_TIMING_INFORMATION
1405
+ ELECTRONIC_SHELF_LABEL = Type.ELECTRONIC_SHELF_LABEL
1406
+ THREE_D_INFORMATION_DATA = Type.THREE_D_INFORMATION_DATA
1407
+ MANUFACTURER_SPECIFIC_DATA = Type.MANUFACTURER_SPECIFIC_DATA
1408
1408
 
1409
1409
  LE_LIMITED_DISCOVERABLE_MODE_FLAG = 0x01
1410
1410
  LE_GENERAL_DISCOVERABLE_MODE_FLAG = 0x02
@@ -1412,12 +1412,12 @@ class AdvertisingData:
1412
1412
  BR_EDR_CONTROLLER_FLAG = 0x08
1413
1413
  BR_EDR_HOST_FLAG = 0x10
1414
1414
 
1415
- ad_structures: List[Tuple[int, bytes]]
1415
+ ad_structures: list[tuple[int, bytes]]
1416
1416
 
1417
1417
  # fmt: on
1418
1418
  # pylint: enable=line-too-long
1419
1419
 
1420
- def __init__(self, ad_structures: Optional[List[Tuple[int, bytes]]] = None) -> None:
1420
+ def __init__(self, ad_structures: Optional[list[tuple[int, bytes]]] = None) -> None:
1421
1421
  if ad_structures is None:
1422
1422
  ad_structures = []
1423
1423
  self.ad_structures = ad_structures[:]
@@ -1444,7 +1444,7 @@ class AdvertisingData:
1444
1444
  return ','.join(bit_flags_to_strings(flags, flag_names))
1445
1445
 
1446
1446
  @staticmethod
1447
- def uuid_list_to_objects(ad_data: bytes, uuid_size: int) -> List[UUID]:
1447
+ def uuid_list_to_objects(ad_data: bytes, uuid_size: int) -> list[UUID]:
1448
1448
  uuids = []
1449
1449
  offset = 0
1450
1450
  while (offset + uuid_size) <= len(ad_data):
@@ -1461,8 +1461,8 @@ class AdvertisingData:
1461
1461
  ]
1462
1462
  )
1463
1463
 
1464
- @staticmethod
1465
- def ad_data_to_string(ad_type, ad_data):
1464
+ @classmethod
1465
+ def ad_data_to_string(cls, ad_type: int, ad_data: bytes) -> str:
1466
1466
  if ad_type == AdvertisingData.FLAGS:
1467
1467
  ad_type_str = 'Flags'
1468
1468
  ad_data_str = AdvertisingData.flags_to_string(ad_data[0], short=True)
@@ -1501,7 +1501,10 @@ class AdvertisingData:
1501
1501
  ad_data_str = f'"{ad_data.decode("utf-8")}"'
1502
1502
  elif ad_type == AdvertisingData.COMPLETE_LOCAL_NAME:
1503
1503
  ad_type_str = 'Complete Local Name'
1504
- ad_data_str = f'"{ad_data.decode("utf-8")}"'
1504
+ try:
1505
+ ad_data_str = f'"{ad_data.decode("utf-8")}"'
1506
+ except UnicodeDecodeError:
1507
+ ad_data_str = ad_data.hex()
1505
1508
  elif ad_type == AdvertisingData.TX_POWER_LEVEL:
1506
1509
  ad_type_str = 'TX Power Level'
1507
1510
  ad_data_str = str(ad_data[0])
@@ -1518,72 +1521,72 @@ class AdvertisingData:
1518
1521
  ad_type_str = 'Broadcast Name'
1519
1522
  ad_data_str = ad_data.decode('utf-8')
1520
1523
  else:
1521
- ad_type_str = AdvertisingData.AD_TYPE_NAMES.get(ad_type, f'0x{ad_type:02X}')
1524
+ ad_type_str = AdvertisingData.Type(ad_type).name
1522
1525
  ad_data_str = ad_data.hex()
1523
1526
 
1524
1527
  return f'[{ad_type_str}]: {ad_data_str}'
1525
1528
 
1526
1529
  # pylint: disable=too-many-return-statements
1527
- @staticmethod
1528
- def ad_data_to_object(ad_type: int, ad_data: bytes) -> AdvertisingDataObject:
1530
+ @classmethod
1531
+ def ad_data_to_object(cls, ad_type: int, ad_data: bytes) -> AdvertisingDataObject:
1529
1532
  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
+ AdvertisingData.Type.COMPLETE_LIST_OF_16_BIT_SERVICE_CLASS_UUIDS,
1534
+ AdvertisingData.Type.INCOMPLETE_LIST_OF_16_BIT_SERVICE_CLASS_UUIDS,
1535
+ AdvertisingData.Type.LIST_OF_16_BIT_SERVICE_SOLICITATION_UUIDS,
1533
1536
  ):
1534
1537
  return AdvertisingData.uuid_list_to_objects(ad_data, 2)
1535
1538
 
1536
1539
  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
+ AdvertisingData.Type.COMPLETE_LIST_OF_32_BIT_SERVICE_CLASS_UUIDS,
1541
+ AdvertisingData.Type.INCOMPLETE_LIST_OF_32_BIT_SERVICE_CLASS_UUIDS,
1542
+ AdvertisingData.Type.LIST_OF_32_BIT_SERVICE_SOLICITATION_UUIDS,
1540
1543
  ):
1541
1544
  return AdvertisingData.uuid_list_to_objects(ad_data, 4)
1542
1545
 
1543
1546
  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
+ AdvertisingData.Type.COMPLETE_LIST_OF_128_BIT_SERVICE_CLASS_UUIDS,
1548
+ AdvertisingData.Type.INCOMPLETE_LIST_OF_128_BIT_SERVICE_CLASS_UUIDS,
1549
+ AdvertisingData.Type.LIST_OF_128_BIT_SERVICE_SOLICITATION_UUIDS,
1547
1550
  ):
1548
1551
  return AdvertisingData.uuid_list_to_objects(ad_data, 16)
1549
1552
 
1550
- if ad_type == AdvertisingData.SERVICE_DATA_16_BIT_UUID:
1553
+ if ad_type == AdvertisingData.Type.SERVICE_DATA_16_BIT_UUID:
1551
1554
  return (UUID.from_bytes(ad_data[:2]), ad_data[2:])
1552
1555
 
1553
- if ad_type == AdvertisingData.SERVICE_DATA_32_BIT_UUID:
1556
+ if ad_type == AdvertisingData.Type.SERVICE_DATA_32_BIT_UUID:
1554
1557
  return (UUID.from_bytes(ad_data[:4]), ad_data[4:])
1555
1558
 
1556
- if ad_type == AdvertisingData.SERVICE_DATA_128_BIT_UUID:
1559
+ if ad_type == AdvertisingData.Type.SERVICE_DATA_128_BIT_UUID:
1557
1560
  return (UUID.from_bytes(ad_data[:16]), ad_data[16:])
1558
1561
 
1559
1562
  if ad_type in (
1560
- AdvertisingData.SHORTENED_LOCAL_NAME,
1561
- AdvertisingData.COMPLETE_LOCAL_NAME,
1562
- AdvertisingData.URI,
1563
- AdvertisingData.BROADCAST_NAME,
1563
+ AdvertisingData.Type.SHORTENED_LOCAL_NAME,
1564
+ AdvertisingData.Type.COMPLETE_LOCAL_NAME,
1565
+ AdvertisingData.Type.URI,
1566
+ AdvertisingData.Type.BROADCAST_NAME,
1564
1567
  ):
1565
1568
  return ad_data.decode("utf-8")
1566
1569
 
1567
- if ad_type in (AdvertisingData.TX_POWER_LEVEL, AdvertisingData.FLAGS):
1570
+ if ad_type in (AdvertisingData.Type.TX_POWER_LEVEL, AdvertisingData.Type.FLAGS):
1568
1571
  return cast(int, struct.unpack('B', ad_data)[0])
1569
1572
 
1570
- if ad_type in (AdvertisingData.ADVERTISING_INTERVAL,):
1573
+ if ad_type in (AdvertisingData.Type.ADVERTISING_INTERVAL,):
1571
1574
  return cast(int, struct.unpack('<H', ad_data)[0])
1572
1575
 
1573
- if ad_type == AdvertisingData.CLASS_OF_DEVICE:
1576
+ if ad_type == AdvertisingData.Type.CLASS_OF_DEVICE:
1574
1577
  return cast(int, struct.unpack('<I', bytes([*ad_data, 0]))[0])
1575
1578
 
1576
- if ad_type == AdvertisingData.PERIPHERAL_CONNECTION_INTERVAL_RANGE:
1577
- return cast(Tuple[int, int], struct.unpack('<HH', ad_data))
1579
+ if ad_type == AdvertisingData.Type.PERIPHERAL_CONNECTION_INTERVAL_RANGE:
1580
+ return cast(tuple[int, int], struct.unpack('<HH', ad_data))
1578
1581
 
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:
1582
+ if ad_type == AdvertisingData.Type.APPEARANCE:
1583
1583
  return Appearance.from_int(
1584
1584
  cast(int, struct.unpack_from('<H', ad_data, 0)[0])
1585
1585
  )
1586
1586
 
1587
+ if ad_type == AdvertisingData.Type.MANUFACTURER_SPECIFIC_DATA:
1588
+ return (cast(int, struct.unpack_from('<H', ad_data, 0)[0]), ad_data[2:])
1589
+
1587
1590
  return ad_data
1588
1591
 
1589
1592
  def append(self, data: bytes) -> None:
@@ -1597,7 +1600,80 @@ class AdvertisingData:
1597
1600
  self.ad_structures.append((ad_type, ad_data))
1598
1601
  offset += length
1599
1602
 
1600
- def get_all(self, type_id: int, raw: bool = False) -> List[AdvertisingDataObject]:
1603
+ @overload
1604
+ def get_all(
1605
+ self,
1606
+ type_id: Literal[
1607
+ AdvertisingData.Type.COMPLETE_LIST_OF_16_BIT_SERVICE_CLASS_UUIDS,
1608
+ AdvertisingData.Type.INCOMPLETE_LIST_OF_16_BIT_SERVICE_CLASS_UUIDS,
1609
+ AdvertisingData.Type.LIST_OF_16_BIT_SERVICE_SOLICITATION_UUIDS,
1610
+ AdvertisingData.Type.COMPLETE_LIST_OF_32_BIT_SERVICE_CLASS_UUIDS,
1611
+ AdvertisingData.Type.INCOMPLETE_LIST_OF_32_BIT_SERVICE_CLASS_UUIDS,
1612
+ AdvertisingData.Type.LIST_OF_32_BIT_SERVICE_SOLICITATION_UUIDS,
1613
+ AdvertisingData.Type.COMPLETE_LIST_OF_128_BIT_SERVICE_CLASS_UUIDS,
1614
+ AdvertisingData.Type.INCOMPLETE_LIST_OF_128_BIT_SERVICE_CLASS_UUIDS,
1615
+ AdvertisingData.Type.LIST_OF_128_BIT_SERVICE_SOLICITATION_UUIDS,
1616
+ ],
1617
+ raw: Literal[False] = False,
1618
+ ) -> list[list[UUID]]: ...
1619
+ @overload
1620
+ def get_all(
1621
+ self,
1622
+ type_id: Literal[
1623
+ AdvertisingData.Type.SERVICE_DATA_16_BIT_UUID,
1624
+ AdvertisingData.Type.SERVICE_DATA_32_BIT_UUID,
1625
+ AdvertisingData.Type.SERVICE_DATA_128_BIT_UUID,
1626
+ ],
1627
+ raw: Literal[False] = False,
1628
+ ) -> list[tuple[UUID, bytes]]: ...
1629
+ @overload
1630
+ def get_all(
1631
+ self,
1632
+ type_id: Literal[
1633
+ AdvertisingData.Type.SHORTENED_LOCAL_NAME,
1634
+ AdvertisingData.Type.COMPLETE_LOCAL_NAME,
1635
+ AdvertisingData.Type.URI,
1636
+ AdvertisingData.Type.BROADCAST_NAME,
1637
+ ],
1638
+ raw: Literal[False] = False,
1639
+ ) -> list[str]: ...
1640
+ @overload
1641
+ def get_all(
1642
+ self,
1643
+ type_id: Literal[
1644
+ AdvertisingData.Type.TX_POWER_LEVEL,
1645
+ AdvertisingData.Type.FLAGS,
1646
+ AdvertisingData.Type.ADVERTISING_INTERVAL,
1647
+ AdvertisingData.Type.CLASS_OF_DEVICE,
1648
+ ],
1649
+ raw: Literal[False] = False,
1650
+ ) -> list[int]: ...
1651
+ @overload
1652
+ def get_all(
1653
+ self,
1654
+ type_id: Literal[AdvertisingData.Type.PERIPHERAL_CONNECTION_INTERVAL_RANGE,],
1655
+ raw: Literal[False] = False,
1656
+ ) -> list[tuple[int, int]]: ...
1657
+ @overload
1658
+ def get_all(
1659
+ self,
1660
+ type_id: Literal[AdvertisingData.Type.MANUFACTURER_SPECIFIC_DATA,],
1661
+ raw: Literal[False] = False,
1662
+ ) -> list[tuple[int, bytes]]: ...
1663
+ @overload
1664
+ def get_all(
1665
+ self,
1666
+ type_id: Literal[AdvertisingData.Type.APPEARANCE,],
1667
+ raw: Literal[False] = False,
1668
+ ) -> list[Appearance]: ...
1669
+ @overload
1670
+ def get_all(self, type_id: int, raw: Literal[True]) -> list[bytes]: ...
1671
+ @overload
1672
+ def get_all(
1673
+ self, type_id: int, raw: bool = False
1674
+ ) -> list[AdvertisingDataObject]: ...
1675
+
1676
+ def get_all(self, type_id: int, raw: bool = False) -> list[AdvertisingDataObject]: # type: ignore[misc]
1601
1677
  '''
1602
1678
  Get Advertising Data Structure(s) with a given type
1603
1679
 
@@ -1609,6 +1685,79 @@ class AdvertisingData:
1609
1685
 
1610
1686
  return [process_ad_data(ad[1]) for ad in self.ad_structures if ad[0] == type_id]
1611
1687
 
1688
+ @overload
1689
+ def get(
1690
+ self,
1691
+ type_id: Literal[
1692
+ AdvertisingData.Type.COMPLETE_LIST_OF_16_BIT_SERVICE_CLASS_UUIDS,
1693
+ AdvertisingData.Type.INCOMPLETE_LIST_OF_16_BIT_SERVICE_CLASS_UUIDS,
1694
+ AdvertisingData.Type.LIST_OF_16_BIT_SERVICE_SOLICITATION_UUIDS,
1695
+ AdvertisingData.Type.COMPLETE_LIST_OF_32_BIT_SERVICE_CLASS_UUIDS,
1696
+ AdvertisingData.Type.INCOMPLETE_LIST_OF_32_BIT_SERVICE_CLASS_UUIDS,
1697
+ AdvertisingData.Type.LIST_OF_32_BIT_SERVICE_SOLICITATION_UUIDS,
1698
+ AdvertisingData.Type.COMPLETE_LIST_OF_128_BIT_SERVICE_CLASS_UUIDS,
1699
+ AdvertisingData.Type.INCOMPLETE_LIST_OF_128_BIT_SERVICE_CLASS_UUIDS,
1700
+ AdvertisingData.Type.LIST_OF_128_BIT_SERVICE_SOLICITATION_UUIDS,
1701
+ ],
1702
+ raw: Literal[False] = False,
1703
+ ) -> Optional[list[UUID]]: ...
1704
+ @overload
1705
+ def get(
1706
+ self,
1707
+ type_id: Literal[
1708
+ AdvertisingData.Type.SERVICE_DATA_16_BIT_UUID,
1709
+ AdvertisingData.Type.SERVICE_DATA_32_BIT_UUID,
1710
+ AdvertisingData.Type.SERVICE_DATA_128_BIT_UUID,
1711
+ ],
1712
+ raw: Literal[False] = False,
1713
+ ) -> Optional[tuple[UUID, bytes]]: ...
1714
+ @overload
1715
+ def get(
1716
+ self,
1717
+ type_id: Literal[
1718
+ AdvertisingData.Type.SHORTENED_LOCAL_NAME,
1719
+ AdvertisingData.Type.COMPLETE_LOCAL_NAME,
1720
+ AdvertisingData.Type.URI,
1721
+ AdvertisingData.Type.BROADCAST_NAME,
1722
+ ],
1723
+ raw: Literal[False] = False,
1724
+ ) -> Optional[Optional[str]]: ...
1725
+ @overload
1726
+ def get(
1727
+ self,
1728
+ type_id: Literal[
1729
+ AdvertisingData.Type.TX_POWER_LEVEL,
1730
+ AdvertisingData.Type.FLAGS,
1731
+ AdvertisingData.Type.ADVERTISING_INTERVAL,
1732
+ AdvertisingData.Type.CLASS_OF_DEVICE,
1733
+ ],
1734
+ raw: Literal[False] = False,
1735
+ ) -> Optional[int]: ...
1736
+ @overload
1737
+ def get(
1738
+ self,
1739
+ type_id: Literal[AdvertisingData.Type.PERIPHERAL_CONNECTION_INTERVAL_RANGE,],
1740
+ raw: Literal[False] = False,
1741
+ ) -> Optional[tuple[int, int]]: ...
1742
+ @overload
1743
+ def get(
1744
+ self,
1745
+ type_id: Literal[AdvertisingData.Type.MANUFACTURER_SPECIFIC_DATA,],
1746
+ raw: Literal[False] = False,
1747
+ ) -> Optional[tuple[int, bytes]]: ...
1748
+ @overload
1749
+ def get(
1750
+ self,
1751
+ type_id: Literal[AdvertisingData.Type.APPEARANCE,],
1752
+ raw: Literal[False] = False,
1753
+ ) -> Optional[Appearance]: ...
1754
+ @overload
1755
+ def get(self, type_id: int, raw: Literal[True]) -> Optional[bytes]: ...
1756
+ @overload
1757
+ def get(
1758
+ self, type_id: int, raw: bool = False
1759
+ ) -> Optional[AdvertisingDataObject]: ...
1760
+
1612
1761
  def get(self, type_id: int, raw: bool = False) -> Optional[AdvertisingDataObject]:
1613
1762
  '''
1614
1763
  Get Advertising Data Structure(s) with a given type