mirror of
https://github.com/home-assistant/core.git
synced 2026-05-08 17:49:37 +01:00
Implement reconfigure config flow in SRP energy (#151542)
Co-authored-by: Joostlek <joostlek@outlook.com>
This commit is contained in:
@@ -7,9 +7,14 @@ from typing import Any
|
||||
from srpenergy.client import SrpEnergyClient
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.config_entries import ConfigFlow, ConfigFlowResult
|
||||
from homeassistant.config_entries import (
|
||||
SOURCE_RECONFIGURE,
|
||||
SOURCE_USER,
|
||||
ConfigFlow,
|
||||
ConfigFlowResult,
|
||||
)
|
||||
from homeassistant.const import CONF_ID, CONF_NAME, CONF_PASSWORD, CONF_USERNAME
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.exceptions import HomeAssistantError
|
||||
|
||||
from .const import CONF_IS_TOU, DOMAIN, LOGGER
|
||||
@@ -40,52 +45,71 @@ class SRPEnergyConfigFlow(ConfigFlow, domain=DOMAIN):
|
||||
|
||||
VERSION = 1
|
||||
|
||||
@callback
|
||||
def _show_form(self, errors: dict[str, Any]) -> ConfigFlowResult:
|
||||
"""Show the form to the user."""
|
||||
LOGGER.debug("Show Form")
|
||||
return self.async_show_form(
|
||||
step_id="user",
|
||||
data_schema=vol.Schema(
|
||||
{
|
||||
vol.Required(
|
||||
CONF_NAME, default=self.hass.config.location_name
|
||||
): str,
|
||||
vol.Required(CONF_ID): str,
|
||||
vol.Required(CONF_USERNAME): str,
|
||||
vol.Required(CONF_PASSWORD): str,
|
||||
vol.Optional(CONF_IS_TOU, default=False): bool,
|
||||
}
|
||||
),
|
||||
errors=errors,
|
||||
)
|
||||
|
||||
async def async_step_user(
|
||||
self, user_input: dict[str, Any] | None = None
|
||||
) -> ConfigFlowResult:
|
||||
"""Handle a flow initialized by the user."""
|
||||
LOGGER.debug("Config entry")
|
||||
errors: dict[str, str] = {}
|
||||
if not user_input:
|
||||
return self._show_form(errors)
|
||||
if user_input:
|
||||
try:
|
||||
await validate_input(self.hass, user_input)
|
||||
except ValueError:
|
||||
# Thrown when the account id is malformed
|
||||
errors["base"] = "invalid_account"
|
||||
except InvalidAuth:
|
||||
errors["base"] = "invalid_auth"
|
||||
except Exception: # noqa: BLE001
|
||||
LOGGER.exception("Unexpected exception")
|
||||
return self.async_abort(reason="unknown")
|
||||
else:
|
||||
await self.async_set_unique_id(user_input[CONF_ID])
|
||||
if self.source == SOURCE_USER:
|
||||
self._abort_if_unique_id_configured()
|
||||
if self.source == SOURCE_RECONFIGURE:
|
||||
self._abort_if_unique_id_mismatch()
|
||||
|
||||
try:
|
||||
await validate_input(self.hass, user_input)
|
||||
except ValueError:
|
||||
# Thrown when the account id is malformed
|
||||
errors["base"] = "invalid_account"
|
||||
return self._show_form(errors)
|
||||
except InvalidAuth:
|
||||
errors["base"] = "invalid_auth"
|
||||
return self._show_form(errors)
|
||||
except Exception: # noqa: BLE001
|
||||
LOGGER.exception("Unexpected exception")
|
||||
return self.async_abort(reason="unknown")
|
||||
if self.source == SOURCE_USER:
|
||||
return self.async_create_entry(
|
||||
title=user_input[CONF_NAME],
|
||||
data=user_input,
|
||||
)
|
||||
return self.async_update_reload_and_abort(
|
||||
self._get_reconfigure_entry(),
|
||||
data=user_input,
|
||||
)
|
||||
return self.async_show_form(
|
||||
step_id="user",
|
||||
data_schema=self.add_suggested_values_to_schema(
|
||||
data_schema=vol.Schema(
|
||||
{
|
||||
vol.Required(CONF_ID): (
|
||||
str
|
||||
if self.source == SOURCE_USER
|
||||
else self._get_reconfigure_entry().data[CONF_ID]
|
||||
),
|
||||
vol.Required(
|
||||
CONF_NAME, default=self.hass.config.location_name
|
||||
): str,
|
||||
vol.Required(CONF_USERNAME): str,
|
||||
vol.Required(CONF_PASSWORD): str,
|
||||
vol.Optional(CONF_IS_TOU, default=False): bool,
|
||||
}
|
||||
),
|
||||
suggested_values=(
|
||||
user_input or self._get_reconfigure_entry().data
|
||||
if self.source == SOURCE_RECONFIGURE
|
||||
else None
|
||||
),
|
||||
),
|
||||
errors=errors,
|
||||
)
|
||||
|
||||
await self.async_set_unique_id(user_input[CONF_ID])
|
||||
self._abort_if_unique_id_configured()
|
||||
|
||||
return self.async_create_entry(title=user_input[CONF_NAME], data=user_input)
|
||||
async def async_step_reconfigure(
|
||||
self, user_input: dict[str, Any]
|
||||
) -> ConfigFlowResult:
|
||||
"""Handle reconfiguration."""
|
||||
return await self.async_step_user()
|
||||
|
||||
|
||||
class InvalidAuth(HomeAssistantError):
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
"config": {
|
||||
"abort": {
|
||||
"already_configured": "[%key:common::config_flow::abort::already_configured_service%]",
|
||||
"reconfigure_successful": "[%key:common::config_flow::abort::reconfigure_successful%]",
|
||||
"unknown": "[%key:common::config_flow::error::unknown%]"
|
||||
},
|
||||
"error": {
|
||||
|
||||
@@ -88,3 +88,12 @@ async def init_integration(
|
||||
await hass.async_block_till_done()
|
||||
|
||||
return mock_config_entry
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mock_setup_entry() -> Generator[MagicMock]:
|
||||
"""Mock async_setup_entry."""
|
||||
with patch(
|
||||
"homeassistant.components.srp_energy.async_setup_entry", return_value=True
|
||||
) as mock_setup_entry:
|
||||
yield mock_setup_entry
|
||||
|
||||
@@ -6,7 +6,13 @@ import pytest
|
||||
|
||||
from homeassistant.components.srp_energy.const import CONF_IS_TOU, DOMAIN
|
||||
from homeassistant.config_entries import SOURCE_USER, ConfigEntryState
|
||||
from homeassistant.const import CONF_ID, CONF_PASSWORD, CONF_SOURCE, CONF_USERNAME
|
||||
from homeassistant.const import (
|
||||
CONF_ID,
|
||||
CONF_NAME,
|
||||
CONF_PASSWORD,
|
||||
CONF_SOURCE,
|
||||
CONF_USERNAME,
|
||||
)
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.data_entry_flow import FlowResultType
|
||||
|
||||
@@ -172,3 +178,111 @@ async def test_flow_multiple_configs(
|
||||
entries = hass.config_entries.async_entries()
|
||||
domain_entries = [entry for entry in entries if entry.domain == DOMAIN]
|
||||
assert len(domain_entries) == 2
|
||||
|
||||
|
||||
async def test_reconfigure(
|
||||
hass: HomeAssistant, init_integration: MockConfigEntry
|
||||
) -> None:
|
||||
"""Test reconfiguring an existing entry."""
|
||||
|
||||
result = await init_integration.start_reconfigure_flow(hass)
|
||||
|
||||
assert result["type"] is FlowResultType.FORM
|
||||
assert result["step_id"] == "user"
|
||||
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
{
|
||||
CONF_ID: ACCNT_ID,
|
||||
CONF_NAME: ACCNT_NAME + "reconf",
|
||||
CONF_USERNAME: ACCNT_USERNAME + "reconf",
|
||||
CONF_PASSWORD: ACCNT_PASSWORD + "reconf",
|
||||
CONF_IS_TOU: not ACCNT_IS_TOU,
|
||||
},
|
||||
)
|
||||
|
||||
assert result["type"] is FlowResultType.ABORT
|
||||
assert result["reason"] == "reconfigure_successful"
|
||||
assert init_integration.data == {
|
||||
CONF_ID: ACCNT_ID,
|
||||
CONF_NAME: ACCNT_NAME + "reconf",
|
||||
CONF_USERNAME: ACCNT_USERNAME + "reconf",
|
||||
CONF_PASSWORD: ACCNT_PASSWORD + "reconf",
|
||||
CONF_IS_TOU: not ACCNT_IS_TOU,
|
||||
}
|
||||
|
||||
|
||||
async def test_reconfigure_error(
|
||||
hass: HomeAssistant,
|
||||
init_integration: MockConfigEntry,
|
||||
mock_srp_energy_config_flow: MagicMock,
|
||||
mock_setup_entry: MagicMock,
|
||||
) -> None:
|
||||
"""Test reconfiguring an existing entry."""
|
||||
|
||||
result = await init_integration.start_reconfigure_flow(hass)
|
||||
|
||||
assert result["type"] is FlowResultType.FORM
|
||||
assert result["step_id"] == "user"
|
||||
|
||||
mock_srp_energy_config_flow.validate.side_effect = ValueError
|
||||
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
{
|
||||
CONF_ID: ACCNT_ID,
|
||||
CONF_NAME: ACCNT_NAME + "reconf",
|
||||
CONF_USERNAME: ACCNT_USERNAME + "reconf",
|
||||
CONF_PASSWORD: ACCNT_PASSWORD + "reconf",
|
||||
CONF_IS_TOU: not ACCNT_IS_TOU,
|
||||
},
|
||||
)
|
||||
|
||||
assert result["type"] is FlowResultType.FORM
|
||||
assert result["errors"] == {"base": "invalid_account"}
|
||||
|
||||
mock_srp_energy_config_flow.validate.side_effect = None
|
||||
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
{
|
||||
CONF_ID: ACCNT_ID,
|
||||
CONF_NAME: ACCNT_NAME + "reconf",
|
||||
CONF_USERNAME: ACCNT_USERNAME + "reconf",
|
||||
CONF_PASSWORD: ACCNT_PASSWORD + "reconf",
|
||||
CONF_IS_TOU: not ACCNT_IS_TOU,
|
||||
},
|
||||
)
|
||||
|
||||
assert result["type"] is FlowResultType.ABORT
|
||||
assert result["reason"] == "reconfigure_successful"
|
||||
|
||||
|
||||
async def test_reconfigure_unknown_error(
|
||||
hass: HomeAssistant,
|
||||
init_integration: MockConfigEntry,
|
||||
mock_srp_energy_config_flow: MagicMock,
|
||||
mock_setup_entry: MagicMock,
|
||||
) -> None:
|
||||
"""Test reconfiguring an existing entry and handling unknown error."""
|
||||
|
||||
result = await init_integration.start_reconfigure_flow(hass)
|
||||
|
||||
assert result["type"] is FlowResultType.FORM
|
||||
assert result["step_id"] == "user"
|
||||
|
||||
mock_srp_energy_config_flow.validate.side_effect = Exception
|
||||
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
{
|
||||
CONF_ID: ACCNT_ID,
|
||||
CONF_NAME: ACCNT_NAME + "reconf",
|
||||
CONF_USERNAME: ACCNT_USERNAME + "reconf",
|
||||
CONF_PASSWORD: ACCNT_PASSWORD + "reconf",
|
||||
CONF_IS_TOU: not ACCNT_IS_TOU,
|
||||
},
|
||||
)
|
||||
|
||||
assert result["type"] is FlowResultType.ABORT
|
||||
assert result["reason"] == "unknown"
|
||||
|
||||
Reference in New Issue
Block a user