From fe7857c8ecf2aecbbfb92ea81f6b00d57d3084f3 Mon Sep 17 00:00:00 2001 From: Jan Bouwhuis Date: Wed, 28 Jun 2023 13:13:55 +0200 Subject: [PATCH] Add current_humidity device_trigger for humidity component (#95435) Add current_humidity device_trigger for humidity --- .../components/humidifier/device_trigger.py | 41 ++++++- .../humidifier/test_device_trigger.py | 110 +++++++++++++++--- 2 files changed, 135 insertions(+), 16 deletions(-) diff --git a/homeassistant/components/humidifier/device_trigger.py b/homeassistant/components/humidifier/device_trigger.py index ee452398a26..0d689a35318 100644 --- a/homeassistant/components/humidifier/device_trigger.py +++ b/homeassistant/components/humidifier/device_trigger.py @@ -26,10 +26,23 @@ from homeassistant.helpers import config_validation as cv, entity_registry as er from homeassistant.helpers.trigger import TriggerActionType, TriggerInfo from homeassistant.helpers.typing import ConfigType -from . import DOMAIN +from . import ATTR_CURRENT_HUMIDITY, DOMAIN # mypy: disallow-any-generics +CURRENT_TRIGGER_SCHEMA = vol.All( + DEVICE_TRIGGER_BASE_SCHEMA.extend( + { + vol.Required(CONF_ENTITY_ID): cv.entity_id_or_uuid, + vol.Required(CONF_TYPE): "current_humidity_changed", + vol.Optional(CONF_BELOW): vol.Any(vol.Coerce(float)), + vol.Optional(CONF_ABOVE): vol.Any(vol.Coerce(float)), + vol.Optional(CONF_FOR): cv.positive_time_period_dict, + } + ), + cv.has_at_least_one_key(CONF_BELOW, CONF_ABOVE), +) + HUMIDIFIER_TRIGGER_SCHEMA = vol.All( DEVICE_TRIGGER_BASE_SCHEMA.extend( { @@ -45,6 +58,7 @@ HUMIDIFIER_TRIGGER_SCHEMA = vol.All( TRIGGER_SCHEMA = vol.All( vol.Any( + CURRENT_TRIGGER_SCHEMA, HUMIDIFIER_TRIGGER_SCHEMA, toggle_entity.TRIGGER_SCHEMA, ), @@ -64,6 +78,8 @@ async def async_get_triggers( if entry.domain != DOMAIN: continue + state = hass.states.get(entry.entity_id) + # Add triggers for each entity that belongs to this integration base_trigger = { CONF_PLATFORM: "device", @@ -79,6 +95,14 @@ async def async_get_triggers( } ) + if state and ATTR_CURRENT_HUMIDITY in state.attributes: + triggers.append( + { + **base_trigger, + CONF_TYPE: "current_humidity_changed", + } + ) + return triggers @@ -89,7 +113,10 @@ async def async_attach_trigger( trigger_info: TriggerInfo, ) -> CALLBACK_TYPE: """Attach a trigger.""" - if config[CONF_TYPE] == "target_humidity_changed": + if (trigger_type := config[CONF_TYPE]) in { + "current_humidity_changed", + "target_humidity_changed", + }: numeric_state_config = { numeric_state_trigger.CONF_PLATFORM: "numeric_state", numeric_state_trigger.CONF_ENTITY_ID: config[CONF_ENTITY_ID], @@ -97,6 +124,14 @@ async def async_attach_trigger( "{{ state.attributes.humidity }}" ), } + if trigger_type == "target_humidity_changed": + numeric_state_config[ + numeric_state_trigger.CONF_VALUE_TEMPLATE + ] = "{{ state.attributes.humidity }}" + else: # trigger_type == "current_humidity_changed" + numeric_state_config[ + numeric_state_trigger.CONF_VALUE_TEMPLATE + ] = "{{ state.attributes.current_humidity }}" if CONF_ABOVE in config: numeric_state_config[CONF_ABOVE] = config[CONF_ABOVE] @@ -121,7 +156,7 @@ async def async_get_trigger_capabilities( hass: HomeAssistant, config: ConfigType ) -> dict[str, vol.Schema]: """List trigger capabilities.""" - if config[CONF_TYPE] == "target_humidity_changed": + if config[CONF_TYPE] in {"current_humidity_changed", "target_humidity_changed"}: return { "extra_fields": vol.Schema( { diff --git a/tests/components/humidifier/test_device_trigger.py b/tests/components/humidifier/test_device_trigger.py index 01c223a8dcb..1953494e0c0 100644 --- a/tests/components/humidifier/test_device_trigger.py +++ b/tests/components/humidifier/test_device_trigger.py @@ -64,12 +64,13 @@ async def test_get_triggers( STATE_ON, { const.ATTR_HUMIDITY: 23, + const.ATTR_CURRENT_HUMIDITY: 48, ATTR_MODE: "home", const.ATTR_AVAILABLE_MODES: ["home", "away"], ATTR_SUPPORTED_FEATURES: 1, }, ) - humidifier_trigger_types = ["target_humidity_changed"] + humidifier_trigger_types = ["current_humidity_changed", "target_humidity_changed"] toggle_trigger_types = ["turned_on", "turned_off", "changed_states"] expected_triggers = [ { @@ -171,6 +172,7 @@ async def test_if_fires_on_state_change( STATE_ON, { const.ATTR_HUMIDITY: 23, + const.ATTR_CURRENT_HUMIDITY: 35, ATTR_MODE: "home", const.ATTR_AVAILABLE_MODES: ["home", "away"], ATTR_SUPPORTED_FEATURES: 1, @@ -225,6 +227,49 @@ async def test_if_fires_on_state_change( "data_template": {"some": "target_humidity_changed_above_for"}, }, }, + { + "trigger": { + "platform": "device", + "domain": DOMAIN, + "device_id": "", + "entity_id": entry.id, + "type": "current_humidity_changed", + "below": 30, + }, + "action": { + "service": "test.automation", + "data_template": {"some": "current_humidity_changed_below"}, + }, + }, + { + "trigger": { + "platform": "device", + "domain": DOMAIN, + "device_id": "", + "entity_id": entry.id, + "type": "current_humidity_changed", + "above": 40, + }, + "action": { + "service": "test.automation", + "data_template": {"some": "current_humidity_changed_above"}, + }, + }, + { + "trigger": { + "platform": "device", + "domain": DOMAIN, + "device_id": "", + "entity_id": entry.id, + "type": "current_humidity_changed", + "above": 40, + "for": {"seconds": 5}, + }, + "action": { + "service": "test.automation", + "data_template": {"some": "current_humidity_changed_above_for"}, + }, + }, { "trigger": { "platform": "device", @@ -302,37 +347,76 @@ async def test_if_fires_on_state_change( ) # Fake that the humidity target is changing - hass.states.async_set(entry.entity_id, STATE_ON, {const.ATTR_HUMIDITY: 7}) + hass.states.async_set( + entry.entity_id, + STATE_ON, + {const.ATTR_HUMIDITY: 7, const.ATTR_CURRENT_HUMIDITY: 35}, + ) await hass.async_block_till_done() assert len(calls) == 1 assert calls[0].data["some"] == "target_humidity_changed_below" - # Fake that the humidity target is changing - hass.states.async_set(entry.entity_id, STATE_ON, {const.ATTR_HUMIDITY: 37}) + # Fake that the current humidity is changing + hass.states.async_set( + entry.entity_id, + STATE_ON, + {const.ATTR_HUMIDITY: 7, const.ATTR_CURRENT_HUMIDITY: 18}, + ) await hass.async_block_till_done() assert len(calls) == 2 - assert calls[1].data["some"] == "target_humidity_changed_above" + assert calls[1].data["some"] == "current_humidity_changed_below" + + # Fake that the humidity target is changing + hass.states.async_set( + entry.entity_id, + STATE_ON, + {const.ATTR_HUMIDITY: 37, const.ATTR_CURRENT_HUMIDITY: 18}, + ) + await hass.async_block_till_done() + assert len(calls) == 3 + assert calls[2].data["some"] == "target_humidity_changed_above" + + # Fake that the current humidity is changing + hass.states.async_set( + entry.entity_id, + STATE_ON, + {const.ATTR_HUMIDITY: 37, const.ATTR_CURRENT_HUMIDITY: 41}, + ) + await hass.async_block_till_done() + assert len(calls) == 4 + assert calls[3].data["some"] == "current_humidity_changed_above" # Wait 6 minutes async_fire_time_changed(hass, dt_util.utcnow() + datetime.timedelta(minutes=6)) await hass.async_block_till_done() - assert len(calls) == 3 - assert calls[2].data["some"] == "target_humidity_changed_above_for" + assert len(calls) == 6 + assert {calls[4].data["some"], calls[5].data["some"]} == { + "current_humidity_changed_above_for", + "target_humidity_changed_above_for", + } # Fake turn off - hass.states.async_set(entry.entity_id, STATE_OFF, {const.ATTR_HUMIDITY: 37}) + hass.states.async_set( + entry.entity_id, + STATE_OFF, + {const.ATTR_HUMIDITY: 37, const.ATTR_CURRENT_HUMIDITY: 41}, + ) await hass.async_block_till_done() - assert len(calls) == 5 - assert {calls[3].data["some"], calls[4].data["some"]} == { + assert len(calls) == 8 + assert {calls[6].data["some"], calls[7].data["some"]} == { "turn_off device - humidifier.test_5678 - on - off - None", "turn_on_or_off device - humidifier.test_5678 - on - off - None", } # Fake turn on - hass.states.async_set(entry.entity_id, STATE_ON, {const.ATTR_HUMIDITY: 37}) + hass.states.async_set( + entry.entity_id, + STATE_ON, + {const.ATTR_HUMIDITY: 37, const.ATTR_CURRENT_HUMIDITY: 41}, + ) await hass.async_block_till_done() - assert len(calls) == 7 - assert {calls[5].data["some"], calls[6].data["some"]} == { + assert len(calls) == 10 + assert {calls[8].data["some"], calls[9].data["some"]} == { "turn_on device - humidifier.test_5678 - off - on - None", "turn_on_or_off device - humidifier.test_5678 - off - on - None", }