mirror of
https://github.com/home-assistant/core.git
synced 2025-07-21 12:17:07 +00:00
Support the Home Assistant Connect ZBT-1 (#114213)
This commit is contained in:
parent
63e28f958d
commit
fc34453caa
@ -14,7 +14,7 @@ from homeassistant.exceptions import ConfigEntryNotReady, HomeAssistantError
|
|||||||
from homeassistant.helpers import discovery_flow
|
from homeassistant.helpers import discovery_flow
|
||||||
|
|
||||||
from .const import DOMAIN
|
from .const import DOMAIN
|
||||||
from .util import get_usb_service_info
|
from .util import get_hardware_variant, get_usb_service_info
|
||||||
|
|
||||||
|
|
||||||
async def _async_usb_scan_done(hass: HomeAssistant, entry: ConfigEntry) -> None:
|
async def _async_usb_scan_done(hass: HomeAssistant, entry: ConfigEntry) -> None:
|
||||||
@ -46,8 +46,9 @@ async def _async_usb_scan_done(hass: HomeAssistant, entry: ConfigEntry) -> None:
|
|||||||
)
|
)
|
||||||
return
|
return
|
||||||
|
|
||||||
|
hw_variant = get_hardware_variant(entry)
|
||||||
hw_discovery_data = {
|
hw_discovery_data = {
|
||||||
"name": "SkyConnect Multiprotocol",
|
"name": f"{hw_variant.short_name} Multiprotocol",
|
||||||
"port": {
|
"port": {
|
||||||
"path": get_zigbee_socket(),
|
"path": get_zigbee_socket(),
|
||||||
},
|
},
|
||||||
|
@ -9,8 +9,8 @@ from homeassistant.components.homeassistant_hardware import silabs_multiprotocol
|
|||||||
from homeassistant.config_entries import ConfigEntry, ConfigFlow, ConfigFlowResult
|
from homeassistant.config_entries import ConfigEntry, ConfigFlow, ConfigFlowResult
|
||||||
from homeassistant.core import callback
|
from homeassistant.core import callback
|
||||||
|
|
||||||
from .const import DOMAIN
|
from .const import DOMAIN, HardwareVariant
|
||||||
from .util import get_usb_service_info
|
from .util import get_hardware_variant, get_usb_service_info
|
||||||
|
|
||||||
|
|
||||||
class HomeAssistantSkyConnectConfigFlow(ConfigFlow, domain=DOMAIN):
|
class HomeAssistantSkyConnectConfigFlow(ConfigFlow, domain=DOMAIN):
|
||||||
@ -39,8 +39,12 @@ class HomeAssistantSkyConnectConfigFlow(ConfigFlow, domain=DOMAIN):
|
|||||||
unique_id = f"{vid}:{pid}_{serial_number}_{manufacturer}_{description}"
|
unique_id = f"{vid}:{pid}_{serial_number}_{manufacturer}_{description}"
|
||||||
if await self.async_set_unique_id(unique_id):
|
if await self.async_set_unique_id(unique_id):
|
||||||
self._abort_if_unique_id_configured(updates={"device": device})
|
self._abort_if_unique_id_configured(updates={"device": device})
|
||||||
|
|
||||||
|
assert description is not None
|
||||||
|
hw_variant = HardwareVariant.from_usb_product_name(description)
|
||||||
|
|
||||||
return self.async_create_entry(
|
return self.async_create_entry(
|
||||||
title="Home Assistant SkyConnect",
|
title=hw_variant.full_name,
|
||||||
data={
|
data={
|
||||||
"device": device,
|
"device": device,
|
||||||
"vid": vid,
|
"vid": vid,
|
||||||
@ -76,10 +80,15 @@ class HomeAssistantSkyConnectOptionsFlow(silabs_multiprotocol_addon.OptionsFlowH
|
|||||||
"""
|
"""
|
||||||
return {"usb": get_usb_service_info(self.config_entry)}
|
return {"usb": get_usb_service_info(self.config_entry)}
|
||||||
|
|
||||||
|
@property
|
||||||
|
def _hw_variant(self) -> HardwareVariant:
|
||||||
|
"""Return the hardware variant."""
|
||||||
|
return get_hardware_variant(self.config_entry)
|
||||||
|
|
||||||
def _zha_name(self) -> str:
|
def _zha_name(self) -> str:
|
||||||
"""Return the ZHA name."""
|
"""Return the ZHA name."""
|
||||||
return "SkyConnect Multiprotocol"
|
return f"{self._hw_variant.short_name} Multiprotocol"
|
||||||
|
|
||||||
def _hardware_name(self) -> str:
|
def _hardware_name(self) -> str:
|
||||||
"""Return the name of the hardware."""
|
"""Return the name of the hardware."""
|
||||||
return "Home Assistant SkyConnect"
|
return self._hw_variant.full_name
|
||||||
|
@ -1,3 +1,41 @@
|
|||||||
"""Constants for the Home Assistant SkyConnect integration."""
|
"""Constants for the Home Assistant SkyConnect integration."""
|
||||||
|
|
||||||
|
import dataclasses
|
||||||
|
import enum
|
||||||
|
from typing import Self
|
||||||
|
|
||||||
DOMAIN = "homeassistant_sky_connect"
|
DOMAIN = "homeassistant_sky_connect"
|
||||||
|
|
||||||
|
|
||||||
|
@dataclasses.dataclass(frozen=True)
|
||||||
|
class VariantInfo:
|
||||||
|
"""Hardware variant information."""
|
||||||
|
|
||||||
|
usb_product_name: str
|
||||||
|
short_name: str
|
||||||
|
full_name: str
|
||||||
|
|
||||||
|
|
||||||
|
class HardwareVariant(VariantInfo, enum.Enum):
|
||||||
|
"""Hardware variants."""
|
||||||
|
|
||||||
|
SKYCONNECT = (
|
||||||
|
"SkyConnect v1.0",
|
||||||
|
"SkyConnect",
|
||||||
|
"Home Assistant SkyConnect",
|
||||||
|
)
|
||||||
|
|
||||||
|
CONNECT_ZBT1 = (
|
||||||
|
"Home Assistant Connect ZBT-1",
|
||||||
|
"Connect ZBT-1",
|
||||||
|
"Home Assistant Connect ZBT-1",
|
||||||
|
)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_usb_product_name(cls, usb_product_name: str) -> Self:
|
||||||
|
"""Get the hardware variant from the USB product name."""
|
||||||
|
for variant in cls:
|
||||||
|
if variant.value.usb_product_name == usb_product_name:
|
||||||
|
return variant
|
||||||
|
|
||||||
|
raise ValueError(f"Unknown SkyConnect product name: {usb_product_name}")
|
||||||
|
@ -6,9 +6,9 @@ from homeassistant.components.hardware.models import HardwareInfo, USBInfo
|
|||||||
from homeassistant.core import HomeAssistant, callback
|
from homeassistant.core import HomeAssistant, callback
|
||||||
|
|
||||||
from .const import DOMAIN
|
from .const import DOMAIN
|
||||||
|
from .util import get_hardware_variant
|
||||||
|
|
||||||
DOCUMENTATION_URL = "https://skyconnect.home-assistant.io/documentation/"
|
DOCUMENTATION_URL = "https://skyconnect.home-assistant.io/documentation/"
|
||||||
DONGLE_NAME = "Home Assistant SkyConnect"
|
|
||||||
|
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
@ -27,7 +27,7 @@ def async_info(hass: HomeAssistant) -> list[HardwareInfo]:
|
|||||||
manufacturer=entry.data["manufacturer"],
|
manufacturer=entry.data["manufacturer"],
|
||||||
description=entry.data["description"],
|
description=entry.data["description"],
|
||||||
),
|
),
|
||||||
name=DONGLE_NAME,
|
name=get_hardware_variant(entry).full_name,
|
||||||
url=DOCUMENTATION_URL,
|
url=DOCUMENTATION_URL,
|
||||||
)
|
)
|
||||||
for entry in entries
|
for entry in entries
|
||||||
|
@ -12,6 +12,12 @@
|
|||||||
"pid": "EA60",
|
"pid": "EA60",
|
||||||
"description": "*skyconnect v1.0*",
|
"description": "*skyconnect v1.0*",
|
||||||
"known_devices": ["SkyConnect v1.0"]
|
"known_devices": ["SkyConnect v1.0"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"vid": "10C4",
|
||||||
|
"pid": "EA60",
|
||||||
|
"description": "*home assistant connect zbt-1*",
|
||||||
|
"known_devices": ["Home Assistant Connect ZBT-1"]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,8 @@ from __future__ import annotations
|
|||||||
from homeassistant.components import usb
|
from homeassistant.components import usb
|
||||||
from homeassistant.config_entries import ConfigEntry
|
from homeassistant.config_entries import ConfigEntry
|
||||||
|
|
||||||
|
from .const import HardwareVariant
|
||||||
|
|
||||||
|
|
||||||
def get_usb_service_info(config_entry: ConfigEntry) -> usb.UsbServiceInfo:
|
def get_usb_service_info(config_entry: ConfigEntry) -> usb.UsbServiceInfo:
|
||||||
"""Return UsbServiceInfo."""
|
"""Return UsbServiceInfo."""
|
||||||
@ -16,3 +18,8 @@ def get_usb_service_info(config_entry: ConfigEntry) -> usb.UsbServiceInfo:
|
|||||||
manufacturer=config_entry.data["manufacturer"],
|
manufacturer=config_entry.data["manufacturer"],
|
||||||
description=config_entry.data["description"],
|
description=config_entry.data["description"],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def get_hardware_variant(config_entry: ConfigEntry) -> HardwareVariant:
|
||||||
|
"""Get the hardware variant from the config entry."""
|
||||||
|
return HardwareVariant.from_usb_product_name(config_entry.data["description"])
|
||||||
|
@ -59,6 +59,9 @@ async def _title(hass: HomeAssistant, discovery_info: HassioServiceInfo) -> str:
|
|||||||
if device and "SkyConnect" in device:
|
if device and "SkyConnect" in device:
|
||||||
return f"Home Assistant SkyConnect ({discovery_info.name})"
|
return f"Home Assistant SkyConnect ({discovery_info.name})"
|
||||||
|
|
||||||
|
if device and "Connect_ZBT-1" in device:
|
||||||
|
return f"Home Assistant Connect ZBT-1 ({discovery_info.name})"
|
||||||
|
|
||||||
return discovery_info.name
|
return discovery_info.name
|
||||||
|
|
||||||
|
|
||||||
|
@ -10,6 +10,12 @@ USB = [
|
|||||||
"pid": "EA60",
|
"pid": "EA60",
|
||||||
"vid": "10C4",
|
"vid": "10C4",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"description": "*home assistant connect zbt-1*",
|
||||||
|
"domain": "homeassistant_sky_connect",
|
||||||
|
"pid": "EA60",
|
||||||
|
"vid": "10C4",
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"domain": "insteon",
|
"domain": "insteon",
|
||||||
"vid": "10BF",
|
"vid": "10BF",
|
||||||
|
@ -19,13 +19,22 @@ from homeassistant.setup import async_setup_component
|
|||||||
|
|
||||||
from tests.common import MockConfigEntry, MockModule, mock_integration
|
from tests.common import MockConfigEntry, MockModule, mock_integration
|
||||||
|
|
||||||
USB_DATA = usb.UsbServiceInfo(
|
USB_DATA_SKY = usb.UsbServiceInfo(
|
||||||
device="bla_device",
|
device="/dev/serial/by-id/usb-Nabu_Casa_SkyConnect_v1.0_9e2adbd75b8beb119fe564a0f320645d-if00-port0",
|
||||||
vid="bla_vid",
|
vid="10C4",
|
||||||
pid="bla_pid",
|
pid="EA60",
|
||||||
serial_number="bla_serial_number",
|
serial_number="9e2adbd75b8beb119fe564a0f320645d",
|
||||||
manufacturer="bla_manufacturer",
|
manufacturer="Nabu Casa",
|
||||||
description="bla_description",
|
description="SkyConnect v1.0",
|
||||||
|
)
|
||||||
|
|
||||||
|
USB_DATA_ZBT1 = usb.UsbServiceInfo(
|
||||||
|
device="/dev/serial/by-id/usb-Nabu_Casa_Home_Assistant_Connect_ZBT-1_9e2adbd75b8beb119fe564a0f320645d-if00-port0",
|
||||||
|
vid="10C4",
|
||||||
|
pid="EA60",
|
||||||
|
serial_number="9e2adbd75b8beb119fe564a0f320645d",
|
||||||
|
manufacturer="Nabu Casa",
|
||||||
|
description="Home Assistant Connect ZBT-1",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@ -38,27 +47,36 @@ def config_flow_handler(hass: HomeAssistant) -> Generator[None, None, None]:
|
|||||||
yield
|
yield
|
||||||
|
|
||||||
|
|
||||||
async def test_config_flow(hass: HomeAssistant) -> None:
|
@pytest.mark.parametrize(
|
||||||
"""Test the config flow."""
|
("usb_data", "title"),
|
||||||
|
[
|
||||||
|
(USB_DATA_SKY, "Home Assistant SkyConnect"),
|
||||||
|
(USB_DATA_ZBT1, "Home Assistant Connect ZBT-1"),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
async def test_config_flow(
|
||||||
|
usb_data: usb.UsbServiceInfo, title: str, hass: HomeAssistant
|
||||||
|
) -> None:
|
||||||
|
"""Test the config flow for SkyConnect."""
|
||||||
with patch(
|
with patch(
|
||||||
"homeassistant.components.homeassistant_sky_connect.async_setup_entry",
|
"homeassistant.components.homeassistant_sky_connect.async_setup_entry",
|
||||||
return_value=True,
|
return_value=True,
|
||||||
) as mock_setup_entry:
|
) as mock_setup_entry:
|
||||||
result = await hass.config_entries.flow.async_init(
|
result = await hass.config_entries.flow.async_init(
|
||||||
DOMAIN, context={"source": "usb"}, data=USB_DATA
|
DOMAIN, context={"source": "usb"}, data=usb_data
|
||||||
)
|
)
|
||||||
|
|
||||||
expected_data = {
|
expected_data = {
|
||||||
"device": USB_DATA.device,
|
"device": usb_data.device,
|
||||||
"vid": USB_DATA.vid,
|
"vid": usb_data.vid,
|
||||||
"pid": USB_DATA.pid,
|
"pid": usb_data.pid,
|
||||||
"serial_number": USB_DATA.serial_number,
|
"serial_number": usb_data.serial_number,
|
||||||
"manufacturer": USB_DATA.manufacturer,
|
"manufacturer": usb_data.manufacturer,
|
||||||
"description": USB_DATA.description,
|
"description": usb_data.description,
|
||||||
}
|
}
|
||||||
|
|
||||||
assert result["type"] == FlowResultType.CREATE_ENTRY
|
assert result["type"] == FlowResultType.CREATE_ENTRY
|
||||||
assert result["title"] == "Home Assistant SkyConnect"
|
assert result["title"] == title
|
||||||
assert result["data"] == expected_data
|
assert result["data"] == expected_data
|
||||||
assert result["options"] == {}
|
assert result["options"] == {}
|
||||||
assert len(mock_setup_entry.mock_calls) == 1
|
assert len(mock_setup_entry.mock_calls) == 1
|
||||||
@ -66,51 +84,35 @@ async def test_config_flow(hass: HomeAssistant) -> None:
|
|||||||
config_entry = hass.config_entries.async_entries(DOMAIN)[0]
|
config_entry = hass.config_entries.async_entries(DOMAIN)[0]
|
||||||
assert config_entry.data == expected_data
|
assert config_entry.data == expected_data
|
||||||
assert config_entry.options == {}
|
assert config_entry.options == {}
|
||||||
assert config_entry.title == "Home Assistant SkyConnect"
|
assert config_entry.title == title
|
||||||
assert (
|
assert (
|
||||||
config_entry.unique_id
|
config_entry.unique_id
|
||||||
== f"{USB_DATA.vid}:{USB_DATA.pid}_{USB_DATA.serial_number}_{USB_DATA.manufacturer}_{USB_DATA.description}"
|
== f"{usb_data.vid}:{usb_data.pid}_{usb_data.serial_number}_{usb_data.manufacturer}_{usb_data.description}"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
async def test_config_flow_unique_id(hass: HomeAssistant) -> None:
|
@pytest.mark.parametrize(
|
||||||
"""Test only a single entry is allowed for a dongle."""
|
("usb_data", "title"),
|
||||||
# Setup an existing config entry
|
[
|
||||||
config_entry = MockConfigEntry(
|
(USB_DATA_SKY, "Home Assistant SkyConnect"),
|
||||||
data={},
|
(USB_DATA_ZBT1, "Home Assistant Connect ZBT-1"),
|
||||||
domain=DOMAIN,
|
],
|
||||||
options={},
|
|
||||||
title="Home Assistant SkyConnect",
|
|
||||||
unique_id=f"{USB_DATA.vid}:{USB_DATA.pid}_{USB_DATA.serial_number}_{USB_DATA.manufacturer}_{USB_DATA.description}",
|
|
||||||
)
|
)
|
||||||
config_entry.add_to_hass(hass)
|
async def test_config_flow_multiple_entries(
|
||||||
|
usb_data: usb.UsbServiceInfo, title: str, hass: HomeAssistant
|
||||||
with patch(
|
) -> None:
|
||||||
"homeassistant.components.homeassistant_sky_connect.async_setup_entry",
|
|
||||||
return_value=True,
|
|
||||||
) as mock_setup_entry:
|
|
||||||
result = await hass.config_entries.flow.async_init(
|
|
||||||
DOMAIN, context={"source": "usb"}, data=USB_DATA
|
|
||||||
)
|
|
||||||
|
|
||||||
assert result["type"] == FlowResultType.ABORT
|
|
||||||
assert result["reason"] == "already_configured"
|
|
||||||
mock_setup_entry.assert_not_called()
|
|
||||||
|
|
||||||
|
|
||||||
async def test_config_flow_multiple_entries(hass: HomeAssistant) -> None:
|
|
||||||
"""Test multiple entries are allowed."""
|
"""Test multiple entries are allowed."""
|
||||||
# Setup an existing config entry
|
# Setup an existing config entry
|
||||||
config_entry = MockConfigEntry(
|
config_entry = MockConfigEntry(
|
||||||
data={},
|
data={},
|
||||||
domain=DOMAIN,
|
domain=DOMAIN,
|
||||||
options={},
|
options={},
|
||||||
title="Home Assistant SkyConnect",
|
title=title,
|
||||||
unique_id=f"{USB_DATA.vid}:{USB_DATA.pid}_{USB_DATA.serial_number}_{USB_DATA.manufacturer}_{USB_DATA.description}",
|
unique_id=f"{usb_data.vid}:{usb_data.pid}_{usb_data.serial_number}_{usb_data.manufacturer}_{usb_data.description}",
|
||||||
)
|
)
|
||||||
config_entry.add_to_hass(hass)
|
config_entry.add_to_hass(hass)
|
||||||
|
|
||||||
usb_data = copy.copy(USB_DATA)
|
usb_data = copy.copy(usb_data)
|
||||||
usb_data.serial_number = "bla_serial_number_2"
|
usb_data.serial_number = "bla_serial_number_2"
|
||||||
|
|
||||||
with patch(
|
with patch(
|
||||||
@ -124,19 +126,28 @@ async def test_config_flow_multiple_entries(hass: HomeAssistant) -> None:
|
|||||||
assert result["type"] == FlowResultType.CREATE_ENTRY
|
assert result["type"] == FlowResultType.CREATE_ENTRY
|
||||||
|
|
||||||
|
|
||||||
async def test_config_flow_update_device(hass: HomeAssistant) -> None:
|
@pytest.mark.parametrize(
|
||||||
|
("usb_data", "title"),
|
||||||
|
[
|
||||||
|
(USB_DATA_SKY, "Home Assistant SkyConnect"),
|
||||||
|
(USB_DATA_ZBT1, "Home Assistant Connect ZBT-1"),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
async def test_config_flow_update_device(
|
||||||
|
usb_data: usb.UsbServiceInfo, title: str, hass: HomeAssistant
|
||||||
|
) -> None:
|
||||||
"""Test updating device path."""
|
"""Test updating device path."""
|
||||||
# Setup an existing config entry
|
# Setup an existing config entry
|
||||||
config_entry = MockConfigEntry(
|
config_entry = MockConfigEntry(
|
||||||
data={},
|
data={},
|
||||||
domain=DOMAIN,
|
domain=DOMAIN,
|
||||||
options={},
|
options={},
|
||||||
title="Home Assistant SkyConnect",
|
title=title,
|
||||||
unique_id=f"{USB_DATA.vid}:{USB_DATA.pid}_{USB_DATA.serial_number}_{USB_DATA.manufacturer}_{USB_DATA.description}",
|
unique_id=f"{usb_data.vid}:{usb_data.pid}_{usb_data.serial_number}_{usb_data.manufacturer}_{usb_data.description}",
|
||||||
)
|
)
|
||||||
config_entry.add_to_hass(hass)
|
config_entry.add_to_hass(hass)
|
||||||
|
|
||||||
usb_data = copy.copy(USB_DATA)
|
usb_data = copy.copy(usb_data)
|
||||||
usb_data.device = "bla_device_2"
|
usb_data.device = "bla_device_2"
|
||||||
|
|
||||||
with patch(
|
with patch(
|
||||||
@ -167,7 +178,16 @@ async def test_config_flow_update_device(hass: HomeAssistant) -> None:
|
|||||||
assert len(mock_unload_entry.mock_calls) == 1
|
assert len(mock_unload_entry.mock_calls) == 1
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
("usb_data", "title"),
|
||||||
|
[
|
||||||
|
(USB_DATA_SKY, "Home Assistant SkyConnect"),
|
||||||
|
(USB_DATA_ZBT1, "Home Assistant ZBT-1"),
|
||||||
|
],
|
||||||
|
)
|
||||||
async def test_option_flow_install_multi_pan_addon(
|
async def test_option_flow_install_multi_pan_addon(
|
||||||
|
usb_data: usb.UsbServiceInfo,
|
||||||
|
title: str,
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
addon_store_info,
|
addon_store_info,
|
||||||
addon_info,
|
addon_info,
|
||||||
@ -182,17 +202,17 @@ async def test_option_flow_install_multi_pan_addon(
|
|||||||
# Setup the config entry
|
# Setup the config entry
|
||||||
config_entry = MockConfigEntry(
|
config_entry = MockConfigEntry(
|
||||||
data={
|
data={
|
||||||
"device": USB_DATA.device,
|
"device": usb_data.device,
|
||||||
"vid": USB_DATA.vid,
|
"vid": usb_data.vid,
|
||||||
"pid": USB_DATA.pid,
|
"pid": usb_data.pid,
|
||||||
"serial_number": USB_DATA.serial_number,
|
"serial_number": usb_data.serial_number,
|
||||||
"manufacturer": USB_DATA.manufacturer,
|
"manufacturer": usb_data.manufacturer,
|
||||||
"description": USB_DATA.description,
|
"description": usb_data.description,
|
||||||
},
|
},
|
||||||
domain=DOMAIN,
|
domain=DOMAIN,
|
||||||
options={},
|
options={},
|
||||||
title="Home Assistant SkyConnect",
|
title=title,
|
||||||
unique_id=f"{USB_DATA.vid}:{USB_DATA.pid}_{USB_DATA.serial_number}_{USB_DATA.manufacturer}_{USB_DATA.description}",
|
unique_id=f"{usb_data.vid}:{usb_data.pid}_{usb_data.serial_number}_{usb_data.manufacturer}_{usb_data.description}",
|
||||||
)
|
)
|
||||||
config_entry.add_to_hass(hass)
|
config_entry.add_to_hass(hass)
|
||||||
|
|
||||||
@ -226,7 +246,7 @@ async def test_option_flow_install_multi_pan_addon(
|
|||||||
{
|
{
|
||||||
"options": {
|
"options": {
|
||||||
"autoflash_firmware": True,
|
"autoflash_firmware": True,
|
||||||
"device": "bla_device",
|
"device": usb_data.device,
|
||||||
"baudrate": "115200",
|
"baudrate": "115200",
|
||||||
"flow_control": True,
|
"flow_control": True,
|
||||||
}
|
}
|
||||||
@ -254,11 +274,20 @@ def mock_detect_radio_type(radio_type=RadioType.ezsp, ret=True):
|
|||||||
return detect
|
return detect
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
("usb_data", "title"),
|
||||||
|
[
|
||||||
|
(USB_DATA_SKY, "Home Assistant SkyConnect"),
|
||||||
|
(USB_DATA_ZBT1, "Home Assistant Connect ZBT-1"),
|
||||||
|
],
|
||||||
|
)
|
||||||
@patch(
|
@patch(
|
||||||
"homeassistant.components.zha.radio_manager.ZhaRadioManager.detect_radio_type",
|
"homeassistant.components.zha.radio_manager.ZhaRadioManager.detect_radio_type",
|
||||||
mock_detect_radio_type(),
|
mock_detect_radio_type(),
|
||||||
)
|
)
|
||||||
async def test_option_flow_install_multi_pan_addon_zha(
|
async def test_option_flow_install_multi_pan_addon_zha(
|
||||||
|
usb_data: usb.UsbServiceInfo,
|
||||||
|
title: str,
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
addon_store_info,
|
addon_store_info,
|
||||||
addon_info,
|
addon_info,
|
||||||
@ -273,22 +302,22 @@ async def test_option_flow_install_multi_pan_addon_zha(
|
|||||||
# Setup the config entry
|
# Setup the config entry
|
||||||
config_entry = MockConfigEntry(
|
config_entry = MockConfigEntry(
|
||||||
data={
|
data={
|
||||||
"device": USB_DATA.device,
|
"device": usb_data.device,
|
||||||
"vid": USB_DATA.vid,
|
"vid": usb_data.vid,
|
||||||
"pid": USB_DATA.pid,
|
"pid": usb_data.pid,
|
||||||
"serial_number": USB_DATA.serial_number,
|
"serial_number": usb_data.serial_number,
|
||||||
"manufacturer": USB_DATA.manufacturer,
|
"manufacturer": usb_data.manufacturer,
|
||||||
"description": USB_DATA.description,
|
"description": usb_data.description,
|
||||||
},
|
},
|
||||||
domain=DOMAIN,
|
domain=DOMAIN,
|
||||||
options={},
|
options={},
|
||||||
title="Home Assistant SkyConnect",
|
title=title,
|
||||||
unique_id=f"{USB_DATA.vid}:{USB_DATA.pid}_{USB_DATA.serial_number}_{USB_DATA.manufacturer}_{USB_DATA.description}",
|
unique_id=f"{usb_data.vid}:{usb_data.pid}_{usb_data.serial_number}_{usb_data.manufacturer}_{usb_data.description}",
|
||||||
)
|
)
|
||||||
config_entry.add_to_hass(hass)
|
config_entry.add_to_hass(hass)
|
||||||
|
|
||||||
zha_config_entry = MockConfigEntry(
|
zha_config_entry = MockConfigEntry(
|
||||||
data={"device": {"path": "bla_device"}, "radio_type": "ezsp"},
|
data={"device": {"path": usb_data.device}, "radio_type": "ezsp"},
|
||||||
domain=ZHA_DOMAIN,
|
domain=ZHA_DOMAIN,
|
||||||
options={},
|
options={},
|
||||||
title="Yellow",
|
title="Yellow",
|
||||||
@ -325,7 +354,7 @@ async def test_option_flow_install_multi_pan_addon_zha(
|
|||||||
{
|
{
|
||||||
"options": {
|
"options": {
|
||||||
"autoflash_firmware": True,
|
"autoflash_firmware": True,
|
||||||
"device": "bla_device",
|
"device": usb_data.device,
|
||||||
"baudrate": "115200",
|
"baudrate": "115200",
|
||||||
"flow_control": True,
|
"flow_control": True,
|
||||||
}
|
}
|
||||||
|
27
tests/components/homeassistant_sky_connect/test_const.py
Normal file
27
tests/components/homeassistant_sky_connect/test_const.py
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
"""Test the Home Assistant SkyConnect constants."""
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from homeassistant.components.homeassistant_sky_connect.const import HardwareVariant
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
("usb_product_name", "expected_variant"),
|
||||||
|
[
|
||||||
|
("SkyConnect v1.0", HardwareVariant.SKYCONNECT),
|
||||||
|
("Home Assistant Connect ZBT-1", HardwareVariant.CONNECT_ZBT1),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
def test_hardware_variant(
|
||||||
|
usb_product_name: str, expected_variant: HardwareVariant
|
||||||
|
) -> None:
|
||||||
|
"""Test hardware variant parsing."""
|
||||||
|
assert HardwareVariant.from_usb_product_name(usb_product_name) == expected_variant
|
||||||
|
|
||||||
|
|
||||||
|
def test_hardware_variant_invalid():
|
||||||
|
"""Test hardware variant parsing with an invalid product."""
|
||||||
|
with pytest.raises(
|
||||||
|
ValueError, match=r"^Unknown SkyConnect product name: Some other product$"
|
||||||
|
):
|
||||||
|
HardwareVariant.from_usb_product_name("Some other product")
|
@ -10,21 +10,21 @@ from tests.common import MockConfigEntry
|
|||||||
from tests.typing import WebSocketGenerator
|
from tests.typing import WebSocketGenerator
|
||||||
|
|
||||||
CONFIG_ENTRY_DATA = {
|
CONFIG_ENTRY_DATA = {
|
||||||
"device": "bla_device",
|
"device": "/dev/serial/by-id/usb-Nabu_Casa_SkyConnect_v1.0_9e2adbd75b8beb119fe564a0f320645d-if00-port0",
|
||||||
"vid": "bla_vid",
|
"vid": "10C4",
|
||||||
"pid": "bla_pid",
|
"pid": "EA60",
|
||||||
"serial_number": "bla_serial_number",
|
"serial_number": "9e2adbd75b8beb119fe564a0f320645d",
|
||||||
"manufacturer": "bla_manufacturer",
|
"manufacturer": "Nabu Casa",
|
||||||
"description": "bla_description",
|
"description": "SkyConnect v1.0",
|
||||||
}
|
}
|
||||||
|
|
||||||
CONFIG_ENTRY_DATA_2 = {
|
CONFIG_ENTRY_DATA_2 = {
|
||||||
"device": "bla_device_2",
|
"device": "/dev/serial/by-id/usb-Nabu_Casa_Home_Assistant_Connect_ZBT-1_9e2adbd75b8beb119fe564a0f320645d-if00-port0",
|
||||||
"vid": "bla_vid_2",
|
"vid": "10C4",
|
||||||
"pid": "bla_pid_2",
|
"pid": "EA60",
|
||||||
"serial_number": "bla_serial_number_2",
|
"serial_number": "9e2adbd75b8beb119fe564a0f320645d",
|
||||||
"manufacturer": "bla_manufacturer_2",
|
"manufacturer": "Nabu Casa",
|
||||||
"description": "bla_description_2",
|
"description": "Home Assistant Connect ZBT-1",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -48,7 +48,7 @@ async def test_hardware_info(
|
|||||||
data=CONFIG_ENTRY_DATA_2,
|
data=CONFIG_ENTRY_DATA_2,
|
||||||
domain=DOMAIN,
|
domain=DOMAIN,
|
||||||
options={},
|
options={},
|
||||||
title="Home Assistant SkyConnect",
|
title="Home Assistant Connect ZBT-1",
|
||||||
unique_id="unique_2",
|
unique_id="unique_2",
|
||||||
)
|
)
|
||||||
config_entry_2.add_to_hass(hass)
|
config_entry_2.add_to_hass(hass)
|
||||||
@ -72,11 +72,11 @@ async def test_hardware_info(
|
|||||||
"board": None,
|
"board": None,
|
||||||
"config_entries": [config_entry.entry_id],
|
"config_entries": [config_entry.entry_id],
|
||||||
"dongle": {
|
"dongle": {
|
||||||
"vid": "bla_vid",
|
"vid": "10C4",
|
||||||
"pid": "bla_pid",
|
"pid": "EA60",
|
||||||
"serial_number": "bla_serial_number",
|
"serial_number": "9e2adbd75b8beb119fe564a0f320645d",
|
||||||
"manufacturer": "bla_manufacturer",
|
"manufacturer": "Nabu Casa",
|
||||||
"description": "bla_description",
|
"description": "SkyConnect v1.0",
|
||||||
},
|
},
|
||||||
"name": "Home Assistant SkyConnect",
|
"name": "Home Assistant SkyConnect",
|
||||||
"url": "https://skyconnect.home-assistant.io/documentation/",
|
"url": "https://skyconnect.home-assistant.io/documentation/",
|
||||||
@ -85,13 +85,13 @@ async def test_hardware_info(
|
|||||||
"board": None,
|
"board": None,
|
||||||
"config_entries": [config_entry_2.entry_id],
|
"config_entries": [config_entry_2.entry_id],
|
||||||
"dongle": {
|
"dongle": {
|
||||||
"vid": "bla_vid_2",
|
"vid": "10C4",
|
||||||
"pid": "bla_pid_2",
|
"pid": "EA60",
|
||||||
"serial_number": "bla_serial_number_2",
|
"serial_number": "9e2adbd75b8beb119fe564a0f320645d",
|
||||||
"manufacturer": "bla_manufacturer_2",
|
"manufacturer": "Nabu Casa",
|
||||||
"description": "bla_description_2",
|
"description": "Home Assistant Connect ZBT-1",
|
||||||
},
|
},
|
||||||
"name": "Home Assistant SkyConnect",
|
"name": "Home Assistant Connect ZBT-1",
|
||||||
"url": "https://skyconnect.home-assistant.io/documentation/",
|
"url": "https://skyconnect.home-assistant.io/documentation/",
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
@ -280,8 +280,25 @@ async def test_hassio_discovery_flow_yellow(
|
|||||||
assert config_entry.unique_id == HASSIO_DATA.uuid
|
assert config_entry.unique_id == HASSIO_DATA.uuid
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
("device", "title"),
|
||||||
|
[
|
||||||
|
(
|
||||||
|
"/dev/serial/by-id/usb-Nabu_Casa_SkyConnect_v1.0_9e2adbd75b8beb119fe564a0f320645d-if00-port0",
|
||||||
|
"Home Assistant SkyConnect (Silicon Labs Multiprotocol)",
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"/dev/serial/by-id/usb-Nabu_Casa_Home_Assistant_Connect_ZBT-1_9e2adbd75b8beb119fe564a0f320645d-if00-port0",
|
||||||
|
"Home Assistant Connect ZBT-1 (Silicon Labs Multiprotocol)",
|
||||||
|
),
|
||||||
|
],
|
||||||
|
)
|
||||||
async def test_hassio_discovery_flow_sky_connect(
|
async def test_hassio_discovery_flow_sky_connect(
|
||||||
hass: HomeAssistant, aioclient_mock: AiohttpClientMocker, addon_info
|
device: str,
|
||||||
|
title: str,
|
||||||
|
hass: HomeAssistant,
|
||||||
|
aioclient_mock: AiohttpClientMocker,
|
||||||
|
addon_info,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Test the hassio discovery flow."""
|
"""Test the hassio discovery flow."""
|
||||||
url = "http://core-silabs-multiprotocol:8081"
|
url = "http://core-silabs-multiprotocol:8081"
|
||||||
@ -290,12 +307,7 @@ async def test_hassio_discovery_flow_sky_connect(
|
|||||||
addon_info.return_value = {
|
addon_info.return_value = {
|
||||||
"available": True,
|
"available": True,
|
||||||
"hostname": None,
|
"hostname": None,
|
||||||
"options": {
|
"options": {"device": device},
|
||||||
"device": (
|
|
||||||
"/dev/serial/by-id/usb-Nabu_Casa_SkyConnect_v1.0_"
|
|
||||||
"9e2adbd75b8beb119fe564a0f320645d-if00-port0"
|
|
||||||
)
|
|
||||||
},
|
|
||||||
"state": None,
|
"state": None,
|
||||||
"update_available": False,
|
"update_available": False,
|
||||||
"version": None,
|
"version": None,
|
||||||
@ -314,7 +326,7 @@ async def test_hassio_discovery_flow_sky_connect(
|
|||||||
}
|
}
|
||||||
|
|
||||||
assert result["type"] == FlowResultType.CREATE_ENTRY
|
assert result["type"] == FlowResultType.CREATE_ENTRY
|
||||||
assert result["title"] == "Home Assistant SkyConnect (Silicon Labs Multiprotocol)"
|
assert result["title"] == title
|
||||||
assert result["data"] == expected_data
|
assert result["data"] == expected_data
|
||||||
assert result["options"] == {}
|
assert result["options"] == {}
|
||||||
assert len(mock_setup_entry.mock_calls) == 1
|
assert len(mock_setup_entry.mock_calls) == 1
|
||||||
@ -322,9 +334,7 @@ async def test_hassio_discovery_flow_sky_connect(
|
|||||||
config_entry = hass.config_entries.async_entries(otbr.DOMAIN)[0]
|
config_entry = hass.config_entries.async_entries(otbr.DOMAIN)[0]
|
||||||
assert config_entry.data == expected_data
|
assert config_entry.data == expected_data
|
||||||
assert config_entry.options == {}
|
assert config_entry.options == {}
|
||||||
assert (
|
assert config_entry.title == title
|
||||||
config_entry.title == "Home Assistant SkyConnect (Silicon Labs Multiprotocol)"
|
|
||||||
)
|
|
||||||
assert config_entry.unique_id == HASSIO_DATA.uuid
|
assert config_entry.unique_id == HASSIO_DATA.uuid
|
||||||
|
|
||||||
|
|
||||||
|
@ -38,6 +38,7 @@ from tests.common import MockConfigEntry
|
|||||||
from tests.typing import ClientSessionGenerator
|
from tests.typing import ClientSessionGenerator
|
||||||
|
|
||||||
SKYCONNECT_DEVICE = "/dev/serial/by-id/usb-Nabu_Casa_SkyConnect_v1.0_9e2adbd75b8beb119fe564a0f320645d-if00-port0"
|
SKYCONNECT_DEVICE = "/dev/serial/by-id/usb-Nabu_Casa_SkyConnect_v1.0_9e2adbd75b8beb119fe564a0f320645d-if00-port0"
|
||||||
|
CONNECT_ZBT1_DEVICE = "/dev/serial/by-id/usb-Nabu_Casa_Home_Assistant_Connect_ZBT-1_9e2adbd75b8beb119fe564a0f320645d-if00-port0"
|
||||||
|
|
||||||
|
|
||||||
def set_flasher_app_type(app_type: ApplicationType) -> Callable[[Flasher], None]:
|
def set_flasher_app_type(app_type: ApplicationType) -> Callable[[Flasher], None]:
|
||||||
@ -66,6 +67,22 @@ def test_detect_radio_hardware(hass: HomeAssistant) -> None:
|
|||||||
)
|
)
|
||||||
skyconnect_config_entry.add_to_hass(hass)
|
skyconnect_config_entry.add_to_hass(hass)
|
||||||
|
|
||||||
|
connect_zbt1_config_entry = MockConfigEntry(
|
||||||
|
data={
|
||||||
|
"device": CONNECT_ZBT1_DEVICE,
|
||||||
|
"vid": "10C4",
|
||||||
|
"pid": "EA60",
|
||||||
|
"serial_number": "3c0ed67c628beb11b1cd64a0f320645d",
|
||||||
|
"manufacturer": "Nabu Casa",
|
||||||
|
"description": "Home Assistant Connect ZBT-1",
|
||||||
|
},
|
||||||
|
domain=SKYCONNECT_DOMAIN,
|
||||||
|
options={},
|
||||||
|
title="Home Assistant Connect ZBT-1",
|
||||||
|
)
|
||||||
|
connect_zbt1_config_entry.add_to_hass(hass)
|
||||||
|
|
||||||
|
assert _detect_radio_hardware(hass, CONNECT_ZBT1_DEVICE) == HardwareType.SKYCONNECT
|
||||||
assert _detect_radio_hardware(hass, SKYCONNECT_DEVICE) == HardwareType.SKYCONNECT
|
assert _detect_radio_hardware(hass, SKYCONNECT_DEVICE) == HardwareType.SKYCONNECT
|
||||||
assert (
|
assert (
|
||||||
_detect_radio_hardware(hass, SKYCONNECT_DEVICE + "_foo") == HardwareType.OTHER
|
_detect_radio_hardware(hass, SKYCONNECT_DEVICE + "_foo") == HardwareType.OTHER
|
||||||
|
Loading…
x
Reference in New Issue
Block a user