LoginSignup
17
25

More than 5 years have passed since last update.

音声(Alexa)でSpreadsheetに赤ちゃんのトイレの時間を記録するまで

Last updated at Posted at 2018-05-05

概要

Alexa を通じて音声で自前の Spreadsheet に記録できるようにするまでにいろいろあったのでメモ。

ちなみに、IFTTT を使えば繋ぎこみ自体は簡単なのだが、文章になってない日本文の認識にかなり難があり、断念した。Alexa に直接仕込んだほうが遥かにいい。

これを実現するために下記の構造を作る:

Alexa 実機
↓
Alexa Skills Kit
↓
AWS Lambda
↓
OAuth 2.0
↓
Google Cloud Platform
↓
Google Apps Script
↓
Google Spreadsheet

1-1. [Spreadsheet] 記録用の Spreadseet を用意

記録するためのスプレッドシートを用意しておく。中身は空っぽで良い。末尾に1行ずつ追加していく想定。

↓こういう感じ

2018/05/05 13:41:52 うんち 大

1-2. [Spreadsheet] 書き込むスクリプト作成

1行追記するためのスクリプトを用意する。
ツール > スクリプト エディタ から下記のソースを追加。Execution API と名付けて保存。

GoogleAppsScript
function appendRecords(data){
  var size = data.query_result.size;
  var mySheet = SpreadsheetApp.getActiveSheet();
  mySheet.appendRow([new Date(), 'うんち ' + size]);
}

function test_appendRecords(){
  appendRecords({
    query_result: {
      size: ''
    }
  });
}

test_appendRecords を実行して行が追加されることを確認する。

1-3. [Spreadsheet] スクリプトをAPIとして公開する

外からこのAPIを叩けるようにする。
Google Apps Script の画面から 公開 > 実行可能 API として導入... を選択。

バージョン は適当に。
スクリプトにアクセスできるユーザー自分のみ に。

更新 のクリックでリロードみたいに画面更新されるが、自分の入力したバージョンの番号がプルダウンに加わっていることを確認する。

1-4. [Spreadsheet] 各種情報をメモしておく

あとで使うので。

Google Apps Script の画面から:

  • API ID : 公開 > 実行可能 API として導入... の画面の 現在の API ID
  • プロジェクトキー : ファイル > プロジェクトのプロパティ の画面の プロジェクト キー(サポート終了)
  • スクリプト ID : ファイル > プロジェクトのプロパティ の画面の スクリプト ID
  • OAuth スコープ : ファイル > プロジェクトのプロパティ の画面の スコープ タブのURL

1-5. [Spreadsheet] OAuth2 の設定画面に飛ぶ

API を叩く際の認証設定をする(ための画面に飛ぶ)。
Google Apps Script の画面から リソース > Cloud Platform プロジェクト... を選択。
ポップアップする画面の このスクリプトが現在関連付けられているプロジェクト 欄に Execution API - project-id-xxxxxxxxxxxxxxxxx のようなリンクが表示されているはず。これをクリック。

2-1. [Cloud Platform] Apps Script API を有効化

Spreadsheet の API をそもそも叩けるように有効化する。
左メニューから API とサービス > ライブラリ を選択して、Apps Script API を検索。
1件ヒットするので、それをクリック。有効化する。

2-2. [Cloud Platform] 認証情報の作成

API を叩く際の認証情報を作っておく。

左メニューから API とサービス > 認証情報 を選択して、認証情報を作成 プルダウンから OAuth クライアントID を選択。

下記のように選択:

  • アプリケーションの種類: ウェブアプリケーション
  • 名前: 適当
  • 制限事項:
    • 承認済みの JavaScript 生成元: https://developers.google.com
    • 承認済みのリダイレクト URI: https://developers.google.com/oauthplayground

JSON ができるので、ダウンロードしておく。

3-1. [OAuth 2.0 Playground] 画面開く

アクセスに必要なトークンを発行するための画面を開く。
https://developers.google.com/oauthplayground

3-2. [OAuth 2.0 Playground] 認証情報設定

前述2-2で発行した認証情報をセットする。
画面右上の Auth 2.0 Configration を開いて

  • Use your own OAuth credentials を ON
  • OAuth Client ID 欄に前述2-2でダウンロードしたJSON内の client_id の値を記入
  • OAuth Client secret 欄に前述2-2でダウンロードしたJSON内の client_secret の値を記入

3-3. [OAuth 2.0 Playground] トークン発行

アクセスに必要なトークンを発行する。
下記のように入力:

  • Step1:
    • https://www.googleapis.com/auth/spreadsheets (前述1-4のOAuth スコープ) を入力
    • Authorize APIs ボタンをクリック
  • Step2:
    • Authorization code: (上記のボタンで自動入力)
    • Exchange authorization code for tokens ボタンをクリック
    • Refresh token: (上記のボタンで自動入力)
    • Access token: (上記のボタンで自動入力)

