玄関開けて５秒でLINE通知（obniz）



# はじめに

「現実をソフトウェア化する」でおなじみの [obniz](https://obniz.io/ja/) とLINE BotとAPIを繋げて何かを作ります。

今回の前提条件は「obniz」「LINE」「何かしらのWebAPI」の使用すること。

# 参考にするサイト

- [1時間でLINE BOTを作るハンズオン (資料+レポート) in Node学園祭2017 #nodefest](https://qiita.com/n0bisuke/items/ceaa09ef8898bee8369d)
- [obniz公式ドキュメント](https://obniz.io/ja/doc/root)



## 作りたいもの

- Iotといったらやはりスマート家電
- リビングに人がいるかいないか検知して電気を自動ON/OFFさせたりとか
- 外出を検知して天気予報とか外で使える情報を送信したりとか
- 定刻、またはリモートで猫にご飯をあげたりとか（猫は飼ってないけど）

今回はLINEを使う、WebAPIも使用する、という条件なので外出検知あたりが妥当だなあと思いそれに決定。

イメージとしては・・・
外出するときに天気情報を取得して、今日の天気と傘が必要かどうか**LINEで通知**してあげて、
帰宅したらLEDやディスプレイで「おかえり」を過剰に表現させます。


## センサーを動かす

使ったもの：[超音波センサーhc-sr04](https://www.amazon.co.jp/exec/obidos/ASIN/B004U8TOE6/shangtioan-22/)

obniz公式ドキュメントに詳しい使い方からサンプルコードまで載ってます、ありがてー
https://obniz.io/ja/sdk/parts/HC-SR04/README.md

このセンサーを玄関前の棚において、玄関の扉までの距離を取得。
初期の距離をデフォルトとして保持しておく。
扉が開いて距離が大きくなったら、その後しばらくデフォルトより距離が短くなることがないなら「退出」したと判断。
デフォルトより距離が短くなる（人が玄関に入ってきた）ならば「入室」と判断。

距離の取得は`500ms`ごとに取得したり判断する想定

```javascript

// デフォルト距離を「init_distance」に格納

const moniterDistance = async () => {
  hcsr04.measure(distance => {
    let distance_num = Number(distance);
    nowFormatted = new Date().toFormat('YYYY/MM/DD HH24:MI');

    if (distance_num > init_distance) count++;
    else count = 0;

    // 扉が開いたかチェック
    if (count > 3) {
      count = -50;
      client.pushMessage(staticUserId, {
        type: 'text',
        text: `扉が空いたみたいよ (${nowFormatted})`
      });
      theThing.on();
      setTimeout(() => {
        theThing.off();
      }, 10000);
    }
    // しばらくたってもまだ開いてたら警告
    else if (count == -20) {
      count = -50;
      client.pushMessage(staticUserId, {
        type: 'text',
        text: `まだ扉が開きっぱなしだけど大丈夫・・・？ (${nowFormatted})`
      });
    }

    distance_cache = distance_num;
  });
  console.log('Done: moniterDistance');
};
setInterval(moniterDistance, 500);
```

（「これで大丈夫っしょ」と思っていましたが後々ダメになります）


## 蛇足機能

Botに送信したメッセージをobnizのdisplayに表示させます。
家族が帰宅したことを通知されたら、`おかえり！冷凍庫にアイスあるよ`とか表示させれたらアッタカイなあ・・・と思いまして。
~~（LINEメッセージで良くない？）~~

```javascript
// 送信したメッセージをobnizのdisplayに表示
const sendPushMessage = async (userId, m) => {
  canvas = createCanvas(128, 64);
  ctx = canvas.getContext('2d');
  ctx.fillStyle = 'white';
  ctx.font = '18px Avenir';
  ctx.fillText(m, 0, 40);
  obniz.display.clear();
  obniz.display.draw(ctx);
  theThing.on();
  setTimeout(() => {
    theThing.off();
  }, 2000);
  setTimeout(() => {
    obniz.display.clear();
  }, 10000);
  console.log('Done: sendPushMessage');
};
```

<br>

今の距離を取得してLINEで連絡します。

```javascript
// 今の距離を表示
const pushNowDistance = async () => {
  hcsr04.measure(distance => {
    let distance_num = Number(distance);
    client.pushMessage(staticUserId, {
      type: 'text',
      text: `今の距離は ${distance_num} mm だよー`
    });
    distance_cache = distance_num;
  });
};
```



## 外出時に天気情報を取得してLINEで通知

天気情報は[OpenWeatherMap](https://openweathermap.org/)を使用。
[このQiita記事](https://qiita.com/nownabe/items/aeac1ce0977be963a740)を参考にしています。

<details><summary>**コード（クリックで展開）**</summary><div>

```javascript
// 天気取得
const getWeather = async () => {
  let res = await axios
    .request(openweathermapConfig)
    .then(function(res) {
      console.log('天気: ', res['data'].list[0].weather[0].icon);
      sendWeather(res['data'].list[0].weather[0].icon);
    })
    .catch(function(err) {
      console.log(err);
    });
};

//天気情報を日本語に変換しつつpushMessage
const sendWeather = async weather => {
  if (weather == '01n' || weather == '01d') {
    weather_text = '快晴';
    rain_info = '今日は傘を持つ必要ないね';
  }
  if (weather == '02n' || weather == '02d') {
    weather_text = '晴れ';
    rain_info = '今日は傘を持つ必要ないね';
  }
  if (weather == '03n' || weather == '03d') {
    weather_text = '曇り';
    rain_info = '今日は傘を持つ必要ないね';
  }
  if (weather == '04n' || weather == '04d') {
    weather_text = '曇り';
    rain_info = '今日は傘を持つ必要ないね';
  }
  if (weather == '09n' || weather == '09d') {
    weather_text = '小雨';
    rain_info = '今日は傘を持ったほうが良いと思うよ';
  }
  if (weather == '10n' || weather == '10d') {
    weather_text = '雨';
    rain_info = '今日は傘を持ったほうが良いと思うよ';
  }
  if (weather == '11n' || weather == '11d') {
    weather_text = '雷雨';
    rain_info = '今日は傘を持ったほうが良いと思うよ';
  }
  if (weather == '13n' || weather == '13d') {
    weather_text = '雪';
    rain_info = '今日は傘を持ったほうが良いと思うよ';
  }
  if (weather == '50n' || weather == '50d') {
    text = '霧';
    rain_info = '雨に変わるかもしれないし念のため傘持って行こう';
  }
  console.log(weather_text, rain_info);

  client.pushMessage(staticUserId, {
    type: 'text',
    text: ` 今日の天気は「${weather_text}」\n ${rain_info}`
  });
};
```
</div>
</details>


## 問題が・・・

リビングで動作確認できたので、実際に超音波センサーを玄関に設置しいざ計測してみると玄関の扉を開いときの距離が正常に取得できていない🤮
調べると外の喧騒音や光の所為やらで上手くは取れなくなるっぽい、「超音波」なので当たり前だがいかんせん素人ですので常識も分かっておらず・・・

## 予定変更

[こちらのサイトを参考にして](http://www.shangtian.tokyo/entry/2017/04/09/001524) 検知した物体の移動方向で入退室を判断するように修正


```javascript
    let sub = distance_cache - distance_num;
    if (sub < -100) { //10cm 以下の差異は誤差
      count02++;
    } else if (sub > 100) {
      count03++;
    } else {
      count02 = 0;
      count03 = 0;
    }

    if (count02 > 2) {
      count02 = -40;  //連続で検知しないように適当な数値
      // 外に出る時の処理
    }

    if (count03 > 2) {
      count03 = -40;  //連続で検知しないように適当な数値
      // 外から帰ってきた時の処理
    }

    distance_cache = distance_num; //monitorの最後
```


## 動いたー

**動作確認映像**
https://www.youtube.com/watch?v=eX0I3PnQpPY

**概念図**
![20190626.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/178043/31d4029d-f951-a9c3-3672-d9950d9d5ec8.jpeg)

## 改善点

- もっと適したセンサーがあるはず
- センサーの設置位置が重要なのでケースとか固定方法とかをしっかりしたい
- 光センサーも組み合わせればさらに精度が上がったかも