Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 6 additions & 4 deletions src/etc/local.d/motiondetectiond.start
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
#!/bin/bash
#:set ts=4 sw=4

SLEEPSECS=4
Expand All @@ -7,7 +8,7 @@ LOGFILE=/var/log/motiondetection.log # Where to place the normal logfile (disa
PRIORITY=0
MACHINE=`hostname`
SUDO='/usr/bin/sudo'
PYTHON=`/usr/bin/which python`
PYTHON='/usr/bin/python'

CFG="/etc/motiondetection/motiondetection.cfg"
EXECUTABLE="/usr/local/bin/motiondetection.py"
Expand All @@ -18,6 +19,7 @@ email=$(awk -F= '/^email=/{print $2}' ${CFG})
verbose=$(awk -F= '/^verbose=/{print $2}' ${CFG})
logfile=$(awk -F= '/^logfile=/{print $2}' ${CFG})
password=$(awk -F= '/^password=/{print $2}' ${CFG})
auto_pause=$(awk -F= '/^auto_pause=/{print $2}' ${CFG})
email_port=$(awk -F= '/^email_port=/{print $2}' ${CFG})
server_port=$(awk -F= '/^server_port=/{print $2}' ${CFG})
filter_pets=$(awk -F= '/^filter_pets=/{print $2}' ${CFG})
Expand All @@ -29,8 +31,8 @@ delta_thresh_max=$(awk -F= '/^delta_thresh_max=/{print $2}' ${CFG})
delta_thresh_min=$(awk -F= '/^delta_thresh_min=/{print $2}' ${CFG})
motion_thresh_min=$(awk -F= '/^motion_thresh_min=/{print $2}' ${CFG})

command=" -e${email} -p${password} -c${cam_location} -b${burst_mode_opts} -f${fps} -l${logfile} -S${server_port}"
command=" ${command} -E${email_port} -C${camview_port} -i${ip} -T${delta_thresh_max} -t${delta_thresh_min} -m${motion_thresh_min}"
command=" -e${email} -p${password} -c${cam_location} -b${burst_mode_opts} -f${fps} -l${logfile} -S${server_port} -E${email_port}"
command=" ${command} -C${camview_port} -i${ip} -T${delta_thresh_max} -t${delta_thresh_min} -m${motion_thresh_min} -A${auto_pause}"

