# 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](https://assets.bosecreative.com/m/496577402d128874/original/SoundTouch-Web-API.pdf) provided by Bose Corporation at their [SoundTouch End-of-Life page](https://www.bose.de/de_de/landing_pages/soundtouch-eol.html). This is an independent project and is not affiliated with or endorsed by Bose Corporation. ## Features ### ✅ Implemented (84% Complete - 16/19 official endpoints) - **HTTP Client with XML Support**: Complete client for SoundTouch Web API - **Device Information**: Get detailed device info via `/info` endpoint - **Device Name**: Get device name via `/name` endpoint - **Device Capabilities**: Get device capabilities via `/capabilities` endpoint - **Configured Presets**: Get preset configurations via `/presets` endpoint - **Now Playing Status**: Get current playback information via `/now_playing` endpoint - **Audio Sources**: Get available sources via `/sources` endpoint - **Media Controls**: Play, pause, stop, track navigation via `/key` endpoint - **Volume Management**: Get/set volume, incremental control via `/volume` endpoint - **Bass Control**: Get/set bass levels (-9 to +9 range) via `/bass` endpoint - **Balance Control**: Get/set balance (-50 to +50 range) via `/balance` endpoint - **Clock/Time Management**: Get/set device time via `/clockTime` and `/clockDisplay` endpoints - **Network Information**: Get network details via `/networkInfo` endpoint - **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: ```bash # 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 ```bash go install github.com/gesellix/bose-soundtouch/cmd/soundtouch-cli@latest ``` ### From Source ```bash 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: ```bash # Copy the example file cp .env.example .env ``` Example `.env` configuration: ```bash # 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 `.env` file (fastest, most reliable) - **UPnP/SSDP**: Universal Plug and Play discovery (widely supported) - **mDNS/Bonjour**: Multicast DNS discovery (Apple ecosystem friendly) See [docs/DISCOVERY.md](docs/DISCOVERY.md) for detailed information. ```bash # 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: ```bash # 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](docs/websocket-events.md) for complete WebSocket documentation. #### Device Information ```bash # 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 ```bash # 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 ```bash # 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 ```bash # 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 ```bash # 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 ```bash # 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 ```bash # 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 ```bash # 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 ```go 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 ```go 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 ```go 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 ```go 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 | ✅ Complete | Track information (duplicate of /now_playing) | | `/addZoneSlave` | POST | ❌ Missing | **Individual slave addition (use AddToZone instead)** | | `/removeZoneSlave` | POST | ❌ Missing | **Individual slave removal (use RemoveFromZone instead)** | | `/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 ```go // 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 ```go 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 ```bash # 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 ```bash # 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 ```bash # 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 ✅ Implemented - `GET /name` - Device name ✅ Implemented - `GET /capabilities` - Device capabilities ✅ Implemented - `GET /presets` - Configured presets ✅ Implemented - `GET /now_playing` - Current playback status ✅ Implemented - `GET /sources` - Available audio sources ✅ Implemented - `POST /key` - Send key commands (play, pause, etc.) ✅ Implemented - `GET/POST /volume` - Volume control ✅ Implemented - `GET/POST /bass` - Bass control ✅ Implemented - `GET/POST /balance` - Stereo balance control ✅ Implemented - `POST /select` - Source selection ✅ Implemented - `GET/POST /clockTime` - Device time management ✅ Implemented - `GET/POST /clockDisplay` - Clock display settings ✅ Implemented - `GET /networkInfo` - Network information ✅ Implemented - WebSocket `/` - Real-time event stream For complete API documentation, see: - [API Endpoints Overview](docs/API-Endpoints-Overview.md) - [Key Controls Documentation](docs/KEY-CONTROLS.md) - [Volume Controls Documentation](docs/VOLUME-CONTROLS.md) - [Host:Port Parsing Feature](docs/HOST-PORT-PARSING.md) ## 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: ```bash # 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 ```xml My SoundTouch Device SoundTouch 10 sm2 rhino SCM 27.0.6.46330.5043500 ``` ### SoundTouch 20 Response ```xml My SoundTouch Device SoundTouch 20 scm spotty SCM 27.0.6.46330.5043500 Lightswitch ``` ## 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 1. Fork the repository 2. Create a feature branch 3. Add tests for new functionality 4. Ensure all tests pass: `make check` 5. 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](https://assets.bosecreative.com/m/496577402d128874/original/SoundTouch-Web-API.pdf) - **Official Bose Page**: [SoundTouch End-of-Life Information](https://www.bose.de/de_de/landing_pages/soundtouch-eol.html) - [Local Copy of API Documentation](docs/2025.12.18%20SoundTouch%20Web%20API.pdf) - [Project Development Plan](docs/PLAN.md) - [Development Guidelines](docs/CLAUDE.md) **Note**: This is an independent implementation based on publicly available API documentation. This project is not affiliated with or endorsed by Bose Corporation.