core/homeassistant/helpers/script.py

127 lines
4.2 KiB
Python
Raw Normal View History

2016-04-21 22:52:20 +00:00
"""Helpers to execute scripts."""
import logging
import threading
from itertools import islice
import homeassistant.util.dt as date_util
from homeassistant.const import EVENT_TIME_CHANGED
from homeassistant.helpers.event import track_point_in_utc_time
from homeassistant.helpers import service
import homeassistant.helpers.config_validation as cv
_LOGGER = logging.getLogger(__name__)
CONF_ALIAS = "alias"
CONF_SERVICE = "service"
CONF_SERVICE_DATA = "data"
CONF_SEQUENCE = "sequence"
CONF_EVENT = "event"
CONF_EVENT_DATA = "event_data"
CONF_DELAY = "delay"
2016-04-22 09:30:30 +00:00
def call_from_config(hass, config, variables=None):
2016-04-21 22:52:20 +00:00
"""Call a script based on a config entry."""
2016-04-22 09:30:30 +00:00
Script(hass, config).run(variables)
2016-04-21 22:52:20 +00:00
class Script():
"""Representation of a script."""
# pylint: disable=too-many-instance-attributes
def __init__(self, hass, sequence, name=None, change_listener=None):
"""Initialize the script."""
self.hass = hass
self.sequence = cv.SCRIPT_SCHEMA(sequence)
self.name = name
self._change_listener = change_listener
self._cur = -1
self.last_action = None
self.can_cancel = any(CONF_DELAY in action for action
in self.sequence)
self._lock = threading.Lock()
self._delay_listener = None
@property
def is_running(self):
"""Return true if script is on."""
return self._cur != -1
2016-04-22 01:42:20 +00:00
def run(self, variables=None):
2016-04-21 22:52:20 +00:00
"""Run script."""
with self._lock:
if self._cur == -1:
self._log('Running script')
self._cur = 0
# Unregister callback if we were in a delay but turn on is called
# again. In that case we just continue execution.
self._remove_listener()
for cur, action in islice(enumerate(self.sequence), self._cur,
None):
if CONF_DELAY in action:
# Call ourselves in the future to continue work
def script_delay(now):
"""Called after delay is done."""
self._delay_listener = None
2016-04-22 01:42:20 +00:00
self.run(variables)
2016-04-21 22:52:20 +00:00
self._delay_listener = track_point_in_utc_time(
self.hass, script_delay,
date_util.utcnow() + action[CONF_DELAY])
self._cur = cur + 1
if self._change_listener:
self._change_listener()
return
elif CONF_EVENT in action:
self._fire_event(action)
2016-04-23 05:11:21 +00:00
else:
self._call_service(action, variables)
2016-04-21 22:52:20 +00:00
self._cur = -1
self.last_action = None
if self._change_listener:
self._change_listener()
def stop(self):
"""Stop running script."""
with self._lock:
if self._cur == -1:
return
self._cur = -1
self._remove_listener()
if self._change_listener:
self._change_listener()
2016-04-22 01:42:20 +00:00
def _call_service(self, action, variables):
2016-04-21 22:52:20 +00:00
"""Call the service specified in the action."""
self.last_action = action.get(CONF_ALIAS, 'call service')
self._log("Executing step %s", self.last_action)
2016-04-23 05:11:21 +00:00
service.call_from_config(self.hass, action, True, variables,
validate_config=False)
2016-04-21 22:52:20 +00:00
def _fire_event(self, action):
"""Fire an event."""
self.last_action = action.get(CONF_ALIAS, action[CONF_EVENT])
self._log("Executing step %s", self.last_action)
self.hass.bus.fire(action[CONF_EVENT], action.get(CONF_EVENT_DATA))
def _remove_listener(self):
"""Remove point in time listener, if any."""
if self._delay_listener:
self.hass.bus.remove_listener(EVENT_TIME_CHANGED,
self._delay_listener)
self._delay_listener = None
def _log(self, msg, *substitutes):
"""Logger helper."""
if self.name is not None:
msg = "Script {}: {}".format(self.name, msg, *substitutes)
_LOGGER.info(msg)