Home Assistant Unofficial Reference 2024.12.1
sensor.py
Go to the documentation of this file.
1 """Support for 1-Wire environment sensors."""
2 
3 from __future__ import annotations
4 
5 from collections.abc import Callable, Mapping
6 import dataclasses
7 import logging
8 import os
9 from types import MappingProxyType
10 from typing import Any
11 
12 from pyownet import protocol
13 
15  SensorDeviceClass,
16  SensorEntity,
17  SensorEntityDescription,
18  SensorStateClass,
19 )
20 from homeassistant.const import (
21  LIGHT_LUX,
22  PERCENTAGE,
23  UnitOfElectricPotential,
24  UnitOfPressure,
25  UnitOfTemperature,
26 )
27 from homeassistant.core import HomeAssistant
28 from homeassistant.helpers.entity_platform import AddEntitiesCallback
29 from homeassistant.helpers.typing import StateType
30 
31 from . import OneWireConfigEntry
32 from .const import (
33  DEVICE_KEYS_0_3,
34  DEVICE_KEYS_A_B,
35  OPTION_ENTRY_DEVICE_OPTIONS,
36  OPTION_ENTRY_SENSOR_PRECISION,
37  PRECISION_MAPPING_FAMILY_28,
38  READ_MODE_FLOAT,
39  READ_MODE_INT,
40 )
41 from .entity import OneWireEntity, OneWireEntityDescription
42 from .onewirehub import OneWireHub
43 
44 
45 @dataclasses.dataclass(frozen=True)
47  """Class describing OneWire sensor entities."""
48 
49  override_key: Callable[[str, Mapping[str, Any]], str] | None = None
50 
51 
52 def _get_sensor_precision_family_28(device_id: str, options: Mapping[str, Any]) -> str:
53  """Get precision form config flow options."""
54  precision: str = (
55  options.get(OPTION_ENTRY_DEVICE_OPTIONS, {})
56  .get(device_id, {})
57  .get(OPTION_ENTRY_SENSOR_PRECISION, "temperature")
58  )
59  if precision in PRECISION_MAPPING_FAMILY_28:
60  return precision
61  _LOGGER.warning(
62  "Invalid sensor precision `%s` for device `%s`: reverting to default",
63  precision,
64  device_id,
65  )
66  return "temperature"
67 
68 
69 SIMPLE_TEMPERATURE_SENSOR_DESCRIPTION = OneWireSensorEntityDescription(
70  key="temperature",
71  device_class=SensorDeviceClass.TEMPERATURE,
72  native_unit_of_measurement=UnitOfTemperature.CELSIUS,
73  read_mode=READ_MODE_FLOAT,
74  state_class=SensorStateClass.MEASUREMENT,
75 )
76 
77 _LOGGER = logging.getLogger(__name__)
78 
79 
80 DEVICE_SENSORS: dict[str, tuple[OneWireSensorEntityDescription, ...]] = {
81  "10": (SIMPLE_TEMPERATURE_SENSOR_DESCRIPTION,),
82  "12": (
84  key="TAI8570/temperature",
85  device_class=SensorDeviceClass.TEMPERATURE,
86  entity_registry_enabled_default=False,
87  native_unit_of_measurement=UnitOfTemperature.CELSIUS,
88  read_mode=READ_MODE_FLOAT,
89  state_class=SensorStateClass.MEASUREMENT,
90  ),
92  key="TAI8570/pressure",
93  device_class=SensorDeviceClass.PRESSURE,
94  entity_registry_enabled_default=False,
95  native_unit_of_measurement=UnitOfPressure.MBAR,
96  read_mode=READ_MODE_FLOAT,
97  state_class=SensorStateClass.MEASUREMENT,
98  ),
99  ),
100  "22": (SIMPLE_TEMPERATURE_SENSOR_DESCRIPTION,),
101  "26": (
102  SIMPLE_TEMPERATURE_SENSOR_DESCRIPTION,
104  key="humidity",
105  device_class=SensorDeviceClass.HUMIDITY,
106  entity_registry_enabled_default=False,
107  native_unit_of_measurement=PERCENTAGE,
108  read_mode=READ_MODE_FLOAT,
109  state_class=SensorStateClass.MEASUREMENT,
110  ),
112  key="HIH3600/humidity",
113  device_class=SensorDeviceClass.HUMIDITY,
114  entity_registry_enabled_default=False,
115  native_unit_of_measurement=PERCENTAGE,
116  read_mode=READ_MODE_FLOAT,
117  state_class=SensorStateClass.MEASUREMENT,
118  translation_key="humidity_hih3600",
119  ),
121  key="HIH4000/humidity",
122  device_class=SensorDeviceClass.HUMIDITY,
123  entity_registry_enabled_default=False,
124  native_unit_of_measurement=PERCENTAGE,
125  read_mode=READ_MODE_FLOAT,
126  state_class=SensorStateClass.MEASUREMENT,
127  translation_key="humidity_hih4000",
128  ),
130  key="HIH5030/humidity",
131  device_class=SensorDeviceClass.HUMIDITY,
132  entity_registry_enabled_default=False,
133  native_unit_of_measurement=PERCENTAGE,
134  read_mode=READ_MODE_FLOAT,
135  state_class=SensorStateClass.MEASUREMENT,
136  translation_key="humidity_hih5030",
137  ),
139  key="HTM1735/humidity",
140  device_class=SensorDeviceClass.HUMIDITY,
141  entity_registry_enabled_default=False,
142  native_unit_of_measurement=PERCENTAGE,
143  read_mode=READ_MODE_FLOAT,
144  state_class=SensorStateClass.MEASUREMENT,
145  translation_key="humidity_htm1735",
146  ),
148  key="B1-R1-A/pressure",
149  device_class=SensorDeviceClass.PRESSURE,
150  entity_registry_enabled_default=False,
151  native_unit_of_measurement=UnitOfPressure.MBAR,
152  read_mode=READ_MODE_FLOAT,
153  state_class=SensorStateClass.MEASUREMENT,
154  ),
156  key="S3-R1-A/illuminance",
157  device_class=SensorDeviceClass.ILLUMINANCE,
158  entity_registry_enabled_default=False,
159  native_unit_of_measurement=LIGHT_LUX,
160  read_mode=READ_MODE_FLOAT,
161  state_class=SensorStateClass.MEASUREMENT,
162  ),
164  key="VAD",
165  device_class=SensorDeviceClass.VOLTAGE,
166  entity_registry_enabled_default=False,
167  native_unit_of_measurement=UnitOfElectricPotential.VOLT,
168  read_mode=READ_MODE_FLOAT,
169  state_class=SensorStateClass.MEASUREMENT,
170  translation_key="voltage_vad",
171  ),
173  key="VDD",
174  device_class=SensorDeviceClass.VOLTAGE,
175  entity_registry_enabled_default=False,
176  native_unit_of_measurement=UnitOfElectricPotential.VOLT,
177  read_mode=READ_MODE_FLOAT,
178  state_class=SensorStateClass.MEASUREMENT,
179  translation_key="voltage_vdd",
180  ),
182  key="vis",
183  device_class=SensorDeviceClass.VOLTAGE,
184  entity_registry_enabled_default=False,
185  native_unit_of_measurement=UnitOfElectricPotential.VOLT,
186  read_mode=READ_MODE_FLOAT,
187  state_class=SensorStateClass.MEASUREMENT,
188  translation_key="voltage_vis",
189  ),
190  ),
191  "28": (
193  key="temperature",
194  device_class=SensorDeviceClass.TEMPERATURE,
195  native_unit_of_measurement=UnitOfTemperature.CELSIUS,
196  override_key=_get_sensor_precision_family_28,
197  read_mode=READ_MODE_FLOAT,
198  state_class=SensorStateClass.MEASUREMENT,
199  ),
200  ),
201  "30": (
202  SIMPLE_TEMPERATURE_SENSOR_DESCRIPTION,
204  key="typeX/temperature",
205  device_class=SensorDeviceClass.TEMPERATURE,
206  entity_registry_enabled_default=False,
207  native_unit_of_measurement=UnitOfTemperature.CELSIUS,
208  read_mode=READ_MODE_FLOAT,
209  override_key=lambda d, o: "typeK/temperature",
210  state_class=SensorStateClass.MEASUREMENT,
211  translation_key="thermocouple_temperature_k",
212  ),
214  key="volt",
215  device_class=SensorDeviceClass.VOLTAGE,
216  entity_registry_enabled_default=False,
217  native_unit_of_measurement=UnitOfElectricPotential.VOLT,
218  read_mode=READ_MODE_FLOAT,
219  state_class=SensorStateClass.MEASUREMENT,
220  ),
222  key="vis",
223  device_class=SensorDeviceClass.VOLTAGE,
224  entity_registry_enabled_default=False,
225  native_unit_of_measurement=UnitOfElectricPotential.VOLT,
226  read_mode=READ_MODE_FLOAT,
227  state_class=SensorStateClass.MEASUREMENT,
228  translation_key="voltage_vis_gradient",
229  ),
230  ),
231  "3B": (SIMPLE_TEMPERATURE_SENSOR_DESCRIPTION,),
232  "42": (SIMPLE_TEMPERATURE_SENSOR_DESCRIPTION,),
233  "1D": tuple(
235  key=f"counter.{device_key}",
236  native_unit_of_measurement="count",
237  read_mode=READ_MODE_INT,
238  state_class=SensorStateClass.TOTAL_INCREASING,
239  translation_key="counter_id",
240  translation_placeholders={"id": str(device_key)},
241  )
242  for device_key in DEVICE_KEYS_A_B
243  ),
244 }
245 
246 # EF sensors are usually hobbyboards specialized sensors.
247 
248 HOBBYBOARD_EF: dict[str, tuple[OneWireSensorEntityDescription, ...]] = {
249  "HobbyBoards_EF": (
251  key="humidity/humidity_corrected",
252  device_class=SensorDeviceClass.HUMIDITY,
253  native_unit_of_measurement=PERCENTAGE,
254  read_mode=READ_MODE_FLOAT,
255  state_class=SensorStateClass.MEASUREMENT,
256  ),
258  key="humidity/humidity_raw",
259  device_class=SensorDeviceClass.HUMIDITY,
260  native_unit_of_measurement=PERCENTAGE,
261  read_mode=READ_MODE_FLOAT,
262  state_class=SensorStateClass.MEASUREMENT,
263  translation_key="humidity_raw",
264  ),
266  key="humidity/temperature",
267  device_class=SensorDeviceClass.TEMPERATURE,
268  native_unit_of_measurement=UnitOfTemperature.CELSIUS,
269  read_mode=READ_MODE_FLOAT,
270  state_class=SensorStateClass.MEASUREMENT,
271  ),
272  ),
273  "HB_MOISTURE_METER": tuple(
275  key=f"moisture/sensor.{device_key}",
276  device_class=SensorDeviceClass.PRESSURE,
277  native_unit_of_measurement=UnitOfPressure.CBAR,
278  read_mode=READ_MODE_FLOAT,
279  state_class=SensorStateClass.MEASUREMENT,
280  translation_key="moisture_id",
281  translation_placeholders={"id": str(device_key)},
282  )
283  for device_key in DEVICE_KEYS_0_3
284  ),
285 }
286 
287 # 7E sensors are special sensors by Embedded Data Systems
288 
289 EDS_SENSORS: dict[str, tuple[OneWireSensorEntityDescription, ...]] = {
290  "EDS0066": (
292  key="EDS0066/temperature",
293  device_class=SensorDeviceClass.TEMPERATURE,
294  native_unit_of_measurement=UnitOfTemperature.CELSIUS,
295  read_mode=READ_MODE_FLOAT,
296  state_class=SensorStateClass.MEASUREMENT,
297  ),
299  key="EDS0066/pressure",
300  device_class=SensorDeviceClass.PRESSURE,
301  native_unit_of_measurement=UnitOfPressure.MBAR,
302  read_mode=READ_MODE_FLOAT,
303  state_class=SensorStateClass.MEASUREMENT,
304  ),
305  ),
306  "EDS0068": (
308  key="EDS0068/temperature",
309  device_class=SensorDeviceClass.TEMPERATURE,
310  native_unit_of_measurement=UnitOfTemperature.CELSIUS,
311  read_mode=READ_MODE_FLOAT,
312  state_class=SensorStateClass.MEASUREMENT,
313  ),
315  key="EDS0068/pressure",
316  device_class=SensorDeviceClass.PRESSURE,
317  native_unit_of_measurement=UnitOfPressure.MBAR,
318  read_mode=READ_MODE_FLOAT,
319  state_class=SensorStateClass.MEASUREMENT,
320  ),
322  key="EDS0068/light",
323  device_class=SensorDeviceClass.ILLUMINANCE,
324  native_unit_of_measurement=LIGHT_LUX,
325  read_mode=READ_MODE_FLOAT,
326  state_class=SensorStateClass.MEASUREMENT,
327  ),
329  key="EDS0068/humidity",
330  device_class=SensorDeviceClass.HUMIDITY,
331  native_unit_of_measurement=PERCENTAGE,
332  read_mode=READ_MODE_FLOAT,
333  state_class=SensorStateClass.MEASUREMENT,
334  ),
335  ),
336 }
337 
338 
340  device_sub_type: str,
341 ) -> dict[str, tuple[OneWireSensorEntityDescription, ...]]:
342  """Return the proper info array for the device type."""
343  if "HobbyBoard" in device_sub_type:
344  return HOBBYBOARD_EF
345  if "EDS" in device_sub_type:
346  return EDS_SENSORS
347  return DEVICE_SENSORS
348 
349 
351  hass: HomeAssistant,
352  config_entry: OneWireConfigEntry,
353  async_add_entities: AddEntitiesCallback,
354 ) -> None:
355  """Set up 1-Wire platform."""
356  entities = await hass.async_add_executor_job(
357  get_entities, config_entry.runtime_data, config_entry.options
358  )
359  async_add_entities(entities, True)
360 
361 
363  onewire_hub: OneWireHub, options: MappingProxyType[str, Any]
364 ) -> list[OneWireSensor]:
365  """Get a list of entities."""
366  if not onewire_hub.devices:
367  return []
368 
369  entities: list[OneWireSensor] = []
370  assert onewire_hub.owproxy
371  for device in onewire_hub.devices:
372  family = device.family
373  device_type = device.type
374  device_id = device.id
375  device_info = device.device_info
376  device_sub_type = "std"
377  device_path = device.path
378  if device_type and "EF" in family:
379  device_sub_type = "HobbyBoard"
380  family = device_type
381  elif device_type and "7E" in family:
382  device_sub_type = "EDS"
383  family = device_type
384  elif "A6" in family:
385  # A6 is a secondary family code for DS2438
386  family = "26"
387 
388  if family not in get_sensor_types(device_sub_type):
389  continue
390  for description in get_sensor_types(device_sub_type)[family]:
391  if description.key.startswith("moisture/"):
392  s_id = description.key.split(".")[1]
393  is_leaf = int(
394  onewire_hub.owproxy.read(
395  f"{device_path}moisture/is_leaf.{s_id}"
396  ).decode()
397  )
398  if is_leaf:
399  description = dataclasses.replace(
400  description,
401  device_class=SensorDeviceClass.HUMIDITY,
402  native_unit_of_measurement=PERCENTAGE,
403  translation_key="wetness_id",
404  translation_placeholders={"id": s_id},
405  )
406  override_key = None
407  if description.override_key:
408  override_key = description.override_key(device_id, options)
409  device_file = os.path.join(
410  os.path.split(device.path)[0],
411  override_key or description.key,
412  )
413  if family == "12":
414  # We need to check if there is TAI8570 plugged in
415  try:
416  onewire_hub.owproxy.read(device_file)
417  except protocol.OwnetError as err:
418  _LOGGER.debug(
419  "Ignoring unreachable sensor %s",
420  device_file,
421  exc_info=err,
422  )
423  continue
424  entities.append(
426  description=description,
427  device_id=device_id,
428  device_file=device_file,
429  device_info=device_info,
430  owproxy=onewire_hub.owproxy,
431  )
432  )
433  return entities
434 
435 
437  """Implementation of a 1-Wire sensor."""
438 
439  entity_description: OneWireSensorEntityDescription
440 
441  @property
442  def native_value(self) -> StateType:
443  """Return the state of the entity."""
444  return self._state_state
web.Response get(self, web.Request request, str config_key)
Definition: view.py:88
None async_setup_entry(HomeAssistant hass, OneWireConfigEntry config_entry, AddEntitiesCallback async_add_entities)
Definition: sensor.py:354
str _get_sensor_precision_family_28(str device_id, Mapping[str, Any] options)
Definition: sensor.py:52
list[OneWireSensor] get_entities(OneWireHub onewire_hub, MappingProxyType[str, Any] options)
Definition: sensor.py:364
dict[str, tuple[OneWireSensorEntityDescription,...]] get_sensor_types(str device_sub_type)
Definition: sensor.py:341