Add media class to media player search and play intent (#147097)

Co-authored-by: Michael Hansen <mike@rhasspy.org>
This commit is contained in:
Paulus Schoutsen 2025-06-23 17:12:32 -04:00 committed by GitHub
parent ab0ea753e9
commit 95abd69cc6
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 100 additions and 4 deletions

View File

@ -27,7 +27,12 @@ from . import (
MediaPlayerDeviceClass,
SearchMedia,
)
from .const import MediaPlayerEntityFeature, MediaPlayerState
from .const import (
ATTR_MEDIA_FILTER_CLASSES,
MediaClass,
MediaPlayerEntityFeature,
MediaPlayerState,
)
INTENT_MEDIA_PAUSE = "HassMediaPause"
INTENT_MEDIA_UNPAUSE = "HassMediaUnpause"
@ -231,6 +236,7 @@ class MediaSearchAndPlayHandler(intent.IntentHandler):
intent_type = INTENT_MEDIA_SEARCH_AND_PLAY
slot_schema = {
vol.Required("search_query"): cv.string,
vol.Optional("media_class"): vol.In([cls.value for cls in MediaClass]),
# Optional name/area/floor slots handled by intent matcher
vol.Optional("name"): cv.string,
vol.Optional("area"): cv.string,
@ -285,14 +291,23 @@ class MediaSearchAndPlayHandler(intent.IntentHandler):
target_entity = match_result.states[0]
target_entity_id = target_entity.entity_id
# Get media class if provided
media_class_slot = slots.get("media_class", {})
media_class_value = media_class_slot.get("value")
# Build search service data
search_data = {"search_query": search_query}
# Add media_filter_classes if media_class is provided
if media_class_value:
search_data[ATTR_MEDIA_FILTER_CLASSES] = [media_class_value]
# 1. Search Media
try:
search_response = await hass.services.async_call(
DOMAIN,
SERVICE_SEARCH_MEDIA,
{
"search_query": search_query,
},
search_data,
target={
"entity_id": target_entity_id,
},

View File

@ -792,3 +792,84 @@ async def test_search_and_play_media_player_intent(hass: HomeAssistant) -> None:
media_player_intent.INTENT_MEDIA_SEARCH_AND_PLAY,
{"search_query": {"value": "error query"}},
)
async def test_search_and_play_media_player_intent_with_media_class(
hass: HomeAssistant,
) -> None:
"""Test HassMediaSearchAndPlay intent with media_class parameter."""
await media_player_intent.async_setup_intents(hass)
entity_id = f"{DOMAIN}.test_media_player"
attributes = {
ATTR_SUPPORTED_FEATURES: MediaPlayerEntityFeature.SEARCH_MEDIA
| MediaPlayerEntityFeature.PLAY_MEDIA
}
hass.states.async_set(entity_id, STATE_IDLE, attributes=attributes)
# Test successful search and play with media_class filter
search_result_item = BrowseMedia(
title="Test Album",
media_class=MediaClass.ALBUM,
media_content_type=MediaType.ALBUM,
media_content_id="library/album/123",
can_play=True,
can_expand=False,
)
# Mock service calls
search_results = [search_result_item]
search_calls = async_mock_service(
hass,
DOMAIN,
SERVICE_SEARCH_MEDIA,
response={entity_id: SearchMedia(result=search_results)},
)
play_calls = async_mock_service(hass, DOMAIN, SERVICE_PLAY_MEDIA)
response = await intent.async_handle(
hass,
"test",
media_player_intent.INTENT_MEDIA_SEARCH_AND_PLAY,
{"search_query": {"value": "test album"}, "media_class": {"value": "album"}},
)
await hass.async_block_till_done()
assert response.response_type == intent.IntentResponseType.ACTION_DONE
# Response should contain a "media" slot with the matched item.
assert not response.speech
media = response.speech_slots.get("media")
assert media["title"] == "Test Album"
assert len(search_calls) == 1
search_call = search_calls[0]
assert search_call.domain == DOMAIN
assert search_call.service == SERVICE_SEARCH_MEDIA
assert search_call.data == {
"entity_id": entity_id,
"search_query": "test album",
"media_filter_classes": ["album"],
}
assert len(play_calls) == 1
play_call = play_calls[0]
assert play_call.domain == DOMAIN
assert play_call.service == SERVICE_PLAY_MEDIA
assert play_call.data == {
"entity_id": entity_id,
"media_content_id": search_result_item.media_content_id,
"media_content_type": search_result_item.media_content_type,
}
# Test with invalid media_class (should raise validation error)
with pytest.raises(intent.InvalidSlotInfo):
await intent.async_handle(
hass,
"test",
media_player_intent.INTENT_MEDIA_SEARCH_AND_PLAY,
{
"search_query": {"value": "test query"},
"media_class": {"value": "invalid_class"},
},
)