📲

ラズパイ+Node-REDで光を検知するとSlackへ通知する

2024/02/22に公開

記事「Node-REDでSlackへ画像を投稿する」と記事「ラズパイ+Node-REDでオムロン製「環境センサ」(USB型)のデータをBLE経由で取得して可視化する」を組み合わせて、パトランプなどの光を検知するとSlackで通知する方法について説明します。
https://zenn.dev/dsl_gunma/articles/99a379c9eee63e
https://zenn.dev/dsl_gunma/articles/5b0bf0d1437de2

Slackの準備

Slackの準備は、以下の記事を参考にしてください。
https://zenn.dev/dsl_gunma/articles/9fc4e58783d7da

今回は、ワークスペース「Node-RED開発」のチャンネル「node-red_alert」に画像を投稿します。

Node-REDによるフローの作成

記事「ラズパイ+Node-REDでオムロン製「環境センサ」(USB型)のデータをBLE経由で取得して可視化する」のフローに、光を検知したときにSlackへメッセージとUSBカメラで撮影した画像を通知するためのフローを作成します。

node-red-contrib-usbcameraのインストール

  1. Node-REDをWebブラウザで開きます。

  2. 右上にある「三」のボタンをクリックしてメニューを開き、「パレットの管理」をクリックします。

  3. 「ノードの追加」タブを選択して、検索のテキストボックスに「usbcamera」と入力します。
    検索されたノードから「node-red-contrib-usbcamera」の「ノードを追加」ボタンをクリックします。

  4. パレットの「Raspberry Pi」グループにノードが追加されます。

node-red-contrib-slackのインストール

  1. 「ノードの追加」タブを選択して、検索のテキストボックスに「slack」と入力します。
    検索されたノードから「node-red-contrib-slack」の「ノードを追加」ボタンをクリックします。

  2. パレットの「ソーシャル」グループにslackのノードが4つ追加されます。

モジュールのインストールと設定

Slackへ画像を投稿するには、画像をStreamオブジェクトで読み込みます。
そのため、Node.jsのfsモジュール(ファイルストリームモジュール)をインストールして、functionノードで利用できるように設定します。

  1. fsモジュールをインストールします。
    Windowsは、コマンドプロンプトを管理者で実行、Raspberry PiなどのLinux系は、ターミナルを実行し、次のコマンドを入力します。
Windows
> npm install fs
Linux
$ cd ~/.node-red
$ npm install fs
  1. settings.jsファイルを編集します。fsライブラリが利用できるように、次の一行を追加します。ファイルの場所は以下のとおりです。
    • Windowsの場合: C:\Users\ユーザー名\.node-red\settings.js
    • Linux系の場合: $HOME/.node-red/settings.js
settings.js
functionGlobalContext: {
+   fs:require('fs'),
    // os:require('os'),
},
  1. Node-REDを再起動します。

ノードの設定

  1. Node-REDのワークスペースに以下のノードを追加し、図のように配置します。

    • switchノード
    • usbcameraノード
    • functionノード
    • slack-web-outノード
    • debugノード

    switchノードの左側のポートは、buffer parserノードの照度の値を取得する3番目のポートに接続します。

  2. 室内の照度は700lx以下なので、環境センサで測定した照度が1000lxより大きくなった場合、光を検知したと判断するようにします。
    switchノードをダブルクリックし、以下の設定をして「完了」ボタンをクリックします。

    • 名前: 「照度 > 1000」と入力
    • 条件: 「>」を選択, 「数値」を選択, 「1000」と入力
  3. usbcameraノードをダブルクリックし、以下の設定をして「完了」ボタンをクリックします。

    • File Name: 「image.jpg」と入力
    • Image Resolution: 「640x480」を選択
  4. functionノードをダブルクリックして、以下のコードを入力します。

functionノード
const fs = global.get('fs');  // fsモジュールを参照
const filepath = "/home/gitc/Pictures/image.jpg";  // 画像ファイルの絶対パス

msg.topic = "files.upload";
msg.payload = {
    "channels": "node-red_alert",
    "initial_comment": "光を検知しました。",
    "filename": filepath,
    "file": fs.createReadStream(filepath),  // 画像ファイルのStreamオブジェクトを生成
    "filetype": "auto"
};

