Zoneminder binding

Would be delighted to test, although I’m on OH1.8

+1 for this binding. Loving my ZM-setup and could really use a binding to OpenHAB to make use of motion detection etc.

I know we’ve drifted off-topic, but just to add - I found the filter system on ZM amazingly useless. Buggy (e.g. impossible to avoid it triggering a notification when a connection is temporarily lost) and poorly designed (e.g. only triggering a notification when an event finishes, when you want it triggered when the event starts).

The hacky log/awk solution is much better, as openhab can then do the heavy lifting and decide whether or not to notify.

Naturally would be much better to have this in a binding.

Does any of you guys have knowledge of how to query things in ZoneMinder? I believe that some things is accessible through the API, but the API isn’t very well documented. I haven’t found a complete .list of functionality, the documentation seems to be samples, but I have found other queries else where - so it isn’t complete.

One thing that I would be quite interested in getting out of ZoneMinder is the actual fps, that is displayed when live view is open.

Right now I have defined a Thing in OpenHAB that corresponds the monitor in zonemonitor. Further more the ZoneMinder Server is a Bridge. For the server I plan to implement a few channels:

  • Online (Switch)
  • CPU Load (Number)
  • Disk Usage (Number)

For the monitor i plan to provide these channels:

  • Online (Switch)
  • Monitor Name (Text)
  • Enabled (Switch)
  • Function (Text) (Modect, Monitor, Nodect, …)
  • Trigger Status (Recording or not) (Switch)
  • Status of zmc daemon (Switch)
  • StatusText of zmc daemon (Text)
  • Status of zma daemon (Switch)
  • StatusText of zma daemon (Text)
  • Status of zmf daemon (Switch)
  • StatusText of zmf daemon (Text)

The channels with Bold is ReadWrite channels, where the others is ReadOnly
I am a little in doubt of how it would be best to implement the trigger.

  1. A TwoWay channel, this means there is only one channel telling if ZM is triggered or not. It won’t tell you if it was triggered by OH or external
  2. Two channels: One read only containing the actual state of the ZM trigger, and one switch to trigger things from OH. The “problem” (or benefit?) with this approach is that you cannot stop a recording that is started by something else.

I tend to prefer the first option.

Another thing regarding the trigger is, that I prefer that the trigger is a fire and forget (eg.it turns off automatically after a while.), some might prefer a trigger that never does timeout? Eg. you have to be sure to turn it off from OH again.
That one will be implemented as a parameter in the Thing Config (you can set a timeout in seconds), I belive that should fit all needs?.

The UI is indeed pretty ugly and not very polished. But the ZoneNinja app for Android is really nice. And I honestly only ever need the UI to find a specific event or to clear out the old and that is just because I haven’t shan’t the time to figure out how to make zm do that automatically.

I’m not sure there is A recommended camera do much as a requirement that it support streaming without requiring a browser plugin. I’ve been experimenting with a Pi Camera streaming with ffmjpeg and that works well.

Fur the inexperienced user, the Windows based iSpy might be a better choice.

That is a much bigger philosophical question. I personally run them both on the same machine in separate Docker containers. The answer in your situation boils down to how you want to install and manage your environment. There is no right answer and lots of tradeoffs based on your particular hardware, amount is RAM, personal technical expertise, etc.

I used to use iSpy but it seems to have been abandoned by its developer, and the last version was very buggy. That drove me to zoneminder.

My technical expertise is middling, so I have openhab and zoneminder on separate machines.

That’s too bad. It looked to be a lot easier and more attractive than ZM and I lamented the fact that it only ran on Windows. Its good to know it is basically abandonware now and I’ll stop recommending it.

If you have them on separate machines it would not be too much of a leap to move one to a VM and consolidate on one machine, assuming that one machine has enough resources.

I have now build the first shaky build and it has been running on my OpenHAB instance since yesterday evening.
I have already encountered some bugs, where some of them needs fixing before a make the Pull request. Especially initialisation and discovery seems a little buggy right now. The problem is that it is often necessary to restart the OpenHAB instance after a things has been added, else it won’t initialize correct. This problem has to be fixed before I release anything.

Although if you really can’t wait and you are ready to use a very shaky build of ZoneMinder Binding, you can find the source code at my GitHub:

Hopefully I have something more stable in a couple of days.

Over the last couple of days, I have been focusing on initialisation of things, and error handling. I have rewritten/refactored a lot of the orignal code. Right now it seems that the error handling is heading in the right direction, although more needs to be done there. There is still some nasty bugs in initialisation, espicially when trying to recover from a wrong config.
My focus will be in fixing this, and when that is done I believe we are ready to rock.

