自作SORACOM InventoryのエージェントinventorydでIoTデバイスを簡単に遠隔操作する

# はじめに

IoTデバイスの遠隔操作したくないですか？
したいに決まってますよね！
ということで今回お勧めするのは[SORACOM Inventory](https://soracom.jp/services/inventory/)です。

# １分でラズパイを遠隔再起動するよ

**注意**
すでにSORACOM Inventoryを使用中の場合、以下を実行すると費用が発生します。(初期費用100円、利用料金50円)
未使用の場合は150円の無料枠に入ります。

---

SORACOM Airで接続中のラズパイに入ってrootで以下のコマンドを実行しましょう。

```bash
curl -L -O https://github.com/1stship/inventoryd/releases/download/v0.0.1/inventoryd_0.0.1_linux_arm.tar.gz
tar zxf inventoryd_0.0.1_linux_arm.tar.gz
./inventoryd --init # 質問にはEnterのみでよい(yで回答したことになる)
echo "/sbin/reboot" > resources/3/0/4
./inventoryd -b
```

こんな感じで表示されればOKです。

```bash
2020/01/13 12:37:59 Start Bootstrap
2020/01/13 12:38:00 Request Bootstrap accepted
2020/01/13 12:38:01 Bootstrap finished
Bootstrap finish
2020/01/13 12:38:01 Registering...
2020/01/13 12:38:04 Register finished. Location is SKdsWwKWEq
2020/01/13 12:38:04 READ /1/0
2020/01/13 12:38:04 READ /2/0
2020/01/13 12:38:05 READ /3/0
2020/01/13 12:38:05 READ /4/0
2020/01/13 12:38:06 READ /5/0
2020/01/13 12:38:06 READ /6/0
2020/01/13 12:38:06 READ /7/0
2020/01/13 12:38:07 READ /8/0
2020/01/13 12:38:07 READ /9/0
```

SORACOMコンソールにログインして、SORACOM Inventoryのデバイス管理メニューに入ります。

![スクリーンショット 2020-01-13 21.12.00.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/542185/3ec104e4-f785-1b0c-ea87-fb0a663623f8.png)

デバイスを選択して、詳細をクリックします。

![スクリーンショット 2020-01-13 21.16.35.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/542185/de3b6394-2c51-e49d-79be-a58decefe195.png)

Reboot /3/0/4を探して、実行ボタンをクリックします。

![スクリーンショット 2020-01-13 21.25.47.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/542185/43404cc6-6f09-25d0-e23a-0993dc9f06ab.png)

「コマンド実行」をクリックします。

![スクリーンショット 2020-01-13 21.27.03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/542185/6aec9929-01d6-7778-b51f-cb39462f4c43.png)

```bash
2020/01/13 12:38:58 EXECUTE /3/0/4
Connection to 192.168.2.3 closed by remote host.
Connection to 192.168.2.3 closed.
```

はい、ラズパイが再起動しました。やったね！完！

# SORACOM Inventoryとは

完！でもよかったのですが、上でやったことは何なのか説明をします。

皆さん、SORACOM Inventory使ってますか？というかそもそも知ってますか？
ぶっちゃけ使ってない人多いんじゃないかと思います。Qiitaのキーワード0件でしたし。(記事自体はありました)

[SORACOM Inventory](https://soracom.jp/services/inventory/)の公式の説明は以下の通り。

> Inventory は、OMA LightweightM2M(LwM2M)をベースにしたデバイス管理のためのフレームワークを提供するサービスです。SORACOM Air と連携したデバイスの自動登録が可能です。

LwM2Mというプロトコルを使ってデバイスを管理するためのサービスです。LwM2Mで必要になるサーバはSORACOMにてフルマネージドで提供されています。そのため、自前でサーバやAWSなどのクラウドを用意することなく、デバイス管理や遠隔操作ができるようになります。

「I」のサービスである「Inventory」は、「H」のサービスである「Harvest」の次に出たサービスです。Harvest以前のサービスのBeamやFunnelは他のサービスと連携することが前提になっていましたが、他のクラウドサービスが必要なくSORACOMだけで完結できるHarvestはとっつきやすく、IoTの入り口として最適ですよね。そしてHarvestによってデータの蓄積や可視化(これはInventoryの翌年出たLagoonで強化される)ができるようになってくると、それをもとにデバイスに制御をかけたい、というのは当然の流れ、、そしてデバイス管理サービスのInventoryが登場します。これもSORACOMだけで完結できる、という点はHarvestと同じで、ユーザーの用意するものはデバイスとSORACOM Airの回線のみです。

使ってみると、デバイスのステータスを取得したり再起動したりということが、SORACOMのコンソールやAPI経由で簡単にできるようになるので、デバイス管理や遠隔操作にはとっても便利です。

でもちょっと大変なところがありまして、LwM2Mのエージェントを自分で作らなければならないんですよね。そのため導入のハードルが高いと思われがちです。（SORACOM HarvestのとりあえずTCP/UDPでデータ投げときゃOKからの落差がすごい）

そもそもLwM2Mって何？というところから始まって、[Wakaama](https://github.com/eclipse/wakaama)や[Leshan](https://github.com/eclipse/leshan)などの参照実装や、懇切丁寧な[README](https://github.com/soracom/soracom-inventory-agent-for-java/blob/master/README_ja.md)のあるSORACOM Inventory agent for Javaはあるものの、これを読んで自分のデバイスに合わせてソースを改変してビルドするのはまあ大変です。なんか簡単に扱えるようにしたい。

# SORACOM Inventoryのエージェントを自作しよう

というところで色々あって僕はSORACOM Inventoryには思い入れがあるので、なんかやろうかなと思いまして、扱いやすいSORACOM Inventoryのエージェントを自作することにしました。というか2019年のゴールデンウイークを丸ごと費やして作って、inventorydという名前でGitHubに公開していたのですが、当時はブログを書いたりしていなかったのでここで改めて紹介します。

**inventoryd**
SORACOM Inventory access tool in Golang
https://github.com/1stship/inventoryd

基本的な考えは以下の２つです。

・ビルド済のシングルバイナリで動作する
・リソース対応のためにリビルドが必要なく、ファイルの配置で対応できる

要は[Apache HTTP Server(httpd)](https://httpd.apache.org/)と同じ感じで使えるようにしたい、ということです。名前もそれっぽくしています。

多くの人はWebサーバ(HTTPサーバ)を立てようとした時、HTTPサーバのソースを改変してビルドしたりしないと思うんですよね。大抵は、httpdやnginxをビルド済のパッケージをインストールして、公開用のディレクトリにファイルを置いたり、PHPなどのスクリプトファイルを置いたりするところから始めると思います。そんな感じで使えるものであれば、ソースコードを読んだり改変したりビルドしたりしない非開発者の人でもSORACOM Inventoryを使えるようになるかもしれない。

そんなものを目指して作りました。Go言語で書いているのはビルド済みのシングルバイナリで提供するのが一番ユーザー側に負担がかからず簡単に導入できるだろう、ということと、当時Go言語に興味があって何か作ってみようという思いがあったからです。（Go言語で書いた最初のプログラムなのでクオリティはお察し。。プロトタイプ的なものと思っていただければ）

# inventorydの基本的な考え方

LwM2Mでは、デバイスへの問い合わせや書き込み、実行などをオブジェクトモデルというもので定義し、そのモデルをパスに割り当てています。

例えば先ほどの「Reboot /3/0/4」というのは、[Deviceというモデルの仕様](https://www.openmobilealliance.org/tech/profiles/LWM2M_Device-v1_1.xml)にObjectIDが3と定義されており、次の0は最初のリソースを表し、(同じ定義のモデルが複数ある場合は/3/1/4、3/2/4と2個目の数字が上がっていく)、その中のItem ID="4"としてRebootが以下のように記載されています。

```xml
<Item ID="4">
  <Name>Reboot</Name>
  <Operations>E</Operations>
  <MultipleInstances>Single</MultipleInstances>
  <Mandatory>Mandatory</Mandatory>
  <Type/>
  <RangeEnumeration/>
  <Units/>
  <Description>
    <![CDATA[ Reboot the LwM2M Device to restore the Device from unexpected firmware failure. ]]>
  </Description>
</Item>
```

従って、/3/0/4はDeviceというモデルの最初のリソースに対してRebootというExecuteを実行するもの、ということになります。HTTPのRESTのパスであれば、
`POST /Device/0/Reboot`
のようになっても良さそうなものですが、LwM2Mは通信が軽量なプロトコルを目指しており、上のような仕様がサーバ、デバイス間で共有されているものとして、番号で対象を伝えることになっています。

このパスと、デバイス内のファイルを一致させるというのがinventorydの基本的な考えです。

```bash
./inventoryd --init
```

を実行した時に、resourcesディレクトリが作成され、その中にはオブジェクトモデルに応じたリソースのファイルが作成されています。そしてこのファイルに対してREADするとそのファイルの中身が返り、WRITEするとファイルが書き換えられ、EXECUTEするとそのファイルがスクリプトとして実行される、という動作をします。

init直後はリソースはデフォルト値を返すだけのものになっており、あまり役に立ちません。このファイルを適切な値やスクリプトで置き換えたり、WRITEされた値を他のプログラムで使用するなどすることによって、ちゃんとしたデバイス管理ができることになります。最初の例では/3/0/4を/sbin/rebootとしましたが、これにより/3/0/4(LwM2Mで定義されたReboot)をラスパイの再起動(/sbin/reboot)と結びつけることができました。

このようなファイルによる対応付けであれば、CやJavaのコードを書いてビルドすることなくファイルの読み書きやシェルスクリプトの記載で対応できるので、比較的簡単に対応できそうですよね。

また、READやWRITEをファイル読み書きでは無く、スクリプトとして実行させたいという場合は、ファイル名の.read、.writeとつけて、実行権をもったスクリプトとすることで対応できるようにしています。

わかりやすいところで説明すると、Current Time /3/0/13はinit直後の状態ではタイムスタンプが0(1970/01/01 00:00:00 UTC)を返すだけですが、/3/0/13.readを

```bash
date +%s
```

として記載し、

```bash
chmod 755 resources/3/0/13.read
```

として実行権を付与することで、現在時刻を返すようになります。

初期状態(1970/01/01 00:00:00 UTC)
![スクリーンショット 2020-01-13 22.56.51.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/542185/42065e77-df4a-6bbf-b25f-ca4348a1f021.png)

スクリプト設置後(現在時刻が表示される)
![スクリーンショット 2020-01-13 22.58.48.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/542185/1f661545-196e-682f-300b-f410730dad86.png)

同様に、/3/0/13.writeに時刻を設定するスクリプトを記載することで、時刻をOSに反映させることができます。(値は標準入力で渡すので、readで受け取れます)

```bash
read TIMESTAMP
date -s "@$TIMESTAMP"
```

設定します。
![スクリーンショット 2020-01-13 23.16.07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/542185/4ef2c2a8-78ff-ad50-9433-c3d0ffdb00bd.png)

これで時刻の設定ができました。(ちなみにタイムスタンプ型はコンソールで読み出すと、「Mon Jan 13 14:14:38 UTC 2020」のような形になるのですが、書き込む時にはYYYYmmddTHHMMSS.fff(UTCからの差、日本時間だと+09)にしないと送信できないという罠があります)

このように、比較的簡単なスクリプトを設置することで、動的な値にも対応できるようになっています。ここではコンソールからの操作をしていますが、当然Web APIも用意されています。

https://dev.soracom.io/jp/docs/api/#/Device

WebAPIを使うことで、管理しているデバイスから値を収集したり、特定の処理を実行させたりを自動化するのも簡単に実現できますね。

# ちょっと高度な使い方： Observe

LwM2MにはObserveという面白い仕組みがありまして、サーバから監視対象として設定したリソースは、サーバから読み出さなくてもデバイスから自発的に送信する、というものです。

例えばメモリの使用量を記録するとしましょう。
Memory Freeは/3/0/10なので、/3/0/10.readを以下のスクリプトにします。

```bash
free | grep 'Mem' | tr -s ' ' | cut -d" " -f7
```

freeコマンドのavailableのメモリを取得しています。LwM2Mの仕様を見ると単位はkBなのですが、変化がわかりにくくなるのでとりあえずbyte単位です。

コンソールから/3/0/10をObserveすると、5秒に１回Notifyというイベントが発生しているのがわかります。

```bash
2020/01/13 14:51:37 OBSERVE /3/0/10
2020/01/13 14:51:37 Notify /3/0/10
2020/01/13 14:51:42 Notify /3/0/10
2020/01/13 14:51:47 Notify /3/0/10
```

inventorydではObserveされたリソースの値を5秒に1回確認し、値が変わっていればサーバーに通知する、という動作をします。そしてこのNotifyはSORACOMのアプリケーションサービスのBeam、Funnel、Harvest、Funk、そしてUnified Endpointに送ることができます。

分かりやすいところでHarvestに連携させるとこんな感じです。

![スクリーンショット 2020-01-13 23.58.22.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/542185/c2ccf718-feb4-7f7e-dc15-ee50ba125244.png)

メモリ量の推移がharvestに記録されていることがわかりますね。これ結構簡単じゃないですか？やったことはinventorydをダウンロード、初期設定、起動したことと、メモリを取得するスクリプトをちょっとググって入れただけです。それだけでもうこの状態になる。ある意味HarvestにTCP/UDPでデータ送るより簡単ですよ。

また、inventorydはObserveの際、値が変わっていればNotifyという動作をするため、例えば何かのエラーフラグとかを監視させておいて、それがtrueになったらFunkに通知させる、といった使い方もできそうです。

SORACOM Inventory、何だかすごく便利そうじゃないですか？

# インターネットからでも使用可能

SORACOM Inventoryの他のアプリケーションサービスにない特長として、インターネットからソラコム回線なしで使用可能、というのがあります。

以下のページの方法でデバイスID、キーを払い出して、
https://dev.soracom.io/jp/start/inventory_registration_with_keys/

inventory実行時のオプションを以下のように変えるだけです。

```bash
inventoryd --identity <払い出されたデバイスID> --psk <払い出されたシークレットキー(base64)>
```

ちなみにこれまで実行した際に指定していた-bというオプションは、LwM2Mのブートストラップという仕組みを使用したもので、これを使うとSORACOM Airの回線で簡単・安全に認証情報の取得ができます。デバイスごとにパスワードを発行・保存しなくても良いのでとても簡単・便利です。逆に認証情報の発行・保存さえすれば、SORACOM Airの回線でなくとも使用できます。この場合、価格的にはInventoryの使用料金50円だけで済むのでとても安価ですね。

使いどころあるかわかりませんが、PCやサーバに入れても動いて、値の取得や遠隔操作できるようになりますよ(外向きのUDP 5684ポートが空いていれば)

# 他の遠隔操作方法との比較

ソラコムを使っていると遠隔操作には色々な方法があります。
最近出たSORACOM Napterはとても強力で、遠隔操作はこれで十分、という考えもあると思います。
また、SORACOM BeamとAWS IoTを組み合わせ、MQTTでコマンドを送る、ということもされていると思います。

比較ポイントはいくつかありますが、以下のように比較してみました。

|項目|Napter|Beam + AWS IoT|Inventory|
|:--|:--:|:--:|:--:|
|価格|300円/月|0.0018円 * コマンド数(AWS IoT部分除く)|50円/月|
|同期/非同期|同期|非同期|同期|
|必要サービス|SORACOMで完結|AWSと連携|SORACOMで完結|
|コマンド発行方法|デバイスにサービスが必要(SSHやWebAPIなど)|AWSのWebAPIもしくはMQTT|SORACOMのWebAPI|
|コマンド発行結果|デバイスのサービス次第|仕組みを考える必要あり|WebAPIの実行結果|
|SORACOM Air回線|必須|必須(BeamではなくKryptonとの組み合わせなら要らない)|必須ではない|

価格としてはコマンド発行数が少なければBeam + AWS IoT、コマンド発行数が多ければInventoryが有利です。Napterは必要な時のみ使用するという考えなので、使用が前提となっているサービスより割高と考えられます。

コマンドの発行や結果の受け取りはInventoryが一番簡単だと考えています。SORACOMのAPIを使えばよく、応答も同期的に返ってきます。一方AWS IoTは非同期で、応答を返す場合は応答用のトピックを用意して、それを受信する何らかのサブスクライバを用意して受信する、といったことが必要になり、かなり面倒です。逆にAWS IoTは仕組みさえできていれば、多数のデバイスに非同期にメッセージを送り、非同期に返されたメッセージをまとめて収集することができます。同期処理はデバイスが多くなってくると、ひとつずつメッセージを送って応答を取得するのに時間がかかると予想されます。

また、最初の方にも上げましたが、SORACOM InventoryはSORACOMだけで完結できるサービスです。Napterもそうですね。BeamはAWS IoTと組み合わせることになるので、クラウド用意するの大変、となる可能性があります。AWS IoTはそこそこ難しいサービスですしね。逆にクラウドに慣れているのであれば、Beam + AWS IoTはAWSの様々なサービスと連携できる長所を活かせます。

ということで、所感としてそれぞれの構成で向いているのは、以下のような感じかと思います。

SORACOM Napter - デバイスに人が直接アクセスしてのアドホックな操作
SORACOM Inventory - デバイスの項目を指定しての取得や監視、設定値の変更、定型的な操作
SORACOM Beam + AWS IoT - 定常的な値の取得や多数のデバイスに対する一斉操作

すでに他の方法で運用されているところに無理に入れることもないですが、今Harvestで値をとっているだけのところに、ちょっとした制御を入れたい、というような場合にはベストチョイスなのでは無いかと思っています。

# おわりに

SORACOM InventoryはIoTデバイスを簡単に管理できるサービスということが感じられましたでしょうか？
inventorydは正直去年公開してからほぼ手つかずですが、また折を見てアップデートしていきたいと思っています。ご要望あればなんらかの形でお伝えください。(このブログへのコメント、GitHubへのIssueなど)そのうち対応するかも知れません。

次回はinventorydを開発の技術的な面を書く予定です。
予定タイトル：「SORACOM Inventoryエージェント開発に向けてDTLS、CoAP、LwM2MをGo言語で実装する」