夏、留守番ネコのためにエアコンを自動で付ける

## はじめに
猫は寒さには強いが、暑さには弱いそうな。
いつも留守番させてごめんね。
SwitchBotで部屋の温度が分かるから、暑くなったらエアコン自動で付けるくらいはしてあげたい。
あとどうせならエアコン操作時はLINEで通知を受けたいし、LINE Notifyの使い方の記事も書いときたい。

## 環境
AWSのAmazon Linux2023を使う。(私の記事は全部いっしょ！)

つまり配置は以下(図を書くほどの記事じゃない)
プログラム・・AWS
通知・・スマホのLINE
家・・SwitchBot製品の温湿度計とシーリングライト(ミニHUB代わり)


## LINE作業
まずは普通にスマホを使ってグループを作る
<img width="400" alt="image.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3625818/9a0025ee-1666-9005-82e6-d605bdde9c30.png">

バッチリ決めた写真をアイコンに設定
<img width="400" alt="image.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3625818/821827e9-20d6-02a8-6aa4-2ab58be74109.png">

LINE Notifyをグループに参加させる (LINE Notifyは友だち登録しておく)
<img width="400" alt="image.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3625818/7c6dd4a9-e10b-8343-9db4-276658f1de36.png">

ちなみに、グループに参加させない状態で後述のプログラム使ってメッセージ送ろうとすると、こんなメッセージが来るのでLINE Notifyはちゃんと招待してあげる
<img width="400" alt="image.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3625818/4a483124-42e3-099f-3370-f50c8e3a4aa6.png">


## パソコン作業

ブラウザでLINE Notify (https://notify-bot.line.me/ja/) にログインして、
マイページをクリック
<img width="800" alt="image.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3625818/4a3d7d99-cff4-1d0a-8102-b3c659db1c36.png">

アクセストークンの発行をクリック
<img width="400" alt="image.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3625818/ba4e91bb-f56e-33ba-1f15-1b72f8faf0d2.png">

「通知を送信するトークルーム」にさっき作ったグループを指定する。
トークン名はお好みで決める。ここでは「！」にした。(トークン名は後から変更できないっぽい！)
<img width="400" alt="image.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3625818/b1abdd95-a74c-1720-4cd0-69bd1c3bee53.png">

発行するボタンを押したら、次のようにトークンが表示されるのでメモする。
<img width="400" alt="image.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3625818/07b3fe3f-51fa-291c-1f74-756aa29476cf.png">

## LINE Notifyを利用するプログラム
まずは単独で動かしてみて、通知が来るか確認しよう

```python:line_notify.py
import requests

YOUR_TOKEN = 'あなたのトークン'

# LINEにメッセージ送信
def send_message(text_message):

    url  = 'https://notify-api.line.me/api/notify'
    headers = {'Authorization' : 'Bearer ' + YOUR_TOKEN}
    payload = {'message' : text_message }

    response = requests.post( url, headers = headers, params=payload)
    if response.status_code != 200:
        print('失敗！', response.status_code)

def main():

    send_message('test')

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

## SwitchBotを利用するプログラム

SwitchBotの使い方は前の記事に書いた

https://qiita.com/dmackey_2128/items/bdfe42a5a64994462871

今回はエアコンをON/OFFする関数を作る。
詳しいことは公式リファレンスを読む。
https://github.com/OpenWonderLabs/SwitchBotAPI

```python:switchbot.py
import json
import time
import hashlib
import hmac
import base64
import uuid

import requests

TOKEN  = 'あなたのキー'
SECRET = 'あなたのキー'

TEMP_DEVICE_ID = '温湿度計のデバイスID'
EAKON_DEVICE_ID = 'エアコンのデバイスID'

# githubのリファレンスより、How to Sign
def create_header():

    # Declare empty header dictionary
    apiHeader = {}
    nonce = uuid.uuid4()
    t = int(round(time.time() * 1000))
    string_to_sign = '{}{}{}'.format(TOKEN, t, nonce)

    string_to_sign = bytes(string_to_sign, 'utf-8')
    secret = bytes(SECRET, 'utf-8')

    sign = base64.b64encode(hmac.new(secret, msg=string_to_sign, digestmod=hashlib.sha256).digest())

    #Build api header JSON
    apiHeader['Authorization']=TOKEN
    apiHeader['Content-Type']='application/json'
    apiHeader['charset']='utf8'
    apiHeader['t']=str(t)
    apiHeader['sign']=str(sign, 'utf-8')
    apiHeader['nonce']=str(nonce)

    return  apiHeader

# デバイス一覧取得
def deviceid_view():
    # ヘッダ作成
    header =create_header()
    response = requests.get("https://api.switch-bot.com/v1.1/devices", headers=header)
    print(response.text.replace('}', '}\n'))

# 温湿度計の情報取得
def get_temp_and_humidity():

    # ヘッダ作成
    header = create_header()

    url = 'https://api.switch-bot.com/v1.1/devices/' + TEMP_DEVICE_ID + '/status'
    res = requests.get( url, headers=header )
    return res.json()

# 寝室のエアコン
def shinshitu_eakon(temperature = 25, power='on'):

    # ヘッダ作成
    header = create_header()

    # he unit of temperature is in celsius;
    # modes include 0/1 (auto), 2 (cool), 3 (dry), 4 (fan), 5 (heat);
    # fan speed includes 1 (auto), 2 (low), 3 (medium), 4 (high);
    # power state includes on and off
    # 設定温度、冷房、ファン速度：中、on or off で要求
    body = {
        "command" : "setAll",
        "parameter" : f"{temperature},2,3,{power}",  # 温度,モード,ファン速度,電源状態
        "comandType": "command"                      # モード 1:自動 2:冷房 3:乾燥 4:ファン 5:加熱
    }                                                # ファン速度 1:自動 2:低 3:中 4:高

    # 寝室のエアコン操作！
    url = 'https://api.switch-bot.com/v1.1/devices/' + EAKON_DEVICE_ID + '/commands'
    requests.post( url, headers=header, data=json.dumps(body) )

def main():
    # デバイス一覧取得
    deviceid_view()

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

## メイン関数
SwitchBotとLINE Notifyを組み合わせたメインプログラムを書く。
ざっくり仕様は
・温度が28℃以上になったらエアコンを冷房 26℃ でON
・温度が24℃以下になったらエアコンをOFF
・エアコン操作時にLINE通知
・ついでに温度と湿度が異常な場合もLINE通知
・一応、夏(5～8月)っぽくて日中(10～18時)と限定させておく。誤検知されても嫌なので、明らかに不要な時はなるべく排除しておきたいという心

```python:main.py
from datetime import datetime
import line_notify
import switchbot

# 温度のLINE通知判定閾値
TEMP_MAX = 30
TEMP_MIN = 18

# 湿度のLINE通知判定閾値
HUMIDITY_MAX = 70
HUMIDITY_MIN = 35

# エアコンON/OFF 閾値
EAKON_ON  = 28
EAKON_OFF = 24

# 設定温度をON閾値の何℃マイナスにするか
SET_TEMP  = 2

# 室温と湿度をチェックし、異常ならLINEにて通知
def check_temp():

    # 温湿度計情報の取得
    meterplus = switchbot.get_temp_and_humidity()
    temp = meterplus['body']['temperature']
    humidity = meterplus['body']['humidity']

    print( '温度=', temp, '湿度=', humidity )

    # 温度/湿度に異常があればライン送信
    if temp >= TEMP_MAX or\
       temp <= TEMP_MIN or \
       humidity >= HUMIDITY_MAX or \
       humidity <= HUMIDITY_MIN:

        msg = '  温度/湿度が異常\n\n温度 ' + str(temp) + '℃\n湿度 ' + str(humidity) + '%\n\n'
        msg = msg + '次の条件でお知らせします\n'
        msg = msg + '温度 {:}℃以上 or {:}℃以下\n'.format( TEMP_MAX, TEMP_MIN )
        msg = msg + '湿度 {:}％以上 or {:}％以下'.format( HUMIDITY_MAX, HUMIDITY_MIN )
        line_notify.send_message(msg)
    # 温度を返す
    return temp

# 夏の暑い時に、寝室のエアコンをつける
def summer_eakon(temp):

    # 現在時刻取得
    dt_now = datetime.now()

    # 夏(5～8月)の日中
    if (dt_now.month >= 5  and dt_now.month <= 8) and \
       (dt_now.hour  >= 10 and dt_now.hour  <= 18):

        # 暑そうならエアコンON
        if temp >= EAKON_ON:
            switchbot.shinshitu_eakon(temperature = EAKON_ON - SET_TEMP, power = 'on')
            msg = f'暑そうなのでエアコンONしました！\n' \
                  f'条件　　 : {EAKON_ON}℃ 以上\n' \
                  f'設定温度 : {EAKON_ON - SET_TEMP}℃'
            line_notify.send_message(msg)

        # 涼しくなったらエアコンOFF
        elif temp <= EAKON_OFF:
            switchbot.shinshitu_eakon(power = 'off')
            msg = f'涼しそうなのでエアコンOFFしました！\n' \
                  f'条件 : {EAKON_OFF}℃ 以下'
            line_notify.send_message(msg)

def main():

    # 室温と湿度をチェックし、異常ならLINEにて通知
    temp = check_temp()

    # 夏の暑い時に、寝室のエアコンをつける
    summer_eakon(temp)

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

## cronに登録

これをcronに登録しておく。
「crontab -l」コマンド結果は以下。1時間毎に起動するようにしている。
```
MAILTO=""
# 1時間毎
0 9-18 * * * cd /home/ec2-user/project/switchbot; python3 main.py
```

## 実際のLINE画面
ちゃんと通知された。
<img width="400" alt="image.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3625818/818b99de-2bea-df1d-a321-b7e1892b8969.png">

ちなみに最初に書いた記事と同じLINEグループにしてるので、ネコが動いても通知がくる。

https://qiita.com/dmackey_2128/items/7a7e01ea47e745913fcb

## 実はこんなプログラム書かなくても実現できる
「Switchbot エアコン 自動」 でググると分かるけどSwitchBotのスマホアプリだけで実現できるみたい！(笑)
なので普通はプログラム書かない気もする

## おわりに
仕組み自体は去年に作ってたけど、自分の中でまとめるためにも記事にした。
１度やったはずなのに戸惑うところもあって、やっぱりちゃんと残しておくのって大事だな～と実感