Configure UI help assets base url.
@ -96,7 +96,7 @@ public class DashboardController extends BaseController {
|
||||
public static final String DASHBOARD_DEFINITION = "The Dashboard object is a heavyweight object that contains information about the dashboard (e.g. title, image, assigned customers) and also configuration JSON (e.g. layouts, widgets, entity aliases).";
|
||||
public static final String HIDDEN_FOR_MOBILE = "Exclude dashboards that are hidden for mobile";
|
||||
|
||||
@Value("${dashboard.max_datapoints_limit}")
|
||||
@Value("${ui.dashboard.max_datapoints_limit}")
|
||||
private long maxDatapointsLimit;
|
||||
|
||||
@ApiOperation(value = "Get server time (getServerTime)",
|
||||
|
||||
@ -0,0 +1,46 @@
|
||||
/**
|
||||
* Copyright © 2016-2021 The Thingsboard Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.thingsboard.server.controller;
|
||||
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMethod;
|
||||
import org.springframework.web.bind.annotation.ResponseBody;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.thingsboard.server.common.data.exception.ThingsboardException;
|
||||
import org.thingsboard.server.queue.util.TbCoreComponent;
|
||||
|
||||
@RestController
|
||||
@TbCoreComponent
|
||||
@RequestMapping("/api")
|
||||
public class UiSettingsController extends BaseController {
|
||||
|
||||
@Value("${ui.help.base-url}")
|
||||
private String helpBaseUrl;
|
||||
|
||||
@ApiOperation(value = "Get UI help base url (getHelpBaseUrl)",
|
||||
notes = "Get UI help base url used to fetch help assets. " +
|
||||
"The actual value of the base url is configurable in the system configuration file.")
|
||||
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
|
||||
@RequestMapping(value = "/uiSettings/helpBaseUrl", method = RequestMethod.GET)
|
||||
@ResponseBody
|
||||
public String getHelpBaseUrl() throws ThingsboardException {
|
||||
return helpBaseUrl;
|
||||
}
|
||||
|
||||
}
|
||||
@ -129,10 +129,16 @@ usage:
|
||||
check:
|
||||
cycle: "${USAGE_STATS_CHECK_CYCLE:60000}"
|
||||
|
||||
# Dashboard parameters
|
||||
dashboard:
|
||||
# UI parameters
|
||||
ui:
|
||||
# Dashboard parameters
|
||||
dashboard:
|
||||
# Maximum allowed datapoints fetched by widgets
|
||||
max_datapoints_limit: "${DASHBOARD_MAX_DATAPOINTS_LIMIT:50000}"
|
||||
# Help parameters
|
||||
help:
|
||||
# Base url for UI help assets
|
||||
base-url: "${UI_HELP_BASE_URL:https://raw.githubusercontent.com/thingsboard/thingsboard/master/ui-ngx/src/assets}"
|
||||
|
||||
database:
|
||||
ts_max_intervals: "${DATABASE_TS_MAX_INTERVALS:700}" # Max number of DB queries generated by single API call to fetch telemetry records
|
||||
|
||||
@ -70,13 +70,12 @@ frontend http-in
|
||||
acl transport_http_acl path_beg /api/v1/
|
||||
acl letsencrypt_http_acl path_beg /.well-known/acme-challenge/
|
||||
acl tb_api_acl path_beg /api/ /swagger /webjars /v2/ /static/rulenode/ /oauth2/ /login/oauth2/ /static/widgets/
|
||||
acl tb_rulenode_assets path_reg ^/assets/help/.*/rulenode/.*$
|
||||
|
||||
redirect scheme https if !letsencrypt_http_acl !transport_http_acl { env(FORCE_HTTPS_REDIRECT) -m str true }
|
||||
|
||||
use_backend letsencrypt_http if letsencrypt_http_acl
|
||||
use_backend tb-http-backend if transport_http_acl
|
||||
use_backend tb-api-backend if tb_api_acl or tb_rulenode_assets
|
||||
use_backend tb-api-backend if tb_api_acl
|
||||
|
||||
default_backend tb-web-backend
|
||||
|
||||
@ -89,10 +88,9 @@ frontend https_in
|
||||
|
||||
acl transport_http_acl path_beg /api/v1/
|
||||
acl tb_api_acl path_beg /api/ /swagger /webjars /v2/ /static/rulenode/ /oauth2/ /login/oauth2/ /static/widgets/
|
||||
acl tb_rulenode_assets path_reg ^/assets/help/.*/rulenode/.*$
|
||||
|
||||
use_backend tb-http-backend if transport_http_acl
|
||||
use_backend tb-api-backend if tb_api_acl or tb_rulenode_assets
|
||||
use_backend tb-api-backend if tb_api_acl
|
||||
|
||||
default_backend tb-web-backend
|
||||
|
||||
|
||||
@ -26,10 +26,6 @@ const PROXY_CONFIG = {
|
||||
"target": ruleNodeUiforwardUrl,
|
||||
"secure": false,
|
||||
},
|
||||
"/assets/help/*/rulenode/**": {
|
||||
"target": ruleNodeUiforwardUrl,
|
||||
"secure": false,
|
||||
},
|
||||
"/static/widgets": {
|
||||
"target": forwardUrl,
|
||||
"secure": false,
|
||||
|
||||
43
ui-ngx/src/app/core/http/ui-settings.service.ts
Normal file
@ -0,0 +1,43 @@
|
||||
///
|
||||
/// Copyright © 2016-2021 The Thingsboard Authors
|
||||
///
|
||||
/// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
/// you may not use this file except in compliance with the License.
|
||||
/// You may obtain a copy of the License at
|
||||
///
|
||||
/// http://www.apache.org/licenses/LICENSE-2.0
|
||||
///
|
||||
/// Unless required by applicable law or agreed to in writing, software
|
||||
/// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
/// See the License for the specific language governing permissions and
|
||||
/// limitations under the License.
|
||||
///
|
||||
|
||||
import { Injectable } from '@angular/core';
|
||||
import { HttpClient } from '@angular/common/http';
|
||||
import { defaultHttpOptions, defaultHttpOptionsFromConfig, RequestConfig } from '@core/http/http-utils';
|
||||
import { Observable } from 'rxjs';
|
||||
import { publishReplay, refCount } from 'rxjs/operators';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class UiSettingsService {
|
||||
|
||||
private helpBaseUrlObservable: Observable<string>;
|
||||
|
||||
constructor(
|
||||
private http: HttpClient
|
||||
) { }
|
||||
|
||||
public getHelpBaseUrl(): Observable<string> {
|
||||
if (!this.helpBaseUrlObservable) {
|
||||
this.helpBaseUrlObservable = this.http.get('/api/uiSettings/helpBaseUrl', {responseType: 'text', ...defaultHttpOptions(true)}).pipe(
|
||||
publishReplay(1),
|
||||
refCount()
|
||||
);
|
||||
}
|
||||
return this.helpBaseUrlObservable;
|
||||
}
|
||||
}
|
||||
@ -18,23 +18,29 @@ import { Injectable } from '@angular/core';
|
||||
import { HttpClient } from '@angular/common/http';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
import { Observable, of } from 'rxjs';
|
||||
import { catchError, mergeMap, tap } from 'rxjs/operators';
|
||||
import { helpBaseUrl } from '@shared/models/constants';
|
||||
import { catchError, map, mergeMap, tap } from 'rxjs/operators';
|
||||
import { helpBaseUrl as siteBaseUrl } from '@shared/models/constants';
|
||||
import { UiSettingsService } from '@core/http/ui-settings.service';
|
||||
|
||||
const NOT_FOUND_CONTENT = '## Not found';
|
||||
const localHelpBaseUrl = '/assets';
|
||||
|
||||
const NOT_FOUND_CONTENT: HelpData = {
|
||||
content: '## Not found',
|
||||
helpBaseUrl: localHelpBaseUrl
|
||||
};
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class HelpService {
|
||||
|
||||
private helpBaseUrl = helpBaseUrl;
|
||||
|
||||
private siteBaseUrl = siteBaseUrl;
|
||||
private helpCache: {[lang: string]: {[key: string]: string}} = {};
|
||||
|
||||
constructor(
|
||||
private translate: TranslateService,
|
||||
private http: HttpClient
|
||||
private http: HttpClient,
|
||||
private uiSettingsService: UiSettingsService
|
||||
) {}
|
||||
|
||||
getHelpContent(key: string): Observable<string> {
|
||||
@ -70,13 +76,38 @@ export class HelpService {
|
||||
}
|
||||
}
|
||||
|
||||
private loadHelpContent(lang: string, key: string): Observable<string> {
|
||||
return this.http.get(`/assets/help/${lang}/${key}.md`, {responseType: 'text'} );
|
||||
private loadHelpContent(lang: string, key: string): Observable<HelpData> {
|
||||
return this.uiSettingsService.getHelpBaseUrl().pipe(
|
||||
mergeMap((helpBaseUrl) => {
|
||||
return this.loadHelpContentFromBaseUrl(helpBaseUrl, lang, key).pipe(
|
||||
catchError((e) => {
|
||||
if (localHelpBaseUrl !== helpBaseUrl) {
|
||||
return this.loadHelpContentFromBaseUrl(localHelpBaseUrl, lang, key);
|
||||
} else {
|
||||
throw e;
|
||||
}
|
||||
})
|
||||
);
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
private processVariables(content: string): string {
|
||||
const baseUrlReg = /\${baseUrl}/g;
|
||||
return content.replace(baseUrlReg, this.helpBaseUrl);
|
||||
private loadHelpContentFromBaseUrl(helpBaseUrl: string, lang: string, key: string): Observable<HelpData> {
|
||||
return this.http.get(`${helpBaseUrl}/help/${lang}/${key}.md`, {responseType: 'text'} ).pipe(
|
||||
map((content) => {
|
||||
return {
|
||||
content,
|
||||
helpBaseUrl
|
||||
};
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
private processVariables(helpData: HelpData): string {
|
||||
const baseUrlReg = /\${siteBaseUrl}/g;
|
||||
helpData.content = helpData.content.replace(baseUrlReg, this.siteBaseUrl);
|
||||
const helpBaseUrlReg = /\${helpBaseUrl}/g;
|
||||
return helpData.content.replace(helpBaseUrlReg, helpData.helpBaseUrl);
|
||||
}
|
||||
|
||||
private processIncludes(content: string): Observable<string> {
|
||||
@ -96,3 +127,8 @@ export class HelpService {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
interface HelpData {
|
||||
content: string;
|
||||
helpBaseUrl: string;
|
||||
}
|
||||
|
||||
@ -58,11 +58,11 @@ return details;
|
||||
|
||||
<br>
|
||||
|
||||
More details about Alarms can be found in [this tutorial{:target="_blank"}](${baseUrl}/docs/user-guide/alarms/).
|
||||
More details about Alarms can be found in [this tutorial{:target="_blank"}](${siteBaseUrl}/docs/user-guide/alarms/).
|
||||
|
||||
You can see the real life example, where this node is used, in the next tutorial:
|
||||
|
||||
- [Create and Clear Alarms{:target="_blank"}](${baseUrl}/docs/user-guide/rule-engine-2-0/tutorials/create-clear-alarms/)
|
||||
- [Create and Clear Alarms{:target="_blank"}](${siteBaseUrl}/docs/user-guide/rule-engine-2-0/tutorials/create-clear-alarms/)
|
||||
|
||||
<br>
|
||||
<br>
|
||||
@ -59,11 +59,11 @@ return details;
|
||||
|
||||
<br>
|
||||
|
||||
More details about Alarms can be found in [this tutorial{:target="_blank"}](${baseUrl}/docs/user-guide/alarms/).
|
||||
More details about Alarms can be found in [this tutorial{:target="_blank"}](${siteBaseUrl}/docs/user-guide/alarms/).
|
||||
|
||||
You can see the real life example, where this node is used, in the next tutorial:
|
||||
|
||||
- [Create and Clear Alarms{:target="_blank"}](${baseUrl}/docs/user-guide/rule-engine-2-0/tutorials/create-clear-alarms/)
|
||||
- [Create and Clear Alarms{:target="_blank"}](${siteBaseUrl}/docs/user-guide/rule-engine-2-0/tutorials/create-clear-alarms/)
|
||||
|
||||
<br>
|
||||
<br>
|
||||
@ -61,8 +61,8 @@ return false;
|
||||
|
||||
You can see real life example, how to use this node in those tutorials:
|
||||
|
||||
- [Create and Clear Alarms{:target="_blank"}](${baseUrl}/docs/user-guide/rule-engine-2-0/tutorials/create-clear-alarms/#node-a-filter-script)
|
||||
- [Reply to RPC Calls{:target="_blank"}](${baseUrl}/docs/user-guide/rule-engine-2-0/tutorials/rpc-reply-tutorial#add-filter-script-node)
|
||||
- [Create and Clear Alarms{:target="_blank"}](${siteBaseUrl}/docs/user-guide/rule-engine-2-0/tutorials/create-clear-alarms/#node-a-filter-script)
|
||||
- [Reply to RPC Calls{:target="_blank"}](${siteBaseUrl}/docs/user-guide/rule-engine-2-0/tutorials/rpc-reply-tutorial#add-filter-script-node)
|
||||
|
||||
<br>
|
||||
<br>
|
||||
@ -31,7 +31,7 @@ return 'Incoming message:\n' + JSON.stringify(msg) +
|
||||
|
||||
You can see real life example, how to use this node in this tutorial:
|
||||
|
||||
- [Reply to RPC Calls{:target="_blank"}](${baseUrl}/docs/user-guide/rule-engine-2-0/tutorials/rpc-reply-tutorial#log-unknown-request)
|
||||
- [Reply to RPC Calls{:target="_blank"}](${siteBaseUrl}/docs/user-guide/rule-engine-2-0/tutorials/rpc-reply-tutorial#log-unknown-request)
|
||||
|
||||
<br>
|
||||
<br>
|
||||
@ -90,7 +90,7 @@ return [];
|
||||
|
||||
You can see real life example, how to use this node in this tutorial:
|
||||
|
||||
- [Data function based on telemetry from 2 devices{:target="_blank"}](${baseUrl}/docs/user-guide/rule-engine-2-0/tutorials/function-based-on-telemetry-from-two-devices#delta-temperature-rule-chain)
|
||||
- [Data function based on telemetry from 2 devices{:target="_blank"}](${siteBaseUrl}/docs/user-guide/rule-engine-2-0/tutorials/function-based-on-telemetry-from-two-devices#delta-temperature-rule-chain)
|
||||
|
||||
<br>
|
||||
<br>
|
||||
@ -52,8 +52,8 @@ return {msg: msg, metadata: metadata, msgType: newType};
|
||||
|
||||
You can see real life example, how to use this node in those tutorials:
|
||||
|
||||
- [Transform incoming telemetry{:target="_blank"}](${baseUrl}/docs/user-guide/rule-engine-2-0/tutorials/transform-incoming-telemetry/)
|
||||
- [Reply to RPC Calls{:target="_blank"}](${baseUrl}/docs/user-guide/rule-engine-2-0/tutorials/rpc-reply-tutorial#add-transform-script-node)
|
||||
- [Transform incoming telemetry{:target="_blank"}](${siteBaseUrl}/docs/user-guide/rule-engine-2-0/tutorials/transform-incoming-telemetry/)
|
||||
- [Reply to RPC Calls{:target="_blank"}](${siteBaseUrl}/docs/user-guide/rule-engine-2-0/tutorials/rpc-reply-tutorial#add-transform-script-node)
|
||||
|
||||
<br>
|
||||
<br>
|
||||
@ -20,6 +20,7 @@ A JavaScript function performing custom action.
|
||||
* Display alert dialog with entity information:
|
||||
|
||||
```javascript
|
||||
{:code-style="max-height: 300px;"}
|
||||
var title;
|
||||
var content;
|
||||
if (entityName) {
|
||||
@ -52,6 +53,7 @@ function showAlertDialog(title, content) {
|
||||
* Delete device after confirmation:
|
||||
|
||||
```javascript
|
||||
{:code-style="max-height: 300px;"}
|
||||
var $injector = widgetContext.$scope.$injector;
|
||||
var dialogs = $injector.get(widgetContext.servicesMap.get('dialogs'));
|
||||
var deviceService = $injector.get(widgetContext.servicesMap.get('deviceService'));
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
#### HTML template of dialog to create a device or an asset
|
||||
|
||||
```html
|
||||
{:code-style="max-height: 400px;"}
|
||||
<form #addEntityForm="ngForm" [formGroup]="addEntityFormGroup"
|
||||
(ngSubmit)="save()" class="add-entity-form">
|
||||
<mat-toolbar fxLayout="row" color="primary">
|
||||
@ -158,3 +159,6 @@
|
||||
</form>
|
||||
{:copy-code}
|
||||
```
|
||||
|
||||
<br>
|
||||
<br>
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
#### Function displaying dialog to create a device or an asset
|
||||
|
||||
```javascript
|
||||
{:code-style="max-height: 400px;"}
|
||||
let $injector = widgetContext.$scope.$injector;
|
||||
let customDialog = $injector.get(widgetContext.servicesMap.get('customDialog'));
|
||||
let assetService = $injector.get(widgetContext.servicesMap.get('assetService'));
|
||||
@ -130,3 +131,6 @@ function AddEntityDialogController(instance) {
|
||||
}
|
||||
{:copy-code}
|
||||
```
|
||||
|
||||
<br>
|
||||
<br>
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
#### HTML template of dialog to edit a device or an asset
|
||||
|
||||
```html
|
||||
{:code-style="max-height: 400px;"}
|
||||
<form #editEntityForm="ngForm" [formGroup]="editEntityFormGroup"
|
||||
(ngSubmit)="save()" class="edit-entity-form">
|
||||
<mat-toolbar fxLayout="row" color="primary">
|
||||
@ -190,3 +191,6 @@
|
||||
</form>
|
||||
{:copy-code}
|
||||
```
|
||||
|
||||
<br>
|
||||
<br>
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
#### Function displaying dialog to edit a device or an asset
|
||||
|
||||
```javascript
|
||||
{:code-style="max-height: 400px;"}
|
||||
let $injector = widgetContext.$scope.$injector;
|
||||
let customDialog = $injector.get(widgetContext.servicesMap.get('customDialog'));
|
||||
let entityService = $injector.get(widgetContext.servicesMap.get('entityService'));
|
||||
@ -218,3 +219,6 @@ function EditEntityDialogController(instance) {
|
||||
}
|
||||
{:copy-code}
|
||||
```
|
||||
|
||||
<br>
|
||||
<br>
|
||||
|
||||
@ -34,7 +34,7 @@ function showQrCodeDialog(title, code, format) {
|
||||
{:copy-code}
|
||||
```
|
||||
|
||||
* Parse code as a device claiming info (in this case ```{deviceName: string, secretKey: string}```)<br>and then claim device (see [Claiming devices{:target="_blank"}](${baseUrl}/docs/user-guide/claiming-devices/) for details):
|
||||
* Parse code as a device claiming info (in this case ```{deviceName: string, secretKey: string}```)<br>and then claim device (see [Claiming devices{:target="_blank"}](${siteBaseUrl}/docs/user-guide/claiming-devices/) for details):
|
||||
|
||||
```javascript
|
||||
var $scope = widgetContext.$scope;
|
||||
|
||||
@ -127,7 +127,7 @@ self.onDataUpdated = function() {
|
||||
|
||||
- Click the **Run** button on the **Widget Editor Toolbar** in order to see the result in **Widget preview** section.
|
||||
|
||||

|
||||

|
||||
|
||||
In this example, the **alarmSource** and **alarms** properties of <span trigger-style="fontSize: 16px;" trigger-text="<b>subscription</b>" tb-help-popup="widget/editor/widget_js_subscription_object"></span> are assigned to **$scope** and become accessible within HTML template.
|
||||
|
||||
|
||||
@ -57,7 +57,7 @@ self.onDataUpdated = function() {
|
||||
|
||||
- Click the **Run** button on the **Widget Editor Toolbar** in order to see the result in **Widget preview** section.
|
||||
|
||||

|
||||

|
||||
|
||||
In this example, the external JS library API was used that becomes available after injecting the corresponding URL in **Resources** section.
|
||||
|
||||
|
||||
@ -98,7 +98,7 @@ self.onDataUpdated = function() {
|
||||
|
||||
- Click the **Run** button on the **Widget Editor Toolbar** in order to see the result in **Widget preview** section.
|
||||
|
||||

|
||||

|
||||
|
||||
In this example, the external JS library API was used that becomes available after injecting the corresponding URL in **Resources** section.
|
||||
|
||||
|
||||
@ -37,7 +37,7 @@ The **Widget Editor** will open, pre-populated with the content of the default *
|
||||
|
||||
- Click the **Run** button on the **Widget Editor Toolbar** in order to see the result in **Widget preview** section.
|
||||
|
||||

|
||||

|
||||
|
||||
In this example, the **data** property of <span trigger-style="fontSize: 16px;" trigger-text="<b>subscription</b>" tb-help-popup="widget/editor/widget_js_subscription_object"></span> is assigned to the **$scope** and becomes accessible within the HTML template.
|
||||
|
||||
|
||||
@ -114,7 +114,7 @@ self.onInit = function() {
|
||||
- Click the **Run** button on the **Widget Editor Toolbar** in order to see the result in **Widget preview** section.
|
||||
- Click dashboard edit button on the preview section to change the size of the resulting widget. Then click dashboard apply button. The final widget should look like the image below.
|
||||
|
||||

|
||||

|
||||
|
||||
- Click the **Save** button on the **Widget Editor Toolbar** to save widget type.
|
||||
|
||||
@ -123,13 +123,13 @@ To test how this widget performs RPC commands, we will need to place it in a das
|
||||
- Login as Tenant administrator.
|
||||
- Navigate to **Devices** and create new device with some name, for ex. "My RPC Device".
|
||||
- Open device details and click "Copy Access Token" button to copy device access token to clipboard.
|
||||
- Download [mqtt-js-rpc-from-server.sh{:target="_blank"}](${baseUrl}/docs/reference/resources/mqtt-js-rpc-from-server.sh) and [mqtt-js-rpc-from-server.js{:target="_blank"}](${baseUrl}/docs/reference/resources/mqtt-js-rpc-from-server.js). Place these files in a folder.
|
||||
- Download [mqtt-js-rpc-from-server.sh{:target="_blank"}](${siteBaseUrl}/docs/reference/resources/mqtt-js-rpc-from-server.sh) and [mqtt-js-rpc-from-server.js{:target="_blank"}](${siteBaseUrl}/docs/reference/resources/mqtt-js-rpc-from-server.js). Place these files in a folder.
|
||||
Edit **mqtt-js-rpc-from-server.sh** - replace **$ACCESS_TOKEN** with your device access token from the clipboard. And install mqtt client library.
|
||||
- Run **mqtt-js-rpc-from-server.sh** script. You should see a "connected" message in the console.
|
||||
- Navigate to **Dashboards** and create a new dashboard with some name, for ex. "My first control dashboard". Open this dashboard.
|
||||
- Click dashboard "edit" button. In the dashboard edit mode, click the "Entity aliases" button located on the dashboard toolbar.
|
||||
|
||||

|
||||

|
||||
|
||||
- Inside **Entity aliases** popup click "Add alias".
|
||||
- Fill "Alias name" field, for ex. "My RPC Device Alias".
|
||||
@ -137,12 +137,12 @@ To test how this widget performs RPC commands, we will need to place it in a das
|
||||
- Choose "Device" in "Type" field.
|
||||
- Select your device in "Entity list" field. In this example "My RPC Device".
|
||||
|
||||

|
||||

|
||||
|
||||
- Click "Add" and then "Save" in **Entity aliases**.
|
||||
- Click dashboard "+" button then click "Create new widget" button.
|
||||
|
||||

|
||||

|
||||
|
||||
- Then select **Widget Bundle** where your RPC widget was saved. Select "Control widget" tab.
|
||||
- Click your widget. In this example, "My first control widget".
|
||||
@ -152,7 +152,7 @@ To test how this widget performs RPC commands, we will need to place it in a das
|
||||
- Fill **RPC params** field with RPC params. For ex. "{ param1: "value1" }".
|
||||
- Click **Send RPC command** button. You should see the following response in the widget.
|
||||
|
||||

|
||||

|
||||
|
||||
The following output should be printed in the device console:
|
||||
|
||||
@ -166,18 +166,18 @@ In order to test "Two way" RPC command mode, we need to change the corresponding
|
||||
- Click dashboard "edit" button. In dashboard edit mode, click **Edit widget** button located in the header of Control widget.
|
||||
- In the widget details, view select "Advanced" tab and uncheck "Is One Way Command" checkbox.
|
||||
|
||||

|
||||

|
||||
|
||||
- Click **Apply changes** button on the widget details header. Close details and click dashboard **Apply changes** button.
|
||||
- Fill widget fields with RPC method name and params like in previous steps.
|
||||
Click **Send RPC command** button. You should see the following response in the widget.
|
||||
|
||||

|
||||

|
||||
|
||||
- stop **mqtt-js-rpc-from-server.sh** script.
|
||||
Click **Send RPC command** button. You should see the following response in the widget.
|
||||
|
||||

|
||||

|
||||
|
||||
In this example, **controlApi** is used to send RPC commands. Additionally, custom widget settings were introduced in order to configure RPC command mode and RPC request timeout.
|
||||
|
||||
|
||||
@ -58,7 +58,7 @@ self.onInit = function() {
|
||||
|
||||
- Click the **Run** button on the **Widget Editor Toolbar** to see the resulting **Widget preview** section.
|
||||
|
||||

|
||||

|
||||
|
||||
This is just a static HTML widget. There is no subscription data and no special widget API was used.
|
||||
|
||||
|
||||
@ -75,7 +75,7 @@ self.onDataUpdated = function() {
|
||||
|
||||
- Click the **Run** button on the **Widget Editor Toolbar** in order to see the result in **Widget preview** section.
|
||||
|
||||

|
||||

|
||||
|
||||
In this example, the <span trigger-style="fontSize: 16px;" trigger-text="<b>subscription</b>" tb-help-popup="widget/editor/widget_js_subscription_object"></span> **datasources** and **data** properties are assigned to **$scope** and become accessible within the HTML template.
|
||||
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
<div class="divider"></div>
|
||||
<br/>
|
||||
|
||||
All widget related JavaScript code according to the [Widget API{:target="_blank"}](${baseUrl}/docs/user-guide/contribution/widgets-development/#basic-widget-api).
|
||||
All widget related JavaScript code according to the [Widget API{:target="_blank"}](${siteBaseUrl}/docs/user-guide/contribution/widgets-development/#basic-widget-api).
|
||||
The built-in variable **self** is a reference to the widget instance.<br>
|
||||
Each widget function should be defined as a property of the **self** variable.
|
||||
**self** variable has property **ctx** of type [WidgetContext{:target="_blank"}](https://github.com/thingsboard/thingsboard/blob/5bb6403407aa4898084832d6698aa9ea6d484889/ui-ngx/src/app/modules/home/models/widget-component.models.ts#L107) - a reference to widget context that has all necessary API and data used by widget instance.
|
||||
@ -129,7 +129,7 @@ Browser debugger (if enabled) will automatically pause code execution at the deb
|
||||
|
||||
##### Further reading
|
||||
|
||||
For more information read [Widgets Development Guide{:target="_blank"}](${baseUrl}/docs/user-guide/contribution/widgets-development).
|
||||
For more information read [Widgets Development Guide{:target="_blank"}](${siteBaseUrl}/docs/user-guide/contribution/widgets-development).
|
||||
|
||||
<br>
|
||||
<br>
|
||||
|
||||
@ -3,10 +3,10 @@
|
||||
<div class="divider"></div>
|
||||
<br/>
|
||||
|
||||
The widget subscription object is instance of [IWidgetSubscription{:target="_blank"}](https://github.com/thingsboard/thingsboard/blob/2627fe51d491055d4140f16617ed543f7f5bd8f6/ui-ngx/src/app/core/api/widget-api.models.ts#L264") and contains all subscription information, including current data, according to the [widget type{:target="_blank"}](${baseUrl}/docs/user-guide/ui/widget-library/#widget-types).
|
||||
The widget subscription object is instance of [IWidgetSubscription{:target="_blank"}](https://github.com/thingsboard/thingsboard/blob/2627fe51d491055d4140f16617ed543f7f5bd8f6/ui-ngx/src/app/core/api/widget-api.models.ts#L264") and contains all subscription information, including current data, according to the [widget type{:target="_blank"}](${siteBaseUrl}/docs/user-guide/ui/widget-library/#widget-types).
|
||||
|
||||
Depending on widget type, subscription object provides different data structures.
|
||||
For [Latest values{:target="_blank"}](${baseUrl}/docs/user-guide/ui/widget-library/#latest-values) and [Time-series{:target="_blank"}](${baseUrl}/docs/user-guide/ui/widget-library/#time-series) widget types, it provides the following properties:
|
||||
For [Latest values{:target="_blank"}](${siteBaseUrl}/docs/user-guide/ui/widget-library/#latest-values) and [Time-series{:target="_blank"}](${siteBaseUrl}/docs/user-guide/ui/widget-library/#time-series) widget types, it provides the following properties:
|
||||
|
||||
- **datasources** - array of datasources (Array<[Datasource{:target="_blank"}](https://github.com/thingsboard/thingsboard/blob/2627fe51d491055d4140f16617ed543f7f5bd8f6/ui-ngx/src/app/shared/models/widget.models.ts#L279)>) used by this subscription, using the following structure:
|
||||
|
||||
@ -54,7 +54,7 @@ For [Latest values{:target="_blank"}](${baseUrl}/docs/user-guide/ui/widget-libra
|
||||
]
|
||||
```
|
||||
|
||||
For [Alarm widget{:target="_blank"}](${baseUrl}/docs/user-guide/ui/widget-library/#alarm-widget) type it provides the following properties:
|
||||
For [Alarm widget{:target="_blank"}](${siteBaseUrl}/docs/user-guide/ui/widget-library/#alarm-widget) type it provides the following properties:
|
||||
|
||||
- **alarmSource** - ([Datasource{:target="_blank"}](https://github.com/thingsboard/thingsboard/blob/2627fe51d491055d4140f16617ed543f7f5bd8f6/ui-ngx/src/app/shared/models/widget.models.ts#L279)) information about entity for which alarms are fetched, using the following structure:
|
||||
|
||||
@ -110,4 +110,4 @@ For [Alarm widget{:target="_blank"}](${baseUrl}/docs/user-guide/ui/widget-librar
|
||||
]
|
||||
```
|
||||
|
||||
For [RPC{:target="_blank"}](${baseUrl}/docs/user-guide/ui/widget-library/#rpc-control-widget) or [Static{:target="_blank"}](${baseUrl}/docs/user-guide/ui/widget-library/#static) widget types, subscription object is optional and does not contain necessary information.
|
||||
For [RPC{:target="_blank"}](${siteBaseUrl}/docs/user-guide/ui/widget-library/#rpc-control-widget) or [Static{:target="_blank"}](${siteBaseUrl}/docs/user-guide/ui/widget-library/#static) widget types, subscription object is optional and does not contain necessary information.
|
||||
|
||||
@ -33,7 +33,7 @@ return '# Some title\n - Entity name: ' + data[0]['entityName'];
|
||||
<ul>
|
||||
<li>
|
||||
Display greetings for currently logged-in user.<br>
|
||||
Let's assume widget has first datasource configured using <code>Current User</code> <a target="_blank" href="${baseUrl}/docs/user-guide/ui/aliases/#single-entity">Single entity</a> alias<br>
|
||||
Let's assume widget has first datasource configured using <code>Current User</code> <a target="_blank" href="${siteBaseUrl}/docs/user-guide/ui/aliases/#single-entity">Single entity</a> alias<br>
|
||||
and has data keys for <code>firstName</code>, <code>lastName</code> and <code>name</code> entity fields:
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
@ -34,7 +34,7 @@ return data[0] ? data[0]['entityName'] : '';
|
||||
<li>
|
||||
Prepare QR code text to use as device claiming info (in this case <code>{deviceName: string, secretKey: string}</code>).<br>
|
||||
Let's assume device has <code>claimingData</code> attribute with string JSON value containing <code>secretKey</code> field<br>
|
||||
(see <a target="_blank" href="${baseUrl}/docs/user-guide/claiming-devices/">Claiming devices</a>):
|
||||
(see <a target="_blank" href="${siteBaseUrl}/docs/user-guide/claiming-devices/">Claiming devices</a>):
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
|
||||
|
After Width: | Height: | Size: 16 KiB |
|
After Width: | Height: | Size: 22 KiB |
|
After Width: | Height: | Size: 11 KiB |
|
After Width: | Height: | Size: 9.7 KiB |
|
After Width: | Height: | Size: 10 KiB |
|
After Width: | Height: | Size: 4.1 KiB |
|
After Width: | Height: | Size: 20 KiB |
|
After Width: | Height: | Size: 6.8 KiB |
|
After Width: | Height: | Size: 4.5 KiB |
|
After Width: | Height: | Size: 43 KiB |
|
After Width: | Height: | Size: 9.2 KiB |
|
After Width: | Height: | Size: 13 KiB |
|
After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 28 KiB |