LINEBotとobnizを繋いでお風呂をためたらいい感じにととのいそう

## 外出先からお風呂をためたい

![お風呂.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3292587/4b5afc9d-5adc-285b-d416-f76dee166305.png)


と思ったことはありませんか？
特に、休日におでかけをして、帰りが遅くなったとき
・家に着いて靴を脱いだらそのまま直行で子供をお風呂に入れたい！
・リビングで一息、なんてつかせたら小一時間は動かない！
なんてことありませんか？（我が家はよくあります）

うちのお風呂はボタンを押すと勝手にたまってくれるようになっているのですが、
今流行りのスマートホームのように外出先からスマホを使ってお風呂をためるような機能は付いておりません。
（毎日決まった時間にためてくれる予約機能は付いてますが使い勝手悪いです）

なので、LINEBotとobnizを繋いで外出先から給湯スイッチを押せる装置を作ってみました！

## 使ったもの

・obniz Board 1Y
・マイクロサーボ9g　SG－90

▼ここから下はコード②で使いました。
・超音波距離センサー　HC－SR04[101-60-142 / 20-019-100-A]
・ブレッドボード・ジャンパーワイヤ（オス－オス）10cmセット「BBJ-20」

## コード①：<br>LINEで「お風呂をためて」と送ったら給湯スイッチを押してくれる

:::note warn
'Obniz_ID'
'作成したBotのチャネルシークレット'
'作成したBotのチャネルアクセストークン'
は書き換えて下さい。
:::

<details><summary>コード①</summary><div>

```Node.js

// ########################################
// obniz処理部分
// Obniz_ID：自分のobniz ID（XXXX-XXXX）
// ########################################
const Obniz = require('obniz');
const obniz = new Obniz('Obniz_ID');
// obnizと接続確立したとき
obniz.onconnect = async () => {
obniz.display.clear();
obniz.display.print('obniz Ready');
}

// サーボを180度動かす関数
const moveServoTo180Degrees = async () => {
const servo = obniz.wired('ServoMotor', { signal: 11 });
servo.angle(180);
await obniz.wait(1000); // 1秒待機
servo.angle(null); // サーボを停止
}

// ########################################
// LINEBot イベント処理部分
// channelSecret：LINE Developers → チャネル基本設定 → チャネルシークレット
// channelAccessToken：LINE Developers → Messaging API設定 → チャネルアクセストークン（長期）
// ターミナルで ngrok http 3000 実行後、発行されたURLをWebhook URLとして設定するのを忘れずに
// 「検証」ボタンをクリックするとターミナルにエラーが出ますがここでは問題ありません（検証イベントのハンドリングをしていないため）
// ########################################
const config = {
channelSecret: '作成したBotのチャネルシークレット',
channelAccessToken: '作成したBotのチャネルアクセストークン'
};

const line = require('@line/bot-sdk');
const client = new line.Client(config);
// ExpressからMessaging APIイベントを渡されて処理するところ
const handleEvent = async (event) => {
// テキストメッセージ以外を受信したときは何も行わずresolveを返す
if (event.type !== 'message' || event.message.type !== 'text') {
return Promise.resolve(null);
}
// テキストメッセージを受信したとき
if (event.message.text === 'お風呂をためて') {
// 「お風呂をためて」と受信したとき
// サーボを180度動かす
await moveServoTo180Degrees();
// メッセージを「リプライ」で送信する
await client.replyMessage(event.replyToken, {
type: 'text',
text: 'お風呂をためますねー。'
});
} else {
// メッセージの中身が「お風呂をためて」以外だったとき
client.replyMessage(event.replyToken, {
type: 'text',
text: '「お風呂をためて」と言ってください。'
});
}
// resolveを返す
return Promise.resolve(null);
}

// ########################################
// Expressサーバー部分
// ########################################
const express = require('express');
const PORT = process.env.PORT || 3000;
const app = express();
// 「(サーバーURL)/webhook」にアクセス（LINEサーバーからのWebhook）があったとき
app.post('/webhook', line.middleware(config), (req, res) => {
// 受信したイベントをターミナルに表示
console.log(req.body.events);
// イベントをhandleEventに渡して1つずつ処理
Promise.all(
req.body.events.map(handleEvent)
).then(
result => res.json(result)
);
});
// PORT番号のポートでサーバーを開始
app.listen(PORT);
console.log('express runnning: PORT =', PORT);

```
</div></details>

## 実際の動き①

<iframe width="448" height="797" src="https://www.youtube.com/embed/v5pfuo-WIMc" title="" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen></iframe>


<br>小さくて文字が全く見えないので手元で撮影した動画も載せます
こっちもほとんど見えませんがリッチメニューを押すと「お風呂をためて」とメッセージが送られ、サーボが回り「お風呂をためますねー。」と返ってきています。

<iframe width="448" height="797" src="https://www.youtube.com/embed/6bKRE4fwI7s" title="" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen></iframe>


## これは最強のととのいIoTなる可能性あるかも？

外出先から給湯スイッチを押す装置が作れたってことは、、、
もう一回押してお湯を止める事も出来るやん！

僕はお風呂の中でこんな感じに寝そべって、耳までお湯の中に入れてボーっとするのが大好き！
（写真では少し分かりにくいですが、だいぶん寝そべっています）
だまされたと思って一回やってみて下さい！ととのうで～



<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3292587/a84c10fb-1794-9029-d0aa-1b2ed2032b30.jpeg" alt="入り方" width="400" height="10">

お風呂の中の段差のところにお尻が当たっている状態で耳がちょうどつかるぐらいが理想の水位。
我が家のお風呂は湯量を3段階で調整出来るようになっているのですが、一番少ない湯量に設定してもお気に入りの入り方をするとおぼれる！
もっと少なく！もっと早く止めたい！その方がエコだしね！

でも、どうやってお湯の量を測ろうか。。。と考えていた時にこんな記事を見つけました。

https://protopedia.net/prototype/2389

超音波センサーで水面までの距離。
温度センサーで気温、水温を測定し、LINEで通知させるという内容です。

なるほど！水面までの距離を測って、給湯スイッチを押すように出来ればいいのか！
記事の中では筒の中に発泡スチロールのような物を浮かべて恐らくそれとの距離を測っていたけど、
水そのものでも感知出来るのかコップに水を入れて実験。

・コップに何も入っていない状態＞約146mm
![水有り.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3292587/c28167fa-3d82-a283-e0a1-49aaba6d1420.jpeg)


・コップに半分ぐらい水をいれた状態＞約63mm
![水無し.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3292587/27d7d4dd-01f1-fc1e-0b6a-e47b1c04e3a8.jpeg)

**イケル！**

実際にお湯をためて測ってみたら、僕の好みの水位は浴槽のへりから-18cm（-180mm）のところでした。
お風呂がたまったら通知が来るようにしたいな。

ということでChatGPT先生と何度もやり取りして出来たのがこちらのコード。


## コード②：<br>LINEで「お風呂をためて」と送ったら給湯スイッチをON。好みの水位までたまったら給湯スイッチOFFにして「お風呂がたまりました」と通知

:::note warn
'Obniz_ID'
'作成したBotのチャネルシークレット'
'作成したBotのチャネルアクセストークン'
は書き換えて下さい。
:::

<details><summary>コード②</summary><div>

```Node.js

// ########################################
// obniz処理部分
// Obniz_ID：自分のobniz ID（XXXX-XXXX）
// ########################################
const Obniz = require('obniz');
const obniz = new Obniz('Obniz_ID');

let sendToID ;

// obnizと接続確立したとき
obniz.onconnect = async () => {
  obniz.display.clear();
  obniz.display.print('obniz Ready');

  // サーボを180度動かす関数
  const moveServoTo180Degrees = async () => {
    const servo = obniz.wired('ServoMotor', { signal: 11 });
    servo.angle(180);
    await obniz.wait(1000); // 1秒待機
    servo.angle(10); // サーボを停止
  }

  // 超音波センサーの処理を記述
  const hcsr04 = obniz.wired('HC-SR04', { gnd: 0, echo: 1, trigger: 2, vcc: 3 });

  // お風呂のたまり具合を管理する変数
  let bathtubFilled = false;

  setInterval(async function () {
    // 距離を取得
    let distance = await hcsr04.measureWait();

    // 距離(mm)をターミナルに表示
    console.log(distance + ' mm');
    console.log(sendToID);
    // obnizディスプレイに表示
    // 一度消してから距離+mmの単位を表示
    obniz.display.clear();
    obniz.display.print(distance + ' mm');

    // 距離が180mm以下かどうかの判定
    if (distance <= 180 && !bathtubFilled) {
      // サーボを180度動かす
      await moveServoTo180Degrees();
      // お風呂のたまり具合を更新
      bathtubFilled = true;
      // メッセージを「プッシュ」で送信する
      await client.pushMessage(sendToID, {
        type: 'text',
        text: 'お風呂がたまりました'
      });
    }
  }, 5000); // 5000ミリ秒 = 5秒おきに実行

// ########################################
// LINEBot イベント処理部分
// channelSecret：LINE Developers → チャネル基本設定 → チャネルシークレット
// channelAccessToken：LINE Developers → Messaging API設定 → チャネルアクセストークン（長期）
// ターミナルで ngrok http 3000 実行後、発行されたURLをWebhook URLとして設定するのを忘れずに
// 「検証」ボタンをクリックするとターミナルにエラーが出ますがここでは問題ありません（検証イベントのハンドリングをしていないため）
// ########################################
const line = require('@line/bot-sdk');
const config = {
  channelSecret: '作成したBotのチャネルシークレット',
  channelAccessToken: '作成したBotのチャネルアクセストークン'
};
const client = new line.Client(config);

// ExpressからMessaging APIイベントを渡されて処理するところ
const handleEvent = async (event) => {
  // テキストメッセージ以外を受信したときは何も行わずresolveを返す
  if (event.type !== 'message' || event.message.type !== 'text') {
    return Promise.resolve(null);
  }

  sendToID = event.source.userId;

  // テキストメッセージを受信したとき
  if (event.message.text === 'お風呂をためて') {
    // 「お風呂をためて」と受信したとき
    // お風呂のたまり具合を一旦リセット
    bathtubFilled = false;
    // サーボを180度動かす
    await moveServoTo180Degrees();
    // お風呂のたまり具合を更新
    //bathtubFilled = true;
    // メッセージを「リプライ」で送信する
    await client.replyMessage(event.replyToken, {
      type: 'text',
      text: 'お風呂をためますねー。'
    });
  } else {
    // メッセージの中身が「お風呂をためて」以外だったとき
    await client.replyMessage(event.replyToken, {
      type: 'text',
      text: '「お風呂をためて」と言ってください。'
    });
  }



  // resolveを返す
  return Promise.resolve(null);
}

// ########################################
// Expressサーバー部分
// ########################################
const express = require('express');
const app = express();
const PORT = process.env.PORT || 3000;

// 「(サーバーURL)/webhook」にアクセス（LINEサーバーからのWebhook）があったとき
app.post('/webhook', line.middleware(config), (req, res) => {
  // 受信したイベントをターミナルに表示
  console.log(req.body.events);
  // イベントをhandleEventに渡して1つずつ処理
  Promise.all(
    req.body.events.map(handleEvent)
  ).then(
    (result) => res.json(result)
  );
});

// PORT番号のポートでサーバーを開始
app.listen(PORT, () => {
  console.log(`Server is listening on port ${PORT}`);
});
}

```
</div></details>


## 実際の動き②

お風呂の現場ではどうしてもうまく動かず。。。
原因は分からないのですが、手元ではこのように動いております。
（手で超音波センサーを覆って180mm以下の状況を無理やり作っています）

<iframe width="448" height="797" src="https://www.youtube.com/embed/1m77fxr2SIs" title="" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen></iframe>

## 今後に向けて

お風呂の現場でうまく動かなかったものもちろんですが、
~~今回書いたコードだと、1回目はうまくいくのですが、2回目以降がどうしてもうまく動きません。。。~~

~~再度動かす為には一度止めて、再度走らせる必要があります。~~
~~まぁ、使い勝手はめちゃくちゃ悪いですが、個人レベルであれば一旦は使えるレベルかなと。。。~~

:::note info

「お風呂をためて」を送ったときの最初の処理でお風呂のたまり具合を管理する変数
bathtubFilledをfalse（リセット）にすることでうまく動くようになりました。

:::

その辺りを検証していきたいのと、友人からも
**・お風呂は水分が多い場所なので、超音波センサーではなく、水位センサーの方が良い
・温度センサーを家の外まで引っ張って、外気温に合わせて適温のお湯がためられればよりととのう
・スイッチを押してからの時間で制御も出来そう**
などなどフィードバックをいただいたので、色々と検討していきたいと思います。

**モノとインターネットが目の前で繋がる**のがとにかく楽しかったので、今回はとりあえず手元にある物で試してみましたが、可能性は無限大ですね。


最後になりましたが、今回もChatGPT大先生には多大なるご協力（ほぼ全て）を頂きましたこと、感謝申し上げます。

ここまでお読みいただいたみなさまもありがとうございました！