Home Assistant Unofficial Reference 2024.12.1
sensor.py
Go to the documentation of this file.
1 """Support for xiaomi ble sensors."""
2 
3 from __future__ import annotations
4 
5 from typing import cast
6 
7 from xiaomi_ble import DeviceClass, SensorUpdate, Units
8 from xiaomi_ble.parser import ExtendedSensorDeviceClass
9 
11  PassiveBluetoothDataUpdate,
12  PassiveBluetoothProcessorEntity,
13 )
15  SensorDeviceClass,
16  SensorEntity,
17  SensorEntityDescription,
18  SensorStateClass,
19 )
20 from homeassistant.const import (
21  CONCENTRATION_MILLIGRAMS_PER_CUBIC_METER,
22  LIGHT_LUX,
23  PERCENTAGE,
24  SIGNAL_STRENGTH_DECIBELS_MILLIWATT,
25  EntityCategory,
26  UnitOfConductivity,
27  UnitOfElectricPotential,
28  UnitOfMass,
29  UnitOfPressure,
30  UnitOfTemperature,
31  UnitOfTime,
32 )
33 from homeassistant.core import HomeAssistant
34 from homeassistant.helpers.entity_platform import AddEntitiesCallback
35 from homeassistant.helpers.sensor import sensor_device_info_to_hass_device_info
36 
37 from .coordinator import XiaomiPassiveBluetoothDataProcessor
38 from .device import device_key_to_bluetooth_entity_key
39 from .types import XiaomiBLEConfigEntry
40 
41 SENSOR_DESCRIPTIONS = {
42  (DeviceClass.BATTERY, Units.PERCENTAGE): SensorEntityDescription(
43  key=f"{DeviceClass.BATTERY}_{Units.PERCENTAGE}",
44  device_class=SensorDeviceClass.BATTERY,
45  native_unit_of_measurement=PERCENTAGE,
46  state_class=SensorStateClass.MEASUREMENT,
47  entity_category=EntityCategory.DIAGNOSTIC,
48  ),
49  (DeviceClass.CONDUCTIVITY, Units.CONDUCTIVITY): SensorEntityDescription(
50  key=str(Units.CONDUCTIVITY),
51  device_class=SensorDeviceClass.CONDUCTIVITY,
52  native_unit_of_measurement=UnitOfConductivity.MICROSIEMENS_PER_CM,
53  state_class=SensorStateClass.MEASUREMENT,
54  ),
55  (
56  DeviceClass.FORMALDEHYDE,
57  Units.CONCENTRATION_MILLIGRAMS_PER_CUBIC_METER,
59  key=f"{DeviceClass.FORMALDEHYDE}_{Units.CONCENTRATION_MILLIGRAMS_PER_CUBIC_METER}",
60  native_unit_of_measurement=CONCENTRATION_MILLIGRAMS_PER_CUBIC_METER,
61  state_class=SensorStateClass.MEASUREMENT,
62  ),
63  (DeviceClass.HUMIDITY, Units.PERCENTAGE): SensorEntityDescription(
64  key=f"{DeviceClass.HUMIDITY}_{Units.PERCENTAGE}",
65  device_class=SensorDeviceClass.HUMIDITY,
66  native_unit_of_measurement=PERCENTAGE,
67  state_class=SensorStateClass.MEASUREMENT,
68  ),
69  (DeviceClass.ILLUMINANCE, Units.LIGHT_LUX): SensorEntityDescription(
70  key=f"{DeviceClass.ILLUMINANCE}_{Units.LIGHT_LUX}",
71  device_class=SensorDeviceClass.ILLUMINANCE,
72  native_unit_of_measurement=LIGHT_LUX,
73  state_class=SensorStateClass.MEASUREMENT,
74  ),
75  # Impedance sensor (ohm)
76  (DeviceClass.IMPEDANCE, Units.OHM): SensorEntityDescription(
77  key=f"{DeviceClass.IMPEDANCE}_{Units.OHM}",
78  icon="mdi:omega",
79  native_unit_of_measurement=Units.OHM,
80  state_class=SensorStateClass.MEASUREMENT,
81  ),
82  # Mass sensor (kg)
83  (DeviceClass.MASS, Units.MASS_KILOGRAMS): SensorEntityDescription(
84  key=f"{DeviceClass.MASS}_{Units.MASS_KILOGRAMS}",
85  device_class=SensorDeviceClass.WEIGHT,
86  native_unit_of_measurement=UnitOfMass.KILOGRAMS,
87  state_class=SensorStateClass.MEASUREMENT,
88  ),
89  # Mass non stabilized sensor (kg)
90  (DeviceClass.MASS_NON_STABILIZED, Units.MASS_KILOGRAMS): SensorEntityDescription(
91  key=f"{DeviceClass.MASS_NON_STABILIZED}_{Units.MASS_KILOGRAMS}",
92  device_class=SensorDeviceClass.WEIGHT,
93  native_unit_of_measurement=UnitOfMass.KILOGRAMS,
94  state_class=SensorStateClass.MEASUREMENT,
95  entity_category=EntityCategory.DIAGNOSTIC,
96  ),
97  (DeviceClass.MOISTURE, Units.PERCENTAGE): SensorEntityDescription(
98  key=f"{DeviceClass.MOISTURE}_{Units.PERCENTAGE}",
99  device_class=SensorDeviceClass.MOISTURE,
100  native_unit_of_measurement=PERCENTAGE,
101  state_class=SensorStateClass.MEASUREMENT,
102  ),
103  (DeviceClass.PRESSURE, Units.PRESSURE_MBAR): SensorEntityDescription(
104  key=f"{DeviceClass.PRESSURE}_{Units.PRESSURE_MBAR}",
105  device_class=SensorDeviceClass.PRESSURE,
106  native_unit_of_measurement=UnitOfPressure.MBAR,
107  state_class=SensorStateClass.MEASUREMENT,
108  ),
109  (
110  DeviceClass.SIGNAL_STRENGTH,
111  Units.SIGNAL_STRENGTH_DECIBELS_MILLIWATT,
113  key=f"{DeviceClass.SIGNAL_STRENGTH}_{Units.SIGNAL_STRENGTH_DECIBELS_MILLIWATT}",
114  device_class=SensorDeviceClass.SIGNAL_STRENGTH,
115  native_unit_of_measurement=SIGNAL_STRENGTH_DECIBELS_MILLIWATT,
116  state_class=SensorStateClass.MEASUREMENT,
117  entity_registry_enabled_default=False,
118  entity_category=EntityCategory.DIAGNOSTIC,
119  ),
120  (DeviceClass.TEMPERATURE, Units.TEMP_CELSIUS): SensorEntityDescription(
121  key=f"{DeviceClass.TEMPERATURE}_{Units.TEMP_CELSIUS}",
122  device_class=SensorDeviceClass.TEMPERATURE,
123  native_unit_of_measurement=UnitOfTemperature.CELSIUS,
124  state_class=SensorStateClass.MEASUREMENT,
125  ),
126  (DeviceClass.VOLTAGE, Units.ELECTRIC_POTENTIAL_VOLT): SensorEntityDescription(
127  key=f"{DeviceClass.VOLTAGE}_{Units.ELECTRIC_POTENTIAL_VOLT}",
128  device_class=SensorDeviceClass.VOLTAGE,
129  native_unit_of_measurement=UnitOfElectricPotential.VOLT,
130  state_class=SensorStateClass.MEASUREMENT,
131  entity_category=EntityCategory.DIAGNOSTIC,
132  ),
133  # E.g. consumable sensor on WX08ZM and M1S-T500
134  (ExtendedSensorDeviceClass.CONSUMABLE, Units.PERCENTAGE): SensorEntityDescription(
135  key=str(ExtendedSensorDeviceClass.CONSUMABLE),
136  native_unit_of_measurement=PERCENTAGE,
137  state_class=SensorStateClass.MEASUREMENT,
138  ),
139  # Score after brushing with a toothbrush
140  (ExtendedSensorDeviceClass.SCORE, None): SensorEntityDescription(
141  key=str(ExtendedSensorDeviceClass.SCORE),
142  state_class=SensorStateClass.MEASUREMENT,
143  ),
144  # Counting during brushing
145  (ExtendedSensorDeviceClass.COUNTER, Units.TIME_SECONDS): SensorEntityDescription(
146  key=str(ExtendedSensorDeviceClass.COUNTER),
147  native_unit_of_measurement=UnitOfTime.SECONDS,
148  state_class=SensorStateClass.MEASUREMENT,
149  ),
150  # Key id for locks and fingerprint readers
151  (ExtendedSensorDeviceClass.KEY_ID, None): SensorEntityDescription(
152  key=str(ExtendedSensorDeviceClass.KEY_ID), icon="mdi:identifier"
153  ),
154  # Lock method for locks
155  (ExtendedSensorDeviceClass.LOCK_METHOD, None): SensorEntityDescription(
156  key=str(ExtendedSensorDeviceClass.LOCK_METHOD), icon="mdi:key-variant"
157  ),
158  # Duration of detected status (in minutes) for Occpancy Sensor
159  (
160  ExtendedSensorDeviceClass.DURATION_DETECTED,
161  Units.TIME_MINUTES,
163  key=str(ExtendedSensorDeviceClass.DURATION_DETECTED),
164  native_unit_of_measurement=UnitOfTime.MINUTES,
165  state_class=SensorStateClass.MEASUREMENT,
166  ),
167  # Duration of cleared status (in minutes) for Occpancy Sensor
168  (
169  ExtendedSensorDeviceClass.DURATION_CLEARED,
170  Units.TIME_MINUTES,
172  key=str(ExtendedSensorDeviceClass.DURATION_CLEARED),
173  native_unit_of_measurement=UnitOfTime.MINUTES,
174  state_class=SensorStateClass.MEASUREMENT,
175  ),
176 }
177 
178 
180  sensor_update: SensorUpdate,
181 ) -> PassiveBluetoothDataUpdate[float | None]:
182  """Convert a sensor update to a bluetooth data update."""
184  devices={
185  device_id: sensor_device_info_to_hass_device_info(device_info)
186  for device_id, device_info in sensor_update.devices.items()
187  },
188  entity_descriptions={
189  device_key_to_bluetooth_entity_key(device_key): SENSOR_DESCRIPTIONS[
190  (description.device_class, description.native_unit_of_measurement)
191  ]
192  for device_key, description in sensor_update.entity_descriptions.items()
193  if description.device_class
194  },
195  entity_data={
196  device_key_to_bluetooth_entity_key(device_key): cast(
197  float | None, sensor_values.native_value
198  )
199  for device_key, sensor_values in sensor_update.entity_values.items()
200  },
201  entity_names={
202  device_key_to_bluetooth_entity_key(device_key): sensor_values.name
203  for device_key, sensor_values in sensor_update.entity_values.items()
204  },
205  )
206 
207 
209  hass: HomeAssistant,
210  entry: XiaomiBLEConfigEntry,
211  async_add_entities: AddEntitiesCallback,
212 ) -> None:
213  """Set up the Xiaomi BLE sensors."""
214  coordinator = entry.runtime_data
216  sensor_update_to_bluetooth_data_update
217  )
218  entry.async_on_unload(
219  processor.async_add_entities_listener(
220  XiaomiBluetoothSensorEntity, async_add_entities
221  )
222  )
223  entry.async_on_unload(
224  coordinator.async_register_processor(processor, SensorEntityDescription)
225  )
226 
227 
229  PassiveBluetoothProcessorEntity[XiaomiPassiveBluetoothDataProcessor[float | None]],
230  SensorEntity,
231 ):
232  """Representation of a xiaomi ble sensor."""
233 
234  @property
235  def native_value(self) -> int | float | None:
236  """Return the native value."""
237  return self.processor.entity_data.get(self.entity_key)
238 
239  @property
240  def available(self) -> bool:
241  """Return True if entity is available."""
242  return self.processor.coordinator.sleepy_device or super().available
PassiveBluetoothEntityKey device_key_to_bluetooth_entity_key(DeviceKey device_key)
Definition: device.py:14
PassiveBluetoothDataUpdate[float|None] sensor_update_to_bluetooth_data_update(SensorUpdate sensor_update)
Definition: sensor.py:181
None async_setup_entry(HomeAssistant hass, XiaomiBLEConfigEntry entry, AddEntitiesCallback async_add_entities)
Definition: sensor.py:212
DeviceInfo sensor_device_info_to_hass_device_info(SensorDeviceInfo sensor_device_info)
Definition: sensor.py:20