diff --git a/homeassistant/components/qnap_qsw/__init__.py b/homeassistant/components/qnap_qsw/__init__.py index 26ed8066686..4d2ee76cd2b 100644 --- a/homeassistant/components/qnap_qsw/__init__.py +++ b/homeassistant/components/qnap_qsw/__init__.py @@ -8,10 +8,15 @@ from homeassistant.const import CONF_PASSWORD, CONF_URL, CONF_USERNAME, Platform from homeassistant.core import HomeAssistant from homeassistant.helpers import aiohttp_client -from .const import DOMAIN -from .coordinator import QswUpdateCoordinator +from .const import DOMAIN, QSW_COORD_DATA, QSW_COORD_FW +from .coordinator import QswDataCoordinator, QswFirmwareCoordinator -PLATFORMS: list[Platform] = [Platform.BINARY_SENSOR, Platform.BUTTON, Platform.SENSOR] +PLATFORMS: list[Platform] = [ + Platform.BINARY_SENSOR, + Platform.BUTTON, + Platform.SENSOR, + Platform.UPDATE, +] async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: @@ -24,10 +29,16 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: qsw = QnapQswApi(aiohttp_client.async_get_clientsession(hass), options) - coordinator = QswUpdateCoordinator(hass, qsw) - await coordinator.async_config_entry_first_refresh() + coord_data = QswDataCoordinator(hass, qsw) + await coord_data.async_config_entry_first_refresh() - hass.data.setdefault(DOMAIN, {})[entry.entry_id] = coordinator + coord_fw = QswFirmwareCoordinator(hass, qsw) + await coord_fw.async_config_entry_first_refresh() + + hass.data.setdefault(DOMAIN, {})[entry.entry_id] = { + QSW_COORD_DATA: coord_data, + QSW_COORD_FW: coord_fw, + } hass.config_entries.async_setup_platforms(entry, PLATFORMS) diff --git a/homeassistant/components/qnap_qsw/binary_sensor.py b/homeassistant/components/qnap_qsw/binary_sensor.py index 467a3314070..71af89778b8 100644 --- a/homeassistant/components/qnap_qsw/binary_sensor.py +++ b/homeassistant/components/qnap_qsw/binary_sensor.py @@ -16,8 +16,8 @@ from homeassistant.core import HomeAssistant, callback from homeassistant.helpers.entity import EntityCategory from homeassistant.helpers.entity_platform import AddEntitiesCallback -from .const import ATTR_MESSAGE, DOMAIN -from .coordinator import QswUpdateCoordinator +from .const import ATTR_MESSAGE, DOMAIN, QSW_COORD_DATA +from .coordinator import QswDataCoordinator from .entity import QswEntityDescription, QswSensorEntity @@ -48,7 +48,7 @@ async def async_setup_entry( hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback ) -> None: """Add QNAP QSW binary sensors from a config_entry.""" - coordinator: QswUpdateCoordinator = hass.data[DOMAIN][entry.entry_id] + coordinator: QswDataCoordinator = hass.data[DOMAIN][entry.entry_id][QSW_COORD_DATA] async_add_entities( QswBinarySensor(coordinator, description, entry) for description in BINARY_SENSOR_TYPES @@ -66,7 +66,7 @@ class QswBinarySensor(QswSensorEntity, BinarySensorEntity): def __init__( self, - coordinator: QswUpdateCoordinator, + coordinator: QswDataCoordinator, description: QswBinarySensorEntityDescription, entry: ConfigEntry, ) -> None: diff --git a/homeassistant/components/qnap_qsw/button.py b/homeassistant/components/qnap_qsw/button.py index 1c13310fe05..9aad411b992 100644 --- a/homeassistant/components/qnap_qsw/button.py +++ b/homeassistant/components/qnap_qsw/button.py @@ -17,9 +17,9 @@ from homeassistant.core import HomeAssistant from homeassistant.helpers.entity import EntityCategory from homeassistant.helpers.entity_platform import AddEntitiesCallback -from .const import DOMAIN, QSW_REBOOT -from .coordinator import QswUpdateCoordinator -from .entity import QswEntity +from .const import DOMAIN, QSW_COORD_DATA, QSW_REBOOT +from .coordinator import QswDataCoordinator +from .entity import QswDataEntity @dataclass @@ -49,20 +49,20 @@ async def async_setup_entry( hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback ) -> None: """Add QNAP QSW buttons from a config_entry.""" - coordinator: QswUpdateCoordinator = hass.data[DOMAIN][entry.entry_id] + coordinator: QswDataCoordinator = hass.data[DOMAIN][entry.entry_id][QSW_COORD_DATA] async_add_entities( QswButton(coordinator, description, entry) for description in BUTTON_TYPES ) -class QswButton(QswEntity, ButtonEntity): +class QswButton(QswDataEntity, ButtonEntity): """Define a QNAP QSW button.""" entity_description: QswButtonDescription def __init__( self, - coordinator: QswUpdateCoordinator, + coordinator: QswDataCoordinator, description: QswButtonDescription, entry: ConfigEntry, ) -> None: diff --git a/homeassistant/components/qnap_qsw/const.py b/homeassistant/components/qnap_qsw/const.py index e583c0250f4..4b5fa9a4a2c 100644 --- a/homeassistant/components/qnap_qsw/const.py +++ b/homeassistant/components/qnap_qsw/const.py @@ -10,5 +10,8 @@ MANUFACTURER: Final = "QNAP" RPM: Final = "rpm" +QSW_COORD_DATA: Final = "coordinator-data" +QSW_COORD_FW: Final = "coordinator-firmware" QSW_REBOOT = "reboot" QSW_TIMEOUT_SEC: Final = 25 +QSW_UPDATE: Final = "update" diff --git a/homeassistant/components/qnap_qsw/coordinator.py b/homeassistant/components/qnap_qsw/coordinator.py index c018c1f3848..73af2f74cc5 100644 --- a/homeassistant/components/qnap_qsw/coordinator.py +++ b/homeassistant/components/qnap_qsw/coordinator.py @@ -5,7 +5,7 @@ from datetime import timedelta import logging from typing import Any -from aioqsw.exceptions import QswError +from aioqsw.exceptions import APIError, QswError from aioqsw.localapi import QnapQswApi import async_timeout @@ -14,12 +14,13 @@ from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, Upda from .const import DOMAIN, QSW_TIMEOUT_SEC -SCAN_INTERVAL = timedelta(seconds=60) +DATA_SCAN_INTERVAL = timedelta(seconds=60) +FW_SCAN_INTERVAL = timedelta(hours=12) _LOGGER = logging.getLogger(__name__) -class QswUpdateCoordinator(DataUpdateCoordinator[dict[str, Any]]): +class QswDataCoordinator(DataUpdateCoordinator[dict[str, Any]]): """Class to manage fetching data from the QNAP QSW device.""" def __init__(self, hass: HomeAssistant, qsw: QnapQswApi) -> None: @@ -30,7 +31,7 @@ class QswUpdateCoordinator(DataUpdateCoordinator[dict[str, Any]]): hass, _LOGGER, name=DOMAIN, - update_interval=SCAN_INTERVAL, + update_interval=DATA_SCAN_INTERVAL, ) async def _async_update_data(self) -> dict[str, Any]: @@ -41,3 +42,29 @@ class QswUpdateCoordinator(DataUpdateCoordinator[dict[str, Any]]): except QswError as error: raise UpdateFailed(error) from error return self.qsw.data() + + +class QswFirmwareCoordinator(DataUpdateCoordinator[dict[str, Any]]): + """Class to manage fetching firmware 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=FW_SCAN_INTERVAL, + ) + + async def _async_update_data(self) -> dict[str, Any]: + """Update firmware data via library.""" + async with async_timeout.timeout(QSW_TIMEOUT_SEC): + try: + await self.qsw.check_firmware() + except APIError as error: + _LOGGER.warning(error) + except QswError as error: + raise UpdateFailed(error) from error + return self.qsw.data() diff --git a/homeassistant/components/qnap_qsw/diagnostics.py b/homeassistant/components/qnap_qsw/diagnostics.py index 3730bab24a8..2467e9181a3 100644 --- a/homeassistant/components/qnap_qsw/diagnostics.py +++ b/homeassistant/components/qnap_qsw/diagnostics.py @@ -10,8 +10,8 @@ from homeassistant.config_entries import ConfigEntry from homeassistant.const import CONF_PASSWORD, CONF_UNIQUE_ID, CONF_USERNAME from homeassistant.core import HomeAssistant -from .const import DOMAIN -from .coordinator import QswUpdateCoordinator +from .const import DOMAIN, QSW_COORD_DATA, QSW_COORD_FW +from .coordinator import QswDataCoordinator, QswFirmwareCoordinator TO_REDACT_CONFIG = [ CONF_USERNAME, @@ -29,9 +29,12 @@ async def async_get_config_entry_diagnostics( hass: HomeAssistant, config_entry: ConfigEntry ) -> dict[str, Any]: """Return diagnostics for a config entry.""" - coordinator: QswUpdateCoordinator = hass.data[DOMAIN][config_entry.entry_id] + entry_data = hass.data[DOMAIN][config_entry.entry_id] + coord_data: QswDataCoordinator = entry_data[QSW_COORD_DATA] + coord_fw: QswFirmwareCoordinator = entry_data[QSW_COORD_FW] return { "config_entry": async_redact_data(config_entry.as_dict(), TO_REDACT_CONFIG), - "coord_data": async_redact_data(coordinator.data, TO_REDACT_DATA), + "coord_data": async_redact_data(coord_data.data, TO_REDACT_DATA), + "coord_fw": async_redact_data(coord_fw.data, TO_REDACT_DATA), } diff --git a/homeassistant/components/qnap_qsw/entity.py b/homeassistant/components/qnap_qsw/entity.py index c3550610d83..7da47f9734f 100644 --- a/homeassistant/components/qnap_qsw/entity.py +++ b/homeassistant/components/qnap_qsw/entity.py @@ -20,15 +20,15 @@ from homeassistant.helpers.entity import DeviceInfo, EntityDescription from homeassistant.helpers.update_coordinator import CoordinatorEntity from .const import MANUFACTURER -from .coordinator import QswUpdateCoordinator +from .coordinator import QswDataCoordinator, QswFirmwareCoordinator -class QswEntity(CoordinatorEntity[QswUpdateCoordinator]): +class QswDataEntity(CoordinatorEntity[QswDataCoordinator]): """Define an QNAP QSW entity.""" def __init__( self, - coordinator: QswUpdateCoordinator, + coordinator: QswDataCoordinator, entry: ConfigEntry, ) -> None: """Initialize.""" @@ -72,7 +72,7 @@ class QswEntityDescription(EntityDescription, QswEntityDescriptionMixin): attributes: dict[str, list[str]] | None = None -class QswSensorEntity(QswEntity): +class QswSensorEntity(QswDataEntity): """Base class for QSW sensor entities.""" entity_description: QswEntityDescription @@ -91,3 +91,38 @@ class QswSensorEntity(QswEntity): key: self.get_device_value(val[0], val[1]) for key, val in self.entity_description.attributes.items() } + + +class QswFirmwareEntity(CoordinatorEntity[QswFirmwareCoordinator]): + """Define a QNAP QSW firmware entity.""" + + def __init__( + self, + coordinator: QswFirmwareCoordinator, + 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 diff --git a/homeassistant/components/qnap_qsw/sensor.py b/homeassistant/components/qnap_qsw/sensor.py index 0de8ec4a39e..618c20b4cc2 100644 --- a/homeassistant/components/qnap_qsw/sensor.py +++ b/homeassistant/components/qnap_qsw/sensor.py @@ -26,8 +26,8 @@ from homeassistant.core import HomeAssistant, callback from homeassistant.helpers.entity import EntityCategory from homeassistant.helpers.entity_platform import AddEntitiesCallback -from .const import ATTR_MAX, DOMAIN, RPM -from .coordinator import QswUpdateCoordinator +from .const import ATTR_MAX, DOMAIN, QSW_COORD_DATA, RPM +from .coordinator import QswDataCoordinator from .entity import QswEntityDescription, QswSensorEntity @@ -82,7 +82,7 @@ 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] + coordinator: QswDataCoordinator = hass.data[DOMAIN][entry.entry_id][QSW_COORD_DATA] async_add_entities( QswSensor(coordinator, description, entry) for description in SENSOR_TYPES @@ -100,7 +100,7 @@ class QswSensor(QswSensorEntity, SensorEntity): def __init__( self, - coordinator: QswUpdateCoordinator, + coordinator: QswDataCoordinator, description: QswSensorEntityDescription, entry: ConfigEntry, ) -> None: diff --git a/homeassistant/components/qnap_qsw/update.py b/homeassistant/components/qnap_qsw/update.py new file mode 100644 index 00000000000..8dfd985ffef --- /dev/null +++ b/homeassistant/components/qnap_qsw/update.py @@ -0,0 +1,89 @@ +"""Support for the QNAP QSW update.""" +from __future__ import annotations + +from typing import Final + +from aioqsw.const import ( + QSD_DESCRIPTION, + QSD_FIRMWARE_CHECK, + QSD_FIRMWARE_INFO, + QSD_PRODUCT, + QSD_SYSTEM_BOARD, + QSD_VERSION, +) + +from homeassistant.components.update import ( + UpdateDeviceClass, + UpdateEntity, + UpdateEntityDescription, +) +from homeassistant.config_entries import ConfigEntry +from homeassistant.core import HomeAssistant, callback +from homeassistant.helpers.entity import EntityCategory +from homeassistant.helpers.entity_platform import AddEntitiesCallback + +from .const import DOMAIN, QSW_COORD_FW, QSW_UPDATE +from .coordinator import QswFirmwareCoordinator +from .entity import QswFirmwareEntity + +UPDATE_TYPES: Final[tuple[UpdateEntityDescription, ...]] = ( + UpdateEntityDescription( + device_class=UpdateDeviceClass.FIRMWARE, + entity_category=EntityCategory.CONFIG, + key=QSW_UPDATE, + name="Firmware Update", + ), +) + + +async def async_setup_entry( + hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback +) -> None: + """Add QNAP QSW updates from a config_entry.""" + coordinator: QswFirmwareCoordinator = hass.data[DOMAIN][entry.entry_id][ + QSW_COORD_FW + ] + async_add_entities( + QswUpdate(coordinator, description, entry) for description in UPDATE_TYPES + ) + + +class QswUpdate(QswFirmwareEntity, UpdateEntity): + """Define a QNAP QSW update.""" + + entity_description: UpdateEntityDescription + + def __init__( + self, + coordinator: QswFirmwareCoordinator, + description: UpdateEntityDescription, + 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}" + self.entity_description = description + + self._attr_installed_version = self.get_device_value( + QSD_FIRMWARE_INFO, QSD_VERSION + ) + 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 attributes.""" + self._attr_latest_version = self.get_device_value( + QSD_FIRMWARE_CHECK, QSD_VERSION + ) + self._attr_release_summary = self.get_device_value( + QSD_FIRMWARE_CHECK, QSD_DESCRIPTION + ) diff --git a/tests/components/qnap_qsw/test_coordinator.py b/tests/components/qnap_qsw/test_coordinator.py index b8e4855fea9..107cfa580b7 100644 --- a/tests/components/qnap_qsw/test_coordinator.py +++ b/tests/components/qnap_qsw/test_coordinator.py @@ -2,10 +2,13 @@ from unittest.mock import patch -from aioqsw.exceptions import QswError +from aioqsw.exceptions import APIError, QswError from homeassistant.components.qnap_qsw.const import DOMAIN -from homeassistant.components.qnap_qsw.coordinator import SCAN_INTERVAL +from homeassistant.components.qnap_qsw.coordinator import ( + DATA_SCAN_INTERVAL, + FW_SCAN_INTERVAL, +) from homeassistant.const import STATE_UNAVAILABLE from homeassistant.core import HomeAssistant from homeassistant.util.dt import utcnow @@ -14,6 +17,7 @@ from .util import ( CONFIG, FIRMWARE_CONDITION_MOCK, FIRMWARE_INFO_MOCK, + FIRMWARE_UPDATE_CHECK_MOCK, SYSTEM_BOARD_MOCK, SYSTEM_SENSOR_MOCK, SYSTEM_TIME_MOCK, @@ -37,6 +41,9 @@ async def test_coordinator_client_connector_error(hass: HomeAssistant) -> None: "homeassistant.components.qnap_qsw.QnapQswApi.get_firmware_info", return_value=FIRMWARE_INFO_MOCK, ) as mock_firmware_info, patch( + "homeassistant.components.qnap_qsw.QnapQswApi.get_firmware_update_check", + return_value=FIRMWARE_UPDATE_CHECK_MOCK, + ) as mock_firmware_update_check, patch( "homeassistant.components.qnap_qsw.QnapQswApi.get_system_board", return_value=SYSTEM_BOARD_MOCK, ) as mock_system_board, patch( @@ -57,14 +64,16 @@ async def test_coordinator_client_connector_error(hass: HomeAssistant) -> None: mock_firmware_condition.assert_called_once() mock_firmware_info.assert_called_once() + mock_firmware_update_check.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_verification.assert_called_once() mock_users_login.assert_called_once() mock_firmware_condition.reset_mock() mock_firmware_info.reset_mock() + mock_firmware_update_check.reset_mock() mock_system_board.reset_mock() mock_system_sensor.reset_mock() mock_system_time.reset_mock() @@ -72,12 +81,28 @@ async def test_coordinator_client_connector_error(hass: HomeAssistant) -> None: mock_users_login.reset_mock() mock_system_sensor.side_effect = QswError - async_fire_time_changed(hass, utcnow() + SCAN_INTERVAL) + async_fire_time_changed(hass, utcnow() + DATA_SCAN_INTERVAL) await hass.async_block_till_done() mock_system_sensor.assert_called_once() - mock_users_verification.assert_called_once() + mock_users_verification.assert_called() mock_users_login.assert_not_called() state = hass.states.get("sensor.qsw_m408_4c_temperature") assert state.state == STATE_UNAVAILABLE + + mock_firmware_update_check.side_effect = APIError + async_fire_time_changed(hass, utcnow() + FW_SCAN_INTERVAL) + await hass.async_block_till_done() + + mock_firmware_update_check.assert_called_once() + mock_firmware_update_check.reset_mock() + + mock_firmware_update_check.side_effect = QswError + async_fire_time_changed(hass, utcnow() + FW_SCAN_INTERVAL) + await hass.async_block_till_done() + + mock_firmware_update_check.assert_called_once() + + update = hass.states.get("update.qsw_m408_4c_firmware_update") + assert update.state == STATE_UNAVAILABLE diff --git a/tests/components/qnap_qsw/test_init.py b/tests/components/qnap_qsw/test_init.py index 211cd7ed41d..75fa9ec8adc 100644 --- a/tests/components/qnap_qsw/test_init.py +++ b/tests/components/qnap_qsw/test_init.py @@ -20,6 +20,9 @@ async def test_unload_entry(hass: HomeAssistant) -> None: config_entry.add_to_hass(hass) with patch( + "homeassistant.components.qnap_qsw.QnapQswApi.check_firmware", + return_value=None, + ), patch( "homeassistant.components.qnap_qsw.QnapQswApi.validate", return_value=None, ), patch( diff --git a/tests/components/qnap_qsw/test_update.py b/tests/components/qnap_qsw/test_update.py new file mode 100644 index 00000000000..69f4a3d08b4 --- /dev/null +++ b/tests/components/qnap_qsw/test_update.py @@ -0,0 +1,26 @@ +"""The sensor tests for the QNAP QSW platform.""" + +from aioqsw.const import API_RESULT, API_VERSION + +from homeassistant.const import STATE_OFF +from homeassistant.core import HomeAssistant + +from .util import FIRMWARE_INFO_MOCK, FIRMWARE_UPDATE_CHECK_MOCK, async_init_integration + + +async def test_qnap_qsw_update(hass: HomeAssistant) -> None: + """Test creation of update entities.""" + + await async_init_integration(hass) + + update = hass.states.get("update.qsw_m408_4c_firmware_update") + assert update is not None + assert update.state == STATE_OFF + assert ( + update.attributes.get("installed_version") + == FIRMWARE_INFO_MOCK[API_RESULT][API_VERSION] + ) + assert ( + update.attributes.get("latest_version") + == FIRMWARE_UPDATE_CHECK_MOCK[API_RESULT][API_VERSION] + ) diff --git a/tests/components/qnap_qsw/util.py b/tests/components/qnap_qsw/util.py index 28e7f7881d5..a057dfbe3ac 100644 --- a/tests/components/qnap_qsw/util.py +++ b/tests/components/qnap_qsw/util.py @@ -12,6 +12,8 @@ from aioqsw.const import ( API_COMMIT_CPSS, API_COMMIT_ISS, API_DATE, + API_DESCRIPTION, + API_DOWNLOAD_URL, API_ERROR_CODE, API_ERROR_MESSAGE, API_FAN1_SPEED, @@ -20,6 +22,7 @@ from aioqsw.const import ( API_MAX_SWITCH_TEMP, API_MESSAGE, API_MODEL, + API_NEWER, API_NUMBER, API_PORT_NUM, API_PRODUCT, @@ -90,6 +93,24 @@ FIRMWARE_INFO_MOCK = { }, } +FIRMWARE_UPDATE_CHECK_MOCK = { + API_ERROR_CODE: 200, + API_ERROR_MESSAGE: "OK", + API_RESULT: { + API_VERSION: "1.2.0", + API_NUMBER: "29649", + API_BUILD_NUMBER: "20220128", + API_DATE: "Fri, 28 Jan 2022 01:17:39 +0800", + API_DESCRIPTION: "", + API_DOWNLOAD_URL: [ + "https://download.qnap.com/Storage/Networking/QSW408FW/QSW-M408AC3-FW.v1.2.0_S20220128_29649.img", + "https://eu1.qnap.com/Storage/Networking/QSW408FW/QSW-M408AC3-FW.v1.2.0_S20220128_29649.img", + "https://us1.qnap.com/Storage/Networking/QSW408FW/QSW-M408AC3-FW.v1.2.0_S20220128_29649.img", + ], + API_NEWER: False, + }, +} + SYSTEM_COMMAND_MOCK = { API_ERROR_CODE: 200, API_ERROR_MESSAGE: "OK", @@ -146,6 +167,9 @@ async def async_init_integration( ), patch( "homeassistant.components.qnap_qsw.QnapQswApi.get_firmware_info", return_value=FIRMWARE_INFO_MOCK, + ), patch( + "homeassistant.components.qnap_qsw.QnapQswApi.get_firmware_update_check", + return_value=FIRMWARE_UPDATE_CHECK_MOCK, ), patch( "homeassistant.components.qnap_qsw.QnapQswApi.get_system_board", return_value=SYSTEM_BOARD_MOCK, @@ -155,6 +179,9 @@ async def async_init_integration( ), patch( "homeassistant.components.qnap_qsw.QnapQswApi.get_system_time", return_value=SYSTEM_TIME_MOCK, + ), patch( + "homeassistant.components.qnap_qsw.QnapQswApi.get_users_verification", + return_value=USERS_VERIFICATION_MOCK, ), patch( "homeassistant.components.qnap_qsw.QnapQswApi.post_users_login", return_value=USERS_LOGIN_MOCK,