From 531b9f7d268bcda8492052a1a172a15aba50aa6f Mon Sep 17 00:00:00 2001 From: Bluetooth Devices Bot Date: Fri, 15 May 2026 16:53:24 +0000 Subject: [PATCH 1/3] fix: track lock notification state in _enable_notifications _enable_notifications never set self._notifications_enabled = True, so the flag stayed False forever. Two consequences: - _disable_notifications short-circuits on `if not self._notifications_enabled`, meaning the disable command would never actually fire on the lock. - Every lock/unlock invocation re-sent the enable command instead of recognising notifications were already on. Set the flag on a successful response, return False on failure, and no-op when already enabled. Existing test only checked the return value; expand it to assert state plus a no-op second call, and add a failure-path test that the flag stays False if the lock rejects the command. Co-Authored-By: Claude Opus 4.7 (1M context) --- switchbot/devices/lock.py | 7 ++++++- tests/test_lock.py | 33 ++++++++++++++++++++++++++++++++- 2 files changed, 38 insertions(+), 2 deletions(-) diff --git a/switchbot/devices/lock.py b/switchbot/devices/lock.py index 275e6280..473b7626 100644 --- a/switchbot/devices/lock.py +++ b/switchbot/devices/lock.py @@ -236,8 +236,13 @@ async def _get_lock_info(self) -> bytes | None: return _data async def _enable_notifications(self) -> bool: + if self._notifications_enabled: + return True result = await self._send_command(COMMAND_ENABLE_NOTIFICATIONS[self._model]) - return self._check_command_result(result, 0, COMMAND_RESULT_EXPECTED_VALUES) + if self._check_command_result(result, 0, COMMAND_RESULT_EXPECTED_VALUES): + self._notifications_enabled = True + return True + return False async def _disable_notifications(self) -> bool: if not self._notifications_enabled: diff --git a/tests/test_lock.py b/tests/test_lock.py index d47a1e99..e5924a0b 100644 --- a/tests/test_lock.py +++ b/tests/test_lock.py @@ -408,9 +408,40 @@ async def test_get_lock_info_failure(model: str): async def test_enable_notifications(model: str): """Test _enable_notifications method.""" device = create_device_for_command_testing(model) - with patch.object(device, "_send_command", return_value=b"\x01\x00"): + assert device._notifications_enabled is False + with patch.object( + device, "_send_command", return_value=b"\x01\x00" + ) as mock_send: + result = await device._enable_notifications() + assert result is True + assert device._notifications_enabled is True + assert mock_send.call_count == 1 + # Second call must be a no-op once notifications are tracked as enabled. result = await device._enable_notifications() assert result is True + assert mock_send.call_count == 1 + + +@pytest.mark.asyncio +@pytest.mark.parametrize( + "model", + [ + SwitchbotModel.LOCK, + SwitchbotModel.LOCK_LITE, + SwitchbotModel.LOCK_PRO, + SwitchbotModel.LOCK_ULTRA, + SwitchbotModel.LOCK_VISION, + SwitchbotModel.LOCK_VISION_PRO, + SwitchbotModel.LOCK_PRO_WIFI, + ], +) +async def test_enable_notifications_failure_keeps_state_false(model: str): + """Failed enable command must not flip the tracked state to True.""" + device = create_device_for_command_testing(model) + with patch.object(device, "_send_command", return_value=b"\x00\x00"): + result = await device._enable_notifications() + assert result is False + assert device._notifications_enabled is False @pytest.mark.asyncio From c4cfc80d74fe362d58a429de1afe400d70b5909e Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Fri, 15 May 2026 16:54:01 +0000 Subject: [PATCH 2/3] chore(pre-commit.ci): auto fixes --- tests/test_lock.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/tests/test_lock.py b/tests/test_lock.py index e5924a0b..9674a690 100644 --- a/tests/test_lock.py +++ b/tests/test_lock.py @@ -409,9 +409,7 @@ async def test_enable_notifications(model: str): """Test _enable_notifications method.""" device = create_device_for_command_testing(model) assert device._notifications_enabled is False - with patch.object( - device, "_send_command", return_value=b"\x01\x00" - ) as mock_send: + with patch.object(device, "_send_command", return_value=b"\x01\x00") as mock_send: result = await device._enable_notifications() assert result is True assert device._notifications_enabled is True From 4bfd30677e50151850c0e8adc2434003c246d18f Mon Sep 17 00:00:00 2001 From: Bluetooth Devices Bot Date: Sat, 20 Jun 2026 14:52:00 +0000 Subject: [PATCH 3/3] fix(lock): re-send enable notifications each call, track state --- switchbot/devices/lock.py | 2 -- tests/test_lock.py | 4 ---- 2 files changed, 6 deletions(-) diff --git a/switchbot/devices/lock.py b/switchbot/devices/lock.py index 473b7626..5564c742 100644 --- a/switchbot/devices/lock.py +++ b/switchbot/devices/lock.py @@ -236,8 +236,6 @@ async def _get_lock_info(self) -> bytes | None: return _data async def _enable_notifications(self) -> bool: - if self._notifications_enabled: - return True result = await self._send_command(COMMAND_ENABLE_NOTIFICATIONS[self._model]) if self._check_command_result(result, 0, COMMAND_RESULT_EXPECTED_VALUES): self._notifications_enabled = True diff --git a/tests/test_lock.py b/tests/test_lock.py index 9674a690..93441ef7 100644 --- a/tests/test_lock.py +++ b/tests/test_lock.py @@ -414,10 +414,6 @@ async def test_enable_notifications(model: str): assert result is True assert device._notifications_enabled is True assert mock_send.call_count == 1 - # Second call must be a no-op once notifications are tracked as enabled. - result = await device._enable_notifications() - assert result is True - assert mock_send.call_count == 1 @pytest.mark.asyncio