Google AssistantとRaspberry Piで自宅の家電を操作する

  • 10
    Like
  • 1
    Comment

いきさつ

Google Homeが流行っているので、Google Assistantから自宅の家電を操作したいと思いました。IRKitを使うのが簡単そうです(IFTTTとIRKitでGoogleHomeから家電をコントロールするなど)が、IRKitは、2017/10/25現在Amazonでの販売(IRKit - iPhone,iPadを使って外出先からエアコン等の家電を操作できる学習リモコン
)が終了しています。

image.png

後継機のNature RemoはIFTTTを用いてGoogle Home等とすぐ接続できて簡単な半面、若干値段が高い。代替案を探していたところ、RaspberryPiから操作可能な赤外線リモコンモジュールを見つけたので、こちらを使って実現してみました。

システム構成

無題のプレゼンテーション.png

Google Asistantがユーザからの特定の発話を受け取ると、IFTTTをトリガーします。
IFTTTからRapberry Piまでの経路(②,③)ですが、IFTTTのMaker Channelでは、アクションとして外部のウェブサーバに対してウェブリクエストを送信するwebhookがあります。そのため、Raspberry Piをwebサーバにし、ドメイン取得&ポート解放するなどして、グローバルにアクセスできるようにするなどの方法が考えられますが、サーバを公開できないような環境(特に自宅LANなど)も多いと思います。そこで今回は、IFTTTがMQTTブローカーBeebotteにREST APIでメッセージを保存し(②)、Raspberry Piがこのブローカーにメッセージを取得しに行く(③)、という形式をとる事にしました。

各要素の概要と選定理由

  • MQTTブローカ: Beebotte
    • 1日あたりのメッセージ数が50,000個まで無料(その他条件あり)
    • RESTとMQTTの両方から利用可能
  • 赤外線リモコンモジュール: ADIR01P
    • Raspberry Piで動作している例がある
    • 出力が強そう

1. Beebotte設定

Beebotteのサイトでアカウントが作成が完了したら、コンソールパネル上でチャンネルとリソースを作成します(②でメッセージをポストする際のURLの構成要素になります)。作成完了後、コンソールのホームから作成したチャンネルにアクセスするためのChannel Tokenが確認できます。

image.png
image.png

作成したチャネル名、リソース名、Channel Tokenを用いて、以下のようにcURLで動作確認できます。

curl -i -H "Content-Type: application/json" \
  -X POST -d '{"data":"Hello World"}' \
  http://api.beebotte.com/v1/data/publish/<channel名>/<リソース名>?token=<Channel Token>

Beebotteが受け取ったデータは、以下の手順でコンソール上で確認ができます。
1. Beebotteのコンソール画面の「Account Settings」の「Credentials」からSecret Keyを取得
2. このSecret Keyを「Console」の「Secret Key」に入力し、Subscribeのパネルにチャネル名とリソース名を入れて、「Subscribe」を押下
3. 前述のcURLでメッセージを送信
4. Messagesに送信したメッセージが表示される

2. IFTTTの設定

あらかじめ、IFTTTのMaker Channelのサイトにアクセスして、有効にしたうえで、以下のようなレシピで新しいAppletを作成します。

  • トリガー: Google Assistant/Say a simple phrase with a text ingredient
項目 設定値
What do you want to say? リビングの $ つけて
What do you want the Assistant to say in Respponse リビングの $ をつけます
Language Japanese

Google Assistantのフレーズが$から始められないため、「リビング」と入れています。

  • アクション: Webhooks/Make a web request
項目 設定値
URL http://api.beebotte.com/v1/data/publish//<リソース名>?token=
Method POST
Content Type application/json
Body { "data": [{"room": "living","device": "{{TextField}}[^2]","action":"on"}]}

トリガーで受け取った$が{{TextField}}に入ります。

上記の設定完了後、Google HomeやGoogle Assistantに「リビングのテレビつけて」と言った際に、前述のBeebotteのコンソールで以下のメッセージが確認できれば、動作確認終了です。

