bumble 0.0.211__py3-none-any.whl → 0.0.213__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/a2dp.py +6 -0
- bumble/apps/README.md +0 -3
- bumble/apps/auracast.py +11 -9
- bumble/apps/bench.py +482 -31
- bumble/apps/console.py +5 -5
- bumble/apps/controller_info.py +47 -10
- bumble/apps/controller_loopback.py +7 -3
- bumble/apps/controllers.py +2 -2
- bumble/apps/device_info.py +2 -2
- bumble/apps/gatt_dump.py +2 -2
- bumble/apps/gg_bridge.py +2 -2
- bumble/apps/hci_bridge.py +2 -2
- bumble/apps/l2cap_bridge.py +2 -2
- bumble/apps/lea_unicast/app.py +6 -1
- bumble/apps/pair.py +204 -43
- bumble/apps/pandora_server.py +2 -2
- bumble/apps/rfcomm_bridge.py +1 -1
- bumble/apps/scan.py +2 -2
- bumble/apps/show.py +4 -2
- bumble/apps/speaker/speaker.html +1 -0
- bumble/apps/speaker/speaker.js +113 -62
- bumble/apps/speaker/speaker.py +126 -18
- bumble/at.py +4 -4
- bumble/att.py +15 -18
- bumble/avc.py +7 -7
- bumble/avctp.py +5 -5
- bumble/avdtp.py +138 -88
- bumble/avrcp.py +52 -58
- bumble/colors.py +2 -2
- bumble/controller.py +84 -23
- bumble/core.py +13 -7
- bumble/{crypto.py → crypto/__init__.py} +11 -95
- bumble/crypto/builtin.py +652 -0
- bumble/crypto/cryptography.py +84 -0
- bumble/device.py +688 -345
- bumble/drivers/__init__.py +2 -2
- bumble/drivers/common.py +0 -2
- bumble/drivers/intel.py +40 -40
- bumble/drivers/rtk.py +28 -35
- bumble/gatt.py +7 -9
- bumble/gatt_adapters.py +4 -5
- bumble/gatt_client.py +31 -34
- bumble/gatt_server.py +15 -17
- bumble/hci.py +2635 -2878
- bumble/helpers.py +4 -5
- bumble/hfp.py +76 -57
- bumble/hid.py +24 -12
- bumble/host.py +117 -34
- bumble/keys.py +68 -52
- bumble/l2cap.py +329 -403
- bumble/link.py +6 -270
- bumble/pairing.py +23 -20
- bumble/pandora/__init__.py +1 -1
- bumble/pandora/config.py +2 -2
- bumble/pandora/device.py +6 -6
- bumble/pandora/host.py +38 -39
- bumble/pandora/l2cap.py +4 -4
- bumble/pandora/security.py +73 -57
- bumble/pandora/utils.py +3 -3
- bumble/profiles/aics.py +3 -5
- bumble/profiles/ancs.py +3 -1
- bumble/profiles/ascs.py +143 -136
- bumble/profiles/asha.py +13 -8
- bumble/profiles/bap.py +3 -4
- bumble/profiles/csip.py +3 -5
- bumble/profiles/device_information_service.py +2 -2
- bumble/profiles/gap.py +2 -2
- bumble/profiles/gatt_service.py +1 -3
- bumble/profiles/hap.py +42 -58
- bumble/profiles/le_audio.py +4 -4
- bumble/profiles/mcp.py +16 -13
- bumble/profiles/vcs.py +8 -10
- bumble/profiles/vocs.py +6 -9
- bumble/rfcomm.py +27 -18
- bumble/rtp.py +1 -2
- bumble/sdp.py +2 -2
- bumble/smp.py +71 -69
- bumble/tools/rtk_util.py +2 -2
- bumble/transport/__init__.py +2 -16
- bumble/transport/android_netsim.py +5 -5
- bumble/transport/common.py +4 -4
- bumble/transport/pyusb.py +2 -2
- bumble/utils.py +2 -5
- bumble/vendor/android/hci.py +118 -200
- bumble/vendor/zephyr/hci.py +32 -27
- {bumble-0.0.211.dist-info → bumble-0.0.213.dist-info}/METADATA +5 -5
- {bumble-0.0.211.dist-info → bumble-0.0.213.dist-info}/RECORD +92 -93
- {bumble-0.0.211.dist-info → bumble-0.0.213.dist-info}/WHEEL +1 -1
- {bumble-0.0.211.dist-info → bumble-0.0.213.dist-info}/entry_points.txt +0 -1
- bumble/apps/link_relay/__init__.py +0 -0
- bumble/apps/link_relay/link_relay.py +0 -289
- bumble/apps/link_relay/logging.yml +0 -21
- {bumble-0.0.211.dist-info → bumble-0.0.213.dist-info}/licenses/LICENSE +0 -0
- {bumble-0.0.211.dist-info → bumble-0.0.213.dist-info}/top_level.txt +0 -0
bumble/avrcp.py
CHANGED
|
@@ -26,14 +26,11 @@ from typing import (
|
|
|
26
26
|
Awaitable,
|
|
27
27
|
Callable,
|
|
28
28
|
cast,
|
|
29
|
-
Dict,
|
|
30
29
|
Iterable,
|
|
31
30
|
List,
|
|
32
31
|
Optional,
|
|
33
32
|
Sequence,
|
|
34
33
|
SupportsBytes,
|
|
35
|
-
Tuple,
|
|
36
|
-
Type,
|
|
37
34
|
TypeVar,
|
|
38
35
|
Union,
|
|
39
36
|
)
|
|
@@ -53,19 +50,10 @@ from bumble.sdp import (
|
|
|
53
50
|
ServiceAttribute,
|
|
54
51
|
)
|
|
55
52
|
from bumble import utils
|
|
56
|
-
from bumble
|
|
57
|
-
InvalidArgumentError,
|
|
58
|
-
ProtocolError,
|
|
59
|
-
BT_L2CAP_PROTOCOL_ID,
|
|
60
|
-
BT_AVCTP_PROTOCOL_ID,
|
|
61
|
-
BT_AV_REMOTE_CONTROL_SERVICE,
|
|
62
|
-
BT_AV_REMOTE_CONTROL_CONTROLLER_SERVICE,
|
|
63
|
-
BT_AV_REMOTE_CONTROL_TARGET_SERVICE,
|
|
64
|
-
)
|
|
53
|
+
from bumble import core
|
|
65
54
|
from bumble import l2cap
|
|
66
55
|
from bumble import avc
|
|
67
56
|
from bumble import avctp
|
|
68
|
-
from bumble import utils
|
|
69
57
|
|
|
70
58
|
|
|
71
59
|
# -----------------------------------------------------------------------------
|
|
@@ -84,10 +72,10 @@ AVRCP_BLUETOOTH_SIG_COMPANY_ID = 0x001958
|
|
|
84
72
|
# -----------------------------------------------------------------------------
|
|
85
73
|
def make_controller_service_sdp_records(
|
|
86
74
|
service_record_handle: int,
|
|
87
|
-
avctp_version:
|
|
88
|
-
avrcp_version:
|
|
75
|
+
avctp_version: tuple[int, int] = (1, 4),
|
|
76
|
+
avrcp_version: tuple[int, int] = (1, 6),
|
|
89
77
|
supported_features: int = 1,
|
|
90
|
-
) ->
|
|
78
|
+
) -> list[ServiceAttribute]:
|
|
91
79
|
# TODO: support a way to compute the supported features from a feature list
|
|
92
80
|
avctp_version_int = avctp_version[0] << 8 | avctp_version[1]
|
|
93
81
|
avrcp_version_int = avrcp_version[0] << 8 | avrcp_version[1]
|
|
@@ -105,8 +93,8 @@ def make_controller_service_sdp_records(
|
|
|
105
93
|
SDP_SERVICE_CLASS_ID_LIST_ATTRIBUTE_ID,
|
|
106
94
|
DataElement.sequence(
|
|
107
95
|
[
|
|
108
|
-
DataElement.uuid(BT_AV_REMOTE_CONTROL_SERVICE),
|
|
109
|
-
DataElement.uuid(BT_AV_REMOTE_CONTROL_CONTROLLER_SERVICE),
|
|
96
|
+
DataElement.uuid(core.BT_AV_REMOTE_CONTROL_SERVICE),
|
|
97
|
+
DataElement.uuid(core.BT_AV_REMOTE_CONTROL_CONTROLLER_SERVICE),
|
|
110
98
|
]
|
|
111
99
|
),
|
|
112
100
|
),
|
|
@@ -116,13 +104,13 @@ def make_controller_service_sdp_records(
|
|
|
116
104
|
[
|
|
117
105
|
DataElement.sequence(
|
|
118
106
|
[
|
|
119
|
-
DataElement.uuid(BT_L2CAP_PROTOCOL_ID),
|
|
107
|
+
DataElement.uuid(core.BT_L2CAP_PROTOCOL_ID),
|
|
120
108
|
DataElement.unsigned_integer_16(avctp.AVCTP_PSM),
|
|
121
109
|
]
|
|
122
110
|
),
|
|
123
111
|
DataElement.sequence(
|
|
124
112
|
[
|
|
125
|
-
DataElement.uuid(BT_AVCTP_PROTOCOL_ID),
|
|
113
|
+
DataElement.uuid(core.BT_AVCTP_PROTOCOL_ID),
|
|
126
114
|
DataElement.unsigned_integer_16(avctp_version_int),
|
|
127
115
|
]
|
|
128
116
|
),
|
|
@@ -135,7 +123,7 @@ def make_controller_service_sdp_records(
|
|
|
135
123
|
[
|
|
136
124
|
DataElement.sequence(
|
|
137
125
|
[
|
|
138
|
-
DataElement.uuid(BT_AV_REMOTE_CONTROL_SERVICE),
|
|
126
|
+
DataElement.uuid(core.BT_AV_REMOTE_CONTROL_SERVICE),
|
|
139
127
|
DataElement.unsigned_integer_16(avrcp_version_int),
|
|
140
128
|
]
|
|
141
129
|
),
|
|
@@ -152,10 +140,10 @@ def make_controller_service_sdp_records(
|
|
|
152
140
|
# -----------------------------------------------------------------------------
|
|
153
141
|
def make_target_service_sdp_records(
|
|
154
142
|
service_record_handle: int,
|
|
155
|
-
avctp_version:
|
|
156
|
-
avrcp_version:
|
|
143
|
+
avctp_version: tuple[int, int] = (1, 4),
|
|
144
|
+
avrcp_version: tuple[int, int] = (1, 6),
|
|
157
145
|
supported_features: int = 0x23,
|
|
158
|
-
) ->
|
|
146
|
+
) -> list[ServiceAttribute]:
|
|
159
147
|
# TODO: support a way to compute the supported features from a feature list
|
|
160
148
|
avctp_version_int = avctp_version[0] << 8 | avctp_version[1]
|
|
161
149
|
avrcp_version_int = avrcp_version[0] << 8 | avrcp_version[1]
|
|
@@ -173,7 +161,7 @@ def make_target_service_sdp_records(
|
|
|
173
161
|
SDP_SERVICE_CLASS_ID_LIST_ATTRIBUTE_ID,
|
|
174
162
|
DataElement.sequence(
|
|
175
163
|
[
|
|
176
|
-
DataElement.uuid(BT_AV_REMOTE_CONTROL_TARGET_SERVICE),
|
|
164
|
+
DataElement.uuid(core.BT_AV_REMOTE_CONTROL_TARGET_SERVICE),
|
|
177
165
|
]
|
|
178
166
|
),
|
|
179
167
|
),
|
|
@@ -183,13 +171,13 @@ def make_target_service_sdp_records(
|
|
|
183
171
|
[
|
|
184
172
|
DataElement.sequence(
|
|
185
173
|
[
|
|
186
|
-
DataElement.uuid(BT_L2CAP_PROTOCOL_ID),
|
|
174
|
+
DataElement.uuid(core.BT_L2CAP_PROTOCOL_ID),
|
|
187
175
|
DataElement.unsigned_integer_16(avctp.AVCTP_PSM),
|
|
188
176
|
]
|
|
189
177
|
),
|
|
190
178
|
DataElement.sequence(
|
|
191
179
|
[
|
|
192
|
-
DataElement.uuid(BT_AVCTP_PROTOCOL_ID),
|
|
180
|
+
DataElement.uuid(core.BT_AVCTP_PROTOCOL_ID),
|
|
193
181
|
DataElement.unsigned_integer_16(avctp_version_int),
|
|
194
182
|
]
|
|
195
183
|
),
|
|
@@ -202,7 +190,7 @@ def make_target_service_sdp_records(
|
|
|
202
190
|
[
|
|
203
191
|
DataElement.sequence(
|
|
204
192
|
[
|
|
205
|
-
DataElement.uuid(BT_AV_REMOTE_CONTROL_SERVICE),
|
|
193
|
+
DataElement.uuid(core.BT_AV_REMOTE_CONTROL_SERVICE),
|
|
206
194
|
DataElement.unsigned_integer_16(avrcp_version_int),
|
|
207
195
|
]
|
|
208
196
|
),
|
|
@@ -291,7 +279,7 @@ class Command:
|
|
|
291
279
|
pdu_id: Protocol.PduId
|
|
292
280
|
parameter: bytes
|
|
293
281
|
|
|
294
|
-
def to_string(self, properties:
|
|
282
|
+
def to_string(self, properties: dict[str, str]) -> str:
|
|
295
283
|
properties_str = ",".join(
|
|
296
284
|
[f"{name}={value}" for name, value in properties.items()]
|
|
297
285
|
)
|
|
@@ -337,7 +325,7 @@ class GetPlayStatusCommand(Command):
|
|
|
337
325
|
# -----------------------------------------------------------------------------
|
|
338
326
|
class GetElementAttributesCommand(Command):
|
|
339
327
|
identifier: int
|
|
340
|
-
attribute_ids:
|
|
328
|
+
attribute_ids: list[MediaAttributeId]
|
|
341
329
|
|
|
342
330
|
@classmethod
|
|
343
331
|
def from_bytes(cls, pdu: bytes) -> GetElementAttributesCommand:
|
|
@@ -409,7 +397,7 @@ class Response:
|
|
|
409
397
|
pdu_id: Protocol.PduId
|
|
410
398
|
parameter: bytes
|
|
411
399
|
|
|
412
|
-
def to_string(self, properties:
|
|
400
|
+
def to_string(self, properties: dict[str, str]) -> str:
|
|
413
401
|
properties_str = ",".join(
|
|
414
402
|
[f"{name}={value}" for name, value in properties.items()]
|
|
415
403
|
)
|
|
@@ -454,7 +442,7 @@ class NotImplementedResponse(Response):
|
|
|
454
442
|
# -----------------------------------------------------------------------------
|
|
455
443
|
class GetCapabilitiesResponse(Response):
|
|
456
444
|
capability_id: GetCapabilitiesCommand.CapabilityId
|
|
457
|
-
capabilities:
|
|
445
|
+
capabilities: list[Union[SupportsBytes, bytes]]
|
|
458
446
|
|
|
459
447
|
@classmethod
|
|
460
448
|
def from_bytes(cls, pdu: bytes) -> GetCapabilitiesResponse:
|
|
@@ -467,7 +455,7 @@ class GetCapabilitiesResponse(Response):
|
|
|
467
455
|
capability_id = GetCapabilitiesCommand.CapabilityId(pdu[0])
|
|
468
456
|
capability_count = pdu[1]
|
|
469
457
|
|
|
470
|
-
capabilities:
|
|
458
|
+
capabilities: list[Union[SupportsBytes, bytes]]
|
|
471
459
|
if capability_id == GetCapabilitiesCommand.CapabilityId.EVENTS_SUPPORTED:
|
|
472
460
|
capabilities = [EventId(pdu[2 + x]) for x in range(capability_count)]
|
|
473
461
|
else:
|
|
@@ -540,13 +528,13 @@ class GetPlayStatusResponse(Response):
|
|
|
540
528
|
|
|
541
529
|
# -----------------------------------------------------------------------------
|
|
542
530
|
class GetElementAttributesResponse(Response):
|
|
543
|
-
attributes:
|
|
531
|
+
attributes: list[MediaAttribute]
|
|
544
532
|
|
|
545
533
|
@classmethod
|
|
546
534
|
def from_bytes(cls, pdu: bytes) -> GetElementAttributesResponse:
|
|
547
535
|
num_attributes = pdu[0]
|
|
548
536
|
offset = 1
|
|
549
|
-
attributes:
|
|
537
|
+
attributes: list[MediaAttribute] = []
|
|
550
538
|
for _ in range(num_attributes):
|
|
551
539
|
(
|
|
552
540
|
attribute_id_int,
|
|
@@ -817,7 +805,7 @@ class PlayerApplicationSettingChangedEvent(Event):
|
|
|
817
805
|
attribute_id: ApplicationSetting.AttributeId
|
|
818
806
|
value_id: utils.OpenIntEnum
|
|
819
807
|
|
|
820
|
-
player_application_settings:
|
|
808
|
+
player_application_settings: list[Setting]
|
|
821
809
|
|
|
822
810
|
@classmethod
|
|
823
811
|
def from_bytes(cls, pdu: bytes) -> PlayerApplicationSettingChangedEvent:
|
|
@@ -939,7 +927,7 @@ class VolumeChangedEvent(Event):
|
|
|
939
927
|
|
|
940
928
|
|
|
941
929
|
# -----------------------------------------------------------------------------
|
|
942
|
-
EVENT_SUBCLASSES:
|
|
930
|
+
EVENT_SUBCLASSES: dict[EventId, type[Event]] = {
|
|
943
931
|
EventId.PLAYBACK_STATUS_CHANGED: PlaybackStatusChangedEvent,
|
|
944
932
|
EventId.PLAYBACK_POS_CHANGED: PlaybackPositionChangedEvent,
|
|
945
933
|
EventId.TRACK_CHANGED: TrackChangedEvent,
|
|
@@ -967,14 +955,14 @@ class Delegate:
|
|
|
967
955
|
def __init__(self, status_code: Protocol.StatusCode) -> None:
|
|
968
956
|
self.status_code = status_code
|
|
969
957
|
|
|
970
|
-
supported_events:
|
|
958
|
+
supported_events: list[EventId]
|
|
971
959
|
volume: int
|
|
972
960
|
|
|
973
961
|
def __init__(self, supported_events: Iterable[EventId] = ()) -> None:
|
|
974
962
|
self.supported_events = list(supported_events)
|
|
975
963
|
self.volume = 0
|
|
976
964
|
|
|
977
|
-
async def get_supported_events(self) ->
|
|
965
|
+
async def get_supported_events(self) -> list[EventId]:
|
|
978
966
|
return self.supported_events
|
|
979
967
|
|
|
980
968
|
async def set_absolute_volume(self, volume: int) -> None:
|
|
@@ -996,6 +984,10 @@ class Delegate:
|
|
|
996
984
|
class Protocol(utils.EventEmitter):
|
|
997
985
|
"""AVRCP Controller and Target protocol."""
|
|
998
986
|
|
|
987
|
+
EVENT_CONNECTION = "connection"
|
|
988
|
+
EVENT_START = "start"
|
|
989
|
+
EVENT_STOP = "stop"
|
|
990
|
+
|
|
999
991
|
class PacketType(enum.IntEnum):
|
|
1000
992
|
SINGLE = 0b00
|
|
1001
993
|
START = 0b01
|
|
@@ -1120,8 +1112,8 @@ class Protocol(utils.EventEmitter):
|
|
|
1120
1112
|
receive_response_state: Optional[ReceiveResponseState]
|
|
1121
1113
|
avctp_protocol: Optional[avctp.Protocol]
|
|
1122
1114
|
free_commands: asyncio.Queue
|
|
1123
|
-
pending_commands:
|
|
1124
|
-
notification_listeners:
|
|
1115
|
+
pending_commands: dict[int, PendingCommand] # Pending commands, by label
|
|
1116
|
+
notification_listeners: dict[EventId, NotificationListener]
|
|
1125
1117
|
|
|
1126
1118
|
@staticmethod
|
|
1127
1119
|
def _check_vendor_dependent_frame(
|
|
@@ -1186,7 +1178,7 @@ class Protocol(utils.EventEmitter):
|
|
|
1186
1178
|
|
|
1187
1179
|
@staticmethod
|
|
1188
1180
|
def _check_response(
|
|
1189
|
-
response_context: ResponseContext, expected_type:
|
|
1181
|
+
response_context: ResponseContext, expected_type: type[_R]
|
|
1190
1182
|
) -> _R:
|
|
1191
1183
|
if isinstance(response_context, Protocol.FinalResponse):
|
|
1192
1184
|
if (
|
|
@@ -1207,7 +1199,7 @@ class Protocol(utils.EventEmitter):
|
|
|
1207
1199
|
def _delegate_command(
|
|
1208
1200
|
self, transaction_label: int, command: Command, method: Awaitable
|
|
1209
1201
|
) -> None:
|
|
1210
|
-
async def call():
|
|
1202
|
+
async def call() -> None:
|
|
1211
1203
|
try:
|
|
1212
1204
|
await method
|
|
1213
1205
|
except Delegate.Error as error:
|
|
@@ -1226,7 +1218,7 @@ class Protocol(utils.EventEmitter):
|
|
|
1226
1218
|
|
|
1227
1219
|
utils.AsyncRunner.spawn(call())
|
|
1228
1220
|
|
|
1229
|
-
async def get_supported_events(self) ->
|
|
1221
|
+
async def get_supported_events(self) -> list[EventId]:
|
|
1230
1222
|
"""Get the list of events supported by the connected peer."""
|
|
1231
1223
|
response_context = await self.send_avrcp_command(
|
|
1232
1224
|
avc.CommandFrame.CommandType.STATUS,
|
|
@@ -1249,7 +1241,7 @@ class Protocol(utils.EventEmitter):
|
|
|
1249
1241
|
|
|
1250
1242
|
async def get_element_attributes(
|
|
1251
1243
|
self, element_identifier: int, attribute_ids: Sequence[MediaAttributeId]
|
|
1252
|
-
) ->
|
|
1244
|
+
) -> list[MediaAttribute]:
|
|
1253
1245
|
"""Get element attributes from the connected peer."""
|
|
1254
1246
|
response_context = await self.send_avrcp_command(
|
|
1255
1247
|
avc.CommandFrame.CommandType.STATUS,
|
|
@@ -1331,7 +1323,7 @@ class Protocol(utils.EventEmitter):
|
|
|
1331
1323
|
|
|
1332
1324
|
async def monitor_player_application_settings(
|
|
1333
1325
|
self,
|
|
1334
|
-
) -> AsyncIterator[
|
|
1326
|
+
) -> AsyncIterator[list[PlayerApplicationSettingChangedEvent.Setting]]:
|
|
1335
1327
|
"""Monitor Player Application Setting changes from the connected peer."""
|
|
1336
1328
|
async for event in self.monitor_events(
|
|
1337
1329
|
EventId.PLAYER_APPLICATION_SETTING_CHANGED, 0
|
|
@@ -1411,7 +1403,7 @@ class Protocol(utils.EventEmitter):
|
|
|
1411
1403
|
def notify_track_changed(self, identifier: bytes) -> None:
|
|
1412
1404
|
"""Notify the connected peer of a Track change."""
|
|
1413
1405
|
if len(identifier) != 8:
|
|
1414
|
-
raise InvalidArgumentError("identifier must be 8 bytes")
|
|
1406
|
+
raise core.InvalidArgumentError("identifier must be 8 bytes")
|
|
1415
1407
|
self.notify_event(TrackChangedEvent(identifier))
|
|
1416
1408
|
|
|
1417
1409
|
def notify_playback_position_changed(self, position: int) -> None:
|
|
@@ -1456,9 +1448,11 @@ class Protocol(utils.EventEmitter):
|
|
|
1456
1448
|
|
|
1457
1449
|
def _on_avctp_connection(self, l2cap_channel: l2cap.ClassicChannel) -> None:
|
|
1458
1450
|
logger.debug("AVCTP connection established")
|
|
1459
|
-
l2cap_channel.on(
|
|
1451
|
+
l2cap_channel.on(
|
|
1452
|
+
l2cap_channel.EVENT_OPEN, lambda: self._on_avctp_channel_open(l2cap_channel)
|
|
1453
|
+
)
|
|
1460
1454
|
|
|
1461
|
-
self.emit(
|
|
1455
|
+
self.emit(self.EVENT_CONNECTION)
|
|
1462
1456
|
|
|
1463
1457
|
def _on_avctp_channel_open(self, l2cap_channel: l2cap.ClassicChannel) -> None:
|
|
1464
1458
|
logger.debug("AVCTP channel open")
|
|
@@ -1473,15 +1467,15 @@ class Protocol(utils.EventEmitter):
|
|
|
1473
1467
|
self.avctp_protocol.register_response_handler(
|
|
1474
1468
|
AVRCP_PID, self._on_avctp_response
|
|
1475
1469
|
)
|
|
1476
|
-
l2cap_channel.on(
|
|
1470
|
+
l2cap_channel.on(l2cap_channel.EVENT_CLOSE, self._on_avctp_channel_close)
|
|
1477
1471
|
|
|
1478
|
-
self.emit(
|
|
1472
|
+
self.emit(self.EVENT_START)
|
|
1479
1473
|
|
|
1480
1474
|
def _on_avctp_channel_close(self) -> None:
|
|
1481
1475
|
logger.debug("AVCTP channel closed")
|
|
1482
1476
|
self.avctp_protocol = None
|
|
1483
1477
|
|
|
1484
|
-
self.emit(
|
|
1478
|
+
self.emit(self.EVENT_STOP)
|
|
1485
1479
|
|
|
1486
1480
|
def _on_avctp_command(
|
|
1487
1481
|
self, transaction_label: int, command: avc.CommandFrame
|
|
@@ -1676,7 +1670,7 @@ class Protocol(utils.EventEmitter):
|
|
|
1676
1670
|
else:
|
|
1677
1671
|
logger.debug("unexpected PDU ID")
|
|
1678
1672
|
pending_command.response.set_exception(
|
|
1679
|
-
ProtocolError(
|
|
1673
|
+
core.ProtocolError(
|
|
1680
1674
|
error_code=None,
|
|
1681
1675
|
error_namespace="avrcp",
|
|
1682
1676
|
details="unexpected PDU ID",
|
|
@@ -1685,7 +1679,7 @@ class Protocol(utils.EventEmitter):
|
|
|
1685
1679
|
else:
|
|
1686
1680
|
logger.debug("unexpected response code")
|
|
1687
1681
|
pending_command.response.set_exception(
|
|
1688
|
-
ProtocolError(
|
|
1682
|
+
core.ProtocolError(
|
|
1689
1683
|
error_code=None,
|
|
1690
1684
|
error_namespace="avrcp",
|
|
1691
1685
|
details="unexpected response code",
|
|
@@ -1863,12 +1857,12 @@ class Protocol(utils.EventEmitter):
|
|
|
1863
1857
|
) -> None:
|
|
1864
1858
|
logger.debug(f"<<< AVRCP command PDU: {command}")
|
|
1865
1859
|
|
|
1866
|
-
async def get_supported_events():
|
|
1860
|
+
async def get_supported_events() -> None:
|
|
1867
1861
|
if (
|
|
1868
1862
|
command.capability_id
|
|
1869
1863
|
!= GetCapabilitiesCommand.CapabilityId.EVENTS_SUPPORTED
|
|
1870
1864
|
):
|
|
1871
|
-
raise
|
|
1865
|
+
raise core.InvalidArgumentError()
|
|
1872
1866
|
|
|
1873
1867
|
supported_events = await self.delegate.get_supported_events()
|
|
1874
1868
|
self.send_avrcp_response(
|
|
@@ -1884,7 +1878,7 @@ class Protocol(utils.EventEmitter):
|
|
|
1884
1878
|
) -> None:
|
|
1885
1879
|
logger.debug(f"<<< AVRCP command PDU: {command}")
|
|
1886
1880
|
|
|
1887
|
-
async def set_absolute_volume():
|
|
1881
|
+
async def set_absolute_volume() -> None:
|
|
1888
1882
|
await self.delegate.set_absolute_volume(command.volume)
|
|
1889
1883
|
effective_volume = await self.delegate.get_absolute_volume()
|
|
1890
1884
|
self.send_avrcp_response(
|
|
@@ -1900,7 +1894,7 @@ class Protocol(utils.EventEmitter):
|
|
|
1900
1894
|
) -> None:
|
|
1901
1895
|
logger.debug(f"<<< AVRCP command PDU: {command}")
|
|
1902
1896
|
|
|
1903
|
-
async def register_notification():
|
|
1897
|
+
async def register_notification() -> None:
|
|
1904
1898
|
# Check if the event is supported.
|
|
1905
1899
|
supported_events = await self.delegate.get_supported_events()
|
|
1906
1900
|
if command.event_id not in supported_events:
|
bumble/colors.py
CHANGED
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
14
14
|
|
|
15
15
|
from functools import partial
|
|
16
|
-
from typing import
|
|
16
|
+
from typing import Optional, Union
|
|
17
17
|
|
|
18
18
|
|
|
19
19
|
class ColorError(ValueError):
|
|
@@ -65,7 +65,7 @@ def color(
|
|
|
65
65
|
bg: Optional[ColorSpec] = None,
|
|
66
66
|
style: Optional[str] = None,
|
|
67
67
|
) -> str:
|
|
68
|
-
codes:
|
|
68
|
+
codes: list[ColorSpec] = []
|
|
69
69
|
|
|
70
70
|
if fg:
|
|
71
71
|
codes.append(_color_code(fg, 30))
|
bumble/controller.py
CHANGED
|
@@ -27,7 +27,7 @@ from bumble.colors import color
|
|
|
27
27
|
from bumble.core import (
|
|
28
28
|
PhysicalTransport,
|
|
29
29
|
)
|
|
30
|
-
|
|
30
|
+
from bumble import hci
|
|
31
31
|
from bumble.hci import (
|
|
32
32
|
HCI_ACL_DATA_PACKET,
|
|
33
33
|
HCI_COMMAND_DISALLOWED_ERROR,
|
|
@@ -63,7 +63,7 @@ from bumble.hci import (
|
|
|
63
63
|
HCI_Packet,
|
|
64
64
|
HCI_Role_Change_Event,
|
|
65
65
|
)
|
|
66
|
-
from typing import Optional, Union,
|
|
66
|
+
from typing import Optional, Union, Any, TYPE_CHECKING
|
|
67
67
|
|
|
68
68
|
if TYPE_CHECKING:
|
|
69
69
|
from bumble.link import LocalLink
|
|
@@ -108,7 +108,9 @@ class Connection:
|
|
|
108
108
|
def on_hci_acl_data_packet(self, packet):
|
|
109
109
|
self.assembler.feed_packet(packet)
|
|
110
110
|
self.controller.send_hci_packet(
|
|
111
|
-
HCI_Number_Of_Completed_Packets_Event(
|
|
111
|
+
HCI_Number_Of_Completed_Packets_Event(
|
|
112
|
+
connection_handles=[self.handle], num_completed_packets=[1]
|
|
113
|
+
)
|
|
112
114
|
)
|
|
113
115
|
|
|
114
116
|
def on_acl_pdu(self, data):
|
|
@@ -132,17 +134,17 @@ class Controller:
|
|
|
132
134
|
self.hci_sink = None
|
|
133
135
|
self.link = link
|
|
134
136
|
|
|
135
|
-
self.central_connections:
|
|
137
|
+
self.central_connections: dict[Address, Connection] = (
|
|
136
138
|
{}
|
|
137
139
|
) # Connections where this controller is the central
|
|
138
|
-
self.peripheral_connections:
|
|
140
|
+
self.peripheral_connections: dict[Address, Connection] = (
|
|
139
141
|
{}
|
|
140
142
|
) # Connections where this controller is the peripheral
|
|
141
|
-
self.classic_connections:
|
|
143
|
+
self.classic_connections: dict[Address, Connection] = (
|
|
142
144
|
{}
|
|
143
145
|
) # Connections in BR/EDR
|
|
144
|
-
self.central_cis_links:
|
|
145
|
-
self.peripheral_cis_links:
|
|
146
|
+
self.central_cis_links: dict[int, CisLink] = {} # CIS links by handle
|
|
147
|
+
self.peripheral_cis_links: dict[int, CisLink] = {} # CIS links by handle
|
|
146
148
|
|
|
147
149
|
self.hci_version = HCI_VERSION_BLUETOOTH_CORE_5_0
|
|
148
150
|
self.hci_revision = 0
|
|
@@ -392,7 +394,7 @@ class Controller:
|
|
|
392
394
|
peer_address=peer_address,
|
|
393
395
|
link=self.link,
|
|
394
396
|
transport=PhysicalTransport.LE,
|
|
395
|
-
link_type=HCI_Connection_Complete_Event.
|
|
397
|
+
link_type=HCI_Connection_Complete_Event.LinkType.ACL,
|
|
396
398
|
)
|
|
397
399
|
self.peripheral_connections[peer_address] = connection
|
|
398
400
|
logger.debug(f'New PERIPHERAL connection handle: 0x{connection_handle:04X}')
|
|
@@ -452,7 +454,7 @@ class Controller:
|
|
|
452
454
|
peer_address=peer_address,
|
|
453
455
|
link=self.link,
|
|
454
456
|
transport=PhysicalTransport.LE,
|
|
455
|
-
link_type=HCI_Connection_Complete_Event.
|
|
457
|
+
link_type=HCI_Connection_Complete_Event.LinkType.ACL,
|
|
456
458
|
)
|
|
457
459
|
self.central_connections[peer_address] = connection
|
|
458
460
|
logger.debug(
|
|
@@ -542,15 +544,14 @@ class Controller:
|
|
|
542
544
|
acl_packet = HCI_AclDataPacket(connection.handle, 2, 0, len(data), data)
|
|
543
545
|
self.send_hci_packet(acl_packet)
|
|
544
546
|
|
|
545
|
-
def on_link_advertising_data(self, sender_address, data):
|
|
547
|
+
def on_link_advertising_data(self, sender_address: Address, data: bytes):
|
|
546
548
|
# Ignore if we're not scanning
|
|
547
549
|
if self.le_scan_enable == 0:
|
|
548
550
|
return
|
|
549
551
|
|
|
550
552
|
# Send a scan report
|
|
551
553
|
report = HCI_LE_Advertising_Report_Event.Report(
|
|
552
|
-
HCI_LE_Advertising_Report_Event.
|
|
553
|
-
event_type=HCI_LE_Advertising_Report_Event.ADV_IND,
|
|
554
|
+
event_type=HCI_LE_Advertising_Report_Event.EventType.ADV_IND,
|
|
554
555
|
address_type=sender_address.address_type,
|
|
555
556
|
address=sender_address,
|
|
556
557
|
data=data,
|
|
@@ -560,8 +561,7 @@ class Controller:
|
|
|
560
561
|
|
|
561
562
|
# Simulate a scan response
|
|
562
563
|
report = HCI_LE_Advertising_Report_Event.Report(
|
|
563
|
-
HCI_LE_Advertising_Report_Event.
|
|
564
|
-
event_type=HCI_LE_Advertising_Report_Event.SCAN_RSP,
|
|
564
|
+
event_type=HCI_LE_Advertising_Report_Event.EventType.SCAN_RSP,
|
|
565
565
|
address_type=sender_address.address_type,
|
|
566
566
|
address=sender_address,
|
|
567
567
|
data=data,
|
|
@@ -618,8 +618,8 @@ class Controller:
|
|
|
618
618
|
cis_sync_delay=0,
|
|
619
619
|
transport_latency_c_to_p=0,
|
|
620
620
|
transport_latency_p_to_c=0,
|
|
621
|
-
phy_c_to_p=
|
|
622
|
-
phy_p_to_c=
|
|
621
|
+
phy_c_to_p=1,
|
|
622
|
+
phy_p_to_c=1,
|
|
623
623
|
nse=0,
|
|
624
624
|
bn_c_to_p=0,
|
|
625
625
|
bn_p_to_c=0,
|
|
@@ -695,7 +695,7 @@ class Controller:
|
|
|
695
695
|
peer_address=peer_address,
|
|
696
696
|
link=self.link,
|
|
697
697
|
transport=PhysicalTransport.BR_EDR,
|
|
698
|
-
link_type=HCI_Connection_Complete_Event.
|
|
698
|
+
link_type=HCI_Connection_Complete_Event.LinkType.ACL,
|
|
699
699
|
)
|
|
700
700
|
self.classic_connections[peer_address] = connection
|
|
701
701
|
logger.debug(
|
|
@@ -709,7 +709,7 @@ class Controller:
|
|
|
709
709
|
connection_handle=connection_handle,
|
|
710
710
|
bd_addr=peer_address,
|
|
711
711
|
encryption_enabled=False,
|
|
712
|
-
link_type=HCI_Connection_Complete_Event.
|
|
712
|
+
link_type=HCI_Connection_Complete_Event.LinkType.ACL,
|
|
713
713
|
)
|
|
714
714
|
)
|
|
715
715
|
else:
|
|
@@ -720,7 +720,7 @@ class Controller:
|
|
|
720
720
|
connection_handle=0,
|
|
721
721
|
bd_addr=peer_address,
|
|
722
722
|
encryption_enabled=False,
|
|
723
|
-
link_type=HCI_Connection_Complete_Event.
|
|
723
|
+
link_type=HCI_Connection_Complete_Event.LinkType.ACL,
|
|
724
724
|
)
|
|
725
725
|
)
|
|
726
726
|
|
|
@@ -945,7 +945,7 @@ class Controller:
|
|
|
945
945
|
)
|
|
946
946
|
)
|
|
947
947
|
self.link.classic_sco_connect(
|
|
948
|
-
self, connection.peer_address, HCI_Connection_Complete_Event.
|
|
948
|
+
self, connection.peer_address, HCI_Connection_Complete_Event.LinkType.ESCO
|
|
949
949
|
)
|
|
950
950
|
|
|
951
951
|
def on_hci_enhanced_accept_synchronous_connection_request_command(self, command):
|
|
@@ -974,10 +974,71 @@ class Controller:
|
|
|
974
974
|
)
|
|
975
975
|
)
|
|
976
976
|
self.link.classic_accept_sco_connection(
|
|
977
|
-
self, connection.peer_address, HCI_Connection_Complete_Event.
|
|
977
|
+
self, connection.peer_address, HCI_Connection_Complete_Event.LinkType.ESCO
|
|
978
|
+
)
|
|
979
|
+
|
|
980
|
+
def on_hci_sniff_mode_command(self, command: hci.HCI_Sniff_Mode_Command):
|
|
981
|
+
'''
|
|
982
|
+
See Bluetooth spec Vol 4, Part E - 7.2.2 Sniff Mode command
|
|
983
|
+
'''
|
|
984
|
+
if self.link is None:
|
|
985
|
+
self.send_hci_packet(
|
|
986
|
+
hci.HCI_Command_Status_Event(
|
|
987
|
+
status=hci.HCI_UNKNOWN_CONNECTION_IDENTIFIER_ERROR,
|
|
988
|
+
num_hci_command_packets=1,
|
|
989
|
+
command_opcode=command.op_code,
|
|
990
|
+
)
|
|
991
|
+
)
|
|
992
|
+
return
|
|
993
|
+
|
|
994
|
+
self.send_hci_packet(
|
|
995
|
+
hci.HCI_Command_Status_Event(
|
|
996
|
+
status=HCI_SUCCESS,
|
|
997
|
+
num_hci_command_packets=1,
|
|
998
|
+
command_opcode=command.op_code,
|
|
999
|
+
)
|
|
1000
|
+
)
|
|
1001
|
+
self.send_hci_packet(
|
|
1002
|
+
hci.HCI_Mode_Change_Event(
|
|
1003
|
+
status=HCI_SUCCESS,
|
|
1004
|
+
connection_handle=command.connection_handle,
|
|
1005
|
+
current_mode=hci.HCI_Mode_Change_Event.Mode.SNIFF,
|
|
1006
|
+
interval=2,
|
|
1007
|
+
)
|
|
1008
|
+
)
|
|
1009
|
+
|
|
1010
|
+
def on_hci_exit_sniff_mode_command(self, command: hci.HCI_Exit_Sniff_Mode_Command):
|
|
1011
|
+
'''
|
|
1012
|
+
See Bluetooth spec Vol 4, Part E - 7.2.3 Exit Sniff Mode command
|
|
1013
|
+
'''
|
|
1014
|
+
|
|
1015
|
+
if self.link is None:
|
|
1016
|
+
self.send_hci_packet(
|
|
1017
|
+
hci.HCI_Command_Status_Event(
|
|
1018
|
+
status=hci.HCI_UNKNOWN_CONNECTION_IDENTIFIER_ERROR,
|
|
1019
|
+
num_hci_command_packets=1,
|
|
1020
|
+
command_opcode=command.op_code,
|
|
1021
|
+
)
|
|
1022
|
+
)
|
|
1023
|
+
return
|
|
1024
|
+
|
|
1025
|
+
self.send_hci_packet(
|
|
1026
|
+
hci.HCI_Command_Status_Event(
|
|
1027
|
+
status=HCI_SUCCESS,
|
|
1028
|
+
num_hci_command_packets=1,
|
|
1029
|
+
command_opcode=command.op_code,
|
|
1030
|
+
)
|
|
1031
|
+
)
|
|
1032
|
+
self.send_hci_packet(
|
|
1033
|
+
hci.HCI_Mode_Change_Event(
|
|
1034
|
+
status=HCI_SUCCESS,
|
|
1035
|
+
connection_handle=command.connection_handle,
|
|
1036
|
+
current_mode=hci.HCI_Mode_Change_Event.Mode.ACTIVE,
|
|
1037
|
+
interval=2,
|
|
1038
|
+
)
|
|
978
1039
|
)
|
|
979
1040
|
|
|
980
|
-
def on_hci_switch_role_command(self, command):
|
|
1041
|
+
def on_hci_switch_role_command(self, command: hci.HCI_Switch_Role_Command):
|
|
981
1042
|
'''
|
|
982
1043
|
See Bluetooth spec Vol 4, Part E - 7.2.8 Switch Role command
|
|
983
1044
|
'''
|
bumble/core.py
CHANGED
|
@@ -809,7 +809,7 @@ class Appearance:
|
|
|
809
809
|
STICK_PC = 0x0F
|
|
810
810
|
|
|
811
811
|
class WatchSubcategory(utils.OpenIntEnum):
|
|
812
|
-
|
|
812
|
+
GENERIC_WATCH = 0x00
|
|
813
813
|
SPORTS_WATCH = 0x01
|
|
814
814
|
SMARTWATCH = 0x02
|
|
815
815
|
|
|
@@ -1127,7 +1127,7 @@ class Appearance:
|
|
|
1127
1127
|
TURNTABLE = 0x05
|
|
1128
1128
|
CD_PLAYER = 0x06
|
|
1129
1129
|
DVD_PLAYER = 0x07
|
|
1130
|
-
|
|
1130
|
+
BLURAY_PLAYER = 0x08
|
|
1131
1131
|
OPTICAL_DISC_PLAYER = 0x09
|
|
1132
1132
|
SET_TOP_BOX = 0x0A
|
|
1133
1133
|
|
|
@@ -1351,6 +1351,12 @@ class AdvertisingData:
|
|
|
1351
1351
|
THREE_D_INFORMATION_DATA = 0x3D
|
|
1352
1352
|
MANUFACTURER_SPECIFIC_DATA = 0xFF
|
|
1353
1353
|
|
|
1354
|
+
class Flags(enum.IntFlag):
|
|
1355
|
+
LE_LIMITED_DISCOVERABLE_MODE = 1 << 0
|
|
1356
|
+
LE_GENERAL_DISCOVERABLE_MODE = 1 << 1
|
|
1357
|
+
BR_EDR_NOT_SUPPORTED = 1 << 2
|
|
1358
|
+
SIMULTANEOUS_LE_BR_EDR_CAPABLE = 1 << 3
|
|
1359
|
+
|
|
1354
1360
|
# For backward-compatibility
|
|
1355
1361
|
FLAGS = Type.FLAGS
|
|
1356
1362
|
INCOMPLETE_LIST_OF_16_BIT_SERVICE_CLASS_UUIDS = Type.INCOMPLETE_LIST_OF_16_BIT_SERVICE_CLASS_UUIDS
|
|
@@ -1407,11 +1413,11 @@ class AdvertisingData:
|
|
|
1407
1413
|
THREE_D_INFORMATION_DATA = Type.THREE_D_INFORMATION_DATA
|
|
1408
1414
|
MANUFACTURER_SPECIFIC_DATA = Type.MANUFACTURER_SPECIFIC_DATA
|
|
1409
1415
|
|
|
1410
|
-
LE_LIMITED_DISCOVERABLE_MODE_FLAG =
|
|
1411
|
-
LE_GENERAL_DISCOVERABLE_MODE_FLAG =
|
|
1412
|
-
BR_EDR_NOT_SUPPORTED_FLAG =
|
|
1413
|
-
BR_EDR_CONTROLLER_FLAG =
|
|
1414
|
-
BR_EDR_HOST_FLAG = 0x10
|
|
1416
|
+
LE_LIMITED_DISCOVERABLE_MODE_FLAG = Flags.LE_LIMITED_DISCOVERABLE_MODE
|
|
1417
|
+
LE_GENERAL_DISCOVERABLE_MODE_FLAG = Flags.LE_GENERAL_DISCOVERABLE_MODE
|
|
1418
|
+
BR_EDR_NOT_SUPPORTED_FLAG = Flags.BR_EDR_NOT_SUPPORTED
|
|
1419
|
+
BR_EDR_CONTROLLER_FLAG = Flags.SIMULTANEOUS_LE_BR_EDR_CAPABLE
|
|
1420
|
+
BR_EDR_HOST_FLAG = 0x10 # Deprecated
|
|
1415
1421
|
|
|
1416
1422
|
ad_structures: list[tuple[int, bytes]]
|
|
1417
1423
|
|