Smart Doorbell using Raspberry Pi

About a year ago, I had to do a project titled Smart Home Automation using a Raspberry Pi and Arduino, part of the project  incorporated a doorbell but not just any doorbell but a Smart doorbell.

Why do I call it a Smart Doorbell?

A classic doorbell can be defined as a signalling device typically placed near an entry door to a building. When a visitor presses a button the bell rings inside the building, alerting the occupant to the presence of the visitor.

The smart doorbell, works in that manner however when a visitor presses the doorbell button it notifies the occupant with an images of the visitor and also sends an email, sms and push notification (to an smart device) to the occupant in case they are not able to hear the bell ringing.

If the button is pressed a pre-recorded voice notification is played for the occupant to check the door as there might be someone, this happens concurrently with an sms notification while a picture of the visitor is captured and sent to both email and push notification. Initially there was a feature to enable 2-way real-time video communication but due to network latency and high resource usage the feature was deprecated.

Click For A Demo

Comment if you need the code

See code snippets(Initial Revision): https://mmphego.wordpress.com/2015/01/11/smart-doorbell-using-rpi-with-voice-and-email-notification/

Sublime Text useful python snippets

For the past few months, I have been trying to move from using Geany to using Sublime Text as my primary text editor.

I find myself repeating some repetitive operations dozen times a day when coding in python, such as import IPython;IPython.embed() and etc.

So here are some of my Sublime Text snippets to enhance my productivity, and simplify my life.

Sublime Text 3 makes it super easy to create a snippet. You simply select Tools > Developer > New Snippet from the toolbar menu and you’re off to the races!

1. IPython debugger

Type ipyt and press tab to automatically insert IPython debugger under current pointer.

<snippet>
    <content>
        <![CDATA[import IPython;IPython.embed()]]>
    </content>
    <tabTrigger>ipython</tabTrigger>
    <scope>source.python</scope>
    <description>Interactive IPython debugger</description>
</snippet>

2. Docstring

Type docstring and press tab to automatically insert a numpy-style valid
docstring pattern right after your function definition

<snippet>
    <content><![CDATA[
"""${1:One liner description}
Parameters
----------
${2}
Returns
-------
${3}
"""
]]></content>
    <tabTrigger>docstring</tabTrigger>
    <scope>source.python</scope>
    <description>Adds a docstring skeleton to function</description>
</snippet>

and save your snippets with the .sublime-snippet file extension in /home/$USER/.config/sublime-text-3/Packages/User

The Zen of Python, by Tim Peters

While browsing around the Interweb I found a little python poem – Words to-live-by in this world of Pythoning/Pythonian/Pythonization/Pythology or whatever its called.

>>> import this
The Zen of Python, by Tim Peters

Beautiful is better than ugly.
Explicit is better than implicit.
Simple is better than complex.
Complex is better than complicated.
Flat is better than nested.
Sparse is better than dense.
Readability counts.
Special cases aren’t special enough to break the rules.
Although practicality beats purity.
Errors should never pass silently.
Unless explicitly silenced.
In the face of ambiguity, refuse the temptation to guess.
There should be one– and preferably only one –obvious way to do it.
Although that way may not be obvious at first unless you’re Dutch.
Now is better than never.
Although never is often better than *right* now.
If the implementation is hard to explain, it’s a bad idea.
If the implementation is easy to explain, it may be a good idea.
Namespaces are one honking great idea — let’s do more of those!

Smart Doorbell using RPi with voice and email notification.

Part of my project involves having a doorbell, I figured a simple doorbell whereby a visitor just presses a button and then it rings/ notify whoever is it the house, That’s not really creative in my point of view.

So I then decided why not have a doorbell that not only rings when the button is pressed but also tell you someone is at the door and then sends you an email notification just in case you might be away(Not able to hear the bell.) as well as log the time that the button was pressed.

IDEA: It would be really cool if I had a USB Webcam that would take a picture of the person visiting and emails it.

first things first, we’ll need to create the wav file ie TextToSpeech. Using this simple tool below.

pico2wave -w DoorNotify.wav "Someone is ringing the doorbell. Please Go Check"

Next, create a python script that will watch the state of GPIO pin 18 on the RPi and take some action when the doorbell brings it to ‘HIGH’.

#!/usr/bin/env python2.7
__author__ = "Mpho Mphego"
__version__ = "$Revision: 1.1 $"
__description__ = "Smart Doorbell Notifier with voice and email notification : Interrupt driven"
__date__ = "$Date: 2015/01/11 02:23 $"
__copyright__ = "Copyright (c) 2015 Mpho Mphego"
__url__ = "mmphego.wordpress.com"
__license__ = "Python"

