bumble 0.0.212__py3-none-any.whl → 0.0.214__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.
Files changed (92) hide show
  1. bumble/_version.py +2 -2
  2. bumble/a2dp.py +6 -0
  3. bumble/apps/README.md +0 -3
  4. bumble/apps/auracast.py +14 -11
  5. bumble/apps/bench.py +482 -37
  6. bumble/apps/console.py +3 -3
  7. bumble/apps/controller_info.py +44 -12
  8. bumble/apps/controller_loopback.py +7 -7
  9. bumble/apps/controllers.py +4 -5
  10. bumble/apps/device_info.py +4 -5
  11. bumble/apps/gatt_dump.py +5 -5
  12. bumble/apps/gg_bridge.py +5 -5
  13. bumble/apps/hci_bridge.py +5 -4
  14. bumble/apps/l2cap_bridge.py +5 -5
  15. bumble/apps/lea_unicast/app.py +8 -3
  16. bumble/apps/pair.py +19 -11
  17. bumble/apps/pandora_server.py +2 -2
  18. bumble/apps/player/player.py +2 -3
  19. bumble/apps/rfcomm_bridge.py +3 -4
  20. bumble/apps/scan.py +4 -5
  21. bumble/apps/show.py +6 -4
  22. bumble/apps/speaker/speaker.html +1 -0
  23. bumble/apps/speaker/speaker.js +113 -62
  24. bumble/apps/speaker/speaker.py +123 -19
  25. bumble/apps/unbond.py +2 -3
  26. bumble/apps/usb_probe.py +2 -3
  27. bumble/at.py +4 -4
  28. bumble/att.py +2 -6
  29. bumble/avc.py +7 -7
  30. bumble/avctp.py +3 -3
  31. bumble/avdtp.py +16 -20
  32. bumble/avrcp.py +42 -54
  33. bumble/colors.py +2 -2
  34. bumble/controller.py +174 -45
  35. bumble/device.py +398 -182
  36. bumble/drivers/__init__.py +2 -2
  37. bumble/drivers/common.py +0 -2
  38. bumble/drivers/intel.py +37 -40
  39. bumble/drivers/rtk.py +28 -35
  40. bumble/gatt.py +4 -4
  41. bumble/gatt_adapters.py +4 -5
  42. bumble/gatt_client.py +26 -31
  43. bumble/gatt_server.py +7 -11
  44. bumble/hci.py +2648 -2909
  45. bumble/helpers.py +4 -5
  46. bumble/hfp.py +32 -37
  47. bumble/host.py +104 -35
  48. bumble/keys.py +5 -5
  49. bumble/l2cap.py +312 -409
  50. bumble/link.py +16 -280
  51. bumble/logging.py +65 -0
  52. bumble/pairing.py +23 -20
  53. bumble/pandora/__init__.py +2 -2
  54. bumble/pandora/config.py +2 -2
  55. bumble/pandora/device.py +6 -6
  56. bumble/pandora/host.py +27 -28
  57. bumble/pandora/l2cap.py +2 -2
  58. bumble/pandora/security.py +6 -6
  59. bumble/pandora/utils.py +3 -3
  60. bumble/profiles/ams.py +404 -0
  61. bumble/profiles/ascs.py +142 -131
  62. bumble/profiles/asha.py +2 -2
  63. bumble/profiles/bap.py +3 -4
  64. bumble/profiles/csip.py +2 -2
  65. bumble/profiles/device_information_service.py +2 -2
  66. bumble/profiles/gap.py +2 -2
  67. bumble/profiles/hap.py +34 -33
  68. bumble/profiles/le_audio.py +4 -4
  69. bumble/profiles/mcp.py +4 -4
  70. bumble/profiles/vcs.py +3 -5
  71. bumble/rfcomm.py +10 -10
  72. bumble/rtp.py +1 -2
  73. bumble/sdp.py +2 -2
  74. bumble/smp.py +62 -63
  75. bumble/tools/intel_util.py +3 -2
  76. bumble/tools/rtk_util.py +6 -5
  77. bumble/transport/__init__.py +2 -16
  78. bumble/transport/android_netsim.py +5 -5
  79. bumble/transport/common.py +4 -4
  80. bumble/transport/pyusb.py +2 -2
  81. bumble/utils.py +2 -5
  82. bumble/vendor/android/hci.py +118 -200
  83. bumble/vendor/zephyr/hci.py +32 -27
  84. {bumble-0.0.212.dist-info → bumble-0.0.214.dist-info}/METADATA +4 -3
  85. {bumble-0.0.212.dist-info → bumble-0.0.214.dist-info}/RECORD +89 -90
  86. {bumble-0.0.212.dist-info → bumble-0.0.214.dist-info}/WHEEL +1 -1
  87. {bumble-0.0.212.dist-info → bumble-0.0.214.dist-info}/entry_points.txt +0 -1
  88. bumble/apps/link_relay/__init__.py +0 -0
  89. bumble/apps/link_relay/link_relay.py +0 -289
  90. bumble/apps/link_relay/logging.yml +0 -21
  91. {bumble-0.0.212.dist-info → bumble-0.0.214.dist-info}/licenses/LICENSE +0 -0
  92. {bumble-0.0.212.dist-info → bumble-0.0.214.dist-info}/top_level.txt +0 -0
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, PhysicalTransport
58
+ from bumble.core import UUID, AdvertisingData
59
59
  from bumble.device import (
60
60
  ConnectionParametersPreferences,
61
61
  ConnectionPHY,
@@ -64,7 +64,7 @@ from bumble.device import (
64
64
  Peer,
65
65
  )
66
66
  from bumble.utils import AsyncRunner
67
- from bumble.transport import open_transport_or_link
67
+ from bumble.transport import open_transport
68
68
  from bumble.gatt import Characteristic, Service, CharacteristicDeclaration, Descriptor
69
69
  from bumble.gatt_client import CharacteristicProxy
70
70
  from bumble.hci import (
@@ -291,7 +291,7 @@ class ConsoleApp:
291
291
  async def run_async(self, device_config, transport):
292
292
  rssi_monitoring_task = asyncio.create_task(self.rssi_monitor_loop())
293
293
 
294
- async with await open_transport_or_link(transport) as (hci_source, hci_sink):
294
+ async with await open_transport(transport) as (hci_source, hci_sink):
295
295
  if device_config:
296
296
  self.device = Device.from_config_file_with_hci(
297
297
  device_config, hci_source, hci_sink
@@ -16,8 +16,6 @@
16
16
  # Imports
17
17
  # -----------------------------------------------------------------------------
18
18
  import asyncio
19
- import os
20
- import logging
21
19
  import time
22
20
 
23
21
  import click
@@ -58,7 +56,8 @@ from bumble.hci import (
58
56
  HCI_Read_Local_Version_Information_Command,
59
57
  )
60
58
  from bumble.host import Host
61
- from bumble.transport import open_transport_or_link
59
+ from bumble.transport import open_transport
60
+ import bumble.logging
62
61
 
63
62
 
64
63
  # -----------------------------------------------------------------------------
@@ -242,28 +241,43 @@ async def get_codecs_info(host: Host) -> None:
242
241
 
243
242
 
244
243
  # -----------------------------------------------------------------------------
245
- async def async_main(latency_probes, transport):
244
+ async def async_main(
245
+ latency_probes, latency_probe_interval, latency_probe_command, transport
246
+ ):
246
247
  print('<<< connecting to HCI...')
247
- async with await open_transport_or_link(transport) as (hci_source, hci_sink):
248
+ async with await open_transport(transport) as (hci_source, hci_sink):
248
249
  print('<<< connected')
249
250
 
250
251
  host = Host(hci_source, hci_sink)
251
252
  await host.reset()
252
253
 
253
254
  # Measure the latency if requested
255
+ # (we add an extra probe at the start, that we ignore, just to ensure that
256
+ # the transport is primed)
254
257
  latencies = []
255
258
  if latency_probes:
256
- for _ in range(latency_probes):
259
+ if latency_probe_command:
260
+ probe_hci_command = HCI_Command.from_bytes(
261
+ bytes.fromhex(latency_probe_command)
262
+ )
263
+ else:
264
+ probe_hci_command = HCI_Read_Local_Version_Information_Command()
265
+
266
+ for iteration in range(1 + latency_probes):
267
+ if latency_probe_interval:
268
+ await asyncio.sleep(latency_probe_interval / 1000)
257
269
  start = time.time()
258
- await host.send_command(HCI_Read_Local_Version_Information_Command())
259
- latencies.append(1000 * (time.time() - start))
270
+ await host.send_command(probe_hci_command)
271
+ if iteration:
272
+ latencies.append(1000 * (time.time() - start))
260
273
  print(
261
274
  color('HCI Command Latency:', 'yellow'),
262
275
  (
263
276
  f'min={min(latencies):.2f}, '
264
277
  f'max={max(latencies):.2f}, '
265
- f'average={sum(latencies)/len(latencies):.2f}'
278
+ f'average={sum(latencies)/len(latencies):.2f},'
266
279
  ),
280
+ [f'{latency:.4}' for latency in latencies],
267
281
  '\n',
268
282
  )
269
283
 
@@ -311,10 +325,28 @@ async def async_main(latency_probes, transport):
311
325
  type=int,
312
326
  help='Send N commands to measure HCI transport latency statistics',
313
327
  )
328
+ @click.option(
329
+ '--latency-probe-interval',
330
+ metavar='INTERVAL',
331
+ type=int,
332
+ help='Interval between latency probes (milliseconds)',
333
+ )
334
+ @click.option(
335
+ '--latency-probe-command',
336
+ metavar='COMMAND_HEX',
337
+ help=(
338
+ 'Probe command (HCI Command packet bytes, in hex. Use 0177FC00 for'
339
+ ' a loopback test with the HCI remote proxy app)'
340
+ ),
341
+ )
314
342
  @click.argument('transport')
315
- def main(latency_probes, transport):
316
- logging.basicConfig(level=os.environ.get('BUMBLE_LOGLEVEL', 'WARNING').upper())
317
- asyncio.run(async_main(latency_probes, transport))
343
+ def main(latency_probes, latency_probe_interval, latency_probe_command, transport):
344
+ bumble.logging.setup_basic_logging()
345
+ asyncio.run(
346
+ async_main(
347
+ latency_probes, latency_probe_interval, latency_probe_command, transport
348
+ )
349
+ )
318
350
 
319
351
 
320
352
  # -----------------------------------------------------------------------------
@@ -16,10 +16,11 @@
16
16
  # Imports
17
17
  # -----------------------------------------------------------------------------
18
18
  import asyncio
19
- import logging
20
- import os
21
19
  import time
22
20
  from typing import Optional
21
+
22
+ import click
23
+
23
24
  from bumble.colors import color
24
25
  from bumble.hci import (
25
26
  HCI_READ_LOOPBACK_MODE_COMMAND,
@@ -29,8 +30,8 @@ from bumble.hci import (
29
30
  LoopbackMode,
30
31
  )
31
32
  from bumble.host import Host
32
- from bumble.transport import open_transport_or_link
33
- import click
33
+ from bumble.transport import open_transport
34
+ import bumble.logging
34
35
 
35
36
 
36
37
  class Loopback:
@@ -88,7 +89,7 @@ class Loopback:
88
89
  async def run(self):
89
90
  """Run a loopback throughput test"""
90
91
  print(color('>>> Connecting to HCI...', 'green'))
91
- async with await open_transport_or_link(self.transport) as (
92
+ async with await open_transport(self.transport) as (
92
93
  hci_source,
93
94
  hci_sink,
94
95
  ):
@@ -194,8 +195,7 @@ class Loopback:
194
195
  )
195
196
  @click.argument('transport')
196
197
  def main(packet_size, packet_count, transport):
197
- logging.basicConfig(level=os.environ.get('BUMBLE_LOGLEVEL', 'WARNING').upper())
198
-
198
+ bumble.logging.setup_basic_logging()
199
199
  loopback = Loopback(packet_size, packet_count, transport)
200
200
  asyncio.run(loopback.run())
201
201
 
@@ -15,14 +15,13 @@
15
15
  # -----------------------------------------------------------------------------
16
16
  # Imports
17
17
  # -----------------------------------------------------------------------------
18
- import logging
19
18
  import asyncio
20
19
  import sys
21
- import os
22
20
 
23
21
  from bumble.controller import Controller
24
22
  from bumble.link import LocalLink
25
- from bumble.transport import open_transport_or_link
23
+ from bumble.transport import open_transport
24
+ import bumble.logging
26
25
 
27
26
 
28
27
  # -----------------------------------------------------------------------------
@@ -42,7 +41,7 @@ async def async_main():
42
41
  transports = []
43
42
  controllers = []
44
43
  for index, transport_name in enumerate(sys.argv[1:]):
45
- transport = await open_transport_or_link(transport_name)
44
+ transport = await open_transport(transport_name)
46
45
  transports.append(transport)
47
46
  controller = Controller(
48
47
  f'C{index}',
@@ -62,7 +61,7 @@ async def async_main():
62
61
 
63
62
  # -----------------------------------------------------------------------------
64
63
  def main():
65
- logging.basicConfig(level=os.environ.get('BUMBLE_LOGLEVEL', 'INFO').upper())
64
+ bumble.logging.setup_basic_logging()
66
65
  asyncio.run(async_main())
67
66
 
68
67
 
@@ -16,8 +16,6 @@
16
16
  # Imports
17
17
  # -----------------------------------------------------------------------------
18
18
  import asyncio
19
- import os
20
- import logging
21
19
  from typing import Callable, Iterable, Optional
22
20
 
23
21
  import click
@@ -32,7 +30,8 @@ from bumble.profiles.gap import GenericAccessServiceProxy
32
30
  from bumble.profiles.pacs import PublishedAudioCapabilitiesServiceProxy
33
31
  from bumble.profiles.tmap import TelephonyAndMediaAudioServiceProxy
34
32
  from bumble.profiles.vcs import VolumeControlServiceProxy
35
- from bumble.transport import open_transport_or_link
33
+ from bumble.transport import open_transport
34
+ import bumble.logging
36
35
 
37
36
 
38
37
  # -----------------------------------------------------------------------------
@@ -215,7 +214,7 @@ async def show_device_info(peer, done: Optional[asyncio.Future]) -> None:
215
214
 
216
215
  # -----------------------------------------------------------------------------
217
216
  async def async_main(device_config, encrypt, transport, address_or_name):
218
- async with await open_transport_or_link(transport) as (hci_source, hci_sink):
217
+ async with await open_transport(transport) as (hci_source, hci_sink):
219
218
 
220
219
  # Create a device
221
220
  if device_config:
@@ -267,7 +266,7 @@ def main(device_config, encrypt, transport, address_or_name):
267
266
  Dump the GATT database on a remote device. If ADDRESS_OR_NAME is not specified,
268
267
  wait for an incoming connection.
269
268
  """
270
- logging.basicConfig(level=os.environ.get('BUMBLE_LOGLEVEL', 'INFO').upper())
269
+ bumble.logging.setup_basic_logging()
271
270
  asyncio.run(async_main(device_config, encrypt, transport, address_or_name))
272
271
 
273
272
 
bumble/apps/gatt_dump.py CHANGED
@@ -16,15 +16,15 @@
16
16
  # Imports
17
17
  # -----------------------------------------------------------------------------
18
18
  import asyncio
19
- import os
20
- import logging
19
+
21
20
  import click
22
21
 
23
22
  import bumble.core
24
23
  from bumble.colors import color
25
24
  from bumble.device import Device, Peer
26
25
  from bumble.gatt import show_services
27
- from bumble.transport import open_transport_or_link
26
+ from bumble.transport import open_transport
27
+ import bumble.logging
28
28
 
29
29
 
30
30
  # -----------------------------------------------------------------------------
@@ -60,7 +60,7 @@ async def dump_gatt_db(peer, done):
60
60
 
61
61
  # -----------------------------------------------------------------------------
62
62
  async def async_main(device_config, encrypt, transport, address_or_name):
63
- async with await open_transport_or_link(transport) as (hci_source, hci_sink):
63
+ async with await open_transport(transport) as (hci_source, hci_sink):
64
64
 
65
65
  # Create a device
66
66
  if device_config:
@@ -112,7 +112,7 @@ def main(device_config, encrypt, transport, address_or_name):
112
112
  Dump the GATT database on a remote device. If ADDRESS_OR_NAME is not specified,
113
113
  wait for an incoming connection.
114
114
  """
115
- logging.basicConfig(level=os.environ.get('BUMBLE_LOGLEVEL', 'INFO').upper())
115
+ bumble.logging.setup_basic_logging()
116
116
  asyncio.run(async_main(device_config, encrypt, transport, address_or_name))
117
117
 
118
118
 
bumble/apps/gg_bridge.py CHANGED
@@ -16,9 +16,8 @@
16
16
  # Imports
17
17
  # -----------------------------------------------------------------------------
18
18
  import asyncio
19
- import os
20
19
  import struct
21
- import logging
20
+
22
21
  import click
23
22
 
24
23
  from bumble import l2cap
@@ -27,8 +26,9 @@ from bumble.device import Device, Peer
27
26
  from bumble.core import AdvertisingData
28
27
  from bumble.gatt import Service, Characteristic, CharacteristicValue
29
28
  from bumble.utils import AsyncRunner
30
- from bumble.transport import open_transport_or_link
29
+ from bumble.transport import open_transport
31
30
  from bumble.hci import HCI_Constant
31
+ import bumble.logging
32
32
 
33
33
 
34
34
  # -----------------------------------------------------------------------------
@@ -325,7 +325,7 @@ async def run(
325
325
  receive_port,
326
326
  ):
327
327
  print('<<< connecting to HCI...')
328
- async with await open_transport_or_link(hci_transport) as (hci_source, hci_sink):
328
+ async with await open_transport(hci_transport) as (hci_source, hci_sink):
329
329
  print('<<< connected')
330
330
 
331
331
  # Instantiate a bridge object
@@ -383,6 +383,7 @@ def main(
383
383
  receive_host,
384
384
  receive_port,
385
385
  ):
386
+ bumble.logging.setup_basic_logging('WARNING')
386
387
  asyncio.run(
387
388
  run(
388
389
  hci_transport,
@@ -397,6 +398,5 @@ def main(
397
398
 
398
399
 
399
400
  # -----------------------------------------------------------------------------
400
- logging.basicConfig(level=os.environ.get('BUMBLE_LOGLEVEL', 'WARNING').upper())
401
401
  if __name__ == '__main__':
402
402
  main()
bumble/apps/hci_bridge.py CHANGED
@@ -17,11 +17,12 @@
17
17
  # -----------------------------------------------------------------------------
18
18
  import logging
19
19
  import asyncio
20
- import os
21
20
  import sys
22
21
 
23
22
  from bumble import hci, transport
24
23
  from bumble.bridge import HCI_Bridge
24
+ import bumble.logging
25
+
25
26
 
26
27
  # -----------------------------------------------------------------------------
27
28
  # Logging
@@ -46,14 +47,14 @@ async def async_main():
46
47
  return
47
48
 
48
49
  print('>>> connecting to HCI...')
49
- async with await transport.open_transport_or_link(sys.argv[1]) as (
50
+ async with await transport.open_transport(sys.argv[1]) as (
50
51
  hci_host_source,
51
52
  hci_host_sink,
52
53
  ):
53
54
  print('>>> connected')
54
55
 
55
56
  print('>>> connecting to HCI...')
56
- async with await transport.open_transport_or_link(sys.argv[2]) as (
57
+ async with await transport.open_transport(sys.argv[2]) as (
57
58
  hci_controller_source,
58
59
  hci_controller_sink,
59
60
  ):
@@ -100,7 +101,7 @@ async def async_main():
100
101
 
101
102
  # -----------------------------------------------------------------------------
102
103
  def main():
103
- logging.basicConfig(level=os.environ.get('BUMBLE_LOGLEVEL', 'INFO').upper())
104
+ bumble.logging.setup_basic_logging()
104
105
  asyncio.run(async_main())
105
106
 
106
107
 
@@ -16,16 +16,16 @@
16
16
  # Imports
17
17
  # -----------------------------------------------------------------------------
18
18
  import asyncio
19
- import logging
20
- import os
19
+
21
20
  import click
22
21
 
23
22
  from bumble import l2cap
24
23
  from bumble.colors import color
25
- from bumble.transport import open_transport_or_link
24
+ from bumble.transport import open_transport
26
25
  from bumble.device import Device
27
26
  from bumble.utils import FlowControlAsyncPipe
28
27
  from bumble.hci import HCI_Constant
28
+ import bumble.logging
29
29
 
30
30
 
31
31
  # -----------------------------------------------------------------------------
@@ -258,7 +258,7 @@ class ClientBridge:
258
258
  # -----------------------------------------------------------------------------
259
259
  async def run(device_config, hci_transport, bridge):
260
260
  print('<<< connecting to HCI...')
261
- async with await open_transport_or_link(hci_transport) as (hci_source, hci_sink):
261
+ async with await open_transport(hci_transport) as (hci_source, hci_sink):
262
262
  print('<<< connected')
263
263
 
264
264
  device = Device.from_config_file_with_hci(device_config, hci_source, hci_sink)
@@ -356,6 +356,6 @@ def client(context, bluetooth_address, tcp_host, tcp_port):
356
356
 
357
357
 
358
358
  # -----------------------------------------------------------------------------
359
- logging.basicConfig(level=os.environ.get('BUMBLE_LOGLEVEL', 'WARNING').upper())
360
359
  if __name__ == '__main__':
360
+ bumble.logging.setup_basic_logging('WARNING')
361
361
  cli(obj={}) # pylint: disable=no-value-for-parameter
@@ -22,7 +22,6 @@ import datetime
22
22
  import functools
23
23
  from importlib import resources
24
24
  import json
25
- import os
26
25
  import logging
27
26
  import pathlib
28
27
  import weakref
@@ -44,6 +43,7 @@ from bumble.device import Device, DeviceConfiguration, AdvertisingParameters, Ci
44
43
  from bumble.transport import open_transport
45
44
  from bumble.profiles import ascs, bap, pacs
46
45
  from bumble.hci import Address, CodecID, CodingFormat, HCI_IsoDataPacket
46
+ import bumble.logging
47
47
 
48
48
 
49
49
  # -----------------------------------------------------------------------------
@@ -337,7 +337,12 @@ class Speaker:
337
337
  ),
338
338
  (
339
339
  AdvertisingData.FLAGS,
340
- bytes([AdvertisingData.LE_GENERAL_DISCOVERABLE_MODE_FLAG]),
340
+ bytes(
341
+ [
342
+ AdvertisingData.LE_GENERAL_DISCOVERABLE_MODE_FLAG
343
+ | AdvertisingData.BR_EDR_NOT_SUPPORTED_FLAG
344
+ ]
345
+ ),
341
346
  ),
342
347
  (
343
348
  AdvertisingData.INCOMPLETE_LIST_OF_16_BIT_SERVICE_CLASS_UUIDS,
@@ -449,7 +454,7 @@ def speaker(ui_port: int, device_config: str, transport: str, lc3_file: str) ->
449
454
 
450
455
  # -----------------------------------------------------------------------------
451
456
  def main():
452
- logging.basicConfig(level=os.environ.get('BUMBLE_LOGLEVEL', 'INFO').upper())
457
+ bumble.logging.setup_basic_logging()
453
458
  speaker()
454
459
 
455
460
 
bumble/apps/pair.py CHANGED
@@ -26,7 +26,7 @@ from prompt_toolkit.shortcuts import PromptSession
26
26
  from bumble.a2dp import make_audio_sink_service_sdp_records
27
27
  from bumble.colors import color
28
28
  from bumble.device import Device, Peer
29
- from bumble.transport import open_transport_or_link
29
+ from bumble.transport import open_transport
30
30
  from bumble.pairing import OobData, PairingDelegate, PairingConfig
31
31
  from bumble.smp import OobContext, OobLegacyContext
32
32
  from bumble.smp import error_name as smp_error_name
@@ -349,7 +349,7 @@ async def pair(
349
349
  Waiter.instance = Waiter(linger=linger)
350
350
 
351
351
  print('<<< connecting to HCI...')
352
- async with await open_transport_or_link(hci_transport) as (hci_source, hci_sink):
352
+ async with await open_transport(hci_transport) as (hci_source, hci_sink):
353
353
  print('<<< connected')
354
354
 
355
355
  # Create a device to manage the host
@@ -402,14 +402,19 @@ async def pair(
402
402
  # Create an OOB context if needed
403
403
  if oob:
404
404
  our_oob_context = OobContext()
405
- shared_data = (
406
- None
407
- if oob == '-'
408
- else OobData.from_ad(
405
+ if oob == '-':
406
+ shared_data = None
407
+ legacy_context = OobLegacyContext()
408
+ else:
409
+ oob_data = OobData.from_ad(
409
410
  AdvertisingData.from_bytes(bytes.fromhex(oob))
410
- ).shared_data
411
- )
412
- legacy_context = OobLegacyContext()
411
+ )
412
+ shared_data = oob_data.shared_data
413
+ legacy_context = oob_data.legacy_context
414
+ if legacy_context is None and not sc:
415
+ print(color('OOB pairing in legacy mode requires TK', 'red'))
416
+ return
417
+
413
418
  oob_contexts = PairingConfig.OobConfig(
414
419
  our_context=our_oob_context,
415
420
  peer_data=shared_data,
@@ -419,7 +424,9 @@ async def pair(
419
424
  print(color('@@@ OOB Data:', 'yellow'))
420
425
  if shared_data is None:
421
426
  oob_data = OobData(
422
- address=device.random_address, shared_data=our_oob_context.share()
427
+ address=device.random_address,
428
+ shared_data=our_oob_context.share(),
429
+ legacy_context=(None if sc else legacy_context),
423
430
  )
424
431
  print(
425
432
  color(
@@ -427,7 +434,8 @@ async def pair(
427
434
  'yellow',
428
435
  )
429
436
  )
430
- print(color(f'@@@ TK={legacy_context.tk.hex()}', 'yellow'))
437
+ if legacy_context:
438
+ print(color(f'@@@ TK={legacy_context.tk.hex()}', 'yellow'))
431
439
  print(color('@@@-----------------------------------', 'yellow'))
432
440
  else:
433
441
  oob_contexts = None
@@ -4,7 +4,7 @@ import logging
4
4
  import json
5
5
 
6
6
  from bumble.pandora import PandoraDevice, Config, serve
7
- from typing import Dict, Any
7
+ from typing import Any
8
8
 
9
9
  BUMBLE_SERVER_GRPC_PORT = 7999
10
10
  ROOTCANAL_PORT_CUTTLEFISH = 7300
@@ -39,7 +39,7 @@ def main(grpc_port: int, rootcanal_port: int, transport: str, config: str) -> No
39
39
  asyncio.run(serve(device, config=server_config, port=grpc_port))
40
40
 
41
41
 
42
- def retrieve_config(config: str) -> Dict[str, Any]:
42
+ def retrieve_config(config: str) -> dict[str, Any]:
43
43
  if not config:
44
44
  return {}
45
45
 
@@ -17,8 +17,6 @@
17
17
  # -----------------------------------------------------------------------------
18
18
  from __future__ import annotations
19
19
  import asyncio
20
- import asyncio.subprocess
21
- import os
22
20
  import logging
23
21
  from typing import Optional, Union
24
22
 
@@ -63,6 +61,7 @@ from bumble.hci import Address, HCI_CONNECTION_ALREADY_EXISTS_ERROR, HCI_Constan
63
61
  from bumble.pairing import PairingConfig
64
62
  from bumble.transport import open_transport
65
63
  from bumble.utils import AsyncRunner
64
+ import bumble.logging
66
65
 
67
66
 
68
67
  # -----------------------------------------------------------------------------
@@ -599,7 +598,7 @@ def play(context, address, audio_format, audio_file):
599
598
 
600
599
  # -----------------------------------------------------------------------------
601
600
  def main():
602
- logging.basicConfig(level=os.environ.get("BUMBLE_LOGLEVEL", "WARNING").upper())
601
+ bumble.logging.setup_basic_logging("WARNING")
603
602
  player_cli()
604
603
 
605
604
 
@@ -16,8 +16,6 @@
16
16
  # Imports
17
17
  # -----------------------------------------------------------------------------
18
18
  import asyncio
19
- import logging
20
- import os
21
19
  import time
22
20
  from typing import Optional
23
21
 
@@ -30,6 +28,7 @@ from bumble import hci
30
28
  from bumble import rfcomm
31
29
  from bumble import transport
32
30
  from bumble import utils
31
+ import bumble.logging
33
32
 
34
33
 
35
34
  # -----------------------------------------------------------------------------
@@ -406,7 +405,7 @@ class ClientBridge:
406
405
  # -----------------------------------------------------------------------------
407
406
  async def run(device_config, hci_transport, bridge):
408
407
  print("<<< connecting to HCI...")
409
- async with await transport.open_transport_or_link(hci_transport) as (
408
+ async with await transport.open_transport(hci_transport) as (
410
409
  hci_source,
411
410
  hci_sink,
412
411
  ):
@@ -515,6 +514,6 @@ def client(context, bluetooth_address, tcp_host, tcp_port, authenticate, encrypt
515
514
 
516
515
 
517
516
  # -----------------------------------------------------------------------------
518
- logging.basicConfig(level=os.environ.get("BUMBLE_LOGLEVEL", "WARNING").upper())
519
517
  if __name__ == "__main__":
518
+ bumble.logging.setup_basic_logging("WARNING")
520
519
  cli(obj={}) # pylint: disable=no-value-for-parameter
bumble/apps/scan.py CHANGED
@@ -16,17 +16,16 @@
16
16
  # Imports
17
17
  # -----------------------------------------------------------------------------
18
18
  import asyncio
19
- import os
20
- import logging
21
19
  import click
22
20
 
23
21
  from bumble.colors import color
24
22
  from bumble.device import Device
25
- from bumble.transport import open_transport_or_link
23
+ from bumble.transport import open_transport
26
24
  from bumble.keys import JsonKeyStore
27
25
  from bumble.smp import AddressResolver
28
26
  from bumble.device import Advertisement
29
27
  from bumble.hci import Address, HCI_Constant, HCI_LE_1M_PHY, HCI_LE_CODED_PHY
28
+ import bumble.logging
30
29
 
31
30
 
32
31
  # -----------------------------------------------------------------------------
@@ -127,7 +126,7 @@ async def scan(
127
126
  transport,
128
127
  ):
129
128
  print('<<< connecting to HCI...')
130
- async with await open_transport_or_link(transport) as (hci_source, hci_sink):
129
+ async with await open_transport(transport) as (hci_source, hci_sink):
131
130
  print('<<< connected')
132
131
 
133
132
  if device_config:
@@ -237,7 +236,7 @@ def main(
237
236
  device_config,
238
237
  transport,
239
238
  ):
240
- logging.basicConfig(level=os.environ.get('BUMBLE_LOGLEVEL', 'WARNING').upper())
239
+ bumble.logging.setup_basic_logging('WARNING')
241
240
  asyncio.run(
242
241
  scan(
243
242
  min_rssi,
bumble/apps/show.py CHANGED
@@ -16,8 +16,8 @@
16
16
  # Imports
17
17
  # -----------------------------------------------------------------------------
18
18
  import datetime
19
+ import importlib
19
20
  import logging
20
- import os
21
21
  import struct
22
22
 
23
23
  import click
@@ -26,6 +26,7 @@ from bumble.colors import color
26
26
  from bumble import hci
27
27
  from bumble.transport.common import PacketReader
28
28
  from bumble.helpers import PacketTracer
29
+ import bumble.logging
29
30
 
30
31
 
31
32
  # -----------------------------------------------------------------------------
@@ -154,9 +155,10 @@ class Printer:
154
155
  def main(format, vendor, filename):
155
156
  for vendor_name in vendor:
156
157
  if vendor_name == 'android':
157
- import bumble.vendor.android.hci
158
+ # Prevent being deleted by linter.
159
+ importlib.import_module('bumble.vendor.android.hci')
158
160
  elif vendor_name == 'zephyr':
159
- import bumble.vendor.zephyr.hci
161
+ importlib.import_module('bumble.vendor.zephyr.hci')
160
162
 
161
163
  input = open(filename, 'rb')
162
164
  if format == 'h4':
@@ -186,5 +188,5 @@ def main(format, vendor, filename):
186
188
 
187
189
  # -----------------------------------------------------------------------------
188
190
  if __name__ == '__main__':
189
- logging.basicConfig(level=os.environ.get('BUMBLE_LOGLEVEL', 'WARNING').upper())
191
+ bumble.logging.setup_basic_logging('WARNING')
190
192
  main() # pylint: disable=no-value-for-parameter
@@ -15,6 +15,7 @@
15
15
  <tr><td>Codec</td><td><span id="codecText"></span></td></tr>
16
16
  <tr><td>Packets</td><td><span id="packetsReceivedText"></span></td></tr>
17
17
  <tr><td>Bytes</td><td><span id="bytesReceivedText"></span></td></tr>
18
+ <tr><td>Bitrate</td><td><span id="bitrate"></span></td></tr>
18
19
  </table>
19
20
  </td>
20
21
  <td>