diff --git a/homeassistant/components/synology_dsm/__init__.py b/homeassistant/components/synology_dsm/__init__.py index 39ce1547a2cedb..b8bc5e03e142f2 100644 --- a/homeassistant/components/synology_dsm/__init__.py +++ b/homeassistant/components/synology_dsm/__init__.py @@ -2,6 +2,7 @@ from datetime import timedelta from synology_dsm import SynologyDSM +from synology_dsm.api.core.security import SynoCoreSecurity from synology_dsm.api.core.utilization import SynoCoreUtilization from synology_dsm.api.dsm.information import SynoDSMInformation from synology_dsm.api.storage.storage import SynoStorage @@ -24,8 +25,10 @@ from homeassistant.helpers.typing import HomeAssistantType from .const import ( + CONF_SECURITY, CONF_VOLUMES, DEFAULT_SCAN_INTERVAL, + DEFAULT_SECURITY, DEFAULT_SSL, DOMAIN, SYNO_API, @@ -126,6 +129,7 @@ def __init__(self, hass: HomeAssistantType, entry: ConfigEntry): self.dsm: SynologyDSM = None self.information: SynoDSMInformation = None self.utilisation: SynoCoreUtilization = None + self.security: SynoCoreSecurity = None self.storage: SynoStorage = None self._unsub_dispatcher = None @@ -147,11 +151,11 @@ async def async_setup(self): ) await self._hass.async_add_executor_job(self._fetch_device_configuration) - await self.update() + await self.async_update() self._unsub_dispatcher = async_track_time_interval( self._hass, - self.update, + self.async_update, timedelta( minutes=self._entry.options.get( CONF_SCAN_INTERVAL, DEFAULT_SCAN_INTERVAL @@ -163,13 +167,15 @@ def _fetch_device_configuration(self): """Fetch initial device config.""" self.information = self.dsm.information self.utilisation = self.dsm.utilisation + if self._entry.options.get(CONF_SECURITY, DEFAULT_SECURITY): + self.security = self.dsm.security self.storage = self.dsm.storage async def async_unload(self): """Stop interacting with the NAS and prepare for removal from hass.""" self._unsub_dispatcher() - async def update(self, now=None): + async def async_update(self, now=None): """Update function for updating API information.""" await self._hass.async_add_executor_job(self.dsm.update) async_dispatcher_send(self._hass, self.signal_sensor_update) diff --git a/homeassistant/components/synology_dsm/config_flow.py b/homeassistant/components/synology_dsm/config_flow.py index a4d7d75e073692..23f4695ff85963 100644 --- a/homeassistant/components/synology_dsm/config_flow.py +++ b/homeassistant/components/synology_dsm/config_flow.py @@ -29,10 +29,12 @@ import homeassistant.helpers.config_validation as cv from .const import ( + CONF_SECURITY, CONF_VOLUMES, DEFAULT_PORT, DEFAULT_PORT_SSL, DEFAULT_SCAN_INTERVAL, + DEFAULT_SECURITY, DEFAULT_SSL, ) from .const import DOMAIN # pylint: disable=unused-import @@ -250,7 +252,13 @@ async def async_step_init(self, user_input=None): default=self.config_entry.options.get( CONF_SCAN_INTERVAL, DEFAULT_SCAN_INTERVAL ), - ): cv.positive_int + ): cv.positive_int, + vol.Optional( + CONF_SECURITY, + default=self.config_entry.options.get( + CONF_SECURITY, DEFAULT_SECURITY + ), + ): bool, } ) return self.async_show_form(step_id="init", data_schema=data_schema) diff --git a/homeassistant/components/synology_dsm/const.py b/homeassistant/components/synology_dsm/const.py index e0a166e908beec..ee0a29a2794232 100644 --- a/homeassistant/components/synology_dsm/const.py +++ b/homeassistant/components/synology_dsm/const.py @@ -14,11 +14,14 @@ UNDO_UPDATE_LISTENER = "undo_update_listener" # Configuration +CONF_SECURITY = "security" CONF_VOLUMES = "volumes" + DEFAULT_SSL = True DEFAULT_PORT = 5000 DEFAULT_PORT_SSL = 5001 # Options +DEFAULT_SECURITY = True DEFAULT_SCAN_INTERVAL = 15 # min UTILISATION_SENSORS = { @@ -58,5 +61,9 @@ "disk_temp": ["Temperature", None, "mdi:thermometer"], } +SECURITY_SENSORS = { + "status": ["Security status", None, "mdi:checkbox-marked-circle-outline"], +} + TEMP_SENSORS_KEYS = ["volume_disk_temp_avg", "volume_disk_temp_max", "disk_temp"] diff --git a/homeassistant/components/synology_dsm/sensor.py b/homeassistant/components/synology_dsm/sensor.py index e776f5e97e38eb..0f5e00ff4f4f10 100644 --- a/homeassistant/components/synology_dsm/sensor.py +++ b/homeassistant/components/synology_dsm/sensor.py @@ -20,6 +20,7 @@ BASE_NAME, CONF_VOLUMES, DOMAIN, + SECURITY_SENSORS, STORAGE_DISK_SENSORS, STORAGE_VOL_SENSORS, SYNO_API, @@ -42,6 +43,12 @@ async def async_setup_entry( for sensor_type in UTILISATION_SENSORS ] + if api.security: + sensors += [ + SynoNasSecuritySensor(api, sensor_type, SECURITY_SENSORS[sensor_type]) + for sensor_type in SECURITY_SENSORS + ] + # Handle all volumes if api.storage.volumes_ids: for volume in entry.data.get(CONF_VOLUMES, api.storage.volumes_ids): @@ -135,7 +142,7 @@ def should_poll(self) -> bool: async def async_update(self): """Only used by the generic entity update service.""" - await self._api.update() + await self._api.async_update() async def async_added_to_hass(self): """Register state update callback.""" @@ -208,3 +215,33 @@ def device_info(self) -> Dict[str, any]: "sw_version": self._api.information.version_string, "via_device": (DOMAIN, self._api.information.serial), } + + +class SynoNasSecuritySensor(SynoNasSensor): + """Representation a Synology Security sensor.""" + + @property + def state(self): + """Return the state.""" + return getattr(self._api.security, self.sensor_type) + + async def async_remove(self): + """Clean up when removing entity. + + Remove entity if no entry in entity registry exist. + Remove entity registry entry if no entry in device registry exist. + Remove entity registry entry if there are more than one entity linked to the device registry entry. + """ + entity_registry = await self.hass.helpers.entity_registry.async_get_registry() + entity_entry = entity_registry.async_get(self.entity_id) + if not entity_entry: + await super().async_remove() + return + + device_registry = await self.hass.helpers.device_registry.async_get_registry() + device_entry = device_registry.async_get(entity_entry.device_id) + if not device_entry: + entity_registry.async_remove(self.entity_id) + return + + entity_registry.async_remove(self.entity_id) diff --git a/homeassistant/components/synology_dsm/strings.json b/homeassistant/components/synology_dsm/strings.json index 7c81b1dae280b0..135a6fea6b03a4 100644 --- a/homeassistant/components/synology_dsm/strings.json +++ b/homeassistant/components/synology_dsm/strings.json @@ -42,7 +42,8 @@ "step": { "init": { "data": { - "scan_interval": "Minutes between scans" + "scan_interval": "Minutes between scans", + "security": "Add security sensors" } } } diff --git a/homeassistant/components/synology_dsm/translations/en.json b/homeassistant/components/synology_dsm/translations/en.json index 3a52b0dc7d47ff..8d1339af15a921 100644 --- a/homeassistant/components/synology_dsm/translations/en.json +++ b/homeassistant/components/synology_dsm/translations/en.json @@ -44,7 +44,8 @@ "step": { "init": { "data": { - "scan_interval": "Minutes between scans" + "scan_interval": "Minutes between scans", + "security": "Add security sensors" } } } diff --git a/tests/components/synology_dsm/test_config_flow.py b/tests/components/synology_dsm/test_config_flow.py index 15a1655fbc19f2..35c330879d3c76 100644 --- a/tests/components/synology_dsm/test_config_flow.py +++ b/tests/components/synology_dsm/test_config_flow.py @@ -14,10 +14,12 @@ from homeassistant.components import ssdp from homeassistant.components.synology_dsm.config_flow import CONF_OTP_CODE from homeassistant.components.synology_dsm.const import ( + CONF_SECURITY, CONF_VOLUMES, DEFAULT_PORT, DEFAULT_PORT_SSL, DEFAULT_SCAN_INTERVAL, + DEFAULT_SECURITY, DEFAULT_SSL, DOMAIN, ) @@ -425,11 +427,13 @@ async def test_options_flow(hass: HomeAssistantType, service: MagicMock): ) assert result["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY assert config_entry.options[CONF_SCAN_INTERVAL] == DEFAULT_SCAN_INTERVAL + assert config_entry.options[CONF_SECURITY] == DEFAULT_SECURITY # Manual result = await hass.config_entries.options.async_init(config_entry.entry_id) result = await hass.config_entries.options.async_configure( - result["flow_id"], user_input={CONF_SCAN_INTERVAL: 2}, + result["flow_id"], user_input={CONF_SCAN_INTERVAL: 2, CONF_SECURITY: False}, ) assert result["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY assert config_entry.options[CONF_SCAN_INTERVAL] == 2 + assert not config_entry.options[CONF_SECURITY]