Commit 641db71ce6b834ec7c194b94a560e32b25da45a2
1 parent
9e9072de
Configure UI help assets base url.
Showing
46 changed files
with
201 additions
and
58 deletions
... | ... | @@ -96,7 +96,7 @@ public class DashboardController extends BaseController { |
96 | 96 | 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)."; |
97 | 97 | public static final String HIDDEN_FOR_MOBILE = "Exclude dashboards that are hidden for mobile"; |
98 | 98 | |
99 | - @Value("${dashboard.max_datapoints_limit}") | |
99 | + @Value("${ui.dashboard.max_datapoints_limit}") | |
100 | 100 | private long maxDatapointsLimit; |
101 | 101 | |
102 | 102 | @ApiOperation(value = "Get server time (getServerTime)", | ... | ... |
1 | +/** | |
2 | + * Copyright © 2016-2021 The Thingsboard Authors | |
3 | + * | |
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | |
5 | + * you may not use this file except in compliance with the License. | |
6 | + * You may obtain a copy of the License at | |
7 | + * | |
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | |
9 | + * | |
10 | + * Unless required by applicable law or agreed to in writing, software | |
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | |
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | + * See the License for the specific language governing permissions and | |
14 | + * limitations under the License. | |
15 | + */ | |
16 | +package org.thingsboard.server.controller; | |
17 | + | |
18 | +import io.swagger.annotations.ApiOperation; | |
19 | +import org.springframework.beans.factory.annotation.Value; | |
20 | +import org.springframework.security.access.prepost.PreAuthorize; | |
21 | +import org.springframework.web.bind.annotation.RequestMapping; | |
22 | +import org.springframework.web.bind.annotation.RequestMethod; | |
23 | +import org.springframework.web.bind.annotation.ResponseBody; | |
24 | +import org.springframework.web.bind.annotation.RestController; | |
25 | +import org.thingsboard.server.common.data.exception.ThingsboardException; | |
26 | +import org.thingsboard.server.queue.util.TbCoreComponent; | |
27 | + | |
28 | +@RestController | |
29 | +@TbCoreComponent | |
30 | +@RequestMapping("/api") | |
31 | +public class UiSettingsController extends BaseController { | |
32 | + | |
33 | + @Value("${ui.help.base-url}") | |
34 | + private String helpBaseUrl; | |
35 | + | |
36 | + @ApiOperation(value = "Get UI help base url (getHelpBaseUrl)", | |
37 | + notes = "Get UI help base url used to fetch help assets. " + | |
38 | + "The actual value of the base url is configurable in the system configuration file.") | |
39 | + @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") | |
40 | + @RequestMapping(value = "/uiSettings/helpBaseUrl", method = RequestMethod.GET) | |
41 | + @ResponseBody | |
42 | + public String getHelpBaseUrl() throws ThingsboardException { | |
43 | + return helpBaseUrl; | |
44 | + } | |
45 | + | |
46 | +} | ... | ... |
... | ... | @@ -129,10 +129,16 @@ usage: |
129 | 129 | check: |
130 | 130 | cycle: "${USAGE_STATS_CHECK_CYCLE:60000}" |
131 | 131 | |
132 | -# Dashboard parameters | |
133 | -dashboard: | |
134 | - # Maximum allowed datapoints fetched by widgets | |
135 | - max_datapoints_limit: "${DASHBOARD_MAX_DATAPOINTS_LIMIT:50000}" | |
132 | +# UI parameters | |
133 | +ui: | |
134 | + # Dashboard parameters | |
135 | + dashboard: | |
136 | + # Maximum allowed datapoints fetched by widgets | |
137 | + max_datapoints_limit: "${DASHBOARD_MAX_DATAPOINTS_LIMIT:50000}" | |
138 | + # Help parameters | |
139 | + help: | |
140 | + # Base url for UI help assets | |
141 | + base-url: "${UI_HELP_BASE_URL:https://raw.githubusercontent.com/thingsboard/thingsboard/master/ui-ngx/src/assets}" | |
136 | 142 | |
137 | 143 | database: |
138 | 144 | 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 |
70 | 70 | acl transport_http_acl path_beg /api/v1/ |
71 | 71 | acl letsencrypt_http_acl path_beg /.well-known/acme-challenge/ |
72 | 72 | acl tb_api_acl path_beg /api/ /swagger /webjars /v2/ /static/rulenode/ /oauth2/ /login/oauth2/ /static/widgets/ |
73 | - acl tb_rulenode_assets path_reg ^/assets/help/.*/rulenode/.*$ | |
74 | 73 | |
75 | 74 | redirect scheme https if !letsencrypt_http_acl !transport_http_acl { env(FORCE_HTTPS_REDIRECT) -m str true } |
76 | 75 | |
77 | 76 | use_backend letsencrypt_http if letsencrypt_http_acl |
78 | 77 | use_backend tb-http-backend if transport_http_acl |
79 | - use_backend tb-api-backend if tb_api_acl or tb_rulenode_assets | |
78 | + use_backend tb-api-backend if tb_api_acl | |
80 | 79 | |
81 | 80 | default_backend tb-web-backend |
82 | 81 | |
... | ... | @@ -89,10 +88,9 @@ frontend https_in |
89 | 88 | |
90 | 89 | acl transport_http_acl path_beg /api/v1/ |
91 | 90 | acl tb_api_acl path_beg /api/ /swagger /webjars /v2/ /static/rulenode/ /oauth2/ /login/oauth2/ /static/widgets/ |
92 | - acl tb_rulenode_assets path_reg ^/assets/help/.*/rulenode/.*$ | |
93 | 91 | |
94 | 92 | use_backend tb-http-backend if transport_http_acl |
95 | - use_backend tb-api-backend if tb_api_acl or tb_rulenode_assets | |
93 | + use_backend tb-api-backend if tb_api_acl | |
96 | 94 | |
97 | 95 | default_backend tb-web-backend |
98 | 96 | ... | ... |
... | ... | @@ -26,10 +26,6 @@ const PROXY_CONFIG = { |
26 | 26 | "target": ruleNodeUiforwardUrl, |
27 | 27 | "secure": false, |
28 | 28 | }, |
29 | - "/assets/help/*/rulenode/**": { | |
30 | - "target": ruleNodeUiforwardUrl, | |
31 | - "secure": false, | |
32 | - }, | |
33 | 29 | "/static/widgets": { |
34 | 30 | "target": forwardUrl, |
35 | 31 | "secure": false, | ... | ... |
1 | +/// | |
2 | +/// Copyright © 2016-2021 The Thingsboard Authors | |
3 | +/// | |
4 | +/// Licensed under the Apache License, Version 2.0 (the "License"); | |
5 | +/// you may not use this file except in compliance with the License. | |
6 | +/// You may obtain a copy of the License at | |
7 | +/// | |
8 | +/// http://www.apache.org/licenses/LICENSE-2.0 | |
9 | +/// | |
10 | +/// Unless required by applicable law or agreed to in writing, software | |
11 | +/// distributed under the License is distributed on an "AS IS" BASIS, | |
12 | +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | +/// See the License for the specific language governing permissions and | |
14 | +/// limitations under the License. | |
15 | +/// | |
16 | + | |
17 | +import { Injectable } from '@angular/core'; | |
18 | +import { HttpClient } from '@angular/common/http'; | |
19 | +import { defaultHttpOptions, defaultHttpOptionsFromConfig, RequestConfig } from '@core/http/http-utils'; | |
20 | +import { Observable } from 'rxjs'; | |
21 | +import { publishReplay, refCount } from 'rxjs/operators'; | |
22 | + | |
23 | +@Injectable({ | |
24 | + providedIn: 'root' | |
25 | +}) | |
26 | +export class UiSettingsService { | |
27 | + | |
28 | + private helpBaseUrlObservable: Observable<string>; | |
29 | + | |
30 | + constructor( | |
31 | + private http: HttpClient | |
32 | + ) { } | |
33 | + | |
34 | + public getHelpBaseUrl(): Observable<string> { | |
35 | + if (!this.helpBaseUrlObservable) { | |
36 | + this.helpBaseUrlObservable = this.http.get('/api/uiSettings/helpBaseUrl', {responseType: 'text', ...defaultHttpOptions(true)}).pipe( | |
37 | + publishReplay(1), | |
38 | + refCount() | |
39 | + ); | |
40 | + } | |
41 | + return this.helpBaseUrlObservable; | |
42 | + } | |
43 | +} | ... | ... |
... | ... | @@ -18,23 +18,29 @@ import { Injectable } from '@angular/core'; |
18 | 18 | import { HttpClient } from '@angular/common/http'; |
19 | 19 | import { TranslateService } from '@ngx-translate/core'; |
20 | 20 | import { Observable, of } from 'rxjs'; |
21 | -import { catchError, mergeMap, tap } from 'rxjs/operators'; | |
22 | -import { helpBaseUrl } from '@shared/models/constants'; | |
21 | +import { catchError, map, mergeMap, tap } from 'rxjs/operators'; | |
22 | +import { helpBaseUrl as siteBaseUrl } from '@shared/models/constants'; | |
23 | +import { UiSettingsService } from '@core/http/ui-settings.service'; | |
23 | 24 | |
24 | -const NOT_FOUND_CONTENT = '## Not found'; | |
25 | +const localHelpBaseUrl = '/assets'; | |
26 | + | |
27 | +const NOT_FOUND_CONTENT: HelpData = { | |
28 | + content: '## Not found', | |
29 | + helpBaseUrl: localHelpBaseUrl | |
30 | +}; | |
25 | 31 | |
26 | 32 | @Injectable({ |
27 | 33 | providedIn: 'root' |
28 | 34 | }) |
29 | 35 | export class HelpService { |
30 | 36 | |
31 | - private helpBaseUrl = helpBaseUrl; | |
32 | - | |
37 | + private siteBaseUrl = siteBaseUrl; | |
33 | 38 | private helpCache: {[lang: string]: {[key: string]: string}} = {}; |
34 | 39 | |
35 | 40 | constructor( |
36 | 41 | private translate: TranslateService, |
37 | - private http: HttpClient | |
42 | + private http: HttpClient, | |
43 | + private uiSettingsService: UiSettingsService | |
38 | 44 | ) {} |
39 | 45 | |
40 | 46 | getHelpContent(key: string): Observable<string> { |
... | ... | @@ -70,13 +76,38 @@ export class HelpService { |
70 | 76 | } |
71 | 77 | } |
72 | 78 | |
73 | - private loadHelpContent(lang: string, key: string): Observable<string> { | |
74 | - return this.http.get(`/assets/help/${lang}/${key}.md`, {responseType: 'text'} ); | |
79 | + private loadHelpContent(lang: string, key: string): Observable<HelpData> { | |
80 | + return this.uiSettingsService.getHelpBaseUrl().pipe( | |
81 | + mergeMap((helpBaseUrl) => { | |
82 | + return this.loadHelpContentFromBaseUrl(helpBaseUrl, lang, key).pipe( | |
83 | + catchError((e) => { | |
84 | + if (localHelpBaseUrl !== helpBaseUrl) { | |
85 | + return this.loadHelpContentFromBaseUrl(localHelpBaseUrl, lang, key); | |
86 | + } else { | |
87 | + throw e; | |
88 | + } | |
89 | + }) | |
90 | + ); | |
91 | + }) | |
92 | + ); | |
93 | + } | |
94 | + | |
95 | + private loadHelpContentFromBaseUrl(helpBaseUrl: string, lang: string, key: string): Observable<HelpData> { | |
96 | + return this.http.get(`${helpBaseUrl}/help/${lang}/${key}.md`, {responseType: 'text'} ).pipe( | |
97 | + map((content) => { | |
98 | + return { | |
99 | + content, | |
100 | + helpBaseUrl | |
101 | + }; | |
102 | + }) | |
103 | + ); | |
75 | 104 | } |
76 | 105 | |
77 | - private processVariables(content: string): string { | |
78 | - const baseUrlReg = /\${baseUrl}/g; | |
79 | - return content.replace(baseUrlReg, this.helpBaseUrl); | |
106 | + private processVariables(helpData: HelpData): string { | |
107 | + const baseUrlReg = /\${siteBaseUrl}/g; | |
108 | + helpData.content = helpData.content.replace(baseUrlReg, this.siteBaseUrl); | |
109 | + const helpBaseUrlReg = /\${helpBaseUrl}/g; | |
110 | + return helpData.content.replace(helpBaseUrlReg, helpData.helpBaseUrl); | |
80 | 111 | } |
81 | 112 | |
82 | 113 | private processIncludes(content: string): Observable<string> { |
... | ... | @@ -96,3 +127,8 @@ export class HelpService { |
96 | 127 | } |
97 | 128 | |
98 | 129 | } |
130 | + | |
131 | +interface HelpData { | |
132 | + content: string; | |
133 | + helpBaseUrl: string; | |
134 | +} | ... | ... |
ui-ngx/src/assets/help/en_US/rulenode/clear_alarm_node_script_fn.md
renamed from
rule-engine/rule-engine-components/src/main/resources/public/assets/help/en_US/rulenode/clear_alarm_node_script_fn.md
... | ... | @@ -58,11 +58,11 @@ return details; |
58 | 58 | |
59 | 59 | <br> |
60 | 60 | |
61 | -More details about Alarms can be found in [this tutorial{:target="_blank"}](${baseUrl}/docs/user-guide/alarms/). | |
61 | +More details about Alarms can be found in [this tutorial{:target="_blank"}](${siteBaseUrl}/docs/user-guide/alarms/). | |
62 | 62 | |
63 | 63 | You can see the real life example, where this node is used, in the next tutorial: |
64 | 64 | |
65 | -- [Create and Clear Alarms{:target="_blank"}](${baseUrl}/docs/user-guide/rule-engine-2-0/tutorials/create-clear-alarms/) | |
65 | +- [Create and Clear Alarms{:target="_blank"}](${siteBaseUrl}/docs/user-guide/rule-engine-2-0/tutorials/create-clear-alarms/) | |
66 | 66 | |
67 | 67 | <br> |
68 | 68 | <br> | ... | ... |
ui-ngx/src/assets/help/en_US/rulenode/common_node_script_args.md
renamed from
rule-engine/rule-engine-components/src/main/resources/public/assets/help/en_US/rulenode/common_node_script_args.md
ui-ngx/src/assets/help/en_US/rulenode/create_alarm_node_script_fn.md
renamed from
rule-engine/rule-engine-components/src/main/resources/public/assets/help/en_US/rulenode/create_alarm_node_script_fn.md
... | ... | @@ -59,11 +59,11 @@ return details; |
59 | 59 | |
60 | 60 | <br> |
61 | 61 | |
62 | -More details about Alarms can be found in [this tutorial{:target="_blank"}](${baseUrl}/docs/user-guide/alarms/). | |
62 | +More details about Alarms can be found in [this tutorial{:target="_blank"}](${siteBaseUrl}/docs/user-guide/alarms/). | |
63 | 63 | |
64 | 64 | You can see the real life example, where this node is used, in the next tutorial: |
65 | 65 | |
66 | -- [Create and Clear Alarms{:target="_blank"}](${baseUrl}/docs/user-guide/rule-engine-2-0/tutorials/create-clear-alarms/) | |
66 | +- [Create and Clear Alarms{:target="_blank"}](${siteBaseUrl}/docs/user-guide/rule-engine-2-0/tutorials/create-clear-alarms/) | |
67 | 67 | |
68 | 68 | <br> |
69 | 69 | <br> | ... | ... |
ui-ngx/src/assets/help/en_US/rulenode/filter_node_script_fn.md
renamed from
rule-engine/rule-engine-components/src/main/resources/public/assets/help/en_US/rulenode/filter_node_script_fn.md
... | ... | @@ -61,8 +61,8 @@ return false; |
61 | 61 | |
62 | 62 | You can see real life example, how to use this node in those tutorials: |
63 | 63 | |
64 | -- [Create and Clear Alarms{:target="_blank"}](${baseUrl}/docs/user-guide/rule-engine-2-0/tutorials/create-clear-alarms/#node-a-filter-script) | |
65 | -- [Reply to RPC Calls{:target="_blank"}](${baseUrl}/docs/user-guide/rule-engine-2-0/tutorials/rpc-reply-tutorial#add-filter-script-node) | |
64 | +- [Create and Clear Alarms{:target="_blank"}](${siteBaseUrl}/docs/user-guide/rule-engine-2-0/tutorials/create-clear-alarms/#node-a-filter-script) | |
65 | +- [Reply to RPC Calls{:target="_blank"}](${siteBaseUrl}/docs/user-guide/rule-engine-2-0/tutorials/rpc-reply-tutorial#add-filter-script-node) | |
66 | 66 | |
67 | 67 | <br> |
68 | 68 | <br> | ... | ... |
ui-ngx/src/assets/help/en_US/rulenode/generator_node_script_fn.md
renamed from
rule-engine/rule-engine-components/src/main/resources/public/assets/help/en_US/rulenode/generator_node_script_fn.md
ui-ngx/src/assets/help/en_US/rulenode/log_node_script_fn.md
renamed from
rule-engine/rule-engine-components/src/main/resources/public/assets/help/en_US/rulenode/log_node_script_fn.md
... | ... | @@ -31,7 +31,7 @@ return 'Incoming message:\n' + JSON.stringify(msg) + |
31 | 31 | |
32 | 32 | You can see real life example, how to use this node in this tutorial: |
33 | 33 | |
34 | -- [Reply to RPC Calls{:target="_blank"}](${baseUrl}/docs/user-guide/rule-engine-2-0/tutorials/rpc-reply-tutorial#log-unknown-request) | |
34 | +- [Reply to RPC Calls{:target="_blank"}](${siteBaseUrl}/docs/user-guide/rule-engine-2-0/tutorials/rpc-reply-tutorial#log-unknown-request) | |
35 | 35 | |
36 | 36 | <br> |
37 | 37 | <br> | ... | ... |
ui-ngx/src/assets/help/en_US/rulenode/switch_node_script_fn.md
renamed from
rule-engine/rule-engine-components/src/main/resources/public/assets/help/en_US/rulenode/switch_node_script_fn.md
... | ... | @@ -90,7 +90,7 @@ return []; |
90 | 90 | |
91 | 91 | You can see real life example, how to use this node in this tutorial: |
92 | 92 | |
93 | -- [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) | |
93 | +- [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) | |
94 | 94 | |
95 | 95 | <br> |
96 | 96 | <br> | ... | ... |
ui-ngx/src/assets/help/en_US/rulenode/transformation_node_script_fn.md
renamed from
rule-engine/rule-engine-components/src/main/resources/public/assets/help/en_US/rulenode/transformation_node_script_fn.md
... | ... | @@ -52,8 +52,8 @@ return {msg: msg, metadata: metadata, msgType: newType}; |
52 | 52 | |
53 | 53 | You can see real life example, how to use this node in those tutorials: |
54 | 54 | |
55 | -- [Transform incoming telemetry{:target="_blank"}](${baseUrl}/docs/user-guide/rule-engine-2-0/tutorials/transform-incoming-telemetry/) | |
56 | -- [Reply to RPC Calls{:target="_blank"}](${baseUrl}/docs/user-guide/rule-engine-2-0/tutorials/rpc-reply-tutorial#add-transform-script-node) | |
55 | +- [Transform incoming telemetry{:target="_blank"}](${siteBaseUrl}/docs/user-guide/rule-engine-2-0/tutorials/transform-incoming-telemetry/) | |
56 | +- [Reply to RPC Calls{:target="_blank"}](${siteBaseUrl}/docs/user-guide/rule-engine-2-0/tutorials/rpc-reply-tutorial#add-transform-script-node) | |
57 | 57 | |
58 | 58 | <br> |
59 | 59 | <br> | ... | ... |
... | ... | @@ -20,6 +20,7 @@ A JavaScript function performing custom action. |
20 | 20 | * Display alert dialog with entity information: |
21 | 21 | |
22 | 22 | ```javascript |
23 | +{:code-style="max-height: 300px;"} | |
23 | 24 | var title; |
24 | 25 | var content; |
25 | 26 | if (entityName) { |
... | ... | @@ -52,6 +53,7 @@ function showAlertDialog(title, content) { |
52 | 53 | * Delete device after confirmation: |
53 | 54 | |
54 | 55 | ```javascript |
56 | +{:code-style="max-height: 300px;"} | |
55 | 57 | var $injector = widgetContext.$scope.$injector; |
56 | 58 | var dialogs = $injector.get(widgetContext.servicesMap.get('dialogs')); |
57 | 59 | var deviceService = $injector.get(widgetContext.servicesMap.get('deviceService')); | ... | ... |
1 | 1 | #### HTML template of dialog to create a device or an asset |
2 | 2 | |
3 | 3 | ```html |
4 | +{:code-style="max-height: 400px;"} | |
4 | 5 | <form #addEntityForm="ngForm" [formGroup]="addEntityFormGroup" |
5 | 6 | (ngSubmit)="save()" class="add-entity-form"> |
6 | 7 | <mat-toolbar fxLayout="row" color="primary"> |
... | ... | @@ -158,3 +159,6 @@ |
158 | 159 | </form> |
159 | 160 | {:copy-code} |
160 | 161 | ``` |
162 | + | |
163 | +<br> | |
164 | +<br> | ... | ... |
1 | 1 | #### Function displaying dialog to create a device or an asset |
2 | 2 | |
3 | 3 | ```javascript |
4 | +{:code-style="max-height: 400px;"} | |
4 | 5 | let $injector = widgetContext.$scope.$injector; |
5 | 6 | let customDialog = $injector.get(widgetContext.servicesMap.get('customDialog')); |
6 | 7 | let assetService = $injector.get(widgetContext.servicesMap.get('assetService')); |
... | ... | @@ -130,3 +131,6 @@ function AddEntityDialogController(instance) { |
130 | 131 | } |
131 | 132 | {:copy-code} |
132 | 133 | ``` |
134 | + | |
135 | +<br> | |
136 | +<br> | ... | ... |
1 | 1 | #### HTML template of dialog to edit a device or an asset |
2 | 2 | |
3 | 3 | ```html |
4 | +{:code-style="max-height: 400px;"} | |
4 | 5 | <form #editEntityForm="ngForm" [formGroup]="editEntityFormGroup" |
5 | 6 | (ngSubmit)="save()" class="edit-entity-form"> |
6 | 7 | <mat-toolbar fxLayout="row" color="primary"> |
... | ... | @@ -190,3 +191,6 @@ |
190 | 191 | </form> |
191 | 192 | {:copy-code} |
192 | 193 | ``` |
194 | + | |
195 | +<br> | |
196 | +<br> | ... | ... |
1 | 1 | #### Function displaying dialog to edit a device or an asset |
2 | 2 | |
3 | 3 | ```javascript |
4 | +{:code-style="max-height: 400px;"} | |
4 | 5 | let $injector = widgetContext.$scope.$injector; |
5 | 6 | let customDialog = $injector.get(widgetContext.servicesMap.get('customDialog')); |
6 | 7 | let entityService = $injector.get(widgetContext.servicesMap.get('entityService')); |
... | ... | @@ -218,3 +219,6 @@ function EditEntityDialogController(instance) { |
218 | 219 | } |
219 | 220 | {:copy-code} |
220 | 221 | ``` |
222 | + | |
223 | +<br> | |
224 | +<br> | ... | ... |
... | ... | @@ -34,7 +34,7 @@ function showQrCodeDialog(title, code, format) { |
34 | 34 | {:copy-code} |
35 | 35 | ``` |
36 | 36 | |
37 | -* 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): | |
37 | +* 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): | |
38 | 38 | |
39 | 39 | ```javascript |
40 | 40 | var $scope = widgetContext.$scope; | ... | ... |
... | ... | @@ -127,7 +127,7 @@ self.onDataUpdated = function() { |
127 | 127 | |
128 | 128 | - Click the **Run** button on the **Widget Editor Toolbar** in order to see the result in **Widget preview** section. |
129 | 129 | |
130 | - | |
130 | + | |
131 | 131 | |
132 | 132 | 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. |
133 | 133 | ... | ... |
... | ... | @@ -57,7 +57,7 @@ self.onDataUpdated = function() { |
57 | 57 | |
58 | 58 | - Click the **Run** button on the **Widget Editor Toolbar** in order to see the result in **Widget preview** section. |
59 | 59 | |
60 | - | |
60 | + | |
61 | 61 | |
62 | 62 | In this example, the external JS library API was used that becomes available after injecting the corresponding URL in **Resources** section. |
63 | 63 | ... | ... |
... | ... | @@ -98,7 +98,7 @@ self.onDataUpdated = function() { |
98 | 98 | |
99 | 99 | - Click the **Run** button on the **Widget Editor Toolbar** in order to see the result in **Widget preview** section. |
100 | 100 | |
101 | - | |
101 | + | |
102 | 102 | |
103 | 103 | In this example, the external JS library API was used that becomes available after injecting the corresponding URL in **Resources** section. |
104 | 104 | ... | ... |
... | ... | @@ -37,7 +37,7 @@ The **Widget Editor** will open, pre-populated with the content of the default * |
37 | 37 | |
38 | 38 | - Click the **Run** button on the **Widget Editor Toolbar** in order to see the result in **Widget preview** section. |
39 | 39 | |
40 | - | |
40 | + | |
41 | 41 | |
42 | 42 | 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. |
43 | 43 | ... | ... |
... | ... | @@ -114,7 +114,7 @@ self.onInit = function() { |
114 | 114 | - Click the **Run** button on the **Widget Editor Toolbar** in order to see the result in **Widget preview** section. |
115 | 115 | - 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. |
116 | 116 | |
117 | - | |
117 | + | |
118 | 118 | |
119 | 119 | - Click the **Save** button on the **Widget Editor Toolbar** to save widget type. |
120 | 120 | |
... | ... | @@ -123,13 +123,13 @@ To test how this widget performs RPC commands, we will need to place it in a das |
123 | 123 | - Login as Tenant administrator. |
124 | 124 | - Navigate to **Devices** and create new device with some name, for ex. "My RPC Device". |
125 | 125 | - Open device details and click "Copy Access Token" button to copy device access token to clipboard. |
126 | -- 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. | |
126 | +- 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. | |
127 | 127 | Edit **mqtt-js-rpc-from-server.sh** - replace **$ACCESS_TOKEN** with your device access token from the clipboard. And install mqtt client library. |
128 | 128 | - Run **mqtt-js-rpc-from-server.sh** script. You should see a "connected" message in the console. |
129 | 129 | - Navigate to **Dashboards** and create a new dashboard with some name, for ex. "My first control dashboard". Open this dashboard. |
130 | 130 | - Click dashboard "edit" button. In the dashboard edit mode, click the "Entity aliases" button located on the dashboard toolbar. |
131 | 131 | |
132 | - | |
132 | + | |
133 | 133 | |
134 | 134 | - Inside **Entity aliases** popup click "Add alias". |
135 | 135 | - 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 |
137 | 137 | - Choose "Device" in "Type" field. |
138 | 138 | - Select your device in "Entity list" field. In this example "My RPC Device". |
139 | 139 | |
140 | - | |
140 | + | |
141 | 141 | |
142 | 142 | - Click "Add" and then "Save" in **Entity aliases**. |
143 | 143 | - Click dashboard "+" button then click "Create new widget" button. |
144 | 144 | |
145 | - | |
145 | + | |
146 | 146 | |
147 | 147 | - Then select **Widget Bundle** where your RPC widget was saved. Select "Control widget" tab. |
148 | 148 | - 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 |
152 | 152 | - Fill **RPC params** field with RPC params. For ex. "{ param1: "value1" }". |
153 | 153 | - Click **Send RPC command** button. You should see the following response in the widget. |
154 | 154 | |
155 | - | |
155 | + | |
156 | 156 | |
157 | 157 | The following output should be printed in the device console: |
158 | 158 | |
... | ... | @@ -166,18 +166,18 @@ In order to test "Two way" RPC command mode, we need to change the corresponding |
166 | 166 | - Click dashboard "edit" button. In dashboard edit mode, click **Edit widget** button located in the header of Control widget. |
167 | 167 | - In the widget details, view select "Advanced" tab and uncheck "Is One Way Command" checkbox. |
168 | 168 | |
169 | - | |
169 | + | |
170 | 170 | |
171 | 171 | - Click **Apply changes** button on the widget details header. Close details and click dashboard **Apply changes** button. |
172 | 172 | - Fill widget fields with RPC method name and params like in previous steps. |
173 | 173 | Click **Send RPC command** button. You should see the following response in the widget. |
174 | 174 | |
175 | - | |
175 | + | |
176 | 176 | |
177 | 177 | - stop **mqtt-js-rpc-from-server.sh** script. |
178 | 178 | Click **Send RPC command** button. You should see the following response in the widget. |
179 | 179 | |
180 | - | |
180 | + | |
181 | 181 | |
182 | 182 | 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. |
183 | 183 | ... | ... |
... | ... | @@ -58,7 +58,7 @@ self.onInit = function() { |
58 | 58 | |
59 | 59 | - Click the **Run** button on the **Widget Editor Toolbar** to see the resulting **Widget preview** section. |
60 | 60 | |
61 | - | |
61 | + | |
62 | 62 | |
63 | 63 | This is just a static HTML widget. There is no subscription data and no special widget API was used. |
64 | 64 | ... | ... |
... | ... | @@ -75,7 +75,7 @@ self.onDataUpdated = function() { |
75 | 75 | |
76 | 76 | - Click the **Run** button on the **Widget Editor Toolbar** in order to see the result in **Widget preview** section. |
77 | 77 | |
78 | - | |
78 | + | |
79 | 79 | |
80 | 80 | 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. |
81 | 81 | ... | ... |
... | ... | @@ -3,7 +3,7 @@ |
3 | 3 | <div class="divider"></div> |
4 | 4 | <br/> |
5 | 5 | |
6 | -All widget related JavaScript code according to the [Widget API{:target="_blank"}](${baseUrl}/docs/user-guide/contribution/widgets-development/#basic-widget-api). | |
6 | +All widget related JavaScript code according to the [Widget API{:target="_blank"}](${siteBaseUrl}/docs/user-guide/contribution/widgets-development/#basic-widget-api). | |
7 | 7 | The built-in variable **self** is a reference to the widget instance.<br> |
8 | 8 | Each widget function should be defined as a property of the **self** variable. |
9 | 9 | **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 |
129 | 129 | |
130 | 130 | ##### Further reading |
131 | 131 | |
132 | -For more information read [Widgets Development Guide{:target="_blank"}](${baseUrl}/docs/user-guide/contribution/widgets-development). | |
132 | +For more information read [Widgets Development Guide{:target="_blank"}](${siteBaseUrl}/docs/user-guide/contribution/widgets-development). | |
133 | 133 | |
134 | 134 | <br> |
135 | 135 | <br> | ... | ... |
... | ... | @@ -3,10 +3,10 @@ |
3 | 3 | <div class="divider"></div> |
4 | 4 | <br/> |
5 | 5 | |
6 | -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). | |
6 | +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). | |
7 | 7 | |
8 | 8 | Depending on widget type, subscription object provides different data structures. |
9 | -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: | |
9 | +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: | |
10 | 10 | |
11 | 11 | - **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: |
12 | 12 | |
... | ... | @@ -54,7 +54,7 @@ For [Latest values{:target="_blank"}](${baseUrl}/docs/user-guide/ui/widget-libra |
54 | 54 | ] |
55 | 55 | ``` |
56 | 56 | |
57 | -For [Alarm widget{:target="_blank"}](${baseUrl}/docs/user-guide/ui/widget-library/#alarm-widget) type it provides the following properties: | |
57 | +For [Alarm widget{:target="_blank"}](${siteBaseUrl}/docs/user-guide/ui/widget-library/#alarm-widget) type it provides the following properties: | |
58 | 58 | |
59 | 59 | - **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: |
60 | 60 | |
... | ... | @@ -110,4 +110,4 @@ For [Alarm widget{:target="_blank"}](${baseUrl}/docs/user-guide/ui/widget-librar |
110 | 110 | ] |
111 | 111 | ``` |
112 | 112 | |
113 | -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. | |
113 | +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']; |
33 | 33 | <ul> |
34 | 34 | <li> |
35 | 35 | Display greetings for currently logged-in user.<br> |
36 | -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> | |
36 | +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> | |
37 | 37 | and has data keys for <code>firstName</code>, <code>lastName</code> and <code>name</code> entity fields: |
38 | 38 | </li> |
39 | 39 | </ul> | ... | ... |
... | ... | @@ -34,7 +34,7 @@ return data[0] ? data[0]['entityName'] : ''; |
34 | 34 | <li> |
35 | 35 | Prepare QR code text to use as device claiming info (in this case <code>{deviceName: string, secretKey: string}</code>).<br> |
36 | 36 | Let's assume device has <code>claimingData</code> attribute with string JSON value containing <code>secretKey</code> field<br> |
37 | -(see <a target="_blank" href="${baseUrl}/docs/user-guide/claiming-devices/">Claiming devices</a>): | |
37 | +(see <a target="_blank" href="${siteBaseUrl}/docs/user-guide/claiming-devices/">Claiming devices</a>): | |
38 | 38 | </li> |
39 | 39 | </ul> |
40 | 40 | ... | ... |
22.3 KB
ui-ngx/src/assets/help/images/widget/editor/examples/control-widget-sample-response-one-way.png
0 → 100644
10.7 KB
ui-ngx/src/assets/help/images/widget/editor/examples/control-widget-sample-response-timeout.png
0 → 100644
9.67 KB
ui-ngx/src/assets/help/images/widget/editor/examples/control-widget-sample-response-two-way.png
0 → 100644
10.2 KB
4.14 KB
ui-ngx/src/assets/help/images/widget/editor/examples/dashboard-create-new-widget-button.png
0 → 100644
6.83 KB
ui-ngx/src/assets/help/images/widget/editor/examples/dashboard-toolbar-entity-aliases.png
0 → 100644
4.48 KB
ui-ngx/src/assets/help/images/widget/editor/examples/external-js-timeseries-widget-sample.png
0 → 100644
43.3 KB
9.21 KB
13 KB