Make helpers.script async
This commit is contained in:
parent
185bd6c28a
commit
b8504f8fc8
1 changed files with 81 additions and 60 deletions
|
@ -1,6 +1,6 @@
|
||||||
"""Helpers to execute scripts."""
|
"""Helpers to execute scripts."""
|
||||||
|
import asyncio
|
||||||
import logging
|
import logging
|
||||||
import threading
|
|
||||||
from itertools import islice
|
from itertools import islice
|
||||||
from typing import Optional, Sequence
|
from typing import Optional, Sequence
|
||||||
|
|
||||||
|
@ -10,9 +10,11 @@ from homeassistant.core import HomeAssistant
|
||||||
from homeassistant.const import CONF_CONDITION
|
from homeassistant.const import CONF_CONDITION
|
||||||
from homeassistant.helpers import (
|
from homeassistant.helpers import (
|
||||||
service, condition, template, config_validation as cv)
|
service, condition, template, config_validation as cv)
|
||||||
from homeassistant.helpers.event import track_point_in_utc_time
|
from homeassistant.helpers.event import async_track_point_in_utc_time
|
||||||
from homeassistant.helpers.typing import ConfigType
|
from homeassistant.helpers.typing import ConfigType
|
||||||
import homeassistant.util.dt as date_util
|
import homeassistant.util.dt as date_util
|
||||||
|
from homeassistant.util.async import (
|
||||||
|
run_coroutine_threadsafe, run_callback_threadsafe)
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
@ -47,8 +49,7 @@ class Script():
|
||||||
self.last_action = None
|
self.last_action = None
|
||||||
self.can_cancel = any(CONF_DELAY in action for action
|
self.can_cancel = any(CONF_DELAY in action for action
|
||||||
in self.sequence)
|
in self.sequence)
|
||||||
self._lock = threading.Lock()
|
self._async_unsub_delay_listener = None
|
||||||
self._unsub_delay_listener = None
|
|
||||||
self._template_cache = {}
|
self._template_cache = {}
|
||||||
|
|
||||||
@property
|
@property
|
||||||
|
@ -56,26 +57,35 @@ class Script():
|
||||||
"""Return true if script is on."""
|
"""Return true if script is on."""
|
||||||
return self._cur != -1
|
return self._cur != -1
|
||||||
|
|
||||||
def run(self, variables: Optional[Sequence]=None) -> None:
|
def run(self, variables=None):
|
||||||
"""Run script."""
|
"""Run script."""
|
||||||
with self._lock:
|
run_coroutine_threadsafe(
|
||||||
|
self.async_run(variables), self.hass.loop).result()
|
||||||
|
|
||||||
|
@asyncio.coroutine
|
||||||
|
def async_run(self, variables: Optional[Sequence]=None) -> None:
|
||||||
|
"""Run script.
|
||||||
|
|
||||||
|
Returns a coroutine.
|
||||||
|
"""
|
||||||
if self._cur == -1:
|
if self._cur == -1:
|
||||||
self._log('Running script')
|
self._log('Running script')
|
||||||
self._cur = 0
|
self._cur = 0
|
||||||
|
|
||||||
# Unregister callback if we were in a delay but turn on is called
|
# Unregister callback if we were in a delay but turn on is called
|
||||||
# again. In that case we just continue execution.
|
# again. In that case we just continue execution.
|
||||||
self._remove_listener()
|
self._async_remove_listener()
|
||||||
|
|
||||||
for cur, action in islice(enumerate(self.sequence), self._cur,
|
for cur, action in islice(enumerate(self.sequence), self._cur,
|
||||||
None):
|
None):
|
||||||
|
|
||||||
if CONF_DELAY in action:
|
if CONF_DELAY in action:
|
||||||
# Call ourselves in the future to continue work
|
# Call ourselves in the future to continue work
|
||||||
|
@asyncio.coroutine
|
||||||
def script_delay(now):
|
def script_delay(now):
|
||||||
"""Called after delay is done."""
|
"""Called after delay is done."""
|
||||||
self._unsub_delay_listener = None
|
self._async_unsub_delay_listener = None
|
||||||
self.run(variables)
|
yield from self.async_run(variables)
|
||||||
|
|
||||||
delay = action[CONF_DELAY]
|
delay = action[CONF_DELAY]
|
||||||
|
|
||||||
|
@ -83,67 +93,71 @@ class Script():
|
||||||
delay = vol.All(
|
delay = vol.All(
|
||||||
cv.time_period,
|
cv.time_period,
|
||||||
cv.positive_timedelta)(
|
cv.positive_timedelta)(
|
||||||
delay.render())
|
delay.async_render())
|
||||||
|
|
||||||
self._unsub_delay_listener = track_point_in_utc_time(
|
self._async_unsub_delay_listener = \
|
||||||
|
async_track_point_in_utc_time(
|
||||||
self.hass, script_delay,
|
self.hass, script_delay,
|
||||||
date_util.utcnow() + delay)
|
date_util.utcnow() + delay)
|
||||||
self._cur = cur + 1
|
self._cur = cur + 1
|
||||||
if self._change_listener:
|
self._trigger_change_listener()
|
||||||
self._change_listener()
|
|
||||||
return
|
return
|
||||||
|
|
||||||
elif CONF_CONDITION in action:
|
elif CONF_CONDITION in action:
|
||||||
if not self._check_condition(action, variables):
|
if not self._async_check_condition(action, variables):
|
||||||
break
|
break
|
||||||
|
|
||||||
elif CONF_EVENT in action:
|
elif CONF_EVENT in action:
|
||||||
self._fire_event(action)
|
self._async_fire_event(action)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
self._call_service(action, variables)
|
yield from self._async_call_service(action, variables)
|
||||||
|
|
||||||
self._cur = -1
|
self._cur = -1
|
||||||
self.last_action = None
|
self.last_action = None
|
||||||
if self._change_listener:
|
self._trigger_change_listener()
|
||||||
self._change_listener()
|
|
||||||
|
|
||||||
def stop(self) -> None:
|
def stop(self) -> None:
|
||||||
"""Stop running script."""
|
"""Stop running script."""
|
||||||
with self._lock:
|
run_callback_threadsafe(self.hass.loop, self.async_stop).result()
|
||||||
|
|
||||||
|
def async_stop(self) -> None:
|
||||||
|
"""Stop running script."""
|
||||||
if self._cur == -1:
|
if self._cur == -1:
|
||||||
return
|
return
|
||||||
|
|
||||||
self._cur = -1
|
self._cur = -1
|
||||||
self._remove_listener()
|
self._async_remove_listener()
|
||||||
if self._change_listener:
|
self._trigger_change_listener()
|
||||||
self._change_listener()
|
|
||||||
|
|
||||||
def _call_service(self, action, variables):
|
@asyncio.coroutine
|
||||||
|
def _async_call_service(self, action, variables):
|
||||||
"""Call the service specified in the action."""
|
"""Call the service specified in the action."""
|
||||||
self.last_action = action.get(CONF_ALIAS, 'call service')
|
self.last_action = action.get(CONF_ALIAS, 'call service')
|
||||||
self._log("Executing step %s" % self.last_action)
|
self._log("Executing step %s" % self.last_action)
|
||||||
service.call_from_config(self.hass, action, True, variables,
|
yield from service.async_call_from_config(
|
||||||
validate_config=False)
|
self.hass, action, True, variables, validate_config=False)
|
||||||
|
|
||||||
def _fire_event(self, action):
|
def _async_fire_event(self, action):
|
||||||
"""Fire an event."""
|
"""Fire an event."""
|
||||||
self.last_action = action.get(CONF_ALIAS, action[CONF_EVENT])
|
self.last_action = action.get(CONF_ALIAS, action[CONF_EVENT])
|
||||||
self._log("Executing step %s" % self.last_action)
|
self._log("Executing step %s" % self.last_action)
|
||||||
self.hass.bus.fire(action[CONF_EVENT], action.get(CONF_EVENT_DATA))
|
self.hass.bus.async_fire(action[CONF_EVENT],
|
||||||
|
action.get(CONF_EVENT_DATA))
|
||||||
|
|
||||||
def _check_condition(self, action, variables):
|
def _async_check_condition(self, action, variables):
|
||||||
"""Test if condition is matching."""
|
"""Test if condition is matching."""
|
||||||
self.last_action = action.get(CONF_ALIAS, action[CONF_CONDITION])
|
self.last_action = action.get(CONF_ALIAS, action[CONF_CONDITION])
|
||||||
check = condition.from_config(action, False)(self.hass, variables)
|
check = condition.async_from_config(action, False)(
|
||||||
|
self.hass, variables)
|
||||||
self._log("Test condition {}: {}".format(self.last_action, check))
|
self._log("Test condition {}: {}".format(self.last_action, check))
|
||||||
return check
|
return check
|
||||||
|
|
||||||
def _remove_listener(self):
|
def _async_remove_listener(self):
|
||||||
"""Remove point in time listener, if any."""
|
"""Remove point in time listener, if any."""
|
||||||
if self._unsub_delay_listener:
|
if self._async_unsub_delay_listener:
|
||||||
self._unsub_delay_listener()
|
self._async_unsub_delay_listener()
|
||||||
self._unsub_delay_listener = None
|
self._async_unsub_delay_listener = None
|
||||||
|
|
||||||
def _log(self, msg):
|
def _log(self, msg):
|
||||||
"""Logger helper."""
|
"""Logger helper."""
|
||||||
|
@ -151,3 +165,10 @@ class Script():
|
||||||
msg = "Script {}: {}".format(self.name, msg)
|
msg = "Script {}: {}".format(self.name, msg)
|
||||||
|
|
||||||
_LOGGER.info(msg)
|
_LOGGER.info(msg)
|
||||||
|
|
||||||
|
def _trigger_change_listener(self):
|
||||||
|
"""Trigger the change listener."""
|
||||||
|
if not self._change_listener:
|
||||||
|
return
|
||||||
|
|
||||||
|
self.hass.async_add_job(self._change_listener)
|
||||||
|
|
Loading…
Add table
Reference in a new issue