Add new QNAP QSW integration (#70151)

Co-authored-by: J. Nick Koston <nick@koston.org>
This commit is contained in:
Álvaro Fernández Rojas
2022-04-27 01:10:23 +02:00
committed by GitHub
parent bd8e705252
commit 8a2b20faf0
18 changed files with 837 additions and 0 deletions

View File

@ -796,6 +796,8 @@ build.json @home-assistant/supervisor
/homeassistant/components/qbittorrent/ @geoffreylagaisse
/homeassistant/components/qld_bushfire/ @exxamalte
/tests/components/qld_bushfire/ @exxamalte
/homeassistant/components/qnap_qsw/ @Noltari
/tests/components/qnap_qsw/ @Noltari
/homeassistant/components/quantum_gateway/ @cisasteelersfan
/homeassistant/components/qvr_pro/ @oblogic7
/homeassistant/components/qwikswitch/ @kellerza

View File

@ -0,0 +1,89 @@
"""The QNAP QSW integration."""
from __future__ import annotations
from typing import Any
from aioqsw.const import (
QSD_FIRMWARE,
QSD_FIRMWARE_INFO,
QSD_MAC,
QSD_PRODUCT,
QSD_SYSTEM_BOARD,
)
from aioqsw.localapi import ConnectionOptions, QnapQswApi
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_PASSWORD, CONF_URL, CONF_USERNAME, Platform
from homeassistant.core import HomeAssistant
from homeassistant.helpers import aiohttp_client
from homeassistant.helpers.device_registry import CONNECTION_NETWORK_MAC
from homeassistant.helpers.entity import DeviceInfo
from homeassistant.helpers.update_coordinator import CoordinatorEntity
from .const import DOMAIN, MANUFACTURER
from .coordinator import QswUpdateCoordinator
PLATFORMS: list[Platform] = [Platform.SENSOR]
class QswEntity(CoordinatorEntity[QswUpdateCoordinator]):
"""Define an QNAP QSW entity."""
def __init__(
self,
coordinator: QswUpdateCoordinator,
entry: ConfigEntry,
) -> None:
"""Initialize."""
super().__init__(coordinator)
self._attr_device_info = DeviceInfo(
configuration_url=entry.data[CONF_URL],
connections={
(
CONNECTION_NETWORK_MAC,
self.get_device_value(QSD_SYSTEM_BOARD, QSD_MAC),
)
},
manufacturer=MANUFACTURER,
model=self.get_device_value(QSD_SYSTEM_BOARD, QSD_PRODUCT),
name=self.get_device_value(QSD_SYSTEM_BOARD, QSD_PRODUCT),
sw_version=self.get_device_value(QSD_FIRMWARE_INFO, QSD_FIRMWARE),
)
def get_device_value(self, key: str, subkey: str) -> Any:
"""Return device value by key."""
value = None
if key in self.coordinator.data:
data = self.coordinator.data[key]
if subkey in data:
value = data[subkey]
return value
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
"""Set up QNAP QSW from a config entry."""
options = ConnectionOptions(
entry.data[CONF_URL],
entry.data[CONF_USERNAME],
entry.data[CONF_PASSWORD],
)
qsw = QnapQswApi(aiohttp_client.async_get_clientsession(hass), options)
coordinator = QswUpdateCoordinator(hass, qsw)
await coordinator.async_config_entry_first_refresh()
hass.data.setdefault(DOMAIN, {})[entry.entry_id] = coordinator
hass.config_entries.async_setup_platforms(entry, PLATFORMS)
return True
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
"""Unload a config entry."""
if unload_ok := await hass.config_entries.async_unload_platforms(entry, PLATFORMS):
hass.data[DOMAIN].pop(entry.entry_id)
return unload_ok

View File

@ -0,0 +1,65 @@
"""Config flow for QNAP QSW."""
from __future__ import annotations
from typing import Any
from aioqsw.exceptions import LoginError, QswError
from aioqsw.localapi import ConnectionOptions, QnapQswApi
import voluptuous as vol
from homeassistant import config_entries
from homeassistant.const import CONF_PASSWORD, CONF_URL, CONF_USERNAME
from homeassistant.data_entry_flow import AbortFlow, FlowResult
from homeassistant.helpers import aiohttp_client
from homeassistant.helpers.device_registry import format_mac
from .const import DOMAIN
class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
"""Handle config flow for a QNAP QSW device."""
async def async_step_user(
self, user_input: dict[str, Any] | None = None
) -> FlowResult:
"""Handle the initial step."""
errors = {}
if user_input is not None:
url = user_input[CONF_URL]
username = user_input[CONF_USERNAME]
password = user_input[CONF_PASSWORD]
qsw = QnapQswApi(
aiohttp_client.async_get_clientsession(self.hass),
ConnectionOptions(url, username, password),
)
try:
system_board = await qsw.validate()
except LoginError:
errors[CONF_PASSWORD] = "invalid_auth"
except QswError:
errors[CONF_URL] = "cannot_connect"
else:
mac = system_board.get_mac()
if mac is None:
raise AbortFlow("invalid_id")
await self.async_set_unique_id(format_mac(mac))
self._abort_if_unique_id_configured()
title = f"QNAP {system_board.get_product()} {mac}"
return self.async_create_entry(title=title, data=user_input)
return self.async_show_form(
step_id="user",
data_schema=vol.Schema(
{
vol.Required(CONF_URL): str,
vol.Required(CONF_USERNAME): str,
vol.Required(CONF_PASSWORD): str,
}
),
errors=errors,
)

View File

@ -0,0 +1,12 @@
"""Constants for the QNAP QSW integration."""
from typing import Final
ATTR_MAX: Final = "max"
DOMAIN: Final = "qnap_qsw"
MANUFACTURER: Final = "QNAP"
RPM: Final = "rpm"
QSW_TIMEOUT_SEC: Final = 25

View File

@ -0,0 +1,42 @@
"""The QNAP QSW coordinator."""
from __future__ import annotations
from datetime import timedelta
import logging
from aioqsw.exceptions import QswError
from aioqsw.localapi import QnapQswApi
import async_timeout
from homeassistant.core import HomeAssistant
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
from .const import DOMAIN, QSW_TIMEOUT_SEC
SCAN_INTERVAL = timedelta(seconds=60)
_LOGGER = logging.getLogger(__name__)
class QswUpdateCoordinator(DataUpdateCoordinator):
"""Class to manage fetching data from the QNAP QSW device."""
def __init__(self, hass: HomeAssistant, qsw: QnapQswApi) -> None:
"""Initialize."""
self.qsw = qsw
super().__init__(
hass,
_LOGGER,
name=DOMAIN,
update_interval=SCAN_INTERVAL,
)
async def _async_update_data(self):
"""Update data via library."""
async with async_timeout.timeout(QSW_TIMEOUT_SEC):
try:
await self.qsw.update()
except QswError as error:
raise UpdateFailed(error) from error
return self.qsw.data()

View File

@ -0,0 +1,10 @@
{
"domain": "qnap_qsw",
"name": "QNAP QSW",
"config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/qnap_qsw",
"requirements": ["aioqsw==0.0.5"],
"codeowners": ["@Noltari"],
"iot_class": "local_polling",
"loggers": ["aioqsw"]
}

View File

@ -0,0 +1,135 @@
"""Support for the QNAP QSW sensors."""
from __future__ import annotations
from dataclasses import dataclass
from typing import Final
from aioqsw.const import (
QSD_FAN1_SPEED,
QSD_FAN2_SPEED,
QSD_PRODUCT,
QSD_SYSTEM_BOARD,
QSD_SYSTEM_SENSOR,
QSD_SYSTEM_TIME,
QSD_TEMP,
QSD_TEMP_MAX,
QSD_UPTIME,
)
from homeassistant.components.sensor import (
SensorDeviceClass,
SensorEntity,
SensorEntityDescription,
SensorStateClass,
)
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import TEMP_CELSIUS, TIME_SECONDS
from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers.entity import EntityCategory
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from . import QswEntity
from .const import ATTR_MAX, DOMAIN, RPM
from .coordinator import QswUpdateCoordinator
@dataclass
class QswSensorEntityDescription(SensorEntityDescription):
"""A class that describes QNAP QSW sensor entities."""
attributes: dict[str, list[str]] | None = None
subkey: str = ""
SENSOR_TYPES: Final[tuple[QswSensorEntityDescription, ...]] = (
QswSensorEntityDescription(
key=QSD_SYSTEM_SENSOR,
name="Fan 1 Speed",
native_unit_of_measurement=RPM,
state_class=SensorStateClass.MEASUREMENT,
subkey=QSD_FAN1_SPEED,
),
QswSensorEntityDescription(
key=QSD_SYSTEM_SENSOR,
name="Fan 2 Speed",
native_unit_of_measurement=RPM,
state_class=SensorStateClass.MEASUREMENT,
subkey=QSD_FAN2_SPEED,
),
QswSensorEntityDescription(
attributes={
ATTR_MAX: [QSD_SYSTEM_SENSOR, QSD_TEMP_MAX],
},
device_class=SensorDeviceClass.TEMPERATURE,
key=QSD_SYSTEM_SENSOR,
name="Temperature",
native_unit_of_measurement=TEMP_CELSIUS,
state_class=SensorStateClass.MEASUREMENT,
subkey=QSD_TEMP,
),
QswSensorEntityDescription(
key=QSD_SYSTEM_TIME,
entity_category=EntityCategory.DIAGNOSTIC,
name="Uptime",
native_unit_of_measurement=TIME_SECONDS,
state_class=SensorStateClass.TOTAL_INCREASING,
subkey=QSD_UPTIME,
),
)
async def async_setup_entry(
hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback
) -> None:
"""Add QNAP QSW sensors from a config_entry."""
coordinator: QswUpdateCoordinator = hass.data[DOMAIN][entry.entry_id]
async_add_entities(
QswSensor(coordinator, description, entry)
for description in SENSOR_TYPES
if (
description.key in coordinator.data
and description.subkey in coordinator.data[description.key]
)
)
class QswSensor(QswEntity, SensorEntity):
"""Define a QNAP QSW sensor."""
entity_description: QswSensorEntityDescription
def __init__(
self,
coordinator: QswUpdateCoordinator,
description: QswSensorEntityDescription,
entry: ConfigEntry,
) -> None:
"""Initialize."""
super().__init__(coordinator, entry)
self._attr_name = (
f"{self.get_device_value(QSD_SYSTEM_BOARD, QSD_PRODUCT)} {description.name}"
)
self._attr_unique_id = (
f"{entry.unique_id}_{description.key}_{description.subkey}"
)
self.entity_description = description
self._async_update_attrs()
@callback
def _handle_coordinator_update(self) -> None:
"""Update attributes when the coordinator updates."""
self._async_update_attrs()
super()._handle_coordinator_update()
@callback
def _async_update_attrs(self) -> None:
"""Update sensor attributes."""
self._attr_native_value = self.get_device_value(
self.entity_description.key, self.entity_description.subkey
)
if self.entity_description.attributes:
self._attr_extra_state_attributes = {
key: self.get_device_value(val[0], val[1])
for key, val in self.entity_description.attributes.items()
}

View File

@ -0,0 +1,21 @@
{
"config": {
"abort": {
"already_configured": "[%key:common::config_flow::abort::already_configured_device%]",
"invalid_id": "Device returned an invalid unique ID"
},
"error": {
"cannot_connect": "[%key:common::config_flow::error::cannot_connect%]",
"invalid_auth": "[%key:common::config_flow::error::invalid_auth%]"
},
"step": {
"user": {
"data": {
"url": "[%key:common::config_flow::data::url%]",
"username": "[%key:common::config_flow::data::username%]",
"password": "[%key:common::config_flow::data::password%]"
}
}
}
}
}

View File

@ -0,0 +1,21 @@
{
"config": {
"abort": {
"already_configured": "Device is already configured",
"invalid_id": "Device returned an invalid unique ID"
},
"error": {
"cannot_connect": "Failed to connect",
"invalid_auth": "Invalid authentication"
},
"step": {
"user": {
"data": {
"url": "URL",
"username": "Username",
"password": "Password"
}
}
}
}
}

View File

@ -271,6 +271,7 @@ FLOWS = {
"pure_energie",
"pvoutput",
"pvpc_hourly_pricing",
"qnap_qsw",
"rachio",
"radio_browser",
"rainforest_eagle",

View File

@ -225,6 +225,9 @@ aiopvpc==3.0.0
# homeassistant.components.sonarr
aiopyarr==22.2.2
# homeassistant.components.qnap_qsw
aioqsw==0.0.5
# homeassistant.components.recollect_waste
aiorecollect==1.0.8

View File

@ -191,6 +191,9 @@ aiopvpc==3.0.0
# homeassistant.components.sonarr
aiopyarr==22.2.2
# homeassistant.components.qnap_qsw
aioqsw==0.0.5
# homeassistant.components.recollect_waste
aiorecollect==1.0.8

View File

@ -0,0 +1 @@
"""Tests for the QNAP QSW integration."""

View File

@ -0,0 +1,136 @@
"""Define tests for the QNAP QSW config flow."""
from unittest.mock import MagicMock, patch
from aioqsw.const import API_MAC_ADDR, API_PRODUCT, API_RESULT
from aioqsw.exceptions import LoginError, QswError
from homeassistant import data_entry_flow
from homeassistant.components.qnap_qsw.const import DOMAIN
from homeassistant.config_entries import SOURCE_USER, ConfigEntryState
from homeassistant.const import CONF_PASSWORD, CONF_URL, CONF_USERNAME
from homeassistant.core import HomeAssistant
from homeassistant.helpers.device_registry import format_mac
from .util import CONFIG, LIVE_MOCK, SYSTEM_BOARD_MOCK, USERS_LOGIN_MOCK
from tests.common import MockConfigEntry
async def test_form(hass: HomeAssistant) -> None:
"""Test that the form is served with valid input."""
with patch(
"homeassistant.components.qnap_qsw.async_setup_entry",
return_value=True,
) as mock_setup_entry, patch(
"homeassistant.components.qnap_qsw.QnapQswApi.get_live",
return_value=LIVE_MOCK,
), patch(
"homeassistant.components.qnap_qsw.QnapQswApi.get_system_board",
return_value=SYSTEM_BOARD_MOCK,
), patch(
"homeassistant.components.qnap_qsw.QnapQswApi.post_users_login",
return_value=USERS_LOGIN_MOCK,
):
result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": SOURCE_USER}
)
assert result["type"] == data_entry_flow.RESULT_TYPE_FORM
assert result["step_id"] == SOURCE_USER
assert result["errors"] == {}
result = await hass.config_entries.flow.async_configure(
result["flow_id"], CONFIG
)
await hass.async_block_till_done()
conf_entries = hass.config_entries.async_entries(DOMAIN)
entry = conf_entries[0]
assert entry.state is ConfigEntryState.LOADED
assert result["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY
assert (
result["title"]
== f"QNAP {SYSTEM_BOARD_MOCK[API_RESULT][API_PRODUCT]} {SYSTEM_BOARD_MOCK[API_RESULT][API_MAC_ADDR]}"
)
assert result["data"][CONF_URL] == CONFIG[CONF_URL]
assert result["data"][CONF_USERNAME] == CONFIG[CONF_USERNAME]
assert result["data"][CONF_PASSWORD] == CONFIG[CONF_PASSWORD]
assert len(mock_setup_entry.mock_calls) == 1
async def test_form_duplicated_id(hass: HomeAssistant) -> None:
"""Test setting up duplicated entry."""
system_board = MagicMock()
system_board.get_mac = MagicMock(
return_value=SYSTEM_BOARD_MOCK[API_RESULT][API_MAC_ADDR]
)
entry = MockConfigEntry(
domain=DOMAIN,
data=CONFIG,
unique_id=format_mac(SYSTEM_BOARD_MOCK[API_RESULT][API_MAC_ADDR]),
)
entry.add_to_hass(hass)
with patch(
"homeassistant.components.qnap_qsw.QnapQswApi.validate",
return_value=system_board,
):
result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": SOURCE_USER}, data=CONFIG
)
assert result["type"] == "abort"
assert result["reason"] == "already_configured"
async def test_form_unique_id_error(hass: HomeAssistant):
"""Test unique ID error."""
system_board = MagicMock()
system_board.get_mac = MagicMock(return_value=None)
with patch(
"homeassistant.components.qnap_qsw.QnapQswApi.validate",
return_value=system_board,
):
result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": SOURCE_USER}, data=CONFIG
)
assert result["type"] == "abort"
assert result["reason"] == "invalid_id"
async def test_connection_error(hass: HomeAssistant):
"""Test connection to host error."""
with patch(
"homeassistant.components.qnap_qsw.QnapQswApi.validate",
side_effect=QswError,
):
result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": SOURCE_USER}, data=CONFIG
)
assert result["errors"] == {CONF_URL: "cannot_connect"}
async def test_login_error(hass: HomeAssistant):
"""Test login error."""
with patch(
"homeassistant.components.qnap_qsw.QnapQswApi.validate",
side_effect=LoginError,
):
result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": SOURCE_USER}, data=CONFIG
)
assert result["errors"] == {CONF_PASSWORD: "invalid_auth"}

