Fully managed C# library

  • 4 January 2013
  • 27 replies
  • 23950 views

Userlevel 4
Badge +14
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.

27 replies

Userlevel 4
Badge +14
On the Todo-list:


  • Smarter Topology change subscription (less chatty)
  • Unsubscribe upon Dispose() (Add IDisposable)
  • Better Player status check
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?
Userlevel 4
Badge +14
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.
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.
Userlevel 2
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.
Badge
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
Userlevel 4
Badge +14

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.
Badge
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!
Badge
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?
Userlevel 4
Badge +14
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.
Badge
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.


Thanks for the info. I suppose the question, then, is how do I properly unsubscribe when my app exits? Is there a public function that unsubscribes or do I just need to properly dispose of the player (or some other) object?
Userlevel 4
Badge +14
Thanks for the info. I suppose the question, then, is how do I properly unsubscribe when my app exits? Is there a public function that unsubscribes or do I just need to properly dispose of the player (or some other) object?

Well, you can't 🙂 I couldn't figure out how to reference the actual subscription for unsubcribing to it. I planned on using a IDisposable implementation and a finalizer to prevent this, but still haven't figured out how to do it.

I didn't have the need for it personally, so I never bothered. I can look at it once again.
Userlevel 2
Hi Jishi,

Appreciate your work on this library - no chance I would have been able to get anything done at my end without it.

I'm having a bit of an issue though - my simple app needs to keep track of two separate players. If I monitor one player, it seems to work fine, but as soon as I'm monitoring two, I get wierdness (empty TransportState and CurrentTrackMetaData on both channels)

Is there any tricks to getting it working against multiple controllers - I notice your sample app only connects to a single one?

Thanks

Kane
Userlevel 4
Badge +14
Hi Jishi,

Appreciate your work on this library - no chance I would have been able to get anything done at my end without it.

I'm having a bit of an issue though - my simple app needs to keep track of two separate players. If I monitor one player, it seems to work fine, but as soon as I'm monitoring two, I get wierdness (empty TransportState and CurrentTrackMetaData on both channels)

Is there any tricks to getting it working against multiple controllers - I notice your sample app only connects to a single one?

Thanks

Kane


Hi, I'm not sure about the issue you are having. When connecting to the system, it should subscribe to all players and also recieve notifications from all players, which means that it should keep state of all of them.

Could you explain what you are doing in depth?
Userlevel 2
Hi, I'm not sure about the issue you are having. When connecting to the system, it should subscribe to all players and also recieve notifications from all players, which means that it should keep state of all of them.

Could you explain what you are doing in depth?


On further investigation, I think it's possible that some of my problems may be due to something being wrong with one of my devices - it drops off every now and then, and I am thinking my issues may have coincided with when it dropped off.

Have a support request in on this, so will hopefully get some clarity (just need to get it to drop out now, as all of a sudden it seems to be behaving!), and then once that's sorted I'll do more testing on my software side of things.

Thanks!
Badge
Jishi-

I can't thank you enough for all of the hard work you put into this library. I have been using it for the past 8 months or so with mostly good results. I have it integrated into a much more extensive server app that I wrote that controls various things in my house, including my Sonos.

I spent a lot of time debugging performance issues with my server app and have isolated the issue to this Sonos library.

I started a new project that ONLY imports the library and does the following:
sonosDiscoverer = New SonosDiscovery
sonosDiscoverer.StartScan()

