package main import ( "context" "encoding/json" "flag" "fmt" "log" "os" "time" "github.com/0x524a/onvif-go" ) var ( endpoint = flag.String("endpoint", "http://192.168.1.201/onvif/device_service", "ONVIF device endpoint") username = flag.String("username", "admin", "Username for authentication") password = flag.String("password", "", "Password for authentication") output = flag.String("output", "test-results.json", "Output file for results") ) type TestResults struct { Timestamp time.Time `json:"timestamp"` CameraInfo *CameraInfo `json:"camera_info"` DeviceTests map[string]interface{} `json:"device_tests"` MediaTests map[string]interface{} `json:"media_tests"` PTZTests map[string]interface{} `json:"ptz_tests"` ImagingTests map[string]interface{} `json:"imaging_tests"` Errors []string `json:"errors"` } type CameraInfo struct { Manufacturer string `json:"manufacturer"` Model string `json:"model"` FirmwareVersion string `json:"firmware_version"` SerialNumber string `json:"serial_number"` HardwareID string `json:"hardware_id"` } func main() { flag.Parse() if *password == "" { log.Fatal("Password is required. Use -password flag") } log.Printf("Testing ONVIF camera at: %s", *endpoint) log.Printf("Username: %s", *username) // Create client client, err := onvif.NewClient( *endpoint, onvif.WithCredentials(*username, *password), onvif.WithTimeout(30*time.Second), ) if err != nil { log.Fatalf("Failed to create client: %v", err) } ctx := context.Background() results := &TestResults{ Timestamp: time.Now(), DeviceTests: make(map[string]interface{}), MediaTests: make(map[string]interface{}), PTZTests: make(map[string]interface{}), ImagingTests: make(map[string]interface{}), Errors: []string{}, } // Initialize client log.Println("\n=== Initializing Client ===") if err := client.Initialize(ctx); err != nil { log.Printf("Warning: Initialize failed: %v", err) results.Errors = append(results.Errors, fmt.Sprintf("Initialize: %v", err)) } // Get basic device information log.Println("\n=== Getting Device Information ===") info, err := client.GetDeviceInformation(ctx) if err != nil { log.Fatalf("Failed to get device information: %v", err) } log.Printf("Camera: %s %s", info.Manufacturer, info.Model) log.Printf("Firmware: %s", info.FirmwareVersion) log.Printf("Serial: %s", info.SerialNumber) results.CameraInfo = &CameraInfo{ Manufacturer: info.Manufacturer, Model: info.Model, FirmwareVersion: info.FirmwareVersion, SerialNumber: info.SerialNumber, HardwareID: info.HardwareID, } // Test NEW Device Service Methods testDeviceService(ctx, client, results) // Test NEW Media Service Methods testMediaService(ctx, client, results) // Test NEW PTZ Service Methods testPTZService(ctx, client, results) // Test NEW Imaging Service Methods testImagingService(ctx, client, results) // Save results saveResults(results) log.Printf("\n=== Test Complete ===") log.Printf("Results saved to: %s", *output) log.Printf("Total errors: %d", len(results.Errors)) } func testDeviceService(ctx context.Context, client *onvif.Client, results *TestResults) { log.Println("\n=== Testing Device Service (NEW Methods) ===") // Test GetHostname log.Println("\n--- GetHostname ---") if hostname, err := client.GetHostname(ctx); err != nil { log.Printf("❌ GetHostname failed: %v", err) results.Errors = append(results.Errors, fmt.Sprintf("GetHostname: %v", err)) } else { log.Printf("✅ Hostname: %+v", hostname) results.DeviceTests["hostname"] = hostname } // Test GetDNS log.Println("\n--- GetDNS ---") if dns, err := client.GetDNS(ctx); err != nil { log.Printf("❌ GetDNS failed: %v", err) results.Errors = append(results.Errors, fmt.Sprintf("GetDNS: %v", err)) } else { log.Printf("✅ DNS: FromDHCP=%v, SearchDomain=%v", dns.FromDHCP, dns.SearchDomain) log.Printf(" DNSFromDHCP: %+v", dns.DNSFromDHCP) log.Printf(" DNSManual: %+v", dns.DNSManual) results.DeviceTests["dns"] = dns } // Test GetNTP log.Println("\n--- GetNTP ---") if ntp, err := client.GetNTP(ctx); err != nil { log.Printf("❌ GetNTP failed: %v", err) results.Errors = append(results.Errors, fmt.Sprintf("GetNTP: %v", err)) } else { log.Printf("✅ NTP: FromDHCP=%v", ntp.FromDHCP) log.Printf(" NTPFromDHCP: %+v", ntp.NTPFromDHCP) log.Printf(" NTPManual: %+v", ntp.NTPManual) results.DeviceTests["ntp"] = ntp } // Test GetNetworkInterfaces log.Println("\n--- GetNetworkInterfaces ---") if interfaces, err := client.GetNetworkInterfaces(ctx); err != nil { log.Printf("❌ GetNetworkInterfaces failed: %v", err) results.Errors = append(results.Errors, fmt.Sprintf("GetNetworkInterfaces: %v", err)) } else { log.Printf("✅ Found %d network interface(s)", len(interfaces)) for i, iface := range interfaces { log.Printf(" Interface %d: Token=%s, Name=%s, Enabled=%v", i+1, iface.Token, iface.Info.Name, iface.Enabled) log.Printf(" HwAddress=%s, MTU=%d", iface.Info.HwAddress, iface.Info.MTU) if iface.IPv4 != nil { log.Printf(" IPv4: Enabled=%v, DHCP=%v", iface.IPv4.Enabled, iface.IPv4.Config.DHCP) for _, addr := range iface.IPv4.Config.Manual { log.Printf(" Manual: %s/%d", addr.Address, addr.PrefixLength) } } } results.DeviceTests["network_interfaces"] = interfaces } // Test GetScopes log.Println("\n--- GetScopes ---") if scopes, err := client.GetScopes(ctx); err != nil { log.Printf("❌ GetScopes failed: %v", err) results.Errors = append(results.Errors, fmt.Sprintf("GetScopes: %v", err)) } else { log.Printf("✅ Found %d scope(s)", len(scopes)) for i, scope := range scopes { log.Printf(" Scope %d: Def=%s, Item=%s", i+1, scope.ScopeDef, scope.ScopeItem) } results.DeviceTests["scopes"] = scopes } // Test GetUsers log.Println("\n--- GetUsers ---") if users, err := client.GetUsers(ctx); err != nil { log.Printf("❌ GetUsers failed: %v", err) results.Errors = append(results.Errors, fmt.Sprintf("GetUsers: %v", err)) } else { log.Printf("✅ Found %d user(s)", len(users)) for i, user := range users { log.Printf(" User %d: Username=%s, Level=%s", i+1, user.Username, user.UserLevel) } results.DeviceTests["users"] = users } } func testMediaService(ctx context.Context, client *onvif.Client, results *TestResults) { log.Println("\n=== Testing Media Service (NEW Methods) ===") // Test GetVideoSources log.Println("\n--- GetVideoSources ---") if sources, err := client.GetVideoSources(ctx); err != nil { log.Printf("❌ GetVideoSources failed: %v", err) results.Errors = append(results.Errors, fmt.Sprintf("GetVideoSources: %v", err)) } else { log.Printf("✅ Found %d video source(s)", len(sources)) for i, source := range sources { log.Printf(" Source %d: Token=%s, Framerate=%.1f", i+1, source.Token, source.Framerate) if source.Resolution != nil { log.Printf(" Resolution: %dx%d", source.Resolution.Width, source.Resolution.Height) } } results.MediaTests["video_sources"] = sources } // Test GetAudioSources log.Println("\n--- GetAudioSources ---") if sources, err := client.GetAudioSources(ctx); err != nil { log.Printf("❌ GetAudioSources failed: %v", err) results.Errors = append(results.Errors, fmt.Sprintf("GetAudioSources: %v", err)) } else { log.Printf("✅ Found %d audio source(s)", len(sources)) for i, source := range sources { log.Printf(" Source %d: Token=%s, Channels=%d", i+1, source.Token, source.Channels) } results.MediaTests["audio_sources"] = sources } // Test GetAudioOutputs log.Println("\n--- GetAudioOutputs ---") if outputs, err := client.GetAudioOutputs(ctx); err != nil { log.Printf("❌ GetAudioOutputs failed: %v", err) results.Errors = append(results.Errors, fmt.Sprintf("GetAudioOutputs: %v", err)) } else { log.Printf("✅ Found %d audio output(s)", len(outputs)) for i, output := range outputs { log.Printf(" Output %d: Token=%s", i+1, output.Token) } results.MediaTests["audio_outputs"] = outputs } // Get profiles for further testing profiles, err := client.GetProfiles(ctx) if err != nil { log.Printf("⚠️ Could not get profiles: %v", err) return } if len(profiles) > 0 { log.Printf("\nUsing profile: %s (%s)", profiles[0].Name, profiles[0].Token) results.MediaTests["test_profile_token"] = profiles[0].Token } } func testPTZService(ctx context.Context, client *onvif.Client, results *TestResults) { log.Println("\n=== Testing PTZ Service (NEW Methods) ===") // Get profiles to find one with PTZ profiles, err := client.GetProfiles(ctx) if err != nil { log.Printf("⚠️ Could not get profiles for PTZ tests: %v", err) return } var ptzProfile *onvif.Profile for _, p := range profiles { if p.PTZConfiguration != nil { ptzProfile = p break } } if ptzProfile == nil { log.Println("⚠️ No PTZ-enabled profile found, skipping PTZ tests") results.PTZTests["skipped"] = "No PTZ profile found" return } log.Printf("Using PTZ profile: %s (%s)", ptzProfile.Name, ptzProfile.Token) results.PTZTests["test_profile_token"] = ptzProfile.Token // Test GetConfigurations log.Println("\n--- GetConfigurations ---") if configs, err := client.GetConfigurations(ctx); err != nil { log.Printf("❌ GetConfigurations failed: %v", err) results.Errors = append(results.Errors, fmt.Sprintf("GetConfigurations: %v", err)) } else { log.Printf("✅ Found %d PTZ configuration(s)", len(configs)) for i, cfg := range configs { log.Printf(" Config %d: Token=%s, Name=%s, NodeToken=%s", i+1, cfg.Token, cfg.Name, cfg.NodeToken) } results.PTZTests["configurations"] = configs } // Test GetConfiguration if ptzProfile.PTZConfiguration != nil { log.Println("\n--- GetConfiguration ---") if cfg, err := client.GetConfiguration(ctx, ptzProfile.PTZConfiguration.Token); err != nil { log.Printf("❌ GetConfiguration failed: %v", err) results.Errors = append(results.Errors, fmt.Sprintf("GetConfiguration: %v", err)) } else { log.Printf("✅ Configuration: Token=%s, Name=%s", cfg.Token, cfg.Name) results.PTZTests["configuration"] = cfg } } // Test GetPresets log.Println("\n--- GetPresets ---") if presets, err := client.GetPresets(ctx, ptzProfile.Token); err != nil { log.Printf("❌ GetPresets failed: %v", err) results.Errors = append(results.Errors, fmt.Sprintf("GetPresets: %v", err)) } else { log.Printf("✅ Found %d preset(s)", len(presets)) for i, preset := range presets { log.Printf(" Preset %d: Token=%s, Name=%s", i+1, preset.Token, preset.Name) if preset.PTZPosition != nil { if preset.PTZPosition.PanTilt != nil { log.Printf(" PanTilt: X=%.2f, Y=%.2f", preset.PTZPosition.PanTilt.X, preset.PTZPosition.PanTilt.Y) } if preset.PTZPosition.Zoom != nil { log.Printf(" Zoom: X=%.2f", preset.PTZPosition.Zoom.X) } } } results.PTZTests["presets"] = presets } // Test GetStatus log.Println("\n--- GetStatus ---") if status, err := client.GetStatus(ctx, ptzProfile.Token); err != nil { log.Printf("❌ GetStatus failed: %v", err) results.Errors = append(results.Errors, fmt.Sprintf("PTZ GetStatus: %v", err)) } else { log.Printf("✅ PTZ Status:") if status.Position != nil { if status.Position.PanTilt != nil { log.Printf(" Position PanTilt: X=%.2f, Y=%.2f", status.Position.PanTilt.X, status.Position.PanTilt.Y) } if status.Position.Zoom != nil { log.Printf(" Position Zoom: X=%.2f", status.Position.Zoom.X) } } if status.MoveStatus != nil { log.Printf(" MoveStatus: PanTilt=%s, Zoom=%s", status.MoveStatus.PanTilt, status.MoveStatus.Zoom) } results.PTZTests["status"] = status } } func testImagingService(ctx context.Context, client *onvif.Client, results *TestResults) { log.Println("\n=== Testing Imaging Service (NEW Methods) ===") // Get video sources first sources, err := client.GetVideoSources(ctx) if err != nil || len(sources) == 0 { log.Printf("⚠️ Could not get video sources for imaging tests: %v", err) return } videoSourceToken := sources[0].Token log.Printf("Using video source: %s", videoSourceToken) results.ImagingTests["test_video_source_token"] = videoSourceToken // Test GetOptions log.Println("\n--- GetOptions ---") if options, err := client.GetOptions(ctx, videoSourceToken); err != nil { log.Printf("❌ GetOptions failed: %v", err) results.Errors = append(results.Errors, fmt.Sprintf("GetOptions: %v", err)) } else { log.Printf("✅ Imaging Options:") if options.Brightness != nil { log.Printf(" Brightness: Min=%.1f, Max=%.1f", options.Brightness.Min, options.Brightness.Max) } if options.ColorSaturation != nil { log.Printf(" ColorSaturation: Min=%.1f, Max=%.1f", options.ColorSaturation.Min, options.ColorSaturation.Max) } if options.Contrast != nil { log.Printf(" Contrast: Min=%.1f, Max=%.1f", options.Contrast.Min, options.Contrast.Max) } results.ImagingTests["options"] = options } // Test GetMoveOptions log.Println("\n--- GetMoveOptions ---") if moveOptions, err := client.GetMoveOptions(ctx, videoSourceToken); err != nil { log.Printf("❌ GetMoveOptions failed: %v", err) results.Errors = append(results.Errors, fmt.Sprintf("GetMoveOptions: %v", err)) } else { log.Printf("✅ Move Options:") if moveOptions.Absolute != nil { log.Printf(" Absolute Position: Min=%.1f, Max=%.1f", moveOptions.Absolute.Position.Min, moveOptions.Absolute.Position.Max) log.Printf(" Absolute Speed: Min=%.1f, Max=%.1f", moveOptions.Absolute.Speed.Min, moveOptions.Absolute.Speed.Max) } if moveOptions.Relative != nil { log.Printf(" Relative Distance: Min=%.1f, Max=%.1f", moveOptions.Relative.Distance.Min, moveOptions.Relative.Distance.Max) } if moveOptions.Continuous != nil { log.Printf(" Continuous Speed: Min=%.1f, Max=%.1f", moveOptions.Continuous.Speed.Min, moveOptions.Continuous.Speed.Max) } results.ImagingTests["move_options"] = moveOptions } // Test GetImagingStatus log.Println("\n--- GetImagingStatus ---") if status, err := client.GetImagingStatus(ctx, videoSourceToken); err != nil { log.Printf("❌ GetImagingStatus failed: %v", err) results.Errors = append(results.Errors, fmt.Sprintf("Imaging GetImagingStatus: %v", err)) } else { log.Printf("✅ Imaging Status:") if status.FocusStatus != nil { log.Printf(" Focus Position: %.2f", status.FocusStatus.Position) log.Printf(" Focus MoveStatus: %s", status.FocusStatus.MoveStatus) if status.FocusStatus.Error != "" { log.Printf(" Focus Error: %s", status.FocusStatus.Error) } } results.ImagingTests["status"] = status } } func saveResults(results *TestResults) { data, err := json.MarshalIndent(results, "", " ") if err != nil { log.Fatalf("Failed to marshal results: %v", err) } if err := os.WriteFile(*output, data, 0644); err != nil { log.Fatalf("Failed to write results: %v", err) } }