SenseCAP WatcherのHTTP Push Notificationを試してみた

Seeed K.K.の中井です。

最近の展示会やイベントなどでSenseCAP Watcherを展示してきましたが、皆さんの反応は良く、AIを手軽に使えるデバイスということに興味を持っていただけているのかなぁと感じています。

そんなSenseCAP Watcherですが、様々な状況を判断し登録したタスクにマッチした状況となればスマホに通知が出来るのですが、専用のアプリ(SenseCraft APP)だけではなく任意のSNSやサービスにもイベントを通知することもできるようです。

私も実際に他のSNSやサービスに通知したことがなかったので、試してみましたよという記事になります。(デバイスを直接制御することができたりするようですがそれはまた別の機会に試したいと思っています)

SenseCAP Watcherは、世界初(?)のフィジカルAIエージェントという製品です。Watcherの"目"(=カメラOV5647)が状況を監視し 特定の状況 を確認した場合に アクション することができるデバイスとなっています。製品の特長と言いますか概要を簡単に説明すると、"特定の状況"や"アクション"(これらをまとめて タスク と表現します)を自然言語やプッシュトーク(※英語のみ対応)で登録することができ、タスク実行に必要となるAIモデルやフローが自動的にデプロイされ、基本的な使用方法であればノーコードでAIを体験・業務利用することができる、というものです。

自然言語でAI??、、そうです。簡単に言うと「チャット感覚でやらせたいことを登録してアプリに知らせることができる」という感じでしょうか。これだけだとまさに万能のデバイスと思われるかもしれませんが、苦手なことも色々あります。詳しくは、公式Wikiにまとめられていますのでそちらをお読みください。

wiki.seeedstudio.com

さて、前置きが長くなってしまいましたが、SenseCAP WatcherのHTTP Push Notificationを試していきたいと思います。公式WikiではHTTP Push Notificationを使って他のSNSやサービスと連携する場合にはNode-REDを用いたインテグレーションを推奨しています。

wiki.seeedstudio.com

Node-REDサーバーを構築して、、サーバー管理したくない人がほとんどだと思いますので、 今回はNode-REDが統合されているenebularを利用してみます。Webhookが使える実行環境が無償で利用できる点もenebularを選んだ理由の1つです。

www.enebular.com

enebularをセットアップする前に、まずはWatcherのHTTP Push Notificationがどのような振る舞いをするのか確認してみました。

HTTP Push Notification

Watcherがどのような通知を行うのか、実際に受信してみて確認してみました。検証には下記のAPIサーバを簡易的に用意しました。

#
# main.py
#
from fastapi import FastAPI, Request
import logging

logger = logging.getLogger('uvicorn')
app = FastAPI()

@app.post("/v1/notification/{path}")
async def get_event(req: Request):
    logger.info("### POST:/vi/notification/{path}")
    logger.info(f"  Path: {req.path_params}")
    logger.info(f"  Query Params: {req.query_params}")
    logger.info(f"  HEADER: {req.headers}")
    logger.info(f"  BODY: {await req.body()}")
    return {"Result": "OK"}
PS C:\Users\nakai\Documents\my_api_server> uvicorn main:app --host 192.168.1.114 --port 8000
INFO:     Started server process [18996]
INFO:     Waiting for application startup.
INFO:     Application startup complete.
INFO:     Uvicorn running on http://192.168.1.114:8000 (Press CTRL+C to quit)

APIサーバを用意した後、SenseCAP側を設定。タスクにはSenseCraft APPでWatcherをタップし、Assign Task画面に標準(?)で表示されるif there is a person, device plays sound 'Hi, I'm watcher, an AI bot'。 Detail Configsに入ってHTTP Push Notificationにチェックを入れ、Go Setupで通知先(HTTP URL)を指定します。もちろん先ほど用意したAPIサーバを指定。HTTP Token にはとりあえずダミーの文字列をいれておきました。

Run TaskしてWatcherが人物を検出すると次のように通知されました。

INFO:     ### POST:/vi/notification/{path}
INFO:       Path: {'path': 'event'}
INFO:       Query Params:
INFO:       HEADER: Headers({'user-agent': 'ESP32 HTTP Client/1.0', 'host': '192.168.1.114:8000', 'content-type': 'application/json', 'authorization': 'Asdfghk', 'api-obiter-device-eui': '2CF7F1C96270009D', 'content-length': '10523'})
INFO:       BODY: b'{"requestId":"5b5ea8ae-36c3-4974-845f-40e56a6ebe2c","deviceEui":"2CF7F1C96270009D","events":{"timestamp":1734488354199,"text":"Hi, I\'m watcher, an AI bot","img":"~base64でエンコードされた画像データ~","data":{"inference":{"boxes":[[172,253,340,308,71,0]],"classes_name":["person"]}}}}'
INFO:     192.168.1.127:63124 - "POST /v1/notification/event HTTP/1.1" 200 OK
{
  "user-agent": "ESP32 HTTP Client/1.0",
  "host": "192.168.1.114:8000",
  "content-type": "application/json",
  "authorization": "Asdfghk",
  "api-obiter-device-eui": "2CF7F1C96270009D",
  "content-length": "10523"
}
Body
{
  "requestId":"5b5ea8ae-36c3-4974-845f-40e56a6ebe2c",
  "deviceEui":"2CF7F1C96270009D",
  "events":{
    "timestamp":1734488354199,
    "text":"Hi, I\'m watcher, an AI bot",
    "img":"~base64でエンコードされた画像データ~",
    "data":{
      "inference":{
        "boxes":[
          [172,253,340,308,71,0]
        ],
        "classes_name":[
          "person"
        ]
      }
    }
  }
}

HeaderにはHTTP Tokenで指定したキーが”authorization”として渡されています。Bodyに"deviceEui"があるので複数のWatcherを利用したとしても識別ができるようになっていますね。events.data.inference.boxesには対象検出時の画像内のボックス座標とAccuracy、Classification IDが確認できます。

ただ、JSONのフォーマットが決まっているので直接外部サービスに送信して使える、というようにはいかなそうです。

インテグレーション

今回はお試しなのでアカウント設定やインテグレーション確認用に設定済みのslackを用いてSenseCAP Watcherのイベントを通知するようにしました。

slackにメッセージを送るためにはslack appを用意する必要があります。この記事では「slack appの作成」、「ワークスペースへのインストール」、「チャンネルへslack appを追加」などの設定方法については割愛しますので、下記などを参考にしてください。

zenn.dev

必要なのは、特定のワークスペース/チャンネルにファイルをアップロードできるappそのappが組み込まれたチャンネルがあれば試すことができます。

enebular

enebularではWatcherのイベントを受け取りslackに転送するフローの作成とその実行環境を作成します。

まずはフローの作成から。プロジェクトを選択してアセットを作成。

デバッグ方法の確認

編集画面に移動して、とりあえずWatcherのイベントの受け口としてhttp inノード、レスポンスを返すためのhttp responseノード、レスポンスを設定するためのfunctionノードを配置して繋ぎます。

事前に確認した通知の内容から、http inはPOSTメソッドを、URLに/vi/notification/eventを設定します。http responseは"200"を返しておくようにします。functionは必要ないかもしれませんがとりあえず"OK"でも返しておきます。debugノードをつけておくとWatcherの通知時のペイロードを確認することができるようになります。

http in http response function

とりあえずここまでのフローはこんな感じです。

一旦、フローをデバッグする方法の確認。フローエディタの右上にある"i"のアイコンにカーソルを合わせるとインスタントURLを確認することができるので、SenseCraft APPでHTTP URLに設定。Watcherで自分を検出させると通知が行われ、enebularが通知を受け取り、デバッグメッセージに通知時のペイロードが表示されます。

画像をデコード

Watcherの通知時のペイロード(Notification Payload)には画像データが含まれていることは確認済みです。それをSlackに添付したいのですが、slackはBase64エンコードされたデータを受け取ったとしても自動的にデコードして画像表示することができないので、slackに送る前にデコードします。

Node-REDでBase64を扱うには追加のライブラリが必要のようです。メニューからパレットの管理を選択して、ノードを追加でnode-red-node-base64ライブラリを追加します。base64で検索すると最上位に出てきました。

とりあえずbase64ノードを配置しておき、バッファ<->Base64の変換、プロパティにはpayload.events.imgを設定しておきます。

