Add additional supported feature support to universal media player (#44711)
* add additional supported feature support to universal media player * add missing servicespull/46550/head
parent
0af634a9f8
commit
a3b733f1ec
|
@ -20,6 +20,7 @@ from homeassistant.components.media_player.const import (
|
|||
ATTR_MEDIA_PLAYLIST,
|
||||
ATTR_MEDIA_POSITION,
|
||||
ATTR_MEDIA_POSITION_UPDATED_AT,
|
||||
ATTR_MEDIA_REPEAT,
|
||||
ATTR_MEDIA_SEASON,
|
||||
ATTR_MEDIA_SEEK_POSITION,
|
||||
ATTR_MEDIA_SERIES_TITLE,
|
||||
|
@ -28,13 +29,23 @@ from homeassistant.components.media_player.const import (
|
|||
ATTR_MEDIA_TRACK,
|
||||
ATTR_MEDIA_VOLUME_LEVEL,
|
||||
ATTR_MEDIA_VOLUME_MUTED,
|
||||
ATTR_SOUND_MODE,
|
||||
ATTR_SOUND_MODE_LIST,
|
||||
DOMAIN,
|
||||
SERVICE_CLEAR_PLAYLIST,
|
||||
SERVICE_PLAY_MEDIA,
|
||||
SERVICE_SELECT_SOUND_MODE,
|
||||
SERVICE_SELECT_SOURCE,
|
||||
SUPPORT_CLEAR_PLAYLIST,
|
||||
SUPPORT_NEXT_TRACK,
|
||||
SUPPORT_PAUSE,
|
||||
SUPPORT_PLAY,
|
||||
SUPPORT_PREVIOUS_TRACK,
|
||||
SUPPORT_REPEAT_SET,
|
||||
SUPPORT_SELECT_SOUND_MODE,
|
||||
SUPPORT_SELECT_SOURCE,
|
||||
SUPPORT_SHUFFLE_SET,
|
||||
SUPPORT_STOP,
|
||||
SUPPORT_TURN_OFF,
|
||||
SUPPORT_TURN_ON,
|
||||
SUPPORT_VOLUME_MUTE,
|
||||
|
@ -55,7 +66,9 @@ from homeassistant.const import (
|
|||
SERVICE_MEDIA_PREVIOUS_TRACK,
|
||||
SERVICE_MEDIA_SEEK,
|
||||
SERVICE_MEDIA_STOP,
|
||||
SERVICE_REPEAT_SET,
|
||||
SERVICE_SHUFFLE_SET,
|
||||
SERVICE_TOGGLE,
|
||||
SERVICE_TURN_OFF,
|
||||
SERVICE_TURN_ON,
|
||||
SERVICE_VOLUME_DOWN,
|
||||
|
@ -382,6 +395,16 @@ class UniversalMediaPlayer(MediaPlayerEntity):
|
|||
"""Name of the current running app."""
|
||||
return self._child_attr(ATTR_APP_NAME)
|
||||
|
||||
@property
|
||||
def sound_mode(self):
|
||||
"""Return the current sound mode of the device."""
|
||||
return self._override_or_child_attr(ATTR_SOUND_MODE)
|
||||
|
||||
@property
|
||||
def sound_mode_list(self):
|
||||
"""List of available sound modes."""
|
||||
return self._override_or_child_attr(ATTR_SOUND_MODE_LIST)
|
||||
|
||||
@property
|
||||
def source(self):
|
||||
"""Return the current input source of the device."""
|
||||
|
@ -392,6 +415,11 @@ class UniversalMediaPlayer(MediaPlayerEntity):
|
|||
"""List of available input sources."""
|
||||
return self._override_or_child_attr(ATTR_INPUT_SOURCE_LIST)
|
||||
|
||||
@property
|
||||
def repeat(self):
|
||||
"""Boolean if repeating is enabled."""
|
||||
return self._override_or_child_attr(ATTR_MEDIA_REPEAT)
|
||||
|
||||
@property
|
||||
def shuffle(self):
|
||||
"""Boolean if shuffling is enabled."""
|
||||
|
@ -407,6 +435,22 @@ class UniversalMediaPlayer(MediaPlayerEntity):
|
|||
if SERVICE_TURN_OFF in self._cmds:
|
||||
flags |= SUPPORT_TURN_OFF
|
||||
|
||||
if SERVICE_MEDIA_PLAY_PAUSE in self._cmds:
|
||||
flags |= SUPPORT_PLAY | SUPPORT_PAUSE
|
||||
else:
|
||||
if SERVICE_MEDIA_PLAY in self._cmds:
|
||||
flags |= SUPPORT_PLAY
|
||||
if SERVICE_MEDIA_PAUSE in self._cmds:
|
||||
flags |= SUPPORT_PAUSE
|
||||
|
||||
if SERVICE_MEDIA_STOP in self._cmds:
|
||||
flags |= SUPPORT_STOP
|
||||
|
||||
if SERVICE_MEDIA_NEXT_TRACK in self._cmds:
|
||||
flags |= SUPPORT_NEXT_TRACK
|
||||
if SERVICE_MEDIA_PREVIOUS_TRACK in self._cmds:
|
||||
flags |= SUPPORT_PREVIOUS_TRACK
|
||||
|
||||
if any([cmd in self._cmds for cmd in [SERVICE_VOLUME_UP, SERVICE_VOLUME_DOWN]]):
|
||||
flags |= SUPPORT_VOLUME_STEP
|
||||
if SERVICE_VOLUME_SET in self._cmds:
|
||||
|
@ -415,7 +459,10 @@ class UniversalMediaPlayer(MediaPlayerEntity):
|
|||
if SERVICE_VOLUME_MUTE in self._cmds and ATTR_MEDIA_VOLUME_MUTED in self._attrs:
|
||||
flags |= SUPPORT_VOLUME_MUTE
|
||||
|
||||
if SERVICE_SELECT_SOURCE in self._cmds:
|
||||
if (
|
||||
SERVICE_SELECT_SOURCE in self._cmds
|
||||
and ATTR_INPUT_SOURCE_LIST in self._attrs
|
||||
):
|
||||
flags |= SUPPORT_SELECT_SOURCE
|
||||
|
||||
if SERVICE_CLEAR_PLAYLIST in self._cmds:
|
||||
|
@ -424,6 +471,15 @@ class UniversalMediaPlayer(MediaPlayerEntity):
|
|||
if SERVICE_SHUFFLE_SET in self._cmds and ATTR_MEDIA_SHUFFLE in self._attrs:
|
||||
flags |= SUPPORT_SHUFFLE_SET
|
||||
|
||||
if SERVICE_REPEAT_SET in self._cmds and ATTR_MEDIA_REPEAT in self._attrs:
|
||||
flags |= SUPPORT_REPEAT_SET
|
||||
|
||||
if (
|
||||
SERVICE_SELECT_SOUND_MODE in self._cmds
|
||||
and ATTR_SOUND_MODE_LIST in self._attrs
|
||||
):
|
||||
flags |= SUPPORT_SELECT_SOUND_MODE
|
||||
|
||||
return flags
|
||||
|
||||
@property
|
||||
|
@ -502,6 +558,13 @@ class UniversalMediaPlayer(MediaPlayerEntity):
|
|||
"""Play or pause the media player."""
|
||||
await self._async_call_service(SERVICE_MEDIA_PLAY_PAUSE)
|
||||
|
||||
async def async_select_sound_mode(self, sound_mode):
|
||||
"""Select sound mode."""
|
||||
data = {ATTR_SOUND_MODE: sound_mode}
|
||||
await self._async_call_service(
|
||||
SERVICE_SELECT_SOUND_MODE, data, allow_override=True
|
||||
)
|
||||
|
||||
async def async_select_source(self, source):
|
||||
"""Set the input source."""
|
||||
data = {ATTR_INPUT_SOURCE: source}
|
||||
|
@ -516,6 +579,15 @@ class UniversalMediaPlayer(MediaPlayerEntity):
|
|||
data = {ATTR_MEDIA_SHUFFLE: shuffle}
|
||||
await self._async_call_service(SERVICE_SHUFFLE_SET, data, allow_override=True)
|
||||
|
||||
async def async_set_repeat(self, repeat):
|
||||
"""Set repeat mode."""
|
||||
data = {ATTR_MEDIA_REPEAT: repeat}
|
||||
await self._async_call_service(SERVICE_REPEAT_SET, data, allow_override=True)
|
||||
|
||||
async def async_toggle(self):
|
||||
"""Toggle the power on the media player."""
|
||||
await self._async_call_service(SERVICE_TOGGLE)
|
||||
|
||||
async def async_update(self):
|
||||
"""Update state in HA."""
|
||||
for child_name in self._children:
|
||||
|
|
|
@ -51,6 +51,7 @@ class MockMediaPlayer(media_player.MediaPlayerEntity):
|
|||
self._tracks = 12
|
||||
self._media_image_url = None
|
||||
self._shuffle = False
|
||||
self._sound_mode = None
|
||||
|
||||
self.service_calls = {
|
||||
"turn_on": mock_service(
|
||||
|
@ -71,6 +72,9 @@ class MockMediaPlayer(media_player.MediaPlayerEntity):
|
|||
"media_pause": mock_service(
|
||||
hass, media_player.DOMAIN, media_player.SERVICE_MEDIA_PAUSE
|
||||
),
|
||||
"media_stop": mock_service(
|
||||
hass, media_player.DOMAIN, media_player.SERVICE_MEDIA_STOP
|
||||
),
|
||||
"media_previous_track": mock_service(
|
||||
hass, media_player.DOMAIN, media_player.SERVICE_MEDIA_PREVIOUS_TRACK
|
||||
),
|
||||
|
@ -92,12 +96,21 @@ class MockMediaPlayer(media_player.MediaPlayerEntity):
|
|||
"media_play_pause": mock_service(
|
||||
hass, media_player.DOMAIN, media_player.SERVICE_MEDIA_PLAY_PAUSE
|
||||
),
|
||||
"select_sound_mode": mock_service(
|
||||
hass, media_player.DOMAIN, media_player.SERVICE_SELECT_SOUND_MODE
|
||||
),
|
||||
"select_source": mock_service(
|
||||
hass, media_player.DOMAIN, media_player.SERVICE_SELECT_SOURCE
|
||||
),
|
||||
"toggle": mock_service(
|
||||
hass, media_player.DOMAIN, media_player.SERVICE_TOGGLE
|
||||
),
|
||||
"clear_playlist": mock_service(
|
||||
hass, media_player.DOMAIN, media_player.SERVICE_CLEAR_PLAYLIST
|
||||
),
|
||||
"repeat_set": mock_service(
|
||||
hass, media_player.DOMAIN, media_player.SERVICE_REPEAT_SET
|
||||
),
|
||||
"shuffle_set": mock_service(
|
||||
hass, media_player.DOMAIN, media_player.SERVICE_SHUFFLE_SET
|
||||
),
|
||||
|
@ -162,18 +175,30 @@ class MockMediaPlayer(media_player.MediaPlayerEntity):
|
|||
"""Mock pause."""
|
||||
self._state = STATE_PAUSED
|
||||
|
||||
def select_sound_mode(self, sound_mode):
|
||||
"""Set the sound mode."""
|
||||
self._sound_mode = sound_mode
|
||||
|
||||
def select_source(self, source):
|
||||
"""Set the input source."""
|
||||
self._source = source
|
||||
|
||||
def async_toggle(self):
|
||||
"""Toggle the power on the media player."""
|
||||
self._state = STATE_OFF if self._state == STATE_ON else STATE_ON
|
||||
|
||||
def clear_playlist(self):
|
||||
"""Clear players playlist."""
|
||||
self._tracks = 0
|
||||
|
||||
def set_shuffle(self, shuffle):
|
||||
"""Clear players playlist."""
|
||||
"""Enable/disable shuffle mode."""
|
||||
self._shuffle = shuffle
|
||||
|
||||
def set_repeat(self, repeat):
|
||||
"""Enable/disable repeat mode."""
|
||||
self._repeat = repeat
|
||||
|
||||
|
||||
class TestMediaPlayer(unittest.TestCase):
|
||||
"""Test the media_player module."""
|
||||
|
@ -205,9 +230,18 @@ class TestMediaPlayer(unittest.TestCase):
|
|||
self.mock_source_id = f"{input_select.DOMAIN}.source"
|
||||
self.hass.states.set(self.mock_source_id, "dvd")
|
||||
|
||||
self.mock_sound_mode_list_id = f"{input_select.DOMAIN}.sound_mode_list"
|
||||
self.hass.states.set(self.mock_sound_mode_list_id, ["music", "movie"])
|
||||
|
||||
self.mock_sound_mode_id = f"{input_select.DOMAIN}.sound_mode"
|
||||
self.hass.states.set(self.mock_sound_mode_id, "music")
|
||||
|
||||
self.mock_shuffle_switch_id = switch.ENTITY_ID_FORMAT.format("shuffle")
|
||||
self.hass.states.set(self.mock_shuffle_switch_id, STATE_OFF)
|
||||
|
||||
self.mock_repeat_switch_id = switch.ENTITY_ID_FORMAT.format("repeat")
|
||||
self.hass.states.set(self.mock_repeat_switch_id, STATE_OFF)
|
||||
|
||||
self.config_children_only = {
|
||||
"name": "test",
|
||||
"platform": "universal",
|
||||
|
@ -230,6 +264,9 @@ class TestMediaPlayer(unittest.TestCase):
|
|||
"source_list": self.mock_source_list_id,
|
||||
"state": self.mock_state_switch_id,
|
||||
"shuffle": self.mock_shuffle_switch_id,
|
||||
"repeat": self.mock_repeat_switch_id,
|
||||
"sound_mode_list": self.mock_sound_mode_list_id,
|
||||
"sound_mode": self.mock_sound_mode_id,
|
||||
},
|
||||
}
|
||||
self.addCleanup(self.tear_down_cleanup)
|
||||
|
@ -507,6 +544,17 @@ class TestMediaPlayer(unittest.TestCase):
|
|||
asyncio.run_coroutine_threadsafe(ump.async_update(), self.hass.loop).result()
|
||||
assert ump.is_volume_muted
|
||||
|
||||
def test_sound_mode_list_children_and_attr(self):
|
||||
"""Test sound mode list property w/ children and attrs."""
|
||||
config = validate_config(self.config_children_and_attr)
|
||||
|
||||
ump = universal.UniversalMediaPlayer(self.hass, **config)
|
||||
|
||||
assert "['music', 'movie']" == ump.sound_mode_list
|
||||
|
||||
self.hass.states.set(self.mock_sound_mode_list_id, ["music", "movie", "game"])
|
||||
assert "['music', 'movie', 'game']" == ump.sound_mode_list
|
||||
|
||||
def test_source_list_children_and_attr(self):
|
||||
"""Test source list property w/ children and attrs."""
|
||||
config = validate_config(self.config_children_and_attr)
|
||||
|
@ -518,6 +566,17 @@ class TestMediaPlayer(unittest.TestCase):
|
|||
self.hass.states.set(self.mock_source_list_id, ["dvd", "htpc", "game"])
|
||||
assert "['dvd', 'htpc', 'game']" == ump.source_list
|
||||
|
||||
def test_sound_mode_children_and_attr(self):
|
||||
"""Test sound modeproperty w/ children and attrs."""
|
||||
config = validate_config(self.config_children_and_attr)
|
||||
|
||||
ump = universal.UniversalMediaPlayer(self.hass, **config)
|
||||
|
||||
assert "music" == ump.sound_mode
|
||||
|
||||
self.hass.states.set(self.mock_sound_mode_id, "movie")
|
||||
assert "movie" == ump.sound_mode
|
||||
|
||||
def test_source_children_and_attr(self):
|
||||
"""Test source property w/ children and attrs."""
|
||||
config = validate_config(self.config_children_and_attr)
|
||||
|
@ -579,8 +638,17 @@ class TestMediaPlayer(unittest.TestCase):
|
|||
"volume_down": excmd,
|
||||
"volume_mute": excmd,
|
||||
"volume_set": excmd,
|
||||
"select_sound_mode": excmd,
|
||||
"select_source": excmd,
|
||||
"repeat_set": excmd,
|
||||
"shuffle_set": excmd,
|
||||
"media_play": excmd,
|
||||
"media_pause": excmd,
|
||||
"media_stop": excmd,
|
||||
"media_next_track": excmd,
|
||||
"media_previous_track": excmd,
|
||||
"toggle": excmd,
|
||||
"clear_playlist": excmd,
|
||||
}
|
||||
config = validate_config(config)
|
||||
|
||||
|
@ -598,13 +666,41 @@ class TestMediaPlayer(unittest.TestCase):
|
|||
| universal.SUPPORT_TURN_OFF
|
||||
| universal.SUPPORT_VOLUME_STEP
|
||||
| universal.SUPPORT_VOLUME_MUTE
|
||||
| universal.SUPPORT_SELECT_SOUND_MODE
|
||||
| universal.SUPPORT_SELECT_SOURCE
|
||||
| universal.SUPPORT_REPEAT_SET
|
||||
| universal.SUPPORT_SHUFFLE_SET
|
||||
| universal.SUPPORT_VOLUME_SET
|
||||
| universal.SUPPORT_PLAY
|
||||
| universal.SUPPORT_PAUSE
|
||||
| universal.SUPPORT_STOP
|
||||
| universal.SUPPORT_NEXT_TRACK
|
||||
| universal.SUPPORT_PREVIOUS_TRACK
|
||||
| universal.SUPPORT_CLEAR_PLAYLIST
|
||||
)
|
||||
|
||||
assert check_flags == ump.supported_features
|
||||
|
||||
def test_supported_features_play_pause(self):
|
||||
"""Test supported media commands with play_pause function."""
|
||||
config = copy(self.config_children_and_attr)
|
||||
excmd = {"service": "media_player.test", "data": {"entity_id": "test"}}
|
||||
config["commands"] = {"media_play_pause": excmd}
|
||||
config = validate_config(config)
|
||||
|
||||
ump = universal.UniversalMediaPlayer(self.hass, **config)
|
||||
ump.entity_id = media_player.ENTITY_ID_FORMAT.format(config["name"])
|
||||
asyncio.run_coroutine_threadsafe(ump.async_update(), self.hass.loop).result()
|
||||
|
||||
self.mock_mp_1._state = STATE_PLAYING
|
||||
self.mock_mp_1.schedule_update_ha_state()
|
||||
self.hass.block_till_done()
|
||||
asyncio.run_coroutine_threadsafe(ump.async_update(), self.hass.loop).result()
|
||||
|
||||
check_flags = universal.SUPPORT_PLAY | universal.SUPPORT_PAUSE
|
||||
|
||||
assert check_flags == ump.supported_features
|
||||
|
||||
def test_service_call_no_active_child(self):
|
||||
"""Test a service call to children with no active child."""
|
||||
config = validate_config(self.config_children_and_attr)
|
||||
|
@ -663,6 +759,11 @@ class TestMediaPlayer(unittest.TestCase):
|
|||
).result()
|
||||
assert 1 == len(self.mock_mp_2.service_calls["media_pause"])
|
||||
|
||||
asyncio.run_coroutine_threadsafe(
|
||||
ump.async_media_stop(), self.hass.loop
|
||||
).result()
|
||||
assert 1 == len(self.mock_mp_2.service_calls["media_stop"])
|
||||
|
||||
asyncio.run_coroutine_threadsafe(
|
||||
ump.async_media_previous_track(), self.hass.loop
|
||||
).result()
|
||||
|
@ -696,6 +797,11 @@ class TestMediaPlayer(unittest.TestCase):
|
|||
).result()
|
||||
assert 1 == len(self.mock_mp_2.service_calls["media_play_pause"])
|
||||
|
||||
asyncio.run_coroutine_threadsafe(
|
||||
ump.async_select_sound_mode("music"), self.hass.loop
|
||||
).result()
|
||||
assert 1 == len(self.mock_mp_2.service_calls["select_sound_mode"])
|
||||
|
||||
asyncio.run_coroutine_threadsafe(
|
||||
ump.async_select_source("dvd"), self.hass.loop
|
||||
).result()
|
||||
|
@ -706,11 +812,19 @@ class TestMediaPlayer(unittest.TestCase):
|
|||
).result()
|
||||
assert 1 == len(self.mock_mp_2.service_calls["clear_playlist"])
|
||||
|
||||
asyncio.run_coroutine_threadsafe(
|
||||
ump.async_set_repeat(True), self.hass.loop
|
||||
).result()
|
||||
assert 1 == len(self.mock_mp_2.service_calls["repeat_set"])
|
||||
|
||||
asyncio.run_coroutine_threadsafe(
|
||||
ump.async_set_shuffle(True), self.hass.loop
|
||||
).result()
|
||||
assert 1 == len(self.mock_mp_2.service_calls["shuffle_set"])
|
||||
|
||||
asyncio.run_coroutine_threadsafe(ump.async_toggle(), self.hass.loop).result()
|
||||
assert 1 == len(self.mock_mp_2.service_calls["toggle"])
|
||||
|
||||
def test_service_call_to_command(self):
|
||||
"""Test service call to command."""
|
||||
config = copy(self.config_children_only)
|
||||
|
|
Loading…
Reference in New Issue