mirror of
https://github.com/home-assistant/core.git
synced 2025-04-30 04:07:51 +00:00
Make Synology DSM integration fully async (#85904)
This commit is contained in:
parent
65ca62c991
commit
a7ebec4d02
@ -84,12 +84,10 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
|
||||
# For SSDP compat
|
||||
if not entry.data.get(CONF_MAC):
|
||||
network = await hass.async_add_executor_job(getattr, api.dsm, "network")
|
||||
hass.config_entries.async_update_entry(
|
||||
entry, data={**entry.data, CONF_MAC: network.macs}
|
||||
entry, data={**entry.data, CONF_MAC: api.dsm.network.macs}
|
||||
)
|
||||
|
||||
# These all create executor jobs so we do not gather here
|
||||
coordinator_central = SynologyDSMCentralUpdateCoordinator(hass, entry, api)
|
||||
await coordinator_central.async_config_entry_first_refresh()
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
"""Support for Synology DSM buttons."""
|
||||
from __future__ import annotations
|
||||
|
||||
from collections.abc import Callable
|
||||
from collections.abc import Callable, Coroutine
|
||||
from dataclasses import dataclass
|
||||
import logging
|
||||
from typing import Any, Final
|
||||
@ -27,7 +27,7 @@ LOGGER = logging.getLogger(__name__)
|
||||
class SynologyDSMbuttonDescriptionMixin:
|
||||
"""Mixin to describe a Synology DSM button entity."""
|
||||
|
||||
press_action: Callable[[SynoApi], Any]
|
||||
press_action: Callable[[SynoApi], Callable[[], Coroutine[Any, Any, None]]]
|
||||
|
||||
|
||||
@dataclass
|
||||
@ -43,14 +43,14 @@ BUTTONS: Final = [
|
||||
name="Reboot",
|
||||
device_class=ButtonDeviceClass.RESTART,
|
||||
entity_category=EntityCategory.CONFIG,
|
||||
press_action=lambda syno_api: syno_api.async_reboot(),
|
||||
press_action=lambda syno_api: syno_api.async_reboot,
|
||||
),
|
||||
SynologyDSMbuttonDescription(
|
||||
key="shutdown",
|
||||
name="Shutdown",
|
||||
icon="mdi:power",
|
||||
entity_category=EntityCategory.CONFIG,
|
||||
press_action=lambda syno_api: syno_api.async_shutdown(),
|
||||
press_action=lambda syno_api: syno_api.async_shutdown,
|
||||
),
|
||||
]
|
||||
|
||||
@ -92,4 +92,4 @@ class SynologyDSMButton(ButtonEntity):
|
||||
self.entity_description.key,
|
||||
self.syno_api.network.hostname,
|
||||
)
|
||||
await self.entity_description.press_action(self.syno_api)
|
||||
await self.entity_description.press_action(self.syno_api)()
|
||||
|
@ -143,7 +143,7 @@ class SynoDSMCamera(SynologyDSMBaseEntity[SynologyDSMCameraUpdateCoordinator], C
|
||||
self._listen_source_updates()
|
||||
await super().async_added_to_hass()
|
||||
|
||||
def camera_image(
|
||||
async def async_camera_image(
|
||||
self, width: int | None = None, height: int | None = None
|
||||
) -> bytes | None:
|
||||
"""Return bytes of camera image."""
|
||||
@ -154,7 +154,7 @@ class SynoDSMCamera(SynologyDSMBaseEntity[SynologyDSMCameraUpdateCoordinator], C
|
||||
if not self.available:
|
||||
return None
|
||||
try:
|
||||
return self._api.surveillance_station.get_camera_image(self.entity_description.key, self.snapshot_quality) # type: ignore[no-any-return]
|
||||
return await self._api.surveillance_station.get_camera_image(self.entity_description.key, self.snapshot_quality) # type: ignore[no-any-return]
|
||||
except (
|
||||
SynologyDSMAPIErrorException,
|
||||
SynologyDSMRequestException,
|
||||
@ -178,22 +178,22 @@ class SynoDSMCamera(SynologyDSMBaseEntity[SynologyDSMCameraUpdateCoordinator], C
|
||||
|
||||
return self.camera_data.live_view.rtsp # type: ignore[no-any-return]
|
||||
|
||||
def enable_motion_detection(self) -> None:
|
||||
async def async_enable_motion_detection(self) -> None:
|
||||
"""Enable motion detection in the camera."""
|
||||
_LOGGER.debug(
|
||||
"SynoDSMCamera.enable_motion_detection(%s)",
|
||||
self.camera_data.name,
|
||||
)
|
||||
self._api.surveillance_station.enable_motion_detection(
|
||||
await self._api.surveillance_station.enable_motion_detection(
|
||||
self.entity_description.key
|
||||
)
|
||||
|
||||
def disable_motion_detection(self) -> None:
|
||||
async def async_disable_motion_detection(self) -> None:
|
||||
"""Disable motion detection in camera."""
|
||||
_LOGGER.debug(
|
||||
"SynoDSMCamera.disable_motion_detection(%s)",
|
||||
self.camera_data.name,
|
||||
)
|
||||
self._api.surveillance_station.disable_motion_detection(
|
||||
await self._api.surveillance_station.disable_motion_detection(
|
||||
self.entity_description.key
|
||||
)
|
||||
|
@ -30,6 +30,7 @@ from homeassistant.const import (
|
||||
CONF_VERIFY_SSL,
|
||||
)
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.helpers.aiohttp_client import async_get_clientsession
|
||||
|
||||
from .const import CONF_DEVICE_TOKEN, SYNOLOGY_CONNECTION_EXCEPTIONS
|
||||
|
||||
@ -71,21 +72,18 @@ class SynoApi:
|
||||
|
||||
async def async_setup(self) -> None:
|
||||
"""Start interacting with the NAS."""
|
||||
await self._hass.async_add_executor_job(self._setup)
|
||||
|
||||
def _setup(self) -> None:
|
||||
"""Start interacting with the NAS in the executor."""
|
||||
session = async_get_clientsession(self._hass, self._entry.data[CONF_VERIFY_SSL])
|
||||
self.dsm = SynologyDSM(
|
||||
session,
|
||||
self._entry.data[CONF_HOST],
|
||||
self._entry.data[CONF_PORT],
|
||||
self._entry.data[CONF_USERNAME],
|
||||
self._entry.data[CONF_PASSWORD],
|
||||
self._entry.data[CONF_SSL],
|
||||
self._entry.data[CONF_VERIFY_SSL],
|
||||
timeout=self._entry.options.get(CONF_TIMEOUT),
|
||||
device_token=self._entry.data.get(CONF_DEVICE_TOKEN),
|
||||
)
|
||||
self.dsm.login()
|
||||
await self.dsm.login()
|
||||
|
||||
# check if surveillance station is used
|
||||
self._with_surveillance_station = bool(
|
||||
@ -93,7 +91,7 @@ class SynoApi:
|
||||
)
|
||||
if self._with_surveillance_station:
|
||||
try:
|
||||
self.dsm.surveillance_station.update()
|
||||
await self.dsm.surveillance_station.update()
|
||||
except SYNOLOGY_CONNECTION_EXCEPTIONS:
|
||||
self._with_surveillance_station = False
|
||||
self.dsm.reset(SynoSurveillanceStation.API_KEY)
|
||||
@ -110,16 +108,16 @@ class SynoApi:
|
||||
|
||||
# check if upgrade is available
|
||||
try:
|
||||
self.dsm.upgrade.update()
|
||||
await self.dsm.upgrade.update()
|
||||
except SYNOLOGY_CONNECTION_EXCEPTIONS as ex:
|
||||
self._with_upgrade = False
|
||||
self.dsm.reset(SynoCoreUpgrade.API_KEY)
|
||||
LOGGER.debug("Disabled fetching upgrade data during setup: %s", ex)
|
||||
|
||||
self._fetch_device_configuration()
|
||||
await self._fetch_device_configuration()
|
||||
|
||||
try:
|
||||
self._update()
|
||||
await self._update()
|
||||
except SYNOLOGY_CONNECTION_EXCEPTIONS as err:
|
||||
LOGGER.debug(
|
||||
"Connection error during setup of '%s' with exception: %s",
|
||||
@ -210,11 +208,11 @@ class SynoApi:
|
||||
self.dsm.reset(self.utilisation)
|
||||
self.utilisation = None
|
||||
|
||||
def _fetch_device_configuration(self) -> None:
|
||||
async def _fetch_device_configuration(self) -> None:
|
||||
"""Fetch initial device config."""
|
||||
self.information = self.dsm.information
|
||||
self.network = self.dsm.network
|
||||
self.network.update()
|
||||
await self.network.update()
|
||||
|
||||
if self._with_security:
|
||||
LOGGER.debug("Enable security api updates for '%s'", self._entry.unique_id)
|
||||
@ -248,7 +246,7 @@ class SynoApi:
|
||||
async def _syno_api_executer(self, api_call: Callable) -> None:
|
||||
"""Synology api call wrapper."""
|
||||
try:
|
||||
await self._hass.async_add_executor_job(api_call)
|
||||
await api_call()
|
||||
except (SynologyDSMAPIErrorException, SynologyDSMRequestException) as err:
|
||||
LOGGER.debug(
|
||||
"Error from '%s': %s", self._entry.unique_id, err, exc_info=True
|
||||
@ -274,7 +272,7 @@ class SynoApi:
|
||||
async def async_update(self) -> None:
|
||||
"""Update function for updating API information."""
|
||||
try:
|
||||
await self._hass.async_add_executor_job(self._update)
|
||||
await self._update()
|
||||
except SYNOLOGY_CONNECTION_EXCEPTIONS as err:
|
||||
LOGGER.debug(
|
||||
"Connection error during update of '%s' with exception: %s",
|
||||
@ -286,8 +284,8 @@ class SynoApi:
|
||||
)
|
||||
await self._hass.config_entries.async_reload(self._entry.entry_id)
|
||||
|
||||
def _update(self) -> None:
|
||||
async def _update(self) -> None:
|
||||
"""Update function for updating API information."""
|
||||
LOGGER.debug("Start data update for '%s'", self._entry.unique_id)
|
||||
self._setup_api_requests()
|
||||
self.dsm.update(self._with_information)
|
||||
await self.dsm.update(self._with_information)
|
||||
|
@ -35,6 +35,7 @@ from homeassistant.const import (
|
||||
)
|
||||
from homeassistant.core import callback
|
||||
from homeassistant.data_entry_flow import FlowResult
|
||||
from homeassistant.helpers.aiohttp_client import async_get_clientsession
|
||||
import homeassistant.helpers.config_validation as cv
|
||||
from homeassistant.helpers.typing import DiscoveryInfoType
|
||||
|
||||
@ -172,15 +173,12 @@ class SynologyDSMFlowHandler(ConfigFlow, domain=DOMAIN):
|
||||
else:
|
||||
port = DEFAULT_PORT
|
||||
|
||||
api = SynologyDSM(
|
||||
host, port, username, password, use_ssl, verify_ssl, timeout=30
|
||||
)
|
||||
session = async_get_clientsession(self.hass, verify_ssl)
|
||||
api = SynologyDSM(session, host, port, username, password, use_ssl, timeout=30)
|
||||
|
||||
errors = {}
|
||||
try:
|
||||
serial = await self.hass.async_add_executor_job(
|
||||
_login_and_fetch_syno_info, api, otp_code
|
||||
)
|
||||
serial = await _login_and_fetch_syno_info(api, otp_code)
|
||||
except SynologyDSMLogin2SARequiredException:
|
||||
return await self.async_step_2sa(user_input)
|
||||
except SynologyDSMLogin2SAFailedException:
|
||||
@ -386,13 +384,13 @@ class SynologyDSMOptionsFlowHandler(OptionsFlow):
|
||||
return self.async_show_form(step_id="init", data_schema=data_schema)
|
||||
|
||||
|
||||
def _login_and_fetch_syno_info(api: SynologyDSM, otp_code: str | None) -> str:
|
||||
async def _login_and_fetch_syno_info(api: SynologyDSM, otp_code: str | None) -> str:
|
||||
"""Login to the NAS and fetch basic data."""
|
||||
# These do i/o
|
||||
api.login(otp_code)
|
||||
api.utilisation.update()
|
||||
api.storage.update()
|
||||
api.network.update()
|
||||
await api.login(otp_code)
|
||||
await api.utilisation.update()
|
||||
await api.storage.update()
|
||||
await api.network.update()
|
||||
|
||||
if (
|
||||
not api.information.serial
|
||||
|
@ -5,7 +5,6 @@ from datetime import timedelta
|
||||
import logging
|
||||
from typing import Any, TypeVar
|
||||
|
||||
import async_timeout
|
||||
from synology_dsm.api.surveillance_station.camera import SynoCamera
|
||||
from synology_dsm.exceptions import SynologyDSMAPIErrorException
|
||||
|
||||
@ -64,20 +63,14 @@ class SynologyDSMSwitchUpdateCoordinator(
|
||||
|
||||
async def async_setup(self) -> None:
|
||||
"""Set up the coordinator initial data."""
|
||||
info = await self.hass.async_add_executor_job(
|
||||
self.api.dsm.surveillance_station.get_info
|
||||
)
|
||||
info = await self.api.dsm.surveillance_station.get_info()
|
||||
self.version = info["data"]["CMSMinVersion"]
|
||||
|
||||
async def _async_update_data(self) -> dict[str, dict[str, Any]]:
|
||||
"""Fetch all data from api."""
|
||||
surveillance_station = self.api.surveillance_station
|
||||
return {
|
||||
"switches": {
|
||||
"home_mode": await self.hass.async_add_executor_job(
|
||||
surveillance_station.get_home_mode_status
|
||||
)
|
||||
}
|
||||
"switches": {"home_mode": await surveillance_station.get_home_mode_status()}
|
||||
}
|
||||
|
||||
|
||||
@ -131,8 +124,7 @@ class SynologyDSMCameraUpdateCoordinator(
|
||||
}
|
||||
|
||||
try:
|
||||
async with async_timeout.timeout(30):
|
||||
await self.hass.async_add_executor_job(surveillance_station.update)
|
||||
await surveillance_station.update()
|
||||
except SynologyDSMAPIErrorException as err:
|
||||
raise UpdateFailed(f"Error communicating with API: {err}") from err
|
||||
|
||||
|
@ -2,7 +2,7 @@
|
||||
"domain": "synology_dsm",
|
||||
"name": "Synology DSM",
|
||||
"documentation": "https://www.home-assistant.io/integrations/synology_dsm",
|
||||
"requirements": ["py-synologydsm-api==1.0.8"],
|
||||
"requirements": ["py-synologydsm-api==2.0.1"],
|
||||
"codeowners": ["@hacf-fr", "@Quentame", "@mib1185"],
|
||||
"config_flow": true,
|
||||
"ssdp": [
|
||||
|
@ -87,9 +87,7 @@ class SynoDSMSurveillanceHomeModeToggle(
|
||||
"SynoDSMSurveillanceHomeModeToggle.turn_on(%s)",
|
||||
self._api.information.serial,
|
||||
)
|
||||
await self.hass.async_add_executor_job(
|
||||
self._api.dsm.surveillance_station.set_home_mode, True
|
||||
)
|
||||
await self._api.dsm.surveillance_station.set_home_mode(True)
|
||||
await self.coordinator.async_request_refresh()
|
||||
|
||||
async def async_turn_off(self, **kwargs: Any) -> None:
|
||||
@ -98,9 +96,7 @@ class SynoDSMSurveillanceHomeModeToggle(
|
||||
"SynoDSMSurveillanceHomeModeToggle.turn_off(%s)",
|
||||
self._api.information.serial,
|
||||
)
|
||||
await self.hass.async_add_executor_job(
|
||||
self._api.dsm.surveillance_station.set_home_mode, False
|
||||
)
|
||||
await self._api.dsm.surveillance_station.set_home_mode(False)
|
||||
await self.coordinator.async_request_refresh()
|
||||
|
||||
@property
|
||||
|
@ -1433,7 +1433,7 @@ py-schluter==0.1.7
|
||||
py-sucks==0.9.8
|
||||
|
||||
# homeassistant.components.synology_dsm
|
||||
py-synologydsm-api==1.0.8
|
||||
py-synologydsm-api==2.0.1
|
||||
|
||||
# homeassistant.components.zabbix
|
||||
py-zabbix==1.1.7
|
||||
|
@ -1042,7 +1042,7 @@ py-melissa-climate==2.1.4
|
||||
py-nightscout==1.2.2
|
||||
|
||||
# homeassistant.components.synology_dsm
|
||||
py-synologydsm-api==1.0.8
|
||||
py-synologydsm-api==2.0.1
|
||||
|
||||
# homeassistant.components.seventeentrack
|
||||
py17track==2021.12.2
|
||||
|
@ -1,5 +1,5 @@
|
||||
"""Configure Synology DSM tests."""
|
||||
from unittest.mock import patch
|
||||
from unittest.mock import AsyncMock, patch
|
||||
|
||||
import pytest
|
||||
|
||||
@ -21,3 +21,17 @@ def bypass_setup_fixture(request):
|
||||
"homeassistant.components.synology_dsm.async_setup_entry", return_value=True
|
||||
):
|
||||
yield
|
||||
|
||||
|
||||
@pytest.fixture(name="mock_dsm")
|
||||
def fixture_dsm():
|
||||
"""Set up SynologyDSM API fixture."""
|
||||
with patch("homeassistant.components.synology_dsm.common.SynologyDSM") as dsm:
|
||||
dsm.login = AsyncMock(return_value=True)
|
||||
dsm.update = AsyncMock(return_value=True)
|
||||
|
||||
dsm.network.update = AsyncMock(return_value=True)
|
||||
dsm.surveillance_station.update = AsyncMock(return_value=True)
|
||||
dsm.upgrade.update = AsyncMock(return_value=True)
|
||||
|
||||
yield dsm
|
||||
|
@ -1,5 +1,5 @@
|
||||
"""Tests for the Synology DSM config flow."""
|
||||
from unittest.mock import MagicMock, Mock, patch
|
||||
from unittest.mock import AsyncMock, MagicMock, Mock, patch
|
||||
|
||||
import pytest
|
||||
from synology_dsm.exceptions import (
|
||||
@ -59,60 +59,89 @@ from tests.common import MockConfigEntry
|
||||
@pytest.fixture(name="service")
|
||||
def mock_controller_service():
|
||||
"""Mock a successful service."""
|
||||
with patch(
|
||||
"homeassistant.components.synology_dsm.config_flow.SynologyDSM"
|
||||
) as service_mock:
|
||||
service_mock.return_value.information.serial = SERIAL
|
||||
service_mock.return_value.utilisation.cpu_user_load = 1
|
||||
service_mock.return_value.storage.disks_ids = ["sda", "sdb", "sdc"]
|
||||
service_mock.return_value.storage.volumes_ids = ["volume_1"]
|
||||
service_mock.return_value.network.macs = MACS
|
||||
yield service_mock
|
||||
with patch("homeassistant.components.synology_dsm.config_flow.SynologyDSM") as dsm:
|
||||
|
||||
dsm.login = AsyncMock(return_value=True)
|
||||
dsm.update = AsyncMock(return_value=True)
|
||||
|
||||
dsm.surveillance_station.update = AsyncMock(return_value=True)
|
||||
dsm.upgrade.update = AsyncMock(return_value=True)
|
||||
dsm.utilisation = Mock(cpu_user_load=1, update=AsyncMock(return_value=True))
|
||||
dsm.network = Mock(update=AsyncMock(return_value=True), macs=MACS)
|
||||
dsm.storage = Mock(
|
||||
disks_ids=["sda", "sdb", "sdc"],
|
||||
volumes_ids=["volume_1"],
|
||||
update=AsyncMock(return_value=True),
|
||||
)
|
||||
dsm.information = Mock(serial=SERIAL)
|
||||
|
||||
yield dsm
|
||||
|
||||
|
||||
@pytest.fixture(name="service_2sa")
|
||||
def mock_controller_service_2sa():
|
||||
"""Mock a successful service with 2SA login."""
|
||||
with patch(
|
||||
"homeassistant.components.synology_dsm.config_flow.SynologyDSM"
|
||||
) as service_mock:
|
||||
service_mock.return_value.login = Mock(
|
||||
with patch("homeassistant.components.synology_dsm.config_flow.SynologyDSM") as dsm:
|
||||
dsm.login = AsyncMock(
|
||||
side_effect=SynologyDSMLogin2SARequiredException(USERNAME)
|
||||
)
|
||||
service_mock.return_value.information.serial = SERIAL
|
||||
service_mock.return_value.utilisation.cpu_user_load = 1
|
||||
service_mock.return_value.storage.disks_ids = ["sda", "sdb", "sdc"]
|
||||
service_mock.return_value.storage.volumes_ids = ["volume_1"]
|
||||
service_mock.return_value.network.macs = MACS
|
||||
yield service_mock
|
||||
dsm.update = AsyncMock(return_value=True)
|
||||
|
||||
dsm.surveillance_station.update = AsyncMock(return_value=True)
|
||||
dsm.upgrade.update = AsyncMock(return_value=True)
|
||||
dsm.utilisation = Mock(cpu_user_load=1, update=AsyncMock(return_value=True))
|
||||
dsm.network = Mock(update=AsyncMock(return_value=True), macs=MACS)
|
||||
dsm.storage = Mock(
|
||||
disks_ids=["sda", "sdb", "sdc"],
|
||||
volumes_ids=["volume_1"],
|
||||
update=AsyncMock(return_value=True),
|
||||
)
|
||||
dsm.information = Mock(serial=SERIAL)
|
||||
yield dsm
|
||||
|
||||
|
||||
@pytest.fixture(name="service_vdsm")
|
||||
def mock_controller_service_vdsm():
|
||||
"""Mock a successful service."""
|
||||
with patch(
|
||||
"homeassistant.components.synology_dsm.config_flow.SynologyDSM"
|
||||
) as service_mock:
|
||||
service_mock.return_value.information.serial = SERIAL
|
||||
service_mock.return_value.utilisation.cpu_user_load = 1
|
||||
service_mock.return_value.storage.disks_ids = []
|
||||
service_mock.return_value.storage.volumes_ids = ["volume_1"]
|
||||
service_mock.return_value.network.macs = MACS
|
||||
yield service_mock
|
||||
with patch("homeassistant.components.synology_dsm.config_flow.SynologyDSM") as dsm:
|
||||
|
||||
dsm.login = AsyncMock(return_value=True)
|
||||
dsm.update = AsyncMock(return_value=True)
|
||||
|
||||
dsm.surveillance_station.update = AsyncMock(return_value=True)
|
||||
dsm.upgrade.update = AsyncMock(return_value=True)
|
||||
dsm.utilisation = Mock(cpu_user_load=1, update=AsyncMock(return_value=True))
|
||||
dsm.network = Mock(update=AsyncMock(return_value=True), macs=MACS)
|
||||
dsm.storage = Mock(
|
||||
disks_ids=[],
|
||||
volumes_ids=["volume_1"],
|
||||
update=AsyncMock(return_value=True),
|
||||
)
|
||||
dsm.information = Mock(serial=SERIAL)
|
||||
|
||||
yield dsm
|
||||
|
||||
|
||||
@pytest.fixture(name="service_failed")
|
||||
def mock_controller_service_failed():
|
||||
"""Mock a failed service."""
|
||||
with patch(
|
||||
"homeassistant.components.synology_dsm.config_flow.SynologyDSM"
|
||||
) as service_mock:
|
||||
service_mock.return_value.information.serial = None
|
||||
service_mock.return_value.utilisation.cpu_user_load = None
|
||||
service_mock.return_value.storage.disks_ids = []
|
||||
service_mock.return_value.storage.volumes_ids = []
|
||||
service_mock.return_value.network.macs = []
|
||||
yield service_mock
|
||||
with patch("homeassistant.components.synology_dsm.config_flow.SynologyDSM") as dsm:
|
||||
|
||||
dsm.login = AsyncMock(return_value=True)
|
||||
dsm.update = AsyncMock(return_value=True)
|
||||
|
||||
dsm.surveillance_station.update = AsyncMock(return_value=True)
|
||||
dsm.upgrade.update = AsyncMock(return_value=True)
|
||||
dsm.utilisation = Mock(cpu_user_load=None, update=AsyncMock(return_value=True))
|
||||
dsm.network = Mock(update=AsyncMock(return_value=True), macs=[])
|
||||
dsm.storage = Mock(
|
||||
disks_ids=[],
|
||||
volumes_ids=[],
|
||||
update=AsyncMock(return_value=True),
|
||||
)
|
||||
dsm.information = Mock(serial=None)
|
||||
|
||||
yield dsm
|
||||
|
||||
|
||||
async def test_user(hass: HomeAssistant, service: MagicMock):
|
||||
@ -123,6 +152,10 @@ async def test_user(hass: HomeAssistant, service: MagicMock):
|
||||
assert result["type"] == data_entry_flow.FlowResultType.FORM
|
||||
assert result["step_id"] == "user"
|
||||
|
||||
with patch(
|
||||
"homeassistant.components.synology_dsm.config_flow.SynologyDSM",
|
||||
return_value=service,
|
||||
):
|
||||
# test with all provided
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN,
|
||||
@ -150,7 +183,11 @@ async def test_user(hass: HomeAssistant, service: MagicMock):
|
||||
assert result["data"].get(CONF_DISKS) is None
|
||||
assert result["data"].get(CONF_VOLUMES) is None
|
||||
|
||||
service.return_value.information.serial = SERIAL_2
|
||||
service.information.serial = SERIAL_2
|
||||
with patch(
|
||||
"homeassistant.components.synology_dsm.config_flow.SynologyDSM",
|
||||
return_value=service,
|
||||
):
|
||||
# test without port + False SSL
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN,
|
||||
@ -180,6 +217,10 @@ async def test_user(hass: HomeAssistant, service: MagicMock):
|
||||
|
||||
async def test_user_2sa(hass: HomeAssistant, service_2sa: MagicMock):
|
||||
"""Test user with 2sa authentication config."""
|
||||
with patch(
|
||||
"homeassistant.components.synology_dsm.config_flow.SynologyDSM",
|
||||
return_value=service_2sa,
|
||||
):
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN,
|
||||
context={"source": SOURCE_USER},
|
||||
@ -200,8 +241,13 @@ async def test_user_2sa(hass: HomeAssistant, service_2sa: MagicMock):
|
||||
assert result["errors"] == {CONF_OTP_CODE: "otp_failed"}
|
||||
|
||||
# Successful login with 2SA code
|
||||
service_2sa.return_value.login = Mock(return_value=True)
|
||||
service_2sa.return_value.device_token = DEVICE_TOKEN
|
||||
service_2sa.login = AsyncMock(return_value=True)
|
||||
service_2sa.device_token = DEVICE_TOKEN
|
||||
|
||||
with patch(
|
||||
"homeassistant.components.synology_dsm.config_flow.SynologyDSM",
|
||||
return_value=service_2sa,
|
||||
):
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"], {CONF_OTP_CODE: "123456"}
|
||||
)
|
||||
@ -223,12 +269,20 @@ async def test_user_2sa(hass: HomeAssistant, service_2sa: MagicMock):
|
||||
|
||||
async def test_user_vdsm(hass: HomeAssistant, service_vdsm: MagicMock):
|
||||
"""Test user config."""
|
||||
with patch(
|
||||
"homeassistant.components.synology_dsm.config_flow.SynologyDSM",
|
||||
return_value=service_vdsm,
|
||||
):
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN, context={"source": SOURCE_USER}, data=None
|
||||
)
|
||||
assert result["type"] == data_entry_flow.FlowResultType.FORM
|
||||
assert result["step_id"] == "user"
|
||||
|
||||
with patch(
|
||||
"homeassistant.components.synology_dsm.config_flow.SynologyDSM",
|
||||
return_value=service_vdsm,
|
||||
):
|
||||
# test with all provided
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN,
|
||||
@ -292,6 +346,10 @@ async def test_reauth(hass: HomeAssistant, service: MagicMock):
|
||||
assert result["type"] == data_entry_flow.FlowResultType.FORM
|
||||
assert result["step_id"] == "reauth_confirm"
|
||||
|
||||
with patch(
|
||||
"homeassistant.components.synology_dsm.config_flow.SynologyDSM",
|
||||
return_value=service,
|
||||
):
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
{
|
||||
@ -318,6 +376,9 @@ async def test_reconfig_user(hass: HomeAssistant, service: MagicMock):
|
||||
with patch(
|
||||
"homeassistant.config_entries.ConfigEntries.async_reload",
|
||||
return_value=True,
|
||||
), patch(
|
||||
"homeassistant.components.synology_dsm.config_flow.SynologyDSM",
|
||||
return_value=service,
|
||||
):
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN,
|
||||
@ -375,6 +436,10 @@ async def test_unknown_failed(hass: HomeAssistant, service: MagicMock):
|
||||
|
||||
async def test_missing_data_after_login(hass: HomeAssistant, service_failed: MagicMock):
|
||||
"""Test when we have errors during connection."""
|
||||
with patch(
|
||||
"homeassistant.components.synology_dsm.config_flow.SynologyDSM",
|
||||
return_value=service_failed,
|
||||
):
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN,
|
||||
context={"source": SOURCE_USER},
|
||||
@ -404,6 +469,10 @@ async def test_form_ssdp(hass: HomeAssistant, service: MagicMock):
|
||||
assert result["step_id"] == "link"
|
||||
assert result["errors"] == {}
|
||||
|
||||
with patch(
|
||||
"homeassistant.components.synology_dsm.config_flow.SynologyDSM",
|
||||
return_value=service,
|
||||
):
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"], {CONF_USERNAME: USERNAME, CONF_PASSWORD: PASSWORD}
|
||||
)
|
||||
|
@ -1,5 +1,5 @@
|
||||
"""Tests for the Synology DSM component."""
|
||||
from unittest.mock import patch
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
import pytest
|
||||
from synology_dsm.exceptions import SynologyDSMLoginInvalidException
|
||||
@ -22,11 +22,12 @@ from tests.common import MockConfigEntry
|
||||
|
||||
|
||||
@pytest.mark.no_bypass_setup
|
||||
async def test_services_registered(hass: HomeAssistant):
|
||||
async def test_services_registered(hass: HomeAssistant, mock_dsm: MagicMock):
|
||||
"""Test if all services are registered."""
|
||||
with patch("homeassistant.components.synology_dsm.common.SynologyDSM"), patch(
|
||||
"homeassistant.components.synology_dsm.PLATFORMS", return_value=[]
|
||||
):
|
||||
with patch(
|
||||
"homeassistant.components.synology_dsm.common.SynologyDSM",
|
||||
return_value=mock_dsm,
|
||||
), patch("homeassistant.components.synology_dsm.PLATFORMS", return_value=[]):
|
||||
entry = MockConfigEntry(
|
||||
domain=DOMAIN,
|
||||
data={
|
||||
|
Loading…
x
Reference in New Issue
Block a user