Skip to main content

Reolink Cameras and NVRs

Reolink cameras and NVRs that expose the local /cgi-bin/api.cgi JSON API on the LAN. One GEM device represents one camera or one NVR — never one device per NVR channel. Each channel you want GEM to track becomes a zone whose Address is the channel index.

The driver polls per-channel motion and AI-detection state, captures JPEG stills, drives PTZ, and switches the white-light spotlight and the siren.

This driver carries state and control, not video

Nothing here streams video. Live view comes from the camera's own RTSP stream, configured on the zone — see Live video below. Motion state, snapshots, PTZ and the accessories come from the JSON API described below.

Prerequisites

  • A Reolink camera or NVR with firmware new enough to expose the JSON API. Most models shipped from 2018 onward qualify; older firmware only offers the legacy CGI interface and will not work.
  • A camera account GEM can use. Operator level is the minimum; admin is recommended — PTZ, spotlight and siren are frequently refused for operator accounts.
  • Network access from the GEM host to the camera's HTTP port (default 80). Some battery-powered models (Argus, Go) reject the API over HTTP and require HTTPS on port 443.
  • The camera's channel numbering, if it is an NVR. get_channels prints it once the device is connected.

Self-signed HTTPS certificates are accepted — Reolink's factory certificate does not need replacing to use https.

Setup

  1. Prove the API from a browser first. Browsing to http://<camera-ip>/cgi-bin/api.cgi?cmd=Login should return a JSON response. An error payload is fine — it proves the API is answering. Sort out VLANs, firewalls and ports at this step, not from inside GEM.
  2. Add the device. On Devices, add a device and set the driver to Reolink Camera / NVR. The driver panel that appears carries a short Before you start / Setup steps / Known quirks summary, plus a View integration guide link that opens this page inline.
  3. Fill in the connection attributes on the same form:
    • IP Address / Hostname — the camera or NVR address.
    • Username / Password — the camera account. The password is stored encrypted.
    • The rest sit behind Show 4 optional attributes and only need changing on HTTPS-only models or when tuning the poll.
  4. Leave the command set alone. If nothing is selected, GEM creates a command set named reolink for you and tells you so. The driver's commands are written into it the next time the device loads, and the device's Commands tab only appears once a command set is assigned.
  5. Save, then reload the device. On a successful login GEM logs the model and the channel count, and the device shows as connected.
  6. List the channels. Open the device's Commands tab, pick the camera as the Test Device, and run get_channels with the test button. Each channel comes back with its number, name and online flag. On a single camera with no channel list of its own, GEM answers with one synthetic channel 0.
  7. Create one zone per channel. On Zones, add a zone, set its Device to the Reolink device, and set its Address to the channel index — 0 for a single camera, 0 through N-1 on an NVR. The subsystem pre-selects Security; move it to Cameras if that matches your site convention. Nothing in the driver depends on which one you pick.
Always set the zone Address

Zones are never created automatically, and a camera zone saved with an empty Address is not skipped — GEM falls back to keying that zone by its internal record number, so the driver ends up polling a channel that does not exist. The camera rejects it, the driver swallows the rejection, and the zone simply never reports motion. If a zone is silent, check its Address first.

Attribute reference

Device

AttributeRequiredDefaultDescription
ipyesLAN IP or hostname of the camera or NVR.
usernameyesadminCamera account. Operator minimum, admin recommended.
passwordyesCamera account password. Stored encrypted.
portno80HTTP(S) port. Use 443 with protocol set to https.
protocolnohttphttp or https. Battery models usually require https.
status_intervalno5000Milliseconds between poll cycles. Anything below 1000 is raised to 1000.
request_timeoutno8000Milliseconds before a single API request is abandoned.

The optional defaults above are written onto the device when you create it through the Devices form. A device that predates those fields — or one created another way — runs on the driver's own fallbacks, which are the same values except request_timeout, which falls back to 10000.

Zone

The zone Address is required and is the channel index as a plain number: 0 for a single camera, 0..N-1 on an NVR. Note the offset against Reolink's own numbering — the app and the RTSP paths call the first channel 1, GEM calls it 0.

AttributeWritten byDescription
statedrivermotion while the channel reports motion, clear otherwise.
motiondriver1 or 0 — the same reading as a number, for conditions and triggers that want an integer.
ai_detecteddriverComma-separated list of the AI classes currently alarming on this channel, for example people,vehicle. Empty string when nothing is detected.
stream_urlyouRTSP URL for live view — see Live video below. Stored encrypted.
frame_rateyouFrames per second the camera viewer requests from the relay. Defaults to 5.