I have noticed that my app continually receives a large amount of UDP data... it seems like it is constantly querying the other UPNP devices on my network. I dug into your Sonos library code a little bit (although I know very little about UPNP or C#) to try to figure out a way to "stop" the scan in the "onDeviceAdded" callback for the UPnPSmartControlPoint ("ControlPoint").

I couldn't figure out a way to do it. Is there some way to make it STOP scanning for new devices after it finds my Sonos player? Am I misunderstanding what it is doing? When I view the network traffic from the process using procmon I see a huge amount of UDP traffic from other UPNP devices on my network (Windows Media Center and Extenders). I'm running this on an atom CPU and it causes the app to use between 10-25% of the CPU. Functionally, it works well, but it bogs down the CPU way too much.

Can you help me out or point me in the right direction?

Here's a representative example of a few of the procmon entries:

code:
9:24:09.2769313 PM	HomeControllerServer2.vshost.exe	20136	TCP Send	Hauto.home:62893 -> MediaCenter:icslap	SUCCESS	Length: 128, startime: 790903, endtime: 790903, seqnum: 0, connid: 0
9:24:09.2769563 PM HomeControllerServer2.vshost.exe 20136 TCP Receive Hauto.home:62893 -> MediaCenter:icslap SUCCESS Length: 190, seqnum: 0, connid: 0
9:24:09.2769648 PM HomeControllerServer2.vshost.exe 20136 TCP Receive Hauto.home:62893 -> MediaCenter:icslap SUCCESS Length: 1440, seqnum: 0, connid: 0
9:24:09.2772874 PM HomeControllerServer2.vshost.exe 20136 TCP Receive Hauto.home:62893 -> MediaCenter:icslap SUCCESS Length: 1440, seqnum: 0, connid: 0
9:24:09.2773004 PM HomeControllerServer2.vshost.exe 20136 TCP Receive Hauto.home:62893 -> MediaCenter:icslap SUCCESS Length: 99, seqnum: 0, connid: 0
9:24:09.2773226 PM HomeControllerServer2.vshost.exe 20136 TCP Send Hauto.home:62894 -> MediaCenter:icslap SUCCESS Length: 128, startime: 790903, endtime: 790903, seqnum: 0, connid: 0
9:24:09.2773295 PM HomeControllerServer2.vshost.exe 20136 TCP Receive Hauto.home:62894 -> MediaCenter:icslap SUCCESS Length: 205, seqnum: 0, connid: 0
9:24:09.2773465 PM HomeControllerServer2.vshost.exe 20136 TCP Receive Hauto.home:62894 -> MediaCenter:icslap SUCCESS Length: 1440, seqnum: 0, connid: 0
9:24:09.2776708 PM HomeControllerServer2.vshost.exe 20136 TCP Receive Hauto.home:62894 -> MediaCenter:icslap SUCCESS Length: 1440, seqnum: 0, connid: 0
9:24:09.2776907 PM HomeControllerServer2.vshost.exe 20136 TCP Receive Hauto.home:62894 -> MediaCenter:icslap SUCCESS Length: 1440, seqnum: 0, connid: 0
9:24:09.2776975 PM HomeControllerServer2.vshost.exe 20136 TCP Receive Hauto.home:62894 -> MediaCenter:icslap SUCCESS Length: 1440, seqnum: 0, connid: 0
9:24:09.2779922 PM HomeControllerServer2.vshost.exe 20136 TCP Receive Hauto.home:62894 -> MediaCenter:icslap SUCCESS Length: 603, seqnum: 0, connid: 0
9:24:09.3090135 PM HomeControllerServer2.vshost.exe 20136 UDP Receive Hauto.home:56296 -> HDHR-1320159B.home:ssdp SUCCESS Length: 278, seqnum: 0, connid: 0
9:24:09.3111922 PM HomeControllerServer2.vshost.exe 20136 UDP Receive Hauto.home:56296 -> HDHR-13201348.home:ssdp SUCCESS Length: 278, seqnum: 0, connid: 0
9:24:09.3139335 PM HomeControllerServer2.vshost.exe 20136 TCP Connect Hauto.home:62895 -> DEL0015995DF2AA:http SUCCESS Length: 0, mss: 1440, sackopt: 1, tsopt: 0, wsopt: 1, rcvwin: 66240, rcvwinscale: 8, sndwinscale: 0, seqnum: 0, connid: 0
9:24:09.3174199 PM HomeControllerServer2.vshost.exe 20136 TCP Receive Hauto.home:62879 -> 0005CD35CE19.home:8080 SUCCESS Length: 817, seqnum: 0, connid: 0
9:24:09.3182590 PM HomeControllerServer2.vshost.exe 20136 TCP Send Hauto.home:62895 -> DEL0015995DF2AA:http SUCCESS Length: 76, startime: 790903, endtime: 790903, seqnum: 0, connid: 0
9:24:09.3233376 PM HomeControllerServer2.vshost.exe 20136 TCP Receive Hauto.home:62895 -> DEL0015995DF2AA:http SUCCESS Length: 24, seqnum: 0, connid: 0
9:24:09.3242665 PM HomeControllerServer2.vshost.exe 20136 TCP Receive Hauto.home:62895 -> DEL0015995DF2AA:http SUCCESS Length: 354, seqnum: 0, connid: 0
9:24:09.3250879 PM HomeControllerServer2.vshost.exe 20136 TCP Disconnect Hauto.home:62895 -> DEL0015995DF2AA:http SUCCESS Length: 0, seqnum: 0, connid: 0
9:24:09.4026198 PM HomeControllerServer2.vshost.exe 20136 UDP Receive Hauto.home:56296 -> MediaCenter.home:ssdp SUCCESS Length: 397, seqnum: 0, connid: 0
9:24:09.4094544 PM HomeControllerServer2.vshost.exe 20136 TCP Connect Hauto.home:62896 -> MediaCenter.home:icslap SUCCESS Length: 0, mss: 1460, sackopt: 1, tsopt: 0, wsopt: 1, rcvwin: 65700, rcvwinscale: 8, sndwinscale: 8, seqnum: 0, connid: 0
9:24:09.4103316 PM HomeControllerServer2.vshost.exe 20136 TCP Send Hauto.home:62896 -> MediaCenter.home:icslap SUCCESS Length: 139, startime: 790904, endtime: 790904, seqnum: 0, connid: 0
9:24:09.4103578 PM HomeControllerServer2.vshost.exe 20136 TCP Receive Hauto.home:62896 -> MediaCenter.home:icslap SUCCESS Length: 205, seqnum: 0, connid: 0
9:24:09.4105961 PM HomeControllerServer2.vshost.exe 20136 TCP Receive Hauto.home:62896 -> MediaCenter.home:icslap SUCCESS Length: 1460, seqnum: 0, connid: 0
9:24:09.4106268 PM HomeControllerServer2.vshost.exe 20136 TCP Receive Hauto.home:62896 -> MediaCenter.home:icslap SUCCESS Length: 2395, seqnum: 0, connid: 0
9:24:09.5751703 PM HomeControllerServer2.vshost.exe 20136 UDP Receive Hauto.home:56296 -> HDHR-13201348.home:ssdp SUCCESS Length: 278, seqnum: 0, connid: 0
9:24:09.5769604 PM HomeControllerServer2.vshost.exe 20136 TCP Connect Hauto.home:62897 -> HDHR-13201348.home:http SUCCESS Length: 0, mss: 1460, sackopt: 0, tsopt: 0, wsopt: 1, rcvwin: 65700, rcvwinscale: 8, sndwinscale: 0, seqnum: 0, connid: 0
9:24:09.5775259 PM HomeControllerServer2.vshost.exe 20136 TCP Send Hauto.home:62897 -> HDHR-13201348.home:http SUCCESS Length: 53, startime: 790906, endtime: 790906, seqnum: 0, connid: 0
9:24:09.5775532 PM HomeControllerServer2.vshost.exe 20136 TCP Receive Hauto.home:62897 -> HDHR-13201348.home:http SUCCESS Length: 214, seqnum: 0, connid: 0
9:24:09.5781084 PM HomeControllerServer2.vshost.exe 20136 TCP Receive Hauto.home:62897 -> HDHR-13201348.home:http SUCCESS Length: 312, seqnum: 0, connid: 0
9:24:09.5781226 PM HomeControllerServer2.vshost.exe 20136 TCP Receive Hauto.home:62897 -> HDHR-13201348.home:http SUCCESS Length: 19, seqnum: 0, connid: 0
9:24:09.5782420 PM HomeControllerServer2.vshost.exe 20136 TCP Receive Hauto.home:62897 -> HDHR-13201348.home:http SUCCESS Length: 257, seqnum: 0, connid: 0
9:24:09.5783046 PM HomeControllerServer2.vshost.exe 20136 TCP Receive Hauto.home:62897 -> HDHR-13201348.home:http SUCCESS Length: 94, seqnum: 0, connid: 0
Userlevel 4
Badge +14
Hi tad.

Unfortunately it is the underlying intel SDK that is responsible for this kind of traffic. I'm kind of surprised that it generates that much traffic, because scanning the network for new devices shouldn't be happening that often.

You could try and update to the latest upnp_av.dll from here: http://opentools.homeip.net/dev-tools-for-upnp

There has been a few updates since I built this, maybe it is something they have addressed.

I have also resumed some work that I did before I based it on the intel SDK. Since it is possible to build a more lightweight implementation if only taking Sonos into account, I wanted to use this approach when building my StreamToSonos implementation. It will not be a compatible replacement, but hopefully easy enough to switch over to if needed. You can check it out here (it doesn't have it's own repo yet): https://github.com/jishi/Jishi.StreamToSonos
Badge
Hi tad.

Unfortunately it is the underlying intel SDK that is responsible for this kind of traffic. I'm kind of surprised that it generates that much traffic, because scanning the network for new devices shouldn't be happening that often.

You could try and update to the latest upnp_av.dll from here: http://opentools.homeip.net/dev-tools-for-upnp

There has been a few updates since I built this, maybe it is something they have addressed.

I have also resumed some work that I did before I based it on the intel SDK. Since it is possible to build a more lightweight implementation if only taking Sonos into account, I wanted to use this approach when building my StreamToSonos implementation. It will not be a compatible replacement, but hopefully easy enough to switch over to if needed. You can check it out here (it doesn't have it's own repo yet): https://github.com/jishi/Jishi.StreamToSonos


Jishi-

I was afraid of something like that. What's weird is that this traffic continues throughout the duration that my program is running. There's no way to get it to stop scanning? It looks like it's getting a ton of traffic from my network-connected TV tuners.

I tried updating to the latest build of the upnp_av.dll as you suggested, but that didn't help with the issue.

I may try switching over to the implementation you used in StreamToSonos, I just hate to move away from something that is otherwise working so well!
Userlevel 4
Badge +14
Do note that the framework that is used for StreamToSonos lacks a lot of features. I only implemented the bare minimum required to make the streaming work. Although, the main stuff is there, so it is more a matter of adding the correct SOAP calls to support more features.
Badge
Do note that the framework that is used for StreamToSonos lacks a lot of features. I only implemented the bare minimum required to make the streaming work. Although, the main stuff is there, so it is more a matter of adding the correct SOAP calls to support more features.

I see that. I'm afraid that migrating this over to do what I need is a little beyond my comprehension. It doesn't look like you implement the callbacks to get Sonos device status (now playing metadata) like you do in the managed library - is that correct?

I've done a bunch more investigating with the Intel UPNP tools - I get the same symptoms when I run any of their tools. CPU usage shoots up to 30-50%, approximately 100MB of ram used, and around 3000-4000 network events per minute shown in procmon. I'd be very interested in knowing whether other people are seeing similar behavior. Is this just the nature of UPNP? Or is there something wrong with the UPNP devices on my network (about a dozen).

There has to be a more lightweight-way to talk to Sonos. Is it possible to feed the UPNP library the IP of the Sonos unit and avoid all of the UPNP scanning? At the heart of it, isn't it just SOAP requests to issue commands to the Sonos and HTTP callbacks made from the Sonos unit back to the app?

Edit:

The plot thickens. I'm not sure what all of the "network activity" in procmon is actually showing. When I fire up Wireshark, I see what I think is a perfectly reasonable amount of SSDP and UDP traffic for a network with a dozen UPNP devices - a SSDP notify packet every second or so for each device and about the same for UDP packets. There are a bunch of HTTP packets when I first launch the app and do a .startScan, but these look very normal as well.

So, I really have no idea what is causing the upnp library to consume so much CPU and launch so many threads.
Userlevel 2
Hi JISHI,

I have just bought my first Sonos device, and I'm planning to use your library with my Netduino to display the current track info on a small screen/LCD.

My issue is I really just need the current track playing in each zone, but that does not seem to be implemented in your library.

SonosPlayer.cs (line 190):
code:
        public SonosTrack CurrentTrack
{
get { throw new NotImplementedException(); }
}


I would like to be able to get the info from the queue, but just for the current track.

Info from queue:
code:
sonosZone.Coordinator.GetQueue().FirstOrDefault().DIDL.Title;


Can you hint me in the right direction?
As I'm a hobby programmer with no UPnP experience, I'm in a bit over my head 🙂
Userlevel 4
Badge +14
Hi JISHI,

I have just bought my first Sonos device, and I'm planning to use your library with my Netduino to display the current track info on a small screen/LCD.

My issue is I really just need the current track playing in each zone, but that does not seem to be implemented in your library.

SonosPlayer.cs (line 190):
code:
        public SonosTrack CurrentTrack
{
get { throw new NotImplementedException(); }
}


I would like to be able to get the info from the queue, but just for the current track.

Info from queue:
code:
sonosZone.Coordinator.GetQueue().FirstOrDefault().DIDL.Title;


Can you hint me in the right direction?
As I'm a hobby programmer with no UPnP experience, I'm in a bit over my head :)


