1
0
mirror of https://github.com/home-assistant/core.git synced 2026-02-15 07:36:16 +00:00

SMA add reconfigure flow (#160743)

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
Erwin Douna
2026-01-16 10:16:34 +01:00
committed by GitHub
parent 7c6a31861e
commit f9aa307cb2
4 changed files with 177 additions and 3 deletions

View File

@@ -144,6 +144,51 @@ class SmaConfigFlow(ConfigFlow, domain=DOMAIN):
errors=errors,
)
async def async_step_reconfigure(
self, user_input: dict[str, Any] | None = None
) -> ConfigFlowResult:
"""Handle reconfiguration of the integration."""
errors: dict[str, str] = {}
reconf_entry = self._get_reconfigure_entry()
if user_input is not None:
errors, device_info = await self._handle_user_input(
user_input={
**reconf_entry.data,
**user_input,
}
)
if not errors:
await self.async_set_unique_id(
str(device_info["serial"]), raise_on_progress=False
)
self._abort_if_unique_id_mismatch()
return self.async_update_reload_and_abort(
reconf_entry,
data_updates={
CONF_HOST: user_input[CONF_HOST],
CONF_SSL: user_input[CONF_SSL],
CONF_VERIFY_SSL: user_input[CONF_VERIFY_SSL],
CONF_GROUP: user_input[CONF_GROUP],
},
)
return self.async_show_form(
step_id="reconfigure",
data_schema=self.add_suggested_values_to_schema(
data_schema=vol.Schema(
{
vol.Required(CONF_HOST): cv.string,
vol.Optional(CONF_SSL): cv.boolean,
vol.Optional(CONF_VERIFY_SSL): cv.boolean,
vol.Optional(CONF_GROUP): vol.In(GROUPS),
}
),
suggested_values=user_input or dict(reconf_entry.data),
),
errors=errors,
)
async def async_step_reauth(
self, entry_data: Mapping[str, Any]
) -> ConfigFlowResult:

View File

