以前、Ruby + SinatraでPX-105のインク残量を取得・表示したことがありました。
Ruby + Sinatra + SNMPでPX-105のインク残量を取得・表示する - メモ的な思考的な
最近さわっているPythonでも同じことができないかと思い、Python3 + Flask + PySNMP + Highcharts + Apache2.4 + Gmail APIで実装してみました。
ソースコードはGitHubへアップしてあります。
thinkAmi/printer-status-py
環境
- Windows10 x64
- Python 3.5.1 32bit
- Flask 0.11
- google-api-python-client 1.5.1
- PySNMP 4.3.2
- Highcharts
- Apache 2.4.10
- 他のアプリ向けのバーチャルホストがすでに設定されている前提
- mod_wsgi 4.5.2
Flaskアプリについて
PX-105からPySNMPを使ってインク情報を取得する
大きな流れはRubyで行った時と同じです。
PySNMPで
g = getCmd(SnmpEngine(), CommunityData(config.COMMUNITY, mpModel=0), UdpTransportTarget((config.PRINTER_HOST_IPV4, 161)), ContextData(), ObjectType(ObjectIdentity("{mib_id}{tail}".format(**vars())))) errIndication, errorStatus, errorIndex, varBinds = next(g)
とすると、変数varBinds
にGET response値が設定されるため、欲しい情報はvarBinds[0][1]
で取り出せます。
ただ、varBinds[0][1]
の値はOctetStringオブジェクトであり、そのままでは扱いづらいことから、必要に応じてint()やstr()で変換します。
なお、Flaskには以下のようにconfigファイルから設定値を取得できる機能があります。
python - How to import from config file in Flask? - Stack Overflow
ただ、今回は後述の通り、タスクマネージャーから動かすPythonスクリプトでもその設定値を参照するため、使えませんでした。
また、ローカル変数の列挙・展開をするため、以下を参考にvars()
を使いました。
vars()関数によるローカル変数の列挙 | Python Snippets
FlaskでHighchartsを使う
以下を参考に、Highchartsへ値を渡します。
Using Flask to output Python data to High Charts
今回、Highchartsの棒グラフを複数表示するため、chart用オブジェクトのリストをテンプレートへと渡しています。
main.py
from flask import Flask, render_template from printer import PX105 app = Flask(__name__) app.config.from_object('config') class HighCharts(object): def __init__(self, chart_id, chart, series, xAxis, plot_options): self.chart_id = chart_id self.chart = chart self.series = series self.xAxis = xAxis self.plot_options = plot_options @app.route("/") @app.route("/px105") def index(): p = PX105() charts = [] for i, tank in enumerate(p.tanks): id = "chart_{}".format(i) charts.append(HighCharts( chart_id = id, chart = {"renderTo": id, "type": "column", "height": 400,}, series = [{"name": "使用済", "data": [100 - tank.rest_volume], "pointWidth": 40, "color": "gray"}, {"name": "インク残量", "data": [tank.rest_volume], "pointWidth": 40, "color": tank.color}], xAxis = {"categories": [tank.name]}, plot_options = { "column": { "stacking": "percent"}}, )) return render_template("index.html", charts=charts, title="PX-105") if __name__ == "__main__": app.run(debug = True, host="0.0.0.0", port=8080, passthrough_errors=True)
Highchartsで描画する部分のテンプレートは以下の通りです。
templates\index.html
<div id="container"> {% for chart in charts %} <div id={{ chart.chart_id|safe }} class="chart" style="height:400px; width: 200px;"></div> <script> $(document).ready(function() { var options = { "title": { "text": null }, "legend": { "layout": "vertical" }, "yAxis": { "title": { "text": null } }, "credits": { "enabled": false }, "chart": {{ chart.chart|safe }}, "xAxis": {{ chart.xAxis|safe }}, "series": {{ chart.series|safe }}, "plotOptions": {{ chart.plot_options|safe }}, }; $({{ chart.chart_id|safe }}).highcharts(options); }); </script> {% endfor %} </div>
static\style.css
div.chart { display: table-cell; }
ここまででローカルで実行する準備ができたため、
(env) D:\printer_status_py>python main.py
のようにしてFlaskを起動し、http://localhost:8080/
へアクセスして、動作を確認します。
Apacheで動作させる
以下を参考に、ApacheでFlaskを動かす設定を行います。
app.wsgiファイルの作成
Flaskのドキュメントに従って記述します。
app.wsgi
import sys, os sys.path.insert(0, os.path.abspath(os.path.dirname(__file__))) from main import app as application
Apache用のconf\extra\httpd-vhosts.conf へ追加
すでにApache2.4がバーチャルホストで動作している環境へと追加するため、以下の項目を httpd-vhosts.conf
ファイルへと追加します。
... # 既存のWSGIPythonPathに、今回のアプリとvirtualenv環境を追加 # Windowsなので、区切りはセミコロン(;) WSGIPythonPath "...;D:\printer_status_py;D:\printer_status_py/env/Lib/site-packages" <VirtualHost *:80> # DNSのCNAMEで追加した名前 ServerName px105-status ErrorLog "logs/printer-status-error.log" # Flask app WSGIScriptAlias / "D:/printer_status_py/app.wsgi" <Directory "D:/printer_status_py"> <Files app.wsgi> Require all granted </Files> </Directory> </VirtualHost>
あとはApacheのサービスを再起動して、http://px105-status/
にアクセスした時に、PX-105のインク残量が確認できればOKです。
インク残量の定期メール送信
Ruby + Sinatraではrufus-scheduler
を使っていたので、Flaskでも同じことができないかを探したところAPScheduler
がありました。Flask用もあり良さそうでした。
- Advanced Python Scheduler — APScheduler 3.1.0.dev1 documentation
- python - Flask - run function every hour - Stack Overflow
- viniciuschiele/flask-apscheduler: Adds APScheduler support to Flask
ただ、mod_wsgiを使っている環境だとダメそうでした。
python: APScheduler in WSGI app - Stack Overflow
そのため、Windowsのタスクスケジューラーを使って、メール送信スクリプトを定期的に叩くようにしました。なお、スクリプトは以前の記事を参考に作成します。
Python3 + google-api-python-clientで、Gmail APIを使ってメールを送信する - メモ的な思考的な
タスクスケジューラのタスクの作成
にて設定する内容は以下の通りです。
をそれぞれフルパスで指定します。
タブ | 項目 | 値 |
---|---|---|
全般 | 名前 | printer-status (任意) |
全般 | タスクの実行時に使うユーザーアカウント | 任意のアカウント |
全般 | ユーザーがログオンしているかどうかにかかわらず実行する | ●選択 |
全般 | 最上位の特権で実行する | ■チェックする |
トリガー | 開始 | 毎日 |
トリガー | 間隔 | 1日 |
操作 | 操作 | プログラムの開始 |
操作 | プログラム/スクリプト | D:\printer_status_py\env\Scripts\python.exe |
操作 | 引数の追加 | D:\printer_status_py\gmail_reminder.py |
操作 | 開始 | 空白 |
タスクを作り終えたら、タスクを実行して問題なく動作するかを確認します。
ソースコード
GitHubに上げました。
thinkAmi/printer-status-py