hikvision-unifi-fire-bridge

hikvision-unifi-fire-bridge

Docker app from Gasmanc's Repository

Overview

Bridges Hikvision (thermal) camera fire-detection events into a UniFi Protect Alarm Manager incoming webhook: camera fire alarm -> Protect siren / priority push notification / any Alarm Manager action. Fail-loud design: re-alerts while the fire stays active, proactive Protect path probe, strict TLS. NOT certified life-safety equipment. This tool is not a substitute for compliant smoke alarms, fire panels, or emergency procedures, and carries no safety certification of any kind. Use at your own risk in addition to, never instead of, properly certified fire detection.

Hikvision → UniFi Protect Fire Bridge

A small Rust daemon that listens to a Hikvision (thermal) camera's ISAPI alert stream and triggers a UniFi Protect Alarm Manager incoming webhook when a fire-detection event occurs — so a camera fire alarm can ring a Protect siren, send a priority push notification, or drive any other Alarm Manager action.

Safety notice: this is an auxiliary automation component, not certified life-safety equipment. It must never replace compliant smoke alarms, fire panels, evacuation procedures, or emergency-services notification. Test the complete chain regularly.

How it works

Hikvision camera ──ISAPI alert stream──> bridge ──HTTPS webhook──> UniFi Protect
 (HTTP Digest auth)                        │                        Alarm Manager
                                           └── /healthz /readyz /status
  • Fail toward duplicate alerts, never missed ones. Alerts trigger on the inactive → active edge, and by default re-alert every 60 s while the fire stays active. Active state expires if the camera goes quiet and is re-armed after every stream reconnect, so a lost inactive notification can never latch the alarm off. An edge suppressed by the cooldown is carried over and fires as soon as the cooldown expires — rate limiting delays an alert, it never discards one.
  • Proactive path monitoring. A periodic probe verifies DNS, routing, and TLS toward the Protect host, so a broken webhook path turns /readyz red before a fire, not during one.
  • Robust stream handling. Byte-level frame extraction (UTF-8- and chunk-split-safe), structural XML parsing (namespace-agnostic), bounded buffers and queues, exponential reconnect backoff that resets after a healthy session.
  • Strict TLS. Certificate validation toward Protect is always on. Plain HTTP is accepted only toward loopback addresses (for testing).
  • Hardened container. Non-root, read-only-friendly, built-in Docker HEALTHCHECK, graceful SIGTERM shutdown.

Quick start (Docker)

docker run -d \
  --name hikvision-unifi-fire-bridge \
  --restart unless-stopped \
  --read-only --cap-drop ALL --security-opt no-new-privileges:true \
  -p 8080:8080 \
  -e HIKVISION_HOST=192.168.x.x \
  -e HIKVISION_USER=hik-operator \
  -e HIKVISION_PASS='...' \
  -e PROTECT_BASE_URL=https://your-unvr-hostname \
  -e PROTECT_WEBHOOK_ID='...' \
  -e PROTECT_API_KEY='...' \
  ghcr.io/gasmanc/hikvision-unifi-fire-bridge:0.1.0

Images are published for linux/amd64 and linux/arm64 (Raspberry Pi 4/5 and other SBCs) under the same tag — Docker pulls the right one for your platform automatically.

Pin a version tag or digest; do not auto-deploy latest for a safety-related service.

Configuration

Variable Required Default Description
HIKVISION_HOST Yes Camera IP or hostname (optionally host:port)
HIKVISION_USER Yes Restricted camera account (events/ISAPI read only)
HIKVISION_PASS Yes Camera account password
HIKVISION_SCHEME No http http or https toward the camera
PROTECT_BASE_URL Yes* HTTPS URL of the UNVR running Protect (certificate-valid hostname)
PROTECT_WEBHOOK_ID Yes* Alarm Manager incoming-webhook ID
PROTECT_WEBHOOK_URL No Full webhook URL override; replaces PROTECT_BASE_URL + PROTECT_WEBHOOK_ID if your Protect version uses a different path
PROTECT_API_KEY Yes Protect integration API key (sent as X-API-Key)
FIRE_EVENT_TYPES No TMA,fireDetection,fire_detection,fireAlarm Comma-separated eventType values treated as fire (case-insensitive)
FIRE_COOLDOWN_SECONDS No 60 Minimum gap between edge-trigger alerts per source
FIRE_REALERT_SECONDS No 60 Re-alert interval while a fire stays active; 0 disables re-alerting
FIRE_ACTIVE_TTL_SECONDS No 300 Active state expires after this silence (anti-latch safety)
PROTECT_PROBE_SECONDS No 60 Protect reachability probe interval; 0 disables
STREAM_IDLE_TIMEOUT_SECONDS No 90 Reconnect when the camera stream is silent this long
WEBHOOK_TIMEOUT_SECONDS No 5 Per-attempt webhook timeout
WEBHOOK_ATTEMPTS No 3 Delivery attempts (permanent 4xx errors are not retried)
RECONNECT_INITIAL_SECONDS No 1 First reconnect delay
RECONNECT_MAX_SECONDS No 30 Reconnect delay ceiling
HEALTH_BIND No 0.0.0.0:8080 Health server bind address
RUST_LOG No info Tracing filter

* Not required when PROTECT_WEBHOOK_URL is set.

Health endpoints

Endpoint Meaning
GET /healthz Process liveness (also used by the container HEALTHCHECK)
GET /readyz 200 + {"ready":true} only when the camera stream is connected, the Protect probe passes, and the last alert delivery did not fail
GET /status Full diagnostic JSON: timestamps, reconnect/malformed/dropped counters, last errors (sanitised — no secrets, no URLs)

A failed alert delivery latches /readyz unready until a later delivery succeeds. That is deliberate: a missed alarm needs a human. Monitor /readyz with something independent of UniFi (e.g. Uptime Kuma) — Protect cannot report the failure of the path used to reach Protect.

Temperature Measurement Alarm (TMA) — recommended primary trigger

Hikvision thermal cameras emit a TMA (Temperature Measurement Alarm) event on the ISAPI stream when a measured temperature crosses a thermometry rule you configure on the camera. Because it triggers on heat crossing a threshold — not on visible flame — TMA generally alarms earlier than the AI fireDetection event, which is why TMA is included in the FIRE_EVENT_TYPES defaults.

To use it:

  1. On the camera, enable Temperature Measurement / Thermography.
  2. Add a rule (point or region) with an alarm threshold appropriate for fire — a temperature well above anything normal for that scene.
  3. Set the rule's linkage to raise a notification so it appears on the alert stream.
  4. Confirm the exact eventType your firmware sends (see below) — some firmwares label it TMA, others thermometryAlarm. Add whatever you see to FIRE_EVENT_TYPES.

Caveat: if you also run thermography for non-fire purposes, a TMA raised by an unrelated rule will trigger an alert too. Scope the camera's rules to fire, or narrow FIRE_EVENT_TYPES accordingly.

Finding your camera's event type

Firmwares differ. Capture a test event and check the eventType:

curl --digest --user 'hik-operator:PASSWORD' --no-buffer \
  'http://CAMERA/ISAPI/Event/notification/alertStream'

If yours differs from the defaults, set FIRE_EVENT_TYPES — no rebuild needed.

The bridge also self-reports misconfiguration: every event type it sees that does not match FIRE_EVENT_TYPES appears in the unmatched_event_types list on /status (and is logged once). If your camera's fire type is wrong in the config, it shows up there instead of failing silently.

Verifying the Protect webhook

Protect UI and API paths can move between versions. Test yours directly:

curl --fail-with-body -X POST \
  -H "X-API-Key: $PROTECT_API_KEY" \
  "https://YOUR_UNVR/proxy/protect/integration/v1/alarm-manager/webhook/$PROTECT_WEBHOOK_ID"

If your Protect version exposes a different URL, pass it verbatim via PROTECT_WEBHOOK_URL.

Development

cargo fmt --check
cargo clippy --all-targets -- -D warnings
cargo test            # unit + end-to-end tests (fake camera & Protect servers)

Releases are tagged vX.Y.Z; CI builds and publishes the container image to ghcr.io/gasmanc/hikvision-unifi-fire-bridge.

