Home Assistant Unofficial Reference 2024.12.1
sensor.py
Go to the documentation of this file.
1 """Sensor for myUplink."""
2 
3 from myuplink import DevicePoint
4 
6  SensorDeviceClass,
7  SensorEntity,
8  SensorEntityDescription,
9  SensorStateClass,
10 )
11 from homeassistant.const import (
12  REVOLUTIONS_PER_MINUTE,
13  Platform,
14  UnitOfElectricCurrent,
15  UnitOfEnergy,
16  UnitOfFrequency,
17  UnitOfPower,
18  UnitOfPressure,
19  UnitOfTemperature,
20  UnitOfTime,
21  UnitOfVolumeFlowRate,
22 )
23 from homeassistant.core import HomeAssistant
24 from homeassistant.helpers.entity_platform import AddEntitiesCallback
25 from homeassistant.helpers.typing import StateType
26 
27 from . import MyUplinkConfigEntry, MyUplinkDataCoordinator
28 from .const import F_SERIES
29 from .entity import MyUplinkEntity
30 from .helpers import find_matching_platform, skip_entity, transform_model_series
31 
32 DEVICE_POINT_UNIT_DESCRIPTIONS: dict[str, SensorEntityDescription] = {
34  key="celsius",
35  device_class=SensorDeviceClass.TEMPERATURE,
36  state_class=SensorStateClass.MEASUREMENT,
37  native_unit_of_measurement=UnitOfTemperature.CELSIUS,
38  ),
40  key="fahrenheit",
41  device_class=SensorDeviceClass.TEMPERATURE,
42  state_class=SensorStateClass.MEASUREMENT,
43  native_unit_of_measurement=UnitOfTemperature.FAHRENHEIT,
44  ),
46  key="ampere",
47  device_class=SensorDeviceClass.CURRENT,
48  state_class=SensorStateClass.MEASUREMENT,
49  native_unit_of_measurement=UnitOfElectricCurrent.AMPERE,
50  ),
52  key="pressure",
53  device_class=SensorDeviceClass.PRESSURE,
54  state_class=SensorStateClass.MEASUREMENT,
55  native_unit_of_measurement=UnitOfPressure.BAR,
56  ),
58  key="days",
59  device_class=SensorDeviceClass.DURATION,
60  state_class=SensorStateClass.MEASUREMENT,
61  native_unit_of_measurement=UnitOfTime.DAYS,
62  suggested_display_precision=0,
63  ),
65  key="hours",
66  device_class=SensorDeviceClass.DURATION,
67  state_class=SensorStateClass.MEASUREMENT,
68  native_unit_of_measurement=UnitOfTime.HOURS,
69  suggested_display_precision=1,
70  ),
72  key="hours_hrs",
73  device_class=SensorDeviceClass.DURATION,
74  state_class=SensorStateClass.MEASUREMENT,
75  native_unit_of_measurement=UnitOfTime.HOURS,
76  suggested_display_precision=1,
77  ),
79  key="hertz",
80  device_class=SensorDeviceClass.FREQUENCY,
81  state_class=SensorStateClass.MEASUREMENT,
82  native_unit_of_measurement=UnitOfFrequency.HERTZ,
83  ),
85  key="power",
86  device_class=SensorDeviceClass.POWER,
87  state_class=SensorStateClass.MEASUREMENT,
88  native_unit_of_measurement=UnitOfPower.KILO_WATT,
89  ),
91  key="energy",
92  device_class=SensorDeviceClass.ENERGY,
93  state_class=SensorStateClass.TOTAL_INCREASING,
94  native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR,
95  ),
97  key="airflow",
98  translation_key="airflow",
99  device_class=SensorDeviceClass.VOLUME_FLOW_RATE,
100  state_class=SensorStateClass.MEASUREMENT,
101  native_unit_of_measurement=UnitOfVolumeFlowRate.CUBIC_METERS_PER_HOUR,
102  ),
104  key="minutes",
105  device_class=SensorDeviceClass.DURATION,
106  state_class=SensorStateClass.MEASUREMENT,
107  native_unit_of_measurement=UnitOfTime.MINUTES,
108  suggested_display_precision=0,
109  ),
111  key="pressure_pa",
112  device_class=SensorDeviceClass.PRESSURE,
113  state_class=SensorStateClass.MEASUREMENT,
114  native_unit_of_measurement=UnitOfPressure.PA,
115  suggested_display_precision=0,
116  ),
118  key="rpm",
119  translation_key="rpm",
120  state_class=SensorStateClass.MEASUREMENT,
121  native_unit_of_measurement=REVOLUTIONS_PER_MINUTE,
122  suggested_display_precision=0,
123  ),
125  key="seconds",
126  device_class=SensorDeviceClass.DURATION,
127  state_class=SensorStateClass.MEASUREMENT,
128  native_unit_of_measurement=UnitOfTime.SECONDS,
129  suggested_display_precision=0,
130  ),
132  key="seconds_sec",
133  device_class=SensorDeviceClass.DURATION,
134  state_class=SensorStateClass.MEASUREMENT,
135  native_unit_of_measurement=UnitOfTime.SECONDS,
136  suggested_display_precision=0,
137  ),
138 }
139 
140 MARKER_FOR_UNKNOWN_VALUE = -32768
141 
142 CATEGORY_BASED_DESCRIPTIONS: dict[str, dict[str, SensorEntityDescription]] = {
143  F_SERIES: {
144  "43108": SensorEntityDescription(
145  key="fan_mode",
146  translation_key="fan_mode",
147  ),
148  "43427": SensorEntityDescription(
149  key="status_compressor",
150  translation_key="status_compressor",
151  device_class=SensorDeviceClass.ENUM,
152  ),
153  "49993": SensorEntityDescription(
154  key="elect_add",
155  translation_key="elect_add",
156  device_class=SensorDeviceClass.ENUM,
157  ),
158  "49994": SensorEntityDescription(
159  key="priority",
160  translation_key="priority",
161  device_class=SensorDeviceClass.ENUM,
162  ),
163  "50095": SensorEntityDescription(
164  key="status",
165  translation_key="status",
166  device_class=SensorDeviceClass.ENUM,
167  ),
168  },
169  "NIBEF": {
170  "43108": SensorEntityDescription(
171  key="fan_mode",
172  translation_key="fan_mode",
173  ),
174  "43427": SensorEntityDescription(
175  key="status_compressor",
176  translation_key="status_compressor",
177  device_class=SensorDeviceClass.ENUM,
178  ),
179  "49993": SensorEntityDescription(
180  key="elect_add",
181  translation_key="elect_add",
182  device_class=SensorDeviceClass.ENUM,
183  ),
184  "49994": SensorEntityDescription(
185  key="priority",
186  translation_key="priority",
187  device_class=SensorDeviceClass.ENUM,
188  ),
189  },
190  "NIBE": {},
191 }
192 
193 
194 def get_description(device_point: DevicePoint) -> SensorEntityDescription | None:
195  """Get description for a device point.
196 
197  Priorities:
198  1. Category specific prefix e.g "NIBEF"
199  2. Global parameter_unit e.g. "°C"
200  3. Default to None
201  """
202  description = None
203  prefix, _, _ = device_point.category.partition(" ")
204  prefix = transform_model_series(prefix)
205  description = CATEGORY_BASED_DESCRIPTIONS.get(prefix, {}).get(
206  device_point.parameter_id
207  )
208  if description is None:
209  description = DEVICE_POINT_UNIT_DESCRIPTIONS.get(device_point.parameter_unit)
210 
211  return description
212 
213 
215  hass: HomeAssistant,
216  config_entry: MyUplinkConfigEntry,
217  async_add_entities: AddEntitiesCallback,
218 ) -> None:
219  """Set up myUplink sensor."""
220 
221  entities: list[SensorEntity] = []
222  coordinator = config_entry.runtime_data
223 
224  # Setup device point sensors
225  for device_id, point_data in coordinator.data.points.items():
226  for point_id, device_point in point_data.items():
227  if skip_entity(device_point.category, device_point):
228  continue
229  if find_matching_platform(device_point) == Platform.SENSOR:
230  description = get_description(device_point)
231  entity_class = MyUplinkDevicePointSensor
232  # Ignore sensors without a description that provide non-numeric values
233  if description is None and not isinstance(
234  device_point.value, (int, float)
235  ):
236  continue
237  if (
238  description is not None
239  and description.device_class == SensorDeviceClass.ENUM
240  ):
241  entities.append(
243  coordinator=coordinator,
244  device_id=device_id,
245  device_point=device_point,
246  entity_description=description,
247  unique_id_suffix=f"{point_id}-raw",
248  )
249  )
250  entity_class = MyUplinkEnumSensor
251 
252  entities.append(
253  entity_class(
254  coordinator=coordinator,
255  device_id=device_id,
256  device_point=device_point,
257  entity_description=description,
258  unique_id_suffix=point_id,
259  )
260  )
261 
262  async_add_entities(entities)
263 
264 
266  """Representation of a myUplink device point sensor."""
267 
268  def __init__(
269  self,
270  coordinator: MyUplinkDataCoordinator,
271  device_id: str,
272  device_point: DevicePoint,
273  entity_description: SensorEntityDescription | None,
274  unique_id_suffix: str,
275  ) -> None:
276  """Initialize the sensor."""
277  super().__init__(
278  coordinator=coordinator,
279  device_id=device_id,
280  unique_id_suffix=unique_id_suffix,
281  )
282 
283  # Internal properties
284  self.point_idpoint_id = device_point.parameter_id
285  # Remove soft hyphens
286  self._attr_name_attr_name = device_point.parameter_name.replace("\u00ad", "")
287 
288  if entity_description is not None:
289  self.entity_descriptionentity_description = entity_description
290  else:
291  self._attr_native_unit_of_measurement_attr_native_unit_of_measurement = device_point.parameter_unit
292 
293  @property
294  def native_value(self) -> StateType:
295  """Sensor state value."""
296  device_point = self.coordinator.data.points[self.device_iddevice_id][self.point_idpoint_id]
297  if device_point.value == MARKER_FOR_UNKNOWN_VALUE:
298  return None
299  return device_point.value # type: ignore[no-any-return]
300 
301 
303  """Representation of a myUplink device point sensor for ENUM device_class."""
304 
305  def __init__(
306  self,
307  coordinator: MyUplinkDataCoordinator,
308  device_id: str,
309  device_point: DevicePoint,
310  entity_description: SensorEntityDescription | None,
311  unique_id_suffix: str,
312  ) -> None:
313  """Initialize the sensor."""
314  super().__init__(
315  coordinator=coordinator,
316  device_id=device_id,
317  device_point=device_point,
318  entity_description=entity_description,
319  unique_id_suffix=unique_id_suffix,
320  )
321 
322  self._attr_options_attr_options = [x["text"].capitalize() for x in device_point.enum_values]
323  self.options_mapoptions_map = {
324  x["value"]: x["text"].capitalize() for x in device_point.enum_values
325  }
326 
327  @property
328  def native_value(self) -> str:
329  """Sensor state value for enum sensor."""
330  device_point = self.coordinator.data.points[self.device_iddevice_id][self.point_idpoint_id]
331  return self.options_mapoptions_map[str(int(device_point.value))] # type: ignore[no-any-return]
332 
333 
335  """Representation of a myUplink device point sensor for raw value from ENUM device_class."""
336 
337  _attr_entity_registry_enabled_default = False
338  _attr_device_class = None
339 
340  def __init__(
341  self,
342  coordinator: MyUplinkDataCoordinator,
343  device_id: str,
344  device_point: DevicePoint,
345  entity_description: SensorEntityDescription | None,
346  unique_id_suffix: str,
347  ) -> None:
348  """Initialize the sensor."""
349  super().__init__(
350  coordinator=coordinator,
351  device_id=device_id,
352  device_point=device_point,
353  entity_description=entity_description,
354  unique_id_suffix=unique_id_suffix,
355  )
356 
357  self._attr_name_attr_name_attr_name = f"{device_point.parameter_name} raw"
web.Response get(self, web.Request request, str config_key)
Definition: view.py:88