View File

@ -0,0 +1,83 @@
"""Define tests for the QNAP QSW coordinator."""
from unittest.mock import patch
from aioqsw.exceptions import QswError
from homeassistant.components.qnap_qsw.const import DOMAIN
from homeassistant.components.qnap_qsw.coordinator import SCAN_INTERVAL
from homeassistant.const import STATE_UNAVAILABLE
from homeassistant.core import HomeAssistant
from homeassistant.util.dt import utcnow
from .util import (
CONFIG,
FIRMWARE_CONDITION_MOCK,
FIRMWARE_INFO_MOCK,
SYSTEM_BOARD_MOCK,
SYSTEM_SENSOR_MOCK,
SYSTEM_TIME_MOCK,
USERS_LOGIN_MOCK,
USERS_VERIFICATION_MOCK,
)
from tests.common import MockConfigEntry, async_fire_time_changed
async def test_coordinator_client_connector_error(hass: HomeAssistant) -> None:
"""Test ClientConnectorError on coordinator update."""
entry = MockConfigEntry(domain=DOMAIN, data=CONFIG)
entry.add_to_hass(hass)
with patch(
"homeassistant.components.qnap_qsw.QnapQswApi.get_firmware_condition",
return_value=FIRMWARE_CONDITION_MOCK,
) as mock_firmware_condition, patch(
"homeassistant.components.qnap_qsw.QnapQswApi.get_firmware_info",
return_value=FIRMWARE_INFO_MOCK,
) as mock_firmware_info, patch(
"homeassistant.components.qnap_qsw.QnapQswApi.get_system_board",
return_value=SYSTEM_BOARD_MOCK,
) as mock_system_board, patch(
"homeassistant.components.qnap_qsw.QnapQswApi.get_system_sensor",
return_value=SYSTEM_SENSOR_MOCK,
) as mock_system_sensor, patch(
"homeassistant.components.qnap_qsw.QnapQswApi.get_system_time",
return_value=SYSTEM_TIME_MOCK,
) as mock_system_time, patch(
"homeassistant.components.qnap_qsw.QnapQswApi.get_users_verification",
return_value=USERS_VERIFICATION_MOCK,
) as mock_users_verification, patch(
"homeassistant.components.qnap_qsw.QnapQswApi.post_users_login",
return_value=USERS_LOGIN_MOCK,
) as mock_users_login:
await hass.config_entries.async_setup(entry.entry_id)
await hass.async_block_till_done()
mock_firmware_condition.assert_called_once()
mock_firmware_info.assert_called_once()
mock_system_board.assert_called_once()
mock_system_sensor.assert_called_once()
mock_system_time.assert_called_once()
mock_users_verification.assert_not_called()
mock_users_login.assert_called_once()
mock_firmware_condition.reset_mock()
mock_firmware_info.reset_mock()
mock_system_board.reset_mock()
mock_system_sensor.reset_mock()
mock_system_time.reset_mock()
mock_users_verification.reset_mock()
mock_users_login.reset_mock()
mock_system_sensor.side_effect = QswError
async_fire_time_changed(hass, utcnow() + SCAN_INTERVAL)
await hass.async_block_till_done()
mock_system_sensor.assert_called_once()
mock_users_verification.assert_called_once()
mock_users_login.assert_not_called()
state = hass.states.get("sensor.qsw_m408_4c_temperature")
assert state.state == STATE_UNAVAILABLE

