Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Getting a SoCoUPnPException: UPnP Error 501 received: Action Failed from <player ip> on any request that calls GetZoneGroupState() #518

Open
powertieke opened this issue Sep 15, 2017 · 18 comments
Labels

Comments

@powertieke
Copy link

@powertieke powertieke commented Sep 15, 2017

soco.discover() and SoCo('').player_name give this error...

soco.exceptions.SoCoUPnPException: UPnP Error 501 received: Action Failed from

@DPH

This comment has been minimized.

Copy link
Member

@DPH DPH commented Sep 16, 2017

Hi @powertieke,
Assume you can play music on your Sonos with the normal controllers?
Assume you are running your code on the same network as your Sonos is?
Try running this (with one of your IP addresses) and post the results:

import soco
s = soco.SoCo('192.168.1.99')
print(s.player_name)

Cheers David

@powertieke

This comment has been minimized.

Copy link
Author

@powertieke powertieke commented Sep 18, 2017

All assumptions are correct.

Created a script containing the lines above.

(soco-env) reallymycomputername:sonos_webinterface reallymyusername$ python showname.py
Traceback (most recent call last):
File "showname.py", line 4, in
print(pl.player_name)
File "/Users/reallymyusername/code/sonos_webinterface/soco-env/lib/python3.5/site-packages/soco-0.12_-py3.5.egg/soco/core.py", line 249, in player_name
File "/Users/reallymyusername/code/sonos_webinterface/soco-env/lib/python3.5/site-> packages/soco-0.12_-py3.5.egg/soco/core.py", line 939, in parse_zone_group_state
File "/Users/reallymyusername/code/sonos_webinterface/soco-env/lib/python3.5/site-packages/soco-0.12
-py3.5.egg/soco/services.py", line 644, in GetZoneGroupState
File "/Users/reallymyusername/code/sonos_webinterface/soco-env/lib/python3.5/site-packages/soco-0.12_-py3.5.egg/soco/services.py", line 408, in send_command
File "/Users/reallymyusername/code/sonos_webinterface/soco-env/lib/python3.5/site-packages/soco-0.12_-py3.5.egg/soco/services.py", line 469, in handle_upnp_error
soco.exceptions.SoCoUPnPException: UPnP Error 501 received: Action Failed from 192.168.1.222

Requests for local settings like bass, treble, volume and loudness return the correct values, and can be set as expected. I can also get the player name using the s.deviceProperties.GetZoneAttributes()["CurrentZoneName"] method mentioned in the SoCo.player_name() method code.

@KennethNielsen

This comment has been minimized.

Copy link
Member

@KennethNielsen KennethNielsen commented Sep 18, 2017

One step at a time.

Discover is known to fail under certain atypical network setup, which is something I believe that there are several optimizations for in the development version, but nothing yet released. Sp the question is, if discovery fails, do you know the IP-address of one of your speakers from somewhere else and have used one of these valid addresses in these scripts?

Regards Kenneth

@powertieke

This comment has been minimized.

Copy link
Author

@powertieke powertieke commented Sep 18, 2017

I got the ip's from the sonos app's 'settings' -> 'about my sonos system' menu. This shows the speakers and ip's in the network / setup.

So yes, I'm using a known sonos speaker ip.

As for the network set-up : The speakers are set up in standard mode (no boost). The wifi system is a big setup with 16 AP's and a central controller. The router is a business grade gateway/firewall (So it won't do UPnP for security reasons).

@powertieke

This comment has been minimized.

Copy link
Author

@powertieke powertieke commented Sep 19, 2017

Extra update: I took down two play:1's from rooms that weren't using them and made a test setup using a home router... Everything worked in this setup. soco.discover() returns a nice set. pl.player_name displays the correct room name.

Now, the odd thing: I'm not getting errors on the old setup anymore... It all works as expected.

Even directed requests from another subnet through the router ( when connected by ip-address ) are working.

Adding the two speakers to the setup causes the error again. It remains for a while after the two are removed from the setup.

