1 """Class to hold all sensor accessories."""
3 from __future__
import annotations
5 from collections.abc
import Callable
7 from typing
import Any, NamedTuple
9 from pyhap.const
import CATEGORY_SENSOR
10 from pyhap.service
import Service
15 ATTR_UNIT_OF_MEASUREMENT,
22 from .accessories
import TYPES, HomeAccessory
24 CHAR_AIR_PARTICULATE_DENSITY,
26 CHAR_CARBON_DIOXIDE_DETECTED,
27 CHAR_CARBON_DIOXIDE_LEVEL,
28 CHAR_CARBON_DIOXIDE_PEAK_LEVEL,
29 CHAR_CARBON_MONOXIDE_DETECTED,
30 CHAR_CARBON_MONOXIDE_LEVEL,
31 CHAR_CARBON_MONOXIDE_PEAK_LEVEL,
32 CHAR_CONTACT_SENSOR_STATE,
33 CHAR_CURRENT_AMBIENT_LIGHT_LEVEL,
34 CHAR_CURRENT_HUMIDITY,
35 CHAR_CURRENT_TEMPERATURE,
38 CHAR_NITROGEN_DIOXIDE_DENSITY,
39 CHAR_OCCUPANCY_DETECTED,
49 SERV_AIR_QUALITY_SENSOR,
50 SERV_CARBON_DIOXIDE_SENSOR,
51 SERV_CARBON_MONOXIDE_SENSOR,
57 SERV_OCCUPANCY_SENSOR,
59 SERV_TEMPERATURE_SENSOR,
65 density_to_air_quality,
66 density_to_air_quality_nitrogen_dioxide,
67 density_to_air_quality_pm10,
68 density_to_air_quality_voc,
69 temperature_to_homekit,
72 _LOGGER = logging.getLogger(__name__)
80 format: Callable[[bool], int | bool]
83 BINARY_SENSOR_SERVICE_MAP: dict[str, SI] = {
84 BinarySensorDeviceClass.CO:
SI(
85 SERV_CARBON_MONOXIDE_SENSOR, CHAR_CARBON_MONOXIDE_DETECTED, int
87 BinarySensorDeviceClass.DOOR:
SI(
88 SERV_CONTACT_SENSOR, CHAR_CONTACT_SENSOR_STATE, int
90 BinarySensorDeviceClass.GARAGE_DOOR:
SI(
91 SERV_CONTACT_SENSOR, CHAR_CONTACT_SENSOR_STATE, int
93 BinarySensorDeviceClass.GAS:
SI(
94 SERV_CARBON_MONOXIDE_SENSOR, CHAR_CARBON_MONOXIDE_DETECTED, int
96 BinarySensorDeviceClass.MOISTURE:
SI(SERV_LEAK_SENSOR, CHAR_LEAK_DETECTED, int),
97 BinarySensorDeviceClass.MOTION:
SI(SERV_MOTION_SENSOR, CHAR_MOTION_DETECTED, bool),
98 BinarySensorDeviceClass.OCCUPANCY:
SI(
99 SERV_OCCUPANCY_SENSOR, CHAR_OCCUPANCY_DETECTED, int
101 BinarySensorDeviceClass.OPENING:
SI(
102 SERV_CONTACT_SENSOR, CHAR_CONTACT_SENSOR_STATE, int
104 BinarySensorDeviceClass.SMOKE:
SI(SERV_SMOKE_SENSOR, CHAR_SMOKE_DETECTED, int),
105 BinarySensorDeviceClass.WINDOW:
SI(
106 SERV_CONTACT_SENSOR, CHAR_CONTACT_SENSOR_STATE, int
111 @TYPES.register("TemperatureSensor")
113 """Generate a TemperatureSensor accessory for a temperature sensor.
115 Sensor entity must return temperature in °C, °F.
119 """Initialize a TemperatureSensor accessory object."""
120 super().
__init__(*args, category=CATEGORY_SENSOR)
123 serv_temp = self.add_preload_service(SERV_TEMPERATURE_SENSOR)
125 CHAR_CURRENT_TEMPERATURE, value=0, properties=PROP_CELSIUS
133 """Update temperature after state changed."""
134 unit = new_state.attributes.get(
135 ATTR_UNIT_OF_MEASUREMENT, UnitOfTemperature.CELSIUS
137 if (temperature := convert_to_float(new_state.state))
is not None:
138 temperature = temperature_to_homekit(temperature, unit)
139 self.
char_tempchar_temp.set_value(temperature)
141 "%s: Current temperature set to %.1f°C", self.
entity_identity_id, temperature
145 @TYPES.register("HumiditySensor")
147 """Generate a HumiditySensor accessory as humidity sensor."""
150 """Initialize a HumiditySensor accessory object."""
151 super().
__init__(*args, category=CATEGORY_SENSOR)
154 serv_humidity = self.add_preload_service(SERV_HUMIDITY_SENSOR)
156 CHAR_CURRENT_HUMIDITY, value=0
164 """Update accessory after state change."""
165 if (humidity := convert_to_float(new_state.state))
is not None:
167 _LOGGER.debug(
"%s: Percent set to %d%%", self.
entity_identity_id, humidity)
170 @TYPES.register("AirQualitySensor")
172 """Generate a AirQualitySensor accessory as air quality sensor."""
175 """Initialize a AirQualitySensor accessory object."""
176 super().
__init__(*args, category=CATEGORY_SENSOR)
186 """Initialize a AirQualitySensor accessory object."""
187 serv_air_quality = self.add_preload_service(
188 SERV_AIR_QUALITY_SENSOR, [CHAR_AIR_PARTICULATE_DENSITY]
190 self.
char_qualitychar_quality = serv_air_quality.configure_char(CHAR_AIR_QUALITY, value=0)
192 CHAR_AIR_PARTICULATE_DENSITY, value=0
197 """Update accessory after state change."""
198 if (density := convert_to_float(new_state.state))
is not None:
201 _LOGGER.debug(
"%s: Set density to %d", self.
entity_identity_id, density)
202 air_quality = density_to_air_quality(density)
204 _LOGGER.debug(
"%s: Set air_quality to %d", self.
entity_identity_id, air_quality)
207 @TYPES.register("PM10Sensor")
209 """Generate a PM10Sensor accessory as PM 10 sensor."""
212 """Override the init function for PM 10 Sensor."""
213 serv_air_quality = self.add_preload_service(
214 SERV_AIR_QUALITY_SENSOR, [CHAR_PM10_DENSITY]
221 """Update accessory after state change."""
222 density = convert_to_float(new_state.state)
227 _LOGGER.debug(
"%s: Set density to %d", self.
entity_identity_id, density)
228 air_quality = density_to_air_quality_pm10(density)
231 _LOGGER.debug(
"%s: Set air_quality to %d", self.
entity_identity_id, air_quality)
234 @TYPES.register("PM25Sensor")
236 """Generate a PM25Sensor accessory as PM 2.5 sensor."""
239 """Override the init function for PM 2.5 Sensor."""
240 serv_air_quality = self.add_preload_service(
241 SERV_AIR_QUALITY_SENSOR, [CHAR_PM25_DENSITY]
248 """Update accessory after state change."""
249 density = convert_to_float(new_state.state)
254 _LOGGER.debug(
"%s: Set density to %d", self.
entity_identity_id, density)
255 air_quality = density_to_air_quality(density)
258 _LOGGER.debug(
"%s: Set air_quality to %d", self.
entity_identity_id, air_quality)
261 @TYPES.register("NitrogenDioxideSensor")
263 """Generate a NitrogenDioxideSensor accessory as NO2 sensor."""
266 """Override the init function for PM 2.5 Sensor."""
267 serv_air_quality = self.add_preload_service(
268 SERV_AIR_QUALITY_SENSOR, [CHAR_NITROGEN_DIOXIDE_DENSITY]
272 CHAR_NITROGEN_DIOXIDE_DENSITY, value=0
277 """Update accessory after state change."""
278 density = convert_to_float(new_state.state)
283 _LOGGER.debug(
"%s: Set density to %d", self.
entity_identity_id, density)
284 air_quality = density_to_air_quality_nitrogen_dioxide(density)
287 _LOGGER.debug(
"%s: Set air_quality to %d", self.
entity_identity_id, air_quality)
290 @TYPES.register("VolatileOrganicCompoundsSensor")
292 """Generate a VolatileOrganicCompoundsSensor accessory as VOCs sensor.
294 Sensor entity must return VOC in µg/m3.
298 """Override the init function for VOC Sensor."""
299 serv_air_quality: Service = self.add_preload_service(
300 SERV_AIR_QUALITY_SENSOR, [CHAR_VOC_DENSITY]
308 PROP_MAX_VALUE: 5000,
314 """Update accessory after state change."""
315 density = convert_to_float(new_state.state)
320 _LOGGER.debug(
"%s: Set density to %d", self.
entity_identity_id, density)
321 air_quality = density_to_air_quality_voc(density)
324 _LOGGER.debug(
"%s: Set air_quality to %d", self.
entity_identity_id, air_quality)
327 @TYPES.register("CarbonMonoxideSensor")
329 """Generate a CarbonMonoxidSensor accessory as CO sensor."""
332 """Initialize a CarbonMonoxideSensor accessory object."""
333 super().
__init__(*args, category=CATEGORY_SENSOR)
336 serv_co = self.add_preload_service(
337 SERV_CARBON_MONOXIDE_SENSOR,
338 [CHAR_CARBON_MONOXIDE_LEVEL, CHAR_CARBON_MONOXIDE_PEAK_LEVEL],
344 self.
char_levelchar_level = serv_co.configure_char(CHAR_CARBON_MONOXIDE_LEVEL, value=0)
346 CHAR_CARBON_MONOXIDE_PEAK_LEVEL, value=0
349 CHAR_CARBON_MONOXIDE_DETECTED, value=0
357 """Update accessory after state change."""
358 if (value := convert_to_float(new_state.state))
is not None:
360 if value > self.
char_peakchar_peak.value:
364 _LOGGER.debug(
"%s: Set to %d", self.
entity_identity_id, value)
367 @TYPES.register("CarbonDioxideSensor")
369 """Generate a CarbonDioxideSensor accessory as CO2 sensor."""
372 """Initialize a CarbonDioxideSensor accessory object."""
373 super().
__init__(*args, category=CATEGORY_SENSOR)
376 serv_co2 = self.add_preload_service(
377 SERV_CARBON_DIOXIDE_SENSOR,
378 [CHAR_CARBON_DIOXIDE_LEVEL, CHAR_CARBON_DIOXIDE_PEAK_LEVEL],
384 self.
char_levelchar_level = serv_co2.configure_char(CHAR_CARBON_DIOXIDE_LEVEL, value=0)
386 CHAR_CARBON_DIOXIDE_PEAK_LEVEL, value=0
389 CHAR_CARBON_DIOXIDE_DETECTED, value=0
397 """Update accessory after state change."""
398 if (value := convert_to_float(new_state.state))
is not None:
400 if value > self.
char_peakchar_peak.value:
404 _LOGGER.debug(
"%s: Set to %d", self.
entity_identity_id, value)
407 @TYPES.register("LightSensor")
409 """Generate a LightSensor accessory as light sensor."""
412 """Initialize a LightSensor accessory object."""
413 super().
__init__(*args, category=CATEGORY_SENSOR)
416 serv_light = self.add_preload_service(SERV_LIGHT_SENSOR)
418 CHAR_CURRENT_AMBIENT_LIGHT_LEVEL, value=0
426 """Update accessory after state change."""
427 if (luminance := convert_to_float(new_state.state))
is not None:
428 self.
char_lightchar_light.set_value(luminance)
429 _LOGGER.debug(
"%s: Set to %d", self.
entity_identity_id, luminance)
432 @TYPES.register("BinarySensor")
434 """Generate a BinarySensor accessory as binary sensor."""
437 """Initialize a BinarySensor accessory object."""
438 super().
__init__(*args, category=CATEGORY_SENSOR)
441 device_class = state.attributes.get(ATTR_DEVICE_CLASS)
443 BINARY_SENSOR_SERVICE_MAP[device_class]
444 if device_class
in BINARY_SENSOR_SERVICE_MAP
445 else BINARY_SENSOR_SERVICE_MAP[BinarySensorDeviceClass.OCCUPANCY]
449 service = self.add_preload_service(service_char.service)
450 initial_value =
False if self.
formatformat
is bool
else 0
452 service_char.char, value=initial_value
460 """Update accessory after state change."""
461 state = new_state.state
462 detected = self.
formatformat(state
in (STATE_ON, STATE_HOME))
464 _LOGGER.debug(
"%s: Set to %d", self.
entity_identity_id, detected)
None async_update_state(self, State new_state)
None create_services(self)
None __init__(self, *Any args)
None async_update_state(self, State new_state)
None __init__(self, *Any args)
None async_update_state(self, State new_state)
None async_update_state(self, State new_state)
None __init__(self, *Any args)
None __init__(self, *Any args)
None async_update_state(self, State new_state)
None async_update_state(self, State new_state)
None __init__(self, *Any args)
None async_update_state(self, State new_state)
None __init__(self, *Any args)
None create_services(self)
None async_update_state(self, State new_state)
None async_update_state(self, State new_state)
None create_services(self)
None create_services(self)
None async_update_state(self, State new_state)
None __init__(self, *Any args)
None async_update_state(self, State new_state)
None create_services(self)
None async_update_state(self, State new_state)
web.Response get(self, web.Request request, str config_key)