問題とその解決策
間違った応答による誤操作
「就寝前の薬を飲んでください」に対して間違って「夜のお薬飲んだよ」と答えてしまい、夜の予定が「(済)」に変わってしまった。これは実験2日目に起きたのと同じインシデント。
対応策
WebAPI (A2)のNotifyNotTaking()関数でカレンダーのタイトルと色を変更するが、ここで現在時刻が開始時刻(start_time)~終了時刻(end_time)の間にあるかどうか調べて、もしなければ時間外である旨応答する。
ここで、GASからどうやって Google Home に喋らせるかが問題になる。
Google Homeにプッシュ通知する製品にGHKitというものがあるらしい。「外部イベントに応じたプッシュ通知で、好みのメッセージをタイミングよくGoogle Homeで音声再生します!」という触れ込みである。
また、GAS、firebase、google-home-notifierを連携させて指定した時刻になるとGoogle Spreadsheetに入力したテキストをGoogle Homeに喋らせるアプリの開発例がある。
我々の開発しているシステムは、firebaseは使っていないが、どちらかというと後者に近い。ベアボーン上で稼働している発話PUSHサーバ(example.js)をngrokでインターネットから利用可能なWebAPIにして、GASから発話依頼するのが最も簡単な方法だろう。
手順はこうである。
- WebAPI (A2) はIFTTTからのカレンダー更新要求(Webhooksを使ってWebAPI を叩く)を待ち受ける。
- IFTTTからカレンダー更新要求が届いたら、現在時刻と開始時刻(start_time)~終了時刻(end_time)を比較し、時間外であればベアボーン上で稼働している発話PUSHサーバ(example.js)へ発話依頼する。
問題点
ここで問題が発生した。example.js起動時に出力されたngrokが払い出す公開URLの期限が8時間に制限されていることが分かった。
これを回避するには、ngrokのサイトからアクセストークンを取得すればよい。そして発話PUSHサーバ(~/pill-reminder/example.js)のHTTPリクエストをListenするプログラムで下記のようにアクセストークンを指定してngrok.connect()関数を実行する。
/** * serverPortをlisten * */ app.listen(serverPort, function () { // トークンを設定 ngrok.authtoken(token); // 現状のセッションを一旦終了 ngrok.kill(); ngrok.connect({authtoken: token, addr: serverPort, region: 'jp'}, function (err, url) { //修正 console.log('Endpoints:'); console.log(' http://' + ip + ':' + serverPort + '/google-home-notifier'); console.log(' ' + url + '/google-home-notifier'); console.log('GET example:'); console.log('curl -X GET ' + url + '/google-home-notifier?text=Hello+Google+Home'); console.log('POST example:'); console.log('curl -X POST -d "text=Hello Google Home" ' + url + '/google-home-notifier'); }); })
リスト1.HTTPリクエストのlisten部分
ここで、tokenはngrokのサイトから取得したAuthtokenである。
※リスト1はngrokのconnectメソッドを利用して公開URLを払い出している。これはv2.3.0の使用方法である。
一方,ngrokを非同期で起動して公開URLを払い出すサンプルがネット上にいくつかある。これはv3.2.5の書き方である。それに従ってリスト1を書き換えたものを以下に示す。
app.listen(serverPort, function () { putLog('Endpoints:'); putLog(' http://' + ip + ':' + serverPort + '/google-home-notifier'); connectNgrok(serverPort).then(url => { putLog(' ' + url + '/google-home-notifier'); putLog('GET example:'); putLog('curl -X GET ' + url + '/google-home-notifier?text=Hello+Google+Home'); putLog('POST example:'); putLog('curl -X POST -d "text=Hello Google Home" ' + url + '/google-home-notifier'); }); }) /** * ngrokを非同期で起動 */ async function connectNgrok(port) { try { let url = await ngrok.connect({ addr: port, region: 'jp', configPath: '~/.ngrok2/ngrok.yml', authtoken: token }); return url; } catch (err) { putLog(err.name + ': ' + err.message); } }
リスト1b.
最初、ngrokのurl払い出しがなかなかうまくいかなかった。試行錯誤しているうち、ngrok.connectメソッドのパラメタに
region: 'jp',を追加すると払い出しができるようになった。regionに'jp'(日本)を設定したのが功を奏したのではないだろうか。近くのサーバを使用することでレスポンスが改善され、ngrok.connectが正しく動作したものと思われる。
configPath: '~/.ngrok2/ngrok.yml',
ここで教訓になったことは、ngrok v2.3.0とv3.5.0では公開URLの払い出し方法が異なるということだ。
次にWebAPI (A2) を以下のように修正する。
/** * 「[朝|昼|夜|就寝前]のお薬飲んだよ」に対して服用済みに変えるファンクション * * @param {String} timing [朝|昼|夜|就寝前] * */ function NotifyNotTaking(timing) { //var timing = e.parameter.timing; //「の薬」とつくイベントの取得 //var timing =e.parameter.timing; debug('NotifyNotTaking: timing=' + timing); // 当日の予定の中から timing (朝|昼|夜|就寝前)を含む予定を検索 var events = CalendarApp.getDefaultCalendar().getEventsForDay( new Date(), {search: timing} // {search:'夜の食後の薬'} ); // 取得した予定からターゲットの予定を探してタイトルと色を変更 events.forEach(function(event,i,array){ var title = event.getTitle(); debug('NotifyNotTaking: title=' + title); if(title.match(/^.の食.の薬\(.+\)$/) || title.match(/就寝前の薬\(.+\)$/)) { // 現在時刻と開始時刻(start_time)~終了時刻(end_time)を比較し、時間内であればタイトルを(済)にし、色を変え、LINEへ通知する if (withinTime(event.getStartTime(), event.getEndTime())) { // タイトルの変更 // ”(”の文字の位置を取得する(http://cya.sakura.ne.jp/js/string.htm#indexOf) result = event.getTitle().indexOf("("); debug('NotifyNotTaking: result=' + result); // "("以降の文字を切り取る。 results = event.getTitle().substr(0,result); debug('NotifyNotTaking: results=' + results); // タイトルを○○の薬(済)に変更する。 event.setTitle(results+'(済)'); // 色を青に変更する。 event.setColor(CalendarApp.EventColor.BLUE); //LINEに送るメッセージ LINE_notify(timing+'のお薬を飲みました。'); } else { // 現在時刻と開始時刻(start_time)~終了時刻(end_time)を比較し、時間外であればベアボーン上で稼働している発話PUSHサーバ(example.js)へ発話依頼する。 var message = '時間外に「' + timing + 'のお薬をのみました」とのメッセージを受け取りました。この要求を無視します。'; debug('NotifyNotTaking: ' + message); LINE_notify(message); } } }); }; /** * 現在時刻が開始時刻(start_time)~終了時刻(end_time)の間にあるかどうかをチェックする * @param {Date} start_time 日付 * @param {Date} end_time 日付 * @return {Boolean} true:現在時刻が開始時刻(start_time)~終了時刻(end_time)の間にある * false: 〃 間にない */ function withinTime(start_time, end_time) { var current_time = new Date(); var result = (start_time <= current_time && current_time <= end_time) ? true : false; return result; } // google-home-notifyにメッセージを送るファンクション function GOOGLE_HOME_notify(message){ var options = { "method" : "post", "payload" : "text=" + message }; // ngrokで払い出された公開用URLxxxxxxxxを書き直す UrlFetchApp.fetch("https://xxxxxxxx.ap.ngrok.io/google-home-notifier",options); }
リスト2.WebAPI (A2) の修正
GOOGLE_HOME_Notify()のUrlFetchApp.fetchメソッドのurlはベアボーン上で稼働する発話PUSHサーバのurlである。これはngrokによって払い出されたものを使う。したがって,発話PUSHサーバが異なれば,当然このurlも書き換えなければならない。
0 件のコメント:
コメントを投稿