Jerry Zhang
Created February 28, 2018

Home Security System

Ask Alexa if someone's at your front door!

111

Things used in this project

Hardware components

Raspberry Pi 3 Model B
Raspberry Pi 3 Model B
×1
Walabot
Walabot
Any of the three Maker models will work; I used the Walabot Creator.
×1
USB-A to Micro-USB Cable
USB-A to Micro-USB Cable
×1

Software apps and online services

AWS IoT
Amazon Web Services AWS IoT
Used for collecting data from Walabot
AWS Lambda
Amazon Web Services AWS Lambda
Used to run the Alexa Skill

Story

Read more

Code

walabot.py

Python
Code to run on the Raspberry Pi. Credits go to sample code from AWS and Walabot.
from __future__ import print_function
import os
import sys
from sys import platform
from os import system
import WalabotAPI as wlbt
import AWSIoTPythonSDK
sys.path.insert(0, os.path.dirname(AWSIoTPythonSDK.__file__))
from AWSIoTPythonSDK.MQTTLib import AWSIoTMQTTClient

import random
import logging
import time
import getopt
import datetime

# Custom MQTT message callback
def customCallback(client, userdata, message):
	print("Received a new message: ")
	print(message.payload)
	print("from topic: ")
	print(message.topic)
	print("--------------\n\n")

# Usage
usageInfo = """Usage:

Use certificate based mutual authentication:
python basicPubSub.py -e <endpoint> -r <rootCAFilePath> -c <certFilePath> -k <privateKeyFilePath>

Use MQTT over WebSocket:
python basicPubSub.py -e <endpoint> -r <rootCAFilePath> -w

Type "python basicPubSub.py -h" for available options.
"""
# Help info
helpInfo = """-e, --endpoint
	Your AWS IoT custom endpoint
-r, --rootCA
	Root CA file path
-c, --cert
	Certificate file path
-k, --key
	Private key file path
-w, --websocket
	Use MQTT over WebSocket
-h, --help
	Help information
"""

# Read in command-line parameters
useWebsocket = False
host = ""
rootCAPath = ""
certificatePath = ""
privateKeyPath = ""
try:
	opts, args = getopt.getopt(sys.argv[1:], "hwe:k:c:r:", ["help", "endpoint=", "key=","cert=","rootCA=", "websocket"])
	if len(opts) == 0:
		raise getopt.GetoptError("No input parameters!")
	for opt, arg in opts:
		if opt in ("-h", "--help"):
			print(helpInfo)
			exit(0)
		if opt in ("-e", "--endpoint"):
			host = arg
		if opt in ("-r", "--rootCA"):
			rootCAPath = arg
		if opt in ("-c", "--cert"):
			certificatePath = arg
		if opt in ("-k", "--key"):
			privateKeyPath = arg
		if opt in ("-w", "--websocket"):
			useWebsocket = True
except getopt.GetoptError:
	print(usageInfo)
	exit(1)

# Missing configuration notification
missingConfiguration = False
if not host:
	print("Missing '-e' or '--endpoint'")
	missingConfiguration = True
if not rootCAPath:
	print("Missing '-r' or '--rootCA'")
	missingConfiguration = True
if not useWebsocket:
	if not certificatePath:
		print("Missing '-c' or '--cert'")
		missingConfiguration = True
	if not privateKeyPath:
		print("Missing '-k' or '--key'")
		missingConfiguration = True
if missingConfiguration:
	exit(2)

# Configure logging
logger = None
if sys.version_info[0] == 3:
	logger = logging.getLogger("core")
else:
	logger = logging.getLogger("AWSIoTPythonSDK.core")  # Python 2
logger.setLevel(logging.DEBUG)
streamHandler = logging.StreamHandler()
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
streamHandler.setFormatter(formatter)
logger.addHandler(streamHandler)

# Init AWSIoTMQTTClient
myAWSIoTMQTTClient = None
if useWebsocket:
	myAWSIoTMQTTClient = AWSIoTMQTTClient("basicPubSub", useWebsocket=True)
	myAWSIoTMQTTClient.configureEndpoint(host, 443)
	myAWSIoTMQTTClient.configureCredentials(rootCAPath)
else:
	myAWSIoTMQTTClient = AWSIoTMQTTClient("basicPubSub")
	myAWSIoTMQTTClient.configureEndpoint(host, 8883)
	myAWSIoTMQTTClient.configureCredentials(rootCAPath, privateKeyPath, certificatePath)

myAWSIoTMQTTClient.configureAutoReconnectBackoffTime(1, 32, 20)
myAWSIoTMQTTClient.configureOfflinePublishQueueing(-1)
myAWSIoTMQTTClient.configureDrainingFrequency(2)
myAWSIoTMQTTClient.configureConnectDisconnectTimeout(10)
myAWSIoTMQTTClient.configureMQTTOperationTimeout(5)

myAWSIoTMQTTClient.connect()

# Publish to the topic in a loop
loopCount = 0
command_str = ''
topic = "frontdoor"
delay_s = 5
sensor_sn = 'dev_r00000002'

