Cleanup media_player universal platform / blocking platform. (#5671)

* Cleanup media_player universal platform / blocking platform.

* fix comments

* fix unittest

* fix lint

* fix unittest lint

* fix coro

* fix test

* fix tests part 2
This commit is contained in:
Pascal Vizeli 2017-02-02 06:45:19 +01:00 committed by Paulus Schoutsen
parent 10a104271e
commit eefb603f17
5 changed files with 299 additions and 181 deletions

View File

@ -311,13 +311,13 @@ class GPMDP(MediaPlayerDevice):
"""Send media_play command to media player.""" """Send media_play command to media player."""
self.send_gpmdp_msg('playback', 'playPause', False) self.send_gpmdp_msg('playback', 'playPause', False)
self._status = STATE_PLAYING self._status = STATE_PLAYING
self.update_ha_state() self.schedule_update_ha_state()
def media_pause(self): def media_pause(self):
"""Send media_pause command to media player.""" """Send media_pause command to media player."""
self.send_gpmdp_msg('playback', 'playPause', False) self.send_gpmdp_msg('playback', 'playPause', False)
self._status = STATE_PAUSED self._status = STATE_PAUSED
self.update_ha_state() self.schedule_update_ha_state()
def media_seek(self, position): def media_seek(self, position):
"""Send media_seek command to media player.""" """Send media_seek command to media player."""
@ -327,7 +327,7 @@ class GPMDP(MediaPlayerDevice):
websocket.send(json.dumps({'namespace': 'playback', websocket.send(json.dumps({'namespace': 'playback',
'method': 'setCurrentTime', 'method': 'setCurrentTime',
'arguments': [position*1000]})) 'arguments': [position*1000]}))
self.update_ha_state() self.schedule_update_ha_state()
def volume_up(self): def volume_up(self):
"""Send volume_up command to media player.""" """Send volume_up command to media player."""
@ -335,7 +335,7 @@ class GPMDP(MediaPlayerDevice):
if websocket is None: if websocket is None:
return return
websocket.send('{"namespace": "volume", "method": "increaseVolume"}') websocket.send('{"namespace": "volume", "method": "increaseVolume"}')
self.update_ha_state() self.schedule_update_ha_state()
def volume_down(self): def volume_down(self):
"""Send volume_down command to media player.""" """Send volume_down command to media player."""
@ -343,7 +343,7 @@ class GPMDP(MediaPlayerDevice):
if websocket is None: if websocket is None:
return return
websocket.send('{"namespace": "volume", "method": "decreaseVolume"}') websocket.send('{"namespace": "volume", "method": "decreaseVolume"}')
self.update_ha_state() self.schedule_update_ha_state()
def set_volume_level(self, volume): def set_volume_level(self, volume):
"""Set volume on media player, range(0..1).""" """Set volume on media player, range(0..1)."""
@ -353,4 +353,4 @@ class GPMDP(MediaPlayerDevice):
websocket.send(json.dumps({'namespace': 'volume', websocket.send(json.dumps({'namespace': 'volume',
'method': 'setVolume', 'method': 'setVolume',
'arguments': [volume*100]})) 'arguments': [volume*100]}))
self.update_ha_state() self.schedule_update_ha_state()

View File

@ -438,13 +438,13 @@ class AirPlayDevice(MediaPlayerDevice):
def turn_on(self): def turn_on(self):
"""Select AirPlay.""" """Select AirPlay."""
self.update_state({"selected": True}) self.update_state({"selected": True})
self.update_ha_state() self.schedule_update_ha_state()
response = self.client.toggle_airplay_device(self._id, True) response = self.client.toggle_airplay_device(self._id, True)
self.update_state(response) self.update_state(response)
def turn_off(self): def turn_off(self):
"""Deselect AirPlay.""" """Deselect AirPlay."""
self.update_state({"selected": False}) self.update_state({"selected": False})
self.update_ha_state() self.schedule_update_ha_state()
response = self.client.toggle_airplay_device(self._id, False) response = self.client.toggle_airplay_device(self._id, False)
self.update_state(response) self.update_state(response)

View File

@ -201,10 +201,6 @@ class SqueezeBoxDevice(MediaPlayerDevice):
return self._lms.async_query( return self._lms.async_query(
*parameters, player=self._id) *parameters, player=self._id)
def query(self, *parameters):
"""Queue up a command to send the LMS."""
self.hass.loop.create_task(self.async_query(*parameters))
@asyncio.coroutine @asyncio.coroutine
def async_update(self): def async_update(self):
"""Retrieve the current state of the player.""" """Retrieve the current state of the player."""
@ -310,85 +306,108 @@ class SqueezeBoxDevice(MediaPlayerDevice):
"""Flag of media commands that are supported.""" """Flag of media commands that are supported."""
return SUPPORT_SQUEEZEBOX return SUPPORT_SQUEEZEBOX
def turn_off(self): def async_turn_off(self):
"""Turn off media player.""" """Turn off media player.
self.query('power', '0')
self.update_ha_state()
def volume_up(self): This method must be run in the event loop and returns a coroutine.
"""Volume up media player.""" """
self.query('mixer', 'volume', '+5') return self.async_query('power', '0')
self.update_ha_state()
def volume_down(self): def async_volume_up(self):
"""Volume down media player.""" """Volume up media player.
self.query('mixer', 'volume', '-5')
self.update_ha_state()
def set_volume_level(self, volume): This method must be run in the event loop and returns a coroutine.
"""Set volume level, range 0..1.""" """
return self.async_query('mixer', 'volume', '+5')
def async_volume_down(self):
"""Volume down media player.
This method must be run in the event loop and returns a coroutine.
"""
return self.async_query('mixer', 'volume', '-5')
def async_set_volume_level(self, volume):
"""Set volume level, range 0..1.
This method must be run in the event loop and returns a coroutine.
"""
volume_percent = str(int(volume*100)) volume_percent = str(int(volume*100))
self.query('mixer', 'volume', volume_percent) return self.async_query('mixer', 'volume', volume_percent)
self.update_ha_state()
def mute_volume(self, mute): def async_mute_volume(self, mute):
"""Mute (true) or unmute (false) media player.""" """Mute (true) or unmute (false) media player.
This method must be run in the event loop and returns a coroutine.
"""
mute_numeric = '1' if mute else '0' mute_numeric = '1' if mute else '0'
self.query('mixer', 'muting', mute_numeric) return self.async_query('mixer', 'muting', mute_numeric)
self.update_ha_state()
def media_play_pause(self): def async_media_play_pause(self):
"""Send pause command to media player.""" """Send pause command to media player.
self.query('pause')
self.update_ha_state()
def media_play(self): This method must be run in the event loop and returns a coroutine.
"""Send play command to media player.""" """
self.query('play') return self.async_query('pause')
self.update_ha_state()
def media_pause(self): def async_media_play(self):
"""Send pause command to media player.""" """Send play command to media player.
self.query('pause', '1')
self.update_ha_state()
def media_next_track(self): This method must be run in the event loop and returns a coroutine.
"""Send next track command.""" """
self.query('playlist', 'index', '+1') return self.async_query('play')
self.update_ha_state()
def media_previous_track(self): def async_media_pause(self):
"""Send next track command.""" """Send pause command to media player.
self.query('playlist', 'index', '-1')
self.update_ha_state()
def media_seek(self, position): This method must be run in the event loop and returns a coroutine.
"""Send seek command.""" """
self.query('time', position) return self.async_query('pause', '1')
self.update_ha_state()
def turn_on(self): def async_media_next_track(self):
"""Turn the media player on.""" """Send next track command.
self.query('power', '1')
self.update_ha_state()
def play_media(self, media_type, media_id, **kwargs): This method must be run in the event loop and returns a coroutine.
"""
return self.async_query('playlist', 'index', '+1')
def async_media_previous_track(self):
"""Send next track command.
This method must be run in the event loop and returns a coroutine.
"""
return self.async_query('playlist', 'index', '-1')
def async_media_seek(self, position):
"""Send seek command.
This method must be run in the event loop and returns a coroutine.
"""
return self.async_query('time', position)
def async_turn_on(self):
"""Turn the media player on.
This method must be run in the event loop and returns a coroutine.
"""
return self.async_query('power', '1')
def async_play_media(self, media_type, media_id, **kwargs):
""" """
Send the play_media command to the media player. Send the play_media command to the media player.
If ATTR_MEDIA_ENQUEUE is True, add `media_id` to the current playlist. If ATTR_MEDIA_ENQUEUE is True, add `media_id` to the current playlist.
This method must be run in the event loop and returns a coroutine.
""" """
if kwargs.get(ATTR_MEDIA_ENQUEUE): if kwargs.get(ATTR_MEDIA_ENQUEUE):
self._add_uri_to_playlist(media_id) return self._add_uri_to_playlist(media_id)
else:
self._play_uri(media_id) return self._play_uri(media_id)
def _play_uri(self, media_id): def _play_uri(self, media_id):
"""Replace the current play list with the uri.""" """Replace the current play list with the uri."""
self.query('playlist', 'play', media_id) return self.async_query('playlist', 'play', media_id)
self.update_ha_state()
def _add_uri_to_playlist(self, media_id): def _add_uri_to_playlist(self, media_id):
"""Add a items to the existing playlist.""" """Add a items to the existing playlist."""
self.query('playlist', 'add', media_id) return self.async_query('playlist', 'add', media_id)
self.update_ha_state()

View File

@ -4,10 +4,12 @@ Combination of multiple media players into one for a universal controller.
For more details about this platform, please refer to the documentation at For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/media_player.universal/ https://home-assistant.io/components/media_player.universal/
""" """
import asyncio
import logging import logging
# pylint: disable=import-error # pylint: disable=import-error
from copy import copy from copy import copy
from homeassistant.core import callback
from homeassistant.components.media_player import ( from homeassistant.components.media_player import (
ATTR_APP_ID, ATTR_APP_NAME, ATTR_MEDIA_ALBUM_ARTIST, ATTR_MEDIA_ALBUM_NAME, ATTR_APP_ID, ATTR_APP_NAME, ATTR_MEDIA_ALBUM_ARTIST, ATTR_MEDIA_ALBUM_NAME,
ATTR_MEDIA_ARTIST, ATTR_MEDIA_CHANNEL, ATTR_MEDIA_CONTENT_ID, ATTR_MEDIA_ARTIST, ATTR_MEDIA_CHANNEL, ATTR_MEDIA_CONTENT_ID,
@ -28,8 +30,8 @@ from homeassistant.const import (
SERVICE_TURN_ON, SERVICE_VOLUME_DOWN, SERVICE_VOLUME_MUTE, SERVICE_TURN_ON, SERVICE_VOLUME_DOWN, SERVICE_VOLUME_MUTE,
SERVICE_VOLUME_SET, SERVICE_VOLUME_UP, STATE_IDLE, STATE_OFF, STATE_ON, SERVICE_VOLUME_SET, SERVICE_VOLUME_UP, STATE_IDLE, STATE_OFF, STATE_ON,
SERVICE_MEDIA_STOP) SERVICE_MEDIA_STOP)
from homeassistant.helpers.event import track_state_change from homeassistant.helpers.event import async_track_state_change
from homeassistant.helpers.service import call_from_config from homeassistant.helpers.service import async_call_from_config
ATTR_ACTIVE_CHILD = 'active_child' ATTR_ACTIVE_CHILD = 'active_child'
@ -47,18 +49,21 @@ REQUIREMENTS = []
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
def setup_platform(hass, config, add_devices, discovery_info=None): @asyncio.coroutine
def async_setup_platform(hass, config, async_add_devices, discovery_info=None):
"""Setup the universal media players.""" """Setup the universal media players."""
if not validate_config(config): if not validate_config(config):
return return
player = UniversalMediaPlayer(hass, player = UniversalMediaPlayer(
config[CONF_NAME], hass,
config[CONF_CHILDREN], config[CONF_NAME],
config[CONF_COMMANDS], config[CONF_CHILDREN],
config[CONF_ATTRS]) config[CONF_COMMANDS],
config[CONF_ATTRS]
)
add_devices([player]) yield from async_add_devices([player])
def validate_config(config): def validate_config(config):
@ -143,15 +148,16 @@ class UniversalMediaPlayer(MediaPlayerDevice):
self._attrs = attributes self._attrs = attributes
self._child_state = None self._child_state = None
def on_dependency_update(*_): @callback
def async_on_dependency_update(*_):
"""Update ha state when dependencies update.""" """Update ha state when dependencies update."""
self.update_ha_state(True) self.hass.add_job(self.async_update_ha_state(True))
depend = copy(children) depend = copy(children)
for entity in attributes.values(): for entity in attributes.values():
depend.append(entity[0]) depend.append(entity[0])
track_state_change(hass, depend, on_dependency_update) async_track_state_change(hass, depend, async_on_dependency_update)
def _entity_lkp(self, entity_id, state_attr=None): def _entity_lkp(self, entity_id, state_attr=None):
"""Look up an entity state.""" """Look up an entity state."""
@ -177,14 +183,15 @@ class UniversalMediaPlayer(MediaPlayerDevice):
active_child = self._child_state active_child = self._child_state
return active_child.attributes.get(attr_name) if active_child else None return active_child.attributes.get(attr_name) if active_child else None
def _call_service(self, service_name, service_data=None, @asyncio.coroutine
allow_override=False): def _async_call_service(self, service_name, service_data=None,
allow_override=False):
"""Call either a specified or active child's service.""" """Call either a specified or active child's service."""
if service_data is None: if service_data is None:
service_data = {} service_data = {}
if allow_override and service_name in self._cmds: if allow_override and service_name in self._cmds:
call_from_config( yield from async_call_from_config(
self.hass, self._cmds[service_name], self.hass, self._cmds[service_name],
variables=service_data, blocking=True) variables=service_data, blocking=True)
return return
@ -196,8 +203,8 @@ class UniversalMediaPlayer(MediaPlayerDevice):
service_data[ATTR_ENTITY_ID] = active_child.entity_id service_data[ATTR_ENTITY_ID] = active_child.entity_id
self.hass.services.call(DOMAIN, service_name, service_data, yield from self.hass.services.async_call(
blocking=True) DOMAIN, service_name, service_data, blocking=True)
@property @property
def should_poll(self): def should_poll(self):
@ -395,77 +402,130 @@ class UniversalMediaPlayer(MediaPlayerDevice):
"""When was the position of the current playing media valid.""" """When was the position of the current playing media valid."""
return self._child_attr(ATTR_MEDIA_POSITION_UPDATED_AT) return self._child_attr(ATTR_MEDIA_POSITION_UPDATED_AT)
def turn_on(self): def async_turn_on(self):
"""Turn the media player on.""" """Turn the media player on.
self._call_service(SERVICE_TURN_ON, allow_override=True)
def turn_off(self): This method must be run in the event loop and returns a coroutine.
"""Turn the media player off.""" """
self._call_service(SERVICE_TURN_OFF, allow_override=True) return self._async_call_service(SERVICE_TURN_ON, allow_override=True)
def mute_volume(self, is_volume_muted): def async_turn_off(self):
"""Mute the volume.""" """Turn the media player off.
This method must be run in the event loop and returns a coroutine.
"""
return self._async_call_service(SERVICE_TURN_OFF, allow_override=True)
def async_mute_volume(self, is_volume_muted):
"""Mute the volume.
This method must be run in the event loop and returns a coroutine.
"""
data = {ATTR_MEDIA_VOLUME_MUTED: is_volume_muted} data = {ATTR_MEDIA_VOLUME_MUTED: is_volume_muted}
self._call_service(SERVICE_VOLUME_MUTE, data, allow_override=True) return self._async_call_service(
SERVICE_VOLUME_MUTE, data, allow_override=True)
def set_volume_level(self, volume_level): def async_set_volume_level(self, volume_level):
"""Set volume level, range 0..1.""" """Set volume level, range 0..1.
This method must be run in the event loop and returns a coroutine.
"""
data = {ATTR_MEDIA_VOLUME_LEVEL: volume_level} data = {ATTR_MEDIA_VOLUME_LEVEL: volume_level}
self._call_service(SERVICE_VOLUME_SET, data, allow_override=True) return self._async_call_service(
SERVICE_VOLUME_SET, data, allow_override=True)
def media_play(self): def async_media_play(self):
"""Send play commmand.""" """Send play commmand.
self._call_service(SERVICE_MEDIA_PLAY)
def media_pause(self): This method must be run in the event loop and returns a coroutine.
"""Send pause command.""" """
self._call_service(SERVICE_MEDIA_PAUSE) return self._async_call_service(SERVICE_MEDIA_PLAY)
def media_stop(self): def async_media_pause(self):
"""Send stop command.""" """Send pause command.
self._call_service(SERVICE_MEDIA_STOP)
def media_previous_track(self): This method must be run in the event loop and returns a coroutine.
"""Send previous track command.""" """
self._call_service(SERVICE_MEDIA_PREVIOUS_TRACK) return self._async_call_service(SERVICE_MEDIA_PAUSE)
def media_next_track(self): def async_media_stop(self):
"""Send next track command.""" """Send stop command.
self._call_service(SERVICE_MEDIA_NEXT_TRACK)
def media_seek(self, position): This method must be run in the event loop and returns a coroutine.
"""Send seek command.""" """
return self._async_call_service(SERVICE_MEDIA_STOP)
def async_media_previous_track(self):
"""Send previous track command.
This method must be run in the event loop and returns a coroutine.
"""
return self._async_call_service(SERVICE_MEDIA_PREVIOUS_TRACK)
def async_media_next_track(self):
"""Send next track command.
This method must be run in the event loop and returns a coroutine.
"""
return self._async_call_service(SERVICE_MEDIA_NEXT_TRACK)
def async_media_seek(self, position):
"""Send seek command.
This method must be run in the event loop and returns a coroutine.
"""
data = {ATTR_MEDIA_SEEK_POSITION: position} data = {ATTR_MEDIA_SEEK_POSITION: position}
self._call_service(SERVICE_MEDIA_SEEK, data) return self._async_call_service(SERVICE_MEDIA_SEEK, data)
def play_media(self, media_type, media_id, **kwargs): def async_play_media(self, media_type, media_id, **kwargs):
"""Play a piece of media.""" """Play a piece of media.
This method must be run in the event loop and returns a coroutine.
"""
data = {ATTR_MEDIA_CONTENT_TYPE: media_type, data = {ATTR_MEDIA_CONTENT_TYPE: media_type,
ATTR_MEDIA_CONTENT_ID: media_id} ATTR_MEDIA_CONTENT_ID: media_id}
self._call_service(SERVICE_PLAY_MEDIA, data) return self._async_call_service(SERVICE_PLAY_MEDIA, data)
def volume_up(self): def async_volume_up(self):
"""Turn volume up for media player.""" """Turn volume up for media player.
self._call_service(SERVICE_VOLUME_UP, allow_override=True)
def volume_down(self): This method must be run in the event loop and returns a coroutine.
"""Turn volume down for media player.""" """
self._call_service(SERVICE_VOLUME_DOWN, allow_override=True) return self._async_call_service(SERVICE_VOLUME_UP, allow_override=True)
def media_play_pause(self): def async_volume_down(self):
"""Play or pause the media player.""" """Turn volume down for media player.
self._call_service(SERVICE_MEDIA_PLAY_PAUSE)
def select_source(self, source): This method must be run in the event loop and returns a coroutine.
"""Set the input source.""" """
return self._async_call_service(
SERVICE_VOLUME_DOWN, allow_override=True)
def async_media_play_pause(self):
"""Play or pause the media player.
This method must be run in the event loop and returns a coroutine.
"""
return self._async_call_service(SERVICE_MEDIA_PLAY_PAUSE)
def async_select_source(self, source):
"""Set the input source.
This method must be run in the event loop and returns a coroutine.
"""
data = {ATTR_INPUT_SOURCE: source} data = {ATTR_INPUT_SOURCE: source}
self._call_service(SERVICE_SELECT_SOURCE, data, allow_override=True) return self._async_call_service(
SERVICE_SELECT_SOURCE, data, allow_override=True)
def clear_playlist(self): def async_clear_playlist(self):
"""Clear players playlist.""" """Clear players playlist.
self._call_service(SERVICE_CLEAR_PLAYLIST)
def update(self): This method must be run in the event loop and returns a coroutine.
"""
return self._async_call_service(SERVICE_CLEAR_PLAYLIST)
@asyncio.coroutine
def async_update(self):
"""Update state in HA.""" """Update state in HA."""
for child_name in self._children: for child_name in self._children:
child_state = self.hass.states.get(child_name) child_state = self.hass.states.get(child_name)

View File

@ -1,4 +1,5 @@
"""The tests for the Universal Media player platform.""" """The tests for the Universal Media player platform."""
import asyncio
from copy import copy from copy import copy
import unittest import unittest
@ -9,6 +10,7 @@ import homeassistant.components.input_slider as input_slider
import homeassistant.components.input_select as input_select 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
from homeassistant.util.async import run_coroutine_threadsafe
from tests.common import mock_service, get_test_home_assistant from tests.common import mock_service, get_test_home_assistant
@ -256,15 +258,20 @@ class TestMediaPlayer(unittest.TestCase):
bad_config = {'platform': 'universal'} bad_config = {'platform': 'universal'}
entities = [] entities = []
@asyncio.coroutine
def add_devices(new_entities): def add_devices(new_entities):
"""Add devices to list.""" """Add devices to list."""
for dev in new_entities: for dev in new_entities:
entities.append(dev) entities.append(dev)
universal.setup_platform(self.hass, bad_config, add_devices) run_coroutine_threadsafe(
universal.async_setup_platform(self.hass, bad_config, add_devices),
self.hass.loop).result()
self.assertEqual(0, len(entities)) self.assertEqual(0, len(entities))
universal.setup_platform(self.hass, config, add_devices) run_coroutine_threadsafe(
universal.async_setup_platform(self.hass, config, add_devices),
self.hass.loop).result()
self.assertEqual(1, len(entities)) self.assertEqual(1, len(entities))
self.assertEqual('test', entities[0].name) self.assertEqual('test', entities[0].name)
@ -305,25 +312,25 @@ class TestMediaPlayer(unittest.TestCase):
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() run_coroutine_threadsafe(ump.async_update(), self.hass.loop).result()
self.assertEqual(None, ump._child_state) self.assertEqual(None, ump._child_state)
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() run_coroutine_threadsafe(ump.async_update(), self.hass.loop).result()
self.assertEqual(self.mock_mp_1.entity_id, self.assertEqual(self.mock_mp_1.entity_id,
ump._child_state.entity_id) ump._child_state.entity_id)
self.mock_mp_2._state = STATE_PLAYING self.mock_mp_2._state = STATE_PLAYING
self.mock_mp_2.update_ha_state() self.mock_mp_2.update_ha_state()
ump.update() run_coroutine_threadsafe(ump.async_update(), self.hass.loop).result()
self.assertEqual(self.mock_mp_1.entity_id, self.assertEqual(self.mock_mp_1.entity_id,
ump._child_state.entity_id) ump._child_state.entity_id)
self.mock_mp_1._state = STATE_OFF self.mock_mp_1._state = STATE_OFF
self.mock_mp_1.update_ha_state() self.mock_mp_1.update_ha_state()
ump.update() run_coroutine_threadsafe(ump.async_update(), self.hass.loop).result()
self.assertEqual(self.mock_mp_2.entity_id, self.assertEqual(self.mock_mp_2.entity_id,
ump._child_state.entity_id) ump._child_state.entity_id)
@ -352,13 +359,13 @@ class TestMediaPlayer(unittest.TestCase):
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() run_coroutine_threadsafe(ump.async_update(), self.hass.loop).result()
self.assertTrue(ump.state, STATE_OFF) self.assertTrue(ump.state, STATE_OFF)
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() run_coroutine_threadsafe(ump.async_update(), self.hass.loop).result()
self.assertEqual(STATE_PLAYING, ump.state) self.assertEqual(STATE_PLAYING, ump.state)
def test_state_with_children_and_attrs(self): def test_state_with_children_and_attrs(self):
@ -368,21 +375,21 @@ class TestMediaPlayer(unittest.TestCase):
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() run_coroutine_threadsafe(ump.async_update(), self.hass.loop).result()
self.assertEqual(STATE_OFF, ump.state) self.assertEqual(STATE_OFF, ump.state)
self.hass.states.set(self.mock_state_switch_id, STATE_ON) self.hass.states.set(self.mock_state_switch_id, STATE_ON)
ump.update() run_coroutine_threadsafe(ump.async_update(), self.hass.loop).result()
self.assertEqual(STATE_ON, ump.state) self.assertEqual(STATE_ON, ump.state)
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() run_coroutine_threadsafe(ump.async_update(), self.hass.loop).result()
self.assertEqual(STATE_PLAYING, ump.state) self.assertEqual(STATE_PLAYING, ump.state)
self.hass.states.set(self.mock_state_switch_id, STATE_OFF) self.hass.states.set(self.mock_state_switch_id, STATE_OFF)
ump.update() run_coroutine_threadsafe(ump.async_update(), self.hass.loop).result()
self.assertEqual(STATE_OFF, ump.state) self.assertEqual(STATE_OFF, ump.state)
def test_volume_level(self): def test_volume_level(self):
@ -392,18 +399,18 @@ class TestMediaPlayer(unittest.TestCase):
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() run_coroutine_threadsafe(ump.async_update(), self.hass.loop).result()
self.assertEqual(None, ump.volume_level) self.assertEqual(None, ump.volume_level)
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() run_coroutine_threadsafe(ump.async_update(), self.hass.loop).result()
self.assertEqual(0, ump.volume_level) self.assertEqual(0, ump.volume_level)
self.mock_mp_1._volume_level = 1 self.mock_mp_1._volume_level = 1
self.mock_mp_1.update_ha_state() self.mock_mp_1.update_ha_state()
ump.update() run_coroutine_threadsafe(ump.async_update(), self.hass.loop).result()
self.assertEqual(1, ump.volume_level) self.assertEqual(1, ump.volume_level)
def test_media_image_url(self): def test_media_image_url(self):
@ -414,14 +421,14 @@ class TestMediaPlayer(unittest.TestCase):
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() run_coroutine_threadsafe(ump.async_update(), self.hass.loop).result()
self.assertEqual(None, ump.media_image_url) self.assertEqual(None, ump.media_image_url)
self.mock_mp_1._state = STATE_PLAYING self.mock_mp_1._state = STATE_PLAYING
self.mock_mp_1._media_image_url = TEST_URL self.mock_mp_1._media_image_url = TEST_URL
self.mock_mp_1.update_ha_state() self.mock_mp_1.update_ha_state()
ump.update() run_coroutine_threadsafe(ump.async_update(), self.hass.loop).result()
# mock_mp_1 will convert the url to the api proxy url. This test # mock_mp_1 will convert the url to the api proxy url. This test
# ensures ump passes through the same url without an additional proxy. # ensures ump passes through the same url without an additional proxy.
self.assertEqual(self.mock_mp_1.entity_picture, ump.entity_picture) self.assertEqual(self.mock_mp_1.entity_picture, ump.entity_picture)
@ -433,18 +440,18 @@ class TestMediaPlayer(unittest.TestCase):
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() run_coroutine_threadsafe(ump.async_update(), self.hass.loop).result()
self.assertFalse(ump.is_volume_muted) self.assertFalse(ump.is_volume_muted)
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() run_coroutine_threadsafe(ump.async_update(), self.hass.loop).result()
self.assertFalse(ump.is_volume_muted) self.assertFalse(ump.is_volume_muted)
self.mock_mp_1._is_volume_muted = True self.mock_mp_1._is_volume_muted = True
self.mock_mp_1.update_ha_state() self.mock_mp_1.update_ha_state()
ump.update() run_coroutine_threadsafe(ump.async_update(), self.hass.loop).result()
self.assertTrue(ump.is_volume_muted) self.assertTrue(ump.is_volume_muted)
def test_source_list_children_and_attr(self): def test_source_list_children_and_attr(self):
@ -502,14 +509,14 @@ class TestMediaPlayer(unittest.TestCase):
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() run_coroutine_threadsafe(ump.async_update(), self.hass.loop).result()
self.assertEqual(0, ump.supported_media_commands) self.assertEqual(0, ump.supported_media_commands)
self.mock_mp_1._supported_media_commands = 512 self.mock_mp_1._supported_media_commands = 512
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() run_coroutine_threadsafe(ump.async_update(), self.hass.loop).result()
self.assertEqual(512, ump.supported_media_commands) self.assertEqual(512, ump.supported_media_commands)
def test_supported_media_commands_children_and_cmds(self): def test_supported_media_commands_children_and_cmds(self):
@ -526,11 +533,11 @@ class TestMediaPlayer(unittest.TestCase):
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() run_coroutine_threadsafe(ump.async_update(), self.hass.loop).result()
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() run_coroutine_threadsafe(ump.async_update(), self.hass.loop).result()
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 \
@ -545,15 +552,17 @@ class TestMediaPlayer(unittest.TestCase):
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() run_coroutine_threadsafe(ump.async_update(), self.hass.loop).result()
self.mock_mp_1._state = STATE_OFF self.mock_mp_1._state = STATE_OFF
self.mock_mp_1.update_ha_state() self.mock_mp_1.update_ha_state()
self.mock_mp_2._state = STATE_OFF self.mock_mp_2._state = STATE_OFF
self.mock_mp_2.update_ha_state() self.mock_mp_2.update_ha_state()
ump.update() run_coroutine_threadsafe(ump.async_update(), self.hass.loop).result()
ump.turn_off() run_coroutine_threadsafe(
ump.async_turn_off(),
self.hass.loop).result()
self.assertEqual(0, len(self.mock_mp_1.service_calls['turn_off'])) self.assertEqual(0, len(self.mock_mp_1.service_calls['turn_off']))
self.assertEqual(0, len(self.mock_mp_2.service_calls['turn_off'])) self.assertEqual(0, len(self.mock_mp_2.service_calls['turn_off']))
@ -564,60 +573,90 @@ class TestMediaPlayer(unittest.TestCase):
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() run_coroutine_threadsafe(ump.async_update(), self.hass.loop).result()
self.mock_mp_2._state = STATE_PLAYING self.mock_mp_2._state = STATE_PLAYING
self.mock_mp_2.update_ha_state() self.mock_mp_2.update_ha_state()
ump.update() run_coroutine_threadsafe(ump.async_update(), self.hass.loop).result()
ump.turn_off() run_coroutine_threadsafe(
ump.async_turn_off(),
self.hass.loop).result()
self.assertEqual(1, len(self.mock_mp_2.service_calls['turn_off'])) self.assertEqual(1, len(self.mock_mp_2.service_calls['turn_off']))
ump.turn_on() run_coroutine_threadsafe(
ump.async_turn_on(),
self.hass.loop).result()
self.assertEqual(1, len(self.mock_mp_2.service_calls['turn_on'])) self.assertEqual(1, len(self.mock_mp_2.service_calls['turn_on']))
ump.mute_volume(True) run_coroutine_threadsafe(
ump.async_mute_volume(True),
self.hass.loop).result()
self.assertEqual(1, len(self.mock_mp_2.service_calls['mute_volume'])) self.assertEqual(1, len(self.mock_mp_2.service_calls['mute_volume']))
ump.set_volume_level(0.5) run_coroutine_threadsafe(
ump.async_set_volume_level(0.5),
self.hass.loop).result()
self.assertEqual( self.assertEqual(
1, len(self.mock_mp_2.service_calls['set_volume_level'])) 1, len(self.mock_mp_2.service_calls['set_volume_level']))
ump.media_play() run_coroutine_threadsafe(
ump.async_media_play(),
self.hass.loop).result()
self.assertEqual(1, len(self.mock_mp_2.service_calls['media_play'])) self.assertEqual(1, len(self.mock_mp_2.service_calls['media_play']))
ump.media_pause() run_coroutine_threadsafe(
ump.async_media_pause(),
self.hass.loop).result()
self.assertEqual(1, len(self.mock_mp_2.service_calls['media_pause'])) self.assertEqual(1, len(self.mock_mp_2.service_calls['media_pause']))
ump.media_previous_track() run_coroutine_threadsafe(
ump.async_media_previous_track(),
self.hass.loop).result()
self.assertEqual( self.assertEqual(
1, len(self.mock_mp_2.service_calls['media_previous_track'])) 1, len(self.mock_mp_2.service_calls['media_previous_track']))
ump.media_next_track() run_coroutine_threadsafe(
ump.async_media_next_track(),
self.hass.loop).result()
self.assertEqual( self.assertEqual(
1, len(self.mock_mp_2.service_calls['media_next_track'])) 1, len(self.mock_mp_2.service_calls['media_next_track']))
ump.media_seek(100) run_coroutine_threadsafe(
ump.async_media_seek(100),
self.hass.loop).result()
self.assertEqual(1, len(self.mock_mp_2.service_calls['media_seek'])) self.assertEqual(1, len(self.mock_mp_2.service_calls['media_seek']))
ump.play_media('movie', 'batman') run_coroutine_threadsafe(
ump.async_play_media('movie', 'batman'),
self.hass.loop).result()
self.assertEqual(1, len(self.mock_mp_2.service_calls['play_media'])) self.assertEqual(1, len(self.mock_mp_2.service_calls['play_media']))
ump.volume_up() run_coroutine_threadsafe(
ump.async_volume_up(),
self.hass.loop).result()
self.assertEqual(1, len(self.mock_mp_2.service_calls['volume_up'])) self.assertEqual(1, len(self.mock_mp_2.service_calls['volume_up']))
ump.volume_down() run_coroutine_threadsafe(
ump.async_volume_down(),
self.hass.loop).result()
self.assertEqual(1, len(self.mock_mp_2.service_calls['volume_down'])) self.assertEqual(1, len(self.mock_mp_2.service_calls['volume_down']))
ump.media_play_pause() run_coroutine_threadsafe(
ump.async_media_play_pause(),
self.hass.loop).result()
self.assertEqual( self.assertEqual(
1, len(self.mock_mp_2.service_calls['media_play_pause'])) 1, len(self.mock_mp_2.service_calls['media_play_pause']))
ump.select_source('dvd') run_coroutine_threadsafe(
ump.async_select_source('dvd'),
self.hass.loop).result()
self.assertEqual( self.assertEqual(
1, len(self.mock_mp_2.service_calls['select_source'])) 1, len(self.mock_mp_2.service_calls['select_source']))
ump.clear_playlist() run_coroutine_threadsafe(
ump.async_clear_playlist(),
self.hass.loop).result()
self.assertEqual( self.assertEqual(
1, len(self.mock_mp_2.service_calls['clear_playlist'])) 1, len(self.mock_mp_2.service_calls['clear_playlist']))
@ -632,11 +671,11 @@ class TestMediaPlayer(unittest.TestCase):
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() run_coroutine_threadsafe(ump.async_update(), self.hass.loop).result()
self.mock_mp_2._state = STATE_PLAYING self.mock_mp_2._state = STATE_PLAYING
self.mock_mp_2.update_ha_state() self.mock_mp_2.update_ha_state()
ump.update() run_coroutine_threadsafe(ump.async_update(), self.hass.loop).result()
ump.turn_off() run_coroutine_threadsafe(ump.async_turn_off(), self.hass.loop).result()
self.assertEqual(1, len(service)) self.assertEqual(1, len(service))