mirror of
https://github.com/home-assistant/core.git
synced 2025-07-20 19:57:07 +00:00
Forward timer events to Wyoming satellites (#118128)
* Add timer tests * Forward timer events to satellites * Use config entry for background tasks
This commit is contained in:
parent
039bc3501b
commit
3766c72ddb
@ -89,7 +89,7 @@ def _make_satellite(
|
|||||||
device_id=device.id,
|
device_id=device.id,
|
||||||
)
|
)
|
||||||
|
|
||||||
return WyomingSatellite(hass, service, satellite_device)
|
return WyomingSatellite(hass, config_entry, service, satellite_device)
|
||||||
|
|
||||||
|
|
||||||
async def update_listener(hass: HomeAssistant, entry: ConfigEntry):
|
async def update_listener(hass: HomeAssistant, entry: ConfigEntry):
|
||||||
|
@ -3,10 +3,10 @@
|
|||||||
"name": "Wyoming Protocol",
|
"name": "Wyoming Protocol",
|
||||||
"codeowners": ["@balloob", "@synesthesiam"],
|
"codeowners": ["@balloob", "@synesthesiam"],
|
||||||
"config_flow": true,
|
"config_flow": true,
|
||||||
"dependencies": ["assist_pipeline"],
|
"dependencies": ["assist_pipeline", "intent"],
|
||||||
"documentation": "https://www.home-assistant.io/integrations/wyoming",
|
"documentation": "https://www.home-assistant.io/integrations/wyoming",
|
||||||
"integration_type": "service",
|
"integration_type": "service",
|
||||||
"iot_class": "local_push",
|
"iot_class": "local_push",
|
||||||
"requirements": ["wyoming==1.5.3"],
|
"requirements": ["wyoming==1.5.4"],
|
||||||
"zeroconf": ["_wyoming._tcp.local."]
|
"zeroconf": ["_wyoming._tcp.local."]
|
||||||
}
|
}
|
||||||
|
@ -11,17 +11,20 @@ from wyoming.asr import Transcribe, Transcript
|
|||||||
from wyoming.audio import AudioChunk, AudioChunkConverter, AudioStart, AudioStop
|
from wyoming.audio import AudioChunk, AudioChunkConverter, AudioStart, AudioStop
|
||||||
from wyoming.client import AsyncTcpClient
|
from wyoming.client import AsyncTcpClient
|
||||||
from wyoming.error import Error
|
from wyoming.error import Error
|
||||||
|
from wyoming.event import Event
|
||||||
from wyoming.info import Describe, Info
|
from wyoming.info import Describe, Info
|
||||||
from wyoming.ping import Ping, Pong
|
from wyoming.ping import Ping, Pong
|
||||||
from wyoming.pipeline import PipelineStage, RunPipeline
|
from wyoming.pipeline import PipelineStage, RunPipeline
|
||||||
from wyoming.satellite import PauseSatellite, RunSatellite
|
from wyoming.satellite import PauseSatellite, RunSatellite
|
||||||
|
from wyoming.timer import TimerCancelled, TimerFinished, TimerStarted, TimerUpdated
|
||||||
from wyoming.tts import Synthesize, SynthesizeVoice
|
from wyoming.tts import Synthesize, SynthesizeVoice
|
||||||
from wyoming.vad import VoiceStarted, VoiceStopped
|
from wyoming.vad import VoiceStarted, VoiceStopped
|
||||||
from wyoming.wake import Detect, Detection
|
from wyoming.wake import Detect, Detection
|
||||||
|
|
||||||
from homeassistant.components import assist_pipeline, stt, tts
|
from homeassistant.components import assist_pipeline, intent, stt, tts
|
||||||
from homeassistant.components.assist_pipeline import select as pipeline_select
|
from homeassistant.components.assist_pipeline import select as pipeline_select
|
||||||
from homeassistant.core import Context, HomeAssistant
|
from homeassistant.config_entries import ConfigEntry
|
||||||
|
from homeassistant.core import Context, HomeAssistant, callback
|
||||||
|
|
||||||
from .const import DOMAIN
|
from .const import DOMAIN
|
||||||
from .data import WyomingService
|
from .data import WyomingService
|
||||||
@ -49,10 +52,15 @@ class WyomingSatellite:
|
|||||||
"""Remove voice satellite running the Wyoming protocol."""
|
"""Remove voice satellite running the Wyoming protocol."""
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self, hass: HomeAssistant, service: WyomingService, device: SatelliteDevice
|
self,
|
||||||
|
hass: HomeAssistant,
|
||||||
|
config_entry: ConfigEntry,
|
||||||
|
service: WyomingService,
|
||||||
|
device: SatelliteDevice,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Initialize satellite."""
|
"""Initialize satellite."""
|
||||||
self.hass = hass
|
self.hass = hass
|
||||||
|
self.config_entry = config_entry
|
||||||
self.service = service
|
self.service = service
|
||||||
self.device = device
|
self.device = device
|
||||||
self.is_running = True
|
self.is_running = True
|
||||||
@ -73,6 +81,10 @@ class WyomingSatellite:
|
|||||||
"""Run and maintain a connection to satellite."""
|
"""Run and maintain a connection to satellite."""
|
||||||
_LOGGER.debug("Running satellite task")
|
_LOGGER.debug("Running satellite task")
|
||||||
|
|
||||||
|
unregister_timer_handler = intent.async_register_timer_handler(
|
||||||
|
self.hass, self.device.device_id, self._handle_timer
|
||||||
|
)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
while self.is_running:
|
while self.is_running:
|
||||||
try:
|
try:
|
||||||
@ -97,6 +109,8 @@ class WyomingSatellite:
|
|||||||
# Wait to restart
|
# Wait to restart
|
||||||
await self.on_restart()
|
await self.on_restart()
|
||||||
finally:
|
finally:
|
||||||
|
unregister_timer_handler()
|
||||||
|
|
||||||
# Ensure sensor is off (before stop)
|
# Ensure sensor is off (before stop)
|
||||||
self.device.set_is_active(False)
|
self.device.set_is_active(False)
|
||||||
|
|
||||||
@ -142,7 +156,8 @@ class WyomingSatellite:
|
|||||||
def _send_pause(self) -> None:
|
def _send_pause(self) -> None:
|
||||||
"""Send a pause message to satellite."""
|
"""Send a pause message to satellite."""
|
||||||
if self._client is not None:
|
if self._client is not None:
|
||||||
self.hass.async_create_background_task(
|
self.config_entry.async_create_background_task(
|
||||||
|
self.hass,
|
||||||
self._client.write_event(PauseSatellite().event()),
|
self._client.write_event(PauseSatellite().event()),
|
||||||
"pause satellite",
|
"pause satellite",
|
||||||
)
|
)
|
||||||
@ -207,11 +222,11 @@ class WyomingSatellite:
|
|||||||
send_ping = True
|
send_ping = True
|
||||||
|
|
||||||
# Read events and check for pipeline end in parallel
|
# Read events and check for pipeline end in parallel
|
||||||
pipeline_ended_task = self.hass.async_create_background_task(
|
pipeline_ended_task = self.config_entry.async_create_background_task(
|
||||||
self._pipeline_ended_event.wait(), "satellite pipeline ended"
|
self.hass, self._pipeline_ended_event.wait(), "satellite pipeline ended"
|
||||||
)
|
)
|
||||||
client_event_task = self.hass.async_create_background_task(
|
client_event_task = self.config_entry.async_create_background_task(
|
||||||
self._client.read_event(), "satellite event read"
|
self.hass, self._client.read_event(), "satellite event read"
|
||||||
)
|
)
|
||||||
pending = {pipeline_ended_task, client_event_task}
|
pending = {pipeline_ended_task, client_event_task}
|
||||||
|
|
||||||
@ -222,8 +237,8 @@ class WyomingSatellite:
|
|||||||
if send_ping:
|
if send_ping:
|
||||||
# Ensure satellite is still connected
|
# Ensure satellite is still connected
|
||||||
send_ping = False
|
send_ping = False
|
||||||
self.hass.async_create_background_task(
|
self.config_entry.async_create_background_task(
|
||||||
self._send_delayed_ping(), "ping satellite"
|
self.hass, self._send_delayed_ping(), "ping satellite"
|
||||||
)
|
)
|
||||||
|
|
||||||
async with asyncio.timeout(_PING_TIMEOUT):
|
async with asyncio.timeout(_PING_TIMEOUT):
|
||||||
@ -234,8 +249,12 @@ class WyomingSatellite:
|
|||||||
# Pipeline run end event was received
|
# Pipeline run end event was received
|
||||||
_LOGGER.debug("Pipeline finished")
|
_LOGGER.debug("Pipeline finished")
|
||||||
self._pipeline_ended_event.clear()
|
self._pipeline_ended_event.clear()
|
||||||
pipeline_ended_task = self.hass.async_create_background_task(
|
pipeline_ended_task = (
|
||||||
self._pipeline_ended_event.wait(), "satellite pipeline ended"
|
self.config_entry.async_create_background_task(
|
||||||
|
self.hass,
|
||||||
|
self._pipeline_ended_event.wait(),
|
||||||
|
"satellite pipeline ended",
|
||||||
|
)
|
||||||
)
|
)
|
||||||
pending.add(pipeline_ended_task)
|
pending.add(pipeline_ended_task)
|
||||||
|
|
||||||
@ -307,8 +326,8 @@ class WyomingSatellite:
|
|||||||
_LOGGER.debug("Unexpected event from satellite: %s", client_event)
|
_LOGGER.debug("Unexpected event from satellite: %s", client_event)
|
||||||
|
|
||||||
# Next event
|
# Next event
|
||||||
client_event_task = self.hass.async_create_background_task(
|
client_event_task = self.config_entry.async_create_background_task(
|
||||||
self._client.read_event(), "satellite event read"
|
self.hass, self._client.read_event(), "satellite event read"
|
||||||
)
|
)
|
||||||
pending.add(client_event_task)
|
pending.add(client_event_task)
|
||||||
|
|
||||||
@ -348,7 +367,8 @@ class WyomingSatellite:
|
|||||||
)
|
)
|
||||||
self._is_pipeline_running = True
|
self._is_pipeline_running = True
|
||||||
self._pipeline_ended_event.clear()
|
self._pipeline_ended_event.clear()
|
||||||
self.hass.async_create_background_task(
|
self.config_entry.async_create_background_task(
|
||||||
|
self.hass,
|
||||||
assist_pipeline.async_pipeline_from_audio_stream(
|
assist_pipeline.async_pipeline_from_audio_stream(
|
||||||
self.hass,
|
self.hass,
|
||||||
context=Context(),
|
context=Context(),
|
||||||
@ -544,3 +564,38 @@ class WyomingSatellite:
|
|||||||
yield chunk
|
yield chunk
|
||||||
except asyncio.CancelledError:
|
except asyncio.CancelledError:
|
||||||
pass # ignore
|
pass # ignore
|
||||||
|
|
||||||
|
@callback
|
||||||
|
def _handle_timer(
|
||||||
|
self, event_type: intent.TimerEventType, timer: intent.TimerInfo
|
||||||
|
) -> None:
|
||||||
|
"""Forward timer events to satellite."""
|
||||||
|
assert self._client is not None
|
||||||
|
|
||||||
|
_LOGGER.debug("Timer event: type=%s, info=%s", event_type, timer)
|
||||||
|
event: Event | None = None
|
||||||
|
if event_type == intent.TimerEventType.STARTED:
|
||||||
|
event = TimerStarted(
|
||||||
|
id=timer.id,
|
||||||
|
total_seconds=timer.seconds,
|
||||||
|
name=timer.name,
|
||||||
|
start_hours=timer.start_hours,
|
||||||
|
start_minutes=timer.start_minutes,
|
||||||
|
start_seconds=timer.start_seconds,
|
||||||
|
).event()
|
||||||
|
elif event_type == intent.TimerEventType.UPDATED:
|
||||||
|
event = TimerUpdated(
|
||||||
|
id=timer.id,
|
||||||
|
is_active=timer.is_active,
|
||||||
|
total_seconds=timer.seconds,
|
||||||
|
).event()
|
||||||
|
elif event_type == intent.TimerEventType.CANCELLED:
|
||||||
|
event = TimerCancelled(id=timer.id).event()
|
||||||
|
elif event_type == intent.TimerEventType.FINISHED:
|
||||||
|
event = TimerFinished(id=timer.id).event()
|
||||||
|
|
||||||
|
if event is not None:
|
||||||
|
# Send timer event to satellite
|
||||||
|
self.config_entry.async_create_background_task(
|
||||||
|
self.hass, self._client.write_event(event), "wyoming timer event"
|
||||||
|
)
|
||||||
|
@ -2894,7 +2894,7 @@ wled==0.18.0
|
|||||||
wolf-comm==0.0.8
|
wolf-comm==0.0.8
|
||||||
|
|
||||||
# homeassistant.components.wyoming
|
# homeassistant.components.wyoming
|
||||||
wyoming==1.5.3
|
wyoming==1.5.4
|
||||||
|
|
||||||
# homeassistant.components.xbox
|
# homeassistant.components.xbox
|
||||||
xbox-webapi==2.0.11
|
xbox-webapi==2.0.11
|
||||||
|
@ -2250,7 +2250,7 @@ wled==0.18.0
|
|||||||
wolf-comm==0.0.8
|
wolf-comm==0.0.8
|
||||||
|
|
||||||
# homeassistant.components.wyoming
|
# homeassistant.components.wyoming
|
||||||
wyoming==1.5.3
|
wyoming==1.5.4
|
||||||
|
|
||||||
# homeassistant.components.xbox
|
# homeassistant.components.xbox
|
||||||
xbox-webapi==2.0.11
|
xbox-webapi==2.0.11
|
||||||
|
@ -17,6 +17,7 @@ from wyoming.info import Info
|
|||||||
from wyoming.ping import Ping, Pong
|
from wyoming.ping import Ping, Pong
|
||||||
from wyoming.pipeline import PipelineStage, RunPipeline
|
from wyoming.pipeline import PipelineStage, RunPipeline
|
||||||
from wyoming.satellite import RunSatellite
|
from wyoming.satellite import RunSatellite
|
||||||
|
from wyoming.timer import TimerCancelled, TimerFinished, TimerStarted, TimerUpdated
|
||||||
from wyoming.tts import Synthesize
|
from wyoming.tts import Synthesize
|
||||||
from wyoming.vad import VoiceStarted, VoiceStopped
|
from wyoming.vad import VoiceStarted, VoiceStopped
|
||||||
from wyoming.wake import Detect, Detection
|
from wyoming.wake import Detect, Detection
|
||||||
@ -26,6 +27,7 @@ from homeassistant.components.wyoming.data import WyomingService
|
|||||||
from homeassistant.components.wyoming.devices import SatelliteDevice
|
from homeassistant.components.wyoming.devices import SatelliteDevice
|
||||||
from homeassistant.config_entries import ConfigEntry
|
from homeassistant.config_entries import ConfigEntry
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
|
from homeassistant.helpers import intent as intent_helper
|
||||||
from homeassistant.setup import async_setup_component
|
from homeassistant.setup import async_setup_component
|
||||||
|
|
||||||
from . import SATELLITE_INFO, WAKE_WORD_INFO, MockAsyncTcpClient
|
from . import SATELLITE_INFO, WAKE_WORD_INFO, MockAsyncTcpClient
|
||||||
@ -111,6 +113,18 @@ class SatelliteAsyncTcpClient(MockAsyncTcpClient):
|
|||||||
self.ping_event = asyncio.Event()
|
self.ping_event = asyncio.Event()
|
||||||
self.ping: Ping | None = None
|
self.ping: Ping | None = None
|
||||||
|
|
||||||
|
self.timer_started_event = asyncio.Event()
|
||||||
|
self.timer_started: TimerStarted | None = None
|
||||||
|
|
||||||
|
self.timer_updated_event = asyncio.Event()
|
||||||
|
self.timer_updated: TimerUpdated | None = None
|
||||||
|
|
||||||
|
self.timer_cancelled_event = asyncio.Event()
|
||||||
|
self.timer_cancelled: TimerCancelled | None = None
|
||||||
|
|
||||||
|
self.timer_finished_event = asyncio.Event()
|
||||||
|
self.timer_finished: TimerFinished | None = None
|
||||||
|
|
||||||
self._mic_audio_chunk = AudioChunk(
|
self._mic_audio_chunk = AudioChunk(
|
||||||
rate=16000, width=2, channels=1, audio=b"chunk"
|
rate=16000, width=2, channels=1, audio=b"chunk"
|
||||||
).event()
|
).event()
|
||||||
@ -159,6 +173,18 @@ class SatelliteAsyncTcpClient(MockAsyncTcpClient):
|
|||||||
elif Ping.is_type(event.type):
|
elif Ping.is_type(event.type):
|
||||||
self.ping = Ping.from_event(event)
|
self.ping = Ping.from_event(event)
|
||||||
self.ping_event.set()
|
self.ping_event.set()
|
||||||
|
elif TimerStarted.is_type(event.type):
|
||||||
|
self.timer_started = TimerStarted.from_event(event)
|
||||||
|
self.timer_started_event.set()
|
||||||
|
elif TimerUpdated.is_type(event.type):
|
||||||
|
self.timer_updated = TimerUpdated.from_event(event)
|
||||||
|
self.timer_updated_event.set()
|
||||||
|
elif TimerCancelled.is_type(event.type):
|
||||||
|
self.timer_cancelled = TimerCancelled.from_event(event)
|
||||||
|
self.timer_cancelled_event.set()
|
||||||
|
elif TimerFinished.is_type(event.type):
|
||||||
|
self.timer_finished = TimerFinished.from_event(event)
|
||||||
|
self.timer_finished_event.set()
|
||||||
|
|
||||||
async def read_event(self) -> Event | None:
|
async def read_event(self) -> Event | None:
|
||||||
"""Receive."""
|
"""Receive."""
|
||||||
@ -1083,3 +1109,186 @@ async def test_wake_word_phrase(hass: HomeAssistant) -> None:
|
|||||||
assert (
|
assert (
|
||||||
mock_run_pipeline.call_args.kwargs.get("wake_word_phrase") == "Test Phrase"
|
mock_run_pipeline.call_args.kwargs.get("wake_word_phrase") == "Test Phrase"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
async def test_timers(hass: HomeAssistant) -> None:
|
||||||
|
"""Test timer events."""
|
||||||
|
assert await async_setup_component(hass, "intent", {})
|
||||||
|
|
||||||
|
with (
|
||||||
|
patch(
|
||||||
|
"homeassistant.components.wyoming.data.load_wyoming_info",
|
||||||
|
return_value=SATELLITE_INFO,
|
||||||
|
),
|
||||||
|
patch(
|
||||||
|
"homeassistant.components.wyoming.satellite.AsyncTcpClient",
|
||||||
|
SatelliteAsyncTcpClient([]),
|
||||||
|
) as mock_client,
|
||||||
|
):
|
||||||
|
entry = await setup_config_entry(hass)
|
||||||
|
device: SatelliteDevice = hass.data[wyoming.DOMAIN][
|
||||||
|
entry.entry_id
|
||||||
|
].satellite.device
|
||||||
|
|
||||||
|
async with asyncio.timeout(1):
|
||||||
|
await mock_client.connect_event.wait()
|
||||||
|
await mock_client.run_satellite_event.wait()
|
||||||
|
|
||||||
|
# Start timer
|
||||||
|
result = await intent_helper.async_handle(
|
||||||
|
hass,
|
||||||
|
"test",
|
||||||
|
intent_helper.INTENT_START_TIMER,
|
||||||
|
{
|
||||||
|
"name": {"value": "test timer"},
|
||||||
|
"hours": {"value": 1},
|
||||||
|
"minutes": {"value": 2},
|
||||||
|
"seconds": {"value": 3},
|
||||||
|
},
|
||||||
|
device_id=device.device_id,
|
||||||
|
)
|
||||||
|
|
||||||
|
assert result.response_type == intent_helper.IntentResponseType.ACTION_DONE
|
||||||
|
async with asyncio.timeout(1):
|
||||||
|
await mock_client.timer_started_event.wait()
|
||||||
|
timer_started = mock_client.timer_started
|
||||||
|
assert timer_started is not None
|
||||||
|
assert timer_started.id
|
||||||
|
assert timer_started.name == "test timer"
|
||||||
|
assert timer_started.start_hours == 1
|
||||||
|
assert timer_started.start_minutes == 2
|
||||||
|
assert timer_started.start_seconds == 3
|
||||||
|
assert timer_started.total_seconds == (1 * 60 * 60) + (2 * 60) + 3
|
||||||
|
|
||||||
|
# Pause
|
||||||
|
mock_client.timer_updated_event.clear()
|
||||||
|
result = await intent_helper.async_handle(
|
||||||
|
hass,
|
||||||
|
"test",
|
||||||
|
intent_helper.INTENT_PAUSE_TIMER,
|
||||||
|
{},
|
||||||
|
device_id=device.device_id,
|
||||||
|
)
|
||||||
|
|
||||||
|
assert result.response_type == intent_helper.IntentResponseType.ACTION_DONE
|
||||||
|
async with asyncio.timeout(1):
|
||||||
|
await mock_client.timer_updated_event.wait()
|
||||||
|
timer_updated = mock_client.timer_updated
|
||||||
|
assert timer_updated is not None
|
||||||
|
assert timer_updated.id == timer_started.id
|
||||||
|
assert not timer_updated.is_active
|
||||||
|
|
||||||
|
# Resume
|
||||||
|
mock_client.timer_updated_event.clear()
|
||||||
|
result = await intent_helper.async_handle(
|
||||||
|
hass,
|
||||||
|
"test",
|
||||||
|
intent_helper.INTENT_UNPAUSE_TIMER,
|
||||||
|
{},
|
||||||
|
device_id=device.device_id,
|
||||||
|
)
|
||||||
|
|
||||||
|
assert result.response_type == intent_helper.IntentResponseType.ACTION_DONE
|
||||||
|
async with asyncio.timeout(1):
|
||||||
|
await mock_client.timer_updated_event.wait()
|
||||||
|
timer_updated = mock_client.timer_updated
|
||||||
|
assert timer_updated is not None
|
||||||
|
assert timer_updated.id == timer_started.id
|
||||||
|
assert timer_updated.is_active
|
||||||
|
|
||||||
|
# Add time
|
||||||
|
mock_client.timer_updated_event.clear()
|
||||||
|
result = await intent_helper.async_handle(
|
||||||
|
hass,
|
||||||
|
"test",
|
||||||
|
intent_helper.INTENT_INCREASE_TIMER,
|
||||||
|
{
|
||||||
|
"hours": {"value": 2},
|
||||||
|
"minutes": {"value": 3},
|
||||||
|
"seconds": {"value": 4},
|
||||||
|
},
|
||||||
|
device_id=device.device_id,
|
||||||
|
)
|
||||||
|
|
||||||
|
assert result.response_type == intent_helper.IntentResponseType.ACTION_DONE
|
||||||
|
async with asyncio.timeout(1):
|
||||||
|
await mock_client.timer_updated_event.wait()
|
||||||
|
timer_updated = mock_client.timer_updated
|
||||||
|
assert timer_updated is not None
|
||||||
|
assert timer_updated.id == timer_started.id
|
||||||
|
assert timer_updated.total_seconds > timer_started.total_seconds
|
||||||
|
|
||||||
|
# Remove time
|
||||||
|
mock_client.timer_updated_event.clear()
|
||||||
|
result = await intent_helper.async_handle(
|
||||||
|
hass,
|
||||||
|
"test",
|
||||||
|
intent_helper.INTENT_DECREASE_TIMER,
|
||||||
|
{
|
||||||
|
"hours": {"value": 2},
|
||||||
|
"minutes": {"value": 3},
|
||||||
|
"seconds": {"value": 5}, # remove 1 extra second
|
||||||
|
},
|
||||||
|
device_id=device.device_id,
|
||||||
|
)
|
||||||
|
|
||||||
|
assert result.response_type == intent_helper.IntentResponseType.ACTION_DONE
|
||||||
|
async with asyncio.timeout(1):
|
||||||
|
await mock_client.timer_updated_event.wait()
|
||||||
|
timer_updated = mock_client.timer_updated
|
||||||
|
assert timer_updated is not None
|
||||||
|
assert timer_updated.id == timer_started.id
|
||||||
|
assert timer_updated.total_seconds < timer_started.total_seconds
|
||||||
|
|
||||||
|
# Cancel
|
||||||
|
result = await intent_helper.async_handle(
|
||||||
|
hass,
|
||||||
|
"test",
|
||||||
|
intent_helper.INTENT_CANCEL_TIMER,
|
||||||
|
{},
|
||||||
|
device_id=device.device_id,
|
||||||
|
)
|
||||||
|
|
||||||
|
assert result.response_type == intent_helper.IntentResponseType.ACTION_DONE
|
||||||
|
async with asyncio.timeout(1):
|
||||||
|
await mock_client.timer_cancelled_event.wait()
|
||||||
|
timer_cancelled = mock_client.timer_cancelled
|
||||||
|
assert timer_cancelled is not None
|
||||||
|
assert timer_cancelled.id == timer_started.id
|
||||||
|
|
||||||
|
# Start a new timer
|
||||||
|
mock_client.timer_started_event.clear()
|
||||||
|
result = await intent_helper.async_handle(
|
||||||
|
hass,
|
||||||
|
"test",
|
||||||
|
intent_helper.INTENT_START_TIMER,
|
||||||
|
{
|
||||||
|
"name": {"value": "test timer"},
|
||||||
|
"minutes": {"value": 1},
|
||||||
|
},
|
||||||
|
device_id=device.device_id,
|
||||||
|
)
|
||||||
|
|
||||||
|
assert result.response_type == intent_helper.IntentResponseType.ACTION_DONE
|
||||||
|
async with asyncio.timeout(1):
|
||||||
|
await mock_client.timer_started_event.wait()
|
||||||
|
timer_started = mock_client.timer_started
|
||||||
|
assert timer_started is not None
|
||||||
|
|
||||||
|
# Finished
|
||||||
|
result = await intent_helper.async_handle(
|
||||||
|
hass,
|
||||||
|
"test",
|
||||||
|
intent_helper.INTENT_DECREASE_TIMER,
|
||||||
|
{
|
||||||
|
"minutes": {"value": 1}, # force finish
|
||||||
|
},
|
||||||
|
device_id=device.device_id,
|
||||||
|
)
|
||||||
|
|
||||||
|
assert result.response_type == intent_helper.IntentResponseType.ACTION_DONE
|
||||||
|
async with asyncio.timeout(1):
|
||||||
|
await mock_client.timer_finished_event.wait()
|
||||||
|
timer_finished = mock_client.timer_finished
|
||||||
|
assert timer_finished is not None
|
||||||
|
assert timer_finished.id == timer_started.id
|
||||||
|
Loading…
x
Reference in New Issue
Block a user