Major Features: • POST /key endpoint implementation with XML model and validation • Comprehensive media control commands (play, pause, stop, volume, presets) • Automatic host:port parsing in CLI for improved UX • Production-ready with full test coverage Key Control Implementation: • Add Key model with XML marshaling and validation (pkg/models/key.go) • Support all standard keys: PLAY, PAUSE, STOP, PREV_TRACK, NEXT_TRACK, VOLUME_UP/DOWN, PRESET_1-6 • Client methods: SendKey(), Play(), Pause(), Stop(), VolumeUp(), VolumeDown(), SelectPreset() • CLI commands: -play, -pause, -stop, -next, -prev, -volume-up, -volume-down, -preset, -key • Critical fix: Use 'Gabbo' as sender (only accepted value by SoundTouch API) Host:Port Parsing Enhancement: • Support -host 192.168.178.28:8090 format in addition to separate -host/-port flags • Robust parsing with IPv4, IPv6, and hostname support • Graceful fallback for invalid input • Backward compatible with existing usage Testing & Documentation: • Comprehensive unit tests for key functionality and host:port parsing • Integration tested with real SoundTouch 10 and SoundTouch 20 devices • Complete documentation in docs/KEY-CONTROLS.md and docs/HOST-PORT-PARSING.md • All tests pass, no diagnostics errors Breaking Changes: None Backward Compatibility: Fully maintained Tested with: • SoundTouch 10 (192.168.178.28:8090) ✅ • SoundTouch 20 (192.168.178.35:8090) ✅
6.6 KiB
Key Control Implementation
This document describes the implementation of the POST /key endpoint for media control commands in the Bose SoundTouch API client.
Overview
The key control functionality allows sending media control commands to SoundTouch devices, including play/pause, volume adjustment, track navigation, and preset selection.
Implementation Files
pkg/models/key.go- XML model and constants for key commandspkg/models/key_test.go- Comprehensive tests for key functionalitypkg/client/client.go- Client methods for sending key commandscmd/soundtouch-cli/main.go- CLI commands for key controls
API Specification
POST /key
Sends a key command to the SoundTouch device.
Request Format:
<key state="press" sender="Gabbo">KEY_NAME</key>
Response Format:
<?xml version="1.0" encoding="UTF-8" ?>
<status>/key</status>
Important Discovery: Sender Field
During implementation, we discovered that the sender attribute is critical for successful key commands. Only specific sender values are accepted:
- ✅ "Gabbo" - Works (canonical example from official documentation)
- ❌ "GoClient" - Rejected with CLIENT_XML_ERROR (1019)
- ❌ "SoundTouch app" - Rejected with CLIENT_XML_ERROR (1019)
- ❌ "" (empty) - Rejected with CLIENT_XML_ERROR (1019)
Our implementation uses "Gabbo" as the default sender, which is the standard value used in official SoundTouch examples.
Available Key Commands
Media Controls
PLAY- Start playbackPAUSE- Pause playbackSTOP- Stop playbackPREV_TRACK- Previous trackNEXT_TRACK- Next track
Volume Controls
VOLUME_UP- Increase volumeVOLUME_DOWN- Decrease volume
Presets
PRESET_1throughPRESET_6- Select preset 1-6
Client API
Basic Methods
// Send any valid key command
err := client.SendKey(models.KeyPlay)
// Send key press (default behavior)
err := client.SendKeyPress(models.KeyPlay)
// Send key release
err := client.SendKeyRelease(models.KeyPlay)
Convenience Methods
// Media controls
err := client.Play()
err := client.Pause()
err := client.Stop()
err := client.NextTrack()
err := client.PrevTrack()
// Volume controls
err := client.VolumeUp()
err := client.VolumeDown()
// Preset selection (1-6)
err := client.SelectPreset(1)
Key Validation
// Check if a key value is valid
isValid := models.IsValidKey("PLAY") // true
isValid := models.IsValidKey("INVALID") // false
// Get all valid key values
allKeys := models.GetAllValidKeys()
CLI Usage
Individual Key Commands
# Media controls
soundtouch-cli -host 192.168.1.100 -play
soundtouch-cli -host 192.168.1.100 -pause
soundtouch-cli -host 192.168.1.100 -stop
soundtouch-cli -host 192.168.1.100 -next
soundtouch-cli -host 192.168.1.100 -prev
# Volume controls
soundtouch-cli -host 192.168.1.100 -volume-up
soundtouch-cli -host 192.168.1.100 -volume-down
# Preset selection
soundtouch-cli -host 192.168.1.100 -preset 1
soundtouch-cli -host 192.168.1.100 -preset 6
Generic Key Command
# Send any valid key using the -key flag
soundtouch-cli -host 192.168.1.100 -key PLAY
soundtouch-cli -host 192.168.1.100 -key STOP
soundtouch-cli -host 192.168.1.100 -key PRESET_3
Error Handling
# Invalid key validation
$ soundtouch-cli -host 192.168.1.100 -key INVALID
Failed to send key command: invalid key value: INVALID
# Multiple commands rejected
$ soundtouch-cli -host 192.168.1.100 -play -pause
Failed to send key command: only one key command can be sent at a time
# Missing host
$ soundtouch-cli -play
Host is required for key commands. Use -host flag or -discover to find devices.
Testing
Unit Tests
The implementation includes comprehensive unit tests in pkg/models/key_test.go:
- XML marshaling/unmarshaling
- Key validation
- Constructor functions
- Constants validation
- Benchmark tests
Run tests:
go test ./pkg/models/...
Integration Testing
Tested with real SoundTouch devices:
- SoundTouch 10 (192.168.1.100:8090) ✅
- SoundTouch 20 (192.168.1.35:8090) ✅
All key commands successfully sent and executed on both devices.
Code Examples
Basic Usage
package main
import (
"log"
"github.com/user_account/bose-soundtouch/pkg/client"
"github.com/user_account/bose-soundtouch/pkg/models"
)
func main() {
// Create client
soundtouchClient := client.NewClientFromHost("192.168.1.100")
// Play music
if err := soundtouchClient.Play(); err != nil {
log.Fatalf("Failed to play: %v", err)
}
// Adjust volume
if err := soundtouchClient.VolumeUp(); err != nil {
log.Fatalf("Failed to increase volume: %v", err)
}
// Select preset
if err := soundtouchClient.SelectPreset(1); err != nil {
log.Fatalf("Failed to select preset: %v", err)
}
}
Advanced Usage with Validation
func sendKeyCommand(client *client.Client, keyValue string) error {
// Validate before sending
if !models.IsValidKey(keyValue) {
return fmt.Errorf("invalid key: %s", keyValue)
}
return client.SendKey(keyValue)
}
func sendAllValidKeys(client *client.Client) {
for _, key := range models.GetAllValidKeys() {
fmt.Printf("Sending key: %s\n", key)
if err := client.SendKey(key); err != nil {
log.Printf("Failed to send %s: %v", key, err)
}
time.Sleep(1 * time.Second) // Avoid overwhelming the device
}
}
Implementation Notes
- Sender Field Critical: The
senderattribute must be "Gabbo" for commands to be accepted - XML Format: Simple XML structure without namespaces or headers
- State Handling: Both "press" and "release" states are supported
- Input Validation: All key values are validated before sending to the device
- Error Handling: Comprehensive error handling for invalid keys and API errors
- CLI Safety: Only one key command allowed per CLI invocation to prevent conflicts
Future Enhancements
Potential areas for future development:
- Key Sequences: Support for sending multiple key commands in sequence
- Macros: Predefined key command sequences (e.g., "power on and play preset 1")
- Key Hold: Support for key hold duration for volume changes
- Device State: Check device state before sending commands
- Async Commands: Non-blocking key command execution
- Key Mapping: Custom key mappings for different device types
Reference
- Official API: Based on Bose SoundTouch Web API documentation
- Test Devices: Validated with SoundTouch 10 and SoundTouch 20
- Standards: Follows existing project patterns and conventions