Zoneminder Monitors
This role automates the addition of camera monitors to ZoneMinder via the REST API.
Zoneminder Monitors Role
Overview
This role automates the addition of camera monitors to ZoneMinder via the REST API. It authenticates with the ZoneMinder API, retrieves the list of existing monitors to prevent duplicates, and creates new camera monitors with customizable settings including name, RTSP path, resolution, recording function, video encoding parameters, and audio recording options. The role is idempotent and will only add monitors that don’t already exist.
Purpose
- Camera Automation: Add multiple cameras via code instead of UI
- Idempotent: Safe to run multiple times, prevents duplicates
- API-Based: Uses ZoneMinder REST API for reliable automation
- Flexible Configuration: Define camera settings in YAML
- Batch Addition: Add multiple cameras in one run
- Code as Configuration: Version control camera definitions
Requirements
- Ansible 2.9 or higher
- ZoneMinder installed and running (see
zoneminder_installrole) - ZoneMinder API enabled
- API user credentials (typically admin account)
- Network connectivity to ZoneMinder
- IP cameras with RTSP streams
- Camera credentials (username/password for RTSP URLs)
What This Role Does
Camera addition workflow:
- Authenticate: Login to ZoneMinder API and get access token
- Check existing: Retrieve list of currently configured monitors
- Compare: Identify which cameras need to be added
- Add new: Create monitors that don’t already exist
- Configure: Set all camera parameters (resolution, function, encoding)
Idempotency: If monitor with same name exists, skip creation.
Role Variables
Required Variables
| Variable | Required | Description |
|---|---|---|
zoneminder_monitors_api_user | Yes | ZoneMinder API username |
zoneminder_monitors_api_password | Yes | API password (from vault) |
zoneminder_monitors_list | Yes | List of cameras to add |
Optional Variables
| Variable | Default | Description |
|---|---|---|
zoneminder_monitors_api_url | https://<host>/zm/api | ZoneMinder API URL |
zoneminder_monitors_validate_certs | true | Validate SSL certificates |
zoneminder_monitors_defaults | See defaults | Default camera settings |
Variable Details
zoneminder_monitors_api_url
Base URL for ZoneMinder API.
Default: "https://{{ hostvars['zoneminder']['ansible_host'] }}/zm/api"
Override for custom host:
zoneminder_monitors_api_url: "https://192.168.x.x/zm/api"
zoneminder_monitors_api_user
ZoneMinder username for API authentication.
Default: admin
Custom user:
zoneminder_monitors_api_user: api_user
Note: User must have permission to add monitors.
zoneminder_monitors_api_password
Password for API authentication.
IMPORTANT: Store in Ansible Vault
# In vault.yml
vault_zoneminder_api_password: "admin_password_here"
# In playbook
zoneminder_monitors_api_password: "{{ vault_zoneminder_api_password }}"
zoneminder_monitors_validate_certs
Whether to validate SSL certificates.
Default: true
Disable for self-signed certs:
zoneminder_monitors_validate_certs: false
zoneminder_monitors_defaults
Default settings for all cameras (overridable per camera).
Default values:
zoneminder_monitors_defaults:
type: "Ffmpeg"
method: "rtpRtsp"
colours: 4
save_jpegs: 0
video_writer: 2
record_audio: 1
encoder_parameters: "# Lines beginning with # are a comment \r\n# For changing quality, use the crf option\r\n# 1 is best, 51 is worst quality\r\ncrf=23\r\nmovflags=faststart"
Field descriptions:
| Field | Description |
|---|---|
type | Monitor type (usually “Ffmpeg” for IP cameras) |
method | RTSP method (“rtpRtsp” for most cameras) |
colours | Color depth (4 = 32-bit color) |
save_jpegs | Save JPEG snapshots (0=no, 1=yes) |
video_writer | Video encoding (0=disabled, 1=encode, 2=passthrough) |
record_audio | Record audio stream (0=no, 1=yes) |
encoder_parameters | FFmpeg encoding options |
zoneminder_monitors_list
List of cameras to add.
Required fields per camera:
name: Display namepath: Full RTSP URL with credentialswidth: Video width in pixelsheight: Video height in pixelsfunction: Recording mode
Optional fields (use defaults if not specified):
enabled: Enable/disable monitortype: Monitor typemethod: RTSP methodcolours: Color depthsave_jpegs: Save JPEG snapshotsvideo_writer: Video encoder moderecord_audio: Record audioencoder_parameters: FFmpeg options
Example:
zoneminder_monitors_list:
- name: "Front Door"
path: "rtsp://username:password@192.168.x.x:554/live/ch0"
width: 1920
height: 1080
function: "Record"
- name: "Backyard"
path: "rtsp://username:password@192.168.x.x:554/live/ch0"
width: 2304
height: 1296
function: "Modect"
save_jpegs: 1 # Override default to save JPEGs
- name: "Garage"
path: "rtsp://admin:pass123@192.168.x.x:554/stream1"
width: 1280
height: 720
function: "Mocord"
record_audio: 0 # No audio for this camera
Dependencies
Requires:
- zoneminder_install: ZoneMinder must be installed first
- ZoneMinder API enabled (enabled by default)
- Camera RTSP URLs and credentials
Example Playbook
Basic Usage
---
- name: Add Cameras to ZoneMinder
hosts: localhost
become: false
vars:
zoneminder_monitors_list:
- name: "Front Door"
path: "rtsp://admin:password@192.168.x.x:554/live/ch0"
width: 1920
height: 1080
function: "Modect"
- name: "Backyard"
path: "rtsp://admin:password@192.168.x.x:554/live/ch0"
width: 1920
height: 1080
function: "Record"
roles:
- zoneminder_monitors
Multiple Cameras with Custom Settings
---
- name: Add Security Cameras
hosts: localhost
become: false
vars:
zoneminder_monitors_list:
# High-quality front camera with motion detection
- name: "Front Entrance"
path: "rtsp://admin:{{ vault_camera_password }}@192.168.x.x:554/live/ch0"
width: 2304
height: 1296
function: "Modect"
save_jpegs: 1 # Save snapshots
video_writer: 1 # Re-encode for quality
# Continuous recording for critical area
- name: "Server Room"
path: "rtsp://admin:{{ vault_camera_password }}@192.168.x.x:554/stream1"
width: 1920
height: 1080
function: "Record"
video_writer: 2 # Passthrough (no re-encoding)
# Low-res camera for parking lot
- name: "Parking Lot"
path: "rtsp://admin:{{ vault_camera_password }}@192.168.x.x:554/stream2"
width: 1280
height: 720
function: "Mocord"
record_audio: 0 # No audio needed
roles:
- zoneminder_monitors
Self-Signed SSL Certificate
---
- name: Add Cameras (Self-Signed Cert)
hosts: localhost
become: false
vars:
zoneminder_monitors_validate_certs: false
zoneminder_monitors_list:
- name: "Camera 1"
path: "rtsp://admin:password@192.168.x.x:554/live/ch0"
width: 1920
height: 1080
function: "Modect"
roles:
- zoneminder_monitors
ZoneMinder Monitor Functions
Recording modes:
| Function | Description | Use Case |
|---|---|---|
Monitor | No recording, live view only | Testing camera connection |
Modect | Motion detection only | Save disk space, event-driven |
Record | Continuous recording | Critical areas, 24/7 coverage |
Mocord | Continuous + enhanced motion events | Best of both (uses more disk) |
Nodect | Continuous, no motion analysis | Reduce CPU, simple recording |
Recommendations:
- Front doors:
Modect(motion-triggered) - Server rooms:
Record(continuous) - Public areas:
Mocord(continuous + events) - Testing:
Monitor(no recording)
RTSP URL Format
Standard RTSP URL:
rtsp://username:password@ip_address:port/path
Examples by manufacturer:
Hikvision:
rtsp://admin:password@192.168.x.x:554/Streaming/Channels/101
Dahua:
rtsp://admin:password@192.168.x.x:554/cam/realmonitor?channel=1&subtype=0
Amcrest:
rtsp://admin:password@192.168.x.x:554/cam/realmonitor?channel=1&subtype=0
Reolink:
rtsp://admin:password@192.168.x.x:554/h264Preview_01_main
Generic ONVIF:
rtsp://admin:password@192.168.x.x:554/stream1
Find RTSP path:
- Check camera manual/datasheet
- Use ONVIF Device Manager (Windows tool)
- Check manufacturer’s website
- Try common paths:
/stream1,/live/ch0,/h264
Video Encoding Options
video_writer
Controls how ZoneMinder handles video encoding.
Values:
| Value | Mode | Description |
|---|---|---|
0 | Disabled | No video encoding (JPEG only) |
1 | Encode | Re-encode video (high CPU, better quality) |
2 | Passthrough | Copy stream (low CPU, preserves quality) |
Recommendations:
- Passthrough (2): Default, best for most use cases
- Encode (1): If camera codec incompatible or quality issues
- Disabled (0): Only if using JPEG snapshots only
encoder_parameters
FFmpeg encoding parameters for video_writer=1.
Default:
crf=23
movflags=faststart
Parameters:
| Parameter | Description | Values |
|---|---|---|
crf | Quality (1-51) | 18=high quality, 23=balanced, 28=low quality |
movflags | MP4 optimization | faststart for web streaming |
preset | Encoding speed | ultrafast, fast, medium, slow |
High quality:
encoder_parameters: "crf=18\r\npreset=slow\r\nmovflags=faststart"
Low CPU:
encoder_parameters: "crf=28\r\npreset=ultrafast\r\nmovflags=faststart"
Resolution Settings
Common resolutions:
| Resolution | Width | Height | Name |
|---|---|---|---|
| 4K | 3840 | 2160 | Ultra HD |
| 2K | 2304 | 1296 | Quad HD |
| 1080p | 1920 | 1080 | Full HD |
| 720p | 1280 | 720 | HD |
| 480p | 640 | 480 | SD |
Recommendations:
- Critical areas: 1080p or higher
- General monitoring: 720p (balanced quality/storage)
- Low-priority: 480p (saves disk space)
Storage impact:
- 1080p uses ~2x disk space vs 720p
- 4K uses ~4x disk space vs 1080p
API Authentication
Authentication Flow
1. POST /zm/api/host/login.json
Body: user=admin&pass=password
Response: { "access_token": "xxx", "refresh_token": "yyy" }
2. Use access_token for subsequent requests
URL: /zm/api/monitors.json?token=xxx
Token expiration: Typically 1 hour
This role: Authenticates once at start of run
What This Role Does (Detailed)
1. Authenticate with ZoneMinder API
POST https://zoneminder/zm/api/host/login.json
Body: user=admin&pass=password
Response:
{
"access_token": "eyJ0eXAiOiJKV1QiLCJhbGc...",
"refresh_token": "...",
"access_token_expires": 3600
}
Extracts: access_token for use in subsequent requests
2. Get Existing Monitors
GET https://zoneminder/zm/api/monitors.json?token=<token>
Response:
{
"monitors": [
{
"Monitor": {
"Id": "1",
"Name": "Front Door",
"Type": "Ffmpeg",
...
}
}
]
}
Extracts: List of existing monitor names
3. Add New Monitors
For each camera in zoneminder_monitors_list:
If name not in existing monitors:
POST https://zoneminder/zm/api/monitors.json
Body (form-encoded):
token=<token>
Monitor[Name]=Front Door
Monitor[Enabled]=1
Monitor[Type]=Ffmpeg
Monitor[Function]=Modect
Monitor[Method]=rtpRtsp
Monitor[Path]=rtsp://...
Monitor[Width]=1920
Monitor[Height]=1080
Monitor[Colours]=4
Monitor[SaveJPEGs]=0
Monitor[VideoWriter]=2
Monitor[RecordAudio]=1
Monitor[EncoderParameters]=crf=23...
Response:
{
"message": "Monitor saved",
"id": 2
}
If name already exists: Skip (idempotent)
Security Considerations
- API Password: Store in Ansible Vault
- Camera Credentials: Included in RTSP URLs (consider vault for these too)
- SSL Validation: Enable in production
- Network Segmentation: Keep cameras on isolated VLAN
- Strong Passwords: Use complex passwords for cameras
- API User Permissions: Use dedicated API user (not admin if possible)
- RTSP Encryption: Consider RTSPS if camera supports it
Vault example:
# vault.yml
vault_zoneminder_api_password: "admin_secure_password"
vault_camera_username: "admin"
vault_camera_password: "camera_secure_password"
# Usage in monitors list
path: "rtsp://{{ vault_camera_username }}:{{ vault_camera_password }}@192.168.x.x:554/live/ch0"
Tags
This role does not define any tags. Use playbook-level tags if needed:
- hosts: localhost
roles:
- zoneminder_monitors
tags:
- zoneminder
- cameras
- monitors
- nvr
Notes
- Role runs on localhost (API calls to ZoneMinder)
become: false(no root needed for API calls)- Idempotent: Checks existing monitors before adding
- Passwords logged to console are hidden with
no_log: true - API authentication token obtained fresh each run
- Only adds monitors that don’t already exist
Troubleshooting
Authentication failed
Symptom: “Authentication failed” or 401 error
Causes:
- Wrong username/password
- ZoneMinder API authentication disabled
- User doesn’t have API permissions
Check:
# Test API manually
curl -X POST https://zoneminder/zm/api/host/login.json \
-d "user=admin&pass=password"
# Should return access_token
Fix:
- Verify credentials in vault
- Enable API auth in ZoneMinder: Options → System → OPT_USE_API
- Check user permissions: Options → Users
Monitor not added
Symptom: Task shows “skipped” or no change
Causes:
- Monitor with same name already exists
- API returned error (check output)
Check existing monitors:
curl "https://zoneminder/zm/api/monitors.json?token=<token>" | jq '.monitors[].Monitor.Name'
Fix:
- Use different monitor name
- Delete existing monitor via UI
- Check API error messages in Ansible output
Camera shows “Red” status in ZoneMinder
Symptom: Monitor added but shows red/offline
Causes:
- RTSP URL incorrect
- Camera not reachable
- Wrong credentials
- Wrong RTSP path
Test RTSP:
# Test with ffmpeg
ffmpeg -i "rtsp://user:pass@ip:554/path" -frames:v 1 test.jpg
# Test with VLC
vlc rtsp://user:pass@ip:554/path
Fix:
- Verify RTSP URL and credentials
- Check network connectivity
- Try different RTSP path (consult camera manual)
- Check firewall rules (RTSP uses port 554)
SSL certificate verification failed
Symptom: “SSL certificate problem” error
Cause: Self-signed certificate on ZoneMinder
Fix:
zoneminder_monitors_validate_certs: false
Or install CA certificate on Ansible control node
Monitor shows “Orange” status
Symptom: Monitor shows orange/yellow status
Meaning: ZoneMinder connected but having issues
Causes:
- Intermittent network issues
- Camera performance problems
- ZoneMinder overloaded (too many cameras/high resolution)
Check ZoneMinder logs:
tail -f /var/log/zm/zmc-m<monitor_id>.log
Fix:
- Reduce FPS or resolution
- Check camera performance
- Increase ZoneMinder system resources
Testing the Role
Verify Authentication
# Test API login
curl -X POST https://zoneminder/zm/api/host/login.json \
-d "user=admin&pass=password" \
-k # Skip cert verification if self-signed
# Should return JSON with access_token
List Existing Monitors
# Get token first (from curl above)
TOKEN="eyJ0eXAiOiJKV1QiLCJhbGc..."
# List monitors
curl "https://zoneminder/zm/api/monitors.json?token=$TOKEN" -k | jq
Verify Monitor Added
Via API:
curl "https://zoneminder/zm/api/monitors.json?token=$TOKEN" -k | \
jq '.monitors[].Monitor | {Id, Name, Path}'
Via Web UI:
- Log into ZoneMinder
- Console tab
- Should see new monitors listed
Test Camera Stream
In ZoneMinder:
- Click monitor name
- Should see live video feed
- Status should be green
Via ffmpeg (if issues):
ffmpeg -i "rtsp://user:pass@camera-ip:554/path" \
-frames:v 1 -y test.jpg
# If succeeds: Camera works, issue is with ZoneMinder config
# If fails: Camera/network issue
Best Practices
- Use vault for credentials: Never commit passwords to git
- Test RTSP URLs first: Verify with VLC or ffmpeg before adding
- Start with lower resolution: Test connectivity before going to 4K
- Use passthrough encoding: Default video_writer=2 for best performance
- Name cameras descriptively: “Front Door” not “Camera 1”
- Document RTSP paths: Keep record of each camera’s RTSP URL format
- Network segmentation: Put cameras on dedicated VLAN
- Monitor disk space: High-res recording uses lots of space
- Regular testing: Verify cameras recording after adding
- Idempotent playbooks: Safe to run multiple times
Common Camera Issues
Wrong RTSP Path
Symptom: Can ping camera but ZoneMinder shows red
Solutions:
- Check camera manual for correct RTSP path
- Try common paths:
/stream1,/live/ch0,/h264Preview_01_main - Use ONVIF Device Manager to discover RTSP URLs
Authentication Issues
Symptom: “401 Unauthorized” from camera
Solutions:
- Verify camera username/password
- URL-encode special characters in password
- Some cameras: admin account disabled, create new user
Network Issues
Symptom: Intermittent red/orange status
Solutions:
- Check network congestion
- Verify camera on same subnet or routed correctly
- Check multicast vs unicast RTSP (prefer unicast)
Related Roles
This role is often used with:
- zoneminder_install: Install ZoneMinder first
- SSL certificate roles: Deploy SSL certificates
- Camera setup roles: Configure camera settings
- Network roles: Configure VLANs for camera isolation
Advanced Configuration
Custom FFmpeg Options
Hardware acceleration (if supported):
encoder_parameters: "hwaccel=vaapi\r\nvaapi_device=/dev/dri/renderD128\r\ncrf=23"
H.265 encoding:
encoder_parameters: "vcodec=libx265\r\ncrf=28\r\npreset=fast"
Motion Detection Tuning
After adding camera, configure in ZoneMinder UI:
- Click camera → Zones
- Edit zones for motion detection areas
- Adjust sensitivity: Options → Images
- Set alarm thresholds
License
MIT
Author
Created for homelab infrastructure management.