Google Home + 赤外線コントローラ + Raspberry piで非IoTデバイスを操作

# Google Homeで家電を操作したい

楽天スーパーセールでGoogle Home miniが半額で売っていたのでついつい衝動買い。
せっかく買ったのだから、Google Homeで家電を操作する未来な生活がしたい！
そんなことをふと思い、どうすればそれができるか調査をしてみました。
調査の結果、我が家で一番安く構築できる方法をメモとして残しておきます。

※お約束で書いておきますが、筆者はこの方法を推奨するわけでも、この方法で動くという保証をするわけでもありません。もし、まねをされる方は自己責任でおねがいしますね。

## 方法１：Google Homeに対応した家電に買い換える

家電をGoogle Homeに対応したものに買い換えるのが一番手っ取り早いです。
しかし、家電をすべてGoogle Home対応に買い換えるのは現実ではないですね。
少なくても、我が家のお財布では無理なので却下。

## 方法２：IFTTTに対応した赤外線コントローラーを購入

今我が家にあるテレビ、エアコン、蛍光灯など家電の多数が、赤外線リモコンで動きます。
ならば、赤外線コントローラーを使うことでこれらを操作できるのではないかと思い調査をしてみると、IFTTTという規格に対応した赤外線コントローラーを購入すれば、Google Homeと連携できるらしい。
わくわく調べてみると、結構対応した機器があるようでした。

