RESTインターフェイスの使用法

RESTについて

概要

Things Cloudでは、すべての外部通信にRESTを採用しています。 通信の発信元がIoTデバイス、Webアプリケーション、またはバックオフィスITシステムであるかに関わらず、通信プロトコルは常にRESTです。

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

ここでの説明は、各インターフェースの詳細が記載されたリファレンスガイドと密接に関連しています。 リファレンスガイド内の関連する章は、次のとおりです。

JavaまたはC#を使用してアプリケーションを開発する場合は、Things Cloudの機能にさらに簡単にアクセスできるよう、このガイドの関連するセクションもご覧ください。 RESTインターフェース全般、およびRESTによるデバイス統合の詳細については、デバイスSDKガイドの REST セクション をご覧ください。

RESTインターフェースの使用法

現在、ほとんどのプログラミング環境において、RESTベース通信への対応が際立っています。Things CloudのRESTインターフェースを理解し、使用するにあたり、利用できる膨大なコマンドラインツールやブラウザ拡張機能が役立ちます。

例えば、多数のオペレーティングシステムに「curl」コマンドが予めインストールされています。Things Cloud APIのブラウジングを開始したい場合、以下のコマンドラインが基本となります。

$ curl -u <username>:<password> https://<yourTenant>.je1.thingscloud.ntt.com/platform

「username」と「password」を、自分がThings Cloudに登録する際に使用するユーザー名とパスワードに書き換えます。同様に、「yourTenant」を、テナント名に書き換えます。

このコマンドはThings Cloudのあらゆる基本インターフェースにリンクを返します。

...
"inventory": {
    "managedObjects": {
        "references": [],
        "self": "https://<yourURL>/inventory/managedObjects"
    },
    "managedObjectsForFragmentType": "https://<yourURL>/inventory/managedObjects?fragmentType={fragmentType}",
    "managedObjectsForListOfIds": "https://<yourURL>/inventory/managedObjects?ids={ids}",
    "managedObjectsForType": "https://<yourURL>/inventory/managedObjects?type={type}",
    "self": "https://<yourURL>/inventory"
},
...

出力形式をもっと整えたい場合、「curl … | python -m json.tool」を試してみてください。

$ curl -u <username>:<password> https://<yourTenant>.je1.thingscloud.ntt.com/platform | python -m json.tool

この時点より、返されるさまざまなオブジェクトへ移動することができます。たとえば、managedObjectsエンドポイントに従って、インベントリ内の項目を取得できます。

$ curl -u <username>:<password> https://<yourTenant>.je1.thingscloud.ntt.com/inventory/managedObjects

インベントリ内の項目のサブセットが実際に返されることが分かります。これが所謂「ページ」です。ページについての詳細はクエリ結果のページングをご覧ください。

Postmanの使用

RESTインターフェースやThings Cloudデータベースのコンテンツを探索する場合、PostmanのようなグラフィカルRESTクライアントが便利です。

Example REST client

