Wing
Published © CC BY

Remember Your Keys

Alexa-powered checklist for things you need to do/grab before you leave your house. Also serves as a template for simple checklist skills.

EasyShowcase (no instructions)5 hours1,466
Remember Your Keys

Things used in this project

Story

Read more

Code

Code for Remember Your Keys

JavaScript
This thing goes on Amazon Lambda. Alexa pings it whenever the skill is called and it tells Alexa what to say. Most of the questions/responses are easily customized and can be changed to suit your own routine and/or make your own checklist skill.
/***
 * Remember Your Keys / a quick Alexa checklist skill
 * Wing L. Mui / Apr 2016
 * 
 * This is a quick skill that has Alexa run through a bunch of items in preparation of you leaving your house.
 * It can also serve as a template for other checklists!
***/



/***
 * Strings for the Checklist
 * This section contains the text that Alexa says and can be easily customized.
 * The length of the arrays can be changed too add questions, but all arrays must have the same length!
 * Note that card titles and the intro text Alexa says at the beginning are not here.
 * To fully customize this checklist you'll need to edit them inline. Sorry.
***/

// This is the array that stores all the questions asked by the skill.
// The first question questionText[0] is asked when the skill is activated.
var questionText = 
[
    "Do you have your keys?",
    "How about your electronic devices?", 
    "Are you carrying all the necessary IDs?", 
    "Do you have a way to pay for things?", 
    "How about the things you need to stay healthy?",
    "Did you turn off your stove, oven, and any other fire hazards?",
];

// This is the text that Alexa says when you ask her to elaborate on a question.
// explainText[i] is the explanation for questionText[i].
var explainText =
[
    "Keys to your home, your car, your office, and any other place you may want to go today.",
    "Your phone, tablet, fitness tracker, and any other small electronics device that you always carry with you.",
    "Drivers license, passport, and other government issued identification that you need to drive, fly, and not be arrested.",
    "Money, credit card, debit card. Maybe checks? No, bitcoins don't count.",
    "Medication, inhalers, and other small medical devices. Also, a bottle of water and some snacks won't hurt.",
    "Stoves, ovens, space heaters, any kind of open flame like candles or bonfires."
];

// This array stores the responses Alexa says when the user answers no to a question.
// If a user says no to questionText[i], Alexa wil say negativeResponse[i].
var negativeResponse =
[
    "Go find your keys. You can't leave without them.",
    "Go find your phone. You know, with the right triggers, I can help you with that.",
    "Go grab your IDs. I'll wait here.",
    "It would be bad if you left without any money.",
    "Please go find them. I would be sad if you got sick. Not that I'm capable of feelings.",
    "Please go do that now. I would like to stay not on fire."
];

// This string is said by Alexa if the user answers yes to all questions.
var successText = "Looks like you're ready to go. Enjoy your day!";

// This is the default "help" message that is said when a user doesn't say anything.
// The question that the user didn't answer will be appended to this and repeated.
var defaultReprompt = "Answer yes or no to each question. If you're not sure what I'm asking, say elaborate, or what do you mean, and I will explain the question.";



/**
 * Alexa Skills Kit Functions
 * Stuff from the Alexa Skills Kit that handles errors and basic structural stuff.
**/

exports.handler = function (event, context) {
    try {
        console.log("event.session.application.applicationId=" + event.session.application.applicationId);

        if (event.session.application.applicationId !== "amzn1.echo-sdk-ams.app.0f02daa1-7b3d-4a8b-a6a3-8f642c1fa25a") {
             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);
    }
};

function onSessionStarted(sessionStartedRequest, session) {
    console.log("onSessionStarted requestId=" + sessionStartedRequest.requestId +
        ", sessionId=" + session.sessionId);
}

function onLaunch(launchRequest, session, callback) {
    console.log("onLaunch requestId=" + launchRequest.requestId +
        ", sessionId=" + session.sessionId);
    getWelcomeResponse(callback);
}

