MonacaとJINS MEMEでサンタさんを捕獲する

:christmas_tree:[Monaca Advent Calendar 2017](https://qiita.com/advent-calendar/2017/monaca):christmas_tree: 4日目の記事です。

クリスマスイブの夜、サンタさんが来るところを見張っていようと思って夜更かしに挑戦するも、いつの間にか寝てしまった…そんな経験はありませんか？
今年こそは寝過ごさないようにするためのアプリをMonacaとメガネ型ウェアラブルデバイスJINS MEMEで作成してみました。

# 環境作成とサンプルプロジェクト
以下のチュートリアルから確認できます。
[JINS MEME SDK - Monaca(Cordova) - チュートリアル](https://jins-meme.github.io/sdkdoc/monaca/tutorial.html)

# 傾き具合に応じてバイブレーションするアプリの実装
上記のサンプルプロジェクトをベースに、JINS MEMEで身体の傾きを取得し、眠りそうな状態になったらスマホのバイブレーションで起こすアプリを作成してみます。
傾きを取るだけならスマホの加速度センサーでも良いのでは？と思うのかもしれませんが、たとえば座ったまま首だけ下に傾けて寝てしまった場合、胸ポケットに入っているスマホでは傾きを検知することができません。
JINS MEMEはメガネ型という特性上、センサーが頭部に固定されるためより正確な傾きを取得することができます。


```html:index.html
<!DOCTYPE HTML>
<html>
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
  <meta http-equiv="Content-Security-Policy" content="default-src * data: gap: https://ssl.gstatic.com; style-src * 'unsafe-inline'; script-src * 'unsafe-inline' 'unsafe-eval'">
  <script src="components/loader.js"></script>
  <script src="lib/onsenui/js/onsenui.min.js"></script>

  <link rel="stylesheet" href="components/loader.css">
  <link rel="stylesheet" href="lib/onsenui/css/onsenui.css">
  <link rel="stylesheet" href="lib/onsenui/css/onsen-css-components.css">
  <link rel="stylesheet" href="css/style.css">

  <style>
    #icon-body {
        transition-property: transform;
        transition-duration: 0.1s;
    }
    .dialog-container{
        height: 500px;
        overflow-y: scroll;
    }
  </style>
  <script>
    // 起動時のイベント
    document.addEventListener('deviceready', function() {
        // アプリの初期化処理
        cordova.plugins.JinsMemePlugin.setAppClientID(
            'アプリID',
            'アプリSecret',
            function() {
                restartScan();
            },
            function() {
                console.log('Error: setAppClientID');
            }
        );
    });
    
    // デバイスのスキャン開始
    function startScan() {
        // デバイス選択ダイアログを表示
        var deviceList = document.getElementById('deviceList');
        deviceList.innerHTML = '<ons-list-header>デバイスを選択</ons-list-header>';
        document.getElementById('selectDeviceDialog').show();
        
        cordova.plugins.JinsMemePlugin.startScan(function(device) {
            // ダイアログにデバイスを追加
            deviceList.innerHTML += "<ons-list-item tappable onclick=\"connect('" + device + "')\">" + device + "</ons-list-item>";
        }, function() {
            console.log('Error: startScan');
        });
    }
    
    // デバイスのスキャン停止
    function stopScan(successCallback) {
        cordova.plugins.JinsMemePlugin.stopScan(function() {
            if(successCallback) successCallback();
        }, function() {
            console.log('Error: stopScan');
        });
    }
    
    // デバイスのスキャン再開
    function restartScan() {
        stopScan(startScan);
    }
    
    // アプリとデバイスの接続
    function connect(device) {
        // スキャン停止
        stopScan();
        // ダイアログを閉じてモーダルを表示
        document.getElementById('selectDeviceDialog').hide();
        document.getElementById('modal').show();
                
        // 選択されたデバイスに接続
        cordova.plugins.JinsMemePlugin.connect(device, function() {
            document.getElementById('modal').hide();
            startDataReport();
        }, function() {
            console.log('Disconnect');
        }, function() {
            console.log('Error: connect');
            document.getElementById('modal').hide();
        });
    }
    
    // アプリとデバイスの切断
    function disconnect() {
        cordova.plugins.JinsMemePlugin.disconnect(function() {}, function() {
            console.log('Error: disconnect');
        });
    }
    
    // 計測開始
    function startDataReport() {
        var currentTime, lastTime = new Date();
        cordova.plugins.JinsMemePlugin.startDataReport(function(data) {
            currentTime = new Date();
            var diff = (currentTime.getTime() - lastTime.getTime()) / 1000;
            // 3秒おきに傾き判定
            if(diff > 3) {
                lastTime = currentTime;
                draw(data);
            }
        }, function() {
            console.log('Error: startDataReport');
            document.getElementById('modal').hide();
        });
    }
    
    // 計測結果を描画する
    function draw(data) {
        document.getElementById('roll').textContent = data.roll;
        document.getElementById('pitch').textContent = data.pitch;
        
        // 前に45度、左右に45度傾いたら危険域
        if(data.pitch > 45 || Math.abs(data.roll) > 45) {
          document.getElementById('msg').style.display = "block";
          setTimeout(function() {
              document.getElementById('msg').style.display = "none";
          }, 800)
          // 振動で起こす
          navigator.vibrate(800);
        }
    }

    // 計測停止
    function stopDataReport() {
        cordova.plugins.JinsMemePlugin.stopDataReport(function() {}, function() {
            console.log('Error: stopDataReport');
        });
    }
    
    // プラグアイコン押下時
    function reconnect() {
        ons.notification.confirm("再接続しますか？")
        .then(function(result) {
           if(result) {
               restartScan();
           }
        });
    }
  </script>
</head>
<body>
  <ons-page>
    <!-- ツールバー -->
    <ons-toolbar>
      <div class="center">JINS MEME</div>
      <div class="right">
        <ons-toolbar-button>
            <ons-icon icon="plug" size="24px" onclick="reconnect()"></ons-icon>
        </ons-toolbar-button>
      </div>
    </ons-toolbar>

      <div style="text-align: center;">
        <h2>roll</h2>
        <div id="roll"></div>
        <h2>pitch</h2>
        <div id="pitch"></div>
        <div id="msg" style="display:none">起きろ！</div>
      </div>

  </ons-page>
  
  <!-- デバイス選択ダイアログ -->
  <ons-dialog id="selectDeviceDialog">
    <ons-list id="deviceList">    
    </ons-list>
  </ons-dialog>
  
  <!-- モーダルウィンドウ -->
  <ons-modal id="modal">
    <p>接続中...</p>
    <ons-icon icon="spinner" size="28px" spin></ons-icon>
  </ons-modal>
      
</body>
</html>
```
チュートリアルからの変更箇所は2か所です。

まず1つ目は、`startDataReport()` 関数内の処理です。
JINS MEMEの計測データを取得する `cordova.plugins.JinsMemePlugin.startDataReport()` はリアルタイムにデータを取得するため、数十ミリ秒間隔でコールバック関数が定期実行されます。
バイブレーションさせている間は何も処理をしたくないので、3秒おきに傾き判定を行うようにしています。

2つ目は、`drow()` 関数内の処理です。
左右に45度、前に45度傾いたら眠ってしまったと判定して、バイブレーションを実行しています。
なお、Cordovaの `navigator.vibrate()` は引数に指定された時間（ミリ秒）振動させますが、iOSの場合は引数が無視され、システム側の設定時間が採用されます。

# デモ動画
<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">めざましあぷり <a href="https://t.co/JCA5GHV4ds">pic.twitter.com/JCA5GHV4ds</a></p>&mdash; coboco (@_coboco) <a href="https://twitter.com/_coboco/status/939065751830347776?ref_src=twsrc%5Etfw">December 8, 2017</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>


なお、本アプリはクリスマス終了後は年越しの瞬間まで起きているためのアプリとしてご利用いただけます。