Add media source support to unifiprotect (#67570)

This commit is contained in:
Paulus Schoutsen 2022-03-14 13:16:22 -07:00 committed by GitHub
parent 7fc0ffd5c5
commit a9fd744247
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 67 additions and 9 deletions

View File

@ -7,13 +7,19 @@ from typing import Any
from pyunifiprotect.data import Camera
from pyunifiprotect.exceptions import StreamError
from homeassistant.components import media_source
from homeassistant.components.media_player import (
BrowseMedia,
MediaPlayerDeviceClass,
MediaPlayerEntity,
MediaPlayerEntityDescription,
)
from homeassistant.components.media_player.browse_media import (
async_process_play_media_url,
)
from homeassistant.components.media_player.const import (
MEDIA_TYPE_MUSIC,
SUPPORT_BROWSE_MEDIA,
SUPPORT_PLAY_MEDIA,
SUPPORT_STOP,
SUPPORT_VOLUME_SET,
@ -74,7 +80,11 @@ class ProtectMediaPlayer(ProtectDeviceEntity, MediaPlayerEntity):
self._attr_name = f"{self.device.name} Speaker"
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
@ -112,16 +122,20 @@ class ProtectMediaPlayer(ProtectDeviceEntity, MediaPlayerEntity):
self, media_type: str, media_id: str, **kwargs: Any
) -> None:
"""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:
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)
await self.async_media_stop()
try:
await self.device.play_audio(media_id, blocking=False)
except StreamError as err:
raise HomeAssistantError from err
raise HomeAssistantError(err) from err
else:
# update state after starting player
self._async_updated_event()
@ -129,3 +143,13 @@ class ProtectMediaPlayer(ProtectDeviceEntity, MediaPlayerEntity):
await self.device.wait_until_audio_completes()
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/"),
)

View File

@ -3,7 +3,7 @@
from __future__ import annotations
from copy import copy
from unittest.mock import AsyncMock, Mock
from unittest.mock import AsyncMock, Mock, patch
import pytest
from pyunifiprotect.data import Camera
@ -80,7 +80,7 @@ async def test_media_player_setup(
assert state
assert state.state == STATE_IDLE
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_VOLUME_LEVEL] == expected_volume
@ -166,7 +166,6 @@ async def test_media_player_play(
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()
@ -179,13 +178,48 @@ async def test_media_player_play(
"play_media",
{
ATTR_ENTITY_ID: camera[1],
"media_content_id": "/test.mp3",
"media_content_id": "http://example.com/test.mp3",
"media_content_type": "music",
},
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()
@ -198,7 +232,7 @@ async def test_media_player_play_invalid(
camera[0].__fields__["play_audio"] = Mock()
camera[0].play_audio = AsyncMock()
with pytest.raises(ValueError):
with pytest.raises(HomeAssistantError):
await hass.services.async_call(
"media_player",
"play_media",