REST

RESTはHTTP(S)およびTCPを基本とする、非常にシンプルでセキュアなプロトコルです。現在、非常にシンプルなデバイスから大規模なITに至るまで、すべてのネットワーク・プログラミング環境でサポートされている事実上のインターネット標準となっています。RESTを紹介している多くの本の中の1つにRESTful Web Servicesがあります。

このセクションでは、Things CloudのRESTインターフェースを使用してデバイスをThings Cloudと統合する方法を説明します。RESTインターフェースを使用する際の一般的な情報や、RESTを使用してThings Cloudの上にアプリケーションを開発するための情報については、Microservice SDKを参照してください。

★この説明は、**「リファレンス ガイド」**のデバイスおよびセンサーライブラリだけでなく、各インターフェースについて詳しく説明するThings Cloud OpenAPI仕様仕様 と密接にリンクしています。

  • REST実装: すべての一般的な概念のリファレンス
  • フラグメントライブラリ: マネージドオブジェクト(デバイス)や、デバイスのあらかじめ定義されたセンサーおよび制御機能を指定します。

Java ME/SE、JavaScript、またはC/C++を使用して開発する場合は、Things Cloudの機能により便利にアクセスできるよう、このガイドの関連する章を確認してください。また、サポートされている開発ボードを使用する場合は、デバイスガイドの該当の説明を参照してください。

デバイスインテグレーション

デバイスをThings Cloudに統合するための基本的なライフサイクルは、デバイスのインターフェースで説明されています。このセクションでは、このライフサイクルがRESTレベルでどのように実装されるかを説明します。 ライフサイクルは、スタートアップフェーズとサイクルフェーズの2つのフェーズで構成されます。

スタートアップフェーズでは、デバイスをThings Cloudに接続し、インベントリのデバイスデータを更新します。また、オペレーションに必要なクリーンアップタスクも実行します。次の手順で構成されます。

  • ステップ0: デバイス認証情報がまだリクエストされていない場合はリクエストします。
  • ステップ1: デバイスがすでに登録されているか確認します。
  • ステップ2: 登録されていない場合、インベントリにデバイスを作成します。
  • ステップ3: デバイスを新規登録します(アイデンティティを作成します)。
  • ステップ4: 登録されている場合、インベントリ内のデバイスを更新します。
  • ステップ5: 子デバイスを検出し、インベントリにそれらのデバイスを作成または更新します。
  • ステップ6: 再起動が必要なオペレーションを完了し、新しいオペレーションをサブスクライブします。

スタートアップフェーズ

サイクルフェーズへと続きます。インベントリを継続的に更新し、メジャーメント、アラーム、イベントを書き込み、必要に応じてオペレーションを実行します。これは、デバイスの電源が切れるまで実行される、デバイスの「メインループ」となります。このループは次の手順で構成されます。

サイクルフェーズ

データの参照モデルは、フラグメントライブラリを参照してください。

スタートアップフェーズ

ステップ0: デバイスの認証情報をリクエストする

Things Cloudへのすべてのリクエストは認証される必要があり、デバイスからのリクエストも認証される必要があります。個々の認証情報をデバイスに割り当てる場合は、デバイス認証情報APIを使用して新しい認証情報を自動的に生成できます。これを行うには、最初の起動時にAPIを介してデバイスの認証情報をリクエストし、それ以降のリクエストに備えてデバイス上のローカルに保存します。

このプロセスは次のように動作します。

  • Things Cloudは、各デバイスが何らかの固有のIDを持つことを前提としています。適切なデバイス識別子は、ネットワークアダプタのMACアドレス、モバイルデバイスのIMEI、またはハードウェアシリアル番号となります。
  • 新しいデバイスを使用する場合は、この固有IDをThings Cloudの「デバイス登録」に入力し、デバイスを起動します。
  • デバイスが起動すると、デバイスはThings Cloudに接続し、固有IDを繰り返し送信します。そのため、Things Cloudでは静的なブートストラップ用認証情報を提供しており、製品サポートにて問い合わせることができます。
  • テナントUIの「デバイス登録」よりデバイスからの接続を受け入れることができ、その場合Things Cloudは生成された認証情報をデバイスに送信します。
  • デバイスは、以降のすべてのリクエストにこれらの認証情報を使用します。

デバイス側の視点から見ると、これは単一のRESTのリクエストです。

POST /devicecontrol/deviceCredentials
Content-Type: application/vnd.com.nsn.cumulocity.devicecredentials+json
Authorization: Basic <<Base64 encoded bootstrap credentials>>
{
  "id" : "0000000017b769d5"
}

デバイスはこのリクエストを繰り返し発行します。ユーザーがデバイスを登録してデバイス管理アプリで承認していない間は、リクエストは「404 Not Found」を返します。デバイスが承認されると、次のレスポンスが返されます。

HTTP/1.1 200 OK
Content-Type: application/vnd.com.nsn.cumulocity.devicecredentials+json;charset=UTF-8;ver=0.9
Content-Length: ...
{
  "id" : "0000000017b769d5",
  "self" : "<<URL of new request>>",
  "tenantId" : "test",
  "username" : "device_0000000017b769d5",
  "password" : "3rasfst4swfa"
}

これで、デバイスはテナントID、ユーザー名、パスワードを使用してThings Cloudに接続できます。ユーザーエイリアスはデバイスではサポートされていません。

エンタープライズテナント(親テナント)の概念が導入されたことにより、テナント名がテナントIDと同じであると想定することはもはや安全ではありません。認証情報リクエストではテナントIDのみを返します。これをサブドメインとして使用したり、ドメイン名と組み合わせて、ユーザー名(およびパスワード)のみでアクセスできるテナントURLを提供したりすることはできません。正しいテナントへのアクセスは、認証にテナントIDとユーザー名を使用することによってのみ保証できます。例えば、<tenant ID>/<username>と認証情報リクエストによって返却されたパスワードのような形式です。この場合、サブドメインは関係ありません。

リクエストヘッダーは次のようになります。

Authorization: Basic <<Base64 encoded credentials <tenant ID>/<username>:<password> >>

