LINEを使用してSwitchBotのスマートロックを操作する方法

LINEアプリでメッセージを送信すればSwitchBotの操作ができる方法を書いていきます。
LINE様と呼んだ方がいいのかもしれない・・・

# 注意事項
今回は仕組みを知るために、無料のngrokを使用しています。
ngrokのセッションを再起動すると、新しいURLが生成されるので実用的な運用には向きません。
ngrokの場合は有料プランに申し込むと、固定のURLが使用できます。
他にも方法がありますが、今回は無料のngrokを使用します。

# 実行環境
- OS:Ubuntu 22.04.4 LTS
- Python:3.10.12
# 使用したツール
- SwitchBot API 
- LINE Messaging API
- ngrok
- Flask
# SwitchBot APIの設定
- APIトークンとクライアントシークレットを取得します。
- スマートロックのデバイスIDを取得します。
※手順はこちらを参照ください。
https://qiita.com/kawano-y/items/6f9f7aad18f71cedbf31
# LINE Developersの設定
- LINE Developers:`https://developers.line.biz/ja/services/messaging-api/`にログインして、Messaging APIを作成します。
- 作成したチャンネルの「Messaging API設定」タブから「チャンネルアクセストークン」を取得します。
- 「Webhook URL」は後から設定するので、ここでは何もしません。
# ngrokのインストール
ngrokをインストールして、ローカル環境を外部からアクセス可能にします。

- ngrokをダウンロードします。
`wget https://bin.equinox.io/c/4VmDzA7iaHb/ngrok-v3-stable-linux-amd64.tgz`
- ダウンロードしたファイルを解凍します。
`tar -xvzf ngrok-v3-stable-linux-amd64.tgz`
- ngrokの場所を移動します。
- `mv ngrok /usr/local/bin`
# Flaskとrequestsのインストール
- `pip install flask requests`
# Pythonスクリプトの作成
- ファイルを作成し、下記のコードを保存します。
```
import os
import json
import requests
import time
import uuid
import hmac
import hashlib
import base64
from flask import Flask, request

app = Flask(__name__)

LINE_ACCESS_TOKEN = os.environ['LINE_ACCESS_TOKEN']
SWITCHBOT_TOKEN = os.environ['SWITCHBOT_TOKEN']
SWITCHBOT_SECRET = os.environ['SWITCHBOT_SECRET']
DEVICE_ID = os.environ['SWITCHBOT_DEVICE_ID']
URL = 'https://api.switch-bot.com'

@app.route('/callback', methods=['POST'])
def callback():
    body = request.get_json()
    for event in body['events']:
        if event['type'] == 'message' and event['message']['type'] == 'text':
            reply_token = event['replyToken']
            user_message = event['message']['text']

            if "施錠" in user_message:
                switchbot_action("lock")
                reply_message(reply_token, "施錠完了")
            elif "開錠" in user_message:
                switchbot_action("unlock")
                reply_message(reply_token, "開錠完了")
            else:
                reply_message(reply_token, "「施錠」または「開錠」と入力")

    return 'OK'

def reply_message(reply_token, message):
    headers = {
        "Content-Type": "application/json",
        "Authorization": f"Bearer {LINE_ACCESS_TOKEN}"
    }

    data = {
        "replyToken": reply_token,
        "messages": [
            {
                "type": "text",
                "text": message
            }
        ]
    }

    requests.post('https://api.line.me/v2/bot/message/reply', headers=headers, data=json.dumps(data))

def switchbot_action(command):
    path = f'/v1.1/devices/{DEVICE_ID}/commands'
    nonce = str(uuid.uuid4())
    timestamp = str(int(time.time() * 1000))

    string_to_sign = SWITCHBOT_TOKEN + timestamp + nonce
    sign = base64.b64encode(
        hmac.new(SWITCHBOT_SECRET.encode('utf-8'), string_to_sign.encode('utf-8'), digestmod=hashlib.sha256).digest()
    )

    headers = {
        'Authorization': SWITCHBOT_TOKEN,
        'sign': sign.decode('utf-8'),
        't': timestamp,
        'nonce': nonce
    }
    
    body = {
        "command": command,
        "parameter": "default",
        "commandType": "command"
    }

    print(f"Headers: {headers}")
    print(f"Body: {body}")

    response = requests.post(URL + path, headers=headers, json=body)
    print(f'Status Code: {response.status_code}')
    print(f'Response Text: {response.text}')

    if response.status_code == 200:
        response_json = response.json()
        print(f'Response JSON: {response_json}')
        return response_json
    else:
        print(f"Error: {response.status_code}, Message: {response.text}")
        return {}

if __name__ == '__main__':
    app.run(port=5000)
```
# 環境変数の設定
ターミナルで必要な環境変数を設定します。
```
export LINE_ACCESS_TOKEN='〇〇〇〇〇'
export SWITCHBOT_TOKEN='〇〇〇〇〇'
export SWITCHBOT_SECRET='〇〇〇〇〇'
export SWITCHBOT_DEVICE_ID='〇〇〇〇〇'
```
- 〇〇〇〇〇を取得したものに編集してください。
- コマンド実行します。(Flaskサーバが起動します。)
# ngrokの起動
- 別のターミナルを開きます。
- `ngrok http 5000`を実行します。
- 生成されたURL(Forwardingの箇所)をコピーしておきます。
(例`:https://〇〇〇〇.ngrok-free.app)`
# LINE Developersの設定
- 「Messaging API設定」タブから「Webhook URL」を編集します。
- ngrokで生成されたURLに`/callback`を追加して更新をクリックします。
- 「Webhookの利用」を有効にします。
# LINEアプリでの操作
- LINE Developersで作成したチャンネルにて「Messaging API設定」タブに移動し、IDかQRコードからLINEの友達追加を行います。
- 追加されたチャンネルに「施錠」「開錠」とメッセージを送信するとスマートロックが操作されるかと思います。
(「施錠」「開錠」以外のメッセージを送信すると、必要なメッセージを教えてくれるようにもしています。)
![LINE.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3803710/d097ca91-0a33-b14e-8103-2c83ef6473c1.jpeg)
# 最後に
LINEでの操作が成功した時は嬉しすぎました。
嬉しすぎて、作成したチャンネルのIDやQRコードを知人(信頼できる人)に教えました。
その知人が連絡なしに家に来たときは、本当に驚きました・・・
(その後サーバは停止しました。)
セキュリティ意識は常日頃から高めておくということも身をもって学べて良かったです。