None of the driver-written attributes record history by default. To chart or report on motion over time, switch History on for that attribute on the zone's Attributes tab.

note
ai_types has no effect

The zone form offers an AI Detection Types field (ai_types), but the driver does not read it — ai_detected always lists every AI class the camera reports as alarming. To act on one class only, test the contents of ai_detected in your trigger or macro condition rather than trying to filter at the camera.

Commands

Run these from the device's Commands tab, or from a macro's Send Command step. Commands that take an address accept the channel number; target the camera zone instead of the device and the channel fills itself in from the zone's Address.

CommandArgsWhat it does
loginForce a fresh login and refresh the cached session token.
logoutInvalidate the current token immediately.
get_dev_infoModel, firmware and channel count.
get_channelsChannel list with numbers, names and online state.
snapshotaddressJPEG still for the channel, returned as base64 with its length and content type.
get_motionaddressOne-shot motion state for the channel.
get_ai_stateaddressOne-shot AI state for the channel, per class.
ptzaddress, op, speed, durationPan/tilt/zoom/focus. See below.
ptz_presetaddress, preset, speedRecall a preset by number. Defaults to preset 1 at speed 32.
spotlight_on / spotlight_offaddressSwitch the white-light spotlight. Cameras with no spotlight answer with an error from the camera itself.
sirenaddressSound the on-camera siren once.
rebootReboot the device.

PTZ arguments. op is one of Up, Down, Left, Right, LeftUp, RightUp, LeftDown, RightDown, ZoomInc, ZoomDec, FocusInc, FocusDec, Auto, Stop. Omitting op sends Stop. speed is 1-64 and defaults to 32. duration is milliseconds: any movement other than Stop keeps going until GEM sends its own Stop after that many milliseconds, capped at 30 seconds. A ptz call with no duration never stops on its own — you must send Stop yourself.

Two gaps worth knowing before you design a UI around this: there is no command to save a PTZ preset (create them in the Reolink app), and there is no command to silence the siren early — it plays its one-shot and stops.

reboot sends the standard reboot request. It has not been confirmed against older NVR firmware, which is documented as using a different envelope; on a camera it is routine.

What the driver polls

Every poll cycle the driver walks each enabled zone bound to this device that has a numeric address and asks the camera two questions per channel — motion state, then AI state — with a short gap between channels. Two things follow from that:

  • Poll cost scales with channel count. A 16-channel NVR at the default 5-second interval is 32 requests every 5 seconds. On a busy NVR, raising status_interval to 10000 costs a few seconds of reaction time and halves the load.
  • Only changes are written. The zone's attributes are updated when a reading differs from the last one, so a quiet camera generates no attribute traffic at all.

Cameras with no AI capability reject the AI question; the driver ignores the rejection and keeps polling motion, so an older camera and a new one can share the same NVR without noise.

Live video

Live view is separate from everything above. To see the camera in the UI, put its RTSP URL in the zone's stream_url attribute on the zone's Attributes tab:

rtsp://<user>:<password>@<camera-ip>:554/h264Preview_01_main
  • The two digits are Reolink's 1-based channel number, so GEM zone address 0 is _01_, address 1 is _02_, and so on.
  • _main is the full-resolution stream; _sub is the low-bitrate substream, which is the better choice for a wall panel showing several cameras at once.
  • If your firmware uses a different path, copy the exact URL out of the camera's own RTSP settings.

stream_url is stored encrypted and is never sent to a browser — GEM relays the stream server-side, so the credentials in that URL stay on the controller. Add the camera to a UI page with the Camera widget, which takes one or several camera zones and expands to the full viewer when tapped.

Driving PTZ from the camera viewer

The on-screen pad in the camera viewer appears whenever the zone has a device bound directly to it (a zone bound only through its subsystem shows no pad). Its buttons send the generic verbs up, down, left, right, zoom_in, zoom_out, load_preset and save_preset — none of which this driver implements on its own, so the pad does nothing against a Reolink camera until you map those verbs onto the driver's ptz command.

Map them with per-verb action overrides on the camera zone. On the zone's Attributes tab add an attribute named after the verb with _action appended, set its type to json, and give it the command to run instead:

AttributeValue
up_action{"command": "ptz", "device_id": 12, "args": {"address": "0", "op": "Up", "speed": 32, "duration": 700}}
down_action{"command": "ptz", "device_id": 12, "args": {"address": "0", "op": "Down", "speed": 32, "duration": 700}}
left_action{"command": "ptz", "device_id": 12, "args": {"address": "0", "op": "Left", "speed": 32, "duration": 700}}
right_action{"command": "ptz", "device_id": 12, "args": {"address": "0", "op": "Right", "speed": 32, "duration": 700}}
zoom_in_action{"command": "ptz", "device_id": 12, "args": {"address": "0", "op": "ZoomInc", "speed": 32, "duration": 500}}
zoom_out_action{"command": "ptz", "device_id": 12, "args": {"address": "0", "op": "ZoomDec", "speed": 32, "duration": 500}}

Replace 12 with the camera's device number and "0" with that zone's channel. The arrow buttons repeat about twice a second while held, so a duration near 700 gives smooth movement that stops shortly after the finger lifts.

The preset buttons cannot be mapped this way. An override always runs with the arguments you stored, never the ones the button sent, so all six preset buttons would recall the same preset — use a macro per preset, or a button bound to ptz_preset with the preset number, instead. save_preset has no equivalent at all; create presets in the Reolink app.

Snapshots

  • On demand. Run snapshot against the camera zone from the Commands tab, or from a macro's Send Command step with the zone as the target.
  • On an access event. Open Access Control, select the door's access device, and set Snapshot Camera Zone to the Reolink camera zone. Every access log entry for that device then carries the still captured at the moment of the event, and the log links back to the camera.
  • The Camera Snapshot macro step does not work with this driver. It expects the image under a different field name than Reolink hands back and reports an empty snapshot. Use a Send Command step with snapshot instead.

Sessions and tokens

The camera issues a session token at login, with a one-hour lease by default. While at least one zone is being polled, GEM renews the token a minute before it expires. Any request that comes back with an expired-or-invalidated token code (-6, -7, -10) triggers a single re-login and one retry, so a token invalidated by another app recovers on the next poll rather than at the next restart.

Reolink rate-limits failed logins and will lock the account for several minutes after a run of bad passwords, so avoid repeatedly saving a device with a wrong password to "retry" it.

Limitations

  • A failed first connection is not retried. If the camera is unreachable or the password is wrong when the device loads, the driver stops there — no polling, no automatic retry. Fix the cause and reload the device from the Devices list. Once a device has connected successfully, later outages heal on their own as soon as the camera answers again.
  • The connected indicator reflects the last successful login, not live reachability. A camera unplugged after GEM connected still shows as connected. Watch the camera's own state attributes, or add a ping monitor, if you need a true online/offline signal.
  • Camera zones have no on/off behavior. They report state and accept the commands listed above; standard zone verbs do nothing.
  • Motion is polled, not pushed. There is no webhook or subscription, so reaction time is bounded by status_interval. Sub-second reaction needs a different integration path.
  • No two-way audio, recording control, playback or clip retrieval from an NVR.
  • Only the JSON API is used. If you need ONVIF events or profiles from a Reolink camera, add it through ONVIF instead of, or alongside, this driver.

Troubleshooting

SymptomCheck
Device never connectsConfirm IP Address / Hostname, Username and Password, that the device is enabled, and that the API answers in a browser. Reload the device after any fix — the first login is not retried on its own.
Login fails with correct credentialsThe account may be locked from earlier failed attempts. Wait a few minutes, or reset it in the Reolink app.
Battery camera never connectsSet protocol to https and port to 443. Argus and Go models reject the API over plain HTTP.
Repeated token errors (-6 / -7)Something else is logging in with the same account and invalidating GEM's session. Give GEM its own camera account.
Snapshot returns "rejected (token / auth)"Older firmware answers with its web page instead of an image when the token is stale. Reload the device, or change the password and update it in GEM.
One zone never reports motionCheck the zone's Address against get_channels, that the zone is enabled, and that it resolves to this device.
No zone reports motionConfirm motion detection and a detection area are configured on the channel in the Reolink app — several models ship with detection off.
ai_detected is always emptyThe camera has no AI detection, or its AI classes are switched off in the Reolink app.
PTZ, spotlight or siren rejectedThe camera account is probably operator-level. Use an admin account. Also confirm the model actually has the accessory.
Camera moves and never stopsA ptz call without duration runs until stopped — send ptz with op set to Stop, and add a duration to the original call.
  • Devices — creating the device, command sets, and reloading it.
  • Zones — zone addresses and binding a zone to a device.
  • Attributes — the Attributes tab, value types, and the History switch.
  • Access Control — linking this camera to a door for access-log snapshots.
  • ONVIF — the alternative path for cameras, including ONVIF event handling.
  • Macros — the Send Command step used for snapshots and PTZ.