Home Assistant Unofficial Reference 2024.12.1
sensor.py
Go to the documentation of this file.
1 """Support for Sonarr sensors."""
2 
3 from __future__ import annotations
4 
5 from collections.abc import Callable
6 from dataclasses import dataclass
7 from typing import Any, Generic
8 
9 from aiopyarr import (
10  Command,
11  Diskspace,
12  SonarrCalendar,
13  SonarrQueue,
14  SonarrSeries,
15  SonarrWantedMissing,
16 )
17 
19  SensorDeviceClass,
20  SensorEntity,
21  SensorEntityDescription,
22 )
23 from homeassistant.config_entries import ConfigEntry
24 from homeassistant.const import UnitOfInformation
25 from homeassistant.core import HomeAssistant
26 from homeassistant.helpers.entity_platform import AddEntitiesCallback
27 from homeassistant.helpers.typing import StateType
28 import homeassistant.util.dt as dt_util
29 
30 from .const import DOMAIN
31 from .coordinator import SonarrDataT, SonarrDataUpdateCoordinator
32 from .entity import SonarrEntity
33 
34 
35 @dataclass(frozen=True)
36 class SonarrSensorEntityDescriptionMixIn(Generic[SonarrDataT]):
37  """Mixin for Sonarr sensor."""
38 
39  attributes_fn: Callable[[SonarrDataT], dict[str, str]]
40  value_fn: Callable[[SonarrDataT], StateType]
41 
42 
43 @dataclass(frozen=True)
45  SensorEntityDescription, SonarrSensorEntityDescriptionMixIn[SonarrDataT]
46 ):
47  """Class to describe a Sonarr sensor."""
48 
49 
50 def get_disk_space_attr(disks: list[Diskspace]) -> dict[str, str]:
51  """Create the attributes for disk space."""
52  attrs: dict[str, str] = {}
53  for disk in disks:
54  free = disk.freeSpace / 1024**3
55  total = disk.totalSpace / 1024**3
56  usage = free / total * 100
57  attrs[disk.path] = (
58  f"{free:.2f}/{total:.2f}{UnitOfInformation.GIGABYTES} ({usage:.2f}%)"
59  )
60  return attrs
61 
62 
63 def get_queue_attr(queue: SonarrQueue) -> dict[str, str]:
64  """Create the attributes for series queue."""
65  attrs: dict[str, str] = {}
66  for item in queue.records:
67  remaining = 1 if item.size == 0 else item.sizeleft / item.size
68  remaining_pct = 100 * (1 - remaining)
69  identifier = (
70  f"S{item.episode.seasonNumber:02d}E{item.episode. episodeNumber:02d}"
71  )
72  attrs[f"{item.series.title} {identifier}"] = f"{remaining_pct:.2f}%"
73  return attrs
74 
75 
76 def get_wanted_attr(wanted: SonarrWantedMissing) -> dict[str, str]:
77  """Create the attributes for missing series."""
78  attrs: dict[str, str] = {}
79  for item in wanted.records:
80  identifier = f"S{item.seasonNumber:02d}E{item.episodeNumber:02d}"
81 
82  name = f"{item.series.title} {identifier}"
83  attrs[name] = dt_util.as_local(
84  item.airDateUtc.replace(tzinfo=dt_util.UTC)
85  ).isoformat()
86  return attrs
87 
88 
89 SENSOR_TYPES: dict[str, SonarrSensorEntityDescription[Any]] = {
90  "commands": SonarrSensorEntityDescription[list[Command]](
91  key="commands",
92  translation_key="commands",
93  native_unit_of_measurement="Commands",
94  entity_registry_enabled_default=False,
95  value_fn=len,
96  attributes_fn=lambda data: {c.name: c.status for c in data},
97  ),
98  "diskspace": SonarrSensorEntityDescription[list[Diskspace]](
99  key="diskspace",
100  translation_key="diskspace",
101  native_unit_of_measurement=UnitOfInformation.GIGABYTES,
102  device_class=SensorDeviceClass.DATA_SIZE,
103  entity_registry_enabled_default=False,
104  value_fn=lambda data: f"{sum(disk.freeSpace for disk in data) / 1024**3:.2f}",
105  attributes_fn=get_disk_space_attr,
106  ),
107  "queue": SonarrSensorEntityDescription[SonarrQueue](
108  key="queue",
109  translation_key="queue",
110  native_unit_of_measurement="Episodes",
111  entity_registry_enabled_default=False,
112  value_fn=lambda data: data.totalRecords,
113  attributes_fn=get_queue_attr,
114  ),
115  "series": SonarrSensorEntityDescription[list[SonarrSeries]](
116  key="series",
117  translation_key="series",
118  native_unit_of_measurement="Series",
119  entity_registry_enabled_default=False,
120  value_fn=len,
121  attributes_fn=lambda data: {
122  i.title: (
123  f"{getattr(i.statistics,'episodeFileCount', 0)}/{getattr(i.statistics, 'episodeCount', 0)} Episodes"
124  )
125  for i in data
126  },
127  ),
128  "upcoming": SonarrSensorEntityDescription[list[SonarrCalendar]](
129  key="upcoming",
130  translation_key="upcoming",
131  native_unit_of_measurement="Episodes",
132  value_fn=len,
133  attributes_fn=lambda data: {
134  e.series.title: f"S{e.seasonNumber:02d}E{e.episodeNumber:02d}" for e in data
135  },
136  ),
137  "wanted": SonarrSensorEntityDescription[SonarrWantedMissing](
138  key="wanted",
139  translation_key="wanted",
140  native_unit_of_measurement="Episodes",
141  entity_registry_enabled_default=False,
142  value_fn=lambda data: data.totalRecords,
143  attributes_fn=get_wanted_attr,
144  ),
145 }
146 
147 
149  hass: HomeAssistant,
150  entry: ConfigEntry,
151  async_add_entities: AddEntitiesCallback,
152 ) -> None:
153  """Set up Sonarr sensors based on a config entry."""
154  coordinators: dict[str, SonarrDataUpdateCoordinator[Any]] = hass.data[DOMAIN][
155  entry.entry_id
156  ]
158  SonarrSensor(coordinators[coordinator_type], description)
159  for coordinator_type, description in SENSOR_TYPES.items()
160  )
161 
162 
163 class SonarrSensor(SonarrEntity[SonarrDataT], SensorEntity):
164  """Implementation of the Sonarr sensor."""
165 
166  coordinator: SonarrDataUpdateCoordinator[SonarrDataT]
167  entity_description: SonarrSensorEntityDescription[SonarrDataT]
168 
169  @property
170  def extra_state_attributes(self) -> dict[str, str]:
171  """Return the state attributes of the entity."""
172  return self.entity_descriptionentity_description.attributes_fn(self.coordinatorcoordinator.data)
173 
174  @property
175  def native_value(self) -> StateType:
176  """Return the state of the sensor."""
177  return self.entity_descriptionentity_description.value_fn(self.coordinatorcoordinator.data)
None async_setup_entry(HomeAssistant hass, ConfigEntry entry, AddEntitiesCallback async_add_entities)
Definition: sensor.py:152
dict[str, str] get_wanted_attr(SonarrWantedMissing wanted)
Definition: sensor.py:76
dict[str, str] get_disk_space_attr(list[Diskspace] disks)
Definition: sensor.py:50
dict[str, str] get_queue_attr(SonarrQueue queue)
Definition: sensor.py:63