# SoundTouch Troubleshooting Guide **Complete guide to diagnosing and fixing common SoundTouch Go client issues** This guide helps you quickly identify and resolve problems with the SoundTouch Go client library. Issues are organized by category with step-by-step solutions. ## 🚨 **Quick Diagnostics** ### Test Your Setup Run these commands to quickly diagnose your setup: ```bash # 1. Test discovery go run ./cmd/soundtouch-cli -discover # 2. Test specific device connection go run ./cmd/soundtouch-cli -host 192.168.1.100 -info # 3. Test basic controls go run ./cmd/soundtouch-cli -host 192.168.1.100 -volume # 4. Test network connectivity ping 192.168.1.100 ``` --- ## 🔍 **Discovery Issues** ### ❌ "No devices found" **Symptoms:** ``` 🔍 Discovering SoundTouch devices... ❌ No devices found on the network ``` **Causes & Solutions:** #### 1. **Network Configuration** ```bash # Check if devices are on same network ip route show default # Your gateway arp -a | grep -i bose # Look for Bose devices ``` **Solution:** Ensure both your computer and SoundTouch are on the same subnet. #### 2. **Firewall Issues** ```bash # Check if firewall is blocking UPnP sudo ufw status # Ubuntu netsh advfirewall show allprofiles # Windows ``` **Solution:** Allow UPnP traffic (port 1900 UDP) or temporarily disable firewall. #### 3. **Device Not Ready** - Power cycle your SoundTouch device - Wait 30 seconds for full boot - Check device is connected to network (solid white LED) #### 4. **Discovery Timeout Too Short** ```go discoverer := discovery.NewDiscoverer(discovery.Config{ Timeout: 30 * time.Second, // Increase timeout }) ``` #### 5. **Use Manual IP** ```go // Bypass discovery entirely client := client.NewClientFromHost("192.168.1.100") ``` ### ❌ "Discovery timeout" **Symptoms:** ``` 🔍 Discovering SoundTouch devices (timeout: 5s)... ❌ Discovery failed: context deadline exceeded ``` **Solutions:** 1. **Increase timeout:** ```go discoverer := discovery.NewDiscoverer(discovery.Config{ Timeout: 15 * time.Second, }) ``` 2. **Check network performance:** ```bash # Test network latency ping -c 4 192.168.1.1 # Check for network congestion iperf3 -c 192.168.1.1 # If iperf server available ``` 3. **Use wired connection if possible** --- ## 🌐 **Connection Issues** ### ❌ "Connection refused" **Symptoms:** ```go Failed to connect: dial tcp 192.168.1.100:8090: connection refused ``` **Diagnostic Steps:** #### 1. **Verify IP and Port** ```bash # Test if port 8090 is open telnet 192.168.1.100 8090 # OR nc -zv 192.168.1.100 8090 # Scan for open ports nmap -p 8080-8100 192.168.1.100 ``` #### 2. **Check Device Status** - Device LED should be solid white (connected) - Blinking white = connecting - Red = error state #### 3. **Router/Network Issues** ```bash # Check routing traceroute 192.168.1.100 # Test basic connectivity ping -c 4 192.168.1.100 ``` ### ❌ "Timeout" / "Context deadline exceeded" **Symptoms:** ```go Failed to get device info: context deadline exceeded ``` **Solutions:** #### 1. **Increase Client Timeout** ```go config := client.ClientConfig{ Host: "192.168.1.100", Port: 8090, Timeout: 30 * time.Second, // Increase from default 10s } ``` #### 2. **Check Network Latency** ```bash # Test response time ping -c 10 192.168.1.100 # Should be < 100ms typically ``` #### 3. **Device Performance Issues** - Device may be overloaded - Try power cycling the device - Check for firmware updates via Bose app ### ❌ "No such host" **Symptoms:** ```go Failed to connect: dial tcp: lookup soundtouch.local: no such host ``` **Solutions:** 1. **Use IP instead of hostname:** ```go client := client.NewClientFromHost("192.168.1.100") // Not "soundtouch.local" ``` 2. **Fix DNS/mDNS:** ```bash # Test hostname resolution nslookup soundtouch.local dig soundtouch.local # Install mDNS tools if needed (Linux) sudo apt-get install avahi-utils avahi-resolve -n soundtouch.local ``` --- ## 🎵 **Playback Control Issues** ### ❌ "Play/Pause not working" **Symptoms:** - Commands succeed but no audio change - Device shows wrong status **Diagnostic Steps:** #### 1. **Check Current Status** ```go nowPlaying, err := client.GetNowPlaying() if err == nil { fmt.Printf("Status: %s, Source: %s\n", nowPlaying.PlayStatus, nowPlaying.Source) } ``` #### 2. **Verify Source Selection** ```go sources, err := client.GetSources() if err == nil { for _, source := range sources.Sources { fmt.Printf("Source: %s, Status: %s\n", source.Source, source.Status) } } ``` **Solutions:** 1. **Select active source first:** ```go client.SelectSpotify() time.Sleep(2 * time.Second) // Wait for source change client.Play() ``` 2. **Use key commands instead:** ```go client.SendKey("PLAY") // Instead of client.Play() client.SendKey("PAUSE") // Instead of client.Pause() ``` 3. **Check device isn't in setup mode** ### ❌ "Source selection fails" **Symptoms:** ```go Failed to select source: API request failed with status 500 ``` **Solutions:** 1. **Check source availability:** ```go sources, _ := client.GetSources() for _, source := range sources.Sources { if source.Source == "SPOTIFY" && source.Status == "READY" { // Source is available client.SelectSource("SPOTIFY", source.SourceAccount) } } ``` 2. **Account-specific sources:** ```go // For streaming services, include account client.SelectSource("SPOTIFY", "your_account_id") ``` 3. **Use convenience methods:** ```go client.SelectSpotify() // Handles account automatically client.SelectBluetooth() client.SelectAux() ``` --- ## 🔊 **Volume & Audio Issues** ### ❌ "Volume control not working" **Symptoms:** - Volume commands succeed but no change - "Permission denied" errors **Diagnostic Steps:** #### 1. **Check Zone Status** ```go zoneStatus, err := client.GetZoneStatus() if err == nil { fmt.Printf("Zone Status: %s\n", zoneStatus) } ``` **Solutions:** 1. **Zone Member Issue:** ```go // Only zone master can control volume if zoneStatus == "MEMBER" { fmt.Println("Device is zone member - only master controls volume") // Find and use master device zone, _ := client.GetZone() // Connect to master device using zone.Master ID } ``` 2. **Use Safe Volume Methods:** ```go client.SetVolumeSafe(50) // Clamps to valid range client.IncreaseVolume(5) // Incremental control client.DecreaseVolume(5) ``` 3. **Check Current Volume:** ```go volume, _ := client.GetVolume() fmt.Printf("Target: %d, Actual: %d, Muted: %t\n", volume.TargetVolume, volume.ActualVolume, volume.Muted) ``` ### ❌ "Bass/Balance control not supported" **Symptoms:** ```go Failed to set bass: API request failed with status 404 ``` **Solutions:** 1. **Check device capabilities:** ```go caps, err := client.GetCapabilities() if err == nil { fmt.Printf("Bass capable: %t\n", caps.BassCapable) } ``` 2. **Use safe methods:** ```go client.SetBassSafe(-5) // Won't fail on unsupported devices client.SetBalanceSafe(10) // Falls back gracefully ``` 3. **Device-specific features:** - SoundTouch 10: Basic bass only - SoundTouch 20/30: Full bass and balance - Soundbar models: Advanced audio controls --- ## 🔔 **Speaker Notification Issues** ### ❌ "speaker beep" command fails with status 400 **Symptoms:** ```bash $ go run ./cmd/soundtouch-cli --host 192.168.178.35 sp beep Playing notification beep from 192.168.178.35:8090... ✗ Failed to play notification beep: API request failed with status 400 ``` **Cause:** This was a bug in earlier versions where the Go client incorrectly used POST instead of GET for the `/playNotification` endpoint. **Solution:** Update to the latest version. The fix changed the `PlayNotificationBeep()` method to use GET requests: ```go // Fixed implementation (v2025.02+) func (c *Client) PlayNotificationBeep() error { var status models.StationResponse return c.get("/playNotification", &status) } ``` **Verification:** Both commands should now work identically: ```bash # CLI command go run ./cmd/soundtouch-cli --host 192.168.178.35 sp beep # Direct curl (for comparison) curl http://192.168.178.35:8090/playNotification ``` ### ❌ "speaker" commands not supported **Symptoms:** ``` ✗ Failed to play notification: endpoint not supported ``` **Causes & Solutions:** #### 1. **Device Model Compatibility** - ✅ **Supported**: SoundTouch 10 (ST-10), SoundTouch 20 (ST-20) - ❌ **Not Supported**: SoundTouch 300 (ST-300), older models **Solution:** Verify device model with: ```bash soundtouch-cli --host info ``` #### 2. **Missing App Key (TTS/URL only)** TTS and URL playback require an app key, but beep does not: ```bash # Beep - no app key needed soundtouch-cli --host speaker beep # TTS - app key required soundtouch-cli --host speaker tts --text "Hello" --app-key "your-key" ``` ### ❌ "Device is busy" during notifications **Symptoms:** ``` ✗ Failed to play notification: device is busy ``` **Solutions:** #### 1. **Wait for Current Notification to Complete** Only one notification can play at a time. Wait a few seconds and retry. #### 2. **Check Current Playback Status** ```go nowPlaying, _ := client.GetNowPlaying() fmt.Printf("Current source: %s, status: %s\n", nowPlaying.Source, nowPlaying.PlayStatus) ``` --- ## 📡 **WebSocket Issues** ### ❌ "WebSocket connection failed" **Symptoms:** ```go Failed to connect WebSocket: dial ws://192.168.1.100:8080/: connection refused ``` **Solutions:** #### 1. **Verify WebSocket Port (8080)** ```bash # WebSocket uses port 8080, not 8090 nc -zv 192.168.1.100 8080 ``` #### 2. **Check Protocol Specification** ```go // WebSocket client should auto-handle this wsClient := client.NewWebSocketClient(nil) // Manual connection (if needed) url := "ws://192.168.1.100:8080/" headers := http.Header{} headers.Set("Sec-WebSocket-Protocol", "gabbo") ``` #### 3. **Connection Conflicts** - Only one WebSocket connection per device - Close other apps using SoundTouch - Restart SoundTouch device if needed ### ❌ "WebSocket disconnects frequently" **Symptoms:** - Connection drops every few minutes - Constant reconnection messages **Solutions:** 1. **Increase ping interval:** ```go config := client.DefaultWebSocketConfig() config.PingInterval = 60 * time.Second // Increase from 30s config.PongTimeout = 20 * time.Second // Increase timeout wsClient := client.NewWebSocketClient(config) ``` 2. **Check network stability:** ```bash # Test for packet loss ping -c 100 192.168.1.100 | grep loss ``` 3. **Power management issues:** ```bash # Disable WiFi power saving (Linux) sudo iwconfig wlan0 power off # Check Windows power management powercfg -devicequery wake_armed ``` ### ❌ "Events not received" **Symptoms:** - WebSocket connects but no events - Missing volume/playback updates **Solutions:** 1. **Verify event handlers:** ```go wsClient.OnVolumeUpdated(func(event *models.VolumeUpdatedEvent) { fmt.Printf("Volume event received: %d\n", event.Volume.TargetVolume) }) // Test by manually changing volume on device ``` 2. **Check event parsing:** ```go wsClient.OnUnknownEvent(func(event *models.WebSocketEvent) { fmt.Printf("Unknown event: %+v\n", event) }) ``` 3. **Device activity required:** - Events only sent when device state changes - Try manual volume/source changes - Check device isn't in standby --- ## 👥 **Multiroom Issues** ### ❌ "Zone creation fails" **Symptoms:** ```go Failed to create zone: API request failed with status 400 ``` **Solutions:** #### 1. **Check Device Compatibility** ```go // Get device capabilities caps, _ := client.GetCapabilities() // Look for multiroom support // Verify devices are on same network for _, client := range clients { network, _ := client.GetNetworkInfo() fmt.Printf("Device IP: %s\n", network.GetConnectedInterface().IPAddress) } ``` #### 2. **Correct Device IDs** ```go // Get exact device IDs info, _ := client.GetDeviceInfo() masterID := info.DeviceID // Use this, not MAC address // Create zone with proper IDs client.CreateZone(masterID, []string{member1ID, member2ID}) ``` #### 3. **Sequential Zone Operations** ```go // Don't create multiple zones simultaneously client1.CreateZone(master1, []string{member1}) time.Sleep(2 * time.Second) client2.CreateZone(master2, []string{member2}) ``` ### ❌ "Device won't join zone" **Symptoms:** - Zone creation succeeds but member doesn't join - Member device shows as standalone **Solutions:** 1. **Check device status:** ```go status, _ := memberClient.GetZoneStatus() fmt.Printf("Member status: %s\n", status) if status == "STANDALONE" { // Device didn't join - check network/permissions } ``` 2. **Firmware compatibility:** - Ensure all devices have recent firmware - Update via Bose SoundTouch app - Some very old devices don't support multiroom 3. **Network subnet issues:** ```bash # Verify devices can reach each other ping -c 4 member_device_ip ``` --- ## 🔧 **Development & Debugging** ### Enable Detailed Logging ```go import "log" // Enable verbose HTTP logging log.SetFlags(log.LstdFlags | log.Lshortfile) // Custom HTTP client with debug transport := &http.Transport{ // Add debug transport if needed } config := client.ClientConfig{ Host: "192.168.1.100", Port: 8090, Timeout: 10 * time.Second, } ``` ### Debug WebSocket Events ```go wsClient.OnUnknownEvent(func(event *models.WebSocketEvent) { log.Printf("Raw event: %+v", event) }) // Enable WebSocket debug logging config := client.DefaultWebSocketConfig() config.Logger = &client.DefaultLogger{} // Or custom logger ``` ### Network Debugging Tools ```bash # Capture SoundTouch traffic sudo tcpdump -i any host 192.168.1.100 and port 8090 # Monitor WebSocket traffic sudo tcpdump -i any host 192.168.1.100 and port 8080 # HTTP debugging with curl curl -v http://192.168.1.100:8090/info curl -v http://192.168.1.100:8090/volume ``` --- ## 📊 **Performance Issues** ### High Memory Usage **Symptoms:** - Go process memory keeps growing - Out of memory errors in long-running apps **Solutions:** 1. **Connection cleanup:** ```go // Always close WebSocket connections defer wsClient.Disconnect() // Use connection pools for multiple devices pool := NewConnectionPool(10, 5*time.Minute) defer pool.Close() ``` 2. **Goroutine leaks:** ```go // Use context for cancellation ctx, cancel := context.WithCancel(context.Background()) defer cancel() // Monitor goroutines go func() { for { fmt.Printf("Goroutines: %d\n", runtime.NumGoroutine()) time.Sleep(10 * time.Second) } }() ``` ### Slow Response Times **Solutions:** 1. **Increase timeouts appropriately:** ```go config := client.ClientConfig{ Timeout: 15 * time.Second, // Reasonable for network ops } ``` 2. **Use connection pooling:** ```go // Reuse connections instead of creating new ones pool := NewConnectionPool(5, 5*time.Minute) client := pool.GetClient(host, port) ``` 3. **Concurrent operations:** ```go // Process multiple devices concurrently var wg sync.WaitGroup for _, client := range clients { wg.Add(1) go func(c *client.Client) { defer wg.Done() // Process device }(client) } wg.Wait() ``` --- ## 🚨 **Emergency Procedures** ### Device Becomes Unresponsive 1. **Power cycle device:** - Unplug for 10 seconds - Reconnect and wait 30 seconds for boot 2. **Network reset:** - Hold Bluetooth and Volume Down for 10 seconds - Device will reset network settings 3. **Factory reset (last resort):** - Hold Power for 10 seconds while plugged in - Will lose all presets and settings ### Multiple Devices Acting Strange 1. **Check router:** - Restart router/access point - Check for firmware updates - Verify DHCP/IP assignment 2. **Network interference:** - Check for 2.4GHz interference - Try 5GHz WiFi if available - Check for microwave/Bluetooth interference ### App Crashes or Hangs 1. **Graceful shutdown:** ```go // Always use context for cancellation ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) defer cancel() // Cleanup resources defer func() { if wsClient != nil { wsClient.Disconnect() } }() ``` 2. **Resource monitoring:** ```go // Monitor resource usage go func() { var m runtime.MemStats for { runtime.ReadMemStats(&m) log.Printf("Alloc = %d KB, Sys = %d KB", m.Alloc/1024, m.Sys/1024) time.Sleep(30 * time.Second) } }() ``` --- ## 📋 **Diagnostic Checklist** Use this checklist to systematically troubleshoot issues: ### Network Connectivity - [ ] Device power LED is solid white - [ ] Both devices on same network subnet - [ ] Firewall allows ports 8090 (HTTP) and 8080 (WebSocket) - [ ] Can ping device IP address - [ ] Can telnet to ports 8090 and 8080 ### Device Status - [ ] Device not in setup mode (solid white LED) - [ ] Recent firmware version (check Bose app) - [ ] Device responds to Bose app - [ ] No other apps connected to device ### Code Configuration - [ ] Correct IP address and ports - [ ] Reasonable timeouts (10-30 seconds) - [ ] Proper error handling - [ ] Resource cleanup (defer statements) ### Multiroom Specific - [ ] All devices support multiroom - [ ] Device IDs are correct (from GetDeviceInfo) - [ ] Devices on same network subnet - [ ] No existing zone conflicts --- ## 🛟 **Getting More Help** ### Information to Gather When reporting issues, include: ```go // Device information info, _ := client.GetDeviceInfo() fmt.Printf("Device: %s %s (ID: %s)\n", info.Type, info.Name, info.DeviceID) // Network information network, _ := client.GetNetworkInfo() fmt.Printf("Network: %+v\n", network) // Go version and OS fmt.Printf("Go version: %s\n", runtime.Version()) fmt.Printf("OS: %s/%s\n", runtime.GOOS, runtime.GOARCH) ``` ### Useful Commands ```bash # System information go version uname -a # Linux/macOS systeminfo # Windows # Network debugging ip addr show # Linux ifconfig # macOS ipconfig /all # Windows # SoundTouch specific go run ./cmd/soundtouch-cli -host -info go run ./cmd/soundtouch-cli -host -network-info ``` ### Support Resources - **GitHub Issues**: Create detailed issue with logs and system info - **Documentation**: Check `/docs` directory for specific topics - **Examples**: Review `/examples` for working code patterns - **CLI Tool**: Use built-in CLI for testing and debugging Remember: Most issues are network-related. Start with basic connectivity testing before investigating code issues.