mirror of
https://github.com/gesellix/Bose-SoundTouch.git
synced 2026-08-18 08:36:13 +00:00
feat: complete CLI refactoring with urfave/cli
- Refactored soundtouch-cli from monolithic main.go to modular command structure - Added urfave/cli/v2 dependency for better CLI organization - Created separate command files for all SoundTouch features: * cmd_discover.go - Device discovery commands * cmd_info.go - Device information commands * cmd_volume.go - Volume control commands * cmd_playback.go - Playback control commands * cmd_source.go - Source selection commands * cmd_bass.go - Bass control commands (NEW) * cmd_balance.go - Balance control commands (NEW) * cmd_clock.go - Clock/time management commands (NEW) * cmd_network.go - Network information commands (NEW) * cmd_zone.go - Multi-room zone management commands (NEW) * common.go - Shared utilities and client setup - Fixed flag conflicts by using --verbose instead of -v and removing -h alias from --host - Implemented comprehensive CLI with organized subcommands and consistent UX - Added proper help documentation and parameter validation - Reduced golangci-lint issues by 70% (108+ → 32) - Added package comments to new command files Breaking changes: - CLI now uses subcommands instead of flat flags - Old: soundtouch-cli -host 192.168.1.100 -volume - New: soundtouch-cli volume get --host 192.168.1.100 Examples: - soundtouch-cli discover devices --all - soundtouch-cli volume set --host 192.168.1.100 --level 50 - soundtouch-cli bass get --host 192.168.1.100 - soundtouch-cli clock set --host 192.168.1.100 --time '14:30' - soundtouch-cli zone create --host 192.168.1.100 --members 192.168.1.101,192.168.1.102
This commit is contained in:
@@ -0,0 +1,147 @@
|
||||
// Package main provides the soundtouch-cli balance control commands.
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
// getBalance retrieves the current balance level from the device
|
||||
func getBalance(c *cli.Context) error {
|
||||
clientConfig := GetClientConfig(c)
|
||||
PrintDeviceHeader("Getting balance level", clientConfig.Host, clientConfig.Port)
|
||||
|
||||
client, err := CreateSoundTouchClient(clientConfig)
|
||||
if err != nil {
|
||||
PrintError(fmt.Sprintf("Failed to create client: %v", err))
|
||||
return err
|
||||
}
|
||||
|
||||
balance, err := client.GetBalance()
|
||||
if err != nil {
|
||||
PrintError(fmt.Sprintf("Failed to get balance: %v", err))
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Printf("Current balance level: %d\n", balance.ActualBalance)
|
||||
|
||||
if balance.TargetBalance != balance.ActualBalance {
|
||||
fmt.Printf("Target balance level: %d\n", balance.TargetBalance)
|
||||
}
|
||||
|
||||
// Display balance direction
|
||||
switch {
|
||||
case balance.ActualBalance > 0:
|
||||
fmt.Printf("Balance direction: Right (+%d)\n", balance.ActualBalance)
|
||||
case balance.ActualBalance < 0:
|
||||
fmt.Printf("Balance direction: Left (%d)\n", balance.ActualBalance)
|
||||
default:
|
||||
fmt.Println("Balance direction: Center (0)")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// setBalance sets the balance level on the device
|
||||
func setBalance(c *cli.Context) error {
|
||||
level := c.Int("level")
|
||||
clientConfig := GetClientConfig(c)
|
||||
PrintDeviceHeader(fmt.Sprintf("Setting balance level to %d", level), clientConfig.Host, clientConfig.Port)
|
||||
|
||||
client, err := CreateSoundTouchClient(clientConfig)
|
||||
if err != nil {
|
||||
PrintError(fmt.Sprintf("Failed to create client: %v", err))
|
||||
return err
|
||||
}
|
||||
|
||||
err = client.SetBalanceSafe(level)
|
||||
if err != nil {
|
||||
PrintError(fmt.Sprintf("Failed to set balance: %v", err))
|
||||
return err
|
||||
}
|
||||
|
||||
PrintSuccess(fmt.Sprintf("Balance level set to %d", level))
|
||||
return nil
|
||||
}
|
||||
|
||||
// balanceLeft shifts balance to the left
|
||||
func balanceLeft(c *cli.Context) error {
|
||||
amount := c.Int("amount")
|
||||
clientConfig := GetClientConfig(c)
|
||||
PrintDeviceHeader(fmt.Sprintf("Shifting balance left by %d", amount), clientConfig.Host, clientConfig.Port)
|
||||
|
||||
client, err := CreateSoundTouchClient(clientConfig)
|
||||
if err != nil {
|
||||
PrintError(fmt.Sprintf("Failed to create client: %v", err))
|
||||
return err
|
||||
}
|
||||
|
||||
// Get current balance level first
|
||||
currentBalance, err := client.GetBalance()
|
||||
if err != nil {
|
||||
PrintError(fmt.Sprintf("Failed to get current balance: %v", err))
|
||||
return err
|
||||
}
|
||||
|
||||
newLevel := currentBalance.ActualBalance - amount
|
||||
err = client.SetBalanceSafe(newLevel)
|
||||
if err != nil {
|
||||
PrintError(fmt.Sprintf("Failed to shift balance left: %v", err))
|
||||
return err
|
||||
}
|
||||
|
||||
PrintSuccess(fmt.Sprintf("Balance shifted from %d to %d (left)", currentBalance.ActualBalance, newLevel))
|
||||
return nil
|
||||
}
|
||||
|
||||
// balanceRight shifts balance to the right
|
||||
func balanceRight(c *cli.Context) error {
|
||||
amount := c.Int("amount")
|
||||
clientConfig := GetClientConfig(c)
|
||||
PrintDeviceHeader(fmt.Sprintf("Shifting balance right by %d", amount), clientConfig.Host, clientConfig.Port)
|
||||
|
||||
client, err := CreateSoundTouchClient(clientConfig)
|
||||
if err != nil {
|
||||
PrintError(fmt.Sprintf("Failed to create client: %v", err))
|
||||
return err
|
||||
}
|
||||
|
||||
// Get current balance level first
|
||||
currentBalance, err := client.GetBalance()
|
||||
if err != nil {
|
||||
PrintError(fmt.Sprintf("Failed to get current balance: %v", err))
|
||||
return err
|
||||
}
|
||||
|
||||
newLevel := currentBalance.ActualBalance + amount
|
||||
err = client.SetBalanceSafe(newLevel)
|
||||
if err != nil {
|
||||
PrintError(fmt.Sprintf("Failed to shift balance right: %v", err))
|
||||
return err
|
||||
}
|
||||
|
||||
PrintSuccess(fmt.Sprintf("Balance shifted from %d to %d (right)", currentBalance.ActualBalance, newLevel))
|
||||
return nil
|
||||
}
|
||||
|
||||
// balanceCenter centers the balance (sets to 0)
|
||||
func balanceCenter(c *cli.Context) error {
|
||||
clientConfig := GetClientConfig(c)
|
||||
PrintDeviceHeader("Centering balance", clientConfig.Host, clientConfig.Port)
|
||||
|
||||
client, err := CreateSoundTouchClient(clientConfig)
|
||||
if err != nil {
|
||||
PrintError(fmt.Sprintf("Failed to create client: %v", err))
|
||||
return err
|
||||
}
|
||||
|
||||
err = client.SetBalanceSafe(0)
|
||||
if err != nil {
|
||||
PrintError(fmt.Sprintf("Failed to center balance: %v", err))
|
||||
return err
|
||||
}
|
||||
|
||||
PrintSuccess("Balance centered")
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,143 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
// getBass retrieves the current bass level from the device
|
||||
func getBass(c *cli.Context) error {
|
||||
clientConfig := GetClientConfig(c)
|
||||
PrintDeviceHeader("Getting bass level", clientConfig.Host, clientConfig.Port)
|
||||
|
||||
client, err := CreateSoundTouchClient(clientConfig)
|
||||
if err != nil {
|
||||
PrintError(fmt.Sprintf("Failed to create client: %v", err))
|
||||
return err
|
||||
}
|
||||
|
||||
bass, err := client.GetBass()
|
||||
if err != nil {
|
||||
PrintError(fmt.Sprintf("Failed to get bass: %v", err))
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Printf("Current bass level: %d\n", bass.ActualBass)
|
||||
|
||||
if bass.TargetBass != bass.ActualBass {
|
||||
fmt.Printf("Target bass level: %d\n", bass.TargetBass)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// setBass sets the bass level on the device
|
||||
func setBass(c *cli.Context) error {
|
||||
level := c.Int("level")
|
||||
clientConfig := GetClientConfig(c)
|
||||
PrintDeviceHeader(fmt.Sprintf("Setting bass level to %d", level), clientConfig.Host, clientConfig.Port)
|
||||
|
||||
client, err := CreateSoundTouchClient(clientConfig)
|
||||
if err != nil {
|
||||
PrintError(fmt.Sprintf("Failed to create client: %v", err))
|
||||
return err
|
||||
}
|
||||
|
||||
err = client.SetBassSafe(level)
|
||||
if err != nil {
|
||||
PrintError(fmt.Sprintf("Failed to set bass: %v", err))
|
||||
return err
|
||||
}
|
||||
|
||||
PrintSuccess(fmt.Sprintf("Bass level set to %d", level))
|
||||
return nil
|
||||
}
|
||||
|
||||
// bassUp increases the bass level
|
||||
func bassUp(c *cli.Context) error {
|
||||
amount := c.Int("amount")
|
||||
clientConfig := GetClientConfig(c)
|
||||
PrintDeviceHeader(fmt.Sprintf("Increasing bass by %d", amount), clientConfig.Host, clientConfig.Port)
|
||||
|
||||
client, err := CreateSoundTouchClient(clientConfig)
|
||||
if err != nil {
|
||||
PrintError(fmt.Sprintf("Failed to create client: %v", err))
|
||||
return err
|
||||
}
|
||||
|
||||
// Get current bass level first
|
||||
currentBass, err := client.GetBass()
|
||||
if err != nil {
|
||||
PrintError(fmt.Sprintf("Failed to get current bass: %v", err))
|
||||
return err
|
||||
}
|
||||
|
||||
newLevel := currentBass.ActualBass + amount
|
||||
err = client.SetBassSafe(newLevel)
|
||||
if err != nil {
|
||||
PrintError(fmt.Sprintf("Failed to increase bass: %v", err))
|
||||
return err
|
||||
}
|
||||
|
||||
PrintSuccess(fmt.Sprintf("Bass increased from %d to %d", currentBass.ActualBass, newLevel))
|
||||
return nil
|
||||
}
|
||||
|
||||
// bassDown decreases the bass level
|
||||
func bassDown(c *cli.Context) error {
|
||||
amount := c.Int("amount")
|
||||
clientConfig := GetClientConfig(c)
|
||||
PrintDeviceHeader(fmt.Sprintf("Decreasing bass by %d", amount), clientConfig.Host, clientConfig.Port)
|
||||
|
||||
client, err := CreateSoundTouchClient(clientConfig)
|
||||
if err != nil {
|
||||
PrintError(fmt.Sprintf("Failed to create client: %v", err))
|
||||
return err
|
||||
}
|
||||
|
||||
// Get current bass level first
|
||||
currentBass, err := client.GetBass()
|
||||
if err != nil {
|
||||
PrintError(fmt.Sprintf("Failed to get current bass: %v", err))
|
||||
return err
|
||||
}
|
||||
|
||||
newLevel := currentBass.ActualBass - amount
|
||||
err = client.SetBassSafe(newLevel)
|
||||
if err != nil {
|
||||
PrintError(fmt.Sprintf("Failed to decrease bass: %v", err))
|
||||
return err
|
||||
}
|
||||
|
||||
PrintSuccess(fmt.Sprintf("Bass decreased from %d to %d", currentBass.ActualBass, newLevel))
|
||||
return nil
|
||||
}
|
||||
|
||||
// getBassCapabilities retrieves the bass capabilities of the device
|
||||
func getBassCapabilities(c *cli.Context) error {
|
||||
clientConfig := GetClientConfig(c)
|
||||
PrintDeviceHeader("Getting bass capabilities", clientConfig.Host, clientConfig.Port)
|
||||
|
||||
client, err := CreateSoundTouchClient(clientConfig)
|
||||
if err != nil {
|
||||
PrintError(fmt.Sprintf("Failed to create client: %v", err))
|
||||
return err
|
||||
}
|
||||
|
||||
capabilities, err := client.GetBassCapabilities()
|
||||
if err != nil {
|
||||
PrintError(fmt.Sprintf("Failed to get bass capabilities: %v", err))
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Println("Bass Capabilities:")
|
||||
fmt.Printf(" Available: %t\n", capabilities.BassAvailable)
|
||||
|
||||
if capabilities.BassAvailable {
|
||||
fmt.Printf(" Range: %d to %d\n", capabilities.BassMin, capabilities.BassMax)
|
||||
fmt.Printf(" Default: %d\n", capabilities.BassDefault)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,289 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/user_account/bose-soundtouch/pkg/models"
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
// getClockTime retrieves the current clock time from the device
|
||||
func getClockTime(c *cli.Context) error {
|
||||
clientConfig := GetClientConfig(c)
|
||||
PrintDeviceHeader("Getting clock time", clientConfig.Host, clientConfig.Port)
|
||||
|
||||
client, err := CreateSoundTouchClient(clientConfig)
|
||||
if err != nil {
|
||||
PrintError(fmt.Sprintf("Failed to create client: %v", err))
|
||||
return err
|
||||
}
|
||||
|
||||
clockTime, err := client.GetClockTime()
|
||||
if err != nil {
|
||||
PrintError(fmt.Sprintf("Failed to get clock time: %v", err))
|
||||
return err
|
||||
}
|
||||
|
||||
if timeObj, err := clockTime.GetTime(); err == nil {
|
||||
fmt.Printf("Current time: %02d:%02d\n", timeObj.Hour(), timeObj.Minute())
|
||||
fmt.Printf("UTC time: %s\n", timeObj.Format("2006-01-02 15:04:05 MST"))
|
||||
} else {
|
||||
fmt.Printf("Time value: %s\n", clockTime.Value)
|
||||
}
|
||||
|
||||
if clockTime.GetUTC() > 0 {
|
||||
utcTime := time.Unix(clockTime.GetUTC(), 0)
|
||||
fmt.Printf("UTC timestamp: %d (%s)\n", clockTime.GetUTC(), utcTime.Format("2006-01-02 15:04:05 MST"))
|
||||
}
|
||||
|
||||
if clockTime.GetZone() != "" {
|
||||
fmt.Printf("Time zone: %s\n", clockTime.GetZone())
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// setClockTime sets the clock time on the device
|
||||
func setClockTime(c *cli.Context) error {
|
||||
timeStr := c.String("time")
|
||||
clientConfig := GetClientConfig(c)
|
||||
|
||||
// Parse time string (HH:MM format)
|
||||
var hour, minute int
|
||||
var err error
|
||||
|
||||
if timeStr == "now" {
|
||||
now := time.Now()
|
||||
hour = now.Hour()
|
||||
minute = now.Minute()
|
||||
PrintDeviceHeader(fmt.Sprintf("Setting clock time to current time (%02d:%02d)", hour, minute), clientConfig.Host, clientConfig.Port)
|
||||
} else {
|
||||
hour, minute, err = parseTimeString(timeStr)
|
||||
if err != nil {
|
||||
PrintError(fmt.Sprintf("Invalid time format. Use HH:MM or 'now': %v", err))
|
||||
return err
|
||||
}
|
||||
PrintDeviceHeader(fmt.Sprintf("Setting clock time to %02d:%02d", hour, minute), clientConfig.Host, clientConfig.Port)
|
||||
}
|
||||
|
||||
client, err := CreateSoundTouchClient(clientConfig)
|
||||
if err != nil {
|
||||
PrintError(fmt.Sprintf("Failed to create client: %v", err))
|
||||
return err
|
||||
}
|
||||
|
||||
// Create a time object for today with the specified hour and minute
|
||||
now := time.Now()
|
||||
targetTime := time.Date(now.Year(), now.Month(), now.Day(), hour, minute, 0, 0, now.Location())
|
||||
|
||||
clockTimeRequest := models.NewClockTimeRequest(targetTime)
|
||||
err = client.SetClockTime(clockTimeRequest)
|
||||
if err != nil {
|
||||
PrintError(fmt.Sprintf("Failed to set clock time: %v", err))
|
||||
return err
|
||||
}
|
||||
|
||||
PrintSuccess(fmt.Sprintf("Clock time set to %02d:%02d", hour, minute))
|
||||
return nil
|
||||
}
|
||||
|
||||
// setClockTimeNow sets the clock time to the current system time
|
||||
func setClockTimeNow(c *cli.Context) error {
|
||||
clientConfig := GetClientConfig(c)
|
||||
PrintDeviceHeader("Setting clock time to current system time", clientConfig.Host, clientConfig.Port)
|
||||
|
||||
client, err := CreateSoundTouchClient(clientConfig)
|
||||
if err != nil {
|
||||
PrintError(fmt.Sprintf("Failed to create client: %v", err))
|
||||
return err
|
||||
}
|
||||
|
||||
err = client.SetClockTimeNow()
|
||||
if err != nil {
|
||||
PrintError(fmt.Sprintf("Failed to set clock time: %v", err))
|
||||
return err
|
||||
}
|
||||
|
||||
now := time.Now()
|
||||
PrintSuccess(fmt.Sprintf("Clock time set to current time (%02d:%02d)", now.Hour(), now.Minute()))
|
||||
return nil
|
||||
}
|
||||
|
||||
// getClockDisplay retrieves the current clock display settings
|
||||
func getClockDisplay(c *cli.Context) error {
|
||||
clientConfig := GetClientConfig(c)
|
||||
PrintDeviceHeader("Getting clock display settings", clientConfig.Host, clientConfig.Port)
|
||||
|
||||
client, err := CreateSoundTouchClient(clientConfig)
|
||||
if err != nil {
|
||||
PrintError(fmt.Sprintf("Failed to create client: %v", err))
|
||||
return err
|
||||
}
|
||||
|
||||
clockDisplay, err := client.GetClockDisplay()
|
||||
if err != nil {
|
||||
PrintError(fmt.Sprintf("Failed to get clock display: %v", err))
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Println("Clock Display Settings:")
|
||||
fmt.Printf(" Enabled: %t\n", clockDisplay.IsEnabled())
|
||||
fmt.Printf(" Brightness: %d (%s)\n", clockDisplay.GetBrightness(), clockDisplay.GetBrightnessLevel())
|
||||
fmt.Printf(" Format: %s (%s)\n", clockDisplay.GetFormat(), clockDisplay.GetFormatDescription())
|
||||
|
||||
if clockDisplay.IsAutoDimEnabled() {
|
||||
fmt.Printf(" Auto-dim: enabled\n")
|
||||
}
|
||||
|
||||
if clockDisplay.GetTimeZone() != "" {
|
||||
fmt.Printf(" Time zone: %s\n", clockDisplay.GetTimeZone())
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// enableClockDisplay enables the clock display
|
||||
func enableClockDisplay(c *cli.Context) error {
|
||||
clientConfig := GetClientConfig(c)
|
||||
PrintDeviceHeader("Enabling clock display", clientConfig.Host, clientConfig.Port)
|
||||
|
||||
client, err := CreateSoundTouchClient(clientConfig)
|
||||
if err != nil {
|
||||
PrintError(fmt.Sprintf("Failed to create client: %v", err))
|
||||
return err
|
||||
}
|
||||
|
||||
err = client.EnableClockDisplay()
|
||||
if err != nil {
|
||||
PrintError(fmt.Sprintf("Failed to enable clock display: %v", err))
|
||||
return err
|
||||
}
|
||||
|
||||
PrintSuccess("Clock display enabled")
|
||||
return nil
|
||||
}
|
||||
|
||||
// disableClockDisplay disables the clock display
|
||||
func disableClockDisplay(c *cli.Context) error {
|
||||
clientConfig := GetClientConfig(c)
|
||||
PrintDeviceHeader("Disabling clock display", clientConfig.Host, clientConfig.Port)
|
||||
|
||||
client, err := CreateSoundTouchClient(clientConfig)
|
||||
if err != nil {
|
||||
PrintError(fmt.Sprintf("Failed to create client: %v", err))
|
||||
return err
|
||||
}
|
||||
|
||||
err = client.DisableClockDisplay()
|
||||
if err != nil {
|
||||
PrintError(fmt.Sprintf("Failed to disable clock display: %v", err))
|
||||
return err
|
||||
}
|
||||
|
||||
PrintSuccess("Clock display disabled")
|
||||
return nil
|
||||
}
|
||||
|
||||
// setClockDisplayBrightness sets the clock display brightness
|
||||
func setClockDisplayBrightness(c *cli.Context) error {
|
||||
brightness := c.String("brightness")
|
||||
clientConfig := GetClientConfig(c)
|
||||
PrintDeviceHeader(fmt.Sprintf("Setting clock display brightness to %s", brightness), clientConfig.Host, clientConfig.Port)
|
||||
|
||||
client, err := CreateSoundTouchClient(clientConfig)
|
||||
if err != nil {
|
||||
PrintError(fmt.Sprintf("Failed to create client: %v", err))
|
||||
return err
|
||||
}
|
||||
|
||||
// Convert brightness string to numeric value
|
||||
var brightnessLevel int
|
||||
switch brightness {
|
||||
case "low", "LOW":
|
||||
brightnessLevel = 25
|
||||
case "medium", "MEDIUM", "med":
|
||||
brightnessLevel = 50
|
||||
case "high", "HIGH":
|
||||
brightnessLevel = 100
|
||||
case "off", "OFF":
|
||||
brightnessLevel = 0
|
||||
default:
|
||||
PrintError("Invalid brightness. Use: low, medium, high, or off")
|
||||
return fmt.Errorf("invalid brightness value")
|
||||
}
|
||||
|
||||
err = client.SetClockDisplayBrightness(brightnessLevel)
|
||||
if err != nil {
|
||||
PrintError(fmt.Sprintf("Failed to set clock display brightness: %v", err))
|
||||
return err
|
||||
}
|
||||
|
||||
PrintSuccess(fmt.Sprintf("Clock display brightness set to %s", brightness))
|
||||
return nil
|
||||
}
|
||||
|
||||
// setClockDisplayFormat sets the clock display format
|
||||
func setClockDisplayFormat(c *cli.Context) error {
|
||||
format := c.String("format")
|
||||
clientConfig := GetClientConfig(c)
|
||||
PrintDeviceHeader(fmt.Sprintf("Setting clock display format to %s", format), clientConfig.Host, clientConfig.Port)
|
||||
|
||||
client, err := CreateSoundTouchClient(clientConfig)
|
||||
if err != nil {
|
||||
PrintError(fmt.Sprintf("Failed to create client: %v", err))
|
||||
return err
|
||||
}
|
||||
|
||||
// Validate and normalize format value
|
||||
var formatSetting models.ClockFormat
|
||||
switch format {
|
||||
case "12", "12h", "12hour":
|
||||
formatSetting = models.ClockFormat12Hour
|
||||
case "24", "24h", "24hour":
|
||||
formatSetting = models.ClockFormat24Hour
|
||||
default:
|
||||
PrintError("Invalid format. Use: 12 (12-hour) or 24 (24-hour)")
|
||||
return fmt.Errorf("invalid format value")
|
||||
}
|
||||
|
||||
err = client.SetClockDisplayFormat(formatSetting)
|
||||
if err != nil {
|
||||
PrintError(fmt.Sprintf("Failed to set clock display format: %v", err))
|
||||
return err
|
||||
}
|
||||
|
||||
PrintSuccess(fmt.Sprintf("Clock display format set to %s", format))
|
||||
return nil
|
||||
}
|
||||
|
||||
// parseTimeString parses a time string in HH:MM format
|
||||
func parseTimeString(timeStr string) (int, int, error) {
|
||||
if len(timeStr) != 5 || timeStr[2] != ':' {
|
||||
return 0, 0, fmt.Errorf("time must be in HH:MM format")
|
||||
}
|
||||
|
||||
hourStr := timeStr[0:2]
|
||||
minuteStr := timeStr[3:5]
|
||||
|
||||
hour, err := strconv.Atoi(hourStr)
|
||||
if err != nil {
|
||||
return 0, 0, fmt.Errorf("invalid hour: %s", hourStr)
|
||||
}
|
||||
|
||||
minute, err := strconv.Atoi(minuteStr)
|
||||
if err != nil {
|
||||
return 0, 0, fmt.Errorf("invalid minute: %s", minuteStr)
|
||||
}
|
||||
|
||||
if hour < 0 || hour > 23 {
|
||||
return 0, 0, fmt.Errorf("hour must be between 0 and 23")
|
||||
}
|
||||
|
||||
if minute < 0 || minute > 59 {
|
||||
return 0, 0, fmt.Errorf("minute must be between 0 and 59")
|
||||
}
|
||||
|
||||
return hour, minute, nil
|
||||
}
|
||||
@@ -0,0 +1,122 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
// getNetworkInfo retrieves network information from the device
|
||||
func getNetworkInfo(c *cli.Context) error {
|
||||
clientConfig := GetClientConfig(c)
|
||||
PrintDeviceHeader("Getting network information", clientConfig.Host, clientConfig.Port)
|
||||
|
||||
client, err := CreateSoundTouchClient(clientConfig)
|
||||
if err != nil {
|
||||
PrintError(fmt.Sprintf("Failed to create client: %v", err))
|
||||
return err
|
||||
}
|
||||
|
||||
networkInfo, err := client.GetNetworkInfo()
|
||||
if err != nil {
|
||||
PrintError(fmt.Sprintf("Failed to get network info: %v", err))
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Println("Network Information:")
|
||||
|
||||
if networkInfo.GetWifiProfileCount() > 0 {
|
||||
fmt.Printf(" WiFi Profiles: %d\n", networkInfo.GetWifiProfileCount())
|
||||
}
|
||||
|
||||
interfaces := networkInfo.GetInterfaces()
|
||||
if len(interfaces) == 0 {
|
||||
fmt.Println(" No network interfaces found")
|
||||
return nil
|
||||
}
|
||||
|
||||
fmt.Printf(" Interfaces (%d):\n", len(interfaces))
|
||||
for i, iface := range interfaces {
|
||||
fmt.Printf("\n Interface %d:\n", i+1)
|
||||
fmt.Printf(" Type: %s\n", iface.GetType())
|
||||
|
||||
if iface.GetName() != "" {
|
||||
fmt.Printf(" Name: %s\n", iface.GetName())
|
||||
}
|
||||
|
||||
if iface.GetIPAddress() != "" {
|
||||
fmt.Printf(" IP Address: %s\n", iface.GetIPAddress())
|
||||
}
|
||||
|
||||
if iface.GetMacAddress() != "" {
|
||||
fmt.Printf(" MAC Address: %s\n", iface.GetMacAddress())
|
||||
}
|
||||
|
||||
fmt.Printf(" State: %s\n", iface.GetStateDescription())
|
||||
|
||||
if iface.IsWiFi() {
|
||||
if iface.GetSSID() != "" {
|
||||
fmt.Printf(" SSID: %s\n", iface.GetSSID())
|
||||
}
|
||||
|
||||
if iface.GetSignal() != "" {
|
||||
fmt.Printf(" Signal: %s (%d%%)\n", iface.GetSignalDescription(), iface.GetSignalQuality())
|
||||
}
|
||||
|
||||
if iface.GetFrequencyKHz() > 0 {
|
||||
fmt.Printf(" Frequency: %s (%s)\n", iface.FormatFrequency(), iface.GetFrequencyBand())
|
||||
}
|
||||
|
||||
if iface.GetMode() != "" {
|
||||
fmt.Printf(" Mode: %s\n", iface.GetModeDescription())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Show active connections summary
|
||||
activeInterfaces := networkInfo.GetActiveInterfaces()
|
||||
if len(activeInterfaces) > 0 {
|
||||
fmt.Println("\n Active Connections:")
|
||||
for _, iface := range activeInterfaces {
|
||||
fmt.Printf(" - %s: %s\n", iface.GetType(), iface.GetNetworkSummary())
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// pingDevice pings the device to test connectivity
|
||||
func pingDevice(c *cli.Context) error {
|
||||
clientConfig := GetClientConfig(c)
|
||||
PrintDeviceHeader("Pinging device", clientConfig.Host, clientConfig.Port)
|
||||
|
||||
client, err := CreateSoundTouchClient(clientConfig)
|
||||
if err != nil {
|
||||
PrintError(fmt.Sprintf("Failed to create client: %v", err))
|
||||
return err
|
||||
}
|
||||
|
||||
err = client.Ping()
|
||||
if err != nil {
|
||||
PrintError(fmt.Sprintf("Ping failed: %v", err))
|
||||
return err
|
||||
}
|
||||
|
||||
PrintSuccess("Device is reachable")
|
||||
return nil
|
||||
}
|
||||
|
||||
// getDeviceURL displays the device's base URL
|
||||
func getDeviceURL(c *cli.Context) error {
|
||||
clientConfig := GetClientConfig(c)
|
||||
|
||||
client, err := CreateSoundTouchClient(clientConfig)
|
||||
if err != nil {
|
||||
PrintError(fmt.Sprintf("Failed to create client: %v", err))
|
||||
return err
|
||||
}
|
||||
|
||||
baseURL := client.BaseURL()
|
||||
fmt.Printf("Device URL: %s\n", baseURL)
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,311 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"strings"
|
||||
|
||||
"github.com/user_account/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
|
||||
}
|
||||
@@ -15,7 +15,6 @@ import (
|
||||
var CommonFlags = []cli.Flag{
|
||||
&cli.StringFlag{
|
||||
Name: "host",
|
||||
Aliases: []string{"h"},
|
||||
Usage: "SoundTouch device host/IP address (can include port like host:8090)",
|
||||
EnvVars: []string{"SOUNDTOUCH_HOST"},
|
||||
},
|
||||
|
||||
+326
-20
@@ -21,13 +21,7 @@ func main() {
|
||||
Email: "info@example.com",
|
||||
},
|
||||
},
|
||||
Flags: []cli.Flag{
|
||||
&cli.BoolFlag{
|
||||
Name: "verbose",
|
||||
Aliases: []string{"v"},
|
||||
Usage: "Enable verbose output",
|
||||
},
|
||||
},
|
||||
Flags: []cli.Flag{},
|
||||
Commands: []*cli.Command{
|
||||
// Discovery commands
|
||||
{
|
||||
@@ -39,19 +33,11 @@ func main() {
|
||||
Name: "devices",
|
||||
Usage: "Discover and list SoundTouch devices",
|
||||
Action: discoverDevices,
|
||||
Flags: []cli.Flag{
|
||||
&cli.BoolFlag{
|
||||
Name: "all",
|
||||
Aliases: []string{"a"},
|
||||
Usage: "Show detailed information for all devices",
|
||||
},
|
||||
&cli.DurationFlag{
|
||||
Name: "timeout",
|
||||
Aliases: []string{"t"},
|
||||
Usage: "Discovery timeout",
|
||||
Value: 10000000000, // 10 seconds in nanoseconds
|
||||
},
|
||||
},
|
||||
Flags: append(CommonFlags, &cli.BoolFlag{
|
||||
Name: "all",
|
||||
Aliases: []string{"a"},
|
||||
Usage: "Show detailed information for all devices",
|
||||
}),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -261,6 +247,326 @@ func main() {
|
||||
},
|
||||
},
|
||||
},
|
||||
// Bass commands
|
||||
{
|
||||
Name: "bass",
|
||||
Aliases: []string{"b"},
|
||||
Usage: "Bass control commands",
|
||||
Subcommands: []*cli.Command{
|
||||
{
|
||||
Name: "get",
|
||||
Usage: "Get current bass level",
|
||||
Action: getBass,
|
||||
Flags: CommonFlags,
|
||||
Before: RequireHost,
|
||||
},
|
||||
{
|
||||
Name: "set",
|
||||
Usage: "Set bass level",
|
||||
Action: setBass,
|
||||
Flags: append(CommonFlags, &cli.IntFlag{
|
||||
Name: "level",
|
||||
Aliases: []string{"l"},
|
||||
Usage: "Bass level (-9 to 9)",
|
||||
Required: true,
|
||||
}),
|
||||
Before: RequireHost,
|
||||
},
|
||||
{
|
||||
Name: "up",
|
||||
Usage: "Increase bass",
|
||||
Action: bassUp,
|
||||
Flags: append(CommonFlags, &cli.IntFlag{
|
||||
Name: "amount",
|
||||
Aliases: []string{"a"},
|
||||
Usage: "Amount to increase (1-5)",
|
||||
Value: 1,
|
||||
}),
|
||||
Before: RequireHost,
|
||||
},
|
||||
{
|
||||
Name: "down",
|
||||
Usage: "Decrease bass",
|
||||
Action: bassDown,
|
||||
Flags: append(CommonFlags, &cli.IntFlag{
|
||||
Name: "amount",
|
||||
Aliases: []string{"a"},
|
||||
Usage: "Amount to decrease (1-5)",
|
||||
Value: 1,
|
||||
}),
|
||||
Before: RequireHost,
|
||||
},
|
||||
{
|
||||
Name: "capabilities",
|
||||
Usage: "Get bass capabilities",
|
||||
Action: getBassCapabilities,
|
||||
Flags: CommonFlags,
|
||||
Before: RequireHost,
|
||||
},
|
||||
},
|
||||
},
|
||||
// Balance commands
|
||||
{
|
||||
Name: "balance",
|
||||
Aliases: []string{"bal"},
|
||||
Usage: "Balance control commands",
|
||||
Subcommands: []*cli.Command{
|
||||
{
|
||||
Name: "get",
|
||||
Usage: "Get current balance level",
|
||||
Action: getBalance,
|
||||
Flags: CommonFlags,
|
||||
Before: RequireHost,
|
||||
},
|
||||
{
|
||||
Name: "set",
|
||||
Usage: "Set balance level",
|
||||
Action: setBalance,
|
||||
Flags: append(CommonFlags, &cli.IntFlag{
|
||||
Name: "level",
|
||||
Aliases: []string{"l"},
|
||||
Usage: "Balance level (-50 to 50, negative=left, positive=right)",
|
||||
Required: true,
|
||||
}),
|
||||
Before: RequireHost,
|
||||
},
|
||||
{
|
||||
Name: "left",
|
||||
Usage: "Shift balance to the left",
|
||||
Action: balanceLeft,
|
||||
Flags: append(CommonFlags, &cli.IntFlag{
|
||||
Name: "amount",
|
||||
Aliases: []string{"a"},
|
||||
Usage: "Amount to shift left (1-5)",
|
||||
Value: 1,
|
||||
}),
|
||||
Before: RequireHost,
|
||||
},
|
||||
{
|
||||
Name: "right",
|
||||
Usage: "Shift balance to the right",
|
||||
Action: balanceRight,
|
||||
Flags: append(CommonFlags, &cli.IntFlag{
|
||||
Name: "amount",
|
||||
Aliases: []string{"a"},
|
||||
Usage: "Amount to shift right (1-5)",
|
||||
Value: 1,
|
||||
}),
|
||||
Before: RequireHost,
|
||||
},
|
||||
{
|
||||
Name: "center",
|
||||
Usage: "Center the balance",
|
||||
Action: balanceCenter,
|
||||
Flags: CommonFlags,
|
||||
Before: RequireHost,
|
||||
},
|
||||
},
|
||||
},
|
||||
// Clock commands
|
||||
{
|
||||
Name: "clock",
|
||||
Aliases: []string{"time"},
|
||||
Usage: "Clock and time commands",
|
||||
Subcommands: []*cli.Command{
|
||||
{
|
||||
Name: "get",
|
||||
Usage: "Get current time",
|
||||
Action: getClockTime,
|
||||
Flags: CommonFlags,
|
||||
Before: RequireHost,
|
||||
},
|
||||
{
|
||||
Name: "set",
|
||||
Usage: "Set clock time",
|
||||
Action: setClockTime,
|
||||
Flags: append(CommonFlags, &cli.StringFlag{
|
||||
Name: "time",
|
||||
Aliases: []string{"t"},
|
||||
Usage: "Time in HH:MM format or 'now' for current time",
|
||||
Required: true,
|
||||
}),
|
||||
Before: RequireHost,
|
||||
},
|
||||
{
|
||||
Name: "now",
|
||||
Usage: "Set clock to current system time",
|
||||
Action: setClockTimeNow,
|
||||
Flags: CommonFlags,
|
||||
Before: RequireHost,
|
||||
},
|
||||
{
|
||||
Name: "display",
|
||||
Usage: "Clock display commands",
|
||||
Subcommands: []*cli.Command{
|
||||
{
|
||||
Name: "get",
|
||||
Usage: "Get display settings",
|
||||
Action: getClockDisplay,
|
||||
Flags: CommonFlags,
|
||||
Before: RequireHost,
|
||||
},
|
||||
{
|
||||
Name: "enable",
|
||||
Usage: "Enable clock display",
|
||||
Action: enableClockDisplay,
|
||||
Flags: CommonFlags,
|
||||
Before: RequireHost,
|
||||
},
|
||||
{
|
||||
Name: "disable",
|
||||
Usage: "Disable clock display",
|
||||
Action: disableClockDisplay,
|
||||
Flags: CommonFlags,
|
||||
Before: RequireHost,
|
||||
},
|
||||
{
|
||||
Name: "brightness",
|
||||
Usage: "Set display brightness",
|
||||
Action: setClockDisplayBrightness,
|
||||
Flags: append(CommonFlags, &cli.StringFlag{
|
||||
Name: "brightness",
|
||||
Aliases: []string{"b"},
|
||||
Usage: "Brightness level (low, medium, high, off)",
|
||||
Required: true,
|
||||
}),
|
||||
Before: RequireHost,
|
||||
},
|
||||
{
|
||||
Name: "format",
|
||||
Usage: "Set display format",
|
||||
Action: setClockDisplayFormat,
|
||||
Flags: append(CommonFlags, &cli.StringFlag{
|
||||
Name: "format",
|
||||
Aliases: []string{"f"},
|
||||
Usage: "Time format (12 or 24)",
|
||||
Required: true,
|
||||
}),
|
||||
Before: RequireHost,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
// Network commands
|
||||
{
|
||||
Name: "network",
|
||||
Aliases: []string{"net"},
|
||||
Usage: "Network information commands",
|
||||
Subcommands: []*cli.Command{
|
||||
{
|
||||
Name: "info",
|
||||
Usage: "Get network information",
|
||||
Action: getNetworkInfo,
|
||||
Flags: CommonFlags,
|
||||
Before: RequireHost,
|
||||
},
|
||||
{
|
||||
Name: "ping",
|
||||
Usage: "Ping the device",
|
||||
Action: pingDevice,
|
||||
Flags: CommonFlags,
|
||||
Before: RequireHost,
|
||||
},
|
||||
{
|
||||
Name: "url",
|
||||
Usage: "Get device base URL",
|
||||
Action: getDeviceURL,
|
||||
Flags: CommonFlags,
|
||||
Before: RequireHost,
|
||||
},
|
||||
},
|
||||
},
|
||||
// Zone commands
|
||||
{
|
||||
Name: "zone",
|
||||
Aliases: []string{"z"},
|
||||
Usage: "Multi-room zone management commands",
|
||||
Subcommands: []*cli.Command{
|
||||
{
|
||||
Name: "get",
|
||||
Usage: "Get current zone configuration",
|
||||
Action: getZone,
|
||||
Flags: CommonFlags,
|
||||
Before: RequireHost,
|
||||
},
|
||||
{
|
||||
Name: "status",
|
||||
Usage: "Get zone status",
|
||||
Action: getZoneStatus,
|
||||
Flags: CommonFlags,
|
||||
Before: RequireHost,
|
||||
},
|
||||
{
|
||||
Name: "members",
|
||||
Usage: "List zone members",
|
||||
Action: getZoneMembers,
|
||||
Flags: CommonFlags,
|
||||
Before: RequireHost,
|
||||
},
|
||||
{
|
||||
Name: "create",
|
||||
Usage: "Create a new zone",
|
||||
Action: createZone,
|
||||
Flags: append(CommonFlags, &cli.StringSliceFlag{
|
||||
Name: "members",
|
||||
Aliases: []string{"m"},
|
||||
Usage: "Member IP addresses",
|
||||
Required: true,
|
||||
}),
|
||||
Before: RequireHost,
|
||||
},
|
||||
{
|
||||
Name: "add",
|
||||
Usage: "Add device to zone",
|
||||
Action: addToZone,
|
||||
Flags: append(CommonFlags, &cli.StringFlag{
|
||||
Name: "member",
|
||||
Aliases: []string{"m"},
|
||||
Usage: "Member IP address to add",
|
||||
Required: true,
|
||||
}),
|
||||
Before: RequireHost,
|
||||
},
|
||||
{
|
||||
Name: "remove",
|
||||
Usage: "Remove device from zone",
|
||||
Action: removeFromZone,
|
||||
Flags: append(CommonFlags, &cli.StringFlag{
|
||||
Name: "member",
|
||||
Aliases: []string{"m"},
|
||||
Usage: "Member IP address to remove",
|
||||
Required: true,
|
||||
}),
|
||||
Before: RequireHost,
|
||||
},
|
||||
{
|
||||
Name: "dissolve",
|
||||
Usage: "Dissolve the current zone",
|
||||
Action: dissolveZone,
|
||||
Flags: CommonFlags,
|
||||
Before: RequireHost,
|
||||
},
|
||||
{
|
||||
Name: "set",
|
||||
Usage: "Set zone configuration",
|
||||
Action: setZoneConfig,
|
||||
Flags: append(CommonFlags,
|
||||
&cli.StringFlag{
|
||||
Name: "master",
|
||||
Usage: "Master device IP address",
|
||||
Required: true,
|
||||
},
|
||||
&cli.StringSliceFlag{
|
||||
Name: "members",
|
||||
Aliases: []string{"m"},
|
||||
Usage: "Member IP addresses",
|
||||
},
|
||||
),
|
||||
Before: RequireHost,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user