From 5eb1d0a28e8c65479da6f86a2efbae4797e28e2f Mon Sep 17 00:00:00 2001 From: Michael Hansen Date: Wed, 26 Mar 2025 22:45:28 -0500 Subject: [PATCH] Add default preannounce sound to Assist satellites (#141522) * Add default preannounce sound * Allow None to disable sound * Register static path instead of HTTP view * Fix path --------- Co-authored-by: Paulus Schoutsen --- .../components/assist_satellite/__init__.py | 17 +++++- .../components/assist_satellite/const.py | 3 + .../components/assist_satellite/entity.py | 10 +-- .../assist_satellite/preannounce.mp3 | Bin 0 -> 17265 bytes .../components/media_player/browse_media.py | 6 +- .../assist_satellite/test_entity.py | 57 +++++++++++++++++- .../esphome/test_assist_satellite.py | 14 ++++- 7 files changed, 95 insertions(+), 12 deletions(-) create mode 100644 homeassistant/components/assist_satellite/preannounce.mp3 diff --git a/homeassistant/components/assist_satellite/__init__.py b/homeassistant/components/assist_satellite/__init__.py index 31afbda1d11..bc2157b10b2 100644 --- a/homeassistant/components/assist_satellite/__init__.py +++ b/homeassistant/components/assist_satellite/__init__.py @@ -1,9 +1,11 @@ """Base class for assist satellite entities.""" import logging +from pathlib import Path import voluptuous as vol +from homeassistant.components.http import StaticPathConfig from homeassistant.config_entries import ConfigEntry from homeassistant.core import HomeAssistant from homeassistant.helpers import config_validation as cv @@ -15,6 +17,8 @@ from .const import ( CONNECTION_TEST_DATA, DATA_COMPONENT, DOMAIN, + PREANNOUNCE_FILENAME, + PREANNOUNCE_URL, AssistSatelliteEntityFeature, ) from .entity import ( @@ -56,7 +60,7 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: { vol.Optional("message"): str, vol.Optional("media_id"): str, - vol.Optional("preannounce_media_id"): str, + vol.Optional("preannounce_media_id"): vol.Any(str, None), } ), cv.has_at_least_one_key("message", "media_id"), @@ -71,7 +75,7 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: { vol.Optional("start_message"): str, vol.Optional("start_media_id"): str, - vol.Optional("preannounce_media_id"): str, + vol.Optional("preannounce_media_id"): vol.Any(str, None), vol.Optional("extra_system_prompt"): str, } ), @@ -84,6 +88,15 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: async_register_websocket_api(hass) hass.http.register_view(ConnectionTestView()) + # Default preannounce sound + await hass.http.async_register_static_paths( + [ + StaticPathConfig( + PREANNOUNCE_URL, str(Path(__file__).parent / PREANNOUNCE_FILENAME) + ) + ] + ) + return True diff --git a/homeassistant/components/assist_satellite/const.py b/homeassistant/components/assist_satellite/const.py index f7ac7e524b4..7fca88f3b12 100644 --- a/homeassistant/components/assist_satellite/const.py +++ b/homeassistant/components/assist_satellite/const.py @@ -20,6 +20,9 @@ CONNECTION_TEST_DATA: HassKey[dict[str, asyncio.Event]] = HassKey( f"{DOMAIN}_connection_tests" ) +PREANNOUNCE_FILENAME = "preannounce.mp3" +PREANNOUNCE_URL = f"/api/assist_satellite/static/{PREANNOUNCE_FILENAME}" + class AssistSatelliteEntityFeature(IntFlag): """Supported features of Assist satellite entity.""" diff --git a/homeassistant/components/assist_satellite/entity.py b/homeassistant/components/assist_satellite/entity.py index 450e6cadbc9..7b4c1b92d8c 100644 --- a/homeassistant/components/assist_satellite/entity.py +++ b/homeassistant/components/assist_satellite/entity.py @@ -28,7 +28,7 @@ from homeassistant.exceptions import HomeAssistantError from homeassistant.helpers import chat_session, entity from homeassistant.helpers.entity import EntityDescription -from .const import AssistSatelliteEntityFeature +from .const import PREANNOUNCE_URL, AssistSatelliteEntityFeature from .errors import AssistSatelliteError, SatelliteBusyError _LOGGER = logging.getLogger(__name__) @@ -180,7 +180,7 @@ class AssistSatelliteEntity(entity.Entity): self, message: str | None = None, media_id: str | None = None, - preannounce_media_id: str | None = None, + preannounce_media_id: str | None = PREANNOUNCE_URL, ) -> None: """Play and show an announcement on the satellite. @@ -190,7 +190,8 @@ class AssistSatelliteEntity(entity.Entity): If media_id is provided, it is played directly. It is possible to omit the message and the satellite will not show any text. - If preannounce_media_id is provided, it is played before the announcement. + If preannounce_media_id is provided, it overrides the default sound. + If preannounce_media_id is None, no sound is played. Calls async_announce with message and media id. """ @@ -228,7 +229,7 @@ class AssistSatelliteEntity(entity.Entity): start_message: str | None = None, start_media_id: str | None = None, extra_system_prompt: str | None = None, - preannounce_media_id: str | None = None, + preannounce_media_id: str | None = PREANNOUNCE_URL, ) -> None: """Start a conversation from the satellite. @@ -239,6 +240,7 @@ class AssistSatelliteEntity(entity.Entity): to omit the message and the satellite will not show any text. If preannounce_media_id is provided, it is played before the announcement. + If preannounce_media_id is None, no sound is played. Calls async_start_conversation. """ diff --git a/homeassistant/components/assist_satellite/preannounce.mp3 b/homeassistant/components/assist_satellite/preannounce.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..6e2fa0aba3e22e797c76a5866ea4e9aa6073e860 GIT binary patch literal 17265 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 TBx*t&-5F>K(9gd>wd_9th%$Od literal 0 HcmV?d00001 diff --git a/homeassistant/components/media_player/browse_media.py b/homeassistant/components/media_player/browse_media.py index c917164a2ee..d234050c1b2 100644 --- a/homeassistant/components/media_player/browse_media.py +++ b/homeassistant/components/media_player/browse_media.py @@ -23,7 +23,11 @@ from homeassistant.helpers.network import ( from .const import CONTENT_AUTH_EXPIRY_TIME, MediaClass, MediaType # Paths that we don't need to sign -PATHS_WITHOUT_AUTH = ("/api/tts_proxy/", "/api/esphome/ffmpeg_proxy/") +PATHS_WITHOUT_AUTH = ( + "/api/tts_proxy/", + "/api/esphome/ffmpeg_proxy/", + "/api/assist_satellite/static/", +) @callback diff --git a/tests/components/assist_satellite/test_entity.py b/tests/components/assist_satellite/test_entity.py index b9f6da6f96c..2b1cc78943f 100644 --- a/tests/components/assist_satellite/test_entity.py +++ b/tests/components/assist_satellite/test_entity.py @@ -22,6 +22,7 @@ from homeassistant.components.assist_satellite import ( AssistSatelliteAnnouncement, SatelliteBusyError, ) +from homeassistant.components.assist_satellite.const import PREANNOUNCE_URL from homeassistant.components.assist_satellite.entity import AssistSatelliteState from homeassistant.components.media_source import PlayMedia from homeassistant.config_entries import ConfigEntry @@ -185,7 +186,7 @@ async def test_new_pipeline_cancels_pipeline( ("service_data", "expected_params"), [ ( - {"message": "Hello"}, + {"message": "Hello", "preannounce_media_id": None}, AssistSatelliteAnnouncement( message="Hello", media_id="http://10.10.10.10:8123/api/tts_proxy/test-token", @@ -198,6 +199,7 @@ async def test_new_pipeline_cancels_pipeline( { "message": "Hello", "media_id": "media-source://given", + "preannounce_media_id": None, }, AssistSatelliteAnnouncement( message="Hello", @@ -208,7 +210,7 @@ async def test_new_pipeline_cancels_pipeline( ), ), ( - {"media_id": "http://example.com/bla.mp3"}, + {"media_id": "http://example.com/bla.mp3", "preannounce_media_id": None}, AssistSatelliteAnnouncement( message="", media_id="http://example.com/bla.mp3", @@ -368,6 +370,24 @@ async def test_announce_cancels_pipeline( mock_async_announce.assert_called_once() +async def test_announce_default_preannounce( + hass: HomeAssistant, init_components: ConfigEntry, entity: MockAssistSatellite +) -> None: + """Test announcing on a device with the default preannouncement sound.""" + + async def async_announce(announcement): + assert announcement.preannounce_media_id.endswith(PREANNOUNCE_URL) + + with patch.object(entity, "async_announce", new=async_announce): + await hass.services.async_call( + "assist_satellite", + "announce", + {"media_id": "test-media-id"}, + target={"entity_id": "assist_satellite.test_entity"}, + blocking=True, + ) + + async def test_context_refresh( hass: HomeAssistant, init_components: ConfigEntry, entity: MockAssistSatellite ) -> None: @@ -521,6 +541,7 @@ async def test_vad_sensitivity_entity_not_found( { "start_message": "Hello", "extra_system_prompt": "Better system prompt", + "preannounce_media_id": None, }, ( "mock-conversation-id", @@ -538,6 +559,7 @@ async def test_vad_sensitivity_entity_not_found( { "start_message": "Hello", "start_media_id": "media-source://given", + "preannounce_media_id": None, }, ( "mock-conversation-id", @@ -552,7 +574,10 @@ async def test_vad_sensitivity_entity_not_found( ), ), ( - {"start_media_id": "http://example.com/given.mp3"}, + { + "start_media_id": "http://example.com/given.mp3", + "preannounce_media_id": None, + }, ( "mock-conversation-id", None, @@ -657,6 +682,32 @@ async def test_start_conversation_reject_builtin_agent( ) +async def test_start_conversation_default_preannounce( + hass: HomeAssistant, init_components: ConfigEntry, entity: MockAssistSatellite +) -> None: + """Test starting a conversation on a device with the default preannouncement sound.""" + + async def async_start_conversation(start_announcement): + assert PREANNOUNCE_URL in start_announcement.preannounce_media_id + + await async_update_pipeline( + hass, + async_get_pipeline(hass), + conversation_engine="conversation.some_llm", + ) + + with ( + patch.object(entity, "async_start_conversation", new=async_start_conversation), + ): + await hass.services.async_call( + "assist_satellite", + "start_conversation", + {"start_media_id": "test-media-id"}, + target={"entity_id": "assist_satellite.test_entity"}, + blocking=True, + ) + + async def test_wake_word_start_keeps_responding( hass: HomeAssistant, init_components: ConfigEntry, entity: MockAssistSatellite ) -> None: diff --git a/tests/components/esphome/test_assist_satellite.py b/tests/components/esphome/test_assist_satellite.py index 7fc46e87503..5f433a6c0ed 100644 --- a/tests/components/esphome/test_assist_satellite.py +++ b/tests/components/esphome/test_assist_satellite.py @@ -1249,7 +1249,11 @@ async def test_announce_message( await hass.services.async_call( assist_satellite.DOMAIN, "announce", - {"entity_id": satellite.entity_id, "message": "test-text"}, + { + "entity_id": satellite.entity_id, + "message": "test-text", + "preannounce_media_id": None, + }, blocking=True, ) await done.wait() @@ -1338,6 +1342,7 @@ async def test_announce_media_id( { "entity_id": satellite.entity_id, "media_id": "https://www.home-assistant.io/resolved.mp3", + "preannounce_media_id": None, }, blocking=True, ) @@ -1545,7 +1550,11 @@ async def test_start_conversation_message( await hass.services.async_call( assist_satellite.DOMAIN, "start_conversation", - {"entity_id": satellite.entity_id, "start_message": "test-text"}, + { + "entity_id": satellite.entity_id, + "start_message": "test-text", + "preannounce_media_id": None, + }, blocking=True, ) await done.wait() @@ -1653,6 +1662,7 @@ async def test_start_conversation_media_id( { "entity_id": satellite.entity_id, "start_media_id": "https://www.home-assistant.io/resolved.mp3", + "preannounce_media_id": None, }, blocking=True, )