Let’s Chat: Making Sonos Talk with the audioClip API

Last week we brought out an early, experimental version of audioClip for everyone to try. Using this namespace developers can make Sonos speakers play short clips of sound that won’t end whatever was playing on that speaker at the time. We’ve heard a lot of folks asking for this capability over the past few years and we’re glad to finally be bringing it to you. Some small details may change and there are still some things that we need to implement, but this is a great preview of what the namespace is capable of.

Of course, what good is a new API namespace if we don’t build fun things with it? Personally I’ve always wanted the ability to have my Sonos speak to me. You can imagine the uses: tell your kids on the third floor to come down for dinner. Announce the score of the football game. Say who that latest email is from. Really, the sky’s the limit. So let’s use Google Translate’s free Text-to-Speech (TTS) API, along with this new audioClip namespace, and build ourselves a browser-based Sonos TTS experience.

Getting Started

We’ll need a few things to get started:

  • A set-up and configured Sonos system, obviously. Make sure you can play some content on it.
  • The username and password associated with that Sonos system.
  • A Control Integration, complete with a Client Key and Secret, with the redirect uri set to ‘http://localhost:3001/redirect’.
  • A machine capable of running a node version that supports at least ES7. I’m using node version 8.6.0 on my Mac.
  • Some topical music. Let’s do the obvious thing and put on some Talking Heads.

We’ll also be using a few external npm packages to help us with some app infrastructure. These are things that aren’t important to learn about for this blog post, but that are still required for our final app to run:

  • google-tts-api: This puts a nice, neat, promise-ready wrapper around the Google Text-to-Speech API.
  • simple-oauth2: A handy package to simplify the process of getting and refreshing access tokens.
  • node-persist: Mimics the HTML5 localStorage API used in browsers, so it’s pretty easily understood.

These packages are all installed automatically when we execute our npm install command in the next step.

Preparing the App

In a directory of your choice clone the github repo and cd into the newly-created directory. Type npm install and wait for it to run through the install process. Next, copy the .env.sample file into a new file called simply .env. Edit this new file and fill in your Sonos client id and client secret, obtained from the developer portal. After this you should have everything set up.

I built this app using React to drive the front-end. React is pretty new to me and I’ve really enjoyed learning about how to put such an app together. I had coincidentally just read this blog post by Phil Nash over at the Twilio blog. (As an aside, you should put that blog on your RSS reader of choice. Consistently great content.) The app structure Phil lays out here seemed to meet all of the needs I anticipated for my app. I cloned his repo and used that as a base for Sonos TTS.

App Architecture

The app we’re building here today consists of a front-end App, built in React, and a back end server. The React app makes calls to the back-end server to get data needed for the front-end UX. It also sends text to the back-end to speak. The back end interfaces with the Sonos auth and API servers. It keeps track of the access tokens that get generated during auth. The front-end app has no real idea that it’s working with Sonos. All the “smarts” are consolidated in the back end.

 

We should note that the back-end server we’re building here is completely unsecured. It shouldn’t be run anywhere except on your local machine. It’ll have access to your Sonos household and will store OAuth 2.0 access and refresh tokens.

Running the App

As Phil notes in his blog post you can choose to run this app as separate back end server and front-end processes. This is useful in the case where you do plan to run the server and front-end on different machines or instances. We’re going to run everything locally, so we’ll take advantage of the script Phil made to run both server and front-end simultaneously. Type npm run dev and wait for things to spin up. Your browser should automatically be brought to the foreground and the app will start up.

If this is your first time running the app you’ll immediately be redirected to the Sonos auth servers to log in to your Sonos account. Once you’ve done so you’ll be sent back to the main app screen.

 

In the screen above you can see we’re presented with a list of speakers in our household and a box in which to type the phrase we want the Sonos to say. Go ahead and pick some speakers, type something (might I suggest “Sonos speakers sound great!”?) and see what happens. Hopefully, your Sonos just talked to you.

