Raspberry Pi と照度センサを使って、トイレの使用状況をSlackからわかるようにしてみました。
※全部会社で買ってもらった(*´ω`*)
動機
職場は2フロアあり、下のフロアがオープンスペースになっていおりトイレもここにあります。私は上のフロアで仕事をしていますので、トイレにいくには下のフロアに降りるのですが、トイレが1個しかないので、降りてみたら使用中で仕方なく上に戻るという非生産的な行動が多発していました。
そのため、上のフロアにいてもひと目でトイレの使用状況がわかるようにしたかったのです。
実現方法
会社のトイレは家庭用のトイレ個室のような感じですので、こちたの記事のように扉の開閉センサーでの判定は難しい(使い終わって扉を閉められると使用中になりうる)ので、個室の電気がついていれば使用中とみなすことにして実装しました。
ざっくりいうとこんな感じです。
- トイレに照度センサーを置いて、明るさを監視する
- 照度センサー等はもろもろラズパイで作る
- 明るさが変化した時点でSlackのBotアカウントのステータスアイコンを変更する
- 使用中ならactive
- 空いている場合はaway
照度センサーについて
会社にあまっていたラズパイ(raspberry pi 3 model B)があったので貰い受け、照度センサ:BH1750をバックオフィスの方にねだることで機材を揃えました。
なお、購入した照度センサはピンがはんだ付けされていないので別途ハンダゴテなども必要です。(これも買ってもらった)
ラズパイとBH1750の接続や値の取得については、以下の記事の輝度センサ部分を参考とさせていただきました。
http://jimaoka.hatenablog.jp/entry/raspi-sensor
一通りセットアップを終えたら以下のPython3のコードで輝度を取得できることを確認しました。
# smbus モジュールの入手
$ pip3 install smbus
import smbus def get_lux(): bus = smbus.SMBus(1) addr = 0x23 lux = bus.read_i2c_block_data(addr, 0x10) return (lux[0]*256+lux[1])/1.2 if __name__ == '__main__': print(get_lux())
私の環境では暗いときは100未満で、蛍光灯レベルでは200以上の値が出ていました。
Skackへの通知
単にトイレ使用開始時にSlackにメッセージを通知してもいいのですが、それはあまりに監視されている感じもするので、SlackBotのステータスで使用中かどうかを表現するようにしました。
とりあえず、ラズパイ上でSlackBotを動かすことにしました。SlackBotはslackbotを使うのが楽そうなので、こちらを使います。
$ pip3 install slackbot
ファイルはこんな感じで構成します。
. ├── bot.py # 実際に実行するスクリプト ├── plugins # 話しかけられた際の応答処理を書いておくディレクトリ │ ├── __init__.py │ └── toilet.py # 会話の応答処理を定義するスクリプト └── slackbot_settings.py # Botの設定ファイル
Slack Tokenの取得
Botを作成するので、事前にSlack側で作業をしておきます。
https://my.slack.com/services/new/bot
あとは設定を保存し、表示されていたAPI トークンを確認しておきます。取得したトークンはslackbot_settings.py
に設定しておきます。
: # botアカウントのトークンを指定 API_TOKEN = '確認しておいたトークン' : :
Botによる会話
このslackbotは話しかけられた際の応答の定義をplugins
配下のスクリプトで定義します。例えば以下をplugins/toilet.py
として配置します。
# coding: utf-8 import smbus from slackbot.bot import respond_to # @botname: で反応するデコーダ from slackbot.bot import default_reply # 該当する応答がない場合に反応するデコーダ def get_lux(): bus = smbus.SMBus(1) addr = 0x23 lux = bus.read_i2c_block_data(addr,0x10) return (lux[0]*256+lux[1])/1.2 # といれ と話しかけらたときの応答 @respond_to('といれ') def check_toilet(message): message.reply('明るさ{}'.format(get_lux())) if get_lux() > 100: message.reply('たぶん、中に誰もいなくないですよ') else: message.reply('たぶん、中に誰もいませんよ') # いずれのルールにも該当しない場合の応答 @default_reply def default_func(message): message.reply('といれ って話しかけてね')
このBotに対してといれ
と話しかけると、現在の明るさと空室状況を応答します。
Botの在籍状況(ステータス)の設定
これで、トイレにいく前にBotに話しかけておけば空室状況がわかるようになりましたが、毎回聞くのは面倒なのでステータスアイコンで判別したいわけです。しかし、今回利用しているslackbot
にはBotアカウントの在籍・離席といったステータスを設定する機能がないようです。そのため、以下のusers.setPresence
というSlackのWEBAPIを直接利用します。
https://api.slack.com/methods/users.setPresence
PythonでHTTPリクエストをするのであれば、requests
が楽なので導入します。
$ pip3 install requests
あとはこんな感じでステータスを変更できました。
API_URL = 'https://slack.com/api/users.setPresence?token={token}&presence={status}' API_TOKEN = 'YOUR_BOT_TOKEN' # ステータスON requests.get(API_URL.format(token=API_TOKEN, status='auto')).content # ステータスOFF requests.get(API_URL.format(token=API_TOKEN, status='away')).content
また、空室状況にあわせてステータスを設定するには定期的に明るさを取得する必要がありますが、そのような定期実行の機能もなさそうでした。
しかたないので、エントリポイントであるbot.py
でBotを起動する際に独自にスレッドを生やして定期実行させることにしました。最終的にbot.py
は以下のようになりました。
#!/usr/bin/env python3 # coding: utf-8 import time import datetime import threading import requests from slackbot.bot import Bot from slackbot.settings import API_TOKEN # toilet.pyから照度取得関数をもってきておく from plugins.toilet import get_lux API_URL = 'https://slack.com/api/users.setPresence?token={token}&presence={status}' LIGHT_THRESHOLD = 100 def set_away(): """ Botのステータスを離席にする :return: """ res = requests.get(API_URL.format(token=API_TOKEN, status='away')) print(res.content) def set_active(): """ Botのステータスを在籍にする :return: """ res = requests.get(API_URL.format(token=API_TOKEN, status='auto')) print(res.content) def check_lux(): """ Threadで定期的に明るさをチェックしてしきい値を下回ったらステータスをOFFにする """ is_using = False # 初期化 set_away() while True: lux = get_lux() print(datetime.datetime.now(), lux) # ステータスが変化したら通知する # 未使用 で明るくなった -> 利用開始 if lux > LIGHT_THRESHOLD and not is_using: is_using = True print('ON') set_active() # 使用中から暗くなった -> 利用終了 elif lux <= 100 and is_using: is_using = False print('OFF') set_away() time.sleep(3) def main(): bot = Bot() # 照度監視用のスレッドを起動する th_me = threading.Thread(target=check_lux, name="th_check_lux") th_me.setDaemon(True) th_me.start() try: # botを起動する bot.run() except Exception as e: print(e) if __name__ == "__main__": print('start slackbot') main()
これで以下のようにBotを起動すれば監視が開始されます。
$ python3 bot.py start slackbot b'{"ok":true}' 2017-12-16 21:58:54.596923 184 ON ★ 明るかったので使用中になった 2017-12-16 21:58:57.598332 126 2017-12-16 21:59:00.602300 180 2017-12-16 21:59:03.607373 3 OFF ★暗くなったので空き状態になった b'{"ok":true}' 2017-12-16 21:59:06.891987 157 :
自動起動の設定
ラズパイを起動した時点で、トイレの監視を自動的に有効化してほしいのでsystemdに登録しておきます。まずは、起動用のシェルスクリプトを書いておきます。
#!/bin/sh # service 登録用のスクリプト python3 /home/pi/apps/toi/bot.py
そして、systemdに登録するための設定をtoiletmonitor.service
として作成します。
[Unit] Description = monitor toilet [Service] ExecStart = /path/to/shell/script ※要書き換え Restart = always Type = simple [Install] WantedBy = multi-user.target
作成したtoiletmonitor.service
を/etc/systemd/system/
の配下に配置します。そして、以下のように起動できるか確認しておきます。
# 起動 $ sudo systemctl start toiletmonitor # 停止 $ sudo systemctl stop toiletmonitor
問題なく動作ができたら、以下で自動起動を有効化します。
$ sudo systemctl enable toiletmonitor
これで、トイレにラズパイを置いて電源をいれるだけで監視が開始されるようになります。
まとめ
実際にこんな感じで会社のトイレに配置しています。
今回のコード等は以下のリポジトリで公開していますのでよろしければご覧ください。
https://github.com/denzow/iott/blob/master/toiletmonitor.service.sample
はじめて真面目にラズパイで遊んでみましたが、ハマりどころもすくなくなかなか楽しかったです。照度センサも3個入りでまだまだ余っていますので他の活用方法も考えてみたいと思います。
なお、本記事を作成中に確認したところ金曜日まで生きていたBotが息をしていないので社外からトイレの利用状況を把握できないという重大なインシデントが発生しているので、週明けに復旧作業をする見込みです・・・