Thingsboard二次開發(fā)系列拷貝篇-10widget(03創(chuàng)建簡單的小部件)

素材和版本

tb-release:3.2.2

小部件(Widget類型)

最新值小部件

Widgets Bundle視圖中,單擊屏幕右下角的大“+”按鈕,然后單擊“創(chuàng)建新的小部件類型”按鈕。單擊選擇小部件類型彈出窗口上的最新值按鈕。該控件編輯器將打開,預(yù)先填充默認(rèn)的內(nèi)容最新值小部件模板。

  • 清除“資源”部分 CSS 選項卡的內(nèi)容。

  • 將以下 HTML 代碼放在“資源”部分的 HTML 選項卡中:

??<div?fxFlex?fxLayout="column"?style="height:?100%;"?fxLayoutAlign="center?stretch">
????<div>My?first?latest?values?widget.</div>
????<div?fxFlex?fxLayout="row"?*ngFor="let?dataKeyData?of?data"?fxLayoutAlign="space-around?center">
????????<div>{{dataKeyData.dataKey.label}}:</div>
????????<div>{{(dataKeyData.data[0]?&&?dataKeyData.data[0][0])?|?date?:?'yyyy-MM-dd?HH:mm:ss'?}}</div>
????????<div>{{dataKeyData.data[0]?&&?dataKeyData.data[0][1]}}</div>
????</div>
??</div>
  • 將以下 JavaScript 代碼放入“JavaScript”部分:

????self.onInit?=?function()?{
???????self.ctx.$scope.data?=?self.ctx.defaultSubscription.data;
????}
????????
????self.onDataUpdated?=?function()?{
????????self.ctx.detectChanges();
????}
  • 單擊小部件編輯器工具欄上的運(yùn)行按鈕,以便在小部件預(yù)覽部分查看結(jié)果。

在這個例子中,訂閱的數(shù)據(jù)屬性被分配給$scope并且可以在 HTML 模板中訪問。在 HTML 中,使用特殊的*ngFor結(jié)構(gòu)角度指令來迭代可用的數(shù)據(jù)鍵和數(shù)據(jù)點,然后使用相應(yīng)的時間戳呈現(xiàn)最新值。

時間序列小部件

Widgets Bundle視圖中,單擊屏幕右下角的大“+”按鈕,然后單擊“創(chuàng)建新的小部件類型”按鈕。單擊選擇小部件類型彈出窗口上的時間序列按鈕。該控件編輯器將打開,預(yù)先填充默認(rèn)時間序列模板控件的內(nèi)容。

  • 將“資源”部分中 CSS 選項卡的內(nèi)容替換為以下內(nèi)容:

.my-data-table?th?{
????text-align:?left;}
  • 將以下 HTML 代碼放在“資源”部分的 HTML 選項卡中:

??<mat-tab-group?style="height:?100%;">
??????<mat-tab?*ngFor="let?datasource?of?datasources;?let?$dsIndex?=?index"?label="{{datasource.name}}">
??????????<table?class="my-data-table"?style="width:?100%;">
??????????????<thead>
??????????????????<tr>
??????????????????????<th>Timestamp</th>
??????????????????????<th?*ngFor="let?dataKeyData?of?datasourceData[$dsIndex]">{{dataKeyData.dataKey.label}}</th>
??????????????????<tr>??????????
??????????????</thead>
??????????????<tbody>
??????????????????<tr?*ngFor="let?data?of?datasourceData[$dsIndex][0].data;?let?$dataIndex?=?index">
??????????????????????<td>{{data[0]?|?date?:?'yyyy-MM-dd?HH:mm:ss'}}</td>
??????????????????????<td?*ngFor="let?dataKeyData?of?datasourceData[$dsIndex]">{{dataKeyData.data[$dataIndex]?&&?dataKeyData.data[$dataIndex][1]}}</td>
??????????????????</tr>??????
??????????????</tbody>??????????
??????????</table>??????????
??????</mat-tab>???????
??</mat-tab-group>
  • 將以下 JavaScript 代碼放入“JavaScript”部分:

self.onInit?=?function()?{
????self.ctx.widgetTitle?=?'My?first?Time-Series?widget';
????self.ctx.$scope.datasources?=?self.ctx.defaultSubscription.datasources;
????self.ctx.$scope.data?=?self.ctx.defaultSubscription.data;
????
????self.ctx.$scope.datasourceData?=?[];
????
????var?currentDatasource?=?null;
????var?currentDatasourceIndex?=?-1;
????
????for?(var?i=0;i<self.ctx.$scope.data.length;i++)?{
????????var?dataKeyData?=?self.ctx.$scope.data[i];
????????if?(dataKeyData.datasource?!=?currentDatasource)?{
????????????currentDatasource?=?dataKeyData.datasource
????????????currentDatasourceIndex++;
????????????self.ctx.$scope.datasourceData[currentDatasourceIndex]?=?[];
????????????
????????}?
????????self.ctx.$scope.datasourceData[currentDatasourceIndex].push(dataKeyData);
????}
????self.ctx.updateWidgetParams();}self.onDataUpdated?=?function()?{
????self.ctx.detectChanges();}
  • 單擊小部件編輯器工具欄上的運(yùn)行按鈕,以便在小部件預(yù)覽部分查看結(jié)果。

在此示例中,訂閱?數(shù)據(jù)源和數(shù)據(jù)屬性被分配給$scope并在 HTML 模板中變得可訪問。在$ scope.datasourceData屬性介紹了數(shù)據(jù)源指數(shù)為HTML模板中靈活地訪問數(shù)據(jù)源的具體dataKeys數(shù)據(jù)映射。在 HTML 中,使用特殊的*ngFor結(jié)構(gòu)角度指令來迭代可用數(shù)據(jù)源并呈現(xiàn)相應(yīng)的選項卡。在每個選項卡內(nèi),使用從數(shù)據(jù)源索引訪問的 datasourceData 范圍屬性獲取的dataKeys 呈現(xiàn)表。每個表通過遍歷所有dataKeyData 來呈現(xiàn)列對象并通過迭代每個dataKeyData 的數(shù)據(jù)數(shù)組來呈現(xiàn)所有可用的數(shù)據(jù)點以呈現(xiàn)時間戳和值。請注意,在這段代碼,onDataUpdated功能與一個呼叫實現(xiàn)detectChanges功能,必要時接收到新的數(shù)據(jù)來執(zhí)行新的變更檢測周期。

RPC(控制)小部件

Widgets Bundle視圖中,單擊屏幕右下角的大“+”按鈕,然后單擊“創(chuàng)建新的小部件類型”按鈕。單擊“選擇小部件類型”彈出窗口上的“控制小部件”按鈕。該控件編輯器將打開,預(yù)先填充的默認(rèn)控件模板控件的內(nèi)容。

  • 清除“資源”部分 CSS 選項卡的內(nèi)容。

  • 將以下 HTML 代碼放在“資源”部分的 HTML 選項卡中:

<form?#rpcForm="ngForm"?(submit)="sendCommand()">
??????<div?class="mat-content?mat-padding"?fxLayout="column">
????????<mat-form-field?class="mat-block">
??????????<mat-label>RPC?method</mat-label>
??????????<input?matInput?required?name="rpcMethod"?#rpcMethodField="ngModel"?[(ngModel)]="rpcMethod"/>
??????????<mat-error?*ngIf="rpcMethodField.hasError('required')">
????????????RPC?method?name?is?required.??????????</mat-error>
????????</mat-form-field>
????????<mat-form-field?class="mat-block">
??????????<mat-label>RPC?params</mat-label>
??????????<input?matInput?required?name="rpcParams"?#rpcParamsField="ngModel"?[(ngModel)]="rpcParams"/>
??????????<mat-error?*ngIf="rpcParamsField.hasError('required')">
????????????RPC?params?is?required.??????????</mat-error>
????????</mat-form-field>
????????<button?[disabled]="rpcForm.invalid?||?!rpcForm.dirty"?mat-raised-button?color="primary"?type="submit"?>
??????????Send?RPC?command????????</button>
????????<div>
??????????<label>RPC?command?response</label>
??????????<div?style="width:?100%;?height:?100px;?border:?solid?2px?gray"?[innerHTML]="rpcCommandResponse">
??????????</div>
????????</div>
??????</div>
????</form>
  • 將以下 JSON 內(nèi)容放在設(shè)置架構(gòu)部分的“設(shè)置架構(gòu)”選項卡中:

{
????"schema":?{
????????"type":?"object",
????????"title":?"Settings",
????????"properties":?{
????????????"oneWayElseTwoWay":?{
????????????????"title":?"Is?One?Way?Command",
????????????????"type":?"boolean",
????????????????"default":?true
????????????},
????????????"requestTimeout":?{
????????????????"title":?"RPC?request?timeout",
????????????????"type":?"number",
????????????????"default":?500
????????????}
????????},
????????"required":?[]
????},
????"form":?[
????????"oneWayElseTwoWay",
????????"requestTimeout"
????]}
  • 將以下 JavaScript 代碼放入“JavaScript”部分:

self.onInit?=?function()?{
????
????self.ctx.$scope.sendCommand?=?function()?{
????????var?rpcMethod?=?self.ctx.$scope.rpcMethod;
????????var?rpcParams?=?self.ctx.$scope.rpcParams;
????????var?timeout?=?self.ctx.settings.requestTimeout;
????????var?oneWayElseTwoWay?=?self.ctx.settings.oneWayElseTwoWay???true?:?false;

????????var?commandObservable;
????????if?(oneWayElseTwoWay)?{
????????????commandObservable?=?self.ctx.controlApi.sendOneWayCommand(rpcMethod,?rpcParams,?timeout);
????????}?else?{
????????????commandObservable?=?self.ctx.controlApi.sendTwoWayCommand(rpcMethod,?rpcParams,?timeout);
????????}
????????commandObservable.subscribe(
????????????function?(response)?{
????????????????if?(oneWayElseTwoWay)?{
????????????????????self.ctx.$scope.rpcCommandResponse?=?"Command?was?successfully?received?by?device.<br>?No?response?body?because?of?one?way?command?mode.";
????????????????}?else?{
????????????????????self.ctx.$scope.rpcCommandResponse?=?"Response?from?device:<br>";????????????????????
????????????????????self.ctx.$scope.rpcCommandResponse?+=?JSON.stringify(response,?undefined,?2);
????????????????}
????????????????self.ctx.detectChanges();
????????????},
????????????function?(rejection)?{
????????????????self.ctx.$scope.rpcCommandResponse?=?"Failed?to?send?command?to?the?device:<br>"
????????????????self.ctx.$scope.rpcCommandResponse?+=?"Status:?"?+?rejection.status?+?"<br>";
????????????????self.ctx.$scope.rpcCommandResponse?+=?"Status?text:?'"?+?rejection.statusText?+?"'";
????????????????self.ctx.detectChanges();
????????????}
????????????
????????);
????}
????}
  • 使用小部件類型名稱填充小部件標(biāo)題字段,例如?!拔业牡谝粋€控件小部件”。

  • 單擊小部件編輯器工具欄上的運(yùn)行按鈕,以便在小部件預(yù)覽部分查看結(jié)果。

  • 單擊預(yù)覽部分上的儀表板編輯按鈕以更改生成的小部件的大小。然后單擊儀表板應(yīng)用按鈕。最終的小部件應(yīng)如下圖所示。

  • 單擊小部件編輯器工具欄上的保存按鈕以保存小部件類型。

要測試此小部件如何執(zhí)行 RPC 命令,我們需要將其放置在儀表板中,然后將其綁定到使用 RPC 命令的設(shè)備。為此,請執(zhí)行以下步驟:

以租戶管理員身份登錄。

導(dǎo)航到設(shè)備并使用某個名稱創(chuàng)建新設(shè)備,例如?!拔业?RPC 設(shè)備”。

打開設(shè)備詳細(xì)信息,然后單擊“復(fù)制訪問令牌”按鈕將設(shè)備訪問令牌復(fù)制到剪貼板。

下載mqtt-js-rpc-from-server.sh和mqtt-js-rpc-from-server.js。將這些文件放在一個文件夾中。編輯mqtt-js-rpc-from-server.sh?- 用剪貼板中的設(shè)備訪問令牌替換$ACCESS_TOKEN。并安裝 mqtt 客戶端庫。

運(yùn)行mqtt-js-rpc-from-server.sh腳本。您應(yīng)該會在控制臺中看到“已連接”消息。

導(dǎo)航到儀表板并創(chuàng)建一個具有某些名稱的新儀表板,例如?!拔业牡谝粋€控制儀表板”。打開此儀表板。

單擊儀表板“編輯”按鈕。在儀表板編輯模式下,單擊儀表板工具欄上的“實體別名”按鈕。

  • 實體別名彈出窗口中單擊“添加別名”。

  • 填寫“別名”字段,例如?!拔业?RPC 設(shè)備別名”。

  • 在“過濾器類型”字段中選擇“實體列表”。

  • 在“類型”字段中選擇“設(shè)備”。

  • 在“實體列表”字段中選擇您的設(shè)備。在此示例中為“我的 RPC 設(shè)備”。

  • 實體別名中單擊“添加”,然后單擊“保存”?。

  • 單擊儀表板“+”按鈕,然后單擊“創(chuàng)建新小部件”按鈕。

  • 然后選擇保存 RPC 小部件的小部件包。選擇“控制小部件”選項卡。

  • 單擊您的小部件。在這個例子中,“我的第一個控件小部件”。

  • 從“添加小部件”彈出窗口中,在“目標(biāo)設(shè)備”部分中選擇您的設(shè)備別名。在此示例中為“我的 RPC 設(shè)備別名”。

  • 單擊添加。您的控制小部件將出現(xiàn)在儀表板中。單擊儀表板應(yīng)用更改按鈕以保存儀表板并退出編輯模式。

  • 使用 RPC 方法名稱填充RPC 方法字段。例如。“測試方法”。

  • 用 RPC params填充RPC params字段。例如。“{ param1: “value1” }”。

  • 單擊發(fā)送 RPC 命令按鈕。您應(yīng)該會在小部件中看到以下響應(yīng)。

