Voice enabling smart-home devices with Amazon Alexa

skills_api_logoOur client is a smart-home devices manufacturer. The company decided to voice-enable their products, starting with an ability to interact with the Amazon Echo family of devices, powered by Amazon Alexa technology.

The client had extensive expertise in smart devices field. Their competent IT department wanted an enterprise-grade solution – easy-to-maintain and built for scale.

We were going to build two Alexa skills above utilizing the Smart Home Skill and Custom Skill APIs. Smarthome API do not support and hence do not require setting-up custom utterances. Smarthome skills are also easier to invoke. However, the lack of customization limits the ability to expose some of finer features offered by specific target hardware. In the time that we have worked with Amazon on Alexa skills, Smarthome API has been enhanced several times. We expect that eventually, it will accommodate more smart-home devices and usage scenarios.

Building a basic skill is fairly easy. We used a configuration with account-linking and an OAuth endpoint authenticating devices with access tokens. While a startup may not need this, we had to accommodate a well-established development process with the product being built for multiple stages such as development, beta, production and having to deal with different API and authentication endpoints depending on the stage and locality – our product needed to support different regions.

Maintaining all of this manually would create a huge problem once the product moved from the initial development to subsequent releases. Therefore, the client had an established system in place, with automated builds and continuous integration.

Alexa skills send events, when users issue commands. One of the Alexa skill configuration parameters is the endpoint processing those events. While various cloud “resources” could be used to host the code processing Alexa requests, programmers typically use Amazon’s own Lambda service. In addition to being an auto-scaling solution, using lambda simplifies the skill configuration and alleviates the need to setup the security certificate.

lambdanodejs-logo

endpoint

Amazon Lambda supports several programming languages including Java, JavaScript and Python. Our language of choice was JavaScript, because much the client’s backend was already running on Node.JS. If we were using Python, we might have opted for the zappa package. However, with Node.JS, we selected the serverless framework for configuration and deployment automation. We like serverless because of support for Amazon Lambda, as well as Google CloudFunctions and Microsoft Azure. That would come in handy when adding Google Home and Microsoft Cortana Assistant.

home_framework_v1_1

With Node.JS and serverless, we almost had a complete toolkit. The remaining problem was the Amazon’s lag behind Node.JS release cycles. At the time of the writing, Amazon Lambda supports only Node.JS version 4.3. Recently, it was 4.2 and the latest upgrade happened about the same time version 7 came out. Our missing ingredients were babel to transpile the code written in the new ES6/ES7 JavaScript syntax to the version Lambda accepts, and webpack to build all of our code, including modules, into a single JavaScript file.

Our project structure will be:

A source directory with Alexa client, IoT-backend client, common functionality directory and one directory for each skill type – smarthome and customskill. In the top source directory, we create three JavaScript files: smarthome_lambda_handler.js and customskill_lambda_handler.js for the two skills, and app.js to run/debug our code locally, without having to upload it to AWS Lambda.

Now, we need to configure webpack to build our code. We create a webpack directory off root of the project tree and populate it with webpack 2.2 configurations. We create one webpack configuration for each development and production builds.

We used a base config shown below:

'use strict';

var webpack = require('webpack');

module.exports = {
    output: {
        library: 'projectpure',
        libraryTarget: 'umd'
    },
    resolve: {
        extensions: ['.json', '.jsx', '.js']
    },
    module: {
        rules: [
            { test: /\.cfg$/, loader: 'raw-loader'},
            { test: /\.js$/, loaders: ['babel-loader'], exclude: /node_modules/ },
        ]
    },
    target: 'node',
    externals: [ 'aws-sdk'  ],
    plugins : [
        new webpack.ContextReplacementPlugin(/moment[\/\\]locale$/, /en/)
    ]
};

Actual build-type configurations inherit from the base, so our development config looks like:
'use strict';

var webpack = require('webpack');
var CopyWebpackPlugin = require('copy-webpack-plugin');
var baseConfig = require('./webpack.config.base');

module.exports = function(options) {
    let skill  = options.skill;
    let locale = options.locale;
    let stage  = options.stage;
    let skillEntry = 'lambda_function_smart_home.js';
    if(options.skill !== 'smarthome') {
        skillEntry = 'lambda_function_custom_skill.js';
    }
    baseConfig.entry = ['babel-polyfill', `./src/${skillEntry}`];
    var config = Object.create(baseConfig);
    config.output.filename = skillEntry;
    config.output.path = `./dist/${locale}/${skill}/${stage}`;
    config.plugins = config.plugins.concat([
        new webpack.DefinePlugin({
            'process.env.NODE_ENV': JSON.stringify('development')
        }),
        new CopyWebpackPlugin([{ from: `./src/config/${locale}/${stage}/config.cfg` }])
    ]);
    return config;
}

Now, our project can be built. Let’s add code to it, and produce a basic skill then deploy to AWS Lambda using serverless framework. See how we did this, in the next post.

Categories

More from our blog

See all posts