← Back to Portal Index

Frigate ONVIF PTZ Proxy

Last Updated: 2026-06-23

1. The Namespace Parsing Bug

The down_street camera (IP 10.9.9.88) runs on a Puwell board using a naive XML parser. Frigate uses the Python zeep SOAP client, which serializes XML namespace declarations inline:



The camera's naive parser fails to extract the x attribute when it is preceded by xmlns:ns1=.... However, it successfully parses y because it is at the end of the element. This caused horizontal pan and zoom to fail while vertical tilt worked.

2. The SOAP Proxy Solution

To fix this in an upgrade-resilient way without patching Frigate's internal containerized code, we set up a lightweight local Python ONVIF proxy service on the LXC 101 host (10.9.9.237).

The proxy:

1. Listens on host port 8081.

2. Intercepts incoming SOAP request bodies.

3. Uses xml.etree.ElementTree to parse and re-serialize the XML. By registering standard namespaces, ElementTree automatically consolidates all declarations to the root tag and strips inline attributes, leaving the child element clean:



4. Forwards the cleaned XML to the camera (10.9.9.88:8080).

5. Rewrites returned URLs (XAddrs) in GetCapabilities and GetServices responses from the camera to point back to the proxy (172.18.0.1:8081) so Frigate does not bypass the proxy on subsequent calls.

6. Intercepts GetPresetsResponse from the camera and translates the non-standard tags (buggy camera implementation) into standard tags so that Zeep's strict schema parser can read and discover the camera's presets.

7. Performs a reverse-rewrite on incoming request bodies, replacing any proxy host address references (like 172.18.0.1:8081) back to the camera's address (10.9.9.88:8080) so that WS-Addressing headers point to the camera's actual URL, allowing preset jumps (GotoPreset) to execute successfully.

3. Systemd Service & Frigate Configuration

The proxy is deployed as a persistent systemd service (onvif-proxy.service) on LXC 101.

We modified Frigate's /opt/frigate/config.yml configuration under the down_street camera to target the proxy:


  down_street:
    live:
      streams:
        WebRTC: down_street
    onvif:
      host: 172.18.0.1
      port: 8081
      user: admin
      password: KIMVUV

onvif-proxy Service

IP Address: 10.9.9.237

4. Firmware Preset Limitations

Although the proxy successfully fixes XML namespace errors for GetPresetsResponse (translating to ) so that ONVIF clients (Frigate, ODM) can discover and list presets:

1. The camera's local firmware has a dummy implementation of GetStatus (PTZ coordinates) which always returns a hardcoded position of x=0.8, y=0.8 regardless of actual movement.

2. When SetPreset is called, the camera saves the current (hardcoded) coordinate 0.8, 0.8 to the database.

3. When GotoPreset is called, the camera compares the preset position (0.8, 0.8) with the current reported position (0.8, 0.8), decides it is already there, and returns a successful SOAP payload (GotoPresetResponse / HTTP 200) without physically moving the motors.

4. The official mobile app (IPC360 Home) presets work because they bypass the ONVIF layer entirely using proprietary motor-step tracking over a cloud tunnel. As a result, standard ONVIF-based presets are physically non-functional on this camera.