2019年10月25日金曜日

実験6日目

問題とその解決策


予定を変更する


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




0 件のコメント:

コメントを投稿