import RPi.GPIO as GPIO
import time
import os

# Connect to Relay which will control the 12V Buzzer.
led = 17 #GPIO0
button = 18 #GPIO1

GPIO.setwarnings(False)
GPIO.setmode(GPIO.BCM)
GPIO.setup(led, GPIO.OUT)
time.sleep(0.1)
GPIO.output(led, False)

# GPIO 1 set up as inputs, pulled up to avoid false detection.
# Both ports are wired to connect to GND on button press.
# So we'll be setting up falling edge detection for both
GPIO.setup(button, GPIO.IN, pull_up_down=GPIO.PUD_UP)

# define callback functions
# this will run when an event are detected
def buttonHandler(channel):
    print "falling edge detected on 18"
    GPIO.output(led, True)
    time.sleep(1)
    GPIO.output(led, False)
    os.system("aplay /home/pi/Scripts/Smart_DoorBell/DoorNotify.wav")			
    os.system("python /home/pi/Scripts/Smart_DoorBell/DoorBellLogger.py")
    time.sleep(1)
    GPIO.output(led, True)
    time.sleep(1)
    GPIO.output(led, False)

# when a falling edge is detected on port 1, regardless of whatever 
# else is happening in the program, the function buttonHandler will be run
GPIO.add_event_detect(button, GPIO.FALLING, callback=buttonHandler, bouncetime=5000)

try:
    print "Waiting for button to be pressed"
    while True:
        # To ease the CPU Usage have a 10s delay on while loop.
        time.sleep(10)
        continue
except:
    GPIO.cleanup()       # clean up GPIO on CTRL+C exit
GPIO.cleanup()           # clean up GPIO on normal exit
print "Clean up by resetting all GPIO"

Now for the fun part, the email notification and data log.


#!/usr/bin/env python
__author__ = "Mpho Mphego"
__description__ = "Doorbell notifier time logger and email notifier."
__version__ = "$Revision: 1.0 $"
__date__ = "$Date: 2015/01/10 02:09 $"
__copyright__ = "Copyright (c) 2015 Mpho Mphego"
__license__ = "Python"

import time
import datetime
import os
import smtplib
from email.mime.text import MIMEText

#-----------------------Data Logger-----------------
f=open('/home/pi/Logs/DoorBell_Data_Log.txt','a')
now = datetime.datetime.now()
timestamp = now.strftime("%H:%M on %Y/%m/%d")
outstring1 = " Someone was at the door at " + str(timestamp) 
outstring2 = "\n********************************************* \n "
outstring = outstring1 + outstring2
f.write(outstring)
f.close()

# -----------------------Email Notifier----------------------
# Replace with your details.
USERNAME = "*******@gmail.com"
PASSWORD = "*********"
MAILTO  = "******@gmail.com"

if os.system("ping -c 1 www.google.com >> /dev/null 2&>1") == 0 :
	print "Sending Email Notification"
	msg = MIMEText('Someone is ringing the doorbell! \nGo Check!')
	msg['Subject'] = 'Doorbell notification!'
	msg['From'] = USERNAME
	msg['To'] = MAILTO

	server = smtplib.SMTP('smtp.gmail.com:587')
	server.ehlo_or_helo_if_needed()
	server.starttls()
	server.ehlo_or_helo_if_needed()
	server.login(USERNAME,PASSWORD)
	server.sendmail(USERNAME, MAILTO, msg.as_string())
	server.quit()
else:
	print "Unable to connect to Internet."

To have this run upon booting, we need to edit /etc/rc.local (as root since this is the owner).

sudo nano /etc/rc.local

At the bottom, just above exit 0 we’ll add a call to our script.

python /home/pi/Scripts/Smart_DoorBell/WaitDoorbell.py 

Memory – Card game

I have been spending quite some time learning python for the past few months from text processing to game development using simplegui. And I must say for a person that always thought in C, I find Python completely straight forward and an easy but high level language.
As my journey to Python continues, This weeks project was a memory game.
Although it does have a couple of bugs in it, for instance keeping the high score and and and…

