Edited at

Google Homeを手軽に喋らせる仕組み(google-home-voicetext)を作った(Dockerにも対応)


開発経緯


google-home-notifierの問題点

今更ながらGoogle Homeを喋らせてみようと思い立って挑戦してみたのですが、発売後結構時間が経っているにもかかわらず、簡単に実施できる方法が見つからず、意外なほど苦戦しました。

Google Homeを喋らせる方法で検索するとgoogle-home-notifier関連の情報が最も多くヒットしますが、2019年2月の時点で、下記のような問題がありました。(最後のは個人の感想ですが)


  • package.jsonを書き換えないと動かない

  • いつ仕様変更で動かなくなるか分からない

  • 声が微妙に不快

google-home-notifierは依存関係のあるgoogle-tts-apiのバージョンが正しく設定されていないせいで、ダウンロードした直後の状態では動きません。(下記参照)

https://qiita.com/ktetsuo/items/21f4f85a98145772be3d

最終的にpackage.jsonを記事の通り書き換えたら動くようになりました。

google-tts-apiは非公式のAPIキーを使って音声を合成しているらしく、仕様変更があると動かなくなるようです。

いつ仕様変更で動かなくなるか分からないというのはかなり不安です。

また、声が微妙に不快で、好きになれませんでした。


google-home-notifier + VoiceTextの問題点

Google Homeを喋らせる方法を調べていると、google-home-notifierのソースを改造してVoiceTextのWeb APIで合成した音声を使うようにする方法もいくつか見つけました。

https://qiita.com/nori-dev-akg/items/bad7eb0c41617110cfa4

https://note.mu/ukshampoo/n/nacfe47ed46a6

VoiceTextはHOYA株式会社が開発した音声合成エンジンで、WEB APIが公開されています。

WEB APIは、商用利用は不可ですが、メールアドレスを登録するだけで無料で利用することが出来ます。

やってみると音声合成を専門としているだけあって、google-tts-apiとは格段に声の質が違います。

公式のAPIなのでgoogle-tts-apiのようにいつ動かなくなるかわからないといった不安もありません。

ただし、この方法も以下のような問題点があります。



  • google-home-notifierをインストールしてからソースコードを書き換えるので手間がかかる


  • google-tts-apiなど余計なものまでインストールされてしまう

  • node_modules配下のソースコードを書き換えるのでGitで管理しにくい

私は忘れっぽいので、この方法で構築した環境を半年後に1から再現できる自信はありません。

そこで未来の自分のために、再構築しやすい形でソースコードをまとめてみました。


方針



  • npm installしたらソースコードを書き換えなくても動くようにする

  • 各種設定は環境変数で行う

  • ローカルネットワーク内部からの呼び出しはREST APIを用いる

  • インターネット側からの呼び出しはCloud Firestoreを用いる

  • Dockerでも動かせるようにする

google-home-notifierをいったんインストールして書き換えるという作業がすべての元凶なので、書き換え済みのソースコードだけをGitHubに登録して、依存関係のあるモジュールはnpm installでインストールできるようにしました。

環境変数で各種設定を行えるようにしたので、ソースコードを書き換えずに動かすことが出来ます。

google-home-notifiernglokでインターネット側からアクセスできるようにしていますが、URLが変わったりして面倒らしいのでFirebaseのCloud Firestoreを使う方針にしました。

Realtime Databaseを使う事例が多く見つかりますが、永らくベータ版だったFirestoreが最近正式版になったので、勉強を兼ねてFirestoreを使ってみました。

さらにDockerで動かせるようにしておけば、ほぼどんな環境でも動かせるようになるので、Dockerfileとdocker-compose.ymlも準備しました。

それぞれのソースコードはこちらです。

https://github.com/sikkimtemi/google-home-voicetext

https://github.com/sikkimtemi/google-home-voicetext-docker


使い方


事前準備


サーバーとなるマシンを準備する

音声ファイルをホストしてGoogle Homeにキャストするサーバーとなるマシンが1台必要です。

よく見かけるのはRaspberry Piを用いた事例ですが、Node.jsが動けば何でも良いです。

私はMacで開発し、自宅ではQNAPのTS-228AにDockerを入れて動かしています。

QNAPで動かす方法はいずれ記事にするかもしれません。


VoiceTextのAPIキーを取得する

下記のページから「無料利用登録」をクリックして必要事項を記入するとメールでAPIキーが送られてきます。

APIキーは後で環境変数に設定します。

https://cloud.voicetext.jp/webapi


サーバーのIPアドレスを確認する

Google Homeとサーバーは同一ネットワーク上に存在する必要があります。(正確には相互に到達可能なネットワーク上にあればOK)

サーバーが有線LANと無線LANで複数のIPアドレスを持っていたりする場合、Google Homeからは見えないネットワークにあるIPアドレスを設定してしまうと、一見動いているように見えるのに声が出ないという状況に陥るので注意しましょう。私はこれで数時間はまりました。


Firebaseプロジェクトを作成し、Cloud Firestoreにフィールドを作成する

この作業は必須ではありません。

インターネット側からの制御でGoogle Homeを喋らせたい場合には必要です。

