15 changed files with 895 additions and 10 deletions
File diff suppressed because one or more lines are too long
@ -0,0 +1,31 @@ |
|||||
|
<!-- |
||||
|
|
||||
|
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]="roundSwitchWidgetSettingsForm" 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.switch-title</mat-label> |
||||
|
<input matInput formControlName="title"> |
||||
|
</mat-form-field> |
||||
|
</fieldset> |
||||
|
<tb-switch-rpc-settings [aliasController]="aliasController" |
||||
|
[targetDeviceAliasId]="targetDeviceAliasId" |
||||
|
formControlName="switchRpcSettings"> |
||||
|
</tb-switch-rpc-settings> |
||||
|
</section> |
||||
@ -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.
|
||||
|
///
|
||||
|
|
||||
|
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 { switchRpcDefaultSettings } from '@home/components/widget/lib/settings/control/switch-rpc-settings.component'; |
||||
|
import { deepClone } from '@core/utils'; |
||||
|
|
||||
|
@Component({ |
||||
|
selector: 'tb-round-switch-widget-settings', |
||||
|
templateUrl: './round-switch-widget-settings.component.html', |
||||
|
styleUrls: ['./../widget-settings.scss'] |
||||
|
}) |
||||
|
export class RoundSwitchWidgetSettingsComponent extends WidgetSettingsComponent { |
||||
|
|
||||
|
roundSwitchWidgetSettingsForm: FormGroup; |
||||
|
|
||||
|
constructor(protected store: Store<AppState>, |
||||
|
private fb: FormBuilder) { |
||||
|
super(store); |
||||
|
} |
||||
|
|
||||
|
get targetDeviceAliasId(): string { |
||||
|
const aliasIds = this.widget.config.targetDeviceAliasIds; |
||||
|
if (aliasIds && aliasIds.length) { |
||||
|
return aliasIds[0]; |
||||
|
} |
||||
|
return null; |
||||
|
} |
||||
|
|
||||
|
protected settingsForm(): FormGroup { |
||||
|
return this.roundSwitchWidgetSettingsForm; |
||||
|
} |
||||
|
|
||||
|
protected defaultSettings(): WidgetSettings { |
||||
|
return { |
||||
|
title: '', |
||||
|
...switchRpcDefaultSettings() |
||||
|
}; |
||||
|
} |
||||
|
|
||||
|
protected onSettingsSet(settings: WidgetSettings) { |
||||
|
this.roundSwitchWidgetSettingsForm = this.fb.group({ |
||||
|
title: [settings.title, []], |
||||
|
switchRpcSettings: [settings.switchRpcSettings, []] |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
protected prepareInputSettings(settings: WidgetSettings): WidgetSettings { |
||||
|
const switchRpcSettings = deepClone(settings, ['title']); |
||||
|
return { |
||||
|
title: settings.title, |
||||
|
switchRpcSettings |
||||
|
}; |
||||
|
} |
||||
|
|
||||
|
protected prepareOutputSettings(settings: any): WidgetSettings { |
||||
|
return { |
||||
|
title: settings.title, |
||||
|
...settings.switchRpcSettings |
||||
|
}; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,58 @@ |
|||||
|
<!-- |
||||
|
|
||||
|
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]="slideToggleWidgetSettingsForm" 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.slide-toggle-label</mat-label> |
||||
|
<input matInput formControlName="title"> |
||||
|
</mat-form-field> |
||||
|
<section fxLayout="column" fxLayout.gt-sm="row" fxLayoutGap="8px"> |
||||
|
<mat-form-field fxFlex class="mat-block"> |
||||
|
<mat-label translate>widgets.rpc.label-position</mat-label> |
||||
|
<mat-select formControlName="labelPosition"> |
||||
|
<mat-option [value]="'before'"> |
||||
|
{{ 'widgets.rpc.label-position-before' | translate }} |
||||
|
</mat-option> |
||||
|
<mat-option [value]="'after'"> |
||||
|
{{ 'widgets.rpc.label-position-after' | translate }} |
||||
|
</mat-option> |
||||
|
</mat-select> |
||||
|
</mat-form-field> |
||||
|
<mat-form-field fxFlex class="mat-block"> |
||||
|
<mat-label translate>widgets.rpc.slider-color</mat-label> |
||||
|
<mat-select formControlName="sliderColor"> |
||||
|
<mat-option [value]="'primary'"> |
||||
|
{{ 'widgets.rpc.slider-color-primary' | translate }} |
||||
|
</mat-option> |
||||
|
<mat-option [value]="'accent'"> |
||||
|
{{ 'widgets.rpc.slider-color-accent' | translate }} |
||||
|
</mat-option> |
||||
|
<mat-option [value]="'warn'"> |
||||
|
{{ 'widgets.rpc.slider-color-warn' | translate }} |
||||
|
</mat-option> |
||||
|
</mat-select> |
||||
|
</mat-form-field> |
||||
|
</section> |
||||
|
</fieldset> |
||||
|
<tb-switch-rpc-settings [aliasController]="aliasController" |
||||
|
[targetDeviceAliasId]="targetDeviceAliasId" |
||||
|
formControlName="switchRpcSettings"> |
||||
|
</tb-switch-rpc-settings> |
||||
|
</section> |
||||
@ -0,0 +1,87 @@ |
|||||
|
///
|
||||
|
/// 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 { switchRpcDefaultSettings } from '@home/components/widget/lib/settings/control/switch-rpc-settings.component'; |
||||
|
import { deepClone } from '@core/utils'; |
||||
|
|
||||
|
@Component({ |
||||
|
selector: 'tb-slide-toggle-widget-settings', |
||||
|
templateUrl: './slide-toggle-widget-settings.component.html', |
||||
|
styleUrls: ['./../widget-settings.scss'] |
||||
|
}) |
||||
|
export class SlideToggleWidgetSettingsComponent extends WidgetSettingsComponent { |
||||
|
|
||||
|
slideToggleWidgetSettingsForm: FormGroup; |
||||
|
|
||||
|
constructor(protected store: Store<AppState>, |
||||
|
private fb: FormBuilder) { |
||||
|
super(store); |
||||
|
} |
||||
|
|
||||
|
get targetDeviceAliasId(): string { |
||||
|
const aliasIds = this.widget.config.targetDeviceAliasIds; |
||||
|
if (aliasIds && aliasIds.length) { |
||||
|
return aliasIds[0]; |
||||
|
} |
||||
|
return null; |
||||
|
} |
||||
|
|
||||
|
protected settingsForm(): FormGroup { |
||||
|
return this.slideToggleWidgetSettingsForm; |
||||
|
} |
||||
|
|
||||
|
protected defaultSettings(): WidgetSettings { |
||||
|
return { |
||||
|
title: '', |
||||
|
labelPosition: 'after', |
||||
|
sliderColor: 'accent', |
||||
|
...switchRpcDefaultSettings() |
||||
|
}; |
||||
|
} |
||||
|
|
||||
|
protected onSettingsSet(settings: WidgetSettings) { |
||||
|
this.slideToggleWidgetSettingsForm = this.fb.group({ |
||||
|
title: [settings.title, []], |
||||
|
labelPosition: [settings.labelPosition, []], |
||||
|
sliderColor: [settings.sliderColor, []], |
||||
|
switchRpcSettings: [settings.switchRpcSettings, []] |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
protected prepareInputSettings(settings: WidgetSettings): WidgetSettings { |
||||
|
const switchRpcSettings = deepClone(settings, ['title', 'labelPosition', 'sliderColor']); |
||||
|
return { |
||||
|
title: settings.title, |
||||
|
labelPosition: settings.labelPosition, |
||||
|
sliderColor: settings.sliderColor, |
||||
|
switchRpcSettings |
||||
|
}; |
||||
|
} |
||||
|
|
||||
|
protected prepareOutputSettings(settings: any): WidgetSettings { |
||||
|
return { |
||||
|
title: settings.title, |
||||
|
labelPosition: settings.labelPosition, |
||||
|
sliderColor: settings.sliderColor, |
||||
|
...settings.switchRpcSettings |
||||
|
}; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,34 @@ |
|||||
|
<!-- |
||||
|
|
||||
|
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]="switchControlWidgetSettingsForm" 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.switch-title</mat-label> |
||||
|
<input matInput formControlName="title"> |
||||
|
</mat-form-field> |
||||
|
<mat-slide-toggle formControlName="showOnOffLabels" class="slide-block"> |
||||
|
{{ 'widgets.rpc.show-on-off-labels' | translate }} |
||||
|
</mat-slide-toggle> |
||||
|
</fieldset> |
||||
|
<tb-switch-rpc-settings [aliasController]="aliasController" |
||||
|
[targetDeviceAliasId]="targetDeviceAliasId" |
||||
|
formControlName="switchRpcSettings"> |
||||
|
</tb-switch-rpc-settings> |
||||
|
</section> |
||||
@ -0,0 +1,83 @@ |
|||||
|
///
|
||||
|
/// 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 { switchRpcDefaultSettings } from '@home/components/widget/lib/settings/control/switch-rpc-settings.component'; |
||||
|
import { deepClone } from '@core/utils'; |
||||
|
|
||||
|
@Component({ |
||||
|
selector: 'tb-switch-control-widget-settings', |
||||
|
templateUrl: './switch-control-widget-settings.component.html', |
||||
|
styleUrls: ['./../widget-settings.scss'] |
||||
|
}) |
||||
|
export class SwitchControlWidgetSettingsComponent extends WidgetSettingsComponent { |
||||
|
|
||||
|
switchControlWidgetSettingsForm: FormGroup; |
||||
|
|
||||
|
constructor(protected store: Store<AppState>, |
||||
|
private fb: FormBuilder) { |
||||
|
super(store); |
||||
|
} |
||||
|
|
||||
|
get targetDeviceAliasId(): string { |
||||
|
const aliasIds = this.widget.config.targetDeviceAliasIds; |
||||
|
if (aliasIds && aliasIds.length) { |
||||
|
return aliasIds[0]; |
||||
|
} |
||||
|
return null; |
||||
|
} |
||||
|
|
||||
|
protected settingsForm(): FormGroup { |
||||
|
return this.switchControlWidgetSettingsForm; |
||||
|
} |
||||
|
|
||||
|
protected defaultSettings(): WidgetSettings { |
||||
|
return { |
||||
|
title: '', |
||||
|
showOnOffLabels: true, |
||||
|
...switchRpcDefaultSettings() |
||||
|
}; |
||||
|
} |
||||
|
|
||||
|
protected onSettingsSet(settings: WidgetSettings) { |
||||
|
this.switchControlWidgetSettingsForm = this.fb.group({ |
||||
|
title: [settings.title, []], |
||||
|
showOnOffLabels: [settings.showOnOffLabels, []], |
||||
|
switchRpcSettings: [settings.switchRpcSettings, []] |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
protected prepareInputSettings(settings: WidgetSettings): WidgetSettings { |
||||
|
const switchRpcSettings = deepClone(settings, ['title', 'showOnOffLabels']); |
||||
|
return { |
||||
|
title: settings.title, |
||||
|
showOnOffLabels: settings.showOnOffLabels, |
||||
|
switchRpcSettings |
||||
|
}; |
||||
|
} |
||||
|
|
||||
|
protected prepareOutputSettings(settings: any): WidgetSettings { |
||||
|
return { |
||||
|
title: settings.title, |
||||
|
showOnOffLabels: settings.showOnOffLabels, |
||||
|
...settings.switchRpcSettings |
||||
|
}; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,123 @@ |
|||||
|
<!-- |
||||
|
|
||||
|
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]="switchRpcSettingsFormGroup" fxLayout="column"> |
||||
|
<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.retrieve-value-settings</legend> |
||||
|
<mat-form-field fxFlex class="mat-block"> |
||||
|
<mat-label translate>widgets.rpc.retrieve-value-method</mat-label> |
||||
|
<mat-select formControlName="retrieveValueMethod"> |
||||
|
<mat-option [value]="'none'"> |
||||
|
{{ 'widgets.rpc.retrieve-value-method-none' | translate }} |
||||
|
</mat-option> |
||||
|
<mat-option [value]="'rpc'"> |
||||
|
{{ 'widgets.rpc.retrieve-value-method-rpc' | translate }} |
||||
|
</mat-option> |
||||
|
<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> |
||||
|
<mat-form-field [fxShow]="switchRpcSettingsFormGroup.get('retrieveValueMethod').value === 'attribute' || |
||||
|
switchRpcSettingsFormGroup.get('retrieveValueMethod').value === 'timeseries'" fxFlex class="mat-block"> |
||||
|
<mat-label>{{ (switchRpcSettingsFormGroup.get('retrieveValueMethod').value === 'attribute' |
||||
|
? 'widgets.rpc.attribute-value-key' |
||||
|
: 'widgets.rpc.timeseries-value-key') | translate }}</mat-label> |
||||
|
<input required matInput |
||||
|
type="text" placeholder="{{ 'entity.key-name' | translate }}" |
||||
|
#keyInput |
||||
|
formControlName="valueKey" |
||||
|
[matAutocomplete]="keyAutocomplete"> |
||||
|
<button *ngIf="switchRpcSettingsFormGroup.get('valueKey').value && !switchRpcSettingsFormGroup.get('valueKey').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> |
||||
|
<mat-form-field [fxShow]="switchRpcSettingsFormGroup.get('retrieveValueMethod').value === 'rpc'" fxFlex class="mat-block"> |
||||
|
<mat-label translate>widgets.rpc.get-value-method</mat-label> |
||||
|
<input required matInput formControlName="getValueMethod"> |
||||
|
</mat-form-field> |
||||
|
<tb-js-func [fxHide]="switchRpcSettingsFormGroup.get('retrieveValueMethod').value === 'none'" |
||||
|
formControlName="parseValueFunction" |
||||
|
[globalVariables]="functionScopeVariables" |
||||
|
[functionArgs]="['data']" |
||||
|
functionTitle="{{ 'widgets.rpc.parse-value-function' | translate }}" |
||||
|
helpId="widget/lib/rpc/parse_value_fn"> |
||||
|
</tb-js-func> |
||||
|
</fieldset> |
||||
|
<fieldset class="fields-group"> |
||||
|
<legend class="group-title" translate>widgets.rpc.update-value-settings</legend> |
||||
|
<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> |
||||
|
<tb-js-func |
||||
|
formControlName="convertValueFunction" |
||||
|
[globalVariables]="functionScopeVariables" |
||||
|
[functionArgs]="['value']" |
||||
|
functionTitle="{{ 'widgets.rpc.convert-value-function' | translate }}" |
||||
|
helpId="widget/lib/rpc/convert_value_fn"> |
||||
|
</tb-js-func> |
||||
|
</fieldset> |
||||
|
</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 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]="switchRpcSettingsFormGroup.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,308 @@ |
|||||
|
///
|
||||
|
/// 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, |
||||
|
FormControl, |
||||
|
FormGroup, |
||||
|
NG_VALIDATORS, |
||||
|
NG_VALUE_ACCESSOR, |
||||
|
Validator, |
||||
|
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 { WidgetService } from '@core/http/widget.service'; |
||||
|
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'; |
||||
|
|
||||
|
export declare type RpcRetrieveValueMethod = 'none' | 'rpc' | 'attribute' | 'timeseries'; |
||||
|
|
||||
|
export interface SwitchRpcSettings { |
||||
|
initialValue: boolean; |
||||
|
retrieveValueMethod: RpcRetrieveValueMethod; |
||||
|
valueKey: string; |
||||
|
getValueMethod: string; |
||||
|
setValueMethod: string; |
||||
|
parseValueFunction: string; |
||||
|
convertValueFunction: string; |
||||
|
requestTimeout: number; |
||||
|
requestPersistent: boolean; |
||||
|
persistentPollingInterval: number; |
||||
|
} |
||||
|
|
||||
|
export function switchRpcDefaultSettings(): SwitchRpcSettings { |
||||
|
return { |
||||
|
initialValue: false, |
||||
|
retrieveValueMethod: 'rpc', |
||||
|
valueKey: 'value', |
||||
|
getValueMethod: 'getValue', |
||||
|
parseValueFunction: 'return data ? true : false;', |
||||
|
setValueMethod: 'setValue', |
||||
|
convertValueFunction: 'return value;', |
||||
|
requestTimeout: 500, |
||||
|
requestPersistent: false, |
||||
|
persistentPollingInterval: 5000 |
||||
|
}; |
||||
|
} |
||||
|
|
||||
|
@Component({ |
||||
|
selector: 'tb-switch-rpc-settings', |
||||
|
templateUrl: './switch-rpc-settings.component.html', |
||||
|
styleUrls: ['./../widget-settings.scss'], |
||||
|
providers: [ |
||||
|
{ |
||||
|
provide: NG_VALUE_ACCESSOR, |
||||
|
useExisting: forwardRef(() => SwitchRpcSettingsComponent), |
||||
|
multi: true |
||||
|
}, |
||||
|
{ |
||||
|
provide: NG_VALIDATORS, |
||||
|
useExisting: forwardRef(() => SwitchRpcSettingsComponent), |
||||
|
multi: true |
||||
|
} |
||||
|
] |
||||
|
}) |
||||
|
export class SwitchRpcSettingsComponent extends PageComponent implements OnInit, ControlValueAccessor, Validator, OnChanges { |
||||
|
|
||||
|
@ViewChild('keyInput') keyInput: ElementRef; |
||||
|
|
||||
|
@Input() |
||||
|
disabled: boolean; |
||||
|
|
||||
|
@Input() |
||||
|
aliasController: IAliasController; |
||||
|
|
||||
|
@Input() |
||||
|
targetDeviceAliasId: string; |
||||
|
|
||||
|
functionScopeVariables = this.widgetService.getWidgetScopeVariables(); |
||||
|
|
||||
|
private modelValue: SwitchRpcSettings; |
||||
|
|
||||
|
private propagateChange = null; |
||||
|
|
||||
|
public switchRpcSettingsFormGroup: 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 widgetService: WidgetService, |
||||
|
private entityService: EntityService, |
||||
|
private fb: FormBuilder) { |
||||
|
super(store); |
||||
|
} |
||||
|
|
||||
|
ngOnInit(): void { |
||||
|
this.switchRpcSettingsFormGroup = this.fb.group({ |
||||
|
|
||||
|
// Value settings
|
||||
|
|
||||
|
initialValue: [false, []], |
||||
|
|
||||
|
// --> Retrieve value settings
|
||||
|
|
||||
|
retrieveValueMethod: ['rpc', []], |
||||
|
valueKey: ['value', [Validators.required]], |
||||
|
getValueMethod: ['getValue', [Validators.required]], |
||||
|
parseValueFunction: ['return data ? true : false;', []], |
||||
|
|
||||
|
// --> Update value settings
|
||||
|
|
||||
|
setValueMethod: ['setValue', [Validators.required]], |
||||
|
convertValueFunction: ['return value;', []], |
||||
|
|
||||
|
// RPC settings
|
||||
|
|
||||
|
requestTimeout: [500, [Validators.min(0)]], |
||||
|
|
||||
|
// Persistent RPC settings
|
||||
|
|
||||
|
requestPersistent: [false, []], |
||||
|
persistentPollingInterval: [5000, [Validators.min(1000)]], |
||||
|
}); |
||||
|
this.switchRpcSettingsFormGroup.get('retrieveValueMethod').valueChanges.subscribe(() => { |
||||
|
this.clearKeysCache(); |
||||
|
this.updateValidators(true); |
||||
|
}); |
||||
|
this.switchRpcSettingsFormGroup.get('requestPersistent').valueChanges.subscribe(() => { |
||||
|
this.updateValidators(true); |
||||
|
}); |
||||
|
this.switchRpcSettingsFormGroup.valueChanges.subscribe(() => { |
||||
|
this.updateModel(); |
||||
|
}); |
||||
|
this.updateValidators(false); |
||||
|
|
||||
|
this.filteredKeys = this.switchRpcSettingsFormGroup.get('valueKey').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') { |
||||
|
this.clearKeysCache(); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
registerOnChange(fn: any): void { |
||||
|
this.propagateChange = fn; |
||||
|
} |
||||
|
|
||||
|
registerOnTouched(fn: any): void { |
||||
|
} |
||||
|
|
||||
|
setDisabledState(isDisabled: boolean): void { |
||||
|
this.disabled = isDisabled; |
||||
|
if (isDisabled) { |
||||
|
this.switchRpcSettingsFormGroup.disable({emitEvent: false}); |
||||
|
} else { |
||||
|
this.switchRpcSettingsFormGroup.enable({emitEvent: false}); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
writeValue(value: SwitchRpcSettings): void { |
||||
|
this.modelValue = value; |
||||
|
this.switchRpcSettingsFormGroup.patchValue( |
||||
|
value, {emitEvent: false} |
||||
|
); |
||||
|
this.updateValidators(false); |
||||
|
} |
||||
|
|
||||
|
clearKey() { |
||||
|
this.switchRpcSettingsFormGroup.get('valueKey').patchValue(null, {emitEvent: true}); |
||||
|
setTimeout(() => { |
||||
|
this.keyInput.nativeElement.blur(); |
||||
|
this.keyInput.nativeElement.focus(); |
||||
|
}, 0); |
||||
|
} |
||||
|
|
||||
|
public validate(c: FormControl) { |
||||
|
return this.switchRpcSettingsFormGroup.valid ? null : { |
||||
|
switchRpcSettings: { |
||||
|
valid: false, |
||||
|
}, |
||||
|
}; |
||||
|
} |
||||
|
|
||||
|
private updateModel() { |
||||
|
const value: SwitchRpcSettings = this.switchRpcSettingsFormGroup.value; |
||||
|
this.modelValue = value; |
||||
|
this.propagateChange(this.modelValue); |
||||
|
} |
||||
|
|
||||
|
private updateValidators(emitEvent?: boolean): void { |
||||
|
const retrieveValueMethod: RpcRetrieveValueMethod = this.switchRpcSettingsFormGroup.get('retrieveValueMethod').value; |
||||
|
const requestPersistent: boolean = this.switchRpcSettingsFormGroup.get('requestPersistent').value; |
||||
|
if (retrieveValueMethod === 'none') { |
||||
|
this.switchRpcSettingsFormGroup.get('valueKey').disable({emitEvent}); |
||||
|
this.switchRpcSettingsFormGroup.get('getValueMethod').disable({emitEvent}); |
||||
|
this.switchRpcSettingsFormGroup.get('parseValueFunction').disable({emitEvent}); |
||||
|
} else if (retrieveValueMethod === 'rpc') { |
||||
|
this.switchRpcSettingsFormGroup.get('valueKey').disable({emitEvent}); |
||||
|
this.switchRpcSettingsFormGroup.get('getValueMethod').enable({emitEvent}); |
||||
|
this.switchRpcSettingsFormGroup.get('parseValueFunction').enable({emitEvent}); |
||||
|
} else { |
||||
|
this.switchRpcSettingsFormGroup.get('valueKey').enable({emitEvent}); |
||||
|
this.switchRpcSettingsFormGroup.get('getValueMethod').disable({emitEvent}); |
||||
|
this.switchRpcSettingsFormGroup.get('parseValueFunction').enable({emitEvent}); |
||||
|
} |
||||
|
if (requestPersistent) { |
||||
|
this.switchRpcSettingsFormGroup.get('persistentPollingInterval').enable({emitEvent}); |
||||
|
} else { |
||||
|
this.switchRpcSettingsFormGroup.get('persistentPollingInterval').disable({emitEvent}); |
||||
|
} |
||||
|
this.switchRpcSettingsFormGroup.get('valueKey').updateValueAndValidity({emitEvent: false}); |
||||
|
this.switchRpcSettingsFormGroup.get('getValueMethod').updateValueAndValidity({emitEvent: false}); |
||||
|
this.switchRpcSettingsFormGroup.get('parseValueFunction').updateValueAndValidity({emitEvent: false}); |
||||
|
this.switchRpcSettingsFormGroup.get('persistentPollingInterval').updateValueAndValidity({emitEvent: false}); |
||||
|
} |
||||
|
|
||||
|
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 retrieveValueMethod: RpcRetrieveValueMethod = this.switchRpcSettingsFormGroup.get('retrieveValueMethod').value; |
||||
|
const dataKeyTypes = retrieveValueMethod === 'attribute' ? [DataKeyType.attribute] : [DataKeyType.timeseries]; |
||||
|
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); |
||||
|
} |
||||
|
} |
||||
Loading…
Reference in new issue