The edge agent is a small program that runs on the shop's network — on the Suraag Raspberry Pi appliance or on any Linux, Windows or Mac PC. It watches your DVR's cameras, records a short rolling buffer continuously without re-encoding, and uploads only the moments with motion to your Suraag server, with a few seconds of pre-roll so the approach is never missed. It survives power cuts and internet outages: clips wait in a durable local queue and are retried with back-off.
At a typical shop's 5 % motion duty, four cameras upload about 1 GB a day.
1. What you need
- A DVR or NVR on the same LAN, with RTSP enabled. Hikvision, CP Plus, Dahua and XMEye families are discovered automatically; anything that speaks ONVIF or RTSP works.
- The DVR's admin username and password.
- Python 3.10 or newer and
ffmpegon the PATH (the Pi appliance ships with both). - An enrolment code and device ID from your administrator (Settings & Integrations → Edge devices → Add device — see the Administrator guide).
2. Install
pip install -r requirements.txt # ffmpeg must also be on the PATH
On the Pi appliance the agent lives in /opt/suraag-edge and is already installed.
3. Enrol
python main.py enroll --server https://suraag.xyz --code <ONE-TIME-CODE> --device-id <DEVICE-ID>
The server exchanges the single-use code for a long-lived device token, which the agent stores in ~/.suraag-edge/device.json (readable by its owner only). A used or expired code is refused — ask your administrator for a fresh one. The token can be revoked from the console at any time.
4. Discover the DVR's cameras
python main.py discover # ONVIF broadcast on the LAN
python main.py discover --ip 192.168.1.108 --user admin --password <PASSWORD> # probe one DVR
Discovery tries, in order: ONVIF WS-Discovery with GetStreamUri per channel, then the known RTSP URL patterns for the Hikvision, Dahua / CP Plus and XMEye families. Every candidate URL is checked with a six-second ffprobe before it is reported, and the output is a ready-to-paste channels: block for the configuration file. If nothing is found: check the DVR's IP and credentials, enable ONVIF in its network settings, or copy the RTSP URL from its manual.
RTSP cheat-sheet:
| Family | Main stream | Substream |
|---|---|---|
| Hikvision | rtsp://user:pass@IP:554/Streaming/Channels/101 | …/Channels/102 |
| CP Plus / Dahua | rtsp://user:pass@IP:554/cam/realmonitor?channel=1&subtype=0 | …&subtype=1 |
| XMEye | rtsp://IP:554/user=USER&password=PASS&channel=1&stream=0.sdp | …&stream=1.sdp |
5. Configure
cp config.example.yaml config.yaml
work_dir: ~/.suraag-edge/work
channels:
front_counter:
rtsp: "rtsp://admin:PASSWORD@192.168.1.108:554/cam/realmonitor?channel=1&subtype=0"
rtsp_substream: "rtsp://admin:PASSWORD@192.168.1.108:554/cam/realmonitor?channel=1&subtype=1"
camera_id: cam_shop_front_counter # stable id — keeps analysis history together
segment_seconds: 15
quiet_seconds: 10 # motion window closes after this much stillness
# always_upload: true # ship everything (testing only — costly)
entrance:
rtsp: "rtsp://admin:PASSWORD@192.168.1.108:554/cam/realmonitor?channel=2&subtype=0"
rtsp_substream: "rtsp://admin:PASSWORD@192.168.1.108:554/cam/realmonitor?channel=2&subtype=1"
camera_id: cam_shop_entrance
rtspis recorded (copied, not decoded) insegment_secondspieces.rtsp_substreamis the low-resolution stream the motion detector watches at about 3 frames per second; using it keeps a Pi 4 comfortable with eight channels.camera_idmust be stable — it is how the console keeps a camera's history, zones and armed hours together. Use the same id your administrator assigned to the device.quiet_secondsdecides when a motion window ends; segments overlapping a window are uploaded with the segment before it as pre-roll.
6. Check the motion gate offline
python main.py test-motion --video some_clip.mp4
Prints the windows the gate would ship, e.g. 120s video → 3 windows, 34s motion (28% duty), so you can tune quiet_seconds before going live.
7. Run
python main.py run --config config.yaml
The agent prints one line per upload (uploaded front_counter_…mp4 (queued) or (dedup) when the server already had that clip) and a back-off notice with the pending count when the uplink is down. Every minute it sends a heartbeat with the pending count and channel list — this is what drives Last seen in the console and the live / quiet / offline status on Live Watch. Uploads larger than the single-request limit go through the chunked, resumable upload endpoint.
As a service (Linux)
systemd/suraag-edge.service runs the agent as user suraag from /opt/suraag-edge, restarts it on failure and starts it on boot:
sudo cp systemd/suraag-edge.service /etc/systemd/system/
sudo systemctl enable --now suraag-edge
journalctl -u suraag-edge -f
8. What leaves the shop
Only motion clips from the channels you configured, the heartbeat described above, and the enrolment request. The agent never opens ports inbound, never streams continuously, and never uploads anything while always_upload is off and there is no motion. Clips are purged from the server after the retention period your administrator set (14 days by default), together with everything derived from them.
9. Updating
The agent is plain Python. To update, replace the files in the install directory with the new release, keep config.yaml and ~/.suraag-edge/, and restart the service (sudo systemctl restart suraag-edge). Enrolment survives updates; you do not need a new code unless the device was revoked.
10. Troubleshooting
| Symptom | Check |
|---|---|
enrollment failed: 4xx | The code was used, expired or revoked — request a new one. Confirm --server includes https://. |
| No streams found | DVR IP and credentials; ONVIF enabled on the DVR; try --ip with the DVR's address; paste the RTSP URL from the manual. |
upload failed … retry with backoff | Internet uplink. Clips are safe in the spool and go out when it returns. |
| Tile shows SIGNAL LOST in the console | The heartbeat has not arrived for over an hour — the agent is stopped or the PC is off. |
| Alerts arrive late | Check the analysis engine's state in System Health; uploads queue while it is stopped. |
| Too many clips | Raise quiet_seconds, lower the substream resolution, or make sure always_upload is not set. |
Support: support@suraag.xyz with the device ID, the last lines of the agent's output and the DVR model.