image.png

googlehome > chant > messageというフィールドを作成してください。

セキュリティ・ルールは安全のため、なるべくロックモードで運用することを推奨します。

service cloud.firestore {

match /databases/{database}/documents {
match /{document=**} {
allow read, write: if false;
}
}
}

image.png

フィールドを作成したら「設定(歯車アイコン)→プロジェクトの設定→サービスアカウント」を表示して、「新しい秘密鍵の生成」をクリックしてください。

JSONファイルがダウンロードされるので、適当なディレクトリに配置してください。

また、表示されているdatabaseURLの値をメモしてください。

この値と、JSONファイルのファイルパスを後で環境変数に設定します。


インストール

$ git clone https://github.com/sikkimtemi/google-home-voicetext.git

$ cd google-home-voicetext
$ npm install


mdnsにパッチをあてる

Macの場合はこの作業は不要です。

Dockerでは必要でした。

未確認ですが、Raspberry Piでも必要になるはずです。

ソースコードを書き換えないという目標が崩れてしまいましたが、パッチファイルを書いたので許してください。

$ patch -u node_modules/mdns/lib/browser.js < mdns_patch/browser.js.patch


環境変数の設定


必須項目


VoiceTextのAPIキー

$ export VOICETEXT_API_KEY={取得したAPIキー}


サーバーのIPアドレスもしくはネットワークモジュール名

どちらかが必須です。通常はIPアドレスを設定しておけばOKです。

IPアドレスが頻繁に変動する環境のために、ネットワークモジュール名でも設定できるようにしましたが、おそらくそんな環境で運用することはないでしょう。

$ export WIRELESS_IP={サーバーのIPアドレス}

$ export WIRELESS_MODULE_NAME={ネットワークモジュール名}


任意項目


Google HomeのIPアドレス

Google Homeは同一ネットワーク上から名前解決で見つけてきますが、うまくいかない場合や複数のGoogle Homeが存在する場合は対象となるGoogle HomeをIPアドレスで指定することも出来ます。

$ export GOOGLE_HOME_IP={Google HomeのIPアドレス}


Firebase関連

データベースURLの値とダウンロードした秘密鍵のファイルパスを設定してください。

$ export FIREBASE_DATABASE_URL={databaseURLの値}

$ export FIREBASE_SECRET_KEY_PATH={JSONファイルのファイルパス}


VoiceTextの話者

デフォルトはHIKARIです。

環境変数VOICETEXT_SPEAKERに以下の値を設定すると話者を変更することが出来ます。

VOICETEXT_SPEAKERの値
説明

BEAR
凶暴なクマ

HARUKA

HIKARIよりも落ち着いた女性の声

SANTA
浮かれたサンタクロース

SHOW
「モヤモヤさまぁ~ず」のナレーター

TAKERU
落ち着いた男性の声

「モヤさま」を見ていたので、自宅ではSHOW君に設定しています。


各種サーバーの起動


file-server.jsの起動

$ node file-server.js

file-server.jsは音声ファイルをホストしてGoogle Homeにわたす役割のプログラムです。

8888ポートを使用しています。


api-server.jsの起動

$ node api-server.js

api-server.jsはPOSTを受信して、任意のテキストをGoogle Homeに喋らせます。

8080ポートを使用しています。


firestore.jsの起動

$ node firestore.js

これは任意です。インターネット側からGoogle Homeを制御したい場合のみ実施してください。


動作確認


curlコマンドで喋らせる

$ curl -X POST -d "text=こんにちは、Googleです。" http://{サーバーのIPアドレス}:8080/google-home-voicetext

api-server.jsが反応してGoogle Homeが喋れば成功です。

text部分を変えれば喋る内容を自由に変更することが出来ます。

REST APIなので大抵のプログラムからアクセス可能です。


Firestore経由で喋らせる

image.png

上の図を参考にmessageフィールドを更新してください。

firestore.jsが反応してGoogle Homeが喋れば成功です。

messageフィールドは自動的に空になるはずです。

私はGoogle Apps ScriptからFirestoreを更新できるようにしています。

これにより、Googleカレンダーに書いたスケジュールを時間になったら自動的に音声で通知したり、会社に無事到着したら自宅のGoogle Homeが喋って家族に知らせたり出来るようになりました(下記の記事を参照)。

https://qiita.com/sikkim/items/60eac02554b37b14e5a6


Dockerで動かす場合

手動でやるのは大変だったので、Dockerfileとdocker-compose.ymlを書きました。

https://github.com/sikkimtemi/google-home-voicetext-docker

これでコマンド一発でコンテナを起動できるようになりました。

詳しくはREADME.mdを参照してください。


まとめと感想


  • ソースコードの書き換え無しでGoogle Homeに任意の言葉を喋らせる仕組みを構築した

  • ローカルネットワーク内ではREST APIで制御する

  • インターネット側からはCloud Firestore経由で制御する

  • Dockerでも動かせるようにした

環境を構築するのは大変ですが、一度作ってしまえば、標準出力にログを書き出す程度の気軽さでGoogle Homeに喋らせることが出来るようになります。

この環境を構築してから、色々とアイディアが湧くようになりました。