例えば、xyz.je1.thingscloud.ntt.comに追加されたデバイスの認証情報リクエストは、「t123456789」のユーザーID、パスワード、およびテナントIDを返す可能性があります。テナントID「t123456789」は、ユーザーIDとパスワードを使用したリクエストのサブドメイン(t123456789.je1.thingscloud.ntt.com)として使用できません。「http403」が返されます。テナントIDは、パスワードとともに「t123456789/」の形式のユーザーIDとともに使用する必要があります。この場合、実際のサブドメインは無関係です。t123456789.je1.thingscloud.ntt.comまたはmanagement.je1.thingscloud.ntt.comまたはanything.je1.thingscloud.ntt.comでも使用できます。

Things Cloudは、ユーザーIDで指定されたテナントIDを使用して、完全な認証と正しいテナントへのリクエストのルーティングを行います。

有効なテナントURLがわかっている場合(上記の例の xyz.je1.thingscloud.ntt.com)、認証にユーザー名の前に<tenant ID>/を付ける必要はありません。

ステップ1: デバイスが登録済みかどうかを確認する

デバイスの固有IDは、デバイスをインベントリに登録するためにも使用されます。登録はアイデンティティAPIを用いて行います。ID APIでは、各マネージドオブジェクトをタイプで識別される複数の識別子に関連付けることができます。例えば、ハードウェアシリアル番号の場合は「c8y_Serial」、MACアドレスの場合は「c8y_MAC」、IMEIの場合は「c8y_IMEI」となります。

デバイスがすでに登録されているかどうかを確認するには、デバイス識別子とそのタイプを使用して、Identity APIのGETリクエストを使用します。次の例では、ハードウェアシリアル番号「0000000017b769d5」のRaspberry Piをチェックしています。

GET /identity/externalIds/c8y_Serial/raspi-0000000017b769d5 HTTP/1.1

HTTP/1.1 200 OK
Content-Type: application/vnd.com.nsn.cumulocity.externalid+json;charset=UTF-8;ver=0.9
...
{
    "externalId": "raspi-0000000017b769d5",
    "managedObject": {
        "id": "2480300",
        "self": "https://.../managedObjects/2480300"
    },
    "self": "https://.../identity/externalIds/c8y_Serial/raspi-0000000017b769d5",
    "type": "c8y_Serial"
}

MACアドレスはグローバルで固有であることが保証されていますが、ハードウェアのシリアル番号は異なるハードウェア間で重複する可能性があることに注意してください。したがって、上記の例では、デバイス登録時、シリアル番号の前に「raspiー」を付けています(ステップ3参照)。

ここでは、デバイスはすでに登録されており、ステータスコード200が返されます。レスポンスでは、インベントリのデバイスへのURLが「managedObject.self」に返されます。このURLは、後でデバイスを操作するために使用することができます。

デバイスがまだ登録されていない場合は、ステータスコード404とエラーメッセージが返されます。

GET /identity/externalIds/c8y_Serial/raspi-0000000017b769d6 HTTP/1.1

HTTP/1.1 404 Not Found
Content-Type: application/vnd.com.nsn.cumulocity.error+json;charset=UTF-8;ver=0.9
...
{
    "error": "identity/Not Found",
    "info": "https://www.cumulocity.com/guides/reference/#error_reporting",
    "message": "外部IDが見つかりません; external id = ID [type=c8y_Serial, value=raspi-0000000017b769d6]"
}

ステップ2: インベントリにデバイスを作成する

上記のステップ1でデバイスを表すマネージドオブジェクトが存在しなかった場合、Things Cloud上にマネージドオブジェクトが作成されます。 マネージドオブジェクトは、デバイスとそのインスタンスとメタデータの両方を記述します。 インスタンスデータには、ハードウェアおよびソフトウェアの情報、シリアル番号、デバイス構成データが含まれます。 メタデータには、サポートされているオペレーションを含むデバイスの機能が記述されています。

マネージドオブジェクトを作成するには、インベントリAPIのマネージドオブジェクトコレクションに対してPOSTリクエストを出します。 次の例では、Linuxエージェントを使用してRaspberry Piを作成しています。

POST /inventory/managedObjects HTTP/1.1
Content-Type: application/vnd.com.nsn.cumulocity.managedobject+json
Accept: application/vnd.com.nsn.cumulocity.managedobject+json
...
{
    "name": "RaspPi BCM2708 0000000017b769d5",
    "type": "c8y_Linux",
    "c8y_IsDevice": {},
    "com_cumulocity_model_Agent": {},
    "c8y_SupportedOperations": [ "c8y_Restart", "c8y_Configuration", "c8y_Software", "c8y_Firmware" ],
    "c8y_Hardware": {
        "revision": "000e",
        "model": "RaspPi BCM2708",
        "serialNumber": "0000000017b769d5"
    },
    "c8y_Configuration": {
        "config": "#Fri Aug 30 09:13:56 BST 2013\nc8y.log.eventLevel=INFO\n..."
    },
    "c8y_Mobile": {
         "imei": "861145013087177",
        "cellId": "4904-A496",
        "iccid": "89490200000876620613"
    },
    "c8y_Firmware": {
        "name": "raspberrypi-bootloader",
        "version": "1.20130207-1"
    },
    "c8y_Software": {
        "pi-driver": "pi-driver-3.4.5.jar",
        "pi4j-gpio-extension": "pi4j-gpio-extension-0.0.5.jar",
        ...
    }
}

HTTP/1.1 201 Created
Content-Type: application/vnd.com.nsn.cumulocity.managedobject+json;charset=UTF-8;ver=0.9
...
{
    "id": "2480300",
    "lastUpdated": "2013-08-30T10:12:24.378+02:00",
    "name": "RaspPi BCM2708 0000000017b769d5",
    "owner": "admin",
    "self": "https://.../inventory/managedObjects/2480300",
    "type": "c8y_Linux",
    "c8y_IsDevice": {},
    ...
    "assetParents": {
        "references": [],
        "self": "https://.../inventory/managedObjects/2480300/assetParents"
    },
    "childAssets": {
        "references": [],
        "self": "https://.../inventory/managedObjects/2480300/childAssets"
    },
    "childDevices": {
        "references": [],
        "self": "https://.../inventory/managedObjects/2480300/childDevices"
    },
    "deviceParents": {
        "references": [],
        "self": "https://.../inventory/managedObjects/2480300/deviceParents"
    }
}