There are a few things to note here:

  • If you’ve got multiple households associated with your Sonos account, you’ll have an extra select list so you can choose which household to target. You can have multiple households if, for example, you’ve got Sonos set up at both your primary residence and vacation home.
  • Remember above where I said that the audioClip namespace is still experimental? Well, one of the things that isn’t fully baked yet is a capability flag, called AUDIO_CLIP. Using this flag a player indicates its ability to actually play audio clips. Until that flag is available this app will just list all speakers. If the user selects a speaker that can’t play audio clips, the app will return an error. At the time of this writing only the Sonos One and Beam support audio clips.

Now that we’ve built and run the app let’s dig into the details to see how we did it.

Authorizing the App with Sonos

There are a few interesting parts of the code to look at. First let’s examine how we set up simple-oauth2 to work with Sonos. There are two main things we need to configure: the various auth endpoints and API keys and secrets, and the redirect handler. (For a quick refresher on authenticating against Sonos’ OAuth2.0 server, see our docs.)

Luckily for us, simple-oauth2 makes this all, well, simple. They provide a nice set of convenience methods for defining the OAuth2.0 parameters and for providing the authorization URLs.

That second constant, the authorizationUri, is built by the authorizeURL method. It’s really handy because it encapsulates everything that’s important in the initial call to the authorization code endpoint. So a simple redirect to authorizationUri is all that’s needed to kick off the auth flow.

At this point the user is sent to the Sonos authorization site. They’ll log in to their account and read about the permissions your app is asking for. After having granted those permissions, they’re sent back to the redirect URI. That URI was specified when the Control Integration was built on the dev portal, and is handled by our app. The handler for that URI takes the authorization code and exchanges it for an access token via the Sonos auth endpoints.

You can see above the simple-oauth2 method getToken which takes care of all the behind-the-scenes stuff for us. Everything is nice and straightforward since we configured simple-oauth2 at the beginning to plug directly in to Sonos’ auth server. We get the token back and save it, using node-persist, to local storage. That way we don’t have to ask the user to log in every time we restart the app. Now obviously local storage is not how you’d want to persist access tokens in a production app, but this simple method works for our purposes.

Talk To Me

Ok, we’ve got our access token and can now make calls to the Sonos Control API. You’ll note that in the code above, once a token is successfully fetched and saved, we send the user back to localhost:3000 which is the URL for our main app. They’ll see the main app screen, shown above. Behind the scenes the app has called our /households endpoint which gets a list of Sonos households associated with the authorized account:

You can see in the code above that we’ve inserted an Authorization header with our recently-fetched access token.

It’s important to note that the /households endpoint that the front-end app is calling is not the Sonos Control API command, but is to the back-end server we’ve built. Remember, the front-end app doesn’t know anything about Sonos. The back end is taking care of all the calls to Sonos as well as handling all authentication.

I’ve built a little bit of UX goodness in to the app. If there’s only one household available for the account, the household select list is not displayed. This is the case for the vast majority of accounts out there. Once the user picks a household (or the single household has been automatically selected), the app calls our /clipCapableSpeakers endpoint. Again, /clipCapableSpeakers is a custom endpoint we’ve built on our back-end server.

After making a GET /groups call to the Sonos Control API, our back-end sorts through the resulting list of players provided in the response. Normally we’d only select those players that have the AUDIO_CLIP capability flag. However, at the time of this writing, that flag has not been implemented. We’ll return all the players and let the user decide which will work with audio clips.

Now the user can select a speaker and in the text box below type something for the speaker to say. After hitting submit, we finally call our custom /speakText endpoint on our back end. The handler for this endpoint receives the text to speak and the selected speaker id. The first thing it does is call the google tts service to turn that speech text into a URL that will play the spoken text:

We take the returned URL, add it to our request body, and make a POST to /audioClip on the Sonos Control API.

If everything went well here, the user’s speaker just spoke their typed text. Imagine the possibilities!

Wrap Up

We did a few cool things here: we built a simple React app, We actually implemented authorization against the Sonos servers, and we made a few calls to the Sonos Control API. The end result of all this work is that now our speakers can talk to us.

A really neat next step here would be to secure the back-end server side of this code, put it in the cloud, and have your own private Sonos TTS service. You could hook up any kind of front-end to that you want. Maybe some IFTTT Webhooks? That’d go a long way towards implementing the “announce the football score” scenario I noted at the beginning of this post.

Again, you’ll find everything you need at the github repo. Head over and check it out.

Thanks for reading this post, and building a basic Sonos TTS app with us. We’re really excited about all the things developers and partners will do with audioClips.

– Matt Welch – Principal Developer Advocate
Currently listening to C’est La Vie No.2 by Phosphorescent

Put Some Music On: Your First Sonos Platform Calls with Postman

The Sonos Control API has been used to build some really great experiences. These range from big and ambitious, to smaller and more straightforward. But what if you want to start smaller still? What if you just want to dip your toe in, and get a feel for how things fit together? We’ll find out one way to do that in this post.

Postman is a configurable REST client, used to test and experiment with APIs. We’ll use it to set up authentication, and build some Control API calls. Let’s forget the UX and presentation and everything else associated with a full-fledged app. We’re going to focus on nothing but getting the music playing.

If you’re the type that likes to read the last page of a good book, scroll to the bottom, where I’ve included all the work done here in a pre-packaged project for you to use. But you should still read the rest of the post!

Getting Started

First, let’s collect our materials:

  • A set-up and configured Sonos system, obviously. Make sure you can play some content on it.
  • The username and password associated with that Sonos system.
  • A few Sonos Favorites saved and showing up in My Sonos.
  • A Control Integration, complete with a Client Key and Secret.
  • An installed copy of Postman, the REST client.
  • Some good hacking music. I’d suggest something by Matthew E. White.

Fire up Postman and look around. We’ll build the required Environment and Collection to generate Sonos API calls.

Set Up the Environment

Before we get started on actual API calls, we need to set up an Environment for our collection to use. An environment, in Postman, is a collection of variables that any collection has access to. So, for instance, you can use {{auth_url}} instead of api.sonos.com. This lets you use the variable in multiple places, and only change the variable value in the environment definition if that value ever needs to change.

Let’s get our Environment set up. Click on the “Manage Environments” button in the upper right of the Postman window. It looks like a gear icon. Then, click on the big, orange “Add” button. Fill it out such that it looks like this:

 

Let’s go over each of the environment variables we’ve specified.

auth_host – The host that handles Sonos authorization calls.
api_host – The host that handles Sonos Control API calls.
client_key – The client key provided when you create a new Control Integration.
client_secret – The client secret provided when you create a new Control integration.

Once you’ve gotten everything set up as noted above, click on “Update”, then close the Environment window. We’re ready to start actually making calls.

Authorization

The next thing we’re going to want to do is create a new collection. A Postman collection is, well, a collection of related REST calls that can share resources. In our case, we’re going to build the authorization handling right in this collection. We’ll let the REST calls contained inside the collection use this authorization so we don’t have to reconfigure for every call.

Click the “New Collection” button in the left column of the Postman window.

 

Give your collection a name, and a description of you’d like.

 

Click on the “Authorization” tab. This is where we do the first really meaty, interesting part. Before we go any further, it’s a good idea to read over the Authorize section of our documentation. That’ll explain the details behind what we’re doing here, and will help make things more clear when you’re building an actual implementation and don’t simply have form fields to fill in.

On the “Authorization” window, select “OAuth 2.0” for the type, and “Request Header” for “Add auth data to”. As noted in our documentation, we employ a standard OAuth 2.0 3-legged OAuth flow, and require that the acquired token is included in request headers.

 

Now we’re going to click on the big orange “Get New Access Token” button on the right side of the window. Here, we’ll fill in the information required to actually get an access token. Fill in everything, so that it looks like this:

 

