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

More than 1 year has passed since last update.


いきさつ

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/<channel名>/<リソース名>?token=<Channel Token>

Method
POST

Content Type
application/json

Body
{ "data": [{"room": "living","device": "{{TextField}}","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を買った方が良いかもしれないです。


参考サイト

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