UniFi Protect
Ubiquiti UniFi Protect controllers (Cloud Key Gen2+, Dream Machine Pro, UNVR, UDM-SE) and the cameras / floodlights / chimes managed by them, integrated via the local Protect Integrations REST API on UniFi OS using a per-controller API key.
The driver subscribes to the console's real-time event stream (/subscribe/events, WebSocket) for motion, smart detections, and doorbell rings, with the camera-state poll as fallback while the stream is down. It fetches JPEG snapshots and exposes a small set of write commands (recording mode, status LED). Lights and chimes are listed but not yet controllable.
Prerequisites
- A UniFi OS console (Cloud Key Gen2+, Dream Machine Pro, UNVR, UDM-SE) running UniFi Protect 3.x or newer.
- Console firmware new enough to expose the Integrations API at
/proxy/protect/integrations/v1/(UniFi OS 3.2+). - An API key minted under Settings → Control Plane → Integrations → Create API Key. The key is shown only once at creation — copy it before navigating away.
- TCP/443 reachable from the GEM controller to the console (LAN IP or hostname).
Setup
- In the UniFi OS web UI, open Settings → Control Plane → Integrations, click Create API Key, and copy the key.
- In GEM admin, add a device with the UniFi Protect (NVR + Cameras) driver, placed under a Security or Cameras subsystem. One GEM device represents the whole console — you do not add a separate device per camera.
- Set the device attributes:
ip— LAN IP or hostname of the UniFi OS console.api_key— the key you copied in step 1 (stored encrypted).
- Save and reload. On boot the driver reaches the console to confirm the key, then enumerates the cameras, lights, and chimes it manages.
- Run the
get_camerascommand to print every discovered camera with its id (a long alphanumeric string) and friendly name. - For each camera you want to track in GEM, add a zone under the same subsystem, bind it to this device, and set its address to that camera id. The driver writes per-camera state onto these zones.
GEM does not create zones automatically from discovery. A camera only surfaces state once a zone whose address is its id exists and is bound to the console device; cameras without a matching zone are enumerated but tracked silently. Use get_cameras to copy the exact id — the long alphanumeric string, never the friendly name.
Attribute reference
Device
| Attribute | Required | Description |
|---|---|---|
ip | yes | LAN IP / hostname of the UniFi OS console. |
api_key | yes | Console API key. Stored encrypted; sent in the X-API-KEY header. |
port | no | HTTPS port. Defaults to 443. |
status_interval | no | Camera-state poll interval in milliseconds. Defaults to 5000. |
enable_events | no | Subscribe to the real-time event stream. Defaults to true; set false to fall back to polling only. |
Zone
| Attribute | Description |
|---|---|
state | motion while a camera reports motion, clear otherwise. Driver-managed, real-time when the event stream is up. |
last_motion | Unix-millisecond timestamp of the last reported motion event. Driver-managed. |
ring | Doorbell ring flag; pulses 1 → 0 (cleared by the event's end frame, or after 3 s if none arrives). Driver-managed. |
last_ring | Unix-millisecond timestamp of the last doorbell ring. Driver-managed. |
person, vehicle, animal, package, license_plate, … | Smart-detection flags, 1 while the detection is active and 0 when it ends. One attribute per detection type the camera reports (names are the Protect types converted to snake_case). A flag auto-clears after 120 s if the end-of-event frame is lost. |
Zone address is required and must be the camera id from get_cameras (NOT the friendly name).
Smart-detection flags only appear for detection types that are enabled on the camera in the Protect UI (Settings → the camera → Smart Detections). Trigger automations on the flag's change to 1 — e.g. person 1 on the driveway camera arms the floodlight macro.
Commands
| Command | Args | Description |
|---|---|---|
get_info | — | Returns controller meta (Protect application version, etc.). |
get_cameras | — | Lists all cameras and refreshes the discovered-camera list GEM keeps for this console. |
get_lights | — | Lists Protect-managed flood lights. |
get_chimes | — | Lists Protect-managed chimes. |
snapshot | address | Grabs a still from the camera at that zone address and saves it under the server's snapshots folder. Returns the served path /snapshots/<device-id>-<address>.jpg, usable directly in a UI image widget or a notification. |
set_recording_mode | address, mode | Mode is one of always, never, detections, schedule. |
set_status_light | address, enabled | Toggles the camera status LED. |
Event stream
The driver holds a WebSocket open to /proxy/protect/integrations/v1/subscribe/events (same X-API-KEY auth as the REST calls) and applies event frames as they arrive:
- motion — zone
stateflips tomotionon the start frame and back toclearon the end frame;last_motiongets the start timestamp. - smartDetectZone / smartDetectLine / smartAudioDetect — each detected type sets its per-type flag to
1, cleared to0by the end frame. - ring — writes
last_ringand pulsesring.
The socket is health-checked with a 30 s ping/pong heartbeat; a silent socket is terminated and redialed with capped backoff (5 s doubling to 60 s). While the stream is down the status_interval poll reconciles motion state exactly as older builds did, so the only thing lost during an outage is latency.
Limitations
- Lights and chimes are read-only. They show up in discovery but per-zone control of flood lights / chime tones is not yet wired through.
- No live video. Snapshots only. RTSPS stream URLs are exposed by the API but not surfaced as a command in this build.
- No camera-side configuration writes beyond recording mode and status LED. Privacy zones, motion sensitivity, and detection types are reachable through the API but not exposed yet.
Troubleshooting
/meta/inforeturns 404 — the console firmware predates the Integrations API. Upgrade UniFi OS to 3.2 or newer./meta/inforeturns 401 — the API key is wrong, expired, or was deleted. Mint a new one and update theapi_keyattribute.- Snapshots return
status 400for some cameras — a few low-power battery cameras only return snapshots while their stream is active. Wake the camera in the Protect UI first. set_recording_mode schedulereturns 400 — the camera has no schedule configured on the controller. Save a schedule in the Protect UI before sending this mode.- All requests time out — confirm 443/tcp from the GEM host to the console. If you front the console behind a reverse proxy, the proxy must forward
/proxy/protect/integrations/v1/and pass through theX-API-KEYheader verbatim (including the WebSocket upgrade for/subscribe/events). - Motion stopped triggering in real time — grep server logs for
unifi protect event socket.heartbeat timeoutmeans the network path died silently (the driver terminates and reconnects on its own);code: 1006is an abnormal network drop;code: 1000is a console-initiated close (reboot / firmware update); repeatedreconnecting ... attempt: Nmeans the console is unreachable or the firmware predates the subscribe endpoints — motion still works via the poll in the meantime. - Smart-detection flags never appear — the detection types must be enabled per camera in the Protect UI, and the camera must support them (AI-series or G4+/G5 models).
Related documentation
- Devices — adding the UniFi OS console as a single device.
- Zones — creating one zone per camera, addressed by the camera id.
- Subsystems — the Security / Cameras subsystem the console and its camera zones live under.
- Commands — running
snapshot,set_recording_mode, andset_status_lightfrom the admin UI, macros, or scenes. - Triggers — firing automations off a camera zone's
statechanging tomotion, a smart-detection flag likepersongoing to1, or a doorbellring. - ONVIF — the vendor-neutral camera path to use when you need RTSP/ONVIF video streams rather than the Protect Integrations API.