Add timer support to VoIP (#139763)

pull/139766/head
Paulus Schoutsen 2025-03-04 09:48:10 -05:00 committed by GitHub
parent 7fb949dff7
commit c51a2317e1
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 96 additions and 2 deletions

View File

@ -3,6 +3,7 @@
from __future__ import annotations
import asyncio
from datetime import timedelta
from enum import IntFlag
from functools import partial
import io
@ -16,7 +17,7 @@ import wave
from voip_utils import SIP_PORT, RtpDatagramProtocol
from voip_utils.sip import SipDatagramProtocol, SipEndpoint, get_sip_endpoint
from homeassistant.components import tts
from homeassistant.components import intent, tts
from homeassistant.components.assist_pipeline import PipelineEvent, PipelineEventType
from homeassistant.components.assist_satellite import (
AssistSatelliteAnnouncement,
@ -25,6 +26,7 @@ from homeassistant.components.assist_satellite import (
AssistSatelliteEntityDescription,
AssistSatelliteEntityFeature,
)
from homeassistant.components.intent import TimerEventType, TimerInfo
from homeassistant.components.network import async_get_source_ip
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import Context, HomeAssistant, callback
@ -161,6 +163,13 @@ class VoipAssistSatellite(VoIPEntity, AssistSatelliteEntity, RtpDatagramProtocol
await super().async_added_to_hass()
self.voip_device.protocol = self
assert self.device_entry is not None
self.async_on_remove(
intent.async_register_timer_handler(
self.hass, self.device_entry.id, self.async_handle_timer_event
)
)
async def async_will_remove_from_hass(self) -> None:
"""Run when entity will be removed from hass."""
await super().async_will_remove_from_hass()
@ -174,6 +183,29 @@ class VoipAssistSatellite(VoIPEntity, AssistSatelliteEntity, RtpDatagramProtocol
"""Get the current satellite configuration."""
raise NotImplementedError
@callback
def async_handle_timer_event(
self,
event_type: TimerEventType,
timer_info: TimerInfo,
) -> None:
"""Handle timer event."""
if event_type != TimerEventType.FINISHED:
return
if timer_info.name:
message = f"{timer_info.name} finished"
else:
message = f"{timedelta(seconds=timer_info.created_seconds)} timer finished"
async def announce_message():
announcement = await self._resolve_announcement_media_id(message, None)
await self.async_announce(announcement)
self.config_entry.async_create_background_task(
self.hass, announce_message(), "voip_announce_timer"
)
async def async_set_configuration(
self, config: AssistSatelliteConfiguration
) -> None:

View File

@ -3,7 +3,7 @@
"name": "Voice over IP",
"codeowners": ["@balloob", "@synesthesiam"],
"config_flow": true,
"dependencies": ["assist_pipeline", "assist_satellite", "network"],
"dependencies": ["assist_pipeline", "assist_satellite", "intent", "network"],
"documentation": "https://www.home-assistant.io/integrations/voip",
"iot_class": "local_push",
"quality_scale": "internal",

View File

@ -0,0 +1,62 @@
"""Test the Assist Satellite platform."""
from unittest.mock import patch
import pytest
from homeassistant.components.voip.devices import VoIPDevice
from homeassistant.core import HomeAssistant
from homeassistant.helpers import intent as intent_helper
@pytest.mark.parametrize(
("intent_args", "message"),
[
(
{},
"0:02:00 timer finished",
),
(
{"name": {"value": "pizza"}},
"pizza finished",
),
],
)
async def test_timer_events(
hass: HomeAssistant, voip_device: VoIPDevice, intent_args: dict, message: str
) -> None:
"""Test for timer events."""
await intent_helper.async_handle(
hass,
"test",
intent_helper.INTENT_START_TIMER,
{
"minutes": {"value": 2},
}
| intent_args,
device_id=voip_device.device_id,
)
with (
patch(
"homeassistant.components.voip.assist_satellite.VoipAssistSatellite._resolve_announcement_media_id",
) as mock_resolve,
patch(
"homeassistant.components.voip.assist_satellite.VoipAssistSatellite.async_announce",
) as mock_announce,
):
await intent_helper.async_handle(
hass,
"test",
intent_helper.INTENT_DECREASE_TIMER,
{
"minutes": {"value": 2},
},
device_id=voip_device.device_id,
)
await hass.async_block_till_done(wait_background_tasks=True)
assert len(mock_resolve.mock_calls) == 1
assert len(mock_announce.mock_calls) == 1
assert mock_resolve.mock_calls[0][1][0] == message