Instant Email Alert - Part 2

Support and queries relating to all previous versions of ZoneMinder
Locked
tnt_tech
Posts: 9
Joined: Mon Jan 12, 2009 10:15 pm

Instant Email Alert - Part 2

Post by tnt_tech »

I posted earlier this month on how to get an instant email alert on motion detection. I spent a lot time since them working on this problem but I am not quite there yet. I know what needs to be done but I just don't have any Perl Script experience and the learning curve is slow. I need help from someone who has been working with Zoneminder for a while and who is a Perl Script programmer. From the number of views it seems that quite a few others would also be interested in this solution. I am willing to compensate you or just donate it to the project. I am sure the amount of work will be minimal for an experienced Perl Script programmer. Any takers?
haus
Posts: 213
Joined: Thu Oct 11, 2007 5:10 am

Post by haus »

I get instant email alerts; my SMTP server is set up correctly, my zmfilter is set to run every 10 seconds (ZM_FILTER_EXECUTE_INTERVAL in Options -> system)...can you be more specific about where the delay is for you?
tnt_tech
Posts: 9
Joined: Mon Jan 12, 2009 10:15 pm

Post by tnt_tech »

Hi Haus,

My SMTP is working fine and I don get the emails but when the zmfilter there is a 30 seconds delay from the time the alert happens and I get the email. My 'ZM_FILTER_EXECUTE_INTERVAL' is set to 5 seconds. For some reason zmfilter seems to take that time to process the email.
haus
Posts: 213
Joined: Thu Oct 11, 2007 5:10 am

Post by haus »

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...
haus
Posts: 213
Joined: Thu Oct 11, 2007 5:10 am

Post by haus »

haus wrote:I get instant email alerts; my SMTP server is set up correctly, my zmfilter is set to run every 10 seconds (ZM_FILTER_EXECUTE_INTERVAL in Options -> system)...can you be more specific about where the delay is for you?
Ok - we posted at the same time, sorry about that. (My long message wasn't a reply to your note I've quoted above.)

does "don" mean "do" or "don't" in your first line? I think you mean you do get the email but it takes 30 seconds. Do this as root:

tail -f /tmp/zmfilter.log

and then make an alarm happen. See what takes the 30 seconds. If it's an SMTP delay, see my thread about SMTP delays, perhaps your mail server or smarthost is causing problems. If it's an FTP upload or something else, you might want to do separate filters so that your emails go out first.
haus
Posts: 213
Joined: Thu Oct 11, 2007 5:10 am

Post by haus »

tnt_tech wrote:Hi Haus,

My SMTP is working fine and I don get the emails but when the zmfilter there is a 30 seconds delay from the time the alert happens and I get the email. My 'ZM_FILTER_EXECUTE_INTERVAL' is set to 5 seconds. For some reason zmfilter seems to take that time to process the email.
Ok - we posted at the same time, sorry about that. (My long message wasn't a reply to your note I've quoted above.)

does "don" mean "do" or "don't" in your first line? I think you mean you do get the email but it takes 30 seconds. Do this as root:

tail -f /tmp/zmfilter.log

and then make an alarm happen. See what takes the 30 seconds. If it's an SMTP delay, see my thread about SMTP delays, perhaps your mail server or smarthost is causing problems. If it's an FTP upload or something else, you might want to do separate filters so that your emails go out first.[/quote]
haus
Posts: 213
Joined: Thu Oct 11, 2007 5:10 am

Post by haus »

Oh, and be careful about running zmfilter too often. If you don't limit the number of events it returns, I'm not sure if you're causing problems on your box by potentially running overlapping instances of it (for example, if it runs longer than 5 seconds, then tries to run again, I don't know if it's written to wait, skip, or just plow through and run again). I use 10 seconds and limit to 5 results, and they have to have occurred in the last 10 minutes.
Locked