alexmohr / sonyapilib

Contains a python api to control sony devices.
MIT License
20 stars 11 forks source link

Unsupported 2010 BDP models #57

Closed mtdcr closed 4 years ago

mtdcr commented 4 years ago

Hi,

I patched sonyapilib to be able to register at my old bluray player (BDP-S370).

Among other things, it doesn't offer a command list. However, it uses the same type="ircc" key codes as listed in getRemoteCommandList.xml.

Here's a copy of an Ircc.xml from this generation:

<?xml version="1.0"?>
<root xmlns="urn:schemas-upnp-org:device-1-0">
  <specVersion>
    <major>1</major>
    <minor>0</minor>
  </specVersion>
  <device>
    <deviceType>urn:schemas-upnp-org:device:Basic:1</deviceType>
    <friendlyName>Blu-ray Disc Player</friendlyName>
    <manufacturer>Sony Corporation</manufacturer>
    <manufacturerURL>http://www.sony.net/</manufacturerURL>
    <modelDescription/>
    <modelName>Blu-ray Disc Player</modelName>
    <modelURL/>
    <UDN>uuid:00000000-0000-1010-8000-544249bd7901</UDN>
    <serviceList>
      <service>
        <serviceType>urn:schemas-sony-com:service:IRCC:1</serviceType>
        <serviceId>urn:schemas-sony-com:serviceId:IRCC</serviceId>
        <SCPDURL>/IRCCSCPD.xml</SCPDURL>
        <controlURL>/upnp/control/IRCC</controlURL>
        <eventSubURL>/upnp/event/IRCC</eventSubURL>
      </service>
    </serviceList>
    <presentationURL/>
    <av:X_IRCC_DeviceInfo xmlns:av="urn:schemas-sony-com:av">
      <av:X_IRCC_Version>1.0</av:X_IRCC_Version>
      <av:X_IRCC_CategoryList>
        <av:X_IRCC_Category>
          <av:X_CategoryInfo>AAMAABxa</av:X_CategoryInfo>
        </av:X_IRCC_Category>
      </av:X_IRCC_CategoryList>
    </av:X_IRCC_DeviceInfo>
    <av:X_UNR_DeviceInfo xmlns:av="urn:schemas-sony-com:av">
      <av:X_UNR_Version>1.0</av:X_UNR_Version>
      <av:X_CERS_URL>http://10.0.0.9:50002/</av:X_CERS_URL>
      <av:X_CERS_ActionList_URL>http://10.0.0.9:50002/</av:X_CERS_ActionList_URL>
      <av:X_Remote_Type>BDPlayer2010</av:X_Remote_Type>
    </av:X_UNR_DeviceInfo>
  </device>
</root>

(Source: https://forums.homeseer.com/forum/media-plug-ins/media-discussion/media-controller-dcorsus/68042-support-for-sony-tv-bluray-media-player#post664783)

This is how a response on http://<host>:50002/ looks like if registration is open:

<?xml version="1.0"?>
<actionList>
  <action name="register"/>
  <action name="getContentInformation"/>
  <action name="getText"/>
  <action name="sendText"/>
</actionList>

If it's closed, the server returns 403.

Action URLs are using this format: http://<host>:50002/?action=register&param=value

diff --git a/sonyapilib/device.py b/sonyapilib/device.py
index ea74635..29c72ee 100644
--- a/sonyapilib/device.py
+++ b/sonyapilib/device.py
@@ -151,17 +151,22 @@ class SonyDevice:

     def _update_service_urls(self):
         """Initialize the device by reading the necessary resources from it."""
-        response = self._send_http(self.dmr_url, method=HttpMethod.GET)
-        if not response:
-            _LOGGER.error("Failed to get DMR")
+        try:
+            response = self._send_http(self.dmr_url, method=HttpMethod.GET, raise_errors=True)
+        except requests.exceptions.ConnectionError:
+            response = None
+        except requests.exceptions.RequestException as exc:
+            _LOGGER.error("Failed to get DMR: %s: %s", type(exc), exc)
             return

         try:
-            self._parse_dmr(response.text)
+            if response:
+                self._parse_dmr(response.text)
             if self.api_version <= 3:
                 self._parse_ircc()
                 self._parse_action_list()
-                self._parse_system_information()
+                if self.api_version > 0:
+                    self._parse_system_information()
             else:
                 self._parse_system_information_v4()

@@ -177,12 +182,21 @@ class SonyDevice:
             action = XmlApiObject(element.attrib)
             self.actions[action.name] = action

+            if action.mode is None:
+                action.mode = self.api_version
+            if action.url is None and action.name:
+                action.url = urljoin(self.actionlist_url, "?action={}".format(action.name))
+                separator = "&"
+            else:
+                separator = "?"
+
             if action.name == "register":
                 # the authentication is based on the device id and the mac
                 action.url = \
-                    "{0}?name={1}&registrationType=initial&deviceId={2}"\
+                    "{0}{1}name={2}&registrationType=initial&deviceId={3}"\
                     .format(
                         action.url,
+                        separator,
                         quote(self.nickname),
                         quote(self.client_id))
                 self.api_version = action.mode

Because the patch above is quite hackish, I'm not going to submit a PR. But I'd like to document my findings. Maybe you or someone else can find a nicer way to integrate it. I don't use my bluray player often these days, so my motivation is lacking a bit.

Good luck with your thesis, Alex!

mtdcr commented 4 years ago

The following calls don't need authentication or special HTTP headers:

alexmohr commented 4 years ago

Thanks for you suggestion. Honestly I don't think that your suggestion looks that hackish.
Only the way to get the correct url is a bit odd but I wouldn't know how to solve this differently at the moment. How do you handle the missing command list, are you using a prepared xml file?

mtdcr commented 4 years ago

How do you handle the missing command list, are you using a prepared xml file?

I don't know yet. How would you think about a default command list embedded as a dict with {name: value}? I wanted it to be usable from Home Assistant, but I think providing an extra XML file wouldn't work well for many users. I guess we could create an object compatible with XmlApiObject from that dict easily.

alexmohr commented 4 years ago

I think using a dict with {name: value} as you've suggested is probably the easiest way to provide defaults. The only drawback would be that you'd have to change the code to adjust default values but I don't think that would be necessary.

alexmohr commented 4 years ago

Fixed in #58 and #60. Thank you :)