diff --git a/homeassistant/components/linkplay/__init__.py b/homeassistant/components/linkplay/__init__.py index 918e52a755d..2da73666cc4 100644 --- a/homeassistant/components/linkplay/__init__.py +++ b/homeassistant/components/linkplay/__init__.py @@ -13,7 +13,7 @@ from homeassistant.const import CONF_HOST from homeassistant.core import HomeAssistant from homeassistant.exceptions import ConfigEntryNotReady -from .const import CONTROLLER, CONTROLLER_KEY, DOMAIN, PLATFORMS +from .const import DOMAIN, PLATFORMS, SHARED_DATA, LinkPlaySharedData from .utils import async_get_client_session @@ -44,11 +44,11 @@ async def async_setup_entry(hass: HomeAssistant, entry: LinkPlayConfigEntry) -> # setup the controller and discover multirooms controller: LinkPlayController | None = None hass.data.setdefault(DOMAIN, {}) - if CONTROLLER not in hass.data[DOMAIN]: + if SHARED_DATA not in hass.data[DOMAIN]: controller = LinkPlayController(session) - hass.data[DOMAIN][CONTROLLER_KEY] = controller + hass.data[DOMAIN][SHARED_DATA] = LinkPlaySharedData(controller, {}) else: - controller = hass.data[DOMAIN][CONTROLLER_KEY] + controller = hass.data[DOMAIN][SHARED_DATA].controller await controller.add_bridge(bridge) await controller.discover_multirooms() @@ -62,4 +62,10 @@ async def async_setup_entry(hass: HomeAssistant, entry: LinkPlayConfigEntry) -> async def async_unload_entry(hass: HomeAssistant, entry: LinkPlayConfigEntry) -> bool: """Unload a config entry.""" + # remove the bridge from the controller and discover multirooms + bridge: LinkPlayBridge | None = entry.runtime_data.bridge + controller: LinkPlayController = hass.data[DOMAIN][SHARED_DATA].controller + await controller.remove_bridge(bridge) + await controller.discover_multirooms() + return await hass.config_entries.async_unload_platforms(entry, PLATFORMS) diff --git a/homeassistant/components/linkplay/const.py b/homeassistant/components/linkplay/const.py index e10450cf255..74b87f4aae9 100644 --- a/homeassistant/components/linkplay/const.py +++ b/homeassistant/components/linkplay/const.py @@ -1,12 +1,23 @@ """LinkPlay constants.""" +from dataclasses import dataclass + from linkplay.controller import LinkPlayController from homeassistant.const import Platform from homeassistant.util.hass_dict import HassKey + +@dataclass +class LinkPlaySharedData: + """Shared data for LinkPlay.""" + + controller: LinkPlayController + entity_to_bridge: dict[str, str] + + DOMAIN = "linkplay" -CONTROLLER = "controller" -CONTROLLER_KEY: HassKey[LinkPlayController] = HassKey(CONTROLLER) +SHARED_DATA = "shared_data" +SHARED_DATA_KEY: HassKey[LinkPlaySharedData] = HassKey(SHARED_DATA) PLATFORMS = [Platform.BUTTON, Platform.MEDIA_PLAYER] DATA_SESSION = "session" diff --git a/homeassistant/components/linkplay/media_player.py b/homeassistant/components/linkplay/media_player.py index 67aa424e3a2..f20c3c80751 100644 --- a/homeassistant/components/linkplay/media_player.py +++ b/homeassistant/components/linkplay/media_player.py @@ -23,19 +23,14 @@ 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, ServiceValidationError -from homeassistant.helpers import ( - config_validation as cv, - entity_platform, - entity_registry as er, -) +from homeassistant.helpers import config_validation as cv, entity_platform from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback from homeassistant.util.dt import utcnow -from . import LinkPlayConfigEntry, LinkPlayData -from .const import CONTROLLER_KEY, DOMAIN +from . import SHARED_DATA, LinkPlayConfigEntry +from .const import DOMAIN from .entity import LinkPlayBaseEntity, exception_wrap _LOGGER = logging.getLogger(__name__) @@ -163,6 +158,13 @@ class LinkPlayMediaPlayerEntity(LinkPlayBaseEntity, MediaPlayerEntity): mode.value for mode in bridge.player.available_equalizer_modes ] + async def async_added_to_hass(self) -> None: + """Handle common setup when added to hass.""" + await super().async_added_to_hass() + self.hass.data[DOMAIN][SHARED_DATA].entity_to_bridge[self.entity_id] = ( + self._bridge.device.uuid + ) + @exception_wrap async def async_update(self) -> None: """Update the state of the media player.""" @@ -276,62 +278,56 @@ class LinkPlayMediaPlayerEntity(LinkPlayBaseEntity, MediaPlayerEntity): 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] + controller: LinkPlayController = self.hass.data[DOMAIN][SHARED_DATA].controller 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) + bridge = await 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: + async def _get_linkplay_bridge(self, entity_id: str) -> LinkPlayBridge: """Get linkplay bridge from entity_id.""" - entity_registry = er.async_get(self.hass) + shared_data = self.hass.data[DOMAIN][SHARED_DATA] + controller = shared_data.controller + bridge_uuid = shared_data.entity_to_bridge.get(entity_id, None) + bridge = await controller.find_bridge(bridge_uuid) - # 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 - ): + if bridge 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 + return 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 - ] + if multiroom is None: + return [] - return [] + shared_data = self.hass.data[DOMAIN][SHARED_DATA] + + return [ + entity_id + for entity_id, bridge in shared_data.entity_to_bridge.items() + if bridge + in [multiroom.leader.device.uuid] + + [follower.device.uuid for follower in multiroom.followers] + ] @exception_wrap async def async_unjoin_player(self) -> None: """Remove this player from any group.""" - controller: LinkPlayController = self.hass.data[DOMAIN][CONTROLLER_KEY] + controller: LinkPlayController = self.hass.data[DOMAIN][SHARED_DATA].controller multiroom = self._bridge.multiroom if multiroom is not None: