Skip to main content

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, relay outputs). Lights and chimes are listed but not yet controllable.

The console also works as an access control device: a doorbell ring fires call rules, request-to-exit buttons on a Protect relay fire rex rules, relay outputs release strikes and gates, and relay inputs and UP-Sense door sensors report door position. See Access control.

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/integration/v1/ (UniFi OS 3.2+). The driver falls back to the /proxy/protect/integrations/v1/ spelling that earlier builds used if the documented path doesn't answer.
  • For access control: Protect 6.x or newer, plus the hardware — a doorbell, a UniFi relay / SuperLink device for strikes and REX buttons, UP-Sense sensors for door position.
  • 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​

  1. In the UniFi OS web UI, open Settings → Control Plane → Integrations, click Create API Key, and copy the key.
  2. 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.
  3. 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).
  4. Save and reload. On boot the driver reaches the console to confirm the key, then enumerates the cameras, lights, and chimes it manages.
  5. Run the get_cameras command to print every discovered camera with its id (a long alphanumeric string) and friendly name.
  6. 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.
One zone per camera — created by you

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​

AttributeRequiredDescription
ipyesLAN IP / hostname of the UniFi OS console.
api_keyyesConsole API key. Stored encrypted; sent in the X-API-KEY header.
portnoHTTPS port. Defaults to 443; a blank, non-numeric or out-of-range value also uses 443 (with one warning in the log).
status_intervalnoCamera-state poll interval in milliseconds. Defaults to 5000. Values below 1000 poll every 1000; 0, negative or unreadable values use 5000.
enable_eventsnoSubscribe to the real-time event stream. Defaults to true; set false (or 0, no, off) to fall back to polling only.
rex_inputsnoRelay inputs wired to request-to-exit buttons, as relayId:channel separated by commas (ids from get_relays). Only these fire rex rules.
rex_triggernoWhich circuit change is a REX press: closed (default — a normally-open button) or open.

Zone​

AttributeDescription
stateCamera zones: motion while the camera reports motion, clear otherwise. Relay output zones: on / off. Relay input and door sensor zones: open / closed. Driver-managed, real-time when the event stream is up.
last_motionUnix-millisecond timestamp of the last reported motion event. Driver-managed.
ringDoorbell ring flag; pulses 1 → 0 (cleared by the event's end frame, or after 3 s if none arrives). Driver-managed.
last_ringUnix-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. Its form picks what the zone tracks (an address typed in a different letter case from the console's id still matches, as long as only one zone matches it):

AddressZone tracks
<cameraId>A camera or doorbell (id from get_cameras — NOT the friendly name).
<sensorId>An UP-Sense door / window sensor (id from get_sensors).
<relayId>:<output>A relay output, e.g. 66d025b301ebc903e80003ea:0.
<relayId>:input:<channel>A relay input, e.g. 66d025b301ebc903e80003ea:input:1.

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​

CommandArgsDescription
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.
snapshotaddressGrabs 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_modeaddress, modeMode is one of always, never, detections, schedule.
set_status_lightaddress, enabledTurns the camera status LED on (enabled = true, 1, on or yes) or off (anything else).
get_relays—Lists Protect relay devices with their output and input channels.
get_sensors—Lists Protect sensors with their open state.
activate_relay_outputaddress, state, pulse_durationDrives a relay output. address is relayId:output. state is on or off; leave it out to toggle. With state on, pulse_duration (ms) turns the output back off — a strike or gate pulse.

snapshot with no address photographs the doorbell that rang in the last 15 seconds. This is how a call rule's access-log entry gets a picture without a fixed camera.

Addresses are trimmed before they are sent. An address left as an unfilled placeholder ([@zone.address]) is refused with an error instead of being sent to the console. snapshot returns an error, and saves nothing, when the console answers with something other than an image (an empty body, or a login or error page).

Event stream​

The driver holds a WebSocket open to /proxy/protect/integration/v1/subscribe/events (same X-API-KEY auth as the REST calls) and applies event frames as they arrive:

  • motion — zone state flips to motion on the start frame and back to clear on the end frame; last_motion gets the start timestamp.
  • smartDetectZone / smartDetectLine / smartAudioDetect — each detected type sets its per-type flag to 1, cleared to 0 by the end frame.
  • ring — writes last_ring and pulses ring, and fires call access rules on this device.
  • relayInputChanged — writes open / closed on a relayId:input:n zone, and fires rex rules for inputs listed in rex_inputs.
  • sensorOpened / sensorClosed — writes open / closed on the sensor's zone.

Relay output state isn't carried on the stream. While any relayId:output zone exists, the status_interval poll also reads the relay list.

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). The backoff resets once the console sends something on the new socket, so a console that accepts the connection and drops it straight away keeps backing off. 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.

If the device is reloaded or disabled while a ring or smart-detection flag is still 1, the driver sets it back to 0, so the next ring or detection still changes the value.

Connection and polling​

  • Console unreachable at startup. If the console does not answer at all when GEM connects (powered off, network down), the driver tries again after 30 s, doubling up to every 5 minutes, until it answers. A console that answers with 401 or 404 (wrong key, old firmware) is not retried, because that needs a configuration change and a reload.
  • Poll back-off. After 3 failed camera polls in a row, the poll slows down, doubling up to once a minute (never faster than status_interval). It goes back to normal on the first good poll. The failure is logged once as unifi protect status poll failed, with a summary line every 15 minutes while it continues.
  • A meta/info reply that isn't JSON (for example a login page served by a proxy) counts as not connected.

Access control​

The console can be the access device on an access control rule, the same way a 2N intercom is. What it supports:

Rule typeFired byNotes
callA doorbell ringThe rule's log entry records the camera id that rang.
rexA relay input listed in rex_inputsFires on the rex_trigger edge. The log records relayId:channel.
pin / rfid—Not supported. See the limitations below.

A typical front door:

  1. Wire the strike to a relay output and add a zone addressed relayId:0.
  2. Wire the REX button to relay input 0 and set rex_inputs to relayId:0.
  3. Wire the door contact (normally closed) to input 1 and add a zone addressed relayId:input:1.
  4. Create a rex rule on the console device. Its action is activate_relay_output on the strike zone with state on and pulse_duration 5000. Set a held-open threshold on the rule to watch the contact zone.
  5. Create a call rule for the doorbell to get a log entry and a snapshot, and to run a chime or notification macro.
Only listed inputs fire REX

An input that isn't in rex_inputs never fires a rex rule. This keeps a door contact on the same relay from unlocking its own door when the door opens.

One device, every door. A rule is attached to the console device, not to a single door. A call rule fires for a ring on any doorbell the console manages, and a rex rule for any input in rex_inputs. Where two doors need different actions, drive each one from a trigger on its own zone (ring, or the input zone's state).

Snapshots. Set camera_zone_id on the console device to always photograph one camera. Leave it empty and a call rule photographs the doorbell that rang. A rex rule has no camera of its own, so it only gets a photo when camera_zone_id is set.

Door position. Relay input zones read circuitClosed as closed. With a normally-closed magnetic contact that means the door is closed. Wire the contact that way, or held-open and forced-door detection will read the door backwards.

Limitations​

  • No PIN / card / fingerprint rules. Protect reports NFC card and fingerprint scans, but only as the UniFi Identity user the console matched. It never sends the card number, so the scan can't be checked against a GEM user's credentials. Credentials are enrolled and checked in UniFi. GEM can't push users to the console either, because the Protect API's user endpoints are read-only.

  • 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/info returns 404 — the console firmware predates the Integrations API. Upgrade UniFi OS to 3.2 or newer.
  • activate_relay_output returns status 503 — the relay is offline or out of range of its link station. Check it in the Protect UI.
  • A rex rule never fires — check that rex_inputs names the relay id exactly as get_relays prints it, with the right channel, and that rex_trigger matches the button's wiring.
  • /meta/info returns 401 — the API key is wrong, expired, or was deleted. Mint a new one and update the api_key attribute.
  • Snapshots return status 400 for 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 schedule returns 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/integration/v1/ and pass through the X-API-KEY header verbatim (including the WebSocket upgrade for /subscribe/events).
  • Motion stopped triggering in real time — grep server logs for unifi protect event socket. heartbeat timeout means the network path died silently (the driver terminates and reconnects on its own); code: 1006 is an abnormal network drop; code: 1000 is a console-initiated close (reboot / firmware update); reconnecting followed by a (repeated N times in Xm) summary means the console is unreachable or the firmware predates the subscribe endpoints — motion still works via the poll in the meantime. Repeated dial errors and closes of sockets that never opened are also collapsed this way; the connected ... after N reconnect attempt(s) line says how many tries it took.
  • 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).
  • 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, and set_status_light from the admin UI, macros, or scenes.
  • Triggers — firing automations off a camera zone's state changing to motion, a smart-detection flag like person going to 1, or a doorbell ring.
  • ONVIF — the vendor-neutral camera path to use when you need RTSP/ONVIF video streams rather than the Protect Integrations API.