Tweet

2018年12月12日水曜日

【AmazonAlexaスキル開発】Echo Spotなどのディスプレイ対応Alexa対応スキルの作り方 ask-sdk V2での作り方

はてなブログ版記事を是非御覧ください!

こちらに大幅に見やすく同じ内容が掲載しております!

【AmazonAlexaスキル開発】
Echo Spotなどのディスプレイ対応Alexa対応スキルの作り方 
ask-sdk V2での作り方 - 指定難病IgA腎症エンジニア ブログ
 http://urx.space/Oquc






こういう人向け

  • AmazonAlexaでEcho Spot(Echo Show)対応のディスプレイ表示スキル作成したい
  • 音声情報だけではなくディスプレイに表示させたい
  • AmazonAlexaキャンペーンでEcho Show無料クーポンを狙っている開発者
  • AmazonAlexaディスプレイ表示でドハマリした人
  • Alexa SDK V2でディスプレイ表示スキルを開発したい開発者様
当記事を読破後、通常のAmazonAlexaスキルを開発されている開発者様全員が
ディスプレイ付き開発が出来るような、そんな記事を目指して書きました。

ディスプレイ対応は難しい?いえいえそんなことない。


ディスプレイ自体そんなに難しい事ではありませんでした。
ただ、僕はそもそもディスプレイ対応が初めてでかつネットに文献が少ないので、
かなりどはまりしてしまいました。

また、開発環境としてAlexa SDK V2です。
ただ、V1開発者の方は以前ご紹介したV1用の記事をご確認下さい。

【AmazonAlexaスキル開発】Echo Spotなどのディスプレイ対応Alexa対応スキルの作り方 ask-sdk V1での作り方 http://iga34engineer.blogspot.com/2018/12/amazonalexaecho-spotalexa-ask-sdk-v1.html

手順の確認

  1. AmazonAlexaスキル内でインターフェースの切り替え(Displayの有効化)
  2. Amazonシミュレータ(ビルド)上で、Displayの有効化
  3. lambda関数のコーディング書き換え・追記
ひとつひとつ順を追って説明していきますね。
結構やっている事自体はシンプルです。

Alexaスキルでディスプレイ使うタイプのスキルですと宣言して、
デバック用にDisplayを表示させて、
コードでディスプレイ対応のコーディングするだけです。

