mirror of
https://github.com/home-assistant/core.git
synced 2025-07-24 21:57:51 +00:00
Remove support for cast dynamic speaker groups (#33884)
This commit is contained in:
parent
496da8823d
commit
6d3046cb42
@ -23,7 +23,6 @@ class ChromecastInfo:
|
|||||||
) # always convert UUID to string if not None
|
) # always convert UUID to string if not None
|
||||||
model_name = attr.ib(type=str, default="")
|
model_name = attr.ib(type=str, default="")
|
||||||
friendly_name = attr.ib(type=Optional[str], default=None)
|
friendly_name = attr.ib(type=Optional[str], default=None)
|
||||||
is_dynamic_group = attr.ib(type=Optional[bool], default=None)
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def is_audio_group(self) -> bool:
|
def is_audio_group(self) -> bool:
|
||||||
@ -33,19 +32,7 @@ class ChromecastInfo:
|
|||||||
@property
|
@property
|
||||||
def is_information_complete(self) -> bool:
|
def is_information_complete(self) -> bool:
|
||||||
"""Return if all information is filled out."""
|
"""Return if all information is filled out."""
|
||||||
want_dynamic_group = self.is_audio_group
|
return all(attr.astuple(self))
|
||||||
have_dynamic_group = self.is_dynamic_group is not None
|
|
||||||
have_all_except_dynamic_group = all(
|
|
||||||
attr.astuple(
|
|
||||||
self,
|
|
||||||
filter=attr.filters.exclude(
|
|
||||||
attr.fields(ChromecastInfo).is_dynamic_group
|
|
||||||
),
|
|
||||||
)
|
|
||||||
)
|
|
||||||
return have_all_except_dynamic_group and (
|
|
||||||
not want_dynamic_group or have_dynamic_group
|
|
||||||
)
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def host_port(self) -> Tuple[str, int]:
|
def host_port(self) -> Tuple[str, int]:
|
||||||
@ -70,8 +57,6 @@ class ChromecastInfo:
|
|||||||
return self
|
return self
|
||||||
|
|
||||||
if self.is_audio_group:
|
if self.is_audio_group:
|
||||||
is_dynamic_group = False
|
|
||||||
|
|
||||||
return ChromecastInfo(
|
return ChromecastInfo(
|
||||||
service=self.service,
|
service=self.service,
|
||||||
host=self.host,
|
host=self.host,
|
||||||
@ -79,7 +64,6 @@ class ChromecastInfo:
|
|||||||
uuid=self.uuid,
|
uuid=self.uuid,
|
||||||
friendly_name=self.friendly_name,
|
friendly_name=self.friendly_name,
|
||||||
model_name=self.model_name,
|
model_name=self.model_name,
|
||||||
is_dynamic_group=is_dynamic_group,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
# Fill out some missing information (friendly_name, uuid) via HTTP dial.
|
# Fill out some missing information (friendly_name, uuid) via HTTP dial.
|
||||||
@ -99,14 +83,6 @@ class ChromecastInfo:
|
|||||||
model_name=self.model_name,
|
model_name=self.model_name,
|
||||||
)
|
)
|
||||||
|
|
||||||
def same_dynamic_group(self, other: "ChromecastInfo") -> bool:
|
|
||||||
"""Test chromecast info is same dynamic group."""
|
|
||||||
return (
|
|
||||||
self.is_audio_group
|
|
||||||
and other.is_dynamic_group
|
|
||||||
and self.friendly_name == other.friendly_name
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class ChromeCastZeroconf:
|
class ChromeCastZeroconf:
|
||||||
"""Class to hold a zeroconf instance."""
|
"""Class to hold a zeroconf instance."""
|
||||||
@ -190,45 +166,3 @@ class CastStatusListener:
|
|||||||
else:
|
else:
|
||||||
self._mz_mgr.deregister_listener(self._uuid, self)
|
self._mz_mgr.deregister_listener(self._uuid, self)
|
||||||
self._valid = False
|
self._valid = False
|
||||||
|
|
||||||
|
|
||||||
class DynamicGroupCastStatusListener:
|
|
||||||
"""Helper class to handle pychromecast status callbacks.
|
|
||||||
|
|
||||||
Necessary because a CastDevice entity can create a new socket client
|
|
||||||
and therefore callbacks from multiple chromecast connections can
|
|
||||||
potentially arrive. This class allows invalidating past chromecast objects.
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self, cast_device, chromecast, mz_mgr):
|
|
||||||
"""Initialize the status listener."""
|
|
||||||
self._cast_device = cast_device
|
|
||||||
self._uuid = chromecast.uuid
|
|
||||||
self._valid = True
|
|
||||||
self._mz_mgr = mz_mgr
|
|
||||||
|
|
||||||
chromecast.register_status_listener(self)
|
|
||||||
chromecast.socket_client.media_controller.register_status_listener(self)
|
|
||||||
chromecast.register_connection_listener(self)
|
|
||||||
self._mz_mgr.add_multizone(chromecast)
|
|
||||||
|
|
||||||
def new_cast_status(self, cast_status):
|
|
||||||
"""Handle reception of a new CastStatus."""
|
|
||||||
|
|
||||||
def new_media_status(self, media_status):
|
|
||||||
"""Handle reception of a new MediaStatus."""
|
|
||||||
if self._valid:
|
|
||||||
self._cast_device.new_dynamic_group_media_status(media_status)
|
|
||||||
|
|
||||||
def new_connection_status(self, connection_status):
|
|
||||||
"""Handle reception of a new ConnectionStatus."""
|
|
||||||
if self._valid:
|
|
||||||
self._cast_device.new_dynamic_group_connection_status(connection_status)
|
|
||||||
|
|
||||||
def invalidate(self):
|
|
||||||
"""Invalidate this status listener.
|
|
||||||
|
|
||||||
All following callbacks won't be forwarded.
|
|
||||||
"""
|
|
||||||
self._mz_mgr.remove_multizone(self._uuid)
|
|
||||||
self._valid = False
|
|
||||||
|
@ -56,12 +56,7 @@ from .const import (
|
|||||||
SIGNAL_HASS_CAST_SHOW_VIEW,
|
SIGNAL_HASS_CAST_SHOW_VIEW,
|
||||||
)
|
)
|
||||||
from .discovery import setup_internal_discovery
|
from .discovery import setup_internal_discovery
|
||||||
from .helpers import (
|
from .helpers import CastStatusListener, ChromecastInfo, ChromeCastZeroconf
|
||||||
CastStatusListener,
|
|
||||||
ChromecastInfo,
|
|
||||||
ChromeCastZeroconf,
|
|
||||||
DynamicGroupCastStatusListener,
|
|
||||||
)
|
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
@ -101,10 +96,6 @@ def _async_create_cast_device(hass: HomeAssistantType, info: ChromecastInfo):
|
|||||||
return CastDevice(info)
|
return CastDevice(info)
|
||||||
|
|
||||||
# Found a cast with UUID
|
# Found a cast with UUID
|
||||||
if info.is_dynamic_group:
|
|
||||||
# This is a dynamic group, do not add it.
|
|
||||||
return None
|
|
||||||
|
|
||||||
added_casts = hass.data[ADDED_CAST_DEVICES_KEY]
|
added_casts = hass.data[ADDED_CAST_DEVICES_KEY]
|
||||||
if info.uuid in added_casts:
|
if info.uuid in added_casts:
|
||||||
# Already added this one, the entity will take care of moved hosts
|
# Already added this one, the entity will take care of moved hosts
|
||||||
@ -201,19 +192,11 @@ class CastDevice(MediaPlayerDevice):
|
|||||||
self.cast_status = None
|
self.cast_status = None
|
||||||
self.media_status = None
|
self.media_status = None
|
||||||
self.media_status_received = None
|
self.media_status_received = None
|
||||||
self._dynamic_group_cast_info: ChromecastInfo = None
|
|
||||||
self._dynamic_group_cast: Optional[pychromecast.Chromecast] = None
|
|
||||||
self.dynamic_group_media_status = None
|
|
||||||
self.dynamic_group_media_status_received = None
|
|
||||||
self.mz_media_status = {}
|
self.mz_media_status = {}
|
||||||
self.mz_media_status_received = {}
|
self.mz_media_status_received = {}
|
||||||
self.mz_mgr = None
|
self.mz_mgr = None
|
||||||
self._available = False
|
self._available = False
|
||||||
self._dynamic_group_available = False
|
|
||||||
self._status_listener: Optional[CastStatusListener] = None
|
self._status_listener: Optional[CastStatusListener] = None
|
||||||
self._dynamic_group_status_listener: Optional[
|
|
||||||
DynamicGroupCastStatusListener
|
|
||||||
] = None
|
|
||||||
self._hass_cast_controller: Optional[HomeAssistantController] = None
|
self._hass_cast_controller: Optional[HomeAssistantController] = None
|
||||||
|
|
||||||
self._add_remove_handler = None
|
self._add_remove_handler = None
|
||||||
@ -232,20 +215,6 @@ class CastDevice(MediaPlayerDevice):
|
|||||||
self.hass.async_create_task(
|
self.hass.async_create_task(
|
||||||
async_create_catching_coro(self.async_set_cast_info(self._cast_info))
|
async_create_catching_coro(self.async_set_cast_info(self._cast_info))
|
||||||
)
|
)
|
||||||
for info in self.hass.data[KNOWN_CHROMECAST_INFO_KEY]:
|
|
||||||
if self._cast_info.same_dynamic_group(info):
|
|
||||||
_LOGGER.debug(
|
|
||||||
"[%s %s (%s:%s)] Found dynamic group: %s",
|
|
||||||
self.entity_id,
|
|
||||||
self._cast_info.friendly_name,
|
|
||||||
self._cast_info.host,
|
|
||||||
self._cast_info.port,
|
|
||||||
info,
|
|
||||||
)
|
|
||||||
self.hass.async_create_task(
|
|
||||||
async_create_catching_coro(self.async_set_dynamic_group(info))
|
|
||||||
)
|
|
||||||
break
|
|
||||||
|
|
||||||
self._cast_view_remove_handler = async_dispatcher_connect(
|
self._cast_view_remove_handler = async_dispatcher_connect(
|
||||||
self.hass, SIGNAL_HASS_CAST_SHOW_VIEW, self._handle_signal_show_view
|
self.hass, SIGNAL_HASS_CAST_SHOW_VIEW, self._handle_signal_show_view
|
||||||
@ -358,69 +327,6 @@ class CastDevice(MediaPlayerDevice):
|
|||||||
self.services,
|
self.services,
|
||||||
)
|
)
|
||||||
|
|
||||||
async def async_set_dynamic_group(self, cast_info):
|
|
||||||
"""Set the cast information and set up the chromecast object."""
|
|
||||||
|
|
||||||
_LOGGER.debug(
|
|
||||||
"[%s %s (%s:%s)] Connecting to dynamic group by host %s",
|
|
||||||
self.entity_id,
|
|
||||||
self._cast_info.friendly_name,
|
|
||||||
self._cast_info.host,
|
|
||||||
self._cast_info.port,
|
|
||||||
cast_info,
|
|
||||||
)
|
|
||||||
|
|
||||||
await self.async_del_dynamic_group()
|
|
||||||
self._dynamic_group_cast_info = cast_info
|
|
||||||
|
|
||||||
# pylint: disable=protected-access
|
|
||||||
chromecast = await self.hass.async_add_executor_job(
|
|
||||||
pychromecast._get_chromecast_from_host,
|
|
||||||
(
|
|
||||||
cast_info.host,
|
|
||||||
cast_info.port,
|
|
||||||
cast_info.uuid,
|
|
||||||
cast_info.model_name,
|
|
||||||
cast_info.friendly_name,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
self._dynamic_group_cast = chromecast
|
|
||||||
|
|
||||||
if CAST_MULTIZONE_MANAGER_KEY not in self.hass.data:
|
|
||||||
self.hass.data[CAST_MULTIZONE_MANAGER_KEY] = MultizoneManager()
|
|
||||||
|
|
||||||
mz_mgr = self.hass.data[CAST_MULTIZONE_MANAGER_KEY]
|
|
||||||
|
|
||||||
self._dynamic_group_status_listener = DynamicGroupCastStatusListener(
|
|
||||||
self, chromecast, mz_mgr
|
|
||||||
)
|
|
||||||
self._dynamic_group_available = False
|
|
||||||
self.dynamic_group_media_status = chromecast.media_controller.status
|
|
||||||
self._dynamic_group_cast.start()
|
|
||||||
self.async_write_ha_state()
|
|
||||||
|
|
||||||
async def async_del_dynamic_group(self):
|
|
||||||
"""Remove the dynamic group."""
|
|
||||||
cast_info = self._dynamic_group_cast_info
|
|
||||||
_LOGGER.debug(
|
|
||||||
"[%s %s (%s:%s)] Remove dynamic group: %s",
|
|
||||||
self.entity_id,
|
|
||||||
self._cast_info.friendly_name,
|
|
||||||
self._cast_info.host,
|
|
||||||
self._cast_info.port,
|
|
||||||
cast_info.service if cast_info else None,
|
|
||||||
)
|
|
||||||
|
|
||||||
self._dynamic_group_available = False
|
|
||||||
self._dynamic_group_cast_info = None
|
|
||||||
if self._dynamic_group_cast is not None:
|
|
||||||
await self.hass.async_add_executor_job(self._dynamic_group_cast.disconnect)
|
|
||||||
|
|
||||||
self._dynamic_group_invalidate()
|
|
||||||
|
|
||||||
self.async_write_ha_state()
|
|
||||||
|
|
||||||
async def _async_disconnect(self):
|
async def _async_disconnect(self):
|
||||||
"""Disconnect Chromecast object if it is set."""
|
"""Disconnect Chromecast object if it is set."""
|
||||||
if self._chromecast is None:
|
if self._chromecast is None:
|
||||||
@ -437,8 +343,6 @@ class CastDevice(MediaPlayerDevice):
|
|||||||
self.async_write_ha_state()
|
self.async_write_ha_state()
|
||||||
|
|
||||||
await self.hass.async_add_executor_job(self._chromecast.disconnect)
|
await self.hass.async_add_executor_job(self._chromecast.disconnect)
|
||||||
if self._dynamic_group_cast is not None:
|
|
||||||
await self.hass.async_add_executor_job(self._dynamic_group_cast.disconnect)
|
|
||||||
|
|
||||||
self._invalidate()
|
self._invalidate()
|
||||||
|
|
||||||
@ -458,15 +362,6 @@ class CastDevice(MediaPlayerDevice):
|
|||||||
self._status_listener.invalidate()
|
self._status_listener.invalidate()
|
||||||
self._status_listener = None
|
self._status_listener = None
|
||||||
|
|
||||||
def _dynamic_group_invalidate(self):
|
|
||||||
"""Invalidate some attributes."""
|
|
||||||
self._dynamic_group_cast = None
|
|
||||||
self.dynamic_group_media_status = None
|
|
||||||
self.dynamic_group_media_status_received = None
|
|
||||||
if self._dynamic_group_status_listener is not None:
|
|
||||||
self._dynamic_group_status_listener.invalidate()
|
|
||||||
self._dynamic_group_status_listener = None
|
|
||||||
|
|
||||||
# ========== Callbacks ==========
|
# ========== Callbacks ==========
|
||||||
def new_cast_status(self, cast_status):
|
def new_cast_status(self, cast_status):
|
||||||
"""Handle updates of the cast status."""
|
"""Handle updates of the cast status."""
|
||||||
@ -515,44 +410,6 @@ class CastDevice(MediaPlayerDevice):
|
|||||||
self._available = new_available
|
self._available = new_available
|
||||||
self.schedule_update_ha_state()
|
self.schedule_update_ha_state()
|
||||||
|
|
||||||
def new_dynamic_group_media_status(self, media_status):
|
|
||||||
"""Handle updates of the media status."""
|
|
||||||
self.dynamic_group_media_status = media_status
|
|
||||||
self.dynamic_group_media_status_received = dt_util.utcnow()
|
|
||||||
self.schedule_update_ha_state()
|
|
||||||
|
|
||||||
def new_dynamic_group_connection_status(self, connection_status):
|
|
||||||
"""Handle updates of connection status."""
|
|
||||||
_LOGGER.debug(
|
|
||||||
"[%s %s (%s:%s)] Received dynamic group connection status: %s",
|
|
||||||
self.entity_id,
|
|
||||||
self._cast_info.friendly_name,
|
|
||||||
self._cast_info.host,
|
|
||||||
self._cast_info.port,
|
|
||||||
connection_status.status,
|
|
||||||
)
|
|
||||||
if connection_status.status == CONNECTION_STATUS_DISCONNECTED:
|
|
||||||
self._dynamic_group_available = False
|
|
||||||
self._dynamic_group_invalidate()
|
|
||||||
self.schedule_update_ha_state()
|
|
||||||
return
|
|
||||||
|
|
||||||
new_available = connection_status.status == CONNECTION_STATUS_CONNECTED
|
|
||||||
if new_available != self._dynamic_group_available:
|
|
||||||
# Connection status callbacks happen often when disconnected.
|
|
||||||
# Only update state when availability changed to put less pressure
|
|
||||||
# on state machine.
|
|
||||||
_LOGGER.debug(
|
|
||||||
"[%s %s (%s:%s)] Dynamic group availability changed: %s",
|
|
||||||
self.entity_id,
|
|
||||||
self._cast_info.friendly_name,
|
|
||||||
self._cast_info.host,
|
|
||||||
self._cast_info.port,
|
|
||||||
connection_status.status,
|
|
||||||
)
|
|
||||||
self._dynamic_group_available = new_available
|
|
||||||
self.schedule_update_ha_state()
|
|
||||||
|
|
||||||
def multizone_new_media_status(self, group_uuid, media_status):
|
def multizone_new_media_status(self, group_uuid, media_status):
|
||||||
"""Handle updates of audio group media status."""
|
"""Handle updates of audio group media status."""
|
||||||
_LOGGER.debug(
|
_LOGGER.debug(
|
||||||
@ -573,18 +430,11 @@ class CastDevice(MediaPlayerDevice):
|
|||||||
"""
|
"""
|
||||||
Return media status.
|
Return media status.
|
||||||
|
|
||||||
First try from our own cast, then dynamic groups and finally
|
First try from our own cast, then groups which our cast is a member in.
|
||||||
groups which our cast is a member in.
|
|
||||||
"""
|
"""
|
||||||
media_status = self.media_status
|
media_status = self.media_status
|
||||||
media_controller = self._chromecast.media_controller
|
media_controller = self._chromecast.media_controller
|
||||||
|
|
||||||
if (
|
|
||||||
media_status is None or media_status.player_state == "UNKNOWN"
|
|
||||||
) and self._dynamic_group_cast is not None:
|
|
||||||
media_status = self.dynamic_group_media_status
|
|
||||||
media_controller = self._dynamic_group_cast.media_controller
|
|
||||||
|
|
||||||
if media_status is None or media_status.player_state == "UNKNOWN":
|
if media_status is None or media_status.player_state == "UNKNOWN":
|
||||||
groups = self.mz_media_status
|
groups = self.mz_media_status
|
||||||
for k, val in groups.items():
|
for k, val in groups.items():
|
||||||
@ -652,7 +502,7 @@ class CastDevice(MediaPlayerDevice):
|
|||||||
|
|
||||||
def play_media(self, media_type, media_id, **kwargs):
|
def play_media(self, media_type, media_id, **kwargs):
|
||||||
"""Play media from a URL."""
|
"""Play media from a URL."""
|
||||||
# We do not want this to be forwarded to a group / dynamic group
|
# We do not want this to be forwarded to a group
|
||||||
self._chromecast.media_controller.play_media(media_id, media_type)
|
self._chromecast.media_controller.play_media(media_id, media_type)
|
||||||
|
|
||||||
# ========== Properties ==========
|
# ========== Properties ==========
|
||||||
@ -685,18 +535,11 @@ class CastDevice(MediaPlayerDevice):
|
|||||||
"""
|
"""
|
||||||
Return media status.
|
Return media status.
|
||||||
|
|
||||||
First try from our own cast, then dynamic groups and finally
|
First try from our own cast, then groups which our cast is a member in.
|
||||||
groups which our cast is a member in.
|
|
||||||
"""
|
"""
|
||||||
media_status = self.media_status
|
media_status = self.media_status
|
||||||
media_status_received = self.media_status_received
|
media_status_received = self.media_status_received
|
||||||
|
|
||||||
if (
|
|
||||||
media_status is None or media_status.player_state == "UNKNOWN"
|
|
||||||
) and self._dynamic_group_cast is not None:
|
|
||||||
media_status = self.dynamic_group_media_status
|
|
||||||
media_status_received = self.dynamic_group_media_status_received
|
|
||||||
|
|
||||||
if media_status is None or media_status.player_state == "UNKNOWN":
|
if media_status is None or media_status.player_state == "UNKNOWN":
|
||||||
groups = self.mz_media_status
|
groups = self.mz_media_status
|
||||||
for k, val in groups.items():
|
for k, val in groups.items():
|
||||||
@ -887,11 +730,6 @@ class CastDevice(MediaPlayerDevice):
|
|||||||
# We can't handle empty UUIDs
|
# We can't handle empty UUIDs
|
||||||
return
|
return
|
||||||
|
|
||||||
if self._cast_info.same_dynamic_group(discover):
|
|
||||||
_LOGGER.debug("Discovered matching dynamic group: %s", discover)
|
|
||||||
await self.async_set_dynamic_group(discover)
|
|
||||||
return
|
|
||||||
|
|
||||||
if self._cast_info.uuid != discover.uuid:
|
if self._cast_info.uuid != discover.uuid:
|
||||||
# Discovered is not our device.
|
# Discovered is not our device.
|
||||||
return
|
return
|
||||||
@ -915,14 +753,6 @@ class CastDevice(MediaPlayerDevice):
|
|||||||
# We can't handle empty UUIDs
|
# We can't handle empty UUIDs
|
||||||
return
|
return
|
||||||
|
|
||||||
if (
|
|
||||||
self._dynamic_group_cast_info is not None
|
|
||||||
and self._dynamic_group_cast_info.uuid == discover.uuid
|
|
||||||
):
|
|
||||||
_LOGGER.debug("Removed matching dynamic group: %s", discover)
|
|
||||||
await self.async_del_dynamic_group()
|
|
||||||
return
|
|
||||||
|
|
||||||
if self._cast_info.uuid != discover.uuid:
|
if self._cast_info.uuid != discover.uuid:
|
||||||
# Removed is not our device.
|
# Removed is not our device.
|
||||||
return
|
return
|
||||||
|
@ -435,58 +435,6 @@ async def test_group_media_states(hass: HomeAssistantType):
|
|||||||
assert state.state == "playing"
|
assert state.state == "playing"
|
||||||
|
|
||||||
|
|
||||||
async def test_dynamic_group_media_states(hass: HomeAssistantType):
|
|
||||||
"""Test media states are read from group if entity has no state."""
|
|
||||||
info = get_fake_chromecast_info()
|
|
||||||
full_info = attr.evolve(
|
|
||||||
info, model_name="google home", friendly_name="Speaker", uuid=FakeUUID
|
|
||||||
)
|
|
||||||
|
|
||||||
with patch(
|
|
||||||
"homeassistant.components.cast.helpers.dial.get_device_status",
|
|
||||||
return_value=full_info,
|
|
||||||
):
|
|
||||||
chromecast, entity = await async_setup_media_player_cast(hass, info)
|
|
||||||
|
|
||||||
entity._available = True
|
|
||||||
entity.schedule_update_ha_state()
|
|
||||||
await hass.async_block_till_done()
|
|
||||||
|
|
||||||
state = hass.states.get("media_player.speaker")
|
|
||||||
assert state is not None
|
|
||||||
assert state.name == "Speaker"
|
|
||||||
assert state.state == "unknown"
|
|
||||||
assert entity.unique_id == full_info.uuid
|
|
||||||
|
|
||||||
group_media_status = MagicMock(images=None)
|
|
||||||
player_media_status = MagicMock(images=None)
|
|
||||||
|
|
||||||
# Player has no state, dynamic group is playing -> Should report 'playing'
|
|
||||||
entity._dynamic_group_cast = MagicMock()
|
|
||||||
group_media_status.player_is_playing = True
|
|
||||||
entity.new_dynamic_group_media_status(group_media_status)
|
|
||||||
await hass.async_block_till_done()
|
|
||||||
state = hass.states.get("media_player.speaker")
|
|
||||||
assert state.state == "playing"
|
|
||||||
|
|
||||||
# Player is paused, dynamic group is playing -> Should report 'paused'
|
|
||||||
player_media_status.player_is_playing = False
|
|
||||||
player_media_status.player_is_paused = True
|
|
||||||
entity.new_media_status(player_media_status)
|
|
||||||
await hass.async_block_till_done()
|
|
||||||
await hass.async_block_till_done()
|
|
||||||
state = hass.states.get("media_player.speaker")
|
|
||||||
assert state.state == "paused"
|
|
||||||
|
|
||||||
# Player is in unknown state, dynamic group is playing -> Should report
|
|
||||||
# 'playing'
|
|
||||||
player_media_status.player_state = "UNKNOWN"
|
|
||||||
entity.new_media_status(player_media_status)
|
|
||||||
await hass.async_block_till_done()
|
|
||||||
state = hass.states.get("media_player.speaker")
|
|
||||||
assert state.state == "playing"
|
|
||||||
|
|
||||||
|
|
||||||
async def test_group_media_control(hass: HomeAssistantType):
|
async def test_group_media_control(hass: HomeAssistantType):
|
||||||
"""Test media states are read from group if entity has no state."""
|
"""Test media states are read from group if entity has no state."""
|
||||||
info = get_fake_chromecast_info()
|
info = get_fake_chromecast_info()
|
||||||
@ -543,61 +491,6 @@ async def test_group_media_control(hass: HomeAssistantType):
|
|||||||
assert chromecast.media_controller.play_media.called
|
assert chromecast.media_controller.play_media.called
|
||||||
|
|
||||||
|
|
||||||
async def test_dynamic_group_media_control(hass: HomeAssistantType):
|
|
||||||
"""Test media states are read from group if entity has no state."""
|
|
||||||
info = get_fake_chromecast_info()
|
|
||||||
full_info = attr.evolve(
|
|
||||||
info, model_name="google home", friendly_name="Speaker", uuid=FakeUUID
|
|
||||||
)
|
|
||||||
|
|
||||||
with patch(
|
|
||||||
"homeassistant.components.cast.helpers.dial.get_device_status",
|
|
||||||
return_value=full_info,
|
|
||||||
):
|
|
||||||
chromecast, entity = await async_setup_media_player_cast(hass, info)
|
|
||||||
|
|
||||||
entity._available = True
|
|
||||||
entity.schedule_update_ha_state()
|
|
||||||
entity._dynamic_group_cast = MagicMock()
|
|
||||||
await hass.async_block_till_done()
|
|
||||||
|
|
||||||
state = hass.states.get("media_player.speaker")
|
|
||||||
assert state is not None
|
|
||||||
assert state.name == "Speaker"
|
|
||||||
assert state.state == "unknown"
|
|
||||||
assert entity.unique_id == full_info.uuid
|
|
||||||
|
|
||||||
group_media_status = MagicMock(images=None)
|
|
||||||
player_media_status = MagicMock(images=None)
|
|
||||||
|
|
||||||
# Player has no state, dynamic group is playing -> Should forward
|
|
||||||
group_media_status.player_is_playing = True
|
|
||||||
entity.new_dynamic_group_media_status(group_media_status)
|
|
||||||
entity.media_previous_track()
|
|
||||||
assert entity._dynamic_group_cast.media_controller.queue_prev.called
|
|
||||||
assert not chromecast.media_controller.queue_prev.called
|
|
||||||
|
|
||||||
# Player is paused, dynamic group is playing -> Should not forward
|
|
||||||
player_media_status.player_is_playing = False
|
|
||||||
player_media_status.player_is_paused = True
|
|
||||||
entity.new_media_status(player_media_status)
|
|
||||||
entity.media_next_track()
|
|
||||||
assert not entity._dynamic_group_cast.media_controller.queue_next.called
|
|
||||||
assert chromecast.media_controller.queue_next.called
|
|
||||||
|
|
||||||
# Player is in unknown state, dynamic group is playing -> Should forward
|
|
||||||
player_media_status.player_state = "UNKNOWN"
|
|
||||||
entity.new_media_status(player_media_status)
|
|
||||||
entity.media_seek(None)
|
|
||||||
assert entity._dynamic_group_cast.media_controller.seek.called
|
|
||||||
assert not chromecast.media_controller.seek.called
|
|
||||||
|
|
||||||
# Verify play_media is not forwarded
|
|
||||||
entity.play_media(None, None)
|
|
||||||
assert not entity._dynamic_group_cast.media_controller.play_media.called
|
|
||||||
assert chromecast.media_controller.play_media.called
|
|
||||||
|
|
||||||
|
|
||||||
async def test_disconnect_on_stop(hass: HomeAssistantType):
|
async def test_disconnect_on_stop(hass: HomeAssistantType):
|
||||||
"""Test cast device disconnects socket on stop."""
|
"""Test cast device disconnects socket on stop."""
|
||||||
info = get_fake_chromecast_info()
|
info = get_fake_chromecast_info()
|
||||||
|
Loading…
x
Reference in New Issue
Block a user