mirror of
https://github.com/home-assistant/core.git
synced 2025-07-23 13:17:32 +00:00
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",
|
"name": "Logitech Squeezebox",
|
||||||
"documentation": "https://www.home-assistant.io/integrations/squeezebox",
|
"documentation": "https://www.home-assistant.io/integrations/squeezebox",
|
||||||
"codeowners": ["@rajlaud"],
|
"codeowners": ["@rajlaud"],
|
||||||
"requirements": ["pysqueezebox==0.1.2"]
|
"requirements": ["pysqueezebox==0.1.4"]
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
"""Support for interfacing to the Logitech SqueezeBox API."""
|
"""Support for interfacing to the Logitech SqueezeBox API."""
|
||||||
import asyncio
|
|
||||||
import logging
|
import logging
|
||||||
import socket
|
import socket
|
||||||
|
|
||||||
@ -25,7 +24,6 @@ from homeassistant.components.media_player.const import (
|
|||||||
)
|
)
|
||||||
from homeassistant.const import (
|
from homeassistant.const import (
|
||||||
ATTR_COMMAND,
|
ATTR_COMMAND,
|
||||||
ATTR_ENTITY_ID,
|
|
||||||
CONF_HOST,
|
CONF_HOST,
|
||||||
CONF_PASSWORD,
|
CONF_PASSWORD,
|
||||||
CONF_PORT,
|
CONF_PORT,
|
||||||
@ -33,11 +31,19 @@ from homeassistant.const import (
|
|||||||
STATE_OFF,
|
STATE_OFF,
|
||||||
)
|
)
|
||||||
from homeassistant.exceptions import PlatformNotReady
|
from homeassistant.exceptions import PlatformNotReady
|
||||||
|
from homeassistant.helpers import config_validation as cv, entity_platform
|
||||||
from homeassistant.helpers.aiohttp_client import async_get_clientsession
|
from homeassistant.helpers.aiohttp_client import async_get_clientsession
|
||||||
import homeassistant.helpers.config_validation as cv
|
|
||||||
from homeassistant.util.dt import utcnow
|
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__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
@ -59,8 +65,6 @@ SUPPORT_SQUEEZEBOX = (
|
|||||||
| SUPPORT_CLEAR_PLAYLIST
|
| SUPPORT_CLEAR_PLAYLIST
|
||||||
)
|
)
|
||||||
|
|
||||||
MEDIA_PLAYER_SCHEMA = vol.Schema({ATTR_ENTITY_ID: cv.comp_entity_ids})
|
|
||||||
|
|
||||||
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
|
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
|
||||||
{
|
{
|
||||||
vol.Required(CONF_HOST): cv.string,
|
vol.Required(CONF_HOST): cv.string,
|
||||||
@ -76,21 +80,12 @@ KNOWN_SERVERS = "squeezebox_known_servers"
|
|||||||
|
|
||||||
ATTR_PARAMETERS = "parameters"
|
ATTR_PARAMETERS = "parameters"
|
||||||
|
|
||||||
SQUEEZEBOX_CALL_METHOD_SCHEMA = MEDIA_PLAYER_SCHEMA.extend(
|
ATTR_OTHER_PLAYER = "other_player"
|
||||||
{
|
|
||||||
vol.Required(ATTR_COMMAND): cv.string,
|
|
||||||
vol.Optional(ATTR_PARAMETERS): vol.All(
|
|
||||||
cv.ensure_list, vol.Length(min=1), [cv.string]
|
|
||||||
),
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
SERVICE_TO_METHOD = {
|
ATTR_TO_PROPERTY = [
|
||||||
SERVICE_CALL_METHOD: {
|
ATTR_QUERY_RESULT,
|
||||||
"method": "async_call_method",
|
ATTR_SYNC_GROUP,
|
||||||
"schema": SQUEEZEBOX_CALL_METHOD_SCHEMA,
|
]
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
async def async_setup_platform(hass, config, async_add_entities, discovery_info=None):
|
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)
|
hass.data[DATA_SQUEEZEBOX].extend(media_players)
|
||||||
async_add_entities(media_players)
|
async_add_entities(media_players)
|
||||||
|
|
||||||
async def async_service_handler(service):
|
platform = entity_platform.current_platform.get()
|
||||||
"""Map services to methods on MediaPlayerEntity."""
|
|
||||||
method = SERVICE_TO_METHOD.get(service.service)
|
|
||||||
if not method:
|
|
||||||
return
|
|
||||||
|
|
||||||
params = {
|
platform.async_register_entity_service(
|
||||||
key: value for key, value in service.data.items() if key != "entity_id"
|
SERVICE_CALL_METHOD,
|
||||||
}
|
{
|
||||||
entity_ids = service.data.get("entity_id")
|
vol.Required(ATTR_COMMAND): cv.string,
|
||||||
if entity_ids:
|
vol.Optional(ATTR_PARAMETERS): vol.All(
|
||||||
target_players = [
|
cv.ensure_list, vol.Length(min=1), [cv.string]
|
||||||
player
|
),
|
||||||
for player in hass.data[DATA_SQUEEZEBOX]
|
},
|
||||||
if player.entity_id in entity_ids
|
"async_call_method",
|
||||||
]
|
)
|
||||||
else:
|
|
||||||
target_players = hass.data[DATA_SQUEEZEBOX]
|
|
||||||
|
|
||||||
update_tasks = []
|
platform.async_register_entity_service(
|
||||||
for player in target_players:
|
SERVICE_CALL_QUERY,
|
||||||
await getattr(player, method["method"])(**params)
|
{
|
||||||
update_tasks.append(player.async_update_ha_state(True))
|
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:
|
platform.async_register_entity_service(
|
||||||
await asyncio.wait(update_tasks)
|
SERVICE_SYNC, {vol.Required(ATTR_OTHER_PLAYER): cv.string}, "async_sync",
|
||||||
|
)
|
||||||
|
|
||||||
for service in SERVICE_TO_METHOD:
|
platform.async_register_entity_service(SERVICE_UNSYNC, None, "async_unsync")
|
||||||
schema = SERVICE_TO_METHOD[service]["schema"]
|
|
||||||
hass.services.async_register(
|
|
||||||
DOMAIN, service, async_service_handler, schema=schema
|
|
||||||
)
|
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
@ -188,6 +180,18 @@ class SqueezeBoxDevice(MediaPlayerEntity):
|
|||||||
"""Initialize the SqueezeBox device."""
|
"""Initialize the SqueezeBox device."""
|
||||||
self._player = player
|
self._player = player
|
||||||
self._last_update = None
|
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
|
@property
|
||||||
def name(self):
|
def name(self):
|
||||||
@ -284,6 +288,21 @@ class SqueezeBoxDevice(MediaPlayerEntity):
|
|||||||
"""Flag media player features that are supported."""
|
"""Flag media player features that are supported."""
|
||||||
return SUPPORT_SQUEEZEBOX
|
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):
|
async def async_turn_off(self):
|
||||||
"""Turn off media player."""
|
"""Turn off media player."""
|
||||||
await self._player.async_set_power(False)
|
await self._player.async_set_power(False)
|
||||||
@ -366,3 +385,35 @@ class SqueezeBoxDevice(MediaPlayerEntity):
|
|||||||
for parameter in parameters:
|
for parameter in parameters:
|
||||||
all_params.append(parameter)
|
all_params.append(parameter)
|
||||||
await self._player.async_query(*all_params)
|
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).
|
description: Command to pass to Logitech Media Server (p0 in the CLI documentation).
|
||||||
example: "playlist"
|
example: "playlist"
|
||||||
parameters:
|
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="]
|
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
|
pyspcwebgw==0.4.0
|
||||||
|
|
||||||
# homeassistant.components.squeezebox
|
# homeassistant.components.squeezebox
|
||||||
pysqueezebox==0.1.2
|
pysqueezebox==0.1.4
|
||||||
|
|
||||||
# homeassistant.components.stiebel_eltron
|
# homeassistant.components.stiebel_eltron
|
||||||
pystiebeleltron==0.0.1.dev2
|
pystiebeleltron==0.0.1.dev2
|
||||||
|
Loading…
x
Reference in New Issue
Block a user