# 'Introduction to Interactive Programming in Python' Course
# RICE University - https://class.coursera.org/interactivepython-005
# by Joe Warren, John Greiner, Stephen Wong, Scott Rixner
# Mini-project for this week is the implementation of a card game - Memory.
#
# Concentration, also known as Memory, 
# is a card game in which all of the cards are laid face down on a surface 
# and two cards are flipped face up over each turn. 
# The object of the game is to turn over pairs of matching cards. 
# Concentration can be played with any number of players or as solitaire
# and is an especially good game for young children, 
# though adults may find it challenging and stimulating as well

__author__ = "Mpho Mphego"
__version__ = "$Revision: 1.14 $"
__date__ = "$Date: 2014/10/25 21:57 $"
__copyright__ = "Copyright (c) 2014 Mpho Mphego"
__url__ = "http://www.codeskulptor.org/#user38_QZuBkN6aVN_14.py"
__license__ = "Python"

try:
    import simplegui
except:
    import SimpleGUICS2Pygame.simpleguics2pygame as simplegui
    # To run simplegui in idle python, install SimpleGUICS2Pygame module
    # download module : https://pypi.python.org/pypi/SimpleGUICS2Pygame
import random


def init():
    """ Initializing Global variables"""
    global range_of_cards, exposed, exited, count, Turns, reveal_card
    global click_counter, game_won, restart_game, restart_count, counter#, Best_turn, Best_time
    counter = 0
    Turns = 0
    click_counter = 0
    count = 0
    restart_count = 0
    exited = False
    game_won = False
    restart_game = False
    

def new_game():
    global range_of_cards, exposed, exited, count, Turns, reveal_card
    global click_counter, game_won, restart_game, restart_count, counter, Best_turn, Best_time
    
    init()
    range_of_cards = [ i for i in range(8)] + [ i for i in range(8)]
    random.shuffle(range_of_cards)
    """Cheat"""
    #print range_of_cards
    exposed = [False for i in range(16)]
    reveal_card = list()
    timer.stop()
    """Game still needs some work on keeping the hi score and best time"""
    Best_turn = "0"
    Best_time = "0:00.0"
    