Hi, interesting hardware. Are you saying that this code actually runs on netmf? That's a bit surprising.

I do actually have support for the CurrentTrack, you are just looking in the wrong place. Check the CurrentState property, which is of the type PlayerState, that object will hold info regarding CurrentTrack.

In order to update your display, you can also subscribe to the StateChanged event of the player.

I have been planning on breaking out the barebone UPnP-project from StreamToSonos into a separate project, but just haven't gotten around to it. It would be a lot more lightweight. However, netmf doesn't have the regular SOAP libraries available, so it would need to be rewritten for netmf. Are you up for that? 🙂
Userlevel 2
Hi, interesting hardware. Are you saying that this code actually runs on netmf? That's a bit surprising.
I haven't tried it yet, I'm just testing it in a console application. My Netduino is currently occupied elsewhere 🙂 But I will test it soon.


I do actually have support for the CurrentTrack, you are just looking in the wrong place. Check the CurrentState property, which is of the type PlayerState, that object will hold info regarding CurrentTrack.

When I do that it is always "null". Am I missing something?

My Program.cs:
code:
using Jishi.Intel.SonosUPnP;
using System;
using System.Diagnostics;
using System.Threading;

namespace SonosTest
{
class Program
{
static void Main(string[] args)
{
SonosDiscovery disc = new SonosDiscovery();

disc.StartScan();
Thread.Sleep(5000);

Debug.Print("*** PLAYERS ***");

foreach (SonosPlayer sp in disc.Players)
{
Debug.Print("Name: " + sp.Name);
Debug.Print("Track: " + sp.CurrentState.CurrentTrack);
}

Console.ReadKey();
}
}
}


