pyzm package
ZoneMinder Client
The typed client for the ZoneMinder REST API. Returns dataclass models
(Monitor, Event, Zone) instead of raw dicts.
Top-level ZoneMinder client – the one-stop public API.
Usage:
from pyzm import ZMClient
zm = ZMClient(api_url="https://zm.example.com/zm/api", user="admin", password="secret")
for m in zm.monitors():
print(m.name, m.function)
for z in m.get_zones():
print(f" zone: {z.name}")
for ev in zm.events(monitor_id=1, since="1 hour ago"):
print(ev.id, ev.cause)
ev.update_notes("detected")
- class pyzm.client.ZMClient(api_url: str | None = None, user: str | None = None, password: str | None = None, *, portal_url: str | None = None, verify_ssl: bool = True, timeout: int = 30, db_user: str | None = None, db_password: str | None = None, db_host: str | None = None, db_name: str | None = None, conf_path: str | None = None, config: ZMClientConfig | None = None)
High-level ZoneMinder client.
- Parameters:
api_url – Full ZM API URL (e.g.
https://server/zm/api).user – ZM username.
Nonewhen auth is disabled.password – ZM password.
portal_url – Full portal URL (e.g.
https://server/zm). Auto-derived from api_url when not provided.verify_ssl – Whether to verify SSL certificates. Set to
Falsefor self-signed certs.config – A pre-built
ZMClientConfig. When provided, all other keyword args are ignored.
- __init__(api_url: str | None = None, user: str | None = None, password: str | None = None, *, portal_url: str | None = None, verify_ssl: bool = True, timeout: int = 30, db_user: str | None = None, db_password: str | None = None, db_host: str | None = None, db_name: str | None = None, conf_path: str | None = None, config: ZMClientConfig | None = None) None
- property api: ZMAPI
The underlying low-level API (for advanced use).
- property api_version: str | None
- config(name: str) dict
Get a single config parameter by name.
- configs() list[dict]
List all ZM configuration parameters.
- delete_events(*, monitor_id: int | None = None, before: str | None = None, min_alarm_frames: int | None = None, limit: int = 100) int
Query events matching filters and delete each one. Returns count deleted.
- disk_usage() dict
Get disk usage info.
- events(*, event_id: int | None = None, monitor_id: int | None = None, since: str | None = None, until: str | None = None, min_alarm_frames: int | None = None, object_only: bool = False, limit: int = 100) list[pyzm.models.zm.Event]
Query events with optional filters.
- is_running() bool
Check if the ZM daemon is running.
- monitor(monitor_id: int | str) Monitor
Return a single monitor by ID (int) or name (str, case-insensitive).
- monitors(*, force_reload: bool = False) list[pyzm.models.zm.Monitor]
Return all monitors. Cached after first call.
- notification(notification_id: int) Notification
Fetch a single notification registration by ID.
- notifications() list[pyzm.models.zm.Notification]
Fetch all push notification token registrations.
- restart() Any
- servers() list[dict]
List all ZM servers (multi-server setups).
- set_config(name: str, value: str) None
Set a config parameter value.
- set_state(state: str) Any
Set ZM to a named state (e.g. ‘start’, ‘stop’, ‘restart’).
- start() Any
- states() list[dict]
List all available ZM states.
- stop() Any
- storage() list[dict]
List all storage areas with disk usage.
- system_load() dict[str, float]
Get system load averages.
- timezone() str
Get server timezone.
- property zm_version: str | None
Logging
Logging utilities for pyzm. setup_zm_logging() provides ZM-native logging
(file, database, syslog) matching Perl’s Logger.pm format.
pyzm.log – ZoneMinder-native logging via stdlib logging.
from pyzm.log import setup_zm_logging adapter = setup_zm_logging(name=”zmesdetect_m1”) adapter.Debug(1, “Hello from pyzm”) adapter.Info(“Something happened”)
- class pyzm.log.ZMLogAdapter(logger: Logger, config: dict[str, Any], process_name: str)
Drop-in replacement for the
pyzm.ZMLogmodule-level API.Provides
Debug,Info,Warning,Error,Fatal,close, andget_configmethods that match the legacy interface.- Debug(level: int = 1, msg: str | None = None, caller: Any = None) None
- Error(msg: str | None = None, caller: Any = None) None
- Fatal(msg: str | None = None, caller: Any = None) None
- Info(msg: str | None = None, caller: Any = None) None
- Warning(msg: str | None = None, caller: Any = None) None
- close() None
- get_config() dict[str, Any]
- pyzm.log.setup_zm_logging(name: str | None = None, override: dict[str, Any] | None = None) ZMLogAdapter
Initialize ZM-native logging and return a
ZMLogAdapter.This is the v2 replacement for
pyzm.ZMLog.init(). It reads ZM config files, the DBConfigtable, and environment variables, then attaches appropriate handlers to a stdlib logger.- Parameters:
name – Log component name (e.g.
"zmesdetect_m1").override – Dict of config keys that override all other sources.
Machine Learning
The ML detection pipeline. Detector is the main entry point –
it manages backends, model sequencing, and result filtering.
Top-level Detector – public API for pyzm v2 ML detection.
Usage:
from pyzm import Detector
# Auto-discover all models in the default path
det = Detector()
# Pick specific models by name (resolved from base_path)
det = Detector(models=["yolo11s", "yolo26s"])
# Custom model directory
det = Detector(models=["yolo11s"], base_path="/my/models")
# Detect
result = det.detect("/path/to/image.jpg")
- class pyzm.ml.detector.Detector(config: DetectorConfig | None = None, models: list[str | pyzm.models.config.ModelConfig] | None = None, base_path: str | Path = '/var/lib/zmeventnotification/models', processor: str | Processor = Processor.CPU, *, gateway: str | None = None, gateway_mode: str = 'url', gateway_timeout: int = 60, gateway_username: str | None = None, gateway_password: str | None = None)
Top-level detection API.
- Parameters:
config – A fully specified
DetectorConfig. Takes precedence over models and base_path.models – A convenience shorthand – a list of model name strings (e.g.
["yolo11s", "yolo26s"]) orModelConfigobjects. String names are resolved against base_path to find weight, config, and label files automatically.base_path – Directory containing model subdirectories. Defaults to
/var/lib/zmeventnotification/models. When models isNoneand config isNone, all models in this directory are auto-discovered. When models contains name strings, they are resolved against this path.processor – Hardware target for auto-discovered/resolved models. Accepts
"cpu","gpu","tpu"or aProcessorenum. Ignored when config is provided or when models containsModelConfigobjects (which carry their own processor).gateway – URL of a remote
pyzm.serveserver (e.g.http://gpu:5000). When set,detect()sends images to the remote server instead of running inference locally.gateway_mode –
"url"(default) sends frame URLs so the server fetches images directly from ZoneMinder."image"sends JPEG-encoded frames instead. Only applies todetect_event(); single-imagedetect()calls always use image mode.gateway_timeout – HTTP timeout in seconds for remote detection requests.
gateway_username – Username for remote server authentication (optional).
gateway_password – Password for remote server authentication (optional).
- __init__(config: DetectorConfig | None = None, models: list[str | pyzm.models.config.ModelConfig] | None = None, base_path: str | Path = '/var/lib/zmeventnotification/models', processor: str | Processor = Processor.CPU, *, gateway: str | None = None, gateway_mode: str = 'url', gateway_timeout: int = 60, gateway_username: str | None = None, gateway_password: str | None = None) None
- detect(input: str | np.ndarray | list[tuple[int | str, np.ndarray]], zones: list['Zone'] | None = None) DetectionResult
Run detection on one or more images.
- Parameters:
input –
str: path to an image file.np.ndarray: a single BGR image array.list[tuple[frame_id, np.ndarray]]: multiple frames. The best frame is chosen byframe_strategy.
zones – Optional detection zone polygons.
- Return type:
- detect_audio(audio_path: str, event_week: int = -1, lat: float = -1.0, lon: float = -1.0) DetectionResult
Run audio detection on a standalone audio file.
This is a convenience method for running audio backends (e.g. BirdNET) on an audio file without a ZoneMinder event. Any format that ffmpeg can read (WAV, MP3, MP4, etc.) is supported.
- Parameters:
audio_path – Path to an audio file.
event_week – ISO week number (1–48) for seasonal filtering. -1 disables.
lat – Latitude/longitude for location-based species filtering. -1 disables.
lon – Latitude/longitude for location-based species filtering. -1 disables.
- Return type:
- detect_event(zm_client: ZMClient, event_id: int, zones: list['Zone'] | None = None, stream_config: StreamConfig | None = None) DetectionResult
Extract frames from a ZM event and run detection.
- Parameters:
zm_client – A
pyzm.client.ZMClient.event_id – ZoneMinder event ID.
zones – Optional detection zones.
stream_config – Controls frame extraction (which frames, resize, etc.).
- Return type:
ModelPipeline orchestrates the model sequence for a single frame.
It groups models by type (object, face, alpr), runs them in sequence with the configured match strategy and fallback, applies pre_existing_labels checks, and runs zone/size/pattern filters.
- class pyzm.ml.pipeline.ModelPipeline(detector_config: DetectorConfig)
Runs the full detection pipeline for a single image frame.
The pipeline:
Groups enabled models by
ModelType(object, face, alpr).For each type (in config order), runs the model variants in sequence applying the configured
MatchStrategy.Applies
pre_existing_labelsgates between types.Runs zone, size, and pattern filters on the combined results.
- __init__(detector_config: DetectorConfig) None
- load() None
Pre-load all enabled backends.
- prepare() None
Create backend objects without loading weights (lazy mode).
Each backend will load its own weights on first
detect()call thanks to theif self._model is None: self.load()guard in every backend’sdetect()method.
- run(image: np.ndarray, zones: list['Zone'] | None = None, original_shape: tuple[int, int] | None = None) DetectionResult
Run the full pipeline on image and return a
DetectionResult.- Parameters:
image – BGR numpy array (OpenCV format).
zones – Optional list of
Zoneobjects. IfNone, a full-image zone is used.original_shape –
(height, width)of the image before resizing. When the image was resized for detection, zone polygons (which are in original coordinates) need to be rescaled to match.Nonemeans no rescaling is needed.
- set_audio_context(audio_path: str | None, event_week: int = -1, monitor_lat: float = -1.0, monitor_lon: float = -1.0) None
Set audio file context for BirdNET / audio backends.
Pure functions for filtering detections.
Every function takes and returns Detection / BBox objects
from pyzm.models.detection. Heavy dependencies (Shapely, pickle) are
imported at function level so they remain optional.
- pyzm.ml.filters.filter_by_pattern(detections: list[pyzm.models.detection.Detection], pattern: str) list[pyzm.models.detection.Detection]
Keep only detections whose label matches pattern (regex).
- pyzm.ml.filters.filter_by_size(detections: list[pyzm.models.detection.Detection], max_size: str | None, image_shape: tuple[int, int]) list[pyzm.models.detection.Detection]
Filter detections whose area exceeds max_size.
max_size may be
"50%"(of image area) or"300px"(absolute pixel area). If max_size isNoneor empty, all detections pass.
- pyzm.ml.filters.filter_by_zone(detections: list[pyzm.models.detection.Detection], zones: list[dict], image_shape: tuple[int, int]) tuple[list[pyzm.models.detection.Detection], list[pyzm.models.detection.BBox]]
Keep only detections whose bounding box intersects at least one zone.
- Parameters:
detections – Raw detections from a backend.
zones – Each zone is a dict-like with
name(str),points(list of (x, y) tuples), and optionallypattern(str | None). These can bepyzm.models.zm.Zoneobjects turned into dicts viaas_dict(), or simple dicts.image_shape –
(height, width)of the analysed image. If zones is empty a full-image zone is synthesised.
- Returns:
kept – Detections that intersect at least one zone and whose label matches the zone’s pattern (or the default
.*).error_boxes – Bounding boxes of detections that were filtered out.
- pyzm.ml.filters.filter_past_per_type(detections: list[Detection], config: DetectorConfig) list[Detection]
Apply past-detection dedup per model-type using config settings.
This is the standalone version of the logic formerly in
ModelPipeline._filter_past_per_type().
- pyzm.ml.filters.load_past_detections(past_file: str) tuple[list[list[int]], list[str]]
Load
(saved_boxes, saved_labels)from a pickle file.Returns
([], [])on missing file, empty file, or read error.
- pyzm.ml.filters.match_past_detections(detections: list[pyzm.models.detection.Detection], saved_boxes: list[list[int]], saved_labels: list[str], max_diff_area: str = '5%', label_area_overrides: dict[str, str] | None = None, ignore_labels: list[str] | None = None, aliases: list[list[str]] | None = None) list[pyzm.models.detection.Detection]
Filter detections against past data. Pure logic, no I/O.
- Parameters:
saved_boxes – Previously saved detection data (from
load_past_detections()).saved_labels – Previously saved detection data (from
load_past_detections()).max_diff_area – Default area tolerance, e.g.
"5%"or"300px".label_area_overrides – Per-label area tolerance, e.g.
{"car": "10%"}.ignore_labels – Labels to skip entirely (always kept, never matched).
aliases – Groups of equivalent labels, e.g.
[["car","bus","truck"]].
- pyzm.ml.filters.save_past_detections(past_file: str, detections: list[pyzm.models.detection.Detection]) None
Save current detections to a pickle file for future comparisons.
Configuration Models
Pydantic v2 models for all pyzm configuration: ZM client settings, detector/model parameters, and stream extraction options.
Typed configuration models for pyzm v2.
All configuration is expressed as Pydantic models with sensible defaults. Typos in field names cause immediate validation errors instead of silent failures.
- class pyzm.models.config.DetectorConfig(*args: Any, **kwargs: Any)
Top-level configuration for the
pyzm.ml.Detector.- frame_strategy: FrameStrategy = 'most_models'
- classmethod from_dict(ml_options: dict[str, Any]) DetectorConfig
Build a DetectorConfig from a nested
ml_sequencedict.This lets YAML configs work directly with the v2 API.
- image_path: str = '/var/lib/zmeventnotification/images'
- match_past_detections: bool = False
- match_strategy: MatchStrategy = 'most'
- max_detection_size: str | None = None
- monitor_id: str | None = None
- past_det_max_diff_area: str = '5%'
- pattern: str = '.*'
- class pyzm.models.config.FrameStrategy(value, names=None, *, module=None, qualname=None, type=None, start=1, boundary=None)
How to pick the best frame across all analysed frames.
- FIRST = 'first'
- FIRST_NEW = 'first_new'
- MOST = 'most'
- MOST_MODELS = 'most_models'
- MOST_UNIQUE = 'most_unique'
- class pyzm.models.config.MatchStrategy(value, names=None, *, module=None, qualname=None, type=None, start=1, boundary=None)
How to pick results when multiple model variants run for the same type.
- FIRST = 'first'
- MOST = 'most'
- MOST_UNIQUE = 'most_unique'
- UNION = 'union'
- class pyzm.models.config.ModelConfig(*args: Any, **kwargs: Any)
Configuration for a single ML model/backend in the pipeline.
- alpr_key: str | None = None
- alpr_service: str | None = None
- alpr_url: str | None = None
- aws_access_key_id: str | None = None
- aws_region: str = 'us-east-1'
- aws_secret_access_key: str | None = None
- birdnet_lat: float = -1.0
- birdnet_lon: float = -1.0
- birdnet_min_conf: float = 0.25
- birdnet_overlap: float = 0.0
- birdnet_sensitivity: float = 1.0
- config: str | None = None
- disable_locks: bool = False
- enabled: bool = True
- face_model: str = 'cnn'
- face_num_jitters: int = 1
- face_recog_dist_threshold: float = 0.6
- face_train_model: str = 'cnn'
- face_upsample_times: int = 1
- framework: ModelFramework = 'opencv'
- known_faces_dir: str | None = None
- labels: str | None = None
- max_detection_size: str | None = None
- max_lock_wait: int = 120
- max_processes: int = 1
- min_confidence: float = 0.3
- model_height: int | None = None
- model_width: int | None = None
- name: str | None = None
- pattern: str = '.*'
- platerec_min_dscore: float = 0.1
- platerec_min_score: float = 0.2
- save_unknown_faces: bool = False
- save_unknown_faces_leeway_pixels: int = 0
- unknown_faces_dir: str | None = None
- weights: str | None = None
- class pyzm.models.config.ModelFramework(value, names=None, *, module=None, qualname=None, type=None, start=1, boundary=None)
Concrete ML backend implementation.
- BIRDNET = 'birdnet'
- CORAL = 'coral_edgetpu'
- FACE_DLIB = 'face_dlib'
- FACE_TPU = 'face_tpu'
- HOG = 'hog'
- OPENALPR = 'openalpr'
- OPENCV = 'opencv'
- PLATE_RECOGNIZER = 'plate_recognizer'
- REKOGNITION = 'aws_rekognition'
- VIRELAI = 'virelai'
- class pyzm.models.config.ModelType(value, names=None, *, module=None, qualname=None, type=None, start=1, boundary=None)
Kind of ML model in the detection pipeline.
- ALPR = 'alpr'
- AUDIO = 'audio'
- FACE = 'face'
- OBJECT = 'object'
- class pyzm.models.config.Processor(value, names=None, *, module=None, qualname=None, type=None, start=1, boundary=None)
Hardware target for inference.
- CPU = 'cpu'
- GPU = 'gpu'
- TPU = 'tpu'
- class pyzm.models.config.ServerConfig(*args: Any, **kwargs: Any)
Configuration for the pyzm ML detection server (
pyzm.serve).- auth_enabled: bool = False
- auth_password: pydantic.SecretStr = ''
- auth_username: str = 'admin'
- base_path: str = '/var/lib/zmeventnotification/models'
- detector_config: DetectorConfig | None = None
- host: str = '0.0.0.0'
- port: int = 5000
- token_expiry_seconds: int = 3600
- token_secret: str = 'change-me'
- class pyzm.models.config.StreamConfig(*args: Any, **kwargs: Any)
Controls how frames are extracted from a ZM event or video file.
- contig_frames_before_error: int = 5
- convert_snapshot_to_fid: bool = False
- delay: int = 0
- delay_between_frames: int = 0
- delay_between_snapshots: int = 0
- delete_after_analyze: bool = False
- disable_ssl_cert_check: bool = True
- download: bool = False
- download_dir: str = '/tmp'
- frame_skip: int = 1
- classmethod from_dict(d: dict[str, Any]) StreamConfig
Build a StreamConfig from a
stream_sequencedict.Handles the string-valued conventions of the YAML config:
resize='no'→None,resize='800'→800frame_set='snapshot,alarm,1'→['snapshot', 'alarm', '1']'yes'/'no'strings →bool
Keys not relevant to StreamConfig (like
api,polygons,mid,frame_strategy) are silently ignored.
- max_attempts: int = 1
- max_frames: int = 0
- resize: int | None = None
- save_frames: bool = False
- save_frames_dir: str = '/tmp'
- sleep_between_attempts: int = 3
- start_frame: int = 1
- class pyzm.models.config.TypeOverrides(*args: Any, **kwargs: Any)
Per-model-type overrides (e.g. under
object.general,face.general).- aliases: list[list[str]] | None = None
- ignore_past_detection_labels: list[str] | None = None
- match_past_detections: bool | None = None
- match_strategy: MatchStrategy | None = None
- max_detection_size: str | None = None
- past_det_max_diff_area: str | None = None
- class pyzm.models.config.ZMClientConfig(*args: Any, **kwargs: Any)
Connection settings for ZoneMinder.
- api_url: str
- basic_auth_password: SecretStr | None = None
- basic_auth_user: str | None = None
- conf_path: str | None = None
- db_host: str | None = None
- db_name: str | None = None
- db_password: SecretStr | None = None
- db_user: str | None = None
- password: SecretStr | None = None
- portal_url: str | None = None
- timeout: int = 30
- user: str | None = None
- verify_ssl: bool = True
Detection result models.
Results are proper objects instead of parallel arrays - no more index-mismatch
bugs from matched_data['labels'][i] / matched_data['boxes'][i].
- class pyzm.models.detection.BBox(x1: int, y1: int, x2: int, y2: int)
Axis-aligned bounding box (x1, y1) top-left to (x2, y2) bottom-right.
- property area: int
- as_list() list[int]
- as_polygon_coords() list[tuple[int, int]]
Return four corners suitable for Shapely
Polygon().
- property center: tuple[int, int]
- property height: int
- property width: int
- x1: int
- x2: int
- y1: int
- y2: int
- class pyzm.models.detection.Detection(label: str, confidence: float, bbox: BBox, model_name: str = '', detection_type: str = 'object')
A single detected object/face/plate.
- confidence: float
- detection_type: str
- label: str
- matches_pattern(pattern: str) bool
- model_name: str
- class pyzm.models.detection.DetectionResult(detections: list[Detection] = <factory>, frame_id: int | str | None = None, image: np.ndarray | None = None, image_dimensions: dict[str, tuple[int, int] | None] = <factory>, error_boxes: list[BBox] = <factory>)
Aggregate result returned by
Detector.detect().- annotate(*, polygons=None, write_conf=True, poly_color=(255, 255, 255), poly_thickness=1, draw_error_boxes=True, **draw_kwargs) np.ndarray
Draw bounding boxes on
self.imageand return the annotated copy.- Parameters:
polygons (list[dict] | None) – Zone polygons to draw. Each dict must have a
'value'key containing a sequence of(x, y)coordinate pairs.write_conf (bool) – If True (default), append
confidence%to each label.poly_color (tuple[int, int, int]) – BGR colour for polygon outlines (default white).
poly_thickness (int) – Line thickness for polygon outlines (default 1).
draw_error_boxes (bool) – If True (default), draw red rectangles for error boxes.
- property boxes: list[list[int]]
- property confidences: list[float]
- filter_by_pattern(pattern: str) DetectionResult
Return a new result keeping only detections whose label matches pattern.
- frame_id: int | str | None = None
- classmethod from_dict(data: dict) DetectionResult
Reconstruct a DetectionResult from a wire dict (inverse of
to_dict).The image field is left as
None— the caller already has the original image and can attach it after reconstruction.
- image: np.ndarray | None = None
- image_dimensions: dict[str, tuple[int, int] | None]
- property labels: list[str]
- property matched: bool
- property summary: str
Human-readable one-liner, e.g.
person:97% car:85%.
- to_dict() dict
Return data in the
matched_datadict format.
ZoneMinder data models.
Typed representations of ZM resources (monitors, events, frames, zones).
Models carry a _client back-reference so that resource operations
(arm, disarm, zones, frames, etc.) are methods on the object itself.
- class pyzm.models.zm.Event(id: int, name: str = '', monitor_id: int = 0, cause: str = '', notes: str = '', start_time: datetime | None = None, end_time: datetime | None = None, length: float = 0.0, frames: int = 0, alarm_frames: int = 0, max_score: int = 0, max_score_frame_id: int | None = None, storage_path: str = '', _raw: dict = <factory>, _client: ZMClient | None = None)
A ZoneMinder event.
When retrieved via
ZMClient, the event carries a back-reference so that resource operations are available as methods:ev = zm.event(12345) ev.update_notes("person detected") ev.tag(["person", "car"]) ev.delete()
- alarm_frames: int = 0
- cause: str = ''
- delete() None
Delete this event.
- end_time: datetime | None = None
- extract_frames(stream_config: StreamConfig | None = None) tuple[list[tuple[int | str, Any]], dict[str, tuple[int, int] | None]]
Extract frames from this event as numpy arrays.
- frames: int = 0
- classmethod from_api_dict(data: dict, client: ZMClient | None = None) Event
Build from a ZM API
EventJSON dict.
- get_frames() list[pyzm.models.zm.Frame]
Fetch per-frame metadata (Score, Type, Delta) for this event.
- id: int
- length: float = 0.0
- max_score: int = 0
- max_score_frame_id: int | None = None
- monitor_id: int = 0
- name: str = ''
- notes: str = ''
- path() str | None
Construct the filesystem path for this event.
- raw() dict
Return the full, unmodified API response dict.
- save_objdetect(image, metadata: dict, path_override: str | None = None) str | None
Write
objdetect.jpgandobjects.jsonto the event directory.Returns the directory path used, or
Noneif no path was available.
- start_time: datetime | None = None
- storage_path: str = ''
- tag(labels: list[str]) None
Tag this event with detected object labels.
- update_notes(notes: str) None
Update the Notes field of this event.
- class pyzm.models.zm.Frame(frame_id: int | str, event_id: int, type: str = '', score: int = 0, delta: float = 0.0, _raw: dict = <factory>)
Metadata for a single event frame.
- delta: float = 0.0
- event_id: int
- frame_id: int | str
- raw() dict
Return the full, unmodified API response dict.
- score: int = 0
- type: str = ''
- class pyzm.models.zm.Monitor(id: int, name: str = '', function: str = '', enabled: bool = True, width: int = 0, height: int = 0, type: str = '', controllable: bool = False, control_id: int | None = None, zones: list[Zone] = <factory>, status: MonitorStatus = <factory>, _raw: dict = <factory>, _client: ZMClient | None = None, _ptz_caps_cache: PTZCapabilities | None = None)
A ZoneMinder monitor.
When retrieved via
ZMClient, the monitor carries a back-reference so that resource operations are available as methods:m = zm.monitor(1) m.zones() m.arm() m.update(Function="Modect")
- alarm_status() dict
Get alarm status for this monitor.
- arm() dict
Trigger alarm ON for this monitor.
- control_id: int | None = None
- controllable: bool = False
- daemon_status() dict
Get daemon status for this monitor’s capture daemon (zmc).
- delete_events(**kwargs) int
Bulk-delete events scoped to this monitor.
Accepts the same filter params as
ZMClient.delete_events():before,min_alarm_frames,limit.
- disarm() dict
Cancel alarm for this monitor.
- enabled: bool = True
- events(**kwargs) list[pyzm.models.zm.Event]
Query events scoped to this monitor.
Accepts the same filter params as
ZMClient.events():since,until,min_alarm_frames,object_only,limit.
- classmethod from_api_dict(data: dict, client: ZMClient | None = None) Monitor
Build from a ZM API
MonitorJSON dict.
- function: str = ''
- get_zones() list[pyzm.models.zm.Zone]
Fetch detection zones for this monitor from the ZM API.
- height: int = 0
- id: int
- name: str = ''
- ptz(command: str, *, mode: str = 'auto', preset: int = 1, stop_after: float | None = None) None
Send a PTZ command to this monitor.
- Parameters:
command – Direction or action:
"up","down","left","right","up_left","up_right","down_left","down_right","zoom_in","zoom_out","stop","home","preset".mode – Movement mode —
"auto"(default, picks the best mode supported by the camera’s control profile),"con"(continuous),"rel"(relative), or"abs"(absolute).preset – Preset number for
command="preset"(default 1).stop_after – If set, automatically send a
"stop"command after this many seconds. Blocks for the duration.
- ptz_capabilities() PTZCapabilities
Fetch PTZ capabilities for this monitor’s control profile.
- raw() dict
Return the full, unmodified API response dict.
- snapshot_url(**kwargs) str
Return a single-frame snapshot URL for this monitor.
- Parameters:
**kwargs – Extra query params forwarded to the URL (e.g.
scale=50).
- status: MonitorStatus
- streaming_url(protocol: str = 'mjpeg', **kwargs) str
Return a streaming URL for this monitor.
- Parameters:
protocol – Streaming protocol. Currently only
"mjpeg"is supported.**kwargs – Extra query params forwarded to the URL (e.g.
maxfps=5,scale=50).
- type: str = ''
- update(**kwargs) None
Update monitor fields (Function, Enabled, Name, etc.).
- width: int = 0
- class pyzm.models.zm.MonitorStatus(state: str = '', fps: float = 0.0, analysis_fps: float = 0.0, bandwidth: int = 0, capturing: str = 'None', _raw: dict = <factory>)
Runtime status of a monitor.
- analysis_fps: float = 0.0
- bandwidth: int = 0
- capturing: str = 'None'
- fps: float = 0.0
- raw() dict
Return the full, unmodified API response dict.
- state: str = ''
- class pyzm.models.zm.Notification(id: int, user_id: int = 0, token: str = '', platform: str = '', monitor_list: str | None = None, interval: int = 0, push_state: str = 'enabled', app_version: str | None = None, profile: str | None = None, badge_count: int = 0, last_notified_at: datetime | None = None, _raw: dict = <factory>, _client: ZMClient | None = None)
A ZoneMinder push notification token registration.
- app_version: str | None = None
- badge_count: int = 0
- delete() None
- classmethod from_api_dict(data: dict, client: ZMClient | None = None) Notification
Build from a ZM API Notification JSON dict.
- id: int
- interval: int = 0
- is_throttled() bool
Check if this token is currently throttled.
- last_notified_at: datetime | None = None
- monitor_list: str | None = None
- monitors() list[int] | None
Parse MonitorList into list of ints. None = all monitors.
- platform: str = ''
- profile: str | None = None
- push_state: str = 'enabled'
- raw() dict
- should_notify(monitor_id: int) bool
Check if this token should receive notifications for the given monitor.
- token: str = ''
- update_last_sent(badge: int | None = None) None
Update LastNotifiedAt to now and optionally set BadgeCount.
- user_id: int = 0
- class pyzm.models.zm.PTZCapabilities(can_move: bool = False, can_move_con: bool = False, can_move_rel: bool = False, can_move_abs: bool = False, can_move_diag: bool = False, can_zoom: bool = False, can_zoom_con: bool = False, can_zoom_rel: bool = False, can_zoom_abs: bool = False, can_pan: bool = False, can_tilt: bool = False, has_presets: bool = False, num_presets: int = 0, has_home_preset: bool = False, can_set_presets: bool = False, can_reset: bool = False, _raw: dict = <factory>)
PTZ control profile capabilities.
- can_move: bool = False
- can_move_abs: bool = False
- can_move_con: bool = False
- can_move_diag: bool = False
- can_move_rel: bool = False
- can_pan: bool = False
- can_reset: bool = False
- can_set_presets: bool = False
- can_tilt: bool = False
- can_zoom: bool = False
- can_zoom_abs: bool = False
- can_zoom_con: bool = False
- can_zoom_rel: bool = False
- classmethod from_api_dict(data: dict) PTZCapabilities
Build from a ZM API
ControlJSON dict.
- has_home_preset: bool = False
- has_presets: bool = False
- num_presets: int = 0
- raw() dict
Return the full, unmodified API response dict.
- class pyzm.models.zm.Zone(name: str, points: list[tuple[float, float]], pattern: str | None = None, ignore_pattern: str | None = None, _raw: dict = <factory>)
A detection zone polygon belonging to a monitor.
- as_dict() dict
- ignore_pattern: str | None = None
- name: str
- pattern: str | None = None
- points: list[tuple[float, float]]
- raw() dict
Return the full, unmodified API response dict.
Remote ML Detection Server
A FastAPI-based server that loads models once and serves detection requests over HTTP. See the serve guide for usage.
FastAPI application factory for the pyzm ML detection server.
- pyzm.serve.app.create_app(config: ServerConfig | None = None) fastapi.FastAPI
Build and return a configured FastAPI application.
The
Detectoris created during the lifespan startup phase so models are loaded once and persist across requests.
JWT authentication for the pyzm serve API.
- pyzm.serve.auth.create_login_route(config: ServerConfig)
Return a login endpoint handler bound to config.
When
config.auth_enabledisFalsethe route accepts any credentials so that clients withml_user/ml_passwordconfigured don’t fail with a 404.
- pyzm.serve.auth.create_token_dependency(config: ServerConfig)
Return a FastAPI dependency that verifies Bearer tokens.