SORACOM LTE-MボタンのAPIを調べてみる

# はじめに

SORACOM社から発売された「SORACOM LTE-M Button powered by AWS」を使って、週末工作で何か面白いものを作ってアドベントカレンダーの記事にしようかと思っていましたが、自身のギックリ腰や子供の発熱などに振り回されて、現在まで取り組めていません。

年末年始に取り組むための事前準備として、今回のボタン向けに追加されたAPIについて調査したいと思います。

なお、SORACOM APIの使い方については、今回の記事内では解説しませんので、公式サイトの「[API利用ガイド](https://dev.soracom.io/jp/docs/api_guide/)」を参照してください。

# LTE-Mボタン向けAPIについて

[SORACOM APIリファレンス](https://dev.soracom.io/jp/docs/api/)を確認すると、各APIを役割別にグループにまとめられています。

新しく「Gadget」のグループが追加されています。こちらが、LTE-Mボタン向けのAPIとなります。
使えるAPIを１つ１つ試して解説していきたいと思います。

## ガジェット一覧取得（listGadgets）

- メソッド：GET
- パス： /gadgets

「指定した条件にマッチするガジェットのリストを返します。
ガジェットの総数が1ページに収まらない場合は、レスポンス中に次のページにアクセスするためのURLをLinkヘッダに含めて返します。」

#### パラメーター

- product_id(任意)
    - 現時点ではガジェットには「button」しか存在しません。
- tag_name(任意)
    - 検索対象のタグの名前(完全一致)を指定します。
- tag_value(任意)
    - 検索対象にするタグの検索文字列。
    - 「tag_name」を指定した場合は必須。
- tag_value_match_mode(任意)
    - タグの検索方法を指定する。
    - exact(default)：完全一致
    - prefix：前方一致
- limit(任意)
    - 取得するガジェット件数の上限を指定する。
- last_evaluated_key(任意)
    - 続き読み用のキー情報を指定する。
    - 現在のページで取得した最後のガジェットのID ({product_id}/{serial_number})を設定する。
    - このパラメータを指定することにより次以降のガジェットリストを取得できる。

### パラメーター例
- パラメータ
    - product_id「button」
    - tag_name「place」
    - tag_value「kyoto_」
    - tag_value_match_mode「prefix」
    - limit「10」
- 解説
    - ガジェット種類が「ボタン」で、placeタグが「kyoto_」から始まるデータを１０件取得する。

### レスポンス

- 条件に該当したGadgetのリスト(JSON形式)

現時点でガジェットはButtonしか有りませんので、ボタンのデータ内容は以下となります。
ボタン１個で以下のデータとなり、リスト形式でデータが格納されています。

- operatorId・・・オペレータID
- id・・・ガジェットのID「{productId}/{serialNumber}」
- productId・・・ガジェットのプロダクトID。現時点では「button」のみ。
- serialNumber・・・ガジェットのシリアルNo。
- status・・・ガジェットのステイタス。「active(使用中)」「terminated(解約済み)」
- tags・・・ガジェットのタグ情報(複数)。
- createdTime・・・作成日時(UNIXタイム整数10桁小数3桁)
- lastModifiedTime・・・最終更新日時(UNIXタイム整数10桁小数3桁)
- terminatedTime・・・解約日時(UNIXタイム整数10桁小数3桁)
- terminationEnabled・・・解約制限(true:解約可能、false:解約ロック)
- attributes・・・ガジェットの属性
    - contractEndingTime・・・契約終了日時(yyyy-mm-ddThh:MM:ssZ形式)
    - firstClickTimestamp・・・最初にクリックした日時(yyyy-mm-ddThh:MM:ssZ形式)
    - remainingCount・・・残ボタン回数(1500回MAX)
- lastSeen・・・前回実施内容
    - batteryLevel・・・電池残量（0.25/0.5/0.75/1の4段階 1が電池フル状態）
    - clickEventPropagated・・・クリックイベントが伝わったかどうか(true, false)
    - clickTime・・・前回クリック日時(yyyy-mm-ddThh:MM:ssZ形式)
    - clickType・・・クリック種別「SINGLE」「DOUBLE」「LONG」

### 実行結果

```http:REST-API
https://api.soracom.io/v1/gadgets?product_id=button&tag_value_match_mode=exact
```

```json:Response
[
  {
    "operatorId": "XXXXXXXXXXXX",
    "id": "button/XXXXXXXXXXXXX",
    "productId": "button",
    "serialNumber": "XXXXXXXXXXXXX",
    "status": "active",
    "tags": {},
    "createdTime": 1541643204869,
    "lastModifiedTime": 1541643204869,
    "terminatedTime": null,
    "terminationEnabled": false,
    "attributes": {
      "contractEndingTime": "2019-11-08T02:16:16Z",
      "firstClickTimestamp": "2018-11-08T02:16:16Z",
      "remainingCount": 1487
    },
    "lastSeen": {
      "batteryLevel": 1,
      "clickEventPropagated": true,
      "clickTime": "2018-11-13T04:33:03.410Z",
      "clickType": "SINGLE"
    }
  },
  {
    "operatorId": "XXXXXXXXXXXXX",
    "id": "button/XXXXXXXXXXXXX",
    "productId": "button",
    "serialNumber": "XXXXXXXXXXXXX",
    "status": "active",
    "tags": {},
    "createdTime": 1541659008424,
    "lastModifiedTime": 1541659008424,
    "terminatedTime": null,
    "terminationEnabled": false,
    "attributes": {
      "contractEndingTime": "2019-11-08T06:46:09Z",
      "firstClickTimestamp": "2018-11-08T06:46:09Z",
      "remainingCount": 1381
    },
    "lastSeen": {
      "batteryLevel": 1,
      "clickEventPropagated": true,
      "clickTime": "2018-12-04T02:18:22.187Z",
      "clickType": "SINGLE"
    }
  }
]
```

## ガジェット情報取得（getGadget）

- メソッド：GET
- パス：/gadgets/{product_id}/{serial_number}

「指定されたガジェットの情報を返します。」

#### パラメーター

- product_id(必須)
    - 現時点ではGadgetにはボタンしか存在しませんので「button」を設定します。
- serial_number(必須)
    - 対象のボタンのシリアルナンバーを設定します。

### レスポンス

- 指定されたGadgetの詳細情報(JSON形式)

ガジェットの詳細情報となりますので、ガジェット一覧情報取得の戻り値の１レコードと同じです。

### 実行結果

```http:REST-API
https://api.soracom.io/v1/gadgets/button/XXXXXXXXXXXX
```

```json:Response
{
  "operatorId": "XXXXXXXXXXX",
  "id": "button/XXXXXXXXXXX",
  "productId": "button",
  "serialNumber": "XXXXXXXXXXX",
  "status": "active",
  "tags": {
    "name": "abc",
    "place": "kyoto"
  },
  "createdTime": 1541643204869,
  "lastModifiedTime": 1544341625937,
  "terminatedTime": null,
  "terminationEnabled": false,
  "attributes": {
    "contractEndingTime": "2019-11-08T02:16:16Z",
    "firstClickTimestamp": "2018-11-08T02:16:16Z",
    "remainingCount": 1487
  },
  "lastSeen": {
    "batteryLevel": 1,
    "clickEventPropagated": true,
    "clickTime": "2018-11-13T04:33:03.410Z",
    "clickType": "SINGLE"
  }
}
```

## 解約不可状態設定（disableTerminationOnGadget）

- メソッド：POST
- パス：/gadgets/{product_id}/{serial_number}/disable_termination

「指定されたガジェットを解約不可状態に設定します。」

#### パラメーター

- product_id(必須)
    - 現時点ではガジェットにはボタンしか存在しませんので「button」を設定します。
- serial_number(必須)
    - 対象のボタンのシリアルナンバーを設定します。

### レスポンス

- 更新後のガジェットの詳細情報(JSON形式)

ガジェットの詳細情報となりますので、ガジェット情報取得の戻り値と同じです。
詳細情報のterminationEnabledが「false」となっていると思います。

### 実行結果

```http:REST-API
https://api.soracom.io/v1/gadgets/button/{serial_number}/disable_termination
```

```json:Response
{
  "operatorId": "XXXXXXXX",
  "id": "button/XXXXXXXXXXXXXXXX",
  "productId": "button",
  "serialNumber": "XXXXXXXXXXXXXXXX",
  "status": "active",
  "tags": {},
  "createdTime": 1541643204869,
  "lastModifiedTime": 1544338621402,
  "terminatedTime": null,
  "terminationEnabled": false,
  "attributes": {
    "contractEndingTime": "2019-11-08T02:16:16Z",
    "firstClickTimestamp": "2018-11-08T02:16:16Z",
    "remainingCount": 1487
  },
  "lastSeen": {
    "batteryLevel": 1,
    "clickEventPropagated": true,
    "clickTime": "2018-11-13T04:33:03.410Z",
    "clickType": "SINGLE"
  }
}
```

## 解約可能状態設定（enableTerminationOnGadget）

- メソッド：POST
- パス：/gadgets/{product_id}/{serial_number}/enable_termination

「指定されたガジェットを解約可能状態に設定する」

#### パラメーター

- product_id(必須)
    - 現時点ではガジェットにはボタンしか存在しませんので「button」を設定します。
- serial_number(必須)
    - 対象のボタンのシリアルナンバーを設定します。

### レスポンス

- 更新後のガジェットの詳細情報(JSON形式)

ガジェットの詳細情報となりますので、ガジェット情報取得の戻り値と同じです。
詳細情報のterminationEnabledが「true」となっていると思います。

### 実施

```http:REST-API
https://api.soracom.io/v1/gadgets/button/{serial_number}/enable_termination
```

```json:Response
{
  "operatorId": "XXXXXXXX",
  "id": "button/XXXXXXXXXXXXXXXX",
  "productId": "button",
  "serialNumber": "XXXXXXXXXXXXXXXX",
  "status": "active",
  "tags": {},
  "createdTime": 1541643204869,
  "lastModifiedTime": 1544338621402,
  "terminatedTime": null,
  "terminationEnabled": true,
  "attributes": {
    "contractEndingTime": "2019-11-08T02:16:16Z",
    "firstClickTimestamp": "2018-11-08T02:16:16Z",
    "remainingCount": 1487
  },
  "lastSeen": {
    "batteryLevel": 1,
    "clickEventPropagated": true,
    "clickTime": "2018-11-13T04:33:03.410Z",
    "clickType": "SINGLE"
  }
}
```

## ガジェット登録（registerGadget）

- メソッド：POST
- パス：/gadgets/{product_id}/{serial_number}/register

「ガジェットを登録します」

#### パラメーター

- product_id(必須)
    - 現時点ではガジェットにはボタンしか存在しませんので「button」を設定します。
- serial_number(必須)
    - 対象のボタンのシリアルナンバーを設定します。
- registration_request(必須)
    - 対象のボタンに登録するタグをBodyにJSON形式で設定します。

### レスポンス

- 登録後のガジェットの詳細情報(JSON形式)

ガジェットの詳細情報となりますので、ガジェット情報取得の戻り値と同じです。

### 実施

```http:REST-API
https://api.soracom.io/v1/gadgets/button/{serial_number}/register
```
```json:body
{
  "tags": {
    "name": "abc",
    "floor": "2",
    "place": "kyoto"
  }
}
```

```json:Response
{
  "operatorId": "XXXXXXXX",
  "id": "button/XXXXXXXXXXXXXXXX",
  "productId": "button",
  "serialNumber": "XXXXXXXXXXXXXXXX",
  "status": "active",
  "tags": {
    "name": "abc",
    "floor": "2",
    "place": "kyoto"
  },
  "createdTime": 1541643204869,
  "lastModifiedTime": 1544338621402,
  "terminatedTime": null,
  "terminationEnabled": true,
  "attributes": {
    "contractEndingTime": "2019-11-08T02:16:16Z",
    "firstClickTimestamp": "2018-11-08T02:16:16Z",
    "remainingCount": 1487
  },
  "lastSeen": {
    "batteryLevel": 1,
    "clickEventPropagated": true,
    "clickTime": "2018-11-13T04:33:03.410Z",
    "clickType": "SINGLE"
  }
}
```

## タグ登録・更新（putGadgetTags）

- メソッド：PUT
- パス：/gadgets/{product_id}/{serial_number}/tags

「指定されたガジェットにタグ情報を登録あるいは更新します」

#### パラメーター

- product_id(必須)
    - 現時点ではガジェットにはボタンしか存在しませんので「button」を設定します。
- serial_number(必須)
    - 対象のボタンのシリアルナンバーを設定します。
- tags(必須)
    - 対象のボタンに登録するタグをBodyにJSON形式で設定します。

### レスポンス

- 更新後のガジェットの詳細情報(JSON形式)

ガジェットの詳細情報となりますので、ガジェット情報取得の戻り値と同じです。
タグに登録・更新された情報が反映されています。

### 実施

```http:REST-API
https://api.soracom.io/v1/gadgets/button/{serial_number}/tags
```
```json:body
{
  "tags": {
    "name": "abcefd",
    "floor": "3",
    "place": "tokyo"
  }
}
```

```json:Response
{
  "operatorId": "XXXXXXXX",
  "id": "button/XXXXXXXXXXXXXXXX",
  "productId": "button",
  "serialNumber": "XXXXXXXXXXXXXXXX",
  "status": "active",
  "tags": {
    "name": "abcdefd",
    "floor": "3",
    "place": "tokyo"
  },
  "createdTime": 1541643204869,
  "lastModifiedTime": 1544338621402,
  "terminatedTime": null,
  "terminationEnabled": true,
  "attributes": {
    "contractEndingTime": "2019-11-08T02:16:16Z",
    "firstClickTimestamp": "2018-11-08T02:16:16Z",
    "remainingCount": 1487
  },
  "lastSeen": {
    "batteryLevel": 1,
    "clickEventPropagated": true,
    "clickTime": "2018-11-13T04:33:03.410Z",
    "clickType": "SINGLE"
  }
}
```

## タグ削除（deleteGadgetTag）

- メソッド：DELETE
- パス：/gadgets/{product_id}/{serial_number}/tags/{tag_name}

「指定されたガジェットのタグ情報を削除します」

#### パラメーター

- product_id(必須)
    - 現時点ではガジェットにはボタンしか存在しませんので「button」を設定します。
- serial_number(必須)
    - 対象のボタンのシリアルナンバーを設定します。
- tag_name(必須)
    - 削除対象のタグ名を設定します。

### レスポンス

- 204:対象のタグの削除が完了
- 404:指定されたガジェットあるいはタグが存在しない

実行後にレスポンスコードが返ってきますので、「*204*」で正常に削除完了です。

### 実施

```http:REST-API
https://api.soracom.io/v1/gadgets/button/{serial_number}/tags/floor
```

```http:Response
204
```

## ガジェット解約（terminateGadget）

- メソッド：POST
- パス：/gadgets/{product_id}/{serial_number}/terminate

「指定されたガジェットを解約します」

#### パラメーター

- product_id(必須)
    - 現時点ではガジェットにはボタンしか存在しませんので「button」を設定します。
- serial_number(必須)
    - 対象のボタンのシリアルナンバーを設定します。
- tag_name(必須)
    - 削除対象のタグ名を設定します。

### レスポンス

- 更新後のガジェットの詳細情報(JSON形式)

ガジェットの詳細情報となりますので、ガジェット情報取得の戻り値と同じです。
解約状態のガジェット情報となります。

### 実施

```http:REST-API
https://api.soracom.io/v1/gadgets/button/{serial_number}/terminate
```

解約すると使えなくなりますので実施していません。。。
こういう場合は「API Sandbox」を使いましょう。

## グループ設定・・・POST 「/gadgets/{product_id}/{serial_number}/set_group」

### setGadgetGroup
「指定されたガジェットの所属先グループを指定あるいは上書き変更します」

#### パラメーター

- product_id(必須)
    - 現時点ではガジェットにはボタンしか存在しませんので「button」を設定します。
- serial_number(必須)
    - 対象のボタンのシリアルナンバーを設定します。
- group(必須)
    - 対象のボタンに登録するグループ情報をBodyにJSON形式で設定します。（IDのみを含めばよい）

### レスポンス

- 更新後のガジェットの詳細情報(JSON形式)

ガジェットの詳細情報となりますので、ガジェット情報取得の戻り値と同じです。

### 実行結果（未実施）

```http:REST-API
https://api.soracom.io/v1/gadgets/button/{serial_number}/set_group
```
```json:body
{
  "configuration": {},
  "createdTime": 0,
  "groupId": "string",
  "lastModifiedTime": 0,
  "operatorId": "string",
  "tags": {
    "location": "tokyo"
  }
}
```

*SORACOMコンソールではボタンのグループ管理の機能は見受けられませんでした。
将来的にガジェット種類が増えた場合に使用するための機能かもしれません。
このAPIで管理されるグループIDはどのような意味合い・使い道があるのでしょうか？
ご存知の方、教えてください！*

## グループ解除（unsetGadgetGroup）

- メソッド：POST
- パス：/gadgets/{product_id}/{serial_number}/unset_group

「指定されたガジェットの所属先グループを解除します」

#### パラメーター

- product_id(必須)
    - 現時点ではガジェットにはボタンしか存在しませんので「button」を設定します。
- serial_number(必須)
    - 対象のボタンのシリアルナンバーを設定します。

### レスポンス

- 更新後のガジェットの詳細情報(JSON形式)

ガジェットの詳細情報となりますので、ガジェット情報取得の戻り値と同じです。

### 実行結果（未実施）

```http:REST-API
https://api.soracom.io/v1/gadgets/button/{serial_number}/unset_group
```

*SORACOMコンソールではボタンのグループ管理の機能は見受けられませんでした。
将来的にガジェット種類が増えた場合に使用するための機能かもしれません。
このAPIで管理されるグループIDはどのような意味合い・使い道があるのでしょうか？
ご存知の方、教えてください！*

# さいごに

さいごにLTE-Mボタンで使用出来るAPIを整理します。

1. ガジェット一覧取得
2. ガジェット情報取得
3. 解約不可設定
4. 解約不可解除
5. ガジェット登録
6. タグ登録・更新
7. タグ削除
8. ガジェット解約
9. グループ設定・更新
10. グループ解除

これらのAPIを使って、何か面白いものを作って、また記事にしたいと思います。