mirror of
https://github.com/home-assistant/core.git
synced 2025-07-23 13:17:32 +00:00
Add intent to cancel all timers (#130873)
* Add intent to cancel all timers * Add intent to llm test
This commit is contained in:
parent
a2ebfe6e83
commit
1a71fbe427
@ -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())
|
||||
|
@ -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."""
|
||||
|
||||
|
@ -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"
|
||||
|
@ -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
|
||||
|
@ -306,6 +306,7 @@ async def test_assist_api_tools(
|
||||
"HassSetPosition",
|
||||
"HassStartTimer",
|
||||
"HassCancelTimer",
|
||||
"HassCancelAllTimers",
|
||||
"HassIncreaseTimer",
|
||||
"HassDecreaseTimer",
|
||||
"HassPauseTimer",
|
||||
|
Loading…
x
Reference in New Issue
Block a user