Replace Synology DSM services with buttons (#57352)

This commit is contained in:
Michael 2022-01-25 09:51:55 +01:00 committed by GitHub
parent 19b7454161
commit 5d7d652237
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 445 additions and 303 deletions

View file

@ -1102,9 +1102,12 @@ omit =
homeassistant/components/synology_chat/notify.py
homeassistant/components/synology_dsm/__init__.py
homeassistant/components/synology_dsm/binary_sensor.py
homeassistant/components/synology_dsm/button.py
homeassistant/components/synology_dsm/camera.py
homeassistant/components/synology_dsm/diagnostics.py
homeassistant/components/synology_dsm/common.py
homeassistant/components/synology_dsm/sensor.py
homeassistant/components/synology_dsm/service.py
homeassistant/components/synology_dsm/switch.py
homeassistant/components/synology_srm/device_tracker.py
homeassistant/components/syslog/notify.py

View file

@ -1,20 +1,11 @@
"""The Synology DSM component."""
from __future__ import annotations
from collections.abc import Callable
from datetime import timedelta
import logging
from typing import Any
import async_timeout
from synology_dsm import SynologyDSM
from synology_dsm.api.core.security import SynoCoreSecurity
from synology_dsm.api.core.system import SynoCoreSystem
from synology_dsm.api.core.upgrade import SynoCoreUpgrade
from synology_dsm.api.core.utilization import SynoCoreUtilization
from synology_dsm.api.dsm.information import SynoDSMInformation
from synology_dsm.api.dsm.network import SynoDSMNetwork
from synology_dsm.api.storage.storage import SynoStorage
from synology_dsm.api.surveillance_station import SynoSurveillanceStation
from synology_dsm.api.surveillance_station.camera import SynoCamera
from synology_dsm.exceptions import (
@ -28,18 +19,8 @@ from synology_dsm.exceptions import (
)
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import (
CONF_HOST,
CONF_MAC,
CONF_PASSWORD,
CONF_PORT,
CONF_SCAN_INTERVAL,
CONF_SSL,
CONF_TIMEOUT,
CONF_USERNAME,
CONF_VERIFY_SSL,
)
from homeassistant.core import HomeAssistant, ServiceCall, callback
from homeassistant.const import CONF_MAC, CONF_SCAN_INTERVAL, CONF_VERIFY_SSL
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import ConfigEntryAuthFailed, ConfigEntryNotReady
from homeassistant.helpers import device_registry
import homeassistant.helpers.config_validation as cv
@ -54,9 +35,8 @@ from homeassistant.helpers.update_coordinator import (
UpdateFailed,
)
from .common import SynoApi
from .const import (
CONF_DEVICE_TOKEN,
CONF_SERIAL,
COORDINATOR_CAMERAS,
COORDINATOR_CENTRAL,
COORDINATOR_SWITCHES,
@ -66,14 +46,12 @@ from .const import (
EXCEPTION_DETAILS,
EXCEPTION_UNKNOWN,
PLATFORMS,
SERVICE_REBOOT,
SERVICE_SHUTDOWN,
SERVICES,
SYNO_API,
SYSTEM_LOADED,
UNDO_UPDATE_LISTENER,
SynologyDSMEntityDescription,
)
from .service import async_setup_services
CONFIG_SCHEMA = cv.removed(DOMAIN, raise_if_present=False)
@ -141,7 +119,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
}
# Services
await _async_setup_services(hass)
await async_setup_services(hass)
# For SSDP compat
if not entry.data.get(CONF_MAC):
@ -249,279 +227,6 @@ async def _async_update_listener(hass: HomeAssistant, entry: ConfigEntry) -> Non
await hass.config_entries.async_reload(entry.entry_id)
async def _async_setup_services(hass: HomeAssistant) -> None:
"""Service handler setup."""
async def service_handler(call: ServiceCall) -> None:
"""Handle service call."""
serial = call.data.get(CONF_SERIAL)
dsm_devices = hass.data[DOMAIN]
if serial:
dsm_device = dsm_devices.get(serial)
elif len(dsm_devices) == 1:
dsm_device = next(iter(dsm_devices.values()))
serial = next(iter(dsm_devices))
else:
_LOGGER.error(
"More than one DSM configured, must specify one of serials %s",
sorted(dsm_devices),
)
return
if not dsm_device:
_LOGGER.error("DSM with specified serial %s not found", serial)
return
_LOGGER.debug("%s DSM with serial %s", call.service, serial)
dsm_api = dsm_device[SYNO_API]
dsm_device[SYSTEM_LOADED] = False
if call.service == SERVICE_REBOOT:
await dsm_api.async_reboot()
elif call.service == SERVICE_SHUTDOWN:
await dsm_api.async_shutdown()
for service in SERVICES:
hass.services.async_register(DOMAIN, service, service_handler)
class SynoApi:
"""Class to interface with Synology DSM API."""
def __init__(self, hass: HomeAssistant, entry: ConfigEntry) -> None:
"""Initialize the API wrapper class."""
self._hass = hass
self._entry = entry
if entry.data.get(CONF_SSL):
self.config_url = f"https://{entry.data[CONF_HOST]}:{entry.data[CONF_PORT]}"
else:
self.config_url = f"http://{entry.data[CONF_HOST]}:{entry.data[CONF_PORT]}"
self.initialized = False
# DSM APIs
self.dsm: SynologyDSM = None
self.information: SynoDSMInformation = None
self.network: SynoDSMNetwork = None
self.security: SynoCoreSecurity = None
self.storage: SynoStorage = None
self.surveillance_station: SynoSurveillanceStation = None
self.system: SynoCoreSystem = None
self.upgrade: SynoCoreUpgrade = None
self.utilisation: SynoCoreUtilization = None
# Should we fetch them
self._fetching_entities: dict[str, set[str]] = {}
self._with_information = True
self._with_security = True
self._with_storage = True
self._with_surveillance_station = True
self._with_system = True
self._with_upgrade = True
self._with_utilisation = True
async def async_setup(self) -> None:
"""Start interacting with the NAS."""
self.dsm = SynologyDSM(
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),
)
await self._hass.async_add_executor_job(self.dsm.login)
# check if surveillance station is used
self._with_surveillance_station = bool(
self.dsm.apis.get(SynoSurveillanceStation.CAMERA_API_KEY)
)
_LOGGER.debug(
"State of Surveillance_station during setup of '%s': %s",
self._entry.unique_id,
self._with_surveillance_station,
)
self._async_setup_api_requests()
await self._hass.async_add_executor_job(self._fetch_device_configuration)
await self.async_update()
self.initialized = True
@callback
def subscribe(self, api_key: str, unique_id: str) -> Callable[[], None]:
"""Subscribe an entity to API fetches."""
_LOGGER.debug("Subscribe new entity: %s", unique_id)
if api_key not in self._fetching_entities:
self._fetching_entities[api_key] = set()
self._fetching_entities[api_key].add(unique_id)
@callback
def unsubscribe() -> None:
"""Unsubscribe an entity from API fetches (when disable)."""
_LOGGER.debug("Unsubscribe entity: %s", unique_id)
self._fetching_entities[api_key].remove(unique_id)
if len(self._fetching_entities[api_key]) == 0:
self._fetching_entities.pop(api_key)
return unsubscribe
@callback
def _async_setup_api_requests(self) -> None:
"""Determine if we should fetch each API, if one entity needs it."""
# Entities not added yet, fetch all
if not self._fetching_entities:
_LOGGER.debug(
"Entities not added yet, fetch all for '%s'", self._entry.unique_id
)
return
# surveillance_station is updated by own coordinator
self.dsm.reset(self.surveillance_station)
# Determine if we should fetch an API
self._with_system = bool(self.dsm.apis.get(SynoCoreSystem.API_KEY))
self._with_security = bool(
self._fetching_entities.get(SynoCoreSecurity.API_KEY)
)
self._with_storage = bool(self._fetching_entities.get(SynoStorage.API_KEY))
self._with_upgrade = bool(self._fetching_entities.get(SynoCoreUpgrade.API_KEY))
self._with_utilisation = bool(
self._fetching_entities.get(SynoCoreUtilization.API_KEY)
)
self._with_information = bool(
self._fetching_entities.get(SynoDSMInformation.API_KEY)
)
# Reset not used API, information is not reset since it's used in device_info
if not self._with_security:
_LOGGER.debug(
"Disable security api from being updated for '%s'",
self._entry.unique_id,
)
self.dsm.reset(self.security)
self.security = None
if not self._with_storage:
_LOGGER.debug(
"Disable storage api from being updatedf or '%s'", self._entry.unique_id
)
self.dsm.reset(self.storage)
self.storage = None
if not self._with_system:
_LOGGER.debug(
"Disable system api from being updated for '%s'", self._entry.unique_id
)
self.dsm.reset(self.system)
self.system = None
if not self._with_upgrade:
_LOGGER.debug(
"Disable upgrade api from being updated for '%s'", self._entry.unique_id
)
self.dsm.reset(self.upgrade)
self.upgrade = None
if not self._with_utilisation:
_LOGGER.debug(
"Disable utilisation api from being updated for '%s'",
self._entry.unique_id,
)
self.dsm.reset(self.utilisation)
self.utilisation = None
def _fetch_device_configuration(self) -> None:
"""Fetch initial device config."""
self.information = self.dsm.information
self.network = self.dsm.network
self.network.update()
if self._with_security:
_LOGGER.debug("Enable security api updates for '%s'", self._entry.unique_id)
self.security = self.dsm.security
if self._with_storage:
_LOGGER.debug("Enable storage api updates for '%s'", self._entry.unique_id)
self.storage = self.dsm.storage
if self._with_upgrade:
_LOGGER.debug("Enable upgrade api updates for '%s'", self._entry.unique_id)
self.upgrade = self.dsm.upgrade
if self._with_system:
_LOGGER.debug("Enable system api updates for '%s'", self._entry.unique_id)
self.system = self.dsm.system
if self._with_utilisation:
_LOGGER.debug(
"Enable utilisation api updates for '%s'", self._entry.unique_id
)
self.utilisation = self.dsm.utilisation
if self._with_surveillance_station:
_LOGGER.debug(
"Enable surveillance_station api updates for '%s'",
self._entry.unique_id,
)
self.surveillance_station = self.dsm.surveillance_station
async def async_reboot(self) -> None:
"""Reboot NAS."""
try:
await self._hass.async_add_executor_job(self.system.reboot)
except (SynologyDSMLoginFailedException, SynologyDSMRequestException) as err:
_LOGGER.error(
"Reboot of '%s' not possible, please try again later",
self._entry.unique_id,
)
_LOGGER.debug("Exception:%s", err)
async def async_shutdown(self) -> None:
"""Shutdown NAS."""
try:
await self._hass.async_add_executor_job(self.system.shutdown)
except (SynologyDSMLoginFailedException, SynologyDSMRequestException) as err:
_LOGGER.error(
"Shutdown of '%s' not possible, please try again later",
self._entry.unique_id,
)
_LOGGER.debug("Exception:%s", err)
async def async_unload(self) -> None:
"""Stop interacting with the NAS and prepare for removal from hass."""
try:
await self._hass.async_add_executor_job(self.dsm.logout)
except (SynologyDSMAPIErrorException, SynologyDSMRequestException) as err:
_LOGGER.debug(
"Logout from '%s' not possible:%s", self._entry.unique_id, err
)
async def async_update(self, now: timedelta | None = None) -> None:
"""Update function for updating API information."""
_LOGGER.debug("Start data update for '%s'", self._entry.unique_id)
self._async_setup_api_requests()
try:
await self._hass.async_add_executor_job(
self.dsm.update, self._with_information
)
except (SynologyDSMLoginFailedException, SynologyDSMRequestException) as err:
if not self.initialized:
raise err
_LOGGER.warning(
"Connection error during update, fallback by reloading the entry"
)
_LOGGER.debug(
"Connection error during update of '%s' with exception: %s",
self._entry.unique_id,
err,
)
await self._hass.config_entries.async_reload(self._entry.entry_id)
return
class SynologyDSMBaseEntity(CoordinatorEntity):
"""Representation of a Synology NAS entry."""

View file

@ -0,0 +1,96 @@
"""Support for Synology DSM buttons."""
from __future__ import annotations
from collections.abc import Callable
from dataclasses import dataclass
import logging
from typing import Any, Final
from homeassistant.components.button import (
ButtonDeviceClass,
ButtonEntity,
ButtonEntityDescription,
)
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity import DeviceInfo, EntityCategory
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from . import SynoApi
from .const import DOMAIN, SYNO_API
LOGGER = logging.getLogger(__name__)
@dataclass
class SynologyDSMbuttonDescriptionMixin:
"""Mixin to describe a Synology DSM button entity."""
press_action: Callable[[SynoApi], Any]
@dataclass
class SynologyDSMbuttonDescription(
ButtonEntityDescription, SynologyDSMbuttonDescriptionMixin
):
"""Class to describe a Synology DSM button entity."""
BUTTONS: Final = [
SynologyDSMbuttonDescription(
key="reboot",
name="Reboot",
device_class=ButtonDeviceClass.RESTART,
entity_category=EntityCategory.CONFIG,
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(),
),
]
async def async_setup_entry(
hass: HomeAssistant,
entry: ConfigEntry,
async_add_entities: AddEntitiesCallback,
) -> None:
"""Set buttons for device."""
data = hass.data[DOMAIN][entry.unique_id]
syno_api: SynoApi = data[SYNO_API]
async_add_entities(SynologyDSMButton(syno_api, button) for button in BUTTONS)
class SynologyDSMButton(ButtonEntity):
"""Defines a Synology DSM button."""
entity_description: SynologyDSMbuttonDescription
def __init__(
self,
api: SynoApi,
description: SynologyDSMbuttonDescription,
) -> None:
"""Initialize the Synology DSM binary_sensor entity."""
self.entity_description = description
self.syno_api = api
self._attr_name = f"{api.network.hostname} {description.name}"
self._attr_unique_id = f"{api.information.serial}_{description.key}"
self._attr_device_info = DeviceInfo(
identifiers={(DOMAIN, api.information.serial)}
)
async def async_press(self) -> None:
"""Triggers the Synology DSM button press service."""
LOGGER.debug(
"Trigger %s for %s",
self.entity_description.key,
self.syno_api.network.hostname,
)
await self.entity_description.press_action(self.syno_api)

View file

@ -0,0 +1,260 @@
"""The Synology DSM component."""
from __future__ import annotations
from collections.abc import Callable
from datetime import timedelta
import logging
from synology_dsm import SynologyDSM
from synology_dsm.api.core.security import SynoCoreSecurity
from synology_dsm.api.core.system import SynoCoreSystem
from synology_dsm.api.core.upgrade import SynoCoreUpgrade
from synology_dsm.api.core.utilization import SynoCoreUtilization
from synology_dsm.api.dsm.information import SynoDSMInformation
from synology_dsm.api.dsm.network import SynoDSMNetwork
from synology_dsm.api.storage.storage import SynoStorage
from synology_dsm.api.surveillance_station import SynoSurveillanceStation
from synology_dsm.exceptions import (
SynologyDSMAPIErrorException,
SynologyDSMLoginFailedException,
SynologyDSMRequestException,
)
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import (
CONF_HOST,
CONF_PASSWORD,
CONF_PORT,
CONF_SSL,
CONF_TIMEOUT,
CONF_USERNAME,
CONF_VERIFY_SSL,
)
from homeassistant.core import HomeAssistant, callback
from .const import CONF_DEVICE_TOKEN
LOGGER = logging.getLogger(__name__)
class SynoApi:
"""Class to interface with Synology DSM API."""
def __init__(self, hass: HomeAssistant, entry: ConfigEntry) -> None:
"""Initialize the API wrapper class."""
self._hass = hass
self._entry = entry
if entry.data.get(CONF_SSL):
self.config_url = f"https://{entry.data[CONF_HOST]}:{entry.data[CONF_PORT]}"
else:
self.config_url = f"http://{entry.data[CONF_HOST]}:{entry.data[CONF_PORT]}"
# DSM APIs
self.dsm: SynologyDSM = None
self.information: SynoDSMInformation = None
self.network: SynoDSMNetwork = None
self.security: SynoCoreSecurity = None
self.storage: SynoStorage = None
self.surveillance_station: SynoSurveillanceStation = None
self.system: SynoCoreSystem = None
self.upgrade: SynoCoreUpgrade = None
self.utilisation: SynoCoreUtilization = None
# Should we fetch them
self._fetching_entities: dict[str, set[str]] = {}
self._with_information = True
self._with_security = True
self._with_storage = True
self._with_surveillance_station = True
self._with_system = True
self._with_upgrade = True
self._with_utilisation = True
async def async_setup(self) -> None:
"""Start interacting with the NAS."""
self.dsm = SynologyDSM(
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),
)
await self._hass.async_add_executor_job(self.dsm.login)
# check if surveillance station is used
self._with_surveillance_station = bool(
self.dsm.apis.get(SynoSurveillanceStation.CAMERA_API_KEY)
)
LOGGER.debug(
"State of Surveillance_station during setup of '%s': %s",
self._entry.unique_id,
self._with_surveillance_station,
)
self._async_setup_api_requests()
await self._hass.async_add_executor_job(self._fetch_device_configuration)
await self.async_update()
@callback
def subscribe(self, api_key: str, unique_id: str) -> Callable[[], None]:
"""Subscribe an entity to API fetches."""
LOGGER.debug("Subscribe new entity: %s", unique_id)
if api_key not in self._fetching_entities:
self._fetching_entities[api_key] = set()
self._fetching_entities[api_key].add(unique_id)
@callback
def unsubscribe() -> None:
"""Unsubscribe an entity from API fetches (when disable)."""
LOGGER.debug("Unsubscribe entity: %s", unique_id)
self._fetching_entities[api_key].remove(unique_id)
if len(self._fetching_entities[api_key]) == 0:
self._fetching_entities.pop(api_key)
return unsubscribe
@callback
def _async_setup_api_requests(self) -> None:
"""Determine if we should fetch each API, if one entity needs it."""
# Entities not added yet, fetch all
if not self._fetching_entities:
LOGGER.debug(
"Entities not added yet, fetch all for '%s'", self._entry.unique_id
)
return
# surveillance_station is updated by own coordinator
self.dsm.reset(self.surveillance_station)
# Determine if we should fetch an API
self._with_system = bool(self.dsm.apis.get(SynoCoreSystem.API_KEY))
self._with_security = bool(
self._fetching_entities.get(SynoCoreSecurity.API_KEY)
)
self._with_storage = bool(self._fetching_entities.get(SynoStorage.API_KEY))
self._with_upgrade = bool(self._fetching_entities.get(SynoCoreUpgrade.API_KEY))
self._with_utilisation = bool(
self._fetching_entities.get(SynoCoreUtilization.API_KEY)
)
self._with_information = bool(
self._fetching_entities.get(SynoDSMInformation.API_KEY)
)
# Reset not used API, information is not reset since it's used in device_info
if not self._with_security:
LOGGER.debug(
"Disable security api from being updated for '%s'",
self._entry.unique_id,
)
self.dsm.reset(self.security)
self.security = None
if not self._with_storage:
LOGGER.debug(
"Disable storage api from being updatedf or '%s'", self._entry.unique_id
)
self.dsm.reset(self.storage)
self.storage = None
if not self._with_system:
LOGGER.debug(
"Disable system api from being updated for '%s'", self._entry.unique_id
)
self.dsm.reset(self.system)
self.system = None
if not self._with_upgrade:
LOGGER.debug(
"Disable upgrade api from being updated for '%s'", self._entry.unique_id
)
self.dsm.reset(self.upgrade)
self.upgrade = None
if not self._with_utilisation:
LOGGER.debug(
"Disable utilisation api from being updated for '%s'",
self._entry.unique_id,
)
self.dsm.reset(self.utilisation)
self.utilisation = None
def _fetch_device_configuration(self) -> None:
"""Fetch initial device config."""
self.information = self.dsm.information
self.network = self.dsm.network
self.network.update()
if self._with_security:
LOGGER.debug("Enable security api updates for '%s'", self._entry.unique_id)
self.security = self.dsm.security
if self._with_storage:
LOGGER.debug("Enable storage api updates for '%s'", self._entry.unique_id)
self.storage = self.dsm.storage
if self._with_upgrade:
LOGGER.debug("Enable upgrade api updates for '%s'", self._entry.unique_id)
self.upgrade = self.dsm.upgrade
if self._with_system:
LOGGER.debug("Enable system api updates for '%s'", self._entry.unique_id)
self.system = self.dsm.system
if self._with_utilisation:
LOGGER.debug(
"Enable utilisation api updates for '%s'", self._entry.unique_id
)
self.utilisation = self.dsm.utilisation
if self._with_surveillance_station:
LOGGER.debug(
"Enable surveillance_station api updates for '%s'",
self._entry.unique_id,
)
self.surveillance_station = self.dsm.surveillance_station
async def _syno_api_executer(self, api_call: Callable) -> None:
"""Synology api call wrapper."""
try:
await self._hass.async_add_executor_job(api_call)
except (SynologyDSMAPIErrorException, SynologyDSMRequestException) as err:
LOGGER.debug(
"Error from '%s': %s", self._entry.unique_id, err, exc_info=True
)
raise err
async def async_reboot(self) -> None:
"""Reboot NAS."""
await self._syno_api_executer(self.system.reboot)
async def async_shutdown(self) -> None:
"""Shutdown NAS."""
await self._syno_api_executer(self.system.shutdown)
async def async_unload(self) -> None:
"""Stop interacting with the NAS and prepare for removal from hass."""
await self._syno_api_executer(self.dsm.logout)
async def async_update(self, now: timedelta | None = None) -> None:
"""Update function for updating API information."""
LOGGER.debug("Start data update for '%s'", self._entry.unique_id)
self._async_setup_api_requests()
try:
await self._hass.async_add_executor_job(
self.dsm.update, self._with_information
)
except (SynologyDSMLoginFailedException, SynologyDSMRequestException) as err:
LOGGER.warning(
"Connection error during update, fallback by reloading the entry"
)
LOGGER.debug(
"Connection error during update of '%s' with exception: %s",
self._entry.unique_id,
err,
)
await self._hass.config_entries.async_reload(self._entry.entry_id)
return