上記の例では、デバイスの多数のメタデータ項目が含まれています。

  • “c8y_IsDevice"は、Things Cloudのデバイス管理アプリケーションで管理できるデバイスを示します。
  • “com_cumulocity_model_Agent"は、Things Cloud エージェントを実行しているデバイスを示します。これらのデバイスは、ルーティングのためにデバイス自体とその子を対象とするすべてのオペレーションを受け取ります。
  • “c8y_SupportedOperations"は、このデバイスを再起動および構成できることを示しています。 さらに、ソフトウェアおよびファームウェアの更新も実行できます。

詳細については、フラグメントライブラリを参照してください。

デバイスが正常に作成された場合は、ステータスコード201が返されます。 例にあるように、元のリクエストに「Accept」ヘッダーが含まれる場合、今後のリクエストでオブジェクトを参照するためのIDとURLを含む、作成されたオブジェクト全体が返されます。 返されるオブジェクトには、子デバイスのコレクションへのリファレンスと、子をデバイスに追加するために使用できる子アセットも含まれます(下記参照)。

ステップ3: デバイスを登録する

新しいデバイスを作成後、ステップ1で説明したように、そのデバイスを組み込み識別子と関連付けることができます。これにより、デバイスは次回の電源投入後にThings Cloudで検出できるようになります。

上記の例に続き、新しく作成したデバイス「2480300」をそのハードウェアのシリアル番号に関連付けます。

POST /identity/globalIds/2480300/externalIds HTTP/1.1
Content-Type: application/vnd.com.nsn.cumulocity.externalid+json
Accept: application/vnd.com.nsn.cumulocity.externalid+json
...
{
    "type" : "c8y_Serial",
    "externalId" : "raspi-0000000017b769d5"
}

HTTP/1.1 201 Created
Content-Type: application/vnd.com.nsn.cumulocity.externalid+json;charset=UTF-8;ver=0.9
...
{
    "externalId": "raspi-0000000017b769d5",
    "managedObject": {
        "id": "2480300",
        "self": "https://.../inventory/managedObjects/2480300"
    },
    "self": "https://.../identity/externalIds/c8y_Serial/raspi-0000000017b769d5",
    "type": "c8y_Serial"
}

ステップ4: インベントリのデバイスを更新する

ステップ1で、デバイスが以前に登録されていると返された場合、インベントリのデバイスの表記が最新であるかを確認する必要があります。 そのために、PUTリクエストがインベントリのデバイスのURLへ送信されます。 変更可能なフラグメントのみが転送されることに注意してください(フラグメントの詳細については、Things Cloudのドメインモデルを参照してください。)

例えば、デバイスのハードウェア情報は通常変更されませんが、ソフトウェアのインストールが変更されている可能性があります。 したがって、デバイスの再起動後に、インベントリのソフトウェア情報を最新の状態にする必要があリます。

PUT /inventory/managedObjects/2480300 HTTP/1.1
Content-Type: application/vnd.com.nsn.cumulocity.managedobject+json
...
{
    "c8y_Software": {
        "pi-driver": "pi-driver-3.4.6.jar",
        "pi4j-gpio-extension": "pi4j-gpio-extension-0.0.5.jar"
    }
}

HTTP/1.1 200 OK
重要
エージェントからデバイス名を更新しないよう注意してください! エージェントは、インベントリで識別できるようにデバイスのデフォルト名を作成しますが、ユーザーがアセット管理の情報より、この名前の編集や更新ができるようにするべきです。

ステップ5: 子デバイスを検出し、インベントリでそれらを作成または更新する

センサーネットワークの複雑さに応じて、デバイスは、関連する子デバイスを有することができます。家庭のさまざまな部屋にさまざまなセンサーやコントロールを設置するホームオートメーションゲートウェイなどがその良い例です。子デバイスの基本登録方法は、子デバイスが通常エージェントインスタンスを実行しないというところまでは、メインデバイスの登録と同じです(したがって、「com_cumulocity_model_Agent」フラグメントは省略されます)。デバイスを子にリンクするには、オブジェクトの作成時に返された子デバイスのURLへ、POSTリクエストを送信します(上記参照)。

例えば、下記URLを持つ子デバイスが作成されたとします。 “https://…/inventory/managedObjects/2543801” このデバイスを親デバイスとリンクするには、次のコマンドを発行します。

POST /inventory/managedObjects/2480300/childDevices HTTP/1.1
Content-Type: application/vnd.com.nsn.cumulocity.managedobjectreference+json
{
    "managedObject" : {
        "id" : "2543801"
    }
}

HTTP/1.1 201 Created

最後に、URLにDELETEリクエストを発行することによって、デバイスと参照を削除することができます。例えば、作成したばかりの親デバイスから子デバイスへの参照を削除するには、次のコマンドを発行します。

DELETE /inventory/managedObjects/2480300/childDevices/2543801 HTTP/1.1

HTTP/1.1 204 No Content

この場合、インベントリのデバイス自体は削除されず、参照のみが削除されます。デバイスを削除するには、次のコマンドを発行します。

DELETE /inventory/managedObjects/2543801 HTTP/1.1

HTTP/1.1 204 No Content

このリクエストは、登録情報、メジャーメント、アラーム、イベント、オペレーションなど、デバイスに関連するすべてのデータを削除します。 通常、デバイスを自動的に削除することはお勧めできません。例えば、デバイスの接続が一時的に失われただけの場合、通常そのデバイスに関連するすべての履歴情報を失いたくないでしょう。

ステップ6: オペレーションを完了し、サブスクライブする

Things Cloudの各オペレーションは、実行フローを通じて循環されます。Things Cloudアプリケーションを介してオペレーションが作成されると、PENDING状態になります(つまり、実行のためにキューに入れられてはいても、まだ実行されていない状態です)。エージェントがオペレーションを選択して実行を開始すると、そのオペレーションにはThings Cloudで「EXECUTING」というマークが付けられます。その後、エージェントは、デバイスまたはその子デバイスに対してオペレーションを実行します(例えば、デバイスを再起動したり、リレーを設定したりします)。その後、デバイスまたはその子デバイスの新しい状態を反映してインベントリを更新するでしょう (例:インベントリのリレーの現在の状態を更新します)。次に、エージェントはThings Cloud内のオペレーションを「SUCCESSFUL」または「FAILED」のいずれかとしてマークし、場合によってはエラーを表示します。

オペレーションステータスダイアグラム

この実行フローの利点は、オフラインで一時的に通信範囲外にあるデバイスにも対応できることです。 また、ファームウェアのアップグレードなど、再起動が必要なオペレーションをデバイスが対応できるようになります。再起動後、デバイスは以前に実行した内容を知る必要があるため、すべての「EXECUTING」オペレーションを照会して成功したかどうかを確認する必要があります。また、キューに入れられた新しいオペレーションをリッスンする必要があります。

まだステータスが「EXECUTING」であるオペレーションをクリーンナップするには、エージェントIDおよび状況別にオペレーションを照会します。 下記の例では、リクエストは次のようになります。

GET /devicecontrol/operations?agentId=2480300&status=EXECUTING HTTP/1.1

HTTP/1.1 200 OK
Content-Type: application/vnd.com.nsn.cumulocity.operationcollection+json;charset=UTF-8;ver=0.9
...
{
    "next": "https://.../devicecontrol/operations?agentId=2480300&status=EXECUTING",
    "operations": [
        {
            "creationTime": "2013-08-29T19:49:15.239+02:00",
            "deviceId": "2480300",
            "id": "2593101",
            "self": "https://.../devicecontrol/operations/2480300",
            "status": "EXECUTING",
            "c8y_Restart": {
            }
        }
    ],
    "statistics": {
        "currentPage": 1,
        "pageSize": 2000
    },
    "self": "https://.../devicecontrol/operations?agentId=2480300&status=EXECUTING"
}

再起動はうまくいったようです – もとに戻りました。オペレーションを「SUCCESSFUL」に設定します。

PUT /devicecontrol/operations/2593101 HTTP/1.1
Content-Type: application/vnd.com.nsn.cumulocity.operation+json
{
    "status": "SUCCESSFUL"
}

HTTP/1.1 200 OK

次に、Things Cloudで作成された新しいオペレーションをリッスンします。 このメカニズムは、Things CloudThings Cloud OpenAPI仕様仕様のデバイスコントロール通知APIで説明されており、標準のBayeuxプロトコルに基づいています。まず、ハンドシェイクが必要です。 ハンドシェイクは、エージェントが通知用として対応しているプロトコルをThings Cloudに伝え、クライアントIDをエージェントに割り当てます。

POST /notification/operations HTTP/1.1
Content-Type: application/json
...
[ {
    "channel": "/meta/handshake",
    "version": "1.0"
} ]

HTTP/1.1 200 OK
...
[ {
    "minimumVersion": "1.0",
    "clientId": "139jhm07u1dlry92fdl63rmq2c",
    "supportedConnectionTypes": [
        "long-polling",
        "smartrest-long-polling",
        "websocket"
    ],
    "channel": "/meta/handshake",
    "version": "1.0",
    "successful": true
} ]

その後、それぞれのデバイスのエージェントはオペレーションを実行する為の通知にサブスクライブする必要があります。 これは、デバイスのIDをサブスクリプションのチャネルとして使用するPOSTリクエストで行います。 次の例では、Raspberry Piがエージェントを実行し、IDは2480300です。

POST /notification/operations HTTP/1.1
Content-Type: application/json
...
[ {
    "channel": "/meta/subscribe",
    "subscription": "/2480300",
    "clientId":"139jhm07u1dlry92fdl63rmq2c"
}]

HTTP/1.1 200 OK
...
[ {
    "channel": "/meta/subscribe",
    "subscription": "/2480300",
    "successful": true
} ]

最後に、デバイスは接続され、オペレーションが送信されるのを待ちます。

POST /notification/operations HTTP/1.1
Content-Type: application/json
...
[ {
    "connectionType": "long-polling",
    "channel": "/meta/connect",
    "clientId": "139jhm07u1dlry92fdl63rmq2c"
} ]

このリクエストは、オペレーションが通るまで保留されます。つまり、HTTPサーバーがすぐに応答できずとも、デバイスのオペレーションが行われるまで待機します(ロングポーリング)。

新しいオペレーションをサブスクライブするとき、「PENDING」のオペレーションがある可能性に注意してください。これらをすべて照会する必要があります。クエリとサブスクリプションの間のオペレーションが失われないよう、これらはサブスクリプション後に実行されます。この技術的な処理は、前述の「EXECUTING」オペレーションと同じですが、代わりに「PENDING」を使用します。

GET /devicecontrol/operations?agentId=2480300&status=PENDING HTTP/1.1

サイクルフェーズ

ステップ7: オペレーションを実行する

まず、エージェントのオペレーションがキューに入れられていると仮定します。これにより、上記のロングポーリングのリクエストがオペレーションで返されます。下記は、単一の構成オペレーションでのレスポンスの一例です。

HTTP/1.1 200 OK
...
[
    {
        "id": "139",
        "data": {
            "creationTime":"2013-09-04T10:53:35.128+02:00",
            "deviceId": "2480300",
            "id": "2546600",
            "self": "https://.../devicecontrol/operations/2546600",
            "status": "PENDING",
            "description": "Configuration update",
            "c8y_Configuration": { "config": "#Wed Sep 04 10:54:06 CEST 2013\n..." }
        },
        "channel": "/2480300"
    }, {
        "id": "3",
        "successful": true,
        "channel": "/meta/connect"
    }
]

エージェントがオペレーションを取得すると、Things Cloud上ではPUTリクエストを使用して「EXECUTING」状態になります(上記の「FAILED」の例を参照)。 デバイスでオペレーションを実行し、Things Cloudインベントリで必要な更新を実行します。最後に、結果に応じてオペレーションは「SUCCESSFUL」または「FAILED」に設定されます。 そして、上記のように”/notification/operations"に再接続し、次のオペレーションを待ちます。

