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

## いきさつ

Google Homeが流行っているので、Google Assistantから自宅の家電を操作したいと思いました。IRKitを使うのが簡単そうです([IFTTTとIRKitでGoogleHomeから家電をコントロールする](https://qiita.com/jyoshida/items/cf550d6b645ef69aae28#_reference-5cfa6b4a58efd1749ce8)など)が、IRKitは、2017/10/25現在Amazonでの販売([IRKit - iPhone,iPadを使って外出先からエアコン等の家電を操作できる学習リモコン](https://www.amazon.co.jp/gp/product/B00H91KK26)
)が終了しています。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/168427/4e66c280-fa0b-a39b-cb4c-9e24b18b6b3f.png)

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

- [ビット・トレード・ワン USB赤外線リモコンアドバンス ADIR01P(株式会社ビット・トレード・ワン)](http://bit-trade-one.co.jp/product/module/adir01p/)
- [irMagician(大宮技研 合同会社)](http://www.omiya-giken.com/?page_id=837)

## システム構成

![無題のプレゼンテーション.png](https://qiita-image-store.s3.amazonaws.com/0/168427/42226d21-7879-3ce9-5acc-e488fb523c2f.png)

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

### 各要素の概要と選定理由

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

## 1. Beebotte設定

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

![image.png](https://qiita-image-store.s3.amazonaws.com/0/168427/1c574e8b-e289-49aa-e174-87d029117a8b.png)
![image.png](https://qiita-image-store.s3.amazonaws.com/0/168427/f9594fd1-46eb-27d8-9d62-f44680df39e3.png)

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

```bash
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のサイト](https://ifttt.com/maker_webhooks)にアクセスして、有効にしたうえで、以下のようなレシピで新しい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のコンソールで以下のメッセージが確認できれば、動作確認終了です。

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

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

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

```bash
$ 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に赤外線リモコンから信号を発射することで、信号を受信・保存することができます。

```bash
$ 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`と同じディレクトリに入れてください。

```bash: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の待ち受けが開始されます。

```bash
$ python3 app.py
```

## 5.動かしてみる

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

## 参考サイト

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

- 赤外線リモコンモジュールについて
    - [IFTTTとIRKitでGoogleHomeから家電をコントロールする](https://qiita.com/jyoshida/items/cf550d6b645ef69aae28#_reference-5cfa6b4a58efd1749ce8)
    - [Google home + IRKit で既存の家電を操作するアプリを作ってみた - Qiita](https://qiita.com/flatfisher/items/8e2da20b054674a23021)
    - [【レビュー】 Nature Remoの設定方法と利用した所感。Google Homeとの連携は最高すぎる](http://benrilife.hatenablog.com/entry/natureremo-review)
    - [Raspberry PiとFirebaseでブラウザからエアコン操作](https://qiita.com/horo/items/1b1549ff8354e9788c9e)
- MQTTについて
    - [IFTTTのトリガーおよびアクションをESP8266で実行する](https://qiita.com/mayfair/items/e761c788a9d8787bc610)
- IFTTTについて
    - [IFTTTのMakerチャンネルにネストされたjsonは送れるか？](https://qiita.com/FePlus/items/78973ac5efc2e2d5ce71)