From b4e49828768006d02aa0d3d64f7cd6272e8559c9 Mon Sep 17 00:00:00 2001 From: ProtoTess <32490978+0x524A@users.noreply.github.com> Date: Mon, 1 Dec 2025 00:49:36 +0000 Subject: [PATCH] Refactor XML response handling in device extended and security tests - Adjusted formatting in XML response strings for consistency in device_extended_test.go and device_security_test.go. - Improved readability by aligning XML declaration and body content. - Updated mock server responses to ensure proper handling of various ONVIF operations. Enhance device security and storage handling - Refactored struct field declarations in device_security.go and device_storage_test.go for improved clarity. - Ensured consistent formatting across struct definitions and XML tags. Standardize whitespace and formatting across multiple files - Removed unnecessary blank lines and adjusted indentation in discovery, imaging, media, and PTZ server files. - Improved overall code readability and maintainability by ensuring consistent formatting. Update example applications for better readability - Cleaned up whitespace in example applications to enhance code clarity. - Ensured consistent formatting in main.go files across various examples. Refactor server and SOAP handler code for consistency - Standardized struct field declarations and XML tag formatting in server and SOAP handler files. - Improved readability by aligning struct fields and ensuring consistent use of whitespace. General code cleanup and formatting adjustments - Applied consistent formatting across various files, including types.go and test files. - Enhanced readability by aligning struct fields and removing unnecessary blank lines. --- .github/workflows/ci.yml | 36 +++++------ .github/workflows/coverage.yml | 4 +- .github/workflows/release.yml | 22 +++---- .github/workflows/test.yml | 6 +- client.go | 5 +- client_test.go | 56 ++++++++-------- cmd/onvif-cli/ascii.go | 18 +++--- cmd/onvif-cli/main.go | 64 +++++++++---------- cmd/onvif-quick/main.go | 13 ++-- cmd/onvif-server/main.go | 2 +- device.go | 1 - device_additional.go | 8 +-- device_additional_test.go | 10 +-- device_certificates.go | 12 ++-- device_certificates_test.go | 4 +- device_extended.go | 24 +++---- device_extended_test.go | 20 +++--- device_security.go | 16 ++--- device_security_test.go | 30 ++++----- device_storage_test.go | 4 +- device_wifi.go | 22 +++---- device_wifi_test.go | 4 +- discovery/discovery.go | 24 +++---- examples/discovery/main.go | 2 +- examples/imaging-settings/main.go | 6 +- examples/ptz-control/main.go | 2 +- examples/test-server/main.go | 6 +- internal/soap/soap_test.go | 12 ++-- server/device.go | 24 +++---- server/imaging.go | 40 ++++++------ server/media.go | 22 +++---- server/ptz.go | 18 +++--- server/server.go | 10 +-- server/soap/handler.go | 14 ++-- server/types.go | 20 +++--- .../captures/enhanced_device_features_test.go | 1 - types.go | 58 ++++++++--------- 37 files changed, 318 insertions(+), 322 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f2663c9..558ab72 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -18,15 +18,15 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@v4 + uses: actions/checkout@d632cb752374ac6c22015e4b4bef1d19db85d69f # v4.2.0 - name: Set up Go - uses: actions/setup-go@v5 + uses: actions/setup-go@0a12ed9d6470c3512128ab8f076f97fbbff3da52 # v5.0.1 with: go-version: '1.23' - name: Cache Go modules - uses: actions/cache@v4 + uses: actions/cache@0c45773b623bea8c8e75f6c82b208c3cf94ea4f9 # v4.0.2 with: path: ~/go/pkg/mod key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }} @@ -45,7 +45,7 @@ jobs: fi - name: Lint - uses: golangci/golangci-lint-action@v4 + uses: golangci/golangci-lint-action@3cfe3a4ac716397848d91e4042119f51a40b6aaf # v4.0.0 with: version: latest args: --timeout=5m --fix=false @@ -58,15 +58,15 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@v4 + uses: actions/checkout@d632cb752374ac6c22015e4b4bef1d19db85d69f # v4.2.0 - name: Set up Go - uses: actions/setup-go@v5 + uses: actions/setup-go@0a12ed9d6470c3512128ab8f076f97fbbff3da52 # v5.0.1 with: go-version: '1.23' - name: Cache Go modules - uses: actions/cache@v4 + uses: actions/cache@0c45773b623bea8c8e75f6c82b208c3cf94ea4f9 # v4.0.2 with: path: ~/go/pkg/mod key: ${{ runner.os }}-go-1.23-${{ hashFiles('**/go.sum') }} @@ -83,7 +83,7 @@ jobs: run: go tool cover -html=coverage.out -o coverage.html - name: Upload coverage to Codecov - uses: codecov/codecov-action@v4 + uses: codecov/codecov-action@8a4b2d1c64a2a31fb92d9fa9f1e2b02e35d2db1a # v4.0.1 with: token: ${{ secrets.CODECOV_TOKEN }} files: ./coverage.out @@ -93,7 +93,7 @@ jobs: - name: Archive coverage if: always() - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@89ef406dd8d8a50a2f1e5e903c0e970c27a27de8 # v4.3.5 with: name: coverage-report path: | @@ -119,15 +119,15 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@v4 + uses: actions/checkout@d632cb752374ac6c22015e4b4bef1d19db85d69f # v4.2.0 - name: Set up Go - uses: actions/setup-go@v5 + uses: actions/setup-go@0a12ed9d6470c3512128ab8f076f97fbbff3da52 # v5.0.1 with: go-version: ${{ matrix.go-version }} - name: Cache Go modules - uses: actions/cache@v4 + uses: actions/cache@0c45773b623bea8c8e75f6c82b208c3cf94ea4f9 # v4.0.2 with: path: | ~/.cache/go-build @@ -151,17 +151,17 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@v4 + uses: actions/checkout@d632cb752374ac6c22015e4b4bef1d19db85d69f # v4.2.0 with: fetch-depth: 0 - name: Download coverage from test job - uses: actions/download-artifact@v4 + uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4.1.8 with: name: coverage-report - name: SonarCloud Scan - uses: SonarSource/sonarcloud-github-action@master + uses: SonarSource/sonarcloud-github-action@3b335e14ab49358d133eef17b8c1590fe7c21c0e # v2.2.0 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} @@ -179,15 +179,15 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@v4 + uses: actions/checkout@d632cb752374ac6c22015e4b4bef1d19db85d69f # v4.2.0 - name: Set up Go - uses: actions/setup-go@v5 + uses: actions/setup-go@0a12ed9d6470c3512128ab8f076f97fbbff3da52 # v5.0.1 with: go-version: '1.23' - name: Cache Go modules - uses: actions/cache@v4 + uses: actions/cache@0c45773b623bea8c8e75f6c82b208c3cf94ea4f9 # v4.0.2 with: path: ~/go/pkg/mod key: ${{ runner.os }}-go-1.23-${{ hashFiles('**/go.sum') }} diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml index d0551b2..15d4674 100644 --- a/.github/workflows/coverage.yml +++ b/.github/workflows/coverage.yml @@ -15,10 +15,10 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@v4 + uses: actions/checkout@d632cb752374ac6c22015e4b4bef1d19db85d69f # v4.2.0 - name: Download artifacts - uses: actions/download-artifact@v4 + uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4.1.8 with: name: coverage-report diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 12cb77f..832aa07 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -39,12 +39,12 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@v4 + uses: actions/checkout@d632cb752374ac6c22015e4b4bef1d19db85d69f # v4.2.0 with: fetch-depth: 0 - name: Set up Go - uses: actions/setup-go@v5 + uses: actions/setup-go@0a12ed9d6470c3512128ab8f076f97fbbff3da52 # v5.0.1 with: go-version: '1.21' @@ -142,12 +142,12 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout code - uses: actions/checkout@v4 + uses: actions/checkout@d632cb752374ac6c22015e4b4bef1d19db85d69f # v4.2.0 with: fetch-depth: 0 - name: Download all artifacts - uses: actions/download-artifact@v4 + uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4.1.8 with: path: all-releases pattern: release-* @@ -177,7 +177,7 @@ jobs: fi - name: Create Release - uses: softprops/action-gh-release@v2 + uses: softprops/action-gh-release@c062e08bd532815e2082a85e87e3ef29c3e6d191 # v2.0.8 with: files: all-releases/* draft: true @@ -228,23 +228,23 @@ jobs: if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v') steps: - name: Checkout code - uses: actions/checkout@v4 + uses: actions/checkout@d632cb752374ac6c22015e4b4bef1d19db85d69f # v4.2.0 - name: Set up QEMU - uses: docker/setup-qemu-action@v3 + uses: docker/setup-qemu-action@49b3bc8e6f341ff684fb5300544cda1ff1a290d5 # v3.2.0 - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3 + uses: docker/setup-buildx-action@4fd812986e6c8ec69f87869bf8f84174f3426a6d # v3.4.0 - name: Login to Docker Hub - uses: docker/login-action@v3 + uses: docker/login-action@9780b0c442fbb1117ed29e0efdff1b4ee7e33440 # v3.3.0 with: username: ${{ secrets.DOCKERHUB_USERNAME }} password: ${{ secrets.DOCKERHUB_TOKEN }} continue-on-error: true - name: Login to GitHub Container Registry - uses: docker/login-action@v3 + uses: docker/login-action@9780b0c442fbb1117ed29e0efdff1b4ee7e33440 # v3.3.0 with: registry: ghcr.io username: ${{ github.actor }} @@ -255,7 +255,7 @@ jobs: run: echo "VERSION=${GITHUB_REF#refs/tags/v}" >> $GITHUB_OUTPUT - name: Build and push - uses: docker/build-push-action@v5 + uses: docker/build-push-action@5f63d2b5a0f13e6fe58a2658b1cf779280e04def # v6.2.0 with: context: . platforms: linux/amd64,linux/arm64,linux/arm/v7 diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 72a177d..9a1259a 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -18,15 +18,15 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@v4 + uses: actions/checkout@d632cb752374ac6c22015e4b4bef1d19db85d69f # v4.2.0 - name: Set up Go - uses: actions/setup-go@v5 + uses: actions/setup-go@0a12ed9d6470c3512128ab8f076f97fbbff3da52 # v5.0.1 with: go-version: ${{ matrix.go-version }} - name: Cache Go modules - uses: actions/cache@v4 + uses: actions/cache@0c45773b623bea8c8e75f6c82b208c3cf94ea4f9 # v4.0.2 with: path: ~/go/pkg/mod key: ${{ runner.os }}-go-${{ matrix.go-version }}-${{ hashFiles('**/go.sum') }} diff --git a/client.go b/client.go index 6817142..0cbd83e 100644 --- a/client.go +++ b/client.go @@ -21,7 +21,7 @@ type Client struct { password string httpClient *http.Client mu sync.RWMutex - + // Service endpoints mediaEndpoint string ptzEndpoint string @@ -130,7 +130,7 @@ func normalizeEndpoint(endpoint string) (string, error) { if err != nil { return "", fmt.Errorf("invalid IP address or hostname: %w", err) } - + if parsedURL.Host == "" { return "", fmt.Errorf("invalid endpoint format") } @@ -497,4 +497,3 @@ func generateNonce() string { // Generate a simple nonce return fmt.Sprintf("%d", time.Now().UnixNano()) } - diff --git a/client_test.go b/client_test.go index 6d3b902..b3bacfb 100644 --- a/client_test.go +++ b/client_test.go @@ -95,19 +95,19 @@ func TestNormalizeEndpoint(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { result, err := normalizeEndpoint(tt.input) - + if tt.wantErr { if err == nil { t.Errorf("normalizeEndpoint() expected error but got none") } return } - + if err != nil { t.Errorf("normalizeEndpoint() unexpected error: %v", err) return } - + if result != tt.expected { t.Errorf("normalizeEndpoint() = %v, want %v", result, tt.expected) } @@ -453,7 +453,7 @@ func TestGetDeviceInformationWithMockServer(t *testing.T) { // Return empty response - will cause EOF error which is expected for now })) defer server.Close() - + client, err := NewClient( server.URL, WithCredentials("admin", "password"), @@ -461,14 +461,14 @@ func TestGetDeviceInformationWithMockServer(t *testing.T) { if err != nil { t.Fatalf("NewClient() failed: %v", err) } - + ctx := context.Background() _, err = client.GetDeviceInformation(ctx) // We expect an error since we're not returning valid SOAP if err == nil { - t.Errorf("Expected error with empty response, but got none") + t.Errorf("Expected error with empty response, but got none") } - + // This test just verifies the client can be created and make requests t.Logf("Expected error occurred: %v", err) } @@ -479,18 +479,18 @@ func TestGetDeviceInformationWithAuth(t *testing.T) { w.WriteHeader(http.StatusUnauthorized) })) defer server.Close() - + client, err := NewClient(server.URL) if err != nil { t.Fatalf("NewClient() failed: %v", err) } - + ctx := context.Background() _, err = client.GetDeviceInformation(ctx) if err == nil { t.Errorf("Expected authentication error, but got none") } - + t.Logf("Authentication error (expected): %v", err) } @@ -504,16 +504,16 @@ func TestInitializeEndpointDiscovery(t *testing.T) { if err != nil { t.Fatalf("NewClient() failed: %v", err) } - + ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second) defer cancel() - + err = client.Initialize(ctx) // We expect this to fail due to network timeout if err == nil { t.Errorf("Expected network error, but got none") } - + t.Logf("Network error (expected): %v", err) } @@ -525,21 +525,21 @@ func TestGetProfilesRequiresInitialization(t *testing.T) { if err != nil { t.Fatalf("NewClient() failed: %v", err) } - + ctx := context.Background() _, err = client.GetProfiles(ctx) // Should fail because Initialize was not called if err == nil { t.Errorf("Expected error when GetProfiles called without Initialize") } - + t.Logf("Expected error: %v", err) } func TestContextTimeout(t *testing.T) { mock := NewMockONVIFServer() defer mock.Close() - + client, err := NewClient( mock.URL(), WithCredentials("admin", "password"), @@ -547,17 +547,17 @@ func TestContextTimeout(t *testing.T) { if err != nil { t.Fatalf("NewClient() failed: %v", err) } - + // Create context with very short timeout ctx, cancel := context.WithTimeout(context.Background(), 1*time.Nanosecond) defer cancel() - + // This should timeout _, err = client.GetDeviceInformation(ctx) if err == nil { t.Errorf("Expected timeout error, but got none") } - + if !strings.Contains(err.Error(), "context deadline exceeded") { t.Errorf("Expected context deadline exceeded error, got: %v", err) } @@ -598,7 +598,7 @@ func BenchmarkNewClient(b *testing.B) { func BenchmarkGetDeviceInformation(b *testing.B) { mock := NewMockONVIFServer() defer mock.Close() - + client, err := NewClient( mock.URL(), WithCredentials("admin", "password"), @@ -606,9 +606,9 @@ func BenchmarkGetDeviceInformation(b *testing.B) { if err != nil { b.Fatalf("NewClient() failed: %v", err) } - + ctx := context.Background() - + b.ResetTimer() for i := 0; i < b.N; i++ { _, err := client.GetDeviceInformation(ctx) @@ -629,24 +629,24 @@ func ExampleClient_GetDeviceInformation() { if err != nil { panic(err) } - + // Get device information ctx := context.Background() info, err := client.GetDeviceInformation(ctx) if err != nil { panic(err) } - + fmt.Printf("Camera: %s %s\n", info.Manufacturer, info.Model) fmt.Printf("Firmware: %s\n", info.FirmwareVersion) } func TestFixLocalhostURL(t *testing.T) { tests := []struct { - name string - clientURL string - serviceURL string - expectedURL string + name string + clientURL string + serviceURL string + expectedURL string }{ { name: "localhost hostname", diff --git a/cmd/onvif-cli/ascii.go b/cmd/onvif-cli/ascii.go index 8a48b3e..917a38e 100644 --- a/cmd/onvif-cli/ascii.go +++ b/cmd/onvif-cli/ascii.go @@ -11,10 +11,10 @@ import ( // ASCIIConfig controls ASCII art generation parameters type ASCIIConfig struct { - Width int // Output width in characters - Height int // Output height in characters - Invert bool // Invert brightness - Quality string // "high", "medium", "low" + Width int // Output width in characters + Height int // Output height in characters + Invert bool // Invert brightness + Quality string // "high", "medium", "low" } // DefaultASCIIConfig returns a sensible default configuration @@ -31,18 +31,18 @@ func DefaultASCIIConfig() ASCIIConfig { var ( // Full charset with many shades charsetFull = []rune{' ', '.', ':', '-', '=', '+', '*', '#', '%', '@'} - + // Medium charset - balanced charsetMedium = []rune{' ', '.', '-', '=', '+', '#', '@'} - + // Simple charset - just a few chars charsetSimple = []rune{' ', '-', '#', '@'} - + // Block charset - using block characters charsetBlock = []rune{' ', '░', '▒', '▓', '█'} - + // Detailed charset - charsetDetailed = []rune{' ', '`', '.', ',', ':', ';', '!', 'i', 'l', 'I', + charsetDetailed = []rune{' ', '`', '.', ',', ':', ';', '!', 'i', 'l', 'I', 'o', 'O', '0', 'e', 'E', 'p', 'P', 'x', 'X', '$', 'D', 'W', 'M', '@', '#'} ) diff --git a/cmd/onvif-cli/main.go b/cmd/onvif-cli/main.go index 75c83d3..c5a8ffb 100644 --- a/cmd/onvif-cli/main.go +++ b/cmd/onvif-cli/main.go @@ -101,7 +101,7 @@ func (c *CLI) discoverCameras() { // Try auto-discovery first (no specific interface) fmt.Println("⏳ Attempting auto-discovery on default interface...") devices, err := discovery.DiscoverWithOptions(ctx, 5*time.Second, &discovery.DiscoverOptions{}) - + // If auto-discovery fails or finds nothing, offer interface selection if err != nil || len(devices) == 0 { if err != nil { @@ -109,11 +109,11 @@ func (c *CLI) discoverCameras() { } else { fmt.Println("⚠️ No cameras found on default interface") } - + fmt.Println() fmt.Println("💡 Trying specific network interfaces...") fmt.Println() - + // Get available interfaces and let user select devices, err = c.discoverWithInterfaceSelection() if err != nil { @@ -139,17 +139,17 @@ func (c *CLI) discoverCameras() { for i, device := range devices { fmt.Printf("📹 Camera #%d:\n", i+1) fmt.Printf(" Endpoint: %s\n", device.GetDeviceEndpoint()) - + name := device.GetName() if name != "" { fmt.Printf(" Name: %s\n", name) } - + location := device.GetLocation() if location != "" { fmt.Printf(" Location: %s\n", location) } - + fmt.Printf(" Types: %v\n", device.Types) fmt.Printf(" XAddrs: %v\n", device.XAddrs) fmt.Println() @@ -280,16 +280,16 @@ func (c *CLI) selectAndConnectCamera(devices []*discovery.Device) { func (c *CLI) connectToDiscoveredCamera(device *discovery.Device) { endpoint := device.GetDeviceEndpoint() - + fmt.Printf("Connecting to: %s\n", endpoint) - + // Warn if using HTTPS if strings.HasPrefix(endpoint, "https://") { fmt.Println("⚠️ HTTPS endpoint detected - you may need to skip TLS verification for self-signed certificates") } - + username := c.readInputWithDefault("Username", "admin") - + fmt.Print("Password: ") password, _ := c.reader.ReadString('\n') password = strings.TrimSpace(password) @@ -309,14 +309,14 @@ func (c *CLI) connectToCamera() { fmt.Println("===================") endpoint := c.readInputWithDefault("Camera endpoint (http://ip:port/onvif/device_service)", "http://192.168.1.100/onvif/device_service") - + // Warn if using HTTPS if strings.HasPrefix(endpoint, "https://") { fmt.Println("⚠️ HTTPS endpoint detected - you may need to skip TLS verification for self-signed certificates") } - + username := c.readInputWithDefault("Username", "admin") - + fmt.Print("Password: ") password, _ := c.reader.ReadString('\n') password = strings.TrimSpace(password) @@ -442,7 +442,7 @@ func (c *CLI) getCapabilities(ctx context.Context) { } fmt.Println("✅ Device Capabilities:") - + if caps.Device != nil { fmt.Printf(" ✓ Device Service\n") } @@ -582,11 +582,11 @@ func (c *CLI) inspectRTSPStream(streamURI string) map[string]interface{} { if firstVideo := streamInfo.GetFirstVideoMedia(); firstVideo != nil { // Get codec format (H264, H265, MJPEG, etc.) details["codec"] = firstVideo.Format - + // Extract resolution directly from the video media if firstVideo.Resolution != nil { - details["resolution"] = fmt.Sprintf("%dx%d", - firstVideo.Resolution.Width, + details["resolution"] = fmt.Sprintf("%dx%d", + firstVideo.Resolution.Width, firstVideo.Resolution.Height) } else { // Fallback to resolution strings @@ -673,7 +673,7 @@ func (c *CLI) getStreamURIs(ctx context.Context) { fmt.Printf(" Stream URI: ❌ Error - %v\n", err) } else { fmt.Printf(" Stream URI: %s\n", streamURI.URI) - + // Warn if camera returns HTTPS when we connected via HTTP if strings.HasPrefix(c.client.Endpoint(), "http://") && strings.HasPrefix(streamURI.URI, "https://") { fmt.Printf(" ⚠️ WARNING: Camera returned HTTPS URL but you connected via HTTP\n") @@ -735,14 +735,14 @@ func (c *CLI) getSnapshotURIs(ctx context.Context) { fmt.Printf(" Snapshot URI: ❌ Error - %v\n", err) } else { fmt.Printf(" Snapshot URI: %s\n", snapshotURI.URI) - + // Warn if camera returns HTTPS when we connected via HTTP if strings.HasPrefix(c.client.Endpoint(), "http://") && strings.HasPrefix(snapshotURI.URI, "https://") { fmt.Printf(" ⚠️ WARNING: Camera returned HTTPS URL but you connected via HTTP\n") fmt.Printf(" 💡 Snapshot may fail due to TLS certificate issues\n") fmt.Printf(" 💡 Consider reconnecting with https:// endpoint and skip TLS verification\n") } - + fmt.Printf(" 🌐 Open this URL in a browser to see the snapshot\n") } fmt.Println() @@ -792,13 +792,13 @@ func (c *CLI) getVideoEncoderConfig(ctx context.Context) { fmt.Printf(" Token: %s\n", config.Token) fmt.Printf(" Use Count: %d\n", config.UseCount) 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(" Encoding Interval: %d\n", config.RateControl.EncodingInterval) @@ -888,7 +888,7 @@ func (c *CLI) getPTZStatus(ctx context.Context, profileToken string) { } fmt.Println("✅ PTZ Status:") - + if status.Position != nil { if status.Position.PanTilt != nil { fmt.Printf(" Pan: %.3f\n", status.Position.PanTilt.X) @@ -1035,10 +1035,10 @@ func (c *CLI) getPTZPresets(ctx context.Context, profileToken string) { fmt.Printf("📍 Preset #%d:\n", i+1) fmt.Printf(" Name: %s\n", preset.Name) fmt.Printf(" Token: %s\n", preset.Token) - + if preset.PTZPosition != nil { if preset.PTZPosition.PanTilt != nil { - fmt.Printf(" Pan: %.3f, Tilt: %.3f\n", + fmt.Printf(" Pan: %.3f, Tilt: %.3f\n", preset.PTZPosition.PanTilt.X, preset.PTZPosition.PanTilt.Y) } @@ -1161,11 +1161,11 @@ func (c *CLI) getImagingSettings(ctx context.Context, videoSourceToken string) { settings, err := c.client.GetImagingSettings(ctx, videoSourceToken) if err != nil { fmt.Printf("❌ Error: %v\n", err) - return + return } fmt.Println("✅ Current Imaging Settings:") - + if settings.Brightness != nil { fmt.Printf(" Brightness: %.1f\n", *settings.Brightness) } @@ -1284,7 +1284,7 @@ func (c *CLI) setSaturation(ctx context.Context, videoSourceToken string) { saturation, err := strconv.ParseFloat(saturationStr, 64) if err != nil { fmt.Println("❌ Invalid saturation value") - return + return } currentSettings.ColorSaturation = &saturation @@ -1313,7 +1313,7 @@ func (c *CLI) setSharpness(ctx context.Context, videoSourceToken string) { } sharpnessStr := c.readInputWithDefault(fmt.Sprintf("Sharpness (0-100, current: %s)", currentValue), currentValue) - sharpness, err := strconv.ParseFloat(sharpnessStr, 64) + sharpness, err := strconv.ParseFloat(sharpnessStr, 64) if err != nil { fmt.Println("❌ Invalid sharpness value") return @@ -1409,7 +1409,7 @@ func (c *CLI) captureAndDisplaySnapshot(ctx context.Context) { } profile := profiles[0] - + fmt.Println("⏳ Getting snapshot URI...") // Get snapshot URI from camera @@ -1515,4 +1515,4 @@ func (c *CLI) captureAndDisplaySnapshot(ctx context.Context) { fmt.Printf("✅ Snapshot saved to %s\n", filename) } } -} \ No newline at end of file +} diff --git a/cmd/onvif-quick/main.go b/cmd/onvif-quick/main.go index 18b88c1..36fca58 100644 --- a/cmd/onvif-quick/main.go +++ b/cmd/onvif-quick/main.go @@ -55,7 +55,7 @@ func main() { func discoverCameras() { reader := bufio.NewReader(os.Stdin) - + fmt.Println("🔍 Discovering cameras on network...") // Ask if user wants to use a specific interface @@ -150,7 +150,6 @@ func listNetworkInterfaces() { } } - func connectAndShowInfo() { reader := bufio.NewReader(os.Stdin) @@ -200,7 +199,7 @@ func connectAndShowInfo() { profiles, err := client.GetProfiles(ctx) if err == nil && len(profiles) > 0 { fmt.Printf("📺 %d profile(s) available\n", len(profiles)) - + // Show first stream URL streamURI, err := client.GetStreamURI(ctx, profiles[0].Token) if err == nil { @@ -228,7 +227,7 @@ func ptzDemo() { password = strings.TrimSpace(password) endpoint := fmt.Sprintf("http://%s/onvif/device_service", ip) - + client, err := onvif.NewClient( endpoint, onvif.WithCredentials(username, password), @@ -333,7 +332,7 @@ func getStreamURLs() { password = strings.TrimSpace(password) endpoint := fmt.Sprintf("http://%s/onvif/device_service", ip) - + client, err := onvif.NewClient( endpoint, onvif.WithCredentials(username, password), @@ -382,7 +381,7 @@ func getStreamURLs() { if profile.VideoEncoderConfiguration != nil { fmt.Printf(" 🎬 Encoding: %s", profile.VideoEncoderConfiguration.Encoding) if profile.VideoEncoderConfiguration.Resolution != nil { - fmt.Printf(" (%dx%d)", + fmt.Printf(" (%dx%d)", profile.VideoEncoderConfiguration.Resolution.Width, profile.VideoEncoderConfiguration.Resolution.Height) } @@ -396,4 +395,4 @@ func getStreamURLs() { fmt.Println(" - Use VLC to open RTSP streams") fmt.Println(" - Open snapshot URLs in a web browser") fmt.Println(" - Some cameras may require authentication in the URL") -} \ No newline at end of file +} diff --git a/cmd/onvif-server/main.go b/cmd/onvif-server/main.go index 442da7a..04b5eb5 100644 --- a/cmd/onvif-server/main.go +++ b/cmd/onvif-server/main.go @@ -158,7 +158,7 @@ func buildConfig(host string, port int, username, password, manufacturer, model, // Generate profiles for i := 0; i < numProfiles; i++ { template := templates[i%len(templates)] - + profile := server.ProfileConfig{ Token: fmt.Sprintf("profile_%d", i), Name: template.name, diff --git a/device.go b/device.go index 07fc933..4e7f28d 100644 --- a/device.go +++ b/device.go @@ -1092,4 +1092,3 @@ func (c *Client) SetNetworkDefaultGateway(ctx context.Context, gateway *NetworkG return nil } - diff --git a/device_additional.go b/device_additional.go index 1d2c4ad..e67d0c8 100644 --- a/device_additional.go +++ b/device_additional.go @@ -110,8 +110,8 @@ func (c *Client) GetDPAddresses(ctx context.Context) ([]NetworkHost, error) { } type GetDPAddressesResponse struct { - XMLName xml.Name `xml:"GetDPAddressesResponse"` - DPAddress []NetworkHost `xml:"DPAddress"` + XMLName xml.Name `xml:"GetDPAddressesResponse"` + DPAddress []NetworkHost `xml:"DPAddress"` } request := GetDPAddressesBody{ @@ -171,8 +171,8 @@ func (c *Client) GetAccessPolicy(ctx context.Context) (*AccessPolicy, error) { } type GetAccessPolicyResponse struct { - XMLName xml.Name `xml:"GetAccessPolicyResponse"` - PolicyFile *BinaryData `xml:"PolicyFile"` + XMLName xml.Name `xml:"GetAccessPolicyResponse"` + PolicyFile *BinaryData `xml:"PolicyFile"` } request := GetAccessPolicyBody{ diff --git a/device_additional_test.go b/device_additional_test.go index c3e051d..201e458 100644 --- a/device_additional_test.go +++ b/device_additional_test.go @@ -54,7 +54,7 @@ func newMockDeviceAdditionalServer() *httptest.Server { `)) case strings.Contains(bodyContent, "GetDPAddresses"): - _, _ = w.Write([]byte(` + _, _ = w.Write([]byte(` @@ -71,7 +71,7 @@ func newMockDeviceAdditionalServer() *httptest.Server { `)) case strings.Contains(bodyContent, "SetDPAddresses"): - _, _ = w.Write([]byte(` + _, _ = w.Write([]byte(` @@ -79,7 +79,7 @@ func newMockDeviceAdditionalServer() *httptest.Server { `)) case strings.Contains(bodyContent, "GetAccessPolicy"): - _, _ = w.Write([]byte(` + _, _ = w.Write([]byte(` @@ -92,7 +92,7 @@ func newMockDeviceAdditionalServer() *httptest.Server { `)) case strings.Contains(bodyContent, "SetAccessPolicy"): - _, _ = w.Write([]byte(` + _, _ = w.Write([]byte(` @@ -100,7 +100,7 @@ func newMockDeviceAdditionalServer() *httptest.Server { `)) case strings.Contains(bodyContent, "GetWsdlUrl"): - _, _ = w.Write([]byte(` + _, _ = w.Write([]byte(` diff --git a/device_certificates.go b/device_certificates.go index 4612e32..8575814 100644 --- a/device_certificates.go +++ b/device_certificates.go @@ -140,7 +140,7 @@ func (c *Client) CreateCertificate(ctx context.Context, certificateID, subject s } type CreateCertificateResponse struct { - XMLName xml.Name `xml:"CreateCertificateResponse"` + XMLName xml.Name `xml:"CreateCertificateResponse"` Certificate *Certificate `xml:"Certificate"` } @@ -204,7 +204,7 @@ func (c *Client) GetCertificateInformation(ctx context.Context, certificateID st } type GetCertificateInformationResponse struct { - XMLName xml.Name `xml:"GetCertificateInformationResponse"` + XMLName xml.Name `xml:"GetCertificateInformationResponse"` CertificateInformation *CertificateInformation `xml:"CertificateInformation"` } @@ -296,8 +296,8 @@ func (c *Client) GetPkcs10Request(ctx context.Context, certificateID, subject st } type GetPkcs10RequestResponse struct { - XMLName xml.Name `xml:"GetPkcs10RequestResponse"` - Pkcs10Request *BinaryData `xml:"Pkcs10Request"` + XMLName xml.Name `xml:"GetPkcs10RequestResponse"` + Pkcs10Request *BinaryData `xml:"Pkcs10Request"` } request := GetPkcs10RequestBody{ @@ -323,8 +323,8 @@ func (c *Client) GetPkcs10Request(ctx context.Context, certificateID, subject st // ONVIF Specification: LoadCertificateWithPrivateKey operation func (c *Client) LoadCertificateWithPrivateKey(ctx context.Context, certificates []*Certificate, privateKey []*BinaryData, certificateIDs []string) error { type LoadCertificateWithPrivateKeyBody struct { - XMLName xml.Name `xml:"tds:LoadCertificateWithPrivateKey"` - Xmlns string `xml:"xmlns:tds,attr"` + XMLName xml.Name `xml:"tds:LoadCertificateWithPrivateKey"` + Xmlns string `xml:"xmlns:tds,attr"` CertificateWithPrivateKey []struct { CertificateID string `xml:"CertificateID"` Certificate *Certificate `xml:"Certificate"` diff --git a/device_certificates_test.go b/device_certificates_test.go index f4391c9..728392d 100644 --- a/device_certificates_test.go +++ b/device_certificates_test.go @@ -14,8 +14,8 @@ func newMockDeviceCertificatesServer() *httptest.Server { w.Header().Set("Content-Type", "application/soap+xml") // Parse request to determine which operation - buf := make([]byte, r.ContentLength) - _, _ = r.Body.Read(buf) + buf := make([]byte, r.ContentLength) + _, _ = r.Body.Read(buf) requestBody := string(buf) var response string diff --git a/device_extended.go b/device_extended.go index ce32b47..1784a29 100644 --- a/device_extended.go +++ b/device_extended.go @@ -131,7 +131,7 @@ func (c *Client) FixedGetSystemDateAndTime(ctx context.Context) (*SystemDateTime } type GetSystemDateAndTimeResponse struct { - XMLName xml.Name `xml:"GetSystemDateAndTimeResponse"` + XMLName xml.Name `xml:"GetSystemDateAndTimeResponse"` SystemDateAndTime struct { DateTimeType string `xml:"DateTimeType"` DaylightSavings bool `xml:"DaylightSavings"` @@ -406,10 +406,10 @@ func (c *Client) GetRelayOutputs(ctx context.Context) ([]*RelayOutput, error) { // SetRelayOutputSettings sets the settings of a relay output func (c *Client) SetRelayOutputSettings(ctx context.Context, token string, settings *RelayOutputSettings) error { type SetRelayOutputSettings struct { - XMLName xml.Name `xml:"tds:SetRelayOutputSettings"` - Xmlns string `xml:"xmlns:tds,attr"` + XMLName xml.Name `xml:"tds:SetRelayOutputSettings"` + Xmlns string `xml:"xmlns:tds,attr"` RelayOutputToken string `xml:"tds:RelayOutputToken"` - Properties struct { + Properties struct { Mode string `xml:"tt:Mode"` DelayTime string `xml:"tt:DelayTime"` IdleState string `xml:"tt:IdleState"` @@ -417,7 +417,7 @@ func (c *Client) SetRelayOutputSettings(ctx context.Context, token string, setti } req := SetRelayOutputSettings{ - Xmlns: deviceNamespace, + Xmlns: deviceNamespace, RelayOutputToken: token, } req.Properties.Mode = string(settings.Mode) @@ -437,16 +437,16 @@ func (c *Client) SetRelayOutputSettings(ctx context.Context, token string, setti // SetRelayOutputState sets the state of a relay output func (c *Client) SetRelayOutputState(ctx context.Context, token string, state RelayLogicalState) error { type SetRelayOutputState struct { - XMLName xml.Name `xml:"tds:SetRelayOutputState"` - Xmlns string `xml:"xmlns:tds,attr"` + XMLName xml.Name `xml:"tds:SetRelayOutputState"` + Xmlns string `xml:"xmlns:tds,attr"` RelayOutputToken string `xml:"tds:RelayOutputToken"` - LogicalState RelayLogicalState `xml:"tds:LogicalState"` + LogicalState RelayLogicalState `xml:"tds:LogicalState"` } req := SetRelayOutputState{ - Xmlns: deviceNamespace, + Xmlns: deviceNamespace, RelayOutputToken: token, - LogicalState: state, + LogicalState: state, } username, password := c.GetCredentials() @@ -628,8 +628,8 @@ func (c *Client) GetSystemUris(ctx context.Context) (*SystemLogUriList, string, } type GetSystemUrisResponse struct { - XMLName xml.Name `xml:"GetSystemUrisResponse"` - SystemLogUris *struct { + XMLName xml.Name `xml:"GetSystemUrisResponse"` + SystemLogUris *struct { SystemLog []struct { Type string `xml:"Type"` Uri string `xml:"Uri"` diff --git a/device_extended_test.go b/device_extended_test.go index f30dec8..6c70be5 100644 --- a/device_extended_test.go +++ b/device_extended_test.go @@ -24,7 +24,7 @@ func newMockDeviceExtendedServer() *httptest.Server { switch { case strings.Contains(bodyContent, "AddScopes"): - _, _ = w.Write([]byte(` + _, _ = w.Write([]byte(` @@ -32,7 +32,7 @@ func newMockDeviceExtendedServer() *httptest.Server { `)) case strings.Contains(bodyContent, "RemoveScopes"): - _, _ = w.Write([]byte(` + _, _ = w.Write([]byte(` @@ -42,7 +42,7 @@ func newMockDeviceExtendedServer() *httptest.Server { `)) case strings.Contains(bodyContent, "SetScopes"): - _, _ = w.Write([]byte(` + _, _ = w.Write([]byte(` @@ -50,7 +50,7 @@ func newMockDeviceExtendedServer() *httptest.Server { `)) case strings.Contains(bodyContent, "GetRelayOutputs"): - _, _ = w.Write([]byte(` + _, _ = w.Write([]byte(` @@ -66,7 +66,7 @@ func newMockDeviceExtendedServer() *httptest.Server { `)) case strings.Contains(bodyContent, "SetRelayOutputSettings"): - _, _ = w.Write([]byte(` + _, _ = w.Write([]byte(` @@ -74,7 +74,7 @@ func newMockDeviceExtendedServer() *httptest.Server { `)) case strings.Contains(bodyContent, "SetRelayOutputState"): - _, _ = w.Write([]byte(` + _, _ = w.Write([]byte(` @@ -82,7 +82,7 @@ func newMockDeviceExtendedServer() *httptest.Server { `)) case strings.Contains(bodyContent, "SendAuxiliaryCommand"): - _, _ = w.Write([]byte(` + _, _ = w.Write([]byte(` @@ -92,7 +92,7 @@ func newMockDeviceExtendedServer() *httptest.Server { `)) case strings.Contains(bodyContent, "GetSystemLog"): - _, _ = w.Write([]byte(` + _, _ = w.Write([]byte(` @@ -104,7 +104,7 @@ func newMockDeviceExtendedServer() *httptest.Server { `)) case strings.Contains(bodyContent, "SetSystemFactoryDefault"): - _, _ = w.Write([]byte(` + _, _ = w.Write([]byte(` @@ -112,7 +112,7 @@ func newMockDeviceExtendedServer() *httptest.Server { `)) case strings.Contains(bodyContent, "StartFirmwareUpgrade"): - _, _ = w.Write([]byte(` + _, _ = w.Write([]byte(` diff --git a/device_security.go b/device_security.go index 9bed272..8ca0059 100644 --- a/device_security.go +++ b/device_security.go @@ -370,7 +370,7 @@ func (c *Client) GetDynamicDNS(ctx context.Context) (*DynamicDNSInformation, err } type GetDynamicDNSResponse struct { - XMLName xml.Name `xml:"GetDynamicDNSResponse"` + XMLName xml.Name `xml:"GetDynamicDNSResponse"` DynamicDNSInformation struct { Type string `xml:"Type"` Name string `xml:"Name"` @@ -431,13 +431,13 @@ func (c *Client) GetPasswordComplexityConfiguration(ctx context.Context) (*Passw } type GetPasswordComplexityConfigurationResponse struct { - XMLName xml.Name `xml:"GetPasswordComplexityConfigurationResponse"` - MinLen int `xml:"MinLen"` - Uppercase int `xml:"Uppercase"` - Number int `xml:"Number"` - SpecialChars int `xml:"SpecialChars"` - BlockUsernameOccurrence bool `xml:"BlockUsernameOccurrence"` - PolicyConfigurationLocked bool `xml:"PolicyConfigurationLocked"` + XMLName xml.Name `xml:"GetPasswordComplexityConfigurationResponse"` + MinLen int `xml:"MinLen"` + Uppercase int `xml:"Uppercase"` + Number int `xml:"Number"` + SpecialChars int `xml:"SpecialChars"` + BlockUsernameOccurrence bool `xml:"BlockUsernameOccurrence"` + PolicyConfigurationLocked bool `xml:"PolicyConfigurationLocked"` } req := GetPasswordComplexityConfiguration{ diff --git a/device_security_test.go b/device_security_test.go index 9164d62..b35c326 100644 --- a/device_security_test.go +++ b/device_security_test.go @@ -24,7 +24,7 @@ func newMockDeviceSecurityServer() *httptest.Server { switch { case strings.Contains(bodyContent, "GetRemoteUser"): - _, _ = w.Write([]byte(` + _, _ = w.Write([]byte(` @@ -38,7 +38,7 @@ func newMockDeviceSecurityServer() *httptest.Server { `)) case strings.Contains(bodyContent, "SetRemoteUser"): - _, _ = w.Write([]byte(` + _, _ = w.Write([]byte(` @@ -46,7 +46,7 @@ func newMockDeviceSecurityServer() *httptest.Server { `)) case strings.Contains(bodyContent, "GetIPAddressFilter"): - _, _ = w.Write([]byte(` + _, _ = w.Write([]byte(` @@ -61,10 +61,10 @@ func newMockDeviceSecurityServer() *httptest.Server { `)) - case strings.Contains(bodyContent, "SetIPAddressFilter"), - strings.Contains(bodyContent, "AddIPAddressFilter"), - strings.Contains(bodyContent, "RemoveIPAddressFilter"): - _, _ = w.Write([]byte(` + case strings.Contains(bodyContent, "SetIPAddressFilter"), + strings.Contains(bodyContent, "AddIPAddressFilter"), + strings.Contains(bodyContent, "RemoveIPAddressFilter"): + _, _ = w.Write([]byte(` @@ -72,7 +72,7 @@ func newMockDeviceSecurityServer() *httptest.Server { `)) case strings.Contains(bodyContent, "GetZeroConfiguration"): - _, _ = w.Write([]byte(` + _, _ = w.Write([]byte(` @@ -86,7 +86,7 @@ func newMockDeviceSecurityServer() *httptest.Server { `)) case strings.Contains(bodyContent, "SetZeroConfiguration"): - _, _ = w.Write([]byte(` + _, _ = w.Write([]byte(` @@ -94,7 +94,7 @@ func newMockDeviceSecurityServer() *httptest.Server { `)) case strings.Contains(bodyContent, "GetPasswordComplexityConfiguration"): - _, _ = w.Write([]byte(` + _, _ = w.Write([]byte(` @@ -109,7 +109,7 @@ func newMockDeviceSecurityServer() *httptest.Server { `)) case strings.Contains(bodyContent, "SetPasswordComplexityConfiguration"): - _, _ = w.Write([]byte(` + _, _ = w.Write([]byte(` @@ -117,7 +117,7 @@ func newMockDeviceSecurityServer() *httptest.Server { `)) case strings.Contains(bodyContent, "GetPasswordHistoryConfiguration"): - _, _ = w.Write([]byte(` + _, _ = w.Write([]byte(` @@ -128,7 +128,7 @@ func newMockDeviceSecurityServer() *httptest.Server { `)) case strings.Contains(bodyContent, "SetPasswordHistoryConfiguration"): - _, _ = w.Write([]byte(` + _, _ = w.Write([]byte(` @@ -136,7 +136,7 @@ func newMockDeviceSecurityServer() *httptest.Server { `)) case strings.Contains(bodyContent, "GetAuthFailureWarningConfiguration"): - _, _ = w.Write([]byte(` + _, _ = w.Write([]byte(` @@ -148,7 +148,7 @@ func newMockDeviceSecurityServer() *httptest.Server { `)) case strings.Contains(bodyContent, "SetAuthFailureWarningConfiguration"): - _, _ = w.Write([]byte(` + _, _ = w.Write([]byte(` diff --git a/device_storage_test.go b/device_storage_test.go index 7aa18ca..f078f68 100644 --- a/device_storage_test.go +++ b/device_storage_test.go @@ -13,8 +13,8 @@ func newMockDeviceStorageServer() *httptest.Server { w.Header().Set("Content-Type", "application/soap+xml") // Parse request to determine which operation - buf := make([]byte, r.ContentLength) - _, _ = r.Body.Read(buf) + buf := make([]byte, r.ContentLength) + _, _ = r.Body.Read(buf) requestBody := string(buf) var response string diff --git a/device_wifi.go b/device_wifi.go index d6c9d8a..04b09b1 100644 --- a/device_wifi.go +++ b/device_wifi.go @@ -18,7 +18,7 @@ func (c *Client) GetDot11Capabilities(ctx context.Context) (*Dot11Capabilities, } type GetDot11CapabilitiesResponse struct { - XMLName xml.Name `xml:"GetDot11CapabilitiesResponse"` + XMLName xml.Name `xml:"GetDot11CapabilitiesResponse"` Capabilities *Dot11Capabilities `xml:"Capabilities"` } @@ -73,14 +73,14 @@ func (c *Client) GetDot11Status(ctx context.Context, interfaceToken string) (*Do // ONVIF Specification: GetDot1XConfiguration operation func (c *Client) GetDot1XConfiguration(ctx context.Context, configToken string) (*Dot1XConfiguration, error) { type GetDot1XConfigurationBody struct { - XMLName xml.Name `xml:"tds:GetDot1XConfiguration"` - Xmlns string `xml:"xmlns:tds,attr"` - Dot1XConfigurationToken string `xml:"tds:Dot1XConfigurationToken"` + XMLName xml.Name `xml:"tds:GetDot1XConfiguration"` + Xmlns string `xml:"xmlns:tds,attr"` + Dot1XConfigurationToken string `xml:"tds:Dot1XConfigurationToken"` } type GetDot1XConfigurationResponse struct { - XMLName xml.Name `xml:"GetDot1XConfigurationResponse"` - Dot1XConfiguration *Dot1XConfiguration `xml:"Dot1XConfiguration"` + XMLName xml.Name `xml:"GetDot1XConfigurationResponse"` + Dot1XConfiguration *Dot1XConfiguration `xml:"Dot1XConfiguration"` } request := GetDot1XConfigurationBody{ @@ -109,8 +109,8 @@ func (c *Client) GetDot1XConfigurations(ctx context.Context) ([]*Dot1XConfigurat } type GetDot1XConfigurationsResponse struct { - XMLName xml.Name `xml:"GetDot1XConfigurationsResponse"` - Dot1XConfiguration []*Dot1XConfiguration `xml:"Dot1XConfiguration"` + XMLName xml.Name `xml:"GetDot1XConfigurationsResponse"` + Dot1XConfiguration []*Dot1XConfiguration `xml:"Dot1XConfiguration"` } request := GetDot1XConfigurationsBody{ @@ -193,9 +193,9 @@ func (c *Client) CreateDot1XConfiguration(ctx context.Context, config *Dot1XConf // ONVIF Specification: DeleteDot1XConfiguration operation func (c *Client) DeleteDot1XConfiguration(ctx context.Context, configToken string) error { type DeleteDot1XConfigurationBody struct { - XMLName xml.Name `xml:"tds:DeleteDot1XConfiguration"` - Xmlns string `xml:"xmlns:tds,attr"` - Dot1XConfigurationToken string `xml:"tds:Dot1XConfigurationToken"` + XMLName xml.Name `xml:"tds:DeleteDot1XConfiguration"` + Xmlns string `xml:"xmlns:tds,attr"` + Dot1XConfigurationToken string `xml:"tds:Dot1XConfigurationToken"` } type DeleteDot1XConfigurationResponse struct { diff --git a/device_wifi_test.go b/device_wifi_test.go index 9fe7cf3..98b81f5 100644 --- a/device_wifi_test.go +++ b/device_wifi_test.go @@ -13,8 +13,8 @@ func newMockDeviceWiFiServer() *httptest.Server { w.Header().Set("Content-Type", "application/soap+xml") // Parse request to determine which operation - buf := make([]byte, r.ContentLength) - _, _ = r.Body.Read(buf) + buf := make([]byte, r.ContentLength) + _, _ = r.Body.Read(buf) requestBody := string(buf) var response string diff --git a/discovery/discovery.go b/discovery/discovery.go index 54e3802..67b5c14 100644 --- a/discovery/discovery.go +++ b/discovery/discovery.go @@ -12,7 +12,7 @@ import ( const ( // WS-Discovery multicast address multicastAddr = "239.255.255.250:3702" - + // WS-Discovery probe message probeTemplate = ` @@ -36,16 +36,16 @@ const ( type Device struct { // Device endpoint address EndpointRef string - + // XAddrs contains the device service addresses XAddrs []string - + // Types contains the device types Types []string - + // Scopes contains the device scopes (name, location, etc.) Scopes []string - + // Metadata version MetadataVersion int } @@ -62,8 +62,8 @@ type ProbeMatch struct { // ProbeMatches represents WS-Discovery probe matches type ProbeMatches struct { - XMLName xml.Name `xml:"ProbeMatches"` - ProbeMatch []ProbeMatch `xml:"ProbeMatch"` + XMLName xml.Name `xml:"ProbeMatches"` + ProbeMatch []ProbeMatch `xml:"ProbeMatch"` } // DiscoverOptions contains options for device discovery @@ -72,7 +72,7 @@ type DiscoverOptions struct { // If empty, the system will choose the default interface. // Examples: "eth0", "wlan0", "192.168.1.100" NetworkInterface string - + // Context and timeout are handled by the caller } @@ -308,13 +308,13 @@ func ListNetworkInterfaces() ([]NetworkInterface, error) { type NetworkInterface struct { // Name of the interface (e.g., "eth0", "wlan0") Name string - + // IP addresses assigned to this interface Addresses []string - + // Up indicates if the interface is up Up bool - + // Multicast indicates if the interface supports multicast Multicast bool } @@ -324,7 +324,7 @@ func (d *Device) GetDeviceEndpoint() string { if len(d.XAddrs) == 0 { return "" } - + // Return the first XAddr return d.XAddrs[0] } diff --git a/examples/discovery/main.go b/examples/discovery/main.go index 12662cb..8558ae2 100644 --- a/examples/discovery/main.go +++ b/examples/discovery/main.go @@ -11,7 +11,7 @@ import ( func main() { fmt.Println("Discovering ONVIF devices on the network...") - + // Create a context with timeout ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) defer cancel() diff --git a/examples/imaging-settings/main.go b/examples/imaging-settings/main.go index e25361f..ce6d80b 100644 --- a/examples/imaging-settings/main.go +++ b/examples/imaging-settings/main.go @@ -100,15 +100,15 @@ func main() { // Modify some settings fmt.Println("\n\nModifying imaging settings...") - + // Increase brightness newBrightness := 60.0 settings.Brightness = &newBrightness - + // Increase contrast newContrast := 55.0 settings.Contrast = &newContrast - + // Set to auto exposure if settings.Exposure != nil { settings.Exposure.Mode = "AUTO" diff --git a/examples/ptz-control/main.go b/examples/ptz-control/main.go index e21dde0..ed3cfc1 100644 --- a/examples/ptz-control/main.go +++ b/examples/ptz-control/main.go @@ -89,7 +89,7 @@ func demonstratePTZ(ctx context.Context, client *onvif.Client, profileToken stri fmt.Println("Moving camera right...") velocity := &onvif.PTZSpeed{ PanTilt: &onvif.Vector2D{ - X: 0.5, // Move right + X: 0.5, // Move right Y: 0.0, }, } diff --git a/examples/test-server/main.go b/examples/test-server/main.go index a48ebda..411a1cf 100644 --- a/examples/test-server/main.go +++ b/examples/test-server/main.go @@ -73,7 +73,7 @@ func main() { for i, profile := range profiles { fmt.Printf("\n Profile %d: %s\n", i+1, profile.Name) fmt.Printf(" Token: %s\n", profile.Token) - + if profile.VideoEncoderConfiguration != nil { fmt.Printf(" Video: %dx%d @ %s\n", profile.VideoEncoderConfiguration.Resolution.Width, @@ -98,7 +98,7 @@ func main() { // Test PTZ if available if profile.PTZConfiguration != nil { fmt.Println(" PTZ: ✓ Enabled") - + // Get PTZ status status, err := client.GetStatus(ctx, profile.Token) if err == nil { @@ -121,7 +121,7 @@ func main() { if len(profiles) > 0 && profiles[0].PTZConfiguration != nil { fmt.Println("🎮 Test 5: Testing PTZ Control...") profileToken := profiles[0].Token - + // Absolute move to home position fmt.Println(" Moving to home position...") position := &onvif.PTZVector{ diff --git a/internal/soap/soap_test.go b/internal/soap/soap_test.go index 4078587..9015bc6 100644 --- a/internal/soap/soap_test.go +++ b/internal/soap/soap_test.go @@ -63,18 +63,18 @@ func TestBuildEnvelope(t *testing.T) { wantErr bool }{ { - name: "with authentication", - body: &testRequest{Value: "test"}, + name: "with authentication", + body: &testRequest{Value: "test"}, username: "admin", password: "password", - wantErr: false, + wantErr: false, }, { - name: "without authentication", - body: &testRequest{Value: "test"}, + name: "without authentication", + body: &testRequest{Value: "test"}, username: "", password: "", - wantErr: false, + wantErr: false, }, } diff --git a/server/device.go b/server/device.go index 8f49a25..ef7311e 100644 --- a/server/device.go +++ b/server/device.go @@ -45,10 +45,10 @@ type AnalyticsCapabilities struct { // DeviceCapabilities represents device service capabilities type DeviceCapabilities struct { - XAddr string `xml:"XAddr"` - Network *NetworkCapabilities `xml:"Network,omitempty"` - System *SystemCapabilities `xml:"System,omitempty"` - IO *IOCapabilities `xml:"IO,omitempty"` + XAddr string `xml:"XAddr"` + Network *NetworkCapabilities `xml:"Network,omitempty"` + System *SystemCapabilities `xml:"System,omitempty"` + IO *IOCapabilities `xml:"IO,omitempty"` Security *SecurityCapabilities `xml:"Security,omitempty"` } @@ -62,12 +62,12 @@ type NetworkCapabilities struct { // SystemCapabilities represents system capabilities type SystemCapabilities struct { - DiscoveryResolve bool `xml:"DiscoveryResolve,attr"` - DiscoveryBye bool `xml:"DiscoveryBye,attr"` - RemoteDiscovery bool `xml:"RemoteDiscovery,attr"` - SystemBackup bool `xml:"SystemBackup,attr"` - SystemLogging bool `xml:"SystemLogging,attr"` - FirmwareUpgrade bool `xml:"FirmwareUpgrade,attr"` + DiscoveryResolve bool `xml:"DiscoveryResolve,attr"` + DiscoveryBye bool `xml:"DiscoveryBye,attr"` + RemoteDiscovery bool `xml:"RemoteDiscovery,attr"` + SystemBackup bool `xml:"SystemBackup,attr"` + SystemLogging bool `xml:"SystemLogging,attr"` + FirmwareUpgrade bool `xml:"FirmwareUpgrade,attr"` } // IOCapabilities represents I/O capabilities @@ -127,8 +127,8 @@ type GetServicesResponse struct { // Service represents a service type Service struct { - Namespace string `xml:"Namespace"` - XAddr string `xml:"XAddr"` + Namespace string `xml:"Namespace"` + XAddr string `xml:"XAddr"` Version Version `xml:"Version"` } diff --git a/server/imaging.go b/server/imaging.go index 296b243..07032e1 100644 --- a/server/imaging.go +++ b/server/imaging.go @@ -42,18 +42,18 @@ type BacklightCompensationSettings struct { // ExposureSettings20 represents exposure settings for ONVIF 2.0 type ExposureSettings20 struct { - Mode string `xml:"Mode"` - Priority *string `xml:"Priority,omitempty"` + Mode string `xml:"Mode"` + Priority *string `xml:"Priority,omitempty"` Window *Rectangle `xml:"Window,omitempty"` - MinExposureTime *float64 `xml:"MinExposureTime,omitempty"` - MaxExposureTime *float64 `xml:"MaxExposureTime,omitempty"` - MinGain *float64 `xml:"MinGain,omitempty"` - MaxGain *float64 `xml:"MaxGain,omitempty"` - MinIris *float64 `xml:"MinIris,omitempty"` - MaxIris *float64 `xml:"MaxIris,omitempty"` - ExposureTime *float64 `xml:"ExposureTime,omitempty"` - Gain *float64 `xml:"Gain,omitempty"` - Iris *float64 `xml:"Iris,omitempty"` + MinExposureTime *float64 `xml:"MinExposureTime,omitempty"` + MaxExposureTime *float64 `xml:"MaxExposureTime,omitempty"` + MinGain *float64 `xml:"MinGain,omitempty"` + MaxGain *float64 `xml:"MaxGain,omitempty"` + MinIris *float64 `xml:"MinIris,omitempty"` + MaxIris *float64 `xml:"MaxIris,omitempty"` + ExposureTime *float64 `xml:"ExposureTime,omitempty"` + Gain *float64 `xml:"Gain,omitempty"` + Iris *float64 `xml:"Iris,omitempty"` } // FocusConfiguration20 represents focus configuration for ONVIF 2.0 @@ -168,15 +168,15 @@ type WhiteBalanceOptions struct { // MoveRequest represents Move (focus) request type MoveRequest struct { - XMLName xml.Name `xml:"http://www.onvif.org/ver20/imaging/wsdl Move"` - VideoSourceToken string `xml:"VideoSourceToken"` - Focus *FocusMove `xml:"Focus"` + XMLName xml.Name `xml:"http://www.onvif.org/ver20/imaging/wsdl Move"` + VideoSourceToken string `xml:"VideoSourceToken"` + Focus *FocusMove `xml:"Focus"` } // FocusMove represents focus move parameters type FocusMove struct { - Absolute *AbsoluteFocus `xml:"Absolute,omitempty"` - Relative *RelativeFocus `xml:"Relative,omitempty"` + Absolute *AbsoluteFocus `xml:"Absolute,omitempty"` + Relative *RelativeFocus `xml:"Relative,omitempty"` Continuous *ContinuousFocus `xml:"Continuous,omitempty"` } @@ -342,10 +342,10 @@ func (s *Server) HandleSetImagingSettings(body interface{}) (interface{}, error) func (s *Server) HandleGetOptions(body interface{}) (interface{}, error) { // Return available imaging options/capabilities options := &ImagingOptions{ - Brightness: &FloatRange{Min: 0, Max: 100}, - ColorSaturation: &FloatRange{Min: 0, Max: 100}, - Contrast: &FloatRange{Min: 0, Max: 100}, - Sharpness: &FloatRange{Min: 0, Max: 100}, + Brightness: &FloatRange{Min: 0, Max: 100}, + ColorSaturation: &FloatRange{Min: 0, Max: 100}, + Contrast: &FloatRange{Min: 0, Max: 100}, + Sharpness: &FloatRange{Min: 0, Max: 100}, IrCutFilterModes: []string{"ON", "OFF", "AUTO"}, BacklightCompensation: &BacklightCompensationOptions{ Mode: []string{"OFF", "ON"}, diff --git a/server/media.go b/server/media.go index b7b8799..e39555e 100644 --- a/server/media.go +++ b/server/media.go @@ -9,7 +9,7 @@ import ( // GetProfilesResponse represents GetProfiles response type GetProfilesResponse struct { - XMLName xml.Name `xml:"http://www.onvif.org/ver10/media/wsdl GetProfilesResponse"` + XMLName xml.Name `xml:"http://www.onvif.org/ver10/media/wsdl GetProfilesResponse"` Profiles []MediaProfile `xml:"Profiles"` } @@ -46,16 +46,16 @@ type AudioSourceConfiguration struct { // VideoEncoderConfiguration represents video encoder configuration type VideoEncoderConfiguration struct { - Token string `xml:"token,attr"` - Name string `xml:"Name"` - UseCount int `xml:"UseCount"` - Encoding string `xml:"Encoding"` - Resolution VideoResolution `xml:"Resolution"` - Quality float64 `xml:"Quality"` - RateControl *VideoRateControl `xml:"RateControl,omitempty"` - H264 *H264Configuration `xml:"H264,omitempty"` + Token string `xml:"token,attr"` + Name string `xml:"Name"` + UseCount int `xml:"UseCount"` + Encoding string `xml:"Encoding"` + Resolution VideoResolution `xml:"Resolution"` + Quality float64 `xml:"Quality"` + RateControl *VideoRateControl `xml:"RateControl,omitempty"` + H264 *H264Configuration `xml:"H264,omitempty"` Multicast *MulticastConfiguration `xml:"Multicast,omitempty"` - SessionTimeout string `xml:"SessionTimeout"` + SessionTimeout string `xml:"SessionTimeout"` } // AudioEncoderConfiguration represents audio encoder configuration @@ -130,7 +130,7 @@ type MulticastConfiguration struct { // IPAddress represents an IP address type IPAddress struct { - Type string `xml:"Type"` + Type string `xml:"Type"` IPv4Address string `xml:"IPv4Address,omitempty"` IPv6Address string `xml:"IPv6Address,omitempty"` } diff --git a/server/ptz.go b/server/ptz.go index 9d1b779..472666a 100644 --- a/server/ptz.go +++ b/server/ptz.go @@ -75,9 +75,9 @@ type GetStatusResponse struct { // PTZStatus represents PTZ status type PTZStatus struct { - Position PTZVector `xml:"Position"` - MoveStatus PTZMoveStatus `xml:"MoveStatus"` - UTCTime string `xml:"UtcTime"` + Position PTZVector `xml:"Position"` + MoveStatus PTZMoveStatus `xml:"MoveStatus"` + UTCTime string `xml:"UtcTime"` } // PTZMoveStatus represents PTZ movement status @@ -113,7 +113,7 @@ type GetPresetsRequest struct { // GetPresetsResponse represents GetPresets response type GetPresetsResponse struct { - XMLName xml.Name `xml:"http://www.onvif.org/ver20/ptz/wsdl GetPresetsResponse"` + XMLName xml.Name `xml:"http://www.onvif.org/ver20/ptz/wsdl GetPresetsResponse"` Preset []PTZPreset `xml:"Preset"` } @@ -153,16 +153,16 @@ type SetPresetResponse struct { // GetConfigurationsResponse represents GetConfigurations response type GetConfigurationsResponse struct { - XMLName xml.Name `xml:"http://www.onvif.org/ver20/ptz/wsdl GetConfigurationsResponse"` + XMLName xml.Name `xml:"http://www.onvif.org/ver20/ptz/wsdl GetConfigurationsResponse"` PTZConfiguration []PTZConfigurationExt `xml:"PTZConfiguration"` } // PTZConfigurationExt represents PTZ configuration with extensions type PTZConfigurationExt struct { - Token string `xml:"token,attr"` - Name string `xml:"Name"` - UseCount int `xml:"UseCount"` - NodeToken string `xml:"NodeToken"` + Token string `xml:"token,attr"` + Name string `xml:"Name"` + UseCount int `xml:"UseCount"` + NodeToken string `xml:"NodeToken"` PanTiltLimits *PanTiltLimits `xml:"PanTiltLimits,omitempty"` ZoomLimits *ZoomLimits `xml:"ZoomLimits,omitempty"` } diff --git a/server/server.go b/server/server.go index ec5b68c..169dfbd 100644 --- a/server/server.go +++ b/server/server.go @@ -27,14 +27,14 @@ func New(config *Config) (*Server, error) { for i := range config.Profiles { profile := &config.Profiles[i] streamPath := fmt.Sprintf("/stream%d", i) - + host := config.Host if host == "0.0.0.0" || host == "" { host = "localhost" } - + streamURI := fmt.Sprintf("rtsp://%s:8554%s", host, streamPath) - + server.streams[profile.Token] = &StreamConfig{ ProfileToken: profile.Token, RTSPPath: streamPath, @@ -104,11 +104,11 @@ func (s *Server) Start(ctx context.Context) error { // Register service handlers s.registerDeviceService(mux) s.registerMediaService(mux) - + if s.config.SupportPTZ { s.registerPTZService(mux) } - + if s.config.SupportImaging { s.registerImagingService(mux) } diff --git a/server/soap/handler.go b/server/soap/handler.go index b54495e..4854dfa 100644 --- a/server/soap/handler.go +++ b/server/soap/handler.go @@ -130,7 +130,7 @@ func (h *Handler) extractAction(bodyXML []byte) string { decoder := xml.NewDecoder(bytes.NewReader(bodyXML)) inBody := false depth := 0 - + for { token, err := decoder.Token() if err != nil { @@ -241,17 +241,17 @@ type GetSystemDateAndTimeRequest struct { // GetSystemDateAndTimeResponse represents GetSystemDateAndTime response type GetSystemDateAndTimeResponse struct { - XMLName xml.Name `xml:"http://www.onvif.org/ver10/device/wsdl GetSystemDateAndTimeResponse"` + XMLName xml.Name `xml:"http://www.onvif.org/ver10/device/wsdl GetSystemDateAndTimeResponse"` SystemDateAndTime SystemDateAndTime `xml:"SystemDateAndTime"` } // SystemDateAndTime represents system date and time type SystemDateAndTime struct { - DateTimeType string `xml:"DateTimeType"` - DaylightSavings bool `xml:"DaylightSavings"` - TimeZone TimeZone `xml:"TimeZone,omitempty"` - UTCDateTime DateTime `xml:"UTCDateTime,omitempty"` - LocalDateTime DateTime `xml:"LocalDateTime,omitempty"` + DateTimeType string `xml:"DateTimeType"` + DaylightSavings bool `xml:"DaylightSavings"` + TimeZone TimeZone `xml:"TimeZone,omitempty"` + UTCDateTime DateTime `xml:"UTCDateTime,omitempty"` + LocalDateTime DateTime `xml:"LocalDateTime,omitempty"` } // TimeZone represents timezone information diff --git a/server/types.go b/server/types.go index badb83a..ab4606f 100644 --- a/server/types.go +++ b/server/types.go @@ -88,15 +88,15 @@ type AudioEncoderConfig struct { // PTZConfig represents PTZ configuration type PTZConfig struct { - NodeToken string // PTZ node token - PanRange Range // Pan range in degrees - TiltRange Range // Tilt range in degrees - ZoomRange Range // Zoom range - DefaultSpeed PTZSpeed // Default speed + NodeToken string // PTZ node token + PanRange Range // Pan range in degrees + TiltRange Range // Tilt range in degrees + ZoomRange Range // Zoom range + DefaultSpeed PTZSpeed // Default speed SupportsContinuous bool // Supports continuous move SupportsAbsolute bool // Supports absolute move SupportsRelative bool // Supports relative move - Presets []Preset // Predefined presets + Presets []Preset // Predefined presets } // SnapshotConfig represents snapshot configuration @@ -195,8 +195,8 @@ type BacklightCompensation struct { // ExposureSettings represents exposure settings type ExposureSettings struct { - Mode string // AUTO, MANUAL - Priority string // LowNoise, FrameRate + Mode string // AUTO, MANUAL + Priority string // LowNoise, FrameRate MinExposure float64 MaxExposure float64 MinGain float64 @@ -207,7 +207,7 @@ type ExposureSettings struct { // FocusSettings represents focus settings type FocusSettings struct { - AutoFocusMode string // AUTO, MANUAL + AutoFocusMode string // AUTO, MANUAL DefaultSpeed float64 NearLimit float64 FarLimit float64 @@ -216,7 +216,7 @@ type FocusSettings struct { // WhiteBalanceSettings represents white balance settings type WhiteBalanceSettings struct { - Mode string // AUTO, MANUAL + Mode string // AUTO, MANUAL CrGain float64 CbGain float64 } diff --git a/testdata/captures/enhanced_device_features_test.go b/testdata/captures/enhanced_device_features_test.go index 7542b6d..42efa16 100644 --- a/testdata/captures/enhanced_device_features_test.go +++ b/testdata/captures/enhanced_device_features_test.go @@ -1,5 +1,4 @@ package onvif -package captures import ( "context" diff --git a/types.go b/types.go index 36657eb..c8b93fc 100644 --- a/types.go +++ b/types.go @@ -872,9 +872,9 @@ const ( // RemoteUser represents remote user configuration type RemoteUser struct { - Username string - Password string - UseDerivedPassword bool + Username string + Password string + UseDerivedPassword bool } // Certificate represents a certificate @@ -917,17 +917,17 @@ type CertificateUsage struct { // DateTimeRange represents date/time range type DateTimeRange struct { - From time.Time + From time.Time Until time.Time } // Dot11Capabilities represents 802.11 capabilities type Dot11Capabilities struct { - TKIP bool - ScanAvailableNetworks bool - MultipleConfiguration bool - AdHocStationMode bool - WEP bool + TKIP bool + ScanAvailableNetworks bool + MultipleConfiguration bool + AdHocStationMode bool + WEP bool } // Dot11Status represents 802.11 status @@ -985,12 +985,12 @@ type TLSConfiguration struct { // Dot11AvailableNetworks represents available 802.11 networks type Dot11AvailableNetworks struct { - SSID string - BSSID string - AuthAndMangementSuite []Dot11AuthAndMangementSuite - PairCipher []Dot11Cipher - GroupCipher []Dot11Cipher - SignalStrength Dot11SignalStrength + SSID string + BSSID string + AuthAndMangementSuite []Dot11AuthAndMangementSuite + PairCipher []Dot11Cipher + GroupCipher []Dot11Cipher + SignalStrength Dot11SignalStrength } // Dot11AuthAndMangementSuite represents auth suite @@ -1011,18 +1011,18 @@ type StorageConfiguration struct { // StorageConfigurationData represents storage configuration data type StorageConfigurationData struct { - Type string - LocalPath string - StorageUri string - User *UserCredential - CertPathValidationPolicyID string + Type string + LocalPath string + StorageUri string + User *UserCredential + CertPathValidationPolicyID string } // UserCredential represents user credentials type UserCredential struct { - UserName string - Password string - Token string + UserName string + Password string + Token string } // LocationEntity represents geo location @@ -1049,12 +1049,12 @@ type AccessPolicy struct { // PasswordComplexityConfiguration represents password complexity config type PasswordComplexityConfiguration struct { - MinLen int - Uppercase int - Number int - SpecialChars int - BlockUsernameOccurrence bool - PolicyConfigurationLocked bool + MinLen int + Uppercase int + Number int + SpecialChars int + BlockUsernameOccurrence bool + PolicyConfigurationLocked bool } // PasswordHistoryConfiguration represents password history config