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

@ -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
https://home-assistant.io/components/media_player.universal/
"""
import asyncio
import logging
# pylint: disable=import-error
from copy import copy
from homeassistant.core import callback
from homeassistant.components.media_player import (
ATTR_APP_ID, ATTR_APP_NAME, ATTR_MEDIA_ALBUM_ARTIST, ATTR_MEDIA_ALBUM_NAME,
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_VOLUME_SET, SERVICE_VOLUME_UP, STATE_IDLE, STATE_OFF, STATE_ON,
SERVICE_MEDIA_STOP)
from homeassistant.helpers.event import track_state_change
from homeassistant.helpers.service import call_from_config
from homeassistant.helpers.event import async_track_state_change
from homeassistant.helpers.service import async_call_from_config
ATTR_ACTIVE_CHILD = 'active_child'
@ -47,18 +49,21 @@ REQUIREMENTS = []
_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."""
if not validate_config(config):
return
player = UniversalMediaPlayer(hass,
config[CONF_NAME],
config[CONF_CHILDREN],
config[CONF_COMMANDS],
config[CONF_ATTRS])
player = UniversalMediaPlayer(
hass,
config[CONF_NAME],
config[CONF_CHILDREN],
config[CONF_COMMANDS],
config[CONF_ATTRS]
)
add_devices([player])
yield from async_add_devices([player])
def validate_config(config):
@ -143,15 +148,16 @@ class UniversalMediaPlayer(MediaPlayerDevice):
self._attrs = attributes
self._child_state = None
def on_dependency_update(*_):
@callback
def async_on_dependency_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)
for entity in attributes.values():
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):
"""Look up an entity state."""
@ -177,14 +183,15 @@ class UniversalMediaPlayer(MediaPlayerDevice):
active_child = self._child_state
return active_child.attributes.get(attr_name) if active_child else None
def _call_service(self, service_name, service_data=None,
allow_override=False):
@asyncio.coroutine
def _async_call_service(self, service_name, service_data=None,
allow_override=False):
"""Call either a specified or active child's service."""
if service_data is None:
service_data = {}
if allow_override and service_name in self._cmds:
call_from_config(
yield from async_call_from_config(
self.hass, self._cmds[service_name],
variables=service_data, blocking=True)
return
@ -196,8 +203,8 @@ class UniversalMediaPlayer(MediaPlayerDevice):
service_data[ATTR_ENTITY_ID] = active_child.entity_id
self.hass.services.call(DOMAIN, service_name, service_data,
blocking=True)
yield from self.hass.services.async_call(
DOMAIN, service_name, service_data, blocking=True)
@property
def should_poll(self):
@ -395,77 +402,130 @@ class UniversalMediaPlayer(MediaPlayerDevice):
"""When was the position of the current playing media valid."""
return self._child_attr(ATTR_MEDIA_POSITION_UPDATED_AT)
def turn_on(self):
"""Turn the media player on."""
self._call_service(SERVICE_TURN_ON, allow_override=True)
def async_turn_on(self):
"""Turn the media player on.
def turn_off(self):
"""Turn the media player off."""
self._call_service(SERVICE_TURN_OFF, allow_override=True)
This method must be run in the event loop and returns a coroutine.
"""
return self._async_call_service(SERVICE_TURN_ON, allow_override=True)
def mute_volume(self, is_volume_muted):
"""Mute the volume."""
def async_turn_off(self):
"""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}
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):
"""Set volume level, range 0..1."""
def async_set_volume_level(self, volume_level):
"""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}
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):
"""Send play commmand."""
self._call_service(SERVICE_MEDIA_PLAY)
def async_media_play(self):
"""Send play commmand.
def media_pause(self):
"""Send pause command."""
self._call_service(SERVICE_MEDIA_PAUSE)
This method must be run in the event loop and returns a coroutine.
"""
return self._async_call_service(SERVICE_MEDIA_PLAY)
def media_stop(self):
"""Send stop command."""
self._call_service(SERVICE_MEDIA_STOP)
def async_media_pause(self):
"""Send pause command.
def media_previous_track(self):
"""Send previous track command."""
self._call_service(SERVICE_MEDIA_PREVIOUS_TRACK)
This method must be run in the event loop and returns a coroutine.
"""
return self._async_call_service(SERVICE_MEDIA_PAUSE)
def media_next_track(self):
"""Send next track command."""
self._call_service(SERVICE_MEDIA_NEXT_TRACK)
def async_media_stop(self):
"""Send stop command.
def media_seek(self, position):
"""Send seek command."""
This method must be run in the event loop and returns a coroutine.
"""
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}
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):
"""Play a piece of media."""
def async_play_media(self, media_type, media_id, **kwargs):
"""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,
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):
"""Turn volume up for media player."""
self._call_service(SERVICE_VOLUME_UP, allow_override=True)
def async_volume_up(self):
"""Turn volume up for media player.
def volume_down(self):
"""Turn volume down for media player."""
self._call_service(SERVICE_VOLUME_DOWN, allow_override=True)
This method must be run in the event loop and returns a coroutine.
"""
return self._async_call_service(SERVICE_VOLUME_UP, allow_override=True)
def media_play_pause(self):
"""Play or pause the media player."""
self._call_service(SERVICE_MEDIA_PLAY_PAUSE)
def async_volume_down(self):
"""Turn volume down for media player.
def select_source(self, source):
"""Set the input source."""
This method must be run in the event loop and returns a coroutine.
"""
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}
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):
"""Clear players playlist."""
self._call_service(SERVICE_CLEAR_PLAYLIST)
def async_clear_playlist(self):
"""Clear players 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."""
for child_name in self._children:
child_state = self.hass.states.get(child_name)