Home Assistant Unofficial Reference 2024.12.1
sensor.py
Go to the documentation of this file.
1 """Support for Lidarr."""
2 
3 from __future__ import annotations
4 
5 from collections.abc import Callable
6 import dataclasses
7 from typing import Any, Generic
8 
9 from aiopyarr import LidarrQueue, LidarrQueueItem, LidarrRootFolder
10 
12  SensorDeviceClass,
13  SensorEntity,
14  SensorEntityDescription,
15  SensorStateClass,
16 )
17 from homeassistant.const import UnitOfInformation
18 from homeassistant.core import HomeAssistant
19 from homeassistant.helpers.entity_platform import AddEntitiesCallback
20 
21 from . import LidarrConfigEntry
22 from .const import BYTE_SIZES
23 from .coordinator import LidarrDataUpdateCoordinator, T
24 from .entity import LidarrEntity
25 
26 
27 def get_space(data: list[LidarrRootFolder], name: str) -> str:
28  """Get space."""
29  space: list[float] = []
30  for mount in data:
31  if name in mount.path:
32  mount.freeSpace = mount.freeSpace if mount.accessible else 0
33  space.append(
34  mount.freeSpace / 1024 ** BYTE_SIZES.index(UnitOfInformation.GIGABYTES)
35  )
36  return f"{space[0]:.2f}"
37 
38 
40  description: LidarrSensorEntityDescription[T], mount: LidarrRootFolder
41 ) -> tuple[LidarrSensorEntityDescription[T], str]:
42  """Return modified description and folder name."""
43  name = mount.path.rsplit("/")[-1].rsplit("\\")[-1]
44  desc = dataclasses.replace(
45  description,
46  key=f"{description.key}_{name}",
47  name=f"{description.name} {name}".capitalize(),
48  )
49  return desc, name
50 
51 
52 @dataclasses.dataclass(frozen=True)
54  """Mixin for required keys."""
55 
56  value_fn: Callable[[T, str], str | int]
57 
58 
59 @dataclasses.dataclass(frozen=True)
61  SensorEntityDescription, LidarrSensorEntityDescriptionMixIn[T], Generic[T]
62 ):
63  """Class to describe a Lidarr sensor."""
64 
65  attributes_fn: Callable[[T], dict[str, str] | None] = lambda _: None
66  description_fn: (
67  Callable[
68  [LidarrSensorEntityDescription[T], LidarrRootFolder],
69  tuple[LidarrSensorEntityDescription[T], str] | None,
70  ]
71  | None
72  ) = None
73 
74 
75 SENSOR_TYPES: dict[str, LidarrSensorEntityDescription[Any]] = {
76  "disk_space": LidarrSensorEntityDescription(
77  key="disk_space",
78  translation_key="disk_space",
79  native_unit_of_measurement=UnitOfInformation.GIGABYTES,
80  device_class=SensorDeviceClass.DATA_SIZE,
81  value_fn=get_space,
82  state_class=SensorStateClass.TOTAL,
83  description_fn=get_modified_description,
84  ),
85  "queue": LidarrSensorEntityDescription[LidarrQueue](
86  key="queue",
87  translation_key="queue",
88  native_unit_of_measurement="albums",
89  value_fn=lambda data, _: data.totalRecords,
90  state_class=SensorStateClass.TOTAL,
91  attributes_fn=lambda data: {i.title: queue_str(i) for i in data.records},
92  ),
93  "wanted": LidarrSensorEntityDescription[LidarrQueue](
94  key="wanted",
95  translation_key="wanted",
96  native_unit_of_measurement="albums",
97  value_fn=lambda data, _: data.totalRecords,
98  state_class=SensorStateClass.TOTAL,
99  entity_registry_enabled_default=False,
100  attributes_fn=lambda data: {
101  album.title: album.artist.artistName for album in data.records
102  },
103  ),
104  "albums": LidarrSensorEntityDescription[int](
105  key="albums",
106  translation_key="albums",
107  native_unit_of_measurement="albums",
108  value_fn=lambda data, _: data,
109  state_class=SensorStateClass.TOTAL,
110  entity_registry_enabled_default=False,
111  ),
112 }
113 
114 
116  hass: HomeAssistant,
117  entry: LidarrConfigEntry,
118  async_add_entities: AddEntitiesCallback,
119 ) -> None:
120  """Set up Lidarr sensors based on a config entry."""
121  entities: list[LidarrSensor[Any]] = []
122  for coordinator_type, description in SENSOR_TYPES.items():
123  coordinator = getattr(entry.runtime_data, coordinator_type)
124  if coordinator_type != "disk_space":
125  entities.append(LidarrSensor(coordinator, description))
126  else:
127  entities.extend(
128  LidarrSensor(coordinator, *get_modified_description(description, mount))
129  for mount in coordinator.data
130  if description.description_fn
131  )
132  async_add_entities(entities)
133 
134 
135 class LidarrSensor(LidarrEntity[T], SensorEntity):
136  """Implementation of the Lidarr sensor."""
137 
138  entity_description: LidarrSensorEntityDescription[T]
139 
140  def __init__(
141  self,
142  coordinator: LidarrDataUpdateCoordinator[T],
143  description: LidarrSensorEntityDescription[T],
144  folder_name: str = "",
145  ) -> None:
146  """Create Lidarr entity."""
147  super().__init__(coordinator, description)
148  self.folder_namefolder_name = folder_name
149 
150  @property
151  def extra_state_attributes(self) -> dict[str, str] | None:
152  """Return the state attributes of the sensor."""
153  return self.entity_descriptionentity_description.attributes_fn(self.coordinator.data)
154 
155  @property
156  def native_value(self) -> str | int:
157  """Return the state of the sensor."""
158  return self.entity_descriptionentity_description.value_fn(self.coordinator.data, self.folder_namefolder_name)
159 
160 
161 def queue_str(item: LidarrQueueItem) -> str:
162  """Return string description of queue item."""
163  if (
164  item.sizeleft > 0
165  and item.timeleft == "00:00:00"
166  or not hasattr(item, "trackedDownloadState")
167  ):
168  return "stopped"
169  return item.trackedDownloadState
None __init__(self, LidarrDataUpdateCoordinator[T] coordinator, LidarrSensorEntityDescription[T] description, str folder_name="")
Definition: sensor.py:145
dict[str, str]|None extra_state_attributes(self)
Definition: sensor.py:151
str queue_str(LidarrQueueItem item)
Definition: sensor.py:161
tuple[LidarrSensorEntityDescription[T], str] get_modified_description(LidarrSensorEntityDescription[T] description, LidarrRootFolder mount)
Definition: sensor.py:41
None async_setup_entry(HomeAssistant hass, LidarrConfigEntry entry, AddEntitiesCallback async_add_entities)
Definition: sensor.py:119
str get_space(list[LidarrRootFolder] data, str name)
Definition: sensor.py:27