From 1b44e73f90d26c86050aa66e27610ff981aad30e Mon Sep 17 00:00:00 2001 From: Josh Wu Date: Mon, 21 Jul 2025 11:32:16 +0800 Subject: [PATCH] Remove link-relay and RemoteLink --- apps/README.md | 3 - apps/bench.py | 6 +- apps/console.py | 4 +- apps/controller_info.py | 4 +- apps/controller_loopback.py | 4 +- apps/controllers.py | 4 +- apps/device_info.py | 4 +- apps/gatt_dump.py | 4 +- apps/gg_bridge.py | 4 +- apps/hci_bridge.py | 4 +- apps/l2cap_bridge.py | 4 +- apps/link_relay/__init__.py | 0 apps/link_relay/link_relay.py | 289 ------------------ apps/link_relay/logging.yml | 21 -- apps/pair.py | 4 +- apps/rfcomm_bridge.py | 2 +- apps/scan.py | 4 +- bumble/link.py | 270 +--------------- bumble/transport/__init__.py | 18 +- docs/mkdocs/mkdocs.yml | 1 - docs/mkdocs/src/apps_and_tools/index.md | 1 - docs/mkdocs/src/apps_and_tools/link_relay.md | 33 -- docs/mkdocs/src/index.md | 7 - examples/battery_server.py | 4 +- examples/device_information_server.py | 4 +- examples/heart_rate_server.py | 4 +- examples/keyboard.py | 4 +- examples/run_a2dp_info.py | 4 +- examples/run_a2dp_sink.py | 4 +- examples/run_a2dp_source.py | 4 +- examples/run_advertiser.py | 4 +- examples/run_asha_sink.py | 4 +- examples/run_avrcp.py | 4 +- examples/run_channel_sounding.py | 4 +- examples/run_cig_setup.py | 4 +- examples/run_classic_connect.py | 4 +- examples/run_classic_discoverable.py | 4 +- examples/run_classic_discovery.py | 4 +- examples/run_connect_and_encrypt.py | 4 +- examples/run_controller.py | 4 +- examples/run_controller_with_scanner.py | 4 +- examples/run_csis_servers.py | 4 +- examples/run_device_with_snooper.py | 4 +- examples/run_esco_connection.py | 4 +- examples/run_extended_advertiser.py | 4 +- examples/run_extended_advertiser_2.py | 4 +- examples/run_gatt_client.py | 4 +- examples/run_gatt_server.py | 4 +- .../run_gatt_server_with_pairing_delegate.py | 4 +- examples/run_hap_server.py | 4 +- examples/run_hfp_gateway.py | 4 +- examples/run_hfp_handsfree.py | 4 +- examples/run_hid_device.py | 4 +- examples/run_hid_host.py | 4 +- examples/run_mcp_client.py | 4 +- examples/run_notifier.py | 4 +- examples/run_rfcomm_client.py | 4 +- examples/run_rfcomm_server.py | 4 +- examples/run_scanner.py | 4 +- examples/run_unicast_server.py | 4 +- examples/run_vcp_renderer.py | 4 +- pyproject.toml | 2 - tests/a2dp_test.py | 2 +- tests/gatt_test.py | 2 +- tests/host_test.py | 2 +- tests/self_test.py | 2 +- tests/test_utils.py | 2 +- tools/rtk_util.py | 4 +- 68 files changed, 114 insertions(+), 749 deletions(-) delete mode 100644 apps/link_relay/__init__.py delete mode 100644 apps/link_relay/link_relay.py delete mode 100644 apps/link_relay/logging.yml delete mode 100644 docs/mkdocs/src/apps_and_tools/link_relay.md diff --git a/apps/README.md b/apps/README.md index 814d23d..af0893c 100644 --- a/apps/README.md +++ b/apps/README.md @@ -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 diff --git a/apps/bench.py b/apps/bench.py index 5941001..e0df336 100644 --- a/apps/bench.py +++ b/apps/bench.py @@ -65,7 +65,7 @@ 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 @@ -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, ): diff --git a/apps/console.py b/apps/console.py index 7ad99de..29e0522 100644 --- a/apps/console.py +++ b/apps/console.py @@ -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 diff --git a/apps/controller_info.py b/apps/controller_info.py index 67f40ae..efda714 100644 --- a/apps/controller_info.py +++ b/apps/controller_info.py @@ -58,7 +58,7 @@ 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 # ----------------------------------------------------------------------------- @@ -246,7 +246,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) diff --git a/apps/controller_loopback.py b/apps/controller_loopback.py index 434704f..a2bdffe 100644 --- a/apps/controller_loopback.py +++ b/apps/controller_loopback.py @@ -29,7 +29,7 @@ from bumble.hci import ( LoopbackMode, ) from bumble.host import Host -from bumble.transport import open_transport_or_link +from bumble.transport import open_transport import click @@ -88,7 +88,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, ): diff --git a/apps/controllers.py b/apps/controllers.py index ac6477e..09974a8 100644 --- a/apps/controllers.py +++ b/apps/controllers.py @@ -22,7 +22,7 @@ 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 # ----------------------------------------------------------------------------- @@ -42,7 +42,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}', diff --git a/apps/device_info.py b/apps/device_info.py index 2e57a3f..979546d 100644 --- a/apps/device_info.py +++ b/apps/device_info.py @@ -32,7 +32,7 @@ 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 # ----------------------------------------------------------------------------- @@ -215,7 +215,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: diff --git a/apps/gatt_dump.py b/apps/gatt_dump.py index 3b3e874..c09dc8a 100644 --- a/apps/gatt_dump.py +++ b/apps/gatt_dump.py @@ -24,7 +24,7 @@ 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 # ----------------------------------------------------------------------------- @@ -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: diff --git a/apps/gg_bridge.py b/apps/gg_bridge.py index c1ee2fb..9fb27de 100644 --- a/apps/gg_bridge.py +++ b/apps/gg_bridge.py @@ -27,7 +27,7 @@ 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 @@ -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 diff --git a/apps/hci_bridge.py b/apps/hci_bridge.py index 00093a0..883166c 100644 --- a/apps/hci_bridge.py +++ b/apps/hci_bridge.py @@ -46,14 +46,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, ): diff --git a/apps/l2cap_bridge.py b/apps/l2cap_bridge.py index 7d744bc..89d3a14 100644 --- a/apps/l2cap_bridge.py +++ b/apps/l2cap_bridge.py @@ -22,7 +22,7 @@ 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 @@ -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) diff --git a/apps/link_relay/__init__.py b/apps/link_relay/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/apps/link_relay/link_relay.py b/apps/link_relay/link_relay.py deleted file mode 100644 index 6036fa0..0000000 --- a/apps/link_relay/link_relay.py +++ /dev/null @@ -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:///. - 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() diff --git a/apps/link_relay/logging.yml b/apps/link_relay/logging.yml deleted file mode 100644 index 0f6df12..0000000 --- a/apps/link_relay/logging.yml +++ /dev/null @@ -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 diff --git a/apps/pair.py b/apps/pair.py index 9567e19..94e65aa 100644 --- a/apps/pair.py +++ b/apps/pair.py @@ -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 diff --git a/apps/rfcomm_bridge.py b/apps/rfcomm_bridge.py index 9fb55c9..5946b10 100644 --- a/apps/rfcomm_bridge.py +++ b/apps/rfcomm_bridge.py @@ -406,7 +406,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, ): diff --git a/apps/scan.py b/apps/scan.py index 9780551..9de272d 100644 --- a/apps/scan.py +++ b/apps/scan.py @@ -22,7 +22,7 @@ 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 @@ -127,7 +127,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: diff --git a/bumble/link.py b/bumble/link.py index 06e291e..c40e4b8 100644 --- a/bumble/link.py +++ b/bumble/link.py @@ -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: @@ -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()}', - ) - ) diff --git a/bumble/transport/__init__.py b/bumble/transport/__init__.py index 34bf374..5473771 100644 --- a/bumble/transport/__init__.py +++ b/bumble/transport/__init__.py @@ -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) diff --git a/docs/mkdocs/mkdocs.yml b/docs/mkdocs/mkdocs.yml index f8e92b4..b89d358 100644 --- a/docs/mkdocs/mkdocs.yml +++ b/docs/mkdocs/mkdocs.yml @@ -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: diff --git a/docs/mkdocs/src/apps_and_tools/index.md b/docs/mkdocs/src/apps_and_tools/index.md index e99a018..51210b4 100644 --- a/docs/mkdocs/src/apps_and_tools/index.md +++ b/docs/mkdocs/src/apps_and_tools/index.md @@ -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. diff --git a/docs/mkdocs/src/apps_and_tools/link_relay.md b/docs/mkdocs/src/apps_and_tools/link_relay.md deleted file mode 100644 index ff3fb8b..0000000 --- a/docs/mkdocs/src/apps_and_tools/link_relay.md +++ /dev/null @@ -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:///` where `` is the hostname to connect to and `` 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` diff --git a/docs/mkdocs/src/index.md b/docs/mkdocs/src/index.md index 8c057c8..12a3cf3 100644 --- a/docs/mkdocs/src/index.md +++ b/docs/mkdocs/src/index.md @@ -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. diff --git a/examples/battery_server.py b/examples/battery_server.py index f509954..e519d4e 100644 --- a/examples/battery_server.py +++ b/examples/battery_server.py @@ -24,7 +24,7 @@ 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 @@ -35,7 +35,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 ) diff --git a/examples/device_information_server.py b/examples/device_information_server.py index 92474bc..e51e069 100644 --- a/examples/device_information_server.py +++ b/examples/device_information_server.py @@ -23,7 +23,7 @@ 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 @@ -34,7 +34,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 ) diff --git a/examples/heart_rate_server.py b/examples/heart_rate_server.py index 3ccf192..39471de 100644 --- a/examples/heart_rate_server.py +++ b/examples/heart_rate_server.py @@ -26,7 +26,7 @@ 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 @@ -39,7 +39,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 ) diff --git a/examples/keyboard.py b/examples/keyboard.py index 52a4c78..7270fee 100644 --- a/examples/keyboard.py +++ b/examples/keyboard.py @@ -27,7 +27,7 @@ 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, @@ -434,7 +434,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 diff --git a/examples/run_a2dp_info.py b/examples/run_a2dp_info.py index ff8ded8..29f85bb 100644 --- a/examples/run_a2dp_info.py +++ b/examples/run_a2dp_info.py @@ -22,7 +22,7 @@ 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, @@ -146,7 +146,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 diff --git a/examples/run_a2dp_sink.py b/examples/run_a2dp_sink.py index 216acb7..828f02b 100644 --- a/examples/run_a2dp_sink.py +++ b/examples/run_a2dp_sink.py @@ -22,7 +22,7 @@ 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, @@ -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: diff --git a/examples/run_a2dp_source.py b/examples/run_a2dp_source.py index ba4e24e..40d91c3 100644 --- a/examples/run_a2dp_source.py +++ b/examples/run_a2dp_source.py @@ -22,7 +22,7 @@ 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, @@ -120,7 +120,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 diff --git a/examples/run_advertiser.py b/examples/run_advertiser.py index 4c67d10..0c28a12 100644 --- a/examples/run_advertiser.py +++ b/examples/run_advertiser.py @@ -24,7 +24,7 @@ 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 # ----------------------------------------------------------------------------- @@ -50,7 +50,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( diff --git a/examples/run_asha_sink.py b/examples/run_asha_sink.py index 485e17e..2ffc89e 100644 --- a/examples/run_asha_sink.py +++ b/examples/run_asha_sink.py @@ -27,7 +27,7 @@ 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 ws_connection: Optional[websockets.WebSocketServerProtocol] = None @@ -50,7 +50,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 ) diff --git a/examples/run_avrcp.py b/examples/run_avrcp.py index eb31b0f..abe2330 100644 --- a/examples/run_avrcp.py +++ b/examples/run_avrcp.py @@ -24,7 +24,7 @@ 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 @@ -344,7 +344,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 diff --git a/examples/run_channel_sounding.py b/examples/run_channel_sounding.py index 346b775..94a9718 100644 --- a/examples/run_channel_sounding.py +++ b/examples/run_channel_sounding.py @@ -26,7 +26,7 @@ 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 # 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( diff --git a/examples/run_cig_setup.py b/examples/run_cig_setup.py index 04c9c40..8e0e2f5 100644 --- a/examples/run_cig_setup.py +++ b/examples/run_cig_setup.py @@ -25,7 +25,7 @@ from bumble.hci import ( OwnAddressType, ) -from bumble.transport import open_transport_or_link +from bumble.transport import open_transport # ----------------------------------------------------------------------------- @@ -40,7 +40,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') diff --git a/examples/run_classic_connect.py b/examples/run_classic_connect.py index 652858b..2e67c3d 100644 --- a/examples/run_classic_connect.py +++ b/examples/run_classic_connect.py @@ -22,7 +22,7 @@ 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_L2CAP_PROTOCOL_ID, CommandTimeoutError from bumble.sdp import ( Client as SDP_Client, @@ -42,7 +42,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 diff --git a/examples/run_classic_discoverable.py b/examples/run_classic_discoverable.py index 52f47fc..d23bc41 100644 --- a/examples/run_classic_discoverable.py +++ b/examples/run_classic_discoverable.py @@ -21,7 +21,7 @@ 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, @@ -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 diff --git a/examples/run_classic_discovery.py b/examples/run_classic_discovery.py index af35bb7..04d272e 100644 --- a/examples/run_classic_discovery.py +++ b/examples/run_classic_discovery.py @@ -22,7 +22,7 @@ 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 @@ -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( diff --git a/examples/run_connect_and_encrypt.py b/examples/run_connect_and_encrypt.py index 3b9d180..03d2d7b 100644 --- a/examples/run_connect_and_encrypt.py +++ b/examples/run_connect_and_encrypt.py @@ -21,7 +21,7 @@ import os import logging from bumble.device import Device -from bumble.transport import open_transport_or_link +from bumble.transport import open_transport # ----------------------------------------------------------------------------- @@ -37,7 +37,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 diff --git a/examples/run_controller.py b/examples/run_controller.py index 05dedfc..fc12b6b 100644 --- a/examples/run_controller.py +++ b/examples/run_controller.py @@ -32,7 +32,7 @@ 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 # ----------------------------------------------------------------------------- @@ -49,7 +49,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 diff --git a/examples/run_controller_with_scanner.py b/examples/run_controller_with_scanner.py index 9e935a9..0ba84ef 100644 --- a/examples/run_controller_with_scanner.py +++ b/examples/run_controller_with_scanner.py @@ -24,7 +24,7 @@ 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 # ----------------------------------------------------------------------------- @@ -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 diff --git a/examples/run_csis_servers.py b/examples/run_csis_servers.py index 7bb1492..44572cc 100644 --- a/examples/run_csis_servers.py +++ b/examples/run_csis_servers.py @@ -29,7 +29,7 @@ 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 # ----------------------------------------------------------------------------- @@ -44,7 +44,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') diff --git a/examples/run_device_with_snooper.py b/examples/run_device_with_snooper.py index 8575087..e4451fd 100644 --- a/examples/run_device_with_snooper.py +++ b/examples/run_device_with_snooper.py @@ -21,7 +21,7 @@ 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 @@ -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( diff --git a/examples/run_esco_connection.py b/examples/run_esco_connection.py index 3681df7..31e3079 100644 --- a/examples/run_esco_connection.py +++ b/examples/run_esco_connection.py @@ -24,7 +24,7 @@ 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 # ----------------------------------------------------------------------------- @@ -42,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') diff --git a/examples/run_extended_advertiser.py b/examples/run_extended_advertiser.py index 6605cfa..8e4d80f 100644 --- a/examples/run_extended_advertiser.py +++ b/examples/run_extended_advertiser.py @@ -27,7 +27,7 @@ from bumble.device import ( ) from bumble.hci import Address -from bumble.transport import open_transport_or_link +from bumble.transport import open_transport # ----------------------------------------------------------------------------- @@ -52,7 +52,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( diff --git a/examples/run_extended_advertiser_2.py b/examples/run_extended_advertiser_2.py index 6f8d123..24443a2 100644 --- a/examples/run_extended_advertiser_2.py +++ b/examples/run_extended_advertiser_2.py @@ -22,7 +22,7 @@ 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 # ----------------------------------------------------------------------------- @@ -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( diff --git a/examples/run_gatt_client.py b/examples/run_gatt_client.py index 4548159..e2af312 100644 --- a/examples/run_gatt_client.py +++ b/examples/run_gatt_client.py @@ -24,7 +24,7 @@ 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 @@ -79,7 +79,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 diff --git a/examples/run_gatt_server.py b/examples/run_gatt_server.py index 1860cf2..84937c2 100644 --- a/examples/run_gatt_server.py +++ b/examples/run_gatt_server.py @@ -21,7 +21,7 @@ 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, @@ -81,7 +81,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 diff --git a/examples/run_gatt_server_with_pairing_delegate.py b/examples/run_gatt_server_with_pairing_delegate.py index 6e1f3e7..def8ab6 100644 --- a/examples/run_gatt_server_with_pairing_delegate.py +++ b/examples/run_gatt_server_with_pairing_delegate.py @@ -21,7 +21,7 @@ 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, @@ -58,7 +58,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 diff --git a/examples/run_hap_server.py b/examples/run_hap_server.py index 666b48f..a273971 100644 --- a/examples/run_hap_server.py +++ b/examples/run_hap_server.py @@ -33,7 +33,7 @@ from bumble.profiles.hap import ( PresetRecord, ) -from bumble.transport import open_transport_or_link +from bumble.transport import open_transport server_features = HearingAidFeatures( HearingAidType.MONAURAL_HEARING_AID, @@ -56,7 +56,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( diff --git a/examples/run_hfp_gateway.py b/examples/run_hfp_gateway.py index 7b2bb80..6d08a9b 100644 --- a/examples/run_hfp_gateway.py +++ b/examples/run_hfp_gateway.py @@ -27,7 +27,7 @@ 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 @@ -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 diff --git a/examples/run_hfp_handsfree.py b/examples/run_hfp_handsfree.py index 16f420a..e690e8a 100644 --- a/examples/run_hfp_handsfree.py +++ b/examples/run_hfp_handsfree.py @@ -29,7 +29,7 @@ from bumble import utils 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 @@ -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. diff --git a/examples/run_hid_device.py b/examples/run_hid_device.py index d9e5b73..e803e7b 100644 --- a/examples/run_hid_device.py +++ b/examples/run_hid_device.py @@ -24,7 +24,7 @@ import websockets import struct 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, @@ -597,7 +597,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 diff --git a/examples/run_hid_host.py b/examples/run_hid_host.py index 12d0108..eb09ef1 100644 --- a/examples/run_hid_host.py +++ b/examples/run_hid_host.py @@ -23,7 +23,7 @@ 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, @@ -324,7 +324,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 diff --git a/examples/run_mcp_client.py b/examples/run_mcp_client.py index 9ec30ec..a1702ed 100644 --- a/examples/run_mcp_client.py +++ b/examples/run_mcp_client.py @@ -52,7 +52,7 @@ from bumble.profiles.mcp import ( MediaControlPointOpcode, ) from bumble.profiles.pacs import PacRecord, PublishedAudioCapabilitiesService -from bumble.transport import open_transport_or_link +from bumble.transport import open_transport from typing import Optional @@ -64,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( diff --git a/examples/run_notifier.py b/examples/run_notifier.py index 869e716..7f6f4c6 100644 --- a/examples/run_notifier.py +++ b/examples/run_notifier.py @@ -22,7 +22,7 @@ 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 @@ -64,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') # Create a device to manage the host diff --git a/examples/run_rfcomm_client.py b/examples/run_rfcomm_client.py index 7b97fc8..a0083a8 100644 --- a/examples/run_rfcomm_client.py +++ b/examples/run_rfcomm_client.py @@ -24,7 +24,7 @@ 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, @@ -178,7 +178,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 diff --git a/examples/run_rfcomm_server.py b/examples/run_rfcomm_server.py index 14bc7eb..483e6b1 100644 --- a/examples/run_rfcomm_server.py +++ b/examples/run_rfcomm_server.py @@ -22,7 +22,7 @@ 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 @@ -124,7 +124,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 diff --git a/examples/run_scanner.py b/examples/run_scanner.py index 5748b48..8d68986 100644 --- a/examples/run_scanner.py +++ b/examples/run_scanner.py @@ -22,7 +22,7 @@ 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 # ----------------------------------------------------------------------------- @@ -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') filter_duplicates = len(sys.argv) == 3 and sys.argv[2] == 'filter' diff --git a/examples/run_unicast_server.py b/examples/run_unicast_server.py index 23e350f..78dbe67 100644 --- a/examples/run_unicast_server.py +++ b/examples/run_unicast_server.py @@ -45,7 +45,7 @@ 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 def _sink_pac_record() -> PacRecord: @@ -81,7 +81,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( diff --git a/examples/run_vcp_renderer.py b/examples/run_vcp_renderer.py index 116d4d6..86596f9 100644 --- a/examples/run_vcp_renderer.py +++ b/examples/run_vcp_renderer.py @@ -44,7 +44,7 @@ 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 bumble.transport import open_transport from typing import Optional @@ -66,7 +66,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( diff --git a/pyproject.toml b/pyproject.toml index 7c78875..d1f4969 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -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", ] diff --git a/tests/a2dp_test.py b/tests/a2dp_test.py index 2a359b5..f790c51 100644 --- a/tests/a2dp_test.py +++ b/tests/a2dp_test.py @@ -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, diff --git a/tests/gatt_test.py b/tests/gatt_test.py index c27f2d3..4af609a 100644 --- a/tests/gatt_test.py +++ b/tests/gatt_test.py @@ -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, diff --git a/tests/host_test.py b/tests/host_test.py index ac3af9d..1f92a76 100644 --- a/tests/host_test.py +++ b/tests/host_test.py @@ -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 # ----------------------------------------------------------------------------- diff --git a/tests/self_test.py b/tests/self_test.py index 2456742..55efa6a 100644 --- a/tests/self_test.py +++ b/tests/self_test.py @@ -29,7 +29,7 @@ from bumble.link import LocalLink from bumble.device import Device, Peer from bumble.host import Host from bumble.gatt import Service, Characteristic -from bumble.transport import AsyncPipeSink +from bumble.transport.common import AsyncPipeSink from bumble.pairing import PairingConfig, PairingDelegate from bumble.smp import ( SMP_PAIRING_NOT_SUPPORTED_ERROR, diff --git a/tests/test_utils.py b/tests/test_utils.py index 143ecc7..136a3c6 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -23,7 +23,7 @@ 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 diff --git a/tools/rtk_util.py b/tools/rtk_util.py index 35afd92..8d4b4fa 100644 --- a/tools/rtk_util.py +++ b/tools/rtk_util.py @@ -50,7 +50,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 +69,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, ):