Make helpers.condition.* async

This commit is contained in:
Paulus Schoutsen 2016-09-30 23:11:57 -07:00
parent 33a51623f8
commit 185bd6c28a

View file

@ -20,15 +20,43 @@ import homeassistant.util.dt as dt_util
from homeassistant.util.async import run_callback_threadsafe
FROM_CONFIG_FORMAT = '{}_from_config'
ASYNC_FROM_CONFIG_FORMAT = 'async_{}_from_config'
_LOGGER = logging.getLogger(__name__)
# PyLint does not like the use of _threaded_factory
# pylint: disable=invalid-name
def from_config(config: ConfigType, config_validation: bool=True):
"""Turn a condition configuration into a method."""
factory = getattr(
sys.modules[__name__],
FROM_CONFIG_FORMAT.format(config.get(CONF_CONDITION)), None)
def _threaded_factory(async_factory):
"""Helper method to create threaded versions of async factories."""
def factory(config, config_validation=True):
"""Threaded factory."""
async_check = async_factory(config, config_validation)
def condition_if(hass, variables=None):
"""Validate condition."""
return run_callback_threadsafe(
hass.loop, async_check, hass, variables,
).result()
return condition_if
return factory
def async_from_config(config: ConfigType, config_validation: bool=True):
"""Turn a condition configuration into a method.
Should be run on the event loop.
"""
for fmt in (ASYNC_FROM_CONFIG_FORMAT, FROM_CONFIG_FORMAT):
factory = getattr(
sys.modules[__name__],
fmt.format(config.get(CONF_CONDITION)), None)
if factory:
break
if factory is None:
raise HomeAssistantError('Invalid condition "{}" specified {}'.format(
@ -37,49 +65,70 @@ def from_config(config: ConfigType, config_validation: bool=True):
return factory(config, config_validation)
def and_from_config(config: ConfigType, config_validation: bool=True):
from_config = _threaded_factory(async_from_config)
def async_and_from_config(config: ConfigType, config_validation: bool=True):
"""Create multi condition matcher using 'AND'."""
if config_validation:
config = cv.AND_CONDITION_SCHEMA(config)
checks = [from_config(entry, False) for entry in config['conditions']]
checks = None
def if_and_condition(hass: HomeAssistant,
variables=None) -> bool:
"""Test and condition."""
for check in checks:
try:
nonlocal checks
if checks is None:
checks = [async_from_config(entry, False) for entry
in config['conditions']]
try:
for check in checks:
if not check(hass, variables):
return False
except Exception as ex: # pylint: disable=broad-except
_LOGGER.warning('Error during and-condition: %s', ex)
return False
except Exception as ex: # pylint: disable=broad-except
_LOGGER.warning('Error during and-condition: %s', ex)
return False
return True
return if_and_condition
def or_from_config(config: ConfigType, config_validation: bool=True):
and_from_config = _threaded_factory(async_and_from_config)
def async_or_from_config(config: ConfigType, config_validation: bool=True):
"""Create multi condition matcher using 'OR'."""
if config_validation:
config = cv.OR_CONDITION_SCHEMA(config)
checks = [from_config(entry, False) for entry in config['conditions']]
checks = None
def if_or_condition(hass: HomeAssistant,
variables=None) -> bool:
"""Test and condition."""
for check in checks:
try:
nonlocal checks
if checks is None:
checks = [async_from_config(entry, False) for entry
in config['conditions']]
try:
for check in checks:
if check(hass, variables):
return True
except Exception as ex: # pylint: disable=broad-except
_LOGGER.warning('Error during or-condition: %s', ex)
except Exception as ex: # pylint: disable=broad-except
_LOGGER.warning('Error during or-condition: %s', ex)
return False
return if_or_condition
or_from_config = _threaded_factory(async_or_from_config)
# pylint: disable=too-many-arguments
def numeric_state(hass: HomeAssistant, entity, below=None, above=None,
value_template=None, variables=None):
@ -125,7 +174,7 @@ def async_numeric_state(hass: HomeAssistant, entity, below=None, above=None,
return True
def numeric_state_from_config(config, config_validation=True):
def async_numeric_state_from_config(config, config_validation=True):
"""Wrap action method with state based condition."""
if config_validation:
config = cv.NUMERIC_STATE_CONDITION_SCHEMA(config)
@ -139,12 +188,15 @@ def numeric_state_from_config(config, config_validation=True):
if value_template is not None:
value_template.hass = hass
return numeric_state(hass, entity_id, below, above, value_template,
variables)
return async_numeric_state(
hass, entity_id, below, above, value_template, variables)
return if_numeric_state
numeric_state_from_config = _threaded_factory(async_numeric_state_from_config)
def state(hass, entity, req_state, for_period=None):
"""Test if state matches requirements."""
if isinstance(entity, str):
@ -235,7 +287,7 @@ def async_template(hass, value_template, variables=None):
return value.lower() == 'true'
def template_from_config(config, config_validation=True):
def async_template_from_config(config, config_validation=True):
"""Wrap action method with state based condition."""
if config_validation:
config = cv.TEMPLATE_CONDITION_SCHEMA(config)
@ -245,11 +297,14 @@ def template_from_config(config, config_validation=True):
"""Validate template based if-condition."""
value_template.hass = hass
return template(hass, value_template, variables)
return async_template(hass, value_template, variables)
return template_if
template_from_config = _threaded_factory(async_template_from_config)
def time(before=None, after=None, weekday=None):
"""Test if local time condition matches.