Home Assistant Unofficial Reference 2024.12.1
sensor.py
Go to the documentation of this file.
1 """Support for BTHome sensors."""
2 
3 from __future__ import annotations
4 
5 from typing import cast
6 
7 from bthome_ble import SensorDeviceClass as BTHomeSensorDeviceClass, SensorUpdate, Units
8 from bthome_ble.const import (
9  ExtendedSensorDeviceClass as BTHomeExtendedSensorDeviceClass,
10 )
11 
13  PassiveBluetoothDataUpdate,
14  PassiveBluetoothProcessorEntity,
15 )
17  SensorDeviceClass,
18  SensorEntity,
19  SensorEntityDescription,
20  SensorStateClass,
21 )
22 from homeassistant.const import (
23  CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
24  CONCENTRATION_PARTS_PER_MILLION,
25  DEGREE,
26  LIGHT_LUX,
27  PERCENTAGE,
28  SIGNAL_STRENGTH_DECIBELS_MILLIWATT,
29  EntityCategory,
30  UnitOfConductivity,
31  UnitOfElectricCurrent,
32  UnitOfElectricPotential,
33  UnitOfEnergy,
34  UnitOfLength,
35  UnitOfMass,
36  UnitOfPower,
37  UnitOfPressure,
38  UnitOfSpeed,
39  UnitOfTemperature,
40  UnitOfTime,
41  UnitOfVolume,
42  UnitOfVolumeFlowRate,
43 )
44 from homeassistant.core import HomeAssistant
45 from homeassistant.helpers.entity_platform import AddEntitiesCallback
46 from homeassistant.helpers.sensor import sensor_device_info_to_hass_device_info
47 
48 from .coordinator import BTHomePassiveBluetoothDataProcessor
49 from .device import device_key_to_bluetooth_entity_key
50 from .types import BTHomeConfigEntry
51 
52 SENSOR_DESCRIPTIONS = {
53  # Acceleration (m/s²)
54  (
55  BTHomeSensorDeviceClass.ACCELERATION,
56  Units.ACCELERATION_METERS_PER_SQUARE_SECOND,
58  key=f"{BTHomeSensorDeviceClass.ACCELERATION}_{Units.ACCELERATION_METERS_PER_SQUARE_SECOND}",
59  native_unit_of_measurement=Units.ACCELERATION_METERS_PER_SQUARE_SECOND,
60  state_class=SensorStateClass.MEASUREMENT,
61  ),
62  # Battery (percent)
63  (BTHomeSensorDeviceClass.BATTERY, Units.PERCENTAGE): SensorEntityDescription(
64  key=f"{BTHomeSensorDeviceClass.BATTERY}_{Units.PERCENTAGE}",
65  device_class=SensorDeviceClass.BATTERY,
66  native_unit_of_measurement=PERCENTAGE,
67  state_class=SensorStateClass.MEASUREMENT,
68  entity_category=EntityCategory.DIAGNOSTIC,
69  ),
70  # Count (-)
71  (BTHomeSensorDeviceClass.COUNT, None): SensorEntityDescription(
72  key=str(BTHomeSensorDeviceClass.COUNT),
73  state_class=SensorStateClass.MEASUREMENT,
74  ),
75  # CO2 (parts per million)
76  (
77  BTHomeSensorDeviceClass.CO2,
78  Units.CONCENTRATION_PARTS_PER_MILLION,
80  key=f"{BTHomeSensorDeviceClass.CO2}_{Units.CONCENTRATION_PARTS_PER_MILLION}",
81  device_class=SensorDeviceClass.CO2,
82  native_unit_of_measurement=CONCENTRATION_PARTS_PER_MILLION,
83  state_class=SensorStateClass.MEASUREMENT,
84  ),
85  # Current (Ampere)
86  (
87  BTHomeSensorDeviceClass.CURRENT,
88  Units.ELECTRIC_CURRENT_AMPERE,
90  key=f"{BTHomeSensorDeviceClass.CURRENT}_{Units.ELECTRIC_CURRENT_AMPERE}",
91  device_class=SensorDeviceClass.CURRENT,
92  native_unit_of_measurement=UnitOfElectricCurrent.AMPERE,
93  state_class=SensorStateClass.MEASUREMENT,
94  ),
95  # Dew Point (°C)
96  (BTHomeSensorDeviceClass.DEW_POINT, Units.TEMP_CELSIUS): SensorEntityDescription(
97  key=f"{BTHomeSensorDeviceClass.DEW_POINT}_{Units.TEMP_CELSIUS}",
98  device_class=SensorDeviceClass.TEMPERATURE,
99  native_unit_of_measurement=UnitOfTemperature.CELSIUS,
100  state_class=SensorStateClass.MEASUREMENT,
101  ),
102  # Distance (mm)
103  (
104  BTHomeSensorDeviceClass.DISTANCE,
105  Units.LENGTH_MILLIMETERS,
107  key=f"{BTHomeSensorDeviceClass.DISTANCE}_{Units.LENGTH_MILLIMETERS}",
108  device_class=SensorDeviceClass.DISTANCE,
109  native_unit_of_measurement=UnitOfLength.MILLIMETERS,
110  state_class=SensorStateClass.MEASUREMENT,
111  ),
112  # Distance (m)
113  (BTHomeSensorDeviceClass.DISTANCE, Units.LENGTH_METERS): SensorEntityDescription(
114  key=f"{BTHomeSensorDeviceClass.DISTANCE}_{Units.LENGTH_METERS}",
115  device_class=SensorDeviceClass.DISTANCE,
116  native_unit_of_measurement=UnitOfLength.METERS,
117  state_class=SensorStateClass.MEASUREMENT,
118  ),
119  # Duration (seconds)
120  (BTHomeSensorDeviceClass.DURATION, Units.TIME_SECONDS): SensorEntityDescription(
121  key=f"{BTHomeSensorDeviceClass.DURATION}_{Units.TIME_SECONDS}",
122  device_class=SensorDeviceClass.DURATION,
123  native_unit_of_measurement=UnitOfTime.SECONDS,
124  state_class=SensorStateClass.MEASUREMENT,
125  ),
126  # Energy (kWh)
127  (
128  BTHomeSensorDeviceClass.ENERGY,
129  Units.ENERGY_KILO_WATT_HOUR,
131  key=f"{BTHomeSensorDeviceClass.ENERGY}_{Units.ENERGY_KILO_WATT_HOUR}",
132  device_class=SensorDeviceClass.ENERGY,
133  native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR,
134  state_class=SensorStateClass.TOTAL,
135  ),
136  # Gas (m3)
137  (
138  BTHomeSensorDeviceClass.GAS,
139  Units.VOLUME_CUBIC_METERS,
141  key=f"{BTHomeSensorDeviceClass.GAS}_{Units.VOLUME_CUBIC_METERS}",
142  device_class=SensorDeviceClass.GAS,
143  native_unit_of_measurement=UnitOfVolume.CUBIC_METERS,
144  state_class=SensorStateClass.TOTAL,
145  ),
146  # Gyroscope (°/s)
147  (
148  BTHomeSensorDeviceClass.GYROSCOPE,
149  Units.GYROSCOPE_DEGREES_PER_SECOND,
151  key=f"{BTHomeSensorDeviceClass.GYROSCOPE}_{Units.GYROSCOPE_DEGREES_PER_SECOND}",
152  native_unit_of_measurement=Units.GYROSCOPE_DEGREES_PER_SECOND,
153  state_class=SensorStateClass.MEASUREMENT,
154  ),
155  # Humidity in (percent)
156  (BTHomeSensorDeviceClass.HUMIDITY, Units.PERCENTAGE): SensorEntityDescription(
157  key=f"{BTHomeSensorDeviceClass.HUMIDITY}_{Units.PERCENTAGE}",
158  device_class=SensorDeviceClass.HUMIDITY,
159  native_unit_of_measurement=PERCENTAGE,
160  state_class=SensorStateClass.MEASUREMENT,
161  ),
162  # Illuminance (lux)
163  (BTHomeSensorDeviceClass.ILLUMINANCE, Units.LIGHT_LUX): SensorEntityDescription(
164  key=f"{BTHomeSensorDeviceClass.ILLUMINANCE}_{Units.LIGHT_LUX}",
165  device_class=SensorDeviceClass.ILLUMINANCE,
166  native_unit_of_measurement=LIGHT_LUX,
167  state_class=SensorStateClass.MEASUREMENT,
168  ),
169  # Mass sensor (kg)
170  (BTHomeSensorDeviceClass.MASS, Units.MASS_KILOGRAMS): SensorEntityDescription(
171  key=f"{BTHomeSensorDeviceClass.MASS}_{Units.MASS_KILOGRAMS}",
172  device_class=SensorDeviceClass.WEIGHT,
173  native_unit_of_measurement=UnitOfMass.KILOGRAMS,
174  state_class=SensorStateClass.MEASUREMENT,
175  ),
176  # Mass sensor (lb)
177  (BTHomeSensorDeviceClass.MASS, Units.MASS_POUNDS): SensorEntityDescription(
178  key=f"{BTHomeSensorDeviceClass.MASS}_{Units.MASS_POUNDS}",
179  device_class=SensorDeviceClass.WEIGHT,
180  native_unit_of_measurement=UnitOfMass.POUNDS,
181  state_class=SensorStateClass.MEASUREMENT,
182  ),
183  # Moisture (percent)
184  (BTHomeSensorDeviceClass.MOISTURE, Units.PERCENTAGE): SensorEntityDescription(
185  key=f"{BTHomeSensorDeviceClass.MOISTURE}_{Units.PERCENTAGE}",
186  device_class=SensorDeviceClass.MOISTURE,
187  native_unit_of_measurement=PERCENTAGE,
188  state_class=SensorStateClass.MEASUREMENT,
189  ),
190  # Packet Id (-)
191  (BTHomeSensorDeviceClass.PACKET_ID, None): SensorEntityDescription(
192  key=str(BTHomeSensorDeviceClass.PACKET_ID),
193  state_class=SensorStateClass.MEASUREMENT,
194  entity_category=EntityCategory.DIAGNOSTIC,
195  entity_registry_enabled_default=False,
196  ),
197  # PM10 (µg/m3)
198  (
199  BTHomeSensorDeviceClass.PM10,
200  Units.CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
202  key=f"{BTHomeSensorDeviceClass.PM10}_{Units.CONCENTRATION_MICROGRAMS_PER_CUBIC_METER}",
203  device_class=SensorDeviceClass.PM10,
204  native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
205  state_class=SensorStateClass.MEASUREMENT,
206  ),
207  # PM2.5 (µg/m3)
208  (
209  BTHomeSensorDeviceClass.PM25,
210  Units.CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
212  key=f"{BTHomeSensorDeviceClass.PM25}_{Units.CONCENTRATION_MICROGRAMS_PER_CUBIC_METER}",
213  device_class=SensorDeviceClass.PM25,
214  native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
215  state_class=SensorStateClass.MEASUREMENT,
216  ),
217  # Power (Watt)
218  (BTHomeSensorDeviceClass.POWER, Units.POWER_WATT): SensorEntityDescription(
219  key=f"{BTHomeSensorDeviceClass.POWER}_{Units.POWER_WATT}",
220  device_class=SensorDeviceClass.POWER,
221  native_unit_of_measurement=UnitOfPower.WATT,
222  state_class=SensorStateClass.MEASUREMENT,
223  ),
224  # Pressure (mbar)
225  (BTHomeSensorDeviceClass.PRESSURE, Units.PRESSURE_MBAR): SensorEntityDescription(
226  key=f"{BTHomeSensorDeviceClass.PRESSURE}_{Units.PRESSURE_MBAR}",
227  device_class=SensorDeviceClass.PRESSURE,
228  native_unit_of_measurement=UnitOfPressure.MBAR,
229  state_class=SensorStateClass.MEASUREMENT,
230  ),
231  # Raw (-)
232  (BTHomeExtendedSensorDeviceClass.RAW, None): SensorEntityDescription(
233  key=str(BTHomeExtendedSensorDeviceClass.RAW),
234  ),
235  # Rotation (°)
236  (BTHomeSensorDeviceClass.ROTATION, Units.DEGREE): SensorEntityDescription(
237  key=f"{BTHomeSensorDeviceClass.ROTATION}_{Units.DEGREE}",
238  native_unit_of_measurement=DEGREE,
239  state_class=SensorStateClass.MEASUREMENT,
240  ),
241  # Signal Strength (RSSI) (dB)
242  (
243  BTHomeSensorDeviceClass.SIGNAL_STRENGTH,
244  Units.SIGNAL_STRENGTH_DECIBELS_MILLIWATT,
246  key=f"{BTHomeSensorDeviceClass.SIGNAL_STRENGTH}_{Units.SIGNAL_STRENGTH_DECIBELS_MILLIWATT}",
247  device_class=SensorDeviceClass.SIGNAL_STRENGTH,
248  native_unit_of_measurement=SIGNAL_STRENGTH_DECIBELS_MILLIWATT,
249  state_class=SensorStateClass.MEASUREMENT,
250  entity_category=EntityCategory.DIAGNOSTIC,
251  entity_registry_enabled_default=False,
252  ),
253  # Speed (m/s)
254  (
255  BTHomeSensorDeviceClass.SPEED,
256  Units.SPEED_METERS_PER_SECOND,
258  key=f"{BTHomeSensorDeviceClass.SPEED}_{Units.SPEED_METERS_PER_SECOND}",
259  device_class=SensorDeviceClass.SPEED,
260  native_unit_of_measurement=UnitOfSpeed.METERS_PER_SECOND,
261  state_class=SensorStateClass.MEASUREMENT,
262  ),
263  # Temperature (°C)
264  (BTHomeSensorDeviceClass.TEMPERATURE, Units.TEMP_CELSIUS): SensorEntityDescription(
265  key=f"{BTHomeSensorDeviceClass.TEMPERATURE}_{Units.TEMP_CELSIUS}",
266  device_class=SensorDeviceClass.TEMPERATURE,
267  native_unit_of_measurement=UnitOfTemperature.CELSIUS,
268  state_class=SensorStateClass.MEASUREMENT,
269  ),
270  # Text (-)
271  (BTHomeExtendedSensorDeviceClass.TEXT, None): SensorEntityDescription(
272  key=str(BTHomeExtendedSensorDeviceClass.TEXT),
273  ),
274  # Timestamp (datetime object)
275  (
276  BTHomeSensorDeviceClass.TIMESTAMP,
277  None,
279  key=str(BTHomeSensorDeviceClass.TIMESTAMP),
280  device_class=SensorDeviceClass.TIMESTAMP,
281  ),
282  # UV index (-)
283  (
284  BTHomeSensorDeviceClass.UV_INDEX,
285  None,
287  key=str(BTHomeSensorDeviceClass.UV_INDEX),
288  state_class=SensorStateClass.MEASUREMENT,
289  ),
290  # Volatile organic Compounds (VOC) (µg/m3)
291  (
292  BTHomeSensorDeviceClass.VOLATILE_ORGANIC_COMPOUNDS,
293  Units.CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
295  key=f"{BTHomeSensorDeviceClass.VOLATILE_ORGANIC_COMPOUNDS}_{Units.CONCENTRATION_MICROGRAMS_PER_CUBIC_METER}",
296  device_class=SensorDeviceClass.VOLATILE_ORGANIC_COMPOUNDS,
297  native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
298  state_class=SensorStateClass.MEASUREMENT,
299  ),
300  # Voltage (volt)
301  (
302  BTHomeSensorDeviceClass.VOLTAGE,
303  Units.ELECTRIC_POTENTIAL_VOLT,
305  key=f"{BTHomeSensorDeviceClass.VOLTAGE}_{Units.ELECTRIC_POTENTIAL_VOLT}",
306  device_class=SensorDeviceClass.VOLTAGE,
307  native_unit_of_measurement=UnitOfElectricPotential.VOLT,
308  state_class=SensorStateClass.MEASUREMENT,
309  ),
310  # Volume (L)
311  (
312  BTHomeSensorDeviceClass.VOLUME,
313  Units.VOLUME_LITERS,
315  key=f"{BTHomeSensorDeviceClass.VOLUME}_{Units.VOLUME_LITERS}",
316  device_class=SensorDeviceClass.VOLUME,
317  native_unit_of_measurement=UnitOfVolume.LITERS,
318  state_class=SensorStateClass.TOTAL,
319  ),
320  # Volume (mL)
321  (
322  BTHomeSensorDeviceClass.VOLUME,
323  Units.VOLUME_MILLILITERS,
325  key=f"{BTHomeSensorDeviceClass.VOLUME}_{Units.VOLUME_MILLILITERS}",
326  device_class=SensorDeviceClass.VOLUME,
327  native_unit_of_measurement=UnitOfVolume.MILLILITERS,
328  state_class=SensorStateClass.TOTAL,
329  ),
330  # Volume Flow Rate (m3/hour)
331  (
332  BTHomeSensorDeviceClass.VOLUME_FLOW_RATE,
333  Units.VOLUME_FLOW_RATE_CUBIC_METERS_PER_HOUR,
335  key=f"{BTHomeSensorDeviceClass.VOLUME_FLOW_RATE}_{Units.VOLUME_FLOW_RATE_CUBIC_METERS_PER_HOUR}",
336  device_class=SensorDeviceClass.VOLUME_FLOW_RATE,
337  native_unit_of_measurement=UnitOfVolumeFlowRate.CUBIC_METERS_PER_HOUR,
338  state_class=SensorStateClass.MEASUREMENT,
339  ),
340  # Volume Storage (L)
341  (
342  BTHomeExtendedSensorDeviceClass.VOLUME_STORAGE,
343  Units.VOLUME_LITERS,
345  key=f"{BTHomeExtendedSensorDeviceClass.VOLUME_STORAGE}_{Units.VOLUME_LITERS}",
346  device_class=SensorDeviceClass.VOLUME_STORAGE,
347  native_unit_of_measurement=UnitOfVolume.LITERS,
348  state_class=SensorStateClass.MEASUREMENT,
349  ),
350  # Water (L)
351  (
352  BTHomeSensorDeviceClass.WATER,
353  Units.VOLUME_LITERS,
355  key=f"{BTHomeSensorDeviceClass.WATER}_{Units.VOLUME_LITERS}",
356  device_class=SensorDeviceClass.WATER,
357  native_unit_of_measurement=UnitOfVolume.LITERS,
358  state_class=SensorStateClass.TOTAL,
359  ),
360  # Conductivity (µS/cm)
361  (
362  BTHomeSensorDeviceClass.CONDUCTIVITY,
363  Units.CONDUCTIVITY,
365  key=f"{BTHomeSensorDeviceClass.CONDUCTIVITY}_{Units.CONDUCTIVITY}",
366  device_class=SensorDeviceClass.CONDUCTIVITY,
367  native_unit_of_measurement=UnitOfConductivity.MICROSIEMENS_PER_CM,
368  state_class=SensorStateClass.MEASUREMENT,
369  ),
370 }
371 
372 
374  sensor_update: SensorUpdate,
375 ) -> PassiveBluetoothDataUpdate[float | None]:
376  """Convert a sensor update to a bluetooth data update."""
378  devices={
379  device_id: sensor_device_info_to_hass_device_info(device_info)
380  for device_id, device_info in sensor_update.devices.items()
381  },
382  entity_descriptions={
383  device_key_to_bluetooth_entity_key(device_key): SENSOR_DESCRIPTIONS[
384  (description.device_class, description.native_unit_of_measurement)
385  ]
386  for device_key, description in sensor_update.entity_descriptions.items()
387  if description.device_class
388  },
389  entity_data={
390  device_key_to_bluetooth_entity_key(device_key): cast(
391  float | None, sensor_values.native_value
392  )
393  for device_key, sensor_values in sensor_update.entity_values.items()
394  },
395  entity_names={
396  device_key_to_bluetooth_entity_key(device_key): sensor_values.name
397  for device_key, sensor_values in sensor_update.entity_values.items()
398  },
399  )
400 
401 
403  hass: HomeAssistant,
404  entry: BTHomeConfigEntry,
405  async_add_entities: AddEntitiesCallback,
406 ) -> None:
407  """Set up the BTHome BLE sensors."""
408  coordinator = entry.runtime_data
410  sensor_update_to_bluetooth_data_update
411  )
412  entry.async_on_unload(
413  processor.async_add_entities_listener(
414  BTHomeBluetoothSensorEntity, async_add_entities
415  )
416  )
417  entry.async_on_unload(
418  coordinator.async_register_processor(processor, SensorEntityDescription)
419  )
420 
421 
423  PassiveBluetoothProcessorEntity[BTHomePassiveBluetoothDataProcessor[float | None]],
424  SensorEntity,
425 ):
426  """Representation of a BTHome BLE sensor."""
427 
428  @property
429  def native_value(self) -> int | float | None:
430  """Return the native value."""
431  return self.processor.entity_data.get(self.entity_key)
432 
433  @property
434  def available(self) -> bool:
435  """Return True if entity is available."""
436  return self.processor.coordinator.sleepy_device or super().available
PassiveBluetoothEntityKey device_key_to_bluetooth_entity_key(DeviceKey device_key)
Definition: device.py:14
None async_setup_entry(HomeAssistant hass, BTHomeConfigEntry entry, AddEntitiesCallback async_add_entities)
Definition: sensor.py:406
PassiveBluetoothDataUpdate[float|None] sensor_update_to_bluetooth_data_update(SensorUpdate sensor_update)
Definition: sensor.py:375
DeviceInfo sensor_device_info_to_hass_device_info(SensorDeviceInfo sensor_device_info)
Definition: sensor.py:20