Espruino on ESP32 で LINE(その1)
Twitterへのアクセスにも飽きてきたので、今回はLINEにメッセージを投げてみます。
LINEにはWebサービスから簡単にメッセージを送信できる、LINE Notifyというサービスがあります。これを使用すると簡単にLINEにメッセージを送信できます。
LINE Notifyでメッセージを送信するには、アクセストークンを取得する必要があります。
例によって、先人の知恵を拝借してアクセストークンを取得しましょう。
[超簡単]LINE notify を使ってみる - Qiita
自分にだけメッセージを送信したい場合は、トークルームではなく
「1:1でLINE Notifyから通知を受け取る」
を選択します。
通知先は変更できません。変更するにはアクセストークンを再発行してください。
アクセストークンを他人に知られると、勝手にメッセージが送られることがあるので、注意しましょう。
アクセストークンを無効にするには、LINEのマイページにある、「連携中のサービス」から対象のサービスの解除ボタンをクリックしてください。
それでは、LINE Notifyにメッセージを送信するモジュールです。
以下をtiny_line.jsというファイル名でオンボードストレージの /node_modules ディレクトリに格納してください。
var tls = require("tls"); if (typeof(E) ==='function') { // platform is espruino var platform = 'espruino'; } else { // platform is node.js var platform = 'node'; var events = require('events'); var util = require('util'); } // ######## percent encording ################################ var RFC3986_encode = function(str) { // var ret = encodeURIComponent(str); // change according to RFC3986 /* ********** RegEx are not supported in Espruino var ret = encodeURIComponent(str).replace(/[!*'()]/g, function(p) { return "%" + p.charCodeAt(0).toString(16); }); ********** */ var tmp = encodeURIComponent(str); var ret = ""; for (var i = 0; i < tmp.length; i++) { var c = tmp[i]; if ((c==="!") || (c==="*") || (c==="'") || (c==="(") || (c===")")) { ret += '%'+c.charCodeAt(0).toString(16); } else { ret += c; } } return ret; } // ######## tiny_line class ################################ var tiny_line = function(access_token, _DEBUG_) { this.access_token = access_token; this._DEBUG_ = _DEBUG_; if (platform == 'node') { // for EventEmitter events.EventEmitter.call(this); } } if (platform == 'node') { // for EventEmitter util.inherits(tiny_line, events.EventEmitter); } // ######## debug print ################################ tiny_line.prototype.debug_print = function(str) { if (this._DEBUG_) { //console.log(Date().toString()); console.log(str); } } // ######## make request message ################################ tiny_line.prototype.makeRequestMessage = function(method, server, endpoint, message){ method = method.toUpperCase(); // to upper // make message body var body = 'message=' + message; // make request var request = method + ' ' + endpoint + ' HTTP/1.1\n'; // make message header var header = 'Host: ' + server + '\n' + 'User-Agent: espruino line Bot v0.1\n' + 'Accept: */*\n' + 'Connection: close\n' // これがないとレスポンス受信後も接続状態が保持されてしまう + 'Authorization: Bearer ' + this.access_token +'\n' + 'Content-Type: application/x-www-form-urlencoded\n' + 'Content-Length: ' + body.length.toString() + '\n'; // request message var ret = request + header + '\n' + body + '\n'; return ret; } // ######## Send message ################################ tiny_line.prototype.sendmessage = function(host, msg) { var _self = this; var client = tls.connect(host, function() { client.on('data', function(data) { var rcv_header = data.toString(); // 文字列に変換 var p0 = rcv_header.indexOf('\n'); // 最初の改行の位置 var line = rcv_header.slice(0, p0); // 最初の1行取り出し var p1 = line.indexOf(' ') + 1; // 最初のスペースの次(エラーコードの位置) var p2 = line.indexOf(' ', p1) + 1; // 二個目のスペースの次(エラーメッセージの位置) var err_code = parseInt(line.slice(p1, p2)); var err_msg = line.slice(p2); _self.emit("response", err_code, err_msg, rcv_header); }); client.on('end', function() { _self.emit("end"); }); _self.emit("connect"); client.write(msg); }); } // ######## notify API ################################ tiny_line.prototype.notify = function(msg, encoded) { if (!msg) { throw new Error("message is required."); } var server = 'notify-api.line.me'; var host = { host: server, port: 443}; // メッセージがエンコードされていなければエンコードする if (!encoded) { msg = RFC3986_encode(msg); } var reqMessage = this.makeRequestMessage('POST', server, '/api/notify', msg); this.debug_print("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%"); this.debug_print(reqMessage); this.debug_print("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%"); this.sendmessage(host, reqMessage); } module.exports = tiny_line; module.exports.RFC3986_encode = RFC3986_encode;
準備ができたら、以下のプログラムを実行してみましょう。
var access_token = '取得したアクセストークン'; if (typeof(ESP32) ==='function') { // platform is espruino on ESP32 // Wi-Fi アクセスポイントへの接続 var wifi = require('MyWifi'); } if (typeof(E) ==='function') { // platform is espruino // set time zone fixed to 'JST' E.setTimeZone(9); } // モジュール読み込み var tiny_line = require("tiny_line"); // 初期化 var tl = new tiny_line(access_token, true); tl.on("connect", function() { console.log("%%%% CONNECTED %%%%"); }); tl.on("response", function(code, msg, data) { if (code == 200) { console.log("%%%% RESPONSE : " + code.toString() + " : " + msg + " %%%%"); } else { console.log("%%%% RESPONSE ERROR!!! : " + code.toString() + " : " + msg + " %%%%"); console.log("#### RESPONSE ####"); console.log(data); console.log("############"); } }); tl.on("end", function() { console.log("%%%% END %%%%"); }); // メッセージ送信 tl.notify("good morning! (or afternoon)", false);
プログラムを実行すると、LINEに以下のようなメッセージが送信されます。
「[トークン名] good morning! (or afternoon)」
以下、プログラムの説明です。
Twitterのときにも使った、ESP32専用設定、Espruino専用設定の部分です。
if (typeof(ESP32) ==='function') { // platform is espruino on ESP32 // Wi-Fi アクセスポイントへの接続 var wifi = require('MyWifi'); } if (typeof(E) ==='function') { // platform is espruino // set time zone fixed to 'JST' E.setTimeZone(9); }
モジュールを読み込みます。
// モジュール読み込み var tiny_line = require("tiny_line");
tiny_lineのインスタンスを生成します。
第1パラメータは↑で取得したアクセストークンです。
第2パラメータはモジュール内部のdebug_print (今回はtypo直しました)を表示するか否かを指定しています。
色々表示されてうっとうしい場合はfalseにしてください。
// 初期化 var tl = new tiny_line(access_token, true);
接続処理時のイベントハンドラです。イベントは"open"、"response"、"end"があります。
"response"イベントのパラメータは
code エラーコード
msg エラーメッセージ
data 受信データ全体
です。
tl.on("connect", function() { console.log("%%%% CONNECTED %%%%"); }); tl.on("response", function(code, msg, data) { if (code == 200) { console.log("%%%% RESPONSE : " + code.toString() + " : " + msg + " %%%%"); } else { console.log("%%%% RESPONSE ERROR!!! : " + code.toString() + " : " + msg + " %%%%"); console.log("#### RESPONSE ####"); console.log(data); console.log("############"); } }); tl.on("end", function() { console.log("%%%% END %%%%"); });
メッセージの送信処理です。
第1パラメータが送信する文字列、
第2パラメータが文字列がパーセントエンコード済みか否かを示すフラグです。これがfalse または省略されていると、送信文字列をnotify関数内部でエンコードします。
// メッセージ送信 tl.notify("good morning! (or afternoon)", false);
メッセージ送信部分を以下のように変更すると、
「[トークン名] 現在の時刻は Sat Jul 29 2017 11:15:37 GMT+0900 です」
のような日本語を含んだメッセージを送信することもできます。
var date = new Date(); date_str = tiny_line.RFC3986_encode(date.toString()); var str1 = "%E7%8F%BE%E5%9C%A8%E3%81%AE%E6%99%82%E5%88%BB%E3%81%AF%20"; // 現在の時刻は var str2 = "%20%E3%81%A7%E3%81%99"; // です tl.notify(str1 + date_str + str2, true);
notify関数の第1パラメータにパーセントエンコード済みの送信文字列を、第2パラメータにtrueを指定します。
パーセントエンコードはRFC2396でも大丈夫なようですが、念のためRFC3986にしてあります。
全角文字を含む静的な文字列のパーセントエンコードは、以下のサイトなどで行えます。
http://www.tagindex.com/tool/url.html
(文字コードで「UTF-8」を選択してから「エンコードする」ボタンをクリックしてください)
動的に変更したい半角文字列はtiny_line の RFC3986_encode 関数がエクスポートされているので、それを使えばOK。