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 }