Merge branch 'master' of github.com:thingsboard/thingsboard
This commit is contained in:
		
						commit
						5632b07009
					
				@ -59,7 +59,7 @@
 | 
			
		||||
    "flot.curvedlines": "https://github.com/MichaelZinsmaier/CurvedLines.git#master",
 | 
			
		||||
    "font-awesome": "^4.7.0",
 | 
			
		||||
    "html2canvas": "^1.4.1",
 | 
			
		||||
    "jquery": "^3.6.3",
 | 
			
		||||
    "jquery": "^3.7.1",
 | 
			
		||||
    "jquery.terminal": "^2.35.3",
 | 
			
		||||
    "js-beautify": "1.14.7",
 | 
			
		||||
    "json-schema-defaults": "^0.4.0",
 | 
			
		||||
@ -127,7 +127,7 @@
 | 
			
		||||
    "@types/flowjs": "^2.13.9",
 | 
			
		||||
    "@types/jasmine": "~3.10.2",
 | 
			
		||||
    "@types/jasminewd2": "^2.0.10",
 | 
			
		||||
    "@types/jquery": "^3.5.16",
 | 
			
		||||
    "@types/jquery": "^3.5.30",
 | 
			
		||||
    "@types/js-beautify": "^1.13.3",
 | 
			
		||||
    "@types/leaflet": "1.8.0",
 | 
			
		||||
    "@types/leaflet-polylinedecorator": "1.6.4",
 | 
			
		||||
 | 
			
		||||
@ -32,6 +32,7 @@ import { AuthService } from '@core/auth/auth.service';
 | 
			
		||||
import { svgIcons, svgIconsUrl } from '@shared/models/icon.models';
 | 
			
		||||
import { ActionSettingsChangeLanguage } from '@core/settings/settings.actions';
 | 
			
		||||
import { SETTINGS_KEY } from '@core/settings/settings.effects';
 | 
			
		||||
import { initCustomJQueryEvents } from '@shared/models/jquery-event.models';
 | 
			
		||||
 | 
			
		||||
