RAKUS Developers Blog | ラクス エンジニアブログ

株式会社ラクスのITエンジニアによる技術ブログです。

Google Home と SwitchBot API を使って、声で家電を自在に操作できるようにしてみた

1. はじめに

家電にはリモコンが欠かせません。
テレビ、エアコン、照明など、私たちの生活に密接に関わっている家電製品は、その操作のために専用のリモコンが必要になります。
私の場合、テレビにブルーレイレコーダーとスピーカーを繋いでいたため、リビングの机の上に3つのリモコンを常設していたのですが、リモコンを置く場所をとったり、リモコンを見失ったり と、解決したい問題が発生していました。
そこで、SwitchBotのリモートリモコンを使い、リモコンをアプリ管理に変更することにしてみました。

  • SwitchBotのリモートリモコンとは、家電製品をスマートホームに対応させるための便利なデバイスです。
    このリモコンはWi-Fiに接続してネットにつながることができ、事前にリモコンの赤外線を学習させることで、対応するスマートフォンのアプリを介して家電を操作できるようになります。

ただ、SwitchBotを使っていても、アプリをタップしてからリモコン操作が可能になるまでに時間がかかり、不便を感じていました。
そこで、Google Homeを使って 声で命令を出せるようにしたのですが、それでも、下記のような不満を抱えてしまいました。
(微妙な言葉の揺れを吸収してくれたりしたので、機能性に関しては満足しました。)

私が使用している照明は、リモコンの「電源」ボタンを押下すると、ON→常夜灯→OFF→ONのサイクルで状態が変わります。
Google Homeは、ONとOFFのステータスしか管理しないため、電気を消したいと思ったとき、
・「OK、Google、照明を消して」-> 照明が常夜灯になり、Google Home上のステータスは「OFF」になる
・もう一度「OK、Google、照明を消して」 -> 照明がOFFになるが、Google Home上のステータスは「ON」になる
といったことが起きました。

照明を消すために、「OK、Google、照明を消して」を2回いう必要があり、その上、ステータスもずれてしまうのです。
ほかにも、スピーカーの音量を調節できない、扇風機の風量を変更できない等、細かい部分で不満点が残りました。

調べていると、Google Assistantのプロジェクトを自作することで、家電を操作することが可能であることを知りました。
自分で家電の操作のロジックを作ることができるのであれば、抱えている不満点を解消できるのではと思い、実践してみました。

本記事では、Local Home SDKを用いて、実装を行ったので、その手順を紹介しようかと思います。
詳細は後述しますが、上記を利用すると 家電を操作するために作成するコード、アプリを外部に公開する必要がなくなり、私にとって大きなメリットに感じたため採用しました。
もし悪用されて、家の家電が見知らぬ人によって勝手に動き出すことを考えると、公開するべきじゃないと思いました、、

また、サンプルのGit プロジェクトがあるため、迷う時間を短縮できるとも思いました。
該当のサンプルは、Local Home SDK を使わないパターンのコードも含まれているため、「コードを外部に公開することに躊躇いがない」場合も、そちらを流用できます。

2. Google Homeのしくみ

まず、Google Home がスマート家電を操作するときの仕組みを理解しておく必要があるので、簡単に説明します。
①例えば、Google Home に「OK、Google、照明を消して」と命令したとします。
Google Homeは受け取った音声を、クラウド上のGoogle Assistant に投げます。
Google Assistantが家電操作の命令と判断した場合、受けとったワードに関連するスマート家電情報をHome Graphから取得します。

Home Graph について

急に現れたワード、"Home Graph" についての説明を簡単に挟みます。
Home Graphとは、端的に言うとGoogle Home が操作可能な家電を管理するデータベースのようなものになります。
Google Home と対応したプロジェクトの連携を行ったとき、そのプロジェクトが管理しているスマートデバイスリストを取得し、保持します。
調べたところ、「ライト」「照明」 などといった言葉の揺れを吸収しているのが、Home Graphのようです。
複数のライトが登録されている時、「リビング」のGoogle Home から「ライトをつけて」とだけ命令しても、暗黙的に「リビングのライト」を選択してくれます。

Home Graph の詳細

developers.google.com ④Home Graph から受け取った情報と紐づくプロジェクトに対して、実際に家電を動かすように「EXECUTE」リクエストを送ります。
 例えば、受け取った家電情報が SwitchBot で登録した照明だった場合、紐づいているプロジェクトの設定に従って、SwitchBot に向かってリクエストが飛びます。
⑤「EXECUTE」リクエストを受け取ったSwitchBot サービスがからリモートリモコンに対して、「照明の電源ボタンを1回押す」といった命令を渡し、
⑥それをリモコンが実行することで、照明が消灯(私の場合は常夜灯)します。

もし、ライトがスマート家電だった場合は、リモートリモコンを介さずに照明に対して直接「照明を消す」命令が飛ぶということになります。

以上のような原理で、Google Homeを使って家電を音声操作することができます。

3. Local Home SDK とは

Local Home SDK とは Google が提供する技術で、こちらを使うことでGoogle Home がローカルのネットワークを使って家電に対して、 操作コマンドを直接送信することができるようになります。

developers.home.google.com

図を使って、簡単に説明します。

まず前提として、プロジェクトはあらかじめ「Local Home SDK」を用いて実装した Google Home 上で動くプログラムファイル(言語はTypeScript系)を用意しておく必要があります。
Google Home は、プロジェクト連携時にそのプログラムファイルを取得します。
※プロジェクトを紐づけるか、端末を再起動することで自動的に取得する模様 命令を受け取り、Home Graph から家電情報を受け取るところまでは同じですが、ここから先のフェーズが異なります。
Google Assistant は、受取った情報とともに「EXECUTE」リクエストを"Google Home"に返します。
⑤「EXECUTE」リクエストを受け取った Google Home は、あらかじめ取得していたプログラムファイルに従って、 操作コマンドをローカルネットワーク経由で家電に対して直接実行します。
⑥リモコンが家電を操作する仕組みは同じです。

Local Home SDK を使って、やろうと思ったこと

Google Home は定期的にローカルネットワークに対して、操作可能なデバイスがないか、スキャンして確認しています。
上記に対して、HomeGraph に登録されている家電情報に紐付くIDを返すデバイスを発見すると、Google Home はそのデバイスを該当のスマート家電と認識します。

つまり、同じローカルネットワーク内にあるPCなどで Google Home のスキャンにして、該当するIDを返す仕組みを実装すると、
以降、Google Home はそのPCをスマート家電と認識するので、「EXECUTE」リクエストを受け取れるようにすることができるようになります。
※ラズパイ等でも代用は可能ですが、知識がない(そもそも持っていない)ので、PCを採用しています。

「EXECUTE」リクエストを任意のデバイスで受け取ってしまえば、そこから先の処理は自作することができます。
図の通り、SwitchBot API を叩いてスマートリモコンを経由で家電の操作も可能になるということです。

上記のような仕組みを利用することで、先述した通り 家電を操作するアプリを外部に公開する必要がなくなります。

4. Local Home SDKの導入手順

最初に話した通り、Local Home SDKのサンプルコードを使って実装の準備を進めます。
Google 公式の CodeLabs もあるので、その手順に従って、準備します。

developers.home.google.com

本記事では、上記に書いてある実装・操作手順的な内容はばっさりそぎ落とし、簡単な補足情報をまとめようかと思います。

①あなたが始める前に

本記事の「2.Google Homeのしくみ」「3.Local Home SDK とは」にあたる内容が記載されています。

②入門

準備として Action on Google と Firebase の初期化を行います。

Firebase を使って、「Google Assistant のプロジェクト」のアプリ部分を作成します。
Action on Google の設定を完了することで、上記で作成したアプリを「Google Assistant のプロジェクト」として登録するイメージです。

③スターター アプリを実行する

ここから用意されているサンプルコードを用いて、実際に実装していくフェーズです。

github.com

コードの更新がしばらく行われておらず、node のバージョンが古くなっていました。
私の場合、node のバージョンが環境と合わず、バージョンの更新とそれに伴う依存関係の修正を行いました。

ここまでの準備が完了すると、Firebase Functions にデプロイしたプログラムに対して、Google Assistant から「EXECUTE」リクエストが送信されるようになります。
既存では、パラメータに応じて Firebase Realtime Database のステータスを変更する 処理が走るのみですが、コードを変更すれば実装可能です。
Local Home SDK の仕組みを使用していませんが、家電を操作する実装を行うことができるようになります。

Local Home SDK の機能を使わなくてもいいのであれば、ここまで準備すれば Google Home を使って家電操作できます。
・functions > index.js を実装することで、家電操作が可能になります
具体的な実装方法の詳細は、後述します。

Local Home SDK を使った仕組みを目指すのであれば、次へ進みます

クラウド フルフィルメントの更新

Local Home SDK を使用するための準備フェーズになります。
Google Home からのスキャンに対して、otherDeviceIds に指定したIDを返すことで、Google Home は該当のデバイスだと認識します。

⑤ローカル フルフィルメントを構成する

ここで設定した内容に従って、Google Home がローカルネットワークのスキャンを行います。

⑥ローカル フルフィルメントの実装

ここで、Local Home SDK を使った実装になります。

  • identifyHandler

Google Home がローカルネットワークをスキャンし、その結果を処理する箇所になります。
対応しているデバイスは、otherDeviceIds に対応した ID を返してくるので、それをそのまま返すようにしています。

  • executeHandler

Home Graph からきた「EXECUTE」リクエストを処理する部分です。

基本的に、Google Assistant から受け取ったコマンドをそのまま仮装デバイスに渡すだけで良いと思うので、
ここのコードは公式の内容から特に変更はしなくて良いかと思います。

ここでSwitchBot API を叩くという荒技もできそうですが、やめておきました。

⑦スマートウォッシャーを開始する

node 上で動く 仮想のスマート家電を起動します。
Local Home SDK を使った音声での命令が成功すると、ログにそのことが出力されます。
また、併せて Firebase Realtime Database のステータスも変更します。

⑧TypeScript アプリをデバッグする

記載の通りGoogle Home 上で動くコードは、同じネットワークにつないでいるPCのChromeブラウザ > inspect でデバックすることが可能です。
また、スクリプトを更新した場合、Google Home を再起動しないとその変更は反映されません。

➈おめでとう

ここまで完了すると、Google Home と仮想スマート家電との疎通が完了します。
サンプルコードはログを出力するだけですが、コードを変更すれば家電操作ロジックを実装できます。

実装方法の詳細は、次の章で解説します。

5. 実装手順

既存のサンプルコードの状態で扱うことができるスマート家電は「洗濯機」で、その機能も限られています。
もちろん、上記は変更可能です。
ここではその方法と、実装手順を紹介いたします。

任意のタイプの家電を登録する

まずは、任意の種類を 家電を登録する方法です。
Google Home からくる、「Sync」リクエスト に応答することで、任意のスマート家電を登録することが可能です。
サンプルコードでいうと、functions > index.js の onSync で返すオブジェクトが、これにあたります。

家電の種類は、payload.devices.type の値で報告できます。
公式のドキュメントに報告できる種別がすべて乗っています。

developers.home.google.com