あとで使うので Refresh tokenAccess token をメモしておく。

3-4. [OAuth 2.0 Playground] アクセステスト

下記のように入力:

  • Step3:
    • HTTP Method: POST
    • Request URI: https://script.googleapis.com/v1/scripts/{前述1-4のプロジェクトキー}:run
    • Enter request body クリックして下記RequestBodyを設定
    • Content-Type: application/json
    • Send the request クリック

{前述1-4のプロジェクトキー} は適宜置き換えること。

RequestBody
{
  "function": "appendRecords",
  "parameters": [{
    "query_result": {
      "size": "大"
    }
  }],
  "devMode": true
}

シートに行が追加されればOK。

4-1. [Alexa Skills Kit] 画面開く

Alexa Skills Kit を使って対話モデルを作成する。
https://developer.amazon.com/ja/alexa-skills-kit
にアクセスしてサインインする。

※注意: サインインするメアドは必ず所有してる Alexa で使ってるメアドと同じでないといけない。同じでないと作り直しになるので注意。

4-2. [Alexa Skills Kit] スキル開発を始める

  • スキル開発を始める クリック
  • スキルの作成 クリック
  • スキル名 は適当にあとで識別できるものを(でもあとで変えられない)
  • スキル作成時のデフォルト: 日本語
  • カスタム選択 して スキルを作成 クリック

4-3. [Alexa Skills Kit] 対話の設定

JSONエディター開いて下記入力:

{
    "interactionModel": {
        "languageModel": {
            "invocationName": "赤ちゃんのトイレ",
            "intents": [
                {
                    "name": "AMAZON.CancelIntent",
                    "samples": [
                        "キャンセル"
                    ]
                },
                {
                    "name": "AMAZON.HelpIntent",
                    "samples": [
                        "使い方",
                        "ヘルプ"
                    ]
                },
                {
                    "name": "AMAZON.StopIntent",
                    "samples": [
                        "ありがとう",
                        "終了",
                        "ストップ"
                    ]
                },
                {
                    "name": "RecordToilet",
                    "slots": [
                        {
                            "name": "Size",
                            "type": "Size"
                        }
                    ],
                    "samples": [
                        "{Size} を記録"
                    ]
                }
            ],
            "types": [
                {
                    "name": "Size",
                    "values": [
                        {
                            "id": "小",
                            "name": {
                                "value": "小",
                                "synonyms": [
                                    "しょう"
                                ]
                            }
                        },
                        {
                            "id": "中",
                            "name": {
                                "value": "中",
                                "synonyms": [
                                    "ちゅう"
                                ]
                            }
                        },
                        {
                            "id": "大",
                            "name": {
                                "value": "大",
                                "synonyms": [
                                    "だい"
                                ]
                            }
                        }
                    ]
                }
            ]
        }
    }
}
  • invocationName は「Alexa, 〇〇で大を記録」という発言の〇〇の部分になるので適宜変更。

4-4. [Alexa Skills Kit] スキルID のメモ

エンドポイントAWS LambdaのARN を選択。
スキルID はあとで AWS 側で使うのでメモ。

4-5. [Alexa Skills Kit] いったんビルド

ここまでをいったんビルドしておく。

5-1. [AWS/Lambda] 関数の作成

AWSで Alexa からの要求を処理する Lambda 関数を作成する。
このときの AWS のアカウントのメアドは、Alexa のメアドと違っていても構わない。

  • 関数の作成
  • 設計図 を選び、alexa-skill-kit-sdk-factskill を選択。
  • 名前、ロールは適当に。
  • 関数の作成 クリック

5-2. [AWS/Lambda] Node.JS の記述

  • アクション > 関数のエクスポート > デプロイパッケージのダウンロード でソースコードを取り出す。
  • 解凍したら package.json のあるフォルダで下記のコマンド実行
$ npm install --save googleapis
$ npm install --save google-auth-library
  • 完了したら index.js を下記のように書き換える:
/* eslint-disable  func-names */
/* eslint quote-props: ["error", "consistent"]*/
/**
 * This sample demonstrates a simple skill built with the Amazon Alexa Skills
 * nodejs skill development kit.
 * This sample supports multiple lauguages. (en-US, en-GB, de-DE).
 * The Intent Schema, Custom Slots and Sample Utterances for this skill, as well
 * as testing instructions are located at https://github.com/alexa/skill-sample-nodejs-fact
 **/

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


//=========================================================================================================================================
const {google} = require('googleapis');
const {OAuth2Client} = require('google-auth-library');

