絖綛 N@i.jp  昨日:00019893
 今日:00001916
 総計:00146469
keywords
管理者専用
  Post   Add link   Control Panel 































新しいトピック
最新:10/01 12:07


新しいコメント
最新:07/28 16:47






管理人へMAIL

プライバシーポリシー

Alexa(amazon Echo)のIRKitスキルを作る(7)

Lambda関数に少し手を加えます


3.8 Lambda関数に少し機能を追加する

 先に「3.3 AWS Lambdaを作成する」で作成したmyIRKitControl関数は、本当に必要最小限のことしか作ってありませんでした。そこで、少しだけ機能を拡張してみます。付け加える機能は、

  1. ログ出力
  2. 応答カード
  3. IRKitスキル以外のアプリからの呼び出しへの対処

です。

1. ログ出力
 ログとは関数が呼び出されたとき、通った経路やその時の状態をAWS CloudWatch Logに出力しておく機能です。ログを見ることによって、スキルが実行された履歴を知ることができたり、エラーが発生したときの原因調査が楽になります。そのため、ログ出力は関数の要所要所に仕込んでおく必要があります。
 ログ出力処理はconsole.log("ログに出力する文字列")です。

2. 応答カード
 応答カードはAlexaアプリのホーム画面に、

こんな具合に表示するものです。本当は音声だけでは通知しきれない詳細情報(一週間の天気予報など)や画像などを表示するための機能なのですが、アプリに応答結果が何も表示されないのも寂しいので・・・
 応答カードを表示するには、音声で応答していたthis.emit(":tell", "応答メッセージ")の部分を、this.emit(":tellWithCard", "応答メッセージ", "応答カードのタイトル", "応答カードに表示するメッセージ")に書き換えるだけです。これは最もシンプルな応答カードで、他にも画像入りの応答カードを表示させることなどもできます。詳しくはスキルの応答にカードを追加するを参照してください。

3. IRKitスキル以外のアプリからの呼び出しへの対処
 myIRKitControl関数はIRKitスキルから呼び出されることを前程に作られていますが、万一他のアプリから呼び出されたらどうなるでしょう?Lambda の ARNが他者に知られてしまったら、勝手に照明を操作されてしまいます。これを防ぐには、IRKitアプリ以外から呼び出されたら実行が失敗するようにしておいた方が安全です。このためには、呼び出し元のアプリケーションIDがIRKitスキルのアプリケーションIDと等しいかどうかで判定します。

 以上を追加したmyIRKitControl関数は、こんな感じになりました。

'use strict';
 
const Alexa = require('alexa-sdk');
 
const APP_ID = process.env.IRKit_APPID;  // IRKit app ID

// IRKitデバイス情報
const clientkey = process.env.My_ClientKey;
const deviceid = process.env.My_DeviceID;

// 応答カードのタイトル
const cardTitle = "IRKit";

// エラー情報
const sendError = Error("Send error");
const unknownError = Error("Unknown error");

// リモコン信号
const lightIR = {
  on: { "format":"raw",
        "freq":38,
        "data":[18031,8755,1275,1037,1275,3228,1232,1037,1190,1037,1190,1037,
                1190,1037,1190,1037,1190,3228,1232,3228,1232,1037,1190,3228,
                1232,3228,1232,1037,1190,3228,1232,3228,1232,1037,1190,3228,
                1232,3228,1232,3228,1232,3228,1232,3228,1232,1037,1190,1037,
                1190,1037,1190,1037,1190,1037,1190,1037,1190,1037,1190,1037,
                1190,3228,1232,3228,1232,3228,1232,65535,0,18031,18031,4251,
                1190,65535,0,65535,0,60108,18031,4251,1190]
  },
  off: {"format":"raw",
        "freq":38,
        "data":[18031,8755,1190,1073,1190,3228,1190,1073,1073,1073,1073,1073,
                1190,1073,1073,1073,1073,3228,1190,3228,1190,1073,1073,3228,
                1190,3228,1190,1073,1073,3228,1190,3228,1190,1073,1073,1073,
                1073,3228,1190,3228,1190,3228,1190,3228,1190,1073,1073,1073,
                1073,1073,1073,3228,1190,1073,1190,1073,1190,1073,1190,1073,
                1190,3228,1150,3228,1150,3228,1150,65535,0,18031,18031,4400,
                1190,65535,0,65535,0,60108,18031,4251,1275]
  }
};