You’ll note a bunch of orange values, surrounded by double curly brackets. Those are the Postman environment variables, which you set up in the previous section.

Let’s run through these fields, one at a time.

Token Name – You can use whatever you want. This is just so that you can remember the token you fetched. I just leave it at the default
Grant Type – Sonos employs an Authorization Code flow, so you should select that here.
Callback URL – This is the URL that is called when the Sonos auth servers want to send the user back to your integration. This callback URL should be prepared to receive data involved in the auth flow, as described in our Authorization docs. This Callback Url must match the redirect uri you specified when you created your Control Integration on the developer portal. The “http://localhost” value included in this screenshot is for my specific integration, and should be changed to match your setup. Note that, while Postman calls it a Callback URL, it is more commonly referred to as a redirect uri. Sonos uses this name, and in fact has a separate, unrelated Control Integration parameter called “Callback URL” which is for player eventing.
Auth URL – This is the URL that an integration will call to get the initial Authorization Code
Access Token URL – An integration will call this URL, with the authorization code, to get access and refresh tokens.
Client ID – The client ID that was generated for you when you created your Control integration.
Client Secret – The client secret that was generated for you when you created your Control integration
Scope – The types of abilities your integration is requesting on behalf of the user. At the time of this writing, the only available scope for Sonos is “playback-control-all”.
State – A value built in the OAuth spec that is used to prevent cross-site forgeries. The {{$guid}} value in the screenshot is a random value generator (more or less) provided by Postman.

Once this is all filled out, click on the big orange “Request Token” button. This will initiate an actual OAuth flow against the Sonos servers, exactly as a user would see inside an actual integration. You’ll be directed to the Sonos login page, told a bit about how to prepare your Sonos system to integrate with third parties, and then be prompted for your username and password.

 

Once you’ve successfully supplied your credentials, you’ll be told about the capabilities the integration is asking for. These are defined by the “scope” parameter in your Authorization calls.

 

Once you click “OK” here, the user is sent back to the redirect/callback URI specified above. Normally, your hypothetical integration would take over at this point, getting the authorization code, then exchanging it for the access token. In this case, though, Postman scrapes the redirect call, gathers the required data, and completes the OAuth flow itself, eventually presenting you with a token that you can choose to use.

Congratulations! You’ve successfully authenticated against the Sonos platform!

Making Your First Call

Now that Postman has the authentication information the Sonos requires for any API call, we can start building those calls. Before we can get to the really fun stuff, like music playback, we have to understand a bit about the user’s system. The user, in this case, is whoever logged in to the Sonos authorization page in the section above. We’ll want to get a list of available households first. Usually, there will be only a single household per user, but it’s quite possible for a user to have more than one. In either case, we need to get a householdId to use to make further calls. You can read a more high level description of this flow in the “Discover” section of our documentation.

Click on the ellipsis (“…”) button on the bottom right of your collection in Postman, then select “Add Folder”. I like to create folders for each namespace in the Sonos API, so I’ll call this one “households”. On the description tab, enter “households” as the Name.

Next select the Authorization tab. This is where Postman really comes in handy. Instead of rebuilding our authorization flow from scratch, we’ll simply select “Inherit Auth from parent” for the type.

Click the orange “Create” button to create the folder and to close this window. Now, let’s add our first request. Click the ellipsis button on the “households” folder. Sometimes you need to hover the mouse over the folder name for this menu to appear. Then, click the “Add Request” menu item. Let’s call this one “households”, since we’ll be doing a simple GET /households request.

You’ll notice that when you save it, it is conveniently labeled right inside the parent folder, with the http method (GET, in this case) helping us identify it. Select the request in the column on the left, and notice that request details page that becomes visible. This is where we’ll specify the command endpoint, as well as any body elements that are required. Fill out the request details as noted below

Notice again that we’re using environment variables, this time {{api_host}}, to build our request. Note also that we’ve once again specified that the request should inherit its auth from its parent. Auth flow from the top level collection is being passed down to the “households” folder, then down to this GET /households request.

