SORACOM Orbit試してみた3回目です。(時間が経ってしまい申し訳ないです。)
さて、ソラコムさんの年次のイベント、SORACOM Discovery Online 2020のナイトイベント内であった
ソラコムウルトラクイズで、大人気なく全力で答えてしまい、
去年に引き続き、景品(「IoT 体験キット ~磁気センサー~」)をゲットしてしまいました。
ソラコムウルトラクイズの景品届いたー!
— Kenichiro Wada (@Keni_W) July 18, 2020
せっかくなので、Let's try IoT プロトタイピングオンラインのドア開閉モニタリングのやつ、やってみよう(一応そろえてたので、やったけど)。
Hosted Funkの部分は、Orbit使って自前のFunk & Lambdaで!#soracom https://t.co/zEgditWjYe
もともと、SORACOM LTE-M Button Plusと磁気式リードスイッチは持っていたのですが、
公式なw組み合わせて、
【IoT DIY レシピ】IoT 体験キット ~磁気センサー~ で作る「ドアの開閉モニタリング」
でやってみることに。
このレシピでは、開閉検知時に、メールで通知するようになっておりますが、
「SORACOM Hosted Funk」という新サービス?を使ってメールを飛ばします。
9. SORACOM Funk でメールによる通知をする
無論、レシピでは、自前のLambdaから飛ばすこともでき、その案内もありますが。。。
前回も記載しましたが、
タグ情報やユーザーデータの情報を取得するためには、Lambda側で以下の実装が必要でした。
- 認証キー(もしくは他の手段)で、トークンを取得
- トークンをパラメータに、SIM情報を取得
そこに登場したのが、SORACOM Orbitです。
Orbitを使えば、この辺が一切不要になります!(再掲)
これは作ってみないとってことで、
今回は、SORACOM Orbitでタグにセットしたメールアドレスと、ユーザーデータの情報を取得し、Funkを使って、AWS Lambdaに渡して、Lambdaからメールを飛ばすというのをやってみました。
前置き長っ!
ここからはちゃっちゃと。
構成
メールはSES(最近来たTokyoリージョン使ってたりします)で飛ばしてます。
個人&小規模の場合は、メールアドレスの認証を忘れずに。。。
送信情報格納
まず、送信先のメールアドレスですが、
ボタンごとに変えたいとかそういうこともある(きっとある)と思うので、
メールアドレスはSIMのタグにセットします。
メールの件名と本文は、SIMグループのユーザーデータにセットします。
JSON形式(今回初めて使ったw)にしてます。
Orbit側
前回のソースを変更してます。
変更点としては、TagからEmailの情報を取得したぐらいでしょうか。
#include <cmath>
#include <cstdint>
#include <cstdlib>
#include <string>
#include <emscripten.h>
#include "soracom/orbit.h"
#include "nlohmann/json.hpp"
using nlohmann::json;
int32_t uplink_body();
extern "C" {
EMSCRIPTEN_KEEPALIVE
int32_t uplink() {
soracom_log("hello, orbit! for IoT Button Edition.\n");
return uplink_body();
}
}
std::string get_tag(const std::string& tag_name) {
const char* tag_value = NULL;
size_t tag_value_len = 0;
int32_t err = soracom_get_tag_value(tag_name.c_str(), tag_name.size(), &tag_value, &tag_value_len);
if (err < 0) {
return "";
}
return std::string(tag_value);
}
std::string get_source_item(const std::string& name) {
const char* value = NULL;
size_t value_len = 0;
int32_t err = soracom_get_source_value(name.c_str(), name.size(), &value, &value_len);
if (err < 0) {
return "";
}
return std::string(value);
}
double deg2rad(double deg) {
return deg * (M_PI / 180);
}
double calc_distance_in_km(double lat1, double lon1, double lat2, double lon2) {
double r = 6371; // Radius of the earth in km
double d_lat = deg2rad(lat2 - lat1);
double d_lon = deg2rad(lon2 - lon1);
double a = sin(d_lat/2)*sin(d_lat/2) + cos(deg2rad(lat1))*cos(deg2rad(lat2))*sin(d_lon/2)*sin(d_lon/2);
double c = 2 * atan2(sqrt(a), sqrt(1-a));
return r * c;
}
double parse_double(const std::string& s, double default_value) {
char *p_end;
double d = strtod(s.c_str(), &p_end);
if (*p_end != NULL) {
return default_value;
}
return d;
}
int32_t uplink_body() {
const char* buf = NULL;
size_t siz = 0;
int32_t err = soracom_get_input_buffer_as_string(&buf, &siz);
if (err < 0) {
return err;
}
soracom_log("received data: %s\n", buf);
json j = json::parse(buf);
soracom_release_input_buffer(buf);
std::string imsi = get_source_item("resourceId");
soracom_log("imsi = %s\n", imsi.c_str());
std::string name = get_tag("name");
std::string center_lat_str = get_tag("center_lat");
std::string center_lon_str = get_tag("center_lon");
std::string radius_str = get_tag("radius");
std::string employee_code = get_tag("employee_code");
std::string email = get_tag("email");
j["name"] = name;
j["email"] = email;
j["employee_code"] = employee_code;
j["imsi"] = imsi;
j["inside_area"] = 0;
const char* userdata = NULL;
size_t ud_siz = 0;
err = soracom_get_userdata_as_string(&userdata, &ud_siz);
if (err < 0) {
return err;
}
soracom_log("userdata: %s\n", userdata);
j["user_data"] = std::string(userdata);
soracom_release_userdata(userdata);
std::string output = j.dump();
soracom_set_json_output(output.c_str(), output.size());
return 0;
}
Lambda側
今回新たに作成しました。
eventオブジェクトから、メールアドレスと、メールの件名、本文を取得し、SESに投げるだけです。
'use strict';
const AWSXRay = require('aws-xray-sdk')
const AWS = AWSXRay.captureAWS(require('aws-sdk'));
AWS.config.update({ region: 'ap-northeast-1' });
const ses = new AWS.SES
async function sendMail(userData, email) {
let mailAddressList = email.split(',');;
let mailSubject;
let mailBody;
let isError = false;
if (userData['subject']) {
mailSubject = userData['subject'];
} else {
console.log('Not Setting subject')
isError = true;
}
if (userData['body']) {
mailBody = userData['body'];
} else {
console.log('Not Setting body')
isError = true;
}
if (!isError) {
const params = {
Source : 'xxxxxxxx@gmail.com',
Destination : {
ToAddresses : mailAddressList
},
Message : {
Subject : {
Data : mailSubject ,
Charset : 'utf-8'
},
Body: {
Text: {
Charset: 'utf-8',
Data: mailBody
}
}
}
}
try {
const res = await ses.sendEmail(params).promise();
console.log(res)
if (res.data === 'ok') {
return true;
} else {
return false;
}
} catch (error) {
console.log('SES SendEmail Error: ' + error);
return false;
}
} else {
return false;
}
}
module.exports.index = async(event, context) => {
console.log(JSON.stringify(event, 2));
console.log(JSON.stringify(context, 2));
let isError = false;
if (!event.email) {
console.log('Send Email Address Not Setting.')
isError = true;
}
if (!isError) {
if (event.user_data) {
if (await sendMail(JSON.parse(event.user_data), event.email) == false) {
isError = true;
}
} else {
console.log('userdata Not Setting.')
isError = true;
}
}
if (isError) {
return {
statusCode: 500,
body: JSON.stringify(
{
message: 'userdata Not Setting. Not Sending Mail.',
input: event,
},
null,
2
),
};
} else {
return {
statusCode: 200,
body: JSON.stringify(
{
message: 'Go Serverless v1.0! Your function executed successfully!',
input: event,
},
null,
2
),
};
}
};
Serverless Frameworkを使っているので、
serverless.yamlも一緒に。
SESを実行権限ついているぐらいでしょうか。。。
service: soracom-iot-button-mail-sender
provider:
name: aws
runtime: nodejs12.x
stage: dev
region: ap-northeast-1
iamRoleStatements:
- Effect: Allow
Action:
- ses:SendEmail
- ses:SendRawEmail
Resource: '*'
- Effect: Allow
Action:
- xray:PutTraceSegments
- xray:PutTelemetryRecords
Resource: "*"
plugins:
- serverless-plugin-tracing
functions:
sendMail:
handler: handler.index
memorySize: 128
timeout: 10
tracing: PassThrough
まあ、そのままですね。
まとめ
前述した通り、
Orbitなしで実装する場合は、
メールアドレスの情報、メール本文等は、
Lambdaでタグの情報、ユーザーデータの情報をSORACOM APIを使って、取得する。
もしくは、メールアドレスはDynamoDBなどで管理する必要がありました。
Orbitを使うことで、
情報をSORACOMプラットフォーム上で一元管理できるので、
構成が非常にシンプルになりました、
次で一旦試してみた系はラストの予定です。