@Component({
 | 
			
		||||
  selector: 'tb-root',
 | 
			
		||||
@ -74,6 +75,8 @@ export class AppComponent implements OnInit {
 | 
			
		||||
 | 
			
		||||
    this.setupTranslate();
 | 
			
		||||
    this.setupAuth();
 | 
			
		||||
 | 
			
		||||
    initCustomJQueryEvents();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  setupTranslate() {
 | 
			
		||||
 | 
			
		||||
@ -27,7 +27,7 @@
 | 
			
		||||
                [class.center-vertical]="centerVertical"
 | 
			
		||||
                [class.center-horizontal]="centerHorizontal"
 | 
			
		||||
                (mousedown)="onDashboardMouseDown($event)"
 | 
			
		||||
                (contextmenu)="openDashboardContextMenu($event)">
 | 
			
		||||
                (tbcontextmenu)="openDashboardContextMenu($event)">
 | 
			
		||||
  <div #dashboardMenuTrigger="matMenuTrigger" style="visibility: hidden; position: fixed"
 | 
			
		||||
       [style.left]="dashboardMenuPosition.x"
 | 
			
		||||
       [style.top]="dashboardMenuPosition.y"
 | 
			
		||||
 | 
			
		||||
@ -58,6 +58,7 @@ import { WidgetComponentAction, WidgetComponentActionType } from '@home/componen
 | 
			
		||||
import { TbPopoverComponent } from '@shared/components/popover.component';
 | 
			
		||||
import { displayGrids } from 'angular-gridster2/lib/gridsterConfig.interface';
 | 
			
		||||
import { coerceBoolean } from '@shared/decorators/coercion';
 | 
			
		||||
import { TbContextMenuEvent } from '@shared/models/jquery-event.models';
 | 
			
		||||
 | 
			
		||||
@Component({
 | 
			
		||||
  selector: 'tb-dashboard',
 | 
			
		||||
@ -187,13 +188,13 @@ export class DashboardComponent extends PageComponent implements IDashboardCompo
 | 
			
		||||
 | 
			
		||||
  dashboardMenuPosition = { x: '0px', y: '0px' };
 | 
			
		||||
 | 
			
		||||
  dashboardContextMenuEvent: MouseEvent;
 | 
			
		||||
  dashboardContextMenuEvent: TbContextMenuEvent;
 | 
			
		||||
 | 
			
		||||
  @ViewChild('widgetMenuTrigger', {static: true}) widgetMenuTrigger: MatMenuTrigger;
 | 
			
		||||
 | 
			
		||||
  widgetMenuPosition = { x: '0px', y: '0px' };
 | 
			
		||||
 | 
			
		||||
  widgetContextMenuEvent: MouseEvent;
 | 
			
		||||
  widgetContextMenuEvent: TbContextMenuEvent;
 | 
			
		||||
 | 
			
		||||
  dashboardWidgets = new DashboardWidgets(this,
 | 
			
		||||
    this.differs.find([]).create<Widget>((_, item) => item),
 | 
			
		||||
@ -395,7 +396,7 @@ export class DashboardComponent extends PageComponent implements IDashboardCompo
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  openDashboardContextMenu($event: MouseEvent) {
 | 
			
		||||
  openDashboardContextMenu($event: TbContextMenuEvent) {
 | 
			
		||||
    if (this.callbacks && this.callbacks.prepareDashboardContextMenu) {
 | 
			
		||||
      const items = this.callbacks.prepareDashboardContextMenu($event);
 | 
			
		||||
      if (items && items.length) {
 | 
			
		||||
@ -410,7 +411,7 @@ export class DashboardComponent extends PageComponent implements IDashboardCompo
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private openWidgetContextMenu($event: MouseEvent, widget: DashboardWidget) {
 | 
			
		||||
  private openWidgetContextMenu($event: TbContextMenuEvent, widget: DashboardWidget) {
 | 
			
		||||
    if (this.callbacks && this.callbacks.prepareWidgetContextMenu) {
 | 
			
		||||
      const items = this.callbacks.prepareWidgetContextMenu($event, widget.widget, widget.isReference);
 | 
			
		||||
      if (items && items.length) {
 | 
			
		||||
 | 
			
		||||
@ -16,20 +16,9 @@
 | 
			
		||||
 | 
			
		||||
-->
 | 
			
		||||
<ng-container [formGroup]="rpcParametersFormGroup">
 | 
			
		||||
  <div fxFlex fxLayout="row">
 | 
			
		||||
    <mat-form-field fxFlex="100">
 | 
			
		||||
      <mat-label>{{ 'gateway.key' | translate }}</mat-label>
 | 
			
		||||
      <input matInput name="value" formControlName="tag" placeholder="{{ 'gateway.set' | translate }}"/>
 | 
			
		||||
      <mat-icon matSuffix
 | 
			
		||||
                matTooltipPosition="above"
 | 
			
		||||
                matTooltipClass="tb-error-tooltip"
 | 
			
		||||
                [matTooltip]="('gateway.key-required') | translate"
 | 
			
		||||
                *ngIf="rpcParametersFormGroup.get('tag').hasError('required') &&
 | 
			
		||||
                                           rpcParametersFormGroup.get('tag').touched"
 | 
			
		||||
                class="tb-error">
 | 
			
		||||
        warning
 | 
			
		||||
      </mat-icon>
 | 
			
		||||
    </mat-form-field>
 | 
			
		||||
  <div class="tb-form-hint tb-primary-fill tb-flex no-padding-top hint-container">
 | 
			
		||||
    {{ 'gateway.rpc.hint.modbus-response-reading' | translate }}<br>
 | 
			
		||||
    {{ 'gateway.rpc.hint.modbus-writing-functions' | translate }}
 | 
			
		||||
  </div>
 | 
			
		||||
  <div fxFlex fxLayout="row" fxLayoutGap="10px">
 | 
			
		||||
    <mat-form-field fxFlex="50" class="mat-block">
 | 
			
		||||
@ -45,21 +34,6 @@
 | 
			
		||||
      </mat-select>
 | 
			
		||||
    </mat-form-field>
 | 
			
		||||
  </div>
 | 
			
		||||
  <div fxFlex fxLayout="row">
 | 
			
		||||
    <mat-form-field fxFlex="100" *ngIf="writeFunctionCodes.includes(rpcParametersFormGroup.get('functionCode').value)">
 | 
			
		||||
      <mat-label>{{ 'gateway.rpc.value' | translate }}</mat-label>
 | 
			
		||||
      <input matInput name="value" formControlName="value" placeholder="{{ 'gateway.set' | translate }}"/>
 | 
			
		||||
      <mat-icon matSuffix
 | 
			
		||||
                matTooltipPosition="above"
 | 
			
		||||
                matTooltipClass="tb-error-tooltip"
 | 
			
		||||
                [matTooltip]="('gateway.value-required') | translate"
 | 
			
		||||
                *ngIf="rpcParametersFormGroup.get('value').hasError('required') &&
 | 
			
		||||
                                           rpcParametersFormGroup.get('value').touched"
 | 
			
		||||
                class="tb-error">
 | 
			
		||||
        warning
 | 
			
		||||
      </mat-icon>
 | 
			
		||||
    </mat-form-field>
 | 
			
		||||
  </div>
 | 
			
		||||
  <div fxFlex fxLayout="row" fxLayoutGap="10px">
 | 
			
		||||
    <mat-form-field fxFlex="50">
 | 
			
		||||
      <mat-label>{{ 'gateway.rpc.address' | translate }}</mat-label>
 | 
			
		||||
@ -88,5 +62,20 @@
 | 
			
		||||
      />
 | 
			
		||||
    </mat-form-field>
 | 
			
		||||
  </div>
 | 
			
		||||
  <div *ngIf="writeFunctionCodes.includes(rpcParametersFormGroup.get('functionCode').value)" fxFlex fxLayout="row">
 | 
			
		||||
    <mat-form-field fxFlex="100">
 | 
			
		||||
      <mat-label>{{ 'gateway.rpc.value' | translate }}</mat-label>
 | 
			
		||||
      <input matInput name="value" formControlName="value" placeholder="{{ 'gateway.set' | translate }}"/>
 | 
			
		||||
      <mat-icon matSuffix
 | 
			
		||||
                matTooltipPosition="above"
 | 
			
		||||
                matTooltipClass="tb-error-tooltip"
 | 
			
		||||
                [matTooltip]="('gateway.value-required') | translate"
 | 
			
		||||
                *ngIf="rpcParametersFormGroup.get('value').hasError('required') && rpcParametersFormGroup.get('value').touched"
 | 
			
		||||
                class="tb-error"
 | 
			
		||||
      >
 | 
			
		||||
        warning
 | 
			
		||||
      </mat-icon>
 | 
			
		||||
    </mat-form-field>
 | 
			
		||||
  </div>
 | 
			
		||||
</ng-container>
 | 
			
		||||
 | 
			
		||||
@ -0,0 +1,20 @@
 | 
			
		||||
/**
 | 
			
		||||
 * Copyright © 2016-2024 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.
 | 
			
		||||
 */
 | 
			
		||||
:host {
 | 
			
		||||
  .hint-container {
 | 
			
		||||
    margin-bottom: 12px;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
@ -39,13 +39,14 @@ import {
 | 
			
		||||
  ModbusEditableDataTypes,
 | 
			
		||||
  ModbusFunctionCodeTranslationsMap,
 | 
			
		||||
  ModbusObjectCountByDataType,
 | 
			
		||||
  ModbusValue,
 | 
			
		||||
  noLeadTrailSpacesRegex,
 | 
			
		||||
  RPCTemplateConfigModbus,
 | 
			
		||||
} from '@home/components/widget/lib/gateway/gateway-widget.models';
 | 
			
		||||
 | 
			
		||||
@Component({
 | 
			
		||||
  selector: 'tb-modbus-rpc-parameters',
 | 
			
		||||
  templateUrl: './modbus-rpc-parameters.component.html',
 | 
			
		||||
  styleUrls: ['./modbus-rpc-parameters.component.scss'],
 | 
			
		||||
  changeDetection: ChangeDetectionStrategy.OnPush,
 | 
			
		||||
  providers: [
 | 
			
		||||
    {
 | 
			
		||||
@ -80,14 +81,13 @@ export class ModbusRpcParametersComponent implements ControlValueAccessor, Valid
 | 
			
		||||
  private readonly readFunctionCodes = [1, 2, 3, 4];
 | 
			
		||||
  private readonly bitsFunctionCodes = [...this.readFunctionCodes, ...this.writeFunctionCodes];
 | 
			
		||||
 | 
			
		||||
  private onChange: (value: ModbusValue) => void;
 | 
			
		||||
  private onChange: (value: RPCTemplateConfigModbus) => void;
 | 
			
		||||
  private onTouched: () => void;
 | 
			
		||||
 | 
			
		||||
  private destroy$ = new Subject<void>();
 | 
			
		||||
 | 
			
		||||
  constructor(private fb: FormBuilder) {
 | 
			
		||||
    this.rpcParametersFormGroup = this.fb.group({
 | 
			
		||||
      tag: ['', [Validators.required, Validators.pattern(noLeadTrailSpacesRegex)]],
 | 
			
		||||
      type: [ModbusDataType.BYTES, [Validators.required]],
 | 
			
		||||
      functionCode: [this.defaultFunctionCodes[0], [Validators.required]],
 | 
			
		||||
      value: [{value: '', disabled: true}, [Validators.required, Validators.pattern(noLeadTrailSpacesRegex)]],
 | 
			
		||||
@ -106,7 +106,7 @@ export class ModbusRpcParametersComponent implements ControlValueAccessor, Valid
 | 
			
		||||
    this.destroy$.complete();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  registerOnChange(fn: (value: ModbusValue) => void): void {
 | 
			
		||||
  registerOnChange(fn: (value: RPCTemplateConfigModbus) => void): void {
 | 
			
		||||
    this.onChange = fn;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@ -120,7 +120,7 @@ export class ModbusRpcParametersComponent implements ControlValueAccessor, Valid
 | 
			
		||||
    };
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  writeValue(value: ModbusValue): void {
 | 
			
		||||
  writeValue(value: RPCTemplateConfigModbus): void {
 | 
			
		||||
    this.rpcParametersFormGroup.patchValue(value, {emitEvent: false});
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@ -0,0 +1,48 @@
 | 
			
		||||
<!--
 | 
			
		||||
 | 
			
		||||
    Copyright © 2016-2024 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.
 | 
			
		||||
 | 
			
		||||
-->
 | 
			
		||||
<ng-container [formGroup]="rpcParametersFormGroup">
 | 
			
		||||
  <mat-form-field>
 | 
			
		||||
    <mat-label>{{ 'gateway.rpc.method-name' | translate }}</mat-label>
 | 
			
		||||
    <input matInput formControlName="methodFilter"
 | 
			
		||||
           placeholder="echo"/>
 | 
			
		||||
  </mat-form-field>
 | 
			
		||||
  <mat-form-field>
 | 
			
		||||
    <mat-label>{{ 'gateway.rpc.requestTopicExpression' | translate }}</mat-label>
 | 
			
		||||
    <input matInput formControlName="requestTopicExpression"
 | 
			
		||||
           placeholder="sensor/${deviceName}/request/${methodName}/${requestId}"/>
 | 
			
		||||
  </mat-form-field>
 | 
			
		||||
  <mat-slide-toggle class="margin" (click)="$event.stopPropagation()" formControlName="withResponse">
 | 
			
		||||
    {{ 'gateway.rpc.withResponse' | translate }}
 | 
			
		||||
  </mat-slide-toggle>
 | 
			
		||||
  <mat-form-field *ngIf="rpcParametersFormGroup.get('withResponse')?.value">
 | 
			
		||||
    <mat-label>{{ 'gateway.rpc.responseTopicExpression' | translate }}</mat-label>
 | 
			
		||||
    <input matInput formControlName="responseTopicExpression"
 | 
			
		||||
           placeholder="sensor/${deviceName}/response/${methodName}/${requestId}"/>
 | 
			
		||||
  </mat-form-field>
 | 
			
		||||
  <mat-form-field *ngIf="rpcParametersFormGroup.get('withResponse')?.value">
 | 
			
		||||
    <mat-label>{{ 'gateway.rpc.responseTimeout' | translate }}</mat-label>
 | 
			
		||||
    <input matInput formControlName="responseTimeout" type="number"
 | 
			
		||||
           placeholder="10000" min="10" step="1"/>
 | 
			
		||||
  </mat-form-field>
 | 
			
		||||
  <mat-form-field>
 | 
			
		||||
    <mat-label>{{ 'gateway.rpc.valueExpression' | translate }}</mat-label>
 | 
			
		||||
    <input matInput formControlName="valueExpression"
 | 
			
		||||
           placeholder="${params}"/>
 | 
			
		||||
  </mat-form-field>
 | 
			
		||||
</ng-container>
 | 
			
		||||
 | 
			
		||||
@ -0,0 +1,24 @@
 | 
			
		||||
/**
 | 
			
		||||
 * Copyright © 2016-2024 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.
 | 
			
		||||
 */
 | 
			
		||||
:host {
 | 
			
		||||
  display: flex;
 | 
			
		||||
  flex-direction: column;
 | 
			
		||||
 | 
			
		||||
  .mat-mdc-slide-toggle.margin {
 | 
			
		||||
    margin-bottom: 10px;
 | 
			
		||||
    margin-left: 10px;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,139 @@
 | 
			
		||||
///
 | 
			
		||||
/// Copyright © 2016-2024 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 {
 | 
			
		||||
  ChangeDetectionStrategy,
 | 
			
		||||
  Component,
 | 
			
		||||
  forwardRef,
 | 
			
		||||
  OnDestroy,
 | 
			
		||||
} from '@angular/core';
 | 
			
		||||
import {
 | 
			
		||||
  ControlValueAccessor,
 | 
			
		||||
  FormBuilder,
 | 
			
		||||
  NG_VALIDATORS,
 | 
			
		||||
  NG_VALUE_ACCESSOR,
 | 
			
		||||
  UntypedFormGroup,
 | 
			
		||||
  ValidationErrors,
 | 
			
		||||
  Validator, Validators,
 | 
			
		||||
} from '@angular/forms';
 | 
			
		||||
import { SharedModule } from '@shared/shared.module';
 | 
			
		||||
import { CommonModule } from '@angular/common';
 | 
			
		||||
import { Subject } from 'rxjs';
 | 
			
		||||
import { takeUntil, tap } from 'rxjs/operators';
 | 
			
		||||
import {
 | 
			
		||||
  integerRegex,
 | 
			
		||||
  noLeadTrailSpacesRegex,
 | 
			
		||||
  RPCTemplateConfigMQTT
 | 
			
		||||
} from '@home/components/widget/lib/gateway/gateway-widget.models';
 | 
			
		||||
 | 
			
		||||
@Component({
 | 
			
		||||
  selector: 'tb-mqtt-rpc-parameters',
 | 
			
		||||
  templateUrl: './mqtt-rpc-parameters.component.html',
 | 
			
		||||
  styleUrls: ['./mqtt-rpc-parameters.component.scss'],
 | 
			
		||||
  changeDetection: ChangeDetectionStrategy.OnPush,
 | 
			
		||||
  providers: [
 | 
			
		||||
    {
 | 
			
		||||
      provide: NG_VALUE_ACCESSOR,
 | 
			
		||||
      useExisting: forwardRef(() => MqttRpcParametersComponent),
 | 
			
		||||
      multi: true
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      provide: NG_VALIDATORS,
 | 
			
		||||
      useExisting: forwardRef(() => MqttRpcParametersComponent),
 | 
			
		||||
      multi: true
 | 
			
		||||
    }
 | 
			
		||||
  ],
 | 
			
		||||
  standalone: true,
 | 
			
		||||
  imports: [
 | 
			
		||||
    CommonModule,
 | 
			
		||||
    SharedModule,
 | 
			
		||||
  ],
 | 
			
		||||
})
 | 
			
		||||
export class MqttRpcParametersComponent implements ControlValueAccessor, Validator, OnDestroy {
 | 
			
		||||
 | 
			
		||||
  rpcParametersFormGroup: UntypedFormGroup;
 | 
			
		||||
 | 
			
		||||
  private onChange: (value: RPCTemplateConfigMQTT) => void = (_) => {};
 | 
			
		||||
  private onTouched: () => void = () => {};
 | 
			
		||||
 | 
			
		||||
  private destroy$ = new Subject<void>();
 | 
			
		||||
 | 
			
		||||
  constructor(private fb: FormBuilder) {
 | 
			
		||||
    this.rpcParametersFormGroup = this.fb.group({
 | 
			
		||||
      methodFilter: [null, [Validators.required, Validators.pattern(noLeadTrailSpacesRegex)]],
 | 
			
		||||
      requestTopicExpression: [null, [Validators.required, Validators.pattern(noLeadTrailSpacesRegex)]],
 | 
			
		||||
      responseTopicExpression: [{ value: null, disabled: true }, [Validators.required, Validators.pattern(noLeadTrailSpacesRegex)]],
 | 
			
		||||
      responseTimeout: [{ value: null, disabled: true }, [Validators.min(10), Validators.pattern(integerRegex)]],
 | 
			
		||||
      valueExpression: [null, [Validators.required, Validators.pattern(noLeadTrailSpacesRegex)]],
 | 
			
		||||
      withResponse: [false, []],
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    this.observeValueChanges();
 | 
			
		||||
    this.observeWithResponse();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  ngOnDestroy(): void {
 | 
			
		||||
    this.destroy$.next();
 | 
			
		||||
    this.destroy$.complete();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  registerOnChange(fn: (value: RPCTemplateConfigMQTT) => void): void {
 | 
			
		||||
    this.onChange = fn;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  registerOnTouched(fn: () => void): void {
 | 
			
		||||
    this.onTouched = fn;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  validate(): ValidationErrors | null {
 | 
			
		||||
    return this.rpcParametersFormGroup.valid ? null : {
 | 
			
		||||
      rpcParametersFormGroup: { valid: false }
 | 
			
		||||
    };
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  writeValue(value: RPCTemplateConfigMQTT): void {
 | 
			
		||||
    this.rpcParametersFormGroup.patchValue(value, {emitEvent: false});
 | 
			
		||||
    this.toggleResponseFields(value.withResponse);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private observeValueChanges(): void {
 | 
			
		||||
    this.rpcParametersFormGroup.valueChanges.pipe(
 | 
			
		||||
      takeUntil(this.destroy$)
 | 
			
		||||
    ).subscribe((value) => {
 | 
			
		||||
      this.onChange(value);
 | 
			
		||||
      this.onTouched();
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private observeWithResponse(): void {
 | 
			
		||||
    this.rpcParametersFormGroup.get('withResponse').valueChanges.pipe(
 | 
			
		||||
      tap((isActive: boolean) => this.toggleResponseFields(isActive)),
 | 
			
		||||
      takeUntil(this.destroy$),
 | 
			
		||||
    ).subscribe();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private toggleResponseFields(enabled: boolean): void {
 | 
			
		||||
    const responseTopicControl = this.rpcParametersFormGroup.get('responseTopicExpression');
 | 
			
		||||
    const responseTimeoutControl = this.rpcParametersFormGroup.get('responseTimeout');
 | 
			
		||||
    if (enabled) {
 | 
			
		||||
      responseTopicControl.enable();
 | 
			
		||||
      responseTimeoutControl.enable();
 | 
			
		||||
    } else {
 | 
			
		||||
      responseTopicControl.disable();
 | 
			
		||||
      responseTimeoutControl.disable();
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,93 @@
 | 
			
		||||
<!--
 | 
			
		||||
 | 
			
		||||
    Copyright © 2016-2024 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.
 | 
			
		||||
 | 
			
		||||
-->
 | 
			
		||||
<ng-container [formGroup]="rpcParametersFormGroup">
 | 
			
		||||
  <div class="tb-form-hint tb-primary-fill tb-flex no-padding-top hint-container">
 | 
			
		||||
    {{ 'gateway.rpc.hint.opc-method' | translate }}
 | 
			
		||||
  </div>
 | 
			
		||||
  <mat-form-field class="tb-flex">
 | 
			
		||||
    <mat-label>{{ 'gateway.rpc.method' | translate }}</mat-label>
 | 
			
		||||
    <input matInput formControlName="method" placeholder="multiply"/>
 | 
			
		||||
  </mat-form-field>
 | 
			
		||||
  <fieldset class="tb-form-panel stroked arguments-container" fxLayout="column" formArrayName="arguments">
 | 
			
		||||
    <strong>
 | 
			
		||||
      <span class="fields-label">{{ 'gateway.rpc.arguments' | translate }}</span>
 | 
			
		||||
    </strong>
 | 
			
		||||
    <div fxFlex fxLayout="row" fxLayoutGap="10px" fxLayoutAlign="center center"
 | 
			
		||||
         *ngFor="let argumentFormGroup of rpcParametersFormGroup.get('arguments')['controls']; let i = index" [formGroup]="argumentFormGroup">
 | 
			
		||||
      <div class="tb-form-row column-xs type-container" fxLayoutAlign="space-between center">
 | 
			
		||||
        <div class="tb-required" translate>gateway.type</div>
 | 
			
		||||
        <div class="tb-flex no-gap">
 | 
			
		||||
          <mat-form-field class="tb-flex no-gap fill-width" appearance="outline" subscriptSizing="dynamic">
 | 
			
		||||
            <mat-select formControlName="type">
 | 
			
		||||
              <mat-select-trigger>
 | 
			
		||||
                <div class="tb-flex align-center">
 | 
			
		||||
                  <mat-icon class="tb-mat-18" [svgIcon]="valueTypes.get(argumentFormGroup.get('type').value)?.icon">
 | 
			
		||||
                  </mat-icon>
 | 
			
		||||
                  <span>{{ valueTypes.get(argumentFormGroup.get('type').value)?.name | translate }}</span>
 | 
			
		||||
                </div>
 | 
			
		||||
              </mat-select-trigger>
 | 
			
		||||
              <mat-option *ngFor="let valueType of valueTypeKeys" [value]="valueType">
 | 
			
		||||
                <mat-icon class="tb-mat-20" svgIcon="{{ valueTypes.get(valueType).icon }}">
 | 
			
		||||
                </mat-icon>
 | 
			
		||||
                <span>{{ valueTypes.get(valueType).name | translate }}</span>
 | 
			
		||||
              </mat-option>
 | 
			
		||||
            </mat-select>
 | 
			
		||||
          </mat-form-field>
 | 
			
		||||
        </div>
 | 
			
		||||
      </div>
 | 
			
		||||
      <div class="tb-form-row column-xs value-container" fxLayoutAlign="space-between center">
 | 
			
		||||
        <div class="tb-required" translate>gateway.value</div>
 | 
			
		||||
        <mat-form-field fxFlex appearance="outline" subscriptSizing="dynamic" class="tb-inline-field flex tb-suffix-absolute">
 | 
			
		||||
          <ng-container [ngSwitch]="argumentFormGroup.get('type').value">
 | 
			
		||||
            <input *ngSwitchCase="MappingValueType.STRING" matInput required formControlName="string"
 | 
			
		||||
                   placeholder="{{ 'gateway.set' | translate }}" />
 | 
			
		||||
            <input *ngSwitchCase="MappingValueType.INTEGER" matInput required formControlName="integer" type="number"
 | 
			
		||||
                   placeholder="{{ 'gateway.set' | translate }}" />
 | 
			
		||||
            <input *ngSwitchCase="MappingValueType.DOUBLE" matInput required formControlName="double" type="number"
 | 
			
		||||
                   placeholder="{{ 'gateway.set' | translate }}" />
 | 
			
		||||
            <mat-select *ngSwitchCase="MappingValueType.BOOLEAN" formControlName="boolean">
 | 
			
		||||
              <mat-option [value]="true">true</mat-option>
 | 
			
		||||
              <mat-option [value]="false">false</mat-option>
 | 
			
		||||
            </mat-select>
 | 
			
		||||
          </ng-container>
 | 
			
		||||
          <mat-icon matSuffix
 | 
			
		||||
                    matTooltipPosition="above"
 | 
			
		||||
                    matTooltipClass="tb-error-tooltip"
 | 
			
		||||
                    [matTooltip]="('gateway.value-required') | translate"
 | 
			
		||||
                    *ngIf="argumentFormGroup.get(argumentFormGroup.get('type').value).hasError('required')
 | 
			
		||||
                              && argumentFormGroup.get(argumentFormGroup.get('type').value).touched"
 | 
			
		||||
                    class="tb-error">
 | 
			
		||||
            warning
 | 
			
		||||
          </mat-icon>
 | 
			
		||||
        </mat-form-field>
 | 
			
		||||
      </div>
 | 
			
		||||
      <button mat-icon-button (click)="removeArgument(i)"
 | 
			
		||||
              class="tb-box-button"
 | 
			
		||||
              matTooltip="{{ 'gateway.rpc.remove' | translate }}"
 | 
			
		||||
              matTooltipPosition="above">
 | 
			
		||||
        <mat-icon>delete</mat-icon>
 | 
			
		||||
      </button>
 | 
			
		||||
    </div>
 | 
			
		||||
    <button mat-raised-button
 | 
			
		||||
            fxFlexAlign="start"
 | 
			
		||||
            (click)="addArgument()">
 | 
			
		||||
      {{ 'gateway.rpc.add-argument' | translate }}
 | 
			
		||||
    </button>
 | 
			
		||||
  </fieldset>
 | 
			
		||||
</ng-container>
 | 
			
		||||
 | 
			
		||||
@ -0,0 +1,32 @@
 | 
			
		||||
/**
 | 
			
		||||
 * Copyright © 2016-2024 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.
 | 
			
		||||
 */
 | 
			
		||||
:host {
 | 
			
		||||
  .arguments-container {
 | 
			
		||||
    margin-bottom: 10px;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  .type-container {
 | 
			
		||||
    width: 40%;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  .value-container {
 | 
			
		||||
    width: 50%;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  .hint-container {
 | 
			
		||||
    margin-bottom: 12px;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,169 @@
 | 
			
		||||
///
 | 
			
		||||
/// Copyright © 2016-2024 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 {
 | 
			
		||||
  ChangeDetectionStrategy,
 | 
			
		||||
  ChangeDetectorRef,
 | 
			
		||||
  Component,
 | 
			
		||||
  forwardRef,
 | 
			
		||||
  OnDestroy,
 | 
			
		||||
} from '@angular/core';
 | 
			
		||||
import {
 | 
			
		||||
  ControlValueAccessor,
 | 
			
		||||
  FormArray,
 | 
			
		||||
  FormBuilder,
 | 
			
		||||
  FormGroup,
 | 
			
		||||
  NG_VALIDATORS,
 | 
			
		||||
  NG_VALUE_ACCESSOR,
 | 
			
		||||
  UntypedFormGroup,
 | 
			
		||||
  ValidationErrors,
 | 
			
		||||
  Validator, Validators,
 | 
			
		||||
} from '@angular/forms';
 | 
			
		||||
import { SharedModule } from '@shared/shared.module';
 | 
			
		||||
import { CommonModule } from '@angular/common';
 | 
			
		||||
import { Subject } from 'rxjs';
 | 
			
		||||
import { takeUntil } from 'rxjs/operators';
 | 
			
		||||
import {
 | 
			
		||||
  integerRegex,
 | 
			
		||||
  MappingValueType,
 | 
			
		||||
  mappingValueTypesMap,
 | 
			
		||||
  noLeadTrailSpacesRegex,
 | 
			
		||||
  OPCTypeValue,
 | 
			
		||||
  RPCTemplateConfigOPC
 | 
			
		||||
} from '@home/components/widget/lib/gateway/gateway-widget.models';
 | 
			
		||||
import { isDefinedAndNotNull, isEqual } from '@core/utils';
 | 
			
		||||
 | 
			
		||||
@Component({
 | 
			
		||||
  selector: 'tb-opc-rpc-parameters',
 | 
			
		||||
  templateUrl: './opc-rpc-parameters.component.html',
 | 
			
		||||
  styleUrls: ['./opc-rpc-parameters.component.scss'],
 | 
			
		||||
  changeDetection: ChangeDetectionStrategy.OnPush,
 | 
			
		||||
  providers: [
 | 
			
		||||
    {
 | 
			
		||||
      provide: NG_VALUE_ACCESSOR,
 | 
			
		||||
      useExisting: forwardRef(() => OpcRpcParametersComponent),
 | 
			
		||||
      multi: true
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      provide: NG_VALIDATORS,
 | 
			
		||||
      useExisting: forwardRef(() => OpcRpcParametersComponent),
 | 
			
		||||
      multi: true
 | 
			
		||||
    }
 | 
			
		||||
  ],
 | 
			
		||||
  standalone: true,
 | 
			
		||||
  imports: [
 | 
			
		||||
    CommonModule,
 | 
			
		||||
    SharedModule,
 | 
			
		||||
  ],
 | 
			
		||||
})
 | 
			
		||||
export class OpcRpcParametersComponent implements ControlValueAccessor, Validator, OnDestroy {
 | 
			
		||||
 | 
			
		||||
  rpcParametersFormGroup: UntypedFormGroup;
 | 
			
		||||
 | 
			
		||||
  readonly valueTypeKeys: MappingValueType[] = Object.values(MappingValueType);
 | 
			
		||||
  readonly MappingValueType = MappingValueType;
 | 
			
		||||
  readonly valueTypes = mappingValueTypesMap;
 | 
			
		||||
 | 
			
		||||
  private onChange: (value: RPCTemplateConfigOPC) => void = (_) => {} ;
 | 
			
		||||
  private onTouched: () => void = () => {};
 | 
			
		||||
 | 
			
		||||
  private destroy$ = new Subject<void>();
 | 
			
		||||
 | 
			
		||||
  constructor(private fb: FormBuilder, private cdr: ChangeDetectorRef) {
 | 
			
		||||
    this.rpcParametersFormGroup = this.fb.group({
 | 
			
		||||
      method: [null, [Validators.required, Validators.pattern(noLeadTrailSpacesRegex)]],
 | 
			
		||||
      arguments: this.fb.array([]),
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    this.observeValueChanges();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  ngOnDestroy(): void {
 | 
			
		||||
    this.destroy$.next();
 | 
			
		||||
    this.destroy$.complete();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  registerOnChange(fn: (value: RPCTemplateConfigOPC) => void): void {
 | 
			
		||||
    this.onChange = fn;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  registerOnTouched(fn: () => void): void {
 | 
			
		||||
    this.onTouched = fn;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  validate(): ValidationErrors | null {
 | 
			
		||||
    return this.rpcParametersFormGroup.valid ? null : {
 | 
			
		||||
      rpcParametersFormGroup: { valid: false }
 | 
			
		||||
    };
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  writeValue(params: RPCTemplateConfigOPC): void {
 | 
			
		||||
    this.clearArguments();
 | 
			
		||||
    params.arguments?.map(({type, value}) => ({type, [type]: value }))
 | 
			
		||||
      .forEach(argument => this.addArgument(argument as OPCTypeValue));
 | 
			
		||||
    this.cdr.markForCheck();
 | 
			
		||||
    this.rpcParametersFormGroup.get('method').patchValue(params.method);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private observeValueChanges(): void {
 | 
			
		||||
    this.rpcParametersFormGroup.valueChanges.pipe(
 | 
			
		||||
      takeUntil(this.destroy$)
 | 
			
		||||
    ).subscribe(params => {
 | 
			
		||||
      const updatedArguments = params.arguments.map(({type, ...config}) => ({type, value: config[type]}));
 | 
			
		||||
      this.onChange({method: params.method, arguments: updatedArguments});
 | 
			
		||||
      this.onTouched();
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  removeArgument(index: number): void {
 | 
			
		||||
    (this.rpcParametersFormGroup.get('arguments') as FormArray).removeAt(index);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  addArgument(value: OPCTypeValue = {} as OPCTypeValue): void {
 | 
			
		||||
    const fromGroup = this.fb.group({
 | 
			
		||||
      type: [value.type ?? MappingValueType.STRING],
 | 
			
		||||
      string: [
 | 
			
		||||
        value.string ?? { value: '', disabled: !(isEqual(value, {}) || value.string)},
 | 
			
		||||
        [Validators.required, Validators.pattern(noLeadTrailSpacesRegex)]
 | 
			
		||||
      ],
 | 
			
		||||
      integer: [
 | 
			
		||||
        {value: value.integer ?? 0, disabled: !isDefinedAndNotNull(value.integer)},
 | 
			
		||||
        [Validators.required, Validators.pattern(integerRegex)]
 | 
			
		||||
      ],
 | 
			
		||||
      double: [{value: value.double ?? 0, disabled: !isDefinedAndNotNull(value.double)}, [Validators.required]],
 | 
			
		||||
      boolean: [{value: value.boolean ?? false, disabled: !isDefinedAndNotNull(value.boolean)}, [Validators.required]],
 | 
			
		||||
    });
 | 
			
		||||
    this.observeTypeChange(fromGroup);
 | 
			
		||||
    (this.rpcParametersFormGroup.get('arguments') as FormArray).push(fromGroup, {emitEvent: false});
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  clearArguments(): void {
 | 
			
		||||
    const formArray = this.rpcParametersFormGroup.get('arguments') as FormArray;
 | 
			
		||||
    while (formArray.length !== 0) {
 | 
			
		||||
      formArray.removeAt(0);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private observeTypeChange(dataKeyFormGroup: FormGroup): void {
 | 
			
		||||
    dataKeyFormGroup.get('type').valueChanges
 | 
			
		||||
      .pipe(takeUntil(this.destroy$))
 | 
			
		||||
      .subscribe(type => {
 | 
			
		||||
        dataKeyFormGroup.disable({emitEvent: false});
 | 
			
		||||
        dataKeyFormGroup.get('type').enable({emitEvent: false});
 | 
			
		||||
        dataKeyFormGroup.get(type).enable({emitEvent: false});
 | 
			
		||||
      });
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
@ -70,8 +70,8 @@
 | 
			
		||||
                              matTooltipPosition="above"
 | 
			
		||||
                              matTooltipClass="tb-error-tooltip"
 | 
			
		||||
                              [matTooltip]="('gateway.value-required') | translate"
 | 
			
		||||
                              *ngIf="keyControl.get(keyControl.get('type').value).hasError('required')
 | 
			
		||||
                              && keyControl.get(keyControl.get('type').value).touched"
 | 
			
		||||
                              *ngIf="keyControl.get(keyControl.get('value').value).hasError('required')
 | 
			
		||||
                              && keyControl.get(keyControl.get('value').value).touched"
 | 
			
		||||
                              class="tb-error">
 | 
			
		||||
                      warning
 | 
			
		||||
                    </mat-icon>
 | 
			
		||||
 | 
			
		||||
@ -104,7 +104,7 @@ export class TypeValuePanelComponent implements ControlValueAccessor, Validator,
 | 
			
		||||
        dataKeyFormGroup.disable({emitEvent: false});
 | 
			
		||||
        dataKeyFormGroup.get('type').enable({emitEvent: false});
 | 
			
		||||
        dataKeyFormGroup.get(type).enable({emitEvent: false});
 | 
			
		||||
      })
 | 
			
		||||
      });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  deleteKey($event: Event, index: number): void {
 | 
			
		||||
 | 
			
		||||
@ -44,7 +44,11 @@
 | 
			
		||||
      <div class="template-key">
 | 
			
		||||
        {{!innerValue ? ('gateway.rpc.' + config.key | translate) : config.key}}
 | 
			
		||||
      </div>
 | 
			
		||||
      <div *ngIf="!isObject(config.value) else RPCObjectRow"
 | 
			
		||||
      <div *ngIf="isArray(config.value)" tbTruncateWithTooltip class="array-value">
 | 
			
		||||
        {{ config.value | getRpcTemplateArrayView }}
 | 
			
		||||
      </div>
 | 
			
		||||
      <ng-container *ngIf="isObject(config.value)" [ngTemplateOutlet]="RPCObjectRow"></ng-container>
 | 
			
		||||
      <div *ngIf="!isObject(config.value) && !isArray(config.value)"
 | 
			
		||||
           [ngClass]="{'boolean-true': config.value === true,
 | 
			
		||||
                   'boolean-false': config.value === false  }">
 | 
			
		||||
        <ng-container *ngIf="config.key === 'method' else value" [ngTemplateOutlet]="SNMPMethod"></ng-container>
 | 
			
		||||
 | 
			
		||||
@ -104,6 +104,10 @@
 | 
			
		||||
    flex: 1;
 | 
			
		||||
    margin: 0;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  .array-value {
 | 
			
		||||
    margin-left: 10px;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -49,7 +49,8 @@ export class GatewayServiceRPCConnectorTemplatesComponent implements OnInit {
 | 
			
		||||
  rpcTemplates: Array<RPCTemplate>;
 | 
			
		||||
 | 
			
		||||
  public readonly originalOrder = (): number => 0;
 | 
			
		||||
  public readonly isObject = (value: any) => isLiteralObject(value);
 | 
			
		||||
  public readonly isObject = (value: unknown) => isLiteralObject(value);
 | 
			
		||||
  public readonly isArray = (value: unknown) => Array.isArray(value);
 | 
			
		||||
  public readonly SNMPMethodsTranslations = SNMPMethodsTranslations;
 | 
			
		||||
 | 
			
		||||
  constructor(private attributeService: AttributeService) {
 | 
			
		||||
 | 
			
		||||
@ -20,36 +20,6 @@
 | 
			
		||||
    class="mat-subtitle-1 title">{{ 'gateway.rpc.title' | translate: {type: gatewayConnectorDefaultTypesTranslates.get(connectorType)} }}</div>
 | 
			
		||||
  <ng-template [ngIf]="connectorType">
 | 
			
		||||
    <ng-container [ngSwitch]="connectorType">
 | 
			
		||||
      <ng-template [ngSwitchCase]="ConnectorType.MQTT">
 | 
			
		||||
        <mat-form-field>
 | 
			
		||||
          <mat-label>{{ 'gateway.rpc.methodFilter' | translate }}</mat-label>
 | 
			
		||||
          <input matInput formControlName="methodFilter"
 | 
			
		||||
                 placeholder="echo"/>
 | 
			
		||||
        </mat-form-field>
 | 
			
		||||
        <mat-form-field>
 | 
			
		||||
          <mat-label>{{ 'gateway.rpc.requestTopicExpression' | translate }}</mat-label>
 | 
			
		||||
          <input matInput formControlName="requestTopicExpression"
 | 
			
		||||
                 placeholder="sensor/${deviceName}/request/${methodName}/${requestId}"/>
 | 
			
		||||
        </mat-form-field>
 | 
			
		||||
        <mat-slide-toggle class="margin" (click)="$event.stopPropagation()" formControlName="withResponse">
 | 
			
		||||
          {{ 'gateway.rpc.withResponse' | translate }}
 | 
			
		||||
        </mat-slide-toggle>
 | 
			
		||||
        <mat-form-field *ngIf="commandForm.get('withResponse')?.value">
 | 
			
		||||
          <mat-label>{{ 'gateway.rpc.responseTopicExpression' | translate }}</mat-label>
 | 
			
		||||
          <input matInput formControlName="responseTopicExpression"
 | 
			
		||||
                 placeholder="sensor/${deviceName}/response/${methodName}/${requestId}"/>
 | 
			
		||||
        </mat-form-field>
 | 
			
		||||
        <mat-form-field *ngIf="commandForm.get('withResponse')?.value">
 | 
			
		||||
          <mat-label>{{ 'gateway.rpc.responseTimeout' | translate }}</mat-label>
 | 
			
		||||
          <input matInput formControlName="responseTimeout" type="number"
 | 
			
		||||
                 placeholder="10000" min="10" step="1"/>
 | 
			
		||||
        </mat-form-field>
 | 
			
		||||
        <mat-form-field>
 | 
			
		||||
          <mat-label>{{ 'gateway.rpc.valueExpression' | translate }}</mat-label>
 | 
			
		||||
          <input matInput formControlName="valueExpression"
 | 
			
		||||
                 placeholder="${params}"/>
 | 
			
		||||
        </mat-form-field>
 | 
			
		||||
      </ng-template>
 | 
			
		||||
      <ng-template [ngSwitchCase]="ConnectorType.BACNET">
 | 
			
		||||
        <mat-form-field>
 | 
			
		||||
          <mat-label>{{ 'gateway.rpc.methodRPC' | translate }}</mat-label>
 | 
			
		||||
@ -407,31 +377,6 @@
 | 
			
		||||
          </button>
 | 
			
		||||
        </fieldset>
 | 
			
		||||
      </ng-template>
 | 
			
		||||
      <ng-template [ngSwitchCase]="ConnectorType.OPCUA" #OPCUAForm>
 | 
			
		||||
        <mat-form-field >
 | 
			
		||||
          <mat-label>{{ 'gateway.rpc.method' | translate }}</mat-label>
 | 
			
		||||
          <input matInput formControlName="method" placeholder="multiply"/>
 | 
			
		||||
        </mat-form-field>
 | 
			
		||||
        <fieldset class="fields border" fxLayout="column" fxLayoutGap="10px" formArrayName="arguments">
 | 
			
		||||
          <span class="fields-label">{{ 'gateway.rpc.arguments' | translate }}</span>
 | 
			
		||||
          <div fxFlex fxLayout="row" fxLayoutGap="10px" fxLayoutAlign="center center"
 | 
			
		||||
               *ngFor="let control of getFormArrayControls('arguments'); let i = index">
 | 
			
		||||
            <mat-form-field appearance="outline" fxFlex>
 | 
			
		||||
              <input matInput [formControl]="control" required/>
 | 
			
		||||
            </mat-form-field>
 | 
			
		||||
            <mat-icon style="cursor:pointer;"
 | 
			
		||||
                      fxFlex="30px"
 | 
			
		||||
                      (click)="removeOCPUAArguments(i)"
 | 
			
		||||
                      matTooltip="{{ 'gateway.rpc.remove' | translate }}">delete
 | 
			
		||||
            </mat-icon>
 | 
			
		||||
          </div>
 | 
			
		||||
          <button mat-raised-button
 | 
			
		||||
                  fxFlexAlign="start"
 | 
			
		||||
                  (click)="addOCPUAArguments()">
 | 
			
		||||
            {{ 'gateway.rpc.add-argument' | translate }}
 | 
			
		||||
          </button>
 | 
			
		||||
        </fieldset>
 | 
			
		||||
      </ng-template>
 | 
			
		||||
      <ng-template ngSwitchDefault>
 | 
			
		||||
        <mat-form-field>
 | 
			
		||||
          <mat-label>{{ 'gateway.statistics.command' | translate }}</mat-label>
 | 
			
		||||
 | 
			
		||||
@ -51,7 +51,6 @@ import {
 | 
			
		||||
} from '@shared/components/dialog/json-object-edit-dialog.component';
 | 
			
		||||
import { jsonRequired } from '@shared/components/json-object-edit.component';
 | 
			
		||||
import { deepClone } from '@core/utils';
 | 
			
		||||
import { takeUntil, tap } from "rxjs/operators";
 | 
			
		||||
import { Subject } from "rxjs";
 | 
			
		||||
 | 
			
		||||
@Component({
 | 
			
		||||
@ -129,7 +128,6 @@ export class GatewayServiceRPCConnectorComponent implements OnInit, OnDestroy, C
 | 
			
		||||
        this.propagateChange({...this.commandForm.value, ...value});
 | 
			
		||||
      }
 | 
			
		||||
    });
 | 
			
		||||
    this.observeMQTTWithResponse();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  ngOnDestroy(): void {
 | 
			
		||||
@ -141,16 +139,6 @@ export class GatewayServiceRPCConnectorComponent implements OnInit, OnDestroy, C
 | 
			
		||||
    let formGroup: FormGroup;
 | 
			
		||||
 | 
			
		||||
    switch (type) {
 | 
			
		||||
      case ConnectorType.MQTT:
 | 
			
		||||
        formGroup = this.fb.group({
 | 
			
		||||
          methodFilter: [null, [Validators.required, Validators.pattern(noLeadTrailSpacesRegex)]],
 | 
			
		||||
          requestTopicExpression: [null, [Validators.required, Validators.pattern(noLeadTrailSpacesRegex)]],
 | 
			
		||||
          responseTopicExpression: [{ value: null, disabled: true }, [Validators.required, Validators.pattern(noLeadTrailSpacesRegex)]],
 | 
			
		||||
          responseTimeout: [{ value: null, disabled: true }, [Validators.min(10), Validators.pattern(this.numbersOnlyPattern)]],
 | 
			
		||||
          valueExpression: [null, [Validators.required, Validators.pattern(noLeadTrailSpacesRegex)]],
 | 
			
		||||
          withResponse: [false, []],
 | 
			
		||||
        });
 | 
			
		||||
        break;
 | 
			
		||||
      case ConnectorType.BACNET:
 | 
			
		||||
        formGroup = this.fb.group({
 | 
			
		||||
          method: [null, [Validators.required, Validators.pattern(noLeadTrailSpacesRegex)]],
 | 
			
		||||
@ -246,12 +234,6 @@ export class GatewayServiceRPCConnectorComponent implements OnInit, OnDestroy, C
 | 
			
		||||
          httpHeaders: this.fb.array([]),
 | 
			
		||||
        })
 | 
			
		||||
        break;
 | 
			
		||||
      case ConnectorType.OPCUA:
 | 
			
		||||
        formGroup = this.fb.group({
 | 
			
		||||
          method: [null, [Validators.required, Validators.pattern(noLeadTrailSpacesRegex)]],
 | 
			
		||||
          arguments: this.fb.array([]),
 | 
			
		||||
        })
 | 
			
		||||
        break;
 | 
			
		||||
      default:
 | 
			
		||||
        formGroup = this.fb.group({
 | 
			
		||||
          command: [null, [Validators.required, Validators.pattern(noLeadTrailSpacesRegex)]],
 | 
			
		||||
@ -293,18 +275,6 @@ export class GatewayServiceRPCConnectorComponent implements OnInit, OnDestroy, C
 | 
			
		||||
    return (this.commandForm.get(path) as FormArray).controls as FormControl[];
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  addOCPUAArguments(value: string = null) {
 | 
			
		||||
    const oidsFA = this.commandForm.get('arguments') as FormArray;
 | 
			
		||||
    if (oidsFA) {
 | 
			
		||||
      oidsFA.push(this.fb.control(value, [Validators.required, Validators.pattern(noLeadTrailSpacesRegex)]), {emitEvent: false});
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  removeOCPUAArguments(index: number) {
 | 
			
		||||
    const oidsFA = this.commandForm.get('arguments') as FormArray;
 | 
			
		||||
    oidsFA.removeAt(index);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  openEditJSONDialog($event: Event) {
 | 
			
		||||
    if ($event) {
 | 
			
		||||
      $event.stopPropagation();
 | 
			
		||||
@ -368,34 +338,8 @@ export class GatewayServiceRPCConnectorComponent implements OnInit, OnDestroy, C
 | 
			
		||||
          })
 | 
			
		||||
          delete value.httpHeaders;
 | 
			
		||||
          break;
 | 
			
		||||
        case ConnectorType.OPCUA:
 | 
			
		||||
          this.clearFromArrayByName("arguments");
 | 
			
		||||
          value.arguments.forEach(value => {
 | 
			
		||||
            this.addOCPUAArguments(value)
 | 
			
		||||
          })
 | 
			
		||||
          delete value.arguments;
 | 
			
		||||
          break;
 | 
			
		||||
      }
 | 
			
		||||
      this.commandForm.patchValue(value, {onlySelf: false});
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private observeMQTTWithResponse(): void {
 | 
			
		||||
    if (this.connectorType === ConnectorType.MQTT) {
 | 
			
		||||
      this.commandForm.get('withResponse').valueChanges.pipe(
 | 
			
		||||
        tap((isActive: boolean) => {
 | 
			
		||||
          const responseTopicControl = this.commandForm.get('responseTopicExpression');
 | 
			
		||||
          const responseTimeoutControl = this.commandForm.get('responseTimeout');
 | 
			
		||||
          if (isActive) {
 | 
			
		||||
            responseTopicControl.enable();
 | 
			
		||||
            responseTimeoutControl.enable();
 | 
			
		||||
          } else {
 | 
			
		||||
            responseTopicControl.disable();
 | 
			
		||||
            responseTimeoutControl.disable();
 | 
			
		||||
          }
 | 
			
		||||
        }),
 | 
			
		||||
        takeUntil(this.destroy$),
 | 
			
		||||
      ).subscribe();
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -42,16 +42,20 @@
 | 
			
		||||
    </ng-container>
 | 
			
		||||
    <ng-template #connectorForm>
 | 
			
		||||
      <tb-gateway-service-rpc-connector
 | 
			
		||||
        *ngIf="connectorType !== ConnectorType.MODBUS else modbusParameters"
 | 
			
		||||
        *ngIf="!typesWithUpdatedParams.has(connectorType) else updatedParameters"
 | 
			
		||||
        formControlName="params"
 | 
			
		||||
        [connectorType]="connectorType"
 | 
			
		||||
        (sendCommand)="sendCommand()"
 | 
			
		||||
        (saveTemplate)="saveTemplate()"
 | 
			
		||||
      />
 | 
			
		||||
      <ng-template #modbusParameters>
 | 
			
		||||
      <ng-template #updatedParameters>
 | 
			
		||||
        <div fxLayout="column" class="rpc-parameters">
 | 
			
		||||
          <div class="mat-subtitle-1 tb-form-panel-title">{{ 'gateway.rpc.title' | translate: {type: gatewayConnectorDefaultTypesTranslates.get(connectorType)} }}</div>
 | 
			
		||||
          <tb-modbus-rpc-parameters formControlName="params"/>
 | 
			
		||||
          <ng-container [ngSwitch]="connectorType">
 | 
			
		||||
            <tb-modbus-rpc-parameters *ngSwitchCase="ConnectorType.MODBUS" formControlName="params"/>
 | 
			
		||||
            <tb-mqtt-rpc-parameters *ngSwitchCase="ConnectorType.MQTT" formControlName="params"/>
 | 
			
		||||
            <tb-opc-rpc-parameters *ngSwitchCase="ConnectorType.OPCUA" formControlName="params"/>
 | 
			
		||||
          </ng-container>
 | 
			
		||||
          <div class="template-actions" fxFlex fxLayout="row" fxLayoutAlign="end center" fxLayoutGap="10px">
 | 
			
		||||
            <button mat-raised-button
 | 
			
		||||
                    (click)="saveTemplate()"
 | 
			
		||||
 | 
			
		||||
@ -74,6 +74,11 @@ export class GatewayServiceRPCComponent implements OnInit {
 | 
			
		||||
 | 
			
		||||
  readonly ConnectorType = ConnectorType;
 | 
			
		||||
  readonly gatewayConnectorDefaultTypesTranslates = GatewayConnectorDefaultTypesTranslatesMap;
 | 
			
		||||
  readonly typesWithUpdatedParams = new Set<ConnectorType>([
 | 
			
		||||
    ConnectorType.MQTT,
 | 
			
		||||
    ConnectorType.OPCUA,
 | 
			
		||||
    ConnectorType.MODBUS,
 | 
			
		||||
  ]);
 | 
			
		||||
 | 
			
		||||
  private subscription: IWidgetSubscription;
 | 
			
		||||
  private subscriptionOptions: WidgetSubscriptionOptions = {
 | 
			
		||||
 | 
			
		||||
@ -297,7 +297,7 @@ export interface LegacyTimeseries {
 | 
			
		||||
 | 
			
		||||
export interface RpcArgument {
 | 
			
		||||
  type: string;
 | 
			
		||||
  value: number;
 | 
			
		||||
  value: number | string | boolean;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export interface RpcMethod {
 | 
			
		||||
@ -542,6 +542,37 @@ export interface RPCTemplateConfig {
 | 
			
		||||
  [key: string]: any;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export interface RPCTemplateConfigMQTT {
 | 
			
		||||
  methodFilter: string;
 | 
			
		||||
  requestTopicExpression: string;
 | 
			
		||||
  responseTopicExpression?: string;
 | 
			
		||||
  responseTimeout?: number;
 | 
			
		||||
  valueExpression: string;
 | 
			
		||||
  withResponse: boolean;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export interface RPCTemplateConfigModbus {
 | 
			
		||||
  tag: string;
 | 
			
		||||
  type: ModbusDataType;
 | 
			
		||||
  functionCode?: number;
 | 
			
		||||
  objectsCount: number;
 | 
			
		||||
  address: number;
 | 
			
		||||
  value?: string;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export interface RPCTemplateConfigOPC {
 | 
			
		||||
  method: string;
 | 
			
		||||
  arguments: RpcArgument[];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export interface OPCTypeValue {
 | 
			
		||||
  type: MappingValueType;
 | 
			
		||||
  boolean?: boolean;
 | 
			
		||||
  double?: number;
 | 
			
		||||
  integer?: number;
 | 
			
		||||
  string?: string;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export interface SaveRPCTemplateData {
 | 
			
		||||
  config: RPCTemplateConfig;
 | 
			
		||||
  templates: Array<RPCTemplate>;
 | 
			
		||||
 | 
			
		||||
@ -0,0 +1,28 @@
 | 
			
		||||
///
 | 
			
		||||
/// Copyright © 2016-2024 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 { Pipe, PipeTransform } from '@angular/core';
 | 
			
		||||
 | 
			
		||||
@Pipe({
 | 
			
		||||
  name: 'getRpcTemplateArrayView',
 | 
			
		||||
  standalone: true,
 | 
			
		||||
})
 | 
			
		||||
export class RpcTemplateArrayViewPipe implements PipeTransform {
 | 
			
		||||
 | 
			
		||||
  transform(values: {value: string | boolean | number}[]): string {
 | 
			
		||||
    return values.map(({value}) => value.toString()).join(', ');
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
@ -141,9 +141,6 @@ import {
 | 
			
		||||
import {
 | 
			
		||||
  TypeValuePanelComponent
 | 
			
		||||
} from '@home/components/widget/lib/gateway/connectors-configuration/type-value-panel/type-value-panel.component';
 | 
			
		||||
import {
 | 
			
		||||
  ModbusRpcParametersComponent
 | 
			
		||||
} from '@home/components/widget/lib/gateway/connectors-configuration/modbus/modbus-rpc-parameters/modbus-rpc-parameters.component';
 | 
			
		||||
import { ScadaSymbolWidgetComponent } from '@home/components/widget/lib/scada/scada-symbol-widget.component';
 | 
			
		||||
import {
 | 
			
		||||
  MqttLegacyBasicConfigComponent
 | 
			
		||||
@ -161,7 +158,17 @@ import {
 | 
			
		||||
  ModbusLegacyBasicConfigComponent
 | 
			
		||||
} from '@home/components/widget/lib/gateway/connectors-configuration/modbus/modbus-basic-config/modbus-legacy-basic-config.component';
 | 
			
		||||
import {
 | 
			
		||||
    ReportStrategyComponent
 | 
			
		||||
  MqttRpcParametersComponent
 | 
			
		||||
} from '@home/components/widget/lib/gateway/connectors-configuration/rpc-parameters/mqtt-rpc-parameters/mqtt-rpc-parameters.component';
 | 
			
		||||
import {
 | 
			
		||||
  OpcRpcParametersComponent
 | 
			
		||||
} from '@home/components/widget/lib/gateway/connectors-configuration/rpc-parameters/opc-rpc-parameters/opc-rpc-parameters.component';
 | 
			
		||||
import {
 | 
			
		||||
  ModbusRpcParametersComponent
 | 
			
		||||
} from '@home/components/widget/lib/gateway/connectors-configuration/rpc-parameters/modbus-rpc-parameters/modbus-rpc-parameters.component';
 | 
			
		||||
import { RpcTemplateArrayViewPipe } from '@home/components/widget/lib/gateway/pipes/rpc-template-array-view.pipe';
 | 
			
		||||
import { 
 | 
			
		||||
  ReportStrategyComponent
 | 
			
		||||
} from '@home/components/widget/lib/gateway/connectors-configuration/report-strategy/report-strategy.component';
 | 
			
		||||
 | 
			
		||||
@NgModule({
 | 
			
		||||
@ -256,6 +263,10 @@ import {
 | 
			
		||||
    GatewayAdvancedConfigurationComponent,
 | 
			
		||||
    OpcUaLegacyBasicConfigComponent,
 | 
			
		||||
    ModbusLegacyBasicConfigComponent,
 | 
			
		||||
    MqttRpcParametersComponent,
 | 
			
		||||
    OpcRpcParametersComponent,
 | 
			
		||||
    ModbusRpcParametersComponent,
 | 
			
		||||
    RpcTemplateArrayViewPipe,
 | 
			
		||||
    ReportStrategyComponent,
 | 
			
		||||
  ],
 | 
			
		||||
  exports: [
 | 
			
		||||
 | 
			
		||||
@ -45,6 +45,7 @@ import { UtilsService } from '@core/services/utils.service';
 | 
			
		||||
import { from } from 'rxjs';
 | 
			
		||||
import { DashboardUtilsService } from '@core/services/dashboard-utils.service';
 | 
			
		||||
import ITooltipsterInstance = JQueryTooltipster.ITooltipsterInstance;
 | 
			
		||||
import { TbContextMenuEvent } from '@shared/models/jquery-event.models';
 | 
			
		||||
 | 
			
		||||
export enum WidgetComponentActionType {
 | 
			
		||||
  MOUSE_DOWN,
 | 
			
		||||
@ -57,7 +58,7 @@ export enum WidgetComponentActionType {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export class WidgetComponentAction {
 | 
			
		||||
  event: MouseEvent;
 | 
			
		||||
  event: MouseEvent | TbContextMenuEvent;
 | 
			
		||||
  actionType: WidgetComponentActionType;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -151,7 +152,7 @@ export class WidgetContainerComponent extends PageComponent implements OnInit, O
 | 
			
		||||
    }
 | 
			
		||||
    $(this.gridsterItem.el).on('mousedown', (e) => this.onMouseDown(e.originalEvent));
 | 
			
		||||
    $(this.gridsterItem.el).on('click', (e) => this.onClicked(e.originalEvent));
 | 
			
		||||
    $(this.gridsterItem.el).on('contextmenu', (e) => this.onContextMenu(e.originalEvent));
 | 
			
		||||
    $(this.gridsterItem.el).on('tbcontextmenu', (e: TbContextMenuEvent) => this.onContextMenu(e));
 | 
			
		||||
    const dashboardContentElement = this.widget.widgetContext.dashboardContentElement;
 | 
			
		||||
    if (dashboardContentElement) {
 | 
			
		||||
      this.initEditWidgetActionTooltip(dashboardContentElement);
 | 
			
		||||
@ -180,6 +181,9 @@ export class WidgetContainerComponent extends PageComponent implements OnInit, O
 | 
			
		||||
    if (this.editWidgetActionsTooltip && !this.editWidgetActionsTooltip.status().destroyed) {
 | 
			
		||||
      this.editWidgetActionsTooltip.destroy();
 | 
			
		||||
    }
 | 
			
		||||
    $(this.gridsterItem.el).off('mousedown');
 | 
			
		||||
    $(this.gridsterItem.el).off('click');
 | 
			
		||||
    $(this.gridsterItem.el).off('tbcontextmenu');
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  isHighlighted(widget: DashboardWidget) {
 | 
			
		||||
@ -219,7 +223,7 @@ export class WidgetContainerComponent extends PageComponent implements OnInit, O
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  onContextMenu(event: MouseEvent) {
 | 
			
		||||
  onContextMenu(event: TbContextMenuEvent) {
 | 
			
		||||
    if (event) {
 | 
			
		||||
      event.stopPropagation();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -185,7 +185,7 @@
 | 
			
		||||
                  matTooltipPosition="above">
 | 
			
		||||
            <mat-icon>{{ isFullscreen ? 'fullscreen_exit' : 'fullscreen' }}</mat-icon>
 | 
			
		||||
          </button>
 | 
			
		||||
          <div class="tb-absolute-fill tb-rulechain-graph" (contextmenu)="openRuleChainContextMenu($event)">
 | 
			
		||||
          <div class="tb-absolute-fill tb-rulechain-graph" (tbcontextmenu)="openRuleChainContextMenu($event)">
 | 
			
		||||
            <div #ruleChainMenuTrigger="matMenuTrigger" style="visibility: hidden; position: fixed"
 | 
			
		||||
                 [style.left]="ruleChainMenuPosition.x"
 | 
			
		||||
                 [style.top]="ruleChainMenuPosition.y"
 | 
			
		||||
 | 
			
		||||
@ -94,6 +94,7 @@ import { VersionControlComponent } from '@home/components/vc/version-control.com
 | 
			
		||||
import { ComponentClusteringMode } from '@shared/models/component-descriptor.models';
 | 
			
		||||
import { MatDrawer } from '@angular/material/sidenav';
 | 
			
		||||
import { HttpStatusCode } from '@angular/common/http';
 | 
			
		||||
import { TbContextMenuEvent } from '@shared/models/jquery-event.models';
 | 
			
		||||
import Timeout = NodeJS.Timeout;
 | 
			
		||||
 | 
			
		||||
@Component({
 | 
			
		||||
@ -131,7 +132,7 @@ export class RuleChainPageComponent extends PageComponent
 | 
			
		||||
 | 
			
		||||
  ruleChainMenuPosition = { x: '0px', y: '0px' };
 | 
			
		||||
 | 
			
		||||
  contextMenuEvent: MouseEvent;
 | 
			
		||||
  contextMenuEvent: TbContextMenuEvent;
 | 
			
		||||
 | 
			
		||||
  ruleNodeTypeDescriptorsMap = ruleNodeTypeDescriptors;
 | 
			
		||||
  ruleNodeTypesLibraryArray = ruleNodeTypesLibrary;
 | 
			
		||||
@ -657,7 +658,7 @@ export class RuleChainPageComponent extends PageComponent
 | 
			
		||||
    this.validate();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  openRuleChainContextMenu($event: MouseEvent) {
 | 
			
		||||
  openRuleChainContextMenu($event: TbContextMenuEvent) {
 | 
			
		||||
    if (this.ruleChainCanvas.modelService && !$event.ctrlKey && !$event.metaKey) {
 | 
			
		||||
      const x = $event.clientX;
 | 
			
		||||
      const y = $event.clientY;
 | 
			
		||||
 | 
			
		||||
@ -1103,8 +1103,9 @@ export const generalStateRenderFunctionCompletions = (ctxCompletion: TbEditorCom
 | 
			
		||||
    ctx: ctxCompletion,
 | 
			
		||||
    svg: {
 | 
			
		||||
      meta: 'argument',
 | 
			
		||||
      type: '<a href="https://svgjs.dev/docs/3.2/container-elements/#svg-svg">SVG.Svg</a>',
 | 
			
		||||
      description: 'A root svg node. Instance of <a href="https://svgjs.dev/docs/3.2/container-elements/#svg-svg">SVG.Svg</a>.'
 | 
			
		||||
      type: '<a href="https://svgjs.dev/docs/3.2/container-elements/#svg-svg" target="_blank">SVG.Svg</a>',
 | 
			
		||||
      description: 'A root svg node. Instance of <a href="https://svgjs.dev/docs/3.2/container-elements/#svg-svg" ' +
 | 
			
		||||
        'target="_blank">SVG.Svg</a>.'
 | 
			
		||||
    }
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
@ -1114,8 +1115,9 @@ export const elementStateRenderFunctionCompletions = (ctxCompletion: TbEditorCom
 | 
			
		||||
      meta: 'argument',
 | 
			
		||||
      type: 'Element',
 | 
			
		||||
      description: 'SVG element.<br>' +
 | 
			
		||||
        'See <a href="https://svgjs.dev/docs/3.2/manipulating/">Manipulating</a> section to manipulate the element.<br>' +
 | 
			
		||||
        'See <a href="https://svgjs.dev/docs/3.2/animating/">Animating</a> section to animate the element.'
 | 
			
		||||
        'See <a href="https://svgjs.dev/docs/3.2/manipulating/" ' +
 | 
			
		||||
        'target="_blank">Manipulating</a> section to manipulate the element.<br>' +
 | 
			
		||||
        'See <a href="https://svgjs.dev/docs/3.2/animating/" target="_blank">Animating</a> section to animate the element.'
 | 
			
		||||
    }
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
@ -1133,7 +1135,7 @@ export const scadaSymbolContextCompletion = (metadata: ScadaSymbolMetadata, tags
 | 
			
		||||
                                             customTranslate: CustomTranslatePipe): TbEditorCompletion => {
 | 
			
		||||
 | 
			
		||||
  const scadaSymbolAnimationLink = HelpLinks.linksMap.scadaSymbolDevAnimation;
 | 
			
		||||
  const scadaSymbolAnimation = `<a href="${scadaSymbolAnimationLink}">ScadaSymbolAnimation</a>`;
 | 
			
		||||
  const scadaSymbolAnimation = `<a href="${scadaSymbolAnimationLink}" target="_blank">ScadaSymbolAnimation</a>`;
 | 
			
		||||
 | 
			
		||||
  const properties: TbEditorCompletion = {
 | 
			
		||||
    meta: 'object',
 | 
			
		||||
@ -1282,8 +1284,8 @@ export const scadaSymbolContextCompletion = (metadata: ScadaSymbolMetadata, tags
 | 
			
		||||
          text: {
 | 
			
		||||
            meta: 'function',
 | 
			
		||||
            description: 'Set text to element(s). Only applicable for elements of type ' +
 | 
			
		||||
              '<a href="https://svgjs.dev/docs/3.2/shape-elements/#svg-text">SVG.Text</a> or ' +
 | 
			
		||||
              '<a href="https://svgjs.dev/docs/3.2/shape-elements/#svg-tspan">SVG.Tspan</a>.',
 | 
			
		||||
              '<a href="https://svgjs.dev/docs/3.2/shape-elements/#svg-text" target="_blank">SVG.Text</a> or ' +
 | 
			
		||||
              '<a href="https://svgjs.dev/docs/3.2/shape-elements/#svg-tspan" target="_blank">SVG.Tspan</a>.',
 | 
			
		||||
            args: [
 | 
			
		||||
              {
 | 
			
		||||
                name: 'element',
 | 
			
		||||
@ -1300,8 +1302,8 @@ export const scadaSymbolContextCompletion = (metadata: ScadaSymbolMetadata, tags
 | 
			
		||||
          font: {
 | 
			
		||||
            meta: 'function',
 | 
			
		||||
            description: 'Set element(s) text font and color. Only applicable for elements of type ' +
 | 
			
		||||
              '<a href="https://svgjs.dev/docs/3.2/shape-elements/#svg-text">SVG.Text</a> or ' +
 | 
			
		||||
              '<a href="https://svgjs.dev/docs/3.2/shape-elements/#svg-tspan">SVG.Tspan</a>.',
 | 
			
		||||
              '<a href="https://svgjs.dev/docs/3.2/shape-elements/#svg-text" target="_blank">SVG.Text</a> or ' +
 | 
			
		||||
              '<a href="https://svgjs.dev/docs/3.2/shape-elements/#svg-tspan" target="_blank">SVG.Tspan</a>.',
 | 
			
		||||
            args: [
 | 
			
		||||
              {
 | 
			
		||||
                name: 'element',
 | 
			
		||||
@ -1323,7 +1325,7 @@ export const scadaSymbolContextCompletion = (metadata: ScadaSymbolMetadata, tags
 | 
			
		||||
          icon: {
 | 
			
		||||
            meta: 'function',
 | 
			
		||||
            description: 'Draws icon inside element(s). Only applicable for elements of type ' +
 | 
			
		||||
              '<a href="https://svgjs.dev/docs/3.2/container-elements/#svg-g">SVG.G</a>.',
 | 
			
		||||
              '<a href="https://svgjs.dev/docs/3.2/container-elements/#svg-g" target="_blank">SVG.G</a>.',
 | 
			
		||||
            args: [
 | 
			
		||||
              {
 | 
			
		||||
                name: 'element',
 | 
			
		||||
@ -1431,8 +1433,9 @@ export const scadaSymbolContextCompletion = (metadata: ScadaSymbolMetadata, tags
 | 
			
		||||
      tags: tagsCompletions,
 | 
			
		||||
      svg: {
 | 
			
		||||
        meta: 'argument',
 | 
			
		||||
        type: '<a href="https://svgjs.dev/docs/3.2/container-elements/#svg-svg">SVG.Svg</a>',
 | 
			
		||||
        description: 'A root svg node. Instance of <a href="https://svgjs.dev/docs/3.2/container-elements/#svg-svg">SVG.Svg</a>.'
 | 
			
		||||
        type: '<a href="https://svgjs.dev/docs/3.2/container-elements/#svg-svg" target="_blank">SVG.Svg</a>',
 | 
			
		||||
        description: 'A root svg node. Instance of <a href="https://svgjs.dev/docs/3.2/container-elements/#svg-svg" ' +
 | 
			
		||||
          'target="_blank">SVG.Svg</a>.'
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
@ -21,7 +21,7 @@ import { serviceCompletions } from '@shared/models/ace/service-completion.models
 | 
			
		||||
const widgetEditorCompletions: TbEditorCompletions = {
 | 
			
		||||
  ... {self: {
 | 
			
		||||
    description: 'Built-in variable <b>self</b> that is a reference to the widget instance',
 | 
			
		||||
    type: '<a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/modules/home/models/widget-component.models.ts#L350">WidgetTypeInstance</a>',
 | 
			
		||||
    type: '<a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/modules/home/models/widget-component.models.ts#L350" target="_blank">WidgetTypeInstance</a>',
 | 
			
		||||
    meta: 'object',
 | 
			
		||||
    children: {
 | 
			
		||||
      ...{
 | 
			
		||||
@ -31,19 +31,19 @@ const widgetEditorCompletions: TbEditorCompletions = {
 | 
			
		||||
        },
 | 
			
		||||
        onDataUpdated: {
 | 
			
		||||
          description: 'Called when the new data is available from the widget subscription.<br>Latest data can be accessed from ' +
 | 
			
		||||
            'the <code>defaultSubscription</code> property of <a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/modules/home/models/widget-component.models.ts#L83">widget context (<code>ctx</code>)</a>.',
 | 
			
		||||
            'the <code>defaultSubscription</code> property of <a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/modules/home/models/widget-component.models.ts#L83" target="_blank">widget context (<code>ctx</code>)</a>.',
 | 
			
		||||
          meta: 'function'
 | 
			
		||||
        },
 | 
			
		||||
        onResize: {
 | 
			
		||||
          description: 'Called when widget container is resized. Latest <code>width</code> and <code>height</code> can be obtained from <a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/modules/home/models/widget-component.models.ts#L83">widget context (<code>ctx</code>)</a>.',
 | 
			
		||||
          description: 'Called when widget container is resized. Latest <code>width</code> and <code>height</code> can be obtained from <a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/modules/home/models/widget-component.models.ts#L83" target="_blank">widget context (<code>ctx</code>)</a>.',
 | 
			
		||||
          meta: 'function'
 | 
			
		||||
        },
 | 
			
		||||
        onEditModeChanged: {
 | 
			
		||||
          description: 'Called when dashboard editing mode is changed. Latest mode is handled by <code>isEdit</code> property of <a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/modules/home/models/widget-component.models.ts#L83">widget context (<code>ctx</code>)</a>.',
 | 
			
		||||
          description: 'Called when dashboard editing mode is changed. Latest mode is handled by <code>isEdit</code> property of <a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/modules/home/models/widget-component.models.ts#L83" target="_blank">widget context (<code>ctx</code>)</a>.',
 | 
			
		||||
          meta: 'function'
 | 
			
		||||
        },
 | 
			
		||||
        onMobileModeChanged: {
 | 
			
		||||
          description: 'Called when dashboard view width crosses mobile breakpoint. Latest state is handled by <code>isMobile</code> property of <a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/modules/home/models/widget-component.models.ts#L83">widget context (<code>ctx</code>)</a>.',
 | 
			
		||||
          description: 'Called when dashboard view width crosses mobile breakpoint. Latest state is handled by <code>isMobile</code> property of <a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/modules/home/models/widget-component.models.ts#L83" target="_blank">widget context (<code>ctx</code>)</a>.',
 | 
			
		||||
          meta: 'function'
 | 
			
		||||
        },
 | 
			
		||||
        onDestroy: {
 | 
			
		||||
@ -51,7 +51,7 @@ const widgetEditorCompletions: TbEditorCompletions = {
 | 
			
		||||
          meta: 'function'
 | 
			
		||||
        },
 | 
			
		||||
        getSettingsSchema: {
 | 
			
		||||
          description: 'Optional function returning widget settings schema json as alternative to <b>Settings tab</b> of <a href="https://thingsboard.io/docs/user-guide/contribution/widgets-development/#settings-schema-section">Settings schema section</a>.',
 | 
			
		||||
          description: 'Optional function returning widget settings schema json as alternative to <b>Settings tab</b> of <a href="https://thingsboard.io/docs/user-guide/contribution/widgets-development/#settings-schema-section" target="_blank">Settings schema section</a>.',
 | 
			
		||||
          meta: 'function',
 | 
			
		||||
          return: {
 | 
			
		||||
            description: 'An widget settings schema json',
 | 
			
		||||
@ -59,7 +59,7 @@ const widgetEditorCompletions: TbEditorCompletions = {
 | 
			
		||||
          }
 | 
			
		||||
        },
 | 
			
		||||
        getDataKeySettingsSchema: {
 | 
			
		||||
          description: 'Optional function returning particular data key settings schema json as alternative to <b>Data key settings schema</b> of <a href="https://thingsboard.io/docs/user-guide/contribution/widgets-development/#settings-schema-section">Settings schema section</a>.',
 | 
			
		||||
          description: 'Optional function returning particular data key settings schema json as alternative to <b>Data key settings schema</b> of <a href="https://thingsboard.io/docs/user-guide/contribution/widgets-development/#settings-schema-section" target="_blank">Settings schema section</a>.',
 | 
			
		||||
          meta: 'function',
 | 
			
		||||
          return: {
 | 
			
		||||
            description: 'A particular data key settings schema json',
 | 
			
		||||
@ -71,7 +71,7 @@ const widgetEditorCompletions: TbEditorCompletions = {
 | 
			
		||||
          meta: 'function',
 | 
			
		||||
          return: {
 | 
			
		||||
            description: 'An object describing widget datasource parameters.',
 | 
			
		||||
            type: '<a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/shared/models/widget.models.ts#L146">WidgetTypeParameters</a>'
 | 
			
		||||
            type: '<a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/shared/models/widget.models.ts#L146" target="_blank">WidgetTypeParameters</a>'
 | 
			
		||||
          }
 | 
			
		||||
        },
 | 
			
		||||
        actionSources: {
 | 
			
		||||
@ -79,7 +79,7 @@ const widgetEditorCompletions: TbEditorCompletions = {
 | 
			
		||||
          meta: 'function',
 | 
			
		||||
          return: {
 | 
			
		||||
            description: 'A map of action sources by action source id.',
 | 
			
		||||
            type: '{[actionSourceId: string]: <a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/shared/models/widget.models.ts#L118">WidgetActionSource</a>}'
 | 
			
		||||
            type: '{[actionSourceId: string]: <a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/shared/models/widget.models.ts#L118" target="_blank">WidgetActionSource</a>}'
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										35
									
								
								ui-ngx/src/app/shared/directives/context-menu.directive.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								ui-ngx/src/app/shared/directives/context-menu.directive.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,35 @@
 | 
			
		||||
///
 | 
			
		||||
/// Copyright © 2016-2024 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 { Directive, ElementRef, EventEmitter, OnDestroy, Output } from '@angular/core';
 | 
			
		||||
import { TbContextMenuEvent } from '@shared/models/jquery-event.models';
 | 
			
		||||
 | 
			
		||||
@Directive({
 | 
			
		||||
  selector: '[tbcontextmenu]'
 | 
			
		||||
})
 | 
			
		||||
export class ContextMenuDirective implements OnDestroy {
 | 
			
		||||
 | 
			
		||||
  @Output()
 | 
			
		||||
  tbcontextmenu = new EventEmitter<TbContextMenuEvent>();
 | 
			
		||||
 | 
			
		||||
  constructor(private el: ElementRef) {
 | 
			
		||||
    $(this.el.nativeElement).on('tbcontextmenu', (e: TbContextMenuEvent) => this.tbcontextmenu.emit(e));
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  ngOnDestroy() {
 | 
			
		||||
    $(this.el.nativeElement).off('tbcontextmenu');
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
@ -16,3 +16,4 @@
 | 
			
		||||
 | 
			
		||||
export * from './truncate-with-tooltip.directive';
 | 
			
		||||
export * from './ellipsis-chip-list.directive';
 | 
			
		||||
export * from './context-menu.directive';
 | 
			
		||||
 | 
			
		||||
@ -16,101 +16,101 @@
 | 
			
		||||
 | 
			
		||||
import { FunctionArg, FunctionArgType, TbEditorCompletions } from '@shared/models/ace/completion.models';
 | 
			
		||||
 | 
			
		||||
export const entityIdHref = '<a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/shared/models/id/entity-id.ts#L20">EntityId</a>';
 | 
			
		||||
export const entityIdHref = '<a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/shared/models/id/entity-id.ts#L20" target="_blank">EntityId</a>';
 | 
			
		||||
 | 
			
		||||
export const baseDataHref = '<a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/shared/models/base-data.ts#L22">Base data</a>';
 | 
			
		||||
export const baseDataHref = '<a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/shared/models/base-data.ts#L22" target="_blank">Base data</a>';
 | 
			
		||||
 | 
			
		||||
export const alarmDataHref = '<a href="https://github.com/thingsboard/thingsboard/blob/master/ui-ngx/src/app/shared/models/query/query.models.ts#L573">Alarm data</a>';
 | 
			
		||||
export const alarmDataHref = '<a href="https://github.com/thingsboard/thingsboard/blob/master/ui-ngx/src/app/shared/models/query/query.models.ts#L573" target="_blank">Alarm data</a>';
 | 
			
		||||
 | 
			
		||||
export const alarmDataQueryHref = '<a href="https://github.com/thingsboard/thingsboard/blob/master/ui-ngx/src/app/shared/models/query/query.models.ts#L558">Alarm data query</a>';
 | 
			
		||||
export const alarmDataQueryHref = '<a href="https://github.com/thingsboard/thingsboard/blob/master/ui-ngx/src/app/shared/models/query/query.models.ts#L558" target="_blank">Alarm data query</a>';
 | 
			
		||||
 | 
			
		||||
export const attributeScopeHref = '<a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/shared/models/telemetry/telemetry.models.ts#L37">Attribute scope</a>';
 | 
			
		||||
export const attributeScopeHref = '<a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/shared/models/telemetry/telemetry.models.ts#L37" target="_blank">Attribute scope</a>';
 | 
			
		||||
 | 
			
		||||
export const entityTypeHref = '<a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/shared/models/entity-type.models.ts#L36">EntityType</a>';
 | 
			
		||||
export const entityTypeHref = '<a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/shared/models/entity-type.models.ts#L36" target="_blank">EntityType</a>';
 | 
			
		||||
 | 
			
		||||
export const pageDataHref = '<a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/shared/models/page/page-data.ts#L17">PageData</a>';
 | 
			
		||||
export const pageDataHref = '<a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/shared/models/page/page-data.ts#L17" target="_blank">PageData</a>';
 | 
			
		||||
 | 
			
		||||
export const deviceInfoHref = '<a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/shared/models/device.models.ts#L33">DeviceInfo</a>';
 | 
			
		||||
export const deviceInfoHref = '<a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/shared/models/device.models.ts#L33" target="_blank">DeviceInfo</a>';
 | 
			
		||||
 | 
			
		||||
export const assetInfoHref = '<a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/shared/models/asset.models.ts#L32">AssetInfo</a>';
 | 
			
		||||
export const assetInfoHref = '<a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/shared/models/asset.models.ts#L32" target="_blank">AssetInfo</a>';
 | 
			
		||||
 | 
			
		||||
export const entityViewInfoHref = '<a href = "https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/shared/models/entity-view.models.ts#L47">EntityViewInfo</a>';
 | 
			
		||||
export const entityViewInfoHref = '<a href = "https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/shared/models/entity-view.models.ts#L47" target="_blank">EntityViewInfo</a>';
 | 
			
		||||
 | 
			
		||||
export const entityRelationsQueryHref = '<a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/shared/models/relation.models.ts#L69">EntityRelationsQuery</a>';
 | 
			
		||||
export const entityRelationsQueryHref = '<a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/shared/models/relation.models.ts#L69" target="_blank">EntityRelationsQuery</a>';
 | 
			
		||||
 | 
			
		||||
export const entityRelationInfoHref = '<a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/shared/models/relation.models.ts#L87">EntityRelationInfo</a>';
 | 
			
		||||
export const entityRelationInfoHref = '<a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/shared/models/relation.models.ts#L87" target="_blank">EntityRelationInfo</a>';
 | 
			
		||||
 | 
			
		||||
export const dashboardInfoHref = '<a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/shared/models/dashboard.models.ts#L25">DashboardInfo</a>';
 | 
			
		||||
export const dashboardInfoHref = '<a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/shared/models/dashboard.models.ts#L25" target="_blank">DashboardInfo</a>';
 | 
			
		||||
 | 
			
		||||
export const deviceHref = '<a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/shared/models/device.models.ts#L24">Device</a>';
 | 
			
		||||
export const deviceHref = '<a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/shared/models/device.models.ts#L24" target="_blank">Device</a>';
 | 
			
		||||
 | 
			
		||||
export const assetHref = '<a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/shared/models/asset.models.ts#L23">Asset</a>';
 | 
			
		||||
export const assetHref = '<a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/shared/models/asset.models.ts#L23" target="_blank">Asset</a>';
 | 
			
		||||
 | 
			
		||||
export const entityViewHref = '<a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/shared/models/entity-view.models.ts#L35">entityView</a>';
 | 
			
		||||
export const entityViewHref = '<a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/shared/models/entity-view.models.ts#L35" target="_blank">entityView</a>';
 | 
			
		||||
 | 
			
		||||
export const entityRelationHref = '<a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/shared/models/relation.models.ts#L79">Entity relation</a>';
 | 
			
		||||
export const entityRelationHref = '<a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/shared/models/relation.models.ts#L79" target="_blank">Entity relation</a>';
 | 
			
		||||
 | 
			
		||||
export const dashboardHref = '<a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/shared/models/dashboard.models.ts#L102">Dashboard</a>';
 | 
			
		||||
export const dashboardHref = '<a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/shared/models/dashboard.models.ts#L102" target="_blank">Dashboard</a>';
 | 
			
		||||
 | 
			
		||||
export const customerHref = '<a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/shared/models/customer.model.ts#L21">Customer</a>';
 | 
			
		||||
export const customerHref = '<a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/shared/models/customer.model.ts#L21" target="_blank">Customer</a>';
 | 
			
		||||
 | 
			
		||||
export const attributeDataHref = '<a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/shared/models/telemetry/telemetry.models.ts#L76">Attribute Data</a>';
 | 
			
		||||
export const attributeDataHref = '<a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/shared/models/telemetry/telemetry.models.ts#L76" target="_blank">Attribute Data</a>';
 | 
			
		||||
 | 
			
		||||
export const timeseriesDataHref = '<a href="https://github.com/thingsboard/thingsboard/blob/627c0577b08452308f925cecb3860e35292c649e/ui-ngx/src/app/shared/models/telemetry/telemetry.models.ts#L91">Timeseries Data</a>';
 | 
			
		||||
export const timeseriesDataHref = '<a href="https://github.com/thingsboard/thingsboard/blob/627c0577b08452308f925cecb3860e35292c649e/ui-ngx/src/app/shared/models/telemetry/telemetry.models.ts#L91" target="_blank">Timeseries Data</a>';
 | 
			
		||||
 | 
			
		||||
export const aggregationTypeHref = '<a href="https://github.com/thingsboard/thingsboard/blob/a8ea887eacf7729e603ace13ce2d7d89dae82931/ui-ngx/src/app/shared/models/time/time.models.ts#L54">Aggregation Type</a>';
 | 
			
		||||
export const aggregationTypeHref = '<a href="https://github.com/thingsboard/thingsboard/blob/a8ea887eacf7729e603ace13ce2d7d89dae82931/ui-ngx/src/app/shared/models/time/time.models.ts#L54" target="_blank">Aggregation Type</a>';
 | 
			
		||||
 | 
			
		||||
export const dataSortOrderHref = '<a href="https://github.com/thingsboard/thingsboard/blob/627c0577b08452308f925cecb3860e35292c649e/ui-ngx/src/app/shared/models/telemetry/telemetry.models.ts#L95">Data Sort Order</a>';
 | 
			
		||||
export const dataSortOrderHref = '<a href="https://github.com/thingsboard/thingsboard/blob/627c0577b08452308f925cecb3860e35292c649e/ui-ngx/src/app/shared/models/telemetry/telemetry.models.ts#L95" target="_blank">Data Sort Order</a>';
 | 
			
		||||
 | 
			
		||||
export const userHref = '<a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/shared/models/user.model.ts#L23">User</a>';
 | 
			
		||||
export const userHref = '<a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/shared/models/user.model.ts#L23" target="_blank">User</a>';
 | 
			
		||||
 | 
			
		||||
export const entityDataHref = '<a href="https://github.com/thingsboard/thingsboard/blob/master/ui-ngx/src/app/shared/models/query/query.models.ts#L567">Entity data</a>';
 | 
			
		||||
export const entityDataHref = '<a href="https://github.com/thingsboard/thingsboard/blob/master/ui-ngx/src/app/shared/models/query/query.models.ts#L567" target="_blank">Entity data</a>';
 | 
			
		||||
 | 
			
		||||
export const entityDataQueryHref = '<a href="https://github.com/thingsboard/thingsboard/blob/master/ui-ngx/src/app/shared/models/query/query.models.ts#L555">Entity Data Query</a>';
 | 
			
		||||
export const entityDataQueryHref = '<a href="https://github.com/thingsboard/thingsboard/blob/master/ui-ngx/src/app/shared/models/query/query.models.ts#L555" target="_blank">Entity Data Query</a>';
 | 
			
		||||
 | 
			
		||||
export const deviceCredentialsHref = '<a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/shared/models/device.models.ts#L50">DeviceCredentials</a>';
 | 
			
		||||
export const deviceCredentialsHref = '<a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/shared/models/device.models.ts#L50" target="_blank">DeviceCredentials</a>';
 | 
			
		||||
 | 
			
		||||
export const entityFilterHref = '<a href="https://github.com/thingsboard/thingsboard/blob/master/ui-ngx/src/app/shared/models/query/query.models.ts#L487">Entity filter</a>';
 | 
			
		||||
export const entityFilterHref = '<a href="https://github.com/thingsboard/thingsboard/blob/master/ui-ngx/src/app/shared/models/query/query.models.ts#L487" target="_blank">Entity filter</a>';
 | 
			
		||||
 | 
			
		||||
export const entityInfoHref = '<a href="https://github.com/thingsboard/thingsboard/blob/master/ui-ngx/src/app/shared/models/entity.models.ts#L20">Entity info</a>';
 | 
			
		||||
export const entityInfoHref = '<a href="https://github.com/thingsboard/thingsboard/blob/master/ui-ngx/src/app/shared/models/entity.models.ts#L20" target="_blank">Entity info</a>';
 | 
			
		||||
 | 
			
		||||
export const aliasEntityTypeHref = '<a href="https://github.com/thingsboard/thingsboard/blob/master/ui-ngx/src/app/shared/models/entity-type.models.ts#L51">Alias Entity Type</a>';
 | 
			
		||||
export const aliasEntityTypeHref = '<a href="https://github.com/thingsboard/thingsboard/blob/master/ui-ngx/src/app/shared/models/entity-type.models.ts#L51" target="_blank">Alias Entity Type</a>';
 | 
			
		||||
 | 
			
		||||
export const aliasFilterTypeHref = '<a href="https://github.com/thingsboard/thingsboard/blob/master/ui-ngx/src/app/shared/models/entity-type.models.ts#L51">Alias filter type</a>';
 | 
			
		||||
export const aliasFilterTypeHref = '<a href="https://github.com/thingsboard/thingsboard/blob/master/ui-ngx/src/app/shared/models/entity-type.models.ts#L51" target="_blank">Alias filter type</a>';
 | 
			
		||||
 | 
			
		||||
export const entityAliasHref = '<a href="https://github.com/thingsboard/thingsboard/blob/master/ui-ngx/src/app/shared/models/alias.models.ts#L150">Entity alias</a>';
 | 
			
		||||
export const entityAliasHref = '<a href="https://github.com/thingsboard/thingsboard/blob/master/ui-ngx/src/app/shared/models/alias.models.ts#L150" target="_blank">Entity alias</a>';
 | 
			
		||||
 | 
			
		||||
export const dataKeyTypeHref = '<a href="https://github.com/thingsboard/thingsboard/blob/master/ui-ngx/src/app/shared/models/telemetry/telemetry.models.ts#L27">Data key type</a>';
 | 
			
		||||
export const dataKeyTypeHref = '<a href="https://github.com/thingsboard/thingsboard/blob/master/ui-ngx/src/app/shared/models/telemetry/telemetry.models.ts#L27" target="_blank">Data key type</a>';
 | 
			
		||||
 | 
			
		||||
export const subscriptionInfoHref = '<a href="https://github.com/thingsboard/thingsboard/blob/master/ui-ngx/src/app/core/api/widget-api.models.ts#L155">Subscription info</a>';
 | 
			
		||||
export const subscriptionInfoHref = '<a href="https://github.com/thingsboard/thingsboard/blob/master/ui-ngx/src/app/core/api/widget-api.models.ts#L155" target="_blank">Subscription info</a>';
 | 
			
		||||
 | 
			
		||||
export const dataSourceHref = '<a href="https://github.com/thingsboard/thingsboard/blob/master/ui-ngx/src/app/shared/models/widget.models.ts#L257">Datasource</a>';
 | 
			
		||||
export const dataSourceHref = '<a href="https://github.com/thingsboard/thingsboard/blob/master/ui-ngx/src/app/shared/models/widget.models.ts#L257" target="_blank">Datasource</a>';
 | 
			
		||||
 | 
			
		||||
export const stateParamsHref = '<a href="https://github.com/thingsboard/thingsboard/blob/master/ui-ngx/src/app/core/api/widget-api.models.ts#L129">State params</a>';
 | 
			
		||||
export const stateParamsHref = '<a href="https://github.com/thingsboard/thingsboard/blob/master/ui-ngx/src/app/core/api/widget-api.models.ts#L129" target="_blank">State params</a>';
 | 
			
		||||
 | 
			
		||||
export const aliasInfoHref = '<a href="https://github.com/thingsboard/thingsboard/blob/master/ui-ngx/src/app/core/api/widget-api.models.ts#L88">Alias info</a>';
 | 
			
		||||
export const aliasInfoHref = '<a href="https://github.com/thingsboard/thingsboard/blob/master/ui-ngx/src/app/core/api/widget-api.models.ts#L88" target="_blank">Alias info</a>';
 | 
			
		||||
 | 
			
		||||
export const entityAliasFilterHref = '<a href="https://github.com/thingsboard/thingsboard/blob/master/ui-ngx/src/app/shared/models/alias.models.ts#L134">Entity alias filter</a>';
 | 
			
		||||
export const entityAliasFilterHref = '<a href="https://github.com/thingsboard/thingsboard/blob/master/ui-ngx/src/app/shared/models/alias.models.ts#L134" target="_blank">Entity alias filter</a>';
 | 
			
		||||
 | 
			
		||||
export const entityAliasFilterResultHref = '<a href="https://github.com/thingsboard/thingsboard/blob/master/ui-ngx/src/app/shared/models/alias.models.ts#L158">Entity alias filter result</a>';
 | 
			
		||||
export const entityAliasFilterResultHref = '<a href="https://github.com/thingsboard/thingsboard/blob/master/ui-ngx/src/app/shared/models/alias.models.ts#L158" target="_blank">Entity alias filter result</a>';
 | 
			
		||||
 | 
			
		||||
export const importEntityDataHref = '<a href="https://github.com/thingsboard/thingsboard/blob/master/ui-ngx/src/app/shared/models/entity.models.ts#L28">Import entity data</a>';
 | 
			
		||||
export const importEntityDataHref = '<a href="https://github.com/thingsboard/thingsboard/blob/master/ui-ngx/src/app/shared/models/entity.models.ts#L28" target="_blank">Import entity data</a>';
 | 
			
		||||
 | 
			
		||||
export const importEntitiesResultInfoHref = '<a href="https://github.com/thingsboard/thingsboard/blob/master/ui-ngx/src/app/shared/models/entity.models.ts#L42">Import entities result info</a>';
 | 
			
		||||
export const importEntitiesResultInfoHref = '<a href="https://github.com/thingsboard/thingsboard/blob/master/ui-ngx/src/app/shared/models/entity.models.ts#L42" target="_blank">Import entities result info</a>';
 | 
			
		||||
 | 
			
		||||
export const customDialogComponentHref = '<a href="https://github.com/thingsboard/thingsboard/blob/master/ui-ngx/src/app/modules/home/components/widget/dialog/custom-dialog.component.ts#L48">CustomDialogComponent</a>';
 | 
			
		||||
export const customDialogComponentHref = '<a href="https://github.com/thingsboard/thingsboard/blob/master/ui-ngx/src/app/modules/home/components/widget/dialog/custom-dialog.component.ts#L48" target="_blank">CustomDialogComponent</a>';
 | 
			
		||||
 | 
			
		||||
export const resourceInfoHref = '<a https://github.com/thingsboard/thingsboard/blob/b033b51712244d08e0f5e0beb8be60c9f8fa4cd2/ui-ngx/src/app/shared/models/resource.models.ts#L51">Resource info</a>';
 | 
			
		||||
export const resourceInfoHref = '<a href="https://github.com/thingsboard/thingsboard/blob/b033b51712244d08e0f5e0beb8be60c9f8fa4cd2/ui-ngx/src/app/shared/models/resource.models.ts#L51" target="_blank">Resource info</a>';
 | 
			
		||||
 | 
			
		||||
export const pageLinkArg: FunctionArg = {
 | 
			
		||||
  name: 'pageLink',
 | 
			
		||||
  type: '<a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/shared/models/page/page-link.ts#L68">PageLink</a>',
 | 
			
		||||
  type: '<a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/shared/models/page/page-link.ts#L68" target="_blank">PageLink</a>',
 | 
			
		||||
  description: 'Page link object used to perform paginated request.'
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export const requestConfigArg: FunctionArg = {
 | 
			
		||||
  name: 'config',
 | 
			
		||||
  type: '<a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/core/http/http-utils.ts#L21">RequestConfig</a>',
 | 
			
		||||
  type: '<a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/core/http/http-utils.ts#L21" target="_blank">RequestConfig</a>',
 | 
			
		||||
  description: 'HTTP request configuration.',
 | 
			
		||||
  optional: true
 | 
			
		||||
};
 | 
			
		||||
@ -167,9 +167,9 @@ export function observablePageDataReturnType(objectType: string): FunctionArgTyp
 | 
			
		||||
export const serviceCompletions: TbEditorCompletions = {
 | 
			
		||||
  deviceService: {
 | 
			
		||||
    description: 'Device Service API<br>' +
 | 
			
		||||
      'See <a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/core/http/device.service.ts#L37">DeviceService</a> for API reference.',
 | 
			
		||||
      'See <a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/core/http/device.service.ts#L37" target="_blank">DeviceService</a> for API reference.',
 | 
			
		||||
    meta: 'service',
 | 
			
		||||
    type: '<a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/core/http/device.service.ts#L37">DeviceService</a>',
 | 
			
		||||
    type: '<a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/core/http/device.service.ts#L37" target="_blank">DeviceService</a>',
 | 
			
		||||
    children: {
 | 
			
		||||
      getTenantDeviceInfos: {
 | 
			
		||||
        description: 'Get tenant devices',
 | 
			
		||||
@ -243,7 +243,7 @@ export const serviceCompletions: TbEditorCompletions = {
 | 
			
		||||
        args: [
 | 
			
		||||
          requestConfigArg
 | 
			
		||||
        ],
 | 
			
		||||
        return: observableArrayReturnType('<a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/shared/models/entity-type.models.ts#L295">EntitySubtype</a>')
 | 
			
		||||
        return: observableArrayReturnType('<a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/shared/models/entity-type.models.ts#L295" target="_blank">EntitySubtype</a>')
 | 
			
		||||
      },
 | 
			
		||||
      getDeviceCredentials: {
 | 
			
		||||
        description: 'Get device credentials by device id',
 | 
			
		||||
@ -322,7 +322,7 @@ export const serviceCompletions: TbEditorCompletions = {
 | 
			
		||||
        description: 'Find devices by search query',
 | 
			
		||||
        meta: 'function',
 | 
			
		||||
        args: [
 | 
			
		||||
          { name: 'query', type: '<a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/shared/models/device.models.ts#L57">DeviceSearchQuery</a>',
 | 
			
		||||
          { name: 'query', type: '<a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/shared/models/device.models.ts#L57" target="_blank">DeviceSearchQuery</a>',
 | 
			
		||||
            description: 'Device search query object'},
 | 
			
		||||
          requestConfigArg
 | 
			
		||||
        ],
 | 
			
		||||
@ -346,7 +346,7 @@ export const serviceCompletions: TbEditorCompletions = {
 | 
			
		||||
            description: 'Claiming device name'},
 | 
			
		||||
          requestConfigArg
 | 
			
		||||
        ],
 | 
			
		||||
        return: observableReturnType('<a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/shared/models/device.models.ts#L71">ClaimResult</a>')
 | 
			
		||||
        return: observableReturnType('<a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/shared/models/device.models.ts#L71" target="_blank">ClaimResult</a>')
 | 
			
		||||
      },
 | 
			
		||||
      unclaimDevice: {
 | 
			
		||||
        description: 'Send un-claim device request',
 | 
			
		||||
@ -362,9 +362,9 @@ export const serviceCompletions: TbEditorCompletions = {
 | 
			
		||||
  },
 | 
			
		||||
  assetService: {
 | 
			
		||||
    description: 'Asset Service API<br>' +
 | 
			
		||||
      'See <a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/core/http/asset.service.ts#L29">AssetService</a> for API reference.',
 | 
			
		||||
      'See <a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/core/http/asset.service.ts#L29" target="_blank">AssetService</a> for API reference.',
 | 
			
		||||
    meta: 'service',
 | 
			
		||||
    type: '<a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/core/http/asset.service.ts#L29">AssetService</a>',
 | 
			
		||||
    type: '<a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/core/http/asset.service.ts#L29" target="_blank">AssetService</a>',
 | 
			
		||||
    children: {
 | 
			
		||||
      getTenantAssetInfos: {
 | 
			
		||||
        description: 'Get tenant assets',
 | 
			
		||||
@ -438,7 +438,7 @@ export const serviceCompletions: TbEditorCompletions = {
 | 
			
		||||
        args: [
 | 
			
		||||
          requestConfigArg
 | 
			
		||||
        ],
 | 
			
		||||
        return: observableArrayReturnType('<a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/shared/models/entity-type.models.ts#L295">EntitySubtype</a>')
 | 
			
		||||
        return: observableArrayReturnType('<a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/shared/models/entity-type.models.ts#L295" target="_blank">EntitySubtype</a>')
 | 
			
		||||
      },
 | 
			
		||||
      makeAssetPublic: {
 | 
			
		||||
        description: 'Make asset public (available from public dashboard)',
 | 
			
		||||
@ -474,7 +474,7 @@ export const serviceCompletions: TbEditorCompletions = {
 | 
			
		||||
        args: [
 | 
			
		||||
          {
 | 
			
		||||
            name: 'query',
 | 
			
		||||
            type: '<a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/shared/models/asset.models.ts#L37">AssetSearchQuery</a>',
 | 
			
		||||
            type: '<a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/shared/models/asset.models.ts#L37" target="_blank">AssetSearchQuery</a>',
 | 
			
		||||
            description: 'Asset search query object'
 | 
			
		||||
          },
 | 
			
		||||
          requestConfigArg
 | 
			
		||||
@ -497,9 +497,9 @@ export const serviceCompletions: TbEditorCompletions = {
 | 
			
		||||
  },
 | 
			
		||||
  entityViewService: {
 | 
			
		||||
    description: 'EntityView Service API<br>' +
 | 
			
		||||
      'See <a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/core/http/entity-view.service.ts#L29">EntityViewService</a> for API reference.',
 | 
			
		||||
      'See <a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/core/http/entity-view.service.ts#L29" target="_blank">EntityViewService</a> for API reference.',
 | 
			
		||||
    meta: 'service',
 | 
			
		||||
    type: '<a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/core/http/entity-view.service.ts#L29">EntityViewService</a>',
 | 
			
		||||
    type: '<a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/core/http/entity-view.service.ts#L29" target="_blank">EntityViewService</a>',
 | 
			
		||||
    children: {
 | 
			
		||||
      getTenantEntityViewInfos: {
 | 
			
		||||
        description: 'Get tenant entity view infos',
 | 
			
		||||
@ -564,7 +564,7 @@ export const serviceCompletions: TbEditorCompletions = {
 | 
			
		||||
        args: [
 | 
			
		||||
          requestConfigArg
 | 
			
		||||
        ],
 | 
			
		||||
        return: observableArrayReturnType('<a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/shared/models/entity-type.models.ts#L295">EntitySubtype</a>')
 | 
			
		||||
        return: observableArrayReturnType('<a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/shared/models/entity-type.models.ts#L295" target="_blank">EntitySubtype</a>')
 | 
			
		||||
      },
 | 
			
		||||
      makeEntityViewPublic: {
 | 
			
		||||
        description: 'Make entity view public (available from public dashboard)',
 | 
			
		||||
@ -600,7 +600,7 @@ export const serviceCompletions: TbEditorCompletions = {
 | 
			
		||||
        args: [
 | 
			
		||||
          {
 | 
			
		||||
            name: 'query',
 | 
			
		||||
            type: '<a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/shared/models/asset.models.ts#L37">AssetSearchQuery</a>',
 | 
			
		||||
            type: '<a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/shared/models/asset.models.ts#L37" target="_blank">AssetSearchQuery</a>',
 | 
			
		||||
            description: 'Entity view search query object'
 | 
			
		||||
          },
 | 
			
		||||
          requestConfigArg
 | 
			
		||||
@ -611,9 +611,9 @@ export const serviceCompletions: TbEditorCompletions = {
 | 
			
		||||
  },
 | 
			
		||||
  customerService: {
 | 
			
		||||
    description: 'Customer Service API<br>' +
 | 
			
		||||
      'See <a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/core/http/customer.service.ts#L28">CustomerService</a> for API reference.',
 | 
			
		||||
      'See <a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/core/http/customer.service.ts#L28" target="_blank">CustomerService</a> for API reference.',
 | 
			
		||||
    meta: 'service',
 | 
			
		||||
    type: '<a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/core/http/customer.service.ts#L28">CustomerService</a>',
 | 
			
		||||
    type: '<a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/core/http/customer.service.ts#L28" target="_blank">CustomerService</a>',
 | 
			
		||||
    children: {
 | 
			
		||||
      getCustomer: {
 | 
			
		||||
        description: 'Get customer by id',
 | 
			
		||||
@ -655,9 +655,9 @@ export const serviceCompletions: TbEditorCompletions = {
 | 
			
		||||
  },
 | 
			
		||||
  dashboardService: {
 | 
			
		||||
    description: 'Dashboard Service API<br>' +
 | 
			
		||||
      'See <a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/core/http/dashboard.service.ts#L32">DashboardService</a> for API reference.',
 | 
			
		||||
      'See <a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/core/http/dashboard.service.ts#L32" target="_blank">DashboardService</a> for API reference.',
 | 
			
		||||
    meta: 'service',
 | 
			
		||||
    type: '<a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/core/http/dashboard.service.ts#L32">DashboardService</a>',
 | 
			
		||||
    type: '<a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/core/http/dashboard.service.ts#L32" target="_blank">DashboardService</a>',
 | 
			
		||||
    children: {
 | 
			
		||||
      getTenantDashboards: {
 | 
			
		||||
        description: 'Get tenant dashboards',
 | 
			
		||||
@ -814,9 +814,9 @@ export const serviceCompletions: TbEditorCompletions = {
 | 
			
		||||
  },
 | 
			
		||||
  userService: {
 | 
			
		||||
    description: 'User Service API<br>' +
 | 
			
		||||
      'See <a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/core/http/user.service.ts#L29">UserService</a> for API reference.',
 | 
			
		||||
      'See <a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/core/http/user.service.ts#L29" target="_blank">UserService</a> for API reference.',
 | 
			
		||||
    meta: 'service',
 | 
			
		||||
    type: '<a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/core/http/user.service.ts#L29">UserService</a>',
 | 
			
		||||
    type: '<a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/core/http/user.service.ts#L29" target="_blank">UserService</a>',
 | 
			
		||||
    children: {
 | 
			
		||||
      getUsers: {
 | 
			
		||||
        description: 'Get  users',
 | 
			
		||||
@ -907,9 +907,9 @@ export const serviceCompletions: TbEditorCompletions = {
 | 
			
		||||
  },
 | 
			
		||||
  entityRelationService: {
 | 
			
		||||
    description: 'Entity Relation Service API<br>' +
 | 
			
		||||
      'See <a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/core/http/entity-relation.service.ts#L27">EntityRelationService</a> for API reference.',
 | 
			
		||||
      'See <a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/core/http/entity-relation.service.ts#L27" target="_blank">EntityRelationService</a> for API reference.',
 | 
			
		||||
    meta: 'service',
 | 
			
		||||
    type: '<a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/core/http/entity-relation.service.ts#L27">EntityRelationService</a>',
 | 
			
		||||
    type: '<a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/core/http/entity-relation.service.ts#L27" target="_blank">EntityRelationService</a>',
 | 
			
		||||
    children: {
 | 
			
		||||
      saveRelation: {
 | 
			
		||||
        description: 'Save relation',
 | 
			
		||||
@ -1029,9 +1029,9 @@ export const serviceCompletions: TbEditorCompletions = {
 | 
			
		||||
  },
 | 
			
		||||
  attributeService: {
 | 
			
		||||
    description: 'Attribute Service API<br>' +
 | 
			
		||||
      'See <a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/core/http/attribute.service.ts#L28">AttributeService</a> for API reference.',
 | 
			
		||||
      'See <a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/core/http/attribute.service.ts#L28" target="_blank">AttributeService</a> for API reference.',
 | 
			
		||||
    meta: 'service',
 | 
			
		||||
    type: '<a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/core/http/attribute.service.ts#L28">AttributeService</a>',
 | 
			
		||||
    type: '<a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/core/http/attribute.service.ts#L28" target="_blank">AttributeService</a>',
 | 
			
		||||
    children: {
 | 
			
		||||
      getEntityAttributes: {
 | 
			
		||||
        description: 'Get entity attributes by id',
 | 
			
		||||
@ -1109,9 +1109,9 @@ export const serviceCompletions: TbEditorCompletions = {
 | 
			
		||||
  },
 | 
			
		||||
  entityService: {
 | 
			
		||||
    description: 'Entity Service API<br>' +
 | 
			
		||||
      'See <a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/core/http/entity.service.ts#L64">EntityService</a> for API reference.',
 | 
			
		||||
      'See <a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/core/http/entity.service.ts#L64" target="_blank">EntityService</a> for API reference.',
 | 
			
		||||
    meta: 'service',
 | 
			
		||||
    type: '<a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/core/http/entity.service.ts#L64">EntityService</a>',
 | 
			
		||||
    type: '<a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/core/http/entity.service.ts#L64" target="_blank">EntityService</a>',
 | 
			
		||||
    children: {
 | 
			
		||||
      getEntity: {
 | 
			
		||||
        description: 'Get entity by id',
 | 
			
		||||
@ -1304,9 +1304,9 @@ export const serviceCompletions: TbEditorCompletions = {
 | 
			
		||||
  },
 | 
			
		||||
  resourceService: {
 | 
			
		||||
    description: 'Resource Service API<br>' +
 | 
			
		||||
      'See <a href="https://github.com/thingsboard/thingsboard/blob/b033b51712244d08e0f5e0beb8be60c9f8fa4cd2/ui-ngx/src/app/core/http/resource.service.ts#L29">ResourceService</a> for API reference.',
 | 
			
		||||
      'See <a href="https://github.com/thingsboard/thingsboard/blob/b033b51712244d08e0f5e0beb8be60c9f8fa4cd2/ui-ngx/src/app/core/http/resource.service.ts#L29" target="_blank">ResourceService</a> for API reference.',
 | 
			
		||||
    meta: 'service',
 | 
			
		||||
    type: '<a href="https://github.com/thingsboard/thingsboard/blob/b033b51712244d08e0f5e0beb8be60c9f8fa4cd2/ui-ngx/src/app/core/http/resource.service.ts#L29">ResourceService</a>',
 | 
			
		||||
    type: '<a href="https://github.com/thingsboard/thingsboard/blob/b033b51712244d08e0f5e0beb8be60c9f8fa4cd2/ui-ngx/src/app/core/http/resource.service.ts#L29" target="_blank">ResourceService</a>',
 | 
			
		||||
    children: {
 | 
			
		||||
      getResources: {
 | 
			
		||||
        description: 'Find resources by search text',
 | 
			
		||||
@ -1321,9 +1321,9 @@ export const serviceCompletions: TbEditorCompletions = {
 | 
			
		||||
  },
 | 
			
		||||
  dialogs: {
 | 
			
		||||
    description: 'Dialogs Service API<br>' +
 | 
			
		||||
      'See <a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/core/services/dialog.service.ts#L39">DialogService</a> for API reference.',
 | 
			
		||||
      'See <a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/core/services/dialog.service.ts#L39" target="_blank">DialogService</a> for API reference.',
 | 
			
		||||
    meta: 'service',
 | 
			
		||||
    type: '<a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/core/services/dialog.service.ts#L39">DialogService</a>',
 | 
			
		||||
    type: '<a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/core/services/dialog.service.ts#L39" target="_blank">DialogService</a>',
 | 
			
		||||
    children: {
 | 
			
		||||
      confirm: {
 | 
			
		||||
        description: 'Confirm',
 | 
			
		||||
@ -1382,9 +1382,9 @@ export const serviceCompletions: TbEditorCompletions = {
 | 
			
		||||
  },
 | 
			
		||||
  customDialog: {
 | 
			
		||||
    description: 'Custom Dialog Service API<br>' +
 | 
			
		||||
      'See <a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/modules/home/components/widget/dialog/custom-dialog.service.ts#L33">CustomDialogService</a> for API reference.',
 | 
			
		||||
      'See <a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/modules/home/components/widget/dialog/custom-dialog.service.ts#L33" target="_blank">CustomDialogService</a> for API reference.',
 | 
			
		||||
    meta: 'service',
 | 
			
		||||
    type: '<a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/modules/home/components/widget/dialog/custom-dialog.service.ts#L33">CustomDialogService</a>',
 | 
			
		||||
    type: '<a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/modules/home/components/widget/dialog/custom-dialog.service.ts#L33" target="_blank">CustomDialogService</a>',
 | 
			
		||||
    children: {
 | 
			
		||||
      customDialog: {
 | 
			
		||||
        description: 'Custom Dialog',
 | 
			
		||||
@ -1400,32 +1400,32 @@ export const serviceCompletions: TbEditorCompletions = {
 | 
			
		||||
  },
 | 
			
		||||
  date: {
 | 
			
		||||
    description: 'Date Pipe<br>Formats a date value according to locale rules.<br>' +
 | 
			
		||||
      'See <a href="https://angular.io/api/common/DatePipe">DatePipe</a> for API reference.',
 | 
			
		||||
      'See <a href="https://angular.io/api/common/DatePipe" target="_blank">DatePipe</a> for API reference.',
 | 
			
		||||
    meta: 'service',
 | 
			
		||||
    type: '<a href="https://angular.io/api/common/DatePipe">DatePipe</a>'
 | 
			
		||||
    type: '<a href="https://angular.io/api/common/DatePipe" target="_blank">DatePipe</a>'
 | 
			
		||||
  },
 | 
			
		||||
  translate: {
 | 
			
		||||
    description: 'Translate Service API<br>' +
 | 
			
		||||
      'See <a href="https://github.com/ngx-translate/core#translateservice">TranslateService</a> for API reference.',
 | 
			
		||||
      'See <a href="https://github.com/ngx-translate/core#translateservice" target="_blank">TranslateService</a> for API reference.',
 | 
			
		||||
    meta: 'service',
 | 
			
		||||
    type: '<a href="https://github.com/ngx-translate/core#translateservice">TranslateService</a>'
 | 
			
		||||
    type: '<a href="https://github.com/ngx-translate/core#translateservice" target="_blank">TranslateService</a>'
 | 
			
		||||
  },
 | 
			
		||||
  http: {
 | 
			
		||||
    description: 'HTTP Client Service<br>' +
 | 
			
		||||
      'See <a href="https://angular.io/api/common/http/HttpClient">HttpClient</a> for API reference.',
 | 
			
		||||
      'See <a href="https://angular.io/api/common/http/HttpClient" target="_blank">HttpClient</a> for API reference.',
 | 
			
		||||
    meta: 'service',
 | 
			
		||||
    type: '<a href="https://angular.io/api/common/http/HttpClient">HttpClient</a>'
 | 
			
		||||
    type: '<a href="https://angular.io/api/common/http/HttpClient" target="_blank">HttpClient</a>'
 | 
			
		||||
  },
 | 
			
		||||
  sanitizer: {
 | 
			
		||||
    description: 'DomSanitizer Service<br>' +
 | 
			
		||||
      'See <a href="https://angular.io/api/platform-browser/DomSanitizer">DomSanitizer</a> for API reference.',
 | 
			
		||||
      'See <a href="https://angular.io/api/platform-browser/DomSanitizer" target="_blank">DomSanitizer</a> for API reference.',
 | 
			
		||||
    meta: 'service',
 | 
			
		||||
    type: '<a href="https://angular.io/api/platform-browser/DomSanitizer">DomSanitizer</a>'
 | 
			
		||||
    type: '<a href="https://angular.io/api/platform-browser/DomSanitizer" target="_blank">DomSanitizer</a>'
 | 
			
		||||
  },
 | 
			
		||||
  router: {
 | 
			
		||||
    description: 'Router Service<br>' +
 | 
			
		||||
      'See <a href="https://angular.io/api/router/Router">Router</a> for API reference.',
 | 
			
		||||
      'See <a href="https://angular.io/api/router/Router" target="_blank">Router</a> for API reference.',
 | 
			
		||||
    meta: 'service',
 | 
			
		||||
    type: '<a href="https://angular.io/api/router/Router">Router</a>'
 | 
			
		||||
    type: '<a href="https://angular.io/api/router/Router" target="_blank">Router</a>'
 | 
			
		||||
  }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@ -20,7 +20,7 @@ import { entityIdHref, serviceCompletions } from '@shared/models/ace/service-com
 | 
			
		||||
export const timewindowCompletion: TbEditorCompletion = {
 | 
			
		||||
  description: 'Timewindow configuration object',
 | 
			
		||||
  meta: 'property',
 | 
			
		||||
  type: '<a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/shared/models/time/time.models.ts#L80">Timewindow</a>',
 | 
			
		||||
  type: '<a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/shared/models/time/time.models.ts#L80" target="_blank">Timewindow</a>',
 | 
			
		||||
  children: {
 | 
			
		||||
    displayValue: {
 | 
			
		||||
      description: 'Current timewindow display value.',
 | 
			
		||||
@ -50,7 +50,7 @@ export const timewindowCompletion: TbEditorCompletion = {
 | 
			
		||||
    realtime: {
 | 
			
		||||
      description: 'Realtime timewindow configuration object.',
 | 
			
		||||
      meta: 'property',
 | 
			
		||||
      type: '<a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/shared/models/time/time.models.ts#L39">IntervalWindow</a>',
 | 
			
		||||
      type: '<a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/shared/models/time/time.models.ts#L39" target="_blank">IntervalWindow</a>',
 | 
			
		||||
      children: {
 | 
			
		||||
        interval: {
 | 
			
		||||
          description: 'Timewindow aggregation interval in milliseconds',
 | 
			
		||||
@ -67,7 +67,7 @@ export const timewindowCompletion: TbEditorCompletion = {
 | 
			
		||||
    history: {
 | 
			
		||||
      description: 'History timewindow configuration object.',
 | 
			
		||||
      meta: 'property',
 | 
			
		||||
      type: '<a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/shared/models/time/time.models.ts#L49">HistoryWindow</a>',
 | 
			
		||||
      type: '<a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/shared/models/time/time.models.ts#L49" target="_blank">HistoryWindow</a>',
 | 
			
		||||
      children: {
 | 
			
		||||
        historyType: {
 | 
			
		||||
          description: 'History timewindow type (0 - last interval, 1 - fixed)',
 | 
			
		||||
@ -87,7 +87,7 @@ export const timewindowCompletion: TbEditorCompletion = {
 | 
			
		||||
        fixedTimewindow: {
 | 
			
		||||
          description: 'Fixed history timewindow configuration object',
 | 
			
		||||
          meta: 'property',
 | 
			
		||||
          type: '<a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/shared/models/time/time.models.ts#L44">FixedWindow</a>',
 | 
			
		||||
          type: '<a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/shared/models/time/time.models.ts#L44" target="_blank">FixedWindow</a>',
 | 
			
		||||
          children: {
 | 
			
		||||
            startTimeMs: {
 | 
			
		||||
              description: 'Timewindow start time in UTC milliseconds',
 | 
			
		||||
@ -106,7 +106,7 @@ export const timewindowCompletion: TbEditorCompletion = {
 | 
			
		||||
    aggregation: {
 | 
			
		||||
      description: 'Timewindow aggregation configuration object.',
 | 
			
		||||
      meta: 'property',
 | 
			
		||||
      type: '<a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/shared/models/time/time.models.ts#L74">Aggregation</a>',
 | 
			
		||||
      type: '<a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/shared/models/time/time.models.ts#L74" target="_blank">Aggregation</a>',
 | 
			
		||||
      children: {
 | 
			
		||||
        interval: {
 | 
			
		||||
          description: 'Aggregation interval in milliseconds',
 | 
			
		||||
@ -116,7 +116,7 @@ export const timewindowCompletion: TbEditorCompletion = {
 | 
			
		||||
        type: {
 | 
			
		||||
          description: 'Aggregation type',
 | 
			
		||||
          meta: 'property',
 | 
			
		||||
          type: '<a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/shared/models/time/time.models.ts#L54">AggregationType</a>'
 | 
			
		||||
          type: '<a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/shared/models/time/time.models.ts#L54" target="_blank">AggregationType</a>'
 | 
			
		||||
        },
 | 
			
		||||
        limit: {
 | 
			
		||||
          description: 'Maximum allowed datapoints when aggregation is disabled (<code>AggregationType == \'NONE\'</code>)',
 | 
			
		||||
@ -132,7 +132,7 @@ export const widgetContextCompletions: TbEditorCompletions = {
 | 
			
		||||
  ctx: {
 | 
			
		||||
    description: 'A reference to widget context that has all necessary API<br>and data used by widget instance.',
 | 
			
		||||
    meta: 'object',
 | 
			
		||||
    type: '<a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/modules/home/models/widget-component.models.ts#L83">WidgetContext</a>',
 | 
			
		||||
    type: '<a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/modules/home/models/widget-component.models.ts#L83" target="_blank">WidgetContext</a>',
 | 
			
		||||
    children: {
 | 
			
		||||
      ...{
 | 
			
		||||
        $container: {
 | 
			
		||||
@ -143,7 +143,7 @@ export const widgetContextCompletions: TbEditorCompletions = {
 | 
			
		||||
        $scope: {
 | 
			
		||||
          description: 'Reference to the current widget component.<br>Can be used to access/modify component properties when widget is built using Angular approach.',
 | 
			
		||||
          meta: 'property',
 | 
			
		||||
          type: '<a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/modules/home/models/widget-component.models.ts#L274">IDynamicWidgetComponent</a>'
 | 
			
		||||
          type: '<a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/modules/home/models/widget-component.models.ts#L274" target="_blank">IDynamicWidgetComponent</a>'
 | 
			
		||||
        },
 | 
			
		||||
        width: {
 | 
			
		||||
          description: 'Current width of widget container in pixels.',
 | 
			
		||||
@ -168,7 +168,7 @@ export const widgetContextCompletions: TbEditorCompletions = {
 | 
			
		||||
        widgetConfig: {
 | 
			
		||||
          description: 'Common widget configuration containing properties such as <code>color</code> (text color), <code>backgroundColor</code> (widget background color), etc.',
 | 
			
		||||
          meta: 'property',
 | 
			
		||||
          type: '<a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/shared/models/widget.models.ts#L341">WidgetConfig</a>',
 | 
			
		||||
          type: '<a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/shared/models/widget.models.ts#L341" target="_blank">WidgetConfig</a>',
 | 
			
		||||
          children: {
 | 
			
		||||
            title: {
 | 
			
		||||
              description: 'Widget title.',
 | 
			
		||||
@ -233,17 +233,17 @@ export const widgetContextCompletions: TbEditorCompletions = {
 | 
			
		||||
            legendConfig: {
 | 
			
		||||
              description: 'Legend configuration.',
 | 
			
		||||
              meta: 'property',
 | 
			
		||||
              type: '<a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/shared/models/widget.models.ts#L198">LegendConfig</a>',
 | 
			
		||||
              type: '<a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/shared/models/widget.models.ts#L198" target="_blank">LegendConfig</a>',
 | 
			
		||||
              children: {
 | 
			
		||||
                position: {
 | 
			
		||||
                  description: 'Legend position. Possible values: <code>\'top\', \'bottom\', \'left\', \'right\'</code>',
 | 
			
		||||
                  meta: 'property',
 | 
			
		||||
                  type: '<a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/shared/models/widget.models.ts#L182">LegendPosition</a>',
 | 
			
		||||
                  type: '<a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/shared/models/widget.models.ts#L182" target="_blank">LegendPosition</a>',
 | 
			
		||||
                },
 | 
			
		||||
                direction: {
 | 
			
		||||
                  description: 'Legend direction. Possible values: <code>\'column\', \'row\'</code>',
 | 
			
		||||
                  meta: 'property',
 | 
			
		||||
                  type: '<a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/shared/models/widget.models.ts#L170">LegendDirection</a>',
 | 
			
		||||
                  type: '<a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/shared/models/widget.models.ts#L170" target="_blank">LegendDirection</a>',
 | 
			
		||||
                },
 | 
			
		||||
                showMin: {
 | 
			
		||||
                  description: 'Whether to display aggregated min values.',
 | 
			
		||||
@ -331,12 +331,12 @@ export const widgetContextCompletions: TbEditorCompletions = {
 | 
			
		||||
            alarmSource: {
 | 
			
		||||
              description: 'Configured alarm source for alarm widget type.',
 | 
			
		||||
              meta: 'property',
 | 
			
		||||
              type: '<a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/shared/models/widget.models.ts#L250">Datasource</a>'
 | 
			
		||||
              type: '<a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/shared/models/widget.models.ts#L250" target="_blank">Datasource</a>'
 | 
			
		||||
            },
 | 
			
		||||
            alarmSearchStatus: {
 | 
			
		||||
              description: 'Configured default alarm search status for alarm widget type.',
 | 
			
		||||
              meta: 'property',
 | 
			
		||||
              type: '<a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/shared/models/alarm.models.ts#L41">AlarmSearchStatus</a>'
 | 
			
		||||
              type: '<a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/shared/models/alarm.models.ts#L41" target="_blank">AlarmSearchStatus</a>'
 | 
			
		||||
            },
 | 
			
		||||
            alarmsPollingInterval: {
 | 
			
		||||
              description: 'Configured alarms polling interval for alarm widget type.',
 | 
			
		||||
@ -356,29 +356,29 @@ export const widgetContextCompletions: TbEditorCompletions = {
 | 
			
		||||
            datasources: {
 | 
			
		||||
              description: 'Array of configured widget datasources.',
 | 
			
		||||
              meta: 'property',
 | 
			
		||||
              type: 'Array<<a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/shared/models/widget.models.ts#L250">Datasource</a>>'
 | 
			
		||||
              type: 'Array<<a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/shared/models/widget.models.ts#L250" target="_blank">Datasource</a>>'
 | 
			
		||||
            }
 | 
			
		||||
          }
 | 
			
		||||
        },
 | 
			
		||||
        settings: {
 | 
			
		||||
          description: 'Widget settings containing widget specific properties according to the defined <a href="https://thingsboard.io/docs/user-guide/contribution/widgets-development/#settings-schema-section">settings json schema</a>',
 | 
			
		||||
          description: 'Widget settings containing widget specific properties according to the defined <a href="https://thingsboard.io/docs/user-guide/contribution/widgets-development/#settings-schema-section" target="_blank">settings json schema</a>',
 | 
			
		||||
          meta: 'property',
 | 
			
		||||
          type: 'object'
 | 
			
		||||
        },
 | 
			
		||||
        datasources: {
 | 
			
		||||
          description: 'Array of resolved widget datasources.',
 | 
			
		||||
          meta: 'property',
 | 
			
		||||
          type: 'Array<<a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/shared/models/widget.models.ts#L250">Datasource</a>>'
 | 
			
		||||
          type: 'Array<<a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/shared/models/widget.models.ts#L250" target="_blank">Datasource</a>>'
 | 
			
		||||
        },
 | 
			
		||||
        data: {
 | 
			
		||||
          description: 'Array of latest datasources data.',
 | 
			
		||||
          meta: 'property',
 | 
			
		||||
          type: 'Array<<a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/shared/models/widget.models.ts#L275">DatasourceData</a>>'
 | 
			
		||||
          type: 'Array<<a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/shared/models/widget.models.ts#L275" target="_blank">DatasourceData</a>>'
 | 
			
		||||
        },
 | 
			
		||||
        timeWindow: {
 | 
			
		||||
          description: 'Current widget timewindow (applicable for timeseries widgets).',
 | 
			
		||||
          meta: 'property',
 | 
			
		||||
          type: '<a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/shared/models/time/time.models.ts#L104">WidgetTimewindow</a>'
 | 
			
		||||
          type: '<a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/shared/models/time/time.models.ts#L104" target="_blank">WidgetTimewindow</a>'
 | 
			
		||||
        },
 | 
			
		||||
        units: {
 | 
			
		||||
          description: 'Optional property defining units text of values displayed by widget. Useful for simple widgets like cards or gauges.',
 | 
			
		||||
@ -393,7 +393,7 @@ export const widgetContextCompletions: TbEditorCompletions = {
 | 
			
		||||
        currentUser: {
 | 
			
		||||
          description: 'Current user object.',
 | 
			
		||||
          meta: 'property',
 | 
			
		||||
          type: '<a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/shared/models/user.model.ts#L45">AuthUser</a>',
 | 
			
		||||
          type: '<a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/shared/models/user.model.ts#L45" target="_blank">AuthUser</a>',
 | 
			
		||||
          children: {
 | 
			
		||||
            sub: {
 | 
			
		||||
              description: 'User subject (email).',
 | 
			
		||||
@ -443,7 +443,7 @@ export const widgetContextCompletions: TbEditorCompletions = {
 | 
			
		||||
            authority: {
 | 
			
		||||
              description: 'User authority. Possible values: SYS_ADMIN, TENANT_ADMIN, CUSTOMER_USER',
 | 
			
		||||
              meta: 'property',
 | 
			
		||||
              type: '<a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/shared/models/authority.enum.ts#L17">Authority</a>'
 | 
			
		||||
              type: '<a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/shared/models/authority.enum.ts#L17" target="_blank">Authority</a>'
 | 
			
		||||
            }
 | 
			
		||||
          }
 | 
			
		||||
        },
 | 
			
		||||
@ -468,12 +468,12 @@ export const widgetContextCompletions: TbEditorCompletions = {
 | 
			
		||||
        defaultSubscription: {
 | 
			
		||||
          description: 'Default widget subscription object contains all subscription information,<br>including current data, according to the widget type.',
 | 
			
		||||
          meta: 'property',
 | 
			
		||||
          type: '<a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/core/api/widget-api.models.ts#L220">IWidgetSubscription</a>'
 | 
			
		||||
          type: '<a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/core/api/widget-api.models.ts#L220" target="_blank">IWidgetSubscription</a>'
 | 
			
		||||
        },
 | 
			
		||||
        timewindowFunctions: {
 | 
			
		||||
          description: 'Object with timewindow functions used to manage widget data time frame. Can by used by Time-series or Alarm widgets.',
 | 
			
		||||
          meta: 'property',
 | 
			
		||||
          type: '<a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/core/api/widget-api.models.ts#L45">TimewindowFunctions</a>',
 | 
			
		||||
          type: '<a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/core/api/widget-api.models.ts#L45" target="_blank">TimewindowFunctions</a>',
 | 
			
		||||
          children: {
 | 
			
		||||
            onUpdateTimewindow: {
 | 
			
		||||
              description: 'This function can be used to update current subscription time frame<br>to historical one identified by <code>startTimeMs</code> and <code>endTimeMs</code> arguments.',
 | 
			
		||||
@ -500,7 +500,7 @@ export const widgetContextCompletions: TbEditorCompletions = {
 | 
			
		||||
        controlApi: {
 | 
			
		||||
          description: 'Object that provides API functions for RPC (Control) widgets.',
 | 
			
		||||
          meta: 'property',
 | 
			
		||||
          type: '<a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/core/api/widget-api.models.ts#L58">RpcApi</a>',
 | 
			
		||||
          type: '<a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/core/api/widget-api.models.ts#L58" target="_blank">RpcApi</a>',
 | 
			
		||||
          children: {
 | 
			
		||||
            sendOneWayCommand: {
 | 
			
		||||
              description: 'Sends one way (without response) RPC command to the device.',
 | 
			
		||||
@ -585,7 +585,7 @@ export const widgetContextCompletions: TbEditorCompletions = {
 | 
			
		||||
        actionsApi: {
 | 
			
		||||
          description: 'Set of API functions to work with user defined actions.',
 | 
			
		||||
          meta: 'property',
 | 
			
		||||
          type: '<a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/core/api/widget-api.models.ts#L67">WidgetActionsApi</a>',
 | 
			
		||||
          type: '<a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/core/api/widget-api.models.ts#L67" target="_blank">WidgetActionsApi</a>',
 | 
			
		||||
          children: {
 | 
			
		||||
            getActionDescriptors: {
 | 
			
		||||
              description: 'Get list of action descriptors for provided <code>actionSourceId</code>.',
 | 
			
		||||
@ -599,7 +599,7 @@ export const widgetContextCompletions: TbEditorCompletions = {
 | 
			
		||||
              ],
 | 
			
		||||
              return: {
 | 
			
		||||
                description: 'The list of action descriptors',
 | 
			
		||||
                type: 'Array<<a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/shared/models/widget.models.ts#L323">WidgetActionDescriptor</a>>'
 | 
			
		||||
                type: 'Array<<a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/shared/models/widget.models.ts#L323" target="_blank">WidgetActionDescriptor</a>>'
 | 
			
		||||
              }
 | 
			
		||||
            },
 | 
			
		||||
            handleWidgetAction: {
 | 
			
		||||
@ -614,7 +614,7 @@ export const widgetContextCompletions: TbEditorCompletions = {
 | 
			
		||||
                {
 | 
			
		||||
                  name: 'descriptor',
 | 
			
		||||
                  description: 'An action descriptor.',
 | 
			
		||||
                  type: '<a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/shared/models/widget.models.ts#L323">WidgetActionDescriptor</a>'
 | 
			
		||||
                  type: '<a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/shared/models/widget.models.ts#L323" target="_blank">WidgetActionDescriptor</a>'
 | 
			
		||||
                },
 | 
			
		||||
                {
 | 
			
		||||
                  name: 'entityId',
 | 
			
		||||
@ -635,7 +635,7 @@ export const widgetContextCompletions: TbEditorCompletions = {
 | 
			
		||||
        stateController: {
 | 
			
		||||
          description: 'Reference to Dashboard state controller, providing API to manage current dashboard state.',
 | 
			
		||||
          meta: 'property',
 | 
			
		||||
          type: '<a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/core/api/widget-api.models.ts#L121">IStateController</a>',
 | 
			
		||||
          type: '<a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/core/api/widget-api.models.ts#L121" target="_blank">IStateController</a>',
 | 
			
		||||
          children: {
 | 
			
		||||
            openState: {
 | 
			
		||||
              description: 'Navigate to new dashboard state.',
 | 
			
		||||
@ -649,7 +649,7 @@ export const widgetContextCompletions: TbEditorCompletions = {
 | 
			
		||||
                {
 | 
			
		||||
                  name: 'params',
 | 
			
		||||
                  description: 'An object with state parameters to use by the new state.',
 | 
			
		||||
                  type: '<a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/core/api/widget-api.models.ts#L111">StateParams</a>',
 | 
			
		||||
                  type: '<a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/core/api/widget-api.models.ts#L111" target="_blank">StateParams</a>',
 | 
			
		||||
                  optional: true
 | 
			
		||||
                },
 | 
			
		||||
                {
 | 
			
		||||
@ -667,7 +667,7 @@ export const widgetContextCompletions: TbEditorCompletions = {
 | 
			
		||||
                {
 | 
			
		||||
                  name: 'id',
 | 
			
		||||
                  description: 'An array state object of the target dashboard state.',
 | 
			
		||||
                  type: 'Array <a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/core/api/widget-api.models.ts#L140">StateObject</a>',
 | 
			
		||||
                  type: 'Array <a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/core/api/widget-api.models.ts#L140" target="_blank">StateObject</a>',
 | 
			
		||||
                },
 | 
			
		||||
                {
 | 
			
		||||
                  name: 'openRightLayout',
 | 
			
		||||
@ -690,7 +690,7 @@ export const widgetContextCompletions: TbEditorCompletions = {
 | 
			
		||||
                {
 | 
			
		||||
                  name: 'params',
 | 
			
		||||
                  description: 'An object with state parameters to update current state parameters.',
 | 
			
		||||
                  type: '<a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/core/api/widget-api.models.ts#L111">StateParams</a>',
 | 
			
		||||
                  type: '<a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/core/api/widget-api.models.ts#L111" target="_blank">StateParams</a>',
 | 
			
		||||
                  optional: true
 | 
			
		||||
                },
 | 
			
		||||
                {
 | 
			
		||||
@ -714,7 +714,7 @@ export const widgetContextCompletions: TbEditorCompletions = {
 | 
			
		||||
              meta: 'function',
 | 
			
		||||
              return: {
 | 
			
		||||
                description: 'current dashboard state parameters.',
 | 
			
		||||
                type: '<a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/core/api/widget-api.models.ts#L111">StateParams</a>'
 | 
			
		||||
                type: '<a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/core/api/widget-api.models.ts#L111" target="_blank">StateParams</a>'
 | 
			
		||||
              }
 | 
			
		||||
            },
 | 
			
		||||
            getStateParamsByStateId: {
 | 
			
		||||
@ -729,7 +729,7 @@ export const widgetContextCompletions: TbEditorCompletions = {
 | 
			
		||||
              ],
 | 
			
		||||
              return: {
 | 
			
		||||
                description: 'current dashboard state parameters.',
 | 
			
		||||
                type: '<a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/core/api/widget-api.models.ts#L111">StateParams</a>'
 | 
			
		||||
                type: '<a href="https://github.com/thingsboard/thingsboard/blob/13e6b10b7ab830e64d31b99614a9d95a1a25928a/ui-ngx/src/app/core/api/widget-api.models.ts#L111" target="_blank">StateParams</a>'
 | 
			
		||||
              }
 | 
			
		||||
            }
 | 
			
		||||
          }
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										80
									
								
								ui-ngx/src/app/shared/models/jquery-event.models.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										80
									
								
								ui-ngx/src/app/shared/models/jquery-event.models.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,80 @@
 | 
			
		||||
///
 | 
			
		||||
/// Copyright © 2016-2024 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 Timeout = NodeJS.Timeout;
 | 
			
		||||
 | 
			
		||||
export interface TbContextMenuEvent extends Event {
 | 
			
		||||
  clientX: number;
 | 
			
		||||
  clientY: number;
 | 
			
		||||
  ctrlKey: boolean;
 | 
			
		||||
  metaKey: boolean;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const isIOSDevice = (): boolean =>
 | 
			
		||||
  /iPhone|iPad|iPod/i.test(navigator.userAgent) || (navigator.userAgent.includes('Mac') && 'ontouchend' in document);
 | 
			
		||||
 | 
			
		||||
export const initCustomJQueryEvents = () => {
 | 
			
		||||
  $.event.special.tbcontextmenu = {
 | 
			
		||||
    setup(this: HTMLElement) {
 | 
			
		||||
      const el = $(this);
 | 
			
		||||
      if (isIOSDevice()) {
 | 
			
		||||
        let timeoutId: Timeout;
 | 
			
		||||
 | 
			
		||||
        el.on('touchstart', (e) => {
 | 
			
		||||
          e.stopPropagation();
 | 
			
		||||
          timeoutId = setTimeout(() => {
 | 
			
		||||
            timeoutId = null;
 | 
			
		||||
            e.stopPropagation();
 | 
			
		||||
            const touch = e.originalEvent.changedTouches[0];
 | 
			
		||||
            const event = $.Event('tbcontextmenu', {
 | 
			
		||||
              clientX: touch.clientX,
 | 
			
		||||
              clientY: touch.clientY,
 | 
			
		||||
              ctrlKey: false,
 | 
			
		||||
              metaKey: false
 | 
			
		||||
            });
 | 
			
		||||
            el.trigger(event, e);
 | 
			
		||||
          }, 500);
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        el.on('touchend touchmove', () => {
 | 
			
		||||
          if (timeoutId) {
 | 
			
		||||
            clearTimeout(timeoutId);
 | 
			
		||||
          }
 | 
			
		||||
        });
 | 
			
		||||
      } else {
 | 
			
		||||
        el.on('contextmenu', (e) => {
 | 
			
		||||
          e.preventDefault();
 | 
			
		||||
          e.stopPropagation();
 | 
			
		||||
          const event = $.Event('tbcontextmenu', {
 | 
			
		||||
            clientX: e.originalEvent.clientX,
 | 
			
		||||
            clientY: e.originalEvent.clientY,
 | 
			
		||||
            ctrlKey: e.originalEvent.ctrlKey,
 | 
			
		||||
            metaKey: e.originalEvent.metaKey,
 | 
			
		||||
          });
 | 
			
		||||
          el.trigger(event, e);
 | 
			
		||||
        });
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    teardown(this: HTMLElement) {
 | 
			
		||||
      const el = $(this);
 | 
			
		||||
      if (isIOSDevice()) {
 | 
			
		||||
        el.off('touchstart touchend touchmove');
 | 
			
		||||
      } else {
 | 
			
		||||
        el.off('contextmenu');
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  };
 | 
			
		||||
};
 | 
			
		||||
@ -68,6 +68,7 @@ import { NgxHmCarouselModule } from 'ngx-hm-carousel';
 | 
			
		||||
import { EditorModule, TINYMCE_SCRIPT_SRC } from '@tinymce/tinymce-angular';
 | 
			
		||||
import { UserMenuComponent } from '@shared/components/user-menu.component';
 | 
			
		||||
import { TruncateWithTooltipDirective } from '@shared/directives/truncate-with-tooltip.directive';
 | 
			
		||||
import { ContextMenuDirective } from '@shared/directives/context-menu.directive';
 | 
			
		||||
import { NospacePipe } from '@shared/pipe/nospace.pipe';
 | 
			
		||||
import { TranslateModule } from '@ngx-translate/core';
 | 
			
		||||
import { TbCheckboxComponent } from '@shared/components/tb-checkbox.component';
 | 
			
		||||
@ -371,6 +372,7 @@ export function MarkedOptionsFactory(markedOptionsService: MarkedOptionsService)
 | 
			
		||||
    LedLightComponent,
 | 
			
		||||
    MarkdownEditorComponent,
 | 
			
		||||
    TruncateWithTooltipDirective,
 | 
			
		||||
    ContextMenuDirective,
 | 
			
		||||
    NospacePipe,
 | 
			
		||||
    MillisecondsToTimeStringPipe,
 | 
			
		||||
    EnumToArrayPipe,
 | 
			
		||||
@ -633,6 +635,7 @@ export function MarkedOptionsFactory(markedOptionsService: MarkedOptionsService)
 | 
			
		||||
    LedLightComponent,
 | 
			
		||||
    MarkdownEditorComponent,
 | 
			
		||||
    TruncateWithTooltipDirective,
 | 
			
		||||
    ContextMenuDirective,
 | 
			
		||||
    NospacePipe,
 | 
			
		||||
    MillisecondsToTimeStringPipe,
 | 
			
		||||
    EnumToArrayPipe,
 | 
			
		||||
 | 
			
		||||
@ -1208,7 +1208,7 @@
 | 
			
		||||
          "dropShadow": false,
 | 
			
		||||
          "enableFullscreen": false,
 | 
			
		||||
          "widgetStyle": {},
 | 
			
		||||
          "widgetCss": " .tb-widget-container > .tb-widget > .tb-widget-content .tb-widget-title > .title-row > .title {\n    padding: 0;\n}\n\n .tb-widget-container > .tb-widget > .tb-widget-content .tb-widget-title > .title-row > .title {\n    padding-bottom: 8px;\n    font-weight: 600;\n    font-size: 20px;\n    line-height: 24px;\n    letter-spacing: 0.1px;\n    color: rgba(0, 0, 0, 0.76);\n}\n\n.tb-widget-container > .tb-widget > .tb-widget-content > .tb-widget .tb-mobile-app-qrcode-panel {\n    padding: 0;\n}\n\n@media screen and (min-width: 960px) and (max-width: 1819px) {\n    .tb-widget-container > .tb-widget > .tb-widget-content .tb-widget-title > .title-row > .title {\n        padding-bottom: 0;\n        font-weight: 500;\n        font-size: 14px;\n        line-height: 20px;\n        letter-spacing: 0.25px;\n  }\n}\n\n@media screen and (min-width: 960px) and (max-width: 1182px) {\n    .tb-widget-container > .tb-widget > .tb-widget-content > .tb-widget .tb-mobile-app-qrcode-panel {\n        gap: 0;\n    }\n    .tb-widget-container > .tb-widget > .tb-widget-content > .tb-widget .tb-mobile-app-qrcode-panel .tb-qrcode {\n        min-width: 150px;\n    }\n}\n\n@media screen and (max-width: 960px) {\n    .tb-widget-container > .tb-widget > .tb-widget-content > .tb-widget .tb-mobile-app-qrcode-panel .tb-qrcode {\n        max-width: 120px;\n    }\n    \n    .tb-widget-container > .tb-widget > .tb-widget-content > .tb-widget .tb-mobile-app-qrcode-panel .tb-badges {\n        max-width: 160px;\n    }\n}\n\n@media screen and (min-width: 1819px) {\n    .tb-widget-container > .tb-widget > .tb-widget-content > .tb-widget .tb-mobile-app-qrcode-panel .tb-qrcode {\n        max-width: 125px;\n    }\n    \n    .tb-widget-container > .tb-widget > .tb-widget-content > .tb-widget .tb-mobile-app-qrcode-panel .tb-badges {\n        max-width: 160px;\n    }\n}\n\n@media screen and (min-width: 960px) and (max-height: 960px) {\n    .tb-widget-container > .tb-widget > .tb-widget-content > .tb-widget .tb-mobile-app-qrcode-panel .tb-qrcode-label {\n        display: none;\n    }\n}",
 | 
			
		||||
          "widgetCss": " .tb-widget-container > .tb-widget > .tb-widget-content .tb-widget-title > .title-row > .title {\n    padding: 0;\n}\n\n .tb-widget-container > .tb-widget > .tb-widget-content .tb-widget-title > .title-row > .title {\n    padding-bottom: 8px;\n    font-weight: 600;\n    font-size: 20px;\n    line-height: 24px;\n    letter-spacing: 0.1px;\n    color: rgba(0, 0, 0, 0.76);\n}\n\n.tb-widget-container > .tb-widget > .tb-widget-content > .tb-widget .tb-mobile-app-qrcode-panel {\n    padding: 0;\n}\n\n@media screen and (min-width: 960px) and (max-width: 1819px) {\n    .tb-widget-container > .tb-widget > .tb-widget-content .tb-widget-title > .title-row > .title {\n        padding-bottom: 0;\n        font-weight: 500;\n        font-size: 14px;\n        line-height: 20px;\n        letter-spacing: 0.25px;\n  }\n}\n\n@media screen and (min-width: 960px) and (max-width: 1182px) {\n    .tb-widget-container > .tb-widget > .tb-widget-content > .tb-widget .tb-mobile-app-qrcode-panel {\n        gap: 0;\n    }\n    .tb-widget-container > .tb-widget > .tb-widget-content > .tb-widget .tb-mobile-app-qrcode-panel .tb-qrcode {\n        min-width: 150px;\n    }\n}\n\n@media screen and (max-width: 960px) {\n    .tb-widget-container > .tb-widget > .tb-widget-content > .tb-widget .tb-mobile-app-qrcode-panel .tb-qrcode {\n        max-width: 120px;\n    }\n    \n    .tb-widget-container > .tb-widget > .tb-widget-content > .tb-widget .tb-mobile-app-qrcode-panel .tb-badges {\n        max-width: 160px;\n    }\n}\n\n@media screen and (min-width: 1819px) {\n    .tb-widget-container > .tb-widget > .tb-widget-content > .tb-widget .tb-mobile-app-qrcode-panel .tb-qrcode {\n        max-width: 125px;\n    }\n    \n    .tb-widget-container > .tb-widget > .tb-widget-content > .tb-widget .tb-mobile-app-qrcode-panel .tb-badges {\n        max-width: 160px;\n    }\n}\n\n@media screen and (min-width: 960px) and (max-height: 965px) {\n    .tb-widget-container > .tb-widget > .tb-widget-content > .tb-widget .tb-mobile-app-qrcode-panel .tb-qrcode-label {\n        display: none;\n    }\n}",
 | 
			
		||||
          "showTitleIcon": false,
 | 
			
		||||
          "titleTooltip": "",
 | 
			
		||||
          "titleStyle": null,
 | 
			
		||||
 | 
			
		||||
@ -3142,10 +3142,11 @@
 | 
			
		||||
            "title": "{{type}} Connector RPC parameters",
 | 
			
		||||
            "templates-title": "Connector RPC Templates",
 | 
			
		||||
            "methodFilter": "Method filter",
 | 
			
		||||
            "method-name": "Method name",
 | 
			
		||||
            "requestTopicExpression": "Request topic expression",
 | 
			
		||||
            "responseTopicExpression": "Response topic expression",
 | 
			
		||||
            "responseTimeout": "Response Time",
 | 
			
		||||
            "valueExpression": "Value Expression",
 | 
			
		||||
            "responseTimeout": "Response timeout",
 | 
			
		||||
            "valueExpression": "Value expression",
 | 
			
		||||
            "tag": "Tag",
 | 
			
		||||
            "type": "Type",
 | 
			
		||||
            "functionCode": "Function Code",
 | 
			
		||||
@ -3184,6 +3185,11 @@
 | 
			
		||||
            "tries": "Tries",
 | 
			
		||||
            "httpHeaders": "HTTP Headers",
 | 
			
		||||
            "header-name": "Header name",
 | 
			
		||||
            "hint": {
 | 
			
		||||
                "modbus-response-reading": "RPC response will return all subtracted values from all connected devices when the reading functions are selected.",
 | 
			
		||||
                "modbus-writing-functions": "RPC will write a filled value to all connected devices when the writing functions are selected.",
 | 
			
		||||
                "opc-method": "A filled method name is the OPC-UA method that will processed on the server side (make sure your node has the requested method)."
 | 
			
		||||
            },
 | 
			
		||||
            "security-name": "Security name",
 | 
			
		||||
            "value": "Value",
 | 
			
		||||
            "security": "Security",
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										3
									
								
								ui-ngx/src/typings/jquery.typings.d.ts
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										3
									
								
								ui-ngx/src/typings/jquery.typings.d.ts
									
									
									
									
										vendored
									
									
								
							@ -14,6 +14,9 @@
 | 
			
		||||
/// limitations under the License.
 | 
			
		||||
///
 | 
			
		||||
 | 
			
		||||
import { TbContextMenuEvent } from '@shared/models/jquery-event.models';
 | 
			
		||||
 | 
			
		||||
interface JQuery {
 | 
			
		||||
  terminal(options?: any): any;
 | 
			
		||||
  on(events: 'tbcontextmenu', handler: (e: TbContextMenuEvent) => void): this;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -3052,10 +3052,10 @@
 | 
			
		||||
  dependencies:
 | 
			
		||||
    "@types/jasmine" "*"
 | 
			
		||||
 | 
			
		||||
"@types/jquery@*", "@types/jquery@^3.5.16", "@types/jquery@^3.5.29":
 | 
			
		||||
  version "3.5.29"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/@types/jquery/-/jquery-3.5.29.tgz#3c06a1f519cd5fc3a7a108971436c00685b5dcea"
 | 
			
		||||
  integrity sha512-oXQQC9X9MOPRrMhPHHOsXqeQDnWeCDT3PelUIg/Oy8FAbzSZtFHRjc7IpbfFVmpLtJ+UOoywpRsuO5Jxjybyeg==
 | 
			
		||||
"@types/jquery@*", "@types/jquery@^3.5.29", "@types/jquery@^3.5.30":
 | 
			
		||||
  version "3.5.30"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/@types/jquery/-/jquery-3.5.30.tgz#888d584cbf844d3df56834b69925085038fd80f7"
 | 
			
		||||
  integrity sha512-nbWKkkyb919DOUxjmRVk8vwtDb0/k8FKncmUKFi+NY+QXqWltooxTrswvz4LspQwxvLdvzBN1TImr6cw3aQx2A==
 | 
			
		||||
  dependencies:
 | 
			
		||||
    "@types/sizzle" "*"
 | 
			
		||||
 | 
			
		||||
@ -7377,7 +7377,7 @@ jquery.terminal@^2.35.3:
 | 
			
		||||
  optionalDependencies:
 | 
			
		||||
    fsevents "^2.3.2"
 | 
			
		||||
 | 
			
		||||
jquery@>=1.9.1, jquery@^3.5.0, jquery@^3.6.3, jquery@^3.7.1:
 | 
			
		||||
jquery@>=1.9.1, jquery@^3.5.0, jquery@^3.7.1:
 | 
			
		||||
  version "3.7.1"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/jquery/-/jquery-3.7.1.tgz#083ef98927c9a6a74d05a6af02806566d16274de"
 | 
			
		||||
  integrity sha512-m4avr8yL8kmFN8psrbFFFmB/If14iN5o9nw/NgnnM+kybDJpRsAynV2BsfpTYrTRysYUdADVD7CkUUizgkpLfg==
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user