Make proper Z-Wave reconfigure flow (#143549)

* Make proper Z-Wave reconfigure flow

* Improve backup_failed string
This commit is contained in:
Martin Hjelmare 2025-04-25 13:19:03 +02:00 committed by GitHub
parent ff2c901930
commit 7c584ece23
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 338 additions and 358 deletions

View File

@ -2,7 +2,6 @@
from __future__ import annotations from __future__ import annotations
from abc import ABC, abstractmethod
import asyncio import asyncio
from datetime import datetime from datetime import datetime
import logging import logging
@ -26,12 +25,9 @@ from homeassistant.components.hassio import (
) )
from homeassistant.config_entries import ( from homeassistant.config_entries import (
SOURCE_USB, SOURCE_USB,
ConfigEntry,
ConfigEntryBaseFlow,
ConfigEntryState, ConfigEntryState,
ConfigFlow, ConfigFlow,
ConfigFlowResult, ConfigFlowResult,
OptionsFlow,
) )
from homeassistant.const import CONF_NAME, CONF_URL from homeassistant.const import CONF_NAME, CONF_URL
from homeassistant.core import HomeAssistant, callback from homeassistant.core import HomeAssistant, callback
@ -177,8 +173,12 @@ async def async_get_usb_ports(hass: HomeAssistant) -> dict[str, str]:
return await hass.async_add_executor_job(get_usb_ports) return await hass.async_add_executor_job(get_usb_ports)
class BaseZwaveJSFlow(ConfigEntryBaseFlow, ABC): class ZWaveJSConfigFlow(ConfigFlow, domain=DOMAIN):
"""Represent the base config flow for Z-Wave JS.""" """Handle a config flow for Z-Wave JS."""
VERSION = 1
_title: str
def __init__(self) -> None: def __init__(self) -> None:
"""Set up flow instance.""" """Set up flow instance."""
@ -196,6 +196,15 @@ class BaseZwaveJSFlow(ConfigEntryBaseFlow, ABC):
self.install_task: asyncio.Task | None = None self.install_task: asyncio.Task | None = None
self.start_task: asyncio.Task | None = None self.start_task: asyncio.Task | None = None
self.version_info: VersionInfo | None = None self.version_info: VersionInfo | None = None
self.original_addon_config: dict[str, Any] | 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
self.backup_filepath: str | None = None
self.use_addon = False
self._reconfiguring = False
self._usb_discovery = False
async def async_step_install_addon( async def async_step_install_addon(
self, user_input: dict[str, Any] | None = None self, user_input: dict[str, Any] | None = None
@ -257,6 +266,8 @@ class BaseZwaveJSFlow(ConfigEntryBaseFlow, ABC):
self, user_input: dict[str, Any] | None = None self, user_input: dict[str, Any] | None = None
) -> ConfigFlowResult: ) -> ConfigFlowResult:
"""Add-on start failed.""" """Add-on start failed."""
if self._reconfiguring:
return await self.async_step_start_failed_reconfigure()
return self.async_abort(reason="addon_start_failed") return self.async_abort(reason="addon_start_failed")
async def _async_start_addon(self) -> None: async def _async_start_addon(self) -> None:
@ -290,13 +301,14 @@ class BaseZwaveJSFlow(ConfigEntryBaseFlow, ABC):
else: else:
raise CannotConnect("Failed to start Z-Wave JS add-on: timeout") raise CannotConnect("Failed to start Z-Wave JS add-on: timeout")
@abstractmethod
async def async_step_configure_addon( async def async_step_configure_addon(
self, user_input: dict[str, Any] | None = None self, user_input: dict[str, Any] | None = None
) -> ConfigFlowResult: ) -> ConfigFlowResult:
"""Ask for config for Z-Wave JS add-on.""" """Ask for config for Z-Wave JS add-on."""
if self._reconfiguring:
return await self.async_step_configure_addon_reconfigure(user_input)
return await self.async_step_configure_addon_user(user_input)
@abstractmethod
async def async_step_finish_addon_setup( async def async_step_finish_addon_setup(
self, user_input: dict[str, Any] | None = None self, user_input: dict[str, Any] | None = None
) -> ConfigFlowResult: ) -> ConfigFlowResult:
@ -305,6 +317,9 @@ class BaseZwaveJSFlow(ConfigEntryBaseFlow, ABC):
Get add-on discovery info and server version info. Get add-on discovery info and server version info.
Set unique id and abort if already configured. Set unique id and abort if already configured.
""" """
if self._reconfiguring:
return await self.async_step_finish_addon_setup_reconfigure(user_input)
return await self.async_step_finish_addon_setup_user(user_input)
async def _async_get_addon_info(self) -> AddonInfo: async def _async_get_addon_info(self) -> AddonInfo:
"""Return and cache Z-Wave JS add-on info.""" """Return and cache Z-Wave JS add-on info."""
@ -342,28 +357,6 @@ class BaseZwaveJSFlow(ConfigEntryBaseFlow, ABC):
return discovery_info_config return discovery_info_config
class ZWaveJSConfigFlow(BaseZwaveJSFlow, ConfigFlow, domain=DOMAIN):
"""Handle a config flow for Z-Wave JS."""
VERSION = 1
_title: str
def __init__(self) -> None:
"""Set up flow instance."""
super().__init__()
self.use_addon = False
self._usb_discovery = False
@staticmethod
@callback
def async_get_options_flow(
config_entry: ConfigEntry,
) -> OptionsFlowHandler:
"""Return the options flow."""
return OptionsFlowHandler()
async def async_step_user( async def async_step_user(
self, user_input: dict[str, Any] | None = None self, user_input: dict[str, Any] | None = None
) -> ConfigFlowResult: ) -> ConfigFlowResult:
@ -373,6 +366,19 @@ class ZWaveJSConfigFlow(BaseZwaveJSFlow, ConfigFlow, domain=DOMAIN):
return await self.async_step_manual() return await self.async_step_manual()
async def async_step_reconfigure(
self, user_input: dict[str, Any] | None = None
) -> ConfigFlowResult:
"""Confirm if we are migrating adapters or just re-configuring."""
self._reconfiguring = True
return self.async_show_menu(
step_id="reconfigure",
menu_options=[
OPTIONS_INTENT_RECONFIGURE,
OPTIONS_INTENT_MIGRATE,
],
)
async def async_step_zeroconf( async def async_step_zeroconf(
self, discovery_info: ZeroconfServiceInfo self, discovery_info: ZeroconfServiceInfo
) -> ConfigFlowResult: ) -> ConfigFlowResult:
@ -568,14 +574,14 @@ class ZWaveJSConfigFlow(BaseZwaveJSFlow, ConfigFlow, domain=DOMAIN):
self.lr_s2_authenticated_key = addon_config.get( self.lr_s2_authenticated_key = addon_config.get(
CONF_ADDON_LR_S2_AUTHENTICATED_KEY, "" CONF_ADDON_LR_S2_AUTHENTICATED_KEY, ""
) )
return await self.async_step_finish_addon_setup() return await self.async_step_finish_addon_setup_user()
if addon_info.state == AddonState.NOT_RUNNING: if addon_info.state == AddonState.NOT_RUNNING:
return await self.async_step_configure_addon() return await self.async_step_configure_addon_user()
return await self.async_step_install_addon() return await self.async_step_install_addon()
async def async_step_configure_addon( async def async_step_configure_addon_user(
self, user_input: dict[str, Any] | None = None self, user_input: dict[str, Any] | None = None
) -> ConfigFlowResult: ) -> ConfigFlowResult:
"""Ask for config for Z-Wave JS add-on.""" """Ask for config for Z-Wave JS add-on."""
@ -661,7 +667,7 @@ class ZWaveJSConfigFlow(BaseZwaveJSFlow, ConfigFlow, domain=DOMAIN):
return self.async_show_form(step_id="configure_addon", data_schema=data_schema) return self.async_show_form(step_id="configure_addon", data_schema=data_schema)
async def async_step_finish_addon_setup( async def async_step_finish_addon_setup_user(
self, user_input: dict[str, Any] | None = None self, user_input: dict[str, Any] | None = None
) -> ConfigFlowResult: ) -> ConfigFlowResult:
"""Prepare info needed to complete the config entry. """Prepare info needed to complete the config entry.
@ -723,35 +729,11 @@ class ZWaveJSConfigFlow(BaseZwaveJSFlow, ConfigFlow, domain=DOMAIN):
}, },
) )
class OptionsFlowHandler(BaseZwaveJSFlow, OptionsFlow):
"""Handle an options flow for Z-Wave JS."""
def __init__(self) -> None:
"""Set up the options flow."""
super().__init__()
self.original_addon_config: dict[str, Any] | 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
self.backup_filepath: str | None = None
@callback @callback
def _async_update_entry(self, data: dict[str, Any]) -> None: def _async_update_entry(self, data: dict[str, Any]) -> None:
"""Update the config entry with new data.""" """Update the config entry with new data."""
self.hass.config_entries.async_update_entry(self.config_entry, data=data) self.hass.config_entries.async_update_entry(
self._get_reconfigure_entry(), data=data
async def async_step_init(
self, user_input: dict[str, Any] | None = None
) -> ConfigFlowResult:
"""Confirm if we are migrating adapters or just re-configuring."""
return self.async_show_menu(
step_id="init",
menu_options=[
OPTIONS_INTENT_RECONFIGURE,
OPTIONS_INTENT_MIGRATE,
],
) )
async def async_step_intent_reconfigure( async def async_step_intent_reconfigure(
@ -759,15 +741,15 @@ class OptionsFlowHandler(BaseZwaveJSFlow, OptionsFlow):
) -> ConfigFlowResult: ) -> ConfigFlowResult:
"""Manage the options.""" """Manage the options."""
if is_hassio(self.hass): if is_hassio(self.hass):
return await self.async_step_on_supervisor() return await self.async_step_on_supervisor_reconfigure()
return await self.async_step_manual() return await self.async_step_manual_reconfigure()
async def async_step_intent_migrate( async def async_step_intent_migrate(
self, user_input: dict[str, Any] | None = None self, user_input: dict[str, Any] | None = None
) -> ConfigFlowResult: ) -> ConfigFlowResult:
"""Confirm the user wants to reset their current controller.""" """Confirm the user wants to reset their current controller."""
if not self.config_entry.data.get(CONF_USE_ADDON): if not self._get_reconfigure_entry().data.get(CONF_USE_ADDON):
return self.async_abort(reason="addon_required") return self.async_abort(reason="addon_required")
if user_input is not None: if user_input is not None:
@ -837,7 +819,7 @@ class OptionsFlowHandler(BaseZwaveJSFlow, OptionsFlow):
# reset the old controller # reset the old controller
try: try:
await self._get_driver().async_hard_reset() await self._get_driver().async_hard_reset()
except FailedCommand as err: except (AbortFlow, FailedCommand) as err:
_LOGGER.error("Failed to reset controller: %s", err) _LOGGER.error("Failed to reset controller: %s", err)
return self.async_abort(reason="reset_failed") return self.async_abort(reason="reset_failed")
@ -848,16 +830,15 @@ class OptionsFlowHandler(BaseZwaveJSFlow, OptionsFlow):
}, },
) )
async def async_step_manual( async def async_step_manual_reconfigure(
self, user_input: dict[str, Any] | None = None self, user_input: dict[str, Any] | None = None
) -> ConfigFlowResult: ) -> ConfigFlowResult:
"""Handle a manual configuration.""" """Handle a manual configuration."""
config_entry = self._get_reconfigure_entry()
if user_input is None: if user_input is None:
return self.async_show_form( return self.async_show_form(
step_id="manual", step_id="manual_reconfigure",
data_schema=get_manual_schema( data_schema=get_manual_schema({CONF_URL: config_entry.data[CONF_URL]}),
{CONF_URL: self.config_entry.data[CONF_URL]}
),
) )
errors = {} errors = {}
@ -870,43 +851,46 @@ class OptionsFlowHandler(BaseZwaveJSFlow, OptionsFlow):
_LOGGER.exception("Unexpected exception") _LOGGER.exception("Unexpected exception")
errors["base"] = "unknown" errors["base"] = "unknown"
else: else:
if self.config_entry.unique_id != str(version_info.home_id): if config_entry.unique_id != str(version_info.home_id):
return self.async_abort(reason="different_device") return self.async_abort(reason="different_device")
# Make sure we disable any add-on handling # Make sure we disable any add-on handling
# if the controller is reconfigured in a manual step. # if the controller is reconfigured in a manual step.
self._async_update_entry( self._async_update_entry(
{ {
**self.config_entry.data, **config_entry.data,
**user_input, **user_input,
CONF_USE_ADDON: False, CONF_USE_ADDON: False,
CONF_INTEGRATION_CREATED_ADDON: False, CONF_INTEGRATION_CREATED_ADDON: False,
} }
) )
self.hass.config_entries.async_schedule_reload(self.config_entry.entry_id) self.hass.config_entries.async_schedule_reload(config_entry.entry_id)
return self.async_create_entry(title=TITLE, data={}) return self.async_abort(reason="reconfigure_successful")
return self.async_show_form( return self.async_show_form(
step_id="manual", data_schema=get_manual_schema(user_input), errors=errors step_id="manual_reconfigure",
data_schema=get_manual_schema(user_input),
errors=errors,
) )
async def async_step_on_supervisor( async def async_step_on_supervisor_reconfigure(
self, user_input: dict[str, Any] | None = None self, user_input: dict[str, Any] | None = None
) -> ConfigFlowResult: ) -> ConfigFlowResult:
"""Handle logic when on Supervisor host.""" """Handle logic when on Supervisor host."""
config_entry = self._get_reconfigure_entry()
if user_input is None: if user_input is None:
return self.async_show_form( return self.async_show_form(
step_id="on_supervisor", step_id="on_supervisor_reconfigure",
data_schema=get_on_supervisor_schema( data_schema=get_on_supervisor_schema(
{CONF_USE_ADDON: self.config_entry.data.get(CONF_USE_ADDON, True)} {CONF_USE_ADDON: config_entry.data.get(CONF_USE_ADDON, True)}
), ),
) )
if not user_input[CONF_USE_ADDON]: if not user_input[CONF_USE_ADDON]:
if self.config_entry.data.get(CONF_USE_ADDON): if config_entry.data.get(CONF_USE_ADDON):
# Unload the config entry before stopping the add-on. # Unload the config entry before stopping the add-on.
await self.hass.config_entries.async_unload(self.config_entry.entry_id) await self.hass.config_entries.async_unload(config_entry.entry_id)
addon_manager = get_addon_manager(self.hass) addon_manager = get_addon_manager(self.hass)
_LOGGER.debug("Stopping Z-Wave JS add-on") _LOGGER.debug("Stopping Z-Wave JS add-on")
try: try:
@ -914,22 +898,23 @@ class OptionsFlowHandler(BaseZwaveJSFlow, OptionsFlow):
except AddonError as err: except AddonError as err:
_LOGGER.error(err) _LOGGER.error(err)
self.hass.config_entries.async_schedule_reload( self.hass.config_entries.async_schedule_reload(
self.config_entry.entry_id config_entry.entry_id
) )
raise AbortFlow("addon_stop_failed") from err raise AbortFlow("addon_stop_failed") from err
return await self.async_step_manual() return await self.async_step_manual_reconfigure()
addon_info = await self._async_get_addon_info() addon_info = await self._async_get_addon_info()
if addon_info.state == AddonState.NOT_INSTALLED: if addon_info.state == AddonState.NOT_INSTALLED:
return await self.async_step_install_addon() return await self.async_step_install_addon()
return await self.async_step_configure_addon() return await self.async_step_configure_addon_reconfigure()
async def async_step_configure_addon( async def async_step_configure_addon_reconfigure(
self, user_input: dict[str, Any] | None = None self, user_input: dict[str, Any] | None = None
) -> ConfigFlowResult: ) -> ConfigFlowResult:
"""Ask for config for Z-Wave JS add-on.""" """Ask for config for Z-Wave JS add-on."""
config_entry = self._get_reconfigure_entry()
addon_info = await self._async_get_addon_info() addon_info = await self._async_get_addon_info()
addon_config = addon_info.options addon_config = addon_info.options
@ -967,11 +952,11 @@ class OptionsFlowHandler(BaseZwaveJSFlow, OptionsFlow):
await self._async_set_addon_config(new_addon_config) await self._async_set_addon_config(new_addon_config)
if addon_info.state == AddonState.RUNNING and not self.restart_addon: if addon_info.state == AddonState.RUNNING and not self.restart_addon:
return await self.async_step_finish_addon_setup() return await self.async_step_finish_addon_setup_reconfigure()
if self.config_entry.data.get(CONF_USE_ADDON): if config_entry.data.get(CONF_USE_ADDON):
# Disconnect integration before restarting add-on. # Disconnect integration before restarting add-on.
await self.hass.config_entries.async_unload(self.config_entry.entry_id) await self.hass.config_entries.async_unload(config_entry.entry_id)
return await self.async_step_start_addon() return await self.async_step_start_addon()
@ -1065,7 +1050,7 @@ class OptionsFlowHandler(BaseZwaveJSFlow, OptionsFlow):
step_id="choose_serial_port", data_schema=data_schema step_id="choose_serial_port", data_schema=data_schema
) )
async def async_step_start_failed( async def async_step_start_failed_reconfigure(
self, user_input: dict[str, Any] | None = None self, user_input: dict[str, Any] | None = None
) -> ConfigFlowResult: ) -> ConfigFlowResult:
"""Add-on start failed.""" """Add-on start failed."""
@ -1087,9 +1072,9 @@ class OptionsFlowHandler(BaseZwaveJSFlow, OptionsFlow):
self, user_input: dict[str, Any] | None = None self, user_input: dict[str, Any] | None = None
) -> ConfigFlowResult: ) -> ConfigFlowResult:
"""Migration done.""" """Migration done."""
return self.async_create_entry(title=TITLE, data={}) return self.async_abort(reason="migration_successful")
async def async_step_finish_addon_setup( async def async_step_finish_addon_setup_reconfigure(
self, user_input: dict[str, Any] | None = None self, user_input: dict[str, Any] | None = None
) -> ConfigFlowResult: ) -> ConfigFlowResult:
"""Prepare info needed to complete the config entry update. """Prepare info needed to complete the config entry update.
@ -1097,6 +1082,7 @@ class OptionsFlowHandler(BaseZwaveJSFlow, OptionsFlow):
Get add-on discovery info and server version info. Get add-on discovery info and server version info.
Check for same unique id and abort if not the same unique id. Check for same unique id and abort if not the same unique id.
""" """
config_entry = self._get_reconfigure_entry()
if self.revert_reason: if self.revert_reason:
self.original_addon_config = None self.original_addon_config = None
reason = self.revert_reason reason = self.revert_reason
@ -1115,14 +1101,14 @@ class OptionsFlowHandler(BaseZwaveJSFlow, OptionsFlow):
except CannotConnect: except CannotConnect:
return await self.async_revert_addon_config(reason="cannot_connect") return await self.async_revert_addon_config(reason="cannot_connect")
if self.backup_data is None and self.config_entry.unique_id != str( if self.backup_data is None and config_entry.unique_id != str(
self.version_info.home_id self.version_info.home_id
): ):
return await self.async_revert_addon_config(reason="different_device") return await self.async_revert_addon_config(reason="different_device")
self._async_update_entry( self._async_update_entry(
{ {
**self.config_entry.data, **config_entry.data,
# this will only be different in a migration flow # this will only be different in a migration flow
"unique_id": str(self.version_info.home_id), "unique_id": str(self.version_info.home_id),
CONF_URL: self.ws_address, CONF_URL: self.ws_address,
@ -1141,8 +1127,8 @@ class OptionsFlowHandler(BaseZwaveJSFlow, OptionsFlow):
return await self.async_step_restore_nvm() return await self.async_step_restore_nvm()
# Always reload entry since we may have disconnected the client. # Always reload entry since we may have disconnected the client.
self.hass.config_entries.async_schedule_reload(self.config_entry.entry_id) self.hass.config_entries.async_schedule_reload(config_entry.entry_id)
return self.async_create_entry(title=TITLE, data={}) return self.async_abort(reason="reconfigure_successful")
async def async_revert_addon_config(self, reason: str) -> ConfigFlowResult: async def async_revert_addon_config(self, reason: str) -> ConfigFlowResult:
"""Abort the options flow. """Abort the options flow.
@ -1157,7 +1143,9 @@ class OptionsFlowHandler(BaseZwaveJSFlow, OptionsFlow):
) )
if self.revert_reason or not self.original_addon_config: if self.revert_reason or not self.original_addon_config:
self.hass.config_entries.async_schedule_reload(self.config_entry.entry_id) self.hass.config_entries.async_schedule_reload(
self._get_reconfigure_entry().entry_id
)
return self.async_abort(reason=reason) return self.async_abort(reason=reason)
self.revert_reason = reason self.revert_reason = reason
@ -1167,7 +1155,7 @@ class OptionsFlowHandler(BaseZwaveJSFlow, OptionsFlow):
if addon_key in ADDON_USER_INPUT_MAP if addon_key in ADDON_USER_INPUT_MAP
} }
_LOGGER.debug("Reverting add-on options, reason: %s", reason) _LOGGER.debug("Reverting add-on options, reason: %s", reason)
return await self.async_step_configure_addon(addon_config_input) return await self.async_step_configure_addon_reconfigure(addon_config_input)
async def _async_backup_network(self) -> None: async def _async_backup_network(self) -> None:
"""Backup the current network.""" """Backup the current network."""
@ -1203,7 +1191,9 @@ class OptionsFlowHandler(BaseZwaveJSFlow, OptionsFlow):
assert self.backup_data is not None assert self.backup_data is not None
# Reload the config entry to reconnect the client after the addon restart # Reload the config entry to reconnect the client after the addon restart
await self.hass.config_entries.async_reload(self.config_entry.entry_id) await self.hass.config_entries.async_reload(
self._get_reconfigure_entry().entry_id
)
@callback @callback
def forward_progress(event: dict) -> None: def forward_progress(event: dict) -> None:
@ -1231,9 +1221,11 @@ class OptionsFlowHandler(BaseZwaveJSFlow, OptionsFlow):
unsub() unsub()
def _get_driver(self) -> Driver: def _get_driver(self) -> Driver:
if self.config_entry.state != ConfigEntryState.LOADED: """Get the driver from the config entry."""
config_entry = self._get_reconfigure_entry()
if config_entry.state != ConfigEntryState.LOADED:
raise AbortFlow("Configuration entry is not loaded") raise AbortFlow("Configuration entry is not loaded")
client: Client = self.config_entry.runtime_data[DATA_CLIENT] client: Client = config_entry.runtime_data[DATA_CLIENT]
assert client.driver is not None assert client.driver is not None
return client.driver return client.driver

