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:
Dan 2016-12-03 23:09:28 -05:00 committed by Paulus Schoutsen
parent 7746ecd98e
commit a099430834
2 changed files with 76 additions and 13 deletions

View File

@ -14,7 +14,7 @@ from homeassistant.components.media_player import (
ATTR_MEDIA_CONTENT_TYPE, ATTR_MEDIA_DURATION, ATTR_MEDIA_EPISODE,
ATTR_MEDIA_PLAYLIST, ATTR_MEDIA_SEASON, ATTR_MEDIA_SEEK_POSITION,
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,
SUPPORT_TURN_OFF, SUPPORT_TURN_ON, SUPPORT_VOLUME_MUTE, SUPPORT_VOLUME_SET,
SUPPORT_VOLUME_STEP, SUPPORT_SELECT_SOURCE, SUPPORT_CLEAR_PLAYLIST,
@ -38,6 +38,7 @@ CONF_COMMANDS = 'commands'
CONF_PLATFORM = 'platform'
CONF_SERVICE = 'service'
CONF_SERVICE_DATA = 'service_data'
ATTR_DATA = 'data'
CONF_STATE = 'state'
OFF_STATES = [STATE_IDLE, STATE_OFF]
@ -178,14 +179,15 @@ class UniversalMediaPlayer(MediaPlayerDevice):
def _call_service(self, service_name, service_data=None,
allow_override=False):
"""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:
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
service_data[ATTR_ENTITY_ID] = active_child.entity_id
@ -233,7 +235,7 @@ class UniversalMediaPlayer(MediaPlayerDevice):
@property
def volume_level(self):
"""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
def is_volume_muted(self):
@ -322,9 +324,14 @@ class UniversalMediaPlayer(MediaPlayerDevice):
return self._child_attr(ATTR_APP_NAME)
@property
def current_source(self):
def source(self):
""""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
def supported_media_commands(self):
@ -340,6 +347,8 @@ class UniversalMediaPlayer(MediaPlayerDevice):
SERVICE_VOLUME_DOWN]]):
flags |= SUPPORT_VOLUME_STEP
flags &= ~SUPPORT_VOLUME_SET
elif SERVICE_VOLUME_SET in self._cmds:
flags |= SUPPORT_VOLUME_SET
if SERVICE_VOLUME_MUTE in self._cmds and \
ATTR_MEDIA_VOLUME_MUTED in self._attrs:
@ -376,7 +385,7 @@ class UniversalMediaPlayer(MediaPlayerDevice):
def set_volume_level(self, volume_level):
"""Set volume level, range 0..1."""
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):
"""Send play commmand."""
@ -424,7 +433,7 @@ class UniversalMediaPlayer(MediaPlayerDevice):
def select_source(self, source):
"""Set the input 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):
"""Clear players playlist."""

View File

@ -5,6 +5,8 @@ import unittest
from homeassistant.const import (
STATE_OFF, STATE_ON, STATE_UNKNOWN, STATE_PLAYING, STATE_PAUSED)
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.universal as universal
@ -142,6 +144,17 @@ class TestMediaPlayer(unittest.TestCase):
self.mock_state_switch_id = switch.ENTITY_ID_FORMAT.format('state')
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 = {
'name': 'test', 'platform': 'universal',
'children': [media_player.ENTITY_ID_FORMAT.format('mock1'),
@ -153,6 +166,9 @@ class TestMediaPlayer(unittest.TestCase):
media_player.ENTITY_ID_FORMAT.format('mock2')],
'attributes': {
'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
}
}
@ -405,6 +421,42 @@ class TestMediaPlayer(unittest.TestCase):
ump.update()
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):
"""Test is volume muted property w/ children and attrs."""
config = self.config_children_and_attr
@ -443,18 +495,20 @@ class TestMediaPlayer(unittest.TestCase):
config['commands']['volume_up'] = 'test'
config['commands']['volume_down'] = 'test'
config['commands']['volume_mute'] = 'test'
config['commands']['volume_set'] = 'test'
config['commands']['select_source'] = 'test'
ump = universal.UniversalMediaPlayer(self.hass, **config)
ump.entity_id = media_player.ENTITY_ID_FORMAT.format(config['name'])
ump.update()
self.mock_mp_1._supported_media_commands = universal.SUPPORT_VOLUME_SET
self.mock_mp_1._state = STATE_PLAYING
self.mock_mp_1.update_ha_state()
ump.update()
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)