View File

@ -0,0 +1,35 @@
"""Define tests for the QNAP QSW init."""
from unittest.mock import patch
from homeassistant.components.qnap_qsw.const import DOMAIN
from homeassistant.config_entries import ConfigEntryState
from homeassistant.core import HomeAssistant
from .util import CONFIG
from tests.common import MockConfigEntry
async def test_unload_entry(hass: HomeAssistant) -> None:
"""Test unload."""
config_entry = MockConfigEntry(
domain=DOMAIN, unique_id="qsw_unique_id", data=CONFIG
)
config_entry.add_to_hass(hass)
with patch(
"homeassistant.components.qnap_qsw.QnapQswApi.validate",
return_value=None,
), patch(
"homeassistant.components.qnap_qsw.QnapQswApi.update",
return_value=None,
):
assert await hass.config_entries.async_setup(config_entry.entry_id)
await hass.async_block_till_done()
assert config_entry.state is ConfigEntryState.LOADED
await hass.config_entries.async_unload(config_entry.entry_id)
await hass.async_block_till_done()
assert config_entry.state is ConfigEntryState.NOT_LOADED

View File

@ -0,0 +1,25 @@
"""The sensor tests for the QNAP QSW platform."""
from homeassistant.components.qnap_qsw.const import ATTR_MAX
from homeassistant.core import HomeAssistant
from .util import async_init_integration
async def test_qnap_qsw_create_sensors(hass: HomeAssistant) -> None:
"""Test creation of sensors."""
await async_init_integration(hass)
state = hass.states.get("sensor.qsw_m408_4c_fan_1_speed")
assert state.state == "1991"
state = hass.states.get("sensor.qsw_m408_4c_fan_2_speed")
assert state is None
state = hass.states.get("sensor.qsw_m408_4c_temperature")
assert state.state == "31"
assert state.attributes.get(ATTR_MAX) == 85
state = hass.states.get("sensor.qsw_m408_4c_uptime")
assert state.state == "91"