キューに入れられたオペレーションが失われないように、デバイスは10秒以内にサーバーに再接続する必要があります。 これは、Things Cloudがリアルタイムのデータをバッファーする時間です。 間隔はハンドシェイク時に指定できます。

ステップ8: インベントリを更新する

通常、デバイスのインベントリ内容は最新の状態を表すので、継続的な更新の対象になります。 例として、GPSチップを搭載したデバイスを考えてみましょう。このデバイスは、インベントリの現在地を最新の状態に保ちます。 同時に、位置の更新とイベントを報告して、位置の追跡を維持します。技術的にこのような更新は、ステップ4と同じリクエストで報告されます。

ステップ9: メジャーメントを送信する

Things Cloudで新しいメジャーメントを作成するには、メジャーメントとともにPOSTリクエストを発行します。 下記は、信号強度のメジャーメントを作成する例になります。

POST /measurement/measurements HTTP/1.1
Content-Type: application/vnd.com.nsn.cumulocity.measurement+json
...
{
    "source": { "id": "2480300" },
    "time": "2013-07-02T16:32:30.152+02:00",
    "type": "SignalStrength",
    "c8y_SignalStrength": {
        "rssi": { "value": -53, "unit": "dBm" },
        "ber": { "value": 0.14, "unit": "%" }
    }
}

HTTP/1.1 201 Created

ステップ10: イベントを送信する

同様に、イベントにもPOSTリクエストを使用します。 次の例は、GPSセンサーからの位置の更新を示しています。

POST /event/events HTTP/1.1
Content-Type: application/vnd.com.nsn.cumulocity.event+json
...
{
    "source": { "id": "2480300" },
    "text": "位置が更新されました",
    "time": "2013-07-19T09:07:22.598+02:00",
    "type": "LocationUpdate",
    "c8y_Position": {
        "alt": 73.9,
        "lng": 6.151782,
        "lat": 51.211971
    }
}

HTTP/1.1 201 Created

Things Cloudのすべてのデータタイプには、追加でフラグメントという形の任意の拡張を含むことができます。上記では、イベントに位置情報が含まれていますが、自己定義のフラグメントを追加することも可能です。

ステップ11: アラームを送信する

アラームとは、解決するために人の介入を必要とするイベントを表します。例えば、デバイスのバッテリーが切れた場合、誰かがデバイスのバッテリーを交換する必要があります。 アラームの作成は、技術的にはイベントの作成と非常によく似ています。

POST /alarm/alarms HTTP/1.1
Content-Type: application/vnd.com.nsn.cumulocity.alarm+json
Accept: application/vnd.com.nsn.cumulocity.alarm+json
...
{
    "source": { "id": "10400" },
    "text": "Tracker lost power",
    "time": "2013-08-19T21:31:22.740+02:00",
    "type": "c8y_PowerAlarm",
    "status": "ACTIVE",
    "severity": "MAJOR",
}

HTTP/1.1 201 Created
Content-Type: application/vnd.com.nsn.cumulocity.alarm+json
...
{
    "id": "214600",
    "self": "https://.../alarm/alarms/214600",
    ...
}

ただし、同じようなアラームがシステム内ですでに有効になっている場合は、デバイスのアラームを作成しない方がよいでしょう。多数のアラームを作成すると、ユーザーインターフェースがいっぱいになり、すべてのアラームを手動でクリアする必要が生じる場合があります。上の例は、Raspberry Piのアクティブアラームを見つけるためのものです。

GET /alarm/alarms?source=2480300&status=ACTIVE HTTP/1.1

イベントとは異なり、アラームは更新ができます。 問題が解決した場合(例:バッテリーが交換され、電源が復旧した場合)、対応するアラームが自動的にクリアされ、手動での作業が不要になります。これは、アラームのURLへのPUT要求によって実行できます。 上記のアラーム作成の例では、「Accept」ヘッダーを使用して、レスポンス内の新しいアラームのURLを取得しました。このURLを使用してアラームをクリアできます。

PUT /alarm/alarms/214600 HTTP/1.1
Content-Type: application/vnd.com.nsn.cumulocity.alarm+json
...
{
    "status": "CLEARED"
}

HTTP/1.1 200 OK

イベントの送信、あるいはアラームの発生のどちらを選ぶか判断ができない場合は、とりあえずイベントを発生させて、ユーザー自身にリアルタイムルールより、イベントをアラームへ変換するかどうか決定させるとよいでしょう。

物理デバイスの交換

すでにThings Cloudプラットフォームに接続されている物理デバイスを、その外部 ID とデバイスが収集したデータを維持したまま交換できます。 下記を実行します。

  1. 古い物理デバイスの電源を切ります。
  2. 通常のデバイスと同様に、新しいデバイスを登録と起動します。
  3. デバイスが新しいマネージドオブジェクトを作成した後、新しい物理デバイスの電源をオフにします。
  4. Things Cloudのデバイス管理アプリケーションで新しいデバイスオブジェクトを開き、デバイスの所有者とデバイスの外部 ID を検索します。
  5. デバイスから外部IDを削除します。
  6. Things Cloudのデバイス管理アプリで古いデバイスを開き、その所有者を調べたものに変更し、新しいデバイスから削除した外部 ID も追加します。
  7. 以前に作成した新しいデバイスオブジェクトを削除し、デバイスユーザーは保持します。
  8. 新しい物理デバイスの電源を入れます。

新しい物理デバイスは、データを既存のマネージドオブジェクトに送信します。

注意
上記の手順は、デバイスが標準のデバイスブートストラップを使用している場合にのみ機能します。 それ以外の場合は、デバイス インテグレーターまたはメーカーにお問い合わせください。
備考
デバイスに子デバイスがある場合は、その所有者も更新する必要があります。

デバイス認証

デバイスは、次の方法で Things Cloud プラットフォームに認証できます。

  • デバイス ユーザー認証情報 (デバイスのユーザー名とパスワードを使用)
  • 証明書認証 (定義された REST エンドポイント プロトコルを経由して X509 証明書を使用し、ポート 8443 で JWT セッション トークンを取得します)。

