Client & Authentication
Connecting to ZoneMinder
from pyzm import ZMClient
zm = ZMClient(
api_url="https://zm.example.com/zm/api",
user="admin",
password="secret",
# verify_ssl=False, # for self-signed certs
)
print(f"ZM {zm.zm_version}, API {zm.api_version}")
api_url must be the full ZM API URL (ending in /api).
Constructor parameters
Parameter |
Default |
Description |
|---|---|---|
|
(required) |
Full ZM API URL (e.g. |
|
|
ZM username. |
|
|
ZM password |
|
auto |
Full portal URL (e.g. |
|
|
Set to |
|
|
HTTP request timeout in seconds |
|
|
Override the ZM database username (normally read from |
|
|
Override the ZM database password |
|
|
Override the ZM database host (e.g. |
|
|
Override the ZM database name |
|
|
Path to the ZM config directory (default |
|
|
A pre-built |
Authentication
pyzm handles authentication internally. When user and password
are provided, it obtains an API token and refreshes it automatically.
Legacy (non-token) authentication is also supported for older ZM
installations.
Set verify_ssl=False if your ZM server uses a self-signed TLS
certificate.
Database access
Some operations — ev.tag(), ev.path(), ev.save_objdetect()
(when no path_override is given), and audio extraction for BirdNET —
require a direct MySQL connection to the ZM database. By
default, pyzm reads credentials from /etc/zm/zm.conf (the same file
ZoneMinder uses).
If the user running pyzm cannot read zm.conf (e.g. permission
denied), you can pass database credentials explicitly:
zm = ZMClient(
api_url="https://zm.example.com/zm/api",
user="admin",
password="secret",
db_user="zmuser",
db_password="zmpass",
db_host="localhost",
)
ev = zm.event(12345)
ev.tag(["person"]) # uses the explicit DB credentials
path = ev.path() # same
The merge strategy is:
Try to read
zm.conf(orconf_pathif set).Overlay any explicit
db_*parameters — explicit values always win.Fall back to
"localhost"for host and"zm"for database name.
Accessing the full API response
pyzm models only extract a subset of the fields returned by the ZM API.
Every API-sourced object carries a raw() method that returns the full,
unmodified API response dict — useful for accessing fields like Path,
Protocol, StorageId, etc.:
m = zm.monitor(1)
m.raw()["Monitor"]["Path"] # e.g. "rtsp://cam/stream"
m.raw()["Monitor"]["Protocol"] # e.g. "rtsp"
m.status.raw() # full Monitor_Status sub-dict
ev = zm.event(12345)
ev.raw()["Event"]["DiskSpace"] # field not on the Event dataclass
frames = ev.get_frames()
frames[0].raw() # full Frame API dict
zones = m.get_zones()
zones[0].raw() # full Zone API dict including AlarmRGB, etc.
raw() is available on Monitor, MonitorStatus, Event,
Frame, Zone, PTZCapabilities, and Notification.
Notifications (Push Tokens)
Requires ZoneMinder 1.39.2+.
ZoneMinder’s Notifications API stores FCM push tokens registered by client
apps (e.g. zmNinjaNg). pyzm provides methods to query and manage these tokens —
primarily used by zm_detect to send push notifications after detection.
# List all registered tokens for the authenticated user
tokens = zm.notifications()
for t in tokens:
print(f"Id={t.id}, platform={t.platform}, token={t.token[:20]}...")
# Get a specific notification by ID
notif = zm.notification(1)
# Check if a token should receive a notification for a given monitor
if notif.should_notify(monitor_id=3):
print("Token is registered for monitor 3")
# Check throttle (returns True if Interval has not elapsed since LastNotifiedAt)
if notif.is_throttled():
print("Skipping — too soon since last notification")
# Update LastNotifiedAt and increment BadgeCount after sending
notif.update_last_sent(badge=notif.badge_count + 1)
# Delete a token (e.g. when FCM reports it as invalid)
notif.delete()
Notification fields
Field |
Type |
Description |
|---|---|---|
|
|
Primary key |
|
|
Owning ZM user ( |
|
|
FCM registration token |
|
|
|
|
|
Comma-separated monitor IDs, or |
|
|
Minimum seconds between notifications (0 = no throttle) |
|
|
|
|
|
Client app version |
|
|
Current badge count |
|
|
When the last push was sent to this token |
Helper methods
monitors()— returns the monitor list as alist[int], or empty list if all monitorsshould_notify(monitor_id)—Trueif this token should receive notifications for the given monitor (checkspush_state,monitor_list)is_throttled()—Trueifintervalseconds have not elapsed sincelast_notified_atupdate_last_sent(badge)— updatesLastNotifiedAtto now and setsBadgeCountdelete()— deletes this notification record from ZM