SORACOMさんから発売された「SORACOM LTE-M Button powered by AWS」を入手して、ちょっとやってみたメモです。
まず繋がるの?
エリア検索で調べたら、自宅、エリア外疑惑。。。
KDDI IoT通信サービス LPWA: エリア検索
・・・結果。
つながりました。ふー。
さて、気を取り直して、色々やって見ます。
作って見た
SORACOMのAPIリファレンスを見ていたら、ボタンの情報がAPIで取れるようになっていたので、
LambdaでAPI叩いて、ボタンの情報取得して、その情報含めてSlackに投稿するようにしました。
(API Reference#Gadget)
突貫感すみません。
流れは以下。
- ボタンクリック
- AWS IoT 1-Clickで受ける
- Lambda起動
- SORACOM APIでボタンの情報取得
- SlackのIncoming WebHooksのURL叩いて、メッセージ投稿
こんな感じ。ボタン名はAPIで取ってます。1号機なんで。。。w(ガンダムネタです)
シングルクリック
ダブルクリック
長押し
Lambda関数(最近node.js使いなので、node.jsで書いてます。)
slack投げる部分は、LambdaのBluePrintとか参考にしてます。
index.js
'use strict'
const request = require('request');
const moment = require('moment-timezone');
const AWS = require('aws-sdk');
AWS.config.update({ region: 'ap-northeast-1' });
const kms = new AWS.KMS();
const encryptedSlackWebHookUrl = process.env['SLACK_WEBHOOK_URL']; // slackのURL
let decryptedSlackWebHookUrl;
const encryptedSoracomKey = process.env['SORACOM_KEY']; // SORACOM API認証キー
let decryptedSoracomKey;
const encryptedSoracomKeyId = process.env['SORACOM_KEY_ID'];SORACOM API認証キー ID
let decryptedSoracomKeyId;
/**
* 初期化
* @param {[type]} event [description]
* @param {[type]} context [description]
* @return {[type]} [description]
*/
const initialize = (event, context) => {
return new Promise((resolve) => {
// クリックのタイプで文字変えてます。
let clickTypeName = 'クリック';
if (event.deviceEvent.buttonClicked.clickType == "DOUBLE") {
clickTypeName = 'ダブルクリック';
} else if (event.deviceEvent.buttonClicked.clickType == "LONG") {
clickTypeName = '長押し';
}
const stash = {
apiKey:'',
token:'',
serialNumber: event.deviceInfo.deviceId,
remainingLife: event.deviceInfo.remainingLife,
clickType: event.deviceEvent.buttonClicked.clickType,
clickTypeName,
reportedTime: event.deviceEvent.buttonClicked.reportedTime,
gadgetsInfo:{},
};
console.log(stash);
resolve(stash);
});
};
/**
* キー情報復号処理
* @param {[type]} stash [description]
* @return {[type]} [description]
*/
const decryptedUrl = (stash) => {
return new Promise((resolve, reject) => {
if (!decryptedSlackWebHookUrl) {
kms.decrypt({ CiphertextBlob: new Buffer(encryptedSlackWebHookUrl, 'base64') }, (err, data) => {
if (err) {
console.log('Decrypt error:', err);
reject(err);
}
decryptedSlackWebHookUrl = data.Plaintext.toString('ascii');
resolve(stash);
});
} else {
resolve(stash);
}
});
};
/**
* キー情報復号処理
* @param {[type]} stash [description]
* @return {[type]} [description]
*/
const decryptedKey = (stash) => {
return new Promise((resolve, reject) => {
if (!decryptedSoracomKey && !decryptedSoracomKeyId) {
kms.decrypt({ CiphertextBlob: new Buffer(encryptedSoracomKey, 'base64') }, (err, data) => {
if (err) {
console.log('Decrypt error:', err);
reject(err);
}
decryptedSoracomKey = data.Plaintext.toString('ascii');
kms.decrypt({ CiphertextBlob: new Buffer(encryptedSoracomKeyId, 'base64') }, (err, data) => {
if (err) {
console.log('Decrypt error:', err);
reject(err);
}
decryptedSoracomKeyId = data.Plaintext.toString('ascii');
resolve(stash);
});
});
} else {
resolve(stash);
}
});
};
/**
* SORACOM API 認証
* @param {[type]} stash [description]
* @return {[type]} [description]
*/
const getSoracomApiAuth = (stash) => {
return new Promise((resolve, reject) => {
const optionsAuth = {
url: 'https://api.soracom.io/v1/auth',
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json'
},
json: {
'authKeyId':decryptedSoracomKeyId,
'authKey':decryptedSoracomKey,
'tokenTimeoutSeconds':86400
}
};
// SORACOM Auth APIコール
request(optionsAuth, function (error, response, body) {
if (error) {
console.log('Auth API Error: ' + error);
reject(error);
} else if (response.statusCode != 200) {
console.log('Auth API Error response.statusCode: ' + response.statusCode);
const err = {
'statusCode': response.statusCode
}
reject(err);
} else {
console.log('APIコール成功');
stash.apiKey = body.apiKey;
stash.token = body.token;
resolve(stash);
}
});
});
};
/**
* SORACOM API ボタン情報取得
* @param {[type]} stash [description]
* @return {[type]} [description]
*/
const getGadgetInfo = (stash) => {
return new Promise((resolve, reject) => {
const apiKey = stash.apiKey;
const token = stash.token;
const optionsGetGadgetsData = {
url: `https://api.soracom.io/v1/gadgets/button/${stash.serialNumber}`,
method: 'GET',
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json',
'X-Soracom-API-Key': apiKey,
'X-Soracom-Token': token
}
};
// APIコール
request(optionsGetGadgetsData, function (error, response, body) {
if (error) {
console.log('Gadgets API Error: ' + error);
reject(error);
} else if (response.statusCode != 200) {
console.log('Gadgets API Error Response.statusCode: ' + response.statusCode);
const err = {
'statusCode': response.statusCode
}
reject(err);
} else {
console.log('APIコール成功 body:' + JSON.parse(body).operatorId);
const gadget = JSON.parse(body);
stash.gadgetInfo = gadget;
resolve(stash);
}
});
});
};
/**
* Slack POST
* @param {[type]} stash [description]
* @return {[type]} [description]
*/
const postSlackMessage = (stash) => {
return new Promise((resolve, reject) => {
console.log('slack Call');
const gadget = stash.gadgetInfo;
let messageArray = [];
messageArray.push(`ボタン${gadget.tags.name}が${stash.clickTypeName}されました。`);
const useCount = 1500 - gadget.attributes.remainingCount;
messageArray.push(`現在のクリック数は${useCount}で残り${gadget.attributes.remainingCount}です。`);
messageArray.push(`有効期限は${moment(gadget.attributes.contractEndingTime).tz("Asia/Tokyo").format('YYYY-MM-DD hh:mm:ss')}です。`);
const message = messageArray.join('\n');
// リクエスト設定
const options = {
url: decryptedSlackWebHookUrl,
headers: {
'Content-type': 'application/json'
},
body: {
"text": message
},
json: true
};
// メッセージ送信
request.post(options, function(error, response, body) {
if (!error && response.statusCode == 200) {
resolve(stash);
} else {
if (error) {
console.log('Slack API Error: ' + error);
reject(error);
} else {
console.log('Slack API Error: ' + response.statusCode);
const err = {
'statusCode': response.statusCode
}
reject(err);
}
}
});
});
};
/**
* Main処理
* @param {[type]} event [description]
* @param {[type]} context [description]
* @param {Function} callback [description]
* @return {[type]} [description]
*/
exports.handler = (event, context, callback) => {
initialize(event, context)
.then(decryptedKey)
.then(getSoracomApiAuth)
.then(getGadgetInfo)
.then(decryptedUrl)
.then(postSlackMessage)
.then(callback.bind(null, null))
.catch(callback);
};
他にも色々できそうですので、それは引き続き...
最後に
/gadgets の公開、待ってました!
あと、APIでもっとボタンの情報取れるといいですね。 これは #ソラコムサンタ かな。