Home Assistant Unofficial Reference 2024.12.1
sensor.py
Go to the documentation of this file.
1 """Support for sensors through the SmartThings cloud API."""
2 
3 from __future__ import annotations
4 
5 from collections.abc import Sequence
6 from typing import NamedTuple
7 
8 from pysmartthings import Attribute, Capability
9 from pysmartthings.device import DeviceEntity
10 
12  SensorDeviceClass,
13  SensorEntity,
14  SensorStateClass,
15 )
16 from homeassistant.config_entries import ConfigEntry
17 from homeassistant.const import (
18  CONCENTRATION_PARTS_PER_MILLION,
19  LIGHT_LUX,
20  PERCENTAGE,
21  EntityCategory,
22  UnitOfArea,
23  UnitOfElectricPotential,
24  UnitOfEnergy,
25  UnitOfMass,
26  UnitOfPower,
27  UnitOfTemperature,
28  UnitOfVolume,
29 )
30 from homeassistant.core import HomeAssistant
31 from homeassistant.helpers.entity_platform import AddEntitiesCallback
32 from homeassistant.util import dt as dt_util
33 
34 from .const import DATA_BROKERS, DOMAIN
35 from .entity import SmartThingsEntity
36 
37 
38 class Map(NamedTuple):
39  """Tuple for mapping Smartthings capabilities to Home Assistant sensors."""
40 
41  attribute: str
42  name: str
43  default_unit: str | None
44  device_class: SensorDeviceClass | None
45  state_class: SensorStateClass | None
46  entity_category: EntityCategory | None
47 
48 
49 CAPABILITY_TO_SENSORS: dict[str, list[Map]] = {
50  Capability.activity_lighting_mode: [
51  Map(
52  Attribute.lighting_mode,
53  "Activity Lighting Mode",
54  None,
55  None,
56  None,
57  EntityCategory.DIAGNOSTIC,
58  )
59  ],
60  Capability.air_conditioner_mode: [
61  Map(
62  Attribute.air_conditioner_mode,
63  "Air Conditioner Mode",
64  None,
65  None,
66  None,
67  EntityCategory.DIAGNOSTIC,
68  )
69  ],
70  Capability.air_quality_sensor: [
71  Map(
72  Attribute.air_quality,
73  "Air Quality",
74  "CAQI",
75  None,
76  SensorStateClass.MEASUREMENT,
77  None,
78  )
79  ],
80  Capability.alarm: [Map(Attribute.alarm, "Alarm", None, None, None, None)],
81  Capability.audio_volume: [
82  Map(Attribute.volume, "Volume", PERCENTAGE, None, None, None)
83  ],
84  Capability.battery: [
85  Map(
86  Attribute.battery,
87  "Battery",
88  PERCENTAGE,
89  SensorDeviceClass.BATTERY,
90  None,
91  EntityCategory.DIAGNOSTIC,
92  )
93  ],
94  Capability.body_mass_index_measurement: [
95  Map(
96  Attribute.bmi_measurement,
97  "Body Mass Index",
98  f"{UnitOfMass.KILOGRAMS}/{UnitOfArea.SQUARE_METERS}",
99  None,
100  SensorStateClass.MEASUREMENT,
101  None,
102  )
103  ],
104  Capability.body_weight_measurement: [
105  Map(
106  Attribute.body_weight_measurement,
107  "Body Weight",
108  UnitOfMass.KILOGRAMS,
109  SensorDeviceClass.WEIGHT,
110  SensorStateClass.MEASUREMENT,
111  None,
112  )
113  ],
114  Capability.carbon_dioxide_measurement: [
115  Map(
116  Attribute.carbon_dioxide,
117  "Carbon Dioxide Measurement",
118  CONCENTRATION_PARTS_PER_MILLION,
119  SensorDeviceClass.CO2,
120  SensorStateClass.MEASUREMENT,
121  None,
122  )
123  ],
124  Capability.carbon_monoxide_detector: [
125  Map(
126  Attribute.carbon_monoxide,
127  "Carbon Monoxide Detector",
128  None,
129  None,
130  None,
131  None,
132  )
133  ],
134  Capability.carbon_monoxide_measurement: [
135  Map(
136  Attribute.carbon_monoxide_level,
137  "Carbon Monoxide Measurement",
138  CONCENTRATION_PARTS_PER_MILLION,
139  SensorDeviceClass.CO,
140  SensorStateClass.MEASUREMENT,
141  None,
142  )
143  ],
144  Capability.dishwasher_operating_state: [
145  Map(
146  Attribute.machine_state, "Dishwasher Machine State", None, None, None, None
147  ),
148  Map(
149  Attribute.dishwasher_job_state,
150  "Dishwasher Job State",
151  None,
152  None,
153  None,
154  None,
155  ),
156  Map(
157  Attribute.completion_time,
158  "Dishwasher Completion Time",
159  None,
160  SensorDeviceClass.TIMESTAMP,
161  None,
162  None,
163  ),
164  ],
165  Capability.dryer_mode: [
166  Map(
167  Attribute.dryer_mode,
168  "Dryer Mode",
169  None,
170  None,
171  None,
172  EntityCategory.DIAGNOSTIC,
173  )
174  ],
175  Capability.dryer_operating_state: [
176  Map(Attribute.machine_state, "Dryer Machine State", None, None, None, None),
177  Map(Attribute.dryer_job_state, "Dryer Job State", None, None, None, None),
178  Map(
179  Attribute.completion_time,
180  "Dryer Completion Time",
181  None,
182  SensorDeviceClass.TIMESTAMP,
183  None,
184  None,
185  ),
186  ],
187  Capability.dust_sensor: [
188  Map(
189  Attribute.fine_dust_level,
190  "Fine Dust Level",
191  None,
192  None,
193  SensorStateClass.MEASUREMENT,
194  None,
195  ),
196  Map(
197  Attribute.dust_level,
198  "Dust Level",
199  None,
200  None,
201  SensorStateClass.MEASUREMENT,
202  None,
203  ),
204  ],
205  Capability.energy_meter: [
206  Map(
207  Attribute.energy,
208  "Energy Meter",
209  UnitOfEnergy.KILO_WATT_HOUR,
210  SensorDeviceClass.ENERGY,
211  SensorStateClass.TOTAL_INCREASING,
212  None,
213  )
214  ],
215  Capability.equivalent_carbon_dioxide_measurement: [
216  Map(
217  Attribute.equivalent_carbon_dioxide_measurement,
218  "Equivalent Carbon Dioxide Measurement",
219  CONCENTRATION_PARTS_PER_MILLION,
220  SensorDeviceClass.CO2,
221  SensorStateClass.MEASUREMENT,
222  None,
223  )
224  ],
225  Capability.formaldehyde_measurement: [
226  Map(
227  Attribute.formaldehyde_level,
228  "Formaldehyde Measurement",
229  CONCENTRATION_PARTS_PER_MILLION,
230  None,
231  SensorStateClass.MEASUREMENT,
232  None,
233  )
234  ],
235  Capability.gas_meter: [
236  Map(
237  Attribute.gas_meter,
238  "Gas Meter",
239  UnitOfEnergy.KILO_WATT_HOUR,
240  SensorDeviceClass.ENERGY,
241  SensorStateClass.MEASUREMENT,
242  None,
243  ),
244  Map(
245  Attribute.gas_meter_calorific, "Gas Meter Calorific", None, None, None, None
246  ),
247  Map(
248  Attribute.gas_meter_time,
249  "Gas Meter Time",
250  None,
251  SensorDeviceClass.TIMESTAMP,
252  None,
253  None,
254  ),
255  Map(
256  Attribute.gas_meter_volume,
257  "Gas Meter Volume",
258  UnitOfVolume.CUBIC_METERS,
259  SensorDeviceClass.GAS,
260  SensorStateClass.MEASUREMENT,
261  None,
262  ),
263  ],
264  Capability.illuminance_measurement: [
265  Map(
266  Attribute.illuminance,
267  "Illuminance",
268  LIGHT_LUX,
269  SensorDeviceClass.ILLUMINANCE,
270  SensorStateClass.MEASUREMENT,
271  None,
272  )
273  ],
274  Capability.infrared_level: [
275  Map(
276  Attribute.infrared_level,
277  "Infrared Level",
278  PERCENTAGE,
279  None,
280  SensorStateClass.MEASUREMENT,
281  None,
282  )
283  ],
284  Capability.media_input_source: [
285  Map(Attribute.input_source, "Media Input Source", None, None, None, None)
286  ],
287  Capability.media_playback_repeat: [
288  Map(
289  Attribute.playback_repeat_mode,
290  "Media Playback Repeat",
291  None,
292  None,
293  None,
294  None,
295  )
296  ],
297  Capability.media_playback_shuffle: [
298  Map(
299  Attribute.playback_shuffle, "Media Playback Shuffle", None, None, None, None
300  )
301  ],
302  Capability.media_playback: [
303  Map(Attribute.playback_status, "Media Playback Status", None, None, None, None)
304  ],
305  Capability.odor_sensor: [
306  Map(Attribute.odor_level, "Odor Sensor", None, None, None, None)
307  ],
308  Capability.oven_mode: [
309  Map(
310  Attribute.oven_mode,
311  "Oven Mode",
312  None,
313  None,
314  None,
315  EntityCategory.DIAGNOSTIC,
316  )
317  ],
318  Capability.oven_operating_state: [
319  Map(Attribute.machine_state, "Oven Machine State", None, None, None, None),
320  Map(Attribute.oven_job_state, "Oven Job State", None, None, None, None),
321  Map(Attribute.completion_time, "Oven Completion Time", None, None, None, None),
322  ],
323  Capability.oven_setpoint: [
324  Map(Attribute.oven_setpoint, "Oven Set Point", None, None, None, None)
325  ],
326  Capability.power_consumption_report: [],
327  Capability.power_meter: [
328  Map(
329  Attribute.power,
330  "Power Meter",
331  UnitOfPower.WATT,
332  SensorDeviceClass.POWER,
333  SensorStateClass.MEASUREMENT,
334  None,
335  )
336  ],
337  Capability.power_source: [
338  Map(
339  Attribute.power_source,
340  "Power Source",
341  None,
342  None,
343  None,
344  EntityCategory.DIAGNOSTIC,
345  )
346  ],
347  Capability.refrigeration_setpoint: [
348  Map(
349  Attribute.refrigeration_setpoint,
350  "Refrigeration Setpoint",
351  None,
352  SensorDeviceClass.TEMPERATURE,
353  None,
354  None,
355  )
356  ],
357  Capability.relative_humidity_measurement: [
358  Map(
359  Attribute.humidity,
360  "Relative Humidity Measurement",
361  PERCENTAGE,
362  SensorDeviceClass.HUMIDITY,
363  SensorStateClass.MEASUREMENT,
364  None,
365  )
366  ],
367  Capability.robot_cleaner_cleaning_mode: [
368  Map(
369  Attribute.robot_cleaner_cleaning_mode,
370  "Robot Cleaner Cleaning Mode",
371  None,
372  None,
373  None,
374  EntityCategory.DIAGNOSTIC,
375  )
376  ],
377  Capability.robot_cleaner_movement: [
378  Map(
379  Attribute.robot_cleaner_movement,
380  "Robot Cleaner Movement",
381  None,
382  None,
383  None,
384  None,
385  )
386  ],
387  Capability.robot_cleaner_turbo_mode: [
388  Map(
389  Attribute.robot_cleaner_turbo_mode,
390  "Robot Cleaner Turbo Mode",
391  None,
392  None,
393  None,
394  EntityCategory.DIAGNOSTIC,
395  )
396  ],
397  Capability.signal_strength: [
398  Map(
399  Attribute.lqi,
400  "LQI Signal Strength",
401  None,
402  None,
403  SensorStateClass.MEASUREMENT,
404  EntityCategory.DIAGNOSTIC,
405  ),
406  Map(
407  Attribute.rssi,
408  "RSSI Signal Strength",
409  None,
410  SensorDeviceClass.SIGNAL_STRENGTH,
411  SensorStateClass.MEASUREMENT,
412  EntityCategory.DIAGNOSTIC,
413  ),
414  ],
415  Capability.smoke_detector: [
416  Map(Attribute.smoke, "Smoke Detector", None, None, None, None)
417  ],
418  Capability.temperature_measurement: [
419  Map(
420  Attribute.temperature,
421  "Temperature Measurement",
422  None,
423  SensorDeviceClass.TEMPERATURE,
424  SensorStateClass.MEASUREMENT,
425  None,
426  )
427  ],
428  Capability.thermostat_cooling_setpoint: [
429  Map(
430  Attribute.cooling_setpoint,
431  "Thermostat Cooling Setpoint",
432  None,
433  SensorDeviceClass.TEMPERATURE,
434  None,
435  None,
436  )
437  ],
438  Capability.thermostat_fan_mode: [
439  Map(
440  Attribute.thermostat_fan_mode,
441  "Thermostat Fan Mode",
442  None,
443  None,
444  None,
445  EntityCategory.DIAGNOSTIC,
446  )
447  ],
448  Capability.thermostat_heating_setpoint: [
449  Map(
450  Attribute.heating_setpoint,
451  "Thermostat Heating Setpoint",
452  None,
453  SensorDeviceClass.TEMPERATURE,
454  None,
455  EntityCategory.DIAGNOSTIC,
456  )
457  ],
458  Capability.thermostat_mode: [
459  Map(
460  Attribute.thermostat_mode,
461  "Thermostat Mode",
462  None,
463  None,
464  None,
465  EntityCategory.DIAGNOSTIC,
466  )
467  ],
468  Capability.thermostat_operating_state: [
469  Map(
470  Attribute.thermostat_operating_state,
471  "Thermostat Operating State",
472  None,
473  None,
474  None,
475  None,
476  )
477  ],
478  Capability.thermostat_setpoint: [
479  Map(
480  Attribute.thermostat_setpoint,
481  "Thermostat Setpoint",
482  None,
483  SensorDeviceClass.TEMPERATURE,
484  None,
485  EntityCategory.DIAGNOSTIC,
486  )
487  ],
488  Capability.three_axis: [],
489  Capability.tv_channel: [
490  Map(Attribute.tv_channel, "Tv Channel", None, None, None, None),
491  Map(Attribute.tv_channel_name, "Tv Channel Name", None, None, None, None),
492  ],
493  Capability.tvoc_measurement: [
494  Map(
495  Attribute.tvoc_level,
496  "Tvoc Measurement",
497  CONCENTRATION_PARTS_PER_MILLION,
498  None,
499  SensorStateClass.MEASUREMENT,
500  None,
501  )
502  ],
503  Capability.ultraviolet_index: [
504  Map(
505  Attribute.ultraviolet_index,
506  "Ultraviolet Index",
507  None,
508  None,
509  SensorStateClass.MEASUREMENT,
510  None,
511  )
512  ],
513  Capability.voltage_measurement: [
514  Map(
515  Attribute.voltage,
516  "Voltage Measurement",
517  UnitOfElectricPotential.VOLT,
518  SensorDeviceClass.VOLTAGE,
519  SensorStateClass.MEASUREMENT,
520  None,
521  )
522  ],
523  Capability.washer_mode: [
524  Map(
525  Attribute.washer_mode,
526  "Washer Mode",
527  None,
528  None,
529  None,
530  EntityCategory.DIAGNOSTIC,
531  )
532  ],
533  Capability.washer_operating_state: [
534  Map(Attribute.machine_state, "Washer Machine State", None, None, None, None),
535  Map(Attribute.washer_job_state, "Washer Job State", None, None, None, None),
536  Map(
537  Attribute.completion_time,
538  "Washer Completion Time",
539  None,
540  SensorDeviceClass.TIMESTAMP,
541  None,
542  None,
543  ),
544  ],
545 }
546 
547 UNITS = {
548  "C": UnitOfTemperature.CELSIUS,
549  "F": UnitOfTemperature.FAHRENHEIT,
550  "lux": LIGHT_LUX,
551 }
552 
553 THREE_AXIS_NAMES = ["X Coordinate", "Y Coordinate", "Z Coordinate"]
554 POWER_CONSUMPTION_REPORT_NAMES = [
555  "energy",
556  "power",
557  "deltaEnergy",
558  "powerEnergy",
559  "energySaved",
560 ]
561 
562 
564  hass: HomeAssistant,
565  config_entry: ConfigEntry,
566  async_add_entities: AddEntitiesCallback,
567 ) -> None:
568  """Add sensors for a config entry."""
569  broker = hass.data[DOMAIN][DATA_BROKERS][config_entry.entry_id]
570  entities: list[SensorEntity] = []
571  for device in broker.devices.values():
572  for capability in broker.get_assigned(device.device_id, "sensor"):
573  if capability == Capability.three_axis:
574  entities.extend(
575  [
576  SmartThingsThreeAxisSensor(device, index)
577  for index in range(len(THREE_AXIS_NAMES))
578  ]
579  )
580  elif capability == Capability.power_consumption_report:
581  entities.extend(
582  [
583  SmartThingsPowerConsumptionSensor(device, report_name)
584  for report_name in POWER_CONSUMPTION_REPORT_NAMES
585  ]
586  )
587  else:
588  maps = CAPABILITY_TO_SENSORS[capability]
589  entities.extend(
590  [
592  device,
593  m.attribute,
594  m.name,
595  m.default_unit,
596  m.device_class,
597  m.state_class,
598  m.entity_category,
599  )
600  for m in maps
601  ]
602  )
603 
604  if broker.any_assigned(device.device_id, "switch"):
605  for capability in (Capability.energy_meter, Capability.power_meter):
606  maps = CAPABILITY_TO_SENSORS[capability]
607  entities.extend(
608  [
610  device,
611  m.attribute,
612  m.name,
613  m.default_unit,
614  m.device_class,
615  m.state_class,
616  m.entity_category,
617  )
618  for m in maps
619  ]
620  )
621 
622  async_add_entities(entities)
623 
624 
625 def get_capabilities(capabilities: Sequence[str]) -> Sequence[str] | None:
626  """Return all capabilities supported if minimum required are present."""
627  return [
628  capability for capability in CAPABILITY_TO_SENSORS if capability in capabilities
629  ]
630 
631 
633  """Define a SmartThings Sensor."""
634 
635  def __init__(
636  self,
637  device: DeviceEntity,
638  attribute: str,
639  name: str,
640  default_unit: str | None,
641  device_class: SensorDeviceClass | None,
642  state_class: str | None,
643  entity_category: EntityCategory | None,
644  ) -> None:
645  """Init the class."""
646  super().__init__(device)
647  self._attribute_attribute = attribute
648  self._attr_name_attr_name_attr_name = f"{device.label} {name}"
649  self._attr_unique_id_attr_unique_id_attr_unique_id = f"{device.device_id}.{attribute}"
650  self._attr_device_class_attr_device_class = device_class
651  self._default_unit_default_unit = default_unit
652  self._attr_state_class_attr_state_class = state_class
653  self._attr_entity_category_attr_entity_category = entity_category
654 
655  @property
656  def native_value(self):
657  """Return the state of the sensor."""
658  value = self._device_device.status.attributes[self._attribute_attribute].value
659 
660  if self.device_classdevice_classdevice_class != SensorDeviceClass.TIMESTAMP:
661  return value
662 
663  return dt_util.parse_datetime(value)
664 
665  @property
667  """Return the unit this state is expressed in."""
668  unit = self._device_device.status.attributes[self._attribute_attribute].unit
669  return UNITS.get(unit, unit) if unit else self._default_unit_default_unit
670 
671 
673  """Define a SmartThings Three Axis Sensor."""
674 
675  def __init__(self, device, index):
676  """Init the class."""
677  super().__init__(device)
678  self._index_index = index
679  self._attr_name_attr_name_attr_name = f"{device.label} {THREE_AXIS_NAMES[index]}"
680  self._attr_unique_id_attr_unique_id_attr_unique_id = f"{device.device_id} {THREE_AXIS_NAMES[index]}"
681 
682  @property
683  def native_value(self):
684  """Return the state of the sensor."""
685  three_axis = self._device_device.status.attributes[Attribute.three_axis].value
686  try:
687  return three_axis[self._index_index]
688  except (TypeError, IndexError):
689  return None
690 
691 
693  """Define a SmartThings Sensor."""
694 
695  def __init__(
696  self,
697  device: DeviceEntity,
698  report_name: str,
699  ) -> None:
700  """Init the class."""
701  super().__init__(device)
702  self.report_namereport_name = report_name
703  self._attr_name_attr_name_attr_name = f"{device.label} {report_name}"
704  self._attr_unique_id_attr_unique_id_attr_unique_id = f"{device.device_id}.{report_name}_meter"
705  if self.report_namereport_name == "power":
706  self._attr_state_class_attr_state_class = SensorStateClass.MEASUREMENT
707  self._attr_device_class_attr_device_class = SensorDeviceClass.POWER
708  self._attr_native_unit_of_measurement_attr_native_unit_of_measurement = UnitOfPower.WATT
709  else:
710  self._attr_state_class_attr_state_class = SensorStateClass.TOTAL_INCREASING
711  self._attr_device_class_attr_device_class = SensorDeviceClass.ENERGY
712  self._attr_native_unit_of_measurement_attr_native_unit_of_measurement = UnitOfEnergy.KILO_WATT_HOUR
713 
714  @property
715  def native_value(self):
716  """Return the state of the sensor."""
717  value = self._device_device.status.attributes[Attribute.power_consumption].value
718  if value is None or value.get(self.report_namereport_name) is None:
719  return None
720  if self.report_namereport_name == "power":
721  return value[self.report_namereport_name]
722  return value[self.report_namereport_name] / 1000
723 
724  @property
726  """Return specific state attributes."""
727  if self.report_namereport_name == "power":
728  attributes = [
729  "power_consumption_start",
730  "power_consumption_end",
731  ]
732  state_attributes = {}
733  for attribute in attributes:
734  value = getattr(self._device_device.status, attribute)
735  if value is not None:
736  state_attributes[attribute] = value
737  return state_attributes
738  return None
SensorDeviceClass|None device_class(self)
Definition: __init__.py:313
None __init__(self, DeviceEntity device, str report_name)
Definition: sensor.py:699
None __init__(self, DeviceEntity device, str attribute, str name, str|None default_unit, SensorDeviceClass|None device_class, str|None state_class, EntityCategory|None entity_category)
Definition: sensor.py:644
Sequence[str]|None get_capabilities(Sequence[str] capabilities)
Definition: sensor.py:625
None async_setup_entry(HomeAssistant hass, ConfigEntry config_entry, AddEntitiesCallback async_add_entities)
Definition: sensor.py:567