Software AG 社から多くの API コマンドのサンプルが提供されています。利用する場合、Postman をダウンロードしてインストール してください。Postman を開始した後、create an account または、Take me straight to the app を選べます。下のボタンをクリックし、インストールした Postman のタイプを選びます。ブラウザ( Windows の"Electron”)から、Postman を本当に実行してよいかセキュリティープロンプトが出る場合もあります。

Run in Postman

では、最上段のタブにある Collections をクリックしてください。Cumulocity API というフォルダーがサンプルと共に表示されているはずです。そのフォルダとサブフォルダ Alarms を開き、Get collection of alarms をクリックしてください。これで Things Cloud からどのようにアラームを取得するかの例が示されます。

例にはプレースホルダが含まれていることに注意してください。この例では、 {{url}}/alarm/alarms の {{url}} がプレースホルダです。 Postmanにこれらのプレースホルダの埋め方と、Things Cloudのアカウントへの接続方法を指定する必要があります。そのためには、environment を作成し、プレースホルダを設定してください。

Postman environment setup

キー auth の正しい値を簡単に得るには、Web上から手に入るBase64エンコード/デコード・ツールを使用すると良いでしょう。たとえば、テナントIDが"tenant”、ユーザー名が"me”、パスワードが"secret"とします。まず、ostermiller.org/calc/encode.htmlへ行き、tenant/me:secretをテキストボックスに入力し、“Base 64"列のEncodeをクリックします。変換後、テキストは"dGVuYW50L21lOnNlY3JldA=="になり、キー auth の値には"Basic dGVuYW50L21lOnNlY3JldA=="を設定してください。

次のようにBase64コマンドを使用しても、同じ結果が得られます。

$ echo -n tenant/me:secret | base64

色々なAPI を使ってみましょう。

アプリケーションの開発

本セクションでは、アプリケーション開発向けにThings Cloud REST APIを使用する場合の基本的なユースケースを簡単に説明します。

基本ユースケース

アセットの登録

アセットとは、あなたのビジネスとアプリケーションが対象としているオブジェクトのことです。たとえば、主にビル管理またはホームオートメーションのビジネスを展開する場合、アセットはビルや部屋になります。または、機器の修理に関するビジネスであれば、アセットは経路や機器となるでしょう。

アセットはデバイスとともにインベントリに保存されますが、多くの場合、デバイスに依存しない独自の構造を持っています。アセットを作成するには、インベントリ内のマネージドオブジェクトのコレクションにPOSTし作成します。たとえば、インベントリに新たな建物を作成するには、次のように入力します。

POST /inventory/managedObjects
Content-Type: application/vnd.com.nsn.cumulocity.managedobject+json
Accept: application/vnd.com.nsn.cumulocity.managedobject+json
Authorization: Basic ...
{
    "name": "Building 043",
    "type": "c8y_Building"
}

応答:

HTTP/1.1 201 Created
Content-Type: application/vnd.com.nsn.cumulocity.managedobject+json;charset=UTF-8;ver=0.9
...

デバイス(アセット)が正常に作成された場合は、ステータスコード201が返されます。上記の例のように、元の要求に「Accept」ヘッダーが含まれている場合は、今後の要求でオブジェクトを参照するためのIDとURLを含む、完全なオブジェクトが返却されます。返されるオブジェクトには、デバイスに子を追加するために使用できる子デバイスおよび子アセットへの参照も含まれます。

たとえば、部屋も作成し、その部屋の「self」プロパティが「https://…/inventory/managedObjects/2549700」である場合、部屋を建物にリンクするには、建物の子アセットコレクション(上記の「childAssets」の「self」プロパティ参照)にPOSTします。

POST /inventory/managedObjects/2549800/childAssets HTTP/1.1
Content-Type: application/vnd.com.nsn.cumulocity.managedobjectreference+json
{

ここで建物を再度取得すると、部屋が建物の子として登録されていることが分かります。

GET /inventory/managedObjects/2549800

応答:

HTTP/1.1 200 OK
Content-Type: application/vnd.com.nsn.cumulocity.managedobject+json; charset=UTF-8; ver=0.9
...

デバイスとアセットのリンク

アセットを他の子アセットにリンクするのと同様に、アセットをモニタリングおよび制御するデバイスにリンクすることもできます。たとえば、部屋に光センサーが設置されており、その光センサーのURLが「https://…/inventory/managedObjects/2480500」であるとします。次のように、部屋のchildDevicesにPOSTします。

POST /inventory/managedObjects/2549700/childDevices
Content-Type: application/vnd.com.nsn.cumulocity.managedobjectreference+json
{
    "managedObject" : {
        "self" : "https://.../inventory/managedObjects/2480500"
    }
}

アセットと外部システムとの同期

多くの場合、企業のアセットを扱うITシステムはThings Cloudだけではありません。外部ITシステムに格納されているアセットの同期の手順は、デバイスの登録手順とまったく同じです。

特定の機能の問い合わせ

アプリケーションを特定のタイプのデバイスの仕様から切り離すために、アプリケーションはフラグメントを使用してインベントリに問い合わせることができます(Things Cloud のドメインモデルのフラグメントのセクションをご覧ください)。たとえば、位置情報を持つすべてのマネージドオブジェクトを検索するには、以下を使用します。

GET /inventory/managedObjects?fragmentType=c8y_Position&withTotalPages=true

応答:

HTTP/1.1 200 OK
Content-Type: application/vnd.com.nsn.cumulocity.managedobjectcollection+json; charset=UTF-8; ver=0.9
...

たとえば、プロパティのc8y_Position を使用して、マップ上にオブジェクトを配置することができます。標準フラグメントは、デバイス管理ライブラリセンサー・ライブラリで定義されています。

/platform リソースに問い合わせると、さらにどのようなデータを問い合わせることができるかがわかります(デバイスSDKガイドの RESTを使用したデバイス統合をご覧ください)。

クエリは必ずしもすべての結果を一度に返すわけではなく、1ページ分の結果のみを返すことに注意してください。ページングの詳細については、RESTの実装>RESTの使用>クエリ結果のページングをご覧ください。オプションパラメータwithTotalPagesを指定すると、クエリにページ全体の統計情報が含まれますが、パフォーマンスは多少低下します。

センサーデータの問い合わせ

インベントリと同様に、特定のセンサーデータを取得することもできます。たとえば、前月(この文章の執筆時点から)の光センサーメジャーメントを次のように問い合わせることができます。

GET /measurement/measurements?dateFrom=2019-04-01&dateTo=2019-05-31&fragmentType=c8y_LightMeasurement

応答:

HTTP/1.1 200 OK
Content-Type: application/vnd.com.nsn.cumulocity.measurementcollection+json; charset=UTF-8; ver=0.9
...

オペレーションをデバイスへ送信

デバイスのオペレーションをトリガーするには、オペレーションをデバイス制御APIへPOSTします。次の例では、ID「2480300」のデバイスを再起動します(デバイスSDKガイド内のデバイス統合で統合されているRaspberry Pi)。

POST /devicecontrol/operations
Content-Type: application/vnd.com.nsn.cumulocity.operation+json;
Accept: application/vnd.com.nsn.cumulocity.operation+json;
{
    "deviceId": "2480300",
    "c8y_Restart":{}
}

応答:

HTTP/1.1 201 Created
Content-Type: application/vnd.com.nsn.cumulocity.operation+json; charset=UTF-8; ver=0.9
...

POSTコマンドは、該当のデバイスに対するオペレーションを作成した後、その作成結果を即座に返します。オペレーションの作成と実行は非同期です。リクエスト例でオプションの「Accept」ヘッダーを追加しましたので、応答においてはselfプロパティにおけるURLを含む、全ての発行済オペレーションを取得することになります。そのURLに対してGETを使用すると、以下の通り、現在のオペレーション実行状態をチェックすることができます。

GET /devicecontrol/operations/2550200 HTTP/1.1

HTTP/1.1 200 OK
Content-Type: application/vnd.com.nsn.cumulocity.operation+json; charset=UTF-8; ver=0.9
{
    "status": "PENDING",
    ...
}

ここでの「PENDING」状態とは、デバイスがまだオペレーションを受け取っていないことを意味します。「EXECUTING」は、デバイスがオペレーションを実行中であることを意味します。最後に、「SUCCESSFUL」または「FAILED」は、オペレーションが完了したことを示します。

イベントの応答を受信する

Things Cloudデータストアへの問い合わせに加え、Things Cloudにおけるリアルタイム処理に記載の通り、イベントをリアルタイムで処理/受信することもできます。例えば、リアルタイムの位置更新情報をマップに表示させたいと想定しましょう。この場合、管理アプリケーション(または、REST API)を使用して、「myRule」という新しいルールモジュールを作成します。

select *
from EventCreated e
where e.event.type = "c8y_LocationUpdate";

位置情報の更新を送信するデバイスをお持ちの場合、ユーザーインターフェースに即座に表示されるはずです。自分のRESTクライアントでこれらを受け取りたい場合、リアルタイム通知APIを使ってそれらにチャネル接続できるようにします。このAPIは、HTTPSロングポーリングを使用したBayeuxプロトコルに基づいています。適用される制限については、リアルタイム通知をご覧ください。まず、ハンドシェイクが必要です。ハンドシェイクは、クライアントが通知のためにサポートするプロトコルを Things Cloud に伝え、クライアントにクライアントIDを割り当てる機能です。

POST /cep/notifications
Content-Type: application/json
...
[ {
    "id": "1",
    "supportedConnectionTypes": ["long-polling"],
    "channel": "/meta/handshake",
    "version": "1.0"
} ]

HTTP/1.1 200 OK
...
[ {
    "id": "1",
    "supportedConnectionTypes": ["long-polling"],
    "channel": "/meta/handshake",
    "version": "1.0",
    "clientId": "71fjkmy0495rxrkfcmp0mhcev1",
    "minimumVersion": "1.0",
    "successful": true
}]

ハンドシェイクの後、クライアントは上記のルールの出力にチャネル接続する必要があります。これは、モジュール名とステートメント名を接続チャネルとして持つPOST要求を使用して行われます。次の例では、モジュール名に「myRule」を使用し、selectステートメント("@Name('')")に名前を付与していませんので、接続チャネルは「/myRule/*」になります。

POST /cep/notifications
Content-Type: application/json
...
[ {
    "id": "2",
    "channel": "/meta/subscribe",
    "subscription": "/myRule/*",
    "clientId": "71fjkmy0495rxrkfcmp0mhcev1"
}]

HTTP/1.1 200 OK
...
[ {
    "id":"2",
    "channel": "/meta/subscribe",
    "subscription": "/myRule/*",
    "successful": true,
} ]

最後に、クライアントは接続状態のまま、イベントが送られてくるのを待ちます。

POST /cep/notifications HTTP/1.1
Content-Type: application/json
...

このリクエストは、モジュールイベントが発生するまでブロックします。一つの位置更新を含む応答例を紹介します。

HTTP/1.1 200 OK
...
[
    {
        "id": "139",
        "data": {
            "creationTime": "...",
            "id": "2481400",
            "self": "https://.../event/events/2481400",
            "source": {
                "id": "2480700",
                "name": "RaspPi BCM2708 0000000017b769d5 Gps eM9",
                "self": "https://.../inventory/managedObjects/2480700"
            },
            "text": "Location updated",
            "time": "...",
            "type": "c8y_LocationUpdate",
            "c8y_Position": {
                "alt": 58.34,
                "lng": 6.769717,
                "lat": 51.267259
            },
            "channel": "/myRule/*"
        },
        "id": "3",
        "successful": true,
        "channel": "/meta/connect"
    }
]

SmartRESTの使用

Things Cloud REST APIは、ほとんどの環境から簡単に使用できる汎用IoTプロトコルを提供します。これはどのようなIoTユースケースにもアドホックに適応させることができ、また標準的なインターネット通信およびセキュリティーメカニズムを利用します。適切な標準技術を利用しているため、独自IoTプロトコルと比べ先進的である一方、ローエンドのマイクロコントローラまたは低帯域幅通信チャネルなど、非常に制約の多い環境には課題をもたらすことがあります。

こうした環境向けに、Things Cloud はいわゆる「SmartREST」プロトコルを提供します。SmartRESTは以下のように、標準技術の利点と特別仕様のプロトコルの利点を同時に提供するものです。

次のセクションでは、SmartRESTの背景にある概念と、使用する基本プロトコルについて説明します。SmartRESTは後述の通り、テンプレートを使ってメタデータをペイロードデータから分離する処理を基本としています。最後に、SmartRESTを使用してデータを送受信する方法を説明します。プロトコル詳細はSmartRESTリファレンスをご覧ください。

SmartRESTの動作方法

下記の画像は、SmartRESTの動作を図示したものです。デバイスおよび他のクライアントは Things Cloud 上の専用SmartRESTエンドポイントに接続し、それぞれのデータをコンマ区切り値からなる行として送信します。これらの行は Things Cloud のSmartRESTプロキシにより、標準の Things Cloud REST APIリクエストへと展開されます。同様に、Things Cloud からの応答は通常のJSON形式からプロキシによってコンマ区切り値に圧縮された後、デバイスに返却されます。

SmartREST architecture

Things Cloud はコンマ区切り値をどのようにして意味のあるRESTリクエストとして解釈するのか?

まず、これを行うためにデバイスはテンプレートを Things Cloud に登録します。テンプレートは、展開されたRESTリクエスト形式をプレースホルダと一緒に保持しており、その中へ Things Cloud の SmartREST プロキシが連続的にコンマ区切り値を挿入します。応答の場合、テンプレートはコンマ区切り値を構築するために、構造化されたREST応答からどの値を抽出するを記述します。

テンプレートは、デバイスのソフトウェアまたはファームウェアのバージョンに関連付けられます。通常、デバイスまたはアプリケーションに固有な実装は、用途に応じたリクエストタイプに対応しており、決まった範囲の要求を発行します。同じ実装のデバイスはすべて、同じ一連のリクエストタイプを共有するのです。したがって、テンプレートは実装段階で定義できます。テンプレートを Things Cloud で使用できるようにするため、特定の実装を伴う最初のデバイスがそのテンプレートを送信し、すべての同様のデバイスで利用できるようにします。

以下の図は、このプロセスを表したものです。「Device_1.0」のバージョンを実装したデバイスがSmartREST経由での通信を開始すると仮定します。デバイスは認証情報を取得した後、SmartRESTプロキシに対し、テンプレートが登録済みであるかを問い合わせます。テンプレートがサーバー上で見つからない場合、デバイスはテンプレートを単一の静的テキストリクエストとして Things Cloud へ送信します。この手順が完了したら、このテンプレートを使用する同類のデバイスはすべて、テンプレートをサーバーに再送しなくても、SmartRESTを使用する通信を開始することができます。

SmartREST templates

この例は、変換プロセス概要も例示しています。「Template 1」において、「%%」はプレースホルダであり、これをSmartRESTプロキシが埋めます。「time」はサーバー側タイムスタンプに置き換えられます(下記参照)。残りのプレースホルダはリクエストデータに置き換えられます。リクエスト例における行「1,200,20.5」は以下のように解釈されます。

基本的なSmartRESTプロトコル

すべてのSmartRESTリクエストの基本構造は次のとおりです。

上記の例で言えば、SmartRESTリクエストは以下の通りとなります。

POST /s HTTP/1.1
Authorization: Basic ...
X-Id: Device_1.0

1,200,20.5

これに呼応する応答例として以下が挙げられます。

HTTP/1.1 200 OK
Transfer-Encoding: chunked

20,0

リクエストと応答を照合するため、応答行には、エラーコードの次に、応答に対応するリクエストを示す行が含まれます。この例で言えば、「20」は「OK」を意味し、「0」はリクエストの1行目を指します。

テンプレートの登録方法は?

前述の通り、クライアントは SmartREST を使用する場合、まず、SmartRESTテンプレートがすでにサーバーにとって既知であるかどうかを尋ねます。これは以下のような空のSmartRESTリクエストを使用して行われます。

POST /s HTTP/1.1
Authorization: Basic ...
X-Id: Device_1.0

デバイス実装が既知であれば、応答はIDを返し、このIDはその後のリクエストにおける「X-Id」ヘッダーの「簡略表現」として使用することができます。

HTTP/1.1 200 OK

20,<id>

デバイス実装が未知の場合、応答は以下のようになります。

HTTP/1.1 200 OK

40,"No template for this X-ID"

この場合、あなたのデバイス実装で使用する全てのテンプレートを作成します。

POST /s HTTP/1.1
Authorization: Basic ...
X-Id: Device_1.0

10,1,POST,/measurement/measurements,application/vnd.com.nsn.cumulocity.measurement+json,,%%,NOW UNSIGNED NUMBER,{ "time": "%%", "type": ... }
...

この例では、「10」はリクエストテンプレートを指します(一方、「11」は応答テンプレートを指します)。このテンプレートは「1」番ですから、このテンプレートを使用するSmartRESTリクエストは最初のカラムに「1」が付くことになります。このテンプレートはエンドポイント「/measurement/measurements」宛の「POST」リクエストを参照し、コンテンツ種別は「application/vnd.com.nsn.cumulocity.measurement+json」となります。このテンプレートで使用されるプレースホルダは「%%」です。プレースホルダはタイムスタンプ(「NOW」)と、符号なし数値および一般数値です。最後に、最終カラムは、入力され送信されることになるリクエストの本体を格納します。

応答はどのように扱われますか?

上記の例ではリクエストとリクエストテンプレートの取り扱いを例示しました。応答の場合、JSONPath表現がThings Cloud REST応答をCSVに変換します。例えば、デバイスにディスプレイ画面があり、メッセージをその画面に表示できるとしましょう。メッセージを更新するためのオペレーションは以下となります。

{
	"c8y_Message": {
		"text": "Hello, world!"
	},
	"creationTime": "2019-02-25T08:32:45.435+01:00",
		"deviceId": "8789602",
		"status": "PENDING",
		...
}

クライアント側では、デバイスは主に表示されることになるテキストを知る必要があります。JSONPathでは、以下の構文を使用して「text」プロパティを抽出します。

$.c8y_Message.text

この構文において、「$」はデータ構造のルートを指し、「.」はデータ構造からの要素を選択します。オプションについて詳しくはJSONPathリファレンスをご覧ください。

デバイスは通常、自らに関連するオペレーションと、PENDING状態のオペレーションをすべて問い合わせます。そうしたクエリに対する Things Cloud の標準応答は以下の通りです。

{
	"operations": [{
		"c8y_Message": {
				"text": "Hello, world!"
		},
		"creationTime": "2014-02-25T08:32:45.435+01:00",
		"deviceId": "8789602",
		"status": "PENDING",
		...
	}, {
		"c8y_Relay": {
			...
		}
	...
	}]
}

つまり、応答にはオペレーションのリストが含まれ、これらのオペレーションはさまざまな種類を有する場合があります。そうした構造に対処するには、以下の応答テンプレートを使用します。

11,2,$.operations,$.c8y_Message,$.c8y_Message.text

これは値ごとに説明すると、以下を意味します。

結果、SmartRESTクライアントは以下の応答を返されることになります。

HTTP/1.1 200 OK

2,0,"Hello, world!"

この応答は、表示メッセージを変換するためのテンプレートであるテンプレート2を使用して作成されました。応答は最初に送られたリクエストを参照します。実際に設定されるメッセージは「Hello, world!」です。