mirror of
https://github.com/gesellix/Bose-SoundTouch.git
synced 2026-08-19 00:56:16 +00:00
Add comprehensive documentation and polish
Major documentation additions: - GETTING-STARTED.md: Complete 10-minute tutorial from discovery to WebSocket monitoring - API-COOKBOOK.md: 1000+ lines of real-world patterns, recipes, and best practices - TROUBLESHOOTING.md: Systematic guide for diagnosing and fixing common issues - DEPLOYMENT.md: Production-ready deployment patterns and operational guidance Key highlights: - Step-by-step examples with working code - Error handling and resilience patterns - Performance optimization strategies - Security and monitoring best practices - Docker, Kubernetes, and systemd deployment examples - Complete troubleshooting checklist with diagnostic commands - Production-ready configuration management This represents a significant investment in developer experience and makes the SoundTouch Go client accessible to developers of all experience levels.
This commit is contained in:
File diff suppressed because it is too large
Load Diff
+1074
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,491 @@
|
||||
# Getting Started with SoundTouch Go Client
|
||||
|
||||
**A complete guide to controlling your Bose SoundTouch devices with Go**
|
||||
|
||||
This guide will get you up and running with the SoundTouch Go client in under 10 minutes. By the end, you'll be able to discover devices, control playback, manage volume, and monitor real-time events.
|
||||
|
||||
## 📋 **Prerequisites**
|
||||
|
||||
- **Go 1.19 or later** installed on your system
|
||||
- **Bose SoundTouch device** on your network (SoundTouch 10, 20, 30, etc.)
|
||||
- **Same network** - Your computer and SoundTouch device must be on the same network
|
||||
|
||||
## 🚀 **Quick Start**
|
||||
|
||||
### Step 1: Create a New Go Project
|
||||
|
||||
```bash
|
||||
mkdir soundtouch-example
|
||||
cd soundtouch-example
|
||||
go mod init soundtouch-example
|
||||
```
|
||||
|
||||
### Step 2: Add the SoundTouch Client
|
||||
|
||||
```bash
|
||||
go get github.com/user_account/bose-soundtouch
|
||||
```
|
||||
|
||||
### Step 3: Find Your Device
|
||||
|
||||
Create `main.go`:
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"time"
|
||||
|
||||
"github.com/user_account/bose-soundtouch/pkg/discovery"
|
||||
)
|
||||
|
||||
func main() {
|
||||
// Discover devices on your network
|
||||
discoverer := discovery.NewDiscoverer(discovery.Config{
|
||||
Timeout: 10 * time.Second,
|
||||
})
|
||||
|
||||
fmt.Println("🔍 Discovering SoundTouch devices...")
|
||||
devices, err := discoverer.DiscoverDevices()
|
||||
if err != nil {
|
||||
log.Fatalf("Discovery failed: %v", err)
|
||||
}
|
||||
|
||||
if len(devices) == 0 {
|
||||
fmt.Println("❌ No devices found. Make sure your SoundTouch is on and connected.")
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Printf("✅ Found %d device(s):\n", len(devices))
|
||||
for i, device := range devices {
|
||||
fmt.Printf("%d. %s at %s:%d\n", i+1, device.Name, device.Host, device.Port)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Run it:
|
||||
```bash
|
||||
go run main.go
|
||||
```
|
||||
|
||||
You should see your SoundTouch device(s) listed!
|
||||
|
||||
### Step 4: Control Your Device
|
||||
|
||||
Now let's add basic control functionality:
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"time"
|
||||
|
||||
"github.com/user_account/bose-soundtouch/pkg/client"
|
||||
"github.com/user_account/bose-soundtouch/pkg/discovery"
|
||||
)
|
||||
|
||||
func main() {
|
||||
// Discover and connect to first device
|
||||
discoverer := discovery.NewDiscoverer(discovery.Config{
|
||||
Timeout: 5 * time.Second,
|
||||
})
|
||||
|
||||
devices, err := discoverer.DiscoverDevices()
|
||||
if err != nil || len(devices) == 0 {
|
||||
log.Fatal("No devices found")
|
||||
}
|
||||
|
||||
// Connect to the first device
|
||||
soundtouch := client.NewClient(client.ClientConfig{
|
||||
Host: devices[0].Host,
|
||||
Port: devices[0].Port,
|
||||
Timeout: 10 * time.Second,
|
||||
})
|
||||
|
||||
// Get device information
|
||||
info, err := soundtouch.GetDeviceInfo()
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to connect: %v", err)
|
||||
}
|
||||
|
||||
fmt.Printf("🎵 Connected to: %s\n", info.Name)
|
||||
fmt.Printf(" Type: %s\n", info.Type)
|
||||
fmt.Printf(" ID: %s\n", info.DeviceID)
|
||||
|
||||
// Basic controls
|
||||
fmt.Println("\n🎮 Testing basic controls...")
|
||||
|
||||
// Set volume to 30
|
||||
fmt.Println("Setting volume to 30...")
|
||||
if err := soundtouch.SetVolume(30); err != nil {
|
||||
fmt.Printf("Volume control failed: %v\n", err)
|
||||
}
|
||||
|
||||
// Play
|
||||
fmt.Println("Sending PLAY command...")
|
||||
if err := soundtouch.Play(); err != nil {
|
||||
fmt.Printf("Play failed: %v\n", err)
|
||||
}
|
||||
|
||||
// Wait a moment
|
||||
time.Sleep(2 * time.Second)
|
||||
|
||||
// Pause
|
||||
fmt.Println("Sending PAUSE command...")
|
||||
if err := soundtouch.Pause(); err != nil {
|
||||
fmt.Printf("Pause failed: %v\n", err)
|
||||
}
|
||||
|
||||
// Get current status
|
||||
nowPlaying, err := soundtouch.GetNowPlaying()
|
||||
if err == nil {
|
||||
fmt.Printf("\n📊 Current Status:\n")
|
||||
fmt.Printf(" Source: %s\n", nowPlaying.Source)
|
||||
if nowPlaying.Track != "" {
|
||||
fmt.Printf(" Track: %s\n", nowPlaying.Track)
|
||||
}
|
||||
if nowPlaying.Artist != "" {
|
||||
fmt.Printf(" Artist: %s\n", nowPlaying.Artist)
|
||||
}
|
||||
fmt.Printf(" Status: %s\n", nowPlaying.PlayStatus)
|
||||
}
|
||||
|
||||
fmt.Println("\n✅ Basic setup complete!")
|
||||
}
|
||||
```
|
||||
|
||||
## 📖 **Core Concepts**
|
||||
|
||||
### Device Discovery
|
||||
|
||||
The SoundTouch library supports multiple discovery methods:
|
||||
|
||||
```go
|
||||
// UPnP discovery (default)
|
||||
discoverer := discovery.NewDiscoverer(discovery.Config{
|
||||
Timeout: 10 * time.Second,
|
||||
})
|
||||
devices, err := discoverer.DiscoverDevices()
|
||||
|
||||
// Or connect directly if you know the IP
|
||||
soundtouch := client.NewClientFromHost("192.168.1.100")
|
||||
```
|
||||
|
||||
### Client Configuration
|
||||
|
||||
```go
|
||||
config := client.ClientConfig{
|
||||
Host: "192.168.1.100",
|
||||
Port: 8090, // Default SoundTouch port
|
||||
Timeout: 10 * time.Second,
|
||||
UserAgent: "MyApp/1.0", // Optional
|
||||
}
|
||||
soundtouch := client.NewClient(config)
|
||||
```
|
||||
|
||||
### Error Handling
|
||||
|
||||
Always check for errors, especially with network operations:
|
||||
|
||||
```go
|
||||
volume, err := soundtouch.GetVolume()
|
||||
if err != nil {
|
||||
log.Printf("Failed to get volume: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Printf("Current volume: %d\n", volume.TargetVolume)
|
||||
if volume.Muted {
|
||||
fmt.Println("Device is muted")
|
||||
}
|
||||
```
|
||||
|
||||
## 🎵 **Common Operations**
|
||||
|
||||
### Playback Control
|
||||
|
||||
```go
|
||||
// Basic playback
|
||||
soundtouch.Play()
|
||||
soundtouch.Pause()
|
||||
soundtouch.Stop()
|
||||
|
||||
// Navigation
|
||||
soundtouch.NextTrack()
|
||||
soundtouch.PrevTrack()
|
||||
|
||||
// Power and mute
|
||||
soundtouch.SendKey("POWER")
|
||||
soundtouch.SendKey("MUTE")
|
||||
```
|
||||
|
||||
### Volume Management
|
||||
|
||||
```go
|
||||
// Get current volume
|
||||
volume, err := soundtouch.GetVolume()
|
||||
if err == nil {
|
||||
fmt.Printf("Volume: %d, Muted: %t\n", volume.TargetVolume, volume.Muted)
|
||||
}
|
||||
|
||||
// Set volume (0-100)
|
||||
soundtouch.SetVolume(50)
|
||||
|
||||
// Incremental control
|
||||
soundtouch.VolumeUp()
|
||||
soundtouch.VolumeDown()
|
||||
|
||||
// Safe volume setting (clamps to valid range)
|
||||
soundtouch.SetVolumeSafe(150) // Will be set to 100
|
||||
```
|
||||
|
||||
### Source Selection
|
||||
|
||||
```go
|
||||
// Get available sources
|
||||
sources, err := soundtouch.GetSources()
|
||||
if err == nil {
|
||||
for _, source := range sources.Sources {
|
||||
fmt.Printf("Source: %s (%s)\n", source.Source, source.Status)
|
||||
}
|
||||
}
|
||||
|
||||
// Select sources
|
||||
soundtouch.SelectSpotify()
|
||||
soundtouch.SelectBluetooth()
|
||||
soundtouch.SelectAux()
|
||||
|
||||
// Or select by name
|
||||
soundtouch.SelectSource("SPOTIFY", "")
|
||||
```
|
||||
|
||||
### Preset Management
|
||||
|
||||
```go
|
||||
// Get presets
|
||||
presets, err := soundtouch.GetPresets()
|
||||
if err == nil {
|
||||
for _, preset := range presets.Presets {
|
||||
fmt.Printf("Preset %d: %s\n", preset.ID, preset.ContentItem.ItemName)
|
||||
}
|
||||
}
|
||||
|
||||
// Select preset (1-6)
|
||||
soundtouch.SelectPreset(1)
|
||||
|
||||
// Or use key command
|
||||
soundtouch.SendKey("PRESET_3")
|
||||
```
|
||||
|
||||
### Device Information
|
||||
|
||||
```go
|
||||
// Basic device info
|
||||
info, _ := soundtouch.GetDeviceInfo()
|
||||
fmt.Printf("Device: %s (%s)\n", info.Name, info.Type)
|
||||
|
||||
// Capabilities
|
||||
caps, _ := soundtouch.GetCapabilities()
|
||||
fmt.Printf("Bass control: %t\n", caps.BassCapable)
|
||||
|
||||
// Network information
|
||||
network, _ := soundtouch.GetNetworkInfo()
|
||||
for _, iface := range network.GetInterfaces() {
|
||||
fmt.Printf("Interface: %s - %s\n", iface.Type, iface.IPAddress)
|
||||
}
|
||||
```
|
||||
|
||||
## 🌐 **Real-time Monitoring**
|
||||
|
||||
One of the most powerful features is real-time event monitoring:
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"os/signal"
|
||||
"syscall"
|
||||
|
||||
"github.com/user_account/bose-soundtouch/pkg/client"
|
||||
)
|
||||
|
||||
func main() {
|
||||
// Connect to device
|
||||
soundtouch := client.NewClientFromHost("192.168.1.100")
|
||||
|
||||
// Create WebSocket client
|
||||
wsClient := soundtouch.NewWebSocketClient(nil)
|
||||
|
||||
// Set up event handlers
|
||||
wsClient.OnNowPlaying(func(event *models.NowPlayingUpdatedEvent) {
|
||||
fmt.Printf("🎵 Now Playing: %s - %s\n",
|
||||
event.NowPlaying.Artist, event.NowPlaying.Track)
|
||||
})
|
||||
|
||||
wsClient.OnVolumeUpdated(func(event *models.VolumeUpdatedEvent) {
|
||||
fmt.Printf("🔊 Volume: %d\n", event.Volume.TargetVolume)
|
||||
})
|
||||
|
||||
// Connect to WebSocket
|
||||
if err := wsClient.Connect(); err != nil {
|
||||
log.Fatalf("WebSocket connection failed: %v", err)
|
||||
}
|
||||
|
||||
fmt.Println("✅ Monitoring events... Press Ctrl+C to exit")
|
||||
|
||||
// Wait for interrupt
|
||||
sigChan := make(chan os.Signal, 1)
|
||||
signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM)
|
||||
<-sigChan
|
||||
|
||||
// Cleanup
|
||||
wsClient.Disconnect()
|
||||
fmt.Println("Disconnected.")
|
||||
}
|
||||
```
|
||||
|
||||
## 👥 **Multiroom Setup**
|
||||
|
||||
Control multiple speakers together:
|
||||
|
||||
```go
|
||||
// Get current zone configuration
|
||||
zone, err := soundtouch.GetZone()
|
||||
if err == nil {
|
||||
fmt.Printf("Zone master: %s\n", zone.Master)
|
||||
fmt.Printf("Members: %d\n", len(zone.Members))
|
||||
}
|
||||
|
||||
// Create a zone (master + members)
|
||||
masterID := "DEVICE123"
|
||||
memberIDs := []string{"DEVICE456", "DEVICE789"}
|
||||
soundtouch.CreateZone(masterID, memberIDs)
|
||||
|
||||
// Add device to existing zone
|
||||
soundtouch.AddToZone("DEVICE999", "192.168.1.15")
|
||||
|
||||
// Remove device from zone
|
||||
soundtouch.RemoveFromZone("DEVICE456")
|
||||
|
||||
// Dissolve zone (make all devices standalone)
|
||||
soundtouch.DissolveZone()
|
||||
```
|
||||
|
||||
## ⚠️ **Common Issues & Solutions**
|
||||
|
||||
### Device Not Found
|
||||
```
|
||||
❌ No devices found
|
||||
```
|
||||
**Solutions:**
|
||||
- Ensure SoundTouch is powered on
|
||||
- Check both devices are on same network
|
||||
- Try specifying IP directly: `client.NewClientFromHost("192.168.1.100")`
|
||||
- Check firewall settings
|
||||
|
||||
### Connection Timeouts
|
||||
```
|
||||
❌ Failed to connect: context deadline exceeded
|
||||
```
|
||||
**Solutions:**
|
||||
- Increase timeout: `Timeout: 30 * time.Second`
|
||||
- Verify IP address and port (default 8090)
|
||||
- Check network connectivity with `ping 192.168.1.100`
|
||||
|
||||
### Volume/Control Issues
|
||||
```
|
||||
❌ Volume control failed
|
||||
```
|
||||
**Solutions:**
|
||||
- Check device isn't in a zone (members can't control volume directly)
|
||||
- Ensure device isn't in setup mode
|
||||
- Try basic commands first (play, pause)
|
||||
|
||||
### WebSocket Connection Issues
|
||||
```
|
||||
❌ WebSocket connection failed
|
||||
```
|
||||
**Solutions:**
|
||||
- WebSocket uses port 8080 (not 8090)
|
||||
- Ensure no other apps are connected
|
||||
- Try disconnecting and reconnecting
|
||||
|
||||
## 🔧 **Development Tips**
|
||||
|
||||
### Enable Debug Logging
|
||||
|
||||
```go
|
||||
// For HTTP requests
|
||||
import "net/http/httputil"
|
||||
|
||||
// Custom transport for debugging
|
||||
transport := &http.Transport{}
|
||||
httpClient := &http.Client{
|
||||
Transport: transport,
|
||||
Timeout: 10 * time.Second,
|
||||
}
|
||||
|
||||
// Use with client...
|
||||
```
|
||||
|
||||
### Testing with CLI Tool
|
||||
|
||||
Use the included CLI for quick testing:
|
||||
|
||||
```bash
|
||||
# Discovery
|
||||
go run ./cmd/soundtouch-cli -discover
|
||||
|
||||
# Device info
|
||||
go run ./cmd/soundtouch-cli -host 192.168.1.100 -info
|
||||
|
||||
# Basic controls
|
||||
go run ./cmd/soundtouch-cli -host 192.168.1.100 -play -volume 50
|
||||
|
||||
# WebSocket monitoring (separate terminal)
|
||||
go run ./cmd/soundtouch-cli -host 192.168.1.100 -monitor
|
||||
```
|
||||
|
||||
### Configuration Management
|
||||
|
||||
```go
|
||||
// Use environment variables
|
||||
import "os"
|
||||
|
||||
host := os.Getenv("SOUNDTOUCH_HOST")
|
||||
if host == "" {
|
||||
host = "192.168.1.100" // fallback
|
||||
}
|
||||
|
||||
soundtouch := client.NewClientFromHost(host)
|
||||
```
|
||||
|
||||
## 📚 **Next Steps**
|
||||
|
||||
Now that you have the basics working:
|
||||
|
||||
1. **Explore Examples**: Check out `/examples` directory for more advanced usage
|
||||
2. **API Reference**: Read `/docs/API-Endpoints-Overview.md` for complete API details
|
||||
3. **WebSocket Events**: See `/docs/websocket-events.md` for real-time monitoring
|
||||
4. **Multiroom Guide**: Check `/docs/zone-management.md` for multiroom setup
|
||||
5. **Production Guide**: Read `/docs/DEPLOYMENT.md` for production considerations
|
||||
|
||||
## 🛟 **Getting Help**
|
||||
|
||||
- **Issues**: Create an issue on GitHub with device model and Go version
|
||||
- **API Reference**: See comprehensive documentation in `/docs`
|
||||
- **Examples**: Check `/examples` directory for working code samples
|
||||
- **CLI Tool**: Use the included CLI for testing and debugging
|
||||
|
||||
## 🎉 **You're Ready!**
|
||||
|
||||
You now have everything needed to build amazing SoundTouch integrations! The library handles all the complexity of device communication, XML parsing, WebSocket management, and error handling - you can focus on building great user experiences.
|
||||
|
||||
**Happy coding!** 🎵
|
||||
@@ -0,0 +1,781 @@
|
||||
# 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
|
||||
|
||||
---
|
||||
|
||||
## 📡 **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 <ip> -info
|
||||
go run ./cmd/soundtouch-cli -host <ip> -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.
|
||||
Reference in New Issue
Block a user