obnizとサーバーレスを組み合わせるTips


これは[obniz advent calendar] (https://qiita.com/advent-calendar/2018/obniz)の25日目の記事です。

obnizとサーバーレスとの組み合わせ方ということで、lambdaとかで使うときに気をつけないといけないtipsを書いていこうかなと思います。ちょっと技術よりになります。


## サーバーレスとの連携について
スマートスピーカーとかIFTTTとか、イベントきっかけに動かすときにサーバーレス便利ですよね。
でも、普通のプログラムと違うところがいくつか出てくるので、注意が必要です。


この記事のコードはaws lambdaでのプログラムを書いています。他のサーバーレスも同じだと思いますが挙動違い等あったらコメントで教えてください。


## 終了を明確にする
サーバーレスでobnizを使う場合、一番気にしないといけないのがこの部分になります。
大抵のサーバーレスは30秒とかのタイムアウトがあり、それ以上プログラムを続けて動かすことができません。
obniz.jsはデフォルトのままだとwebsocketでずっとプログラムを動かそうとしてしまうので、きちんと明示的にobnizへの接続を切る必要があります。


```javascript
// aws lambda code
var Obniz = require("obniz");

exports.helloHandler = function(event, context, callback) {
  var obniz = new Obniz("obniz_id_here");
  obniz.onconnect = async function () {
    obniz.display.clear();
    obniz.display.print("Hello World");

    await obniz.wait(2000);     // 1) 2秒間待つ
    obniz.close();              // 2) ここで明示的に接続を切っている

    callback(null, "success");  // 3) lambdaにレスポンスを返す
  };
};

```

コメントで1)〜3)と書きましたが、この順番がまた重要です。

2)でobnizとの接続を切ると、obniz側がプログラムが終了したとおもってリセット（QRコードの画面）になります。"Hello World"を表示した瞬間に接続を切ってQR画面になると"Hello World"が表示されるのが一瞬すぎて見えないので、1)のwaitを2秒間入れています。

3)のレスポンスを返すのは`obniz.close()`よりも後である必要があります。
lambdaにレスポンスを返すと、強制的にプログラムが終了してしまう（ことがある）ので、これより後にプログラムを書くのは危険です。実行されない可能性があります。
  ※ココらへんはサーバーレスのサービスごとの仕様によるので、レスポンス返した後も動くものもあるようです


## 永続的に動作するようにする
2秒だけじゃなくもっと表示したい場合、`wait`を10秒とか20秒とかにするのもありですが、lambdaの起動制限(3秒）に引っかかります。
設定100秒とかにで変えることもできますが、その分lambdaの時間を使うのでお金がかかります。CPUもメモリも使ってないただの待ち時間にお金を払うのはちょっと気が引けます。

というわけで、obniz側の設定で乗り切りましょう。
obnizにはプログラム終了後もそのまま出力を維持するコマンド`resetOnDisconnect`があります。

対象はDisplayとPWMとIOのアウトプットと限定的ですが、lambdaのプログラムが終了後も動かすことができます


```javascript
// aws lambda code
var Obniz = require("obniz");

exports.helloHandler = function(event, context, callback) {
  var obniz = new Obniz("obniz_id_here");
  obniz.onconnect = async function () {
    obniz.resetOnDisconnect(false);   // 1) プログラム終了後も動くようにする

    obniz.display.clear();
    obniz.display.print("Hello World");

    obniz.close();              // 2) ここで明示的に接続を切っている

    callback(null, "success");  // 3) lambdaにレスポンスを返す
  };
};

```

これを実行すると、lambdaが終了しても画面に"Hello World"が出たままになります。
消したい場合はobnizの電源を抜き差しするのが手っ取り早いです。
そうするとQRコードが表示される画面に戻ります。

もしくは別のところで`obniz.resetOnDisconnect(true);`とすることでリセットするように戻すことができます。


## obnizがofflineのときの挙動を書く
上では`obniz.onconnect`で接続がうまく行った時にいろいろ実行していました。
ではobnizがオフラインだったらどうなるでしょうか。ずっとobnizを探し続けて、lambdaのタイムアウトまで探し続けます。ちょっと料金がもったいないですよね。

探す時間を5秒までにしてみます

Javascriptに詳しい方は`Promise.race`とか使えばいいのですが、複雑になるので別の方法で行います。


```javascript
// aws lambda code
var Obniz = require("obniz");

exports.helloHandler = function(event, context, callback) {  
 
  (async function(){    // 1) 全体を (async function(){  ... })(); で囲む

    var obniz = new Obniz("obniz_id_here");
    let connected = await obniz.connectWait({timeout:5}); // 2) onconnectをconnectWaitに変える

    if(connected){  // 3) if文でobnizがオンラインかどうかを判定する
      obniz.resetOnDisconnect(false);
      obniz.display.clear();
      obniz.display.print("Hello World");

      obniz.close();
     callback(null, "success"); 
    }else{
     callback(new Error("obniz is not online")); 
    }
  })();   // 1) 全体を (async function(){  ... })(); で囲む
  
};

```

ちょと難しくなりました。
obnizには`obniz.connectWait`という関数もあり、こちらはタイムアウトを設定できるようになっています。
この関数がasync関数なので、awaitが使えるように全体を`(async function(){  ... })();`で囲っています

そして`obniz.connectWait`の引数に`{timeout:5}`と、5秒タイムアウトを設定しています。
あとは返り値である`connected`を判定してobnizがオンラインかどうかを見ています。


## まとめ

さて、サーバーレスとobnizを連動させるときはいろいろ工夫の余地があるのがわかったでしょうか。
とりあえず動く！というところから、順番にステップアップしていければと思います。