It seems I'm crossing some sort of magic barrier. As soon as there are more than 28 sonos devices in the air, the UPnP error 501 starts showing up. Controller still works, but grouping/ungrouping gets less stable.

@KennethNielsen

This comment has been minimized.

Copy link
Member

@KennethNielsen KennethNielsen commented Sep 20, 2017

Ok @powertieke. This is puzzling. A follow-up question. You mentioned that the official app is slow. I wonder if something about the Sonos network doesn't scale (although I can't imagine what) and the regular just knows to retry. Can I get you to try the same request in a loop, and with different delays between attempts and see whether it succeeds ever?

@powertieke

This comment has been minimized.

Copy link
Author

@powertieke powertieke commented Sep 21, 2017

No, it's really consistent when it emerges, no 'lucky hits' after multiple repeats.

But it gets worse. The number of unique groups in the household seems to matter too. I found this out while experimenting with unjoining all found players. Over a certain amount of unique groups the UPnP 501 error pops up again. Grouping rooms together in the app so that we get below this number of groups, and the errors disappear (immediately).

I'll update this later tonight, I found out some pretty specific things about the errors and when they pop up.

@KennethNielsen

This comment has been minimized.

Copy link
Member

@KennethNielsen KennethNielsen commented Sep 21, 2017

@powertieke Well, if not a retry I could still be something as simple as a larger timeout needed.

Will look into how to test that. If we have no luck there, then I don't think there is any way around it that you will have to pull out wireshark and see whether the official app does something different with the communication in these cases.

@KennethNielsen

This comment has been minimized.

Copy link
Member

@KennethNielsen KennethNielsen commented Sep 21, 2017

@powertieke forget what I said about a timeout, you do get a reply, it is just that the command failed, so obviously not a requests timeout issue. /me goes to make more coffee.

I was wondering, just to rule out some effects outside the realm of our control (like network issues), can you try outside of SoCo to just use requests to ask the controller for something. Like e.g.

import requests
r = requests.get('http://192.168.1.18:1400/xml/device_description.xml')
r.content

(obviously replacing the IP) when in the configuration where you cannot get SoCo to work

@powertieke

This comment has been minimized.

Copy link
Author

@powertieke powertieke commented Sep 21, 2017

Did two requests (while getting 501 errors on soco.discovery()) using the code above to 'http://192.168.1.222:1400/xml/device_description.xml' and 'http://192.168.1.222:1400/xml/ZoneGroupTopology1.xml'. No errors as far as I can see.

Only the "GetZoneGroupState" action seems to give the error. Could it be possible that the official app, if faced with this error, decides to talk to each device individually and builds the group/room tree locally? That could explain the sluggish response?

Guess I'll read up on wireshark.

