From aca375c31279ab61a504cbd15e7d6d1ddba5ddd1 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Sat, 17 Sep 2016 18:28:01 -0700 Subject: [PATCH] Asyncio event helpers (#3415) * Automation - Event: Use coroutine * Convert event helpers to coroutine * Fix linting * Add hass.async_add_job * Automation - Event to use async_add_job --- homeassistant/components/automation/event.py | 4 +++- homeassistant/core.py | 16 ++++++++++++--- homeassistant/helpers/event.py | 21 ++++++++++++++------ 3 files changed, 31 insertions(+), 10 deletions(-) diff --git a/homeassistant/components/automation/event.py b/homeassistant/components/automation/event.py index 795dd94a71f..7b77dd04627 100644 --- a/homeassistant/components/automation/event.py +++ b/homeassistant/components/automation/event.py @@ -4,6 +4,7 @@ Offer event listening automation rules. For more details about this automation rule, please refer to the documentation at https://home-assistant.io/components/automation/#event-trigger """ +import asyncio import logging import voluptuous as vol @@ -28,11 +29,12 @@ def trigger(hass, config, action): event_type = config.get(CONF_EVENT_TYPE) event_data = config.get(CONF_EVENT_DATA) + @asyncio.coroutine def handle_event(event): """Listen for events and calls the action when data matches.""" if not event_data or all(val == event.data.get(key) for key, val in event_data.items()): - action({ + hass.async_add_job(action, { 'trigger': { 'platform': 'event', 'event': event, diff --git a/homeassistant/core.py b/homeassistant/core.py index 50b04e79f6d..48702c15513 100644 --- a/homeassistant/core.py +++ b/homeassistant/core.py @@ -6,6 +6,7 @@ of entities and react to changes. """ # pylint: disable=unused-import, too-many-lines import asyncio +from concurrent.futures import ThreadPoolExecutor import enum import functools as ft import logging @@ -13,8 +14,8 @@ import os import re import signal import sys +import threading import time -from concurrent.futures import ThreadPoolExecutor from types import MappingProxyType @@ -205,6 +206,17 @@ class HomeAssistant(object): """ self.pool.add_job(priority, (target,) + args) + def async_add_job(self, target: Callable[..., None], *args: Any): + """Add a job from within the eventloop. + + target: target to call. + args: parameters for method to call. + """ + if asyncio.iscoroutinefunction(target): + self.loop.create_task(target(*args)) + else: + self.add_job(target, *args) + def _loop_empty(self): """Python 3.4.2 empty loop compatibility function.""" # pylint: disable=protected-access @@ -217,8 +229,6 @@ class HomeAssistant(object): def block_till_done(self): """Block till all pending work is done.""" - import threading - complete = threading.Event() @asyncio.coroutine diff --git a/homeassistant/helpers/event.py b/homeassistant/helpers/event.py index 512b173a249..ab0641cab9e 100644 --- a/homeassistant/helpers/event.py +++ b/homeassistant/helpers/event.py @@ -1,4 +1,5 @@ """Helpers for listening to events.""" +import asyncio import functools as ft from datetime import timedelta @@ -28,6 +29,7 @@ def track_state_change(hass, entity_ids, action, from_state=None, entity_ids = tuple(entity_id.lower() for entity_id in entity_ids) @ft.wraps(action) + @asyncio.coroutine def state_change_listener(event): """The listener that listens for specific state changes.""" if entity_ids != MATCH_ALL and \ @@ -45,9 +47,9 @@ def track_state_change(hass, entity_ids, action, from_state=None, new_state = None if _matcher(old_state, from_state) and _matcher(new_state, to_state): - action(event.data.get('entity_id'), - event.data.get('old_state'), - event.data.get('new_state')) + hass.async_add_job(action, event.data.get('entity_id'), + event.data.get('old_state'), + event.data.get('new_state')) return hass.bus.listen(EVENT_STATE_CHANGED, state_change_listener) @@ -70,6 +72,7 @@ def track_point_in_utc_time(hass, action, point_in_time): point_in_time = dt_util.as_utc(point_in_time) @ft.wraps(action) + @asyncio.coroutine def point_in_time_listener(event): """Listen for matching time_changed events.""" now = event.data[ATTR_NOW] @@ -83,8 +86,13 @@ def track_point_in_utc_time(hass, action, point_in_time): # listener gets lined up twice to be executed. This will make # sure the second time it does nothing. point_in_time_listener.run = True - remove() - action(now) + + def fire_action(): + """Run the point in time listener action.""" + remove() + action(now) + + hass.add_job(fire_action) remove = hass.bus.listen(EVENT_TIME_CHANGED, point_in_time_listener) return remove @@ -171,6 +179,7 @@ def track_utc_time_change(hass, action, year=None, month=None, day=None, hour, minute, second = pmp(hour), pmp(minute), pmp(second) @ft.wraps(action) + @asyncio.coroutine def pattern_time_change_listener(event): """Listen for matching time_changed events.""" now = event.data[ATTR_NOW] @@ -187,7 +196,7 @@ def track_utc_time_change(hass, action, year=None, month=None, day=None, mat(now.minute, minute) and \ mat(now.second, second): - action(now) + hass.async_add_job(action, now) return hass.bus.listen(EVENT_TIME_CHANGED, pattern_time_change_listener)