Bose SoundTouch Toolkit

Documentation for controlling and preserving Bose SoundTouch devices

View the Project on GitHub gesellix/Bose-SoundTouch

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

API Specification

POST /key

Sends a key command to the SoundTouch device.

Request Format: According to the API documentation, proper key simulation requires sending both press and release states:

<key state="press" sender="Gabbo">KEY_NAME</key>
<key state="release" 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:

Our implementation uses “Gabbo” as the default sender, which is the standard value used in official SoundTouch examples.

Available Key Commands

Playback Controls

Rating and Bookmark Controls

Power and System Controls

Volume Controls

Preset Controls

Input Controls

Shuffle Controls

Repeat Controls

Client API

Basic Methods

// Send complete key command (press + release - recommended)
err := client.SendKey(models.KeyPlay)

// Send key press and release (alias for SendKey)
err := client.SendKeyPress(models.KeyPlay)

// Send only key press state (advanced usage)
err := client.SendKeyPressOnly(models.KeyPlay)

// Send only key release state (advanced usage)
err := client.SendKeyReleaseOnly(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:

Run tests:

go test ./pkg/models/...

Integration Testing

Tested with real SoundTouch devices:

All key commands successfully sent and executed on both devices.

Code Examples

Basic Usage

package main

import (
    "log"
    "github.com/gesellix/bose-soundtouch/pkg/client"
    "github.com/gesellix/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)
    }
    
    // SendKey automatically sends both press and release states
    return client.SendKey(keyValue)
}

func sendAllValidKeys(client *client.Client) {
    for _, key := range models.GetAllValidKeys() {
        fmt.Printf("Sending key: %s (press+release)\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

  1. Press + Release Pattern: Following API documentation, SendKey() sends both press and release states for proper key simulation
  2. Sender Field Critical: The sender attribute must be “Gabbo” for commands to be accepted
  3. XML Format: Simple XML structure without namespaces or headers
  4. State Handling: Both “press” and “release” states are supported, with complete press+release cycle as default
  5. Input Validation: All key values are validated before sending to the device
  6. Error Handling: Comprehensive error handling for invalid keys and API errors
  7. CLI Safety: Only one key command allowed per CLI invocation to prevent conflicts

Future Enhancements

Potential areas for future development:

  1. Key Sequences: Support for sending multiple key commands in sequence
  2. Macros: Predefined key command sequences (e.g., “power on and play preset 1”)
  3. Key Hold: Support for key hold duration for volume changes
  4. Device State: Check device state before sending commands
  5. Async Commands: Non-blocking key command execution
  6. Key Mapping: Custom key mappings for different device types

Reference