應(yīng)在設(shè)備控制臺中打印以下輸出:

1
2??request.topic:?v1/devices/me/rpc/request/0
??request.body:?{"method":"TestMethod","params":"{?param1:?\"value1\"?}"}

為了測試“雙向”RPC 命令模式,我們需要更改相應(yīng)的小部件設(shè)置屬性。為此,請執(zhí)行以下步驟:

  • 單擊儀表板“編輯”按鈕。在儀表板編輯模式下,單擊位于控件小部件標(biāo)題中的編輯小部件按鈕。

  • 在小部件詳細(xì)信息中,查看選擇“高級”選項卡并取消選中“單向命令”復(fù)選框。

  • 單擊小部件詳細(xì)信息標(biāo)題上的應(yīng)用更改按鈕。關(guān)閉詳細(xì)信息并單擊儀表板應(yīng)用更改按鈕。

  • 像前面的步驟一樣,用 RPC 方法名稱和參數(shù)填充小部件字段。單擊發(fā)送 RPC 命令按鈕。您應(yīng)該會在小部件中看到以下響應(yīng)。

  • 停止mqtt-js-rpc-from-server.sh腳本。單擊發(fā)送 RPC 命令按鈕。您應(yīng)該會在小部件中看到以下響應(yīng)。

在本例中,controlApi用于發(fā)送 RPC 命令。此外,還引入了自定義小部件設(shè)置以配置 RPC 命令模式和 RPC 請求超時。來自設(shè)備的響應(yīng)由commandObservable處理。它具有成功和失敗的回調(diào)以及相應(yīng)的響應(yīng),或包含有關(guān)請求執(zhí)行結(jié)果信息的拒絕對象。

鬧鐘小工具

在Widgets Bundle視圖中,單擊屏幕右下角的大“+”按鈕,然后單擊“創(chuàng)建新的小部件類型”按鈕。單擊“選擇小部件類型”彈出窗口上的“警報小部件”按鈕。該控件編輯器將被打開,預(yù)填充了默認(rèn)的內(nèi)容報警模板窗口小部件。

將“資源”部分中 CSS 選項卡的內(nèi)容替換為以下內(nèi)容:

1
2
3.my-alarm-table th {
? ?text-align: left;}

將以下 HTML 代碼放在“資源”部分的 HTML 選項卡中:

? <div fxFlex fxLayout="column" style="height: 100%;">
? ? ?<div>My first Alarm widget.</div>
? ? ?<table class="my-alarm-table" style="width: 100%;">
? ? ? ? ?<thead>
? ? ? ? ? ? ?<tr>
? ? ? ? ? ? ? ? ?<th *ngFor="let dataKey of alarmSource?.dataKeys">{{dataKey.label}}</th>
? ? ? ? ? ? ?<tr> ? ? ? ? ?
? ? ? ? ?</thead>
? ? ? ? ?<tbody>
? ? ? ? ? ? ?<tr *ngFor="let alarm of alarms">
? ? ? ? ? ? ? ? ?<td *ngFor="let dataKey of alarmSource?.dataKeys"
? ? ? ? ? ? ? ? ? ? ?[ngStyle]="getAlarmCellStyle(alarm, dataKey)">
? ? ? ? ? ? ? ? ? ? ?{{getAlarmValue(alarm, dataKey)}} ? ? ? ? ? ? ? ? ?</td>
? ? ? ? ? ? ?</tr> ? ? ?
? ? ? ? ?</tbody> ? ? ? ? ?
? ? ?</table> ? ? ? ? ?
?</div>

將以下 JSON 內(nèi)容放在設(shè)置架構(gòu)部分的“設(shè)置架構(gòu)”選項卡中:

{
? ?"schema": {
? ? ? ?"type": "object",
? ? ? ?"title": "AlarmTableSettings",
? ? ? ?"properties": {
? ? ? ? ? ?"alarmSeverityColorFunction": {
? ? ? ? ? ? ? ?"title": "Alarm severity color function: f(severity)",
? ? ? ? ? ? ? ?"type": "string",
? ? ? ? ? ? ? ?"default": "if(severity == 'CRITICAL') {return 'red';} else if (severity == 'MAJOR') {return 'orange';} else return 'green'; "
? ? ? ? ? ?}
? ? ? ?},
? ? ? ?"required": []
? ?},
? ?"form": [
? ? ? ?{
? ? ? ? ? ?"key": "alarmSeverityColorFunction",
? ? ? ? ? ?"type": "javascript"
? ? ? ?}
? ?]}