相互 TLS (mTLS) は、通信セッションでのクライアントとサーバーの両方の認証に X.509 証明書を使用するセキュリティ プロトコルです。

mTLS プロトコルは、Web サービス、API、およびその他のネットワーク アプリケーションでの接続を保護するために一般的に使用されます。 mTLS を使用してトークンを生成する場合、プロセスには、X.509 証明書を使用したクライアントとサーバーの両方の認証が含まれます。

証明書を使用してプラットフォームからデバイス アクセス トークンを取得する場合、テナント ID、ユーザー名、パスワードは必要ありません。認証情報は証明書から取得されます。 デバイス証明書の直接の発行者が信頼できる証明書リストにアップロードされている場合、デバイス リーフ証明書のみを送信することでデバイス アクセス トークンを取得できます。アップロードされた信頼できる証明書がデバイス証明書の直接の発行者ではないが、デバイスの信頼チェーンに属している場合、デバイスは X-Ssl-Cert-Chain の証明書チェーン全体を送信して正常に認証され、デバイス アクセス トークンを取得する必要があります。

JWTセッショントークンの取得

デバイスは、以下のエンドポイントを使用してThings Cloudに対してX.509証明書を使用して認証できます。これに応じて、認証が成功した後、JWT セッション トークンが Things Cloud によって発行され、後続のリクエストに使用できます。

デバイス アクセス トークン API は、次の curl ステートメントを実行することで呼び出すことができます。

curl -v -cert domain-cert.pem -key domain-private-key.pem \
   -H 'Accept: application/json' \
   -H 'X-Ssl-Cert-Chain:<device certificate chain>' \
   -X POST \
   https://<Things Cloud tenant domain>:8443/devicecontrol/deviceAccessToken

Things Cloudへの登録時には、<device certificate chain> を有効な証明書チェーンに置き換えてください。Things Cloudにデバイス証明書の即時発行者がある場合、ヘッダー X-Ssl-Cert-Chain は必須ではありません。

curl -v -cert domain-cert.pem -key domain-private-key.pem \
   -H 'Accept: application/json' \
   -X POST \
   https://<Things Cloud tenant domain>:8443/devicecontrol/deviceAccessToken

以下のようなレスポンスが返されます。

HTTP/1.1 200 Success
Content-Type: application/vnd.com.nsn.cumulocity.managedobject+json; charset=UTF-8; ver=0.9
...
{
    "accessToken": "eyJhbGciOiJSUI6IkpXVCJ9.eyJktYTJmYy0x...S 04HPk3GQUd-fHyJ2oKSuetWFWpUSBPzJzl_73_3yauIlplHorlSoQ"
}

デバイストークンは、REST呼び出しを介してThings Cloudにアクセスするために使用されます。デバイストークンを取得した後は、以降のリクエストで証明書を送信する必要はありません。 デバイストークンのライフタイムは、oauth.internalのカテゴリとdevice-token.lifespan.secondsのキーを使用してテナントオプションを使って構成できます。 デフォルト値は1時間です。 最小許容値は5分です。 詳細については、Things Cloud OpenAPI仕様のテナントAPIを参照してください。

デバイスは、HTTP接続を閉じる前に明示的にlogout APIを呼び出すことでセッションを無効にすることを推奨します。これにより、生成されたJWTセッショントークンの誤用を防ぐことができます。こちらがログアウトAPIです。詳細については、Things Cloud OpenAPI仕様のユーザーAPIを参照してください。

    POST /user/logout
    Accept: application/json
    Content-Type: application/json
    Authorization: Bearer "JWT Session token"
注意
証明書認証を使用するために登録されたデバイスのみが、このエンドポイントを使用してJWTセッショントークンを取得できます。デバイスが証明書を使用して正常に認証されると(つまり、プライベートキーと証明書チェーンを使用して)、デバイスはJWTセッショントークンを取得します。このmTLS over HTTPエンドポイントは、ポート8443上のこのエンドポイントに対してのみ使用できます。

RESTクライアント例

Hello REST

このセクションでは、Things Cloudでデバイス表現を作成し、それに関連するメジャーメントデータを送信する、非常に基本的な例を紹介します。

すべての手順はRESTインターフェースを呼び出すことで実行します。これらのREST呼び出しは、コマンドラインで実行できるCURLステートメントで示されています。

CURLの簡単な使い方についてはRESTインターフェースの利用を参照してください。

前提条件

このチュートリアルを進めるには、次の前提条件を満たしていることを確認してください。

  • Things Cloudにアクセスするための有効なテナント、ユーザー、パスワードを持っていること。
  • コマンドラインツールCURLがシステムにインストールされていること。

REST呼び出しの実行

これから、2つのREST呼び出しを順番に実行します。詳細は次の通りです。

  • ステップ1: Things Cloudのインベントリに新しいデバイスを作成する
  • ステップ2: そのデバイスに関連するメジャーメントデータを送信する

現実のユースケースでは、これらのステップは「デバイスエージェント」によって実行されます。

ステップ1は、デバイスが初めてThings Cloudに接続された際に一度だけ実施されます。

その後は、この手順で返される内部IDでデバイスを参照することで、デバイスに関連する操作が可能となります。

新しいデバイスを作成する

Things Cloudのインベントリに新しいデバイスを作成するには、次のRESTリクエストが必要です。

POST /inventory/managedObjects HTTP/1.1
Content-Type: application/vnd.com.nsn.cumulocity.managedobject+json
Accept: application/vnd.com.nsn.cumulocity.managedobject+json
Authorization: Basic <<Base64 encoded credentials <tenant ID>/<username>:<password> >>
...
{
    "c8y_IsDevice" : {},
    "name" : "HelloWorldDevice"
}

この呼び出しは、次のcurlステートメントを実行することで行えます。

curl -v -u <username>:<password> \
   -H 'Accept: application/vnd.com.nsn.cumulocity.managedobject+json' \
   -H 'Content-type: application/vnd.com.nsn.cumulocity.managedobject+json' \
   -X POST \
   -d '{"c8y_IsDevice":{},"name":"HelloWorldDevice"}' \
   https://<Things Cloud tenant domain>/inventory/managedObjects

