Here’s what we’re going to setup in this guide:

  1. 24/7 Monitoring with motion detection
  2. Smartphone push notifications (motion alerts)
  3. Controlling Raspberry Pi and the monitoring system with an app

Sounds cool? Yeah, let’s jump right into it.

Hardware/Budget Requirements

  1. Raspberry Pi - depending on the version €30-70
  2. USB Camera - got mine for €10 from a local shop
  3. 16GB SD card - anywhere from €15-50

Stage Zero - Setting up a headless Raspberry Pi

Preparing the SD card

Before we begin, download the Raspbian Lite and balenaEtcher.

Once downloaded, launch balenaEtcher and select the Raspbian Lite zip as source, your SD card as targe and click “Flash!”.

The process of burning Raspbian onto the card might take few mins depending on your hardware.

Enabling SSH and connecting to Wi-Fi

Once balenaEtcher finishes with your SD card, open up your terminal and mount the newly created RPi boot partition on your local OS.

Keep in mind that /boot is always the first partiton on the SD - for example, my SD card known in-OS as /dev/mmcblk, so I will need to mount /dev/mmcblkp1

root@ssh.guru # mkdir /tmp/boot
root@ssh.guru # mount /dev/mmcblkp1 /tmp/boot

Next, we will enable SSH on boot by creating a blank ssh file in the mounted /boot partition.

root@ssh.guru # touch /tmp/boot/ssh

Finally, we will configure Wi-Fi by creating a wpa_supplicant.conf file in the mounted directory.

root@ssh.guru # vi /tmp/boot/wpa_supplicant.conf

Add the following to the new file and save changes:

country=IE     # change the country code to whatever fits your location
ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=netdev
update_config=1

network={
scan_ssid=1
ssid="WIFISSIDNAME"         # Wi-Fi SSID in plaintext
psk="WIFI123456789#!£$%"    # Wi-Fi password in plaintext
}

We can now unmount the SD card from our system and plug it into our Raspberry Pi. You should be able to ping it with its default hostname 1-2mins after powering it on (hostname is resolved via mDNS so don’t worry about your local nameserver). Use pi as the user and raspberry as password.

root@ssh.guru # umount /tmp/boot
root@ssh.guru # ssh pi@raspberrypi.local

Basic Raspberry Pi config

For starters, let’s run sudo raspi-config and use the nice and shiny GUI to change the hostname of our device and the pi password.

pi@raspberrypi.local:~ $ raspi-config

Once that’s out of the way, run the following command to edit the file used for setting up a static IP address:

pi@rasp001.ssh.guru.lan:~ $ sudo vi /etc/dhcpcd.conf

To configure a static IP address on the Wi-Fi interface (wlan0), uncomment and/or edit the following lines and adjust to your needs:

interface wlan0
static ip_address=192.168.0.254/24
static routers=192.168.0.1
static domain_name_servers=1.1.1.1

Save your changes and reboot - our Raspberry Pi is ready for the next stage of this guide.

Stage One - 24/7 Monitoring with motion detection

24/7 Monitoring - aka Live Stream

Before continuing, make sure the USB camera is plugged into one of the USB ports on the Raspberry Pi. I’d suggest to stick with a cheaper cam to avoid power problems. RPis are unfortunately known for not delivering enough power for the beefier webcams on the market (connecting a powered USB dock would workaround this issue).

For live monitoring, we will use a package known as motion. Let’s install it straight away:

pi@rasp001.ssh.guru.lan:~ $ sudo apt install motion nginx

Once installed, edit the following file.

pi@rasp001.ssh.guru.lan:~ $ vi /etc/motion/motion.conf

There are some settings in this file we need to adjust. Firstly, we need to enable the daemon setting:

############################################################
# Daemon
############################################################

# Start in daemon (background) mode and release terminal (default: off)
daemon on

Next, allow the stream to be viewed from outside the localhost:

# Restrict stream connections to localhost only (default: on)
stream_localhost off

That’s all we need for now. Save the file and enable+start the motion service unit:

pi@babypi:~ $ systemctl enable motion
pi@babypi:~ $ systemctl start motion

At this point, you should be able to point your browser (on any device including smartphones, smartwatches, TVs etc.) to http://raspberrypi.local:8081 and view the stream from your webcam (below has been obscured on purpose).

Motion Detection

The config file discussed in the section above also allows to setup motion detection. Here’s a sample config of mine. The options are very well described so I’m sure it won’t be a problem for you to adjust it to your needs.

############################################################
# Motion Detection Settings:
############################################################

# Threshold for number of changed pixels in an image that
# triggers motion detection (default: 1500)
threshold 1500

