Raspberry pi based MQTT video doorbell

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()
6 Likes

A bit extravagant, but a nice project.:thumbsup:

How are you managing your whole home audio-server?
I’m currently running a plex mediaserver and some airplay receivers.

Greets
Jacko

thx. I use a combination approach. Plex, Xbox-One, iOS and misc devices. Airplay is handled via AirFoil servers and receivers. All driven through a win2k12 server (like the MS SAPI for TTS) piped through a XAP 800 and multizone amp.

This looks really cool! How is it working now, some months on? What’s reliability like?
What does the visitor see on the display?