Add preannounce_media_id to Assist Satellite (#141317)

Add preannounce_media_id
This commit is contained in:
Michael Hansen 2025-03-24 16:04:13 -05:00 committed by GitHub
parent 61a76b4064
commit 33198cd704
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 83 additions and 4 deletions

View File

@ -56,6 +56,7 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
{ {
vol.Optional("message"): str, vol.Optional("message"): str,
vol.Optional("media_id"): str, vol.Optional("media_id"): str,
vol.Optional("preannounce_media_id"): str,
} }
), ),
cv.has_at_least_one_key("message", "media_id"), cv.has_at_least_one_key("message", "media_id"),
@ -70,6 +71,7 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
{ {
vol.Optional("start_message"): str, vol.Optional("start_message"): str,
vol.Optional("start_media_id"): str, vol.Optional("start_media_id"): str,
vol.Optional("preannounce_media_id"): str,
vol.Optional("extra_system_prompt"): str, vol.Optional("extra_system_prompt"): str,
} }
), ),

View File

@ -101,6 +101,9 @@ class AssistSatelliteAnnouncement:
media_id_source: Literal["url", "media_id", "tts"] media_id_source: Literal["url", "media_id", "tts"]
"""Source of the media ID.""" """Source of the media ID."""
preannounce_media_id: str | None = None
"""Media ID to be played before announcement."""
class AssistSatelliteEntity(entity.Entity): class AssistSatelliteEntity(entity.Entity):
"""Entity encapsulating the state and functionality of an Assist satellite.""" """Entity encapsulating the state and functionality of an Assist satellite."""
@ -177,6 +180,7 @@ class AssistSatelliteEntity(entity.Entity):
self, self,
message: str | None = None, message: str | None = None,
media_id: str | None = None, media_id: str | None = None,
preannounce_media_id: str | None = None,
) -> None: ) -> None:
"""Play and show an announcement on the satellite. """Play and show an announcement on the satellite.
@ -186,6 +190,8 @@ class AssistSatelliteEntity(entity.Entity):
If media_id is provided, it is played directly. It is possible If media_id is provided, it is played directly. It is possible
to omit the message and the satellite will not show any text. to omit the message and the satellite will not show any text.
If preannounce_media_id is provided, it is played before the announcement.
Calls async_announce with message and media id. Calls async_announce with message and media id.
""" """
await self._cancel_running_pipeline() await self._cancel_running_pipeline()
@ -193,7 +199,9 @@ class AssistSatelliteEntity(entity.Entity):
if message is None: if message is None:
message = "" message = ""
announcement = await self._resolve_announcement_media_id(message, media_id) announcement = await self._resolve_announcement_media_id(
message, media_id, preannounce_media_id
)
if self._is_announcing: if self._is_announcing:
raise SatelliteBusyError raise SatelliteBusyError
@ -220,6 +228,7 @@ class AssistSatelliteEntity(entity.Entity):
start_message: str | None = None, start_message: str | None = None,
start_media_id: str | None = None, start_media_id: str | None = None,
extra_system_prompt: str | None = None, extra_system_prompt: str | None = None,
preannounce_media_id: str | None = None,
) -> None: ) -> None:
"""Start a conversation from the satellite. """Start a conversation from the satellite.
@ -229,6 +238,8 @@ class AssistSatelliteEntity(entity.Entity):
If start_media_id is provided, it is played directly. It is possible If start_media_id is provided, it is played directly. It is possible
to omit the message and the satellite will not show any text. to omit the message and the satellite will not show any text.
If preannounce_media_id is provided, it is played before the announcement.
Calls async_start_conversation. Calls async_start_conversation.
""" """
await self._cancel_running_pipeline() await self._cancel_running_pipeline()
@ -244,7 +255,7 @@ class AssistSatelliteEntity(entity.Entity):
start_message = "" start_message = ""
announcement = await self._resolve_announcement_media_id( announcement = await self._resolve_announcement_media_id(
start_message, start_media_id start_message, start_media_id, preannounce_media_id
) )
if self._is_announcing: if self._is_announcing:
@ -470,7 +481,10 @@ class AssistSatelliteEntity(entity.Entity):
return vad.VadSensitivity.to_seconds(vad_sensitivity) return vad.VadSensitivity.to_seconds(vad_sensitivity)
async def _resolve_announcement_media_id( async def _resolve_announcement_media_id(
self, message: str, media_id: str | None self,
message: str,
media_id: str | None,
preannounce_media_id: str | None = None,
) -> AssistSatelliteAnnouncement: ) -> AssistSatelliteAnnouncement:
"""Resolve the media ID.""" """Resolve the media ID."""
media_id_source: Literal["url", "media_id", "tts"] | None = None media_id_source: Literal["url", "media_id", "tts"] | None = None
@ -478,7 +492,6 @@ class AssistSatelliteEntity(entity.Entity):
if media_id: if media_id:
original_media_id = media_id original_media_id = media_id
else: else:
media_id_source = "tts" media_id_source = "tts"
# Synthesize audio and get URL # Synthesize audio and get URL
@ -530,10 +543,26 @@ class AssistSatelliteEntity(entity.Entity):
# Resolve to full URL # Resolve to full URL
media_id = async_process_play_media_url(self.hass, media_id) media_id = async_process_play_media_url(self.hass, media_id)
# Resolve preannounce media id
if preannounce_media_id:
if media_source.is_media_source_id(preannounce_media_id):
preannounce_media = await media_source.async_resolve_media(
self.hass,
preannounce_media_id,
None,
)
preannounce_media_id = preannounce_media.url
# Resolve to full URL
preannounce_media_id = async_process_play_media_url(
self.hass, preannounce_media_id
)
return AssistSatelliteAnnouncement( return AssistSatelliteAnnouncement(
message=message, message=message,
media_id=media_id, media_id=media_id,
original_media_id=original_media_id, original_media_id=original_media_id,
tts_token=tts_token, tts_token=tts_token,
media_id_source=media_id_source, media_id_source=media_id_source,
preannounce_media_id=preannounce_media_id,
) )

