diff --git a/doc.go b/doc.go index d285c75..7681f15 100644 --- a/doc.go +++ b/doc.go @@ -56,7 +56,8 @@ // import "github.com/gesellix/bose-soundtouch/pkg/discovery" // // // Discover devices using UPnP/SSDP -// devices, err := discovery.DiscoverDevices(ctx, 5*time.Second) +// service := discovery.NewService(5*time.Second) +// devices, err := service.DiscoverDevices(ctx) // if err != nil { // log.Fatal(err) // } diff --git a/pkg/client/client.go b/pkg/client/client.go index 4ea983e..4a58b10 100644 --- a/pkg/client/client.go +++ b/pkg/client/client.go @@ -73,11 +73,11 @@ // } // // // Create a new zone with multiple speakers -// newZone := &models.Zone{ +// newZone := &models.ZoneRequest{ // Master: "192.168.1.100", -// Members: []models.ZoneMember{ -// {IPAddress: "192.168.1.101"}, -// {IPAddress: "192.168.1.102"}, +// Members: []models.MemberEntry{ +// {IP: "192.168.1.101"}, +// {IP: "192.168.1.102"}, // }, // } // err = client.SetZone(newZone) diff --git a/pkg/client/example_test.go b/pkg/client/example_test.go index 32a6071..f7e54e2 100644 --- a/pkg/client/example_test.go +++ b/pkg/client/example_test.go @@ -1,7 +1,6 @@ package client_test import ( - "context" "fmt" "log" "time" @@ -21,7 +20,7 @@ func Example() { c := client.NewClient(config) // Get device information - info, err := c.GetInfo() + info, err := c.GetDeviceInfo() if err != nil { log.Fatal(err) } @@ -83,7 +82,7 @@ func ExampleClient_SetVolume() { } fmt.Printf("Volume: %d\n", volume.ActualVolume) - fmt.Printf("Muted: %t\n", volume.Muted) + fmt.Printf("Muted: %t\n", volume.MuteEnabled) // Output: // Volume: 75 @@ -171,11 +170,11 @@ func ExampleClient_SetZone() { c := client.NewClient(config) // Create a zone with multiple speakers - zone := &models.Zone{ + zone := &models.ZoneRequest{ Master: "192.168.1.100", - Members: []models.ZoneMember{ - {IPAddress: "192.168.1.101"}, - {IPAddress: "192.168.1.102"}, + Members: []models.MemberEntry{ + {IP: "192.168.1.101"}, + {IP: "192.168.1.102"}, }, } @@ -200,8 +199,8 @@ func ExampleClient_GetPresets() { log.Fatal(err) } - for _, preset := range presets.Presets { - fmt.Printf("Preset %d: %s (%s)\n", preset.ID, preset.Name, preset.Source) + for _, preset := range presets.Preset { + fmt.Printf("Preset %d: %s (%s)\n", preset.ID, preset.GetDisplayName(), preset.GetSource()) } // Output: @@ -210,39 +209,25 @@ func ExampleClient_GetPresets() { // Preset 3: NPR News (INTERNET_RADIO) } -// ExampleClient_SubscribeToEvents demonstrates real-time event monitoring. -func ExampleClient_SubscribeToEvents() { +// ExampleClient_NewWebSocketClient demonstrates WebSocket client creation. +func ExampleClient_NewWebSocketClient() { config := &client.Config{Host: "192.168.1.100"} c := client.NewClient(config) - ctx := context.Background() - events, err := c.SubscribeToEvents(ctx) + // Create WebSocket client for real-time events + wsClient := c.NewWebSocketClient(nil) + + // Connect to device WebSocket + err := wsClient.Connect() if err != nil { log.Fatal(err) } + defer wsClient.Disconnect() - // Monitor events for a short time - timeout := time.After(5 * time.Second) - - for { - select { - case event := <-events: - switch e := event.(type) { - case *models.NowPlayingUpdated: - fmt.Printf("Track changed: %s by %s\n", e.Track, e.Artist) - case *models.VolumeUpdated: - fmt.Printf("Volume changed: %d\n", e.ActualVolume) - } - case <-timeout: - fmt.Println("Event monitoring completed") - return - } - } + fmt.Printf("WebSocket connected: %t\n", wsClient.IsConnected()) // Output: - // Track changed: Stairway to Heaven by Led Zeppelin - // Volume changed: 65 - // Event monitoring completed + // WebSocket connected: true } // ExampleClient_SendKey demonstrates sending key commands. @@ -254,7 +239,7 @@ func ExampleClient_SendKey() { commands := []string{"PLAY", "PAUSE", "NEXT_TRACK", "PREV_TRACK", "MUTE"} for _, cmd := range commands { - err := c.SendKey(cmd, "press") + err := c.SendKey(cmd) if err != nil { log.Printf("Failed to send %s: %v", cmd, err) continue @@ -280,16 +265,16 @@ func ExampleClient_GetCapabilities() { log.Fatal(err) } - fmt.Printf("Device supports %d sources\n", len(capabilities.Sources)) - for _, source := range capabilities.Sources { - fmt.Printf("- %s (%s)\n", source.Source, source.SourceAccount) + fmt.Printf("Device supports %d capabilities\n", len(capabilities.Capability)) + for _, capability := range capabilities.Capability { + fmt.Printf("- %s (URL: %s)\n", capability.Name, capability.URL) } // Output: - // Device supports 5 sources - // - SPOTIFY (spotify_user123) - // - BLUETOOTH () - // - AUX () - // - AIRPLAY () - // - INTERNET_RADIO () + // Device supports 5 capabilities + // - VOLUME (/volume) + // - BASS (/bass) + // - SOURCES (/sources) + // - PRESETS (/presets) + // - ZONE (/getZone) } diff --git a/pkg/discovery/example_test.go b/pkg/discovery/example_test.go index e28afbf..4c6fe1b 100644 --- a/pkg/discovery/example_test.go +++ b/pkg/discovery/example_test.go @@ -6,16 +6,17 @@ import ( "log" "time" + "github.com/gesellix/bose-soundtouch/pkg/config" "github.com/gesellix/bose-soundtouch/pkg/discovery" ) // Example demonstrates basic device discovery. func Example() { + service := discovery.NewService(5 * time.Second) ctx := context.Background() - timeout := 5 * time.Second // Discover all SoundTouch devices on the network - devices, err := discovery.DiscoverDevices(ctx, timeout) + devices, err := service.DiscoverDevices(ctx) if err != nil { log.Fatal(err) } @@ -31,12 +32,13 @@ func Example() { // - Kitchen at 192.168.1.101:8090 } -// ExampleDiscoverDevices demonstrates discovering devices with timeout. -func ExampleDiscoverDevices() { +// ExampleService_DiscoverDevices demonstrates discovering devices with timeout. +func ExampleService_DiscoverDevices() { + service := discovery.NewService(3 * time.Second) ctx := context.Background() // Quick discovery with 3 second timeout - devices, err := discovery.DiscoverDevices(ctx, 3*time.Second) + devices, err := service.DiscoverDevices(ctx) if err != nil { log.Fatal(err) } @@ -50,39 +52,40 @@ func ExampleDiscoverDevices() { for _, device := range devices { fmt.Printf("Device: %s\n", device.Name) fmt.Printf(" Address: %s:%d\n", device.Host, device.Port) - fmt.Printf(" MAC: %s\n", device.MACAddress) - fmt.Printf(" Method: %s\n", device.DiscoveryMethod) - fmt.Printf(" URL: %s\n", device.BaseURL) + fmt.Printf(" Serial: %s\n", device.SerialNo) + fmt.Printf(" Location: %s\n", device.Location) + fmt.Printf(" Host: %s:%d\n", device.Host, device.Port) fmt.Println() } // Output: // Device: Living Room // Address: 192.168.1.100:8090 - // MAC: AA:BB:CC:DD:EE:FF - // Method: UPnP - // URL: http://192.168.1.100:8090 + // Serial: AA123456789 + // Location: /device.xml + // Host: 192.168.1.100:8090 // // Device: Kitchen // Address: 192.168.1.101:8090 - // MAC: BB:CC:DD:EE:FF:AA - // Method: mDNS - // URL: http://192.168.1.101:8090 + // Serial: BB123456789 + // Location: /device.xml + // Host: 192.168.1.101:8090 } -// ExampleUnifiedDiscoveryService_DiscoverWithCache demonstrates caching functionality. -func ExampleUnifiedDiscoveryService_DiscoverWithCache() { - service, err := discovery.NewUnifiedDiscoveryService() - if err != nil { - log.Fatal(err) +// ExampleUnifiedDiscoveryService_DiscoverDevices demonstrates caching functionality. +func ExampleUnifiedDiscoveryService_DiscoverDevices() { + cfg := &config.Config{ + DiscoveryTimeout: 5 * time.Second, + CacheEnabled: true, + CacheTTL: 5 * time.Minute, } + service := discovery.NewUnifiedDiscoveryService(cfg) ctx := context.Background() - timeout := 5 * time.Second // First discovery scan fmt.Println("First scan:") - devices, err := service.DiscoverWithCache(ctx, timeout) + devices, err := service.DiscoverDevices(ctx) if err != nil { log.Fatal(err) } @@ -90,7 +93,7 @@ func ExampleUnifiedDiscoveryService_DiscoverWithCache() { // Second scan (should use cache) fmt.Println("Second scan (cached):") - devices, err = service.DiscoverWithCache(ctx, timeout) + devices, err = service.DiscoverDevices(ctx) if err != nil { log.Fatal(err) } @@ -103,108 +106,55 @@ func ExampleUnifiedDiscoveryService_DiscoverWithCache() { // Found 2 devices (from cache) } -// ExampleUnifiedDiscoveryService_DiscoverUPnP demonstrates UPnP-only discovery. -func ExampleUnifiedDiscoveryService_DiscoverUPnP() { - service, err := discovery.NewUnifiedDiscoveryService() - if err != nil { - log.Fatal(err) - } - +// Example_upnpOnlyDiscovery demonstrates UPnP-only discovery. +func Example_upnpOnlyDiscovery() { + service := discovery.NewService(3 * time.Second) ctx := context.Background() - timeout := 3 * time.Second - // Use only UPnP/SSDP discovery - devices, err := service.DiscoverUPnP(ctx, timeout) + // Use UPnP/SSDP discovery + devices, err := service.DiscoverDevices(ctx) if err != nil { log.Fatal(err) } fmt.Printf("UPnP discovered %d devices:\n", len(devices)) for _, device := range devices { - fmt.Printf("- %s (Method: %s)\n", device.Name, device.DiscoveryMethod) + fmt.Printf("- %s at %s:%d\n", device.Name, device.Host, device.Port) } // Output: // UPnP discovered 1 devices: - // - Living Room (Method: UPnP) + // - Living Room at 192.168.1.100:8090 } -// ExampleUnifiedDiscoveryService_DiscoverMDNS demonstrates mDNS-only discovery. -func ExampleUnifiedDiscoveryService_DiscoverMDNS() { - service, err := discovery.NewUnifiedDiscoveryService() - if err != nil { - log.Fatal(err) - } - +// ExampleMDNSDiscoveryService_DiscoverDevices demonstrates mDNS-only discovery. +func ExampleMDNSDiscoveryService_DiscoverDevices() { + service := discovery.NewMDNSDiscoveryService(3 * time.Second) ctx := context.Background() - timeout := 3 * time.Second // Use only mDNS discovery - devices, err := service.DiscoverMDNS(ctx, timeout) + devices, err := service.DiscoverDevices(ctx) if err != nil { log.Fatal(err) } fmt.Printf("mDNS discovered %d devices:\n", len(devices)) for _, device := range devices { - fmt.Printf("- %s (Method: %s)\n", device.Name, device.DiscoveryMethod) + fmt.Printf("- %s at %s:%d\n", device.Name, device.Host, device.Port) } // Output: // mDNS discovered 1 devices: - // - Kitchen (Method: mDNS) -} - -// ExampleService_Discover demonstrates basic UPnP discovery service. -func ExampleService_Discover() { - service := discovery.NewService() - ctx := context.Background() - timeout := 5 * time.Second - - devices, err := service.Discover(ctx, timeout) - if err != nil { - log.Fatal(err) - } - - fmt.Printf("UPnP/SSDP found %d devices:\n", len(devices)) - for _, device := range devices { - fmt.Printf("- %s at %s\n", device.Name, device.BaseURL) - } - - // Output: - // UPnP/SSDP found 1 devices: - // - Living Room at http://192.168.1.100:8090 -} - -// ExampleMDNSDiscoveryService_Discover demonstrates mDNS discovery service. -func ExampleMDNSDiscoveryService_Discover() { - service := discovery.NewMDNSDiscoveryService() - ctx := context.Background() - timeout := 5 * time.Second - - devices, err := service.Discover(ctx, timeout) - if err != nil { - log.Fatal(err) - } - - fmt.Printf("mDNS found %d devices:\n", len(devices)) - for _, device := range devices { - fmt.Printf("- %s at %s\n", device.Name, device.BaseURL) - } - - // Output: - // mDNS found 1 devices: - // - Kitchen at http://192.168.1.101:8090 + // - Kitchen at 192.168.1.101:8090 } // Example_errorHandling demonstrates proper error handling in discovery. func Example_errorHandling() { + // Very short timeout to demonstrate timeout handling + service := discovery.NewService(100 * time.Millisecond) ctx := context.Background() - // Very short timeout to demonstrate timeout handling - shortTimeout := 100 * time.Millisecond - - devices, err := discovery.DiscoverDevices(ctx, shortTimeout) + devices, err := service.DiscoverDevices(ctx) if err != nil { fmt.Printf("Discovery error: %v\n", err) return @@ -227,7 +177,9 @@ func Example_contextCancellation() { ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second) defer cancel() - devices, err := discovery.DiscoverDevices(ctx, 10*time.Second) + service := discovery.NewService(10 * time.Second) + + devices, err := service.DiscoverDevices(ctx) if err != nil { if ctx.Err() == context.DeadlineExceeded { fmt.Println("Discovery cancelled due to context timeout") diff --git a/pkg/models/doc.go b/pkg/models/doc.go index f22dafb..2feb63d 100644 --- a/pkg/models/doc.go +++ b/pkg/models/doc.go @@ -88,12 +88,11 @@ // Device discovery structures: // // device := models.DiscoveredDevice{ -// Name: "Living Room", -// Host: "192.168.1.100", -// Port: 8090, -// MACAddress: "AA:BB:CC:DD:EE:FF", -// DiscoveryMethod: "UPnP", -// BaseURL: "http://192.168.1.100:8090", +// Name: "Living Room", +// Host: "192.168.1.100", +// Port: 8090, +// SerialNo: "AA123456789", +// Location: "/device.xml", // } // // # Validation and Constraints