<username><password><tenant-ID>は、Things Cloudに登録した際に与えられた認証情報で置き換えてください。

Things Cloud Web GUIへアクセスする際と同じ認証情報でREST呼び出しを実行できます。

次のようなレスポンスが返されます。

HTTP/1.1 201 Created
Content-Type: application/vnd.com.nsn.cumulocity.managedobject+json; charset=UTF-8; ver=0.9
Authorization: Basic <<Base64 encoded credentials <tenant ID>/<username>:<password> >>
...
{
    "id": "1231234"
    "lastUpdated": "2014-12-15T14:58:26.279+01:00",
    "name": "HelloWorldDevice",
    "owner": "<username>",
    "self": "https://.../inventory/managedObjects/1231234",
    "c8y_IsDevice": {},
    ...
}

デバイスを作成すると、Things CloudによってIDが発行されます。これは今後このデバイスを参照する際に必要となります。レスポンスの"id"属性値として確認できます。

メジャーメントデータを送信する

デバイス作成後、メジャーメントデータを送信できます。

ここでは、摂氏の単位で測定された温度メジャーメントを、ある時刻に収集したものとして送信します。

POST /measurement/measurements
Content-Type: application/vnd.com.nsn.cumulocity.measurement+json
Accept: application/vnd.com.nsn.cumulocity.measurement+json
...
{
    "c8y_TemperatureMeasurement": {
        "T": {
            "value": 21.23,
            "unit":"C"
        }
    },
    "time": "2014-12-15T13:00:00.123+02:00",
    "source": {
        "id": "1231234"
    },
    "type":"c8y_PTCMeasurement"
}

idの値は、最初のステップで受け取ったものに適切に置き換えてください。

また、Things CloudのUIで後からメジャーメントを見つけやすいよう、time値も最近のタイムスタンプに更新してください。

タイムスタンプ値のデータフォーマットについては、Swagger/OpenAPI Specificationdate-timeとして説明されていますのでご参照ください。

curl -v -u <username>:<password> \
   -H 'Accept: application/vnd.com.nsn.cumulocity.measurement+json' \
   -H 'Content-type: application/vnd.com.nsn.cumulocity.measurement+json' \
   -X POST \
   -d '{"c8y_TemperatureMeasurement":{"T":{"value":21.23,"unit":"C"}},"time":"2014-12-15T13:00:00.123+02:00","source":{"id":"1231234"},"type":"c8y_PTCMeasurement"}' \
   https://<Things Cloud tenant domain>/measurement/measurements/

このリクエストのレスポンスは次のようになります。

HTTP/1.1 201 Created
Content-Type: application/vnd.com.nsn.cumulocity.measurement+json; charset=UTF-8; ver=0.9
...
{
    "id": "4711",
    "self": "https://.../measurement/measurements/4711",
    "source": {
        "id": "1231234",
        "self": "https://.../inventory/managedObjects/1231234"
    },
    "time": "2014-12-15T12:00:00.123+01:00",
    "type": "c8y_PTCMeasurement",
    "c8y_TemperatureMeasurement": {
        "T" : {
            "unit" : "C",
            "value" : 21.23
        }
    }
}

必要に応じてメジャーメントの送信を繰り返すこともできます。再度リクエストを送信する前に、’time’属性の値を更新して時系列データを作成してください。

これで完了です。Things Cloud UIのデバイス管理アプリケーションに入り、「すべてのデバイス」ページで自身のデバイスを選択し、「メジャーメント」タブに切り替えてください。ここでメジャーメントデータを確認できます。

データが表示されない場合は、例えば「先週」など、提出したメジャーメントのタイムスタンプを含むフィルタ設定に変更する必要があるかもしれません。

さらに進むには

ここで紹介したREST呼び出しの手順は、デバイス統合で説明されている手順を短縮したものです。 最初のステップ(新規デバイス作成)は「スタートアップフェーズ」に該当し、2つ目のステップ(メジャーメント送信)は「サイクルフェーズ」に該当します。

実際のエージェント実装に必要な情報を得るには、デバイス統合のセクションを参照してください。


Hello X509 REST

このセクションでは、Things Cloudを使用してmTLSプロトコルでJWTトークンを生成する方法を学びます。 Things Cloudとの認証にはX.509証明書を使用します。デバイスアクセス用トークンAPIはポート8443でのみアクセスできます。

前提条件

このチュートリアルを進めるには、以下の前提条件を確認してください。

  • Maven 3とJava 8以上がインストールされていることを確認してください。

  • OpenSSLがインストールされていることを確認してください。

    $ mvn -v
    Maven home: /Library/Maven/apache-maven-3.6.0
    Java version: 1.8.0_201, vendor: Oracle Corporation, runtime: /Library/Java/JavaVirtualMachines/jdk1.8.0_201.jdk/Contents/Home/jre
    Default locale: en_GB, platform encoding: UTF-8
    OS name: "mac os x", version: "10.14.2", arch: "x86_64", family: "mac"
    

MavenはMavenのウェブサイトからダウンロードできます。

Mavenプロジェクトをコピーする

cumulocity-examplesリポジトリからMavenで構成されたJavaプロジェクトをコピーし、次のコマンドを実行します。

$ mvn clean install

これによりプロジェクトがコンパイルされます。

有効な証明書を生成する

有効な証明書がない場合は、以下の手順に従ってテスト用に証明書を生成できます。

  1. cumulocity-examplesリポジトリからスクリプトをダウンロードします。

  2. ルート自己署名証明書を作成し(スクリプト 00createRootSelfSignedCertificate.sh を実行)、テナントにアップロードします。これをUIのデバイス管理アプリケーションまたはRESTを介して行うことができます。

  3. 証明書を作成し、署名します(スクリプト 01createSignedCertificate.sh を実行)。

  4. 証明書をキーストアに移動します(スクリプト 02moveCertificatesToKeystore.sh を実行)。

  5. 対応する環境から公開サーバーキーをダウンロードし、次のコマンドを使用してJKSにインポートします。

    $ keytool -import -file platform.dev.c8y.io.crt -alias servercertificate -keystore truststore.jks
    

