Allow triggering on all state changes, ignoring attributes (#59713)
* Allow triggering on all state changes, ignoring attributes * Add comment * Apply suggestions from code review Co-authored-by: Franck Nijhof <git@frenck.dev> Co-authored-by: Franck Nijhof <git@frenck.dev>pull/59768/head
parent
9256a033a6
commit
4f01631bd6
|
@ -39,8 +39,8 @@ BASE_SCHEMA = cv.TRIGGER_BASE_SCHEMA.extend(
|
|||
TRIGGER_STATE_SCHEMA = BASE_SCHEMA.extend(
|
||||
{
|
||||
# These are str on purpose. Want to catch YAML conversions
|
||||
vol.Optional(CONF_FROM): vol.Any(str, [str]),
|
||||
vol.Optional(CONF_TO): vol.Any(str, [str]),
|
||||
vol.Optional(CONF_FROM): vol.Any(str, [str], None),
|
||||
vol.Optional(CONF_TO): vol.Any(str, [str], None),
|
||||
}
|
||||
)
|
||||
|
||||
|
@ -75,11 +75,15 @@ async def async_attach_trigger(
|
|||
) -> CALLBACK_TYPE:
|
||||
"""Listen for state changes based on configuration."""
|
||||
entity_id = config.get(CONF_ENTITY_ID)
|
||||
from_state = config.get(CONF_FROM, MATCH_ALL)
|
||||
to_state = config.get(CONF_TO, MATCH_ALL)
|
||||
if (from_state := config.get(CONF_FROM)) is None:
|
||||
from_state = MATCH_ALL
|
||||
if (to_state := config.get(CONF_TO)) is None:
|
||||
to_state = MATCH_ALL
|
||||
time_delta = config.get(CONF_FOR)
|
||||
template.attach(hass, time_delta)
|
||||
match_all = from_state == MATCH_ALL and to_state == MATCH_ALL
|
||||
# If neither CONF_FROM or CONF_TO are specified,
|
||||
# fire on all changes to the state or an attribute
|
||||
match_all = CONF_FROM not in config and CONF_TO not in config
|
||||
unsub_track_same = {}
|
||||
period: dict[str, timedelta] = {}
|
||||
match_from_state = process_state_match(from_state)
|
||||
|
|
|
@ -106,7 +106,7 @@ async def test_if_fires_on_entity_change_with_from_filter(hass, calls):
|
|||
|
||||
|
||||
async def test_if_fires_on_entity_change_with_to_filter(hass, calls):
|
||||
"""Test for firing on entity change with no filter."""
|
||||
"""Test for firing on entity change with to filter."""
|
||||
assert await async_setup_component(
|
||||
hass,
|
||||
automation.DOMAIN,
|
||||
|
@ -128,6 +128,54 @@ async def test_if_fires_on_entity_change_with_to_filter(hass, calls):
|
|||
assert len(calls) == 1
|
||||
|
||||
|
||||
async def test_if_fires_on_entity_change_with_from_filter_all(hass, calls):
|
||||
"""Test for firing on entity change with filter."""
|
||||
assert await async_setup_component(
|
||||
hass,
|
||||
automation.DOMAIN,
|
||||
{
|
||||
automation.DOMAIN: {
|
||||
"trigger": {
|
||||
"platform": "state",
|
||||
"entity_id": "test.entity",
|
||||
"from": None,
|
||||
},
|
||||
"action": {"service": "test.automation"},
|
||||
}
|
||||
},
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
hass.states.async_set("test.entity", "world")
|
||||
hass.states.async_set("test.entity", "world", {"attribute": 5})
|
||||
await hass.async_block_till_done()
|
||||
assert len(calls) == 1
|
||||
|
||||
|
||||
async def test_if_fires_on_entity_change_with_to_filter_all(hass, calls):
|
||||
"""Test for firing on entity change with to filter."""
|
||||
assert await async_setup_component(
|
||||
hass,
|
||||
automation.DOMAIN,
|
||||
{
|
||||
automation.DOMAIN: {
|
||||
"trigger": {
|
||||
"platform": "state",
|
||||
"entity_id": "test.entity",
|
||||
"to": None,
|
||||
},
|
||||
"action": {"service": "test.automation"},
|
||||
}
|
||||
},
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
hass.states.async_set("test.entity", "world")
|
||||
hass.states.async_set("test.entity", "world", {"attribute": 5})
|
||||
await hass.async_block_till_done()
|
||||
assert len(calls) == 1
|
||||
|
||||
|
||||
async def test_if_fires_on_attribute_change_with_to_filter(hass, calls):
|
||||
"""Test for not firing on attribute change."""
|
||||
assert await async_setup_component(
|
||||
|
@ -1217,6 +1265,94 @@ async def test_attribute_if_fires_on_entity_where_attr_stays_constant(hass, call
|
|||
assert len(calls) == 1
|
||||
|
||||
|
||||
async def test_attribute_if_fires_on_entity_where_attr_stays_constant_filter(
|
||||
hass, calls
|
||||
):
|
||||
"""Test for firing if attribute stays the same."""
|
||||
hass.states.async_set("test.entity", "bla", {"name": "other_name"})
|
||||
|
||||
assert await async_setup_component(
|
||||
hass,
|
||||
automation.DOMAIN,
|
||||
{
|
||||
automation.DOMAIN: {
|
||||
"trigger": {
|
||||
"platform": "state",
|
||||
"entity_id": "test.entity",
|
||||
"attribute": "name",
|
||||
"to": "best_name",
|
||||
},
|
||||
"action": {"service": "test.automation"},
|
||||
}
|
||||
},
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
# Leave all attributes the same
|
||||
hass.states.async_set(
|
||||
"test.entity", "bla", {"name": "best_name", "other": "old_value"}
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
assert len(calls) == 1
|
||||
|
||||
# Change the untracked attribute
|
||||
hass.states.async_set(
|
||||
"test.entity", "bla", {"name": "best_name", "other": "new_value"}
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
assert len(calls) == 1
|
||||
|
||||
# Change the tracked attribute
|
||||
hass.states.async_set(
|
||||
"test.entity", "bla", {"name": "other_name", "other": "old_value"}
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
assert len(calls) == 1
|
||||
|
||||
|
||||
async def test_attribute_if_fires_on_entity_where_attr_stays_constant_all(hass, calls):
|
||||
"""Test for firing if attribute stays the same."""
|
||||
hass.states.async_set("test.entity", "bla", {"name": "hello", "other": "old_value"})
|
||||
|
||||
assert await async_setup_component(
|
||||
hass,
|
||||
automation.DOMAIN,
|
||||
{
|
||||
automation.DOMAIN: {
|
||||
"trigger": {
|
||||
"platform": "state",
|
||||
"entity_id": "test.entity",
|
||||
"attribute": "name",
|
||||
"to": None,
|
||||
},
|
||||
"action": {"service": "test.automation"},
|
||||
}
|
||||
},
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
# Leave all attributes the same
|
||||
hass.states.async_set(
|
||||
"test.entity", "bla", {"name": "name_1", "other": "old_value"}
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
assert len(calls) == 1
|
||||
|
||||
# Change the untracked attribute
|
||||
hass.states.async_set(
|
||||
"test.entity", "bla", {"name": "name_1", "other": "new_value"}
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
assert len(calls) == 1
|
||||
|
||||
# Change the tracked attribute
|
||||
hass.states.async_set(
|
||||
"test.entity", "bla", {"name": "name_2", "other": "old_value"}
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
assert len(calls) == 2
|
||||
|
||||
|
||||
async def test_attribute_if_not_fires_on_entities_change_with_for_after_stop(
|
||||
hass, calls
|
||||
):
|
||||
|
|
Loading…
Reference in New Issue