diff --git a/cmd/soundtouch-cli/cmd_audio.go b/cmd/soundtouch-cli/cmd_audio.go
index b5fb4dd..f126fcd 100644
--- a/cmd/soundtouch-cli/cmd_audio.go
+++ b/cmd/soundtouch-cli/cmd_audio.go
@@ -62,9 +62,11 @@ func setAudioDSPControls(c *cli.Context) error {
}
fmt.Println("ā
DSP controls updated successfully")
+
if audioMode != "" {
fmt.Printf(" Audio Mode: %s\n", audioMode)
}
+
if videoSyncDelay != 0 {
fmt.Printf(" Video Sync Delay: %d ms\n", videoSyncDelay)
}
@@ -161,21 +163,24 @@ func setAudioToneControls(c *cli.Context) error {
}
var bass, treble *int
+
var err error
if bassStr != "" {
- bassVal, err := strconv.Atoi(bassStr)
- if err != nil {
+ bassVal, errVal := strconv.Atoi(bassStr)
+ if errVal != nil {
return fmt.Errorf("invalid bass value: %s", bassStr)
}
+
bass = &bassVal
}
if trebleStr != "" {
- trebleVal, err := strconv.Atoi(trebleStr)
- if err != nil {
+ trebleVal, errVal := strconv.Atoi(trebleStr)
+ if errVal != nil {
return fmt.Errorf("invalid treble value: %s", trebleStr)
}
+
treble = &trebleVal
}
@@ -194,9 +199,11 @@ func setAudioToneControls(c *cli.Context) error {
}
fmt.Println("ā
Advanced tone controls updated successfully")
+
if bass != nil {
fmt.Printf(" Bass: %d\n", *bass)
}
+
if treble != nil {
fmt.Printf(" Treble: %d\n", *treble)
}
@@ -295,21 +302,24 @@ func setAudioLevelControls(c *cli.Context) error {
}
var frontCenter, rearSurround *int
+
var err error
if frontCenterStr != "" {
- frontCenterVal, err := strconv.Atoi(frontCenterStr)
- if err != nil {
+ frontCenterVal, errVal := strconv.Atoi(frontCenterStr)
+ if errVal != nil {
return fmt.Errorf("invalid front-center value: %s", frontCenterStr)
}
+
frontCenter = &frontCenterVal
}
if rearSurroundStr != "" {
- rearSurroundVal, err := strconv.Atoi(rearSurroundStr)
- if err != nil {
+ rearSurroundVal, errVal := strconv.Atoi(rearSurroundStr)
+ if errVal != nil {
return fmt.Errorf("invalid rear-surround value: %s", rearSurroundStr)
}
+
rearSurround = &rearSurroundVal
}
@@ -328,9 +338,11 @@ func setAudioLevelControls(c *cli.Context) error {
}
fmt.Println("ā
Speaker level controls updated successfully")
+
if frontCenter != nil {
fmt.Printf(" Front-Center Speaker: %d\n", *frontCenter)
}
+
if rearSurround != nil {
fmt.Printf(" Rear-Surround Speakers: %d\n", *rearSurround)
}
diff --git a/examples/advanced-audio-controls.go b/examples/advanced-audio-controls/main.go
similarity index 58%
rename from examples/advanced-audio-controls.go
rename to examples/advanced-audio-controls/main.go
index 4c0cdb7..7d8eb9c 100644
--- a/examples/advanced-audio-controls.go
+++ b/examples/advanced-audio-controls/main.go
@@ -1,3 +1,4 @@
+// Package main provides an example of using advanced audio controls.
package main
import (
@@ -19,7 +20,37 @@ func main() {
fmt.Println("=================================================")
// Example 1: Check device capabilities first
+ checkCapabilities(soundtouchClient)
+
+ // Example 2: DSP Audio Controls
+ demonstrateDSPControls(soundtouchClient)
+
+ time.Sleep(2 * time.Second)
+
+ // Example 3: Advanced Tone Controls (Bass/Treble)
+ demonstrateToneControls(soundtouchClient)
+
+ time.Sleep(2 * time.Second)
+
+ // Example 4: Speaker Level Controls
+ demonstrateLevelControls(soundtouchClient)
+
+ // Example 5: Compare with basic controls
+ demonstrateBasicControls(soundtouchClient)
+
+ // Example 6: Error handling and validation
+ demonstrateErrorHandling(soundtouchClient)
+
+ // Example 7: CLI command equivalents
+ showCLIEquivalents(deviceIP)
+
+ fmt.Println("\nš Advanced audio controls example completed!")
+ printNotes()
+}
+
+func checkCapabilities(soundtouchClient *client.Client) {
fmt.Println("\n1. Checking device capabilities...")
+
capabilities, err := soundtouchClient.GetCapabilities()
if err != nil {
log.Printf("ā Failed to get capabilities: %v", err)
@@ -27,7 +58,6 @@ func main() {
}
fmt.Printf("š Device: %s\n", capabilities.DeviceID)
- fmt.Printf(" Type: %s\n", capabilities.Type)
// Look for advanced audio capabilities in the response
// (Note: Advanced audio controls are only available on professional/high-end devices)
@@ -35,160 +65,174 @@ func main() {
fmt.Println(" - DSP Controls: Check device response for 'audiodspcontrols'")
fmt.Println(" - Tone Controls: Check device response for 'audioproducttonecontrols'")
fmt.Println(" - Level Controls: Check device response for 'audioproductlevelcontrols'")
+}
- // Example 2: DSP Audio Controls
+func demonstrateDSPControls(soundtouchClient *client.Client) {
fmt.Println("\n2. DSP Audio Controls...")
dspControls, err := soundtouchClient.GetAudioDSPControls()
if err != nil {
log.Printf("ā ļø DSP controls not available on this device: %v", err)
fmt.Println(" This is normal for consumer-grade SoundTouch devices")
- } else {
- fmt.Printf("šļø Current DSP Settings: %s\n", dspControls.String())
- // Try setting a different audio mode
- supportedModes := dspControls.GetSupportedAudioModes()
- if len(supportedModes) > 0 {
- newMode := supportedModes[0]
- if newMode != dspControls.AudioMode && newMode != "" {
- fmt.Printf(" Changing audio mode to: %s\n", newMode)
+ return
+ }
- err = soundtouchClient.SetAudioMode(newMode)
- if err != nil {
- log.Printf("ā Failed to set audio mode: %v", err)
- } else {
- fmt.Printf("ā
Audio mode changed successfully\n")
- }
- }
- }
+ fmt.Printf("šļø Current DSP Settings: %s\n", dspControls.String())
- // Demonstrate video sync delay adjustment
- if dspControls.VideoSyncAudioDelay != 50 {
- fmt.Println(" Setting video sync audio delay to 50ms...")
- err = soundtouchClient.SetVideoSyncAudioDelay(50)
+ // Try setting a different audio mode
+ supportedModes := dspControls.GetSupportedAudioModes()
+ if len(supportedModes) > 0 {
+ newMode := supportedModes[0]
+ if newMode != dspControls.AudioMode && newMode != "" {
+ fmt.Printf(" Changing audio mode to: %s\n", newMode)
+
+ err = soundtouchClient.SetAudioMode(newMode)
if err != nil {
- log.Printf("ā Failed to set video sync delay: %v", err)
+ log.Printf("ā Failed to set audio mode: %v", err)
} else {
- fmt.Printf("ā
Video sync delay adjusted\n")
+ fmt.Printf("ā
Audio mode changed successfully\n")
}
}
-
- // Combined DSP settings update
- fmt.Println(" Updating DSP controls (mode + delay)...")
- err = soundtouchClient.SetAudioDSPControls("NORMAL", 25)
- if err != nil {
- log.Printf("ā Failed to set DSP controls: %v", err)
- } else {
- fmt.Printf("ā
DSP controls updated\n")
- }
}
- time.Sleep(2 * time.Second)
+ // Demonstrate video sync delay adjustment
+ if dspControls.VideoSyncAudioDelay != 50 {
+ fmt.Println(" Setting video sync audio delay to 50ms...")
- // Example 3: Advanced Tone Controls (Bass/Treble)
+ err = soundtouchClient.SetVideoSyncAudioDelay(50)
+ if err != nil {
+ log.Printf("ā Failed to set video sync delay: %v", err)
+ } else {
+ fmt.Printf("ā
Video sync delay adjusted\n")
+ }
+ }
+
+ // Combined DSP settings update
+ fmt.Println(" Updating DSP controls (mode + delay)...")
+
+ err = soundtouchClient.SetAudioDSPControls("NORMAL", 25)
+ if err != nil {
+ log.Printf("ā Failed to set DSP controls: %v", err)
+ } else {
+ fmt.Printf("ā
DSP controls updated\n")
+ }
+}
+
+func demonstrateToneControls(soundtouchClient *client.Client) {
fmt.Println("\n3. Advanced Tone Controls...")
toneControls, err := soundtouchClient.GetAudioProductToneControls()
if err != nil {
log.Printf("ā ļø Advanced tone controls not available on this device: %v", err)
fmt.Println(" Use the basic bass control instead (soundtouch-cli bass)")
- } else {
- fmt.Printf("šļø Current Tone Settings: %s\n", toneControls.String())
- // Adjust bass only
- newBassLevel := 3
- if toneControls.Bass.Value != newBassLevel {
- fmt.Printf(" Setting advanced bass to %d...\n", newBassLevel)
- err = soundtouchClient.SetAdvancedBass(newBassLevel)
- if err != nil {
- log.Printf("ā Failed to set advanced bass: %v", err)
- } else {
- fmt.Printf("ā
Advanced bass adjusted\n")
- }
- }
+ return
+ }
- time.Sleep(1 * time.Second)
+ fmt.Printf("šļø Current Tone Settings: %s\n", toneControls.String())
- // Adjust treble only
- newTrebleLevel := -1
- if toneControls.Treble.Value != newTrebleLevel {
- fmt.Printf(" Setting advanced treble to %d...\n", newTrebleLevel)
- err = soundtouchClient.SetAdvancedTreble(newTrebleLevel)
- if err != nil {
- log.Printf("ā Failed to set advanced treble: %v", err)
- } else {
- fmt.Printf("ā
Advanced treble adjusted\n")
- }
- }
+ // Adjust bass only
+ newBassLevel := 3
+ if toneControls.Bass.Value != newBassLevel {
+ fmt.Printf(" Setting advanced bass to %d...\n", newBassLevel)
- time.Sleep(1 * time.Second)
-
- // Adjust both bass and treble together
- combinedBass := 2
- combinedTreble := 1
- fmt.Printf(" Setting bass to %d and treble to %d together...\n", combinedBass, combinedTreble)
- err = soundtouchClient.SetAudioProductToneControls(&combinedBass, &combinedTreble)
+ err = soundtouchClient.SetAdvancedBass(newBassLevel)
if err != nil {
- log.Printf("ā Failed to set tone controls: %v", err)
+ log.Printf("ā Failed to set advanced bass: %v", err)
} else {
- fmt.Printf("ā
Both tone controls adjusted\n")
+ fmt.Printf("ā
Advanced bass adjusted\n")
}
}
- time.Sleep(2 * time.Second)
+ time.Sleep(1 * time.Second)
- // Example 4: Speaker Level Controls
+ // Adjust treble only
+ newTrebleLevel := -1
+ if toneControls.Treble.Value != newTrebleLevel {
+ fmt.Printf(" Setting advanced treble to %d...\n", newTrebleLevel)
+
+ err = soundtouchClient.SetAdvancedTreble(newTrebleLevel)
+ if err != nil {
+ log.Printf("ā Failed to set advanced treble: %v", err)
+ } else {
+ fmt.Printf("ā
Advanced treble adjusted\n")
+ }
+ }
+
+ time.Sleep(1 * time.Second)
+
+ // Adjust both bass and treble together
+ combinedBass := 2
+ combinedTreble := 1
+ fmt.Printf(" Setting bass to %d and treble to %d together...\n", combinedBass, combinedTreble)
+
+ err = soundtouchClient.SetAudioProductToneControls(&combinedBass, &combinedTreble)
+ if err != nil {
+ log.Printf("ā Failed to set tone controls: %v", err)
+ } else {
+ fmt.Printf("ā
Both tone controls adjusted\n")
+ }
+}
+
+func demonstrateLevelControls(soundtouchClient *client.Client) {
fmt.Println("\n4. Speaker Level Controls...")
levelControls, err := soundtouchClient.GetAudioProductLevelControls()
if err != nil {
log.Printf("ā ļø Speaker level controls not available on this device: %v", err)
fmt.Println(" This feature is only available on surround sound systems")
- } else {
- fmt.Printf("š Current Speaker Levels: %s\n", levelControls.String())
- // Adjust front-center speaker level
- newFrontCenterLevel := 2
- if levelControls.FrontCenterSpeakerLevel.Value != newFrontCenterLevel {
- fmt.Printf(" Setting front-center speaker level to %d...\n", newFrontCenterLevel)
- err = soundtouchClient.SetFrontCenterSpeakerLevel(newFrontCenterLevel)
- if err != nil {
- log.Printf("ā Failed to set front-center level: %v", err)
- } else {
- fmt.Printf("ā
Front-center speaker level adjusted\n")
- }
- }
+ return
+ }
- time.Sleep(1 * time.Second)
+ fmt.Printf("š Current Speaker Levels: %s\n", levelControls.String())
- // Adjust rear-surround speakers level
- newRearSurroundLevel := -1
- if levelControls.RearSurroundSpeakersLevel.Value != newRearSurroundLevel {
- fmt.Printf(" Setting rear-surround speakers level to %d...\n", newRearSurroundLevel)
- err = soundtouchClient.SetRearSurroundSpeakersLevel(newRearSurroundLevel)
- if err != nil {
- log.Printf("ā Failed to set rear-surround level: %v", err)
- } else {
- fmt.Printf("ā
Rear-surround speakers level adjusted\n")
- }
- }
+ // Adjust front-center speaker level
+ newFrontCenterLevel := 2
+ if levelControls.FrontCenterSpeakerLevel.Value != newFrontCenterLevel {
+ fmt.Printf(" Setting front-center speaker level to %d...\n", newFrontCenterLevel)
- time.Sleep(1 * time.Second)
-
- // Adjust both speaker levels together
- combinedFrontCenter := 1
- combinedRearSurround := 0
- fmt.Printf(" Setting front-center to %d and rear-surround to %d together...\n",
- combinedFrontCenter, combinedRearSurround)
- err = soundtouchClient.SetAudioProductLevelControls(&combinedFrontCenter, &combinedRearSurround)
+ err = soundtouchClient.SetFrontCenterSpeakerLevel(newFrontCenterLevel)
if err != nil {
- log.Printf("ā Failed to set speaker levels: %v", err)
+ log.Printf("ā Failed to set front-center level: %v", err)
} else {
- fmt.Printf("ā
Both speaker levels adjusted\n")
+ fmt.Printf("ā
Front-center speaker level adjusted\n")
}
}
- // Example 5: Compare with basic controls
+ time.Sleep(1 * time.Second)
+
+ // Adjust rear-surround speakers level
+ newRearSurroundLevel := -1
+ if levelControls.RearSurroundSpeakersLevel.Value != newRearSurroundLevel {
+ fmt.Printf(" Setting rear-surround speakers level to %d...\n", newRearSurroundLevel)
+
+ err = soundtouchClient.SetRearSurroundSpeakersLevel(newRearSurroundLevel)
+ if err != nil {
+ log.Printf("ā Failed to set rear-surround level: %v", err)
+ } else {
+ fmt.Printf("ā
Rear-surround speakers level adjusted\n")
+ }
+ }
+
+ time.Sleep(1 * time.Second)
+
+ // Adjust both speaker levels together
+ combinedFrontCenter := 1
+ combinedRearSurround := 0
+ fmt.Printf(" Setting front-center to %d and rear-surround to %d together...\n",
+ combinedFrontCenter, combinedRearSurround)
+
+ err = soundtouchClient.SetAudioProductLevelControls(&combinedFrontCenter, &combinedRearSurround)
+ if err != nil {
+ log.Printf("ā Failed to set speaker levels: %v", err)
+ } else {
+ fmt.Printf("ā
Both speaker levels adjusted\n")
+ }
+}
+
+func demonstrateBasicControls(soundtouchClient *client.Client) {
fmt.Println("\n5. Comparison with Basic Audio Controls...")
fmt.Println(" Basic controls available on all devices:")
@@ -215,24 +259,28 @@ func main() {
} else {
fmt.Printf(" Balance: %d (range: -50 to +50)\n", balance.TargetBalance)
}
+}
- // Example 6: Error handling and validation
+func demonstrateErrorHandling(soundtouchClient *client.Client) {
fmt.Println("\n6. Error Handling Examples...")
// Try to set invalid DSP controls to demonstrate validation
fmt.Println(" Testing invalid audio mode...")
- err = soundtouchClient.SetAudioMode("INVALID_MODE")
+
+ err := soundtouchClient.SetAudioMode("INVALID_MODE")
if err != nil {
fmt.Printf("ā ļø Expected error for invalid mode: %v\n", err)
}
fmt.Println(" Testing negative video sync delay...")
+
err = soundtouchClient.SetVideoSyncAudioDelay(-10)
if err != nil {
fmt.Printf("ā ļø Expected error for negative delay: %v\n", err)
}
+}
- // Example 7: CLI command equivalents
+func showCLIEquivalents(deviceIP string) {
fmt.Println("\n7. CLI Command Equivalents...")
fmt.Println(" You can also use the CLI for these operations:")
fmt.Println(" ")
@@ -250,8 +298,9 @@ func main() {
fmt.Printf(" soundtouch-cli audio level get --host %s\n", deviceIP)
fmt.Printf(" soundtouch-cli audio level set --host %s --front-center 2 --rear-surround -1\n", deviceIP)
fmt.Printf(" soundtouch-cli audio level front-center --host %s --level 3\n", deviceIP)
+}
- fmt.Println("\nš Advanced audio controls example completed!")
+func printNotes() {
fmt.Println("\nNotes:")
fmt.Println("⢠Advanced audio controls are only available on professional/high-end devices")
fmt.Println("⢠Consumer SoundTouch devices typically only support basic controls")
diff --git a/examples/zone-slave-operations.go b/examples/zone-slave-operations/main.go
similarity index 98%
rename from examples/zone-slave-operations.go
rename to examples/zone-slave-operations/main.go
index 8f39c8a..792c104 100644
--- a/examples/zone-slave-operations.go
+++ b/examples/zone-slave-operations/main.go
@@ -1,3 +1,4 @@
+// Package main provides an example of using zone slave operations.
package main
import (
diff --git a/pkg/client/audio_test.go b/pkg/client/audio_test.go
index cf2b923..ffba25b 100644
--- a/pkg/client/audio_test.go
+++ b/pkg/client/audio_test.go
@@ -44,6 +44,13 @@ func TestClient_GetAudioDSPControls(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ if r.URL.Path == "/capabilities" {
+ w.WriteHeader(http.StatusOK)
+ _, _ = w.Write([]byte(``))
+
+ return
+ }
+
if r.Method != "GET" {
t.Errorf("Expected GET request, got %s", r.Method)
}
@@ -53,7 +60,7 @@ func TestClient_GetAudioDSPControls(t *testing.T) {
}
w.WriteHeader(tt.responseStatus)
- w.Write([]byte(tt.responseBody))
+ _, _ = w.Write([]byte(tt.responseBody))
}))
defer server.Close()
@@ -64,6 +71,7 @@ func TestClient_GetAudioDSPControls(t *testing.T) {
if err == nil {
t.Errorf("Expected error but got none")
}
+
return
}
@@ -125,13 +133,23 @@ func TestClient_SetAudioDSPControls(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
callCount := 0
+
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
callCount++
+ // Handle capabilities check
+ if r.URL.Path == "/capabilities" {
+ w.WriteHeader(http.StatusOK)
+ _, _ = w.Write([]byte(``))
+
+ return
+ }
+
// First call might be GET for validation
if r.Method == "GET" && r.URL.Path == "/audiodspcontrols" {
w.WriteHeader(http.StatusOK)
- w.Write([]byte(``))
+ _, _ = w.Write([]byte(``))
+
return
}
@@ -145,7 +163,7 @@ func TestClient_SetAudioDSPControls(t *testing.T) {
}
w.WriteHeader(tt.responseStatus)
- w.Write([]byte(tt.responseBody))
+ _, _ = w.Write([]byte(tt.responseBody))
}))
defer server.Close()
@@ -167,17 +185,27 @@ func TestClient_SetAudioDSPControls(t *testing.T) {
func TestClient_SetAudioMode(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ // Handle capabilities check
+ if r.URL.Path == "/capabilities" {
+ w.WriteHeader(http.StatusOK)
+ _, _ = w.Write([]byte(``))
+
+ return
+ }
+
// First call might be GET for validation
if r.Method == "GET" && r.URL.Path == "/audiodspcontrols" {
w.WriteHeader(http.StatusOK)
- w.Write([]byte(``))
+ _, _ = w.Write([]byte(``))
+
return
}
// POST call for setting
if r.Method == "POST" && r.URL.Path == "/audiodspcontrols" {
w.WriteHeader(http.StatusOK)
- w.Write([]byte(`OK`))
+ _, _ = w.Write([]byte(`OK`))
+
return
}
@@ -186,8 +214,8 @@ func TestClient_SetAudioMode(t *testing.T) {
defer server.Close()
client := createTestClient(server.URL)
- err := client.SetAudioMode("MUSIC")
+ err := client.SetAudioMode("MUSIC")
if err != nil {
t.Errorf("Expected no error but got: %v", err)
}
@@ -228,18 +256,19 @@ func TestClient_SetVideoSyncAudioDelay(t *testing.T) {
if err == nil {
t.Errorf("Expected error but got none")
}
+
return
}
- server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
w.WriteHeader(http.StatusOK)
- w.Write([]byte(`OK`))
+ _, _ = w.Write([]byte(`OK`))
}))
defer server.Close()
client := createTestClient(server.URL)
- err := client.SetVideoSyncAudioDelay(tt.delay)
+ err := client.SetVideoSyncAudioDelay(tt.delay)
if err != nil {
t.Errorf("Expected no error but got: %v", err)
}
@@ -289,6 +318,13 @@ func TestClient_GetAudioProductToneControls(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ if r.URL.Path == "/capabilities" {
+ w.WriteHeader(http.StatusOK)
+ _, _ = w.Write([]byte(``))
+
+ return
+ }
+
if r.Method != "GET" {
t.Errorf("Expected GET request, got %s", r.Method)
}
@@ -298,7 +334,7 @@ func TestClient_GetAudioProductToneControls(t *testing.T) {
}
w.WriteHeader(tt.responseStatus)
- w.Write([]byte(tt.responseBody))
+ _, _ = w.Write([]byte(tt.responseBody))
}))
defer server.Close()
@@ -309,6 +345,7 @@ func TestClient_GetAudioProductToneControls(t *testing.T) {
if err == nil {
t.Errorf("Expected error but got none")
}
+
return
}
@@ -374,13 +411,22 @@ func TestClient_SetAudioProductToneControls(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ // Handle capabilities check
+ if r.URL.Path == "/capabilities" {
+ w.WriteHeader(http.StatusOK)
+ _, _ = w.Write([]byte(``))
+
+ return
+ }
+
// First call might be GET for validation
if r.Method == "GET" && r.URL.Path == "/audioproducttonecontrols" {
w.WriteHeader(http.StatusOK)
- w.Write([]byte(`
+ _, _ = w.Write([]byte(`
`))
+
return
}
@@ -394,7 +440,7 @@ func TestClient_SetAudioProductToneControls(t *testing.T) {
}
w.WriteHeader(tt.responseStatus)
- w.Write([]byte(tt.responseBody))
+ _, _ = w.Write([]byte(tt.responseBody))
}))
defer server.Close()
@@ -416,20 +462,30 @@ func TestClient_SetAudioProductToneControls(t *testing.T) {
func TestClient_SetAdvancedBass(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ // Handle capabilities check
+ if r.URL.Path == "/capabilities" {
+ w.WriteHeader(http.StatusOK)
+ _, _ = w.Write([]byte(``))
+
+ return
+ }
+
// First call might be GET for validation
if r.Method == "GET" && r.URL.Path == "/audioproducttonecontrols" {
w.WriteHeader(http.StatusOK)
- w.Write([]byte(`
+ _, _ = w.Write([]byte(`
`))
+
return
}
// POST call for setting
if r.Method == "POST" && r.URL.Path == "/audioproducttonecontrols" {
w.WriteHeader(http.StatusOK)
- w.Write([]byte(`OK`))
+ _, _ = w.Write([]byte(`OK`))
+
return
}
@@ -438,8 +494,8 @@ func TestClient_SetAdvancedBass(t *testing.T) {
defer server.Close()
client := createTestClient(server.URL)
- err := client.SetAdvancedBass(5)
+ err := client.SetAdvancedBass(5)
if err != nil {
t.Errorf("Expected no error but got: %v", err)
}
@@ -447,20 +503,30 @@ func TestClient_SetAdvancedBass(t *testing.T) {
func TestClient_SetAdvancedTreble(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ // Handle capabilities check
+ if r.URL.Path == "/capabilities" {
+ w.WriteHeader(http.StatusOK)
+ _, _ = w.Write([]byte(``))
+
+ return
+ }
+
// First call might be GET for validation
if r.Method == "GET" && r.URL.Path == "/audioproducttonecontrols" {
w.WriteHeader(http.StatusOK)
- w.Write([]byte(`
+ _, _ = w.Write([]byte(`
`))
+
return
}
// POST call for setting
if r.Method == "POST" && r.URL.Path == "/audioproducttonecontrols" {
w.WriteHeader(http.StatusOK)
- w.Write([]byte(`OK`))
+ _, _ = w.Write([]byte(`OK`))
+
return
}
@@ -469,8 +535,8 @@ func TestClient_SetAdvancedTreble(t *testing.T) {
defer server.Close()
client := createTestClient(server.URL)
- err := client.SetAdvancedTreble(-2)
+ err := client.SetAdvancedTreble(-2)
if err != nil {
t.Errorf("Expected no error but got: %v", err)
}
@@ -518,6 +584,13 @@ func TestClient_GetAudioProductLevelControls(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ if r.URL.Path == "/capabilities" {
+ w.WriteHeader(http.StatusOK)
+ _, _ = w.Write([]byte(``))
+
+ return
+ }
+
if r.Method != "GET" {
t.Errorf("Expected GET request, got %s", r.Method)
}
@@ -527,7 +600,7 @@ func TestClient_GetAudioProductLevelControls(t *testing.T) {
}
w.WriteHeader(tt.responseStatus)
- w.Write([]byte(tt.responseBody))
+ _, _ = w.Write([]byte(tt.responseBody))
}))
defer server.Close()
@@ -538,6 +611,7 @@ func TestClient_GetAudioProductLevelControls(t *testing.T) {
if err == nil {
t.Errorf("Expected error but got none")
}
+
return
}
@@ -605,13 +679,22 @@ func TestClient_SetAudioProductLevelControls(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ // Handle capabilities check
+ if r.URL.Path == "/capabilities" {
+ w.WriteHeader(http.StatusOK)
+ _, _ = w.Write([]byte(``))
+
+ return
+ }
+
// First call might be GET for validation
if r.Method == "GET" && r.URL.Path == "/audioproductlevelcontrols" {
w.WriteHeader(http.StatusOK)
- w.Write([]byte(`
+ _, _ = w.Write([]byte(`
`))
+
return
}
@@ -625,7 +708,7 @@ func TestClient_SetAudioProductLevelControls(t *testing.T) {
}
w.WriteHeader(tt.responseStatus)
- w.Write([]byte(tt.responseBody))
+ _, _ = w.Write([]byte(tt.responseBody))
}))
defer server.Close()
@@ -647,20 +730,30 @@ func TestClient_SetAudioProductLevelControls(t *testing.T) {
func TestClient_SetFrontCenterSpeakerLevel(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ // Handle capabilities check
+ if r.URL.Path == "/capabilities" {
+ w.WriteHeader(http.StatusOK)
+ _, _ = w.Write([]byte(``))
+
+ return
+ }
+
// First call might be GET for validation
if r.Method == "GET" && r.URL.Path == "/audioproductlevelcontrols" {
w.WriteHeader(http.StatusOK)
- w.Write([]byte(`
+ _, _ = w.Write([]byte(`
`))
+
return
}
// POST call for setting
if r.Method == "POST" && r.URL.Path == "/audioproductlevelcontrols" {
w.WriteHeader(http.StatusOK)
- w.Write([]byte(`OK`))
+ _, _ = w.Write([]byte(`OK`))
+
return
}
@@ -669,8 +762,8 @@ func TestClient_SetFrontCenterSpeakerLevel(t *testing.T) {
defer server.Close()
client := createTestClient(server.URL)
- err := client.SetFrontCenterSpeakerLevel(5)
+ err := client.SetFrontCenterSpeakerLevel(5)
if err != nil {
t.Errorf("Expected no error but got: %v", err)
}
@@ -678,20 +771,30 @@ func TestClient_SetFrontCenterSpeakerLevel(t *testing.T) {
func TestClient_SetRearSurroundSpeakersLevel(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ // Handle capabilities check
+ if r.URL.Path == "/capabilities" {
+ w.WriteHeader(http.StatusOK)
+ _, _ = w.Write([]byte(``))
+
+ return
+ }
+
// First call might be GET for validation
if r.Method == "GET" && r.URL.Path == "/audioproductlevelcontrols" {
w.WriteHeader(http.StatusOK)
- w.Write([]byte(`
+ _, _ = w.Write([]byte(`
`))
+
return
}
// POST call for setting
if r.Method == "POST" && r.URL.Path == "/audioproductlevelcontrols" {
w.WriteHeader(http.StatusOK)
- w.Write([]byte(`OK`))
+ _, _ = w.Write([]byte(`OK`))
+
return
}
@@ -700,8 +803,8 @@ func TestClient_SetRearSurroundSpeakersLevel(t *testing.T) {
defer server.Close()
client := createTestClient(server.URL)
- err := client.SetRearSurroundSpeakersLevel(-3)
+ err := client.SetRearSurroundSpeakersLevel(-3)
if err != nil {
t.Errorf("Expected no error but got: %v", err)
}
@@ -742,6 +845,7 @@ func TestClient_AudioEndpoints_NetworkError(t *testing.T) {
bass := 5
treble := -2
+
err = client.SetAudioProductToneControls(&bass, &treble)
if err == nil {
t.Errorf("Expected network error for SetAudioProductToneControls but got none")
@@ -764,6 +868,7 @@ func TestClient_AudioEndpoints_NetworkError(t *testing.T) {
frontCenter := 2
rearSurround := -1
+
err = client.SetAudioProductLevelControls(&frontCenter, &rearSurround)
if err == nil {
t.Errorf("Expected network error for SetAudioProductLevelControls but got none")
diff --git a/pkg/client/client.go b/pkg/client/client.go
index fad9f41..1de7471 100644
--- a/pkg/client/client.go
+++ b/pkg/client/client.go
@@ -1075,6 +1075,7 @@ func (c *Client) GetAudioDSPControls() (*models.AudioDSPControls, error) {
}
var dspControls models.AudioDSPControls
+
err = c.get("/audiodspcontrols", &dspControls)
return &dspControls, err
@@ -1146,6 +1147,7 @@ func (c *Client) GetAudioProductToneControls() (*models.AudioProductToneControls
}
var toneControls models.AudioProductToneControls
+
err = c.get("/audioproducttonecontrols", &toneControls)
return &toneControls, err
@@ -1199,6 +1201,7 @@ func (c *Client) GetAudioProductLevelControls() (*models.AudioProductLevelContro
}
var levelControls models.AudioProductLevelControls
+
err = c.get("/audioproductlevelcontrols", &levelControls)
return &levelControls, err
diff --git a/pkg/client/system_test.go b/pkg/client/system_test.go
index f9407c0..e9456f8 100644
--- a/pkg/client/system_test.go
+++ b/pkg/client/system_test.go
@@ -296,6 +296,18 @@ func TestClient_SetClockDisplay(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
+ if tt.expectError && tt.statusCode == 0 {
+ // For client-side validation errors, we don't need a server
+ client := createTestClient("http://localhost:8080")
+
+ err := client.SetClockDisplay(tt.request)
+ if err == nil {
+ t.Error("Expected error, got none")
+ }
+
+ return
+ }
+
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.URL.Path != "/clockDisplay" {
t.Errorf("Expected path '/clockDisplay', got '%s'", r.URL.Path)
@@ -305,7 +317,11 @@ func TestClient_SetClockDisplay(t *testing.T) {
t.Errorf("Expected POST method, got '%s'", r.Method)
}
- w.WriteHeader(tt.statusCode)
+ if tt.statusCode != 0 {
+ w.WriteHeader(tt.statusCode)
+ } else {
+ w.WriteHeader(http.StatusOK)
+ }
}))
defer server.Close()
diff --git a/pkg/client/zone_slave_test.go b/pkg/client/zone_slave_test.go
index 00138ae..912a8d0 100644
--- a/pkg/client/zone_slave_test.go
+++ b/pkg/client/zone_slave_test.go
@@ -94,9 +94,11 @@ func TestClient_AddZoneSlave(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
- var receivedMethod string
- var receivedPath string
- var receivedBody string
+ var (
+ receivedMethod string
+ receivedPath string
+ receivedBody string
+ )
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
receivedMethod = r.Method
@@ -104,12 +106,12 @@ func TestClient_AddZoneSlave(t *testing.T) {
if r.Method == "POST" {
body := make([]byte, r.ContentLength)
- r.Body.Read(body)
+ _, _ = r.Body.Read(body)
receivedBody = string(body)
}
w.WriteHeader(tt.responseStatus)
- w.Write([]byte(tt.responseBody))
+ _, _ = w.Write([]byte(tt.responseBody))
}))
defer server.Close()
@@ -122,6 +124,7 @@ func TestClient_AddZoneSlave(t *testing.T) {
if err == nil {
t.Errorf("Expected error but got none")
}
+
return
}
@@ -171,7 +174,7 @@ func TestClient_AddZoneSlaveByDeviceID(t *testing.T) {
// Read and verify body
body := make([]byte, r.ContentLength)
- r.Body.Read(body)
+ _, _ = r.Body.Read(body)
bodyStr := string(body)
if !strings.Contains(bodyStr, `MASTER123`) {
@@ -188,7 +191,7 @@ func TestClient_AddZoneSlaveByDeviceID(t *testing.T) {
}
w.WriteHeader(http.StatusOK)
- w.Write([]byte(`OK`))
+ _, _ = w.Write([]byte(`OK`))
}))
defer server.Close()
@@ -255,9 +258,11 @@ func TestClient_RemoveZoneSlave(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
- var receivedMethod string
- var receivedPath string
- var receivedBody string
+ var (
+ receivedMethod string
+ receivedPath string
+ receivedBody string
+ )
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
receivedMethod = r.Method
@@ -265,12 +270,12 @@ func TestClient_RemoveZoneSlave(t *testing.T) {
if r.Method == "POST" {
body := make([]byte, r.ContentLength)
- r.Body.Read(body)
+ _, _ = r.Body.Read(body)
receivedBody = string(body)
}
w.WriteHeader(tt.responseStatus)
- w.Write([]byte(tt.responseBody))
+ _, _ = w.Write([]byte(tt.responseBody))
}))
defer server.Close()
@@ -283,6 +288,7 @@ func TestClient_RemoveZoneSlave(t *testing.T) {
if err == nil {
t.Errorf("Expected error but got none")
}
+
return
}
@@ -332,7 +338,7 @@ func TestClient_RemoveZoneSlaveByDeviceID(t *testing.T) {
// Read and verify body
body := make([]byte, r.ContentLength)
- r.Body.Read(body)
+ _, _ = r.Body.Read(body)
bodyStr := string(body)
if !strings.Contains(bodyStr, `MASTER123`) {
@@ -344,7 +350,7 @@ func TestClient_RemoveZoneSlaveByDeviceID(t *testing.T) {
}
w.WriteHeader(http.StatusOK)
- w.Write([]byte(`OK`))
+ _, _ = w.Write([]byte(`OK`))
}))
defer server.Close()
@@ -507,6 +513,7 @@ func TestZoneSlaveRequest_HelperMethods(t *testing.T) {
request.AddSlave("SLAVE456", "192.168.1.101")
str := request.String()
+
expected := "Zone slave operation: master=MASTER123, slave=SLAVE456 (192.168.1.101)"
if str != expected {
t.Errorf("Expected string '%s', got '%s'", expected, str)
@@ -518,6 +525,7 @@ func TestZoneSlaveRequest_HelperMethods(t *testing.T) {
request.AddSlave("SLAVE456", "")
str := request.String()
+
expected := "Zone slave operation: master=MASTER123, slave=SLAVE456"
if str != expected {
t.Errorf("Expected string '%s', got '%s'", expected, str)
diff --git a/pkg/models/audio.go b/pkg/models/audio.go
index 3c5fe31..fa63185 100644
--- a/pkg/models/audio.go
+++ b/pkg/models/audio.go
@@ -129,6 +129,7 @@ func (adsp *AudioDSPControls) GetSupportedAudioModes() []string {
if adsp.SupportedAudioModes == "" {
return []string{}
}
+
return strings.Split(adsp.SupportedAudioModes, "|")
}
@@ -140,12 +141,14 @@ func (adsp *AudioDSPControls) IsAudioModeSupported(mode string) bool {
return true
}
}
+
return false
}
// String returns a human-readable string representation of DSP controls
func (adsp *AudioDSPControls) String() string {
supportedModes := strings.Join(adsp.GetSupportedAudioModes(), ", ")
+
return fmt.Sprintf("Audio Mode: %s, Video Sync Delay: %d ms, Supported Modes: [%s]",
adsp.AudioMode, adsp.VideoSyncAudioDelay, supportedModes)
}
@@ -171,6 +174,7 @@ func (bc *BassControlSetting) ValidateBass(value int) error {
if value < bc.MinValue || value > bc.MaxValue {
return fmt.Errorf("bass value %d is outside valid range [%d, %d]", value, bc.MinValue, bc.MaxValue)
}
+
return nil
}
@@ -179,9 +183,11 @@ func (bc *BassControlSetting) ClampValue(value int) int {
if value < bc.MinValue {
return bc.MinValue
}
+
if value > bc.MaxValue {
return bc.MaxValue
}
+
return value
}
@@ -190,6 +196,7 @@ func (tc *TrebleControlSetting) ValidateTreble(value int) error {
if value < tc.MinValue || value > tc.MaxValue {
return fmt.Errorf("treble value %d is outside valid range [%d, %d]", value, tc.MinValue, tc.MaxValue)
}
+
return nil
}
@@ -198,9 +205,11 @@ func (tc *TrebleControlSetting) ClampValue(value int) int {
if value < tc.MinValue {
return tc.MinValue
}
+
if value > tc.MaxValue {
return tc.MaxValue
}
+
return value
}
@@ -249,6 +258,7 @@ func (fc *FrontCenterLevelSetting) ValidateLevel(value int) error {
if value < fc.MinValue || value > fc.MaxValue {
return fmt.Errorf("front-center speaker level %d is outside valid range [%d, %d]", value, fc.MinValue, fc.MaxValue)
}
+
return nil
}
@@ -257,9 +267,11 @@ func (fc *FrontCenterLevelSetting) ClampLevel(value int) int {
if value < fc.MinValue {
return fc.MinValue
}
+
if value > fc.MaxValue {
return fc.MaxValue
}
+
return value
}
@@ -268,6 +280,7 @@ func (rs *RearSurroundLevelSetting) ValidateLevel(value int) error {
if value < rs.MinValue || value > rs.MaxValue {
return fmt.Errorf("rear-surround speaker level %d is outside valid range [%d, %d]", value, rs.MinValue, rs.MaxValue)
}
+
return nil
}
@@ -276,9 +289,11 @@ func (rs *RearSurroundLevelSetting) ClampLevel(value int) int {
if value < rs.MinValue {
return rs.MinValue
}
+
if value > rs.MaxValue {
return rs.MaxValue
}
+
return value
}
@@ -337,15 +352,19 @@ func (ac *AudioCapabilities) HasAdvancedAudioControls() bool {
// GetAvailableControls returns a list of available advanced audio controls
func (ac *AudioCapabilities) GetAvailableControls() []string {
var controls []string
+
if ac.DSPControls {
controls = append(controls, "DSP Controls")
}
+
if ac.ProductToneControls {
controls = append(controls, "Tone Controls")
}
+
if ac.ProductLevelControls {
controls = append(controls, "Level Controls")
}
+
return controls
}
@@ -356,5 +375,6 @@ func (ac *AudioCapabilities) String() string {
}
controls := ac.GetAvailableControls()
+
return fmt.Sprintf("Available controls: %s", strings.Join(controls, ", "))
}
diff --git a/pkg/models/audio_test.go b/pkg/models/audio_test.go
index e26758e..e9e6992 100644
--- a/pkg/models/audio_test.go
+++ b/pkg/models/audio_test.go
@@ -160,6 +160,7 @@ func TestAudioDSPControlsRequest_Validate(t *testing.T) {
t.Errorf("Expected error but got none")
return
}
+
if !strings.Contains(err.Error(), tt.errorMsg) {
t.Errorf("Expected error message to contain '%s', got '%s'", tt.errorMsg, err.Error())
}
@@ -330,6 +331,7 @@ func TestAudioProductToneControlsRequest_Validate(t *testing.T) {
t.Errorf("Expected error but got none")
return
}
+
if !strings.Contains(err.Error(), tt.errorMsg) {
t.Errorf("Expected error message to contain '%s', got '%s'", tt.errorMsg, err.Error())
}
@@ -455,6 +457,7 @@ func TestAudioProductLevelControlsRequest_Validate(t *testing.T) {
t.Errorf("Expected error but got none")
return
}
+
if !strings.Contains(err.Error(), tt.errorMsg) {
t.Errorf("Expected error message to contain '%s', got '%s'", tt.errorMsg, err.Error())
}
diff --git a/pkg/models/zone.go b/pkg/models/zone.go
index 2e075cc..8244da2 100644
--- a/pkg/models/zone.go
+++ b/pkg/models/zone.go
@@ -452,6 +452,7 @@ func (zsr *ZoneSlaveRequest) GetSlaveDeviceID() string {
if len(zsr.Members) > 0 {
return zsr.Members[0].DeviceID
}
+
return ""
}
@@ -460,6 +461,7 @@ func (zsr *ZoneSlaveRequest) GetSlaveIP() string {
if len(zsr.Members) > 0 {
return zsr.Members[0].IP
}
+
return ""
}
diff --git a/pkg/models/zone_slave_test.go b/pkg/models/zone_slave_test.go
index d4ee583..2b13b2d 100644
--- a/pkg/models/zone_slave_test.go
+++ b/pkg/models/zone_slave_test.go
@@ -151,6 +151,7 @@ func TestZoneSlaveRequest_HelperMethods(t *testing.T) {
request.AddSlave("SLAVE456", "192.168.1.101")
deviceID := request.GetSlaveDeviceID()
+
expected := "SLAVE456"
if deviceID != expected {
t.Errorf("Expected device ID '%s', got '%s'", expected, deviceID)
@@ -171,6 +172,7 @@ func TestZoneSlaveRequest_HelperMethods(t *testing.T) {
request.AddSlave("SLAVE456", "192.168.1.101")
ip := request.GetSlaveIP()
+
expected := "192.168.1.101"
if ip != expected {
t.Errorf("Expected IP '%s', got '%s'", expected, ip)
@@ -208,6 +210,7 @@ func TestZoneSlaveRequest_String(t *testing.T) {
setup: func() *ZoneSlaveRequest {
req := NewZoneSlaveRequest("MASTER123")
req.AddSlave("SLAVE456", "192.168.1.101")
+
return req
},
expected: "Zone slave operation: master=MASTER123, slave=SLAVE456 (192.168.1.101)",
@@ -217,6 +220,7 @@ func TestZoneSlaveRequest_String(t *testing.T) {
setup: func() *ZoneSlaveRequest {
req := NewZoneSlaveRequest("MASTER123")
req.AddSlave("SLAVE456", "")
+
return req
},
expected: "Zone slave operation: master=MASTER123, slave=SLAVE456",
@@ -330,12 +334,14 @@ func TestZoneSlaveRequest_XMLUnmarshaling(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
var request ZoneSlaveRequest
+
err := xml.Unmarshal([]byte(tt.xmlData), &request)
if tt.expectError {
if err == nil {
t.Errorf("Expected error but got none")
}
+
return
}
@@ -381,6 +387,7 @@ func TestZoneSlaveEntry_XMLMarshaling(t *testing.T) {
}
xmlStr := string(xmlData)
+
expected := `SLAVE456`
if xmlStr != expected {
t.Errorf("Expected XML '%s', got '%s'", expected, xmlStr)
@@ -399,6 +406,7 @@ func TestZoneSlaveEntry_XMLMarshaling(t *testing.T) {
}
xmlStr := string(xmlData)
+
expected := `SLAVE456`
if xmlStr != expected {
t.Errorf("Expected XML '%s', got '%s'", expected, xmlStr)