-
-
Notifications
You must be signed in to change notification settings - Fork 33.8k
Allow reloading automation without restarting HA #3002
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
365e345
5411c80
60cfedb
00dbef9
3962bc1
2f55454
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -6,10 +6,13 @@ | |
""" | ||
from functools import partial | ||
import logging | ||
import os | ||
|
||
import voluptuous as vol | ||
|
||
from homeassistant.bootstrap import prepare_setup_platform | ||
from homeassistant.bootstrap import ( | ||
prepare_setup_platform, prepare_setup_component) | ||
from homeassistant import config as conf_util | ||
from homeassistant.const import ( | ||
ATTR_ENTITY_ID, CONF_PLATFORM, STATE_ON, SERVICE_TURN_ON, SERVICE_TURN_OFF, | ||
SERVICE_TOGGLE) | ||
|
@@ -46,6 +49,7 @@ | |
ATTR_LAST_TRIGGERED = 'last_triggered' | ||
ATTR_VARIABLES = 'variables' | ||
SERVICE_TRIGGER = 'trigger' | ||
SERVICE_RELOAD = 'reload' | ||
|
||
_LOGGER = logging.getLogger(__name__) | ||
|
||
|
@@ -112,6 +116,8 @@ def validator(config): | |
vol.Optional(ATTR_VARIABLES, default={}): dict, | ||
}) | ||
|
||
RELOAD_SERVICE_SCHEMA = vol.Schema({}) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should this be |
||
|
||
|
||
def is_on(hass, entity_id=None): | ||
""" | ||
|
@@ -148,40 +154,23 @@ def trigger(hass, entity_id=None): | |
hass.services.call(DOMAIN, SERVICE_TRIGGER, data) | ||
|
||
|
||
def reload(hass): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Since we're only reloading the automations: There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's part of the from homeassistant.components import automation
automation.reload(hass) Which is in line with the |
||
"""Reload the automation from config.""" | ||
hass.services.call(DOMAIN, SERVICE_RELOAD) | ||
|
||
|
||
def setup(hass, config): | ||
"""Setup the automation.""" | ||
# pylint: disable=too-many-locals | ||
component = EntityComponent(_LOGGER, DOMAIN, hass) | ||
|
||
success = False | ||
for config_key in extract_domain_configs(config, DOMAIN): | ||
conf = config[config_key] | ||
|
||
for list_no, config_block in enumerate(conf): | ||
name = config_block.get(CONF_ALIAS) or "{} {}".format(config_key, | ||
list_no) | ||
|
||
action = _get_action(hass, config_block.get(CONF_ACTION, {}), name) | ||
|
||
if CONF_CONDITION in config_block: | ||
cond_func = _process_if(hass, config, config_block) | ||
|
||
if cond_func is None: | ||
continue | ||
else: | ||
def cond_func(variables): | ||
"""Condition will always pass.""" | ||
return True | ||
|
||
attach_triggers = partial(_process_trigger, hass, config, | ||
config_block.get(CONF_TRIGGER, []), name) | ||
entity = AutomationEntity(name, attach_triggers, cond_func, action) | ||
component.add_entities((entity,)) | ||
success = True | ||
success = _process_config(hass, config, component) | ||
|
||
if not success: | ||
return False | ||
|
||
descriptions = conf_util.load_yaml_config_file( | ||
os.path.join(os.path.dirname(__file__), 'services.yaml')) | ||
|
||
def trigger_service_handler(service_call): | ||
"""Handle automation triggers.""" | ||
for entity in component.extract_from_service(service_call): | ||
|
@@ -192,11 +181,34 @@ def service_handler(service_call): | |
for entity in component.extract_from_service(service_call): | ||
getattr(entity, service_call.service)() | ||
|
||
def reload_service_handler(service_call): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
"""Remove all automations and load new ones from config.""" | ||
try: | ||
path = conf_util.find_config_file(hass.config.config_dir) | ||
conf = conf_util.load_yaml_config_file(path) | ||
except HomeAssistantError as err: | ||
_LOGGER.error(err) | ||
return | ||
|
||
conf = prepare_setup_component(hass, conf, DOMAIN) | ||
|
||
if conf is None: | ||
return | ||
|
||
component.reset() | ||
_process_config(hass, conf, component) | ||
|
||
hass.services.register(DOMAIN, SERVICE_TRIGGER, trigger_service_handler, | ||
descriptions.get(SERVICE_TRIGGER), | ||
schema=TRIGGER_SERVICE_SCHEMA) | ||
|
||
hass.services.register(DOMAIN, SERVICE_RELOAD, reload_service_handler, | ||
descriptions.get(SERVICE_RELOAD), | ||
schema=RELOAD_SERVICE_SCHEMA) | ||
|
||
for service in (SERVICE_TURN_ON, SERVICE_TURN_OFF, SERVICE_TOGGLE): | ||
hass.services.register(DOMAIN, service, service_handler, | ||
descriptions.get(service), | ||
schema=SERVICE_SCHEMA) | ||
|
||
return True | ||
|
@@ -263,6 +275,43 @@ def trigger(self, variables): | |
self._last_triggered = utcnow() | ||
self.update_ha_state() | ||
|
||
def remove(self): | ||
"""Remove automation from HASS.""" | ||
self.turn_off() | ||
super().remove() | ||
|
||
|
||
def _process_config(hass, config, component): | ||
"""Process config and add automations.""" | ||
success = False | ||
|
||
for config_key in extract_domain_configs(config, DOMAIN): | ||
conf = config[config_key] | ||
|
||
for list_no, config_block in enumerate(conf): | ||
name = config_block.get(CONF_ALIAS) or "{} {}".format(config_key, | ||
list_no) | ||
|
||
action = _get_action(hass, config_block.get(CONF_ACTION, {}), name) | ||
|
||
if CONF_CONDITION in config_block: | ||
cond_func = _process_if(hass, config, config_block) | ||
|
||
if cond_func is None: | ||
continue | ||
else: | ||
def cond_func(variables): | ||
"""Condition will always pass.""" | ||
return True | ||
|
||
attach_triggers = partial(_process_trigger, hass, config, | ||
config_block.get(CONF_TRIGGER, []), name) | ||
entity = AutomationEntity(name, attach_triggers, cond_func, action) | ||
component.add_entities((entity,)) | ||
success = True | ||
|
||
return success | ||
|
||
|
||
def _get_action(hass, config, name): | ||
"""Return an action based on a configuration.""" | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
turn_on: | ||
description: Enable an automation. | ||
|
||
fields: | ||
entity_id: | ||
description: Name of the automation to turn on. | ||
example: 'automation.notify_home' | ||
|
||
turn_off: | ||
description: Disable an automation. | ||
|
||
fields: | ||
entity_id: | ||
description: Name of the automation to turn off. | ||
example: 'automation.notify_home' | ||
|
||
toggle: | ||
description: Toggle an automation. | ||
|
||
fields: | ||
entity_id: | ||
description: Name of the automation to toggle on/off. | ||
example: 'automation.notify_home' | ||
|
||
trigger: | ||
description: Trigger the action of an automation. | ||
|
||
fields: | ||
entity_id: | ||
description: Name of the automation to trigger. | ||
example: 'automation.notify_home' | ||
|
||
reload: | ||
description: Reload the automation configuration. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is slightly different from what I envisioned in #2912 - to have a function for config validation only
But I'm taking my sweet time, so let me rebase that PR from this one and continue working on it
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I went this approach to be in line with the
prepare_setup_platform