mirror of
https://github.com/home-assistant/core.git
synced 2026-09-26 09:23:17 -04:00
Offer retry when Teslemetry Powerwall approval window expires (#181334)
This commit is contained in:
@@ -451,6 +451,7 @@ class EnergySiteSubentryFlowHandler(ConfigSubentryFlow):
|
||||
self._discovered_host: str = ""
|
||||
self._site_id: int | None = None
|
||||
self._site_name: str = ""
|
||||
self._approval_expired: bool = False
|
||||
|
||||
async def async_step_user(
|
||||
self, user_input: dict[str, Any] | None = None
|
||||
@@ -603,6 +604,11 @@ class EnergySiteSubentryFlowHandler(ConfigSubentryFlow):
|
||||
if user_input is None:
|
||||
return self.async_show_form(step_id="pair")
|
||||
|
||||
if self._approval_expired:
|
||||
# The user saw the expired-window notice and submitted to try again.
|
||||
self._approval_expired = False
|
||||
return await self._async_begin_pairing()
|
||||
|
||||
try:
|
||||
client = await self._find_authorized_client()
|
||||
except PowerwallLookupError:
|
||||
@@ -618,6 +624,10 @@ class EnergySiteSubentryFlowHandler(ConfigSubentryFlow):
|
||||
return await self.async_step_credentials()
|
||||
if client.state == AuthorizedClientState.PENDING_VERIFICATION:
|
||||
return self.async_show_form(step_id="pair", errors={"base": "key_pending"})
|
||||
if client.state == AuthorizedClientState.PENDING_VERIFICATION_TIMEOUT:
|
||||
# Surface the expiry; the user's next submit reopens the window.
|
||||
self._approval_expired = True
|
||||
return self.async_show_form(step_id="pair", errors={"base": "key_expired"})
|
||||
# An unrecognized state reported as pending would trap the user forever.
|
||||
LOGGER.debug("Unrecognized authorized-client state: %s", client.state)
|
||||
return self.async_show_form(step_id="pair", errors={"base": "cannot_connect"})
|
||||
|
||||
@@ -58,6 +58,7 @@
|
||||
"error": {
|
||||
"cannot_connect": "[%key:common::config_flow::error::cannot_connect%]",
|
||||
"invalid_password": "[%key:common::config_flow::error::invalid_auth%]",
|
||||
"key_expired": "The approval window on your Powerwall closed before the key was approved. Submit to open a new window, flick the On/Off switch on your primary Powerwall off and back on to approve it, then submit again.",
|
||||
"key_not_approved": "Your Powerwall system rejected Home Assistant's key because it has not been approved. Flick the On/Off switch on your primary Powerwall off and back on to approve it, then submit again.",
|
||||
"key_not_registered": "Home Assistant's key is no longer registered on your Powerwall system. Restart setup to register it again.",
|
||||
"key_pending": "Home Assistant's key has not been approved yet. Flick the On/Off switch on your primary Powerwall off and back on to approve it, then submit again."
|
||||
|
||||
@@ -2195,6 +2195,11 @@ async def test_add_authorized_client_failure_aborts(hass: HomeAssistant) -> None
|
||||
[
|
||||
pytest.param(InvalidResponse(), "cannot_connect", id="lookup_failure"),
|
||||
pytest.param(_empty_clients(), "key_not_registered", id="key_not_registered"),
|
||||
pytest.param(
|
||||
_own_key_clients(AuthorizedClientState.PENDING_VERIFICATION_TIMEOUT),
|
||||
"key_expired",
|
||||
id="key_expired",
|
||||
),
|
||||
pytest.param(_own_key_clients("gremlin"), "cannot_connect", id="unknown_state"),
|
||||
],
|
||||
)
|
||||
@@ -2228,6 +2233,69 @@ async def test_pair_step_second_lookup_errors(
|
||||
assert result["errors"] == {"base": expected_error}
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("mock_rsa_key")
|
||||
async def test_pair_step_timeout_retry_reopens_window_and_succeeds(
|
||||
hass: HomeAssistant,
|
||||
) -> None:
|
||||
"""Submitting the expired form reopens the window and pairing can complete."""
|
||||
entry = await _setup_account_no_subentry(hass)
|
||||
|
||||
client = _mock_powerwall_client()
|
||||
with (
|
||||
patch(
|
||||
"tesla_fleet_api.teslemetry.energysite.TeslemetryEnergySite.find_authorized_clients",
|
||||
new=AsyncMock(
|
||||
side_effect=[
|
||||
_empty_clients(),
|
||||
_own_key_clients(
|
||||
AuthorizedClientState.PENDING_VERIFICATION_TIMEOUT
|
||||
),
|
||||
_own_key_clients(
|
||||
AuthorizedClientState.PENDING_VERIFICATION_TIMEOUT
|
||||
),
|
||||
_own_key_clients(AuthorizedClientState.VERIFIED),
|
||||
]
|
||||
),
|
||||
),
|
||||
patch(
|
||||
"tesla_fleet_api.teslemetry.energysite.TeslemetryEnergySite.add_authorized_client",
|
||||
new=AsyncMock(),
|
||||
) as mock_add,
|
||||
patch(
|
||||
"homeassistant.components.teslemetry.config_flow.PowerwallClient",
|
||||
return_value=client,
|
||||
),
|
||||
patch.object(hass.config_entries, "async_schedule_reload"),
|
||||
):
|
||||
result = await _start_add_flow_select_site(hass, entry)
|
||||
assert result["step_id"] == "pair"
|
||||
|
||||
result = await hass.config_entries.subentries.async_configure(
|
||||
result["flow_id"], {}
|
||||
)
|
||||
assert result["step_id"] == "pair"
|
||||
assert result["errors"] == {"base": "key_expired"}
|
||||
|
||||
# Submitting the expired form re-registers to open a fresh window.
|
||||
result = await hass.config_entries.subentries.async_configure(
|
||||
result["flow_id"], {}
|
||||
)
|
||||
assert result["step_id"] == "pair"
|
||||
|
||||
result = await hass.config_entries.subentries.async_configure(
|
||||
result["flow_id"], {}
|
||||
)
|
||||
assert result["step_id"] == "credentials"
|
||||
|
||||
result = await hass.config_entries.subentries.async_configure(
|
||||
result["flow_id"], {CONF_HOST: HOST, CONF_PASSWORD: PASSWORD}
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert result["type"] is FlowResultType.CREATE_ENTRY
|
||||
assert mock_add.await_count == 2
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("mock_rsa_key")
|
||||
@pytest.mark.parametrize(
|
||||
("patch_target", "error"),
|
||||
|
||||
Reference in New Issue
Block a user