BluetoothプッシュボタンをFIWAREで扱えるようにし、外からスマートロックを操作する「開けゴマボタン」を作る


**「FIWAREでスマートホームする」** シリーズ第二弾として、Bluetoothプッシュボタン(いわゆるIoTボタン)をFIWAREに対応させて、それをトリガーにスマートロックを開けゴマするように設定してみました。

# できること
Bluetoothプッシュボタンの押下をトリガーに、スマートロックを操作できます。ブッシュボタンの動作結果はコンテキストブローカーに反映されるので、それをNGSIで参照(Subscription)することで、Slackに通知を出したり等、様々な連携動作を実現できます。

# 設定方法
## 前提

<dl>
<dt>FIWARE環境がある（Context Brokerが動いている）</dt>
<dd>さくらのクラウドなどクラウド上に環境構築しても良いですし、ローカルマシン上で環境を作ることもできます。今回はさくらのクラウド上に環境が構築されていることにしています。</dd>
<dt>スマートロックが設置・稼働している</dt>
<dd>私が書いた以下の記事内容に準じて、セサミスマートロックがすでにNGSIで操作できる状態になっていることを前提にします。</dd>
</dl>

https://qiita.com/kikuzo/items/5e137e303ced21dbf2c1

<dl>
<dt>Bluetoothプッシュボタンを入手済み</dt>
<dd>100円ショップ等で入手できます。私は200円で買いました。</dd>
<dt>Raspberry Piを動作させる</dt>
<dd>プッシュボタンのレシーバーおよびNGSIデータ送信にラズパイを利用します。私はラズパイ3を利用していますが、4やZero系でも大丈夫（なはず）。</dd>
</dl>

## Bluetoothプッシュボタンの準備（ラズパイの設定）
プッシュボタン押下状態を受信してFIWARE環境にNGSIでデータを送信するようにする設定から始めます。
プッシュボタンを入手した上で、ラズパイに設定を投入していきます。ラズパイはすでにOSをインストールして、ネットワークに接続して動作している状態であるとします。

