LINE BOTでラズパイから部屋の温度を教えてもらう【Push API】【Node.js】

#### 前回できたこと
+ LINEのReplyAPIを利用したおうむ返しBot
+ ラズパイ、DHT11を利用し温湿度データをBotで返信

前回記事を参照
[RaspberryPi3を使ってLINE BOTに部屋の温度を教えてもらう 【node.js】](https://qiita.com/mochifuture/items/16085eca1d49ab9effaa)

#### 前提条件
+ LINE Messaging APIのDeveloper Trialのアカウント（またはビジネスプラン）を利用

フリープランだとPushAPIは使えないようです
[MessagingAPIで利用できる料金プラン](https://at.line.me/jp/plan)


# 今回の作業内容
1. PUSH通知してみる
2. 温湿度を通知してみる
3. 温湿度を常に監視するため、プログラムを繰り返し実行させる(node-cronのインストール）

node-cronは定時通知（月曜日に通知、毎日朝7時に通知など）にも使えるよ！
【所要時間 30分】


> クロン / クーロン / クローン
> cron 【 crond 】 crontab
> cronとは、UNIX系システムの常駐プログラム(デーモン)の一種で、ユーザの設定したスケジュールに基づいて指定したコマンドやシェルスクリプトなどを自動実行してくれるプログラム。
> 実行スケジュールは毎時、毎日、毎週、毎月をはじめ細かく指定でき、「crontab」というファイルに記述・保存しておく。crond(cron daemon)というプロセスがcrontabの内容に従って、決められた日時にコマンドなどを実行してくれる。 -- IT用語辞典より

# 1. PUSH通知してみる

作業ディレクトリ構成はこうなる予定です（詳細は前回参照）

```shell-session
workspace/mylinebot $ ls
cron.js  dht.js  node_modules  package-lock.json  package.json  plugins  server.js
```

### 送り先の確認
ID、グループを指定できます。今回は自分のIDに送ります。
LINE Developersのコンソール画面より自分のIDを確認
https://developers.line.me/
<img width="640" alt="id.png" src="https://qiita-image-store.s3.amazonaws.com/0/258078/284d0564-9cf1-3713-ff3b-99ab49d48df4.png">

Botのチャンネル設定の一番下、Uから始まる文字列が自分のIDです
<img width="640" alt="id2.png" src="https://qiita-image-store.s3.amazonaws.com/0/258078/46a82ad3-2b15-aa70-b124-54ba42775de5.png">


### server.jsの編集
line-bot-sdk-nodejsを利用するので簡単！
client.pushMessage()を利用します

```js
client.pushMessage('user_or_group_or_room_id', {
  type: 'text',
  text: 'hello, world',
})
```
参照元: https://github.com/line/line-bot-sdk-nodejs/blob/master/docs/pages/api-reference/client.md#pushmessageto-string-messages-message--message-promiseany

```js:server.js
const express = require('express');
const line = require('@line/bot-sdk');
const PORT = process.env.PORT || 3000;

const config = {
    channelAccessToken: '',
    channelSecret: ''
};

const app = express();

app.post('/webhook', line.middleware(config), (req, res) => {
    console.log(req.body.events);
    Promise
      .all(req.body.events.map(handleEvent))
      .then((result) => res.json(result));
});

const client = new line.Client(config);

client.pushMessage('送信先ID', {
  type: 'text',
  text: 'hello, world',
})

// 以下省略
```
### nodeで実行してみる

```shell-session
$ node server.js
```

LINEにメッセージが届いた！
<img width="375" alt="IMG_6183.jpg" src="https://qiita-image-store.s3.amazonaws.com/0/258078/75924fa2-af90-c4e1-bc40-786c64d3295b.jpeg">

# 2. 温湿度を通知してみる
ログ出力のためmoment-timezoneをインストールしました

```shell-session
$ npm install moment-timezone
```

server.jsの編集

```js:server.js
const express = require('express');
const line = require('@line/bot-sdk');
const PORT = process.env.PORT || 3000;
const moment = require('moment-timezone');

const config = {
    channelAccessToken: '',
    channelSecret: ''
};

const app = express();

app.post('/webhook', line.middleware(config), (req, res) => {
    console.log(req.body.events);
    Promise
      .all(req.body.events.map(handleEvent))
      .then((result) => res.json(result));
});

const client = new line.Client(config);

// pushMessageを送る処理
function sendPushMessage(mes) {
   client.pushMessage('送信先ID', {
    type: 'text',
    text: mes,
   })
   console.log(moment().tz("Asia/Tokyo").format() + ' 送信完了：push message');
}

const readout = dht.getData();
console.log(readout);

const mes = '室内の温度は'+readout.temperature.toFixed(1)+'℃、'+'湿度は'+readout.humidity.toFixed(1)+'%です。'
sendPushMessage(mes);

// replyMessageを送る処理
function handleEvent(event) {
  if (event.type !== 'message' || event.message.type !== 'text') {
    return Promise.resolve(null);
  }

  var mes = '';
    mes = event.message.text;

  if(event.message.text === 'いまなんど') {
    // mes = dht.getData();
    const readout = dht.getData();
    mes = '室内の温度は'+readout.temperature.toFixed(1)+'℃、'+'湿度は'+readout.humidity.toFixed(1)+'%です。'
  } else {
    mes = event.message.text;
  }

  return client.replyMessage(event.replyToken, {
    type: 'text',
    text: mes
  });
}
```
dht.jsの変更
前回はメッセージを返していましたが、データをそのまま返すように変更しました

```js:dht.js
const sensorLib = require('node-dht-sensor');

const sensor = {
    sensors: [ {
        name: "DHT11",
        type: 11,
        pin: 4
    } ],
    read: function() {
        // 前回はメッセージを返していましたが、データをそのまま返すように変更
        const readout = sensorLib.read(this.sensors[0].type, this.sensors[0].pin);
        // return '室内の温度は'+readout.temperature.toFixed(1)+'℃、'+'湿度は'+readout.humidity.toFixed(1)+'%です。';
        return readout;
    }
}

exports.getData = function() {
    try {
        return sensor.read();
    } catch (error) {
        return '不明です';
    }
}
```

nodeで実行してみる

```shell-session
$ node server.js
```

LINEにメッセージが届いた！
<img width="696" alt="スクリーンショット 2018-05-18 午後1.29.44.png" src="https://qiita-image-store.s3.amazonaws.com/0/258078/95d59547-9801-1ae1-01df-995b467d9a29.png">


# 3. 温湿度を常に監視するため、繰り返し実行させる(node-cronのインストール）
### node-cronのインストール

```shell-session
$ npm install node-cron
```

node-cron https://www.npmjs.com/package/node-cron

### cron.jsを作成

```shell-session
$ nano cron.js
```

```js:cron.js
var mycron = require('cron').CronJob;

var task = new mycron({
  cronTime: '* * * * * *', //毎秒実行
  onTick: function() {
    console.log('毎秒実行');
    }
  },
  start: true, // newした後即時実行するかどうか
});

task.start();
```
参照記事：http://chkkchy.hatenablog.com/entry/2014/08/07/201314

cron.jsを実行

```shell-session
$ node cron.js
```

ログが毎秒出るのを確認！
<img width="465" alt="ontick.png" src="https://qiita-image-store.s3.amazonaws.com/0/258078/00c5f64d-cabc-58c3-a6c2-60b2b50fbead.png">

cron.jsの編集（実際の処理）

```js:cron.js
var mycron = require('cron').CronJob;
const server = require('./server');
const dht = require('./dht');

var task = new mycron({
  cronTime: '*/10 * * * * *', //毎10秒実行
  onTick: function() {
    const readout = dht.getData();
    console.log(readout);
    // 一定温度を超えていたらアラート通知
    if ((readout.temperature > 25) || (readout.temperature < 19)){
      const mes = '室内の温度は'+readout.temperature.toFixed(1)+'℃、'+'湿度は'+readout.humidity.toFixed(1)+'%です。'
      server.sendPushMessage(mes);
    }
  },
  start: true,
});

task.start();
```
```js:server.js
// 追記
const cron = require('./cron');

// ...省略

const client = new line.Client(config);

// pushMessageを送る処理
function sendPushMessage(mes) {
   client.pushMessage('送信先ID', {
    type: 'text',
    text: mes,
   })
   console.log(moment().tz("Asia/Tokyo").format() + ' 送信完了：push message');
}

// replyMessageを送る処理
function handleEvent(event) {
  if (event.type !== 'message' || event.message.type !== 'text') {
    return Promise.resolve(null);
  }

  var mes = '';
    mes = event.message.text;

  if(event.message.text === 'いまなんど') {
    // mes = dht.getData();
    const readout = dht.getData();
    mes = '室内の温度は'+readout.temperature.toFixed(1)+'℃、'+'湿度は'+readout.humidity.toFixed(1)+'%です。'
  } else {
    mes = event.message.text;
  }

  return client.replyMessage(event.replyToken, {
    type: 'text',
    text: mes
  });
}
```

server.jsを実行

```shell-session
$ node server.js
```

LINEにメッセージがたくさん届くよ！（毎10秒。上記コードだと室温が20~25度だと届きません。）
<img width="375" alt="IMG_6185.jpg" src="https://qiita-image-store.s3.amazonaws.com/0/258078/990ca990-330a-8d3c-3b96-c7fe66bd8ada.jpeg">

ちなみに
毎10秒 `cronTime: '*/10 * * * * *'`
毎2分 `cronTime: '*/2 * * * *'`

毎日朝7時に通知など、node-cronは定時通知にも使えます
詳しくはライブラリのREAD MEを参照してください

### もしnpmのインストールで怒られたら

```shell-session
$ sudo npm install --unsafe-perm
```
`--unsafe-perm`を追加するとうまくいくかも！
参照記事　https://github.com/nodejs/node-gyp/issues/454

# 次回への課題
+ ラズパイにSSH接続しているMacがスリープすると、プログラムが終了する
  + →ラズパイ内で自動実行の永続化が必要
+ ラズパイのセキュリティ