From bfea7d0baa0c94e9f048972cf4645d11ff8c7380 Mon Sep 17 00:00:00 2001 From: Anders Melchiorsen Date: Fri, 19 Feb 2021 13:15:30 +0100 Subject: [PATCH] Raise ConditionError for and/or/not errors (#46767) --- .../components/automation/__init__.py | 17 +++++-- homeassistant/helpers/condition.py | 48 +++++++++++++------ tests/helpers/test_condition.py | 20 ++++++-- 3 files changed, 63 insertions(+), 22 deletions(-) diff --git a/homeassistant/components/automation/__init__.py b/homeassistant/components/automation/__init__.py index ae8c71b4fb8..3a48b3e3cc2 100644 --- a/homeassistant/components/automation/__init__.py +++ b/homeassistant/components/automation/__init__.py @@ -615,12 +615,21 @@ async def _async_process_if(hass, config, p_config): def if_action(variables=None): """AND all conditions.""" - try: - return all(check(hass, variables) for check in checks) - except ConditionError as ex: - LOGGER.warning("Error in 'condition' evaluation: %s", ex) + errors = [] + for check in checks: + try: + if not check(hass, variables): + return False + except ConditionError as ex: + errors.append(f"Error in 'condition' evaluation: {ex}") + + if errors: + for error in errors: + LOGGER.warning("%s", error) return False + return True + if_action.config = if_configs return if_action diff --git a/homeassistant/helpers/condition.py b/homeassistant/helpers/condition.py index e09176dc098..b66ee6c7976 100644 --- a/homeassistant/helpers/condition.py +++ b/homeassistant/helpers/condition.py @@ -108,13 +108,19 @@ async def async_and_from_config( hass: HomeAssistant, variables: TemplateVarsType = None ) -> bool: """Test and condition.""" - try: - for check in checks: + errors = [] + for check in checks: + try: 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 ConditionError as ex: + errors.append(str(ex)) + except Exception as ex: # pylint: disable=broad-except + errors.append(str(ex)) + + # Raise the errors if no check was false + if errors: + raise ConditionError("Error in 'and' condition: " + ", ".join(errors)) return True @@ -134,13 +140,20 @@ async def async_or_from_config( def if_or_condition( hass: HomeAssistant, variables: TemplateVarsType = None ) -> bool: - """Test and condition.""" - try: - for check in checks: + """Test or condition.""" + errors = [] + for check in checks: + try: if check(hass, variables): return True - except Exception as ex: # pylint: disable=broad-except - _LOGGER.warning("Error during or-condition: %s", ex) + except ConditionError as ex: + errors.append(str(ex)) + except Exception as ex: # pylint: disable=broad-except + errors.append(str(ex)) + + # Raise the errors if no check was true + if errors: + raise ConditionError("Error in 'or' condition: " + ", ".join(errors)) return False @@ -161,12 +174,19 @@ async def async_not_from_config( hass: HomeAssistant, variables: TemplateVarsType = None ) -> bool: """Test not condition.""" - try: - for check in checks: + errors = [] + for check in checks: + try: if check(hass, variables): return False - except Exception as ex: # pylint: disable=broad-except - _LOGGER.warning("Error during not-condition: %s", ex) + except ConditionError as ex: + errors.append(str(ex)) + except Exception as ex: # pylint: disable=broad-except + errors.append(str(ex)) + + # Raise the errors if no check was true + if errors: + raise ConditionError("Error in 'not' condition: " + ", ".join(errors)) return True diff --git a/tests/helpers/test_condition.py b/tests/helpers/test_condition.py index 0db8220bc50..3e7833b24dd 100644 --- a/tests/helpers/test_condition.py +++ b/tests/helpers/test_condition.py @@ -50,6 +50,9 @@ async def test_and_condition(hass): }, ) + with pytest.raises(ConditionError): + test(hass) + hass.states.async_set("sensor.temperature", 120) assert not test(hass) @@ -111,6 +114,9 @@ async def test_or_condition(hass): }, ) + with pytest.raises(ConditionError): + test(hass) + hass.states.async_set("sensor.temperature", 120) assert not test(hass) @@ -169,6 +175,9 @@ async def test_not_condition(hass): }, ) + with pytest.raises(ConditionError): + test(hass) + hass.states.async_set("sensor.temperature", 101) assert test(hass) @@ -466,7 +475,8 @@ async def test_state_attribute(hass): ) hass.states.async_set("sensor.temperature", 100, {"unkown_attr": 200}) - assert not test(hass) + with pytest.raises(ConditionError): + test(hass) hass.states.async_set("sensor.temperature", 100, {"attribute1": 200}) assert test(hass) @@ -720,7 +730,7 @@ async def test_numeric_state_multiple_entities(hass): assert not test(hass) -async def test_numberic_state_attribute(hass): +async def test_numeric_state_attribute(hass): """Test with numeric state attribute in condition.""" test = await condition.async_from_config( hass, @@ -738,7 +748,8 @@ async def test_numberic_state_attribute(hass): ) hass.states.async_set("sensor.temperature", 100, {"unkown_attr": 10}) - assert not test(hass) + with pytest.raises(ConditionError): + assert test(hass) hass.states.async_set("sensor.temperature", 100, {"attribute1": 49}) assert test(hass) @@ -750,7 +761,8 @@ async def test_numberic_state_attribute(hass): assert not test(hass) hass.states.async_set("sensor.temperature", 100, {"attribute1": None}) - assert not test(hass) + with pytest.raises(ConditionError): + assert test(hass) async def test_numeric_state_using_input_number(hass):