Raspberry PiとSlackで外出先から自宅のエアコンを動かそう！

# はじめに

暑いですよね。外出時に家電（エアコン）をリモコン制御してみたいと思い、Raspberry Piを買ってみました。

リモコンは下記の学習リモコン（Raspberry Pi専用）を買いました。便利です！

* http://bit-trade-one.co.jp/product/module/adrsir/

# 全体像

Slackで命令すると、自宅のエアコンが稼働します。やったね！！！

![](https://qiita-image-store.s3.amazonaws.com/0/203323/632bfe67-688a-8c0b-a645-c5cbfccbaf06.png)

AWS IoTを使ってみたかったのです。
（自宅のRaspberry Piとクラウド（インターネット）をセキュアに接続したかった、というのもあります）


それでは、下記の順番で簡単に解説していきます。

1. IoT Core
2. API Gateway
3. Slack

# 環境など

## 家

### Raspberry Pi

* Raspberry Pi 3 Mobile B+
* Raspbian 9.4
* Python 3.6

### 家電

* エアコン
    * 本体：MSZ-GV225-W
    * リモコン：RH151

## AWS

* API Gateway
* IoT Core

# IoT Core

こちらを参考にIoT CoreとRaspberry Piの設定を行います。

* https://qiita.com/gnk263/items/a7937259746c81a6b052
    * 本記事を実現するにあたって、事前確認した際の記事です

具体的には下記となります。

* ポリシーの作成（IoT Core用）
* モノの作成
* 証明書の作成とダウンロード
* Raspberry PiにAWS IoTのSDKをインストール

# API Gateway

API GatewayとIoT Coreの連携は、下記を参考にさせていただきました。

* https://qiita.com/ma2shita/items/d558929c58d497a622e3

## ポリシー作成

ロールにアタッチするポリシーを作成します。ポリシー名は任意です。

* サービス： IoT
* アクション： 書き込み（Publish）
* リソース： すべてのリソース
* 名前： iot-publish-poricy

![](https://qiita-image-store.s3.amazonaws.com/0/203323/be5af9c6-242f-d4e2-5d78-501cd6dc0e3f.png)

![](https://qiita-image-store.s3.amazonaws.com/0/203323/5d612ff0-0300-37f4-31a5-3233b95131c8.png)

## ロール作成

API Gatewayで使用するロールを作成します。ロール名は任意です。

* ロール名： ApiGateway-HomeControl-Role

![](https://qiita-image-store.s3.amazonaws.com/0/203323/d9dc14e7-4df0-cdc2-c010-c17ca8250b37.png)

![](https://qiita-image-store.s3.amazonaws.com/0/203323/18cb0940-b91a-2ee2-cbbc-6f66f3cb1cf7.png)

さきほど作成したポリシーをアタッチします。

![](https://qiita-image-store.s3.amazonaws.com/0/203323/08d24d07-aa5d-e469-1d40-efcb6e5dcfdf.png)

なお、ここで作成したロールARNをコピーしておきます。

![](https://qiita-image-store.s3.amazonaws.com/0/203323/8ca5bda3-d0ea-fc7e-983c-db4a88c9a266.png)

## IoTエンドポイントの確認

IoT Coreの設定を選択し、エンドポイントをメモしておきます。

![](https://qiita-image-store.s3.amazonaws.com/0/203323/7799f5aa-e7fc-54b9-670f-306c95fc949e.png)

![](https://qiita-image-store.s3.amazonaws.com/0/203323/71bc71a2-ae4a-d0e0-9930-bb42037a9873.png)

## APIの作成

API GatewayのTOP画面にアクセスし、API作成を行います。API名は任意です。

* API名： HomeControl

![](https://qiita-image-store.s3.amazonaws.com/0/203323/39522a46-b056-7a3a-65aa-46f2294c8931.png)

### リソースの作成

作成したAPIに対するリソースを作成します。リソース名は任意です。

* リソース名： control

![](https://qiita-image-store.s3.amazonaws.com/0/203323/5d361879-cb57-f2a5-ef97-2bc1f3641fb9.png)

![](https://qiita-image-store.s3.amazonaws.com/0/203323/7fad93d6-b92f-7611-71cd-23f14abec072.png)

### メソッドの作成

POSTメソッドを作成します。

![](https://qiita-image-store.s3.amazonaws.com/0/203323/92cb928d-59eb-267a-1791-0d7204932535.png)

![](https://qiita-image-store.s3.amazonaws.com/0/203323/7fcfb49c-c18c-9301-8dc6-8fccc7025b0f.png)

POSTメソッドのセットアップを行います。AWSリージョンやサブドメインは必要に応じて読み替えてください。

* 統合タイプ： AWSサービス
* AWSリージョン： ap-northeast-1
* AWSサブドメイン： xxxxx(IoTエンドポイント先頭の固有部分)
* HTTPメソッド： POST
* アクションの種類： パス上書きの使用
* パス上書き： Publishしたいtopics（先頭のtopicsが必要）
* 実行ロール： 先ほど作成したロール

![](https://qiita-image-store.s3.amazonaws.com/0/203323/a363cb4a-0909-7ef4-53da-39212a873fe5.png)


## APIの動作確認

### IoT Coreのテスト準備

IoT Coreのテストを選択し、さきほど設定したトピックをsubscribeします。
なお、このときは、`topics/`を除いて入力してください。

![](https://qiita-image-store.s3.amazonaws.com/0/203323/d4972117-95ba-9dfe-30b4-b4e7ec1f5c92.png)

![Screenshot_2018-07-16 AWS IoT.png](https://qiita-image-store.s3.amazonaws.com/0/203323/f6bad99a-15b2-2b83-df36-4aa6ad033321.png)

### API Gatewayでテスト実行

API Gatewayでさきほど作成したPOSTメソッドを選択し、テストします。

![](https://qiita-image-store.s3.amazonaws.com/0/203323/dee53207-298b-80ad-eb57-d90be601ecf0.png)

リクエスト本文を適当に入力し、テストボタンを選択します。

```json
{
    "text": "this is test message!!!"
}
```

![](https://qiita-image-store.s3.amazonaws.com/0/203323/881bd176-de3e-caf2-6194-25877e99e86d.png)

### テスト結果

動作結果として、下記となれば成功です！

* API Gatewayのメソッドテスト
    * ステータス： 200
    * レスポンス本文： "message": "OK"
* IoT Coreのsubscribe
    * "text": "this is test message!!!"

![](https://qiita-image-store.s3.amazonaws.com/0/203323/d0c333e3-4861-4019-d46e-33852dea29f0.png)

![](https://qiita-image-store.s3.amazonaws.com/0/203323/cbda2749-5e7f-8a59-db9e-c5f04a7b88bf.png)

## APIのデプロイ

アクションからAPIのデプロイを選択します。

![](https://qiita-image-store.s3.amazonaws.com/0/203323/f0a698f4-6646-8f75-86da-1df840bf2d73.png)

新しいステージを作成してデプロイします。内容は適当です。

![](https://qiita-image-store.s3.amazonaws.com/0/203323/b86ab264-5a96-c36e-e94e-5355f3b6669e.png)

先ほど作成したControlのPOSTメソッドを選択し、URLをコピーしておきます。

![](https://qiita-image-store.s3.amazonaws.com/0/203323/0e5c1ee7-c0ca-bb24-dfbd-0968b0a386a6.png)

# Slack

Slash Commandsを使用します。アプリを検索して追加してください。

![](https://qiita-image-store.s3.amazonaws.com/0/203323/da2ef399-0d86-597e-ecaa-8d2ebe67c380.png)


コマンドは`/control`としておきます。お好みでどうぞ。

![](https://qiita-image-store.s3.amazonaws.com/0/203323/b9ead568-e4ea-3e5b-5425-e3b0b0bbf5c4.png)

設定画面のURLに、さきほどデプロイしたAPIのアドレスを貼り付けます。

![](https://qiita-image-store.s3.amazonaws.com/0/203323/382a5b39-2a92-bdd6-ad86-be15c57c31a8.png)

他の設定はお好みでどうぞ。最後に保存しましょう。

![](https://qiita-image-store.s3.amazonaws.com/0/203323/965d268c-53dd-26da-c83a-fb90412ff1bc.png)


## 動作確認

さきほどと同じようにIoT Coreの準備をしましょう。手順は省略します。
ブラウザを開きっぱなしの場合はそのままでOKです。

では、実際にslackで試しましょう！

![](https://qiita-image-store.s3.amazonaws.com/0/203323/317f5719-fbd5-6a99-52a1-619d24377e95.png)

### テスト結果

#### Slackのテスト結果

とりあえず応答が返ってきました。いい感じです。

![](https://qiita-image-store.s3.amazonaws.com/0/203323/a5d41544-a5ee-9c56-6d7b-c4e6fe1d9705.png)

#### IoT Coreのテスト結果

IoT Coreはこうなりました。

![Screenshot_2018-07-16 AWS IoT(2).png](https://qiita-image-store.s3.amazonaws.com/0/203323/f8a52717-bf6a-132d-c016-06f33787de38.png)

どうやらjson形式ではないようです。
API Gatewayの設定を頑張ればjson形式に変換できそうですが、今回はこのまま進めます。

# Raspberry Pi

では、最後にRaspberry Piの環境作成を行っていきます。

AWS IoT CoreとRaspberry Piの接続については、下記を参考にしてください。これ以降はできるものとして進めていきます。

* https://qiita.com/gnk263/items/a7937259746c81a6b052

## リモコンの学習

最初に紹介した通り、下記を使用します。

* http://bit-trade-one.co.jp/product/module/adrsir/

SW-No.1にエアコン電源ONを学習させます。
（学習方法は製品の説明書を参考にしてください）

* SW-No.1
    * GPIO No.4
    * PIN No.7

学習後、正常に動作するかを確認してください。

## ソースコード

プログラム終了時に`GPIO.cleanup()`を実行する必要があるため、実行ディレクトリに`finish.txt`を作成することで終了トリガーとしています。
（と、ここまで作っておいて、Slackでfinishパラメータを与えたらいいだけでは？と気づきました……）

IoT Coreとの接続に必要なパラメータは、外部設定ファイル（config.ini）から読み込むようにしています。

```python:sample_remote_control.py
import os
import configparser
import time
import json
import RPi.GPIO as GPIO
from AWSIoTPythonSDK.MQTTLib import AWSIoTMQTTClient

#Use PIN 7 (GPIO 4)
GPIO_AIRCON_PIN = 7

#programme finish trigger file
FINISH_FILE = "finish.txt"


def main():
    init_gpio()

    (root_ca, private_key, certificate,
        client_id, endpoint, port, topic) = parse_config_file()

    # https://s3.amazonaws.com/aws-iot-device-sdk-python-docs/sphinx/html/index.html
    client = AWSIoTMQTTClient(client_id)
    client.configureEndpoint(endpoint, port)
    client.configureCredentials(root_ca, private_key, certificate)

    client.configureAutoReconnectBackoffTime(1, 32, 20)
    client.configureOfflinePublishQueueing(-1)
    client.configureDrainingFrequency(2)
    client.configureConnectDisconnectTimeout(10)
    client.configureMQTTOperationTimeout(5)

    client.connect()
    client.subscribe(topic, 1, subscribe_callback)

    while True:
        time.sleep(5)

        if is_finish():
            os.remove(FINISH_FILE)
            GPIO.cleanup()
            print("Finish remote controller")
            break

def init_gpio():
    GPIO.setmode(GPIO.BOARD)
    GPIO.setup(GPIO_AIRCON_PIN, GPIO.OUT, initial=GPIO.HIGH)

def parse_config_file():
    config = configparser.ConfigParser()
    config.read("config.ini")

    return (
        config["AWS_IOT_CONNECT"]["ROOT_CA"],
        config["AWS_IOT_CONNECT"]["PRIVATE_KEY"],
        config["AWS_IOT_CONNECT"]["CERTIFICATE"],
        config["AWS_IOT_CORE"]["CLIENT_ID"],
        config["AWS_IOT_CORE"]["ENDPOINT"],
        int(config["AWS_IOT_CORE"]["PORT"]),
        config["AWS_IOT_CORE"]["TOPIC"]
    )

def subscribe_callback(client, userdata, message):
    print("Received a new message: ")
    print(message.payload)
    print("from topic: ")
    print(message.topic)

    params = parse_payload(message.payload.decode(encoding="utf-8"))
    print(json.dumps(params, indent=4))

    remote_control(params)

    print("--------------\n\n")

def parse_payload(payload):
    params = {}
    key_value_list = payload.split("&")
    for item in key_value_list:
        (key, value) = item.split("=")
        params[key] = value
    return params

def remote_control(params):
    if params["text"] == "aircon":
        print("Execute GPIO_AIRCON_PIN")
        execute(GPIO_AIRCON_PIN)

def execute(pin_no):
    GPIO.output(pin_no, True)
    time.sleep(0.5)
    GPIO.output(pin_no, False)
    time.sleep(0.5)

def is_finish():
    if os.path.isfile(FINISH_FILE):
        return True
    return False

if __name__ == "__main__":
    main()
```

```ini:config.ini
[AWS_IOT_CONNECT]
ROOT_CA = ./cert/root_ca.pem
PRIVATE_KEY = ./cert/private.pem.key
CERTIFICATE = ./cert/certificate.pem.crt.txt

[AWS_IOT_CORE]
CLIENT_ID = test_client_id
ENDPOINT = xxxxxxxnortheast-1.amazonaws.com
PORT = 8883
TOPIC = raspberry_pi/test
```

config.iniファイルの内容は、適宜変更してください。

ソースコードは、[GitHub](https://github.com/gnk263/RaspberryPi_App)にも置いています。

# 動作方法

それでは、実際に動作してみましょう！！！！

## 開始

Raspberry Piで下記コマンドを実行します。

ssh接続を解除してもバックグラウンド実行してほしいため、`nohup`を使用しています。

```sh
$ nohup sudo python sample_remote_control.py > output.txt &
```

## slackで制御！

```text
/control aircon
```

![](https://qiita-image-store.s3.amazonaws.com/0/203323/6053bc5c-9e15-5753-4693-05c97c6e66fb.png)

## エアコン

が起動する！！！

## 終了

Raspberry Piで下記コマンドを実行します。

```sh
$ touch finish.txt
```

# まとめ

エアコンの電気代を節約しつつ、仕事から解放されたらSlackでエアコンをONにすることで、帰宅時には快適です！！！

# 参考

* http://bit-trade-one.co.jp/product/module/adrsir/
* https://api.slack.com/slash-commands
* https://qiita.com/ma2shita/items/d558929c58d497a622e3
* https://qiita.com/gnk263/items/a7937259746c81a6b052