diff --git a/cmd/soundtouch-cli/cmd_clock.go b/cmd/soundtouch-cli/cmd_clock.go index 426bf54..0a62eb5 100644 --- a/cmd/soundtouch-cli/cmd_clock.go +++ b/cmd/soundtouch-cli/cmd_clock.go @@ -1,3 +1,4 @@ +// Package main provides the soundtouch-cli clock control commands. package main import ( @@ -60,12 +61,21 @@ func setClockTime(c *cli.Context) error { 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 + // Try to parse as Unix timestamp first + if timestamp, parseErr := strconv.ParseInt(timeStr, 10, 64); parseErr == nil { + targetTime := time.Unix(timestamp, 0) + hour = targetTime.Hour() + minute = targetTime.Minute() + PrintDeviceHeader(fmt.Sprintf("Setting clock time from Unix timestamp %d (%02d:%02d)", timestamp, hour, minute), clientConfig.Host, clientConfig.Port) + } else { + // Parse as HH:MM format + hour, minute, err = parseTimeString(timeStr) + if err != nil { + PrintError(fmt.Sprintf("Invalid time format. Use HH:MM, Unix timestamp, or 'now': %v", err)) + return err + } + PrintDeviceHeader(fmt.Sprintf("Setting clock time to %02d:%02d", hour, minute), clientConfig.Host, clientConfig.Port) } - PrintDeviceHeader(fmt.Sprintf("Setting clock time to %02d:%02d", hour, minute), clientConfig.Host, clientConfig.Port) } client, err := CreateSoundTouchClient(clientConfig) @@ -243,8 +253,10 @@ func setClockDisplayFormat(c *cli.Context) error { formatSetting = models.ClockFormat12Hour case "24", "24h", "24hour": formatSetting = models.ClockFormat24Hour + case "auto": + formatSetting = models.ClockFormatAuto default: - PrintError("Invalid format. Use: 12 (12-hour) or 24 (24-hour)") + PrintError("Invalid format. Use: 12 (12-hour), 24 (24-hour), or auto") return fmt.Errorf("invalid format value") } diff --git a/cmd/soundtouch-cli/cmd_info.go b/cmd/soundtouch-cli/cmd_info.go index 287371b..846e390 100644 --- a/cmd/soundtouch-cli/cmd_info.go +++ b/cmd/soundtouch-cli/cmd_info.go @@ -2,6 +2,7 @@ package main import ( "fmt" + "time" "github.com/urfave/cli/v2" ) @@ -183,9 +184,75 @@ func getPresets(c *cli.Context) error { // Show preset creation time if available if preset.CreatedOn != nil && *preset.CreatedOn != 0 { - fmt.Printf(" Created: Unix timestamp %d\n", *preset.CreatedOn) + createdTime := time.Unix(*preset.CreatedOn, 0) + fmt.Printf(" Created: %s\n", createdTime.Format("2006-01-02 15:04:05")) } + + fmt.Println() } return nil } + +// selectPreset selects a preset by number (1-6) +func selectPreset(c *cli.Context) error { + presetNum := c.Int("preset") + clientConfig := GetClientConfig(c) + PrintDeviceHeader(fmt.Sprintf("Selecting preset %d", presetNum), clientConfig.Host, clientConfig.Port) + + client, err := CreateSoundTouchClient(clientConfig) + if err != nil { + PrintError(fmt.Sprintf("Failed to create client: %v", err)) + return err + } + + err = client.SelectPreset(presetNum) + if err != nil { + PrintError(fmt.Sprintf("Failed to select preset: %v", err)) + return err + } + + PrintSuccess(fmt.Sprintf("Preset %d selected", presetNum)) + return nil +} + +// getTrackInfo gets the track information +func getTrackInfo(c *cli.Context) error { + clientConfig := GetClientConfig(c) + PrintDeviceHeader("Getting track information", clientConfig.Host, clientConfig.Port) + + client, err := CreateSoundTouchClient(clientConfig) + if err != nil { + PrintError(fmt.Sprintf("Failed to create client: %v", err)) + return err + } + + trackInfo, err := client.GetTrackInfo() + if err != nil { + PrintError(fmt.Sprintf("Failed to get track info: %v", err)) + return err + } + + fmt.Println("Track Information:") + fmt.Printf(" Source: %s\n", trackInfo.Source) + + if trackInfo.Track != "" { + fmt.Printf(" Track: %s\n", trackInfo.Track) + } + + if trackInfo.Artist != "" { + fmt.Printf(" Artist: %s\n", trackInfo.Artist) + } + + if trackInfo.Album != "" { + fmt.Printf(" Album: %s\n", trackInfo.Album) + } + + if trackInfo.StationName != "" { + fmt.Printf(" Station: %s\n", trackInfo.StationName) + } + + fmt.Printf(" Play Status: %s\n", trackInfo.PlayStatus) + + return nil +} diff --git a/cmd/soundtouch-cli/cmd_playback.go b/cmd/soundtouch-cli/cmd_playback.go index fd2af5f..6ae0e19 100644 --- a/cmd/soundtouch-cli/cmd_playback.go +++ b/cmd/soundtouch-cli/cmd_playback.go @@ -2,6 +2,7 @@ package main import ( "fmt" + "strings" "github.com/user_account/bose-soundtouch/pkg/models" "github.com/urfave/cli/v2" @@ -157,3 +158,151 @@ func prevCommand(c *cli.Context) error { PrintSuccess("Previous track command sent") return nil } + +// sendKey sends a generic key command +func sendKey(c *cli.Context) error { + key := c.String("key") + clientConfig := GetClientConfig(c) + PrintDeviceHeader(fmt.Sprintf("Sending %s key command", key), clientConfig.Host, clientConfig.Port) + + client, err := CreateSoundTouchClient(clientConfig) + if err != nil { + PrintError(fmt.Sprintf("Failed to create client: %v", err)) + return err + } + + err = client.SendKey(strings.ToUpper(key)) + if err != nil { + PrintError(fmt.Sprintf("Failed to send key command: %v", err)) + return err + } + + PrintSuccess(fmt.Sprintf("%s key command sent", key)) + return nil +} + +// powerCommand sends the POWER key command +func powerCommand(c *cli.Context) error { + clientConfig := GetClientConfig(c) + PrintDeviceHeader("Sending POWER key command", clientConfig.Host, clientConfig.Port) + + client, err := CreateSoundTouchClient(clientConfig) + if err != nil { + PrintError(fmt.Sprintf("Failed to create client: %v", err)) + return err + } + + err = client.SendKey(models.KeyPower) + if err != nil { + PrintError(fmt.Sprintf("Failed to send power command: %v", err)) + return err + } + + PrintSuccess("Power command sent") + return nil +} + +// muteCommand sends the MUTE key command +func muteCommand(c *cli.Context) error { + clientConfig := GetClientConfig(c) + PrintDeviceHeader("Sending MUTE key command", clientConfig.Host, clientConfig.Port) + + client, err := CreateSoundTouchClient(clientConfig) + if err != nil { + PrintError(fmt.Sprintf("Failed to create client: %v", err)) + return err + } + + err = client.SendKey(models.KeyMute) + if err != nil { + PrintError(fmt.Sprintf("Failed to send mute command: %v", err)) + return err + } + + PrintSuccess("Mute command sent") + return nil +} + +// thumbsUpCommand sends the THUMBS_UP key command +func thumbsUpCommand(c *cli.Context) error { + clientConfig := GetClientConfig(c) + PrintDeviceHeader("Sending THUMBS_UP key command", clientConfig.Host, clientConfig.Port) + + client, err := CreateSoundTouchClient(clientConfig) + if err != nil { + PrintError(fmt.Sprintf("Failed to create client: %v", err)) + return err + } + + err = client.SendKey(models.KeyThumbsUp) + if err != nil { + PrintError(fmt.Sprintf("Failed to send thumbs up command: %v", err)) + return err + } + + PrintSuccess("Thumbs up command sent") + return nil +} + +// thumbsDownCommand sends the THUMBS_DOWN key command +func thumbsDownCommand(c *cli.Context) error { + clientConfig := GetClientConfig(c) + PrintDeviceHeader("Sending THUMBS_DOWN key command", clientConfig.Host, clientConfig.Port) + + client, err := CreateSoundTouchClient(clientConfig) + if err != nil { + PrintError(fmt.Sprintf("Failed to create client: %v", err)) + return err + } + + err = client.SendKey(models.KeyThumbsDown) + if err != nil { + PrintError(fmt.Sprintf("Failed to send thumbs down command: %v", err)) + return err + } + + PrintSuccess("Thumbs down command sent") + return nil +} + +// volumeUpKey sends the VOLUME_UP key command +func volumeUpKey(c *cli.Context) error { + clientConfig := GetClientConfig(c) + PrintDeviceHeader("Sending VOLUME_UP key command", clientConfig.Host, clientConfig.Port) + + client, err := CreateSoundTouchClient(clientConfig) + if err != nil { + PrintError(fmt.Sprintf("Failed to create client: %v", err)) + return err + } + + err = client.VolumeUp() + if err != nil { + PrintError(fmt.Sprintf("Failed to send volume up command: %v", err)) + return err + } + + PrintSuccess("Volume up command sent") + return nil +} + +// volumeDownKey sends the VOLUME_DOWN key command +func volumeDownKey(c *cli.Context) error { + clientConfig := GetClientConfig(c) + PrintDeviceHeader("Sending VOLUME_DOWN key command", clientConfig.Host, clientConfig.Port) + + client, err := CreateSoundTouchClient(clientConfig) + if err != nil { + PrintError(fmt.Sprintf("Failed to create client: %v", err)) + return err + } + + err = client.VolumeDown() + if err != nil { + PrintError(fmt.Sprintf("Failed to send volume down command: %v", err)) + return err + } + + PrintSuccess("Volume down command sent") + return nil +} diff --git a/cmd/soundtouch-cli/main.go b/cmd/soundtouch-cli/main.go index 3498c47..d16e528 100644 --- a/cmd/soundtouch-cli/main.go +++ b/cmd/soundtouch-cli/main.go @@ -141,6 +141,88 @@ func main() { }, }, }, + // Preset commands + { + Name: "preset", + Usage: "Select preset by number", + Action: selectPreset, + Flags: append(CommonFlags, &cli.IntFlag{ + Name: "preset", + Usage: "Preset number (1-6)", + Required: true, + }), + Before: RequireHost, + }, + // Key commands + { + Name: "key", + Aliases: []string{"k"}, + Usage: "Send key commands", + Subcommands: []*cli.Command{ + { + Name: "send", + Usage: "Send generic key command", + Action: sendKey, + Flags: append(CommonFlags, &cli.StringFlag{ + Name: "key", + Aliases: []string{"k"}, + Usage: "Key name (PLAY, PAUSE, STOP, POWER, MUTE, etc.)", + Required: true, + }), + Before: RequireHost, + }, + { + Name: "power", + Usage: "Send POWER key command", + Action: powerCommand, + Flags: CommonFlags, + Before: RequireHost, + }, + { + Name: "mute", + Usage: "Send MUTE key command", + Action: muteCommand, + Flags: CommonFlags, + Before: RequireHost, + }, + { + Name: "thumbs-up", + Usage: "Send THUMBS_UP key command", + Action: thumbsUpCommand, + Flags: CommonFlags, + Before: RequireHost, + }, + { + Name: "thumbs-down", + Usage: "Send THUMBS_DOWN key command", + Action: thumbsDownCommand, + Flags: CommonFlags, + Before: RequireHost, + }, + { + Name: "volume-up", + Usage: "Send VOLUME_UP key command", + Action: volumeUpKey, + Flags: CommonFlags, + Before: RequireHost, + }, + { + Name: "volume-down", + Usage: "Send VOLUME_DOWN key command", + Action: volumeDownKey, + Flags: CommonFlags, + Before: RequireHost, + }, + }, + }, + // Track info + { + Name: "track", + Usage: "Get track information", + Action: getTrackInfo, + Flags: CommonFlags, + Before: RequireHost, + }, // Volume commands { Name: "volume", @@ -337,8 +419,8 @@ func main() { Flags: append(CommonFlags, &cli.IntFlag{ Name: "amount", Aliases: []string{"a"}, - Usage: "Amount to shift left (1-5)", - Value: 1, + Usage: "Amount to shift left (1-10, default: 5)", + Value: 5, }), Before: RequireHost, }, @@ -349,8 +431,8 @@ func main() { Flags: append(CommonFlags, &cli.IntFlag{ Name: "amount", Aliases: []string{"a"}, - Usage: "Amount to shift right (1-5)", - Value: 1, + Usage: "Amount to shift right (1-10, default: 5)", + Value: 5, }), Before: RequireHost, },