Home Assistant Unofficial Reference 2024.12.1
sensor.py
Go to the documentation of this file.
1 """Sensor support for Melnor Bluetooth water timer."""
2 
3 from __future__ import annotations
4 
5 from collections.abc import Callable
6 from dataclasses import dataclass
7 from datetime import datetime
8 from typing import Any
9 
10 from melnor_bluetooth.device import Device, Valve
11 
13  SensorDeviceClass,
14  SensorEntity,
15  SensorEntityDescription,
16  SensorStateClass,
17 )
18 from homeassistant.config_entries import ConfigEntry
19 from homeassistant.const import (
20  PERCENTAGE,
21  SIGNAL_STRENGTH_DECIBELS_MILLIWATT,
22  EntityCategory,
23 )
24 from homeassistant.core import HomeAssistant
25 from homeassistant.helpers.entity_platform import AddEntitiesCallback
26 from homeassistant.helpers.typing import StateType
27 from homeassistant.util import dt as dt_util
28 
29 from .const import DOMAIN
30 from .coordinator import MelnorDataUpdateCoordinator
31 from .entity import MelnorBluetoothEntity, MelnorZoneEntity, get_entities_for_valves
32 
33 
34 def watering_seconds_left(valve: Valve) -> datetime | None:
35  """Calculate the number of minutes left in the current watering cycle."""
36 
37  if valve.is_watering is not True or dt_util.now() > dt_util.utc_from_timestamp(
38  valve.watering_end_time
39  ):
40  return None
41 
42  return dt_util.utc_from_timestamp(valve.watering_end_time)
43 
44 
45 def next_cycle(valve: Valve) -> datetime | None:
46  """Return the value of the next_cycle date, only if the cycle is enabled."""
47 
48  if valve.schedule_enabled is True:
49  return valve.next_cycle
50 
51  return None
52 
53 
54 @dataclass(frozen=True, kw_only=True)
56  """Describes Melnor sensor entity."""
57 
58  state_fn: Callable[[Valve], Any]
59 
60 
61 @dataclass(frozen=True, kw_only=True)
63  """Describes Melnor sensor entity."""
64 
65  state_fn: Callable[[Device], Any]
66 
67 
68 DEVICE_ENTITY_DESCRIPTIONS: list[MelnorSensorEntityDescription] = [
70  device_class=SensorDeviceClass.BATTERY,
71  entity_category=EntityCategory.DIAGNOSTIC,
72  key="battery",
73  native_unit_of_measurement=PERCENTAGE,
74  state_class=SensorStateClass.MEASUREMENT,
75  state_fn=lambda device: device.battery_level,
76  ),
78  device_class=SensorDeviceClass.SIGNAL_STRENGTH,
79  entity_category=EntityCategory.DIAGNOSTIC,
80  entity_registry_enabled_default=False,
81  key="rssi",
82  translation_key="rssi",
83  native_unit_of_measurement=SIGNAL_STRENGTH_DECIBELS_MILLIWATT,
84  state_class=SensorStateClass.MEASUREMENT,
85  state_fn=lambda device: device.rssi,
86  ),
87 ]
88 
89 ZONE_ENTITY_DESCRIPTIONS: list[MelnorZoneSensorEntityDescription] = [
91  device_class=SensorDeviceClass.TIMESTAMP,
92  key="manual_cycle_end",
93  translation_key="manual_cycle_end",
94  state_fn=watering_seconds_left,
95  ),
97  device_class=SensorDeviceClass.TIMESTAMP,
98  key="next_cycle",
99  translation_key="next_cycle",
100  state_fn=next_cycle,
101  ),
102 ]
103 
104 
106  hass: HomeAssistant,
107  config_entry: ConfigEntry,
108  async_add_entities: AddEntitiesCallback,
109 ) -> None:
110  """Set up the sensor platform."""
111 
112  coordinator: MelnorDataUpdateCoordinator = hass.data[DOMAIN][config_entry.entry_id]
113 
114  # Device-level sensors
117  coordinator,
118  description,
119  )
120  for description in DEVICE_ENTITY_DESCRIPTIONS
121  )
122 
123  # Valve/Zone-level sensors
125  get_entities_for_valves(
126  coordinator,
127  ZONE_ENTITY_DESCRIPTIONS,
128  lambda valve, description: MelnorZoneSensorEntity(
129  coordinator, description, valve
130  ),
131  )
132  )
133 
134 
136  """Representation of a Melnor sensor."""
137 
138  entity_description: MelnorSensorEntityDescription
139 
140  def __init__(
141  self,
142  coordinator: MelnorDataUpdateCoordinator,
143  entity_description: MelnorSensorEntityDescription,
144  ) -> None:
145  """Initialize a sensor for a Melnor device."""
146  super().__init__(coordinator)
147 
148  self._attr_unique_id_attr_unique_id = f"{self._device.mac}-{entity_description.key}"
149 
150  self.entity_descriptionentity_description = entity_description
151 
152  @property
153  def native_value(self) -> StateType:
154  """Return the sensor value."""
155  return self.entity_descriptionentity_description.state_fn(self._device_device_device)
156 
157 
159  """Representation of a Melnor sensor."""
160 
161  entity_description: MelnorZoneSensorEntityDescription
162 
163  def __init__(
164  self,
165  coordinator: MelnorDataUpdateCoordinator,
166  entity_description: MelnorZoneSensorEntityDescription,
167  valve: Valve,
168  ) -> None:
169  """Initialize a sensor for a Melnor device."""
170  super().__init__(coordinator, entity_description, valve)
171 
172  @property
173  def native_value(self) -> StateType:
174  """Return the sensor value."""
175  return self.entity_descriptionentity_description.state_fn(self._valve_valve)
None __init__(self, MelnorDataUpdateCoordinator coordinator, MelnorSensorEntityDescription entity_description)
Definition: sensor.py:144
None __init__(self, MelnorDataUpdateCoordinator coordinator, MelnorZoneSensorEntityDescription entity_description, Valve valve)
Definition: sensor.py:168
datetime|None watering_seconds_left(Valve valve)
Definition: sensor.py:34
None async_setup_entry(HomeAssistant hass, ConfigEntry config_entry, AddEntitiesCallback async_add_entities)
Definition: sensor.py:109
datetime|None next_cycle(Valve valve)
Definition: sensor.py:45