Google Homeコントローラ

 服用時刻になったらGoogle Homeから服薬督促を行うためにsemi2018ではGoogle Homeコントローラを作成した。詳細は「ベアボーンサーバ」および「発話PUSHサーバ」に記載している。

そこでは、Linuxサーバ上に2つのNodejsのプログラムexample.jsとpill-reminder.jsが稼働している。前者は発話PUSHサーバでgoogle-home-notifierに付随するサンプルプログラムをベースに作成したものである。後者は、服用時刻がきたら服用督促メッセージを発話PUSHサーバに送信依頼するプログラムである。

今回、その2つのプログラムを統合し、YakuShare.jsというプログラムを作成したので紹介する。

使用するライブラリ

まずは、プログラムの冒頭で使用するライブラリの宣言とインスタンス化を行う。

var express = require('express');
var googlehome = require('google-home-notifier');
var ngrok = require('ngrok');
var bodyParser = require('body-parser');
var urlencodedParser = bodyParser.urlencoded({ extended: false });
var os = require('os');
var app = express();
var webclient = require("request");

リスト1.ライブラリの宣言とインスタンス化

タイマー管理クラス Timer

次いでタイマー管理クラスTimerを定義し、インスタンスtimerを作成する。

/**
 * タイマー管理クラス
 */
class Timer {
  constructor() {
    this.timer_ids = {};
  }
  
  // Timer IDを設定
  set(event_id, timer_id) {
    this.timer_ids[event_id] = timer_id;
  }
  
  // Timer IDを削除
  del(event_id) {
    if(this.timer_ids[event_id]) {
      delete this.timer_ids[event_id];
    }
  }

  // すべてのTimer IDを表示
  show(print = console.log) {
    for (let event_id in this.timer_ids) {
      print('event_id=' + event_id + ', timer_id=' + this.timer_ids[event_id]);
    }
  }
  
  // すべてのTimer IDをクリア
  clearAll() {
    for (let event_id in this.timer_ids) {
      clearTimeout(this.timer_ids[event_id]);
    }
    this.timer_ids = {};
  }
}

// タイマー管理クラスのインスタンス化
const timer = new Timer();

リスト2.タイマー管理クラスとそのインスタンス化

タイマー管理クラスはこのプログラムで使用するタイマーを管理する。具体的には、服用時刻になったら起動する関数や翌日の予定をスケジュールする関数をsetTimeout関数でタイマー設定するが、そのときのタイマーIDを管理し、必要に応じてタイマーIDを表示したり、すべてのタイマーIDをクリアしたりするメソッドを実装している。

設定情報(グローバル変数)config

このプログラムで使用するグローバル変数を管理するオブジェクトconfigとその初期化を行うinitConfig()関数をリスト3に示す。

/**
 * コンフィグレーション
 */
const config = {
  // WebAPI(A2) semi2020kumw
  'api2': 'https://script.google.com/macros/s/AKfycb...MTboEv/exec',
  // WebAPI(A3) カレンダーの服薬予定取得(semi2020kumw) 2021.04.30
  'api3': 'https://script.google.com/macros/s/AKfycb...2cPWHIQ/exec',

  // Server port/ip
  'serverPort': 8092,
  'serverIP': '192.168.0.13',

  // Google Home のデバイス名とIPアドレス
  'deviceName': 'Pills',
  'google_home_ip': '192.168.0.12',
  // default language code
  'language': 'ja',
  
  // ngrok払出しurl
  'ngrok': null,
  // ngrok のアクセストークン(semi2020kumw@gmail.com)
  'authtoken': '1rrQke...MXhJPt',

  // 服用通知発話間隔
  'C_INTERVAL': 5 * 60 * 1000,
  
};

/**
 * 初期設定
 */
