mirror of
https://github.com/home-assistant/core.git
synced 2025-07-18 18:57:06 +00:00
Add media class to media player search and play intent (#147097)
Co-authored-by: Michael Hansen <mike@rhasspy.org>
This commit is contained in:
parent
ab0ea753e9
commit
95abd69cc6
@ -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,
|
||||
},
|
||||
|
@ -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"},
|
||||
},
|
||||
)
|
||||
|
Loading…
x
Reference in New Issue
Block a user