- [iRemocon Wi-Fi IRM-03WLA](http://i-remocon.com/) ￥20,741（2017/11/24現在）
- [Nature Remo](http://nature.global/) ¥13,000（2017/11/24現在）

・・・うーん、どちらも私のお小遣いだとちょっと厳しい。

## 方法３：Raspberry pi + IFTTT非対応赤外線コントローラー

できるだけ低価格でできないものか考えた結果、Raspberry piを活用しつつ、普通の赤外線コントローラーを使う方法を発見。

- BroadLink社 RM Mini3
- LinkJapan社 eRemote mini

のいずれかを使えばいけそう。

幸い、我が家にはVPNサーバ/SIPサーバとして稼働中のRaspberry piもあることから、赤外線コントローラーさえあれば動かせるような気がします。
ということで、前置きが長くなりましたが、この方法で家電操作IoTを実現してみたいと思います。

なお、eRemote miniとRM Mini3のスペックはほぼ変わらないようです。
RM Mini3は値段が約1/3となりますが、スマートフォンアプリが日本ではダウンロードできないようなので、eRemoteを使う方がよいかもしれません。

# 実現方法

![001.png](https://qiita-image-store.s3.amazonaws.com/0/143896/47dd867f-7cec-491e-71d6-d8c180efd9ed.png)

順番に見ていくと、

1. Google Homeに対して、音声で指示を飛ばします。
2. Google HomeはIFTTTというサービスを照会し、音声で指示された命令があるかチェックします。命令が存在した場合は、それを実行します。今回はWebhooksを利用して、Beebotteに対してMQTT publish（メッセージの発行）を行います。
3. 自宅内に設置したRaspberryPiで、MQTTメッセージ購読を行います。MQTTメッセージを受信した場合、Pythonコマンド or Shellを実行し、BlackBeanControlを実行します。
4. BlackBeanControlは、赤外線コントローラーに対して指示を飛ばします。

今回の構築では、4→３→２の順番で構築していきます。

# BlackBeanControlの構築
まず、Raspberry piに[BlackBeanControl](https://github.com/davorf/BlackBeanControl)を構築します。

## BlackBeanControlのインストール
やり方はGitHubに書いてありますが、私がやった作業を以下に残しておきます。
ちなみに、私のRaspberry piはすでにいろんな環境を立ててあるため、BlackBeanControlのインストールに必要なものがすでに入っていた可能性もあります。
あくまでご参考程度に。

```
// 作業用ディレクトリを/opt/work/googlehomeに作成
$ sudo mkdir /opt/work/googlehome
$ sudo chown -R pi.pi /opt/work

// pipのインストール
$ cd /opt/work/googlehome
$ wget https://bootstrap.pypa.io/get-pip.py
$ sudo python get-pip.py 

// python-devのインストール
$ sudo apt-get install python-dev

// configparser、netaddr、pycryptoのインストール
$ sudo python -m pip install configparser
$ sudo python -m pip install netaddr
$ python -m pip install pycrypto --user

// broadlinkのインストール
$ git clone https://github.com/mjg59/python-broadlink.git
$ cd python-broadlink/
$ sudo pip install brodlink

// BlackBeanを/opt/BlackBeanにインストール
$ cd /opt/
$ sudo mkdir BlackBean
$ sudo chown pi.pi BlackBean/
$ cd BlackBean
$ git clone https://github.com/davorf/BlackBeanControl.git
```
## 赤外線コントローラーをネットワークに接続

「eRemote mini」 or 「RM Mini3」をネットワークに接続します。
接続には公式アプリで行う必要があります。
RM Mini3の場合、日本のAppStoreからダウンロードできないため、接続については自力でがんばってください。
接続が完了したら、ルーターの管理コンソールかなんかんで、IPアドレスとMacアドレスを調べてください。

## 設定
設定ファイルを変更します。

```BlackBeanControl.ini
[General]
IPAddress = xxx.xxx.xxx.xxx
Port = 80
MACAddress = xx:xx:xx:xx:xx:xx
Timeout = 10

[Commands]
※これ以下は書かなくてOK
```

| プロパティ | 設定 |
|:-:|:-:|
| IPAddress | 赤外線コントローラーのIPアドレス |
| Port | 赤外線コントローラーのPort（80固定だと思う） |
| MACAddress | 赤外線コントローラーのMacアドレス |
| Timeout | 赤外線コントローラーの学習を行う際のタイムアウト |

## 赤外線の学習
RaspberryPiで以下のコマンドを実行後に、上記で設定したTimeout時間内に赤外線コントローラーにリモコンの学習をさせます。

```
$ python2 /opt/BlackBean/BlackBeanControl/BlackBeanControl.py -c <コマンド名>
```

コマンド名は、私の場合「デバイス名_アクション」にしています。
以下、こののルールで説明をしたいと思います。

# Beebotteの設定
MQTTサービスであるBeebotteに加入します。
Beebotteは、1日50,000アクセスまで無料で使えるので、一般家庭で家電を操作するのには十分だと思います。

## Beebotteの登録
[Beebotte](https://beebotte.com/)にアクセスし「Sign up」をクリックし、必要情報を入力して会員登録を行います。

## Channelの登録
登録が完了したらSign inし、Homeにアクセスします。
My Channelsが表示されるので「Create New」をクリックし、必要情報を入力します。

<img width="851" src="https://qiita-image-store.s3.amazonaws.com/0/143896/2b676c78-1034-ae11-0273-f04d5121c4bc.png">

| 入力フィールド | 設定 |
|:-:|:-:|
| Channel Name | チャンネル名 |
| Channel Description | チャンネルの説明。なんでもいいです。 |
| Publicチェックボックス | OFF |
| Resource name | チャンネル内のリソース名 |
| Resource Description | リソースの説明 |
| プルダウン | 「any」を選択してください。 |
| SoSチェックボックス | ON |

## Channel Tokenを控える
BeebotteのHomeを開き、channelsの中から先ほど作成したチャンネルのリンクをクリックします。
<img width="917" src="https://qiita-image-store.s3.amazonaws.com/0/143896/42501870-b80c-0cdf-9715-443498674fbc.png">

表示されるページの「Channel Token」を控えます。
これは、IFTTTのWebhooksからBeebotteにMQTTメッセージを飛ばす際に使用します。
<img width="949" src="https://qiita-image-store.s3.amazonaws.com/0/143896/dc3e7ba7-8616-32ed-8b2f-cdd8a78fecb9.png">

## Secret Keyを控える
Beebotteの「Account Settings」を開き「Credentials」タブを開きます。
その中にある「Secret Key」を控えます。
これは、MQTTクライアントのユーザー名として使用します。
<img width="941" src="https://qiita-image-store.s3.amazonaws.com/0/143896/1901c05b-29f6-65e2-0732-30af9d8e30fc.png">


# MQTTクライアントの実装
Beebotteを監視し、MQTTメッセージを受信した際、BlackBeanControlのコマンドを実行するMQTTクライアントを実装します。
今回はPythonが簡単そうだったので、Pythonで実装しました。
（実はPython初挑戦）

## PythonのMQTTライブラリをインストール

PythonのMQTTライブラリである「paho-mqtt」をインストールします。

```
$ sudo pip install paho-mqtt
```

## MQTTクライアントの実装
Beebotteから受信し、受信したJSONを解析し、BlackBeanControlコマンドを実行するPythonを以下のように作りました。

```py3:mqtt_subscriber.py
#!/usr/bin/python
# -*- coding: utf-8 -*-

import paho.mqtt.client as mqtt
import json
import commands

host = 'mqtt.beebotte.com'
port = 1883
username = '<上記で控えたSecret Key>'

topic = '<上記で設定したチャンネル名>/<上記で設定したチャンネル内のリソース名>'

def on_connect(client, userdata, flags, respons_code):
    print('status {0}'.format(respons_code))
    client.subscribe(topic, 1)

def on_message(client, userdata, msg):
    print(msg.topic + ' ' + str(msg.payload))
    json_dict = json.loads(str(msg.payload))
    group = json_dict['data'][0]['group']
    action = json_dict['data'][0]['action']
    commandType = json_dict['data'][0]['commandType']
    print('debug:' + 'commandType is ' + commandType + ' / ' + 'group is ' + group + ' / ' + 'action is ' + action)

    if commandType == 'python':
      commands.getoutput("python2 /opt/BlackBean/BlackBeanControl/BlackBeanControl.py -c " + group + "_" + action)
    elif commandType == 'shellArg1':
      arg1 = json_dict['data'][0]['arg1']
      commands.getoutput("/opt/BlackBeanShell/./" + group + "_" + action + " " + arg1)
    elif commandType == 'shell':
      commands.getoutput("/opt/BlackBeanShell/./" + group + "_" + action + ".sh")
    else:
      print('エラー')

if __name__ == '__main__':
    client = mqtt.Client(protocol=mqtt.MQTTv311)

    client.username_pw_set(username)
    client.username_pw_set(username)

    ### callback function
    client.on_connect = on_connect
    client.on_message = on_message

    client.connect(host, port=port, keepalive=60)
    client.loop_forever()
```

## MQTTクライアントの実行
上記で作ったMQTTクライアントを、以下のコマンドで実行します。
（MQTTクライアントが/opt/mqtt/mqtt_subscriber.pyの場合）

```
python /opt/mqtt/mqtt_subscriber.py
```
コンソール上に「status 0」が表示されれば準備OKです。
0以外のステータスが表示されている場合、設定に不備がある可能性があります。
[こちら](https://jaimyn.com.au/mqtt-connection-failed-status-codes-connack-return-codes/)を参考にしながら解決してください。

## MQTTクライアントが動くか実験
実装したMQTTクライアントが動くか実験します。
ターミナルから、以下のコマンドを実行し、BeebotteにMQTT publishしてみます。
以下のコマンドは、BlackBeanControlの赤外線学習で「TV_on」で学習させた場合の見本です。
groupを「TV」、actionを「on」にしています。

```
  curl -i -H "Content-Type: application/json" \
  -X POST -d '{ "data": [{"commandType":"python","group": "TV","action":"on"}]}' \
  http://api.beebotte.com/v1/data/publish/<上記で設定したチャンネル名>/<上記で設定したチャンネル内のresource名>?token=<上記で控えたChannel Token>
```

MQTTクライアントを動かしているサーバーのターミナルに、JSONが表示されれば成功です。
（これまでの設定がうまくいっていて、赤外線コントローラーがテレビの近くにあれば、テレビがつきます）

# IFTTTの設定
ここまで来ると、もうあと一踏ん張り。
IFTTTの設定を行います。

## IFTTTの登録
[IFTTT](https://ifttt.com/)のページに行き、「Sign Up」をクリックします。
Google Homeで使用するGoogleアカウントでアクセスするのがおすすめなので「Continue with Google」から、ログインを行います。

## レシピの追加
右上のユーザー名をクリックし「New Applet」を選択します。
<img width="1183" src="https://qiita-image-store.s3.amazonaws.com/0/143896/b1628d52-f804-7076-e9a2-bf594720b90e.png">

次に、「＋this」をクリックします。これ、最初わからなくてハマりました。
<img width="1137" src="https://qiita-image-store.s3.amazonaws.com/0/143896/fc9660f8-3564-7a2c-883b-1d5acf9a8d81.png">

サービス選択で「Google Assistant」を選択します。
<img width="1120" src="https://qiita-image-store.s3.amazonaws.com/0/143896/5bd3643a-795b-37c2-f0d6-4addfb0656f3.png">

今回は「Say a simple phrease」を選択します。
チャンネル切り替えなどは「say a phrase with a number」を選択します。
<img width="1124" src="https://qiita-image-store.s3.amazonaws.com/0/143896/65b6e858-3085-9d95-6fb3-215f1be6d87c.png">

次の画面は画面キャプチャが面倒くさいので説明のみ。

| 入力フィールド | 説明 | 設定例 |
|:-:|:-:|:-:|
| What do you want to say? | Google Homeに対して言う言葉 | テレビをつけて |
| What's another way to say it? (optional) | （必要であれば）別の言い方 | モニターをつけて |
| And another way? (optional) | （必要であれば）別の言い方 | テレビジョンをつけて |
| What do you want the Assistant to say in response? | Google Homeからの返事 | はい、テレビをつけます。 |
| Language | これらのやりとりの言語 | Japanese |

次に、「＋that」をクリックします。
<img width="1122" src="https://qiita-image-store.s3.amazonaws.com/0/143896/a53e49a4-ac62-e0b5-cd0c-b674a2199303.png">

「Webhooks」を選択します。
<img width="1124" src="https://qiita-image-store.s3.amazonaws.com/0/143896/02c0fc72-7b5b-0592-80ce-13b912db9387.png">

「Make a web request」を選択します。
<img width="1122" src="https://qiita-image-store.s3.amazonaws.com/0/143896/7d231303-2680-597a-8045-a3788aefde02.png">

| 入力フィールド | 説明 | 設定例 |
|:-:|:-:|:-:|
| URL | BeebotteのURL | http://api.beebotte.com/v1/data/publish/<上記で設定したチャンネル名>/<上記で設定したチャンネル内のresource名>?token=<上記で控えたChannel Token> |
| Method | HTTPのMethod | POST |
| Content Type | BeebotteのWebhooksする際のコンテンツタイプ | application/json |
| Body | Beebotteに送るJSON | { "data": [{"commandType":"python","group": "TV","action":"on"}]} |

最後に「Finish」ボタンが出てくるので、そちらをクリック。

これで設定が完了です。
Google Homeに「OK Google。テレビをつけて」と言ってみましょう。
きっと表示されるはずです。

# 最後に
思った以上に大がかりなシステムになってしまいました。
いろんなシステムを還して実現しているため、どうしてもレスポンスが遅いです。
テレビのザッピングには向かないなぁという印象。
お金があるのなら、設定も楽だし、素直にIFTTTに対応した赤外線コントローラーを買う方が賢明だと思います。
まぁ、IT-DIYが大好きな人向けですね。