Events

158 views
Skip to first unread message

Orph

unread,
May 23, 2017, 4:24:58 PM5/23/17
to SoCo - Sonos Controller
Hi,
As a learning project, I am building a Sonos controller using soco. ( Great library! )
Currently everything is is pull based. Trying to have the events pushed to the controller, but I am really having a hard time to understand how the event module works. Needless to say, I am a novice.
Is there any one that can help in the right direction, on where to start or has a code snippet i can use to understand and learn ? 

I want to receive events from all the speakers on the network - and with the current documentation, I can get it to work on one of the speakers.

Thank you in advance.

DPH

unread,
May 25, 2017, 5:08:02 AM5/25/17
to SoCo - Sonos Controller
Hi Orph,
events are great for monitoring the status of Sonos. There are 2 main types of events associated with what's playing that is avTransport which tells you what music is playing and renderingControl that tells you about volumes, mute etc. 
There are other events such as group and queue etc but I guess you are interested in the above. 

For each event, on each Sonos player you need to subscribe to that event. Like taking out a magazine subscription, once you have started a subscription the magazines keep popping through your door! Typically you put them in a pile to read when you get round to it. The same for events on SoCo: once subscribed to an event the events for that subscription will appear on an events queue (think of pile). You can loop round and get events from the queue (pile) that are there. If there are no events on that queue just carry on. In the current event module it puts events onto a queue for you to collect when ready. Your program has to loop looking for events on each queue, and take whatever action you require if it finds an event. 

There is a good example at the start of events.py line 50 - this takes a random Sonos player and subscribes to both av and render events (sub and sub2)
There is then a loop which checks each sub. I assume you have used this to get one Sonos unit working?

To expand to more players just subscribe to them and include them in the loop.

I hope this makes sense. 
I have several approaches for subscribing to all players which I can share if it helps. 
The easiest way is to create a python dictionary or list of subscriptions then your loop can loop through the list / dic. looking for events. 

If you want more help please reply here. 
If you say more about what you are trying to achieve we can help more specifically.

Cheers David

Orph

unread,
May 26, 2017, 5:25:43 AM5/26/17
to SoCo - Sonos Controller
Hi David,

Firstly - THANK YOU !
For taking the time, and explaining it to me.

I would greatly apprechiate if you could share your approach with me.
I am combining til Soco lib with the PyQT lib - for the gui part.
Placed the loop in main, after creating the GUI instance, but do get an error, as soon as there is more than one speakers in the loop.

I want to build the code as flexible as possible, without setting a fixed number of devices on the network. I am trying to figure out a way the loop through the number of devices on network (device = soco.discover() ), filtering the group coordinator( if device.is_coordinator == True), setting the subs and getting the events.

Simon

DPH

unread,
May 26, 2017, 3:50:50 PM5/26/17
to SoCo - Sonos Controller
Hi again,
try just the soco code on its own and get that working then you can integrate it to a GUI.

The render event will give you volume and the AV event what's playing.  If a player is a slave then there is an AV event changing it to say the CurrentURI = 'x-rincon:RINCON_000E5832xxxxx1400' where this is the UID of the master (coordinator). After that you don't get any AV events. So you can subscribe and unsubscribe to these but I am not sure its necessary.

The code below is a demo (tested in Python 3) that subscribes to AV and Render for all Sonos players.
At present it just prints it has got an event but obviously you could change that to process the event.
Not perfect Python but demos what I think you want!

from __future__ import print_function
try:
import queue as queue
except: # Py2.7
import Queue as queue

import soco
from soco.events import event_listener
import queue
from pprint import pprint

# create a list to hold all subscriptions
sub_list = []

# get all sonos devices
devices = soco.discover()

for device in devices:
# subscribe to both av and render events for each sonos device
sub_list.append(device.avTransport.subscribe())
sub_list.append(device.renderingControl.subscribe())

keep_running = True
while keep_running:
for sub in sub_list:
try:
event = sub.events.get(timeout=0.1)

print('event_type: {:20} from: {}'.format(
event.service.service_type,
event.service.soco.player_name))

# pprint(event.variables)

except queue.Empty:
pass

except KeyboardInterrupt:
keep_running = False

for sub in sub_list:
sub.unsubscribe()
event_listener.stop()

Try this and see if it does what you want. If not post your soco code so I can look at it. 

As far as the GUI is concerned I have not used PyQT, so can't help much there. 
I do know that most GUIs have a loop that has to run for the GUI to respond - that means you need to find a way to have the soco read events loop running as well.
This therefore gets more complex, and it typically done using threads. So you could run your listen to sonos events in a thread and feed actions to your GUI via a queue. 
If your not familiar with threads read up about them as they can be great, but also can get you into a lot of trouble!
It may be that PyQT helps with this - can anyone else comment on PyQT?

