1. Install
a) Same box (ZM and gateway together) One install, with the serve extra:
sudo /opt/zoneminder/venv/bin/pip install --force-reinstall --no-deps \
"pyzm[serve] @ git+https://github.com/ZoneMinder/pyzmNg.git@master"
b) Separate boxes ZM box — client only, no extra:
sudo /opt/zoneminder/venv/bin/pip install --force-reinstall --no-deps \
"pyzm @ git+https://github.com/ZoneMinder/pyzmNg.git@master"
Gateway box — needs the serve extra. Use whatever env runs the server; if it's a fresh box:
python3 -m venv ~/pyzm-serve
~/pyzm-serve/bin/pip install "pyzm[serve] @ git+https://github.com/ZoneMinder/pyzmNg.git@master"
Substitute ~/pyzm-serve/bin/python for /opt/zoneminder/venv/bin/python everywhere below on that box.
Verify on each box:
/opt/zoneminder/venv/bin/python -c "import pyzm;print(pyzm.__version__)" # 2.5.0
Gateway only:
grep -c '"/infer"' /opt/zoneminder/venv/lib/python3.12/site-packages/pyzm/serve/app.py # 1
2. Run the gateway Model files must be on this box, under /var/lib/zmeventnotification/models or --base-path. Publish each model under the name your objectconfig.yml uses — that's what the client asks for:
/opt/zoneminder/venv/bin/python -m pyzm.serve \
--models "YOLOv11 ONNX=yolo11s" --host 0.0.0.0 --port 5000 --debug
Or from a file:
# /etc/zm/pyzm-serve.yml
host: "0.0.0.0"
port: 5000
base_path: "/var/lib/zmeventnotification/models"
processor: cpu
models:
- "YOLOv11 ONNX=yolo11s"
/opt/zoneminder/venv/bin/python -m pyzm.serve --config /etc/zm/pyzm-serve.yml --debug
Check it, from the ZM box (localhost if same box):
curl -s http://<gateway>:5000/health
curl -s http://<gateway>:5000/models | python3 -m json.tool
curl -s http://<gateway>:5000/openapi.json | python3 -c "import sys,json;print(list(json.load(sys.stdin)['paths']))"
/models must list the exact names your config uses. Paths must include /infer.
3. Configure the client
objectconfig.yml on the ZM box:
remote:
ml_gateway: "http://<gateway-ip>:5000"
ml_gateway_mode: "url"
ml_fallback_local: "no"
ml_timeout: 60
ml_fallback_local: "no" for testing only — a gateway problem then fails loudly instead of quietly running local. Restore "yes" after. url mode has the gateway pull frames from ZM, so check from the gateway box:
4. Test
sudo -u www-data /var/lib/zmeventnotification/bin/zm_detect.py \
--config /etc/zm/objectconfig.yml --eventid <EID> --monitorid <MID> --debug
See logs on both sides (enable debug logs)
Then set ml_fallback_local: "yes" back.