For a simple GET /households request, this is all that’s required, so let’s make our very first call! Click on the big blue “Send” button. You should get back a JSON body that looks something like this:

There you go! You’ve got your list of Sonos households available to the authenticated user. Note the household id returned above, as we’ll need that for the next section. If you’ve got more than one household available, just pick one of the ids returned.

Finding Out What Groups and Players are Available

Most commands on the Sonos Control API require either the householdId or the targeted groupId. We’ve got the hhid, so let’s make a call and get a target groupId. We’ll build a GET /groups request to return what we’re looking for. Just as above, we’ll make a new folder, this time for the “groups” namespace, and then a new request inside that new folder for the actual GET /groups call. To summarize:

  1. Create a new “groups” folder.
  2. Create a new “groups” command.
  3. Set the auth type to “inherit”, and enter the URL as such: https://{{api_host}}/control/api/v1/households/<< householdId from above >>/groups

If you click “Send” now, you should get back a groups response object that contains all of the players in the household, as well as a listing of how those players are currently grouped. It’ll look something like this

Just like before, we’re going to want to grab a groupId from one of these, so pick a group that looks interesting from the groups object in the response. Note the associated groupId. In the example above, there are two groups returned, so I might choose the “Office Speakers + 1” group, with the RINCON_11111:12345 id.

Get the Sonos Favorites

So we’ve gotten all the boring housekeeping out of the way; now it’s time to actually make our Sonos speakers do something. Let’s put a Sonos Favorite on the group you just selected. If you haven’t done so yet, make sure you’ve added something to “My Sonos”, which is the Sonos app container for favorites and playlists. We’ll use the favorites namespace to look for saved favorites, then play one back.

The first thing we’ll want to do is execute a GET /favorites command. You know the drill at this point:

  1. Create a new “favorites” folder underneath the top level collection.
  2. Create a new “favorite” command.
  3. Set the auth type to “inherit”, and enter the URL as such: https://{{api_host}}/control/api/v1/households/<< householdId from above >>/favorites

That’s it. If you hit “Send” now, you should get back a JSON body containing all of the Sonos Favorites you’ve saved. It should look something like this:

Pick your favorite Favorite, and note the id. We’ll be using that in the next section.

Finally, Let’s Play Some Music

We’ve gotten the household id. We’ve used that to get the list of available groups, and then picked a group id. We’ve gotten a favorite id. Not let’s put it all together and play that favorite on that group.

Inside the existing “favorites” folder, let’s create a new request, and call it “loadFavorites”. Have it inherit its parent’s auth, of course. But here’s where things get a little different. The first thing we’ll do is change the HTTP method from the default GET to POST. This means we’re sending data to the request endpoint instead of just asking for data. Check out the following screenshot of how you should get things configured.

You’ll note that this time, we’ve included a request body by clicking on the “Body” tab in the request. The JSON in that body tells Sonos which favorite id to play, and how to queue it up. Obviously, if the favorite you’ve selected is a streaming radio station, setting the play mode shuffle to TRUE doesn’t make any sense, and is ignored.

Hitting “Send” on this request should result in your selected Sonos Favorite playing on your selected Sonos group. Congratulations! You’ve built your first Sonos app. Of course, there’s no UX around it, and you’re doing all the heavy lifting instead of letting code do it, but make no mistake, this is an integration.

Wrap Up

We’ve learned here how to use Postman to authenticate against Sonos’ servers, and how to make requests to find out about the state of a user’s system. And finally, we actually made music play. From here, some great next steps would be to implement more commands, like volume or playback control, inside Postman. Get a feel for how things fit together.

A couple notes:

  1. Obviously, you can’t subscribe to any eventing with Postman.
  2. For whatever reason, Postman doesn’t implement support for refresh tokens, so you’ll have to go back in to the top collection’s Authorization screen and request a new token every 24 hours.

