Home Assistant Unofficial Reference 2024.12.1
sensor.py
Go to the documentation of this file.
1 """Support for switch entities."""
2 
3 from __future__ import annotations
4 
5 from dataclasses import dataclass, field
6 from datetime import UTC, datetime, timedelta
7 
8 from gardena_bluetooth.const import Battery, Sensor, Valve
9 from gardena_bluetooth.parse import Characteristic
10 
12  SensorDeviceClass,
13  SensorEntity,
14  SensorEntityDescription,
15  SensorStateClass,
16 )
17 from homeassistant.const import PERCENTAGE, EntityCategory
18 from homeassistant.core import HomeAssistant
19 from homeassistant.helpers.entity_platform import AddEntitiesCallback
20 import homeassistant.util.dt as dt_util
21 
22 from . import GardenaBluetoothConfigEntry
23 from .coordinator import GardenaBluetoothCoordinator
24 from .entity import GardenaBluetoothDescriptorEntity, GardenaBluetoothEntity
25 
26 
27 @dataclass(frozen=True)
29  """Description of entity."""
30 
31  char: Characteristic = field(default_factory=lambda: Characteristic(""))
32  connected_state: Characteristic | None = None
33 
34  @property
35  def context(self) -> set[str]:
36  """Context needed for update coordinator."""
37  data = {self.char.uuid}
38  if self.connected_state:
39  data.add(self.connected_state.uuid)
40  return data
41 
42 
43 DESCRIPTIONS = (
45  key=Valve.activation_reason.uuid,
46  translation_key="activation_reason",
47  state_class=SensorStateClass.MEASUREMENT,
48  entity_category=EntityCategory.DIAGNOSTIC,
49  entity_registry_enabled_default=False,
50  char=Valve.activation_reason,
51  ),
53  key=Battery.battery_level.uuid,
54  state_class=SensorStateClass.MEASUREMENT,
55  device_class=SensorDeviceClass.BATTERY,
56  entity_category=EntityCategory.DIAGNOSTIC,
57  native_unit_of_measurement=PERCENTAGE,
58  char=Battery.battery_level,
59  ),
61  key=Sensor.battery_level.uuid,
62  translation_key="sensor_battery_level",
63  state_class=SensorStateClass.MEASUREMENT,
64  device_class=SensorDeviceClass.BATTERY,
65  entity_category=EntityCategory.DIAGNOSTIC,
66  native_unit_of_measurement=PERCENTAGE,
67  char=Sensor.battery_level,
68  connected_state=Sensor.connected_state,
69  ),
71  key=Sensor.value.uuid,
72  state_class=SensorStateClass.MEASUREMENT,
73  device_class=SensorDeviceClass.MOISTURE,
74  native_unit_of_measurement=PERCENTAGE,
75  char=Sensor.value,
76  connected_state=Sensor.connected_state,
77  ),
79  key=Sensor.type.uuid,
80  translation_key="sensor_type",
81  entity_category=EntityCategory.DIAGNOSTIC,
82  char=Sensor.type,
83  connected_state=Sensor.connected_state,
84  ),
86  key=Sensor.measurement_timestamp.uuid,
87  translation_key="sensor_measurement_timestamp",
88  device_class=SensorDeviceClass.TIMESTAMP,
89  entity_category=EntityCategory.DIAGNOSTIC,
90  char=Sensor.measurement_timestamp,
91  connected_state=Sensor.connected_state,
92  ),
93 )
94 
95 
97  hass: HomeAssistant,
98  entry: GardenaBluetoothConfigEntry,
99  async_add_entities: AddEntitiesCallback,
100 ) -> None:
101  """Set up Gardena Bluetooth sensor based on a config entry."""
102  coordinator = entry.runtime_data
103  entities: list[GardenaBluetoothEntity] = [
104  GardenaBluetoothSensor(coordinator, description, description.context)
105  for description in DESCRIPTIONS
106  if description.key in coordinator.characteristics
107  ]
108  if Valve.remaining_open_time.uuid in coordinator.characteristics:
109  entities.append(GardenaBluetoothRemainSensor(coordinator))
110  async_add_entities(entities)
111 
112 
114  """Representation of a sensor."""
115 
116  entity_description: GardenaBluetoothSensorEntityDescription
117 
118  def _handle_coordinator_update(self) -> None:
119  value = self.coordinator.get_cached(self.entity_descriptionentity_description.char)
120  if isinstance(value, datetime):
121  value = value.replace(tzinfo=dt_util.get_default_time_zone())
122  self._attr_native_value_attr_native_value = value
123 
124  if char := self.entity_descriptionentity_description.connected_state:
125  self._attr_available_attr_available = bool(self.coordinator.get_cached(char))
126  else:
127  self._attr_available_attr_available = True
128 
130 
131 
133  """Representation of a sensor."""
134 
135  _attr_device_class = SensorDeviceClass.TIMESTAMP
136  _attr_native_value: datetime | None = None
137  _attr_translation_key = "remaining_open_timestamp"
138 
139  def __init__(
140  self,
141  coordinator: GardenaBluetoothCoordinator,
142  ) -> None:
143  """Initialize the sensor."""
144  super().__init__(coordinator, {Valve.remaining_open_time.uuid})
145  self._attr_unique_id_attr_unique_id = f"{coordinator.address}-remaining_open_timestamp"
146 
147  def _handle_coordinator_update(self) -> None:
148  value = self.coordinator.get_cached(Valve.remaining_open_time)
149  if not value:
150  self._attr_native_value_attr_native_value = None
152  return
153 
154  time = datetime.now(UTC) + timedelta(seconds=value)
155  if not self._attr_native_value_attr_native_value:
156  self._attr_native_value_attr_native_value = time
158  return
159 
160  error = time - self._attr_native_value_attr_native_value
161  if abs(error.total_seconds()) > 10:
162  self._attr_native_value_attr_native_value = time
164  return
165 
166  @property
167  def available(self) -> bool:
168  """Sensor only available when open."""
169  return super().available and self._attr_native_value_attr_native_value is not None
CharacteristicType|None get_cached(self, Characteristic[CharacteristicType] char)
Definition: coordinator.py:80
None __init__(self, GardenaBluetoothCoordinator coordinator)
Definition: sensor.py:142
None async_setup_entry(HomeAssistant hass, GardenaBluetoothConfigEntry entry, AddEntitiesCallback async_add_entities)
Definition: sensor.py:100