Apply Z-Wave JS add-on config reverts directly (#179899)

Co-authored-by: Paulus Schoutsen <balloob@gmail.com>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Balloob Bot
2026-08-23 19:26:09 +02:00
committed by GitHub
co-authored by Paulus Schoutsen Claude Fable 5
parent 41a9ea753b
commit 2c6f11befc
2 changed files with 25 additions and 59 deletions
@@ -149,11 +149,6 @@ class SecurityKeys:
}
ADDON_USER_INPUT_MAP = {
CONF_ADDON_DEVICE: CONF_USB_PATH,
CONF_ADDON_SOCKET: CONF_SOCKET_PATH,
} | {field.name: field.name for field in fields(SecurityKeys)}
CONF_ADDON_RF_REGION = "rf_region"
EXAMPLE_SERVER_URL = "ws://localhost:3000"
@@ -403,7 +398,6 @@ class ZWaveJSConfigFlow(ConfigFlow, domain=DOMAIN):
self.install_task: asyncio.Task | None = None
self.start_task: asyncio.Task | None = None
self.version_info: VersionInfo | None = None
self.revert_reason: str | None = None
self.backup_task: asyncio.Task | None = None
self.restore_backup_task: asyncio.Task | None = None
self.backup_data: bytes | None = None
@@ -1462,8 +1456,7 @@ class ZWaveJSConfigFlow(ConfigFlow, domain=DOMAIN):
if user_input is not None:
# Missing keys default to the current add-on config, so
# existing keys are preserved. The revert helper always passes
# all keys, so the defaults never apply while reverting.
# existing keys are preserved.
self.security_keys = default_keys.updated_from_user_input(user_input)
self.usb_path = user_input.get(CONF_USB_PATH) or None
self.socket_path = user_input.get(CONF_SOCKET_PATH) or None
@@ -1646,11 +1639,6 @@ class ZWaveJSConfigFlow(ConfigFlow, domain=DOMAIN):
"""
config_entry = self._reconfigure_config_entry
assert config_entry is not None
if self.revert_reason:
self._addon_setup.original_config = None
reason = self.revert_reason
self.revert_reason = None
return await self.async_revert_addon_config(reason=reason)
if not self.ws_address:
discovery_info = await self._addon_setup.async_get_addon_discovery_info()
@@ -1801,32 +1789,20 @@ class ZWaveJSConfigFlow(ConfigFlow, domain=DOMAIN):
return await self.async_step_installation_type()
async def async_revert_addon_config(self, reason: str) -> ConfigFlowResult:
"""Abort the options flow.
"""Abort the flow.
If the add-on options have been changed, revert those and restart add-on.
"""
# If reverting the add-on options failed, abort immediately.
if self.revert_reason:
_LOGGER.error(
"Failed to revert add-on options before aborting flow, reason: %s",
reason,
)
if self.revert_reason or not self._addon_setup.original_config:
config_entry = self._reconfigure_config_entry
assert config_entry is not None
_LOGGER.debug("Reverting add-on options, reason: %s", reason)
if (original_config := self._addon_setup.original_config) is None:
self._async_schedule_entry_reload()
return self.async_abort(reason=reason)
self.revert_reason = reason
original_config = self._addon_setup.original_config
addon_config_input = SecurityKeys.from_config(original_config).to_dict() | {
ADDON_USER_INPUT_MAP[addon_key]: addon_val
for addon_key, addon_val in original_config.items()
if addon_key in (CONF_ADDON_DEVICE, CONF_ADDON_SOCKET)
}
_LOGGER.debug("Reverting app options, reason: %s", reason)
return await self.async_step_configure_addon_reconfigure(addon_config_input)
else:
# Clear the abandoned-flow recovery state, so async_remove
# doesn't restore the add-on config a second time.
self._addon_setup.original_config = None
self._entry_unloaded_by_flow = False
await self._async_restore_addon_config_and_reload(original_config)
return self.async_abort(reason=reason)
async def _async_backup_network(self) -> None:
"""Backup the current network."""
+14 -24
View File
@@ -4595,8 +4595,6 @@ async def different_device_server_version(*args):
"s2_access_control_key": "old456",
"s2_authenticated_key": "old789",
"s2_unauthenticated_key": "old987",
"lr_s2_access_control_key": "",
"lr_s2_authenticated_key": "",
},
0,
different_device_server_version,
@@ -4673,17 +4671,8 @@ async def test_reconfigure_different_device(
assert set_addon_options.call_args == call(
"core_zwave_js", AddonsOptions(config=revert_addon_options)
)
assert result["type"] is FlowResultType.SHOW_PROGRESS
assert result["step_id"] == "start_addon"
await hass.async_block_till_done()
assert restart_addon.call_count == 2
assert restart_addon.call_args == call("core_zwave_js")
result = await hass.config_entries.flow.async_configure(result["flow_id"])
await hass.async_block_till_done()
assert result["type"] is FlowResultType.ABORT
assert result["reason"] == "different_device"
assert entry.data == data
@@ -4700,9 +4689,11 @@ async def test_reconfigure_different_device(
"new_addon_options",
"disconnect_calls",
"restart_addon_side_effect",
"final_connect_calls",
"final_disconnect_calls",
),
[
(
pytest.param(
{},
{
"device": "/test",
@@ -4734,8 +4725,11 @@ async def test_reconfigure_different_device(
},
0,
[SupervisorError(), None],
2,
1,
id="revert_restart_success",
),
(
pytest.param(
{},
{
"device": "/test",
@@ -4770,6 +4764,9 @@ async def test_reconfigure_different_device(
SupervisorError(),
SupervisorError(),
],
1,
0,
id="revert_restart_failed",
),
],
)
@@ -4785,6 +4782,8 @@ async def test_reconfigure_addon_restart_failed(
form_data: dict[str, Any],
new_addon_options: dict[str, Any],
disconnect_calls: int,
final_connect_calls: int,
final_disconnect_calls: int,
) -> None:
"""Test reconfigure flow and add-on restart failure."""
addon_options.update(old_addon_options)
@@ -4843,22 +4842,13 @@ async def test_reconfigure_addon_restart_failed(
assert set_addon_options.call_args == call(
"core_zwave_js", AddonsOptions(config=old_addon_options)
)
assert result["type"] is FlowResultType.SHOW_PROGRESS
assert result["step_id"] == "start_addon"
await hass.async_block_till_done()
assert restart_addon.call_count == 2
assert restart_addon.call_args == call("core_zwave_js")
result = await hass.config_entries.flow.async_configure(result["flow_id"])
await hass.async_block_till_done()
assert result["type"] is FlowResultType.ABORT
assert result["reason"] == "addon_start_failed"
assert entry.data == data
assert client.connect.call_count == 2
assert client.disconnect.call_count == 1
assert client.connect.call_count == final_connect_calls
assert client.disconnect.call_count == final_disconnect_calls
@pytest.mark.usefixtures("supervisor", "addon_running", "restart_addon")