mirror of
https://github.com/home-assistant/core.git
synced 2025-07-23 21:27:38 +00:00
Fix Z-Wave unique id update during controller migration (#145185)
This commit is contained in:
parent
0fc81d6b33
commit
08104eec56
@ -105,6 +105,7 @@ from .const import (
|
||||
CONF_USE_ADDON,
|
||||
DATA_CLIENT,
|
||||
DOMAIN,
|
||||
DRIVER_READY_TIMEOUT,
|
||||
EVENT_DEVICE_ADDED_TO_REGISTRY,
|
||||
EVENT_VALUE_UPDATED,
|
||||
LIB_LOGGER,
|
||||
@ -135,7 +136,6 @@ from .services import ZWaveServices
|
||||
|
||||
CONNECT_TIMEOUT = 10
|
||||
DATA_DRIVER_EVENTS = "driver_events"
|
||||
DRIVER_READY_TIMEOUT = 60
|
||||
|
||||
CONFIG_SCHEMA = vol.Schema(
|
||||
{
|
||||
|
@ -88,9 +88,9 @@ from .const import (
|
||||
CONF_INSTALLER_MODE,
|
||||
DATA_CLIENT,
|
||||
DOMAIN,
|
||||
DRIVER_READY_TIMEOUT,
|
||||
EVENT_DEVICE_ADDED_TO_REGISTRY,
|
||||
LOGGER,
|
||||
RESTORE_NVM_DRIVER_READY_TIMEOUT,
|
||||
USER_AGENT,
|
||||
)
|
||||
from .helpers import (
|
||||
@ -189,8 +189,6 @@ STRATEGY = "strategy"
|
||||
# https://github.com/zwave-js/node-zwave-js/blob/master/packages/core/src/security/QR.ts#L41
|
||||
MINIMUM_QR_STRING_LENGTH = 52
|
||||
|
||||
HARD_RESET_CONTROLLER_DRIVER_READY_TIMEOUT = 60
|
||||
|
||||
|
||||
# Helper schemas
|
||||
PLANNED_PROVISIONING_ENTRY_SCHEMA = vol.All(
|
||||
@ -2866,7 +2864,7 @@ async def websocket_hard_reset_controller(
|
||||
await driver.async_hard_reset()
|
||||
|
||||
with suppress(TimeoutError):
|
||||
async with asyncio.timeout(HARD_RESET_CONTROLLER_DRIVER_READY_TIMEOUT):
|
||||
async with asyncio.timeout(DRIVER_READY_TIMEOUT):
|
||||
await wait_driver_ready.wait()
|
||||
|
||||
# When resetting the controller, the controller home id is also changed.
|
||||
@ -3113,7 +3111,7 @@ async def websocket_restore_nvm(
|
||||
await controller.async_restore_nvm_base64(msg["data"])
|
||||
|
||||
with suppress(TimeoutError):
|
||||
async with asyncio.timeout(RESTORE_NVM_DRIVER_READY_TIMEOUT):
|
||||
async with asyncio.timeout(DRIVER_READY_TIMEOUT):
|
||||
await wait_driver_ready.wait()
|
||||
await hass.config_entries.async_reload(entry.entry_id)
|
||||
|
||||
|
@ -65,7 +65,7 @@ from .const import (
|
||||
CONF_USE_ADDON,
|
||||
DATA_CLIENT,
|
||||
DOMAIN,
|
||||
RESTORE_NVM_DRIVER_READY_TIMEOUT,
|
||||
DRIVER_READY_TIMEOUT,
|
||||
)
|
||||
from .helpers import CannotConnect, async_get_version_info
|
||||
|
||||
@ -776,16 +776,13 @@ class ZWaveJSConfigFlow(ConfigFlow, domain=DOMAIN):
|
||||
)
|
||||
|
||||
@callback
|
||||
def _async_update_entry(
|
||||
self, updates: dict[str, Any], *, schedule_reload: bool = True
|
||||
) -> None:
|
||||
def _async_update_entry(self, updates: dict[str, Any]) -> None:
|
||||
"""Update the config entry with new data."""
|
||||
config_entry = self._reconfigure_config_entry
|
||||
assert config_entry is not None
|
||||
self.hass.config_entries.async_update_entry(
|
||||
config_entry, data=config_entry.data | updates
|
||||
)
|
||||
if schedule_reload:
|
||||
self.hass.config_entries.async_schedule_reload(config_entry.entry_id)
|
||||
|
||||
async def async_step_intent_reconfigure(
|
||||
@ -896,15 +893,63 @@ class ZWaveJSConfigFlow(ConfigFlow, domain=DOMAIN):
|
||||
# Now that the old controller is gone, we can scan for serial ports again
|
||||
return await self.async_step_choose_serial_port()
|
||||
|
||||
try:
|
||||
driver = self._get_driver()
|
||||
except AbortFlow:
|
||||
return self.async_abort(reason="config_entry_not_loaded")
|
||||
|
||||
@callback
|
||||
def set_driver_ready(event: dict) -> None:
|
||||
"Set the driver ready event."
|
||||
wait_driver_ready.set()
|
||||
|
||||
wait_driver_ready = asyncio.Event()
|
||||
|
||||
unsubscribe = driver.once("driver ready", set_driver_ready)
|
||||
|
||||
# reset the old controller
|
||||
try:
|
||||
await self._get_driver().async_hard_reset()
|
||||
except (AbortFlow, FailedCommand) as err:
|
||||
await driver.async_hard_reset()
|
||||
except FailedCommand as err:
|
||||
unsubscribe()
|
||||
_LOGGER.error("Failed to reset controller: %s", err)
|
||||
return self.async_abort(reason="reset_failed")
|
||||
|
||||
# Update the unique id of the config entry
|
||||
# to the new home id, which requires waiting for the driver
|
||||
# to be ready before getting the new home id.
|
||||
# If the backup restore, done later in the flow, fails,
|
||||
# the config entry unique id should be the new home id
|
||||
# after the controller reset.
|
||||
try:
|
||||
async with asyncio.timeout(DRIVER_READY_TIMEOUT):
|
||||
await wait_driver_ready.wait()
|
||||
except TimeoutError:
|
||||
pass
|
||||
finally:
|
||||
unsubscribe()
|
||||
|
||||
config_entry = self._reconfigure_config_entry
|
||||
assert config_entry is not None
|
||||
|
||||
try:
|
||||
version_info = await async_get_version_info(
|
||||
self.hass, config_entry.data[CONF_URL]
|
||||
)
|
||||
except CannotConnect:
|
||||
# Just log this error, as there's nothing to do about it here.
|
||||
# The stale unique id needs to be handled by a repair flow,
|
||||
# after the config entry has been reloaded, if the backup restore
|
||||
# also fails.
|
||||
_LOGGER.debug(
|
||||
"Failed to get server version, cannot update config entry "
|
||||
"unique id with new home id, after controller reset"
|
||||
)
|
||||
else:
|
||||
self.hass.config_entries.async_update_entry(
|
||||
config_entry, unique_id=str(version_info.home_id)
|
||||
)
|
||||
|
||||
# Unload the config entry before asking the user to unplug the controller.
|
||||
await self.hass.config_entries.async_unload(config_entry.entry_id)
|
||||
|
||||
@ -1154,14 +1199,17 @@ class ZWaveJSConfigFlow(ConfigFlow, domain=DOMAIN):
|
||||
assert ws_address is not None
|
||||
version_info = self.version_info
|
||||
assert version_info is not None
|
||||
config_entry = self._reconfigure_config_entry
|
||||
assert config_entry is not None
|
||||
|
||||
# We need to wait for the config entry to be reloaded,
|
||||
# before restoring the backup.
|
||||
# We will do this in the restore nvm progress task,
|
||||
# to get a nicer user experience.
|
||||
self._async_update_entry(
|
||||
{
|
||||
"unique_id": str(version_info.home_id),
|
||||
self.hass.config_entries.async_update_entry(
|
||||
config_entry,
|
||||
data={
|
||||
**config_entry.data,
|
||||
CONF_URL: ws_address,
|
||||
CONF_USB_PATH: self.usb_path,
|
||||
CONF_S0_LEGACY_KEY: self.s0_legacy_key,
|
||||
@ -1173,8 +1221,9 @@ class ZWaveJSConfigFlow(ConfigFlow, domain=DOMAIN):
|
||||
CONF_USE_ADDON: True,
|
||||
CONF_INTEGRATION_CREATED_ADDON: self.integration_created_addon,
|
||||
},
|
||||
schedule_reload=False,
|
||||
unique_id=str(version_info.home_id),
|
||||
)
|
||||
|
||||
return await self.async_step_restore_nvm()
|
||||
|
||||
async def async_step_finish_addon_setup_reconfigure(
|
||||
@ -1321,8 +1370,24 @@ class ZWaveJSConfigFlow(ConfigFlow, domain=DOMAIN):
|
||||
raise AbortFlow(f"Failed to restore network: {err}") from err
|
||||
else:
|
||||
with suppress(TimeoutError):
|
||||
async with asyncio.timeout(RESTORE_NVM_DRIVER_READY_TIMEOUT):
|
||||
async with asyncio.timeout(DRIVER_READY_TIMEOUT):
|
||||
await wait_driver_ready.wait()
|
||||
try:
|
||||
version_info = await async_get_version_info(
|
||||
self.hass, config_entry.data[CONF_URL]
|
||||
)
|
||||
except CannotConnect:
|
||||
# Just log this error, as there's nothing to do about it here.
|
||||
# The stale unique id needs to be handled by a repair flow,
|
||||
# after the config entry has been reloaded.
|
||||
_LOGGER.error(
|
||||
"Failed to get server version, cannot update config entry "
|
||||
"unique id with new home id, after controller reset"
|
||||
)
|
||||
else:
|
||||
self.hass.config_entries.async_update_entry(
|
||||
config_entry, unique_id=str(version_info.home_id)
|
||||
)
|
||||
await self.hass.config_entries.async_reload(config_entry.entry_id)
|
||||
finally:
|
||||
for unsub in unsubs:
|
||||
|
@ -204,4 +204,4 @@ COVER_TILT_PROPERTY_KEYS: set[str | int | None] = {
|
||||
|
||||
# Other constants
|
||||
|
||||
RESTORE_NVM_DRIVER_READY_TIMEOUT = 60
|
||||
DRIVER_READY_TIMEOUT = 60
|
||||
|
@ -5191,7 +5191,7 @@ async def test_hard_reset_controller(
|
||||
client.async_send_command.side_effect = async_send_command_no_driver_ready
|
||||
|
||||
with patch(
|
||||
"homeassistant.components.zwave_js.api.HARD_RESET_CONTROLLER_DRIVER_READY_TIMEOUT",
|
||||
"homeassistant.components.zwave_js.api.DRIVER_READY_TIMEOUT",
|
||||
new=0,
|
||||
):
|
||||
await ws_client.send_json_auto_id(
|
||||
@ -5663,7 +5663,7 @@ async def test_restore_nvm(
|
||||
client.async_send_command.side_effect = async_send_command_no_driver_ready
|
||||
|
||||
with patch(
|
||||
"homeassistant.components.zwave_js.api.RESTORE_NVM_DRIVER_READY_TIMEOUT",
|
||||
"homeassistant.components.zwave_js.api.DRIVER_READY_TIMEOUT",
|
||||
new=0,
|
||||
):
|
||||
# Send the subscription request
|
||||
|
@ -159,19 +159,6 @@ def mock_sdk_version(client: MagicMock) -> Generator[None]:
|
||||
client.driver.controller.data["sdkVersion"] = original_sdk_version
|
||||
|
||||
|
||||
@pytest.fixture(name="driver_ready_timeout")
|
||||
def mock_driver_ready_timeout() -> Generator[None]:
|
||||
"""Mock migration nvm restore driver ready timeout."""
|
||||
with patch(
|
||||
(
|
||||
"homeassistant.components.zwave_js.config_flow."
|
||||
"RESTORE_NVM_DRIVER_READY_TIMEOUT"
|
||||
),
|
||||
new=0,
|
||||
):
|
||||
yield
|
||||
|
||||
|
||||
async def test_manual(hass: HomeAssistant) -> None:
|
||||
"""Test we create an entry with manual step."""
|
||||
|
||||
@ -867,8 +854,11 @@ async def test_usb_discovery_migration(
|
||||
restart_addon: AsyncMock,
|
||||
client: MagicMock,
|
||||
integration: MockConfigEntry,
|
||||
get_server_version: AsyncMock,
|
||||
) -> None:
|
||||
"""Test usb discovery migration."""
|
||||
version_info = get_server_version.return_value
|
||||
version_info.home_id = 4321
|
||||
addon_options["device"] = "/dev/ttyUSB0"
|
||||
entry = integration
|
||||
assert client.connect.call_count == 1
|
||||
@ -893,6 +883,13 @@ async def test_usb_discovery_migration(
|
||||
side_effect=mock_backup_nvm_raw
|
||||
)
|
||||
|
||||
async def mock_reset_controller():
|
||||
client.driver.emit(
|
||||
"driver ready", {"event": "driver ready", "source": "driver"}
|
||||
)
|
||||
|
||||
client.driver.async_hard_reset = AsyncMock(side_effect=mock_reset_controller)
|
||||
|
||||
async def mock_restore_nvm(data: bytes):
|
||||
client.driver.controller.emit(
|
||||
"nvm convert progress",
|
||||
@ -944,6 +941,7 @@ async def test_usb_discovery_migration(
|
||||
|
||||
assert result["type"] is FlowResultType.FORM
|
||||
assert result["step_id"] == "instruct_unplug"
|
||||
assert entry.unique_id == "4321"
|
||||
|
||||
result = await hass.config_entries.flow.async_configure(result["flow_id"], {})
|
||||
|
||||
@ -958,6 +956,8 @@ async def test_usb_discovery_migration(
|
||||
|
||||
assert restart_addon.call_args == call("core_zwave_js")
|
||||
|
||||
version_info.home_id = 5678
|
||||
|
||||
result = await hass.config_entries.flow.async_configure(result["flow_id"])
|
||||
|
||||
assert result["type"] is FlowResultType.SHOW_PROGRESS
|
||||
@ -976,9 +976,10 @@ async def test_usb_discovery_migration(
|
||||
|
||||
assert result["type"] is FlowResultType.ABORT
|
||||
assert result["reason"] == "migration_successful"
|
||||
assert integration.data["url"] == "ws://host1:3001"
|
||||
assert integration.data["usb_path"] == USB_DISCOVERY_INFO.device
|
||||
assert integration.data["use_addon"] is True
|
||||
assert entry.data["url"] == "ws://host1:3001"
|
||||
assert entry.data["usb_path"] == USB_DISCOVERY_INFO.device
|
||||
assert entry.data["use_addon"] is True
|
||||
assert entry.unique_id == "5678"
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("supervisor", "addon_running", "get_addon_discovery_info")
|
||||
@ -995,10 +996,9 @@ async def test_usb_discovery_migration(
|
||||
]
|
||||
],
|
||||
)
|
||||
async def test_usb_discovery_migration_driver_ready_timeout(
|
||||
async def test_usb_discovery_migration_restore_driver_ready_timeout(
|
||||
hass: HomeAssistant,
|
||||
addon_options: dict[str, Any],
|
||||
driver_ready_timeout: None,
|
||||
mock_usb_serial_by_id: MagicMock,
|
||||
set_addon_options: AsyncMock,
|
||||
restart_addon: AsyncMock,
|
||||
@ -1030,6 +1030,13 @@ async def test_usb_discovery_migration_driver_ready_timeout(
|
||||
side_effect=mock_backup_nvm_raw
|
||||
)
|
||||
|
||||
async def mock_reset_controller():
|
||||
client.driver.emit(
|
||||
"driver ready", {"event": "driver ready", "source": "driver"}
|
||||
)
|
||||
|
||||
client.driver.async_hard_reset = AsyncMock(side_effect=mock_reset_controller)
|
||||
|
||||
async def mock_restore_nvm(data: bytes):
|
||||
client.driver.controller.emit(
|
||||
"nvm convert progress",
|
||||
@ -1092,6 +1099,10 @@ async def test_usb_discovery_migration_driver_ready_timeout(
|
||||
|
||||
assert restart_addon.call_args == call("core_zwave_js")
|
||||
|
||||
with patch(
|
||||
("homeassistant.components.zwave_js.config_flow.DRIVER_READY_TIMEOUT"),
|
||||
new=0,
|
||||
):
|
||||
result = await hass.config_entries.flow.async_configure(result["flow_id"])
|
||||
|
||||
assert result["type"] is FlowResultType.SHOW_PROGRESS
|
||||
@ -3662,6 +3673,20 @@ async def test_reconfigure_migrate_low_sdk_version(
|
||||
]
|
||||
],
|
||||
)
|
||||
@pytest.mark.parametrize(
|
||||
(
|
||||
"reset_server_version_side_effect",
|
||||
"reset_unique_id",
|
||||
"restore_server_version_side_effect",
|
||||
"final_unique_id",
|
||||
),
|
||||
[
|
||||
(None, "4321", None, "8765"),
|
||||
(aiohttp.ClientError("Boom"), "1234", None, "8765"),
|
||||
(None, "4321", aiohttp.ClientError("Boom"), "5678"),
|
||||
(aiohttp.ClientError("Boom"), "1234", aiohttp.ClientError("Boom"), "5678"),
|
||||
],
|
||||
)
|
||||
async def test_reconfigure_migrate_with_addon(
|
||||
hass: HomeAssistant,
|
||||
client,
|
||||
@ -3671,8 +3696,16 @@ async def test_reconfigure_migrate_with_addon(
|
||||
restart_addon,
|
||||
set_addon_options,
|
||||
get_addon_discovery_info,
|
||||
get_server_version: AsyncMock,
|
||||
reset_server_version_side_effect: Exception | None,
|
||||
reset_unique_id: str,
|
||||
restore_server_version_side_effect: Exception | None,
|
||||
final_unique_id: str,
|
||||
) -> None:
|
||||
"""Test migration flow with add-on."""
|
||||
get_server_version.side_effect = reset_server_version_side_effect
|
||||
version_info = get_server_version.return_value
|
||||
version_info.home_id = 4321
|
||||
entry = integration
|
||||
assert client.connect.call_count == 1
|
||||
hass.config_entries.async_update_entry(
|
||||
@ -3696,6 +3729,13 @@ async def test_reconfigure_migrate_with_addon(
|
||||
side_effect=mock_backup_nvm_raw
|
||||
)
|
||||
|
||||
async def mock_reset_controller():
|
||||
client.driver.emit(
|
||||
"driver ready", {"event": "driver ready", "source": "driver"}
|
||||
)
|
||||
|
||||
client.driver.async_hard_reset = AsyncMock(side_effect=mock_reset_controller)
|
||||
|
||||
async def mock_restore_nvm(data: bytes):
|
||||
client.driver.controller.emit(
|
||||
"nvm convert progress",
|
||||
@ -3746,6 +3786,175 @@ async def test_reconfigure_migrate_with_addon(
|
||||
assert result["type"] is FlowResultType.FORM
|
||||
assert result["step_id"] == "instruct_unplug"
|
||||
assert entry.state is config_entries.ConfigEntryState.NOT_LOADED
|
||||
assert entry.unique_id == reset_unique_id
|
||||
|
||||
result = await hass.config_entries.flow.async_configure(result["flow_id"], {})
|
||||
|
||||
assert result["type"] is FlowResultType.FORM
|
||||
assert result["step_id"] == "choose_serial_port"
|
||||
assert result["data_schema"].schema[CONF_USB_PATH]
|
||||
|
||||
# Reset side effect before starting the add-on.
|
||||
get_server_version.side_effect = None
|
||||
version_info.home_id = 5678
|
||||
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
user_input={
|
||||
CONF_USB_PATH: "/test",
|
||||
},
|
||||
)
|
||||
|
||||
assert result["type"] is FlowResultType.SHOW_PROGRESS
|
||||
assert result["step_id"] == "start_addon"
|
||||
assert set_addon_options.call_args == call(
|
||||
"core_zwave_js", AddonsOptions(config={"device": "/test"})
|
||||
)
|
||||
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert restart_addon.call_args == call("core_zwave_js")
|
||||
|
||||
result = await hass.config_entries.flow.async_configure(result["flow_id"])
|
||||
|
||||
assert entry.unique_id == "5678"
|
||||
get_server_version.side_effect = restore_server_version_side_effect
|
||||
version_info.home_id = 8765
|
||||
|
||||
assert result["type"] is FlowResultType.SHOW_PROGRESS
|
||||
assert result["step_id"] == "restore_nvm"
|
||||
assert client.connect.call_count == 2
|
||||
|
||||
await hass.async_block_till_done()
|
||||
assert client.connect.call_count == 3
|
||||
assert entry.state is config_entries.ConfigEntryState.LOADED
|
||||
assert client.driver.controller.async_restore_nvm.call_count == 1
|
||||
assert len(events) == 2
|
||||
assert events[0].data["progress"] == 0.25
|
||||
assert events[1].data["progress"] == 0.75
|
||||
|
||||
result = await hass.config_entries.flow.async_configure(result["flow_id"])
|
||||
|
||||
assert result["type"] is FlowResultType.ABORT
|
||||
assert result["reason"] == "migration_successful"
|
||||
assert entry.data["url"] == "ws://host1:3001"
|
||||
assert entry.data["usb_path"] == "/test"
|
||||
assert entry.data["use_addon"] is True
|
||||
assert entry.unique_id == final_unique_id
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"discovery_info",
|
||||
[
|
||||
[
|
||||
Discovery(
|
||||
addon="core_zwave_js",
|
||||
service="zwave_js",
|
||||
uuid=uuid4(),
|
||||
config=ADDON_DISCOVERY_INFO,
|
||||
)
|
||||
]
|
||||
],
|
||||
)
|
||||
async def test_reconfigure_migrate_reset_driver_ready_timeout(
|
||||
hass: HomeAssistant,
|
||||
client,
|
||||
supervisor,
|
||||
integration,
|
||||
addon_running,
|
||||
restart_addon,
|
||||
set_addon_options,
|
||||
get_addon_discovery_info,
|
||||
get_server_version: AsyncMock,
|
||||
) -> None:
|
||||
"""Test migration flow with driver ready timeout after controller reset."""
|
||||
version_info = get_server_version.return_value
|
||||
version_info.home_id = 4321
|
||||
entry = integration
|
||||
assert client.connect.call_count == 1
|
||||
hass.config_entries.async_update_entry(
|
||||
entry,
|
||||
unique_id="1234",
|
||||
data={
|
||||
"url": "ws://localhost:3000",
|
||||
"use_addon": True,
|
||||
"usb_path": "/dev/ttyUSB0",
|
||||
},
|
||||
)
|
||||
|
||||
async def mock_backup_nvm_raw():
|
||||
await asyncio.sleep(0)
|
||||
client.driver.controller.emit(
|
||||
"nvm backup progress", {"bytesRead": 100, "total": 200}
|
||||
)
|
||||
return b"test_nvm_data"
|
||||
|
||||
client.driver.controller.async_backup_nvm_raw = AsyncMock(
|
||||
side_effect=mock_backup_nvm_raw
|
||||
)
|
||||
|
||||
async def mock_reset_controller():
|
||||
await asyncio.sleep(0)
|
||||
|
||||
client.driver.async_hard_reset = AsyncMock(side_effect=mock_reset_controller)
|
||||
|
||||
async def mock_restore_nvm(data: bytes):
|
||||
client.driver.controller.emit(
|
||||
"nvm convert progress",
|
||||
{"event": "nvm convert progress", "bytesRead": 100, "total": 200},
|
||||
)
|
||||
await asyncio.sleep(0)
|
||||
client.driver.controller.emit(
|
||||
"nvm restore progress",
|
||||
{"event": "nvm restore progress", "bytesWritten": 100, "total": 200},
|
||||
)
|
||||
client.driver.emit(
|
||||
"driver ready", {"event": "driver ready", "source": "driver"}
|
||||
)
|
||||
|
||||
client.driver.controller.async_restore_nvm = AsyncMock(side_effect=mock_restore_nvm)
|
||||
|
||||
events = async_capture_events(
|
||||
hass, data_entry_flow.EVENT_DATA_ENTRY_FLOW_PROGRESS_UPDATE
|
||||
)
|
||||
|
||||
result = await entry.start_reconfigure_flow(hass)
|
||||
|
||||
assert result["type"] is FlowResultType.MENU
|
||||
assert result["step_id"] == "reconfigure"
|
||||
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"], {"next_step_id": "intent_migrate"}
|
||||
)
|
||||
|
||||
assert result["type"] is FlowResultType.FORM
|
||||
assert result["step_id"] == "intent_migrate"
|
||||
|
||||
with (
|
||||
patch(
|
||||
("homeassistant.components.zwave_js.config_flow.DRIVER_READY_TIMEOUT"),
|
||||
new=0,
|
||||
),
|
||||
patch("pathlib.Path.write_bytes") as mock_file,
|
||||
):
|
||||
result = await hass.config_entries.flow.async_configure(result["flow_id"], {})
|
||||
|
||||
assert result["type"] is FlowResultType.SHOW_PROGRESS
|
||||
assert result["step_id"] == "backup_nvm"
|
||||
|
||||
await hass.async_block_till_done()
|
||||
assert client.driver.controller.async_backup_nvm_raw.call_count == 1
|
||||
assert mock_file.call_count == 1
|
||||
assert len(events) == 1
|
||||
assert events[0].data["progress"] == 0.5
|
||||
events.clear()
|
||||
|
||||
result = await hass.config_entries.flow.async_configure(result["flow_id"])
|
||||
|
||||
assert result["type"] is FlowResultType.FORM
|
||||
assert result["step_id"] == "instruct_unplug"
|
||||
assert entry.state is config_entries.ConfigEntryState.NOT_LOADED
|
||||
assert entry.unique_id == "4321"
|
||||
|
||||
result = await hass.config_entries.flow.async_configure(result["flow_id"], {})
|
||||
|
||||
@ -3770,6 +3979,8 @@ async def test_reconfigure_migrate_with_addon(
|
||||
|
||||
assert restart_addon.call_args == call("core_zwave_js")
|
||||
|
||||
version_info.home_id = 5678
|
||||
|
||||
result = await hass.config_entries.flow.async_configure(result["flow_id"])
|
||||
|
||||
assert result["type"] is FlowResultType.SHOW_PROGRESS
|
||||
@ -3788,9 +3999,10 @@ async def test_reconfigure_migrate_with_addon(
|
||||
|
||||
assert result["type"] is FlowResultType.ABORT
|
||||
assert result["reason"] == "migration_successful"
|
||||
assert integration.data["url"] == "ws://host1:3001"
|
||||
assert integration.data["usb_path"] == "/test"
|
||||
assert integration.data["use_addon"] is True
|
||||
assert entry.data["url"] == "ws://host1:3001"
|
||||
assert entry.data["usb_path"] == "/test"
|
||||
assert entry.data["use_addon"] is True
|
||||
assert entry.unique_id == "5678"
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
@ -3806,13 +4018,12 @@ async def test_reconfigure_migrate_with_addon(
|
||||
]
|
||||
],
|
||||
)
|
||||
async def test_reconfigure_migrate_driver_ready_timeout(
|
||||
async def test_reconfigure_migrate_restore_driver_ready_timeout(
|
||||
hass: HomeAssistant,
|
||||
client,
|
||||
supervisor,
|
||||
integration,
|
||||
addon_running,
|
||||
driver_ready_timeout: None,
|
||||
restart_addon,
|
||||
set_addon_options,
|
||||
get_addon_discovery_info,
|
||||
@ -3841,6 +4052,13 @@ async def test_reconfigure_migrate_driver_ready_timeout(
|
||||
side_effect=mock_backup_nvm_raw
|
||||
)
|
||||
|
||||
async def mock_reset_controller():
|
||||
client.driver.emit(
|
||||
"driver ready", {"event": "driver ready", "source": "driver"}
|
||||
)
|
||||
|
||||
client.driver.async_hard_reset = AsyncMock(side_effect=mock_reset_controller)
|
||||
|
||||
async def mock_restore_nvm(data: bytes):
|
||||
client.driver.controller.emit(
|
||||
"nvm convert progress",
|
||||
@ -3912,6 +4130,10 @@ async def test_reconfigure_migrate_driver_ready_timeout(
|
||||
|
||||
assert restart_addon.call_args == call("core_zwave_js")
|
||||
|
||||
with patch(
|
||||
("homeassistant.components.zwave_js.config_flow.DRIVER_READY_TIMEOUT"),
|
||||
new=0,
|
||||
):
|
||||
result = await hass.config_entries.flow.async_configure(result["flow_id"])
|
||||
|
||||
assert result["type"] is FlowResultType.SHOW_PROGRESS
|
||||
@ -4045,10 +4267,14 @@ async def test_reconfigure_migrate_start_addon_failure(
|
||||
client.driver.controller.async_backup_nvm_raw = AsyncMock(
|
||||
side_effect=mock_backup_nvm_raw
|
||||
)
|
||||
client.driver.controller.async_restore_nvm = AsyncMock(
|
||||
side_effect=FailedCommand("test_error", "unknown_error")
|
||||
|
||||
async def mock_reset_controller():
|
||||
client.driver.emit(
|
||||
"driver ready", {"event": "driver ready", "source": "driver"}
|
||||
)
|
||||
|
||||
client.driver.async_hard_reset = AsyncMock(side_effect=mock_reset_controller)
|
||||
|
||||
result = await entry.start_reconfigure_flow(hass)
|
||||
|
||||
assert result["type"] is FlowResultType.MENU
|
||||
@ -4140,6 +4366,13 @@ async def test_reconfigure_migrate_restore_failure(
|
||||
client.driver.controller.async_backup_nvm_raw = AsyncMock(
|
||||
side_effect=mock_backup_nvm_raw
|
||||
)
|
||||
|
||||
async def mock_reset_controller():
|
||||
client.driver.emit(
|
||||
"driver ready", {"event": "driver ready", "source": "driver"}
|
||||
)
|
||||
|
||||
client.driver.async_hard_reset = AsyncMock(side_effect=mock_reset_controller)
|
||||
client.driver.controller.async_restore_nvm = AsyncMock(
|
||||
side_effect=FailedCommand("test_error", "unknown_error")
|
||||
)
|
||||
@ -4292,7 +4525,7 @@ async def test_get_driver_failure_instruct_unplug(
|
||||
result = await hass.config_entries.flow.async_configure(result["flow_id"])
|
||||
|
||||
assert result["type"] is FlowResultType.ABORT
|
||||
assert result["reason"] == "reset_failed"
|
||||
assert result["reason"] == "config_entry_not_loaded"
|
||||
|
||||
|
||||
async def test_hard_reset_failure(hass: HomeAssistant, integration, client) -> None:
|
||||
@ -4358,6 +4591,13 @@ async def test_choose_serial_port_usb_ports_failure(
|
||||
side_effect=mock_backup_nvm_raw
|
||||
)
|
||||
|
||||
async def mock_reset_controller():
|
||||
client.driver.emit(
|
||||
"driver ready", {"event": "driver ready", "source": "driver"}
|
||||
)
|
||||
|
||||
client.driver.async_hard_reset = AsyncMock(side_effect=mock_reset_controller)
|
||||
|
||||
result = await entry.start_reconfigure_flow(hass)
|
||||
|
||||
assert result["type"] is FlowResultType.MENU
|
||||
|
Loading…
x
Reference in New Issue
Block a user