mirror of
https://github.com/home-assistant/core.git
synced 2025-07-15 17:27:10 +00:00
Repair Z-Wave unknown controller (#144738)
Co-authored-by: Franck Nijhof <git@frenck.dev>
This commit is contained in:
parent
15a4514c7d
commit
3eed552c56
@ -278,6 +278,39 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
# and we'll handle the clean up below.
|
||||
await driver_events.setup(driver)
|
||||
|
||||
if (old_unique_id := entry.unique_id) is not None and old_unique_id != (
|
||||
new_unique_id := str(driver.controller.home_id)
|
||||
):
|
||||
device_registry = dr.async_get(hass)
|
||||
controller_model = "Unknown model"
|
||||
if (
|
||||
(own_node := driver.controller.own_node)
|
||||
and (
|
||||
controller_device_entry := device_registry.async_get_device(
|
||||
identifiers={get_device_id(driver, own_node)}
|
||||
)
|
||||
)
|
||||
and (model := controller_device_entry.model)
|
||||
):
|
||||
controller_model = model
|
||||
async_create_issue(
|
||||
hass,
|
||||
DOMAIN,
|
||||
f"migrate_unique_id.{entry.entry_id}",
|
||||
data={
|
||||
"config_entry_id": entry.entry_id,
|
||||
"config_entry_title": entry.title,
|
||||
"controller_model": controller_model,
|
||||
"new_unique_id": new_unique_id,
|
||||
"old_unique_id": old_unique_id,
|
||||
},
|
||||
is_fixable=True,
|
||||
severity=IssueSeverity.ERROR,
|
||||
translation_key="migrate_unique_id",
|
||||
)
|
||||
else:
|
||||
async_delete_issue(hass, DOMAIN, f"migrate_unique_id.{entry.entry_id}")
|
||||
|
||||
# If the listen task is already failed, we need to raise ConfigEntryNotReady
|
||||
if listen_task.done():
|
||||
listen_error, error_message = _get_listen_task_error(listen_task)
|
||||
|
@ -57,6 +57,47 @@ class DeviceConfigFileChangedFlow(RepairsFlow):
|
||||
)
|
||||
|
||||
|
||||
class MigrateUniqueIDFlow(RepairsFlow):
|
||||
"""Handler for an issue fixing flow."""
|
||||
|
||||
def __init__(self, data: dict[str, str]) -> None:
|
||||
"""Initialize."""
|
||||
self.description_placeholders: dict[str, str] = {
|
||||
"config_entry_title": data["config_entry_title"],
|
||||
"controller_model": data["controller_model"],
|
||||
"new_unique_id": data["new_unique_id"],
|
||||
"old_unique_id": data["old_unique_id"],
|
||||
}
|
||||
self._config_entry_id: str = data["config_entry_id"]
|
||||
|
||||
async def async_step_init(
|
||||
self, user_input: dict[str, str] | None = None
|
||||
) -> data_entry_flow.FlowResult:
|
||||
"""Handle the first step of a fix flow."""
|
||||
return await self.async_step_confirm()
|
||||
|
||||
async def async_step_confirm(
|
||||
self, user_input: dict[str, str] | None = None
|
||||
) -> data_entry_flow.FlowResult:
|
||||
"""Handle the confirm step of a fix flow."""
|
||||
if user_input is not None:
|
||||
config_entry = self.hass.config_entries.async_get_entry(
|
||||
self._config_entry_id
|
||||
)
|
||||
# If config entry was removed, we can ignore the issue.
|
||||
if config_entry is not None:
|
||||
self.hass.config_entries.async_update_entry(
|
||||
config_entry,
|
||||
unique_id=self.description_placeholders["new_unique_id"],
|
||||
)
|
||||
return self.async_create_entry(data={})
|
||||
|
||||
return self.async_show_form(
|
||||
step_id="confirm",
|
||||
description_placeholders=self.description_placeholders,
|
||||
)
|
||||
|
||||
|
||||
async def async_create_fix_flow(
|
||||
hass: HomeAssistant, issue_id: str, data: dict[str, str] | None
|
||||
) -> RepairsFlow:
|
||||
@ -65,4 +106,7 @@ async def async_create_fix_flow(
|
||||
if issue_id.split(".")[0] == "device_config_file_changed":
|
||||
assert data
|
||||
return DeviceConfigFileChangedFlow(data)
|
||||
if issue_id.split(".")[0] == "migrate_unique_id":
|
||||
assert data
|
||||
return MigrateUniqueIDFlow(data)
|
||||
return ConfirmRepairFlow()
|
||||
|
@ -273,6 +273,17 @@
|
||||
"invalid_server_version": {
|
||||
"description": "The version of Z-Wave Server you are currently running is too old for this version of Home Assistant. Please update the Z-Wave Server to the latest version to fix this issue.",
|
||||
"title": "Newer version of Z-Wave Server needed"
|
||||
},
|
||||
"migrate_unique_id": {
|
||||
"fix_flow": {
|
||||
"step": {
|
||||
"confirm": {
|
||||
"description": "A Z-Wave controller of model {controller_model} with a different ID ({new_unique_id}) than the previously connected controller ({old_unique_id}) was connected to the {config_entry_title} configuration entry.\n\nReasons for a different controller ID could be:\n\n1. The controller was factory reset, with a 3rd party application.\n2. A controller Non Volatile Memory (NVM) backup was restored to the controller, with a 3rd party application.\n3. A different controller was connected to this configuration entry.\n\nIf a different controller was connected, you should instead set up a new configuration entry for the new controller.\n\nIf you are sure that the current controller is the correct controller you can confirm this by pressing Submit, and the configuration entry will remember the new controller ID.",
|
||||
"title": "An unknown controller was detected"
|
||||
}
|
||||
}
|
||||
},
|
||||
"title": "An unknown controller was detected"
|
||||
}
|
||||
},
|
||||
"services": {
|
||||
|
@ -843,7 +843,11 @@ async def integration_fixture(
|
||||
platforms: list[Platform],
|
||||
) -> MockConfigEntry:
|
||||
"""Set up the zwave_js integration."""
|
||||
entry = MockConfigEntry(domain="zwave_js", data={"url": "ws://test.org"})
|
||||
entry = MockConfigEntry(
|
||||
domain="zwave_js",
|
||||
data={"url": "ws://test.org"},
|
||||
unique_id=str(client.driver.controller.home_id),
|
||||
)
|
||||
entry.add_to_hass(hass)
|
||||
with patch("homeassistant.components.zwave_js.PLATFORMS", platforms):
|
||||
await hass.config_entries.async_setup(entry.entry_id)
|
||||
|
@ -12,6 +12,7 @@ from homeassistant.components.zwave_js.helpers import get_device_id
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers import device_registry as dr, issue_registry as ir
|
||||
|
||||
from tests.common import MockConfigEntry
|
||||
from tests.components.repairs import (
|
||||
async_process_repairs_platforms,
|
||||
process_repair_fix_flow,
|
||||
@ -268,3 +269,118 @@ async def test_abort_confirm(
|
||||
assert data["type"] == "abort"
|
||||
assert data["reason"] == "cannot_connect"
|
||||
assert data["description_placeholders"] == {"device_name": device.name}
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("client")
|
||||
async def test_migrate_unique_id(
|
||||
hass: HomeAssistant,
|
||||
hass_client: ClientSessionGenerator,
|
||||
hass_ws_client: WebSocketGenerator,
|
||||
) -> None:
|
||||
"""Test the migrate unique id flow."""
|
||||
old_unique_id = "123456789"
|
||||
config_entry = MockConfigEntry(
|
||||
domain=DOMAIN,
|
||||
title="Z-Wave JS",
|
||||
data={
|
||||
"url": "ws://test.org",
|
||||
},
|
||||
unique_id=old_unique_id,
|
||||
)
|
||||
config_entry.add_to_hass(hass)
|
||||
|
||||
await hass.config_entries.async_setup(config_entry.entry_id)
|
||||
|
||||
await async_process_repairs_platforms(hass)
|
||||
ws_client = await hass_ws_client(hass)
|
||||
http_client = await hass_client()
|
||||
|
||||
# Assert the issue is present
|
||||
await ws_client.send_json_auto_id({"type": "repairs/list_issues"})
|
||||
msg = await ws_client.receive_json()
|
||||
assert msg["success"]
|
||||
assert len(msg["result"]["issues"]) == 1
|
||||
issue = msg["result"]["issues"][0]
|
||||
issue_id = issue["issue_id"]
|
||||
assert issue_id == f"migrate_unique_id.{config_entry.entry_id}"
|
||||
|
||||
data = await start_repair_fix_flow(http_client, DOMAIN, issue_id)
|
||||
|
||||
flow_id = data["flow_id"]
|
||||
assert data["step_id"] == "confirm"
|
||||
assert data["description_placeholders"] == {
|
||||
"config_entry_title": "Z-Wave JS",
|
||||
"controller_model": "ZW090",
|
||||
"new_unique_id": "3245146787",
|
||||
"old_unique_id": old_unique_id,
|
||||
}
|
||||
|
||||
# Apply fix
|
||||
data = await process_repair_fix_flow(http_client, flow_id)
|
||||
|
||||
assert data["type"] == "create_entry"
|
||||
assert config_entry.unique_id == "3245146787"
|
||||
|
||||
await ws_client.send_json_auto_id({"type": "repairs/list_issues"})
|
||||
msg = await ws_client.receive_json()
|
||||
assert msg["success"]
|
||||
assert len(msg["result"]["issues"]) == 0
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("client")
|
||||
async def test_migrate_unique_id_missing_config_entry(
|
||||
hass: HomeAssistant,
|
||||
hass_client: ClientSessionGenerator,
|
||||
hass_ws_client: WebSocketGenerator,
|
||||
) -> None:
|
||||
"""Test the migrate unique id flow with missing config entry."""
|
||||
old_unique_id = "123456789"
|
||||
config_entry = MockConfigEntry(
|
||||
domain=DOMAIN,
|
||||
title="Z-Wave JS",
|
||||
data={
|
||||
"url": "ws://test.org",
|
||||
},
|
||||
unique_id=old_unique_id,
|
||||
)
|
||||
config_entry.add_to_hass(hass)
|
||||
|
||||
await hass.config_entries.async_setup(config_entry.entry_id)
|
||||
|
||||
await async_process_repairs_platforms(hass)
|
||||
ws_client = await hass_ws_client(hass)
|
||||
http_client = await hass_client()
|
||||
|
||||
# Assert the issue is present
|
||||
await ws_client.send_json_auto_id({"type": "repairs/list_issues"})
|
||||
msg = await ws_client.receive_json()
|
||||
assert msg["success"]
|
||||
assert len(msg["result"]["issues"]) == 1
|
||||
issue = msg["result"]["issues"][0]
|
||||
issue_id = issue["issue_id"]
|
||||
assert issue_id == f"migrate_unique_id.{config_entry.entry_id}"
|
||||
|
||||
await hass.config_entries.async_remove(config_entry.entry_id)
|
||||
|
||||
assert not hass.config_entries.async_get_entry(config_entry.entry_id)
|
||||
|
||||
data = await start_repair_fix_flow(http_client, DOMAIN, issue_id)
|
||||
|
||||
flow_id = data["flow_id"]
|
||||
assert data["step_id"] == "confirm"
|
||||
assert data["description_placeholders"] == {
|
||||
"config_entry_title": "Z-Wave JS",
|
||||
"controller_model": "ZW090",
|
||||
"new_unique_id": "3245146787",
|
||||
"old_unique_id": old_unique_id,
|
||||
}
|
||||
|
||||
# Apply fix
|
||||
data = await process_repair_fix_flow(http_client, flow_id)
|
||||
|
||||
assert data["type"] == "create_entry"
|
||||
|
||||
await ws_client.send_json_auto_id({"type": "repairs/list_issues"})
|
||||
msg = await ws_client.receive_json()
|
||||
assert msg["success"]
|
||||
assert len(msg["result"]["issues"]) == 0
|
||||
|
Loading…
x
Reference in New Issue
Block a user