Output every time I run it:
code:
*** PLAYERS ***
Name: Køkken
Track:



In order to update your display, you can also subscribe to the StateChanged event of the player.

Great input!
I think I will update the play time every second or so in my final application. For now everything is just proof of concept.


I have been planning on breaking out the barebone UPnP-project from StreamToSonos into a separate project, but just haven't gotten around to it. It would be a lot more lightweight. However, netmf doesn't have the regular SOAP libraries available, so it would need to be rewritten for netmf. Are you up for that? :)

I would love to have that barebone project to fiddle with :)
As you might have seen from my questions so far I'm not that experienced, and I'm not sure I'm the right person for a rewrite.
But I will most certainly give it a try, and if I'm lucky someone more experienced over at the Netduino forums will see the need for this to happen 🙂
Userlevel 4
Badge +14
I haven't tried it yet, I'm just testing it in a console application. My Netduino is currently occupied elsewhere 🙂 But I will test it soon.


When I do that it is always "null". Am I missing something?

My Program.cs:
code:
using Jishi.Intel.SonosUPnP;
using System;
using System.Diagnostics;
using System.Threading;

namespace SonosTest
{
class Program
{
static void Main(string[] args)
{
SonosDiscovery disc = new SonosDiscovery();

disc.StartScan();
Thread.Sleep(5000);

Debug.Print("*** PLAYERS ***");

foreach (SonosPlayer sp in disc.Players)
{
Debug.Print("Name: " + sp.Name);
Debug.Print("Track: " + sp.CurrentState.CurrentTrack);
}

Console.ReadKey();
}
}
}


