mirror of
https://github.com/home-assistant/core.git
synced 2025-07-19 03:07:37 +00:00
Add ozw config flow add-on management (#42421)
Co-authored-by: Paulus Schoutsen <balloob@gmail.com>
This commit is contained in:
parent
a7bd521768
commit
6c5ef0970a
@ -145,7 +145,7 @@ async def async_install_addon(hass: HomeAssistantType, slug: str) -> dict:
|
|||||||
"""
|
"""
|
||||||
hassio = hass.data[DOMAIN]
|
hassio = hass.data[DOMAIN]
|
||||||
command = f"/addons/{slug}/install"
|
command = f"/addons/{slug}/install"
|
||||||
return await hassio.send_command(command)
|
return await hassio.send_command(command, timeout=None)
|
||||||
|
|
||||||
|
|
||||||
@bind_hass
|
@bind_hass
|
||||||
@ -169,7 +169,7 @@ async def async_start_addon(hass: HomeAssistantType, slug: str) -> dict:
|
|||||||
"""
|
"""
|
||||||
hassio = hass.data[DOMAIN]
|
hassio = hass.data[DOMAIN]
|
||||||
command = f"/addons/{slug}/start"
|
command = f"/addons/{slug}/start"
|
||||||
return await hassio.send_command(command)
|
return await hassio.send_command(command, timeout=60)
|
||||||
|
|
||||||
|
|
||||||
@bind_hass
|
@bind_hass
|
||||||
|
@ -20,6 +20,7 @@ from openzwavemqtt.models.value import OZWValue
|
|||||||
import voluptuous as vol
|
import voluptuous as vol
|
||||||
|
|
||||||
from homeassistant.components import mqtt
|
from homeassistant.components import mqtt
|
||||||
|
from homeassistant.components.hassio.handler import HassioAPIError
|
||||||
from homeassistant.config_entries import ConfigEntry
|
from homeassistant.config_entries import ConfigEntry
|
||||||
from homeassistant.core import HomeAssistant, callback
|
from homeassistant.core import HomeAssistant, callback
|
||||||
from homeassistant.helpers.device_registry import async_get_registry as get_dev_reg
|
from homeassistant.helpers.device_registry import async_get_registry as get_dev_reg
|
||||||
@ -27,6 +28,7 @@ from homeassistant.helpers.dispatcher import async_dispatcher_send
|
|||||||
|
|
||||||
from . import const
|
from . import const
|
||||||
from .const import (
|
from .const import (
|
||||||
|
CONF_INTEGRATION_CREATED_ADDON,
|
||||||
DATA_UNSUBSCRIBE,
|
DATA_UNSUBSCRIBE,
|
||||||
DOMAIN,
|
DOMAIN,
|
||||||
MANAGER,
|
MANAGER,
|
||||||
@ -265,6 +267,22 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry):
|
|||||||
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")
|
||||||
|
except HassioAPIError as err:
|
||||||
|
_LOGGER.error("Failed to stop the OpenZWave add-on: %s", err)
|
||||||
|
return
|
||||||
|
try:
|
||||||
|
await hass.components.hassio.async_uninstall_addon("core_zwave")
|
||||||
|
except HassioAPIError as err:
|
||||||
|
_LOGGER.error("Failed to uninstall the OpenZWave add-on: %s", err)
|
||||||
|
|
||||||
|
|
||||||
async def async_handle_remove_node(hass: HomeAssistant, node: OZWNode):
|
async def async_handle_remove_node(hass: HomeAssistant, node: OZWNode):
|
||||||
"""Handle the removal of a Z-Wave node, removing all traces in device/entity registry."""
|
"""Handle the removal of a Z-Wave node, removing all traces in device/entity registry."""
|
||||||
dev_registry = await get_dev_reg(hass)
|
dev_registry = await get_dev_reg(hass)
|
||||||
|
@ -1,10 +1,26 @@
|
|||||||
"""Config flow for ozw integration."""
|
"""Config flow for ozw integration."""
|
||||||
from homeassistant import config_entries
|
import logging
|
||||||
|
|
||||||
|
import voluptuous as vol
|
||||||
|
|
||||||
|
from homeassistant import config_entries
|
||||||
|
from homeassistant.core import callback
|
||||||
|
from homeassistant.data_entry_flow import AbortFlow
|
||||||
|
|
||||||
|
from .const import CONF_INTEGRATION_CREATED_ADDON
|
||||||
from .const import DOMAIN # pylint:disable=unused-import
|
from .const import DOMAIN # pylint:disable=unused-import
|
||||||
|
|
||||||
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
CONF_ADDON_DEVICE = "device"
|
||||||
|
CONF_ADDON_NETWORK_KEY = "network_key"
|
||||||
|
CONF_NETWORK_KEY = "network_key"
|
||||||
|
CONF_USB_PATH = "usb_path"
|
||||||
|
CONF_USE_ADDON = "use_addon"
|
||||||
TITLE = "OpenZWave"
|
TITLE = "OpenZWave"
|
||||||
|
|
||||||
|
ON_SUPERVISOR_SCHEMA = vol.Schema({vol.Optional(CONF_USE_ADDON, default=False): bool})
|
||||||
|
|
||||||
|
|
||||||
class DomainConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
class DomainConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
||||||
"""Handle a config flow for ozw."""
|
"""Handle a config flow for ozw."""
|
||||||
@ -12,13 +28,156 @@ class DomainConfigFlow(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):
|
||||||
|
"""Set up flow instance."""
|
||||||
|
self.addon_config = None
|
||||||
|
self.network_key = None
|
||||||
|
self.usb_path = None
|
||||||
|
self.use_addon = False
|
||||||
|
# If we install the add-on we should uninstall it on entry remove.
|
||||||
|
self.integration_created_addon = False
|
||||||
|
|
||||||
async def async_step_user(self, user_input=None):
|
async def async_step_user(self, user_input=None):
|
||||||
"""Handle the initial step."""
|
"""Handle the initial step."""
|
||||||
if self._async_current_entries():
|
if self._async_current_entries():
|
||||||
return self.async_abort(reason="single_instance_allowed")
|
return self.async_abort(reason="single_instance_allowed")
|
||||||
|
|
||||||
|
# Currently all flow results need the MQTT integration.
|
||||||
|
# This will change when we have the direct MQTT client connection.
|
||||||
|
# When that is implemented, move this check to _async_use_mqtt_integration.
|
||||||
if "mqtt" not in self.hass.config.components:
|
if "mqtt" not in self.hass.config.components:
|
||||||
return self.async_abort(reason="mqtt_required")
|
return self.async_abort(reason="mqtt_required")
|
||||||
if user_input is not None:
|
|
||||||
return self.async_create_entry(title=TITLE, data={})
|
|
||||||
|
|
||||||
return self.async_show_form(step_id="user")
|
if not self.hass.components.hassio.is_hassio():
|
||||||
|
return self._async_use_mqtt_integration()
|
||||||
|
|
||||||
|
return await self.async_step_on_supervisor()
|
||||||
|
|
||||||
|
def _async_create_entry_from_vars(self):
|
||||||
|
"""Return a config entry for the flow."""
|
||||||
|
return self.async_create_entry(
|
||||||
|
title=TITLE,
|
||||||
|
data={
|
||||||
|
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,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
@callback
|
||||||
|
def _async_use_mqtt_integration(self):
|
||||||
|
"""Handle logic when using the MQTT integration.
|
||||||
|
|
||||||
|
This is the entry point for the logic that is needed
|
||||||
|
when this integration will depend on the MQTT integration.
|
||||||
|
"""
|
||||||
|
return self._async_create_entry_from_vars()
|
||||||
|
|
||||||
|
async def async_step_on_supervisor(self, user_input=None):
|
||||||
|
"""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 self._async_create_entry_from_vars()
|
||||||
|
|
||||||
|
self.use_addon = True
|
||||||
|
|
||||||
|
if await self._async_is_addon_running():
|
||||||
|
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):
|
||||||
|
"""Install OpenZWave add-on."""
|
||||||
|
try:
|
||||||
|
await self.hass.components.hassio.async_install_addon("core_zwave")
|
||||||
|
except self.hass.components.hassio.HassioAPIError as err:
|
||||||
|
_LOGGER.error("Failed to install OpenZWave add-on: %s", err)
|
||||||
|
return self.async_abort(reason="addon_install_failed")
|
||||||
|
self.integration_created_addon = True
|
||||||
|
|
||||||
|
return await self.async_step_start_addon()
|
||||||
|
|
||||||
|
async def async_step_start_addon(self, user_input=None):
|
||||||
|
"""Ask for config and start OpenZWave 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}
|
||||||
|
if self.network_key:
|
||||||
|
new_addon_config[CONF_ADDON_NETWORK_KEY] = self.network_key
|
||||||
|
|
||||||
|
if new_addon_config != self.addon_config:
|
||||||
|
await self._async_set_addon_config(new_addon_config)
|
||||||
|
|
||||||
|
try:
|
||||||
|
await self.hass.components.hassio.async_start_addon("core_zwave")
|
||||||
|
except self.hass.components.hassio.HassioAPIError as err:
|
||||||
|
_LOGGER.error("Failed to start OpenZWave add-on: %s", err)
|
||||||
|
errors["base"] = "addon_start_failed"
|
||||||
|
else:
|
||||||
|
return self._async_create_entry_from_vars()
|
||||||
|
|
||||||
|
self.usb_path = self.addon_config.get(CONF_ADDON_DEVICE, "")
|
||||||
|
self.network_key = self.addon_config.get(CONF_ADDON_NETWORK_KEY, "")
|
||||||
|
|
||||||
|
data_schema = vol.Schema(
|
||||||
|
{
|
||||||
|
vol.Required(CONF_USB_PATH, default=self.usb_path): str,
|
||||||
|
vol.Optional(CONF_NETWORK_KEY, default=self.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):
|
||||||
|
"""Return and cache OpenZWave add-on info."""
|
||||||
|
try:
|
||||||
|
addon_info = await self.hass.components.hassio.async_get_addon_info(
|
||||||
|
"core_zwave"
|
||||||
|
)
|
||||||
|
except self.hass.components.hassio.HassioAPIError as err:
|
||||||
|
_LOGGER.error("Failed to get OpenZWave add-on info: %s", err)
|
||||||
|
raise AbortFlow("addon_info_failed") from err
|
||||||
|
|
||||||
|
return addon_info
|
||||||
|
|
||||||
|
async def _async_is_addon_running(self):
|
||||||
|
"""Return True if OpenZWave add-on is running."""
|
||||||
|
addon_info = await self._async_get_addon_info()
|
||||||
|
return addon_info["state"] == "started"
|
||||||
|
|
||||||
|
async def _async_is_addon_installed(self):
|
||||||
|
"""Return True if OpenZWave 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):
|
||||||
|
"""Get OpenZWave add-on config."""
|
||||||
|
addon_info = await self._async_get_addon_info()
|
||||||
|
return addon_info["options"]
|
||||||
|
|
||||||
|
async def _async_set_addon_config(self, config):
|
||||||
|
"""Set OpenZWave add-on config."""
|
||||||
|
options = {"options": config}
|
||||||
|
try:
|
||||||
|
await self.hass.components.hassio.async_set_addon_options(
|
||||||
|
"core_zwave", options
|
||||||
|
)
|
||||||
|
except self.hass.components.hassio.HassioAPIError as err:
|
||||||
|
_LOGGER.error("Failed to set OpenZWave add-on config: %s", err)
|
||||||
|
raise AbortFlow("addon_set_config_failed") from err
|
||||||
|
@ -10,6 +10,9 @@ from homeassistant.components.switch import DOMAIN as SWITCH_DOMAIN
|
|||||||
|
|
||||||
DOMAIN = "ozw"
|
DOMAIN = "ozw"
|
||||||
DATA_UNSUBSCRIBE = "unsubscribe"
|
DATA_UNSUBSCRIBE = "unsubscribe"
|
||||||
|
|
||||||
|
CONF_INTEGRATION_CREATED_ADDON = "integration_created_addon"
|
||||||
|
|
||||||
PLATFORMS = [
|
PLATFORMS = [
|
||||||
BINARY_SENSOR_DOMAIN,
|
BINARY_SENSOR_DOMAIN,
|
||||||
COVER_DOMAIN,
|
COVER_DOMAIN,
|
||||||
|
@ -1,13 +1,25 @@
|
|||||||
{
|
{
|
||||||
"config": {
|
"config": {
|
||||||
"step": {
|
"step": {
|
||||||
"user": {
|
"on_supervisor": {
|
||||||
"title": "Confirm set up"
|
"title": "Select connection method",
|
||||||
|
"description": "Do you want to use the OpenZWave Supervisor add-on?",
|
||||||
|
"data": {"use_addon": "Use the OpenZWave Supervisor add-on"}
|
||||||
|
},
|
||||||
|
"start_addon": {
|
||||||
|
"title": "Enter the OpenZWave add-on configuration",
|
||||||
|
"data": {"usb_path": "[%key:common::config_flow::data::usb_path%]", "network_key": "Network Key"}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"abort": {
|
"abort": {
|
||||||
|
"addon_info_failed": "Failed to get OpenZWave add-on info.",
|
||||||
|
"addon_install_failed": "Failed to install the OpenZWave add-on.",
|
||||||
|
"addon_set_config_failed": "Failed to set OpenZWave configuration.",
|
||||||
"single_instance_allowed": "[%key:common::config_flow::abort::single_instance_allowed%]",
|
"single_instance_allowed": "[%key:common::config_flow::abort::single_instance_allowed%]",
|
||||||
"mqtt_required": "The MQTT integration is not set up"
|
"mqtt_required": "The MQTT integration is not set up"
|
||||||
|
},
|
||||||
|
"error": {
|
||||||
|
"addon_start_failed": "Failed to start the OpenZWave add-on. Check the configuration."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,13 +1,29 @@
|
|||||||
{
|
{
|
||||||
"config": {
|
"config": {
|
||||||
"abort": {
|
"abort": {
|
||||||
|
"addon_info_failed": "Failed to get OpenZWave add-on info.",
|
||||||
|
"addon_install_failed": "Failed to install the OpenZWave add-on.",
|
||||||
|
"addon_set_config_failed": "Failed to set OpenZWave configuration.",
|
||||||
"mqtt_required": "The MQTT integration is not set up",
|
"mqtt_required": "The MQTT integration is not set up",
|
||||||
"one_instance_allowed": "The integration only supports one Z-Wave instance",
|
|
||||||
"single_instance_allowed": "Already configured. Only a single configuration possible."
|
"single_instance_allowed": "Already configured. Only a single configuration possible."
|
||||||
},
|
},
|
||||||
|
"error": {
|
||||||
|
"addon_start_failed": "Failed to start the OpenZWave add-on. Check the configuration."
|
||||||
|
},
|
||||||
"step": {
|
"step": {
|
||||||
"user": {
|
"on_supervisor": {
|
||||||
"title": "Confirm set up"
|
"data": {
|
||||||
|
"use_addon": "Use the OpenZWave Supervisor add-on"
|
||||||
|
},
|
||||||
|
"description": "Do you want to use the OpenZWave Supervisor add-on?",
|
||||||
|
"title": "Select connection method"
|
||||||
|
},
|
||||||
|
"start_addon": {
|
||||||
|
"data": {
|
||||||
|
"network_key": "Network Key",
|
||||||
|
"usb_path": "USB Device Path"
|
||||||
|
},
|
||||||
|
"title": "Enter the OpenZWave add-on configuration"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -237,3 +237,19 @@ async def lock_msg_fixture(hass):
|
|||||||
message = MQTTMessage(topic=lock_json["topic"], payload=lock_json["payload"])
|
message = MQTTMessage(topic=lock_json["topic"], payload=lock_json["payload"])
|
||||||
message.encode()
|
message.encode()
|
||||||
return message
|
return message
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(name="stop_addon")
|
||||||
|
def mock_install_addon():
|
||||||
|
"""Mock stop add-on."""
|
||||||
|
with patch("homeassistant.components.hassio.async_stop_addon") as stop_addon:
|
||||||
|
yield stop_addon
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(name="uninstall_addon")
|
||||||
|
def mock_uninstall_addon():
|
||||||
|
"""Mock uninstall add-on."""
|
||||||
|
with patch(
|
||||||
|
"homeassistant.components.hassio.async_uninstall_addon"
|
||||||
|
) as uninstall_addon:
|
||||||
|
yield uninstall_addon
|
||||||
|
@ -1,5 +1,8 @@
|
|||||||
"""Test the Z-Wave over MQTT config flow."""
|
"""Test the Z-Wave over MQTT config flow."""
|
||||||
|
import pytest
|
||||||
|
|
||||||
from homeassistant import config_entries, setup
|
from homeassistant import config_entries, setup
|
||||||
|
from homeassistant.components.hassio.handler import HassioAPIError
|
||||||
from homeassistant.components.ozw.config_flow import TITLE
|
from homeassistant.components.ozw.config_flow import TITLE
|
||||||
from homeassistant.components.ozw.const import DOMAIN
|
from homeassistant.components.ozw.const import DOMAIN
|
||||||
|
|
||||||
@ -7,15 +10,70 @@ from tests.async_mock import patch
|
|||||||
from tests.common import MockConfigEntry
|
from tests.common import MockConfigEntry
|
||||||
|
|
||||||
|
|
||||||
async def test_user_create_entry(hass):
|
@pytest.fixture(name="supervisor")
|
||||||
"""Test the user step creates an entry."""
|
def mock_supervisor_fixture():
|
||||||
|
"""Mock Supervisor."""
|
||||||
|
with patch("homeassistant.components.hassio.is_hassio", return_value=True):
|
||||||
|
yield
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(name="addon_info")
|
||||||
|
def mock_addon_info():
|
||||||
|
"""Mock Supervisor add-on info."""
|
||||||
|
with patch("homeassistant.components.hassio.async_get_addon_info") 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
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(name="set_addon_options")
|
||||||
|
def mock_set_addon_options():
|
||||||
|
"""Mock set add-on options."""
|
||||||
|
with patch(
|
||||||
|
"homeassistant.components.hassio.async_set_addon_options"
|
||||||
|
) 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")
|
||||||
|
def mock_start_addon():
|
||||||
|
"""Mock start add-on."""
|
||||||
|
with patch("homeassistant.components.hassio.async_start_addon") as start_addon:
|
||||||
|
yield start_addon
|
||||||
|
|
||||||
|
|
||||||
|
async def test_user_not_supervisor_create_entry(hass):
|
||||||
|
"""Test the user step creates an entry not on Supervisor."""
|
||||||
hass.config.components.add("mqtt")
|
hass.config.components.add("mqtt")
|
||||||
await setup.async_setup_component(hass, "persistent_notification", {})
|
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["errors"] is None
|
|
||||||
|
|
||||||
with patch(
|
with patch(
|
||||||
"homeassistant.components.ozw.async_setup", return_value=True
|
"homeassistant.components.ozw.async_setup", return_value=True
|
||||||
@ -23,12 +81,19 @@ async def test_user_create_entry(hass):
|
|||||||
"homeassistant.components.ozw.async_setup_entry",
|
"homeassistant.components.ozw.async_setup_entry",
|
||||||
return_value=True,
|
return_value=True,
|
||||||
) as mock_setup_entry:
|
) as mock_setup_entry:
|
||||||
result2 = await hass.config_entries.flow.async_configure(result["flow_id"], {})
|
result = await hass.config_entries.flow.async_init(
|
||||||
|
DOMAIN, context={"source": config_entries.SOURCE_USER}
|
||||||
|
)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
assert result2["type"] == "create_entry"
|
assert result["type"] == "create_entry"
|
||||||
assert result2["title"] == TITLE
|
assert result["title"] == TITLE
|
||||||
assert result2["data"] == {}
|
assert result["data"] == {
|
||||||
|
"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
|
||||||
|
|
||||||
@ -52,3 +117,230 @@ async def test_one_instance_allowed(hass):
|
|||||||
)
|
)
|
||||||
assert result["type"] == "abort"
|
assert result["type"] == "abort"
|
||||||
assert result["reason"] == "single_instance_allowed"
|
assert result["reason"] == "single_instance_allowed"
|
||||||
|
|
||||||
|
|
||||||
|
async def test_not_addon(hass, supervisor):
|
||||||
|
"""Test opting out of add-on on Supervisor."""
|
||||||
|
hass.config.components.add("mqtt")
|
||||||
|
await setup.async_setup_component(hass, "persistent_notification", {})
|
||||||
|
|
||||||
|
result = await hass.config_entries.flow.async_init(
|
||||||
|
DOMAIN, context={"source": config_entries.SOURCE_USER}
|
||||||
|
)
|
||||||
|
|
||||||
|
with patch(
|
||||||
|
"homeassistant.components.ozw.async_setup", return_value=True
|
||||||
|
) as mock_setup, patch(
|
||||||
|
"homeassistant.components.ozw.async_setup_entry",
|
||||||
|
return_value=True,
|
||||||
|
) as mock_setup_entry:
|
||||||
|
result = await hass.config_entries.flow.async_configure(
|
||||||
|
result["flow_id"], {"use_addon": False}
|
||||||
|
)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
assert result["type"] == "create_entry"
|
||||||
|
assert result["title"] == TITLE
|
||||||
|
assert result["data"] == {
|
||||||
|
"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_addon_running(hass, supervisor, addon_running):
|
||||||
|
"""Test add-on already running on Supervisor."""
|
||||||
|
hass.config.components.add("mqtt")
|
||||||
|
await setup.async_setup_component(hass, "persistent_notification", {})
|
||||||
|
|
||||||
|
result = await hass.config_entries.flow.async_init(
|
||||||
|
DOMAIN, context={"source": config_entries.SOURCE_USER}
|
||||||
|
)
|
||||||
|
|
||||||
|
with patch(
|
||||||
|
"homeassistant.components.ozw.async_setup", return_value=True
|
||||||
|
) as mock_setup, patch(
|
||||||
|
"homeassistant.components.ozw.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"] == {
|
||||||
|
"usb_path": None,
|
||||||
|
"network_key": None,
|
||||||
|
"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_addon_info_failure(hass, supervisor, addon_info):
|
||||||
|
"""Test add-on info failure."""
|
||||||
|
hass.config.components.add("mqtt")
|
||||||
|
addon_info.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}
|
||||||
|
)
|
||||||
|
|
||||||
|
result = await hass.config_entries.flow.async_configure(
|
||||||
|
result["flow_id"], {"use_addon": True}
|
||||||
|
)
|
||||||
|
|
||||||
|
assert result["type"] == "abort"
|
||||||
|
assert result["reason"] == "addon_info_failed"
|
||||||
|
|
||||||
|
|
||||||
|
async def test_addon_installed(
|
||||||
|
hass, supervisor, addon_installed, addon_options, set_addon_options, start_addon
|
||||||
|
):
|
||||||
|
"""Test add-on already installed but not running on Supervisor."""
|
||||||
|
hass.config.components.add("mqtt")
|
||||||
|
await setup.async_setup_component(hass, "persistent_notification", {})
|
||||||
|
|
||||||
|
result = await hass.config_entries.flow.async_init(
|
||||||
|
DOMAIN, context={"source": config_entries.SOURCE_USER}
|
||||||
|
)
|
||||||
|
result = await hass.config_entries.flow.async_configure(
|
||||||
|
result["flow_id"], {"use_addon": True}
|
||||||
|
)
|
||||||
|
|
||||||
|
with patch(
|
||||||
|
"homeassistant.components.ozw.async_setup", return_value=True
|
||||||
|
) as mock_setup, patch(
|
||||||
|
"homeassistant.components.ozw.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"] == {
|
||||||
|
"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_set_addon_config_failure(
|
||||||
|
hass, supervisor, addon_installed, addon_options, set_addon_options
|
||||||
|
):
|
||||||
|
"""Test add-on set config failure."""
|
||||||
|
hass.config.components.add("mqtt")
|
||||||
|
set_addon_options.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}
|
||||||
|
)
|
||||||
|
result = await hass.config_entries.flow.async_configure(
|
||||||
|
result["flow_id"], {"use_addon": True}
|
||||||
|
)
|
||||||
|
|
||||||
|
result = await hass.config_entries.flow.async_configure(
|
||||||
|
result["flow_id"], {"usb_path": "/test", "network_key": "abc123"}
|
||||||
|
)
|
||||||
|
|
||||||
|
assert result["type"] == "abort"
|
||||||
|
assert result["reason"] == "addon_set_config_failed"
|
||||||
|
|
||||||
|
|
||||||
|
async def test_start_addon_failure(
|
||||||
|
hass, supervisor, addon_installed, addon_options, set_addon_options, start_addon
|
||||||
|
):
|
||||||
|
"""Test add-on start failure."""
|
||||||
|
hass.config.components.add("mqtt")
|
||||||
|
start_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}
|
||||||
|
)
|
||||||
|
result = await hass.config_entries.flow.async_configure(
|
||||||
|
result["flow_id"], {"use_addon": True}
|
||||||
|
)
|
||||||
|
|
||||||
|
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"}
|
||||||
|
|
||||||
|
|
||||||
|
async def test_addon_not_installed(
|
||||||
|
hass,
|
||||||
|
supervisor,
|
||||||
|
addon_installed,
|
||||||
|
install_addon,
|
||||||
|
addon_options,
|
||||||
|
set_addon_options,
|
||||||
|
start_addon,
|
||||||
|
):
|
||||||
|
"""Test add-on not installed."""
|
||||||
|
hass.config.components.add("mqtt")
|
||||||
|
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}
|
||||||
|
)
|
||||||
|
result = await hass.config_entries.flow.async_configure(
|
||||||
|
result["flow_id"], {"use_addon": True}
|
||||||
|
)
|
||||||
|
|
||||||
|
with patch(
|
||||||
|
"homeassistant.components.ozw.async_setup", return_value=True
|
||||||
|
) as mock_setup, patch(
|
||||||
|
"homeassistant.components.ozw.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"] == {
|
||||||
|
"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."""
|
||||||
|
hass.config.components.add("mqtt")
|
||||||
|
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}
|
||||||
|
)
|
||||||
|
result = await hass.config_entries.flow.async_configure(
|
||||||
|
result["flow_id"], {"use_addon": True}
|
||||||
|
)
|
||||||
|
|
||||||
|
assert result["type"] == "abort"
|
||||||
|
assert result["reason"] == "addon_install_failed"
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
"""Test integration initialization."""
|
"""Test integration initialization."""
|
||||||
from homeassistant import config_entries
|
from homeassistant import config_entries
|
||||||
|
from homeassistant.components.hassio.handler import HassioAPIError
|
||||||
from homeassistant.components.ozw import DOMAIN, PLATFORMS, const
|
from homeassistant.components.ozw import DOMAIN, PLATFORMS, const
|
||||||
|
|
||||||
from .common import setup_ozw
|
from .common import setup_ozw
|
||||||
@ -60,3 +61,70 @@ async def test_unload_entry(hass, generic_data, switch_msg, caplog):
|
|||||||
assert len(hass.states.async_entity_ids("switch")) == 1
|
assert len(hass.states.async_entity_ids("switch")) == 1
|
||||||
for record in caplog.records:
|
for record in caplog.records:
|
||||||
assert record.levelname != "ERROR"
|
assert record.levelname != "ERROR"
|
||||||
|
|
||||||
|
|
||||||
|
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",
|
||||||
|
connection_class=config_entries.CONN_CLASS_LOCAL_PUSH,
|
||||||
|
data={"integration_created_addon": False},
|
||||||
|
)
|
||||||
|
entry.add_to_hass(hass)
|
||||||
|
assert entry.state == config_entries.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 == config_entries.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",
|
||||||
|
connection_class=config_entries.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)
|
||||||
|
|
||||||
|
stop_addon.call_count == 1
|
||||||
|
uninstall_addon.call_count == 1
|
||||||
|
assert entry.state == config_entries.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)
|
||||||
|
|
||||||
|
stop_addon.call_count == 1
|
||||||
|
uninstall_addon.call_count == 0
|
||||||
|
assert entry.state == config_entries.ENTRY_STATE_NOT_LOADED
|
||||||
|
assert len(hass.config_entries.async_entries(DOMAIN)) == 0
|
||||||
|
assert "Failed to stop the OpenZWave 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)
|
||||||
|
|
||||||
|
stop_addon.call_count == 1
|
||||||
|
uninstall_addon.call_count == 1
|
||||||
|
assert entry.state == config_entries.ENTRY_STATE_NOT_LOADED
|
||||||
|
assert len(hass.config_entries.async_entries(DOMAIN)) == 0
|
||||||
|
assert "Failed to uninstall the OpenZWave add-on" in caplog.text
|
||||||
|
Loading…
x
Reference in New Issue
Block a user