JavaScript
lambda
Slack
IoT
SORACOM

SORACOM LTE-M ButtonでSlackにモールス信号を送れるようにした


はじめに


  • ある日、会社にたくさんのSORACOMボタンが届いた


  • 何か開発したいと思いつつSORACOMさんのドキュメントを読んでみる


  • 1つのボタンでシングルクリック、ダブルクリック、長押しが出来るようだ


  • モールス信号が打てますね!思いついたらすぐ開発していく



開発方法


仕様



  • 和文モールス符号を扱えるようにする

  • クリックはそれぞれ以下のように扱う


    • シングルクリック: トン

    • 長押し: ツー

    • ダブルクリック: 区切り文字


      • (e.g.)シングルクリック、長押し、ダブルクリック => イ





  • ダブルクリックが2連続で来たらSlackへ通知させる

  • 1回のシングルクリック、ダブルクリック、長押しごとにAWS Lambdaがコールされてくるが全信号の終わりまで状態を持つ必要がある

  • AWS Lambdaは/tmpを扱えるのでファイルを作成しダブルクリックが2連続で来るまで状態を管理する


コード


index.js

const https = require('https');

const url = require('url');
const fs = require('fs');

const defaultSlackUrl = process.env['SLACK_URL']
const tmpFilePath = "/tmp/morse_signal.txt"
const getSignal = {
"SINGLE": ".", // トン
"DOUBLE": " ", // デリミタ
"LONG": "-" // ツー
}

const wabunMorseCode = {
".-": "イ",
".-.-": "ロ",
"-...": "ハ",
"-.-.": "ニ",
"-..": "ホ",
".": "ヘ",
"..-..": "ト",
"..-.": "チ",
"--.": "リ",
"....": "ヌ",
"-.--.": "ル",
".---": "ヲ",
"-.-": "ワ",
".-..": "カ",
"--": "ヨ",
"-.": "タ",
"---": "レ",
"---.": "ソ",
".--.": "ツ",
"--.-": "ネ",
".-.": "ナ",
"...": "ラ",
"-": "ム",
"..-": "ウ",
".-..-": "ヰ",
"..--": "ノ",
".-...": "オ",
"...-": "ク",
".--": "ヤ",
"-..-": "マ",
"-.--": "ケ",
"--..": "フ",
"----": "コ",
"-.---": "エ",
".-.--": "テ",
"--.--": "ア",
"-.-.-": "サ",
"-.-..": "キ",
"-..--": "ユ",
"-...-": "メ",
"..-.-": "ミ",
"--.-.": "シ",
".--..": "ヱ",
"--..-": "ヒ",
"-..-.": "モ",
".---.": "セ",
"---.-": "ス",
".-.-.": "ン",
"..": "゛",
"..--.": "゜"
}

const writeSignal = (signal) => {
console.log(signal);
fs.appendFileSync(tmpFilePath, signal);
}

const readSignal = () => {
return fs.readFileSync(tmpFilePath, 'utf8');
}

const decodeWabunMorseCode = (signal) => {
return wabunMorseCode[signal] ? wabunMorseCode[signal] : "?" // デコードできない場合は?を返す
}

const sendSlack = (event, context, callback, message) => {
var slackUrl = (event.placementInfo.attributes.slackUrl) ? event.placementInfo.attributes.slackUrl : defaultSlackUrl
if (!slackUrl) {

}
var slackReqOptions = url.parse(slackUrl);
slackReqOptions.method = 'POST';
slackReqOptions.headers = { 'Content-Type': 'application/json' };

var payload = { 'text': message }

if (event.placementInfo.attributes.username) {
payload.username = event.placementInfo.attributes.username;
}
if (event.placementInfo.attributes.iconEmoji) {
payload.icon_emoji = event.placementInfo.attributes.iconEmoji;
}
if (event.placementInfo.attributes.iconUrl) {
payload.icon_url = event.placementInfo.attributes.iconUrl;
payload.as_user = false;
}
if (event.placementInfo.attributes.slackChannel) {
payload.channel = event.placementInfo.attributes.slackChannel;
}
var body = JSON.stringify(payload);
slackReqOptions.headers = {
'Content-Type': 'application/json',
'Content-Length': Buffer.byteLength(body),
};
var req = https.request(slackReqOptions, function(res) {
if (res.statusCode === 200) {
console.log('Posted to slack');
callback(null, { "result": "ok" });
}
else {
callback(false, { "result": "ng", "reason": 'Failed to post slack ' + res.statusCode })
}
return res;
});
req.write(body)
req.end();
}

exports.handler = (event, context, callback) => {
console.log('Received event:', JSON.stringify(event, null, 2));

writeSignal(getSignal[event.deviceEvent.buttonClicked.clickType]);
const singals = readSignal();
console.log("\"" + singals + "\"");

// デリミタが連続で来たらメッセージ送信
if (singals.match(/.+ $/)) {
var message = singals.split(" ").map(function(signal) {
console.log("signal: " + signal);
if (signal === "") { // デリミタのみを無視
return "";
}
else {
return decodeWabunMorseCode(signal)
// return wabun_morse[signal];
}
});
// console.log(message.join(""));
sendSlack(event, context, callback, message.join(""));
fs.unlinkSync(tmpFilePath);
}
};



モールス信号を送ってみる


  • 送りたいメッセージを決めて間違えないようにボタンをクリックする

  • この記事のなかでこの作業が一番難しいです

----    コ

.-.-. ン
-.-. ニ
..-. チ
-... ハ
.---. セ
.-.. カ
.- イ


  • 最後にダブルクリックが連続になるようにしてSlackへ送る

Screenshot from 2018-11-11 19-19-42.png


  • いい感じですね


終わりに


  • 1回のクリックから送信完了までそこそこ時間がかかってしまう

  • 本当はモールス信号でQiita記事を書きたかったんですが無理すぎました

  • 電池とクリック回数の減りがマッハ