bumble 0.0.209__py3-none-any.whl → 0.0.210__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 +7 -7
- bumble/apps/auracast.py +37 -29
- bumble/apps/bench.py +9 -7
- bumble/apps/console.py +1 -1
- bumble/apps/lea_unicast/app.py +6 -2
- bumble/apps/pair.py +4 -3
- bumble/apps/player/player.py +3 -3
- bumble/apps/rfcomm_bridge.py +1 -1
- bumble/apps/speaker/speaker.py +4 -2
- bumble/att.py +2 -3
- bumble/avc.py +5 -5
- bumble/avdtp.py +9 -10
- bumble/avrcp.py +18 -19
- bumble/bridge.py +2 -2
- bumble/controller.py +6 -7
- bumble/core.py +56 -56
- bumble/device.py +169 -137
- bumble/drivers/__init__.py +2 -2
- bumble/gap.py +1 -1
- bumble/gatt_adapters.py +3 -3
- bumble/gatt_client.py +27 -21
- bumble/gatt_server.py +9 -10
- bumble/hci.py +34 -20
- bumble/hfp.py +3 -3
- bumble/hid.py +4 -3
- bumble/host.py +22 -16
- bumble/keys.py +3 -3
- bumble/l2cap.py +19 -17
- bumble/link.py +3 -4
- bumble/pairing.py +3 -3
- bumble/pandora/__init__.py +5 -5
- bumble/pandora/host.py +18 -12
- bumble/pandora/l2cap.py +2 -2
- bumble/pandora/security.py +15 -16
- bumble/profiles/aics.py +6 -6
- bumble/profiles/ancs.py +9 -10
- bumble/profiles/ascs.py +17 -10
- bumble/profiles/asha.py +5 -5
- bumble/profiles/bass.py +1 -1
- bumble/profiles/csip.py +10 -10
- bumble/profiles/gatt_service.py +12 -12
- bumble/profiles/hap.py +16 -16
- bumble/profiles/mcp.py +26 -24
- bumble/profiles/pacs.py +6 -6
- bumble/profiles/pbp.py +1 -1
- bumble/profiles/vcs.py +6 -4
- bumble/profiles/vocs.py +3 -3
- bumble/rfcomm.py +8 -8
- bumble/sdp.py +1 -1
- bumble/smp.py +36 -30
- bumble/transport/__init__.py +24 -19
- bumble/transport/android_emulator.py +8 -4
- bumble/transport/android_netsim.py +8 -5
- bumble/transport/common.py +5 -1
- bumble/transport/file.py +1 -1
- bumble/transport/hci_socket.py +1 -1
- bumble/transport/pty.py +1 -1
- bumble/transport/pyusb.py +3 -3
- bumble/transport/serial.py +1 -1
- bumble/transport/tcp_client.py +1 -1
- bumble/transport/tcp_server.py +1 -1
- bumble/transport/udp.py +1 -1
- bumble/transport/unix.py +1 -1
- bumble/transport/vhci.py +2 -2
- bumble/transport/ws_client.py +6 -1
- bumble/transport/ws_server.py +1 -1
- bumble/utils.py +89 -76
- {bumble-0.0.209.dist-info → bumble-0.0.210.dist-info}/METADATA +3 -2
- {bumble-0.0.209.dist-info → bumble-0.0.210.dist-info}/RECORD +74 -74
- {bumble-0.0.209.dist-info → bumble-0.0.210.dist-info}/WHEEL +1 -1
- {bumble-0.0.209.dist-info → bumble-0.0.210.dist-info}/entry_points.txt +0 -0
- {bumble-0.0.209.dist-info → bumble-0.0.210.dist-info/licenses}/LICENSE +0 -0
- {bumble-0.0.209.dist-info → bumble-0.0.210.dist-info}/top_level.txt +0 -0
bumble/_version.py
CHANGED
bumble/a2dp.py
CHANGED
|
@@ -26,9 +26,9 @@ from typing import Awaitable, Callable
|
|
|
26
26
|
from typing_extensions import ClassVar, Self
|
|
27
27
|
|
|
28
28
|
|
|
29
|
-
from .codecs import AacAudioRtpPacket
|
|
30
|
-
from .company_ids import COMPANY_IDENTIFIERS
|
|
31
|
-
from .sdp import (
|
|
29
|
+
from bumble.codecs import AacAudioRtpPacket
|
|
30
|
+
from bumble.company_ids import COMPANY_IDENTIFIERS
|
|
31
|
+
from bumble.sdp import (
|
|
32
32
|
DataElement,
|
|
33
33
|
ServiceAttribute,
|
|
34
34
|
SDP_PUBLIC_BROWSE_ROOT,
|
|
@@ -38,7 +38,7 @@ from .sdp import (
|
|
|
38
38
|
SDP_PROTOCOL_DESCRIPTOR_LIST_ATTRIBUTE_ID,
|
|
39
39
|
SDP_BLUETOOTH_PROFILE_DESCRIPTOR_LIST_ATTRIBUTE_ID,
|
|
40
40
|
)
|
|
41
|
-
from .core import (
|
|
41
|
+
from bumble.core import (
|
|
42
42
|
BT_L2CAP_PROTOCOL_ID,
|
|
43
43
|
BT_AUDIO_SOURCE_SERVICE,
|
|
44
44
|
BT_AUDIO_SINK_SERVICE,
|
|
@@ -46,7 +46,7 @@ from .core import (
|
|
|
46
46
|
BT_ADVANCED_AUDIO_DISTRIBUTION_SERVICE,
|
|
47
47
|
name_or_number,
|
|
48
48
|
)
|
|
49
|
-
from .rtp import MediaPacket
|
|
49
|
+
from bumble.rtp import MediaPacket
|
|
50
50
|
|
|
51
51
|
|
|
52
52
|
# -----------------------------------------------------------------------------
|
|
@@ -155,7 +155,7 @@ def flags_to_list(flags, values):
|
|
|
155
155
|
# -----------------------------------------------------------------------------
|
|
156
156
|
def make_audio_source_service_sdp_records(service_record_handle, version=(1, 3)):
|
|
157
157
|
# pylint: disable=import-outside-toplevel
|
|
158
|
-
from .avdtp import AVDTP_PSM
|
|
158
|
+
from bumble.avdtp import AVDTP_PSM
|
|
159
159
|
|
|
160
160
|
version_int = version[0] << 8 | version[1]
|
|
161
161
|
return [
|
|
@@ -209,7 +209,7 @@ def make_audio_source_service_sdp_records(service_record_handle, version=(1, 3))
|
|
|
209
209
|
# -----------------------------------------------------------------------------
|
|
210
210
|
def make_audio_sink_service_sdp_records(service_record_handle, version=(1, 3)):
|
|
211
211
|
# pylint: disable=import-outside-toplevel
|
|
212
|
-
from .avdtp import AVDTP_PSM
|
|
212
|
+
from bumble.avdtp import AVDTP_PSM
|
|
213
213
|
|
|
214
214
|
version_int = version[0] << 8 | version[1]
|
|
215
215
|
return [
|
bumble/apps/auracast.py
CHANGED
|
@@ -18,7 +18,6 @@
|
|
|
18
18
|
from __future__ import annotations
|
|
19
19
|
|
|
20
20
|
import asyncio
|
|
21
|
-
import asyncio.subprocess
|
|
22
21
|
import collections
|
|
23
22
|
import contextlib
|
|
24
23
|
import dataclasses
|
|
@@ -36,7 +35,6 @@ from typing import (
|
|
|
36
35
|
)
|
|
37
36
|
|
|
38
37
|
import click
|
|
39
|
-
import pyee
|
|
40
38
|
|
|
41
39
|
try:
|
|
42
40
|
import lc3 # type: ignore # pylint: disable=E0401
|
|
@@ -99,12 +97,31 @@ def codec_config_string(
|
|
|
99
97
|
return '\n'.join(indent + line for line in lines)
|
|
100
98
|
|
|
101
99
|
|
|
100
|
+
def broadcast_code_bytes(broadcast_code: str) -> bytes:
|
|
101
|
+
"""
|
|
102
|
+
Convert a broadcast code string to a 16-byte value.
|
|
103
|
+
|
|
104
|
+
If `broadcast_code` is `0x` followed by 32 hex characters, it is interpreted as a
|
|
105
|
+
raw 16-byte raw broadcast code in big-endian byte order.
|
|
106
|
+
Otherwise, `broadcast_code` is converted to a 16-byte value as specified in
|
|
107
|
+
BLUETOOTH CORE SPECIFICATION Version 6.0 | Vol 3, Part C , section 3.2.6.3
|
|
108
|
+
"""
|
|
109
|
+
if broadcast_code.startswith("0x") and len(broadcast_code) == 34:
|
|
110
|
+
return bytes.fromhex(broadcast_code[2:])[::-1]
|
|
111
|
+
|
|
112
|
+
broadcast_code_utf8 = broadcast_code.encode("utf-8")
|
|
113
|
+
if len(broadcast_code_utf8) > 16:
|
|
114
|
+
raise ValueError("broadcast code must be <= 16 bytes in utf-8 encoding")
|
|
115
|
+
padding = bytes(16 - len(broadcast_code_utf8))
|
|
116
|
+
return broadcast_code_utf8 + padding
|
|
117
|
+
|
|
118
|
+
|
|
102
119
|
# -----------------------------------------------------------------------------
|
|
103
120
|
# Scan For Broadcasts
|
|
104
121
|
# -----------------------------------------------------------------------------
|
|
105
|
-
class BroadcastScanner(
|
|
122
|
+
class BroadcastScanner(bumble.utils.EventEmitter):
|
|
106
123
|
@dataclasses.dataclass
|
|
107
|
-
class Broadcast(
|
|
124
|
+
class Broadcast(bumble.utils.EventEmitter):
|
|
108
125
|
name: str | None
|
|
109
126
|
sync: bumble.device.PeriodicAdvertisingSync
|
|
110
127
|
broadcast_id: int
|
|
@@ -234,22 +251,14 @@ class BroadcastScanner(pyee.EventEmitter):
|
|
|
234
251
|
|
|
235
252
|
if self.biginfo:
|
|
236
253
|
print(color(' BIG:', 'cyan'))
|
|
237
|
-
print(
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
)
|
|
241
|
-
print(
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
)
|
|
245
|
-
print(
|
|
246
|
-
color(' Framed: ', 'magenta'),
|
|
247
|
-
self.biginfo.framed,
|
|
248
|
-
)
|
|
249
|
-
print(
|
|
250
|
-
color(' Encrypted: ', 'magenta'),
|
|
251
|
-
self.biginfo.encrypted,
|
|
252
|
-
)
|
|
254
|
+
print(color(' Number of BIS:', 'magenta'), self.biginfo.num_bis)
|
|
255
|
+
print(color(' ISO Interval: ', 'magenta'), self.biginfo.iso_interval)
|
|
256
|
+
print(color(' Max PDU: ', 'magenta'), self.biginfo.max_pdu)
|
|
257
|
+
print(color(' SDU Interval: ', 'magenta'), self.biginfo.sdu_interval)
|
|
258
|
+
print(color(' Max SDU: ', 'magenta'), self.biginfo.max_sdu)
|
|
259
|
+
print(color(' PHY: ', 'magenta'), self.biginfo.phy.name)
|
|
260
|
+
print(color(' Framed: ', 'magenta'), self.biginfo.framed)
|
|
261
|
+
print(color(' Encrypted: ', 'magenta'), self.biginfo.encrypted)
|
|
253
262
|
|
|
254
263
|
def on_sync_establishment(self) -> None:
|
|
255
264
|
self.emit('sync_establishment')
|
|
@@ -365,7 +374,7 @@ class BroadcastScanner(pyee.EventEmitter):
|
|
|
365
374
|
self.emit('broadcast_loss', broadcast)
|
|
366
375
|
|
|
367
376
|
|
|
368
|
-
class PrintingBroadcastScanner(
|
|
377
|
+
class PrintingBroadcastScanner(bumble.utils.EventEmitter):
|
|
369
378
|
def __init__(
|
|
370
379
|
self, device: bumble.device.Device, filter_duplicates: bool, sync_timeout: float
|
|
371
380
|
) -> None:
|
|
@@ -702,14 +711,13 @@ async def run_receive(
|
|
|
702
711
|
|
|
703
712
|
def on_change() -> None:
|
|
704
713
|
if (
|
|
705
|
-
broadcast.basic_audio_announcement
|
|
706
|
-
|
|
707
|
-
):
|
|
714
|
+
broadcast.basic_audio_announcement and broadcast.biginfo
|
|
715
|
+
) and not basic_audio_announcement_scanned.is_set():
|
|
708
716
|
basic_audio_announcement_scanned.set()
|
|
709
717
|
|
|
710
718
|
broadcast.on('change', on_change)
|
|
711
|
-
if not broadcast.basic_audio_announcement:
|
|
712
|
-
print('Wait for Basic Audio Announcement...')
|
|
719
|
+
if not broadcast.basic_audio_announcement or not broadcast.biginfo:
|
|
720
|
+
print('Wait for Basic Audio Announcement and BIG Info...')
|
|
713
721
|
await basic_audio_announcement_scanned.wait()
|
|
714
722
|
print('Basic Audio Announcement found')
|
|
715
723
|
broadcast.print()
|
|
@@ -730,7 +738,7 @@ async def run_receive(
|
|
|
730
738
|
big_sync_timeout=0x4000,
|
|
731
739
|
bis=[bis.index for bis in subgroup.bis],
|
|
732
740
|
broadcast_code=(
|
|
733
|
-
|
|
741
|
+
broadcast_code_bytes(broadcast_code) if broadcast_code else None
|
|
734
742
|
),
|
|
735
743
|
),
|
|
736
744
|
)
|
|
@@ -944,7 +952,7 @@ async def run_transmit(
|
|
|
944
952
|
max_transport_latency=65,
|
|
945
953
|
rtn=4,
|
|
946
954
|
broadcast_code=(
|
|
947
|
-
|
|
955
|
+
broadcast_code_bytes(broadcast_code) if broadcast_code else None
|
|
948
956
|
),
|
|
949
957
|
),
|
|
950
958
|
)
|
|
@@ -1088,7 +1096,7 @@ def pair(ctx, transport, address):
|
|
|
1088
1096
|
'--broadcast-code',
|
|
1089
1097
|
metavar='BROADCAST_CODE',
|
|
1090
1098
|
type=str,
|
|
1091
|
-
help='Broadcast encryption code
|
|
1099
|
+
help='Broadcast encryption code (string or raw hex format prefixed with 0x)',
|
|
1092
1100
|
)
|
|
1093
1101
|
@click.option(
|
|
1094
1102
|
'--sync-timeout',
|
bumble/apps/bench.py
CHANGED
|
@@ -28,8 +28,7 @@ import click
|
|
|
28
28
|
|
|
29
29
|
from bumble import l2cap
|
|
30
30
|
from bumble.core import (
|
|
31
|
-
|
|
32
|
-
BT_LE_TRANSPORT,
|
|
31
|
+
PhysicalTransport,
|
|
33
32
|
BT_L2CAP_PROTOCOL_ID,
|
|
34
33
|
BT_RFCOMM_PROTOCOL_ID,
|
|
35
34
|
UUID,
|
|
@@ -42,8 +41,7 @@ from bumble.hci import (
|
|
|
42
41
|
HCI_LE_1M_PHY,
|
|
43
42
|
HCI_LE_2M_PHY,
|
|
44
43
|
HCI_LE_CODED_PHY,
|
|
45
|
-
|
|
46
|
-
HCI_PERIPHERAL_ROLE,
|
|
44
|
+
Role,
|
|
47
45
|
HCI_Constant,
|
|
48
46
|
HCI_Error,
|
|
49
47
|
HCI_StatusError,
|
|
@@ -113,7 +111,7 @@ def print_connection_phy(phy):
|
|
|
113
111
|
|
|
114
112
|
def print_connection(connection):
|
|
115
113
|
params = []
|
|
116
|
-
if connection.transport ==
|
|
114
|
+
if connection.transport == PhysicalTransport.LE:
|
|
117
115
|
params.append(
|
|
118
116
|
'DL=('
|
|
119
117
|
f'TX:{connection.data_length[0]}/{connection.data_length[1]},'
|
|
@@ -189,7 +187,7 @@ def log_stats(title, stats, precision=2):
|
|
|
189
187
|
|
|
190
188
|
|
|
191
189
|
async def switch_roles(connection, role):
|
|
192
|
-
target_role =
|
|
190
|
+
target_role = Role.CENTRAL if role == "central" else Role.PERIPHERAL
|
|
193
191
|
if connection.role != target_role:
|
|
194
192
|
logging.info(f'{color("### Switching roles to:", "cyan")} {role}')
|
|
195
193
|
try:
|
|
@@ -1275,7 +1273,11 @@ class Central(Connection.Listener):
|
|
|
1275
1273
|
self.connection = await self.device.connect(
|
|
1276
1274
|
self.peripheral_address,
|
|
1277
1275
|
connection_parameters_preferences=self.connection_parameter_preferences,
|
|
1278
|
-
transport=
|
|
1276
|
+
transport=(
|
|
1277
|
+
PhysicalTransport.BR_EDR
|
|
1278
|
+
if self.classic
|
|
1279
|
+
else PhysicalTransport.LE
|
|
1280
|
+
),
|
|
1279
1281
|
)
|
|
1280
1282
|
except CommandTimeoutError:
|
|
1281
1283
|
logging.info(color('!!! Connection timed out', 'red'))
|
bumble/apps/console.py
CHANGED
|
@@ -55,7 +55,7 @@ from prompt_toolkit.layout import (
|
|
|
55
55
|
from bumble import __version__
|
|
56
56
|
import bumble.core
|
|
57
57
|
from bumble import colors
|
|
58
|
-
from bumble.core import UUID, AdvertisingData,
|
|
58
|
+
from bumble.core import UUID, AdvertisingData, PhysicalTransport
|
|
59
59
|
from bumble.device import (
|
|
60
60
|
ConnectionParametersPreferences,
|
|
61
61
|
ConnectionPHY,
|
bumble/apps/lea_unicast/app.py
CHANGED
|
@@ -37,6 +37,7 @@ import click
|
|
|
37
37
|
import aiohttp.web
|
|
38
38
|
|
|
39
39
|
import bumble
|
|
40
|
+
from bumble import utils
|
|
40
41
|
from bumble.core import AdvertisingData
|
|
41
42
|
from bumble.colors import color
|
|
42
43
|
from bumble.device import Device, DeviceConfiguration, AdvertisingParameters, CisLink
|
|
@@ -359,7 +360,9 @@ class Speaker:
|
|
|
359
360
|
pcm = decoder.decode(
|
|
360
361
|
pdu.iso_sdu_fragment, bit_depth=DEFAULT_PCM_BYTES_PER_SAMPLE * 8
|
|
361
362
|
)
|
|
362
|
-
|
|
363
|
+
utils.cancel_on_event(
|
|
364
|
+
self.device, 'disconnection', self.ui_server.send_audio(pcm)
|
|
365
|
+
)
|
|
363
366
|
|
|
364
367
|
def on_ase_state_change(ase: ascs.AseStateMachine) -> None:
|
|
365
368
|
codec_config = ase.codec_specific_configuration
|
|
@@ -373,7 +376,8 @@ class Speaker:
|
|
|
373
376
|
or codec_config.codec_frames_per_sdu is None
|
|
374
377
|
):
|
|
375
378
|
return
|
|
376
|
-
|
|
379
|
+
utils.cancel_on_event(
|
|
380
|
+
ase.cis_link,
|
|
377
381
|
'disconnection',
|
|
378
382
|
lc3_source_task(
|
|
379
383
|
filename=self.lc3_input_file_path,
|
bumble/apps/pair.py
CHANGED
|
@@ -31,8 +31,7 @@ from bumble.keys import JsonKeyStore
|
|
|
31
31
|
from bumble.core import (
|
|
32
32
|
AdvertisingData,
|
|
33
33
|
ProtocolError,
|
|
34
|
-
|
|
35
|
-
BT_BR_EDR_TRANSPORT,
|
|
34
|
+
PhysicalTransport,
|
|
36
35
|
)
|
|
37
36
|
from bumble.gatt import (
|
|
38
37
|
GATT_DEVICE_NAME_CHARACTERISTIC,
|
|
@@ -422,7 +421,9 @@ async def pair(
|
|
|
422
421
|
print(color(f'=== Connecting to {address_or_name}...', 'green'))
|
|
423
422
|
connection = await device.connect(
|
|
424
423
|
address_or_name,
|
|
425
|
-
transport=
|
|
424
|
+
transport=(
|
|
425
|
+
PhysicalTransport.LE if mode == 'le' else PhysicalTransport.BR_EDR
|
|
426
|
+
),
|
|
426
427
|
)
|
|
427
428
|
|
|
428
429
|
if not request:
|
bumble/apps/player/player.py
CHANGED
|
@@ -56,7 +56,7 @@ from bumble.core import (
|
|
|
56
56
|
AdvertisingData,
|
|
57
57
|
ConnectionError as BumbleConnectionError,
|
|
58
58
|
DeviceClass,
|
|
59
|
-
|
|
59
|
+
PhysicalTransport,
|
|
60
60
|
)
|
|
61
61
|
from bumble.device import Connection, Device, DeviceConfiguration
|
|
62
62
|
from bumble.hci import Address, HCI_CONNECTION_ALREADY_EXISTS_ERROR, HCI_Constant
|
|
@@ -286,7 +286,7 @@ class Player:
|
|
|
286
286
|
|
|
287
287
|
async def connect(self, device: Device, address: str) -> Connection:
|
|
288
288
|
print(color(f"Connecting to {address}...", "green"))
|
|
289
|
-
connection = await device.connect(address, transport=
|
|
289
|
+
connection = await device.connect(address, transport=PhysicalTransport.BR_EDR)
|
|
290
290
|
|
|
291
291
|
# Request authentication
|
|
292
292
|
if self.authenticate:
|
|
@@ -402,7 +402,7 @@ class Player:
|
|
|
402
402
|
|
|
403
403
|
async def pair(self, device: Device, address: str) -> None:
|
|
404
404
|
print(color(f"Connecting to {address}...", "green"))
|
|
405
|
-
connection = await device.connect(address, transport=
|
|
405
|
+
connection = await device.connect(address, transport=PhysicalTransport.BR_EDR)
|
|
406
406
|
|
|
407
407
|
print(color("Pairing...", "magenta"))
|
|
408
408
|
await connection.authenticate()
|
bumble/apps/rfcomm_bridge.py
CHANGED
|
@@ -271,7 +271,7 @@ class ClientBridge:
|
|
|
271
271
|
print(color(f"@@@ Connecting to Bluetooth {self.address}", "blue"))
|
|
272
272
|
assert self.device
|
|
273
273
|
self.connection = await self.device.connect(
|
|
274
|
-
self.address, transport=core.
|
|
274
|
+
self.address, transport=core.PhysicalTransport.BR_EDR
|
|
275
275
|
)
|
|
276
276
|
print(color(f"@@@ Bluetooth connection: {self.connection}", "blue"))
|
|
277
277
|
self.connection.on("disconnection", self.on_disconnection)
|
bumble/apps/speaker/speaker.py
CHANGED
|
@@ -34,7 +34,7 @@ from aiohttp import web
|
|
|
34
34
|
|
|
35
35
|
import bumble
|
|
36
36
|
from bumble.colors import color
|
|
37
|
-
from bumble.core import
|
|
37
|
+
from bumble.core import PhysicalTransport, CommandTimeoutError
|
|
38
38
|
from bumble.device import Connection, Device, DeviceConfiguration
|
|
39
39
|
from bumble.hci import HCI_StatusError
|
|
40
40
|
from bumble.pairing import PairingConfig
|
|
@@ -568,7 +568,9 @@ class Speaker:
|
|
|
568
568
|
async def connect(self, address):
|
|
569
569
|
# Connect to the source
|
|
570
570
|
print(f'=== Connecting to {address}...')
|
|
571
|
-
connection = await self.device.connect(
|
|
571
|
+
connection = await self.device.connect(
|
|
572
|
+
address, transport=PhysicalTransport.BR_EDR
|
|
573
|
+
)
|
|
572
574
|
print(f'=== Connected to {connection.peer_address}')
|
|
573
575
|
|
|
574
576
|
# Request authentication
|
bumble/att.py
CHANGED
|
@@ -41,7 +41,6 @@ from typing import (
|
|
|
41
41
|
TYPE_CHECKING,
|
|
42
42
|
)
|
|
43
43
|
|
|
44
|
-
from pyee import EventEmitter
|
|
45
44
|
|
|
46
45
|
from bumble import utils
|
|
47
46
|
from bumble.core import UUID, name_or_number, InvalidOperationError, ProtocolError
|
|
@@ -798,7 +797,7 @@ class AttributeValue(Generic[_T]):
|
|
|
798
797
|
|
|
799
798
|
|
|
800
799
|
# -----------------------------------------------------------------------------
|
|
801
|
-
class Attribute(EventEmitter, Generic[_T]):
|
|
800
|
+
class Attribute(utils.EventEmitter, Generic[_T]):
|
|
802
801
|
class Permissions(enum.IntFlag):
|
|
803
802
|
READABLE = 0x01
|
|
804
803
|
WRITEABLE = 0x02
|
|
@@ -845,7 +844,7 @@ class Attribute(EventEmitter, Generic[_T]):
|
|
|
845
844
|
permissions: Union[str, Attribute.Permissions],
|
|
846
845
|
value: Union[AttributeValue[_T], _T, None] = None,
|
|
847
846
|
) -> None:
|
|
848
|
-
EventEmitter.__init__(self)
|
|
847
|
+
utils.EventEmitter.__init__(self)
|
|
849
848
|
self.handle = 0
|
|
850
849
|
self.end_group_handle = 0
|
|
851
850
|
if isinstance(permissions, str):
|
bumble/avc.py
CHANGED
|
@@ -21,7 +21,7 @@ import struct
|
|
|
21
21
|
from typing import Dict, Type, Union, Tuple
|
|
22
22
|
|
|
23
23
|
from bumble import core
|
|
24
|
-
from bumble
|
|
24
|
+
from bumble import utils
|
|
25
25
|
|
|
26
26
|
|
|
27
27
|
# -----------------------------------------------------------------------------
|
|
@@ -43,7 +43,7 @@ class Frame:
|
|
|
43
43
|
EXTENDED = 0x1E
|
|
44
44
|
UNIT = 0x1F
|
|
45
45
|
|
|
46
|
-
class OperationCode(OpenIntEnum):
|
|
46
|
+
class OperationCode(utils.OpenIntEnum):
|
|
47
47
|
# 0x00 - 0x0F: Unit and subunit commands
|
|
48
48
|
VENDOR_DEPENDENT = 0x00
|
|
49
49
|
RESERVE = 0x01
|
|
@@ -204,7 +204,7 @@ class Frame:
|
|
|
204
204
|
|
|
205
205
|
# -----------------------------------------------------------------------------
|
|
206
206
|
class CommandFrame(Frame):
|
|
207
|
-
class CommandType(OpenIntEnum):
|
|
207
|
+
class CommandType(utils.OpenIntEnum):
|
|
208
208
|
# AV/C Digital Interface Command Set General Specification Version 4.1
|
|
209
209
|
# Table 7.1
|
|
210
210
|
CONTROL = 0x00
|
|
@@ -240,7 +240,7 @@ class CommandFrame(Frame):
|
|
|
240
240
|
|
|
241
241
|
# -----------------------------------------------------------------------------
|
|
242
242
|
class ResponseFrame(Frame):
|
|
243
|
-
class ResponseCode(OpenIntEnum):
|
|
243
|
+
class ResponseCode(utils.OpenIntEnum):
|
|
244
244
|
# AV/C Digital Interface Command Set General Specification Version 4.1
|
|
245
245
|
# Table 7.2
|
|
246
246
|
NOT_IMPLEMENTED = 0x08
|
|
@@ -368,7 +368,7 @@ class PassThroughFrame:
|
|
|
368
368
|
PRESSED = 0
|
|
369
369
|
RELEASED = 1
|
|
370
370
|
|
|
371
|
-
class OperationId(OpenIntEnum):
|
|
371
|
+
class OperationId(utils.OpenIntEnum):
|
|
372
372
|
SELECT = 0x00
|
|
373
373
|
UP = 0x01
|
|
374
374
|
DOWN = 0x01
|
bumble/avdtp.py
CHANGED
|
@@ -37,16 +37,15 @@ from typing import (
|
|
|
37
37
|
cast,
|
|
38
38
|
)
|
|
39
39
|
|
|
40
|
-
from pyee import EventEmitter
|
|
41
40
|
|
|
42
|
-
from .core import (
|
|
41
|
+
from bumble.core import (
|
|
43
42
|
BT_ADVANCED_AUDIO_DISTRIBUTION_SERVICE,
|
|
44
43
|
InvalidStateError,
|
|
45
44
|
ProtocolError,
|
|
46
45
|
InvalidArgumentError,
|
|
47
46
|
name_or_number,
|
|
48
47
|
)
|
|
49
|
-
from .a2dp import (
|
|
48
|
+
from bumble.a2dp import (
|
|
50
49
|
A2DP_CODEC_TYPE_NAMES,
|
|
51
50
|
A2DP_MPEG_2_4_AAC_CODEC_TYPE,
|
|
52
51
|
A2DP_NON_A2DP_CODEC_TYPE,
|
|
@@ -56,9 +55,9 @@ from .a2dp import (
|
|
|
56
55
|
SbcMediaCodecInformation,
|
|
57
56
|
VendorSpecificMediaCodecInformation,
|
|
58
57
|
)
|
|
59
|
-
from .rtp import MediaPacket
|
|
60
|
-
from
|
|
61
|
-
from .colors import color
|
|
58
|
+
from bumble.rtp import MediaPacket
|
|
59
|
+
from bumble import sdp, device, l2cap, utils
|
|
60
|
+
from bumble.colors import color
|
|
62
61
|
|
|
63
62
|
|
|
64
63
|
# -----------------------------------------------------------------------------
|
|
@@ -1194,7 +1193,7 @@ class DelayReport_Reject(Simple_Reject):
|
|
|
1194
1193
|
|
|
1195
1194
|
|
|
1196
1195
|
# -----------------------------------------------------------------------------
|
|
1197
|
-
class Protocol(EventEmitter):
|
|
1196
|
+
class Protocol(utils.EventEmitter):
|
|
1198
1197
|
local_endpoints: List[LocalStreamEndPoint]
|
|
1199
1198
|
remote_endpoints: Dict[int, DiscoveredStreamEndPoint]
|
|
1200
1199
|
streams: Dict[int, Stream]
|
|
@@ -1680,7 +1679,7 @@ class Protocol(EventEmitter):
|
|
|
1680
1679
|
|
|
1681
1680
|
|
|
1682
1681
|
# -----------------------------------------------------------------------------
|
|
1683
|
-
class Listener(EventEmitter):
|
|
1682
|
+
class Listener(utils.EventEmitter):
|
|
1684
1683
|
servers: Dict[int, Protocol]
|
|
1685
1684
|
|
|
1686
1685
|
@staticmethod
|
|
@@ -2063,7 +2062,7 @@ class DiscoveredStreamEndPoint(StreamEndPoint, StreamEndPointProxy):
|
|
|
2063
2062
|
|
|
2064
2063
|
|
|
2065
2064
|
# -----------------------------------------------------------------------------
|
|
2066
|
-
class LocalStreamEndPoint(StreamEndPoint, EventEmitter):
|
|
2065
|
+
class LocalStreamEndPoint(StreamEndPoint, utils.EventEmitter):
|
|
2067
2066
|
stream: Optional[Stream]
|
|
2068
2067
|
|
|
2069
2068
|
def __init__(
|
|
@@ -2076,7 +2075,7 @@ class LocalStreamEndPoint(StreamEndPoint, EventEmitter):
|
|
|
2076
2075
|
configuration: Optional[Iterable[ServiceCapabilities]] = None,
|
|
2077
2076
|
):
|
|
2078
2077
|
StreamEndPoint.__init__(self, seid, media_type, tsep, 0, capabilities)
|
|
2079
|
-
EventEmitter.__init__(self)
|
|
2078
|
+
utils.EventEmitter.__init__(self)
|
|
2080
2079
|
self.protocol = protocol
|
|
2081
2080
|
self.configuration = configuration if configuration is not None else []
|
|
2082
2081
|
self.stream = None
|
bumble/avrcp.py
CHANGED
|
@@ -38,7 +38,6 @@ from typing import (
|
|
|
38
38
|
Union,
|
|
39
39
|
)
|
|
40
40
|
|
|
41
|
-
import pyee
|
|
42
41
|
|
|
43
42
|
from bumble.colors import color
|
|
44
43
|
from bumble.device import Device, Connection
|
|
@@ -53,7 +52,7 @@ from bumble.sdp import (
|
|
|
53
52
|
DataElement,
|
|
54
53
|
ServiceAttribute,
|
|
55
54
|
)
|
|
56
|
-
from bumble
|
|
55
|
+
from bumble import utils
|
|
57
56
|
from bumble.core import (
|
|
58
57
|
InvalidArgumentError,
|
|
59
58
|
ProtocolError,
|
|
@@ -307,7 +306,7 @@ class Command:
|
|
|
307
306
|
|
|
308
307
|
# -----------------------------------------------------------------------------
|
|
309
308
|
class GetCapabilitiesCommand(Command):
|
|
310
|
-
class CapabilityId(OpenIntEnum):
|
|
309
|
+
class CapabilityId(utils.OpenIntEnum):
|
|
311
310
|
COMPANY_ID = 0x02
|
|
312
311
|
EVENTS_SUPPORTED = 0x03
|
|
313
312
|
|
|
@@ -637,7 +636,7 @@ class RegisterNotificationResponse(Response):
|
|
|
637
636
|
|
|
638
637
|
|
|
639
638
|
# -----------------------------------------------------------------------------
|
|
640
|
-
class EventId(OpenIntEnum):
|
|
639
|
+
class EventId(utils.OpenIntEnum):
|
|
641
640
|
PLAYBACK_STATUS_CHANGED = 0x01
|
|
642
641
|
TRACK_CHANGED = 0x02
|
|
643
642
|
TRACK_REACHED_END = 0x03
|
|
@@ -657,12 +656,12 @@ class EventId(OpenIntEnum):
|
|
|
657
656
|
|
|
658
657
|
|
|
659
658
|
# -----------------------------------------------------------------------------
|
|
660
|
-
class CharacterSetId(OpenIntEnum):
|
|
659
|
+
class CharacterSetId(utils.OpenIntEnum):
|
|
661
660
|
UTF_8 = 0x06
|
|
662
661
|
|
|
663
662
|
|
|
664
663
|
# -----------------------------------------------------------------------------
|
|
665
|
-
class MediaAttributeId(OpenIntEnum):
|
|
664
|
+
class MediaAttributeId(utils.OpenIntEnum):
|
|
666
665
|
TITLE = 0x01
|
|
667
666
|
ARTIST_NAME = 0x02
|
|
668
667
|
ALBUM_NAME = 0x03
|
|
@@ -682,7 +681,7 @@ class MediaAttribute:
|
|
|
682
681
|
|
|
683
682
|
|
|
684
683
|
# -----------------------------------------------------------------------------
|
|
685
|
-
class PlayStatus(OpenIntEnum):
|
|
684
|
+
class PlayStatus(utils.OpenIntEnum):
|
|
686
685
|
STOPPED = 0x00
|
|
687
686
|
PLAYING = 0x01
|
|
688
687
|
PAUSED = 0x02
|
|
@@ -701,33 +700,33 @@ class SongAndPlayStatus:
|
|
|
701
700
|
|
|
702
701
|
# -----------------------------------------------------------------------------
|
|
703
702
|
class ApplicationSetting:
|
|
704
|
-
class AttributeId(OpenIntEnum):
|
|
703
|
+
class AttributeId(utils.OpenIntEnum):
|
|
705
704
|
EQUALIZER_ON_OFF = 0x01
|
|
706
705
|
REPEAT_MODE = 0x02
|
|
707
706
|
SHUFFLE_ON_OFF = 0x03
|
|
708
707
|
SCAN_ON_OFF = 0x04
|
|
709
708
|
|
|
710
|
-
class EqualizerOnOffStatus(OpenIntEnum):
|
|
709
|
+
class EqualizerOnOffStatus(utils.OpenIntEnum):
|
|
711
710
|
OFF = 0x01
|
|
712
711
|
ON = 0x02
|
|
713
712
|
|
|
714
|
-
class RepeatModeStatus(OpenIntEnum):
|
|
713
|
+
class RepeatModeStatus(utils.OpenIntEnum):
|
|
715
714
|
OFF = 0x01
|
|
716
715
|
SINGLE_TRACK_REPEAT = 0x02
|
|
717
716
|
ALL_TRACK_REPEAT = 0x03
|
|
718
717
|
GROUP_REPEAT = 0x04
|
|
719
718
|
|
|
720
|
-
class ShuffleOnOffStatus(OpenIntEnum):
|
|
719
|
+
class ShuffleOnOffStatus(utils.OpenIntEnum):
|
|
721
720
|
OFF = 0x01
|
|
722
721
|
ALL_TRACKS_SHUFFLE = 0x02
|
|
723
722
|
GROUP_SHUFFLE = 0x03
|
|
724
723
|
|
|
725
|
-
class ScanOnOffStatus(OpenIntEnum):
|
|
724
|
+
class ScanOnOffStatus(utils.OpenIntEnum):
|
|
726
725
|
OFF = 0x01
|
|
727
726
|
ALL_TRACKS_SCAN = 0x02
|
|
728
727
|
GROUP_SCAN = 0x03
|
|
729
728
|
|
|
730
|
-
class GenericValue(OpenIntEnum):
|
|
729
|
+
class GenericValue(utils.OpenIntEnum):
|
|
731
730
|
pass
|
|
732
731
|
|
|
733
732
|
|
|
@@ -816,7 +815,7 @@ class PlayerApplicationSettingChangedEvent(Event):
|
|
|
816
815
|
@dataclass
|
|
817
816
|
class Setting:
|
|
818
817
|
attribute_id: ApplicationSetting.AttributeId
|
|
819
|
-
value_id: OpenIntEnum
|
|
818
|
+
value_id: utils.OpenIntEnum
|
|
820
819
|
|
|
821
820
|
player_application_settings: List[Setting]
|
|
822
821
|
|
|
@@ -824,7 +823,7 @@ class PlayerApplicationSettingChangedEvent(Event):
|
|
|
824
823
|
def from_bytes(cls, pdu: bytes) -> PlayerApplicationSettingChangedEvent:
|
|
825
824
|
def setting(attribute_id_int: int, value_id_int: int):
|
|
826
825
|
attribute_id = ApplicationSetting.AttributeId(attribute_id_int)
|
|
827
|
-
value_id: OpenIntEnum
|
|
826
|
+
value_id: utils.OpenIntEnum
|
|
828
827
|
if attribute_id == ApplicationSetting.AttributeId.EQUALIZER_ON_OFF:
|
|
829
828
|
value_id = ApplicationSetting.EqualizerOnOffStatus(value_id_int)
|
|
830
829
|
elif attribute_id == ApplicationSetting.AttributeId.REPEAT_MODE:
|
|
@@ -994,7 +993,7 @@ class Delegate:
|
|
|
994
993
|
|
|
995
994
|
|
|
996
995
|
# -----------------------------------------------------------------------------
|
|
997
|
-
class Protocol(
|
|
996
|
+
class Protocol(utils.EventEmitter):
|
|
998
997
|
"""AVRCP Controller and Target protocol."""
|
|
999
998
|
|
|
1000
999
|
class PacketType(enum.IntEnum):
|
|
@@ -1003,7 +1002,7 @@ class Protocol(pyee.EventEmitter):
|
|
|
1003
1002
|
CONTINUE = 0b10
|
|
1004
1003
|
END = 0b11
|
|
1005
1004
|
|
|
1006
|
-
class PduId(OpenIntEnum):
|
|
1005
|
+
class PduId(utils.OpenIntEnum):
|
|
1007
1006
|
GET_CAPABILITIES = 0x10
|
|
1008
1007
|
LIST_PLAYER_APPLICATION_SETTING_ATTRIBUTES = 0x11
|
|
1009
1008
|
LIST_PLAYER_APPLICATION_SETTING_VALUES = 0x12
|
|
@@ -1024,7 +1023,7 @@ class Protocol(pyee.EventEmitter):
|
|
|
1024
1023
|
GET_FOLDER_ITEMS = 0x71
|
|
1025
1024
|
GET_TOTAL_NUMBER_OF_ITEMS = 0x75
|
|
1026
1025
|
|
|
1027
|
-
class StatusCode(OpenIntEnum):
|
|
1026
|
+
class StatusCode(utils.OpenIntEnum):
|
|
1028
1027
|
INVALID_COMMAND = 0x00
|
|
1029
1028
|
INVALID_PARAMETER = 0x01
|
|
1030
1029
|
PARAMETER_CONTENT_ERROR = 0x02
|
|
@@ -1466,7 +1465,7 @@ class Protocol(pyee.EventEmitter):
|
|
|
1466
1465
|
if self.avctp_protocol is not None:
|
|
1467
1466
|
# TODO: find a better strategy instead of just closing
|
|
1468
1467
|
logger.warning("AVCTP protocol already active, closing connection")
|
|
1469
|
-
AsyncRunner.spawn(l2cap_channel.disconnect())
|
|
1468
|
+
utils.AsyncRunner.spawn(l2cap_channel.disconnect())
|
|
1470
1469
|
return
|
|
1471
1470
|
|
|
1472
1471
|
self.avctp_protocol = avctp.Protocol(l2cap_channel)
|
bumble/bridge.py
CHANGED
|
@@ -17,8 +17,8 @@
|
|
|
17
17
|
# -----------------------------------------------------------------------------
|
|
18
18
|
import logging
|
|
19
19
|
|
|
20
|
-
from .hci import HCI_Packet
|
|
21
|
-
from .helpers import PacketTracer
|
|
20
|
+
from bumble.hci import HCI_Packet
|
|
21
|
+
from bumble.helpers import PacketTracer
|
|
22
22
|
|
|
23
23
|
# -----------------------------------------------------------------------------
|
|
24
24
|
# Logging
|