function initConfig() {
  
  // ベアボーンサーバーのIPアドレス
  const localAddress = getLocalAddress();
  putLog('initConfig: localAddress=' + JSON.stringify(localAddress, null, ' '));
  config.serverIP = localAddress.ipv4[0].address;
  putLog('initConfig: config.serverIP=' + config.serverIP);

  /**
   * WebAPI(A2)を叩いてGoogle HomeのIPアドレスを取得
   *
  */
  webclient.get(
    {
      url: config.api2 + "?ip=google_home_ip" 
    }, 
    function (error, response, body) {
      if(!error) {
        var json = JSON.parse(body);
        putLog('initConfig: Google HomeのIPアドレス:\n' + JSON.stringify(json, null, " "));
        config.google_home_ip = json.ip;
        // 服薬スケジューリング
        dosing_schedule();
        putLog('initConfig: config=' + JSON.stringify(config, null, ' '));
      } else {
        putLog('initConfig: WebAPI A2 呼び出しでエラーが発生しました。Google HomeのIPアドレス取得できませんでした。:' + error);
      }
    }
  );
}

// 初期設定
initConfig();

リスト3.configオブジェクトとその初期化関数initConfig

WebAPIのurl

config.api2には、次の機能を提供するGASで作成したWebAPIのurlを設定している。

  1. Google HomeのIPアドレスを取得
  2. Google Homeコントローラを外部公開するngrokが払い出すurlを登録

リスト3のconfigを初期化する関数 initConfig 内でGoogle Home のIPアドレスを取得してconfig.google_home_ipに設定し、リスト4のconnectNgrok関数によって非同期にngrokを起動して外部公開用urlを払い出し、sendNgrok関数を使ってそれを登録している。

config.api3には、次の機能を提供するGASで作成したWebAPIのurlを設定している。

  1. 当日の服薬スケジュールを取得
  2. IDを指定して服薬スケジュールを取得

リスト*の関数 dosing_schedule にて当日のスケジュールを取得し、タイムアウト発生時に呼び出される関数 dosing_remind にてIDを指定して当該服薬スケジュールを取得している。

サーバのIPアドレスと待ち受けポート番号

config.serverIPには、本プログラムが稼働するサーバのIPアドレスが設定されている。これは初期設定関数initConfig内で、関数getLocalAddressを呼び出して自動設定する。

config.serverPortには、本プログラムがHTTPリクエストを待ち受けるポート番号を設定する。ここでは8092を設定している。

Google Home のデバイス名とIPアドレス及びデフォルト言語

config.deviceNameにはGoogle Homeのデバイス名を、config.google_home_ipにはGoogle HomeのIPアドレスを、languageにはGoogle Homeのデフォルト言語を設定する。なお、config.google_home_ipは上述したようにconfig.api2のWebAPIによって取得したIPアドレスが設定値に上書きされる。

これらの設定値はGoogle Homeに発話依頼する際に使用される。

Ngrok払い出しURLとアクセストークン

config.ngrokはNgrokによって払い出された外部公開用URLを格納する。これは、ポート番号config.serverPortでHTTP接続を待ち受ける際にconnectNgrok関数を使って取得している。取得の際に使用するのがNgrokのアクセストークンで、config.authtokenに設定する。

服用通知発話間隔

スケジュールされた服薬時間がくるとGoogle Homeから服薬を促すメッセージが発話される。このメッセージは利用者が薬を飲んだことをGoogle Homeに伝えない限り一定時間間隔で繰り返す。その時間間隔をミリ秒単位で設定するのがconfig.C_INTERVALで、5分に設定されている。

HTTPリクエストの待ち受け

HTTPリクエストをconfig.serverPortで待ち受けるプログラムである(リスト4)。その際、ngrokを非同期で起動して、払い出されたngrok urlを関数sendNgrokUrlを利用してGoogleスプレッドシートへ書き込んでいる。

/**
 * 待ち受け
 * config.serverPort(=8092)をlisten
 * 同時にngrokのURLを取得して出力
 */
app.listen(config.serverPort, function () {
  putLog('Endpoints:');
  putLog('    http://' + config.serverIP + ':' + config.serverPort + '/google-home-notifier');
  connectNgrok(config.serverPort).then(url => {
    config.ngrok = url;
    var ngrok_url = url + '/google-home-notifier';
    putLog('    ' + ngrok_url);
    putLog('GET example:');
    putLog('curl -X GET ' + ngrok_url + '?text=Hello+Google+Home');
    putLog('POST example:');
    putLog('curl -X POST -d "text=Hello Google Home" ' + ngrok_url);
    sendNgrokUrl(ngrok_url);
  });
})

