プラグイン向けWeb SDK

プラグイン向けWeb SDK は非推奨です。新しいWebアプリケーションを開発する場合、 Angular向けWeb SDK を使用することを推奨します。すでに開発済みのプラグインがある場合、 Angular向けWeb SDKへプラグインをインポートする方法については、Angular向けWeb SDK > 移行 を参照してください。

概要

続くセクションでは、プラグイン向け Web SDK の概要を説明します。これにより、次のことができるようになります。

アーキテクチャ

上図では、アプリケーションやプラグイン開発の背景となるアーキテクチャを示しています。コアアプリケーションと同様に、AngularJS や Cumulocity の提供する “c8y.core”, “c8y.ui” JavaScript API 上に構築できます。これらのモジュールによって、Things Cloud Web アプリケーションへのアクセスサービスが提供されます。“c8y.core” モジュールはユーザー、マネージドオブジェクトのような異なるデータ種別へのアクセスサービス、そして基本機能を提供するのに対し、“c8y.ui” はアプリケーションやプラグインのユーザーインターフェースの修正(例えばメニューアイテムやウィジェットの追加)のサービスを提供します。モジュールは、Things Cloud で提供される REST API を組み合わせて利用しています。プラグイン例でどのようにこれらのサービスを利用するかの例が示されます。

まず、この文書では、アプリケーションやプラグインの背後にあるコンセプトを説明します。そして必要とされるフォルダ構成とアプリケーションやプラグインで異なるオプション設定を規定します。続いてアプリケーションとプラグインを開発するのに必要なセットアップが書かれています。プラグイン向け Web SDK は次のような構成です:

その後、サンプルプラグインの作り方を順を追って説明します:

次の文書には、別のもっと複雑な例があります:

この章でアプリケーションとプラグインのコンセプトの概要が説明されますが、アプリケーション開発に記載される Things Cloud アプリケーションの基本コンセプトを見ておくことをお勧めします。

コンセプト

アプリケーションやプラグインを構築する前に、アプリケーションやプラグインとは何かを正しく理解することが重要です。この意味で、アプリケーションは Cumulocity UI フレームワークをベースとし、Things Cloud UI をつくっています。デフォルトでは、Things Cloud UI は3つのコアアプリケーション、つまり “デバイス管理”、“管理”、“コックピット” で構成されています。そして、アプリケーションはプラグインで構成されています。プラグインはアプリケーションに追加したいすべての機能を表現しています。プラグインでできることは以下です:

下に画面を示します:

プラグインの拡張箇所

例として、“コックピット” アプリケーションが利用しているプラグインリストの抜粋を見てみましょう。以下が含まれます:

Things Cloud のコアアプリケーション(管理、コックピット、デバイス管理)の機能を拡張 できることに注意してください。通常のテナントでは、拡張前にコアアプリケーションの複製を作成しなければなりません。アプリケーションの複製は、“管理” の UI 経由で複製するか、任意のアプリケーションとして同一のプラグインを使った新たなアプリケーションを作成するかのいずれかでできます。

プロジェクト構成

新しいアプリケーションやプラグインを作成するときは、必ず次のフォルダ構成に従わなければなりません。従わない場合、アプリケーションやプラグインが動作しないことがあります。 アプリケーションのデフォルトのフォルダ構成は以下の通りです:

<<root folder>>
├── cumulocity.json
|	...
└── plugins
	└── <<plugin name>>
		├── cumulocity.json
		└── index.js
			...

アプリケーションのフォルダ内には、"アプリケーションマニフェスト” と呼ぶ情報が “cumulocity.json” ファイルに格納されます。“plugins” フォルダには、アプリケーションによって提供されるプラグイン単位のフォルダが含まれています。プラグインのフォルダ名は、アプリケーション名と合わせて一意になっています。プラグインフォルダには、"プラグインマニフェスト” と呼ぶ情報が別の “cumulocity.json” ファイルに格納されます。アプリケーションマニフェストとプラグインマニフェストのフォーマットは以下に記載されています。 プラグインを既存アプリケーションに追加するだけでよい場合、上述のフォルダ構成の一部を使います:

<<root folder>>
└── <<plugin name>>
	├── cumulocity.json
	└── index.js
		...

プロジェクト用の明示的なルートフォルダを作成してください。 Web SDK は、ルートフォルダの親フォルダがオペレーティングシステムのユーザーに読み取り可能であることを前提とします。

マニフェスト

アプリケーションマニフェスト

アプリケーションマニフェストは、アプリケーションがどこに格納されているか、また Things Cloud でどのように見られるかが記述されています。次のプロパティが利用できます:

注記:“contextPath” と “key” は一意である必要があります。“PRIVATE” アプリケーションの場合、“name” と “contextPath” はあなたのテナント内のみで一意である必要があります。

プラグインマニフェスト

プラグインマニフェストとは、あなたのプラグインの Things Cloud  管理・アプリケーション内での表示方法(名称、説明、カテゴリー、ギャラリー、リスト)と、プラグインを実行するためにビルドおよびロードされる必要のあるファイル(ngModules、js、imports、css、less、copy)を示すものです。

セットアップ

前提条件

プラグインは HTML5 をベースとしており、以下の技術に精通していることが望ましいでしょう:

プラグインを開発し、実行するための前提条件は以下の通りです。

Cumulocity CLI ツール

前提条件が満たされれば、独自のアプリケーションとプラグインのビルドの準備はほとんどできたようなものです。プラグイン開発(ビルド、テーマ設定、変換、デプロイ)の処理には、npm パッケージ “cumulocity-tools” がコンピュータにインストールされている必要があります。npm パッケージのインストールには、次のコマンドをターミナルで実行してください。

$ npm i cumulocity-tools -g

これでコマンドラインインターフェース(CLI)ツールが利用できるようになります。次のコマンドを試してみてください:

$ c8y --help

“–help” オプションは、CLI ツールで使えるすべてのコマンドを表示します。

Cumulocity UI パッケージ

上述のように、アプリケーションはプラグインの集まりです。プラグインセットが準備されており、独自のものを追加ビルドすることができます。ですがその前に、“package.json” ファイルをアプリケーション用のフォルダに追加する必要があります。“package.json” を自動生成するには、次のコマンドを実行します:

$ npm init

注記:このコマンドでは “package.json” ファイルに含めるいくつかのプロパティの値を入力する必要があります。“package.json” ファイルは少なくとも name と version を含める必要があります。プロパティ入力をスキップする場合、enter キーを押してください。

続いて、次のコマンドを入力して、プラグインセットを含む Cumulocity UI のインストールを続けてください:

$ c8y install latest

このコマンドでは:

このバージョンはバックエンドのバージョン番号と同じか、それより小さくなければなりません。

注記:プロジェクトを共有する場合、他の開発者はアプリケーションプロジェクトのルートフォルダ内で npm install を実行すれば大丈夫です。それは、Cumulocity UI パッケージのバージョンはすでに “package.json” ファイルに dependency として定義されているからです。c8y install を走らせればいつでも他のバージョンをインストールできます。

