Compare commits

...

6 Commits

Author SHA1 Message Date
Gilles Boccon-Gibod
a6ead0147e fix #722 2025-07-28 13:36:55 -07:00
Gilles Boccon-Gibod
0665e9ca5c Merge pull request #731 from google/gbg/common-logger
use common logger
2025-07-28 10:22:30 -07:00
Gilles Boccon-Gibod
b8b78ca1ee add missing file 2025-07-27 15:02:42 -07:00
Gilles Boccon-Gibod
d611d25802 resolve merge conflicts 2025-07-26 21:20:52 -07:00
zxzxwu
cce2e4d4e3 Merge pull request #729 from zxzxwu/link
Remove link-relay and RemoteLink
2025-07-23 14:35:59 +08:00
Josh Wu
1b44e73f90 Remove link-relay and RemoteLink 2025-07-21 12:37:55 +08:00
86 changed files with 443 additions and 1132 deletions

View File

@@ -102,5 +102,7 @@
"."
],
"python.testing.unittestEnabled": false,
"python.testing.pytestEnabled": true
"python.testing.pytestEnabled": true,
"python-envs.defaultEnvManager": "ms-python.python:system",
"python-envs.pythonProjects": []
}

View File

@@ -12,9 +12,6 @@ Apps
## `show.py`
Parse a file with HCI packets and print the details of each packet in a human readable form
## `link_relay.py`
Simple WebSocket relay for virtual RemoteLink instances to communicate with each other through.
## `hci_bridge.py`
This app acts as a simple bridge between two HCI transports, with a host on one side and
a controller on the other. All the HCI packets bridged between the two are printed on the console

View File

