From 0f937cad7452e1b606dddd4f25666572dbf43b97 Mon Sep 17 00:00:00 2001 From: Ryan Kraus Date: Sun, 24 Jan 2016 15:28:09 -0500 Subject: [PATCH] Initial pass at event decorators Created event decorators for custom components. Decorators were created for the events: track_state_change, track_sunrise, track_sunset, and track_time_change. --- homeassistant/bootstrap.py | 4 + homeassistant/helpers/event_decorators.py | 146 ++++++++++++++++++++++ 2 files changed, 150 insertions(+) create mode 100644 homeassistant/helpers/event_decorators.py diff --git a/homeassistant/bootstrap.py b/homeassistant/bootstrap.py index b704fc082ac..e78d70fd11a 100644 --- a/homeassistant/bootstrap.py +++ b/homeassistant/bootstrap.py @@ -24,6 +24,7 @@ import homeassistant.config as config_util import homeassistant.loader as loader import homeassistant.components as core_components import homeassistant.components.group as group +from homeassistnat.helpers import event_decorators from homeassistant.helpers.entity import Entity from homeassistant.const import ( __version__, EVENT_COMPONENT_LOADED, CONF_LATITUDE, CONF_LONGITUDE, @@ -203,6 +204,9 @@ def from_config_dict(config, hass=None, config_dir=None, enable_log=True, for domain in loader.load_order_components(components): _setup_component(hass, domain, config) + # activate event decorators + event_decorators.activate(hass) + return hass diff --git a/homeassistant/helpers/event_decorators.py b/homeassistant/helpers/event_decorators.py new file mode 100644 index 00000000000..35b1247cf60 --- /dev/null +++ b/homeassistant/helpers/event_decorators.py @@ -0,0 +1,146 @@ +""" Event Decorators for custom components """ + +from datetime import datetime +import functools +import inspect +import logging + +from homeassistant.helpers import event +from homeassistant.components import logbook + +REGISTERED_DECORATORS = [] +_LOGGER = logging.getLogger(__name__) + + +def track_state_change(entity_ids, from_state=None, to_state=None): + """ Decorator factory to track state changes for entity id """ + + def track_state_change_decorator(action): + """ Decorator to track state changes """ + return Automation(action, event.track_state_change, + {"entity_ids": entity_ids, "from_state": from_state, + "to_state": to_state}) + + return track_state_change_decorator + + +def track_sunrise(offset=None): + """ Decorator factory to track sunrise events """ + + def track_sunrise_decorator(action): + """ Decorator to track sunrise events """ + return Automation(action, event.track_sunrise, {"offset": offset}) + + return track_sunrise_decorator + + +def track_sunset(offset=None): + """ Decorator factory to track sunset events """ + + def track_sunset_decorator(action): + """ Decorator to track sunset events """ + return Automation(action, event.track_sunset, {"offset": offset}) + + return track_sunset_decorator + + +# pylint: disable=too-many-arguments +def track_time_change(year=None, month=None, day=None, hour=None, minute=None, + second=None): + """ Decorator factory to track time changes """ + + def track_time_change_decorator(action): + """ Decorator to track time changes """ + return Automation(action, event.track_time_change, + {"year": year, "month": month, "day": day, + "hour": hour, "minute": minute, "second": second}) + + return track_time_change_decorator + + +def activate(hass): + """ Activate all event decorators """ + Automation.hass = hass + + return all([rule.activate() for rule in REGISTERED_DECORATORS]) + + +class Automation(object): + """ Base Decorator for automation functions """ + + hass = None + + def __init__(self, action, event, event_args): + # store action and config + self.action = action + self._event = (event, event_args) + self._activated = False + self._last_run = None + self._running = 0 + module = inspect.getmodule(action) + self._domain = module.DOMAIN + + REGISTERED_DECORATORS.append(self) + + functools.update_wrapper(self, action) + + def __call__(self, *args, **kwargs): + """ Call the action """ + if not self.activated: + return + + self._running += 1 + + _LOGGER.info('Executing %s', self.alias) + logbook.log_entry(self.hass, self.alias, 'has been triggered', + self._domain) + + try: + self.action(*args, **kwargs) + except Exception: + _LOGGER.exception('Error running Python automation: %s', + self.alias) + else: + self._last_run = datetime.now() + + self._running -= 1 + + @property + def alias(self): + """ The name of the action """ + return self.action.__name__ + + @property + def domain(self): + """ The domain to which this automation belongs """ + return self._domain + + @property + def is_running(self): + """ Boolean if the automation is running """ + return self._running > 0 + + @property + def num_running(self): + """ Integer of how many instances of the automation are running """ + return self._running + + @property + def activated(self): + """ Boolean indicating if the automation has been activated """ + return self._activated + + @property + def last_run(self): + """ Datetime object of the last automation completion """ + return self._last_run + + def activate(self): + """ Activates the automation with HASS """ + if self.activated: + return True + + self._event[0](hass=self.hass, action=self.action, **self._event[1]) + + self._activated = True + return True