Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 37 additions & 0 deletions homeassistant/components/vistapool/config_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@
}
)

RECONFIGURE_SCHEMA = vol.Schema({vol.Required(CONF_PASSWORD): cv.string})


class VistapoolConfigFlow(ConfigFlow, domain=DOMAIN):
"""Vistapool config flow (one entry per Hayward account)."""
Expand Down Expand Up @@ -74,3 +76,38 @@ async def async_step_user(
return self.async_show_form(
step_id="user", data_schema=AUTH_SCHEMA, errors=errors
)

async def async_step_reconfigure(
self, user_input: dict[str, Any] | None = None
) -> ConfigFlowResult:
"""Let the user proactively update the stored Vistapool password."""
errors: dict[str, str] = {}
entry = self._get_reconfigure_entry()
username = entry.data[CONF_USERNAME]

if user_input is not None:
password = user_input[CONF_PASSWORD]
session = async_get_clientsession(self.hass)
auth = AquariteAuth(session, username, password)
try:
await auth.authenticate()
except AuthenticationError:
errors["base"] = "invalid_auth"
except AquariteError:
errors["base"] = "cannot_connect"
except Exception:
_LOGGER.exception("Unexpected error during reconfiguration")
errors["base"] = "unknown"
else:
await self.async_set_unique_id(auth.user_id)
self._abort_if_unique_id_mismatch(reason="account_mismatch")
return self.async_update_reload_and_abort(
entry, data_updates={CONF_PASSWORD: password}
)

return self.async_show_form(
step_id="reconfigure",
data_schema=RECONFIGURE_SCHEMA,
description_placeholders={"username": username},
errors=errors,
)
2 changes: 1 addition & 1 deletion homeassistant/components/vistapool/quality_scale.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ rules:
entity-translations: done
exception-translations: done
icon-translations: done
reconfiguration-flow: todo
reconfiguration-flow: done
repair-issues:
status: exempt
comment: No known repair scenarios
Expand Down
14 changes: 13 additions & 1 deletion homeassistant/components/vistapool/strings.json
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
{
"config": {
"abort": {
"account_mismatch": "The credentials entered are for a different Vistapool account than the one being reconfigured.",
"already_configured": "[%key:common::config_flow::abort::already_configured_account%]",
"already_in_progress": "[%key:common::config_flow::abort::already_in_progress%]"
"already_in_progress": "[%key:common::config_flow::abort::already_in_progress%]",
"reconfigure_successful": "[%key:common::config_flow::abort::reconfigure_successful%]"
},
"error": {
"cannot_connect": "[%key:common::config_flow::error::cannot_connect%]",
Expand All @@ -12,6 +14,16 @@
},
"flow_title": "Vistapool pool controller",
"step": {
"reconfigure": {
"data": {
"password": "[%key:common::config_flow::data::password%]"
},
"data_description": {
"password": "The new password for your Vistapool account."
},
"description": "Update the stored password for {username}.",
"title": "Reconfigure Vistapool"
},
"user": {
"data": {
"password": "Password",
Expand Down
77 changes: 77 additions & 0 deletions tests/components/vistapool/test_config_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -303,3 +303,80 @@ async def test_dhcp_discovery_aborts_when_in_progress(

assert second["type"] is FlowResultType.ABORT
assert second["reason"] == "already_in_progress"


_NEW_PASSWORD = "new-password"


async def test_reconfigure_flow(
hass: HomeAssistant,
mock_config_entry: MockConfigEntry,
mock_setup_entry: AsyncMock,
mock_vistapool_client: AsyncMock,
) -> None:
"""Test the reconfigure flow updates the stored password and reloads the entry."""
Comment thread
fdebrus marked this conversation as resolved.
mock_config_entry.add_to_hass(hass)

result = await mock_config_entry.start_reconfigure_flow(hass)
assert result["type"] is FlowResultType.FORM
assert result["step_id"] == "reconfigure"

result = await hass.config_entries.flow.async_configure(
result["flow_id"], {CONF_PASSWORD: _NEW_PASSWORD}
)

assert result["type"] is FlowResultType.ABORT
assert result["reason"] == "reconfigure_successful"
assert mock_config_entry.data[CONF_PASSWORD] == _NEW_PASSWORD
assert mock_setup_entry.call_count == 1
Comment on lines +324 to +331


async def test_reconfigure_invalid_auth(
hass: HomeAssistant,
mock_config_entry: MockConfigEntry,
mock_setup_entry: AsyncMock,
mock_vistapool_client: AsyncMock,
mock_vistapool_auth: MagicMock,
) -> None:
"""Test the reconfigure flow surfaces invalid_auth and recovers on retry."""
mock_config_entry.add_to_hass(hass)
mock_vistapool_auth.authenticate.side_effect = AuthenticationError
Comment thread
fdebrus marked this conversation as resolved.

result = await mock_config_entry.start_reconfigure_flow(hass)
result = await hass.config_entries.flow.async_configure(
result["flow_id"], {CONF_PASSWORD: _NEW_PASSWORD}
)

assert result["type"] is FlowResultType.FORM
assert result["errors"] == {"base": "invalid_auth"}

mock_vistapool_auth.authenticate.side_effect = None
result = await hass.config_entries.flow.async_configure(
result["flow_id"], {CONF_PASSWORD: _NEW_PASSWORD}
)

assert result["type"] is FlowResultType.ABORT
assert result["reason"] == "reconfigure_successful"
assert mock_config_entry.data[CONF_PASSWORD] == _NEW_PASSWORD
assert mock_setup_entry.call_count == 1
Comment on lines +358 to +361


async def test_reconfigure_account_mismatch(
hass: HomeAssistant,
mock_config_entry: MockConfigEntry,
mock_setup_entry: AsyncMock,
mock_vistapool_client: AsyncMock,
mock_vistapool_auth: MagicMock,
) -> None:
"""Test the reconfigure flow aborts when credentials belong to a different account."""
mock_config_entry.add_to_hass(hass)
mock_vistapool_auth.user_id = "a-different-firebase-uid"

result = await mock_config_entry.start_reconfigure_flow(hass)
result = await hass.config_entries.flow.async_configure(
result["flow_id"], {CONF_PASSWORD: _NEW_PASSWORD}
)

assert result["type"] is FlowResultType.ABORT
assert result["reason"] == "account_mismatch"
assert mock_setup_entry.call_count == 0
Comment on lines +380 to +382
Loading