Bump pychromecast to 7.0.1 (#37225)
* Bump pychromecast to 7.0.1 * Fix tests * Mark configuration via platform for removal in 0.116 * Fix uuid checkpull/37104/head
parent
333dccc7af
commit
86c27b50f1
|
@ -21,20 +21,16 @@ _LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
def discover_chromecast(hass: HomeAssistant, info: ChromecastInfo):
|
def discover_chromecast(hass: HomeAssistant, info: ChromecastInfo):
|
||||||
"""Discover a Chromecast."""
|
"""Discover a Chromecast."""
|
||||||
if info in hass.data[KNOWN_CHROMECAST_INFO_KEY]:
|
if info.uuid is None:
|
||||||
_LOGGER.debug("Discovered previous chromecast %s", info)
|
_LOGGER.error("Discovered chromecast without uuid %s", info)
|
||||||
|
return
|
||||||
|
|
||||||
# Either discovered completely new chromecast or a "moved" one.
|
if info.uuid in hass.data[KNOWN_CHROMECAST_INFO_KEY]:
|
||||||
_LOGGER.debug("Discovered chromecast %s", info)
|
_LOGGER.debug("Discovered update for known chromecast %s", info)
|
||||||
|
else:
|
||||||
|
_LOGGER.debug("Discovered chromecast %s", info)
|
||||||
|
|
||||||
if info.uuid is not None:
|
hass.data[KNOWN_CHROMECAST_INFO_KEY][info.uuid] = info
|
||||||
# Remove previous cast infos with same uuid from known chromecasts.
|
|
||||||
same_uuid = {
|
|
||||||
x for x in hass.data[KNOWN_CHROMECAST_INFO_KEY] if info.uuid == x.uuid
|
|
||||||
}
|
|
||||||
hass.data[KNOWN_CHROMECAST_INFO_KEY] -= same_uuid
|
|
||||||
|
|
||||||
hass.data[KNOWN_CHROMECAST_INFO_KEY].add(info)
|
|
||||||
dispatcher_send(hass, SIGNAL_CAST_DISCOVERED, info)
|
dispatcher_send(hass, SIGNAL_CAST_DISCOVERED, info)
|
||||||
|
|
||||||
|
|
||||||
|
@ -54,47 +50,72 @@ def setup_internal_discovery(hass: HomeAssistant) -> None:
|
||||||
# Internal discovery is already running
|
# Internal discovery is already running
|
||||||
return
|
return
|
||||||
|
|
||||||
def internal_add_callback(name):
|
def internal_add_update_callback(uuid, service_name):
|
||||||
"""Handle zeroconf discovery of a new chromecast."""
|
"""Handle zeroconf discovery of a new or updated chromecast."""
|
||||||
mdns = listener.services[name]
|
service = listener.services[uuid]
|
||||||
|
|
||||||
|
# For support of deprecated IP based white listing
|
||||||
|
zconf = ChromeCastZeroconf.get_zeroconf()
|
||||||
|
service_info = None
|
||||||
|
tries = 0
|
||||||
|
while service_info is None and tries < 4:
|
||||||
|
try:
|
||||||
|
service_info = zconf.get_service_info(
|
||||||
|
"_googlecast._tcp.local.", service_name
|
||||||
|
)
|
||||||
|
except OSError:
|
||||||
|
# If the zeroconf fails to receive the necessary data we abort
|
||||||
|
# adding the service
|
||||||
|
break
|
||||||
|
tries += 1
|
||||||
|
|
||||||
|
if not service_info:
|
||||||
|
_LOGGER.warning(
|
||||||
|
"setup_internal_discovery failed to get info for %s, %s",
|
||||||
|
uuid,
|
||||||
|
service_name,
|
||||||
|
)
|
||||||
|
return
|
||||||
|
|
||||||
|
addresses = service_info.parsed_addresses()
|
||||||
|
host = addresses[0] if addresses else service_info.server
|
||||||
|
|
||||||
discover_chromecast(
|
discover_chromecast(
|
||||||
hass,
|
hass,
|
||||||
ChromecastInfo(
|
ChromecastInfo(
|
||||||
service=name,
|
services=service[0],
|
||||||
host=mdns[0],
|
uuid=service[1],
|
||||||
port=mdns[1],
|
model_name=service[2],
|
||||||
uuid=mdns[2],
|
friendly_name=service[3],
|
||||||
model_name=mdns[3],
|
host=host,
|
||||||
friendly_name=mdns[4],
|
port=service_info.port,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
def internal_remove_callback(name, mdns):
|
def internal_remove_callback(uuid, service_name, service):
|
||||||
"""Handle zeroconf discovery of a removed chromecast."""
|
"""Handle zeroconf discovery of a removed chromecast."""
|
||||||
_remove_chromecast(
|
_remove_chromecast(
|
||||||
hass,
|
hass,
|
||||||
ChromecastInfo(
|
ChromecastInfo(
|
||||||
service=name,
|
services=service[0],
|
||||||
host=mdns[0],
|
uuid=service[1],
|
||||||
port=mdns[1],
|
model_name=service[2],
|
||||||
uuid=mdns[2],
|
friendly_name=service[3],
|
||||||
model_name=mdns[3],
|
|
||||||
friendly_name=mdns[4],
|
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
_LOGGER.debug("Starting internal pychromecast discovery.")
|
_LOGGER.debug("Starting internal pychromecast discovery.")
|
||||||
listener = pychromecast.CastListener(
|
listener = pychromecast.CastListener(
|
||||||
internal_add_callback,
|
internal_add_update_callback,
|
||||||
internal_remove_callback,
|
internal_remove_callback,
|
||||||
internal_add_callback, # Use internal_add_callback also for updates
|
internal_add_update_callback,
|
||||||
)
|
)
|
||||||
browser = pychromecast.start_discovery(listener, ChromeCastZeroconf.get_zeroconf())
|
browser = pychromecast.start_discovery(listener, ChromeCastZeroconf.get_zeroconf())
|
||||||
|
|
||||||
def stop_discovery(event):
|
def stop_discovery(event):
|
||||||
"""Stop discovery of new chromecasts."""
|
"""Stop discovery of new chromecasts."""
|
||||||
_LOGGER.debug("Stopping internal pychromecast discovery.")
|
_LOGGER.debug("Stopping internal pychromecast discovery.")
|
||||||
pychromecast.stop_discovery(browser)
|
pychromecast.discovery.stop_discovery(browser)
|
||||||
hass.data[INTERNAL_DISCOVERY_RUNNING_KEY].release()
|
hass.data[INTERNAL_DISCOVERY_RUNNING_KEY].release()
|
||||||
|
|
||||||
hass.bus.listen_once(EVENT_HOMEASSISTANT_STOP, stop_discovery)
|
hass.bus.listen_once(EVENT_HOMEASSISTANT_STOP, stop_discovery)
|
||||||
|
|
|
@ -14,9 +14,9 @@ class ChromecastInfo:
|
||||||
This also has the same attributes as the mDNS fields by zeroconf.
|
This also has the same attributes as the mDNS fields by zeroconf.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
host = attr.ib(type=str)
|
services = attr.ib(type=Optional[set])
|
||||||
port = attr.ib(type=int)
|
host = attr.ib(type=Optional[str], default=None)
|
||||||
service = attr.ib(type=Optional[str], default=None)
|
port = attr.ib(type=Optional[int], default=0)
|
||||||
uuid = attr.ib(
|
uuid = attr.ib(
|
||||||
type=Optional[str], converter=attr.converters.optional(str), default=None
|
type=Optional[str], converter=attr.converters.optional(str), default=None
|
||||||
) # always convert UUID to string if not None
|
) # always convert UUID to string if not None
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
"name": "Google Cast",
|
"name": "Google Cast",
|
||||||
"config_flow": true,
|
"config_flow": true,
|
||||||
"documentation": "https://www.home-assistant.io/integrations/cast",
|
"documentation": "https://www.home-assistant.io/integrations/cast",
|
||||||
"requirements": ["pychromecast==6.0.0"],
|
"requirements": ["pychromecast==7.0.1"],
|
||||||
"after_dependencies": ["cloud","zeroconf"],
|
"after_dependencies": ["cloud","zeroconf"],
|
||||||
"zeroconf": ["_googlecast._tcp.local."],
|
"zeroconf": ["_googlecast._tcp.local."],
|
||||||
"codeowners": ["@emontnemery"]
|
"codeowners": ["@emontnemery"]
|
||||||
|
|
|
@ -55,7 +55,6 @@ from .const import (
|
||||||
DOMAIN as CAST_DOMAIN,
|
DOMAIN as CAST_DOMAIN,
|
||||||
KNOWN_CHROMECAST_INFO_KEY,
|
KNOWN_CHROMECAST_INFO_KEY,
|
||||||
SIGNAL_CAST_DISCOVERED,
|
SIGNAL_CAST_DISCOVERED,
|
||||||
SIGNAL_CAST_REMOVED,
|
|
||||||
SIGNAL_HASS_CAST_SHOW_VIEW,
|
SIGNAL_HASS_CAST_SHOW_VIEW,
|
||||||
)
|
)
|
||||||
from .discovery import setup_internal_discovery
|
from .discovery import setup_internal_discovery
|
||||||
|
@ -64,6 +63,7 @@ from .helpers import CastStatusListener, ChromecastInfo, ChromeCastZeroconf
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
CONF_IGNORE_CEC = "ignore_cec"
|
CONF_IGNORE_CEC = "ignore_cec"
|
||||||
|
CONF_UUID = "uuid"
|
||||||
CAST_SPLASH = "https://www.home-assistant.io/images/cast/splash.png"
|
CAST_SPLASH = "https://www.home-assistant.io/images/cast/splash.png"
|
||||||
|
|
||||||
SUPPORT_CAST = (
|
SUPPORT_CAST = (
|
||||||
|
@ -78,11 +78,26 @@ SUPPORT_CAST = (
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
|
ENTITY_SCHEMA = vol.All(
|
||||||
{
|
cv.deprecated(CONF_HOST, invalidation_version="0.116"),
|
||||||
vol.Optional(CONF_HOST): cv.string,
|
vol.Schema(
|
||||||
vol.Optional(CONF_IGNORE_CEC, default=[]): vol.All(cv.ensure_list, [cv.string]),
|
{
|
||||||
}
|
vol.Exclusive(CONF_HOST, "device_identifier"): cv.string,
|
||||||
|
vol.Exclusive(CONF_UUID, "device_identifier"): cv.string,
|
||||||
|
vol.Optional(CONF_IGNORE_CEC): vol.All(cv.ensure_list, [cv.string]),
|
||||||
|
}
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
PLATFORM_SCHEMA = vol.All(
|
||||||
|
cv.deprecated(CONF_HOST, invalidation_version="0.116"),
|
||||||
|
PLATFORM_SCHEMA.extend(
|
||||||
|
{
|
||||||
|
vol.Exclusive(CONF_HOST, "device_identifier"): cv.string,
|
||||||
|
vol.Exclusive(CONF_UUID, "device_identifier"): cv.string,
|
||||||
|
vol.Optional(CONF_IGNORE_CEC): vol.All(cv.ensure_list, [cv.string]),
|
||||||
|
}
|
||||||
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@ -111,13 +126,14 @@ def _async_create_cast_device(hass: HomeAssistantType, info: ChromecastInfo):
|
||||||
async def async_setup_platform(
|
async def async_setup_platform(
|
||||||
hass: HomeAssistantType, config: ConfigType, async_add_entities, discovery_info=None
|
hass: HomeAssistantType, config: ConfigType, async_add_entities, discovery_info=None
|
||||||
):
|
):
|
||||||
"""Set up thet Cast platform.
|
"""Set up the Cast platform.
|
||||||
|
|
||||||
Deprecated.
|
Deprecated.
|
||||||
"""
|
"""
|
||||||
_LOGGER.warning(
|
_LOGGER.warning(
|
||||||
"Setting configuration for Cast via platform is deprecated. "
|
"Setting configuration for Cast via platform is deprecated. "
|
||||||
"Configure via Cast integration instead."
|
"Configure via Cast integration instead."
|
||||||
|
"This option will become invalid in version 0.116."
|
||||||
)
|
)
|
||||||
await _async_setup_platform(hass, config, async_add_entities, discovery_info)
|
await _async_setup_platform(hass, config, async_add_entities, discovery_info)
|
||||||
|
|
||||||
|
@ -130,7 +146,10 @@ async def async_setup_entry(hass, config_entry, async_add_entities):
|
||||||
|
|
||||||
# no pending task
|
# no pending task
|
||||||
done, _ = await asyncio.wait(
|
done, _ = await asyncio.wait(
|
||||||
[_async_setup_platform(hass, cfg, async_add_entities, None) for cfg in config]
|
[
|
||||||
|
_async_setup_platform(hass, ENTITY_SCHEMA(cfg), async_add_entities, None)
|
||||||
|
for cfg in config
|
||||||
|
]
|
||||||
)
|
)
|
||||||
if any([task.exception() for task in done]):
|
if any([task.exception() for task in done]):
|
||||||
exceptions = [task.exception() for task in done]
|
exceptions = [task.exception() for task in done]
|
||||||
|
@ -146,18 +165,25 @@ async def _async_setup_platform(
|
||||||
# Import CEC IGNORE attributes
|
# Import CEC IGNORE attributes
|
||||||
pychromecast.IGNORE_CEC += config.get(CONF_IGNORE_CEC, [])
|
pychromecast.IGNORE_CEC += config.get(CONF_IGNORE_CEC, [])
|
||||||
hass.data.setdefault(ADDED_CAST_DEVICES_KEY, set())
|
hass.data.setdefault(ADDED_CAST_DEVICES_KEY, set())
|
||||||
hass.data.setdefault(KNOWN_CHROMECAST_INFO_KEY, set())
|
hass.data.setdefault(KNOWN_CHROMECAST_INFO_KEY, dict())
|
||||||
|
|
||||||
info = None
|
info = None
|
||||||
if discovery_info is not None:
|
if discovery_info is not None:
|
||||||
info = ChromecastInfo(host=discovery_info["host"], port=discovery_info["port"])
|
info = ChromecastInfo(
|
||||||
|
host=discovery_info["host"], port=discovery_info["port"], services=None
|
||||||
|
)
|
||||||
|
elif CONF_UUID in config:
|
||||||
|
info = ChromecastInfo(uuid=config[CONF_UUID], services=None)
|
||||||
elif CONF_HOST in config:
|
elif CONF_HOST in config:
|
||||||
info = ChromecastInfo(host=config[CONF_HOST], port=DEFAULT_PORT)
|
info = ChromecastInfo(host=config[CONF_HOST], port=DEFAULT_PORT, services=None)
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
def async_cast_discovered(discover: ChromecastInfo) -> None:
|
def async_cast_discovered(discover: ChromecastInfo) -> None:
|
||||||
"""Handle discovery of a new chromecast."""
|
"""Handle discovery of a new chromecast."""
|
||||||
if info is not None and info.host_port != discover.host_port:
|
if info is not None and (
|
||||||
|
(info.uuid is not None and info.uuid != discover.uuid)
|
||||||
|
or (info.host is not None and info.host_port != discover.host_port)
|
||||||
|
):
|
||||||
# Waiting for a specific cast device, this is not it.
|
# Waiting for a specific cast device, this is not it.
|
||||||
return
|
return
|
||||||
|
|
||||||
|
@ -168,7 +194,7 @@ async def _async_setup_platform(
|
||||||
async_dispatcher_connect(hass, SIGNAL_CAST_DISCOVERED, async_cast_discovered)
|
async_dispatcher_connect(hass, SIGNAL_CAST_DISCOVERED, async_cast_discovered)
|
||||||
# Re-play the callback for all past chromecasts, store the objects in
|
# Re-play the callback for all past chromecasts, store the objects in
|
||||||
# a list to avoid concurrent modification resulting in exception.
|
# a list to avoid concurrent modification resulting in exception.
|
||||||
for chromecast in list(hass.data[KNOWN_CHROMECAST_INFO_KEY]):
|
for chromecast in hass.data[KNOWN_CHROMECAST_INFO_KEY].values():
|
||||||
async_cast_discovered(chromecast)
|
async_cast_discovered(chromecast)
|
||||||
|
|
||||||
ChromeCastZeroconf.set_zeroconf(await zeroconf.async_get_instance(hass))
|
ChromeCastZeroconf.set_zeroconf(await zeroconf.async_get_instance(hass))
|
||||||
|
@ -187,10 +213,7 @@ class CastDevice(MediaPlayerEntity):
|
||||||
"""Initialize the cast device."""
|
"""Initialize the cast device."""
|
||||||
|
|
||||||
self._cast_info = cast_info
|
self._cast_info = cast_info
|
||||||
self.services = None
|
self.services = cast_info.services
|
||||||
if cast_info.service:
|
|
||||||
self.services = set()
|
|
||||||
self.services.add(cast_info.service)
|
|
||||||
self._chromecast: Optional[pychromecast.Chromecast] = None
|
self._chromecast: Optional[pychromecast.Chromecast] = None
|
||||||
self.cast_status = None
|
self.cast_status = None
|
||||||
self.media_status = None
|
self.media_status = None
|
||||||
|
@ -211,9 +234,6 @@ class CastDevice(MediaPlayerEntity):
|
||||||
self._add_remove_handler = async_dispatcher_connect(
|
self._add_remove_handler = async_dispatcher_connect(
|
||||||
self.hass, SIGNAL_CAST_DISCOVERED, self._async_cast_discovered
|
self.hass, SIGNAL_CAST_DISCOVERED, self._async_cast_discovered
|
||||||
)
|
)
|
||||||
self._del_remove_handler = async_dispatcher_connect(
|
|
||||||
self.hass, SIGNAL_CAST_REMOVED, self._async_cast_removed
|
|
||||||
)
|
|
||||||
self.hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, self._async_stop)
|
self.hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, self._async_stop)
|
||||||
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))
|
||||||
|
@ -245,42 +265,26 @@ class CastDevice(MediaPlayerEntity):
|
||||||
|
|
||||||
self._cast_info = cast_info
|
self._cast_info = cast_info
|
||||||
|
|
||||||
if self.services is not None:
|
|
||||||
if cast_info.service not in self.services:
|
|
||||||
_LOGGER.debug(
|
|
||||||
"[%s %s (%s:%s)] Got new service: %s (%s)",
|
|
||||||
self.entity_id,
|
|
||||||
self._cast_info.friendly_name,
|
|
||||||
self._cast_info.host,
|
|
||||||
self._cast_info.port,
|
|
||||||
cast_info.service,
|
|
||||||
self.services,
|
|
||||||
)
|
|
||||||
|
|
||||||
self.services.add(cast_info.service)
|
|
||||||
|
|
||||||
if self._chromecast is not None:
|
if self._chromecast is not None:
|
||||||
# Only setup the chromecast once, added elements to services
|
# Only setup the chromecast once, added elements to services
|
||||||
# will automatically be picked up.
|
# will automatically be picked up.
|
||||||
return
|
return
|
||||||
|
|
||||||
_LOGGER.debug(
|
_LOGGER.debug(
|
||||||
"[%s %s (%s:%s)] Connecting to cast device by service %s",
|
"[%s %s] Connecting to cast device by service %s",
|
||||||
self.entity_id,
|
self.entity_id,
|
||||||
self._cast_info.friendly_name,
|
self._cast_info.friendly_name,
|
||||||
self._cast_info.host,
|
|
||||||
self._cast_info.port,
|
|
||||||
self.services,
|
self.services,
|
||||||
)
|
)
|
||||||
chromecast = await self.hass.async_add_executor_job(
|
chromecast = await self.hass.async_add_executor_job(
|
||||||
pychromecast.get_chromecast_from_service,
|
pychromecast.get_chromecast_from_service,
|
||||||
(
|
(
|
||||||
self.services,
|
self.services,
|
||||||
ChromeCastZeroconf.get_zeroconf(),
|
|
||||||
cast_info.uuid,
|
cast_info.uuid,
|
||||||
cast_info.model_name,
|
cast_info.model_name,
|
||||||
cast_info.friendly_name,
|
cast_info.friendly_name,
|
||||||
),
|
),
|
||||||
|
ChromeCastZeroconf.get_zeroconf(),
|
||||||
)
|
)
|
||||||
self._chromecast = chromecast
|
self._chromecast = chromecast
|
||||||
|
|
||||||
|
@ -296,30 +300,15 @@ class CastDevice(MediaPlayerEntity):
|
||||||
self._chromecast.start()
|
self._chromecast.start()
|
||||||
self.async_write_ha_state()
|
self.async_write_ha_state()
|
||||||
|
|
||||||
async def async_del_cast_info(self, cast_info):
|
|
||||||
"""Remove the service."""
|
|
||||||
self.services.discard(cast_info.service)
|
|
||||||
_LOGGER.debug(
|
|
||||||
"[%s %s (%s:%s)] Remove service: %s (%s)",
|
|
||||||
self.entity_id,
|
|
||||||
self._cast_info.friendly_name,
|
|
||||||
self._cast_info.host,
|
|
||||||
self._cast_info.port,
|
|
||||||
cast_info.service,
|
|
||||||
self.services,
|
|
||||||
)
|
|
||||||
|
|
||||||
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:
|
||||||
# Can't disconnect if not connected.
|
# Can't disconnect if not connected.
|
||||||
return
|
return
|
||||||
_LOGGER.debug(
|
_LOGGER.debug(
|
||||||
"[%s %s (%s:%s)] Disconnecting from chromecast socket.",
|
"[%s %s] Disconnecting from chromecast socket.",
|
||||||
self.entity_id,
|
self.entity_id,
|
||||||
self._cast_info.friendly_name,
|
self._cast_info.friendly_name,
|
||||||
self._cast_info.host,
|
|
||||||
self._cast_info.port,
|
|
||||||
)
|
)
|
||||||
self._available = False
|
self._available = False
|
||||||
self.async_write_ha_state()
|
self.async_write_ha_state()
|
||||||
|
@ -359,11 +348,9 @@ class CastDevice(MediaPlayerEntity):
|
||||||
def new_connection_status(self, connection_status):
|
def new_connection_status(self, connection_status):
|
||||||
"""Handle updates of connection status."""
|
"""Handle updates of connection status."""
|
||||||
_LOGGER.debug(
|
_LOGGER.debug(
|
||||||
"[%s %s (%s:%s)] Received cast device connection status: %s",
|
"[%s %s] Received cast device connection status: %s",
|
||||||
self.entity_id,
|
self.entity_id,
|
||||||
self._cast_info.friendly_name,
|
self._cast_info.friendly_name,
|
||||||
self._cast_info.host,
|
|
||||||
self._cast_info.port,
|
|
||||||
connection_status.status,
|
connection_status.status,
|
||||||
)
|
)
|
||||||
if connection_status.status == CONNECTION_STATUS_DISCONNECTED:
|
if connection_status.status == CONNECTION_STATUS_DISCONNECTED:
|
||||||
|
@ -378,11 +365,9 @@ class CastDevice(MediaPlayerEntity):
|
||||||
# Only update state when availability changed to put less pressure
|
# Only update state when availability changed to put less pressure
|
||||||
# on state machine.
|
# on state machine.
|
||||||
_LOGGER.debug(
|
_LOGGER.debug(
|
||||||
"[%s %s (%s:%s)] Cast device availability changed: %s",
|
"[%s %s] Cast device availability changed: %s",
|
||||||
self.entity_id,
|
self.entity_id,
|
||||||
self._cast_info.friendly_name,
|
self._cast_info.friendly_name,
|
||||||
self._cast_info.host,
|
|
||||||
self._cast_info.port,
|
|
||||||
connection_status.status,
|
connection_status.status,
|
||||||
)
|
)
|
||||||
self._available = new_available
|
self._available = new_available
|
||||||
|
@ -391,11 +376,9 @@ class CastDevice(MediaPlayerEntity):
|
||||||
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(
|
||||||
"[%s %s (%s:%s)] Multizone %s media status: %s",
|
"[%s %s] Multizone %s media status: %s",
|
||||||
self.entity_id,
|
self.entity_id,
|
||||||
self._cast_info.friendly_name,
|
self._cast_info.friendly_name,
|
||||||
self._cast_info.host,
|
|
||||||
self._cast_info.port,
|
|
||||||
group_uuid,
|
group_uuid,
|
||||||
media_status,
|
media_status,
|
||||||
)
|
)
|
||||||
|
@ -738,32 +721,9 @@ class CastDevice(MediaPlayerEntity):
|
||||||
# Discovered is not our device.
|
# Discovered is not our device.
|
||||||
return
|
return
|
||||||
|
|
||||||
if self.services is None:
|
|
||||||
_LOGGER.warning(
|
|
||||||
"[%s %s (%s:%s)] Received update for manually added Cast",
|
|
||||||
self.entity_id,
|
|
||||||
self._cast_info.friendly_name,
|
|
||||||
self._cast_info.host,
|
|
||||||
self._cast_info.port,
|
|
||||||
)
|
|
||||||
return
|
|
||||||
|
|
||||||
_LOGGER.debug("Discovered chromecast with same UUID: %s", discover)
|
_LOGGER.debug("Discovered chromecast with same UUID: %s", discover)
|
||||||
await self.async_set_cast_info(discover)
|
await self.async_set_cast_info(discover)
|
||||||
|
|
||||||
async def _async_cast_removed(self, discover: ChromecastInfo):
|
|
||||||
"""Handle removal of Chromecast."""
|
|
||||||
if self._cast_info.uuid is None:
|
|
||||||
# We can't handle empty UUIDs
|
|
||||||
return
|
|
||||||
|
|
||||||
if self._cast_info.uuid != discover.uuid:
|
|
||||||
# Removed is not our device.
|
|
||||||
return
|
|
||||||
|
|
||||||
_LOGGER.debug("Removed chromecast with same UUID: %s", discover)
|
|
||||||
await self.async_del_cast_info(discover)
|
|
||||||
|
|
||||||
async def _async_stop(self, event):
|
async def _async_stop(self, event):
|
||||||
"""Disconnect socket on Home Assistant stop."""
|
"""Disconnect socket on Home Assistant stop."""
|
||||||
await self._async_disconnect()
|
await self._async_disconnect()
|
||||||
|
|
|
@ -1255,7 +1255,7 @@ pycfdns==0.0.1
|
||||||
pychannels==1.0.0
|
pychannels==1.0.0
|
||||||
|
|
||||||
# homeassistant.components.cast
|
# homeassistant.components.cast
|
||||||
pychromecast==6.0.0
|
pychromecast==7.0.1
|
||||||
|
|
||||||
# homeassistant.components.cmus
|
# homeassistant.components.cmus
|
||||||
pycmus==0.1.1
|
pycmus==0.1.1
|
||||||
|
|
|
@ -567,7 +567,7 @@ pyblackbird==0.5
|
||||||
pybotvac==0.0.17
|
pybotvac==0.0.17
|
||||||
|
|
||||||
# homeassistant.components.cast
|
# homeassistant.components.cast
|
||||||
pychromecast==6.0.0
|
pychromecast==7.0.1
|
||||||
|
|
||||||
# homeassistant.components.coolmaster
|
# homeassistant.components.coolmaster
|
||||||
pycoolmasternet==0.0.4
|
pycoolmasternet==0.0.4
|
||||||
|
|
|
@ -56,10 +56,24 @@ def get_fake_chromecast_info(
|
||||||
):
|
):
|
||||||
"""Generate a Fake ChromecastInfo with the specified arguments."""
|
"""Generate a Fake ChromecastInfo with the specified arguments."""
|
||||||
return ChromecastInfo(
|
return ChromecastInfo(
|
||||||
host=host, port=port, uuid=uuid, friendly_name="Speaker", service="the-service"
|
host=host,
|
||||||
|
port=port,
|
||||||
|
uuid=uuid,
|
||||||
|
friendly_name="Speaker",
|
||||||
|
services={"the-service"},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def get_fake_zconf(host="192.168.178.42", port=8009):
|
||||||
|
"""Generate a Fake Zeroconf object with the specified arguments."""
|
||||||
|
parsed_addresses = MagicMock()
|
||||||
|
parsed_addresses.return_value = [host]
|
||||||
|
service_info = MagicMock(parsed_addresses=parsed_addresses, port=port)
|
||||||
|
zconf = MagicMock()
|
||||||
|
zconf.get_service_info.return_value = service_info
|
||||||
|
return zconf
|
||||||
|
|
||||||
|
|
||||||
async def async_setup_cast(hass, config=None, discovery_info=None):
|
async def async_setup_cast(hass, config=None, discovery_info=None):
|
||||||
"""Set up the cast platform."""
|
"""Set up the cast platform."""
|
||||||
if config is None:
|
if config is None:
|
||||||
|
@ -96,14 +110,13 @@ async def async_setup_cast_internal_discovery(hass, config=None, discovery_info=
|
||||||
|
|
||||||
def discover_chromecast(service_name: str, info: ChromecastInfo) -> None:
|
def discover_chromecast(service_name: str, info: ChromecastInfo) -> None:
|
||||||
"""Discover a chromecast device."""
|
"""Discover a chromecast device."""
|
||||||
listener.services[service_name] = (
|
listener.services[info.uuid] = (
|
||||||
info.host,
|
{service_name},
|
||||||
info.port,
|
|
||||||
info.uuid,
|
info.uuid,
|
||||||
info.model_name,
|
info.model_name,
|
||||||
info.friendly_name,
|
info.friendly_name,
|
||||||
)
|
)
|
||||||
discovery_callback(service_name)
|
discovery_callback(info.uuid, service_name)
|
||||||
|
|
||||||
return discover_chromecast, add_entities
|
return discover_chromecast, add_entities
|
||||||
|
|
||||||
|
@ -113,6 +126,7 @@ async def async_setup_media_player_cast(hass: HomeAssistantType, info: Chromecas
|
||||||
listener = MagicMock(services={})
|
listener = MagicMock(services={})
|
||||||
browser = MagicMock(zc={})
|
browser = MagicMock(zc={})
|
||||||
chromecast = get_fake_chromecast(info)
|
chromecast = get_fake_chromecast(info)
|
||||||
|
zconf = get_fake_zconf(host=info.host, port=info.port)
|
||||||
|
|
||||||
cast.CastStatusListener = MagicMock()
|
cast.CastStatusListener = MagicMock()
|
||||||
|
|
||||||
|
@ -125,6 +139,9 @@ async def async_setup_media_player_cast(hass: HomeAssistantType, info: Chromecas
|
||||||
) as cast_listener, patch(
|
) as cast_listener, patch(
|
||||||
"homeassistant.components.cast.discovery.pychromecast.start_discovery",
|
"homeassistant.components.cast.discovery.pychromecast.start_discovery",
|
||||||
return_value=browser,
|
return_value=browser,
|
||||||
|
), patch(
|
||||||
|
"homeassistant.components.cast.discovery.ChromeCastZeroconf.get_zeroconf",
|
||||||
|
return_value=zconf,
|
||||||
):
|
):
|
||||||
await async_setup_component(
|
await async_setup_component(
|
||||||
hass,
|
hass,
|
||||||
|
@ -138,14 +155,13 @@ async def async_setup_media_player_cast(hass: HomeAssistantType, info: Chromecas
|
||||||
|
|
||||||
def discover_chromecast(service_name: str, info: ChromecastInfo) -> None:
|
def discover_chromecast(service_name: str, info: ChromecastInfo) -> None:
|
||||||
"""Discover a chromecast device."""
|
"""Discover a chromecast device."""
|
||||||
listener.services[service_name] = (
|
listener.services[info.uuid] = (
|
||||||
info.host,
|
{service_name},
|
||||||
info.port,
|
|
||||||
info.uuid,
|
info.uuid,
|
||||||
info.model_name,
|
info.model_name,
|
||||||
info.friendly_name,
|
info.friendly_name,
|
||||||
)
|
)
|
||||||
discovery_callback(service_name)
|
discovery_callback(info.uuid, service_name)
|
||||||
|
|
||||||
discover_chromecast("the-service", info)
|
discover_chromecast("the-service", info)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
|
@ -184,7 +200,7 @@ async def test_stop_discovery_called_on_stop(hass):
|
||||||
assert start_discovery.call_count == 1
|
assert start_discovery.call_count == 1
|
||||||
|
|
||||||
with patch(
|
with patch(
|
||||||
"homeassistant.components.cast.discovery.pychromecast.stop_discovery"
|
"homeassistant.components.cast.discovery.pychromecast.discovery.stop_discovery"
|
||||||
) as stop_discovery:
|
) as stop_discovery:
|
||||||
# stop discovery should be called on shutdown
|
# stop discovery should be called on shutdown
|
||||||
hass.bus.async_fire(EVENT_HOMEASSISTANT_STOP)
|
hass.bus.async_fire(EVENT_HOMEASSISTANT_STOP)
|
||||||
|
@ -229,15 +245,26 @@ async def test_replay_past_chromecasts(hass):
|
||||||
cast_group2 = get_fake_chromecast_info(
|
cast_group2 = get_fake_chromecast_info(
|
||||||
host="host2", port=42, uuid=UUID("9462202c-e747-4af5-a66b-7dce0e1ebc09")
|
host="host2", port=42, uuid=UUID("9462202c-e747-4af5-a66b-7dce0e1ebc09")
|
||||||
)
|
)
|
||||||
|
zconf_1 = get_fake_zconf(host="host1", port=42)
|
||||||
|
zconf_2 = get_fake_zconf(host="host2", port=42)
|
||||||
|
|
||||||
discover_cast, add_dev1 = await async_setup_cast_internal_discovery(
|
discover_cast, add_dev1 = await async_setup_cast_internal_discovery(
|
||||||
hass, discovery_info={"host": "host1", "port": 42}
|
hass, discovery_info={"host": "host1", "port": 42}
|
||||||
)
|
)
|
||||||
discover_cast("service2", cast_group2)
|
|
||||||
await hass.async_block_till_done()
|
|
||||||
assert add_dev1.call_count == 0
|
|
||||||
|
|
||||||
discover_cast("service1", cast_group1)
|
with patch(
|
||||||
|
"homeassistant.components.cast.discovery.ChromeCastZeroconf.get_zeroconf",
|
||||||
|
return_value=zconf_2,
|
||||||
|
):
|
||||||
|
discover_cast("service2", cast_group2)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
assert add_dev1.call_count == 0
|
||||||
|
|
||||||
|
with patch(
|
||||||
|
"homeassistant.components.cast.discovery.ChromeCastZeroconf.get_zeroconf",
|
||||||
|
return_value=zconf_1,
|
||||||
|
):
|
||||||
|
discover_cast("service1", cast_group1)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
await hass.async_block_till_done() # having tasks that add jobs
|
await hass.async_block_till_done() # having tasks that add jobs
|
||||||
assert add_dev1.call_count == 1
|
assert add_dev1.call_count == 1
|
||||||
|
@ -249,21 +276,61 @@ async def test_replay_past_chromecasts(hass):
|
||||||
assert add_dev2.call_count == 1
|
assert add_dev2.call_count == 1
|
||||||
|
|
||||||
|
|
||||||
async def test_manual_cast_chromecasts(hass):
|
async def test_manual_cast_chromecasts_host(hass):
|
||||||
"""Test only wanted casts are added for manual configuration."""
|
"""Test only wanted casts are added for manual configuration."""
|
||||||
cast_1 = get_fake_chromecast_info(host="configured_host")
|
cast_1 = get_fake_chromecast_info(host="configured_host")
|
||||||
cast_2 = get_fake_chromecast_info(host="other_host", uuid=FakeUUID2)
|
cast_2 = get_fake_chromecast_info(host="other_host", uuid=FakeUUID2)
|
||||||
|
zconf_1 = get_fake_zconf(host="configured_host")
|
||||||
|
zconf_2 = get_fake_zconf(host="other_host")
|
||||||
|
|
||||||
# Manual configuration of media player with host "configured_host"
|
# Manual configuration of media player with host "configured_host"
|
||||||
discover_cast, add_dev1 = await async_setup_cast_internal_discovery(
|
discover_cast, add_dev1 = await async_setup_cast_internal_discovery(
|
||||||
hass, config={"host": "configured_host"}
|
hass, config={"host": "configured_host"}
|
||||||
)
|
)
|
||||||
discover_cast("service2", cast_2)
|
with patch(
|
||||||
|
"homeassistant.components.cast.discovery.ChromeCastZeroconf.get_zeroconf",
|
||||||
|
return_value=zconf_2,
|
||||||
|
):
|
||||||
|
discover_cast("service2", cast_2)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
await hass.async_block_till_done() # having tasks that add jobs
|
await hass.async_block_till_done() # having tasks that add jobs
|
||||||
assert add_dev1.call_count == 0
|
assert add_dev1.call_count == 0
|
||||||
|
|
||||||
discover_cast("service1", cast_1)
|
with patch(
|
||||||
|
"homeassistant.components.cast.discovery.ChromeCastZeroconf.get_zeroconf",
|
||||||
|
return_value=zconf_1,
|
||||||
|
):
|
||||||
|
discover_cast("service1", cast_1)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
await hass.async_block_till_done() # having tasks that add jobs
|
||||||
|
assert add_dev1.call_count == 1
|
||||||
|
|
||||||
|
|
||||||
|
async def test_manual_cast_chromecasts_uuid(hass):
|
||||||
|
"""Test only wanted casts are added for manual configuration."""
|
||||||
|
cast_1 = get_fake_chromecast_info(host="host_1", uuid=FakeUUID)
|
||||||
|
cast_2 = get_fake_chromecast_info(host="host_2", uuid=FakeUUID2)
|
||||||
|
zconf_1 = get_fake_zconf(host="host_1")
|
||||||
|
zconf_2 = get_fake_zconf(host="host_2")
|
||||||
|
|
||||||
|
# Manual configuration of media player with host "configured_host"
|
||||||
|
discover_cast, add_dev1 = await async_setup_cast_internal_discovery(
|
||||||
|
hass, config={"uuid": FakeUUID}
|
||||||
|
)
|
||||||
|
with patch(
|
||||||
|
"homeassistant.components.cast.discovery.ChromeCastZeroconf.get_zeroconf",
|
||||||
|
return_value=zconf_2,
|
||||||
|
):
|
||||||
|
discover_cast("service2", cast_2)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
await hass.async_block_till_done() # having tasks that add jobs
|
||||||
|
assert add_dev1.call_count == 0
|
||||||
|
|
||||||
|
with patch(
|
||||||
|
"homeassistant.components.cast.discovery.ChromeCastZeroconf.get_zeroconf",
|
||||||
|
return_value=zconf_1,
|
||||||
|
):
|
||||||
|
discover_cast("service1", cast_1)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
await hass.async_block_till_done() # having tasks that add jobs
|
await hass.async_block_till_done() # having tasks that add jobs
|
||||||
assert add_dev1.call_count == 1
|
assert add_dev1.call_count == 1
|
||||||
|
@ -273,15 +340,25 @@ async def test_auto_cast_chromecasts(hass):
|
||||||
"""Test all discovered casts are added for default configuration."""
|
"""Test all discovered casts are added for default configuration."""
|
||||||
cast_1 = get_fake_chromecast_info(host="some_host")
|
cast_1 = get_fake_chromecast_info(host="some_host")
|
||||||
cast_2 = get_fake_chromecast_info(host="other_host", uuid=FakeUUID2)
|
cast_2 = get_fake_chromecast_info(host="other_host", uuid=FakeUUID2)
|
||||||
|
zconf_1 = get_fake_zconf(host="some_host")
|
||||||
|
zconf_2 = get_fake_zconf(host="other_host")
|
||||||
|
|
||||||
# Manual configuration of media player with host "configured_host"
|
# Manual configuration of media player with host "configured_host"
|
||||||
discover_cast, add_dev1 = await async_setup_cast_internal_discovery(hass)
|
discover_cast, add_dev1 = await async_setup_cast_internal_discovery(hass)
|
||||||
discover_cast("service2", cast_2)
|
with patch(
|
||||||
|
"homeassistant.components.cast.discovery.ChromeCastZeroconf.get_zeroconf",
|
||||||
|
return_value=zconf_1,
|
||||||
|
):
|
||||||
|
discover_cast("service2", cast_2)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
await hass.async_block_till_done() # having tasks that add jobs
|
await hass.async_block_till_done() # having tasks that add jobs
|
||||||
assert add_dev1.call_count == 1
|
assert add_dev1.call_count == 1
|
||||||
|
|
||||||
discover_cast("service1", cast_1)
|
with patch(
|
||||||
|
"homeassistant.components.cast.discovery.ChromeCastZeroconf.get_zeroconf",
|
||||||
|
return_value=zconf_2,
|
||||||
|
):
|
||||||
|
discover_cast("service1", cast_1)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
await hass.async_block_till_done() # having tasks that add jobs
|
await hass.async_block_till_done() # having tasks that add jobs
|
||||||
assert add_dev1.call_count == 2
|
assert add_dev1.call_count == 2
|
||||||
|
@ -291,15 +368,26 @@ async def test_update_cast_chromecasts(hass):
|
||||||
"""Test discovery of same UUID twice only adds one cast."""
|
"""Test discovery of same UUID twice only adds one cast."""
|
||||||
cast_1 = get_fake_chromecast_info(host="old_host")
|
cast_1 = get_fake_chromecast_info(host="old_host")
|
||||||
cast_2 = get_fake_chromecast_info(host="new_host")
|
cast_2 = get_fake_chromecast_info(host="new_host")
|
||||||
|
zconf_1 = get_fake_zconf(host="old_host")
|
||||||
|
zconf_2 = get_fake_zconf(host="new_host")
|
||||||
|
|
||||||
# Manual configuration of media player with host "configured_host"
|
# Manual configuration of media player with host "configured_host"
|
||||||
discover_cast, add_dev1 = await async_setup_cast_internal_discovery(hass)
|
discover_cast, add_dev1 = await async_setup_cast_internal_discovery(hass)
|
||||||
discover_cast("service1", cast_1)
|
|
||||||
|
with patch(
|
||||||
|
"homeassistant.components.cast.discovery.ChromeCastZeroconf.get_zeroconf",
|
||||||
|
return_value=zconf_1,
|
||||||
|
):
|
||||||
|
discover_cast("service1", cast_1)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
await hass.async_block_till_done() # having tasks that add jobs
|
await hass.async_block_till_done() # having tasks that add jobs
|
||||||
assert add_dev1.call_count == 1
|
assert add_dev1.call_count == 1
|
||||||
|
|
||||||
discover_cast("service2", cast_2)
|
with patch(
|
||||||
|
"homeassistant.components.cast.discovery.ChromeCastZeroconf.get_zeroconf",
|
||||||
|
return_value=zconf_2,
|
||||||
|
):
|
||||||
|
discover_cast("service2", cast_2)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
await hass.async_block_till_done() # having tasks that add jobs
|
await hass.async_block_till_done() # having tasks that add jobs
|
||||||
assert add_dev1.call_count == 1
|
assert add_dev1.call_count == 1
|
||||||
|
|
Loading…
Reference in New Issue