mirror of
https://github.com/home-assistant/core.git
synced 2025-07-18 18:57:06 +00:00
More features for the Bluesound component (#11450)
* Added support for join and unjoin * Added support for sleep functionality * Fixed supported features * Removed long lines and fixed documentation strings * Fixed D401, imperative mood * Added shuffle support * Removed unnecessary log row * Removed model, modelname and brand * Removed descriptions * Removed polling command on method run. This change is not needed * Fixed merge errors * Removed unused usings * Pylint fixes * Hound fixes * Remove attr Sleep and removed white space in services.xml
This commit is contained in:
parent
72fa170265
commit
1143499301
@ -16,14 +16,16 @@ import async_timeout
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.components.media_player import (
|
||||
MEDIA_TYPE_MUSIC, PLATFORM_SCHEMA, SUPPORT_CLEAR_PLAYLIST,
|
||||
SUPPORT_NEXT_TRACK, SUPPORT_PAUSE, SUPPORT_PLAY, SUPPORT_PLAY_MEDIA,
|
||||
SUPPORT_PREVIOUS_TRACK, SUPPORT_SEEK, SUPPORT_SELECT_SOURCE, SUPPORT_STOP,
|
||||
ATTR_MEDIA_ENQUEUE, DOMAIN, MEDIA_TYPE_MUSIC, PLATFORM_SCHEMA,
|
||||
SUPPORT_CLEAR_PLAYLIST, SUPPORT_NEXT_TRACK, SUPPORT_PAUSE, SUPPORT_PLAY,
|
||||
SUPPORT_PLAY_MEDIA, SUPPORT_PREVIOUS_TRACK, SUPPORT_SEEK,
|
||||
SUPPORT_SELECT_SOURCE, SUPPORT_SHUFFLE_SET, SUPPORT_STOP,
|
||||
SUPPORT_VOLUME_MUTE, SUPPORT_VOLUME_SET, SUPPORT_VOLUME_STEP,
|
||||
MediaPlayerDevice)
|
||||
from homeassistant.const import (
|
||||
CONF_HOST, CONF_HOSTS, CONF_NAME, CONF_PORT, EVENT_HOMEASSISTANT_START,
|
||||
EVENT_HOMEASSISTANT_STOP, STATE_IDLE, STATE_PAUSED, STATE_PLAYING)
|
||||
ATTR_ENTITY_ID, CONF_HOST, CONF_HOSTS, CONF_NAME, CONF_PORT,
|
||||
EVENT_HOMEASSISTANT_START, EVENT_HOMEASSISTANT_STOP, STATE_IDLE,
|
||||
STATE_OFF, STATE_PAUSED, STATE_PLAYING)
|
||||
from homeassistant.core import callback
|
||||
from homeassistant.helpers.aiohttp_client import async_get_clientsession
|
||||
import homeassistant.helpers.config_validation as cv
|
||||
@ -35,10 +37,14 @@ REQUIREMENTS = ['xmltodict==0.11.0']
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
STATE_OFFLINE = 'offline'
|
||||
ATTR_MODEL = 'model'
|
||||
ATTR_MODEL_NAME = 'model_name'
|
||||
ATTR_BRAND = 'brand'
|
||||
STATE_GROUPED = 'grouped'
|
||||
|
||||
ATTR_MASTER = 'master'
|
||||
|
||||
SERVICE_JOIN = 'bluesound_join'
|
||||
SERVICE_UNJOIN = 'bluesound_unjoin'
|
||||
SERVICE_SET_TIMER = 'bluesound_set_sleep_timer'
|
||||
SERVICE_CLEAR_TIMER = 'bluesound_clear_sleep_timer'
|
||||
|
||||
DATA_BLUESOUND = 'bluesound'
|
||||
DEFAULT_PORT = 11000
|
||||
@ -58,6 +64,29 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
|
||||
}])
|
||||
})
|
||||
|
||||
BS_SCHEMA = vol.Schema({
|
||||
vol.Optional(ATTR_ENTITY_ID): cv.entity_ids,
|
||||
})
|
||||
|
||||
BS_JOIN_SCHEMA = BS_SCHEMA.extend({
|
||||
vol.Required(ATTR_MASTER): cv.entity_id,
|
||||
})
|
||||
|
||||
SERVICE_TO_METHOD = {
|
||||
SERVICE_JOIN: {
|
||||
'method': 'async_join',
|
||||
'schema': BS_JOIN_SCHEMA},
|
||||
SERVICE_UNJOIN: {
|
||||
'method': 'async_unjoin',
|
||||
'schema': BS_SCHEMA},
|
||||
SERVICE_SET_TIMER: {
|
||||
'method': 'async_increase_timer',
|
||||
'schema': BS_SCHEMA},
|
||||
SERVICE_CLEAR_TIMER: {
|
||||
'method': 'async_clear_timer',
|
||||
'schema': BS_SCHEMA}
|
||||
}
|
||||
|
||||
|
||||
def _add_player(hass, async_add_devices, host, port=None, name=None):
|
||||
"""Add Bluesound players."""
|
||||
@ -120,6 +149,30 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None):
|
||||
hass, async_add_devices, host.get(CONF_HOST),
|
||||
host.get(CONF_PORT), host.get(CONF_NAME))
|
||||
|
||||
@asyncio.coroutine
|
||||
def async_service_handler(service):
|
||||
"""Map services to method of Bluesound devices."""
|
||||
method = SERVICE_TO_METHOD.get(service.service)
|
||||
if not method:
|
||||
return
|
||||
|
||||
params = {key: value for key, value in service.data.items()
|
||||
if key != ATTR_ENTITY_ID}
|
||||
entity_ids = service.data.get(ATTR_ENTITY_ID)
|
||||
if entity_ids:
|
||||
target_players = [player for player in hass.data[DATA_BLUESOUND]
|
||||
if player.entity_id in entity_ids]
|
||||
else:
|
||||
target_players = hass.data[DATA_BLUESOUND]
|
||||
|
||||
for player in target_players:
|
||||
yield from getattr(player, method['method'])(**params)
|
||||
|
||||
for service in SERVICE_TO_METHOD:
|
||||
schema = SERVICE_TO_METHOD[service]['schema']
|
||||
hass.services.async_register(
|
||||
DOMAIN, service, async_service_handler, schema=schema)
|
||||
|
||||
|
||||
class BluesoundPlayer(MediaPlayerDevice):
|
||||
"""Representation of a Bluesound Player."""
|
||||
@ -128,13 +181,10 @@ class BluesoundPlayer(MediaPlayerDevice):
|
||||
"""Initialize the media player."""
|
||||
self.host = host
|
||||
self._hass = hass
|
||||
self._port = port
|
||||
self.port = port
|
||||
self._polling_session = async_get_clientsession(hass)
|
||||
self._polling_task = None # The actual polling task.
|
||||
self._name = name
|
||||
self._brand = None
|
||||
self._model = None
|
||||
self._model_name = None
|
||||
self._icon = None
|
||||
self._capture_items = []
|
||||
self._services_items = []
|
||||
@ -145,9 +195,13 @@ class BluesoundPlayer(MediaPlayerDevice):
|
||||
self._is_online = False
|
||||
self._retry_remove = None
|
||||
self._lastvol = None
|
||||
self._master = None
|
||||
self._is_master = False
|
||||
self._group_name = None
|
||||
|
||||
self._init_callback = init_callback
|
||||
if self._port is None:
|
||||
self._port = DEFAULT_PORT
|
||||
if self.port is None:
|
||||
self.port = DEFAULT_PORT
|
||||
|
||||
@staticmethod
|
||||
def _try_get_index(string, search_string):
|
||||
@ -158,7 +212,7 @@ class BluesoundPlayer(MediaPlayerDevice):
|
||||
return -1
|
||||
|
||||
@asyncio.coroutine
|
||||
def _internal_update_sync_status(
|
||||
def force_update_sync_status(
|
||||
self, on_updated_cb=None, raise_timeout=False):
|
||||
"""Update the internal status."""
|
||||
resp = None
|
||||
@ -174,14 +228,27 @@ class BluesoundPlayer(MediaPlayerDevice):
|
||||
|
||||
if not self._name:
|
||||
self._name = self._sync_status.get('@name', self.host)
|
||||
if not self._brand:
|
||||
self._brand = self._sync_status.get('@brand', self.host)
|
||||
if not self._model:
|
||||
self._model = self._sync_status.get('@model', self.host)
|
||||
if not self._icon:
|
||||
self._icon = self._sync_status.get('@icon', self.host)
|
||||
if not self._model_name:
|
||||
self._model_name = self._sync_status.get('@modelName', self.host)
|
||||
|
||||
master = self._sync_status.get('master', None)
|
||||
if master is not None:
|
||||
self._is_master = False
|
||||
master_host = master.get('#text')
|
||||
master_device = [device for device in
|
||||
self._hass.data[DATA_BLUESOUND]
|
||||
if device.host == master_host]
|
||||
|
||||
if master_device and master_host != self.host:
|
||||
self._master = master_device[0]
|
||||
else:
|
||||
self._master = None
|
||||
_LOGGER.error("Master not found %s", master_host)
|
||||
else:
|
||||
if self._master is not None:
|
||||
self._master = None
|
||||
slaves = self._sync_status.get('slave', None)
|
||||
self._is_master = slaves is not None
|
||||
|
||||
if on_updated_cb:
|
||||
on_updated_cb()
|
||||
@ -223,7 +290,7 @@ class BluesoundPlayer(MediaPlayerDevice):
|
||||
self._retry_remove()
|
||||
self._retry_remove = None
|
||||
|
||||
yield from self._internal_update_sync_status(
|
||||
yield from self.force_update_sync_status(
|
||||
self._init_callback, True)
|
||||
except (asyncio.TimeoutError, ClientError):
|
||||
_LOGGER.info("Node %s is offline, retrying later", self.host)
|
||||
@ -256,7 +323,7 @@ class BluesoundPlayer(MediaPlayerDevice):
|
||||
|
||||
if method[0] == '/':
|
||||
method = method[1:]
|
||||
url = "http://{}:{}/{}".format(self.host, self._port, method)
|
||||
url = "http://{}:{}/{}".format(self.host, self.port, method)
|
||||
|
||||
_LOGGER.debug("Calling URL: %s", url)
|
||||
response = None
|
||||
@ -297,26 +364,47 @@ class BluesoundPlayer(MediaPlayerDevice):
|
||||
etag = self._status.get('@etag', '')
|
||||
|
||||
if etag != '':
|
||||
url = 'Status?etag={}&timeout=60.0'.format(etag)
|
||||
url = "http://{}:{}/{}".format(self.host, self._port, url)
|
||||
url = 'Status?etag={}&timeout=120.0'.format(etag)
|
||||
url = "http://{}:{}/{}".format(self.host, self.port, url)
|
||||
|
||||
_LOGGER.debug("Calling URL: %s", url)
|
||||
|
||||
try:
|
||||
|
||||
with async_timeout.timeout(65, loop=self._hass.loop):
|
||||
with async_timeout.timeout(125, loop=self._hass.loop):
|
||||
response = yield from self._polling_session.get(
|
||||
url,
|
||||
headers={CONNECTION: KEEP_ALIVE})
|
||||
|
||||
if response.status != 200:
|
||||
_LOGGER.error("Error %s on %s", response.status, url)
|
||||
_LOGGER.error("Error %s on %s. Trying one more time.",
|
||||
response.status, url)
|
||||
else:
|
||||
result = yield from response.text()
|
||||
self._is_online = True
|
||||
self._last_status_update = dt_util.utcnow()
|
||||
self._status = xmltodict.parse(result)['status'].copy()
|
||||
|
||||
result = yield from response.text()
|
||||
self._is_online = True
|
||||
self._last_status_update = dt_util.utcnow()
|
||||
self._status = xmltodict.parse(result)['status'].copy()
|
||||
self.schedule_update_ha_state()
|
||||
group_name = self._status.get('groupName', None)
|
||||
if group_name != self._group_name:
|
||||
_LOGGER.debug('Group name change detected on device: %s',
|
||||
self.host)
|
||||
self._group_name = group_name
|
||||
# the sleep is needed to make sure that the
|
||||
# devices is synced
|
||||
yield from asyncio.sleep(1, loop=self._hass.loop)
|
||||
yield from self.async_trigger_sync_on_all()
|
||||
elif self.is_grouped:
|
||||
# when player is grouped we need to fetch volume from
|
||||
# sync_status. We will force an update if the player is
|
||||
# grouped this isn't a foolproof solution. A better
|
||||
# solution would be to fetch sync_status more often when
|
||||
# the device is playing. This would solve alot of
|
||||
# problems. This change will be done when the
|
||||
# communication is moved to a separate library
|
||||
yield from self.force_update_sync_status()
|
||||
|
||||
self.schedule_update_ha_state()
|
||||
|
||||
except (asyncio.TimeoutError, ClientError):
|
||||
self._is_online = False
|
||||
@ -327,12 +415,20 @@ class BluesoundPlayer(MediaPlayerDevice):
|
||||
self._name)
|
||||
raise
|
||||
|
||||
@asyncio.coroutine
|
||||
def async_trigger_sync_on_all(self):
|
||||
"""Trigger sync status update on all devices."""
|
||||
_LOGGER.debug("Trigger sync status on all devices")
|
||||
|
||||
for player in self._hass.data[DATA_BLUESOUND]:
|
||||
yield from player.force_update_sync_status()
|
||||
|
||||
@asyncio.coroutine
|
||||
@Throttle(SYNC_STATUS_INTERVAL)
|
||||
def async_update_sync_status(self, on_updated_cb=None,
|
||||
raise_timeout=False):
|
||||
"""Update sync status."""
|
||||
yield from self._internal_update_sync_status(
|
||||
yield from self.force_update_sync_status(
|
||||
on_updated_cb, raise_timeout=False)
|
||||
|
||||
@asyncio.coroutine
|
||||
@ -433,7 +529,10 @@ class BluesoundPlayer(MediaPlayerDevice):
|
||||
def state(self):
|
||||
"""Return the state of the device."""
|
||||
if self._status is None:
|
||||
return STATE_OFFLINE
|
||||
return STATE_OFF
|
||||
|
||||
if self.is_grouped and not self.is_master:
|
||||
return STATE_GROUPED
|
||||
|
||||
status = self._status.get('state', None)
|
||||
if status == 'pause' or status == 'stop':
|
||||
@ -445,7 +544,8 @@ class BluesoundPlayer(MediaPlayerDevice):
|
||||
@property
|
||||
def media_title(self):
|
||||
"""Title of current playing media."""
|
||||
if self._status is None:
|
||||
if (self._status is None or
|
||||
(self.is_grouped and not self.is_master)):
|
||||
return None
|
||||
|
||||
return self._status.get('title1', None)
|
||||
@ -456,6 +556,9 @@ class BluesoundPlayer(MediaPlayerDevice):
|
||||
if self._status is None:
|
||||
return None
|
||||
|
||||
if self.is_grouped and not self.is_master:
|
||||
return self._group_name
|
||||
|
||||
artist = self._status.get('artist', None)
|
||||
if not artist:
|
||||
artist = self._status.get('title2', None)
|
||||
@ -464,7 +567,8 @@ class BluesoundPlayer(MediaPlayerDevice):
|
||||
@property
|
||||
def media_album_name(self):
|
||||
"""Artist of current playing media (Music track only)."""
|
||||
if self._status is None:
|
||||
if (self._status is None or
|
||||
(self.is_grouped and not self.is_master)):
|
||||
return None
|
||||
|
||||
album = self._status.get('album', None)
|
||||
@ -475,21 +579,23 @@ class BluesoundPlayer(MediaPlayerDevice):
|
||||
@property
|
||||
def media_image_url(self):
|
||||
"""Image url of current playing media."""
|
||||
if self._status is None:
|
||||
if (self._status is None or
|
||||
(self.is_grouped and not self.is_master)):
|
||||
return None
|
||||
|
||||
url = self._status.get('image', None)
|
||||
if not url:
|
||||
return
|
||||
if url[0] == '/':
|
||||
url = "http://{}:{}{}".format(self.host, self._port, url)
|
||||
url = "http://{}:{}{}".format(self.host, self.port, url)
|
||||
|
||||
return url
|
||||
|
||||
@property
|
||||
def media_position(self):
|
||||
"""Position of current playing media in seconds."""
|
||||
if self._status is None:
|
||||
if (self._status is None or
|
||||
(self.is_grouped and not self.is_master)):
|
||||
return None
|
||||
|
||||
mediastate = self.state
|
||||
@ -510,7 +616,8 @@ class BluesoundPlayer(MediaPlayerDevice):
|
||||
@property
|
||||
def media_duration(self):
|
||||
"""Duration of current playing media in seconds."""
|
||||
if self._status is None:
|
||||
if (self._status is None or
|
||||
(self.is_grouped and not self.is_master)):
|
||||
return None
|
||||
|
||||
duration = self._status.get('totlen', None)
|
||||
@ -526,10 +633,10 @@ class BluesoundPlayer(MediaPlayerDevice):
|
||||
@property
|
||||
def volume_level(self):
|
||||
"""Volume level of the media player (0..1)."""
|
||||
if self._status is None:
|
||||
return None
|
||||
|
||||
volume = self._status.get('volume', None)
|
||||
if self.is_grouped:
|
||||
volume = self._sync_status.get('@volume', None)
|
||||
|
||||
if volume is not None:
|
||||
return int(volume) / 100
|
||||
return None
|
||||
@ -537,9 +644,6 @@ class BluesoundPlayer(MediaPlayerDevice):
|
||||
@property
|
||||
def is_volume_muted(self):
|
||||
"""Boolean if volume is currently muted."""
|
||||
if not self._status:
|
||||
return None
|
||||
|
||||
volume = self.volume_level
|
||||
if not volume:
|
||||
return None
|
||||
@ -558,7 +662,8 @@ class BluesoundPlayer(MediaPlayerDevice):
|
||||
@property
|
||||
def source_list(self):
|
||||
"""List of available input sources."""
|
||||
if self._status is None:
|
||||
if (self._status is None or
|
||||
(self.is_grouped and not self.is_master)):
|
||||
return None
|
||||
|
||||
sources = []
|
||||
@ -581,7 +686,8 @@ class BluesoundPlayer(MediaPlayerDevice):
|
||||
"""Name of the current input source."""
|
||||
from urllib import parse
|
||||
|
||||
if self._status is None:
|
||||
if (self._status is None or
|
||||
(self.is_grouped and not self.is_master)):
|
||||
return None
|
||||
|
||||
current_service = self._status.get('service', '')
|
||||
@ -649,12 +755,17 @@ class BluesoundPlayer(MediaPlayerDevice):
|
||||
if self._status is None:
|
||||
return None
|
||||
|
||||
if self.is_grouped and not self.is_master:
|
||||
return SUPPORT_VOLUME_STEP | SUPPORT_VOLUME_SET | \
|
||||
SUPPORT_VOLUME_MUTE
|
||||
|
||||
supported = SUPPORT_CLEAR_PLAYLIST
|
||||
|
||||
if self._status.get('indexing', '0') == '0':
|
||||
supported = supported | SUPPORT_PAUSE | SUPPORT_PREVIOUS_TRACK | \
|
||||
SUPPORT_NEXT_TRACK | SUPPORT_PLAY_MEDIA | \
|
||||
SUPPORT_STOP | SUPPORT_PLAY | SUPPORT_SELECT_SOURCE
|
||||
SUPPORT_STOP | SUPPORT_PLAY | SUPPORT_SELECT_SOURCE | \
|
||||
SUPPORT_SHUFFLE_SET
|
||||
|
||||
current_vol = self.volume_level
|
||||
if current_vol is not None and current_vol >= 0:
|
||||
@ -667,17 +778,87 @@ class BluesoundPlayer(MediaPlayerDevice):
|
||||
return supported
|
||||
|
||||
@property
|
||||
def device_state_attributes(self):
|
||||
"""Return the state attributes."""
|
||||
return {
|
||||
ATTR_MODEL: self._model,
|
||||
ATTR_MODEL_NAME: self._model_name,
|
||||
ATTR_BRAND: self._brand,
|
||||
}
|
||||
def is_master(self):
|
||||
"""Return true if player is a coordinator."""
|
||||
return self._is_master
|
||||
|
||||
@property
|
||||
def is_grouped(self):
|
||||
"""Return true if player is a coordinator."""
|
||||
return self._master is not None or self._is_master
|
||||
|
||||
@property
|
||||
def shuffle(self):
|
||||
"""Return true if shuffle is active."""
|
||||
return True if self._status.get('shuffle', '0') == '1' else False
|
||||
|
||||
@asyncio.coroutine
|
||||
def async_join(self, master):
|
||||
"""Join the player to a group."""
|
||||
master_device = [device for device in self.hass.data[DATA_BLUESOUND]
|
||||
if device.entity_id == master]
|
||||
|
||||
if master_device:
|
||||
_LOGGER.debug("Trying to join player: %s to master: %s",
|
||||
self.host, master_device[0].host)
|
||||
|
||||
yield from master_device[0].async_add_slave(self)
|
||||
else:
|
||||
_LOGGER.error("Master not found %s", master_device)
|
||||
|
||||
@asyncio.coroutine
|
||||
def async_unjoin(self):
|
||||
"""Unjoin the player from a group."""
|
||||
if self._master is None:
|
||||
return
|
||||
|
||||
_LOGGER.debug("Trying to unjoin player: %s", self.host)
|
||||
yield from self._master.async_remove_slave(self)
|
||||
|
||||
@asyncio.coroutine
|
||||
def async_add_slave(self, slave_device):
|
||||
"""Add slave to master."""
|
||||
return self.send_bluesound_command('/AddSlave?slave={}&port={}'
|
||||
.format(slave_device.host,
|
||||
slave_device.port))
|
||||
|
||||
@asyncio.coroutine
|
||||
def async_remove_slave(self, slave_device):
|
||||
"""Remove slave to master."""
|
||||
return self.send_bluesound_command('/RemoveSlave?slave={}&port={}'
|
||||
.format(slave_device.host,
|
||||
slave_device.port))
|
||||
|
||||
@asyncio.coroutine
|
||||
def async_increase_timer(self):
|
||||
"""Increase sleep time on player."""
|
||||
sleep_time = yield from self.send_bluesound_command('/Sleep')
|
||||
if sleep_time is None:
|
||||
_LOGGER.error('Error while increasing sleep time on player: %s',
|
||||
self.host)
|
||||
return 0
|
||||
|
||||
return int(sleep_time.get('sleep', '0'))
|
||||
|
||||
@asyncio.coroutine
|
||||
def async_clear_timer(self):
|
||||
"""Clear sleep timer on player."""
|
||||
sleep = 1
|
||||
while sleep > 0:
|
||||
sleep = yield from self.async_increase_timer()
|
||||
|
||||
@asyncio.coroutine
|
||||
def async_set_shuffle(self, shuffle):
|
||||
"""Enable or disable shuffle mode."""
|
||||
return self.send_bluesound_command('/Shuffle?state={}'
|
||||
.format('1' if shuffle else '0'))
|
||||
|
||||
@asyncio.coroutine
|
||||
def async_select_source(self, source):
|
||||
"""Select input source."""
|
||||
if self.is_grouped and not self.is_master:
|
||||
return
|
||||
|
||||
items = [x for x in self._preset_items if x['title'] == source]
|
||||
|
||||
if len(items) < 1:
|
||||
@ -700,11 +881,17 @@ class BluesoundPlayer(MediaPlayerDevice):
|
||||
@asyncio.coroutine
|
||||
def async_clear_playlist(self):
|
||||
"""Clear players playlist."""
|
||||
if self.is_grouped and not self.is_master:
|
||||
return
|
||||
|
||||
return self.send_bluesound_command('Clear')
|
||||
|
||||
@asyncio.coroutine
|
||||
def async_media_next_track(self):
|
||||
"""Send media_next command to media player."""
|
||||
if self.is_grouped and not self.is_master:
|
||||
return
|
||||
|
||||
cmd = 'Skip'
|
||||
if self._status and 'actions' in self._status:
|
||||
for action in self._status['actions']['action']:
|
||||
@ -717,6 +904,9 @@ class BluesoundPlayer(MediaPlayerDevice):
|
||||
@asyncio.coroutine
|
||||
def async_media_previous_track(self):
|
||||
"""Send media_previous command to media player."""
|
||||
if self.is_grouped and not self.is_master:
|
||||
return
|
||||
|
||||
cmd = 'Back'
|
||||
if self._status and 'actions' in self._status:
|
||||
for action in self._status['actions']['action']:
|
||||
@ -729,23 +919,52 @@ class BluesoundPlayer(MediaPlayerDevice):
|
||||
@asyncio.coroutine
|
||||
def async_media_play(self):
|
||||
"""Send media_play command to media player."""
|
||||
if self.is_grouped and not self.is_master:
|
||||
return
|
||||
|
||||
return self.send_bluesound_command('Play')
|
||||
|
||||
@asyncio.coroutine
|
||||
def async_media_pause(self):
|
||||
"""Send media_pause command to media player."""
|
||||
if self.is_grouped and not self.is_master:
|
||||
return
|
||||
|
||||
return self.send_bluesound_command('Pause')
|
||||
|
||||
@asyncio.coroutine
|
||||
def async_media_stop(self):
|
||||
"""Send stop command."""
|
||||
if self.is_grouped and not self.is_master:
|
||||
return
|
||||
|
||||
return self.send_bluesound_command('Pause')
|
||||
|
||||
@asyncio.coroutine
|
||||
def async_media_seek(self, position):
|
||||
"""Send media_seek command to media player."""
|
||||
if self.is_grouped and not self.is_master:
|
||||
return
|
||||
|
||||
return self.send_bluesound_command('Play?seek=' + str(float(position)))
|
||||
|
||||
@asyncio.coroutine
|
||||
def async_play_media(self, media_type, media_id, **kwargs):
|
||||
"""
|
||||
Send the play_media command to the media player.
|
||||
|
||||
If ATTR_MEDIA_ENQUEUE is True, add `media_id` to the queue.
|
||||
"""
|
||||
if self.is_grouped and not self.is_master:
|
||||
return
|
||||
|
||||
url = 'Play?url={}'.format(media_id)
|
||||
|
||||
if kwargs.get(ATTR_MEDIA_ENQUEUE):
|
||||
return self.send_bluesound_command(url)
|
||||
|
||||
return self.send_bluesound_command(url)
|
||||
|
||||
@asyncio.coroutine
|
||||
def async_volume_up(self):
|
||||
"""Volume up the media player."""
|
||||
|
@ -323,7 +323,6 @@ squeezebox_call_method:
|
||||
|
||||
yamaha_enable_output:
|
||||
description: Enable or disable an output port
|
||||
|
||||
fields:
|
||||
entity_id:
|
||||
description: Name(s) of entites to enable/disable port on.
|
||||
@ -334,3 +333,34 @@ yamaha_enable_output:
|
||||
enabled:
|
||||
description: Boolean indicating if port should be enabled or not.
|
||||
example: true
|
||||
|
||||
bluesound_join:
|
||||
description: Group player together.
|
||||
fields:
|
||||
master:
|
||||
description: Entity ID of the player that should become the master of the group.
|
||||
example: 'media_player.bluesound_livingroom'
|
||||
entity_id:
|
||||
description: Name(s) of entities that will coordinate the grouping. Platform dependent.
|
||||
example: 'media_player.bluesound_livingroom'
|
||||
|
||||
bluesound_unjoin:
|
||||
description: Unjoin the player from a group.
|
||||
fields:
|
||||
entity_id:
|
||||
description: Name(s) of entities that will be unjoined from their group. Platform dependent.
|
||||
example: 'media_player.bluesound_livingroom'
|
||||
|
||||
bluesound_set_sleep_timer:
|
||||
description: "Set a Bluesound timer. It will increase timer in steps: 15, 30, 45, 60, 90, 0"
|
||||
fields:
|
||||
entity_id:
|
||||
description: Name(s) of entities that will have a timer set.
|
||||
example: 'media_player.bluesound_livingroom'
|
||||
|
||||
bluesound_clear_sleep_timer:
|
||||
description: Clear a Bluesound timer.
|
||||
fields:
|
||||
entity_id:
|
||||
description: Name(s) of entities that will have the timer cleared.
|
||||
example: 'media_player.bluesound_livingroom'
|
||||
|
Loading…
x
Reference in New Issue
Block a user