From 0b81c836ef0e5f1604b4abb7b830330762351fb9 Mon Sep 17 00:00:00 2001 From: emanuelst <9994339+emanuelst@users.noreply.github.com> Date: Mon, 20 Feb 2023 18:56:41 +0100 Subject: [PATCH] Update HomeKit VOC mappings (#87663) Co-authored-by: J. Nick Koston --- .../components/homekit/type_sensors.py | 21 +++++++++++++++---- homeassistant/components/homekit/util.py | 17 +++++++++------ tests/components/homekit/test_type_sensors.py | 20 +++++++++--------- 3 files changed, 38 insertions(+), 20 deletions(-) diff --git a/homeassistant/components/homekit/type_sensors.py b/homeassistant/components/homekit/type_sensors.py index 27a1156111f..240cdd888d2 100644 --- a/homeassistant/components/homekit/type_sensors.py +++ b/homeassistant/components/homekit/type_sensors.py @@ -6,6 +6,7 @@ import logging from typing import NamedTuple from pyhap.const import CATEGORY_SENSOR +from pyhap.service import Service from homeassistant.components.binary_sensor import BinarySensorDeviceClass from homeassistant.const import ( @@ -40,6 +41,8 @@ from .const import ( CHAR_SMOKE_DETECTED, CHAR_VOC_DENSITY, PROP_CELSIUS, + PROP_MAX_VALUE, + PROP_MIN_VALUE, SERV_AIR_QUALITY_SENSOR, SERV_CARBON_DIOXIDE_SENSOR, SERV_CARBON_MONOXIDE_SENSOR, @@ -281,15 +284,25 @@ class NitrogenDioxideSensor(AirQualitySensor): @TYPES.register("VolatileOrganicCompoundsSensor") class VolatileOrganicCompoundsSensor(AirQualitySensor): - """Generate a VolatileOrganicCompoundsSensor accessory as VOCs sensor.""" + """Generate a VolatileOrganicCompoundsSensor accessory as VOCs sensor. + + Sensor entity must return VOC in µg/m3. + """ def create_services(self): - """Override the init function for PM 2.5 Sensor.""" - serv_air_quality = self.add_preload_service( + """Override the init function for VOC Sensor.""" + serv_air_quality: Service = self.add_preload_service( SERV_AIR_QUALITY_SENSOR, [CHAR_VOC_DENSITY] ) self.char_quality = serv_air_quality.configure_char(CHAR_AIR_QUALITY, value=0) - self.char_density = serv_air_quality.configure_char(CHAR_VOC_DENSITY, value=0) + self.char_density = serv_air_quality.configure_char( + CHAR_VOC_DENSITY, + value=0, + properties={ + PROP_MIN_VALUE: 0, + PROP_MAX_VALUE: 5000, + }, + ) @callback def async_update_state(self, new_state): diff --git a/homeassistant/components/homekit/util.py b/homeassistant/components/homekit/util.py index 53fd9fbfed7..5f0838d91a9 100644 --- a/homeassistant/components/homekit/util.py +++ b/homeassistant/components/homekit/util.py @@ -447,16 +447,21 @@ def density_to_air_quality_nitrogen_dioxide(density: float) -> int: def density_to_air_quality_voc(density: float) -> int: - """Map VOCs µg/m3 to HomeKit AirQuality level.""" - if density <= 24: + """Map VOCs µg/m3 to HomeKit AirQuality level. + + The VOC mappings use the IAQ guidelines for Europe released by the WHO (World Health Organization). + Referenced from Sensirion_Gas_Sensors_SGP3x_TVOC_Concept.pdf + https://github.com/paulvha/svm30/blob/master/extras/Sensirion_Gas_Sensors_SGP3x_TVOC_Concept.pdf + """ + if density <= 250: # WHO IAQ 1 (HomeKit: Excellent) return 1 - if density <= 48: + if density <= 500: # WHO IAQ 2 (HomeKit: Good) return 2 - if density <= 64: + if density <= 1000: # WHO IAQ 3 (HomeKit: Fair) return 3 - if density <= 96: + if density <= 3000: # WHO IAQ 4 (HomeKit: Inferior) return 4 - return 5 + return 5 # WHOA IAQ 5 (HomeKit: Poor) def get_persist_filename_for_entry_id(entry_id: str) -> str: diff --git a/tests/components/homekit/test_type_sensors.py b/tests/components/homekit/test_type_sensors.py index eb60acde05f..c48ebb86ce3 100644 --- a/tests/components/homekit/test_type_sensors.py +++ b/tests/components/homekit/test_type_sensors.py @@ -304,29 +304,29 @@ async def test_voc(hass: HomeAssistant, hk_driver) -> None: assert acc.char_density.value == 0 assert acc.char_quality.value == 0 - hass.states.async_set(entity_id, "24") + hass.states.async_set(entity_id, "250") await hass.async_block_till_done() - assert acc.char_density.value == 24 + assert acc.char_density.value == 250 assert acc.char_quality.value == 1 - hass.states.async_set(entity_id, "48") + hass.states.async_set(entity_id, "500") await hass.async_block_till_done() - assert acc.char_density.value == 48 + assert acc.char_density.value == 500 assert acc.char_quality.value == 2 - hass.states.async_set(entity_id, "64") + hass.states.async_set(entity_id, "1000") await hass.async_block_till_done() - assert acc.char_density.value == 64 + assert acc.char_density.value == 1000 assert acc.char_quality.value == 3 - hass.states.async_set(entity_id, "96") + hass.states.async_set(entity_id, "3000") await hass.async_block_till_done() - assert acc.char_density.value == 96 + assert acc.char_density.value == 3000 assert acc.char_quality.value == 4 - hass.states.async_set(entity_id, "128") + hass.states.async_set(entity_id, "5000") await hass.async_block_till_done() - assert acc.char_density.value == 128 + assert acc.char_density.value == 5000 assert acc.char_quality.value == 5