bumble 0.0.219__py3-none-any.whl → 0.0.221__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 +5 -5
- bumble/apps/auracast.py +746 -479
- bumble/apps/bench.py +4 -5
- bumble/apps/console.py +5 -10
- bumble/apps/controller_info.py +12 -7
- bumble/apps/controller_loopback.py +1 -2
- bumble/apps/device_info.py +2 -3
- bumble/apps/gatt_dump.py +0 -1
- bumble/apps/lea_unicast/app.py +1 -1
- bumble/apps/pair.py +49 -46
- bumble/apps/pandora_server.py +2 -2
- bumble/apps/player/player.py +10 -12
- bumble/apps/rfcomm_bridge.py +10 -11
- bumble/apps/scan.py +1 -3
- bumble/apps/speaker/speaker.py +3 -4
- bumble/at.py +4 -5
- bumble/att.py +91 -25
- bumble/audio/io.py +8 -6
- bumble/avc.py +1 -2
- bumble/avctp.py +2 -3
- bumble/avdtp.py +53 -57
- bumble/avrcp.py +25 -27
- bumble/codecs.py +15 -15
- bumble/colors.py +7 -8
- bumble/controller.py +1201 -643
- bumble/core.py +41 -49
- bumble/crypto/__init__.py +2 -1
- bumble/crypto/builtin.py +2 -8
- bumble/data_types.py +2 -1
- bumble/decoder.py +2 -3
- bumble/device.py +278 -325
- bumble/drivers/__init__.py +3 -2
- bumble/drivers/intel.py +6 -8
- bumble/drivers/rtk.py +1 -1
- bumble/gatt.py +9 -9
- bumble/gatt_adapters.py +6 -6
- bumble/gatt_client.py +110 -60
- bumble/gatt_server.py +209 -139
- bumble/hci.py +87 -74
- bumble/helpers.py +5 -5
- bumble/hfp.py +27 -26
- bumble/hid.py +9 -9
- bumble/host.py +44 -50
- bumble/keys.py +17 -17
- bumble/l2cap.py +1015 -218
- bumble/link.py +54 -284
- bumble/ll.py +200 -0
- bumble/lmp.py +324 -0
- bumble/pairing.py +14 -15
- bumble/pandora/__init__.py +2 -2
- bumble/pandora/device.py +6 -4
- bumble/pandora/host.py +19 -10
- bumble/pandora/l2cap.py +8 -9
- bumble/pandora/security.py +18 -16
- bumble/pandora/utils.py +4 -4
- bumble/profiles/aics.py +6 -8
- bumble/profiles/ams.py +3 -5
- bumble/profiles/ancs.py +11 -11
- bumble/profiles/ascs.py +5 -5
- bumble/profiles/asha.py +10 -9
- bumble/profiles/bass.py +9 -3
- bumble/profiles/battery_service.py +1 -2
- bumble/profiles/csip.py +9 -10
- bumble/profiles/device_information_service.py +16 -17
- bumble/profiles/gap.py +3 -4
- bumble/profiles/gatt_service.py +0 -1
- bumble/profiles/gmap.py +12 -13
- bumble/profiles/hap.py +3 -3
- bumble/profiles/heart_rate_service.py +7 -8
- bumble/profiles/le_audio.py +1 -1
- bumble/profiles/mcp.py +28 -28
- bumble/profiles/pacs.py +13 -17
- bumble/profiles/pbp.py +16 -0
- bumble/profiles/vcs.py +2 -2
- bumble/profiles/vocs.py +6 -9
- bumble/rfcomm.py +19 -18
- bumble/sdp.py +12 -11
- bumble/smp.py +20 -30
- bumble/snoop.py +12 -5
- bumble/tools/generate_company_id_list.py +1 -1
- bumble/tools/intel_util.py +2 -2
- bumble/tools/rtk_fw_download.py +1 -1
- bumble/tools/rtk_util.py +1 -1
- bumble/transport/__init__.py +1 -2
- bumble/transport/android_emulator.py +2 -3
- bumble/transport/android_netsim.py +49 -40
- bumble/transport/common.py +9 -9
- bumble/transport/file.py +1 -2
- bumble/transport/hci_socket.py +2 -3
- bumble/transport/pty.py +3 -5
- bumble/transport/pyusb.py +8 -5
- bumble/transport/serial.py +1 -2
- bumble/transport/vhci.py +1 -2
- bumble/transport/ws_server.py +2 -3
- bumble/utils.py +23 -14
- bumble/vendor/android/hci.py +4 -2
- {bumble-0.0.219.dist-info → bumble-0.0.221.dist-info}/METADATA +4 -3
- bumble-0.0.221.dist-info/RECORD +185 -0
- bumble-0.0.219.dist-info/RECORD +0 -183
- {bumble-0.0.219.dist-info → bumble-0.0.221.dist-info}/WHEEL +0 -0
- {bumble-0.0.219.dist-info → bumble-0.0.221.dist-info}/entry_points.txt +0 -0
- {bumble-0.0.219.dist-info → bumble-0.0.221.dist-info}/licenses/LICENSE +0 -0
- {bumble-0.0.219.dist-info → bumble-0.0.221.dist-info}/top_level.txt +0 -0
bumble/apps/bench.py
CHANGED
|
@@ -22,7 +22,6 @@ import logging
|
|
|
22
22
|
import statistics
|
|
23
23
|
import struct
|
|
24
24
|
import time
|
|
25
|
-
from typing import Optional
|
|
26
25
|
|
|
27
26
|
import click
|
|
28
27
|
|
|
@@ -257,8 +256,8 @@ async def pre_power_on(device: Device, classic: bool) -> None:
|
|
|
257
256
|
|
|
258
257
|
async def post_power_on(
|
|
259
258
|
device: Device,
|
|
260
|
-
le_scan:
|
|
261
|
-
le_advertise:
|
|
259
|
+
le_scan: tuple[int, int] | None,
|
|
260
|
+
le_advertise: int | None,
|
|
262
261
|
classic_page_scan: bool,
|
|
263
262
|
classic_inquiry_scan: bool,
|
|
264
263
|
) -> None:
|
|
@@ -1300,7 +1299,7 @@ class IsoClient(StreamedPacketIO):
|
|
|
1300
1299
|
super().__init__()
|
|
1301
1300
|
self.device = device
|
|
1302
1301
|
self.ready = asyncio.Event()
|
|
1303
|
-
self.cis_link:
|
|
1302
|
+
self.cis_link: CisLink | None = None
|
|
1304
1303
|
|
|
1305
1304
|
async def on_connection(
|
|
1306
1305
|
self, connection: Connection, cis_link: CisLink, sender: bool
|
|
@@ -1341,7 +1340,7 @@ class IsoServer(StreamedPacketIO):
|
|
|
1341
1340
|
):
|
|
1342
1341
|
super().__init__()
|
|
1343
1342
|
self.device = device
|
|
1344
|
-
self.cis_link:
|
|
1343
|
+
self.cis_link: CisLink | None = None
|
|
1345
1344
|
self.ready = asyncio.Event()
|
|
1346
1345
|
|
|
1347
1346
|
logging.info(
|
bumble/apps/console.py
CHANGED
|
@@ -24,7 +24,6 @@ import logging
|
|
|
24
24
|
import os
|
|
25
25
|
import re
|
|
26
26
|
from collections import OrderedDict
|
|
27
|
-
from typing import Optional, Union
|
|
28
27
|
|
|
29
28
|
import click
|
|
30
29
|
import humanize
|
|
@@ -126,8 +125,8 @@ def parse_phys(phys):
|
|
|
126
125
|
# Console App
|
|
127
126
|
# -----------------------------------------------------------------------------
|
|
128
127
|
class ConsoleApp:
|
|
129
|
-
connected_peer:
|
|
130
|
-
connection_phy:
|
|
128
|
+
connected_peer: Peer | None
|
|
129
|
+
connection_phy: ConnectionPHY | None
|
|
131
130
|
|
|
132
131
|
def __init__(self):
|
|
133
132
|
self.known_addresses = set()
|
|
@@ -520,7 +519,7 @@ class ConsoleApp:
|
|
|
520
519
|
|
|
521
520
|
self.show_attributes(attributes)
|
|
522
521
|
|
|
523
|
-
def find_remote_characteristic(self, param) ->
|
|
522
|
+
def find_remote_characteristic(self, param) -> CharacteristicProxy | None:
|
|
524
523
|
if not self.connected_peer:
|
|
525
524
|
return None
|
|
526
525
|
parts = param.split('.')
|
|
@@ -542,9 +541,7 @@ class ConsoleApp:
|
|
|
542
541
|
|
|
543
542
|
return None
|
|
544
543
|
|
|
545
|
-
def find_local_attribute(
|
|
546
|
-
self, param
|
|
547
|
-
) -> Optional[Union[Characteristic, Descriptor]]:
|
|
544
|
+
def find_local_attribute(self, param) -> Characteristic | Descriptor | None:
|
|
548
545
|
parts = param.split('.')
|
|
549
546
|
if len(parts) == 3:
|
|
550
547
|
service_uuid = UUID(parts[0])
|
|
@@ -1096,9 +1093,7 @@ class DeviceListener(Device.Listener, Connection.Listener):
|
|
|
1096
1093
|
if self.app.connected_peer.connection.is_encrypted
|
|
1097
1094
|
else 'not encrypted'
|
|
1098
1095
|
)
|
|
1099
|
-
self.app.append_to_output(
|
|
1100
|
-
'connection encryption change: ' f'{encryption_state}'
|
|
1101
|
-
)
|
|
1096
|
+
self.app.append_to_output(f'connection encryption change: {encryption_state}')
|
|
1102
1097
|
|
|
1103
1098
|
def on_connection_data_length_change(self):
|
|
1104
1099
|
self.app.append_to_output(
|
bumble/apps/controller_info.py
CHANGED
|
@@ -35,8 +35,6 @@ from bumble.hci import (
|
|
|
35
35
|
HCI_READ_BUFFER_SIZE_COMMAND,
|
|
36
36
|
HCI_READ_LOCAL_NAME_COMMAND,
|
|
37
37
|
HCI_SUCCESS,
|
|
38
|
-
HCI_VERSION_NAMES,
|
|
39
|
-
LMP_VERSION_NAMES,
|
|
40
38
|
CodecID,
|
|
41
39
|
HCI_Command,
|
|
42
40
|
HCI_Command_Complete_Event,
|
|
@@ -54,6 +52,7 @@ from bumble.hci import (
|
|
|
54
52
|
HCI_Read_Local_Supported_Codecs_V2_Command,
|
|
55
53
|
HCI_Read_Local_Version_Information_Command,
|
|
56
54
|
LeFeature,
|
|
55
|
+
SpecificationVersion,
|
|
57
56
|
map_null_terminated_utf8_string,
|
|
58
57
|
)
|
|
59
58
|
from bumble.host import Host
|
|
@@ -275,7 +274,7 @@ async def async_main(
|
|
|
275
274
|
(
|
|
276
275
|
f'min={min(latencies):.2f}, '
|
|
277
276
|
f'max={max(latencies):.2f}, '
|
|
278
|
-
f'average={sum(latencies)/len(latencies):.2f},'
|
|
277
|
+
f'average={sum(latencies) / len(latencies):.2f},'
|
|
279
278
|
),
|
|
280
279
|
[f'{latency:.4}' for latency in latencies],
|
|
281
280
|
'\n',
|
|
@@ -289,14 +288,20 @@ async def async_main(
|
|
|
289
288
|
)
|
|
290
289
|
print(
|
|
291
290
|
color(' HCI Version: ', 'green'),
|
|
292
|
-
|
|
291
|
+
SpecificationVersion(host.local_version.hci_version).name,
|
|
292
|
+
)
|
|
293
|
+
print(
|
|
294
|
+
color(' HCI Subversion:', 'green'),
|
|
295
|
+
f'0x{host.local_version.hci_subversion:04x}',
|
|
293
296
|
)
|
|
294
|
-
print(color(' HCI Subversion:', 'green'), host.local_version.hci_subversion)
|
|
295
297
|
print(
|
|
296
298
|
color(' LMP Version: ', 'green'),
|
|
297
|
-
|
|
299
|
+
SpecificationVersion(host.local_version.lmp_version).name,
|
|
300
|
+
)
|
|
301
|
+
print(
|
|
302
|
+
color(' LMP Subversion:', 'green'),
|
|
303
|
+
f'0x{host.local_version.lmp_subversion:04x}',
|
|
298
304
|
)
|
|
299
|
-
print(color(' LMP Subversion:', 'green'), host.local_version.lmp_subversion)
|
|
300
305
|
|
|
301
306
|
# Get the Classic info
|
|
302
307
|
await get_classic_info(host)
|
|
@@ -17,7 +17,6 @@
|
|
|
17
17
|
# -----------------------------------------------------------------------------
|
|
18
18
|
import asyncio
|
|
19
19
|
import time
|
|
20
|
-
from typing import Optional
|
|
21
20
|
|
|
22
21
|
import click
|
|
23
22
|
|
|
@@ -41,7 +40,7 @@ class Loopback:
|
|
|
41
40
|
self.transport = transport
|
|
42
41
|
self.packet_size = packet_size
|
|
43
42
|
self.packet_count = packet_count
|
|
44
|
-
self.connection_handle:
|
|
43
|
+
self.connection_handle: int | None = None
|
|
45
44
|
self.connection_event = asyncio.Event()
|
|
46
45
|
self.done = asyncio.Event()
|
|
47
46
|
self.expected_cid = 0
|
bumble/apps/device_info.py
CHANGED
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
# Imports
|
|
17
17
|
# -----------------------------------------------------------------------------
|
|
18
18
|
import asyncio
|
|
19
|
-
from
|
|
19
|
+
from collections.abc import Callable, Iterable
|
|
20
20
|
|
|
21
21
|
import click
|
|
22
22
|
|
|
@@ -174,7 +174,7 @@ async def show_vcs(vcs: VolumeControlServiceProxy) -> None:
|
|
|
174
174
|
|
|
175
175
|
|
|
176
176
|
# -----------------------------------------------------------------------------
|
|
177
|
-
async def show_device_info(peer, done:
|
|
177
|
+
async def show_device_info(peer, done: asyncio.Future | None) -> None:
|
|
178
178
|
try:
|
|
179
179
|
# Discover all services
|
|
180
180
|
print(color('### Discovering Services and Characteristics', 'magenta'))
|
|
@@ -215,7 +215,6 @@ async def show_device_info(peer, done: Optional[asyncio.Future]) -> None:
|
|
|
215
215
|
# -----------------------------------------------------------------------------
|
|
216
216
|
async def async_main(device_config, encrypt, transport, address_or_name):
|
|
217
217
|
async with await open_transport(transport) as (hci_source, hci_sink):
|
|
218
|
-
|
|
219
218
|
# Create a device
|
|
220
219
|
if device_config:
|
|
221
220
|
device = Device.from_config_file_with_hci(
|
bumble/apps/gatt_dump.py
CHANGED
|
@@ -61,7 +61,6 @@ async def dump_gatt_db(peer, done):
|
|
|
61
61
|
# -----------------------------------------------------------------------------
|
|
62
62
|
async def async_main(device_config, encrypt, transport, address_or_name):
|
|
63
63
|
async with await open_transport(transport) as (hci_source, hci_sink):
|
|
64
|
-
|
|
65
64
|
# Create a device
|
|
66
65
|
if device_config:
|
|
67
66
|
device = Device.from_config_file_with_hci(
|
bumble/apps/lea_unicast/app.py
CHANGED
|
@@ -268,7 +268,6 @@ class UiServer:
|
|
|
268
268
|
|
|
269
269
|
# -----------------------------------------------------------------------------
|
|
270
270
|
class Speaker:
|
|
271
|
-
|
|
272
271
|
def __init__(
|
|
273
272
|
self,
|
|
274
273
|
device_config_path: str | None,
|
|
@@ -299,6 +298,7 @@ class Speaker:
|
|
|
299
298
|
advertising_interval_max=25,
|
|
300
299
|
address=Address('F1:F2:F3:F4:F5:F6'),
|
|
301
300
|
identity_address_type=Address.RANDOM_DEVICE_ADDRESS,
|
|
301
|
+
eatt_enabled=True,
|
|
302
302
|
)
|
|
303
303
|
|
|
304
304
|
device_config.le_enabled = True
|
bumble/apps/pair.py
CHANGED
|
@@ -15,10 +15,11 @@
|
|
|
15
15
|
# -----------------------------------------------------------------------------
|
|
16
16
|
# Imports
|
|
17
17
|
# -----------------------------------------------------------------------------
|
|
18
|
+
from __future__ import annotations
|
|
19
|
+
|
|
18
20
|
import asyncio
|
|
19
21
|
import logging
|
|
20
22
|
import os
|
|
21
|
-
import struct
|
|
22
23
|
|
|
23
24
|
import click
|
|
24
25
|
from prompt_toolkit.shortcuts import PromptSession
|
|
@@ -64,7 +65,7 @@ POST_PAIRING_DELAY = 1
|
|
|
64
65
|
|
|
65
66
|
# -----------------------------------------------------------------------------
|
|
66
67
|
class Waiter:
|
|
67
|
-
instance = None
|
|
68
|
+
instance: Waiter | None = None
|
|
68
69
|
|
|
69
70
|
def __init__(self, linger=False):
|
|
70
71
|
self.done = asyncio.get_running_loop().create_future()
|
|
@@ -328,25 +329,25 @@ async def on_pairing_failure(connection, reason):
|
|
|
328
329
|
|
|
329
330
|
# -----------------------------------------------------------------------------
|
|
330
331
|
async def pair(
|
|
331
|
-
mode,
|
|
332
|
-
sc,
|
|
333
|
-
mitm,
|
|
334
|
-
bond,
|
|
335
|
-
ctkd,
|
|
336
|
-
advertising_address,
|
|
337
|
-
identity_address,
|
|
338
|
-
linger,
|
|
339
|
-
io,
|
|
340
|
-
oob,
|
|
341
|
-
prompt,
|
|
342
|
-
request,
|
|
343
|
-
print_keys,
|
|
344
|
-
keystore_file,
|
|
345
|
-
advertise_service_uuids,
|
|
346
|
-
advertise_appearance,
|
|
347
|
-
device_config,
|
|
348
|
-
hci_transport,
|
|
349
|
-
address_or_name,
|
|
332
|
+
mode: str,
|
|
333
|
+
sc: bool,
|
|
334
|
+
mitm: bool,
|
|
335
|
+
bond: bool,
|
|
336
|
+
ctkd: bool,
|
|
337
|
+
advertising_address: str,
|
|
338
|
+
identity_address: str,
|
|
339
|
+
linger: bool,
|
|
340
|
+
io: str,
|
|
341
|
+
oob: str,
|
|
342
|
+
prompt: bool,
|
|
343
|
+
request: bool,
|
|
344
|
+
print_keys: bool,
|
|
345
|
+
keystore_file: str,
|
|
346
|
+
advertise_service_uuids: str,
|
|
347
|
+
advertise_appearance: str,
|
|
348
|
+
device_config: str,
|
|
349
|
+
hci_transport: str,
|
|
350
|
+
address_or_name: str,
|
|
350
351
|
):
|
|
351
352
|
Waiter.instance = Waiter(linger=linger)
|
|
352
353
|
|
|
@@ -404,6 +405,7 @@ async def pair(
|
|
|
404
405
|
# Create an OOB context if needed
|
|
405
406
|
if oob:
|
|
406
407
|
our_oob_context = OobContext()
|
|
408
|
+
legacy_context: OobLegacyContext | None
|
|
407
409
|
if oob == '-':
|
|
408
410
|
shared_data = None
|
|
409
411
|
legacy_context = OobLegacyContext()
|
|
@@ -528,7 +530,9 @@ async def pair(
|
|
|
528
530
|
if advertise_appearance:
|
|
529
531
|
advertise_appearance = advertise_appearance.upper()
|
|
530
532
|
try:
|
|
531
|
-
|
|
533
|
+
appearance = data_types.Appearance.from_int(
|
|
534
|
+
int(advertise_appearance)
|
|
535
|
+
)
|
|
532
536
|
except ValueError:
|
|
533
537
|
category, subcategory = advertise_appearance.split('/')
|
|
534
538
|
try:
|
|
@@ -546,12 +550,11 @@ async def pair(
|
|
|
546
550
|
except ValueError:
|
|
547
551
|
print(color(f'Invalid subcategory {subcategory}', 'red'))
|
|
548
552
|
return
|
|
549
|
-
|
|
550
|
-
|
|
553
|
+
appearance = data_types.Appearance(
|
|
554
|
+
category_enum, subcategory_enum
|
|
551
555
|
)
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
)
|
|
556
|
+
|
|
557
|
+
advertising_data_types.append(appearance)
|
|
555
558
|
device.advertising_data = bytes(AdvertisingData(advertising_data_types))
|
|
556
559
|
await device.start_advertising(
|
|
557
560
|
auto_restart=True,
|
|
@@ -661,25 +664,25 @@ class LogHandler(logging.Handler):
|
|
|
661
664
|
@click.argument('hci_transport')
|
|
662
665
|
@click.argument('address-or-name', required=False)
|
|
663
666
|
def main(
|
|
664
|
-
mode,
|
|
665
|
-
sc,
|
|
666
|
-
mitm,
|
|
667
|
-
bond,
|
|
668
|
-
ctkd,
|
|
669
|
-
advertising_address,
|
|
670
|
-
identity_address,
|
|
671
|
-
linger,
|
|
672
|
-
io,
|
|
673
|
-
oob,
|
|
674
|
-
prompt,
|
|
675
|
-
request,
|
|
676
|
-
print_keys,
|
|
677
|
-
keystore_file,
|
|
678
|
-
advertise_service_uuid,
|
|
679
|
-
advertise_appearance,
|
|
680
|
-
device_config,
|
|
681
|
-
hci_transport,
|
|
682
|
-
address_or_name,
|
|
667
|
+
mode: str,
|
|
668
|
+
sc: bool,
|
|
669
|
+
mitm: bool,
|
|
670
|
+
bond: bool,
|
|
671
|
+
ctkd: bool,
|
|
672
|
+
advertising_address: str,
|
|
673
|
+
identity_address: str,
|
|
674
|
+
linger: bool,
|
|
675
|
+
io: str,
|
|
676
|
+
oob: str,
|
|
677
|
+
prompt: bool,
|
|
678
|
+
request: bool,
|
|
679
|
+
print_keys: bool,
|
|
680
|
+
keystore_file: str,
|
|
681
|
+
advertise_service_uuid: str,
|
|
682
|
+
advertise_appearance: str,
|
|
683
|
+
device_config: str,
|
|
684
|
+
hci_transport: str,
|
|
685
|
+
address_or_name: str,
|
|
683
686
|
):
|
|
684
687
|
# Setup logging
|
|
685
688
|
log_handler = LogHandler()
|
bumble/apps/pandora_server.py
CHANGED
|
@@ -19,7 +19,7 @@ ROOTCANAL_PORT_CUTTLEFISH = 7300
|
|
|
19
19
|
@click.option(
|
|
20
20
|
'--transport',
|
|
21
21
|
help='HCI transport',
|
|
22
|
-
default=
|
|
22
|
+
default='tcp-client:127.0.0.1:<rootcanal-port>',
|
|
23
23
|
)
|
|
24
24
|
@click.option(
|
|
25
25
|
'--config',
|
|
@@ -44,7 +44,7 @@ def retrieve_config(config: str) -> dict[str, Any]:
|
|
|
44
44
|
if not config:
|
|
45
45
|
return {}
|
|
46
46
|
|
|
47
|
-
with open(config
|
|
47
|
+
with open(config) as f:
|
|
48
48
|
return json.load(f)
|
|
49
49
|
|
|
50
50
|
|
bumble/apps/player/player.py
CHANGED
|
@@ -19,7 +19,6 @@ from __future__ import annotations
|
|
|
19
19
|
|
|
20
20
|
import asyncio
|
|
21
21
|
import logging
|
|
22
|
-
from typing import Optional, Union
|
|
23
22
|
|
|
24
23
|
import click
|
|
25
24
|
|
|
@@ -47,14 +46,13 @@ from bumble.avdtp import (
|
|
|
47
46
|
AVDTP_DELAY_REPORTING_SERVICE_CATEGORY,
|
|
48
47
|
MediaCodecCapabilities,
|
|
49
48
|
MediaPacketPump,
|
|
49
|
+
find_avdtp_service_with_connection,
|
|
50
50
|
)
|
|
51
51
|
from bumble.avdtp import Protocol as AvdtpProtocol
|
|
52
|
-
from bumble.avdtp import find_avdtp_service_with_connection
|
|
53
52
|
from bumble.avrcp import Protocol as AvrcpProtocol
|
|
54
53
|
from bumble.colors import color
|
|
55
|
-
from bumble.core import AdvertisingData
|
|
54
|
+
from bumble.core import AdvertisingData, DeviceClass, PhysicalTransport
|
|
56
55
|
from bumble.core import ConnectionError as BumbleConnectionError
|
|
57
|
-
from bumble.core import DeviceClass, PhysicalTransport
|
|
58
56
|
from bumble.device import Connection, Device, DeviceConfiguration
|
|
59
57
|
from bumble.hci import HCI_CONNECTION_ALREADY_EXISTS_ERROR, Address, HCI_Constant
|
|
60
58
|
from bumble.pairing import PairingConfig
|
|
@@ -191,7 +189,7 @@ class Player:
|
|
|
191
189
|
def __init__(
|
|
192
190
|
self,
|
|
193
191
|
transport: str,
|
|
194
|
-
device_config:
|
|
192
|
+
device_config: str | None,
|
|
195
193
|
authenticate: bool,
|
|
196
194
|
encrypt: bool,
|
|
197
195
|
) -> None:
|
|
@@ -199,8 +197,8 @@ class Player:
|
|
|
199
197
|
self.device_config = device_config
|
|
200
198
|
self.authenticate = authenticate
|
|
201
199
|
self.encrypt = encrypt
|
|
202
|
-
self.avrcp_protocol:
|
|
203
|
-
self.done:
|
|
200
|
+
self.avrcp_protocol: AvrcpProtocol | None = None
|
|
201
|
+
self.done: asyncio.Event | None
|
|
204
202
|
|
|
205
203
|
async def run(self, workload) -> None:
|
|
206
204
|
self.done = asyncio.Event()
|
|
@@ -315,7 +313,7 @@ class Player:
|
|
|
315
313
|
codec_type: int,
|
|
316
314
|
vendor_id: int,
|
|
317
315
|
codec_id: int,
|
|
318
|
-
packet_source:
|
|
316
|
+
packet_source: SbcPacketSource | AacPacketSource | OpusPacketSource,
|
|
319
317
|
codec_capabilities: MediaCodecCapabilities,
|
|
320
318
|
):
|
|
321
319
|
# Discover all endpoints on the remote device
|
|
@@ -381,11 +379,11 @@ class Player:
|
|
|
381
379
|
print(f">>> {color(address.to_string(False), 'yellow')}:")
|
|
382
380
|
print(f" Device Class (raw): {class_of_device:06X}")
|
|
383
381
|
major_class_name = DeviceClass.major_device_class_name(major_device_class)
|
|
384
|
-
print(" Device Major Class:
|
|
382
|
+
print(f" Device Major Class: {major_class_name}")
|
|
385
383
|
minor_class_name = DeviceClass.minor_device_class_name(
|
|
386
384
|
major_device_class, minor_device_class
|
|
387
385
|
)
|
|
388
|
-
print(" Device Minor Class:
|
|
386
|
+
print(f" Device Minor Class: {minor_class_name}")
|
|
389
387
|
print(
|
|
390
388
|
" Device Services: "
|
|
391
389
|
f"{', '.join(DeviceClass.service_class_labels(service_classes))}"
|
|
@@ -420,7 +418,7 @@ class Player:
|
|
|
420
418
|
async def play(
|
|
421
419
|
self,
|
|
422
420
|
device: Device,
|
|
423
|
-
address:
|
|
421
|
+
address: str | None,
|
|
424
422
|
audio_format: str,
|
|
425
423
|
audio_file: str,
|
|
426
424
|
) -> None:
|
|
@@ -449,7 +447,7 @@ class Player:
|
|
|
449
447
|
return input_file.read(byte_count)
|
|
450
448
|
|
|
451
449
|
# Obtain the codec capabilities from the stream
|
|
452
|
-
packet_source:
|
|
450
|
+
packet_source: SbcPacketSource | AacPacketSource | OpusPacketSource
|
|
453
451
|
vendor_id = 0
|
|
454
452
|
codec_id = 0
|
|
455
453
|
if audio_format == "sbc":
|
bumble/apps/rfcomm_bridge.py
CHANGED
|
@@ -17,7 +17,6 @@
|
|
|
17
17
|
# -----------------------------------------------------------------------------
|
|
18
18
|
import asyncio
|
|
19
19
|
import time
|
|
20
|
-
from typing import Optional
|
|
21
20
|
|
|
22
21
|
import click
|
|
23
22
|
|
|
@@ -82,14 +81,14 @@ class ServerBridge:
|
|
|
82
81
|
def __init__(
|
|
83
82
|
self, channel: int, uuid: str, trace: bool, tcp_host: str, tcp_port: int
|
|
84
83
|
) -> None:
|
|
85
|
-
self.device:
|
|
84
|
+
self.device: Device | None = None
|
|
86
85
|
self.channel = channel
|
|
87
86
|
self.uuid = uuid
|
|
88
87
|
self.tcp_host = tcp_host
|
|
89
88
|
self.tcp_port = tcp_port
|
|
90
|
-
self.rfcomm_channel:
|
|
91
|
-
self.tcp_tracer:
|
|
92
|
-
self.rfcomm_tracer:
|
|
89
|
+
self.rfcomm_channel: rfcomm.DLC | None = None
|
|
90
|
+
self.tcp_tracer: Tracer | None
|
|
91
|
+
self.rfcomm_tracer: Tracer | None
|
|
93
92
|
|
|
94
93
|
if trace:
|
|
95
94
|
self.tcp_tracer = Tracer(color("RFCOMM->TCP", "cyan"))
|
|
@@ -242,14 +241,14 @@ class ClientBridge:
|
|
|
242
241
|
self.tcp_port = tcp_port
|
|
243
242
|
self.authenticate = authenticate
|
|
244
243
|
self.encrypt = encrypt
|
|
245
|
-
self.device:
|
|
246
|
-
self.connection:
|
|
247
|
-
self.rfcomm_client:
|
|
248
|
-
self.rfcomm_mux:
|
|
244
|
+
self.device: Device | None = None
|
|
245
|
+
self.connection: Connection | None = None
|
|
246
|
+
self.rfcomm_client: rfcomm.Client | None
|
|
247
|
+
self.rfcomm_mux: rfcomm.Multiplexer | None
|
|
249
248
|
self.tcp_connected: bool = False
|
|
250
249
|
|
|
251
|
-
self.tcp_tracer:
|
|
252
|
-
self.rfcomm_tracer:
|
|
250
|
+
self.tcp_tracer: Tracer | None
|
|
251
|
+
self.rfcomm_tracer: Tracer | None
|
|
253
252
|
|
|
254
253
|
if trace:
|
|
255
254
|
self.tcp_tracer = Tracer(color("RFCOMM->TCP", "cyan"))
|
bumble/apps/scan.py
CHANGED
|
@@ -217,9 +217,7 @@ async def scan(
|
|
|
217
217
|
@click.option(
|
|
218
218
|
'--irk',
|
|
219
219
|
metavar='<IRK_HEX>:<ADDRESS>',
|
|
220
|
-
help=(
|
|
221
|
-
'Use this IRK for resolving private addresses ' '(may be used more than once)'
|
|
222
|
-
),
|
|
220
|
+
help=('Use this IRK for resolving private addresses (may be used more than once)'),
|
|
223
221
|
multiple=True,
|
|
224
222
|
)
|
|
225
223
|
@click.option(
|
bumble/apps/speaker/speaker.py
CHANGED
|
@@ -26,7 +26,6 @@ import pathlib
|
|
|
26
26
|
import subprocess
|
|
27
27
|
import weakref
|
|
28
28
|
from importlib import resources
|
|
29
|
-
from typing import Optional
|
|
30
29
|
|
|
31
30
|
import aiohttp
|
|
32
31
|
import click
|
|
@@ -156,7 +155,7 @@ class QueuedOutput(Output):
|
|
|
156
155
|
|
|
157
156
|
packets: asyncio.Queue
|
|
158
157
|
extractor: AudioExtractor
|
|
159
|
-
packet_pump_task:
|
|
158
|
+
packet_pump_task: asyncio.Task | None
|
|
160
159
|
started: bool
|
|
161
160
|
|
|
162
161
|
def __init__(self, extractor):
|
|
@@ -230,8 +229,8 @@ class WebSocketOutput(QueuedOutput):
|
|
|
230
229
|
class FfplayOutput(QueuedOutput):
|
|
231
230
|
MAX_QUEUE_SIZE = 32768
|
|
232
231
|
|
|
233
|
-
subprocess:
|
|
234
|
-
ffplay_task:
|
|
232
|
+
subprocess: asyncio.subprocess.Process | None
|
|
233
|
+
ffplay_task: asyncio.Task | None
|
|
235
234
|
|
|
236
235
|
def __init__(self, codec: str) -> None:
|
|
237
236
|
super().__init__(AudioExtractor.create(codec))
|
bumble/at.py
CHANGED
|
@@ -12,7 +12,6 @@
|
|
|
12
12
|
# See the License for the specific language governing permissions and
|
|
13
13
|
# limitations under the License.
|
|
14
14
|
|
|
15
|
-
from typing import Union
|
|
16
15
|
|
|
17
16
|
from bumble import core
|
|
18
17
|
|
|
@@ -36,7 +35,7 @@ def tokenize_parameters(buffer: bytes) -> list[bytes]:
|
|
|
36
35
|
|
|
37
36
|
if in_quotes:
|
|
38
37
|
token.extend(char)
|
|
39
|
-
if char == b'
|
|
38
|
+
if char == b'"':
|
|
40
39
|
in_quotes = False
|
|
41
40
|
tokens.append(token[1:-1])
|
|
42
41
|
token = bytearray()
|
|
@@ -63,18 +62,18 @@ def tokenize_parameters(buffer: bytes) -> list[bytes]:
|
|
|
63
62
|
return [bytes(token) for token in tokens if len(token) > 0]
|
|
64
63
|
|
|
65
64
|
|
|
66
|
-
def parse_parameters(buffer: bytes) -> list[
|
|
65
|
+
def parse_parameters(buffer: bytes) -> list[bytes | list]:
|
|
67
66
|
"""Parse the parameters using the comma and parenthesis separators.
|
|
68
67
|
Raises AtParsingError in case of invalid input string."""
|
|
69
68
|
|
|
70
69
|
tokens = tokenize_parameters(buffer)
|
|
71
70
|
accumulator: list[list] = [[]]
|
|
72
|
-
current:
|
|
71
|
+
current: bytes | list = b''
|
|
73
72
|
|
|
74
73
|
for token in tokens:
|
|
75
74
|
if token == b',':
|
|
76
75
|
accumulator[-1].append(current)
|
|
77
|
-
current =
|
|
76
|
+
current = b''
|
|
78
77
|
elif token == b'(':
|
|
79
78
|
accumulator.append([])
|
|
80
79
|
elif token == b')':
|