Skip to main content
Headliner II
January 4, 2013

Fully managed C# library

Hi, I have been working on a simplified UPnP library which is aimed at Sonos that I want to make available to others.

You can find the source-code here:

https://github.com/jishi/Jishi.Intel.SonosUPnP

This is based upon Intels UPnP framework, which is opposed to the COM object that other attempts that I have found, based completely on managed code (no unmanaged code). This makes a huge difference if you are building anything multithreaded (WinForms, WPF etc).

I will try to put up some example code for people to look at.

Right now some of the features are pretty hacky since I'm still learning best practices, but hopefully it simplifies it a bit for someone.

This is built for .NET 4 Client Profile. The requirements is only .NET 4, previous versions doesn't need to exist.

Basic usage is this:

code:

// Create the main object
var discovery = new SonosDiscovery();

// Attach an event handler for topologychanged, this will be triggered as soon as it has found players (and when topology change)
discovery.TopologyChanged += TopologyChanged;

// This will start the UPnP-scanning, this is a manual call so that you are able to setup all event handlers first
discovery.StartScan();


If you want a more descriptive example, you can check out my Dashboard project:

https://github.com/jishi/Panagora.Sonos.Dashboard/blob/master/Panagora.Sonos.Dashboard/MainWindow.xaml.cs

Is a really simple fullscreen app that shows currently playing song for the most relevant player (Biggest group, longest queue).

Any suggestions and feedback is appreciated. As of today it only supports the following features:

Zone discovery
Player status notifications
Play/Pause
Enqueue
Seek (to queue position)
SetAVTransportURI
    This topic has been closed for further comments. You can use the search bar to find a similar topic, or create a new one by clicking Create Topic at the top of the page.

    26 replies

    jishiAuthor
    Headliner II
    January 4, 2013
    On the Todo-list:


    • Smarter Topology change subscription (less chatty)
    • Unsubscribe upon Dispose() (Add IDisposable)
    • Better Player status check
    BarryM
    January 4, 2013
    Looks fun. I downloaded both, and had a quick peek before heading off for bed.

    Would love to have a play, but my version of VS was out of date, and I ditched it when upgrading to my new PC.

    Are you able to add tracks into the Sonos queue, or just cause individual tracks to be played on a Sonos zone?

    I am building a Sonos class library too, but in Python. So far mine is just an observer (discovery, zone objects with properties, and system-wide subscribe-able events for track start, track ready to scrobble, with track play data such as source (local library, lfm etc), track data, and a fair stab at who the listener is). I then have an app which uses this class library for play count collection, updating of local databases and scrobbling.

    I would like to extend it some time, or have something, to cover track queuing like you are working upon. My observation part has to be able to run under Linux (it runs on a Raspberry pi attached to my LAN), but I would be ok with having the browsing queuing part requiring .Net. The Intel UPnP class library you are using is .Net only?
    jishiAuthor
    Headliner II
    January 4, 2013
    I have been working in VS2012, so I assume that is the culprit. However, I think it should open up okay directly in the Express version (free).

    Yes, Enqueue is adding tracks directly into the Queue of the player (last), or as next track (special case, same as the official controller). I don't think it's possible to insert it anywhere in the queue, however I'm uncertain how the DesiredFirstTrackNumberEnqueued parameter is used. I'm using this for my PartyMode-app that I'm working on.

    If you just want to utilize track queue, it's fairly simple to invoke all those actions without the need for a UPnP library. As long as you have the IP-address of the player, creating the SOAP call is fairly trivial.

    My first attempt was completely without a library to begin with. M-SEARCH and reading out the player XMLs, as well as invoking all the basic commands was already done, but the hard part came when I needed the subscriptions. That's when I found the Intel library and made the decision to drop my first attempt.

    The Intel UPnP framework is .NET only (2.0 I think), but I assume that python has it's own framework that should have all the necessary tools that you need.
    BarryM
    January 5, 2013
    I have been working in VS2012, so I assume that is the culprit. However, I think it should open up okay directly in the Express version (free).

    What I meant was that I had let my MSDN subscription lapse, as it was a bit $exy for just a hobby, and I had pretty much lost faith in Microsoft & .Net, so I had not installed VS onto my new PC. I had forgotten that there was a free version. Thanks.

    Yes, Enqueue is adding tracks directly into the Queue of the player (last), or as next track (special case, same as the official controller). I don't think it's possible to insert it anywhere in the queue, however I'm uncertain how the DesiredFirstTrackNumberEnqueued parameter is used. I'm using this for my PartyMode-app that I'm working on.

    At the end of the queue is quite ok. It was late, and I was getting mixed up with what my issue was.

    I do all my browsing via MediaMonkey as I have it with indices filtered or sorted by criteria such as Date Released, Date Ripped, Date Last Played, and/or Number of Times Played etc.

    MediaMonkey is also a DLNA media Server, so I can browse these indices via my Sonos controllers. The issue was that I can only queue a single track at a time to the queue (sorry about the confusion). This has been partially alleviated by the fact that new desktop controller now has the capability for multi-row selection. The remaining problem is that the half baked Sonos support of DLNA will only present the album's tracks in alphabetic sequence. So I can quickly browse to an album which I haven't played for a year, or whatever criteria I feel like at the time, but I can't queue it. ... I then need to exit back into the Sonos Local library section within the controller, find the album again, before I can queue it.

    What I would be looking for is to write a plug-in for MediaMonkey, or the MediaMonkey database at least, that can queue the album being browsed.

    Sounds like the work that you are doing gives the basis for this.


    If you just want to utilize track queue, it's fairly simple to invoke all those actions without the need for a UPnP library. As long as you have the IP-address of the player, creating the SOAP call is fairly trivial.

    My first attempt was completely without a library to begin with. M-SEARCH and reading out the player XMLs, as well as invoking all the basic commands was already done, but the hard part came when I needed the subscriptions. That's when I found the Intel library and made the decision to drop my first attempt.

    The Intel UPnP framework is .NET only (2.0 I think), but I assume that python has it's own framework that should have all the necessary tools that you need.


    There is an abandon-ware Python UPnP class library. I am ok with C# and .Net (MediaMonkey is a Windows product anyway) although Python is more fun. The problem is that class library holds me back to Python v2, which wouldn't be my 1st choice.

    Good talking to you. Some time soon I will get hold of the Express version of VS and have a play with what you are doing. Thanks for posting it. I'll get back to you with any feedback. It won't be until around March.
    Contributor I
    January 25, 2013
    Good work, might just migrate my Windows Forms app to use this library at some point. Now I'm just doing some uPnP calls to get currently playing media and it is in fact working very well.
    Trending Lyricist I
    March 15, 2013
    Awesome work!

    In about half an hour, I was able to get the zone and player detected and the events for zone player state changes to fire. I'm now successfully getting metadata (track, artist, and album art) from the Sonos unit - awesome!

    What I have not been able to do yet:
    1.) Retrieve a listing of "Sonos Favorites"
    2.) Retrieve a listing of the "Music Library"
    2.) Start playback of either of the above.

    Have you implemented this functionality? If so, can you point me in the right direction?

    It looks like I need be able to invoke a "browse" action on the "ContentDirectory" service - but it doesn't look like you've implemented that yet. I tried doing a .GetService on the MediaRenderer device, but I'm not getting any services returned from .GetService
    jishiAuthor
    Headliner II
    March 18, 2013

    GetArtists() takes no parameters and returns a list of artists
    GetAlbums(artistName) takes the artist name and returns a list of albums belong to that artist
    GetTracks(artistName) or GetTracks(artistName, albumName) takes either the artist name alone (to return all tracks from the artist) or the artist and album name (to return just a list of tracks on that album).


    Cool, and I assume a call to GetAlbums() without parameters should return all albums (and the same goes for GetTracks())?

    Implementing a ClearQueue should be easy enough as well. I can look into it tonight.
    Trending Lyricist I
    March 18, 2013
    Cool, and I assume a call to GetAlbums() without parameters should return all albums (and the same goes for GetTracks())?

    Implementing a ClearQueue should be easy enough as well. I can look into it tonight.


    Agreed - I think it would be the most flexible (and potentially useful to others, as well) if GetAlbums and GetTracks could alternatively be called without any parameters. That would allow, for example, someone to build a search functionality into their own app.

    Thanks again!
    Trending Lyricist I
    March 20, 2013
    I'm having an issue where, after perhaps 10 minutes or so, I stop receiving status updates.

    I'm trying to figure out if this is an issue with my code or with the library.

    Out of curiosity, what is the "600" in the AVTransport.Subscribe function in the zoneplayer class? That's not limiting the subscribe to 10 minutes (i.e., 60*10), is it?

    The other possibility is that when my app crashes (during debugging), it's causing the problem detailed in this thread: http://forums.sonos.com/showthread.php?t=31804&page=2

    "If you don't unsubscribe to an event (AVT in this case), when quitting your application, the player will try to send data to the subscription URL for a certain time...Now, the odd thing about this is that when this finally fails, it will kill all subscriptions associated with the same IP"

    If that's the problem, any ideas on resolution?
    jishiAuthor
    Headliner II
    March 20, 2013
    I'm having an issue where, after perhaps 10 minutes or so, I stop receiving status updates.

    I'm trying to figure out if this is an issue with my code or with the library.

    Out of curiosity, what is the "600" in the AVTransport.Subscribe function in the zoneplayer class? That's not limiting the subscribe to 10 minutes (i.e., 60*10), is it?

    The other possibility is that when my app crashes (during debugging), it's causing the problem detailed in this thread: http://forums.sonos.com/showthread.php?t=31804&page=2

    "If you don't unsubscribe to an event (AVT in this case), when quitting your application, the player will try to send data to the subscription URL for a certain time...Now, the odd thing about this is that when this finally fails, it will kill all subscriptions associated with the same IP"

    If that's the problem, any ideas on resolution?


    the 600 is the subscription timeout, however it should re-subscribe after 10 minutes so that shouldn't be a problem. I'm running the same library on a screen here at work and it usually manage through a whole day without loosing the events.

    However, the other problem (where the player kills all subscriptions for a certain IP) has no real workaround without rewriting the library, since my library utilizes unique URLs for each subscription. The official client reuses the same URL, so a re-subscription would "cancel" the subscription kills.

    I have thought about rewriting it from scratch and not rely on a separate UPnP lib, but that would probably change the API drastically. I actually started out that way, but got into trouble with the subscriptions and decided to pass on it. However, since I started with my node.js API I have a better understanding on how it works and it might be easier to implement it this time around.