445 lines
15 KiB
Go
445 lines
15 KiB
Go
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")
|
|
verbose = flag.Bool("verbose", true, "Enable verbose output")
|
|
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)
|
|
}
|
|
}
|