Skip to content

Commit bdc9eb3

Browse files
balloobfrenck
authored andcommitted
Z-Wave to support migrating from USB to socket with same home ID (home-assistant#153522)
1 parent e0afcbc commit bdc9eb3

File tree

2 files changed

+130
-3
lines changed

2 files changed

+130
-3
lines changed

homeassistant/components/zwave_js/config_flow.py

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -918,7 +918,7 @@ async def async_step_finish_addon_setup_user(
918918
discovery_info = await self._async_get_addon_discovery_info()
919919
self.ws_address = f"ws://{discovery_info['host']}:{discovery_info['port']}"
920920

921-
if not self.unique_id or self.source in (SOURCE_USB, SOURCE_ESPHOME):
921+
if not self.unique_id or self.source == SOURCE_USB:
922922
if not self.version_info:
923923
try:
924924
self.version_info = await async_get_version_info(
@@ -942,7 +942,12 @@ async def async_step_finish_addon_setup_user(
942942
CONF_S2_UNAUTHENTICATED_KEY: self.s2_unauthenticated_key,
943943
CONF_LR_S2_ACCESS_CONTROL_KEY: self.lr_s2_access_control_key,
944944
CONF_LR_S2_AUTHENTICATED_KEY: self.lr_s2_authenticated_key,
945-
}
945+
},
946+
error=(
947+
"migration_successful"
948+
if self.source in (SOURCE_USB, SOURCE_ESPHOME)
949+
else "already_configured"
950+
),
946951
)
947952
return self._async_create_entry_from_vars()
948953

@@ -1490,6 +1495,8 @@ async def async_step_esphome(
14901495
)
14911496
# Only update existing entries that are configured via sockets
14921497
and existing_entry.data.get(CONF_SOCKET_PATH)
1498+
# And use the add-on
1499+
and existing_entry.data.get(CONF_USE_ADDON)
14931500
):
14941501
await self._async_set_addon_config(
14951502
{CONF_ADDON_SOCKET: discovery_info.socket_path}
@@ -1498,6 +1505,11 @@ async def async_step_esphome(
14981505
self.hass.config_entries.async_schedule_reload(existing_entry.entry_id)
14991506
return self.async_abort(reason="already_configured")
15001507

1508+
# We are not aborting if home ID configured here, we just want to make sure that it's set
1509+
# We will update a USB based config entry automatically in `async_step_finish_addon_setup_user`
1510+
await self.async_set_unique_id(
1511+
str(discovery_info.zwave_home_id), raise_on_progress=False
1512+
)
15011513
self.socket_path = discovery_info.socket_path
15021514
self.context["title_placeholders"] = {
15031515
CONF_NAME: f"{discovery_info.name} via ESPHome"

tests/components/zwave_js/test_config_flow.py

Lines changed: 116 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1303,7 +1303,11 @@ async def test_esphome_discovery_already_configured(
13031303
entry = MockConfigEntry(
13041304
entry_id="mock-entry-id",
13051305
domain=DOMAIN,
1306-
data={CONF_SOCKET_PATH: "esphome://existing-device:6053"},
1306+
data={
1307+
CONF_SOCKET_PATH: "esphome://existing-device:6053",
1308+
"use_addon": True,
1309+
"integration_created_addon": True,
1310+
},
13071311
title=TITLE,
13081312
unique_id="1234",
13091313
)
@@ -1333,6 +1337,117 @@ async def test_esphome_discovery_already_configured(
13331337
)
13341338

13351339

1340+
@pytest.mark.usefixtures("supervisor", "addon_not_installed", "addon_info")
1341+
async def test_esphome_discovery_usb_same_home_id(
1342+
hass: HomeAssistant,
1343+
install_addon: AsyncMock,
1344+
set_addon_options: AsyncMock,
1345+
start_addon: AsyncMock,
1346+
) -> None:
1347+
"""Test ESPHome discovery works if USB stick with same home ID is configured."""
1348+
entry = MockConfigEntry(
1349+
entry_id="mock-entry-id",
1350+
domain=DOMAIN,
1351+
data={
1352+
CONF_USB_PATH: "/dev/ttyUSB0",
1353+
"use_addon": True,
1354+
"integration_created_addon": True,
1355+
},
1356+
title=TITLE,
1357+
unique_id="1234",
1358+
)
1359+
entry.add_to_hass(hass)
1360+
1361+
result = await hass.config_entries.flow.async_init(
1362+
DOMAIN,
1363+
context={"source": config_entries.SOURCE_ESPHOME},
1364+
data=ESPHOME_DISCOVERY_INFO,
1365+
)
1366+
1367+
assert result["type"] is FlowResultType.MENU
1368+
assert result["step_id"] == "installation_type"
1369+
assert result["menu_options"] == ["intent_recommended", "intent_custom"]
1370+
1371+
result = await hass.config_entries.flow.async_configure(
1372+
result["flow_id"], {"next_step_id": "intent_custom"}
1373+
)
1374+
1375+
assert result["step_id"] == "install_addon"
1376+
assert result["type"] is FlowResultType.SHOW_PROGRESS
1377+
1378+
# Make sure the flow continues when the progress task is done.
1379+
await hass.async_block_till_done()
1380+
1381+
result = await hass.config_entries.flow.async_configure(result["flow_id"])
1382+
1383+
assert install_addon.call_args == call("core_zwave_js")
1384+
1385+
assert result["type"] is FlowResultType.FORM
1386+
assert result["step_id"] == "network_type"
1387+
1388+
result = await hass.config_entries.flow.async_configure(
1389+
result["flow_id"],
1390+
{
1391+
"network_type": "existing",
1392+
},
1393+
)
1394+
1395+
assert result["type"] is FlowResultType.FORM
1396+
assert result["step_id"] == "configure_security_keys"
1397+
1398+
result = await hass.config_entries.flow.async_configure(
1399+
result["flow_id"],
1400+
{
1401+
"s0_legacy_key": "new123",
1402+
"s2_access_control_key": "new456",
1403+
"s2_authenticated_key": "new789",
1404+
"s2_unauthenticated_key": "new987",
1405+
"lr_s2_access_control_key": "new654",
1406+
"lr_s2_authenticated_key": "new321",
1407+
},
1408+
)
1409+
1410+
assert set_addon_options.call_args == call(
1411+
"core_zwave_js",
1412+
AddonsOptions(
1413+
config={
1414+
"socket": "esphome://192.168.1.100:6053",
1415+
"s0_legacy_key": "new123",
1416+
"s2_access_control_key": "new456",
1417+
"s2_authenticated_key": "new789",
1418+
"s2_unauthenticated_key": "new987",
1419+
"lr_s2_access_control_key": "new654",
1420+
"lr_s2_authenticated_key": "new321",
1421+
}
1422+
),
1423+
)
1424+
1425+
assert result["type"] is FlowResultType.SHOW_PROGRESS
1426+
assert result["step_id"] == "start_addon"
1427+
1428+
await hass.async_block_till_done()
1429+
result = await hass.config_entries.flow.async_configure(result["flow_id"])
1430+
await hass.async_block_till_done()
1431+
1432+
assert start_addon.call_args == call("core_zwave_js")
1433+
1434+
assert result["type"] is FlowResultType.ABORT
1435+
assert result["reason"] == "migration_successful"
1436+
assert entry.data == {
1437+
"url": "ws://host1:3001",
1438+
"usb_path": None,
1439+
"socket_path": "esphome://192.168.1.100:6053",
1440+
"s0_legacy_key": "new123",
1441+
"s2_access_control_key": "new456",
1442+
"s2_authenticated_key": "new789",
1443+
"s2_unauthenticated_key": "new987",
1444+
"lr_s2_access_control_key": "new654",
1445+
"lr_s2_authenticated_key": "new321",
1446+
"use_addon": True,
1447+
"integration_created_addon": True,
1448+
}
1449+
1450+
13361451
@pytest.mark.usefixtures("supervisor", "addon_installed")
13371452
async def test_discovery_addon_not_running(
13381453
hass: HomeAssistant,

0 commit comments

Comments
 (0)