Skip to content

Instantly share code, notes, and snippets.

@aclud
Created February 29, 2024 20:09
Show Gist options
  • Save aclud/e70ef2acc48521c8a0e723f54232cb8b to your computer and use it in GitHub Desktop.
Save aclud/e70ef2acc48521c8a0e723f54232cb8b to your computer and use it in GitHub Desktop.
Alexa garage control skill + raspberry pi script
Intent schema:
{
"intents": [
{
"intent": "GetGarageState"
},
{
"intent": "GetTemp"
},
{
"intent": "GarageAction",
"slots": [
{
"name": "Action",
"type": "LITERAL"
}
]
}
]
}
Sample utterances:
GetGarageState state
GetGarageState door state
GetGarageState status
GetGarageState door status
GarageAction {open|Action}
GarageAction to {open|Action}
GarageAction {close|Action}
GarageAction to {close|Action}
GarageAction {shut|Action}
GarageAction to {shut|Action}
GetTemp for the temp
GetTemp for the temperature
GetTemp the temp
GetTemp the temperature
GetTemp temp
GetTemp temperature
#!/usr/bin/python
from bottle import route, run, template, error, request, static_file, post, get
import time, datetime, logging
from logging.handlers import RotatingFileHandler
import threading
import sys
import RPi.GPIO as io
import Adafruit_DHT as ada
try:
io.setmode(io.BCM)
io.setwarnings(False)
#key
myKey = 'blahh'
# Define io to use on Pi
io_temp = 24
#io_PIR = 17
#io_LED = 21
io_DOORSWITCH = 22
#io_LIGHTSENSOR = 6
io_GARAGEREMOTE1 = 19
intDoorCloseTime = 30
# Set pin in/out
#io.setup(io_PIR, io.IN)
#io.setup(io_LED, io.OUT, initial=0)
io.setup(io_DOORSWITCH, io.IN, pull_up_down=io.PUD_UP)
io.setup(io_GARAGEREMOTE1, io.OUT, initial=0)
# logging
enableConsoleDebug = True # set to true to write output to console
logFileName = '/home/pi/gsvc/gweb.log'
fmtDateTime = '%Y-%m-%d %H:%M:%S'
# Create logging object
log = logging.getLogger('gweb')
logFormatter = logging.Formatter("%(asctime)s [%(threadName)-12.12s] [%(levelname)-5.5s] %(message)s")
log.setLevel(logging.DEBUG)
# Add the log message handler to the logger
handler = logging.handlers.RotatingFileHandler(logFileName, maxBytes=1024000, backupCount=5)
handler.setFormatter(logFormatter)
log.addHandler(handler)
# Write all the messages out to console for debugging purposes
if enableConsoleDebug:
consoleHandler = logging.StreamHandler()
consoleHandler.setFormatter(logFormatter)
log.addHandler(consoleHandler)
# Function to return formatted date time
def fnDateTimeNow():
global dttmnow
dttmnow = datetime.datetime.now().strftime(fmtDateTime)
return dttmnow
# Function to control garage door. action = open/close, pin = pin # door connected to, source used for debugging
def fnGarageControl(action, doorswitchpin, remotepin):
#log.debug("in fn1 door switch pin: "+str(doorswitchpin)+", remote pin: "+str(remotepin)+", action: "+action+".")
intTryCount = 0
intDoorState = io.input(doorswitchpin)
intDoorStatePrev = io.input(doorswitchpin)
while intDoorState == intDoorStatePrev and intTryCount < 3:
intTryCount += 1
intDoorState = io.input(doorswitchpin)
if action == "open":
if intDoorState:
log.info("door is already open...")
return "already open"
elif not intDoorState:
log.info("opening door...")
io.setup(remotepin, io.OUT)
io.output(remotepin, io.HIGH)
time.sleep(2)
io.output(remotepin, io.LOW)
time.sleep(intDoorCloseTime)
#if not intDoorState:
#send_ifttt(BASE_URL, "garage1wrong", KEY)
#log.warning("something went wrong opening the door, pausing script for " +
# str(intSleeptimeMins) + " minutes")
#time.sleep(intSleeptimeMins * 60)
if action == "close":
if not intDoorState:
log.info("door is already closed...")
return "already closed"
elif intDoorState:
log.info("closing door...")
io.setup(remotepin, io.OUT)
io.output(remotepin, io.HIGH)
time.sleep(2)
io.output(remotepin, io.LOW)
time.sleep(intDoorCloseTime) # give the door time to close
intDoorState = io.input(doorswitchpin)
#if intDoorState:
#send_ifttt(BASE_URL, "garage1wrong", KEY)
#log.warning("something went wrong closing the door, pausing script for " +
# str(intSleeptimeMins) + " minutes.")
#time.sleep(intSleeptimeMins * 60)
# Refresh door state for while loop
intDoorState = io.input(doorswitchpin)
if intTryCount == 3:
log.error("Error occurred changing door state to "+action+".")
# Log a startup message
log.info("Script startup.")
@error(404)
def error404(error):
return ('404')
@route('/garage/toggle/<pin:int>/<seconds:int>/<key>')
def toggle(pin, seconds, key):
try:
if key == myKey:
remote_ip = request.environ.get('REMOTE_ADDR')
log.info("IP: %s is toggling pin %s for %s seconds." % (remote_ip, pin, seconds))
io.setup(pin, io.OUT)
io.output(pin, io.HIGH)
time.sleep(seconds)
io.output(pin, io.LOW)
return {'pin':pin, 'seconds':seconds, 'success':True}
else:
log.info("IP: %s is attempting to toggle pin %s for %s seconds without a valid key." % (remote_ip, pin, seconds))
return {'pin':'no_key', 'seconds':'no_key', 'success':False}
except:
return {'pin':pin, 'seconds':seconds, 'success':False}
@route('/garage/status/<pin:int>/<key>')
def status(pin, key):
try:
if key == myKey:
remote_ip = request.environ.get('REMOTE_ADDR')
if remote_ip != '127.0.0.1':
log.info("IP: %s is requesting status of pin %s." % (remote_ip, pin))
return {'pin':pin, 'status':io.input(pin), 'success':True}
else:
log.error("IP: %s is requesting status of pin %s without a valid key." % (remote_ip, pin))
return {'pin':pin, 'success':False}
except:
return {'pin':pin, 'success':False}
@route('/garage/<action:re:open|close>/<key>')
def openClose(action, key):
try:
remote_ip = request.environ.get('REMOTE_ADDR')
if key == myKey:
log.info("IP: %s is requesting to %s the garage." % (remote_ip, action))
garageCurrentState = io.input(io_DOORSWITCH) #0=closed, 1=open
if not garageCurrentState and action == 'open':
log.info("IP: %s opened the garage." % (remote_ip))
#fnGarageControl("open", io_DOORSWITCH, io_GARAGEREMOTE1)
thr = threading.Thread(target=fnGarageControl, args=("open", io_DOORSWITCH, io_GARAGEREMOTE1), kwargs={})
thr.start()
return {'response':'Opening the garage'}
elif garageCurrentState and action == 'open':
log.info("IP: %s garage is already open." % (remote_ip))
return {'response':'the garage is already open.'}
elif garageCurrentState and action == 'close':
log.info("IP: %s %s the garage." % (remote_ip, action))
#fnGarageControl("close", io_DOORSWITCH, io_GARAGEREMOTE1)
thr = threading.Thread(target=fnGarageControl, args=("close", io_DOORSWITCH, io_GARAGEREMOTE1), kwargs={})
thr.start()
return {'response':'Closing the garage'}
elif not garageCurrentState and action == 'close':
log.info("IP: %s garage is already closed." % (remote_ip))
return {'response':'the garage is already closed.'}
else:
log.error("IP: %s attempted to %s the garage with an invalid key." % (remote_ip, action))
return 'no key'
except:
return {'action':action, 'success':False}
@route('/garage/temp/')
def gettemp():
remote_ip = request.environ.get('REMOTE_ADDR')
humidity, temperature = ada.read_retry(ada.AM2302, io_temp)
temperature = temperature * 9/5.0 + 32
if humidity is not None and temperature is not None:
temperature = '{:.1f} degrees'.format(temperature)
humidity = '{:.1f}%'.format(humidity)
log.info("IP: %s is requesting temp data (%s, %s humidity)." % (remote_ip, temperature, humidity))
return {'temp':temperature, 'humidity':humidity}
else:
#print('Failed to get reading. Try again!')
log.error("IP: %s failed to retrieve temp data." % (remote_ip))
return {'temp':unknown, 'humidity':unknown}
run(host='0.0.0.0', port=8412, reloader=True)
except Exception:
exc_type, exc_obj, exc_tb = sys.exc_info()
log.error("Caught Exception at line " + str(exc_tb.tb_lineno) + ": " + str(sys.exc_info()))
# todo: send alert or email w/ detail before dying
pass
finally:
log.info("Script exit")
io.cleanup()
var http = require('http');
var url = '';
// Route the incoming request based on type (LaunchRequest, IntentRequest,
// etc.) The JSON body of the request is provided in the event parameter.
exports.handler = function (event, context) {
try {
console.log("event.session.application.applicationId=" + event.session.application.applicationId);
/**
* Uncomment this if statement and replace application.id with yours
* to prevent other voice applications from using this function.
*/
if (event.session.application.applicationId !== "amzn1.ask.skill.blah") {
context.fail("Invalid Application ID");
}
if (event.session.new) {
onSessionStarted({requestId: event.request.requestId}, event.session);
}
if (event.request.type === "LaunchRequest") {
onLaunch(event.request,
event.session,
function callback(sessionAttributes, speechletResponse) {
context.succeed(buildResponse(sessionAttributes, speechletResponse));
});
} else if (event.request.type === "IntentRequest") {
onIntent(event.request,
event.session,
function callback(sessionAttributes, speechletResponse) {
context.succeed(buildResponse(sessionAttributes, speechletResponse));
});
} else if (event.request.type === "SessionEndedRequest") {
onSessionEnded(event.request, event.session);
context.succeed();
}
} catch (e) {
context.fail("Exception: " + e);
}
};
/**
* Called when the session starts.
*/
function onSessionStarted(sessionStartedRequest, session) {
console.log("onSessionStarted requestId=" + sessionStartedRequest.requestId
+ ", sessionId=" + session.sessionId);
}
/**
* Called when the user launches the app without specifying what they want.
*/
function onLaunch(launchRequest, session, callback) {
console.log("onLaunch requestId=" + launchRequest.requestId
+ ", sessionId=" + session.sessionId);
//Have Alexa say a welcome message
getWelcomeResponse(callback);
}
/**
* Called when the user specifies an intent for this application.
*/
function onIntent(intentRequest, session, callback) {
console.log("onIntent requestId=" + intentRequest.requestId
+ ", sessionId=" + session.sessionId);
var intent = intentRequest.intent,
intentName = intentRequest.intent.name;
if ("GarageAction" === intentName) {
console.log("Garage action was requested...");
ControlGarage(intent, session, callback);
} else if ("GetGarageState" === intentName) {
console.log("Garage state was requested...");
GetGarageState(intent, session, callback);
} else if ("GetTemp" === intentName) {
console.log("Garage temperature was requested...");
GetTemp(intent, session, callback);
} else {
throw "Invalid intent";
}
}
/**
* Called when the user ends the session.
* Is not called when the app returns shouldEndSession=true.
*/
function onSessionEnded(sessionEndedRequest, session) {
console.log("onSessionEnded requestId=" + sessionEndedRequest.requestId
+ ", sessionId=" + session.sessionId);
// Add cleanup logic here
}
/**
* Helpers that build all of the responses.
*/
function buildSpeechletResponse(title, output, repromptText, shouldEndSession) {
return {
outputSpeech: {
type: "PlainText",
text: output
},
card: {
type: "Simple",
title: title,
content: output
},
reprompt: {
outputSpeech: {
type: "PlainText",
text: repromptText
}
},
shouldEndSession: shouldEndSession
}
}
function buildResponse(sessionAttributes, speechletResponse) {
return {
version: "1.0",
sessionAttributes: sessionAttributes,
response: speechletResponse
}
}
/**
* Functions that control the app's behavior.
*/
function getWelcomeResponse(callback) {
// If we wanted to initialize the session to have some attributes we could add those here.
var sessionAttributes = {};
var cardTitle = "Welcome";
var speechOutput = "You can ask me to open the garage, close the garage, door state, or the temperature. "
+ "Please tell me what you want to do with your garage";
// If the user either does not reply to the welcome message or says something that is not
// understood, they will be prompted again with this text.
var repromptText = "Please tell me what you want to do with your garage";
var shouldEndSession = false;
callback(sessionAttributes,
buildSpeechletResponse(cardTitle, speechOutput, repromptText, shouldEndSession));
}
function ControlGarage(intent, session, callback) {
var cardTitle = "Garage Control";
var repromptText = "";
var sessionAttributes = {};
var shouldEndSession = true;
var speechOutput = "";
var action = intent.slots.Action;
console.log("Requested to "+action.value+" the garage.");
if (action.value == "open") {
//console.log("Requested to open the garage");
url = 'blah';
} else if (action.value == "shut" || action.value == "close") {
//console.log("Requested to close the garage");
url = 'blah';
} else {
throw "Invalid slot";
}
console.log("connecting to: " + url);
http.get( url, function( response ) {
var data = '';
response.on( 'data', function( x ) { data += x; } );
response.on( 'end', function() {
var obj = JSON.parse(data);
console.log(obj);
speechOutput = obj.response;
callback(sessionAttributes, buildSpeechletResponse(cardTitle, speechOutput, repromptText, shouldEndSession));
} );
} );
}
function GetGarageState(intent, session, callback) {
var cardTitle = "Garage Status";
var repromptText = "";
var sessionAttributes = {};
var shouldEndSession = true;
var speechOutput = "";
url = "blah";
console.log("connecting to: " + url);
http.get( url, function( response ) {
var data = '';
response.on( 'data', function( x ) { data += x; } );
response.on( 'end', function() {
var obj = JSON.parse(data);
switch(obj.status)
{
case 0:
text = "The garage is closed.";
break;
case 1:
text = "The garage is open.";
break;
default:
text = "Could not connect to the garage";
}
console.log(obj);
speechOutput = text;
callback(sessionAttributes, buildSpeechletResponse(cardTitle, speechOutput, repromptText, shouldEndSession));
} );
} );
}
function GetTemp(intent, session, callback) {
var cardTitle = "Outside temperature";
var repromptText = "";
var sessionAttributes = {};
var shouldEndSession = true;
var speechOutput = "";
var url = "blah";
console.log("connecting to: " + url);
http.get( url, function( response ) {
var data = '';
response.on( 'data', function( x ) { data += x; } );
response.on( 'end', function() {
var obj = JSON.parse(data);
console.log(obj);
speechOutput = "The temperature is "+obj.temp+", humidity at "+obj.humidity;
callback(sessionAttributes, buildSpeechletResponse(cardTitle, speechOutput, repromptText, shouldEndSession));
} );
} );
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment