Files
Bose-SoundTouch/pkg/models/key.go
T
Tobias Gesellchen 7d73da7986 feat: implement POST /key endpoint for media controls with host:port parsing
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) 
2026-01-08 23:46:34 +01:00

97 lines
2.0 KiB
Go

package models
import "encoding/xml"
// Key represents a key press command for the /key endpoint
type Key struct {
XMLName xml.Name `xml:"key"`
State string `xml:"state,attr"`
Sender string `xml:"sender,attr"`
Value string `xml:",chardata"`
}
// KeyState constants for key press states
const (
KeyStatePress = "press"
KeyStateRelease = "release"
)
// KeyValue constants for available keys
const (
KeyPlay = "PLAY"
KeyPause = "PAUSE"
KeyStop = "STOP"
KeyPrevTrack = "PREV_TRACK"
KeyNextTrack = "NEXT_TRACK"
KeyVolumeUp = "VOLUME_UP"
KeyVolumeDown = "VOLUME_DOWN"
KeyPreset1 = "PRESET_1"
KeyPreset2 = "PRESET_2"
KeyPreset3 = "PRESET_3"
KeyPreset4 = "PRESET_4"
KeyPreset5 = "PRESET_5"
KeyPreset6 = "PRESET_6"
)
// NewKey creates a new key press command
func NewKey(keyValue string) *Key {
return &Key{
State: KeyStatePress,
Sender: "Gabbo",
Value: keyValue,
}
}
// NewKeyPress creates a new key press command (alias for NewKey)
func NewKeyPress(keyValue string) *Key {
return NewKey(keyValue)
}
// NewKeyRelease creates a new key release command
func NewKeyRelease(keyValue string) *Key {
return &Key{
State: KeyStateRelease,
Sender: "Gabbo",
Value: keyValue,
}
}
// IsValidKey checks if the key value is valid
func IsValidKey(keyValue string) bool {
validKeys := map[string]bool{
KeyPlay: true,
KeyPause: true,
KeyStop: true,
KeyPrevTrack: true,
KeyNextTrack: true,
KeyVolumeUp: true,
KeyVolumeDown: true,
KeyPreset1: true,
KeyPreset2: true,
KeyPreset3: true,
KeyPreset4: true,
KeyPreset5: true,
KeyPreset6: true,
}
return validKeys[keyValue]
}
// GetAllValidKeys returns a slice of all valid key values
func GetAllValidKeys() []string {
return []string{
KeyPlay,
KeyPause,
KeyStop,
KeyPrevTrack,
KeyNextTrack,
KeyVolumeUp,
KeyVolumeDown,
KeyPreset1,
KeyPreset2,
KeyPreset3,
KeyPreset4,
KeyPreset5,
KeyPreset6,
}
}