return msg;
  1. slack-web-outノードをダブルクリックして、「新規にslack-configを追加」をクリックします。

    「slack-config」が既に登録されている場合は、その項目を選択してください。

  2. Slackアプリのトークンを貼り付け、「追加」ボタンをクリックします。

  3. 「完了」ボタンをクリックします。

  4. 「デプロイ」ボタンをクリックします。

  5. 完成したフローです。

作成したフローのJSONを以下に貼っておきます。
Node-REDのメニューから「読み出し」->「クリップボード」で配置することができます。
slack-web-outノードのTokenは再設定してください。

[{"id":"72b184ac5563be22","type":"tab","label":"環境センサ Slackデモ","disabled":false,"info":"","env":[]},{"id":"363d4a57b2ed9a17","type":"BLE Beacon Scanner","z":"72b184ac5563be22","name":"","x":140,"y":60,"wires":[["e1d4aa435295cc89","c0e2949e362d921e"]]},{"id":"fac273dd97f36036","type":"debug","z":"72b184ac5563be22","name":"debug 3","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":980,"y":60,"wires":[]},{"id":"e1d4aa435295cc89","type":"switch","z":"72b184ac5563be22","name":"環境センサを取得","property":"payload.id","propertyType":"msg","rules":[{"t":"eq","v":"c5caad021adc","vt":"str"}],"checkall":"true","repair":false,"outputs":1,"x":370,"y":60,"wires":[["bfacc71955c09637","73a2597a8e515b75"]]},{"id":"bfacc71955c09637","type":"change","z":"72b184ac5563be22","name":"データ部分を取出","rules":[{"t":"set","p":"payload","pt":"msg","to":"payload.other","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":590,"y":60,"wires":[["301da99ef43423bb"]]},{"id":"cd1de4fc399da95e","type":"buffer-parser","z":"72b184ac5563be22","name":"","data":"payload","dataType":"msg","specification":"spec","specificationType":"ui","items":[{"type":"int16le","name":"temperature","offset":4,"length":1,"offsetbit":0,"scale":"0.01","mask":""},{"type":"int16le","name":"reelativeHumidity","offset":6,"length":1,"offsetbit":0,"scale":"0.01","mask":""},{"type":"int16le","name":"ambientLight","offset":8,"length":1,"offsetbit":0,"scale":"1","mask":""},{"type":"int32le","name":"pressure","offset":10,"length":1,"offsetbit":0,"scale":"0.001","mask":""},{"type":"int16le","name":"noise","offset":14,"length":1,"offsetbit":0,"scale":"0.01","mask":""}],"swap1":"","swap2":"","swap3":"","swap1Type":"swap","swap2Type":"swap","swap3Type":"swap","msgProperty":"payload","msgPropertyType":"str","resultType":"value","resultTypeType":"return","multipleResult":true,"fanOutMultipleResult":true,"setTopic":true,"outputs":5,"x":370,"y":260,"wires":[["b7640df40e8a0b3f","f8300351b0b6f218","262dd4708fdcec82","6e7e9e99582efe76"],["7f0d63f1ee9786d9","0fa1ba24b04d3d61","9c4a701ecfb3fc17","6e7e9e99582efe76"],["bb3c38892ba60a86","8ae64cf98efe304e","db4497bc6b3a1849","6e7e9e99582efe76","061290898ebb772f"],["765a7db1b202ac55","6e7e9e99582efe76"],["5ece76d44a81c19b","6e7e9e99582efe76"]]},{"id":"6e7e9e99582efe76","type":"debug","z":"72b184ac5563be22","name":"debug 4","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":580,"y":200,"wires":[]},{"id":"301da99ef43423bb","type":"delay","z":"72b184ac5563be22","name":"","pauseType":"rate","timeout":"5","timeoutUnits":"seconds","rate":"1","nbRateUnits":"10","rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":true,"allowrate":false,"outputs":1,"x":800,"y":60,"wires":[["cd1de4fc399da95e","fac273dd97f36036"]]},{"id":"7f0d63f1ee9786d9","type":"ui_text","z":"72b184ac5563be22","group":"4e3a2cc5ba267455","order":2,"width":0,"height":0,"name":"湿度","label":"湿度","format":"{{msg.payload}} %","layout":"row-spread","className":"","x":710,"y":340,"wires":[]},{"id":"bb3c38892ba60a86","type":"ui_text","z":"72b184ac5563be22","group":"4e3a2cc5ba267455","order":3,"width":0,"height":0,"name":"照度","label":"照度","format":"{{msg.payload}} lx","layout":"row-spread","className":"","x":710,"y":420,"wires":[]},{"id":"765a7db1b202ac55","type":"ui_text","z":"72b184ac5563be22","group":"4e3a2cc5ba267455","order":5,"width":0,"height":0,"name":"気圧","label":"気圧","format":"{{msg.payload}} hPa","layout":"row-spread","className":"","x":710,"y":460,"wires":[]},{"id":"5ece76d44a81c19b","type":"ui_text","z":"72b184ac5563be22","group":"4e3a2cc5ba267455","order":6,"width":0,"height":0,"name":"騒音","label":"騒音","format":"{{msg.payload}} dB","layout":"row-spread","className":"","x":710,"y":500,"wires":[]},{"id":"f8300351b0b6f218","type":"ui_gauge","z":"72b184ac5563be22","name":"温度","group":"11b4750a8d281283","order":1,"width":0,"height":0,"gtype":"gage","title":"guage","label":"℃","format":"{{value}}","min":0,"max":"50","colors":["#00b500","#e6e600","#ca3838"],"seg1":"","seg2":"","diff":false,"className":"","x":850,"y":260,"wires":[]},{"id":"262dd4708fdcec82","type":"ui_chart","z":"72b184ac5563be22","name":"気温","group":"11b4750a8d281283","order":2,"width":0,"height":0,"label":"chart","chartType":"line","legend":"false","xformat":"HH:mm","interpolate":"linear","nodata":"","dot":false,"ymin":"0","ymax":"50","removeOlder":"1","removeOlderPoints":"","removeOlderUnit":"3600","cutout":0,"useOneColor":false,"useUTC":false,"colors":["#1f77b4","#aec7e8","#ff7f0e","#2ca02c","#98df8a","#d62728","#ff9896","#9467bd","#c5b0d5"],"outputs":1,"useDifferentColor":false,"className":"","x":990,"y":260,"wires":[[]]},{"id":"0fa1ba24b04d3d61","type":"ui_gauge","z":"72b184ac5563be22","name":"湿度","group":"a2c107024ae69f17","order":1,"width":0,"height":0,"gtype":"gage","title":"gauge","label":"%","format":"{{value}}","min":0,"max":"100","colors":["#00b500","#e6e600","#ca3838"],"seg1":"","seg2":"","diff":false,"className":"","x":850,"y":340,"wires":[]},{"id":"9c4a701ecfb3fc17","type":"ui_chart","z":"72b184ac5563be22","name":"湿度","group":"a2c107024ae69f17","order":2,"width":0,"height":0,"label":"chart","chartType":"line","legend":"false","xformat":"HH:mm","interpolate":"linear","nodata":"","dot":false,"ymin":"0","ymax":"100","removeOlder":"1","removeOlderPoints":"","removeOlderUnit":"3600","cutout":0,"useOneColor":false,"useUTC":false,"colors":["#1f77b4","#aec7e8","#ff7f0e","#2ca02c","#98df8a","#d62728","#ff9896","#9467bd","#c5b0d5"],"outputs":1,"useDifferentColor":false,"className":"","x":990,"y":340,"wires":[[]]},{"id":"b7640df40e8a0b3f","type":"ui_text","z":"72b184ac5563be22","group":"4e3a2cc5ba267455","order":1,"width":0,"height":0,"name":"温度","label":"気温","format":"{{msg.payload}} ℃","layout":"row-spread","className":"","x":710,"y":260,"wires":[]},{"id":"8ae64cf98efe304e","type":"ui_gauge","z":"72b184ac5563be22","name":"照度","group":"d55fded6aebe3ac8","order":1,"width":0,"height":0,"gtype":"gage","title":"gauge","label":"lx","format":"{{value}}","min":0,"max":"2000","colors":["#00b500","#e6e600","#ca3838"],"seg1":"","seg2":"","diff":false,"className":"","x":850,"y":420,"wires":[]},{"id":"db4497bc6b3a1849","type":"ui_chart","z":"72b184ac5563be22","name":"照度","group":"d55fded6aebe3ac8","order":2,"width":0,"height":0,"label":"chart","chartType":"line","legend":"false","xformat":"HH:mm","interpolate":"linear","nodata":"","dot":false,"ymin":"0","ymax":"2000","removeOlder":"1","removeOlderPoints":"","removeOlderUnit":"3600","cutout":0,"useOneColor":false,"useUTC":false,"colors":["#1f77b4","#aec7e8","#ff7f0e","#2ca02c","#98df8a","#d62728","#ff9896","#9467bd","#c5b0d5"],"outputs":1,"useDifferentColor":false,"className":"","x":990,"y":420,"wires":[[]]},{"id":"c0e2949e362d921e","type":"debug","z":"72b184ac5563be22","name":"debug 1","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":340,"y":120,"wires":[]},{"id":"73a2597a8e515b75","type":"debug","z":"72b184ac5563be22","name":"debug 2","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":540,"y":120,"wires":[]},{"id":"061290898ebb772f","type":"switch","z":"72b184ac5563be22","name":"照度 > 1000","property":"payload","propertyType":"msg","rules":[{"t":"gt","v":"1000","vt":"num"}],"checkall":"true","repair":false,"outputs":1,"x":430,"y":560,"wires":[["eb07eafaada4792a"]]},{"id":"eb07eafaada4792a","type":"usbcamera","z":"72b184ac5563be22","filemode":"1","filename":"image.jpg","filedefpath":"1","filepath":"","fileformat":"jpeg","resolution":"2","name":"","x":610,"y":560,"wires":[["c599dc197cd9df48"]]},{"id":"c599dc197cd9df48","type":"function","z":"72b184ac5563be22","name":"画像をSlackに投稿","func":"const fs = global.get('fs');  // fsモジュールを参照\nconst filepath = \"/home/gitc/Pictures/image.jpg\";  // 画像ファイルの絶対パス\n\nmsg.topic = \"files.upload\";\nmsg.payload = {\n    \"channels\": \"node-red_alert\",\n    \"initial_comment\": \"光を検知しました。\",\n    \"filename\": filepath,\n    \"file\": fs.createReadStream(filepath),  // 画像ファイルのStreamオブジェクトを生成\n    \"filetype\": \"auto\"\n};\n\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":810,"y":560,"wires":[["64270b827585f447"]]},{"id":"b7b6038b9ceb0229","type":"debug","z":"72b184ac5563be22","name":"debug 5","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":1200,"y":560,"wires":[]},{"id":"64270b827585f447","type":"slack-web-out","z":"72b184ac5563be22","client":"","name":"","x":1020,"y":560,"wires":[["b7b6038b9ceb0229"]]},{"id":"4e3a2cc5ba267455","type":"ui_group","name":"計測データ","tab":"b1d9caa0c4795482","order":1,"disp":true,"width":"6","collapse":false,"className":""},{"id":"11b4750a8d281283","type":"ui_group","name":"気温","tab":"b1d9caa0c4795482","order":2,"disp":true,"width":"6","collapse":false,"className":""},{"id":"a2c107024ae69f17","type":"ui_group","name":"湿度","tab":"b1d9caa0c4795482","order":3,"disp":true,"width":"6","collapse":false,"className":""},{"id":"d55fded6aebe3ac8","type":"ui_group","name":"照度","tab":"b1d9caa0c4795482","order":4,"disp":true,"width":"6","collapse":false,"className":""},{"id":"b1d9caa0c4795482","type":"ui_tab","name":"環境センサ","icon":"dashboard","order":4,"disabled":false,"hidden":false}]

動作確認

ラズベリーパイにUSBカメラを接続して、画像を撮影したい場所に配置します。

パトランプなどに環境センサが光を検知するように取り付けます。パトランプが点灯して光を検知すると照度が1000以上になるので、USBカメラの画像を撮影して、Slackにメッセージを送信します。
Slackアプリで、光を検知した時刻とUSBカメラで撮影した画像を確認することができます。

Discussion