Compare commits

..

1 Commits

Author SHA1 Message Date
mib1185
51ff1ca2d8 add domain driven triggers 2025-12-17 20:26:50 +00:00
9 changed files with 113 additions and 175 deletions

View File

@@ -132,8 +132,8 @@ _EXPERIMENTAL_TRIGGER_PLATFORMS = {
"fan",
"lawn_mower",
"light",
"lock",
"media_player",
"schedule",
"switch",
"text",
"update",

View File

@@ -22,19 +22,5 @@
"unlock": {
"service": "mdi:lock-open-variant"
}
},
"triggers": {
"jammed": {
"trigger": "mdi:lock-alert"
},
"locked": {
"trigger": "mdi:lock"
},
"opened": {
"trigger": "mdi:lock-open-variant"
},
"unlocked": {
"trigger": "mdi:lock-open-variant"
}
}
}

View File

@@ -1,8 +1,4 @@
{
"common": {
"trigger_behavior_description": "The behavior of the targeted locks to trigger on.",
"trigger_behavior_name": "Behavior"
},
"device_automation": {
"action_type": {
"lock": "Lock {entity_name}",
@@ -54,15 +50,6 @@
"message": "The code for {entity_id} doesn't match pattern {code_format}."
}
},
"selector": {
"trigger_behavior": {
"options": {
"any": "Any",
"first": "First",
"last": "Last"
}
}
},
"services": {
"lock": {
"description": "Locks a lock.",
@@ -95,47 +82,5 @@
"name": "Unlock"
}
},
"title": "Lock",
"triggers": {
"jammed": {
"description": "Triggers after one or more locks jam.",
"fields": {
"behavior": {
"description": "[%key:component::lock::common::trigger_behavior_description%]",
"name": "[%key:component::lock::common::trigger_behavior_name%]"
}
},
"name": "Lock jammed"
},
"locked": {
"description": "Triggers after one or more locks lock.",
"fields": {
"behavior": {
"description": "[%key:component::lock::common::trigger_behavior_description%]",
"name": "[%key:component::lock::common::trigger_behavior_name%]"
}
},
"name": "Lock locked"
},
"opened": {
"description": "Triggers after one or more locks open.",
"fields": {
"behavior": {
"description": "[%key:component::lock::common::trigger_behavior_description%]",
"name": "[%key:component::lock::common::trigger_behavior_name%]"
}
},
"name": "Lock opened"
},
"unlocked": {
"description": "Triggers after one or more locks unlock.",
"fields": {
"behavior": {
"description": "[%key:component::lock::common::trigger_behavior_description%]",
"name": "[%key:component::lock::common::trigger_behavior_name%]"
}
},
"name": "Lock unlocked"
}
}
"title": "Lock"
}

View File

@@ -1,18 +0,0 @@
"""Provides triggers for locks."""
from homeassistant.core import HomeAssistant
from homeassistant.helpers.trigger import Trigger, make_entity_target_state_trigger
from .const import DOMAIN, LockState
TRIGGERS: dict[str, type[Trigger]] = {
"jammed": make_entity_target_state_trigger(DOMAIN, LockState.JAMMED),
"locked": make_entity_target_state_trigger(DOMAIN, LockState.LOCKED),
"opened": make_entity_target_state_trigger(DOMAIN, LockState.OPEN),
"unlocked": make_entity_target_state_trigger(DOMAIN, LockState.UNLOCKED),
}
async def async_get_triggers(hass: HomeAssistant) -> dict[str, type[Trigger]]:
"""Return the triggers for locks."""
return TRIGGERS

View File

@@ -6,5 +6,13 @@
"reload": {
"service": "mdi:reload"
}
},
"triggers": {
"turned_off": {
"trigger": "mdi:calendar-blank"
},
"turned_on": {
"trigger": "mdi:calendar-clock"
}
}
}

View File

@@ -1,4 +1,8 @@
{
"common": {
"trigger_behavior_description": "The behavior of the targeted schedules to trigger on.",
"trigger_behavior_name": "Behavior"
},
"entity_component": {
"_": {
"name": "[%key:component::schedule::title%]",
@@ -20,6 +24,15 @@
}
}
},
"selector": {
"trigger_behavior": {
"options": {
"any": "Any",
"first": "First",
"last": "Last"
}
}
},
"services": {
"get_schedule": {
"description": "Retrieves the configured time ranges of one or multiple schedules.",
@@ -30,5 +43,27 @@
"name": "[%key:common::action::reload%]"
}
},
"title": "Schedule"
"title": "Schedule",
"triggers": {
"turned_off": {
"description": "Triggers after one or more schedules turn off.",
"fields": {
"behavior": {
"description": "[%key:component::schedule::common::trigger_behavior_description%]",
"name": "[%key:component::schedule::common::trigger_behavior_name%]"
}
},
"name": "Schedule turned off"
},
"turned_on": {
"description": "Triggers after one or more schedules turn on.",
"fields": {
"behavior": {
"description": "[%key:component::schedule::common::trigger_behavior_description%]",
"name": "[%key:component::schedule::common::trigger_behavior_name%]"
}
},
"name": "Schedule turned on"
}
}
}

