Node-RED の WebSocket ノードを使って、WAN 側から Google Home を喋らせる

Posts

Node-Red サーバーで、node-red-contrib-cast ノードを使うと、簡単に Google Home で任意のメッセージを喋らせることができます。 ただし、Google Home は LAN 側にいるため Node-RED サーバーはローカルに立てる必要があり、外(WAN 側)から操作できません。

これを解決するのに、Heroku に立てた WebSocket サーバーとローカルに立てた Node-RED サーバーの WebSocket ノードを通信させることにしました。

システムの構成は下図のようになります。 構成図

動作を確認した環境

  • Node-RED v0.20.5
  • Node.js v12.14.0
  • Heroku / Heroku CLI v7.38.1
  • Google Home
  • Google Home アプリケーション(iPhone)

WebSocket Server を Heroku 上に立てる

Using WebSockets on Heroku with Node.js を参考に、Heroku 上に WebSocket Server(とブラウザから入力を受け付けるフォームのための WebServer)を立てます。

  1. プロジェクトのルートで npm を初期化します。

    npm init -y
    
  2. package.json に以下を追記します。

      "engines": {
        "node": "12.x"
      },
      "scripts": {
        "start": "node server.js"
      }
    
  3. WebServer を立てるための Express と WebSocket Clinet Server の ws(といくつか modules)を追加します。

    npm install --save express
    npm install --save ws bufferutil utf-8-validate
    
  4. 次の内容で、server.js を作成します。

    'use strict';
    
    const express = require('express');
    const { Server } = require('ws');
    
    const PORT = process.env.PORT || 3000;
    const INDEX = '/index.html';
    
    // WebServer を作成
    const server = express()
      .use((req, res) => res.sendFile(INDEX, { root: __dirname }))
      .listen(PORT, () => console.log(`Listening on ${PORT}`));
    
    // WebSocket Sever を作成
    const wss = new Server({ server });
    
    // message イベントでブラウザから受け取ったメッセージを WebSocket で通信しているクライアントに送信する
    wss.on('connection', (ws) => {
      console.log('Client connected');
      ws.on('message', (message) => {
        sendAll(message);
      });
      ws.on('close', () => console.log('Client disconnected'));
    });
    
    const sendAll = (message) => {
      wss.clients.forEach((client) => {
        client.send(message);
      });
    }
    
  5. テキストボックスとボタンを配置した、index.html を作成します。

    <!DOCTYPE html>
    <html>
      <head>
        <script>
          const HOST = location.origin.replace(/^http/, 'ws')
          const ws = new WebSocket(HOST);
          ws.onmessage = (event) => {
            const el = document.getElementById('server-message');
            el.innerHTML = 'Message: ' + event.data;
          };
        </script>
        <title>WebSocket Server</title>
      </head>
      <body>
        <input type="text" id="text" value="" name="text">
        <input type="submit" id="button" value="送信する">
        <p id="server-message"></p>
      </body>
      <script>
        document.getElementById('button').addEventListener('click', ()=> {
          const text = document.getElementById('text').value;
          ws.send(text);
        })
      </script>
    </html>
    
  6. ローカルで動作確認します。

    npm start
    

    ブラウザで http://localhost:3000 を開き、入力フォームにメッセージを入れて送信します。

    送信すると、「Message: 入力したメッセージ」が表示されます。また、開発者ツールのネットワークタブで、[WS] でフィルターすると、WebSocket 通信のキャプチャを確認できます。

  7. Git を初期化し、node_modules 以下を .gitignore に追加します。

    git init
    echo '*node_modules' >> .gitignore
    
  8. ディレクトリ次の内容でコミットを作り、Heroku に push します。APP_NAME は Heroku のアプリケーション名で、任意の値を設定します。

    git add .
    git commit -am "first commit"
    heroku create APP_NAME
    git push heroku master
    
  9. Heroku 上にできたアプリケーションで、ローカルでやったのと同様に動作確認をします。

    heroku open
    

    このとき、開発者ツールのネットワークタブで、[WS] でフィルターし、キャプチャしている WebSocket 通信の [Name] を右クリック > [Copy] > [Copy link address] で WebSocket の通信先 URL をコピーしておきます。

Google Home の設定を行う

Google Home と接続したスマートフォンの Google Home アプリケーションを開き、設定画面から以下を行います。

  • 言語設定を日本語にする
  • 設定画面の最下部にある IP アドレスを確認する

Node-RED サーバーをローカルに立てる

  1. Node-RED をローカルにインストールします。

    npm install -g node-red
    
  2. Node-RED を起動します。

    node-red
    
  3. http://127.0.0.1:1880/ にアクセスし、Node-RED の起動を確認します。

WebSocket ノードと Cast ノードを追加する

  1. Node-RED の画面で、右上のハンバーガーメニューから[パレットの管理]をクリックします。

  2. [ノードの追加]タブを選択し、「node-red-contrib-cast」を検索してインストールします。

  3. 左サイドメニューから、次のノードをエディタ上にドラッグ&ドロップします。

    <dl> <dt>入力</dt><dd>WebSocket</dd> <dt>機能</dt><dd>キャスト</dd> </dl>

  4. WebSocket ノードは次のように設定します。

    項目
    種類接続
    URL開発者ツールのネットワークタブで確認した、WebSocket の通信先 URL
    送信/受信ペイロードを送信/受信
  5. キャストノードは次のように設定します。

    項目
    IPGoogle Home の IP アドレス
    Port8009
    languageja
  6. WebSocket ノードとキャストノードをつなぎます。

  1. [デプロイ]ボタンでデプロイします。

動作を確認する

Heroku アプリケーションを開き、テキストボックスに喋らせたい内容を入力します。 [送信する]ボタンでクリックし、Google Home が入力した内容を喋ってくれれば成功です。