Breaking Changes
pyzmNg v2 and pyzm.serve (ES 7.x)
pyzmNg has been rewritten as v2. This is the ML detection library used by zm_detect.py.
The key changes that affect ES users:
What changed:
mlapiis replaced bypyzm.serve— the remote ML detection server is now built into pyzmNg itself. No separatemlapipackage ormlapiconfig.inineeded.``Detector`` is the single entry point — replaces
DetectSequence,ObjectDetect,FaceDetect, and other scattered classes.zm_detect.pynow usesDetector.from_dict()andDetector.detect_event()instead of calling ML backends directly.Pydantic v2 configuration —
DetectorConfig,ModelConfig,StreamConfigreplace the old dict-based config parsing. Yourobjectconfig.ymlformat is unchanged (from_dict()handles the conversion), but the underlying code is stricter about typos and invalid values.Typed detection results —
DetectionResultwith.labels,.summary,.annotate()replaces the oldmatched_datanested dicts.Python 3.10+ required — pyzmNg v2 uses
matchstatements, union type syntax (X | Y), and other modern Python features.
What you need to do:
Install pyzmNg v2:
pip install pyzm(or letinstall.shhandle it)If using remote ML: Replace
mlapiwithpyzm.serve:On the GPU box:
pip install pyzm[serve]thenpython -m pyzm.serve --models yolo11s --port 5000In
objectconfig.yml: use theremote:section withml_gateway(see below)Delete
mlapiconfig.ini— it is no longer used
New: URL-mode remote detection: If your GPU box can reach ZoneMinder directly, set
ml_gateway_mode: "url"in theremote:section. The server will fetch frames directly from ZM instead of having them uploaded by the ZM box. This is more efficient.No changes to objectconfig.yml format — the
ml_sequenceandstream_sequenceYAML structures are fully backward compatible.Detector.from_dict()reads them identically.
Remote ML config — before (mlapi) vs. after (pyzm.serve):
Before (objectconfig.yml with mlapi):
remote:
ml_gateway: "http://gpu-box:5000"
# Also needed: mlapiconfig.ini on the GPU box
After (objectconfig.yml with pyzm.serve):
remote:
ml_gateway: "http://gpu-box:5000"
ml_gateway_mode: "url" # NEW — "image" (default) or "url"
ml_fallback_local: "yes"
ml_user: "!ML_USER"
ml_password: "!ML_PASSWORD"
ml_timeout: 60
On the GPU box, start with:
pip install pyzm[serve]
python -m pyzm.serve --models yolo11s --port 5000 --auth --auth-user admin --auth-password secret
Frame Match Type Setting Moved (7.x)
The keep_frame_match_type setting has been removed from the ES config
(zmeventnotification.yml) and replaced by show_frame_match_type in
objectconfig.yml under the general: section.
What changed:
The ES no longer strips the
[a]/[s]/[x]prefix from detection output. Instead, the hook itself decides whether to include it.show_frame_match_type: "yes"(default) includes the prefix — same behavior as the oldkeep_frame_match_type: "yes".show_frame_match_type: "no"suppresses the prefix entirely, both when runningzm_detect.pydirectly and when the ES sends push notifications.
What you need to do:
Running install.sh handles most of this automatically:
keep_frame_match_typeis automatically removed from yourzmeventnotification.ymlduring upgrade.show_frame_match_typeis automatically added to yourobjectconfig.ymlwith the default value of"yes".
Manual step (only if you had ``keep_frame_match_type: “no”``):
If you previously set keep_frame_match_type: "no" in your ES config to
hide the prefix, you need to manually set show_frame_match_type: "no" in
the general: section of your objectconfig.yml after running
install.sh.
Config Cleanup (7.x)
Several config keys have been moved, removed, or fixed. If you have a custom
objectconfig.yml, review the changes below. Running install.sh will
automatically merge new defaults from the example config.
Keys removed:
general.version— was never read by any codegeneral.delete_after_analyze— was never read byzm_detect.pygeneral.cpu_max_processes,general.tpu_max_processes,general.gpu_max_processes— pyzmNg reads these per-model fromml_sequenceitems, not from flat configgeneral.cpu_max_lock_wait,general.tpu_max_lock_wait,general.gpu_max_lock_wait— same reasonanimationsection (create_animation,animation_types,animation_width,animation_retry_sleep,animation_max_tries,fast_gif) — animation support removed entirely
Keys moved to correct section:
ml.stream_sequence.frame_strategy→ml.ml_sequence.general.frame_strategy—StreamConfigignores this key;DetectorConfigreads it fromml_sequence.generalml.disable_locks→ml.ml_sequence.general.disable_locks— was a sibling ofml_sequence/stream_sequenceand never reached pyzmNgmonitors.<id>.resize→monitors.<id>.stream_sequence.resize— top-levelresizein a monitor override was stored ing.configbut pyzmNg reads it fromstream_sequencemonitors.<id>.match_past_detections→monitors.<id>.ml_sequence.general.match_past_detections— same issue: top-level copy never reached pyzmNg
Default changed:
stream_sequence.resizedefault changed from800toNone(no resize). You must now explicitly setresize: 800(or another value) instream_sequenceif you want frame downscaling before detection.
No action required if you run install.sh — it merges new keys from the example config
via config_upgrade_yaml.py. If you maintain your config manually, apply the moves listed above.
YAML Migration (current master)
This is a major breaking change. All configuration files have been migrated from INI/JSON to YAML format:
zmeventnotification.ini→zmeventnotification.ymlsecrets.ini→secrets.ymlobjectconfig.ini→objectconfig.ymles_rules.json→es_rules.yml
The old INI/JSON files have been moved to the legacy/ directory for reference. The install.sh script
will automatically migrate your existing INI/JSON configs to YAML if you have old configs and no YAML
equivalents. The old files will be renamed with a .migrated suffix.
Key changes:
YAML format — All config files now use YAML syntax instead of INI sections/key-value pairs. For example,
[general]sections becomegeneral:with indented keys beneath them.Secrets file — Now
/etc/zm/secrets.yml. The!TOKEN_NAMEsyntax for referencing secrets remains the same. The secrets file itself uses a top-levelsecrets:key with tokens underneath.Templating removed —
objectconfig.ymlno longer supports{{variable}}template substitution. All values inml_sequenceandstream_sequenceare specified directly/inline. Theuse_sequenceflag no longer exists — sequences are always used.Parameter substitution removed —
common_params.pyand its substitution engine have been removed.${base_data_path}is the only substitution still supported (for path expansion).Default model changed to YOLO ONNX — YOLOv3 defaults are now disabled. The default enabled model is
yolo11nusing ONNX format via OpenCV DNN. This requires OpenCV 4.13+. Direct Ultralytics/PyTorch support has been removed in favor of ONNX via OpenCV DNN.Perl config module changed — The ES now uses
YAML::XS(via thelibyaml-libyaml-perlpackage on Debian/Ubuntu) instead ofConfig::IniFiles.Rules file is now YAML —
es_rules.jsonhas been replaced byes_rules.yml. Theinstall.shscript will auto-convert the old JSON file.New migration tools —
tools/config_upgrade.pyhas been replaced by:tools/config_upgrade_yaml.py— upgrades an existing YAML config with new keys from the exampletools/es_config_migrate_yaml.py— migrates ES INI config and secrets to YAMLtools/config_migrate_yaml.py— migratesobjectconfig.inito YAML
New feature: tag_detected_objects — When set to
yesin thehooksection ofzmeventnotification.yml, detected object labels (e.g. person, cat) will be written as Tags into ZoneMinder’s database. Requires ZM >= 1.37.44.Install flags changed —
INSTALL_YOLOV11/INSTALL_YOLOV26now default toyes.install.shdownloads both YOLOv11 and YOLOv26 ONNX models by default. YOLOv3 defaults are nowno. Set either flag tonoto skip.
Upgrading:
Run
sudo ./install.sh— it will auto-detect old INI/JSON files and migrate them to YAMLReview the generated
.ymlfiles and verify the migration looks correctOld files are renamed to
*.migratedand originals are in thelegacy/directory for referenceIf you had custom
{{}}templates inobjectconfig.ini, you will need to manually inline those values inobjectconfig.ymlas templating is no longer supported