View File

@ -0,0 +1,153 @@
"""Tests for the QNAP QSW integration."""
from unittest.mock import patch
from aioqsw.const import (
API_ANOMALY,
API_BUILD_NUMBER,
API_CHIP_ID,
API_CI_BRANCH,
API_CI_COMMIT,
API_CI_PIPELINE,
API_COMMIT_CPSS,
API_COMMIT_ISS,
API_DATE,
API_ERROR_CODE,
API_ERROR_MESSAGE,
API_FAN1_SPEED,
API_FAN2_SPEED,
API_MAC_ADDR,
API_MAX_SWITCH_TEMP,
API_MESSAGE,
API_MODEL,
API_NUMBER,
API_PORT_NUM,
API_PRODUCT,
API_PUB_DATE,
API_RESULT,
API_SERIAL,
API_SWITCH_TEMP,
API_TRUNK_NUM,
API_UPTIME,
API_VERSION,
)
from homeassistant.components.qnap_qsw import DOMAIN
from homeassistant.const import CONF_PASSWORD, CONF_URL, CONF_USERNAME
from homeassistant.core import HomeAssistant
from tests.common import MockConfigEntry
CONFIG = {
CONF_URL: "http://192.168.1.100",
CONF_USERNAME: "username",
CONF_PASSWORD: "password",
}
LIVE_MOCK = {
API_ERROR_CODE: 200,
API_ERROR_MESSAGE: "OK",
API_RESULT: "None",
}
SYSTEM_BOARD_MOCK = {
API_ERROR_CODE: 200,
API_ERROR_MESSAGE: "OK",
API_RESULT: {
API_MAC_ADDR: "MAC",
API_SERIAL: "SERIAL",
API_CHIP_ID: "ALLEYCAT3",
API_MODEL: "M408",
API_PORT_NUM: 12,
API_PRODUCT: "QSW-M408-4C",
API_TRUNK_NUM: 0,
},
}
FIRMWARE_CONDITION_MOCK = {
API_ERROR_CODE: 200,
API_ERROR_MESSAGE: "OK",
API_RESULT: {
API_ANOMALY: False,
API_MESSAGE: "",
},
}
FIRMWARE_INFO_MOCK = {
API_ERROR_CODE: 200,
API_ERROR_MESSAGE: "OK",
API_RESULT: {
API_VERSION: "1.2.0",
API_DATE: "20220128",
API_PUB_DATE: "Fri, 28 Jan 2022 01:17:39 +0800",
API_BUILD_NUMBER: "20220128",
API_NUMBER: "29649",
API_CI_COMMIT: "b2eb4c8ffb549995aeb4f9c4e645c6d882997c17",
API_CI_BRANCH: "m408/codesigning",
API_CI_PIPELINE: "9898",
API_COMMIT_CPSS: "",
API_COMMIT_ISS: "448a3208e5ea744c393b2580f4b9733add9c2faa",
},
}
SYSTEM_SENSOR_MOCK = {
API_ERROR_CODE: 200,
API_ERROR_MESSAGE: "OK",
API_RESULT: {
API_FAN1_SPEED: 1991,
API_FAN2_SPEED: -2,
API_MAX_SWITCH_TEMP: 85,
API_SWITCH_TEMP: 31,
},
}
SYSTEM_TIME_MOCK = {
API_ERROR_CODE: 200,
API_ERROR_MESSAGE: "OK",
API_RESULT: {
API_UPTIME: 91,
},
}
USERS_LOGIN_MOCK = {
API_ERROR_CODE: 200,
API_ERROR_MESSAGE: "OK",
API_RESULT: "TOKEN",
}
USERS_VERIFICATION_MOCK = {
API_ERROR_CODE: 200,
API_ERROR_MESSAGE: "OK",
API_RESULT: "None",
}
async def async_init_integration(
hass: HomeAssistant,
) -> None:
"""Set up the QNAP QSW integration in Home Assistant."""
entry = MockConfigEntry(domain=DOMAIN, data=CONFIG)
entry.add_to_hass(hass)
with patch(
"homeassistant.components.qnap_qsw.QnapQswApi.get_firmware_condition",
return_value=FIRMWARE_CONDITION_MOCK,
), patch(
"homeassistant.components.qnap_qsw.QnapQswApi.get_firmware_info",
return_value=FIRMWARE_INFO_MOCK,
), patch(
"homeassistant.components.qnap_qsw.QnapQswApi.get_system_board",
return_value=SYSTEM_BOARD_MOCK,
), patch(
"homeassistant.components.qnap_qsw.QnapQswApi.get_system_sensor",
return_value=SYSTEM_SENSOR_MOCK,
), patch(
"homeassistant.components.qnap_qsw.QnapQswApi.get_system_time",
return_value=SYSTEM_TIME_MOCK,
), patch(
"homeassistant.components.qnap_qsw.QnapQswApi.post_users_login",
return_value=USERS_LOGIN_MOCK,
):
await hass.config_entries.async_setup(entry.entry_id)
await hass.async_block_till_done()