Python
AWS
JSON
IoT
SORACOM

SORACOM LTE-M Buttonでお母さんに親孝行しようと思った話(元気やでボタン)


とある休日に、何を思ったか


  • SORACOMのボタン(俗に言う「あのボタン」)が発売されるねんなぁ

  • 何作ろうかなぁ

  • そりゃ作るのはできるけどさ、せっかく作るなら使うもんがいいなぁ


そんな時、どこからともなくお母さんからLINEが


  • それに対し、息子はこう返す。 



  • そういえば、そろそろお母さんの誕生日やねんなぁ…


息子の悩み

実家の大阪、自分の住む関東。

元気やけど、それを言うのはなんか気恥ずかしい。

でもお母さんから来る「元気か?」「元気やろ?」はちょっと嬉しい。

お母さんに心配はかけたくない。


そうだ、これだ

いつでもどこでもお母さんに「元気やで」って言えればメッチャ良くね?

…お母さんの誕生日に贈ることにしました。あのボタン。


どんなものを作ろうと思ったか


  • ボタンを押すと、LINEに通知が届く

  • ボタンの役割によって届くメッセージが異なる

  • どうせだったら汎用的に使えるものが欲しい

  • 名付けて

fudechar_jibyt.png


仕組み

こんな感じです。

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側の設定


の前に、気をつけたいこと


コードで工夫した点


  • 例外処理ちゃんと書いて、CloudWatchで出力されるログに、エラーが出るようにした

  • LINE Notifyのトークンが設定されていれば、そのログをLINE側に通知するようにした

  • LINE Notifyのトークンをコード内に設定できるようにした(環境変数にしたら良かった…)


実際のコード

messageのところは、適宜書き換えて好きなフォーマットで出力してください。


lambda_function.py

# -*- 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に通知されます。

(トークンだけ書き換えてね)


test_event.json

{

"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"
}
}
}


実際の画面

やりとりのイメージ

エラー出力


作ってみた感想



  • 実は今回AWSというかLambda初めて触ったけどメッチャ面白い


    • アカウント作って1週間くらい(いや、ずっとやりたいとは思ってたのよ)




  • お母さんに早く渡したい


    • お母さんと電話して頭出しは済んでる

    • お母さん「それよりあんた、誕生日プレゼントにひこにゃんのLINEスタンプ欲しいわ

    • まさかのスタンプおねだりかよ



  • え、VSCodeメッチャ便利…(これまでメモ帳で頑張ってた)

  • PythonいいよPython、モジュール使いやすいぜ

  • よく考えたら「元気やでボタン」やのに本文中は「元気です」とか言ってるごめん

  • IoTって技術ありきじゃなくて、アイデア勝負やんな、やっぱ

  • IoTはきっと人を幸せにできる


今後考えてること


  • DynamoDBに入れたいなぁ

  • もうちょい可視化したいなぁ

  • LINEじゃなくてAlexaに出力しても面白いなぁ

  • Wio + モータードライバ or リレーとか使って、元気ならぬいぐるみ動かしても面白いなぁ

以上