Creating A Custom Amazon Alexa Skill

Ryan Nguyen .NET, Azure, Conversational Apps, Programming, Tutorial 1 Comment

Attention: The following article was published over 7 years ago, and the information provided may be aged or outdated. Please keep that in mind as you read the post.

With the explosion of internet of things (IoT), many companies are competing to create the best smart home ecosystem for consumers. Such big players include Samsung, Apple, Amazon, Insteon, Wink, Phillips, etc. Each offers a unique experience and claims to be the best in the business.

In this blog we will discuss the Amazon Echo and its Alexa application. We will go through the process to create a custom Alexa Skill about the Keyhole blog, paying particular attention to keywords you’ll need to understand when you create your own Skill. From there, we’ll show how to test a Skill via simulators and deploy it to your Amazon Echo.

What is Amazon Echo & Alexa?

Amazon Echo is a robust system that allows the user to interact with their smart devices via voice command. Not only does it sync up to hundreds of smart devices (including switches, thermostats, garage doors, sprinklers, door locks, etc.), it also allows the users to play music through streaming websites including Spotify, Pandora, iHeartRadio, and more. More information could be found via Amazon’s website.

Alexa is the application that the Echo communicates with. You can consider Alexa as the brain of the Amazon Echo. It controls how your Amazon Echo communicates with your other smart devices and services. It also allows the third-party companies to create custom Skills which are then accessible through the Amazon Echo.

Let’s Get Started

Prerequisite
Creating Your First Alexa Skill

To create a custom Alexa Skill, an Amazon developer account is required. Navigate to the Amazon developer page and sign up. This process is free to use. However, whichever account you choose, this account is required to be synced up to the Alexa App and Amazon Echo.

  • Navigate to https://developer.amazon.com/
  • Under the “Alexa” tab, click on “Alexa Skills Kit Getting Started
  • Click on “Add a New Skill” to create your custom skill
  • Fill out the next page with the following information:
Skill Information
  1. Skill Type = Custom

  2. Name
    • Keyhole Software

  3. Invocation Name – The activation name for the custom app.  For example, “Alexa ask Keyhole…”
    • Keyhole

Interaction Model

Now we’ll walk through the parts of the Interaction Model that are required.

  1. Intent – “A JSON structure which declares the set of intents your service can accept and process.” – Amazon

      {
    	"intents": [
    		{
    			"intent": "LatestBlogIntent"
    		},
    		{
    			"intent": "BlogCountIntent"
    		},
    		{
    			"slots": [
    				{
    					"name": "Author",
    					"type": "LIST_OF_AUTHORS"
    				}
    			],
    			"intent": "BlogIntent"
    		}
    	]
    }
  2. Slots – “A list of values for specific items used by your Skill and referenced in the intents when using a custom slot type” – Amazon
  3. Utterances – “A structured text file that connects the intents to likely spoken phrases and containing as many representative phrases as possible.” – Amazon

    LatestBlogIntent what is latest blog
    LatestBlogIntent latest blog
    BlogCountIntent what is the blog count
    BlogCountIntent blog count
    BlogIntent get {Author} blog
    BlogIntent get blog from {Author}
    
Configuration

In this blog, I have created a sample service endpoint which you can utilize. I will not be discussing the creation of the Web API Project and its deployment to Microsoft Azure, as I feel it would be out of scope and deserve a blog of its own.

Instead, a sample code and endpoint with the appropriate return response is provided.

Service Endpoint Sample Code: AlexaSkill
Service Endpoint URLhttps://alexaskillwebapp.azurewebsites.net/api/Alexa/GetBlogInfo

SSL Certificate

As I am hosting my service endpoint with Microsoft Azure, Azure assigns it to a subdomain of Azurewebsites.net.

For example, if the service is called AlexaSkillWebApp, our service endpoint would be https://alexaskillwebapp.azurewebsites.net/. Microsoft also automatically applies its certifications to all of its subdomains, resulting in us not having to upload our own certificate.

Testing

This is a very important and useful page for us to test and debug our application. The voice simulator allows the developer to exactly hear how Alexa would respond. It supports plain text or SSML. You can read more about SSML and the supported tags by Alexa. This is a very handy tool for us to control how Alexa would respond.

The Service Simulator allows the developer to mimic the end user. It allows developers to see what Alexa is sending as the request JSON parameter based on entered utterance and what Alexa is actually receiving back as a response. With all of this information, it is important to test our application and service before publishing our Skills to the market.

Testing the Service with Service Simulator

To test our Alexa Skill, simply enter in the utterance phrase “Get Ryan’s Blog” in the Textbox.

Service Request:
{
  "session": {
    "sessionId": "SessionId.42d5fd62-caf4-4371-8b1d-d73c1e2fde4a",
    "application": {
      "applicationId": "amzn1.ask.skill.1ded7905-8101-4683-8772-41aab132ba6c"
    },
    "attributes": {},
    "user": {
      "userId": "amzn1.ask.account.AEX77IQZ7QXAMCAEVQ2W4TD3OC2V6FJFU2WWY7WP3IYHZMYUUXNIW3QY3TWDIZMD5IJZZ4VZXCCUZD3XTPJPMUTHURJEVTGAZCCRH2QIUGBHDD66N7SW55X77Y676XT6EDHS7FLMH2QFXYPVLBUBWKBXVE5NF657WJI63SQNJNPI34FVTMHQFMB3JCPT4CCJRXNEFB55AUNHAXI"
    },
    "new": true
  },
  "request": {
    "type": "IntentRequest",
    "requestId": "EdwRequestId.7b154f70-6d0a-44ea-825b-6f9ab92937dc",
    "locale": "en-US",
    "timestamp": "2017-04-17T00:03:20Z",
    "intent": {
      "name": "BlogIntent",
      "slots": {
        "Author": {
          "name": "Author",
          "value": "Ryan's"
        }
      }
    }
  },
  "version": "1.0"
}