View File

@ -14,6 +14,10 @@ announce:
required: false required: false
selector: selector:
text: text:
preannounce_media_id:
required: false
selector:
text:
start_conversation: start_conversation:
target: target:
entity: entity:
@ -34,3 +38,7 @@ start_conversation:
required: false required: false
selector: selector:
text: text:
preannounce_media_id:
required: false
selector:
text:

View File

@ -23,6 +23,10 @@
"media_id": { "media_id": {
"name": "Media ID", "name": "Media ID",
"description": "The media ID to announce instead of using text-to-speech." "description": "The media ID to announce instead of using text-to-speech."
},
"preannounce_media_id": {
"name": "Preannounce Media ID",
"description": "The media ID to play before the announcement."
} }
} }
}, },
@ -41,6 +45,10 @@
"extra_system_prompt": { "extra_system_prompt": {
"name": "Extra system prompt", "name": "Extra system prompt",
"description": "Provide background information to the AI about the request." "description": "Provide background information to the AI about the request."
},
"preannounce_media_id": {
"name": "Preannounce Media ID",
"description": "The media ID to play before the start message or media."
} }
} }
} }

View File

@ -217,6 +217,20 @@ async def test_new_pipeline_cancels_pipeline(
media_id_source="url", media_id_source="url",
), ),
), ),
(
{
"media_id": "http://example.com/bla.mp3",
"preannounce_media_id": "http://example.com/preannounce.mp3",
},
AssistSatelliteAnnouncement(
message="",
media_id="http://example.com/bla.mp3",
original_media_id="http://example.com/bla.mp3",
tts_token=None,
media_id_source="url",
preannounce_media_id="http://example.com/preannounce.mp3",
),
),
], ],
) )
async def test_announce( async def test_announce(
@ -551,6 +565,24 @@ async def test_vad_sensitivity_entity_not_found(
), ),
), ),
), ),
(
{
"start_media_id": "http://example.com/given.mp3",
"preannounce_media_id": "http://example.com/preannounce.mp3",
},
(
"mock-conversation-id",
None,
AssistSatelliteAnnouncement(
message="",
media_id="http://example.com/given.mp3",
tts_token=None,
original_media_id="http://example.com/given.mp3",
media_id_source="url",
preannounce_media_id="http://example.com/preannounce.mp3",
),
),
),
], ],
) )
@pytest.mark.usefixtures("mock_chat_session_conversation_id") @pytest.mark.usefixtures("mock_chat_session_conversation_id")