Security

  • Use a dedicated, least-privilege camera account and a minimum-scope Protect API key; rotate on any suspected exposure.
  • TLS certificate validation is always enforced toward Protect and cannot be disabled.
  • Restrict the health port to your monitoring network.
  • Report vulnerabilities via GitHub private vulnerability reporting.

License

Copyright © 2026 contributors.

Licensed under the GNU Affero General Public License, version 3 or later (LICENSE, AGPL-3.0-or-later).

Install hikvision-unifi-fire-bridge on Unraid in a few clicks.

Find hikvision-unifi-fire-bridge in Community Apps on your Unraid server, review the template, and click Install. Unraid handles the Docker app or plugin setup from the published template.

Open the Apps tab on your Unraid server Search Community Apps for hikvision-unifi-fire-bridge Review the template variables and paths Click Install

Requirements

A Hikvision camera exposing the ISAPI alert stream, and UniFi Protect with an Alarm Manager incoming webhook + API key. The Protect host must present a valid TLS certificate.

Related apps

Details

Repository
ghcr.io/gasmanc/hikvision-unifi-fire-bridge:0.1.0
Last Updated2026-07-17
First Seen2026-07-16

Runtime arguments

Web UI
http://[IP]:[PORT:8080]/status
Network
bridge
Shell
sh
Privileged
false
Extra Params
--restart unless-stopped --read-only --tmpfs /tmp:rw,noexec,nosuid,size=16m --cap-drop ALL --security-opt no-new-privileges:true

Template configuration

Health portPorttcp

Health/readiness/status HTTP port.

Target
8080
Default
8080
Value
8080
Hikvision hostVariable

Camera IP or hostname (optionally host:port).

Target
HIKVISION_HOST
Hikvision userVariable

Restricted camera account (event/ISAPI read only).

Target
HIKVISION_USER
Default
fire-bridge
Value
fire-bridge
Hikvision passwordVariable

Camera account password.

Target
HIKVISION_PASS
Hikvision schemeVariable

http or https toward the camera.

Target
HIKVISION_SCHEME
Default
http
Value
http
Protect base URLVariable

HTTPS URL of the UNVR running Protect (certificate-valid hostname, e.g. https://unvr.example.com).

Target
PROTECT_BASE_URL
Protect webhook IDVariable

Alarm Manager incoming-webhook ID.

Target
PROTECT_WEBHOOK_ID
Protect API keyVariable

Minimum-scope Protect integration API key (sent as X-API-Key).

Target
PROTECT_API_KEY
Protect webhook URL overrideVariable

Optional full webhook URL; replaces base URL + webhook ID if your Protect version uses a different path.

Target
PROTECT_WEBHOOK_URL
Fire event typesVariable

Comma-separated eventType values treated as fire (case-insensitive). Empty = fireDetection,fire_detection,fireAlarm.

Target
FIRE_EVENT_TYPES
Fire cooldown secondsVariable

Minimum gap between edge-trigger alerts per source.

Target
FIRE_COOLDOWN_SECONDS
Default
60
Value
60
Re-alert secondsVariable

Re-alert interval while a fire stays active; 0 disables re-alerting.

Target
FIRE_REALERT_SECONDS
Default
60
Value
60
Active TTL secondsVariable

Active state expires after this many seconds of silence (anti-latch safety).

Target
FIRE_ACTIVE_TTL_SECONDS
Default
300
Value
300
Protect probe secondsVariable

Protect reachability probe interval; 0 disables.

Target
PROTECT_PROBE_SECONDS
Default
60
Value
60
Stream idle timeoutVariable

Reconnect when the camera stream is silent this long.

Target
STREAM_IDLE_TIMEOUT_SECONDS
Default
90
Value
90
Webhook timeoutVariable

Per-attempt Protect request timeout.

Target
WEBHOOK_TIMEOUT_SECONDS
Default
5
Value
5
Webhook attemptsVariable

Delivery attempts (permanent 4xx not retried).

Target
WEBHOOK_ATTEMPTS
Default
3
Value
3
Log levelVariable

Tracing filter (error, warn, info, debug).

Target
RUST_LOG
Default
info
Value
info