問題とその解決策
予定を変更する
WebAPI (A2) の修正
実験2日目のブログで「予定を変更する」機能があるといいのにと書いたが、それを実装した。その方法はGoogle Homeから服用のお知らせがあったのち「後でお薬飲む」と伝えると、Googleカレンダーの開始日・終了日を指定した時間(今回は10分間)だけ遅らせるというものである。
修正対象のプログラムは、WebAPI (A2) である。まず、HTTPリクエストのエンドポイントとなる関数 doPost は以下のように修正する。
/**
* IFTTTからのリクエストに応じてGoogleカレンダーを服用済みに更新する
* @param {Object} e POSTメソッドで送られてくるデータ
* delay: 10 (開始を10分間遅らせる)
* timing: [朝|昼|夜|就寝前]
*
*/
function doPost(e) {
var params = JSON.parse(e.postData.getDataAsString()); // ※
if (params.timing) {
//カレンダーのタイトルと色を変える
var timing = params.timing; // => [朝|昼|夜|就寝前]が取れる
NotifyNotTaking(timing);
} else if (params.delay) {
//開始時刻を遅らせる
var delay = parseInt(params.delay); // => 遅らせる時間[分]
delayStartTime(delay);
}
var out = ContentService.createTextOutput();
//Mime TypeをJSONに設定
out.setMimeType(ContentService.MimeType.JSON);
//JSONテキストをセットする
result = {
status: 'OK'
};
out.setContent(JSON.stringify(result));
return out;
}
リスト1.WebAPI (A2) のエントリポイント
パラメータにtimingがなくdelayがある場合、リスト2に示す関数delayStartTimeを呼び出す。
/**
* 現在時刻の予定を指定した時間だけ遅らせる
* @param {Integer} delay 遅らせる時間(分)
*/
function delayStartTime(delay) {
debug('delayStartTime:delay=' + delay);
// 当日の服薬予定(未)をすべて取得する
var events = CalendarApp.getDefaultCalendar().getEventsForDay(
new Date(),
{search: '(未)'}
);
// 取得した予定から現在時刻が開始~終了時刻に含まれる予定を探してdelay(分)だけ遅らせる
var current_time = new Date();
events.forEach(function(event,i,array){
var start_time = event.getStartTime();
var end_time = event.getEndTime();
if (start_time <= current_time && current_time <= end_time) {
event.setTime(delayTime(start_time, delay), delayTime(end_time, delay));
}
});
}
// 引数に指定した日時target_timeをdelay[分]だけ遅らせる
function delayTime(target_time, delay){
var delay_time = new Date(target_time.getYear(), target_time.getMonth(), target_time.getDate(), target_time.getHours(), target_time.getMinutes() + delay, target_time.getSeconds() );
return delay_time;
}
リスト2.予定時刻を遅らせる関数delayStartTime
当日の予定から'(未)'を含む予定を探し、delayだけ開始・終了時刻を遅らせる。
IFTTTの登録
次に、「後でお薬飲む」とスピーカーに向かって話すと「分かりました。予定を10分遅らせます。」と返答して、Webhooks機能を用いて WebAPI (A3) を呼び出すIFTTTのアクティビティを登録する。
トリガーはGoogle Assistantで、下記のような設定にする。
What do you want to say?
-> 後でお薬飲む
What's another way to say it? (optional)
-> 後で飲む
And another way? (optional)
-> 少し待って
What do you want the Assistant to say in response?
-> 分かりました。予定を10分遅らせます。
Language
-> Japanese
リスト3.Trigger
続いて、下記はWeb requestの設定である。
URL
-> https://script.google.com/macros/s/AKfy..............................nIbMw/exec
Method
-> POST
Content Type (optional)
-> application/json
Body (optional)
-> {"delay": 10}
リスト4.Web request
ここで、URLは WebAPI (A3) のURLである。
ベアボーンサーバ(発話依頼プログラム pill-reminder.js)
ベアボーンサーバ上で稼働する発話依頼プログラム pill-reminder.js は、起動時に読み込んだGoogleカレンダーの予定通りに服薬通知を発話するようプログラムされている。
しかし、服用開始時刻を変更できるようにしたため、それに同期して発話の時刻を変更しなければならない。そこで、
予定の開始時間になったら呼び出される関数dosing_remindを以下のように修正した。
function dosing_remind(speech, counter, id) {
counter++;
putLog('"' + speech + '"の発話時刻になりました(' + counter + '回目), id=' + id);
if (counter > 10) {
putLog('催促回数が上限を超えました');
return;
}
// WebAPI A3 をid指定で叩いて服用済みかどうかを確認する
webclient.get(
{
url: data + "?id=" + id + "&num_reminder=" + counter
},
function (error, response, body) {
if(!error) {
var json = JSON.parse(body);
putLog('当該服薬スケジュール(id=' + id + '):\n' + JSON.stringify(json, null, " "));
if(json.taiking_status == "0" && !isOver(json)){
message = speech + 'を飲んでください';
putLog(message + '(' + counter + '回目)');
// まだ開始時刻(json.drug_notifies[0].startTime)になっていなければ、開始時刻にタイマーを設定する
var startTime = new Date(json.drug_notifies[0].startTime);
var now = new Date();
var df = startTime - now;
if (df > 0) {
putLog('予定が変更されています。この予定は' + parseInt(df / 60000) + '分後に発話されます。');
setTimeout(dosing_remind, df, speech, 0, id);//繰り返し
} else {
// Google homeへ発話依頼(開始時刻を過ぎている場合)
setTimeout(dosing_remind, C_INTERVAL, speech, counter, id);//繰り返し
webclient.get({
url: url,
qs: {
text: message,
}
},
function (error, response, body) {
if(!error) {
putLog('発話依頼成功:' + body);
} else {
putLog('発話依頼失敗:' + error);
}
});
}
} else {
putLog('済になったか催促上限オーバーになったので催促を終了します。');
}
} else {
putLog('WebAPI A3 呼び出しに失敗しました:' + error + '(id=' + id + ')');
}
}
);
}
リスト5.予定の開始時間になったら呼び出される関数dosing_remind