def mouseclick(pos):
    global click_counter, exposed, Turns, game_won
  
    if click_counter == 0:
        reveal_card.append(pos[0]//50)
        exposed[pos[0]//50] = True
        click_counter += 1
        Turns = 1
        timer.start()
        
    elif click_counter == 1:
        if not (pos[0]//50 in reveal_card):
            reveal_card.append(pos[0]//50)
            click_counter = 2
        
        exposed[pos[0]//50] = True
        
    else:
        if not (pos[0]//50 in reveal_card):
            if range_of_cards[reveal_card[-1]]!=range_of_cards[reveal_card[-2]]:
                exposed[reveal_card[-1]]=False
                exposed[reveal_card[-2]]=False
                reveal_card.pop()
                reveal_card.pop()
            click_counter = 1
            Turns += 1
            exposed[pos[0]//50] = True
            reveal_card.append(pos[0]//50)
      
    if exposed == [True for i in range(16)]:
        game_won = True
        #counter = 0
        timer.stop()
    else:
        counter = 0
        timer.start()

    return Turns 
    
def draw(canvas):
    global range_of_cards, exposed, exited, count, Turns
    global click_counter, game_won, restart_game, restart_count, newgame
    global counter, Best_turn, Best_time
    
    label.set_text("Turns = " + str(Turns))
    if exited == False:
        label2.set_text("Timer: " + format(counter))
#    if Best_turn > str(Turns):
#        label3.set_text("High Score = " + str(Best_turn))
#        label4.set_text("Best Time: " + str(Best_time))
                
    for i in range(16):        
        canvas.draw_polyline([[50*(i%50 + .5) , 80], [50*(i%50 + .5) , 170], [50*(i%50 + .5), 100]], 45, 'White')            
        canvas.draw_text("Time:" + format(counter), (550, 50), 50, 'Black')
        canvas.draw_text("No. of Turns: " + str(Turns), (10, 50), 50, 'Black')
        
        if exposed[i]:
            canvas.draw_polyline([[50*(i%50 + .5) , 80], [50*(i%50 + .5) , 170], [50*(i%50 + .5), 100]], 45, 'Green')            
            canvas.draw_text(str(range_of_cards[i]), (50*(i%50 + .2), 140), 54, 'White', 'monospace')    
            if exposed == [True for i in range(16)]:
                game_won = True
                
    #if counter > 4 :game_won = True;timer.stop() #debugging
    if game_won == True:
        for i in range(16):
            restart_count += 1
            canvas.draw_polyline([[50*i , 0], [50*(i+1) , 120], [50*i, 100]], 200, 'White')                       
            canvas.draw_text("Congradulations!!!", (185, 60), 35, 'Black', 'monospace')
            canvas.draw_text("You won the game", (210, 90), 35, 'Black', 'monospace')
            canvas.draw_text("In " + str(Turns) + " Turns.", (250, 120), 35, 'Black', 'monospace')
            canvas.draw_text("Time: " + format(counter), (225, 150), 35, 'Black', 'monospace')
            canvas.draw_text(str(__copyright__), (615, 175), 12, 'Red', 'sans-serif')
            game_won = False
            Best_time = format(counter)
            Best_turn = str(Turns)
            
            if restart_count > 10000:
                new_game()
                restart_count = 0                               
        
    if exited == True:
        #print timer.is_running()
        if timer.is_running() == False: timer.start()
            
        for i in range(16):
            canvas.draw_polyline([[50*i , 0], [50*(i+1) , 120], [50*i, 100]], 200, 'White')                       
            canvas.draw_text("Game created by " + str(__author__), (30, 55), 45, 'Black', 'monospace')
            canvas.draw_text("Thank you for playing", (100, 100), 45, 'Black', 'monospace')
            canvas.draw_text("Please wait...", (250, 150), 35, 'Red', 'monospace')
        
        #print "Please wait ", counter # debugging
        if counter > 30: 
            frame.stop()
            timer.stop()
    #print Best_turn
    #print Best_time
                               
def tick():
    global counter
    counter += 1
    #print counter
    
def format(value):
    A=value//600
    B=((value//10)%60)//10
    C=((value//10)%60)%10
    D=value%10
    #print str(A)+":" + str(B) +str(C)+ "."+str(D)
    return str(A)+":" + str(B) +str(C)+ "."+str(D)
   
def exit_game():
    global exited, count
    exited = True    
     
# create frame and add a button and labels
frame = simplegui.create_frame("Memory", 800, 180)
timer = simplegui.create_timer(100, tick)
frame.set_canvas_background('Green')
frame.add_button("Restart Game", new_game)
frame.add_button("Exit Game", exit_game)
label = frame.add_label("Turns = 0")
label2 = frame.add_label("Time: 0:00.0")
label3 = frame.add_label("High Score = 0")
label4 = frame.add_label("Best Time: 0:00.0")

# register event handlers
frame.set_mouseclick_handler(mouseclick)
frame.set_draw_handler(draw)
#frame.set_draw_handler(draw_handler)
# get things rolling
new_game()
frame.start()
#timer.start()

# Always remember to review the grading rubric

script to generate BASH scripts

I create scripts on a day to day basis, and I eventually got bored of manually creating scripts from initial phase everytime, hence this master of scripts.

#!/bin/bash
# BASH script to generate BASH scripts
# Created by Mpho Mphego
 
echo "What do you want to call your BASH script?"
read RESPONSE
 
echo "#!/bin/bash" | tee ~/tmp/$RESPONSE.sh
echo "# Filename:$RESPONSE.sh" | tee -a ~/tmp/$RESPONSE.sh
echo "# The purpose of this script is to ..." | tee -a ~/tmp/$RESPONSE.sh
echo "# Script written by Mpho Mphego Marion Island SANSA Engineer 2014-2015" | tee -a ~/tmp/$RESPONSE.sh
 
sudo chmod +x ~/tmp/$RESPONSE.sh
 
sudo ln -s ~/tmp/$RESPONSE.sh ~/bin/$RESPONSE.sh
sudo chmod +x ~/bin/$RESPONSE.sh
 
gedit ~/tmp/$RESPONSE.sh

1972 Pong “python”

That feeling when, your program compiles and runs like it should…
So last week, was a pong game challenge, need I say more.
Here goes nothing.

#http://www.codeskulptor.org/#user38_5WtyGnsPgb_0.py
# Implementation of classic arcade game Pong
try:
    import simplegui
except:
    import SimpleGUICS2Pygame.simpleguics2pygame as simplegui
    # To run simplegui in idle python, install SimpleGUICS2Pygame module
    # download module : https://pypi.python.org/pypi/SimpleGUICS2Pygame

import random

# initialize globals - pos and vel encode vertical info for paddles
WIDTH = 600
HEIGHT = 400       
BALL_RADIUS = 20
PAD_WIDTH = 8
PAD_HEIGHT = 80
HALF_PAD_WIDTH = PAD_WIDTH / 2
HALF_PAD_HEIGHT = PAD_HEIGHT / 2
LEFT = False
RIGHT = True
paddle1_vel, paddle2_vel = 0,0

paddle1_pos = [WIDTH - HALF_PAD_WIDTH,  HEIGHT / 2]#,[WIDTH - PAD_WIDTH, HEIGHT]#[WIDTH / 2, HEIGHT / 2]
paddle2_pos = [HALF_PAD_WIDTH, HEIGHT / 2]

ball_pos = [WIDTH / 2, HEIGHT / 2]
# initialize ball_pos and ball_vel for new bal in middle of table
# if direction is RIGHT, the ball's velocity is upper right, else upper left
def spawn_ball(direction):
    global ball_pos, ball_vel # these are vectors stored as lists


# define event handlers
def new_game():
    global paddle1_pos, paddle2_pos, paddle1_vel, paddle2_vel  # these are numbers
    global score1, score2  # these are ints
   


 
def draw(canvas):
    global score1, score2, paddle1_pos, paddle2_pos, ball_pos, ball_vel, paddle1_vel, paddle2_vel
        
    # draw mid line and gutters
    canvas.draw_line([WIDTH / 2, 0],[WIDTH / 2, HEIGHT], 1, "White")
    canvas.draw_line([PAD_WIDTH, 0],[PAD_WIDTH, HEIGHT], 1, "White")
    canvas.draw_line([WIDTH - PAD_WIDTH, 0],[WIDTH - PAD_WIDTH, HEIGHT], 1, "White")

    # update ball
            
    # draw ball
    canvas.draw_circle(ball_pos, BALL_RADIUS, 2, "Red", "White")

    """Limit keeps the paddle on the screen"""
    if paddle1_pos[1] < HALF_PAD_HEIGHT:
        paddle1_pos[1] = HALF_PAD_HEIGHT
        paddle1_vel = 0
    elif paddle1_pos[1] > HEIGHT - HALF_PAD_HEIGHT:
        paddle1_pos[1] = HEIGHT - HALF_PAD_HEIGHT
        paddle1_vel = 0
    if paddle2_pos[1] < HALF_PAD_HEIGHT:
        paddle2_pos[1] = HALF_PAD_HEIGHT
        paddle2_vel = 0
    elif paddle2_pos[1] > HEIGHT - HALF_PAD_HEIGHT:
        paddle2_pos[1] = HEIGHT - HALF_PAD_HEIGHT
        paddle2_vel = 0
        
    """Drawing Paddles"""
    paddle1_pos[1] += paddle1_vel
    paddle2_pos[1] += paddle2_vel
    
    paddle1_top =  [paddle1_pos[0], paddle1_pos[1] - HALF_PAD_HEIGHT]
    paddle1_bot =  [paddle1_pos[0], paddle1_pos[1] + HALF_PAD_HEIGHT]
    canvas.draw_line(paddle1_top, paddle1_bot, PAD_WIDTH, "White")

    paddle2_top =  [paddle2_pos[0], paddle2_pos[1] - HALF_PAD_HEIGHT]
    paddle2_bot =  [paddle2_pos[0], paddle2_pos[1] + HALF_PAD_HEIGHT]
    canvas.draw_line(paddle2_top ,paddle2_bot, PAD_WIDTH, "White")

    # draw scores
        
def keydown(key):
    global paddle1_vel, paddle2_vel
    acc = 5
    if key == simplegui.KEY_MAP["up"]:
        paddle1_vel -= acc
    if key == simplegui.KEY_MAP["down"]:
        paddle1_vel += acc
    if key == simplegui.KEY_MAP["w"]:
        paddle2_vel -= acc 
    if key == simplegui.KEY_MAP["s"]:
        paddle2_vel += acc
   
def keyup(key):
    global paddle1_vel, paddle2_vel
    acc = 0
    if key == simplegui.KEY_MAP["up"]:
        paddle1_vel = acc
    if key == simplegui.KEY_MAP["down"]:
        paddle1_vel = acc
    if key == simplegui.KEY_MAP["w"]:
        paddle2_vel = acc 
    if key == simplegui.KEY_MAP["s"]:
        paddle2_vel = acc

def exit_button():
    frame.stop()

# create frame
frame = simplegui.create_frame("Pong", WIDTH, HEIGHT)
frame.set_draw_handler(draw)
frame.set_keydown_handler(keydown)
frame.set_keyup_handler(keyup)
frame.add_button('Exit', exit_button, 50)

# start frame
new_game()
frame.start()