mirror of
https://github.com/home-assistant/core.git
synced 2025-07-22 20:57:21 +00:00
Add zwave_js add-on config flow support (#45552)
This commit is contained in:
parent
c805baf88c
commit
f387e833c3
@ -6,6 +6,7 @@ from async_timeout import timeout
|
|||||||
from zwave_js_server.client import Client as ZwaveClient
|
from zwave_js_server.client import Client as ZwaveClient
|
||||||
from zwave_js_server.model.node import Node as ZwaveNode
|
from zwave_js_server.model.node import Node as ZwaveNode
|
||||||
|
|
||||||
|
from homeassistant.components.hassio.handler import HassioAPIError
|
||||||
from homeassistant.config_entries import ConfigEntry
|
from homeassistant.config_entries import ConfigEntry
|
||||||
from homeassistant.const import CONF_URL, EVENT_HOMEASSISTANT_STOP
|
from homeassistant.const import CONF_URL, EVENT_HOMEASSISTANT_STOP
|
||||||
from homeassistant.core import Event, HomeAssistant, callback
|
from homeassistant.core import Event, HomeAssistant, callback
|
||||||
@ -16,6 +17,7 @@ from homeassistant.helpers.dispatcher import async_dispatcher_send
|
|||||||
|
|
||||||
from .api import async_register_api
|
from .api import async_register_api
|
||||||
from .const import (
|
from .const import (
|
||||||
|
CONF_INTEGRATION_CREATED_ADDON,
|
||||||
DATA_CLIENT,
|
DATA_CLIENT,
|
||||||
DATA_UNSUBSCRIBE,
|
DATA_UNSUBSCRIBE,
|
||||||
DOMAIN,
|
DOMAIN,
|
||||||
@ -196,3 +198,19 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
|||||||
await info[DATA_CLIENT].disconnect()
|
await info[DATA_CLIENT].disconnect()
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
async def async_remove_entry(hass: HomeAssistant, entry: ConfigEntry) -> None:
|
||||||
|
"""Remove a config entry."""
|
||||||
|
if not entry.data.get(CONF_INTEGRATION_CREATED_ADDON):
|
||||||
|
return
|
||||||
|
|
||||||
|
try:
|
||||||
|
await hass.components.hassio.async_stop_addon("core_zwave_js")
|
||||||
|
except HassioAPIError as err:
|
||||||
|
LOGGER.error("Failed to stop the Z-Wave JS add-on: %s", err)
|
||||||
|
return
|
||||||
|
try:
|
||||||
|
await hass.components.hassio.async_uninstall_addon("core_zwave_js")
|
||||||
|
except HassioAPIError as err:
|
||||||
|
LOGGER.error("Failed to uninstall the Z-Wave JS add-on: %s", err)
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
"""Config flow for Z-Wave JS integration."""
|
"""Config flow for Z-Wave JS integration."""
|
||||||
import asyncio
|
import asyncio
|
||||||
import logging
|
import logging
|
||||||
from typing import Any, Dict, Optional
|
from typing import Any, Dict, Optional, cast
|
||||||
|
|
||||||
import aiohttp
|
import aiohttp
|
||||||
from async_timeout import timeout
|
from async_timeout import timeout
|
||||||
@ -10,14 +10,27 @@ from zwave_js_server.version import VersionInfo, get_server_version
|
|||||||
|
|
||||||
from homeassistant import config_entries, core, exceptions
|
from homeassistant import config_entries, core, exceptions
|
||||||
from homeassistant.const import CONF_URL
|
from homeassistant.const import CONF_URL
|
||||||
|
from homeassistant.data_entry_flow import AbortFlow
|
||||||
from homeassistant.helpers.aiohttp_client import async_get_clientsession
|
from homeassistant.helpers.aiohttp_client import async_get_clientsession
|
||||||
|
|
||||||
from .const import DOMAIN, NAME # pylint:disable=unused-import
|
from .const import ( # pylint:disable=unused-import
|
||||||
|
CONF_INTEGRATION_CREATED_ADDON,
|
||||||
|
CONF_USE_ADDON,
|
||||||
|
DOMAIN,
|
||||||
|
)
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
CONF_ADDON_DEVICE = "device"
|
||||||
|
CONF_ADDON_NETWORK_KEY = "network_key"
|
||||||
|
CONF_NETWORK_KEY = "network_key"
|
||||||
|
CONF_USB_PATH = "usb_path"
|
||||||
DEFAULT_URL = "ws://localhost:3000"
|
DEFAULT_URL = "ws://localhost:3000"
|
||||||
|
TITLE = "Z-Wave JS"
|
||||||
|
|
||||||
|
ADDON_SETUP_TIME = 10
|
||||||
|
|
||||||
|
ON_SUPERVISOR_SCHEMA = vol.Schema({vol.Optional(CONF_USE_ADDON, default=True): bool})
|
||||||
STEP_USER_DATA_SCHEMA = vol.Schema({vol.Required(CONF_URL, default=DEFAULT_URL): str})
|
STEP_USER_DATA_SCHEMA = vol.Schema({vol.Required(CONF_URL, default=DEFAULT_URL): str})
|
||||||
|
|
||||||
|
|
||||||
@ -28,11 +41,26 @@ async def validate_input(hass: core.HomeAssistant, user_input: dict) -> VersionI
|
|||||||
if not ws_address.startswith(("ws://", "wss://")):
|
if not ws_address.startswith(("ws://", "wss://")):
|
||||||
raise InvalidInput("invalid_ws_url")
|
raise InvalidInput("invalid_ws_url")
|
||||||
|
|
||||||
|
try:
|
||||||
|
return await async_get_version_info(hass, ws_address)
|
||||||
|
except CannotConnect as err:
|
||||||
|
raise InvalidInput("cannot_connect") from err
|
||||||
|
|
||||||
|
|
||||||
|
async def async_get_version_info(
|
||||||
|
hass: core.HomeAssistant, ws_address: str
|
||||||
|
) -> VersionInfo:
|
||||||
|
"""Return Z-Wave JS version info."""
|
||||||
async with timeout(10):
|
async with timeout(10):
|
||||||
try:
|
try:
|
||||||
return await get_server_version(ws_address, async_get_clientsession(hass))
|
version_info: VersionInfo = await get_server_version(
|
||||||
|
ws_address, async_get_clientsession(hass)
|
||||||
|
)
|
||||||
except (asyncio.TimeoutError, aiohttp.ClientError) as err:
|
except (asyncio.TimeoutError, aiohttp.ClientError) as err:
|
||||||
raise InvalidInput("cannot_connect") from err
|
_LOGGER.error("Failed to connect to Z-Wave JS server: %s", err)
|
||||||
|
raise CannotConnect from err
|
||||||
|
|
||||||
|
return version_info
|
||||||
|
|
||||||
|
|
||||||
class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
||||||
@ -41,19 +69,39 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
|||||||
VERSION = 1
|
VERSION = 1
|
||||||
CONNECTION_CLASS = config_entries.CONN_CLASS_LOCAL_PUSH
|
CONNECTION_CLASS = config_entries.CONN_CLASS_LOCAL_PUSH
|
||||||
|
|
||||||
|
def __init__(self) -> None:
|
||||||
|
"""Set up flow instance."""
|
||||||
|
self.addon_config: Optional[dict] = None
|
||||||
|
self.network_key: Optional[str] = None
|
||||||
|
self.usb_path: Optional[str] = None
|
||||||
|
self.use_addon = False
|
||||||
|
self.ws_address: Optional[str] = None
|
||||||
|
# If we install the add-on we should uninstall it on entry remove.
|
||||||
|
self.integration_created_addon = False
|
||||||
|
self.install_task: Optional[asyncio.Task] = None
|
||||||
|
|
||||||
async def async_step_user(
|
async def async_step_user(
|
||||||
self, user_input: Optional[Dict[str, Any]] = None
|
self, user_input: Optional[Dict[str, Any]] = None
|
||||||
) -> Dict[str, Any]:
|
) -> Dict[str, Any]:
|
||||||
"""Handle the initial step."""
|
"""Handle the initial step."""
|
||||||
|
assert self.hass # typing
|
||||||
|
if self.hass.components.hassio.is_hassio():
|
||||||
|
return await self.async_step_on_supervisor()
|
||||||
|
|
||||||
|
return await self.async_step_manual()
|
||||||
|
|
||||||
|
async def async_step_manual(
|
||||||
|
self, user_input: Optional[Dict[str, Any]] = None
|
||||||
|
) -> Dict[str, Any]:
|
||||||
|
"""Handle a manual configuration."""
|
||||||
if user_input is None:
|
if user_input is None:
|
||||||
return self.async_show_form(
|
return self.async_show_form(
|
||||||
step_id="user", data_schema=STEP_USER_DATA_SCHEMA
|
step_id="manual", data_schema=STEP_USER_DATA_SCHEMA
|
||||||
)
|
)
|
||||||
|
|
||||||
errors = {}
|
errors = {}
|
||||||
|
|
||||||
assert self.hass # typing
|
assert self.hass # typing
|
||||||
|
|
||||||
try:
|
try:
|
||||||
version_info = await validate_input(self.hass, user_input)
|
version_info = await validate_input(self.hass, user_input)
|
||||||
except InvalidInput as err:
|
except InvalidInput as err:
|
||||||
@ -62,14 +110,268 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
|||||||
_LOGGER.exception("Unexpected exception")
|
_LOGGER.exception("Unexpected exception")
|
||||||
errors["base"] = "unknown"
|
errors["base"] = "unknown"
|
||||||
else:
|
else:
|
||||||
await self.async_set_unique_id(version_info.home_id)
|
await self.async_set_unique_id(
|
||||||
|
version_info.home_id, raise_on_progress=False
|
||||||
|
)
|
||||||
self._abort_if_unique_id_configured(user_input)
|
self._abort_if_unique_id_configured(user_input)
|
||||||
return self.async_create_entry(title=NAME, data=user_input)
|
self.ws_address = user_input[CONF_URL]
|
||||||
|
return self._async_create_entry_from_vars()
|
||||||
|
|
||||||
return self.async_show_form(
|
return self.async_show_form(
|
||||||
step_id="user", data_schema=STEP_USER_DATA_SCHEMA, errors=errors
|
step_id="manual", data_schema=STEP_USER_DATA_SCHEMA, errors=errors
|
||||||
)
|
)
|
||||||
|
|
||||||
|
async def async_step_hassio( # type: ignore
|
||||||
|
self, discovery_info: Dict[str, Any]
|
||||||
|
) -> Dict[str, Any]:
|
||||||
|
"""Receive configuration from add-on discovery info.
|
||||||
|
|
||||||
|
This flow is triggered by the Z-Wave JS add-on.
|
||||||
|
"""
|
||||||
|
assert self.hass
|
||||||
|
self.ws_address = f"ws://{discovery_info['host']}:{discovery_info['port']}"
|
||||||
|
try:
|
||||||
|
version_info = await async_get_version_info(self.hass, self.ws_address)
|
||||||
|
except CannotConnect:
|
||||||
|
return self.async_abort(reason="cannot_connect")
|
||||||
|
|
||||||
|
await self.async_set_unique_id(version_info.home_id)
|
||||||
|
self._abort_if_unique_id_configured(updates={CONF_URL: self.ws_address})
|
||||||
|
|
||||||
|
return await self.async_step_hassio_confirm()
|
||||||
|
|
||||||
|
async def async_step_hassio_confirm(
|
||||||
|
self, user_input: Optional[Dict[str, Any]] = None
|
||||||
|
) -> Dict[str, Any]:
|
||||||
|
"""Confirm the add-on discovery."""
|
||||||
|
if user_input is not None:
|
||||||
|
return await self.async_step_on_supervisor(
|
||||||
|
user_input={CONF_USE_ADDON: True}
|
||||||
|
)
|
||||||
|
|
||||||
|
return self.async_show_form(step_id="hassio_confirm")
|
||||||
|
|
||||||
|
def _async_create_entry_from_vars(self) -> Dict[str, Any]:
|
||||||
|
"""Return a config entry for the flow."""
|
||||||
|
return self.async_create_entry(
|
||||||
|
title=TITLE,
|
||||||
|
data={
|
||||||
|
CONF_URL: self.ws_address,
|
||||||
|
CONF_USB_PATH: self.usb_path,
|
||||||
|
CONF_NETWORK_KEY: self.network_key,
|
||||||
|
CONF_USE_ADDON: self.use_addon,
|
||||||
|
CONF_INTEGRATION_CREATED_ADDON: self.integration_created_addon,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
async def async_step_on_supervisor(
|
||||||
|
self, user_input: Optional[Dict[str, Any]] = None
|
||||||
|
) -> Dict[str, Any]:
|
||||||
|
"""Handle logic when on Supervisor host."""
|
||||||
|
if user_input is None:
|
||||||
|
return self.async_show_form(
|
||||||
|
step_id="on_supervisor", data_schema=ON_SUPERVISOR_SCHEMA
|
||||||
|
)
|
||||||
|
if not user_input[CONF_USE_ADDON]:
|
||||||
|
return await self.async_step_manual()
|
||||||
|
|
||||||
|
self.use_addon = True
|
||||||
|
|
||||||
|
if await self._async_is_addon_running():
|
||||||
|
discovery_info = await self._async_get_addon_discovery_info()
|
||||||
|
self.ws_address = f"ws://{discovery_info['host']}:{discovery_info['port']}"
|
||||||
|
|
||||||
|
if not self.unique_id:
|
||||||
|
assert self.hass
|
||||||
|
try:
|
||||||
|
version_info = await async_get_version_info(
|
||||||
|
self.hass, self.ws_address
|
||||||
|
)
|
||||||
|
except CannotConnect:
|
||||||
|
return self.async_abort(reason="cannot_connect")
|
||||||
|
await self.async_set_unique_id(
|
||||||
|
version_info.home_id, raise_on_progress=False
|
||||||
|
)
|
||||||
|
|
||||||
|
self._abort_if_unique_id_configured()
|
||||||
|
addon_config = await self._async_get_addon_config()
|
||||||
|
self.usb_path = addon_config[CONF_ADDON_DEVICE]
|
||||||
|
self.network_key = addon_config.get(CONF_ADDON_NETWORK_KEY, "")
|
||||||
|
return self._async_create_entry_from_vars()
|
||||||
|
|
||||||
|
if await self._async_is_addon_installed():
|
||||||
|
return await self.async_step_start_addon()
|
||||||
|
|
||||||
|
return await self.async_step_install_addon()
|
||||||
|
|
||||||
|
async def async_step_install_addon(
|
||||||
|
self, user_input: Optional[Dict[str, Any]] = None
|
||||||
|
) -> Dict[str, Any]:
|
||||||
|
"""Install Z-Wave JS add-on."""
|
||||||
|
assert self.hass
|
||||||
|
if not self.install_task:
|
||||||
|
self.install_task = self.hass.async_create_task(self._async_install_addon())
|
||||||
|
return self.async_show_progress(
|
||||||
|
step_id="install_addon", progress_action="install_addon"
|
||||||
|
)
|
||||||
|
|
||||||
|
assert self.hass
|
||||||
|
try:
|
||||||
|
await self.install_task
|
||||||
|
except self.hass.components.hassio.HassioAPIError as err:
|
||||||
|
_LOGGER.error("Failed to install Z-Wave JS add-on: %s", err)
|
||||||
|
return self.async_show_progress_done(next_step_id="install_failed")
|
||||||
|
|
||||||
|
self.integration_created_addon = True
|
||||||
|
|
||||||
|
return self.async_show_progress_done(next_step_id="start_addon")
|
||||||
|
|
||||||
|
async def async_step_install_failed(
|
||||||
|
self, user_input: Optional[Dict[str, Any]] = None
|
||||||
|
) -> Dict[str, Any]:
|
||||||
|
"""Add-on installation failed."""
|
||||||
|
return self.async_abort(reason="addon_install_failed")
|
||||||
|
|
||||||
|
async def async_step_start_addon(
|
||||||
|
self, user_input: Optional[Dict[str, Any]] = None
|
||||||
|
) -> Dict[str, Any]:
|
||||||
|
"""Ask for config and start Z-Wave JS add-on."""
|
||||||
|
if self.addon_config is None:
|
||||||
|
self.addon_config = await self._async_get_addon_config()
|
||||||
|
|
||||||
|
errors = {}
|
||||||
|
|
||||||
|
if user_input is not None:
|
||||||
|
self.network_key = user_input[CONF_NETWORK_KEY]
|
||||||
|
self.usb_path = user_input[CONF_USB_PATH]
|
||||||
|
|
||||||
|
new_addon_config = {
|
||||||
|
CONF_ADDON_DEVICE: self.usb_path,
|
||||||
|
CONF_ADDON_NETWORK_KEY: self.network_key,
|
||||||
|
}
|
||||||
|
|
||||||
|
if new_addon_config != self.addon_config:
|
||||||
|
await self._async_set_addon_config(new_addon_config)
|
||||||
|
|
||||||
|
assert self.hass
|
||||||
|
try:
|
||||||
|
await self.hass.components.hassio.async_start_addon("core_zwave_js")
|
||||||
|
except self.hass.components.hassio.HassioAPIError as err:
|
||||||
|
_LOGGER.error("Failed to start Z-Wave JS add-on: %s", err)
|
||||||
|
errors["base"] = "addon_start_failed"
|
||||||
|
else:
|
||||||
|
# Sleep some seconds to let the add-on start properly before connecting.
|
||||||
|
await asyncio.sleep(ADDON_SETUP_TIME)
|
||||||
|
discovery_info = await self._async_get_addon_discovery_info()
|
||||||
|
self.ws_address = (
|
||||||
|
f"ws://{discovery_info['host']}:{discovery_info['port']}"
|
||||||
|
)
|
||||||
|
|
||||||
|
if not self.unique_id:
|
||||||
|
try:
|
||||||
|
version_info = await async_get_version_info(
|
||||||
|
self.hass, self.ws_address
|
||||||
|
)
|
||||||
|
except CannotConnect:
|
||||||
|
return self.async_abort(reason="cannot_connect")
|
||||||
|
await self.async_set_unique_id(
|
||||||
|
version_info.home_id, raise_on_progress=False
|
||||||
|
)
|
||||||
|
|
||||||
|
self._abort_if_unique_id_configured()
|
||||||
|
return self._async_create_entry_from_vars()
|
||||||
|
|
||||||
|
usb_path = self.addon_config.get(CONF_ADDON_DEVICE, self.usb_path or "")
|
||||||
|
network_key = self.addon_config.get(
|
||||||
|
CONF_ADDON_NETWORK_KEY, self.network_key or ""
|
||||||
|
)
|
||||||
|
|
||||||
|
data_schema = vol.Schema(
|
||||||
|
{
|
||||||
|
vol.Required(CONF_USB_PATH, default=usb_path): str,
|
||||||
|
vol.Optional(CONF_NETWORK_KEY, default=network_key): str,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
return self.async_show_form(
|
||||||
|
step_id="start_addon", data_schema=data_schema, errors=errors
|
||||||
|
)
|
||||||
|
|
||||||
|
async def _async_get_addon_info(self) -> dict:
|
||||||
|
"""Return and cache Z-Wave JS add-on info."""
|
||||||
|
assert self.hass
|
||||||
|
try:
|
||||||
|
addon_info: dict = await self.hass.components.hassio.async_get_addon_info(
|
||||||
|
"core_zwave_js"
|
||||||
|
)
|
||||||
|
except self.hass.components.hassio.HassioAPIError as err:
|
||||||
|
_LOGGER.error("Failed to get Z-Wave JS add-on info: %s", err)
|
||||||
|
raise AbortFlow("addon_info_failed") from err
|
||||||
|
|
||||||
|
return addon_info
|
||||||
|
|
||||||
|
async def _async_is_addon_running(self) -> bool:
|
||||||
|
"""Return True if Z-Wave JS add-on is running."""
|
||||||
|
addon_info = await self._async_get_addon_info()
|
||||||
|
return bool(addon_info["state"] == "started")
|
||||||
|
|
||||||
|
async def _async_is_addon_installed(self) -> bool:
|
||||||
|
"""Return True if Z-Wave JS add-on is installed."""
|
||||||
|
addon_info = await self._async_get_addon_info()
|
||||||
|
return addon_info["version"] is not None
|
||||||
|
|
||||||
|
async def _async_get_addon_config(self) -> dict:
|
||||||
|
"""Get Z-Wave JS add-on config."""
|
||||||
|
addon_info = await self._async_get_addon_info()
|
||||||
|
return cast(dict, addon_info["options"])
|
||||||
|
|
||||||
|
async def _async_set_addon_config(self, config: dict) -> None:
|
||||||
|
"""Set Z-Wave JS add-on config."""
|
||||||
|
assert self.hass
|
||||||
|
options = {"options": config}
|
||||||
|
try:
|
||||||
|
await self.hass.components.hassio.async_set_addon_options(
|
||||||
|
"core_zwave_js", options
|
||||||
|
)
|
||||||
|
except self.hass.components.hassio.HassioAPIError as err:
|
||||||
|
_LOGGER.error("Failed to set Z-Wave JS add-on config: %s", err)
|
||||||
|
raise AbortFlow("addon_set_config_failed") from err
|
||||||
|
|
||||||
|
async def _async_install_addon(self) -> None:
|
||||||
|
"""Install the Z-Wave JS add-on."""
|
||||||
|
assert self.hass
|
||||||
|
try:
|
||||||
|
await self.hass.components.hassio.async_install_addon("core_zwave_js")
|
||||||
|
finally:
|
||||||
|
# Continue the flow after show progress when the task is done.
|
||||||
|
self.hass.async_create_task(
|
||||||
|
self.hass.config_entries.flow.async_configure(flow_id=self.flow_id)
|
||||||
|
)
|
||||||
|
|
||||||
|
async def _async_get_addon_discovery_info(self) -> dict:
|
||||||
|
"""Return add-on discovery info."""
|
||||||
|
assert self.hass
|
||||||
|
try:
|
||||||
|
discovery_info: dict = (
|
||||||
|
await self.hass.components.hassio.async_get_addon_discovery_info(
|
||||||
|
"core_zwave_js"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
except self.hass.components.hassio.HassioAPIError as err:
|
||||||
|
_LOGGER.error("Failed to get Z-Wave JS add-on discovery info: %s", err)
|
||||||
|
raise AbortFlow("addon_get_discovery_info_failed") from err
|
||||||
|
|
||||||
|
if not discovery_info:
|
||||||
|
_LOGGER.error("Failed to get Z-Wave JS add-on discovery info")
|
||||||
|
raise AbortFlow("addon_missing_discovery_info")
|
||||||
|
|
||||||
|
discovery_info_config: dict = discovery_info["config"]
|
||||||
|
return discovery_info_config
|
||||||
|
|
||||||
|
|
||||||
|
class CannotConnect(exceptions.HomeAssistantError):
|
||||||
|
"""Indicate connection error."""
|
||||||
|
|
||||||
|
|
||||||
class InvalidInput(exceptions.HomeAssistantError):
|
class InvalidInput(exceptions.HomeAssistantError):
|
||||||
"""Error to indicate input data is invalid."""
|
"""Error to indicate input data is invalid."""
|
||||||
|
@ -1,8 +1,7 @@
|
|||||||
"""Constants for the Z-Wave JS integration."""
|
"""Constants for the Z-Wave JS integration."""
|
||||||
|
CONF_INTEGRATION_CREATED_ADDON = "integration_created_addon"
|
||||||
|
CONF_USE_ADDON = "use_addon"
|
||||||
DOMAIN = "zwave_js"
|
DOMAIN = "zwave_js"
|
||||||
NAME = "Z-Wave JS"
|
|
||||||
PLATFORMS = [
|
PLATFORMS = [
|
||||||
"binary_sensor",
|
"binary_sensor",
|
||||||
"climate",
|
"climate",
|
||||||
|
@ -2,19 +2,48 @@
|
|||||||
"title": "Z-Wave JS",
|
"title": "Z-Wave JS",
|
||||||
"config": {
|
"config": {
|
||||||
"step": {
|
"step": {
|
||||||
"user": {
|
"manual": {
|
||||||
"data": {
|
"data": {
|
||||||
"url": "[%key:common::config_flow::data::url%]"
|
"url": "[%key:common::config_flow::data::url%]"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"on_supervisor": {
|
||||||
|
"title": "Select connection method",
|
||||||
|
"description": "Do you want to use the Z-Wave JS Supervisor add-on?",
|
||||||
|
"data": { "use_addon": "Use the Z-Wave JS Supervisor add-on" }
|
||||||
|
},
|
||||||
|
"install_addon": {
|
||||||
|
"title": "The Z-Wave JS add-on installation has started"
|
||||||
|
},
|
||||||
|
"start_addon": {
|
||||||
|
"title": "Enter the Z-Wave JS add-on configuration",
|
||||||
|
"data": {
|
||||||
|
"usb_path": "[%key:common::config_flow::data::usb_path%]",
|
||||||
|
"network_key": "Network Key"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"hassio_confirm": {
|
||||||
|
"title": "Set up Z-Wave JS integration with the Z-Wave JS add-on"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"error": {
|
"error": {
|
||||||
|
"addon_start_failed": "Failed to start the Z-Wave JS add-on. Check the configuration.",
|
||||||
"invalid_ws_url": "Invalid websocket URL",
|
"invalid_ws_url": "Invalid websocket URL",
|
||||||
"cannot_connect": "[%key:common::config_flow::error::cannot_connect%]",
|
"cannot_connect": "[%key:common::config_flow::error::cannot_connect%]",
|
||||||
"unknown": "[%key:common::config_flow::error::unknown%]"
|
"unknown": "[%key:common::config_flow::error::unknown%]"
|
||||||
},
|
},
|
||||||
"abort": {
|
"abort": {
|
||||||
"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%]",
|
||||||
|
"addon_info_failed": "Failed to get Z-Wave JS add-on info.",
|
||||||
|
"addon_install_failed": "Failed to install the Z-Wave JS add-on.",
|
||||||
|
"addon_set_config_failed": "Failed to set Z-Wave JS configuration.",
|
||||||
|
"addon_get_discovery_info_failed": "Failed to get Z-Wave JS add-on discovery info.",
|
||||||
|
"addon_missing_discovery_info": "Missing Z-Wave JS add-on discovery info.",
|
||||||
|
"cannot_connect": "[%key:common::config_flow::error::cannot_connect%]"
|
||||||
|
},
|
||||||
|
"progress": {
|
||||||
|
"install_addon": "Please wait while the Z-Wave JS add-on installation finishes. This can take several minutes."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,18 +1,49 @@
|
|||||||
{
|
{
|
||||||
"config": {
|
"config": {
|
||||||
"abort": {
|
"abort": {
|
||||||
"already_configured": "Device is already configured"
|
"addon_get_discovery_info_failed": "Failed to get Z-Wave JS add-on discovery info.",
|
||||||
|
"addon_info_failed": "Failed to get Z-Wave JS add-on info.",
|
||||||
|
"addon_install_failed": "Failed to install the Z-Wave JS add-on.",
|
||||||
|
"addon_missing_discovery_info": "Missing Z-Wave JS add-on discovery info.",
|
||||||
|
"addon_set_config_failed": "Failed to set Z-Wave JS configuration.",
|
||||||
|
"already_configured": "Device is already configured",
|
||||||
|
"already_in_progress": "Configuration flow is already in progress",
|
||||||
|
"cannot_connect": "Failed to connect"
|
||||||
},
|
},
|
||||||
"error": {
|
"error": {
|
||||||
|
"addon_start_failed": "Failed to start the Z-Wave JS add-on. Check the configuration.",
|
||||||
"cannot_connect": "Failed to connect",
|
"cannot_connect": "Failed to connect",
|
||||||
"invalid_ws_url": "Invalid websocket URL",
|
"invalid_ws_url": "Invalid websocket URL",
|
||||||
"unknown": "Unexpected error"
|
"unknown": "Unexpected error"
|
||||||
},
|
},
|
||||||
|
"progress": {
|
||||||
|
"install_addon": "Please wait while the Z-Wave JS add-on installation finishes. This can take several minutes."
|
||||||
|
},
|
||||||
"step": {
|
"step": {
|
||||||
"user": {
|
"hassio_confirm": {
|
||||||
|
"title": "Set up Z-Wave JS integration with the Z-Wave JS add-on"
|
||||||
|
},
|
||||||
|
"install_addon": {
|
||||||
|
"title": "The Z-Wave JS add-on installation has started"
|
||||||
|
},
|
||||||
|
"manual": {
|
||||||
"data": {
|
"data": {
|
||||||
"url": "URL"
|
"url": "URL"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"on_supervisor": {
|
||||||
|
"data": {
|
||||||
|
"use_addon": "Use the Z-Wave JS Supervisor add-on"
|
||||||
|
},
|
||||||
|
"description": "Do you want to use the Z-Wave JS Supervisor add-on?",
|
||||||
|
"title": "Select connection method"
|
||||||
|
},
|
||||||
|
"start_addon": {
|
||||||
|
"data": {
|
||||||
|
"network_key": "Network Key",
|
||||||
|
"usb_path": "USB Device Path"
|
||||||
|
},
|
||||||
|
"title": "Enter the Z-Wave JS add-on configuration"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -21,6 +21,29 @@ async def device_registry_fixture(hass):
|
|||||||
return await async_get_device_registry(hass)
|
return await async_get_device_registry(hass)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(name="discovery_info")
|
||||||
|
def discovery_info_fixture():
|
||||||
|
"""Return the discovery info from the supervisor."""
|
||||||
|
return DEFAULT
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(name="discovery_info_side_effect")
|
||||||
|
def discovery_info_side_effect_fixture():
|
||||||
|
"""Return the discovery info from the supervisor."""
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(name="get_addon_discovery_info")
|
||||||
|
def mock_get_addon_discovery_info(discovery_info, discovery_info_side_effect):
|
||||||
|
"""Mock get add-on discovery info."""
|
||||||
|
with patch(
|
||||||
|
"homeassistant.components.hassio.async_get_addon_discovery_info",
|
||||||
|
side_effect=discovery_info_side_effect,
|
||||||
|
return_value=discovery_info,
|
||||||
|
) as get_addon_discovery_info:
|
||||||
|
yield get_addon_discovery_info
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(name="controller_state", scope="session")
|
@pytest.fixture(name="controller_state", scope="session")
|
||||||
def controller_state_fixture():
|
def controller_state_fixture():
|
||||||
"""Load the controller state fixture data."""
|
"""Load the controller state fixture data."""
|
||||||
|
@ -2,14 +2,141 @@
|
|||||||
import asyncio
|
import asyncio
|
||||||
from unittest.mock import patch
|
from unittest.mock import patch
|
||||||
|
|
||||||
|
import pytest
|
||||||
from zwave_js_server.version import VersionInfo
|
from zwave_js_server.version import VersionInfo
|
||||||
|
|
||||||
from homeassistant import config_entries, setup
|
from homeassistant import config_entries, setup
|
||||||
|
from homeassistant.components.hassio.handler import HassioAPIError
|
||||||
|
from homeassistant.components.zwave_js.config_flow import TITLE
|
||||||
from homeassistant.components.zwave_js.const import DOMAIN
|
from homeassistant.components.zwave_js.const import DOMAIN
|
||||||
|
|
||||||
|
from tests.common import MockConfigEntry
|
||||||
|
|
||||||
async def test_user_step_full(hass):
|
ADDON_DISCOVERY_INFO = {
|
||||||
"""Test we create an entry with user step."""
|
"addon": "Z-Wave JS",
|
||||||
|
"host": "host1",
|
||||||
|
"port": 3001,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(name="supervisor")
|
||||||
|
def mock_supervisor_fixture():
|
||||||
|
"""Mock Supervisor."""
|
||||||
|
with patch("homeassistant.components.hassio.is_hassio", return_value=True):
|
||||||
|
yield
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(name="addon_info_side_effect")
|
||||||
|
def addon_info_side_effect_fixture():
|
||||||
|
"""Return the add-on info side effect."""
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(name="addon_info")
|
||||||
|
def mock_addon_info(addon_info_side_effect):
|
||||||
|
"""Mock Supervisor add-on info."""
|
||||||
|
with patch(
|
||||||
|
"homeassistant.components.hassio.async_get_addon_info",
|
||||||
|
side_effect=addon_info_side_effect,
|
||||||
|
) as addon_info:
|
||||||
|
addon_info.return_value = {}
|
||||||
|
yield addon_info
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(name="addon_running")
|
||||||
|
def mock_addon_running(addon_info):
|
||||||
|
"""Mock add-on already running."""
|
||||||
|
addon_info.return_value["state"] = "started"
|
||||||
|
return addon_info
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(name="addon_installed")
|
||||||
|
def mock_addon_installed(addon_info):
|
||||||
|
"""Mock add-on already installed but not running."""
|
||||||
|
addon_info.return_value["state"] = "stopped"
|
||||||
|
addon_info.return_value["version"] = "1.0"
|
||||||
|
return addon_info
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(name="addon_options")
|
||||||
|
def mock_addon_options(addon_info):
|
||||||
|
"""Mock add-on options."""
|
||||||
|
addon_info.return_value["options"] = {}
|
||||||
|
return addon_info.return_value["options"]
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(name="set_addon_options_side_effect")
|
||||||
|
def set_addon_options_side_effect_fixture():
|
||||||
|
"""Return the set add-on options side effect."""
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(name="set_addon_options")
|
||||||
|
def mock_set_addon_options(set_addon_options_side_effect):
|
||||||
|
"""Mock set add-on options."""
|
||||||
|
with patch(
|
||||||
|
"homeassistant.components.hassio.async_set_addon_options",
|
||||||
|
side_effect=set_addon_options_side_effect,
|
||||||
|
) as set_options:
|
||||||
|
yield set_options
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(name="install_addon")
|
||||||
|
def mock_install_addon():
|
||||||
|
"""Mock install add-on."""
|
||||||
|
with patch("homeassistant.components.hassio.async_install_addon") as install_addon:
|
||||||
|
yield install_addon
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(name="start_addon_side_effect")
|
||||||
|
def start_addon_side_effect_fixture():
|
||||||
|
"""Return the set add-on options side effect."""
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(name="start_addon")
|
||||||
|
def mock_start_addon(start_addon_side_effect):
|
||||||
|
"""Mock start add-on."""
|
||||||
|
with patch(
|
||||||
|
"homeassistant.components.hassio.async_start_addon",
|
||||||
|
side_effect=start_addon_side_effect,
|
||||||
|
) as start_addon:
|
||||||
|
yield start_addon
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(name="server_version_side_effect")
|
||||||
|
def server_version_side_effect_fixture():
|
||||||
|
"""Return the server version side effect."""
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(name="get_server_version", autouse=True)
|
||||||
|
def mock_get_server_version(server_version_side_effect):
|
||||||
|
"""Mock server version."""
|
||||||
|
version_info = VersionInfo(
|
||||||
|
driver_version="mock-driver-version",
|
||||||
|
server_version="mock-server-version",
|
||||||
|
home_id=1234,
|
||||||
|
)
|
||||||
|
with patch(
|
||||||
|
"homeassistant.components.zwave_js.config_flow.get_server_version",
|
||||||
|
side_effect=server_version_side_effect,
|
||||||
|
return_value=version_info,
|
||||||
|
) as mock_version:
|
||||||
|
yield mock_version
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(name="addon_setup_time", autouse=True)
|
||||||
|
def mock_addon_setup_time():
|
||||||
|
"""Mock add-on setup sleep time."""
|
||||||
|
with patch(
|
||||||
|
"homeassistant.components.zwave_js.config_flow.ADDON_SETUP_TIME", new=0
|
||||||
|
) as addon_setup_time:
|
||||||
|
yield addon_setup_time
|
||||||
|
|
||||||
|
|
||||||
|
async def test_manual(hass):
|
||||||
|
"""Test we create an entry with manual step."""
|
||||||
await setup.async_setup_component(hass, "persistent_notification", {})
|
await setup.async_setup_component(hass, "persistent_notification", {})
|
||||||
result = await hass.config_entries.flow.async_init(
|
result = await hass.config_entries.flow.async_init(
|
||||||
DOMAIN, context={"source": config_entries.SOURCE_USER}
|
DOMAIN, context={"source": config_entries.SOURCE_USER}
|
||||||
@ -17,13 +144,6 @@ async def test_user_step_full(hass):
|
|||||||
assert result["type"] == "form"
|
assert result["type"] == "form"
|
||||||
|
|
||||||
with patch(
|
with patch(
|
||||||
"homeassistant.components.zwave_js.config_flow.get_server_version",
|
|
||||||
return_value=VersionInfo(
|
|
||||||
driver_version="mock-driver-version",
|
|
||||||
server_version="mock-server-version",
|
|
||||||
home_id=1234,
|
|
||||||
),
|
|
||||||
), patch(
|
|
||||||
"homeassistant.components.zwave_js.async_setup", return_value=True
|
"homeassistant.components.zwave_js.async_setup", return_value=True
|
||||||
) as mock_setup, patch(
|
) as mock_setup, patch(
|
||||||
"homeassistant.components.zwave_js.async_setup_entry",
|
"homeassistant.components.zwave_js.async_setup_entry",
|
||||||
@ -41,59 +161,720 @@ async def test_user_step_full(hass):
|
|||||||
assert result2["title"] == "Z-Wave JS"
|
assert result2["title"] == "Z-Wave JS"
|
||||||
assert result2["data"] == {
|
assert result2["data"] == {
|
||||||
"url": "ws://localhost:3000",
|
"url": "ws://localhost:3000",
|
||||||
|
"usb_path": None,
|
||||||
|
"network_key": None,
|
||||||
|
"use_addon": False,
|
||||||
|
"integration_created_addon": False,
|
||||||
}
|
}
|
||||||
assert len(mock_setup.mock_calls) == 1
|
assert len(mock_setup.mock_calls) == 1
|
||||||
assert len(mock_setup_entry.mock_calls) == 1
|
assert len(mock_setup_entry.mock_calls) == 1
|
||||||
assert result2["result"].unique_id == 1234
|
assert result2["result"].unique_id == 1234
|
||||||
|
|
||||||
|
|
||||||
async def test_user_step_invalid_input(hass):
|
@pytest.mark.parametrize(
|
||||||
"""Test we handle invalid auth in the user step."""
|
"url, server_version_side_effect, error",
|
||||||
|
[
|
||||||
|
(
|
||||||
|
"not-ws-url",
|
||||||
|
None,
|
||||||
|
"invalid_ws_url",
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"ws://localhost:3000",
|
||||||
|
asyncio.TimeoutError,
|
||||||
|
"cannot_connect",
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"ws://localhost:3000",
|
||||||
|
Exception("Boom"),
|
||||||
|
"unknown",
|
||||||
|
),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
async def test_manual_errors(
|
||||||
|
hass,
|
||||||
|
url,
|
||||||
|
error,
|
||||||
|
):
|
||||||
|
"""Test all errors with a manual set up."""
|
||||||
result = await hass.config_entries.flow.async_init(
|
result = await hass.config_entries.flow.async_init(
|
||||||
DOMAIN, context={"source": config_entries.SOURCE_USER}
|
DOMAIN, context={"source": config_entries.SOURCE_USER}
|
||||||
)
|
)
|
||||||
|
|
||||||
with patch(
|
assert result["type"] == "form"
|
||||||
"homeassistant.components.zwave_js.config_flow.get_server_version",
|
assert result["step_id"] == "manual"
|
||||||
side_effect=asyncio.TimeoutError,
|
|
||||||
):
|
result = await hass.config_entries.flow.async_configure(
|
||||||
result2 = await hass.config_entries.flow.async_configure(
|
result["flow_id"],
|
||||||
|
{
|
||||||
|
"url": url,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
assert result["type"] == "form"
|
||||||
|
assert result["step_id"] == "manual"
|
||||||
|
assert result["errors"] == {"base": error}
|
||||||
|
|
||||||
|
|
||||||
|
async def test_manual_already_configured(hass):
|
||||||
|
"""Test that only one unique instance is allowed."""
|
||||||
|
entry = MockConfigEntry(domain=DOMAIN, data={}, title=TITLE, unique_id=1234)
|
||||||
|
entry.add_to_hass(hass)
|
||||||
|
|
||||||
|
await setup.async_setup_component(hass, "persistent_notification", {})
|
||||||
|
result = await hass.config_entries.flow.async_init(
|
||||||
|
DOMAIN, context={"source": config_entries.SOURCE_USER}
|
||||||
|
)
|
||||||
|
|
||||||
|
assert result["type"] == "form"
|
||||||
|
assert result["step_id"] == "manual"
|
||||||
|
|
||||||
|
result = await hass.config_entries.flow.async_configure(
|
||||||
result["flow_id"],
|
result["flow_id"],
|
||||||
{
|
{
|
||||||
"url": "ws://localhost:3000",
|
"url": "ws://localhost:3000",
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
assert result2["type"] == "form"
|
assert result["type"] == "abort"
|
||||||
assert result2["errors"] == {"base": "cannot_connect"}
|
assert result["reason"] == "already_configured"
|
||||||
|
|
||||||
result3 = await hass.config_entries.flow.async_configure(
|
|
||||||
result["flow_id"],
|
@pytest.mark.parametrize("discovery_info", [{"config": ADDON_DISCOVERY_INFO}])
|
||||||
{
|
async def test_supervisor_discovery(
|
||||||
"url": "not-ws-url",
|
hass, supervisor, addon_running, addon_options, get_addon_discovery_info
|
||||||
},
|
):
|
||||||
|
"""Test flow started from Supervisor discovery."""
|
||||||
|
await setup.async_setup_component(hass, "persistent_notification", {})
|
||||||
|
|
||||||
|
addon_options["device"] = "/test"
|
||||||
|
addon_options["network_key"] = "abc123"
|
||||||
|
|
||||||
|
result = await hass.config_entries.flow.async_init(
|
||||||
|
DOMAIN,
|
||||||
|
context={"source": config_entries.SOURCE_HASSIO},
|
||||||
|
data=ADDON_DISCOVERY_INFO,
|
||||||
)
|
)
|
||||||
|
|
||||||
assert result3["type"] == "form"
|
with patch(
|
||||||
assert result3["errors"] == {"base": "invalid_ws_url"}
|
"homeassistant.components.zwave_js.async_setup", return_value=True
|
||||||
|
) as mock_setup, patch(
|
||||||
|
"homeassistant.components.zwave_js.async_setup_entry",
|
||||||
|
return_value=True,
|
||||||
|
) as mock_setup_entry:
|
||||||
|
result = await hass.config_entries.flow.async_configure(result["flow_id"], {})
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
assert result["type"] == "create_entry"
|
||||||
|
assert result["title"] == TITLE
|
||||||
|
assert result["data"] == {
|
||||||
|
"url": "ws://host1:3001",
|
||||||
|
"usb_path": "/test",
|
||||||
|
"network_key": "abc123",
|
||||||
|
"use_addon": True,
|
||||||
|
"integration_created_addon": False,
|
||||||
|
}
|
||||||
|
assert len(mock_setup.mock_calls) == 1
|
||||||
|
assert len(mock_setup_entry.mock_calls) == 1
|
||||||
|
|
||||||
|
|
||||||
async def test_user_step_unexpected_exception(hass):
|
@pytest.mark.parametrize(
|
||||||
"""Test we handle unexpected exception."""
|
"discovery_info, server_version_side_effect",
|
||||||
|
[({"config": ADDON_DISCOVERY_INFO}, asyncio.TimeoutError())],
|
||||||
|
)
|
||||||
|
async def test_supervisor_discovery_cannot_connect(
|
||||||
|
hass, supervisor, get_addon_discovery_info
|
||||||
|
):
|
||||||
|
"""Test Supervisor discovery and cannot connect."""
|
||||||
|
await setup.async_setup_component(hass, "persistent_notification", {})
|
||||||
|
|
||||||
|
result = await hass.config_entries.flow.async_init(
|
||||||
|
DOMAIN,
|
||||||
|
context={"source": config_entries.SOURCE_HASSIO},
|
||||||
|
data=ADDON_DISCOVERY_INFO,
|
||||||
|
)
|
||||||
|
|
||||||
|
assert result["type"] == "abort"
|
||||||
|
assert result["reason"] == "cannot_connect"
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize("discovery_info", [{"config": ADDON_DISCOVERY_INFO}])
|
||||||
|
async def test_clean_discovery_on_user_create(
|
||||||
|
hass, supervisor, addon_running, addon_options, get_addon_discovery_info
|
||||||
|
):
|
||||||
|
"""Test discovery flow is cleaned up when a user flow is finished."""
|
||||||
|
await setup.async_setup_component(hass, "persistent_notification", {})
|
||||||
|
|
||||||
|
addon_options["device"] = "/test"
|
||||||
|
addon_options["network_key"] = "abc123"
|
||||||
|
|
||||||
|
result = await hass.config_entries.flow.async_init(
|
||||||
|
DOMAIN,
|
||||||
|
context={"source": config_entries.SOURCE_HASSIO},
|
||||||
|
data=ADDON_DISCOVERY_INFO,
|
||||||
|
)
|
||||||
|
|
||||||
|
assert result["type"] == "form"
|
||||||
|
|
||||||
result = await hass.config_entries.flow.async_init(
|
result = await hass.config_entries.flow.async_init(
|
||||||
DOMAIN, context={"source": config_entries.SOURCE_USER}
|
DOMAIN, context={"source": config_entries.SOURCE_USER}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
assert result["type"] == "form"
|
||||||
|
assert result["step_id"] == "on_supervisor"
|
||||||
|
|
||||||
|
result = await hass.config_entries.flow.async_configure(
|
||||||
|
result["flow_id"], {"use_addon": False}
|
||||||
|
)
|
||||||
|
|
||||||
|
assert result["type"] == "form"
|
||||||
|
assert result["step_id"] == "manual"
|
||||||
|
|
||||||
with patch(
|
with patch(
|
||||||
"homeassistant.components.zwave_js.config_flow.get_server_version",
|
"homeassistant.components.zwave_js.async_setup", return_value=True
|
||||||
side_effect=Exception("Boom"),
|
) as mock_setup, patch(
|
||||||
):
|
"homeassistant.components.zwave_js.async_setup_entry",
|
||||||
result2 = await hass.config_entries.flow.async_configure(
|
return_value=True,
|
||||||
|
) as mock_setup_entry:
|
||||||
|
result = await hass.config_entries.flow.async_configure(
|
||||||
result["flow_id"],
|
result["flow_id"],
|
||||||
{
|
{
|
||||||
"url": "ws://localhost:3000",
|
"url": "ws://localhost:3000",
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
assert result2["type"] == "form"
|
assert len(hass.config_entries.flow.async_progress()) == 0
|
||||||
assert result2["errors"] == {"base": "unknown"}
|
assert result["type"] == "create_entry"
|
||||||
|
assert result["title"] == TITLE
|
||||||
|
assert result["data"] == {
|
||||||
|
"url": "ws://localhost:3000",
|
||||||
|
"usb_path": None,
|
||||||
|
"network_key": None,
|
||||||
|
"use_addon": False,
|
||||||
|
"integration_created_addon": False,
|
||||||
|
}
|
||||||
|
assert len(mock_setup.mock_calls) == 1
|
||||||
|
assert len(mock_setup_entry.mock_calls) == 1
|
||||||
|
|
||||||
|
|
||||||
|
async def test_abort_discovery_with_existing_entry(
|
||||||
|
hass, supervisor, addon_running, addon_options
|
||||||
|
):
|
||||||
|
"""Test discovery flow is aborted if an entry already exists."""
|
||||||
|
await setup.async_setup_component(hass, "persistent_notification", {})
|
||||||
|
|
||||||
|
entry = MockConfigEntry(
|
||||||
|
domain=DOMAIN, data={"url": "ws://localhost:3000"}, title=TITLE, unique_id=1234
|
||||||
|
)
|
||||||
|
entry.add_to_hass(hass)
|
||||||
|
|
||||||
|
result = await hass.config_entries.flow.async_init(
|
||||||
|
DOMAIN,
|
||||||
|
context={"source": config_entries.SOURCE_HASSIO},
|
||||||
|
data=ADDON_DISCOVERY_INFO,
|
||||||
|
)
|
||||||
|
|
||||||
|
assert result["type"] == "abort"
|
||||||
|
assert result["reason"] == "already_configured"
|
||||||
|
# Assert that the entry data is updated with discovery info.
|
||||||
|
assert entry.data["url"] == "ws://host1:3001"
|
||||||
|
|
||||||
|
|
||||||
|
async def test_discovery_addon_not_running(
|
||||||
|
hass, supervisor, addon_installed, addon_options, set_addon_options, start_addon
|
||||||
|
):
|
||||||
|
"""Test discovery with add-on already installed but not running."""
|
||||||
|
addon_options["device"] = None
|
||||||
|
await setup.async_setup_component(hass, "persistent_notification", {})
|
||||||
|
|
||||||
|
result = await hass.config_entries.flow.async_init(
|
||||||
|
DOMAIN,
|
||||||
|
context={"source": config_entries.SOURCE_HASSIO},
|
||||||
|
data=ADDON_DISCOVERY_INFO,
|
||||||
|
)
|
||||||
|
|
||||||
|
assert result["step_id"] == "hassio_confirm"
|
||||||
|
assert result["type"] == "form"
|
||||||
|
|
||||||
|
result = await hass.config_entries.flow.async_configure(result["flow_id"], {})
|
||||||
|
|
||||||
|
assert result["step_id"] == "start_addon"
|
||||||
|
assert result["type"] == "form"
|
||||||
|
|
||||||
|
|
||||||
|
async def test_discovery_addon_not_installed(
|
||||||
|
hass, supervisor, addon_installed, install_addon, addon_options
|
||||||
|
):
|
||||||
|
"""Test discovery with add-on not installed."""
|
||||||
|
addon_installed.return_value["version"] = None
|
||||||
|
await setup.async_setup_component(hass, "persistent_notification", {})
|
||||||
|
|
||||||
|
result = await hass.config_entries.flow.async_init(
|
||||||
|
DOMAIN,
|
||||||
|
context={"source": config_entries.SOURCE_HASSIO},
|
||||||
|
data=ADDON_DISCOVERY_INFO,
|
||||||
|
)
|
||||||
|
|
||||||
|
assert result["step_id"] == "hassio_confirm"
|
||||||
|
assert result["type"] == "form"
|
||||||
|
|
||||||
|
result = await hass.config_entries.flow.async_configure(result["flow_id"], {})
|
||||||
|
|
||||||
|
assert result["step_id"] == "install_addon"
|
||||||
|
assert result["type"] == "progress"
|
||||||
|
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
result = await hass.config_entries.flow.async_configure(result["flow_id"])
|
||||||
|
|
||||||
|
assert result["type"] == "form"
|
||||||
|
assert result["step_id"] == "start_addon"
|
||||||
|
|
||||||
|
|
||||||
|
async def test_not_addon(hass, supervisor):
|
||||||
|
"""Test opting out of add-on on Supervisor."""
|
||||||
|
await setup.async_setup_component(hass, "persistent_notification", {})
|
||||||
|
|
||||||
|
result = await hass.config_entries.flow.async_init(
|
||||||
|
DOMAIN, context={"source": config_entries.SOURCE_USER}
|
||||||
|
)
|
||||||
|
|
||||||
|
assert result["type"] == "form"
|
||||||
|
assert result["step_id"] == "on_supervisor"
|
||||||
|
|
||||||
|
result = await hass.config_entries.flow.async_configure(
|
||||||
|
result["flow_id"], {"use_addon": False}
|
||||||
|
)
|
||||||
|
|
||||||
|
assert result["type"] == "form"
|
||||||
|
assert result["step_id"] == "manual"
|
||||||
|
|
||||||
|
with patch(
|
||||||
|
"homeassistant.components.zwave_js.async_setup", return_value=True
|
||||||
|
) as mock_setup, patch(
|
||||||
|
"homeassistant.components.zwave_js.async_setup_entry",
|
||||||
|
return_value=True,
|
||||||
|
) as mock_setup_entry:
|
||||||
|
result = await hass.config_entries.flow.async_configure(
|
||||||
|
result["flow_id"],
|
||||||
|
{
|
||||||
|
"url": "ws://localhost:3000",
|
||||||
|
},
|
||||||
|
)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
assert result["type"] == "create_entry"
|
||||||
|
assert result["title"] == TITLE
|
||||||
|
assert result["data"] == {
|
||||||
|
"url": "ws://localhost:3000",
|
||||||
|
"usb_path": None,
|
||||||
|
"network_key": None,
|
||||||
|
"use_addon": False,
|
||||||
|
"integration_created_addon": False,
|
||||||
|
}
|
||||||
|
assert len(mock_setup.mock_calls) == 1
|
||||||
|
assert len(mock_setup_entry.mock_calls) == 1
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize("discovery_info", [{"config": ADDON_DISCOVERY_INFO}])
|
||||||
|
async def test_addon_running(
|
||||||
|
hass,
|
||||||
|
supervisor,
|
||||||
|
addon_running,
|
||||||
|
addon_options,
|
||||||
|
get_addon_discovery_info,
|
||||||
|
):
|
||||||
|
"""Test add-on already running on Supervisor."""
|
||||||
|
addon_options["device"] = "/test"
|
||||||
|
addon_options["network_key"] = "abc123"
|
||||||
|
await setup.async_setup_component(hass, "persistent_notification", {})
|
||||||
|
|
||||||
|
result = await hass.config_entries.flow.async_init(
|
||||||
|
DOMAIN, context={"source": config_entries.SOURCE_USER}
|
||||||
|
)
|
||||||
|
|
||||||
|
assert result["type"] == "form"
|
||||||
|
assert result["step_id"] == "on_supervisor"
|
||||||
|
|
||||||
|
with patch(
|
||||||
|
"homeassistant.components.zwave_js.async_setup", return_value=True
|
||||||
|
) as mock_setup, patch(
|
||||||
|
"homeassistant.components.zwave_js.async_setup_entry",
|
||||||
|
return_value=True,
|
||||||
|
) as mock_setup_entry:
|
||||||
|
result = await hass.config_entries.flow.async_configure(
|
||||||
|
result["flow_id"], {"use_addon": True}
|
||||||
|
)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
assert result["type"] == "create_entry"
|
||||||
|
assert result["title"] == TITLE
|
||||||
|
assert result["data"] == {
|
||||||
|
"url": "ws://host1:3001",
|
||||||
|
"usb_path": "/test",
|
||||||
|
"network_key": "abc123",
|
||||||
|
"use_addon": True,
|
||||||
|
"integration_created_addon": False,
|
||||||
|
}
|
||||||
|
assert len(mock_setup.mock_calls) == 1
|
||||||
|
assert len(mock_setup_entry.mock_calls) == 1
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
"discovery_info, discovery_info_side_effect, server_version_side_effect, "
|
||||||
|
"addon_info_side_effect, abort_reason",
|
||||||
|
[
|
||||||
|
(
|
||||||
|
{"config": ADDON_DISCOVERY_INFO},
|
||||||
|
HassioAPIError(),
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
"addon_get_discovery_info_failed",
|
||||||
|
),
|
||||||
|
(
|
||||||
|
{"config": ADDON_DISCOVERY_INFO},
|
||||||
|
None,
|
||||||
|
asyncio.TimeoutError,
|
||||||
|
None,
|
||||||
|
"cannot_connect",
|
||||||
|
),
|
||||||
|
(
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
"addon_missing_discovery_info",
|
||||||
|
),
|
||||||
|
(
|
||||||
|
{"config": ADDON_DISCOVERY_INFO},
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
HassioAPIError(),
|
||||||
|
"addon_info_failed",
|
||||||
|
),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
async def test_addon_running_failures(
|
||||||
|
hass,
|
||||||
|
supervisor,
|
||||||
|
addon_running,
|
||||||
|
get_addon_discovery_info,
|
||||||
|
abort_reason,
|
||||||
|
):
|
||||||
|
"""Test all failures when add-on is running."""
|
||||||
|
await setup.async_setup_component(hass, "persistent_notification", {})
|
||||||
|
|
||||||
|
result = await hass.config_entries.flow.async_init(
|
||||||
|
DOMAIN, context={"source": config_entries.SOURCE_USER}
|
||||||
|
)
|
||||||
|
|
||||||
|
assert result["type"] == "form"
|
||||||
|
assert result["step_id"] == "on_supervisor"
|
||||||
|
|
||||||
|
result = await hass.config_entries.flow.async_configure(
|
||||||
|
result["flow_id"], {"use_addon": True}
|
||||||
|
)
|
||||||
|
|
||||||
|
assert result["type"] == "abort"
|
||||||
|
assert result["reason"] == abort_reason
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize("discovery_info", [{"config": ADDON_DISCOVERY_INFO}])
|
||||||
|
async def test_addon_running_already_configured(
|
||||||
|
hass, supervisor, addon_running, get_addon_discovery_info
|
||||||
|
):
|
||||||
|
"""Test that only one unique instance is allowed when add-on is running."""
|
||||||
|
entry = MockConfigEntry(domain=DOMAIN, data={}, title=TITLE, unique_id=1234)
|
||||||
|
entry.add_to_hass(hass)
|
||||||
|
|
||||||
|
await setup.async_setup_component(hass, "persistent_notification", {})
|
||||||
|
result = await hass.config_entries.flow.async_init(
|
||||||
|
DOMAIN, context={"source": config_entries.SOURCE_USER}
|
||||||
|
)
|
||||||
|
|
||||||
|
assert result["type"] == "form"
|
||||||
|
assert result["step_id"] == "on_supervisor"
|
||||||
|
|
||||||
|
result = await hass.config_entries.flow.async_configure(
|
||||||
|
result["flow_id"], {"use_addon": True}
|
||||||
|
)
|
||||||
|
|
||||||
|
assert result["type"] == "abort"
|
||||||
|
assert result["reason"] == "already_configured"
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize("discovery_info", [{"config": ADDON_DISCOVERY_INFO}])
|
||||||
|
async def test_addon_installed(
|
||||||
|
hass,
|
||||||
|
supervisor,
|
||||||
|
addon_installed,
|
||||||
|
addon_options,
|
||||||
|
set_addon_options,
|
||||||
|
start_addon,
|
||||||
|
get_addon_discovery_info,
|
||||||
|
):
|
||||||
|
"""Test add-on already installed but not running on Supervisor."""
|
||||||
|
await setup.async_setup_component(hass, "persistent_notification", {})
|
||||||
|
|
||||||
|
result = await hass.config_entries.flow.async_init(
|
||||||
|
DOMAIN, context={"source": config_entries.SOURCE_USER}
|
||||||
|
)
|
||||||
|
|
||||||
|
assert result["type"] == "form"
|
||||||
|
assert result["step_id"] == "on_supervisor"
|
||||||
|
|
||||||
|
result = await hass.config_entries.flow.async_configure(
|
||||||
|
result["flow_id"], {"use_addon": True}
|
||||||
|
)
|
||||||
|
|
||||||
|
assert result["type"] == "form"
|
||||||
|
assert result["step_id"] == "start_addon"
|
||||||
|
|
||||||
|
with patch(
|
||||||
|
"homeassistant.components.zwave_js.async_setup", return_value=True
|
||||||
|
) as mock_setup, patch(
|
||||||
|
"homeassistant.components.zwave_js.async_setup_entry",
|
||||||
|
return_value=True,
|
||||||
|
) as mock_setup_entry:
|
||||||
|
result = await hass.config_entries.flow.async_configure(
|
||||||
|
result["flow_id"], {"usb_path": "/test", "network_key": "abc123"}
|
||||||
|
)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
assert result["type"] == "create_entry"
|
||||||
|
assert result["title"] == TITLE
|
||||||
|
assert result["data"] == {
|
||||||
|
"url": "ws://host1:3001",
|
||||||
|
"usb_path": "/test",
|
||||||
|
"network_key": "abc123",
|
||||||
|
"use_addon": True,
|
||||||
|
"integration_created_addon": False,
|
||||||
|
}
|
||||||
|
assert len(mock_setup.mock_calls) == 1
|
||||||
|
assert len(mock_setup_entry.mock_calls) == 1
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
"discovery_info, start_addon_side_effect",
|
||||||
|
[({"config": ADDON_DISCOVERY_INFO}, HassioAPIError())],
|
||||||
|
)
|
||||||
|
async def test_addon_installed_start_failure(
|
||||||
|
hass,
|
||||||
|
supervisor,
|
||||||
|
addon_installed,
|
||||||
|
addon_options,
|
||||||
|
set_addon_options,
|
||||||
|
start_addon,
|
||||||
|
get_addon_discovery_info,
|
||||||
|
):
|
||||||
|
"""Test add-on start failure when add-on is installed."""
|
||||||
|
await setup.async_setup_component(hass, "persistent_notification", {})
|
||||||
|
|
||||||
|
result = await hass.config_entries.flow.async_init(
|
||||||
|
DOMAIN, context={"source": config_entries.SOURCE_USER}
|
||||||
|
)
|
||||||
|
|
||||||
|
assert result["type"] == "form"
|
||||||
|
assert result["step_id"] == "on_supervisor"
|
||||||
|
|
||||||
|
result = await hass.config_entries.flow.async_configure(
|
||||||
|
result["flow_id"], {"use_addon": True}
|
||||||
|
)
|
||||||
|
|
||||||
|
assert result["type"] == "form"
|
||||||
|
assert result["step_id"] == "start_addon"
|
||||||
|
|
||||||
|
result = await hass.config_entries.flow.async_configure(
|
||||||
|
result["flow_id"], {"usb_path": "/test", "network_key": "abc123"}
|
||||||
|
)
|
||||||
|
|
||||||
|
assert result["type"] == "form"
|
||||||
|
assert result["errors"] == {"base": "addon_start_failed"}
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
"set_addon_options_side_effect, start_addon_side_effect, discovery_info, "
|
||||||
|
"server_version_side_effect, abort_reason",
|
||||||
|
[
|
||||||
|
(
|
||||||
|
HassioAPIError(),
|
||||||
|
None,
|
||||||
|
{"config": ADDON_DISCOVERY_INFO},
|
||||||
|
None,
|
||||||
|
"addon_set_config_failed",
|
||||||
|
),
|
||||||
|
(
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
{"config": ADDON_DISCOVERY_INFO},
|
||||||
|
asyncio.TimeoutError,
|
||||||
|
"cannot_connect",
|
||||||
|
),
|
||||||
|
(
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
"addon_missing_discovery_info",
|
||||||
|
),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
async def test_addon_installed_failures(
|
||||||
|
hass,
|
||||||
|
supervisor,
|
||||||
|
addon_installed,
|
||||||
|
addon_options,
|
||||||
|
set_addon_options,
|
||||||
|
start_addon,
|
||||||
|
get_addon_discovery_info,
|
||||||
|
abort_reason,
|
||||||
|
):
|
||||||
|
"""Test all failures when add-on is installed."""
|
||||||
|
await setup.async_setup_component(hass, "persistent_notification", {})
|
||||||
|
|
||||||
|
result = await hass.config_entries.flow.async_init(
|
||||||
|
DOMAIN, context={"source": config_entries.SOURCE_USER}
|
||||||
|
)
|
||||||
|
|
||||||
|
assert result["type"] == "form"
|
||||||
|
assert result["step_id"] == "on_supervisor"
|
||||||
|
|
||||||
|
result = await hass.config_entries.flow.async_configure(
|
||||||
|
result["flow_id"], {"use_addon": True}
|
||||||
|
)
|
||||||
|
|
||||||
|
assert result["type"] == "form"
|
||||||
|
assert result["step_id"] == "start_addon"
|
||||||
|
|
||||||
|
result = await hass.config_entries.flow.async_configure(
|
||||||
|
result["flow_id"], {"usb_path": "/test", "network_key": "abc123"}
|
||||||
|
)
|
||||||
|
|
||||||
|
assert result["type"] == "abort"
|
||||||
|
assert result["reason"] == abort_reason
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize("discovery_info", [{"config": ADDON_DISCOVERY_INFO}])
|
||||||
|
async def test_addon_installed_already_configured(
|
||||||
|
hass,
|
||||||
|
supervisor,
|
||||||
|
addon_installed,
|
||||||
|
addon_options,
|
||||||
|
set_addon_options,
|
||||||
|
start_addon,
|
||||||
|
get_addon_discovery_info,
|
||||||
|
):
|
||||||
|
"""Test that only one unique instance is allowed when add-on is installed."""
|
||||||
|
entry = MockConfigEntry(domain=DOMAIN, data={}, title=TITLE, unique_id=1234)
|
||||||
|
entry.add_to_hass(hass)
|
||||||
|
|
||||||
|
await setup.async_setup_component(hass, "persistent_notification", {})
|
||||||
|
result = await hass.config_entries.flow.async_init(
|
||||||
|
DOMAIN, context={"source": config_entries.SOURCE_USER}
|
||||||
|
)
|
||||||
|
|
||||||
|
assert result["type"] == "form"
|
||||||
|
assert result["step_id"] == "on_supervisor"
|
||||||
|
|
||||||
|
result = await hass.config_entries.flow.async_configure(
|
||||||
|
result["flow_id"], {"use_addon": True}
|
||||||
|
)
|
||||||
|
|
||||||
|
assert result["type"] == "form"
|
||||||
|
assert result["step_id"] == "start_addon"
|
||||||
|
|
||||||
|
result = await hass.config_entries.flow.async_configure(
|
||||||
|
result["flow_id"], {"usb_path": "/test", "network_key": "abc123"}
|
||||||
|
)
|
||||||
|
|
||||||
|
assert result["type"] == "abort"
|
||||||
|
assert result["reason"] == "already_configured"
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize("discovery_info", [{"config": ADDON_DISCOVERY_INFO}])
|
||||||
|
async def test_addon_not_installed(
|
||||||
|
hass,
|
||||||
|
supervisor,
|
||||||
|
addon_installed,
|
||||||
|
install_addon,
|
||||||
|
addon_options,
|
||||||
|
set_addon_options,
|
||||||
|
start_addon,
|
||||||
|
get_addon_discovery_info,
|
||||||
|
):
|
||||||
|
"""Test add-on not installed."""
|
||||||
|
addon_installed.return_value["version"] = None
|
||||||
|
await setup.async_setup_component(hass, "persistent_notification", {})
|
||||||
|
|
||||||
|
result = await hass.config_entries.flow.async_init(
|
||||||
|
DOMAIN, context={"source": config_entries.SOURCE_USER}
|
||||||
|
)
|
||||||
|
|
||||||
|
assert result["type"] == "form"
|
||||||
|
assert result["step_id"] == "on_supervisor"
|
||||||
|
|
||||||
|
result = await hass.config_entries.flow.async_configure(
|
||||||
|
result["flow_id"], {"use_addon": True}
|
||||||
|
)
|
||||||
|
|
||||||
|
assert result["type"] == "progress"
|
||||||
|
|
||||||
|
# Make sure the flow continues when the progress task is done.
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
result = await hass.config_entries.flow.async_configure(result["flow_id"])
|
||||||
|
|
||||||
|
assert result["type"] == "form"
|
||||||
|
assert result["step_id"] == "start_addon"
|
||||||
|
|
||||||
|
with patch(
|
||||||
|
"homeassistant.components.zwave_js.async_setup", return_value=True
|
||||||
|
) as mock_setup, patch(
|
||||||
|
"homeassistant.components.zwave_js.async_setup_entry",
|
||||||
|
return_value=True,
|
||||||
|
) as mock_setup_entry:
|
||||||
|
result = await hass.config_entries.flow.async_configure(
|
||||||
|
result["flow_id"], {"usb_path": "/test", "network_key": "abc123"}
|
||||||
|
)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
assert result["type"] == "create_entry"
|
||||||
|
assert result["title"] == TITLE
|
||||||
|
assert result["data"] == {
|
||||||
|
"url": "ws://host1:3001",
|
||||||
|
"usb_path": "/test",
|
||||||
|
"network_key": "abc123",
|
||||||
|
"use_addon": True,
|
||||||
|
"integration_created_addon": True,
|
||||||
|
}
|
||||||
|
assert len(mock_setup.mock_calls) == 1
|
||||||
|
assert len(mock_setup_entry.mock_calls) == 1
|
||||||
|
|
||||||
|
|
||||||
|
async def test_install_addon_failure(hass, supervisor, addon_installed, install_addon):
|
||||||
|
"""Test add-on install failure."""
|
||||||
|
addon_installed.return_value["version"] = None
|
||||||
|
install_addon.side_effect = HassioAPIError()
|
||||||
|
await setup.async_setup_component(hass, "persistent_notification", {})
|
||||||
|
|
||||||
|
result = await hass.config_entries.flow.async_init(
|
||||||
|
DOMAIN, context={"source": config_entries.SOURCE_USER}
|
||||||
|
)
|
||||||
|
|
||||||
|
assert result["type"] == "form"
|
||||||
|
assert result["step_id"] == "on_supervisor"
|
||||||
|
|
||||||
|
result = await hass.config_entries.flow.async_configure(
|
||||||
|
result["flow_id"], {"use_addon": True}
|
||||||
|
)
|
||||||
|
|
||||||
|
assert result["type"] == "progress"
|
||||||
|
|
||||||
|
# Make sure the flow continues when the progress task is done.
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
result = await hass.config_entries.flow.async_configure(result["flow_id"])
|
||||||
|
|
||||||
|
assert result["type"] == "abort"
|
||||||
|
assert result["reason"] == "addon_install_failed"
|
||||||
|
@ -5,8 +5,10 @@ from unittest.mock import patch
|
|||||||
import pytest
|
import pytest
|
||||||
from zwave_js_server.model.node import Node
|
from zwave_js_server.model.node import Node
|
||||||
|
|
||||||
|
from homeassistant.components.hassio.handler import HassioAPIError
|
||||||
from homeassistant.components.zwave_js.const import DOMAIN
|
from homeassistant.components.zwave_js.const import DOMAIN
|
||||||
from homeassistant.config_entries import (
|
from homeassistant.config_entries import (
|
||||||
|
CONN_CLASS_LOCAL_PUSH,
|
||||||
ENTRY_STATE_LOADED,
|
ENTRY_STATE_LOADED,
|
||||||
ENTRY_STATE_NOT_LOADED,
|
ENTRY_STATE_NOT_LOADED,
|
||||||
ENTRY_STATE_SETUP_RETRY,
|
ENTRY_STATE_SETUP_RETRY,
|
||||||
@ -25,6 +27,22 @@ def connect_timeout_fixture():
|
|||||||
yield timeout
|
yield timeout
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(name="stop_addon")
|
||||||
|
def stop_addon_fixture():
|
||||||
|
"""Mock stop add-on."""
|
||||||
|
with patch("homeassistant.components.hassio.async_stop_addon") as stop_addon:
|
||||||
|
yield stop_addon
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(name="uninstall_addon")
|
||||||
|
def uninstall_addon_fixture():
|
||||||
|
"""Mock uninstall add-on."""
|
||||||
|
with patch(
|
||||||
|
"homeassistant.components.hassio.async_uninstall_addon"
|
||||||
|
) as uninstall_addon:
|
||||||
|
yield uninstall_addon
|
||||||
|
|
||||||
|
|
||||||
async def test_entry_setup_unload(hass, client, integration):
|
async def test_entry_setup_unload(hass, client, integration):
|
||||||
"""Test the integration set up and unload."""
|
"""Test the integration set up and unload."""
|
||||||
entry = integration
|
entry = integration
|
||||||
@ -205,3 +223,70 @@ async def test_existing_node_not_ready(hass, client, multisensor_6, device_regis
|
|||||||
assert device_registry.async_get_device(
|
assert device_registry.async_get_device(
|
||||||
identifiers={(DOMAIN, air_temperature_device_id)}
|
identifiers={(DOMAIN, air_temperature_device_id)}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
async def test_remove_entry(hass, stop_addon, uninstall_addon, caplog):
|
||||||
|
"""Test remove the config entry."""
|
||||||
|
# test successful remove without created add-on
|
||||||
|
entry = MockConfigEntry(
|
||||||
|
domain=DOMAIN,
|
||||||
|
title="Z-Wave JS",
|
||||||
|
connection_class=CONN_CLASS_LOCAL_PUSH,
|
||||||
|
data={"integration_created_addon": False},
|
||||||
|
)
|
||||||
|
entry.add_to_hass(hass)
|
||||||
|
assert entry.state == ENTRY_STATE_NOT_LOADED
|
||||||
|
assert len(hass.config_entries.async_entries(DOMAIN)) == 1
|
||||||
|
|
||||||
|
await hass.config_entries.async_remove(entry.entry_id)
|
||||||
|
|
||||||
|
assert entry.state == ENTRY_STATE_NOT_LOADED
|
||||||
|
assert len(hass.config_entries.async_entries(DOMAIN)) == 0
|
||||||
|
|
||||||
|
# test successful remove with created add-on
|
||||||
|
entry = MockConfigEntry(
|
||||||
|
domain=DOMAIN,
|
||||||
|
title="Z-Wave JS",
|
||||||
|
connection_class=CONN_CLASS_LOCAL_PUSH,
|
||||||
|
data={"integration_created_addon": True},
|
||||||
|
)
|
||||||
|
entry.add_to_hass(hass)
|
||||||
|
assert len(hass.config_entries.async_entries(DOMAIN)) == 1
|
||||||
|
|
||||||
|
await hass.config_entries.async_remove(entry.entry_id)
|
||||||
|
|
||||||
|
assert stop_addon.call_count == 1
|
||||||
|
assert uninstall_addon.call_count == 1
|
||||||
|
assert entry.state == ENTRY_STATE_NOT_LOADED
|
||||||
|
assert len(hass.config_entries.async_entries(DOMAIN)) == 0
|
||||||
|
stop_addon.reset_mock()
|
||||||
|
uninstall_addon.reset_mock()
|
||||||
|
|
||||||
|
# test add-on stop failure
|
||||||
|
entry.add_to_hass(hass)
|
||||||
|
assert len(hass.config_entries.async_entries(DOMAIN)) == 1
|
||||||
|
stop_addon.side_effect = HassioAPIError()
|
||||||
|
|
||||||
|
await hass.config_entries.async_remove(entry.entry_id)
|
||||||
|
|
||||||
|
assert stop_addon.call_count == 1
|
||||||
|
assert uninstall_addon.call_count == 0
|
||||||
|
assert entry.state == ENTRY_STATE_NOT_LOADED
|
||||||
|
assert len(hass.config_entries.async_entries(DOMAIN)) == 0
|
||||||
|
assert "Failed to stop the Z-Wave JS add-on" in caplog.text
|
||||||
|
stop_addon.side_effect = None
|
||||||
|
stop_addon.reset_mock()
|
||||||
|
uninstall_addon.reset_mock()
|
||||||
|
|
||||||
|
# test add-on uninstall failure
|
||||||
|
entry.add_to_hass(hass)
|
||||||
|
assert len(hass.config_entries.async_entries(DOMAIN)) == 1
|
||||||
|
uninstall_addon.side_effect = HassioAPIError()
|
||||||
|
|
||||||
|
await hass.config_entries.async_remove(entry.entry_id)
|
||||||
|
|
||||||
|
assert stop_addon.call_count == 1
|
||||||
|
assert uninstall_addon.call_count == 1
|
||||||
|
assert entry.state == ENTRY_STATE_NOT_LOADED
|
||||||
|
assert len(hass.config_entries.async_entries(DOMAIN)) == 0
|
||||||
|
assert "Failed to uninstall the Z-Wave JS add-on" in caplog.text
|
||||||
|
Loading…
x
Reference in New Issue
Block a user