SORACOM LTE-M Buttonのボタンを押したら、Slackに通知する

SORACOM LTE-M Buttonのボタンを押したら、Slackに通知する:

SORACOMさんから発売された「SORACOM LTE-M Button powered by AWS」を入手して、ちょっとやってみたメモです。


まず繋がるの?

エリア検索で調べたら、自宅、エリア外疑惑。。。
KDDI IoT通信サービス LPWA: エリア検索

・・・結果。

つながりました。ふー。

さて、気を取り直して、色々やって見ます。


作って見た

SORACOMのAPIリファレンスを見ていたら、ボタンの情報がAPIで取れるようになっていたので、

LambdaでAPI叩いて、ボタンの情報取得して、その情報含めてSlackに投稿するようにしました。

API Reference#Gadget



soracom-slack.png


突貫感すみません。

流れは以下。

  1. ボタンクリック
  2. AWS IoT 1-Clickで受ける
  3. Lambda起動
  4. SORACOM APIでボタンの情報取得
  5. SlackのIncoming WebHooksのURL叩いて、メッセージ投稿
こんな感じ。ボタン名はAPIで取ってます。1号機なんで。。。w(ガンダムネタです)


シングルクリック

button-single.png


ダブルクリック

button-double.png


長押し

button-long.png

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でもっとボタンの情報取れるといいですね。 これは #ソラコムサンタ かな。

コメント

このブログの人気の投稿

投稿時間:2021-06-17 22:08:45 RSSフィード2021-06-17 22:00 分まとめ(2089件)

投稿時間:2021-06-20 02:06:12 RSSフィード2021-06-20 02:00 分まとめ(3871件)

投稿時間:2021-06-17 05:05:34 RSSフィード2021-06-17 05:00 分まとめ(1274件)