上記から一つだけ選択して、報告します。
例えば、照明に変更したい場合は、"action.devices.types.LIGHT"を指定します。

    payload: {
      agentUserId: USER_ID,
      devices: [{
        id: 'washer',
        type: 'action.devices.types.LIGHT',  // ここを変更する
        ....

これでアプリ上のロゴもライトに変わり、Home Graph で解釈する言葉の揺れも、照明用に代わります。
言葉の揺れ に関して、payload.devices.name.nicknames を変更することで、任意のワードを登録することができるようになります。

        ....
        name: {
          defaultNames: ['My Washer'],
          name: 'Washer',
          nicknames: ['間接照明', 'テレビ台の上のライト', ],  // ここを変更する
        },
        ....

上記のように設定すると、「OK、Google. 間接照明をつけて」や「OK、Google. テレビ台の上のライトを消して」と話しかけた場合も、登録した機器が選択されるようになります。
「Sync」リクエスト で報告する内容の詳細は下記を参考にしてみてください。

developers.home.google.com

バイスに備わってる機能を変更する

こちらは、payload.devices.traits の値を変更することで解決することができます。
バイスタイプと同様に、公式対応表を参照します。

developers.home.google.com

こちらは、複数指定することができます。
例えば、"action.devices.traits.Brightless"を指定することで 、明るさを調整するための命令ができるようになり、対応する「EXECUTE」リクエストが送られるようになります。

余談ですが、、
私は、"action.devices.traits.Modes" を乱用しています。
任意の「モード」を追加することができ、様々なことに汎用することができます。
「テレビ」に対して、「NETFLIXモード」「YouTubeモード」「Amazon Primeモード」を設定すれば、「OK、Google. テレビをAmazo Primeモードにして」の一言で、
テレビで Amazon Prime を再生させることも可能になります。

SYNC:追加された機能の詳細

実は、traits で任意の機能を報告するだけでは、機能を追加することができない場合があります。
Volume (音量調整) がわかりやすいので、例としてみてみたいと思います。

developers.home.google.com

「デバイスの属性」の章を確認すると、「Sync」リクエスト で次の情報を報告する必要があることがわかります。

  • volumeMaxLevel:ボリュームの最大値

  • volumeCanMuteAndUnmute:ミュート機能に対応しているかどうか

  • volumeDefaultPercentage:デフォルトのボリューム値

  • levelStepSize:ボリュームを相対的に調整するときの変化量

  • commandOnlyVolume:現在のステータスを報告できるかどうか

traits = action.devices.traits.Volume と併せて、上記を報告することで初めて、ボリューム調整機能を追加することができます。

Query:現在の家電の状態を報告する

現在の状態やステータスを、HomeGraph に報告することで Google Home アプリに現在のスマート家電の状態を表示することができます。
これがなくても動かすことは可能ですが、実装することをお勧めします。

サンプルコードでは、functions > index.js の onQuery に実装されています。

  return {
    on: data.on,
    isPaused: data.isPaused,
    isRunning: data.isRunning,
    currentRunCycle: [{
      currentCycle: 'rinse',
      nextCycle: 'spin',
      lang: 'en',
    }],
    currentTotalRemainingTime: 1212,
    currentCycleRemainingTime: 301,
  };

報告が必要な key は、報告している traits によって決まります。
traits の詳細のページで、確認することができます。

'action.devices.traits.OnOff'(電源のオンオフ切り替え)と'action.devices.traits.Brightless'(明るさ調整)機能を報告した照明を登録した場合を考えてみます。

それぞれを確認すると、on と、brightness を返す必要があることがわかります。

  return {
    on: true,  
    brightness: 50
  };

※電源がついており、その明るさが 50% であることを報告している

EXECUTE:追加される「EXECUTE」リクエスト に対応する

payload.devices.traits を追加して機能を増やすと、その機能に対応した「EXECUTE」リクエストを処理する必要が出てきます。
サンプルコードでいうと、app-start > functions > index.js の onExecute、もしくは、virtual-device > washer.js の state(既存の関数名だと微妙ですが)で「EXECUTE」リクエストを処理しています。

公式のドキュメントを確認することで、どのような「EXECUTE」リクエストが来るかを確認することができるので、
それを参考に、実装を行います。
"action.devices.traits.OnOff"を例に、私の実装を例として簡単に紹介します。

developers.home.google.com

「ライトをオンにして」と Google Home に話しかけたら、下記のような「EXECUTE」リクエストを受け取ることがわかります。

{
  "command": "action.devices.commands.OnOff",
  "params": {
    "on": true
  }
}

params > on の値を参照し、下記のように実装すれば、家電の電源を切り替えることができます。

// req = 受け取った「EXECUTE」リクエスト
const data = req.body;

// コマンド種別を取得する
const command = data.command

switch (command) {
    case 'action.devices.commands.OnOff'
        // 切り替えたいステータスを取得
        const isOn = data.params.on;
        if (isOn) {
            // 電源を入れるケース
            switchBotApi.on();
        } else {
            // 電源を切るケース
            // 電源ボタンを2回押下する
            switchBotApi.off();
        }
    case 'action.devices.commands.BrightnessAbsolute'
        // 省略
}

オフの時だけ電源ボタンを2回押下するように実装できるので、初めに挙げていた照明のステータスがずれる問題を解決することができました。
※SwitchBot API の叩き方の詳細は、下記をご覧ください。

github.com

6. さいごに

「OK、Google テレビをつけて」の指示で、テレビとスピーカーの電源を同時に入れるようにしたり、
「OK、Google 広告をオフにして」の指示で、YouTube の広告をスキップさせたりと、好きなように家電をコントロールすることができるようになります。

単に Google Home と、SwitchBot サービスを連携させるだけでは上記は実現できませんし、最初に上げたような不具合が発生したりします。
そこを自分で解決することができたので、試して良かったと感じています。

また、コードを動かすトリガーを声にすることができるので、SwitchBot API に限らず、様々なこと(家電操作以外も)ができそうです。
他になにかできないか模索し、より便利なスマートハウスの構築を目指していこうかと思います。

Copyright © RAKUS Co., Ltd. All rights reserved.