function onIntent(intentRequest, session, callback) {
    console.log("onIntent requestId=" + intentRequest.requestId +
        ", sessionId=" + session.sessionId);

    var intent = intentRequest.intent,
        intentName = intentRequest.intent.name;

    // This giant conditional determines what the user said and directs
    // Alexa to the appropriate function / response.
    if ("IntentContinue" === intentName) {
        continueChecklist(intent, session, callback);
    } else if ("IntentStop" === intentName) {
        stopChecklist(intent, session, callback);
    } else if ("IntentBypass" === intentName) {
        bypassChecklist(intent, session, callback);
    } else if ("IntentConfused" === intentName) {
        explainChecklist(intent, session, callback);
    } else if ("AMAZON.HelpIntent" === intentName) {
        explainChecklist(intent, session, callback);
    } else if ("AMAZON.YesIntent" === intentName) {
        continueChecklist(intent, session, callback);
    } else if ("AMAZON.NoIntent" === intentName) {
        stopChecklist(intent, session, callback);
    } else if ("AMAZON.StopIntent" === intentName) {
        cancelChecklist(intent, session, callback);
    } else if ("AMAZON.CancelIntent" === intentName) {
        cancelChecklist(intent, session, callback);
    } else {
        throw "Invalid intent";
    }
}

function onSessionEnded(sessionEndedRequest, session) {
    console.log("onSessionEnded requestId=" + sessionEndedRequest.requestId +
        ", sessionId=" + session.sessionId);
}



/**
 * Checklist Functions
 * These functions actually control the checklist process.
**/

// If a user opens the skill with no other intent/instructions this thing runs.
// It creates an introduction and then asks the first question.
// If you want to customize the checklist you should probably edit the intro here.
function getWelcomeResponse(callback) {
    var sessionAttributes = {step: 0};
    var cardTitle = "Let's get started...";
    var speechOutput = "Let's see if you're ready to go. First of all, " + questionText[0];
    var repromptText = defaultReprompt + " " + questionText[0];
    var shouldEndSession = false;

    callback(sessionAttributes,
        buildSpeechletResponse(cardTitle, speechOutput, repromptText, shouldEndSession));
}

// Whenever a user answers affirmatively this function runs and proceeds to the next thing.
// If there is no next thing, then the user is done! Yay!
function continueChecklist(intent, session, callback) {
    var cardTitle = "Running through the checklist...";
    var speechOutput = "";
    var repromptText = defaultReprompt;
    var sessionAttributes = session.attributes;
    var shouldEndSession = false;
    
    sessionAttributes.step++;
    
    if (sessionAttributes.step < questionText.length)
    {
        speechOutput = questionText[sessionAttributes.step];
        repromptText += " " + questionText[sessionAttributes.step];
    }
    else
    {
        speechOutput = successText;
        cardTitle = "Looks like you're ready to go!";
        shouldEndSession = true;
    }

    callback(sessionAttributes,
        buildSpeechletResponse(cardTitle, speechOutput, repromptText, shouldEndSession));
}

// If a user starts the skill by asking it "am I ready to go?" or something like that
// this thing runs. It bypasses the intro sentence but that's about it.
function bypassChecklist(intent, session, callback) {
    var cardTitle = "Starting the checklist...";
    var speechOutput = questionText[0];
    var repromptText = defaultReprompt + " " + questionText[0];
    var sessionAttributes = {step: 0};
    var shouldEndSession = false;
    
    callback(sessionAttributes,
        buildSpeechletResponse(cardTitle, speechOutput, repromptText, shouldEndSession));
}

// If a user answers no to anything, this function runs.
// Alexa says the appropriate negative response and then ends the session.
function stopChecklist(intent, session, callback) {
    var cardTitle = "Looks like you're not ready to go yet...";
    var speechOutput = "";
    var repromptText = "";
    var sessionAttributes = session.attributes;
    var shouldEndSession = true;
    
    speechOutput = negativeResponse[sessionAttributes.step];
    
    callback(sessionAttributes,
        buildSpeechletResponse(cardTitle, speechOutput, repromptText, shouldEndSession));
}

