Home Assistant Unofficial Reference 2024.12.1
sensor.py
Go to the documentation of this file.
1 """Support for EufyLife sensors."""
2 
3 from __future__ import annotations
4 
5 from typing import Any
6 
7 from eufylife_ble_client import MODEL_TO_NAME
8 
9 from homeassistant import config_entries
10 from homeassistant.components.bluetooth import async_address_present
12  RestoreSensor,
13  SensorDeviceClass,
14  SensorEntity,
15 )
16 from homeassistant.const import STATE_UNAVAILABLE, STATE_UNKNOWN, UnitOfMass
17 from homeassistant.core import HomeAssistant, callback
18 from homeassistant.helpers import device_registry as dr
19 from homeassistant.helpers.device_registry import DeviceInfo
20 from homeassistant.helpers.entity_platform import AddEntitiesCallback
21 from homeassistant.util.unit_system import US_CUSTOMARY_SYSTEM
22 
23 from .const import DOMAIN
24 from .models import EufyLifeData
25 
26 IGNORED_STATES = {STATE_UNAVAILABLE, STATE_UNKNOWN}
27 
28 
30  hass: HomeAssistant,
32  async_add_entities: AddEntitiesCallback,
33 ) -> None:
34  """Set up the EufyLife sensors."""
35  data: EufyLifeData = hass.data[DOMAIN][entry.entry_id]
36 
37  entities = [
40  ]
41 
42  if data.client.supports_heart_rate:
43  entities.append(EufyLifeHeartRateSensorEntity(data))
44 
45  async_add_entities(entities)
46 
47 
49  """Representation of an EufyLife sensor."""
50 
51  _attr_has_entity_name = True
52 
53  def __init__(self, data: EufyLifeData) -> None:
54  """Initialize the weight sensor entity."""
55  self._data_data = data
56 
57  self._attr_device_info_attr_device_info = DeviceInfo(
58  name=MODEL_TO_NAME[data.model],
59  connections={(dr.CONNECTION_BLUETOOTH, data.address)},
60  )
61 
62  @property
63  def available(self) -> bool:
64  """Determine if the entity is available."""
65  if self._data_data.client.advertisement_data_contains_state:
66  # If the device only uses advertisement data, just check if the address is present.
67  return async_address_present(self.hasshass, self._data_data.address)
68 
69  # If the device needs an active connection, availability is based on whether it is connected.
70  return self._data_data.client.is_connected
71 
72  @callback
73  def _handle_state_update(self, *args: Any) -> None:
74  """Handle state update."""
75  self.async_write_ha_stateasync_write_ha_state()
76 
77  async def async_added_to_hass(self) -> None:
78  """Register callback."""
79  self.async_on_removeasync_on_remove(
80  self._data_data.client.register_callback(self._handle_state_update_handle_state_update)
81  )
82 
83 
85  """Representation of an EufyLife real-time weight sensor."""
86 
87  _attr_translation_key = "real_time_weight"
88  _attr_native_unit_of_measurement = UnitOfMass.KILOGRAMS
89  _attr_device_class = SensorDeviceClass.WEIGHT
90 
91  def __init__(self, data: EufyLifeData) -> None:
92  """Initialize the real-time weight sensor entity."""
93  super().__init__(data)
94  self._attr_unique_id_attr_unique_id = f"{data.address}_real_time_weight"
95 
96  @property
97  def native_value(self) -> float | None:
98  """Return the native value."""
99  if self._data_data.client.state is not None:
100  return self._data_data.client.state.weight_kg
101  return None
102 
103  @property
104  def suggested_unit_of_measurement(self) -> str | None:
105  """Set the suggested unit based on the unit system."""
106  if self.hasshass.config.units is US_CUSTOMARY_SYSTEM:
107  return UnitOfMass.POUNDS
108 
109  return UnitOfMass.KILOGRAMS
110 
111 
113  """Representation of an EufyLife weight sensor."""
114 
115  _attr_translation_key = "weight"
116  _attr_native_unit_of_measurement = UnitOfMass.KILOGRAMS
117  _attr_device_class = SensorDeviceClass.WEIGHT
118 
119  def __init__(self, data: EufyLifeData) -> None:
120  """Initialize the weight sensor entity."""
121  super().__init__(data)
122  self._attr_unique_id_attr_unique_id = f"{data.address}_weight"
123 
124  @property
125  def available(self) -> bool:
126  """Determine if the entity is available."""
127  return True
128 
129  @property
130  def suggested_unit_of_measurement(self) -> str | None:
131  """Set the suggested unit based on the unit system."""
132  if self.hasshass.config.units is US_CUSTOMARY_SYSTEM:
133  return UnitOfMass.POUNDS
134 
135  return UnitOfMass.KILOGRAMS
136 
137  @callback
138  def _handle_state_update(self, *args: Any) -> None:
139  """Handle state update."""
140  state = self._data_data.client.state
141  if state is not None and state.final_weight_kg is not None:
142  self._attr_native_value_attr_native_value = state.final_weight_kg
143 
144  super()._handle_state_update(args)
145 
146  async def async_added_to_hass(self) -> None:
147  """Restore state on startup."""
148  await super().async_added_to_hass()
149 
150  last_state = await self.async_get_last_state()
151  last_sensor_data = await self.async_get_last_sensor_dataasync_get_last_sensor_data()
152 
153  if not last_state or not last_sensor_data or last_state.state in IGNORED_STATES:
154  return
155 
156  self._attr_native_value_attr_native_value = last_sensor_data.native_value
157 
158 
160  """Representation of an EufyLife heart rate sensor."""
161 
162  _attr_translation_key = "heart_rate"
163  _attr_native_unit_of_measurement = "bpm"
164 
165  def __init__(self, data: EufyLifeData) -> None:
166  """Initialize the heart rate sensor entity."""
167  super().__init__(data)
168  self._attr_unique_id_attr_unique_id = f"{data.address}_heart_rate"
169 
170  @property
171  def available(self) -> bool:
172  """Determine if the entity is available."""
173  return True
174 
175  @callback
176  def _handle_state_update(self, *args: Any) -> None:
177  """Handle state update."""
178  state = self._data_data.client.state
179  if state is not None and state.heart_rate is not None:
180  self._attr_native_value_attr_native_value = state.heart_rate
181 
182  super()._handle_state_update(args)
183 
184  async def async_added_to_hass(self) -> None:
185  """Restore state on startup."""
186  await super().async_added_to_hass()
187 
188  last_state = await self.async_get_last_state()
189  last_sensor_data = await self.async_get_last_sensor_dataasync_get_last_sensor_data()
190 
191  if not last_state or not last_sensor_data or last_state.state in IGNORED_STATES:
192  return
193 
194  self._attr_native_value_attr_native_value = last_sensor_data.native_value
SensorExtraStoredData|None async_get_last_sensor_data(self)
Definition: __init__.py:934
None async_on_remove(self, CALLBACK_TYPE func)
Definition: entity.py:1331
bool async_address_present(HomeAssistant hass, str address, bool connectable=True)
Definition: api.py:104
None async_setup_entry(HomeAssistant hass, config_entries.ConfigEntry entry, AddEntitiesCallback async_add_entities)
Definition: sensor.py:33