Bose SoundTouch Webservices API

Our family Christmas gift this year was a Bose SoundTouch 10 wireless music system. This thing is great. The setup was very simple. Using it on a daily basis cannot be more user friendly. This all in one system connects to the Internet and streams music like a boss. I like the 6 preset buttons and the ability to control which is playing with a remote or my iPhone. Often, I use the SoundTouch while watching movies, streaming the audio over Bluetooth.

There is one feature with the SoundTouch that I find is missing. I would like to interface with the device using my laptop and a web browser. Before purchasing the SoundTouch, we were using Logitech Media Server to stream our music. It has an iPhone app and a web interface. I looked but I did not find a web interface for the SoundTouch.

Luckily, the SoundTouch device has a web services API. Developers can build their own web interfaces for this device. To get the developer documentation, browse this link and follow the instructions on the page. The documentation is easy to understand. I was quickly able to write a proof of concept web interface to control which preset is selected in addition to controlling the volume on my device. I ended up wrapping their API with a service of my own. The SoundTouch “talks” XML. I proxy the API calls because dealing with the XML is more comfortable in Go. The front end code is very basic and only deals with JSON.

In this post I’m going to describe how to interact with the SoundTouch music system by using their web services API. In addition, I’m going to describe a proof of concept web application that was built with it. The link to the source for this application can be found at the end of this post.

Interacting With the Device

Once you have setup your SoundTouch, you need to find it on the network. You need the device’s IP. I used IP Scanner on the Mac to locate it. Note that once you have the device’s MAC address you can probably setup a lease reservation to ensure the IP doesn’t change on you.

Here’s what the result of running IP Scanner looks like for me :

Notice that the SoundTouch device’s IP is 192.168.2.10. With this, I can now poke the SoundTouch’s API.

To get the list of presets on the device, you would do a GET request on the /presets URL (while changing the IP to suit your context). Using curl, the following command will get the list of presets on the SoundTouch :

curl http://192.168.2.10:8090/presets | xmllint --format -

The results of the command above should look something like :

To change the selected preset to something different you would post some data to the /select URL. Here’s what changing the SoundTouch to preset number 2 on my system looks like using curl:

curl \
--request POST \
--header "Content-Type: text/xml" \
--data '
<ContentItem source="INTERNET_RADIO" sourceAccount="" location="49358">
    <itemName>Ambiance Reggae</itemName>
</ContentItem>
' \
http://192.168.2.10:8090/select

Note that the payload above is XML when talking to the device directly.

Proxying Calls to the SoundTouch

The /presets and /select URLs can be used to build a very simple web application to control which preset is selected on the SoudTouch. The SoundTouch web services API sends XML back and forth. My front end code could interact directly with the device and use XML. I decided to proxy my web service calls to the SoundTouch with a server that was written in Go. It turns out that processing XML is super easy in this language.

To unmarshal an XML document, you define a struct and tag where the field data is coming from in the XML document. Here is the struct definition for the XML document that represents presets coming back from a service call to the SoundTouch :

package domain

type Preset struct {
	Id          string      `xml:"id,attr"`
	ContentItem ContentItem `xml:"ContentItem"`
}

type ContentItem struct {
	Source   string `xml:"source,attr"`
	Location string `xml:"location,attr"`
	ItemName string `xml:"itemName"`
}

type Presets struct {
	Presets []Preset `xml:"preset"`
}

The code below fetches the presets from the SoundTouch, unmarshalls the XML document, marshals it as JSON and returns it as a pointer to a string.

func (h *Presets) fetchPresets() (*string, error) {
	url := config.ServerConf.SoundTouchUrl + "/presets"

	req, err := http.NewRequest("GET", url, nil)

	client := &http.Client{}

	resp, err := client.Do(req)

	if err != nil {
		return nil, err
	}

	defer resp.Body.Close()

	body, _ := ioutil.ReadAll(resp.Body)

	x := domain.Presets{}

	err = xml.Unmarshal([]byte(body), &x)
	if err != nil {
		return nil, err
	}

	j, err := json.Marshal(x)
	if err != nil {
		return nil, err
	}

	retVal := string(j)

	return &retVal, nil
}

Working with XML in Go is very nice.

A Very Simple Web Interface

The functionality in my application is very limited. I was able to cheat and ensure that my proxying service only deals with GET requests and “talks” JSON (as described above). This makes the front end JavaScript code calling it simpler (no XML).

As you can see from the image below there’s almost nothing to the web interface. I can only control which preset is selected and the device’s volume. However, this is all I need at the moment and I’m quite happy to use this version as is.

The REST endpoints

I created these REST enpoints for my proxying service :

  • http://localhost:8080/rest/presets - (To get the list of presets on the device)
  • http://localhost:8080/rest/select/{source}/{location} - (To set the preset on the device)
  • http://localhost:8080/rest/volume - (To get the current volume on the device)
  • http://localhost:8080/rest/volume/{target} - (To set the volume on the device)

The front end code makes use of these endpoints to display/select the presets and control the volume. See the GitHub link at the end of this post for more information on how to obtain the project code. Note that there is a configuration file that probably will need tweaking for the server to run properly.

Final Thoughts

It was fun discovering that our SoundTouch has an API and what it offers. You can do a lot more with the API than what I showed here but I’m glad that I got to explore it a bit with this post. In addition, I now have the ability to control the SoundTouch settings I want with my laptop and a browser.

Notifications from the SoundTouch are also available over WebSocket. This informs clients of changes with a SoundTouch device. They serve to keep clients in sync with the server. They are sent over HTTP on port 8080 by the SoundTouch. These notifications can inform clients that the current song that is playing has now changed for example. These notification messages could be used to collect data and possibly do analytics on it or share it etc… I will probably not be doing this but it would be a cool way to explore WebSockets a little more.

Source files for the example application in this post : GitHub Repository. Enjoy!

comments powered by Disqus