And finally, here’s a special treat. Clicking this button will import a new Postman Collection and Environment, all set up with a number of Sonos namespaces. In addition, I’ve created a number of post-request scripts that will parse the results, and do things like automatically populate groupIds into calls. All you need to do is open the enclosed Environment and set “target_player” to the name of a player in your household, and add your client key and secret. After that, every time you run the GET /groups command, Postman will look for your player in the response, find its current group, and use that groupId in other requests, like loadFavorite.

Thanks for reading, and we’re so excited that you’re building great things with us.

Debugging Your Cloud Queue with Wireshark

You’ve read our documentation and built a fantastic cloud queue implementation. You’ve got your contexts and queue versions all set up. You fire up the cloud queue (CQ) server, point loadCloudQueue at it, and… nothing. The music doesn’t play. What next?

The first step to seeing what’s going on is to understand what the player is asking for from the cloud queue server, and what the server is telling it. Now, you could build all kinds of logging in to your server (you should probably be doing that anyway!), but it’s often a very iterative, time consuming process to arrive at exactly what kind of logging you need. And even then, it’s difficult to display all logged data in a way that’s easily digestible.

Enter Wireshark.

Wireshark is a robust app, but with that extensive capability comes some complexity, at least for those of us who aren’t networking savants. But if we can arrive at the right settings, Wireshark is a fantastic tool for looking at cloud queue traffic.

Important Caveat

The techniques we’re about to talk about here will be really valuable for debugging cloud queue implementations, but they’re predicated on observing traffic over insecure HTTP. If you try to observe the traffic over secure connections, you can’t see what’s being sent and received. So: develop on insecure endpoints, deploy to production after you’ve debugged, and with secure endpoints.

Hardware Setup

Wireshark can most easily examine network traffic to and from the local machine, so you’ll have to run your cloud queue server locally. There are some tricks we can employ that’ll let us look at traffic between a player and a remote server. We’ll look at those a bit later.

I’ve got our cloud queue sample server running on my dev machine, along with Wireshark. This machine is connected via Wi-Fi to the same network as my Play:5. Hardwired Ethernet would be fine, too.

Example Sonos Player and Computer IP Addresses

Once we’ve gotten the IP addresses for the two devices, we can begin to configure Wireshark to examine CQ traffic. I’ll leave it to you to get the IP address of your computer, but to get that of your target Sonos speaker, simply open the “About My Sonos System” section of the Sonos App.

Wireshark Setup

In the above setup, we can see that the Sonos player is at IP address 192.168.55.190. That means we’ll want to tell Wireshark to look at all traffic to and from this address. To do this, we want to “Capture …using this filter” on the opening screen of Wireshark.

Wireshark Capture Settings

First, select the network interface you’ve connected to your Wi-Fi network. It’s usually pretty easy to figure out which one to select, as the active interface will have a small activity waveform after its entry in the list.

In the filter text field, we’ve specified that we want to examine traffic from a “host” with an IP address of 192.168.55.190. If we wanted to only examine traffic in one direction, we could prepend the above input with either “src” or “dest”. We want it all, so we’ll leave those off.

Once you’ve got everything the way you want it, hit “Enter” to be taken to the Wireshark Capture screen.

Wireshark Capture

We’re capturing cloud queue packets between the player and the server! That’s great, but the problem is, we’re capturing a bunch of other, distracting stuff as well. (This stuff is useful information, but not when you’re looking to debug your CQ server.) Things are just really hard to sort, so we’re going to apply a display filter.

First, let’s talk about what we’re interested in capturing. We know that the cloud queue server receives HTTP callouts from the player, things like GET /itemWindow or POST /timePlayed. And we know the CQ server responds with JSON bodies. So those are the two things we’re really interested in here. Luckily Wireshark has some display filters that meet our needs.