/** 
 * ngrokを非同期で起動
 */
async function connectNgrok(port) {
  try {
    let url = await ngrok.connect({
      addr: port,
      //region: 'ap', 
      //configPath: '~/.ngrok2/ngrok.yml',
      authtoken: config.authtoken
    });
    return url;
  } catch (err) {
    putLog('connectNgrok:\n' + JSON.stringify(err, null, ' '));
  }
}

/**
 * ngrokが払い出したurlをWebAPI(A2)を使ってGoogleスプレッドシートへ書き込む
 * Googleスプレッドシート: semi2018kumwのpill-reminder
 *
 */
function sendNgrokUrl(ngrok_url) {
    putLog('sendNgrokUrl(' + ngrok_url + ')');

  webclient.post({
    url: config.api2,
    headers: {
    "content-type": "application/json"
    },
    body: JSON.stringify({"ngrok_url": ngrok_url})
  }, function (error, response, body){
    if(!error) {
      putLog("****** sendNgrokUrl 成功 ******");
    } else {
      putLog("****** sendNgrokUrl エラー ******");
      putLog('error:' + error);
      putLog('response:' + response);
      putLog('body:' + body);
    }
  });
}

リスト4.待ち受け関数とngrok関連関数

発話PUSH機能 (/google-home-notifier)

Google Homeへの発話要求をルーティングするプログラムをリスト5に示す。ここで、ルーティングとはYakuShareが特定のエンドポイントに対するクライアント要求に応答する方法として、URI (またはパス) と特定の HTTP 要求メソッド (GET、POST など) を決定することである。ここでのパスは"/google-home-notifier"で、HTTP要求メソッドはPOSTとGETの両方を準備しており、各々、app.postとapp.getである。

/**
 * 発話PUSH機能 (/google-home-notifier)
 *
 */
app.post('/google-home-notifier', urlencodedParser, function (req, res) {
  
  if (!req.body) return res.sendStatus(400);
  putLog('app.post/google-home-notifier: ' + JSON.stringify(req.body, null, ' '));
  
  var text = req.body.text;
  putLog('app.post/google-home-notifier: text=' + text);
  
  if (req.body.ip) {
     config.serverIP = req.body.ip;
  }

  if (req.body.language) {
    config.language = req.body.language;
  }

  googlehome.ip(config.google_home_ip, config.language); 
  googlehome.device(config.deviceName, config.language);
  googlehome.accent(config.language);

  if (text){
    try {
      if (text.startsWith('http')){
        var mp3_url = text;
        googlehome.play(mp3_url, function(notifyRes) {
          putLog(notifyRes);
          res.send(config.deviceName + ' will play sound from url: ' + mp3_url + '\n');
        });
      } else {
        googlehome.notify(text, function(notifyRes) {
          putLog('app.post/google-home-notifier: notifyRes=' + notifyRes);
          res.send(config.deviceName + ' will say: ' + text + '\n');
        });
      }
    } catch(err) {
      putLog(err);
      res.sendStatus(500);
      res.send(err);
    }
  }else{
    res.send('Please GET "text=Hello Google Home"');
  }
});

app.get('/google-home-notifier', function (req, res) {

  if (!req.query) return res.sendStatus(400);
  putLog('app.get/google-home-notifier: ' + JSON.stringify(req.query, null, ' '));

  var text = req.query.text;
  putLog('app.get/google-home-notifier: text=' + text);

  if (req.query.ip) {
     config.serverIP = req.query.ip;
  }

  if (req.query.language) {
    config.language = req.query.language;
  }
  googlehome.ip(config.google_home_ip, config.language);
  googlehome.device(config.deviceName, config.language);
  googlehome.accent(config.language);
  if (text) {
    try {
      if (text.startsWith('http')){
        var mp3_url = text;
        googlehome.play(mp3_url, function(notifyRes) {
          putLog(notifyRes);
          res.send(config.deviceName + ' will play sound from url: ' + mp3_url + '\n');
        });
      } else {
        googlehome.notify(text, function(notifyRes) {
          putLog('app.get/google-home-notifier: notifyRes=' + notifyRes);
          res.send(config.deviceName + ' will say: ' + text + '\n');
        });
      }
    } catch(err) {
      putLog(err);
      res.sendStatus(500);
      res.send(err);
    }
  }else{
    res.send('Please GET "text=Hello+Google+Home"');
  }
});

