From 7208fd6642f6628b805c357dd3cc6142d5d02706 Mon Sep 17 00:00:00 2001 From: Abel Lucas Date: Wed, 19 Oct 2022 16:39:06 +0000 Subject: [PATCH] classic: update `Device.connect` to allow parallels connection creation According to the specification nothing prevent the Host from creating multiple connections at the same time. This commit add this mechanisme by matching the `connection` and `connection_failure` events against the peer address. --- bumble/device.py | 35 ++++++++++++++++++++++++++--------- bumble/host.py | 4 ++-- 2 files changed, 28 insertions(+), 11 deletions(-) diff --git a/bumble/device.py b/bumble/device.py index ae15088..5393573 100644 --- a/bumble/device.py +++ b/bumble/device.py @@ -1144,7 +1144,7 @@ class Device(CompositeEventEmitter): transport = BT_LE_TRANSPORT # Check that there isn't already a pending connection - if self.is_connecting: + if transport == BT_LE_TRANSPORT and self.is_connecting: raise InvalidStateError('connection already pending') if type(peer_address) is str: @@ -1155,10 +1155,22 @@ class Device(CompositeEventEmitter): logger.debug('looking for peer by name') peer_address = await self.find_peer_by_name(peer_address, transport) # TODO: timeout + def on_connection(connection): + if transport == BT_LE_TRANSPORT or ( + # match BR/EDR connection event against peer address + connection.transport == transport and connection.peer_address == peer_address): + pending_connection.set_result(connection) + + def on_connection_failure(error): + if transport == BT_LE_TRANSPORT or ( + # match BR/EDR connection failure event against peer address + error.transport == transport and error.peer_address == peer_address): + pending_connection.set_exception(error) + # Create a future so that we can wait for the connection's result pending_connection = asyncio.get_running_loop().create_future() - self.on('connection', pending_connection.set_result) - self.on('connection_failure', pending_connection.set_exception) + self.on('connection', on_connection) + self.on('connection_failure', on_connection_failure) try: # Tell the controller to connect @@ -1248,7 +1260,8 @@ class Device(CompositeEventEmitter): raise HCI_StatusError(result) # Wait for the connection process to complete - self.connecting = True + if transport == BT_LE_TRANSPORT: + self.connecting = True if timeout is None: return await pending_connection else: @@ -1265,9 +1278,10 @@ class Device(CompositeEventEmitter): except ConnectionError: raise TimeoutError() finally: - self.remove_listener('connection', pending_connection.set_result) - self.remove_listener('connection_failure', pending_connection.set_exception) - self.connecting = False + self.remove_listener('connection', on_connection) + self.remove_listener('connection_failure', on_connection_failure) + if transport == BT_LE_TRANSPORT: + self.connecting = False @asynccontextmanager async def connect_as_gatt(self, peer_address): @@ -1704,11 +1718,11 @@ class Device(CompositeEventEmitter): asyncio.create_task(new_connection()) @host_event_handler - def on_connection_failure(self, connection_handle, error_code): + def on_connection_failure(self, transport, connection_handle, peer_address, error_code): logger.debug(f'*** Connection failed: {HCI_Constant.error_name(error_code)}') # For directed advertising, this means a timeout - if self.advertising and self.advertising_type.is_directed: + if transport == BT_LE_TRANSPORT and self.advertising and self.advertising_type.is_directed: self.advertising = False # Notify listeners @@ -1717,6 +1731,9 @@ class Device(CompositeEventEmitter): 'hci', HCI_Constant.error_name(error_code) ) + error.transport = transport + error.connection_handle = connection_handle # FIXME: Connection handle sounds to be a dummy value here + error.peer_address = peer_address self.emit('connection_failure', error) @host_event_handler diff --git a/bumble/host.py b/bumble/host.py index 35efad4..276d334 100644 --- a/bumble/host.py +++ b/bumble/host.py @@ -383,7 +383,7 @@ class Host(EventEmitter): logger.debug(f'### CONNECTION FAILED: {event.status}') # Notify the listeners - self.emit('connection_failure', event.connection_handle, event.status) + self.emit('connection_failure', BT_LE_TRANSPORT, event.connection_handle, event.peer_address, event.status) def on_hci_le_enhanced_connection_complete_event(self, event): # Just use the same implementation as for the non-enhanced event for now @@ -413,7 +413,7 @@ class Host(EventEmitter): logger.debug(f'### BR/EDR CONNECTION FAILED: {event.status}') # Notify the client - self.emit('connection_failure', event.connection_handle, event.status) + self.emit('connection_failure', BT_BR_EDR_TRANSPORT, event.connection_handle, event.bd_addr, event.status) def on_hci_disconnection_complete_event(self, event): # Find the connection