【SwitchBot】湿度に応じて自動で除湿機を起動する

## はじめに
今年は梅雨入りの時期が例年より早く、ジメジメとした季節が続いていますね。そこで、室内の湿度を監視し、湿度が高くなったら自動で除湿機を起動し、室内の湿度を下げるような仕組みを構築しました（元々構築してあったのですが、記事にするのが遅くなりました）。流れとしては、Raspberry Pi で湿度を取得し、湿度が高くなったら、 SwitchBotAPI を利用して除湿機を起動し、湿度が低くなったら除湿機を停止します。

## 準備したもの
* [Raspberry Pi 4 Model B 4GB](https://amzn.to/3xzQ1aO)
* [DHT22 温湿度センサーモジュール](https://amzn.to/42Njjjk)
* [SwitchBot 温湿度計](https://amzn.to/3Ne3iNL)
* [SwitchBot スマートリモコン ハブミニ](https://amzn.to/3Ei6Pab)
* [SwitchBot スイッチボット スイッチ](https://amzn.to/3EgoTkQ)
* [アイリスオーヤマ 除湿機](https://amzn.to/3Phfcck)

※温湿度計はどちらか一つで大丈夫です。値段的にもほとんど変わらないので（ハブミニは必要になりますが…）温湿度計のほうが楽ちんでおすすめです（デジタル表記もしてくれるので分かりやすいですね）。
## 環境
* Raspberry Pi 4 Model B
    * CentOS Stream 8
        * Python 3.11.0
            * requests 2.28.2
            * Adafruit-DHT 1.4.0
        
## 導入

### Raspberry Pi のセットアップ
Raspberry Pi のセットアップについてはまとめてみたので参考にしてみて下さい。

https://qiita.com/Pumila/items/4f73263025ffbc427e90

### モジュールのインストール
まずは、Pythonに必要なライブラリをインストールしていきましょう。こちらは DHT22 温湿度センサーモジュールを使う場合に必要なライブラリになります。温湿度計は必要ありません。
```console
pip install Adafruit-DHT
```

### SwitchBot の設定
SwichBot アプリ（[iOS](https://apps.apple.com/us/app/switchbot/id1087374760), [Android](https://play.google.com/store/apps/details?id=com.theswitchbot.switchbot&hl=en)）から SwitchBot スイッチデバイスの登録をして下さい。[近くのボット] から登録ができるはずです（詳細は割愛します）。この時、名前を変更しておくと後々便利です。次にスイッチデバイスを選択し（ここでは CO2ON を選択していますが、除湿機を選択して下さい）、設定からクラウドサービスの有効化を行って下さい。
<img width="200" alt="IMG_0053.jpg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1115291/21c740e3-8f1d-85c6-0a44-7e1b5ffed598.jpeg"><img width="200" alt="IMG_0054.jpg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1115291/a9172637-86a8-2c59-e92d-f6c407765c16.jpeg">

#### 開発者トークンの取得
SwitchBot デバイスにアクセスするために必要な開発者トークンの取得を行います。SwitchBot アプリの [プロフィール] より [設定] を開きます。[アプリバージョン] の項目を10回連続でタップすると、[開発者向けオプション] が表示されます。

<img width="200" alt="IMG_0055.jpg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1115291/2f219f4b-fce3-5487-f98b-028234d8e2ca.jpeg"><img width="200" alt="IMG_0056.jpg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1115291/04ae617a-1f16-2beb-9c6f-1d79e273d80a.jpeg"><img width="200" alt="IMG_0057.jpg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1115291/7a56dec1-7a15-c7be-21e9-1457f79c0832.jpeg">

[開発者向けオプション] を開き [トークンを取得] をタップします。すると、トークンが表示されるのでコピーし PC に送って下さい。

<img width="200" alt="IMG_0058.jpg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1115291/fd4cf50e-b2c3-c83b-9a7d-520b280428e8.jpeg"><img width="200" alt="IMG_0059.jpg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1115291/24381e1e-5dbf-5c2d-6e98-ab500b45e754.jpeg">

#### デバイス ID の取得
続いて、SwitchBot デバイスのデバイス ID を取得していきます。以下のスクリプトの "YourAccessToken" 部分を先程取得したトークンに置き換えて実行して下さい。いちいちコマンドを打つのは面倒なのでスクリプトとして保存しておいたほうが後々楽です。
```bash:getdeviceid.sh
#!/bin/sh

TOKEN="YourAccessToken"

/usr/bin/curl -s -X GET "https://api.switch-bot.com/v1.0/devices" -H "Authorization:$TOKEN"| sed 's/{\"deviceId\"/\n{\"deviceId\"/g' 
```

実行すると以下のようにデバイス ID が表示されます。
```console:Result
{"statusCode":100,"body":{"deviceList":[
{"deviceId":"C02CF2","deviceName":"CO2OFF","deviceType":"Bot","enableCloudService":true,"hubDeviceId":"F144D9"},
{"deviceId":"C27E36","deviceName":"温湿度計 07","deviceType":"Meter","enableCloudService":true,"hubDeviceId":"F144D9"},
{"deviceId":"C7E384","deviceName":"カーテン D3","deviceType":"Curtain","hubDeviceId":"000000","curtainDevicesIds":["E76E3C","C7E384"],"calibrate":true,"group":true,"master":false,"openDirection":"right"},
{"deviceId":"C9DFFF","deviceName":"CO2ON","deviceType":"Bot","enableCloudService":true,"hubDeviceId":"F144D9"},
{"deviceId":"D371D0","deviceName":"除湿機","deviceType":"Bot","enableCloudService":true,"hubDeviceId":"F144D9"},
{"deviceId":"E76E3C","deviceName":"カーテン CC","deviceType":"Curtain","enableCloudService":true,"hubDeviceId":"F144D9","curtainDevicesIds":["E76E3C","C7E384"],"calibrate":true,"group":true,"master":true,"openDirection":"left"},
{"deviceId":"EE93FD","deviceName":"リモートボタン 6A","deviceType":"Remote","enableCloudService":false,"hubDeviceId":"000000"},
{"deviceId":"F144D9","deviceName":"ハブミニ 7A","deviceType":"Hub Mini","hubDeviceId":"000000"}],"infraredRemoteList":[]},"message":"success"}
```
今回欲しいデバイス ID は"除湿機"の "D371D0" と"温湿度計"の "C27E36" になります。メモしておきましょう。これで、SwichBot デバイスを API で制御する準備ができました（温湿度計を使わずに DHT22 温湿度センサーモジュールを使う場合は温湿度計のデバイス ID は必要ありません）。

#### SwitchBot デバイスの操作
続いて、SwitchBotAPI を実行します。ソースコードは以下の通りです。

```python:switchbot.py
#!/usr/bin/env python3
import requests, json
# 取得したトークンに置き換えて下さい。
ACCESS_TOKEN="YourAccessToken"
API_BASE_URL="https://api.switch-bot.com"

# Switch ON/OFF
def device_control3():
# 取得したデバイス ID に置き換えて下さい。
    DEVICEID="D371D0"
    headers = {
        # ヘッダー
        'Content-Type': 'application/json; charset: utf8',
        'Authorization': ACCESS_TOKEN
    }
    url = API_BASE_URL + "/v1.0/devices/" + DEVICEID + "/commands"
    body = {
        # 操作内容
        "command":"turnOn",
        "parameter":"default",
        "commandType":"command"
    }
    ddd = json.dumps(body)
    res = requests.post(url, data=ddd, headers=headers)
    return res

# DHT22 温湿度センサーモジュール を使う場合は必要ありません。
def device_control4() -> dict:
# 取得したデバイス ID に置き換えて下さい。
    DEVICEID="C27E36"
    headers = {
        # ヘッダー
        'Content-Type': 'application/json; charset: utf8',
        'Authorization': ACCESS_TOKEN
    }
    url = API_BASE_URL + "/v1.0/devices/" + DEVICEID + "/status"
    res = requests.get(url, headers=headers)
    #print(res.text)
    return res.json()["body"]

if __name__ == '__main__':
    # 処理呼び出し
    #device_control3()
    #device_control4()
```
デバイスごとに設定するパラメータ（command, parameter, commandType）については、
SwitchBotAPI の [README](https://github.com/OpenWonderLabs/SwitchBotAPI#command-set-for-physical-devices) に記載されています。参考にしてみて下さい。実行してスイッチデバイスがきちんと動けば問題ありません。

### 湿度の測定

#### SwitchBot 温湿度計の場合
先程作成した `switchbot.py` と同じディレクトリに以下のソースコードを保存して下さい。
```python:chkhumidity.py
#!/usr/bin/env python3
import time
import switchbot

triggered = False
while True:
    time.sleep(5)
    res=switchbot.device_control4()
    t=res.get("temperature")
    h=res.get("humidity")
    print( "Temp= {0:0.1f} \u2103".format(t))
    print( "Humidity= {0:0.1f} %".format(h))
```
実行した結果、正しく湿度が表示されていれば問題ありません（ここでは同時に温度も取得しています）。

```console
[web@localhost humidity]$ ./chkhumidity2.py 
Temp= 28.1 ℃
Humidity= 52.0 %
```

#### DHT22 温湿度センサーモジュールの場合
続いて、DHT22 温湿度センサーモジュール（以下、センサー）を使う場合の説明をしていきます。このセンサーには以下の画像のようにデータ（DAT） のやり取りをするピンと電源用のピン（VCC）、グラウンド（グランド）用のピン（GND）の3種類があります。これを正しく Raspberry Pi の GPIO に挿し込まなければ機能しません。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1115291/02c73338-674f-6c5e-8c06-16bdced485c5.jpeg" width=300>
GPIO の概要について軽く説明しておきます。GPIO を構成するピンは、主に4つの機能に分類できます。
* 電源ピン：電源を供給するピン。3.3V と 5V があります
* GNDピン：グランドピン。電位の基準を決めている回路のこと
* DNC：ユーザが使用できないピン。古いタイプの Raspberry Pi に存在しましたが、現在では I/Oとして使用できるように改良されています
* I/O：汎用入出力ピン


Raspberry Pi 4 の GPIO ピン配置（ピンマッピング）については、以下記事にまとめてあるので参考にして下さい。

https://qiita.com/Pumila/items/f362a3d68e57cd088713

![画像1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1115291/2fce2ef3-9d43-13a9-9de4-d33ad296ba90.png)

デバイスによっては、代替機能が利用されている場合があり、競合する可能性があります。そのため、代替機能がある GPIO ピンの利用は避けた方がいいかもしれません。また、GPIO ピンによっては接触不良なのかきちんとセンサーのデータが読み込めないことがあります。その時は GPIO ピンを差し替えたりしてみて下さい。私の場合、GPIO 22ピンを利用しようとしたのですが、データが読み込めなかったので GPIO 27ピンを利用しています。電源は 3.3V, 5V どちらでも構いません。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1115291/6275df63-e0b6-dd4e-706c-2732b5f1a592.jpeg" width=300><img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1115291/02c73338-674f-6c5e-8c06-16bdced485c5.jpeg" width=300>

動作確認のために以下のソースコードを保存し、root 権限で実行して下さい。なぜなら GPIO ピンへのアクセスには通常 root 権限が必要になるからです。ソースコード中のピン番号は、接続したピン番号に置き換えて下さい。
```python:chkhumidity.py
#!/usr/bin/env python3
import Adafruit_DHT as DHT
import time

sensor = DHT.DHT22
# 接続したピン番号に置き換えて下さい。
pin = 27
triggered = False
while True:
    time.sleep(5)
    h,t = DHT.read_retry(sensor, pin)
    print( "Temp= {0:0.1f} \u2103".format(t))
    print( "Humidity= {0:0.1f} %".format(h))
```
実行した結果、正しく湿度が表示されていれば問題ありません（ここでは同時に温度も取得しています）。

```console
[root@localhost humidity]# ./chkhumidity.py 
Temp= 29.6 ℃
Humidity= 58.8 %
```

### 本実装
スイッチデバイスが動くことが確認でき、湿度も取得することがでたので、続いて室内の湿度に応じて自動で除湿機を起動するコードの実装に移ります。SwitchBot 温湿度計の場合と DHT22 温湿度センサーモジュールの場合がありますが、それぞれ湿度の校正が異なっており、同じ数値を出してはくれません。なので、湿度のしきい値に関しては実際に運用してみて、決めて下さい。以下、ソースコードになります。
#### SwitchBot 温湿度計の場合
```python:humidity.py
#!/usr/bin/env python3
import time
import switchbot

triggered = False
while True:
    res=switchbot.device_control4()
    humidity=res.get("humidity")
    #print(humidity)
    if humidity >= 62 and not triggered:
      res = switchbot.device_control3()
      triggered = True
    if humidity <= 55 and triggered:
      res = switchbot.device_control3()
      triggered = False
    time.sleep(120)
```

#### DHT22 温湿度センサーモジュールの場合
```python:humidity.py
#!/usr/bin/env python3
import Adafruit_DHT as DHT
import time
import switchbot

sensor = DHT.DHT22
pin = 27
triggered = False
while True:
    data = DHT.read_retry(sensor, pin)
    humidity = data[0]
    temp = data[1]  
    print(humidity,temp)
    if humidity >= 62 and not triggered:
      res = switchbot.device_control3()
      triggered = True
    if humidity <= 55 and triggered:
      res = switchbot.device_control3()
      triggered = False
    time.sleep(120)
```

### service への登録
常時稼働させておくために、サービスとして登録しておきましょう。登録するためには root 権限が必要になります。まずは、以下ディレクトリに移動してください。
```console
[root@localhost ~]# cd /etc/systemd/system/
```
その後、以下のコードを `humidity.service` として保存して下さい。
```bash:humidity.service
[Unit]
Description=humidity

[Install]
WantedBy=multi-user.target

[Service]
User=root
# 実行するコードのディレクトリに置き換えて下さい
ExecStart=/root/humidity/humidity.py
Restart=always
```
最後にサーバ起動時にサービスも自動起動してくれるように設定します。
```console:サービスの自動起動
[root@localhost system]# systemctl start humidity.service
[root@localhost system]# systemctl enable humidity.service 
Created symlink /etc/systemd/system/multi-user.target.wants/humidity.service → /etc/systemd/system/humidity.service.
```
以下コマンドを実行して `Active: active (running)` と表示されていれば問題ありません。
```console:サービスの状態
[root@localhost system]# systemctl status humidity
● humidity.service - humidity
   Loaded: loaded (/etc/systemd/system/humidity.service; enabled; vendor preset: disabled)
   Active: active (running) since Fri 2018-06-22 20:12:09 JST; 4 years 11 months ago
 Main PID: 1072 (python3)
    Tasks: 1 (limit: 23882)
   CGroup: /system.slice/humidity.service
           └─1072 python3 /root/humidity/humidity2.py

Jun 22 20:12:09 localhost.localdomain systemd[1]: humidity.service: Service RestartSec=100ms expired, s>
Jun 22 20:12:09 localhost.localdomain systemd[1]: humidity.service: Scheduled restart job, restart coun>
Jun 22 20:12:09 localhost.localdomain systemd[1]: Stopped humidity.
Jun 22 20:12:09 localhost.localdomain systemd[1]: Started humidity.
```

## 最後に
冷房ではなく、ドライ（除湿）を使えばいいのではとも思うのですが、なんとなくドライ使うのが嫌なんですよね（なんででしょう…）。そのため、湿気が気になるので除湿機を購入し今回の自動で除湿機を起動する機構を構築しました。いかがったでしょうか。もう梅雨入りしているため、記事の投稿（2023/06/18）が遅くなってしまいましたが、皆さんの生活が快適になればと思います。