なおこの話は、しばらく前に流行った「IoTボタンを使おう」でのやり方をベースにしています。この記事などが参考になります。
- [Bluetoothシャットダウンボタンを作る #300円でIoTボタン](https://qiita.com/nori-dev-akg/items/96584d9591d329f9dcb2 "Bluetoothシャットダウンボタンを作る #300円でIoTボタン")


### ruby環境をインストール
まず、以下で使うBluebuttonがrubyで書かれているので、ruby環境を入れます。
```bash:
$ sudo apt-get install ruby
```
### Bluebuttonをインストール
[Bluebutton](https://github.com/kinnalru/bluebutton "Bluebutton")というとても便利なプログラムがあります。Bluetoothの状態を読み取って任意のコマンドを実行できます。これが肝というかほぼ全てなのです。
```bash:
$ sudo gem install bluebutton
```
### プッシュボタンのペアリング設定
一番最初に一度だけ実行します。
```bash:
$ bluetoothctl
[bluetooth]$ scan on
[NEW] Device XX:XX:XX:XX:XX:XX BT Shutter
[bluetooth]$ pair XX:XX:XX:XX:XX:XX
[CHG] Device XX:XX:XX:XX:XX:XX Paired: yes
Pairing successful
[BT Shutter            ]$ trust XX:XX:XX:XX:XX:XX
[CHG] Device FF:FF:1D:14:79:80 Trusted: yes
[BT Shutter            ]$ quit
```
この内容は、↑で紹介した記事からお借りしました。バージョン等によって少し表示は異なるかもしれません。
要するに、scan onしてプッシュボタンを電源ONにして探し、そのアドレスでpairして、trustしておきます。

### NGSI Goコマンドのインストール
ここからすこしFIWARE(NGSI)に特有の設定になります。

プッシュボタン動作をトリガーにNGSIでメッセージを送信するために、NGSI Goコマンドを利用します。NGSI Goコマンドは、NGSIの様々な操作をコマンドラインで簡単に実施できるようにするためのサポートコマンドです。
 
https://github.com/lets-fiware/ngsi-go/

インストールはとても簡単です。
```bash
$curl -OL https://github.com/lets-fiware/ngsi-go/releases/download/v0.12.0/ngsi-v0.12.0-linux-arm.tar.gz
```
インストールできたら、自身のFIWARE環境で利用するための設定を投入します。
#### FIWARE環境をFIWARE Big-Bangで構築している場合
NGSI Goの設定を自動でするためのスクリプトが提供されているのでそれを利用します。

FIWARE環境を構築したホストにログインしてください。
![スクリーンショット 2023-03-29 11.55.46.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/172921/c3dc27c6-b97b-ab10-5b94-efbaa07457d8.png)

こんな感じで、FIWARE Big-Bangを実行したディレクトリ内に **setup_ngsi_go.sh** というスクリプトが作成されていますので、それをラズパイ環境にコピーしてきます（scpを利用するか、あるいは大した分量ではないので、画面に表示させてコピペしてもOKです！）。

ラズパイ側にコピー(あるいは作成)できたら、スクリプトを実行します。
```bash:
$ sh ./setup_ngsi_go.sh
```
実行すると、FIWARE環境のAdminユーザのID(メールアドレス)とパスワードが聞かれますので、それを入力します。（IDとパスワードはFIWARE環境側で```$ make admin```コマンドで表示させられます。このあたりの詳細は [FIWARE Big-Bangドキュメント](https://fi-bb.letsfiware.jp/ja/system_administration/ 
"FIWARE Big-Bangドキュメント")を参照してください。）

設定完了したら、試しにFIWARE環境のorionのバージョンを取得してみましょう。
```bash:
pi@raspberrypi:~ $ ngsi version
{
"orion" : {
  "version" : "3.8.0",
  "uptime" : "20 d, 2 h, 4 m, 24 s",
  "git_hash" : "18d3634afeb366c62af90b9219c5ffac8e894cad",
  "compile_time" : "Thu Jan 12 12:06:24 UTC 2023",
  "compiled_by" : "root",
  "compiled_in" : "bb473a3d4230",
  "release_date" : "Thu Jan 12 12:06:24 UTC 2023",
  "machine" : "x86_64",
  "doc" : "https://fiware-orion.rtfd.io/en/3.8.0/",
  "libversions": {
     "boost": "1_74",
     "libcurl": "libcurl/7.74.0 OpenSSL/1.1.1n zlib/1.2.12 brotli/1.0.9 libidn2/2.3.0 libpsl/0.21.0 (+libidn2/2.3.0) libssh2/1.9.0 nghttp2/1.43.0 librtmp/2.3",
     "libmosquitto": "2.0.15",
     "libmicrohttpd": "0.9.73",
     "openssl": "1.1",
     "rapidjson": "1.1.0",
     "mongoc": "1.23.1",
     "bson": "1.23.1"
  }
}
}
pi@raspberrypi:~ $
```
こんな感じに表示されればOKです。失敗している場合はエラーになります。

:::note warn
なお、FIWARE Big-Bang v0.24.0以前は、このsetup_ngsi_go.shのジェネレータ部分にバグが有り、正しいスクリプトが生成されません。("https"となっているはずの部分が"ttps"となっている。)　スクリプトを手動で修正するか、NGSI Goの設定を手動で実施する必要があります。
:::

#### FIWARE環境がFIWARE Big-Bang由来ではない場合
ご自身でNGSI Goの設定をする必要があります。[このあたり](https://ngsi-go.letsfiware.jp/management/broker/ "ngsi go broker setting")を参考にしていただければ。認証系を入れていない場合は簡単で、難しいことはありません。

### プッシュボタン押下時の設定
プッシュボタンが押されたら、NGSIのメッセージが発行されてコンテキストブローカーに状態が反映されるようにします。
Bluebuttonの設定ファイルを以下のように書きます。
```bash:bluebutton.config
keyup=echo UP
keydown=sh /home/pi/bluebutton/send_upsert.sh
longup=echo LONG UP
longdown=echo LONG DOWN
```
keyupやlongup/downは使っていないのでいじっていません。
そしてsend_upsert.shスクリプトの内容は以下です。
```bash:send_upsert.sh
#!/bin/sh
/usr/local/bin/ngsi --batch upsert entity --host orion.example.com --data '{"id":"urn:ngsi-ld:Button:001", "type":"Button", "value":{"type":"Integer", "value":0}}'
```
このように設定することにより、プッシュボタンが押下されて（離された）ときに、ngsiコマンドが実行されて、FIWARE環境上（コンテキストブローカー上）のurn:ngsi-ld:Button:001が更新されます。（実際にはvalueの値は変わらないのですが、更新があった、という情報がコンテキストブローカー内を流通します。）
なお、特に複数のFIWARE環境を併用しているような場合は、ngsiコマンドにおいて、```--host orion.example.com```といった具合にターゲットとなるアクセス先を明示するようにしてください。そうしないと、ngsiコマンドで別のFIWARE環境にアクセスした後にこのスクリプトが実行されたときに、予期せぬ動作になってしまいます。（更にいうと、FIWAREServiceとFIWAREServicePathも明に指定したほうがいいです。）

### 常時起動設定
ラズパイをリブートしてもBluebuttonが自動的に起動するように設定しておきます。
いくつかやり方があるのですが、ここではcrontabに設定する方法を使います。
```bash:crontab -l
# Edit this file to introduce tasks to be run by cron.
#
# Each task to run has to be defined through a single line
# indicating with different fields when the task will be run
# and what command to run for the task
#
# To define the time you can provide concrete values for
# minute (m), hour (h), day of month (dom), month (mon),
# and day of week (dow) or use '*' in these fields (for 'any').
#
# Notice that tasks will be started based on the cron's system
# daemon's notion of time and timezones.
#
# Output of the crontab jobs (including errors) is sent through
# email to the user the crontab file belongs to (unless redirected).
#
# For example, you can run a backup of all your user accounts
# at 5 a.m every week with:
# 0 5 * * 1 tar -zcf /var/backups/home.tgz /home/
#
# For more information see the manual pages of crontab(5) and cron(8)
#
# m h  dom mon dow   command
@reboot /usr/local/bin/bluebutton -d='BT Shutter' -c /home/pi/bluebutton/bluebutton.config &
```
crontab設定で@rebootという書き出しにすると、リブート時にコマンドを実行してくれるんですね。（実は今回調べて初めて知りました！）

注意点として、Bluebuttonはpiユーザ権限（正確には、NGSI Goをインストールしたユーザ）で動かす必要があります。そのために今回はpiユーザのcrontabで設定しています。/etc/rc.localを使う方法の場合はそのままではroot権限で動いてしまいますので、suコマンドでpiユーザに変更してからBluebuttonを起動するなどの設定をする必要があります。

以上でラズパイ側の設定は終了です。

## FIWARE環境側の設定
プッシュボタンからのデータの更新を受けて、それをトリガーにして、スマートロックを解錠する設定を追加します。

スマートロックの解錠は、以下の記事を参考にもう実現できている状態であるとします。

https://qiita.com/kikuzo/items/5e137e303ced21dbf2c1

連携の設定は、NGSI的には「Subscription」の設定をすることになります。プッシュボタンのデータ更新を監視(subscribe)しておき、変化があったら特定のURLを叩く、という動作を設定します。

FIWARE環境(orionコンテキストブローカー)上で次のように作業をします。以下の例では、FIWARE環境はexample.comとしています。
 
まず環境変数をセットします。
```bash
$ export ORION_URL=https://orion.example.com
$ export CONTEXT_PROVIDER_URL=http://orion.example.com:8081
$ export ORION_TOKEN=$(ngsi token)
```
その上で、以下のスクリプトを実行します。
```bash:create-subscription.sh
#!/bin/bash
: ${ORION_URL:?Not found}
: ${CONTEXT_PROVIDER_URL:?Not found}
: ${ORION_TOKEN:?Not found}
curl -iX POST \
  "$ORION_URL/v2/subscriptions" \
  --header "Authorization: Bearer $ORION_TOKEN" \
  --header "Content-Type: application/json" \
  --data "{
    \"description\": \"Notify to door opener\",
    \"subject\": {
      \"entities\": [
        {
          \"idPattern\": \"urn:ngsi-ld:Button:001\",
          \"type\": \"Button\"
        }
      ],
      \"condition\": {
        \"attrs\": [ \"value\" ],
        \"alterationTypes\": [ \"entityUpdate\" ]
      }
    },
    \"notification\": {
      \"httpCustom\": {
        \"url\": \"$CONTEXT_PROVIDER_URL/v1/Door/DoorOpen\",
        \"method\": \"GET\",
        \"payload\": null
      }
    }
  }"
```

このスクリプトを実行させることにより、コンテキストブローカー上にSubscription設定が入ります。

このsubscriptionは、少しだけ特殊な設定をしているのでそこを解説しておきます。
- ```"alterationTypes":["entityUpdate"]```を設定することで、当該IDのアトリビュート値(value)が更新されていなくても、通知(notification部で設定しているもの)が発行されるようになります。
- ```"httpCustom"```と```"method":"GET"```を設定することで、トリガーとしてHTTP GETが発行されます。(これを設定しないデフォルトではPOSTメッセージが発行され、bodyにsubscribe対象のIDのアトリビュート値(conditionのところで設定しているもの)が送付されます。)

orion context brokerの詳細については、

https://fiware-orion.letsfiware.jp/orion-api/#fiware-ngsiv2-orion-api

にドキュメントがありますので参考にしてください。

# 完成！
以上、ラズパイとContext Broker上の両方の設定を投入することで、プッシュボタンの動作をトリガーにスマートロックを動作させることができるようになります！

ただ、私が試したところ、ラズパイが起動してからプッシュボタンが動作するようになるまで、2分くらいかかるようです。十分に待って動作させてみてください。また、Bluetoothプッシュボタンとラズパイ間の通信はあまり安定していないようなので、時々ボタンが反応しないこともあります。なので、何度かボタンを押さないとだめな場合もあるようです。

是非試してみてくださいね。

# 今後
今回は、プッシュボタンでスマートロック解除を連携させましたが、NGSIにより、例えばSlackにメッセージを送ることや、あるセンサーが反応したらドアを開ける、など様々な組み合わせを設定可能です。複数の状態を組み合わせたり条件設定をしたりすることもできるので、いろいろ発展の期待があります。

いろいろなものを組み合わせていけることがFIWARE/NGSIの最大のメリットなので、今後は、それを生かしたような様々な事例を作っていきたいと思います。
では。



# 参考文献

https://qiita.com/kikuzo/items/5e137e303ced21dbf2c1

https://fiware-orion.letsfiware.jp/orion-api/#fiware-ngsiv2-orion-api

https://qiita.com/nori-dev-akg/items/96584d9591d329f9dcb2

https://github.com/lets-fiware/ngsi-go/