# Automatically tune the threshold down if possible (default: off)
threshold_tune on

# Noise threshold for the motion detection (default: 32)
noise_level 32

# Automatically tune the noise threshold (default: on)
noise_tune on

# Despeckle motion image using (e)rode or (d)ilate or (l)abel (Default: not defined)
# Recommended value is EedDl. Any combination (and number of) of E, e, d, and D is valid.
# (l)abeling must only be used once and the 'l' must be the last letter.
# Comment out to disable
despeckle_filter EedDl

# Detect motion in predefined areas (1 - 9). Areas are numbered like that:  1 2 3
# A script (on_area_detected) is started immediately when motion is         4 5 6
# detected in one of the given areas, but only once during an event.        7 8 9
# One or more areas can be specified with this option. Take care: This option
# does NOT restrict detection to these areas! (Default: not defined)
; area_detect

# PGM file to use as a sensitivity mask.
# Full path name to. (Default: not defined)
; mask_file value

# PGM file to completely mask out a area of image.
# Full path name to. (Default: not defined)
# mask_privacy value

# Dynamically create a mask file during operation (default: 0)
# Adjust speed of mask changes from 0 (off) to 10 (fast)
smart_mask_speed 0

# Ignore sudden massive light intensity changes given as a percentage of the picture
# area that changed intensity. Valid range: 0 - 100 , default: 0 = disabled
lightswitch 100

# Picture frames must contain motion at least the specified number of frames
# in a row before they are detected as true motion. At the default of 1, all
# motion is detected. Valid range: 1 to thousands, recommended 1-5
minimum_motion_frames 5

# Specifies the number of pre-captured (buffered) pictures from before motion
# was detected that will be output at motion detection.
# Recommended range: 0 to 5 (default: 0)
# Do not use large values! Large values will cause Motion to skip video frames and
# cause unsmooth movies. To smooth movies use larger values of post_capture instead.
pre_capture 0

# Number of frames to capture after motion is no longer detected (default: 0)
post_capture 0

# Event Gap is the seconds of no motion detection that triggers the end of an event.
# An event is defined as a series of motion images taken within a short timeframe.
# Recommended value is 60 seconds (Default). The value -1 is allowed and disables
# events causing all Motion to be written to one single movie file and no pre_capture.
# If set to 0, motion is running in gapless mode. Movies don't have gaps anymore. An
# event ends right after no more motion is detected and post_capture is over.
event_gap 30

# Maximum length in seconds of a movie
# When value is exceeded a new movie file is created. (Default: 0 = infinite)
max_movie_time 0

# Always save images even if there was no motion (default: off)
emulate_motion off

Stage Two - Smartphone Push Notifications (motion detection)

Pushbullet

For generating the push notifications, we are going to use the API of Pushbullet. Go ahead and create an account for yourself. No cost involved in what we’re going to use it for.

After creating the account, download the Pushbullet app on your phone and associate the device.

Now, go back onto Pushbullet.com and navigate to Settings->Account. Use the “Create Access Token” to generate a unique API token.

Save your token somewhere handy - we will need it soon.

Next, check your device name in the Devices tab just underneath the Account section and note it down as well.

Python!

Time to create a script for sending a push notification.

Prerequisites: python3, python3-pip and PushBullet Python module.

pi@rasp001.ssh.guru.lan:~ $ sudo apt install python3 python3-pip
pi@rasp001.ssh.guru.lan:~ $ sudo pip install PushBullet

Create a push.py file on the Raspberry Pi and make it executable:

pi@rasp001.ssh.guru.lan:~ $ touch push.py
pi@rasp001.ssh.guru.lan:~ $ chmod +x push.py

Next, paste the following code into the file.

from pushbullet import Pushbullet

pb = Pushbullet("APIKEY")

dev = pb.get_device('Galaxy S10e')
push = dev.push_note("ALERT", "MOTION DETECTED")

Alternatively, if you’d like to have multiple devices notified of detected motion, setup multiple Pushbullet accounts, associate each device with an individual account, generate API tokens for all accounts and use this version of the script:

from pushbullet import Pushbullet

pb1 = Pushbullet("APIKEY1")
pb2 = Pushbullet("APIKEY2")
# and so on and so on

dev1 = pb1.get_device('Galaxy S10e')
push = dev1.push_note("ALERT", "MOTION DETECTED")
dev2 = pb2.get_device('Galaxy S20')
push = dev2.push_note("ALERT", "MOTION DETECTED")
# ...and repeat for each account/device/token

You can quickly test if everything’s working as expected by running a command below:

pi@rasp001.ssh.guru.lan:~ $ python ./push.py

Putting it together

The last step of getting our push notifications to work is actually quite easy.

First, create a home directory for the motion user and move the script there.

pi@rasp001.ssh.guru.lan:~ $ sudo mkdir /home/motion
pi@rasp001.ssh.guru.lan:~ $ sudo chown motion:motion /home/motion
pi@rasp001.ssh.guru.lan:~ $ sudo cp ./push.py /home/motion/push.py
pi@rasp001.ssh.guru.lan:~ $ sudo chown motion:motion /home/motion/push.py

And then, edit the motion.conf file again and a line below to ensure a push notification will be generated on each motion detection event:

# Command to be executed when an event starts. (default: none)
# An event starts at first motion detected after a period of no motion defined by event_gap
on_event_start python /home/motion/push.py

Stage Three - Monitoring System Control App

Raspberry API

Finally, we’re going to make our lives easier by setting up an app to control our Raspberry Pi and the monitoring system running on it.

We will achieve the above by leveraging a Homeberry framework.

First, we need to prepare the Raspberry Pi for management via API calls. Let’s begin by pulling the files from Homeberry-API repo.

pi@rasp001.ssh.guru.lan:~ $ wget https://raw.githubusercontent.com/AmkSk/homeberry-api/master/homeberryApi.py
pi@rasp001.ssh.guru.lan:~ $ wget https://raw.githubusercontent.com/AmkSk/homeberry-api/master/homeberryApi.service

Next, we need to setup Python3 virtualenv for hosting a Flask server. This can be quickly done in these three simple steps:

#install virtualenv via pip - it has to be for Python3
pi@rasp001.ssh.guru.lan:~ $ python3 -m pip install virutalenv 
# create a "homeberry" virtualenv
pi@rasp001.ssh.guru.lan:~ $ python3 -m virtualenv homeberryApi
# use the pip nested within the virtualenv to install flask
pi@rasp001.ssh.guru.lan:~ $ /homeberryApi/bin/pip install flask

Now we can start working on the homeberryApi.py script downloaded from Github. Here’s an example of what I did with mine:

#!/home/pi/homeberryApi/bin/python
from flask import Flask
import subprocess

app = Flask(__name__)

@app.route("/monitor/testalert")
def testAlert():
    subprocess.run("sudo /home/motion/push.py".split())
        return "Sending a test alert"

@app.route("/monitor/restart")
def restartFeed():
        subprocess.run("sudo systemctl restart motion".split())
        return "Restarting live feed"
    
@app.route("/monitor/stop")
def stopFeed():
        subprocess.run("sudo systemctl stop motion".split())
        return "Stopping live feed"

@app.route("/monitor/start")
def startFeed():
        subprocess.run("sudo systemctl start motion".split())
        return "Starting live feed"

@app.route("/reboot")
def reboot():
        subprocess.run("sudo shutdown -r now".split())
        return "Restarting the OS"

if __name__ == '__main__':
        app.run(host='0.0.0.0', port=5000)

As you can see, it is pretty easy to follow. Basically, you need to create a new @app.route for each command you wish to expose over API.

Template @app.route

@app.route("/API/path")
def functionName():
        subprocess.run("command_to_run".split())
        return "System message"

Once you’re happy with the script, copy it into the homeberry directory and make it executable.

pi@rasp001.ssh.guru.lan:~ $ chmod +x homeberryApi.py
pi@rasp001.ssh.guru.lan:~ $ cp ./homeberryApi.py ./homeberryApi/homeberryApi.py

Lastly, use the other file we got from Github to create a service unit for Homeberry:

pi@rasp001.ssh.guru.lan:~ $ sudo cp homeberryApi.service /etc/systemd/system
pi@rasp001.ssh.guru.lan:~ $ sudo systemctl enable homeberryApi.service
pi@rasp001.ssh.guru.lan:~ $ sudo systemctl start homeberryApi.service

Raspberry Ap(Pi)

To finish it all off, download the homeberry.apk on your phone and install it (make sure to allow unknown sources in settings).

To configure the Homeberry app to interact with our Raspberry, select the cog icon in the upper right part of the screen.

On the following page, enter the IP address of the Raspberry followed by port 5000 (I do not recommend hostnames for this):

Next, create a new button by pressing the big plus sign at the bottom. You can give it whatever name you like (doesn’t have to be related to the API functions defined earlier). The endpoint URL should reflect the API paths specified in @app.routes in homeberryApi.py.

Activating the Raspberry commands defined in HomeberryApi script, can now be launched with a press of a button!