室温を監視し、設定温度を超えた際にslackで通知するものを作ったのでメモ。
使ったもの。
- Raspberry Pi Zero W
- USB Thermometer
室温監視
raspbianのインストール
NOOBS LiteでRaspbian Liteをインストール。
https://www.raspberrypi.org/downloads/noobs/
特段気をつけることはなく、インストーラに従ってインストールする。
この辺り参考。
Thermometerの設定
Amazonから購入したが、ファームウェアのバージョンが2種類あり、どちらがやってくるかは運次第の模様。
今回手元に届いたのは、413d:2107
というバージョンのものだったので、ここから下は同バージョンで動作確認できた手順です。
(他のバージョンではgitからライブラリを持ってくるだけで動くとか動かないとか...)
HIDAPIのインストール
USBと通信するため、HIDAPIライブラリをインストールする。
https://github.com/signal11/hidapi
READMEにある通り、下記ライブラリが必要になるのでインストールする。
# まずは依存ライブラリのインストール
$ sudo apt-get install libudev-dev libusb-1.0-0-dev libfox-1.6-dev
$ sudo apt-get install autotools-dev autoconf automake libtool
# HIDAPIをインストール
$ git https://github.com/signal11/hidapi.git
$ cd linux/
$ make -f Makefile-manual
TEMPeredのインストール
いよいよ、thermometerから温度を取得するためのTEMPeredライブラリをインストールする。
$ git clone https://github.com/hughesr/TEMPered
$ cd TEMPered
# 上手くいかなかったのでissues見てたら最新リビジョンはMacに対応っぽかったので、
# Linuxに対応しているリビジョンまで戻してmake
# https://github.com/edorfaus/TEMPered/issues/51
$ git checkout hack-413d-2107
$ git reset --hard 75aa1e2
$ cmake .
$ make
これで温度が取得できるようになった。
$ sudo utils/tempered
/dev/hidraw1 0: temperature 25.37 °C
/dev/hidraw1 1: Failed to get the temperature: Not enough data was read from the sensor.
/dev/hidraw1 1: no sensor data available
室温監視スクリプト作成
温度が取れるようになったので、下記要件のスクリプトを作成。
- 5分おきに温度を取得し、DBに格納する
※下記ソースは2019/8/18時点のもので、この後の機能追加及びリファクタリングで変更される可能性あり。
#!/bin/bash
# TEMPeredで温度を取得し、awkで温度のみを切り出す
TEMPER=`sudo /home/pi/TEMPered/utils/tempered | head -n 1 | awk '{ print $4 }'`
# 温度を引数にDB登録、slack通知のpythonを起動
cd /home/pi/temperature_monitor/
python3 handle_temperature.py $TEMPER
monitor.shをcronに設定し、5分おきに起動するようにする。
Slack連携
下記の条件でslackに通知を送る。
- 5回連続気温の取得に失敗したらslackで通知する
- 気温が30度を上回ったらslackで通知する
SlackのAPIはいくつか種類があるみたいで、今回はメッセージを送信したいだけなので、Incoming Webhooksを使用。
下記URLからwebhookのエンドポイントを作成。
https://api.slack.com/apps?new_app=1
作成すると、 https://hooks.slack.com/services/********/*******/**********
の様なエンドポイントが与えられる。
ここに対して正しいフォーマットでPOSTしてやればslackに投稿ができる。
スクリプトはpython3で作成。
import logging
import configparser
import sys
import datetime
import sqlite3
from contextlib import closing
from messaging_slack import notice
config = configparser.ConfigParser()
config.read('config')
SUCCESS = config['STATUS']['SUCCESS']
ERROR = config['STATUS']['ERROR']
COUNT_DEFAULT = config['ERROR_COUNT']['DEFAULT']
COUNT_LIMIT = config['ERROR_COUNT']['LIMIT']
DB = config['DB']['file']
log_file = config['LOG']['file']
THRESHOLD = 33.0
NOTICE_MESSAGE = "<!channel> 室温が {} 度になりました。"
ERROR_MESSAGE = "<!channel> デバイスに接続できません。"
logger = logging.getLogger(__name__)
fmt = "%(asctime)s %(levelname)s :%(message)s"
logging.basicConfig(level=logging.DEBUG, format=fmt, filename=log_file)
def error_check(flg):
if flg == SUCCESS:
update_error_count()
else:
error_count = get_error_count()
error_count += 1
if error_count == COUNT_LIMIT:
notice(ERROR_MESSAGE.format(t))
logger.error(ERROR_MESSAGE.format(datetime.datetime.now()))
update_error_count(error_count)
def get_error_count():
with closing(sqlite3.connect(DB)) as conn:
cursor = conn.cursor()
cursor.execute("select count from error_counts")
res = cursor.fetchone()
conn.close()
return res[0]
def update_error_count(error_count=COUNT_DEFAULT):
with closing(sqlite3.connect(DB)) as conn:
cursor = conn.cursor()
sql = "update error_counts set count = ?, updated_at = ?"
cursor.execute(sql, (error_count, datetime.datetime.now()))
conn.commit()
conn.close()
def str_add(s, num):
i = int(s)
i += num
return str(i)
if __name__ == "__main__":
args = sys.argv
try:
t = args[1]
t = float(t)
error_check(ERROR)
except:
error_check(ERROR)
logger.error(ERROR_MESSAGE.format(datetime.datetime.now()))
exit()
if t > THRESHOLD:
message = NOTICE_MESSAGE
notice(message.format(t))
with closing(sqlite3.connect(DB)) as conn:
cursor = conn.cursor()
sql = "insert into temperature (temperature, created_at) values (?, ?)"
now = datetime.datetime.now()
cursor.execute(sql, (t, now.strftime("%Y-%m-%d %H:%M")))
conn.commit()
conn.close()
import json
import os
import urllib.request
def notice(message):
data = {
'text': message,
}
headers = {
'Content-type': 'application/json'
}
# slackのIncoming WebhooksのURLは環境変数に設定
req = urllib.request.Request(os.environ['SLACK_URL'], json.dumps(data).encode(), headers)
urllib.request.urlopen(req)
以上でRaspberryPi Zeroを使った室温監視が構築できた。
次は、Slackのbotを使って室温の問い合わせや室温推移のグラフを描きたい。