API Tricks/Create Monitors

From ZoneMinder Wiki
Jump to navigationJump to search

Back to list of Tricks.

Creating Multiple Monitors

I have 10 cameras, all Grandstream, each one serves up a low def stream and a high def stream. I do motion detection on the low def, link the hi def monitor to it. So any motion on the low def stream gets recorded on both.

I mistakenly blew away my config when I updated to 1.28.109 so I was looking for an easy way to create a new setup quickly. I decided to use the API.

Decided to use perl script to create the curl commands using attributes read from a CSV file. I'm no expert in any of this (perl, zoneminder or API). I just hammer away with what I do know until I get the results I want.

CSV file

I started with a CSV file to define specfics about each camera. Name,MonitorID,Function,Last Octet ofIP,Path,Width,Height,Linked Monitor,Label Format Size

Those items aren't the same across the board, the rest of the config is identical on each monitor. (in my case anyway)


The perl script

I probably should have done the research so I could have used some of the perl modules that make use of curl, but I just decided to use perl to create all the curl commands I would need to run. (I added " \" to break up the lines here, where you see them it should be all one long line.)

# Build all monitors at once
use Text::CSV;
# use Data::Dumper;

$monitors = Text::CSV::->new();
open (CSV, "<", $infile) or die $!;
while (<CSV>) {
	if ($monitors->parse($_)) {
		my @columns = $monitors->fields();
		push @monitors, {
			Name => @columns[0],
			Id => @columns[1],
			Function => @columns[2],
			Ip => @columns[3],
			Path => @columns[4],
			Width => @columns[5],
			Height => @columns[6],
			Linked => @columns[7],
			LabelSize => @columns[8],
close CSV;

@sorted = sort { $a->{Id} cmp $b->{Id} } @monitors;

foreach $cam (@sorted) {
	print "curl -k -XPOST https://localhost/zm/api/monitors.json -d \"Monitor\[Name\]=$cam->{Name}& \
Monitor\[Id\]=$cam->{Id}&Monitor[Function]=$cam->{Function}&Monitor[Path]=rtsp://192.168.1.$cam->{Ip}/$cam->{Path}?tcp& \
Monitor[Width]=$cam->{Width}&Monitor[Height]=$cam->{Height}&Monitor[LinkedMonitors]=$cam->{Linked}& \

$common="Monitor[LabelFormat]=%N-%d/%m/%y %H:%M:%S.%f&Monitor[Colours]=3&Monitor[LabelX]=0&Monitor[LabelY]=0& \
Monitor[WarmupCount]=25&Monitor[PreEventCount]=25&Monitor[PostEventCount]=300&Monitor[StreamReplayBuffer]=1000& \
Monitor[AlarmFrameCount]=3&Monitor[SectionLength]=600&Monitor[MaximumFPS]=0.00&Monitor[AlarmMaxFPS]=0.00& \
Monitor[RefBlendPerc]=6&Monitor[AlarmRefBlendPerc]=6&Monitor[TrackMotion]=0&Monitor[Method]=rtpRtsp& \
Monitor[RefBlendPerc]=12&Monitor[AlarmRefBlendPerc]=12&Monitor[ImageBufferCount]=100&Monitor[WarmupCount]=25& \

foreach $cam (@sorted) {
	print "curl -k -XPUT https://localhost/zm/api/monitors/$cam->{Id}.json -d \"${common}\"\n";

Using it

When the perl script is run it just prints out the commands needed to create the monitors. I redirected that to a temporary script and executed it. Takes a bit of time to run through each command but it did work quite nicely.

I ended up with a large script containing two commands for each monitor.

Initially create the monitor with it's specific info:

curl -k -XPOST https://localhost/zm/api/monitors.json -d "Monitor[Name]=Front&Monitor[Id]=101&Monitor[Function]=Nodect& \
Monitor[Path]=rtsp://[Width]=1920&Monitor[Height]=1080&Monitor[LinkedMonitors]=201& \

Then set all the settings that are the same across the board.

curl -k -XPUT https://localhost/zm/api/monitors/101.json -d "Monitor[LabelFormat]=%N-%d/%m/%y %H:%M:%S.%f&Monitor[Colours]=3& \
Monitor[LabelX]=0&Monitor[LabelY]=0&Monitor[WarmupCount]=25&Monitor[PreEventCount]=25&Monitor[PostEventCount]=300& \
Monitor[StreamReplayBuffer]=1000&Monitor[AlarmFrameCount]=3&Monitor[SectionLength]=600&Monitor[MaximumFPS]=0.00& \
Monitor[AlarmMaxFPS]=0.00&Monitor[RefBlendPerc]=6&Monitor[AlarmRefBlendPerc]=6&Monitor[TrackMotion]=0& \
Monitor[Method]=rtpRtsp&Monitor[RefBlendPerc]=12&Monitor[AlarmRefBlendPerc]=12&Monitor[ImageBufferCount]=100& \

Some notes

  • I tested it by stopping zoneminder, running "mysql -u root -p" and logging in.
    • drop database zm; exit;
    • mysql -u root -p < /usr/share/zoneminder/db/zm_create.sql
    • mysql -u root -p reload
      • Any future use of this I'll just do a "drop table monitors;" in mysql and pull just the monitor table creation section out of the zm_create.sql script. That way all the global config won't get touched.
  • Then restarted zoneminder and ran my script.
  • Some wins:
    • You can delete all your monitors and recreate them in a different order pretty quick and easy.
    • You can define the monitorID instead of letting zoneminder pick the next ID sequentially. This let me use 3 digit ID's, all the hi def monitors are 1?? and the low def are 2??. Might be silly but later on if I add a new camera both streams will be grouped the way I want them instead of just added on to the end. Pretty sure when I get around to using this method to create zones I can do something similar, i.e. hi def monitorID is 201, low def ID is 101, active zone is 1101, exclusive zone is 2101. So the ID's will be relatable. Maybe overkill, but who knows could turn out to be a big win later on for very little setup now.
  • Some drawbacks
    • No default zone is created. But I plan to work out what's needed to rebuild the zones also. Shouldn't be too hard. I've created them manually right now and can pull that config via the API to use as a starting point.
    • I did all this before enabling OPT_USE_AUTH. If I tried it now I'd have to mess around with cookies or temporarily turn the auth off.
    • If you have a variety of camera manufactures/models you might not be able to do this as easily. Widely varying setups would get to be a bit more difficult.