Node.js
RaspberryPi
GoogleHome
google-home-notifier

Google Homeに業務開始と終了を教えてもらう

google-home-notifier を使うと、Google Home から任意発話ができると聞いて、会社に置いてあるGoogle Home に自動で始業、終業の時報を実行してもらおうと思います。(何番煎じかわかりませんがやり方が異なったり独自性があるかも云々。。)

処理実装は、Node.js で行っています。

構成図

構成は下図の通り。(構成と言うには色々足りない気がしますが)
goohou.png

google-home-notifier 関連のファイルはなるべくそのままに、他のファイルを付け足す形で構成しています。
「タイマー・発話内容スクリプト」では、node-schedule ライブラリを利用して決まった時間に、「発話内容受付・送信サーバ」に curl でリクエストを送信しています。
発話させたいのが音声ファイルの場合、デフォルトの example.js が、POST されてくる'text'値の頭が'http'であれば、'text'の内容が音声ファイルの URLだと判断するので、URL先となる「音声ファイル API サーバ」を用意した形になります。

今回 Google Home と同じネットワークにいる ubuntu の仮想マシンを使っていますが、別に RaspberryPi で Rasbian を使用してももちろん構いません。

実装

実装するのは構成図の通り、下記になります。

  • 発話内容受付・送信サーバ
  • 音声ファイル API サーバ
  • タイマー・発話内容スクリプト

発話内容受付・送信サーバ

「発話内容受付・送信サーバ」と当記事ではつけていますが、あるがままのgoogle-home-notifierを使っているだけです。

google-home-notifier の設定は、下記記事に詳細に記載されています。参考にさせていただきました。

GoogleHomeスピーカーに外部からプッシュして自発的に話してもらいます

「google-home-notifierの設定」項目を参考に実装します。
example.js ファイルの'serverPort'と'ip'、'language'の値を変更し、"googlehome.device(deviceName,language)"を適切な場所に追加します。

※サーバの起動は最後に foreverコマンドを使って、他の処理とまとめて行いますので、ここでは書きません。

音声ファイル API サーバ

上記した構成項目で述べたように、音声ファイルを再生しようとする場合、「発話内容受付・送信サーバ」として実行されている example.js ファイルが、POST されてくる'text'値の頭が'http'であれば、'text'の内容を音声ファイルの URL と認識します。なので音声ファイルを再生するには、URL先の「音声ファイル API サーバ」を作成、実行しておく必要があります。

下記ファイルを作成し、実行すれば出来上がります。

app.js
const express = require('express');
const bodyParser = require('body-parser');
const fs = require('fs');

const app = express();
const serverPort = 8080;
const urlencodedParser = bodyParser.urlencoded({ extended: false });

// CORSを許可
app.use(function(req, res, next) {
  res.header("Access-Control-Allow-Origin", "*");
  res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
  next();
});

// 音声ファイルを取得
app.get('/googlehome/:audioName', (req, res) => {
  const audioName = req.params.audioName;
  console.log(audioName);
  if (!audioName) {
    res.status(400).send('Invalid Parameters.');
  }

  const file = fs.readFileSync('/音声ファイルのあるフォルダパス/' + audioName, 'binary');
  res.setHeader('Content-Length', file.length);
  res.write(file, 'binary');
  console.log('res ok!');
  res.end();
});

app.listen(serverPort, () => {
  console.log(`Start api-server. Port is ${serverPort}`);
})

'serverPort'の値は、'8080'としていますが、任意に設定してください。発話内容受付・送信サーバの example.js で指定したポート番号とは別のポートを指定してください。

タイマー・発話内容スクリプト

発話させたいタイミングごとに jsファイルを作成します。(何分何時間何日おきに実行などは1ファイルで作成できます。)
下記コードでは、8時45分に「8時45分です。おはようございます。」と発話する内容になっています。

schedule_0845.js
const schedule = require('node-schedule');
let job = schedule.scheduleJob('0 45 8 * * *', () => {
    console.log('実行');
    const exec = require('child_process').exec;

    let text = '8時45分です。おはようございます';
    let cmd = 'curl -X POST -d "text=' + text + '" http://localhost:8091/google-home-notifier';
    console.log(cmd);

    const child = exec(cmd, (error) => {
        // Resolve with result of process
        let fireDate = new Date();
        console.log('Problem with request: ' + error);
        console.log('firewtime: ' + fireDate);
    });
    // Log process stdout and stderr
    child.stdout.on('data', console.log);
    child.stderr.on('data', console.error);
});

node-schedule ライブラリを利用してタイマーを実行し、child_process を利用した curl で発話内容を POST リクエストする処理になっています。

音声ファイルを再生したい場合は、'text'変数の行を下記のように書き換えればOKです。

let text = 'http://localhost:8080/googlehome/音声ファイル.mp3';

curl コマンド内の URL に"localhost"が 2つ並ぶことからも「発話内容受付・送信サーバ」と「音声ファイル API サーバ」のポート番号が同じだとダメだというのがわかってもらえると思います。

上記コードは、8時45分の「始業」のスクリプトになるので、17時30分の「終業」の"schedule_1730.js"を「始業」スクリプトをコピペして作成し、'.scheduleJob'処理の第一引数を '0 30 17 * * *' に、'text'の値を終業用の内容に変更して作成します。

実行

各 js ファイルの実行には、forever コマンドを使います。

forever コマンドの操作概要については、下記記事が参考になりました。

foreverコマンドでNodeJSアプリを起動したままにする

  • 発話内容受付・送信サーバ
  • 音声ファイル API サーバ
  • タイマー・発話内容スクリプト

上記を稼働させるため、下記コマンドを実行します。

forever start 各ファイル名.js 

今回の場合だと、下記ファイル名でそれぞれ"forever start"を実行することになります。

  • example.js
  • app.js
  • schedule_0845.js
  • schedule_1730.js

実行が完了すれば完了で、あとは時間が来れば勝手に発話してくれます。

まとめ

今回は定期的に同じ内容を発話するというものを作りましたが、何かをトリガーにして発話させるというものも作ってみたいと思います。

example.js と app.js の処理は 1つのファイルで記載できたような気がします。また発話したいタイミングごとにファイルとセッションを追加していくという形を取りましたが、もっといいやり方がありそうな気がします。(これはこれでやりやすいとは思っていますが。。)

参考