將以下 JavaScript 代碼放入“JavaScript”部分:

self.onInit = function() {
? ?var pageLink = self.ctx.pageLink();

? ?pageLink.typeList = self.ctx.widgetConfig.alarmTypeList;
? ?pageLink.statusList = self.ctx.widgetConfig.alarmStatusList;
? ?pageLink.severityList = self.ctx.widgetConfig.alarmSeverityList;
? ?pageLink.searchPropagatedAlarms = self.ctx.widgetConfig.searchPropagatedAlarms;

? ?self.ctx.defaultSubscription.subscribeForAlarms(pageLink, null);
? ?self.ctx.$scope.alarmSource = self.ctx.defaultSubscription.alarmSource;
? ?
? ?var alarmSeverityColorFunctionBody = self.ctx.settings.alarmSeverityColorFunction;
? ?if (typeof alarmSeverityColorFunctionBody === 'undefined' || !alarmSeverityColorFunctionBody.length) {
? ? ? ?alarmSeverityColorFunctionBody = "if(severity == 'CRITICAL') {return 'red';} else if (severity == 'MAJOR') {return 'orange';} else return 'green';";
? ?}
? ?
? ?var alarmSeverityColorFunction = null;
? ?try {
? ? ? ?alarmSeverityColorFunction = new Function('severity', alarmSeverityColorFunctionBody);
? ?} catch (e) {
? ? ? ?alarmSeverityColorFunction = null;
? ?}

? ?self.ctx.$scope.getAlarmValue = function(alarm, dataKey) {
? ? ? ?var alarmKey = dataKey.name;
? ? ? ?if (alarmKey === 'originator') {
? ? ? ? ? ?alarmKey = 'originatorName';
? ? ? ?}
? ? ? ?var value = alarm[alarmKey];
? ? ? ?if (alarmKey === 'createdTime') {
? ? ? ? ? ?return self.ctx.date.transform(value, 'yyyy-MM-dd HH:mm:ss');
? ? ? ?} else {
? ? ? ? ? ?return value;
? ? ? ?}
? ?}
? ?
? ?self.ctx.$scope.getAlarmCellStyle = function(alarm, dataKey) {
? ? ? ?var alarmKey = dataKey.name;
? ? ? ?if (alarmKey === 'severity' && alarmSeverityColorFunction) {
? ? ? ? ? ?var severity = alarm[alarmKey];
? ? ? ? ? ?var color = alarmSeverityColorFunction(severity);
? ? ? ? ? ?return {
? ? ? ? ? ? ? ?color: color ?
? ? ? ? ? ?};
? ? ? ?}
? ? ? ?return {};
? ?}}self.onDataUpdated = function() {
? ?self.ctx.$scope.alarms = self.ctx.defaultSubscription.alarms.data;
? ?self.ctx.detectChanges();}

單擊小部件編輯器工具欄上的運(yùn)行按鈕,以便在小部件預(yù)覽部分查看結(jié)果。

在這個例子中,訂閱的alarmSource和alarms屬性被分配給$scope并且可以在 HTML 模板中訪問。在 HTML 中,使用了一個特殊的*ngFor結(jié)構(gòu)角度指令,以便迭代alarmSource的可用警報dataKeys并呈現(xiàn)相應(yīng)的列。表格行通過迭代警報數(shù)組呈現(xiàn),相應(yīng)的單元格通過迭代dataKeys呈現(xiàn)。該功能getAlarmValue被取報警值和格式化createdTime使用報警屬性DatePipe可通過ctx 的date屬性訪問角管。函數(shù)getAlarmCellStyle用于為每個警報單元格分配自定義單元格樣式。在此示例中,我們引入了名為alarmSeverityColorFunction 的新設(shè)置屬性,其中包含根據(jù)警報嚴(yán)重性返回顏色的函數(shù)體。在getAlarmCellStyle函數(shù)中,有相應(yīng)的調(diào)用帶有嚴(yán)重性值的alarmSeverityColorFunction以獲取警報嚴(yán)重性單元格的顏色。請注意,在此代碼中實現(xiàn)了onDataUpdated函數(shù),以便使用來自訂閱的最新警報更新警報屬性并使用調(diào)用更改檢測檢測更改()函數(shù)。


?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容