mirror of
https://github.com/home-assistant/core.git
synced 2025-07-20 03:37:07 +00:00
Universal source list (#4086)
* Add source_list to universal media player * Expanded attirubte and command support for UMP Added support to the universal media player for the following: Volume Set Current Source Set Source Current Volume The goal is to facilitate a single-card media player that includes source selection and setting the volume of the receiver. Example setup: ``` media_player: - platform: universal name: Media Center children: - media_player.kodi - media_player.cast commands: select_source: service: media_player.select_source data: entity_id: media_player.receiver volume_set: service: media_player.volume_set data: entity_id: media_player.receiver volume_mute: service: media_player.volume_mute data: entity_id: media_player.receiver turn_on: service: homeassistant.turn_on data: entity_id: media_player.receiver turn_off: service: homeassistant.turn_off data: entity_id: media_player.receiver attributes: state: media_player.receiver is_volume_muted: media_player.receiver|is_volume_muted volume_level: media_player.receiver|volume_level source: media_player.receiver|source source_list: media_player.receiver|source_list ``` * Remove print statements * Change service call back to use call_from_config * Modified service calls to use template data * linting fixes * Add tests * linting fices * More pylinting
This commit is contained in:
parent
7746ecd98e
commit
a099430834
@ -14,7 +14,7 @@ from homeassistant.components.media_player import (
|
|||||||
ATTR_MEDIA_CONTENT_TYPE, ATTR_MEDIA_DURATION, ATTR_MEDIA_EPISODE,
|
ATTR_MEDIA_CONTENT_TYPE, ATTR_MEDIA_DURATION, ATTR_MEDIA_EPISODE,
|
||||||
ATTR_MEDIA_PLAYLIST, ATTR_MEDIA_SEASON, ATTR_MEDIA_SEEK_POSITION,
|
ATTR_MEDIA_PLAYLIST, ATTR_MEDIA_SEASON, ATTR_MEDIA_SEEK_POSITION,
|
||||||
ATTR_MEDIA_SERIES_TITLE, ATTR_MEDIA_TITLE, ATTR_MEDIA_TRACK,
|
ATTR_MEDIA_SERIES_TITLE, ATTR_MEDIA_TITLE, ATTR_MEDIA_TRACK,
|
||||||
ATTR_MEDIA_VOLUME_LEVEL, ATTR_MEDIA_VOLUME_MUTED,
|
ATTR_MEDIA_VOLUME_LEVEL, ATTR_MEDIA_VOLUME_MUTED, ATTR_INPUT_SOURCE_LIST,
|
||||||
ATTR_SUPPORTED_MEDIA_COMMANDS, DOMAIN, SERVICE_PLAY_MEDIA,
|
ATTR_SUPPORTED_MEDIA_COMMANDS, DOMAIN, SERVICE_PLAY_MEDIA,
|
||||||
SUPPORT_TURN_OFF, SUPPORT_TURN_ON, SUPPORT_VOLUME_MUTE, SUPPORT_VOLUME_SET,
|
SUPPORT_TURN_OFF, SUPPORT_TURN_ON, SUPPORT_VOLUME_MUTE, SUPPORT_VOLUME_SET,
|
||||||
SUPPORT_VOLUME_STEP, SUPPORT_SELECT_SOURCE, SUPPORT_CLEAR_PLAYLIST,
|
SUPPORT_VOLUME_STEP, SUPPORT_SELECT_SOURCE, SUPPORT_CLEAR_PLAYLIST,
|
||||||
@ -38,6 +38,7 @@ CONF_COMMANDS = 'commands'
|
|||||||
CONF_PLATFORM = 'platform'
|
CONF_PLATFORM = 'platform'
|
||||||
CONF_SERVICE = 'service'
|
CONF_SERVICE = 'service'
|
||||||
CONF_SERVICE_DATA = 'service_data'
|
CONF_SERVICE_DATA = 'service_data'
|
||||||
|
ATTR_DATA = 'data'
|
||||||
CONF_STATE = 'state'
|
CONF_STATE = 'state'
|
||||||
|
|
||||||
OFF_STATES = [STATE_IDLE, STATE_OFF]
|
OFF_STATES = [STATE_IDLE, STATE_OFF]
|
||||||
@ -178,14 +179,15 @@ class UniversalMediaPlayer(MediaPlayerDevice):
|
|||||||
def _call_service(self, service_name, service_data=None,
|
def _call_service(self, service_name, service_data=None,
|
||||||
allow_override=False):
|
allow_override=False):
|
||||||
"""Call either a specified or active child's service."""
|
"""Call either a specified or active child's service."""
|
||||||
if allow_override and service_name in self._cmds:
|
|
||||||
call_from_config(
|
|
||||||
self.hass, self._cmds[service_name], blocking=True)
|
|
||||||
return
|
|
||||||
|
|
||||||
if service_data is None:
|
if service_data is None:
|
||||||
service_data = {}
|
service_data = {}
|
||||||
|
|
||||||
|
if allow_override and service_name in self._cmds:
|
||||||
|
call_from_config(
|
||||||
|
self.hass, self._cmds[service_name],
|
||||||
|
variables=service_data, blocking=True)
|
||||||
|
return
|
||||||
|
|
||||||
active_child = self._child_state
|
active_child = self._child_state
|
||||||
service_data[ATTR_ENTITY_ID] = active_child.entity_id
|
service_data[ATTR_ENTITY_ID] = active_child.entity_id
|
||||||
|
|
||||||
@ -233,7 +235,7 @@ class UniversalMediaPlayer(MediaPlayerDevice):
|
|||||||
@property
|
@property
|
||||||
def volume_level(self):
|
def volume_level(self):
|
||||||
"""Volume level of entity specified in attributes or active child."""
|
"""Volume level of entity specified in attributes or active child."""
|
||||||
return self._child_attr(ATTR_MEDIA_VOLUME_LEVEL)
|
return self._override_or_child_attr(ATTR_MEDIA_VOLUME_LEVEL)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def is_volume_muted(self):
|
def is_volume_muted(self):
|
||||||
@ -322,9 +324,14 @@ class UniversalMediaPlayer(MediaPlayerDevice):
|
|||||||
return self._child_attr(ATTR_APP_NAME)
|
return self._child_attr(ATTR_APP_NAME)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def current_source(self):
|
def source(self):
|
||||||
""""Return the current input source of the device."""
|
""""Return the current input source of the device."""
|
||||||
return self._child_attr(ATTR_INPUT_SOURCE)
|
return self._override_or_child_attr(ATTR_INPUT_SOURCE)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def source_list(self):
|
||||||
|
"""List of available input sources."""
|
||||||
|
return self._override_or_child_attr(ATTR_INPUT_SOURCE_LIST)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def supported_media_commands(self):
|
def supported_media_commands(self):
|
||||||
@ -340,6 +347,8 @@ class UniversalMediaPlayer(MediaPlayerDevice):
|
|||||||
SERVICE_VOLUME_DOWN]]):
|
SERVICE_VOLUME_DOWN]]):
|
||||||
flags |= SUPPORT_VOLUME_STEP
|
flags |= SUPPORT_VOLUME_STEP
|
||||||
flags &= ~SUPPORT_VOLUME_SET
|
flags &= ~SUPPORT_VOLUME_SET
|
||||||
|
elif SERVICE_VOLUME_SET in self._cmds:
|
||||||
|
flags |= SUPPORT_VOLUME_SET
|
||||||
|
|
||||||
if SERVICE_VOLUME_MUTE in self._cmds and \
|
if SERVICE_VOLUME_MUTE in self._cmds and \
|
||||||
ATTR_MEDIA_VOLUME_MUTED in self._attrs:
|
ATTR_MEDIA_VOLUME_MUTED in self._attrs:
|
||||||
@ -376,7 +385,7 @@ class UniversalMediaPlayer(MediaPlayerDevice):
|
|||||||
def set_volume_level(self, volume_level):
|
def set_volume_level(self, volume_level):
|
||||||
"""Set volume level, range 0..1."""
|
"""Set volume level, range 0..1."""
|
||||||
data = {ATTR_MEDIA_VOLUME_LEVEL: volume_level}
|
data = {ATTR_MEDIA_VOLUME_LEVEL: volume_level}
|
||||||
self._call_service(SERVICE_VOLUME_SET, data)
|
self._call_service(SERVICE_VOLUME_SET, data, allow_override=True)
|
||||||
|
|
||||||
def media_play(self):
|
def media_play(self):
|
||||||
"""Send play commmand."""
|
"""Send play commmand."""
|
||||||
@ -424,7 +433,7 @@ class UniversalMediaPlayer(MediaPlayerDevice):
|
|||||||
def select_source(self, source):
|
def select_source(self, source):
|
||||||
"""Set the input source."""
|
"""Set the input source."""
|
||||||
data = {ATTR_INPUT_SOURCE: source}
|
data = {ATTR_INPUT_SOURCE: source}
|
||||||
self._call_service(SERVICE_SELECT_SOURCE, data)
|
self._call_service(SERVICE_SELECT_SOURCE, data, allow_override=True)
|
||||||
|
|
||||||
def clear_playlist(self):
|
def clear_playlist(self):
|
||||||
"""Clear players playlist."""
|
"""Clear players playlist."""
|
||||||
|
@ -5,6 +5,8 @@ import unittest
|
|||||||
from homeassistant.const import (
|
from homeassistant.const import (
|
||||||
STATE_OFF, STATE_ON, STATE_UNKNOWN, STATE_PLAYING, STATE_PAUSED)
|
STATE_OFF, STATE_ON, STATE_UNKNOWN, STATE_PLAYING, STATE_PAUSED)
|
||||||
import homeassistant.components.switch as switch
|
import homeassistant.components.switch as switch
|
||||||
|
import homeassistant.components.input_slider as input_slider
|
||||||
|
import homeassistant.components.input_select as input_select
|
||||||
import homeassistant.components.media_player as media_player
|
import homeassistant.components.media_player as media_player
|
||||||
import homeassistant.components.media_player.universal as universal
|
import homeassistant.components.media_player.universal as universal
|
||||||
|
|
||||||
@ -142,6 +144,17 @@ class TestMediaPlayer(unittest.TestCase):
|
|||||||
self.mock_state_switch_id = switch.ENTITY_ID_FORMAT.format('state')
|
self.mock_state_switch_id = switch.ENTITY_ID_FORMAT.format('state')
|
||||||
self.hass.states.set(self.mock_state_switch_id, STATE_OFF)
|
self.hass.states.set(self.mock_state_switch_id, STATE_OFF)
|
||||||
|
|
||||||
|
self.mock_volume_id = input_slider.ENTITY_ID_FORMAT.format(
|
||||||
|
'volume_level')
|
||||||
|
self.hass.states.set(self.mock_volume_id, 0)
|
||||||
|
|
||||||
|
self.mock_source_list_id = input_select.ENTITY_ID_FORMAT.format(
|
||||||
|
'source_list')
|
||||||
|
self.hass.states.set(self.mock_source_list_id, ['dvd', 'htpc'])
|
||||||
|
|
||||||
|
self.mock_source_id = input_select.ENTITY_ID_FORMAT.format('source')
|
||||||
|
self.hass.states.set(self.mock_source_id, 'dvd')
|
||||||
|
|
||||||
self.config_children_only = {
|
self.config_children_only = {
|
||||||
'name': 'test', 'platform': 'universal',
|
'name': 'test', 'platform': 'universal',
|
||||||
'children': [media_player.ENTITY_ID_FORMAT.format('mock1'),
|
'children': [media_player.ENTITY_ID_FORMAT.format('mock1'),
|
||||||
@ -153,6 +166,9 @@ class TestMediaPlayer(unittest.TestCase):
|
|||||||
media_player.ENTITY_ID_FORMAT.format('mock2')],
|
media_player.ENTITY_ID_FORMAT.format('mock2')],
|
||||||
'attributes': {
|
'attributes': {
|
||||||
'is_volume_muted': self.mock_mute_switch_id,
|
'is_volume_muted': self.mock_mute_switch_id,
|
||||||
|
'volume_level': self.mock_volume_id,
|
||||||
|
'source': self.mock_source_id,
|
||||||
|
'source_list': self.mock_source_list_id,
|
||||||
'state': self.mock_state_switch_id
|
'state': self.mock_state_switch_id
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -405,6 +421,42 @@ class TestMediaPlayer(unittest.TestCase):
|
|||||||
ump.update()
|
ump.update()
|
||||||
self.assertTrue(ump.is_volume_muted)
|
self.assertTrue(ump.is_volume_muted)
|
||||||
|
|
||||||
|
def test_source_list_children_and_attr(self):
|
||||||
|
"""Test source list property w/ children and attrs."""
|
||||||
|
config = self.config_children_and_attr
|
||||||
|
universal.validate_config(config)
|
||||||
|
|
||||||
|
ump = universal.UniversalMediaPlayer(self.hass, **config)
|
||||||
|
|
||||||
|
self.assertEqual("['dvd', 'htpc']", ump.source_list)
|
||||||
|
|
||||||
|
self.hass.states.set(self.mock_source_list_id, ['dvd', 'htpc', 'game'])
|
||||||
|
self.assertEqual("['dvd', 'htpc', 'game']", ump.source_list)
|
||||||
|
|
||||||
|
def test_source_children_and_attr(self):
|
||||||
|
"""Test source property w/ children and attrs."""
|
||||||
|
config = self.config_children_and_attr
|
||||||
|
universal.validate_config(config)
|
||||||
|
|
||||||
|
ump = universal.UniversalMediaPlayer(self.hass, **config)
|
||||||
|
|
||||||
|
self.assertEqual('dvd', ump.source)
|
||||||
|
|
||||||
|
self.hass.states.set(self.mock_source_id, 'htpc')
|
||||||
|
self.assertEqual('htpc', ump.source)
|
||||||
|
|
||||||
|
def test_volume_level_children_and_attr(self):
|
||||||
|
"""Test volume level property w/ children and attrs."""
|
||||||
|
config = self.config_children_and_attr
|
||||||
|
universal.validate_config(config)
|
||||||
|
|
||||||
|
ump = universal.UniversalMediaPlayer(self.hass, **config)
|
||||||
|
|
||||||
|
self.assertEqual('0', ump.volume_level)
|
||||||
|
|
||||||
|
self.hass.states.set(self.mock_volume_id, 100)
|
||||||
|
self.assertEqual('100', ump.volume_level)
|
||||||
|
|
||||||
def test_is_volume_muted_children_and_attr(self):
|
def test_is_volume_muted_children_and_attr(self):
|
||||||
"""Test is volume muted property w/ children and attrs."""
|
"""Test is volume muted property w/ children and attrs."""
|
||||||
config = self.config_children_and_attr
|
config = self.config_children_and_attr
|
||||||
@ -443,18 +495,20 @@ class TestMediaPlayer(unittest.TestCase):
|
|||||||
config['commands']['volume_up'] = 'test'
|
config['commands']['volume_up'] = 'test'
|
||||||
config['commands']['volume_down'] = 'test'
|
config['commands']['volume_down'] = 'test'
|
||||||
config['commands']['volume_mute'] = 'test'
|
config['commands']['volume_mute'] = 'test'
|
||||||
|
config['commands']['volume_set'] = 'test'
|
||||||
|
config['commands']['select_source'] = 'test'
|
||||||
|
|
||||||
ump = universal.UniversalMediaPlayer(self.hass, **config)
|
ump = universal.UniversalMediaPlayer(self.hass, **config)
|
||||||
ump.entity_id = media_player.ENTITY_ID_FORMAT.format(config['name'])
|
ump.entity_id = media_player.ENTITY_ID_FORMAT.format(config['name'])
|
||||||
ump.update()
|
ump.update()
|
||||||
|
|
||||||
self.mock_mp_1._supported_media_commands = universal.SUPPORT_VOLUME_SET
|
|
||||||
self.mock_mp_1._state = STATE_PLAYING
|
self.mock_mp_1._state = STATE_PLAYING
|
||||||
self.mock_mp_1.update_ha_state()
|
self.mock_mp_1.update_ha_state()
|
||||||
ump.update()
|
ump.update()
|
||||||
|
|
||||||
check_flags = universal.SUPPORT_TURN_ON | universal.SUPPORT_TURN_OFF \
|
check_flags = universal.SUPPORT_TURN_ON | universal.SUPPORT_TURN_OFF \
|
||||||
| universal.SUPPORT_VOLUME_STEP | universal.SUPPORT_VOLUME_MUTE
|
| universal.SUPPORT_VOLUME_STEP | universal.SUPPORT_VOLUME_MUTE \
|
||||||
|
| universal.SUPPORT_SELECT_SOURCE
|
||||||
|
|
||||||
self.assertEqual(check_flags, ump.supported_media_commands)
|
self.assertEqual(check_flags, ump.supported_media_commands)
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user