Notice the Intent Request in the JSON shows BlogIntent Alexa determines which intent to use by getting the utterance phrase and matching it to the intent schema. Had we used a different phrase like Latest Blog or Blog Count, then a different intent would have been used. This is helpful and important when creating your service endpoint. Depending on different types of intent, you can simply create a switch condition to return the appropriate response.

In addition, the slots will only be set in the IntentRequest if it was defined in your intent schema. In this example, the only intent that has a slot is the BlogIntent. This will supply us with an Author object.

Service Response:

This is the response that our endpoint returns back to the requester.

{
  "version": "1.0",
  "response": {
    "outputSpeech": {
      "type": "PlainText",
      "text": "Getting started with Alexa Skill was created by Ryan on 4/16/2017 11:21:51 PM"
    },
    "card": {
      "content": "Getting User Blog Info",
      "title": "Keyhole Software",
      "type": "Simple"
    },
    "shouldEndSession": true
  },
  "sessionAttributes": {}
}

In this case, it is Amazon Alexa Service simulator. The outputSpeech is what Amazon Echo will use as the speech response. As mentioned earlier in this blog, we can set the type of output speech from PlainText or SSML.

The shouldEndSession flag is set to true if you want the session to end. Set it to false if you want Alexa to follow up with a different action while still using the same session. More information can be found here in regards to using sessions.

The card object is the information which would be pushed out into your Amazon Alexa phone app. You can set properties such as the title, content, image, etc. More information on card can be found here.

Service Endpoint Sample Code
using AlexaSkill.DAL;
using AlexaSkill.Helper;
using HtmlAgilityPack;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Text.RegularExpressions;
using System.Web.Http;

namespace AlexaSkill.Controllers
{

    [RoutePrefix("api/Alexa")]
    public class AlexaController : ApiController
    {
        private AlexaSkillDBContext context;
        public AlexaController()
        {
            context = new DAL.AlexaSkillDBContext();
        }
        private string GetLatestBlog()
        {
            var latestBlog = context.Blogs.OrderByDescending(x => x.BlogDate).FirstOrDefault();
            var blogUser = context.Users.FirstOrDefault(x => x.Id == latestBlog.UserId);

            return string.Format("The latest blog post was created by {0}, titled {1}", blogUser.FirstName + " " + blogUser.LastName, latestBlog.Title);
        }

        private string GetBlogCount()
        {
            var BlogCount = context.Blogs.Count();
            return string.Format("There are {0} blogs in total", BlogCount);
        }

        private string GetBlogByUser(dynamic request)
        {
            var authorName = ((string)(request.request.intent.slots.Author.value)).Split('\'').FirstOrDefault();
            var context = new DAL.AlexaSkillDBContext();
            var blogByUser = (from user in context.Users
                              join blog in context.Blogs on user.Id equals blog.UserId
                              where user.FirstName == authorName
                              select blog).ToList();

            string returnText;
            if (blogByUser == null)
            {
                returnText = string.Format("There are no current blog post created by {0}", authorName);
            }
            else
            {
                returnText = string.Format("{0} was created by {1} on {2}", blogByUser.FirstOrDefault().Title, authorName, blogByUser.FirstOrDefault().BlogDate);
            }

            return returnText;

        }

        [HttpPost]
        [Route("GetBlogInfo")]
        public dynamic GetBlogInfo(dynamic request)
        {
            string returnText = null;
            var requestNotNull = ((JObject)request).Count != 0;

            if (requestNotNull == false)
            {
                return null;
            }

            var IntentName = request.request.intent.name;
            if (IntentName == "BlogIntent")
            {
                returnText = GetBlogByUser(request);

            }
            else if (IntentName == "LatestBlogIntent")
            {
                returnText = GetLatestBlog();
            }
            else if (IntentName == "BlogCountIntent")
            {
                returnText = GetBlogCount();
            }


            return new
            {
                version = "1.0",
                sessionAttributes = new { },
                response = new
                {
                    outputSpeech = new
                    {
                        type = "PlainText",
                        text = returnText
                    },
                    card = new
                    {
                        type = "Simple",
                        title = "Keyhole Software",
                        content = "Getting User Blog Info"
                    },

                    shouldEndSession = true
                }

            };
        }

    }

   
}

Testing On Amazon Echo

In the Test page, make sure this option is enabled.

To verify your Skill is enabled:

  1. Open your Alexa App 
  2. Click on “Menu
  3. Navigate to “Skills
  4. On the top-right hand, click on “Your Skills

Testing New Skill

Check out the following quick videos to see it in action!
[youtube https://www.youtube.com/watch?v=2hoz71duSM4] [youtube https://www.youtube.com/watch?v=-gaHw6Vlzas] [youtube https://www.youtube.com/watch?v=6VdraWDkuIc] Success!

Summary

In this blog, you learned how simple it was to create an Alexa Skill. Furthermore, you learned how to test the Skill via the simulators and deploy it to your Amazon Echo. 

I hope you enjoyed this blog and found it helpful in creating your own Alexa Skill. Amazon has done an excellent job with documentation, so be sure to check it out, including:

0 0 votes
Article Rating
Subscribe
Notify of
guest

1 Comment
Oldest
Newest Most Voted
Inline Feedbacks
View all comments