Events & Frames

Querying events

# Events from the last hour
events = zm.events(since="1 hour ago", limit=5)
for ev in events:
    print(f"Event {ev.id}: {ev.cause} ({ev.length:.1f}s, {ev.alarm_frames} alarm frames)")

# Events for a specific monitor
events = zm.events(monitor_id=1, since="1 hour ago")

# Single event
ev = zm.event(12345)

Filters: event_id, monitor_id, since, until, min_alarm_frames, object_only, limit.

object_only=True filters to events that have been tagged by ML detection (events whose Notes field matches "detected").

Per-frame metadata

ev = zm.event(12345)
frames = ev.get_frames()
for f in frames:
    print(f"Frame {f.frame_id}: type={f.type} score={f.score} delta={f.delta:.2f}s")

# Find the highest-scoring frame
best = max(frames, key=lambda f: f.score)
print(f"Best frame: {best.frame_id} (score={best.score})")

get_frames() returns list[Frame] with per-frame Score, Type (Normal/Alarm/Bulk), and Delta (seconds since event start).

Event methods

ev = zm.event(12345)

# Update the Notes field
ev.update_notes("person detected at front door")

# Tag with detected object labels (requires ZM >= 1.37.44, DB access)
ev.tag(["person", "car"])

# Get the filesystem path (requires DB access)
path = ev.path()
# e.g. "/var/cache/zoneminder/events/1/2024-01-15/12345"

# Save detection artifacts to the event directory
annotated = result.annotate()
ev.save_objdetect(annotated, result.to_dict())
# Writes objdetect.jpg and objects.json to ev.path()
# Use path_override="..." to write to a different directory

tag(), path(), and save_objdetect() (when no path_override is given) require a direct database connection. By default, credentials are read from /etc/zm/zm.conf. If that file is not readable, pass db_user, db_password, etc. when creating the client — see Client & Authentication for details.

# Extract frames as numpy arrays (requires pyzm[ml]) frames, dims = ev.extract_frames(stream_config=StreamConfig(resize=800)) # frames: list of (frame_id, numpy_array) tuples # dims: {“original”: (h, w), “resized”: (rh, rw) or None}

# Delete ev.delete()

Bulk operations

# Bulk delete events matching filters
count = zm.delete_events(monitor_id=1, before="7 days ago", limit=500)
print(f"Deleted {count} events")

# OOP: query events scoped to a monitor
m = zm.monitor(1)
recent = m.events(since="6 hours ago", limit=10)

# OOP: bulk-delete old events for a monitor
count = m.delete_events(before="1 week ago", limit=500)
print(f"Deleted {count} events from {m.name}")

delete_events() filters: monitor_id, before, min_alarm_frames, limit.