From 4eba2ccebcb5caa235f2bd5bbac287e5f2acdd3d Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Fri, 3 Sep 2021 23:35:28 +0200 Subject: [PATCH] Use EntityDescription - synology_dsm (#55407) --- .../components/synology_dsm/__init__.py | 104 +-- .../components/synology_dsm/binary_sensor.py | 75 ++- .../components/synology_dsm/camera.py | 57 +- .../components/synology_dsm/const.py | 590 +++++++++--------- .../components/synology_dsm/sensor.py | 103 +-- .../components/synology_dsm/switch.py | 29 +- 6 files changed, 486 insertions(+), 472 deletions(-) diff --git a/homeassistant/components/synology_dsm/__init__.py b/homeassistant/components/synology_dsm/__init__.py index 0bc88b683b7..685d2c27d60 100644 --- a/homeassistant/components/synology_dsm/__init__.py +++ b/homeassistant/components/synology_dsm/__init__.py @@ -26,14 +26,9 @@ from synology_dsm.exceptions import ( SynologyDSMRequestException, ) -from homeassistant.components.sensor import ATTR_STATE_CLASS from homeassistant.config_entries import SOURCE_REAUTH, ConfigEntry from homeassistant.const import ( ATTR_ATTRIBUTION, - ATTR_DEVICE_CLASS, - ATTR_ICON, - ATTR_NAME, - ATTR_UNIT_OF_MEASUREMENT, CONF_HOST, CONF_MAC, CONF_PASSWORD, @@ -68,7 +63,6 @@ from .const import ( DEFAULT_SCAN_INTERVAL, DEFAULT_VERIFY_SSL, DOMAIN, - ENTITY_ENABLE, EXCEPTION_DETAILS, EXCEPTION_UNKNOWN, PLATFORMS, @@ -82,7 +76,7 @@ from .const import ( SYSTEM_LOADED, UNDO_UPDATE_LISTENER, UTILISATION_SENSORS, - EntityInfo, + SynologyDSMEntityDescription, ) CONFIG_SCHEMA = cv.deprecated(DOMAIN) @@ -109,12 +103,12 @@ async def async_setup_entry( # noqa: C901 if "SYNO." in entity_entry.unique_id: return None - entries = { - **STORAGE_DISK_BINARY_SENSORS, - **STORAGE_DISK_SENSORS, - **STORAGE_VOL_SENSORS, - **UTILISATION_SENSORS, - } + entries = ( + *STORAGE_DISK_BINARY_SENSORS, + *STORAGE_DISK_SENSORS, + *STORAGE_VOL_SENSORS, + *UTILISATION_SENSORS, + ) infos = entity_entry.unique_id.split("_") serial = infos.pop(0) label = infos.pop(0) @@ -129,22 +123,22 @@ async def async_setup_entry( # noqa: C901 return None entity_type: str | None = None - for entity_key, entity_attrs in entries.items(): + for description in entries: if ( device_id - and entity_attrs[ATTR_NAME] == "Status" + and description.name == "Status" and "Status" in entity_entry.unique_id and "(Smart)" not in entity_entry.unique_id ): - if "sd" in device_id and "disk" in entity_key: - entity_type = entity_key + if "sd" in device_id and "disk" in description.key: + entity_type = description.key continue - if "volume" in device_id and "volume" in entity_key: - entity_type = entity_key + if "volume" in device_id and "volume" in description.key: + entity_type = description.key continue - if entity_attrs[ATTR_NAME] == label: - entity_type = entity_key + if description.name == label: + entity_type = description.key if entity_type is None: return None @@ -604,51 +598,25 @@ class SynoApi: class SynologyDSMBaseEntity(CoordinatorEntity): """Representation of a Synology NAS entry.""" + entity_description: SynologyDSMEntityDescription + unique_id: str + _attr_extra_state_attributes = {ATTR_ATTRIBUTION: ATTRIBUTION} + def __init__( self, api: SynoApi, - entity_type: str, - entity_info: EntityInfo, coordinator: DataUpdateCoordinator[dict[str, dict[str, Any]]], + description: SynologyDSMEntityDescription, ) -> None: """Initialize the Synology DSM entity.""" super().__init__(coordinator) + self.entity_description = description self._api = api - self._api_key = entity_type.split(":")[0] - self.entity_type = entity_type.split(":")[-1] - self._name = f"{api.network.hostname} {entity_info[ATTR_NAME]}" - self._class = entity_info[ATTR_DEVICE_CLASS] - self._enable_default = entity_info[ENTITY_ENABLE] - self._icon = entity_info[ATTR_ICON] - self._unit = entity_info[ATTR_UNIT_OF_MEASUREMENT] - self._unique_id = f"{self._api.information.serial}_{entity_type}" - self._attr_state_class = entity_info[ATTR_STATE_CLASS] - - @property - def unique_id(self) -> str: - """Return a unique ID.""" - return self._unique_id - - @property - def name(self) -> str: - """Return the name.""" - return self._name - - @property - def icon(self) -> str | None: - """Return the icon.""" - return self._icon - - @property - def device_class(self) -> str | None: - """Return the class of this device.""" - return self._class - - @property - def extra_state_attributes(self) -> dict[str, Any]: - """Return the state attributes.""" - return {ATTR_ATTRIBUTION: ATTRIBUTION} + self._attr_name = f"{api.network.hostname} {description.name}" + self._attr_unique_id: str = ( + f"{api.information.serial}_{description.api_key}:{description.key}" + ) @property def device_info(self) -> DeviceInfo: @@ -661,14 +629,11 @@ class SynologyDSMBaseEntity(CoordinatorEntity): "sw_version": self._api.information.version_string, } - @property - def entity_registry_enabled_default(self) -> bool: - """Return if the entity should be enabled when first added to the entity registry.""" - return self._enable_default - async def async_added_to_hass(self) -> None: """Register entity for updates from API.""" - self.async_on_remove(self._api.subscribe(self._api_key, self.unique_id)) + self.async_on_remove( + self._api.subscribe(self.entity_description.api_key, self.unique_id) + ) await super().async_added_to_hass() @@ -678,13 +643,12 @@ class SynologyDSMDeviceEntity(SynologyDSMBaseEntity): def __init__( self, api: SynoApi, - entity_type: str, - entity_info: EntityInfo, coordinator: DataUpdateCoordinator[dict[str, dict[str, Any]]], + description: SynologyDSMEntityDescription, device_id: str | None = None, ) -> None: """Initialize the Synology DSM disk or volume entity.""" - super().__init__(api, entity_type, entity_info, coordinator) + super().__init__(api, coordinator, description) self._device_id = device_id self._device_name: str | None = None self._device_manufacturer: str | None = None @@ -692,7 +656,7 @@ class SynologyDSMDeviceEntity(SynologyDSMBaseEntity): self._device_firmware: str | None = None self._device_type = None - if "volume" in entity_type: + if "volume" in description.key: volume = self._api.storage.get_volume(self._device_id) # Volume does not have a name self._device_name = volume["id"].replace("_", " ").capitalize() @@ -705,7 +669,7 @@ class SynologyDSMDeviceEntity(SynologyDSMBaseEntity): .replace("raid", "RAID") .replace("shr", "SHR") ) - elif "disk" in entity_type: + elif "disk" in description.key: disk = self._api.storage.get_disk(self._device_id) self._device_name = disk["name"] self._device_manufacturer = disk["vendor"] @@ -713,9 +677,9 @@ class SynologyDSMDeviceEntity(SynologyDSMBaseEntity): self._device_firmware = disk["firm"] self._device_type = disk["diskType"] self._name = ( - f"{self._api.network.hostname} {self._device_name} {entity_info[ATTR_NAME]}" + f"{self._api.network.hostname} {self._device_name} {description.name}" ) - self._unique_id += f"_{self._device_id}" + self._attr_unique_id += f"_{self._device_id}" @property def available(self) -> bool: diff --git a/homeassistant/components/synology_dsm/binary_sensor.py b/homeassistant/components/synology_dsm/binary_sensor.py index 5f27aa3b038..fc518d6c662 100644 --- a/homeassistant/components/synology_dsm/binary_sensor.py +++ b/homeassistant/components/synology_dsm/binary_sensor.py @@ -1,11 +1,14 @@ """Support for Synology DSM binary sensors.""" from __future__ import annotations +from typing import Any + from homeassistant.components.binary_sensor import BinarySensorEntity from homeassistant.config_entries import ConfigEntry from homeassistant.const import CONF_DISKS from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddEntitiesCallback +from homeassistant.helpers.update_coordinator import DataUpdateCoordinator from . import SynoApi, SynologyDSMBaseEntity, SynologyDSMDeviceEntity from .const import ( @@ -15,6 +18,7 @@ from .const import ( STORAGE_DISK_BINARY_SENSORS, SYNO_API, UPGRADE_BINARY_SENSORS, + SynologyDSMBinarySensorEntityDescription, ) @@ -32,39 +36,52 @@ async def async_setup_entry( | SynoDSMUpgradeBinarySensor | SynoDSMStorageBinarySensor ] = [ - SynoDSMSecurityBinarySensor(api, sensor_type, sensor, coordinator) - for sensor_type, sensor in SECURITY_BINARY_SENSORS.items() + SynoDSMSecurityBinarySensor(api, coordinator, description) + for description in SECURITY_BINARY_SENSORS ] - entities += [ - SynoDSMUpgradeBinarySensor(api, sensor_type, sensor, coordinator) - for sensor_type, sensor in UPGRADE_BINARY_SENSORS.items() - ] + entities.extend( + [ + SynoDSMUpgradeBinarySensor(api, coordinator, description) + for description in UPGRADE_BINARY_SENSORS + ] + ) # Handle all disks if api.storage.disks_ids: - for disk in entry.data.get(CONF_DISKS, api.storage.disks_ids): - entities += [ - SynoDSMStorageBinarySensor( - api, - sensor_type, - sensor, - coordinator, - disk, - ) - for sensor_type, sensor in STORAGE_DISK_BINARY_SENSORS.items() + entities.extend( + [ + SynoDSMStorageBinarySensor(api, coordinator, description, disk) + for disk in entry.data.get(CONF_DISKS, api.storage.disks_ids) + for description in STORAGE_DISK_BINARY_SENSORS ] + ) async_add_entities(entities) -class SynoDSMSecurityBinarySensor(SynologyDSMBaseEntity, BinarySensorEntity): +class SynoDSMBinarySensor(SynologyDSMBaseEntity, BinarySensorEntity): + """Mixin for binary sensor specific attributes.""" + + entity_description: SynologyDSMBinarySensorEntityDescription + + def __init__( + self, + api: SynoApi, + coordinator: DataUpdateCoordinator[dict[str, dict[str, Any]]], + description: SynologyDSMBinarySensorEntityDescription, + ) -> None: + """Initialize the Synology DSM binary_sensor entity.""" + super().__init__(api, coordinator, description) + + +class SynoDSMSecurityBinarySensor(SynoDSMBinarySensor): """Representation a Synology Security binary sensor.""" @property def is_on(self) -> bool: """Return the state.""" - return getattr(self._api.security, self.entity_type) != "safe" # type: ignore[no-any-return] + return getattr(self._api.security, self.entity_description.key) != "safe" # type: ignore[no-any-return] @property def available(self) -> bool: @@ -77,22 +94,36 @@ class SynoDSMSecurityBinarySensor(SynologyDSMBaseEntity, BinarySensorEntity): return self._api.security.status_by_check # type: ignore[no-any-return] -class SynoDSMStorageBinarySensor(SynologyDSMDeviceEntity, BinarySensorEntity): +class SynoDSMStorageBinarySensor(SynologyDSMDeviceEntity, SynoDSMBinarySensor): """Representation a Synology Storage binary sensor.""" + entity_description: SynologyDSMBinarySensorEntityDescription + + def __init__( + self, + api: SynoApi, + coordinator: DataUpdateCoordinator[dict[str, dict[str, Any]]], + description: SynologyDSMBinarySensorEntityDescription, + device_id: str | None = None, + ) -> None: + """Initialize the Synology DSM storage binary_sensor entity.""" + super().__init__(api, coordinator, description, device_id) + @property def is_on(self) -> bool: """Return the state.""" - return bool(getattr(self._api.storage, self.entity_type)(self._device_id)) + return bool( + getattr(self._api.storage, self.entity_description.key)(self._device_id) + ) -class SynoDSMUpgradeBinarySensor(SynologyDSMBaseEntity, BinarySensorEntity): +class SynoDSMUpgradeBinarySensor(SynoDSMBinarySensor): """Representation a Synology Upgrade binary sensor.""" @property def is_on(self) -> bool: """Return the state.""" - return bool(getattr(self._api.upgrade, self.entity_type)) + return bool(getattr(self._api.upgrade, self.entity_description.key)) @property def available(self) -> bool: diff --git a/homeassistant/components/synology_dsm/camera.py b/homeassistant/components/synology_dsm/camera.py index d609a434ae2..c305d11f4e0 100644 --- a/homeassistant/components/synology_dsm/camera.py +++ b/homeassistant/components/synology_dsm/camera.py @@ -1,6 +1,7 @@ """Support for Synology DSM cameras.""" from __future__ import annotations +from dataclasses import dataclass import logging from synology_dsm.api.surveillance_station import SynoCamera, SynoSurveillanceStation @@ -9,26 +10,30 @@ from synology_dsm.exceptions import ( SynologyDSMRequestException, ) -from homeassistant.components.camera import SUPPORT_STREAM, Camera -from homeassistant.components.sensor import ATTR_STATE_CLASS -from homeassistant.config_entries import ConfigEntry -from homeassistant.const import ( - ATTR_DEVICE_CLASS, - ATTR_ICON, - ATTR_NAME, - ATTR_UNIT_OF_MEASUREMENT, +from homeassistant.components.camera import ( + SUPPORT_STREAM, + Camera, + CameraEntityDescription, ) +from homeassistant.config_entries import ConfigEntry from homeassistant.core import HomeAssistant from homeassistant.helpers.entity import DeviceInfo from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.update_coordinator import DataUpdateCoordinator from . import SynoApi, SynologyDSMBaseEntity -from .const import COORDINATOR_CAMERAS, DOMAIN, ENTITY_ENABLE, SYNO_API +from .const import COORDINATOR_CAMERAS, DOMAIN, SYNO_API, SynologyDSMEntityDescription _LOGGER = logging.getLogger(__name__) +@dataclass +class SynologyDSMCameraEntityDescription( + CameraEntityDescription, SynologyDSMEntityDescription +): + """Describes Synology DSM camera entity.""" + + async def async_setup_entry( hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback ) -> None: @@ -56,6 +61,7 @@ class SynoDSMCamera(SynologyDSMBaseEntity, Camera): """Representation a Synology camera.""" coordinator: DataUpdateCoordinator[dict[str, dict[str, SynoCamera]]] + entity_description: SynologyDSMCameraEntityDescription def __init__( self, @@ -64,26 +70,21 @@ class SynoDSMCamera(SynologyDSMBaseEntity, Camera): camera_id: str, ) -> None: """Initialize a Synology camera.""" - super().__init__( - api, - f"{SynoSurveillanceStation.CAMERA_API_KEY}:{camera_id}", - { - ATTR_NAME: coordinator.data["cameras"][camera_id].name, - ENTITY_ENABLE: coordinator.data["cameras"][camera_id].is_enabled, - ATTR_DEVICE_CLASS: None, - ATTR_ICON: None, - ATTR_UNIT_OF_MEASUREMENT: None, - ATTR_STATE_CLASS: None, - }, - coordinator, + description = SynologyDSMCameraEntityDescription( + api_key=SynoSurveillanceStation.CAMERA_API_KEY, + key=camera_id, + name=coordinator.data["cameras"][camera_id].name, + entity_registry_enabled_default=coordinator.data["cameras"][ + camera_id + ].is_enabled, ) + super().__init__(api, coordinator, description) Camera.__init__(self) - self._camera_id = camera_id @property def camera_data(self) -> SynoCamera: """Camera data.""" - return self.coordinator.data["cameras"][self._camera_id] + return self.coordinator.data["cameras"][self.entity_description.key] @property def device_info(self) -> DeviceInfo: @@ -134,7 +135,7 @@ class SynoDSMCamera(SynologyDSMBaseEntity, Camera): if not self.available: return None try: - return self._api.surveillance_station.get_camera_image(self._camera_id) # type: ignore[no-any-return] + return self._api.surveillance_station.get_camera_image(self.entity_description.key) # type: ignore[no-any-return] except ( SynologyDSMAPIErrorException, SynologyDSMRequestException, @@ -163,7 +164,9 @@ class SynoDSMCamera(SynologyDSMBaseEntity, Camera): "SynoDSMCamera.enable_motion_detection(%s)", self.camera_data.name, ) - self._api.surveillance_station.enable_motion_detection(self._camera_id) + self._api.surveillance_station.enable_motion_detection( + self.entity_description.key + ) def disable_motion_detection(self) -> None: """Disable motion detection in camera.""" @@ -171,4 +174,6 @@ class SynoDSMCamera(SynologyDSMBaseEntity, Camera): "SynoDSMCamera.disable_motion_detection(%s)", self.camera_data.name, ) - self._api.surveillance_station.disable_motion_detection(self._camera_id) + self._api.surveillance_station.disable_motion_detection( + self.entity_description.key + ) diff --git a/homeassistant/components/synology_dsm/const.py b/homeassistant/components/synology_dsm/const.py index 1fc6ba6e09b..e054a9594a0 100644 --- a/homeassistant/components/synology_dsm/const.py +++ b/homeassistant/components/synology_dsm/const.py @@ -1,7 +1,7 @@ """Constants for Synology DSM.""" from __future__ import annotations -from typing import Final, TypedDict +from dataclasses import dataclass from synology_dsm.api.core.security import SynoCoreSecurity from synology_dsm.api.core.upgrade import SynoCoreUpgrade @@ -13,13 +13,14 @@ from synology_dsm.api.surveillance_station import SynoSurveillanceStation from homeassistant.components.binary_sensor import ( DEVICE_CLASS_SAFETY, DEVICE_CLASS_UPDATE, + BinarySensorEntityDescription, ) -from homeassistant.components.sensor import ATTR_STATE_CLASS, STATE_CLASS_MEASUREMENT +from homeassistant.components.sensor import ( + STATE_CLASS_MEASUREMENT, + SensorEntityDescription, +) +from homeassistant.components.switch import SwitchEntityDescription from homeassistant.const import ( - ATTR_DEVICE_CLASS, - ATTR_ICON, - ATTR_NAME, - ATTR_UNIT_OF_MEASUREMENT, DATA_MEGABYTES, DATA_RATE_KILOBYTES_PER_SECOND, DATA_TERABYTES, @@ -28,18 +29,7 @@ from homeassistant.const import ( PERCENTAGE, TEMP_CELSIUS, ) - - -class EntityInfo(TypedDict): - """TypedDict for EntityInfo.""" - - name: str - unit_of_measurement: str | None - icon: str | None - device_class: str | None - state_class: str | None - enable: bool - +from homeassistant.helpers.entity import EntityDescription DOMAIN = "synology_dsm" PLATFORMS = ["binary_sensor", "camera", "sensor", "switch"] @@ -68,7 +58,6 @@ DEFAULT_SCAN_INTERVAL = 15 # min DEFAULT_TIMEOUT = 10 # sec ENTITY_UNIT_LOAD = "load" -ENTITY_ENABLE: Final = "enable" # Services SERVICE_REBOOT = "reboot" @@ -78,285 +67,302 @@ SERVICES = [ SERVICE_SHUTDOWN, ] -# Entity keys should start with the API_KEY to fetch + +@dataclass +class SynologyDSMRequiredKeysMixin: + """Mixin for required keys.""" + + api_key: str + + +@dataclass +class SynologyDSMEntityDescription(EntityDescription, SynologyDSMRequiredKeysMixin): + """Generic Synology DSM entity description.""" + + +@dataclass +class SynologyDSMBinarySensorEntityDescription( + BinarySensorEntityDescription, SynologyDSMEntityDescription +): + """Describes Synology DSM binary sensor entity.""" + + +@dataclass +class SynologyDSMSensorEntityDescription( + SensorEntityDescription, SynologyDSMEntityDescription +): + """Describes Synology DSM sensor entity.""" + + +@dataclass +class SynologyDSMSwitchEntityDescription( + SwitchEntityDescription, SynologyDSMEntityDescription +): + """Describes Synology DSM switch entity.""" + # Binary sensors -UPGRADE_BINARY_SENSORS: dict[str, EntityInfo] = { - f"{SynoCoreUpgrade.API_KEY}:update_available": { - ATTR_NAME: "Update available", - ATTR_UNIT_OF_MEASUREMENT: None, - ATTR_ICON: None, - ATTR_DEVICE_CLASS: DEVICE_CLASS_UPDATE, - ENTITY_ENABLE: True, - ATTR_STATE_CLASS: None, - }, -} +UPGRADE_BINARY_SENSORS: tuple[SynologyDSMBinarySensorEntityDescription, ...] = ( + SynologyDSMBinarySensorEntityDescription( + api_key=SynoCoreUpgrade.API_KEY, + key="update_available", + name="Update available", + device_class=DEVICE_CLASS_UPDATE, + ), +) -SECURITY_BINARY_SENSORS: dict[str, EntityInfo] = { - f"{SynoCoreSecurity.API_KEY}:status": { - ATTR_NAME: "Security status", - ATTR_UNIT_OF_MEASUREMENT: None, - ATTR_ICON: None, - ATTR_DEVICE_CLASS: DEVICE_CLASS_SAFETY, - ENTITY_ENABLE: True, - ATTR_STATE_CLASS: None, - }, -} +SECURITY_BINARY_SENSORS: tuple[SynologyDSMBinarySensorEntityDescription, ...] = ( + SynologyDSMBinarySensorEntityDescription( + api_key=SynoCoreSecurity.API_KEY, + key="status", + name="Security status", + device_class=DEVICE_CLASS_SAFETY, + ), +) -STORAGE_DISK_BINARY_SENSORS: dict[str, EntityInfo] = { - f"{SynoStorage.API_KEY}:disk_exceed_bad_sector_thr": { - ATTR_NAME: "Exceeded Max Bad Sectors", - ATTR_UNIT_OF_MEASUREMENT: None, - ATTR_ICON: None, - ATTR_DEVICE_CLASS: DEVICE_CLASS_SAFETY, - ENTITY_ENABLE: True, - ATTR_STATE_CLASS: None, - }, - f"{SynoStorage.API_KEY}:disk_below_remain_life_thr": { - ATTR_NAME: "Below Min Remaining Life", - ATTR_UNIT_OF_MEASUREMENT: None, - ATTR_ICON: None, - ATTR_DEVICE_CLASS: DEVICE_CLASS_SAFETY, - ENTITY_ENABLE: True, - ATTR_STATE_CLASS: None, - }, -} +STORAGE_DISK_BINARY_SENSORS: tuple[SynologyDSMBinarySensorEntityDescription, ...] = ( + SynologyDSMBinarySensorEntityDescription( + api_key=SynoStorage.API_KEY, + key="disk_exceed_bad_sector_thr", + name="Exceeded Max Bad Sectors", + device_class=DEVICE_CLASS_SAFETY, + ), + SynologyDSMBinarySensorEntityDescription( + api_key=SynoStorage.API_KEY, + key="disk_below_remain_life_thr", + name="Below Min Remaining Life", + device_class=DEVICE_CLASS_SAFETY, + ), +) # Sensors -UTILISATION_SENSORS: dict[str, EntityInfo] = { - f"{SynoCoreUtilization.API_KEY}:cpu_other_load": { - ATTR_NAME: "CPU Utilization (Other)", - ATTR_UNIT_OF_MEASUREMENT: PERCENTAGE, - ATTR_ICON: "mdi:chip", - ATTR_DEVICE_CLASS: None, - ENTITY_ENABLE: False, - ATTR_STATE_CLASS: STATE_CLASS_MEASUREMENT, - }, - f"{SynoCoreUtilization.API_KEY}:cpu_user_load": { - ATTR_NAME: "CPU Utilization (User)", - ATTR_UNIT_OF_MEASUREMENT: PERCENTAGE, - ATTR_ICON: "mdi:chip", - ATTR_DEVICE_CLASS: None, - ENTITY_ENABLE: True, - ATTR_STATE_CLASS: STATE_CLASS_MEASUREMENT, - }, - f"{SynoCoreUtilization.API_KEY}:cpu_system_load": { - ATTR_NAME: "CPU Utilization (System)", - ATTR_UNIT_OF_MEASUREMENT: PERCENTAGE, - ATTR_ICON: "mdi:chip", - ATTR_DEVICE_CLASS: None, - ENTITY_ENABLE: False, - ATTR_STATE_CLASS: STATE_CLASS_MEASUREMENT, - }, - f"{SynoCoreUtilization.API_KEY}:cpu_total_load": { - ATTR_NAME: "CPU Utilization (Total)", - ATTR_UNIT_OF_MEASUREMENT: PERCENTAGE, - ATTR_ICON: "mdi:chip", - ATTR_DEVICE_CLASS: None, - ENTITY_ENABLE: True, - ATTR_STATE_CLASS: STATE_CLASS_MEASUREMENT, - }, - f"{SynoCoreUtilization.API_KEY}:cpu_1min_load": { - ATTR_NAME: "CPU Load Average (1 min)", - ATTR_UNIT_OF_MEASUREMENT: ENTITY_UNIT_LOAD, - ATTR_ICON: "mdi:chip", - ATTR_DEVICE_CLASS: None, - ENTITY_ENABLE: False, - ATTR_STATE_CLASS: None, - }, - f"{SynoCoreUtilization.API_KEY}:cpu_5min_load": { - ATTR_NAME: "CPU Load Average (5 min)", - ATTR_UNIT_OF_MEASUREMENT: ENTITY_UNIT_LOAD, - ATTR_ICON: "mdi:chip", - ATTR_DEVICE_CLASS: None, - ENTITY_ENABLE: True, - ATTR_STATE_CLASS: None, - }, - f"{SynoCoreUtilization.API_KEY}:cpu_15min_load": { - ATTR_NAME: "CPU Load Average (15 min)", - ATTR_UNIT_OF_MEASUREMENT: ENTITY_UNIT_LOAD, - ATTR_ICON: "mdi:chip", - ATTR_DEVICE_CLASS: None, - ENTITY_ENABLE: True, - ATTR_STATE_CLASS: None, - }, - f"{SynoCoreUtilization.API_KEY}:memory_real_usage": { - ATTR_NAME: "Memory Usage (Real)", - ATTR_UNIT_OF_MEASUREMENT: PERCENTAGE, - ATTR_ICON: "mdi:memory", - ATTR_DEVICE_CLASS: None, - ENTITY_ENABLE: True, - ATTR_STATE_CLASS: STATE_CLASS_MEASUREMENT, - }, - f"{SynoCoreUtilization.API_KEY}:memory_size": { - ATTR_NAME: "Memory Size", - ATTR_UNIT_OF_MEASUREMENT: DATA_MEGABYTES, - ATTR_ICON: "mdi:memory", - ATTR_DEVICE_CLASS: None, - ENTITY_ENABLE: False, - ATTR_STATE_CLASS: STATE_CLASS_MEASUREMENT, - }, - f"{SynoCoreUtilization.API_KEY}:memory_cached": { - ATTR_NAME: "Memory Cached", - ATTR_UNIT_OF_MEASUREMENT: DATA_MEGABYTES, - ATTR_ICON: "mdi:memory", - ATTR_DEVICE_CLASS: None, - ENTITY_ENABLE: False, - ATTR_STATE_CLASS: STATE_CLASS_MEASUREMENT, - }, - f"{SynoCoreUtilization.API_KEY}:memory_available_swap": { - ATTR_NAME: "Memory Available (Swap)", - ATTR_UNIT_OF_MEASUREMENT: DATA_MEGABYTES, - ATTR_ICON: "mdi:memory", - ATTR_DEVICE_CLASS: None, - ENTITY_ENABLE: True, - ATTR_STATE_CLASS: STATE_CLASS_MEASUREMENT, - }, - f"{SynoCoreUtilization.API_KEY}:memory_available_real": { - ATTR_NAME: "Memory Available (Real)", - ATTR_UNIT_OF_MEASUREMENT: DATA_MEGABYTES, - ATTR_ICON: "mdi:memory", - ATTR_DEVICE_CLASS: None, - ENTITY_ENABLE: True, - ATTR_STATE_CLASS: STATE_CLASS_MEASUREMENT, - }, - f"{SynoCoreUtilization.API_KEY}:memory_total_swap": { - ATTR_NAME: "Memory Total (Swap)", - ATTR_UNIT_OF_MEASUREMENT: DATA_MEGABYTES, - ATTR_ICON: "mdi:memory", - ATTR_DEVICE_CLASS: None, - ENTITY_ENABLE: True, - ATTR_STATE_CLASS: STATE_CLASS_MEASUREMENT, - }, - f"{SynoCoreUtilization.API_KEY}:memory_total_real": { - ATTR_NAME: "Memory Total (Real)", - ATTR_UNIT_OF_MEASUREMENT: DATA_MEGABYTES, - ATTR_ICON: "mdi:memory", - ATTR_DEVICE_CLASS: None, - ENTITY_ENABLE: True, - ATTR_STATE_CLASS: STATE_CLASS_MEASUREMENT, - }, - f"{SynoCoreUtilization.API_KEY}:network_up": { - ATTR_NAME: "Upload Throughput", - ATTR_UNIT_OF_MEASUREMENT: DATA_RATE_KILOBYTES_PER_SECOND, - ATTR_ICON: "mdi:upload", - ATTR_DEVICE_CLASS: None, - ENTITY_ENABLE: True, - ATTR_STATE_CLASS: STATE_CLASS_MEASUREMENT, - }, - f"{SynoCoreUtilization.API_KEY}:network_down": { - ATTR_NAME: "Download Throughput", - ATTR_UNIT_OF_MEASUREMENT: DATA_RATE_KILOBYTES_PER_SECOND, - ATTR_ICON: "mdi:download", - ATTR_DEVICE_CLASS: None, - ENTITY_ENABLE: True, - ATTR_STATE_CLASS: STATE_CLASS_MEASUREMENT, - }, -} -STORAGE_VOL_SENSORS: dict[str, EntityInfo] = { - f"{SynoStorage.API_KEY}:volume_status": { - ATTR_NAME: "Status", - ATTR_UNIT_OF_MEASUREMENT: None, - ATTR_ICON: "mdi:checkbox-marked-circle-outline", - ATTR_DEVICE_CLASS: None, - ENTITY_ENABLE: True, - ATTR_STATE_CLASS: None, - }, - f"{SynoStorage.API_KEY}:volume_size_total": { - ATTR_NAME: "Total Size", - ATTR_UNIT_OF_MEASUREMENT: DATA_TERABYTES, - ATTR_ICON: "mdi:chart-pie", - ATTR_DEVICE_CLASS: None, - ENTITY_ENABLE: False, - ATTR_STATE_CLASS: STATE_CLASS_MEASUREMENT, - }, - f"{SynoStorage.API_KEY}:volume_size_used": { - ATTR_NAME: "Used Space", - ATTR_UNIT_OF_MEASUREMENT: DATA_TERABYTES, - ATTR_ICON: "mdi:chart-pie", - ATTR_DEVICE_CLASS: None, - ENTITY_ENABLE: True, - ATTR_STATE_CLASS: STATE_CLASS_MEASUREMENT, - }, - f"{SynoStorage.API_KEY}:volume_percentage_used": { - ATTR_NAME: "Volume Used", - ATTR_UNIT_OF_MEASUREMENT: PERCENTAGE, - ATTR_ICON: "mdi:chart-pie", - ATTR_DEVICE_CLASS: None, - ENTITY_ENABLE: True, - ATTR_STATE_CLASS: None, - }, - f"{SynoStorage.API_KEY}:volume_disk_temp_avg": { - ATTR_NAME: "Average Disk Temp", - ATTR_UNIT_OF_MEASUREMENT: TEMP_CELSIUS, - ATTR_ICON: None, - ATTR_DEVICE_CLASS: DEVICE_CLASS_TEMPERATURE, - ENTITY_ENABLE: True, - ATTR_STATE_CLASS: None, - }, - f"{SynoStorage.API_KEY}:volume_disk_temp_max": { - ATTR_NAME: "Maximum Disk Temp", - ATTR_UNIT_OF_MEASUREMENT: TEMP_CELSIUS, - ATTR_ICON: None, - ATTR_DEVICE_CLASS: DEVICE_CLASS_TEMPERATURE, - ENTITY_ENABLE: False, - ATTR_STATE_CLASS: None, - }, -} -STORAGE_DISK_SENSORS: dict[str, EntityInfo] = { - f"{SynoStorage.API_KEY}:disk_smart_status": { - ATTR_NAME: "Status (Smart)", - ATTR_UNIT_OF_MEASUREMENT: None, - ATTR_ICON: "mdi:checkbox-marked-circle-outline", - ATTR_DEVICE_CLASS: None, - ENTITY_ENABLE: False, - ATTR_STATE_CLASS: None, - }, - f"{SynoStorage.API_KEY}:disk_status": { - ATTR_NAME: "Status", - ATTR_UNIT_OF_MEASUREMENT: None, - ATTR_ICON: "mdi:checkbox-marked-circle-outline", - ATTR_DEVICE_CLASS: None, - ENTITY_ENABLE: True, - ATTR_STATE_CLASS: None, - }, - f"{SynoStorage.API_KEY}:disk_temp": { - ATTR_NAME: "Temperature", - ATTR_UNIT_OF_MEASUREMENT: TEMP_CELSIUS, - ATTR_ICON: None, - ATTR_DEVICE_CLASS: DEVICE_CLASS_TEMPERATURE, - ENTITY_ENABLE: True, - ATTR_STATE_CLASS: STATE_CLASS_MEASUREMENT, - }, -} +UTILISATION_SENSORS: tuple[SynologyDSMSensorEntityDescription, ...] = ( + SynologyDSMSensorEntityDescription( + api_key=SynoCoreUtilization.API_KEY, + key="cpu_other_load", + name="CPU Utilization (Other)", + native_unit_of_measurement=PERCENTAGE, + icon="mdi:chip", + entity_registry_enabled_default=False, + state_class=STATE_CLASS_MEASUREMENT, + ), + SynologyDSMSensorEntityDescription( + api_key=SynoCoreUtilization.API_KEY, + key="cpu_user_load", + name="CPU Utilization (User)", + native_unit_of_measurement=PERCENTAGE, + icon="mdi:chip", + state_class=STATE_CLASS_MEASUREMENT, + ), + SynologyDSMSensorEntityDescription( + api_key=SynoCoreUtilization.API_KEY, + key="cpu_system_load", + name="CPU Utilization (System)", + native_unit_of_measurement=PERCENTAGE, + icon="mdi:chip", + entity_registry_enabled_default=False, + state_class=STATE_CLASS_MEASUREMENT, + ), + SynologyDSMSensorEntityDescription( + api_key=SynoCoreUtilization.API_KEY, + key="cpu_total_load", + name="CPU Utilization (Total)", + native_unit_of_measurement=PERCENTAGE, + icon="mdi:chip", + state_class=STATE_CLASS_MEASUREMENT, + ), + SynologyDSMSensorEntityDescription( + api_key=SynoCoreUtilization.API_KEY, + key="cpu_1min_load", + name="CPU Load Average (1 min)", + native_unit_of_measurement=ENTITY_UNIT_LOAD, + icon="mdi:chip", + entity_registry_enabled_default=False, + ), + SynologyDSMSensorEntityDescription( + api_key=SynoCoreUtilization.API_KEY, + key="cpu_5min_load", + name="CPU Load Average (5 min)", + native_unit_of_measurement=ENTITY_UNIT_LOAD, + icon="mdi:chip", + ), + SynologyDSMSensorEntityDescription( + api_key=SynoCoreUtilization.API_KEY, + key="cpu_15min_load", + name="CPU Load Average (15 min)", + native_unit_of_measurement=ENTITY_UNIT_LOAD, + icon="mdi:chip", + ), + SynologyDSMSensorEntityDescription( + api_key=SynoCoreUtilization.API_KEY, + key="memory_real_usage", + name="Memory Usage (Real)", + native_unit_of_measurement=PERCENTAGE, + icon="mdi:memory", + state_class=STATE_CLASS_MEASUREMENT, + ), + SynologyDSMSensorEntityDescription( + api_key=SynoCoreUtilization.API_KEY, + key="memory_size", + name="Memory Size", + native_unit_of_measurement=DATA_MEGABYTES, + icon="mdi:memory", + entity_registry_enabled_default=False, + state_class=STATE_CLASS_MEASUREMENT, + ), + SynologyDSMSensorEntityDescription( + api_key=SynoCoreUtilization.API_KEY, + key="memory_cached", + name="Memory Cached", + native_unit_of_measurement=DATA_MEGABYTES, + icon="mdi:memory", + entity_registry_enabled_default=False, + state_class=STATE_CLASS_MEASUREMENT, + ), + SynologyDSMSensorEntityDescription( + api_key=SynoCoreUtilization.API_KEY, + key="memory_available_swap", + name="Memory Available (Swap)", + native_unit_of_measurement=DATA_MEGABYTES, + icon="mdi:memory", + state_class=STATE_CLASS_MEASUREMENT, + ), + SynologyDSMSensorEntityDescription( + api_key=SynoCoreUtilization.API_KEY, + key="memory_available_real", + name="Memory Available (Real)", + native_unit_of_measurement=DATA_MEGABYTES, + icon="mdi:memory", + state_class=STATE_CLASS_MEASUREMENT, + ), + SynologyDSMSensorEntityDescription( + api_key=SynoCoreUtilization.API_KEY, + key="memory_total_swap", + name="Memory Total (Swap)", + native_unit_of_measurement=DATA_MEGABYTES, + icon="mdi:memory", + state_class=STATE_CLASS_MEASUREMENT, + ), + SynologyDSMSensorEntityDescription( + api_key=SynoCoreUtilization.API_KEY, + key="memory_total_real", + name="Memory Total (Real)", + native_unit_of_measurement=DATA_MEGABYTES, + icon="mdi:memory", + state_class=STATE_CLASS_MEASUREMENT, + ), + SynologyDSMSensorEntityDescription( + api_key=SynoCoreUtilization.API_KEY, + key="network_up", + name="Upload Throughput", + native_unit_of_measurement=DATA_RATE_KILOBYTES_PER_SECOND, + icon="mdi:upload", + state_class=STATE_CLASS_MEASUREMENT, + ), + SynologyDSMSensorEntityDescription( + api_key=SynoCoreUtilization.API_KEY, + key="network_down", + name="Download Throughput", + native_unit_of_measurement=DATA_RATE_KILOBYTES_PER_SECOND, + icon="mdi:download", + state_class=STATE_CLASS_MEASUREMENT, + ), +) +STORAGE_VOL_SENSORS: tuple[SynologyDSMSensorEntityDescription, ...] = ( + SynologyDSMSensorEntityDescription( + api_key=SynoStorage.API_KEY, + key="volume_status", + name="Status", + icon="mdi:checkbox-marked-circle-outline", + ), + SynologyDSMSensorEntityDescription( + api_key=SynoStorage.API_KEY, + key="volume_size_total", + name="Total Size", + native_unit_of_measurement=DATA_TERABYTES, + icon="mdi:chart-pie", + entity_registry_enabled_default=False, + state_class=STATE_CLASS_MEASUREMENT, + ), + SynologyDSMSensorEntityDescription( + api_key=SynoStorage.API_KEY, + key="volume_size_used", + name="Used Space", + native_unit_of_measurement=DATA_TERABYTES, + icon="mdi:chart-pie", + state_class=STATE_CLASS_MEASUREMENT, + ), + SynologyDSMSensorEntityDescription( + api_key=SynoStorage.API_KEY, + key="volume_percentage_used", + name="Volume Used", + native_unit_of_measurement=PERCENTAGE, + icon="mdi:chart-pie", + ), + SynologyDSMSensorEntityDescription( + api_key=SynoStorage.API_KEY, + key="volume_disk_temp_avg", + name="Average Disk Temp", + native_unit_of_measurement=TEMP_CELSIUS, + device_class=DEVICE_CLASS_TEMPERATURE, + ), + SynologyDSMSensorEntityDescription( + api_key=SynoStorage.API_KEY, + key="volume_disk_temp_max", + name="Maximum Disk Temp", + native_unit_of_measurement=TEMP_CELSIUS, + device_class=DEVICE_CLASS_TEMPERATURE, + entity_registry_enabled_default=False, + ), +) +STORAGE_DISK_SENSORS: tuple[SynologyDSMSensorEntityDescription, ...] = ( + SynologyDSMSensorEntityDescription( + api_key=SynoStorage.API_KEY, + key="disk_smart_status", + name="Status (Smart)", + icon="mdi:checkbox-marked-circle-outline", + entity_registry_enabled_default=False, + ), + SynologyDSMSensorEntityDescription( + api_key=SynoStorage.API_KEY, + key="disk_status", + name="Status", + icon="mdi:checkbox-marked-circle-outline", + ), + SynologyDSMSensorEntityDescription( + api_key=SynoStorage.API_KEY, + key="disk_temp", + name="Temperature", + native_unit_of_measurement=TEMP_CELSIUS, + device_class=DEVICE_CLASS_TEMPERATURE, + state_class=STATE_CLASS_MEASUREMENT, + ), +) -INFORMATION_SENSORS: dict[str, EntityInfo] = { - f"{SynoDSMInformation.API_KEY}:temperature": { - ATTR_NAME: "temperature", - ATTR_UNIT_OF_MEASUREMENT: TEMP_CELSIUS, - ATTR_ICON: None, - ATTR_DEVICE_CLASS: DEVICE_CLASS_TEMPERATURE, - ENTITY_ENABLE: True, - ATTR_STATE_CLASS: STATE_CLASS_MEASUREMENT, - }, - f"{SynoDSMInformation.API_KEY}:uptime": { - ATTR_NAME: "last boot", - ATTR_UNIT_OF_MEASUREMENT: None, - ATTR_ICON: None, - ATTR_DEVICE_CLASS: DEVICE_CLASS_TIMESTAMP, - ENTITY_ENABLE: False, - ATTR_STATE_CLASS: None, - }, -} +INFORMATION_SENSORS: tuple[SynologyDSMSensorEntityDescription, ...] = ( + SynologyDSMSensorEntityDescription( + api_key=SynoDSMInformation.API_KEY, + key="temperature", + name="temperature", + native_unit_of_measurement=TEMP_CELSIUS, + device_class=DEVICE_CLASS_TEMPERATURE, + state_class=STATE_CLASS_MEASUREMENT, + ), + SynologyDSMSensorEntityDescription( + api_key=SynoDSMInformation.API_KEY, + key="uptime", + name="last boot", + device_class=DEVICE_CLASS_TIMESTAMP, + entity_registry_enabled_default=False, + ), +) # Switch -SURVEILLANCE_SWITCH: dict[str, EntityInfo] = { - f"{SynoSurveillanceStation.HOME_MODE_API_KEY}:home_mode": { - ATTR_NAME: "home mode", - ATTR_UNIT_OF_MEASUREMENT: None, - ATTR_ICON: "mdi:home-account", - ATTR_DEVICE_CLASS: None, - ENTITY_ENABLE: True, - ATTR_STATE_CLASS: None, - }, -} +SURVEILLANCE_SWITCH: tuple[SynologyDSMSwitchEntityDescription, ...] = ( + SynologyDSMSwitchEntityDescription( + api_key=SynoSurveillanceStation.HOME_MODE_API_KEY, + key="home_mode", + name="home mode", + icon="mdi:home-account", + ), +) diff --git a/homeassistant/components/synology_dsm/sensor.py b/homeassistant/components/synology_dsm/sensor.py index 72ddb944b11..1aa7e35d992 100644 --- a/homeassistant/components/synology_dsm/sensor.py +++ b/homeassistant/components/synology_dsm/sensor.py @@ -28,7 +28,7 @@ from .const import ( STORAGE_VOL_SENSORS, SYNO_API, UTILISATION_SENSORS, - EntityInfo, + SynologyDSMSensorEntityDescription, ) @@ -42,77 +42,77 @@ async def async_setup_entry( coordinator = data[COORDINATOR_CENTRAL] entities: list[SynoDSMUtilSensor | SynoDSMStorageSensor | SynoDSMInfoSensor] = [ - SynoDSMUtilSensor(api, sensor_type, sensor, coordinator) - for sensor_type, sensor in UTILISATION_SENSORS.items() + SynoDSMUtilSensor(api, coordinator, description) + for description in UTILISATION_SENSORS ] # Handle all volumes if api.storage.volumes_ids: - for volume in entry.data.get(CONF_VOLUMES, api.storage.volumes_ids): - entities += [ - SynoDSMStorageSensor( - api, - sensor_type, - sensor, - coordinator, - volume, - ) - for sensor_type, sensor in STORAGE_VOL_SENSORS.items() + entities.extend( + [ + SynoDSMStorageSensor(api, coordinator, description, volume) + for volume in entry.data.get(CONF_VOLUMES, api.storage.volumes_ids) + for description in STORAGE_VOL_SENSORS ] + ) # Handle all disks if api.storage.disks_ids: - for disk in entry.data.get(CONF_DISKS, api.storage.disks_ids): - entities += [ - SynoDSMStorageSensor( - api, - sensor_type, - sensor, - coordinator, - disk, - ) - for sensor_type, sensor in STORAGE_DISK_SENSORS.items() + entities.extend( + [ + SynoDSMStorageSensor(api, coordinator, description, disk) + for disk in entry.data.get(CONF_DISKS, api.storage.disks_ids) + for description in STORAGE_DISK_SENSORS ] + ) - entities += [ - SynoDSMInfoSensor(api, sensor_type, sensor, coordinator) - for sensor_type, sensor in INFORMATION_SENSORS.items() - ] + entities.extend( + [ + SynoDSMInfoSensor(api, coordinator, description) + for description in INFORMATION_SENSORS + ] + ) async_add_entities(entities) -class SynoDSMSensor(SynologyDSMBaseEntity): +class SynoDSMSensor(SynologyDSMBaseEntity, SensorEntity): """Mixin for sensor specific attributes.""" - @property - def native_unit_of_measurement(self) -> str | None: - """Return the unit the value is expressed in.""" - return self._unit + entity_description: SynologyDSMSensorEntityDescription + + def __init__( + self, + api: SynoApi, + coordinator: DataUpdateCoordinator[dict[str, dict[str, Any]]], + description: SynologyDSMSensorEntityDescription, + ) -> None: + """Initialize the Synology DSM sensor entity.""" + super().__init__(api, coordinator, description) -class SynoDSMUtilSensor(SynoDSMSensor, SensorEntity): +class SynoDSMUtilSensor(SynoDSMSensor): """Representation a Synology Utilisation sensor.""" @property def native_value(self) -> Any | None: """Return the state.""" - attr = getattr(self._api.utilisation, self.entity_type) + attr = getattr(self._api.utilisation, self.entity_description.key) if callable(attr): attr = attr() if attr is None: return None # Data (RAM) - if self._unit == DATA_MEGABYTES: + if self.native_unit_of_measurement == DATA_MEGABYTES: return round(attr / 1024.0 ** 2, 1) # Network - if self._unit == DATA_RATE_KILOBYTES_PER_SECOND: + if self.native_unit_of_measurement == DATA_RATE_KILOBYTES_PER_SECOND: return round(attr / 1024.0, 1) # CPU load average - if self._unit == ENTITY_UNIT_LOAD: + if self.native_unit_of_measurement == ENTITY_UNIT_LOAD: return round(attr / 100, 2) return attr @@ -123,46 +123,57 @@ class SynoDSMUtilSensor(SynoDSMSensor, SensorEntity): return bool(self._api.utilisation) -class SynoDSMStorageSensor(SynologyDSMDeviceEntity, SynoDSMSensor, SensorEntity): +class SynoDSMStorageSensor(SynologyDSMDeviceEntity, SynoDSMSensor): """Representation a Synology Storage sensor.""" + entity_description: SynologyDSMSensorEntityDescription + + def __init__( + self, + api: SynoApi, + coordinator: DataUpdateCoordinator[dict[str, dict[str, Any]]], + description: SynologyDSMSensorEntityDescription, + device_id: str | None = None, + ) -> None: + """Initialize the Synology DSM storage sensor entity.""" + super().__init__(api, coordinator, description, device_id) + @property def native_value(self) -> Any | None: """Return the state.""" - attr = getattr(self._api.storage, self.entity_type)(self._device_id) + attr = getattr(self._api.storage, self.entity_description.key)(self._device_id) if attr is None: return None # Data (disk space) - if self._unit == DATA_TERABYTES: + if self.native_unit_of_measurement == DATA_TERABYTES: return round(attr / 1024.0 ** 4, 2) return attr -class SynoDSMInfoSensor(SynoDSMSensor, SensorEntity): +class SynoDSMInfoSensor(SynoDSMSensor): """Representation a Synology information sensor.""" def __init__( self, api: SynoApi, - entity_type: str, - entity_info: EntityInfo, coordinator: DataUpdateCoordinator[dict[str, dict[str, Any]]], + description: SynologyDSMSensorEntityDescription, ) -> None: """Initialize the Synology SynoDSMInfoSensor entity.""" - super().__init__(api, entity_type, entity_info, coordinator) + super().__init__(api, coordinator, description) self._previous_uptime: str | None = None self._last_boot: str | None = None @property def native_value(self) -> Any | None: """Return the state.""" - attr = getattr(self._api.information, self.entity_type) + attr = getattr(self._api.information, self.entity_description.key) if attr is None: return None - if self.entity_type == "uptime": + if self.entity_description.key == "uptime": # reboot happened or entity creation if self._previous_uptime is None or self._previous_uptime > attr: last_boot = utcnow() - timedelta(seconds=attr) diff --git a/homeassistant/components/synology_dsm/switch.py b/homeassistant/components/synology_dsm/switch.py index e08516ec03a..c5144d64a48 100644 --- a/homeassistant/components/synology_dsm/switch.py +++ b/homeassistant/components/synology_dsm/switch.py @@ -19,7 +19,7 @@ from .const import ( DOMAIN, SURVEILLANCE_SWITCH, SYNO_API, - EntityInfo, + SynologyDSMSwitchEntityDescription, ) _LOGGER = logging.getLogger(__name__) @@ -42,12 +42,14 @@ async def async_setup_entry( # initial data fetch coordinator: DataUpdateCoordinator = data[COORDINATOR_SWITCHES] await coordinator.async_refresh() - entities += [ - SynoDSMSurveillanceHomeModeToggle( - api, sensor_type, switch, version, coordinator - ) - for sensor_type, switch in SURVEILLANCE_SWITCH.items() - ] + entities.extend( + [ + SynoDSMSurveillanceHomeModeToggle( + api, version, coordinator, description + ) + for description in SURVEILLANCE_SWITCH + ] + ) async_add_entities(entities, True) @@ -56,28 +58,23 @@ class SynoDSMSurveillanceHomeModeToggle(SynologyDSMBaseEntity, ToggleEntity): """Representation a Synology Surveillance Station Home Mode toggle.""" coordinator: DataUpdateCoordinator[dict[str, dict[str, bool]]] + entity_description: SynologyDSMSwitchEntityDescription def __init__( self, api: SynoApi, - entity_type: str, - entity_info: EntityInfo, version: str, coordinator: DataUpdateCoordinator[dict[str, dict[str, bool]]], + description: SynologyDSMSwitchEntityDescription, ) -> None: """Initialize a Synology Surveillance Station Home Mode.""" - super().__init__( - api, - entity_type, - entity_info, - coordinator, - ) + super().__init__(api, coordinator, description) self._version = version @property def is_on(self) -> bool: """Return the state.""" - return self.coordinator.data["switches"][self.entity_type] + return self.coordinator.data["switches"][self.entity_description.key] async def async_turn_on(self, **kwargs: Any) -> None: """Turn on Home mode."""