プリンタのインク残量が低下したらSlackに通知する方法

IoT(Internet of Things, モノのインターネット)という言葉が存在感を増し続けている今日この頃ですが、ネットワーク対応のプリンタはIoT機器の元祖とも呼べる存在です。
そのネットワーク機能を使ってちょっとした業務改善をしてみました。

課題

プリンタを使おうとしたときに、インク切れですぐに印刷を始められないという状況を無くしたい。

印刷しようとしたときにインクが切れてるとイラッとしますよね。

対策

プリンタのネットワーク機能を使ってインクの残量を監視して、一定量よりも少なくなったらSlackで通知します。
監視用のサーバとして、当社の勤怠システムでも使っているRaspberry Piを利用します。

調査

オフィスで使っているプリンタはCanonのMG7530です。
問題はどうやってインクの残量を取得するかというところです。
初めはMG7530のLinux用ドライバをRaspberry Piにインストールしてそこからなんとか取得できないかと考えたのですが、どうやらこのドライバではインクの残量はサポートしていないようです。

そこで、MG7530のウェブインタフェースから取得することを考えました。
プリンタのIPアドレスにHTTPアクセスすると以下のような管理用ウェブUIが利用できます。

しかしながら、該当ウェブページのソースを見てもインク残量のデータそのものは含まれておらず、単純なスクレイピングはできそうもありません。

                <div class="InkPattern" id="inktank0">
                    <div class="InkMess"></div>
                    <div class="InkArea"><div class="InkBar"></div></div>
                </div>
                <div class="InkPattern" id="inktank1">
                    <div class="InkMess"></div>
                    <div class="InkArea"><div class="InkBar"></div></div>
                </div>
                <div class="InkPattern" id="inktank2">
                    <div class="InkMess"></div>
                    <div class="InkArea"><div class="InkBar"></div></div>
                </div>
                <div class="InkPattern" id="inktank3">
                    <div class="InkMess"></div>
                    <div class="InkArea"><div class="InkBar"></div></div>
                </div>
                <div class="InkPattern" id="inktank4">
                    <div class="InkMess"></div>
                    <div class="InkArea"><div class="InkBar"></div></div>
                </div>
                <div class="InkPattern" id="inktank5">
                    <div class="InkMess"></div>
                    <div class="InkArea"><div class="InkBar"></div></div>
                </div>
                <div class="InkPattern" id="inktank6">
                    <div class="InkMess"></div>
                    <div class="InkArea"><div class="InkBar"></div></div>
                </div>

これはどうやらJavaScriptで値を取得して埋めてるパターンだろうということでブラウザの開発ツールを開いてしばらく見張っていると、prninfo_data.cgiというCGIに定期的なアクセスが発生していることが分かりました。

prninfo_data.cgiリクエストへの応答はこんな感じ。

<?xml version="1.0" ?>
<response>
<PAGE_PRNINFO>
<INKREST0>0,0,0</INKREST0>
<INKREST1>5,6,0</INKREST1>
<INKREST2>4,2,0</INKREST2>
<INKREST3>1,2,0</INKREST3>
<INKREST4>2,7,0</INKREST4>
<INKREST5>3,3,0</INKREST5>
</PAGE_PRNINFO>
(略)

1桁目は全て異なる数値になっているのでインクの種類だろうということは想像できます。
2桁目はインク残量っぽいけど数値の意味がはっきりしません。3桁目はなんだろう?

データの意味を調べるためにprninfo_data.cgiを呼び出しているJavaScriptのファイルを調べたところ、こんな関数を発見しました。

function dispInkInfo(inktank_info, idStr) {

    var inkLVL = ['Lv100', 'Lv090', 'Lv080', 'Lv070', 'Lv060', 'Lv050', 'Lv040', 'Lv030', 'Lv020', 'Lv010', 'Lv000', 'Lv00X'];
    var inkMRK = ['InkMess', 'InkMess LwInk', 'InkMess NoInk', 'InkMess UnInk'];

    var inkCOL = ['InkBlk', 'InkPbk', 'InkCia', 'InkMaz', 'InkYel', 'InkGry', 'InkClr', 'InkBlk', 'InkCMYK'];

この関数の後ろの方を読むと、たとえば、<INKREST1>5,6,0</INKREST1>というデータはinkCOL[5](‘inkGry’), inkLVL[6](‘Lv030’), inkMRK[0](‘InkMess’)という風に使われるようです。
インク残量の画像と比べてみると、グレイが30%くらいになっているのでこれで合ってそうですね。

ということは、CGIの応答の3桁目が1(‘InkMess LwInk’)になっていたら通知すれば良さそうです。

実装

というわけで監視用のスクリプトを実装しました。

#!/bin/bash -eu

PRINTER_IP=X.X.X.X

function post_message() {
    check_dir=/home/pi/printer_ink_monitor
    check_file=POSTED

    exists=`find $check_dir -name $check_file -mtime -1|wc -l`
    if [ $exists -ne 0 ]; then
	return
    fi
    touch $check_dir/$check_file
    curl -X POST --data-urlencode 'payload={"channel": "#channel1", "username": "MG7530", "text": "プリンタのインク残量が僅かになりました", "icon_emoji": ":printer:"}' https://hooks.slack.com/services/XXX/YYY/ZZZ
}


xml=`curl -m 2 -s "http://${PRINTER_IP}/rui/prninfo_data.cgi" -H 'Content-Type: application/x-www-form-urlencoded' -H 'Authorization: Basic HOGEHOGE' --data 'GETINFO=0'`

result=`echo $xml | /home/pi/printer_ink_monitor/parsrx.sh |grep INKREST |cut -d, -f3`
for status in $result; do
    # 0: normal, 1: Low Ink, 2: No Ink, 3: Un Ink(?)
    if [ $status -eq 1 ]; then
	post_message
    fi
done

parsrx.shでXMLのデータを行指向のデータに変換しているので、以下のようにしてインク残量のデータだけを取り出せます。
この数値がどれか1個でも1になっていたら通知するようにしています。

$ curl -m 2 -s "http://X.X.X.X/rui/prninfo_data.cgi" -H 'Content-Type: application/x-www-form-urlencoded' -H 'Authorization: Basic HOGEHOGE' --data 'GETINFO=0' | bash printer_ink_monitor/parse_xml.sh | grep INKREST | cut -d',' -f3
0
0
0
0
0
0

また、通知が連続するとうっとうしいことになるので、1日1回だけ送るようにしています。

テスト

インク切れの状態はなかなか作り出せないので、上のスクリプトをいじってインク残量が0だったら通知するようにしてテスト実行してみました。

通知来た!

まとめ

完成したので毎分チェックするようにcronを設定しました。(毎分なのは、プリンタは使う時だけ電源を入れるルールなので、電源が入っているタイミングを逃さないためです)

先日インク切れが発生し通知がきたので、無事印刷をする前にインク交換をすることができました。

Related Post