Support the Home Assistant Connect ZBT-1 (#114213)

This commit is contained in:
puddly 2024-03-27 13:20:48 -04:00 committed by GitHub
parent 63e28f958d
commit fc34453caa
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
13 changed files with 268 additions and 115 deletions

View File

@ -14,7 +14,7 @@ from homeassistant.exceptions import ConfigEntryNotReady, HomeAssistantError
from homeassistant.helpers import discovery_flow
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:
@ -46,8 +46,9 @@ async def _async_usb_scan_done(hass: HomeAssistant, entry: ConfigEntry) -> None:
)
return
hw_variant = get_hardware_variant(entry)
hw_discovery_data = {
"name": "SkyConnect Multiprotocol",
"name": f"{hw_variant.short_name} Multiprotocol",
"port": {
"path": get_zigbee_socket(),
},

View File

@ -9,8 +9,8 @@ from homeassistant.components.homeassistant_hardware import silabs_multiprotocol
from homeassistant.config_entries import ConfigEntry, ConfigFlow, ConfigFlowResult
from homeassistant.core import callback
from .const import DOMAIN
from .util import get_usb_service_info
from .const import DOMAIN, HardwareVariant
from .util import get_hardware_variant, get_usb_service_info
class HomeAssistantSkyConnectConfigFlow(ConfigFlow, domain=DOMAIN):
@ -39,8 +39,12 @@ class HomeAssistantSkyConnectConfigFlow(ConfigFlow, domain=DOMAIN):
unique_id = f"{vid}:{pid}_{serial_number}_{manufacturer}_{description}"
if await self.async_set_unique_id(unique_id):
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(
title="Home Assistant SkyConnect",
title=hw_variant.full_name,
data={
"device": device,
"vid": vid,
@ -76,10 +80,15 @@ class HomeAssistantSkyConnectOptionsFlow(silabs_multiprotocol_addon.OptionsFlowH
"""
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:
"""Return the ZHA name."""
return "SkyConnect Multiprotocol"
return f"{self._hw_variant.short_name} Multiprotocol"
def _hardware_name(self) -> str:
"""Return the name of the hardware."""
return "Home Assistant SkyConnect"
return self._hw_variant.full_name

View File

@ -1,3 +1,41 @@
"""Constants for the Home Assistant SkyConnect integration."""
import dataclasses
import enum
from typing import Self
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}")

View File

@ -6,9 +6,9 @@ from homeassistant.components.hardware.models import HardwareInfo, USBInfo
from homeassistant.core import HomeAssistant, callback
from .const import DOMAIN
from .util import get_hardware_variant
DOCUMENTATION_URL = "https://skyconnect.home-assistant.io/documentation/"
DONGLE_NAME = "Home Assistant SkyConnect"
@callback
@ -27,7 +27,7 @@ def async_info(hass: HomeAssistant) -> list[HardwareInfo]:
manufacturer=entry.data["manufacturer"],
description=entry.data["description"],
),
name=DONGLE_NAME,
name=get_hardware_variant(entry).full_name,
url=DOCUMENTATION_URL,
)
for entry in entries

View File

@ -12,6 +12,12 @@
"pid": "EA60",
"description": "*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"]
}
]
}

View File

@ -5,6 +5,8 @@ from __future__ import annotations
from homeassistant.components import usb
from homeassistant.config_entries import ConfigEntry
from .const import HardwareVariant
def get_usb_service_info(config_entry: ConfigEntry) -> usb.UsbServiceInfo:
"""Return UsbServiceInfo."""
@ -16,3 +18,8 @@ def get_usb_service_info(config_entry: ConfigEntry) -> usb.UsbServiceInfo:
manufacturer=config_entry.data["manufacturer"],
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"])

View File

@ -59,6 +59,9 @@ async def _title(hass: HomeAssistant, discovery_info: HassioServiceInfo) -> str:
if device and "SkyConnect" in device:
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

View File

@ -10,6 +10,12 @@ USB = [
"pid": "EA60",
"vid": "10C4",
},
{
"description": "*home assistant connect zbt-1*",
"domain": "homeassistant_sky_connect",
"pid": "EA60",
"vid": "10C4",
},
{
"domain": "insteon",
"vid": "10BF",

View File

@ -19,13 +19,22 @@ from homeassistant.setup import async_setup_component
from tests.common import MockConfigEntry, MockModule, mock_integration
USB_DATA = usb.UsbServiceInfo(
device="bla_device",
vid="bla_vid",
pid="bla_pid",
serial_number="bla_serial_number",
manufacturer="bla_manufacturer",
description="bla_description",
USB_DATA_SKY = usb.UsbServiceInfo(
device="/dev/serial/by-id/usb-Nabu_Casa_SkyConnect_v1.0_9e2adbd75b8beb119fe564a0f320645d-if00-port0",
vid="10C4",
pid="EA60",
serial_number="9e2adbd75b8beb119fe564a0f320645d",
manufacturer="Nabu Casa",
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
async def test_config_flow(hass: HomeAssistant) -> None:
"""Test the config flow."""
@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(
usb_data: usb.UsbServiceInfo, title: str, hass: HomeAssistant
) -> None:
"""Test the config flow for SkyConnect."""
with patch(
"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
DOMAIN, context={"source": "usb"}, data=usb_data
)
expected_data = {
"device": USB_DATA.device,
"vid": USB_DATA.vid,
"pid": USB_DATA.pid,
"serial_number": USB_DATA.serial_number,
"manufacturer": USB_DATA.manufacturer,
"description": USB_DATA.description,
"device": usb_data.device,
"vid": usb_data.vid,
"pid": usb_data.pid,
"serial_number": usb_data.serial_number,
"manufacturer": usb_data.manufacturer,
"description": usb_data.description,
}
assert result["type"] == FlowResultType.CREATE_ENTRY
assert result["title"] == "Home Assistant SkyConnect"
assert result["title"] == title
assert result["data"] == expected_data
assert result["options"] == {}
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]
assert config_entry.data == expected_data
assert config_entry.options == {}
assert config_entry.title == "Home Assistant SkyConnect"
assert config_entry.title == title
assert (
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:
"""Test only a single entry is allowed for a dongle."""
# Setup an existing config entry
config_entry = MockConfigEntry(
data={},
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)
with patch(
"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:
@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_multiple_entries(
usb_data: usb.UsbServiceInfo, title: str, hass: HomeAssistant
) -> None:
"""Test multiple entries are allowed."""
# Setup an existing config entry
config_entry = MockConfigEntry(
data={},
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}",
title=title,
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)
usb_data = copy.copy(USB_DATA)
usb_data = copy.copy(usb_data)
usb_data.serial_number = "bla_serial_number_2"
with patch(
@ -124,19 +126,28 @@ async def test_config_flow_multiple_entries(hass: HomeAssistant) -> None:
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."""
# Setup an existing config entry
config_entry = MockConfigEntry(
data={},
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}",
title=title,
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)
usb_data = copy.copy(USB_DATA)
usb_data = copy.copy(usb_data)
usb_data.device = "bla_device_2"
with patch(
@ -167,7 +178,16 @@ async def test_config_flow_update_device(hass: HomeAssistant) -> None:
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(
usb_data: usb.UsbServiceInfo,
title: str,
hass: HomeAssistant,
addon_store_info,
addon_info,
@ -182,17 +202,17 @@ async def test_option_flow_install_multi_pan_addon(
# Setup the config entry
config_entry = MockConfigEntry(
data={
"device": USB_DATA.device,
"vid": USB_DATA.vid,
"pid": USB_DATA.pid,
"serial_number": USB_DATA.serial_number,
"manufacturer": USB_DATA.manufacturer,
"description": USB_DATA.description,
"device": usb_data.device,
"vid": usb_data.vid,
"pid": usb_data.pid,
"serial_number": usb_data.serial_number,
"manufacturer": usb_data.manufacturer,
"description": usb_data.description,
},
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}",
title=title,
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)
@ -226,7 +246,7 @@ async def test_option_flow_install_multi_pan_addon(
{
"options": {
"autoflash_firmware": True,
"device": "bla_device",
"device": usb_data.device,
"baudrate": "115200",
"flow_control": True,
}
@ -254,11 +274,20 @@ def mock_detect_radio_type(radio_type=RadioType.ezsp, ret=True):
return detect
@pytest.mark.parametrize(
("usb_data", "title"),
[
(USB_DATA_SKY, "Home Assistant SkyConnect"),
(USB_DATA_ZBT1, "Home Assistant Connect ZBT-1"),
],
)
@patch(
"homeassistant.components.zha.radio_manager.ZhaRadioManager.detect_radio_type",
mock_detect_radio_type(),
)
async def test_option_flow_install_multi_pan_addon_zha(
usb_data: usb.UsbServiceInfo,
title: str,
hass: HomeAssistant,
addon_store_info,
addon_info,
@ -273,22 +302,22 @@ async def test_option_flow_install_multi_pan_addon_zha(
# Setup the config entry
config_entry = MockConfigEntry(
data={
"device": USB_DATA.device,
"vid": USB_DATA.vid,
"pid": USB_DATA.pid,
"serial_number": USB_DATA.serial_number,
"manufacturer": USB_DATA.manufacturer,
"description": USB_DATA.description,
"device": usb_data.device,
"vid": usb_data.vid,
"pid": usb_data.pid,
"serial_number": usb_data.serial_number,
"manufacturer": usb_data.manufacturer,
"description": usb_data.description,
},
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}",
title=title,
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)
zha_config_entry = MockConfigEntry(
data={"device": {"path": "bla_device"}, "radio_type": "ezsp"},
data={"device": {"path": usb_data.device}, "radio_type": "ezsp"},
domain=ZHA_DOMAIN,
options={},
title="Yellow",
@ -325,7 +354,7 @@ async def test_option_flow_install_multi_pan_addon_zha(
{
"options": {
"autoflash_firmware": True,
"device": "bla_device",
"device": usb_data.device,
"baudrate": "115200",
"flow_control": True,
}

View 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")

View File

@ -10,21 +10,21 @@ from tests.common import MockConfigEntry
from tests.typing import WebSocketGenerator
CONFIG_ENTRY_DATA = {
"device": "bla_device",
"vid": "bla_vid",
"pid": "bla_pid",
"serial_number": "bla_serial_number",
"manufacturer": "bla_manufacturer",
"description": "bla_description",
"device": "/dev/serial/by-id/usb-Nabu_Casa_SkyConnect_v1.0_9e2adbd75b8beb119fe564a0f320645d-if00-port0",
"vid": "10C4",
"pid": "EA60",
"serial_number": "9e2adbd75b8beb119fe564a0f320645d",
"manufacturer": "Nabu Casa",
"description": "SkyConnect v1.0",
}
CONFIG_ENTRY_DATA_2 = {
"device": "bla_device_2",
"vid": "bla_vid_2",
"pid": "bla_pid_2",
"serial_number": "bla_serial_number_2",
"manufacturer": "bla_manufacturer_2",
"description": "bla_description_2",
"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",
}
@ -48,7 +48,7 @@ async def test_hardware_info(
data=CONFIG_ENTRY_DATA_2,
domain=DOMAIN,
options={},
title="Home Assistant SkyConnect",
title="Home Assistant Connect ZBT-1",
unique_id="unique_2",
)
config_entry_2.add_to_hass(hass)
@ -72,11 +72,11 @@ async def test_hardware_info(
"board": None,
"config_entries": [config_entry.entry_id],
"dongle": {
"vid": "bla_vid",
"pid": "bla_pid",
"serial_number": "bla_serial_number",
"manufacturer": "bla_manufacturer",
"description": "bla_description",
"vid": "10C4",
"pid": "EA60",
"serial_number": "9e2adbd75b8beb119fe564a0f320645d",
"manufacturer": "Nabu Casa",
"description": "SkyConnect v1.0",
},
"name": "Home Assistant SkyConnect",
"url": "https://skyconnect.home-assistant.io/documentation/",
@ -85,13 +85,13 @@ async def test_hardware_info(
"board": None,
"config_entries": [config_entry_2.entry_id],
"dongle": {
"vid": "bla_vid_2",
"pid": "bla_pid_2",
"serial_number": "bla_serial_number_2",
"manufacturer": "bla_manufacturer_2",
"description": "bla_description_2",
"vid": "10C4",
"pid": "EA60",
"serial_number": "9e2adbd75b8beb119fe564a0f320645d",
"manufacturer": "Nabu Casa",
"description": "Home Assistant Connect ZBT-1",
},
"name": "Home Assistant SkyConnect",
"name": "Home Assistant Connect ZBT-1",
"url": "https://skyconnect.home-assistant.io/documentation/",
},
]

View File

@ -280,8 +280,25 @@ async def test_hassio_discovery_flow_yellow(
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(
hass: HomeAssistant, aioclient_mock: AiohttpClientMocker, addon_info
device: str,
title: str,
hass: HomeAssistant,
aioclient_mock: AiohttpClientMocker,
addon_info,
) -> None:
"""Test the hassio discovery flow."""
url = "http://core-silabs-multiprotocol:8081"
@ -290,12 +307,7 @@ async def test_hassio_discovery_flow_sky_connect(
addon_info.return_value = {
"available": True,
"hostname": None,
"options": {
"device": (
"/dev/serial/by-id/usb-Nabu_Casa_SkyConnect_v1.0_"
"9e2adbd75b8beb119fe564a0f320645d-if00-port0"
)
},
"options": {"device": device},
"state": None,
"update_available": False,
"version": None,
@ -314,7 +326,7 @@ async def test_hassio_discovery_flow_sky_connect(
}
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["options"] == {}
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]
assert config_entry.data == expected_data
assert config_entry.options == {}
assert (
config_entry.title == "Home Assistant SkyConnect (Silicon Labs Multiprotocol)"
)
assert config_entry.title == title
assert config_entry.unique_id == HASSIO_DATA.uuid

View File

@ -38,6 +38,7 @@ from tests.common import MockConfigEntry
from tests.typing import ClientSessionGenerator
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]:
@ -66,6 +67,22 @@ def test_detect_radio_hardware(hass: HomeAssistant) -> None:
)
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 + "_foo") == HardwareType.OTHER