if [[ $verbose == 'Treu' ]] ; then
command="$command --verbose";
Expand Down Expand Up @@ -69,7 +71,7 @@ run_motiondetectiond() {
else
if test "x${email}" != "x" ; then
echo "motiondetection.py on $MACHINE died with exit status $EXITSTATUS. Might want to take a peek." | \
mail -s "motiondetection.py Died" $email
#mail -s "motiondetection.py Died" $email
message "Exited on signal $EXITSTATUS"
fi
fi
Expand Down
2 changes: 2 additions & 0 deletions src/etc/motiondetection/motiondetection.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,5 @@ delta_thresh_max=10000
burst_mode_opts=5
motion_thresh_min=500
server_port=50050
auto_pause=False
whitelist=[]
86 changes: 86 additions & 0 deletions src/motiondetection.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
from io import BytesIO
from shutil import copyfile
from optparse import OptionParser
from scapy.all import ARP, IP, ICMP, sniff, sr

from email.mime.image import MIMEImage
from email.mime.multipart import MIMEMultipart
Expand Down Expand Up @@ -85,6 +86,66 @@ def log(level,message,verbose=True):
pass
return

class NetworkStack(object):

ips = []

def __init__(self,whitelist=[],ignore=[]):

self.ignore = ignore
self.whitelist = whitelist

if not self.ignore:
self.ignore = ['0.0.0.0','192.168.1.1','127.0.0.1']

def _append(self,item):
if item not in NetworkStack.ips:
NetworkStack.ips.append(item)
Logging.log("INFO","Adding "
+ item
+ " to NetworkStack.ips.")

def _sniff(self):
sniff(prn=self.arp_monitor_callback, filter="arp", store=0)

def arp_monitor_callback(self,pkt):
if ARP in pkt and pkt[ARP].op in (1,2):
self._append(pkt.sprintf("%ARP.psrc%"))
return pkt.sprintf("%ARP.psrc%")

def ip_is_in_whitelist(self):
return any([ip in [whitelist for whitelist in self.whitelist] for ip in NetworkStack.ips])

def monitor_stack(self,_sleep=0.5):
while True:
time.sleep(_sleep)
for ip in NetworkStack.ips:
packet = sr(IP(dst=ip, ttl=1)/ICMP(), timeout=1)
if re.search('ICMP:(\d+)',str(packet[1])).group(1) == '1':
NetworkStack.ips.remove(ip)
Logging.log("INFO", ip
+ " is no longer connected. Removing it from ip list.")
if ip_is_in_whitelist:
Logging.log("INFO", "Disabling the sending of E-mail notifications.")
Mail.__disabled__ = True
else:
Logging.log("INFO", "Enabling the sending of E-mail notifications.")
Mail.__disabled__ = False

@staticmethod
def start_thread(proc,*args):
thread=None
try:
if args:
thread = threading.Thread(target=proc,args=args)
else:
thread = threading.Thread(target=proc)
thread.daemon = True
thread.start()
except Exception as eStartThread:
Logging.log("ERROR","Threading exception eStartThread => "
+ str(eStartThread))

# The config filename is passed to this class in the ImageCapture classes __init__ method.
# The option is the default value set in optparser and is blank by default. See the
# optparser declaration at the bottom in the if __name__ == '__main__' check.
Expand Down Expand Up @@ -144,6 +205,8 @@ def config_options(self):
config_dict[0][comm.group(1)][0] = str(comm.group(2))
elif re.search('([0-9]{1,6})', comm.group(2)) is not None:
config_dict[0][comm.group(1)][0] = int(comm.group(2))
elif re.search('(\[.*\])', comm.group(2)) is not None:
config_dict[0][comm.group(1)][0] = re.sub("[\[\]]","",comm.group(2))
else:
config_dict[0][comm.group(1)][0] = comm.group(2)
return config_dict
Expand Down Expand Up @@ -741,6 +804,17 @@ def server_main(self):
dest='fps', type='int', default='30',
help='This sets the frames per second for the motion '
+ 'capture system. It defaults to 30 frames p/s.')
parser.add_option('-W', '--whitelist',
dest="whitelist", default=[], nargs='+',
help="If a device in this whitelist connects to your wifi, "
+ "the system will not send E-mails. This option depends "
+ "on the --auto-pause option to be set as well.")
parser.add_option('-A', '--auto-pause',
dest="auto_pause", action='store_true', default=False,
help="The --auto-pause option is passed with the --whitelist "
+ "option if you want to disable E-mails while you are home. "
+ "This causes the system to continue to run and log everything "
+ "like it usually does but it will not send E-mails with pictures.")
parser.add_option('-e', '--email',
dest='email',
help='This argument is required unless you pass the '
Expand Down Expand Up @@ -809,7 +883,9 @@ def server_main(self):
'verbose': ['', options.verbose],
'logfile': ['', options.logfile],
'password': ['', options.password],
'whitelist': ['', options.whitelist],
'email_port': ['', options.email_port],
'auto_pause': ['', options.auto_pause],
'configfile': ['', options.configfile],
'server_port': ['', options.server_port],
'cam_location': ['', options.cam_location],
Expand All @@ -821,6 +897,16 @@ def server_main(self):
'motion_thresh_min': ['', options.motion_thresh_min]
}, []]

networkstack = NetworkStack()

if options.auto_pause:
if not options.whitelist:
Logging.log("WARN", "The whitelist option must be passed if the "
+ "--auto-pause option is being passed. Disabling the --auto-pause feature.")
else:
NetworkStack.start_thread(networkstack._sniff)
NetworkStack.start_thread(networkstack.monitor_stack)

configFile = ConfigFile(options.configfile)
configFile.config_options()
configFile.populate_empty_options()
Expand Down