Hope this all helps
Shout if you need more help

Cheers David

Orph

unread,
May 28, 2017, 5:52:37 AM5/28/17
to SoCo - Sonos Controller
Hi David, 

Well it works perfectly.
Ill just spend some  time understanding why it works and my generated an error and  how to implement it into the PyQT - I have a couple of ideas.

Thank you - greatly appreciated ! !

Orph

unread,
May 28, 2017, 3:42:02 PM5/28/17
to SoCo - Sonos Controller
Just an update:

Read about threading and placed a tread in the pyQT init - worked like a charm ( except QPixmap - it dosnt like threading :) )

Once again thx for helping out.

Orph.
Orphlab.com

Den fredag den 26. maj 2017 kl. 21.50.50 UTC+2 skrev DPH:

Kenneth Nielsen

unread,
Jun 2, 2017, 7:51:57 AM6/2/17
to SoCo - Sonos Controller
Hi Orph

A little trick. Qt is event based and has its own event loop which runs in the main thread. Qt doesn't like if you keep anything running forever in the main thread, because it will lock up the event loop. So in general you cannot have infinite loops in Qt without threading, but you can "cheat" and use Qt's event loop to emulate an infinite loop as long as you don't do very much work in it i.e. wait most of the time, just like this case. So a way to transform:

while not stop:
    get_from_queue()
    sleep(1)

to something that will run in Qt, would be something like this in a method in a class would be something like:

def __init__(self):
    ...
    call_later(self.process_events, 1)  # To start it

def process_events(self)
    get_from_queue()
    if not stop:
        call_later(self.process_events)

Now, obviously this is pseudo-code. There is no call_later function, but what it just means is have the event loop call a function for you later and you can google how to do that. If I remember correctly it involves the QTimer class.

Regards Kenneth
Message has been deleted

Kristian Doolan

unread,
Mar 29, 2020, 5:37:07 AM3/29/20
to SoCo - Sonos Controller
Hey guys,
love the thread and the extension!  Thanks for the input.  I am new to Python and trying to use your logic to listen for an event (my bedroom speaker playing), then check if there is a sleep timer already running, if yes - nothing else, if no set the sleep timer.

my issue is that i am struggling with the syntax to determine the PLAYING state and have it as a logical condition.  I thought that this would do it but, alas no cigar.  
If SleepTimerValue>'1' and TransportStateValue=PLAYING

I am sure this is something simple and probably more of a python question than soco per se but i have searched extensively and i cant seem to get this working.  Any tips would be amazing.
Also i am aware i still have some code in there that is not required but i dont believe it is harmful just inelegant, i wanted to get things working first :)
Cheers
Kristian


from __future__ import print_function
try:
    from queue import Empty
except:  # Py2.7
    from Queue import Empty

import logging
logging.basicConfig()
import soco
from pprint import pprint
from soco.events import event_listener
from soco import SoCo
device=SoCo('192.168.1.103'
# pick a device at random and use it to get
# the group coordinator
print (device.player_name)
sub = device.renderingControl.subscribe()
sub2 = device.avTransport.subscribe()

SleepTimerValue=device.get_sleep_timer()
TransportStateValue=device.get_current_transport_info

while True:
    try:
        event = sub.events.get(timeout=0.5)
        pprint (event.variables)

#        If SleepTimerValue>'1' and TransportStateValue=PLAYING
        if get_sleep_timer()>1 and get_current_transport_info is ('PLAYING'):
            device.set_sleep_timer(3600)

    except Empty:
        pass
    try:
        event = sub2.events.get(timeout=0.5)
        pprint (event.variables)
    except Empty:
        pass

    except KeyboardInterrupt:
        sub.unsubscribe()
        sub2.unsubscribe()
        event_listener.stop()
        break

Marco Quezada

unread,
Mar 29, 2020, 9:50:32 AM3/29/20
to Kristian Doolan, SoCo - Sonos Controller
Not sure if this is relevant to your error, but the event only gets sent when the state changes, so you can't use them to get the current state of the player. Anyway, maybe I misunderstood nthe question but that is something I noticed in your code. Good luck!

--
You received this message because you are subscribed to the Google Groups "SoCo - Sonos Controller" group.
To unsubscribe from this group and stop receiving emails from it, send an email to python-soco...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/python-soco/fad9d456-ab53-43f7-ad2a-ada51e24c939%40googlegroups.com.
Reply all
Reply to author
Forward
0 new messages