24 changed files with 1456 additions and 163 deletions
File diff suppressed because one or more lines are too long
@ -0,0 +1,41 @@ |
|||
<!-- |
|||
|
|||
Copyright © 2016-2022 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. |
|||
|
|||
--> |
|||
<mat-form-field fxFlex class="mat-block" [formGroup]="deviceKeyFormGroup"> |
|||
<mat-label>{{ (keyType === dataKeyType.attribute |
|||
? attributeLabel |
|||
: timeseriesLabel) | translate }}</mat-label> |
|||
<input [required]="required" matInput |
|||
type="text" placeholder="{{ 'entity.key-name' | translate }}" |
|||
#keyInput |
|||
(focusin)="onFocus()" |
|||
formControlName="key" |
|||
[matAutocomplete]="keyAutocomplete"> |
|||
<button *ngIf="deviceKeyFormGroup.get('key').value && !deviceKeyFormGroup.get('key').disabled" |
|||
type="button" |
|||
matSuffix mat-icon-button aria-label="Clear" |
|||
(click)="clearKey()"> |
|||
<mat-icon class="material-icons">close</mat-icon> |
|||
</button> |
|||
<mat-autocomplete |
|||
class="tb-autocomplete" |
|||
#keyAutocomplete="matAutocomplete"> |
|||
<mat-option *ngFor="let key of filteredKeys | async" [value]="key"> |
|||
<span [innerHTML]="key | highlight:keySearchText"></span> |
|||
</mat-option> |
|||
</mat-autocomplete> |
|||
</mat-form-field> |
|||
@ -0,0 +1,216 @@ |
|||
///
|
|||
/// Copyright © 2016-2022 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 { Component, ElementRef, forwardRef, Input, OnChanges, OnInit, SimpleChanges, ViewChild } from '@angular/core'; |
|||
import { ControlValueAccessor, FormBuilder, FormGroup, NG_VALUE_ACCESSOR, Validators } from '@angular/forms'; |
|||
import { PageComponent } from '@shared/components/page.component'; |
|||
import { Store } from '@ngrx/store'; |
|||
import { AppState } from '@core/core.state'; |
|||
import { TranslateService } from '@ngx-translate/core'; |
|||
import { DataKeyType } from '@shared/models/telemetry/telemetry.models'; |
|||
import { Observable, of } from 'rxjs'; |
|||
import { IAliasController } from '@core/api/widget-api.models'; |
|||
import { catchError, map, mergeMap, publishReplay, refCount, tap } from 'rxjs/operators'; |
|||
import { DataKey } from '@shared/models/widget.models'; |
|||
import { EntityService } from '@core/http/entity.service'; |
|||
import { coerceBooleanProperty } from '@angular/cdk/coercion'; |
|||
|
|||
@Component({ |
|||
selector: 'tb-device-key-autocomplete', |
|||
templateUrl: './device-key-autocomplete.component.html', |
|||
styleUrls: [], |
|||
providers: [ |
|||
{ |
|||
provide: NG_VALUE_ACCESSOR, |
|||
useExisting: forwardRef(() => DeviceKeyAutocompleteComponent), |
|||
multi: true |
|||
} |
|||
] |
|||
}) |
|||
export class DeviceKeyAutocompleteComponent extends PageComponent implements OnInit, ControlValueAccessor, OnChanges { |
|||
|
|||
@ViewChild('keyInput') keyInput: ElementRef; |
|||
|
|||
@Input() |
|||
disabled: boolean; |
|||
|
|||
@Input() |
|||
aliasController: IAliasController; |
|||
|
|||
@Input() |
|||
targetDeviceAliasId: string; |
|||
|
|||
@Input() |
|||
keyType: DataKeyType; |
|||
|
|||
@Input() |
|||
attributeLabel = 'widgets.rpc.attribute-value-key'; |
|||
|
|||
@Input() |
|||
timeseriesLabel = 'widgets.rpc.timeseries-value-key'; |
|||
|
|||
private requiredValue: boolean; |
|||
get required(): boolean { |
|||
return this.requiredValue; |
|||
} |
|||
@Input() |
|||
set required(value: boolean) { |
|||
this.requiredValue = coerceBooleanProperty(value); |
|||
} |
|||
|
|||
dataKeyType = DataKeyType; |
|||
|
|||
private modelValue: string; |
|||
|
|||
private propagateChange = null; |
|||
|
|||
public deviceKeyFormGroup: FormGroup; |
|||
|
|||
filteredKeys: Observable<Array<string>>; |
|||
keySearchText = ''; |
|||
|
|||
private latestKeySearchResult: Array<string> = null; |
|||
private keysFetchObservable$: Observable<Array<string>> = null; |
|||
|
|||
constructor(protected store: Store<AppState>, |
|||
private translate: TranslateService, |
|||
private entityService: EntityService, |
|||
private fb: FormBuilder) { |
|||
super(store); |
|||
} |
|||
|
|||
ngOnInit(): void { |
|||
this.deviceKeyFormGroup = this.fb.group({ |
|||
key: [null, this.required ? [Validators.required] : []] |
|||
}); |
|||
this.deviceKeyFormGroup.valueChanges.subscribe(() => { |
|||
this.updateModel(); |
|||
}); |
|||
|
|||
this.filteredKeys = this.deviceKeyFormGroup.get('key').valueChanges |
|||
.pipe( |
|||
map(value => value ? value : ''), |
|||
mergeMap(name => this.fetchKeys(name) ) |
|||
); |
|||
} |
|||
|
|||
ngOnChanges(changes: SimpleChanges): void { |
|||
for (const propName of Object.keys(changes)) { |
|||
const change = changes[propName]; |
|||
if (!change.firstChange && change.currentValue !== change.previousValue) { |
|||
if (propName === 'targetDeviceAliasId' || propName === 'keyType') { |
|||
this.clearKeysCache(); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
registerOnChange(fn: any): void { |
|||
this.propagateChange = fn; |
|||
} |
|||
|
|||
registerOnTouched(fn: any): void { |
|||
} |
|||
|
|||
setDisabledState(isDisabled: boolean): void { |
|||
this.disabled = isDisabled; |
|||
if (isDisabled) { |
|||
this.deviceKeyFormGroup.disable({emitEvent: false}); |
|||
} else { |
|||
this.deviceKeyFormGroup.enable({emitEvent: false}); |
|||
} |
|||
} |
|||
|
|||
writeValue(value: string): void { |
|||
this.modelValue = value; |
|||
this.deviceKeyFormGroup.patchValue( |
|||
{key: value}, {emitEvent: false} |
|||
); |
|||
} |
|||
|
|||
clearKey() { |
|||
this.deviceKeyFormGroup.get('key').patchValue(null, {emitEvent: true}); |
|||
setTimeout(() => { |
|||
this.keyInput.nativeElement.blur(); |
|||
this.keyInput.nativeElement.focus(); |
|||
}, 0); |
|||
} |
|||
|
|||
onFocus() { |
|||
this.deviceKeyFormGroup.get('key').updateValueAndValidity({onlySelf: true, emitEvent: true}); |
|||
} |
|||
|
|||
private updateModel() { |
|||
const value: string = this.deviceKeyFormGroup.get('key').value; |
|||
this.modelValue = value; |
|||
this.propagateChange(this.modelValue); |
|||
} |
|||
|
|||
private clearKeysCache(): void { |
|||
this.latestKeySearchResult = null; |
|||
this.keysFetchObservable$ = null; |
|||
} |
|||
|
|||
private fetchKeys(searchText?: string): Observable<Array<string>> { |
|||
if (this.keySearchText !== searchText || this.latestKeySearchResult === null) { |
|||
this.keySearchText = searchText; |
|||
const dataKeyFilter = this.createKeyFilter(this.keySearchText); |
|||
return this.getKeys().pipe( |
|||
map(name => name.filter(dataKeyFilter)), |
|||
tap(res => this.latestKeySearchResult = res) |
|||
); |
|||
} |
|||
return of(this.latestKeySearchResult); |
|||
} |
|||
|
|||
private getKeys() { |
|||
if (this.keysFetchObservable$ === null) { |
|||
let fetchObservable: Observable<Array<DataKey>>; |
|||
if (this.targetDeviceAliasId) { |
|||
const dataKeyTypes = [this.keyType]; |
|||
fetchObservable = this.fetchEntityKeys(this.targetDeviceAliasId, dataKeyTypes); |
|||
} else { |
|||
fetchObservable = of([]); |
|||
} |
|||
this.keysFetchObservable$ = fetchObservable.pipe( |
|||
map((dataKeys) => dataKeys.map((dataKey) => dataKey.name)), |
|||
publishReplay(1), |
|||
refCount() |
|||
); |
|||
} |
|||
return this.keysFetchObservable$; |
|||
} |
|||
|
|||
private fetchEntityKeys(entityAliasId: string, dataKeyTypes: Array<DataKeyType>): Observable<Array<DataKey>> { |
|||
return this.aliasController.getAliasInfo(entityAliasId).pipe( |
|||
mergeMap((aliasInfo) => { |
|||
return this.entityService.getEntityKeysByEntityFilter( |
|||
aliasInfo.entityFilter, |
|||
dataKeyTypes, |
|||
{ignoreLoading: true, ignoreErrors: true} |
|||
).pipe( |
|||
catchError(() => of([])) |
|||
); |
|||
}), |
|||
catchError(() => of([] as Array<DataKey>)) |
|||
); |
|||
} |
|||
|
|||
private createKeyFilter(query: string): (key: string) => boolean { |
|||
const lowercaseQuery = query.toLowerCase(); |
|||
return key => key.toLowerCase().startsWith(lowercaseQuery); |
|||
} |
|||
} |
|||
@ -0,0 +1,81 @@ |
|||
<!-- |
|||
|
|||
Copyright © 2016-2022 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. |
|||
|
|||
--> |
|||
|
|||
<section class="tb-widget-settings" [formGroup]="knobControlWidgetSettingsForm" fxLayout="column"> |
|||
<fieldset class="fields-group"> |
|||
<legend class="group-title" translate>widgets.rpc.common-settings</legend> |
|||
<mat-form-field fxFlex class="mat-block"> |
|||
<mat-label translate>widgets.rpc.knob-title</mat-label> |
|||
<input matInput formControlName="title"> |
|||
</mat-form-field> |
|||
</fieldset> |
|||
<fieldset class="fields-group"> |
|||
<legend class="group-title" translate>widgets.rpc.value-settings</legend> |
|||
<mat-form-field fxFlex class="mat-block"> |
|||
<mat-label translate>widgets.rpc.initial-value</mat-label> |
|||
<input matInput type="number" formControlName="initialValue"> |
|||
</mat-form-field> |
|||
<section fxLayout="column" fxLayout.gt-sm="row" fxLayoutGap="8px"> |
|||
<mat-form-field fxFlex class="mat-block"> |
|||
<mat-label translate>widgets.rpc.min-value</mat-label> |
|||
<input required matInput type="number" formControlName="minValue"> |
|||
</mat-form-field> |
|||
<mat-form-field fxFlex class="mat-block"> |
|||
<mat-label translate>widgets.rpc.max-value</mat-label> |
|||
<input required matInput type="number" formControlName="maxValue"> |
|||
</mat-form-field> |
|||
</section> |
|||
<mat-form-field fxFlex class="mat-block"> |
|||
<mat-label translate>widgets.rpc.get-value-method</mat-label> |
|||
<input required matInput formControlName="getValueMethod"> |
|||
</mat-form-field> |
|||
<mat-form-field fxFlex class="mat-block"> |
|||
<mat-label translate>widgets.rpc.set-value-method</mat-label> |
|||
<input required matInput formControlName="setValueMethod"> |
|||
</mat-form-field> |
|||
</fieldset> |
|||
<fieldset class="fields-group"> |
|||
<legend class="group-title" translate>widgets.rpc.rpc-settings</legend> |
|||
<mat-form-field fxFlex class="mat-block"> |
|||
<mat-label translate>widgets.rpc.request-timeout</mat-label> |
|||
<input required matInput type="number" min="0" formControlName="requestTimeout"> |
|||
</mat-form-field> |
|||
<fieldset class="fields-group fields-group-slider"> |
|||
<legend class="group-title" translate>widgets.rpc.persistent-rpc-settings</legend> |
|||
<mat-expansion-panel class="tb-settings" [expanded]="knobControlWidgetSettingsForm.get('requestPersistent').value"> |
|||
<mat-expansion-panel-header fxLayout="row wrap"> |
|||
<mat-panel-title> |
|||
<mat-slide-toggle formControlName="requestPersistent" (click)="$event.stopPropagation()" |
|||
fxLayoutAlign="center"> |
|||
{{ 'widgets.rpc.request-persistent' | translate }} |
|||
</mat-slide-toggle> |
|||
</mat-panel-title> |
|||
<mat-panel-description fxLayoutAlign="end center" fxHide.xs translate> |
|||
widget-config.advanced-settings |
|||
</mat-panel-description> |
|||
</mat-expansion-panel-header> |
|||
<ng-template matExpansionPanelContent> |
|||
<mat-form-field fxFlex class="mat-block"> |
|||
<mat-label translate>widgets.rpc.persistent-polling-interval</mat-label> |
|||
<input matInput type="number" min="1000" formControlName="persistentPollingInterval"> |
|||
</mat-form-field> |
|||
</ng-template> |
|||
</mat-expansion-panel> |
|||
</fieldset> |
|||
</fieldset> |
|||
</section> |
|||
@ -0,0 +1,95 @@ |
|||
///
|
|||
/// Copyright © 2016-2022 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 { Component } from '@angular/core'; |
|||
import { WidgetSettings, WidgetSettingsComponent } from '@shared/models/widget.models'; |
|||
import { FormBuilder, FormGroup, Validators } from '@angular/forms'; |
|||
import { Store } from '@ngrx/store'; |
|||
import { AppState } from '@core/core.state'; |
|||
|
|||
@Component({ |
|||
selector: 'tb-knob-control-widget-settings', |
|||
templateUrl: './knob-control-widget-settings.component.html', |
|||
styleUrls: ['./../widget-settings.scss'] |
|||
}) |
|||
export class KnobControlWidgetSettingsComponent extends WidgetSettingsComponent { |
|||
|
|||
knobControlWidgetSettingsForm: FormGroup; |
|||
|
|||
constructor(protected store: Store<AppState>, |
|||
private fb: FormBuilder) { |
|||
super(store); |
|||
} |
|||
|
|||
protected settingsForm(): FormGroup { |
|||
return this.knobControlWidgetSettingsForm; |
|||
} |
|||
|
|||
protected defaultSettings(): WidgetSettings { |
|||
return { |
|||
title: '', |
|||
minValue: 0, |
|||
maxValue: 100, |
|||
initialValue: 50, |
|||
getValueMethod: 'getValue', |
|||
setValueMethod: 'setValue', |
|||
requestTimeout: 500, |
|||
requestPersistent: false, |
|||
persistentPollingInterval: 5000 |
|||
}; |
|||
} |
|||
|
|||
protected onSettingsSet(settings: WidgetSettings) { |
|||
this.knobControlWidgetSettingsForm = this.fb.group({ |
|||
|
|||
// Common settings
|
|||
|
|||
title: [settings.title, []], |
|||
|
|||
// Value settings
|
|||
|
|||
initialValue: [settings.initialValue, []], |
|||
minValue: [settings.minValue, [Validators.required]], |
|||
maxValue: [settings.maxValue, [Validators.required]], |
|||
|
|||
getValueMethod: [settings.getValueMethod, [Validators.required]], |
|||
setValueMethod: [settings.setValueMethod, [Validators.required]], |
|||
|
|||
// RPC settings
|
|||
|
|||
requestTimeout: [settings.requestTimeout, [Validators.min(0), Validators.required]], |
|||
|
|||
// --> Persistent RPC settings
|
|||
|
|||
requestPersistent: [settings.requestPersistent, []], |
|||
persistentPollingInterval: [settings.persistentPollingInterval, [Validators.min(1000)]] |
|||
}); |
|||
} |
|||
|
|||
protected validatorTriggers(): string[] { |
|||
return ['requestPersistent']; |
|||
} |
|||
|
|||
protected updateValidators(emitEvent: boolean): void { |
|||
const requestPersistent: boolean = this.knobControlWidgetSettingsForm.get('requestPersistent').value; |
|||
if (requestPersistent) { |
|||
this.knobControlWidgetSettingsForm.get('persistentPollingInterval').enable({emitEvent}); |
|||
} else { |
|||
this.knobControlWidgetSettingsForm.get('persistentPollingInterval').disable({emitEvent}); |
|||
} |
|||
this.knobControlWidgetSettingsForm.get('persistentPollingInterval').updateValueAndValidity({emitEvent: false}); |
|||
} |
|||
} |
|||
@ -0,0 +1,104 @@ |
|||
<!-- |
|||
|
|||
Copyright © 2016-2022 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. |
|||
|
|||
--> |
|||
|
|||
<section class="tb-widget-settings" [formGroup]="ledIndicatorWidgetSettingsForm" fxLayout="column"> |
|||
<fieldset class="fields-group"> |
|||
<legend class="group-title" translate>widgets.rpc.common-settings</legend> |
|||
<mat-form-field fxFlex class="mat-block"> |
|||
<mat-label translate>widgets.rpc.led-title</mat-label> |
|||
<input matInput formControlName="title"> |
|||
</mat-form-field> |
|||
<tb-color-input fxFlex |
|||
formControlName="ledColor" |
|||
icon="format_color_fill" |
|||
label="{{ 'widgets.rpc.led-color' | translate }}" openOnInput colorClearButton> |
|||
</tb-color-input> |
|||
</fieldset> |
|||
<fieldset class="fields-group"> |
|||
<legend class="group-title" translate>widgets.rpc.value-settings</legend> |
|||
<mat-checkbox formControlName="initialValue" style="margin-bottom: 16px;"> |
|||
{{ 'widgets.rpc.initial-value' | translate }} |
|||
</mat-checkbox> |
|||
<fieldset class="fields-group"> |
|||
<legend class="group-title" translate>widgets.rpc.check-status-settings</legend> |
|||
<mat-slide-toggle formControlName="performCheckStatus" class="slide-block"> |
|||
{{ 'widgets.rpc.perform-rpc-status-check' | translate }} |
|||
</mat-slide-toggle> |
|||
<mat-form-field [fxShow]="!ledIndicatorWidgetSettingsForm.get('performCheckStatus').value" fxFlex class="mat-block"> |
|||
<mat-label translate>widgets.rpc.retrieve-led-status-value-method</mat-label> |
|||
<mat-select formControlName="retrieveValueMethod"> |
|||
<mat-option [value]="'attribute'"> |
|||
{{ 'widgets.rpc.retrieve-value-method-attribute' | translate }} |
|||
</mat-option> |
|||
<mat-option [value]="'timeseries'"> |
|||
{{ 'widgets.rpc.retrieve-value-method-timeseries' | translate }} |
|||
</mat-option> |
|||
</mat-select> |
|||
</mat-form-field> |
|||
<tb-device-key-autocomplete fxFlex |
|||
[fxShow]="!ledIndicatorWidgetSettingsForm.get('performCheckStatus').value" |
|||
[aliasController]="aliasController" |
|||
[targetDeviceAliasId]="targetDeviceAliasId" |
|||
[keyType]="ledIndicatorWidgetSettingsForm.get('retrieveValueMethod').value === 'attribute' ? dataKeyType.attribute : dataKeyType.timeseries" |
|||
attributeLabel="widgets.rpc.led-status-value-attribute" |
|||
timeseriesLabel="widgets.rpc.led-status-value-timeseries" |
|||
required |
|||
formControlName="valueAttribute"> |
|||
</tb-device-key-autocomplete> |
|||
<mat-form-field [fxShow]="ledIndicatorWidgetSettingsForm.get('performCheckStatus').value" fxFlex class="mat-block"> |
|||
<mat-label translate>widgets.rpc.check-status-method</mat-label> |
|||
<input required matInput formControlName="checkStatusMethod"> |
|||
</mat-form-field> |
|||
<tb-js-func formControlName="parseValueFunction" |
|||
[globalVariables]="functionScopeVariables" |
|||
[functionArgs]="['data']" |
|||
functionTitle="{{ 'widgets.rpc.parse-led-status-value-function' | translate }}" |
|||
helpId="widget/lib/rpc/parse_value_fn"> |
|||
</tb-js-func> |
|||
</fieldset> |
|||
</fieldset> |
|||
<fieldset [fxShow]="ledIndicatorWidgetSettingsForm.get('performCheckStatus').value" class="fields-group"> |
|||
<legend class="group-title" translate>widgets.rpc.rpc-settings</legend> |
|||
<mat-form-field fxFlex class="mat-block"> |
|||
<mat-label translate>widgets.rpc.request-timeout</mat-label> |
|||
<input matInput type="number" min="0" formControlName="requestTimeout"> |
|||
</mat-form-field> |
|||
<fieldset class="fields-group fields-group-slider"> |
|||
<legend class="group-title" translate>widgets.rpc.persistent-rpc-settings</legend> |
|||
<mat-expansion-panel class="tb-settings" [expanded]="ledIndicatorWidgetSettingsForm.get('requestPersistent').value"> |
|||
<mat-expansion-panel-header fxLayout="row wrap"> |
|||
<mat-panel-title> |
|||
<mat-slide-toggle formControlName="requestPersistent" (click)="$event.stopPropagation()" |
|||
fxLayoutAlign="center"> |
|||
{{ 'widgets.rpc.request-persistent' | translate }} |
|||
</mat-slide-toggle> |
|||
</mat-panel-title> |
|||
<mat-panel-description fxLayoutAlign="end center" fxHide.xs translate> |
|||
widget-config.advanced-settings |
|||
</mat-panel-description> |
|||
</mat-expansion-panel-header> |
|||
<ng-template matExpansionPanelContent> |
|||
<mat-form-field fxFlex class="mat-block"> |
|||
<mat-label translate>widgets.rpc.persistent-polling-interval</mat-label> |
|||
<input matInput type="number" min="1000" formControlName="persistentPollingInterval"> |
|||
</mat-form-field> |
|||
</ng-template> |
|||
</mat-expansion-panel> |
|||
</fieldset> |
|||
</fieldset> |
|||
</section> |
|||
@ -0,0 +1,133 @@ |
|||
///
|
|||
/// Copyright © 2016-2022 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 { Component } from '@angular/core'; |
|||
import { WidgetSettings, WidgetSettingsComponent } from '@shared/models/widget.models'; |
|||
import { FormBuilder, FormGroup, Validators } from '@angular/forms'; |
|||
import { Store } from '@ngrx/store'; |
|||
import { AppState } from '@core/core.state'; |
|||
import { WidgetService } from '@core/http/widget.service'; |
|||
import { DataKeyType } from '@shared/models/telemetry/telemetry.models'; |
|||
|
|||
@Component({ |
|||
selector: 'tb-led-indicator-widget-settings', |
|||
templateUrl: './led-indicator-widget-settings.component.html', |
|||
styleUrls: ['./../widget-settings.scss'] |
|||
}) |
|||
export class LedIndicatorWidgetSettingsComponent extends WidgetSettingsComponent { |
|||
|
|||
functionScopeVariables = this.widgetService.getWidgetScopeVariables(); |
|||
|
|||
get targetDeviceAliasId(): string { |
|||
const aliasIds = this.widget.config.targetDeviceAliasIds; |
|||
if (aliasIds && aliasIds.length) { |
|||
return aliasIds[0]; |
|||
} |
|||
return null; |
|||
} |
|||
|
|||
dataKeyType = DataKeyType; |
|||
|
|||
ledIndicatorWidgetSettingsForm: FormGroup; |
|||
|
|||
constructor(protected store: Store<AppState>, |
|||
private widgetService: WidgetService, |
|||
private fb: FormBuilder) { |
|||
super(store); |
|||
} |
|||
|
|||
protected settingsForm(): FormGroup { |
|||
return this.ledIndicatorWidgetSettingsForm; |
|||
} |
|||
|
|||
protected defaultSettings(): WidgetSettings { |
|||
return { |
|||
title: '', |
|||
ledColor: 'green', |
|||
initialValue: false, |
|||
performCheckStatus: true, |
|||
checkStatusMethod: 'checkStatus', |
|||
retrieveValueMethod: 'attribute', |
|||
valueAttribute: 'value', |
|||
parseValueFunction: 'return data ? true : false;', |
|||
requestTimeout: 500, |
|||
requestPersistent: false, |
|||
persistentPollingInterval: 5000 |
|||
}; |
|||
} |
|||
|
|||
protected onSettingsSet(settings: WidgetSettings) { |
|||
this.ledIndicatorWidgetSettingsForm = this.fb.group({ |
|||
|
|||
// Common settings
|
|||
|
|||
title: [settings.title, []], |
|||
ledColor: [settings.ledColor, []], |
|||
|
|||
// Value settings
|
|||
|
|||
initialValue: [settings.initialValue, []], |
|||
|
|||
// --> Check status settings
|
|||
|
|||
performCheckStatus: [settings.performCheckStatus, []], |
|||
retrieveValueMethod: [settings.retrieveValueMethod, []], |
|||
checkStatusMethod: [settings.checkStatusMethod, [Validators.required]], |
|||
valueAttribute: [settings.valueAttribute, [Validators.required]], |
|||
parseValueFunction: [settings.parseValueFunction, []], |
|||
|
|||
// RPC settings
|
|||
|
|||
requestTimeout: [settings.requestTimeout, [Validators.min(0)]], |
|||
|
|||
// --> Persistent RPC settings
|
|||
|
|||
requestPersistent: [settings.requestPersistent, []], |
|||
persistentPollingInterval: [settings.persistentPollingInterval, [Validators.min(1000)]] |
|||
}); |
|||
} |
|||
|
|||
protected validatorTriggers(): string[] { |
|||
return ['performCheckStatus', 'requestPersistent']; |
|||
} |
|||
|
|||
protected updateValidators(emitEvent: boolean): void { |
|||
const performCheckStatus: boolean = this.ledIndicatorWidgetSettingsForm.get('performCheckStatus').value; |
|||
const requestPersistent: boolean = this.ledIndicatorWidgetSettingsForm.get('requestPersistent').value; |
|||
if (performCheckStatus) { |
|||
this.ledIndicatorWidgetSettingsForm.get('valueAttribute').disable({emitEvent}); |
|||
this.ledIndicatorWidgetSettingsForm.get('checkStatusMethod').enable({emitEvent}); |
|||
this.ledIndicatorWidgetSettingsForm.get('requestTimeout').enable({emitEvent}); |
|||
this.ledIndicatorWidgetSettingsForm.get('requestPersistent').enable({emitEvent: false}); |
|||
if (requestPersistent) { |
|||
this.ledIndicatorWidgetSettingsForm.get('persistentPollingInterval').enable({emitEvent}); |
|||
} else { |
|||
this.ledIndicatorWidgetSettingsForm.get('persistentPollingInterval').disable({emitEvent}); |
|||
} |
|||
} else { |
|||
this.ledIndicatorWidgetSettingsForm.get('valueAttribute').enable({emitEvent}); |
|||
this.ledIndicatorWidgetSettingsForm.get('checkStatusMethod').disable({emitEvent}); |
|||
this.ledIndicatorWidgetSettingsForm.get('requestTimeout').disable({emitEvent}); |
|||
this.ledIndicatorWidgetSettingsForm.get('requestPersistent').disable({emitEvent: false}); |
|||
this.ledIndicatorWidgetSettingsForm.get('persistentPollingInterval').disable({emitEvent}); |
|||
} |
|||
this.ledIndicatorWidgetSettingsForm.get('valueAttribute').updateValueAndValidity({emitEvent: false}); |
|||
this.ledIndicatorWidgetSettingsForm.get('checkStatusMethod').updateValueAndValidity({emitEvent: false}); |
|||
this.ledIndicatorWidgetSettingsForm.get('requestTimeout').updateValueAndValidity({emitEvent: false}); |
|||
this.ledIndicatorWidgetSettingsForm.get('requestPersistent').updateValueAndValidity({emitEvent: false}); |
|||
this.ledIndicatorWidgetSettingsForm.get('persistentPollingInterval').updateValueAndValidity({emitEvent: false}); |
|||
} |
|||
} |
|||
@ -0,0 +1,40 @@ |
|||
<!-- |
|||
|
|||
Copyright © 2016-2022 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. |
|||
|
|||
--> |
|||
<section class="tb-widget-settings" [formGroup]="rpcButtonStyleFormGroup" fxLayout="column"> |
|||
<fieldset class="fields-group"> |
|||
<legend class="group-title" translate>widgets.rpc.button-style</legend> |
|||
<mat-slide-toggle formControlName="isRaised" class="slide-block"> |
|||
{{ 'widgets.rpc.button-raised' | translate }} |
|||
</mat-slide-toggle> |
|||
<mat-slide-toggle formControlName="isPrimary" class="slide-block"> |
|||
{{ 'widgets.rpc.button-primary' | translate }} |
|||
</mat-slide-toggle> |
|||
<section fxLayout="column" fxLayout.gt-sm="row" fxLayoutGap="8px"> |
|||
<tb-color-input fxFlex |
|||
formControlName="textColor" |
|||
icon="format_color_fill" |
|||
label="{{ 'widgets.rpc.button-text-color' | translate }}" openOnInput colorClearButton> |
|||
</tb-color-input> |
|||
<tb-color-input fxFlex |
|||
formControlName="bgColor" |
|||
icon="format_color_fill" |
|||
label="{{ 'widgets.rpc.button-background-color' | translate }}" openOnInput colorClearButton> |
|||
</tb-color-input> |
|||
</section> |
|||
</fieldset> |
|||
</section> |
|||
@ -0,0 +1,98 @@ |
|||
///
|
|||
/// Copyright © 2016-2022 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 { Component, forwardRef, Input, OnInit } from '@angular/core'; |
|||
import { ControlValueAccessor, FormBuilder, FormGroup, NG_VALUE_ACCESSOR } from '@angular/forms'; |
|||
import { PageComponent } from '@shared/components/page.component'; |
|||
import { Store } from '@ngrx/store'; |
|||
import { AppState } from '@core/core.state'; |
|||
|
|||
export interface RpcButtonStyle { |
|||
isRaised: boolean; |
|||
isPrimary: boolean; |
|||
bgColor: string; |
|||
textColor: string; |
|||
} |
|||
|
|||
@Component({ |
|||
selector: 'tb-rpc-button-style', |
|||
templateUrl: './rpc-button-style.component.html', |
|||
styleUrls: ['./../widget-settings.scss'], |
|||
providers: [ |
|||
{ |
|||
provide: NG_VALUE_ACCESSOR, |
|||
useExisting: forwardRef(() => RpcButtonStyleComponent), |
|||
multi: true |
|||
} |
|||
] |
|||
}) |
|||
export class RpcButtonStyleComponent extends PageComponent implements OnInit, ControlValueAccessor { |
|||
|
|||
@Input() |
|||
disabled: boolean; |
|||
|
|||
private modelValue: RpcButtonStyle; |
|||
|
|||
private propagateChange = null; |
|||
|
|||
public rpcButtonStyleFormGroup: FormGroup; |
|||
|
|||
constructor(protected store: Store<AppState>, |
|||
private fb: FormBuilder) { |
|||
super(store); |
|||
} |
|||
|
|||
ngOnInit(): void { |
|||
this.rpcButtonStyleFormGroup = this.fb.group({ |
|||
isRaised: [true, []], |
|||
isPrimary: [false, []], |
|||
bgColor: [null, []], |
|||
textColor: [null, []] |
|||
}); |
|||
this.rpcButtonStyleFormGroup.valueChanges.subscribe(() => { |
|||
this.updateModel(); |
|||
}); |
|||
} |
|||
|
|||
registerOnChange(fn: any): void { |
|||
this.propagateChange = fn; |
|||
} |
|||
|
|||
registerOnTouched(fn: any): void { |
|||
} |
|||
|
|||
setDisabledState(isDisabled: boolean): void { |
|||
this.disabled = isDisabled; |
|||
if (isDisabled) { |
|||
this.rpcButtonStyleFormGroup.disable({emitEvent: false}); |
|||
} else { |
|||
this.rpcButtonStyleFormGroup.enable({emitEvent: false}); |
|||
} |
|||
} |
|||
|
|||
writeValue(value: RpcButtonStyle): void { |
|||
this.modelValue = value; |
|||
this.rpcButtonStyleFormGroup.patchValue( |
|||
value, {emitEvent: false} |
|||
); |
|||
} |
|||
|
|||
private updateModel() { |
|||
this.modelValue = this.rpcButtonStyleFormGroup.value; |
|||
this.propagateChange(this.modelValue); |
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,27 @@ |
|||
<!-- |
|||
|
|||
Copyright © 2016-2022 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. |
|||
|
|||
--> |
|||
|
|||
<section class="tb-widget-settings" [formGroup]="rpcShellWidgetSettingsForm" fxLayout="column"> |
|||
<fieldset class="fields-group"> |
|||
<legend class="group-title" translate>widgets.rpc.rpc-settings</legend> |
|||
<mat-form-field fxFlex class="mat-block"> |
|||
<mat-label translate>widgets.rpc.request-timeout</mat-label> |
|||
<input required matInput type="number" min="0" formControlName="requestTimeout"> |
|||
</mat-form-field> |
|||
</fieldset> |
|||
</section> |
|||
@ -0,0 +1,53 @@ |
|||
///
|
|||
/// Copyright © 2016-2022 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 { Component } from '@angular/core'; |
|||
import { WidgetSettings, WidgetSettingsComponent } from '@shared/models/widget.models'; |
|||
import { FormBuilder, FormGroup, Validators } from '@angular/forms'; |
|||
import { Store } from '@ngrx/store'; |
|||
import { AppState } from '@core/core.state'; |
|||
|
|||
@Component({ |
|||
selector: 'tb-rpc-shell-widget-settings', |
|||
templateUrl: './rpc-shell-widget-settings.component.html', |
|||
styleUrls: ['./../widget-settings.scss'] |
|||
}) |
|||
export class RpcShellWidgetSettingsComponent extends WidgetSettingsComponent { |
|||
|
|||
rpcShellWidgetSettingsForm: FormGroup; |
|||
|
|||
constructor(protected store: Store<AppState>, |
|||
private fb: FormBuilder) { |
|||
super(store); |
|||
} |
|||
|
|||
protected settingsForm(): FormGroup { |
|||
return this.rpcShellWidgetSettingsForm; |
|||
} |
|||
|
|||
protected defaultSettings(): WidgetSettings { |
|||
return { |
|||
requestTimeout: 500 |
|||
}; |
|||
} |
|||
|
|||
protected onSettingsSet(settings: WidgetSettings) { |
|||
this.rpcShellWidgetSettingsForm = this.fb.group({ |
|||
// RPC settings
|
|||
requestTimeout: [settings.requestTimeout, [Validators.min(0), Validators.required]] |
|||
}); |
|||
} |
|||
} |
|||
@ -0,0 +1,49 @@ |
|||
<!-- |
|||
|
|||
Copyright © 2016-2022 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. |
|||
|
|||
--> |
|||
|
|||
<section class="tb-widget-settings" [formGroup]="rpcTerminalWidgetSettingsForm" fxLayout="column"> |
|||
<fieldset class="fields-group"> |
|||
<legend class="group-title" translate>widgets.rpc.rpc-settings</legend> |
|||
<mat-form-field fxFlex class="mat-block"> |
|||
<mat-label translate>widgets.rpc.request-timeout</mat-label> |
|||
<input required matInput type="number" min="0" formControlName="requestTimeout"> |
|||
</mat-form-field> |
|||
<fieldset class="fields-group fields-group-slider"> |
|||
<legend class="group-title" translate>widgets.rpc.persistent-rpc-settings</legend> |
|||
<mat-expansion-panel class="tb-settings" [expanded]="rpcTerminalWidgetSettingsForm.get('requestPersistent').value"> |
|||
<mat-expansion-panel-header fxLayout="row wrap"> |
|||
<mat-panel-title> |
|||
<mat-slide-toggle formControlName="requestPersistent" (click)="$event.stopPropagation()" |
|||
fxLayoutAlign="center"> |
|||
{{ 'widgets.rpc.request-persistent' | translate }} |
|||
</mat-slide-toggle> |
|||
</mat-panel-title> |
|||
<mat-panel-description fxLayoutAlign="end center" fxHide.xs translate> |
|||
widget-config.advanced-settings |
|||
</mat-panel-description> |
|||
</mat-expansion-panel-header> |
|||
<ng-template matExpansionPanelContent> |
|||
<mat-form-field fxFlex class="mat-block"> |
|||
<mat-label translate>widgets.rpc.persistent-polling-interval</mat-label> |
|||
<input matInput type="number" min="1000" formControlName="persistentPollingInterval"> |
|||
</mat-form-field> |
|||
</ng-template> |
|||
</mat-expansion-panel> |
|||
</fieldset> |
|||
</fieldset> |
|||
</section> |
|||
@ -0,0 +1,75 @@ |
|||
///
|
|||
/// Copyright © 2016-2022 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 { Component } from '@angular/core'; |
|||
import { WidgetSettings, WidgetSettingsComponent } from '@shared/models/widget.models'; |
|||
import { FormBuilder, FormGroup, Validators } from '@angular/forms'; |
|||
import { Store } from '@ngrx/store'; |
|||
import { AppState } from '@core/core.state'; |
|||
|
|||
@Component({ |
|||
selector: 'tb-rpc-terminal-widget-settings', |
|||
templateUrl: './rpc-terminal-widget-settings.component.html', |
|||
styleUrls: ['./../widget-settings.scss'] |
|||
}) |
|||
export class RpcTerminalWidgetSettingsComponent extends WidgetSettingsComponent { |
|||
|
|||
rpcTerminalWidgetSettingsForm: FormGroup; |
|||
|
|||
constructor(protected store: Store<AppState>, |
|||
private fb: FormBuilder) { |
|||
super(store); |
|||
} |
|||
|
|||
protected settingsForm(): FormGroup { |
|||
return this.rpcTerminalWidgetSettingsForm; |
|||
} |
|||
|
|||
protected defaultSettings(): WidgetSettings { |
|||
return { |
|||
requestTimeout: 500, |
|||
requestPersistent: false, |
|||
persistentPollingInterval: 5000 |
|||
}; |
|||
} |
|||
|
|||
protected onSettingsSet(settings: WidgetSettings) { |
|||
this.rpcTerminalWidgetSettingsForm = this.fb.group({ |
|||
// RPC settings
|
|||
|
|||
requestTimeout: [settings.requestTimeout, [Validators.min(0), Validators.required]], |
|||
|
|||
// --> Persistent RPC settings
|
|||
|
|||
requestPersistent: [settings.requestPersistent, []], |
|||
persistentPollingInterval: [settings.persistentPollingInterval, [Validators.min(1000)]] |
|||
}); |
|||
} |
|||
|
|||
protected validatorTriggers(): string[] { |
|||
return ['requestPersistent']; |
|||
} |
|||
|
|||
protected updateValidators(emitEvent: boolean): void { |
|||
const requestPersistent: boolean = this.rpcTerminalWidgetSettingsForm.get('requestPersistent').value; |
|||
if (requestPersistent) { |
|||
this.rpcTerminalWidgetSettingsForm.get('persistentPollingInterval').enable({emitEvent}); |
|||
} else { |
|||
this.rpcTerminalWidgetSettingsForm.get('persistentPollingInterval').disable({emitEvent}); |
|||
} |
|||
this.rpcTerminalWidgetSettingsForm.get('persistentPollingInterval').updateValueAndValidity({emitEvent: false}); |
|||
} |
|||
} |
|||
@ -0,0 +1,79 @@ |
|||
<!-- |
|||
|
|||
Copyright © 2016-2022 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. |
|||
|
|||
--> |
|||
|
|||
<section class="tb-widget-settings" [formGroup]="sendRpcWidgetSettingsForm" fxLayout="column"> |
|||
<fieldset class="fields-group"> |
|||
<legend class="group-title" translate>widgets.rpc.common-settings</legend> |
|||
<mat-form-field fxFlex class="mat-block"> |
|||
<mat-label translate>widgets.rpc.widget-title</mat-label> |
|||
<input matInput formControlName="title"> |
|||
</mat-form-field> |
|||
<mat-form-field fxFlex class="mat-block"> |
|||
<mat-label translate>widgets.rpc.button-label</mat-label> |
|||
<input matInput formControlName="buttonText"> |
|||
</mat-form-field> |
|||
</fieldset> |
|||
<fieldset class="fields-group"> |
|||
<legend class="group-title" translate>widgets.rpc.rpc-settings</legend> |
|||
<mat-checkbox formControlName="oneWayElseTwoWay" style="margin-bottom: 16px;"> |
|||
{{ 'widgets.rpc.is-one-way-command' | translate }} |
|||
</mat-checkbox> |
|||
<mat-form-field fxFlex class="mat-block"> |
|||
<mat-label translate>widgets.rpc.rpc-method</mat-label> |
|||
<input matInput formControlName="methodName"> |
|||
</mat-form-field> |
|||
<tb-json-content |
|||
label="{{ 'widgets.rpc.rpc-method-params' | translate }}" |
|||
[contentType]="contentTypes.JSON" |
|||
validateOnChange |
|||
formControlName="methodParams"> |
|||
</tb-json-content> |
|||
<mat-form-field fxFlex class="mat-block"> |
|||
<mat-label translate>widgets.rpc.request-timeout</mat-label> |
|||
<input matInput type="number" min="0" formControlName="requestTimeout"> |
|||
</mat-form-field> |
|||
<mat-checkbox formControlName="showError" style="margin-bottom: 16px;"> |
|||
{{ 'widgets.rpc.show-rpc-error' | translate }} |
|||
</mat-checkbox> |
|||
<fieldset class="fields-group fields-group-slider"> |
|||
<legend class="group-title" translate>widgets.rpc.persistent-rpc-settings</legend> |
|||
<mat-expansion-panel class="tb-settings" [expanded]="sendRpcWidgetSettingsForm.get('requestPersistent').value"> |
|||
<mat-expansion-panel-header fxLayout="row wrap"> |
|||
<mat-panel-title> |
|||
<mat-slide-toggle formControlName="requestPersistent" (click)="$event.stopPropagation()" |
|||
fxLayoutAlign="center"> |
|||
{{ 'widgets.rpc.request-persistent' | translate }} |
|||
</mat-slide-toggle> |
|||
</mat-panel-title> |
|||
<mat-panel-description fxLayoutAlign="end center" fxHide.xs translate> |
|||
widget-config.advanced-settings |
|||
</mat-panel-description> |
|||
</mat-expansion-panel-header> |
|||
<ng-template matExpansionPanelContent> |
|||
<mat-form-field fxFlex class="mat-block"> |
|||
<mat-label translate>widgets.rpc.persistent-polling-interval</mat-label> |
|||
<input matInput type="number" min="1000" formControlName="persistentPollingInterval"> |
|||
</mat-form-field> |
|||
</ng-template> |
|||
</mat-expansion-panel> |
|||
</fieldset> |
|||
</fieldset> |
|||
<tb-rpc-button-style |
|||
formControlName="styleButton"> |
|||
</tb-rpc-button-style> |
|||
</section> |
|||
@ -0,0 +1,99 @@ |
|||
///
|
|||
/// Copyright © 2016-2022 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 { Component } from '@angular/core'; |
|||
import { WidgetSettings, WidgetSettingsComponent } from '@shared/models/widget.models'; |
|||
import { FormBuilder, FormGroup, Validators } from '@angular/forms'; |
|||
import { Store } from '@ngrx/store'; |
|||
import { AppState } from '@core/core.state'; |
|||
import { ContentType } from '@shared/models/constants'; |
|||
|
|||
@Component({ |
|||
selector: 'tb-send-rpc-widget-settings', |
|||
templateUrl: './send-rpc-widget-settings.component.html', |
|||
styleUrls: ['./../widget-settings.scss'] |
|||
}) |
|||
export class SendRpcWidgetSettingsComponent extends WidgetSettingsComponent { |
|||
|
|||
sendRpcWidgetSettingsForm: FormGroup; |
|||
|
|||
contentTypes = ContentType; |
|||
|
|||
constructor(protected store: Store<AppState>, |
|||
private fb: FormBuilder) { |
|||
super(store); |
|||
} |
|||
|
|||
protected settingsForm(): FormGroup { |
|||
return this.sendRpcWidgetSettingsForm; |
|||
} |
|||
|
|||
protected defaultSettings(): WidgetSettings { |
|||
return { |
|||
title: '', |
|||
buttonText: 'Send RPC', |
|||
oneWayElseTwoWay: true, |
|||
showError: false, |
|||
methodName: 'rpcCommand', |
|||
methodParams: '{}', |
|||
requestTimeout: 5000, |
|||
requestPersistent: false, |
|||
persistentPollingInterval: 5000, |
|||
styleButton: { |
|||
isRaised: true, |
|||
isPrimary: false, |
|||
bgColor: null, |
|||
textColor: null |
|||
} |
|||
}; |
|||
} |
|||
|
|||
protected onSettingsSet(settings: WidgetSettings) { |
|||
this.sendRpcWidgetSettingsForm = this.fb.group({ |
|||
title: [settings.title, []], |
|||
buttonText: [settings.buttonText, []], |
|||
|
|||
// RPC settings
|
|||
|
|||
oneWayElseTwoWay: [settings.oneWayElseTwoWay, []], |
|||
methodName: [settings.methodName, []], |
|||
methodParams: [settings.methodParams, []], |
|||
requestTimeout: [settings.requestTimeout, [Validators.min(0)]], |
|||
showError: [settings.showError, []], |
|||
|
|||
// Persistent RPC settings
|
|||
|
|||
requestPersistent: [settings.requestPersistent, []], |
|||
persistentPollingInterval: [settings.persistentPollingInterval, [Validators.min(1000)]], |
|||
|
|||
styleButton: [settings.styleButton, []] |
|||
}); |
|||
} |
|||
|
|||
protected validatorTriggers(): string[] { |
|||
return ['requestPersistent']; |
|||
} |
|||
|
|||
protected updateValidators(emitEvent: boolean): void { |
|||
const requestPersistent: boolean = this.sendRpcWidgetSettingsForm.get('requestPersistent').value; |
|||
if (requestPersistent) { |
|||
this.sendRpcWidgetSettingsForm.get('persistentPollingInterval').enable({emitEvent}); |
|||
} else { |
|||
this.sendRpcWidgetSettingsForm.get('persistentPollingInterval').disable({emitEvent}); |
|||
} |
|||
this.sendRpcWidgetSettingsForm.get('persistentPollingInterval').updateValueAndValidity({emitEvent: false}); |
|||
} |
|||
} |
|||
@ -0,0 +1,51 @@ |
|||
<!-- |
|||
|
|||
Copyright © 2016-2022 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. |
|||
|
|||
--> |
|||
|
|||
<section class="tb-widget-settings" [formGroup]="updateDeviceAttributeWidgetSettingsForm" fxLayout="column"> |
|||
<fieldset class="fields-group"> |
|||
<legend class="group-title" translate>widgets.rpc.common-settings</legend> |
|||
<mat-form-field fxFlex class="mat-block"> |
|||
<mat-label translate>widgets.rpc.widget-title</mat-label> |
|||
<input matInput formControlName="title"> |
|||
</mat-form-field> |
|||
<mat-form-field fxFlex class="mat-block"> |
|||
<mat-label translate>widgets.rpc.button-label</mat-label> |
|||
<input matInput formControlName="buttonText"> |
|||
</mat-form-field> |
|||
<mat-form-field fxFlex class="mat-block"> |
|||
<mat-label translate>widgets.rpc.device-attribute-scope</mat-label> |
|||
<mat-select formControlName="entityAttributeType"> |
|||
<mat-option [value]="'SERVER_SCOPE'"> |
|||
{{ 'widgets.rpc.server-attribute' | translate }} |
|||
</mat-option> |
|||
<mat-option [value]="'SHARED_SCOPE'"> |
|||
{{ 'widgets.rpc.shared-attribute' | translate }} |
|||
</mat-option> |
|||
</mat-select> |
|||
</mat-form-field> |
|||
<tb-json-content |
|||
label="{{ 'widgets.rpc.device-attribute-parameters' | translate }}" |
|||
[contentType]="contentTypes.JSON" |
|||
validateOnChange |
|||
formControlName="entityParameters"> |
|||
</tb-json-content> |
|||
</fieldset> |
|||
<tb-rpc-button-style |
|||
formControlName="styleButton"> |
|||
</tb-rpc-button-style> |
|||
</section> |
|||
@ -0,0 +1,68 @@ |
|||
///
|
|||
/// Copyright © 2016-2022 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 { Component } from '@angular/core'; |
|||
import { WidgetSettings, WidgetSettingsComponent } from '@shared/models/widget.models'; |
|||
import { FormBuilder, FormGroup } from '@angular/forms'; |
|||
import { Store } from '@ngrx/store'; |
|||
import { AppState } from '@core/core.state'; |
|||
import { ContentType } from '@shared/models/constants'; |
|||
|
|||
@Component({ |
|||
selector: 'tb-update-device-attribute-widget-settings', |
|||
templateUrl: './update-device-attribute-widget-settings.component.html', |
|||
styleUrls: ['./../widget-settings.scss'] |
|||
}) |
|||
export class UpdateDeviceAttributeWidgetSettingsComponent extends WidgetSettingsComponent { |
|||
|
|||
updateDeviceAttributeWidgetSettingsForm: FormGroup; |
|||
|
|||
contentTypes = ContentType; |
|||
|
|||
constructor(protected store: Store<AppState>, |
|||
private fb: FormBuilder) { |
|||
super(store); |
|||
} |
|||
|
|||
protected settingsForm(): FormGroup { |
|||
return this.updateDeviceAttributeWidgetSettingsForm; |
|||
} |
|||
|
|||
protected defaultSettings(): WidgetSettings { |
|||
return { |
|||
title: '', |
|||
buttonText: 'Update device attribute', |
|||
entityAttributeType: 'SERVER_SCOPE', |
|||
entityParameters: '{}', |
|||
styleButton: { |
|||
isRaised: true, |
|||
isPrimary: false, |
|||
bgColor: null, |
|||
textColor: null |
|||
} |
|||
}; |
|||
} |
|||
|
|||
protected onSettingsSet(settings: WidgetSettings) { |
|||
this.updateDeviceAttributeWidgetSettingsForm = this.fb.group({ |
|||
title: [settings.title, []], |
|||
buttonText: [settings.buttonText, []], |
|||
entityAttributeType: [settings.entityAttributeType, []], |
|||
entityParameters: [settings.entityParameters, []], |
|||
styleButton: [settings.styleButton, []] |
|||
}); |
|||
} |
|||
} |
|||
Loading…
Reference in new issue