mirror of
https://github.com/home-assistant/core.git
synced 2025-04-25 01:38:02 +00:00
LinkPlay multiroom support (#127862)
This commit is contained in:
parent
66ca424d3a
commit
3734fa948f
@ -4,6 +4,7 @@ from dataclasses import dataclass
|
||||
|
||||
from aiohttp import ClientSession
|
||||
from linkplay.bridge import LinkPlayBridge
|
||||
from linkplay.controller import LinkPlayController
|
||||
from linkplay.discovery import linkplay_factory_httpapi_bridge
|
||||
from linkplay.exceptions import LinkPlayRequestException
|
||||
|
||||
@ -12,7 +13,7 @@ from homeassistant.const import CONF_HOST
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.exceptions import ConfigEntryNotReady
|
||||
|
||||
from .const import PLATFORMS
|
||||
from .const import CONTROLLER, CONTROLLER_KEY, DOMAIN, PLATFORMS
|
||||
from .utils import async_get_client_session
|
||||
|
||||
|
||||
@ -32,6 +33,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: LinkPlayConfigEntry) ->
|
||||
session: ClientSession = await async_get_client_session(hass)
|
||||
bridge: LinkPlayBridge | None = None
|
||||
|
||||
# try create a bridge
|
||||
try:
|
||||
bridge = await linkplay_factory_httpapi_bridge(entry.data[CONF_HOST], session)
|
||||
except LinkPlayRequestException as exception:
|
||||
@ -39,6 +41,19 @@ async def async_setup_entry(hass: HomeAssistant, entry: LinkPlayConfigEntry) ->
|
||||
f"Failed to connect to LinkPlay device at {entry.data[CONF_HOST]}"
|
||||
) from exception
|
||||
|
||||
# setup the controller and discover multirooms
|
||||
controller: LinkPlayController | None = None
|
||||
hass.data.setdefault(DOMAIN, {})
|
||||
if CONTROLLER not in hass.data[DOMAIN]:
|
||||
controller = LinkPlayController(session)
|
||||
hass.data[DOMAIN][CONTROLLER_KEY] = controller
|
||||
else:
|
||||
controller = hass.data[DOMAIN][CONTROLLER_KEY]
|
||||
|
||||
await controller.add_bridge(bridge)
|
||||
await controller.discover_multirooms()
|
||||
|
||||
# forward to platforms
|
||||
entry.runtime_data = LinkPlayData(bridge=bridge)
|
||||
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
|
||||
return True
|
||||
|
@ -1,7 +1,12 @@
|
||||
"""LinkPlay constants."""
|
||||
|
||||
from linkplay.controller import LinkPlayController
|
||||
|
||||
from homeassistant.const import Platform
|
||||
from homeassistant.util.hass_dict import HassKey
|
||||
|
||||
DOMAIN = "linkplay"
|
||||
CONTROLLER = "controller"
|
||||
CONTROLLER_KEY: HassKey[LinkPlayController] = HassKey(CONTROLLER)
|
||||
PLATFORMS = [Platform.MEDIA_PLAYER]
|
||||
DATA_SESSION = "session"
|
||||
|
@ -8,6 +8,7 @@ from typing import Any, Concatenate
|
||||
|
||||
from linkplay.bridge import LinkPlayBridge
|
||||
from linkplay.consts import EqualizerMode, LoopMode, PlayingMode, PlayingStatus
|
||||
from linkplay.controller import LinkPlayController, LinkPlayMultiroom
|
||||
from linkplay.exceptions import LinkPlayException, LinkPlayRequestException
|
||||
import voluptuous as vol
|
||||
|
||||
@ -22,18 +23,20 @@ from homeassistant.components.media_player import (
|
||||
RepeatMode,
|
||||
async_process_play_media_url,
|
||||
)
|
||||
from homeassistant.const import Platform
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.exceptions import HomeAssistantError
|
||||
from homeassistant.exceptions import HomeAssistantError, ServiceValidationError
|
||||
from homeassistant.helpers import (
|
||||
config_validation as cv,
|
||||
device_registry as dr,
|
||||
entity_platform,
|
||||
entity_registry as er,
|
||||
)
|
||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
from homeassistant.util.dt import utcnow
|
||||
|
||||
from . import LinkPlayConfigEntry
|
||||
from .const import DOMAIN
|
||||
from . import LinkPlayConfigEntry, LinkPlayData
|
||||
from .const import CONTROLLER_KEY, DOMAIN
|
||||
from .utils import MANUFACTURER_GENERIC, get_info_from_project
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
@ -290,6 +293,73 @@ class LinkPlayMediaPlayerEntity(MediaPlayerEntity):
|
||||
"""Play preset number."""
|
||||
await self._bridge.player.play_preset(preset_number)
|
||||
|
||||
@exception_wrap
|
||||
async def async_join_players(self, group_members: list[str]) -> None:
|
||||
"""Join `group_members` as a player group with the current player."""
|
||||
|
||||
controller: LinkPlayController = self.hass.data[DOMAIN][CONTROLLER_KEY]
|
||||
multiroom = self._bridge.multiroom
|
||||
if multiroom is None:
|
||||
multiroom = LinkPlayMultiroom(self._bridge)
|
||||
|
||||
for group_member in group_members:
|
||||
bridge = self._get_linkplay_bridge(group_member)
|
||||
if bridge:
|
||||
await multiroom.add_follower(bridge)
|
||||
|
||||
await controller.discover_multirooms()
|
||||
|
||||
def _get_linkplay_bridge(self, entity_id: str) -> LinkPlayBridge:
|
||||
"""Get linkplay bridge from entity_id."""
|
||||
|
||||
entity_registry = er.async_get(self.hass)
|
||||
|
||||
# Check for valid linkplay media_player entity
|
||||
entity_entry = entity_registry.async_get(entity_id)
|
||||
|
||||
if (
|
||||
entity_entry is None
|
||||
or entity_entry.domain != Platform.MEDIA_PLAYER
|
||||
or entity_entry.platform != DOMAIN
|
||||
or entity_entry.config_entry_id is None
|
||||
):
|
||||
raise ServiceValidationError(
|
||||
translation_domain=DOMAIN,
|
||||
translation_key="invalid_grouping_entity",
|
||||
translation_placeholders={"entity_id": entity_id},
|
||||
)
|
||||
|
||||
config_entry = self.hass.config_entries.async_get_entry(
|
||||
entity_entry.config_entry_id
|
||||
)
|
||||
assert config_entry
|
||||
|
||||
# Return bridge
|
||||
data: LinkPlayData = config_entry.runtime_data
|
||||
return data.bridge
|
||||
|
||||
@property
|
||||
def group_members(self) -> list[str]:
|
||||
"""List of players which are grouped together."""
|
||||
multiroom = self._bridge.multiroom
|
||||
if multiroom is not None:
|
||||
return [multiroom.leader.device.uuid] + [
|
||||
follower.device.uuid for follower in multiroom.followers
|
||||
]
|
||||
|
||||
return []
|
||||
|
||||
@exception_wrap
|
||||
async def async_unjoin_player(self) -> None:
|
||||
"""Remove this player from any group."""
|
||||
controller: LinkPlayController = self.hass.data[DOMAIN][CONTROLLER_KEY]
|
||||
|
||||
multiroom = self._bridge.multiroom
|
||||
if multiroom is not None:
|
||||
await multiroom.remove_follower(self._bridge)
|
||||
|
||||
await controller.discover_multirooms()
|
||||
|
||||
def _update_properties(self) -> None:
|
||||
"""Update the properties of the media player."""
|
||||
self._attr_available = True
|
||||
|
@ -34,5 +34,10 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"exceptions": {
|
||||
"invalid_grouping_entity": {
|
||||
"message": "Entity with id {entity_id} can't be added to the LinkPlay multiroom. Is the entity a LinkPlay mediaplayer?"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user