Nani: Nani
Creating a Skill

In this module we will be building an Alexa Skill in order to gather any data we wish from our server via REST calls. You will be able to customize this skill so that you may ask Alexa for any information you want and she will read the response for you! Alexa is in the spotlight of the IoT world, so bringing in this product into our project will allow us to harness a lot of functionality.

Alexa Skills are generally divided into two parts: the Voice User Interface (VUI) and the Lambda Function Backend. These two parts can be conceptualized similarly to a website’s frontend and backend. The user will interact with the VUI, which will consequently send data to the skill’s Lambda backend and perform some sort of useful function. In our case, we will use the VUI to listen to some queues from the user, determine what type of function they desire, and ask the Lambda backend to perform that action (a RESTful call).

In this tutorial we will be building both components, as well as connecting them. We will build an Interaction Model for the VUI within the Amazon Developer Portal and we will connect it to a Lambda Function in Amazon Web Services.

These are two different Amazon services, not to be confused with one another.

Voice User Interface (VUI)

To begin the development of our VUI, log into the Amazon Developer Portal at https://developer.amazon.com/.

Then select Alexa, followed by Alexa Skills Kit -> Get Started -> Alexa Skills Kit at the top navigation bar. Next, click on Start a Skill. Here, you’ll see a dashboard of all the Alexa Skills you own (which might be zero at the moment). Click on Create Skill to create and customize your skill. Choose a Skill Name and select Custom model.

In the Interaction Model dashboard, we see three categories: Invocation, Intent, and Slot Type. Invocation is simply the name used to trigger this skill. Every Alexa Skill has a unique name to be identified with, such as Spotify, Uber, or OneBusAway. You choose an invocation name to help Alexa know what skill you are attempting to use. Intent is the action to be performed with that specific skill. Using the Uber example “Alexa, ask Uber to call me a ride”, Uber is the Invocation and call me a ride is the Intent.

Next is the Slot Type. These are variables that can be passed into your skill. Using a WeatherApp example: “Alexa, ask WeatherApp to tell me what the weather is in New York

  • WeatherApp is the Invocation
  • tell me what the weather is is the Intent
  • New York is the Slot

One last facet is the notion of “utterances”. These are the natural language phrases a user might say while using the skill. You need to cover as much ground as possible with utterances.

You are able to customize these as you like using the JSON editor (the GUI editing functionality is not perfect yet). So click on JSON Editor and enter the following code:

{
	"interactionModel": {
    	"languageModel": {
        	"invocationName": "Invocation Name",
        	"intents": [
            	{
                	"name": "AMAZON.FallbackIntent",
                	"samples": []
            	},
{
                	"name": "AMAZON.CancelIntent",
                	"samples": []
            	},
            	{
                	"name": "AMAZON.HelpIntent",
                	"samples": []
            	},
            	{
                	"name": "AMAZON.StopIntent",
                	"samples": []
            	},
            	{
                	"name": "MySkillIntent",
                	"slots": [
                    	{
                        	"name": "slotName",
                        	"type": "AMAZON.LITERAL"
                    	}
                	],
                	"samples": [
                    	"the {defaultValue|slotName} value",
                    	"the {defaultValue|slotName} in my device",
                    	"the {defaultValue|slotName} reading",
                    	"the {defaultValue|slotName}"
                	]
            	}
        	],
        	"types": []
    	}
	}
}

Make sure to replace the placeholders with the correct values, depending on what you choose to name the different parts of your skill. This includes

  • Invocation Name
  • MySkillIntent
  • slotName
  • defaultValue|slotName pairs
Note that the “utterances” exist under the “samples” portion. defaultValue is simply the default value of the slot you choose; this is required in the case where the used chooses a wrong slot. For example, if you’re reading from a device that reads temperature and humidity, your slot could be called sensor and your default could be temperature, resulting in:

Once you’re happy with the way your Interaction Model looks, click on Save Model and Build Model to save and build the VUI respectively.

Lambda Backend

We will choose the template alexa-skills-kit-nodejs-factskill since it contains a lot of what we require, and lets us skip a lot of the setup. We will modify this template and adjust it to our needs.

