Add connection parameter update from peripheral
diff --git a/bumble/core.py b/bumble/core.py
index b00c40e..4dff432 100644
--- a/bumble/core.py
+++ b/bumble/core.py
@@ -142,6 +142,10 @@
self.peer_address = peer_address
+class ConnectionParameterUpdateError(BaseError):
+ """Connection Parameter Update Error"""
+
+
# -----------------------------------------------------------------------------
# UUID
#
diff --git a/bumble/device.py b/bumble/device.py
index fca2e7a..9a784e7 100644
--- a/bumble/device.py
+++ b/bumble/device.py
@@ -141,6 +141,7 @@
BT_LE_TRANSPORT,
BT_PERIPHERAL_ROLE,
AdvertisingData,
+ ConnectionParameterUpdateError,
CommandTimeoutError,
ConnectionPHY,
InvalidStateError,
@@ -723,6 +724,7 @@
connection_interval_max,
max_latency,
supervision_timeout,
+ use_l2cap=False,
):
return await self.device.update_connection_parameters(
self,
@@ -730,6 +732,7 @@
connection_interval_max,
max_latency,
supervision_timeout,
+ use_l2cap=use_l2cap,
)
async def set_phy(self, tx_phys=None, rx_phys=None, phy_options=None):
@@ -2110,11 +2113,30 @@
supervision_timeout,
min_ce_length=0,
max_ce_length=0,
- ):
+ use_l2cap=False,
+ ) -> None:
'''
NOTE: the name of the parameters may look odd, but it just follows the names
used in the Bluetooth spec.
'''
+
+ if use_l2cap:
+ if connection.role != BT_PERIPHERAL_ROLE:
+ raise InvalidStateError(
+ 'only peripheral can update connection parameters with l2cap'
+ )
+ l2cap_result = (
+ await self.l2cap_channel_manager.update_connection_parameters(
+ connection,
+ connection_interval_min,
+ connection_interval_max,
+ max_latency,
+ supervision_timeout,
+ )
+ )
+ if l2cap_result != l2cap.L2CAP_CONNECTION_PARAMETERS_ACCEPTED_RESULT:
+ raise ConnectionParameterUpdateError(l2cap_result)
+
result = await self.send_command(
HCI_LE_Connection_Update_Command(
connection_handle=connection.handle,
@@ -2124,7 +2146,7 @@
supervision_timeout=supervision_timeout,
min_ce_length=min_ce_length,
max_ce_length=max_ce_length,
- )
+ ) # type: ignore[call-arg]
)
if result.status != HCI_Command_Status_Event.PENDING:
raise HCI_StatusError(result)
diff --git a/bumble/l2cap.py b/bumble/l2cap.py
index 270d909..fea8a1d 100644
--- a/bumble/l2cap.py
+++ b/bumble/l2cap.py
@@ -1387,6 +1387,7 @@
le_coc_requests: Dict[int, L2CAP_LE_Credit_Based_Connection_Request]
fixed_channels: Dict[int, Optional[Callable[[int, bytes], Any]]]
_host: Optional[Host]
+ connection_parameters_update_response: Optional[asyncio.Future[int]]
def __init__(
self,
@@ -1408,6 +1409,7 @@
self.le_coc_requests = {} # LE CoC connection requests, by identifier
self.extended_features = extended_features
self.connectionless_mtu = connectionless_mtu
+ self.connection_parameters_update_response = None
@property
def host(self) -> Host:
@@ -1865,11 +1867,45 @@
),
)
+ async def update_connection_parameters(
+ self,
+ connection: Connection,
+ interval_min: int,
+ interval_max: int,
+ latency: int,
+ timeout: int,
+ ) -> int:
+ # Check that there isn't already a request pending
+ if self.connection_parameters_update_response:
+ raise InvalidStateError('request already pending')
+ self.connection_parameters_update_response = (
+ asyncio.get_running_loop().create_future()
+ )
+ self.send_control_frame(
+ connection,
+ L2CAP_LE_SIGNALING_CID,
+ L2CAP_Connection_Parameter_Update_Request(
+ interval_min=interval_min,
+ interval_max=interval_max,
+ latency=latency,
+ timeout=timeout,
+ ),
+ )
+ return await self.connection_parameters_update_response
+
def on_l2cap_connection_parameter_update_response(
self, connection: Connection, cid: int, response
) -> None:
- # TODO: check response
- pass
+ if self.connection_parameters_update_response:
+ self.connection_parameters_update_response.set_result(response.result)
+ self.connection_parameters_update_response = None
+ else:
+ logger.warning(
+ color(
+ 'received l2cap_connection_parameter_update_response without a pending request',
+ 'red',
+ )
+ )
def on_l2cap_le_credit_based_connection_request(
self, connection: Connection, cid: int, request