Files

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

254 lines
8.3 KiB
Python
Raw Permalink Normal View History

"""Test counter triggers."""
from typing import Any
import pytest
from homeassistant.components.counter import (
CONF_INITIAL,
CONF_MAXIMUM,
CONF_MINIMUM,
DOMAIN,
)
2026-03-26 00:51:14 +01:00
from homeassistant.const import STATE_UNAVAILABLE, STATE_UNKNOWN
from homeassistant.core import HomeAssistant
from tests.components.common import (
BasicTriggerStateDescription,
TriggerStateDescription,
arm_trigger,
2026-05-27 16:01:11 +02:00
assert_trigger_behavior_all,
assert_trigger_behavior_each,
assert_trigger_behavior_first,
assert_trigger_options_supported,
parametrize_target_entities,
parametrize_trigger_states,
set_or_remove_state,
target_entities,
)
BEHAVIOR_AWARE_TRIGGERS = [
*parametrize_trigger_states(
trigger="counter.maximum_reached",
target_states=[("2", {CONF_MAXIMUM: 2})],
other_states=[("1", {CONF_MAXIMUM: 2})],
),
*parametrize_trigger_states(
trigger="counter.minimum_reached",
target_states=[("1", {CONF_MINIMUM: 1})],
other_states=[("2", {CONF_MINIMUM: 1})],
),
*parametrize_trigger_states(
trigger="counter.reset",
target_states=[("2", {CONF_INITIAL: 2})],
other_states=[("3", {CONF_INITIAL: 2})],
),
]
@pytest.fixture
async def target_counters(hass: HomeAssistant) -> dict[str, list[str]]:
"""Create multiple counter entities associated with different targets."""
return await target_entities(hass, DOMAIN)
@pytest.mark.parametrize(
("trigger_key", "base_options", "supports_behavior", "supports_duration"),
[
("counter.incremented", None, False, False),
("counter.decremented", None, False, False),
("counter.maximum_reached", {}, True, True),
("counter.minimum_reached", {}, True, True),
("counter.reset", {}, True, True),
],
)
async def test_counter_trigger_options_validation(
hass: HomeAssistant,
trigger_key: str,
base_options: dict[str, Any] | None,
supports_behavior: bool,
supports_duration: bool,
) -> None:
"""Test that counter triggers support the expected options."""
await assert_trigger_options_supported(
hass,
trigger_key,
base_options,
supports_behavior=supports_behavior,
supports_duration=supports_duration,
)
@pytest.mark.parametrize(
("trigger_target_config", "entity_id", "entities_in_target"),
parametrize_target_entities(DOMAIN),
)
@pytest.mark.parametrize(
("trigger", "states"),
[
(
"counter.decremented",
[
{"included_state": {"state": None, "attributes": {}}, "count": 0},
{"included_state": {"state": "1", "attributes": {}}, "count": 0},
{
"included_state": {"state": STATE_UNAVAILABLE, "attributes": {}},
"count": 0,
},
{"included_state": {"state": "1", "attributes": {}}, "count": 0},
{
"included_state": {"state": STATE_UNKNOWN, "attributes": {}},
"count": 0,
},
{"included_state": {"state": "1", "attributes": {}}, "count": 0},
{"included_state": {"state": "2", "attributes": {}}, "count": 0},
{"included_state": {"state": "1", "attributes": {}}, "count": 1},
],
),
(
"counter.incremented",
[
{"included_state": {"state": None, "attributes": {}}, "count": 0},
{"included_state": {"state": "2", "attributes": {}}, "count": 0},
{
"included_state": {"state": STATE_UNAVAILABLE, "attributes": {}},
"count": 0,
},
{"included_state": {"state": "2", "attributes": {}}, "count": 0},
{
"included_state": {"state": STATE_UNKNOWN, "attributes": {}},
"count": 0,
},
{"included_state": {"state": "2", "attributes": {}}, "count": 0},
{"included_state": {"state": "1", "attributes": {}}, "count": 0},
{"included_state": {"state": "2", "attributes": {}}, "count": 1},
],
),
],
)
async def test_counter_state_trigger(
hass: HomeAssistant,
target_counters: dict[str, list[str]],
trigger_target_config: dict,
entity_id: str,
entities_in_target: int,
trigger: str,
states: list[BasicTriggerStateDescription],
) -> None:
"""Test that the counter decrement and increment triggers fire correctly."""
2026-03-26 00:51:14 +01:00
calls: list[str] = []
other_entity_ids = set(target_counters["included_entities"]) - {entity_id}
# Set all counters, including the tested one, to the initial state
for eid in target_counters["included_entities"]:
set_or_remove_state(hass, eid, states[0]["included_state"])
await hass.async_block_till_done()
2026-03-26 00:51:14 +01:00
await arm_trigger(hass, trigger, None, trigger_target_config, calls)
for state in states[1:]:
included_state = state["included_state"]
set_or_remove_state(hass, entity_id, included_state)
await hass.async_block_till_done()
2026-03-26 00:51:14 +01:00
assert len(calls) == state["count"]
for call in calls:
assert call == entity_id
calls.clear()
# Check if changing other counters also triggers
for other_entity_id in other_entity_ids:
set_or_remove_state(hass, other_entity_id, included_state)
await hass.async_block_till_done()
2026-03-26 00:51:14 +01:00
assert len(calls) == (entities_in_target - 1) * state["count"]
calls.clear()
@pytest.mark.parametrize(
("trigger_target_config", "entity_id", "entities_in_target"),
parametrize_target_entities(DOMAIN),
)
@pytest.mark.parametrize(
("trigger", "trigger_options", "states"), BEHAVIOR_AWARE_TRIGGERS
)
2026-05-27 16:01:11 +02:00
async def test_counter_state_trigger_behavior_each(
hass: HomeAssistant,
target_counters: dict[str, list[str]],
trigger_target_config: dict,
entity_id: str,
entities_in_target: int,
trigger: str,
trigger_options: dict[str, Any] | None,
states: list[TriggerStateDescription],
) -> None:
"""Test counter trigger fires on any state change."""
2026-05-27 16:01:11 +02:00
await assert_trigger_behavior_each(
hass,
target_entities=target_counters,
trigger_target_config=trigger_target_config,
entity_id=entity_id,
entities_in_target=entities_in_target,
trigger=trigger,
trigger_options=trigger_options,
states=states,
)
@pytest.mark.parametrize(
("trigger_target_config", "entity_id", "entities_in_target"),
parametrize_target_entities(DOMAIN),
)
@pytest.mark.parametrize(
("trigger", "trigger_options", "states"), BEHAVIOR_AWARE_TRIGGERS
)
async def test_counter_state_trigger_behavior_first(
hass: HomeAssistant,
target_counters: dict[str, list[str]],
trigger_target_config: dict,
entity_id: str,
entities_in_target: int,
trigger: str,
trigger_options: dict[str, Any],
states: list[TriggerStateDescription],
) -> None:
"""Test counter trigger fires on first state change."""
await assert_trigger_behavior_first(
hass,
target_entities=target_counters,
trigger_target_config=trigger_target_config,
entity_id=entity_id,
entities_in_target=entities_in_target,
trigger=trigger,
trigger_options=trigger_options,
states=states,
)
@pytest.mark.parametrize(
("trigger_target_config", "entity_id", "entities_in_target"),
parametrize_target_entities(DOMAIN),
)
@pytest.mark.parametrize(
("trigger", "trigger_options", "states"), BEHAVIOR_AWARE_TRIGGERS
)
2026-05-27 16:01:11 +02:00
async def test_counter_state_trigger_behavior_all(
hass: HomeAssistant,
target_counters: dict[str, list[str]],
trigger_target_config: dict,
entity_id: str,
entities_in_target: int,
trigger: str,
trigger_options: dict[str, Any],
states: list[TriggerStateDescription],
) -> None:
2026-05-27 16:01:11 +02:00
"""Test counter trigger fires when all counters have changed state."""
await assert_trigger_behavior_all(
hass,
target_entities=target_counters,
trigger_target_config=trigger_target_config,
entity_id=entity_id,
entities_in_target=entities_in_target,
trigger=trigger,
trigger_options=trigger_options,
states=states,
)