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.
- bumble/_version.py +9 -4
- bumble/apps/auracast.py +631 -98
- bumble/apps/bench.py +238 -157
- bumble/apps/console.py +19 -12
- bumble/apps/controller_info.py +23 -7
- bumble/apps/device_info.py +50 -4
- bumble/apps/gg_bridge.py +1 -1
- bumble/apps/lea_unicast/app.py +61 -201
- bumble/att.py +51 -37
- bumble/audio/__init__.py +17 -0
- bumble/audio/io.py +553 -0
- bumble/controller.py +24 -9
- bumble/core.py +305 -156
- bumble/device.py +1090 -99
- bumble/gatt.py +36 -226
- bumble/gatt_adapters.py +374 -0
- bumble/gatt_client.py +52 -33
- bumble/gatt_server.py +5 -5
- bumble/hci.py +812 -14
- bumble/host.py +367 -65
- bumble/l2cap.py +3 -16
- bumble/pairing.py +5 -5
- bumble/pandora/host.py +7 -12
- bumble/profiles/aics.py +48 -57
- bumble/profiles/ascs.py +8 -19
- bumble/profiles/asha.py +16 -14
- bumble/profiles/bass.py +16 -22
- bumble/profiles/battery_service.py +13 -3
- bumble/profiles/device_information_service.py +16 -14
- bumble/profiles/gap.py +12 -8
- bumble/profiles/gatt_service.py +167 -0
- bumble/profiles/gmap.py +198 -0
- bumble/profiles/hap.py +8 -6
- bumble/profiles/heart_rate_service.py +20 -4
- bumble/profiles/le_audio.py +87 -4
- bumble/profiles/mcp.py +11 -9
- bumble/profiles/pacs.py +61 -16
- bumble/profiles/tmap.py +8 -12
- bumble/profiles/{vcp.py → vcs.py} +35 -29
- bumble/profiles/vocs.py +62 -85
- bumble/sdp.py +223 -93
- bumble/smp.py +1 -1
- bumble/utils.py +12 -2
- bumble/vendor/android/hci.py +1 -1
- {bumble-0.0.204.dist-info → bumble-0.0.208.dist-info}/METADATA +13 -11
- {bumble-0.0.204.dist-info → bumble-0.0.208.dist-info}/RECORD +50 -46
- {bumble-0.0.204.dist-info → bumble-0.0.208.dist-info}/WHEEL +1 -1
- {bumble-0.0.204.dist-info → bumble-0.0.208.dist-info}/entry_points.txt +1 -0
- bumble/apps/lea_unicast/liblc3.wasm +0 -0
- {bumble-0.0.204.dist-info → bumble-0.0.208.dist-info}/LICENSE +0 -0
- {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-
|
|
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
|
-
|
|
19
|
+
|
|
20
20
|
import enum
|
|
21
21
|
import struct
|
|
22
|
-
from typing import
|
|
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:
|
|
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:
|
|
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) ->
|
|
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) ->
|
|
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
|
-
|
|
1284
|
-
|
|
1283
|
+
list[UUID],
|
|
1284
|
+
tuple[UUID, bytes],
|
|
1285
1285
|
bytes,
|
|
1286
1286
|
str,
|
|
1287
1287
|
int,
|
|
1288
|
-
|
|
1289
|
-
|
|
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
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
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:
|
|
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[
|
|
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) ->
|
|
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
|
-
@
|
|
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
|
-
|
|
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.
|
|
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
|
-
@
|
|
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(
|
|
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.
|
|
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
|
-
|
|
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
|