#!/usr/bin/perl # # This script will create a montage from a collection # of images, this montage will then be sent to a list of # users defined in /etc/motionmail.d/settings.conf # # typically called from motion # motion detection software for linux # (http://www.lavrsen.dk/twiki/bin/view/Motion/WebHome) # use the following in motion.conf: # 'on_event_end /home/cam/motionmail.pl -e%v -q' use Getopt::Long; use Pod::Usage; my $help = ''; my $man = ''; my $event = ''; my $ver = ''; my $quiet = ''; my $dbg = ''; # enable verbose output $now = time; $version = '0.02'; undef $err; GetOptions ( 'help|h' => \$help, 'man' => \$man, 'e=s' => \$event, 'version|V' => \$ver, 'q' => \$quiet, 'debug' => \$dbg # option '-debug' will enable verbose output ); # only used for development if((!$event)&&(!$man)&&($ver)){ $help=1;} if($help) { pod2usage( -verbose => 1) && exit; } if($man) { pod2usage({-verbose => 2, -output => \*STDOUT}); } if($ver) { print "motionmail version $version\n"; exit; } if(!-e "/etc/motionmail.d/settings.conf"){ print "\n\t/etc/motionmail.d/settings.conf not found!\n" if(!$quiet); print "\tsee 'motionmail -man' for instructions.\n\n" if(!$quiet); exit; }else{ $excl = get_settings(); create_montage(); if($excl){ # do not notify when the event was within an exclusion time block print "(".(get_date($now))[2].") Within exclusion time, not sending.\n" if(!$quiet); exit; } notify("rmontage$event.png"); } sub create_montage { # create a montage from the images captured during this event opendir(DIR,$setting{'file_dir'}) || die "Can't open file_dir ($setting{'file_dir'}): $!\n"; @files=readdir(DIR); close(DIR); @files=grep(/$event-/,@files); chdir $setting{'file_dir'}; # Sort the files so the tiles appear in order @files=sort(@files); $files=join " ",@files; # failsave: if no images are found, don't try to run the montage command if(!$files){ print "no files found, exiting...\n" if(!$quiet); exit; } # exec the montage command # command for email thumbnails $err = `montage -verbose -geometry 35% -adjoin $files rmontage$event.png` if (($setting{'make_montage'})&&(!$excl)); print $err if($dbg); # command for montage that gets left behind on the server $err = `montage -verbose -label %f -frame 5 -geometry 100% -adjoin $files montage$event.png` if (($setting{'make_montage'})&&(!$excl)); print $err if($dbg); # move event images to the history dir if(!-e "./history"){ mkdir "./history", 0755; } foreach $file(@files){ $err = `mv --verbose $file ./history` if($setting{'keep_history'}); print $err if($dbg); } } sub notify { my($f)=shift; #this stuff would be good to move into configuration varibales my $message = "Motion was detected ".(get_date($now))[3]; send_mail($setting{'send_to'},"Motion Detected ($event)",$message,"$setting{'file_dir'}/$f"); } sub send_mail{ my($to,$subj,$message,$file)=@_; my $html=< $message
EOF use MIME::Lite; my $mime_msg = MIME::Lite->new( From => "$setting{'app_name'} <$setting{'app_email'}>", To => $to, CC => $cc, Subject => $subj, 'X-Application' => "motionmail $version by Taco Scheltema ", Type => 'multipart/related' ) || die("Error creating MIME message: $!"); $mime_msg->attach( Type => 'text/html', Data =>$html ); $mime_msg->attach( Type => 'image/png', Path =>$file, Id =>"event_$event.png" ) if($file); MIME::Lite->send('smtp', $setting{'mail_server'}) if ($setting{'mail_server'}); $mime_msg->send() || die("Error sending message: $!"); unlink $file; } sub get_settings { open(SETTINGS,"; close(SETTINGS); # the following hash is only used to sort the settings properly # very ugly solution. it is only used with the verbose (-debug) # option so it probably can be removed altogether to reduce the # size of this script %wd=( 'sunday' => 0, 'monday' => 1, 'tuesday' => 2, 'wednesday' => 3, 'thursday' => 4, 'friday' => 5, 'saturday' => 6, 'send_to' => 7, 'file_dir' => 8 ); # get rid of irrelevant lines @lines = grep(!/^#/,@lines); @lines = grep(!/^\s/,@lines); @lines = grep(!/^\n/,@lines); # fill setting hash with values from the settings.conf file foreach $line(@lines) { $line=~s/\s//g; ($key,$val)=split(/=/,$line); $setting{$key}=$val; if($val=~m/(\d{4})-(\d{4})/) { $begin{$key}=$1; $end{$key}=$2; } } # print a neatly formatted list of settings # (only with -debug) print "Settings:\n" if(($dbg)&&(!$quiet)); for $key (sort { $wd{$a} <=> $wd{$b} } keys %setting){ printf ("%14s = %-40s\n",$key,$setting{$key}) if(($dbg)&&(!$quiet)); } # check if current event was detected within an exclusion time block # if it is, do not send anything. Events will still be saved. my($day,$time)=(get_date($now))[1,2]; if(($time>=$begin{$day})&&($time<$end{$day})){ $dont_send=1; } } sub get_date { my $time = shift; $time or ($time = time); my @weekday = qw/sunday monday tuesday wednesday thursday friday saturday/; my ($min, $hr, $day, $mon, $yr, $wday) = (localtime($time))[1,2,3,4,5,6]; $yr = $yr + 1900; $mon=$mon+1; ($min < 10) and ($min = "0$min"); ($hr < 10) and ($hr = "0$hr"); ($day < 10) and ($day = "0$day"); ($mon < 10) and ($mon = "0$mon"); return "$day$mon$yr","$weekday[$wday]","$hr$min","$weekday[$wday] $day/$mon/$yr $hr:$min"; } __END__ =pod =head1 NAME motionmail - creates a montage from a collection of images and sends it to a list of email addresses. =head1 SYNOPSIS motionmail -e [-q] =head1 OPTIONS =over 8 =item B<-help> B<-h> displays a brief help message =item B<-man> displays the full manual =item B<-version> B<-V> displays version information =item B<-e> sets the event number. motionmail will look for images for this event in the image directory, it will then create a montage from these images and send the montage to the email addresses defined in the settings.conf file =item B<-q> quiet operation, this will prevent any output from being displayed. Be aware that this will also prevent errors from being displayed. =back =head1 DESCRIPTION motionmail will search for images that were stored by motion for the event given. It will create a montage of these images and subsequently send this montage to the email addresses defined in /etc/motionmail.d/settings.conf. The event images will be moved to a subdirectory called 'history', the montage image will be deleted after sending. a separate cronjob should be defined to delete old events to prevent an excess of data. This script can be called by adding: on_event_end /usr/local/bin/motionmail.pl -e=%v -q To your motion.conf =head1 FILES B Configuration settings. this file should contain the following: #---------- settings.conf ------------ # settings file for motionmail # (see motionmail -man for instructions) # comment lines start with # # blank lines, lines starting with a space and # lines starting with # will be ignored # comma separated list of email addresses send_to = taco@cannex.com.au # directory where event images are stored by motion # (see the 'target_dir' setting in motion.conf) file_dir = /home/cam/public_html # make the montage.png image (1=yes,0=no) # (nothing will be sent if this is set to 0) make_montage = 1 # keep event images (1=yes,0=no) # this will store all event images in a subdir of # file_dir called 'history' keep_history = 1 # application name # this name will appear in the 'From' field of all email app_name = Motion # application email # this will be the sender address of all email app_email = motion@cannex.com.au # the 'From:' field will be formed like this: # From: app_name # for each weekday an exclusion time can be set # this is done in 24h notation in the form of -, # events will only be sent outside the defined time block, when left empty # events will be sent for the full 24 hours. see example below monday = 0800-1700 tuesday = 0800-1700 wednesday= 0800-1700 thursday = 0800-1700 friday = 0800-1600 saturday = sunday = #---------- settings.conf ------------ =head1 REQUIREMENTS Getopt::Long, Pod::Usage, MIME::Lite =head1 VERSION motionmail version 0.01 ( Mon Jun 6 08:03:03 EST 2005 ) =head1 AUTHOR Taco Scheltema , =cut