@@ -23,7 +23,6 @@ import contextlib
import dataclasses
import functools
import logging
import os
import struct
from typing import (
Any,
@@ -54,6 +53,8 @@ from bumble.profiles import bass
import bumble.device
import bumble.transport
import bumble.utils
import bumble.logging
# -----------------------------------------------------------------------------
# Logging
@@ -1235,7 +1236,7 @@ def transmit(
def main():
logging.basicConfig(level=os.environ.get('BUMBLE_LOGLEVEL', 'INFO').upper())
bumble.logging.setup_basic_logging()
auracast()

View File

@@ -19,7 +19,6 @@ import asyncio
import dataclasses
import enum
import logging
import os
import statistics
import struct
import time
@@ -65,11 +64,12 @@ from bumble.sdp import (
DataElement,
ServiceAttribute,
)
from bumble.transport import open_transport_or_link
from bumble.transport import open_transport
import bumble.rfcomm
import bumble.core
from bumble.utils import AsyncRunner
from bumble.pairing import PairingConfig
import bumble.logging
# -----------------------------------------------------------------------------
@@ -1470,7 +1470,7 @@ class Central(Connection.Listener):
async def run(self):
logging.info(color('>>> Connecting to HCI...', 'green'))
async with await open_transport_or_link(self.transport) as (
async with await open_transport(self.transport) as (
hci_source,
hci_sink,
):
@@ -1705,7 +1705,7 @@ class Peripheral(Device.Listener, Connection.Listener):
async def run(self):
logging.info(color('>>> Connecting to HCI...', 'green'))
async with await open_transport_or_link(self.transport) as (
async with await open_transport(self.transport) as (
hci_source,
hci_sink,
):
@@ -2321,11 +2321,7 @@ def peripheral(ctx, transport):
def main():
logging.basicConfig(
level=os.environ.get('BUMBLE_LOGLEVEL', 'INFO').upper(),
format="[%(asctime)s.%(msecs)03d] %(levelname)s:%(name)s:%(message)s",
datefmt="%H:%M:%S",
)
bumble.logging.setup_basic_logging('INFO')
bench()

View File

@@ -64,7 +64,7 @@ from bumble.device import (
Peer,
)
from bumble.utils import AsyncRunner
from bumble.transport import open_transport_or_link
from bumble.transport import open_transport
from bumble.gatt import Characteristic, Service, CharacteristicDeclaration, Descriptor
from bumble.gatt_client import CharacteristicProxy
from bumble.hci import (
@@ -291,7 +291,7 @@ class ConsoleApp:
async def run_async(self, device_config, transport):
rssi_monitoring_task = asyncio.create_task(self.rssi_monitor_loop())
async with await open_transport_or_link(transport) as (hci_source, hci_sink):
async with await open_transport(transport) as (hci_source, hci_sink):
if device_config:
self.device = Device.from_config_file_with_hci(
device_config, hci_source, hci_sink

View File

@@ -16,8 +16,6 @@
# Imports
# -----------------------------------------------------------------------------
import asyncio
import os
import logging
import time
import click
@@ -58,7 +56,8 @@ from bumble.hci import (
HCI_Read_Local_Version_Information_Command,
)
from bumble.host import Host
from bumble.transport import open_transport_or_link
from bumble.transport import open_transport
import bumble.logging
# -----------------------------------------------------------------------------
@@ -246,7 +245,7 @@ async def async_main(
latency_probes, latency_probe_interval, latency_probe_command, transport
):
print('<<< connecting to HCI...')
async with await open_transport_or_link(transport) as (hci_source, hci_sink):
async with await open_transport(transport) as (hci_source, hci_sink):
print('<<< connected')
host = Host(hci_source, hci_sink)
@@ -342,11 +341,7 @@ async def async_main(
)
@click.argument('transport')
def main(latency_probes, latency_probe_interval, latency_probe_command, transport):
logging.basicConfig(
level=os.environ.get('BUMBLE_LOGLEVEL', 'INFO').upper(),
format="[%(asctime)s.%(msecs)03d] %(levelname)s:%(name)s:%(message)s",
datefmt="%H:%M:%S",
)
bumble.logging.setup_basic_logging()
asyncio.run(
async_main(
latency_probes, latency_probe_interval, latency_probe_command, transport

View File

@@ -16,10 +16,11 @@
# Imports
# -----------------------------------------------------------------------------
import asyncio
import logging
import os
import time
from typing import Optional
import click
from bumble.colors import color
from bumble.hci import (
HCI_READ_LOOPBACK_MODE_COMMAND,
@@ -29,8 +30,8 @@ from bumble.hci import (
LoopbackMode,
)
from bumble.host import Host
from bumble.transport import open_transport_or_link
import click
from bumble.transport import open_transport
import bumble.logging
class Loopback:
@@ -88,7 +89,7 @@ class Loopback:
async def run(self):
"""Run a loopback throughput test"""
print(color('>>> Connecting to HCI...', 'green'))
async with await open_transport_or_link(self.transport) as (
async with await open_transport(self.transport) as (
hci_source,
hci_sink,
):
@@ -194,12 +195,7 @@ class Loopback:
)
@click.argument('transport')
def main(packet_size, packet_count, transport):
logging.basicConfig(
level=os.environ.get('BUMBLE_LOGLEVEL', 'INFO').upper(),
format="[%(asctime)s.%(msecs)03d] %(levelname)s:%(name)s:%(message)s",
datefmt="%H:%M:%S",
)
bumble.logging.setup_basic_logging()
loopback = Loopback(packet_size, packet_count, transport)
asyncio.run(loopback.run())

View File

@@ -15,14 +15,13 @@
# -----------------------------------------------------------------------------
# Imports
# -----------------------------------------------------------------------------
import logging
import asyncio
import sys
import os
from bumble.controller import Controller
from bumble.link import LocalLink
from bumble.transport import open_transport_or_link
from bumble.transport import open_transport
import bumble.logging
# -----------------------------------------------------------------------------
@@ -42,7 +41,7 @@ async def async_main():
transports = []
controllers = []
for index, transport_name in enumerate(sys.argv[1:]):
transport = await open_transport_or_link(transport_name)
transport = await open_transport(transport_name)
transports.append(transport)
controller = Controller(
f'C{index}',
@@ -62,7 +61,7 @@ async def async_main():
# -----------------------------------------------------------------------------
def main():
logging.basicConfig(level=os.environ.get('BUMBLE_LOGLEVEL', 'INFO').upper())
bumble.logging.setup_basic_logging()
asyncio.run(async_main())

View File

@@ -16,8 +16,6 @@
# Imports
# -----------------------------------------------------------------------------
import asyncio
import os
import logging
from typing import Callable, Iterable, Optional
import click
@@ -32,7 +30,8 @@ from bumble.profiles.gap import GenericAccessServiceProxy
from bumble.profiles.pacs import PublishedAudioCapabilitiesServiceProxy
from bumble.profiles.tmap import TelephonyAndMediaAudioServiceProxy
from bumble.profiles.vcs import VolumeControlServiceProxy
from bumble.transport import open_transport_or_link
from bumble.transport import open_transport
import bumble.logging
# -----------------------------------------------------------------------------
@@ -215,7 +214,7 @@ async def show_device_info(peer, done: Optional[asyncio.Future]) -> None:
# -----------------------------------------------------------------------------
async def async_main(device_config, encrypt, transport, address_or_name):
async with await open_transport_or_link(transport) as (hci_source, hci_sink):
async with await open_transport(transport) as (hci_source, hci_sink):
# Create a device
if device_config:
@@ -267,7 +266,7 @@ def main(device_config, encrypt, transport, address_or_name):
Dump the GATT database on a remote device. If ADDRESS_OR_NAME is not specified,
wait for an incoming connection.
"""
logging.basicConfig(level=os.environ.get('BUMBLE_LOGLEVEL', 'INFO').upper())
bumble.logging.setup_basic_logging()
asyncio.run(async_main(device_config, encrypt, transport, address_or_name))

View File

@@ -16,15 +16,15 @@
# Imports
# -----------------------------------------------------------------------------
import asyncio
import os
import logging
import click
import bumble.core
from bumble.colors import color
from bumble.device import Device, Peer
from bumble.gatt import show_services
from bumble.transport import open_transport_or_link
from bumble.transport import open_transport
import bumble.logging
# -----------------------------------------------------------------------------
@@ -60,7 +60,7 @@ async def dump_gatt_db(peer, done):
# -----------------------------------------------------------------------------
async def async_main(device_config, encrypt, transport, address_or_name):
async with await open_transport_or_link(transport) as (hci_source, hci_sink):
async with await open_transport(transport) as (hci_source, hci_sink):
# Create a device
if device_config:
@@ -112,7 +112,7 @@ def main(device_config, encrypt, transport, address_or_name):
Dump the GATT database on a remote device. If ADDRESS_OR_NAME is not specified,
wait for an incoming connection.
"""
logging.basicConfig(level=os.environ.get('BUMBLE_LOGLEVEL', 'INFO').upper())
bumble.logging.setup_basic_logging()
asyncio.run(async_main(device_config, encrypt, transport, address_or_name))

View File

@@ -16,9 +16,8 @@
# Imports
# -----------------------------------------------------------------------------
import asyncio
import os
import struct
import logging
import click
from bumble import l2cap
@@ -27,8 +26,9 @@ from bumble.device import Device, Peer
from bumble.core import AdvertisingData
from bumble.gatt import Service, Characteristic, CharacteristicValue
from bumble.utils import AsyncRunner
from bumble.transport import open_transport_or_link
from bumble.transport import open_transport
from bumble.hci import HCI_Constant
import bumble.logging
# -----------------------------------------------------------------------------
@@ -325,7 +325,7 @@ async def run(
receive_port,
):
print('<<< connecting to HCI...')
async with await open_transport_or_link(hci_transport) as (hci_source, hci_sink):
async with await open_transport(hci_transport) as (hci_source, hci_sink):
print('<<< connected')
# Instantiate a bridge object
@@ -383,6 +383,7 @@ def main(
receive_host,
receive_port,
):
bumble.logging.setup_basic_logging('WARNING')
asyncio.run(
run(
hci_transport,
@@ -397,6 +398,5 @@ def main(
# -----------------------------------------------------------------------------
logging.basicConfig(level=os.environ.get('BUMBLE_LOGLEVEL', 'WARNING').upper())
if __name__ == '__main__':
main()

View File

@@ -17,11 +17,12 @@
# -----------------------------------------------------------------------------
import logging
import asyncio
import os
import sys
from bumble import hci, transport
from bumble.bridge import HCI_Bridge
import bumble.logging
# -----------------------------------------------------------------------------
# Logging
@@ -46,14 +47,14 @@ async def async_main():
return
print('>>> connecting to HCI...')
async with await transport.open_transport_or_link(sys.argv[1]) as (
async with await transport.open_transport(sys.argv[1]) as (
hci_host_source,
hci_host_sink,
):
print('>>> connected')
print('>>> connecting to HCI...')
async with await transport.open_transport_or_link(sys.argv[2]) as (
async with await transport.open_transport(sys.argv[2]) as (
hci_controller_source,
hci_controller_sink,
):
@@ -100,7 +101,7 @@ async def async_main():
# -----------------------------------------------------------------------------
def main():
logging.basicConfig(level=os.environ.get('BUMBLE_LOGLEVEL', 'INFO').upper())
bumble.logging.setup_basic_logging()
asyncio.run(async_main())

View File

@@ -16,16 +16,16 @@
# Imports
# -----------------------------------------------------------------------------
import asyncio
import logging
import os
import click
from bumble import l2cap
from bumble.colors import color
from bumble.transport import open_transport_or_link
from bumble.transport import open_transport
from bumble.device import Device
from bumble.utils import FlowControlAsyncPipe
from bumble.hci import HCI_Constant
import bumble.logging
# -----------------------------------------------------------------------------
@@ -258,7 +258,7 @@ class ClientBridge:
# -----------------------------------------------------------------------------
async def run(device_config, hci_transport, bridge):
print('<<< connecting to HCI...')
async with await open_transport_or_link(hci_transport) as (hci_source, hci_sink):
async with await open_transport(hci_transport) as (hci_source, hci_sink):
print('<<< connected')
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):
# -----------------------------------------------------------------------------
logging.basicConfig(level=os.environ.get('BUMBLE_LOGLEVEL', 'WARNING').upper())
if __name__ == '__main__':
bumble.logging.setup_basic_logging('WARNING')
cli(obj={}) # pylint: disable=no-value-for-parameter

View File

@@ -22,7 +22,6 @@ import datetime
import functools
from importlib import resources
import json
import os
import logging
import pathlib
import weakref
@@ -44,6 +43,7 @@ from bumble.device import Device, DeviceConfiguration, AdvertisingParameters, Ci
from bumble.transport import open_transport
from bumble.profiles import ascs, bap, pacs
from bumble.hci import Address, CodecID, CodingFormat, HCI_IsoDataPacket
import bumble.logging
# -----------------------------------------------------------------------------
@@ -454,7 +454,7 @@ def speaker(ui_port: int, device_config: str, transport: str, lc3_file: str) ->
# -----------------------------------------------------------------------------
def main():
logging.basicConfig(level=os.environ.get('BUMBLE_LOGLEVEL', 'INFO').upper())
bumble.logging.setup_basic_logging()
speaker()

View File

@@ -1,289 +0,0 @@
# Copyright 2021-2022 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ----------------------------------------------------------------------------
# Imports
# ----------------------------------------------------------------------------
import sys
import logging
import json
import asyncio
import argparse
import uuid
import os
from urllib.parse import urlparse
import websockets
from bumble.colors import color
# -----------------------------------------------------------------------------
# Logging
# -----------------------------------------------------------------------------
logger = logging.getLogger(__name__)
# ----------------------------------------------------------------------------
# Constants
# ----------------------------------------------------------------------------
DEFAULT_RELAY_PORT = 10723
# ----------------------------------------------------------------------------
# Utils
# ----------------------------------------------------------------------------
def error_to_json(error):
return json.dumps({'error': error})
def error_to_result(error):
return f'result:{error_to_json(error)}'
async def broadcast_message(message, connections):
# Send to all the connections
tasks = [connection.send_message(message) for connection in connections]
if tasks:
await asyncio.gather(*tasks)
# ----------------------------------------------------------------------------
# Connection class
# ----------------------------------------------------------------------------
class Connection:
"""
A Connection represents a client connected to the relay over a websocket
"""
def __init__(self, room, websocket):
self.room = room
self.websocket = websocket
self.address = str(uuid.uuid4())
async def send_message(self, message):
try:
logger.debug(color(f'->{self.address}: {message}', 'yellow'))
return await self.websocket.send(message)
except websockets.exceptions.WebSocketException as error:
logger.info(f'! client "{self}" disconnected: {error}')
await self.cleanup()
async def send_error(self, error):
return await self.send_message(f'result:{error_to_json(error)}')
async def receive_message(self):
try:
message = await self.websocket.recv()
logger.debug(color(f'<-{self.address}: {message}', 'blue'))
return message
except websockets.exceptions.WebSocketException as error:
logger.info(color(f'! client "{self}" disconnected: {error}', 'red'))
await self.cleanup()
async def cleanup(self):
if self.room:
await self.room.remove_connection(self)
def set_address(self, address):
logger.info(f'Connection address changed: {self.address} -> {address}')
self.address = address
def __str__(self):
return (
f'Connection(address="{self.address}", '
f'client={self.websocket.remote_address[0]}:'
f'{self.websocket.remote_address[1]})'
)
# ----------------------------------------------------------------------------
# Room class
# ----------------------------------------------------------------------------
class Room:
"""
A Room is a collection of bridged connections
"""
def __init__(self, relay, name):
self.relay = relay
self.name = name
self.observers = []
self.connections = []
async def add_connection(self, connection):
logger.info(f'New participant in {self.name}: {connection}')
self.connections.append(connection)
await self.broadcast_message(connection, f'joined:{connection.address}')
async def remove_connection(self, connection):
if connection in self.connections:
self.connections.remove(connection)
await self.broadcast_message(connection, f'left:{connection.address}')
def find_connections_by_address(self, address):
return [c for c in self.connections if c.address == address]
async def bridge_connection(self, connection):
while True:
# Wait for a message
message = await connection.receive_message()
# Skip empty messages
if message is None:
return
# Parse the message to decide how to handle it
if message.startswith('@'):
# This is a targeted message
await self.on_targeted_message(connection, message)
elif message.startswith('/'):
# This is an RPC request
await self.on_rpc_request(connection, message)
else:
await connection.send_message(
f'result:{error_to_json("error: invalid message")}'
)
async def broadcast_message(self, sender, message):
'''
Send to all connections in the room except back to the sender
'''
await broadcast_message(message, [c for c in self.connections if c != sender])
async def on_rpc_request(self, connection, message):
command, *params = message.split(' ', 1)
if handler := getattr(
self, f'on_{command[1:].lower().replace("-","_")}_command', None
):
try:
result = await handler(connection, params)
except Exception as error:
result = error_to_result(error)
else:
result = error_to_result('unknown command')
await connection.send_message(result or 'result:{}')
async def on_targeted_message(self, connection, message):
target, *payload = message.split(' ', 1)
if not payload:
return error_to_json('missing arguments')
payload = payload[0]
target = target[1:]
# Determine what targets to send to
if target == '*':
# Send to all connections in the room except the connection from which the
# message was received
connections = [c for c in self.connections if c != connection]
else:
connections = self.find_connections_by_address(target)
if not connections:
# Unicast with no recipient, let the sender know
await connection.send_message(f'unreachable:{target}')
# Send to targets
await broadcast_message(f'message:{connection.address}/{payload}', connections)
async def on_set_address_command(self, connection, params):
if not params:
return error_to_result('missing address')
current_address = connection.address
new_address = params[0]
connection.set_address(new_address)
await self.broadcast_message(
connection, f'address-changed:from={current_address},to={new_address}'
)
# ----------------------------------------------------------------------------
class Relay:
"""
A relay accepts connections with the following url: ws://<hostname>/<room>.
Participants in a room can communicate with each other
"""
def __init__(self, port):
self.port = port
self.rooms = {}
self.observers = []
def start(self):
logger.info(f'Starting Relay on port {self.port}')
# pylint: disable-next=no-member
return websockets.serve(self.serve, '0.0.0.0', self.port, ping_interval=None)
async def serve_as_controller(self, connection):
pass
async def serve(self, websocket, path):
logger.debug(f'New connection with path {path}')
# Parse the path
parsed = urlparse(path)
# Check if this is a controller client
if parsed.path == '/':
return await self.serve_as_controller(Connection('', websocket))
# Find or create a room for this connection
room_name = parsed.path[1:].split('/')[0]
if room_name not in self.rooms:
self.rooms[room_name] = Room(self, room_name)
room = self.rooms[room_name]
# Add the connection to the room
connection = Connection(room, websocket)
await room.add_connection(connection)
# Bridge until the connection is closed
await room.bridge_connection(connection)
# ----------------------------------------------------------------------------
def main():
# Check the Python version
if sys.version_info < (3, 6, 1):
print('ERROR: Python 3.6.1 or higher is required')
sys.exit(1)
logging.basicConfig(level=os.environ.get('BUMBLE_LOGLEVEL', 'INFO').upper())
# Parse arguments
arg_parser = argparse.ArgumentParser(description='Bumble Link Relay')
arg_parser.add_argument('--log-level', default='INFO', help='logger level')
arg_parser.add_argument('--log-config', help='logger config file (YAML)')
arg_parser.add_argument(
'--port', type=int, default=DEFAULT_RELAY_PORT, help='Port to listen on'
)
args = arg_parser.parse_args()
# Setup logger
if args.log_config:
from logging import config # pylint: disable=import-outside-toplevel
config.fileConfig(args.log_config)
else:
logging.basicConfig(level=getattr(logging, args.log_level.upper()))
# Start a relay
relay = Relay(args.port)
asyncio.get_event_loop().run_until_complete(relay.start())
asyncio.get_event_loop().run_forever()
# ----------------------------------------------------------------------------
if __name__ == '__main__':
main()

View File

@@ -1,21 +0,0 @@
[loggers]
keys=root
[handlers]
keys=stream_handler
[formatters]
keys=formatter
[logger_root]
level=DEBUG
handlers=stream_handler
[handler_stream_handler]
class=StreamHandler
level=DEBUG
formatter=formatter
args=(sys.stderr,)
[formatter_formatter]
format=%(asctime)s %(name)-12s %(levelname)-8s %(message)s

View File

@@ -26,7 +26,7 @@ from prompt_toolkit.shortcuts import PromptSession
from bumble.a2dp import make_audio_sink_service_sdp_records
from bumble.colors import color
from bumble.device import Device, Peer
from bumble.transport import open_transport_or_link
from bumble.transport import open_transport
from bumble.pairing import OobData, PairingDelegate, PairingConfig
from bumble.smp import OobContext, OobLegacyContext
from bumble.smp import error_name as smp_error_name
@@ -349,7 +349,7 @@ async def pair(
Waiter.instance = Waiter(linger=linger)
print('<<< connecting to HCI...')
async with await open_transport_or_link(hci_transport) as (hci_source, hci_sink):
async with await open_transport(hci_transport) as (hci_source, hci_sink):
print('<<< connected')
# Create a device to manage the host

View File

@@ -17,8 +17,6 @@
# -----------------------------------------------------------------------------
from __future__ import annotations
import asyncio
import asyncio.subprocess
import os
import logging
from typing import Optional, Union
@@ -63,6 +61,7 @@ from bumble.hci import Address, HCI_CONNECTION_ALREADY_EXISTS_ERROR, HCI_Constan
from bumble.pairing import PairingConfig
from bumble.transport import open_transport
from bumble.utils import AsyncRunner
import bumble.logging
# -----------------------------------------------------------------------------
@@ -599,7 +598,7 @@ def play(context, address, audio_format, audio_file):
# -----------------------------------------------------------------------------
def main():
logging.basicConfig(level=os.environ.get("BUMBLE_LOGLEVEL", "WARNING").upper())
bumble.logging.setup_basic_logging("WARNING")
player_cli()

View File

@@ -16,8 +16,6 @@
# Imports
# -----------------------------------------------------------------------------
import asyncio
import logging
import os
import time
from typing import Optional
@@ -30,6 +28,7 @@ from bumble import hci
from bumble import rfcomm
from bumble import transport
from bumble import utils
import bumble.logging
# -----------------------------------------------------------------------------
@@ -406,7 +405,7 @@ class ClientBridge:
# -----------------------------------------------------------------------------
async def run(device_config, hci_transport, bridge):
print("<<< connecting to HCI...")
async with await transport.open_transport_or_link(hci_transport) as (
async with await transport.open_transport(hci_transport) as (
hci_source,
hci_sink,
):
@@ -515,6 +514,6 @@ def client(context, bluetooth_address, tcp_host, tcp_port, authenticate, encrypt
# -----------------------------------------------------------------------------
logging.basicConfig(level=os.environ.get("BUMBLE_LOGLEVEL", "WARNING").upper())
if __name__ == "__main__":
bumble.logging.setup_basic_logging("WARNING")
cli(obj={}) # pylint: disable=no-value-for-parameter

View File

@@ -16,17 +16,16 @@
# Imports
# -----------------------------------------------------------------------------
import asyncio
import os
import logging
import click
from bumble.colors import color
from bumble.device import Device
from bumble.transport import open_transport_or_link
from bumble.transport import open_transport
from bumble.keys import JsonKeyStore
from bumble.smp import AddressResolver
from bumble.device import Advertisement
from bumble.hci import Address, HCI_Constant, HCI_LE_1M_PHY, HCI_LE_CODED_PHY
import bumble.logging
# -----------------------------------------------------------------------------
@@ -127,7 +126,7 @@ async def scan(
transport,
):
print('<<< connecting to HCI...')
async with await open_transport_or_link(transport) as (hci_source, hci_sink):
async with await open_transport(transport) as (hci_source, hci_sink):
print('<<< connected')
if device_config:
@@ -237,7 +236,7 @@ def main(
device_config,
transport,
):
logging.basicConfig(level=os.environ.get('BUMBLE_LOGLEVEL', 'WARNING').upper())
bumble.logging.setup_basic_logging('WARNING')
asyncio.run(
scan(
min_rssi,

View File

@@ -18,7 +18,6 @@
import datetime
import importlib
import logging
import os
import struct
import click
@@ -27,6 +26,7 @@ from bumble.colors import color
from bumble import hci
from bumble.transport.common import PacketReader
from bumble.helpers import PacketTracer
import bumble.logging
# -----------------------------------------------------------------------------
@@ -188,5 +188,5 @@ def main(format, vendor, filename):
# -----------------------------------------------------------------------------
if __name__ == '__main__':
logging.basicConfig(level=os.environ.get('BUMBLE_LOGLEVEL', 'WARNING').upper())
bumble.logging.setup_basic_logging('WARNING')
main() # pylint: disable=no-value-for-parameter

View File

@@ -21,7 +21,6 @@ import asyncio.subprocess
from importlib import resources
import enum
import json
import os
import logging
import pathlib
import subprocess
@@ -58,6 +57,7 @@ from bumble.a2dp import (
from bumble.utils import AsyncRunner
from bumble.codecs import AacAudioRtpPacket
from bumble.rtp import MediaPacket
import bumble.logging
# -----------------------------------------------------------------------------
@@ -833,11 +833,7 @@ def speaker(
# -----------------------------------------------------------------------------
def main():
logging.basicConfig(
level=os.environ.get('BUMBLE_LOGLEVEL', 'WARNING').upper(),
format="[%(asctime)s.%(msecs)03d] %(levelname)s:%(name)s:%(message)s",
datefmt="%H:%M:%S",
)
bumble.logging.setup_basic_logging('WARNING')
speaker()

View File

@@ -16,13 +16,12 @@
# Imports
# -----------------------------------------------------------------------------
import asyncio
import os
import logging
import click
from bumble.device import Device
from bumble.keys import JsonKeyStore
from bumble.transport import open_transport
import bumble.logging
# -----------------------------------------------------------------------------
@@ -68,7 +67,7 @@ def main(keystore_file, hci_transport, device_config, address):
instantiated.
If no address is passed, the existing pairing keys for all addresses are printed.
"""
logging.basicConfig(level=os.environ.get('BUMBLE_LOGLEVEL', 'INFO').upper())
bumble.logging.setup_basic_logging()
if not keystore_file and not hci_transport:
print('either --keystore-file or --hci-transport must be specified.')

View File

@@ -26,13 +26,12 @@
# -----------------------------------------------------------------------------
# Imports
# -----------------------------------------------------------------------------
import os
import logging
import click
import usb1
from bumble.colors import color
from bumble.transport.usb import load_libusb
import bumble.logging
# -----------------------------------------------------------------------------
@@ -169,7 +168,7 @@ def is_bluetooth_hci(device):
@click.command()
@click.option('--verbose', is_flag=True, default=False, help='Print more details')
def main(verbose):
logging.basicConfig(level=os.environ.get('BUMBLE_LOGLEVEL', 'WARNING').upper())
bumble.logging.setup_basic_logging('WARNING')
load_libusb()
with usb1.USBContext() as context:

View File

@@ -370,6 +370,12 @@ class Controller:
return connection
return None
def find_peripheral_connection_by_handle(self, handle):
for connection in self.peripheral_connections.values():
if connection.handle == handle:
return connection
return None
def find_classic_connection_by_handle(self, handle):
for connection in self.classic_connections.values():
if connection.handle == handle:
@@ -414,7 +420,7 @@ class Controller:
)
)
def on_link_central_disconnected(self, peer_address, reason):
def on_link_disconnected(self, peer_address, reason):
'''
Called when an active disconnection occurs from a peer
'''
@@ -431,6 +437,17 @@ class Controller:
# Remove the connection
del self.peripheral_connections[peer_address]
elif connection := self.central_connections.get(peer_address):
self.send_hci_packet(
HCI_Disconnection_Complete_Event(
status=HCI_SUCCESS,
connection_handle=connection.handle,
reason=reason,
)
)
# Remove the connection
del self.central_connections[peer_address]
else:
logger.warning(f'!!! No peripheral connection found for {peer_address}')
@@ -479,7 +496,7 @@ class Controller:
)
)
def on_link_peripheral_disconnection_complete(self, disconnection_command, status):
def on_link_disconnection_complete(self, disconnection_command, status):
'''
Called when a disconnection has been completed
'''
@@ -499,26 +516,11 @@ class Controller:
):
logger.debug(f'CENTRAL Connection removed: {connection}')
del self.central_connections[connection.peer_address]
def on_link_peripheral_disconnected(self, peer_address):
'''
Called when a connection to a peripheral is broken
'''
# Send a disconnection complete event
if connection := self.central_connections.get(peer_address):
self.send_hci_packet(
HCI_Disconnection_Complete_Event(
status=HCI_SUCCESS,
connection_handle=connection.handle,
reason=HCI_CONNECTION_TIMEOUT_ERROR,
)
)
# Remove the connection
del self.central_connections[peer_address]
else:
logger.warning(f'!!! No central connection found for {peer_address}')
elif connection := self.find_peripheral_connection_by_handle(
disconnection_command.connection_handle
):
logger.debug(f'PERIPHERAL Connection removed: {connection}')
del self.peripheral_connections[connection.peer_address]
def on_link_encrypted(self, peer_address, _rand, _ediv, _ltk):
# For now, just setup the encryption without asking the host
@@ -877,6 +879,14 @@ class Controller:
else:
# Remove the connection
del self.central_connections[connection.peer_address]
elif connection := self.find_peripheral_connection_by_handle(handle):
if self.link:
self.link.disconnect(
self.random_address, connection.peer_address, command
)
else:
# Remove the connection
del self.peripheral_connections[connection.peer_address]
elif connection := self.find_classic_connection_by_handle(handle):
if self.link:
self.link.classic_disconnect(

View File

@@ -17,19 +17,13 @@
# -----------------------------------------------------------------------------
import logging
import asyncio
from functools import partial
from bumble.core import (
PhysicalTransport,
InvalidStateError,
)
from bumble.colors import color
from bumble import core
from bumble.hci import (
Address,
Role,
HCI_SUCCESS,
HCI_CONNECTION_ACCEPT_TIMEOUT_ERROR,
HCI_CONNECTION_TIMEOUT_ERROR,
HCI_UNKNOWN_CONNECTION_IDENTIFIER_ERROR,
HCI_PAGE_TIMEOUT_ERROR,
HCI_Connection_Complete_Event,
@@ -115,10 +109,10 @@ class LocalLink:
def send_acl_data(self, sender_controller, destination_address, transport, data):
# Send the data to the first controller with a matching address
if transport == PhysicalTransport.LE:
if transport == core.PhysicalTransport.LE:
destination_controller = self.find_controller(destination_address)
source_address = sender_controller.random_address
elif transport == PhysicalTransport.BR_EDR:
elif transport == core.PhysicalTransport.BR_EDR:
destination_controller = self.find_classic_controller(destination_address)
source_address = sender_controller.public_address
else:
@@ -165,29 +159,29 @@ class LocalLink:
asyncio.get_running_loop().call_soon(self.on_connection_complete)
def on_disconnection_complete(
self, central_address, peripheral_address, disconnect_command
self, initiating_address, target_address, disconnect_command
):
# Find the controller that initiated the disconnection
if not (central_controller := self.find_controller(central_address)):
if not (initiating_controller := self.find_controller(initiating_address)):
logger.warning('!!! Initiating controller not found')
return
# Disconnect from the first controller with a matching address
if peripheral_controller := self.find_controller(peripheral_address):
peripheral_controller.on_link_central_disconnected(
central_address, disconnect_command.reason
if target_controller := self.find_controller(target_address):
target_controller.on_link_disconnected(
initiating_address, disconnect_command.reason
)
central_controller.on_link_peripheral_disconnection_complete(
initiating_controller.on_link_disconnection_complete(
disconnect_command, HCI_SUCCESS
)
def disconnect(self, central_address, peripheral_address, disconnect_command):
def disconnect(self, initiating_address, target_address, disconnect_command):
logger.debug(
f'$$$ DISCONNECTION {central_address} -> '
f'{peripheral_address}: reason = {disconnect_command.reason}'
f'$$$ DISCONNECTION {initiating_address} -> '
f'{target_address}: reason = {disconnect_command.reason}'
)
args = [central_address, peripheral_address, disconnect_command]
args = [initiating_address, target_address, disconnect_command]
asyncio.get_running_loop().call_soon(self.on_disconnection_complete, *args)
# pylint: disable=too-many-arguments
@@ -384,261 +378,3 @@ class LocalLink:
responder_controller.on_classic_sco_connection_complete(
initiator_controller.public_address, HCI_SUCCESS, link_type
)
# -----------------------------------------------------------------------------
class RemoteLink:
'''
A Link implementation that communicates with other virtual controllers via a
WebSocket relay
'''
def __init__(self, uri):
self.controller = None
self.uri = uri
self.execution_queue = asyncio.Queue()
self.websocket = asyncio.get_running_loop().create_future()
self.rpc_result = None
self.pending_connection = None
self.central_connections = set() # List of addresses that we have connected to
self.peripheral_connections = (
set()
) # List of addresses that have connected to us
# Connect and run asynchronously
asyncio.create_task(self.run_connection())
asyncio.create_task(self.run_executor_loop())
def add_controller(self, controller):
if self.controller:
raise InvalidStateError('controller already set')
self.controller = controller
def remove_controller(self, controller):
if self.controller != controller:
raise InvalidStateError('controller mismatch')
self.controller = None
def get_pending_connection(self):
return self.pending_connection
def get_pending_classic_connection(self):
return self.pending_classic_connection
async def wait_until_connected(self):
await self.websocket
def execute(self, async_function):
self.execution_queue.put_nowait(async_function())
async def run_executor_loop(self):
logger.debug('executor loop starting')
while True:
item = await self.execution_queue.get()
try:
await item
except Exception as error:
logger.warning(
f'{color("!!! Exception in async handler:", "red")} {error}'
)
async def run_connection(self):
import websockets # lazy import
# Connect to the relay
logger.debug(f'connecting to {self.uri}')
# pylint: disable-next=no-member
websocket = await websockets.connect(self.uri)
self.websocket.set_result(websocket)
logger.debug(f'connected to {self.uri}')
while True:
message = await websocket.recv()
logger.debug(f'received message: {message}')
keyword, *payload = message.split(':', 1)
handler_name = f'on_{keyword}_received'
handler = getattr(self, handler_name, None)
if handler:
await handler(payload[0] if payload else None)
def close(self):
if self.websocket.done():
logger.debug('closing websocket')
websocket = self.websocket.result()
asyncio.create_task(websocket.close())
async def on_result_received(self, result):
if self.rpc_result:
self.rpc_result.set_result(result)
async def on_left_received(self, address):
if address in self.central_connections:
self.controller.on_link_peripheral_disconnected(Address(address))
self.central_connections.remove(address)
if address in self.peripheral_connections:
self.controller.on_link_central_disconnected(
address, HCI_CONNECTION_TIMEOUT_ERROR
)
self.peripheral_connections.remove(address)
async def on_unreachable_received(self, target):
await self.on_left_received(target)
async def on_message_received(self, message):
sender, *payload = message.split('/', 1)
if payload:
keyword, *payload = payload[0].split(':', 1)
handler_name = f'on_{keyword}_message_received'
handler = getattr(self, handler_name, None)
if handler:
await handler(sender, payload[0] if payload else None)
async def on_advertisement_message_received(self, sender, advertisement):
try:
self.controller.on_link_advertising_data(
Address(sender), bytes.fromhex(advertisement)
)
except Exception:
logger.exception('exception')
async def on_acl_message_received(self, sender, acl_data):
try:
self.controller.on_link_acl_data(Address(sender), bytes.fromhex(acl_data))
except Exception:
logger.exception('exception')
async def on_connect_message_received(self, sender, _):
# Remember the connection
self.peripheral_connections.add(sender)
# Notify the controller
logger.debug(f'connection from central {sender}')
self.controller.on_link_central_connected(Address(sender))
# Accept the connection by responding to it
await self.send_targeted_message(sender, 'connected')
async def on_connected_message_received(self, sender, _):
if not self.pending_connection:
logger.warning('received a connection ack, but no connection is pending')
return
# Remember the connection
self.central_connections.add(sender)
# Notify the controller
logger.debug(f'connected to peripheral {self.pending_connection.peer_address}')
self.controller.on_link_peripheral_connection_complete(
self.pending_connection, HCI_SUCCESS
)
async def on_disconnect_message_received(self, sender, message):
# Notify the controller
params = parse_parameters(message)
reason = int(params.get('reason', str(HCI_CONNECTION_TIMEOUT_ERROR)))
self.controller.on_link_central_disconnected(Address(sender), reason)
# Forget the connection
if sender in self.peripheral_connections:
self.peripheral_connections.remove(sender)
async def on_encrypted_message_received(self, sender, _):
# TODO parse params to get real args
self.controller.on_link_encrypted(Address(sender), bytes(8), 0, bytes(16))
async def send_rpc_command(self, command):
# Ensure we have a connection
websocket = await self.websocket
# Create a future value to hold the eventual result
assert self.rpc_result is None
self.rpc_result = asyncio.get_running_loop().create_future()
# Send the command
await websocket.send(command)
# Wait for the result
rpc_result = await self.rpc_result
self.rpc_result = None
logger.debug(f'rpc_result: {rpc_result}')
# TODO: parse the result
async def send_targeted_message(self, target, message):
# Ensure we have a connection
websocket = await self.websocket
# Send the message
await websocket.send(f'@{target} {message}')
async def notify_address_changed(self):
await self.send_rpc_command(f'/set-address {self.controller.random_address}')
def on_address_changed(self, controller):
logger.info(f'address changed for {controller}: {controller.random_address}')
# Notify the relay of the change
self.execute(self.notify_address_changed)
async def send_advertising_data_to_relay(self, data):
await self.send_targeted_message('*', f'advertisement:{data.hex()}')
def send_advertising_data(self, _, data):
self.execute(partial(self.send_advertising_data_to_relay, data))
async def send_acl_data_to_relay(self, peer_address, data):
await self.send_targeted_message(peer_address, f'acl:{data.hex()}')
def send_acl_data(self, _, peer_address, _transport, data):
# TODO: handle different transport
self.execute(partial(self.send_acl_data_to_relay, peer_address, data))
async def send_connection_request_to_relay(self, peer_address):
await self.send_targeted_message(peer_address, 'connect')
def connect(self, _, le_create_connection_command):
if self.pending_connection:
logger.warning('connection already pending')
return
self.pending_connection = le_create_connection_command
self.execute(
partial(
self.send_connection_request_to_relay,
str(le_create_connection_command.peer_address),
)
)
def on_disconnection_complete(self, disconnect_command):
self.controller.on_link_peripheral_disconnection_complete(
disconnect_command, HCI_SUCCESS
)
def disconnect(self, central_address, peripheral_address, disconnect_command):
logger.debug(
f'disconnect {central_address} -> '
f'{peripheral_address}: reason = {disconnect_command.reason}'
)
self.execute(
partial(
self.send_targeted_message,
peripheral_address,
f'disconnect:reason={disconnect_command.reason}',
)
)
asyncio.get_running_loop().call_soon(
self.on_disconnection_complete, disconnect_command
)
def on_connection_encrypted(self, _, peripheral_address, rand, ediv, ltk):
asyncio.get_running_loop().call_soon(
self.controller.on_link_encrypted, peripheral_address, rand, ediv, ltk
)
self.execute(
partial(
self.send_targeted_message,
peripheral_address,
f'encrypted:ltk={ltk.hex()}',
)
)

65
bumble/logging.py Normal file
View File

@@ -0,0 +1,65 @@
# Copyright 2025 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# -----------------------------------------------------------------------------
# Imports
# -----------------------------------------------------------------------------
import functools
import logging
import os
from bumble import colors
# -----------------------------------------------------------------------------
class ColorFormatter(logging.Formatter):
_colorizers = {
logging.DEBUG: functools.partial(colors.color, fg="white"),
logging.INFO: functools.partial(colors.color, fg="green"),
logging.WARNING: functools.partial(colors.color, fg="yellow"),
logging.ERROR: functools.partial(colors.color, fg="red"),
logging.CRITICAL: functools.partial(colors.color, fg="black", bg="red"),
}
_formatters = {
level: logging.Formatter(
fmt=colorizer("{asctime}.{msecs:03.0f} {levelname:.1} {name}: ")
+ "{message}",
datefmt="%H:%M:%S",
style="{",
)
for level, colorizer in _colorizers.items()
}
def format(self, record: logging.LogRecord) -> str:
return self._formatters[record.levelno].format(record)
def setup_basic_logging(default_level: str = "INFO") -> None:
"""
Set up basic logging with logging.basicConfig, configured with a simple formatter
that prints out the date and log level in color.
If the BUMBLE_LOGLEVEL environment variable is set to the name of a log level, it
is used. Otherwise the default_level argument is used.
Args:
default_level: default logging level
"""
handler = logging.StreamHandler()
handler.setFormatter(ColorFormatter())
logging.basicConfig(
level=os.environ.get("BUMBLE_LOGLEVEL", default_level).upper(),
handlers=[handler],
)

View File

@@ -20,9 +20,9 @@ import logging
import os
from typing import Optional
from bumble import utils
from bumble.transport.common import (
Transport,
AsyncPipeSink,
SnoopingTransport,
TransportSpecError,
)
@@ -195,6 +195,7 @@ async def _open_transport(scheme: str, spec: Optional[str]) -> Transport:
# -----------------------------------------------------------------------------
@utils.deprecated("RemoteLink has been removed. Use open_transport instead.")
async def open_transport_or_link(name: str) -> Transport:
"""
Open a transport or a link relay.
@@ -205,21 +206,6 @@ async def open_transport_or_link(name: str) -> Transport:
When the name starts with "link-relay:", open a link relay (see RemoteLink
for details on what the arguments are).
For other namespaces, see `open_transport`.
"""
if name.startswith('link-relay:'):
logger.warning('Link Relay has been deprecated.')
from bumble.controller import Controller
from bumble.link import RemoteLink # lazy import
link = RemoteLink(name[11:])
await link.wait_until_connected()
controller = Controller('remote', link=link) # type:ignore[arg-type]
class LinkTransport(Transport):
async def close(self):
link.close()
return _wrap_transport(LinkTransport(controller, AsyncPipeSink(controller)))
return await open_transport(name)

View File

@@ -57,7 +57,6 @@ nav:
- Pair: apps_and_tools/pair.md
- Unbond: apps_and_tools/unbond.md
- USB Probe: apps_and_tools/usb_probe.md
- Link Relay: apps_and_tools/link_relay.md
- Hardware:
- hardware/index.md
- Platforms:

View File

@@ -13,4 +13,3 @@ These include:
* [Golden Gate Bridge](gg_bridge.md) - Bridge between GATT and UDP to use with the Golden Gate "stack tool".
* [Show](show.md) - Parse a file with HCI packets and print the details of each packet in a human readable form.
* [Speaker](speaker.md) - Virtual Bluetooth speaker, with a command line and browser-based UI.
* [Link Relay](link_relay.md) - WebSocket relay for virtual RemoteLink instances to communicate with each other.

View File

@@ -1,33 +0,0 @@
LINK RELAY TOOL
===============
The Link Relay is a WebSocket relay, which acts like an online chat system, where each "chat room" can be joined by multiple virtual controllers, which can then communicate with each other, as if connected with radio communication.
```
usage: python link_relay.py [-h] [--log-level LOG_LEVEL] [--log-config LOG_CONFIG] [--port PORT]
optional arguments:
-h, --help show this help message and exit
--log-level LOG_LEVEL
logger level
--log-config LOG_CONFIG
logger config file (YAML)
--port PORT Port to listen on
```
(the default port is `10723`)
When running, the link relay waits for connections on its listening port.
The WebSocket path used by a connecting client indicates which virtual "chat room" to join.
!!! tip "Connecting to the relay as a controller"
Most of the examples and tools that take a transport moniker as an argument also accept a link relay moniker, which is equivalent to a transport to a virtual controller that is connected to a relay.
The moniker syntax is: `link-relay:ws://<hostname>/<room>` where `<hostname>` is the hostname to connect to and `<room>` is the virtual "chat room" in a relay.
Example: `link-relay:ws://localhost:10723/test` will join the `test` "chat room"
!!! tip "Connecting to the relay as an observer"
It is possible to connect to a "chat room" in a relay as an observer, rather than a virtual controller. In this case, a text-based console can be used to observe what is going on in the "chat room". Tools like [`wscat`](https://github.com/websockets/wscat#readme) or [`websocat`](https://github.com/vi/websocat) can be used for that.
Example: `wscat --connect ws://localhost:10723/test`

View File

@@ -56,13 +56,6 @@ Included in the project are two types of Link interface implementations:
The LocalLink implementation is a simple object used by an application that instantiates
more than one Controller objects and connects them in-memory and in-process.
#### Remote Link
The RemoteLink implementation communicates with other virtual controllers over a WebSocket.
Multiple instances of RemoteLink objects communicate with each other through a simple
WebSocket relay that can host any number of virtual 'rooms', where each 'room' is
a set of controllers that can communicate between themselves.
The `link_relay` app is where this relay is implemented.
## Host
The Host component connects to a controller over an HCI interface. It is responsible to sending commands and ACL data to the controller and receiving back events and ACL data.

View File

@@ -15,11 +15,10 @@
# -----------------------------------------------------------------------------
# Imports
# -----------------------------------------------------------------------------
import logging
import asyncio
import os
from bumble.utils import AsyncRunner
import bumble.logging
# -----------------------------------------------------------------------------
my_work_queue1 = AsyncRunner.WorkQueue()
@@ -83,5 +82,5 @@ async def main() -> None:
# -----------------------------------------------------------------------------
logging.basicConfig(level=os.environ.get('BUMBLE_LOGLEVEL', 'DEBUG').upper())
bumble.logging.setup_basic_logging('DEBUG')
asyncio.run(main())

View File

@@ -17,13 +17,12 @@
# -----------------------------------------------------------------------------
import asyncio
import sys
import os
import logging
from bumble.colors import color
from bumble.device import Device
from bumble.hci import Address
from bumble.transport import open_transport
from bumble.profiles.battery_service import BatteryServiceProxy
import bumble.logging
# -----------------------------------------------------------------------------
@@ -72,5 +71,5 @@ async def main() -> None:
# -----------------------------------------------------------------------------
logging.basicConfig(level=os.environ.get('BUMBLE_LOGLEVEL', 'DEBUG').upper())
bumble.logging.setup_basic_logging('DEBUG')
asyncio.run(main())

View File

@@ -17,15 +17,14 @@
# -----------------------------------------------------------------------------
import asyncio
import sys
import os
import logging
import random
import struct
from bumble.core import AdvertisingData
from bumble.device import Device
from bumble.transport import open_transport_or_link
from bumble.transport import open_transport
from bumble.profiles.battery_service import BatteryService
import bumble.logging
# -----------------------------------------------------------------------------
@@ -35,7 +34,7 @@ async def main() -> None:
print('example: python battery_server.py device1.json usb:0')
return
async with await open_transport_or_link(sys.argv[2]) as hci_transport:
async with await open_transport(sys.argv[2]) as hci_transport:
device = Device.from_config_file_with_hci(
sys.argv[1], hci_transport.source, hci_transport.sink
)
@@ -74,5 +73,5 @@ async def main() -> None:
# -----------------------------------------------------------------------------
logging.basicConfig(level=os.environ.get('BUMBLE_LOGLEVEL', 'DEBUG').upper())
bumble.logging.setup_basic_logging('DEBUG')
asyncio.run(main())

View File

@@ -17,13 +17,13 @@
# -----------------------------------------------------------------------------
import asyncio
import sys
import os
import logging
from bumble.colors import color
from bumble.device import Device, Peer
from bumble.hci import Address
from bumble.profiles.device_information_service import DeviceInformationServiceProxy
from bumble.transport import open_transport
import bumble.logging
# -----------------------------------------------------------------------------
@@ -116,5 +116,5 @@ async def main() -> None:
# -----------------------------------------------------------------------------
logging.basicConfig(level=os.environ.get('BUMBLE_LOGLEVEL', 'DEBUG').upper())
bumble.logging.setup_basic_logging('DEBUG')
asyncio.run(main())

View File

@@ -17,14 +17,13 @@
# -----------------------------------------------------------------------------
import asyncio
import sys
import os
import logging
import struct
from bumble.core import AdvertisingData
from bumble.device import Device
from bumble.transport import open_transport_or_link
from bumble.transport import open_transport
from bumble.profiles.device_information_service import DeviceInformationService
import bumble.logging
# -----------------------------------------------------------------------------
@@ -34,7 +33,7 @@ async def main() -> None:
print('example: python device_info_server.py device1.json usb:0')
return
async with await open_transport_or_link(sys.argv[2]) as hci_transport:
async with await open_transport(sys.argv[2]) as hci_transport:
device = Device.from_config_file_with_hci(
sys.argv[1], hci_transport.source, hci_transport.sink
)
@@ -70,5 +69,5 @@ async def main() -> None:
# -----------------------------------------------------------------------------
logging.basicConfig(level=os.environ.get('BUMBLE_LOGLEVEL', 'DEBUG').upper())
bumble.logging.setup_basic_logging('DEBUG')
asyncio.run(main())

View File

@@ -17,13 +17,13 @@
# -----------------------------------------------------------------------------
import asyncio
import sys
import os
import logging
from bumble.colors import color
from bumble.device import Device
from bumble.hci import Address
from bumble.transport import open_transport
from bumble.profiles.heart_rate_service import HeartRateServiceProxy
import bumble.logging
# -----------------------------------------------------------------------------
@@ -76,5 +76,5 @@ async def main() -> None:
# -----------------------------------------------------------------------------
logging.basicConfig(level=os.environ.get('BUMBLE_LOGLEVEL', 'DEBUG').upper())
bumble.logging.setup_basic_logging('DEBUG')
asyncio.run(main())

View File

@@ -20,16 +20,15 @@ import time
import math
import random
import struct
import logging
import asyncio
import os
from bumble.core import AdvertisingData
from bumble.device import Device
from bumble.transport import open_transport_or_link
from bumble.transport import open_transport
from bumble.profiles.device_information_service import DeviceInformationService
from bumble.profiles.heart_rate_service import HeartRateService
from bumble.utils import AsyncRunner
import bumble.logging
# -----------------------------------------------------------------------------
@@ -39,7 +38,7 @@ async def main() -> None:
print('example: python heart_rate_server.py device1.json usb:0')
return
async with await open_transport_or_link(sys.argv[2]) as hci_transport:
async with await open_transport(sys.argv[2]) as hci_transport:
device = Device.from_config_file_with_hci(
sys.argv[1], hci_transport.source, hci_transport.sink
)
@@ -128,5 +127,5 @@ async def main() -> None:
# -----------------------------------------------------------------------------
logging.basicConfig(level=os.environ.get('BUMBLE_LOGLEVEL', 'DEBUG').upper())
bumble.logging.setup_basic_logging('DEBUG')
asyncio.run(main())

View File

@@ -17,17 +17,16 @@
# -----------------------------------------------------------------------------
import asyncio
import sys
import os
import logging
import struct
import json
import websockets
from bumble.colors import color
import websockets
from bumble.colors import color
from bumble.core import AdvertisingData
from bumble.device import Device, Connection, Peer
from bumble.utils import AsyncRunner
from bumble.transport import open_transport_or_link
from bumble.transport import open_transport
from bumble.gatt import (
Descriptor,
Service,
@@ -45,6 +44,8 @@ from bumble.gatt import (
GATT_HID_CONTROL_POINT_CHARACTERISTIC,
GATT_REPORT_REFERENCE_DESCRIPTOR,
)
import bumble.logging
# -----------------------------------------------------------------------------
@@ -434,7 +435,7 @@ async def main() -> None:
)
return
async with await open_transport_or_link(sys.argv[2]) as hci_transport:
async with await open_transport(sys.argv[2]) as hci_transport:
# Create a device to manage the host
device = Device.from_config_file_with_hci(
sys.argv[1], hci_transport.source, hci_transport.sink
@@ -450,5 +451,5 @@ async def main() -> None:
# -----------------------------------------------------------------------------
logging.basicConfig(level=os.environ.get('BUMBLE_LOGLEVEL', 'DEBUG').upper())
bumble.logging.setup_basic_logging('DEBUG')
asyncio.run(main())

View File

@@ -17,12 +17,10 @@
# -----------------------------------------------------------------------------
import asyncio
import sys
import os
import logging
from bumble.colors import color
from bumble.device import Device
from bumble.transport import open_transport_or_link
from bumble.transport import open_transport
from bumble.core import (
PhysicalTransport,
BT_AVDTP_PROTOCOL_ID,
@@ -39,6 +37,7 @@ from bumble.sdp import (
SDP_BLUETOOTH_PROFILE_DESCRIPTOR_LIST_ATTRIBUTE_ID,
SDP_SERVICE_CLASS_ID_LIST_ATTRIBUTE_ID,
)
import bumble.logging
# -----------------------------------------------------------------------------
@@ -146,7 +145,7 @@ async def main() -> None:
return
print('<<< connecting to HCI...')
async with await open_transport_or_link(sys.argv[2]) as hci_transport:
async with await open_transport(sys.argv[2]) as hci_transport:
print('<<< connected')
# Create a device
@@ -198,5 +197,5 @@ async def main() -> None:
# -----------------------------------------------------------------------------
logging.basicConfig(level=os.environ.get('BUMBLE_LOGLEVEL', 'DEBUG').upper())
bumble.logging.setup_basic_logging('DEBUG')
asyncio.run(main())

View File

@@ -17,12 +17,10 @@
# -----------------------------------------------------------------------------
import asyncio
import sys
import os
import logging
from typing import Any
from bumble.device import Device
from bumble.transport import open_transport_or_link
from bumble.transport import open_transport
from bumble.core import PhysicalTransport
from bumble.avdtp import (
AVDTP_AUDIO_MEDIA_TYPE,
@@ -35,6 +33,8 @@ from bumble.a2dp import (
A2DP_SBC_CODEC_TYPE,
SbcMediaCodecInformation,
)
import bumble.logging
Context: dict[Any, Any] = {'output': None}
@@ -112,7 +112,7 @@ async def main() -> None:
return
print('<<< connecting to HCI...')
async with await open_transport_or_link(sys.argv[2]) as hci_transport:
async with await open_transport(sys.argv[2]) as hci_transport:
print('<<< connected')
with open(sys.argv[3], 'wb') as sbc_file:
@@ -166,5 +166,5 @@ async def main() -> None:
# -----------------------------------------------------------------------------
logging.basicConfig(level=os.environ.get('BUMBLE_LOGLEVEL', 'DEBUG').upper())
bumble.logging.setup_basic_logging('DEBUG')
asyncio.run(main())

View File

@@ -17,12 +17,10 @@
# -----------------------------------------------------------------------------
import asyncio
import sys
import os
import logging
from bumble.colors import color
from bumble.device import Device
from bumble.transport import open_transport_or_link
from bumble.transport import open_transport
from bumble.core import PhysicalTransport
from bumble.avdtp import (
find_avdtp_service_with_connection,
@@ -38,6 +36,7 @@ from bumble.a2dp import (
SbcMediaCodecInformation,
SbcPacketSource,
)
import bumble.logging
# -----------------------------------------------------------------------------
@@ -120,7 +119,7 @@ async def main() -> None:
return
print('<<< connecting to HCI...')
async with await open_transport_or_link(sys.argv[2]) as hci_transport:
async with await open_transport(sys.argv[2]) as hci_transport:
print('<<< connected')
# Create a device
@@ -186,5 +185,5 @@ async def main() -> None:
# -----------------------------------------------------------------------------
logging.basicConfig(level=os.environ.get('BUMBLE_LOGLEVEL', 'DEBUG').upper())
bumble.logging.setup_basic_logging('DEBUG')
asyncio.run(main())

View File

@@ -16,15 +16,14 @@
# Imports
# -----------------------------------------------------------------------------
import asyncio
import logging
import sys
import os
import struct
from bumble.core import AdvertisingData
from bumble.device import AdvertisingType, Device
from bumble.hci import Address
from bumble.transport import open_transport_or_link
from bumble.transport import open_transport
import bumble.logging
# -----------------------------------------------------------------------------
@@ -50,7 +49,7 @@ async def main() -> None:
target = None
print('<<< connecting to HCI...')
async with await open_transport_or_link(sys.argv[2]) as hci_transport:
async with await open_transport(sys.argv[2]) as hci_transport:
print('<<< connected')
device = Device.from_config_file_with_hci(
@@ -72,5 +71,5 @@ async def main() -> None:
# -----------------------------------------------------------------------------
logging.basicConfig(level=os.environ.get('BUMBLE_LOGLEVEL', 'DEBUG').upper())
bumble.logging.setup_basic_logging('DEBUG')
asyncio.run(main())

View File

@@ -17,10 +17,8 @@
# -----------------------------------------------------------------------------
import asyncio
import sys
import os
import logging
from bumble.colors import color
from bumble.colors import color
from bumble.device import Device, Peer
from bumble.transport import open_transport
from bumble.profiles.ancs import (
@@ -31,6 +29,7 @@ from bumble.profiles.ancs import (
Notification,
NotificationAttributeId,
)
import bumble.logging
# -----------------------------------------------------------------------------
@@ -210,5 +209,5 @@ async def main() -> None:
# -----------------------------------------------------------------------------
logging.basicConfig(level=os.environ.get('BUMBLE_LOGLEVEL', 'INFO').upper())
bumble.logging.setup_basic_logging('DEBUG')
asyncio.run(main())

View File

@@ -17,18 +17,19 @@
# -----------------------------------------------------------------------------
import asyncio
import sys
import os
import logging
import websockets
from typing import Optional
import websockets
from bumble import decoder
from bumble import gatt
from bumble.core import AdvertisingData
from bumble.device import Device, AdvertisingParameters
from bumble.transport import open_transport_or_link
from bumble.transport import open_transport
from bumble.profiles import asha
import bumble.logging
ws_connection: Optional[websockets.WebSocketServerProtocol] = None
g722_decoder = decoder.G722Decoder()
@@ -50,7 +51,7 @@ async def main() -> None:
print('example: python run_asha_sink.py device1.json usb:0')
return
async with await open_transport_or_link(sys.argv[2]) as hci_transport:
async with await open_transport(sys.argv[2]) as hci_transport:
device = Device.from_config_file_with_hci(
sys.argv[1], hci_transport.source, hci_transport.sink
)
@@ -111,9 +112,5 @@ async def main() -> None:
# -----------------------------------------------------------------------------
logging.basicConfig(
level=os.environ.get('BUMBLE_LOGLEVEL', 'DEBUG').upper(),
format='%(asctime)s.%(msecs)03d %(levelname)s %(module)s - %(funcName)s: %(message)s',
datefmt='%Y-%m-%d %H:%M:%S',
)
bumble.logging.setup_basic_logging('DEBUG')
asyncio.run(main())

View File

@@ -19,18 +19,19 @@ from __future__ import annotations
import asyncio
import json
import sys
import os
import logging
import websockets
from bumble.device import Device
from bumble.transport import open_transport_or_link
from bumble.transport import open_transport
from bumble.core import PhysicalTransport
from bumble import avc
from bumble import avrcp
from bumble import avdtp
from bumble import a2dp
from bumble import utils
import bumble.logging
logger = logging.getLogger(__name__)
@@ -344,7 +345,7 @@ async def main() -> None:
return
print('<<< connecting to HCI...')
async with await open_transport_or_link(sys.argv[2]) as hci_transport:
async with await open_transport(sys.argv[2]) as hci_transport:
print('<<< connected')
# Create a device
@@ -409,5 +410,5 @@ async def main() -> None:
# -----------------------------------------------------------------------------
logging.basicConfig(level=os.environ.get('BUMBLE_LOGLEVEL', 'DEBUG').upper())
bumble.logging.setup_basic_logging('DEBUG')
asyncio.run(main())

View File

@@ -18,15 +18,15 @@
from __future__ import annotations
import asyncio
import logging
import sys
import os
import functools
from bumble import core
from bumble import hci
from bumble.device import Connection, Device, ChannelSoundingCapabilities
from bumble.transport import open_transport_or_link
from bumble.transport import open_transport
import bumble.logging
# From https://cs.android.com/android/platform/superproject/main/+/main:packages/modules/Bluetooth/system/gd/hci/distance_measurement_manager.cc.
CS_TONE_ANTENNA_CONFIG_MAPPING_TABLE = [
@@ -78,7 +78,7 @@ async def main() -> None:
return
print('<<< connecting to HCI...')
async with await open_transport_or_link(sys.argv[2]) as hci_transport:
async with await open_transport(sys.argv[2]) as hci_transport:
print('<<< connected')
device = Device.from_config_file_with_hci(
@@ -150,5 +150,5 @@ async def main() -> None:
# -----------------------------------------------------------------------------
logging.basicConfig(level=os.environ.get('BUMBLE_LOGLEVEL', 'DEBUG').upper())
bumble.logging.setup_basic_logging('DEBUG')
asyncio.run(main())

View File

@@ -16,16 +16,14 @@
# Imports
# -----------------------------------------------------------------------------
import asyncio
import logging
import sys
import os
from bumble import utils
from bumble.device import Device, CigParameters, CisLink, Connection
from bumble.hci import (
OwnAddressType,
)
from bumble.transport import open_transport_or_link
from bumble.transport import open_transport
import bumble.logging
# -----------------------------------------------------------------------------
@@ -40,7 +38,7 @@ async def main() -> None:
print('<<< connecting to HCI...')
hci_transports = await asyncio.gather(
open_transport_or_link(sys.argv[2]), open_transport_or_link(sys.argv[3])
open_transport(sys.argv[2]), open_transport(sys.argv[3])
)
print('<<< connected')
@@ -104,5 +102,5 @@ async def main() -> None:
# -----------------------------------------------------------------------------
logging.basicConfig(level=os.environ.get('BUMBLE_LOGLEVEL', 'DEBUG').upper())
bumble.logging.setup_basic_logging('DEBUG')
asyncio.run(main())

View File

@@ -17,18 +17,17 @@
# -----------------------------------------------------------------------------
import asyncio
import sys
import os
import logging
from bumble.colors import color
from bumble.colors import color
from bumble.device import Device
from bumble.transport import open_transport_or_link
from bumble.transport import open_transport
from bumble.core import PhysicalTransport, BT_L2CAP_PROTOCOL_ID, CommandTimeoutError
from bumble.sdp import (
Client as SDP_Client,
SDP_PUBLIC_BROWSE_ROOT,
SDP_ALL_ATTRIBUTES_RANGE,
)
import bumble.logging
# -----------------------------------------------------------------------------
@@ -42,7 +41,7 @@ async def main() -> None:
return
print('<<< connecting to HCI...')
async with await open_transport_or_link(sys.argv[2]) as hci_transport:
async with await open_transport(sys.argv[2]) as hci_transport:
print('<<< connected')
# Create a device
@@ -117,5 +116,5 @@ async def main() -> None:
# -----------------------------------------------------------------------------
logging.basicConfig(level=os.environ.get('BUMBLE_LOGLEVEL', 'DEBUG').upper())
bumble.logging.setup_basic_logging('DEBUG')
asyncio.run(main())

View File

@@ -17,11 +17,9 @@
# -----------------------------------------------------------------------------
import asyncio
import sys
import os
import logging
from bumble.device import Device
from bumble.transport import open_transport_or_link
from bumble.transport import open_transport
from bumble.sdp import (
DataElement,
ServiceAttribute,
@@ -38,6 +36,8 @@ from bumble.core import (
BT_AVDTP_PROTOCOL_ID,
BT_ADVANCED_AUDIO_DISTRIBUTION_SERVICE,
)
import bumble.logging
# -----------------------------------------------------------------------------
SDP_SERVICE_RECORDS = {
@@ -98,7 +98,7 @@ async def main() -> None:
return
print('<<< connecting to HCI...')
async with await open_transport_or_link(sys.argv[2]) as hci_transport:
async with await open_transport(sys.argv[2]) as hci_transport:
print('<<< connected')
# Create a device
@@ -117,5 +117,5 @@ async def main() -> None:
# -----------------------------------------------------------------------------
logging.basicConfig(level=os.environ.get('BUMBLE_LOGLEVEL', 'DEBUG').upper())
bumble.logging.setup_basic_logging('DEBUG')
asyncio.run(main())

View File

@@ -17,13 +17,13 @@
# -----------------------------------------------------------------------------
import asyncio
import sys
import os
import logging
from bumble.colors import color
from bumble.device import Device
from bumble.hci import Address
from bumble.transport import open_transport_or_link
from bumble.transport import open_transport
from bumble.core import DeviceClass
import bumble.logging
# -----------------------------------------------------------------------------
@@ -60,7 +60,7 @@ async def main() -> None:
return
print('<<< connecting to HCI...')
async with await open_transport_or_link(sys.argv[1]) as hci_transport:
async with await open_transport(sys.argv[1]) as hci_transport:
print('<<< connected')
device = Device.with_hci(
@@ -77,5 +77,5 @@ async def main() -> None:
# -----------------------------------------------------------------------------
logging.basicConfig(level=os.environ.get('BUMBLE_LOGLEVEL', 'DEBUG').upper())
bumble.logging.setup_basic_logging('DEBUG')
asyncio.run(main())

View File

@@ -17,11 +17,10 @@
# -----------------------------------------------------------------------------
import asyncio
import sys
import os
import logging
from bumble.device import Device
from bumble.transport import open_transport_or_link
from bumble.transport import open_transport
import bumble.logging
# -----------------------------------------------------------------------------
@@ -37,7 +36,7 @@ async def main() -> None:
return
print('<<< connecting to HCI...')
async with await open_transport_or_link(sys.argv[2]) as hci_transport:
async with await open_transport(sys.argv[2]) as hci_transport:
print('<<< connected')
# Create a device
@@ -62,5 +61,5 @@ async def main() -> None:
# -----------------------------------------------------------------------------
logging.basicConfig(level=os.environ.get('BUMBLE_LOGLEVEL', 'DEBUG').upper())
bumble.logging.setup_basic_logging('DEBUG')
asyncio.run(main())

View File

@@ -15,10 +15,8 @@
# -----------------------------------------------------------------------------
# Imports
# -----------------------------------------------------------------------------
import logging
import asyncio
import sys
import os
from bumble.gatt import (
GATT_CHARACTERISTIC_USER_DESCRIPTION_DESCRIPTOR,
@@ -32,7 +30,8 @@ from bumble.device import Device
from bumble.host import Host
from bumble.controller import Controller
from bumble.link import LocalLink
from bumble.transport import open_transport_or_link
from bumble.transport import open_transport
import bumble.logging
# -----------------------------------------------------------------------------
@@ -49,7 +48,7 @@ async def main() -> None:
return
print('>>> connecting to HCI...')
async with await open_transport_or_link(sys.argv[3]) as hci_transport:
async with await open_transport(sys.argv[3]) as hci_transport:
print('>>> connected')
# Create a local link
@@ -105,5 +104,5 @@ async def main() -> None:
# -----------------------------------------------------------------------------
logging.basicConfig(level=os.environ.get('BUMBLE_LOGLEVEL', 'DEBUG').upper())
bumble.logging.setup_basic_logging('DEBUG')
asyncio.run(main())

View File

@@ -15,16 +15,16 @@
# -----------------------------------------------------------------------------
# Imports
# -----------------------------------------------------------------------------
import logging
import asyncio
import sys
import os
from bumble.colors import color
from bumble.device import Device
from bumble.controller import Controller
from bumble.hci import Address
from bumble.link import LocalLink
from bumble.transport import open_transport_or_link
from bumble.transport import open_transport
import bumble.logging
# -----------------------------------------------------------------------------
@@ -52,7 +52,7 @@ async def main() -> None:
return
print('>>> connecting to HCI...')
async with await open_transport_or_link(sys.argv[1]) as hci_transport:
async with await open_transport(sys.argv[1]) as hci_transport:
print('>>> connected')
# Create a local link
@@ -82,5 +82,5 @@ async def main() -> None:
# -----------------------------------------------------------------------------
logging.basicConfig(level=os.environ.get('BUMBLE_LOGLEVEL', 'DEBUG').upper())
bumble.logging.setup_basic_logging('DEBUG')
asyncio.run(main())

View File

@@ -16,9 +16,7 @@
# Imports
# -----------------------------------------------------------------------------
import asyncio
import logging
import sys
import os
import secrets
from bumble.core import AdvertisingData
@@ -28,8 +26,8 @@ from bumble.hci import (
)
from bumble.profiles.cap import CommonAudioServiceService
from bumble.profiles.csip import CoordinatedSetIdentificationService, SirkType
from bumble.transport import open_transport_or_link
from bumble.transport import open_transport
import bumble.logging
# -----------------------------------------------------------------------------
@@ -44,7 +42,7 @@ async def main() -> None:
print('<<< connecting to HCI...')
hci_transports = await asyncio.gather(
open_transport_or_link(sys.argv[2]), open_transport_or_link(sys.argv[3])
open_transport(sys.argv[2]), open_transport(sys.argv[3])
)
print('<<< connected')
@@ -101,5 +99,5 @@ async def main() -> None:
# -----------------------------------------------------------------------------
logging.basicConfig(level=os.environ.get('BUMBLE_LOGLEVEL', 'DEBUG').upper())
bumble.logging.setup_basic_logging('DEBUG')
asyncio.run(main())

View File

@@ -17,12 +17,12 @@
# -----------------------------------------------------------------------------
import asyncio
import sys
import os
import logging
from bumble.hci import Address
from bumble.device import Device
from bumble.transport import open_transport_or_link
from bumble.transport import open_transport
from bumble.snoop import BtSnooper
import bumble.logging
# -----------------------------------------------------------------------------
@@ -33,7 +33,7 @@ async def main() -> None:
return
print('<<< connecting to HCI...')
async with await open_transport_or_link(sys.argv[1]) as hci_transport:
async with await open_transport(sys.argv[1]) as hci_transport:
print('<<< connected')
device = Device.with_hci(
@@ -52,5 +52,5 @@ async def main() -> None:
# -----------------------------------------------------------------------------
logging.basicConfig(level=os.environ.get('BUMBLE_LOGLEVEL', 'DEBUG').upper())
bumble.logging.setup_basic_logging('DEBUG')
asyncio.run(main())

View File

@@ -16,15 +16,14 @@
# Imports
# -----------------------------------------------------------------------------
import asyncio
import logging
import sys
import os
from bumble.core import PhysicalTransport
from bumble.device import Device, ScoLink
from bumble.hci import HCI_Enhanced_Setup_Synchronous_Connection_Command
from bumble.hfp import DefaultCodecParameters, ESCO_PARAMETERS
from bumble.transport import open_transport_or_link
from bumble.transport import open_transport
import bumble.logging
# -----------------------------------------------------------------------------
@@ -42,7 +41,7 @@ async def main() -> None:
print('<<< connecting to HCI...')
hci_transports = await asyncio.gather(
open_transport_or_link(sys.argv[2]), open_transport_or_link(sys.argv[3])
open_transport(sys.argv[2]), open_transport(sys.argv[3])
)
print('<<< connected')
@@ -83,5 +82,5 @@ async def main() -> None:
# -----------------------------------------------------------------------------
logging.basicConfig(level=os.environ.get('BUMBLE_LOGLEVEL', 'DEBUG').upper())
bumble.logging.setup_basic_logging('DEBUG')
asyncio.run(main())

View File

@@ -16,9 +16,8 @@
# Imports
# -----------------------------------------------------------------------------
import asyncio
import logging
import sys
import os
from bumble.device import (
AdvertisingParameters,
AdvertisingEventProperties,
@@ -26,8 +25,8 @@ from bumble.device import (
Device,
)
from bumble.hci import Address
from bumble.transport import open_transport_or_link
from bumble.transport import open_transport
import bumble.logging
# -----------------------------------------------------------------------------
@@ -52,7 +51,7 @@ async def main() -> None:
peer_address = Address.ANY
print('<<< connecting to HCI...')
async with await open_transport_or_link(sys.argv[2]) as hci_transport:
async with await open_transport(sys.argv[2]) as hci_transport:
print('<<< connected')
device = Device.from_config_file_with_hci(
@@ -69,5 +68,5 @@ async def main() -> None:
# -----------------------------------------------------------------------------
logging.basicConfig(level=os.environ.get('BUMBLE_LOGLEVEL', 'DEBUG').upper())
bumble.logging.setup_basic_logging('DEBUG')
asyncio.run(main())

View File

@@ -16,13 +16,13 @@
# Imports
# -----------------------------------------------------------------------------
import asyncio
import logging
import sys
import os
from bumble.device import AdvertisingParameters, AdvertisingEventProperties, Device
from bumble.hci import Address
from bumble.core import AdvertisingData
from bumble.transport import open_transport_or_link
from bumble.transport import open_transport
import bumble.logging
# -----------------------------------------------------------------------------
@@ -33,7 +33,7 @@ async def main() -> None:
return
print('<<< connecting to HCI...')
async with await open_transport_or_link(sys.argv[2]) as hci_transport:
async with await open_transport(sys.argv[2]) as hci_transport:
print('<<< connected')
device = Device.from_config_file_with_hci(
@@ -96,5 +96,5 @@ async def main() -> None:
# -----------------------------------------------------------------------------
logging.basicConfig(level=os.environ.get('BUMBLE_LOGLEVEL', 'DEBUG').upper())
bumble.logging.setup_basic_logging('DEBUG')
asyncio.run(main())

View File

@@ -17,15 +17,14 @@
# -----------------------------------------------------------------------------
import asyncio
import sys
import os
import logging
from bumble.colors import color
from bumble.core import ProtocolError
from bumble.device import Device, Peer
from bumble.gatt import show_services
from bumble.transport import open_transport_or_link
from bumble.transport import open_transport
from bumble.utils import AsyncRunner
import bumble.logging
# -----------------------------------------------------------------------------
@@ -79,7 +78,7 @@ async def main() -> None:
return
print('<<< connecting to HCI...')
async with await open_transport_or_link(sys.argv[2]) as hci_transport:
async with await open_transport(sys.argv[2]) as hci_transport:
print('<<< connected')
# Create a device to manage the host, with a custom listener
@@ -101,5 +100,5 @@ async def main() -> None:
# -----------------------------------------------------------------------------
logging.basicConfig(level=os.environ.get('BUMBLE_LOGLEVEL', 'DEBUG').upper())
bumble.logging.setup_basic_logging('DEBUG')
asyncio.run(main())

View File

@@ -16,8 +16,7 @@
# Imports
# -----------------------------------------------------------------------------
import asyncio
import os
import logging
from bumble.colors import color
from bumble.core import ProtocolError
from bumble.controller import Controller
@@ -34,6 +33,7 @@ from bumble.gatt import (
GATT_DEVICE_INFORMATION_SERVICE,
)
from bumble.gatt_client import show_services
import bumble.logging
# -----------------------------------------------------------------------------
@@ -119,5 +119,5 @@ async def main() -> None:
# -----------------------------------------------------------------------------
logging.basicConfig(level=os.environ.get('BUMBLE_LOGLEVEL', 'DEBUG').upper())
bumble.logging.setup_basic_logging('DEBUG')
asyncio.run(main())

View File

@@ -17,11 +17,9 @@
# -----------------------------------------------------------------------------
import asyncio
import sys
import os
import logging
from bumble.device import Device, Connection
from bumble.transport import open_transport_or_link
from bumble.transport import open_transport
from bumble.att import ATT_Error, ATT_INSUFFICIENT_ENCRYPTION_ERROR
from bumble.gatt import (
Service,
@@ -32,6 +30,7 @@ from bumble.gatt import (
GATT_MANUFACTURER_NAME_STRING_CHARACTERISTIC,
GATT_DEVICE_INFORMATION_SERVICE,
)
import bumble.logging
# -----------------------------------------------------------------------------
@@ -81,7 +80,7 @@ async def main() -> None:
return
print('<<< connecting to HCI...')
async with await open_transport_or_link(sys.argv[2]) as hci_transport:
async with await open_transport(sys.argv[2]) as hci_transport:
print('<<< connected')
# Create a device to manage the host
@@ -152,5 +151,5 @@ async def main() -> None:
# -----------------------------------------------------------------------------
logging.basicConfig(level=os.environ.get('BUMBLE_LOGLEVEL', 'DEBUG').upper())
bumble.logging.setup_basic_logging('DEBUG')
asyncio.run(main())

View File

@@ -17,16 +17,15 @@
# -----------------------------------------------------------------------------
import asyncio
import sys
import os
import logging
from bumble.device import Device
from bumble.transport import open_transport_or_link
from bumble.transport import open_transport
from bumble.gatt import (
Service,
Characteristic,
)
from bumble.pairing import PairingConfig, PairingDelegate
import bumble.logging
# -----------------------------------------------------------------------------
@@ -58,7 +57,7 @@ async def main() -> None:
return
print('<<< connecting to HCI...')
async with await open_transport_or_link(sys.argv[2]) as hci_transport:
async with await open_transport(sys.argv[2]) as hci_transport:
print('<<< connected')
# Create a device to manage the host
@@ -107,5 +106,5 @@ async def main() -> None:
# -----------------------------------------------------------------------------
logging.basicConfig(level=os.environ.get('BUMBLE_LOGLEVEL', 'DEBUG').upper())
bumble.logging.setup_basic_logging('DEBUG')
asyncio.run(main())

View File

@@ -20,8 +20,6 @@ import asyncio
import dataclasses
import functools
import enum
import logging
import os
import random
import struct
import sys
@@ -34,6 +32,7 @@ from bumble import gatt_adapters
from bumble import gatt_client
from bumble import hci
from bumble import core
import bumble.logging
# -----------------------------------------------------------------------------
@@ -432,5 +431,5 @@ async def main() -> None:
# -----------------------------------------------------------------------------
logging.basicConfig(level=os.environ.get('BUMBLE_LOGLEVEL', 'INFO').upper())
bumble.logging.setup_basic_logging('DEBUG')
asyncio.run(main())

View File

@@ -16,9 +16,7 @@
# Imports
# -----------------------------------------------------------------------------
import asyncio
import logging
import sys
import os
from bumble.core import AdvertisingData
from bumble.device import Device
@@ -32,8 +30,9 @@ from bumble.profiles.hap import (
WritablePresetsSupport,
PresetRecord,
)
from bumble.transport import open_transport
import bumble.logging
from bumble.transport import open_transport_or_link
server_features = HearingAidFeatures(
HearingAidType.MONAURAL_HEARING_AID,
@@ -56,7 +55,7 @@ async def main() -> None:
return
print('<<< connecting to HCI...')
async with await open_transport_or_link(sys.argv[2]) as hci_transport:
async with await open_transport(sys.argv[2]) as hci_transport:
print('<<< connected')
device = Device.from_config_file_with_hci(
@@ -102,5 +101,5 @@ async def main() -> None:
# -----------------------------------------------------------------------------
logging.basicConfig(level=os.environ.get('BUMBLE_LOGLEVEL', 'DEBUG').upper())
bumble.logging.setup_basic_logging('DEBUG')
asyncio.run(main())

View File

@@ -18,7 +18,6 @@
import asyncio
import json
import sys
import os
import io
import logging
from typing import Iterable, Optional
@@ -27,9 +26,10 @@ import websockets
import bumble.core
from bumble.device import Device, ScoLink
from bumble.transport import open_transport_or_link
from bumble.transport import open_transport
from bumble.core import PhysicalTransport
from bumble import hci, rfcomm, hfp
import bumble.logging
logger = logging.getLogger(__name__)
@@ -191,7 +191,7 @@ async def main() -> None:
return
print('<<< connecting to HCI...')
async with await open_transport_or_link(sys.argv[2]) as hci_transport:
async with await open_transport(sys.argv[2]) as hci_transport:
print('<<< connected')
# Create a device
@@ -286,5 +286,5 @@ async def main() -> None:
# -----------------------------------------------------------------------------
logging.basicConfig(level=os.environ.get('BUMBLE_LOGLEVEL', 'DEBUG').upper())
bumble.logging.setup_basic_logging('DEBUG')
asyncio.run(main())

View File

@@ -18,20 +18,20 @@
import asyncio
import contextlib
import sys
import os
import logging
import json
import websockets
import functools
from typing import Optional
from bumble import utils
import websockets
from bumble import rfcomm
from bumble import hci
from bumble.device import Device, Connection
from bumble.transport import open_transport_or_link
from bumble.transport import open_transport
from bumble import hfp
from bumble.hfp import HfProtocol
import bumble.logging
ws: Optional[websockets.WebSocketServerProtocol] = None
hf_protocol: Optional[HfProtocol] = None
@@ -94,7 +94,7 @@ async def main() -> None:
return
print('<<< connecting to HCI...')
async with await open_transport_or_link(sys.argv[2]) as hci_transport:
async with await open_transport(sys.argv[2]) as hci_transport:
print('<<< connected')
# Hands-Free profile configuration.
@@ -175,5 +175,5 @@ async def main() -> None:
# -----------------------------------------------------------------------------
logging.basicConfig(level=os.environ.get('BUMBLE_LOGLEVEL', 'DEBUG').upper())
bumble.logging.setup_basic_logging('DEBUG')
asyncio.run(main())

View File

@@ -17,14 +17,13 @@
# -----------------------------------------------------------------------------
import asyncio
import sys
import os
import logging
import json
import websockets
import struct
import websockets
from bumble.device import Device
from bumble.transport import open_transport_or_link
from bumble.transport import open_transport
from bumble.core import (
PhysicalTransport,
BT_L2CAP_PROTOCOL_ID,
@@ -49,6 +48,8 @@ from bumble.sdp import (
SDP_SERVICE_RECORD_HANDLE_ATTRIBUTE_ID,
SDP_BROWSE_GROUP_LIST_ATTRIBUTE_ID,
)
import bumble.logging
# -----------------------------------------------------------------------------
# SDP attributes for Bluetooth HID devices
@@ -597,7 +598,7 @@ async def main() -> None:
asyncio.create_task(handle_virtual_cable_unplug())
print('<<< connecting to HCI...')
async with await open_transport_or_link(sys.argv[2]) as hci_transport:
async with await open_transport(sys.argv[2]) as hci_transport:
print('<<< connected')
# Create a device
@@ -744,5 +745,5 @@ async def main() -> None:
# -----------------------------------------------------------------------------
logging.basicConfig(level=os.environ.get('BUMBLE_LOGLEVEL', 'DEBUG').upper())
bumble.logging.setup_basic_logging('DEBUG')
asyncio.run(main())

View File

@@ -17,13 +17,11 @@
# -----------------------------------------------------------------------------
import asyncio
import sys
import os
import logging
from bumble.colors import color
from bumble.device import Device
from bumble.transport import open_transport_or_link
from bumble.transport import open_transport
from bumble.core import (
BT_HUMAN_INTERFACE_DEVICE_SERVICE,
PhysicalTransport,
@@ -41,6 +39,7 @@ from bumble.sdp import (
SDP_SERVICE_RECORD_HANDLE_ATTRIBUTE_ID,
SDP_BROWSE_GROUP_LIST_ATTRIBUTE_ID,
)
import bumble.logging
from hid_report_parser import ReportParser
# -----------------------------------------------------------------------------
@@ -324,7 +323,7 @@ async def main() -> None:
asyncio.create_task(handle_virtual_cable_unplug())
print('<<< connecting to HCI...')
async with await open_transport_or_link(sys.argv[2]) as hci_transport:
async with await open_transport(sys.argv[2]) as hci_transport:
print('<<< CONNECTED')
# Create a device
@@ -565,6 +564,5 @@ async def main() -> None:
# -----------------------------------------------------------------------------
logging.basicConfig(level=os.environ.get('BUMBLE_LOGLEVEL', 'DEBUG').upper())
bumble.logging.setup_basic_logging('DEBUG')
asyncio.run(main())

View File

@@ -16,13 +16,12 @@
# Imports
# -----------------------------------------------------------------------------
import asyncio
import logging
import sys
import os
import websockets
import json
from typing import Optional
import websockets
from bumble import utils
from bumble.core import AdvertisingData
from bumble.device import (
Device,
@@ -52,9 +51,8 @@ from bumble.profiles.mcp import (
MediaControlPointOpcode,
)
from bumble.profiles.pacs import PacRecord, PublishedAudioCapabilitiesService
from bumble.transport import open_transport_or_link
from typing import Optional
from bumble.transport import open_transport
import bumble.logging
# -----------------------------------------------------------------------------
@@ -64,7 +62,7 @@ async def main() -> None:
return
print('<<< connecting to HCI...')
async with await open_transport_or_link(sys.argv[2]) as hci_transport:
async with await open_transport(sys.argv[2]) as hci_transport:
print('<<< connected')
device = Device.from_config_file_with_hci(
@@ -191,5 +189,5 @@ async def main() -> None:
# -----------------------------------------------------------------------------
logging.basicConfig(level=os.environ.get('BUMBLE_LOGLEVEL', 'DEBUG').upper())
bumble.logging.setup_basic_logging('DEBUG')
asyncio.run(main())

View File

@@ -17,13 +17,12 @@
# -----------------------------------------------------------------------------
import asyncio
import sys
import os
import random
import logging
from bumble.device import Device, Connection
from bumble.transport import open_transport_or_link
from bumble.transport import open_transport
from bumble.gatt import Service, Characteristic
import bumble.logging
# -----------------------------------------------------------------------------
@@ -64,7 +63,7 @@ async def main() -> None:
return
print('<<< connecting to HCI...')
async with await open_transport_or_link(sys.argv[2]) as hci_transport:
async with await open_transport(sys.argv[2]) as hci_transport:
print('<<< connected')
# Create a device to manage the host
@@ -128,5 +127,5 @@ async def main() -> None:
# -----------------------------------------------------------------------------
logging.basicConfig(level=os.environ.get('BUMBLE_LOGLEVEL', 'DEBUG').upper())
bumble.logging.setup_basic_logging('DEBUG')
asyncio.run(main())

View File

@@ -17,14 +17,12 @@
# -----------------------------------------------------------------------------
import asyncio
import sys
import os
import logging
from bumble.colors import color
import bumble.core
from bumble.device import Device
from bumble.transport import open_transport_or_link
from bumble.transport import open_transport
from bumble.core import (
BT_L2CAP_PROTOCOL_ID,
BT_RFCOMM_PROTOCOL_ID,
@@ -39,6 +37,7 @@ from bumble.sdp import (
SDP_SERVICE_CLASS_ID_LIST_ATTRIBUTE_ID,
SDP_BLUETOOTH_PROFILE_DESCRIPTOR_LIST_ATTRIBUTE_ID,
)
import bumble.logging
# -----------------------------------------------------------------------------
@@ -178,7 +177,7 @@ async def main() -> None:
return
print('<<< connecting to HCI...')
async with await open_transport_or_link(sys.argv[2]) as hci_transport:
async with await open_transport(sys.argv[2]) as hci_transport:
print('<<< connected')
# Create a device
@@ -237,5 +236,5 @@ async def main() -> None:
# -----------------------------------------------------------------------------
logging.basicConfig(level=os.environ.get('BUMBLE_LOGLEVEL', 'DEBUG').upper())
bumble.logging.setup_basic_logging('DEBUG')
asyncio.run(main())

View File

@@ -17,15 +17,14 @@
# -----------------------------------------------------------------------------
import asyncio
import sys
import os
import logging
from bumble.core import UUID
from bumble.device import Device
from bumble.transport import open_transport_or_link
from bumble.transport import open_transport
from bumble.rfcomm import Server
from bumble.utils import AsyncRunner
from bumble.rfcomm import make_service_sdp_records
import bumble.logging
# -----------------------------------------------------------------------------
@@ -124,7 +123,7 @@ async def main() -> None:
uuid = 'E6D55659-C8B4-4B85-96BB-B1143AF6D3AE'
print('<<< connecting to HCI...')
async with await open_transport_or_link(sys.argv[2]) as hci_transport:
async with await open_transport(sys.argv[2]) as hci_transport:
print('<<< connected')
# Create a device
@@ -159,5 +158,5 @@ async def main() -> None:
# -----------------------------------------------------------------------------
logging.basicConfig(level=os.environ.get('BUMBLE_LOGLEVEL', 'DEBUG').upper())
bumble.logging.setup_basic_logging('DEBUG')
asyncio.run(main())

View File

@@ -17,12 +17,11 @@
# -----------------------------------------------------------------------------
import asyncio
import sys
import os
import logging
from bumble.colors import color
from bumble.hci import Address
from bumble.device import Device
from bumble.transport import open_transport_or_link
from bumble.transport import open_transport
from bumble import logging
# -----------------------------------------------------------------------------
@@ -33,7 +32,7 @@ async def main() -> None:
return
print('<<< connecting to HCI...')
async with await open_transport_or_link(sys.argv[1]) as hci_transport:
async with await open_transport(sys.argv[1]) as hci_transport:
print('<<< connected')
filter_duplicates = len(sys.argv) == 3 and sys.argv[2] == 'filter'
@@ -79,5 +78,5 @@ async def main() -> None:
# -----------------------------------------------------------------------------
logging.basicConfig(level=os.environ.get('BUMBLE_LOGLEVEL', 'DEBUG').upper())
logging.setup_basic_logging('DEBUG')
asyncio.run(main())

View File

@@ -18,9 +18,7 @@
import asyncio
import datetime
import functools
import logging
import sys
import os
import io
import struct
import secrets
@@ -45,7 +43,8 @@ from bumble.profiles.bap import (
from bumble.profiles.cap import CommonAudioServiceService
from bumble.profiles.csip import CoordinatedSetIdentificationService, SirkType
from bumble.profiles.pacs import PacRecord, PublishedAudioCapabilitiesService
from bumble.transport import open_transport_or_link
from bumble.transport import open_transport
import bumble.logging
def _sink_pac_record() -> PacRecord:
@@ -81,7 +80,7 @@ async def main() -> None:
return
print('<<< connecting to HCI...')
async with await open_transport_or_link(sys.argv[2]) as hci_transport:
async with await open_transport(sys.argv[2]) as hci_transport:
print('<<< connected')
device = Device.from_config_file_with_hci(
@@ -200,5 +199,5 @@ async def main() -> None:
# -----------------------------------------------------------------------------
logging.basicConfig(level=os.environ.get('BUMBLE_LOGLEVEL', 'DEBUG').upper())
bumble.logging.setup_basic_logging('DEBUG')
asyncio.run(main())

View File

@@ -16,12 +16,12 @@
# Imports
# -----------------------------------------------------------------------------
import asyncio
import logging
import sys
import os
import secrets
import websockets
import json
from typing import Optional
import websockets
from bumble.core import AdvertisingData
from bumble.device import Device, AdvertisingParameters, AdvertisingEventProperties
@@ -43,10 +43,8 @@ from bumble.profiles.pacs import PacRecord, PublishedAudioCapabilitiesService
from bumble.profiles.cap import CommonAudioServiceService
from bumble.profiles.csip import CoordinatedSetIdentificationService, SirkType
from bumble.profiles.vcs import VolumeControlService
from bumble.transport import open_transport_or_link
from typing import Optional
from bumble.transport import open_transport
import bumble.logging
def dumps_volume_state(volume_setting: int, muted: int, change_counter: int) -> str:
@@ -66,7 +64,7 @@ async def main() -> None:
return
print('<<< connecting to HCI...')
async with await open_transport_or_link(sys.argv[2]) as hci_transport:
async with await open_transport(sys.argv[2]) as hci_transport:
print('<<< connected')
device = Device.from_config_file_with_hci(
@@ -186,5 +184,5 @@ async def main() -> None:
# -----------------------------------------------------------------------------
logging.basicConfig(level=os.environ.get('BUMBLE_LOGLEVEL', 'DEBUG').upper())
bumble.logging.setup_basic_logging('DEBUG')
asyncio.run(main())

View File

@@ -85,7 +85,6 @@ bumble-scan = "bumble.apps.scan:main"
bumble-show = "bumble.apps.show:main"
bumble-unbond = "bumble.apps.unbond:main"
bumble-usb-probe = "bumble.apps.usb_probe:main"
bumble-link-relay = "bumble.apps.link_relay.link_relay:main"
bumble-bench = "bumble.apps.bench:main"
bumble-player = "bumble.apps.player.player:main"
bumble-speaker = "bumble.apps.speaker.speaker:main"
@@ -107,7 +106,6 @@ packages = [
"bumble.drivers",
"bumble.profiles",
"bumble.apps",
"bumble.apps.link_relay",
"bumble.pandora",
"bumble.tools",
]

View File

@@ -25,7 +25,7 @@ from bumble.core import PhysicalTransport
from bumble.link import LocalLink
from bumble.device import Device
from bumble.host import Host
from bumble.transport import AsyncPipeSink
from bumble.transport.common import AsyncPipeSink
from bumble.avdtp import (
AVDTP_IDLE_STATE,
AVDTP_STREAMING_STATE,

View File

@@ -54,7 +54,7 @@ from bumble.gatt_adapters import (
EnumCharacteristicAdapter,
EnumCharacteristicProxyAdapter,
)
from bumble.transport import AsyncPipeSink
from bumble.transport.common import AsyncPipeSink
from bumble.core import UUID
from bumble.att import (
Attribute,

View File

@@ -22,7 +22,7 @@ import unittest
from bumble.controller import Controller
from bumble.host import Host, DataPacketQueue
from bumble.transport import AsyncPipeSink
from bumble.transport.common import AsyncPipeSink
from bumble.hci import HCI_AclDataPacket
# -----------------------------------------------------------------------------

View File

@@ -23,13 +23,9 @@ import pytest
from unittest.mock import AsyncMock, MagicMock, patch
from bumble.controller import Controller
from bumble.core import PhysicalTransport
from bumble.link import LocalLink
from bumble.device import Device, Peer
from bumble.host import Host
from bumble.device import Peer
from bumble.gatt import Service, Characteristic
from bumble.transport import AsyncPipeSink
from bumble.pairing import PairingConfig, PairingDelegate
from bumble.smp import (
SMP_PAIRING_NOT_SUPPORTED_ERROR,
@@ -38,9 +34,10 @@ from bumble.smp import (
OobLegacyContext,
)
from bumble.core import ProtocolError
from bumble.keys import PairingKeys
from bumble.hci import Role
from .test_utils import TwoDevices
# -----------------------------------------------------------------------------
# Logging
@@ -49,63 +46,26 @@ logger = logging.getLogger(__name__)
# -----------------------------------------------------------------------------
class TwoDevices:
def __init__(self):
self.connections = [None, None]
addresses = ['F0:F1:F2:F3:F4:F5', 'F5:F4:F3:F2:F1:F0']
self.link = LocalLink()
self.controllers = [
Controller('C1', link=self.link, public_address=addresses[0]),
Controller('C2', link=self.link, public_address=addresses[1]),
]
self.devices = [
Device(
address=addresses[0],
host=Host(self.controllers[0], AsyncPipeSink(self.controllers[0])),
),
Device(
address=addresses[1],
host=Host(self.controllers[1], AsyncPipeSink(self.controllers[1])),
),
]
self.paired = [
asyncio.get_event_loop().create_future(),
asyncio.get_event_loop().create_future(),
]
def on_connection(self, which, connection):
self.connections[which] = connection
def on_paired(self, which: int, keys: PairingKeys):
self.paired[which].set_result(keys)
@pytest.mark.asyncio
async def test_self_connection():
two_devices = TwoDevices()
await two_devices.setup_connection()
# -----------------------------------------------------------------------------
@pytest.mark.asyncio
async def test_self_connection():
# Create two devices, each with a controller, attached to the same link
async def test_self_disconnection():
two_devices = TwoDevices()
await two_devices.setup_connection()
await two_devices.connections[0].disconnect()
assert two_devices.connections[0] is None
assert two_devices.connections[1] is None
# Attach listeners
two_devices.devices[0].on(
'connection', lambda connection: two_devices.on_connection(0, connection)
)
two_devices.devices[1].on(
'connection', lambda connection: two_devices.on_connection(1, connection)
)
# Start
await two_devices.devices[0].power_on()
await two_devices.devices[1].power_on()
# Connect the two devices
await two_devices.devices[0].connect(two_devices.devices[1].random_address)
# Check the post conditions
assert two_devices.connections[0] is not None
assert two_devices.connections[1] is not None
two_devices = TwoDevices()
await two_devices.setup_connection()
await two_devices.connections[1].disconnect()
assert two_devices.connections[0] is None
assert two_devices.connections[1] is None
# -----------------------------------------------------------------------------
@@ -115,24 +75,14 @@ async def test_self_connection():
(Role.CENTRAL, Role.PERIPHERAL),
)
async def test_self_classic_connection(responder_role):
# Create two devices, each with a controller, attached to the same link
two_devices = TwoDevices()
# Attach listeners
two_devices.devices[0].on(
'connection', lambda connection: two_devices.on_connection(0, connection)
)
two_devices.devices[1].on(
'connection', lambda connection: two_devices.on_connection(1, connection)
)
# Enable Classic connections
two_devices.devices[0].classic_enabled = True
two_devices.devices[1].classic_enabled = True
# Start
await two_devices.devices[0].power_on()
await two_devices.devices[1].power_on()
await two_devices.setup_connection()
# Connect the two devices
await asyncio.gather(
@@ -203,15 +153,9 @@ async def test_self_gatt():
s4 = Service('3A12C182-14E2-4FE0-8C5B-65D7C569F9DB', [], included_services=[s2, s3])
two_devices.devices[1].add_services([s1, s2, s4])
# Start
await two_devices.devices[0].power_on()
await two_devices.devices[1].power_on()
# Connect the two devices
connection = await two_devices.devices[0].connect(
two_devices.devices[1].random_address
)
peer = Peer(connection)
await two_devices.setup_connection()
peer = Peer(two_devices.connections[0])
bogus_uuid = 'A0AA6007-0B48-4BBE-80AC-0DE9AAF541EA'
result = await peer.discover_services([bogus_uuid])
@@ -264,15 +208,9 @@ async def test_self_gatt_long_read():
service = Service('8140E247-04F0-42C1-BC34-534C344DAFCA', characteristics)
two_devices.devices[1].add_service(service)
# Start
await two_devices.devices[0].power_on()
await two_devices.devices[1].power_on()
# Connect the two devices
connection = await two_devices.devices[0].connect(
two_devices.devices[1].random_address
)
peer = Peer(connection)
await two_devices.setup_connection()
peer = Peer(two_devices.connections[0])
result = await peer.discover_service(service.uuid)
assert len(result) == 1
@@ -289,25 +227,12 @@ async def _test_self_smp_with_configs(pairing_config1, pairing_config2):
# Create two devices, each with a controller, attached to the same link
two_devices = TwoDevices()
# Start
await two_devices.devices[0].power_on()
await two_devices.devices[1].power_on()
# Attach listeners
two_devices.devices[0].on(
'connection', lambda connection: two_devices.on_connection(0, connection)
)
two_devices.devices[1].on(
'connection', lambda connection: two_devices.on_connection(1, connection)
)
# Connect the two devices
connection = await two_devices.devices[0].connect(
two_devices.devices[1].random_address
)
await two_devices.setup_connection()
connection = two_devices.connections[0]
assert not connection.is_encrypted
# Attach connection listeners
# Attach pairing listeners
two_devices.connections[0].on(
'pairing', lambda keys: two_devices.on_paired(0, keys)
)
@@ -488,23 +413,13 @@ async def test_self_smp_over_classic():
# Create two devices, each with a controller, attached to the same link
two_devices = TwoDevices()
# Attach listeners
two_devices.devices[0].on(
'connection', lambda connection: two_devices.on_connection(0, connection)
)
two_devices.devices[1].on(
'connection', lambda connection: two_devices.on_connection(1, connection)
)
# Enable Classic connections
two_devices.devices[0].classic_enabled = True
two_devices.devices[1].classic_enabled = True
# Start
# Connect the two devices
await two_devices.devices[0].power_on()
await two_devices.devices[1].power_on()
# Connect the two devices
await asyncio.gather(
two_devices.devices[0].connect(
two_devices.devices[1].public_address, transport=PhysicalTransport.BR_EDR
@@ -650,6 +565,7 @@ async def test_self_smp_oob_legacy():
# -----------------------------------------------------------------------------
async def run_test_self():
await test_self_connection()
await test_self_disconnection()
await test_self_gatt()
await test_self_gatt_long_read()
await test_self_smp()

View File

@@ -23,8 +23,9 @@ from bumble.controller import Controller
from bumble.link import LocalLink
from bumble.device import Device, Connection
from bumble.host import Host
from bumble.transport import AsyncPipeSink
from bumble.transport.common import AsyncPipeSink
from bumble.hci import Address
from bumble.keys import PairingKeys
# -----------------------------------------------------------------------------
@@ -51,16 +52,6 @@ class TwoDevices:
),
]
self.paired = [None, None]
def on_connection(self, which, connection):
self.connections[which] = connection
def on_paired(self, which, keys):
self.paired[which] = keys
async def setup_connection(self) -> None:
# Attach listeners
self.devices[0].on(
'connection', lambda connection: self.on_connection(0, connection)
)
@@ -68,6 +59,22 @@ class TwoDevices:
'connection', lambda connection: self.on_connection(1, connection)
)
self.paired = [
asyncio.get_event_loop().create_future(),
asyncio.get_event_loop().create_future(),
]
def on_connection(self, which, connection):
self.connections[which] = connection
connection.on('disconnection', lambda code: self.on_disconnection(which))
def on_disconnection(self, which):
self.connections[which] = None
def on_paired(self, which: int, keys: PairingKeys) -> None:
self.paired[which].set_result(keys)
async def setup_connection(self) -> None:
# Start
await self.devices[0].power_on()
await self.devices[1].power_on()

View File

@@ -17,7 +17,6 @@
# -----------------------------------------------------------------------------
import logging
import asyncio
import os
from typing import Any, Optional
import click
@@ -26,6 +25,8 @@ from bumble.colors import color
from bumble import transport
from bumble.drivers import intel
from bumble.host import Host
import bumble.logging
# -----------------------------------------------------------------------------
# Logging
@@ -107,7 +108,7 @@ async def do_bootloader(usb_transport: str, force: bool) -> None:
# -----------------------------------------------------------------------------
@click.group()
def main():
logging.basicConfig(level=os.environ.get('BUMBLE_LOGLEVEL', 'INFO').upper())
bumble.logging.setup_basic_logging()
@main.command

View File

@@ -15,15 +15,16 @@
# -----------------------------------------------------------------------------
# Imports
# -----------------------------------------------------------------------------
import logging
import asyncio
import os
import logging
import click
from bumble import transport
from bumble.host import Host
from bumble.drivers import rtk
import bumble.logging
# -----------------------------------------------------------------------------
# Logging
@@ -50,7 +51,7 @@ def do_parse(firmware_path):
# -----------------------------------------------------------------------------
async def do_load(usb_transport, force):
async with await transport.open_transport_or_link(usb_transport) as (
async with await transport.open_transport(usb_transport) as (
hci_source,
hci_sink,
):
@@ -69,7 +70,7 @@ async def do_load(usb_transport, force):
# -----------------------------------------------------------------------------
async def do_drop(usb_transport):
async with await transport.open_transport_or_link(usb_transport) as (
async with await transport.open_transport(usb_transport) as (
hci_source,
hci_sink,
):
@@ -112,7 +113,7 @@ async def do_info(usb_transport, force):
# -----------------------------------------------------------------------------
@click.group()
def main():
logging.basicConfig(level=os.environ.get('BUMBLE_LOGLEVEL', 'INFO').upper())
bumble.logging.setup_basic_logging()
@main.command