zmonvif-watch.pl zoneminder integration

This commit is contained in:
Jan M. Hochstein 2014-10-16 21:34:33 +02:00
parent cd3e8feb35
commit 7da7763059
1 changed files with 93 additions and 89 deletions

View File

@ -24,6 +24,11 @@
# This module contains the implementation of the ONVIF event watcher
#
## SETUP:
## chmod g+rw /dev/shm/zm*
## chgrp users /dev/shm/zm*
## systemctl stop iptables
use ZoneMinder;
use DBI;
@ -40,6 +45,7 @@ use SOAP::Transport::HTTP;
use Time::HiRes qw( usleep );
use Data::Dump qw(dump);
use Carp;
# ========================================================================
# Constants
@ -53,24 +59,26 @@ use constant MONITOR_RELOAD_INTERVAL => 300;
# Globals
my $verbose = 0;
my $client;
my $daemon_pid;
my $dbh;
my %monitors;
my $monitor_reload_time = 0;
# this does not work on all architectures
my @EXTRA_SOCK_OPTS = (
'ReuseAddr' => '1',
'ReusePort' => '1'
);
# =========================================================================
# signal handling
sub handler { # 1st argument is signal name
my($sig) = @_;
print "Caught a SIG$sig -- shutting down\n";
confess();
if(defined $daemon_pid){
kill($daemon_pid);
}
@ -132,7 +140,8 @@ sub loadMonitors
my %new_monitors = ();
my $sql = "select * from Monitors where find_in_set( Function, 'Modect,Mocord,Nodect' ) and ConfigType='ONVIF'";
# my $sql = "select * from Monitors where find_in_set( Function, 'Modect,Mocord,Nodect' )>0 and ConfigType='ONVIF'";
my $sql = "select * from Monitors where ConfigType='ONVIF'";
my $sth = $dbh->prepare_cached( $sql ) or Fatal( "Can't prepare '$sql': ".$dbh->errstr() );
my $res = $sth->execute() or Fatal( "Can't execute: ".$sth->errstr() );
while( my $monitor = $sth->fetchrow_hashref() )
@ -155,11 +164,43 @@ sub loadMonitors
{
$monitor->{LastEvent} = zmGetLastEvent( $monitor );
}
print "URL: " .$monitor->{ConfigURL}. "\n";
## set up ONVIF client for monitor
next if( ! $monitor->{ConfigURL} );
my $soap_version;
if($monitor->{ConfigOptions} =~ /SOAP1([12])/) {
$soap_version = "1.$1";
}
else {
$soap_version = "1.1";
}
my $client = ONVIF::Client->new( {
'url_svc_device' => $monitor->{ConfigURL},
'soap_version' => $soap_version } );
if($monitor->{User}) {
$client->set_credentials($monitor->{User}, $monitor->{Pass}, 0);
}
$client->create_services();
$monitor->{onvif_client} = $client;
$new_monitors{$monitor->{Id}} = $monitor;
}
%monitors = %new_monitors;
}
sub freeMonitors
{
foreach my $monitor ( values(%monitors) )
{
# Free up any used memory handle
zmMemInvalidate( $monitor );
}
}
# =========================================================================
### (experimental) send email
@ -271,15 +312,9 @@ require WSNotification::Elements::Subscribe;
sub subscribe
{
my ($localaddr, $topic_str, $duration) = @_;
# my $topic = WSNotification::Types::TopicExpressionType->new(
# xmlattr => {
# Dialect => "http://www.onvif.org/ver10/tev/topicExpression/ConcreteSet",
# },
# value => $topic_str
# );
my ($client, $localaddr, $topic_str, $duration) = @_;
# for debugging:
# $client->get_endpoint('events')->no_dispatch(1);
my $result = $client->get_endpoint('events')->Subscribe( {
@ -290,16 +325,16 @@ sub subscribe
# Metadata => { # WSNotification::Types::MetadataType
# },
},
# Filter => { # WSNotification::Types::FilterType
# TopicExpression => { # WSNotification::Types::TopicExpressionType
# xmlattr => {
# Dialect => "http://www.onvif.org/ver10/tev/topicExpression/ConcreteSet",
# },
# value => "tns1:RuleEngine//.",
# },
Filter => { # WSNotification::Types::FilterType
TopicExpression => { # WSNotification::Types::TopicExpressionType
xmlattr => {
Dialect => "http://www.onvif.org/ver10/tev/topicExpression/ConcreteSet",
},
value => $topic_str,
},
# MessageContent => { # WSNotification::Types::QueryExpressionType
# },
# },
},
InitialTerminationTime => xs_duration($duration), # AbsoluteOrRelativeTimeType
# SubscriptionPolicy => {
# },
@ -346,17 +381,13 @@ sub events
{
my ($localip, $localport) = @_;
Info( "Trigger daemon starting\n" );
Info( "ONVIF Trigger daemon starting\n" );
if(!defined $localip) {
$localip = '192.168.0.2';
$localport = '0';
}
my $event_svc = $client->get_endpoint('events');
# re-use local address/port
# @LWP::Protocol::http::EXTRA_SOCK_OPTS =
*LWP::Protocol::http::_extra_sock_opts = sub
@ -378,39 +409,52 @@ sub events
$port =~ s|/.*$||;
$localaddr = $localip . ':' . $port;
# ReuseAddr, ReusePort
# print "Contact to SOAP server at ", $daemon->url, "\n";
print "Server using local address " . $localaddr . "\n";
print "Daemon uses local address " . $localaddr . "\n";
# This value is passed as the LocalAddr argument to IO::Socket::INET.
my $transport = SOAP::Transport::HTTP::Client->new(
'local_address' => $localaddr );
# dump( $transport );
$event_svc->set_transport($transport);
print "Sending from local address " .
$event_svc->get_transport()->local_address . "\n";
# 'local_address' => $localaddr ); ## REUSE port
'local_address' => $localip );
#print xs_duration(89);
#exit(1);
my $submgr_svc = subscribe($localaddr, 'tns1:RuleEngine//.', SUBSCRIPTION_RENEW_INTERVAL);
if(!$submgr_svc) {
exit;
loadMonitors();
foreach $monitor (values(%monitors)) {
my $client = $monitor->{onvif_client};
my $event_svc = $client->get_endpoint('events');
$event_svc->set_transport($transport);
# print "Sending from local address " .
# $event_svc->get_transport()->local_address . "\n";
my $submgr_svc = subscribe($client, $localaddr, 'tns1:RuleEngine//.', SUBSCRIPTION_RENEW_INTERVAL);
if(!$submgr_svc) {
print "Subscription failed\n";
next;
}
$monitor->{submgr_svc} = $submgr_svc;
}
while(1) {
print "Sleeping for " . (SUBSCRIPTION_RENEW_INTERVAL - SUBSCRIPTION_RENEW_EARLY) . " seconds\n";
sleep(SUBSCRIPTION_RENEW_INTERVAL - SUBSCRIPTION_RENEW_EARLY);
print "Renewal\n";
renew($submgr_svc, SUBSCRIPTION_RENEW_INTERVAL + SUBSCRIPTION_RENEW_EARLY);
foreach $monitor (%monitors) {
if(defined $monitor->{submgr_svc}) {
renew($monitor->{submgr_svc}, SUBSCRIPTION_RENEW_INTERVAL + SUBSCRIPTION_RENEW_EARLY);
}
}
};
unsubscribe($submgr_svc);
Info( "ONVIF Trigger daemon exited\n" );
foreach $monitor (values(%monitors)) {
if(defined $monitor->{submgr_svc}) {
unsubscribe($monitor->{submgr_svc});
}
}
}
# =========================================================================
@ -462,39 +506,23 @@ sub metadata
sub HELP_MESSAGE
{
my ($fh, $pkg, $ver, $opts) = @_;
print $fh "Usage: " . __FILE__ . " [-v] <command> <device URI> <soap version> <user> <password>\n";
print $fh "Usage: " . __FILE__ . " <parameters>\n";
print $fh <<EOF
Commands are:
metadata - print some of the device's configuration settings
events - listen for events
Common parameters:
-v - increase verbosity
Device access parameters (for all commands):
device URL - the ONVIF Device service URL
soap version - SOAP version (1.1 or 1.2)
user - username of a user with access to the device
password - password for the user
Parameters:
-v - increase verbosity
-l|local-addr - listen on address (host[:port])
EOF
}
# ========================================================================
# MAIN
my $action;
my $url_svc_device;
my $soap_version;
my ($username, $password);
my ($localaddr, $localip, $localport);
logInit();
logSetSignal();
if(!GetOptions(
'command|c=s' => \$action,
'device|d=s' => \$url_svc_device,
'soap-version|s=s' => \$soap_version,
'username|u=s' => \$username,
'password|p=s' => \$password,
'local-addr|l=s' => \$localaddr,
'verbose|v=s' => \$verbose,
)) {
@ -502,11 +530,6 @@ if(!GetOptions(
exit(1);
}
if(!defined $action) {
HELP_MESSAGE(\*STDOUT);
exit(1);
}
if(defined $localaddr) {
if($localaddr =~ /(.*):(.*)/)
{
@ -520,24 +543,5 @@ if(defined $localaddr) {
{
initZm();
# all actions need URI and credentials
$client = ONVIF::Client->new( {
'url_svc_device' => $url_svc_device,
'soap_version' => $soap_version } );
$client->set_credentials($username, $password, 1);
$client->create_services();
if($action eq "metadata") {
metadata();
}
elsif($action eq "events") {
events($localip, $localport);
}
else {
print("Error: Unknown command\"$action\"");
exit(1);
}
events($localip, $localport);
}