とある休日に、何を思ったか
- SORACOMのボタン(俗に言う「あのボタン」)が発売されるねんなぁ
- 何作ろうかなぁ
- そりゃ作るのはできるけどさ、せっかく作るなら使うもんがいいなぁ
そんな時、どこからともなくお母さんからLINEが
息子の悩み
実家の大阪、自分の住む関東。
元気やけど、それを言うのはなんか気恥ずかしい。
でもお母さんから来る「元気か?」「元気やろ?」はちょっと嬉しい。
お母さんに心配はかけたくない。
そうだ、これだ
いつでもどこでもお母さんに「元気やで」って言えればメッチャ良くね?
…お母さんの誕生日に贈ることにしました。あのボタン。
どんなものを作ろうと思ったか
- ボタンを押すと、LINEに通知が届く
- ボタンの役割によって届くメッセージが異なる
- どうせだったら汎用的に使えるものが欲しい
- 名付けて
仕組み
こんな感じです。
1. AWS IoT 1-Clickで、ボタンにLambdaの関数を割り当て
2. ボタンの属性に、それぞれのクリックに対応するメッセージを仕込む
3. ボタンの押された種別に応じて、LambdaからLINE Notifyにメッセージを発行
AWS IoT 1-Clickの設定について
SORACOMのMaxさんが分かりやすく書いてくれてます。
SORACOM LTE-M Button powered by AWS を用いた開発 TIPS - SORACOM Blog
これを基にして、今回のプロジェクトで設定した内容を図解したので参考になれば。
Lambda側の設定
の前に、気をつけたいこと
-
Lambdaのプロジェクトを作るときに、Python3.6を使う
(いや別になんでもいいけど笑) -
LambdaのPythonにはデフォルトでrequestsモジュールが入っていない
- 一旦ローカルにpipでモジュールをダウンロード
- そこに下記のlambda_function.pyと一緒にzipで固めてアップロードする必要がある
- 参考:【AWS】Lambdaでpipしたいと思ったときにすべきこと - Qiita
-
zipでアップロードしたらさっきまでLambdaのWebで書いてたコードは上書きされるぞ
- これで泣いたぞ、1回
コードで工夫した点
- 例外処理ちゃんと書いて、CloudWatchで出力されるログに、エラーが出るようにした
- LINE Notifyのトークンが設定されていれば、そのログをLINE側に通知するようにした
- LINE Notifyのトークンをコード内に設定できるようにした(環境変数にしたら良かった…)
実際のコード
messageのところは、適宜書き換えて好きなフォーマットで出力してください。
# -*- coding:utf-8 -*-
import json
import requests
import datetime as dt
# LINE NotifyのURLとデフォルトの通知先トークン(これよりもボタン側に設定したトークンが優先される)
LINE_NOTIFY_URL = "https://notify-api.line.me/api/notify"
LINE_NOTIFY_TOKEN = ""
# UTCとの時差(時間)
UTC = 9
def lambda_handler(event, context):
global LINE_NOTIFY_TOKEN
print("Received event: " + json.dumps(event))
# デバイス情報(デバッグ用)
deviceInfo = event['deviceInfo']
deviceId = deviceInfo['deviceId']
deviceAttributes = deviceInfo['attributes']
# プロジェクト名、プレイスメント名(デバッグ用)
projectName = deviceAttributes['projectName']
placementName = deviceAttributes['placementName']
# クリック種別とクリック時間の取得
buttonClicked = event['deviceEvent']['buttonClicked']
clickType = buttonClicked['clickType']
reportedTime = buttonClicked['reportedTime']
# AWSデフォルト表記:2018-11-09T12:37:10.614Z
input_datetime_format = '%Y-%m-%dT%H:%M:%S.%fZ'
# 出力表記:2018/11/09 12:37:10
output_datetime_format = '%Y/%m/%d %H:%M:%S'
# datetimeオブジェクト作成
datetime_obj = dt.datetime.strptime(reportedTime, input_datetime_format)
# 時差補正
td = dt.timedelta(hours=UTC)
# 日時フォーマット整形
reportedTime = (datetime_obj + td).strftime(output_datetime_format)
# 属性情報の取得
attributes = event['placementInfo']['attributes']
try:
# トークンが設定されていれば
if 'LINE_NOTIFY_TOKEN' in attributes:
LINE_NOTIFY_TOKEN = attributes['LINE_NOTIFY_TOKEN']
elif LINE_NOTIFY_TOKEN == "":
raise AttributeError("LINE Notifyのトークンが見つかりません。")
# ボタン名が設定されていれば
if 'BUTTON_NAME' in attributes:
BUTTON_NAME = attributes['BUTTON_NAME']
else:
raise AttributeError("ボタン名が見つかりません。")
# クリック種別に応じた処理
if clickType == "SINGLE":
if 'SINGLE_MESSAGE' in attributes:
message = attributes['SINGLE_MESSAGE']
else:
raise AttributeError("シングルクリック時のメッセージが定義されていません。")
elif clickType == "DOUBLE":
if 'DOUBLE_MESSAGE' in attributes:
message = attributes['DOUBLE_MESSAGE']
else:
raise AttributeError("ダブルクリック時のメッセージが定義されていません。")
elif clickType == "LONG":
if 'LONG_MESSAGE' in attributes:
message = attributes['LONG_MESSAGE']
else:
raise AttributeError("ロングクリック時のメッセージが定義されていません。")
# メッセージ生成
message = "\n"+BUTTON_NAME+"が「"+message+"」と言っています。\n" \
+ "時刻:"+reportedTime
# + "時刻:"+reportedTime+"\n" \
# + "ボタン名:"+placementName+"\n" \
# + "シリアル:"+deviceId
# LINE送信処理
send_to_line(message)
except AttributeError as e:
error_message = "\nプロジェクト名["+projectName+"]の端末["+placementName+"("+deviceId+")]でエラーが発生しました:"+e.args[0]+"\n" \
+ "時刻:"+reportedTime
print(error_message)
# LINE Notifyのトークンが設定されていれば、エラーメッセージを通知
if 'LINE_NOTIFY_TOKEN' in attributes:
send_to_line(error_message)
def send_to_line(message):
headers = {"Authorization": "Bearer " + LINE_NOTIFY_TOKEN}
payload = {"message": message}
r = requests.post(LINE_NOTIFY_URL, headers=headers, params=payload)
print(r.text)
テストイベント用のJSON
これをLambdaのテストイベントの箇所に貼り付けてテストしたら、LINEに通知されます。
(トークンだけ書き換えてね)
{
"deviceInfo": {
"deviceId": "1AB2CDEFGHI3456",
"type": "button",
"remainingLife": 98.4,
"attributes": {
"projectRegion": "ap-northeast-1",
"projectName": "howareyou_mom",
"placementName": "button_01",
"deviceTemplateName": "notifyToLINE"
}
},
"deviceEvent": {
"buttonClicked": {
"clickType": "SINGLE",
"reportedTime": "2018-11-09T16:29:40.474Z"
}
},
"placementInfo": {
"projectName": "howareyou_mom",
"placementName": "button_01",
"attributes": {
"SINGLE_MESSAGE": "元気です。",
"DOUBLE_MESSAGE": "元気してる?",
"LINE_NOTIFY_TOKEN": "(LINE Notifyトークン)",
"LONG_MESSAGE2": "連絡ちょうだい!",
"BUTTON_NAME": "息子"
},
"devices": {
"notifyToLINE": "1AB2CDEFGHI3456"
}
}
}
実際の画面
できたー!オカンがもうすぐ誕生日やから、このボタン渡して親孝行するんだ!日本初の「オカンにソラコムボタン渡した息子」になりたい…!
— itox(dev) (@itoxdev) 2018年11月9日
AWS IoT 1-Click側の設定だけでLambdaには手を加えなくていい親切設計です。メッチャ汎用的。(近日Qiitaに書きます)#SORACOM #あのボタン #AWS #IoT pic.twitter.com/lqdj3xm0Uj
作ってみた感想
-
実は今回AWSというかLambda初めて触ったけどメッチャ面白い
- アカウント作って1週間くらい(いや、ずっとやりたいとは思ってたのよ)
-
お母さんに早く渡したい
- お母さんと電話して頭出しは済んでる
- お母さん「それよりあんた、誕生日プレゼントにひこにゃんのLINEスタンプ欲しいわ」
- まさかのスタンプおねだりかよ
- え、VSCodeメッチャ便利…(これまでメモ帳で頑張ってた)
- PythonいいよPython、モジュール使いやすいぜ
- よく考えたら「元気やでボタン」やのに本文中は「元気です」とか言ってるごめん
- IoTって技術ありきじゃなくて、アイデア勝負やんな、やっぱ
- IoTはきっと人を幸せにできる
今後考えてること
- DynamoDBに入れたいなぁ
- もうちょい可視化したいなぁ
- LINEじゃなくてAlexaに出力しても面白いなぁ
- Wio + モータードライバ or リレーとか使って、元気ならぬいぐるみ動かしても面白いなぁ
以上