""" Implements the DIAL-protocol to communicate with the Chromecast """ import xml.etree.ElementTree as ET from collections import namedtuple from uuid import UUID import requests import six XML_NS_UPNP_DEVICE = "{urn:schemas-upnp-org:device-1-0}" FORMAT_BASE_URL = "http://{}:8008" CC_SESSION = requests.Session() CC_SESSION.headers['content-type'] = 'application/json' # Regular chromecast, supports video/audio CAST_TYPE_CHROMECAST = 'cast' # Cast Audio device, supports only audio CAST_TYPE_AUDIO = 'audio' # Cast Audio group device, supports only audio CAST_TYPE_GROUP = 'group' CAST_TYPES = { u'chromecast': CAST_TYPE_CHROMECAST, u'chromecast audio': CAST_TYPE_AUDIO, u'google cast group': CAST_TYPE_GROUP, } def reboot(host): """ Reboots the chromecast. """ CC_SESSION.post(FORMAT_BASE_URL.format(host) + "/setup/reboot", data='{"params":"now"}', timeout=10) def get_device_status(host): """ :param host: Hostname or ip to fetch status from :type host: str :return: The device status as a named tuple. :rtype: pychromecast.dial.DeviceStatus or None """ try: req = CC_SESSION.get( FORMAT_BASE_URL.format(host) + "/ssdp/device-desc.xml", timeout=10) # The Requests library will fall back to guessing the encoding in case # no encoding is specified in the response headers - which is the case # for the Chromecast. # The standard mandates utf-8 encoding, let's fall back to that instead # if no encoding is provided, since the autodetection does not always # provide correct results. if req.encoding is None: req.encoding = 'utf-8' status_el = ET.fromstring(req.text.encode("UTF-8")) device_info_el = status_el.find(XML_NS_UPNP_DEVICE + "device") api_version_el = status_el.find(XML_NS_UPNP_DEVICE + "specVersion") friendly_name = _read_xml_element(device_info_el, XML_NS_UPNP_DEVICE, "friendlyName", "Unknown Chromecast") model_name = _read_xml_element(device_info_el, XML_NS_UPNP_DEVICE, "modelName", "Unknown model name") manufacturer = _read_xml_element(device_info_el, XML_NS_UPNP_DEVICE, "manufacturer", "Unknown manufacturer") udn = _read_xml_element(device_info_el, XML_NS_UPNP_DEVICE, "UDN", None) api_version = (int(_read_xml_element(api_version_el, XML_NS_UPNP_DEVICE, "major", -1)), int(_read_xml_element(api_version_el, XML_NS_UPNP_DEVICE, "minor", -1))) cast_type = CAST_TYPES.get(model_name.lower(), CAST_TYPE_CHROMECAST) uuid = None if udn and udn.startswith('uuid:'): uuid = UUID(udn[len('uuid:'):].replace("-", "")) return DeviceStatus(friendly_name, model_name, manufacturer, api_version, uuid, cast_type) except (requests.exceptions.RequestException, ET.ParseError): return None def _read_xml_element(element, xml_ns, tag_name, default=""): """ Helper method to read text from an element. """ try: text = element.find(xml_ns + tag_name).text if isinstance(text, six.text_type): return text else: return text.decode('utf-8') except AttributeError: return default DeviceStatus = namedtuple("DeviceStatus", ["friendly_name", "model_name", "manufacturer", "api_version", "uuid", "cast_type"])