Implement additional DataUpdateCoordinator to harmonize the data update handling of Synology DSM (#46113)

This commit is contained in:
Michael 2021-02-23 23:23:50 +01:00 committed by GitHub
parent b583ded8b5
commit d96249e39c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 210 additions and 176 deletions

View File

@ -39,12 +39,6 @@ from homeassistant.core import ServiceCall, callback
from homeassistant.exceptions import ConfigEntryNotReady
from homeassistant.helpers import entity_registry
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.dispatcher import (
async_dispatcher_connect,
async_dispatcher_send,
)
from homeassistant.helpers.entity import Entity
from homeassistant.helpers.event import async_track_time_interval
from homeassistant.helpers.typing import HomeAssistantType
from homeassistant.helpers.update_coordinator import (
CoordinatorEntity,
@ -53,9 +47,12 @@ from homeassistant.helpers.update_coordinator import (
)
from .const import (
CONF_DEVICE_TOKEN,
CONF_SERIAL,
CONF_VOLUMES,
COORDINATOR_SURVEILLANCE,
COORDINATOR_CAMERAS,
COORDINATOR_CENTRAL,
COORDINATOR_SWITCHES,
DEFAULT_SCAN_INTERVAL,
DEFAULT_USE_SSL,
DEFAULT_VERIFY_SSL,
@ -73,6 +70,7 @@ from .const import (
STORAGE_DISK_SENSORS,
STORAGE_VOL_SENSORS,
SYNO_API,
SYSTEM_LOADED,
TEMP_SENSORS_KEYS,
UNDO_UPDATE_LISTENER,
UTILISATION_SENSORS,
@ -196,12 +194,11 @@ async def async_setup_entry(hass: HomeAssistantType, entry: ConfigEntry):
_LOGGER.debug("async_setup_entry() - Unable to connect to DSM: %s", err)
raise ConfigEntryNotReady from err
undo_listener = entry.add_update_listener(_async_update_listener)
hass.data.setdefault(DOMAIN, {})
hass.data[DOMAIN][entry.unique_id] = {
UNDO_UPDATE_LISTENER: entry.add_update_listener(_async_update_listener),
SYNO_API: api,
UNDO_UPDATE_LISTENER: undo_listener,
SYSTEM_LOADED: True,
}
# Services
@ -214,32 +211,82 @@ async def async_setup_entry(hass: HomeAssistantType, entry: ConfigEntry):
entry, data={**entry.data, CONF_MAC: network.macs}
)
# setup DataUpdateCoordinator
async def async_coordinator_update_data_surveillance_station():
"""Fetch all surveillance station data from api."""
async def async_coordinator_update_data_cameras():
"""Fetch all camera data from api."""
if not hass.data[DOMAIN][entry.unique_id][SYSTEM_LOADED]:
raise UpdateFailed("System not fully loaded")
if SynoSurveillanceStation.CAMERA_API_KEY not in api.dsm.apis:
return None
surveillance_station = api.surveillance_station
try:
async with async_timeout.timeout(10):
await hass.async_add_executor_job(surveillance_station.update)
except SynologyDSMAPIErrorException as err:
_LOGGER.debug(
"async_coordinator_update_data_cameras() - exception: %s", err
)
raise UpdateFailed(f"Error communicating with API: {err}") from err
if SynoSurveillanceStation.CAMERA_API_KEY not in api.dsm.apis:
return
return {
"cameras": {
camera.id: camera for camera in surveillance_station.get_all_cameras()
}
}
hass.data[DOMAIN][entry.unique_id][
COORDINATOR_SURVEILLANCE
] = DataUpdateCoordinator(
async def async_coordinator_update_data_central():
"""Fetch all device and sensor data from api."""
try:
await api.async_update()
except Exception as err:
_LOGGER.debug(
"async_coordinator_update_data_central() - exception: %s", err
)
raise UpdateFailed(f"Error communicating with API: {err}") from err
return None
async def async_coordinator_update_data_switches():
"""Fetch all switch data from api."""
if not hass.data[DOMAIN][entry.unique_id][SYSTEM_LOADED]:
raise UpdateFailed("System not fully loaded")
if SynoSurveillanceStation.HOME_MODE_API_KEY not in api.dsm.apis:
return None
surveillance_station = api.surveillance_station
return {
"switches": {
"home_mode": await hass.async_add_executor_job(
surveillance_station.get_home_mode_status
)
}
}
hass.data[DOMAIN][entry.unique_id][COORDINATOR_CAMERAS] = DataUpdateCoordinator(
hass,
_LOGGER,
name=f"{entry.unique_id}_surveillance_station",
update_method=async_coordinator_update_data_surveillance_station,
name=f"{entry.unique_id}_cameras",
update_method=async_coordinator_update_data_cameras,
update_interval=timedelta(seconds=30),
)
hass.data[DOMAIN][entry.unique_id][COORDINATOR_CENTRAL] = DataUpdateCoordinator(
hass,
_LOGGER,
name=f"{entry.unique_id}_central",
update_method=async_coordinator_update_data_central,
update_interval=timedelta(
minutes=entry.options.get(CONF_SCAN_INTERVAL, DEFAULT_SCAN_INTERVAL)
),
)
hass.data[DOMAIN][entry.unique_id][COORDINATOR_SWITCHES] = DataUpdateCoordinator(
hass,
_LOGGER,
name=f"{entry.unique_id}_switches",
update_method=async_coordinator_update_data_switches,
update_interval=timedelta(seconds=30),
)
@ -304,10 +351,11 @@ async def _async_setup_services(hass: HomeAssistantType):
_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.system.shutdown()
await dsm_api.async_shutdown()
for service in SERVICES:
hass.services.async_register(DOMAIN, service, service_handler)
@ -342,16 +390,8 @@ class SynoApi:
self._with_upgrade = True
self._with_utilisation = True
self._unsub_dispatcher = None
@property
def signal_sensor_update(self) -> str:
"""Event specific per Synology DSM entry to signal updates in sensors."""
return f"{DOMAIN}-{self.information.serial}-sensor-update"
async def async_setup(self):
"""Start interacting with the NAS."""
# init SynologyDSM object and login
self.dsm = SynologyDSM(
self._entry.data[CONF_HOST],
self._entry.data[CONF_PORT],
@ -360,7 +400,7 @@ class SynoApi:
self._entry.data[CONF_SSL],
self._entry.data[CONF_VERIFY_SSL],
timeout=self._entry.options.get(CONF_TIMEOUT),
device_token=self._entry.data.get("device_token"),
device_token=self._entry.data.get(CONF_DEVICE_TOKEN),
)
await self._hass.async_add_executor_job(self.dsm.login)
@ -373,24 +413,14 @@ class SynoApi:
self._with_surveillance_station,
)
self._async_setup_api_requests()
self._setup_api_requests()
await self._hass.async_add_executor_job(self._fetch_device_configuration)
await self.async_update()
self._unsub_dispatcher = async_track_time_interval(
self._hass,
self.async_update,
timedelta(
minutes=self._entry.options.get(
CONF_SCAN_INTERVAL, DEFAULT_SCAN_INTERVAL
)
),
)
@callback
def subscribe(self, api_key, unique_id):
"""Subscribe an entity from API fetches."""
"""Subscribe an entity to API fetches."""
_LOGGER.debug(
"SynoAPI.subscribe() - api_key:%s, unique_id:%s", api_key, unique_id
)
@ -401,31 +431,35 @@ class SynoApi:
@callback
def unsubscribe() -> None:
"""Unsubscribe an entity from API fetches (when disable)."""
_LOGGER.debug(
"SynoAPI.unsubscribe() - api_key:%s, unique_id:%s", api_key, 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):
def _setup_api_requests(self):
"""Determine if we should fetch each API, if one entity needs it."""
_LOGGER.debug(
"SynoAPI._async_setup_api_requests() - self._fetching_entities:%s",
self._fetching_entities,
)
# Entities not added yet, fetch all
if not self._fetching_entities:
_LOGGER.debug(
"SynoAPI._async_setup_api_requests() - Entities not added yet, fetch all"
"SynoAPI._setup_api_requests() - Entities not added yet, fetch all"
)
return
# Determine if we should fetch an API
self._with_system = bool(self.dsm.apis.get(SynoCoreSystem.API_KEY))
self._with_surveillance_station = bool(
self.dsm.apis.get(SynoSurveillanceStation.CAMERA_API_KEY)
) or bool(self.dsm.apis.get(SynoSurveillanceStation.HOME_MODE_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_system = bool(self._fetching_entities.get(SynoCoreSystem.API_KEY))
self._with_upgrade = bool(self._fetching_entities.get(SynoCoreUpgrade.API_KEY))
self._with_utilisation = bool(
self._fetching_entities.get(SynoCoreUtilization.API_KEY)
@ -433,39 +467,36 @@ class SynoApi:
self._with_information = bool(
self._fetching_entities.get(SynoDSMInformation.API_KEY)
)
self._with_surveillance_station = bool(
self.dsm.apis.get(SynoSurveillanceStation.CAMERA_API_KEY)
)
# Reset not used API, information is not reset since it's used in device_info
if not self._with_security:
_LOGGER.debug("SynoAPI._async_setup_api_requests() - disable security")
_LOGGER.debug("SynoAPI._setup_api_requests() - disable security")
self.dsm.reset(self.security)
self.security = None
if not self._with_storage:
_LOGGER.debug("SynoAPI._async_setup_api_requests() - disable storage")
_LOGGER.debug("SynoAPI._setup_api_requests() - disable storage")
self.dsm.reset(self.storage)
self.storage = None
if not self._with_system:
_LOGGER.debug("SynoAPI._async_setup_api_requests() - disable system")
_LOGGER.debug("SynoAPI._setup_api_requests() - disable system")
self.dsm.reset(self.system)
self.system = None
if not self._with_upgrade:
_LOGGER.debug("SynoAPI._async_setup_api_requests() - disable upgrade")
_LOGGER.debug("SynoAPI._setup_api_requests() - disable upgrade")
self.dsm.reset(self.upgrade)
self.upgrade = None
if not self._with_utilisation:
_LOGGER.debug("SynoAPI._async_setup_api_requests() - disable utilisation")
_LOGGER.debug("SynoAPI._setup_api_requests() - disable utilisation")
self.dsm.reset(self.utilisation)
self.utilisation = None
if not self._with_surveillance_station:
_LOGGER.debug(
"SynoAPI._async_setup_api_requests() - disable surveillance_station"
"SynoAPI._setup_api_requests() - disable surveillance_station"
)
self.dsm.reset(self.surveillance_station)
self.surveillance_station = None
@ -504,26 +535,31 @@ class SynoApi:
async def async_reboot(self):
"""Reboot NAS."""
if not self.system:
_LOGGER.debug("SynoAPI.async_reboot() - System API not ready: %s", self)
return
await self._hass.async_add_executor_job(self.system.reboot)
try:
await self._hass.async_add_executor_job(self.system.reboot)
except (SynologyDSMLoginFailedException, SynologyDSMRequestException) as err:
_LOGGER.error("Reboot not possible, please try again later")
_LOGGER.debug("Exception:%s", err)
async def async_shutdown(self):
"""Shutdown NAS."""
if not self.system:
_LOGGER.debug("SynoAPI.async_shutdown() - System API not ready: %s", self)
return
await self._hass.async_add_executor_job(self.system.shutdown)
try:
await self._hass.async_add_executor_job(self.system.shutdown)
except (SynologyDSMLoginFailedException, SynologyDSMRequestException) as err:
_LOGGER.error("Shutdown not possible, please try again later")
_LOGGER.debug("Exception:%s", err)
async def async_unload(self):
"""Stop interacting with the NAS and prepare for removal from hass."""
self._unsub_dispatcher()
try:
await self._hass.async_add_executor_job(self.dsm.logout)
except (SynologyDSMAPIErrorException, SynologyDSMRequestException) as err:
_LOGGER.debug("Logout not possible:%s", err)
async def async_update(self, now=None):
"""Update function for updating API information."""
_LOGGER.debug("SynoAPI.async_update()")
self._async_setup_api_requests()
self._setup_api_requests()
try:
await self._hass.async_add_executor_job(
self.dsm.update, self._with_information
@ -535,10 +571,9 @@ class SynoApi:
_LOGGER.debug("SynoAPI.async_update() - exception: %s", err)
await self._hass.config_entries.async_reload(self._entry.entry_id)
return
async_dispatcher_send(self._hass, self.signal_sensor_update)
class SynologyDSMBaseEntity(Entity):
class SynologyDSMBaseEntity(CoordinatorEntity):
"""Representation of a Synology NAS entry."""
def __init__(
@ -546,8 +581,11 @@ class SynologyDSMBaseEntity(Entity):
api: SynoApi,
entity_type: str,
entity_info: Dict[str, str],
coordinator: DataUpdateCoordinator,
):
"""Initialize the Synology DSM entity."""
super().__init__(coordinator)
self._api = api
self._api_key = entity_type.split(":")[0]
self.entity_type = entity_type.split(":")[-1]
@ -606,59 +644,13 @@ class SynologyDSMBaseEntity(Entity):
"""Return if the entity should be enabled when first added to the entity registry."""
return self._enable_default
class SynologyDSMDispatcherEntity(SynologyDSMBaseEntity, Entity):
"""Representation of a Synology NAS entry."""
def __init__(
self,
api: SynoApi,
entity_type: str,
entity_info: Dict[str, str],
):
"""Initialize the Synology DSM entity."""
super().__init__(api, entity_type, entity_info)
Entity.__init__(self)
@property
def should_poll(self) -> bool:
"""No polling needed."""
return False
async def async_update(self):
"""Only used by the generic entity update service."""
if not self.enabled:
return
await self._api.async_update()
async def async_added_to_hass(self):
"""Register state update callback."""
self.async_on_remove(
async_dispatcher_connect(
self.hass, self._api.signal_sensor_update, self.async_write_ha_state
)
)
"""Register entity for updates from API."""
self.async_on_remove(self._api.subscribe(self._api_key, self.unique_id))
await super().async_added_to_hass()
class SynologyDSMCoordinatorEntity(SynologyDSMBaseEntity, CoordinatorEntity):
"""Representation of a Synology NAS entry."""
def __init__(
self,
api: SynoApi,
entity_type: str,
entity_info: Dict[str, str],
coordinator: DataUpdateCoordinator,
):
"""Initialize the Synology DSM entity."""
super().__init__(api, entity_type, entity_info)
CoordinatorEntity.__init__(self, coordinator)
class SynologyDSMDeviceEntity(SynologyDSMDispatcherEntity):
class SynologyDSMDeviceEntity(SynologyDSMBaseEntity):
"""Representation of a Synology NAS disk or volume entry."""
def __init__(
@ -666,10 +658,11 @@ class SynologyDSMDeviceEntity(SynologyDSMDispatcherEntity):
api: SynoApi,
entity_type: str,
entity_info: Dict[str, str],
coordinator: DataUpdateCoordinator,
device_id: str = None,
):
"""Initialize the Synology DSM disk or volume entity."""
super().__init__(api, entity_type, entity_info)
super().__init__(api, entity_type, entity_info, coordinator)
self._device_id = device_id
self._device_name = None
self._device_manufacturer = None

View File

@ -6,8 +6,9 @@ from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_DISKS
from homeassistant.helpers.typing import HomeAssistantType
from . import SynologyDSMDeviceEntity, SynologyDSMDispatcherEntity
from . import SynologyDSMBaseEntity, SynologyDSMDeviceEntity
from .const import (
COORDINATOR_CENTRAL,
DOMAIN,
SECURITY_BINARY_SENSORS,
STORAGE_DISK_BINARY_SENSORS,
@ -21,18 +22,20 @@ async def async_setup_entry(
) -> None:
"""Set up the Synology NAS binary sensor."""
api = hass.data[DOMAIN][entry.unique_id][SYNO_API]
data = hass.data[DOMAIN][entry.unique_id]
api = data[SYNO_API]
coordinator = data[COORDINATOR_CENTRAL]
entities = [
SynoDSMSecurityBinarySensor(
api, sensor_type, SECURITY_BINARY_SENSORS[sensor_type]
api, sensor_type, SECURITY_BINARY_SENSORS[sensor_type], coordinator
)
for sensor_type in SECURITY_BINARY_SENSORS
]
entities += [
SynoDSMUpgradeBinarySensor(
api, sensor_type, UPGRADE_BINARY_SENSORS[sensor_type]
api, sensor_type, UPGRADE_BINARY_SENSORS[sensor_type], coordinator
)
for sensor_type in UPGRADE_BINARY_SENSORS
]
@ -42,7 +45,11 @@ async def async_setup_entry(
for disk in entry.data.get(CONF_DISKS, api.storage.disks_ids):
entities += [
SynoDSMStorageBinarySensor(
api, sensor_type, STORAGE_DISK_BINARY_SENSORS[sensor_type], disk
api,
sensor_type,
STORAGE_DISK_BINARY_SENSORS[sensor_type],
coordinator,
disk,
)
for sensor_type in STORAGE_DISK_BINARY_SENSORS
]
@ -50,7 +57,7 @@ async def async_setup_entry(
async_add_entities(entities)
class SynoDSMSecurityBinarySensor(SynologyDSMDispatcherEntity, BinarySensorEntity):
class SynoDSMSecurityBinarySensor(SynologyDSMBaseEntity, BinarySensorEntity):
"""Representation a Synology Security binary sensor."""
@property
@ -78,7 +85,7 @@ class SynoDSMStorageBinarySensor(SynologyDSMDeviceEntity, BinarySensorEntity):
return getattr(self._api.storage, self.entity_type)(self._device_id)
class SynoDSMUpgradeBinarySensor(SynologyDSMDispatcherEntity, BinarySensorEntity):
class SynoDSMUpgradeBinarySensor(SynologyDSMBaseEntity, BinarySensorEntity):
"""Representation a Synology Upgrade binary sensor."""
@property

View File

@ -3,16 +3,19 @@ import logging
from typing import Dict
from synology_dsm.api.surveillance_station import SynoSurveillanceStation
from synology_dsm.exceptions import SynologyDSMAPIErrorException
from synology_dsm.exceptions import (
SynologyDSMAPIErrorException,
SynologyDSMRequestException,
)
from homeassistant.components.camera import SUPPORT_STREAM, Camera
from homeassistant.config_entries import ConfigEntry
from homeassistant.helpers.typing import HomeAssistantType
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator
from . import SynoApi, SynologyDSMCoordinatorEntity
from . import SynoApi, SynologyDSMBaseEntity
from .const import (
COORDINATOR_SURVEILLANCE,
COORDINATOR_CAMERAS,
DOMAIN,
ENTITY_CLASS,
ENTITY_ENABLE,
@ -37,7 +40,7 @@ async def async_setup_entry(
return
# initial data fetch
coordinator = data[COORDINATOR_SURVEILLANCE]
coordinator = data[COORDINATOR_CAMERAS]
await coordinator.async_refresh()
async_add_entities(
@ -46,7 +49,7 @@ async def async_setup_entry(
)
class SynoDSMCamera(SynologyDSMCoordinatorEntity, Camera):
class SynoDSMCamera(SynologyDSMBaseEntity, Camera):
"""Representation a Synology camera."""
def __init__(
@ -125,7 +128,11 @@ class SynoDSMCamera(SynologyDSMCoordinatorEntity, Camera):
return None
try:
return self._api.surveillance_station.get_camera_image(self._camera_id)
except (SynologyDSMAPIErrorException) as err:
except (
SynologyDSMAPIErrorException,
SynologyDSMRequestException,
ConnectionRefusedError,
) as err:
_LOGGER.debug(
"SynoDSMCamera.camera_image(%s) - Exception:%s",
self.camera_data.name,

View File

@ -31,6 +31,7 @@ from homeassistant.core import callback
import homeassistant.helpers.config_validation as cv
from .const import (
CONF_DEVICE_TOKEN,
CONF_VOLUMES,
DEFAULT_PORT,
DEFAULT_PORT_SSL,
@ -180,7 +181,7 @@ class SynologyDSMFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
CONF_MAC: api.network.macs,
}
if otp_code:
config_data["device_token"] = api.device_token
config_data[CONF_DEVICE_TOKEN] = api.device_token
if user_input.get(CONF_DISKS):
config_data[CONF_DISKS] = user_input[CONF_DISKS]
if user_input.get(CONF_VOLUMES):

View File

@ -19,7 +19,10 @@ from homeassistant.const import (
DOMAIN = "synology_dsm"
PLATFORMS = ["binary_sensor", "camera", "sensor", "switch"]
COORDINATOR_SURVEILLANCE = "coordinator_surveillance_station"
COORDINATOR_CAMERAS = "coordinator_cameras"
COORDINATOR_CENTRAL = "coordinator_central"
COORDINATOR_SWITCHES = "coordinator_switches"
SYSTEM_LOADED = "system_loaded"
# Entry keys
SYNO_API = "syno_api"
@ -28,6 +31,7 @@ UNDO_UPDATE_LISTENER = "undo_update_listener"
# Configuration
CONF_SERIAL = "serial"
CONF_VOLUMES = "volumes"
CONF_DEVICE_TOKEN = "device_token"
DEFAULT_USE_SSL = True
DEFAULT_VERIFY_SSL = False

View File

@ -13,11 +13,13 @@ from homeassistant.const import (
)
from homeassistant.helpers.temperature import display_temp
from homeassistant.helpers.typing import HomeAssistantType
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator
from homeassistant.util.dt import utcnow
from . import SynoApi, SynologyDSMDeviceEntity, SynologyDSMDispatcherEntity
from . import SynoApi, SynologyDSMBaseEntity, SynologyDSMDeviceEntity
from .const import (
CONF_VOLUMES,
COORDINATOR_CENTRAL,
DOMAIN,
ENTITY_UNIT_LOAD,
INFORMATION_SENSORS,
@ -34,10 +36,14 @@ async def async_setup_entry(
) -> None:
"""Set up the Synology NAS Sensor."""
api = hass.data[DOMAIN][entry.unique_id][SYNO_API]
data = hass.data[DOMAIN][entry.unique_id]
api = data[SYNO_API]
coordinator = data[COORDINATOR_CENTRAL]
entities = [
SynoDSMUtilSensor(api, sensor_type, UTILISATION_SENSORS[sensor_type])
SynoDSMUtilSensor(
api, sensor_type, UTILISATION_SENSORS[sensor_type], coordinator
)
for sensor_type in UTILISATION_SENSORS
]
@ -46,7 +52,11 @@ async def async_setup_entry(
for volume in entry.data.get(CONF_VOLUMES, api.storage.volumes_ids):
entities += [
SynoDSMStorageSensor(
api, sensor_type, STORAGE_VOL_SENSORS[sensor_type], volume
api,
sensor_type,
STORAGE_VOL_SENSORS[sensor_type],
coordinator,
volume,
)
for sensor_type in STORAGE_VOL_SENSORS
]
@ -56,20 +66,26 @@ async def async_setup_entry(
for disk in entry.data.get(CONF_DISKS, api.storage.disks_ids):
entities += [
SynoDSMStorageSensor(
api, sensor_type, STORAGE_DISK_SENSORS[sensor_type], disk
api,
sensor_type,
STORAGE_DISK_SENSORS[sensor_type],
coordinator,
disk,
)
for sensor_type in STORAGE_DISK_SENSORS
]
entities += [
SynoDSMInfoSensor(api, sensor_type, INFORMATION_SENSORS[sensor_type])
SynoDSMInfoSensor(
api, sensor_type, INFORMATION_SENSORS[sensor_type], coordinator
)
for sensor_type in INFORMATION_SENSORS
]
async_add_entities(entities)
class SynoDSMUtilSensor(SynologyDSMDispatcherEntity):
class SynoDSMUtilSensor(SynologyDSMBaseEntity):
"""Representation a Synology Utilisation sensor."""
@property
@ -122,12 +138,18 @@ class SynoDSMStorageSensor(SynologyDSMDeviceEntity):
return attr
class SynoDSMInfoSensor(SynologyDSMDispatcherEntity):
class SynoDSMInfoSensor(SynologyDSMBaseEntity):
"""Representation a Synology information sensor."""
def __init__(self, api: SynoApi, entity_type: str, entity_info: Dict[str, str]):
def __init__(
self,
api: SynoApi,
entity_type: str,
entity_info: Dict[str, str],
coordinator: DataUpdateCoordinator,
):
"""Initialize the Synology SynoDSMInfoSensor entity."""
super().__init__(api, entity_type, entity_info)
super().__init__(api, entity_type, entity_info, coordinator)
self._previous_uptime = None
self._last_boot = None

View File

@ -7,9 +7,10 @@ from synology_dsm.api.surveillance_station import SynoSurveillanceStation
from homeassistant.components.switch import ToggleEntity
from homeassistant.config_entries import ConfigEntry
from homeassistant.helpers.typing import HomeAssistantType
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator
from . import SynoApi, SynologyDSMDispatcherEntity
from .const import DOMAIN, SURVEILLANCE_SWITCH, SYNO_API
from . import SynoApi, SynologyDSMBaseEntity
from .const import COORDINATOR_SWITCHES, DOMAIN, SURVEILLANCE_SWITCH, SYNO_API
_LOGGER = logging.getLogger(__name__)
@ -19,16 +20,21 @@ async def async_setup_entry(
) -> None:
"""Set up the Synology NAS switch."""
api = hass.data[DOMAIN][entry.unique_id][SYNO_API]
data = hass.data[DOMAIN][entry.unique_id]
api = data[SYNO_API]
entities = []
if SynoSurveillanceStation.INFO_API_KEY in api.dsm.apis:
info = await hass.async_add_executor_job(api.dsm.surveillance_station.get_info)
version = info["data"]["CMSMinVersion"]
# initial data fetch
coordinator = data[COORDINATOR_SWITCHES]
await coordinator.async_refresh()
entities += [
SynoDSMSurveillanceHomeModeToggle(
api, sensor_type, SURVEILLANCE_SWITCH[sensor_type], version
api, sensor_type, SURVEILLANCE_SWITCH[sensor_type], version, coordinator
)
for sensor_type in SURVEILLANCE_SWITCH
]
@ -36,58 +42,52 @@ async def async_setup_entry(
async_add_entities(entities, True)
class SynoDSMSurveillanceHomeModeToggle(SynologyDSMDispatcherEntity, ToggleEntity):
class SynoDSMSurveillanceHomeModeToggle(SynologyDSMBaseEntity, ToggleEntity):
"""Representation a Synology Surveillance Station Home Mode toggle."""
def __init__(
self, api: SynoApi, entity_type: str, entity_info: Dict[str, str], version: str
self,
api: SynoApi,
entity_type: str,
entity_info: Dict[str, str],
version: str,
coordinator: DataUpdateCoordinator,
):
"""Initialize a Synology Surveillance Station Home Mode."""
super().__init__(
api,
entity_type,
entity_info,
coordinator,
)
self._version = version
self._state = None
@property
def is_on(self) -> bool:
"""Return the state."""
if self.entity_type == "home_mode":
return self._state
return None
return self.coordinator.data["switches"][self.entity_type]
@property
def should_poll(self) -> bool:
"""No polling needed."""
return True
async def async_update(self):
"""Update the toggle state."""
_LOGGER.debug(
"SynoDSMSurveillanceHomeModeToggle.async_update(%s)",
self._api.information.serial,
)
self._state = await self.hass.async_add_executor_job(
self._api.surveillance_station.get_home_mode_status
)
def turn_on(self, **kwargs) -> None:
async def async_turn_on(self, **kwargs) -> None:
"""Turn on Home mode."""
_LOGGER.debug(
"SynoDSMSurveillanceHomeModeToggle.turn_on(%s)",
self._api.information.serial,
)
self._api.surveillance_station.set_home_mode(True)
await self.hass.async_add_executor_job(
self._api.dsm.surveillance_station.set_home_mode, True
)
await self.coordinator.async_request_refresh()
def turn_off(self, **kwargs) -> None:
async def async_turn_off(self, **kwargs) -> None:
"""Turn off Home mode."""
_LOGGER.debug(
"SynoDSMSurveillanceHomeModeToggle.turn_off(%s)",
self._api.information.serial,
)
self._api.surveillance_station.set_home_mode(False)
await self.hass.async_add_executor_job(
self._api.dsm.surveillance_station.set_home_mode, False
)
await self.coordinator.async_request_refresh()
@property
def available(self) -> bool: