mirror of
https://github.com/home-assistant/core.git
synced 2025-07-19 03:07:37 +00:00
Async support in media player component (#4995)
* Async support in media player component * Removed redundant new tests * Update to new 'reduce coroutine' standards * Remove extra create_task
This commit is contained in:
parent
b78cf4772d
commit
2a7a419ff3
@ -5,6 +5,7 @@ For more details about this component, please refer to the documentation at
|
|||||||
https://home-assistant.io/components/media_player/
|
https://home-assistant.io/components/media_player/
|
||||||
"""
|
"""
|
||||||
import asyncio
|
import asyncio
|
||||||
|
import functools as ft
|
||||||
import hashlib
|
import hashlib
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
@ -100,20 +101,62 @@ SUPPORT_SELECT_SOURCE = 2048
|
|||||||
SUPPORT_STOP = 4096
|
SUPPORT_STOP = 4096
|
||||||
SUPPORT_CLEAR_PLAYLIST = 8192
|
SUPPORT_CLEAR_PLAYLIST = 8192
|
||||||
|
|
||||||
# simple services that only take entity_id(s) as optional argument
|
# Service call validation schemas
|
||||||
|
MEDIA_PLAYER_SCHEMA = vol.Schema({
|
||||||
|
ATTR_ENTITY_ID: cv.entity_ids,
|
||||||
|
})
|
||||||
|
|
||||||
|
MEDIA_PLAYER_SET_VOLUME_SCHEMA = MEDIA_PLAYER_SCHEMA.extend({
|
||||||
|
vol.Required(ATTR_MEDIA_VOLUME_LEVEL): cv.small_float,
|
||||||
|
})
|
||||||
|
|
||||||
|
MEDIA_PLAYER_MUTE_VOLUME_SCHEMA = MEDIA_PLAYER_SCHEMA.extend({
|
||||||
|
vol.Required(ATTR_MEDIA_VOLUME_MUTED): cv.boolean,
|
||||||
|
})
|
||||||
|
|
||||||
|
MEDIA_PLAYER_MEDIA_SEEK_SCHEMA = MEDIA_PLAYER_SCHEMA.extend({
|
||||||
|
vol.Required(ATTR_MEDIA_SEEK_POSITION):
|
||||||
|
vol.All(vol.Coerce(float), vol.Range(min=0)),
|
||||||
|
})
|
||||||
|
|
||||||
|
MEDIA_PLAYER_SELECT_SOURCE_SCHEMA = MEDIA_PLAYER_SCHEMA.extend({
|
||||||
|
vol.Required(ATTR_INPUT_SOURCE): cv.string,
|
||||||
|
})
|
||||||
|
|
||||||
|
MEDIA_PLAYER_PLAY_MEDIA_SCHEMA = MEDIA_PLAYER_SCHEMA.extend({
|
||||||
|
vol.Required(ATTR_MEDIA_CONTENT_TYPE): cv.string,
|
||||||
|
vol.Required(ATTR_MEDIA_CONTENT_ID): cv.string,
|
||||||
|
vol.Optional(ATTR_MEDIA_ENQUEUE): cv.boolean,
|
||||||
|
})
|
||||||
|
|
||||||
SERVICE_TO_METHOD = {
|
SERVICE_TO_METHOD = {
|
||||||
SERVICE_TURN_ON: 'turn_on',
|
SERVICE_TURN_ON: {'method': 'async_turn_on'},
|
||||||
SERVICE_TURN_OFF: 'turn_off',
|
SERVICE_TURN_OFF: {'method': 'async_turn_off'},
|
||||||
SERVICE_TOGGLE: 'toggle',
|
SERVICE_TOGGLE: {'method': 'async_toggle'},
|
||||||
SERVICE_VOLUME_UP: 'volume_up',
|
SERVICE_VOLUME_UP: {'method': 'async_volume_up'},
|
||||||
SERVICE_VOLUME_DOWN: 'volume_down',
|
SERVICE_VOLUME_DOWN: {'method': 'async_volume_down'},
|
||||||
SERVICE_MEDIA_PLAY_PAUSE: 'media_play_pause',
|
SERVICE_MEDIA_PLAY_PAUSE: {'method': 'async_media_play_pause'},
|
||||||
SERVICE_MEDIA_PLAY: 'media_play',
|
SERVICE_MEDIA_PLAY: {'method': 'async_media_play'},
|
||||||
SERVICE_MEDIA_PAUSE: 'media_pause',
|
SERVICE_MEDIA_PAUSE: {'method': 'async_media_pause'},
|
||||||
SERVICE_MEDIA_STOP: 'media_stop',
|
SERVICE_MEDIA_STOP: {'method': 'async_media_stop'},
|
||||||
SERVICE_MEDIA_NEXT_TRACK: 'media_next_track',
|
SERVICE_MEDIA_NEXT_TRACK: {'method': 'async_media_next_track'},
|
||||||
SERVICE_MEDIA_PREVIOUS_TRACK: 'media_previous_track',
|
SERVICE_MEDIA_PREVIOUS_TRACK: {'method': 'async_media_previous_track'},
|
||||||
SERVICE_CLEAR_PLAYLIST: 'clear_playlist'
|
SERVICE_CLEAR_PLAYLIST: {'method': 'async_clear_playlist'},
|
||||||
|
SERVICE_VOLUME_SET: {
|
||||||
|
'method': 'async_set_volume_level',
|
||||||
|
'schema': MEDIA_PLAYER_SET_VOLUME_SCHEMA},
|
||||||
|
SERVICE_VOLUME_MUTE: {
|
||||||
|
'method': 'async_mute_volume',
|
||||||
|
'schema': MEDIA_PLAYER_MUTE_VOLUME_SCHEMA},
|
||||||
|
SERVICE_MEDIA_SEEK: {
|
||||||
|
'method': 'async_media_seek',
|
||||||
|
'schema': MEDIA_PLAYER_MEDIA_SEEK_SCHEMA},
|
||||||
|
SERVICE_SELECT_SOURCE: {
|
||||||
|
'method': 'async_select_source',
|
||||||
|
'schema': MEDIA_PLAYER_SELECT_SOURCE_SCHEMA},
|
||||||
|
SERVICE_PLAY_MEDIA: {
|
||||||
|
'method': 'async_play_media',
|
||||||
|
'schema': MEDIA_PLAYER_PLAY_MEDIA_SCHEMA},
|
||||||
}
|
}
|
||||||
|
|
||||||
ATTR_TO_PROPERTY = [
|
ATTR_TO_PROPERTY = [
|
||||||
@ -141,34 +184,6 @@ ATTR_TO_PROPERTY = [
|
|||||||
ATTR_INPUT_SOURCE_LIST,
|
ATTR_INPUT_SOURCE_LIST,
|
||||||
]
|
]
|
||||||
|
|
||||||
# Service call validation schemas
|
|
||||||
MEDIA_PLAYER_SCHEMA = vol.Schema({
|
|
||||||
ATTR_ENTITY_ID: cv.entity_ids,
|
|
||||||
})
|
|
||||||
|
|
||||||
MEDIA_PLAYER_MUTE_VOLUME_SCHEMA = MEDIA_PLAYER_SCHEMA.extend({
|
|
||||||
vol.Required(ATTR_MEDIA_VOLUME_MUTED): cv.boolean,
|
|
||||||
})
|
|
||||||
|
|
||||||
MEDIA_PLAYER_SET_VOLUME_SCHEMA = MEDIA_PLAYER_SCHEMA.extend({
|
|
||||||
vol.Required(ATTR_MEDIA_VOLUME_LEVEL): cv.small_float,
|
|
||||||
})
|
|
||||||
|
|
||||||
MEDIA_PLAYER_MEDIA_SEEK_SCHEMA = MEDIA_PLAYER_SCHEMA.extend({
|
|
||||||
vol.Required(ATTR_MEDIA_SEEK_POSITION):
|
|
||||||
vol.All(vol.Coerce(float), vol.Range(min=0)),
|
|
||||||
})
|
|
||||||
|
|
||||||
MEDIA_PLAYER_PLAY_MEDIA_SCHEMA = MEDIA_PLAYER_SCHEMA.extend({
|
|
||||||
vol.Required(ATTR_MEDIA_CONTENT_TYPE): cv.string,
|
|
||||||
vol.Required(ATTR_MEDIA_CONTENT_ID): cv.string,
|
|
||||||
vol.Optional(ATTR_MEDIA_ENQUEUE): cv.boolean,
|
|
||||||
})
|
|
||||||
|
|
||||||
MEDIA_PLAYER_SELECT_SOURCE_SCHEMA = MEDIA_PLAYER_SCHEMA.extend({
|
|
||||||
vol.Required(ATTR_INPUT_SOURCE): cv.string,
|
|
||||||
})
|
|
||||||
|
|
||||||
|
|
||||||
def is_on(hass, entity_id=None):
|
def is_on(hass, entity_id=None):
|
||||||
"""
|
"""
|
||||||
@ -304,109 +319,67 @@ def clear_playlist(hass, entity_id=None):
|
|||||||
hass.services.call(DOMAIN, SERVICE_CLEAR_PLAYLIST, data)
|
hass.services.call(DOMAIN, SERVICE_CLEAR_PLAYLIST, data)
|
||||||
|
|
||||||
|
|
||||||
def setup(hass, config):
|
@asyncio.coroutine
|
||||||
|
def async_setup(hass, config):
|
||||||
"""Track states and offer events for media_players."""
|
"""Track states and offer events for media_players."""
|
||||||
component = EntityComponent(
|
component = EntityComponent(
|
||||||
logging.getLogger(__name__), DOMAIN, hass, SCAN_INTERVAL)
|
logging.getLogger(__name__), DOMAIN, hass, SCAN_INTERVAL)
|
||||||
|
|
||||||
hass.http.register_view(MediaPlayerImageView(component.entities))
|
hass.http.register_view(MediaPlayerImageView(component.entities))
|
||||||
|
|
||||||
component.setup(config)
|
yield from component.async_setup(config)
|
||||||
|
|
||||||
descriptions = load_yaml_config_file(
|
descriptions = yield from hass.loop.run_in_executor(
|
||||||
os.path.join(os.path.dirname(__file__), 'services.yaml'))
|
None, load_yaml_config_file, os.path.join(
|
||||||
|
os.path.dirname(__file__), 'services.yaml'))
|
||||||
|
|
||||||
def media_player_service_handler(service):
|
@asyncio.coroutine
|
||||||
|
def async_service_handler(service):
|
||||||
"""Map services to methods on MediaPlayerDevice."""
|
"""Map services to methods on MediaPlayerDevice."""
|
||||||
method = SERVICE_TO_METHOD[service.service]
|
method = SERVICE_TO_METHOD.get(service.service)
|
||||||
|
if not method:
|
||||||
|
return
|
||||||
|
|
||||||
for player in component.extract_from_service(service):
|
params = {}
|
||||||
getattr(player, method)()
|
if service.service == SERVICE_VOLUME_SET:
|
||||||
|
params['volume'] = service.data.get(ATTR_MEDIA_VOLUME_LEVEL)
|
||||||
|
elif service.service == SERVICE_VOLUME_MUTE:
|
||||||
|
params['mute'] = service.data.get(ATTR_MEDIA_VOLUME_MUTED)
|
||||||
|
elif service.service == SERVICE_MEDIA_SEEK:
|
||||||
|
params['position'] = service.data.get(ATTR_MEDIA_SEEK_POSITION)
|
||||||
|
elif service.service == SERVICE_SELECT_SOURCE:
|
||||||
|
params['source'] = service.data.get(ATTR_INPUT_SOURCE)
|
||||||
|
elif service.service == SERVICE_PLAY_MEDIA:
|
||||||
|
params['media_type'] = \
|
||||||
|
service.data.get(ATTR_MEDIA_CONTENT_TYPE)
|
||||||
|
params['media_id'] = service.data.get(ATTR_MEDIA_CONTENT_ID)
|
||||||
|
params[ATTR_MEDIA_ENQUEUE] = \
|
||||||
|
service.data.get(ATTR_MEDIA_ENQUEUE)
|
||||||
|
target_players = component.async_extract_from_service(service)
|
||||||
|
|
||||||
if player.should_poll:
|
update_tasks = []
|
||||||
player.update_ha_state(True)
|
for player in target_players:
|
||||||
|
yield from getattr(player, method['method'])(**params)
|
||||||
|
|
||||||
|
for player in target_players:
|
||||||
|
if not player.should_poll:
|
||||||
|
continue
|
||||||
|
|
||||||
|
update_coro = player.async_update_ha_state(True)
|
||||||
|
if hasattr(player, 'async_update'):
|
||||||
|
update_tasks.append(update_coro)
|
||||||
|
else:
|
||||||
|
yield from update_coro
|
||||||
|
|
||||||
|
if update_tasks:
|
||||||
|
yield from asyncio.wait(update_tasks, loop=hass.loop)
|
||||||
|
|
||||||
for service in SERVICE_TO_METHOD:
|
for service in SERVICE_TO_METHOD:
|
||||||
hass.services.register(DOMAIN, service, media_player_service_handler,
|
schema = SERVICE_TO_METHOD[service].get(
|
||||||
descriptions.get(service),
|
'schema', MEDIA_PLAYER_SCHEMA)
|
||||||
schema=MEDIA_PLAYER_SCHEMA)
|
hass.services.async_register(
|
||||||
|
DOMAIN, service, async_service_handler,
|
||||||
def volume_set_service(service):
|
descriptions.get(service), schema=schema)
|
||||||
"""Set specified volume on the media player."""
|
|
||||||
volume = service.data.get(ATTR_MEDIA_VOLUME_LEVEL)
|
|
||||||
|
|
||||||
for player in component.extract_from_service(service):
|
|
||||||
player.set_volume_level(volume)
|
|
||||||
|
|
||||||
if player.should_poll:
|
|
||||||
player.update_ha_state(True)
|
|
||||||
|
|
||||||
hass.services.register(DOMAIN, SERVICE_VOLUME_SET, volume_set_service,
|
|
||||||
descriptions.get(SERVICE_VOLUME_SET),
|
|
||||||
schema=MEDIA_PLAYER_SET_VOLUME_SCHEMA)
|
|
||||||
|
|
||||||
def volume_mute_service(service):
|
|
||||||
"""Mute (true) or unmute (false) the media player."""
|
|
||||||
mute = service.data.get(ATTR_MEDIA_VOLUME_MUTED)
|
|
||||||
|
|
||||||
for player in component.extract_from_service(service):
|
|
||||||
player.mute_volume(mute)
|
|
||||||
|
|
||||||
if player.should_poll:
|
|
||||||
player.update_ha_state(True)
|
|
||||||
|
|
||||||
hass.services.register(DOMAIN, SERVICE_VOLUME_MUTE, volume_mute_service,
|
|
||||||
descriptions.get(SERVICE_VOLUME_MUTE),
|
|
||||||
schema=MEDIA_PLAYER_MUTE_VOLUME_SCHEMA)
|
|
||||||
|
|
||||||
def media_seek_service(service):
|
|
||||||
"""Seek to a position."""
|
|
||||||
position = service.data.get(ATTR_MEDIA_SEEK_POSITION)
|
|
||||||
|
|
||||||
for player in component.extract_from_service(service):
|
|
||||||
player.media_seek(position)
|
|
||||||
|
|
||||||
if player.should_poll:
|
|
||||||
player.update_ha_state(True)
|
|
||||||
|
|
||||||
hass.services.register(DOMAIN, SERVICE_MEDIA_SEEK, media_seek_service,
|
|
||||||
descriptions.get(SERVICE_MEDIA_SEEK),
|
|
||||||
schema=MEDIA_PLAYER_MEDIA_SEEK_SCHEMA)
|
|
||||||
|
|
||||||
def select_source_service(service):
|
|
||||||
"""Change input to selected source."""
|
|
||||||
input_source = service.data.get(ATTR_INPUT_SOURCE)
|
|
||||||
|
|
||||||
for player in component.extract_from_service(service):
|
|
||||||
player.select_source(input_source)
|
|
||||||
|
|
||||||
if player.should_poll:
|
|
||||||
player.update_ha_state(True)
|
|
||||||
|
|
||||||
hass.services.register(DOMAIN, SERVICE_SELECT_SOURCE,
|
|
||||||
select_source_service,
|
|
||||||
descriptions.get(SERVICE_SELECT_SOURCE),
|
|
||||||
schema=MEDIA_PLAYER_SELECT_SOURCE_SCHEMA)
|
|
||||||
|
|
||||||
def play_media_service(service):
|
|
||||||
"""Play specified media_id on the media player."""
|
|
||||||
media_type = service.data.get(ATTR_MEDIA_CONTENT_TYPE)
|
|
||||||
media_id = service.data.get(ATTR_MEDIA_CONTENT_ID)
|
|
||||||
enqueue = service.data.get(ATTR_MEDIA_ENQUEUE)
|
|
||||||
|
|
||||||
kwargs = {
|
|
||||||
ATTR_MEDIA_ENQUEUE: enqueue,
|
|
||||||
}
|
|
||||||
|
|
||||||
for player in component.extract_from_service(service):
|
|
||||||
player.play_media(media_type, media_id, **kwargs)
|
|
||||||
|
|
||||||
if player.should_poll:
|
|
||||||
player.update_ha_state(True)
|
|
||||||
|
|
||||||
hass.services.register(DOMAIN, SERVICE_PLAY_MEDIA, play_media_service,
|
|
||||||
descriptions.get(SERVICE_PLAY_MEDIA),
|
|
||||||
schema=MEDIA_PLAYER_PLAY_MEDIA_SCHEMA)
|
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
@ -548,54 +521,158 @@ class MediaPlayerDevice(Entity):
|
|||||||
"""Turn the media player on."""
|
"""Turn the media player on."""
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
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.hass.loop.run_in_executor(
|
||||||
|
None, self.turn_on)
|
||||||
|
|
||||||
def turn_off(self):
|
def turn_off(self):
|
||||||
"""Turn the media player off."""
|
"""Turn the media player off."""
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
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.hass.loop.run_in_executor(
|
||||||
|
None, self.turn_off)
|
||||||
|
|
||||||
def mute_volume(self, mute):
|
def mute_volume(self, mute):
|
||||||
"""Mute the volume."""
|
"""Mute the volume."""
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
def async_mute_volume(self, mute):
|
||||||
|
"""Mute the volume.
|
||||||
|
|
||||||
|
This method must be run in the event loop and returns a coroutine.
|
||||||
|
"""
|
||||||
|
return self.hass.loop.run_in_executor(
|
||||||
|
None, self.mute_volume, mute)
|
||||||
|
|
||||||
def set_volume_level(self, volume):
|
def set_volume_level(self, volume):
|
||||||
"""Set volume level, range 0..1."""
|
"""Set volume level, range 0..1."""
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
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.
|
||||||
|
"""
|
||||||
|
return self.hass.loop.run_in_executor(
|
||||||
|
None, self.set_volume_level, volume)
|
||||||
|
|
||||||
def media_play(self):
|
def media_play(self):
|
||||||
"""Send play commmand."""
|
"""Send play commmand."""
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
def async_media_play(self):
|
||||||
|
"""Send play commmand.
|
||||||
|
|
||||||
|
This method must be run in the event loop and returns a coroutine.
|
||||||
|
"""
|
||||||
|
return self.hass.loop.run_in_executor(
|
||||||
|
None, self.media_play)
|
||||||
|
|
||||||
def media_pause(self):
|
def media_pause(self):
|
||||||
"""Send pause command."""
|
"""Send pause command."""
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
def async_media_pause(self):
|
||||||
|
"""Send pause command.
|
||||||
|
|
||||||
|
This method must be run in the event loop and returns a coroutine.
|
||||||
|
"""
|
||||||
|
return self.hass.loop.run_in_executor(
|
||||||
|
None, self.media_pause)
|
||||||
|
|
||||||
def media_stop(self):
|
def media_stop(self):
|
||||||
"""Send stop command."""
|
"""Send stop command."""
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
def async_media_stop(self):
|
||||||
|
"""Send stop command.
|
||||||
|
|
||||||
|
This method must be run in the event loop and returns a coroutine.
|
||||||
|
"""
|
||||||
|
return self.hass.loop.run_in_executor(
|
||||||
|
None, self.media_stop)
|
||||||
|
|
||||||
def media_previous_track(self):
|
def media_previous_track(self):
|
||||||
"""Send previous track command."""
|
"""Send previous track command."""
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
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.hass.loop.run_in_executor(
|
||||||
|
None, self.media_previous_track)
|
||||||
|
|
||||||
def media_next_track(self):
|
def media_next_track(self):
|
||||||
"""Send next track command."""
|
"""Send next track command."""
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
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.hass.loop.run_in_executor(
|
||||||
|
None, self.media_next_track)
|
||||||
|
|
||||||
def media_seek(self, position):
|
def media_seek(self, position):
|
||||||
"""Send seek command."""
|
"""Send seek command."""
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
def play_media(self, media_type, media_id):
|
def async_media_seek(self, position):
|
||||||
|
"""Send seek command.
|
||||||
|
|
||||||
|
This method must be run in the event loop and returns a coroutine.
|
||||||
|
"""
|
||||||
|
return self.hass.loop.run_in_executor(
|
||||||
|
None, self.media_seek, position)
|
||||||
|
|
||||||
|
def play_media(self, media_type, media_id, **kwargs):
|
||||||
"""Play a piece of media."""
|
"""Play a piece of media."""
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
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.
|
||||||
|
"""
|
||||||
|
return self.hass.loop.run_in_executor(
|
||||||
|
None, ft.partial(self.play_media, media_type, media_id, **kwargs))
|
||||||
|
|
||||||
def select_source(self, source):
|
def select_source(self, source):
|
||||||
"""Select input source."""
|
"""Select input source."""
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
def async_select_source(self, source):
|
||||||
|
"""Select input source.
|
||||||
|
|
||||||
|
This method must be run in the event loop and returns a coroutine.
|
||||||
|
"""
|
||||||
|
return self.hass.loop.run_in_executor(
|
||||||
|
None, self.select_source, source)
|
||||||
|
|
||||||
def clear_playlist(self):
|
def clear_playlist(self):
|
||||||
"""Clear players playlist."""
|
"""Clear players playlist."""
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
def async_clear_playlist(self):
|
||||||
|
"""Clear players playlist.
|
||||||
|
|
||||||
|
This method must be run in the event loop and returns a coroutine.
|
||||||
|
"""
|
||||||
|
return self.hass.loop.run_in_executor(
|
||||||
|
None, self.clear_playlist)
|
||||||
|
|
||||||
# No need to overwrite these.
|
# No need to overwrite these.
|
||||||
@property
|
@property
|
||||||
def support_pause(self):
|
def support_pause(self):
|
||||||
@ -654,16 +731,40 @@ class MediaPlayerDevice(Entity):
|
|||||||
else:
|
else:
|
||||||
self.turn_off()
|
self.turn_off()
|
||||||
|
|
||||||
|
def async_toggle(self):
|
||||||
|
"""Toggle the power on the media player.
|
||||||
|
|
||||||
|
This method must be run in the event loop and returns a coroutine.
|
||||||
|
"""
|
||||||
|
if self.state in [STATE_OFF, STATE_IDLE]:
|
||||||
|
return self.async_turn_on()
|
||||||
|
else:
|
||||||
|
return self.async_turn_off()
|
||||||
|
|
||||||
def volume_up(self):
|
def volume_up(self):
|
||||||
"""Turn volume up for media player."""
|
"""Turn volume up for media player."""
|
||||||
if self.volume_level < 1:
|
if self.volume_level < 1:
|
||||||
self.set_volume_level(min(1, self.volume_level + .1))
|
self.set_volume_level(min(1, self.volume_level + .1))
|
||||||
|
|
||||||
|
def async_volume_up(self):
|
||||||
|
"""Turn volume up for media player.
|
||||||
|
|
||||||
|
This method must be run in the event loop and returns a coroutine.
|
||||||
|
"""
|
||||||
|
return self.async_set_volume_level(min(1, self.volume_level + .1))
|
||||||
|
|
||||||
def volume_down(self):
|
def volume_down(self):
|
||||||
"""Turn volume down for media player."""
|
"""Turn volume down for media player."""
|
||||||
if self.volume_level > 0:
|
if self.volume_level > 0:
|
||||||
self.set_volume_level(max(0, self.volume_level - .1))
|
self.set_volume_level(max(0, self.volume_level - .1))
|
||||||
|
|
||||||
|
def async_volume_down(self):
|
||||||
|
"""Turn volume down for media player.
|
||||||
|
|
||||||
|
This method must be run in the event loop and returns a coroutine.
|
||||||
|
"""
|
||||||
|
return self.async_set_volume_level(max(0, self.volume_level - .1))
|
||||||
|
|
||||||
def media_play_pause(self):
|
def media_play_pause(self):
|
||||||
"""Play or pause the media player."""
|
"""Play or pause the media player."""
|
||||||
if self.state == STATE_PLAYING:
|
if self.state == STATE_PLAYING:
|
||||||
@ -671,6 +772,16 @@ class MediaPlayerDevice(Entity):
|
|||||||
else:
|
else:
|
||||||
self.media_play()
|
self.media_play()
|
||||||
|
|
||||||
|
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.
|
||||||
|
"""
|
||||||
|
if self.state == STATE_PLAYING:
|
||||||
|
return self.async_media_pause()
|
||||||
|
else:
|
||||||
|
return self.async_media_play()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def entity_picture(self):
|
def entity_picture(self):
|
||||||
"""Return image of the media playing."""
|
"""Return image of the media playing."""
|
||||||
|
@ -69,7 +69,6 @@ class TestDemoMediaPlayer(unittest.TestCase):
|
|||||||
self.hass, mp.DOMAIN,
|
self.hass, mp.DOMAIN,
|
||||||
{'media_player': {'platform': 'demo'}})
|
{'media_player': {'platform': 'demo'}})
|
||||||
state = self.hass.states.get(entity_id)
|
state = self.hass.states.get(entity_id)
|
||||||
print(state)
|
|
||||||
assert 1.0 == state.attributes.get('volume_level')
|
assert 1.0 == state.attributes.get('volume_level')
|
||||||
|
|
||||||
mp.set_volume_level(self.hass, None, entity_id)
|
mp.set_volume_level(self.hass, None, entity_id)
|
||||||
@ -203,7 +202,7 @@ class TestDemoMediaPlayer(unittest.TestCase):
|
|||||||
state.attributes.get('supported_media_commands'))
|
state.attributes.get('supported_media_commands'))
|
||||||
|
|
||||||
@patch('homeassistant.components.media_player.demo.DemoYoutubePlayer.'
|
@patch('homeassistant.components.media_player.demo.DemoYoutubePlayer.'
|
||||||
'media_seek')
|
'media_seek', autospec=True)
|
||||||
def test_play_media(self, mock_seek):
|
def test_play_media(self, mock_seek):
|
||||||
"""Test play_media ."""
|
"""Test play_media ."""
|
||||||
assert setup_component(
|
assert setup_component(
|
||||||
|
Loading…
x
Reference in New Issue
Block a user