mirror of https://github.com/home-assistant/core
Squeezebox add query and sync (#31748)
* Add query and sync * Update description of call_query * Remove backup files accidentally committed to repo * Update after pysqueezebox refactor * Use entity service helper * Implement suggested changes * Fix linter error in services.yaml * Fix long lines in services.yaml
This commit is contained in:
parent
7f2c6b43d2
commit
dbf383f713
|
@ -3,5 +3,5 @@
|
|||
"name": "Logitech Squeezebox",
|
||||
"documentation": "https://www.home-assistant.io/integrations/squeezebox",
|
||||
"codeowners": ["@rajlaud"],
|
||||
"requirements": ["pysqueezebox==0.1.2"]
|
||||
"requirements": ["pysqueezebox==0.1.4"]
|
||||
}
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
"""Support for interfacing to the Logitech SqueezeBox API."""
|
||||
import asyncio
|
||||
import logging
|
||||
import socket
|
||||
|
||||
|
@ -25,7 +24,6 @@ from homeassistant.components.media_player.const import (
|
|||
)
|
||||
from homeassistant.const import (
|
||||
ATTR_COMMAND,
|
||||
ATTR_ENTITY_ID,
|
||||
CONF_HOST,
|
||||
CONF_PASSWORD,
|
||||
CONF_PORT,
|
||||
|
@ -33,11 +31,19 @@ from homeassistant.const import (
|
|||
STATE_OFF,
|
||||
)
|
||||
from homeassistant.exceptions import PlatformNotReady
|
||||
from homeassistant.helpers import config_validation as cv, entity_platform
|
||||
from homeassistant.helpers.aiohttp_client import async_get_clientsession
|
||||
import homeassistant.helpers.config_validation as cv
|
||||
from homeassistant.util.dt import utcnow
|
||||
|
||||
from .const import DOMAIN, SERVICE_CALL_METHOD, SQUEEZEBOX_MODE
|
||||
from .const import SQUEEZEBOX_MODE
|
||||
|
||||
SERVICE_CALL_METHOD = "call_method"
|
||||
SERVICE_CALL_QUERY = "call_query"
|
||||
SERVICE_SYNC = "sync"
|
||||
SERVICE_UNSYNC = "unsync"
|
||||
|
||||
ATTR_QUERY_RESULT = "query_result"
|
||||
ATTR_SYNC_GROUP = "sync_group"
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
@ -59,8 +65,6 @@ SUPPORT_SQUEEZEBOX = (
|
|||
| SUPPORT_CLEAR_PLAYLIST
|
||||
)
|
||||
|
||||
MEDIA_PLAYER_SCHEMA = vol.Schema({ATTR_ENTITY_ID: cv.comp_entity_ids})
|
||||
|
||||
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
|
||||
{
|
||||
vol.Required(CONF_HOST): cv.string,
|
||||
|
@ -76,21 +80,12 @@ KNOWN_SERVERS = "squeezebox_known_servers"
|
|||
|
||||
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]
|
||||
),
|
||||
}
|
||||
)
|
||||
ATTR_OTHER_PLAYER = "other_player"
|
||||
|
||||
SERVICE_TO_METHOD = {
|
||||
SERVICE_CALL_METHOD: {
|
||||
"method": "async_call_method",
|
||||
"schema": SQUEEZEBOX_CALL_METHOD_SCHEMA,
|
||||
}
|
||||
}
|
||||
ATTR_TO_PROPERTY = [
|
||||
ATTR_QUERY_RESULT,
|
||||
ATTR_SYNC_GROUP,
|
||||
]
|
||||
|
||||
|
||||
async def async_setup_platform(hass, config, async_add_entities, discovery_info=None):
|
||||
|
@ -141,38 +136,35 @@ async def async_setup_platform(hass, config, async_add_entities, discovery_info=
|
|||
hass.data[DATA_SQUEEZEBOX].extend(media_players)
|
||||
async_add_entities(media_players)
|
||||
|
||||
async def async_service_handler(service):
|
||||
"""Map services to methods on MediaPlayerEntity."""
|
||||
method = SERVICE_TO_METHOD.get(service.service)
|
||||
if not method:
|
||||
return
|
||||
platform = entity_platform.current_platform.get()
|
||||
|
||||
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]
|
||||
platform.async_register_entity_service(
|
||||
SERVICE_CALL_METHOD,
|
||||
{
|
||||
vol.Required(ATTR_COMMAND): cv.string,
|
||||
vol.Optional(ATTR_PARAMETERS): vol.All(
|
||||
cv.ensure_list, vol.Length(min=1), [cv.string]
|
||||
),
|
||||
},
|
||||
"async_call_method",
|
||||
)
|
||||
|
||||
update_tasks = []
|
||||
for player in target_players:
|
||||
await getattr(player, method["method"])(**params)
|
||||
update_tasks.append(player.async_update_ha_state(True))
|
||||
platform.async_register_entity_service(
|
||||
SERVICE_CALL_QUERY,
|
||||
{
|
||||
vol.Required(ATTR_COMMAND): cv.string,
|
||||
vol.Optional(ATTR_PARAMETERS): vol.All(
|
||||
cv.ensure_list, vol.Length(min=1), [cv.string]
|
||||
),
|
||||
},
|
||||
"async_call_query",
|
||||
)
|
||||
|
||||
if update_tasks:
|
||||
await asyncio.wait(update_tasks)
|
||||
platform.async_register_entity_service(
|
||||
SERVICE_SYNC, {vol.Required(ATTR_OTHER_PLAYER): cv.string}, "async_sync",
|
||||
)
|
||||
|
||||
for service in SERVICE_TO_METHOD:
|
||||
schema = SERVICE_TO_METHOD[service]["schema"]
|
||||
hass.services.async_register(
|
||||
DOMAIN, service, async_service_handler, schema=schema
|
||||
)
|
||||
platform.async_register_entity_service(SERVICE_UNSYNC, None, "async_unsync")
|
||||
|
||||
return True
|
||||
|
||||
|
@ -188,6 +180,18 @@ class SqueezeBoxDevice(MediaPlayerEntity):
|
|||
"""Initialize the SqueezeBox device."""
|
||||
self._player = player
|
||||
self._last_update = None
|
||||
self._query_result = {}
|
||||
|
||||
@property
|
||||
def device_state_attributes(self):
|
||||
"""Return device-specific attributes."""
|
||||
squeezebox_attr = {
|
||||
attr: getattr(self, attr)
|
||||
for attr in ATTR_TO_PROPERTY
|
||||
if getattr(self, attr) is not None
|
||||
}
|
||||
|
||||
return squeezebox_attr
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
|
@ -284,6 +288,21 @@ class SqueezeBoxDevice(MediaPlayerEntity):
|
|||
"""Flag media player features that are supported."""
|
||||
return SUPPORT_SQUEEZEBOX
|
||||
|
||||
@property
|
||||
def sync_group(self):
|
||||
"""List players we are synced with."""
|
||||
player_ids = {p.unique_id: p.entity_id for p in self.hass.data[DATA_SQUEEZEBOX]}
|
||||
sync_group = []
|
||||
for player in self._player.sync_group:
|
||||
if player in player_ids:
|
||||
sync_group.append(player_ids[player])
|
||||
return sync_group
|
||||
|
||||
@property
|
||||
def query_result(self):
|
||||
"""Return the result from the call_query service."""
|
||||
return self._query_result
|
||||
|
||||
async def async_turn_off(self):
|
||||
"""Turn off media player."""
|
||||
await self._player.async_set_power(False)
|
||||
|
@ -366,3 +385,35 @@ class SqueezeBoxDevice(MediaPlayerEntity):
|
|||
for parameter in parameters:
|
||||
all_params.append(parameter)
|
||||
await self._player.async_query(*all_params)
|
||||
|
||||
async def async_call_query(self, command, parameters=None):
|
||||
"""
|
||||
Call Squeezebox JSON/RPC method where we care about the result.
|
||||
|
||||
Additional 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(parameter)
|
||||
self._query_result = await self._player.async_query(*all_params)
|
||||
_LOGGER.debug("call_query got result %s", self._query_result)
|
||||
|
||||
async def async_sync(self, other_player):
|
||||
"""
|
||||
Add another Squeezebox player to this player's sync group.
|
||||
|
||||
If the other player is a member of a sync group, it will leave the current sync group
|
||||
without asking.
|
||||
"""
|
||||
player_ids = {p.entity_id: p.unique_id for p in self.hass.data[DATA_SQUEEZEBOX]}
|
||||
other_player_id = player_ids.get(other_player)
|
||||
if other_player_id:
|
||||
await self._player.async_sync(other_player_id)
|
||||
else:
|
||||
_LOGGER.info("Could not find player_id for %s. Not syncing.", other_player)
|
||||
|
||||
async def async_unsync(self):
|
||||
"""Unsync this Squeezebox player."""
|
||||
await self._player.async_unsync()
|
||||
|
|
|
@ -8,5 +8,35 @@ call_method:
|
|||
description: Command to pass to Logitech Media Server (p0 in the CLI documentation).
|
||||
example: "playlist"
|
||||
parameters:
|
||||
description: Array of additional parameters to pass to Logitech Media Server (p1, ..., pN in the CLI documentation).
|
||||
description: >
|
||||
Array of additional parameters to pass to Logitech Media Server (p1, ..., pN in the CLI documentation).
|
||||
example: ["loadtracks", "album.titlesearch="]
|
||||
call_query:
|
||||
description: >
|
||||
Call a custom Squeezebox JSONRPC API. Result will be stored in 'query_result' attribute of the Squeezebox entity.
|
||||
fields:
|
||||
entity_id:
|
||||
description: Name(s) of the Squeezebox entities where to run the API method.
|
||||
example: 'media_player.squeezebox_radio'
|
||||
command:
|
||||
description: Command to pass to Logitech Media Server (p0 in the CLI documentation).
|
||||
example: 'albums'
|
||||
parameters:
|
||||
description: >
|
||||
Array of additional parameters to pass to Logitech Media Server (p1, ..., pN in the CLI documentation).
|
||||
example: ["0", "20", "Revolver"]
|
||||
sync:
|
||||
description: >
|
||||
Add another player to this player's sync group. If the other player is already in a sync group, it will leave it.
|
||||
fields:
|
||||
entity_id:
|
||||
description: Name of the Squeezebox entity where to run the API method.
|
||||
example: "media_player.bedroom"
|
||||
other_player:
|
||||
description: Name of the other Squeezebox player to link.
|
||||
example: "media_player.living_room"
|
||||
unsync:
|
||||
description: Remove this player from its sync group.
|
||||
fields:
|
||||
entity_id:
|
||||
description: Name of the Squeezebox entity to unsync.
|
||||
|
|
|
@ -1590,7 +1590,7 @@ pysonos==0.0.25
|
|||
pyspcwebgw==0.4.0
|
||||
|
||||
# homeassistant.components.squeezebox
|
||||
pysqueezebox==0.1.2
|
||||
pysqueezebox==0.1.4
|
||||
|
||||
# homeassistant.components.stiebel_eltron
|
||||
pystiebeleltron==0.0.1.dev2
|
||||
|
|
Loading…
Reference in New Issue