{ "channel": <チャンネル名>, "resource": <リソース名>", "eid": xxxx, "data": [ { "room": "living", "device": " テレビ", "action": "on" } ] }

3. ADIR01Pの接続とリモコン信号の学習

ADIR01PとRaspberry PiをUSBで接続し(mini USBなので注意)、動作確認・リモコン信号の学習を行います。今回はRaspberry Pi3 Model Bを利用しました。
ADIR01PはUNIX系環境用コマンドラインツールが公開されていますので、それをダウンロードし、コンパイルして利用します。Raspberry Pi上からダウンロードできなかったので、他のマシンでダウンロードしてからSCPでコピーしました。libusbも必要なので、インストールしてください。

$ unzip bto_advanced_USBIR_cmd.zip
$ cd bto_advanced_USBIR_cmd
$ sudo apt-get update
$ sudo apt-get install libusb-1.0.0
$ make
$ sudo make install

コマンドラインツールbto_advanced_USBIR_cmdが作成されるはずです。
次に、以下の手順で受信を開始し、ADIR01Pに赤外線リモコンから信号を発射することで、信号を受信・保存することができます。

$ bto_advanced_USBIR_cmd -r         # 受信開始
# 次に記録したい機器の赤外線を、受信部に向けて、発射します。
$ bto_advanced_USBIR_cmd -s         # 受信停止
$ bto_advanced_USBIR_cmd -g | tee living_light_on.txt  # ファイル保存

living_light_on.txtに信号が保存されます。今回は証明用のリモコンを記録させました。

4. Raspberry PiでのMQTT受信と赤外線信号送信

Pythonを使って、MQTT受信と赤外線信号送信用のスクリプトを作ります。今回用いたpythonと依存パッケージのバージョンは以下の通りです。

  • Python 3.4.2
  • paho-mqtt==1.3.1

以下のファイルを先ほど作成したliving_light_on.txtと同じディレクトリに入れてください。

app.py
#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import paho.mqtt.client as mqtt
import json
import subprocess
import os
_TOKEN = 'XXXXXXXXXX' #IFTTTに設定したものと同じChannel Tokenを設定
HOSTNAME = 'api.beebotte.com'
PORT = 1883 #SSLの場合は8883
TOPIC = 'YYYYYYY/ZZZZZZZZZZZ' #トピック名/リソース名 の形で設定

# 名詞の変換表
noun_conv = {
    "照明": "light",
    "ライト": "light",
    "らいと": "light",
    "しょうめい": "light",
    "ショウメイ": "light",
    "証明": "light"
}

# 接続中の処理を記載→subscribeする
def on_connect(client, userdata, flags, respons_code):
    print('status {0}'.format(respons_code))
    client.subscribe(TOPIC)

# メッセージ受信時の処理を記載
def on_message(client, userdata, msg):
    print(msg.topic + ' ' + str(msg.payload))
    data = json.loads(msg.payload.decode("utf-8"))["data"][0]
    # Google Assistantの側でTextFiledの前後に半角スペースが入ることがあるのでstripして削除
    data = {key:value.strip() for key, value in data.items()}
    if "room" in data.keys():
        # 「証明を」と言ってしまった際に「を」が入ってTextFieldに入ってくることがあるので削除
        if data["device"].endswith("を"):
            data["device"] = data["device"][:-1]
        # 日本語の表記ゆれに併せて、IFTTTのTextFieldをlightに修正
        if not data["device"] in set(noun_conv.values()):
            try:
                data["device"] = noun_conv[data["device"]]
            except:
                print("unkown device")
                return
        fname = "%s_%s_%s.txt"%(data["room"],
                                data["device"],
                                data["action"])
        if os.path.exists(fname):
            signal = open(fname).read()
            subprocess.call(['bto_advanced_USBIR_cmd', '-d', signal])

if __name__ == '__main__':
    client = mqtt.Client()
    #トークンを使って認証
    client.username_pw_set("token:%s"%_TOKEN)

    client.on_connect = on_connect
    client.on_message = on_message

    client.connect(HOSTNAME, port=PORT, keepalive=60)

    # 待ち受け状態にする
    client.loop_forever()

app.pyの入ったディレクトリで以下を実行すると、MQTTの待ち受けが開始されます。

$ python3 app.py

5.動かしてみる

Google HomeやGoogle Assistantに「リビングのライトつけて」と言うと、リビングのライトが無事つきました。
Google AssistantにIFTTT, Beebotteなどのサービスを組み合わせると、簡単に音声でのリモコン操作が実現できました。
ただ、RaspberryPiとirMagicianの料金を合わせると、Nature Remoとほぼ変わらないような値段になってしまいました。すでにある機材を組み合わせたい場合などを除いた方が、Nature Remoを買った方が良いかもしれないです。

参考サイト

以下のサイトを参考にさせていただきました。ありがとうございます。

301contribution

リンクしていただきありがとうございます:relaxed: