Only reload modified automations (#80282)
* Only reload modified automations * Update tests * Adjust spelling * Improve efficiency of matching automations and configurations * Reload automations without an alias if they have been moved * Add test * Add test * Add test
This commit is contained in:
parent
dd266b7119
commit
81f40afd80
2 changed files with 433 additions and 65 deletions
|
@ -15,6 +15,7 @@ from homeassistant.components.automation import (
|
|||
EVENT_AUTOMATION_RELOADED,
|
||||
EVENT_AUTOMATION_TRIGGERED,
|
||||
SERVICE_TRIGGER,
|
||||
AutomationEntity,
|
||||
)
|
||||
from homeassistant.const import (
|
||||
ATTR_ENTITY_ID,
|
||||
|
@ -720,6 +721,7 @@ async def test_automation_stops(hass, calls, service):
|
|||
blocking=True,
|
||||
)
|
||||
else:
|
||||
config[automation.DOMAIN]["alias"] = "goodbye"
|
||||
with patch(
|
||||
"homeassistant.config.load_yaml_config_file",
|
||||
autospec=True,
|
||||
|
@ -735,6 +737,271 @@ async def test_automation_stops(hass, calls, service):
|
|||
assert len(calls) == (1 if service == "turn_off_no_stop" else 0)
|
||||
|
||||
|
||||
async def test_reload_unchanged_does_not_stop(hass, calls):
|
||||
"""Test that turning off / reloading stops any running actions as appropriate."""
|
||||
test_entity = "test.entity"
|
||||
|
||||
config = {
|
||||
automation.DOMAIN: {
|
||||
"alias": "hello",
|
||||
"trigger": {"platform": "event", "event_type": "test_event"},
|
||||
"action": [
|
||||
{"event": "running"},
|
||||
{"wait_template": "{{ is_state('test.entity', 'goodbye') }}"},
|
||||
{"service": "test.automation"},
|
||||
],
|
||||
}
|
||||
}
|
||||
assert await async_setup_component(hass, automation.DOMAIN, config)
|
||||
|
||||
running = asyncio.Event()
|
||||
|
||||
@callback
|
||||
def running_cb(event):
|
||||
running.set()
|
||||
|
||||
hass.bus.async_listen_once("running", running_cb)
|
||||
hass.states.async_set(test_entity, "hello")
|
||||
|
||||
hass.bus.async_fire("test_event")
|
||||
await running.wait()
|
||||
|
||||
with patch(
|
||||
"homeassistant.config.load_yaml_config_file",
|
||||
autospec=True,
|
||||
return_value=config,
|
||||
):
|
||||
await hass.services.async_call(automation.DOMAIN, SERVICE_RELOAD, blocking=True)
|
||||
|
||||
hass.states.async_set(test_entity, "goodbye")
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert len(calls) == 1
|
||||
|
||||
|
||||
async def test_reload_moved_automation_without_alias(hass, calls):
|
||||
"""Test that changing the order of automations without alias triggers reload."""
|
||||
with patch(
|
||||
"homeassistant.components.automation.AutomationEntity", wraps=AutomationEntity
|
||||
) as automation_entity_init:
|
||||
config = {
|
||||
automation.DOMAIN: [
|
||||
{
|
||||
"trigger": {"platform": "event", "event_type": "test_event"},
|
||||
"action": [{"service": "test.automation"}],
|
||||
},
|
||||
{
|
||||
"alias": "automation_with_alias",
|
||||
"trigger": {"platform": "event", "event_type": "test_event2"},
|
||||
"action": [{"service": "test.automation"}],
|
||||
},
|
||||
]
|
||||
}
|
||||
assert await async_setup_component(hass, automation.DOMAIN, config)
|
||||
assert automation_entity_init.call_count == 2
|
||||
automation_entity_init.reset_mock()
|
||||
|
||||
assert hass.states.get("automation.automation_0")
|
||||
assert not hass.states.get("automation.automation_1")
|
||||
assert hass.states.get("automation.automation_with_alias")
|
||||
|
||||
hass.bus.async_fire("test_event")
|
||||
await hass.async_block_till_done()
|
||||
assert len(calls) == 1
|
||||
|
||||
# Reverse the order of the automations
|
||||
config[automation.DOMAIN].reverse()
|
||||
with patch(
|
||||
"homeassistant.config.load_yaml_config_file",
|
||||
autospec=True,
|
||||
return_value=config,
|
||||
):
|
||||
await hass.services.async_call(
|
||||
automation.DOMAIN, SERVICE_RELOAD, blocking=True
|
||||
)
|
||||
|
||||
assert automation_entity_init.call_count == 1
|
||||
automation_entity_init.reset_mock()
|
||||
|
||||
assert not hass.states.get("automation.automation_0")
|
||||
assert hass.states.get("automation.automation_1")
|
||||
assert hass.states.get("automation.automation_with_alias")
|
||||
|
||||
hass.bus.async_fire("test_event")
|
||||
await hass.async_block_till_done()
|
||||
assert len(calls) == 2
|
||||
|
||||
|
||||
async def test_reload_identical_automations_without_id(hass, calls):
|
||||
"""Test reloading of identical automations without id."""
|
||||
with patch(
|
||||
"homeassistant.components.automation.AutomationEntity", wraps=AutomationEntity
|
||||
) as automation_entity_init:
|
||||
config = {
|
||||
automation.DOMAIN: [
|
||||
{
|
||||
"alias": "dolly",
|
||||
"trigger": {"platform": "event", "event_type": "test_event"},
|
||||
"action": [{"service": "test.automation"}],
|
||||
},
|
||||
{
|
||||
"alias": "dolly",
|
||||
"trigger": {"platform": "event", "event_type": "test_event"},
|
||||
"action": [{"service": "test.automation"}],
|
||||
},
|
||||
{
|
||||
"alias": "dolly",
|
||||
"trigger": {"platform": "event", "event_type": "test_event"},
|
||||
"action": [{"service": "test.automation"}],
|
||||
},
|
||||
]
|
||||
}
|
||||
assert await async_setup_component(hass, automation.DOMAIN, config)
|
||||
assert automation_entity_init.call_count == 3
|
||||
automation_entity_init.reset_mock()
|
||||
|
||||
assert hass.states.get("automation.dolly")
|
||||
assert hass.states.get("automation.dolly_2")
|
||||
assert hass.states.get("automation.dolly_3")
|
||||
|
||||
hass.bus.async_fire("test_event")
|
||||
await hass.async_block_till_done()
|
||||
assert len(calls) == 3
|
||||
|
||||
# Reload the automations without any change
|
||||
with patch(
|
||||
"homeassistant.config.load_yaml_config_file",
|
||||
autospec=True,
|
||||
return_value=config,
|
||||
):
|
||||
await hass.services.async_call(
|
||||
automation.DOMAIN, SERVICE_RELOAD, blocking=True
|
||||
)
|
||||
|
||||
assert automation_entity_init.call_count == 0
|
||||
automation_entity_init.reset_mock()
|
||||
|
||||
assert hass.states.get("automation.dolly")
|
||||
assert hass.states.get("automation.dolly_2")
|
||||
assert hass.states.get("automation.dolly_3")
|
||||
|
||||
hass.bus.async_fire("test_event")
|
||||
await hass.async_block_till_done()
|
||||
assert len(calls) == 6
|
||||
|
||||
# Remove two clones
|
||||
del config[automation.DOMAIN][-1]
|
||||
del config[automation.DOMAIN][-1]
|
||||
with patch(
|
||||
"homeassistant.config.load_yaml_config_file",
|
||||
autospec=True,
|
||||
return_value=config,
|
||||
):
|
||||
await hass.services.async_call(
|
||||
automation.DOMAIN, SERVICE_RELOAD, blocking=True
|
||||
)
|
||||
|
||||
assert automation_entity_init.call_count == 0
|
||||
automation_entity_init.reset_mock()
|
||||
|
||||
assert hass.states.get("automation.dolly")
|
||||
|
||||
hass.bus.async_fire("test_event")
|
||||
await hass.async_block_till_done()
|
||||
assert len(calls) == 7
|
||||
|
||||
# Add two clones
|
||||
config[automation.DOMAIN].append(config[automation.DOMAIN][-1])
|
||||
config[automation.DOMAIN].append(config[automation.DOMAIN][-1])
|
||||
with patch(
|
||||
"homeassistant.config.load_yaml_config_file",
|
||||
autospec=True,
|
||||
return_value=config,
|
||||
):
|
||||
await hass.services.async_call(
|
||||
automation.DOMAIN, SERVICE_RELOAD, blocking=True
|
||||
)
|
||||
|
||||
assert automation_entity_init.call_count == 2
|
||||
automation_entity_init.reset_mock()
|
||||
|
||||
assert hass.states.get("automation.dolly")
|
||||
assert hass.states.get("automation.dolly_2")
|
||||
assert hass.states.get("automation.dolly_3")
|
||||
|
||||
hass.bus.async_fire("test_event")
|
||||
await hass.async_block_till_done()
|
||||
assert len(calls) == 10
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"automation_config",
|
||||
(
|
||||
{
|
||||
"trigger": {"platform": "event", "event_type": "test_event"},
|
||||
"action": [{"service": "test.automation"}],
|
||||
},
|
||||
# An automation using templates
|
||||
{
|
||||
"trigger": {"platform": "event", "event_type": "test_event"},
|
||||
"action": [{"service": "{{ 'test.automation' }}"}],
|
||||
},
|
||||
# An automation using blueprint
|
||||
{
|
||||
"use_blueprint": {
|
||||
"path": "test_event_service.yaml",
|
||||
"input": {
|
||||
"trigger_event": "test_event",
|
||||
"service_to_call": "test.automation",
|
||||
"a_number": 5,
|
||||
},
|
||||
}
|
||||
},
|
||||
# An automation using blueprint with templated input
|
||||
{
|
||||
"use_blueprint": {
|
||||
"path": "test_event_service.yaml",
|
||||
"input": {
|
||||
"trigger_event": "{{ 'test_event' }}",
|
||||
"service_to_call": "{{ 'test.automation' }}",
|
||||
"a_number": 5,
|
||||
},
|
||||
}
|
||||
},
|
||||
),
|
||||
)
|
||||
async def test_reload_unchanged_automation(hass, calls, automation_config):
|
||||
"""Test an unmodified automation is not reloaded."""
|
||||
with patch(
|
||||
"homeassistant.components.automation.AutomationEntity", wraps=AutomationEntity
|
||||
) as automation_entity_init:
|
||||
config = {automation.DOMAIN: [automation_config]}
|
||||
assert await async_setup_component(hass, automation.DOMAIN, config)
|
||||
assert automation_entity_init.call_count == 1
|
||||
automation_entity_init.reset_mock()
|
||||
|
||||
hass.bus.async_fire("test_event")
|
||||
await hass.async_block_till_done()
|
||||
assert len(calls) == 1
|
||||
|
||||
# Reload the automations without any change
|
||||
with patch(
|
||||
"homeassistant.config.load_yaml_config_file",
|
||||
autospec=True,
|
||||
return_value=config,
|
||||
):
|
||||
await hass.services.async_call(
|
||||
automation.DOMAIN, SERVICE_RELOAD, blocking=True
|
||||
)
|
||||
|
||||
assert automation_entity_init.call_count == 0
|
||||
automation_entity_init.reset_mock()
|
||||
|
||||
hass.bus.async_fire("test_event")
|
||||
await hass.async_block_till_done()
|
||||
assert len(calls) == 2
|
||||
|
||||
|
||||
async def test_automation_restore_state(hass):
|
||||
"""Ensure states are restored on startup."""
|
||||
time = dt_util.utcnow()
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue