mirror of
https://github.com/gesellix/Bose-SoundTouch.git
synced 2026-08-18 08:36:13 +00:00
feat: Implement official /addZoneSlave and /removeZoneSlave endpoints
Implements the remaining zone slave management endpoints from the official Bose SoundTouch Web API v1.0 specification, bringing API coverage to 89%. ## New Features ### Client Methods - AddZoneSlave(masterID, slaveID, slaveIP) - Add individual device to zone - AddZoneSlaveByDeviceID(masterID, slaveID) - Add device by ID only - RemoveZoneSlave(masterID, slaveID, slaveIP) - Remove individual device - RemoveZoneSlaveByDeviceID(masterID, slaveID) - Remove device by ID only ### Models - ZoneSlaveRequest - Request structure for slave operations - ZoneSlaveEntry - Individual slave entry with IP address support - Complete XML marshaling/unmarshaling with proper omitempty handling - Comprehensive validation and error handling ### CLI Commands - zone add-slave --master ID --slave ID [--slave-ip IP] - zone remove-slave --master ID --slave ID [--slave-ip IP] ## Implementation Details - Follows official API specification exactly (POST /addZoneSlave, /removeZoneSlave) - Supports both device ID + IP and device ID only operations - Comprehensive input validation (IP addresses, device ID conflicts) - Proper XML formatting with omitempty for optional IP addresses - Extensive test coverage (580+ lines of tests) - Integration with existing high-level zone management API ## Testing - 200+ new test cases covering all functionality - Complete model validation and XML marshaling tests - HTTP client integration tests with mock servers - Error handling and edge case coverage - Network error simulation tests ## Documentation Updates - Updated API coverage from 84% to 89% (17/19 endpoints) - Comprehensive API coverage analysis document - Updated README.md with new endpoint status - Added practical usage examples - CLI help documentation ## Compatibility - Maintains full backward compatibility - Complements existing high-level zone API - Users can choose between low-level official API or enhanced high-level API - No breaking changes to existing functionality This implementation provides both the exact official API endpoints and enhanced high-level zone management, giving users maximum flexibility for zone operations while maintaining full API compliance.
This commit is contained in:
@@ -0,0 +1,147 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"time"
|
||||
|
||||
"github.com/gesellix/bose-soundtouch/pkg/client"
|
||||
)
|
||||
|
||||
func main() {
|
||||
// Configure your device
|
||||
deviceIP := "192.168.1.100" // Replace with your SoundTouch device IP
|
||||
|
||||
// Create client
|
||||
soundtouchClient := client.NewClientFromHost(deviceIP)
|
||||
|
||||
fmt.Println("🎵 Bose SoundTouch Zone Slave Operations Example")
|
||||
fmt.Println("==============================================")
|
||||
|
||||
// Example 1: Add a slave to an existing zone using official /addZoneSlave endpoint
|
||||
fmt.Println("\n1. Adding slave to zone using official API...")
|
||||
|
||||
masterDeviceID := "ABCD1234EFGH" // Replace with actual master device ID
|
||||
slaveDeviceID := "WXYZ5678IJKL" // Replace with actual slave device ID
|
||||
slaveIP := "192.168.1.101" // Replace with actual slave IP
|
||||
|
||||
err := soundtouchClient.AddZoneSlave(masterDeviceID, slaveDeviceID, slaveIP)
|
||||
if err != nil {
|
||||
log.Printf("❌ Failed to add zone slave: %v", err)
|
||||
} else {
|
||||
fmt.Printf("✅ Successfully added slave '%s' to master '%s'\n", slaveDeviceID, masterDeviceID)
|
||||
}
|
||||
|
||||
// Wait a moment for the zone change to take effect
|
||||
time.Sleep(2 * time.Second)
|
||||
|
||||
// Example 2: Check zone status after adding slave
|
||||
fmt.Println("\n2. Checking zone status...")
|
||||
|
||||
zone, err := soundtouchClient.GetZone()
|
||||
if err != nil {
|
||||
log.Printf("❌ Failed to get zone info: %v", err)
|
||||
} else {
|
||||
fmt.Printf("📡 Zone Status: %s\n", zone.String())
|
||||
fmt.Printf(" Total devices: %d\n", zone.GetTotalDeviceCount())
|
||||
|
||||
for _, member := range zone.Members {
|
||||
fmt.Printf(" Member: %s (%s)\n", member.DeviceID, member.IP)
|
||||
}
|
||||
}
|
||||
|
||||
// Example 3: Add slave by device ID only (without IP)
|
||||
fmt.Println("\n3. Adding another slave by device ID only...")
|
||||
|
||||
anotherSlaveID := "PQRS9012MNOP" // Replace with actual device ID
|
||||
|
||||
err = soundtouchClient.AddZoneSlaveByDeviceID(masterDeviceID, anotherSlaveID)
|
||||
if err != nil {
|
||||
log.Printf("❌ Failed to add zone slave by ID: %v", err)
|
||||
} else {
|
||||
fmt.Printf("✅ Successfully added slave '%s' to master '%s' (by ID only)\n", anotherSlaveID, masterDeviceID)
|
||||
}
|
||||
|
||||
time.Sleep(2 * time.Second)
|
||||
|
||||
// Example 4: Remove a slave from the zone using official /removeZoneSlave endpoint
|
||||
fmt.Println("\n4. Removing slave from zone using official API...")
|
||||
|
||||
err = soundtouchClient.RemoveZoneSlave(masterDeviceID, slaveDeviceID, slaveIP)
|
||||
if err != nil {
|
||||
log.Printf("❌ Failed to remove zone slave: %v", err)
|
||||
} else {
|
||||
fmt.Printf("✅ Successfully removed slave '%s' from master '%s'\n", slaveDeviceID, masterDeviceID)
|
||||
}
|
||||
|
||||
time.Sleep(2 * time.Second)
|
||||
|
||||
// Example 5: Remove slave by device ID only
|
||||
fmt.Println("\n5. Removing another slave by device ID only...")
|
||||
|
||||
err = soundtouchClient.RemoveZoneSlaveByDeviceID(masterDeviceID, anotherSlaveID)
|
||||
if err != nil {
|
||||
log.Printf("❌ Failed to remove zone slave by ID: %v", err)
|
||||
} else {
|
||||
fmt.Printf("✅ Successfully removed slave '%s' from master '%s' (by ID only)\n", anotherSlaveID, masterDeviceID)
|
||||
}
|
||||
|
||||
// Example 6: Final zone status check
|
||||
fmt.Println("\n6. Final zone status...")
|
||||
|
||||
finalZone, err := soundtouchClient.GetZone()
|
||||
if err != nil {
|
||||
log.Printf("❌ Failed to get final zone info: %v", err)
|
||||
} else {
|
||||
fmt.Printf("📡 Final Zone Status: %s\n", finalZone.String())
|
||||
|
||||
if finalZone.IsStandalone() {
|
||||
fmt.Println(" Device is now standalone (no zone)")
|
||||
} else {
|
||||
fmt.Printf(" Zone has %d total devices\n", finalZone.GetTotalDeviceCount())
|
||||
}
|
||||
}
|
||||
|
||||
// Example 7: Comparison with high-level zone API
|
||||
fmt.Println("\n7. Comparison: High-level zone API (enhanced functionality)...")
|
||||
fmt.Println(" For more complex zone operations, you can also use:")
|
||||
fmt.Printf(" - soundtouchClient.CreateZoneWithIPs(master, []string{slave1, slave2})\n")
|
||||
fmt.Printf(" - soundtouchClient.AddToZone(master, slave)\n")
|
||||
fmt.Printf(" - soundtouchClient.RemoveFromZone(master, slave)\n")
|
||||
fmt.Printf(" - soundtouchClient.DissolveZone(master)\n")
|
||||
|
||||
fmt.Println("\n🎉 Zone slave operations example completed!")
|
||||
|
||||
// Example 8: Error handling demonstration
|
||||
fmt.Println("\n8. Error handling example...")
|
||||
|
||||
// Try to add a non-existent device to demonstrate error handling
|
||||
err = soundtouchClient.AddZoneSlave("INVALID123", "NOTFOUND456", "192.168.1.999")
|
||||
if err != nil {
|
||||
fmt.Printf("⚠️ Expected error for invalid operation: %v\n", err)
|
||||
fmt.Println(" This demonstrates proper error handling for invalid device IDs or IPs")
|
||||
}
|
||||
}
|
||||
|
||||
// Notes for usage:
|
||||
//
|
||||
// 1. Replace the device IPs and IDs with your actual SoundTouch devices
|
||||
// 2. Ensure devices are on the same network and powered on
|
||||
// 3. The master device should be capable of creating zones
|
||||
// 4. Zone slave operations require exact device IDs (MAC addresses)
|
||||
// 5. IP addresses are optional but recommended for faster operations
|
||||
//
|
||||
// To get device IDs:
|
||||
// info, _ := soundtouchClient.GetDeviceInfo()
|
||||
// deviceID := info.DeviceID
|
||||
//
|
||||
// To discover devices on your network:
|
||||
// Use the discovery package or the soundtouch-cli discover command
|
||||
//
|
||||
// Official API endpoints implemented:
|
||||
// POST /addZoneSlave - Add individual slave to existing zone
|
||||
// POST /removeZoneSlave - Remove individual slave from existing zone
|
||||
//
|
||||
// These complement the high-level zone management API:
|
||||
// GET /getZone - Get zone information
|
||||
// POST /setZone - Create/modify zones with multiple members
|
||||
Reference in New Issue
Block a user