Click on Blueprints and search for alexa-skill-kit-sdk-factskill. Select it and give a name to your new Lambda Function. Under Role, select Create a custom role. This will determine which permissions your Lambda function has, in our case we want it to be able to freely interact with both Alexa and our server. Name your role in the pop up window, and click on Allow. Back in the Lambda window, click on Create Function. Next, go to the Function Code portion and input the following:

/* eslint-disable  func-names */
/* eslint quote-props: ["error", "consistent"]*/

const Alexa = require('alexa-sdk');
const https = require('https');

//========================================================
//TODO: The items below this comment need your attention.
//========================================================

const SKILL_NAME =  'Test Alexa';
const GET_FACT_MESSAGE =  'Here’s your data:';
const HELP_MESSAGE = 'You can do the following: ';
const HELP_REPROMPT =  'How can I help?';
const STOP_MESSAGE = 'Goodbye!';

//====
// TODO external API options
//====

var apiOptions = {
	host: 'my-host-url.herokuapp.com',
	port: 443,
	path: '/my/api/path/',
	method: 'GET'
};

const handlers = {
	'LaunchRequest': function () {
    	this.emit('MySkillIntent');
	},
	'MySkillIntent': function() {
    	var responseString = '';
    	var mythis = this;
    	var slotNameValue = this.event.request.intent.slots.slotName.value;
   	 
    	https.get(apiOptions, (res) => {
        	console.log('statusCode:', res.statusCode);
        	console.log('headers:', res.headers);
    
        	res.on('data', (d) => {
            	responseString += d;
        	});
      
        	res.on('end', function(res) {
            	var responseJSON = JSON.parse(responseString);

            	var reading = responseJSON[responseJSON.length - 1];
           	 
            	if (reading[slotNameValue] == undefined) {
                	mythis.response.cardRenderer(SKILL_NAME, "Sorry, I couldn't find what you needed");
                	mythis.response.speak("Sorry, I couldn't find what you needed");
                	mythis.emit(':responseReady');
            	} else {
                	const speechOutput = "The " + slotNameValue + " reading is " + reading[slotNameValue];
                	console.log('==> Answering: ', speechOutput);
               	 
                	mythis.response.cardRenderer(SKILL_NAME, speechOutput);
                	mythis.response.speak(speechOutput);
                	mythis.emit(':responseReady');
            	}
        	});
    	}).on('error', (e) => {
        	console.error(e);
    	});
	},
	'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');
	},
};

exports.handler = function (event, context, callback) {
	const alexa = Alexa.handler(event, context, callback);
	alexa.APP_ID = APP_ID;
	alexa.registerHandlers(handlers);
	alexa.execute();
};

Make sure to change all of the defaults to fit your design. Look for

  • slotName
  • MySkillIntent
  • The const variables under the TODO Note
  • "host" in apiOptions (this is your web app url)
  • "path" in apiOptions (this is the route you want to access)
  • "method" in apiOptions (this is the type of request you want to make)
Click Save once you are finished editing.

That's it! Now all we have to do is connect the lambda function to the VUI!

Connecting the Pieces

In the Lambda page, select Add Triggers and pick Alexa Skills Kit to begin the connection process. In the Configuration Triggers, click enable. Back in the Alexa portion of the Amazon Developer Portal (where we edited the VUI), find the Endpoint menu. Copy the Skill ID, which should look something like this:

Amzn1.ask.skill.e66a0593-feaf-4bad-8052-d6f4817052a1

And paste it in the Configuration Triggers menu from before. Click Save and make sure it works properly.

In the Lambda page, look for the ARN value at the top. It should look something like:

arn:aws:lambda:us-east-1:407202906549:function:testAlexa

This is the Lambda function’s unique identifier used to connect it to the VUI. Back in the Alexa portion of the Amazon Developer Portal, find the Endpoint menu. Select AWS Lambda ARN and paste the ARN from the previous step into the Default Region box. Click on Save Endpoints followed by Build Model (in the interaction model menu) and now you are done!

Test your Alexa skill by enabling it in one of your Alexa devices and saying one of the utterances you configured! You can customize your skill to your liking, and use the testing infrastructure to try out different things!