mirror of
https://github.com/gesellix/Bose-SoundTouch.git
synced 2026-08-18 00:26:29 +00:00
The web player is intrinsically LAN-resident: it reaches speakers directly and only delegates cloud-only features (e.g. TTS) to a possibly-remote AfterTouch service via --service-url. That is exactly what a cloud-hosted soundtouch-service cannot do, so the standalone player binary stays useful and is not being deprecated. Rename it to state its purpose, with a transition window so existing downloads keep working. - cmd/soundtouch-web -> cmd/soundtouch-player; CLI name is now soundtouch-player. When the binary is invoked under its old name it prints a one-line rename notice (filepath.Base(os.Args[0])). - Build/release both names from the same source: Makefile (build-player + build-web alias, dev-player* targets), Dockerfile (soundtouch-player image + transitional soundtouch-web image), release.yml and ci.yml (player + web artifacts, checksums, Docker images; release notes announce the rename). The soundtouch-web binary, image, and install script remain a transitional alias to be dropped in a future release (which will break stale fetch scripts and nudge users to the release notes). - scripts/raspberry-pi/install-player.sh is canonical; install-web.sh keeps working but warns. - Sweep docs, code comments, user-facing strings, and assets (soundtouch-web-ui.png, soundtouch-web-tunein.png, soundtouch-web-roadmap.md) to soundtouch-player; README documents the rename and why the player remains separate from the embedded /app. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
788 lines
26 KiB
Markdown
788 lines
26 KiB
Markdown
# Bose SoundTouch API Client - Golang Implementation Plan
|
|
|
|
## Overview
|
|
|
|
This document describes the planning for a Golang-based API client for the Bose SoundTouch Web API. The client follows modern Go patterns and supports both native Go library and WASM integration with embedded web UI.
|
|
|
|
**New insights from pattern analysis:**
|
|
- Single binary deployment with embedded assets
|
|
- Multi-target build system (Native + WASM)
|
|
- CORS proxy pattern for browser integration
|
|
- Robust XML API client patterns
|
|
- Production-ready configuration management
|
|
|
|
## API Fundamentals
|
|
|
|
### Basic Information
|
|
- **Protocol**: HTTP REST-like
|
|
- **Data format**: XML Request/Response
|
|
- **Port**: 8090 (default)
|
|
- **Authentication**: No complex authentication required
|
|
- **Real-time updates**: WebSocket connection available
|
|
- **Device discovery**: UPnP discovery possible
|
|
|
|
### Core API Endpoints
|
|
- `GET /info` - Device information
|
|
- `GET /now_playing` - Currently playing music
|
|
- `POST /key` - Send key commands (PLAY, PAUSE, etc.)
|
|
- `GET/POST /volume` - Control volume
|
|
- `GET/POST /bass` - Bass settings
|
|
- `GET/POST /sources` - Available sources
|
|
- `POST /select` - Select source
|
|
- `GET /presets` - Read presets (1-6) ✅ COMPLETE
|
|
- `POST /storePreset` - Store/update presets ✅ COMPLETE (via SoundTouch Plus Wiki)
|
|
- `POST /removePreset` - Remove presets ✅ COMPLETE (via SoundTouch Plus Wiki)
|
|
- `WebSocket /` - Live updates for events
|
|
|
|
## Architecture Based on Modern Go Patterns
|
|
|
|
### Final Project Structure
|
|
```
|
|
github.com/gesellix/bose-soundtouch/
|
|
├── cmd/
|
|
│ ├── cli/ # CLI Tool (Main Application)
|
|
│ │ └── main.go
|
|
│ ├── webapp/ # Web Application with embedded Assets
|
|
│ │ ├── main.go
|
|
│ │ └── web/ # Embedded HTML/CSS/JS
|
|
│ │ ├── index.html
|
|
│ │ ├── app.js
|
|
│ │ └── style.css
|
|
│ └── wasm/ # WASM Entry Point
|
|
│ └── main.go
|
|
├── pkg/ # Public API (external usage)
|
|
│ ├── client/ # HTTP Client with XML Support
|
|
│ ├── discovery/ # UPnP Device Discovery
|
|
│ ├── models/ # Type-safe XML Data Models
|
|
│ ├── websocket/ # Event Streaming Client
|
|
│ ├── wasm/ # WASM JavaScript Bridge
|
|
│ └── config/ # Configuration Management
|
|
├── internal/ # Private Implementation Details
|
|
│ ├── xml/ # XML Parsing Utilities
|
|
│ ├── http/ # HTTP Utilities & Middleware
|
|
│ └── testing/ # Mock Client & Test Utilities
|
|
├── web/ # Frontend Development Assets
|
|
│ ├── src/ # Source files
|
|
│ └── dist/ # Build output → cmd/webapp/web/
|
|
├── examples/ # Usage Examples & Demos
|
|
├── test/ # Integration Tests & Docker
|
|
├── Makefile # Comprehensive Build System
|
|
├── .env.example # Configuration Template
|
|
├── .air-webapp.toml # Hot Reload Config
|
|
├── .air-wasm.toml # WASM Development Config
|
|
├── docker-compose.yml # Development Environment
|
|
├── PROJECT-PATTERNS.md # Pattern Documentation
|
|
├── API-Endpoints-Overview.md # API Reference
|
|
├── go.mod
|
|
└── README.md
|
|
```
|
|
|
|
### Core Components (Updated)
|
|
|
|
#### 1. HTTP Client with XML Support (`pkg/client`)
|
|
```go
|
|
type Client struct {
|
|
baseURL string
|
|
httpClient *http.Client
|
|
timeout time.Duration
|
|
userAgent string
|
|
}
|
|
|
|
type ClientConfig struct {
|
|
Host string
|
|
Port int
|
|
Timeout time.Duration
|
|
UserAgent string
|
|
}
|
|
|
|
// Core API methods
|
|
func NewClient(config ClientConfig) *Client
|
|
func (c *Client) GetDeviceInfo() (*models.DeviceInfo, error)
|
|
func (c *Client) GetNowPlaying() (*models.NowPlaying, error)
|
|
func (c *Client) SetVolume(volume int) error
|
|
func (c *Client) GetVolume() (*models.Volume, error)
|
|
func (c *Client) SendKey(key models.Key) error
|
|
func (c *Client) GetSources() (*models.Sources, error)
|
|
func (c *Client) SelectSource(source models.ContentItem) error
|
|
func (c *Client) GetPresets() (*models.Presets, error)
|
|
func (c *Client) SetPreset(id int, content models.ContentItem) error
|
|
|
|
// HTTP utilities with XML handling
|
|
func (c *Client) get(endpoint string, result interface{}) error
|
|
func (c *Client) post(endpoint string, data interface{}, result interface{}) error
|
|
```
|
|
|
|
#### 2. UPnP Device Discovery (`pkg/discovery`)
|
|
```go
|
|
type DiscoveryService struct {
|
|
timeout time.Duration
|
|
cache map[string]*Device
|
|
cacheTTL time.Duration
|
|
mutex sync.RWMutex
|
|
}
|
|
|
|
type Device struct {
|
|
Name string `json:"name"`
|
|
Host string `json:"host"`
|
|
Port int `json:"port"`
|
|
ModelID string `json:"modelId"`
|
|
SerialNo string `json:"serialNo"`
|
|
Location string `json:"location"`
|
|
LastSeen time.Time `json:"lastSeen"`
|
|
}
|
|
|
|
func NewDiscoveryService(timeout time.Duration) *DiscoveryService
|
|
func (d *DiscoveryService) DiscoverDevices() ([]Device, error)
|
|
func (d *DiscoveryService) DiscoverDevice(name string) (*Device, error)
|
|
func (d *DiscoveryService) GetCachedDevices() []Device
|
|
func (d *DiscoveryService) ClearCache()
|
|
|
|
// SSDP/UPnP implementation
|
|
func (d *DiscoveryService) sendMSearch() error
|
|
func (d *DiscoveryService) parseResponse(response string) (*Device, error)
|
|
```
|
|
|
|
#### 3. Typsichere XML Models (`pkg/models`)
|
|
```go
|
|
// Base XML response with error handling
|
|
type XMLResponse struct {
|
|
XMLName xml.Name `xml:",innerxml"`
|
|
Error *APIError `xml:"error,omitempty"`
|
|
}
|
|
|
|
type APIError struct {
|
|
Code string `xml:"code,attr"`
|
|
Message string `xml:",innerxml"`
|
|
}
|
|
|
|
// Device Info
|
|
type DeviceInfo struct {
|
|
XMLResponse
|
|
XMLName xml.Name `xml:"info"`
|
|
DeviceID string `xml:"deviceID,attr"`
|
|
Name string `xml:"name"`
|
|
Type string `xml:"type"`
|
|
Components []string `xml:"components>component"`
|
|
// ... additional fields
|
|
}
|
|
|
|
// Now Playing with complete structure
|
|
type NowPlaying struct {
|
|
XMLResponse
|
|
XMLName xml.Name `xml:"nowPlaying"`
|
|
DeviceID string `xml:"deviceID,attr"`
|
|
Source string `xml:"source,attr"`
|
|
Content ContentItem `xml:"ContentItem"`
|
|
Track string `xml:"track"`
|
|
Artist string `xml:"artist"`
|
|
Album string `xml:"album"`
|
|
Art Art `xml:"art"`
|
|
PlayStatus PlayStatus `xml:"playStatus"`
|
|
Position Position `xml:"position,omitempty"`
|
|
}
|
|
|
|
// Enum types with validation
|
|
type PlayStatus string
|
|
const (
|
|
PlayStatusPlaying PlayStatus = "PLAY_STATE"
|
|
PlayStatusPaused PlayStatus = "PAUSE_STATE"
|
|
PlayStatusStopped PlayStatus = "STOP_STATE"
|
|
)
|
|
|
|
type Key string
|
|
const (
|
|
KeyPlay Key = "PLAY"
|
|
KeyPause Key = "PAUSE"
|
|
KeyStop Key = "STOP"
|
|
KeyPrevTrack Key = "PREV_TRACK"
|
|
KeyNextTrack Key = "NEXT_TRACK"
|
|
KeyVolumeUp Key = "VOLUME_UP"
|
|
KeyVolumeDown Key = "VOLUME_DOWN"
|
|
KeyMute Key = "MUTE"
|
|
KeyPower Key = "POWER"
|
|
KeyPreset1 Key = "PRESET_1"
|
|
KeyPreset2 Key = "PRESET_2"
|
|
KeyPreset3 Key = "PRESET_3"
|
|
KeyPreset4 Key = "PRESET_4"
|
|
KeyPreset5 Key = "PRESET_5"
|
|
KeyPreset6 Key = "PRESET_6"
|
|
)
|
|
```
|
|
|
|
#### 4. WebSocket Event Client (`pkg/websocket`)
|
|
```go
|
|
type EventClient struct {
|
|
client *client.Client
|
|
conn *websocket.Conn
|
|
handlers map[string]EventHandler
|
|
stopChan chan bool
|
|
reconnect bool
|
|
backoff time.Duration
|
|
maxBackoff time.Duration
|
|
}
|
|
|
|
type EventHandler func(event Event)
|
|
|
|
type Event struct {
|
|
Type string `xml:"type,attr"`
|
|
DeviceID string `xml:"deviceID,attr"`
|
|
Data interface{} `xml:",innerxml"`
|
|
Timestamp time.Time `json:"timestamp"`
|
|
}
|
|
|
|
func NewEventClient(client *client.Client) *EventClient
|
|
func (e *EventClient) Subscribe(eventType string, handler EventHandler)
|
|
func (e *EventClient) Unsubscribe(eventType string)
|
|
func (e *EventClient) Start() error
|
|
func (e *EventClient) Stop() error
|
|
func (e *EventClient) IsConnected() bool
|
|
|
|
// Event types
|
|
const (
|
|
EventNowPlayingUpdated = "nowPlayingUpdated"
|
|
EventVolumeUpdated = "volumeUpdated"
|
|
EventConnectionState = "connectionStateUpdated"
|
|
EventPresetUpdated = "presetUpdated"
|
|
)
|
|
```
|
|
|
|
#### 5. WASM JavaScript Bridge (`pkg/wasm`)
|
|
```go
|
|
//go:build wasm
|
|
// +build wasm
|
|
|
|
import "syscall/js"
|
|
|
|
// Global WASM API registration
|
|
func RegisterWASMFunctions()
|
|
|
|
// Device Discovery (via proxy)
|
|
func wasmDiscoverDevices(this js.Value, args []js.Value) interface{}
|
|
|
|
// Client Management
|
|
func wasmCreateClient(this js.Value, args []js.Value) interface{}
|
|
func wasmGetNowPlaying(this js.Value, args []js.Value) interface{}
|
|
func wasmSendKey(this js.Value, args []js.Value) interface{}
|
|
func wasmSetVolume(this js.Value, args []js.Value) interface{}
|
|
func wasmGetSources(this js.Value, args []js.Value) interface{}
|
|
|
|
// Event Streaming
|
|
func wasmStartEventStream(this js.Value, args []js.Value) interface{}
|
|
func wasmStopEventStream(this js.Value, args []js.Value) interface{}
|
|
```
|
|
|
|
#### 6. Configuration Management (`pkg/config`)
|
|
```go
|
|
type Config struct {
|
|
// Server configuration
|
|
WebPort int `env:"WEB_PORT" default:"8080"`
|
|
APITimeout time.Duration `env:"API_TIMEOUT" default:"10s"`
|
|
|
|
// Discovery configuration
|
|
DiscoveryTimeout time.Duration `env:"DISCOVERY_TIMEOUT" default:"5s"`
|
|
CacheDevices bool `env:"CACHE_DEVICES" default:"true"`
|
|
CacheTTL time.Duration `env:"CACHE_TTL" default:"5m"`
|
|
|
|
// CORS configuration (for web proxy)
|
|
CORSOrigins []string `env:"CORS_ORIGINS" default:"*"`
|
|
|
|
// Logging
|
|
LogLevel string `env:"LOG_LEVEL" default:"info"`
|
|
LogFormat string `env:"LOG_FORMAT" default:"json"`
|
|
|
|
// Development
|
|
DevMode bool `env:"DEV_MODE" default:"false"`
|
|
}
|
|
|
|
func Load() Config
|
|
func LoadFromFile(filename string) (Config, error)
|
|
func (c Config) Validate() error
|
|
```
|
|
|
|
## Implementation Roadmap (Updated)
|
|
|
|
### Phase 1: Foundation & Core API ⭐ ✅ COMPLETE
|
|
- [x] Go module setup with modern dependencies
|
|
- [x] **HTTP Client with XML Support** ✅ DONE
|
|
- [x] Basic client structure
|
|
- [x] GET/POST methods with XML marshaling
|
|
- [x] Error handling for HTTP + XML
|
|
- [x] Timeout and retry logic
|
|
- [x] **Core XML models** ✅ DONE
|
|
- [x] DeviceInfo - Device information endpoint
|
|
- [x] NowPlaying - Current playback status endpoint
|
|
- [x] Sources - Available audio sources endpoint
|
|
- [x] Name - Device name endpoint
|
|
- [x] Capabilities - Device capabilities endpoint
|
|
- [x] Presets - Configured presets endpoint
|
|
- [x] Key - Media control commands endpoint
|
|
- [x] Volume - Volume control endpoints
|
|
- [x] Custom XML unmarshaling for enums
|
|
- [x] Validation and defaults
|
|
- [x] **Media Controls** ✅ DONE
|
|
- [x] POST /key endpoint implementation
|
|
- [x] Press + Release key pattern (API compliant)
|
|
- [x] Play, pause, stop, track navigation
|
|
- [x] Volume up/down via keys
|
|
- [x] Preset selection (1-6)
|
|
- [x] Key validation and error handling
|
|
- [x] **Volume Management** ✅ DONE
|
|
- [x] GET /volume endpoint
|
|
- [x] POST /volume endpoint
|
|
- [x] Incremental volume control
|
|
- [x] Volume level validation and clamping
|
|
- [x] Safety features and warnings
|
|
- [x] **Enhanced CLI tool** ✅ DONE
|
|
- [x] Host:port parsing (host:8090 format)
|
|
- [x] Device discovery via UPnP
|
|
- [x] All informational endpoints
|
|
- [x] Media control commands
|
|
- [x] Volume management with safety
|
|
- [x] Comprehensive help and examples
|
|
- [x] **Unit tests with mocks** ✅ DONE
|
|
- [x] HTTP client tests
|
|
- [x] XML parsing tests
|
|
- [x] Key control tests
|
|
- [x] Volume control tests
|
|
- [x] Host:port parsing tests
|
|
- [x] Mock responses with real device data
|
|
|
|
### Phase 2: Device Discovery & Management ✅ COMPLETE
|
|
- [x] **UPnP SSDP Discovery** ✅ DONE
|
|
- [x] M-SEARCH implementation
|
|
- [x] Response parsing
|
|
- [x] Device caching with TTL
|
|
- [x] **CLI Device Selection** ✅ DONE
|
|
- [x] Automatic discovery
|
|
- [x] Host:port parsing enhancement
|
|
- [x] Configuration-based device lists
|
|
- [x] **Integration Tests** ✅ DONE
|
|
- [x] Tests against real SoundTouch devices (SoundTouch 10 & 20)
|
|
- [x] Comprehensive real device validation
|
|
- [x] **Error Handling & Logging** ✅ DONE
|
|
- [x] Structured error messages
|
|
- [x] Graceful error handling
|
|
- [x] Network timeout management
|
|
|
|
### Phase 3: Additional Control Endpoints 🎛️ ✅ COMPLETE
|
|
- [x] **Source Management** ✅ DONE
|
|
- POST /select - Switch audio sources
|
|
- Source validation and error handling
|
|
- Convenience methods (SelectSpotify, SelectBluetooth, etc.)
|
|
- [x] **Bass Control** ✅ DONE
|
|
- GET /bass - Get bass settings
|
|
- POST /bass - Set bass level (-9 to +9)
|
|
- Range validation and safety features
|
|
- Incremental bass control methods
|
|
- [x] **Balance Control** ✅ DONE
|
|
- GET/POST /balance - Stereo balance (-50 to +50)
|
|
- Balance adjustment with clamping
|
|
- Left/right convenience methods
|
|
- [x] **Preset Management (Complete)** ✅ DONE
|
|
- Complete preset analysis and helper methods
|
|
- ✅ Implemented `/storePreset` and `/removePreset` endpoints (discovered via [SoundTouch Plus Wiki](https://github.com/thlucas1/homeassistantcomponent_soundtouchplus/wiki/SoundTouch-WebServices-API))
|
|
- Full CRUD operations: Create, Read, Update, Delete presets
|
|
- CLI commands: `preset store`, `preset store-current`, `preset remove`
|
|
- Note: Official docs marked POST /presets as "N/A" but working endpoints found via community documentation
|
|
- [x] **System Features** ✅ DONE
|
|
- GET/POST /clockTime - Device time management
|
|
- GET/POST /clockDisplay - Clock display settings
|
|
- GET /networkInfo - Network diagnostics
|
|
- GET /name, POST /name - Device name management
|
|
- GET /bassCapabilities - Bass capability detection
|
|
|
|
### Phase 4: WebSocket Real-time Events 📡 ✅ COMPLETE
|
|
- [x] **Implement WebSocket Client** ✅ DONE
|
|
- Connection Management
|
|
- Event parsing and routing
|
|
- Reconnection with exponential backoff
|
|
- Automatic connection recovery
|
|
- [x] **Event Handler System** ✅ DONE
|
|
- 12 typed event structs (NowPlayingUpdated, VolumeUpdated, etc.)
|
|
- Handler Registration and callback system
|
|
- Event Filtering and routing
|
|
- Comprehensive event type coverage
|
|
- [x] **CLI Real-time Monitoring** ✅ DONE
|
|
- Live Now-Playing Updates
|
|
- Volume Change Monitoring
|
|
- Connection Status Display
|
|
- Real-time event streaming with formatted output
|
|
- [x] **Event Management** ✅ DONE
|
|
- Event logging for debugging
|
|
- Connection state monitoring
|
|
- Error handling and recovery
|
|
|
|
### Phase 5: Multiroom Zone Management 🏠 ✅ COMPLETE
|
|
- [x] **Zone Information** ✅ DONE
|
|
- GET /getZone - Retrieve zone configuration
|
|
- Zone status and membership queries
|
|
- Master/slave device identification
|
|
- [x] **Zone Operations** ✅ DONE
|
|
- POST /setZone - Create and modify zones
|
|
- Zone creation with multiple devices
|
|
- Add/remove devices from existing zones
|
|
- Dissolve zones completely
|
|
- [x] **Zone Management API** ✅ DONE
|
|
- CreateZone(), AddToZone(), RemoveFromZone()
|
|
- IP validation and duplicate detection
|
|
- Comprehensive error handling
|
|
- Zone builder with fluent API
|
|
- [x] **Low-Level Zone API** ✅ DONE
|
|
- POST /addZoneSlave - Individual slave addition
|
|
- POST /removeZoneSlave - Individual slave removal
|
|
- Direct device ID and IP-based operations
|
|
|
|
### Phase 6: Advanced Audio Controls 🎛️ ✅ COMPLETE
|
|
- [x] **DSP Audio Controls** ✅ DONE
|
|
- GET/POST /audiodspcontrols - DSP settings and audio modes
|
|
- Video sync delay adjustment
|
|
- Audio mode switching (movie, music, etc.)
|
|
- [x] **Advanced Tone Controls** ✅ DONE
|
|
- GET/POST /audioproducttonecontrols - Advanced bass/treble
|
|
- Professional-grade audio adjustment
|
|
- Device capability detection
|
|
- [x] **Speaker Level Controls** ✅ DONE
|
|
- GET/POST /audioproductlevelcontrols - Individual speaker levels
|
|
- Front-center and rear-surround adjustment
|
|
- Multi-channel audio management
|
|
|
|
### Phase 7: Web Application & CORS Proxy 🌐 (Future Enhancement)
|
|
- [ ] **Create Embedded Web UI**
|
|
- HTML/CSS/JS for SoundTouch control
|
|
- Responsive design for mobile
|
|
- Real-time Updates via WebSocket
|
|
- [ ] **CORS-Proxy Server**
|
|
- HTTP proxy to local SoundTouch devices
|
|
- WebSocket proxy for events
|
|
- CORS Header Management
|
|
- [ ] **Single Binary with Embedded Assets**
|
|
- go:embed for web assets
|
|
- Static File Serving
|
|
- SPA Routing Support
|
|
- [ ] **Web-UI Features**
|
|
- Device Discovery & Selection
|
|
- Now playing display with album art
|
|
- Volume & Bass Controls
|
|
- Source Selection
|
|
- Preset Management
|
|
|
|
### Phase 8: WASM Browser Integration 🧩 (Future Enhancement)
|
|
- [ ] **WASM Build Configuration**
|
|
- Build tags and conditional compilation
|
|
- WASM-specific HTTP client (via proxy)
|
|
- JavaScript Promise Integration
|
|
- [ ] **WASM JavaScript Bridge**
|
|
- Go function export to JavaScript
|
|
- Asynchronous API calls
|
|
- Error handling via promise rejection
|
|
- [ ] **Browser Demo Application**
|
|
- Pure Frontend SoundTouch Control
|
|
- Local Network Device Discovery (via Proxy)
|
|
- Real-time Event Updates
|
|
- [ ] **Cross-Origin Solutions**
|
|
- Local proxy server for development
|
|
- Browser Extension Support
|
|
- Documentation for CORS issues
|
|
|
|
### Phase 9: Production Features & Polish 🚀 (Future Enhancement)
|
|
- [ ] **Advanced Configuration**
|
|
- Environment-based Config
|
|
- Configuration File Support
|
|
- Runtime Configuration Updates
|
|
- [ ] **Multi-Device Support**
|
|
- Multiple Device Connections
|
|
- Device Groups/Zones
|
|
- Synchronized Operations
|
|
- [ ] **Preset & Source Management**
|
|
- Preset Backup/Restore
|
|
- Custom Source Integration
|
|
- Playlist Management
|
|
- [ ] **Performance Optimizations**
|
|
- Connection Pooling
|
|
- Request Caching
|
|
- Lazy Loading
|
|
- [ ] **Documentation & Examples**
|
|
- Comprehensive API Documentation
|
|
- Usage examples for all use cases
|
|
- Best Practices Guide
|
|
|
|
## Build System Based on Modern Patterns
|
|
|
|
### Makefile with Multi-Target Support
|
|
```makefile
|
|
BINARY_NAME=soundtouch
|
|
VERSION?=$(shell git describe --tags --always --dirty 2>/dev/null || echo "dev")
|
|
BUILD_TIME=$(shell date -u '+%Y-%m-%d_%H:%M:%S')
|
|
GO_VERSION=$(shell go version | cut -d ' ' -f 3)
|
|
|
|
LDFLAGS=-ldflags "-X main.Version=$(VERSION) -X main.BuildTime=$(BUILD_TIME) -X main.GoVersion=$(GO_VERSION)"
|
|
BUILD_FLAGS=-trimpath $(LDFLAGS)
|
|
|
|
# Development builds
|
|
build:
|
|
go build $(BUILD_FLAGS) -o $(BINARY_NAME) ./cmd/cli
|
|
|
|
build-webapp:
|
|
go build $(BUILD_FLAGS) -o $(BINARY_NAME)-webapp ./cmd/webapp
|
|
|
|
# WASM build
|
|
build-wasm:
|
|
GOOS=js GOARCH=wasm go build $(BUILD_FLAGS) -o web/soundtouch.wasm ./cmd/wasm
|
|
cp "$(shell go env GOROOT)/misc/wasm/wasm_exec.js" web/
|
|
|
|
# Cross-platform builds
|
|
build-all: build-linux build-darwin build-windows
|
|
|
|
# Development with hot reload
|
|
dev-cli:
|
|
air -c .air-cli.toml
|
|
|
|
dev-webapp:
|
|
air -c .air-webapp.toml
|
|
|
|
dev-wasm:
|
|
air -c .air-wasm.toml
|
|
|
|
# Testing
|
|
test:
|
|
go test -v -race ./...
|
|
|
|
test-coverage:
|
|
go test -v -coverprofile=coverage.out ./...
|
|
go tool cover -html=coverage.out -o coverage.html
|
|
|
|
# Quality checks
|
|
check: fmt vet lint test
|
|
|
|
# Docker development environment
|
|
docker-dev:
|
|
docker compose up --build
|
|
|
|
# Release packaging
|
|
release: build-all
|
|
mkdir -p dist
|
|
tar -czf dist/$(BINARY_NAME)-$(VERSION)-linux-amd64.tar.gz $(BINARY_NAME)-linux-amd64
|
|
tar -czf dist/$(BINARY_NAME)-$(VERSION)-darwin-amd64.tar.gz $(BINARY_NAME)-darwin-amd64
|
|
zip dist/$(BINARY_NAME)-$(VERSION)-windows-amd64.zip $(BINARY_NAME)-windows-amd64.exe
|
|
```
|
|
|
|
## Technical Solution Approaches (Updated)
|
|
|
|
### WASM Browser Integration
|
|
1. **CORS Proxy Pattern**: Go web app as proxy between browser and SoundTouch devices
|
|
2. **Local Development Server**: CORS headers for local development
|
|
3. **WebSocket Proxy**: Real-time events via secure WebSocket connection
|
|
4. **Graceful Degradation**: Functionality depending on browser environment
|
|
|
|
### XML API Robustness
|
|
1. **Type-Safe Models**: Strict Go structs with validation
|
|
2. **Custom Unmarshaling**: Enum validation and error recovery
|
|
3. **Timeout Handling**: Robust network calls with retry logic
|
|
4. **Connection Pooling**: Efficient HTTP client reuse
|
|
|
|
### Multi-Platform Deployment
|
|
1. **Single Binary**: Embedded assets eliminate external dependencies
|
|
2. **Cross-Compilation**: Native binaries for all platforms
|
|
3. **Docker Support**: Containerized development and deployment
|
|
4. **Progressive Enhancement**: CLI → WebApp → WASM depending on requirements
|
|
|
|
## Example Usage (Updated)
|
|
|
|
### Native Go Library
|
|
```go
|
|
package main
|
|
|
|
import (
|
|
"fmt"
|
|
"log"
|
|
"time"
|
|
|
|
"github.com/gesellix/bose-soundtouch/pkg/client"
|
|
"github.com/gesellix/bose-soundtouch/pkg/discovery"
|
|
"github.com/gesellix/bose-soundtouch/pkg/models"
|
|
)
|
|
|
|
func main() {
|
|
// Discover devices
|
|
discoveryService := discovery.NewDiscoveryService(5 * time.Second)
|
|
devices, err := discoveryService.DiscoverDevices()
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
|
|
if len(devices) == 0 {
|
|
log.Fatal("No SoundTouch devices found")
|
|
}
|
|
|
|
// Create client for first device
|
|
client := client.NewClient(client.ClientConfig{
|
|
Host: devices[0].Host,
|
|
Port: 8090,
|
|
Timeout: 10 * time.Second,
|
|
})
|
|
|
|
// Get device info
|
|
info, err := client.GetDeviceInfo()
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
fmt.Printf("Connected to: %s\n", info.Name)
|
|
|
|
// Get current playback
|
|
nowPlaying, err := client.GetNowPlaying()
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
|
|
if nowPlaying.PlayStatus == models.PlayStatusPlaying {
|
|
fmt.Printf("Playing: %s - %s (%s)\n",
|
|
nowPlaying.Artist, nowPlaying.Track, nowPlaying.Album)
|
|
}
|
|
|
|
// Control playback
|
|
if nowPlaying.PlayStatus == models.PlayStatusPlaying {
|
|
client.SendKey(models.KeyPause)
|
|
fmt.Println("Paused playback")
|
|
} else {
|
|
client.SendKey(models.KeyPlay)
|
|
fmt.Println("Started playback")
|
|
}
|
|
}
|
|
```
|
|
|
|
### CLI Usage
|
|
```bash
|
|
# Discover devices
|
|
soundtouch discover
|
|
|
|
# Device operations
|
|
soundtouch --device 192.0.2.100 info
|
|
soundtouch --device 192.0.2.100 play
|
|
soundtouch --device 192.0.2.100 volume 50
|
|
soundtouch --device 192.0.2.100 preset 1
|
|
|
|
# Interactive mode
|
|
soundtouch interactive
|
|
|
|
# Web interface
|
|
soundtouch-playerapp --port 8080
|
|
```
|
|
|
|
### JavaScript/WASM Usage
|
|
```javascript
|
|
// Load WASM module
|
|
await loadWASM('/soundtouch.wasm');
|
|
|
|
// Discover devices (via proxy)
|
|
const devices = await boseAPI.discoverDevices();
|
|
console.log('Found devices:', devices);
|
|
|
|
// Create client
|
|
const client = boseAPI.createClient(devices[0].host, 8090);
|
|
|
|
// Get now playing
|
|
const nowPlaying = await client.getNowPlaying();
|
|
console.log(`Playing: ${nowPlaying.artist} - ${nowPlaying.track}`);
|
|
|
|
// Control playback
|
|
await client.sendKey('PAUSE');
|
|
|
|
// Volume control
|
|
await client.setVolume(75);
|
|
|
|
// Real-time events
|
|
client.startEventStream((event) => {
|
|
if (event.type === 'nowPlayingUpdated') {
|
|
updateUI(event.data);
|
|
}
|
|
});
|
|
```
|
|
|
|
## Testing Strategy (Enhanced)
|
|
|
|
### Unit Tests
|
|
- **Mock HTTP Client**: Simulierte SoundTouch-Responses
|
|
- **XML Parsing Tests**: Robustness für verschiedene Response-Formate
|
|
- **Model Validation**: Enum-Validation und Edge-Cases
|
|
- **Error Handling**: Network Failures und API Errors
|
|
|
|
### Integration Tests
|
|
- **Real Device Tests**: Gegen echte SoundTouch-Hardware
|
|
- **Docker Mock Server**: Simulierte SoundTouch-API für CI/CD
|
|
- **Discovery Tests**: UPnP SSDP in verschiedenen Netzwerk-Szenarien
|
|
- **WebSocket Tests**: Event-Streaming und Reconnection
|
|
|
|
### E2E Tests
|
|
- **CLI Tests**: Command-Line Interface Validation
|
|
- **Web Interface Tests**: Browser-basierte Tests mit Headless Chrome
|
|
- **WASM Tests**: Browser WASM Module Loading und Execution
|
|
- **Cross-Platform Tests**: Builds auf Linux/macOS/Windows
|
|
|
|
## Deployment Strategies
|
|
|
|
### Single Binary Distribution
|
|
```bash
|
|
# CLI Tool
|
|
./soundtouch-linux-amd64 discover
|
|
./soundtouch-linux-amd64 --device IP play
|
|
|
|
# Web Application (embedded assets)
|
|
./soundtouch-playerapp-linux-amd64 --port 8080
|
|
|
|
# Docker
|
|
docker run -p 8080:8080 soundtouch-playerapp
|
|
```
|
|
|
|
### Development Environment
|
|
```bash
|
|
# Local development with hot reload
|
|
make dev-webapp # Web app development
|
|
make dev-wasm # WASM development
|
|
make dev-cli # CLI development
|
|
|
|
# Full development environment
|
|
docker compose up # Mock devices + web app
|
|
```
|
|
|
|
## Success Criteria
|
|
|
|
### Phase 1-2 (Foundation) ✅ COMPLETE
|
|
- ✅ Stable HTTP API connection to SoundTouch devices
|
|
- ✅ XML model coverage for all core APIs (DeviceInfo, NowPlaying, Sources, Name, Capabilities, Presets, Volume, Key controls)
|
|
- ✅ Automatic device discovery via UPnP and mDNS
|
|
- ✅ Comprehensive CLI tool with all endpoint commands
|
|
- ✅ Media controls with proper press+release key patterns
|
|
- ✅ Volume management with safety features
|
|
- ✅ Real device validation on SoundTouch 10 and 20
|
|
|
|
### Phase 3-4 (Audio Controls & Real-time Events) ✅ COMPLETE
|
|
- ✅ Source selection with convenience methods (Spotify, Bluetooth, etc.)
|
|
- ✅ Bass control with range validation (-9 to +9)
|
|
- ✅ Balance control for stereo devices (-50 to +50)
|
|
- ✅ Clock and display management (time, brightness, format)
|
|
- ✅ Network information retrieval
|
|
- ✅ WebSocket event streaming with 12 event types
|
|
- ✅ Automatic reconnection and connection management
|
|
|
|
### Phase 5-6 (Multiroom & Advanced Audio) ✅ COMPLETE
|
|
- ✅ Complete multiroom zone management (create, modify, dissolve)
|
|
- ✅ Zone status and membership queries
|
|
- ✅ Advanced audio controls (DSP, tone, speaker levels)
|
|
- ✅ Professional-grade audio adjustment features
|
|
- ✅ Device capability detection and validation
|
|
|
|
### Phase 7+ (Future Enhancements)
|
|
- ✅ WASM integration with JavaScript bridge
|
|
- ✅ Multi-Device Support
|
|
- ✅ Production-ready Configuration Management
|
|
- ✅ Comprehensive documentation and examples
|
|
|
|
## Resources & References
|
|
|
|
- [Bose SoundTouch Web API Documentation](https://assets.bosecreative.com/m/496577402d128874/original/SoundTouch-Web-API.pdf)
|
|
- [Go WebAssembly](https://github.com/golang/go/wiki/WebAssembly)
|
|
- [UPnP Device Architecture](http://upnp.org/specs/arch/UPnP-arch-DeviceArchitecture-v1.0.pdf)
|
|
- [Go Embed Directive](https://pkg.go.dev/embed)
|
|
- [Gorilla WebSocket](https://github.com/gorilla/websocket)
|
|
- [PROJECT-PATTERNS.md](../content/docs/appendix/PROJECT-PATTERNS.md) - Detailed pattern documentation
|