アプリケーションのプラグインリストを見るには、 “c8y util:showimports [appContextPath]” とします。

サンプルプラグイン

全てのセットアップとフォルダ構成、マニフェストについて理解したら、最初のアプリケーションプラグイン"Hello World!” を走らせることができます。

name テナントのベース URL が聞かれ、またユーザー名、パスワードも聞かれます。これらのプロンプトに毎回入力するのを避けるには、次の環境変数を設定してください。 C8Y_TENANT, C8Y_USER, C8Y_PASS, C8Y_BASE_URL たとえば、Unix システムで “export C8Y_TENANT=demos” と入力すると、デフォルトでテナント “demos” にアプリケーションが作成されます。

アプリケーションデプロイ後、Things Cloud の “管理” アプリケーションの中で、“所有アプリケーション” メニューがあらわれます。

My Application

“Hello World!” プラグインを見るには、作成したサンプルに移動し、“New plugin” メニューを選択してください。あなたは “Hello World!” という文章をみることができます。他のプラグイン例も試すことができます。プラグイン例の詳細については、サイトの各文書を参照してください。

Hello world

このプラグインの目的は、新しいアプリケーションをアプリケーションスイッチャーに追加することです。このアプリケーションはユーザーが1つのメニュー項目をクリックすると “Hello World!” ページを表示するものです。最後に、このアプリケーションは次のように見えます:

Hello world プラグイン

このゴールまでに、次のステップが必要です:

アプリケーションプロジェクトのセットアップ

まず、アプリケーションプロジェクトを以下の手順で作成してください:

上記の手順を踏むと以下のフォルダ構造となっているはずです:

<<root folder>>
├── node_modules
|     └── ...
├── cumulocity.json
└── package.json

アプリケーションマニフェストの設定

次に、アプリケーションマニフェスト(root フォルダ内の"cumulocity.json"ファイル)の名称、キー、URL、依存関係などの Cumulocity アプリケーション情報を埋めてください。今回の例では、以下のプロパティを設定する必要があります:

{
  "availability": "PRIVATE",
  "contextPath": "myapplication",
  "key": "myapplication-appkey",
  "name": "myapplication",
  "resourcesUrl": "/",
  "type": "HOSTED",
  "imports": ["core/c8yBranding"]
}

ここまでのプロジェクト構造があればアプリケーションの検証が可能です。“c8yBranding"プラグインを Things Cloud UI パッケージからインポートへ追加すれば、アプリケーションへアクセスする時、完全に空という状態ではなくなります。呼称でもおわかりのように、このプラグインは Things Cloud のブランディングを自身のアプリケーションへ追加します。 アプリケーションをローカルで検証する場合は最初に自身のテナント内にそれを作成する必要があります。

マニフェストの他のプロパティの詳細については”マニフェスト“をご参照ください。

自身のテナント内にアプリケーションを作成する

Things Cloud の UI アプリケーションへのログインに成功するとアプリケーション・キーは自動的に取得されます。即ち、アプリケーションを開発する場合自身のテナントで作成する必要があります。自身のテナント内でアプリケーションを作成するにはc8y deploy:app [appContextPath]を使用して展開してください。 appContextPath を削除した場合、contextPath はコマンドが実行されたパスの"cumulocity.json"から読み取られます。

$ c8y deploy:app myapplication
? Tenant piedpiper
? User admin
? Password ***********
? Base URL https://piedpier.cumulocity.com
GET application/applicationsByOwner/piedpier?pageSize=10000 200
POST application/applications/31337/binaries/ 201
PUT /application/applications/31337 200

テナント名、テナントのベース URL、ユーザー名、パスワードをそれぞれ請求されます。このようなプロンプトを繰り返し入力することを避けたい場合は以下の環境変数を定義すると良いでしょう: C8Y_TENANTC8Y_USERC8Y_PASSC8Y_BASE_URL.

アプリケーションのデプロイすると、“管理” アプリの “カスタムアプリ “ メニュー に表示されます。

My Application

アプリケーションの検証

自身のアプリケーションをローカルで実行したい場合は c8y serverを実行してください。以下のオプションも利用できます。

{
  "name": "Examples",
  "comment": "Release with additional example plugins",
  "replaceImports": {
    "core/c8yBranding": "myapplication/myBranding"
  },
  "applications": [
    {
      "contextPath": "administration",
      "addImports": ["myapplication/weatherAdmin"]
    },
    {
      "contextPath": "devicemanagement",
      "addImports": [
        "myapplication/deviceEventsRealTime",
        "myapplication/deviceContact"
      ]
    },
    {
      "contextPath": "cockpit",
      "addImports": ["myapplication/weather", "myapplication/iconmap"]
    }
  ]
}

例:

$ c8y server -u https://tenant.cumulocity.com -t targets/examples.json
Cumulocity UI development server running in port 9000.
Proxying api requests to https://tenant.cumulocity.com
140 modules loaded.
5 application manifest loaded.
http://localhost:9000/apps/myapplication/ cumulocity.json
http://localhost:9000/apps/fieldbus4/  Packaged App
http://localhost:9000/apps/administration/  Packaged App
http://localhost:9000/apps/cockpit/  Packaged App
http://localhost:9000/apps/devicemanagement/  Packaged App

これで、URL “http://localhost:9000/apps/myapplication/” を自身のブラウザで開くことによりアプリケーションを検証することができます。アプリケーションへアクセスすると以下を見ることができるはずです:

My Application

あとは、ナビゲータ内に新規のメニューアイテムを追加するプラグインが必要です。

プラグインマニフェストの設定

自身のアプリケーション内にあるプラグインフォルダ内には各プラグインがサブフォルダに分けられ保存されています。プラグインをアプリケーションに追加するには以下の手順を踏む必要があります:

上記の手順を踏むと以下のフォルダ構造となるはずです:

<<root folder>>
├── node_modules
├── plugins
|	└── myplugin
|		├──views
|		|     └── hello.html
|		└── cumulocity.json
├── cumulocity.js
└── package.json

プラグインマニフェストには名称、説明文、必要なファイル、主要アプリに追加する必要のある angular モジュールなど、プラグイン情報が記載されています。例えば、“cumulocity.json"ファイルに以下のスクリプトを追加します:

{
  "name": "Hello world plugin testing",
  "description": "Simple hello world plugin."
}

マニフェストの他のプロパティの詳細については”マニフェスト“をご参照ください。

アプリケーションにプラグインを追加した今、アプリケーションマニフェストのインポート内にも追加する必要があります。インポートの呼称は/を挟んで二つの部位で成り立っています。前半の部分はプラグインが入っているアプリケーションの context path となり、後半の部分はプラグインのフォルダ名となります。今回の例ではアプリケーションマニフェスト内で特定されているように、“myapplication"という context path を持つアプリケーション内にプラグインがあり、“myplugin"という名のプラグインフォルダ名なので、以下の通りとなります:

{
  "availability": "PRIVATE",
  "contextPath": "myapplication",
  "key": "myapplication-appkey",
  "name": "myapplication",
  "resourcesUrl": "/",
  "type": "HOSTED",
  "imports": ["core/c8yBranding", "myapplication/myplugin"]
}

アプリケーションにプラグインフォルダを追加した後は機能性を導入することができます。

プラグイン初期化機能を導入

今回の例題の範囲は狭いですが、それでもモジュラーアプローチをお勧めします。そのために"hello.module.js"というモジュールファイルと"hello.config.js"という設定ファイルと"hello.controller.js"というコントローラを"myplugin"フォルダ内に作成します。

“hello.module.js"ファイル内でプラグインのモジュールを初期化します:

(function() {
  "use strict";

  angular.module("myapp.hello", []);
})();

自作ビューへとリダイレクトするメニューアイテムをナビゲーターへ新規に追加する場合"hello.config.js"を設定する必要があります。そこでCumulocity JavaScript APIで提供されている"c8yNavigatorProvider"と"c8yViewsProvider"を使用します。このサービスを自身の設定へ入れ込み、以下の関数を呼んでください:

(function() {
  "use strict";

  angular.module("myapp.hello").config(configure);

  configure.$inject = ["c8yNavigatorProvider", "c8yViewsProvider"];

  function configure(c8yNavigatorProvider, c8yViewsProvider) {
    c8yNavigatorProvider.addNavigation({
      // adds a menu item to the navigator with ...
      name: "hello", // ... the name *"hello"*
      icon: "cube", // ... the cube icon (icons are provided by the great Font Awesome library and you can use any of their [icon names](http://fontawesome.io/icons/) without the *fa-* prefix here
      priority: 100000, // ... a priority of 100000, which means that all menu items with a priority lower than 100000 appear before this menu item and all with a priority higher than 100000 appear after this menu item
      path: "hello" // ... */hello* as path
    });

    c8yViewsProvider.when("/hello", {
      // when the path "/hello" is accessed ...
      templateUrl: ":::PLUGIN_PATH:::/views/hello.html", //  ... display our html file "hello.html" inside the "views" folder of our plugin (the plugin's folder is represented using the magic string ```:::PLUGIN_PATH:::```, which is replaced by the actual path during the build process)
      controller: "HelloController", // ... use "HelloController" as controller
      controllerAs: "vm"
    });
  }
})();

コントローラーを実装

次に、画面上にコントローラーを実装する必要があります。こちらの例ではコントローラーは"text"の変数をシンプルな固定テキスト"hello, world"として定義します:

(function() {
  "use strict";

  angular.module("myapp.hello").controller("HelloController", HelloController);

  function HelloController() {
    var vm = this;

    vm.text = "hello, world";
  }
})();

モジュール、設定、コントローラーを追加した今、“myapp.hello"としてモジュールを特定し、各 java スクリプトファイルをプラグインマニフェストへ追加する必要があります:

{
  "name": "Hello world plugin testing",
  "description": "Simple hello world plugin.",
  "ngModules": ["myapp.hello"],
  "js": ["hello.module.js", "hello.config.js", "hello.controller.js"]
}

ビューテンプレート

“text"の変数を定義した後はビューテンプレートへアクセスが可能となります。テキストを表示したい場合は以下を自身の hello.html ファイルに追加してください:

<div>{{vm.text}}</div>

アプリケーションの検証

アプリケーションを検証したい場合は、c8y serverコマンドを自身テナントをパラメータに入れた完全な URL と一緒に使用してください。

自身のアプリケーションとプラグインの構築と展開

c8y --helpを走らせれば利用可能なコマンドの一覧を見ることができます。 最終的には Things Cloud の管理に手動で追加可能な zip ファイルとなるアプリケーションを構築するか、または自身のテナントは直接アプリケーションを展開することができます。

build:app

指定フォルダ内にアプリケーションを構築する。(デフォルトは./build) outputFolder 内には[appContextPath]という名のディレクトリと[appContextPath].zip という zip ファイルがあります。この zip ファイルは管理アプリケーション上へアップロードができます。 appContextPath を削除した場合、contextPath はコマンドが実行されたパスの"cumulocity.json"から読み取られます。

$ c8y build:app [appContextPath] [outputFolder]

build:plugin

指定フォルダ内にプラグインを構築する。(デフォルトは./build). outputFolder 内には[pluginName] という名のディレクトリと[pluginName].zip という zip ファイルがあります。この zip ファイルは管理インターフェース上へアップロードができ、どのアプリケーションへも追加が可能です。

$ c8y build:plugin <pluginName> [outputFolder]

deploy:app

プラグインを構築しアプリケーションを組立て、定義されたテナントへアップロードします。もしアプリがリモートインスタンス内に存在しない場合でも自動的に作成されます。appContextPath を削除した場合 contextPath はコマンドが実行されたパスの"cumulocity.json"から読み取られます。

$ c8y deploy:app <appContextPath>

プラグイン構築プロセスの手順は次の通りです:

  1. $injectを使って angular 関数に注釈を付ける。 (ng-annotateの使用方法)
  2. :::PLUGIN_PATH:::を適切なストリングに差替える。
  3. $templateCacheを使って全ての html ファイルが含まれるよう変換する。
  4. マニフェスト内の全ての js ファイルを連結させ最小化する。(UglifyJS 2の使用方法)
  5. 全ての less ファイルをコンパイルする。
  6. less ファイルの css と result を連結させ、最小化する。
  7. マニフェスト内の ‘copy’ に定義された全てのファイルをコピーする。
  8. プラグイン内のローカルズフォルダ内で得られそうなローカル化ファイルを全てコピーする。
  9. プラグインマニフェストをコピーする。
  10. 上記全てを含んだ zip ファイルを作成する。

アプリ構築プロセスの手順は次の通りです:

  1. インポートリストで定義された各プラグインの built バージョンをコピーする。
  2. 各プラグインで得られる全てのローカル化ファイルを集め、利用可能言語ごとに一つの.json と.po ファイルとして組み立てる。
  3. index.html を生成する。
  4. アプリケーションマニフェストをコピーする。
  5. 上記全てを含んだ zip ファイルを作成する。

コアアプリケーション内に独自のプラグインを展開する 

ターゲットを特定することでコアアプリケーションへプラグインを追加または差し替えすることができます。このファイルは名前やパスによる限定はありません。

{
  "name": "Examples",
  "comment": "Release with additional example plugins",
  "applications": [
    {
      "contextPath": "administration",
      "addImports": ["myapplication/myplugin"]
    }
  ]
}

上記の例では自身で開発したプラグインをコアアプリケーションの一つ(ここでは管理アプリケーション)へ追加する方法が記されています。プラグインを特定するとき、必ずプラグインが含まれるアプリケーションの contextPath を記載してください。この例では"myplugin” というプラグインは"myapplication"という contextPath のアプリケーションのプラグインフォルダ内に入っています。

マネージメントテナント上に展開しない場合は以下のフラグメントを自身の.json ファイルへ追加する必要があります:

"allApplications": {
    "availability": "PRIVATE"
}

ターゲットファイルを展開する場合は、c8y deploy:target[targetFile]を実行する必要があります。ただし以下のフォルダ構造であると想定します:

<<root folder>>
├── targets
|     └── target.json
├── plugins
|     └── ...
├── cumulocity.js
└── package.json

下記のコマンドを実行する必要があります:

c8y deploy:target targets/target.json

ターゲット

ターゲットファイルはアプリケーションのビルドを定義したり、オプションを設定したり、各アプリケーションからプラグインを削除するのに使用されるJSONファイルです。

例:

{
  "name": "acme", // Unique name to identify the target
  "options": { // runtime options to customize each option
    "globalTitle": "Acme IoT", // Browser title
    "hide_powered": true, // Toggle the 'Powered by' on the bottom of navigator
    "supportUrl": false, // Customize the support URL. A falsy value will hide the link.
    "rightDrawer": false, // Toggle the right drawer on the app
    "login_extra_link": { // Creates an extra link on login screen
      "url": "https://acme/eula",
      "label": "EULA"
    },
    "company_name": "Acme, Inc", // Company name to be used when needed on the UI
    "guideHrefTemplate": "${docsBaseUrl}", // The template used for help links ${docsBaseUrl}${partialUrl} is the default value
    "docsBaseUrl": "https://acme/help/" // The value for docsBaseUrl variable that can be used in documentation link templates
  },
  /**
  * The replaceImports is a map of plugins that will be replaced in all the applications.
  * Keys represent the existing plugins and values are the plugins that should replace each of them.
  * This technique is very commonly used for branding
  */
  "replaceImports": {
    "core/c8yBranding": "core/acme-branding"
  },

  /**
  * Describe the applications to be built or served locally.
  * For each of the applications there are 3 properties that can be used to change the plugins that are included
  *  - replaceImports: A map of plugins to be replaced
  *  - addImports: A list of plugins to be added
  *  - removeImport: A list of plugins to be removed from the  application
  */
  "applications": [
    {
      "contextPath": "administration"
      "replaceImports": {},
      "addImports": [],
      "removeImports": []
    },
    {
      "contextPath": "devicemanagement"
    },
    {
      "contextPath": "cockpit"
    }
  ]
}

ブランディングプラグイン

ここではアプリケーションのブランディングを変更させるプラグインの開発に関する概要を説明します。

ブランディング用プラグインを開発する前に、まずはアプリケーションとプラグインの基本コンセプトおよび"Hello world!“スタイルデモプラグインを説明した概要の一読をお勧めします。

これらの他、紹介されているプラグインは全てcumulocity-ui-plugin-examplesリポジトリで参照できます。

ブランディング例

主要 CSS はよく使用される CSS フレームワークBootstrap 3を基盤にしています. Things Cloud の基本ブランディングを基に Less 変数をオーバーライドして独自ブランディングを作成することが可能です。

myBranding の例はほかのプラグインと比較して量が多いので、自身のプラグインフォルダへ myBranding フォルダをコピーすると良いでしょう。 中にファイルがいくつかありますが、やる事は一つです:C8yBranding の基本テーマ設定でオーバーライドしている less 変数を定義していくことです。

C8yBranding の基本テーマ設定でオーバーライドしている less 変数を定義していくことです。

ブランディング用プラグインは名前によって識別されているので、名前は固有のものである必要があります。その他、Brandingで終わる必要があります。(例: piedpiperBranding ) アプリケーション内で使用するには、インポートステートメントに追加してください。アプリケーションマニフェスト。これについては以下のリポジトリで参照できます。

使用したいブランディング用ファイルのみが定義されているか必ずご確認ください。そうでなければ、全てのブランディング用ファイルがロードされてしまいます。c8yBranding ブランディングを差し替えるにはターゲットとする.json ファイルを作成する必要があります。最低でも以下が含まれている必要があります:

{
  "name": "Examples",
  "comment": "Release with new branding plugin",
  "replaceImports": {
    "core/c8yBranding": "myapplication/myBranding"
  }
}

ウィジェットプラグイン

ここではダッシュボードに追加できるウィジェットのプラグイン開発の概要について説明します。

ウィジェットプラグインの開発を手掛ける前にアプリケーションとプラグインの基本コンセプトおよび “Hello world!“スタイルデモプラグインを説明した概要の一読をお勧めします。

これらの他、ドキュメント内で照会されているプラグインは全てcumulocity-ui-plugin-examplesリポジトリで参照できます。

アイコンマップ プラグイン

アイコンマッププラグインを使用するとダッシュボード上にデバイスをアイコンとしてマップ上に表示されるウィジェットが新しく利用できるようになります。ウィジェットは以下のように表示されます:

アイコンマップウィジェット

こちらを表示するためには以下の手順を踏んでください:

このプラグインを追加するアプリケーションをすでに作成されていると想定しますが、もし無い場合はリポジトリ内で提供されているアプリケーションをご使用ください。ここで紹介されている例題は"plugins/iconmap"フォルダ内に保存されています。

プラグインの作成

アプリケーションフォルダ内で以下のコマンドを走らせてください:

$ c8y create:plugin iconmap

/plugins/iconmap内のプラグインマニフェストを編集し、以下を追記してください:

{
  "name": "Icon Map",
  "description": "Shows devices on a map using an icon for the device type.",
}

次に以下の内容を含んだ"iconmap.module.js"ファイルをプラグインのルートフォルダ内に作成します:

(function () {
  'use strict';

  angular.module('myapp.iconmap', []);
}());

新規プラグインをインポート一覧へ追加するためにアプリケーションマニフェストを更新してください。

{
  (...)
  "imports": [
    (...)
    "myapplication/iconmap"
  ]
}

ウェジェットリスト上にアイテムを追加する方法

次に、ウィジェットメニューにアイテムを追加する設定ファイルを追加する必要があります。ここではCumulocity JavaScript APIで提供されている"c8yComponentsProvider"サービスを使用できます。このサービスを設定に挿入し以下の関数を呼んでください:

(function () {
  'use strict';

  angular
    .module('myapp.iconmap')
    .config(configure);

  configure.$inject = [
    'c8yComponentsProvider',
    'gettext'
  ];

  function configure(
    c8yComponentsProvider,
    gettext
  ) {
    c8yComponentsProvider.add({ // adds a menu item to the widget menu list with ...
      name: 'iconmap', // ... the identifier *"iconmap"* which has to be unique among the widgets in the application
      nameDisplay: gettext('Icon Map'), // ... the displayed name *"Icon Map"*
      description: gettext('Displays a map with icons for devices instead of markers'), // ... a description
      templateUrl: ':::PLUGIN_PATH:::/views/iconmap.main.html', // ... displaying *"iconmap.main.html"* when added to the dashboard
      options: { noDeviceTarget: true }
    });
  }
}());

デバイス用の画像を取得する方法

