Home Assistant Unofficial Reference 2024.12.1
sensor.py
Go to the documentation of this file.
1 """Support for Synology DSM sensors."""
2 
3 from __future__ import annotations
4 
5 from dataclasses import dataclass
6 from datetime import datetime, timedelta
7 from typing import cast
8 
9 from synology_dsm.api.core.utilization import SynoCoreUtilization
10 from synology_dsm.api.dsm.information import SynoDSMInformation
11 from synology_dsm.api.storage.storage import SynoStorage
12 
14  SensorDeviceClass,
15  SensorEntity,
16  SensorEntityDescription,
17  SensorStateClass,
18 )
19 from homeassistant.config_entries import ConfigEntry
20 from homeassistant.const import (
21  CONF_DISKS,
22  PERCENTAGE,
23  EntityCategory,
24  UnitOfDataRate,
25  UnitOfInformation,
26  UnitOfTemperature,
27 )
28 from homeassistant.core import HomeAssistant
29 from homeassistant.helpers.entity_platform import AddEntitiesCallback
30 from homeassistant.helpers.typing import StateType
31 from homeassistant.util.dt import utcnow
32 
33 from . import SynoApi
34 from .const import CONF_VOLUMES, DOMAIN, ENTITY_UNIT_LOAD
35 from .coordinator import SynologyDSMCentralUpdateCoordinator
36 from .entity import (
37  SynologyDSMBaseEntity,
38  SynologyDSMDeviceEntity,
39  SynologyDSMEntityDescription,
40 )
41 from .models import SynologyDSMData
42 
43 
44 @dataclass(frozen=True, kw_only=True)
46  SensorEntityDescription, SynologyDSMEntityDescription
47 ):
48  """Describes Synology DSM sensor entity."""
49 
50 
51 UTILISATION_SENSORS: tuple[SynologyDSMSensorEntityDescription, ...] = (
53  api_key=SynoCoreUtilization.API_KEY,
54  key="cpu_other_load",
55  translation_key="cpu_other_load",
56  native_unit_of_measurement=PERCENTAGE,
57  entity_registry_enabled_default=False,
58  state_class=SensorStateClass.MEASUREMENT,
59  ),
61  api_key=SynoCoreUtilization.API_KEY,
62  key="cpu_user_load",
63  translation_key="cpu_user_load",
64  native_unit_of_measurement=PERCENTAGE,
65  state_class=SensorStateClass.MEASUREMENT,
66  ),
68  api_key=SynoCoreUtilization.API_KEY,
69  key="cpu_system_load",
70  translation_key="cpu_system_load",
71  native_unit_of_measurement=PERCENTAGE,
72  entity_registry_enabled_default=False,
73  state_class=SensorStateClass.MEASUREMENT,
74  ),
76  api_key=SynoCoreUtilization.API_KEY,
77  key="cpu_total_load",
78  translation_key="cpu_total_load",
79  native_unit_of_measurement=PERCENTAGE,
80  state_class=SensorStateClass.MEASUREMENT,
81  ),
83  api_key=SynoCoreUtilization.API_KEY,
84  key="cpu_1min_load",
85  translation_key="cpu_1min_load",
86  native_unit_of_measurement=ENTITY_UNIT_LOAD,
87  entity_registry_enabled_default=False,
88  ),
90  api_key=SynoCoreUtilization.API_KEY,
91  key="cpu_5min_load",
92  translation_key="cpu_5min_load",
93  native_unit_of_measurement=ENTITY_UNIT_LOAD,
94  ),
96  api_key=SynoCoreUtilization.API_KEY,
97  key="cpu_15min_load",
98  translation_key="cpu_15min_load",
99  native_unit_of_measurement=ENTITY_UNIT_LOAD,
100  ),
102  api_key=SynoCoreUtilization.API_KEY,
103  key="memory_real_usage",
104  translation_key="memory_real_usage",
105  native_unit_of_measurement=PERCENTAGE,
106  state_class=SensorStateClass.MEASUREMENT,
107  ),
109  api_key=SynoCoreUtilization.API_KEY,
110  key="memory_size",
111  translation_key="memory_size",
112  native_unit_of_measurement=UnitOfInformation.BYTES,
113  suggested_unit_of_measurement=UnitOfInformation.MEGABYTES,
114  suggested_display_precision=1,
115  device_class=SensorDeviceClass.DATA_SIZE,
116  entity_registry_enabled_default=False,
117  state_class=SensorStateClass.MEASUREMENT,
118  ),
120  api_key=SynoCoreUtilization.API_KEY,
121  key="memory_cached",
122  translation_key="memory_cached",
123  native_unit_of_measurement=UnitOfInformation.BYTES,
124  suggested_unit_of_measurement=UnitOfInformation.MEGABYTES,
125  suggested_display_precision=1,
126  device_class=SensorDeviceClass.DATA_SIZE,
127  entity_registry_enabled_default=False,
128  state_class=SensorStateClass.MEASUREMENT,
129  ),
131  api_key=SynoCoreUtilization.API_KEY,
132  key="memory_available_swap",
133  translation_key="memory_available_swap",
134  native_unit_of_measurement=UnitOfInformation.BYTES,
135  suggested_unit_of_measurement=UnitOfInformation.MEGABYTES,
136  suggested_display_precision=1,
137  device_class=SensorDeviceClass.DATA_SIZE,
138  state_class=SensorStateClass.MEASUREMENT,
139  ),
141  api_key=SynoCoreUtilization.API_KEY,
142  key="memory_available_real",
143  translation_key="memory_available_real",
144  native_unit_of_measurement=UnitOfInformation.BYTES,
145  suggested_unit_of_measurement=UnitOfInformation.MEGABYTES,
146  suggested_display_precision=1,
147  device_class=SensorDeviceClass.DATA_SIZE,
148  state_class=SensorStateClass.MEASUREMENT,
149  ),
151  api_key=SynoCoreUtilization.API_KEY,
152  key="memory_total_swap",
153  translation_key="memory_total_swap",
154  native_unit_of_measurement=UnitOfInformation.BYTES,
155  suggested_unit_of_measurement=UnitOfInformation.MEGABYTES,
156  suggested_display_precision=1,
157  device_class=SensorDeviceClass.DATA_SIZE,
158  state_class=SensorStateClass.MEASUREMENT,
159  ),
161  api_key=SynoCoreUtilization.API_KEY,
162  key="memory_total_real",
163  translation_key="memory_total_real",
164  native_unit_of_measurement=UnitOfInformation.BYTES,
165  suggested_unit_of_measurement=UnitOfInformation.MEGABYTES,
166  suggested_display_precision=1,
167  device_class=SensorDeviceClass.DATA_SIZE,
168  state_class=SensorStateClass.MEASUREMENT,
169  ),
171  api_key=SynoCoreUtilization.API_KEY,
172  key="network_up",
173  translation_key="network_up",
174  native_unit_of_measurement=UnitOfDataRate.BYTES_PER_SECOND,
175  suggested_unit_of_measurement=UnitOfDataRate.KILOBYTES_PER_SECOND,
176  suggested_display_precision=1,
177  device_class=SensorDeviceClass.DATA_RATE,
178  state_class=SensorStateClass.MEASUREMENT,
179  ),
181  api_key=SynoCoreUtilization.API_KEY,
182  key="network_down",
183  translation_key="network_down",
184  native_unit_of_measurement=UnitOfDataRate.BYTES_PER_SECOND,
185  suggested_unit_of_measurement=UnitOfDataRate.KILOBYTES_PER_SECOND,
186  suggested_display_precision=1,
187  device_class=SensorDeviceClass.DATA_RATE,
188  state_class=SensorStateClass.MEASUREMENT,
189  ),
190 )
191 STORAGE_VOL_SENSORS: tuple[SynologyDSMSensorEntityDescription, ...] = (
193  api_key=SynoStorage.API_KEY,
194  key="volume_status",
195  translation_key="volume_status",
196  ),
198  api_key=SynoStorage.API_KEY,
199  key="volume_size_total",
200  translation_key="volume_size_total",
201  native_unit_of_measurement=UnitOfInformation.BYTES,
202  suggested_unit_of_measurement=UnitOfInformation.TERABYTES,
203  suggested_display_precision=2,
204  device_class=SensorDeviceClass.DATA_SIZE,
205  entity_registry_enabled_default=False,
206  state_class=SensorStateClass.MEASUREMENT,
207  ),
209  api_key=SynoStorage.API_KEY,
210  key="volume_size_used",
211  translation_key="volume_size_used",
212  native_unit_of_measurement=UnitOfInformation.BYTES,
213  suggested_unit_of_measurement=UnitOfInformation.TERABYTES,
214  suggested_display_precision=2,
215  device_class=SensorDeviceClass.DATA_SIZE,
216  state_class=SensorStateClass.MEASUREMENT,
217  ),
219  api_key=SynoStorage.API_KEY,
220  key="volume_percentage_used",
221  translation_key="volume_percentage_used",
222  native_unit_of_measurement=PERCENTAGE,
223  ),
225  api_key=SynoStorage.API_KEY,
226  key="volume_disk_temp_avg",
227  translation_key="volume_disk_temp_avg",
228  native_unit_of_measurement=UnitOfTemperature.CELSIUS,
229  device_class=SensorDeviceClass.TEMPERATURE,
230  entity_category=EntityCategory.DIAGNOSTIC,
231  ),
233  api_key=SynoStorage.API_KEY,
234  key="volume_disk_temp_max",
235  translation_key="volume_disk_temp_max",
236  native_unit_of_measurement=UnitOfTemperature.CELSIUS,
237  device_class=SensorDeviceClass.TEMPERATURE,
238  entity_registry_enabled_default=False,
239  entity_category=EntityCategory.DIAGNOSTIC,
240  ),
241 )
242 STORAGE_DISK_SENSORS: tuple[SynologyDSMSensorEntityDescription, ...] = (
244  api_key=SynoStorage.API_KEY,
245  key="disk_smart_status",
246  translation_key="disk_smart_status",
247  entity_registry_enabled_default=False,
248  entity_category=EntityCategory.DIAGNOSTIC,
249  ),
251  api_key=SynoStorage.API_KEY,
252  key="disk_status",
253  translation_key="disk_status",
254  entity_category=EntityCategory.DIAGNOSTIC,
255  ),
257  api_key=SynoStorage.API_KEY,
258  key="disk_temp",
259  translation_key="disk_temp",
260  native_unit_of_measurement=UnitOfTemperature.CELSIUS,
261  device_class=SensorDeviceClass.TEMPERATURE,
262  state_class=SensorStateClass.MEASUREMENT,
263  entity_category=EntityCategory.DIAGNOSTIC,
264  ),
265 )
266 
267 INFORMATION_SENSORS: tuple[SynologyDSMSensorEntityDescription, ...] = (
269  api_key=SynoDSMInformation.API_KEY,
270  key="temperature",
271  translation_key="temperature",
272  native_unit_of_measurement=UnitOfTemperature.CELSIUS,
273  device_class=SensorDeviceClass.TEMPERATURE,
274  state_class=SensorStateClass.MEASUREMENT,
275  entity_category=EntityCategory.DIAGNOSTIC,
276  ),
278  api_key=SynoDSMInformation.API_KEY,
279  key="uptime",
280  translation_key="uptime",
281  device_class=SensorDeviceClass.TIMESTAMP,
282  entity_registry_enabled_default=False,
283  entity_category=EntityCategory.DIAGNOSTIC,
284  ),
285 )
286 
287 
289  hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback
290 ) -> None:
291  """Set up the Synology NAS Sensor."""
292  data: SynologyDSMData = hass.data[DOMAIN][entry.unique_id]
293  api = data.api
294  coordinator = data.coordinator_central
295  storage = api.storage
296  assert storage is not None
297 
298  entities: list[SynoDSMUtilSensor | SynoDSMStorageSensor | SynoDSMInfoSensor] = [
299  SynoDSMUtilSensor(api, coordinator, description)
300  for description in UTILISATION_SENSORS
301  ]
302 
303  # Handle all volumes
304  if storage.volumes_ids:
305  entities.extend(
306  [
307  SynoDSMStorageSensor(api, coordinator, description, volume)
308  for volume in entry.data.get(CONF_VOLUMES, storage.volumes_ids)
309  for description in STORAGE_VOL_SENSORS
310  ]
311  )
312 
313  # Handle all disks
314  if storage.disks_ids:
315  entities.extend(
316  [
317  SynoDSMStorageSensor(api, coordinator, description, disk)
318  for disk in entry.data.get(CONF_DISKS, storage.disks_ids)
319  for description in STORAGE_DISK_SENSORS
320  ]
321  )
322 
323  entities.extend(
324  [
325  SynoDSMInfoSensor(api, coordinator, description)
326  for description in INFORMATION_SENSORS
327  ]
328  )
329 
330  async_add_entities(entities)
331 
332 
334  SynologyDSMBaseEntity[SynologyDSMCentralUpdateCoordinator], SensorEntity
335 ):
336  """Mixin for sensor specific attributes."""
337 
338  entity_description: SynologyDSMSensorEntityDescription
339 
340  def __init__(
341  self,
342  api: SynoApi,
343  coordinator: SynologyDSMCentralUpdateCoordinator,
344  description: SynologyDSMSensorEntityDescription,
345  ) -> None:
346  """Initialize the Synology DSM sensor entity."""
347  super().__init__(api, coordinator, description)
348 
349 
351  """Representation a Synology Utilisation sensor."""
352 
353  @property
354  def native_value(self) -> StateType:
355  """Return the state."""
356  attr = getattr(self._api.utilisation, self.entity_description.key)
357  if callable(attr):
358  attr = attr()
359 
360  # CPU load average
361  if (
362  isinstance(attr, int)
363  and self.native_unit_of_measurementnative_unit_of_measurementnative_unit_of_measurement == ENTITY_UNIT_LOAD
364  ):
365  return round(attr / 100, 2)
366 
367  return attr # type: ignore[no-any-return]
368 
369  @property
370  def available(self) -> bool:
371  """Return True if entity is available."""
372  return bool(self._api.utilisation) and super().available
373 
374 
376  """Representation a Synology Storage sensor."""
377 
378  entity_description: SynologyDSMSensorEntityDescription
379 
380  def __init__(
381  self,
382  api: SynoApi,
383  coordinator: SynologyDSMCentralUpdateCoordinator,
384  description: SynologyDSMSensorEntityDescription,
385  device_id: str | None = None,
386  ) -> None:
387  """Initialize the Synology DSM storage sensor entity."""
388  super().__init__(api, coordinator, description, device_id)
389 
390  @property
391  def native_value(self) -> StateType:
392  """Return the state."""
393  return cast(
394  StateType,
395  getattr(self._api.storage, self.entity_description.key)(self._device_id_device_id),
396  )
397 
398 
400  """Representation a Synology information sensor."""
401 
402  def __init__(
403  self,
404  api: SynoApi,
405  coordinator: SynologyDSMCentralUpdateCoordinator,
406  description: SynologyDSMSensorEntityDescription,
407  ) -> None:
408  """Initialize the Synology SynoDSMInfoSensor entity."""
409  super().__init__(api, coordinator, description)
410  self._previous_uptime_previous_uptime: str | None = None
411  self._last_boot_last_boot: datetime | None = None
412 
413  @property
414  def native_value(self) -> StateType | datetime:
415  """Return the state."""
416  attr = getattr(self._api.information, self.entity_description.key)
417  if attr is None:
418  return None
419 
420  if self.entity_description.key == "uptime":
421  # reboot happened or entity creation
422  if self._previous_uptime_previous_uptime is None or self._previous_uptime_previous_uptime > attr:
423  self._last_boot_last_boot = utcnow() - timedelta(seconds=attr)
424 
425  self._previous_uptime_previous_uptime = attr
426  return self._last_boot_last_boot
427  return attr # type: ignore[no-any-return]
None __init__(self, SynoApi api, SynologyDSMCentralUpdateCoordinator coordinator, SynologyDSMSensorEntityDescription description)
Definition: sensor.py:407
None __init__(self, SynoApi api, SynologyDSMCentralUpdateCoordinator coordinator, SynologyDSMSensorEntityDescription description)
Definition: sensor.py:345
None __init__(self, SynoApi api, SynologyDSMCentralUpdateCoordinator coordinator, SynologyDSMSensorEntityDescription description, str|None device_id=None)
Definition: sensor.py:386
None async_setup_entry(HomeAssistant hass, ConfigEntry entry, AddEntitiesCallback async_add_entities)
Definition: sensor.py:290