Documentation for controlling and preserving Bose SoundTouch devices
This document covers preset management functionality in the Bose SoundTouch API client.
Bose SoundTouch devices support up to 6 presets that can store favorite music sources, playlists, radio stations, and other audio content. The API provides comprehensive read and write access to preset information through both official endpoints and reverse-engineered preset management functionality.
GET /presets - Retrieve all configured presetsPOST /presets - Officially marked as “N/A” in Bose API documentation# Get all configured presets
soundtouch-cli -host 192.168.1.10 -presets
Example Output:
Configured Presets:
Used Slots: 6/6
Spotify Presets: 6
Preset 1: My Favorite Playlist
Source: SPOTIFY (user@example.com)
Type: tracklisturl
Created: 2024-04-30 07:37:40
Updated: 2024-04-30 07:37:40
Artwork: https://i.scdn.co/image/...
Preset 2: Rock Hits Radio
Source: TUNEIN
Type: station
Artwork: https://cdn-radiotime-logos.tunein.com/...
Available Slots: []
Most Recent: Preset 1 (My Favorite Playlist)
package main
import (
"fmt"
"github.com/gesellix/bose-soundtouch/pkg/client"
)
func main() {
// Create client
soundtouchClient := client.NewClientFromHost("192.168.1.10")
// Get all presets
presets, err := soundtouchClient.GetPresets()
if err != nil {
panic(err)
}
// Display preset information
fmt.Printf("Total presets: %d\n", presets.GetPresetCount())
fmt.Printf("Used slots: %d\n", len(presets.GetUsedPresetSlots()))
fmt.Printf("Empty slots: %v\n", presets.GetEmptyPresetSlots())
// Check for Spotify presets
spotifyPresets := presets.GetSpotifyPresets()
fmt.Printf("Spotify presets: %d\n", len(spotifyPresets))
// Get specific preset
preset1 := presets.GetPresetByID(1)
if preset1 != nil && !preset1.IsEmpty() {
fmt.Printf("Preset 1: %s\n", preset1.GetDisplayName())
fmt.Printf(" Source: %s\n", preset1.GetSource())
fmt.Printf(" Type: %s\n", preset1.GetContentType())
if preset1.HasTimestamps() {
fmt.Printf(" Created: %s\n", preset1.GetCreatedTime())
fmt.Printf(" Updated: %s\n", preset1.GetUpdatedTime())
}
}
// Find most recent preset
if recent := presets.GetMostRecentPreset(); recent != nil {
fmt.Printf("Most recent: Preset %d (%s)\n",
recent.ID, recent.GetDisplayName())
}
}
<presets>
<preset id="1" createdOn="1745991460" updatedOn="1745991460">
<ContentItem source="SPOTIFY" type="tracklisturl"
location="/playback/container/..."
sourceAccount="user@example.com"
isPresetable="true">
<itemName>My Favorite Songs</itemName>
<containerArt>https://i.scdn.co/image/...</containerArt>
</ContentItem>
</preset>
<preset id="2">
<ContentItem source="TUNEIN" type="station"
location="s12345" isPresetable="true">
<itemName>Classic Rock Radio</itemName>
<containerArt>https://cdn-radiotime-logos.tunein.com/...</containerArt>
</ContentItem>
</preset>
</presets>
type Presets struct {
XMLName xml.Name `xml:"presets"`
Preset []Preset `xml:"preset"`
}
type Preset struct {
XMLName xml.Name `xml:"preset"`
ID int `xml:"id,attr"`
CreatedOn *int64 `xml:"createdOn,attr,omitempty"`
UpdatedOn *int64 `xml:"updatedOn,attr,omitempty"`
ContentItem *ContentItem `xml:"ContentItem,omitempty"`
}
// Get preset by ID
preset := presets.GetPresetByID(3)
// Check if preset has content
if !preset.IsEmpty() {
// Use preset
}
// Get presets by source type
spotifyPresets := presets.GetPresetsBySource("SPOTIFY")
tuneInPresets := presets.GetPresetsBySource("TUNEIN")
// Find available slots
emptySlots := presets.GetEmptyPresetSlots() // Returns [4, 5] if slots 4-5 are empty
usedSlots := presets.GetUsedPresetSlots() // Returns [1, 2, 3, 6] if those are used
// Get display information
name := preset.GetDisplayName() // "My Playlist" or "Preset 1" fallback
source := preset.GetSource() // "SPOTIFY", "TUNEIN", etc.
account := preset.GetSourceAccount() // "user@example.com"
contentType := preset.GetContentType() // "playlist", "station", etc.
artwork := preset.GetArtworkURL() // Album/station artwork URL
// Check preset characteristics
isSpotify := preset.IsSpotifyPreset()
isPresetable := preset.IsPresetable()
// Time information (if available)
if preset.HasTimestamps() {
created := preset.GetCreatedTime()
updated := preset.GetUpdatedTime()
}
Common content types found in presets:
| Source | Type | Description | Example Location |
|---|---|---|---|
SPOTIFY |
tracklisturl |
Playlist/Album | /playback/container/c3Bv... |
SPOTIFY |
track |
Single Track | /playback/container/c3Bv... |
TUNEIN |
station |
Radio Station | s12345 |
PANDORA |
station |
Pandora Station | TR:station:12345 |
AMAZON |
playlist |
Amazon Playlist | amzn1.dv.gti... |
While you cannot create presets via API, you can select existing presets:
# Select preset 1-6 using key commands
soundtouch-cli -host 192.168.1.10 -preset 1
soundtouch-cli -host 192.168.1.10 -key PRESET_3
// Select preset using key command
err := soundtouchClient.SelectPreset(1)
// Or use direct key command
err := soundtouchClient.SendKey("PRESET_1")
Despite official documentation marking POST /presets as “N/A”, we discovered working preset management endpoints through the comprehensive SoundTouch Plus Wiki:
POST /storePreset - Fully functional preset creation and updatingPOST /removePreset - Complete preset deletion and slot clearingpresetsUpdated notificationshttp://device-ip:8090 in browserBefore attempting to save content as a preset (via app/hardware), you can check if the current content supports preset saving:
// Check if currently playing content can be saved as preset
nowPlaying, err := client.GetNowPlaying()
if err != nil {
return err
}
if nowPlaying.ContentItem != nil && nowPlaying.ContentItem.IsPresetable {
fmt.Println("✓ Current content can be saved as a preset")
fmt.Printf(" Content: %s\n", nowPlaying.ContentItem.ItemName)
fmt.Printf(" Source: %s\n", nowPlaying.ContentItem.Source)
fmt.Printf(" Type: %s\n", nowPlaying.ContentItem.Type)
} else {
fmt.Println("✗ Current content cannot be saved as a preset")
}
Or use the convenience method:
// Simple presetability check
presetable, err := client.IsCurrentContentPresetable()
if err != nil {
return err
}
if presetable {
fmt.Println("✓ Content is presetable - use app or device buttons to save")
} else {
fmt.Println("✗ Content cannot be saved as preset")
}
presets, err := client.GetPresets()
if err != nil {
return err
}
emptySlots := presets.GetEmptyPresetSlots()
if len(emptySlots) == 0 {
fmt.Println("All preset slots are occupied")
// Consider which preset to overwrite
} else {
fmt.Printf("Available preset slots: %v\n", emptySlots)
}
// Get summary statistics
summary := presets.GetPresetsSummary()
fmt.Printf("Total: %d, Used: %d, Empty: %d\n",
summary["total"], summary["used"], summary["empty"])
// Check source distribution
if summary["SPOTIFY"] > 0 {
fmt.Printf("Spotify presets: %d\n", summary["SPOTIFY"])
}
if summary["TUNEIN"] > 0 {
fmt.Printf("TuneIn presets: %d\n", summary["TUNEIN"])
}
// Find recently used presets
if recent := presets.GetMostRecentPreset(); recent != nil {
fmt.Printf("Most recently updated: Preset %d (%s)\n",
recent.ID, recent.GetDisplayName())
}
if oldest := presets.GetOldestPreset(); oldest != nil {
fmt.Printf("Oldest preset: Preset %d (%s)\n",
oldest.ID, oldest.GetDisplayName())
}
Despite the official Bose SoundTouch API documentation marking preset creation as “not supported”, we discovered working preset management endpoints through the SoundTouch Plus Wiki:
POST /storePreset - Complete preset creation and updating functionalityPOST /removePreset - Full preset deletion and clearing capabilitypresetsUpdated notificationsThe original API limitation appears to have been either:
This implementation now provides the full preset management lifecycle:
Preset management in the Bose SoundTouch API is intentionally read-only by design. The API provides excellent capabilities for analyzing and understanding preset configurations, but preset creation must be done through official channels (app or device). This is a deliberate design decision that respects user control over their personal preset configurations.
For most use cases, reading preset information is sufficient for building applications that work with existing user configurations. For preset creation, guide users to use the official app or device controls, which provide the proper user experience and validation.