In the mean time I'll work around the problem by reducing the number of devices in the subnet to a number where this doesn't happen (Seems that I can have about 22 devices and 22 groups without error, so that'll have to do).

Can I use soco to join players without calls to the GetZoneGroupState action?

@KennethNielsen

This comment has been minimized.

Copy link
Member

@KennethNielsen KennethNielsen commented Sep 22, 2017

Could it be possible that the official app, if faced with this error, decides to talk to each device individually and builds the group/room tree locally? That could explain the sluggish response?

Maybe. I think at this point I don't have any other ideas than to pull out wireshark. As I remember it, it is easier to install wireshark on a machine that can run the official app and then you can pull network traffic there.

Please keep us updated about whether you want to do this work, so that we can determine whether to keep the issue open or not, as I can't think of anything else to do for this as it is.

@KennethNielsen KennethNielsen added Bug and removed Support labels Sep 22, 2017
@powertieke

This comment has been minimized.

Copy link
Author

@powertieke powertieke commented Sep 25, 2017

I'll try to get some more information with Wireshark later this week / next week.

Thanks.

@fenner

This comment has been minimized.

Copy link

@fenner fenner commented Jan 10, 2018

This happens for me when accessing any of the players in my network:

>>> player = soco.SoCo('192.168.1.54')
>>> print( player.player_name )
Traceback (most recent call last):
...
  File "/home/homeassistant/homeassistant/lib64/python3.5/site-packages/soco/services.py", line 640, in GetZoneGroupState
    return self.send_command('GetZoneGroupState', *args, **kwargs)
  File "/home/homeassistant/homeassistant/lib64/python3.5/site-packages/soco/services.py", line 404, in send_command
    self.handle_upnp_error(response.text)
  File "/home/homeassistant/homeassistant/lib64/python3.5/site-packages/soco/services.py", line 465, in handle_upnp_error
    error_xml=xml_error
soco.exceptions.SoCoUPnPException: UPnP Error 501 received: Action Failed from 192.168.1.54

The text of the response is not at all illuminating:

(Pdb) p response.text
'<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"><s:Body><s:Fault><faultcode>s:Client</faultcode><faultstring>UPnPError</faultstring><detail><UPnPError xmlns="urn:schemas-upnp-org:control-1-0"><errorCode>501</errorCode></UPnPError></detail></s:Fault></s:Body></s:Envelope>'

You probably know this, but the request was:

(Pdb) p body
'<?xml version="1.0"?><s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"><s:Body><u:GetZoneGroupState xmlns:u="urn:schemas-upnp-org:service:ZoneGroupTopology:1"></u:GetZoneGroupState></s:Body></s:Envelope>'

I captured the interactions between my mac and the speaker while grouping and ungrouping it; I did not see any GetZoneGroupState SOAP call. I see Play, Pause, and tons of GetPositionInfo.

@fenner

This comment has been minimized.

Copy link

@fenner fenner commented Jan 10, 2018

Following up on powertieke's observation that the number of groups matters, I grouped my 28 speakers into 3 big groups, and no longer get the exception.

@fenner

This comment has been minimized.

Copy link

@fenner fenner commented Jan 10, 2018

I used tcpflow to extract all of the conversations out of a dump I took of all TCP to port 1400, while starting and quitting the SONOS Mac app. I got the following operations:

   1 POST /MusicServices/Control
   1 SUBSCRIBE /AlarmClock/Event
   1 SUBSCRIBE /MusicServices/Event
   1 SUBSCRIBE /SystemProperties/Event
   1 SUBSCRIBE /ZoneGroupTopology/Event
   1 UNSUBSCRIBE /AlarmClock/Event
   1 UNSUBSCRIBE /MusicServices/Event
   1 UNSUBSCRIBE /SystemProperties/Event
   1 UNSUBSCRIBE /ZoneGroupTopology/Event
   2 SUBSCRIBE /MediaRenderer/Queue/Event
   2 UNSUBSCRIBE /MediaRenderer/Queue/Event
   3 POST /MediaServer/ContentDirectory/Control
   7 POST /AlarmClock/Control
  12 POST /MediaRenderer/AVTransport/Control
  18 SUBSCRIBE /MediaRenderer/AVTransport/Event
  18 UNSUBSCRIBE /MediaRenderer/AVTransport/Event
  21 POST /SystemProperties/Control
  23 SUBSCRIBE /MediaRenderer/RenderingControl/Event
  23 UNSUBSCRIBE /MediaRenderer/RenderingControl/Event
  25 SUBSCRIBE /MediaServer/ContentDirectory/Event
  25 UNSUBSCRIBE /MediaServer/ContentDirectory/Event
  29 GET /xml/device_description.xml

(meaning, e.g., there were 25 SUBSCRIBEs and 25 UNSUBSCRIBEs to /MediaServer/ContentDirectory/Event). Which of these items look like they might be interesting?

@KennethNielsen

This comment has been minimized.

Copy link
Member

@KennethNielsen KennethNielsen commented Feb 22, 2018

Hmm. I seem to remember the discovery code specifically mention stopping waiting for replies on the discovery in favor of using a call to one speaker to get the topology. That might be modded.

@fenner

This comment has been minimized.

Copy link

@fenner fenner commented Dec 31, 2019

I tried a fresh install of soco and rediscovered this issue. I realize that it only happens on large installations with many ungrouped speakers. Since the SONOS controller app doesn't use the GetZoneGroupState SOAP call, it seems unlikely that they care that it's broken. I see that lots of code has comments like "we could do this this way but it's faster to get it from the zone topology". So if we do something like

--- a/soco/core.py
+++ b/soco/core.py
@@ -281,12 +281,12 @@ class SoCo(_SocoSingletonBase):
     @property
     def player_name(self):
         """str: The speaker's name."""
-        # We could get the name like this:
-        # result = self.deviceProperties.GetZoneAttributes()
-        # return result["CurrentZoneName"]
-        # but it is probably quicker to get it from the group topology
-        # and take advantage of any caching
         self._parse_zone_group_state()
+        if self._player_name is None:
+            # The zone config handling failed.
+            # Fetch it from the player directly.
+            result = self.deviceProperties.GetZoneAttributes()
+            return result["CurrentZoneName"]
         return self._player_name
 
     @player_name.setter

we end up with

>>> player = soco.SoCo( '192.168.13.125' )
>>> print( player.player_name )
Powder Room

Unless we discover another mechanism to leverage the players discovering each other, soco would need a method to receive discovery responses from all speakers and query each individually. Based on the app behavior, this appears to be what the SONOS controller app does: it populates the speaker list incrementally.

Given that there hasn't been any movement on this in ~2 years, it seems clear that there is little interest in using soco on networks with a large number of speakers. If I experiment in this area, would the project be willing to accept patches?

@fenner

This comment has been minimized.

Copy link

@fenner fenner commented Dec 31, 2019

As an example of what I'm talking about wrt discovery, this works on my network:

--- a/soco/discovery.py
+++ b/soco/discovery.py
@@ -130,6 +130,8 @@ def discover(timeout=5, include_invisible=False, interface_a
         for _sock in _sockets:
             _sock.sendto(really_utf8(PLAYER_SEARCH), (MCAST_GRP, MCAST_PORT))

+    retval = set()
+
     t0 = time.time()
     while True:
         # Check if the timeout is exceeded. We could do this check just
@@ -140,7 +142,10 @@ def discover(timeout=5, include_invisible=False, interface_
         # is no monotonic timer available before Python 3.3.
         t1 = time.time()
         if t1 - t0 > timeout:
-            return None
+            if retval:
+                return retval
+            else:
+                return None

         # The timeout of the select call is set to be no greater than
         # 100ms, so as not to exceed (too much) the required timeout
@@ -179,6 +184,15 @@ def discover(timeout=5, include_invisible=False, interface_
                     # Player's ability to find the others, than to wait for
                     # query responses from them ourselves.
                     zone = config.SOCO_CLASS(addr[0])
+                    # The attempt to find others may fail.  If there
+                    # are no zones in "all zones", this has failed
+                    # (this player must be in "all zones"), so we add the
+                    # player to the list and keep discovering until
+                    # timeout.
+                    if not zone.all_zones:
+                        retval.add( zone )
+                        continue
+
                     if include_invisible:
                         return zone.all_zones
                     else:
>>> [p.player_name for p in soco.discover()]
['Barn Sonos', 'Dining Room', 'Cottage MBR', 'Powder Room', 'Living Room', 'Roaming Play:1', 'Laundry Room', 'Patio', 'Cottage Kitchen', 'Cottage Bathroom', 'Lower Garage', 'Living Room', 'Master Bedroom', 'Kitchen', 'Workshop', 'Master Patio', u'Julia\u2019s Bathroom', 'Roaming Play:3', 'Living Room', 'Library', 'Foyer', 'Master Bathroom', 'Barn Downstairs', 'Barn Sonos', 'Sports Court', u'Julia\u2019s Bedroom', 'Living Room', 'Fitness Center', 'Cottage LivingRm']
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
4 participants
You can’t perform that action at this time.