Node-REDのWebSocketノードを使って、WAN側からGoogle Homeを喋らせる
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)を立てます。
プロジェクトのルートでnpmを初期化します。
npm init -y
package.json
に以下を追記します。"engines": { "node": "12.x" }, "scripts": { "start": "node server.js" }
WebServerを立てるためのExpressとWebSocket Clinet Serverのws(といくつかmodules)を追加します。
npm install --save express npm install --save ws bufferutil utf-8-validate
次の内容で、
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); }); }
テキストボックスとボタンを配置した、
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>
ローカルで動作確認します。
npm start
ブラウザで
http://localhost:3000
を開き、入力フォームにメッセージを入れて送信します。送信すると、「Message: 入力したメッセージ」が表示されます。また、開発者ツールのネットワークタブで、[WS]でフィルターすると、WebSocket通信のキャプチャを確認できます。
Gitを初期化し、
node_modules
以下を.gitignore
に追加します。git init echo '*node_modules' >> .gitignore
ディレクトリ次の内容でコミットを作り、Herokuにpushします。
APP_NAME
はHerokuのアプリケーション名で、任意の値を設定します。git add . git commit -am "first commit" heroku create APP_NAME git push heroku master
Heroku上にできたアプリケーションで、ローカルでやったのと同様に動作確認をします。
heroku open
このとき、開発者ツールのネットワークタブで、[WS]でフィルターし、キャプチャしているWebSocket通信の[Name]を右クリック >[Copy]>[Copy link address]で WebSocket の通信先 URL をコピーしておきます。
Google Homeの設定を行う
Google Homeと接続したスマートフォンのGoogle Homeアプリケーションを開き、設定画面から以下を行います。
- 言語設定を日本語にする
- 設定画面の最下部にあるIPアドレスを確認する
Node-REDサーバーをローカルに立てる
Node-REDをローカルにインストールします。
npm install -g node-red
Node-REDを起動します。
node-red
http://127.0.0.1:1880/
にアクセスし、Node-REDの起動を確認します。
WebSocketノードとCastノードを追加する
Node-REDの画面で、右上のハンバーガーメニューから[パレットの管理]をクリックします。
[ノードの追加]タブを選択し、「node-red-contrib-cast」を検索してインストールします。
左サイドメニューから、次のノードをエディタ上にドラッグ&ドロップします。
<dl> <dt>入力</dt><dd>WebSocket</dd> <dt>機能</dt><dd>キャスト</dd> </dl>
WebSocketノードは次のように設定します。
項目 値 種類 接続 URL 開発者ツールのネットワークタブで確認した、WebSocket の通信先 URL 送信/受信 ペイロードを送信/受信 キャストノードは次のように設定します。
項目 値 IP Google Home の IP アドレス Port 8009 language ja WebSocketノードとキャストノードをつなぎます。
- [デプロイ]ボタンでデプロイします。
動作を確認する
Herokuアプリケーションを開き、テキストボックスに喋らせたい内容を入力します。 [送信する]ボタンでクリックし、Google Homeが入力した内容を喋ってくれれば成功です。