R_MIN, R_MAX, R_RES = 20, 600, 8
T_MIN, T_MAX, T_RES = -15, 15, 10
P_MIN, P_MAX, P_RES = -25, 25, 8
SLICES = [R_MAX * 0.2, R_MAX * 0.4, R_MAX * 0.6, R_MAX * 0.8]
D_FACTOR = 5  # should be changed according to environment
C_MIN = 0
C_MAX = D_FACTOR * 2

counters = [0, 0, 0, 0]


def distance(t):
    return (t.yPosCm ** 2 + t.zPosCm ** 2) ** 0.5


wlbt.Init()
wlbt.SetSettingsFolder()
wlbt.ConnectAny()
wlbt.SetProfile(wlbt.PROF_SENSOR)
wlbt.SetArenaR(R_MIN, R_MAX, R_RES)
wlbt.SetArenaTheta(T_MIN, T_MAX, T_RES)
wlbt.SetArenaPhi(P_MIN, P_MAX, P_RES)
wlbt.SetThreshold(100)
wlbt.SetDynamicImageFilter(wlbt.FILTER_TYPE_MTI)
wlbt.Start()

lastTime = 0
outputLastTime = 0

while True:
    wlbt.Trigger()
    targets = wlbt.GetSensorTargets()
    for t in targets:
        pass
    system('cls' if platform == 'win32' else 'clear')  # clear the terminal
    for i, s in enumerate(SLICES):
        targetsBelow = [t for t in targets if distance(t) < s]
        if targetsBelow:
            counters[i] = min(counters[i] + 1, C_MAX)
            targets = [t for t in targets if t not in targetsBelow]
        else:
            counters[i] -= 0.5 if counters[i] > 0 else 1
            counters[i] = max(counters[i], C_MIN)
    validSlices = [i for (i, a) in enumerate(counters) if a > D_FACTOR]
    print(counters)
    if not validSlices:
        output = "No targets"
        print(output)
    for i in validSlices:
        if i is 0:
            output = "People below {} cm".format(SLICES[i])
            print(output)
        else:
            output = "People between {} cm and {} cm".format(
                SLICES[i-1], SLICES[i]
            )
            print(output)
    currentTime = time.time()
    if (currentTime > lastTime + 5) and not(output == outputLastTime):
        msg = '"S": "{}"'.format(output)
        msg = '{'+msg+'}'
        myAWSIoTMQTTClient.publish(topic, msg, 0)
        lastTime = time.time()
        outputLastTime = output
        
wlbt.Stop()
myAWSIoTMQTTClient.disconnect()
wlbt.Disconnect()

index.js

JavaScript
The code for the Lambda function to power the Alexa skill.
'use strict';
const Alexa = require('alexa-sdk');

const AWS = require('aws-sdk');

const APP_ID = undefined;

const SKILL_NAME = 'Front Door Indicator';
const GET_INFO_MESSAGE = "Here's the status of your front door: ";
const HELP_MESSAGE = 'You can say is there someone at my front door, or, you can say exit... What can I help you with?';
const HELP_REPROMPT = 'What can I help you with?';
const STOP_MESSAGE = 'See you next time!';

var globalPayload;
const params = {
	TableName: 'RaspPi3',
	
	Key: {
		"device_id": {
			S: "frontdoor"
		},
		"timestamp": {
			S: "Number"
		}
	},
};
const AWSregion = 'us-east-1';

AWS.config.update({
	region: AWSregion
});

exports.handler = function(event, context, callback) {
//	console.log("hello");
//	let scanningParameters = {
//		TableName: 'RaspPi3',
//		Limit: 1
//	};
//	docClient.scan(scanningParameters, function(err, data) {
//		if (err) {
//			console.log("err" + err);
//			callback(err, null);
//		} else {
//			console.log("no error");
//			globalPayload = data;
//			callback(null, data);
//		}
//	});
	
    var alexa = Alexa.handler(event, context);
    alexa.appId = APP_ID;
    alexa.registerHandlers(handlers);
    alexa.execute();
};

const handlers = {
    'LaunchRequest': function () {
        this.emit('GetInfoIntent');
    },
    'GetInfoIntent': function () {
		readDynamoItem(params, result=>{
			var info = '';
			info = result;
			const speechOutput = GET_INFO_MESSAGE + info;

        	this.response.cardRenderer(SKILL_NAME, info);
			this.response.speak(speechOutput);
			this.emit(':responseReady');
		});
    },
    'AMAZON.HelpIntent': function () {
        const speechOutput = HELP_MESSAGE;
        const reprompt = HELP_REPROMPT;

        this.response.speak(speechOutput).listen(reprompt);
        this.emit(':responseReady');
    },
    'AMAZON.CancelIntent': function () {
        this.response.speak(STOP_MESSAGE);
        this.emit(':responseReady');
    },
    'AMAZON.StopIntent': function () {
        this.response.speak(STOP_MESSAGE);
        this.emit(':responseReady');
    },
};

function readDynamoItem(params, callback) {
	var AWS = require('aws-sdk');
	AWS.config.update({region: AWSregion});
	var docClient = new AWS.DynamoDB.DocumentClient();
	console.log('reading item');
	docClient.get(params, (err, data) => {
		if (err) {
			console.error("ERROR: ", err);
		} else {
			console.log("Success: ", JSON.stringify(data, null, 2));
			callback(data.Item.message);
		}
	});
}

Credits

Jerry Zhang

Jerry Zhang

5 projects • 9 followers

Comments