Add camera test framework and initial tests for Bosch FLEXIDOME indoor 5100i IR

- Introduced a new directory `testdata/captures/` containing captured XML archives and README documentation for the camera test framework.
- Added a mock server implementation to replay captured SOAP responses for testing.
- Created automated tests for Bosch FLEXIDOME indoor 5100i IR using captured responses, validating device information, system date and time, capabilities, and profiles.
- Implemented enhanced device features tests, covering hostname, DNS, NTP, network interfaces, scopes, and user management.
- Added support for enhanced media and imaging features, including video and audio sources, and imaging options.
- Updated types to include new configurations and options for network, imaging, and device capabilities.
This commit is contained in:
ProtoTess
2025-11-11 02:10:04 +00:00
parent 3340094f4f
commit 3bf078ed3f
27 changed files with 5701 additions and 147 deletions
+255
View File
@@ -0,0 +1,255 @@
package main
import (
"context"
"fmt"
"log"
"time"
"github.com/0x524A/go-onvif"
)
func main() {
// Camera connection details
endpoint := "http://192.168.1.201/onvif/device_service"
username := "service"
password := "Service.1234"
fmt.Println("=== Comprehensive ONVIF Camera Test ===")
fmt.Println("Connecting to:", endpoint)
fmt.Println()
// 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()
// Test 1: Get Device Information
fmt.Println("=== Test 1: GetDeviceInformation ===")
info, err := client.GetDeviceInformation(ctx)
if err != nil {
log.Printf("ERROR: %v\n", err)
} else {
fmt.Printf("✓ Manufacturer: %s\n", info.Manufacturer)
fmt.Printf("✓ Model: %s\n", info.Model)
fmt.Printf("✓ Firmware: %s\n", info.FirmwareVersion)
fmt.Printf("✓ Serial Number: %s\n", info.SerialNumber)
fmt.Printf("✓ Hardware ID: %s\n", info.HardwareID)
}
fmt.Println()
// Test 2: Get System Date and Time
fmt.Println("=== Test 2: GetSystemDateAndTime ===")
dateTime, err := client.GetSystemDateAndTime(ctx)
if err != nil {
log.Printf("ERROR: %v\n", err)
} else {
fmt.Printf("✓ System Date/Time: %+v\n", dateTime)
}
fmt.Println()
// Test 3: Get Capabilities
fmt.Println("=== Test 3: GetCapabilities ===")
capabilities, err := client.GetCapabilities(ctx)
if err != nil {
log.Printf("ERROR: %v\n", err)
} else {
fmt.Println("✓ Capabilities retrieved successfully:")
if capabilities.Device != nil {
fmt.Printf(" - Device: %s\n", capabilities.Device.XAddr)
}
if capabilities.Media != nil {
fmt.Printf(" - Media: %s\n", capabilities.Media.XAddr)
}
if capabilities.PTZ != nil {
fmt.Printf(" - PTZ: %s\n", capabilities.PTZ.XAddr)
}
if capabilities.Imaging != nil {
fmt.Printf(" - Imaging: %s\n", capabilities.Imaging.XAddr)
}
if capabilities.Events != nil {
fmt.Printf(" - Events: %s\n", capabilities.Events.XAddr)
}
if capabilities.Analytics != nil {
fmt.Printf(" - Analytics: %s\n", capabilities.Analytics.XAddr)
}
}
fmt.Println()
// Initialize client to discover service endpoints
fmt.Println("=== Test 4: Initialize (Discover Services) ===")
if err := client.Initialize(ctx); err != nil {
log.Printf("ERROR: %v\n", err)
} else {
fmt.Println("✓ Services discovered successfully")
}
fmt.Println()
// Test 5: Get Media Profiles
fmt.Println("=== Test 5: GetProfiles ===")
profiles, err := client.GetProfiles(ctx)
if err != nil {
log.Printf("ERROR: %v\n", err)
} else {
fmt.Printf("✓ Found %d profile(s)\n", len(profiles))
for i, profile := range profiles {
fmt.Printf(" Profile %d: %s (Token: %s)\n", i+1, profile.Name, profile.Token)
if profile.VideoEncoderConfiguration != nil {
fmt.Printf(" - Encoding: %s\n", profile.VideoEncoderConfiguration.Encoding)
if profile.VideoEncoderConfiguration.Resolution != nil {
fmt.Printf(" - Resolution: %dx%d\n",
profile.VideoEncoderConfiguration.Resolution.Width,
profile.VideoEncoderConfiguration.Resolution.Height)
}
}
}
}
fmt.Println()
// Test 6: Get Stream URIs
fmt.Println("=== Test 6: GetStreamURI (for first profile) ===")
if len(profiles) > 0 {
streamURI, err := client.GetStreamURI(ctx, profiles[0].Token)
if err != nil {
log.Printf("ERROR: %v\n", err)
} else {
fmt.Printf("✓ Stream URI: %s\n", streamURI.URI)
fmt.Printf(" - Invalid After Connect: %v\n", streamURI.InvalidAfterConnect)
fmt.Printf(" - Invalid After Reboot: %v\n", streamURI.InvalidAfterReboot)
}
}
fmt.Println()
// Test 7: Get Snapshot URI
fmt.Println("=== Test 7: GetSnapshotURI (for first profile) ===")
if len(profiles) > 0 {
snapshotURI, err := client.GetSnapshotURI(ctx, profiles[0].Token)
if err != nil {
log.Printf("ERROR: %v\n", err)
} else {
fmt.Printf("✓ Snapshot URI: %s\n", snapshotURI.URI)
}
}
fmt.Println()
// Test 8: Get Video Encoder Configuration
fmt.Println("=== Test 8: GetVideoEncoderConfiguration ===")
if len(profiles) > 0 && profiles[0].VideoEncoderConfiguration != nil {
config, err := client.GetVideoEncoderConfiguration(ctx, profiles[0].VideoEncoderConfiguration.Token)
if err != nil {
log.Printf("ERROR: %v\n", err)
} else {
fmt.Printf("✓ Video Encoder Configuration:\n")
fmt.Printf(" - Name: %s\n", config.Name)
fmt.Printf(" - Encoding: %s\n", config.Encoding)
if config.Resolution != nil {
fmt.Printf(" - Resolution: %dx%d\n", config.Resolution.Width, config.Resolution.Height)
}
fmt.Printf(" - Quality: %.1f\n", config.Quality)
if config.RateControl != nil {
fmt.Printf(" - Frame Rate Limit: %d\n", config.RateControl.FrameRateLimit)
fmt.Printf(" - Bitrate Limit: %d\n", config.RateControl.BitrateLimit)
}
}
}
fmt.Println()
// Test 9: PTZ Operations (if PTZ is available)
fmt.Println("=== Test 9: PTZ Operations ===")
if len(profiles) > 0 && profiles[0].PTZConfiguration != nil {
fmt.Println("PTZ configuration detected, testing PTZ operations...")
// Get PTZ Status
ptzStatus, err := client.GetStatus(ctx, profiles[0].Token)
if err != nil {
log.Printf("ERROR getting PTZ status: %v\n", err)
} else {
fmt.Printf("✓ PTZ Status retrieved\n")
if ptzStatus.Position != nil {
if ptzStatus.Position.PanTilt != nil {
fmt.Printf(" - Pan/Tilt Position: X=%.2f, Y=%.2f\n",
ptzStatus.Position.PanTilt.X,
ptzStatus.Position.PanTilt.Y)
}
if ptzStatus.Position.Zoom != nil {
fmt.Printf(" - Zoom Position: %.2f\n", ptzStatus.Position.Zoom.X)
}
}
if ptzStatus.MoveStatus != nil {
fmt.Printf(" - Pan/Tilt Move Status: %s\n", ptzStatus.MoveStatus.PanTilt)
fmt.Printf(" - Zoom Move Status: %s\n", ptzStatus.MoveStatus.Zoom)
}
}
// Get PTZ Presets
presets, err := client.GetPresets(ctx, profiles[0].Token)
if err != nil {
log.Printf("ERROR getting PTZ presets: %v\n", err)
} else {
fmt.Printf("✓ Found %d PTZ preset(s)\n", len(presets))
for i, preset := range presets {
fmt.Printf(" Preset %d: %s (Token: %s)\n", i+1, preset.Name, preset.Token)
}
}
} else {
fmt.Println("⊘ No PTZ configuration found for this profile")
}
fmt.Println()
// Test 10: Imaging Settings
fmt.Println("=== Test 10: Imaging Settings ===")
if len(profiles) > 0 && profiles[0].VideoSourceConfiguration != nil {
settings, err := client.GetImagingSettings(ctx, profiles[0].VideoSourceConfiguration.SourceToken)
if err != nil {
log.Printf("ERROR: %v\n", err)
} else {
fmt.Printf("✓ Imaging Settings:\n")
if settings.Brightness != nil {
fmt.Printf(" - Brightness: %.1f\n", *settings.Brightness)
}
if settings.ColorSaturation != nil {
fmt.Printf(" - Color Saturation: %.1f\n", *settings.ColorSaturation)
}
if settings.Contrast != nil {
fmt.Printf(" - Contrast: %.1f\n", *settings.Contrast)
}
if settings.Sharpness != nil {
fmt.Printf(" - Sharpness: %.1f\n", *settings.Sharpness)
}
if settings.IrCutFilter != nil {
fmt.Printf(" - IR Cut Filter: %s\n", *settings.IrCutFilter)
}
if settings.BacklightCompensation != nil {
fmt.Printf(" - Backlight Compensation: %s (Level: %.1f)\n",
settings.BacklightCompensation.Mode,
settings.BacklightCompensation.Level)
}
if settings.Exposure != nil {
fmt.Printf(" - Exposure Mode: %s\n", settings.Exposure.Mode)
fmt.Printf(" Priority: %s\n", settings.Exposure.Priority)
}
if settings.Focus != nil {
fmt.Printf(" - Focus Mode: %s\n", settings.Focus.AutoFocusMode)
}
if settings.WhiteBalance != nil {
fmt.Printf(" - White Balance Mode: %s\n", settings.WhiteBalance.Mode)
}
if settings.WideDynamicRange != nil {
fmt.Printf(" - Wide Dynamic Range: %s (Level: %.1f)\n",
settings.WideDynamicRange.Mode,
settings.WideDynamicRange.Level)
}
}
}
fmt.Println()
fmt.Println("=== Test Summary ===")
fmt.Println("All tests completed!")
}
+152
View File
@@ -0,0 +1,152 @@
package main
import (
"bytes"
"context"
"crypto/rand"
"crypto/sha1"
"encoding/base64"
"encoding/xml"
"fmt"
"io"
"log"
"net/http"
"time"
)
// SOAP Envelope structures
type Envelope struct {
XMLName xml.Name `xml:"http://www.w3.org/2003/05/soap-envelope Envelope"`
Header *Header `xml:"http://www.w3.org/2003/05/soap-envelope Header,omitempty"`
Body Body `xml:"http://www.w3.org/2003/05/soap-envelope Body"`
}
type Header struct {
Security *Security `xml:"Security,omitempty"`
}
type Body struct {
Content interface{} `xml:",omitempty"`
}
type Security struct {
XMLName xml.Name `xml:"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd Security"`
MustUnderstand string `xml:"http://www.w3.org/2003/05/soap-envelope mustUnderstand,attr,omitempty"`
UsernameToken *UsernameToken `xml:"UsernameToken,omitempty"`
}
type UsernameToken struct {
XMLName xml.Name `xml:"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd UsernameToken"`
Username string `xml:"Username"`
Password Password `xml:"Password"`
Nonce Nonce `xml:"Nonce"`
Created string `xml:"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd Created"`
}
type Password struct {
Type string `xml:"Type,attr"`
Password string `xml:",chardata"`
}
type Nonce struct {
Type string `xml:"EncodingType,attr"`
Nonce string `xml:",chardata"`
}
type GetDeviceInformation struct {
XMLName xml.Name `xml:"tds:GetDeviceInformation"`
Xmlns string `xml:"xmlns:tds,attr"`
}
func createSecurityHeader(username, password string) *Security {
nonceBytes := make([]byte, 16)
rand.Read(nonceBytes)
nonce := base64.StdEncoding.EncodeToString(nonceBytes)
created := time.Now().UTC().Format(time.RFC3339)
hash := sha1.New()
hash.Write(nonceBytes)
hash.Write([]byte(created))
hash.Write([]byte(password))
digest := base64.StdEncoding.EncodeToString(hash.Sum(nil))
return &Security{
MustUnderstand: "1",
UsernameToken: &UsernameToken{
Username: username,
Password: Password{
Type: "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordDigest",
Password: digest,
},
Nonce: Nonce{
Type: "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary",
Nonce: nonce,
},
Created: created,
},
}
}
func main() {
endpoint := "http://192.168.1.201/onvif/device_service"
username := "service"
password := "Service.1234"
fmt.Println("Testing direct SOAP request to camera...")
// Build request
req := GetDeviceInformation{
Xmlns: "http://www.onvif.org/ver10/device/wsdl",
}
envelope := &Envelope{
Header: &Header{
Security: createSecurityHeader(username, password),
},
Body: Body{
Content: req,
},
}
// Marshal to XML
body, err := xml.MarshalIndent(envelope, "", " ")
if err != nil {
log.Fatalf("Failed to marshal: %v", err)
}
xmlBody := append([]byte(xml.Header), body...)
fmt.Println("\n=== Request XML ===")
fmt.Println(string(xmlBody))
// Create HTTP request
httpReq, err := http.NewRequestWithContext(context.Background(), "POST", endpoint, bytes.NewReader(xmlBody))
if err != nil {
log.Fatalf("Failed to create request: %v", err)
}
httpReq.Header.Set("Content-Type", "application/soap+xml; charset=utf-8")
// Send request
client := &http.Client{Timeout: 30 * time.Second}
resp, err := client.Do(httpReq)
if err != nil {
log.Fatalf("Failed to send request: %v", err)
}
defer resp.Body.Close()
// Read response
respBody, err := io.ReadAll(resp.Body)
if err != nil {
log.Fatalf("Failed to read response: %v", err)
}
fmt.Printf("\n=== HTTP Status: %d ===\n", resp.StatusCode)
fmt.Printf("\n=== Response Headers ===\n")
for k, v := range resp.Header {
fmt.Printf("%s: %v\n", k, v)
}
fmt.Printf("\n=== Response Body ===\n")
fmt.Println(string(respBody))
}
+162
View File
@@ -0,0 +1,162 @@
package main
import (
"bytes"
"context"
"crypto/rand"
"crypto/sha1"
"encoding/base64"
"encoding/xml"
"fmt"
"io"
"log"
"net/http"
"time"
)
// SOAP Envelope structures
type Envelope struct {
XMLName xml.Name `xml:"http://www.w3.org/2003/05/soap-envelope Envelope"`
Header *Header `xml:"http://www.w3.org/2003/05/soap-envelope Header,omitempty"`
Body Body `xml:"http://www.w3.org/2003/05/soap-envelope Body"`
}
type Header struct {
Security *Security `xml:"Security,omitempty"`
}
type Body struct {
Content interface{} `xml:",omitempty"`
}
type Security struct {
XMLName xml.Name `xml:"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd Security"`
MustUnderstand string `xml:"http://www.w3.org/2003/05/soap-envelope mustUnderstand,attr,omitempty"`
UsernameToken *UsernameToken `xml:"UsernameToken,omitempty"`
}
type UsernameToken struct {
XMLName xml.Name `xml:"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd UsernameToken"`
Username string `xml:"Username"`
Password Password `xml:"Password"`
Nonce Nonce `xml:"Nonce"`
Created string `xml:"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd Created"`
}
type Password struct {
Type string `xml:"Type,attr"`
Password string `xml:",chardata"`
}
type Nonce struct {
Type string `xml:"EncodingType,attr"`
Nonce string `xml:",chardata"`
}
type GetStreamUri struct {
XMLName xml.Name `xml:"trt:GetStreamUri"`
Xmlns string `xml:"xmlns:trt,attr"`
Xmlnst string `xml:"xmlns:tt,attr"`
StreamSetup struct {
Stream string `xml:"tt:Stream"`
Transport struct {
Protocol string `xml:"tt:Protocol"`
} `xml:"tt:Transport"`
} `xml:"trt:StreamSetup"`
ProfileToken string `xml:"trt:ProfileToken"`
}
func createSecurityHeader(username, password string) *Security {
nonceBytes := make([]byte, 16)
rand.Read(nonceBytes)
nonce := base64.StdEncoding.EncodeToString(nonceBytes)
created := time.Now().UTC().Format(time.RFC3339)
hash := sha1.New()
hash.Write(nonceBytes)
hash.Write([]byte(created))
hash.Write([]byte(password))
digest := base64.StdEncoding.EncodeToString(hash.Sum(nil))
return &Security{
MustUnderstand: "1",
UsernameToken: &UsernameToken{
Username: username,
Password: Password{
Type: "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordDigest",
Password: digest,
},
Nonce: Nonce{
Type: "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary",
Nonce: nonce,
},
Created: created,
},
}
}
func main() {
// Using the media service endpoint
endpoint := "http://192.168.1.201/onvif/media_service"
username := "service"
password := "Service.1234"
profileToken := "0"
fmt.Println("Testing GetStreamUri SOAP request...")
// Build request
req := GetStreamUri{
Xmlns: "http://www.onvif.org/ver10/media/wsdl",
Xmlnst: "http://www.onvif.org/ver10/schema",
ProfileToken: profileToken,
}
req.StreamSetup.Stream = "RTP-Unicast"
req.StreamSetup.Transport.Protocol = "RTSP"
envelope := &Envelope{
Header: &Header{
Security: createSecurityHeader(username, password),
},
Body: Body{
Content: req,
},
}
// Marshal to XML
body, err := xml.MarshalIndent(envelope, "", " ")
if err != nil {
log.Fatalf("Failed to marshal: %v", err)
}
xmlBody := append([]byte(xml.Header), body...)
fmt.Println("\n=== Request XML ===")
fmt.Println(string(xmlBody))
// Create HTTP request
httpReq, err := http.NewRequestWithContext(context.Background(), "POST", endpoint, bytes.NewReader(xmlBody))
if err != nil {
log.Fatalf("Failed to create request: %v", err)
}
httpReq.Header.Set("Content-Type", "application/soap+xml; charset=utf-8")
// Send request
client := &http.Client{Timeout: 30 * time.Second}
resp, err := client.Do(httpReq)
if err != nil {
log.Fatalf("Failed to send request: %v", err)
}
defer resp.Body.Close()
// Read response
respBody, err := io.ReadAll(resp.Body)
if err != nil {
log.Fatalf("Failed to read response: %v", err)
}
fmt.Printf("\n=== HTTP Status: %d ===\n", resp.StatusCode)
fmt.Printf("\n=== Response Body ===\n")
fmt.Println(string(respBody))
}
+255
View File
@@ -0,0 +1,255 @@
package main
import (
"context"
"fmt"
"log"
"time"
"github.com/0x524A/go-onvif"
"github.com/0x524A/go-onvif/discovery"
)
func main() {
fmt.Println("🔍 Discovering ONVIF cameras on the network...")
fmt.Println("This may take a few seconds...")
fmt.Println()
ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second)
defer cancel()
devices, err := discovery.Discover(ctx, 10*time.Second)
if err != nil {
log.Fatalf("❌ Discovery failed: %v", err)
}
if len(devices) == 0 {
fmt.Println("❌ No ONVIF cameras found on the network")
fmt.Println("💡 Make sure:")
fmt.Println(" - Camera is powered on and connected to the network")
fmt.Println(" - ONVIF is enabled on the camera")
fmt.Println(" - You're on the same network segment as the camera")
fmt.Println(" - Camera IP 192.168.1.201 is reachable (try: ping 192.168.1.201)")
return
}
fmt.Printf("✅ Found %d camera(s):\n\n", len(devices))
var targetDevice *discovery.Device
for i, device := range devices {
fmt.Printf("📹 Camera #%d:\n", i+1)
fmt.Printf(" Endpoint: %s\n", device.GetDeviceEndpoint())
fmt.Printf(" Name: %s\n", device.GetName())
fmt.Printf(" Location: %s\n", device.GetLocation())
fmt.Printf(" Types: %v\n", device.Types)
fmt.Printf(" XAddrs: %v\n", device.XAddrs)
fmt.Println()
// Check if this is our target camera (192.168.1.201)
endpoint := device.GetDeviceEndpoint()
if len(endpoint) > 7 {
// Simple check if endpoint contains the IP
if len(endpoint) > 20 && (endpoint[7:20] == "192.168.1.201" || endpoint[7:21] == "192.168.1.201:") {
targetDevice = device
}
}
}
if targetDevice == nil {
fmt.Println("⚠️ Camera at 192.168.1.201 was not discovered")
fmt.Println("💡 You can still try to connect manually with the correct endpoint")
return
}
// Now try to connect to the discovered camera
fmt.Printf("\n🎯 Found target camera at 192.168.1.201\n")
fmt.Printf("Endpoint: %s\n", targetDevice.GetDeviceEndpoint())
fmt.Println()
// Test connection with credentials
username := "service"
password := "Service.1234"
fmt.Println("📡 Connecting with credentials...")
client, err := onvif.NewClient(
targetDevice.GetDeviceEndpoint(),
onvif.WithCredentials(username, password),
onvif.WithTimeout(30*time.Second),
)
if err != nil {
log.Fatalf("❌ Failed to create client: %v", err)
}
ctx2 := context.Background()
// Get device information
fmt.Println("🔍 Retrieving device information...")
info, err := client.GetDeviceInformation(ctx2)
if err != nil {
log.Fatalf("❌ Failed to get device information: %v\n\n💡 Possible issues:\n - Wrong username or password\n - Camera requires different authentication\n - Try username/password combinations like: admin/admin, admin/12345, etc.\n", err)
}
fmt.Printf("\n✅ Device Information:\n")
fmt.Printf(" Manufacturer: %s\n", info.Manufacturer)
fmt.Printf(" Model: %s\n", info.Model)
fmt.Printf(" Firmware: %s\n", info.FirmwareVersion)
fmt.Printf(" Serial Number: %s\n", info.SerialNumber)
fmt.Printf(" Hardware ID: %s\n", info.HardwareID)
// Initialize client (discover service endpoints)
fmt.Println("\n🔧 Initializing client and discovering services...")
if err := client.Initialize(ctx2); err != nil {
log.Fatalf("❌ Failed to initialize client: %v", err)
}
fmt.Println("✅ Services discovered successfully")
// Get capabilities
fmt.Println("\n🎯 Getting device capabilities...")
caps, err := client.GetCapabilities(ctx2)
if err != nil {
log.Printf("⚠️ Failed to get capabilities: %v", err)
} else {
fmt.Println("✅ Supported Services:")
if caps.Device != nil {
fmt.Println(" ✓ Device Service")
}
if caps.Media != nil {
fmt.Println(" ✓ Media Service (Streaming)")
}
if caps.PTZ != nil {
fmt.Println(" ✓ PTZ Service (Pan/Tilt/Zoom)")
}
if caps.Imaging != nil {
fmt.Println(" ✓ Imaging Service")
}
if caps.Events != nil {
fmt.Println(" ✓ Event Service")
}
if caps.Analytics != nil {
fmt.Println(" ✓ Analytics Service")
}
}
// Get media profiles
fmt.Println("\n📹 Retrieving media profiles...")
profiles, err := client.GetProfiles(ctx2)
if err != nil {
log.Fatalf("❌ Failed to get profiles: %v", err)
}
fmt.Printf("\n✅ Found %d profile(s):\n", len(profiles))
for i, profile := range profiles {
fmt.Printf("\n📺 Profile #%d:\n", i+1)
fmt.Printf(" Token: %s\n", profile.Token)
fmt.Printf(" Name: %s\n", profile.Name)
if profile.VideoEncoderConfiguration != nil {
fmt.Printf(" Encoding: %s\n", profile.VideoEncoderConfiguration.Encoding)
if profile.VideoEncoderConfiguration.Resolution != nil {
fmt.Printf(" Resolution: %dx%d\n",
profile.VideoEncoderConfiguration.Resolution.Width,
profile.VideoEncoderConfiguration.Resolution.Height)
}
fmt.Printf(" Quality: %.1f\n", profile.VideoEncoderConfiguration.Quality)
if profile.VideoEncoderConfiguration.RateControl != nil {
fmt.Printf(" Frame Rate: %d fps\n", profile.VideoEncoderConfiguration.RateControl.FrameRateLimit)
fmt.Printf(" Bitrate: %d kbps\n", profile.VideoEncoderConfiguration.RateControl.BitrateLimit)
}
}
if profile.PTZConfiguration != nil {
fmt.Printf(" PTZ: Enabled\n")
}
// Get stream URI
streamURI, err := client.GetStreamURI(ctx2, profile.Token)
if err != nil {
fmt.Printf(" Stream URI: ❌ Error - %v\n", err)
} else {
fmt.Printf(" Stream URI: %s\n", streamURI.URI)
fmt.Printf(" 📱 Use this URL in VLC or other RTSP player\n")
}
// Get snapshot URI
snapshotURI, err := client.GetSnapshotURI(ctx2, profile.Token)
if err != nil {
fmt.Printf(" Snapshot URI: ❌ Error - %v\n", err)
} else {
fmt.Printf(" Snapshot URI: %s\n", snapshotURI.URI)
fmt.Printf(" 🌐 You can open this URL in a browser\n")
}
}
// Test PTZ if available
if len(profiles) > 0 {
fmt.Println("\n🎮 Testing PTZ capabilities...")
profileToken := profiles[0].Token
status, err := client.GetStatus(ctx2, profileToken)
if err != nil {
fmt.Printf("⚠️ PTZ not supported or error: %v\n", err)
} else {
fmt.Println("✅ PTZ is supported!")
if status.Position != nil && status.Position.PanTilt != nil {
fmt.Printf(" Current Position: Pan=%.3f, Tilt=%.3f\n",
status.Position.PanTilt.X,
status.Position.PanTilt.Y)
}
if status.Position != nil && status.Position.Zoom != nil {
fmt.Printf(" Current Zoom: %.3f\n", status.Position.Zoom.X)
}
// Get presets
presets, err := client.GetPresets(ctx2, profileToken)
if err != nil {
fmt.Printf(" Presets: ❌ Error - %v\n", err)
} else {
fmt.Printf(" Available Presets: %d\n", len(presets))
for _, preset := range presets {
fmt.Printf(" - %s (Token: %s)\n", preset.Name, preset.Token)
}
}
}
}
// Test Imaging if available
if len(profiles) > 0 && profiles[0].VideoSourceConfiguration != nil {
fmt.Println("\n🎨 Testing Imaging capabilities...")
videoSourceToken := profiles[0].VideoSourceConfiguration.SourceToken
settings, err := client.GetImagingSettings(ctx2, videoSourceToken)
if err != nil {
fmt.Printf("⚠️ Imaging settings not available: %v\n", err)
} else {
fmt.Println("✅ Current Imaging Settings:")
if settings.Brightness != nil {
fmt.Printf(" Brightness: %.1f\n", *settings.Brightness)
}
if settings.Contrast != nil {
fmt.Printf(" Contrast: %.1f\n", *settings.Contrast)
}
if settings.ColorSaturation != nil {
fmt.Printf(" Saturation: %.1f\n", *settings.ColorSaturation)
}
if settings.Sharpness != nil {
fmt.Printf(" Sharpness: %.1f\n", *settings.Sharpness)
}
if settings.Exposure != nil {
fmt.Printf(" Exposure Mode: %s\n", settings.Exposure.Mode)
}
if settings.Focus != nil {
fmt.Printf(" Focus Mode: %s\n", settings.Focus.AutoFocusMode)
}
if settings.WhiteBalance != nil {
fmt.Printf(" White Balance: %s\n", settings.WhiteBalance.Mode)
}
}
}
fmt.Println("\n✅ All tests completed successfully!")
fmt.Println("\n💡 Next steps:")
fmt.Println(" - Use the stream URI in VLC to view the live feed")
fmt.Println(" - Open the snapshot URI in a browser to see still images")
fmt.Println(" - Use the PTZ controls to move the camera (if supported)")
fmt.Println(" - Adjust imaging settings for better image quality")
}
+39
View File
@@ -0,0 +1,39 @@
package main
import (
"context"
"fmt"
"log"
"time"
"github.com/0x524A/go-onvif/discovery"
)
func main() {
fmt.Println("Discovering ONVIF cameras on the network...")
ctx := context.Background()
devices, err := discovery.Discover(ctx, 10*time.Second)
if err != nil {
log.Fatalf("Discovery failed: %v", err)
}
if len(devices) == 0 {
fmt.Println("No ONVIF devices found")
return
}
fmt.Printf("\nFound %d device(s):\n\n", len(devices))
for i, device := range devices {
fmt.Printf("Device #%d:\n", i+1)
fmt.Printf(" Endpoint Ref: %s\n", device.EndpointRef)
fmt.Printf(" XAddrs: %v\n", device.XAddrs)
fmt.Printf(" Device Endpoint: %s\n", device.GetDeviceEndpoint())
fmt.Printf(" Name: %s\n", device.GetName())
fmt.Printf(" Location: %s\n", device.GetLocation())
fmt.Printf(" Types: %v\n", device.Types)
fmt.Printf(" Scopes: %v\n", device.Scopes)
fmt.Println()
}
}
+100
View File
@@ -0,0 +1,100 @@
package main
import (
"bytes"
"fmt"
"io"
"log"
"net/http"
"time"
)
func main() {
// Test SOAP request manually
endpoint := "http://192.168.1.201/onvif/device_service"
username := "service"
password := "Service.1234"
fmt.Println("🔧 Manual SOAP Test for ONVIF Camera")
fmt.Println("=====================================")
fmt.Printf("Endpoint: %s\n", endpoint)
fmt.Printf("Username: %s\n", username)
fmt.Println()
// Simple GetDeviceInformation SOAP request (without auth for now)
soapRequest := `<?xml version="1.0" encoding="UTF-8"?>
<soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope"
xmlns:tds="http://www.onvif.org/ver10/device/wsdl">
<soap:Body>
<tds:GetDeviceInformation/>
</soap:Body>
</soap:Envelope>`
fmt.Println("📤 Sending SOAP request (without authentication)...")
fmt.Println()
req, err := http.NewRequest("POST", endpoint, bytes.NewBufferString(soapRequest))
if err != nil {
log.Fatalf("Failed to create request: %v", err)
}
req.Header.Set("Content-Type", "application/soap+xml; charset=utf-8")
client := &http.Client{
Timeout: 10 * time.Second,
}
resp, err := client.Do(req)
if err != nil {
log.Fatalf("❌ Failed to send request: %v", err)
}
defer resp.Body.Close()
fmt.Printf("📥 Response Status: %s\n", resp.Status)
fmt.Println("📋 Response Headers:")
for key, values := range resp.Header {
for _, value := range values {
fmt.Printf(" %s: %s\n", key, value)
}
}
fmt.Println()
body, err := io.ReadAll(resp.Body)
if err != nil {
log.Fatalf("Failed to read response: %v", err)
}
fmt.Println("📄 Response Body:")
fmt.Println(string(body))
fmt.Println()
if resp.StatusCode != 200 {
fmt.Printf("⚠️ Non-200 status code: %d\n", resp.StatusCode)
if resp.StatusCode == 401 {
fmt.Println("💡 Authentication required - this is expected!")
fmt.Println("💡 Now testing with go-onvif client library...")
fmt.Println()
testWithClient(username, password)
} else {
fmt.Println("💡 Unexpected status code. Check:")
fmt.Println(" - Is ONVIF enabled on the camera?")
fmt.Println(" - Is the endpoint path correct?")
}
} else {
fmt.Println("✅ Got successful response!")
}
}
func testWithClient(username, password string) {
// Import locally to avoid conflicts
onvif := struct{}{}
_ = onvif
fmt.Println("Note: Would test with go-onvif client here, but keeping this simple.")
fmt.Println("The camera appears to be responding to ONVIF requests.")
fmt.Println()
fmt.Println("💡 Next step: Check if the credentials are correct")
fmt.Printf(" Username: %s\n", username)
fmt.Printf(" Password: %s\n", password)
}
+444
View File
@@ -0,0 +1,444 @@
package main
import (
"context"
"encoding/json"
"flag"
"fmt"
"log"
"os"
"time"
"github.com/0x524A/go-onvif"
)
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)
}
}
+93
View File
@@ -0,0 +1,93 @@
package main
import (
"context"
"fmt"
"log"
"time"
"github.com/0x524A/go-onvif"
)
func main() {
// Camera connection details
endpoint := "http://192.168.1.201/onvif/device_service"
username := "service"
password := "Service.1234"
fmt.Println("Connecting to ONVIF camera at 192.168.1.201...")
// Create a new ONVIF 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()
// Get device information
fmt.Println("\nRetrieving device information...")
info, err := client.GetDeviceInformation(ctx)
if err != nil {
log.Fatalf("Failed to get device information: %v", err)
}
fmt.Printf("\nDevice Information:\n")
fmt.Printf(" Manufacturer: %s\n", info.Manufacturer)
fmt.Printf(" Model: %s\n", info.Model)
fmt.Printf(" Firmware: %s\n", info.FirmwareVersion)
fmt.Printf(" Serial Number: %s\n", info.SerialNumber)
fmt.Printf(" Hardware ID: %s\n", info.HardwareID)
// Initialize client (discover service endpoints)
fmt.Println("\nInitializing client and discovering services...")
if err := client.Initialize(ctx); err != nil {
log.Fatalf("Failed to initialize client: %v", err)
}
// Get media profiles
fmt.Println("\nRetrieving media profiles...")
profiles, err := client.GetProfiles(ctx)
if err != nil {
log.Fatalf("Failed to get profiles: %v", err)
}
fmt.Printf("\nFound %d profile(s):\n", len(profiles))
for i, profile := range profiles {
fmt.Printf("\nProfile #%d:\n", i+1)
fmt.Printf(" Token: %s\n", profile.Token)
fmt.Printf(" Name: %s\n", profile.Name)
if profile.VideoEncoderConfiguration != nil {
fmt.Printf(" Video Encoding: %s\n", profile.VideoEncoderConfiguration.Encoding)
if profile.VideoEncoderConfiguration.Resolution != nil {
fmt.Printf(" Resolution: %dx%d\n",
profile.VideoEncoderConfiguration.Resolution.Width,
profile.VideoEncoderConfiguration.Resolution.Height)
}
fmt.Printf(" Quality: %.1f\n", profile.VideoEncoderConfiguration.Quality)
}
// Get stream URI
streamURI, err := client.GetStreamURI(ctx, profile.Token)
if err != nil {
fmt.Printf(" Stream URI: Error - %v\n", err)
} else {
fmt.Printf(" Stream URI: %s\n", streamURI.URI)
}
// Get snapshot URI
snapshotURI, err := client.GetSnapshotURI(ctx, profile.Token)
if err != nil {
fmt.Printf(" Snapshot URI: Error - %v\n", err)
} else {
fmt.Printf(" Snapshot URI: %s\n", snapshotURI.URI)
}
}
fmt.Println("\nDone!")
}