mirror of
https://github.com/home-assistant/core.git
synced 2025-07-21 04:07:08 +00:00
Add support for USB discovery to zwave_js (#54938)
Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
This commit is contained in:
parent
33f660118f
commit
a7d8e2b817
@ -12,8 +12,9 @@ import voluptuous as vol
|
|||||||
from zwave_js_server.version import VersionInfo, get_server_version
|
from zwave_js_server.version import VersionInfo, get_server_version
|
||||||
|
|
||||||
from homeassistant import config_entries, exceptions
|
from homeassistant import config_entries, exceptions
|
||||||
|
from homeassistant.components import usb
|
||||||
from homeassistant.components.hassio import is_hassio
|
from homeassistant.components.hassio import is_hassio
|
||||||
from homeassistant.const import CONF_URL
|
from homeassistant.const import CONF_NAME, CONF_URL
|
||||||
from homeassistant.core import HomeAssistant, callback
|
from homeassistant.core import HomeAssistant, callback
|
||||||
from homeassistant.data_entry_flow import (
|
from homeassistant.data_entry_flow import (
|
||||||
AbortFlow,
|
AbortFlow,
|
||||||
@ -286,6 +287,7 @@ class ConfigFlow(BaseZwaveJSFlow, config_entries.ConfigFlow, domain=DOMAIN):
|
|||||||
"""Set up flow instance."""
|
"""Set up flow instance."""
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self.use_addon = False
|
self.use_addon = False
|
||||||
|
self._title: str | None = None
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def flow_manager(self) -> config_entries.ConfigEntriesFlowManager:
|
def flow_manager(self) -> config_entries.ConfigEntriesFlowManager:
|
||||||
@ -309,6 +311,64 @@ class ConfigFlow(BaseZwaveJSFlow, config_entries.ConfigFlow, domain=DOMAIN):
|
|||||||
|
|
||||||
return await self.async_step_manual()
|
return await self.async_step_manual()
|
||||||
|
|
||||||
|
async def async_step_usb(self, discovery_info: dict[str, str]) -> FlowResult:
|
||||||
|
"""Handle USB Discovery."""
|
||||||
|
if not is_hassio(self.hass):
|
||||||
|
return self.async_abort(reason="discovery_requires_supervisor")
|
||||||
|
if self._async_current_entries():
|
||||||
|
return self.async_abort(reason="already_configured")
|
||||||
|
if self._async_in_progress():
|
||||||
|
return self.async_abort(reason="already_in_progress")
|
||||||
|
|
||||||
|
vid = discovery_info["vid"]
|
||||||
|
pid = discovery_info["pid"]
|
||||||
|
serial_number = discovery_info["serial_number"]
|
||||||
|
device = discovery_info["device"]
|
||||||
|
manufacturer = discovery_info["manufacturer"]
|
||||||
|
description = discovery_info["description"]
|
||||||
|
# The Nortek sticks are a special case since they
|
||||||
|
# have a Z-Wave and a Zigbee radio. We need to reject
|
||||||
|
# the Zigbee radio.
|
||||||
|
if vid == "10C4" and pid == "8A2A" and "Z-Wave" not in description:
|
||||||
|
return self.async_abort(reason="not_zwave_device")
|
||||||
|
# Zooz uses this vid/pid, but so do 2652 sticks
|
||||||
|
if vid == "10C4" and pid == "EA60" and "2652" in description:
|
||||||
|
return self.async_abort(reason="not_zwave_device")
|
||||||
|
|
||||||
|
addon_info = await self._async_get_addon_info()
|
||||||
|
if addon_info.state not in (AddonState.NOT_INSTALLED, AddonState.NOT_RUNNING):
|
||||||
|
return self.async_abort(reason="already_configured")
|
||||||
|
|
||||||
|
await self.async_set_unique_id(
|
||||||
|
f"{vid}:{pid}_{serial_number}_{manufacturer}_{description}"
|
||||||
|
)
|
||||||
|
self._abort_if_unique_id_configured()
|
||||||
|
dev_path = await self.hass.async_add_executor_job(usb.get_serial_by_id, device)
|
||||||
|
self.usb_path = dev_path
|
||||||
|
self._title = usb.human_readable_device_name(
|
||||||
|
dev_path,
|
||||||
|
serial_number,
|
||||||
|
manufacturer,
|
||||||
|
description,
|
||||||
|
vid,
|
||||||
|
pid,
|
||||||
|
)
|
||||||
|
self.context["title_placeholders"] = {CONF_NAME: self._title}
|
||||||
|
return await self.async_step_usb_confirm()
|
||||||
|
|
||||||
|
async def async_step_usb_confirm(
|
||||||
|
self, user_input: dict[str, Any] | None = None
|
||||||
|
) -> FlowResult:
|
||||||
|
"""Handle USB Discovery confirmation."""
|
||||||
|
if user_input is None:
|
||||||
|
return self.async_show_form(
|
||||||
|
step_id="usb_confirm",
|
||||||
|
description_placeholders={CONF_NAME: self._title},
|
||||||
|
data_schema=vol.Schema({}),
|
||||||
|
)
|
||||||
|
|
||||||
|
return await self.async_step_on_supervisor({CONF_USE_ADDON: True})
|
||||||
|
|
||||||
async def async_step_manual(
|
async def async_step_manual(
|
||||||
self, user_input: dict[str, Any] | None = None
|
self, user_input: dict[str, Any] | None = None
|
||||||
) -> FlowResult:
|
) -> FlowResult:
|
||||||
@ -352,6 +412,9 @@ class ConfigFlow(BaseZwaveJSFlow, config_entries.ConfigFlow, domain=DOMAIN):
|
|||||||
|
|
||||||
This flow is triggered by the Z-Wave JS add-on.
|
This flow is triggered by the Z-Wave JS add-on.
|
||||||
"""
|
"""
|
||||||
|
if self._async_in_progress():
|
||||||
|
return self.async_abort(reason="already_in_progress")
|
||||||
|
|
||||||
self.ws_address = f"ws://{discovery_info['host']}:{discovery_info['port']}"
|
self.ws_address = f"ws://{discovery_info['host']}:{discovery_info['port']}"
|
||||||
try:
|
try:
|
||||||
version_info = await async_get_version_info(self.hass, self.ws_address)
|
version_info = await async_get_version_info(self.hass, self.ws_address)
|
||||||
@ -422,7 +485,7 @@ class ConfigFlow(BaseZwaveJSFlow, config_entries.ConfigFlow, domain=DOMAIN):
|
|||||||
|
|
||||||
return await self.async_step_start_addon()
|
return await self.async_step_start_addon()
|
||||||
|
|
||||||
usb_path = addon_config.get(CONF_ADDON_DEVICE, self.usb_path or "")
|
usb_path = addon_config.get(CONF_ADDON_DEVICE) or self.usb_path or ""
|
||||||
network_key = addon_config.get(CONF_ADDON_NETWORK_KEY, self.network_key or "")
|
network_key = addon_config.get(CONF_ADDON_NETWORK_KEY, self.network_key or "")
|
||||||
|
|
||||||
data_schema = vol.Schema(
|
data_schema = vol.Schema(
|
||||||
@ -446,7 +509,7 @@ class ConfigFlow(BaseZwaveJSFlow, config_entries.ConfigFlow, domain=DOMAIN):
|
|||||||
discovery_info = await self._async_get_addon_discovery_info()
|
discovery_info = await self._async_get_addon_discovery_info()
|
||||||
self.ws_address = f"ws://{discovery_info['host']}:{discovery_info['port']}"
|
self.ws_address = f"ws://{discovery_info['host']}:{discovery_info['port']}"
|
||||||
|
|
||||||
if not self.unique_id:
|
if not self.unique_id or self.context["source"] == config_entries.SOURCE_USB:
|
||||||
if not self.version_info:
|
if not self.version_info:
|
||||||
try:
|
try:
|
||||||
self.version_info = await async_get_version_info(
|
self.version_info = await async_get_version_info(
|
||||||
@ -471,6 +534,10 @@ class ConfigFlow(BaseZwaveJSFlow, config_entries.ConfigFlow, domain=DOMAIN):
|
|||||||
@callback
|
@callback
|
||||||
def _async_create_entry_from_vars(self) -> FlowResult:
|
def _async_create_entry_from_vars(self) -> FlowResult:
|
||||||
"""Return a config entry for the flow."""
|
"""Return a config entry for the flow."""
|
||||||
|
# Abort any other flows that may be in progress
|
||||||
|
for progress in self._async_in_progress():
|
||||||
|
self.hass.config_entries.flow.async_abort(progress["flow_id"])
|
||||||
|
|
||||||
return self.async_create_entry(
|
return self.async_create_entry(
|
||||||
title=TITLE,
|
title=TITLE,
|
||||||
data={
|
data={
|
||||||
|
@ -5,6 +5,11 @@
|
|||||||
"documentation": "https://www.home-assistant.io/integrations/zwave_js",
|
"documentation": "https://www.home-assistant.io/integrations/zwave_js",
|
||||||
"requirements": ["zwave-js-server-python==0.29.0"],
|
"requirements": ["zwave-js-server-python==0.29.0"],
|
||||||
"codeowners": ["@home-assistant/z-wave"],
|
"codeowners": ["@home-assistant/z-wave"],
|
||||||
"dependencies": ["http", "websocket_api"],
|
"dependencies": ["usb", "http", "websocket_api"],
|
||||||
"iot_class": "local_push"
|
"iot_class": "local_push",
|
||||||
|
"usb": [
|
||||||
|
{"vid":"0658","pid":"0200"},
|
||||||
|
{"vid":"10C4","pid":"8A2A"},
|
||||||
|
{"vid":"10C4","pid":"EA60"}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
@ -1,11 +1,15 @@
|
|||||||
{
|
{
|
||||||
"config": {
|
"config": {
|
||||||
|
"flow_title": "{name}",
|
||||||
"step": {
|
"step": {
|
||||||
"manual": {
|
"manual": {
|
||||||
"data": {
|
"data": {
|
||||||
"url": "[%key:common::config_flow::data::url%]"
|
"url": "[%key:common::config_flow::data::url%]"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"usb_confirm": {
|
||||||
|
"description": "Do you want to setup {name} with the Z-Wave JS add-on?"
|
||||||
|
},
|
||||||
"on_supervisor": {
|
"on_supervisor": {
|
||||||
"title": "Select connection method",
|
"title": "Select connection method",
|
||||||
"description": "Do you want to use the Z-Wave JS Supervisor add-on?",
|
"description": "Do you want to use the Z-Wave JS Supervisor add-on?",
|
||||||
@ -44,7 +48,9 @@
|
|||||||
"addon_set_config_failed": "Failed to set Z-Wave JS configuration.",
|
"addon_set_config_failed": "Failed to set Z-Wave JS configuration.",
|
||||||
"addon_start_failed": "Failed to start the Z-Wave JS add-on.",
|
"addon_start_failed": "Failed to start the Z-Wave JS add-on.",
|
||||||
"addon_get_discovery_info_failed": "Failed to get Z-Wave JS add-on discovery info.",
|
"addon_get_discovery_info_failed": "Failed to get Z-Wave JS add-on discovery info.",
|
||||||
"cannot_connect": "[%key:common::config_flow::error::cannot_connect%]"
|
"cannot_connect": "[%key:common::config_flow::error::cannot_connect%]",
|
||||||
|
"discovery_requires_supervisor": "Discovery requires the supervisor.",
|
||||||
|
"not_zwave_device": "Discovered device is not a Z-Wave device."
|
||||||
},
|
},
|
||||||
"progress": {
|
"progress": {
|
||||||
"install_addon": "Please wait while the Z-Wave JS add-on installation finishes. This can take several minutes.",
|
"install_addon": "Please wait while the Z-Wave JS add-on installation finishes. This can take several minutes.",
|
||||||
|
@ -8,7 +8,9 @@
|
|||||||
"addon_start_failed": "Failed to start the Z-Wave JS add-on.",
|
"addon_start_failed": "Failed to start the Z-Wave JS add-on.",
|
||||||
"already_configured": "Device is already configured",
|
"already_configured": "Device is already configured",
|
||||||
"already_in_progress": "Configuration flow is already in progress",
|
"already_in_progress": "Configuration flow is already in progress",
|
||||||
"cannot_connect": "Failed to connect"
|
"cannot_connect": "Failed to connect",
|
||||||
|
"discovery_requires_supervisor": "Discovery requires the supervisor.",
|
||||||
|
"not_zwave_device": "Discovered device is not a Z-Wave device."
|
||||||
},
|
},
|
||||||
"error": {
|
"error": {
|
||||||
"addon_start_failed": "Failed to start the Z-Wave JS add-on. Check the configuration.",
|
"addon_start_failed": "Failed to start the Z-Wave JS add-on. Check the configuration.",
|
||||||
@ -16,6 +18,7 @@
|
|||||||
"invalid_ws_url": "Invalid websocket URL",
|
"invalid_ws_url": "Invalid websocket URL",
|
||||||
"unknown": "Unexpected error"
|
"unknown": "Unexpected error"
|
||||||
},
|
},
|
||||||
|
"flow_title": "{name}",
|
||||||
"progress": {
|
"progress": {
|
||||||
"install_addon": "Please wait while the Z-Wave JS add-on installation finishes. This can take several minutes.",
|
"install_addon": "Please wait while the Z-Wave JS add-on installation finishes. This can take several minutes.",
|
||||||
"start_addon": "Please wait while the Z-Wave JS add-on start completes. This may take some seconds."
|
"start_addon": "Please wait while the Z-Wave JS add-on start completes. This may take some seconds."
|
||||||
@ -48,6 +51,9 @@
|
|||||||
},
|
},
|
||||||
"start_addon": {
|
"start_addon": {
|
||||||
"title": "The Z-Wave JS add-on is starting."
|
"title": "The Z-Wave JS add-on is starting."
|
||||||
|
},
|
||||||
|
"usb_confirm": {
|
||||||
|
"description": "Do you want to setup {name} with the Z-Wave JS add-on?"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -25,5 +25,20 @@ USB = [
|
|||||||
"domain": "zha",
|
"domain": "zha",
|
||||||
"vid": "10C4",
|
"vid": "10C4",
|
||||||
"pid": "8A2A"
|
"pid": "8A2A"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"domain": "zwave_js",
|
||||||
|
"vid": "0658",
|
||||||
|
"pid": "0200"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"domain": "zwave_js",
|
||||||
|
"vid": "10C4",
|
||||||
|
"pid": "8A2A"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"domain": "zwave_js",
|
||||||
|
"vid": "10C4",
|
||||||
|
"pid": "EA60"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
@ -20,6 +20,34 @@ ADDON_DISCOVERY_INFO = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
USB_DISCOVERY_INFO = {
|
||||||
|
"device": "/dev/zwave",
|
||||||
|
"pid": "AAAA",
|
||||||
|
"vid": "AAAA",
|
||||||
|
"serial_number": "1234",
|
||||||
|
"description": "zwave radio",
|
||||||
|
"manufacturer": "test",
|
||||||
|
}
|
||||||
|
|
||||||
|
NORTEK_ZIGBEE_DISCOVERY_INFO = {
|
||||||
|
"device": "/dev/zigbee",
|
||||||
|
"pid": "8A2A",
|
||||||
|
"vid": "10C4",
|
||||||
|
"serial_number": "1234",
|
||||||
|
"description": "nortek zigbee radio",
|
||||||
|
"manufacturer": "nortek",
|
||||||
|
}
|
||||||
|
|
||||||
|
CP2652_ZIGBEE_DISCOVERY_INFO = {
|
||||||
|
"device": "/dev/zigbee",
|
||||||
|
"pid": "EA60",
|
||||||
|
"vid": "10C4",
|
||||||
|
"serial_number": "",
|
||||||
|
"description": "cp2652",
|
||||||
|
"manufacturer": "generic",
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(name="persistent_notification", autouse=True)
|
@pytest.fixture(name="persistent_notification", autouse=True)
|
||||||
async def setup_persistent_notification(hass):
|
async def setup_persistent_notification(hass):
|
||||||
"""Set up persistent notification integration."""
|
"""Set up persistent notification integration."""
|
||||||
@ -383,6 +411,94 @@ async def test_abort_discovery_with_existing_entry(
|
|||||||
assert entry.data["url"] == "ws://host1:3001"
|
assert entry.data["url"] == "ws://host1:3001"
|
||||||
|
|
||||||
|
|
||||||
|
async def test_abort_hassio_discovery_with_existing_flow(
|
||||||
|
hass, supervisor, addon_options
|
||||||
|
):
|
||||||
|
"""Test hassio discovery flow is aborted when another discovery has happened."""
|
||||||
|
result = await hass.config_entries.flow.async_init(
|
||||||
|
DOMAIN,
|
||||||
|
context={"source": config_entries.SOURCE_USB},
|
||||||
|
data=USB_DISCOVERY_INFO,
|
||||||
|
)
|
||||||
|
assert result["type"] == "form"
|
||||||
|
assert result["step_id"] == "usb_confirm"
|
||||||
|
|
||||||
|
result2 = await hass.config_entries.flow.async_init(
|
||||||
|
DOMAIN,
|
||||||
|
context={"source": config_entries.SOURCE_HASSIO},
|
||||||
|
data=ADDON_DISCOVERY_INFO,
|
||||||
|
)
|
||||||
|
|
||||||
|
assert result2["type"] == "abort"
|
||||||
|
assert result2["reason"] == "already_in_progress"
|
||||||
|
|
||||||
|
|
||||||
|
async def test_usb_discovery(
|
||||||
|
hass,
|
||||||
|
supervisor,
|
||||||
|
install_addon,
|
||||||
|
addon_options,
|
||||||
|
get_addon_discovery_info,
|
||||||
|
set_addon_options,
|
||||||
|
start_addon,
|
||||||
|
):
|
||||||
|
"""Test usb discovery success path."""
|
||||||
|
result = await hass.config_entries.flow.async_init(
|
||||||
|
DOMAIN,
|
||||||
|
context={"source": config_entries.SOURCE_USB},
|
||||||
|
data=USB_DISCOVERY_INFO,
|
||||||
|
)
|
||||||
|
assert result["type"] == "form"
|
||||||
|
assert result["step_id"] == "usb_confirm"
|
||||||
|
|
||||||
|
result = await hass.config_entries.flow.async_configure(result["flow_id"], {})
|
||||||
|
|
||||||
|
assert result["type"] == "progress"
|
||||||
|
assert result["step_id"] == "install_addon"
|
||||||
|
|
||||||
|
# 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 install_addon.call_args == call(hass, "core_zwave_js")
|
||||||
|
|
||||||
|
assert result["type"] == "form"
|
||||||
|
assert result["step_id"] == "configure_addon"
|
||||||
|
|
||||||
|
result = await hass.config_entries.flow.async_configure(
|
||||||
|
result["flow_id"], {"usb_path": "/test", "network_key": "abc123"}
|
||||||
|
)
|
||||||
|
|
||||||
|
assert set_addon_options.call_args == call(
|
||||||
|
hass, "core_zwave_js", {"options": {"device": "/test", "network_key": "abc123"}}
|
||||||
|
)
|
||||||
|
|
||||||
|
assert result["type"] == "progress"
|
||||||
|
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:
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
result = await hass.config_entries.flow.async_configure(result["flow_id"])
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
assert start_addon.call_args == call(hass, "core_zwave_js")
|
||||||
|
|
||||||
|
assert result["type"] == "create_entry"
|
||||||
|
assert result["title"] == TITLE
|
||||||
|
assert result["data"]["usb_path"] == "/test"
|
||||||
|
assert result["data"]["integration_created_addon"] is True
|
||||||
|
assert result["data"]["use_addon"] is True
|
||||||
|
assert result["data"]["network_key"] == "abc123"
|
||||||
|
assert len(mock_setup.mock_calls) == 1
|
||||||
|
assert len(mock_setup_entry.mock_calls) == 1
|
||||||
|
|
||||||
|
|
||||||
async def test_discovery_addon_not_running(
|
async def test_discovery_addon_not_running(
|
||||||
hass, supervisor, addon_installed, addon_options, set_addon_options, start_addon
|
hass, supervisor, addon_installed, addon_options, set_addon_options, start_addon
|
||||||
):
|
):
|
||||||
@ -512,6 +628,84 @@ async def test_discovery_addon_not_installed(
|
|||||||
assert len(mock_setup_entry.mock_calls) == 1
|
assert len(mock_setup_entry.mock_calls) == 1
|
||||||
|
|
||||||
|
|
||||||
|
async def test_abort_usb_discovery_with_existing_flow(hass, supervisor, addon_options):
|
||||||
|
"""Test usb discovery flow is aborted when another discovery has happened."""
|
||||||
|
result = await hass.config_entries.flow.async_init(
|
||||||
|
DOMAIN,
|
||||||
|
context={"source": config_entries.SOURCE_HASSIO},
|
||||||
|
data=ADDON_DISCOVERY_INFO,
|
||||||
|
)
|
||||||
|
|
||||||
|
assert result["type"] == "form"
|
||||||
|
assert result["step_id"] == "hassio_confirm"
|
||||||
|
|
||||||
|
result2 = await hass.config_entries.flow.async_init(
|
||||||
|
DOMAIN,
|
||||||
|
context={"source": config_entries.SOURCE_USB},
|
||||||
|
data=USB_DISCOVERY_INFO,
|
||||||
|
)
|
||||||
|
assert result2["type"] == "abort"
|
||||||
|
assert result2["reason"] == "already_in_progress"
|
||||||
|
|
||||||
|
|
||||||
|
async def test_abort_usb_discovery_already_configured(hass, supervisor, addon_options):
|
||||||
|
"""Test usb discovery flow is aborted when there is an existing entry."""
|
||||||
|
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_USB},
|
||||||
|
data=USB_DISCOVERY_INFO,
|
||||||
|
)
|
||||||
|
assert result["type"] == "abort"
|
||||||
|
assert result["reason"] == "already_configured"
|
||||||
|
|
||||||
|
|
||||||
|
async def test_usb_discovery_requires_supervisor(hass):
|
||||||
|
"""Test usb discovery flow is aborted when there is no supervisor."""
|
||||||
|
result = await hass.config_entries.flow.async_init(
|
||||||
|
DOMAIN,
|
||||||
|
context={"source": config_entries.SOURCE_USB},
|
||||||
|
data=USB_DISCOVERY_INFO,
|
||||||
|
)
|
||||||
|
assert result["type"] == "abort"
|
||||||
|
assert result["reason"] == "discovery_requires_supervisor"
|
||||||
|
|
||||||
|
|
||||||
|
async def test_usb_discovery_already_running(hass, supervisor, addon_running):
|
||||||
|
"""Test usb discovery flow is aborted when the addon is running."""
|
||||||
|
result = await hass.config_entries.flow.async_init(
|
||||||
|
DOMAIN,
|
||||||
|
context={"source": config_entries.SOURCE_USB},
|
||||||
|
data=USB_DISCOVERY_INFO,
|
||||||
|
)
|
||||||
|
assert result["type"] == "abort"
|
||||||
|
assert result["reason"] == "already_configured"
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
"discovery_info",
|
||||||
|
[
|
||||||
|
NORTEK_ZIGBEE_DISCOVERY_INFO,
|
||||||
|
CP2652_ZIGBEE_DISCOVERY_INFO,
|
||||||
|
],
|
||||||
|
)
|
||||||
|
async def test_abort_usb_discovery_aborts_specific_devices(
|
||||||
|
hass, supervisor, addon_options, discovery_info
|
||||||
|
):
|
||||||
|
"""Test usb discovery flow is aborted on specific devices."""
|
||||||
|
result = await hass.config_entries.flow.async_init(
|
||||||
|
DOMAIN,
|
||||||
|
context={"source": config_entries.SOURCE_USB},
|
||||||
|
data=discovery_info,
|
||||||
|
)
|
||||||
|
assert result["type"] == "abort"
|
||||||
|
assert result["reason"] == "not_zwave_device"
|
||||||
|
|
||||||
|
|
||||||
async def test_not_addon(hass, supervisor):
|
async def test_not_addon(hass, supervisor):
|
||||||
"""Test opting out of add-on on Supervisor."""
|
"""Test opting out of add-on on Supervisor."""
|
||||||
await setup.async_setup_component(hass, "persistent_notification", {})
|
await setup.async_setup_component(hass, "persistent_notification", {})
|
||||||
|
Loading…
x
Reference in New Issue
Block a user