インターフェースの切り替え


    カスタム、対話モデル、インテントなどの左タブの下側に
    「インターフェース」という項目がございます。

    「インターフェース」>「Displayインターフェース」をチェックつけて、
    「インターフェースを保存」して「モデルをビルド」してください。

    ビルトインインサイトにディスプレイに関するものが追加されていると思います。
    現時点ではこちらでOKです。

    ※補足:読み飛ばしてもOK
    AmazonAlexaスキルシミュレータ内で、ディスプレイありなしを判別をどうやって切り替えるのか調べた結果、こちらのインターフェースを切り替える事でシミュレータ内部処理的にディスプレイを持っているかどうかオンオフになる事がわかりました。
    最終的なデバックでディスプレイが無い機種の場合の挙動を調べたい時はこちらをオフにすれば、OKそうです。

    Alexaシミュレータ上でDisplayを有効化する。


    これは普段からご実施の方もいらっしゃるかもしれません。

    ページ右上にある ☑Device Displayにチェックを入れるだけです。
    そうすればAlexaシミュレータ上でディスプレイを出す事が出来ます。
    基本的にはいつもONでも良いと思います、非ディスプレイスキルだとしても。

    ※上記にも説明があるように
    ☑Device Displayはあくまで「ディスプレイをここの画面に表示する?しない?」の機能になります。
    内部的にディスプレイを所持しているかどうかをいじりたい場合はインターフェースを調整してください。


    Lambda関数側でディスプレイ対応のコーディングする


    こちらのURLにコード公開しました、
    URLを押すだけで閲覧可能です。


    ヘルパー関数を実装する


    ソースコード冒頭のconst宣言の直下に関数(function)を置きました。

    'use strict';
    const Alexa = require('ask-sdk');

    function supportsDisplay(handlerInput) {
      var hasDisplay =
        handlerInput.requestEnvelope.context &&
        handlerInput.requestEnvelope.context.System &&
        handlerInput.requestEnvelope.context.System.device &&
        handlerInput.requestEnvelope.context.System.device.supportedInterfaces &&
        handlerInput.requestEnvelope.context.System.device.supportedInterfaces.Display;
      return hasDisplay;
    }

    ちなみにV1時代とは書き方が変わっているので注意してください。

    ■読み飛ばしてもOK:AmazonAlexaでディスプレイありなし機種の判別方法
    こちらの関数の意味は、「ディスプレイありなしか識別しております」
    AmazonAlexaでは、ディスプレイありなし判定は入力JSONで識別可能です。


    機種によって入力JSONが変化するので、こちらの情報を取得しています。
    外部にヘルパー関数を出す意味としては、毎回記述していたら長くなってしまうので、
    外に出しましょうということです。

    こちらのヘルパー関数はそういう記述になります。

    ディスプレイを表示させるためのコーディング


    実行したい箇所に下記コーディングしてみましょう。

                if (supportsDisplay(handlerInput)) {
                      const myImage = new Alexa.ImageHelper()
                      .addImageInstance('https://i.imgur.com/rpcYKDD.jpg')
                      .getImage();

                      const primaryText = new Alexa.RichTextContentHelper()
                      .withPrimaryText('テストディスプレイメッセージ')
                      .getTextContent();

                      handlerInput.responseBuilder.addRenderTemplateDirective({
                      type: 'BodyTemplate1',
                      token: 'string',
                      backButton: 'HIDDEN',
                      backgroundImage: myImage,
                      title: "Pizza Suggest",
                      textContent: primaryText,
                });

    その結果が下記画像です。

    if (supportsDisplay(handlerInput)){


    ヘルパー関数で返ってきた値で条件分岐しています。
    ヘルパー関数めっちゃ便利。正分岐がディスプレイある場合、偽分岐が無しの場合です。
    なので、ディスプレイが無い場合は変な処理などは起こりません。

    余談:この後の処理


                      handlerInput.responseBuilder.addRenderTemplateDirective({

    上記コード後に、下記コードを通り、.getResponse();を通った段階で反映されます。


     return handlerInput.responseBuilder

                    .speak(speechText)
                    .reprompt('次にお調べする調味料があればおっしゃってください。')
                    .getResponse();

    結論

    • 最悪サンプルコードそのままでも行けちゃいます。
      • JSONエディターもindex.jsも先程のURLに公開しております。
      • ディスプレイ対応は別に難しい事じゃない。当記事読めば5分で出来る。


    【AmazonAlexaスキル開発】今後の投稿のSDK Verについて(Alexa SDK V1とV2について)

    アナウンス


    18年12月12日にブログ管理者がAmazon Alexaスキル開発に関して、
    Alexa SDK V1 から Alexa SDK V2に移行した為、
    今後当ブログでは基本的にV2での書き方を紹介していく事がメインとなっていきます。

    あらかじめご了承ください。

    また、Alexa SDK V1 時代の記事に関しては、
    Alexa SDK V2でも出来るように内容をリビルドして再投稿する予定です。

    当ブログのラベルにて、どのバージョンについて記事か判別可能ですので、
    ご活用ください。


    Alexa SDK V2とはそもそも何か


    解説記事を投稿しておりますのでご一読ください。

    【Alexaスキル開発】
    AlexaSDK V2 (Ver2/ask-sdk)にアップデートしよう!
    絶対挫折しない導入解説!移行のコツなども解説。【サンプルコード配布あり】 http://iga34engineer.blogspot.com/2018/12/alexaalexasdk-v2-ver2ask-sdk.html

    【Alexaスキル開発】AlexaSDK V2 (Ver2/ask-sdk)にアップデートしよう!絶対挫折しない導入解説!移行のコツなども解説。【サンプルコード配布あり】

    はてなブログ版記事を是非御覧ください!

    大幅に見やすく同じ内容が掲載しております!
    https://iga34engineer.hatenablog.jp/entry/2018/12/12/171404

    こういう人向け

    • AlexaSDK ?って何って人
    • Alexa SDK V1を使用していて、
      そろそろV2と言われるものに移行しないと…と考えている人
    • Alexa SDK V2を導入しようとしてターミナルで挫折した
    • Alexa SDK V2で、SDK V1コードを書き直してみたい人
    • Alexa SDK V2で動くサンプルコードが見たい

    おことわり


    世の中の多くのAlexa SDK V2導入記事では、
    ターミナルの使用や使いこなすのを前提として導入紹介する記事が多いですが、
    ターミナルを使わずファイル配布する事で導入のハードルを大幅に下げてます。

    ターミナルの使用に抵抗がある方でもご安心下さい。

    自分の開発環境がAlexa SDK V1/V2か、確認する方法


    //Alexa SDK V1 -> const Alexa = require('alexa-sdk');
    //Alexa SDK V2 -> const Alexa = require('ask-sdk');

    皆様のソースコードのはじめの一文を見れば判別可能です。
    上記を参考に見分けてみて下さい。

    Alexa SDK V2に移行するメリット

    • V1と比較して確実に長い間スポンサードされる。
    • 機能追加されている。
    特に大きな理由が無ければ最新開発環境でチャレンジするべきではないかと、思います。
    機能追加について具体的な内容を紹介します。
    • NPMパッケージとして提供される。
    • コア機能のみのパッケージも利用可能(v2)
    • 各種のリクエストをハンドラとして定義できる。
    • ハンドラは柔軟にグループ化や共通処理が可能(V2)
    • セッションごとやセッションを跨ぐデータの永続化が可能(V2)
    • DynamoDB等のDBが使用可能(V2)
    • すべての音声出力はSSMLでラップされる
    • 全てのLambdaイベントとコンテキストにアクセスが可能。
    • デバイスアドレスなどのAlexaサービスをラップし、簡単に利用可能(V2)
    • TypeScript定義ファイルと.d.tsファイルの読み取りが可能(V2)
    • anync/awaitが利用可能(V2)
    V1でわざわざ拡張する必要があった機能などが既に拡張されて対応されている、
    +αで出来る事が増えているという程度の認識でOKです。
    後程勉強して分かるようになったら上記読めば理解が深まるのではないでしょうか。

    Alexa SDK V2 導入について


    基本的にはAlexa SDK V1時代にご紹介した方法と同じにしてます。
    1. URLを共有するのでzipファイルをChromeブラウザでダウンロード。
    2. 「zipファイルをアップロード」
    3. いつも通りインラインコード編集でコーディング
    下記手順で問題ないと思いますが、
    より丁寧に説明しているSDK V1時代と同じ手順になりますので、
    過去記事をご活用ください。


    Alexa SDK V2 zipファイル 配布について


    こちらにzipファイルを置いております。

    Chromeブラウザでダウンロードしてください。解凍は不要です。

    Amazon lambda関数にzipファイルをアップロードする



    lambda関数でいつものように新規作成して、
    「コードエントリタイプ」から「.zipファイルをアップロード」で、
    先程ダウンロードしたzipファイルをそのまま上げて、
    ページ最上部の「保存」を押してください。

    Alexa SDK V2、フォルダの整形(お手間かけてすみません)


    File not found '/index.js'
    こちらのメッセージが出てくると思いますが、スルーでOKです。
    正しい位置に「/index.js」が存在していないのでシステムが見つけられませんでしたというメッセージですから。

    アップロードしたばかりのフォルダ階層をチェックしてみましょう。


    ▶test_01(関数名と同一)
     ▶フォルダ「_MACOSX」
     ▶フォルダ「AWS lambda アップロード」
       ▶フォルダ「node_modules」
       ▶ index.js
       ▶package.json

    上記のようなフォルダ構造になっております。

    上記の中で必要なのは色の付いたファイルだけになりますので、
    フォルダ「test_01」の直下に置いてあげましょう。

    一番上をファイルを押して一番下のファイルをshiftキー押せば複数選択が出来ます。
    青くした状態で「test_01」へドラック&ドロップしてください。
    ここらへんはパソコンのフォルダ整理と一緒だから大丈夫だよね・・・?

    フォルダ整理後


    上記3ファイルが階層が上がり、左側に移動したのが分かりますか。
    これでOKです。
    不要な「_MACOSX」「AWS lambdaアップロード」フォルダは右クリックで削除しましょう。
    deleteという選択肢でOKです。

    これで保存すればインラインコード編集が可能となります。

    Alexa SDK V2 コーディングについて


    SDK V1と変わらず、index.jsを編集すればOKです。
    試しにサンプルコード置いてみました。

    こちらに「index.js」と「JSONエディター」あげてます。

    Alexa SDK V2 で大きく変わった点、意識する所について

    • モジュール採用は SDK V2用にしてある必要がある
      • const Alexa = require ('ask-sdk');
    • ErrorHandler(エラーハンドラ)が優秀に。
      • 特定インテント内で処理がおかしくなった時でも、
        エラーハンドラに飛ばされるように。
      • V1時代の「スキルが応答しませんでした」が激減しました。
    • intentへのアクセスの仕方が変わったので注意。
    • 各インテントを実行する条件(canHandler)が設定された。
    • 各インテントを新規追加する時は注意。
    • tell、ask完全廃止。Responseオブジェクトで記載する。

    ErrorHandler(エラーハンドラ)が優秀に。


    ■Alexa SDK V1時代の「予期せぬ発話」
        //予期せぬ発話
        'Unhandled': function () {
            const speechOutput="すみません、まだわからないことが多くて。勉強しておきます。";
            this.emit(":ask",speechOutput,HELP_REPROMPT);
        },

    SDK V1時代にも上記のような「予期せぬ発話」はありましたが、
    実際ここのインテントが動く事はかなり少なかったです。
    ですので各自分の実装していたインテント内で、
    不適切な言葉が来たら弾くなどの処理を
    皆さん書かれていたかと思います。

    Amazon Alexa SDK V2 、ErrorHandler(エラーハンドラ)

    ■Alexa SDK V2.0時代のいわゆる「予期せぬ発話」

    const ErrorHandler = {
        canHandle() {
            return true;
        },
        handle(handlerInput, error) {
            return handlerInput.responseBuilder
                .speak('ただしく認識出来ませんでした。もう一度おっしゃっていただけますか。')
                .getResponse();
        },
    };

    こんな感じの記述になります。

    何が大きく変わったかというと、
    各自作したインテント内でエラー処理が行われると、
    エラーハンドラが呼び出されて処理
    してくれます。

    上記サンプルコード内で例を挙げるのであれば、
    唯一のインテント:orderSeasoningインテント内でconst massageに代入したら
    エラーになりますが、その場合はエラーハンドラが動作します

    SKD V1時代では「スキルが応答しませんでした」と完全に落ちていた処理が、
    ユーザーが聞いている分には違和感が起きないように処理が変更されました。

    これは一長一短ですが、基本的には良いことではないのでしょうか。
    デメリットは少しだけ感じたので後述します。

    intentなどの出力JSONへのアクセスの仕方が変わってます。


    SDK V1時代では下記のように記述されていたかと思います。
    • const intent =this.event.request.intent;
    しかし、SDK V2では下記のように記述してください。
    • const intent = handlerInput.requestEnvelope.request.intent;
    単純に書き換えて、頂くだけで結構です。
    SDK V2で 「this.event.request.intent」にアクセスすると落ちて、
    エラーハンドラに飛ばされるので注意して下さい。


    各インテントが実行してOKかどうかの条件が設定できる(canHandler)


    const LaunchRequestHandler = {
      canHandle(handlerInput) {
        return handlerInput.requestEnvelope.request.type === 'LaunchRequest';
      },
      handle(handlerInput) {
        const speechText = 'さじグラムプラスへようこそ。「醤油」のようにお調べしたい調味料をおっしゃってください。どの調味料をお調べしますか?';

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

    冒頭の赤字部分がそれに該当します。
    出力JSONで「LaunchRequest」が来た時だけ、このインテントを処理します、という
    内容です。

    たとえば自作したインテント「orderSearsoning」であれば、
    const orderSeasoning = {
        canHandle(handlerInput) {
            return handlerInput.requestEnvelope.request.type === 'IntentRequest'
                && handlerInput.requestEnvelope.request.intent.name === 'orderSeasoning';
        },

    と記述いたします。

    intentを新しく作る場合はexports.handlerにしっかり追記する。


    仮に「addIntent」インテントを追加する場合は下記を追加します。

    const addIntent = {
        canHandle(handlerInput) {
            return handlerInput.requestEnvelope.request.type === 'IntentRequest'
                && handlerInput.requestEnvelope.request.intent.name === 'addIntent';
        },
        handle(handlerInput) {
                //addIntent内で実行したい処理
                return handlerInput.responseBuilder
                    .speak('テスト')
                    .reprompt('テスト')
                    .getResponse();
                }

     };

    その後、最下部に記載してあるexports.handlerにインテント名を追記してください。


    exports.handler = Alexa.SkillBuilders.standard()
      .addRequestHandlers(LaunchRequestHandler, orderSeasoning, addIntent)
      .addErrorHandlers(ErrorHandler) // これを追加
      .lambda();

    お手数かもしれませんが、必要な記載なので宜しくお願いします。

    tell、ask廃止 → Responseオブジェクトでの書き方に統一。


    【AmazonAlexaスキル開発】Responseオブジェクトについて考えてみる(ask,tellなど) http://iga34engineer.blogspot.com/2018/12/amazonalexaresponseasktell_10.html

    詳細につきましては上記記事を一度ご一読ください。

                return handlerInput.responseBuilder
                    .speak('こんにちは'))
                    .reprompt('次にお調べする調味料があればおっしゃってください。')
                    .getResponse();

    Alexa SDK V1時代と同じように、
    Responseオブジェクトは上記のようにご記載ください。
    次の見出しで書き方についてご紹介いたします。

    SDK V2で「会話(セッション)を終了させる場合」(以前のtell)


                return handlerInput.responseBuilder
                    .speak('こんにちは')
                    .getResponse();

    レスポンスには「shouldEndSessionキー」が含まれていない為、
    trueとなり、アレクサの発話後、直ちにセッションは終了します。
    以前tellで書かれていた内容になります。


    SDK V2で「会話(セッション)を継続する場合」(以前のask)


                return handlerInput.responseBuilder
                    .speak('こんにちは')
                    .reprompt('次にお調べする調味料があればおっしゃってください。')
                    .getResponse();

    レスポンスには「shouldEndSessionキー」がfalseとなり、
    アレクサの発話後、会話は継続します。
    そしてアレクサの発話の後、ユーザーからの返答を待つようになります。
    もし返答が無ければreprompt()内を発話します。
    それでもユーザーの返事がなければセッションが終了します。

    余談(SDK V1 互換モジュールについて)




    一応こういうモジュールも無くはないですが、
    最終的にはSDK V2に統合されると思うので、
    まずはV2での書き方を取り扱ってみました。

    配布ファイルには「ask-sdk-vladaptor」も入っているので
    モジュール読み込みして実行は出来る筈ですので、
    興味のある方は試してみて下さい。

    余談(npmインストール手順について)


    読み飛ばしてOKですし、理解する必要はありません。
    ただ、ターミナルを利用したインストール手順など知りたい方もいらっしゃると思うので
    一応簡単に手順だけはご紹介だけはしておきます。
    1. npmのバージョンチェック
    2. 「npm install --save ask-sdk」でインストール
    3. 生成された「node_modules」内のフォルダ内ファイルの中から
      下記ファイルをコピー
      • ask-sdk
      • ask-sdk-core
      • ask-sdk-dynmodb-persistence-adapter
      • ask-sdk-model
      • ask-sdk-runtime
    4. 以前使っていたSDK V1時代の「node_modules」内を全削除して、
      上記ファイルをコピペ
    5. zip化してアップロード(以降は当記事で、みなさんにご紹介している方法)

    まとめ

    • Alexa SDK V2、導入実際頑張ったら難しくなかった。
    • 当記事読んだら更に楽だと思う!
    • みんなでAlexa SDK V2にアップデートしましょう!