In your first thread someone pointed a link to another thread I had replied in. There is a perl script in the WIki that's pretty much a drop-in script, but you do have to add your own 'what to do' upon motion detection.
Here's mine:
Code: Select all
#!/usr/bin/perl -w
use strict;
use ZoneMinder;
$| = 1;
zmDbgInit( "myscript", level=>0, to_log=>0, to_syslog=>0, to_term=>1 );
my $dbh = DBI->connect( "DBI:mysql:database=".ZM_DB_NAME.";host=".ZM_DB_HOST, ZM_DB_USER, ZM_DB_PASS );
my $check_alert_iterations = 6;
my $pause_seconds = 7;
while ( 1 ) {
my $sql = "select M.*, max(E.Id) as LastEventId from Monitors as M left join Events as E on M.Id = E.MonitorId where M.Function != 'None' and M.Name like '-%' group by (M.Id)";
my $sth = $dbh->prepare_cached( $sql ) or die( "Can't prepare '$sql': ".$dbh->errstr() );
my $res = $sth->execute() or die( "Can't execute '$sql': ".$sth->errstr() );
my @monitors;
while ( my $monitor = $sth->fetchrow_hashref() )
{
push( @monitors, $monitor );
}
for ( my $j = 1; $j <= $check_alert_iterations; $j++) {
foreach my $monitor ( @monitors )
{
next if ( !zmShmVerify( $monitor ) );
if ( my $last_event_id = zmHasAlarmed( $monitor, $monitor->{LastEventId} ) )
{
$monitor->{LastEventId} = $last_event_id;
#print( "Monitor ".$monitor->{Name}." has alarmed\n" );
system("aplay -q /usr/share/sounds/alert.wav");
}
}
sleep( $pause_seconds );
}
$sth->finish();
}
Quick explanation of my major changes:
1. my $sql line -> I only care about monitors whose function isn't "None" and name starts with "-" (all my indoor cams start with "-" as an easy distinction from the outdoor cams).
2. the iteration loop - if memory serves, you have to do this because if the status of your monitors changes and you keep running this script, you'll get errors about shared memory failing or something like that. So from time to time you have to re-run the query so that this script can pick up any new monitors that match the criteria in the $sql line or drop any monitors that are no longer active. I use run states to determine the "armed" or "disarmed" status of my zm installation.
But I don't want to re-run the database statement every second, so I put in a 7-second pause and a 6-loop iteration on the query, that just periodically re-runs the sql statement to be sure it has the latest information from the database. Those are arbitrary numbers designed to keep the database queries at a reasonable level.
3. system("aplay -q ...") this line plays an alert which I grabbed from AT&T's text to speech demo that says "alert; motion has been detected' in a very loud voice in my house. So if anyone is in here when the system is armed, they'll know they're being watched. The best part is that the entire system is on a UPS, so even if you cut the power to the house, she'll still holler at you in that dead-behind-the-eyes voice with a rather stern British accent. Priceless.
And yes, this script does provide truly instant alerts as opposed to waiting for events to finish. I rarely have the problem of events not finishing quickly because my cams are situated in "walk through" areas rather than "activity areas" (i.e., someone moves through the frame on their way elsewhere rather than loitering in the modect area).
Oh, and to run this, I just have a script called "zm_status_check.pl" that runs via CRON every minute:
Code: Select all
#!/usr/bin/perl
#
# ==========================================================================
use strict;
use bytes;
# This will be run via CRON. Every minute, see if zm_alert.pl is running
# by looking at `ps -e | grep zm_alert.pl`. If it's running, exit nicely.
# If it's not running, start it.
# Also make sure the intruder and west pathway alerts are running.
# ==========================================================================
# Are we already running?
my $PID2 = `ps -e | grep zm_alert.pl`; # Indoor intruder alert
if ($PID2 =~ /zm_alert.pl/)
{
#print "zm_alert.pl is already running. Exiting...";
} else {
print "Starting zm_alert.pl...";
system("/usr/bin/zm_alert.pl &");
}
exit;
Hope that helps somewhat...