One thing that I would appreciate is if somebody might help me with some knowledge about the older ZoneMinder servers (eg. how can I install older versions? I have 1.30 installed, but would like to test on 1.29 as well, and also 1.28 to verify that I block this version correctly. Anybody has the knowledge of getting an older ZoneMinder server up and running? A tutorial would be fine.

Hi,

I’m not sure if you want to try to provide backward compatibility or not. I think that the API was available in 1.28, but didn’t function well. 1.29 was better, but I’m not 100% sure that the API was totally functional until 1.30. It might be best to just baseline at 1.30 and move from there. I can’t imagine a reason for anyone to want to run an older version of ZM.

BTW, I wrote a small PERL script that I use to interact with ZM via the APIs. It started as a proof of concept, but made its way into my production OH system. Basically I have OH switches that when I turn on/off run this script to enable / disable monitors. I have a “home” and “away” mode in my OH setup and I like to turn off my indoor ZM cameras when I am home, so I just have these switches turn off when I move to “home” mode. My code is below. It is far from being super-awesome, but maybe it would be useful as an example for you to take to the next level.

I’m pretty excited about you creating a proper plugin. I will definitely be a user.

#!/usr/bin/perl

## zmIntegration.pl Written by T.C. Weichmann on 08-APR-2016


#use strict;
#use warnings;
use XML::Simple;
use Data::Dumper;
use LWP::UserAgent;
use HTTP::Cookies;
use HTTP::Request;
use HTTP::Request::Common;

############################################################################
my $zmServer = "http://ip";
my $zmUser = "usnme";
my $zmPass = "passwd";
############################################################################


## This creates a new browser instance
my $ua = LWP::UserAgent->new();

if ($ARGV[0] eq "status") {
	$runStatusXML = getRunStatus();
}elsif ($ARGV[0] eq "listmonitors") {
	$listMonitors = getMonitors();
}elsif ($ARGV[0] eq "disable") {
	$disableMon5 = disableMonitor("$ARGV[1]");
}elsif ($ARGV[0] eq "enable") {
	$enableMon5 = enableMonitor("$ARGV[1]")
}elsif ($ARGV[0] eq "restart") {
	$restart = restartZM();
}else{
	print "Useage: zm-integration.pl COMMAND [N]\n\n";
	print "Commands:\n";
	print "status\t\tLists the run status of zoneminder\n";
	print "listmonitors\tLists all monitors and their settings\n";
	print "disable [N]\tDisables monitor N where N=the monitor ID\n";
	print "enable [N]\tEnables monitor N where N=the monitor ID\n";
	print "restart\t\tRestarts the ZM server\n";
}


sub login{  
	##  Login to the ZM server and get the session key
	#
	#
	#print "$zmServer,$zmUser,$zmPass\n"; ## debug - to make sure vars are being passed

	## This sets up the cookie jar variable so it can be referenced later
	my $cookie_jar = HTTP::Cookies->new(
		file => "$ENV{'HOME'}/zm-cookies-new.txt",
		autosave => 1,
		ignore_discard => 1
	);

	## This connects the browser to the cookie jar
	$ua->cookie_jar($cookie_jar);

	## This sets up the path on the server to find ZM
	$zmurl="$zmServer/zm/index.php";

	## This sends the http request to the ZM server
	my $req = $ua->post(
		"$zmServer/zm/index.php", [
			"username"=>$zmUser,
			"password"=>$zmPass,
			"action"=>"login",
			"view"=>"console",
		]
	);

	##This extracts the cookies and prints to console - for debug only
	#$cookie_jar->extract_cookies( $req );
	#print "Cookies:\n", $cookie_jar->as_string,"\n";

	die "Can't connect to $zmServer -- ", $req->status_line
		unless $req->is_success;

	die "Hey, I was expecting HTML, not ", $req->content_type
		unless $req->content_type eq 'text/html';
}

sub getRunStatus{
	login();
	#print "Server address: $zmServer\n";
	my $req = $ua->get(
		"$zmServer/zm/api/host/daemonCheck.xml"
	);
	print $req->content,"\n";
        print $req>status_line,"\n";
}

sub getMonitors{
	login();
        print "Getting Monitor list at: $zmServer\n";
        my $req = $ua->get(
                "$zmServer/zm/api/monitors.xml"
        );
        print $req->content,"\n";
        print $req>status_line,"\n";

	my $simple = XML::Simple->new( );             # initialize the object
	my $tree = $simple->XMLin( $req->content );   # read, store document

	# test access to the tree
	print Dumper( $tree );
	#print "The user prefers the font " . $tree->{ font }->{ name } . " at " . $tree->{ font }->{ size } . " points.\n";
	print "Monitor: " . $tree->{ monitors }->{ Monitor }->{ Name } . "\n";

} 

sub disableMonitor{
	# From API documentation:
	# curl -XPOST http://server/zm/api/monitors/1.json -d "Monitor[Function]=Modect&Monitor[Enabled]:true"
	# curl -XPUT http://server/zm/api/monitors/1.json -d "Monitor[Name]=test1"
	# Monitor[Function]=Modect
	#
	my ($monID) = @_;
	login();
	
	print "Disabeling Monitor $monID\n";
        my $req = $ua->put(
                "$zmServer/zm/api/monitors/$monID.json", [
                        "Monitor[Function]"=>"None"
				]
        );
	print $req->content,"\n";
        print $req>status_line,"\n";
}

sub enableMonitor{
        # From API documentation:
        # curl -XPOST http://server/zm/api/monitors/1.json -d "Monitor[Function]=Modect&Monitor[Enabled]:true"
        my ($monID) = @_;
		login();

        print "Enabeling Monitor $monID\n";
        my $req = $ua->post(
                "$zmServer/zm/api/monitors/$monID.json", [
					"Monitor[Enabled]"=>"1",
					"Monitor[Function]"=>"Modect",
               ]
        );
        print $req->content,"\n";
        print $req>status_line,"\n";
}
sub restartZM{
	# From API documentation:
	# curl -XPOST  http://server/zm/api/states/change/restart.json
        login();

        print "Restarting ZM";
        my $req = $ua->post(
                "$zmServer/zm/api/states/change/restart.json"
        );
        print $req->content,"\n";
        print $req>status_line,"\n";
}

For the same reasons that you clearly state I don’t want to support versions older than 1.29. Actually I read on the ZM forum that they state that the API isn’t there in 1.28 (as far as I remeber it is there but at a experimental state), I started my POC on ZM 1.29 and it seems to run OK. Still I would like different environments to test against (in 1.29 the getVersion is reporting something like 1.29.1 for the API, where it reports 1.0 in 1.30. Such things like that would be nice to test against.
The reason I want to test against 1.28, is to make sure that I catches this particular version and gives some corresponding warnings in the log.

Btw. thanks for sharing your script, I will see if it brings any new knowledge to my head :slight_smile:

Docker is great for this sort of thing. I’m running 1.29 in a docker container. Specifically the aptalca/zoneminder-1.29 image. Installation is just:

docker pull aptalca/zoneminder-1.29

See the image’s page on docker hub for details on running it:

https://hub.docker.com/r/aptalca/zoneminder-1.29/

Because it is what they have and they are not yet ready to upgrade for various reasons. My current reason is I don’t and won’t have time to find and/or build a new Docker image with 1.30 for quite some time.

Just i little status update, A couple of days ago, I finally realised that the code was in a poor condition, with a lot of problems built in. First and biggeest problem is that the code was cluttered code used in my Proof Of Concept way back. So I finally decided that I would rewrite the worst part. I have decided to put all code that handles access (listener and http) to the zoneminder server in a separate library, so it is encapsulated. My problem is that I am not familiar with Java, and have used some time just for getting the project to build as I wish :slight_smile:
Right now my existing code is more or less moved to that library. I wil now start to clean to OH binding. That cleaning will remove a lot of the code, and thus making it lot more obvious what is going on.

I have now created a pull request, so this process has started and it will off course take the time of reviews, and comments, and some corrections of the souce code, before it appears as a standard OH Binding.

Meanwhile it can be retrieved from my personal github: https://github.com/Mr-Eskildsen/openhab2-addons

I register Issues, and right now there is one Issue registered, which I expect to correct ASAP. I believe I have found the cause, but didn’t manage to test it yesterday evening. It would be apreciated if you can start using it and give some feedback of things not working as expected. Though my tiem is limited, I will try to prioritize bug fixes. New functionality will probably be postponed until after the pull request is finished.

I am very excited about your binding! Thank you for all the hard work.

I currently have a Pi camera running MotionEyeOS and while I like the UI a lot, I can’t easily integrate it with OH (as you know). I want to have a camera at the front and rear of the house with motion sensing capabilities so I can pass that motion event to OH and have it turn on lights, while the cameras are armed of course.

I’m currently running OH1 but would be happy to upgrade to OH2 if your binding will only work on that platform. Please keep us updated on the progress with the binding. I’ll be switching to OH2 and running Zoneminder on my Pi camera so I can use your binding.

Thanks again!

-Ryan

The Pull request is still in queue, there is a lot of other pull requests in front of ZoneMinder. Also I am afraid that since I am NOT expericed Java programmer, I expect a feedback loop, though I have tried to “listen and learn” from other reviews.
I will see if I can provide a jar of the binding, so that you can run it from your addon folder.

cool - a jar would be fantastic

Dan

https://github.com/openhab/openhab2-addons/files/648873/zoneminder-2.0.0.zip

There is also a readme:

Thank you very much! I started installing ZM on my Pi camera last night but ran into some snags. Once that’s up and running I’ll upgrade to OH2 and get this running.

Do you have any examples you can add to the Wiki? As someone who is new to this, the examples help me muddle through things. Of course you’ve already done so much I don’t want to be greedy!

Thanks again!

-Ryan