Add or update .codecov copy.yml
This commit is contained in:
@@ -0,0 +1,158 @@
|
||||
# Test Data for ONVIF Camera Testing
|
||||
|
||||
This directory contains discovered camera data for testing the onvif-go library.
|
||||
|
||||
## Files
|
||||
|
||||
### discovered_cameras_20260113.json
|
||||
JSON file containing structured data for all 8 cameras discovered on the network:
|
||||
- Complete endpoint information
|
||||
- XAddrs (service URLs)
|
||||
- Manufacturer and model details
|
||||
- Supported ONVIF profiles
|
||||
- Network configuration (IP, port)
|
||||
- HTTPS support status
|
||||
|
||||
### test_cameras_config.go
|
||||
Go package providing programmatic access to test camera data:
|
||||
- `TestCameras` slice with all discovered cameras
|
||||
- `GetCameraByManufacturer()` - filter by manufacturer
|
||||
- `GetCameraByProfile()` - filter by ONVIF profile support
|
||||
- `GetHTTPSCameras()` - get cameras with HTTPS support
|
||||
|
||||
## Discovery Summary (2026-01-13)
|
||||
|
||||
**Total Cameras Found:** 8
|
||||
|
||||
### By Manufacturer:
|
||||
- **AXIS:** 3 cameras (P3818-PVE, Q3819-PVE, P5655-E)
|
||||
- **Bosch:** 3 cameras (AUTODOME IP starlight 5000i, FLEXIDOME IP starlight 8000i, FLEXIDOME panoramic 5100i)
|
||||
- **Reolink:** 2 cameras (E1Zoom, ReolinkTrackMixWiFi)
|
||||
|
||||
### By ONVIF Profile Support:
|
||||
- **Profile Streaming:** 8/8 (100%)
|
||||
- **Profile T (Streaming):** 8/8 (100%)
|
||||
- **Profile G (Recording):** 6/8 (75%)
|
||||
- **Profile M (Metadata):** 4/8 (50%)
|
||||
|
||||
### Network Configuration:
|
||||
- Network: 192.168.2.0/24
|
||||
- HTTPS Support: 6/8 cameras
|
||||
- Port 80: 6 cameras
|
||||
- Port 8000: 2 cameras (Reolink)
|
||||
|
||||
## Usage in Tests
|
||||
|
||||
### Example 1: Using JSON Data
|
||||
```go
|
||||
import (
|
||||
"encoding/json"
|
||||
"os"
|
||||
)
|
||||
|
||||
type CameraData struct {
|
||||
Cameras []struct {
|
||||
IP string `json:"ip"`
|
||||
XAddrs []string `json:"xaddrs"`
|
||||
Manufacturer string `json:"manufacturer"`
|
||||
Model string `json:"model"`
|
||||
} `json:"cameras"`
|
||||
}
|
||||
|
||||
func loadTestCameras() (*CameraData, error) {
|
||||
data, err := os.ReadFile("testdata/discovered_cameras_20260113.json")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var cameras CameraData
|
||||
err = json.Unmarshal(data, &cameras)
|
||||
return &cameras, err
|
||||
}
|
||||
```
|
||||
|
||||
### Example 2: Using Go Package
|
||||
```go
|
||||
import "github.com/yourusername/onvif-go/testdata"
|
||||
|
||||
func TestWithAxisCameras(t *testing.T) {
|
||||
axisCameras := testdata.GetCameraByManufacturer("AXIS")
|
||||
for _, cam := range axisCameras {
|
||||
t.Logf("Testing with %s %s at %s", cam.Manufacturer, cam.Model, cam.IP)
|
||||
// Run your tests...
|
||||
}
|
||||
}
|
||||
|
||||
func TestProfileM(t *testing.T) {
|
||||
metadataCameras := testdata.GetCameraByProfile("M")
|
||||
if len(metadataCameras) == 0 {
|
||||
t.Skip("No cameras with Profile M support")
|
||||
}
|
||||
// Test metadata operations...
|
||||
}
|
||||
|
||||
func TestHTTPS(t *testing.T) {
|
||||
httpsCameras := testdata.GetHTTPSCameras()
|
||||
for _, cam := range httpsCameras {
|
||||
// Test HTTPS connections...
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Camera Details
|
||||
|
||||
### High-End Cameras (Profile G + M)
|
||||
- AXIS P3818-PVE (192.168.2.82)
|
||||
- AXIS Q3819-PVE (192.168.2.190) - Dual network interfaces
|
||||
- AXIS P5655-E (192.168.2.30)
|
||||
- Bosch FLEXIDOME panoramic 5100i (192.168.2.24)
|
||||
|
||||
### Mid-Range Cameras (Profile G)
|
||||
- Bosch AUTODOME IP starlight 5000i (192.168.2.57)
|
||||
- Bosch FLEXIDOME IP starlight 8000i (192.168.2.200)
|
||||
|
||||
### Basic Cameras (Profile T only)
|
||||
- Reolink E1Zoom (192.168.2.61:8000)
|
||||
- Reolink ReolinkTrackMixWiFi (192.168.2.236:8000)
|
||||
|
||||
## Notes
|
||||
|
||||
1. **Credentials Required:** These endpoints require authentication. Set test credentials using environment variables:
|
||||
```bash
|
||||
export ONVIF_TEST_USERNAME="your_username"
|
||||
export ONVIF_TEST_PASSWORD="your_password"
|
||||
```
|
||||
|
||||
2. **Network Access:** Tests require access to the 192.168.2.0/24 network.
|
||||
|
||||
3. **Camera Availability:** Ensure cameras are powered on and network-accessible before running tests.
|
||||
|
||||
4. **HTTPS Certificates:** AXIS and Bosch cameras use self-signed certificates. Tests may need to skip certificate verification:
|
||||
```go
|
||||
client.HTTPClient = &http.Client{
|
||||
Transport: &http.Transport{
|
||||
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
5. **Rate Limiting:** Some cameras may rate-limit requests. Add delays between test runs if needed.
|
||||
|
||||
## Updating Test Data
|
||||
|
||||
To refresh the discovered camera data:
|
||||
|
||||
```bash
|
||||
# Run discovery and save output
|
||||
./bin/discover 2>&1 | tee camera-discovery-$(date +%Y%m%d-%H%M%S).log
|
||||
|
||||
# Discovery will run for ~10 seconds
|
||||
# Press Ctrl+C to stop when cameras are found
|
||||
|
||||
# Update JSON and Go files with new data as needed
|
||||
```
|
||||
|
||||
## See Also
|
||||
|
||||
- [Main Testing Documentation](../docs/testing/)
|
||||
- [Camera Test Reports](../CAMERA_TEST_REPORT.md)
|
||||
- [Quick Start Guide](../docs/QUICKSTART.md)
|
||||
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
@@ -0,0 +1,298 @@
|
||||
# Camera Test Framework
|
||||
|
||||
This directory contains camera-specific tests generated from real camera XML captures. These tests ensure the ONVIF client works correctly with various camera models and prevents regressions when making changes.
|
||||
|
||||
## Overview
|
||||
|
||||
The test framework consists of:
|
||||
|
||||
1. **Captured XML Archives** (`*.tar.gz`) - Real SOAP XML request/response pairs from cameras
|
||||
2. **Generated Tests** (`*_test.go`) - Automated tests that replay captures through a mock server
|
||||
3. **Test Generator** (`cmd/generate-tests`) - Tool to create tests from captures
|
||||
4. **Mock Server** (`testing/mock_server.go`) - HTTP server that replays captured responses
|
||||
|
||||
## Benefits
|
||||
|
||||
✅ **Test Without Hardware** - Run ONVIF tests without needing physical cameras
|
||||
✅ **Prevent Regressions** - Catch breaking changes before they affect real deployments
|
||||
✅ **Camera Coverage** - Test against multiple camera manufacturers and models
|
||||
✅ **Fast Feedback** - Tests complete in milliseconds vs. minutes with real cameras
|
||||
✅ **CI/CD Ready** - Automated tests that can run in continuous integration
|
||||
|
||||
## Running Tests
|
||||
|
||||
### Run All Camera Tests
|
||||
|
||||
```bash
|
||||
go test -v ./testdata/captures/
|
||||
```
|
||||
|
||||
### Run Specific Camera
|
||||
|
||||
```bash
|
||||
go test -v ./testdata/captures/ -run TestBosch
|
||||
```
|
||||
|
||||
### Run from Project Root
|
||||
|
||||
```bash
|
||||
go test -v ./...
|
||||
```
|
||||
|
||||
## Adding New Camera Tests
|
||||
|
||||
### 1. Capture Camera XML
|
||||
|
||||
First, capture SOAP XML from your camera:
|
||||
|
||||
```bash
|
||||
# Run diagnostic with XML capture
|
||||
./onvif-diagnostics \
|
||||
-endpoint "http://camera-ip/onvif/device_service" \
|
||||
-username "user" \
|
||||
-password "pass" \
|
||||
-capture-xml \
|
||||
-verbose
|
||||
```
|
||||
|
||||
This creates an archive like:
|
||||
```
|
||||
camera-logs/Manufacturer_Model_Firmware_xmlcapture_timestamp.tar.gz
|
||||
```
|
||||
|
||||
### 2. Copy to testdata/captures
|
||||
|
||||
```bash
|
||||
cp camera-logs/Manufacturer_Model_*_xmlcapture_*.tar.gz testdata/captures/
|
||||
```
|
||||
|
||||
### 3. Generate Test
|
||||
|
||||
```bash
|
||||
./generate-tests \
|
||||
-capture testdata/captures/Manufacturer_Model_*_xmlcapture_*.tar.gz \
|
||||
-output testdata/captures/
|
||||
```
|
||||
|
||||
This generates:
|
||||
```
|
||||
testdata/captures/manufacturer_model_firmware_test.go
|
||||
```
|
||||
|
||||
### 4. Run the Test
|
||||
|
||||
```bash
|
||||
go test -v ./testdata/captures/ -run TestManufacturerModel
|
||||
```
|
||||
|
||||
## Example Workflow
|
||||
|
||||
Complete example adding an AXIS camera:
|
||||
|
||||
```bash
|
||||
# 1. Capture from camera
|
||||
./onvif-diagnostics \
|
||||
-endpoint "http://192.168.1.100/onvif/device_service" \
|
||||
-username "root" \
|
||||
-password "pass" \
|
||||
-capture-xml
|
||||
|
||||
# Output: camera-logs/AXIS_Q3626-VE_12.6.104_xmlcapture_20251110-130000.tar.gz
|
||||
|
||||
# 2. Copy to testdata
|
||||
cp camera-logs/AXIS_Q3626-VE_12.6.104_xmlcapture_20251110-130000.tar.gz testdata/captures/
|
||||
|
||||
# 3. Generate test
|
||||
./generate-tests \
|
||||
-capture testdata/captures/AXIS_Q3626-VE_12.6.104_xmlcapture_20251110-130000.tar.gz \
|
||||
-output testdata/captures/
|
||||
|
||||
# Output: testdata/captures/axis_q3626-ve_12.6.104_test.go
|
||||
|
||||
# 4. Run test
|
||||
go test -v ./testdata/captures/ -run TestAXIS
|
||||
```
|
||||
|
||||
## Directory Structure
|
||||
|
||||
```
|
||||
testdata/captures/
|
||||
├── README.md # This file
|
||||
├── Bosch_FLEXIDOME_indoor_5100i_IR_8.71.0066_xmlcapture_*.tar.gz # Capture archive
|
||||
├── bosch_flexidome_indoor_5100i_ir_8.71.0066_test.go # Generated test
|
||||
├── AXIS_Q3626-VE_12.6.104_xmlcapture_*.tar.gz # Another camera
|
||||
└── axis_q3626-ve_12.6.104_test.go # Its test
|
||||
```
|
||||
|
||||
## How It Works
|
||||
|
||||
### Capture Archive Contents
|
||||
|
||||
Each `*.tar.gz` archive contains:
|
||||
|
||||
```
|
||||
capture_001.json # Request/response metadata
|
||||
capture_001_request.xml # SOAP request
|
||||
capture_001_response.xml # SOAP response
|
||||
capture_002.json
|
||||
capture_002_request.xml
|
||||
capture_002_response.xml
|
||||
...
|
||||
```
|
||||
|
||||
### Mock Server
|
||||
|
||||
The test framework includes a mock HTTP server that:
|
||||
|
||||
1. Loads all captured exchanges from the archive
|
||||
2. Extracts SOAP operation names from requests (GetDeviceInformation, GetProfiles, etc.)
|
||||
3. Matches incoming test requests to captured responses by operation name
|
||||
4. Returns the exact SOAP response the real camera sent
|
||||
|
||||
This allows the ONVIF client to interact with "virtual cameras" that behave exactly like the real ones.
|
||||
|
||||
### Generated Test
|
||||
|
||||
Each generated test:
|
||||
|
||||
1. Creates a mock server from the capture archive
|
||||
2. Creates an ONVIF client pointing to the mock server
|
||||
3. Runs common ONVIF operations (GetDeviceInformation, GetProfiles, etc.)
|
||||
4. Validates responses match expected values
|
||||
|
||||
## Customizing Tests
|
||||
|
||||
### Adding Custom Assertions
|
||||
|
||||
Edit the generated test file to add camera-specific validations:
|
||||
|
||||
```go
|
||||
t.Run("GetDeviceInformation", func(t *testing.T) {
|
||||
info, err := client.GetDeviceInformation(ctx)
|
||||
if err != nil {
|
||||
t.Errorf("GetDeviceInformation failed: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
// Add custom assertions
|
||||
if info.Manufacturer != "Bosch" {
|
||||
t.Errorf("Expected Bosch, got %s", info.Manufacturer)
|
||||
}
|
||||
if !strings.Contains(info.Model, "FLEXIDOME") {
|
||||
t.Errorf("Expected FLEXIDOME model, got %s", info.Model)
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
### Testing Specific Operations
|
||||
|
||||
Add tests for camera-specific features:
|
||||
|
||||
```go
|
||||
t.Run("PTZPresets", func(t *testing.T) {
|
||||
// Only for PTZ cameras
|
||||
presets, err := client.GetPresets(ctx, "profile_token")
|
||||
if err != nil {
|
||||
t.Errorf("GetPresets failed: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
if len(presets) == 0 {
|
||||
t.Error("Expected at least one preset")
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Test Fails: "No matching capture found"
|
||||
|
||||
The mock server couldn't find a captured response for the operation.
|
||||
|
||||
**Solution**: Re-capture from the camera to include all operations.
|
||||
|
||||
### Test Fails: Unexpected Response
|
||||
|
||||
The client is receiving the wrong SOAP response.
|
||||
|
||||
**Solution**: Check that operation names match. The mock server matches by SOAP operation name extracted from the `<Body>` element.
|
||||
|
||||
### Archive Not Found
|
||||
|
||||
```
|
||||
Failed to create mock server: failed to open archive: no such file or directory
|
||||
```
|
||||
|
||||
**Solution**: Ensure the capture archive is in `testdata/captures/` directory.
|
||||
|
||||
## Maintenance
|
||||
|
||||
### Updating Captures
|
||||
|
||||
When camera firmware changes:
|
||||
|
||||
1. Re-run diagnostics with `-capture-xml`
|
||||
2. Replace old capture archive
|
||||
3. Regenerate test (or manually update paths)
|
||||
4. Re-run tests to verify
|
||||
|
||||
### Cleaning Up
|
||||
|
||||
Remove old captures and tests:
|
||||
|
||||
```bash
|
||||
rm testdata/captures/old_camera_*.tar.gz
|
||||
rm testdata/captures/old_camera_test.go
|
||||
```
|
||||
|
||||
## CI/CD Integration
|
||||
|
||||
### GitHub Actions
|
||||
|
||||
```yaml
|
||||
name: Camera Tests
|
||||
on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-go@v4
|
||||
with:
|
||||
go-version: '1.21'
|
||||
|
||||
- name: Run Camera Tests
|
||||
run: go test -v ./testdata/captures/
|
||||
```
|
||||
|
||||
### Benefits in CI
|
||||
|
||||
- Tests run on every commit
|
||||
- Prevents merging code that breaks camera compatibility
|
||||
- No need for test cameras in CI environment
|
||||
- Fast execution (< 1 second for all cameras)
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. **Capture from latest firmware** - Use up-to-date camera firmware
|
||||
2. **Include all operations** - Run full diagnostic to capture all SOAP operations
|
||||
3. **Document camera models** - Add comments in tests noting camera specifics
|
||||
4. **Version control captures** - Commit `.tar.gz` files to track camera behavior over time
|
||||
5. **Test before changes** - Run tests before making client changes to establish baseline
|
||||
6. **Test after changes** - Verify all camera tests pass after modifications
|
||||
|
||||
## Related Tools
|
||||
|
||||
- **onvif-diagnostics** - Captures XML from cameras (`cmd/onvif-diagnostics`)
|
||||
- **generate-tests** - Creates tests from captures (`cmd/generate-tests`)
|
||||
- **mock_server** - Test server implementation (`testing/mock_server.go`)
|
||||
|
||||
## Support
|
||||
|
||||
For issues or questions:
|
||||
|
||||
1. Check that capture archive is valid (can extract with `tar -xzf`)
|
||||
2. Verify test file package is `onvif_test`
|
||||
3. Run with `-v` flag for verbose output
|
||||
4. Check `testing/mock_server.go` logs for operation matching details
|
||||
BIN
Binary file not shown.
BIN
Binary file not shown.
@@ -0,0 +1,98 @@
|
||||
package onvif_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/0x524a/onvif-go"
|
||||
onviftesting "github.com/0x524a/onvif-go/testing"
|
||||
)
|
||||
|
||||
// TestBosch_FLEXIDOME_indoor_5100i_IR_8710066 tests ONVIF client against Bosch_FLEXIDOME_indoor_5100i_IR_8.71.0066 captured responses
|
||||
func TestBosch_FLEXIDOME_indoor_5100i_IR_8710066(t *testing.T) {
|
||||
// Load capture archive (in same directory as test)
|
||||
captureArchive := "Bosch_FLEXIDOME_indoor_5100i_IR_8.71.0066_xmlcapture_20251110-123259.tar.gz"
|
||||
|
||||
mockServer, err := onviftesting.NewMockSOAPServer(captureArchive)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create mock server: %v", err)
|
||||
}
|
||||
defer mockServer.Close()
|
||||
|
||||
// Create ONVIF client pointing to mock server
|
||||
client, err := onvif.NewClient(
|
||||
mockServer.URL()+"/onvif/device_service",
|
||||
onvif.WithCredentials("testuser", "testpass"),
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create ONVIF client: %v", err)
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
|
||||
defer cancel()
|
||||
|
||||
t.Run("GetDeviceInformation", func(t *testing.T) {
|
||||
info, err := client.GetDeviceInformation(ctx)
|
||||
if err != nil {
|
||||
t.Errorf("GetDeviceInformation failed: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
// Validate expected values
|
||||
if info.Manufacturer == "" {
|
||||
t.Error("Manufacturer is empty")
|
||||
}
|
||||
if info.Model == "" {
|
||||
t.Error("Model is empty")
|
||||
}
|
||||
if info.FirmwareVersion == "" {
|
||||
t.Error("FirmwareVersion is empty")
|
||||
}
|
||||
|
||||
t.Logf("Device: %s %s (Firmware: %s)", info.Manufacturer, info.Model, info.FirmwareVersion)
|
||||
})
|
||||
|
||||
t.Run("GetSystemDateAndTime", func(t *testing.T) {
|
||||
_, err := client.GetSystemDateAndTime(ctx)
|
||||
if err != nil {
|
||||
t.Errorf("GetSystemDateAndTime failed: %v", err)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("GetCapabilities", func(t *testing.T) {
|
||||
caps, err := client.GetCapabilities(ctx)
|
||||
if err != nil {
|
||||
t.Errorf("GetCapabilities failed: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
if caps.Device == nil {
|
||||
t.Error("Device capabilities is nil")
|
||||
}
|
||||
if caps.Media == nil {
|
||||
t.Error("Media capabilities is nil")
|
||||
}
|
||||
|
||||
t.Logf("Capabilities: Device=%v, Media=%v, Imaging=%v, PTZ=%v",
|
||||
caps.Device != nil, caps.Media != nil, caps.Imaging != nil, caps.PTZ != nil)
|
||||
})
|
||||
|
||||
t.Run("GetProfiles", func(t *testing.T) {
|
||||
profiles, err := client.GetProfiles(ctx)
|
||||
if err != nil {
|
||||
t.Errorf("GetProfiles failed: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
if len(profiles) == 0 {
|
||||
t.Error("No profiles returned")
|
||||
}
|
||||
|
||||
t.Logf("Found %d profile(s)", len(profiles))
|
||||
for i, profile := range profiles {
|
||||
t.Logf(" Profile %d: %s (Token: %s)", i+1, profile.Name, profile.Token)
|
||||
}
|
||||
})
|
||||
|
||||
}
|
||||
@@ -0,0 +1,392 @@
|
||||
//go:build real_camera
|
||||
|
||||
package onvif
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/0x524a/onvif-go"
|
||||
)
|
||||
|
||||
// getTestCredentials returns ONVIF credentials from environment variables.
|
||||
// Required environment variables:
|
||||
// - ONVIF_ENDPOINT: Camera endpoint URL (e.g., http://192.168.1.201/onvif/device_service)
|
||||
// - ONVIF_USERNAME: ONVIF username
|
||||
// - ONVIF_PASSWORD: ONVIF password
|
||||
func getTestCredentials(t *testing.T) (endpoint, username, password string) {
|
||||
endpoint = os.Getenv("ONVIF_ENDPOINT")
|
||||
username = os.Getenv("ONVIF_USERNAME")
|
||||
password = os.Getenv("ONVIF_PASSWORD")
|
||||
|
||||
if endpoint == "" || username == "" || password == "" {
|
||||
t.Skip("ONVIF credentials not configured. Set ONVIF_ENDPOINT, ONVIF_USERNAME, and ONVIF_PASSWORD environment variables.")
|
||||
}
|
||||
|
||||
return endpoint, username, password
|
||||
}
|
||||
|
||||
// TestEnhancedDeviceFeatures tests new Device service methods with real camera data
|
||||
// Based on test results from Bosch FLEXIDOME indoor 5100i IR (8.71.0066)
|
||||
func TestEnhancedDeviceFeatures(t *testing.T) {
|
||||
endpoint, username, password := getTestCredentials(t)
|
||||
|
||||
// Create client with test credentials
|
||||
client, err := onvif.NewClient(
|
||||
endpoint,
|
||||
onvif.WithCredentials(username, password),
|
||||
onvif.WithTimeout(30*time.Second),
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create client: %v", err)
|
||||
}
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
t.Run("GetHostname", func(t *testing.T) {
|
||||
hostname, err := client.GetHostname(ctx)
|
||||
if err != nil {
|
||||
t.Fatalf("GetHostname failed: %v", err)
|
||||
}
|
||||
|
||||
// Bosch camera has hostname configuration
|
||||
if hostname == nil {
|
||||
t.Fatal("Expected hostname information, got nil")
|
||||
}
|
||||
|
||||
t.Logf("Hostname: FromDHCP=%v, Name=%q", hostname.FromDHCP, hostname.Name)
|
||||
})
|
||||
|
||||
t.Run("GetDNS", func(t *testing.T) {
|
||||
dns, err := client.GetDNS(ctx)
|
||||
if err != nil {
|
||||
t.Fatalf("GetDNS failed: %v", err)
|
||||
}
|
||||
|
||||
if dns == nil {
|
||||
t.Fatal("Expected DNS information, got nil")
|
||||
}
|
||||
|
||||
// Bosch camera uses DHCP for DNS
|
||||
if !dns.FromDHCP {
|
||||
t.Logf("Note: Camera not using DHCP for DNS")
|
||||
}
|
||||
|
||||
// Should have at least one DNS server
|
||||
if len(dns.DNSFromDHCP) == 0 && len(dns.DNSManual) == 0 {
|
||||
t.Error("Expected at least one DNS server")
|
||||
}
|
||||
|
||||
t.Logf("DNS: FromDHCP=%v, Servers=%d (DHCP) + %d (Manual)",
|
||||
dns.FromDHCP, len(dns.DNSFromDHCP), len(dns.DNSManual))
|
||||
})
|
||||
|
||||
t.Run("GetNTP", func(t *testing.T) {
|
||||
ntp, err := client.GetNTP(ctx)
|
||||
if err != nil {
|
||||
t.Fatalf("GetNTP failed: %v", err)
|
||||
}
|
||||
|
||||
if ntp == nil {
|
||||
t.Fatal("Expected NTP information, got nil")
|
||||
}
|
||||
|
||||
// Bosch camera uses DHCP for NTP
|
||||
if !ntp.FromDHCP {
|
||||
t.Logf("Note: Camera not using DHCP for NTP")
|
||||
}
|
||||
|
||||
t.Logf("NTP: FromDHCP=%v, Servers=%d (DHCP) + %d (Manual)",
|
||||
ntp.FromDHCP, len(ntp.NTPFromDHCP), len(ntp.NTPManual))
|
||||
})
|
||||
|
||||
t.Run("GetNetworkInterfaces", func(t *testing.T) {
|
||||
interfaces, err := client.GetNetworkInterfaces(ctx)
|
||||
if err != nil {
|
||||
t.Fatalf("GetNetworkInterfaces failed: %v", err)
|
||||
}
|
||||
|
||||
// Bosch camera has 1 network interface
|
||||
if len(interfaces) == 0 {
|
||||
t.Fatal("Expected at least one network interface")
|
||||
}
|
||||
|
||||
iface := interfaces[0]
|
||||
if iface.Token == "" {
|
||||
t.Error("Expected interface to have token")
|
||||
}
|
||||
|
||||
if iface.Info.Name == "" {
|
||||
t.Error("Expected interface to have name")
|
||||
}
|
||||
|
||||
if iface.Info.HwAddress == "" {
|
||||
t.Error("Expected interface to have hardware address")
|
||||
}
|
||||
|
||||
// Bosch camera has MTU of 1514
|
||||
if iface.Info.MTU == 0 {
|
||||
t.Error("Expected interface to have MTU")
|
||||
}
|
||||
|
||||
t.Logf("Interface: Token=%s, Name=%s, HwAddr=%s, MTU=%d",
|
||||
iface.Token, iface.Info.Name, iface.Info.HwAddress, iface.Info.MTU)
|
||||
|
||||
if iface.IPv4 != nil {
|
||||
t.Logf(" IPv4: Enabled=%v, DHCP=%v",
|
||||
iface.IPv4.Enabled, iface.IPv4.Config.DHCP)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("GetScopes", func(t *testing.T) {
|
||||
scopes, err := client.GetScopes(ctx)
|
||||
if err != nil {
|
||||
t.Fatalf("GetScopes failed: %v", err)
|
||||
}
|
||||
|
||||
// Bosch camera has 8 scopes
|
||||
if len(scopes) == 0 {
|
||||
t.Fatal("Expected at least one scope")
|
||||
}
|
||||
|
||||
// Check for expected scopes
|
||||
foundManufacturer := false
|
||||
foundType := false
|
||||
foundProfiles := 0
|
||||
|
||||
for _, scope := range scopes {
|
||||
if scope.ScopeItem == "onvif://www.onvif.org/name/Bosch" {
|
||||
foundManufacturer = true
|
||||
}
|
||||
if scope.ScopeItem == "onvif://www.onvif.org/type/Network_Video_Transmitter" {
|
||||
foundType = true
|
||||
}
|
||||
// Count ONVIF profiles
|
||||
if len(scope.ScopeItem) > 30 && scope.ScopeItem[:30] == "onvif://www.onvif.org/Profile/" {
|
||||
foundProfiles++
|
||||
}
|
||||
}
|
||||
|
||||
if !foundManufacturer {
|
||||
t.Error("Expected to find manufacturer scope")
|
||||
}
|
||||
if !foundType {
|
||||
t.Error("Expected to find device type scope")
|
||||
}
|
||||
|
||||
t.Logf("Scopes: Total=%d, Manufacturer=%v, Type=%v, Profiles=%d",
|
||||
len(scopes), foundManufacturer, foundType, foundProfiles)
|
||||
})
|
||||
|
||||
t.Run("GetUsers", func(t *testing.T) {
|
||||
users, err := client.GetUsers(ctx)
|
||||
if err != nil {
|
||||
t.Fatalf("GetUsers failed: %v", err)
|
||||
}
|
||||
|
||||
// Bosch camera has 3 users
|
||||
if len(users) == 0 {
|
||||
t.Fatal("Expected at least one user")
|
||||
}
|
||||
|
||||
// Verify user levels
|
||||
userLevels := make(map[string]int)
|
||||
for _, user := range users {
|
||||
if user.Username == "" {
|
||||
t.Error("Expected user to have username")
|
||||
}
|
||||
if user.UserLevel == "" {
|
||||
t.Error("Expected user to have level")
|
||||
}
|
||||
userLevels[user.UserLevel]++
|
||||
}
|
||||
|
||||
t.Logf("Users: Total=%d, Administrator=%d, Operator=%d, User=%d",
|
||||
len(users),
|
||||
userLevels["Administrator"],
|
||||
userLevels["Operator"],
|
||||
userLevels["User"])
|
||||
})
|
||||
}
|
||||
|
||||
// TestEnhancedMediaFeatures tests new Media service methods
|
||||
func TestEnhancedMediaFeatures(t *testing.T) {
|
||||
endpoint, username, password := getTestCredentials(t)
|
||||
|
||||
client, err := onvif.NewClient(
|
||||
endpoint,
|
||||
onvif.WithCredentials(username, password),
|
||||
onvif.WithTimeout(30*time.Second),
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create client: %v", err)
|
||||
}
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
// Initialize to get media endpoint
|
||||
if err := client.Initialize(ctx); err != nil {
|
||||
t.Logf("Warning: Initialize failed: %v", err)
|
||||
}
|
||||
|
||||
t.Run("GetVideoSources", func(t *testing.T) {
|
||||
sources, err := client.GetVideoSources(ctx)
|
||||
if err != nil {
|
||||
t.Fatalf("GetVideoSources failed: %v", err)
|
||||
}
|
||||
|
||||
// Bosch camera has 1 video source
|
||||
if len(sources) == 0 {
|
||||
t.Fatal("Expected at least one video source")
|
||||
}
|
||||
|
||||
source := sources[0]
|
||||
if source.Token == "" {
|
||||
t.Error("Expected source to have token")
|
||||
}
|
||||
|
||||
// Bosch camera supports 30fps
|
||||
if source.Framerate == 0 {
|
||||
t.Error("Expected source to have framerate")
|
||||
}
|
||||
|
||||
// Bosch camera has 1920x1080 resolution
|
||||
if source.Resolution == nil {
|
||||
t.Error("Expected source to have resolution")
|
||||
} else {
|
||||
if source.Resolution.Width == 0 || source.Resolution.Height == 0 {
|
||||
t.Error("Expected valid resolution dimensions")
|
||||
}
|
||||
t.Logf("Video Source: Token=%s, Framerate=%.1ffps, Resolution=%dx%d",
|
||||
source.Token, source.Framerate,
|
||||
source.Resolution.Width, source.Resolution.Height)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("GetAudioSources", func(t *testing.T) {
|
||||
sources, err := client.GetAudioSources(ctx)
|
||||
if err != nil {
|
||||
t.Fatalf("GetAudioSources failed: %v", err)
|
||||
}
|
||||
|
||||
// Bosch camera has 1 audio source with 2 channels
|
||||
if len(sources) == 0 {
|
||||
t.Fatal("Expected at least one audio source")
|
||||
}
|
||||
|
||||
source := sources[0]
|
||||
if source.Token == "" {
|
||||
t.Error("Expected source to have token")
|
||||
}
|
||||
|
||||
t.Logf("Audio Source: Token=%s, Channels=%d",
|
||||
source.Token, source.Channels)
|
||||
})
|
||||
|
||||
t.Run("GetAudioOutputs", func(t *testing.T) {
|
||||
outputs, err := client.GetAudioOutputs(ctx)
|
||||
if err != nil {
|
||||
t.Fatalf("GetAudioOutputs failed: %v", err)
|
||||
}
|
||||
|
||||
// Bosch camera has 1 audio output
|
||||
if len(outputs) == 0 {
|
||||
t.Fatal("Expected at least one audio output")
|
||||
}
|
||||
|
||||
output := outputs[0]
|
||||
if output.Token == "" {
|
||||
t.Error("Expected output to have token")
|
||||
}
|
||||
|
||||
t.Logf("Audio Output: Token=%s", output.Token)
|
||||
})
|
||||
}
|
||||
|
||||
// TestEnhancedImagingFeatures tests new Imaging service methods
|
||||
func TestEnhancedImagingFeatures(t *testing.T) {
|
||||
endpoint, username, password := getTestCredentials(t)
|
||||
|
||||
client, err := onvif.NewClient(
|
||||
endpoint,
|
||||
onvif.WithCredentials(username, password),
|
||||
onvif.WithTimeout(30*time.Second),
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create client: %v", err)
|
||||
}
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
// Initialize to get imaging endpoint
|
||||
if err := client.Initialize(ctx); err != nil {
|
||||
t.Logf("Warning: Initialize failed: %v", err)
|
||||
}
|
||||
|
||||
// Get video source token
|
||||
sources, err := client.GetVideoSources(ctx)
|
||||
if err != nil || len(sources) == 0 {
|
||||
t.Skip("No video sources available for imaging tests")
|
||||
}
|
||||
|
||||
videoSourceToken := sources[0].Token
|
||||
|
||||
t.Run("GetOptions", func(t *testing.T) {
|
||||
options, err := client.GetOptions(ctx, videoSourceToken)
|
||||
if err != nil {
|
||||
t.Fatalf("GetOptions failed: %v", err)
|
||||
}
|
||||
|
||||
if options == nil {
|
||||
t.Fatal("Expected imaging options, got nil")
|
||||
}
|
||||
|
||||
// Bosch camera supports brightness (0-255)
|
||||
if options.Brightness != nil {
|
||||
if options.Brightness.Min > options.Brightness.Max {
|
||||
t.Error("Expected Min <= Max for brightness")
|
||||
}
|
||||
t.Logf("Brightness range: %.0f - %.0f",
|
||||
options.Brightness.Min, options.Brightness.Max)
|
||||
}
|
||||
|
||||
// Bosch camera supports color saturation (0-255)
|
||||
if options.ColorSaturation != nil {
|
||||
if options.ColorSaturation.Min > options.ColorSaturation.Max {
|
||||
t.Error("Expected Min <= Max for color saturation")
|
||||
}
|
||||
t.Logf("ColorSaturation range: %.0f - %.0f",
|
||||
options.ColorSaturation.Min, options.ColorSaturation.Max)
|
||||
}
|
||||
|
||||
// Bosch camera supports contrast (0-255)
|
||||
if options.Contrast != nil {
|
||||
if options.Contrast.Min > options.Contrast.Max {
|
||||
t.Error("Expected Min <= Max for contrast")
|
||||
}
|
||||
t.Logf("Contrast range: %.0f - %.0f",
|
||||
options.Contrast.Min, options.Contrast.Max)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("GetMoveOptions", func(t *testing.T) {
|
||||
moveOptions, err := client.GetMoveOptions(ctx, videoSourceToken)
|
||||
if err != nil {
|
||||
t.Fatalf("GetMoveOptions failed: %v", err)
|
||||
}
|
||||
|
||||
if moveOptions == nil {
|
||||
t.Fatal("Expected move options, got nil")
|
||||
}
|
||||
|
||||
// Log available move options
|
||||
hasAbsolute := moveOptions.Absolute != nil
|
||||
hasRelative := moveOptions.Relative != nil
|
||||
hasContinuous := moveOptions.Continuous != nil
|
||||
|
||||
t.Logf("Move Options: Absolute=%v, Relative=%v, Continuous=%v",
|
||||
hasAbsolute, hasRelative, hasContinuous)
|
||||
})
|
||||
}
|
||||
Binary file not shown.
@@ -0,0 +1,141 @@
|
||||
{
|
||||
"discovery_date": "2026-01-13T13:22:10",
|
||||
"total_cameras": 8,
|
||||
"cameras": [
|
||||
{
|
||||
"id": 1,
|
||||
"endpoint": "urn:uuid:15020314-0204-0408-1500-ec71db465af7",
|
||||
"xaddrs": [
|
||||
"http://192.168.2.61:8000/onvif/device_service"
|
||||
],
|
||||
"manufacturer": "Reolink",
|
||||
"model": "E1Zoom",
|
||||
"ip": "192.168.2.61",
|
||||
"port": 8000,
|
||||
"profiles": ["Streaming", "T"],
|
||||
"location": "china"
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"endpoint": "urn:uuid:00075fe0-a604-04a6-e05f-0700075fe05f",
|
||||
"xaddrs": [
|
||||
"http://192.168.2.57/onvif/device_service",
|
||||
"https://192.168.2.57/onvif/device_service"
|
||||
],
|
||||
"manufacturer": "Bosch",
|
||||
"model": "AUTODOME_IP_starlight_5000i",
|
||||
"ip": "192.168.2.57",
|
||||
"port": 80,
|
||||
"profiles": ["Streaming", "G", "T"],
|
||||
"location": "",
|
||||
"supports_https": true
|
||||
},
|
||||
{
|
||||
"id": 3,
|
||||
"endpoint": "urn:uuid:555a3d17-6698-43d9-9a52-2a199ff14dec",
|
||||
"xaddrs": [
|
||||
"http://192.168.2.82/onvif/device_service"
|
||||
],
|
||||
"manufacturer": "AXIS",
|
||||
"model": "P3818-PVE",
|
||||
"ip": "192.168.2.82",
|
||||
"port": 80,
|
||||
"profiles": ["Streaming", "G", "M", "T"],
|
||||
"location": ""
|
||||
},
|
||||
{
|
||||
"id": 4,
|
||||
"endpoint": "urn:uuid:12060714-0005-0000-0302-ec71dbe838cc",
|
||||
"xaddrs": [
|
||||
"http://192.168.2.236:8000/onvif/device_service"
|
||||
],
|
||||
"manufacturer": "Reolink",
|
||||
"model": "ReolinkTrackMixWiFi",
|
||||
"ip": "192.168.2.236",
|
||||
"port": 8000,
|
||||
"profiles": ["Streaming", "T"],
|
||||
"location": "china"
|
||||
},
|
||||
{
|
||||
"id": 5,
|
||||
"endpoint": "urn:uuid:00075fca-f8fa-faf8-ca5f-0700075fca5f",
|
||||
"xaddrs": [
|
||||
"http://192.168.2.200/onvif/device_service",
|
||||
"https://192.168.2.200/onvif/device_service"
|
||||
],
|
||||
"manufacturer": "Bosch",
|
||||
"model": "FLEXIDOME_IP_starlight_8000i",
|
||||
"ip": "192.168.2.200",
|
||||
"port": 80,
|
||||
"profiles": ["Streaming", "G", "T"],
|
||||
"location": "",
|
||||
"supports_https": true
|
||||
},
|
||||
{
|
||||
"id": 6,
|
||||
"endpoint": "urn:uuid:00075fd5-9fbe-be9f-d55f-0700075fd55f",
|
||||
"xaddrs": [
|
||||
"http://192.168.2.24/onvif/device_service",
|
||||
"https://192.168.2.24/onvif/device_service"
|
||||
],
|
||||
"manufacturer": "Bosch",
|
||||
"model": "FLEXIDOME_panoramic_5100i",
|
||||
"ip": "192.168.2.24",
|
||||
"port": 80,
|
||||
"profiles": ["Streaming", "G", "T", "M"],
|
||||
"location": "",
|
||||
"supports_https": true
|
||||
},
|
||||
{
|
||||
"id": 7,
|
||||
"endpoint": "urn:uuid:cbc93166-2a81-4635-9fe3-dcd5e99528d3",
|
||||
"xaddrs": [
|
||||
"http://192.168.2.190/onvif/device_service",
|
||||
"https://192.168.2.190/onvif/device_service",
|
||||
"http://169.254.34.187/onvif/device_service",
|
||||
"https://169.254.34.187/onvif/device_service"
|
||||
],
|
||||
"manufacturer": "AXIS",
|
||||
"model": "Q3819-PVE",
|
||||
"ip": "192.168.2.190",
|
||||
"port": 80,
|
||||
"profiles": ["Streaming", "G", "M", "T"],
|
||||
"location": "",
|
||||
"supports_https": true,
|
||||
"additional_ips": ["169.254.34.187"]
|
||||
},
|
||||
{
|
||||
"id": 8,
|
||||
"endpoint": "urn:uuid:9e8de0a1-c818-448d-90eb-85670b2b9872",
|
||||
"xaddrs": [
|
||||
"http://192.168.2.30/onvif/device_service",
|
||||
"https://192.168.2.30/onvif/device_service"
|
||||
],
|
||||
"manufacturer": "AXIS",
|
||||
"model": "P5655-E",
|
||||
"ip": "192.168.2.30",
|
||||
"port": 80,
|
||||
"profiles": ["Streaming", "G", "M", "T"],
|
||||
"location": "",
|
||||
"supports_https": true
|
||||
}
|
||||
],
|
||||
"manufacturers": {
|
||||
"Reolink": 2,
|
||||
"Bosch": 3,
|
||||
"AXIS": 3
|
||||
},
|
||||
"profile_support": {
|
||||
"Streaming": 8,
|
||||
"T": 8,
|
||||
"G": 6,
|
||||
"M": 4
|
||||
},
|
||||
"notes": [
|
||||
"All cameras discovered on 192.168.2.0/24 network",
|
||||
"3 Bosch cameras support HTTPS",
|
||||
"3 AXIS cameras support HTTPS and Profile M (metadata)",
|
||||
"2 Reolink cameras are basic (Profile T only)",
|
||||
"Camera 7 (AXIS Q3819-PVE) has dual network interfaces"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
Discovering ONVIF cameras on the network...
|
||||
|
||||
Found 8 camera(s):
|
||||
|
||||
Camera 1:
|
||||
Endpoint: urn:uuid:15020314-0204-0408-1500-ec71db465af7
|
||||
XAddr: http://192.168.2.61:8000/onvif/device_service
|
||||
Scopes:
|
||||
- onvif://www.onvif.org/type/video_encoder
|
||||
- onvif://www.onvif.org/location/country/china
|
||||
- onvif://www.onvif.org/type/network_video_transmitter
|
||||
- onvif://www.onvif.org/Profile/Streaming
|
||||
- onvif://www.onvif.org/Profile/T
|
||||
- onvif://www.onvif.org/name/IPC-BO
|
||||
- onvif://www.onvif.org/hardware/E1Zoom
|
||||
- onvif://www.onvif.org/name/IPC
|
||||
|
||||
Camera 2:
|
||||
Endpoint: urn:uuid:00075fe0-a604-04a6-e05f-0700075fe05f
|
||||
XAddr: http://192.168.2.57/onvif/device_service
|
||||
XAddr: https://192.168.2.57/onvif/device_service
|
||||
Scopes:
|
||||
- onvif://www.onvif.org/type/Network_Video_Transmitter
|
||||
- onvif://www.onvif.org/name/Bosch
|
||||
- onvif://www.onvif.org/location/
|
||||
- onvif://www.onvif.org/hardware/AUTODOME_IP_starlight_5000i
|
||||
- onvif://www.onvif.org/Profile/Streaming
|
||||
- onvif://www.onvif.org/Profile/G
|
||||
- onvif://www.onvif.org/Profile/T
|
||||
|
||||
Camera 3:
|
||||
Endpoint: urn:uuid:555a3d17-6698-43d9-9a52-2a199ff14dec
|
||||
XAddr: http://192.168.2.82/onvif/device_service
|
||||
Scopes:
|
||||
- onvif://www.onvif.org/Profile/Streaming
|
||||
- onvif://www.onvif.org/Profile/G
|
||||
- onvif://www.onvif.org/hardware/P3818-PVE
|
||||
- onvif://www.onvif.org/name/AXIS%20P3818-PVE
|
||||
- onvif://www.onvif.org/Profile/M
|
||||
- onvif://www.onvif.org/Profile/T
|
||||
- onvif://www.onvif.org/location/
|
||||
|
||||
Camera 4:
|
||||
Endpoint: urn:uuid:12060714-0005-0000-0302-ec71dbe838cc
|
||||
XAddr: http://192.168.2.236:8000/onvif/device_service
|
||||
Scopes:
|
||||
- onvif://www.onvif.org/type/video_encoder
|
||||
- onvif://www.onvif.org/location/country/china
|
||||
- onvif://www.onvif.org/type/network_video_transmitter
|
||||
- onvif://www.onvif.org/Profile/Streaming
|
||||
- onvif://www.onvif.org/Profile/T
|
||||
- onvif://www.onvif.org/name/IPC-BO
|
||||
- onvif://www.onvif.org/hardware/ReolinkTrackMixWiFi
|
||||
- onvif://www.onvif.org/name/IPC
|
||||
|
||||
Camera 5:
|
||||
Endpoint: urn:uuid:00075fca-f8fa-faf8-ca5f-0700075fca5f
|
||||
XAddr: http://192.168.2.200/onvif/device_service
|
||||
XAddr: https://192.168.2.200/onvif/device_service
|
||||
Scopes:
|
||||
- onvif://www.onvif.org/type/Network_Video_Transmitter
|
||||
- onvif://www.onvif.org/name/Bosch
|
||||
- onvif://www.onvif.org/location/
|
||||
- onvif://www.onvif.org/hardware/FLEXIDOME_IP_starlight_8000i
|
||||
- onvif://www.onvif.org/Profile/Streaming
|
||||
- onvif://www.onvif.org/Profile/G
|
||||
- onvif://www.onvif.org/Profile/T
|
||||
|
||||
Camera 6:
|
||||
Endpoint: urn:uuid:00075fd5-9fbe-be9f-d55f-0700075fd55f
|
||||
XAddr: http://192.168.2.24/onvif/device_service
|
||||
XAddr: https://192.168.2.24/onvif/device_service
|
||||
Scopes:
|
||||
- onvif://www.onvif.org/type/Network_Video_Transmitter
|
||||
- onvif://www.onvif.org/name/Bosch
|
||||
- onvif://www.onvif.org/location/
|
||||
- onvif://www.onvif.org/hardware/FLEXIDOME_panoramic_5100i
|
||||
- onvif://www.onvif.org/Profile/Streaming
|
||||
- onvif://www.onvif.org/Profile/G
|
||||
- onvif://www.onvif.org/Profile/T
|
||||
- onvif://www.onvif.org/Profile/M
|
||||
|
||||
Camera 7:
|
||||
Endpoint: urn:uuid:cbc93166-2a81-4635-9fe3-dcd5e99528d3
|
||||
XAddr: http://192.168.2.190/onvif/device_service
|
||||
XAddr: https://192.168.2.190/onvif/device_service
|
||||
XAddr: http://169.254.34.187/onvif/device_service
|
||||
XAddr: https://169.254.34.187/onvif/device_service
|
||||
Scopes:
|
||||
- onvif://www.onvif.org/Profile/Streaming
|
||||
- onvif://www.onvif.org/Profile/G
|
||||
- onvif://www.onvif.org/hardware/Q3819-PVE
|
||||
- onvif://www.onvif.org/name/AXIS%20Q3819-PVE
|
||||
- onvif://www.onvif.org/Profile/M
|
||||
- onvif://www.onvif.org/Profile/T
|
||||
- onvif://www.onvif.org/location/
|
||||
|
||||
Camera 8:
|
||||
Endpoint: urn:uuid:9e8de0a1-c818-448d-90eb-85670b2b9872
|
||||
XAddr: http://192.168.2.30/onvif/device_service
|
||||
XAddr: https://192.168.2.30/onvif/device_service
|
||||
Scopes:
|
||||
- onvif://www.onvif.org/Profile/Streaming
|
||||
- onvif://www.onvif.org/Profile/G
|
||||
- onvif://www.onvif.org/hardware/P5655-E
|
||||
- onvif://www.onvif.org/name/AXIS%20P5655-E
|
||||
- onvif://www.onvif.org/Profile/M
|
||||
- onvif://www.onvif.org/Profile/T
|
||||
- onvif://www.onvif.org/location/
|
||||
|
||||
@@ -0,0 +1,141 @@
|
||||
// Package testdata provides camera configuration data for testing
|
||||
// Auto-generated from network discovery on 2026-01-13
|
||||
package testdata
|
||||
|
||||
// DiscoveredCamera represents a camera found on the network
|
||||
type DiscoveredCamera struct {
|
||||
ID int
|
||||
Endpoint string
|
||||
XAddrs []string
|
||||
Manufacturer string
|
||||
Model string
|
||||
IP string
|
||||
Port int
|
||||
Profiles []string
|
||||
SupportsHTTPS bool
|
||||
}
|
||||
|
||||
// TestCameras contains the discovered cameras for testing
|
||||
var TestCameras = []DiscoveredCamera{
|
||||
{
|
||||
ID: 1,
|
||||
Endpoint: "urn:uuid:15020314-0204-0408-1500-ec71db465af7",
|
||||
XAddrs: []string{"http://192.168.2.61:8000/onvif/device_service"},
|
||||
Manufacturer: "Reolink",
|
||||
Model: "E1Zoom",
|
||||
IP: "192.168.2.61",
|
||||
Port: 8000,
|
||||
Profiles: []string{"Streaming", "T"},
|
||||
},
|
||||
{
|
||||
ID: 2,
|
||||
Endpoint: "urn:uuid:00075fe0-a604-04a6-e05f-0700075fe05f",
|
||||
XAddrs: []string{"http://192.168.2.57/onvif/device_service", "https://192.168.2.57/onvif/device_service"},
|
||||
Manufacturer: "Bosch",
|
||||
Model: "AUTODOME_IP_starlight_5000i",
|
||||
IP: "192.168.2.57",
|
||||
Port: 80,
|
||||
Profiles: []string{"Streaming", "G", "T"},
|
||||
SupportsHTTPS: true,
|
||||
},
|
||||
{
|
||||
ID: 3,
|
||||
Endpoint: "urn:uuid:555a3d17-6698-43d9-9a52-2a199ff14dec",
|
||||
XAddrs: []string{"http://192.168.2.82/onvif/device_service"},
|
||||
Manufacturer: "AXIS",
|
||||
Model: "P3818-PVE",
|
||||
IP: "192.168.2.82",
|
||||
Port: 80,
|
||||
Profiles: []string{"Streaming", "G", "M", "T"},
|
||||
},
|
||||
{
|
||||
ID: 4,
|
||||
Endpoint: "urn:uuid:12060714-0005-0000-0302-ec71dbe838cc",
|
||||
XAddrs: []string{"http://192.168.2.236:8000/onvif/device_service"},
|
||||
Manufacturer: "Reolink",
|
||||
Model: "ReolinkTrackMixWiFi",
|
||||
IP: "192.168.2.236",
|
||||
Port: 8000,
|
||||
Profiles: []string{"Streaming", "T"},
|
||||
},
|
||||
{
|
||||
ID: 5,
|
||||
Endpoint: "urn:uuid:00075fca-f8fa-faf8-ca5f-0700075fca5f",
|
||||
XAddrs: []string{"http://192.168.2.200/onvif/device_service", "https://192.168.2.200/onvif/device_service"},
|
||||
Manufacturer: "Bosch",
|
||||
Model: "FLEXIDOME_IP_starlight_8000i",
|
||||
IP: "192.168.2.200",
|
||||
Port: 80,
|
||||
Profiles: []string{"Streaming", "G", "T"},
|
||||
SupportsHTTPS: true,
|
||||
},
|
||||
{
|
||||
ID: 6,
|
||||
Endpoint: "urn:uuid:00075fd5-9fbe-be9f-d55f-0700075fd55f",
|
||||
XAddrs: []string{"http://192.168.2.24/onvif/device_service", "https://192.168.2.24/onvif/device_service"},
|
||||
Manufacturer: "Bosch",
|
||||
Model: "FLEXIDOME_panoramic_5100i",
|
||||
IP: "192.168.2.24",
|
||||
Port: 80,
|
||||
Profiles: []string{"Streaming", "G", "T", "M"},
|
||||
SupportsHTTPS: true,
|
||||
},
|
||||
{
|
||||
ID: 7,
|
||||
Endpoint: "urn:uuid:cbc93166-2a81-4635-9fe3-dcd5e99528d3",
|
||||
XAddrs: []string{"http://192.168.2.190/onvif/device_service", "https://192.168.2.190/onvif/device_service"},
|
||||
Manufacturer: "AXIS",
|
||||
Model: "Q3819-PVE",
|
||||
IP: "192.168.2.190",
|
||||
Port: 80,
|
||||
Profiles: []string{"Streaming", "G", "M", "T"},
|
||||
SupportsHTTPS: true,
|
||||
},
|
||||
{
|
||||
ID: 8,
|
||||
Endpoint: "urn:uuid:9e8de0a1-c818-448d-90eb-85670b2b9872",
|
||||
XAddrs: []string{"http://192.168.2.30/onvif/device_service", "https://192.168.2.30/onvif/device_service"},
|
||||
Manufacturer: "AXIS",
|
||||
Model: "P5655-E",
|
||||
IP: "192.168.2.30",
|
||||
Port: 80,
|
||||
Profiles: []string{"Streaming", "G", "M", "T"},
|
||||
SupportsHTTPS: true,
|
||||
},
|
||||
}
|
||||
|
||||
// GetCameraByManufacturer returns cameras filtered by manufacturer
|
||||
func GetCameraByManufacturer(manufacturer string) []DiscoveredCamera {
|
||||
var result []DiscoveredCamera
|
||||
for _, cam := range TestCameras {
|
||||
if cam.Manufacturer == manufacturer {
|
||||
result = append(result, cam)
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// GetCameraByProfile returns cameras that support a specific profile
|
||||
func GetCameraByProfile(profile string) []DiscoveredCamera {
|
||||
var result []DiscoveredCamera
|
||||
for _, cam := range TestCameras {
|
||||
for _, p := range cam.Profiles {
|
||||
if p == profile {
|
||||
result = append(result, cam)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// GetHTTPSCameras returns cameras that support HTTPS
|
||||
func GetHTTPSCameras() []DiscoveredCamera {
|
||||
var result []DiscoveredCamera
|
||||
for _, cam := range TestCameras {
|
||||
if cam.SupportsHTTPS {
|
||||
result = append(result, cam)
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
Reference in New Issue
Block a user