Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions homeassistant/components/media_player/services.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -281,3 +281,16 @@ kodi_call_method:
method:
description: Name of the Kodi JSONRPC API method to be called.
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"]'
93 changes: 90 additions & 3 deletions homeassistant/components/media_player/squeezebox.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,19 +8,22 @@
import asyncio
import urllib.parse
import json
import os
import aiohttp
import async_timeout

import voluptuous as vol

from homeassistant.config import load_yaml_config_file
from homeassistant.components.media_player import (
ATTR_MEDIA_ENQUEUE, SUPPORT_PLAY_MEDIA,
MEDIA_TYPE_MUSIC, SUPPORT_NEXT_TRACK, SUPPORT_PAUSE, PLATFORM_SCHEMA,
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 (
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
from homeassistant.helpers.aiohttp_client import async_get_clientsession
from homeassistant.util.dt import utcnow
Expand All @@ -33,7 +36,7 @@
SUPPORT_SQUEEZEBOX = SUPPORT_PAUSE | SUPPORT_VOLUME_SET | \
SUPPORT_VOLUME_MUTE | SUPPORT_PREVIOUS_TRACK | SUPPORT_NEXT_TRACK | \
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({
vol.Required(CONF_HOST): cv.string,
Expand All @@ -42,12 +45,33 @@
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
def async_setup_platform(hass, config, async_add_devices, discovery_info=None):
"""Set up the squeezebox platform."""
import socket

if DATA_SQUEEZEBOX not in hass.data:
hass.data[DATA_SQUEEZEBOX] = []

username = config.get(CONF_USERNAME)
password = config.get(CONF_PASSWORD)

Expand All @@ -74,8 +98,44 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None):
lms = LogitechMediaServer(hass, host, port, username, password)

players = yield from lms.create_players()

hass.data[DATA_SQUEEZEBOX].extend(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


Expand Down Expand Up @@ -305,6 +365,12 @@ def media_album_name(self):
if 'album' in self._status:
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
def supported_features(self):
"""Flag media player features that are supported."""
Expand Down Expand Up @@ -415,3 +481,24 @@ def _play_uri(self, media_id):
def _add_uri_to_playlist(self, media_id):
"""Add a items to the existing playlist."""
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)