mirror of
https://github.com/home-assistant/core.git
synced 2025-07-23 13:17:32 +00:00
Raise ConditionError for numeric_state errors (#45923)
This commit is contained in:
parent
9e07910ab0
commit
b9b1caf4d7
@ -32,7 +32,7 @@ from homeassistant.core import (
|
|||||||
callback,
|
callback,
|
||||||
split_entity_id,
|
split_entity_id,
|
||||||
)
|
)
|
||||||
from homeassistant.exceptions import HomeAssistantError
|
from homeassistant.exceptions import ConditionError, HomeAssistantError
|
||||||
from homeassistant.helpers import condition, extract_domain_configs, template
|
from homeassistant.helpers import condition, extract_domain_configs, template
|
||||||
import homeassistant.helpers.config_validation as cv
|
import homeassistant.helpers.config_validation as cv
|
||||||
from homeassistant.helpers.entity import ToggleEntity
|
from homeassistant.helpers.entity import ToggleEntity
|
||||||
@ -588,7 +588,11 @@ async def _async_process_if(hass, config, p_config):
|
|||||||
|
|
||||||
def if_action(variables=None):
|
def if_action(variables=None):
|
||||||
"""AND all conditions."""
|
"""AND all conditions."""
|
||||||
|
try:
|
||||||
return all(check(hass, variables) for check in checks)
|
return all(check(hass, variables) for check in checks)
|
||||||
|
except ConditionError as ex:
|
||||||
|
LOGGER.warning("Error in 'condition' evaluation: %s", ex)
|
||||||
|
return False
|
||||||
|
|
||||||
if_action.config = if_configs
|
if_action.config = if_configs
|
||||||
|
|
||||||
|
@ -17,7 +17,7 @@ from homeassistant.const import (
|
|||||||
STATE_UNKNOWN,
|
STATE_UNKNOWN,
|
||||||
)
|
)
|
||||||
from homeassistant.core import callback
|
from homeassistant.core import callback
|
||||||
from homeassistant.exceptions import TemplateError
|
from homeassistant.exceptions import ConditionError, TemplateError
|
||||||
from homeassistant.helpers import condition
|
from homeassistant.helpers import condition
|
||||||
import homeassistant.helpers.config_validation as cv
|
import homeassistant.helpers.config_validation as cv
|
||||||
from homeassistant.helpers.event import (
|
from homeassistant.helpers.event import (
|
||||||
@ -340,6 +340,7 @@ class BayesianBinarySensor(BinarySensorEntity):
|
|||||||
"""Return True if numeric condition is met."""
|
"""Return True if numeric condition is met."""
|
||||||
entity = entity_observation["entity_id"]
|
entity = entity_observation["entity_id"]
|
||||||
|
|
||||||
|
try:
|
||||||
return condition.async_numeric_state(
|
return condition.async_numeric_state(
|
||||||
self.hass,
|
self.hass,
|
||||||
entity,
|
entity,
|
||||||
@ -348,6 +349,8 @@ class BayesianBinarySensor(BinarySensorEntity):
|
|||||||
None,
|
None,
|
||||||
entity_observation,
|
entity_observation,
|
||||||
)
|
)
|
||||||
|
except ConditionError:
|
||||||
|
return False
|
||||||
|
|
||||||
def _process_state(self, entity_observation):
|
def _process_state(self, entity_observation):
|
||||||
"""Return True if state conditions are met."""
|
"""Return True if state conditions are met."""
|
||||||
|
@ -96,12 +96,19 @@ async def async_attach_trigger(
|
|||||||
@callback
|
@callback
|
||||||
def check_numeric_state(entity_id, from_s, to_s):
|
def check_numeric_state(entity_id, from_s, to_s):
|
||||||
"""Return True if criteria are now met."""
|
"""Return True if criteria are now met."""
|
||||||
if to_s is None:
|
try:
|
||||||
return False
|
|
||||||
|
|
||||||
return condition.async_numeric_state(
|
return condition.async_numeric_state(
|
||||||
hass, to_s, below, above, value_template, variables(entity_id), attribute
|
hass,
|
||||||
|
to_s,
|
||||||
|
below,
|
||||||
|
above,
|
||||||
|
value_template,
|
||||||
|
variables(entity_id),
|
||||||
|
attribute,
|
||||||
)
|
)
|
||||||
|
except exceptions.ConditionError as err:
|
||||||
|
_LOGGER.warning("%s", err)
|
||||||
|
return False
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
def state_automation_listener(event):
|
def state_automation_listener(event):
|
||||||
|
@ -25,6 +25,10 @@ class TemplateError(HomeAssistantError):
|
|||||||
super().__init__(f"{exception.__class__.__name__}: {exception}")
|
super().__init__(f"{exception.__class__.__name__}: {exception}")
|
||||||
|
|
||||||
|
|
||||||
|
class ConditionError(HomeAssistantError):
|
||||||
|
"""Error during condition evaluation."""
|
||||||
|
|
||||||
|
|
||||||
class PlatformNotReady(HomeAssistantError):
|
class PlatformNotReady(HomeAssistantError):
|
||||||
"""Error to indicate that platform is not ready."""
|
"""Error to indicate that platform is not ready."""
|
||||||
|
|
||||||
|
@ -36,7 +36,7 @@ from homeassistant.const import (
|
|||||||
WEEKDAYS,
|
WEEKDAYS,
|
||||||
)
|
)
|
||||||
from homeassistant.core import HomeAssistant, State, callback
|
from homeassistant.core import HomeAssistant, State, callback
|
||||||
from homeassistant.exceptions import HomeAssistantError, TemplateError
|
from homeassistant.exceptions import ConditionError, HomeAssistantError, TemplateError
|
||||||
import homeassistant.helpers.config_validation as cv
|
import homeassistant.helpers.config_validation as cv
|
||||||
from homeassistant.helpers.sun import get_astral_event_date
|
from homeassistant.helpers.sun import get_astral_event_date
|
||||||
from homeassistant.helpers.template import Template
|
from homeassistant.helpers.template import Template
|
||||||
@ -204,11 +204,22 @@ def async_numeric_state(
|
|||||||
attribute: Optional[str] = None,
|
attribute: Optional[str] = None,
|
||||||
) -> bool:
|
) -> bool:
|
||||||
"""Test a numeric state condition."""
|
"""Test a numeric state condition."""
|
||||||
|
if entity is None:
|
||||||
|
raise ConditionError("No entity specified")
|
||||||
|
|
||||||
if isinstance(entity, str):
|
if isinstance(entity, str):
|
||||||
|
entity_id = entity
|
||||||
entity = hass.states.get(entity)
|
entity = hass.states.get(entity)
|
||||||
|
|
||||||
if entity is None or (attribute is not None and attribute not in entity.attributes):
|
if entity is None:
|
||||||
return False
|
raise ConditionError(f"Unknown entity {entity_id}")
|
||||||
|
else:
|
||||||
|
entity_id = entity.entity_id
|
||||||
|
|
||||||
|
if attribute is not None and attribute not in entity.attributes:
|
||||||
|
raise ConditionError(
|
||||||
|
f"Attribute '{attribute}' (of entity {entity_id}) does not exist"
|
||||||
|
)
|
||||||
|
|
||||||
value: Any = None
|
value: Any = None
|
||||||
if value_template is None:
|
if value_template is None:
|
||||||
@ -222,30 +233,27 @@ def async_numeric_state(
|
|||||||
try:
|
try:
|
||||||
value = value_template.async_render(variables)
|
value = value_template.async_render(variables)
|
||||||
except TemplateError as ex:
|
except TemplateError as ex:
|
||||||
_LOGGER.error("Template error: %s", ex)
|
raise ConditionError(f"Template error: {ex}") from ex
|
||||||
return False
|
|
||||||
|
|
||||||
if value in (STATE_UNAVAILABLE, STATE_UNKNOWN):
|
if value in (STATE_UNAVAILABLE, STATE_UNKNOWN):
|
||||||
return False
|
raise ConditionError("State is not available")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
fvalue = float(value)
|
fvalue = float(value)
|
||||||
except ValueError:
|
except ValueError as ex:
|
||||||
_LOGGER.warning(
|
raise ConditionError(
|
||||||
"Value cannot be processed as a number: %s (Offending entity: %s)",
|
f"Entity {entity_id} state '{value}' cannot be processed as a number"
|
||||||
entity,
|
) from ex
|
||||||
value,
|
|
||||||
)
|
|
||||||
return False
|
|
||||||
|
|
||||||
if below is not None:
|
if below is not None:
|
||||||
if isinstance(below, str):
|
if isinstance(below, str):
|
||||||
below_entity = hass.states.get(below)
|
below_entity = hass.states.get(below)
|
||||||
if (
|
if not below_entity or below_entity.state in (
|
||||||
not below_entity
|
STATE_UNAVAILABLE,
|
||||||
or below_entity.state in (STATE_UNAVAILABLE, STATE_UNKNOWN)
|
STATE_UNKNOWN,
|
||||||
or fvalue >= float(below_entity.state)
|
|
||||||
):
|
):
|
||||||
|
raise ConditionError(f"The below entity {below} is not available")
|
||||||
|
if fvalue >= float(below_entity.state):
|
||||||
return False
|
return False
|
||||||
elif fvalue >= below:
|
elif fvalue >= below:
|
||||||
return False
|
return False
|
||||||
@ -253,11 +261,12 @@ def async_numeric_state(
|
|||||||
if above is not None:
|
if above is not None:
|
||||||
if isinstance(above, str):
|
if isinstance(above, str):
|
||||||
above_entity = hass.states.get(above)
|
above_entity = hass.states.get(above)
|
||||||
if (
|
if not above_entity or above_entity.state in (
|
||||||
not above_entity
|
STATE_UNAVAILABLE,
|
||||||
or above_entity.state in (STATE_UNAVAILABLE, STATE_UNKNOWN)
|
STATE_UNKNOWN,
|
||||||
or fvalue <= float(above_entity.state)
|
|
||||||
):
|
):
|
||||||
|
raise ConditionError(f"The above entity {above} is not available")
|
||||||
|
if fvalue <= float(above_entity.state):
|
||||||
return False
|
return False
|
||||||
elif fvalue <= above:
|
elif fvalue <= above:
|
||||||
return False
|
return False
|
||||||
|
@ -519,7 +519,12 @@ class _ScriptRun:
|
|||||||
CONF_ALIAS, self._action[CONF_CONDITION]
|
CONF_ALIAS, self._action[CONF_CONDITION]
|
||||||
)
|
)
|
||||||
cond = await self._async_get_condition(self._action)
|
cond = await self._async_get_condition(self._action)
|
||||||
|
try:
|
||||||
check = cond(self._hass, self._variables)
|
check = cond(self._hass, self._variables)
|
||||||
|
except exceptions.ConditionError as ex:
|
||||||
|
_LOGGER.warning("Error in 'condition' evaluation: %s", ex)
|
||||||
|
check = False
|
||||||
|
|
||||||
self._log("Test condition %s: %s", self._script.last_action, check)
|
self._log("Test condition %s: %s", self._script.last_action, check)
|
||||||
if not check:
|
if not check:
|
||||||
raise _StopScript
|
raise _StopScript
|
||||||
@ -570,10 +575,15 @@ class _ScriptRun:
|
|||||||
]
|
]
|
||||||
for iteration in itertools.count(1):
|
for iteration in itertools.count(1):
|
||||||
set_repeat_var(iteration)
|
set_repeat_var(iteration)
|
||||||
|
try:
|
||||||
if self._stop.is_set() or not all(
|
if self._stop.is_set() or not all(
|
||||||
cond(self._hass, self._variables) for cond in conditions
|
cond(self._hass, self._variables) for cond in conditions
|
||||||
):
|
):
|
||||||
break
|
break
|
||||||
|
except exceptions.ConditionError as ex:
|
||||||
|
_LOGGER.warning("Error in 'while' evaluation: %s", ex)
|
||||||
|
break
|
||||||
|
|
||||||
await async_run_sequence(iteration)
|
await async_run_sequence(iteration)
|
||||||
|
|
||||||
elif CONF_UNTIL in repeat:
|
elif CONF_UNTIL in repeat:
|
||||||
@ -583,10 +593,14 @@ class _ScriptRun:
|
|||||||
for iteration in itertools.count(1):
|
for iteration in itertools.count(1):
|
||||||
set_repeat_var(iteration)
|
set_repeat_var(iteration)
|
||||||
await async_run_sequence(iteration)
|
await async_run_sequence(iteration)
|
||||||
|
try:
|
||||||
if self._stop.is_set() or all(
|
if self._stop.is_set() or all(
|
||||||
cond(self._hass, self._variables) for cond in conditions
|
cond(self._hass, self._variables) for cond in conditions
|
||||||
):
|
):
|
||||||
break
|
break
|
||||||
|
except exceptions.ConditionError as ex:
|
||||||
|
_LOGGER.warning("Error in 'until' evaluation: %s", ex)
|
||||||
|
break
|
||||||
|
|
||||||
if saved_repeat_vars:
|
if saved_repeat_vars:
|
||||||
self._variables["repeat"] = saved_repeat_vars
|
self._variables["repeat"] = saved_repeat_vars
|
||||||
@ -599,9 +613,14 @@ class _ScriptRun:
|
|||||||
choose_data = await self._script._async_get_choose_data(self._step)
|
choose_data = await self._script._async_get_choose_data(self._step)
|
||||||
|
|
||||||
for conditions, script in choose_data["choices"]:
|
for conditions, script in choose_data["choices"]:
|
||||||
if all(condition(self._hass, self._variables) for condition in conditions):
|
try:
|
||||||
|
if all(
|
||||||
|
condition(self._hass, self._variables) for condition in conditions
|
||||||
|
):
|
||||||
await self._async_run_script(script)
|
await self._async_run_script(script)
|
||||||
return
|
return
|
||||||
|
except exceptions.ConditionError as ex:
|
||||||
|
_LOGGER.warning("Error in 'choose' evaluation: %s", ex)
|
||||||
|
|
||||||
if choose_data["default"]:
|
if choose_data["default"]:
|
||||||
await self._async_run_script(choose_data["default"])
|
await self._async_run_script(choose_data["default"])
|
||||||
|
@ -162,18 +162,20 @@ async def test_trigger_service_ignoring_condition(hass, calls):
|
|||||||
"alias": "test",
|
"alias": "test",
|
||||||
"trigger": [{"platform": "event", "event_type": "test_event"}],
|
"trigger": [{"platform": "event", "event_type": "test_event"}],
|
||||||
"condition": {
|
"condition": {
|
||||||
"condition": "state",
|
"condition": "numeric_state",
|
||||||
"entity_id": "non.existing",
|
"entity_id": "non.existing",
|
||||||
"state": "beer",
|
"above": "1",
|
||||||
},
|
},
|
||||||
"action": {"service": "test.automation"},
|
"action": {"service": "test.automation"},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
with patch("homeassistant.components.automation.LOGGER.warning") as logwarn:
|
||||||
hass.bus.async_fire("test_event")
|
hass.bus.async_fire("test_event")
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
assert len(calls) == 0
|
assert len(calls) == 0
|
||||||
|
assert len(logwarn.mock_calls) == 1
|
||||||
|
|
||||||
await hass.services.async_call(
|
await hass.services.async_call(
|
||||||
"automation", "trigger", {"entity_id": "automation.test"}, blocking=True
|
"automation", "trigger", {"entity_id": "automation.test"}, blocking=True
|
||||||
|
@ -572,6 +572,32 @@ async def test_if_not_fires_if_entity_not_match(hass, calls, below):
|
|||||||
assert len(calls) == 0
|
assert len(calls) == 0
|
||||||
|
|
||||||
|
|
||||||
|
async def test_if_not_fires_and_warns_if_below_entity_unknown(hass, calls):
|
||||||
|
"""Test if warns with unknown below entity."""
|
||||||
|
assert await async_setup_component(
|
||||||
|
hass,
|
||||||
|
automation.DOMAIN,
|
||||||
|
{
|
||||||
|
automation.DOMAIN: {
|
||||||
|
"trigger": {
|
||||||
|
"platform": "numeric_state",
|
||||||
|
"entity_id": "test.entity",
|
||||||
|
"below": "input_number.unknown",
|
||||||
|
},
|
||||||
|
"action": {"service": "test.automation"},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
with patch(
|
||||||
|
"homeassistant.components.homeassistant.triggers.numeric_state._LOGGER.warning"
|
||||||
|
) as logwarn:
|
||||||
|
hass.states.async_set("test.entity", 1)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
assert len(calls) == 0
|
||||||
|
assert len(logwarn.mock_calls) == 1
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("below", (10, "input_number.value_10"))
|
@pytest.mark.parametrize("below", (10, "input_number.value_10"))
|
||||||
async def test_if_fires_on_entity_change_below_with_attribute(hass, calls, below):
|
async def test_if_fires_on_entity_change_below_with_attribute(hass, calls, below):
|
||||||
"""Test attributes change."""
|
"""Test attributes change."""
|
||||||
|
@ -4,7 +4,7 @@ from unittest.mock import patch
|
|||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from homeassistant.exceptions import HomeAssistantError
|
from homeassistant.exceptions import ConditionError, HomeAssistantError
|
||||||
from homeassistant.helpers import condition
|
from homeassistant.helpers import condition
|
||||||
from homeassistant.helpers.template import Template
|
from homeassistant.helpers.template import Template
|
||||||
from homeassistant.setup import async_setup_component
|
from homeassistant.setup import async_setup_component
|
||||||
@ -338,8 +338,8 @@ async def test_time_using_input_datetime(hass):
|
|||||||
assert not condition.time(hass, before="input_datetime.not_existing")
|
assert not condition.time(hass, before="input_datetime.not_existing")
|
||||||
|
|
||||||
|
|
||||||
async def test_if_numeric_state_not_raise_on_unavailable(hass):
|
async def test_if_numeric_state_raises_on_unavailable(hass):
|
||||||
"""Test numeric_state doesn't raise on unavailable/unknown state."""
|
"""Test numeric_state raises on unavailable/unknown state."""
|
||||||
test = await condition.async_from_config(
|
test = await condition.async_from_config(
|
||||||
hass,
|
hass,
|
||||||
{"condition": "numeric_state", "entity_id": "sensor.temperature", "below": 42},
|
{"condition": "numeric_state", "entity_id": "sensor.temperature", "below": 42},
|
||||||
@ -347,11 +347,13 @@ async def test_if_numeric_state_not_raise_on_unavailable(hass):
|
|||||||
|
|
||||||
with patch("homeassistant.helpers.condition._LOGGER.warning") as logwarn:
|
with patch("homeassistant.helpers.condition._LOGGER.warning") as logwarn:
|
||||||
hass.states.async_set("sensor.temperature", "unavailable")
|
hass.states.async_set("sensor.temperature", "unavailable")
|
||||||
assert not test(hass)
|
with pytest.raises(ConditionError):
|
||||||
|
test(hass)
|
||||||
assert len(logwarn.mock_calls) == 0
|
assert len(logwarn.mock_calls) == 0
|
||||||
|
|
||||||
hass.states.async_set("sensor.temperature", "unknown")
|
hass.states.async_set("sensor.temperature", "unknown")
|
||||||
assert not test(hass)
|
with pytest.raises(ConditionError):
|
||||||
|
test(hass)
|
||||||
assert len(logwarn.mock_calls) == 0
|
assert len(logwarn.mock_calls) == 0
|
||||||
|
|
||||||
|
|
||||||
@ -550,6 +552,108 @@ async def test_state_using_input_entities(hass):
|
|||||||
assert test(hass)
|
assert test(hass)
|
||||||
|
|
||||||
|
|
||||||
|
async def test_numeric_state_raises(hass):
|
||||||
|
"""Test that numeric_state raises ConditionError on errors."""
|
||||||
|
# Unknown entity_id
|
||||||
|
with pytest.raises(ConditionError, match="Unknown entity"):
|
||||||
|
test = await condition.async_from_config(
|
||||||
|
hass,
|
||||||
|
{
|
||||||
|
"condition": "numeric_state",
|
||||||
|
"entity_id": "sensor.temperature_unknown",
|
||||||
|
"above": 0,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
assert test(hass)
|
||||||
|
|
||||||
|
# Unknown attribute
|
||||||
|
with pytest.raises(ConditionError, match=r"Attribute .* does not exist"):
|
||||||
|
test = await condition.async_from_config(
|
||||||
|
hass,
|
||||||
|
{
|
||||||
|
"condition": "numeric_state",
|
||||||
|
"entity_id": "sensor.temperature",
|
||||||
|
"attribute": "temperature",
|
||||||
|
"above": 0,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
hass.states.async_set("sensor.temperature", 50)
|
||||||
|
test(hass)
|
||||||
|
|
||||||
|
# Template error
|
||||||
|
with pytest.raises(ConditionError, match="ZeroDivisionError"):
|
||||||
|
test = await condition.async_from_config(
|
||||||
|
hass,
|
||||||
|
{
|
||||||
|
"condition": "numeric_state",
|
||||||
|
"entity_id": "sensor.temperature",
|
||||||
|
"value_template": "{{ 1 / 0 }}",
|
||||||
|
"above": 0,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
hass.states.async_set("sensor.temperature", 50)
|
||||||
|
test(hass)
|
||||||
|
|
||||||
|
# Unavailable state
|
||||||
|
with pytest.raises(ConditionError, match="State is not available"):
|
||||||
|
test = await condition.async_from_config(
|
||||||
|
hass,
|
||||||
|
{
|
||||||
|
"condition": "numeric_state",
|
||||||
|
"entity_id": "sensor.temperature",
|
||||||
|
"above": 0,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
hass.states.async_set("sensor.temperature", "unavailable")
|
||||||
|
test(hass)
|
||||||
|
|
||||||
|
# Bad number
|
||||||
|
with pytest.raises(ConditionError, match="cannot be processed as a number"):
|
||||||
|
test = await condition.async_from_config(
|
||||||
|
hass,
|
||||||
|
{
|
||||||
|
"condition": "numeric_state",
|
||||||
|
"entity_id": "sensor.temperature",
|
||||||
|
"above": 0,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
hass.states.async_set("sensor.temperature", "fifty")
|
||||||
|
test(hass)
|
||||||
|
|
||||||
|
# Below entity missing
|
||||||
|
with pytest.raises(ConditionError, match="below entity"):
|
||||||
|
test = await condition.async_from_config(
|
||||||
|
hass,
|
||||||
|
{
|
||||||
|
"condition": "numeric_state",
|
||||||
|
"entity_id": "sensor.temperature",
|
||||||
|
"below": "input_number.missing",
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
hass.states.async_set("sensor.temperature", 50)
|
||||||
|
test(hass)
|
||||||
|
|
||||||
|
# Above entity missing
|
||||||
|
with pytest.raises(ConditionError, match="above entity"):
|
||||||
|
test = await condition.async_from_config(
|
||||||
|
hass,
|
||||||
|
{
|
||||||
|
"condition": "numeric_state",
|
||||||
|
"entity_id": "sensor.temperature",
|
||||||
|
"above": "input_number.missing",
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
hass.states.async_set("sensor.temperature", 50)
|
||||||
|
test(hass)
|
||||||
|
|
||||||
|
|
||||||
async def test_numeric_state_multiple_entities(hass):
|
async def test_numeric_state_multiple_entities(hass):
|
||||||
"""Test with multiple entities in condition."""
|
"""Test with multiple entities in condition."""
|
||||||
test = await condition.async_from_config(
|
test = await condition.async_from_config(
|
||||||
@ -660,10 +764,12 @@ async def test_numeric_state_using_input_number(hass):
|
|||||||
)
|
)
|
||||||
assert test(hass)
|
assert test(hass)
|
||||||
|
|
||||||
assert not condition.async_numeric_state(
|
with pytest.raises(ConditionError):
|
||||||
|
condition.async_numeric_state(
|
||||||
hass, entity="sensor.temperature", below="input_number.not_exist"
|
hass, entity="sensor.temperature", below="input_number.not_exist"
|
||||||
)
|
)
|
||||||
assert not condition.async_numeric_state(
|
with pytest.raises(ConditionError):
|
||||||
|
condition.async_numeric_state(
|
||||||
hass, entity="sensor.temperature", above="input_number.not_exist"
|
hass, entity="sensor.temperature", above="input_number.not_exist"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -990,6 +990,32 @@ async def test_wait_for_trigger_generated_exception(hass, caplog):
|
|||||||
assert "something bad" in caplog.text
|
assert "something bad" in caplog.text
|
||||||
|
|
||||||
|
|
||||||
|
async def test_condition_warning(hass):
|
||||||
|
"""Test warning on condition."""
|
||||||
|
event = "test_event"
|
||||||
|
events = async_capture_events(hass, event)
|
||||||
|
sequence = cv.SCRIPT_SCHEMA(
|
||||||
|
[
|
||||||
|
{"event": event},
|
||||||
|
{
|
||||||
|
"condition": "numeric_state",
|
||||||
|
"entity_id": "test.entity",
|
||||||
|
"above": 0,
|
||||||
|
},
|
||||||
|
{"event": event},
|
||||||
|
]
|
||||||
|
)
|
||||||
|
script_obj = script.Script(hass, sequence, "Test Name", "test_domain")
|
||||||
|
|
||||||
|
hass.states.async_set("test.entity", "string")
|
||||||
|
with patch("homeassistant.helpers.script._LOGGER.warning") as logwarn:
|
||||||
|
await script_obj.async_run(context=Context())
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
assert len(logwarn.mock_calls) == 1
|
||||||
|
|
||||||
|
assert len(events) == 1
|
||||||
|
|
||||||
|
|
||||||
async def test_condition_basic(hass):
|
async def test_condition_basic(hass):
|
||||||
"""Test if we can use conditions in a script."""
|
"""Test if we can use conditions in a script."""
|
||||||
event = "test_event"
|
event = "test_event"
|
||||||
@ -1100,6 +1126,44 @@ async def test_repeat_count(hass):
|
|||||||
assert event.data.get("last") == (index == count - 1)
|
assert event.data.get("last") == (index == count - 1)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize("condition", ["while", "until"])
|
||||||
|
async def test_repeat_condition_warning(hass, condition):
|
||||||
|
"""Test warning on repeat conditions."""
|
||||||
|
event = "test_event"
|
||||||
|
events = async_capture_events(hass, event)
|
||||||
|
count = 0 if condition == "while" else 1
|
||||||
|
|
||||||
|
sequence = {
|
||||||
|
"repeat": {
|
||||||
|
"sequence": [
|
||||||
|
{
|
||||||
|
"event": event,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sequence["repeat"][condition] = {
|
||||||
|
"condition": "numeric_state",
|
||||||
|
"entity_id": "sensor.test",
|
||||||
|
"value_template": "{{ unassigned_variable }}",
|
||||||
|
"above": "0",
|
||||||
|
}
|
||||||
|
|
||||||
|
script_obj = script.Script(
|
||||||
|
hass, cv.SCRIPT_SCHEMA(sequence), f"Test {condition}", "test_domain"
|
||||||
|
)
|
||||||
|
|
||||||
|
# wait_started = async_watch_for_action(script_obj, "wait")
|
||||||
|
hass.states.async_set("sensor.test", "1")
|
||||||
|
|
||||||
|
with patch("homeassistant.helpers.script._LOGGER.warning") as logwarn:
|
||||||
|
hass.async_create_task(script_obj.async_run(context=Context()))
|
||||||
|
await asyncio.wait_for(hass.async_block_till_done(), 1)
|
||||||
|
assert len(logwarn.mock_calls) == 1
|
||||||
|
|
||||||
|
assert len(events) == count
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("condition", ["while", "until"])
|
@pytest.mark.parametrize("condition", ["while", "until"])
|
||||||
@pytest.mark.parametrize("direct_template", [False, True])
|
@pytest.mark.parametrize("direct_template", [False, True])
|
||||||
async def test_repeat_conditional(hass, condition, direct_template):
|
async def test_repeat_conditional(hass, condition, direct_template):
|
||||||
@ -1305,6 +1369,51 @@ async def test_repeat_nested(hass, variables, first_last, inside_x):
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
async def test_choose_warning(hass):
|
||||||
|
"""Test warning on choose."""
|
||||||
|
event = "test_event"
|
||||||
|
events = async_capture_events(hass, event)
|
||||||
|
|
||||||
|
sequence = cv.SCRIPT_SCHEMA(
|
||||||
|
{
|
||||||
|
"choose": [
|
||||||
|
{
|
||||||
|
"conditions": {
|
||||||
|
"condition": "numeric_state",
|
||||||
|
"entity_id": "test.entity",
|
||||||
|
"value_template": "{{ undefined_a + undefined_b }}",
|
||||||
|
"above": 1,
|
||||||
|
},
|
||||||
|
"sequence": {"event": event, "event_data": {"choice": "first"}},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"conditions": {
|
||||||
|
"condition": "numeric_state",
|
||||||
|
"entity_id": "test.entity",
|
||||||
|
"value_template": "{{ 'string' }}",
|
||||||
|
"above": 2,
|
||||||
|
},
|
||||||
|
"sequence": {"event": event, "event_data": {"choice": "second"}},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"default": {"event": event, "event_data": {"choice": "default"}},
|
||||||
|
}
|
||||||
|
)
|
||||||
|
script_obj = script.Script(hass, sequence, "Test Name", "test_domain")
|
||||||
|
|
||||||
|
hass.states.async_set("test.entity", "9")
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
with patch("homeassistant.helpers.script._LOGGER.warning") as logwarn:
|
||||||
|
await script_obj.async_run(context=Context())
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
print(logwarn.mock_calls)
|
||||||
|
assert len(logwarn.mock_calls) == 2
|
||||||
|
|
||||||
|
assert len(events) == 1
|
||||||
|
assert events[0].data["choice"] == "default"
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("var,result", [(1, "first"), (2, "second"), (3, "default")])
|
@pytest.mark.parametrize("var,result", [(1, "first"), (2, "second"), (3, "default")])
|
||||||
async def test_choose(hass, var, result):
|
async def test_choose(hass, var, result):
|
||||||
"""Test choose action."""
|
"""Test choose action."""
|
||||||
|
Loading…
x
Reference in New Issue
Block a user