pyzm package

API

ZMApi

Python API wrapper for ZM. Exposes login, monitors, events, etc. API

Important

Make sure you have the following settings in ZM:

  • AUTH_RELAY is set to hashed
  • A valid AUTH_HASH_SECRET is provided (not empty)
  • AUTH_HASH_IPS is disabled
  • OPT_USE_APIS is enabled
  • If you are using any version lower than ZM 1.34, OPT_USE_GOOG_RECAPTCHA is disabled
  • If you are NOT using authentication at all in ZM, that is OPT_USE_AUTH is disabled, then make sure you also disable authentication in zmNinja, otherwise it will keep waiting for auth keys.
  • I don’t quite know why, but on some devices, connection issues are caused because ZoneMinder’s CSRF code causes issues. See this thread, for example. In this case, try turning off CSRF checks by going to ZM->Options->System and disable “Enable CSRF magic”.
class pyzm.api.ZMApi(options={})

Bases: pyzm.helpers.Base.Base

__init__(options={})

Options is a dict with the following keys:

  • apiurl - the full API URL (example https://server/zm/api)
  • portalurl - the full portal URL (example https://server/zm). Only needed if you are downloading events/images
  • user - username (don’t specify if no auth)
  • password - password (don’t specify if no auth)
  • disable_ssl_cert_check - if True will let you use self signed certs

Note: you can connect your own customer logging class to the API in which case all modules will use your custom class. Your class will need to implement some methods for this to work. See pyzm.helpers.Base.SimpleLog for method details.

authenticated()

True if login API worked

Returns:boolean – True if Login API worked
configs(options={})

Returns config values of ZM

Args:

options (dict, optional): Defaults to {}. options:

{
    'force_reload': boolean # if True, reloads
}
Returns:ZM configs
Return type:pyzm.helpers.Configs
events(options={})

Returns list of events based on filter criteria. Note that each time you called events, a new HTTP call is made.

Parameters:options (dict, optional) –

Various filters that will be applied to events. Defaults to {}. Available fields:

{
    'event_id': string # specific event ID to fetch
    'tz': string # long form timezone (example America/New_York),
    'from': string # string # minimum start time (including human readable
                   # strings like '1 hour ago' or '10 minutes ago to 5 minutes ago' to create a range)
    'to': string # string # maximum end time
    'mid': int # monitor id
    'min_alarmed_frames': int # minimum alarmed frames
    'max_alarmed_frames': int # maximum alarmed frames
    'object_only': boolean # if True will only pick events
                           # that have objects

}
Returns:list of events that match criteria
Return type:list of pyzm.helpers.Event
get_apibase()
get_auth()
get_portalbase()
get_session()
monitors(options={})

Returns list of monitors. Given monitors are fairly static, maintains a cache and returns from cache on subsequent calls.

Args:

options (dict, optional): Available fields:

{
    'force_reload': boolean # if True refreshes monitors

}
Returns:list of monitors
Return type:list of pyzm.helpers.Monitor
restart()

Restarts ZoneMinder

Returns:json value of restart command
Return type:json
set_state(state)

Sets Zoneminder state to specific state

Parameters:state (string) – Name of state
Returns:value of state change command
Return type:json
start()

Starts ZoneMinder

Returns:json value of start command
Return type:json
states(options={})

Returns configured states

Parameters:options (dict, optional) – Not used. Defaults to {}.
Returns:list of states
Return type:list of pyzm.helpers.State
stop()

Stops ZoneMinder

Returns:json value of stop command
Return type:json
tz()

Returns timezone of ZoneMinder server

Returns:timezone of ZoneMinder server (or None if API not supported)
Return type:string
version()

Returns version of API and ZM

Returns:Version of API and ZM:
{
    status: string # if 'error' then will also have 'reason'
    api_version: string # if status is 'ok'
    zm_version: string # if status is 'ok'
}
Return type:dict

Classes returned by the API

Monitors

Holds a list of Monitors for a ZM configuration Given monitors are fairly static, maintains a cache of monitors which can be overriden

class pyzm.helpers.Monitors.Monitors(api=None)

Bases: pyzm.helpers.Base.Base

add(options={})

Adds a new monitor

Parameters:options (dict) –

Set of attributes that define the monitor:

{
    'function': string # function of monitor
    'name': string # name of monitor
    'enabled': boolean
    'protocol': string
    'host': string
    'port': int
    'path': string
    'width': int
    'height': int
    'raw': {
        # Any other monitor value that is not exposed above. Example:
        'Monitor[Colours]': '4',
        'Monitor[Method]': 'simple'
    }

}
Returns:json response of API request
Return type:json
find(id=None, name=None)

Given an id or name, returns matching monitor object

Parameters:
  • id (int, optional) – MonitorId of monitor. Defaults to None.
  • name (string, optional) – Monitor name of monitor. Defaults to None.
Returns:

Matching monitor object

Return type:

pyzm.helpers.Monitor

list()

Monitor

Each Monitor will hold a single ZoneMinder Monitor. It is basically a bunch of getters for each access to event data. If you don’t see a specific getter, just use the generic get function to get the full object

class pyzm.helpers.Monitor.Monitor(api=None, monitor=None)

Bases: pyzm.helpers.Base.Base

arm()

Arms monitor (forces alarm)

Returns:API response
Return type:json
delete()

Deletes monitor

Returns:API response
Return type:json
dimensions()

Returns width and height of monitor

Returns:as below:
{
    'width': int,
    'height': int
}
Return type:dict
disarm()

Disarm monito (removes alarm)

Returns:API response
Return type:json
enabled()

True if monitor is enabled

Returns:Enabled or not
Return type:bool
eventcount(options={})

Returns count of events for monitor

Parameters:options (dict, optional) – Same as options for pyzm.helpers.Event. Defaults to {}.
Returns:count
Return type:int
events(options={})

Returns events associated to the monitor, subject to filters in options

Parameters:options (dict, optional) – Same as options for pyzm.helpers.Event. Defaults to {}.
Returns:pyzm.helpers.Events
function()

returns monitor function

Returns:monitor function
Return type:string
get()

Returns monitor object

Returns:Monitor object
Return type:pyzm.helpers.Monitor
id()

Returns monitor Id

Returns:Monitor Id
Return type:int
name()

Returns monitor name

Returns:monitor name
Return type:string
set_parameter(options={})

Changes monitor parameters

Parameters:options (dict, optional) –

As below. Defaults to {}:

{
    'function': string # function of monitor
    'name': string # name of monitor
    'enabled': boolean
    'raw': {
        # Any other monitor value that is not exposed above. Example:
        'Monitor[Colours]': '4',
        'Monitor[Method]': 'simple'
    }

}
Returns:API Response
Return type:json
status()
Returns status of monitor, as reported by zmdc
TBD: crappy return, need to normalize
Returns:API response
Return type:json
type()

Returns monitor type

Returns:Monitor type
Return type:string

Events

Holds a list of Events for a ZM configuration You can pass different conditions to filter the events Each invocation results in a new API call as events are very dynamic

You are not expected to use this module directly. It is instantiated when you use the pyzm.api.ZMApi.events() method of pyzm.api

class pyzm.helpers.Events.Events(api=None, options=None)

Bases: pyzm.helpers.Base.Base

count()

Returns number of events retrieved in previous invocation

Returns:int – number of events
get(options={})

Returns the full list of events. Typically useful if you need access to data for which you don’t have an easy getter

Keyword Arguments:

  • options: dict with same parameters as the one you pass in pyzm.api.ZMApi.events(). This is really a convenience instead of re-creating the instance.
Returns:list – of pyzm.helpers.Events
list()

Returns list of event

Returns:list – events of Event

Event

Each Event will hold a single ZoneMinder Event. It is basically a bunch of getters for each access to event data. If you don’t see a specific getter, just use the generic get function to get the full object

class pyzm.helpers.Event.Event(event=None, api=None)

Bases: pyzm.helpers.Base.Base

alarmed_frames()

Returns total alarmed frames in event.

Returns:total alarmed frames
Return type:int
cause()

returns event cause.

Returns:event cause
Return type:string
delete()

Deletes this event

Returns:API response
Return type:json
download_image(fid='snapshot', dir='.', show_progress=False)

Downloads an image frame of the current event object

Parameters:
  • fid (str, optional) – Frame ID. Defaults to ‘snapshot’.
  • dir (str, optional) – Path to save the image to. Defaults to ‘.’.
  • show_progress (bool, optional) – If enabled shows a progress bar (if possible). Defaults to False.
Returns:

path+filename of downloaded image

Return type:

string

download_video(dir='.', show_progress=False)

Downloads a video mp4 of the current event object Only works if there an actual video

Parameters:
  • dir (str, optional) – Path to save the image to. Defaults to ‘.’.
  • show_progress (bool, optional) – If enabled shows a progress bar (if possible). Defaults to False.
Returns:

path+filename of downloaded video

Return type:

string

duration()

Returns duration of event in seconds.

Returns:duration
Return type:float
fspath()

returns the filesystem path where the event is stored. Only available in ZM 1.33+

Returns:path
Return type:string
get()

Returns event object.

Returns:Event object
Return type:pyzm.helpers.Event
get_image_url(fid='snapshot')

Returns the image URL for the specified frame

Parameters:fid (str, optional) – Default frame identification. Defaults to ‘snapshot’.
Returns:URL for the image
Return type:string
get_video_url()

Returms the video URL for the specified event

Returns:URL for the video file
Return type:string
id()

returns event id of event.

Returns:event id
Return type:int
monitor_id()

returns monitor ID of event object.

Returns:monitor id
Return type:int
name()

returns name of event.

Returns:name of event
Return type:string
notes()

returns event notes.

Returns:event notes
Return type:string
score()

Returns total, average and max scores of event.

Returns:As below:
{
    'total': float,
    'average': float,
    'max': float
}
Return type:dict
total_frames()

Returns total frames in event.

Returns:total frames
Return type:int
video_file()

returns name of video file in which the event was stored.

Returns:name of video file
Return type:string

States

Holds a list of States for a ZM configuration Given states are fairly static, maintains a cache of states which can be overriden

class pyzm.helpers.States.States(api=None)

Bases: pyzm.helpers.Base.Base

find(id=None, name=None)

Return a state object that matches either and id or a

Parameters:
  • id (int, optional) – Id of state. Defaults to None.
  • name (string, optional) – name of state. Defaults to None.
Returns:

State object that matches

Return type:

pyzm.helpers.State

list()

Returns list of state objects

Returns:list of state objects
Return type:list of pyzm.helpers.State

State

Each State will hold a single ZoneMinder State. It is basically a bunch of getters for each access to event data. If you don’t see a specific getter, just use the generic get function to get the full object

class pyzm.helpers.State.State(api=None, state=None)

Bases: pyzm.helpers.Base.Base

active()

whether this state is active or not

Returns:True if active
Return type:bool
definition()

Returns the description text of this state

Returns:description
Return type:string
get()

Returns raw state object

Returns:raw state object
Return type:pyzm.helpers.State
id()

Id of this state

Returns:id of this state
Return type:int
name()

Name of this state

Returns:name of this state
Return type:string

Base

All classes derive from this Base class. It implements some common functions that apply across all. For now, this basically holds a pointer to the logging function to invoke to log messages including a simple console based print function if none is provided

class pyzm.helpers.Base.Base

Bases: object

class pyzm.helpers.Base.ConsoleLog

Bases: object

console based logging function that is used if no logging handler is passed

Debug(level, message, caller=None)
Error(message, caller=None)
Fatal(message, caller=None)
Info(message, caller=None)
Panic(message, caller=None)
Warning(message, caller=None)
get_level()
set_level(level)

Logging

ZMLog

Implements a python implementation of ZoneMinder’s logging system You could use this to connect it to the APIs if you want

pyzm.ZMLog.Debug(level=1, message=None, caller=None)

Debug level ZM message

Parameters:
  • level (int) – ZM Debug level
  • message (string) – Message to log
  • caller (stack frame info, optional) – Used to log caller id/line #. Picked up automatically if none. Defaults to None.
pyzm.ZMLog.Error(message=None, caller=None)

Error level ZM message

Parameters:
  • message (string) – Message to log
  • caller (stack frame info, optional) – Used to log caller id/line #. Picked up automatically if none. Defaults to None.
pyzm.ZMLog.Fatal(message=None, caller=None)

Fatal level ZM message. Quits after logging

Parameters:
  • message (string) – Message to log
  • caller (stack frame info, optional) – Used to log caller id/line #. Picked up automatically if none. Defaults to None.
pyzm.ZMLog.Info(message=None, caller=None)

Info level ZM message

Parameters:
  • message (string) – Message to log
  • caller (stack frame info, optional) – Used to log caller id/line #. Picked up automatically if none. Defaults to None.
pyzm.ZMLog.Panic(message=None, caller=None)

Panic level ZM message. Quits after logging

Parameters:
  • message (string) – Message to log
  • caller (stack frame info, optional) – Used to log caller id/line #. Picked up automatically if none. Defaults to None.
pyzm.ZMLog.Warning(message=None, caller=None)

Warning level ZM message

Parameters:
  • message (string) – Message to log
  • caller (stack frame info, optional) – Used to log caller id/line #. Picked up automatically if none. Defaults to None.
pyzm.ZMLog.close()

Closes all handles. Invoke this before you exit your app

pyzm.ZMLog.get_config()

Returns configuration of ZM logger

Returns:config params
Return type:dict
pyzm.ZMLog.init(name=None, override={})

Initializes the ZM logging system. It follows ZM logging principles and ties into appropriate ZM logging levels. Like the rest of ZM, it can write to files, syslog and the ZM DB.

To make it simpler to override, you can pass various options in the override dict. When passed, they will override any ZM setting

Parameters:
  • name (string, optional) – Name to be used while writing logs. If not specified, it will use the process name. Defaults to None.
  • override (dict, optional) –

    Various parameters that can supercede standard ZM logs. Defaults to {}. The parameters that can be overriden are:

    {
        'dump_console': False,
        'conf_path': '/etc/zm',
        'user' : None,
        'password' : None,
        'host' : None,
        'webuser': 'www-data',
        'webgroup': 'www-data',
        'dbname' : None,
        'logpath' : '/var/log',
        'log_level_syslog' : 0,
        'log_level_file' : 0,
        'log_level_db' : 0,
        'log_debug' : 0,
        'log_level_debug' : 1,
        'log_debug_target' : '',
        'log_debug_file' :'',
        'server_id': 0,
        'driver': 'mysql+mysqlconnector'
    }
    
pyzm.ZMLog.set_level(level)
pyzm.ZMLog.sig_intr(sig, frame)
pyzm.ZMLog.sig_log_rot(sig, frame)

Event Notification

ZMEventNotification

Implements a python implementation of the ZM ES server.

class pyzm.ZMEventNotification.ZMEventNotification(options)
__init__(options)

Instantiates a thread that connects to the ZM Notification Server

Parameters:options (dict) –

As below:

{
    'url': string # websocket url
    'user': string # zm user name
    'password': string # zm password
    'allow_untrusted': boolean # set to true for self-signed certs
    'on_es_message': callback function when a message is received
    'on_es_close': callback function when the connection is closed
    'on_es_error': callback function when an error occurs
}
Raises:ValueError – if no server is provided
connect()

Connect to the ES

disconnect()

Disconnect from the ES

send(msg)

Send message to ES

Parameters:msg (dict) – message to send. The message should follow a control structure as specified in The ES developer guide

Memory

ZMMemory

Wrapper to access SHM for Monitor status

class pyzm.ZMMemory.ZMMemory(api=None, path='/dev/shm', mid=None)
__init__(api=None, path='/dev/shm', mid=None)

Initialize self. See help(type(self)) for accurate signature.

alarm_state()

Returns alarm state

Returns:as below:
{
    'id': int # state id
    'state': string # name of state
}
Return type:dict
cause()

Returns alarm and trigger cause as applicable

Returns:as below:
{
    'alarm_cause': string
    'trigger_cause': string
}
Return type:dict
close()

Closes the handle

get()

returns raw shared and trigger data as a dict

Returns:raw shared and trigger data
{
    'shared_data': dict, # of shared data,
    'trigger_data': dict # trigger data
}
Return type:dict
get_shared_data()

Returns just the shared data

Returns:shared data
Return type:dict
get_trigger_data()

Returns just the trigger data

Returns:trigger data
Return type:dict
is_alarmed()

True if monitor is currently alarmed

Returns:True if monitor is currently alarmed
Return type:bool
is_valid()

True if the memory handle is valid

Returns:True if memory handle is valid
Return type:bool
last_event()

Returns last event ID

Returns:last event id
Return type:int
reload()

Reloads monitor information. Call after you get an invalid memory report

Raises:ValueError – if no monitor is provided
trigger()

Returns trigger information

Returns:as below:
{
    'trigger_text': string,
    'trigger_showtext': string,
    'trigger_cause': string,
    'trigger:state' {
        'id': int,
        'state': string
    }
}
Return type:dict

Machine Learning

DetectSequence

Primary entry point to invoke machine learning classes in pyzm It is recommended you only use DetectSequence methods and not lower level interfaces as they may change drastically.

class pyzm.ml.detect_sequence.DetectSequence(options={}, global_config={})
__init__(options={}, global_config={})

Initializes ML entry point with various parameters

Parameters:options (-) –

Variety of ML options. Best described by an example as below

options = {
    'general': {
        # sequence of models you want to run for every specified frame
        'model_sequence': 'object,face,alpr' ,
        # If 'yes', will not use portalocks
        'disable_locks':'no',

    },

    # We now specify all the config parameters per model_sequence entry
    'object': {
        'general':{
            # 'first' - When detecting objects, if there are multiple fallbacks, break out
            # the moment we get a match using any object detection library.
            # 'most' - run through all libraries, select one that has most object matches
            # 'most_unique' - run through all libraries, select one that has most unique object matches

            'same_model_sequence_strategy': 'first' # 'first' 'most', 'most_unique', 'union'
            'pattern': '.*' # any pattern
        },

        # within object, this is a sequence of object detection libraries. In this case,
        # I want to first try on my TPU and if it fails, try GPU
        'sequence': [{
            #First run on TPU
            'object_weights':'/var/lib/zmeventnotification/models/coral_edgetpu/ssd_mobilenet_v2_coco_quant_postprocess_edgetpu.tflite',
            'object_labels': '/var/lib/zmeventnotification/models/coral_edgetpu/coco_indexed.names',
            'object_min_confidence': 0.3,
            'object_framework':'coral_edgetpu'
        },
        {
            # YoloV4 on GPU if TPU fails (because sequence strategy is 'first')
            'object_config':'/var/lib/zmeventnotification/models/yolov4/yolov4.cfg',
            'object_weights':'/var/lib/zmeventnotification/models/yolov4/yolov4.weights',
            'object_labels': '/var/lib/zmeventnotification/models/yolov4/coco.names',
            'object_min_confidence': 0.3,
            'object_framework':'opencv',
            'object_processor': 'gpu',
            # These are optional below. Default is 416. Change if your model is trained for larger sizes
            'model_width': 416,
            'model_height': 416
        }]
    },

    # We repeat the same exercise with 'face' as it is next in model_sequence
    'face': {
        'general':{
            'same_model_sequence_strategy': 'first'
        },
        'sequence': [{
            # if max_size is specified, not matter what
            # the resize value in stream_options, it will be rescaled down to this
            # value if needed
            'max_size':800,
            'face_detection_framework': 'dlib',
            'known_images_path': '/var/lib/zmeventnotification/known_faces',
            'face_model': 'cnn',
            'face_train_model': 'cnn',
            'face_recog_dist_threshold': 0.6,
            'face_num_jitters': 1,
            'face_upsample_times':1
        }]
    },

    # We repeat the same exercise with 'alpr' as it is next in model_sequence
    'alpr': {
        'general':{
            'same_model_sequence_strategy': 'first',

            # This can be applied to any model. This means, don't run this model
            # unless a previous model detected one of these labels.
            # In this case, I'm not calling ALPR unless we've detected a vehile
            # bacause platerec has an API threshold

            'pre_existing_labels':['car', 'motorbike', 'bus', 'truck', 'boat'],

        },
        'sequence': [{
            'alpr_api_type': 'cloud',
            'alpr_service': 'plate_recognizer',
            'alpr_key': g.config['alpr_key'],
            'platrec_stats': 'no',
            'platerec_min_dscore': 0.1,
            'platerec_min_score': 0.2,
        }]
    }
} # ml_options

- global_config (dict): Used by zm_detect and mlapi to pass
  additional config parameters that may not be present in ml_config
detect_stream(stream, options={}, ml_overrides={})

Implements detection on a video stream

Parameters:
  • stream (string) – location of media (file, url or event ID)
  • ml_overrides (string) – Ignore it. You will almost never need it. zm_detect uses it for ugly foo
  • options (dict, optional) –

    Various options that control the detection process. Defaults to {}:

    • delay (int): Delay in seconds before starting media stream
    • delay_between_frames (int): Delay in seconds between each frame read
    • delay_between_snapshots (int): Delay in seconds between each snapshot frame read (useful if you want to read snapshot multiple times, for example. frameset: [‘snapshot’,’snapshot’,’snapshot’])
    • download (boolean): if True, will download video before analysis. Defaults to False
    • download_dir (string): directory where downloads will be kept (only applies to videos). Default is /tmp
    • start_frame (int): Which frame to start analysis. Default 1.
    • frame_skip: (int): Number of frames to skip in video (example, 3 means process every 3rd frame)
    • max_frames (int): Total number of frames to process before stopping
    • pattern (string): regexp for objects that will be matched. ‘frame_strategy’ key below will be applied to only objects that match this pattern
    • frame_set (string or list): comma separated frames to read. Example ‘alarm,21,31,41,snapshot’ or [‘snapshot’,’alarm’,’1’,’2’] Note that if you are specifying frame IDs and using ZM, remember that ZM has a frame buffer Default is 20, I think. So you may want to start at frame 21.
    • contig_frames_before_error (int): How many contiguous frames should fail before we give up on reading this stream. Default 5
    • max_attempts (int): Only for ZM indirection. How many times to retry a failed frame get. Default 1
    • sleep_between_attempts (int): Only for ZM indirection. Time to wait before re-trying a failed frame
    • disable_ssl_cert_check (bool): If True (default) will allow self-signed certs to work
    • save_frames (boolean): If True, will save frames used in analysis. Default False
    • save_analyzed_frames (boolean): If True, will save analyzed frames (with boxes). Default False
    • save_frames_dir (string): Directory to save analyzed frames. Default /tmp
    • frame_strategy: (string): various conditions to stop matching as below
      • ’most_models’: Match the frame that has matched most models (does not include same model alternatives) (Default)
      • ’first’: Stop at first match
      • ’most’: Match the frame that has the highest number of detected objects
      • ’most_unique’ Match the frame that has the highest number of unique detected objects
    • resize (int): Width to resize image, default 800
    • polygons(object): object # set of polygons that the detected image needs to intersect
    • convert_snapshot_to_fid (bool): if True, will convert ‘snapshot’ to an actual fid. If you are seeing boxes at wrong places for snapshot frames, this may fix it. However, it can also result in frame 404 errors if that frame ID is not yet written to disk. So you may want to add a delay if you enable this. Default is False.
Returns:

representing matched frame, consists of:

  • box (array): list of bounding boxes for matched frame
  • label (array): list of labels for matched frame
  • confidence (array): list of confidences for matched frame
  • id (int): frame id of matched frame
  • img (cv2 image): image grab of matched frame
  • array of objects:
  • list of boxes,labels,confidences of all frames matched

Return type:

  • object

Note:

The same frames are not retrieved depending on whether you set download to True or False. When set to True, we use OpenCV’s frame reading logic and when False we use ZoneMinder’s image.php function which uses time based approximation. Therefore, the retrieve different frame offsets, but I assume they should be reasonably close.

get_ml_options()
set_ml_options(options, force_reload=False)

Use this to change ml options later. Note that models will not be reloaded unless you add force_reload=True