From d4acd86819a664fca73176443569a7facd14a62f Mon Sep 17 00:00:00 2001 From: Fabrice Date: Fri, 24 May 2024 13:28:19 +0200 Subject: [PATCH] Make co/co2 threshold configurable via entity_config (#112978) * make co/co2 threshold configurable via entity_config * Split threshold into co/co2_threshold configuration --- homeassistant/components/homekit/const.py | 2 + .../components/homekit/type_sensors.py | 14 ++++- homeassistant/components/homekit/util.py | 12 ++++ tests/components/homekit/test_type_sensors.py | 58 +++++++++++++++++++ tests/components/homekit/test_util.py | 8 +++ 5 files changed, 92 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/homekit/const.py b/homeassistant/components/homekit/const.py index 9f44e2ab616..00b3de49169 100644 --- a/homeassistant/components/homekit/const.py +++ b/homeassistant/components/homekit/const.py @@ -59,6 +59,8 @@ CONF_MAX_WIDTH = "max_width" CONF_STREAM_ADDRESS = "stream_address" CONF_STREAM_SOURCE = "stream_source" CONF_SUPPORT_AUDIO = "support_audio" +CONF_THRESHOLD_CO = "co_threshold" +CONF_THRESHOLD_CO2 = "co2_threshold" CONF_VIDEO_CODEC = "video_codec" CONF_VIDEO_PROFILE_NAMES = "video_profile_names" CONF_VIDEO_MAP = "video_map" diff --git a/homeassistant/components/homekit/type_sensors.py b/homeassistant/components/homekit/type_sensors.py index bfa97756bb4..48327910be6 100644 --- a/homeassistant/components/homekit/type_sensors.py +++ b/homeassistant/components/homekit/type_sensors.py @@ -41,6 +41,8 @@ from .const import ( CHAR_PM25_DENSITY, CHAR_SMOKE_DETECTED, CHAR_VOC_DENSITY, + CONF_THRESHOLD_CO, + CONF_THRESHOLD_CO2, PROP_CELSIUS, PROP_MAX_VALUE, PROP_MIN_VALUE, @@ -335,6 +337,10 @@ class CarbonMonoxideSensor(HomeAccessory): SERV_CARBON_MONOXIDE_SENSOR, [CHAR_CARBON_MONOXIDE_LEVEL, CHAR_CARBON_MONOXIDE_PEAK_LEVEL], ) + + self.threshold_co = self.config.get(CONF_THRESHOLD_CO, THRESHOLD_CO) + _LOGGER.debug("%s: Set CO threshold to %d", self.entity_id, self.threshold_co) + self.char_level = serv_co.configure_char(CHAR_CARBON_MONOXIDE_LEVEL, value=0) self.char_peak = serv_co.configure_char( CHAR_CARBON_MONOXIDE_PEAK_LEVEL, value=0 @@ -353,7 +359,7 @@ class CarbonMonoxideSensor(HomeAccessory): self.char_level.set_value(value) if value > self.char_peak.value: self.char_peak.set_value(value) - co_detected = value > THRESHOLD_CO + co_detected = value > self.threshold_co self.char_detected.set_value(co_detected) _LOGGER.debug("%s: Set to %d", self.entity_id, value) @@ -371,6 +377,10 @@ class CarbonDioxideSensor(HomeAccessory): SERV_CARBON_DIOXIDE_SENSOR, [CHAR_CARBON_DIOXIDE_LEVEL, CHAR_CARBON_DIOXIDE_PEAK_LEVEL], ) + + self.threshold_co2 = self.config.get(CONF_THRESHOLD_CO2, THRESHOLD_CO2) + _LOGGER.debug("%s: Set CO2 threshold to %d", self.entity_id, self.threshold_co2) + self.char_level = serv_co2.configure_char(CHAR_CARBON_DIOXIDE_LEVEL, value=0) self.char_peak = serv_co2.configure_char( CHAR_CARBON_DIOXIDE_PEAK_LEVEL, value=0 @@ -389,7 +399,7 @@ class CarbonDioxideSensor(HomeAccessory): self.char_level.set_value(value) if value > self.char_peak.value: self.char_peak.set_value(value) - co2_detected = value > THRESHOLD_CO2 + co2_detected = value > self.threshold_co2 self.char_detected.set_value(co2_detected) _LOGGER.debug("%s: Set to %d", self.entity_id, value) diff --git a/homeassistant/components/homekit/util.py b/homeassistant/components/homekit/util.py index dec7fe8eba7..8fbd7c6b13b 100644 --- a/homeassistant/components/homekit/util.py +++ b/homeassistant/components/homekit/util.py @@ -72,6 +72,8 @@ from .const import ( CONF_STREAM_COUNT, CONF_STREAM_SOURCE, CONF_SUPPORT_AUDIO, + CONF_THRESHOLD_CO, + CONF_THRESHOLD_CO2, CONF_VIDEO_CODEC, CONF_VIDEO_MAP, CONF_VIDEO_PACKET_SIZE, @@ -223,6 +225,13 @@ SWITCH_TYPE_SCHEMA = BASIC_INFO_SCHEMA.extend( } ) +SENSOR_SCHEMA = BASIC_INFO_SCHEMA.extend( + { + vol.Optional(CONF_THRESHOLD_CO): vol.Any(None, cv.positive_int), + vol.Optional(CONF_THRESHOLD_CO2): vol.Any(None, cv.positive_int), + } +) + HOMEKIT_CHAR_TRANSLATIONS = { 0: " ", # nul @@ -297,6 +306,9 @@ def validate_entity_config(values: dict) -> dict[str, dict]: elif domain == "cover": config = COVER_SCHEMA(config) + elif domain == "sensor": + config = SENSOR_SCHEMA(config) + else: config = BASIC_INFO_SCHEMA(config) diff --git a/tests/components/homekit/test_type_sensors.py b/tests/components/homekit/test_type_sensors.py index ac086b8100e..fc68b7c8ecf 100644 --- a/tests/components/homekit/test_type_sensors.py +++ b/tests/components/homekit/test_type_sensors.py @@ -5,6 +5,8 @@ from unittest.mock import patch from homeassistant.components.binary_sensor import BinarySensorDeviceClass from homeassistant.components.homekit import get_accessory from homeassistant.components.homekit.const import ( + CONF_THRESHOLD_CO, + CONF_THRESHOLD_CO2, PROP_CELSIUS, THRESHOLD_CO, THRESHOLD_CO2, @@ -375,6 +377,34 @@ async def test_co(hass: HomeAssistant, hk_driver) -> None: assert acc.char_detected.value == 0 +async def test_co_with_configured_threshold(hass: HomeAssistant, hk_driver) -> None: + """Test if co threshold of accessory can be configured .""" + entity_id = "sensor.co" + + co_threshold = 10 + assert co_threshold < THRESHOLD_CO + + hass.states.async_set(entity_id, None) + await hass.async_block_till_done() + acc = CarbonMonoxideSensor( + hass, hk_driver, "CO", entity_id, 2, {CONF_THRESHOLD_CO: co_threshold} + ) + acc.run() + await hass.async_block_till_done() + + value = 15 + assert value > co_threshold + hass.states.async_set(entity_id, str(value)) + await hass.async_block_till_done() + assert acc.char_detected.value == 1 + + value = 5 + assert value < co_threshold + hass.states.async_set(entity_id, str(value)) + await hass.async_block_till_done() + assert acc.char_detected.value == 0 + + async def test_co2(hass: HomeAssistant, hk_driver) -> None: """Test if accessory is updated after state change.""" entity_id = "sensor.co2" @@ -415,6 +445,34 @@ async def test_co2(hass: HomeAssistant, hk_driver) -> None: assert acc.char_detected.value == 0 +async def test_co2_with_configured_threshold(hass: HomeAssistant, hk_driver) -> None: + """Test if co2 threshold of accessory can be configured .""" + entity_id = "sensor.co2" + + co2_threshold = 500 + assert co2_threshold < THRESHOLD_CO2 + + hass.states.async_set(entity_id, None) + await hass.async_block_till_done() + acc = CarbonDioxideSensor( + hass, hk_driver, "CO2", entity_id, 2, {CONF_THRESHOLD_CO2: co2_threshold} + ) + acc.run() + await hass.async_block_till_done() + + value = 800 + assert value > co2_threshold + hass.states.async_set(entity_id, str(value)) + await hass.async_block_till_done() + assert acc.char_detected.value == 1 + + value = 400 + assert value < co2_threshold + hass.states.async_set(entity_id, str(value)) + await hass.async_block_till_done() + assert acc.char_detected.value == 0 + + async def test_light(hass: HomeAssistant, hk_driver) -> None: """Test if accessory is updated after state change.""" entity_id = "sensor.light" diff --git a/tests/components/homekit/test_util.py b/tests/components/homekit/test_util.py index 17e38a0a145..a7b9dae416e 100644 --- a/tests/components/homekit/test_util.py +++ b/tests/components/homekit/test_util.py @@ -11,6 +11,8 @@ from homeassistant.components.homekit.const import ( CONF_FEATURE_LIST, CONF_LINKED_BATTERY_SENSOR, CONF_LOW_BATTERY_THRESHOLD, + CONF_THRESHOLD_CO, + CONF_THRESHOLD_CO2, DEFAULT_CONFIG_FLOW_PORT, DOMAIN, FEATURE_ON_OFF, @@ -170,6 +172,12 @@ def test_validate_entity_config() -> None: assert vec({"switch.demo": {CONF_TYPE: TYPE_VALVE}}) == { "switch.demo": {CONF_TYPE: TYPE_VALVE, CONF_LOW_BATTERY_THRESHOLD: 20} } + assert vec({"sensor.co": {CONF_THRESHOLD_CO: 500}}) == { + "sensor.co": {CONF_THRESHOLD_CO: 500, CONF_LOW_BATTERY_THRESHOLD: 20} + } + assert vec({"sensor.co2": {CONF_THRESHOLD_CO2: 500}}) == { + "sensor.co2": {CONF_THRESHOLD_CO2: 500, CONF_LOW_BATTERY_THRESHOLD: 20} + } def test_validate_media_player_features() -> None: