Based on real device testing, the /trackInfo endpoint returns
'AllegroWebserver timeout' errors despite being documented in the
official Bose SoundTouch Web API v1.0 specification.
## Changes
- Updated API coverage from 89% to 84% (16/19 functional endpoints)
- Marked /trackInfo as ❌ Non-functional in all documentation
- Added warning comments to GetTrackInfo() method
- Updated CLI command with warning message
- Recommend using /now_playing instead for track information
## Real Device Evidence
- Device: SoundTouch at 192.168.178.28:8090
- Error: 'AllegroWebserver timeout: /trackInfo'
- Status: Endpoint documented but not working on hardware
This reflects the reality that some officially documented endpoints
may not function properly on actual devices, emphasizing the importance
of real hardware testing in API implementation.
Bose SoundTouch API Client
A modern Go library and CLI tool for interacting with Bose SoundTouch devices via their Web API.
Note
: This implementation is based on the official Bose SoundTouch Web API documentation provided by Bose Corporation at their SoundTouch End-of-Life page. This is an independent project and is not affiliated with or endorsed by Bose Corporation.
Features
✅ Implemented (84% Complete - 16/19 functional endpoints)
- HTTP Client with XML Support: Complete client for SoundTouch Web API
- Device Information: Get detailed device info via
/infoendpoint - Device Name: Get device name via
/nameendpoint - Device Capabilities: Get device capabilities via
/capabilitiesendpoint - Configured Presets: Get preset configurations via
/presetsendpoint - Now Playing Status: Get current playback information via
/now_playingendpoint - Audio Sources: Get available sources via
/sourcesendpoint - Media Controls: Play, pause, stop, track navigation via
/keyendpoint - Volume Management: Get/set volume, incremental control via
/volumeendpoint - Bass Control: Get/set bass levels (-9 to +9 range) via
/bassendpoint - Balance Control: Get/set balance (-50 to +50 range) via
/balanceendpoint - Clock/Time Management: Get/set device time via
/clockTimeand/clockDisplayendpoints - Network Information: Get network details via
/networkInfoendpoint - Real-time WebSocket Events: Live monitoring of device state changes
- UPnP/SSDP Discovery: Automatic device discovery using Universal Plug and Play
- mDNS/Bonjour Discovery: Multicast DNS device discovery support
- Cross-Platform: Works on Windows, macOS, Linux, and WASM
- CLI Tool: Command-line interface for testing and control operations
- Flexible Configuration: Support for .env files and environment variables
- Unified Discovery: Combines UPnP, mDNS, and configured device lists
- Safety Features: Volume warnings, increment limits, error validation
✅ Recently Completed - Zone Management
- Multiroom Support: GET/POST /getZone, /setZone with comprehensive zone operations
- Zone Operations: Create, modify, add members, remove members, dissolve zones
- Zone Status: Query zone membership, master/slave status, device counting
✅ All High Priority Endpoints Complete
All originally planned high-priority endpoints have been implemented. The API client now has comprehensive coverage of all available SoundTouch functionality.
❌ Not Supported by API
- Preset Creation: POST /presets (officially not supported by SoundTouch API)
Recent Additions - WebSocket Events ⚡
NEW: Real-time WebSocket support has been added! Monitor device state changes in real-time with comprehensive event handling.
Key Features:
- 🎵 Live Now Playing Updates: Track changes, playback status, shuffle/repeat
- 🔊 Real-time Volume Changes: Volume levels and mute status
- 🌐 Connection Monitoring: Network connectivity and signal strength
- 📻 Preset Notifications: Preset updates and selections
- 🏠 Multiroom Events: Zone membership changes
- 🎚️ Audio Settings: Bass level adjustments
- 🔄 Auto-Reconnection: Robust connection management
- 🎛️ Event Filtering: Subscribe to specific event types
- 📊 Comprehensive Logging: Debug and monitoring capabilities
CLI Demo:
# Quick start - auto-discover and monitor all events
go run ./cmd/websocket-demo -discover
# Monitor specific device with event filtering
go run ./cmd/websocket-demo -host 192.168.1.10 -filter nowPlaying,volume -verbose
Installation
Using Go
go install github.com/gesellix/bose-soundtouch/cmd/soundtouch-cli@latest
From Source
git clone https://github.com/gesellix/bose-soundtouch.git
cd bose-soundtouch
make build
Quick Start
Configuration
Create a .env file in your working directory to configure preferred devices:
# Copy the example file
cp .env.example .env
Example .env configuration:
# Discovery Settings
DISCOVERY_TIMEOUT=5s
UPNP_ENABLED=true
MDNS_ENABLED=true
# Preferred Devices (alternative to UPnP)
# Format: name@host:port;name@host:port;...
PREFERRED_DEVICES="Living Room@192.168.1.10;Kitchen@192.168.1.11;192.168.1.12:8091"
# HTTP Client Settings
HTTP_TIMEOUT=10s
USER_AGENT="Bose-SoundTouch-Go-Client/1.0"
CLI Usage
Device Discovery
The library supports multiple discovery methods automatically:
- Configuration: Manually specified devices in
.envfile (fastest, most reliable) - UPnP/SSDP: Universal Plug and Play discovery (widely supported)
- mDNS/Bonjour: Multicast DNS discovery (Apple ecosystem friendly)
See docs/DISCOVERY.md for detailed information.
# Discover SoundTouch devices (combines UPnP, mDNS + configured devices)
soundtouch-cli discover devices
# Discover and show detailed info for all devices
soundtouch-cli discover devices --all
# Discover with custom timeout
soundtouch-cli discover devices --timeout 10s
Real-time WebSocket Events
Monitor device state changes in real-time using WebSocket connections:
# Auto-discover device and monitor all events
go run ./cmd/websocket-demo -discover
# Connect to specific device and monitor all events
go run ./cmd/websocket-demo -host 192.168.1.10
# Monitor only volume and now playing events
go run ./cmd/websocket-demo -host 192.168.1.10 -filter volume,nowPlaying
# Monitor for 5 minutes with verbose output
go run ./cmd/websocket-demo -host 192.168.1.10 -duration 5m -verbose
# Available event types for filtering:
# nowPlaying, volume, connection, preset, zone, bass, sdkInfo, userActivity
Supported WebSocket Events:
- 🎵 Now Playing: Track changes, playback status, shuffle/repeat settings
- 🔊 Volume: Volume level and mute status changes
- 🌐 Connection: Network connectivity and signal strength
- 📻 Preset: Preset configuration updates
- 🏠 Zone: Multiroom zone membership changes
- 🎚️ Bass: Bass equalizer level adjustments
- 📡 SDK Info: Server version and build information (sent on connection)
- 👤 User Activity: User interaction notifications
See docs/websocket-events.md for complete WebSocket documentation.
Device Information
# Get device information by IP address
soundtouch-cli --host 192.168.1.10 info
# With custom port and timeout
soundtouch-cli --host 192.168.1.10 --port 8090 --timeout 15s info
Now Playing Status
# Get current playback information
soundtouch-cli --host 192.168.1.10 play now
# Example output:
# Now Playing:
# Device ID: ABCD1234EFGH
# Source: SPOTIFY
# Status: Playing
# Title: In Between Breaths - Paris Unplugged
# Artist: SYML
# Album: Paris Unplugged
# Duration: 2:32 / 3:30
# Shuffle: Off
# Repeat: Off
# Artwork: https://i.scdn.co/image/...
# Capabilities: Skip, Skip Previous, Seek, Favorite
#### Audio Sources
```bash
# Get available audio sources
soundtouch-cli --host 192.168.1.10 source list
# Example output:
# Available Audio Sources:
# Device ID: ABCD1234EFGH
# Ready Sources:
# • AUX IN [Local, Available]
# • user+spotify@example.com (user) [Streaming]
# • Alexa
# • Tunein
# • Local_internet_radio
#
# All Sources:
# • AUX IN (Available)
# • SPOTIFY (Remote)
# Account: user+spotify@example.com
#
# Streaming Services:
# • user+spotify@example.com (user)
# • Tunein
# • Local_internet_radio
Media Controls
# Basic playback controls
soundtouch-cli --host 192.168.1.10 play start
soundtouch-cli --host 192.168.1.10 play pause
soundtouch-cli --host 192.168.1.10 play stop
# Track navigation
soundtouch-cli --host 192.168.1.10 play next
soundtouch-cli --host 192.168.1.10 play prev
# Volume controls (key-based)
soundtouch-cli --host 192.168.1.10 key volume-up
soundtouch-cli --host 192.168.1.10 key volume-down
# Preset selection
soundtouch-cli --host 192.168.1.10 preset --preset 1
soundtouch-cli --host 192.168.1.10 preset --preset 6
# Generic key command
soundtouch-cli --host 192.168.1.10 key send --key STOP
Volume Management
# Get current volume
soundtouch-cli --host 192.168.1.10 volume get
# Example output:
# Current Volume:
# Device ID: ABCD1234EFGH
# Current Level: 50 (Medium)
# Target Level: 50
# Muted: false
# Set specific volume (0-100)
soundtouch-cli --host 192.168.1.10 volume set --level 25
soundtouch-cli --host 192.168.1.10 volume set --level 0 # Mute
# Incremental volume control
soundtouch-cli --host 192.168.1.10 volume up --amount 3
soundtouch-cli --host 192.168.1.10 volume down --amount 5
Device Name
# Get device name
soundtouch-cli --host 192.168.1.10 name get
# Set device name
soundtouch-cli --host 192.168.1.10 name set --value "My SoundTouch"
# Example output:
# Device Name: My SoundTouch
Device Capabilities
# Get device capabilities
soundtouch-cli --host 192.168.1.10 capabilities
# Example output:
# Device Capabilities:
# Device ID: ABCD1234EFGH
#
# System Features:
# • Power Saving Disabled
#
# Audio Features:
# • L/R Stereo
#
# Network Features:
# • Dual Mode
# • WSAPI Proxy
#
# Extended Capabilities:
# • systemtimeout (/systemtimeout)
# • rebroadcastlatencymode (/rebroadcastlatencymode)
Configured Presets
# Get configured presets
soundtouch-cli --host 192.168.1.10 presets
# Example output:
# Configured Presets:
# Used Slots: 6/6
# Spotify Presets: 6
#
# Preset 1: My Playlist
# Source: SPOTIFY (user@example.com)
# Type: tracklisturl
# Created: 2024-06-23 09:40:36
# Updated: 2024-10-12 15:39:42
# Artwork: https://i.scdn.co/image/...
#
# Most Recent: Preset 4 (Movie Soundtrack)
System Information
# Get device clock time
soundtouch-cli --host 192.168.1.10 clock get
# Set device time to current system time
soundtouch-cli --host 192.168.1.10 clock now
# Get clock display settings
soundtouch-cli --host 192.168.1.10 clock display get
# Configure clock display
soundtouch-cli --host 192.168.1.10 clock display enable
soundtouch-cli --host 192.168.1.10 clock display format --format 24
soundtouch-cli --host 192.168.1.10 clock display brightness --brightness high
# Get network information
soundtouch-cli --host 192.168.1.10 network info
# Bass control
soundtouch-cli --host 192.168.1.10 bass get
soundtouch-cli --host 192.168.1.10 bass set --level 3
soundtouch-cli --host 192.168.1.10 bass up --amount 2
# Balance control
soundtouch-cli --host 192.168.1.10 balance get
soundtouch-cli --host 192.168.1.10 balance set --level -10
soundtouch-cli --host 192.168.1.10 balance center
# Source selection
soundtouch-cli --host 192.168.1.10 source spotify
soundtouch-cli --host 192.168.1.10 source bluetooth
soundtouch-cli --host 192.168.1.10 source select --source SPOTIFY --account user@example.com
# Zone management
soundtouch-cli --host 192.168.1.10 zone get
soundtouch-cli --host 192.168.1.10 zone create --members 192.168.1.11,192.168.1.12
soundtouch-cli --host 192.168.1.10 zone add --member 192.168.1.13
soundtouch-cli --host 192.168.1.10 zone dissolve
Go Library Usage
Basic HTTP Client Usage
package main
import (
"fmt"
"log"
"github.com/gesellix/bose-soundtouch/pkg/client"
)
func main() {
// Create client
soundTouchClient := client.NewClientFromHost("192.168.1.10")
// Get device information
deviceInfo, err := soundTouchClient.GetDeviceInfo()
if err != nil {
log.Fatal(err)
}
fmt.Printf("Device: %s (%s)\n", deviceInfo.Name, deviceInfo.Type)
// Get now playing
nowPlaying, err := soundTouchClient.GetNowPlaying()
if err != nil {
log.Fatal(err)
}
if !nowPlaying.IsEmpty() {
fmt.Printf("Now Playing: %s by %s\n", nowPlaying.Track, nowPlaying.Artist)
fmt.Printf("Status: %s\n", nowPlaying.PlayStatus.String())
}
// Volume control
volume, err := soundTouchClient.GetVolume()
if err != nil {
log.Fatal(err)
}
fmt.Printf("Volume: %d\n", volume.ActualVolume)
// Set volume safely (with warnings)
err = soundTouchClient.SetVolumeSafe(25)
if err != nil {
log.Fatal(err)
}
// Media controls
soundTouchClient.Play()
soundTouchClient.Pause()
soundTouchClient.VolumeUp()
// Source selection
soundTouchClient.SelectSpotify()
soundTouchClient.SelectPreset(1)
}
Real-time WebSocket Events
package main
import (
"context"
"log"
"os"
"os/signal"
"syscall"
"github.com/gesellix/bose-soundtouch/pkg/client"
"github.com/gesellix/bose-soundtouch/pkg/models"
)
func main() {
// Create SoundTouch client
soundTouchClient := client.NewClientFromHost("192.168.1.10")
// Create WebSocket client
wsClient := soundTouchClient.NewWebSocketClient(nil)
// Set up event handlers
wsClient.OnNowPlaying(func(event *models.NowPlayingUpdatedEvent) {
np := &event.NowPlaying
log.Printf("🎵 Now Playing: %s by %s", np.Track, np.Artist)
log.Printf(" Status: %s, Source: %s", np.PlayStatus.String(), np.Source)
if np.HasTimeInfo() {
log.Printf(" Duration: %s", np.FormatDuration())
}
})
wsClient.OnVolumeUpdated(func(event *models.VolumeUpdatedEvent) {
vol := &event.Volume
if vol.IsMuted() {
log.Println("🔇 Volume: Muted")
} else {
log.Printf("🔊 Volume: %d (%s)", vol.ActualVolume,
models.GetVolumeLevelName(vol.ActualVolume))
}
})
wsClient.OnConnectionState(func(event *models.ConnectionStateUpdatedEvent) {
cs := &event.ConnectionState
if cs.IsConnected() {
log.Printf("✅ Connected (Signal: %s)", cs.GetSignalStrength())
} else {
log.Printf("❌ Connection: %s", cs.State)
}
})
wsClient.OnBassUpdated(func(event *models.BassUpdatedEvent) {
bass := &event.Bass
log.Printf("🎚️ Bass: %d", bass.ActualBass)
})
// Handle unknown events for debugging
wsClient.OnUnknownEvent(func(event *models.WebSocketEvent) {
log.Printf("❓ Unknown event types: %v", event.GetEventTypes())
})
// Connect to WebSocket
if err := wsClient.Connect(); err != nil {
log.Fatalf("Failed to connect: %v", err)
}
log.Println("Connected! Listening for events... (Press Ctrl+C to stop)")
// Set up graceful shutdown
sigChan := make(chan os.Signal, 1)
signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM)
// Wait for shutdown signal
<-sigChan
log.Println("Shutting down...")
// Disconnect
if err := wsClient.Disconnect(); err != nil {
log.Printf("Error during disconnect: %v", err)
}
log.Println("Disconnected successfully")
}
Advanced WebSocket Configuration
package main
import (
"log"
"time"
"github.com/gesellix/bose-soundtouch/pkg/client"
"github.com/gesellix/bose-soundtouch/pkg/models"
)
// Custom logger for WebSocket events
type CustomLogger struct{}
func (c *CustomLogger) Printf(format string, v ...interface{}) {
timestamp := time.Now().Format("15:04:05.000")
log.Printf("[%s] [WebSocket] %s", timestamp, fmt.Sprintf(format, v...))
}
func main() {
soundTouchClient := client.NewClientFromHost("192.168.1.10")
// Custom WebSocket configuration
config := &client.WebSocketConfig{
ReconnectInterval: 3 * time.Second, // Reconnect every 3 seconds
MaxReconnectAttempts: 5, // Try 5 times before giving up
PingInterval: 15 * time.Second, // Ping every 15 seconds
PongTimeout: 5 * time.Second, // Wait 5 seconds for pong
ReadBufferSize: 4096, // 4KB read buffer
WriteBufferSize: 4096, // 4KB write buffer
Logger: &CustomLogger{}, // Custom logger
}
wsClient := soundTouchClient.NewWebSocketClient(config)
// Set up handlers for specific events only
wsClient.OnNowPlaying(func(event *models.NowPlayingUpdatedEvent) {
// Handle only now playing events
log.Printf("Track changed: %s", event.NowPlaying.GetDisplayTitle())
})
// Connect with custom config
if err := wsClient.ConnectWithConfig(config); err != nil {
log.Fatal(err)
}
// Keep running
wsClient.Wait()
}
Device Discovery with WebSocket
package main
import (
"context"
"log"
"time"
"github.com/gesellix/bose-soundtouch/pkg/client"
"github.com/gesellix/bose-soundtouch/pkg/config"
"github.com/gesellix/bose-soundtouch/pkg/discovery"
"github.com/gesellix/bose-soundtouch/pkg/models"
)
func main() {
// Discover devices
cfg := &config.Config{
DiscoveryTimeout: 10 * time.Second,
CacheEnabled: false,
}
discoveryService := discovery.NewUnifiedDiscoveryService(cfg)
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
devices, err := discoveryService.DiscoverDevices(ctx)
if err != nil {
log.Fatal(err)
}
if len(devices) == 0 {
log.Fatal("No devices found")
}
// Connect to first device found
device := devices[0]
log.Printf("Connecting to: %s (%s:%d)", device.Name, device.Host, device.Port)
clientConfig := client.ClientConfig{
Host: device.Host,
Port: device.Port,
}
soundTouchClient := client.NewClient(clientConfig)
// Test basic connectivity
deviceInfo, err := soundTouchClient.GetDeviceInfo()
if err != nil {
log.Fatal(err)
}
log.Printf("Connected to: %s", deviceInfo.Name)
// Set up WebSocket monitoring
wsClient := soundTouchClient.NewWebSocketClient(nil)
wsClient.OnNowPlaying(func(event *models.NowPlayingUpdatedEvent) {
log.Printf("[%s] Now Playing: %s",
deviceInfo.Name, event.NowPlaying.GetDisplayTitle())
})
if err := wsClient.Connect(); err != nil {
log.Fatal(err)
}
log.Println("Monitoring events...")
wsClient.Wait()
}
Project Structure
Bose-SoundTouch/
├── cmd/
│ ├── soundtouch-cli/ # Main CLI tool (fully functional)
│ ├── websocket-demo/ # WebSocket event monitoring demo
│ ├── example-upnp/ # UPnP discovery examples
│ ├── example-mdns/ # mDNS discovery examples
│ └── mdns-scanner/ # Network scanning utility
├── pkg/
│ ├── client/ # HTTP & WebSocket clients
│ │ ├── client.go # Main HTTP API client
│ │ ├── websocket.go # WebSocket event client
│ │ └── *_test.go # Comprehensive tests
│ ├── models/ # Typed XML models
│ │ ├── websocket.go # WebSocket event models
│ │ ├── nowplaying.go # Now playing models
│ │ ├── volume.go # Volume control models
│ │ ├── bass.go # Bass control models
│ │ ├── balance.go # Balance control models
│ │ └── *.go # Other endpoint models
│ ├── discovery/ # Device discovery
│ │ ├── unified.go # Unified discovery service
│ │ ├── upnp.go # UPnP/SSDP discovery
│ │ └── mdns.go # mDNS/Bonjour discovery
│ └── config/ # Configuration management
└── docs/ # Comprehensive documentation
├── websocket-events.md # WebSocket API documentation
├── DISCOVERY.md # Device discovery guide
└── API.md # HTTP API reference
API Coverage Status
| Endpoint | Method | Status | Description |
|---|---|---|---|
/info |
GET | ✅ Complete | Device information and capabilities |
/name |
GET | ✅ Complete | Device name |
/capabilities |
GET | ✅ Complete | Device feature capabilities |
/now_playing |
GET | ✅ Complete | Current playback status |
/sources |
GET | ✅ Complete | Available audio sources |
/sources |
POST | ✅ Complete | Select audio source |
/key |
POST | ✅ Complete | Send key commands (24 commands) |
/volume |
GET/POST | ✅ Complete | Volume control with safety features |
/bass |
GET/POST | ✅ Complete | Bass control (-9 to +9) |
/balance |
GET/POST | ✅ Complete | Balance control (-50 to +50) |
/presets |
GET | ✅ Complete | Preset configurations (read-only) |
/presets |
POST | ❌ Not Supported | Officially not supported by SoundTouch API |
/clockTime |
GET/POST | ✅ Complete | Device time management |
/clockDisplay |
GET/POST | ✅ Complete | Clock display settings |
/networkInfo |
GET | ✅ Complete | Network connectivity information |
| WebSocket | / |
✅ Complete | Real-time event monitoring |
| Discovery | UPnP/mDNS | ✅ Complete | Device discovery services |
/getZone |
GET | ✅ NEW | Multiroom zone information |
/setZone |
POST | ✅ NEW | Zone creation and management |
/name |
POST | ✅ Complete | Set device name |
/bassCapabilities |
GET | ✅ Complete | Bass capability detection |
/trackInfo |
GET | ❌ Not Working | Documented but times out on real devices |
/addZoneSlave |
POST | ✅ Complete | Individual slave addition to existing zone |
/removeZoneSlave |
POST | ✅ Complete | Individual slave removal from existing zone |
/audiodspcontrols |
GET/POST | ❌ Missing | DSP audio modes and video sync delay |
/audioproducttonecontrols |
GET/POST | ❌ Missing | Advanced bass/treble controls |
/audioproductlevelcontrols |
GET/POST | ❌ Missing | Speaker level controls (front-center/rear-surround) |
Zone Management Features ✅ NEW
| Feature | Status | Description |
|---|---|---|
| Zone Information | ✅ Complete | Get current zone configuration |
| Zone Status | ✅ Complete | Check master/slave status for device |
| Zone Members | ✅ Complete | List all devices in zone |
| Create Zone | ✅ Complete | Create multiroom zone with multiple devices |
| Add to Zone | ✅ Complete | Add device to existing zone |
| Remove from Zone | ✅ Complete | Remove device from zone |
| Dissolve Zone | ✅ Complete | Break up zone, make devices standalone |
| Zone Builder | ✅ Complete | Fluent API for zone construction |
| Validation | ✅ Complete | IP validation, duplicate detection |
| Error Handling | ✅ Complete | Comprehensive zone error types |
Testing Coverage
- Unit Tests: 200+ test cases covering all functionality
- Integration Tests: Real device testing scenarios
- Benchmark Tests: Performance validation
- WebSocket Tests: Comprehensive event handling tests
- Zone Management Tests: Complete multiroom functionality tests
- Discovery Tests: Multi-protocol device discovery tests
// Run all tests
go test ./... -v
# Run specific test suites
go test ./pkg/client -v -run TestWebSocket
go test ./pkg/models -v -run TestWebSocket
go test ./pkg/client -v -run TestZone
go test ./pkg/models -v -run TestZone
go test ./pkg/discovery -v
# Run benchmarks
go test ./pkg/client -bench=.
go test ./pkg/models -bench=.
Quick Start Examples
Basic HTTP Client
package main
import (
"context"
"fmt"
"log"
"time"
"github.com/gesellix/bose-soundtouch/pkg/client"
"github.com/gesellix/bose-soundtouch/pkg/discovery"
)
func main() {
// Option 1: Connect to known device
soundtouchClient := client.NewClientFromHost("192.168.1.10")
deviceInfo, err := soundtouchClient.GetDeviceInfo()
if err != nil {
log.Fatal(err)
}
fmt.Printf("Device: %s (%s)\n", deviceInfo.Name, deviceInfo.Type)
// Option 2: Discover devices automatically (unified: UPnP + mDNS + config)
cfg, _ := config.LoadFromEnv()
discoveryService := discovery.NewUnifiedDiscoveryService(cfg)
ctx := context.Background()
devices, err := discoveryService.DiscoverDevices(ctx)
if err != nil {
log.Fatal(err)
}
for _, device := range devices {
fmt.Printf("Found: %s at %s:%d\n", device.Name, device.Host, device.Port)
}
// Get current playback status
nowPlaying, err := soundtouchClient.GetNowPlaying()
if err != nil {
log.Fatal(err)
}
if !nowPlaying.IsEmpty() {
fmt.Printf("Now Playing: %s by %s\n",
nowPlaying.GetDisplayTitle(),
nowPlaying.GetDisplayArtist())
}
// Get available audio sources
sources, err := soundtouchClient.GetSources()
if err != nil {
log.Fatal(err)
}
fmt.Printf("Ready Sources: %d/%d\n",
sources.GetReadySourceCount(),
sources.GetSourceCount())
if sources.HasSpotify() {
fmt.Println("Spotify is available")
}
// Get device name
name, err := soundtouchClient.GetName()
if err != nil {
log.Fatal(err)
}
fmt.Printf("Device: %s\n", name.GetName())
// Get device capabilities
capabilities, err := soundtouchClient.GetCapabilities()
if err != nil {
log.Fatal(err)
}
fmt.Printf("L/R Stereo Support: %v\n", capabilities.HasLRStereoCapability())
// Get presets
presets, err := soundtouchClient.GetPresets()
if err != nil {
log.Fatal(err)
}
fmt.Printf("Presets Used: %d/6\n", len(presets.GetUsedPresetSlots()))
// Media controls
fmt.Println("Playing music...")
if err := soundtouchClient.Play(); err != nil {
log.Printf("Failed to play: %v", err)
}
// Volume control
volume, err := soundtouchClient.GetVolume()
if err != nil {
log.Fatal(err)
}
fmt.Printf("Current Volume: %d (%s)\n",
volume.GetLevel(),
volume.GetVolumeString())
// Set comfortable volume
if err := soundtouchClient.SetVolume(25); err != nil {
log.Printf("Failed to set volume: %v", err)
}
}
Project Structure
├── cmd/
│ └── soundtouch-cli/ # CLI application
├── pkg/
│ ├── client/ # HTTP client with XML support
│ ├── discovery/ # Device discovery (UPnP/SSDP + mDNS/Bonjour)
│ └── models/ # XML data models
├── docs/ # Documentation
└── build/ # Build artifacts
Development
Prerequisites
- Go 1.25.5 or later
- Make (optional, for convenience)
Building
# Build CLI tool
make build
# Build for all platforms
make build-all
# Build and run tests
make check
# Run tests with coverage
make test-coverage
Testing
# Run all tests
make test
# Run specific package tests
go test -v ./pkg/client
go test -v ./pkg/discovery
# Test with real devices
make dev-info HOST=192.168.1.10
# Test device discovery
make dev-discover
# Test mDNS discovery example
make dev-mdns
Development Commands
# Format code
make fmt
# Run linter (requires golangci-lint)
make lint
# Clean build artifacts
make clean
# Show help
make help
API Documentation
The SoundTouch Web API uses HTTP with XML payloads. Key endpoints include:
GET /info- Device information ✅ ImplementedGET /name- Device name ✅ ImplementedGET /capabilities- Device capabilities ✅ ImplementedGET /presets- Configured presets ✅ ImplementedGET /now_playing- Current playback status ✅ ImplementedGET /sources- Available audio sources ✅ ImplementedPOST /key- Send key commands (play, pause, etc.) ✅ ImplementedGET/POST /volume- Volume control ✅ ImplementedGET/POST /bass- Bass control ✅ ImplementedGET/POST /balance- Stereo balance control ✅ ImplementedPOST /select- Source selection ✅ ImplementedGET/POST /clockTime- Device time management ✅ ImplementedGET/POST /clockDisplay- Clock display settings ✅ ImplementedGET /networkInfo- Network information ✅ Implemented- WebSocket
/- Real-time event stream
For complete API documentation, see:
- API Endpoints Overview
- Key Controls Documentation
- Volume Controls Documentation
- Host:Port Parsing Feature
Configuration Options
The application supports configuration through .env files and environment variables:
| Variable | Default | Description |
|---|---|---|
DISCOVERY_TIMEOUT |
5s |
Timeout for device discovery |
UPNP_ENABLED |
true |
Enable/disable UPnP/SSDP discovery |
MDNS_ENABLED |
true |
Enable/disable mDNS/Bonjour discovery |
PREFERRED_DEVICES |
(empty) | Semicolon-separated list of devices |
HTTP_TIMEOUT |
10s |
HTTP client timeout |
CACHE_ENABLED |
true |
Enable device caching |
CACHE_TTL |
30s |
Cache time-to-live |
Device Configuration Format
The PREFERRED_DEVICES environment variable supports multiple formats:
# Host only (uses default port 8090)
PREFERRED_DEVICES="192.168.1.10"
# Host with port
PREFERRED_DEVICES="192.168.1.10:8091"
# Named device
PREFERRED_DEVICES="Living Room@192.168.1.10"
# Multiple devices
PREFERRED_DEVICES="Living Room@192.168.1.10;Kitchen@192.168.1.11:8091"
Supported Devices
Tested with:
- Bose SoundTouch 10
- Bose SoundTouch 20
Should work with all SoundTouch series devices that support the Web API.
Real Device Examples
SoundTouch 10 Response
<info deviceID="ABCD1234EFGH">
<name>My SoundTouch Device</name>
<type>SoundTouch 10</type>
<moduleType>sm2</moduleType>
<variant>rhino</variant>
<components>
<component>
<componentCategory>SCM</componentCategory>
<softwareVersion>27.0.6.46330.5043500</softwareVersion>
</component>
</components>
</info>
SoundTouch 20 Response
<info deviceID="ABCD1234EFGH">
<name>My SoundTouch Device</name>
<type>SoundTouch 20</type>
<moduleType>scm</moduleType>
<variant>spotty</variant>
<components>
<component>
<componentCategory>SCM</componentCategory>
<softwareVersion>27.0.6.46330.5043500</softwareVersion>
</component>
<component>
<componentCategory>Lightswitch</componentCategory>
</component>
</components>
</info>
Architecture
This project follows modern Go patterns:
- Clean Architecture: Separated concerns with pkg structure
- Interface-Based Design: Testable and mockable components
- Cross-Platform: Supports Windows, macOS, Linux, and WASM
- Test-Driven: Comprehensive unit and integration tests
- Real Device Integration: Tested with actual SoundTouch hardware
Contributing
- Fork the repository
- Create a feature branch
- Add tests for new functionality
- Ensure all tests pass:
make check - Submit a pull request
Development Guidelines
- Tests are mandatory: Every feature needs corresponding tests
- KISS principle: Keep implementations simple and readable
- Small iterations: Break large features into testable chunks
- Real device testing: Validate against actual SoundTouch devices
- Cross-platform compatibility: Test on multiple platforms
License
This project is licensed under the MIT License - see the LICENSE file for details.
References
This project is based on the official Bose SoundTouch Web API documentation provided by Bose Corporation:
- API Documentation Source: SoundTouch Web API PDF
- Official Bose Page: SoundTouch End-of-Life Information
- Local Copy of API Documentation
- Project Development Plan
- Development Guidelines
Note: This is an independent implementation based on publicly available API documentation. This project is not affiliated with or endorsed by Bose Corporation.