Events

You can receive events about changes on the Sonos network.

The soco.services.Service.subscribe() method of a service now returns a soco.events.Subscription object. To unsubscribe, call the soco.events.Subscription.unsubscribe() method on the returned object.

Each subscription has its own queue. Events relevant to that subscription are put onto that queue, which can be accessed from subscription.events.get().

Some XML parsing is done for you when you retrieve an event from the event queue. The get and get_nowait methods will return a dict with keys which are the evented variables and values which are the values sent by the event.

See the events_twisted module page for more information about soco.events_twisted.

See the events_asyncio module page for more information about soco.events_asyncio.

Example: setting up

soco.events

from queue import Empty

import soco
from soco.events import event_listener
import logging
logging.basicConfig(level=logging.DEBUG)
# pick a device
device = soco.discover().pop()
# Subscribe to ZGT events
sub = device.zoneGroupTopology.subscribe()

# print out the events as they arise
while True:
    try:
        event = sub.events.get(timeout=0.5)
        print(event)
        print(event.sid)
        print(event.seq)

    except Empty:
        pass
    except KeyboardInterrupt:
        sub.unsubscribe()
        event_listener.stop()
        break

soco.events_twisted

import soco
from soco import events_twisted
soco.config.EVENTS_MODULE = events_twisted
from twisted.internet import reactor
import logging
logging.basicConfig(level=logging.DEBUG)

def print_event(event):
    print (event)
    print(event.sid)
    print(event.seq)

def main():
    # pick a device
    device = soco.discover().pop()
    # Subscribe to ZGT events
    sub = device.zoneGroupTopology.subscribe().subscription
    # print out the events as they arise
    sub.callback = print_event

    def before_shutdown():
        sub.unsubscribe()
        events_twisted.event_listener.stop()

    reactor.addSystemEventTrigger(
        'before', 'shutdown', before_shutdown)

if __name__=='__main__':
    reactor.callWhenRunning(main)
    reactor.run()

soco.events_asyncio

See soco.events_asyncio for a setup example.

Examples: specific features

Autorenewal

A Subscription may be granted by the Sonos system for a finite time. Unless it is renewed before it times out, the subscription will become defunct once it times out. To avoid this, the autorenewal feature can be used. If the auto-renew flag is set to True, the subscription will automatically renew when 85% of its time has expired.

soco.events:

sub = device.renderingControl.subscribe(auto_renew=True)

soco.events_twisted:

sub = device.renderingControl.subscribe(auto_renew=True).subscription

Timeout

When subscribing for events, a timeout of a specific duration can be requested.

soco.events:

sub = device.renderingControl.subscribe(requested_timeout=60) # 60 seconds

soco.events_twisted:

sub = device.renderingControl.subscribe(requested_timeout=60).subscription

Renewal

To renew without relying on autorenewal, the renew method can be used:

sub.renew(requested_timeout=10)

Autorenew failure

If you want your application to respond to an autorenew failure (for example if the Sonos system dropped off the network), you can set an optional callback that will be called with the exception that occurred on the attempted autorenew:

import logging
logging.basicConfig()
log = logging.getLogger(__name__)

def errback(exception): # events_twisted: failure
    msg = 'Error received on autorenew: {}'.format(str(exception))
    # Redundant, as the exception will be logged by the events module
    log.exception(msg)

sub.auto_renew_fail=errback

Note: In soco.events the auto_renew_fail function will be called from a thread, so it must be threadsafe.

Lenient error handling

By default, if an exception occurs when subscribing, renewing or unsubscribing a subscription, the exception will be raised. This can be changed so the exception is logged instead, by setting the strict flag to be false:

sub.unsubscribe(strict=False)

Events_twisted: adding callbacks and errbacks

If the events_twisted module is used, subscribe, renew and unsubscribe return a Deferred, the result of which will be the Subscription instance. Callbacks and errbacks can be added in the usual way:

device.renderingControl.subscribe().addCallback(myCallback).addErrback(
    myErrback)