Output every time I run it:
code:
*** PLAYERS ***
Name: Køkken
Track:



Great input!
I think I will update the play time every second or so in my final application. For now everything is just proof of concept.


I would love to have that barebone project to fiddle with :)
As you might have seen from my questions so far I'm not that experienced, and I'm not sure I'm the right person for a rewrite.
But I will most certainly give it a try, and if I'm lucky someone more experienced over at the Netduino forums will see the need for this to happen :)


Sorry, my bad. Was a long time ago since I used this. I looked at my old Dashboard code, and apparently, I did this:

code:

var state = player.CurrentState;
if (state == null || state.CurrentTrackMetaData == null) return;
SonosDIDL didlData = SonosDIDL.Parse(state.CurrentTrackMetaData).First();


didlData will have .Artist, .Title and stuff like that. Hopefully that helps.
Userlevel 2
Sorry, my bad. Was a long time ago since I used this. I looked at my old Dashboard code, and apparently, I did this:

code:

var state = player.CurrentState;
if (state == null || state.CurrentTrackMetaData == null) return;
SonosDIDL didlData = SonosDIDL.Parse(state.CurrentTrackMetaData).First();


didlData will have .Artist, .Title and stuff like that. Hopefully that helps.

It works great now!
Thank you for all the help :)

Please let me know when you have taken the bare bones out of your project, and I will dig into it.