デバイスアクセス用トークンAPIを呼び出す前に、以下の構成が必要です。

  • KEYSTORE_NAME - デバイスが自身を認証するために使用するプライベートキーと証明書チェーンを含むキーストアのパス。
  • KEYSTORE_PASSWORD - プライベートキーを使用するためにキーストアに作成されたパスワード。
  • KEYSTORE_FORMAT - ファイル形式に応じて「JKS」または「PKCS12」。パスはKEYSTORE_NAMEによって提供されます。
  • TRUSTSTORE_NAME - サーバーの証明書を含むトラストストアのパス。
  • TRUSTSTORE_PASSWORD - トラストストアへのアクセスパスワード。
  • TRUSTSTORE_FORMAT - ファイル形式に応じて「JKS」または「PKCS12」。パスはTRUSTSTOREによって提供されます。
  • PLATFORM_URL - プラットフォームのURL。
  • PLATFORM_MTLS_PORT - ポート8443はデバイスアクセス用トークンAPIに利用可能。
  • DEVICE_ACCESS_TOKEN_PATH API - mTLSプロトコルを担当するエンドポイント。
  • LOCAL_DEVICE_CHAIN - PEM形式の全チェーン。このヘッダーは必須ではなく、Things Cloudにデバイス証明書の即時発行者を信頼された証明書としてアップロードしている場合は省略可能です。

構成を変更する

REST Javaクライアントの構成を変更するには、ファイル chain-with-private-key-iot-device-0001.jks をリソースフォルダにコピーし、構成を設定します。使用されるスクリプト(ステップ4)は、パスワード changeit を使用します。スクリプト内で値を変更した場合は、以下の例でも KEYSTORE_PASSWORDTRUSTSTORE_PASSWORD を同様に変更してください。

        private static final String KEYSTORE_NAME = "chain-with-private-key-iot-device-0001.jks";
        private static final String KEYSTORE_PASSWORD = "changeit";
        private static final String KEYSTORE_FORMAT = "jks";
        private static final String TRUSTSTORE_NAME = "truststore.jks";
        private static final String TRUSTSTORE_PASSWORD = "changeit";
        private static final String TRUSTSTORE_FORMAT = "jks";
        private static final String LOCAL_DEVICE_CHAIN = "-----BEGIN CERTIFICATE----- MIIcQhNJJ0F/lfjm -----END CERTIFICATE-----";
        private static final String PLATFORM_URL = "<URL of the platform>";
        private static final String PLATFORM_MTLS_PORT = "8443";

デバイスはこれでJWTトークンを生成できます。最初の接続前に他のアクションを行う必要はないことに注意してください。たとえば、ユーザーを作成する必要はありません。ユーザーは自動登録プロセス中に作成されます。

備考
REST Javaクライアントで証明書を使用する場合、パスワード、ユーザー、テナントを設定する必要はありません。Things Cloudは提供された証明書によってテナントとユーザーを認識します。

このデータを入力した後、例のクライアントは提供されたデータを使用して、証明書を介して指定されたプラットフォームにデバイスアクセス用トークンを取得します。

一般的に、mTLSプロトコルクライアントは、SSLを介したセキュアな接続を提供するためにJava開発キットの一部であるJava Secure Socket Extensionを使用します。 JSSEは、開発者がそのクラスを使用して構成できるSSLおよびTLSプロトコルのJava実装を提供します。 Java Secure Socket Extensionのドキュメントは、SSL接続がどのように確立されるかを示し、実装のカスタマイズに関するいくつかの例を提供します。 完全なドキュメントは公式Oracleウェブサイトで入手できます。 Things Cloud mTLSプロトコルはセキュアなSSL接続をサポートしています。

main のコードは何をするのでしょうか?

  • mTLS接続を構成します。
  • mTLSプロトコルを介してThings Cloudに接続します。
  • X.509証明書を使用して成功した認証後にJWTトークンを生成します。
  • このJWTトークンを使用して、さらなるREST操作を証明書なしで実行できます。

アプリケーションのビルドと実行

アプリケーションをビルドするには、以下のコマンドを使用します。

      $ cd x509-rest-client
      $ mvn clean install
      ...
      [INFO]
      [INFO] --- maven-jar-plugin:2.4:jar (default-jar) @ x509-rest-client ---
      [INFO] Building jar: /home/schm/Pulpit/device-jwt-rest-client/target/x509-rest-client-1.0-SNAPSHOT.jar
      [INFO]
      [INFO] --- maven-install-plugin:2.4:install (default-install) @ x509-rest-client ---
      [INFO] Installing /home/schm/Pulpit/x509-rest-client/target/x509-rest-client-1.0-SNAPSHOT.jar to /home/schm/.m2/repository/c8y/example/x509/x509-rest-client/1.0-SNAPSHOT/x509-rest-client-1.0-SNAPSHOT.jar
      [INFO] Installing /home/schm/Pulpit/x509-rest-client/pom.xml to /home/schm/.m2/repository/c8y/example/x509/x509-rest-client/1.0-SNAPSHOT/x509-rest-client-1.0-SNAPSHOT.pom
      [INFO] ------------------------------------------------------------------------
      [INFO] BUILD SUCCESS
      [INFO] ------------------------------------------------------------------------
      [INFO] Total time: 2.642 s
      [INFO] Finished at: 2017-03-14T09:16:25+01:00
      [INFO] Final Memory: 14M/301M
      [INFO] ------------------------------------------------------------------------

実行するには以下のコマンドを使用します。

      $ mvn exec:java -Dexec.mainClass="c8y.example.x509.X509RestClient"
      ...
      [INFO]                                                                         
      [INFO] ------------------------------------------------------------------------
      [INFO] Building x509-rest-client 1.0-SNAPSHOT
      [INFO] ------------------------------------------------------------------------
      [INFO]
      [INFO] --- exec-maven-plugin:1.6.0:java (default-cli) @ x509-rest-client ---
      access_token ="eyJhbGciOiJSUI6IkpXVCJ9.eyJktYTJmYy0x...S 04HPk3GQUd-fHyJ2oKSuetWFWpUSBPzJzl_73_3yauIlplHorlSoQ"