1
0
mirror of https://github.com/home-assistant/core.git synced 2026-05-08 17:49:37 +01:00

Add unique_id to openevse user flow and import flow (#160436)

Co-authored-by: Joostlek <joostlek@outlook.com>
This commit is contained in:
Chris
2026-01-07 15:06:25 -07:00
committed by GitHub
parent 10d32b7f23
commit 2c6d6f8ab4
4 changed files with 67 additions and 10 deletions
@@ -9,7 +9,7 @@ from homeassistant.config_entries import ConfigFlow, ConfigFlowResult
from homeassistant.const import CONF_HOST, CONF_NAME
from homeassistant.helpers.service_info import zeroconf
from .const import CONF_ID, DOMAIN
from .const import CONF_ID, CONF_SERIAL, DOMAIN
class OpenEVSEConfigFlow(ConfigFlow, domain=DOMAIN):
@@ -22,27 +22,29 @@ class OpenEVSEConfigFlow(ConfigFlow, domain=DOMAIN):
"""Set up the instance."""
self.discovery_info: dict[str, Any] = {}
async def check_status(self, host: str) -> bool:
async def check_status(self, host: str) -> tuple[bool, str | None]:
"""Check if we can connect to the OpenEVSE charger."""
charger = OpenEVSE(host)
try:
await charger.test_and_get()
result = await charger.test_and_get()
except TimeoutError:
return False
else:
return True
return False, None
return True, result.get(CONF_SERIAL)
async def async_step_user(
self, user_input: dict[str, Any] | None = None
) -> ConfigFlowResult:
"""Handle the initial step."""
errors = None
errors = {}
if user_input is not None:
self._async_abort_entries_match({CONF_HOST: user_input[CONF_HOST]})
if await self.check_status(user_input[CONF_HOST]):
if (result := await self.check_status(user_input[CONF_HOST]))[0]:
if (serial := result[1]) is not None:
await self.async_set_unique_id(serial, raise_on_progress=False)
self._abort_if_unique_id_configured()
return self.async_create_entry(
title=f"OpenEVSE {user_input[CONF_HOST]}",
data=user_input,
@@ -60,7 +62,11 @@ class OpenEVSEConfigFlow(ConfigFlow, domain=DOMAIN):
self._async_abort_entries_match({CONF_HOST: data[CONF_HOST]})
if not await self.check_status(data[CONF_HOST]):
if (result := await self.check_status(data[CONF_HOST]))[0]:
if (serial := result[1]) is not None:
await self.async_set_unique_id(serial)
self._abort_if_unique_id_configured()
else:
return self.async_abort(reason="unavailable_host")
return self.async_create_entry(
@@ -87,7 +93,7 @@ class OpenEVSEConfigFlow(ConfigFlow, domain=DOMAIN):
)
self.context.update({"title_placeholders": {"name": name}})
if not await self.check_status(host):
if not (await self.check_status(host))[0]:
return self.async_abort(reason="cannot_connect")
return await self.async_step_discovery_confirm()
@@ -1,5 +1,6 @@
"""Constants for the OpenEVSE integration."""
CONF_ID = "id"
CONF_SERIAL = "serial"
DOMAIN = "openevse"
INTEGRATION_TITLE = "OpenEVSE"
+5
View File
@@ -34,6 +34,11 @@ def mock_charger() -> Generator[MagicMock]:
charger.usage_session = 15000 # 15 kWh in Wh
charger.usage_total = 500000 # 500 kWh in Wh
charger.charging_current = 32.0
charger.test_and_get = AsyncMock()
charger.test_and_get.return_value = {
"serial": "deadbeeffeed",
"model": "openevse_wifi_v1",
}
yield charger
@@ -3,6 +3,8 @@
from ipaddress import ip_address
from unittest.mock import AsyncMock, MagicMock
from openevsehttp.exceptions import MissingSerial
from homeassistant.components.openevse.const import DOMAIN
from homeassistant.config_entries import SOURCE_IMPORT, SOURCE_USER, SOURCE_ZEROCONF
from homeassistant.const import CONF_HOST
@@ -35,6 +37,7 @@ async def test_user_flow(
assert result["data"] == {
CONF_HOST: "10.0.0.131",
}
assert result["result"].unique_id == "deadbeeffeed"
async def test_user_flow_flaky(
@@ -68,6 +71,7 @@ async def test_user_flow_flaky(
assert result["data"] == {
CONF_HOST: "10.0.0.131",
}
assert result["result"].unique_id == "deadbeeffeed"
async def test_user_flow_duplicate(
@@ -108,6 +112,7 @@ async def test_import_flow(
assert result["data"] == {
CONF_HOST: "10.0.0.131",
}
assert result["result"].unique_id == "deadbeeffeed"
async def test_import_flow_bad(
@@ -266,3 +271,43 @@ async def test_zeroconf_already_configured_host(
# Should abort because the host matches an existing entry
assert result["type"] is FlowResultType.ABORT
assert result["reason"] == "already_configured"
async def test_user_flow_no_serial(
hass: HomeAssistant,
mock_charger: MagicMock,
mock_setup_entry: AsyncMock,
) -> None:
"""Test user flow handles missing serial gracefully."""
mock_charger.test_and_get.side_effect = [{}, MissingSerial]
result = await hass.config_entries.flow.async_init(
DOMAIN,
context={"source": SOURCE_USER},
)
assert result["type"] is FlowResultType.FORM
result = await hass.config_entries.flow.async_configure(
result["flow_id"],
{CONF_HOST: "10.0.0.131"},
)
assert result["type"] is FlowResultType.CREATE_ENTRY
assert result["title"] == "OpenEVSE 10.0.0.131"
assert result["result"].unique_id is None
async def test_import_flow_no_serial(
hass: HomeAssistant,
mock_charger: MagicMock,
mock_setup_entry: AsyncMock,
) -> None:
"""Test import flow handles missing serial gracefully."""
mock_charger.test_and_get.side_effect = [{}, MissingSerial]
result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": SOURCE_IMPORT}, data={CONF_HOST: "10.0.0.131"}
)
# Assert the flow continued to create the entry
assert result["type"] is FlowResultType.CREATE_ENTRY
assert result["title"] == "OpenEVSE 10.0.0.131"
assert result["result"].unique_id is None