まずは、マップ上で表示されたデバイス全ての持つ配列「マーカー」を定義する必要があります。ここでは、デバイスのハードウェアモデルにより画像を割り当てます。画像を取得するには、“c8yBinary"サービスを使用してインベントリ内の全てのバイナリオブジェクトを取得する必要があります。そして、取得した全てのバイナリオブジェクトをフィルタリングし、特定のハードウェアモデルを表す画像を取得します。その後、各デバイスは"c8y_Position"フラグメントに応じてハードウェアモデルの画像があればその画像が、また、該当する画像が存在しない場合はいつものマーカーがマップに表示されます。

(function () {
  'use strict';

  angular
    .module('myapp.iconmap')
    .controller('iconmapController', iconmapController);

  iconmapController.$inject = [
    '$scope',
    '$q',
    'c8yInventory',
    'c8yBinary'
  ];

  function iconmapController(
    $scope,
    $q,
    c8yInventory,
    c8yBinary
  ) {
    $scope.markers = [];

    var getDevicesAndBinaries = {
      devices: getDevicesWithLocation(),
      binaries: c8yBinary.list({})
    };
    $q.all(getDevicesAndBinaries).then(placeTypes);

    function getDevicesWithLocation() {
      var filters = {fragmentType: 'c8y_Position' };
      return c8yInventory.list(filters);
    }

    function placeTypes(devicesAndBinaries) {
      var devicesOfType = createTypeMap(devicesAndBinaries.devices);
      var iconOfType = createIconMap(devicesAndBinaries.binaries);
      angular.forEach(devicesOfType, _.curry(placeType)(iconOfType));
    }

    function placeType(iconOfType, devices, type) {
      var icon = iconOfType[type];
      if (icon) {
        var placeDevices = _.curry(place)(devices);
        c8yBinary.downloadAsDataUri(icon).then(placeDevices);
      } else {
        place(devices);
      }
    }

    function createTypeMap(devices) {
      var typeMap = {};
      angular.forEach(devices, _.curry(addDeviceToTypeMap)(typeMap));
      return typeMap;
    }

    function addDeviceToTypeMap(typeMap, device) {
      var hw = 'default';
      if (device.c8y_Hardware && device.c8y_Hardware.model) {
        hw = device.c8y_Hardware.model;
      }

      if (!typeMap[hw]) {
        typeMap[hw] = [];
      }

      typeMap[hw].push(device);
    }

    function createIconMap(binaries) {
      var iconMap = {};
      angular.forEach(binaries, _.curry(addIconToIconMap)(iconMap));
      return iconMap;
    }

    function addIconToIconMap(iconMap, icon) {
      if (c8yBinary.isImage(icon)) {
        var name = icon.name;
        name = name.substring(0, name.lastIndexOf('.'));
        iconMap[name] = icon;
      }
    }

    function place(devices, uri) {
      angular.forEach(devices, _.curry(placeDevice)(_, uri));
    }

    function placeDevice(device, uri) {
      var pos = device.c8y_Position;
      var marker = {
        lat: pos.lat,
        lng: pos.lng,
        message: '<a href="#/device/' + device.id + '">' + device.name + '</a>'
      };

      if (uri) {
        marker.icon = { iconUrl: uri };
      }

      $scope.markers.push(marker);
    }
  }
}());

モジュール、設定、コントローラーを追加した今、“myapp.iconmap"をモジュールとして特定し、各javaスクリプトファイルをプラグインマニフェスト内に追加する必要があります:

{
	"name": "Icon Map",
	"description": "Shows devices on a map using an icon for the device type.",
  "ngModules": [
    "myapp.iconmap"
  ],
  "js": [
    "iconmap.module.js",
    "iconmap.config.js",
    "iconmap.controller.js"
  ]
}

ウィジェットのビューを作成

設定上すでにウィジェットのビューを含んでいる.htmlファイルを特定しました。この例題のウィジェットは簡単なマップ表示します。ビューにマップを追加する場合はプラグインフォルダ内に"views"フォルダを作成し、以下の記載を含む"iconmap.main.html"を作成してください:

<div ng-controller="iconmapController">
    <leaflet markers="markers" ></leaflet>
</div>

「リーフレット」タグウィジェットにインタラクティブマップを追加しています。デバイスをマップ上に表示する場合はコントローラーで定義した配列を「リーフレット」タグの「マーカー」属性に割り当てます。

プラグインを検証

自身のテナントにプラグインを展開した後、“Icon Map"ウィジェットを作成することができます。注意)デバイスの画像を表示したい場合自身のテナント内にある ファイルリポジトリ内にデバイスタイプ名の画像をアップロードする必要があります。

天気プラグイン

ここで紹介するプラグインはデバイスが置かれている場所の現在の天気情報をダッシュボード上で表示するウィジェットとなります。このウィジェットは以下の図のように表示されます:

天気ウィジェット

以下の手順を踏んでください:

新規プラグインを追加するアプリケーションをすでに作成していると想定しますが、もしアプリケーションが無い場合はリポジトリ内で提供されている上記に記載されたアプリケーションをご使用ください。ここで紹介された例題は “plugins/weather”、“plugins/weatherAdmin”、“plugins/weatherService"の各フォルダ内に格納されています。

Dark Sky APIを使用したプラグインを作成

ここではリポジトリから “weatherService"をダウンロードし、自身のアプリケーション内に保存することをお勧めします。このプラグインはAPIキーを保存・ロードする機能および天気情報を取得する機能を提供します。

アプリケーションマニフェスト内にプラグインが含んでいることを確認してください:

{
  (...)
  "imports": [
    (...)
    "myapplication/weatherService"
  ]
}

APIキーの入力プラグインを作成

自身のアプリケーションフォルダ内で以下のコマンドを走らせてください:

$ c8y create:plugin weatherAdmin

/plugins/weatherAdmin内にあるプラグインマニフェストを編集し、以下の情報を追記してください:

{
  "name": "Weather settings",
  "description": "Configure the API key for weather forecasts",
  "icon": "cloud",
  "category": "Administrator",
  "imports": [
    "myapplication/weatherService"
  ]
}

APIキーをロード、およびユーザーが入力したAPIキーを保存する機能の備わっている"weatherService"プラグインをインポートします。

インポート一覧に新規プラグインを追加するためにアプリケーションマニフェストを更新します。

{
  (...)
	"imports": [
    (...)
    "myapplication/weatherAdmin"
  ]
}

ナビゲーションメニューにアイテムを追加する方法

次に、ナビゲーションメニューにアイテムを追加する設定ファイルを作成する必要があります。ここではCumulocity JavaScript API内で提供している"c8yNavigatorProvider"と"c8yViewsProvider"のサービスを使用します。サービスを設定内に挿入し以下の関数を呼んでください:

(function () {
  'use strict';

  angular
  .module('myapp.weatherAdmin', [ 'myapp.weatherService' ])
  .config(configure);

  configure.$inject = [
    'c8yNavigatorProvider',
    'c8yViewsProvider',
    'gettext'
  ];

  function configure(c8yNavigatorProvider, c8yViewsProvider, gettext) {
    c8yNavigatorProvider.addNavigation({ // adds a menu item to the navigator with ...
      parent: gettext('Settings'), // ... the category *"Settings"*
      name: gettext('Weather'), // ... the name *"Weather"*
      path: 'weather', // ... */weather* as path
      icon: 'cloud' // ... the cloud icon (icons are provided by the great Font Awesome library and you can use any of their [icon names](http://fontawesome.io/icons/) without the *fa-* prefix here
    });

    c8yViewsProvider.when('/weather', { // when the path "/weather" is accessed ...
      templateUrl: ':::PLUGIN_PATH:::/views/weatherAdmin.html' //  ... display our html file "weatherAdmin.html" inside the "views" folder of our plugin (the plugin's folder is represented using the magic string ```:::PLUGIN_PATH:::```, which is replaced by the actual path during the build process)
    });
  }
}());

コントローラーに、APIキーをロードする機能とユーザーが入力したAPIキーを保存する機能を導入する必要があります。APIキーをロードする機能は"weatherService"機能で提供されている"load"を使用します。

(function () {
  'use strict';

  angular
    .module('myapp.weatherAdmin')
    .controller('weatherAdminController', weatherAdminController);

  weatherAdminController.$inject = [
    '$scope',
    'c8yTitle',
    'weatherService',
    'gettext'
  ];

  function weatherAdminController($scope, c8yTitle, weatherService, gettext) {
    $scope.updateKey = updateKey;
    weatherService.load().then(function setOpt(key) {
      $scope.key = key;
    });

    c8yTitle.changeTitle({
      title: gettext('Weather provider settings')
    });

    function updateKey() {
      weatherService.save($scope.key);
    }
  }
}());

プラグイン内に設定とコントローラーを追加した今、“myapp.weatherAdmin"をモジュールとして特定し、各javaスクリプトをプラグインマニフェストに追加する必要があります:

{
  "name": "Weather settings",
  "description": "Configure the API key for weather forecasts",
  "icon": "cloud",
  "category": "Administrator",
  "imports": [
    "myapplication/weatherService"
  ],
  "ngModules": [
    "myapp.weatherAdmin"
  ],
  "js": [
    "weatheradmin.config.js",
    "weatheradmin.controller.js"
  ]
}

APIキーをユーザーが保存できるビューの作成方法

設定上すでにナビゲーションアイテムのビューの入った.htmlファイルを特定しました。ここでは簡単なテキストと入力フィールド、そして保存ボタンが表示させます。このビューをプラグインに追加するにはプラグインフォルダ内に"views"フォルダを作成し、以下の内容を記した"weatherAdmin.main.html"ファイルを作成します:

<div ng-controller="weatherAdminController">
  <div class="col-lg-6 panel panel-clean">
    <p translate>Weather functionality is based on the <a href="https://darksky.net" target="_blank">Dark Sky</a> service. Usage of Dark Sky requires an API key that can be obtained by registering at <a href="https://darksky.net/dev/" target="_blank">https://darksky.net/dev/</a>. Paste the API key below.</p>
    <form class="form-horizontal" name="weatherAdminForm" novalidate>
      <div class="form-group">
        <label for="key" class="control-label" translate>API Key</label>
        <div ng-class="{'has-error': invalid('license')}">
          <input type="text" class="form-control" required name="key" id="key" ng-model="key" c8y-autocomplete="off">
        </div>
      </div>
      <div class="form-group ">
        <button type="submit" class="btn btn-primary" ng-click="updateKey()"
                ng-disabled="weatherAdminForm.$invalid||weatherAdminForm.$pristine" translate>
          Save
        </button>
      </div>
    </form>
  </div>
</div>

ウィジェット用プラグインの作成

アプリケーションフォルダの中で以下のコマンドを走らせてください:

$ c8y create:plugin weather

/plugins/weatherにあるプラグインマニフェスト を編集し、以下の情報を追記してください:

{
  "name": "Weather",
  "description": "Shows the current weather at the location of a device.",
  "category": "Widgets",
  "icon": "cloud",
  "imports": [
    "myapplication/weatherService"
  ]
}

特定の場所の天気情報が得られる"weatherService"プラグインをインポートします。

このプラグインをインポートリストに追加するためにアプリケーションマニフェストを更新してください。

{
  "imports": [
    (...)
    "myapplication/weather"
  ]
}

天気プラグインを使えるようにするために、この2つをアプリケーションマニフェストに追加する必要があります。

はじめに、ユーザが天気ウィジェットのインスタンスを作成できるように、アプリケーションでダッシュボードを使用できるようにする必要があります。これは、2つのコアプラグインをインポートすることにより可能になります:

{
  "imports": [
    (...)
    "core/dashboard2",
    "core/dashboardUI"
  ]
}

次に、アプリケーションがDark Sky APIへリクエストをを送信できるようにする必要があります。このために、 contentSecurityPolicy と呼ばれるアプリケーションマニフェストに、次の値を持つ新しいプロパティを追加しましょう:

{
  (...)
  "contentSecurityPolicy": "connect-src 'self' *.darksky.net"
  (...)
}

ウェジェットのメニューにアイテムを追加する方法

次に、メニューにアイテムを追加する設定ファイルを作成する必要があります。ここではCumulocity JavaScript APIで提供されている"c8yComponentsProvider"サービスが使用可能です。設定内にサービスを挿入し、以下の関数を呼んでください:

(function () {
  'use strict';

  angular
    .module('myapp.weather', [ 'myapp.weatherService' ])
    .config(configure);

  configure.$inject = [
    'c8yComponentsProvider',
    'gettext'
  ];

  function configure(c8yComponentsProvider, gettext) {
    c8yComponentsProvider.add({ // adds a menu item to the widget menu list with ...
      name: 'weather', // ... the identifier *"weather"* which has to be unique among the widgets in the application
      nameDisplay: gettext('Weather'), // ... the displayed name *"weather"*
      description: gettext('Shows the current weather at the location of a device'), // ... a description
      templateUrl: ':::PLUGIN_PATH:::/views/weather.main.html' // ... displaying *"weather.main.html"* when added to the dashboard
    });
  }
}());

デバイスの天気を取得

コントローラーでは、ウィジェットダイアログで選択されたデバイスの位置によって天気情報を取得します。もしデバイスが変更されるとウィジェットも更新されます。

(function () {
  'use strict';

  angular
  .module('myapp.weather')
  .controller('weatherController', weatherController);

  weatherController.$inject = [
    '$scope',
    '$q',
    'weatherService',
    'gettext',
    'c8yInventory'
  ];

  function weatherController($scope, $q, weatherService, gettext, c8yInventory) {
    $scope.$watch('child.config.device', function reInit(newVal, oldVal) {
      if (newVal && !angular.equals(newVal, oldVal)) {
        init();
      }
    }, true);
    init();

    function init() {
      getDevice().then(tryGetWeather).then(showWeather, printError);
    }

    function getDevice() {
      var deviceId = $scope.child.config.device.id;
      $scope.status = gettext('Retrieving device ...');
      return c8yInventory.detail(deviceId);
    }

    function tryGetWeather(res) {
      $scope.device = res.data;

      if (locationAvailable($scope.device)) {
        $scope.status = gettext('Retrieving weather ...');
        return getWeather($scope.device.c8y_Position);
      }

      $scope.status = gettext('Device has not reported a location, cannot retrieve weather.');
      return $q.reject();
    }

    function locationAvailable(device) {
      return device && device.c8y_Position && device.c8y_Position.lat && device.c8y_Position.lng;
    }

    function getWeather(coordinate) {
      return weatherService.weather.getCurrent(coordinate.lat, coordinate.lng);
    }

    function showWeather(weather) {
      $scope.weather = weather;
      $scope.windDirection = {
        'display': 'inline-block',
        '-ms-transform': rotate(weather),
        '-webkit-transform': rotate(weather),
        'transform': rotate(weather)
      };
      $scope.status = 'ready';
    }

    function printError() {
      $scope.status = gettext('Error retrieving weather information.');
    }

    function rotate(weather) {
      var direction = (weather.currently.windBearing + 180) % 360;
      return 'rotate(' + direction + 'deg)';
    }
  }
}());

プラグインに設定とコントローラーを追加した今、“myapp.weather"をモジュールとして特定し、各javaスクリプトファイルをプラグインマニフェスト内に追加する必要があります。

{
  "name": "Weather",
  "description": "Shows the current weather at the location of a device.",
  "category": "Widgets",
  "icon": "cloud",
  "imports": [
    "myapplication/weatherService"
  ],
  "ngModules": [
    "myapplication.weather"
  ],
  "js": [
    "weather.config.js",
    "weather.controller.js"
  ]
}

ウィジェット用のビューを作成

さきほど設定上では、ビューが保存された.htmlファイルを特定しました。ここで、デバイスが位置する温度、気圧、湿度、風力の各情報を記した簡単な表をウィジェット内に表示したいとします。ビューに表を追加したい場合プラグインフォルダ内に"views"フォルダを作成し、以下を追記した"weather.main.html"ファイルを作成します。

<div ng-controller="weatherController" style="padding: 10px">
  <div ng-show="status != 'ready'" class="alert alert-info">{{ status }}</div>
  <div ng-show="status == 'ready'">
    <table class="table">
      <tbody>
        <tr>
          <td>{{ 'Weather' | translate }}</td>
          <td>
            <dark-sky-icon icon="{{ weather.currently.icon }}" uib-tooltip="{{weather.currently.summary | translate }}" tooltip-append-to-body="true"></dark-sky-icon>
          </td>
        </tr>
        <tr>
          <td>{{ 'Temperature' | translate }}</td>
          <td>{{weather.currently.temperature}} C</td>
        </tr>
        <tr>
          <td>{{ 'Pressure' | translate }}</td>
          <td>{{weather.currently.pressure}} hPa</td>
        </tr>
        <tr>
          <td>{{ 'Humidity' | translate }}</td>
          <td>{{weather.currently.humidity * 100}} %</td>
        </tr>
        <tr>
          <td>{{ 'Wind' | translate }}</td>
          <td>{{weather.currently.windSpeed}} {{ 'm/s' | translate }}
            <span class="direction" ng-style="windDirection" uib-tooltip="{{weather.currently.windBearing}} {{ 'deg' | translate }}">↑</span>
          </td>
        </tr>
      </tbody>
    </table>
    <a href="https://darksky.net/poweredby/" target="_blank">Powered by Dark Sky</a>
  </div>
</div>

プラグインを検証

テナントへプラグインを デプロイ した後、天気ウィジェットを作成できるようになっているはずです。天気情報を閲覧するためには、まずはじめにAPIキーを入力する必要があることに注意してください。

タブプラグイン

以下ではデバイスに新規タブを追加するプラグインの開発概要を記載しています。

概要

タブプラグインの開発を手がける前にアプリケーションとプラグインおよび"Hello world!“スタイルのデモプラグインの基礎コンセプトが解説されている 概要の一読をお勧めします。

これらの他、ドキュメント内で照会されているプラグインは全てcumulocity-ui-plugin-examplesリポジトリで参照できます。

デバイス連絡先プラグイン

このセクションではデバイスに「連絡先」というタブを追加するプラグインを作成する方法を紹介します。「連絡先」をクリックすると連絡先を記入できるフォームが出てきます。ユーザーが入力したフォームを保存するとデバイスのオブジェクトととしてインベントリ内に保存されます。新規のタブは以下の図のようになります。

連絡先タブ

これを作成するには以下の手順を踏む必要があります:

ここでは、プラグインを挿入したいアプリケーションをすでに手元に持っていることを想定しておりますが、もし持っていない場合はリポジトリで提供されているものを使用することができます。“plugins/deviceContact"フォルダ内にもここの例題で使用されているアプリが保存されています。

従属物の追加

今回、「デバイスマネジメント」のアプリケーションを拡張したいとしましょう。実際にはデバイスマネジメントにプラグインを追加し、自身のアプリケーションマニフェスト内のインポートに追加することを意味します。開発環境上で既存アプリケーションの使用中プラグインの一覧を表示したい場合は以下のコマンドを走らせてください:c8y util:showimports <appContextPath>

今回の場合:

$ c8y util:showimports devicemanagement

アプリケーションマニフェストのインポート定義へ表示されたプラグイン一覧を追加してください。

注記: もし自身のブランディングプラグインを定義している場合は c8yBranding プラグインを削除する必要があります。

もし、最小主義のアプローチでいく場合はcumulocity-ui-plugin-examples 内の"cumulocity.json"ファイルを読取り、デバイス連絡先プラグインに必要なプラグインのみをインポートしてください。

ヒント c8y util:showimports cockpit または c8y util:showimports administration を走らせ、利用可能なプラグインを表示させてください。“node*modules/cumulocity-ui-build"内にある *_apps.json_ に既存アプリケーション用のマニフェストが保存してあります。

プラグインの作成

自身のアプリケーションフォルダ内で以下のコマンドを走らせてください:

$ c8y create:plugin deviceContact

“plugins/deviceControl” 内にあるプラグインマニフェスト を編集し、以下の情報を追加してください:

{
  "name": "Device Details - Contact",
  "description": "Plugin adds a Contact tab to Device Details view"
}

プラグインのルートフォルダ内に"deviceContact.module.js"というファイルを作成し、以下の内容を入力してください:

(function() {
  "use strict";

  angular.module("myapp.deviceContact", []);
})();

インポート一覧に以下の新規プラグインを追加するために、アプリケーションマニフェストを更新してください:

{
    ...
    "imports": [
      ...
      "myapplication/deviceContact"
    ]
}

デバイスにタブを追加

ここではデバイスの詳細一覧上に空の「連絡先」タブを作成します。以下の手順でタブ内の中身を入れていきます。プラグインフォルダ内に"deviceContact.config.js"というファイルを下記のような内容で作成してください。 “Hello world!"の例題のように独自アプリケーションに新規のビューを追加するために Cumulocity JavaScript APIで提供されている"c8yViewsProvider"サービスを使用します。

