mirror of
https://github.com/home-assistant/core.git
synced 2025-07-23 05:07:41 +00:00
Expose media_player async_browse_media as service (#116452)
* initial commit * make fields optional * x * ruff issues * ruff issues * ruff issues * ruff issues * update example * update description * use constants * Update homeassistant/components/media_player/strings.json Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com> * update service call metadata * update description * patch the demo * Update homeassistant/components/media_player/strings.json Co-authored-by: Martin Hjelmare <marhje52@gmail.com> * revert unrelated change * update test metadata * update test metadata * change patch target to be more specific --------- Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com> Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
This commit is contained in:
parent
da1e3c29ed
commit
1393f417ed
@ -52,7 +52,7 @@ from homeassistant.const import ( # noqa: F401
|
||||
STATE_PLAYING,
|
||||
STATE_STANDBY,
|
||||
)
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.core import HomeAssistant, SupportsResponse
|
||||
from homeassistant.helpers import config_validation as cv
|
||||
from homeassistant.helpers.aiohttp_client import async_get_clientsession
|
||||
from homeassistant.helpers.deprecation import (
|
||||
@ -124,6 +124,7 @@ from .const import ( # noqa: F401
|
||||
CONTENT_AUTH_EXPIRY_TIME,
|
||||
DOMAIN,
|
||||
REPEAT_MODES,
|
||||
SERVICE_BROWSE_MEDIA,
|
||||
SERVICE_CLEAR_PLAYLIST,
|
||||
SERVICE_JOIN,
|
||||
SERVICE_PLAY_MEDIA,
|
||||
@ -201,6 +202,12 @@ MEDIA_PLAYER_PLAY_MEDIA_SCHEMA = {
|
||||
vol.Optional(ATTR_MEDIA_EXTRA, default={}): dict,
|
||||
}
|
||||
|
||||
MEDIA_PLAYER_BROWSE_MEDIA_SCHEMA = {
|
||||
vol.Optional(ATTR_MEDIA_CONTENT_TYPE): cv.string,
|
||||
vol.Optional(ATTR_MEDIA_CONTENT_ID): cv.string,
|
||||
}
|
||||
|
||||
|
||||
ATTR_TO_PROPERTY = [
|
||||
ATTR_MEDIA_VOLUME_LEVEL,
|
||||
ATTR_MEDIA_VOLUME_MUTED,
|
||||
@ -431,6 +438,15 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
|
||||
"async_play_media",
|
||||
[MediaPlayerEntityFeature.PLAY_MEDIA],
|
||||
)
|
||||
component.async_register_entity_service(
|
||||
SERVICE_BROWSE_MEDIA,
|
||||
{
|
||||
vol.Optional(ATTR_MEDIA_CONTENT_TYPE): cv.string,
|
||||
vol.Optional(ATTR_MEDIA_CONTENT_ID): cv.string,
|
||||
},
|
||||
"async_browse_media",
|
||||
supports_response=SupportsResponse.ONLY,
|
||||
)
|
||||
component.async_register_entity_service(
|
||||
SERVICE_SHUFFLE_SET,
|
||||
{vol.Required(ATTR_MEDIA_SHUFFLE): cv.boolean},
|
||||
|
@ -173,6 +173,7 @@ _DEPRECATED_MEDIA_TYPE_VIDEO = DeprecatedConstantEnum(MediaType.VIDEO, "2025.10"
|
||||
SERVICE_CLEAR_PLAYLIST = "clear_playlist"
|
||||
SERVICE_JOIN = "join"
|
||||
SERVICE_PLAY_MEDIA = "play_media"
|
||||
SERVICE_BROWSE_MEDIA = "browse_media"
|
||||
SERVICE_SELECT_SOUND_MODE = "select_sound_mode"
|
||||
SERVICE_SELECT_SOURCE = "select_source"
|
||||
SERVICE_UNJOIN = "unjoin"
|
||||
|
@ -32,6 +32,9 @@
|
||||
}
|
||||
},
|
||||
"services": {
|
||||
"browse_media": {
|
||||
"service": "mdi:folder-search"
|
||||
},
|
||||
"clear_playlist": {
|
||||
"service": "mdi:playlist-remove"
|
||||
},
|
||||
|
@ -165,6 +165,22 @@ play_media:
|
||||
selector:
|
||||
boolean:
|
||||
|
||||
browse_media:
|
||||
target:
|
||||
entity:
|
||||
domain: media_player
|
||||
fields:
|
||||
media_content_type:
|
||||
required: false
|
||||
example: "music"
|
||||
selector:
|
||||
text:
|
||||
media_content_id:
|
||||
required: false
|
||||
example: "A:ALBUMARTIST/Beatles"
|
||||
selector:
|
||||
text:
|
||||
|
||||
select_source:
|
||||
target:
|
||||
entity:
|
||||
|
@ -260,6 +260,20 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"browse_media": {
|
||||
"name": "Browse media",
|
||||
"description": "Browses the available media.",
|
||||
"fields": {
|
||||
"media_content_id": {
|
||||
"name": "Content ID",
|
||||
"description": "The ID of the content to browse. Integration dependent."
|
||||
},
|
||||
"media_content_type": {
|
||||
"name": "Content type",
|
||||
"description": "The type of the content to browse, such as image, music, tv show, video, episode, channel, or playlist."
|
||||
}
|
||||
}
|
||||
},
|
||||
"select_source": {
|
||||
"name": "Select source",
|
||||
"description": "Sends the media player the command to change input source.",
|
||||
|
@ -10,12 +10,15 @@ import voluptuous as vol
|
||||
|
||||
from homeassistant.components import media_player
|
||||
from homeassistant.components.media_player import (
|
||||
ATTR_MEDIA_CONTENT_ID,
|
||||
ATTR_MEDIA_CONTENT_TYPE,
|
||||
BrowseMedia,
|
||||
MediaClass,
|
||||
MediaPlayerEnqueue,
|
||||
MediaPlayerEntity,
|
||||
MediaPlayerEntityFeature,
|
||||
)
|
||||
from homeassistant.components.media_player.const import SERVICE_BROWSE_MEDIA
|
||||
from homeassistant.components.websocket_api import TYPE_RESULT
|
||||
from homeassistant.const import ATTR_ENTITY_ID, STATE_OFF
|
||||
from homeassistant.core import HomeAssistant
|
||||
@ -339,6 +342,75 @@ async def test_media_browse(
|
||||
assert msg["result"] == {"bla": "yo"}
|
||||
|
||||
|
||||
async def test_media_browse_service(hass: HomeAssistant) -> None:
|
||||
"""Test browsing media using service call."""
|
||||
await async_setup_component(
|
||||
hass, "media_player", {"media_player": {"platform": "demo"}}
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
with patch(
|
||||
"homeassistant.components.demo.media_player.DemoBrowsePlayer.async_browse_media",
|
||||
return_value=BrowseMedia(
|
||||
media_class=MediaClass.DIRECTORY,
|
||||
media_content_id="mock-id",
|
||||
media_content_type="mock-type",
|
||||
title="Mock Title",
|
||||
can_play=False,
|
||||
can_expand=True,
|
||||
children=[
|
||||
BrowseMedia(
|
||||
media_class=MediaClass.ALBUM,
|
||||
media_content_id="album1 content id",
|
||||
media_content_type="album",
|
||||
title="Album 1",
|
||||
can_play=True,
|
||||
can_expand=True,
|
||||
),
|
||||
BrowseMedia(
|
||||
media_class=MediaClass.ALBUM,
|
||||
media_content_id="album2 content id",
|
||||
media_content_type="album",
|
||||
title="Album 2",
|
||||
can_play=True,
|
||||
can_expand=True,
|
||||
),
|
||||
],
|
||||
),
|
||||
) as mock_browse_media:
|
||||
result = await hass.services.async_call(
|
||||
"media_player",
|
||||
SERVICE_BROWSE_MEDIA,
|
||||
{
|
||||
ATTR_ENTITY_ID: "media_player.browse",
|
||||
ATTR_MEDIA_CONTENT_TYPE: "album",
|
||||
ATTR_MEDIA_CONTENT_ID: "title=Album*",
|
||||
},
|
||||
blocking=True,
|
||||
return_response=True,
|
||||
)
|
||||
|
||||
mock_browse_media.assert_called_with(
|
||||
media_content_type="album", media_content_id="title=Album*"
|
||||
)
|
||||
browse_res: BrowseMedia = result["media_player.browse"]
|
||||
assert browse_res.title == "Mock Title"
|
||||
assert browse_res.media_class == "directory"
|
||||
assert browse_res.media_content_type == "mock-type"
|
||||
assert browse_res.media_content_id == "mock-id"
|
||||
assert browse_res.can_play is False
|
||||
assert browse_res.can_expand is True
|
||||
assert len(browse_res.children) == 2
|
||||
assert browse_res.children[0].title == "Album 1"
|
||||
assert browse_res.children[0].media_class == "album"
|
||||
assert browse_res.children[0].media_content_id == "album1 content id"
|
||||
assert browse_res.children[0].media_content_type == "album"
|
||||
assert browse_res.children[1].title == "Album 2"
|
||||
assert browse_res.children[1].media_class == "album"
|
||||
assert browse_res.children[1].media_content_id == "album2 content id"
|
||||
assert browse_res.children[1].media_content_type == "album"
|
||||
|
||||
|
||||
async def test_group_members_available_when_off(hass: HomeAssistant) -> None:
|
||||
"""Test that group_members are still available when media_player is off."""
|
||||
await async_setup_component(
|
||||
|
Loading…
x
Reference in New Issue
Block a user