Add intent to cancel all timers (#130873)

* Add intent to cancel all timers

* Add intent to llm test
This commit is contained in:
Jan-Philipp Benecke 2024-11-26 16:59:41 +01:00 committed by GitHub
parent a2ebfe6e83
commit 1a71fbe427
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 208 additions and 0 deletions

View File

@ -45,6 +45,7 @@ from homeassistant.util import dt as dt_util
from .const import DOMAIN, TIMER_DATA
from .timers import (
CancelAllTimersIntentHandler,
CancelTimerIntentHandler,
DecreaseTimerIntentHandler,
IncreaseTimerIntentHandler,
@ -130,6 +131,7 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
intent.async_register(hass, SetPositionIntentHandler())
intent.async_register(hass, StartTimerIntentHandler())
intent.async_register(hass, CancelTimerIntentHandler())
intent.async_register(hass, CancelAllTimersIntentHandler())
intent.async_register(hass, IncreaseTimerIntentHandler())
intent.async_register(hass, DecreaseTimerIntentHandler())
intent.async_register(hass, PauseTimerIntentHandler())

View File

@ -887,6 +887,32 @@ class CancelTimerIntentHandler(intent.IntentHandler):
return intent_obj.create_response()
class CancelAllTimersIntentHandler(intent.IntentHandler):
"""Intent handler for cancelling all timers."""
intent_type = intent.INTENT_CANCEL_ALL_TIMERS
description = "Cancels all timers"
slot_schema = {
vol.Optional("area"): cv.string,
}
async def async_handle(self, intent_obj: intent.Intent) -> intent.IntentResponse:
"""Handle the intent."""
hass = intent_obj.hass
timer_manager: TimerManager = hass.data[TIMER_DATA]
slots = self.async_validate_slots(intent_obj.slots)
canceled = 0
for timer in _find_timers(hass, intent_obj.device_id, slots):
timer_manager.cancel_timer(timer.id)
canceled += 1
response = intent_obj.create_response()
response.async_set_speech_slots({"canceled": canceled})
return response
class IncreaseTimerIntentHandler(intent.IntentHandler):
"""Intent handler for increasing the time of a timer."""

View File

@ -49,6 +49,7 @@ INTENT_NEVERMIND = "HassNevermind"
INTENT_SET_POSITION = "HassSetPosition"
INTENT_START_TIMER = "HassStartTimer"
INTENT_CANCEL_TIMER = "HassCancelTimer"
INTENT_CANCEL_ALL_TIMERS = "HassCancelAllTimers"
INTENT_INCREASE_TIMER = "HassIncreaseTimer"
INTENT_DECREASE_TIMER = "HassDecreaseTimer"
INTENT_PAUSE_TIMER = "HassPauseTimer"

View File

@ -1587,3 +1587,181 @@ async def test_async_device_supports_timers(hass: HomeAssistant) -> None:
# After handler registration
assert async_device_supports_timers(hass, device_id)
async def test_cancel_all_timers(hass: HomeAssistant, init_components) -> None:
"""Test cancelling all timers."""
device_id = "test_device"
started_event = asyncio.Event()
num_started = 0
@callback
def handle_timer(event_type: TimerEventType, timer: TimerInfo) -> None:
nonlocal num_started
if event_type == TimerEventType.STARTED:
num_started += 1
if num_started == 3:
started_event.set()
async_register_timer_handler(hass, device_id, handle_timer)
# Start timers
result = await intent.async_handle(
hass,
"test",
intent.INTENT_START_TIMER,
{"name": {"value": "pizza"}, "minutes": {"value": 10}},
device_id=device_id,
)
assert result.response_type == intent.IntentResponseType.ACTION_DONE
result = await intent.async_handle(
hass,
"test",
intent.INTENT_START_TIMER,
{"name": {"value": "tv"}, "minutes": {"value": 10}},
device_id=device_id,
)
assert result.response_type == intent.IntentResponseType.ACTION_DONE
result2 = await intent.async_handle(
hass,
"test",
intent.INTENT_START_TIMER,
{"name": {"value": "media"}, "minutes": {"value": 15}},
device_id=device_id,
)
assert result2.response_type == intent.IntentResponseType.ACTION_DONE
# Wait for all timers to start
async with asyncio.timeout(1):
await started_event.wait()
# Cancel all timers
result = await intent.async_handle(
hass, "test", intent.INTENT_CANCEL_ALL_TIMERS, {}, device_id=device_id
)
assert result.response_type == intent.IntentResponseType.ACTION_DONE
assert result.speech_slots.get("canceled", 0) == 3
# No timers should be running for test_device
result = await intent.async_handle(
hass, "test", intent.INTENT_TIMER_STATUS, {}, device_id=device_id
)
assert result.response_type == intent.IntentResponseType.ACTION_DONE
timers = result.speech_slots.get("timers", [])
assert len(timers) == 0
async def test_cancel_all_timers_area(
hass: HomeAssistant,
init_components,
area_registry: ar.AreaRegistry,
device_registry: dr.DeviceRegistry,
) -> None:
"""Test cancelling all timers in an area."""
entry = MockConfigEntry()
entry.add_to_hass(hass)
area_kitchen = area_registry.async_create("kitchen")
device_kitchen = device_registry.async_get_or_create(
config_entry_id=entry.entry_id,
connections=set(),
identifiers={("test", "kitchen-device")},
)
device_registry.async_update_device(device_kitchen.id, area_id=area_kitchen.id)
area_living_room = area_registry.async_create("living room")
device_living_room = device_registry.async_get_or_create(
config_entry_id=entry.entry_id,
connections=set(),
identifiers={("test", "living_room-device")},
)
device_registry.async_update_device(
device_living_room.id, area_id=area_living_room.id
)
started_event = asyncio.Event()
num_timers = 3
num_started = 0
@callback
def handle_timer(event_type: TimerEventType, timer: TimerInfo) -> None:
nonlocal num_started
if event_type == TimerEventType.STARTED:
num_started += 1
if num_started == num_timers:
started_event.set()
async_register_timer_handler(hass, device_kitchen.id, handle_timer)
async_register_timer_handler(hass, device_living_room.id, handle_timer)
# Start timers in different areas
result = await intent.async_handle(
hass,
"test",
intent.INTENT_START_TIMER,
{"name": {"value": "pizza"}, "minutes": {"value": 10}},
device_id=device_kitchen.id,
)
assert result.response_type == intent.IntentResponseType.ACTION_DONE
result = await intent.async_handle(
hass,
"test",
intent.INTENT_START_TIMER,
{"name": {"value": "tv"}, "minutes": {"value": 10}},
device_id=device_living_room.id,
)
assert result.response_type == intent.IntentResponseType.ACTION_DONE
result = await intent.async_handle(
hass,
"test",
intent.INTENT_START_TIMER,
{"name": {"value": "media"}, "minutes": {"value": 15}},
device_id=device_living_room.id,
)
assert result.response_type == intent.IntentResponseType.ACTION_DONE
# Wait for all timers to start
async with asyncio.timeout(1):
await started_event.wait()
# Cancel all timers in kitchen
result = await intent.async_handle(
hass,
"test",
intent.INTENT_CANCEL_ALL_TIMERS,
{"area": {"value": "kitchen"}},
device_id=device_kitchen.id,
)
assert result.response_type == intent.IntentResponseType.ACTION_DONE
assert result.speech_slots.get("canceled", 0) == 1
# No timers should be running in kitchen
result = await intent.async_handle(
hass,
"test",
intent.INTENT_TIMER_STATUS,
{"area": {"value": "kitchen"}},
device_id=device_kitchen.id,
)
assert result.response_type == intent.IntentResponseType.ACTION_DONE
timers = result.speech_slots.get("timers", [])
assert len(timers) == 0
# timers should be running in living room
result = await intent.async_handle(
hass,
"test",
intent.INTENT_TIMER_STATUS,
{"area": {"value": "living room"}},
device_id=device_living_room.id,
)
assert result.response_type == intent.IntentResponseType.ACTION_DONE
timers = result.speech_slots.get("timers", [])
assert len(timers) == 2

View File

@ -306,6 +306,7 @@ async def test_assist_api_tools(
"HassSetPosition",
"HassStartTimer",
"HassCancelTimer",
"HassCancelAllTimers",
"HassIncreaseTimer",
"HassDecreaseTimer",
"HassPauseTimer",