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