From a1eff958e60859a748cbf926be6b6b40e92001c5 Mon Sep 17 00:00:00 2001 From: Gilles Boccon-Gibod Date: Sat, 2 Aug 2025 21:10:45 -0700 Subject: [PATCH 1/4] do not wait for display --- bumble/smp.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/bumble/smp.py b/bumble/smp.py index bf4b257..0edafa8 100644 --- a/bumble/smp.py +++ b/bumble/smp.py @@ -946,7 +946,9 @@ class Session: self.tk = self.passkey.to_bytes(16, byteorder='little') logger.debug(f'TK from passkey = {self.tk.hex()}') - await self.pairing_config.delegate.display_number(self.passkey, digits=6) + self.connection.cancel_on_disconnection( + self.pairing_config.delegate.display_number(self.passkey, digits=6) + ) def input_passkey(self, next_steps: Optional[Callable[[], None]] = None) -> None: # Prompt the user for the passkey displayed on the peer From c034297bc02c676cde3dd0984de3b88bdc85761d Mon Sep 17 00:00:00 2001 From: Gilles Boccon-Gibod Date: Sat, 2 Aug 2025 21:11:34 -0700 Subject: [PATCH 2/4] update to black formatter 25.1 --- .vscode/settings.json | 10 ++++++---- bumble/avrcp.py | 2 +- bumble/pandora/__init__.py | 2 +- pyproject.toml | 2 +- tests/hfp_test.py | 18 +++++++++--------- 5 files changed, 18 insertions(+), 16 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index c6696ab..7e81bc9 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -94,15 +94,17 @@ "ycursor" ], "[python]": { - "editor.rulers": [88] + "editor.rulers": [88], + "editor.defaultFormatter": "ms-python.black-formatter" }, - "python.formatting.provider": "black", - "pylint.importStrategy": "useBundled", "python.testing.pytestArgs": [ "." ], "python.testing.unittestEnabled": false, "python.testing.pytestEnabled": true, "python-envs.defaultEnvManager": "ms-python.python:system", - "python-envs.pythonProjects": [] + "python-envs.pythonProjects": [], + "python.terminal.launchArgs": [ + + ] } diff --git a/bumble/avrcp.py b/bumble/avrcp.py index b723215..c8a89ab 100644 --- a/bumble/avrcp.py +++ b/bumble/avrcp.py @@ -1117,7 +1117,7 @@ class Protocol(utils.EventEmitter): @staticmethod def _check_vendor_dependent_frame( - frame: Union[avc.VendorDependentCommandFrame, avc.VendorDependentResponseFrame] + frame: Union[avc.VendorDependentCommandFrame, avc.VendorDependentResponseFrame], ) -> bool: if frame.company_id != AVRCP_BLUETOOTH_SIG_COMPANY_ID: logger.debug("unsupported company id, ignoring") diff --git a/bumble/pandora/__init__.py b/bumble/pandora/__init__.py index f9fb335..5279f47 100644 --- a/bumble/pandora/__init__.py +++ b/bumble/pandora/__init__.py @@ -49,7 +49,7 @@ _SERVICERS_HOOKS: list[Callable[[PandoraDevice, Config, grpc.aio.Server], None]] def register_servicer_hook( - hook: Callable[[PandoraDevice, Config, grpc.aio.Server], None] + hook: Callable[[PandoraDevice, Config, grpc.aio.Server], None], ) -> None: _SERVICERS_HOOKS.append(hook) diff --git a/pyproject.toml b/pyproject.toml index c94fb71..1507e41 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -44,7 +44,7 @@ test = [ "coverage >= 6.4", ] development = [ - "black == 24.3", + "black ~= 25.1", "bt-test-interfaces >= 0.0.6", "grpcio-tools >= 1.62.1", "invoke >= 1.7.3", diff --git a/tests/hfp_test.py b/tests/hfp_test.py index f817916..f2d3515 100644 --- a/tests/hfp_test.py +++ b/tests/hfp_test.py @@ -237,7 +237,7 @@ async def test_hf_indicator(hfp_connections: tuple[hfp.HfProtocol, hfp.AgProtoco # ----------------------------------------------------------------------------- @pytest.mark.asyncio async def test_codec_negotiation( - hfp_connections: tuple[hfp.HfProtocol, hfp.AgProtocol] + hfp_connections: tuple[hfp.HfProtocol, hfp.AgProtocol], ): hf, ag = hfp_connections @@ -281,7 +281,7 @@ async def test_answer(hfp_connections: tuple[hfp.HfProtocol, hfp.AgProtocol]): # ----------------------------------------------------------------------------- @pytest.mark.asyncio async def test_reject_incoming_call( - hfp_connections: tuple[hfp.HfProtocol, hfp.AgProtocol] + hfp_connections: tuple[hfp.HfProtocol, hfp.AgProtocol], ): hf, ag = hfp_connections @@ -307,7 +307,7 @@ async def test_terminate_call(hfp_connections: tuple[hfp.HfProtocol, hfp.AgProto # ----------------------------------------------------------------------------- @pytest.mark.asyncio async def test_query_calls_without_calls( - hfp_connections: tuple[hfp.HfProtocol, hfp.AgProtocol] + hfp_connections: tuple[hfp.HfProtocol, hfp.AgProtocol], ): hf, ag = hfp_connections @@ -317,7 +317,7 @@ async def test_query_calls_without_calls( # ----------------------------------------------------------------------------- @pytest.mark.asyncio async def test_query_calls_with_calls( - hfp_connections: tuple[hfp.HfProtocol, hfp.AgProtocol] + hfp_connections: tuple[hfp.HfProtocol, hfp.AgProtocol], ): hf, ag = hfp_connections ag.calls.append( @@ -418,7 +418,7 @@ async def test_speaker_volume(hfp_connections: tuple[hfp.HfProtocol, hfp.AgProto # ----------------------------------------------------------------------------- @pytest.mark.asyncio async def test_microphone_volume( - hfp_connections: tuple[hfp.HfProtocol, hfp.AgProtocol] + hfp_connections: tuple[hfp.HfProtocol, hfp.AgProtocol], ): hf, ag = hfp_connections microphone_volume_future = asyncio.get_running_loop().create_future() @@ -448,7 +448,7 @@ async def test_cli_notification(hfp_connections: tuple[hfp.HfProtocol, hfp.AgPro # ----------------------------------------------------------------------------- @pytest.mark.asyncio async def test_voice_recognition_from_hf( - hfp_connections: tuple[hfp.HfProtocol, hfp.AgProtocol] + hfp_connections: tuple[hfp.HfProtocol, hfp.AgProtocol], ): hf, ag = hfp_connections voice_recognition_future = asyncio.get_running_loop().create_future() @@ -462,7 +462,7 @@ async def test_voice_recognition_from_hf( # ----------------------------------------------------------------------------- @pytest.mark.asyncio async def test_voice_recognition_from_ag( - hfp_connections: tuple[hfp.HfProtocol, hfp.AgProtocol] + hfp_connections: tuple[hfp.HfProtocol, hfp.AgProtocol], ): hf, ag = hfp_connections voice_recognition_future = asyncio.get_running_loop().create_future() @@ -572,7 +572,7 @@ async def test_sco_setup(): # ----------------------------------------------------------------------------- @pytest.mark.asyncio async def test_hf_batched_response( - hfp_connections: tuple[hfp.HfProtocol, hfp.AgProtocol] + hfp_connections: tuple[hfp.HfProtocol, hfp.AgProtocol], ): hf, ag = hfp_connections @@ -584,7 +584,7 @@ async def test_hf_batched_response( # ----------------------------------------------------------------------------- @pytest.mark.asyncio async def test_ag_batched_commands( - hfp_connections: tuple[hfp.HfProtocol, hfp.AgProtocol] + hfp_connections: tuple[hfp.HfProtocol, hfp.AgProtocol], ): hf, ag = hfp_connections From a7111d0107013bac91891d9089de0b5495206bf9 Mon Sep 17 00:00:00 2001 From: Gilles Boccon-Gibod Date: Mon, 4 Aug 2025 19:18:12 -0700 Subject: [PATCH 3/4] send public keys earlier --- bumble/smp.py | 11 ++++++----- tests/self_test.py | 4 +++- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/bumble/smp.py b/bumble/smp.py index 0edafa8..f22f320 100644 --- a/bumble/smp.py +++ b/bumble/smp.py @@ -1571,11 +1571,12 @@ class Session: if self.pairing_method == PairingMethod.CTKD_OVER_CLASSIC: # Authentication is already done in SMP, so remote shall start keys distribution immediately return - elif self.sc: + + if self.sc: + self.send_public_key_command() + if self.pairing_method == PairingMethod.PASSKEY: self.display_or_input_passkey() - - self.send_public_key_command() else: if self.pairing_method == PairingMethod.PASSKEY: self.display_or_input_passkey(self.send_pairing_confirm_command) @@ -1848,10 +1849,10 @@ class Session: elif self.pairing_method == PairingMethod.PASSKEY: self.send_pairing_confirm_command() else: + # Send our public key back to the initiator + self.send_public_key_command() def next_steps() -> None: - # Send our public key back to the initiator - self.send_public_key_command() if self.pairing_method in ( PairingMethod.JUST_WORKS, diff --git a/tests/self_test.py b/tests/self_test.py index 9098299..4467299 100644 --- a/tests/self_test.py +++ b/tests/self_test.py @@ -322,7 +322,9 @@ async def test_self_smp(io_caps, sc, mitm, key_dist): return 0 else: if ( - self.peer_delegate.io_capability + self.io_capability + == PairingDelegate.IoCapability.KEYBOARD_INPUT_ONLY + and self.peer_delegate.io_capability == PairingDelegate.IoCapability.KEYBOARD_INPUT_ONLY ): peer_number = 6789 From 12ca1c01f0e8e0f17857f6149fbc44ebeccf9a8f Mon Sep 17 00:00:00 2001 From: Gilles Boccon-Gibod Date: Mon, 4 Aug 2025 19:24:30 -0700 Subject: [PATCH 4/4] Revert "update to black formatter 25.1" This reverts commit c034297bc02c676cde3dd0984de3b88bdc85761d. --- .vscode/settings.json | 10 ++++------ bumble/avrcp.py | 2 +- bumble/pandora/__init__.py | 2 +- pyproject.toml | 2 +- tests/hfp_test.py | 18 +++++++++--------- 5 files changed, 16 insertions(+), 18 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index 7e81bc9..c6696ab 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -94,17 +94,15 @@ "ycursor" ], "[python]": { - "editor.rulers": [88], - "editor.defaultFormatter": "ms-python.black-formatter" + "editor.rulers": [88] }, + "python.formatting.provider": "black", + "pylint.importStrategy": "useBundled", "python.testing.pytestArgs": [ "." ], "python.testing.unittestEnabled": false, "python.testing.pytestEnabled": true, "python-envs.defaultEnvManager": "ms-python.python:system", - "python-envs.pythonProjects": [], - "python.terminal.launchArgs": [ - - ] + "python-envs.pythonProjects": [] } diff --git a/bumble/avrcp.py b/bumble/avrcp.py index c8a89ab..b723215 100644 --- a/bumble/avrcp.py +++ b/bumble/avrcp.py @@ -1117,7 +1117,7 @@ class Protocol(utils.EventEmitter): @staticmethod def _check_vendor_dependent_frame( - frame: Union[avc.VendorDependentCommandFrame, avc.VendorDependentResponseFrame], + frame: Union[avc.VendorDependentCommandFrame, avc.VendorDependentResponseFrame] ) -> bool: if frame.company_id != AVRCP_BLUETOOTH_SIG_COMPANY_ID: logger.debug("unsupported company id, ignoring") diff --git a/bumble/pandora/__init__.py b/bumble/pandora/__init__.py index 5279f47..f9fb335 100644 --- a/bumble/pandora/__init__.py +++ b/bumble/pandora/__init__.py @@ -49,7 +49,7 @@ _SERVICERS_HOOKS: list[Callable[[PandoraDevice, Config, grpc.aio.Server], None]] def register_servicer_hook( - hook: Callable[[PandoraDevice, Config, grpc.aio.Server], None], + hook: Callable[[PandoraDevice, Config, grpc.aio.Server], None] ) -> None: _SERVICERS_HOOKS.append(hook) diff --git a/pyproject.toml b/pyproject.toml index 1507e41..c94fb71 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -44,7 +44,7 @@ test = [ "coverage >= 6.4", ] development = [ - "black ~= 25.1", + "black == 24.3", "bt-test-interfaces >= 0.0.6", "grpcio-tools >= 1.62.1", "invoke >= 1.7.3", diff --git a/tests/hfp_test.py b/tests/hfp_test.py index f2d3515..f817916 100644 --- a/tests/hfp_test.py +++ b/tests/hfp_test.py @@ -237,7 +237,7 @@ async def test_hf_indicator(hfp_connections: tuple[hfp.HfProtocol, hfp.AgProtoco # ----------------------------------------------------------------------------- @pytest.mark.asyncio async def test_codec_negotiation( - hfp_connections: tuple[hfp.HfProtocol, hfp.AgProtocol], + hfp_connections: tuple[hfp.HfProtocol, hfp.AgProtocol] ): hf, ag = hfp_connections @@ -281,7 +281,7 @@ async def test_answer(hfp_connections: tuple[hfp.HfProtocol, hfp.AgProtocol]): # ----------------------------------------------------------------------------- @pytest.mark.asyncio async def test_reject_incoming_call( - hfp_connections: tuple[hfp.HfProtocol, hfp.AgProtocol], + hfp_connections: tuple[hfp.HfProtocol, hfp.AgProtocol] ): hf, ag = hfp_connections @@ -307,7 +307,7 @@ async def test_terminate_call(hfp_connections: tuple[hfp.HfProtocol, hfp.AgProto # ----------------------------------------------------------------------------- @pytest.mark.asyncio async def test_query_calls_without_calls( - hfp_connections: tuple[hfp.HfProtocol, hfp.AgProtocol], + hfp_connections: tuple[hfp.HfProtocol, hfp.AgProtocol] ): hf, ag = hfp_connections @@ -317,7 +317,7 @@ async def test_query_calls_without_calls( # ----------------------------------------------------------------------------- @pytest.mark.asyncio async def test_query_calls_with_calls( - hfp_connections: tuple[hfp.HfProtocol, hfp.AgProtocol], + hfp_connections: tuple[hfp.HfProtocol, hfp.AgProtocol] ): hf, ag = hfp_connections ag.calls.append( @@ -418,7 +418,7 @@ async def test_speaker_volume(hfp_connections: tuple[hfp.HfProtocol, hfp.AgProto # ----------------------------------------------------------------------------- @pytest.mark.asyncio async def test_microphone_volume( - hfp_connections: tuple[hfp.HfProtocol, hfp.AgProtocol], + hfp_connections: tuple[hfp.HfProtocol, hfp.AgProtocol] ): hf, ag = hfp_connections microphone_volume_future = asyncio.get_running_loop().create_future() @@ -448,7 +448,7 @@ async def test_cli_notification(hfp_connections: tuple[hfp.HfProtocol, hfp.AgPro # ----------------------------------------------------------------------------- @pytest.mark.asyncio async def test_voice_recognition_from_hf( - hfp_connections: tuple[hfp.HfProtocol, hfp.AgProtocol], + hfp_connections: tuple[hfp.HfProtocol, hfp.AgProtocol] ): hf, ag = hfp_connections voice_recognition_future = asyncio.get_running_loop().create_future() @@ -462,7 +462,7 @@ async def test_voice_recognition_from_hf( # ----------------------------------------------------------------------------- @pytest.mark.asyncio async def test_voice_recognition_from_ag( - hfp_connections: tuple[hfp.HfProtocol, hfp.AgProtocol], + hfp_connections: tuple[hfp.HfProtocol, hfp.AgProtocol] ): hf, ag = hfp_connections voice_recognition_future = asyncio.get_running_loop().create_future() @@ -572,7 +572,7 @@ async def test_sco_setup(): # ----------------------------------------------------------------------------- @pytest.mark.asyncio async def test_hf_batched_response( - hfp_connections: tuple[hfp.HfProtocol, hfp.AgProtocol], + hfp_connections: tuple[hfp.HfProtocol, hfp.AgProtocol] ): hf, ag = hfp_connections @@ -584,7 +584,7 @@ async def test_hf_batched_response( # ----------------------------------------------------------------------------- @pytest.mark.asyncio async def test_ag_batched_commands( - hfp_connections: tuple[hfp.HfProtocol, hfp.AgProtocol], + hfp_connections: tuple[hfp.HfProtocol, hfp.AgProtocol] ): hf, ag = hfp_connections