// If a user says one of the universal "cancel" phrases (AMAZON.Cancel/StopIntent)
// then this function kicks in and just kills the skill.
function cancelChecklist(intent, session, callback) {
    var cardTitle = "Bye!";
    var speechOutput = "Okay, never mind.";
    var repromptText = "";
    var sessionAttributes = session.attributes;
    var shouldEndSession = true;
    
    callback(sessionAttributes,
        buildSpeechletResponse(cardTitle, speechOutput, repromptText, shouldEndSession));
}

// When a user asks for an elaboration this function gives it to them.
// Note that the question is repeated after the explaination.
function explainChecklist(intent, session, callback) {
    var cardTitle = "What are you talking about?";
    var speechOutput = "";
    var repromptText = defaultReprompt;
    var sessionAttributes = session.attributes;
    var shouldEndSession = false;
    
    speechOutput = explainText[sessionAttributes.step] + " So, " + questionText[sessionAttributes.step];
    repromptText += " " + questionText[sessionAttributes.step];
    
    callback(sessionAttributes,
        buildSpeechletResponse(cardTitle, speechOutput, repromptText, shouldEndSession));
}



/**
 * Helper Functions from Alexa Skills Kit
 * These functions actually build the responses as JSON things Alexa understands.
 * All questions / text are placed in cards on the Alexa app for user friendliness.
**/

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
    };
}

Remember Your Keys Intent Schema

JSON
This is the JSON thingie that tells Alexa what a user's intents can be. In this skill it's pretty simple since all a user really does is say "yes" or "no" or maybe "explain this thing please". It also uses some Amazon Alexa default intents so I don't have to go and define all the ways a person can say "yes", as the folks at Amazon did that already.
{
  "intents": [
    {
      "intent": "IntentContinue"
    },
    {
      "intent": "IntentStop"
    },
    {
      "intent": "IntentConfused"
    },
    {
      "intent": "IntentBypass"
    },
    {
      "intent": "AMAZON.HelpIntent"
    },
    {
      "intent": "AMAZON.YesIntent"
    },
    {
      "intent": "AMAZON.NoIntent"
    },
    {
      "intent": "AMAZON.StopIntent"
    },
    {
      "intent": "AMAZON.CancelIntent"
    }
  ]
}

Sample Utterances for Remember Your Keys

Plain text
These are sample templates for things a person may say in response to things Alexa says in this skill. Since the skill uses a lot of Amazon's default intents I didn't really have to add custom ways for a user to say yes or no, but I wanted to anyway in order to capture some of the other less common ways that I answer yes or no to a question. The IntentBypass strings are things users say after "Alexa, ask Remember Your Keys" when invoking the skill.
IntentContinue yes
IntentContinue yeah
IntentContinue yup
IntentContinue affirmative
IntentContinue definitely
IntentContinue of course
IntentContinue duh
IntentContinue i think so
IntentStop no
IntentStop nope
IntentStop oops
IntentStop negative
IntentStop i forgot
IntentStop crap
IntentStop not at all
IntentStop not yet
IntentConfused not sure
IntentConfused i'm not sure
IntentConfused what do you mean
IntentConfused huh
IntentConfused i don't get it
IntentConfused i don't understand
IntentConfused can you explain
IntentConfused elaborate
IntentConfused what are you talking about
IntentConfused maybe
IntentConfused i don't know'
IntentBypass am i ready to go
IntentBypass am i ready
IntentBypass can i leave
IntentBypass if i am ready to go
IntentBypass if i am ready
IntentBypass if i can leave
IntentBypass whether i am ready to go
IntentBypass whether i am ready
IntentBypass whether i can leave
IntentBypass for my checklist
IntentBypass for a checklist

Credits

Wing

Wing

2 projects • 3 followers
Teacher, Crafter, Maker, Singer, Dancer, Lv3 Cat / Lv4 Social Justice Bard. Award winning sheep. Pronouns depend on number of corvids present.
Contact
Thanks to Guy Sie.

Comments