LineからLambda経由でSesameの鍵を開け閉めするまで


やりたいこと

Sesameを買って、スマートホーム化を進めているところ。
どうしてもSesameの公式アプリのレスポンスが遅い。
「開けて」って言ったら、すぐ開けてほしい。

※Sesameとは
自宅の鍵に取り付けるだけで、スマホから開け閉めできる!というやつ。
https://jp.candyhouse.co

できたこと

Lineの個人チャンネルにて「鍵あけて」「鍵しめて」というと、Sesameがスッと起動する。

構成

Line→AWS API Gateway→Lambda→Sesame

Lambda上はPythonで書いてます。
コードはPycharmで書いて、そのままデプロイまでしちゃいます。簡単。

ローカル環境はMacOSです。

1.SesameのAPIを確認する。

まず公式ページの「ダッシュボード(BETA)」からログイン。
「API設定」から認証コードを入手します。

入手した認証コードを使いながら、とりあえずcurlで叩いてみます。
API仕様の詳細は、公式見てください。
https://docs.candyhouse.co/#get-sesame-list

こう打つと
curl -H "Authorization: **************" \
https://api.candyhouse.co/public/sesames
こう返ってくる
{
     "device_id": "*******************",
     "serial": "********",
     "nickname": "************"
}

応答はすべてJSON形式。
上の"device_id"をもとに、ロック・ロック解除・ステータス確認のAPIを使います。

2.LambdaからAPIを叩く

pythonで書くことにしているので、line-bot-sdkを使いました。

line-bot-sdkの使い方はこちら。
https://github.com/line/line-bot-sdk-python

1.でCURLしていたのは、requestsで実現します。
CURLをpython形式にしてくれるサイトが合ったので、使わせて頂きました。
https://curl.trillworks.com

pythonのソースは以下のとおり。
鍵のOPEN/CLOSEの指示のあと、10秒Sleepしてから、鍵の状態を返却します。
ちなみにAWS Lambdaのタイムアウト値は初期値が3秒なので、広げないと永遠に成立しません。
(実は結構ハマった)

app.py
import os, sys
import json
import time
import requests

from linebot import (
    LineBotApi, WebhookHandler
)
from linebot.models import (
    MessageEvent, TextMessage, TextSendMessage,
)
from linebot.exceptions import (
    LineBotApiError, InvalidSignatureError
)

channel_secret = os.getenv('LINE_CHANNEL_SECRET', None)
channel_access_token = os.getenv('LINE_CHANNEL_ACCESS_TOKEN', None)
if channel_secret is None:
    print('Specify LINE_CHANNEL_SECRET as environment variable.')
    sys.exit(1)
if channel_access_token is None:
    print('Specify LINE_CHANNEL_ACCESS_TOKEN as environment variable.')
    sys.exit(1)

line_bot_api = LineBotApi(channel_access_token)
handler = WebhookHandler(channel_secret)


def lambda_handler(event, context):
    signature = event["headers"]["X-Line-Signature"]
    body = event["body"]
    ok_json = {"isBase64Encoded": False,
               "statusCode": 200,
               "headers": {},
               "body": ""}
    error_json = {"isBase64Encoded": False,
                  "statusCode": 403,
                  "headers": {},
                  "body": "Error"}

    def controll_sesame(controll):
        # ロック解除
        controll_headers = {
            'Authorization': '**************************',
            'Content-Type': 'application/json',
        }
        if controll == "OPEN":
            controll_data = '{"command":"unlock"}'
        elif controll == "CLOSE":
            controll_data = '{"command":"lock"}'
        else:
            pass

        controll_response = requests.post(
            'https://api.candyhouse.co/public/sesame/*************************',
            headers=controll_headers, data=controll_data)

        time.sleep(10)

        # 状態確認
        confirm_headers = {
            'Authorization': '*************************',
        }
        confirm_response = requests.get('https://api.candyhouse.co/public/sesame/*************************',
                                        headers=confirm_headers)
        response_data = confirm_response.json()
        locked = response_data['locked']

        # 結果返却
        if locked:
            message_text = "閉まってるよ"
        else:
            message_text = "空いてるよ"

        return message_text

    @handler.add(MessageEvent, message=TextMessage)
    def message(line_event):
        text = line_event.message.text
        if text == "鍵あけて":
            message_text = controll_sesame("OPEN")
        elif text == "鍵しめて":
            message_text = controll_sesame("CLOSE")
        else:
            message_text = text

        line_bot_api.reply_message(line_event.reply_token, TextSendMessage(text=message_text))

    try:
        handler.handle(body, signature)
    except LineBotApiError:
        return error_json
    except InvalidSignatureError:
        return error_json

    return ok_json

3.API Gatewayを設定する

AWSコンソールからAPI GatewayをLambdaに括り付ける。
あまり詳しく学びきれてないので、公式見ながらチクチク作る。

最近?HTTP APIができたようですが、REST APIを選択します。
できたら「ダッシュボード」に書かれているURLを控えておきます。

4.LineからAPI Gatewayに接続する。

まずはDeveloperとして登録。
https://developers.line.biz/ja/

Messaging APIとしてチャンネルを作成します。
「Webhook URL」に3.で控えたAPI GatewayのURLを指定します。

またLineチャンネルに設定されているチャネルアクセストークンと
チャネルシークレットは、Lambdaの環境変数に登録しておきます。

5.ハマりどころ

pythonのコードはPycharmで書いていました。
PycharmにはAWS Toolkitというプラグインがあり、
これを導入することでローカルからLambdaへのデプロイが可能になります。

ただこれをやる際、IAMロールに権限をつけておく必要があるのに気付かず
手探りで権限を広げて、とりあえずデプロイが成功。
結果として、Lambda、IAM、S3、APIGateway、CloudFormationの権限を付与しました。

不足はないですが、過剰はあるかもしれません。
勉強不足。。