mirror of
https://github.com/gesellix/Bose-SoundTouch.git
synced 2026-08-19 00:56:16 +00:00
feat: show ContentItem location details for all sources in 'play now' command
- Add automatic display of ContentItem location (URI/ID) for all sources with location data - Add --verbose flag to 'play now' command for detailed content information - Show location details by default for any source (SPOTIFY, TUNEIN, PANDORA, STORED_MUSIC, etc.) - Add comprehensive test coverage for content details display logic - Add preset-store.md documentation for future /storePreset implementation - Update documentation to reflect universal location support This enables users to easily capture location URIs needed for the planned /storePreset feature: - Spotify: spotify:track:123456789 - TUNEIN: /v1/playback/station/s33828 - Internet Radio: https://stream.example.com/radio - NAS Music: 6_a2874b5d_4f83d999 - Pandora: 126740707481236361 Resolves #14 preparation work
This commit is contained in:
+801
-413
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,300 @@
|
||||
# SoundTouch API Comparison: Community Wiki vs Current Implementation
|
||||
|
||||
**Date:** January 2026
|
||||
**Source:** [SoundTouch Plus Wiki](https://github.com/thlucas1/homeassistantcomponent_soundtouchplus/wiki/SoundTouch-WebServices-API)
|
||||
**Our Implementation:** Bose-SoundTouch Go Library v1.0
|
||||
|
||||
## Executive Summary
|
||||
|
||||
The SoundTouch Plus community wiki documents **87 distinct API endpoints** with comprehensive examples, while our current implementation covers **23 endpoints**. This represents a significant opportunity to expand our API coverage from basic functionality to comprehensive SoundTouch ecosystem management.
|
||||
|
||||
### Key Findings
|
||||
- 📊 **Wiki Coverage**: 87 endpoints documented with real-world examples
|
||||
- 📊 **Our Coverage**: 23 endpoints implemented (26% of wiki coverage)
|
||||
- 🎯 **Gap**: 64 additional endpoints available for implementation
|
||||
- ⭐ **Quality**: Wiki provides production-ready XML examples and device-specific notes
|
||||
|
||||
---
|
||||
|
||||
## Implementation Status Matrix
|
||||
|
||||
### ✅ Already Implemented (23 endpoints)
|
||||
|
||||
| Endpoint | Wiki Status | Our Status | Notes |
|
||||
|----------|-------------|------------|-------|
|
||||
| `/info` | ✅ Documented | ✅ Complete | Device information |
|
||||
| `/now_playing` | ✅ Documented | ✅ Complete | Current playback status |
|
||||
| `/key` | ✅ Documented | ✅ Complete | Key press/release simulation |
|
||||
| `/volume` | ✅ Documented | ✅ Complete | Volume and mute control |
|
||||
| `/bass` | ✅ Documented | ✅ Complete | Bass level control |
|
||||
| `/bassCapabilities` | ✅ Documented | ✅ Complete | Bass capability detection |
|
||||
| `/sources` | ✅ Documented | ✅ Complete | Available audio sources |
|
||||
| `/select` | ✅ Documented | ✅ Complete | Source selection |
|
||||
| `/presets` | ✅ Documented | ✅ Complete | Preset configurations (read-only) |
|
||||
| `/getZone` | ✅ Documented | ✅ Complete | Zone status and membership |
|
||||
| `/setZone` | ✅ Documented | ✅ Complete | Zone creation and management |
|
||||
| `/addZoneSlave` | ✅ Documented | ✅ Complete | Add device to zone |
|
||||
| `/removeZoneSlave` | ✅ Documented | ✅ Complete | Remove device from zone |
|
||||
| `/capabilities` | ✅ Documented | ✅ Complete | Device feature capabilities |
|
||||
| `/audiodspcontrols` | ✅ Documented | ✅ Complete | Audio DSP modes and video sync |
|
||||
| `/audioproducttonecontrols` | ✅ Documented | ✅ Complete | Advanced bass/treble controls |
|
||||
| `/audioproductlevelcontrols` | ✅ Documented | ✅ Complete | Speaker level controls |
|
||||
| `/name` (GET/POST) | ✅ Documented | ✅ Complete | Device name management |
|
||||
| `/balance` | ✅ Documented | ✅ Complete | Stereo balance control |
|
||||
| `/clockTime` | ✅ Documented | ✅ Complete | Device time management |
|
||||
| `/clockDisplay` | ✅ Documented | ✅ Complete | Clock display settings |
|
||||
| `/networkInfo` | ✅ Documented | ✅ Complete | Network connectivity info |
|
||||
| `/requestToken` | ✅ Documented | ✅ Complete | Bearer token generation |
|
||||
|
||||
### 🔥 High Priority Missing (20 endpoints)
|
||||
|
||||
| Endpoint | Wiki Status | Priority | Use Case |
|
||||
|----------|-------------|----------|----------|
|
||||
| `/storePreset` | ✅ Detailed | **HIGH** | Save stations/playlists to presets |
|
||||
| `/removePreset` | ✅ Detailed | **HIGH** | Delete saved presets |
|
||||
| `/selectPreset` | ✅ Detailed | **HIGH** | Play preset by ID |
|
||||
| `/setMusicServiceAccount` | ✅ Detailed | **HIGH** | Add Spotify/Pandora accounts |
|
||||
| `/removeMusicServiceAccount` | ✅ Detailed | **HIGH** | Remove music service accounts |
|
||||
| `/searchStation` | ✅ Detailed | **HIGH** | Find Pandora/Spotify content |
|
||||
| `/addStation` | ✅ Detailed | **HIGH** | Add stations to favorites |
|
||||
| `/removeStation` | ✅ Detailed | **HIGH** | Remove stations from favorites |
|
||||
| `/navigate` | ✅ Detailed | **HIGH** | Browse music libraries/services |
|
||||
| `/search` | ✅ Detailed | **HIGH** | Search music content |
|
||||
| `/userPlayControl` | ✅ Detailed | **HIGH** | Play/pause/stop controls |
|
||||
| `/userRating` | ✅ Detailed | **HIGH** | Thumbs up/down ratings |
|
||||
| `/recents` | ✅ Detailed | **HIGH** | Recently played content |
|
||||
| `/standby` | ✅ Detailed | **HIGH** | Power management |
|
||||
| `/powerManagement` | ✅ Detailed | **HIGH** | Power state information |
|
||||
| `/lowPowerStandby` | ✅ Detailed | **HIGH** | Low-power mode |
|
||||
| `/listMediaServers` | ✅ Detailed | **HIGH** | UPnP/DLNA server discovery |
|
||||
| `/serviceAvailability` | ✅ Detailed | **HIGH** | Source availability status |
|
||||
| `/introspect` | ✅ Detailed | **HIGH** | Music service account status |
|
||||
| `/language` | ✅ Detailed | **HIGH** | Device language settings |
|
||||
|
||||
### 🎵 Music Service Management (12 endpoints)
|
||||
|
||||
| Category | Endpoints | Wiki Coverage | Notes |
|
||||
|----------|-----------|---------------|-------|
|
||||
| **Account Management** | `/setMusicServiceAccount`, `/removeMusicServiceAccount` | ✅ Full XML examples | Pandora, Spotify, NAS setup |
|
||||
| **Station Management** | `/searchStation`, `/addStation`, `/removeStation` | ✅ Pandora tested | Station discovery and favorites |
|
||||
| **Content Navigation** | `/navigate`, `/search` | ✅ Detailed examples | Music library browsing |
|
||||
| **Track Information** | `/trackInfo`, `/introspect` | ✅ Service-specific | Extended metadata |
|
||||
|
||||
### 🏠 Smart Home Integration (15 endpoints)
|
||||
|
||||
| Category | Endpoints | Wiki Coverage | Notes |
|
||||
|----------|-----------|---------------|-------|
|
||||
| **Notifications** | `/speaker`, `/playNotification` | ✅ TTS examples | Text-to-speech, URL playback |
|
||||
| **Power Management** | `/standby`, `/powerManagement`, `/lowPowerStandby` | ✅ Complete | Smart home automation |
|
||||
| **Network Management** | `/performWirelessSiteSurvey`, `/addWirelessProfile`, `/getActiveWirelessProfile` | ✅ WiFi setup | Network configuration |
|
||||
| **Bluetooth** | `/enterBluetoothPairing`, `/clearBluetoothPaired`, `/bluetoothInfo` | ✅ Pairing control | Bluetooth management |
|
||||
| **Source Control** | `/selectLastSource`, `/selectLastSoundTouchSource`, `/selectLocalSource` | ✅ Source switching | Quick source access |
|
||||
|
||||
### 📱 Advanced Device Features (19 endpoints)
|
||||
|
||||
| Category | Endpoints | Wiki Coverage | Notes |
|
||||
|----------|-----------|---------------|-------|
|
||||
| **Stereo Pairs** | `/getGroup`, `/addGroup`, `/removeGroup`, `/updateGroup` | ✅ ST-10 specific | L/R speaker pairing |
|
||||
| **System Info** | `/soundTouchConfigurationStatus`, `/systemtimeout`, `/rebroadcastlatencymode` | ✅ Configuration | Device state management |
|
||||
| **Software Updates** | `/swUpdateCheck`, `/swUpdateQuery`, `/swUpdateAbort`, `/swUpdateStart` | ✅ Update process | Firmware management |
|
||||
| **Audio Processing** | `/DSPMonoStereo`, `/audiospeakerattributeandsetting` | ✅ Hardware-specific | Advanced audio features |
|
||||
|
||||
---
|
||||
|
||||
## Wiki Documentation Quality Analysis
|
||||
|
||||
### 🌟 Exceptional Documentation Quality
|
||||
|
||||
**Real-World Examples:**
|
||||
- ✅ Complete XML request/response examples
|
||||
- ✅ Device-specific behavior notes (ST-10 vs ST-300)
|
||||
- ✅ Error conditions and troubleshooting
|
||||
- ✅ WebSocket event generation documentation
|
||||
- ✅ Service-specific requirements (Pandora Premium, etc.)
|
||||
|
||||
**Production-Ready Details:**
|
||||
```xml
|
||||
<!-- Example from wiki - POST /storePreset -->
|
||||
<preset id="3" createdOn="1701220500" updatedOn="1701220500">
|
||||
<ContentItem source="TUNEIN" type="stationurl" location="/v1/playback/station/s309605" sourceAccount="" isPresetable="true">
|
||||
<itemName>K-LOVE 90s</itemName>
|
||||
<containerArt>http://cdn-profiles.tunein.com/s309605/images/logog.png</containerArt>
|
||||
</ContentItem>
|
||||
</preset>
|
||||
```
|
||||
|
||||
**Device Compatibility Matrix:**
|
||||
- ST-10: Supports notifications, stereo pairing
|
||||
- ST-300: Supports advanced audio controls, HDMI
|
||||
- All devices: Support basic playback and zone management
|
||||
|
||||
### 🎯 Implementation Guidance
|
||||
|
||||
**Safety Notes from Wiki:**
|
||||
- Volume limits: Devices auto-limit 10-70 for notifications
|
||||
- Timeout handling: Some endpoints timeout on unsupported devices
|
||||
- State requirements: Certain operations require specific device states
|
||||
|
||||
**WebSocket Events Documented:**
|
||||
- `presetsUpdated` - Preset changes
|
||||
- `groupUpdated` - Stereo pair changes
|
||||
- `zoneUpdated` - Multi-room changes
|
||||
- `nowPlayingUpdated` - Source/playback changes
|
||||
- `volumeUpdated` - Volume/mute changes
|
||||
- `audiodspcontrols` - Audio mode changes
|
||||
|
||||
---
|
||||
|
||||
## Implementation Roadmap
|
||||
|
||||
### Phase 1: Essential Missing Features (High Impact)
|
||||
**Target: 20 endpoints in 4 weeks**
|
||||
|
||||
```go
|
||||
// Preset Management
|
||||
func (c *Client) StorePreset(id int, content ContentItem) error
|
||||
func (c *Client) RemovePreset(id int) error
|
||||
func (c *Client) SelectPreset(id int) error
|
||||
|
||||
// Music Service Setup
|
||||
func (c *Client) SetMusicServiceAccount(source, user, pass string) error
|
||||
func (c *Client) RemoveMusicServiceAccount(source, user string) error
|
||||
|
||||
// Content Discovery
|
||||
func (c *Client) NavigateLibrary(source, account string, startItem, numItems int) (*NavigateResponse, error)
|
||||
func (c *Client) SearchContent(source, account, term string) (*SearchResponse, error)
|
||||
|
||||
// Power Management
|
||||
func (c *Client) Standby() error
|
||||
func (c *Client) GetPowerState() (*PowerState, error)
|
||||
```
|
||||
|
||||
### Phase 2: Smart Home Integration (Medium Impact)
|
||||
**Target: 15 endpoints in 3 weeks**
|
||||
|
||||
```go
|
||||
// Notification System
|
||||
func (c *Client) PlayTTSMessage(message string, volume int) error
|
||||
func (c *Client) PlayURL(url string, volume int) error
|
||||
|
||||
// Network Management
|
||||
func (c *Client) PerformWiFiSurvey() (*WiFiNetworks, error)
|
||||
func (c *Client) AddWiFiProfile(ssid, password, securityType string) error
|
||||
|
||||
// Enhanced Controls
|
||||
func (c *Client) SendPlayControl(action PlayControlAction) error
|
||||
func (c *Client) RateCurrentTrack(rating RatingValue) error
|
||||
```
|
||||
|
||||
### Phase 3: Advanced Features (Lower Impact)
|
||||
**Target: 19 endpoints in 4 weeks**
|
||||
|
||||
```go
|
||||
// Stereo Pair Management
|
||||
func (c *Client) CreateStereoPair(leftIP, rightIP string, name string) error
|
||||
func (c *Client) GetStereoPairStatus() (*StereoPair, error)
|
||||
|
||||
// System Management
|
||||
func (c *Client) CheckSoftwareUpdate() (*UpdateInfo, error)
|
||||
func (c *Client) GetSystemTimeout() (*TimeoutConfig, error)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Integration Benefits
|
||||
|
||||
### 🏆 Complete Ecosystem Support
|
||||
- **Music Services**: Full Spotify, Pandora, NAS integration
|
||||
- **Smart Home**: Power, notifications, network management
|
||||
- **Professional**: Advanced audio controls, system configuration
|
||||
|
||||
### 🔧 Developer Experience
|
||||
- **Comprehensive Examples**: Wiki provides copy-paste XML structures
|
||||
- **Error Handling**: Well-documented failure modes and recovery
|
||||
- **Device Compatibility**: Clear hardware-specific feature matrix
|
||||
|
||||
### 📈 Use Case Expansion
|
||||
- **Home Automation**: Complete power and network control
|
||||
- **Music Management**: Full playlist and station management
|
||||
- **Professional Audio**: Advanced DSP and speaker configuration
|
||||
- **System Administration**: Update management and configuration
|
||||
|
||||
---
|
||||
|
||||
## Technical Implementation Notes
|
||||
|
||||
### Request/Response Patterns from Wiki
|
||||
|
||||
**Standard Success Response:**
|
||||
```xml
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<status>/endpointName</status>
|
||||
```
|
||||
|
||||
**Complex Response Example (from `/navigate`):**
|
||||
```xml
|
||||
<navigateResponse source="STORED_MUSIC" sourceAccount="guid/0">
|
||||
<totalItems>10</totalItems>
|
||||
<items>
|
||||
<item Playable="1">
|
||||
<name>Album Artists</name>
|
||||
<type>dir</type>
|
||||
<ContentItem source="STORED_MUSIC" location="107" sourceAccount="guid/0" isPresetable="true">
|
||||
<itemName>Album Artists</itemName>
|
||||
</ContentItem>
|
||||
</item>
|
||||
</items>
|
||||
</navigateResponse>
|
||||
```
|
||||
|
||||
### Error Handling Patterns
|
||||
|
||||
**Device Compatibility:**
|
||||
```go
|
||||
// Check capabilities before calling advanced features
|
||||
capabilities, err := client.GetCapabilities()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !capabilities.SupportsAudioDSPControls {
|
||||
return ErrFeatureNotSupported
|
||||
}
|
||||
```
|
||||
|
||||
### WebSocket Event Integration
|
||||
Each POST endpoint maps to specific WebSocket events that our existing event system can handle:
|
||||
|
||||
```go
|
||||
// Extend existing event system
|
||||
type WebSocketEvent struct {
|
||||
PresetUpdated *PresetsUpdate `xml:"presetsUpdated"`
|
||||
GroupUpdated *GroupUpdate `xml:"groupUpdated"`
|
||||
// Add new event types...
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Conclusion
|
||||
|
||||
The SoundTouch Plus Wiki represents a **treasure trove** of production-ready API documentation that can transform our library from basic device control to comprehensive SoundTouch ecosystem management.
|
||||
|
||||
### Key Opportunities:
|
||||
- 🎯 **3x Coverage Expansion**: From 23 to 87+ endpoints
|
||||
- 🏠 **Smart Home Ready**: Complete automation integration
|
||||
- 🎵 **Music Service Integration**: Full streaming service support
|
||||
- 📱 **Professional Features**: Advanced audio and system control
|
||||
- ✅ **Production Ready**: Real-world tested examples and error handling
|
||||
|
||||
### Immediate Next Steps:
|
||||
1. **Phase 1 Implementation**: Focus on preset management and music services (high user impact)
|
||||
2. **Test Infrastructure**: Set up automated testing against real devices
|
||||
3. **Documentation**: Integrate wiki examples into our API documentation
|
||||
4. **Community Engagement**: Collaborate with SoundTouch Plus project for mutual benefit
|
||||
|
||||
**This wiki documentation provides everything needed to implement a complete, production-ready SoundTouch API library that rivals official Bose applications in functionality.**
|
||||
|
||||
---
|
||||
|
||||
*Note: All endpoints documented in the wiki are tested against real hardware. Device-specific limitations are clearly documented with compatibility matrices for ST-10, ST-300, and other SoundTouch models.*
|
||||
@@ -0,0 +1,632 @@
|
||||
# SoundTouch API Wiki Implementation Plan
|
||||
|
||||
**Date:** January 2026
|
||||
**Source:** [SoundTouch Plus Wiki](https://github.com/thlucas1/homeassistantcomponent_soundtouchplus/wiki/SoundTouch-WebServices-API)
|
||||
**Target:** Complete implementation of 64 additional endpoints from wiki documentation
|
||||
|
||||
## Project Overview
|
||||
|
||||
### Scope
|
||||
Implement 64 additional API endpoints documented in the SoundTouch Plus Wiki to achieve comprehensive SoundTouch ecosystem coverage.
|
||||
|
||||
### Current Status
|
||||
- ✅ **Implemented**: 23 endpoints (core functionality)
|
||||
- 🎯 **Target**: 87 endpoints (comprehensive functionality)
|
||||
- 📈 **Expansion**: 3.8x increase in API coverage
|
||||
|
||||
---
|
||||
|
||||
## Implementation Phases
|
||||
|
||||
## Phase 1: Essential User Features (4 weeks)
|
||||
**Priority:** CRITICAL
|
||||
**Endpoints:** 20
|
||||
**User Impact:** HIGH
|
||||
|
||||
### 1.1 Preset Management (Week 1)
|
||||
Essential for user experience - save and manage favorite stations/playlists.
|
||||
|
||||
#### Endpoints to Implement:
|
||||
```go
|
||||
// pkg/api/presets.go (new file)
|
||||
func (c *Client) StorePreset(id int, content ContentItem) error
|
||||
func (c *Client) RemovePreset(id int) error
|
||||
func (c *Client) SelectPreset(id int) error
|
||||
```
|
||||
|
||||
#### XML Structures:
|
||||
```xml
|
||||
<!-- Store Preset Request -->
|
||||
<preset id="3" createdOn="1701220500" updatedOn="1701220500">
|
||||
<ContentItem source="TUNEIN" type="stationurl" location="/v1/playback/station/s309605" sourceAccount="" isPresetable="true">
|
||||
<itemName>K-LOVE 90s</itemName>
|
||||
<containerArt>http://cdn-profiles.tunein.com/s309605/images/logog.png</containerArt>
|
||||
</ContentItem>
|
||||
</preset>
|
||||
|
||||
<!-- Remove Preset Request -->
|
||||
<preset id="4"></preset>
|
||||
```
|
||||
|
||||
#### WebSocket Events:
|
||||
- `presetsUpdated` - Triggered on store/remove operations
|
||||
|
||||
### 1.2 Music Service Management (Week 1-2)
|
||||
Critical for streaming service integration - Spotify, Pandora, NAS libraries.
|
||||
|
||||
#### Endpoints to Implement:
|
||||
```go
|
||||
// pkg/api/music_services.go (new file)
|
||||
func (c *Client) SetMusicServiceAccount(source, user, password, displayName string) error
|
||||
func (c *Client) RemoveMusicServiceAccount(source, user string) error
|
||||
func (c *Client) ListMediaServers() (*MediaServerList, error)
|
||||
func (c *Client) GetServiceAvailability() (*ServiceAvailability, error)
|
||||
```
|
||||
|
||||
#### Service Types:
|
||||
```go
|
||||
type MusicService string
|
||||
|
||||
const (
|
||||
ServicePandora MusicService = "PANDORA"
|
||||
ServiceSpotify MusicService = "SPOTIFY"
|
||||
ServiceStoredMusic MusicService = "STORED_MUSIC"
|
||||
ServiceLocalMusic MusicService = "LOCAL_MUSIC"
|
||||
)
|
||||
|
||||
type MediaServer struct {
|
||||
ID string `xml:"id,attr"`
|
||||
MAC string `xml:"mac,attr"`
|
||||
IP string `xml:"ip,attr"`
|
||||
Manufacturer string `xml:"manufacturer,attr"`
|
||||
ModelName string `xml:"model_name,attr"`
|
||||
FriendlyName string `xml:"friendly_name,attr"`
|
||||
Location string `xml:"location,attr"`
|
||||
}
|
||||
```
|
||||
|
||||
#### XML Examples:
|
||||
```xml
|
||||
<!-- Pandora Account Setup -->
|
||||
<credentials source="PANDORA" displayName="Pandora Music Service">
|
||||
<user>YourPandoraUserId</user>
|
||||
<pass>YourPandoraPassword$1pd</pass>
|
||||
</credentials>
|
||||
|
||||
<!-- NAS Library Setup -->
|
||||
<credentials source="STORED_MUSIC" displayName="My NAS Media Library:">
|
||||
<user>d09708a1-5953-44bc-a413-123456789012/0</user>
|
||||
<pass />
|
||||
</credentials>
|
||||
```
|
||||
|
||||
### 1.3 Content Discovery (Week 2-3)
|
||||
Essential for browsing music libraries and searching content.
|
||||
|
||||
#### Endpoints to Implement:
|
||||
```go
|
||||
// pkg/api/content.go (new file)
|
||||
func (c *Client) Navigate(source, sourceAccount string, options NavigateOptions) (*NavigateResponse, error)
|
||||
func (c *Client) Search(source, sourceAccount, searchTerm string, options SearchOptions) (*SearchResponse, error)
|
||||
func (c *Client) GetRecents() (*RecentsResponse, error)
|
||||
func (c *Client) Introspect(source, sourceAccount string) (*IntrospectResponse, error)
|
||||
```
|
||||
|
||||
#### Data Structures:
|
||||
```go
|
||||
type NavigateOptions struct {
|
||||
StartItem int `xml:"startItem"`
|
||||
NumItems int `xml:"numItems"`
|
||||
Item *ContentItem `xml:"item,omitempty"`
|
||||
Sort string `xml:"sort,attr,omitempty"`
|
||||
Menu string `xml:"menu,attr,omitempty"`
|
||||
}
|
||||
|
||||
type NavigateResponse struct {
|
||||
Source string `xml:"source,attr"`
|
||||
SourceAccount string `xml:"sourceAccount,attr"`
|
||||
TotalItems int `xml:"totalItems"`
|
||||
Items []ContentItem `xml:"items>item"`
|
||||
}
|
||||
|
||||
type SearchOptions struct {
|
||||
StartItem int `xml:"startItem"`
|
||||
NumItems int `xml:"numItems"`
|
||||
Filter string `xml:"searchTerm,attr,omitempty"` // "track", "artist", "album"
|
||||
}
|
||||
```
|
||||
|
||||
### 1.4 Station Management (Week 3)
|
||||
Pandora and other music service station management.
|
||||
|
||||
#### Endpoints to Implement:
|
||||
```go
|
||||
// pkg/api/stations.go (new file)
|
||||
func (c *Client) SearchStations(source, sourceAccount, searchTerm string) (*StationSearchResponse, error)
|
||||
func (c *Client) AddStation(source, sourceAccount, token, name string) error
|
||||
func (c *Client) RemoveStation(content ContentItem) error
|
||||
```
|
||||
|
||||
### 1.5 Enhanced Playback Control (Week 4)
|
||||
Advanced playback and rating controls.
|
||||
|
||||
#### Endpoints to Implement:
|
||||
```go
|
||||
// pkg/api/playback.go (extend existing)
|
||||
func (c *Client) SendPlayControl(action PlayControlAction) error
|
||||
func (c *Client) RateCurrentTrack(rating RatingValue) error
|
||||
```
|
||||
|
||||
#### Enums:
|
||||
```go
|
||||
type PlayControlAction string
|
||||
const (
|
||||
PlayControlPause PlayControlAction = "PAUSE_CONTROL"
|
||||
PlayControlPlay PlayControlAction = "PLAY_CONTROL"
|
||||
PlayControlPlayPause PlayControlAction = "PLAY_PAUSE_CONTROL"
|
||||
PlayControlStop PlayControlAction = "STOP_CONTROL"
|
||||
)
|
||||
|
||||
type RatingValue string
|
||||
const (
|
||||
RatingUp RatingValue = "UP"
|
||||
RatingDown RatingValue = "DOWN"
|
||||
)
|
||||
```
|
||||
|
||||
### 1.6 Power Management (Week 4)
|
||||
Essential for smart home integration.
|
||||
|
||||
#### Endpoints to Implement:
|
||||
```go
|
||||
// pkg/api/power.go (new file)
|
||||
func (c *Client) Standby() error
|
||||
func (c *Client) GetPowerState() (*PowerState, error)
|
||||
func (c *Client) SetLowPowerStandby() error
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Phase 2: Smart Home Integration (3 weeks)
|
||||
**Priority:** HIGH
|
||||
**Endpoints:** 15
|
||||
**User Impact:** MEDIUM-HIGH
|
||||
|
||||
### 2.1 Notification System (Week 1)
|
||||
Text-to-speech and URL playback for smart home notifications.
|
||||
|
||||
#### Endpoints to Implement:
|
||||
```go
|
||||
// pkg/api/notifications.go (new file)
|
||||
func (c *Client) PlayTTSMessage(message string, options TTSOptions) error
|
||||
func (c *Client) PlayURL(url string, options PlayOptions) error
|
||||
func (c *Client) PlayNotificationBeep() error
|
||||
```
|
||||
|
||||
#### Data Structures:
|
||||
```go
|
||||
type TTSOptions struct {
|
||||
VolumeLevel int `xml:"volume,omitempty"`
|
||||
Language string `xml:"tl,omitempty"` // "EN", "DE", etc.
|
||||
AppKey string `xml:"app_key"`
|
||||
Service string `xml:"service"`
|
||||
Message string `xml:"message"`
|
||||
Reason string `xml:"reason"`
|
||||
}
|
||||
|
||||
type PlayOptions struct {
|
||||
VolumeLevel int `xml:"volume,omitempty"`
|
||||
AppKey string `xml:"app_key"`
|
||||
Service string `xml:"service"`
|
||||
Message string `xml:"message"`
|
||||
Reason string `xml:"reason"`
|
||||
}
|
||||
```
|
||||
|
||||
#### XML Examples:
|
||||
```xml
|
||||
<!-- TTS Message -->
|
||||
<play_info>
|
||||
<url>http://translate.google.com/translate_tts?ie=UTF-8&tl=EN&client=tw-ob&q=There%20is%20activity%20at%20the%20front%20door.</url>
|
||||
<app_key>YourAppKey</app_key>
|
||||
<service>TTS Notification</service>
|
||||
<message>Google TTS</message>
|
||||
<reason>There is activity at the front door.</reason>
|
||||
<volume>70</volume>
|
||||
</play_info>
|
||||
```
|
||||
|
||||
### 2.2 Network Management (Week 2)
|
||||
WiFi configuration and network information.
|
||||
|
||||
#### Endpoints to Implement:
|
||||
```go
|
||||
// pkg/api/network.go (extend existing)
|
||||
func (c *Client) PerformWiFiSurvey() (*WiFiSurveyResponse, error)
|
||||
func (c *Client) AddWiFiProfile(ssid, password string, securityType SecurityType) error
|
||||
func (c *Client) GetActiveWiFiProfile() (*WiFiProfile, error)
|
||||
func (c *Client) GetNetworkStats() (*NetworkStats, error)
|
||||
```
|
||||
|
||||
#### Security Types:
|
||||
```go
|
||||
type SecurityType string
|
||||
const (
|
||||
SecurityNone SecurityType = "none"
|
||||
SecurityWEP SecurityType = "wep"
|
||||
SecurityWPATKIP SecurityType = "wpatkip"
|
||||
SecurityWPAAES SecurityType = "wpaaes"
|
||||
SecurityWPA2TKIP SecurityType = "wpa2tkip"
|
||||
SecurityWPA2AES SecurityType = "wpa2aes"
|
||||
SecurityWPAOrWPA2 SecurityType = "wpa_or_wpa2" // Recommended
|
||||
)
|
||||
```
|
||||
|
||||
### 2.3 Bluetooth Management (Week 2)
|
||||
Bluetooth pairing and connection management.
|
||||
|
||||
#### Endpoints to Implement:
|
||||
```go
|
||||
// pkg/api/bluetooth.go (new file)
|
||||
func (c *Client) EnterBluetoothPairing() error
|
||||
func (c *Client) ClearBluetoothPairings() error
|
||||
func (c *Client) GetBluetoothInfo() (*BluetoothInfo, error)
|
||||
```
|
||||
|
||||
### 2.4 Language and System Configuration (Week 3)
|
||||
Device language and system settings.
|
||||
|
||||
#### Endpoints to Implement:
|
||||
```go
|
||||
// pkg/api/system.go (new file)
|
||||
func (c *Client) GetLanguage() (LanguageCode, error)
|
||||
func (c *Client) SetLanguage(lang LanguageCode) error
|
||||
func (c *Client) GetConfigurationStatus() (*ConfigurationStatus, error)
|
||||
func (c *Client) GetSystemTimeout() (*SystemTimeout, error)
|
||||
```
|
||||
|
||||
#### Language Codes:
|
||||
```go
|
||||
type LanguageCode int
|
||||
const (
|
||||
LangDanish LanguageCode = 1
|
||||
LangGerman LanguageCode = 2
|
||||
LangEnglish LanguageCode = 3
|
||||
LangSpanish LanguageCode = 4
|
||||
LangFrench LanguageCode = 5
|
||||
LangItalian LanguageCode = 6
|
||||
LangDutch LanguageCode = 7
|
||||
LangSwedish LanguageCode = 8
|
||||
LangJapanese LanguageCode = 9
|
||||
LangSimplifiedChinese LanguageCode = 10
|
||||
LangTraditionalChinese LanguageCode = 11
|
||||
LangKorean LanguageCode = 12
|
||||
)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Phase 3: Advanced Features (4 weeks)
|
||||
**Priority:** MEDIUM
|
||||
**Endpoints:** 19
|
||||
**User Impact:** MEDIUM
|
||||
|
||||
### 3.1 Stereo Pair Management (Week 1)
|
||||
ST-10 specific left/right speaker pairing.
|
||||
|
||||
#### Endpoints to Implement:
|
||||
```go
|
||||
// pkg/api/groups.go (new file)
|
||||
func (c *Client) GetStereoPairStatus() (*StereoPair, error)
|
||||
func (c *Client) CreateStereoPair(leftDeviceID, rightDeviceID string, name string) (*StereoPair, error)
|
||||
func (c *Client) RemoveStereoPair() error
|
||||
func (c *Client) UpdateStereoPairName(groupID, newName string) (*StereoPair, error)
|
||||
```
|
||||
|
||||
#### Data Structures:
|
||||
```go
|
||||
type StereoPair struct {
|
||||
ID string `xml:"id,attr"`
|
||||
Name string `xml:"name"`
|
||||
MasterDeviceID string `xml:"masterDeviceId"`
|
||||
Roles []GroupRole `xml:"roles>groupRole"`
|
||||
SenderIPAddress string `xml:"senderIPAddress"`
|
||||
Status string `xml:"status"`
|
||||
}
|
||||
|
||||
type GroupRole struct {
|
||||
DeviceID string `xml:"deviceId"`
|
||||
Role string `xml:"role"` // "LEFT", "RIGHT"
|
||||
IPAddress string `xml:"ipAddress"`
|
||||
}
|
||||
```
|
||||
|
||||
### 3.2 Software Update Management (Week 2)
|
||||
Firmware update checking and management.
|
||||
|
||||
#### Endpoints to Implement:
|
||||
```go
|
||||
// pkg/api/updates.go (new file)
|
||||
func (c *Client) CheckSoftwareUpdate() (*UpdateInfo, error)
|
||||
func (c *Client) GetUpdateStatus() (*UpdateStatus, error)
|
||||
func (c *Client) StartSoftwareUpdate() error
|
||||
func (c *Client) AbortSoftwareUpdate() error
|
||||
```
|
||||
|
||||
### 3.3 Advanced Audio Features (Week 3)
|
||||
Advanced DSP and speaker configuration.
|
||||
|
||||
#### Endpoints to Implement:
|
||||
```go
|
||||
// pkg/api/audio_advanced.go (new file)
|
||||
func (c *Client) GetDSPMonoStereo() (*DSPMonoStereoConfig, error)
|
||||
func (c *Client) SetDSPMonoStereo(enabled bool) error
|
||||
func (c *Client) GetAudioSpeakerAttributes() (*SpeakerAttributes, error)
|
||||
func (c *Client) GetRebroadcastLatencyMode() (*LatencyMode, error)
|
||||
```
|
||||
|
||||
### 3.4 Source Selection Shortcuts (Week 4)
|
||||
Quick source switching utilities.
|
||||
|
||||
#### Endpoints to Implement:
|
||||
```go
|
||||
// pkg/api/sources.go (extend existing)
|
||||
func (c *Client) SelectLastSource() error
|
||||
func (c *Client) SelectLastSoundTouchSource() error
|
||||
func (c *Client) SelectLastWiFiSource() error
|
||||
func (c *Client) SelectLocalSource() error
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Phase 4: Professional Features (2 weeks)
|
||||
**Priority:** LOW
|
||||
**Endpoints:** 10
|
||||
**User Impact:** LOW
|
||||
|
||||
### 4.1 HDMI and Product Controls
|
||||
ST-300 specific HDMI and product controls.
|
||||
|
||||
#### Endpoints to Implement:
|
||||
```go
|
||||
// pkg/api/product.go (new file)
|
||||
func (c *Client) GetProductCECHDMIControl() (*CECHDMIControl, error)
|
||||
func (c *Client) SetProductCECHDMIControl(config CECHDMIControl) error
|
||||
func (c *Client) GetProductHDMIAssignmentControls() (*HDMIAssignmentControls, error)
|
||||
func (c *Client) SetProductHDMIAssignmentControls(config HDMIAssignmentControls) error
|
||||
```
|
||||
|
||||
### 4.2 System Administration
|
||||
Advanced system configuration and diagnostics.
|
||||
|
||||
#### Endpoints to Implement:
|
||||
```go
|
||||
// pkg/api/admin.go (new file)
|
||||
func (c *Client) GetCriticalErrors() (*CriticalErrors, error)
|
||||
func (c *Client) PerformFactoryDefault() error
|
||||
func (c *Client) GetBCOReset() (*BCOResetStatus, error)
|
||||
func (c *Client) SetBCOReset(enabled bool) error
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Implementation Guidelines
|
||||
|
||||
### File Structure
|
||||
```
|
||||
pkg/
|
||||
├── api/
|
||||
│ ├── presets.go (Phase 1.1)
|
||||
│ ├── music_services.go (Phase 1.2)
|
||||
│ ├── content.go (Phase 1.3)
|
||||
│ ├── stations.go (Phase 1.4)
|
||||
│ ├── playback.go (Phase 1.5 - extend existing)
|
||||
│ ├── power.go (Phase 1.6)
|
||||
│ ├── notifications.go (Phase 2.1)
|
||||
│ ├── network.go (Phase 2.2 - extend existing)
|
||||
│ ├── bluetooth.go (Phase 2.3)
|
||||
│ ├── system.go (Phase 2.4)
|
||||
│ ├── groups.go (Phase 3.1)
|
||||
│ ├── updates.go (Phase 3.2)
|
||||
│ ├── audio_advanced.go (Phase 3.3)
|
||||
│ ├── sources.go (Phase 3.4 - extend existing)
|
||||
│ ├── product.go (Phase 4.1)
|
||||
│ └── admin.go (Phase 4.2)
|
||||
├── types/
|
||||
│ ├── presets.go
|
||||
│ ├── music_services.go
|
||||
│ ├── content.go
|
||||
│ ├── notifications.go
|
||||
│ ├── network.go
|
||||
│ ├── bluetooth.go
|
||||
│ ├── system.go
|
||||
│ ├── groups.go
|
||||
│ ├── updates.go
|
||||
│ └── product.go
|
||||
└── websocket/
|
||||
└── events.go (extend with new event types)
|
||||
```
|
||||
|
||||
### Error Handling Strategy
|
||||
|
||||
#### Device Capability Checking
|
||||
```go
|
||||
// Always check capabilities before calling advanced features
|
||||
func (c *Client) callAdvancedEndpoint() error {
|
||||
capabilities, err := c.GetCapabilities()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get capabilities: %w", err)
|
||||
}
|
||||
|
||||
if !capabilities.SupportsFeature("targetFeature") {
|
||||
return ErrFeatureNotSupported
|
||||
}
|
||||
|
||||
// Proceed with endpoint call
|
||||
}
|
||||
```
|
||||
|
||||
#### Timeout Handling
|
||||
```go
|
||||
// Some endpoints timeout on unsupported devices
|
||||
func (c *Client) callWithTimeout(endpoint string) error {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
||||
defer cancel()
|
||||
|
||||
// Make request with context
|
||||
if err := c.makeRequest(ctx, endpoint); err != nil {
|
||||
if errors.Is(err, context.DeadlineExceeded) {
|
||||
return ErrEndpointNotSupported
|
||||
}
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
```
|
||||
|
||||
### Testing Strategy
|
||||
|
||||
#### Unit Tests
|
||||
- XML marshaling/unmarshaling for all new types
|
||||
- Error handling scenarios
|
||||
- Input validation
|
||||
|
||||
#### Integration Tests
|
||||
- Real device testing for each endpoint
|
||||
- Device compatibility matrix validation
|
||||
- WebSocket event verification
|
||||
|
||||
#### Device Matrix Testing
|
||||
```go
|
||||
var deviceTests = []struct {
|
||||
model string
|
||||
endpoints []string
|
||||
supported bool
|
||||
}{
|
||||
{"ST-10", []string{"/playNotification", "/getGroup"}, true},
|
||||
{"ST-300", []string{"/audiodspcontrols", "/productcechdmicontrol"}, true},
|
||||
{"ST-10", []string{"/audiodspcontrols"}, false},
|
||||
}
|
||||
```
|
||||
|
||||
### WebSocket Event Integration
|
||||
|
||||
#### Extend Existing Event System
|
||||
```go
|
||||
// pkg/websocket/events.go (extend existing)
|
||||
type WebSocketEvent struct {
|
||||
// Existing events...
|
||||
VolumeUpdated *VolumeUpdate `xml:"volumeUpdated"`
|
||||
NowPlayingUpdated *NowPlayingUpdate `xml:"nowPlayingUpdated"`
|
||||
|
||||
// New events from wiki
|
||||
PresetsUpdated *PresetsUpdate `xml:"presetsUpdated"`
|
||||
GroupUpdated *GroupUpdate `xml:"groupUpdated"`
|
||||
AudioDSPUpdated *AudioDSPUpdate `xml:"audiodspcontrols"`
|
||||
ToneControlsUpdated *ToneUpdate `xml:"audioproducttonecontrols"`
|
||||
LevelControlsUpdated *LevelUpdate `xml:"audioproductlevelcontrols"`
|
||||
}
|
||||
```
|
||||
|
||||
### Documentation Integration
|
||||
|
||||
#### Wiki Examples in Go Docs
|
||||
```go
|
||||
// StorePreset saves a preset to the device (maximum 6 presets).
|
||||
//
|
||||
// Example from SoundTouch Plus Wiki:
|
||||
// preset := PresetData{
|
||||
// ID: 3,
|
||||
// ContentItem: ContentItem{
|
||||
// Source: "TUNEIN",
|
||||
// Type: "stationurl",
|
||||
// Location: "/v1/playback/station/s309605",
|
||||
// IsPresetable: true,
|
||||
// ItemName: "K-LOVE 90s",
|
||||
// ContainerArt: "http://cdn-profiles.tunein.com/s309605/images/logog.png",
|
||||
// },
|
||||
// }
|
||||
// err := client.StorePreset(preset.ID, preset.ContentItem)
|
||||
//
|
||||
// This generates a presetsUpdated WebSocket event.
|
||||
func (c *Client) StorePreset(id int, content ContentItem) error
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Success Metrics
|
||||
|
||||
### Phase 1 Completion Criteria
|
||||
- [ ] All 20 endpoints implemented with full XML support
|
||||
- [ ] Comprehensive unit test coverage (>90%)
|
||||
- [ ] Real device testing on ST-10 and ST-300
|
||||
- [ ] Documentation with wiki examples
|
||||
- [ ] WebSocket event integration
|
||||
|
||||
### Phase 2 Completion Criteria
|
||||
- [ ] Smart home integration examples
|
||||
- [ ] Network management automation
|
||||
- [ ] Notification system with TTS
|
||||
- [ ] Bluetooth management
|
||||
- [ ] Language configuration
|
||||
|
||||
### Phase 3 Completion Criteria
|
||||
- [ ] Stereo pair management
|
||||
- [ ] Software update automation
|
||||
- [ ] Advanced audio features
|
||||
- [ ] Source switching utilities
|
||||
|
||||
### Phase 4 Completion Criteria
|
||||
- [ ] Professional HDMI controls
|
||||
- [ ] System administration features
|
||||
- [ ] Complete device capability matrix
|
||||
- [ ] Production deployment guide
|
||||
|
||||
### Overall Success Metrics
|
||||
- ✅ 87+ total endpoints implemented
|
||||
- ✅ Complete SoundTouch ecosystem coverage
|
||||
- ✅ Production-ready error handling
|
||||
- ✅ Comprehensive documentation
|
||||
- ✅ Real-world testing validation
|
||||
- ✅ Community collaboration with SoundTouch Plus project
|
||||
|
||||
---
|
||||
|
||||
## Risk Mitigation
|
||||
|
||||
### Technical Risks
|
||||
1. **Device Compatibility**: Test each endpoint on multiple device models
|
||||
2. **Timeout Issues**: Implement capability checking before endpoint calls
|
||||
3. **XML Complexity**: Thorough marshaling/unmarshaling tests
|
||||
4. **WebSocket Events**: Validate event generation for all POST operations
|
||||
|
||||
### Schedule Risks
|
||||
1. **Resource Availability**: Prioritize high-impact endpoints first
|
||||
2. **Device Access**: Arrange access to multiple SoundTouch models
|
||||
3. **Complexity Underestimation**: Buffer time in each phase
|
||||
4. **Integration Issues**: Continuous integration testing
|
||||
|
||||
### Quality Risks
|
||||
1. **Incomplete Testing**: Mandate real device validation
|
||||
2. **Poor Documentation**: Use wiki examples in all documentation
|
||||
3. **Breaking Changes**: Maintain backward compatibility
|
||||
4. **Performance**: Benchmark all new endpoints
|
||||
|
||||
---
|
||||
|
||||
## Conclusion
|
||||
|
||||
This implementation plan leverages the comprehensive SoundTouch Plus Wiki to transform our library from basic device control to complete ecosystem management. The phased approach prioritizes user-facing features while ensuring quality and maintainability.
|
||||
|
||||
**Key Benefits:**
|
||||
- 🎯 **3.8x API Coverage Expansion**: From 23 to 87+ endpoints
|
||||
- 🏠 **Complete Smart Home Integration**: Power, notifications, network management
|
||||
- 🎵 **Full Music Service Support**: Spotify, Pandora, NAS libraries
|
||||
- ✅ **Production-Ready Implementation**: Real-world tested examples
|
||||
- 📚 **Comprehensive Documentation**: Wiki integration and examples
|
||||
|
||||
**Timeline:** 13 weeks total for complete implementation
|
||||
**Resources:** 1-2 developers with access to multiple SoundTouch devices
|
||||
**Outcome:** Industry-leading SoundTouch API library with complete ecosystem support
|
||||
|
||||
*This plan transforms our library into the definitive Go implementation for SoundTouch integration, suitable for everything from basic home automation to professional audio installations.*
|
||||
@@ -0,0 +1,377 @@
|
||||
# SoundTouch `/storePreset` Implementation Guide
|
||||
|
||||
## Overview
|
||||
|
||||
This document analyzes the feasibility and implementation approach for adding `/storePreset` functionality to the Bose SoundTouch API client, based on [GitHub Issue #14](https://github.com/gesellix/Bose-SoundTouch/issues/14).
|
||||
|
||||
## Current Implementation Status
|
||||
|
||||
### ✅ Already Implemented
|
||||
- `GetPresets()` - Read presets from device
|
||||
- `SelectPreset()` - Select preset by number (1-6)
|
||||
- `GetNextAvailablePresetSlot()` - Find next available preset slot
|
||||
- `IsCurrentContentPresetable()` - Check if current content can be saved as preset
|
||||
- Complete data models (`models.Preset`, `models.ContentItem`)
|
||||
- WebSocket events for preset updates
|
||||
|
||||
### ❌ Missing Functionality
|
||||
- `StorePreset()` - Save content as preset
|
||||
- `RemovePreset()` - Delete existing preset
|
||||
|
||||
## API Capabilities
|
||||
|
||||
According to the [SoundTouch WebServices API Wiki](https://github.com/thlucas1/homeassistantcomponent_soundtouchplus/wiki/SoundTouch-WebServices-API#preset-store), `/storePreset` supports:
|
||||
|
||||
1. **Radio Stations** (TUNEIN, LOCAL_INTERNET_RADIO)
|
||||
2. **Spotify Content** (Playlists, Albums, Artists, Tracks)
|
||||
3. **Local Music** (STORED_MUSIC, LOCAL_MUSIC)
|
||||
4. **Maximum 6 Presets** per device
|
||||
5. **Automatic Timestamps** (createdOn, updatedOn)
|
||||
6. **WebSocket Events** (`presetsUpdated`)
|
||||
|
||||
## Implementation Examples
|
||||
|
||||
### Core Client Methods
|
||||
|
||||
```go
|
||||
// StorePreset saves content as a preset on the SoundTouch device
|
||||
func (c *Client) StorePreset(id int, contentItem *models.ContentItem) error {
|
||||
now := time.Now().Unix()
|
||||
preset := &models.Preset{
|
||||
ID: id,
|
||||
CreatedOn: &now,
|
||||
UpdatedOn: &now,
|
||||
ContentItem: contentItem,
|
||||
}
|
||||
|
||||
var response models.Presets
|
||||
return c.post("/storePreset", preset, &response)
|
||||
}
|
||||
|
||||
// RemovePreset deletes a preset from the SoundTouch device
|
||||
func (c *Client) RemovePreset(id int) error {
|
||||
preset := &models.Preset{ID: id}
|
||||
var response models.Presets
|
||||
return c.post("/removePreset", preset, &response)
|
||||
}
|
||||
|
||||
// StoreCurrentAsPreset saves currently playing content as preset
|
||||
func (c *Client) StoreCurrentAsPreset(id int) error {
|
||||
nowPlaying, err := c.GetNowPlaying()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get current content: %w", err)
|
||||
}
|
||||
|
||||
if !nowPlaying.ContentItem.IsPresetable {
|
||||
return fmt.Errorf("current content is not presetable")
|
||||
}
|
||||
|
||||
return c.StorePreset(id, nowPlaying.ContentItem)
|
||||
}
|
||||
```
|
||||
|
||||
### CLI Commands
|
||||
|
||||
```bash
|
||||
# Store currently playing content as preset
|
||||
soundtouch-cli --host 192.168.1.100 preset store-current --slot 3
|
||||
|
||||
# Store specific content as preset
|
||||
soundtouch-cli --host 192.168.1.100 preset store \
|
||||
--slot 1 \
|
||||
--source SPOTIFY \
|
||||
--location "spotify:playlist:37i9dQZF1DX0XUsuxWHRQd" \
|
||||
--source-account "yourusername" \
|
||||
--name "My Worship Mix"
|
||||
|
||||
# Store radio station as preset
|
||||
soundtouch-cli --host 192.168.1.100 preset store \
|
||||
--slot 2 \
|
||||
--source TUNEIN \
|
||||
--location "/v1/playback/station/s33828" \
|
||||
--name "K-LOVE Radio"
|
||||
|
||||
# Remove preset
|
||||
soundtouch-cli --host 192.168.1.100 preset remove --slot 3
|
||||
|
||||
# Show current content details (including location URI for all sources)
|
||||
soundtouch-cli --host 192.168.1.100 play now
|
||||
|
||||
# Show detailed content information
|
||||
soundtouch-cli --host 192.168.1.100 play now --verbose
|
||||
```
|
||||
|
||||
## Spotify Integration Examples
|
||||
|
||||
### 1. Spotify Playlist
|
||||
```go
|
||||
contentItem := &models.ContentItem{
|
||||
Source: "SPOTIFY",
|
||||
Type: "uri",
|
||||
Location: "spotify:playlist:37i9dQZF1DX0XUsuxWHRQd",
|
||||
SourceAccount: "yourspotifyusername",
|
||||
IsPresetable: true,
|
||||
ItemName: "My Worship Mix",
|
||||
ContainerArt: "https://i.scdn.co/image/ab67706c0000da84820d2514932c9e2ea40f6473",
|
||||
}
|
||||
```
|
||||
|
||||
### 2. Spotify Album
|
||||
```go
|
||||
contentItem := &models.ContentItem{
|
||||
Source: "SPOTIFY",
|
||||
Type: "uri",
|
||||
Location: "spotify:album:6vc9OTcyd3hyzabCmsdnwE",
|
||||
SourceAccount: "yourspotifyusername",
|
||||
IsPresetable: true,
|
||||
ItemName: "Welcome to the New",
|
||||
ContainerArt: "https://i.scdn.co/image/ab67616d0000b27316c019c87a927829804caf0b",
|
||||
}
|
||||
```
|
||||
|
||||
### 3. Spotify Artist
|
||||
```go
|
||||
contentItem := &models.ContentItem{
|
||||
Source: "SPOTIFY",
|
||||
Type: "uri",
|
||||
Location: "spotify:artist:6APm8EjxOHSYM5B4i3vT3q",
|
||||
SourceAccount: "yourspotifyusername",
|
||||
IsPresetable: true,
|
||||
ItemName: "MercyMe",
|
||||
ContainerArt: "https://i.scdn.co/image/ab6761610000e5eb16c019c87a927829804caf0b",
|
||||
}
|
||||
```
|
||||
|
||||
## Getting Spotify URIs (Location Values)
|
||||
|
||||
### Method 1: From Spotify App
|
||||
1. Right-click on playlist/album/song in Spotify app
|
||||
2. "Share" → "Copy link to playlist"
|
||||
3. Convert URL to URI:
|
||||
- URL: `https://open.spotify.com/playlist/37i9dQZF1DX0XUsuxWHRQd`
|
||||
- URI: `spotify:playlist:37i9dQZF1DX0XUsuxWHRQd`
|
||||
|
||||
### Method 2: From Currently Playing Content (All Sources)
|
||||
```go
|
||||
func getCurrentContentLocation(client *soundtouch.Client) (string, string, error) {
|
||||
nowPlaying, err := client.GetNowPlaying()
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
|
||||
if nowPlaying.ContentItem == nil || nowPlaying.ContentItem.Location == "" {
|
||||
return "", "", fmt.Errorf("no content location available")
|
||||
}
|
||||
|
||||
return nowPlaying.ContentItem.Location, nowPlaying.ContentItem.Source, nil
|
||||
}
|
||||
```
|
||||
|
||||
### Method 3: URL to URI Converter
|
||||
```go
|
||||
func SpotifyURLToURI(url string) (string, error) {
|
||||
re := regexp.MustCompile(`https://open\.spotify\.com/(playlist|album|artist|track|episode|show)/([a-zA-Z0-9]+)`)
|
||||
matches := re.FindStringSubmatch(url)
|
||||
|
||||
if len(matches) != 3 {
|
||||
return "", fmt.Errorf("invalid Spotify URL format")
|
||||
}
|
||||
|
||||
contentType := matches[1]
|
||||
contentID := matches[2]
|
||||
|
||||
return fmt.Sprintf("spotify:%s:%s", contentType, contentID), nil
|
||||
}
|
||||
```
|
||||
|
||||
## XML Request Format
|
||||
|
||||
The actual XML request sent to the SoundTouch API:
|
||||
|
||||
```xml
|
||||
<preset id="3" createdOn="1701220500" updatedOn="1701220500">
|
||||
<ContentItem source="SPOTIFY" type="uri" location="spotify:playlist:37i9dQZF1DX0XUsuxWHRQd" sourceAccount="yourusername" isPresetable="true">
|
||||
<itemName>My Worship Mix</itemName>
|
||||
<containerArt>https://i.scdn.co/image/ab67706c0000da84820d2514932c9e2ea40f6473</containerArt>
|
||||
</ContentItem>
|
||||
</preset>
|
||||
```
|
||||
|
||||
## Radio Station Examples
|
||||
|
||||
### TUNEIN Radio
|
||||
```go
|
||||
contentItem := &models.ContentItem{
|
||||
Source: "TUNEIN",
|
||||
Type: "stationurl",
|
||||
Location: "/v1/playback/station/s33828",
|
||||
SourceAccount: "",
|
||||
IsPresetable: true,
|
||||
ItemName: "K-LOVE Radio",
|
||||
ContainerArt: "http://cdn-profiles.tunein.com/s33828/images/logog.png",
|
||||
}
|
||||
```
|
||||
|
||||
### Local Internet Radio
|
||||
```go
|
||||
contentItem := &models.ContentItem{
|
||||
Source: "LOCAL_INTERNET_RADIO",
|
||||
Type: "stationurl",
|
||||
Location: "https://content.api.bose.io/core02/svc-bmx-adapter-orion/prod/orion/station?data=eyJ...",
|
||||
SourceAccount: "",
|
||||
IsPresetable: true,
|
||||
ItemName: "Custom Radio Station",
|
||||
ContainerArt: "",
|
||||
}
|
||||
```
|
||||
|
||||
## Implementation Roadmap
|
||||
|
||||
### Phase 1: Core Functionality
|
||||
1. Add `StorePreset()` method to client
|
||||
2. Add `RemovePreset()` method to client
|
||||
3. Add basic CLI commands
|
||||
4. Add unit tests
|
||||
|
||||
### Phase 2: Enhanced CLI
|
||||
1. Add `store-current` command
|
||||
2. Add Spotify URL-to-URI conversion
|
||||
3. Add content validation
|
||||
4. Add batch import functionality
|
||||
|
||||
### Phase 3: Advanced Features
|
||||
1. Add preset management utilities
|
||||
2. Add content discovery helpers
|
||||
3. Add preset backup/restore
|
||||
4. Integration with Spotify Web API for search
|
||||
|
||||
## Technical Requirements
|
||||
|
||||
### Prerequisites
|
||||
- Existing HTTP client infrastructure ✅
|
||||
- XML marshaling/unmarshaling ✅
|
||||
- WebSocket event system ✅
|
||||
- CLI framework ✅
|
||||
- Data models ✅
|
||||
|
||||
### Implementation Effort
|
||||
- **Client methods**: ~50-100 lines of code
|
||||
- **CLI commands**: ~100-150 lines of code
|
||||
- **Tests**: ~200-300 lines of code
|
||||
- **Documentation**: This document + API docs
|
||||
|
||||
## WebSocket Events
|
||||
|
||||
When presets are stored or removed, the device generates `presetsUpdated` events:
|
||||
|
||||
```xml
|
||||
<updates deviceID="1004567890AA">
|
||||
<presetsUpdated>
|
||||
<presets>
|
||||
<preset id="1" createdOn="1700536011" updatedOn="1700536011">
|
||||
<ContentItem source="SPOTIFY" type="uri" location="spotify:playlist:37i9dQZF1DX0XUsuxWHRQd" sourceAccount="username" isPresetable="true">
|
||||
<itemName>My Worship Mix</itemName>
|
||||
<containerArt>https://i.scdn.co/image/ab67706c0000da84820d2514932c9e2ea40f6473</containerArt>
|
||||
</ContentItem>
|
||||
</preset>
|
||||
</presets>
|
||||
</presetsUpdated>
|
||||
</updates>
|
||||
```
|
||||
|
||||
## CLI Command Updates
|
||||
|
||||
The CLI now automatically shows location details for **all sources** when using `play now`:
|
||||
|
||||
### Automatic Location Display
|
||||
```bash
|
||||
# Location automatically shown for any source with location data
|
||||
go run ./cmd/soundtouch-cli --host 192.168.1.100 play now
|
||||
```
|
||||
|
||||
**Example outputs:**
|
||||
|
||||
**TUNEIN Radio:**
|
||||
```
|
||||
Now Playing:
|
||||
Source: TUNEIN
|
||||
Track: K-LOVE Radio
|
||||
|
||||
Content Details:
|
||||
Location: /v1/playbook/station/s33828
|
||||
```
|
||||
|
||||
**LOCAL_INTERNET_RADIO:**
|
||||
```
|
||||
Now Playing:
|
||||
Source: LOCAL_INTERNET_RADIO
|
||||
Track: Custom Radio Station
|
||||
|
||||
Content Details:
|
||||
Location: https://stream.example.com/radio
|
||||
```
|
||||
|
||||
**STORED_MUSIC (NAS):**
|
||||
```
|
||||
Now Playing:
|
||||
Source: STORED_MUSIC
|
||||
Track: Welcome Home
|
||||
Artist: MercyMe
|
||||
|
||||
Content Details:
|
||||
Location: 6_a2874b5d_4f83d999
|
||||
```
|
||||
|
||||
### Verbose Mode for Complete Details
|
||||
```bash
|
||||
go run ./cmd/soundtouch-cli --host 192.168.1.100 play now --verbose
|
||||
```
|
||||
|
||||
Shows additional information:
|
||||
```
|
||||
Content Details:
|
||||
Location: /v1/playbook/station/s33828
|
||||
Content Type: stationurl
|
||||
Item Name: K-LOVE Radio
|
||||
Presetable: true
|
||||
```
|
||||
|
||||
## Use Cases
|
||||
|
||||
1. **Quick Access to Favorite Playlists**: Store frequently used Spotify playlists as presets 1-6
|
||||
2. **Radio Station Shortcuts**: Save favorite TUNEIN and internet radio stations for instant access
|
||||
3. **NAS Music Collections**: Store favorite albums from your network storage as presets
|
||||
4. **Pandora Stations**: Save your custom Pandora radio stations for quick access
|
||||
5. **Mood-based Presets**: Organize content by activity (workout, relaxation, work)
|
||||
6. **Family-friendly Setup**: Each family member gets their own preset slots
|
||||
7. **Smart Home Integration**: Trigger specific music for different scenarios
|
||||
|
||||
## Spotify URI Reference
|
||||
|
||||
## Location Reference for All Sources
|
||||
|
||||
| Source | Location Format | Example |
|
||||
|--------|-----------------|---------|
|
||||
| **Spotify Playlist** | `spotify:playlist:ID` | `spotify:playlist:37i9dQZF1DX0XUsuxWHRQd` |
|
||||
| **Spotify Album** | `spotify:album:ID` | `spotify:album:4aawyAB9vmqN3uQ7FjRGTy` |
|
||||
| **Spotify Artist** | `spotify:artist:ID` | `spotify:artist:6APm8EjxOHSYM5B4i3vT3q` |
|
||||
| **Spotify Track** | `spotify:track:ID` | `spotify:track:17GmwQ9Q3MTAz05OokmNNB` |
|
||||
| **TUNEIN Radio** | `/v1/playbook/station/ID` | `/v1/playbook/station/s33828` |
|
||||
| **Internet Radio** | `URL or encoded URL` | `https://stream.example.com/radio` |
|
||||
| **STORED_MUSIC** | `Container ID` | `6_a2874b5d_4f83d999` |
|
||||
| **LOCAL_MUSIC** | `album:ID` or `track:ID` | `album:983`, `track:2579` |
|
||||
| **PANDORA Station** | `Station ID` | `126740707481236361` |
|
||||
|
||||
## Conclusion
|
||||
|
||||
The `/storePreset` feature is **highly feasible** and would add significant value to the SoundTouch API client. The existing infrastructure provides a solid foundation, and the implementation would be straightforward.
|
||||
|
||||
Key benefits:
|
||||
- ✅ **User-friendly**: Simple CLI commands for preset management with automatic location detection
|
||||
- ✅ **Universal**: Supports ALL content sources (Spotify, TUNEIN, Internet Radio, NAS Music, Pandora, Local Music)
|
||||
- ✅ **Well-documented**: Complete API specification available
|
||||
- ✅ **Event-driven**: WebSocket integration for real-time updates
|
||||
- ✅ **Low complexity**: Leverages existing code patterns and infrastructure
|
||||
- ✅ **Enhanced CLI**: Automatic location display makes it easy to capture preset data
|
||||
|
||||
This feature would enable SoundTouch users to fully utilize their device's preset capabilities programmatically, making it easier to manage and access their favorite content from any supported source.
|
||||
Reference in New Issue
Block a user