diff --git a/homeassistant/components/switch/condition.py b/homeassistant/components/switch/condition.py index 283524d2178..65d61ce723c 100644 --- a/homeassistant/components/switch/condition.py +++ b/homeassistant/components/switch/condition.py @@ -1,14 +1,18 @@ """Provides conditions for switches.""" +from homeassistant.components.input_boolean import DOMAIN as INPUT_BOOLEAN_DOMAIN from homeassistant.const import STATE_OFF, STATE_ON from homeassistant.core import HomeAssistant +from homeassistant.helpers.automation import DomainSpec from homeassistant.helpers.condition import Condition, make_entity_state_condition from .const import DOMAIN +SWITCH_DOMAIN_SPECS = {DOMAIN: DomainSpec(), INPUT_BOOLEAN_DOMAIN: DomainSpec()} + CONDITIONS: dict[str, type[Condition]] = { - "is_off": make_entity_state_condition(DOMAIN, STATE_OFF), - "is_on": make_entity_state_condition(DOMAIN, STATE_ON), + "is_off": make_entity_state_condition(SWITCH_DOMAIN_SPECS, STATE_OFF), + "is_on": make_entity_state_condition(SWITCH_DOMAIN_SPECS, STATE_ON), } diff --git a/homeassistant/components/switch/conditions.yaml b/homeassistant/components/switch/conditions.yaml index f293d020b45..ea9adeb6f74 100644 --- a/homeassistant/components/switch/conditions.yaml +++ b/homeassistant/components/switch/conditions.yaml @@ -1,7 +1,8 @@ .condition_common: &condition_common target: entity: - domain: switch + - domain: switch + - domain: input_boolean fields: behavior: required: true diff --git a/tests/components/switch/test_condition.py b/tests/components/switch/test_condition.py index eaa906fca04..76a0dffffb8 100644 --- a/tests/components/switch/test_condition.py +++ b/tests/components/switch/test_condition.py @@ -4,11 +4,13 @@ from typing import Any import pytest -from homeassistant.const import STATE_OFF, STATE_ON +from homeassistant.const import CONF_ENTITY_ID, STATE_OFF, STATE_ON from homeassistant.core import HomeAssistant from tests.components.common import ( ConditionStateDescription, + assert_condition_behavior_all, + assert_condition_behavior_any, assert_condition_gated_by_labs_flag, create_target_condition, parametrize_condition_states_all, @@ -35,6 +37,12 @@ async def target_switches(hass: HomeAssistant) -> dict[str, list[str]]: return await target_entities(hass, "switch") +@pytest.fixture +async def target_input_booleans(hass: HomeAssistant) -> dict[str, list[str]]: + """Create multiple input_boolean entities associated with different targets.""" + return await target_entities(hass, "input_boolean") + + @pytest.mark.parametrize( "condition", [ @@ -176,3 +184,128 @@ async def test_switch_state_condition_behavior_all( await hass.async_block_till_done() assert condition(hass) == state["condition_true"] + + +CONDITION_STATES = [ + *parametrize_condition_states_any( + condition="switch.is_on", + target_states=[STATE_ON], + other_states=[STATE_OFF], + ), + *parametrize_condition_states_any( + condition="switch.is_off", + target_states=[STATE_OFF], + other_states=[STATE_ON], + ), +] + +CONDITION_STATES_ALL = [ + *parametrize_condition_states_all( + condition="switch.is_on", + target_states=[STATE_ON], + other_states=[STATE_OFF], + ), + *parametrize_condition_states_all( + condition="switch.is_off", + target_states=[STATE_OFF], + other_states=[STATE_ON], + ), +] + + +@pytest.mark.usefixtures("enable_labs_preview_features") +@pytest.mark.parametrize( + ("condition_target_config", "entity_id", "entities_in_target"), + parametrize_target_entities("input_boolean"), +) +@pytest.mark.parametrize( + ("condition", "condition_options", "states"), + CONDITION_STATES, +) +async def test_input_boolean_state_condition_behavior_any( + hass: HomeAssistant, + target_input_booleans: dict[str, list[str]], + condition_target_config: dict, + entity_id: str, + entities_in_target: int, + condition: str, + condition_options: dict[str, Any], + states: list[ConditionStateDescription], +) -> None: + """Test the switch condition fires for input_boolean with 'any' behavior.""" + await assert_condition_behavior_any( + hass, + target_entities=target_input_booleans, + condition_target_config=condition_target_config, + entity_id=entity_id, + entities_in_target=entities_in_target, + condition=condition, + condition_options=condition_options, + states=states, + ) + + +@pytest.mark.usefixtures("enable_labs_preview_features") +@pytest.mark.parametrize( + ("condition_target_config", "entity_id", "entities_in_target"), + parametrize_target_entities("input_boolean"), +) +@pytest.mark.parametrize( + ("condition", "condition_options", "states"), + CONDITION_STATES_ALL, +) +async def test_input_boolean_state_condition_behavior_all( + hass: HomeAssistant, + target_input_booleans: dict[str, list[str]], + condition_target_config: dict, + entity_id: str, + entities_in_target: int, + condition: str, + condition_options: dict[str, Any], + states: list[ConditionStateDescription], +) -> None: + """Test the switch condition fires for input_boolean with 'all' behavior.""" + await assert_condition_behavior_all( + hass, + target_entities=target_input_booleans, + condition_target_config=condition_target_config, + entity_id=entity_id, + entities_in_target=entities_in_target, + condition=condition, + condition_options=condition_options, + states=states, + ) + + +@pytest.mark.usefixtures("enable_labs_preview_features") +async def test_switch_condition_evaluates_both_domains( + hass: HomeAssistant, +) -> None: + """Test that the switch condition evaluates both switch and input_boolean entities.""" + entity_id_switch = "switch.test_switch" + entity_id_input_boolean = "input_boolean.test_input_boolean" + + hass.states.async_set(entity_id_switch, STATE_OFF) + hass.states.async_set(entity_id_input_boolean, STATE_OFF) + await hass.async_block_till_done() + + condition = await create_target_condition( + hass, + condition="switch.is_on", + target={CONF_ENTITY_ID: [entity_id_switch, entity_id_input_boolean]}, + behavior="any", + ) + + # Both off - condition should be false + assert condition(hass) is False + + # switch entity turns on - condition should be true + hass.states.async_set(entity_id_switch, STATE_ON) + await hass.async_block_till_done() + assert condition(hass) is True + + # Reset switch, turn on input_boolean - condition should still be true + hass.states.async_set(entity_id_switch, STATE_OFF) + hass.states.async_set(entity_id_input_boolean, STATE_ON) + await hass.async_block_till_done() + assert condition(hass) is True