mirror of
https://github.com/home-assistant/core.git
synced 2026-09-26 09:23:17 -04:00
Fix SwitchBot encrypted device method selection not resetting on back (#167749)
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
This commit is contained in:
co-authored by
Claude Sonnet 4.6
Martin Hjelmare
parent
998f24649d
commit
872120821c
@@ -96,6 +96,7 @@ class SwitchbotConfigFlow(ConfigFlow, domain=DOMAIN):
|
||||
self._discovered_advs: dict[str, SwitchBotAdvertisement] = {}
|
||||
self._cloud_username: str | None = None
|
||||
self._cloud_password: str | None = None
|
||||
self._encryption_method_selected = False
|
||||
|
||||
async def async_step_bluetooth(
|
||||
self, discovery_info: BluetoothServiceInfoBleak
|
||||
@@ -197,6 +198,13 @@ class SwitchbotConfigFlow(ConfigFlow, domain=DOMAIN):
|
||||
assert self._discovered_adv is not None
|
||||
description_placeholders: dict[str, str] = {}
|
||||
|
||||
if user_input is None:
|
||||
if not self._encryption_method_selected and not (
|
||||
self._cloud_username and self._cloud_password
|
||||
):
|
||||
return await self.async_step_encrypted_choose_method()
|
||||
self._encryption_method_selected = False
|
||||
|
||||
# If we have saved credentials from cloud login, try them first
|
||||
if user_input is None and self._cloud_username and self._cloud_password:
|
||||
user_input = {
|
||||
@@ -258,6 +266,7 @@ class SwitchbotConfigFlow(ConfigFlow, domain=DOMAIN):
|
||||
"""Handle the SwitchBot API chose method step."""
|
||||
assert self._discovered_adv is not None
|
||||
|
||||
self._encryption_method_selected = True
|
||||
return self.async_show_menu(
|
||||
step_id="encrypted_choose_method",
|
||||
menu_options=["encrypted_auth", "encrypted_key"],
|
||||
@@ -272,6 +281,12 @@ class SwitchbotConfigFlow(ConfigFlow, domain=DOMAIN):
|
||||
"""Handle the encryption key step."""
|
||||
errors: dict[str, str] = {}
|
||||
assert self._discovered_adv is not None
|
||||
|
||||
if user_input is None:
|
||||
if not self._encryption_method_selected:
|
||||
return await self.async_step_encrypted_choose_method()
|
||||
self._encryption_method_selected = False
|
||||
|
||||
if user_input is not None:
|
||||
model: SwitchbotModel = self._discovered_adv.data["modelName"]
|
||||
cls = ENCRYPTED_SWITCHBOT_MODEL_TO_CLASS[model]
|
||||
|
||||
@@ -221,6 +221,113 @@ async def test_bluetooth_discovery_key(hass: HomeAssistant) -> None:
|
||||
assert len(mock_setup_entry.mock_calls) == 1
|
||||
|
||||
|
||||
async def test_bluetooth_discovery_encrypted_key_back_navigation(
|
||||
hass: HomeAssistant,
|
||||
) -> None:
|
||||
"""Test that resuming an abandoned encrypted_key flow resets to the method menu."""
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN,
|
||||
context={"source": SOURCE_BLUETOOTH},
|
||||
data=WOLOCK_SERVICE_INFO,
|
||||
)
|
||||
assert result["type"] is FlowResultType.MENU
|
||||
assert result["step_id"] == "encrypted_choose_method"
|
||||
|
||||
# User selects encrypted_key
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"], user_input={"next_step_id": "encrypted_key"}
|
||||
)
|
||||
assert result["type"] is FlowResultType.FORM
|
||||
assert result["step_id"] == "encrypted_key"
|
||||
|
||||
# Simulate user closing dialog and re-opening: call the step with no input
|
||||
# (as HA does when resuming an in-progress flow)
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"], user_input=None
|
||||
)
|
||||
assert result["type"] is FlowResultType.MENU
|
||||
assert result["step_id"] == "encrypted_choose_method"
|
||||
|
||||
# User can now pick a method again and complete the flow
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"], user_input={"next_step_id": "encrypted_key"}
|
||||
)
|
||||
assert result["type"] is FlowResultType.FORM
|
||||
assert result["step_id"] == "encrypted_key"
|
||||
|
||||
with (
|
||||
patch_async_setup_entry() as mock_setup_entry,
|
||||
patch(
|
||||
"switchbot.SwitchbotLock.verify_encryption_key",
|
||||
return_value=True,
|
||||
),
|
||||
):
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
{
|
||||
CONF_KEY_ID: "ff",
|
||||
CONF_ENCRYPTION_KEY: "ffffffffffffffffffffffffffffffff",
|
||||
},
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert result["type"] is FlowResultType.CREATE_ENTRY
|
||||
assert len(mock_setup_entry.mock_calls) == 1
|
||||
|
||||
|
||||
async def test_bluetooth_discovery_encrypted_auth_back_navigation(
|
||||
hass: HomeAssistant,
|
||||
) -> None:
|
||||
"""Test that resuming an abandoned encrypted_auth flow resets to the method menu."""
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN,
|
||||
context={"source": SOURCE_BLUETOOTH},
|
||||
data=WOLOCK_SERVICE_INFO,
|
||||
)
|
||||
assert result["type"] is FlowResultType.MENU
|
||||
assert result["step_id"] == "encrypted_choose_method"
|
||||
|
||||
# User selects encrypted_auth
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"], user_input={"next_step_id": "encrypted_auth"}
|
||||
)
|
||||
assert result["type"] is FlowResultType.FORM
|
||||
assert result["step_id"] == "encrypted_auth"
|
||||
|
||||
# Simulate user closing dialog and re-opening: call the step with no input
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"], user_input=None
|
||||
)
|
||||
assert result["type"] is FlowResultType.MENU
|
||||
assert result["step_id"] == "encrypted_choose_method"
|
||||
|
||||
# User can switch to encrypted_key and complete the flow
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"], user_input={"next_step_id": "encrypted_key"}
|
||||
)
|
||||
assert result["type"] is FlowResultType.FORM
|
||||
assert result["step_id"] == "encrypted_key"
|
||||
|
||||
with (
|
||||
patch_async_setup_entry() as mock_setup_entry,
|
||||
patch(
|
||||
"switchbot.SwitchbotLock.verify_encryption_key",
|
||||
return_value=True,
|
||||
),
|
||||
):
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
{
|
||||
CONF_KEY_ID: "ff",
|
||||
CONF_ENCRYPTION_KEY: "ffffffffffffffffffffffffffffffff",
|
||||
},
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert result["type"] is FlowResultType.CREATE_ENTRY
|
||||
assert len(mock_setup_entry.mock_calls) == 1
|
||||
|
||||
|
||||
async def test_bluetooth_discovery_already_setup(hass: HomeAssistant) -> None:
|
||||
"""Test discovery via bluetooth with a valid device when already setup."""
|
||||
entry = MockConfigEntry(
|
||||
@@ -1208,12 +1315,22 @@ async def test_user_cloud_login_then_encrypted_device(hass: HomeAssistant) -> No
|
||||
assert result["type"] is FlowResultType.FORM
|
||||
assert result["step_id"] == "encrypted_auth"
|
||||
|
||||
# Simulate the user navigating away and re-opening the dialog.
|
||||
# The failed auto-auth cleared credentials, so calling with None now
|
||||
# redirects back to the method selection menu.
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
None,
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert result["type"] is FlowResultType.MENU
|
||||
assert result["step_id"] == "encrypted_choose_method"
|
||||
|
||||
# User selects encrypted_auth again and manually enters credentials
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"], user_input={"next_step_id": "encrypted_auth"}
|
||||
)
|
||||
assert result["type"] is FlowResultType.FORM
|
||||
assert result["step_id"] == "encrypted_auth"
|
||||
|
||||
|
||||
Reference in New Issue
Block a user