Fix golangci-lint issues: resolve range copy, naming, and whitespace violations

- Fix range copy issues in cmd_network.go (use indexing instead of copying 168-byte structs)
- Rename DiscoveryService to Service to avoid package name stuttering
- Update all references to use new Service constructor names
- Apply automatic whitespace fixes using golangci-lint --fix
- Reduce linting issues from 32 to 7 (only cyclomatic complexity remains)

Remaining issues are architectural complexity violations that require manual refactoring.
This commit is contained in:
Tobias Gesellchen
2026-01-10 00:02:59 +01:00
parent 433368788b
commit 62a67818d2
15 changed files with 149 additions and 45 deletions
+1 -1
View File
@@ -58,7 +58,7 @@ func main() {
}
// Use the configured discovery service to isolate UPnP
configuredService := discovery.NewDiscoveryServiceWithConfig(cfg)
configuredService := discovery.NewServiceWithConfig(cfg)
devices, err := configuredService.DiscoverDevices(ctx)
duration := time.Since(start)
+6
View File
@@ -62,6 +62,7 @@ func setBalance(c *cli.Context) error {
}
PrintSuccess(fmt.Sprintf("Balance level set to %d", level))
return nil
}
@@ -85,6 +86,7 @@ func balanceLeft(c *cli.Context) error {
}
newLevel := currentBalance.ActualBalance - amount
err = client.SetBalanceSafe(newLevel)
if err != nil {
PrintError(fmt.Sprintf("Failed to shift balance left: %v", err))
@@ -92,6 +94,7 @@ func balanceLeft(c *cli.Context) error {
}
PrintSuccess(fmt.Sprintf("Balance shifted from %d to %d (left)", currentBalance.ActualBalance, newLevel))
return nil
}
@@ -115,6 +118,7 @@ func balanceRight(c *cli.Context) error {
}
newLevel := currentBalance.ActualBalance + amount
err = client.SetBalanceSafe(newLevel)
if err != nil {
PrintError(fmt.Sprintf("Failed to shift balance right: %v", err))
@@ -122,6 +126,7 @@ func balanceRight(c *cli.Context) error {
}
PrintSuccess(fmt.Sprintf("Balance shifted from %d to %d (right)", currentBalance.ActualBalance, newLevel))
return nil
}
@@ -143,5 +148,6 @@ func balanceCenter(c *cli.Context) error {
}
PrintSuccess("Balance centered")
return nil
}
+5
View File
@@ -51,6 +51,7 @@ func setBass(c *cli.Context) error {
}
PrintSuccess(fmt.Sprintf("Bass level set to %d", level))
return nil
}
@@ -74,6 +75,7 @@ func bassUp(c *cli.Context) error {
}
newLevel := currentBass.ActualBass + amount
err = client.SetBassSafe(newLevel)
if err != nil {
PrintError(fmt.Sprintf("Failed to increase bass: %v", err))
@@ -81,6 +83,7 @@ func bassUp(c *cli.Context) error {
}
PrintSuccess(fmt.Sprintf("Bass increased from %d to %d", currentBass.ActualBass, newLevel))
return nil
}
@@ -104,6 +107,7 @@ func bassDown(c *cli.Context) error {
}
newLevel := currentBass.ActualBass - amount
err = client.SetBassSafe(newLevel)
if err != nil {
PrintError(fmt.Sprintf("Failed to decrease bass: %v", err))
@@ -111,6 +115,7 @@ func bassDown(c *cli.Context) error {
}
PrintSuccess(fmt.Sprintf("Bass decreased from %d to %d", currentBass.ActualBass, newLevel))
return nil
}
+11
View File
@@ -53,6 +53,7 @@ func setClockTime(c *cli.Context) error {
// Parse time string (HH:MM format)
var hour, minute int
var err error
if timeStr == "now" {
@@ -74,6 +75,7 @@ func setClockTime(c *cli.Context) error {
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)
}
}
@@ -89,6 +91,7 @@ func setClockTime(c *cli.Context) error {
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))
@@ -96,6 +99,7 @@ func setClockTime(c *cli.Context) error {
}
PrintSuccess(fmt.Sprintf("Clock time set to %02d:%02d", hour, minute))
return nil
}
@@ -118,6 +122,7 @@ func setClockTimeNow(c *cli.Context) error {
now := time.Now()
PrintSuccess(fmt.Sprintf("Clock time set to current time (%02d:%02d)", now.Hour(), now.Minute()))
return nil
}
@@ -172,6 +177,7 @@ func enableClockDisplay(c *cli.Context) error {
}
PrintSuccess("Clock display enabled")
return nil
}
@@ -193,6 +199,7 @@ func disableClockDisplay(c *cli.Context) error {
}
PrintSuccess("Clock display disabled")
return nil
}
@@ -210,6 +217,7 @@ func setClockDisplayBrightness(c *cli.Context) error {
// Convert brightness string to numeric value
var brightnessLevel int
switch brightness {
case "low", "LOW":
brightnessLevel = 25
@@ -231,6 +239,7 @@ func setClockDisplayBrightness(c *cli.Context) error {
}
PrintSuccess(fmt.Sprintf("Clock display brightness set to %s", brightness))
return nil
}
@@ -248,6 +257,7 @@ func setClockDisplayFormat(c *cli.Context) error {
// Validate and normalize format value
var formatSetting models.ClockFormat
switch format {
case "12", "12h", "12hour":
formatSetting = models.ClockFormat12Hour
@@ -267,6 +277,7 @@ func setClockDisplayFormat(c *cli.Context) error {
}
PrintSuccess(fmt.Sprintf("Clock display format set to %s", format))
return nil
}
+4
View File
@@ -16,10 +16,12 @@ func discoverDevices(c *cli.Context) error {
showAll := c.Bool("all")
fmt.Printf("Discovering SoundTouch devices...\n")
if showAll {
fmt.Printf("Timeout: %v\n", timeout)
fmt.Printf("Mode: Detailed information\n")
}
fmt.Println()
// Load configuration
@@ -35,6 +37,7 @@ func discoverDevices(c *cli.Context) error {
// Create discovery service
discoveryService := discovery.NewUnifiedDiscoveryService(cfg)
ctx, cancel := context.WithTimeout(context.Background(), cfg.DiscoveryTimeout+5*time.Second)
defer cancel()
@@ -52,6 +55,7 @@ func discoverDevices(c *cli.Context) error {
fmt.Println("- Devices are on a different network segment")
fmt.Println("- Network blocks multicast traffic")
fmt.Println("- Firewall is blocking discovery ports")
return nil
}
+14
View File
@@ -10,6 +10,7 @@ import (
// getDeviceInfo handles the device info command
func getDeviceInfo(c *cli.Context) error {
clientConfig := GetClientConfig(c)
client, err := CreateSoundTouchClient(clientConfig)
if err != nil {
return err
@@ -27,6 +28,7 @@ func getDeviceInfo(c *cli.Context) error {
fmt.Printf(" Name: %s\n", deviceInfo.Name)
fmt.Printf(" Type: %s\n", deviceInfo.Type)
fmt.Printf(" Device ID: %s\n", deviceInfo.DeviceID)
if deviceInfo.MargeAccountUUID != "" {
fmt.Printf(" Account UUID: %s\n", deviceInfo.MargeAccountUUID)
}
@@ -46,9 +48,11 @@ func getDeviceInfo(c *cli.Context) error {
for _, component := range deviceInfo.Components {
fmt.Printf(" - Category: %s\n", component.ComponentCategory)
if component.SoftwareVersion != "" {
fmt.Printf(" Software Version: %s\n", component.SoftwareVersion)
}
if component.SerialNumber != "" {
fmt.Printf(" Serial Number: %s\n", component.SerialNumber)
}
@@ -61,6 +65,7 @@ func getDeviceInfo(c *cli.Context) error {
// getDeviceName handles getting the device name
func getDeviceName(c *cli.Context) error {
clientConfig := GetClientConfig(c)
client, err := CreateSoundTouchClient(clientConfig)
if err != nil {
return err
@@ -74,12 +79,14 @@ func getDeviceName(c *cli.Context) error {
}
fmt.Printf("Device Name: %s\n", name)
return nil
}
// setDeviceName handles setting the device name
func setDeviceName(c *cli.Context) error {
clientConfig := GetClientConfig(c)
client, err := CreateSoundTouchClient(clientConfig)
if err != nil {
return err
@@ -98,12 +105,14 @@ func setDeviceName(c *cli.Context) error {
}
PrintSuccess(fmt.Sprintf("Device name set to '%s'", newName))
return nil
}
// getCapabilities handles getting device capabilities
func getCapabilities(c *cli.Context) error {
clientConfig := GetClientConfig(c)
client, err := CreateSoundTouchClient(clientConfig)
if err != nil {
return err
@@ -137,9 +146,11 @@ func getCapabilities(c *cli.Context) error {
for _, capName := range capNames {
capability := capabilities.GetCapabilityByName(capName)
fmt.Printf(" - %s", capName)
if capability.URL != "" {
fmt.Printf(" (%s)", capability.URL)
}
fmt.Println()
}
}
@@ -150,6 +161,7 @@ func getCapabilities(c *cli.Context) error {
// getPresets handles getting device presets
func getPresets(c *cli.Context) error {
clientConfig := GetClientConfig(c)
client, err := CreateSoundTouchClient(clientConfig)
if err != nil {
return err
@@ -170,6 +182,7 @@ func getPresets(c *cli.Context) error {
}
fmt.Printf(" Configured Presets:\n")
for _, preset := range presets.Preset {
fmt.Printf(" %d. %s\n", preset.ID, preset.GetDisplayName())
fmt.Printf(" Source: %s\n", preset.ContentItem.Source)
@@ -213,6 +226,7 @@ func selectPreset(c *cli.Context) error {
}
PrintSuccess(fmt.Sprintf("Preset %d selected", presetNum))
return nil
}
+8 -2
View File
@@ -36,7 +36,9 @@ func getNetworkInfo(c *cli.Context) error {
}
fmt.Printf(" Interfaces (%d):\n", len(interfaces))
for i, iface := range interfaces {
for i := range interfaces {
iface := &interfaces[i]
fmt.Printf("\n Interface %d:\n", i+1)
fmt.Printf(" Type: %s\n", iface.GetType())
@@ -77,7 +79,9 @@ func getNetworkInfo(c *cli.Context) error {
activeInterfaces := networkInfo.GetActiveInterfaces()
if len(activeInterfaces) > 0 {
fmt.Println("\n Active Connections:")
for _, iface := range activeInterfaces {
for i := range activeInterfaces {
iface := &activeInterfaces[i]
fmt.Printf(" - %s: %s\n", iface.GetType(), iface.GetNetworkSummary())
}
}
@@ -103,6 +107,7 @@ func pingDevice(c *cli.Context) error {
}
PrintSuccess("Device is reachable")
return nil
}
@@ -118,5 +123,6 @@ func getDeviceURL(c *cli.Context) error {
baseURL := client.BaseURL()
fmt.Printf("Device URL: %s\n", baseURL)
return nil
}
+19
View File
@@ -11,6 +11,7 @@ import (
// getNowPlaying handles getting the current playback status
func getNowPlaying(c *cli.Context) error {
clientConfig := GetClientConfig(c)
client, err := CreateSoundTouchClient(clientConfig)
if err != nil {
return err
@@ -48,6 +49,7 @@ func getNowPlaying(c *cli.Context) error {
if nowPlaying.HasTimeInfo() {
fmt.Printf(" Duration: %s\n", nowPlaying.FormatDuration())
if nowPlaying.Position != nil {
fmt.Printf(" Position: %s\n", nowPlaying.FormatPosition())
}
@@ -67,6 +69,7 @@ func getNowPlaying(c *cli.Context) error {
// playCommand handles play command
func playCommand(c *cli.Context) error {
clientConfig := GetClientConfig(c)
client, err := CreateSoundTouchClient(clientConfig)
if err != nil {
return err
@@ -80,12 +83,14 @@ func playCommand(c *cli.Context) error {
}
PrintSuccess("Play command sent")
return nil
}
// pauseCommand handles pause command
func pauseCommand(c *cli.Context) error {
clientConfig := GetClientConfig(c)
client, err := CreateSoundTouchClient(clientConfig)
if err != nil {
return err
@@ -99,12 +104,14 @@ func pauseCommand(c *cli.Context) error {
}
PrintSuccess("Pause command sent")
return nil
}
// stopCommand handles stop command
func stopCommand(c *cli.Context) error {
clientConfig := GetClientConfig(c)
client, err := CreateSoundTouchClient(clientConfig)
if err != nil {
return err
@@ -118,12 +125,14 @@ func stopCommand(c *cli.Context) error {
}
PrintSuccess("Stop command sent")
return nil
}
// nextCommand handles next track command
func nextCommand(c *cli.Context) error {
clientConfig := GetClientConfig(c)
client, err := CreateSoundTouchClient(clientConfig)
if err != nil {
return err
@@ -137,12 +146,14 @@ func nextCommand(c *cli.Context) error {
}
PrintSuccess("Next track command sent")
return nil
}
// prevCommand handles previous track command
func prevCommand(c *cli.Context) error {
clientConfig := GetClientConfig(c)
client, err := CreateSoundTouchClient(clientConfig)
if err != nil {
return err
@@ -156,6 +167,7 @@ func prevCommand(c *cli.Context) error {
}
PrintSuccess("Previous track command sent")
return nil
}
@@ -178,6 +190,7 @@ func sendKey(c *cli.Context) error {
}
PrintSuccess(fmt.Sprintf("%s key command sent", key))
return nil
}
@@ -199,6 +212,7 @@ func powerCommand(c *cli.Context) error {
}
PrintSuccess("Power command sent")
return nil
}
@@ -220,6 +234,7 @@ func muteCommand(c *cli.Context) error {
}
PrintSuccess("Mute command sent")
return nil
}
@@ -241,6 +256,7 @@ func thumbsUpCommand(c *cli.Context) error {
}
PrintSuccess("Thumbs up command sent")
return nil
}
@@ -262,6 +278,7 @@ func thumbsDownCommand(c *cli.Context) error {
}
PrintSuccess("Thumbs down command sent")
return nil
}
@@ -283,6 +300,7 @@ func volumeUpKey(c *cli.Context) error {
}
PrintSuccess("Volume up command sent")
return nil
}
@@ -304,5 +322,6 @@ func volumeDownKey(c *cli.Context) error {
}
PrintSuccess("Volume down command sent")
return nil
}
+17
View File
@@ -10,6 +10,7 @@ import (
// listSources handles listing available audio sources
func listSources(c *cli.Context) error {
clientConfig := GetClientConfig(c)
client, err := CreateSoundTouchClient(clientConfig)
if err != nil {
return err
@@ -32,6 +33,7 @@ func listSources(c *cli.Context) error {
for _, source := range availableSources {
fmt.Printf(" • %s", source.GetDisplayName())
if source.SourceAccount != "" && source.SourceAccount != source.Source {
fmt.Printf(" (%s)", source.SourceAccount)
}
@@ -40,18 +42,22 @@ func listSources(c *cli.Context) error {
if source.IsLocalSource() {
attributes = append(attributes, "Local")
}
if source.IsLocalSource() {
attributes = append(attributes, "Available")
}
if len(attributes) > 0 {
fmt.Printf(" [%s]", strings.Join(attributes, ", "))
}
fmt.Println()
}
}
// Show all configured sources
fmt.Printf(" All Sources:\n")
for _, source := range sources.SourceItem {
status := "Available"
if !source.IsLocalSource() {
@@ -59,6 +65,7 @@ func listSources(c *cli.Context) error {
}
fmt.Printf(" • %s (%s)\n", source.GetDisplayName(), status)
if source.SourceAccount != "" && source.SourceAccount != source.Source {
fmt.Printf(" Account: %s\n", source.SourceAccount)
}
@@ -68,11 +75,14 @@ func listSources(c *cli.Context) error {
streamingSources := sources.GetStreamingSources()
if len(streamingSources) > 0 {
fmt.Printf(" Streaming Services:\n")
for _, source := range streamingSources {
fmt.Printf(" • %s", source.GetDisplayName())
if source.SourceAccount != "" {
fmt.Printf(" (%s)", source.SourceAccount)
}
fmt.Println()
}
}
@@ -83,6 +93,7 @@ func listSources(c *cli.Context) error {
// selectSource handles selecting an audio source
func selectSource(c *cli.Context) error {
clientConfig := GetClientConfig(c)
client, err := CreateSoundTouchClient(clientConfig)
if err != nil {
return err
@@ -110,6 +121,7 @@ func selectSource(c *cli.Context) error {
// selectSpotify handles selecting Spotify source
func selectSpotify(c *cli.Context) error {
clientConfig := GetClientConfig(c)
client, err := CreateSoundTouchClient(clientConfig)
if err != nil {
return err
@@ -123,12 +135,14 @@ func selectSpotify(c *cli.Context) error {
}
PrintSuccess("Spotify source selected")
return nil
}
// selectBluetooth handles selecting Bluetooth source
func selectBluetooth(c *cli.Context) error {
clientConfig := GetClientConfig(c)
client, err := CreateSoundTouchClient(clientConfig)
if err != nil {
return err
@@ -142,12 +156,14 @@ func selectBluetooth(c *cli.Context) error {
}
PrintSuccess("Bluetooth source selected")
return nil
}
// selectAux handles selecting AUX input source
func selectAux(c *cli.Context) error {
clientConfig := GetClientConfig(c)
client, err := CreateSoundTouchClient(clientConfig)
if err != nil {
return err
@@ -161,5 +177,6 @@ func selectAux(c *cli.Context) error {
}
PrintSuccess("AUX input source selected")
return nil
}
+6
View File
@@ -11,6 +11,7 @@ import (
// getVolume handles getting the current volume level
func getVolume(c *cli.Context) error {
clientConfig := GetClientConfig(c)
client, err := CreateSoundTouchClient(clientConfig)
if err != nil {
return err
@@ -39,6 +40,7 @@ func getVolume(c *cli.Context) error {
// setVolume handles setting the volume level
func setVolume(c *cli.Context) error {
clientConfig := GetClientConfig(c)
client, err := CreateSoundTouchClient(clientConfig)
if err != nil {
return err
@@ -77,6 +79,7 @@ func setVolume(c *cli.Context) error {
// volumeUp handles increasing the volume
func volumeUp(c *cli.Context) error {
clientConfig := GetClientConfig(c)
client, err := CreateSoundTouchClient(clientConfig)
if err != nil {
return err
@@ -95,12 +98,14 @@ func volumeUp(c *cli.Context) error {
}
PrintSuccess(fmt.Sprintf("Volume increased to %d (%s)", volume.GetLevel(), models.GetVolumeLevelName(volume.GetLevel())))
return nil
}
// volumeDown handles decreasing the volume
func volumeDown(c *cli.Context) error {
clientConfig := GetClientConfig(c)
client, err := CreateSoundTouchClient(clientConfig)
if err != nil {
return err
@@ -119,5 +124,6 @@ func volumeDown(c *cli.Context) error {
}
PrintSuccess(fmt.Sprintf("Volume decreased to %d (%s)", volume.GetLevel(), models.GetVolumeLevelName(volume.GetLevel())))
return nil
}
+15
View File
@@ -36,11 +36,14 @@ func getZone(c *cli.Context) error {
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 {
@@ -102,11 +105,14 @@ func getZoneMembers(c *cli.Context) error {
}
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()
}
@@ -133,12 +139,14 @@ func createZone(c *cli.Context) error {
// 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)
}
@@ -146,6 +154,7 @@ func createZone(c *cli.Context) error {
// 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()
@@ -158,6 +167,7 @@ func createZone(c *cli.Context) error {
}
PrintSuccess(fmt.Sprintf("Zone created with members: %s", strings.Join(members, ", ")))
return nil
}
@@ -189,6 +199,7 @@ func addToZone(c *cli.Context) error {
// 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))
@@ -196,6 +207,7 @@ func addToZone(c *cli.Context) error {
}
PrintSuccess(fmt.Sprintf("Added %s to zone", memberIP))
return nil
}
@@ -227,6 +239,7 @@ func removeFromZone(c *cli.Context) error {
// 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))
@@ -234,6 +247,7 @@ func removeFromZone(c *cli.Context) error {
}
PrintSuccess(fmt.Sprintf("Removed %s from zone", memberIP))
return nil
}
@@ -255,6 +269,7 @@ func dissolveZone(c *cli.Context) error {
}
PrintSuccess("Zone dissolved")
return nil
}
+1
View File
@@ -66,6 +66,7 @@ func RequireHost(c *cli.Context) error {
if c.String("host") == "" {
return fmt.Errorf("host is required. Use --host flag or set SOUNDTOUCH_HOST environment variable")
}
return nil
}
+2 -2
View File
@@ -12,7 +12,7 @@ import (
// UnifiedDiscoveryService combines SSDP and mDNS discovery methods
type UnifiedDiscoveryService struct {
ssdpService *DiscoveryService
ssdpService *Service
mdnsService *MDNSDiscoveryService
config *config.Config
cache map[string]*models.DiscoveredDevice
@@ -33,7 +33,7 @@ func NewUnifiedDiscoveryService(cfg *config.Config) *UnifiedDiscoveryService {
}
return &UnifiedDiscoveryService{
ssdpService: NewDiscoveryServiceWithConfig(cfg),
ssdpService: NewServiceWithConfig(cfg),
mdnsService: NewMDNSDiscoveryService(timeout),
config: cfg,
cache: make(map[string]*models.DiscoveredDevice),
+22 -22
View File
@@ -16,8 +16,8 @@ import (
"github.com/user_account/bose-soundtouch/pkg/models"
)
// DiscoveryService handles UPnP SSDP discovery of SoundTouch devices
type DiscoveryService struct {
// Service handles UPnP SSDP discovery of SoundTouch devices
type Service struct {
timeout time.Duration
cache map[string]*models.DiscoveredDevice
cacheTTL time.Duration
@@ -25,13 +25,13 @@ type DiscoveryService struct {
config *config.Config
}
// NewDiscoveryService creates a new UPnP discovery service
func NewDiscoveryService(timeout time.Duration) *DiscoveryService {
// NewService creates a new UPnP discovery service
func NewService(timeout time.Duration) *Service {
if timeout == 0 {
timeout = defaultTimeout
}
return &DiscoveryService{
return &Service{
timeout: timeout,
cache: make(map[string]*models.DiscoveredDevice),
cacheTTL: defaultCacheTTL,
@@ -40,8 +40,8 @@ func NewDiscoveryService(timeout time.Duration) *DiscoveryService {
}
}
// NewDiscoveryServiceWithConfig creates a new discovery service with configuration
func NewDiscoveryServiceWithConfig(cfg *config.Config) *DiscoveryService {
// NewServiceWithConfig creates a new discovery service with configuration
func NewServiceWithConfig(cfg *config.Config) *Service {
timeout := cfg.DiscoveryTimeout
if timeout == 0 {
timeout = defaultTimeout
@@ -52,7 +52,7 @@ func NewDiscoveryServiceWithConfig(cfg *config.Config) *DiscoveryService {
cacheTTL = defaultCacheTTL
}
return &DiscoveryService{
return &Service{
timeout: timeout,
cache: make(map[string]*models.DiscoveredDevice),
cacheTTL: cacheTTL,
@@ -62,7 +62,7 @@ func NewDiscoveryServiceWithConfig(cfg *config.Config) *DiscoveryService {
}
// DiscoverDevices discovers all SoundTouch devices on the network
func (d *DiscoveryService) DiscoverDevices(ctx context.Context) ([]*models.DiscoveredDevice, error) {
func (d *Service) DiscoverDevices(ctx context.Context) ([]*models.DiscoveredDevice, error) {
// Check cache first
d.cleanupCache()
@@ -97,7 +97,7 @@ func (d *DiscoveryService) DiscoverDevices(ctx context.Context) ([]*models.Disco
}
// DiscoverDevice discovers a specific SoundTouch device by host
func (d *DiscoveryService) DiscoverDevice(ctx context.Context, host string) (*models.DiscoveredDevice, error) {
func (d *Service) DiscoverDevice(ctx context.Context, host string) (*models.DiscoveredDevice, error) {
// Check cache first
d.mutex.RLock()
@@ -124,13 +124,13 @@ func (d *DiscoveryService) DiscoverDevice(ctx context.Context, host string) (*mo
}
// GetCachedDevices returns all cached devices that haven't expired
func (d *DiscoveryService) GetCachedDevices() []*models.DiscoveredDevice {
func (d *Service) GetCachedDevices() []*models.DiscoveredDevice {
d.cleanupCache()
return d.getCachedDevices()
}
// ClearCache clears the device cache
func (d *DiscoveryService) ClearCache() {
func (d *Service) ClearCache() {
d.mutex.Lock()
defer d.mutex.Unlock()
@@ -138,7 +138,7 @@ func (d *DiscoveryService) ClearCache() {
}
// performDiscovery performs the actual UPnP SSDP discovery
func (d *DiscoveryService) performDiscovery(ctx context.Context) ([]*models.DiscoveredDevice, error) {
func (d *Service) performDiscovery(ctx context.Context) ([]*models.DiscoveredDevice, error) {
log.Printf("UPnP: Starting SSDP discovery for '%s' with timeout %v", soundTouchURN, d.timeout)
// Create UDP connection for multicast
@@ -235,7 +235,7 @@ func (d *DiscoveryService) performDiscovery(ctx context.Context) ([]*models.Disc
}
// buildMSearchRequest builds the M-SEARCH request for SoundTouch devices
func (d *DiscoveryService) buildMSearchRequest() string {
func (d *Service) buildMSearchRequest() string {
return fmt.Sprintf(
"M-SEARCH * HTTP/1.1\r\n"+
"HOST: %s\r\n"+
@@ -250,7 +250,7 @@ func (d *DiscoveryService) buildMSearchRequest() string {
}
// parseResponse parses UPnP SSDP response and extracts device information
func (d *DiscoveryService) parseResponse(response string) (*models.DiscoveredDevice, error) {
func (d *Service) parseResponse(response string) (*models.DiscoveredDevice, error) {
log.Printf("UPnP: Parsing response (%d chars): %.100s...", len(response), strings.ReplaceAll(response, "\r\n", "\\r\\n"))
// Try both \r\n and \n line endings
@@ -338,7 +338,7 @@ func (d *DiscoveryService) parseResponse(response string) (*models.DiscoveredDev
}
// parseLocationURL extracts basic device info from the location URL
func (d *DiscoveryService) parseLocationURL(location string) (*models.DiscoveredDevice, error) {
func (d *Service) parseLocationURL(location string) (*models.DiscoveredDevice, error) {
log.Printf("UPnP: Parsing location URL: %s", location)
// Parse the URL to extract host and port
@@ -366,7 +366,7 @@ func (d *DiscoveryService) parseLocationURL(location string) (*models.Discovered
}
// enrichDeviceInfo tries to get additional device information from the device description
func (d *DiscoveryService) enrichDeviceInfo(_ *models.DiscoveredDevice, location string) error {
func (d *Service) enrichDeviceInfo(_ *models.DiscoveredDevice, location string) error {
log.Printf("UPnP: Attempting to enrich device info by fetching %s", location)
client := &http.Client{
@@ -391,7 +391,7 @@ func (d *DiscoveryService) enrichDeviceInfo(_ *models.DiscoveredDevice, location
}
// updateCache updates the device cache with discovered devices
func (d *DiscoveryService) updateCache(devices []*models.DiscoveredDevice) {
func (d *Service) updateCache(devices []*models.DiscoveredDevice) {
d.mutex.Lock()
defer d.mutex.Unlock()
@@ -401,7 +401,7 @@ func (d *DiscoveryService) updateCache(devices []*models.DiscoveredDevice) {
}
// getCachedDevices returns all valid cached devices (internal method)
func (d *DiscoveryService) getCachedDevices() []*models.DiscoveredDevice {
func (d *Service) getCachedDevices() []*models.DiscoveredDevice {
d.mutex.RLock()
defer d.mutex.RUnlock()
@@ -416,7 +416,7 @@ func (d *DiscoveryService) getCachedDevices() []*models.DiscoveredDevice {
}
// cleanupCache removes expired devices from cache
func (d *DiscoveryService) cleanupCache() {
func (d *Service) cleanupCache() {
d.mutex.Lock()
defer d.mutex.Unlock()
@@ -428,12 +428,12 @@ func (d *DiscoveryService) cleanupCache() {
}
// getConfiguredDevices returns devices from configuration
func (d *DiscoveryService) getConfiguredDevices() []*models.DiscoveredDevice {
func (d *Service) getConfiguredDevices() []*models.DiscoveredDevice {
return d.config.GetPreferredDevicesAsDiscovered()
}
// mergeDevices merges two device lists, avoiding duplicates based on host
func (d *DiscoveryService) mergeDevices(existing, newDevices []*models.DiscoveredDevice) []*models.DiscoveredDevice {
func (d *Service) mergeDevices(existing, newDevices []*models.DiscoveredDevice) []*models.DiscoveredDevice {
hostSet := make(map[string]bool)
result := make([]*models.DiscoveredDevice, 0, len(existing)+len(newDevices))
+18 -18
View File
@@ -12,7 +12,7 @@ import (
func TestNewDiscoveryService(t *testing.T) {
timeout := 5 * time.Second
service := NewDiscoveryService(timeout)
service := NewService(timeout)
if service.timeout != timeout {
t.Errorf("Expected timeout %v, got %v", timeout, service.timeout)
@@ -28,7 +28,7 @@ func TestNewDiscoveryService(t *testing.T) {
}
func TestNewDiscoveryServiceWithDefaultTimeout(t *testing.T) {
service := NewDiscoveryService(0)
service := NewService(0)
if service.timeout != defaultTimeout {
t.Errorf("Expected default timeout %v, got %v", defaultTimeout, service.timeout)
@@ -36,7 +36,7 @@ func TestNewDiscoveryServiceWithDefaultTimeout(t *testing.T) {
}
func TestBuildMSearchRequest(t *testing.T) {
service := NewDiscoveryService(5 * time.Second)
service := NewService(5 * time.Second)
request := service.buildMSearchRequest()
expectedLines := []string{
@@ -60,7 +60,7 @@ func TestBuildMSearchRequest(t *testing.T) {
}
func TestParseLocationURL_Valid(t *testing.T) {
service := NewDiscoveryService(5 * time.Second)
service := NewService(1 * time.Second)
location := "http://192.168.1.100:8090/device.xml"
device, err := service.parseLocationURL(location)
@@ -91,7 +91,7 @@ func TestParseLocationURL_Valid(t *testing.T) {
}
func TestParseLocationURL_Invalid(t *testing.T) {
service := NewDiscoveryService(5 * time.Second)
service := NewService(1 * time.Second)
invalidURLs := []string{
"not-a-url",
@@ -109,7 +109,7 @@ func TestParseLocationURL_Invalid(t *testing.T) {
}
func TestParseResponse_ValidMediaRenderer(t *testing.T) {
service := NewDiscoveryService(5 * time.Second)
service := NewService(1 * time.Second)
validResponse := `HTTP/1.1 200 OK
Cache-Control: max-age=1800
@@ -141,7 +141,7 @@ USN: uuid:12345678-1234-5678-9012-123456789012::urn:schemas-upnp-org:device:Medi
}
func TestParseResponse_NotMediaRenderer(t *testing.T) {
service := NewDiscoveryService(5 * time.Second)
service := NewService(1 * time.Second)
nonMediaRendererResponse := `HTTP/1.1 200 OK
Cache-Control: max-age=1800
@@ -170,7 +170,7 @@ USN: uuid:12345678-1234-5678-9012-123456789012::urn:schemas-upnp-org:device:Some
}
func TestParseResponse_InvalidHTTP(t *testing.T) {
service := NewDiscoveryService(5 * time.Second)
service := NewService(1 * time.Second)
invalidResponses := []string{
"not http response",
@@ -192,7 +192,7 @@ func TestParseResponse_InvalidHTTP(t *testing.T) {
}
func TestParseResponse_NoLocation(t *testing.T) {
service := NewDiscoveryService(5 * time.Second)
service := NewService(1 * time.Second)
responseWithoutLocation := `HTTP/1.1 200 OK
Cache-Control: max-age=1800
@@ -217,7 +217,7 @@ ST: urn:schemas-upnp-org:device:MediaRenderer:1
}
func TestCacheOperations(t *testing.T) {
service := NewDiscoveryService(5 * time.Second)
service := NewService(1 * time.Second)
// Test empty cache
devices := service.GetCachedDevices()
@@ -259,7 +259,7 @@ func TestCacheOperations(t *testing.T) {
}
func TestCacheExpiration(t *testing.T) {
service := NewDiscoveryService(5 * time.Second)
service := NewService(5 * time.Second)
service.cacheTTL = 100 * time.Millisecond // Short TTL for testing
// Add device with old timestamp
@@ -295,7 +295,7 @@ func TestCacheExpiration(t *testing.T) {
}
func TestDiscoverDevices_UseCache(t *testing.T) {
service := NewDiscoveryService(5 * time.Second)
service := NewService(5 * time.Second)
// Add fresh device to cache
freshDevice := &models.DiscoveredDevice{
@@ -326,7 +326,7 @@ func TestDiscoverDevices_UseCache(t *testing.T) {
}
func TestDiscoverDevice_FromCache(t *testing.T) {
service := NewDiscoveryService(5 * time.Second)
service := NewService(5 * time.Second)
// Add device to cache
device := &models.DiscoveredDevice{
@@ -357,7 +357,7 @@ func TestDiscoverDevice_FromCache(t *testing.T) {
}
func TestDiscoverDevice_NotFound(t *testing.T) {
service := NewDiscoveryService(100 * time.Millisecond) // Short timeout
service := NewService(100 * time.Millisecond) // Short timeout
ctx, cancel := context.WithTimeout(context.Background(), 200*time.Millisecond)
defer cancel()
@@ -384,7 +384,7 @@ func TestNewDiscoveryServiceWithConfig(t *testing.T) {
},
}
service := NewDiscoveryServiceWithConfig(cfg)
service := NewServiceWithConfig(cfg)
if service.timeout != 15*time.Second {
t.Errorf("Expected timeout 15s, got %v", service.timeout)
@@ -407,7 +407,7 @@ func TestGetConfiguredDevices(t *testing.T) {
},
}
service := NewDiscoveryServiceWithConfig(cfg)
service := NewServiceWithConfig(cfg)
devices := service.getConfiguredDevices()
if len(devices) != 2 {
@@ -428,7 +428,7 @@ func TestGetConfiguredDevices(t *testing.T) {
}
func TestMergeDevices(t *testing.T) {
service := NewDiscoveryService(5 * time.Second)
service := NewService(5 * time.Second)
existing := []*models.DiscoveredDevice{
{Host: "192.168.1.100", Name: "Device 1", Port: 8090},
@@ -475,7 +475,7 @@ func TestDiscoverDevices_ConfiguredOnly(t *testing.T) {
},
}
service := NewDiscoveryServiceWithConfig(cfg)
service := NewServiceWithConfig(cfg)
ctx := context.Background()
devices, err := service.DiscoverDevices(ctx)