Let’s start with our first type of captured data: HTTP requests from the player. It turns out that Wireshark has a display filter called, simply, http. We could just use that, and we’d get all the HTTP callouts, both from the player and from the CQ server (though we know the server won’t be making any callouts). The problem with simply using http by itself is that the player makes a lot of other HTTP callouts that are unrelated to cloud queue activity. All this extra information clutters our results.

So let’s drill in a bit on that http display filter. It turns out we can do things like look at the URL, or even the path. That last part is interesting to us. As I noted, I’m using our CQ sample server, which has a default path of http://ip.address/musicqueue/1.0. We can reference that “musicqueue” part when we specify an HTTP URL path to examine.

The second type of data we’re looking for is the JSON responses from the CQ server to the player. You won’t be surprised to find out that Wireshark has a json display filter. It also has a bunch of neat filters, like the ability to filter by keys. We pretty much want all the JSON data going back and forth between our two devices under observation, so we’ll just stick with json.

Putting the two together, using the boolean operator support in Wireshark, we come up with something like http.url.path contains "musicqueue" || json. Once we put that in the display filters text box, our traffic becomes more readable.

Examining Cloud Queue Traffic

Taking a look at the example image below, the first thing we can see is an HTTP callout, as noted in the “Protocol” column. This callout is from 192.168.55.190, our player IP address, to 192.168.55.174, our CQ server. Looking at the “Info” column, you can see that it was a callout to the GET /itemWindow endpoint, with various itemWindow request variables, like itemId, included as URL parameters. You can click on the image to open a bigger version in a new window.

Cloud Queue Traffic Between Server and Player

The very next line is the response from the server. This request/response relationship is noted by Wireshark in the far left column with the light grey outgoing and incoming arrows. If you click on the response entry, as I’ve done in the image, you can examine specifics about the HTTP response. What we’re interested in is the JSON response body, which I’ve expanded. In there, you can see many of the expected GET /itemWindow response values, like our items array, and the individual item objects inside.

Congratulations! Now go fix that cloud queue bug, and bring the joy of music to your listeners’ homes.

Cloud Queue Debugging Extra Credit

Remember above where I mentioned that we were limited to running the CQ server on the same machine as Wireshark, due to the fact that only traffic to and from the local machine is visible on a network interface? Well, that’s not exactly true. There’s a technique we can use to examine traffic between a player and a remote server. Doing so requires just a little bit of network understanding.

Modern networks are switched, which means that a network interface (like the ethernet port on your computer) will only get traffic that is meant for that IP address. Stuff happening elsewhere never makes it to that ethernet port. Routers and switches take care of all that stuff for us, and our networks are vastly better off for their efforts.

But we want ALL the traffic, right? It turns out, we can get it all. Back in the early days of networking, a lot of intranets were built with simple ethernet “hubs”, which basically just broadcast anything coming into the hub out to everything connected to the hub. Packet chaos, sure, but a “feature” that we can exploit, if we configure our network correctly.

Both the Sonos player you want to examine and the computer running Wireshark will need to be wired via an Ethernet connection to the hub. Your setup will look something like the image below.

Wireshark “Spying” Hardware Setup

As you can see, the player and the CQ server are free to communicate as usual. The difference is that the Wireshark machine is able to “spy” on all that traffic. To do that, we’ll want to set up Wireshark to gather the appropriate communications. Using what we learned, above, we can either target all traffic coming from and going to the player, or the server. In this case, it’s actually easier to target the traffic to and from the server, as it’s pretty certain that we’re interested in all of it.

So we set up the Capture page in Wireshark with “host server.ip.address.or.hostname”. Below, I’ve substituted the fake IP address “w.x.y.z” for the real one I was using. (Security through obscurity!)

Wireshark “Spying” Capture Setup

Once we’ve got this set up, and then apply our display filters on the capture page, we can easily see traffic between a cloud queue server and a player.

Wireshark “Spying” Capture

Wrap Up

Wireshark can be a valuable tool in your Sonos Platform dev toolkit. It provides the ability to not just see data in context, but to save an entire capture to a Wireshark format to distribute for education or support purposes.