因みにnode-red-contrib-image-outputライブラリのimageノードを使うとフローエディタ上に画像を表示することができます。Base64でエンコードされたデータでもデコードした後でもどちらでもプレビュー表示することができます。

note. 画像データはJPEGフォーマット

Base64でエンコードされた画像データをデコード後に確認してみると、FF D8 FF E0で始まっているのでJPEG画像ということがわかりました。

参考. JPEG File Interchange Format - Wikipedia

slack

Node-REDにはslack用のライブラリnode-red-contrib-slackも用意されているのでパレットに追加します。

追加するとパレットのソーシャルグループにいくつかのノードが追加されます。今回はslackにメッセージを送信したいのでslack-web-outノードを選択して配置します。このノードのSlack Clientには、メッセージを送るワークスペースにインストールしたslack appのOAuth Tokenを指定して追加します。

note. slack appのOAuth Tokenの取得

OAuth Tokenは、slack apiYour Appsに移動して、対象のAppを選択して表示されるメニューのOAuth & Permissions(または、Install App)から取得することができます。

続いて上記で設定したslack appに対して実行するAPIの設定をfunctionノードで行います。今回は画像とテキストを送信したいので、files.uploadメソッドを利用します。msg.topicにメソッドを設定し、msg.payload.channelsには送信対象のチャンネル名またはチャンネルIDを設定します。functionノードはslack-web-outの前に接続します。

msg.topic = "files.upload";
msg.payload = {
    "channels": "**channel names or IDs**",
    "initial_comment": msg.payload.events.text,
    "filename": "image",
    "file": msg.payload.events.img,
    "filetype": "auto"
};
return msg;

フローの動作確認

ここまでに作成したフローでは、

  • WatcherからPOSTリクエストを受ける: http in
  • Base64エンコードされた画像データをデコード: base64
  • files.uploadメソッドのパラメータを設定: function
  • slackに送信: slack-web-out

という流れで処理が行われるように設定してきました。ここで一旦動作確認しておきます。 先ほどと同様にインスタントURLをSenseCraft APPで設定して自分に向けると、、

無事slackにメッセージが飛んできました!

最後にクラウド実行環境の設定

インスタントURLですと短期的に利用はできるのですが、(4時間ほど)時間が経つと使えなくなってしまいます。恒久的にフローが動作するように実行環境を作成してそこにデプロイしてあげます。

enebularにはLow-Code Development Platform(LCDP)というのが用意されていて、定期実行が可能なスケジュールトリガーやHTTPトリガーなどを設定してフローを実行することができるクラウド環境です。

フリープランでも2つの実行環境を作成でき、月当たり24時間稼働させることができます。今回のフローは1回あたり1分程度の稼働なので、24時間も稼働できれば十分かなと思います。

早速、クラウド実行環境の設定をしていきます。 作成方法などは公式のドキュメントを参照してください。

名称を入力すると1~2分待てば実行環境が出来上がります。(簡単w)

続いてSenseCAP WatcherのNotificationの受け口、HTTPトリガーを設定します。

クラウド実行環境の準備はこれだけなので、フローをクラウド実行環境上で利用できるように修正します。公式のドキュメントに記載があるようにLCDP inLCDP outノードで挟み込んであげればよいだけなので、http inノードをLCDP inノードに置き換え、slack-web-outノードの後ろにLCDP outノードを付け加えてあげます。また、http responseなどは不要になるので削除、デバッグもできなさそう(?)なので削除しています。

このフローをクラウド実行環境にデプロイして完成です。

まとめ

  • SenseCAP WatcherのHTTP Push Notificationで外部サービスと連携する場合に指定できるのは、URLとTokenのみ
  • Notificationに含まれる画像データはBase64でエンコードされている
  • enebular(Node-RED)を使うとインテグレーションが容易になる
  • enebularはクラウド実行環境が用意されていてフリーで利用が可能 (素晴らしい)

今回作成したフローは公開しましたので、テンプレートとしてお使いください。使用される場合には送信対象のslackチャンネルとTokenの設定が必要となります。

参考リンク

変更履歴

日付 変更者 変更内容
2024/12/25 mnakai 作成