Home Assistant Unofficial Reference 2024.12.1
sensor.py
Go to the documentation of this file.
1 """Support for Xiaomi Aqara sensors."""
2 
3 from __future__ import annotations
4 
5 import logging
6 
8  SensorDeviceClass,
9  SensorEntity,
10  SensorEntityDescription,
11  SensorStateClass,
12 )
13 from homeassistant.config_entries import ConfigEntry
14 from homeassistant.const import (
15  ATTR_BATTERY_LEVEL,
16  LIGHT_LUX,
17  PERCENTAGE,
18  UnitOfPower,
19  UnitOfPressure,
20  UnitOfTemperature,
21 )
22 from homeassistant.core import HomeAssistant
23 from homeassistant.helpers.entity_platform import AddEntitiesCallback
24 
25 from .const import BATTERY_MODELS, DOMAIN, GATEWAYS_KEY, POWER_MODELS
26 from .entity import XiaomiDevice
27 
28 _LOGGER = logging.getLogger(__name__)
29 
30 SENSOR_TYPES: dict[str, SensorEntityDescription] = {
31  "temperature": SensorEntityDescription(
32  key="temperature",
33  native_unit_of_measurement=UnitOfTemperature.CELSIUS,
34  device_class=SensorDeviceClass.TEMPERATURE,
35  state_class=SensorStateClass.MEASUREMENT,
36  ),
37  "humidity": SensorEntityDescription(
38  key="humidity",
39  native_unit_of_measurement=PERCENTAGE,
40  device_class=SensorDeviceClass.HUMIDITY,
41  state_class=SensorStateClass.MEASUREMENT,
42  ),
43  "illumination": SensorEntityDescription(
44  key="illumination",
45  native_unit_of_measurement="lm",
46  state_class=SensorStateClass.MEASUREMENT,
47  ),
49  key="lux",
50  native_unit_of_measurement=LIGHT_LUX,
51  device_class=SensorDeviceClass.ILLUMINANCE,
52  state_class=SensorStateClass.MEASUREMENT,
53  ),
54  "pressure": SensorEntityDescription(
55  key="pressure",
56  native_unit_of_measurement=UnitOfPressure.HPA,
57  device_class=SensorDeviceClass.PRESSURE,
58  state_class=SensorStateClass.MEASUREMENT,
59  ),
60  "bed_activity": SensorEntityDescription(
61  key="bed_activity",
62  native_unit_of_measurement="μm",
63  device_class=None,
64  state_class=SensorStateClass.MEASUREMENT,
65  ),
66  "load_power": SensorEntityDescription(
67  key="load_power",
68  native_unit_of_measurement=UnitOfPower.WATT,
69  device_class=SensorDeviceClass.POWER,
70  state_class=SensorStateClass.MEASUREMENT,
71  ),
72  "final_tilt_angle": SensorEntityDescription(
73  key="final_tilt_angle",
74  ),
75  "coordination": SensorEntityDescription(
76  key="coordination",
77  ),
78  "Battery": SensorEntityDescription(
79  key="Battery",
80  state_class=SensorStateClass.MEASUREMENT,
81  ),
82 }
83 
84 
86  hass: HomeAssistant,
87  config_entry: ConfigEntry,
88  async_add_entities: AddEntitiesCallback,
89 ) -> None:
90  """Perform the setup for Xiaomi devices."""
91  entities: list[XiaomiSensor | XiaomiBatterySensor] = []
92  gateway = hass.data[DOMAIN][GATEWAYS_KEY][config_entry.entry_id]
93  for device in gateway.devices["sensor"]:
94  if device["model"] == "sensor_ht":
95  entities.append(
97  device, "Temperature", "temperature", gateway, config_entry
98  )
99  )
100  entities.append(
101  XiaomiSensor(device, "Humidity", "humidity", gateway, config_entry)
102  )
103  elif device["model"] in ("weather", "weather.v1"):
104  entities.append(
105  XiaomiSensor(
106  device, "Temperature", "temperature", gateway, config_entry
107  )
108  )
109  entities.append(
110  XiaomiSensor(device, "Humidity", "humidity", gateway, config_entry)
111  )
112  entities.append(
113  XiaomiSensor(device, "Pressure", "pressure", gateway, config_entry)
114  )
115  elif device["model"] == "sensor_motion.aq2":
116  entities.append(
117  XiaomiSensor(device, "Illumination", "lux", gateway, config_entry)
118  )
119  elif device["model"] in ("gateway", "gateway.v3", "acpartner.v3"):
120  entities.append(
121  XiaomiSensor(
122  device, "Illumination", "illumination", gateway, config_entry
123  )
124  )
125  elif device["model"] in ("vibration",):
126  entities.append(
127  XiaomiSensor(
128  device, "Bed Activity", "bed_activity", gateway, config_entry
129  )
130  )
131  entities.append(
132  XiaomiSensor(
133  device, "Tilt Angle", "final_tilt_angle", gateway, config_entry
134  )
135  )
136  entities.append(
137  XiaomiSensor(
138  device, "Coordination", "coordination", gateway, config_entry
139  )
140  )
141  else:
142  _LOGGER.warning("Unmapped Device Model")
143 
144  # Set up battery sensors
145  seen_sids = set() # Set of device sids that are already seen
146  for devices in gateway.devices.values():
147  for device in devices:
148  if device["sid"] in seen_sids:
149  continue
150  seen_sids.add(device["sid"])
151  if device["model"] in BATTERY_MODELS:
152  entities.append(
153  XiaomiBatterySensor(device, "Battery", gateway, config_entry)
154  )
155  if device["model"] in POWER_MODELS:
156  entities.append(
157  XiaomiSensor(
158  device, "Load Power", "load_power", gateway, config_entry
159  )
160  )
161  async_add_entities(entities)
162 
163 
165  """Representation of a XiaomiSensor."""
166 
167  def __init__(self, device, name, data_key, xiaomi_hub, config_entry):
168  """Initialize the XiaomiSensor."""
169  self._data_key_data_key = data_key
170  self.entity_descriptionentity_description = SENSOR_TYPES[data_key]
171  super().__init__(device, name, xiaomi_hub, config_entry)
172 
173  def parse_data(self, data, raw_data):
174  """Parse data sent by gateway."""
175  if (value := data.get(self._data_key_data_key)) is None:
176  return False
177  if self._data_key_data_key in ("coordination", "status"):
178  self._attr_native_value_attr_native_value = value
179  return True
180  value = float(value)
181  if self._data_key_data_key in ("temperature", "humidity", "pressure"):
182  value /= 100
183  elif self._data_key_data_key in ("illumination",):
184  value = max(value - 300, 0)
185  if self._data_key_data_key == "temperature" and (value < -50 or value > 60):
186  return False
187  if self._data_key_data_key == "humidity" and (value <= 0 or value > 100):
188  return False
189  if self._data_key_data_key == "pressure" and value == 0:
190  return False
191  if self._data_key_data_key in ("illumination", "lux"):
192  self._attr_native_value_attr_native_value = round(value)
193  else:
194  self._attr_native_value_attr_native_value = round(value, 1)
195  return True
196 
197 
199  """Representation of a XiaomiSensor."""
200 
201  _attr_native_unit_of_measurement = PERCENTAGE
202  _attr_device_class = SensorDeviceClass.BATTERY
203 
204  def parse_data(self, data, raw_data):
205  """Parse data sent by gateway."""
206  succeed = super().parse_voltage(data)
207  if not succeed:
208  return False
209  battery_level = int(self._extra_state_attributes_extra_state_attributes.pop(ATTR_BATTERY_LEVEL))
210  if battery_level <= 0 or battery_level > 100:
211  return False
212  self._attr_native_value_attr_native_value = battery_level
213  return True
214 
215  def parse_voltage(self, data):
216  """Parse battery level data sent by gateway."""
217  return False # Override parse_voltage to do nothing
def __init__(self, device, name, data_key, xiaomi_hub, config_entry)
Definition: sensor.py:167
None async_setup_entry(HomeAssistant hass, ConfigEntry config_entry, AddEntitiesCallback async_add_entities)
Definition: sensor.py:89