node.jsでswitchbotの温湿度計データ取得

switchbotの[温湿度計](https://www.amazon.co.jp/dp/B07L4QNZVF)を購入したので、node.jsでデータを取得し、s3にアップロードしてみた。

# データの取得
データの取得には[noble](https://github.com/noble/noble)を使用した。
nodeのv9以降では動かないようなので、v8系をインストールすること。
nobleの使い方は割愛。

microbotの開発元？のOpenWonderLabsがAPI仕様を公開しているので、それを参照しながらデータを読み取る。
https://github.com/OpenWonderLabs/python-host/wiki/Meter-BLE-open-API
ビットの操作はとっても苦手なので、苦労した。
例えば、4バイト目の後ろ7ビットには温度のデータが入っているようなので、このようにして取得。

```js
const temperature = data.readUInt8(4) & 0x7f;
```
&（ビット演算子）で0x7f(1111111)をビットANDして、後ろ7桁を切り出すらしい。

前3桁が欲しかったら、こんな感じで0xE0(11100000)をビットANDして、
さらにシフト演算子 `>>>` （0埋めバージョン）で後ろ5桁を溢れさせる。

```js
var a = data.readUInt8(4) & 0xE0 >>> 5
```

web開発ばっかりやってた人間としては、このあたりはとっても苦手な領域。


データが取れたら `fs` でファイルに書き出す。
後で扱いやすいように `moment.js` を使って日時分秒をファイル名にした。

↓ソース全体(scan.js)

```js
'use strict';

const noble = require('noble');
const fs = require('fs');
const moment = require('moment');

// living swithbot
const MAC = "xxxxxxx"; // switchbotのmacアドレス

const tmpDir = "/var/tmp/envdata/"; // データを保存したいディレクトリ

noble.on('stateChange', (state) => {
    if (noble.state === 'poweredOn'){
        noble.startScanning();
    } else {
        noble.on('stateChange', scanStart);
    }
});

noble.on('scanStart', () => {
    console.log('start scanning');
});

noble.on('scanStop', () => {
    console.log('stop scanning');
    process.exit(0);
});

noble.on('discover', (peripheral) => {
    if (peripheral.uuid === MAC) {
        noble.stopScanning();

        const data = peripheral.advertisement.serviceData[0].data;
        const deviceType = data.slice(0, 1).toString('ascii');
        const battery = data.readUInt8(2) & 0x7f;
        const temperature = data.readUInt8(4) & 0x7f;
        const humidity = data.readUInt8(5) & 0x7f;
        const time = moment().format('YYYY-MM-DD_HH:mm:ss');

        const jsonData = {
            "datetime": time,
            "battery": battery,
            "temperature": temperature,
            "humidity": humidity
        };

        // write to file
        fs.writeFileSync(tmpDir + time + '.log', JSON.stringify(jsonData));
        
        noble.stopScanning();
    }
});
```


# s3へのアップロード

s3へのアップロードはいたって簡単です。

### 下準備
`aws-sdk` のライブラリを `npm install` する。
AWSのIAMでアップロード用のユーザ作って`AmazonS3FullAccess`のポリシー（uploadだけだったら権限絞ったポリシーで良い気もする）付けて、そのcredential情報をjsonファイルに書き出しておく。

```json
{
  "accessKeyId": "xxxxxxxxxxxxx",
  "secretAccessKey": "xxxxxxxxxx"
}
```
こんな具合。

### S3への接続
こんな感じでs3オブジェクトをセットアップ。

```js
AWS.config.loadFromPath('/home/pi/.aws/credential.json'); // 保存したcredentialファイルのパスを指定
AWS.config.update({region: 'ap-northeast-1'});

const s3 = new AWS.S3();
```

あとはアップロードしたいファイルの情報を整えて `s3.putObject()` でアップロード。

↓ソース(upload.js)

```js
const AWS = require('aws-sdk');
const fs = require('fs');

AWS.config.loadFromPath('/home/pi/.aws/credential.json');
AWS.config.update({region: 'ap-northeast-1'});

const s3 = new AWS.S3();
const tmpdir = '/var/tmp/envdata/';


fs.readdir(tmpdir, (err, files) => {
  if (err) throw err;

  files.forEach( (file) => {
    var params = {
      Bucket: 'switchbot-room',
      Key: file
    };

    params.Body = fs.readFileSync(tmpdir + file);
    s3.putObject(params, (err, data) => {
      if (err) throw err;
      fs.unlinkSync(tmpdir + file, (err) => {
        if (err) throw err;
      });
    });
  });
});
```

# 自動起動設定
cron設定するだけ。

```shell
 */5 * * * * /usr/local/bin/node /home/pi/swithbot/scan.js
 */5 * * * * /usr/local/bin/node /home/pi/swithbot/upload.js
```
5分間隔でデータを取得して、アップロード。

今のところ問題なくデータが上がってます。

# NextAction
次は、上がったデータを可視化したい。
QuickSight, Mackerel, ES + Kibana どれでやろうか検討中。chart.js使って自分で作ってもいいけど、
一番費用が掛からない方法がいいな。