今回は以前サンプルで動かしたIRセンサーの操作や温度・気圧・湿度・照度センサーの取得の情報を REST API で取得や実行できるようにしようと思います。
概要イメージ
「nginx」「gunicorn」「falcon」を利用して REST API を作成して、「RPZ-IR-Sensor」に対して正面のON/OFFの命令の実行とセンサーから取得した情報の取得を行う機能を実装します。
API の機能
- センサー情報(光度、温度、湿度、気圧)の取得
- 照明を点ける
- 照明を消す
- 照明の電燈を切り変え
各パッケージのインストール
falcon インストール
$ sudo pip install falcon
Collecting falcon
Downloading falcon-1.3.0-py2.py3-none-any.whl (150kB)
100% |████████████████████████████████| 153kB 249kB/s
Requirement already satisfied: six>=1.4.0 in /usr/lib/python2.7/dist-packages (from falcon)
Collecting python-mimeparse>=1.5.2 (from falcon)
Downloading python_mimeparse-1.6.0-py2.py3-none-any.whl
Installing collected packages: python-mimeparse, falcon
Successfully installed falcon-1.3.0 python-mimeparse-1.6.0
cython インストール
$ sudo pip install cython
Collecting cython
Downloading Cython-0.27.3.tar.gz (1.8MB)
100% |████████████████████████████████| 1.8MB 46kB/s
Building wheels for collected packages: cython
Running setup.py bdist_wheel for cython ... done
Stored in directory: /root/.cache/pip/wheels/d1/2d/01/94d746e3ab647f56f2b1ef9547e37f62ed010d9956c988ab9f
Successfully built cython
Installing collected packages: cython
Successfully installed cython-0.27.3
※ Raspberry Pi zero で実行するとすごい時間がかかります。。。
gunicorn インストール
$ sudo pip install gunicorn
Collecting gunicorn
Downloading gunicorn-19.7.1-py2.py3-none-any.whl (111kB)
100% |████████████████████████████████| 112kB 240kB/s
Installing collected packages: gunicorn
Successfully installed gunicorn-19.7.1
nginx インストール
$ sudo apt install nginx
パッケージリストを読み込んでいます... 完了
依存関係ツリーを作成しています
状態情報を読み取っています... 完了
以下の追加パッケージがインストールされます:
libnginx-mod-http-auth-pam libnginx-mod-http-dav-ext libnginx-mod-http-echo libnginx-mod-http-geoip libnginx-mod-http-image-filter libnginx-mod-http-subs-filter
libnginx-mod-http-upstream-fair libnginx-mod-http-xslt-filter libnginx-mod-mail libnginx-mod-stream nginx-common nginx-full
提案パッケージ:
fcgiwrap nginx-doc ssl-cert
以下のパッケージが新たにインストールされます:
libnginx-mod-http-auth-pam libnginx-mod-http-dav-ext libnginx-mod-http-echo libnginx-mod-http-geoip libnginx-mod-http-image-filter libnginx-mod-http-subs-filter
libnginx-mod-http-upstream-fair libnginx-mod-http-xslt-filter libnginx-mod-mail libnginx-mod-stream nginx nginx-common nginx-full
アップグレード: 0 個、新規インストール: 13 個、削除: 0 個、保留: 0 個。
1,502 kB のアーカイブを取得する必要があります。
この操作後に追加で 2,563 kB のディスク容量が消費されます。
続行しますか? [Y/n] y
取得:1 http://ftp.tsukuba.wide.ad.jp/Linux/raspbian/raspbian stretch/main armhf nginx-common all 1.10.3-1+deb9u1 [104 kB]
取得:2 http://ftp.tsukuba.wide.ad.jp/Linux/raspbian/raspbian stretch/main armhf libnginx-mod-http-auth-pam armhf 1.10.3-1+deb9u1 [85.4 kB]
取得:3 http://ftp.tsukuba.wide.ad.jp/Linux/raspbian/raspbian stretch/main armhf libnginx-mod-http-dav-ext armhf 1.10.3-1+deb9u1 [87.0 kB]
取得:4 http://ftp.tsukuba.wide.ad.jp/Linux/raspbian/raspbian stretch/main armhf libnginx-mod-http-echo armhf 1.10.3-1+deb9u1 [95.1 kB]
取得:5 http://ftp.tsukuba.wide.ad.jp/Linux/raspbian/raspbian stretch/main armhf libnginx-mod-http-geoip armhf 1.10.3-1+deb9u1 [86.5 kB]
取得:6 http://ftp.tsukuba.wide.ad.jp/Linux/raspbian/raspbian stretch/main armhf libnginx-mod-http-image-filter armhf 1.10.3-1+deb9u1 [89.3 kB]
取得:7 http://ftp.tsukuba.wide.ad.jp/Linux/raspbian/raspbian stretch/main armhf libnginx-mod-http-subs-filter armhf 1.10.3-1+deb9u1 [88.5 kB]
取得:8 http://ftp.tsukuba.wide.ad.jp/Linux/raspbian/raspbian stretch/main armhf libnginx-mod-http-upstream-fair armhf 1.10.3-1+deb9u1 [88.6 kB]
取得:9 http://ftp.tsukuba.wide.ad.jp/Linux/raspbian/raspbian stretch/main armhf libnginx-mod-http-xslt-filter armhf 1.10.3-1+deb9u1 [88.0 kB]
取得:10 http://ftp.tsukuba.wide.ad.jp/Linux/raspbian/raspbian stretch/main armhf libnginx-mod-mail armhf 1.10.3-1+deb9u1 [113 kB]
取得:11 http://ftp.tsukuba.wide.ad.jp/Linux/raspbian/raspbian stretch/main armhf libnginx-mod-stream armhf 1.10.3-1+deb9u1 [106 kB]
取得:12 http://ftp.tsukuba.wide.ad.jp/Linux/raspbian/raspbian stretch/main armhf nginx-full armhf 1.10.3-1+deb9u1 [389 kB]
取得:13 http://ftp.jaist.ac.jp/pub/Linux/raspbian-archive/raspbian stretch/main armhf nginx all 1.10.3-1+deb9u1 [81.5 kB]
1,502 kB を 32秒 で取得しました (46.7 kB/s)
パッケージを事前設定しています ...
以前に未選択のパッケージ nginx-common を選択しています。
(データベースを読み込んでいます ... 現在 123024 個のファイルとディレクトリがインストールされています。)
.../00-nginx-common_1.10.3-1+deb9u1_all.deb を展開する準備をしています ...
nginx-common (1.10.3-1+deb9u1) を展開しています...
以前に未選択のパッケージ libnginx-mod-http-auth-pam を選択しています。
.../01-libnginx-mod-http-auth-pam_1.10.3-1+deb9u1_armhf.deb を展開する準備をしています ...
libnginx-mod-http-auth-pam (1.10.3-1+deb9u1) を展開しています...
以前に未選択のパッケージ libnginx-mod-http-dav-ext を選択しています。
.../02-libnginx-mod-http-dav-ext_1.10.3-1+deb9u1_armhf.deb を展開する準備をしています ...
libnginx-mod-http-dav-ext (1.10.3-1+deb9u1) を展開しています...
以前に未選択のパッケージ libnginx-mod-http-echo を選択しています。
.../03-libnginx-mod-http-echo_1.10.3-1+deb9u1_armhf.deb を展開する準備をしています ...
libnginx-mod-http-echo (1.10.3-1+deb9u1) を展開しています...
以前に未選択のパッケージ libnginx-mod-http-geoip を選択しています。
.../04-libnginx-mod-http-geoip_1.10.3-1+deb9u1_armhf.deb を展開する準備をしています ...
libnginx-mod-http-geoip (1.10.3-1+deb9u1) を展開しています...
以前に未選択のパッケージ libnginx-mod-http-image-filter を選択しています。
.../05-libnginx-mod-http-image-filter_1.10.3-1+deb9u1_armhf.deb を展開する準備をしています ...
libnginx-mod-http-image-filter (1.10.3-1+deb9u1) を展開しています...
以前に未選択のパッケージ libnginx-mod-http-subs-filter を選択しています。
.../06-libnginx-mod-http-subs-filter_1.10.3-1+deb9u1_armhf.deb を展開する準備をしています ...
libnginx-mod-http-subs-filter (1.10.3-1+deb9u1) を展開しています...
以前に未選択のパッケージ libnginx-mod-http-upstream-fair を選択しています。
.../07-libnginx-mod-http-upstream-fair_1.10.3-1+deb9u1_armhf.deb を展開する準備をしています ...
libnginx-mod-http-upstream-fair (1.10.3-1+deb9u1) を展開しています...
以前に未選択のパッケージ libnginx-mod-http-xslt-filter を選択しています。
.../08-libnginx-mod-http-xslt-filter_1.10.3-1+deb9u1_armhf.deb を展開する準備をしています ...
libnginx-mod-http-xslt-filter (1.10.3-1+deb9u1) を展開しています...
以前に未選択のパッケージ libnginx-mod-mail を選択しています。
.../09-libnginx-mod-mail_1.10.3-1+deb9u1_armhf.deb を展開する準備をしています ...
libnginx-mod-mail (1.10.3-1+deb9u1) を展開しています...
以前に未選択のパッケージ libnginx-mod-stream を選択しています。
.../10-libnginx-mod-stream_1.10.3-1+deb9u1_armhf.deb を展開する準備をしています ...
libnginx-mod-stream (1.10.3-1+deb9u1) を展開しています...
以前に未選択のパッケージ nginx-full を選択しています。
.../11-nginx-full_1.10.3-1+deb9u1_armhf.deb を展開する準備をしています ...
nginx-full (1.10.3-1+deb9u1) を展開しています...
以前に未選択のパッケージ nginx を選択しています。
.../12-nginx_1.10.3-1+deb9u1_all.deb を展開する準備をしています ...
nginx (1.10.3-1+deb9u1) を展開しています...
nginx-common (1.10.3-1+deb9u1) を設定しています ...
Created symlink /etc/systemd/system/multi-user.target.wants/nginx.service → /lib/systemd/system/nginx.service.
libnginx-mod-http-image-filter (1.10.3-1+deb9u1) を設定しています ...
libnginx-mod-http-subs-filter (1.10.3-1+deb9u1) を設定しています ...
systemd (232-25+deb9u1) のトリガを処理しています ...
libnginx-mod-http-auth-pam (1.10.3-1+deb9u1) を設定しています ...
libnginx-mod-http-dav-ext (1.10.3-1+deb9u1) を設定しています ...
libnginx-mod-mail (1.10.3-1+deb9u1) を設定しています ...
man-db (2.7.6.1-2) のトリガを処理しています ...
libnginx-mod-http-xslt-filter (1.10.3-1+deb9u1) を設定しています ...
libnginx-mod-http-upstream-fair (1.10.3-1+deb9u1) を設定しています ...
libnginx-mod-http-geoip (1.10.3-1+deb9u1) を設定しています ...
libnginx-mod-stream (1.10.3-1+deb9u1) を設定しています ...
libnginx-mod-http-echo (1.10.3-1+deb9u1) を設定しています ...
nginx-full (1.10.3-1+deb9u1) を設定しています ...
nginx (1.10.3-1+deb9u1) を設定しています ...
python で REST API を作成
以下の機能を満たす API を作ります。
- センサー情報(光度、温度、湿度、気圧)の取得
- 照明を点ける
- 照明を消す
- 照明の電燈を切り変え
API 設計は面倒だったので、全て GET で endpoint を変更して操作するようにしました。
一応、簡単なエラーハンドリングもしておきました。
センサー類の扱いについては以下のリンクを参考にしてください。
#!/usr/bin/env python3
"""
TSL2561/BME280/IR Control Module
"""
import os
import falcon
from bme280i2c import BME280I2C
from tsl2561 import TSL2561
class Sensor:
def on_get(self, req, resp, action):
"""Handles GET requests"""
if action == "show":
resp.media = self.__get_sensor_response()
elif action in {"on", "off", "change"}:
self.__execute_light_action(action)
resp.media = self.__get_sensor_response()
else:
resp.media = self.__get_error_response("Illegal Sensor Action [" + action + "]")
def __execute_light_action(self, action):
os.system('irsend SEND_ONCE lightd ' + action)
def __get_sensor_response(self):
bme280 = BME280I2C(0x77)
tsl2561 = TSL2561(0x29)
bme = bme280.meas()
tsl = tsl2561.meas_single()
if not (bme or tsl):
msg = self.__get_error_response("No Sensor Available")
else:
msg = {
"Temp": bme280.T
, "Pressure": bme280.P
, "Humidity": bme280.H
, "Lux": tsl2561.lux
}
return msg
def __get_error_response(self, message):
return {
"error": message
}
api = falcon.API()
api.add_route('/sensor/{action}', Sensor())
※ tsl2561.py, bme280i2c.py は サンプルプログラムから流用
gunicorn を起動
$ gunicorn sensor:api
[2017-12-10 15:19:19 +0000] [870] [INFO] Starting gunicorn 19.7.1
[2017-12-10 15:19:19 +0000] [870] [INFO] Listening at: http://127.0.0.1:8000 (870)
[2017-12-10 15:19:19 +0000] [870] [INFO] Using worker: sync
[2017-12-10 15:19:19 +0000] [874] [INFO] Booting worker with pid: 874
nginx のリバースプロキシを設定
デフォルトの設定だと sites-enabled/default ファイルの設定が有効になってしまうのでコメントアウト
$ sudo vi /etc/nginx/nginx.conf
61 ##
62
63 include /etc/nginx/conf.d/*.conf;
64 # include /etc/nginx/sites-enabled/*;
65 }
sensor 用の設定ファイルを作成(とりあえずセンサーしか使わないので全てをリバースプロキシの対象に)
$ sudo vi /etc/nginx/conf.d/sensor.conf
upstream app_server {
server 127.0.0.1:8000 fail_timeout=0;
}
server {
listen 80 default;
server_name _;
keepalive_timeout 5;
location / {
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_redirect off;
if (!-f $request_filename) {
proxy_pass http://app_server;
break;
}
}
}
nginx を再起動
$ sudo service nginx restart
API を試す
照明を点ける
$ time curl http://192.168.3.99/sensor/on | python -m json.tool
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 71 100 71 0 0 188 0 --:--:-- --:--:-- --:--:-- 188
{
"Humidity": 28,
"Lux": 177.262099009901,
"Pressure": 1009,
"Temp": 26
}
real 0m0.403s
user 0m0.033s
sys 0m0.040s
照明の電燈を切り変え
$ time curl http://192.168.3.99/sensor/change | python -m json.tool
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 63 100 63 0 0 76 0 --:--:-- --:--:-- --:--:-- 76
{
"Humidity": 27,
"Lux": 120.7792,
"Pressure": 1009,
"Temp": 26
}
real 0m0.851s
user 0m0.034s
sys 0m0.040s
照明を消す
$ time curl http://192.168.3.99/sensor/off | python -m json.tool
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 62 100 62 0 0 70 0 --:--:-- --:--:-- --:--:-- 70
{
"Humidity": 27,
"Lux": 34.1696,
"Pressure": 1009,
"Temp": 26
}
real 0m0.909s
user 0m0.035s
sys 0m0.043s
センサー情報(光度、温度、湿度、気圧)の取得
$ time curl http://192.168.3.99/sensor/show | python -m json.tool
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 62 100 62 0 0 94 0 --:--:-- --:--:-- --:--:-- 94
{
"Humidity": 27,
"Lux": 33.9872,
"Pressure": 1009,
"Temp": 26
}
real 0m0.684s
user 0m0.037s
sys 0m0.047s
存在しないアクションの実行
$ time curl http://192.168.3.99/sensor/hoge | python -m json.tool
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 41 100 41 0 0 1158 0 --:--:-- --:--:-- --:--:-- 1171
{
"error": "Illegal Sensor Action [hoge]"
}
real 0m0.129s
user 0m0.030s
sys 0m0.072s
まとめ
API 経由でのセンサーへのアクセスですが思った以上に簡単にできました。
単純にインストールと nginx の設定ではまったところで時間をくったくらいでハマらなければサクサクできます。
python はあまり慣れていないので、パッケージングとか自動起動とかそこらへんを今後はまとめていきたいと思います。