122 lines
3.9 KiB
Python
122 lines
3.9 KiB
Python
"""Deal with Cast discovery."""
|
|
import logging
|
|
import threading
|
|
|
|
import pychromecast
|
|
|
|
from homeassistant.const import EVENT_HOMEASSISTANT_STOP
|
|
from homeassistant.core import HomeAssistant
|
|
from homeassistant.helpers.dispatcher import dispatcher_send
|
|
|
|
from .const import (
|
|
INTERNAL_DISCOVERY_RUNNING_KEY,
|
|
KNOWN_CHROMECAST_INFO_KEY,
|
|
SIGNAL_CAST_DISCOVERED,
|
|
SIGNAL_CAST_REMOVED,
|
|
)
|
|
from .helpers import ChromecastInfo, ChromeCastZeroconf
|
|
|
|
_LOGGER = logging.getLogger(__name__)
|
|
|
|
|
|
def discover_chromecast(hass: HomeAssistant, info: ChromecastInfo):
|
|
"""Discover a Chromecast."""
|
|
if info.uuid is None:
|
|
_LOGGER.error("Discovered chromecast without uuid %s", info)
|
|
return
|
|
|
|
if info.uuid in hass.data[KNOWN_CHROMECAST_INFO_KEY]:
|
|
_LOGGER.debug("Discovered update for known chromecast %s", info)
|
|
else:
|
|
_LOGGER.debug("Discovered chromecast %s", info)
|
|
|
|
hass.data[KNOWN_CHROMECAST_INFO_KEY][info.uuid] = info
|
|
dispatcher_send(hass, SIGNAL_CAST_DISCOVERED, info)
|
|
|
|
|
|
def _remove_chromecast(hass: HomeAssistant, info: ChromecastInfo):
|
|
# Removed chromecast
|
|
_LOGGER.debug("Removed chromecast %s", info)
|
|
|
|
dispatcher_send(hass, SIGNAL_CAST_REMOVED, info)
|
|
|
|
|
|
def setup_internal_discovery(hass: HomeAssistant) -> None:
|
|
"""Set up the pychromecast internal discovery."""
|
|
if INTERNAL_DISCOVERY_RUNNING_KEY not in hass.data:
|
|
hass.data[INTERNAL_DISCOVERY_RUNNING_KEY] = threading.Lock()
|
|
|
|
if not hass.data[INTERNAL_DISCOVERY_RUNNING_KEY].acquire(blocking=False):
|
|
# Internal discovery is already running
|
|
return
|
|
|
|
def internal_add_update_callback(uuid, service_name):
|
|
"""Handle zeroconf discovery of a new or updated chromecast."""
|
|
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(
|
|
hass,
|
|
ChromecastInfo(
|
|
services=service[0],
|
|
uuid=service[1],
|
|
model_name=service[2],
|
|
friendly_name=service[3],
|
|
host=host,
|
|
port=service_info.port,
|
|
),
|
|
)
|
|
|
|
def internal_remove_callback(uuid, service_name, service):
|
|
"""Handle zeroconf discovery of a removed chromecast."""
|
|
_remove_chromecast(
|
|
hass,
|
|
ChromecastInfo(
|
|
services=service[0],
|
|
uuid=service[1],
|
|
model_name=service[2],
|
|
friendly_name=service[3],
|
|
),
|
|
)
|
|
|
|
_LOGGER.debug("Starting internal pychromecast discovery")
|
|
listener = pychromecast.CastListener(
|
|
internal_add_update_callback,
|
|
internal_remove_callback,
|
|
internal_add_update_callback,
|
|
)
|
|
browser = pychromecast.start_discovery(listener, ChromeCastZeroconf.get_zeroconf())
|
|
|
|
def stop_discovery(event):
|
|
"""Stop discovery of new chromecasts."""
|
|
_LOGGER.debug("Stopping internal pychromecast discovery")
|
|
pychromecast.discovery.stop_discovery(browser)
|
|
hass.data[INTERNAL_DISCOVERY_RUNNING_KEY].release()
|
|
|
|
hass.bus.listen_once(EVENT_HOMEASSISTANT_STOP, stop_discovery)
|