Bose SoundTouch Toolkit

Documentation for controlling and preserving Bose SoundTouch devices

View the Project on GitHub gesellix/Bose-SoundTouch

Navigation API Reference

Overview

This document provides a complete API reference for the Bose SoundTouch navigation and station management functionality. For usage examples and workflows, see NAVIGATION-GUIDE.md.

Table of Contents

Client Methods

Browse content within a source.

Parameters:

Returns:

Example:

response, err := client.Navigate("TUNEIN", "", 1, 25)

Validation:


Browse content with specific menu and sorting options (primarily for Pandora).

Parameters:

Returns:

Example:

response, err := client.NavigateWithMenu("PANDORA", "user123", "radioStations", "dateCreated", 1, 100)

Browse into a specific container/directory.

Parameters:

Returns:

Example:

response, err := client.NavigateContainer("STORED_MUSIC", "device/0", 1, 100, albumContentItem)

Validation:


Convenience Navigation Methods

GetTuneInStations(sourceAccount string) (*models.NavigateResponse, error)

Browse TuneIn radio stations.

Parameters:

Returns:

Example:

stations, err := client.GetTuneInStations("")

GetPandoraStations(sourceAccount string) (*models.NavigateResponse, error)

Browse Pandora radio stations with proper sorting.

Parameters:

Returns:

Example:

stations, err := client.GetPandoraStations("user123")

Validation:


GetStoredMusicLibrary(sourceAccount string) (*models.NavigateResponse, error)

Browse stored/local music library.

Parameters:

Returns:

Example:

library, err := client.GetStoredMusicLibrary("A81B6A536A98/0")

Validation:


Search Methods

SearchStation(source, sourceAccount, searchTerm string) (*models.SearchStationResponse, error)

Search for stations and content within a music service.

Parameters:

Returns:

Example:

results, err := client.SearchStation("PANDORA", "user123", "jazz")

Validation:


SearchTuneInStations(searchTerm string) (*models.SearchStationResponse, error)

Search TuneIn radio stations.

Parameters:

Returns:

Example:

results, err := client.SearchTuneInStations("classical music")

SearchPandoraStations(sourceAccount, searchTerm string) (*models.SearchStationResponse, error)

Search Pandora for artists and stations.

Parameters:

Returns:

Example:

results, err := client.SearchPandoraStations("user123", "Taylor Swift")

Validation:


SearchSpotifyContent(sourceAccount, searchTerm string) (*models.SearchStationResponse, error)

Search Spotify for tracks, albums, and playlists.

Parameters:

Returns:

Example:

results, err := client.SearchSpotifyContent("user@example.com", "Queen")

Validation:


Station Management Methods

AddStation(source, sourceAccount, token, name string) error

Add a station to music service collection and immediately start playing it.

Parameters:

Returns:

Example:

err := client.AddStation("PANDORA", "user123", "R4328162", "Classic Rock Radio")

Behavior:

Validation:


RemoveStation(contentItem *models.ContentItem) error

Remove a station from music service collection.

Parameters:

Returns:

Example:

err := client.RemoveStation(stationContentItem)

Behavior:

Validation:


Models

Request structure for /navigate endpoint.

type NavigateRequest struct {
    Source        string        `xml:"source,attr"`
    SourceAccount string        `xml:"sourceAccount,attr,omitempty"`
    Menu          string        `xml:"menu,attr,omitempty"`
    Sort          string        `xml:"sort,attr,omitempty"`
    StartItem     int           `xml:"startItem"`
    NumItems      int           `xml:"numItems"`
    Item          *NavigateItem `xml:"item,omitempty"`
}

Constructors:


Response structure from navigation operations.

type NavigateResponse struct {
    Source        string         `xml:"source,attr"`
    SourceAccount string         `xml:"sourceAccount,attr,omitempty"`
    TotalItems    int            `xml:"totalItems"`
    Items         []NavigateItem `xml:"items>item"`
}

Helper Methods:


Individual item within navigation response.

type NavigateItem struct {
    Playable           int                 `xml:"Playable,attr,omitempty"`
    Name               string              `xml:"name"`
    Type               string              `xml:"type"`
    ContentItem        *ContentItem        `xml:"ContentItem,omitempty"`
    MediaItemContainer *MediaItemContainer `xml:"mediaItemContainer,omitempty"`
    ArtistName         string              `xml:"artistName,omitempty"`
    AlbumName          string              `xml:"albumName,omitempty"`
}

Helper Methods:

Common Type Values:


SearchStationRequest

Request structure for station search.

type SearchStationRequest struct {
    Source        string `xml:"source,attr"`
    SourceAccount string `xml:"sourceAccount,attr,omitempty"`
    SearchTerm    string `xml:",chardata"`
}

Constructor:


SearchStationResponse

Response structure from search operations.

type SearchStationResponse struct {
    DeviceID      string         `xml:"deviceID,attr"`
    Source        string         `xml:"source,attr"`
    SourceAccount string         `xml:"sourceAccount,attr,omitempty"`
    Songs         []SearchResult `xml:"songs>searchResult"`
    Artists       []SearchResult `xml:"artists>searchResult"`
    Stations      []SearchResult `xml:"stations>searchResult"`
}

Helper Methods:


SearchResult

Individual search result item.

type SearchResult struct {
    Source        string `xml:"source,attr"`
    SourceAccount string `xml:"sourceAccount,attr,omitempty"`
    Token         string `xml:"token,attr"`
    Name          string `xml:"name"`
    Artist        string `xml:"artist,omitempty"`
    Album         string `xml:"album,omitempty"`
    Logo          string `xml:"logo,omitempty"`
    Description   string `xml:"description,omitempty"`
}

Helper Methods:

Token Usage: The Token field is used with AddStation() to add the result to your collection.


AddStationRequest

Request structure for adding stations.

type AddStationRequest struct {
    Source        string `xml:"source,attr"`
    SourceAccount string `xml:"sourceAccount,attr,omitempty"`
    Token         string `xml:"token,attr"`
    Name          string `xml:"name"`
}

Constructor:


StationResponse

Response structure from station management operations.

type StationResponse struct {
    Status string `xml:",chardata"`
}

Common Values:


HTTP Endpoints

POST /navigate

Browse content within a source.

Request Body:

<navigate source="TUNEIN" sourceAccount="">
  <startItem>1</startItem>
  <numItems>25</numItems>
</navigate>

Response Body:

<navigateResponse source="TUNEIN">
  <totalItems>5</totalItems>
  <items>
    <item Playable="1">
      <name>Station Name</name>
      <type>stationurl</type>
      <ContentItem source="TUNEIN" location="/v1/playback/station/s12345" isPresetable="true">
        <itemName>Station Name</itemName>
      </ContentItem>
    </item>
  </items>
</navigateResponse>

POST /searchStation

Search for stations and content.

Request Body:

<search source="PANDORA" sourceAccount="user123">Taylor Swift</search>

Response Body:

<results deviceID="A81B6A536A98" source="PANDORA" sourceAccount="user123">
  <songs>
    <searchResult source="PANDORA" sourceAccount="user123" token="S123">
      <name>Love Story</name>
      <artist>Taylor Swift</artist>
      <logo>http://example.com/artwork.jpg</logo>
    </searchResult>
  </songs>
  <artists>
    <searchResult source="PANDORA" sourceAccount="user123" token="R456">
      <name>Taylor Swift</name>
      <logo>http://example.com/artist.jpg</logo>
    </searchResult>
  </artists>
</results>

POST /addStation

Add a station to collection and start playing.

Request Body:

<addStation source="PANDORA" sourceAccount="user123" token="R456">
  <name>Taylor Swift Radio</name>
</addStation>

Response Body:

<status>/addStation</status>

POST /removeStation

Remove a station from collection.

Request Body:

<ContentItem source="PANDORA" location="126740707481236361" sourceAccount="user123" isPresetable="true">
  <itemName>Taylor Swift Radio</itemName>
</ContentItem>

Response Body:

<status>/removeStation</status>

XML Schemas

<xs:element name="navigate">
  <xs:complexType>
    <xs:sequence>
      <xs:element name="startItem" type="xs:int"/>
      <xs:element name="numItems" type="xs:int"/>
      <xs:element name="item" minOccurs="0">
        <xs:complexType>
          <xs:sequence>
            <xs:element name="name" type="xs:string"/>
            <xs:element name="type" type="xs:string"/>
            <xs:element name="ContentItem" type="ContentItemType"/>
          </xs:sequence>
          <xs:attribute name="Playable" type="xs:int"/>
        </xs:complexType>
      </xs:element>
    </xs:sequence>
    <xs:attribute name="source" type="xs:string" use="required"/>
    <xs:attribute name="sourceAccount" type="xs:string"/>
    <xs:attribute name="menu" type="xs:string"/>
    <xs:attribute name="sort" type="xs:string"/>
  </xs:complexType>
</xs:element>

Search Request Schema