// 言語リソース
const languageStrings = {
    "ja": {     // 日本語
        translation: {
            WELCOME_MESSAGE: "ようこそ、アイアールキットリモコンで家電を制御します。何をしましょうか?",
            HELP_MESSAGE:    "アイアールキットリモコンで家電を制御します。",
            CANCEL_MESSAGE:  "アイアールキットリモコンを中止します。",
            STOP_MESSAGE:    "アイアールキットリモコンを終了します。",
            UNKNOWN_MESSAGE: "すみません、分かりません。",
            RETRY_MESSAGE:   "すみません、もう一度お願いします。",
            LIGHT_ON:        "明りを点けます。",
            LIGHT_OFF:       "明りを消します。",
            LIGHT_USAGE:     "明りを点けて、明りを消して、などと指示してください。",
        },
    },
};

// リモコン信号の送信
function sendIR(ir, callback) {
    var https = require("https");
    var qs = require("querystring");
    var postData = qs.stringify({
        clientkey: clientkey,
        deviceid: deviceid,
        message: JSON.stringify(ir)
    });
    var req = https.request({
        host: "api.getirkit.com",
        path: "/1/messages",
        method: "POST",
        headers: {
            "Content-Type": "application/x-www-form-urlencoded; charset=utf-8",
            "Content-Length": postData.length
        }
    }, function (res) {
        var result = res.statusCode;
        if (result == 200) {
            console.log("res.statusCode=" + result);    // ログ出力
            callback(null, "success");                  // 送信成功
        }
        else {
            console.log(postData);                      // ログ出力
            console.log("res.statusCode=" + result);    // ログ出力
            callback(sendError);                        // 送信エラーを通知
        }
    });
    req.write(postData);
    req.end();
}

function onLightOn(callback) {
    sendIR(lightIR.on, callback);	    // 明かりを点ける
}

function onLightOff(callback) {
    sendIR(lightIR.off, callback);	    // 明かりを消す
}


const handlers = {
    'LaunchRequest': function () {
        this.emit(":ask", this.t("WELCOME_MESSAGE"));
    },
    'LightControl': function () {
        // 照明インテントのハンドラ
        var session = this.event.session;			// セッション
        var apid = session.application.applicationId;	// アプリケーションID
        var now = this.event.request.timestamp;		// 日付&時刻
        var intent = this.event.request.intent;		// インテント
        var onoff = intent.slots.Switch.value;		// スロット{Switch}の値
        var msg;

        console.log("LightControl: Switch=" + onoff);	// ログ出力
        switch(onoff){
            case 'オン':
            case '点け':
            case '点灯':
                msg = this.t("LIGHT_ON");
                this.emit(":tellWithCard", msg, cardTitle, msg);
                onLightOn(this.callback);
                break;
            case 'オフ':
            case '消す':
            case '消し':
            case '切る':
            case '消灯':
                msg = this.t("LIGHT_OFF");
                this.emit(":tellWithCard", msg, cardTitle, msg);
                onLightOff(this.callback);
                break;
            default:
                msg = this.t("RETRY_MESSAGE") + this.t("LIGHT_USAGE");
                this.emit(":ask", msg);
                break;
        }
    },
    'AMAZON.HelpIntent': function () {
        this.emit(":ask", this.t("HELP_MESSAGE"));
    },
    'AMAZON.CancelIntent': function () {
        this.emit(":tell", this.t("CANCEL_MESSAGE"));
        this.callback(null);
    },
    'AMAZON.StopIntent': function () {
        this.emit(":tell", this.t("STOP_MESSAGE"));
        this.callback(null);
    },
    'Unhandled': function() {
        this.emit(":ask", this.t("UNKNOWN_MESSAGE"));
    }
};

// Lambda関数ハンドラー
// パラメタ
//  event: イベントデータ
//  context: ランタイム情報
//  callback: 呼び出し元へ情報を返すためのコールバック関数
exports.handler = function (event, context, callback) {
    var session = event.session;                    // セッション
    var apid = session.application.applicationId;   // アプリケーションID
    const alexa = Alexa.handler(event, context, callback);

    if (apid != APP_ID) {
        context.fail("Invalid Application ID");	    // IRKitスキル以外からの呼び出しは拒否
        return;
    }
    alexa.appId = APP_ID;
    console.log("event.session.application.applicationId=" + apid); // ログ出力
    // To enable string internationalization (i18n) features, set a resources object.
    alexa.resources = languageStrings;  // 言語リソースを設定
    alexa.registerHandlers(handlers);   // ハンドラを登録
    alexa.execute();
};

 他にもエラー処理などはもっと作り込んだ方が良いですね。今はIRKitでリモコン信号の送信が失敗しても、それをログでしか知ることができません。このへんは後々の課題にでも・・・


< 過去の記事 [ 12月の プログラミング リスト ] 新しい記事 >

2017 calendar
12月
12
3456789
10111213141516
17181920212223
24252627282930
31


掲示板
最新:08/15 17:19


GsBlog was developed by GUSTAV, Copyright(C) 2003, Web Application Factory All Rights Reserved.