mirror of
https://github.com/home-assistant/core.git
synced 2025-07-21 12:17:07 +00:00
Add announce support to ESPHome Assist Satellite platform (#125157)
Rebuild
This commit is contained in:
parent
056e6eae82
commit
1dc496a2dd
@ -74,6 +74,8 @@ _TIMER_EVENT_TYPES: EsphomeEnumMapper[VoiceAssistantTimerEventType, TimerEventTy
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
_ANNOUNCEMENT_TIMEOUT_SEC = 5 * 60 # 5 minutes
|
||||||
|
|
||||||
|
|
||||||
async def async_setup_entry(
|
async def async_setup_entry(
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
@ -183,6 +185,12 @@ class EsphomeAssistSatellite(
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if feature_flags & VoiceAssistantFeature.ANNOUNCE:
|
||||||
|
# Device supports announcements
|
||||||
|
self._attr_supported_features |= (
|
||||||
|
assist_satellite.AssistSatelliteEntityFeature.ANNOUNCE
|
||||||
|
)
|
||||||
|
|
||||||
async def async_will_remove_from_hass(self) -> None:
|
async def async_will_remove_from_hass(self) -> None:
|
||||||
"""Run when entity will be removed from hass."""
|
"""Run when entity will be removed from hass."""
|
||||||
await super().async_will_remove_from_hass()
|
await super().async_will_remove_from_hass()
|
||||||
@ -251,6 +259,20 @@ class EsphomeAssistSatellite(
|
|||||||
|
|
||||||
self.cli.send_voice_assistant_event(event_type, data_to_send)
|
self.cli.send_voice_assistant_event(event_type, data_to_send)
|
||||||
|
|
||||||
|
async def async_announce(self, message: str, media_id: str) -> None:
|
||||||
|
"""Announce media on the satellite.
|
||||||
|
|
||||||
|
Should block until the announcement is done playing.
|
||||||
|
"""
|
||||||
|
_LOGGER.debug(
|
||||||
|
"Waiting for announcement to finished (message=%s, media_id=%s)",
|
||||||
|
message,
|
||||||
|
media_id,
|
||||||
|
)
|
||||||
|
await self.cli.send_voice_assistant_announcement_await_response(
|
||||||
|
media_id, _ANNOUNCEMENT_TIMEOUT_SEC, message
|
||||||
|
)
|
||||||
|
|
||||||
async def handle_pipeline_start(
|
async def handle_pipeline_start(
|
||||||
self,
|
self,
|
||||||
conversation_id: str,
|
conversation_id: str,
|
||||||
|
@ -27,6 +27,7 @@ from homeassistant.components import assist_satellite, tts
|
|||||||
from homeassistant.components.assist_pipeline import PipelineEvent, PipelineEventType
|
from homeassistant.components.assist_pipeline import PipelineEvent, PipelineEventType
|
||||||
from homeassistant.components.assist_satellite.entity import (
|
from homeassistant.components.assist_satellite.entity import (
|
||||||
AssistSatelliteEntity,
|
AssistSatelliteEntity,
|
||||||
|
AssistSatelliteEntityFeature,
|
||||||
AssistSatelliteState,
|
AssistSatelliteState,
|
||||||
)
|
)
|
||||||
from homeassistant.components.esphome import DOMAIN
|
from homeassistant.components.esphome import DOMAIN
|
||||||
@ -34,6 +35,7 @@ from homeassistant.components.esphome.assist_satellite import (
|
|||||||
EsphomeAssistSatellite,
|
EsphomeAssistSatellite,
|
||||||
VoiceAssistantUDPServer,
|
VoiceAssistantUDPServer,
|
||||||
)
|
)
|
||||||
|
from homeassistant.components.media_source import PlayMedia
|
||||||
from homeassistant.const import Platform
|
from homeassistant.const import Platform
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
from homeassistant.helpers import entity_registry as er, intent as intent_helper
|
from homeassistant.helpers import entity_registry as er, intent as intent_helper
|
||||||
@ -891,3 +893,148 @@ async def test_tts_format_from_media_player(
|
|||||||
tts.ATTR_PREFERRED_SAMPLE_CHANNELS: 1,
|
tts.ATTR_PREFERRED_SAMPLE_CHANNELS: 1,
|
||||||
tts.ATTR_PREFERRED_SAMPLE_BYTES: 2,
|
tts.ATTR_PREFERRED_SAMPLE_BYTES: 2,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
async def test_announce_supported_features(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
mock_client: APIClient,
|
||||||
|
mock_esphome_device: Callable[
|
||||||
|
[APIClient, list[EntityInfo], list[UserService], list[EntityState]],
|
||||||
|
Awaitable[MockESPHomeDevice],
|
||||||
|
],
|
||||||
|
) -> None:
|
||||||
|
"""Test that the announce supported feature is set by flags."""
|
||||||
|
mock_device: MockESPHomeDevice = await mock_esphome_device(
|
||||||
|
mock_client=mock_client,
|
||||||
|
entity_info=[],
|
||||||
|
user_service=[],
|
||||||
|
states=[],
|
||||||
|
device_info={
|
||||||
|
"voice_assistant_feature_flags": VoiceAssistantFeature.VOICE_ASSISTANT
|
||||||
|
},
|
||||||
|
)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
satellite = get_satellite_entity(hass, mock_device.device_info.mac_address)
|
||||||
|
assert satellite is not None
|
||||||
|
|
||||||
|
assert not (satellite.supported_features & AssistSatelliteEntityFeature.ANNOUNCE)
|
||||||
|
|
||||||
|
|
||||||
|
async def test_announce_message(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
mock_client: APIClient,
|
||||||
|
mock_esphome_device: Callable[
|
||||||
|
[APIClient, list[EntityInfo], list[UserService], list[EntityState]],
|
||||||
|
Awaitable[MockESPHomeDevice],
|
||||||
|
],
|
||||||
|
) -> None:
|
||||||
|
"""Test announcement with message."""
|
||||||
|
mock_device: MockESPHomeDevice = await mock_esphome_device(
|
||||||
|
mock_client=mock_client,
|
||||||
|
entity_info=[],
|
||||||
|
user_service=[],
|
||||||
|
states=[],
|
||||||
|
device_info={
|
||||||
|
"voice_assistant_feature_flags": VoiceAssistantFeature.VOICE_ASSISTANT
|
||||||
|
| VoiceAssistantFeature.SPEAKER
|
||||||
|
| VoiceAssistantFeature.API_AUDIO
|
||||||
|
| VoiceAssistantFeature.ANNOUNCE
|
||||||
|
},
|
||||||
|
)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
satellite = get_satellite_entity(hass, mock_device.device_info.mac_address)
|
||||||
|
assert satellite is not None
|
||||||
|
|
||||||
|
done = asyncio.Event()
|
||||||
|
|
||||||
|
async def send_voice_assistant_announcement_await_response(
|
||||||
|
media_id: str, timeout: float, text: str
|
||||||
|
):
|
||||||
|
assert media_id == "https://www.home-assistant.io/resolved.mp3"
|
||||||
|
assert text == "test-text"
|
||||||
|
|
||||||
|
done.set()
|
||||||
|
|
||||||
|
with (
|
||||||
|
patch(
|
||||||
|
"homeassistant.components.assist_satellite.entity.tts_generate_media_source_id",
|
||||||
|
return_value="media-source://bla",
|
||||||
|
),
|
||||||
|
patch(
|
||||||
|
"homeassistant.components.media_source.async_resolve_media",
|
||||||
|
return_value=PlayMedia(
|
||||||
|
url="https://www.home-assistant.io/resolved.mp3",
|
||||||
|
mime_type="audio/mp3",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
patch.object(
|
||||||
|
mock_client,
|
||||||
|
"send_voice_assistant_announcement_await_response",
|
||||||
|
new=send_voice_assistant_announcement_await_response,
|
||||||
|
),
|
||||||
|
):
|
||||||
|
async with asyncio.timeout(1):
|
||||||
|
await hass.services.async_call(
|
||||||
|
assist_satellite.DOMAIN,
|
||||||
|
"announce",
|
||||||
|
{"entity_id": satellite.entity_id, "message": "test-text"},
|
||||||
|
blocking=True,
|
||||||
|
)
|
||||||
|
await done.wait()
|
||||||
|
|
||||||
|
|
||||||
|
async def test_announce_media_id(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
mock_client: APIClient,
|
||||||
|
mock_esphome_device: Callable[
|
||||||
|
[APIClient, list[EntityInfo], list[UserService], list[EntityState]],
|
||||||
|
Awaitable[MockESPHomeDevice],
|
||||||
|
],
|
||||||
|
) -> None:
|
||||||
|
"""Test announcement with media id."""
|
||||||
|
mock_device: MockESPHomeDevice = await mock_esphome_device(
|
||||||
|
mock_client=mock_client,
|
||||||
|
entity_info=[],
|
||||||
|
user_service=[],
|
||||||
|
states=[],
|
||||||
|
device_info={
|
||||||
|
"voice_assistant_feature_flags": VoiceAssistantFeature.VOICE_ASSISTANT
|
||||||
|
| VoiceAssistantFeature.SPEAKER
|
||||||
|
| VoiceAssistantFeature.API_AUDIO
|
||||||
|
| VoiceAssistantFeature.ANNOUNCE
|
||||||
|
},
|
||||||
|
)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
satellite = get_satellite_entity(hass, mock_device.device_info.mac_address)
|
||||||
|
assert satellite is not None
|
||||||
|
|
||||||
|
done = asyncio.Event()
|
||||||
|
|
||||||
|
async def send_voice_assistant_announcement_await_response(
|
||||||
|
media_id: str, timeout: float, text: str
|
||||||
|
):
|
||||||
|
assert media_id == "https://www.home-assistant.io/resolved.mp3"
|
||||||
|
|
||||||
|
done.set()
|
||||||
|
|
||||||
|
with (
|
||||||
|
patch.object(
|
||||||
|
mock_client,
|
||||||
|
"send_voice_assistant_announcement_await_response",
|
||||||
|
new=send_voice_assistant_announcement_await_response,
|
||||||
|
),
|
||||||
|
):
|
||||||
|
async with asyncio.timeout(1):
|
||||||
|
await hass.services.async_call(
|
||||||
|
assist_satellite.DOMAIN,
|
||||||
|
"announce",
|
||||||
|
{
|
||||||
|
"entity_id": satellite.entity_id,
|
||||||
|
"media_id": "https://www.home-assistant.io/resolved.mp3",
|
||||||
|
},
|
||||||
|
blocking=True,
|
||||||
|
)
|
||||||
|
await done.wait()
|
||||||
|
Loading…
x
Reference in New Issue
Block a user