View File

@ -4,17 +4,22 @@
"addon_get_discovery_info_failed": "Failed to get Z-Wave add-on discovery info.", "addon_get_discovery_info_failed": "Failed to get Z-Wave add-on discovery info.",
"addon_info_failed": "Failed to get Z-Wave add-on info.", "addon_info_failed": "Failed to get Z-Wave add-on info.",
"addon_install_failed": "Failed to install the Z-Wave add-on.", "addon_install_failed": "Failed to install the Z-Wave add-on.",
"addon_required": "The Z-Wave migration flow requires the integration to be configured using the Z-Wave Supervisor add-on. You can still use the Backup and Restore buttons to migrate your network manually.",
"addon_set_config_failed": "Failed to set Z-Wave configuration.", "addon_set_config_failed": "Failed to set Z-Wave configuration.",
"addon_start_failed": "Failed to start the Z-Wave add-on.", "addon_start_failed": "Failed to start the Z-Wave add-on.",
"addon_stop_failed": "Failed to stop the Z-Wave add-on.",
"already_configured": "[%key:common::config_flow::abort::already_configured_device%]", "already_configured": "[%key:common::config_flow::abort::already_configured_device%]",
"already_in_progress": "[%key:common::config_flow::abort::already_in_progress%]", "already_in_progress": "[%key:common::config_flow::abort::already_in_progress%]",
"backup_failed": "Failed to back up network.",
"cannot_connect": "[%key:common::config_flow::error::cannot_connect%]", "cannot_connect": "[%key:common::config_flow::error::cannot_connect%]",
"different_device": "The connected USB device is not the same as previously configured for this config entry. Please instead create a new config entry for the new device.",
"discovery_requires_supervisor": "Discovery requires the supervisor.", "discovery_requires_supervisor": "Discovery requires the supervisor.",
"migration_successful": "Migration successful.",
"not_zwave_device": "Discovered device is not a Z-Wave device.", "not_zwave_device": "Discovered device is not a Z-Wave device.",
"not_zwave_js_addon": "Discovered add-on is not the official Z-Wave add-on.", "not_zwave_js_addon": "Discovered add-on is not the official Z-Wave add-on.",
"backup_failed": "Failed to backup network.", "reconfigure_successful": "[%key:common::config_flow::abort::reconfigure_successful%]",
"restore_failed": "Failed to restore network.",
"reset_failed": "Failed to reset controller.", "reset_failed": "Failed to reset controller.",
"restore_failed": "Failed to restore network.",
"usb_ports_failed": "Failed to get USB devices." "usb_ports_failed": "Failed to get USB devices."
}, },
"error": { "error": {
@ -42,6 +47,19 @@
"description": "The add-on will generate security keys if those fields are left empty.", "description": "The add-on will generate security keys if those fields are left empty.",
"title": "Enter the Z-Wave add-on configuration" "title": "Enter the Z-Wave add-on configuration"
}, },
"configure_addon_reconfigure": {
"data": {
"emulate_hardware": "Emulate Hardware",
"log_level": "Log level",
"s0_legacy_key": "[%key:component::zwave_js::config::step::configure_addon::data::s0_legacy_key%]",
"s2_access_control_key": "[%key:component::zwave_js::config::step::configure_addon::data::s2_access_control_key%]",
"s2_authenticated_key": "[%key:component::zwave_js::config::step::configure_addon::data::s2_authenticated_key%]",
"s2_unauthenticated_key": "[%key:component::zwave_js::config::step::configure_addon::data::s2_unauthenticated_key%]",
"usb_path": "[%key:common::config_flow::data::usb_path%]"
},
"description": "[%key:component::zwave_js::config::step::configure_addon::description%]",
"title": "[%key:component::zwave_js::config::step::configure_addon::title%]"
},
"hassio_confirm": { "hassio_confirm": {
"title": "Set up Z-Wave integration with the Z-Wave add-on" "title": "Set up Z-Wave integration with the Z-Wave add-on"
}, },
@ -53,6 +71,11 @@
"url": "[%key:common::config_flow::data::url%]" "url": "[%key:common::config_flow::data::url%]"
} }
}, },
"manual_reconfigure": {
"data": {
"url": "[%key:common::config_flow::data::url%]"
}
},
"on_supervisor": { "on_supervisor": {
"data": { "data": {
"use_addon": "Use the Z-Wave Supervisor add-on" "use_addon": "Use the Z-Wave Supervisor add-on"
@ -60,6 +83,13 @@
"description": "Do you want to use the Z-Wave Supervisor add-on?", "description": "Do you want to use the Z-Wave Supervisor add-on?",
"title": "Select connection method" "title": "Select connection method"
}, },
"on_supervisor_reconfigure": {
"data": {
"use_addon": "[%key:component::zwave_js::config::step::on_supervisor::data::use_addon%]"
},
"description": "[%key:component::zwave_js::config::step::on_supervisor::description%]",
"title": "[%key:component::zwave_js::config::step::on_supervisor::title%]"
},
"start_addon": { "start_addon": {
"title": "The Z-Wave add-on is starting." "title": "The Z-Wave add-on is starting."
}, },
@ -69,6 +99,28 @@
"zeroconf_confirm": { "zeroconf_confirm": {
"description": "Do you want to add the Z-Wave Server with home ID {home_id} found at {url} to Home Assistant?", "description": "Do you want to add the Z-Wave Server with home ID {home_id} found at {url} to Home Assistant?",
"title": "Discovered Z-Wave Server" "title": "Discovered Z-Wave Server"
},
"reconfigure": {
"title": "Migrate or re-configure",
"description": "Are you migrating to a new controller or re-configuring the current controller?",
"menu_options": {
"intent_migrate": "Migrate to a new controller",
"intent_reconfigure": "Re-configure the current controller"
}
},
"intent_migrate": {
"title": "[%key:component::zwave_js::config::step::reconfigure::menu_options::intent_migrate%]",
"description": "Before setting up your new controller, your old controller needs to be reset. A backup will be performed first.\n\nDo you wish to continue?"
},
"instruct_unplug": {
"title": "Unplug your old controller",
"description": "Backup saved to \"{file_path}\"\n\nYour old controller has been reset. If the hardware is no longer needed, you can now unplug it.\n\nPlease make sure your new controller is plugged in before continuing."
},
"choose_serial_port": {
"data": {
"usb_path": "[%key:common::config_flow::data::usb_path%]"
},
"title": "Select your Z-Wave device"
} }
} }
}, },
@ -213,90 +265,6 @@
"title": "Newer version of Z-Wave Server needed" "title": "Newer version of Z-Wave Server needed"
} }
}, },
"options": {
"abort": {
"addon_get_discovery_info_failed": "[%key:component::zwave_js::config::abort::addon_get_discovery_info_failed%]",
"addon_info_failed": "[%key:component::zwave_js::config::abort::addon_info_failed%]",
"addon_install_failed": "[%key:component::zwave_js::config::abort::addon_install_failed%]",
"addon_set_config_failed": "[%key:component::zwave_js::config::abort::addon_set_config_failed%]",
"addon_start_failed": "[%key:component::zwave_js::config::abort::addon_start_failed%]",
"addon_stop_failed": "Failed to stop the Z-Wave add-on.",
"already_configured": "[%key:common::config_flow::abort::already_configured_device%]",
"cannot_connect": "[%key:common::config_flow::error::cannot_connect%]",
"different_device": "The connected USB device is not the same as previously configured for this config entry. Please instead create a new config entry for the new device.",
"addon_required": "The Z-Wave migration flow requires the integration to be configured using the Z-Wave Supervisor add-on. You can still use the Backup and Restore buttons to migrate your network manually.",
"backup_failed": "[%key:component::zwave_js::config::abort::backup_failed%]",
"restore_failed": "[%key:component::zwave_js::config::abort::restore_failed%]",
"reset_failed": "[%key:component::zwave_js::config::abort::reset_failed%]",
"usb_ports_failed": "[%key:component::zwave_js::config::abort::usb_ports_failed%]"
},
"error": {
"cannot_connect": "[%key:common::config_flow::error::cannot_connect%]",
"invalid_ws_url": "[%key:component::zwave_js::config::error::invalid_ws_url%]",
"unknown": "[%key:common::config_flow::error::unknown%]"
},
"progress": {
"install_addon": "[%key:component::zwave_js::config::progress::install_addon%]",
"start_addon": "[%key:component::zwave_js::config::progress::start_addon%]",
"backup_nvm": "[%key:component::zwave_js::config::progress::backup_nvm%]",
"restore_nvm": "[%key:component::zwave_js::config::progress::restore_nvm%]"
},
"step": {
"init": {
"title": "Migrate or re-configure",
"description": "Are you migrating to a new controller or re-configuring the current controller?",
"menu_options": {
"intent_migrate": "Migrate to a new controller",
"intent_reconfigure": "Re-configure the current controller"
}
},
"intent_migrate": {
"title": "[%key:component::zwave_js::options::step::init::menu_options::intent_migrate%]",
"description": "Before setting up your new controller, your old controller needs to be reset. A backup will be performed first.\n\nDo you wish to continue?"
},
"instruct_unplug": {
"title": "Unplug your old controller",
"description": "Backup saved to \"{file_path}\"\n\nYour old controller has been reset. If the hardware is no longer needed, you can now unplug it.\n\nPlease make sure your new controller is plugged in before continuing."
},
"configure_addon": {
"data": {
"emulate_hardware": "Emulate Hardware",
"log_level": "Log level",
"s0_legacy_key": "[%key:component::zwave_js::config::step::configure_addon::data::s0_legacy_key%]",
"s2_access_control_key": "[%key:component::zwave_js::config::step::configure_addon::data::s2_access_control_key%]",
"s2_authenticated_key": "[%key:component::zwave_js::config::step::configure_addon::data::s2_authenticated_key%]",
"s2_unauthenticated_key": "[%key:component::zwave_js::config::step::configure_addon::data::s2_unauthenticated_key%]",
"usb_path": "[%key:common::config_flow::data::usb_path%]"
},
"description": "[%key:component::zwave_js::config::step::configure_addon::description%]",
"title": "[%key:component::zwave_js::config::step::configure_addon::title%]"
},
"choose_serial_port": {
"data": {
"usb_path": "[%key:common::config_flow::data::usb_path%]"
},
"title": "Select your Z-Wave device"
},
"install_addon": {
"title": "[%key:component::zwave_js::config::step::install_addon::title%]"
},
"manual": {
"data": {
"url": "[%key:common::config_flow::data::url%]"
}
},
"on_supervisor": {
"data": {
"use_addon": "[%key:component::zwave_js::config::step::on_supervisor::data::use_addon%]"
},
"description": "[%key:component::zwave_js::config::step::on_supervisor::description%]",
"title": "[%key:component::zwave_js::config::step::on_supervisor::title%]"
},
"start_addon": {
"title": "[%key:component::zwave_js::config::step::start_addon::title%]"
}
}
},
"services": { "services": {
"bulk_set_partial_config_parameters": { "bulk_set_partial_config_parameters": {
"description": "Allows for bulk setting partial parameters. Useful when multiple partial parameters have to be set at the same time.", "description": "Allows for bulk setting partial parameters. Useful when multiple partial parameters have to be set at the same time.",

File diff suppressed because it is too large Load Diff