A DIY video doorbell, made from a raspberry pi 3, 1.8” tft, pi camera, and a pushbutton. Not wanting to spend $100’s of dollars on a video doorbell, I decided to make my own. I have a decent setup tied into Home-Assistant. When the doorbell is pushed, a video feed is activated, along with a “pub” sent out to the MQTT broker. HA picks this change up and I also have a separate python script running on my “whole-home audio” media server that fires a doorbell wav along with some python TTS announcing “Someone is at the front door”.
doorbell.py (ran on the pi)
import os
import sys
import time
import RPi.GPIO as GPIO
import pygame
import logging
import logging.handlers
import argparse
import paho.mqtt.client as mqtt
time_stamp_prev=0
GPIO.setmode(GPIO.BCM)
GPIO.setup(26, GPIO.IN, pull_up_down=GPIO.PUD_UP)
os.environ["SDL_FBDEV"] = "/dev/fb1"
os.environ['SDL_VIDEODRIVER']="fbcon"
# Deafults
LOG_FILENAME = "/tmp/doorbell.log"
LOG_LEVEL = logging.INFO # Could be e.g. "DEBUG" or "WARNING"
# Define and parse command line arguments
parser = argparse.ArgumentParser(description="Doorbell Service")
parser.add_argument("-l", "--log", help="file to write log to (default '" + LOG_FILENAME + "')")
# If the log file is specified on the command line then override the default
args = parser.parse_args()
if args.log:
LOG_FILENAME = args.log
# Configure logging to log to a file, making a new file at midnight and keeping the last 3 day's data
# Give the logger a unique name (good practice)
logger = logging.getLogger(__name__)
# Set the log level to LOG_LEVEL
logger.setLevel(LOG_LEVEL)
# Make a handler that writes to a file, making a new file at midnight and keeping 3 backups
handler = logging.handlers.TimedRotatingFileHandler(LOG_FILENAME, when="midnight", backupCount=3)
# Format each log message like this
formatter = logging.Formatter('%(asctime)s %(levelname)-8s %(message)s')
# Attach the formatter to the handler
handler.setFormatter(formatter)
# Attach the handler to the logger
logger.addHandler(handler)
# Make a class we can use to capture stdout and sterr in the log
class MyLogger(object):
def __init__(self, logger, level):
"""Needs a logger and a logger level."""
self.logger = logger
self.level = level
def write(self, message):
# Only log if there is a message (not just a new line)
if message.rstrip() != "":
self.logger.log(self.level, message.rstrip())
# Replace stdout with logging to file at INFO level
sys.stdout = MyLogger(logger, logging.INFO)
# Replace stderr with logging to file at ERROR level
sys.stderr = MyLogger(logger, logging.ERROR)
def displaytext(text,size,line,color,clearscreen):
if clearscreen:
screen.fill((255,255,255))
font = pygame.font.Font(None,size)
text = font.render(text,0,color)
rotated = pygame.transform.rotate(text,0)
textpos = rotated.get_rect()
textpos.centery = 80
if line == 1:
textpos.centerx = 99
screen.blit(rotated,textpos)
elif line == 2:
textpos.centerx = 61
screen.blit(rotated,textpos)
elif line == 3:
textpos.centerx = 25
screen.blit(rotated,textpos)
def main():
global screen
pygame.init()
pygame.mouse.set_visible(0)
size = width,height = 160,128
screen = pygame.display.set_mode(size)
while True:
#displaytext(time.strftime("%d.%m.%Y"),40,1,(0,0,0),True)
displaytext(time.strftime("%H:%M:%S"),40,2,(0,0,0),True)
#displaytext("www.gtkdb.de",20,3,(0,0,0),False)
pygame.display.flip()
time.sleep(1)
input_state = GPIO.input(26)
if input_state == False:
print('Button Pressed')
mqttc = mqtt.Client("piDoorbell")
mqttc.connect(“x.x.x.x”, 1883)
mqttc.publish("home/core/doorbell", "ON")
mqttc.loop(2) #timeout = 2s
if __name__ == '__main__':
main()
doorbell_responder.py (ran on the audio server)
import paho.mqtt.client as mqtt
import json, time
import pyttsx
import pyglet
import logging
LOG_FILENAME = 'doorbell_logs.out'
logging.basicConfig(filename=LOG_FILENAME,
level=logging.DEBUG,
)
music = pyglet.media.load('doorbell.wav', streaming=False)
player = pyglet.media.Player()
# --------------- #
# Callback events #
# --------------- #
# connection event
def on_connect(client, data, flags, rc):
logging.debug('Connected, rc: ' + str(rc))
# subscription event
def on_subscribe(client, userdata, mid, gqos):
logging.debug('Subscribed: ' + str(mid))
# received message event
def on_message(client, obj, msg):
# get the message
data = msg.payload
player.queue(music)
player.play()
time.sleep(2)
engine = pyttsx.init()
voices = engine.getProperty('voices')
engine.setProperty('voice', voices[2].id)
engine.say("Someone is at the front door")
engine.runAndWait()
# ------------- #
# MQTT settings #
# ------------- #
# create the MQTT client
client = mqtt.Client("piDoorbell-chime")
# assign event callbacks
client.on_message = on_message
client.on_connect = on_connect
client.on_subscribe = on_subscribe
# device topics
in_topic = 'home/core/doorbell' # receiving messages
# client connection
client.connect(“x.x.x.x”, 1883) # MQTT server address
client.subscribe(in_topic, 0) # MQTT subscribtion (with QoS level 0)
# Continue the network loop, exit when an error occurs
client.loop_forever()