@@ -3,7 +3,9 @@
"abort": {
"already_configured": "[%key:common::config_flow::abort::already_configured_device%]",
"already_in_progress": "[%key:common::config_flow::abort::already_in_progress%]",
"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%]",
"unique_id_mismatch": "You selected a different SMA device than the one this config entry was configured with, this is not allowed."
},
"error": {
"cannot_connect": "[%key:common::config_flow::error::cannot_connect%]",
@@ -29,6 +31,17 @@
"description": "The SMA integration needs to re-authenticate your connection details",
"title": "[%key:common::config_flow::title::reauth%]"
},
"reconfigure": {
"data": {
"group": "[%key:component::sma::config::step::user::data::group%]",
"host": "[%key:common::config_flow::data::host%]",
"password": "[%key:common::config_flow::data::password%]",
"ssl": "[%key:common::config_flow::data::ssl%]",
"verify_ssl": "[%key:common::config_flow::data::verify_ssl%]"
},
"description": "Use the following form to reconfigure your SMA device.",
"title": "Reconfigure SMA Solar Integration"
},
"user": {
"data": {
"group": "Group",

View File

@@ -35,6 +35,14 @@ MOCK_USER_REAUTH = {
CONF_PASSWORD: "new_password",
}
MOCK_USER_RECONFIGURE = {
CONF_HOST: "1.1.1.2",
CONF_SSL: True,
CONF_VERIFY_SSL: False,
CONF_GROUP: "user",
}
MOCK_DHCP_DISCOVERY_INPUT = {
CONF_SSL: True,
CONF_VERIFY_SSL: False,

View File

@@ -3,11 +3,12 @@
from unittest.mock import AsyncMock, MagicMock, patch
from pysma import SmaAuthenticationException, SmaConnectionException, SmaReadException
from pysma.helpers import DeviceInfo
import pytest
from homeassistant.components.sma.const import DOMAIN
from homeassistant.components.sma.const import CONF_GROUP, DOMAIN
from homeassistant.config_entries import SOURCE_DHCP, SOURCE_USER
from homeassistant.const import CONF_MAC
from homeassistant.const import CONF_HOST, CONF_MAC, CONF_SSL, CONF_VERIFY_SSL
from homeassistant.core import HomeAssistant
from homeassistant.data_entry_flow import FlowResultType
from homeassistant.helpers.device_registry import format_mac
@@ -19,6 +20,7 @@ from . import (
MOCK_DHCP_DISCOVERY_INPUT,
MOCK_USER_INPUT,
MOCK_USER_REAUTH,
MOCK_USER_RECONFIGURE,
)
from tests.conftest import MockConfigEntry
@@ -311,3 +313,109 @@ async def test_reauth_flow_exceptions(
assert result["type"] is FlowResultType.ABORT
assert result["reason"] == "reauth_successful"
async def test_full_flow_reconfigure(
hass: HomeAssistant,
mock_setup_entry: MockConfigEntry,
mock_sma_client: AsyncMock,
) -> None:
"""Test the full flow of the config flow."""
entry = MockConfigEntry(domain=DOMAIN, data=MOCK_USER_INPUT, unique_id="123456789")
entry.add_to_hass(hass)
result = await 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"],
user_input=MOCK_USER_RECONFIGURE,
)
assert result["type"] is FlowResultType.ABORT
assert result["reason"] == "reconfigure_successful"
assert entry.data[CONF_HOST] == "1.1.1.2"
assert entry.data[CONF_SSL] is True
assert entry.data[CONF_VERIFY_SSL] is False
assert entry.data[CONF_GROUP] == "user"
assert len(mock_setup_entry.mock_calls) == 1
@pytest.mark.parametrize(
("exception", "error"),
[
(SmaConnectionException, "cannot_connect"),
(SmaAuthenticationException, "invalid_auth"),
(SmaReadException, "cannot_retrieve_device_info"),
(Exception, "unknown"),
],
)
async def test_full_flow_reconfigure_exceptions(
hass: HomeAssistant,
mock_setup_entry: MockConfigEntry,
mock_sma_client: AsyncMock,
exception: Exception,
error: str,
) -> None:
"""Test we handle cannot connect error and recover from it."""
entry = MockConfigEntry(domain=DOMAIN, data=MOCK_USER_INPUT, unique_id="123456789")
entry.add_to_hass(hass)
result = await entry.start_reconfigure_flow(hass)
assert result["type"] is FlowResultType.FORM
assert result["step_id"] == "reconfigure"
mock_sma_client.new_session.side_effect = exception
result = await hass.config_entries.flow.async_configure(
result["flow_id"],
MOCK_USER_RECONFIGURE,
)
assert result["type"] is FlowResultType.FORM
assert result["errors"] == {"base": error}
mock_sma_client.new_session.side_effect = None
result = await hass.config_entries.flow.async_configure(
result["flow_id"],
user_input=MOCK_USER_RECONFIGURE,
)
assert result["type"] is FlowResultType.ABORT
assert result["reason"] == "reconfigure_successful"
assert entry.data[CONF_HOST] == "1.1.1.2"
assert entry.data[CONF_SSL] is True
assert entry.data[CONF_VERIFY_SSL] is False
assert entry.data[CONF_GROUP] == "user"
assert len(mock_setup_entry.mock_calls) == 1
async def test_reconfigure_mismatch_id(
hass: HomeAssistant,
mock_setup_entry: MockConfigEntry,
mock_sma_client: AsyncMock,
) -> None:
"""Test when a mismatch happens during reconfigure."""
entry = MockConfigEntry(domain=DOMAIN, data=MOCK_USER_INPUT, unique_id="123456789")
entry.add_to_hass(hass)
result = await entry.start_reconfigure_flow(hass)
assert result["type"] is FlowResultType.FORM
assert result["step_id"] == "reconfigure"
# New device, on purpose to demonstrate we can't switch
different_device = DeviceInfo(
manufacturer="SMA",
name="Different SMA Device",
type="Sunny Boy 5.0",
serial=987654321,
sw_version="2.0.0",
)
mock_sma_client.device_info = AsyncMock(return_value=different_device)
result = await hass.config_entries.flow.async_configure(
result["flow_id"],
user_input=MOCK_USER_RECONFIGURE,
)
assert result["type"] is FlowResultType.ABORT
assert result["reason"] == "unique_id_mismatch"