(function() {
  "use strict";

  angular.module("myapp.deviceContact").config(configure);

  configure.$inject = ["c8yViewsProvider"];

  function configure(c8yViewsProvider) {
    c8yViewsProvider.when("/device/:deviceId", {
      // when the path "/device/:deviceId" is accessed ...
      name: "Contact", // ... show a tab with the name *"Contact"*
      icon: "envelope-o", // ... use the envelope-o icon (icons are provided by the great Font Awesome library and you can use any of their [icon names](http://fontawesome.io/icons/) without the *fa-* prefix here
      priority: 1000, // ... set the priority to 1000, which means that all tabs with a priority lower than 1000 appear before this tab and all with a priority higher than 1000 appear after this tab
      templateUrl: ":::PLUGIN_PATH:::/views/deviceContact.html", //  ... display our html file "deviceContact.html" inside the "views" folder of our plugin (the plugin's folder is represented using the magic string ```:::PLUGIN_PATH:::```, which is replaced by the actual path during the build process)
      controller: "deviceContactCtrl" // ... use "deviceContactCtrl" as controller
    });
  }
})();

注記: ルート (この場合”/device/:deviceId”)に複数のビューを添付する場合、タブは自動的に作成されます。デバイス詳細一覧ではすでに/device/:deviceId を使用するので「連絡先」はタブとして追加表示されます。

次に、コントローラーとビューを定義する必要があります。コントローラーについては、“deviceContact.controller.js"という新規ファイルをプラグインフォルダ内に作成し、以下の内容を追記してください:

(function() {
  "use strict";

  angular
    .module("myapp.deviceContact")
    .controller("deviceContactCtrl", DeviceContactController);

  function DeviceContactController() {}
})();

プラグインフォルダ内に"views"という新規フォルダを作成し、以下を追記した"deviceContact.html"というファイルを追加作成してください:

<div class="panel panel-clean">
  <div class="panel-body">
    Contact
  </div>
</div>

モジュール、設定、コントローラをプラグインに追加した今、“myapp.deviceContact"を自身のモジュールとして特定する必要があり、それぞれの java スクリプトファイルをプラグインマニフェストへ追加する必要があります:

{
  "name": "Device Details - Contact",
  "description": "Plugin adds a Contact tab to Device Details view",
  "category": "Examples",
  "ngModules": ["myapp.deviceContact"],
  "js": [
    "deviceContact.module.js",
    "deviceContact.config.js",
    "deviceContact.controller.js"
  ]
}

この時点で展開をすることにより、アプリケーションの検証 が可能です。デバイスを選択する際、「連絡先」タブが表示されるはずです。

連絡先タブ上にデータを表示

先ほどの段階では、「連絡先」タブにはダミーのビューが設定されています。ここでは実際の連絡先情報をデバイスのビューに表示します。まずは連絡先データーがデバイスのインベントリにある"c8y_Contact"というフラグメントに保存されていることを以下のように定義します:

{
  "c8y_Contact": {
    "name": "John Smith",
    "email": "john.smith@example.com",
    "phone": "123-456-789",
    "address": "Sample Street 11 A"
  }
}

“deviceContact.controller.js"にロード機能を追加し、 以下のように必要な従属物を挿入してください。この機能は表示されたデバイス($routeParams.deviceId)の詳細を取得し、ローカルスコープにデバイス ID と"c8y_Contact"フラグメントを追加します。

(function() {
  "use strict";

  angular
    .module("myapp.deviceContact")
    .controller("deviceContactCtrl", DeviceContactController);

  DeviceContactController.$inject = ["$scope", "$routeParams", "c8yDevices"];

  function DeviceContactController($scope, $routeParams, c8yDevices) {
    function load() {
      c8yDevices.detail($routeParams.deviceId).then(function(res) {
        var device = res.data;
        $scope.device.id = device.id;
        $scope.device.c8y_Contact = device.c8y_Contact;
      });
    }

    $scope.device = {};

    load();
  }
})();

deviceContact.html にあるデバイス連絡先のビューを下記のように編集してください:

<div class="panel panel-clean">
  <div class="panel-body">
    <form name="contactForm">
      <div class="form-group">
        <label for="contact_name">Name</label>
        <input
          id="contact_name"
          type="text"
          class="form-control"
          ng-model="device.c8y_Contact.name"
        />
      </div>
      <div class="form-group">
        <label for="contact_email">E-mail address</label>
        <input
          id="contact_email"
          type="text"
          class="form-control"
          ng-model="device.c8y_Contact.email"
        />
      </div>
      <div class="form-group">
        <label for="contact_phone">Phone</label>
        <input
          id="contact_phone"
          type="text"
          class="form-control"
          ng-model="device.c8y_Contact.phone"
        />
      </div>
      <div class="form-group">
        <label for="contact_address">Address</label>
        <input
          id="contact_address"
          type="text"
          class="form-control"
          ng-model="device.c8y_Contact.address"
        />
      </div>
    </form>
  </div>
</div>

ユーザーにデータ保存の許可を与える

以下の手順を踏めば、新規連絡先フォームに入力されたデータを保存することが可能になります。

データを保存できるよう、“deviceContact.controller.js"内のコントローラーに追加の従属物と以下の内容をロード関数の閉じ括弧の後に追加し、更新してください。 “c8yDevices.save” とは Things Cloud REST API を使用しデバイスを保存するライブラリ機能 です。“c8yAlert.success” とはユーザーインターフェース上部に緑の確認ボックスを表示するライブラリ機能です。

(function() {
  "use strict";

  angular
    .module("myapp.deviceContact")
    .controller("deviceContactCtrl", DeviceContactController);

  DeviceContactController.$inject = [
    "$scope",
    "$routeParams",
    "c8yDevices",
    "c8yAlert"
  ];

  function DeviceContactController($scope, $routeParams, c8yDevices, c8yAlert) {
    function load() {
      c8yDevices.detail($routeParams.deviceId).then(function(res) {
        var device = res.data;
        $scope.device.id = device.id;
        $scope.device.c8y_Contact = device.c8y_Contact;
      });
    }

    function save(device) {
      c8yDevices.save(device).then(onSave);
    }

    function onSave() {
      c8yAlert.success("Contact information successfully saved!");
    }

    $scope.save = save;
    $scope.device = {};

    load();
  }
})();

「保存の変更」 ボタンをデバイス連絡先のビューに追加しましょう。deviceContact.html の form タグを閉じる前に以下の div を貼り付けてください。このボタンは先ほど定義したsave機能を作動させます。

<div>
  <a
    href=""
    class="btn btn-primary"
    ng-click="save(device)"
    ng-disabled="contactForm.$invalid"
    >Save changes</a
  >
</div>

これでプラグインの完成です!プラグインを展開し、ウェブブラウザ上で独自アプリケーションを開きデバイスをクリックして「連絡先」タブを確認しましょう。

JSDoc

JavaScript ライブラリに関する JSDoc は、Cumulocity 社リソースサイト に収録されています。