const writeToSpreadsheet = (size) => {
    let scriptId = process.env['GOOGLE_SCRIPT_ID'];
    let functionName = "appendRecords";
    const auth = new OAuth2Client(process.env['GOOGLE_CLIENT_ID'], process.env['GOOGLE_CLIENT_SECRET']);
    auth.setCredentials({
        access_token: process.env['GOOGLE_ACCESS_TOKEN'],
        refresh_token: process.env['GOOGLE_REFRESH_TOKEN']
    });
    const script = google.script('v1');
    return new Promise((resolve, reject) => {
        script.scripts.run({
            auth: auth,
            scriptId: scriptId,
            resource: {
                function: functionName,
                parameters: [{
                    "query_result": {
                        "size": size
                    }
                }],
                devMode: true
            }
        }, (err, result) => {
            if (err) {
                console.log(err);
                reject(new Error(err));
            } else {
                resolve(result);
            }
        });
    });
};
//=========================================================================================================================================



//Replace with your app ID (OPTIONAL).  You can find this value at the top of your skill's page on http://developer.amazon.com.
//Make sure to enclose your value in quotes, like this: const APP_ID = 'amzn1.ask.skill.bb4045e6-b3e8-4133-b650-72923c5980f1';
const APP_ID = process.env['ALEXA_APP_ID'];

exports.handler = function (event, context, callback) {
    const alexa = Alexa.handler(event, context, callback);
    alexa.APP_ID = APP_ID;
    const handlers = {
        'RecordToilet': function () {
            //let size = event.request.intent.slots.Size.name; //キー名
            // let size = event.request.intent.slots.Size.value;
            let size = event.request.intent.slots.Size.resolutions.resolutionsPerAuthority[0].values[0].value.id;
            console.log('size: ' + size);
            writeToSpreadsheet(size).then(() => {
                this.emit(':tell', size + 'を書き込みました');
            }).catch(error => {
                context.fail(error);
                this.emit(':tell', '書き込みに失敗しました');
            });
        },
        'AMAZON.HelpIntent': function () {
            this.emit(':tell', '大を記録、中を記録、小を記録のいずれかを言ってください');
        },
        // キャンセル(デフォルト)への返答
        'AMAZON.CancelIntent': function () {
            this.emit(':tell', 'キャンセルします');
        },
        // 対応できないアクションへの返答
        'AMAZON.StopIntent': function () {
            this.emit(':tell', 'もう一度お願いします');
        },
    };
    alexa.registerHandlers(handlers);
    alexa.execute();
};

5-3. [AWS/Lambda] Node.JS のアップロード

  • node_modules, index.js, package.json を選択して右クリック > 送る(N) > 圧縮 (zip 形式) フォルダー で圧縮

※ 注意: このZipを解凍したときに第一階層にフォルダができる形だとエラーになる。第一階層に上記3つが並ぶように、上記のように圧縮すること。

  • 関数コード > コード エントリ タイプ プルダウンで .ZIP ファイルをアップロード を選び、アップロード ボタンで上記の zip ファイルをアップロードし、保存 ボタンを押す。

5-4. [AWS/Lambda] 環境変数

Node.JS で使う環境変数を設定する。
ソース入れ替えは面倒なので、外部要因で変わりうる部分は環境変数に切り出しておくと楽。

下記の設定が必要:

変数名 値の元
GOOGLE_SCRIPT_ID 前述1-4の スクリプト ID
GOOGLE_CLIENT_ID 前述2-2でダウンロードしたJSON内の client_id
GOOGLE_CLIENT_SECRET 前述2-2でダウンロードしたJSON内の client_secret
GOOGLE_ACCESS_TOKEN 前述3-3の Access token
GOOGLE_REFRESH_TOKEN 前述3-3の Refresh token
ALEXA_APP_ID 前述4-4の スキルID

5-5. [AWS/Lambda] ARNのメモ

ARN を Alexa 側にもっていくのでメモ。
画面右上の arn:aws:lambda:ap-northeast-1:xxxxxx:function:xxxxxxxxxxxxxxxxx の文字列。

5-6. [AWS/Lambda] トリガーの設定

  • トリガーの追加 から Alexa Skills Kit を選ぶ。
  • スキル ID 検証 に前述4-4の スキルID の値を入力。
  • 保存

6-1. [Alexa Skills Kit] ARNを設定

  • 前述5-5の ARN を、エンドポイント > AWS LambdaのARN > デフォルトの地域 に転記。
  • エンドポイントを保存

6-2. [Alexa Skills Kit] ビルド

ここまでの内容をビルドして使えるようにする。

7-1. [Alexa Skills Kit] シミュレータでのテスト

  • Alexa シミュレータ で「赤ちゃんのトイレで大を記録」と入力してみる。
  • 「大を記録しました」と応答が来るのを確認
  • Spreadsheet を開いてデータが追記されているのを確認

8-1. [Amazon Alexa] 実機の設定画面開く

実機にスキルを追加するため、
https://alexa.amazon.co.jp
にアクセスして Alexa 実機の設定画面を開く。

スキル > 有効なスキル > DEV スキル タブ
に今回追加したものがあるので有効にする。

これで完了です!

17
25
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
17
25