リスト5.発話要求へのルーティング

curlコマンドを使ってPOSTメソッドで発話要求する際のパラメタは以下のように指定する。

curl -X POST -d "text=Hello Google Home" https://6ceXXX911.ngrok.io/google-home-notifier

リスト6.POSTメソッドでのデータ受け渡し(curlコマンド)

一方、GASのUrlFetchApp.fetchメソッドを使ってPOSTメソッドで発話要求する際のパラメタは以下のように指定する。

  var options =
  {
    "method"  : "post",
    "payload" : "text=Hello Google Home"
  };
  var ngrok_url = 'https://6ceXXX911.ngrok.io/google-home-notifier';
  UrlFetchApp.fetch(ngrok_url, options);

リスト7.POSTメソッドでのデータ受け渡し(GAS)

curlコマンドを使ってGETメソッドで発話要求する際のパラメタは以下のように指定する。

curl -X GET https://6ceXXX911.ngrok.io/google-home-notifier?text=Hello+Google+Home&ip=192.168.0.12&language=ja

リスト8.GETメソッドでのデータ受け渡し(curlコマンド)

一方、Nodejsのrequestモジュールを使ってGETメソッドで発話要求する際のパラメタは以下のように指定する。

  var webclient = require('request');
  webclient.get({
    "url"  : "https://6ceXXX911.ngrok.io/google-home-notifier",
    "qs" : {
      "text": "Hello Google Home",
      "ip": "192.168.0.12",
      "language": "ja"
    }
  },
  function(error, response, body) {
    if(!error) {
      // 成功時の処理
    } else {
      // 失敗時の処理
    }
  });

リスト9.GETメソッドでのデータ受け渡し(Nodejs)

服薬スケジュール

config.api3で本日の服薬スケジュールを取得したものをリスト10に示す。

{
 "drug_notifies": [
  {
   "title": "朝の食後の薬(未)",
   "description": "クラビット細粒10% 100mg(レボフロキサシンとして) 3g",
   "id": "4unup4q0nvecekung915hhaq68@google.com",
   "location": "0",
   "startTime": "2019-10-11T08:36:00.000Z",
   "endTime": "2019-10-11T08:39:00.000Z",
   "color": "11"
  },
  {
   "title": "夜の食後の薬(未)",
   "description": "クラビット細粒10% 100mg(レボフロキサシンとして) 3g",
   "id": "0s826bi4tecvamgadmklj9faod@google.com",
   "location": "0",
   "startTime": "2019-10-11T10:00:00.000Z",
   "endTime": "2019-10-11T10:03:00.000Z",
   "color": "11"
  },
  {
   "title": "昼の食後の薬(未)",
   "description": "クラビット細粒10% 100mg(レボフロキサシンとして) 3g",
   "id": "2tjms6utgpmqi07nma49v4tt0e@google.com",
   "location": "0",
   "startTime": "2019-10-11T11:00:00.000Z",
   "endTime": "2019-10-11T11:03:00.000Z",
   "color": "11"
  }
 ],
 "num_reminder": "0",
 "taiking_status": "0",
 "status": "0"
}

リスト10.服薬スケジュール(当日の全件)

これを読み込み、各予定の服用時刻がきたら服薬促進メッセージを発話依頼するタイマーを設定するのがリスト11に示す関数dosing_schedule()である。

