Add 2 media_player services and 1 custom service to Squeezebox platform (#10969)
* Add 2 media_player services and 1 custom service to Squeezebox platform * Fix pylint error * Remove apostrophe in example * Split method into command and parameters * Fix Lint error
This commit is contained in:
parent
cba55402b1
commit
92014bf1d1
2 changed files with 103 additions and 3 deletions
|
@ -307,3 +307,16 @@ kodi_call_method:
|
||||||
method:
|
method:
|
||||||
description: Name of the Kodi JSONRPC API method to be called.
|
description: Name of the Kodi JSONRPC API method to be called.
|
||||||
example: 'VideoLibrary.GetRecentlyAddedEpisodes'
|
example: 'VideoLibrary.GetRecentlyAddedEpisodes'
|
||||||
|
|
||||||
|
squeezebox_call_method:
|
||||||
|
description: 'Call a Squeezebox JSON/RPC API method.'
|
||||||
|
fields:
|
||||||
|
entity_id:
|
||||||
|
description: Name(s) of the Squeexebox entities where to run the API method.
|
||||||
|
example: 'media_player.squeezebox_radio'
|
||||||
|
command:
|
||||||
|
description: Name of the Squeezebox command.
|
||||||
|
example: 'playlist'
|
||||||
|
parameters:
|
||||||
|
description: Optional array of parameters to be appended to the command. See 'Command Line Interface' official help page from Logitech for details.
|
||||||
|
example: '["loadtracks", "track.titlesearch=highway to hell"]'
|
||||||
|
|
|
@ -8,19 +8,22 @@ import logging
|
||||||
import asyncio
|
import asyncio
|
||||||
import urllib.parse
|
import urllib.parse
|
||||||
import json
|
import json
|
||||||
|
import os
|
||||||
import aiohttp
|
import aiohttp
|
||||||
import async_timeout
|
import async_timeout
|
||||||
|
|
||||||
import voluptuous as vol
|
import voluptuous as vol
|
||||||
|
|
||||||
|
from homeassistant.config import load_yaml_config_file
|
||||||
from homeassistant.components.media_player import (
|
from homeassistant.components.media_player import (
|
||||||
ATTR_MEDIA_ENQUEUE, SUPPORT_PLAY_MEDIA,
|
ATTR_MEDIA_ENQUEUE, SUPPORT_PLAY_MEDIA,
|
||||||
MEDIA_TYPE_MUSIC, SUPPORT_NEXT_TRACK, SUPPORT_PAUSE, PLATFORM_SCHEMA,
|
MEDIA_TYPE_MUSIC, SUPPORT_NEXT_TRACK, SUPPORT_PAUSE, PLATFORM_SCHEMA,
|
||||||
SUPPORT_PREVIOUS_TRACK, SUPPORT_SEEK, SUPPORT_TURN_OFF, SUPPORT_TURN_ON,
|
SUPPORT_PREVIOUS_TRACK, SUPPORT_SEEK, SUPPORT_TURN_OFF, SUPPORT_TURN_ON,
|
||||||
SUPPORT_VOLUME_MUTE, SUPPORT_VOLUME_SET, SUPPORT_PLAY, MediaPlayerDevice)
|
SUPPORT_VOLUME_MUTE, SUPPORT_VOLUME_SET, SUPPORT_PLAY, MediaPlayerDevice,
|
||||||
|
MEDIA_PLAYER_SCHEMA, DOMAIN, SUPPORT_SHUFFLE_SET, SUPPORT_CLEAR_PLAYLIST)
|
||||||
from homeassistant.const import (
|
from homeassistant.const import (
|
||||||
CONF_HOST, CONF_PASSWORD, CONF_USERNAME, STATE_IDLE, STATE_OFF,
|
CONF_HOST, CONF_PASSWORD, CONF_USERNAME, STATE_IDLE, STATE_OFF,
|
||||||
STATE_PAUSED, STATE_PLAYING, STATE_UNKNOWN, CONF_PORT)
|
STATE_PAUSED, STATE_PLAYING, STATE_UNKNOWN, CONF_PORT, ATTR_COMMAND)
|
||||||
import homeassistant.helpers.config_validation as cv
|
import homeassistant.helpers.config_validation as cv
|
||||||
from homeassistant.helpers.aiohttp_client import async_get_clientsession
|
from homeassistant.helpers.aiohttp_client import async_get_clientsession
|
||||||
from homeassistant.util.dt import utcnow
|
from homeassistant.util.dt import utcnow
|
||||||
|
@ -33,7 +36,7 @@ TIMEOUT = 10
|
||||||
SUPPORT_SQUEEZEBOX = SUPPORT_PAUSE | SUPPORT_VOLUME_SET | \
|
SUPPORT_SQUEEZEBOX = SUPPORT_PAUSE | SUPPORT_VOLUME_SET | \
|
||||||
SUPPORT_VOLUME_MUTE | SUPPORT_PREVIOUS_TRACK | SUPPORT_NEXT_TRACK | \
|
SUPPORT_VOLUME_MUTE | SUPPORT_PREVIOUS_TRACK | SUPPORT_NEXT_TRACK | \
|
||||||
SUPPORT_SEEK | SUPPORT_TURN_ON | SUPPORT_TURN_OFF | SUPPORT_PLAY_MEDIA | \
|
SUPPORT_SEEK | SUPPORT_TURN_ON | SUPPORT_TURN_OFF | SUPPORT_PLAY_MEDIA | \
|
||||||
SUPPORT_PLAY
|
SUPPORT_PLAY | SUPPORT_SHUFFLE_SET | SUPPORT_CLEAR_PLAYLIST
|
||||||
|
|
||||||
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
|
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
|
||||||
vol.Required(CONF_HOST): cv.string,
|
vol.Required(CONF_HOST): cv.string,
|
||||||
|
@ -42,12 +45,33 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
|
||||||
vol.Optional(CONF_USERNAME): cv.string,
|
vol.Optional(CONF_USERNAME): cv.string,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
SERVICE_CALL_METHOD = 'squeezebox_call_method'
|
||||||
|
|
||||||
|
DATA_SQUEEZEBOX = 'squeexebox'
|
||||||
|
|
||||||
|
ATTR_PARAMETERS = 'parameters'
|
||||||
|
|
||||||
|
SQUEEZEBOX_CALL_METHOD_SCHEMA = MEDIA_PLAYER_SCHEMA.extend({
|
||||||
|
vol.Required(ATTR_COMMAND): cv.string,
|
||||||
|
vol.Optional(ATTR_PARAMETERS):
|
||||||
|
vol.All(cv.ensure_list, vol.Length(min=1), [cv.string]),
|
||||||
|
})
|
||||||
|
|
||||||
|
SERVICE_TO_METHOD = {
|
||||||
|
SERVICE_CALL_METHOD: {
|
||||||
|
'method': 'async_call_method',
|
||||||
|
'schema': SQUEEZEBOX_CALL_METHOD_SCHEMA},
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@asyncio.coroutine
|
@asyncio.coroutine
|
||||||
def async_setup_platform(hass, config, async_add_devices, discovery_info=None):
|
def async_setup_platform(hass, config, async_add_devices, discovery_info=None):
|
||||||
"""Set up the squeezebox platform."""
|
"""Set up the squeezebox platform."""
|
||||||
import socket
|
import socket
|
||||||
|
|
||||||
|
if DATA_SQUEEZEBOX not in hass.data:
|
||||||
|
hass.data[DATA_SQUEEZEBOX] = []
|
||||||
|
|
||||||
username = config.get(CONF_USERNAME)
|
username = config.get(CONF_USERNAME)
|
||||||
password = config.get(CONF_PASSWORD)
|
password = config.get(CONF_PASSWORD)
|
||||||
|
|
||||||
|
@ -74,8 +98,44 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None):
|
||||||
lms = LogitechMediaServer(hass, host, port, username, password)
|
lms = LogitechMediaServer(hass, host, port, username, password)
|
||||||
|
|
||||||
players = yield from lms.create_players()
|
players = yield from lms.create_players()
|
||||||
|
|
||||||
|
hass.data[DATA_SQUEEZEBOX].extend(players)
|
||||||
async_add_devices(players)
|
async_add_devices(players)
|
||||||
|
|
||||||
|
@asyncio.coroutine
|
||||||
|
def async_service_handler(service):
|
||||||
|
"""Map services to methods on MediaPlayerDevice."""
|
||||||
|
method = SERVICE_TO_METHOD.get(service.service)
|
||||||
|
if not method:
|
||||||
|
return
|
||||||
|
|
||||||
|
params = {key: value for key, value in service.data.items()
|
||||||
|
if key != 'entity_id'}
|
||||||
|
entity_ids = service.data.get('entity_id')
|
||||||
|
if entity_ids:
|
||||||
|
target_players = [player for player in hass.data[DATA_SQUEEZEBOX]
|
||||||
|
if player.entity_id in entity_ids]
|
||||||
|
else:
|
||||||
|
target_players = hass.data[DATA_SQUEEZEBOX]
|
||||||
|
|
||||||
|
update_tasks = []
|
||||||
|
for player in target_players:
|
||||||
|
yield from getattr(player, method['method'])(**params)
|
||||||
|
update_tasks.append(player.async_update_ha_state(True))
|
||||||
|
|
||||||
|
if update_tasks:
|
||||||
|
yield from asyncio.wait(update_tasks, loop=hass.loop)
|
||||||
|
|
||||||
|
descriptions = yield from hass.async_add_job(
|
||||||
|
load_yaml_config_file, os.path.join(
|
||||||
|
os.path.dirname(__file__), 'services.yaml'))
|
||||||
|
|
||||||
|
for service in SERVICE_TO_METHOD:
|
||||||
|
schema = SERVICE_TO_METHOD[service]['schema']
|
||||||
|
hass.services.async_register(
|
||||||
|
DOMAIN, service, async_service_handler,
|
||||||
|
description=descriptions.get(service), schema=schema)
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
@ -305,6 +365,12 @@ class SqueezeBoxDevice(MediaPlayerDevice):
|
||||||
if 'album' in self._status:
|
if 'album' in self._status:
|
||||||
return self._status['album']
|
return self._status['album']
|
||||||
|
|
||||||
|
@property
|
||||||
|
def shuffle(self):
|
||||||
|
"""Boolean if shuffle is enabled."""
|
||||||
|
if 'playlist_shuffle' in self._status:
|
||||||
|
return self._status['playlist_shuffle'] == 1
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def supported_features(self):
|
def supported_features(self):
|
||||||
"""Flag media player features that are supported."""
|
"""Flag media player features that are supported."""
|
||||||
|
@ -415,3 +481,24 @@ class SqueezeBoxDevice(MediaPlayerDevice):
|
||||||
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."""
|
||||||
return self.async_query('playlist', 'add', media_id)
|
return self.async_query('playlist', 'add', media_id)
|
||||||
|
|
||||||
|
def async_set_shuffle(self, shuffle):
|
||||||
|
"""Enable/disable shuffle mode."""
|
||||||
|
return self.async_query('playlist', 'shuffle', int(shuffle))
|
||||||
|
|
||||||
|
def async_clear_playlist(self):
|
||||||
|
"""Send the media player the command for clear playlist."""
|
||||||
|
return self.async_query('playlist', 'clear')
|
||||||
|
|
||||||
|
def async_call_method(self, command, parameters=None):
|
||||||
|
"""
|
||||||
|
Call Squeezebox JSON/RPC method.
|
||||||
|
|
||||||
|
Escaped optional parameters are added to the command to form the list
|
||||||
|
of positional parameters (p0, p1..., pN) passed to JSON/RPC server.
|
||||||
|
"""
|
||||||
|
all_params = [command]
|
||||||
|
if parameters:
|
||||||
|
for parameter in parameters:
|
||||||
|
all_params.append(urllib.parse.quote(parameter, safe=':='))
|
||||||
|
return self.async_query(*all_params)
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue