diff --git a/sonyapilib/device.py b/sonyapilib/device.py index bcd822e..c1d0321 100644 --- a/sonyapilib/device.py +++ b/sonyapilib/device.py @@ -156,6 +156,16 @@ def __init__(self, host, nickname, psk=None, self.dmr_port = dmr_port self.ircc_port = ircc_port + # information about target system + self.friendly_name = None + self.manufacturer = None + self.manufacturer_url = None + self.model_description = None + self.model_name = None + self.model_url = None + self.model_number = None + self.icons = None + # actions are thing like getting status self.actions = {} self.headers = {} @@ -168,16 +178,17 @@ def __init__(self, host, nickname, psk=None, self.mac = None self.api_version = 0 - self.dmr_url = f"http://{self.host}:{self.dmr_port}/dmr.xml" + self.dmr_base = f"http://{self.host}:{self.dmr_port}" + self.dmr_url = f"{self.dmr_base}/dmr.xml" self.app_url = f"http://{self.host}:{self.app_port}" self.base_url = f"http://{self.host}/sony/" - ircc_base = f"http://{self.host}:{self.ircc_port}" + self.ircc_base = f"http://{self.host}:{self.ircc_port}" if self.ircc_port == self.dmr_port: self.ircc_url = self.dmr_url else: - self.ircc_url = urljoin(ircc_base, "/Ircc.xml") + self.ircc_url = urljoin(self.ircc_base, "/Ircc.xml") - self.irccscpd_url = urljoin(ircc_base, "/IRCCSCPD.xml") + self.irccscpd_url = urljoin(self.ircc_base, "/IRCCSCPD.xml") self._ircc_categories = set() self._add_headers() @@ -272,6 +283,12 @@ def _parse_ircc(self): self.ircc_url, method=HttpMethod.GET, raise_errors=True) upnp_device = f"{URN_UPNP_DEVICE}device" + + self._set_value('ircc_base', f"http://{self.host}:{self.ircc_port}") + + self._parse_system_info(response.text, self.ircc_base, + upnp_device=upnp_device) + # the action list contains everything the device supports self.actionlist_url = find_in_xml( response.text, @@ -320,6 +337,62 @@ def _parse_ircc(self): self._ircc_categories.add(category_info.text) + def _parse_system_info(self, text, base_url, upnp_device=None): + upnp_device = upnp_device or f"{URN_UPNP_DEVICE}device" + + self._set_value('friendly_name', self._find_device_info( + text, "friendlyName", + upnp_device=upnp_device + )) + self._set_value('manufacturer', self._find_device_info( + text, "manufacturer", + upnp_device=upnp_device + )) + self._set_value('manufacturer_url', self._find_device_info( + text, "manufacturerURL", + upnp_device=upnp_device + )) + self._set_value('model_description', self._find_device_info( + text, "modelDescription", + upnp_device=upnp_device + )) + self._set_value('model_name', self._find_device_info( + text, "modelName", + upnp_device=upnp_device + )) + self._set_value('model_url', self._find_device_info( + text, "modelURL", + upnp_device=upnp_device + )) + self._set_value('model_number', self._find_device_info( + text, "modelNumber", + upnp_device=upnp_device + )) + + if hasattr(self, 'icons') and self.icons: + return + + icons = find_in_xml( + text, + [upnp_device, + f"{URN_UPNP_DEVICE}iconList", + (f"{URN_UPNP_DEVICE}icon", True), + f"{URN_UPNP_DEVICE}url"]) + + self.icons = [f"{base_url}{icon.text}" for icon in icons] + + @staticmethod + def _find_device_info(text, info, upnp_device=None): + upnp_device = upnp_device or f"{URN_UPNP_DEVICE}device" + + element = find_in_xml( + text, + [upnp_device, + f"{URN_UPNP_DEVICE}{info}"] + ) + + return element.text if element is not None else None + def _parse_system_information_v4(self): url = urljoin(self.base_url, "system") json_data = self._create_api_json("getSystemSupportedFunction") @@ -350,6 +423,10 @@ def _parse_system_information(self): "functionItem").attrib["value"] def _parse_dmr(self, data): + self._set_value('dmr_base', f"http://{self.host}:{self.dmr_port}") + + self._parse_system_info(data, self.dmr_base) + lirc_url = urlparse(self.ircc_url) xml_data = xml.etree.ElementTree.fromstring(data) @@ -694,6 +771,12 @@ def _recreate_auth_cookie(self): cookies.set("auth", self.cookies.get("auth")) return cookies + def _set_value(self, attribute, value): + if not hasattr(self, attribute): + setattr(self, attribute, value) + elif value and not getattr(self, attribute): + setattr(self, attribute, value) + def register(self): """Register at the api. diff --git a/tests/data/v0.5.0.json b/tests/data/v0.5.0.json new file mode 100644 index 0000000..568c202 --- /dev/null +++ b/tests/data/v0.5.0.json @@ -0,0 +1,663 @@ +{ + "py/object": "sonyapilib.device.SonyDevice", + "host": "test", + "nickname": "test", + "client_id": "test", + "actionlist_url": "http://192.168.240.4:50002/actionList", + "control_url": "http://test:50001/upnp/control/IRCC", + "av_transport_url": "http://test:52323/upnp/control/AVTransport", + "app_url": "http://test:50202", + "psk": null, + "app_port": 50202, + "dmr_port": 52323, + "ircc_port": 50001, + "actions": { + "register": { + "py/object": "sonyapilib.device.XmlApiObject", + "name": "register", + "mode": 3, + "url": "http://192.168.240.4:50002/register?name=test®istrationType=initial&deviceId=test&wolSupport=true", + "type": null, + "value": null, + "mac": null, + "id": null + }, + "getText": { + "py/object": "sonyapilib.device.XmlApiObject", + "name": "getText", + "mode": 3, + "url": "http://192.168.240.4:50002/getText", + "type": null, + "value": null, + "mac": null, + "id": null + }, + "sendText": { + "py/object": "sonyapilib.device.XmlApiObject", + "name": "sendText", + "mode": 3, + "url": "http://192.168.240.4:50002/sendText", + "type": null, + "value": null, + "mac": null, + "id": null + }, + "getContentInformation": { + "py/object": "sonyapilib.device.XmlApiObject", + "name": "getContentInformation", + "mode": 3, + "url": "http://192.168.240.4:50002/getContentInformation", + "type": null, + "value": null, + "mac": null, + "id": null + }, + "getSystemInformation": { + "py/object": "sonyapilib.device.XmlApiObject", + "name": "getSystemInformation", + "mode": 3, + "url": "http://192.168.240.4:50002/getSystemInformation", + "type": null, + "value": null, + "mac": null, + "id": null + }, + "getRemoteCommandList": { + "py/object": "sonyapilib.device.XmlApiObject", + "name": "getRemoteCommandList", + "mode": 3, + "url": "http://192.168.240.4:50002/getRemoteCommandList", + "type": null, + "value": null, + "mac": null, + "id": null + }, + "getStatus": { + "py/object": "sonyapilib.device.XmlApiObject", + "name": "getStatus", + "mode": 3, + "url": "http://192.168.240.4:50002/getStatus", + "type": null, + "value": null, + "mac": null, + "id": null + }, + "getHistoryList": { + "py/object": "sonyapilib.device.XmlApiObject", + "name": "getHistoryList", + "mode": 3, + "url": "http://192.168.240.4:50002/getHistoryList", + "type": null, + "value": null, + "mac": null, + "id": null + }, + "getContentUrl": { + "py/object": "sonyapilib.device.XmlApiObject", + "name": "getContentUrl", + "mode": 3, + "url": "http://192.168.240.4:50002/getContentUrl", + "type": null, + "value": null, + "mac": null, + "id": null + }, + "sendContentUrl": { + "py/object": "sonyapilib.device.XmlApiObject", + "name": "sendContentUrl", + "mode": 3, + "url": "http://192.168.240.4:50002/sendContentUrl", + "type": null, + "value": null, + "mac": null, + "id": null + } + }, + "headers": { + "X-CERS-DEVICE-ID": "test", + "X-CERS-DEVICE-INFO": "test" + }, + "commands": { + "Confirm": { + "py/object": "sonyapilib.device.XmlApiObject", + "name": "Confirm", + "mode": null, + "url": null, + "type": "ircc", + "value": "AAAAAwAAHFoAAAA9Aw==", + "mac": null, + "id": null + }, + "Up": { + "py/object": "sonyapilib.device.XmlApiObject", + "name": "Up", + "mode": null, + "url": null, + "type": "ircc", + "value": "AAAAAwAAHFoAAAA5Aw==", + "mac": null, + "id": null + }, + "Down": { + "py/object": "sonyapilib.device.XmlApiObject", + "name": "Down", + "mode": null, + "url": null, + "type": "ircc", + "value": "AAAAAwAAHFoAAAA6Aw==", + "mac": null, + "id": null + }, + "Right": { + "py/object": "sonyapilib.device.XmlApiObject", + "name": "Right", + "mode": null, + "url": null, + "type": "ircc", + "value": "AAAAAwAAHFoAAAA8Aw==", + "mac": null, + "id": null + }, + "Left": { + "py/object": "sonyapilib.device.XmlApiObject", + "name": "Left", + "mode": null, + "url": null, + "type": "ircc", + "value": "AAAAAwAAHFoAAAA7Aw==", + "mac": null, + "id": null + }, + "Home": { + "py/object": "sonyapilib.device.XmlApiObject", + "name": "Home", + "mode": null, + "url": null, + "type": "ircc", + "value": "AAAAAwAAHFoAAABCAw==", + "mac": null, + "id": null + }, + "Options": { + "py/object": "sonyapilib.device.XmlApiObject", + "name": "Options", + "mode": null, + "url": null, + "type": "ircc", + "value": "AAAAAwAAHFoAAAA/Aw==", + "mac": null, + "id": null + }, + "Return": { + "py/object": "sonyapilib.device.XmlApiObject", + "name": "Return", + "mode": null, + "url": null, + "type": "ircc", + "value": "AAAAAwAAHFoAAABDAw==", + "mac": null, + "id": null + }, + "Num1": { + "py/object": "sonyapilib.device.XmlApiObject", + "name": "Num1", + "mode": null, + "url": null, + "type": "ircc", + "value": "AAAAAwAAHFoAAAAAAw==", + "mac": null, + "id": null + }, + "Num2": { + "py/object": "sonyapilib.device.XmlApiObject", + "name": "Num2", + "mode": null, + "url": null, + "type": "ircc", + "value": "AAAAAwAAHFoAAAABAw==", + "mac": null, + "id": null + }, + "Num3": { + "py/object": "sonyapilib.device.XmlApiObject", + "name": "Num3", + "mode": null, + "url": null, + "type": "ircc", + "value": "AAAAAwAAHFoAAAACAw==", + "mac": null, + "id": null + }, + "Num4": { + "py/object": "sonyapilib.device.XmlApiObject", + "name": "Num4", + "mode": null, + "url": null, + "type": "ircc", + "value": "AAAAAwAAHFoAAAADAw==", + "mac": null, + "id": null + }, + "Num5": { + "py/object": "sonyapilib.device.XmlApiObject", + "name": "Num5", + "mode": null, + "url": null, + "type": "ircc", + "value": "AAAAAwAAHFoAAAAEAw==", + "mac": null, + "id": null + }, + "Num6": { + "py/object": "sonyapilib.device.XmlApiObject", + "name": "Num6", + "mode": null, + "url": null, + "type": "ircc", + "value": "AAAAAwAAHFoAAAAFAw==", + "mac": null, + "id": null + }, + "Num7": { + "py/object": "sonyapilib.device.XmlApiObject", + "name": "Num7", + "mode": null, + "url": null, + "type": "ircc", + "value": "AAAAAwAAHFoAAAAGAw==", + "mac": null, + "id": null + }, + "Num8": { + "py/object": "sonyapilib.device.XmlApiObject", + "name": "Num8", + "mode": null, + "url": null, + "type": "ircc", + "value": "AAAAAwAAHFoAAAAHAw==", + "mac": null, + "id": null + }, + "Num9": { + "py/object": "sonyapilib.device.XmlApiObject", + "name": "Num9", + "mode": null, + "url": null, + "type": "ircc", + "value": "AAAAAwAAHFoAAAAIAw==", + "mac": null, + "id": null + }, + "Num0": { + "py/object": "sonyapilib.device.XmlApiObject", + "name": "Num0", + "mode": null, + "url": null, + "type": "ircc", + "value": "AAAAAwAAHFoAAAAJAw==", + "mac": null, + "id": null + }, + "Power": { + "py/object": "sonyapilib.device.XmlApiObject", + "name": "Power", + "mode": null, + "url": null, + "type": "ircc", + "value": "AAAAAwAAHFoAAAAVAw==", + "mac": null, + "id": null + }, + "Display": { + "py/object": "sonyapilib.device.XmlApiObject", + "name": "Display", + "mode": null, + "url": null, + "type": "ircc", + "value": "AAAAAwAAHFoAAABBAw==", + "mac": null, + "id": null + }, + "Audio": { + "py/object": "sonyapilib.device.XmlApiObject", + "name": "Audio", + "mode": null, + "url": null, + "type": "ircc", + "value": "AAAAAwAAHFoAAABkAw==", + "mac": null, + "id": null + }, + "SubTitle": { + "py/object": "sonyapilib.device.XmlApiObject", + "name": "SubTitle", + "mode": null, + "url": null, + "type": "ircc", + "value": "AAAAAwAAHFoAAABjAw==", + "mac": null, + "id": null + }, + "Favorites": { + "py/object": "sonyapilib.device.XmlApiObject", + "name": "Favorites", + "mode": null, + "url": null, + "type": "ircc", + "value": "AAAAAwAAHFoAAABeAw==", + "mac": null, + "id": null + }, + "Yellow": { + "py/object": "sonyapilib.device.XmlApiObject", + "name": "Yellow", + "mode": null, + "url": null, + "type": "ircc", + "value": "AAAAAwAAHFoAAABpAw==", + "mac": null, + "id": null + }, + "Blue": { + "py/object": "sonyapilib.device.XmlApiObject", + "name": "Blue", + "mode": null, + "url": null, + "type": "ircc", + "value": "AAAAAwAAHFoAAABmAw==", + "mac": null, + "id": null + }, + "Red": { + "py/object": "sonyapilib.device.XmlApiObject", + "name": "Red", + "mode": null, + "url": null, + "type": "ircc", + "value": "AAAAAwAAHFoAAABnAw==", + "mac": null, + "id": null + }, + "Green": { + "py/object": "sonyapilib.device.XmlApiObject", + "name": "Green", + "mode": null, + "url": null, + "type": "ircc", + "value": "AAAAAwAAHFoAAABoAw==", + "mac": null, + "id": null + }, + "Play": { + "py/object": "sonyapilib.device.XmlApiObject", + "name": "Play", + "mode": null, + "url": null, + "type": "ircc", + "value": "AAAAAwAAHFoAAAAaAw==", + "mac": null, + "id": null + }, + "Stop": { + "py/object": "sonyapilib.device.XmlApiObject", + "name": "Stop", + "mode": null, + "url": null, + "type": "ircc", + "value": "AAAAAwAAHFoAAAAYAw==", + "mac": null, + "id": null + }, + "Pause": { + "py/object": "sonyapilib.device.XmlApiObject", + "name": "Pause", + "mode": null, + "url": null, + "type": "ircc", + "value": "AAAAAwAAHFoAAAAZAw==", + "mac": null, + "id": null + }, + "Rewind": { + "py/object": "sonyapilib.device.XmlApiObject", + "name": "Rewind", + "mode": null, + "url": null, + "type": "ircc", + "value": "AAAAAwAAHFoAAAAbAw==", + "mac": null, + "id": null + }, + "Forward": { + "py/object": "sonyapilib.device.XmlApiObject", + "name": "Forward", + "mode": null, + "url": null, + "type": "ircc", + "value": "AAAAAwAAHFoAAAAcAw==", + "mac": null, + "id": null + }, + "Prev": { + "py/object": "sonyapilib.device.XmlApiObject", + "name": "Prev", + "mode": null, + "url": null, + "type": "ircc", + "value": "AAAAAwAAHFoAAABXAw==", + "mac": null, + "id": null + }, + "Next": { + "py/object": "sonyapilib.device.XmlApiObject", + "name": "Next", + "mode": null, + "url": null, + "type": "ircc", + "value": "AAAAAwAAHFoAAABWAw==", + "mac": null, + "id": null + }, + "Replay": { + "py/object": "sonyapilib.device.XmlApiObject", + "name": "Replay", + "mode": null, + "url": null, + "type": "ircc", + "value": "AAAAAwAAHFoAAAB2Aw==", + "mac": null, + "id": null + }, + "Advance": { + "py/object": "sonyapilib.device.XmlApiObject", + "name": "Advance", + "mode": null, + "url": null, + "type": "ircc", + "value": "AAAAAwAAHFoAAAB1Aw==", + "mac": null, + "id": null + }, + "Angle": { + "py/object": "sonyapilib.device.XmlApiObject", + "name": "Angle", + "mode": null, + "url": null, + "type": "ircc", + "value": "AAAAAwAAHFoAAABlAw==", + "mac": null, + "id": null + }, + "TopMenu": { + "py/object": "sonyapilib.device.XmlApiObject", + "name": "TopMenu", + "mode": null, + "url": null, + "type": "ircc", + "value": "AAAAAwAAHFoAAAAsAw==", + "mac": null, + "id": null + }, + "PopUpMenu": { + "py/object": "sonyapilib.device.XmlApiObject", + "name": "PopUpMenu", + "mode": null, + "url": null, + "type": "ircc", + "value": "AAAAAwAAHFoAAAApAw==", + "mac": null, + "id": null + }, + "Eject": { + "py/object": "sonyapilib.device.XmlApiObject", + "name": "Eject", + "mode": null, + "url": null, + "type": "ircc", + "value": "AAAAAwAAHFoAAAAWAw==", + "mac": null, + "id": null + }, + "Karaoke": { + "py/object": "sonyapilib.device.XmlApiObject", + "name": "Karaoke", + "mode": null, + "url": null, + "type": "ircc", + "value": "AAAAAwAAHFoAAABKAw==", + "mac": null, + "id": null + }, + "Netflix": { + "py/object": "sonyapilib.device.XmlApiObject", + "name": "Netflix", + "mode": null, + "url": null, + "type": "ircc", + "value": "AAAAAwAAHFoAAABLAw==", + "mac": null, + "id": null + }, + "Mode3D": { + "py/object": "sonyapilib.device.XmlApiObject", + "name": "Mode3D", + "mode": null, + "url": null, + "type": "ircc", + "value": "AAAAAwAAHFoAAABNAw==", + "mac": null, + "id": null + }, + "ZoomIn": { + "py/object": "sonyapilib.device.XmlApiObject", + "name": "ZoomIn", + "mode": null, + "url": null, + "type": "url", + "value": "http://10.0.0.102:50002/setBrowse?action=zoomIn", + "mac": null, + "id": null + }, + "ZoomOut": { + "py/object": "sonyapilib.device.XmlApiObject", + "name": "ZoomOut", + "mode": null, + "url": null, + "type": "url", + "value": "http://10.0.0.102:50002/setBrowse?action=zoomOut", + "mac": null, + "id": null + }, + "BrowserBack": { + "py/object": "sonyapilib.device.XmlApiObject", + "name": "BrowserBack", + "mode": null, + "url": null, + "type": "url", + "value": "http://10.0.0.102:50002/setBrowse?action=back", + "mac": null, + "id": null + }, + "BrowserForward": { + "py/object": "sonyapilib.device.XmlApiObject", + "name": "BrowserForward", + "mode": null, + "url": null, + "type": "url", + "value": "http://10.0.0.102:50002/setBrowse?action=forward", + "mac": null, + "id": null + }, + "BrowserBookmarkList": { + "py/object": "sonyapilib.device.XmlApiObject", + "name": "BrowserBookmarkList", + "mode": null, + "url": null, + "type": "url", + "value": "http://10.0.0.102:50002/getBrowse?action=bookmarkList", + "mac": null, + "id": null + } + }, + "apps": {}, + "pin": null, + "cookies": { + "py/object": "requests.cookies.RequestsCookieJar", + "py/state": { + "_cookies": { + "192.168.170.23": { + "/sony/": { + "auth": { + "py/object": "http.cookiejar.Cookie", + "_rest": {}, + "comment": null, + "comment_url": null, + "discard": false, + "domain": "192.168.170.23", + "domain_initial_dot": false, + "domain_specified": false, + "expires": 1556462645, + "name": "auth", + "path": "/sony/", + "path_specified": true, + "port": null, + "port_specified": false, + "rfc2109": false, + "secure": false, + "value": "b2d0ff57a51b475cb3d1a9b7516f366ed2b4ffe0164cd0fd560ff6ceb28b101a", + "version": 0 + } + } + } + }, + "_now": 1555253045, + "_policy": { + "py/object": "http.cookiejar.DefaultCookiePolicy", + "_allowed_domains": null, + "_blocked_domains": { + "py/tuple": [] + }, + "_now": 1555253045, + "hide_cookie2": false, + "netscape": true, + "rfc2109_as_netscape": null, + "rfc2965": false, + "strict_domain": false, + "strict_ns_domain": 0, + "strict_ns_set_initial_dollar": false, + "strict_ns_set_path": false, + "strict_ns_unverifiable": false, + "strict_rfc2965_unverifiable": true + } + } + }, + "mac": "30-52-cb-cc-16-ee", + "api_version": 3, + "dmr_url": "http://test:52323/dmr.xml", + "base_url": "http://test/sony/", + "ircc_url": "http://test:50001/Ircc.xml", + "irccscpd_url": "http://test:50001/IRCCSCPD.xml", + "_ircc_categories": { + "py/set": [] + } +} \ No newline at end of file diff --git a/tests/data/v0.6.0.json b/tests/data/v0.6.0.json new file mode 100644 index 0000000..a6cd281 --- /dev/null +++ b/tests/data/v0.6.0.json @@ -0,0 +1,679 @@ +{ + "py/object": "sonyapilib.device.SonyDevice", + "host": "test", + "nickname": "test", + "client_id": "test", + "actionlist_url": "http://192.168.240.4:50002/actionList", + "control_url": "http://test:50001/upnp/control/IRCC", + "av_transport_url": "http://test:52323/upnp/control/AVTransport", + "rendering_control_url": "http://test:52323/upnp/control/RenderingControl", + "app_url": "http://test:50202", + "psk": null, + "app_port": 50202, + "dmr_port": 52323, + "ircc_port": 50001, + "friendly_name": "Blu-ray Disc Player", + "manufacturer": "Sony Corporation", + "manufacturer_url": "http://www.sony.net/", + "model_description": null, + "model_name": "BDP-S5500", + "model_url": null, + "model_number": "BDP-2015", + "icons": [ + "http://test:52323/bdp_ax_device_icon_large.jpg", + "http://test:52323/bdp_ax_device_icon_large.png", + "http://test:52323/bdp_ax_device_icon_small.jpg", + "http://test:52323/bdp_ax_device_icon_small.png" + ], + "actions": { + "register": { + "py/object": "sonyapilib.device.XmlApiObject", + "name": "register", + "mode": 3, + "url": "http://192.168.240.4:50002/register?name=test®istrationType=initial&deviceId=test&wolSupport=true", + "type": null, + "value": null, + "mac": null, + "id": null + }, + "getText": { + "py/object": "sonyapilib.device.XmlApiObject", + "name": "getText", + "mode": 3, + "url": "http://192.168.240.4:50002/getText", + "type": null, + "value": null, + "mac": null, + "id": null + }, + "sendText": { + "py/object": "sonyapilib.device.XmlApiObject", + "name": "sendText", + "mode": 3, + "url": "http://192.168.240.4:50002/sendText", + "type": null, + "value": null, + "mac": null, + "id": null + }, + "getContentInformation": { + "py/object": "sonyapilib.device.XmlApiObject", + "name": "getContentInformation", + "mode": 3, + "url": "http://192.168.240.4:50002/getContentInformation", + "type": null, + "value": null, + "mac": null, + "id": null + }, + "getSystemInformation": { + "py/object": "sonyapilib.device.XmlApiObject", + "name": "getSystemInformation", + "mode": 3, + "url": "http://192.168.240.4:50002/getSystemInformation", + "type": null, + "value": null, + "mac": null, + "id": null + }, + "getRemoteCommandList": { + "py/object": "sonyapilib.device.XmlApiObject", + "name": "getRemoteCommandList", + "mode": 3, + "url": "http://192.168.240.4:50002/getRemoteCommandList", + "type": null, + "value": null, + "mac": null, + "id": null + }, + "getStatus": { + "py/object": "sonyapilib.device.XmlApiObject", + "name": "getStatus", + "mode": 3, + "url": "http://192.168.240.4:50002/getStatus", + "type": null, + "value": null, + "mac": null, + "id": null + }, + "getHistoryList": { + "py/object": "sonyapilib.device.XmlApiObject", + "name": "getHistoryList", + "mode": 3, + "url": "http://192.168.240.4:50002/getHistoryList", + "type": null, + "value": null, + "mac": null, + "id": null + }, + "getContentUrl": { + "py/object": "sonyapilib.device.XmlApiObject", + "name": "getContentUrl", + "mode": 3, + "url": "http://192.168.240.4:50002/getContentUrl", + "type": null, + "value": null, + "mac": null, + "id": null + }, + "sendContentUrl": { + "py/object": "sonyapilib.device.XmlApiObject", + "name": "sendContentUrl", + "mode": 3, + "url": "http://192.168.240.4:50002/sendContentUrl", + "type": null, + "value": null, + "mac": null, + "id": null + } + }, + "headers": { + "X-CERS-DEVICE-ID": "test", + "X-CERS-DEVICE-INFO": "test" + }, + "commands": { + "Confirm": { + "py/object": "sonyapilib.device.XmlApiObject", + "name": "Confirm", + "mode": null, + "url": null, + "type": "ircc", + "value": "AAAAAwAAHFoAAAA9Aw==", + "mac": null, + "id": null + }, + "Up": { + "py/object": "sonyapilib.device.XmlApiObject", + "name": "Up", + "mode": null, + "url": null, + "type": "ircc", + "value": "AAAAAwAAHFoAAAA5Aw==", + "mac": null, + "id": null + }, + "Down": { + "py/object": "sonyapilib.device.XmlApiObject", + "name": "Down", + "mode": null, + "url": null, + "type": "ircc", + "value": "AAAAAwAAHFoAAAA6Aw==", + "mac": null, + "id": null + }, + "Right": { + "py/object": "sonyapilib.device.XmlApiObject", + "name": "Right", + "mode": null, + "url": null, + "type": "ircc", + "value": "AAAAAwAAHFoAAAA8Aw==", + "mac": null, + "id": null + }, + "Left": { + "py/object": "sonyapilib.device.XmlApiObject", + "name": "Left", + "mode": null, + "url": null, + "type": "ircc", + "value": "AAAAAwAAHFoAAAA7Aw==", + "mac": null, + "id": null + }, + "Home": { + "py/object": "sonyapilib.device.XmlApiObject", + "name": "Home", + "mode": null, + "url": null, + "type": "ircc", + "value": "AAAAAwAAHFoAAABCAw==", + "mac": null, + "id": null + }, + "Options": { + "py/object": "sonyapilib.device.XmlApiObject", + "name": "Options", + "mode": null, + "url": null, + "type": "ircc", + "value": "AAAAAwAAHFoAAAA/Aw==", + "mac": null, + "id": null + }, + "Return": { + "py/object": "sonyapilib.device.XmlApiObject", + "name": "Return", + "mode": null, + "url": null, + "type": "ircc", + "value": "AAAAAwAAHFoAAABDAw==", + "mac": null, + "id": null + }, + "Num1": { + "py/object": "sonyapilib.device.XmlApiObject", + "name": "Num1", + "mode": null, + "url": null, + "type": "ircc", + "value": "AAAAAwAAHFoAAAAAAw==", + "mac": null, + "id": null + }, + "Num2": { + "py/object": "sonyapilib.device.XmlApiObject", + "name": "Num2", + "mode": null, + "url": null, + "type": "ircc", + "value": "AAAAAwAAHFoAAAABAw==", + "mac": null, + "id": null + }, + "Num3": { + "py/object": "sonyapilib.device.XmlApiObject", + "name": "Num3", + "mode": null, + "url": null, + "type": "ircc", + "value": "AAAAAwAAHFoAAAACAw==", + "mac": null, + "id": null + }, + "Num4": { + "py/object": "sonyapilib.device.XmlApiObject", + "name": "Num4", + "mode": null, + "url": null, + "type": "ircc", + "value": "AAAAAwAAHFoAAAADAw==", + "mac": null, + "id": null + }, + "Num5": { + "py/object": "sonyapilib.device.XmlApiObject", + "name": "Num5", + "mode": null, + "url": null, + "type": "ircc", + "value": "AAAAAwAAHFoAAAAEAw==", + "mac": null, + "id": null + }, + "Num6": { + "py/object": "sonyapilib.device.XmlApiObject", + "name": "Num6", + "mode": null, + "url": null, + "type": "ircc", + "value": "AAAAAwAAHFoAAAAFAw==", + "mac": null, + "id": null + }, + "Num7": { + "py/object": "sonyapilib.device.XmlApiObject", + "name": "Num7", + "mode": null, + "url": null, + "type": "ircc", + "value": "AAAAAwAAHFoAAAAGAw==", + "mac": null, + "id": null + }, + "Num8": { + "py/object": "sonyapilib.device.XmlApiObject", + "name": "Num8", + "mode": null, + "url": null, + "type": "ircc", + "value": "AAAAAwAAHFoAAAAHAw==", + "mac": null, + "id": null + }, + "Num9": { + "py/object": "sonyapilib.device.XmlApiObject", + "name": "Num9", + "mode": null, + "url": null, + "type": "ircc", + "value": "AAAAAwAAHFoAAAAIAw==", + "mac": null, + "id": null + }, + "Num0": { + "py/object": "sonyapilib.device.XmlApiObject", + "name": "Num0", + "mode": null, + "url": null, + "type": "ircc", + "value": "AAAAAwAAHFoAAAAJAw==", + "mac": null, + "id": null + }, + "Power": { + "py/object": "sonyapilib.device.XmlApiObject", + "name": "Power", + "mode": null, + "url": null, + "type": "ircc", + "value": "AAAAAwAAHFoAAAAVAw==", + "mac": null, + "id": null + }, + "Display": { + "py/object": "sonyapilib.device.XmlApiObject", + "name": "Display", + "mode": null, + "url": null, + "type": "ircc", + "value": "AAAAAwAAHFoAAABBAw==", + "mac": null, + "id": null + }, + "Audio": { + "py/object": "sonyapilib.device.XmlApiObject", + "name": "Audio", + "mode": null, + "url": null, + "type": "ircc", + "value": "AAAAAwAAHFoAAABkAw==", + "mac": null, + "id": null + }, + "SubTitle": { + "py/object": "sonyapilib.device.XmlApiObject", + "name": "SubTitle", + "mode": null, + "url": null, + "type": "ircc", + "value": "AAAAAwAAHFoAAABjAw==", + "mac": null, + "id": null + }, + "Favorites": { + "py/object": "sonyapilib.device.XmlApiObject", + "name": "Favorites", + "mode": null, + "url": null, + "type": "ircc", + "value": "AAAAAwAAHFoAAABeAw==", + "mac": null, + "id": null + }, + "Yellow": { + "py/object": "sonyapilib.device.XmlApiObject", + "name": "Yellow", + "mode": null, + "url": null, + "type": "ircc", + "value": "AAAAAwAAHFoAAABpAw==", + "mac": null, + "id": null + }, + "Blue": { + "py/object": "sonyapilib.device.XmlApiObject", + "name": "Blue", + "mode": null, + "url": null, + "type": "ircc", + "value": "AAAAAwAAHFoAAABmAw==", + "mac": null, + "id": null + }, + "Red": { + "py/object": "sonyapilib.device.XmlApiObject", + "name": "Red", + "mode": null, + "url": null, + "type": "ircc", + "value": "AAAAAwAAHFoAAABnAw==", + "mac": null, + "id": null + }, + "Green": { + "py/object": "sonyapilib.device.XmlApiObject", + "name": "Green", + "mode": null, + "url": null, + "type": "ircc", + "value": "AAAAAwAAHFoAAABoAw==", + "mac": null, + "id": null + }, + "Play": { + "py/object": "sonyapilib.device.XmlApiObject", + "name": "Play", + "mode": null, + "url": null, + "type": "ircc", + "value": "AAAAAwAAHFoAAAAaAw==", + "mac": null, + "id": null + }, + "Stop": { + "py/object": "sonyapilib.device.XmlApiObject", + "name": "Stop", + "mode": null, + "url": null, + "type": "ircc", + "value": "AAAAAwAAHFoAAAAYAw==", + "mac": null, + "id": null + }, + "Pause": { + "py/object": "sonyapilib.device.XmlApiObject", + "name": "Pause", + "mode": null, + "url": null, + "type": "ircc", + "value": "AAAAAwAAHFoAAAAZAw==", + "mac": null, + "id": null + }, + "Rewind": { + "py/object": "sonyapilib.device.XmlApiObject", + "name": "Rewind", + "mode": null, + "url": null, + "type": "ircc", + "value": "AAAAAwAAHFoAAAAbAw==", + "mac": null, + "id": null + }, + "Forward": { + "py/object": "sonyapilib.device.XmlApiObject", + "name": "Forward", + "mode": null, + "url": null, + "type": "ircc", + "value": "AAAAAwAAHFoAAAAcAw==", + "mac": null, + "id": null + }, + "Prev": { + "py/object": "sonyapilib.device.XmlApiObject", + "name": "Prev", + "mode": null, + "url": null, + "type": "ircc", + "value": "AAAAAwAAHFoAAABXAw==", + "mac": null, + "id": null + }, + "Next": { + "py/object": "sonyapilib.device.XmlApiObject", + "name": "Next", + "mode": null, + "url": null, + "type": "ircc", + "value": "AAAAAwAAHFoAAABWAw==", + "mac": null, + "id": null + }, + "Replay": { + "py/object": "sonyapilib.device.XmlApiObject", + "name": "Replay", + "mode": null, + "url": null, + "type": "ircc", + "value": "AAAAAwAAHFoAAAB2Aw==", + "mac": null, + "id": null + }, + "Advance": { + "py/object": "sonyapilib.device.XmlApiObject", + "name": "Advance", + "mode": null, + "url": null, + "type": "ircc", + "value": "AAAAAwAAHFoAAAB1Aw==", + "mac": null, + "id": null + }, + "Angle": { + "py/object": "sonyapilib.device.XmlApiObject", + "name": "Angle", + "mode": null, + "url": null, + "type": "ircc", + "value": "AAAAAwAAHFoAAABlAw==", + "mac": null, + "id": null + }, + "TopMenu": { + "py/object": "sonyapilib.device.XmlApiObject", + "name": "TopMenu", + "mode": null, + "url": null, + "type": "ircc", + "value": "AAAAAwAAHFoAAAAsAw==", + "mac": null, + "id": null + }, + "PopUpMenu": { + "py/object": "sonyapilib.device.XmlApiObject", + "name": "PopUpMenu", + "mode": null, + "url": null, + "type": "ircc", + "value": "AAAAAwAAHFoAAAApAw==", + "mac": null, + "id": null + }, + "Eject": { + "py/object": "sonyapilib.device.XmlApiObject", + "name": "Eject", + "mode": null, + "url": null, + "type": "ircc", + "value": "AAAAAwAAHFoAAAAWAw==", + "mac": null, + "id": null + }, + "Karaoke": { + "py/object": "sonyapilib.device.XmlApiObject", + "name": "Karaoke", + "mode": null, + "url": null, + "type": "ircc", + "value": "AAAAAwAAHFoAAABKAw==", + "mac": null, + "id": null + }, + "Netflix": { + "py/object": "sonyapilib.device.XmlApiObject", + "name": "Netflix", + "mode": null, + "url": null, + "type": "ircc", + "value": "AAAAAwAAHFoAAABLAw==", + "mac": null, + "id": null + }, + "Mode3D": { + "py/object": "sonyapilib.device.XmlApiObject", + "name": "Mode3D", + "mode": null, + "url": null, + "type": "ircc", + "value": "AAAAAwAAHFoAAABNAw==", + "mac": null, + "id": null + }, + "ZoomIn": { + "py/object": "sonyapilib.device.XmlApiObject", + "name": "ZoomIn", + "mode": null, + "url": null, + "type": "url", + "value": "http://10.0.0.102:50002/setBrowse?action=zoomIn", + "mac": null, + "id": null + }, + "ZoomOut": { + "py/object": "sonyapilib.device.XmlApiObject", + "name": "ZoomOut", + "mode": null, + "url": null, + "type": "url", + "value": "http://10.0.0.102:50002/setBrowse?action=zoomOut", + "mac": null, + "id": null + }, + "BrowserBack": { + "py/object": "sonyapilib.device.XmlApiObject", + "name": "BrowserBack", + "mode": null, + "url": null, + "type": "url", + "value": "http://10.0.0.102:50002/setBrowse?action=back", + "mac": null, + "id": null + }, + "BrowserForward": { + "py/object": "sonyapilib.device.XmlApiObject", + "name": "BrowserForward", + "mode": null, + "url": null, + "type": "url", + "value": "http://10.0.0.102:50002/setBrowse?action=forward", + "mac": null, + "id": null + }, + "BrowserBookmarkList": { + "py/object": "sonyapilib.device.XmlApiObject", + "name": "BrowserBookmarkList", + "mode": null, + "url": null, + "type": "url", + "value": "http://10.0.0.102:50002/getBrowse?action=bookmarkList", + "mac": null, + "id": null + } + }, + "apps": {}, + "pin": null, + "cookies": { + "py/object": "requests.cookies.RequestsCookieJar", + "py/state": { + "_cookies": { + "192.168.170.23": { + "/sony/": { + "auth": { + "py/object": "http.cookiejar.Cookie", + "_rest": {}, + "comment": null, + "comment_url": null, + "discard": false, + "domain": "192.168.170.23", + "domain_initial_dot": false, + "domain_specified": false, + "expires": 1556462645, + "name": "auth", + "path": "/sony/", + "path_specified": true, + "port": null, + "port_specified": false, + "rfc2109": false, + "secure": false, + "value": "b2d0ff57a51b475cb3d1a9b7516f366ed2b4ffe0164cd0fd560ff6ceb28b101a", + "version": 0 + } + } + } + }, + "_now": 1555253045, + "_policy": { + "py/object": "http.cookiejar.DefaultCookiePolicy", + "_allowed_domains": null, + "_blocked_domains": { + "py/tuple": [] + }, + "_now": 1555253045, + "hide_cookie2": false, + "netscape": true, + "rfc2109_as_netscape": null, + "rfc2965": false, + "strict_domain": false, + "strict_ns_domain": 0, + "strict_ns_set_initial_dollar": false, + "strict_ns_set_path": false, + "strict_ns_unverifiable": false, + "strict_rfc2965_unverifiable": true + } + } + }, + "mac": "30-52-cb-cc-16-ee", + "api_version": 3, + "dmr_base": "http://test:52323", + "dmr_url": "http://test:52323/dmr.xml", + "base_url": "http://test/sony/", + "ircc_base": "http://test:50001", + "ircc_url": "http://test:50001/Ircc.xml", + "irccscpd_url": "http://test:50001/IRCCSCPD.xml", + "_ircc_categories": { + "py/set": [] + } +} \ No newline at end of file diff --git a/tests/device_test.py b/tests/device_test.py index 5b3d5aa..12ce80f 100644 --- a/tests/device_test.py +++ b/tests/device_test.py @@ -20,7 +20,7 @@ # otherwise it must be installed after every change import sonyapilib.device # import to change timeout from sonyapilib.ssdp import SSDPResponse -from sonyapilib.device import SonyDevice, XmlApiObject, AuthenticationResult +from sonyapilib.device import SonyDevice, XmlApiObject, AuthenticationResult, HttpMethod sys.path.pop(0) @@ -279,6 +279,22 @@ def test_update_service_urls_error_response(self): device = self.create_device() device._update_service_urls() + @mock.patch('requests.get', side_effect=mocked_requests_get) + @mock.patch('requests.post', side_effect=mocked_requests_post) + def test_load_v0_5_0_json_file(self, mocked_requests_post, mocked_requests_get): + content = read_file("data/v0.5.0.json") + device = SonyDevice.load_from_json(content) + + self.verify_json_load_fields(device) + + @mock.patch('requests.get', side_effect=mocked_requests_get) + @mock.patch('requests.post', side_effect=mocked_requests_post) + def test_load_v0_6_0_json_file(self, mocked_requests_post, mocked_requests_get): + content = read_file("data/v0.6.0.json") + device = SonyDevice.load_from_json(content) + + self.verify_json_load_fields(device) + @mock.patch('requests.get', side_effect=mocked_requests_get) @mock.patch('sonyapilib.device.SonyDevice._parse_ircc', side_effect=mock_error) def test_update_service_urls_error_processing(self, mock_error, mocked_requests_get): @@ -369,6 +385,42 @@ def test_parse_ircc_no_missing_info(self, mock_get): self.assertEqual( device.control_url, 'http://test:50001/upnp/control/IRCC') + @mock.patch('requests.get', side_effect=mocked_requests_get) + def test_find_device_info_none_upnp_device(self, mock_get): + device = self.create_device() + response = device._send_http(device.ircc_url, method=HttpMethod.GET, raise_errors=True) + + self.assertEqual(device._find_device_info(response.text, "friendlyName"), "Blu-ray Disc Player") + + @mock.patch('requests.get', side_effect=mocked_requests_get) + def test_parse_system_info_none_upnp_device(self, mock_get): + device = self.create_device() + response = device._send_http(device.ircc_url, method=HttpMethod.GET, raise_errors=True) + + device._parse_system_info(response.text, device.ircc_base) + + self.verify_system_info_fields(device) + + def test_set_value(self): + device = self.create_device() + self.assertEqual(hasattr(device, "test"), False) + + device._set_value("test", None) + self.assertEqual(hasattr(device, "test"), True) + + device._set_value("test", "test1") + self.assertEqual(getattr(device, "test"), "test1") + + device._set_value("test", "test2") + self.assertEqual(getattr(device, "test"), "test1") + + @mock.patch('requests.get', side_effect=mocked_requests_get) + def test_system_info(self, mock_get): + device = self.create_device() + device._parse_ircc() + + self.verify_system_info_fields(device) + def test_parse_action_list_error(self): # just make sure nothing crashes device = self.create_device() @@ -933,6 +985,45 @@ def create_device(): device.cookies = jsonpickle.decode(read_file("data/cookies.json")) return device + def verify_json_load_fields(self, device): + """Make sure all "new" fields are present in the json.""" + self.assertEqual(device.rendering_control_url, "http://test:52323/upnp/control/RenderingControl") + self.assertEqual(device.dmr_base, "http://test:52323") + self.assertEqual(device.ircc_base, "http://test:50001") + + self.verify_system_info_fields(device, "BDP-S5500", "BDP-2015", [ + "http://test:52323/bdp_ax_device_icon_large.jpg", + "http://test:52323/bdp_ax_device_icon_large.png", + "http://test:52323/bdp_ax_device_icon_small.jpg", + "http://test:52323/bdp_ax_device_icon_small.png" + ]) + + def verify_system_info_fields( + self, + device, + model_name="Blu-ray Disc Player", + model_number=None, + icons=None + ): + """Make sure all system fields are present in the device.""" + if not icons: + icons = [ + "http://test:50001/bdp_ax3d_device_icon_large.jpg", + "http://test:50001/bdp_ax3d_device_icon_large.png", + "http://test:50001/bdp_ax3d_device_icon_small.jpg", + "http://test:50001/bdp_ax3d_device_icon_small.png" + ] + + self.assertEqual(device.friendly_name, "Blu-ray Disc Player") + self.assertEqual(device.manufacturer, "Sony Corporation") + self.assertEqual(device.manufacturer_url, "http://www.sony.net/") + self.assertEqual(device.model_description, None) + self.assertEqual(device.model_name, model_name) + self.assertEqual(device.model_url, None) + self.assertEqual(device.model_number, model_number) + + self.assertEqual(device.icons, icons) + def verify_device_dmr(self, device): """Make sure a dmr has been set""" self.assertEqual(device.av_transport_url, AV_TRANSPORT_URL)