bumble 0.0.202__py3-none-any.whl → 0.0.204__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 +2 -2
- bumble/apps/auracast.py +22 -13
- bumble/apps/bench.py +2 -1
- bumble/apps/hci_bridge.py +1 -1
- bumble/apps/lea_unicast/app.py +24 -6
- bumble/apps/pair.py +13 -8
- bumble/apps/show.py +6 -6
- bumble/att.py +11 -15
- bumble/controller.py +58 -2
- bumble/device.py +454 -494
- bumble/drivers/common.py +2 -0
- bumble/drivers/intel.py +593 -24
- bumble/gatt.py +33 -7
- bumble/gatt_client.py +3 -3
- bumble/gatt_server.py +14 -3
- bumble/hci.py +139 -56
- bumble/hfp.py +20 -17
- bumble/host.py +5 -2
- bumble/l2cap.py +2 -8
- bumble/pairing.py +3 -0
- bumble/pandora/host.py +1 -1
- bumble/profiles/aics.py +37 -53
- bumble/profiles/bap.py +114 -42
- bumble/profiles/bass.py +4 -7
- bumble/profiles/device_information_service.py +4 -1
- bumble/profiles/heart_rate_service.py +5 -6
- bumble/profiles/vocs.py +330 -0
- bumble/sdp.py +1 -7
- bumble/smp.py +9 -7
- bumble/tools/intel_fw_download.py +130 -0
- bumble/tools/intel_util.py +154 -0
- bumble/transport/usb.py +8 -2
- bumble/utils.py +20 -5
- bumble/vendor/android/hci.py +29 -4
- {bumble-0.0.202.dist-info → bumble-0.0.204.dist-info}/METADATA +15 -15
- {bumble-0.0.202.dist-info → bumble-0.0.204.dist-info}/RECORD +40 -37
- {bumble-0.0.202.dist-info → bumble-0.0.204.dist-info}/WHEEL +1 -1
- {bumble-0.0.202.dist-info → bumble-0.0.204.dist-info}/entry_points.txt +2 -0
- {bumble-0.0.202.dist-info → bumble-0.0.204.dist-info}/LICENSE +0 -0
- {bumble-0.0.202.dist-info → bumble-0.0.204.dist-info}/top_level.txt +0 -0
bumble/gatt.py
CHANGED
|
@@ -28,12 +28,15 @@ import functools
|
|
|
28
28
|
import logging
|
|
29
29
|
import struct
|
|
30
30
|
from typing import (
|
|
31
|
+
Any,
|
|
31
32
|
Callable,
|
|
32
33
|
Dict,
|
|
33
34
|
Iterable,
|
|
34
35
|
List,
|
|
35
36
|
Optional,
|
|
36
37
|
Sequence,
|
|
38
|
+
SupportsBytes,
|
|
39
|
+
Type,
|
|
37
40
|
Union,
|
|
38
41
|
TYPE_CHECKING,
|
|
39
42
|
)
|
|
@@ -41,6 +44,7 @@ from typing import (
|
|
|
41
44
|
from bumble.colors import color
|
|
42
45
|
from bumble.core import BaseBumbleError, UUID
|
|
43
46
|
from bumble.att import Attribute, AttributeValue
|
|
47
|
+
from bumble.utils import ByteSerializable
|
|
44
48
|
|
|
45
49
|
if TYPE_CHECKING:
|
|
46
50
|
from bumble.gatt_client import AttributeProxy
|
|
@@ -343,7 +347,7 @@ class Service(Attribute):
|
|
|
343
347
|
def __init__(
|
|
344
348
|
self,
|
|
345
349
|
uuid: Union[str, UUID],
|
|
346
|
-
characteristics:
|
|
350
|
+
characteristics: Iterable[Characteristic],
|
|
347
351
|
primary=True,
|
|
348
352
|
included_services: Iterable[Service] = (),
|
|
349
353
|
) -> None:
|
|
@@ -362,7 +366,7 @@ class Service(Attribute):
|
|
|
362
366
|
)
|
|
363
367
|
self.uuid = uuid
|
|
364
368
|
self.included_services = list(included_services)
|
|
365
|
-
self.characteristics = characteristics
|
|
369
|
+
self.characteristics = list(characteristics)
|
|
366
370
|
self.primary = primary
|
|
367
371
|
|
|
368
372
|
def get_advertising_data(self) -> Optional[bytes]:
|
|
@@ -393,7 +397,7 @@ class TemplateService(Service):
|
|
|
393
397
|
|
|
394
398
|
def __init__(
|
|
395
399
|
self,
|
|
396
|
-
characteristics:
|
|
400
|
+
characteristics: Iterable[Characteristic],
|
|
397
401
|
primary: bool = True,
|
|
398
402
|
included_services: Iterable[Service] = (),
|
|
399
403
|
) -> None:
|
|
@@ -410,7 +414,7 @@ class IncludedServiceDeclaration(Attribute):
|
|
|
410
414
|
|
|
411
415
|
def __init__(self, service: Service) -> None:
|
|
412
416
|
declaration_bytes = struct.pack(
|
|
413
|
-
'<HH2s', service.handle, service.end_group_handle, service.uuid
|
|
417
|
+
'<HH2s', service.handle, service.end_group_handle, bytes(service.uuid)
|
|
414
418
|
)
|
|
415
419
|
super().__init__(
|
|
416
420
|
GATT_INCLUDE_ATTRIBUTE_TYPE, Attribute.READABLE, declaration_bytes
|
|
@@ -490,7 +494,7 @@ class Characteristic(Attribute):
|
|
|
490
494
|
uuid: Union[str, bytes, UUID],
|
|
491
495
|
properties: Characteristic.Properties,
|
|
492
496
|
permissions: Union[str, Attribute.Permissions],
|
|
493
|
-
value:
|
|
497
|
+
value: Any = b'',
|
|
494
498
|
descriptors: Sequence[Descriptor] = (),
|
|
495
499
|
):
|
|
496
500
|
super().__init__(uuid, permissions, value)
|
|
@@ -525,7 +529,11 @@ class CharacteristicDeclaration(Attribute):
|
|
|
525
529
|
|
|
526
530
|
characteristic: Characteristic
|
|
527
531
|
|
|
528
|
-
def __init__(
|
|
532
|
+
def __init__(
|
|
533
|
+
self,
|
|
534
|
+
characteristic: Characteristic,
|
|
535
|
+
value_handle: int,
|
|
536
|
+
) -> None:
|
|
529
537
|
declaration_bytes = (
|
|
530
538
|
struct.pack('<BH', characteristic.properties, value_handle)
|
|
531
539
|
+ characteristic.uuid.to_pdu_bytes()
|
|
@@ -705,7 +713,7 @@ class MappedCharacteristicAdapter(PackedCharacteristicAdapter):
|
|
|
705
713
|
'''
|
|
706
714
|
Adapter that packs/unpacks characteristic values according to a standard
|
|
707
715
|
Python `struct` format.
|
|
708
|
-
The adapted `read_value` and `write_value` methods return/accept
|
|
716
|
+
The adapted `read_value` and `write_value` methods return/accept a dictionary which
|
|
709
717
|
is packed/unpacked according to format, with the arguments extracted from the
|
|
710
718
|
dictionary by key, in the same order as they occur in the `keys` parameter.
|
|
711
719
|
'''
|
|
@@ -735,6 +743,24 @@ class UTF8CharacteristicAdapter(CharacteristicAdapter):
|
|
|
735
743
|
return value.decode('utf-8')
|
|
736
744
|
|
|
737
745
|
|
|
746
|
+
# -----------------------------------------------------------------------------
|
|
747
|
+
class SerializableCharacteristicAdapter(CharacteristicAdapter):
|
|
748
|
+
'''
|
|
749
|
+
Adapter that converts any class to/from bytes using the class'
|
|
750
|
+
`to_bytes` and `__bytes__` methods, respectively.
|
|
751
|
+
'''
|
|
752
|
+
|
|
753
|
+
def __init__(self, characteristic, cls: Type[ByteSerializable]):
|
|
754
|
+
super().__init__(characteristic)
|
|
755
|
+
self.cls = cls
|
|
756
|
+
|
|
757
|
+
def encode_value(self, value: SupportsBytes) -> bytes:
|
|
758
|
+
return bytes(value)
|
|
759
|
+
|
|
760
|
+
def decode_value(self, value: bytes) -> Any:
|
|
761
|
+
return self.cls.from_bytes(value)
|
|
762
|
+
|
|
763
|
+
|
|
738
764
|
# -----------------------------------------------------------------------------
|
|
739
765
|
class Descriptor(Attribute):
|
|
740
766
|
'''
|
bumble/gatt_client.py
CHANGED
|
@@ -292,7 +292,7 @@ class Client:
|
|
|
292
292
|
logger.debug(
|
|
293
293
|
f'GATT Command from client: [0x{self.connection.handle:04X}] {command}'
|
|
294
294
|
)
|
|
295
|
-
self.send_gatt_pdu(command
|
|
295
|
+
self.send_gatt_pdu(bytes(command))
|
|
296
296
|
|
|
297
297
|
async def send_request(self, request: ATT_PDU):
|
|
298
298
|
logger.debug(
|
|
@@ -310,7 +310,7 @@ class Client:
|
|
|
310
310
|
self.pending_request = request
|
|
311
311
|
|
|
312
312
|
try:
|
|
313
|
-
self.send_gatt_pdu(request
|
|
313
|
+
self.send_gatt_pdu(bytes(request))
|
|
314
314
|
response = await asyncio.wait_for(
|
|
315
315
|
self.pending_response, GATT_REQUEST_TIMEOUT
|
|
316
316
|
)
|
|
@@ -328,7 +328,7 @@ class Client:
|
|
|
328
328
|
f'GATT Confirmation from client: [0x{self.connection.handle:04X}] '
|
|
329
329
|
f'{confirmation}'
|
|
330
330
|
)
|
|
331
|
-
self.send_gatt_pdu(confirmation
|
|
331
|
+
self.send_gatt_pdu(bytes(confirmation))
|
|
332
332
|
|
|
333
333
|
async def request_mtu(self, mtu: int) -> int:
|
|
334
334
|
# Check the range
|
bumble/gatt_server.py
CHANGED
|
@@ -28,7 +28,17 @@ import asyncio
|
|
|
28
28
|
import logging
|
|
29
29
|
from collections import defaultdict
|
|
30
30
|
import struct
|
|
31
|
-
from typing import
|
|
31
|
+
from typing import (
|
|
32
|
+
Dict,
|
|
33
|
+
Iterable,
|
|
34
|
+
List,
|
|
35
|
+
Optional,
|
|
36
|
+
Tuple,
|
|
37
|
+
TypeVar,
|
|
38
|
+
Type,
|
|
39
|
+
Union,
|
|
40
|
+
TYPE_CHECKING,
|
|
41
|
+
)
|
|
32
42
|
from pyee import EventEmitter
|
|
33
43
|
|
|
34
44
|
from bumble.colors import color
|
|
@@ -68,6 +78,7 @@ from bumble.gatt import (
|
|
|
68
78
|
GATT_REQUEST_TIMEOUT,
|
|
69
79
|
GATT_SECONDARY_SERVICE_ATTRIBUTE_TYPE,
|
|
70
80
|
Characteristic,
|
|
81
|
+
CharacteristicAdapter,
|
|
71
82
|
CharacteristicDeclaration,
|
|
72
83
|
CharacteristicValue,
|
|
73
84
|
IncludedServiceDeclaration,
|
|
@@ -353,7 +364,7 @@ class Server(EventEmitter):
|
|
|
353
364
|
logger.debug(
|
|
354
365
|
f'GATT Response from server: [0x{connection.handle:04X}] {response}'
|
|
355
366
|
)
|
|
356
|
-
self.send_gatt_pdu(connection.handle, response
|
|
367
|
+
self.send_gatt_pdu(connection.handle, bytes(response))
|
|
357
368
|
|
|
358
369
|
async def notify_subscriber(
|
|
359
370
|
self,
|
|
@@ -450,7 +461,7 @@ class Server(EventEmitter):
|
|
|
450
461
|
)
|
|
451
462
|
|
|
452
463
|
try:
|
|
453
|
-
self.send_gatt_pdu(connection.handle, indication
|
|
464
|
+
self.send_gatt_pdu(connection.handle, bytes(indication))
|
|
454
465
|
await asyncio.wait_for(pending_confirmation, GATT_REQUEST_TIMEOUT)
|
|
455
466
|
except asyncio.TimeoutError as error:
|
|
456
467
|
logger.warning(color('!!! GATT Indicate timeout', 'red'))
|
bumble/hci.py
CHANGED
|
@@ -915,6 +915,8 @@ HCI_SUPPORTED_COMMANDS_MASKS = {
|
|
|
915
915
|
HCI_READ_CURRENT_IAC_LAP_COMMAND : 1 << (11*8+3),
|
|
916
916
|
HCI_WRITE_CURRENT_IAC_LAP_COMMAND : 1 << (11*8+4),
|
|
917
917
|
HCI_SET_AFH_HOST_CHANNEL_CLASSIFICATION_COMMAND : 1 << (12*8+1),
|
|
918
|
+
HCI_LE_CS_READ_REMOTE_FAE_TABLE_COMMAND : 1 << (12*8+2),
|
|
919
|
+
HCI_LE_CS_WRITE_CACHED_REMOTE_FAE_TABLE_COMMAND : 1 << (12*8+3),
|
|
918
920
|
HCI_READ_INQUIRY_SCAN_TYPE_COMMAND : 1 << (12*8+4),
|
|
919
921
|
HCI_WRITE_INQUIRY_SCAN_TYPE_COMMAND : 1 << (12*8+5),
|
|
920
922
|
HCI_READ_INQUIRY_MODE_COMMAND : 1 << (12*8+6),
|
|
@@ -940,6 +942,8 @@ HCI_SUPPORTED_COMMANDS_MASKS = {
|
|
|
940
942
|
HCI_SETUP_SYNCHRONOUS_CONNECTION_COMMAND : 1 << (16*8+3),
|
|
941
943
|
HCI_ACCEPT_SYNCHRONOUS_CONNECTION_REQUEST_COMMAND : 1 << (16*8+4),
|
|
942
944
|
HCI_REJECT_SYNCHRONOUS_CONNECTION_REQUEST_COMMAND : 1 << (16*8+5),
|
|
945
|
+
HCI_LE_CS_CREATE_CONFIG_COMMAND : 1 << (16*8+6),
|
|
946
|
+
HCI_LE_CS_REMOVE_CONFIG_COMMAND : 1 << (16*8+7),
|
|
943
947
|
HCI_READ_EXTENDED_INQUIRY_RESPONSE_COMMAND : 1 << (17*8+0),
|
|
944
948
|
HCI_WRITE_EXTENDED_INQUIRY_RESPONSE_COMMAND : 1 << (17*8+1),
|
|
945
949
|
HCI_REFRESH_ENCRYPTION_KEY_COMMAND : 1 << (17*8+2),
|
|
@@ -963,13 +967,20 @@ HCI_SUPPORTED_COMMANDS_MASKS = {
|
|
|
963
967
|
HCI_SEND_KEYPRESS_NOTIFICATION_COMMAND : 1 << (20*8+2),
|
|
964
968
|
HCI_IO_CAPABILITY_REQUEST_NEGATIVE_REPLY_COMMAND : 1 << (20*8+3),
|
|
965
969
|
HCI_READ_ENCRYPTION_KEY_SIZE_COMMAND : 1 << (20*8+4),
|
|
970
|
+
HCI_LE_CS_READ_LOCAL_SUPPORTED_CAPABILITIES_COMMAND : 1 << (20*8+5),
|
|
971
|
+
HCI_LE_CS_READ_REMOTE_SUPPORTED_CAPABILITIES_COMMAND : 1 << (20*8+6),
|
|
972
|
+
HCI_LE_CS_WRITE_CACHED_REMOTE_SUPPORTED_CAPABILITIES : 1 << (20*8+7),
|
|
966
973
|
HCI_SET_EVENT_MASK_PAGE_2_COMMAND : 1 << (22*8+2),
|
|
967
974
|
HCI_READ_FLOW_CONTROL_MODE_COMMAND : 1 << (23*8+0),
|
|
968
975
|
HCI_WRITE_FLOW_CONTROL_MODE_COMMAND : 1 << (23*8+1),
|
|
969
976
|
HCI_READ_DATA_BLOCK_SIZE_COMMAND : 1 << (23*8+2),
|
|
977
|
+
HCI_LE_CS_TEST_COMMAND : 1 << (23*8+3),
|
|
978
|
+
HCI_LE_CS_TEST_END_COMMAND : 1 << (23*8+4),
|
|
970
979
|
HCI_READ_ENHANCED_TRANSMIT_POWER_LEVEL_COMMAND : 1 << (24*8+0),
|
|
980
|
+
HCI_LE_CS_SECURITY_ENABLE_COMMAND : 1 << (24*8+1),
|
|
971
981
|
HCI_READ_LE_HOST_SUPPORT_COMMAND : 1 << (24*8+5),
|
|
972
982
|
HCI_WRITE_LE_HOST_SUPPORT_COMMAND : 1 << (24*8+6),
|
|
983
|
+
HCI_LE_CS_SET_DEFAULT_SETTINGS_COMMAND : 1 << (24*8+7),
|
|
973
984
|
HCI_LE_SET_EVENT_MASK_COMMAND : 1 << (25*8+0),
|
|
974
985
|
HCI_LE_READ_BUFFER_SIZE_COMMAND : 1 << (25*8+1),
|
|
975
986
|
HCI_LE_READ_LOCAL_SUPPORTED_FEATURES_COMMAND : 1 << (25*8+2),
|
|
@@ -1000,6 +1011,10 @@ HCI_SUPPORTED_COMMANDS_MASKS = {
|
|
|
1000
1011
|
HCI_LE_RECEIVER_TEST_COMMAND : 1 << (28*8+4),
|
|
1001
1012
|
HCI_LE_TRANSMITTER_TEST_COMMAND : 1 << (28*8+5),
|
|
1002
1013
|
HCI_LE_TEST_END_COMMAND : 1 << (28*8+6),
|
|
1014
|
+
HCI_LE_ENABLE_MONITORING_ADVERTISERS_COMMAND : 1 << (28*8+7),
|
|
1015
|
+
HCI_LE_CS_SET_CHANNEL_CLASSIFICATION_COMMAND : 1 << (29*8+0),
|
|
1016
|
+
HCI_LE_CS_SET_PROCEDURE_PARAMETERS_COMMAND : 1 << (29*8+1),
|
|
1017
|
+
HCI_LE_CS_PROCEDURE_ENABLE_COMMAND : 1 << (29*8+2),
|
|
1003
1018
|
HCI_ENHANCED_SETUP_SYNCHRONOUS_CONNECTION_COMMAND : 1 << (29*8+3),
|
|
1004
1019
|
HCI_ENHANCED_ACCEPT_SYNCHRONOUS_CONNECTION_REQUEST_COMMAND : 1 << (29*8+4),
|
|
1005
1020
|
HCI_READ_LOCAL_SUPPORTED_CODECS_COMMAND : 1 << (29*8+5),
|
|
@@ -1136,11 +1151,21 @@ HCI_SUPPORTED_COMMANDS_MASKS = {
|
|
|
1136
1151
|
HCI_LE_SET_DEFAULT_SUBRATE_COMMAND : 1 << (46*8+0),
|
|
1137
1152
|
HCI_LE_SUBRATE_REQUEST_COMMAND : 1 << (46*8+1),
|
|
1138
1153
|
HCI_LE_SET_EXTENDED_ADVERTISING_PARAMETERS_V2_COMMAND : 1 << (46*8+2),
|
|
1154
|
+
HCI_LE_SET_DECISION_DATA_COMMAND : 1 << (46*8+3),
|
|
1155
|
+
HCI_LE_SET_DECISION_INSTRUCTIONS_COMMAND : 1 << (46*8+4),
|
|
1139
1156
|
HCI_LE_SET_PERIODIC_ADVERTISING_SUBEVENT_DATA_COMMAND : 1 << (46*8+5),
|
|
1140
1157
|
HCI_LE_SET_PERIODIC_ADVERTISING_RESPONSE_DATA_COMMAND : 1 << (46*8+6),
|
|
1141
1158
|
HCI_LE_SET_PERIODIC_SYNC_SUBEVENT_COMMAND : 1 << (46*8+7),
|
|
1142
1159
|
HCI_LE_EXTENDED_CREATE_CONNECTION_V2_COMMAND : 1 << (47*8+0),
|
|
1143
1160
|
HCI_LE_SET_PERIODIC_ADVERTISING_PARAMETERS_V2_COMMAND : 1 << (47*8+1),
|
|
1161
|
+
HCI_LE_READ_ALL_LOCAL_SUPPORTED_FEATURES_COMMAND : 1 << (47*8+2),
|
|
1162
|
+
HCI_LE_READ_ALL_REMOTE_FEATURES_COMMAND : 1 << (47*8+3),
|
|
1163
|
+
HCI_LE_SET_HOST_FEATURE_V2_COMMAND : 1 << (47*8+4),
|
|
1164
|
+
HCI_LE_ADD_DEVICE_TO_MONITORED_ADVERTISERS_LIST_COMMAND : 1 << (47*8+5),
|
|
1165
|
+
HCI_LE_REMOVE_DEVICE_FROM_MONITORED_ADVERTISERS_LIST_COMMAND : 1 << (47*8+6),
|
|
1166
|
+
HCI_LE_CLEAR_MONITORED_ADVERTISERS_LIST_COMMAND : 1 << (47*8+7),
|
|
1167
|
+
HCI_LE_READ_MONITORED_ADVERTISERS_LIST_SIZE_COMMAND : 1 << (48*8+0),
|
|
1168
|
+
HCI_LE_FRAME_SPACE_UPDATE_COMMAND : 1 << (48*8+1),
|
|
1144
1169
|
}
|
|
1145
1170
|
|
|
1146
1171
|
# LE Supported Features
|
|
@@ -1457,7 +1482,7 @@ class CodingFormat:
|
|
|
1457
1482
|
vendor_specific_codec_id: int = 0
|
|
1458
1483
|
|
|
1459
1484
|
@classmethod
|
|
1460
|
-
def parse_from_bytes(cls, data: bytes, offset: int):
|
|
1485
|
+
def parse_from_bytes(cls, data: bytes, offset: int) -> tuple[int, CodingFormat]:
|
|
1461
1486
|
(codec_id, company_id, vendor_specific_codec_id) = struct.unpack_from(
|
|
1462
1487
|
'<BHH', data, offset
|
|
1463
1488
|
)
|
|
@@ -1467,14 +1492,15 @@ class CodingFormat:
|
|
|
1467
1492
|
vendor_specific_codec_id=vendor_specific_codec_id,
|
|
1468
1493
|
)
|
|
1469
1494
|
|
|
1470
|
-
|
|
1495
|
+
@classmethod
|
|
1496
|
+
def from_bytes(cls, data: bytes) -> CodingFormat:
|
|
1497
|
+
return cls.parse_from_bytes(data, 0)[1]
|
|
1498
|
+
|
|
1499
|
+
def __bytes__(self) -> bytes:
|
|
1471
1500
|
return struct.pack(
|
|
1472
1501
|
'<BHH', self.codec_id, self.company_id, self.vendor_specific_codec_id
|
|
1473
1502
|
)
|
|
1474
1503
|
|
|
1475
|
-
def __bytes__(self) -> bytes:
|
|
1476
|
-
return self.to_bytes()
|
|
1477
|
-
|
|
1478
1504
|
|
|
1479
1505
|
# -----------------------------------------------------------------------------
|
|
1480
1506
|
class HCI_Constant:
|
|
@@ -1691,7 +1717,7 @@ class HCI_Object:
|
|
|
1691
1717
|
field_length = len(field_bytes)
|
|
1692
1718
|
field_bytes = bytes([field_length]) + field_bytes
|
|
1693
1719
|
elif isinstance(field_value, (bytes, bytearray)) or hasattr(
|
|
1694
|
-
field_value, '
|
|
1720
|
+
field_value, '__bytes__'
|
|
1695
1721
|
):
|
|
1696
1722
|
field_bytes = bytes(field_value)
|
|
1697
1723
|
if isinstance(field_type, int) and 4 < field_type <= 256:
|
|
@@ -1736,7 +1762,7 @@ class HCI_Object:
|
|
|
1736
1762
|
def from_bytes(cls, data, offset, fields):
|
|
1737
1763
|
return cls(fields, **cls.dict_from_bytes(data, offset, fields))
|
|
1738
1764
|
|
|
1739
|
-
def
|
|
1765
|
+
def __bytes__(self):
|
|
1740
1766
|
return HCI_Object.dict_to_bytes(self.__dict__, self.fields)
|
|
1741
1767
|
|
|
1742
1768
|
@staticmethod
|
|
@@ -1831,9 +1857,6 @@ class HCI_Object:
|
|
|
1831
1857
|
for field_name, field_value in field_strings
|
|
1832
1858
|
)
|
|
1833
1859
|
|
|
1834
|
-
def __bytes__(self):
|
|
1835
|
-
return self.to_bytes()
|
|
1836
|
-
|
|
1837
1860
|
def __init__(self, fields, **kwargs):
|
|
1838
1861
|
self.fields = fields
|
|
1839
1862
|
self.init_from_fields(self, fields, kwargs)
|
|
@@ -2008,9 +2031,6 @@ class Address:
|
|
|
2008
2031
|
def is_static(self):
|
|
2009
2032
|
return self.is_random and (self.address_bytes[5] >> 6 == 3)
|
|
2010
2033
|
|
|
2011
|
-
def to_bytes(self):
|
|
2012
|
-
return self.address_bytes
|
|
2013
|
-
|
|
2014
2034
|
def to_string(self, with_type_qualifier=True):
|
|
2015
2035
|
'''
|
|
2016
2036
|
String representation of the address, MSB first, with an optional type
|
|
@@ -2022,7 +2042,7 @@ class Address:
|
|
|
2022
2042
|
return result + '/P'
|
|
2023
2043
|
|
|
2024
2044
|
def __bytes__(self):
|
|
2025
|
-
return self.
|
|
2045
|
+
return self.address_bytes
|
|
2026
2046
|
|
|
2027
2047
|
def __hash__(self):
|
|
2028
2048
|
return hash(self.address_bytes)
|
|
@@ -2228,16 +2248,13 @@ class HCI_Command(HCI_Packet):
|
|
|
2228
2248
|
self.op_code = op_code
|
|
2229
2249
|
self.parameters = parameters
|
|
2230
2250
|
|
|
2231
|
-
def
|
|
2251
|
+
def __bytes__(self):
|
|
2232
2252
|
parameters = b'' if self.parameters is None else self.parameters
|
|
2233
2253
|
return (
|
|
2234
2254
|
struct.pack('<BHB', HCI_COMMAND_PACKET, self.op_code, len(parameters))
|
|
2235
2255
|
+ parameters
|
|
2236
2256
|
)
|
|
2237
2257
|
|
|
2238
|
-
def __bytes__(self):
|
|
2239
|
-
return self.to_bytes()
|
|
2240
|
-
|
|
2241
2258
|
def __str__(self):
|
|
2242
2259
|
result = color(self.name, 'green')
|
|
2243
2260
|
if fields := getattr(self, 'fields', None):
|
|
@@ -4302,6 +4319,61 @@ class HCI_LE_Clear_Advertising_Sets_Command(HCI_Command):
|
|
|
4302
4319
|
'''
|
|
4303
4320
|
|
|
4304
4321
|
|
|
4322
|
+
# -----------------------------------------------------------------------------
|
|
4323
|
+
@HCI_Command.command(
|
|
4324
|
+
[
|
|
4325
|
+
('advertising_handle', 1),
|
|
4326
|
+
('periodic_advertising_interval_min', 2),
|
|
4327
|
+
('periodic_advertising_interval_max', 2),
|
|
4328
|
+
('periodic_advertising_properties', 2),
|
|
4329
|
+
]
|
|
4330
|
+
)
|
|
4331
|
+
class HCI_LE_Set_Periodic_Advertising_Parameters_Command(HCI_Command):
|
|
4332
|
+
'''
|
|
4333
|
+
See Bluetooth spec @ 7.8.61 LE Set Periodic Advertising Parameters command
|
|
4334
|
+
'''
|
|
4335
|
+
|
|
4336
|
+
class Properties(enum.IntFlag):
|
|
4337
|
+
INCLUDE_TX_POWER = 1 << 6
|
|
4338
|
+
|
|
4339
|
+
advertising_handle: int
|
|
4340
|
+
periodic_advertising_interval_min: int
|
|
4341
|
+
periodic_advertising_interval_max: int
|
|
4342
|
+
periodic_advertising_properties: int
|
|
4343
|
+
|
|
4344
|
+
|
|
4345
|
+
# -----------------------------------------------------------------------------
|
|
4346
|
+
@HCI_Command.command(
|
|
4347
|
+
[
|
|
4348
|
+
('advertising_handle', 1),
|
|
4349
|
+
(
|
|
4350
|
+
'operation',
|
|
4351
|
+
{
|
|
4352
|
+
'size': 1,
|
|
4353
|
+
'mapper': lambda x: HCI_LE_Set_Extended_Advertising_Data_Command.Operation(
|
|
4354
|
+
x
|
|
4355
|
+
).name,
|
|
4356
|
+
},
|
|
4357
|
+
),
|
|
4358
|
+
(
|
|
4359
|
+
'advertising_data',
|
|
4360
|
+
{
|
|
4361
|
+
'parser': HCI_Object.parse_length_prefixed_bytes,
|
|
4362
|
+
'serializer': HCI_Object.serialize_length_prefixed_bytes,
|
|
4363
|
+
},
|
|
4364
|
+
),
|
|
4365
|
+
]
|
|
4366
|
+
)
|
|
4367
|
+
class HCI_LE_Set_Periodic_Advertising_Data_Command(HCI_Command):
|
|
4368
|
+
'''
|
|
4369
|
+
See Bluetooth spec @ 7.8.62 LE Set Periodic Advertising Data command
|
|
4370
|
+
'''
|
|
4371
|
+
|
|
4372
|
+
advertising_handle: int
|
|
4373
|
+
operation: int
|
|
4374
|
+
advertising_data: bytes
|
|
4375
|
+
|
|
4376
|
+
|
|
4305
4377
|
# -----------------------------------------------------------------------------
|
|
4306
4378
|
@HCI_Command.command([('enable', 1), ('advertising_handle', 1)])
|
|
4307
4379
|
class HCI_LE_Set_Periodic_Advertising_Enable_Command(HCI_Command):
|
|
@@ -4996,6 +5068,7 @@ class HCI_Event(HCI_Packet):
|
|
|
4996
5068
|
hci_packet_type = HCI_EVENT_PACKET
|
|
4997
5069
|
event_names: Dict[int, str] = {}
|
|
4998
5070
|
event_classes: Dict[int, Type[HCI_Event]] = {}
|
|
5071
|
+
vendor_factory: Optional[Callable[[bytes], Optional[HCI_Event]]] = None
|
|
4999
5072
|
|
|
5000
5073
|
@staticmethod
|
|
5001
5074
|
def event(fields=()):
|
|
@@ -5053,37 +5126,41 @@ class HCI_Event(HCI_Packet):
|
|
|
5053
5126
|
|
|
5054
5127
|
return event_class
|
|
5055
5128
|
|
|
5056
|
-
@
|
|
5057
|
-
def from_bytes(packet: bytes) -> HCI_Event:
|
|
5129
|
+
@classmethod
|
|
5130
|
+
def from_bytes(cls, packet: bytes) -> HCI_Event:
|
|
5058
5131
|
event_code = packet[1]
|
|
5059
5132
|
length = packet[2]
|
|
5060
5133
|
parameters = packet[3:]
|
|
5061
5134
|
if len(parameters) != length:
|
|
5062
5135
|
raise InvalidPacketError('invalid packet length')
|
|
5063
5136
|
|
|
5064
|
-
|
|
5137
|
+
subclass: Any
|
|
5065
5138
|
if event_code == HCI_LE_META_EVENT:
|
|
5066
5139
|
# We do this dispatch here and not in the subclass in order to avoid call
|
|
5067
5140
|
# loops
|
|
5068
5141
|
subevent_code = parameters[0]
|
|
5069
|
-
|
|
5070
|
-
if
|
|
5142
|
+
subclass = HCI_LE_Meta_Event.subevent_classes.get(subevent_code)
|
|
5143
|
+
if subclass is None:
|
|
5071
5144
|
# No class registered, just use a generic class instance
|
|
5072
5145
|
return HCI_LE_Meta_Event(subevent_code, parameters)
|
|
5073
5146
|
elif event_code == HCI_VENDOR_EVENT:
|
|
5074
|
-
|
|
5075
|
-
|
|
5076
|
-
if cls
|
|
5077
|
-
|
|
5078
|
-
|
|
5147
|
+
# Invoke all the registered factories to see if any of them can handle
|
|
5148
|
+
# the event
|
|
5149
|
+
if cls.vendor_factory:
|
|
5150
|
+
if event := cls.vendor_factory(parameters):
|
|
5151
|
+
return event
|
|
5152
|
+
|
|
5153
|
+
# No factory, or the factory could not create an instance,
|
|
5154
|
+
# return a generic vendor event
|
|
5155
|
+
return HCI_Event(event_code, parameters)
|
|
5079
5156
|
else:
|
|
5080
|
-
|
|
5081
|
-
if
|
|
5157
|
+
subclass = HCI_Event.event_classes.get(event_code)
|
|
5158
|
+
if subclass is None:
|
|
5082
5159
|
# No class registered, just use a generic class instance
|
|
5083
5160
|
return HCI_Event(event_code, parameters)
|
|
5084
5161
|
|
|
5085
5162
|
# Invoke the factory to create a new instance
|
|
5086
|
-
return
|
|
5163
|
+
return subclass.from_parameters(parameters) # type: ignore
|
|
5087
5164
|
|
|
5088
5165
|
@classmethod
|
|
5089
5166
|
def from_parameters(cls, parameters):
|
|
@@ -5106,13 +5183,10 @@ class HCI_Event(HCI_Packet):
|
|
|
5106
5183
|
self.event_code = event_code
|
|
5107
5184
|
self.parameters = parameters
|
|
5108
5185
|
|
|
5109
|
-
def
|
|
5186
|
+
def __bytes__(self):
|
|
5110
5187
|
parameters = b'' if self.parameters is None else self.parameters
|
|
5111
5188
|
return bytes([HCI_EVENT_PACKET, self.event_code, len(parameters)]) + parameters
|
|
5112
5189
|
|
|
5113
|
-
def __bytes__(self):
|
|
5114
|
-
return self.to_bytes()
|
|
5115
|
-
|
|
5116
5190
|
def __str__(self):
|
|
5117
5191
|
result = color(self.name, 'magenta')
|
|
5118
5192
|
if fields := getattr(self, 'fields', None):
|
|
@@ -5129,11 +5203,11 @@ HCI_Event.register_events(globals())
|
|
|
5129
5203
|
# -----------------------------------------------------------------------------
|
|
5130
5204
|
class HCI_Extended_Event(HCI_Event):
|
|
5131
5205
|
'''
|
|
5132
|
-
HCI_Event subclass for events that
|
|
5206
|
+
HCI_Event subclass for events that have a subevent code.
|
|
5133
5207
|
'''
|
|
5134
5208
|
|
|
5135
5209
|
subevent_names: Dict[int, str] = {}
|
|
5136
|
-
subevent_classes: Dict[int, Type[HCI_Extended_Event]]
|
|
5210
|
+
subevent_classes: Dict[int, Type[HCI_Extended_Event]] = {}
|
|
5137
5211
|
|
|
5138
5212
|
@classmethod
|
|
5139
5213
|
def event(cls, fields=()):
|
|
@@ -5184,7 +5258,22 @@ class HCI_Extended_Event(HCI_Event):
|
|
|
5184
5258
|
cls.subevent_names.update(cls.subevent_map(symbols))
|
|
5185
5259
|
|
|
5186
5260
|
@classmethod
|
|
5187
|
-
def
|
|
5261
|
+
def subclass_from_parameters(
|
|
5262
|
+
cls, parameters: bytes
|
|
5263
|
+
) -> Optional[HCI_Extended_Event]:
|
|
5264
|
+
"""
|
|
5265
|
+
Factory method that parses the subevent code, finds a registered subclass,
|
|
5266
|
+
and creates an instance if found.
|
|
5267
|
+
"""
|
|
5268
|
+
subevent_code = parameters[0]
|
|
5269
|
+
if subclass := cls.subevent_classes.get(subevent_code):
|
|
5270
|
+
return subclass.from_parameters(parameters)
|
|
5271
|
+
|
|
5272
|
+
return None
|
|
5273
|
+
|
|
5274
|
+
@classmethod
|
|
5275
|
+
def from_parameters(cls, parameters: bytes) -> HCI_Extended_Event:
|
|
5276
|
+
"""Factory method for subclasses (the subevent code has already been parsed)"""
|
|
5188
5277
|
self = cls.__new__(cls)
|
|
5189
5278
|
HCI_Extended_Event.__init__(self, self.subevent_code, parameters)
|
|
5190
5279
|
if fields := getattr(self, 'fields', None):
|
|
@@ -5225,12 +5314,6 @@ class HCI_LE_Meta_Event(HCI_Extended_Event):
|
|
|
5225
5314
|
HCI_LE_Meta_Event.register_subevents(globals())
|
|
5226
5315
|
|
|
5227
5316
|
|
|
5228
|
-
# -----------------------------------------------------------------------------
|
|
5229
|
-
class HCI_Vendor_Event(HCI_Extended_Event):
|
|
5230
|
-
event_code: int = HCI_VENDOR_EVENT
|
|
5231
|
-
subevent_classes = {}
|
|
5232
|
-
|
|
5233
|
-
|
|
5234
5317
|
# -----------------------------------------------------------------------------
|
|
5235
5318
|
@HCI_LE_Meta_Event.event(
|
|
5236
5319
|
[
|
|
@@ -6104,8 +6187,9 @@ class HCI_Command_Complete_Event(HCI_Event):
|
|
|
6104
6187
|
See Bluetooth spec @ 7.7.14 Command Complete Event
|
|
6105
6188
|
'''
|
|
6106
6189
|
|
|
6107
|
-
|
|
6190
|
+
num_hci_command_packets: int
|
|
6108
6191
|
command_opcode: int
|
|
6192
|
+
return_parameters = b''
|
|
6109
6193
|
|
|
6110
6194
|
def map_return_parameters(self, return_parameters):
|
|
6111
6195
|
'''Map simple 'status' return parameters to their named constant form'''
|
|
@@ -6641,6 +6725,14 @@ class HCI_Remote_Host_Supported_Features_Notification_Event(HCI_Event):
|
|
|
6641
6725
|
'''
|
|
6642
6726
|
|
|
6643
6727
|
|
|
6728
|
+
# -----------------------------------------------------------------------------
|
|
6729
|
+
@HCI_Event.event([('data', "*")])
|
|
6730
|
+
class HCI_Vendor_Event(HCI_Event):
|
|
6731
|
+
'''
|
|
6732
|
+
See Bluetooth spec @ 5.4.4 HCI Event packet
|
|
6733
|
+
'''
|
|
6734
|
+
|
|
6735
|
+
|
|
6644
6736
|
# -----------------------------------------------------------------------------
|
|
6645
6737
|
class HCI_AclDataPacket(HCI_Packet):
|
|
6646
6738
|
'''
|
|
@@ -6663,7 +6755,7 @@ class HCI_AclDataPacket(HCI_Packet):
|
|
|
6663
6755
|
connection_handle, pb_flag, bc_flag, data_total_length, data
|
|
6664
6756
|
)
|
|
6665
6757
|
|
|
6666
|
-
def
|
|
6758
|
+
def __bytes__(self):
|
|
6667
6759
|
h = (self.pb_flag << 12) | (self.bc_flag << 14) | self.connection_handle
|
|
6668
6760
|
return (
|
|
6669
6761
|
struct.pack('<BHH', HCI_ACL_DATA_PACKET, h, self.data_total_length)
|
|
@@ -6677,9 +6769,6 @@ class HCI_AclDataPacket(HCI_Packet):
|
|
|
6677
6769
|
self.data_total_length = data_total_length
|
|
6678
6770
|
self.data = data
|
|
6679
6771
|
|
|
6680
|
-
def __bytes__(self):
|
|
6681
|
-
return self.to_bytes()
|
|
6682
|
-
|
|
6683
6772
|
def __str__(self):
|
|
6684
6773
|
return (
|
|
6685
6774
|
f'{color("ACL", "blue")}: '
|
|
@@ -6713,7 +6802,7 @@ class HCI_SynchronousDataPacket(HCI_Packet):
|
|
|
6713
6802
|
connection_handle, packet_status, data_total_length, data
|
|
6714
6803
|
)
|
|
6715
6804
|
|
|
6716
|
-
def
|
|
6805
|
+
def __bytes__(self) -> bytes:
|
|
6717
6806
|
h = (self.packet_status << 12) | self.connection_handle
|
|
6718
6807
|
return (
|
|
6719
6808
|
struct.pack('<BHB', HCI_SYNCHRONOUS_DATA_PACKET, h, self.data_total_length)
|
|
@@ -6732,9 +6821,6 @@ class HCI_SynchronousDataPacket(HCI_Packet):
|
|
|
6732
6821
|
self.data_total_length = data_total_length
|
|
6733
6822
|
self.data = data
|
|
6734
6823
|
|
|
6735
|
-
def __bytes__(self) -> bytes:
|
|
6736
|
-
return self.to_bytes()
|
|
6737
|
-
|
|
6738
6824
|
def __str__(self) -> str:
|
|
6739
6825
|
return (
|
|
6740
6826
|
f'{color("SCO", "blue")}: '
|
|
@@ -6807,9 +6893,6 @@ class HCI_IsoDataPacket(HCI_Packet):
|
|
|
6807
6893
|
)
|
|
6808
6894
|
|
|
6809
6895
|
def __bytes__(self) -> bytes:
|
|
6810
|
-
return self.to_bytes()
|
|
6811
|
-
|
|
6812
|
-
def to_bytes(self) -> bytes:
|
|
6813
6896
|
fmt = '<BHH'
|
|
6814
6897
|
args = [
|
|
6815
6898
|
HCI_ISO_DATA_PACKET,
|