Add reconfigure flow to Peblar Rocksolid EV Chargers integration (#133785)

This commit is contained in:
Franck Nijhof 2024-12-22 14:23:12 +01:00 committed by GitHub
parent 075f95b9c4
commit 959f20c523
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 175 additions and 2 deletions

View File

@ -76,6 +76,57 @@ class PeblarFlowHandler(ConfigFlow, domain=DOMAIN):
errors=errors,
)
async def async_step_reconfigure(
self, user_input: dict[str, Any] | None = None
) -> ConfigFlowResult:
"""Handle reconfiguration of a Peblar device."""
errors = {}
reconfigure_entry = self._get_reconfigure_entry()
if user_input is not None:
peblar = Peblar(
host=user_input[CONF_HOST],
session=async_create_clientsession(
self.hass, cookie_jar=CookieJar(unsafe=True)
),
)
try:
await peblar.login(password=user_input[CONF_PASSWORD])
info = await peblar.system_information()
except PeblarAuthenticationError:
errors[CONF_PASSWORD] = "invalid_auth"
except PeblarConnectionError:
errors[CONF_HOST] = "cannot_connect"
except Exception: # noqa: BLE001
LOGGER.exception("Unexpected exception")
errors["base"] = "unknown"
else:
await self.async_set_unique_id(info.product_serial_number)
self._abort_if_unique_id_mismatch(reason="different_device")
return self.async_update_reload_and_abort(
reconfigure_entry,
data_updates=user_input,
)
host = reconfigure_entry.data[CONF_HOST]
if user_input is not None:
host = user_input[CONF_HOST]
return self.async_show_form(
step_id="reconfigure",
data_schema=vol.Schema(
{
vol.Required(CONF_HOST, default=host): TextSelector(
TextSelectorConfig(autocomplete="off")
),
vol.Required(CONF_PASSWORD): TextSelector(
TextSelectorConfig(type=TextSelectorType.PASSWORD)
),
}
),
errors=errors,
)
async def async_step_zeroconf(
self, discovery_info: zeroconf.ZeroconfServiceInfo
) -> ConfigFlowResult:

View File

@ -66,7 +66,7 @@ rules:
comment: |
The coordinator needs translation when the update failed.
icon-translations: done
reconfiguration-flow: todo
reconfiguration-flow: done
repair-issues:
status: exempt
comment: |

View File

@ -2,8 +2,10 @@
"config": {
"abort": {
"already_configured": "[%key:common::config_flow::abort::already_configured_device%]",
"different_device": "The information entered is from a different Peblar EV charger.",
"no_serial_number": "The discovered Peblar device did not provide a serial number.",
"reauth_successful": "[%key:common::config_flow::abort::reauth_successful%]"
"reauth_successful": "[%key:common::config_flow::abort::reauth_successful%]",
"reconfigure_successful": "[%key:common::config_flow::abort::reconfigure_successful%]"
},
"error": {
"cannot_connect": "[%key:common::config_flow::error::cannot_connect%]",
@ -20,6 +22,17 @@
},
"description": "Reauthenticate with your Peblar RV charger.\n\nTo do so, you will need to enter your new password you use to log into Peblar's device web interface."
},
"reconfigure": {
"data": {
"host": "[%key:common::config_flow::data::host%]",
"password": "[%key:common::config_flow::data::password%]"
},
"data_description": {
"host": "[%key:component::peblar::config::step::user::data_description::host%]",
"password": "[%key:component::peblar::config::step::user::data_description::password%]"
},
"description": "Reconfigure your Peblar EV charger.\n\nThis allows you to change the IP address of your Peblar charger and the password you use to log into the Peblar device' web interface."
},
"user": {
"data": {
"host": "[%key:common::config_flow::data::host%]",

View File

@ -117,6 +117,115 @@ async def test_user_flow_already_configured(
assert result["reason"] == "already_configured"
@pytest.mark.usefixtures("mock_peblar")
async def test_reconfigure_flow(
hass: HomeAssistant, mock_config_entry: MockConfigEntry
) -> None:
"""Test the full happy path reconfigure flow from start to finish."""
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"
assert mock_config_entry.data == {
CONF_HOST: "127.0.0.127",
CONF_PASSWORD: "OMGSPIDERS",
}
result = await hass.config_entries.flow.async_configure(
result["flow_id"],
user_input={
CONF_HOST: "127.0.0.1",
CONF_PASSWORD: "OMGPUPPIES",
},
)
assert result["type"] is FlowResultType.ABORT
assert result["reason"] == "reconfigure_successful"
assert mock_config_entry.data == {
CONF_HOST: "127.0.0.1",
CONF_PASSWORD: "OMGPUPPIES",
}
@pytest.mark.usefixtures("mock_peblar")
async def test_reconfigure_to_different_device(
hass: HomeAssistant,
mock_config_entry: MockConfigEntry,
) -> None:
"""Test reconfiguring to a different device doesn't work."""
mock_config_entry.add_to_hass(hass)
# Change the unique ID of the entry, so we have a mismatch
hass.config_entries.async_update_entry(mock_config_entry, unique_id="mismatch")
result = await mock_config_entry.start_reconfigure_flow(hass)
assert result["type"] is FlowResultType.FORM
result = await hass.config_entries.flow.async_configure(
result["flow_id"],
user_input={
CONF_HOST: "127.0.0.1",
CONF_PASSWORD: "OMGPUPPIES",
},
)
assert result["type"] is FlowResultType.ABORT
assert result["reason"] == "different_device"
@pytest.mark.parametrize(
("side_effect", "expected_error"),
[
(PeblarConnectionError, {CONF_HOST: "cannot_connect"}),
(PeblarAuthenticationError, {CONF_PASSWORD: "invalid_auth"}),
(Exception, {"base": "unknown"}),
],
)
async def test_reconfigure_flow_errors(
hass: HomeAssistant,
mock_peblar: MagicMock,
mock_config_entry: MockConfigEntry,
side_effect: Exception,
expected_error: dict[str, str],
) -> None:
"""Test we show user form on a connection error."""
mock_config_entry.add_to_hass(hass)
mock_peblar.login.side_effect = side_effect
result = await mock_config_entry.start_reconfigure_flow(hass)
assert result["type"] is FlowResultType.FORM
result = await hass.config_entries.flow.async_configure(
result["flow_id"],
user_input={
CONF_HOST: "127.0.0.1",
CONF_PASSWORD: "OMGPUPPIES",
},
)
assert result["type"] is FlowResultType.FORM
assert result["errors"] == expected_error
mock_peblar.login.side_effect = None
result = await hass.config_entries.flow.async_configure(
result["flow_id"],
user_input={
CONF_HOST: "127.0.0.2",
CONF_PASSWORD: "OMGPUPPIES",
},
)
assert result["type"] is FlowResultType.ABORT
assert mock_config_entry.data == {
CONF_HOST: "127.0.0.2",
CONF_PASSWORD: "OMGPUPPIES",
}
@pytest.mark.usefixtures("mock_peblar")
async def test_zeroconf_flow(hass: HomeAssistant) -> None:
"""Test the zeroconf happy flow from start to finish."""