2
2

More than 1 year has passed since last update.

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

Last updated at Posted at 2019-12-23

はじめまして@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

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

さいごに

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

2
2
1

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
  3. You can use dark theme
What you can do with signing up
2
2