廊下の電気を自動消灯 ─ SwitchBot照度センサー+誤動作防止の連続検知ロジック

Raspberry Pi

廊下の電気をつけっぱなしにして寝てしまう……そんな省エネの悩みを Raspberry Pi と SwitchBot で解決しました。
ただし単純に「明るかったら消す」という実装では、誰かが廊下を通過した瞬間にも誤って消灯してしまいます。
本記事では、SwitchBot の照度センサーを使いながら「一定時間連続して明るい状態が続いた場合だけ消灯」する連続検知ロジックと、InfluxDB のデータが古い場合に備えた直前 API 確認による二段階の誤動作防止策を解説します。

なぜこの機能を作ったのか?

廊下の照明は誰かが通るたびにつけるものですが、そのままにして別の部屋に行ってしまうと消し忘れが発生しがちです。
モーションセンサー連動のダウンライトのような設備を新設するのはコストがかかるため、すでに設置している SwitchBot の照度センサーと SwitchBot Bot(物理的にスイッチを押すデバイス)を組み合わせてソフトウェアで解決することにしました。
最初はシンプルに「照度センサーが明るい → 消灯」と実装しましたが、廊下を通過しただけでも消えてしまう誤動作が頻発したため、連続検知ロジックを導入した経緯があります。

廊下の電気のつけっぱなしをなくしたい!でも人がいるときに誤って消えてしまうのも困る!

実現したいこと

  • 廊下の照明が一定時間つけっぱなしになったら自動で消灯する
  • 誰かが廊下を通過しただけで誤消灯しないよう、連続検知ロジックで誤動作を防ぐ
    • 「明るい」状態が一定回数連続して続いた場合のみ消灯する
  • 消灯直前に SwitchBot API を直接叩いて最新状態を再確認し、DB の古いデータによる誤動作も防ぐ
  • 廊下を長時間使用する場合(掃除・洗濯など)は外部から自動消灯を一時無効にできる
  • Raspberry Pi 上で systemd サービスとして常時稼働する

この記事でわかること

  • SwitchBot の照度センサー(コンタクトセンサー)から明暗の状態を取得する方法
  • 連続検知カウンターを使って誤動作を防ぐロジックの設計と実装
  • InfluxDB に蓄積したセンサーデータを参照しつつ、消灯直前に SwitchBot API で最新状態を確認する二段階確認の実装
  • SwitchBot Bot API を使って物理的なスイッチを押すコマンドを送る方法
  • 外部から自動消灯を一時無効にする「長時間点灯モード」の実装

必要な準備と用意するもの

ハードウェア
  • Raspberry Pi(例:Raspberry Pi 5)
  • SwitchBot コンタクトセンサー(照度センサー内蔵)
    • 明るさを “bright” / “dark” で返す
  • SwitchBot Bot(物理スイッチを押すデバイス)
    • 既存の壁スイッチに取り付けるだけで電気をオン・オフできる
  • SwitchBot ハブ(コンタクトセンサーおよび Bot をクラウド経由で操作するために必要)
ソフトウェア/サービス
  • Python
    • requests
    • time / datetime(標準ライブラリ)
  • SwitchBot API v1.1
  • InfluxDB(センサーデータの保存先)
  • SwitchBot 連携ライブラリ(自作:switchbot.py)
  • systemd(常駐サービス化)

完成イメージ

廊下の照度センサーが「明るい(bright)」を返し続けると、30秒ごとのチェックで検知カウンターが積み上がっていきます。
6回連続(約3分間)で「明るい」が続いた場合に消灯処理に入ります。ただし消灯前に SwitchBot API を直接呼び出して最新の状態を最終確認し、その瞬間も明るければ SwitchBot Bot が壁スイッチを押して消灯します。
途中で誰かが廊下の電気を消すか、または廊下を通過して一瞬でも「暗い(dark)」に変わればカウンターは 0 にリセットされ、消灯処理は行われません。

システムの仕組み

センサーデータは別プログラムが定期的に InfluxDB に記録しています。本プログラムはその InfluxDB のデータを参照しながら消灯可否を判断します。

  1. センサーデータ取得:InfluxDB から廊下の照度センサーの最新の明暗状態(brightness)を取得する
  2. 連続検知カウント:「bright」が続くたびにカウンターをインクリメント。「bright」以外になればカウンターを 0 にリセット
  3. 消灯判断:カウンターが 5 に達したタイミングで消灯処理を開始(6回連続≒3分間)
  4. 直前 API 確認(二段階目):消灯直前に SwitchBot API を直接呼び出し、この瞬間も「bright」かを再確認。確認が取れた場合のみ SwitchBot Bot に消灯コマンドを送る
  5. 30秒待機:上記処理を 30 秒ごとに繰り返す

実装のポイント

連続検知カウンターによる誤動作防止

通過時の誤消灯を防げる
廊下を通り過ぎる程度(数秒〜数十秒)では消灯されない。約3分間継続して点灯している場合のみ消灯するため「誰かがいるのに消えた」という誤動作がほぼなくなる

シンプルなロジックで実現
整数のカウンター変数ひとつで管理できるため、コードが複雑にならない

消灯タイミングが正確ではない
「ちょうど3分で消灯」ではなく「約3分以上経過後の最初のチェックで消灯」という動作になる。厳密なタイマー消灯が必要な場合には別途 datetime で経過時間を管理する設計が必要

消灯直前の API 直接確認(二段階確認)

InfluxDB の記録は別プログラムが定期的に書き込むため、最大で数十秒〜数分古い場合があります。
カウンターが 5 に達した時点で InfluxDB を信じてそのまま消灯すると「実際にはもう誰かが電気を消した後に SwitchBot Bot がさらに Press してしまう(オンになってしまう)」という誤動作が発生します。
そこで消灯コマンドを送る直前に SwitchBot API を直接呼び出して最新の明暗状態を確認し、「今この瞬間も bright である」という確証が取れた場合のみ消灯コマンドを実行しています。

DB の遅延による誤動作を完全に防ぐ
消灯判断は DB のデータ(ループ全体の効率化)、最終確認は API のリアルタイムデータ(精度保証)という役割分担で両者のいいとこ取りができる

消灯時に API コールが1回増える
消灯のたびに SwitchBot API を追加で1回呼ぶため、API のレート制限に余裕がある前提で設計する必要がある

長時間点灯モード(自動消灯の一時無効)

廊下で掃除や洗濯など長時間作業する場合に自動消灯されると困るため、外部から is_Long_Lighting_Enabled フラグを立てると消灯カウンターが大きい値にリセットされ、通常の自動消灯ロジックが無効になります。
フラグは hall_lighting_turnoff_flag_change.py を実行することで設定できます(赤外線リモコンや音声コマンドからのトリガーに対応可能)。

事前準備

センサーデータの InfluxDB 書き込み準備

本プログラムは InfluxDB に書き込まれたコンタクトセンサーの brightness フィールドを参照します。
SwitchBot のセンサーデータを定期的に InfluxDB に記録する別プログラムを事前に起動しておく必要があります(別記事参照)。

SwitchBot デバイス ID の確認

SwitchBot アプリまたは SwitchBot API の GET /v1.1/devices エンドポイントから下記2つのデバイス ID を確認して設定ファイルに登録しておきます。

  • hall_lighting_sensor:廊下に設置した SwitchBot コンタクトセンサーの Device ID
  • hall_lighting_bot:廊下の壁スイッチに取り付けた SwitchBot Bot の Device ID
{
    "switchbot": {
        "api": "<SwitchBot APIトークン>",
        "secret": "<SwitchBot シークレットキー>",
        "api_url_v11": "https://api.switch-bot.com/v1.1/",
        "device_id": {
            "hall_lighting_sensor": "<コンタクトセンサーのDevice ID>",
            "hall_lighting_bot":    "<SwitchBot BotのDevice ID>"
        }
    }
}

実装方法

自動消灯メインプログラムの作成

# -*- coding: utf-8 -*-
import sys
import time
import switchbot
import common
# InfluxDB クライアント(センサーデータ取得用)
from influx.v2 import client as influx_client

# SwitchBot インスタンスを生成
switchbot_instance = switchbot.Switchbot()

# 廊下の照度センサーと SwitchBot Bot の Device ID を設定ファイルから取得
HALL_SENSOR_ID = settings['switchbot']['device_id']['hall_lighting_sensor']
HALL_BOT_ID    = settings['switchbot']['device_id']['hall_lighting_bot']

# SwitchBot Bot に送るコマンド(press = ボタンを1回押す)
press_command = {
    'command':     'press',
    'parameter':   'default',
    'commandType': 'command'
}

# 起動時に長時間点灯モードを無効化
common.set_temporary_parameter('is_Long_Lighting_Enabled', False)

check_count = 0

while True:
    # 長時間点灯モードが有効なら check_count を大きい値にリセットして自動消灯を無効化
    if common.get_temporary_parameter('is_Long_Lighting_Enabled'):
        check_count = 60        # 5 に戻らないため自動消灯しなくなる
        common.set_temporary_parameter('is_Long_Lighting_Enabled', False)

    # InfluxDB からコンタクトセンサーの最新の明暗状態を取得
    res = influx_client.query_dict_by_influxql(
        'contact_sensor',
        {'brightness'},
        {'deviceId': HALL_SENSOR_ID},
        query_mode=influx_client.QueryMode.LAST
    )
    brightness = res[0].get('brightness') if res else 'unknown'
    print(f'廊下の照明の状態: {brightness}  (check_count={check_count})')

    if brightness == 'bright':
        if check_count == 5:
            # ★ 消灯直前に SwitchBot API を直接呼び出して最新状態を最終確認
            latest = switchbot_instance.get_device_status(HALL_SENSOR_ID)
            latest_brightness = str(latest['brightness'])
            print(f'最終確認(API直接取得): {latest_brightness}')

            if latest_brightness == 'bright':
                # この瞬間も明るければ消灯コマンドを送る
                switchbot_instance.execute_command(HALL_BOT_ID, press_command)
                print('廊下の照明を消しました。')
            check_count = 0
        else:
            check_count += 1    # 連続検知カウンターをインクリメント
    else:
        check_count = 0         # 暗くなったらカウンターをリセット

    time.sleep(30)              # 30秒ごとにチェック

長時間点灯モード切り替えプログラム

掃除などで廊下を長時間使用する場合に、このプログラムを実行すると自動消灯が無効になります。
赤外線リモコンや音声コマンドをトリガーにしてこのプログラムを呼び出す設計にすると便利です。

# -*- coding: utf-8 -*-
# 廊下の電気を自動消灯しないモードを有効化する
import common

# is_Long_Lighting_Enabled = True にすることで自動消灯を一時無効化
common.set_temporary_parameter('is_Long_Lighting_Enabled', True)

systemd サービスとして登録する

[Unit]
Description=Hall Lighting Turnoff
After=nginx.service influxdb.service webapi.service
Wants=nginx.service influxdb.service webapi.service

[Service]
ExecStartPre=/bin/sleep 30
ExecStart=/home/<username>/Projects/venv/bin/python /home/<username>/Projects/smarthome/core/control/energy_saving/hall_lighting_turnoff.py
Restart=always
RestartSec=60
Type=simple
User=<username>

[Install]
WantedBy=multi-user.target

ExecStartPre=/bin/sleep 30 は InfluxDB や Web API サービスの起動完了を待つための遅延です。

sudo systemctl enable smh-hall-lighting-turnoff.service
sudo systemctl start smh-hall-lighting-turnoff.service
sudo systemctl status smh-hall-lighting-turnoff.service

コードの解説

連続検知カウンターの動作詳細

カウンターが 5 になった次のループで消灯処理が動きます。30秒ごとのループで6回連続「bright」が確認されると消灯が実行されます(約3分間)。

  • ループ1回目:bright → check_count : 0 → 1
  • ループ2回目:bright → check_count : 1 → 2
  • ループ3回目:bright → check_count : 2 → 3
  • ループ4回目:bright → check_count : 3 → 4
  • ループ5回目:bright → check_count : 4 → 5
  • ループ6回目:bright → check_count == 5 → 消灯処理へ

途中で「bright」以外になると check_count が 0 にリセットされるため、再び6回連続で「bright」にならない限り消灯されません。

二段階確認のコード

if check_count == 5:
    # ①InfluxDB でなく SwitchBot API を直接呼んで最新の明暗状態を取得
    latest = switchbot_instance.get_device_status(HALL_SENSOR_ID)
    latest_brightness = str(latest['brightness'])

    # ②この瞬間も明るければ初めて消灯コマンドを実行
    if latest_brightness == 'bright':
        switchbot_instance.execute_command(HALL_BOT_ID, press_command)

①の API 直接取得で「bright でない」と判明した場合(誰かがすでに消した直後など)は消灯コマンドを送りません。
これにより SwitchBot Bot が「消灯しようとしたら実は消えていて逆に点灯させてしまう」事故を防いでいます。

長時間点灯モードのロジック

if common.get_temporary_parameter('is_Long_Lighting_Enabled'):
    check_count = 60   # 5 には戻らない値にセット → 自動消灯が無効化される
    common.set_temporary_parameter('is_Long_Lighting_Enabled', False)

消灯判定は check_count == 5 の場合のみ実行されます。check_count が 60 になると、「bright」が続くたびに 61、62 と増え続けるため、check_count == 5 に戻ることはなく自動消灯が無効化されます。
廊下の電気を手動で消すと照度センサーが「dark」を返し、check_count が 0 にリセットされてメインロジックが正常動作に戻ります。

動作確認

サービス起動後、廊下の電気をつけた状態でログを確認します。

sudo journalctl -u smh-hall-lighting-turnoff.service -f

下記のようなログが出力されれば正常に動作しています。

廊下の照明の状態: bright  (check_count=0)
廊下の照明の状態: bright  (check_count=1)
廊下の照明の状態: bright  (check_count=2)
廊下の照明の状態: bright  (check_count=3)
廊下の照明の状態: bright  (check_count=4)
廊下の照明の状態: bright  (check_count=5)
最終確認(API直接取得): bright
廊下の照明を消しました。
廊下の照明の状態: dark  (check_count=0)

SwitchBot Bot は「Press(1回押す)」コマンドを送るため、スイッチの現在の状態(ON/OFF)を把握していません。消灯しようとしたときにすでに消えていた場合は逆に点灯してしまいます。二段階確認の API チェックでこの問題をほぼ防いでいますが、センサーのレスポンス遅延などで稀に誤動作する可能性は残ります。

まとめ

この記事では、SwitchBot の照度センサーと連続検知ロジックを組み合わせた廊下の自動消灯システムを実装しました。

  • 「6回連続 bright」という連続検知ロジックで通過時の誤消灯を防止
  • DB 参照(効率化)+ 消灯直前 API 直接確認(精度保証)の二段階で確実な消灯を実現
  • SwitchBot Bot が物理スイッチを押すため、既存の壁スイッチをそのまま活用できる
  • 長時間点灯モードで掃除・洗濯中の誤消灯を防止
  • systemd サービスで Raspberry Pi 起動時から自動稼働

既存の SwitchBot センサーを活用するため追加コストはほぼかかりません。連続検知のチェック回数(現在は5回)や間隔(30秒)は環境に合わせて調整してみてください。

タイトルとURLをコピーしました