/**
*
* 本日のスケジュールを取得
*
*/
function dosing_schedule() {
  // タイマー管理オブジェクトを初期化
  timer.clearAll();
  // WebAPI(A3)を叩いて本日のスケジュールを取得
  webclient.get(
    {
      url: config.api3,
    }, 
    function (error, response, body) {
      if(!error) {
        const json = JSON.parse(body);
        putLog('dosing_schedule: 本日の服薬スケジュール:\n' + JSON.stringify(json, null, " "));
        for (let drug_notify of json.drug_notifies){
          putLog('dosing_schedule: startTime=' + drug_notify.startTime + ', title=' + drug_notify.title + ', id=' + drug_notify.id);
          const startTime = new Date(drug_notify.startTime);
          const now = new Date();
          const df = startTime - now;
          const speech = getSpeech(drug_notify.title);
          if (df > 0) {
            putLog('dosing_schedule: この予定(speech=' + speech + ')は' + parseInt(df / 60000) + '分後に発話されます。');
            const timer_id = setTimeout(dosing_remind, df, speech, 1, drug_notify.id );  // 2021.04.30 counterの値を0 -> 1
            timer.set(drug_notify.id, timer_id);
          } else {
            putLog('dosing_schedule: この予定(speech=' + speech + ')はすでに終了しているので発話されません。');
          }
        }
        // 翌日の午前1時にdosing_scheduleを呼び出すようにタイマーをセット
        const startTime = getNextDateAt1am();
        const now = new Date();
        const df = startTime - now;
        const event_id = formatDate(startTime, 'YYYY-MM-DD');
        const timer_id = setTimeout(dosing_schedule, df);
        timer.set(event_id, timer_id);
        show_config();
        show_usage();
      } else {
        putLog('dosing_schedule: WebAPI A3 呼び出しでエラーが発生しました(当日分):' + error);
      }
    }
  );
}

/**
 * 翌日の午前1時を返す関数
 */
function getNextDateAt1am() {
  const now = new Date();
  now.setDate(now.getDate() + 1);
  now.setHours(1);
  now.setMinutes(0);
  now.setSeconds(0);
  now.setMilliseconds(0);
  return now;
}

リスト11.服薬スケジュール関数dosing_schedule()

まず、関数timer.clearAllでタイマー管理オブジェクトを初期化し、次いでconfig.api3で当日分の服薬スケジュールを取得し、個々の予定時刻になったらdosing_remind関数を起動するようにスケジューリングする。その際、setTimeout関数が返してくるタイマーIDをtimerオブジェクトのsetメソッドで記憶しておく。 これは、再スケジューリング時のタイマーリセットに備えてのことである。

個々の服薬スケジュールが完了したら翌日の服薬スケジューリングを行うべく関数dosing_scheduleを再帰的に呼び出すようにタイマーをセットする。これによって毎日の服薬スケジューリングを絶え間なく実行することができる。

dosing_remind関数

服用時刻がきたら服薬促進メッセージを発話依頼する。それを行うのがdosing_remind関数である(リスト12)。

/**
*
* 時間がきたときの処理
*
*/
function dosing_remind(speech, counter, id) {
  // タイマーIDを削除
  timer.del(id);
  putLog('dosing_remind: "' + speech + '"の発話時刻になりました(' + counter + '回目), id=' + id);
  if (counter > 10) {
    putLog('dosing_remind: 催促回数が上限を超えました');
    return;
  }

  // WebAPI A3 をid指定で叩いて服用済みかどうかを確認する
  webclient.get(
    {
      url: config.api3 + "?id=" + id + "&num_reminder=" + counter
    }, 
    function (error, response, body) {
      if(!error) {
        var json = JSON.parse(body);
        putLog('dosing_remind: 当該服薬スケジュール(id=' + id + '):\n' + JSON.stringify(json, null, " "));
        if(json.taiking_status == "0" && !isOver(json)){
          message = speech + 'を飲んでください';
          putLog('dosing_remind: ' + 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('dosing_remind: 予定が変更されています。この予定は' + parseInt(df / 60000) + '分後に発話されます。');
            // 2021.04.30 0 -> counter
            const timer_id = setTimeout(dosing_remind, df, speech, counter, id);//繰り返し
            timer.set(id, timer_id);
          } else {
            // Google homeへ発話依頼(開始時刻を過ぎている場合)
            counter++;  // 2021.04.30 counterのインクリメントを追加
            const timer_id = setTimeout(dosing_remind, config.C_INTERVAL, speech, counter, id);//繰り返し
            timer.set(id, timer_id);
            // Google Home 発話
            googlehome.ip(config.google_home_ip, config.language); 
            googlehome.device(config.deviceName, config.language);
            googlehome.accent(config.language);
            googlehome.notify(message, function(notifyRes) {
              putLog('dosing_remind: notifyRes=' + notifyRes);
              putLog('dosing_remind: ' + config.deviceName + ' will say: ' + message + '\n');
            });
          }
        } else {
          putLog('dosing_remind: 済になったか催促上限オーバーになったので催促を終了します。');
        }

      } else {
        putLog('dosing_remind: WebAPI A3 呼び出しに失敗しました:' + error + '(id=' + id + ')');
      }
    }
  );
}