<xs:element name="search">
  <xs:complexType>
    <xs:simpleContent>
      <xs:extension base="xs:string">
        <xs:attribute name="source" type="xs:string" use="required"/>
        <xs:attribute name="sourceAccount" type="xs:string"/>
      </xs:extension>
    </xs:simpleContent>
  </xs:complexType>
</xs:element>

ContentItem Type Schema

<xs:complexType name="ContentItemType">
  <xs:sequence>
    <xs:element name="itemName" type="xs:string" minOccurs="0"/>
    <xs:element name="containerArt" type="xs:string" minOccurs="0"/>
  </xs:sequence>
  <xs:attribute name="source" type="xs:string" use="required"/>
  <xs:attribute name="type" type="xs:string"/>
  <xs:attribute name="location" type="xs:string"/>
  <xs:attribute name="sourceAccount" type="xs:string"/>
  <xs:attribute name="isPresetable" type="xs:boolean"/>
</xs:complexType>

Error Codes

HTTP Status Codes

Status Meaning Description
200 OK Request successful
400 Bad Request Invalid parameters or XML
404 Not Found Endpoint or content not found
500 Internal Server Error Device error

Common Error Responses

Invalid Source:

<error>
  <code>INVALID_SOURCE</code>
  <message>Source 'INVALID' is not available</message>
</error>

Authentication Required:

<error>
  <code>AUTH_REQUIRED</code>
  <message>Source account required for this service</message>
</error>

Service Unavailable:

<error>
  <code>SERVICE_UNAVAILABLE</code>
  <message>PANDORA service is not configured</message>
</error>

Client-Side Validation Errors

The Go client performs validation before sending requests:

Error Message Cause Solution
"source cannot be empty" Empty source parameter Provide valid source
"search term cannot be empty" Empty search query Provide search term
"startItem must be >= 1" Invalid start position Use 1-based indexing
"numItems must be >= 1" Invalid page size Use positive number
"content item cannot be nil" Nil ContentItem Provide valid ContentItem
"container item cannot be nil" Nil container for NavigateContainer Provide valid container
"Pandora source account cannot be empty" Missing Pandora account Configure Pandora account
"token cannot be empty" Missing station token Use token from search results
"station name cannot be empty" Missing station name Provide station name

WebSocket Events

Navigation and station operations generate WebSocket events:

presetsUpdated

Generated when stations are added/removed that affect presets.

<presetsUpdated deviceID="A81B6A536A98">
  <presets>
    <!-- Updated preset list -->
  </presets>
</presetsUpdated>

nowPlayingUpdated

Generated when station operations affect current playback.

<nowPlayingUpdated deviceID="A81B6A536A98">
  <nowPlaying source="PANDORA">
    <ContentItem source="PANDORA" location="R456" sourceAccount="user123" isPresetable="true">
      <itemName>Taylor Swift Radio</itemName>
    </ContentItem>
    <track>Love Story</track>
    <artist>Taylor Swift</artist>
    <playStatus>PLAY_STATE</playStatus>
  </nowPlaying>
</nowPlayingUpdated>

Best Practices

Parameter Validation

Always validate parameters before API calls:

func validateNavigateParams(source string, startItem, numItems int) error {
    if source == "" {
        return fmt.Errorf("source cannot be empty")
    }
    if startItem < 1 {
        return fmt.Errorf("startItem must be >= 1")
    }
    if numItems < 1 {
        return fmt.Errorf("numItems must be >= 1")
    }
    return nil
}

Error Handling

Handle both network and API errors:

response, err := client.Navigate("TUNEIN", "", 1, 25)
if err != nil {
    // Check if it's a known API error
    if strings.Contains(err.Error(), "not available") {
        log.Printf("TuneIn not configured on device")
        return
    }
    return fmt.Errorf("navigation failed: %w", err)
}

Pagination

Use appropriate page sizes for different contexts:

// Small pages for interactive browsing
response, err := client.Navigate("TUNEIN", "", 1, 25)

// Larger pages for bulk processing
response, err := client.Navigate("STORED_MUSIC", "device/0", 1, 100)

Resource Management

Cache frequently accessed data:

type CachedClient struct {
    client      *client.Client
    sources     *models.Sources
    sourcesTime time.Time
}

func (c *CachedClient) GetSources() (*models.Sources, error) {
    if c.sources == nil || time.Since(c.sourcesTime) > 5*time.Minute {
        var err error
        c.sources, err = c.client.GetSources()
        c.sourcesTime = time.Now()
        return c.sources, err
    }
    return c.sources, nil
}

For complete usage examples and workflows, see NAVIGATION-GUIDE.md.