mirror of
https://github.com/home-assistant/core.git
synced 2025-04-26 18:27:51 +00:00
Add media source support to unifiprotect (#67570)
This commit is contained in:
parent
7fc0ffd5c5
commit
a9fd744247
@ -7,13 +7,19 @@ from typing import Any
|
|||||||
from pyunifiprotect.data import Camera
|
from pyunifiprotect.data import Camera
|
||||||
from pyunifiprotect.exceptions import StreamError
|
from pyunifiprotect.exceptions import StreamError
|
||||||
|
|
||||||
|
from homeassistant.components import media_source
|
||||||
from homeassistant.components.media_player import (
|
from homeassistant.components.media_player import (
|
||||||
|
BrowseMedia,
|
||||||
MediaPlayerDeviceClass,
|
MediaPlayerDeviceClass,
|
||||||
MediaPlayerEntity,
|
MediaPlayerEntity,
|
||||||
MediaPlayerEntityDescription,
|
MediaPlayerEntityDescription,
|
||||||
)
|
)
|
||||||
|
from homeassistant.components.media_player.browse_media import (
|
||||||
|
async_process_play_media_url,
|
||||||
|
)
|
||||||
from homeassistant.components.media_player.const import (
|
from homeassistant.components.media_player.const import (
|
||||||
MEDIA_TYPE_MUSIC,
|
MEDIA_TYPE_MUSIC,
|
||||||
|
SUPPORT_BROWSE_MEDIA,
|
||||||
SUPPORT_PLAY_MEDIA,
|
SUPPORT_PLAY_MEDIA,
|
||||||
SUPPORT_STOP,
|
SUPPORT_STOP,
|
||||||
SUPPORT_VOLUME_SET,
|
SUPPORT_VOLUME_SET,
|
||||||
@ -74,7 +80,11 @@ class ProtectMediaPlayer(ProtectDeviceEntity, MediaPlayerEntity):
|
|||||||
|
|
||||||
self._attr_name = f"{self.device.name} Speaker"
|
self._attr_name = f"{self.device.name} Speaker"
|
||||||
self._attr_supported_features = (
|
self._attr_supported_features = (
|
||||||
SUPPORT_PLAY_MEDIA | SUPPORT_VOLUME_SET | SUPPORT_VOLUME_STEP | SUPPORT_STOP
|
SUPPORT_PLAY_MEDIA
|
||||||
|
| SUPPORT_VOLUME_SET
|
||||||
|
| SUPPORT_VOLUME_STEP
|
||||||
|
| SUPPORT_STOP
|
||||||
|
| SUPPORT_BROWSE_MEDIA
|
||||||
)
|
)
|
||||||
self._attr_media_content_type = MEDIA_TYPE_MUSIC
|
self._attr_media_content_type = MEDIA_TYPE_MUSIC
|
||||||
|
|
||||||
@ -112,16 +122,20 @@ class ProtectMediaPlayer(ProtectDeviceEntity, MediaPlayerEntity):
|
|||||||
self, media_type: str, media_id: str, **kwargs: Any
|
self, media_type: str, media_id: str, **kwargs: Any
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Play a piece of media."""
|
"""Play a piece of media."""
|
||||||
|
if media_source.is_media_source_id(media_id):
|
||||||
|
media_type = MEDIA_TYPE_MUSIC
|
||||||
|
play_item = await media_source.async_resolve_media(self.hass, media_id)
|
||||||
|
media_id = async_process_play_media_url(self.hass, play_item.url)
|
||||||
|
|
||||||
if media_type != MEDIA_TYPE_MUSIC:
|
if media_type != MEDIA_TYPE_MUSIC:
|
||||||
raise ValueError("Only music media type is supported")
|
raise HomeAssistantError("Only music media type is supported")
|
||||||
|
|
||||||
_LOGGER.debug("Playing Media %s for %s Speaker", media_id, self.device.name)
|
_LOGGER.debug("Playing Media %s for %s Speaker", media_id, self.device.name)
|
||||||
await self.async_media_stop()
|
await self.async_media_stop()
|
||||||
try:
|
try:
|
||||||
await self.device.play_audio(media_id, blocking=False)
|
await self.device.play_audio(media_id, blocking=False)
|
||||||
except StreamError as err:
|
except StreamError as err:
|
||||||
raise HomeAssistantError from err
|
raise HomeAssistantError(err) from err
|
||||||
else:
|
else:
|
||||||
# update state after starting player
|
# update state after starting player
|
||||||
self._async_updated_event()
|
self._async_updated_event()
|
||||||
@ -129,3 +143,13 @@ class ProtectMediaPlayer(ProtectDeviceEntity, MediaPlayerEntity):
|
|||||||
await self.device.wait_until_audio_completes()
|
await self.device.wait_until_audio_completes()
|
||||||
|
|
||||||
self._async_updated_event()
|
self._async_updated_event()
|
||||||
|
|
||||||
|
async def async_browse_media(
|
||||||
|
self, media_content_type: str | None = None, media_content_id: str | None = None
|
||||||
|
) -> BrowseMedia:
|
||||||
|
"""Implement the websocket media browsing helper."""
|
||||||
|
return await media_source.async_browse_media(
|
||||||
|
self.hass,
|
||||||
|
media_content_id,
|
||||||
|
content_filter=lambda item: item.media_content_type.startswith("audio/"),
|
||||||
|
)
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from copy import copy
|
from copy import copy
|
||||||
from unittest.mock import AsyncMock, Mock
|
from unittest.mock import AsyncMock, Mock, patch
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
from pyunifiprotect.data import Camera
|
from pyunifiprotect.data import Camera
|
||||||
@ -80,7 +80,7 @@ async def test_media_player_setup(
|
|||||||
assert state
|
assert state
|
||||||
assert state.state == STATE_IDLE
|
assert state.state == STATE_IDLE
|
||||||
assert state.attributes[ATTR_ATTRIBUTION] == DEFAULT_ATTRIBUTION
|
assert state.attributes[ATTR_ATTRIBUTION] == DEFAULT_ATTRIBUTION
|
||||||
assert state.attributes[ATTR_SUPPORTED_FEATURES] == 5636
|
assert state.attributes[ATTR_SUPPORTED_FEATURES] == 136708
|
||||||
assert state.attributes[ATTR_MEDIA_CONTENT_TYPE] == "music"
|
assert state.attributes[ATTR_MEDIA_CONTENT_TYPE] == "music"
|
||||||
assert state.attributes[ATTR_MEDIA_VOLUME_LEVEL] == expected_volume
|
assert state.attributes[ATTR_MEDIA_VOLUME_LEVEL] == expected_volume
|
||||||
|
|
||||||
@ -166,7 +166,6 @@ async def test_media_player_play(
|
|||||||
camera: tuple[Camera, str],
|
camera: tuple[Camera, str],
|
||||||
):
|
):
|
||||||
"""Test media_player entity test play_media."""
|
"""Test media_player entity test play_media."""
|
||||||
|
|
||||||
camera[0].__fields__["stop_audio"] = Mock()
|
camera[0].__fields__["stop_audio"] = Mock()
|
||||||
camera[0].__fields__["play_audio"] = Mock()
|
camera[0].__fields__["play_audio"] = Mock()
|
||||||
camera[0].__fields__["wait_until_audio_completes"] = Mock()
|
camera[0].__fields__["wait_until_audio_completes"] = Mock()
|
||||||
@ -179,13 +178,48 @@ async def test_media_player_play(
|
|||||||
"play_media",
|
"play_media",
|
||||||
{
|
{
|
||||||
ATTR_ENTITY_ID: camera[1],
|
ATTR_ENTITY_ID: camera[1],
|
||||||
"media_content_id": "/test.mp3",
|
"media_content_id": "http://example.com/test.mp3",
|
||||||
"media_content_type": "music",
|
"media_content_type": "music",
|
||||||
},
|
},
|
||||||
blocking=True,
|
blocking=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
camera[0].play_audio.assert_called_once_with("/test.mp3", blocking=False)
|
camera[0].play_audio.assert_called_once_with(
|
||||||
|
"http://example.com/test.mp3", blocking=False
|
||||||
|
)
|
||||||
|
camera[0].wait_until_audio_completes.assert_called_once()
|
||||||
|
|
||||||
|
|
||||||
|
async def test_media_player_play_media_source(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
camera: tuple[Camera, str],
|
||||||
|
):
|
||||||
|
"""Test media_player entity test play_media."""
|
||||||
|
camera[0].__fields__["stop_audio"] = Mock()
|
||||||
|
camera[0].__fields__["play_audio"] = Mock()
|
||||||
|
camera[0].__fields__["wait_until_audio_completes"] = Mock()
|
||||||
|
camera[0].stop_audio = AsyncMock()
|
||||||
|
camera[0].play_audio = AsyncMock()
|
||||||
|
camera[0].wait_until_audio_completes = AsyncMock()
|
||||||
|
|
||||||
|
with patch(
|
||||||
|
"homeassistant.components.media_source.async_resolve_media",
|
||||||
|
return_value=Mock(url="http://example.com/test.mp3"),
|
||||||
|
):
|
||||||
|
await hass.services.async_call(
|
||||||
|
"media_player",
|
||||||
|
"play_media",
|
||||||
|
{
|
||||||
|
ATTR_ENTITY_ID: camera[1],
|
||||||
|
"media_content_id": "media-source://some_source/some_id",
|
||||||
|
"media_content_type": "audio/mpeg",
|
||||||
|
},
|
||||||
|
blocking=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
camera[0].play_audio.assert_called_once_with(
|
||||||
|
"http://example.com/test.mp3", blocking=False
|
||||||
|
)
|
||||||
camera[0].wait_until_audio_completes.assert_called_once()
|
camera[0].wait_until_audio_completes.assert_called_once()
|
||||||
|
|
||||||
|
|
||||||
@ -198,7 +232,7 @@ async def test_media_player_play_invalid(
|
|||||||
camera[0].__fields__["play_audio"] = Mock()
|
camera[0].__fields__["play_audio"] = Mock()
|
||||||
camera[0].play_audio = AsyncMock()
|
camera[0].play_audio = AsyncMock()
|
||||||
|
|
||||||
with pytest.raises(ValueError):
|
with pytest.raises(HomeAssistantError):
|
||||||
await hass.services.async_call(
|
await hass.services.async_call(
|
||||||
"media_player",
|
"media_player",
|
||||||
"play_media",
|
"play_media",
|
||||||
|
Loading…
x
Reference in New Issue
Block a user