/**
 * 発話内容を編集
 * title=昼の食後の薬(未)
 * (未)を除去して返す関数
 */
function getSpeech(title) {
  var match = title.match(/^(.+)\((.+)\)$/);
  return match[1];
}

/**
 * 終了時刻が過ぎているかを確認
 * @params {object} json
 *
 */
function isOver(json) {
  if (json.drug_notifies.length > 0) {
    var endTime = new Date(json.drug_notifies[0].endTime);
    var now = new Date();
    var df = endTime - now;
    if (df < 0) {
      putLog('isOver: 終了時刻を過ぎています。');
      return true;
    }
  } else {
    putLog('isOver: json.drug_notifiesが空です。');
    return false;
  }
  return false;
}

リスト12.服薬時刻がきた時の処理を行うdosing_remind関数

dosing_remind関数はspeech, counter, idの3つのパラメタをとる。speechは服薬の内容(例えば「朝食後の薬」)、counterは何度目の発話依頼か、idは服薬予定を識別するIDである。

dosing_remind関数は、最初にtimerオブジェクトのdelメソッドでidをクリアする。次に、counterが10回を超えた場合「催促回数が上限を超えました」とログに出力して関数を抜ける。

counterが10回以内の場合はconfig.api3を使って当該IDの服薬スケジュールを取得する(リスト13)。

{
  "drug_notifies": [
    {
      "title": "昼の食後の薬(応答無)",
      "description": "クラビット細粒10% 100mg(レボフロキサシンとして) 3g",
      "id": "4unup4q0nvecekung915hhaq68@google.com",
      "location": "0",
      "startTime": "2019-10-10T23:37:00.000Z",
      "endTime": "2019-10-11T00:37:00.000Z",
      "color": "5"
    }
  ],
  "num_reminder": "0",
  "taiking_status": "0",
  "status": "0"
}

リスト13.服薬スケジュール(単品)

取得した服薬スケジュール情報(リスト13)からisOver関数などを使って未服用かどうかを判定し、未服用であればGoogle Homeに対して服用を促すメッセージを発話するよう依頼する。その際、まだ開始時刻(json.drug_notifies[0].startTime)になっていなければ、開始時刻にdosing_remind関数を起動するようにタイマーを設定する。

リロード(/reload)

当日の服薬スケジュールを再読み込みする機能がリスト14に示すリロードである。実体は、関数dosing_scheduleを呼び出しているだけである。

app.get('/reload', function (req, res) {
  res.send('app.get/reload: accepted\n');
  dosing_schedule();
});

リスト14.リロード(/reload)

この機能を呼び出すには以下のcurlコマンドを実行する。

curl -X GET https://6ce...911.ngrok.io/reload

リスト15.リロードを実行するcurlコマンド

設定確認(/config)

設定情報configとタイマー設定情報をコンソールに表示する機能である。

app.get('/config', function (req, res) {
  res.send('app.get/config: accepted\n');
  show_config();
});

function show_config() {
  putLog('show config:');
  putLog(JSON.stringify(config, null, ' '));
  putLog('show timer:');
  timer.show(putLog);
}

リスト16.設定確認(/config)

この機能を呼び出すには以下のcurlコマンドを実行する。

curl -X GET https://6ce...911.ngrok.io/config

