見出し画像

オートロックのアルゴリズムを考えてみる

考えてみる、と書いたが、実際にはすでに実装済みで、以前の投稿で紹介した自宅サーバーの一機能です。
今回は、スマートロックと開閉センサーを組み合わせて実装したそのオートロックのプログラムについて書きます。何かの参考になったら幸いです。

経緯

スマートロック単体でもオートロックの機能はあるのですが、それは単純に設定時間経過したら施錠するというものです。
毎朝、デイサービスが母を迎えに来るのだけど、車椅子なので出るまでに時間がかかり、その間ドアが開きっぱなしとなるため、スマートロック単体のオートロックは長めに10分に設定されています。
それでも良いのだけど、開閉センサーと組み合わせて、ドアが閉まったらオートロックするということができないか?と思い、作成してみたものが今回のプログラムになります。

構成

スマートロックはSesame3、開閉センサーはSwitchbotです。

IFTTTを使って、Switchbotの開閉センサーの開閉のイベントをBeebotte経由で自宅サーバーに通知しています。そのあたりの仕組みは説明すると長くなるので、それはまた機会があれば、書きたいと思います。

アルゴリズム

簡単な流れとしては、ドアが閉じたら一定時間後施錠する、となります。それに加えて、一定時間待機中にドアが開いたら処理を中止するというのも必要です。通常は起こりませんが、処理中「閉じる」が再度通知された場合も考慮した方が良いでしょう。そのときは新たにこの一連の処理が起動することになりますので、新たに発生した方を中止するか、今処理しているものを中止するかになるかと思いますが、今回は今処理しているものを中止することとしました。
フローチャートで書くと以下のようになるかと思います。

よく見てみると、時間経過、「ドア開」、「ドア閉」のいずれか早いものでその後の処理が決まります。従って、最初に終わったものに対応した処理を行ない終了するだけでオートロックを実現できそうです。

コーディング

と、いうことで、書いたコードが以下です。実際はもっといろいろ書いているけど(ログ出力など)、長くなるのでちょっとまとめました。

// オートロック制御
mqtt.on("door.closed", () => {
  Promise.race([
    (async () => {
      if (that.settings.autolock > 0) {
        await misc.delay(that.settings.autolock * 1000);
        const res = await getStatus();
        if (res && res.status == 200 && res.data && res.data.CHSesame2Status == "locked") {
        } else {
          return () => {
            lock();
        };
      }
    })(),
    events.once(mqtt, "door.opened").then(() => () => {}),
    events.once(mqtt, "door.closed").then(() => () => {}),
  ]).then((fn) => {
    if (fn) fn();
  });
});

プログラムの他の部分を載せていないので補足すると、mqttはこのシステムにあるモジュールでMQTTで受信した内容に従ってイベントを発生させるものです。"door.opened"という名前のイベントがドアが開いた時、"door.closed"という名前のイベントがドアが閉じた時に発生します。getStatus()は施錠状態の取得、lock()は施錠の要求処理となります。misc.delay()は指定時間ウェイトする関数です。
また、フローチャートでは「時間経過」とだけ書いていますが、実際のコーディングでは、時間経過後、施錠状態を確認する処理も入れています。

解説

Promise.race()は指定した複数の非同期処理で最も早く終了した関数の戻り値を戻り値として受け取ります。
ここでは後続の処理を行う関数を戻り値として返しています。設定時間経過後の処理なら戻り値として施錠を要求する関数を返していますし、ドア開、ドア閉のイベント発生時は何もしない関数を返しています。

events.once()はそのイベントの発生まで待つPromiseを返します。
上記のコードでは、ドア開、ドア閉のイベントをそれぞれ待った後、戻り値を戻しています。

従って、時間経過(+施錠確認)が一番早く終われば、施錠処理する関数が、ドア開あるいはドア閉のイベントの発生の方が早く発生すれば、何もしない関数が、Promise.race()の戻り値になります。その戻り値を呼び出すと処理が完了、ということになります。

おわりに

今回は、オートロックのプログラムについて書かせていただきました。いかがだったでしょうか。あまりうまく説明できなかった感じがあるので、どこまで伝わったか微妙ですが、もしも何かの参考になったなら嬉しいです。

今回の記事を書いているときに見つけましたが、Sesame4とか出てるんですね。どこかSesame3とは変わったのかなぁ。

では、また。

この記事が気に入ったらサポートをしてみませんか?