Raspberry Pi motion detection and notification program

PiDoorCam updated

This is the amended, latest version of my Raspberry Pi program to watch the street outside, detect change, and send a picture to another computer for storage, and to my phone. I’m often in the back garden, and can’t hear the doorbell; missing deliveries is rather annoying!

It no longer tries to take pictures when it’s dark, not just because I don’t have an infra-red camera and infra-red floodlights, but mainly because the street light outside flashes on and off all night, and I don’t want hundreds of pictures of that!

You’ll need to set up a Pushover account, which is free provided you don’t send too many notifications, and a Ramdisk with a directory called /var/tmp on the Pi.

Program listing

# Program for PiDoorCam

# Detects motion, and when it spots some, takes a high resolution
# picture, and sends the picture to another computer, also
# a notification via Pushover, to my phone.

import io
import os
import picamera
import ftplib
import time
import datetime
from PIL import Image
import requests
import json
import schedule

camera = picamera.PiCamera()
picamera.PiCamera.CAPTURE_TIMEOUT = 30

# If we detect 100 pixels that changed by 30, we have seen movement.
pixels = 100
difference = 30

# Use the maximum resolution of the camera.
# This is for V1. V2 is 3280 x 2464.
# It’s also correct for the ZeroCam.
width = 2592
height = 1944

# Internet lookup of sunrise and sunset at our location
def sunrise_sunset():
    global sunrise, sunset
    
    # Location of greenhouse is lat = yyyyy lon = xxxxx
    url = ‘https://api.sunrise-sunset.org/json?lat=yyyyy&lng=xxxxx’
    response = requests.get(url)
    dict = response.json()
    res = dict.get(‘results’)

    curtim = datetime.datetime.now()
    hm = res.get(‘sunrise’).split(“:”)
    sunrise = curtim.replace(hour=int(hm[0])-1, minute=int(hm[1]))
    print(“An hour before sunrise “,sunrise)
    
    hm = res.get(‘sunset’).split(“:”)
    sunset = curtim.replace(hour=int(hm[0])+13, minute=int(hm[1]))
    print(“An hour after sunset   “,sunset)

# I copied this voodoo motion detection from somewhere. Changed the timeout
#  setting above to prevent the occasional failures to complete captures.
# Only alter this if you know what you are doing!
def compare():
   camera.resolution = (100, 75)
   stream = io.BytesIO()
   format = ‘bmp’
   camera.capture(stream, format)
   stream.seek(0)
   im = Image.open(stream)
   buffer = im.load()
   stream.close()
   return im, buffer

# Function to take a new high resolution picture, send it to another computer,
# send it to my phone, and then delete it.
def newimage(width, height):
    when = datetime.datetime.now()
    filename = “door-%04d%02d%02d-%02d%02d%02d.jpg”
               % (when.year, when.month, when.day, when.hour, when.minute, when.second)
    camera.resolution = (width, height)
    camera.capture(“/var/tmp/”+filename)

    connected = True
    ftp = ftplib.FTP()
    ftp.connect(“computer-name”)
    
    try:
        ftp.login(“user-name”,”password”)
    except ftplib.all_errors:
        connected = False
        print (“Failed to login to server.”)
        ftp.quit()
        
    if connected:
        ftp.storbinary(‘STOR ‘+filename, open(“/var/tmp/”+filename, “rb”))
        print (“Sent to server “, filename)

    ftp.quit()

# Code to send the Pushover message. Make picture smaller first.
# Note this uses a Ramdisk you must set up elsewhere.
    im = Image.open(“/var/tmp/”+filename)
    im.resize((324,243),Image.ANTIALIAS)
    im.save(“/var/tmp/”+filename)
    
    r = requests.post(“https://api.pushover.net/1/messages.json”, data = {
        “token”: “you-need-to-get-a-token-from-pushover”,
        “user”: “you-need-to-get-a-user-name-from-pushover”,
        “device”: “your-device”,
        “sound”: “intermission”,
        “message”: filename
    },
    files = {
        “attachment”: (filename, open(“/var/tmp/”+filename, “rb”), “image/jpeg”)
    })
   # Check r for problems – maybe put a delay here?
    if r.status_code != 200:
        print(“Pushover message failed.”)
    else:
        print(“Pushover accepted the message.”)
         
# Now delete the file.
    os.remove(“/var/tmp/”+filename)
    # Delay to avoid being nasty to Pushover server.
    time.sleep(5)

# Main program.

camera.rotation = 0
print(“Running door.py”)
image1, buffer1 = compare()

# Find sunrise and sunset times at two in the morning, and once
# at startup.
schedule.every().day.at(“02:00”).do(sunrise_sunset)
sunrise_sunset()
while (True):
   # See if it’s time to get sunrise and sunset.
   schedule.run_pending()

   image2, buffer2 = compare()

   changedpixels = 0
   for x in range(0, 100):
      for y in range(0, 75):
         pixdiff = abs(buffer1[x,y][1] – buffer2[x,y][1])
         if pixdiff > difference:
            changedpixels += 1

   # See if we think something moved.
   if changedpixels > pixels:
   # See if it’s light enough to take a picture.
      now = datetime.datetime.now()
      if now > sunrise and now < sunset:
          newimage(width, height)
      else:
          print(“A bit dark at “,now)

   image1 = image2
   buffer1 = buffer2
Me, wearing a Panama hat, spectacles, and a fine growth of facial hair.

Author: Walrus

Just this guy, you know.