mirror of
https://github.com/home-assistant/core.git
synced 2025-07-20 03:37:07 +00:00
Allow automation to be turned off without stopping actions (#38436)
This commit is contained in:
parent
1c7cc63f4c
commit
9e12e3f96c
@ -67,6 +67,7 @@ CONF_TRIGGER = "trigger"
|
|||||||
CONF_CONDITION_TYPE = "condition_type"
|
CONF_CONDITION_TYPE = "condition_type"
|
||||||
CONF_INITIAL_STATE = "initial_state"
|
CONF_INITIAL_STATE = "initial_state"
|
||||||
CONF_SKIP_CONDITION = "skip_condition"
|
CONF_SKIP_CONDITION = "skip_condition"
|
||||||
|
CONF_STOP_ACTIONS = "stop_actions"
|
||||||
|
|
||||||
CONDITION_USE_TRIGGER_VALUES = "use_trigger_values"
|
CONDITION_USE_TRIGGER_VALUES = "use_trigger_values"
|
||||||
CONDITION_TYPE_AND = "and"
|
CONDITION_TYPE_AND = "and"
|
||||||
@ -75,6 +76,7 @@ CONDITION_TYPE_OR = "or"
|
|||||||
|
|
||||||
DEFAULT_CONDITION_TYPE = CONDITION_TYPE_AND
|
DEFAULT_CONDITION_TYPE = CONDITION_TYPE_AND
|
||||||
DEFAULT_INITIAL_STATE = True
|
DEFAULT_INITIAL_STATE = True
|
||||||
|
DEFAULT_STOP_ACTIONS = True
|
||||||
|
|
||||||
EVENT_AUTOMATION_RELOADED = "automation_reloaded"
|
EVENT_AUTOMATION_RELOADED = "automation_reloaded"
|
||||||
EVENT_AUTOMATION_TRIGGERED = "automation_triggered"
|
EVENT_AUTOMATION_TRIGGERED = "automation_triggered"
|
||||||
@ -225,7 +227,11 @@ async def async_setup(hass, config):
|
|||||||
)
|
)
|
||||||
component.async_register_entity_service(SERVICE_TOGGLE, {}, "async_toggle")
|
component.async_register_entity_service(SERVICE_TOGGLE, {}, "async_toggle")
|
||||||
component.async_register_entity_service(SERVICE_TURN_ON, {}, "async_turn_on")
|
component.async_register_entity_service(SERVICE_TURN_ON, {}, "async_turn_on")
|
||||||
component.async_register_entity_service(SERVICE_TURN_OFF, {}, "async_turn_off")
|
component.async_register_entity_service(
|
||||||
|
SERVICE_TURN_OFF,
|
||||||
|
{vol.Optional(CONF_STOP_ACTIONS, default=DEFAULT_STOP_ACTIONS): cv.boolean},
|
||||||
|
"async_turn_off",
|
||||||
|
)
|
||||||
|
|
||||||
async def reload_service_handler(service_call):
|
async def reload_service_handler(service_call):
|
||||||
"""Remove all automations and load new ones from config."""
|
"""Remove all automations and load new ones from config."""
|
||||||
@ -261,6 +267,7 @@ class AutomationEntity(ToggleEntity, RestoreEntity):
|
|||||||
self._async_detach_triggers = None
|
self._async_detach_triggers = None
|
||||||
self._cond_func = cond_func
|
self._cond_func = cond_func
|
||||||
self.action_script = action_script
|
self.action_script = action_script
|
||||||
|
self.action_script.change_listener = self.async_write_ha_state
|
||||||
self._last_triggered = None
|
self._last_triggered = None
|
||||||
self._initial_state = initial_state
|
self._initial_state = initial_state
|
||||||
self._is_enabled = False
|
self._is_enabled = False
|
||||||
@ -289,11 +296,10 @@ class AutomationEntity(ToggleEntity, RestoreEntity):
|
|||||||
attrs = {
|
attrs = {
|
||||||
ATTR_LAST_TRIGGERED: self._last_triggered,
|
ATTR_LAST_TRIGGERED: self._last_triggered,
|
||||||
ATTR_MODE: self.action_script.script_mode,
|
ATTR_MODE: self.action_script.script_mode,
|
||||||
|
ATTR_CUR: self.action_script.runs,
|
||||||
}
|
}
|
||||||
if self.action_script.supports_max:
|
if self.action_script.supports_max:
|
||||||
attrs[ATTR_MAX] = self.action_script.max_runs
|
attrs[ATTR_MAX] = self.action_script.max_runs
|
||||||
if self.is_on:
|
|
||||||
attrs[ATTR_CUR] = self.action_script.runs
|
|
||||||
return attrs
|
return attrs
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@ -388,7 +394,10 @@ class AutomationEntity(ToggleEntity, RestoreEntity):
|
|||||||
|
|
||||||
async def async_turn_off(self, **kwargs: Any) -> None:
|
async def async_turn_off(self, **kwargs: Any) -> None:
|
||||||
"""Turn the entity off."""
|
"""Turn the entity off."""
|
||||||
await self.async_disable()
|
if CONF_STOP_ACTIONS in kwargs:
|
||||||
|
await self.async_disable(kwargs[CONF_STOP_ACTIONS])
|
||||||
|
else:
|
||||||
|
await self.async_disable()
|
||||||
|
|
||||||
async def async_trigger(self, variables, skip_condition=False, context=None):
|
async def async_trigger(self, variables, skip_condition=False, context=None):
|
||||||
"""Trigger automation.
|
"""Trigger automation.
|
||||||
@ -456,9 +465,9 @@ class AutomationEntity(ToggleEntity, RestoreEntity):
|
|||||||
)
|
)
|
||||||
self.async_write_ha_state()
|
self.async_write_ha_state()
|
||||||
|
|
||||||
async def async_disable(self):
|
async def async_disable(self, stop_actions=DEFAULT_STOP_ACTIONS):
|
||||||
"""Disable the automation entity."""
|
"""Disable the automation entity."""
|
||||||
if not self._is_enabled:
|
if not self._is_enabled and not self.action_script.runs:
|
||||||
return
|
return
|
||||||
|
|
||||||
self._is_enabled = False
|
self._is_enabled = False
|
||||||
@ -467,7 +476,8 @@ class AutomationEntity(ToggleEntity, RestoreEntity):
|
|||||||
self._async_detach_triggers()
|
self._async_detach_triggers()
|
||||||
self._async_detach_triggers = None
|
self._async_detach_triggers = None
|
||||||
|
|
||||||
await self.action_script.async_stop()
|
if stop_actions:
|
||||||
|
await self.action_script.async_stop()
|
||||||
|
|
||||||
self.async_write_ha_state()
|
self.async_write_ha_state()
|
||||||
|
|
||||||
|
@ -12,6 +12,9 @@ turn_off:
|
|||||||
entity_id:
|
entity_id:
|
||||||
description: Name of the automation to turn off.
|
description: Name of the automation to turn off.
|
||||||
example: "automation.notify_home"
|
example: "automation.notify_home"
|
||||||
|
stop_actions:
|
||||||
|
description: Stop currently running actions (defaults to true).
|
||||||
|
example: false
|
||||||
|
|
||||||
toggle:
|
toggle:
|
||||||
description: Toggle an automation.
|
description: Toggle an automation.
|
||||||
@ -27,7 +30,7 @@ trigger:
|
|||||||
description: Name of the automation to trigger.
|
description: Name of the automation to trigger.
|
||||||
example: "automation.notify_home"
|
example: "automation.notify_home"
|
||||||
skip_condition:
|
skip_condition:
|
||||||
description: Whether or not the condition will be skipped (defaults to True).
|
description: Whether or not the condition will be skipped (defaults to true).
|
||||||
example: true
|
example: true
|
||||||
|
|
||||||
reload:
|
reload:
|
||||||
|
@ -278,11 +278,10 @@ class ScriptEntity(ToggleEntity):
|
|||||||
attrs = {
|
attrs = {
|
||||||
ATTR_LAST_TRIGGERED: self.script.last_triggered,
|
ATTR_LAST_TRIGGERED: self.script.last_triggered,
|
||||||
ATTR_MODE: self.script.script_mode,
|
ATTR_MODE: self.script.script_mode,
|
||||||
|
ATTR_CUR: self.script.runs,
|
||||||
}
|
}
|
||||||
if self.script.supports_max:
|
if self.script.supports_max:
|
||||||
attrs[ATTR_MAX] = self.script.max_runs
|
attrs[ATTR_MAX] = self.script.max_runs
|
||||||
if self.is_on:
|
|
||||||
attrs[ATTR_CUR] = self.script.runs
|
|
||||||
if self.script.last_action:
|
if self.script.last_action:
|
||||||
attrs[ATTR_LAST_ACTION] = self.script.last_action
|
attrs[ATTR_LAST_ACTION] = self.script.last_action
|
||||||
return attrs
|
return attrs
|
||||||
|
@ -390,6 +390,17 @@ async def test_services(hass, calls):
|
|||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
assert len(calls) == 2
|
assert len(calls) == 2
|
||||||
|
|
||||||
|
await common.async_toggle(hass, entity_id)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
assert not automation.is_on(hass, entity_id)
|
||||||
|
hass.bus.async_fire("test_event")
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
assert len(calls) == 2
|
||||||
|
|
||||||
|
await common.async_toggle(hass, entity_id)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
await common.async_trigger(hass, entity_id)
|
await common.async_trigger(hass, entity_id)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
assert len(calls) == 3
|
assert len(calls) == 3
|
||||||
@ -556,9 +567,9 @@ async def test_reload_config_handles_load_fails(hass, calls):
|
|||||||
assert len(calls) == 2
|
assert len(calls) == 2
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("service", ["turn_off", "reload"])
|
@pytest.mark.parametrize("service", ["turn_off_stop", "turn_off_no_stop", "reload"])
|
||||||
async def test_automation_stops(hass, calls, service):
|
async def test_automation_stops(hass, calls, service):
|
||||||
"""Test that turning off / reloading an automation stops any running actions."""
|
"""Test that turning off / reloading stops any running actions as appropriate."""
|
||||||
entity_id = "automation.hello"
|
entity_id = "automation.hello"
|
||||||
test_entity = "test.entity"
|
test_entity = "test.entity"
|
||||||
|
|
||||||
@ -587,13 +598,20 @@ async def test_automation_stops(hass, calls, service):
|
|||||||
hass.bus.async_fire("test_event")
|
hass.bus.async_fire("test_event")
|
||||||
await running.wait()
|
await running.wait()
|
||||||
|
|
||||||
if service == "turn_off":
|
if service == "turn_off_stop":
|
||||||
await hass.services.async_call(
|
await hass.services.async_call(
|
||||||
automation.DOMAIN,
|
automation.DOMAIN,
|
||||||
SERVICE_TURN_OFF,
|
SERVICE_TURN_OFF,
|
||||||
{ATTR_ENTITY_ID: entity_id},
|
{ATTR_ENTITY_ID: entity_id},
|
||||||
blocking=True,
|
blocking=True,
|
||||||
)
|
)
|
||||||
|
elif service == "turn_off_no_stop":
|
||||||
|
await hass.services.async_call(
|
||||||
|
automation.DOMAIN,
|
||||||
|
SERVICE_TURN_OFF,
|
||||||
|
{ATTR_ENTITY_ID: entity_id, automation.CONF_STOP_ACTIONS: False},
|
||||||
|
blocking=True,
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
with patch(
|
with patch(
|
||||||
"homeassistant.config.load_yaml_config_file",
|
"homeassistant.config.load_yaml_config_file",
|
||||||
@ -605,7 +623,7 @@ async def test_automation_stops(hass, calls, service):
|
|||||||
hass.states.async_set(test_entity, "goodbye")
|
hass.states.async_set(test_entity, "goodbye")
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
assert len(calls) == 0
|
assert len(calls) == (1 if service == "turn_off_no_stop" else 0)
|
||||||
|
|
||||||
|
|
||||||
async def test_automation_restore_state(hass):
|
async def test_automation_restore_state(hass):
|
||||||
|
Loading…
x
Reference in New Issue
Block a user