View file

@ -32,7 +32,13 @@ from homeassistant.const import (
from homeassistant.helpers.entity import EntityCategory, EntityDescription
DOMAIN = "synology_dsm"
PLATFORMS = [Platform.BINARY_SENSOR, Platform.CAMERA, Platform.SENSOR, Platform.SWITCH]
PLATFORMS = [
Platform.BINARY_SENSOR,
Platform.BUTTON,
Platform.CAMERA,
Platform.SENSOR,
Platform.SWITCH,
]
COORDINATOR_CAMERAS = "coordinator_cameras"
COORDINATOR_CENTRAL = "coordinator_central"
COORDINATOR_SWITCHES = "coordinator_switches"

View file

@ -0,0 +1,72 @@
"""The Synology DSM component."""
from __future__ import annotations
import logging
from synology_dsm.exceptions import SynologyDSMException
from homeassistant.core import HomeAssistant, ServiceCall
from .common import SynoApi
from .const import (
CONF_SERIAL,
DOMAIN,
SERVICE_REBOOT,
SERVICE_SHUTDOWN,
SERVICES,
SYNO_API,
SYSTEM_LOADED,
)
LOGGER = logging.getLogger(__name__)
async def async_setup_services(hass: HomeAssistant) -> None:
"""Service handler setup."""
async def service_handler(call: ServiceCall) -> None:
"""Handle service call."""
serial = call.data.get(CONF_SERIAL)
dsm_devices = hass.data[DOMAIN]
if serial:
dsm_device = dsm_devices.get(serial)
elif len(dsm_devices) == 1:
dsm_device = next(iter(dsm_devices.values()))
serial = next(iter(dsm_devices))
else:
LOGGER.error(
"More than one DSM configured, must specify one of serials %s",
sorted(dsm_devices),
)
return
if not dsm_device:
LOGGER.error("DSM with specified serial %s not found", serial)
return
if call.service in [SERVICE_REBOOT, SERVICE_SHUTDOWN]:
dsm_device = hass.data[DOMAIN].get(serial)
if not dsm_device:
LOGGER.error("DSM with specified serial %s not found", serial)
return
LOGGER.debug("%s DSM with serial %s", call.service, serial)
LOGGER.warning(
"The %s service is deprecated and will be removed in future release. Please use the corresponding button entity",
call.service,
)
dsm_api: SynoApi = dsm_device[SYNO_API]
try:
dsm_device[SYSTEM_LOADED] = False
await getattr(dsm_api, f"async_{call.service}")()
except SynologyDSMException as ex:
LOGGER.error(
"%s of DSM with serial %s not possible, because of %s",
call.service,
serial,
ex,
)
return
for service in SERVICES:
hass.services.async_register(DOMAIN, service, service_handler)

View file

@ -2,7 +2,7 @@
reboot:
name: Reboot
description: Reboot the NAS.
description: Reboot the NAS. This service is deprecated and will be removed in future release. Please use the corresponding button entity.
fields:
serial:
name: Serial
@ -13,7 +13,7 @@ reboot:
shutdown:
name: Shutdown
description: Shutdown the NAS.
description: Shutdown the NAS. This service is deprecated and will be removed in future release. Please use the corresponding button entity.
fields:
serial:
name: Serial