View File

@@ -0,0 +1,17 @@
"""Provides triggers for schedulers."""
from homeassistant.const import STATE_OFF, STATE_ON
from homeassistant.core import HomeAssistant
from homeassistant.helpers.trigger import Trigger, make_entity_target_state_trigger
from . import DOMAIN
TRIGGERS: dict[str, type[Trigger]] = {
"turned_on": make_entity_target_state_trigger(DOMAIN, STATE_ON),
"turned_off": make_entity_target_state_trigger(DOMAIN, STATE_OFF),
}
async def async_get_triggers(hass: HomeAssistant) -> dict[str, type[Trigger]]:
"""Return the triggers for schedulers."""
return TRIGGERS

View File

@@ -1,7 +1,7 @@
.trigger_common: &trigger_common
target:
entity:
domain: lock
domain: schedule
fields:
behavior:
required: true
@@ -14,7 +14,5 @@
- any
translation_key: trigger_behavior
jammed: *trigger_common
locked: *trigger_common
opened: *trigger_common
unlocked: *trigger_common
turned_off: *trigger_common
turned_on: *trigger_common

View File

@@ -1,18 +1,17 @@
"""Test lock triggers."""
"""Test schedule triggers."""
from collections.abc import Generator
from unittest.mock import patch
import pytest
from homeassistant.components.lock import DOMAIN, LockState
from homeassistant.const import ATTR_LABEL_ID, CONF_ENTITY_ID
from homeassistant.components.schedule.const import DOMAIN
from homeassistant.const import ATTR_LABEL_ID, CONF_ENTITY_ID, STATE_OFF, STATE_ON
from homeassistant.core import HomeAssistant, ServiceCall
from tests.components import (
StateDescription,
arm_trigger,
other_states,
parametrize_target_entities,
parametrize_trigger_states,
set_or_remove_state,
@@ -36,24 +35,22 @@ def enable_experimental_triggers_conditions() -> Generator[None]:
@pytest.fixture
async def target_locks(hass: HomeAssistant) -> list[str]:
"""Create multiple lock entities associated with different targets."""
async def target_schedules(hass: HomeAssistant) -> list[str]:
"""Create multiple schedule entities associated with different targets."""
return (await target_entities(hass, DOMAIN))["included"]
@pytest.mark.parametrize(
"trigger_key",
[
"lock.jammed",
"lock.locked",
"lock.opened",
"lock.unlocked",
"schedule.turned_off",
"schedule.turned_on",
],
)
async def test_lock_triggers_gated_by_labs_flag(
async def test_schedule_triggers_gated_by_labs_flag(
hass: HomeAssistant, caplog: pytest.LogCaptureFixture, trigger_key: str
) -> None:
"""Test the lock triggers are gated by the labs flag."""
"""Test the schedule triggers are gated by the labs flag."""
await arm_trigger(hass, trigger_key, None, {ATTR_LABEL_ID: "test_label"})
assert (
"Unnamed automation failed to setup triggers and has been disabled: Trigger "
@@ -72,42 +69,32 @@ async def test_lock_triggers_gated_by_labs_flag(
("trigger", "states"),
[
*parametrize_trigger_states(
trigger="lock.jammed",
target_states=[LockState.JAMMED],
other_states=other_states(LockState.JAMMED),
trigger="schedule.turned_off",
target_states=[STATE_OFF],
other_states=[STATE_ON],
),
*parametrize_trigger_states(
trigger="lock.locked",
target_states=[LockState.LOCKED],
other_states=other_states(LockState.LOCKED),
),
*parametrize_trigger_states(
trigger="lock.opened",
target_states=[LockState.OPEN],
other_states=other_states(LockState.OPEN),
),
*parametrize_trigger_states(
trigger="lock.unlocked",
target_states=[LockState.UNLOCKED],
other_states=other_states(LockState.UNLOCKED),
trigger="schedule.turned_on",
target_states=[STATE_ON],
other_states=[STATE_OFF],
),
],
)
async def test_lock_state_trigger_behavior_any(
async def test_schedule_state_trigger_behavior_any(
hass: HomeAssistant,
service_calls: list[ServiceCall],
target_locks: list[str],
target_schedules: list[str],
trigger_target_config: dict,
entity_id: str,
entities_in_target: int,
trigger: str,
states: list[StateDescription],
) -> None:
"""Test that the lock state trigger fires when any lock state changes to a specific state."""
other_entity_ids = set(target_locks) - {entity_id}
"""Test that the schedule state trigger fires when any schedule state changes to a specific state."""
other_entity_ids = set(target_schedules) - {entity_id}
# Set all locks, including the tested one, to the initial state
for eid in target_locks:
# Set all schedules, including the tested one, to the initial state
for eid in target_schedules:
set_or_remove_state(hass, eid, states[0]["included"])
await hass.async_block_till_done()
@@ -122,7 +109,7 @@ async def test_lock_state_trigger_behavior_any(
assert service_call.data[CONF_ENTITY_ID] == entity_id
service_calls.clear()
# Check if changing other locks also triggers
# Check if changing other schedules 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()
@@ -139,42 +126,32 @@ async def test_lock_state_trigger_behavior_any(
("trigger", "states"),
[
*parametrize_trigger_states(
trigger="lock.jammed",
target_states=[LockState.JAMMED],
other_states=other_states(LockState.JAMMED),
trigger="schedule.turned_off",
target_states=[STATE_OFF],
other_states=[STATE_ON],
),
*parametrize_trigger_states(
trigger="lock.locked",
target_states=[LockState.LOCKED],
other_states=other_states(LockState.LOCKED),
),
*parametrize_trigger_states(
trigger="lock.opened",
target_states=[LockState.OPEN],
other_states=other_states(LockState.OPEN),
),
*parametrize_trigger_states(
trigger="lock.unlocked",
target_states=[LockState.UNLOCKED],
other_states=other_states(LockState.UNLOCKED),
trigger="schedule.turned_on",
target_states=[STATE_ON],
other_states=[STATE_OFF],
),
],
)
async def test_lock_state_trigger_behavior_first(
async def test_schedule_state_trigger_behavior_first(
hass: HomeAssistant,
service_calls: list[ServiceCall],
target_locks: list[str],
target_schedules: list[str],
trigger_target_config: dict,
entity_id: str,
entities_in_target: int,
trigger: str,
states: list[StateDescription],
) -> None:
"""Test that the lock state trigger fires when the first lock changes to a specific state."""
other_entity_ids = set(target_locks) - {entity_id}
"""Test that the schedule state trigger fires when the first schedule changes to a specific state."""
other_entity_ids = set(target_schedules) - {entity_id}
# Set all locks, including the tested one, to the initial state
for eid in target_locks:
# Set all schedules, including the tested one, to the initial state
for eid in target_schedules:
set_or_remove_state(hass, eid, states[0]["included"])
await hass.async_block_till_done()
@@ -189,7 +166,7 @@ async def test_lock_state_trigger_behavior_first(
assert service_call.data[CONF_ENTITY_ID] == entity_id
service_calls.clear()
# Triggering other locks should not cause the trigger to fire again
# Triggering other schedules should not cause the trigger to fire again
for other_entity_id in other_entity_ids:
set_or_remove_state(hass, other_entity_id, included_state)
await hass.async_block_till_done()
@@ -205,42 +182,32 @@ async def test_lock_state_trigger_behavior_first(
("trigger", "states"),
[
*parametrize_trigger_states(
trigger="lock.jammed",
target_states=[LockState.JAMMED],
other_states=other_states(LockState.JAMMED),
trigger="schedule.turned_off",
target_states=[STATE_OFF],
other_states=[STATE_ON],
),
*parametrize_trigger_states(
trigger="lock.locked",
target_states=[LockState.LOCKED],
other_states=other_states(LockState.LOCKED),
),
*parametrize_trigger_states(
trigger="lock.opened",
target_states=[LockState.OPEN],
other_states=other_states(LockState.OPEN),
),
*parametrize_trigger_states(
trigger="lock.unlocked",
target_states=[LockState.UNLOCKED],
other_states=other_states(LockState.UNLOCKED),
trigger="schedule.turned_on",
target_states=[STATE_ON],
other_states=[STATE_OFF],
),
],
)
async def test_lock_state_trigger_behavior_last(
async def test_schedule_state_trigger_behavior_last(
hass: HomeAssistant,
service_calls: list[ServiceCall],
target_locks: list[str],
target_schedules: list[str],
trigger_target_config: dict,
entity_id: str,
entities_in_target: int,
trigger: str,
states: list[StateDescription],
) -> None:
"""Test that the lock state trigger fires when the last lock changes to a specific state."""
other_entity_ids = set(target_locks) - {entity_id}
"""Test that the schedule state trigger fires when the last schedule changes to a specific state."""
other_entity_ids = set(target_schedules) - {entity_id}
# Set all locks, including the tested one, to the initial state
for eid in target_locks:
# Set all schedules, including the tested one, to the initial state
for eid in target_schedules:
set_or_remove_state(hass, eid, states[0]["included"])
await hass.async_block_till_done()