LoginSignup
2

More than 1 year has passed since last update.

posted at

updated at

Alexa+lambdaでスマート(じゃない)ホームスキルをつくる

はじめまして@ufoo68です。今回はAWSのアドベントカレンダーにお邪魔させていただきました。

はじめに

このアドベントカレンダーに登録したきっかけは最近re:invent2019に参加した経験からです。普段はあまりAWS関係の記事は書いたりしないのですが、せっかく大きなイベントに参加したのでlambdaについて書いてみよと思った次第です。イベントの発表を聴いて気に入ったサービスについてはこの記事でまとめました。今回はその中のAlexaに挑戦してみようと思い、思い切ってEcho Show 5を購入してみました。

が、到着予定日がまさかの1/17~1/23とかいうことになってしまい(サイバーマンデーでポチったのでこういうこになったのか?)、Alexa開発がアドベントカレンダーに間に合わないという事態に!!というわけで、今回はシミュレーターとかを使ってスキルを作ってみようかなと思います。まあ、lambda+Alexaでの今後の開発のための練習ということにしておきましょうw

スキルについて

今回つくるのは「スマート(じゃない)ホームスキル」です。本当はスマートホーム的なのやりたかったわけですが、何せ実機がないものですからこういうものを考えました。これは何かというと、スマートホームのフリをして返事だけは一丁前にするスキル、つまり「ライトをつけて」とかいうと「わかりました」と返事だけはするが実際には何もしてくれない、そんなスマートホームもどきのスキルをつくってみようと思います。

スキルの実装

今回はこちらを参考にしてCodeStarを用いてコードを実装やらデプロイを行いました。これを用いると対話モデルの作成とか全部をAWSでやってくれるみたいです。開発は「Hallo World Skill」というサンプルテンプレートを用いて開発しました。ここで公開しています。

まずは、skill.jsonを以下のようにしました。

skill.json
{
  "manifest": {
    "publishingInformation": {
      "locales": {
        "ja-JP": {
          "summary": "スマートじゃない、スマートホームスキル",
          "examplePhrases": [
            "Alexa、スマートじゃないホームを起動して",
            "電気をつけて",
            "電気をけして"
          ],
          "name": "スマート(じゃない)ホーム",
          "description": "スマートじゃない、スマートホームスキル"
        }
      },
      "isAvailableWorldwide": true,
      "testingInstructions": "Sample Testing Instructions.",
      "category": "EDUCATION_AND_REFERENCE",
      "distributionCountries": []
    },
    "apis": {
      "custom": {
      }
    },
    "manifestVersion": "1.0"
  }
}

あとは対話モデルは以下のように設定しました。

interactionModels\custom\ja-JP.json
{
    "interactionModel": {
        "languageModel": {
            "invocationName": "スマートじゃないホーム",
            "intents": [
                {
                    "name": "AMAZON.CancelIntent",
                    "samples": []
                },
                {
                    "name": "AMAZON.HelpIntent",
                    "samples": []
                },
                {
                    "name": "AMAZON.StopIntent",
                    "samples": []
                },
                {
                    "name": "LightOnIntent",
                    "slots": [],
                    "samples": [
                        "電気をつけて",
                        "ライトをつけて"
                    ]
                },
                {
                    "name": "LightOffIntent",
                    "slots": [],
                    "samples": [
                        "電気を消して",
                        "ライトを消して"
                    ]

                }
            ],
            "types": []
        }
    }
}

コードもあまりサンプルと変わってませんが、こんな感じです。

lambda\custom\index.js
// This sample demonstrates handling intents from an Alexa skill using the Alexa Skills Kit SDK (v2).
// Please visit https://alexa.design/cookbook for additional examples on implementing slots, dialog management,
// session persistence, api calls, and more.
const Alexa = require('ask-sdk-core');

const LaunchRequestHandler = {
    canHandle(handlerInput) {
      return handlerInput.requestEnvelope.request.type === 'LaunchRequest';
    },
    handle(handlerInput) {
      const speechText = 'スマートじゃないホームへようこそ。';
      return handlerInput.responseBuilder
        .speak(speechText)
        .reprompt(speechText)
        .getResponse();
    }
};
const LightOnIntentHandler = {
    canHandle(handlerInput) {
      return handlerInput.requestEnvelope.request.type === 'IntentRequest'
        && handlerInput.requestEnvelope.request.intent.name === 'LightOnIntent';
    },
    handle(handlerInput) {
      const speechText = 'はい、つけます。';
      return handlerInput.responseBuilder
        .speak(speechText)
        //.reprompt('add a reprompt if you want to keep the session open for the user to respond')
        .getResponse();
    }
};
const LightOffIntentHandler = {
  canHandle(handlerInput) {
    return handlerInput.requestEnvelope.request.type === 'IntentRequest'
      && handlerInput.requestEnvelope.request.intent.name === 'LightOffIntent';
  },
  handle(handlerInput) {
    const speechText = 'はい、消します。';
    return handlerInput.responseBuilder
      .speak(speechText)
      //.reprompt('add a reprompt if you want to keep the session open for the user to respond')
      .getResponse();
  }
};
const CancelAndStopIntentHandler = {
    canHandle(handlerInput) {
      return handlerInput.requestEnvelope.request.type === 'IntentRequest'
        && (handlerInput.requestEnvelope.request.intent.name === 'AMAZON.CancelIntent'
          || handlerInput.requestEnvelope.request.intent.name === 'AMAZON.StopIntent');
    },
   handle(handlerInput) {
      const speechText = 'さようなら!';
      return handlerInput.responseBuilder
        .speak(speechText)
        .getResponse();
    }
};
const SessionEndedRequestHandler = {
    canHandle(handlerInput) {
      return handlerInput.requestEnvelope.request.type === 'SessionEndedRequest';
    },
    handle(handlerInput) {
      // Any cleanup logic goes here.
      return handlerInput.responseBuilder.getResponse();
    }
};

// Generic error handling to capture any syntax or routing errors. If you receive an error
// stating the request handler chain is not found, you have not implemented a handler for
// the intent being invoked or included it in the skill builder below.
const ErrorHandler = {
    canHandle() {
      return true;
    },
    handle(handlerInput, error) {
      console.log(`~~~~ Error handled: ${error.message}`);
      const speechText = `すいません、聞き取れませんでした。`;

      return handlerInput.responseBuilder
        .speak(speechText)
        .reprompt(speechText)
        .getResponse();
    }
};

// This handler acts as the entry point for your skill, routing all request and response
// payloads to the handlers above. Make sure any new handlers or interceptors you've
// defined are included below. The order matters - they're processed top to bottom.
exports.handler = Alexa.SkillBuilders.custom()
  .addRequestHandlers(
    LaunchRequestHandler,
    LightOnIntentHandler,
    LightOffIntentHandler,
    CancelAndStopIntentHandler,
    SessionEndedRequestHandler) // make sure IntentReflectorHandler is last so it doesn't override your custom intent handlers
  .addErrorHandlers(
    ErrorHandler)
  .lambda();

動作のようす

シミュレーターで動かしてこんな感じでちゃんと動いてくれました。

slill.png

まあ、実機もないしスマートホームに対応させてないので返事だけなのですが。。。

さいごに

とりあえず実機が届いたらもう少し色々やってみたいですね。

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
What you can do with signing up
2