Home Assistant Unofficial Reference 2024.12.1
sensor.py
Go to the documentation of this file.
1 """Support for Canary sensors."""
2 
3 from __future__ import annotations
4 
5 from typing import Final
6 
7 from canary.model import Device, Location, SensorType
8 
9 from homeassistant.components.sensor import SensorDeviceClass, SensorEntity
10 from homeassistant.config_entries import ConfigEntry
11 from homeassistant.const import (
12  PERCENTAGE,
13  SIGNAL_STRENGTH_DECIBELS_MILLIWATT,
14  UnitOfTemperature,
15 )
16 from homeassistant.core import HomeAssistant
17 from homeassistant.helpers.device_registry import DeviceInfo
18 from homeassistant.helpers.entity_platform import AddEntitiesCallback
19 from homeassistant.helpers.update_coordinator import CoordinatorEntity
20 
21 from .const import DATA_COORDINATOR, DOMAIN, MANUFACTURER
22 from .coordinator import CanaryDataUpdateCoordinator
23 
24 type SensorTypeItem = tuple[
25  str, str | None, str | None, SensorDeviceClass | None, list[str]
26 ]
27 
28 SENSOR_VALUE_PRECISION: Final = 2
29 ATTR_AIR_QUALITY: Final = "air_quality"
30 
31 # Define variables to store the device names, as referred to by the Canary API.
32 # Note: If Canary change the name of any of their devices (which they have done),
33 # then these variables will need updating, otherwise the sensors will stop working
34 # and disappear in Home Assistant.
35 CANARY_PRO: Final = "Canary Pro"
36 CANARY_FLEX: Final = "Canary Flex"
37 
38 # Sensor types are defined like so:
39 # sensor type name, unit_of_measurement, icon, device class, products supported
40 SENSOR_TYPES: Final[list[SensorTypeItem]] = [
41  (
42  "temperature",
43  UnitOfTemperature.CELSIUS,
44  None,
45  SensorDeviceClass.TEMPERATURE,
46  [CANARY_PRO],
47  ),
48  ("humidity", PERCENTAGE, None, SensorDeviceClass.HUMIDITY, [CANARY_PRO]),
49  ("air_quality", None, "mdi:weather-windy", None, [CANARY_PRO]),
50  (
51  "wifi",
52  SIGNAL_STRENGTH_DECIBELS_MILLIWATT,
53  None,
54  SensorDeviceClass.SIGNAL_STRENGTH,
55  [CANARY_FLEX],
56  ),
57  ("battery", PERCENTAGE, None, SensorDeviceClass.BATTERY, [CANARY_FLEX]),
58 ]
59 
60 STATE_AIR_QUALITY_NORMAL: Final = "normal"
61 STATE_AIR_QUALITY_ABNORMAL: Final = "abnormal"
62 STATE_AIR_QUALITY_VERY_ABNORMAL: Final = "very_abnormal"
63 
64 
66  hass: HomeAssistant,
67  entry: ConfigEntry,
68  async_add_entities: AddEntitiesCallback,
69 ) -> None:
70  """Set up Canary sensors based on a config entry."""
71  coordinator: CanaryDataUpdateCoordinator = hass.data[DOMAIN][entry.entry_id][
72  DATA_COORDINATOR
73  ]
74  sensors: list[CanarySensor] = []
75 
76  for location in coordinator.data["locations"].values():
77  for device in location.devices:
78  if device.is_online:
79  device_type = device.device_type
80  sensors.extend(
81  CanarySensor(coordinator, sensor_type, location, device)
82  for sensor_type in SENSOR_TYPES
83  if device_type.get("name") in sensor_type[4]
84  )
85 
86  async_add_entities(sensors, True)
87 
88 
89 class CanarySensor(CoordinatorEntity[CanaryDataUpdateCoordinator], SensorEntity):
90  """Representation of a Canary sensor."""
91 
92  def __init__(
93  self,
94  coordinator: CanaryDataUpdateCoordinator,
95  sensor_type: SensorTypeItem,
96  location: Location,
97  device: Device,
98  ) -> None:
99  """Initialize the sensor."""
100 
101  super().__init__(coordinator)
102  self._sensor_type_sensor_type = sensor_type
103  self._device_id_device_id = device.device_id
104 
105  sensor_type_name = sensor_type[0].replace("_", " ").title()
106  self._attr_name_attr_name = f"{location.name} {device.name} {sensor_type_name}"
107 
108  canary_sensor_type = None
109  if self._sensor_type_sensor_type[0] == "air_quality":
110  canary_sensor_type = SensorType.AIR_QUALITY
111  elif self._sensor_type_sensor_type[0] == "temperature":
112  canary_sensor_type = SensorType.TEMPERATURE
113  elif self._sensor_type_sensor_type[0] == "humidity":
114  canary_sensor_type = SensorType.HUMIDITY
115  elif self._sensor_type_sensor_type[0] == "wifi":
116  canary_sensor_type = SensorType.WIFI
117  elif self._sensor_type_sensor_type[0] == "battery":
118  canary_sensor_type = SensorType.BATTERY
119 
120  self._canary_type_canary_type = canary_sensor_type
121  self._attr_unique_id_attr_unique_id = f"{device.device_id}_{sensor_type[0]}"
122  self._attr_device_info_attr_device_info = DeviceInfo(
123  identifiers={(DOMAIN, str(device.device_id))},
124  model=device.device_type["name"],
125  manufacturer=MANUFACTURER,
126  name=device.name,
127  )
128  self._attr_native_unit_of_measurement_attr_native_unit_of_measurement = sensor_type[1]
129  self._attr_device_class_attr_device_class = sensor_type[3]
130  self._attr_icon_attr_icon = sensor_type[2]
131 
132  @property
133  def reading(self) -> float | None:
134  """Return the device sensor reading."""
135  readings = self.coordinator.data["readings"][self._device_id_device_id]
136 
137  value = next(
138  (
139  reading.value
140  for reading in readings
141  if reading.sensor_type == self._canary_type_canary_type
142  ),
143  None,
144  )
145 
146  if value is not None:
147  return round(float(value), SENSOR_VALUE_PRECISION)
148 
149  return None
150 
151  @property
152  def native_value(self) -> float | None:
153  """Return the state of the sensor."""
154  return self.readingreading
155 
156  @property
157  def extra_state_attributes(self) -> dict[str, str] | None:
158  """Return the state attributes."""
159  reading = self.readingreading
160 
161  if self._sensor_type_sensor_type[0] == "air_quality" and reading is not None:
162  air_quality = None
163  if reading <= 0.4:
164  air_quality = STATE_AIR_QUALITY_VERY_ABNORMAL
165  elif reading <= 0.59:
166  air_quality = STATE_AIR_QUALITY_ABNORMAL
167  else:
168  air_quality = STATE_AIR_QUALITY_NORMAL
169 
170  return {ATTR_AIR_QUALITY: air_quality}
171 
172  return None
dict[str, str]|None extra_state_attributes(self)
Definition: sensor.py:157
None __init__(self, CanaryDataUpdateCoordinator coordinator, SensorTypeItem sensor_type, Location location, Device device)
Definition: sensor.py:98
None async_setup_entry(HomeAssistant hass, ConfigEntry entry, AddEntitiesCallback async_add_entities)
Definition: sensor.py:69