mirror of
https://github.com/gesellix/Bose-SoundTouch.git
synced 2026-08-10 12:46:13 +00:00
- Reference original API documentation source from Bose Corporation - Link to official Bose SoundTouch End-of-Life page - Clarify this is an independent implementation - Add disclaimer about non-affiliation with Bose Corporation - Provide both online and local documentation references
327 lines
8.0 KiB
Go
327 lines
8.0 KiB
Go
package main
|
|
|
|
import (
|
|
"fmt"
|
|
"net"
|
|
"strings"
|
|
|
|
"github.com/gesellix/bose-soundtouch/pkg/models"
|
|
"github.com/urfave/cli/v2"
|
|
)
|
|
|
|
// getZone retrieves the current zone configuration
|
|
func getZone(c *cli.Context) error {
|
|
clientConfig := GetClientConfig(c)
|
|
PrintDeviceHeader("Getting zone information", clientConfig.Host, clientConfig.Port)
|
|
|
|
client, err := CreateSoundTouchClient(clientConfig)
|
|
if err != nil {
|
|
PrintError(fmt.Sprintf("Failed to create client: %v", err))
|
|
return err
|
|
}
|
|
|
|
zone, err := client.GetZone()
|
|
if err != nil {
|
|
PrintError(fmt.Sprintf("Failed to get zone: %v", err))
|
|
return err
|
|
}
|
|
|
|
if zone.Master == "" {
|
|
fmt.Println("Device is not in a zone")
|
|
return nil
|
|
}
|
|
|
|
fmt.Println("Zone Configuration:")
|
|
fmt.Printf(" Master: %s\n", zone.Master)
|
|
|
|
if len(zone.Members) > 0 {
|
|
fmt.Printf(" Members (%d):\n", len(zone.Members))
|
|
|
|
for _, member := range zone.Members {
|
|
fmt.Printf(" - %s", member.DeviceID)
|
|
|
|
if member.IP != "" {
|
|
fmt.Printf(" (IP: %s)", member.IP)
|
|
}
|
|
|
|
fmt.Println()
|
|
}
|
|
} else {
|
|
fmt.Println(" Members: none (standalone device)")
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// getZoneStatus retrieves the zone status
|
|
func getZoneStatus(c *cli.Context) error {
|
|
clientConfig := GetClientConfig(c)
|
|
PrintDeviceHeader("Getting zone status", clientConfig.Host, clientConfig.Port)
|
|
|
|
client, err := CreateSoundTouchClient(clientConfig)
|
|
if err != nil {
|
|
PrintError(fmt.Sprintf("Failed to create client: %v", err))
|
|
return err
|
|
}
|
|
|
|
status, err := client.GetZoneStatus()
|
|
if err != nil {
|
|
PrintError(fmt.Sprintf("Failed to get zone status: %v", err))
|
|
return err
|
|
}
|
|
|
|
fmt.Printf("Zone Status: %s\n", status)
|
|
|
|
inZone, err := client.IsInZone()
|
|
if err != nil {
|
|
PrintWarning(fmt.Sprintf("Could not determine zone membership: %v", err))
|
|
} else {
|
|
fmt.Printf("In Zone: %t\n", inZone)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// getZoneMembers lists all zone members
|
|
func getZoneMembers(c *cli.Context) error {
|
|
clientConfig := GetClientConfig(c)
|
|
PrintDeviceHeader("Getting zone members", clientConfig.Host, clientConfig.Port)
|
|
|
|
client, err := CreateSoundTouchClient(clientConfig)
|
|
if err != nil {
|
|
PrintError(fmt.Sprintf("Failed to create client: %v", err))
|
|
return err
|
|
}
|
|
|
|
members, err := client.GetZoneMembers()
|
|
if err != nil {
|
|
PrintError(fmt.Sprintf("Failed to get zone members: %v", err))
|
|
return err
|
|
}
|
|
|
|
if len(members) == 0 {
|
|
fmt.Println("No zone members found")
|
|
return nil
|
|
}
|
|
|
|
fmt.Printf("Zone Members (%d):\n", len(members))
|
|
|
|
for i, member := range members {
|
|
fmt.Printf(" %d. %s", i+1, member)
|
|
|
|
if member == clientConfig.Host {
|
|
fmt.Print(" (this device)")
|
|
}
|
|
|
|
fmt.Println()
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// createZone creates a new zone with specified members
|
|
func createZone(c *cli.Context) error {
|
|
members := c.StringSlice("members")
|
|
clientConfig := GetClientConfig(c)
|
|
|
|
if len(members) == 0 {
|
|
PrintError("At least one member must be specified")
|
|
return fmt.Errorf("no members specified")
|
|
}
|
|
|
|
PrintDeviceHeader(fmt.Sprintf("Creating zone with %d members", len(members)), clientConfig.Host, clientConfig.Port)
|
|
|
|
client, err := CreateSoundTouchClient(clientConfig)
|
|
if err != nil {
|
|
PrintError(fmt.Sprintf("Failed to create client: %v", err))
|
|
return err
|
|
}
|
|
|
|
// Parse and validate member IPs
|
|
var memberIPs []net.IP
|
|
|
|
for _, member := range members {
|
|
ip := net.ParseIP(member)
|
|
if ip == nil {
|
|
PrintError(fmt.Sprintf("Invalid IP address: %s", member))
|
|
return fmt.Errorf("invalid IP address: %s", member)
|
|
}
|
|
|
|
memberIPs = append(memberIPs, ip)
|
|
}
|
|
|
|
// For simplicity, use the first member as master and rest as members
|
|
// In a real scenario, you might want to specify the master separately
|
|
masterDeviceID := "master" // This would need to be a real device ID
|
|
memberMap := make(map[string]string)
|
|
|
|
for i, ip := range memberIPs {
|
|
deviceID := fmt.Sprintf("device_%d", i+1)
|
|
memberMap[deviceID] = ip.String()
|
|
}
|
|
|
|
err = client.CreateZoneWithIPs(masterDeviceID, memberMap)
|
|
if err != nil {
|
|
PrintError(fmt.Sprintf("Failed to create zone: %v", err))
|
|
return err
|
|
}
|
|
|
|
PrintSuccess(fmt.Sprintf("Zone created with members: %s", strings.Join(members, ", ")))
|
|
|
|
return nil
|
|
}
|
|
|
|
// addToZone adds a device to the current zone
|
|
func addToZone(c *cli.Context) error {
|
|
memberIP := c.String("member")
|
|
clientConfig := GetClientConfig(c)
|
|
|
|
if memberIP == "" {
|
|
PrintError("Member IP address is required")
|
|
return fmt.Errorf("member IP is required")
|
|
}
|
|
|
|
PrintDeviceHeader(fmt.Sprintf("Adding %s to zone", memberIP), clientConfig.Host, clientConfig.Port)
|
|
|
|
client, err := CreateSoundTouchClient(clientConfig)
|
|
if err != nil {
|
|
PrintError(fmt.Sprintf("Failed to create client: %v", err))
|
|
return err
|
|
}
|
|
|
|
// Validate IP address
|
|
ip := net.ParseIP(memberIP)
|
|
if ip == nil {
|
|
PrintError(fmt.Sprintf("Invalid IP address: %s", memberIP))
|
|
return fmt.Errorf("invalid IP address: %s", memberIP)
|
|
}
|
|
|
|
// For this example, we'll use the IP as the device ID
|
|
// In practice, you'd need the actual device ID
|
|
deviceID := memberIP
|
|
|
|
err = client.AddToZone(deviceID, memberIP)
|
|
if err != nil {
|
|
PrintError(fmt.Sprintf("Failed to add to zone: %v", err))
|
|
return err
|
|
}
|
|
|
|
PrintSuccess(fmt.Sprintf("Added %s to zone", memberIP))
|
|
|
|
return nil
|
|
}
|
|
|
|
// removeFromZone removes a device from the current zone
|
|
func removeFromZone(c *cli.Context) error {
|
|
memberIP := c.String("member")
|
|
clientConfig := GetClientConfig(c)
|
|
|
|
if memberIP == "" {
|
|
PrintError("Member IP address is required")
|
|
return fmt.Errorf("member IP is required")
|
|
}
|
|
|
|
PrintDeviceHeader(fmt.Sprintf("Removing %s from zone", memberIP), clientConfig.Host, clientConfig.Port)
|
|
|
|
client, err := CreateSoundTouchClient(clientConfig)
|
|
if err != nil {
|
|
PrintError(fmt.Sprintf("Failed to create client: %v", err))
|
|
return err
|
|
}
|
|
|
|
// Validate IP address
|
|
ip := net.ParseIP(memberIP)
|
|
if ip == nil {
|
|
PrintError(fmt.Sprintf("Invalid IP address: %s", memberIP))
|
|
return fmt.Errorf("invalid IP address: %s", memberIP)
|
|
}
|
|
|
|
// For this example, we'll use the IP as the device ID
|
|
// In practice, you'd need the actual device ID
|
|
deviceID := memberIP
|
|
|
|
err = client.RemoveFromZone(deviceID)
|
|
if err != nil {
|
|
PrintError(fmt.Sprintf("Failed to remove from zone: %v", err))
|
|
return err
|
|
}
|
|
|
|
PrintSuccess(fmt.Sprintf("Removed %s from zone", memberIP))
|
|
|
|
return nil
|
|
}
|
|
|
|
// dissolveZone dissolves the current zone
|
|
func dissolveZone(c *cli.Context) error {
|
|
clientConfig := GetClientConfig(c)
|
|
PrintDeviceHeader("Dissolving zone", clientConfig.Host, clientConfig.Port)
|
|
|
|
client, err := CreateSoundTouchClient(clientConfig)
|
|
if err != nil {
|
|
PrintError(fmt.Sprintf("Failed to create client: %v", err))
|
|
return err
|
|
}
|
|
|
|
err = client.DissolveZone()
|
|
if err != nil {
|
|
PrintError(fmt.Sprintf("Failed to dissolve zone: %v", err))
|
|
return err
|
|
}
|
|
|
|
PrintSuccess("Zone dissolved")
|
|
|
|
return nil
|
|
}
|
|
|
|
// setZoneConfig sets zone configuration
|
|
func setZoneConfig(c *cli.Context) error {
|
|
master := c.String("master")
|
|
members := c.StringSlice("members")
|
|
clientConfig := GetClientConfig(c)
|
|
|
|
if master == "" {
|
|
PrintError("Master device IP is required")
|
|
return fmt.Errorf("master IP is required")
|
|
}
|
|
|
|
PrintDeviceHeader(fmt.Sprintf("Setting zone with master %s", master), clientConfig.Host, clientConfig.Port)
|
|
|
|
client, err := CreateSoundTouchClient(clientConfig)
|
|
if err != nil {
|
|
PrintError(fmt.Sprintf("Failed to create client: %v", err))
|
|
return err
|
|
}
|
|
|
|
// Create zone configuration using ZoneRequest
|
|
zoneRequest := models.NewZoneRequest(master)
|
|
|
|
// Add members if specified
|
|
if len(members) > 0 {
|
|
for _, memberIP := range members {
|
|
// Validate IP address
|
|
if net.ParseIP(memberIP) == nil {
|
|
PrintError(fmt.Sprintf("Invalid member IP address: %s", memberIP))
|
|
return fmt.Errorf("invalid IP address: %s", memberIP)
|
|
}
|
|
|
|
// Use IP as device ID for simplicity - in practice you'd need real device IDs
|
|
deviceID := memberIP
|
|
zoneRequest.AddMember(deviceID, memberIP)
|
|
}
|
|
}
|
|
|
|
err = client.SetZone(zoneRequest)
|
|
if err != nil {
|
|
PrintError(fmt.Sprintf("Failed to set zone: %v", err))
|
|
return err
|
|
}
|
|
|
|
if len(members) > 0 {
|
|
PrintSuccess(fmt.Sprintf("Zone configured with master %s and members: %s", master, strings.Join(members, ", ")))
|
|
} else {
|
|
PrintSuccess(fmt.Sprintf("Zone configured with master %s", master))
|
|
}
|
|
|
|
return nil
|
|
}
|