diff --git a/homeassistant/components/zwave_js/config_flow.py b/homeassistant/components/zwave_js/config_flow.py index 1877658ce42..64590a69a77 100644 --- a/homeassistant/components/zwave_js/config_flow.py +++ b/homeassistant/components/zwave_js/config_flow.py @@ -2,7 +2,6 @@ from __future__ import annotations -from abc import ABC, abstractmethod import asyncio from datetime import datetime import logging @@ -26,12 +25,9 @@ from homeassistant.components.hassio import ( ) from homeassistant.config_entries import ( SOURCE_USB, - ConfigEntry, - ConfigEntryBaseFlow, ConfigEntryState, ConfigFlow, ConfigFlowResult, - OptionsFlow, ) from homeassistant.const import CONF_NAME, CONF_URL 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) -class BaseZwaveJSFlow(ConfigEntryBaseFlow, ABC): - """Represent the base config flow for Z-Wave JS.""" +class ZWaveJSConfigFlow(ConfigFlow, domain=DOMAIN): + """Handle a config flow for Z-Wave JS.""" + + VERSION = 1 + + _title: str def __init__(self) -> None: """Set up flow instance.""" @@ -196,6 +196,15 @@ class BaseZwaveJSFlow(ConfigEntryBaseFlow, ABC): self.install_task: asyncio.Task | None = None self.start_task: asyncio.Task | 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( self, user_input: dict[str, Any] | None = None @@ -257,6 +266,8 @@ class BaseZwaveJSFlow(ConfigEntryBaseFlow, ABC): self, user_input: dict[str, Any] | None = None ) -> ConfigFlowResult: """Add-on start failed.""" + if self._reconfiguring: + return await self.async_step_start_failed_reconfigure() return self.async_abort(reason="addon_start_failed") async def _async_start_addon(self) -> None: @@ -290,13 +301,14 @@ class BaseZwaveJSFlow(ConfigEntryBaseFlow, ABC): else: raise CannotConnect("Failed to start Z-Wave JS add-on: timeout") - @abstractmethod async def async_step_configure_addon( self, user_input: dict[str, Any] | None = None ) -> ConfigFlowResult: """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( self, user_input: dict[str, Any] | None = None ) -> ConfigFlowResult: @@ -305,6 +317,9 @@ class BaseZwaveJSFlow(ConfigEntryBaseFlow, ABC): Get add-on discovery info and server version info. 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: """Return and cache Z-Wave JS add-on info.""" @@ -342,28 +357,6 @@ class BaseZwaveJSFlow(ConfigEntryBaseFlow, ABC): 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( self, user_input: dict[str, Any] | None = None ) -> ConfigFlowResult: @@ -373,6 +366,19 @@ class ZWaveJSConfigFlow(BaseZwaveJSFlow, ConfigFlow, domain=DOMAIN): 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( self, discovery_info: ZeroconfServiceInfo ) -> ConfigFlowResult: @@ -568,14 +574,14 @@ class ZWaveJSConfigFlow(BaseZwaveJSFlow, ConfigFlow, domain=DOMAIN): self.lr_s2_authenticated_key = addon_config.get( 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: - return await self.async_step_configure_addon() + return await self.async_step_configure_addon_user() 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 ) -> ConfigFlowResult: """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) - async def async_step_finish_addon_setup( + async def async_step_finish_addon_setup_user( self, user_input: dict[str, Any] | None = None ) -> ConfigFlowResult: """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 def _async_update_entry(self, data: dict[str, Any]) -> None: """Update the config entry with new data.""" - self.hass.config_entries.async_update_entry(self.config_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, - ], + self.hass.config_entries.async_update_entry( + self._get_reconfigure_entry(), data=data ) async def async_step_intent_reconfigure( @@ -759,15 +741,15 @@ class OptionsFlowHandler(BaseZwaveJSFlow, OptionsFlow): ) -> ConfigFlowResult: """Manage the options.""" 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( self, user_input: dict[str, Any] | None = None ) -> ConfigFlowResult: """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") if user_input is not None: @@ -837,7 +819,7 @@ class OptionsFlowHandler(BaseZwaveJSFlow, OptionsFlow): # reset the old controller try: await self._get_driver().async_hard_reset() - except FailedCommand as err: + except (AbortFlow, FailedCommand) as err: _LOGGER.error("Failed to reset controller: %s", err) 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 ) -> ConfigFlowResult: """Handle a manual configuration.""" + config_entry = self._get_reconfigure_entry() if user_input is None: return self.async_show_form( - step_id="manual", - data_schema=get_manual_schema( - {CONF_URL: self.config_entry.data[CONF_URL]} - ), + step_id="manual_reconfigure", + data_schema=get_manual_schema({CONF_URL: config_entry.data[CONF_URL]}), ) errors = {} @@ -870,43 +851,46 @@ class OptionsFlowHandler(BaseZwaveJSFlow, OptionsFlow): _LOGGER.exception("Unexpected exception") errors["base"] = "unknown" 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") # Make sure we disable any add-on handling # if the controller is reconfigured in a manual step. self._async_update_entry( { - **self.config_entry.data, + **config_entry.data, **user_input, CONF_USE_ADDON: False, CONF_INTEGRATION_CREATED_ADDON: False, } ) - self.hass.config_entries.async_schedule_reload(self.config_entry.entry_id) - return self.async_create_entry(title=TITLE, data={}) + self.hass.config_entries.async_schedule_reload(config_entry.entry_id) + return self.async_abort(reason="reconfigure_successful") 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 ) -> ConfigFlowResult: """Handle logic when on Supervisor host.""" + config_entry = self._get_reconfigure_entry() if user_input is None: return self.async_show_form( - step_id="on_supervisor", + step_id="on_supervisor_reconfigure", 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 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. - 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) _LOGGER.debug("Stopping Z-Wave JS add-on") try: @@ -914,22 +898,23 @@ class OptionsFlowHandler(BaseZwaveJSFlow, OptionsFlow): except AddonError as err: _LOGGER.error(err) self.hass.config_entries.async_schedule_reload( - self.config_entry.entry_id + config_entry.entry_id ) 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() if addon_info.state == AddonState.NOT_INSTALLED: 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 ) -> ConfigFlowResult: """Ask for config for Z-Wave JS add-on.""" + config_entry = self._get_reconfigure_entry() addon_info = await self._async_get_addon_info() addon_config = addon_info.options @@ -967,11 +952,11 @@ class OptionsFlowHandler(BaseZwaveJSFlow, OptionsFlow): await self._async_set_addon_config(new_addon_config) 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. - 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() @@ -1065,7 +1050,7 @@ class OptionsFlowHandler(BaseZwaveJSFlow, OptionsFlow): 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 ) -> ConfigFlowResult: """Add-on start failed.""" @@ -1087,9 +1072,9 @@ class OptionsFlowHandler(BaseZwaveJSFlow, OptionsFlow): self, user_input: dict[str, Any] | None = None ) -> ConfigFlowResult: """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 ) -> ConfigFlowResult: """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. Check for same unique id and abort if not the same unique id. """ + config_entry = self._get_reconfigure_entry() if self.revert_reason: self.original_addon_config = None reason = self.revert_reason @@ -1115,14 +1101,14 @@ class OptionsFlowHandler(BaseZwaveJSFlow, OptionsFlow): except CannotConnect: 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 ): return await self.async_revert_addon_config(reason="different_device") self._async_update_entry( { - **self.config_entry.data, + **config_entry.data, # this will only be different in a migration flow "unique_id": str(self.version_info.home_id), CONF_URL: self.ws_address, @@ -1141,8 +1127,8 @@ class OptionsFlowHandler(BaseZwaveJSFlow, OptionsFlow): return await self.async_step_restore_nvm() # Always reload entry since we may have disconnected the client. - self.hass.config_entries.async_schedule_reload(self.config_entry.entry_id) - return self.async_create_entry(title=TITLE, data={}) + self.hass.config_entries.async_schedule_reload(config_entry.entry_id) + return self.async_abort(reason="reconfigure_successful") async def async_revert_addon_config(self, reason: str) -> ConfigFlowResult: """Abort the options flow. @@ -1157,7 +1143,9 @@ class OptionsFlowHandler(BaseZwaveJSFlow, OptionsFlow): ) 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) self.revert_reason = reason @@ -1167,7 +1155,7 @@ class OptionsFlowHandler(BaseZwaveJSFlow, OptionsFlow): if addon_key in ADDON_USER_INPUT_MAP } _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: """Backup the current network.""" @@ -1203,7 +1191,9 @@ class OptionsFlowHandler(BaseZwaveJSFlow, OptionsFlow): assert self.backup_data is not None # 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 def forward_progress(event: dict) -> None: @@ -1231,9 +1221,11 @@ class OptionsFlowHandler(BaseZwaveJSFlow, OptionsFlow): unsub() 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") - client: Client = self.config_entry.runtime_data[DATA_CLIENT] + client: Client = config_entry.runtime_data[DATA_CLIENT] assert client.driver is not None return client.driver diff --git a/homeassistant/components/zwave_js/strings.json b/homeassistant/components/zwave_js/strings.json index 8f445beaf23..d287c7b073a 100644 --- a/homeassistant/components/zwave_js/strings.json +++ b/homeassistant/components/zwave_js/strings.json @@ -4,17 +4,22 @@ "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_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_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_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%]", + "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.", + "migration_successful": "Migration successful.", "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.", - "backup_failed": "Failed to backup network.", - "restore_failed": "Failed to restore network.", + "reconfigure_successful": "[%key:common::config_flow::abort::reconfigure_successful%]", "reset_failed": "Failed to reset controller.", + "restore_failed": "Failed to restore network.", "usb_ports_failed": "Failed to get USB devices." }, "error": { @@ -42,6 +47,19 @@ "description": "The add-on will generate security keys if those fields are left empty.", "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": { "title": "Set up Z-Wave integration with the Z-Wave add-on" }, @@ -53,6 +71,11 @@ "url": "[%key:common::config_flow::data::url%]" } }, + "manual_reconfigure": { + "data": { + "url": "[%key:common::config_flow::data::url%]" + } + }, "on_supervisor": { "data": { "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?", "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": { "title": "The Z-Wave add-on is starting." }, @@ -69,6 +99,28 @@ "zeroconf_confirm": { "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" + }, + "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" } }, - "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": { "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.", diff --git a/tests/components/zwave_js/test_config_flow.py b/tests/components/zwave_js/test_config_flow.py index aaa7353882c..c6b38f39053 100644 --- a/tests/components/zwave_js/test_config_flow.py +++ b/tests/components/zwave_js/test_config_flow.py @@ -17,11 +17,7 @@ from zwave_js_server.exceptions import FailedCommand from zwave_js_server.version import VersionInfo from homeassistant import config_entries, data_entry_flow -from homeassistant.components.zwave_js.config_flow import ( - SERVER_VERSION_TIMEOUT, - TITLE, - OptionsFlowHandler, -) +from homeassistant.components.zwave_js.config_flow import SERVER_VERSION_TIMEOUT, TITLE from homeassistant.components.zwave_js.const import ADDON_SLUG, CONF_USB_PATH, DOMAIN from homeassistant.core import HomeAssistant from homeassistant.data_entry_flow import FlowResultType @@ -299,29 +295,30 @@ async def test_manual_errors(hass: HomeAssistant, integration, url, error) -> No ), ], ) -async def test_manual_errors_options_flow( +async def test_reconfigure_manual_errors( hass: HomeAssistant, integration, url, error ) -> None: - """Test all errors with a manual set up.""" - result = await hass.config_entries.options.async_init(integration.entry_id) + """Test all errors with a manual set up in a reconfigure flow.""" + entry = integration + result = await entry.start_reconfigure_flow(hass) assert result["type"] is FlowResultType.MENU - assert result["step_id"] == "init" - result = await hass.config_entries.options.async_configure( + assert result["step_id"] == "reconfigure" + result = await hass.config_entries.flow.async_configure( result["flow_id"], {"next_step_id": "intent_reconfigure"} ) assert result["type"] is FlowResultType.FORM - assert result["step_id"] == "manual" + assert result["step_id"] == "manual_reconfigure" - result = await hass.config_entries.options.async_configure( + result = await hass.config_entries.flow.async_configure( result["flow_id"], { "url": url, }, ) - assert result["step_id"] == "manual" + assert result["step_id"] == "manual_reconfigure" assert result["errors"] == {"base": error} @@ -2027,32 +2024,33 @@ async def test_install_addon_failure( assert result["reason"] == "addon_install_failed" -async def test_options_manual(hass: HomeAssistant, client, integration) -> None: - """Test manual settings in options flow.""" +async def test_reconfigure_manual(hass: HomeAssistant, client, integration) -> None: + """Test manual settings in reconfigure flow.""" entry = integration hass.config_entries.async_update_entry(entry, unique_id="1234") assert client.connect.call_count == 1 assert client.disconnect.call_count == 0 - result = await hass.config_entries.options.async_init(entry.entry_id) + result = await entry.start_reconfigure_flow(hass) assert result["type"] is FlowResultType.MENU - assert result["step_id"] == "init" + assert result["step_id"] == "reconfigure" - result = await hass.config_entries.options.async_configure( + result = await hass.config_entries.flow.async_configure( result["flow_id"], {"next_step_id": "intent_reconfigure"} ) assert result["type"] is FlowResultType.FORM - assert result["step_id"] == "manual" + assert result["step_id"] == "manual_reconfigure" - result = await hass.config_entries.options.async_configure( + result = await hass.config_entries.flow.async_configure( result["flow_id"], {"url": "ws://1.1.1.1:3001"} ) await hass.async_block_till_done() - assert result["type"] is FlowResultType.CREATE_ENTRY + assert result["type"] is FlowResultType.ABORT + assert result["reason"] == "reconfigure_successful" assert entry.data["url"] == "ws://1.1.1.1:3001" assert entry.data["use_addon"] is False assert entry.data["integration_created_addon"] is False @@ -2060,26 +2058,26 @@ async def test_options_manual(hass: HomeAssistant, client, integration) -> None: assert client.disconnect.call_count == 1 -async def test_options_manual_different_device( +async def test_reconfigure_manual_different_device( hass: HomeAssistant, integration ) -> None: - """Test options flow manual step connecting to different device.""" + """Test reconfigure flow manual step connecting to different device.""" entry = integration hass.config_entries.async_update_entry(entry, unique_id="5678") - result = await hass.config_entries.options.async_init(entry.entry_id) + result = await entry.start_reconfigure_flow(hass) assert result["type"] is FlowResultType.MENU - assert result["step_id"] == "init" + assert result["step_id"] == "reconfigure" - result = await hass.config_entries.options.async_configure( + result = await hass.config_entries.flow.async_configure( result["flow_id"], {"next_step_id": "intent_reconfigure"} ) assert result["type"] is FlowResultType.FORM - assert result["step_id"] == "manual" + assert result["step_id"] == "manual_reconfigure" - result = await hass.config_entries.options.async_configure( + result = await hass.config_entries.flow.async_configure( result["flow_id"], {"url": "ws://1.1.1.1:3001"} ) await hass.async_block_till_done() @@ -2088,36 +2086,36 @@ async def test_options_manual_different_device( assert result["reason"] == "different_device" -async def test_options_not_addon( +async def test_reconfigure_not_addon( hass: HomeAssistant, client, supervisor, integration ) -> None: - """Test options flow and opting out of add-on on Supervisor.""" + """Test reconfigure flow and opting out of add-on on Supervisor.""" entry = integration hass.config_entries.async_update_entry(entry, unique_id="1234") assert client.connect.call_count == 1 assert client.disconnect.call_count == 0 - result = await hass.config_entries.options.async_init(entry.entry_id) + result = await entry.start_reconfigure_flow(hass) assert result["type"] is FlowResultType.MENU - assert result["step_id"] == "init" + assert result["step_id"] == "reconfigure" - result = await hass.config_entries.options.async_configure( + result = await hass.config_entries.flow.async_configure( result["flow_id"], {"next_step_id": "intent_reconfigure"} ) assert result["type"] is FlowResultType.FORM - assert result["step_id"] == "on_supervisor" + assert result["step_id"] == "on_supervisor_reconfigure" - result = await hass.config_entries.options.async_configure( + result = await hass.config_entries.flow.async_configure( result["flow_id"], {"use_addon": False} ) assert result["type"] is FlowResultType.FORM - assert result["step_id"] == "manual" + assert result["step_id"] == "manual_reconfigure" - result = await hass.config_entries.options.async_configure( + result = await hass.config_entries.flow.async_configure( result["flow_id"], { "url": "ws://localhost:3000", @@ -2125,7 +2123,8 @@ async def test_options_not_addon( ) await hass.async_block_till_done() - assert result["type"] is FlowResultType.CREATE_ENTRY + assert result["type"] is FlowResultType.ABORT + assert result["reason"] == "reconfigure_successful" assert entry.data["url"] == "ws://localhost:3000" assert entry.data["use_addon"] is False assert entry.data["integration_created_addon"] is False @@ -2134,14 +2133,14 @@ async def test_options_not_addon( @pytest.mark.usefixtures("supervisor") -async def test_options_not_addon_with_addon( +async def test_reconfigure_not_addon_with_addon( hass: HomeAssistant, setup_entry: AsyncMock, unload_entry: AsyncMock, integration: MockConfigEntry, stop_addon: AsyncMock, ) -> None: - """Test options flow opting out of add-on on Supervisor with add-on.""" + """Test reconfigure flow opting out of add-on on Supervisor with add-on.""" entry = integration hass.config_entries.async_update_entry( entry, @@ -2153,19 +2152,19 @@ async def test_options_not_addon_with_addon( assert unload_entry.call_count == 0 setup_entry.reset_mock() - result = await hass.config_entries.options.async_init(entry.entry_id) + result = await entry.start_reconfigure_flow(hass) assert result["type"] is FlowResultType.MENU - assert result["step_id"] == "init" + assert result["step_id"] == "reconfigure" - result = await hass.config_entries.options.async_configure( + result = await hass.config_entries.flow.async_configure( result["flow_id"], {"next_step_id": "intent_reconfigure"} ) assert result["type"] is FlowResultType.FORM - assert result["step_id"] == "on_supervisor" + assert result["step_id"] == "on_supervisor_reconfigure" - result = await hass.config_entries.options.async_configure( + result = await hass.config_entries.flow.async_configure( result["flow_id"], {"use_addon": False} ) @@ -2176,9 +2175,9 @@ async def test_options_not_addon_with_addon( assert stop_addon.call_args == call("core_zwave_js") assert result["type"] is FlowResultType.FORM - assert result["step_id"] == "manual" + assert result["step_id"] == "manual_reconfigure" - result = await hass.config_entries.options.async_configure( + result = await hass.config_entries.flow.async_configure( result["flow_id"], { "url": "ws://localhost:3000", @@ -2186,7 +2185,8 @@ async def test_options_not_addon_with_addon( ) await hass.async_block_till_done() - assert result["type"] is FlowResultType.CREATE_ENTRY + assert result["type"] is FlowResultType.ABORT + assert result["reason"] == "reconfigure_successful" assert entry.data["url"] == "ws://localhost:3000" assert entry.data["use_addon"] is False assert entry.data["integration_created_addon"] is False @@ -2200,14 +2200,14 @@ async def test_options_not_addon_with_addon( @pytest.mark.usefixtures("supervisor") -async def test_options_not_addon_with_addon_stop_fail( +async def test_reconfigure_not_addon_with_addon_stop_fail( hass: HomeAssistant, setup_entry: AsyncMock, unload_entry: AsyncMock, integration: MockConfigEntry, stop_addon: AsyncMock, ) -> None: - """Test options flow opting out of add-on and add-on stop error.""" + """Test reconfigure flow opting out of add-on and add-on stop error.""" stop_addon.side_effect = SupervisorError("Boom!") entry = integration hass.config_entries.async_update_entry( @@ -2220,19 +2220,19 @@ async def test_options_not_addon_with_addon_stop_fail( assert unload_entry.call_count == 0 setup_entry.reset_mock() - result = await hass.config_entries.options.async_init(entry.entry_id) + result = await entry.start_reconfigure_flow(hass) assert result["type"] is FlowResultType.MENU - assert result["step_id"] == "init" + assert result["step_id"] == "reconfigure" - result = await hass.config_entries.options.async_configure( + result = await hass.config_entries.flow.async_configure( result["flow_id"], {"next_step_id": "intent_reconfigure"} ) assert result["type"] is FlowResultType.FORM - assert result["step_id"] == "on_supervisor" + assert result["step_id"] == "on_supervisor_reconfigure" - result = await hass.config_entries.options.async_configure( + result = await hass.config_entries.flow.async_configure( result["flow_id"], {"use_addon": False} ) await hass.async_block_till_done() @@ -2330,7 +2330,7 @@ async def test_options_not_addon_with_addon_stop_fail( ), ], ) -async def test_options_addon_running( +async def test_reconfigure_addon_running( hass: HomeAssistant, client, supervisor, @@ -2346,7 +2346,7 @@ async def test_options_addon_running( new_addon_options, disconnect_calls, ) -> None: - """Test options flow and add-on already running on Supervisor.""" + """Test reconfigure flow and add-on already running on Supervisor.""" addon_options.update(old_addon_options) entry = integration data = {**entry.data, **entry_data} @@ -2357,26 +2357,26 @@ async def test_options_addon_running( assert client.connect.call_count == 1 assert client.disconnect.call_count == 0 - result = await hass.config_entries.options.async_init(entry.entry_id) + result = await entry.start_reconfigure_flow(hass) assert result["type"] is FlowResultType.MENU - assert result["step_id"] == "init" + assert result["step_id"] == "reconfigure" - result = await hass.config_entries.options.async_configure( + result = await hass.config_entries.flow.async_configure( result["flow_id"], {"next_step_id": "intent_reconfigure"} ) assert result["type"] is FlowResultType.FORM - assert result["step_id"] == "on_supervisor" + assert result["step_id"] == "on_supervisor_reconfigure" - result = await hass.config_entries.options.async_configure( + result = await hass.config_entries.flow.async_configure( result["flow_id"], {"use_addon": True} ) assert result["type"] is FlowResultType.FORM assert result["step_id"] == "configure_addon" - result = await hass.config_entries.options.async_configure( + result = await hass.config_entries.flow.async_configure( result["flow_id"], new_addon_options, ) @@ -2392,12 +2392,13 @@ async def test_options_addon_running( assert result["step_id"] == "start_addon" await hass.async_block_till_done() - result = await hass.config_entries.options.async_configure(result["flow_id"]) + result = await hass.config_entries.flow.async_configure(result["flow_id"]) await hass.async_block_till_done() assert restart_addon.call_args == call("core_zwave_js") - assert result["type"] is FlowResultType.CREATE_ENTRY + assert result["type"] is FlowResultType.ABORT + assert result["reason"] == "reconfigure_successful" assert entry.data["url"] == "ws://host1:3001" assert entry.data["usb_path"] == new_addon_options["device"] assert entry.data["s0_legacy_key"] == new_addon_options["s0_legacy_key"] @@ -2465,7 +2466,7 @@ async def test_options_addon_running( ), ], ) -async def test_options_addon_running_no_changes( +async def test_reconfigure_addon_running_no_changes( hass: HomeAssistant, client, supervisor, @@ -2480,7 +2481,7 @@ async def test_options_addon_running_no_changes( old_addon_options, new_addon_options, ) -> None: - """Test options flow without changes, and add-on already running on Supervisor.""" + """Test reconfigure flow without changes, and add-on already running on Supervisor.""" addon_options.update(old_addon_options) entry = integration data = {**entry.data, **entry_data} @@ -2491,26 +2492,26 @@ async def test_options_addon_running_no_changes( assert client.connect.call_count == 1 assert client.disconnect.call_count == 0 - result = await hass.config_entries.options.async_init(entry.entry_id) + result = await entry.start_reconfigure_flow(hass) assert result["type"] is FlowResultType.MENU - assert result["step_id"] == "init" + assert result["step_id"] == "reconfigure" - result = await hass.config_entries.options.async_configure( + result = await hass.config_entries.flow.async_configure( result["flow_id"], {"next_step_id": "intent_reconfigure"} ) assert result["type"] is FlowResultType.FORM - assert result["step_id"] == "on_supervisor" + assert result["step_id"] == "on_supervisor_reconfigure" - result = await hass.config_entries.options.async_configure( + result = await hass.config_entries.flow.async_configure( result["flow_id"], {"use_addon": True} ) assert result["type"] is FlowResultType.FORM assert result["step_id"] == "configure_addon" - result = await hass.config_entries.options.async_configure( + result = await hass.config_entries.flow.async_configure( result["flow_id"], new_addon_options, ) @@ -2520,7 +2521,8 @@ async def test_options_addon_running_no_changes( assert set_addon_options.call_count == 0 assert restart_addon.call_count == 0 - assert result["type"] is FlowResultType.CREATE_ENTRY + assert result["type"] is FlowResultType.ABORT + assert result["reason"] == "reconfigure_successful" assert entry.data["url"] == "ws://host1:3001" assert entry.data["usb_path"] == new_addon_options["device"] assert entry.data["s0_legacy_key"] == new_addon_options["s0_legacy_key"] @@ -2643,7 +2645,7 @@ async def different_device_server_version(*args): ), ], ) -async def test_options_different_device( +async def test_reconfigure_different_device( hass: HomeAssistant, client, supervisor, @@ -2660,7 +2662,7 @@ async def test_options_different_device( disconnect_calls, server_version_side_effect, ) -> None: - """Test options flow and configuring a different device.""" + """Test reconfigure flow and configuring a different device.""" addon_options.update(old_addon_options) entry = integration data = {**entry.data, **entry_data} @@ -2671,26 +2673,26 @@ async def test_options_different_device( assert client.connect.call_count == 1 assert client.disconnect.call_count == 0 - result = await hass.config_entries.options.async_init(entry.entry_id) + result = await entry.start_reconfigure_flow(hass) assert result["type"] is FlowResultType.MENU - assert result["step_id"] == "init" + assert result["step_id"] == "reconfigure" - result = await hass.config_entries.options.async_configure( + result = await hass.config_entries.flow.async_configure( result["flow_id"], {"next_step_id": "intent_reconfigure"} ) assert result["type"] is FlowResultType.FORM - assert result["step_id"] == "on_supervisor" + assert result["step_id"] == "on_supervisor_reconfigure" - result = await hass.config_entries.options.async_configure( + result = await hass.config_entries.flow.async_configure( result["flow_id"], {"use_addon": True} ) assert result["type"] is FlowResultType.FORM assert result["step_id"] == "configure_addon" - result = await hass.config_entries.options.async_configure( + result = await hass.config_entries.flow.async_configure( result["flow_id"], new_addon_options, ) @@ -2709,7 +2711,7 @@ async def test_options_different_device( assert restart_addon.call_count == 1 assert restart_addon.call_args == call("core_zwave_js") - result = await hass.config_entries.options.async_configure(result["flow_id"]) + result = await hass.config_entries.flow.async_configure(result["flow_id"]) await hass.async_block_till_done() # Default emulate_hardware is False. @@ -2729,7 +2731,7 @@ async def test_options_different_device( assert restart_addon.call_count == 2 assert restart_addon.call_args == call("core_zwave_js") - result = await hass.config_entries.options.async_configure(result["flow_id"]) + result = await hass.config_entries.flow.async_configure(result["flow_id"]) await hass.async_block_till_done() assert result["type"] is FlowResultType.ABORT @@ -2826,7 +2828,7 @@ async def test_options_different_device( ), ], ) -async def test_options_addon_restart_failed( +async def test_reconfigure_addon_restart_failed( hass: HomeAssistant, client, supervisor, @@ -2843,7 +2845,7 @@ async def test_options_addon_restart_failed( disconnect_calls, restart_addon_side_effect, ) -> None: - """Test options flow and add-on restart failure.""" + """Test reconfigure flow and add-on restart failure.""" addon_options.update(old_addon_options) entry = integration data = {**entry.data, **entry_data} @@ -2854,26 +2856,26 @@ async def test_options_addon_restart_failed( assert client.connect.call_count == 1 assert client.disconnect.call_count == 0 - result = await hass.config_entries.options.async_init(entry.entry_id) + result = await entry.start_reconfigure_flow(hass) assert result["type"] is FlowResultType.MENU - assert result["step_id"] == "init" + assert result["step_id"] == "reconfigure" - result = await hass.config_entries.options.async_configure( + result = await hass.config_entries.flow.async_configure( result["flow_id"], {"next_step_id": "intent_reconfigure"} ) assert result["type"] is FlowResultType.FORM - assert result["step_id"] == "on_supervisor" + assert result["step_id"] == "on_supervisor_reconfigure" - result = await hass.config_entries.options.async_configure( + result = await hass.config_entries.flow.async_configure( result["flow_id"], {"use_addon": True} ) assert result["type"] is FlowResultType.FORM assert result["step_id"] == "configure_addon" - result = await hass.config_entries.options.async_configure( + result = await hass.config_entries.flow.async_configure( result["flow_id"], new_addon_options, ) @@ -2892,7 +2894,7 @@ async def test_options_addon_restart_failed( assert restart_addon.call_count == 1 assert restart_addon.call_args == call("core_zwave_js") - result = await hass.config_entries.options.async_configure(result["flow_id"]) + result = await hass.config_entries.flow.async_configure(result["flow_id"]) await hass.async_block_till_done() # The legacy network key should not be reset. @@ -2909,7 +2911,7 @@ async def test_options_addon_restart_failed( assert restart_addon.call_count == 2 assert restart_addon.call_args == call("core_zwave_js") - result = await hass.config_entries.options.async_configure(result["flow_id"]) + result = await hass.config_entries.flow.async_configure(result["flow_id"]) await hass.async_block_till_done() assert result["type"] is FlowResultType.ABORT @@ -2967,7 +2969,7 @@ async def test_options_addon_restart_failed( ), ], ) -async def test_options_addon_running_server_info_failure( +async def test_reconfigure_addon_running_server_info_failure( hass: HomeAssistant, client, supervisor, @@ -2984,7 +2986,7 @@ async def test_options_addon_running_server_info_failure( disconnect_calls, server_version_side_effect, ) -> None: - """Test options flow and add-on already running with server info failure.""" + """Test reconfigure flow and add-on already running with server info failure.""" addon_options.update(old_addon_options) entry = integration data = {**entry.data, **entry_data} @@ -2995,26 +2997,26 @@ async def test_options_addon_running_server_info_failure( assert client.connect.call_count == 1 assert client.disconnect.call_count == 0 - result = await hass.config_entries.options.async_init(entry.entry_id) + result = await entry.start_reconfigure_flow(hass) assert result["type"] is FlowResultType.MENU - assert result["step_id"] == "init" + assert result["step_id"] == "reconfigure" - result = await hass.config_entries.options.async_configure( + result = await hass.config_entries.flow.async_configure( result["flow_id"], {"next_step_id": "intent_reconfigure"} ) assert result["type"] is FlowResultType.FORM - assert result["step_id"] == "on_supervisor" + assert result["step_id"] == "on_supervisor_reconfigure" - result = await hass.config_entries.options.async_configure( + result = await hass.config_entries.flow.async_configure( result["flow_id"], {"use_addon": True} ) assert result["type"] is FlowResultType.FORM assert result["step_id"] == "configure_addon" - result = await hass.config_entries.options.async_configure( + result = await hass.config_entries.flow.async_configure( result["flow_id"], new_addon_options, ) @@ -3104,7 +3106,7 @@ async def test_options_addon_running_server_info_failure( ), ], ) -async def test_options_addon_not_installed( +async def test_reconfigure_addon_not_installed( hass: HomeAssistant, client, supervisor, @@ -3121,7 +3123,7 @@ async def test_options_addon_not_installed( new_addon_options, disconnect_calls, ) -> None: - """Test options flow and add-on not installed on Supervisor.""" + """Test reconfigure flow and add-on not installed on Supervisor.""" addon_options.update(old_addon_options) entry = integration data = {**entry.data, **entry_data} @@ -3132,19 +3134,19 @@ async def test_options_addon_not_installed( assert client.connect.call_count == 1 assert client.disconnect.call_count == 0 - result = await hass.config_entries.options.async_init(entry.entry_id) + result = await entry.start_reconfigure_flow(hass) assert result["type"] is FlowResultType.MENU - assert result["step_id"] == "init" + assert result["step_id"] == "reconfigure" - result = await hass.config_entries.options.async_configure( + result = await hass.config_entries.flow.async_configure( result["flow_id"], {"next_step_id": "intent_reconfigure"} ) assert result["type"] is FlowResultType.FORM - assert result["step_id"] == "on_supervisor" + assert result["step_id"] == "on_supervisor_reconfigure" - result = await hass.config_entries.options.async_configure( + result = await hass.config_entries.flow.async_configure( result["flow_id"], {"use_addon": True} ) @@ -3154,14 +3156,14 @@ async def test_options_addon_not_installed( # Make sure the flow continues when the progress task is done. await hass.async_block_till_done() - result = await hass.config_entries.options.async_configure(result["flow_id"]) + result = await hass.config_entries.flow.async_configure(result["flow_id"]) assert install_addon.call_args == call("core_zwave_js") assert result["type"] is FlowResultType.FORM assert result["step_id"] == "configure_addon" - result = await hass.config_entries.options.async_configure( + result = await hass.config_entries.flow.async_configure( result["flow_id"], new_addon_options, ) @@ -3180,11 +3182,12 @@ async def test_options_addon_not_installed( assert start_addon.call_count == 1 assert start_addon.call_args == call("core_zwave_js") - result = await hass.config_entries.options.async_configure(result["flow_id"]) + result = await hass.config_entries.flow.async_configure(result["flow_id"]) await hass.async_block_till_done() await hass.async_block_till_done() - assert result["type"] is FlowResultType.CREATE_ENTRY + assert result["type"] is FlowResultType.ABORT + assert result["reason"] == "reconfigure_successful" assert entry.data["url"] == "ws://host1:3001" assert entry.data["usb_path"] == new_addon_options["device"] assert entry.data["s0_legacy_key"] == new_addon_options["s0_legacy_key"] @@ -3244,19 +3247,19 @@ async def test_zeroconf(hass: HomeAssistant) -> None: assert len(mock_setup_entry.mock_calls) == 1 -async def test_options_migrate_no_addon(hass: HomeAssistant, integration) -> None: +async def test_reconfigure_migrate_no_addon(hass: HomeAssistant, integration) -> None: """Test migration flow fails when not using add-on.""" entry = integration hass.config_entries.async_update_entry( entry, unique_id="1234", data={**entry.data, "use_addon": False} ) - result = await hass.config_entries.options.async_init(entry.entry_id) + result = await entry.start_reconfigure_flow(hass) assert result["type"] is FlowResultType.MENU - assert result["step_id"] == "init" + assert result["step_id"] == "reconfigure" - result = await hass.config_entries.options.async_configure( + result = await hass.config_entries.flow.async_configure( result["flow_id"], {"next_step_id": "intent_migrate"} ) @@ -3277,7 +3280,7 @@ async def test_options_migrate_no_addon(hass: HomeAssistant, integration) -> Non ] ], ) -async def test_options_migrate_with_addon( +async def test_reconfigure_migrate_with_addon( hass: HomeAssistant, client, supervisor, @@ -3288,8 +3291,9 @@ async def test_options_migrate_with_addon( get_addon_discovery_info, ) -> None: """Test migration flow with add-on.""" + entry = integration hass.config_entries.async_update_entry( - integration, + entry, unique_id="1234", data={ "url": "ws://localhost:3000", @@ -3328,19 +3332,19 @@ async def test_options_migrate_with_addon( hass, data_entry_flow.EVENT_DATA_ENTRY_FLOW_PROGRESS_UPDATE ) - result = await hass.config_entries.options.async_init(integration.entry_id) + result = await entry.start_reconfigure_flow(hass) assert result["type"] == FlowResultType.MENU - assert result["step_id"] == "init" + assert result["step_id"] == "reconfigure" - result = await hass.config_entries.options.async_configure( + result = await hass.config_entries.flow.async_configure( result["flow_id"], {"next_step_id": "intent_migrate"} ) assert result["type"] == FlowResultType.FORM assert result["step_id"] == "intent_migrate" - result = await hass.config_entries.options.async_configure(result["flow_id"], {}) + result = await hass.config_entries.flow.async_configure(result["flow_id"], {}) assert result["type"] == FlowResultType.SHOW_PROGRESS assert result["step_id"] == "backup_nvm" @@ -3353,18 +3357,18 @@ async def test_options_migrate_with_addon( assert events[0].data["progress"] == 0.5 events.clear() - result = await hass.config_entries.options.async_configure(result["flow_id"]) + result = await hass.config_entries.flow.async_configure(result["flow_id"]) assert result["type"] == FlowResultType.FORM assert result["step_id"] == "instruct_unplug" - result = await hass.config_entries.options.async_configure(result["flow_id"], {}) + result = await hass.config_entries.flow.async_configure(result["flow_id"], {}) assert result["type"] == FlowResultType.FORM assert result["step_id"] == "choose_serial_port" assert result["data_schema"].schema[CONF_USB_PATH] - result = await hass.config_entries.options.async_configure( + result = await hass.config_entries.flow.async_configure( result["flow_id"], user_input={ CONF_USB_PATH: "/test", @@ -3381,7 +3385,7 @@ async def test_options_migrate_with_addon( assert restart_addon.call_args == call("core_zwave_js") - result = await hass.config_entries.options.async_configure(result["flow_id"]) + result = await hass.config_entries.flow.async_configure(result["flow_id"]) assert result["type"] == FlowResultType.SHOW_PROGRESS assert result["step_id"] == "restore_nvm" @@ -3393,15 +3397,16 @@ async def test_options_migrate_with_addon( assert events[0].data["progress"] == 0.25 assert events[1].data["progress"] == 0.75 - result = await hass.config_entries.options.async_configure(result["flow_id"]) + result = await hass.config_entries.flow.async_configure(result["flow_id"]) - assert result["type"] == FlowResultType.CREATE_ENTRY + 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 -async def test_options_migrate_backup_failure( +async def test_reconfigure_migrate_backup_failure( hass: HomeAssistant, integration, client ) -> None: """Test backup failure.""" @@ -3414,25 +3419,25 @@ async def test_options_migrate_backup_failure( side_effect=FailedCommand("test_error", "unknown_error") ) - result = await hass.config_entries.options.async_init(entry.entry_id) + result = await entry.start_reconfigure_flow(hass) assert result["type"] == FlowResultType.MENU - assert result["step_id"] == "init" + assert result["step_id"] == "reconfigure" - result = await hass.config_entries.options.async_configure( + result = await hass.config_entries.flow.async_configure( result["flow_id"], {"next_step_id": "intent_migrate"} ) assert result["type"] == FlowResultType.FORM assert result["step_id"] == "intent_migrate" - result = await hass.config_entries.options.async_configure(result["flow_id"], {}) + result = await hass.config_entries.flow.async_configure(result["flow_id"], {}) assert result["type"] == FlowResultType.ABORT assert result["reason"] == "backup_failed" -async def test_options_migrate_backup_file_failure( +async def test_reconfigure_migrate_backup_file_failure( hass: HomeAssistant, integration, client ) -> None: """Test backup file failure.""" @@ -3449,19 +3454,19 @@ async def test_options_migrate_backup_file_failure( side_effect=mock_backup_nvm_raw ) - result = await hass.config_entries.options.async_init(entry.entry_id) + result = await entry.start_reconfigure_flow(hass) assert result["type"] == FlowResultType.MENU - assert result["step_id"] == "init" + assert result["step_id"] == "reconfigure" - result = await hass.config_entries.options.async_configure( + result = await hass.config_entries.flow.async_configure( result["flow_id"], {"next_step_id": "intent_migrate"} ) assert result["type"] == FlowResultType.FORM assert result["step_id"] == "intent_migrate" - result = await hass.config_entries.options.async_configure(result["flow_id"], {}) + result = await hass.config_entries.flow.async_configure(result["flow_id"], {}) assert result["type"] == FlowResultType.SHOW_PROGRESS assert result["step_id"] == "backup_nvm" @@ -3472,7 +3477,7 @@ async def test_options_migrate_backup_file_failure( await hass.async_block_till_done() assert client.driver.controller.async_backup_nvm_raw.call_count == 1 - result = await hass.config_entries.options.async_configure(result["flow_id"]) + result = await hass.config_entries.flow.async_configure(result["flow_id"]) assert result["type"] == FlowResultType.ABORT assert result["reason"] == "backup_failed" @@ -3491,7 +3496,7 @@ async def test_options_migrate_backup_file_failure( ] ], ) -async def test_options_migrate_restore_failure( +async def test_reconfigure_migrate_restore_failure( hass: HomeAssistant, client, supervisor, @@ -3502,8 +3507,9 @@ async def test_options_migrate_restore_failure( get_addon_discovery_info, ) -> None: """Test restore failure.""" + entry = integration hass.config_entries.async_update_entry( - integration, unique_id="1234", data={**integration.data, "use_addon": True} + entry, unique_id="1234", data={**entry.data, "use_addon": True} ) async def mock_backup_nvm_raw(): @@ -3517,19 +3523,19 @@ async def test_options_migrate_restore_failure( side_effect=FailedCommand("test_error", "unknown_error") ) - result = await hass.config_entries.options.async_init(integration.entry_id) + result = await entry.start_reconfigure_flow(hass) assert result["type"] == FlowResultType.MENU - assert result["step_id"] == "init" + assert result["step_id"] == "reconfigure" - result = await hass.config_entries.options.async_configure( + result = await hass.config_entries.flow.async_configure( result["flow_id"], {"next_step_id": "intent_migrate"} ) assert result["type"] == FlowResultType.FORM assert result["step_id"] == "intent_migrate" - result = await hass.config_entries.options.async_configure(result["flow_id"], {}) + result = await hass.config_entries.flow.async_configure(result["flow_id"], {}) assert result["type"] == FlowResultType.SHOW_PROGRESS assert result["step_id"] == "backup_nvm" @@ -3539,17 +3545,17 @@ async def test_options_migrate_restore_failure( assert client.driver.controller.async_backup_nvm_raw.call_count == 1 assert mock_file.call_count == 1 - result = await hass.config_entries.options.async_configure(result["flow_id"]) + result = await hass.config_entries.flow.async_configure(result["flow_id"]) assert result["type"] == FlowResultType.FORM assert result["step_id"] == "instruct_unplug" - result = await hass.config_entries.options.async_configure(result["flow_id"], {}) + result = await hass.config_entries.flow.async_configure(result["flow_id"], {}) assert result["type"] == FlowResultType.FORM assert result["step_id"] == "choose_serial_port" - result = await hass.config_entries.options.async_configure( + result = await hass.config_entries.flow.async_configure( result["flow_id"], user_input={ CONF_USB_PATH: "/test", @@ -3560,7 +3566,7 @@ async def test_options_migrate_restore_failure( assert result["step_id"] == "start_addon" await hass.async_block_till_done() - result = await hass.config_entries.options.async_configure(result["flow_id"]) + result = await hass.config_entries.flow.async_configure(result["flow_id"]) assert result["type"] == FlowResultType.SHOW_PROGRESS assert result["step_id"] == "restore_nvm" @@ -3569,7 +3575,7 @@ async def test_options_migrate_restore_failure( assert client.driver.controller.async_restore_nvm.call_count == 1 - result = await hass.config_entries.options.async_configure(result["flow_id"]) + result = await hass.config_entries.flow.async_configure(result["flow_id"]) assert result["type"] == FlowResultType.ABORT assert result["reason"] == "restore_failed" @@ -3577,18 +3583,32 @@ async def test_options_migrate_restore_failure( async def test_get_driver_failure(hass: HomeAssistant, integration, client) -> None: """Test get driver failure.""" + entry = integration + hass.config_entries.async_update_entry( + integration, unique_id="1234", data={**integration.data, "use_addon": True} + ) + result = await entry.start_reconfigure_flow(hass) + assert result["type"] == 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"] == FlowResultType.FORM + assert result["step_id"] == "intent_migrate" - handler = OptionsFlowHandler() - handler.hass = hass - handler._config_entry = integration await hass.config_entries.async_unload(integration.entry_id) - with pytest.raises(data_entry_flow.AbortFlow): - await handler._get_driver() + result = await hass.config_entries.flow.async_configure(result["flow_id"], {}) + + assert result["type"] == FlowResultType.ABORT + assert result["reason"] == "backup_failed" async def test_hard_reset_failure(hass: HomeAssistant, integration, client) -> None: """Test hard reset failure.""" + entry = integration hass.config_entries.async_update_entry( integration, unique_id="1234", data={**integration.data, "use_addon": True} ) @@ -3604,19 +3624,19 @@ async def test_hard_reset_failure(hass: HomeAssistant, integration, client) -> N side_effect=FailedCommand("test_error", "unknown_error") ) - result = await hass.config_entries.options.async_init(integration.entry_id) + result = await entry.start_reconfigure_flow(hass) assert result["type"] == FlowResultType.MENU - assert result["step_id"] == "init" + assert result["step_id"] == "reconfigure" - result = await hass.config_entries.options.async_configure( + result = await hass.config_entries.flow.async_configure( result["flow_id"], {"next_step_id": "intent_migrate"} ) assert result["type"] == FlowResultType.FORM assert result["step_id"] == "intent_migrate" - result = await hass.config_entries.options.async_configure(result["flow_id"], {}) + result = await hass.config_entries.flow.async_configure(result["flow_id"], {}) assert result["type"] == FlowResultType.SHOW_PROGRESS assert result["step_id"] == "backup_nvm" @@ -3626,7 +3646,7 @@ async def test_hard_reset_failure(hass: HomeAssistant, integration, client) -> N assert client.driver.controller.async_backup_nvm_raw.call_count == 1 assert mock_file.call_count == 1 - result = await hass.config_entries.options.async_configure(result["flow_id"]) + result = await hass.config_entries.flow.async_configure(result["flow_id"]) assert result["type"] == FlowResultType.ABORT assert result["reason"] == "reset_failed" @@ -3636,6 +3656,7 @@ async def test_choose_serial_port_usb_ports_failure( hass: HomeAssistant, integration, client ) -> None: """Test choose serial port usb ports failure.""" + entry = integration hass.config_entries.async_update_entry( integration, unique_id="1234", data={**integration.data, "use_addon": True} ) @@ -3648,19 +3669,19 @@ async def test_choose_serial_port_usb_ports_failure( side_effect=mock_backup_nvm_raw ) - result = await hass.config_entries.options.async_init(integration.entry_id) + result = await entry.start_reconfigure_flow(hass) assert result["type"] == FlowResultType.MENU - assert result["step_id"] == "init" + assert result["step_id"] == "reconfigure" - result = await hass.config_entries.options.async_configure( + result = await hass.config_entries.flow.async_configure( result["flow_id"], {"next_step_id": "intent_migrate"} ) assert result["type"] == FlowResultType.FORM assert result["step_id"] == "intent_migrate" - result = await hass.config_entries.options.async_configure(result["flow_id"], {}) + result = await hass.config_entries.flow.async_configure(result["flow_id"], {}) assert result["type"] == FlowResultType.SHOW_PROGRESS assert result["step_id"] == "backup_nvm" @@ -3670,7 +3691,7 @@ async def test_choose_serial_port_usb_ports_failure( assert client.driver.controller.async_backup_nvm_raw.call_count == 1 assert mock_file.call_count == 1 - result = await hass.config_entries.options.async_configure(result["flow_id"]) + result = await hass.config_entries.flow.async_configure(result["flow_id"]) assert result["type"] == FlowResultType.FORM assert result["step_id"] == "instruct_unplug" @@ -3679,9 +3700,7 @@ async def test_choose_serial_port_usb_ports_failure( "homeassistant.components.zwave_js.config_flow.async_get_usb_ports", side_effect=OSError("test_error"), ): - result = await hass.config_entries.options.async_configure( - result["flow_id"], {} - ) + result = await hass.config_entries.flow.async_configure(result["flow_id"], {}) assert result["type"] == FlowResultType.ABORT assert result["reason"] == "usb_ports_failed" @@ -3690,23 +3709,24 @@ async def test_configure_addon_usb_ports_failure( hass: HomeAssistant, integration, addon_installed, supervisor ) -> None: """Test configure addon usb ports failure.""" - result = await hass.config_entries.options.async_init(integration.entry_id) + entry = integration + result = await entry.start_reconfigure_flow(hass) assert result["type"] == FlowResultType.MENU - assert result["step_id"] == "init" + assert result["step_id"] == "reconfigure" - result = await hass.config_entries.options.async_configure( + result = await hass.config_entries.flow.async_configure( result["flow_id"], {"next_step_id": "intent_reconfigure"} ) assert result["type"] == FlowResultType.FORM - assert result["step_id"] == "on_supervisor" + assert result["step_id"] == "on_supervisor_reconfigure" with patch( "homeassistant.components.zwave_js.config_flow.async_get_usb_ports", side_effect=OSError("test_error"), ): - result = await hass.config_entries.options.async_configure( + result = await hass.config_entries.flow.async_configure( result["flow_id"], {"use_addon": True} ) assert result["type"] == FlowResultType.ABORT