リスト17.設定情報を確認するcurlコマンド

上記curlコマンドの実行結果を以下に示す。

2021-05-09 10:18:58.819: show config:
2021-05-09 10:18:58.820: {
 "api2": "https://script.google.com/macros/s/AKfycb...MTboEv/exec",
 "api3": "https://script.google.com/macros/s/AKfycb...cPWHIQ/exec",
 "serverPort": 8092,
 "serverIP": "192.168.0.13",
 "deviceName": "Pills",
 "google_home_ip": "192.168.0.12",
 "language": "ja",
 "ngrok": "https://6ce910ab7911.ngrok.io",
 "authtoken": "1rr...JPt",
 "C_INTERVAL": 300000
}
2021-05-09 10:18:58.820: show timer:
2021-05-09 10:18:58.820: event_id=2021-05-10, timer_id=[object Object]

リスト18.設定情報を確認するcurlコマンドの実行結果

ヘルプ(/help)

操作方法をコンソールに表示する機能である。

app.get('/help', function (req, res) {
  res.send('app.get/help: accepted\n');
  putLog('app.get/help');
  show_usage();
});

function show_usage() {
  putLog('YakuShare.js usage:');
  putLog('curl -X POST -d "text=Hello Google Home" ' + config.ngrok + '/google-home-notifier');
  putLog('curl -X GET ' + config.ngrok + '/google-home-notifier?text=Hello+Google+Home');
  putLog('curl -X GET ' + config.ngrok + '/reload');
  putLog('curl -X GET ' + config.ngrok + '/config');
  putLog('curl -X GET ' + config.ngrok + '/help');
}

リスト19.ヘルプ(/help)

この機能を呼び出すには以下のcurlコマンドを実行する。

curl -X GET https://6ce...911.ngrok.io/help

リスト20.操作方法を表示するcurlコマンド

上記curlコマンドの実行結果を以下に示す。

2021-05-09 10:24:28.682: app.get/help
2021-05-09 10:24:28.684: YakuShare.js usage:
2021-05-09 10:24:28.685: curl -X POST -d "text=Hello Google Home" https://6ce...911.ngrok.io/google-home-notifier
2021-05-09 10:24:28.686: curl -X GET https://6ce...911.ngrok.io/google-home-notifier?text=Hello+Google+Home
2021-05-09 10:24:28.687: curl -X GET https://6ce...911.ngrok.io/reload
2021-05-09 10:24:28.687: curl -X GET https://6ce...911.ngrok.io/config
2021-05-09 10:24:28.688: curl -X GET https://6ce...911.ngrok.io/help

リスト21.操作方法を表示するcurlコマンドの実行結果

ログの出力関数

リスト22にログの出力関数と日付をフォーマットする関数を示す。

/**
 * ログの出力
 *
 */
function putLog(msg) {
 console.log(formatDate(new Date()) + ": " + msg);
}

/**
 * 日付をフォーマットする
 * @param  {Date}   date     日付
 * @param  {String} [format] フォーマット
 * @return {String}          フォーマット済み日付
 */
function formatDate(date, format) {
  if (!format) format = 'YYYY-MM-DD hh:mm:ss.SSS';
  format = format.replace(/YYYY/g, date.getFullYear());
  format = format.replace(/MM/g, ('0' + (date.getMonth() + 1)).slice(-2));
  format = format.replace(/DD/g, ('0' + date.getDate()).slice(-2));
  format = format.replace(/hh/g, ('0' + date.getHours()).slice(-2));
  format = format.replace(/mm/g, ('0' + date.getMinutes()).slice(-2));
  format = format.replace(/ss/g, ('0' + date.getSeconds()).slice(-2));
  if (format.match(/S/g)) {
    var milliSeconds = ('00' + date.getMilliseconds()).slice(-3);
    var length = format.match(/S/g).length;
    for (var i = 0; i < length; i++) format = format.replace(/S/, milliSeconds.substring(i, i + 1));
  }
  return format;
};

リスト22.ログの出力関数と日付をフォーマットする関数

0 件のコメント:

コメントを投稿