UI: Improved knob control widgets

This commit is contained in:
Vladyslav_Prykhodko 2025-06-02 18:14:49 +03:00
parent 0047168bfc
commit c37b6d6d46
7 changed files with 288 additions and 223 deletions

View File

@ -12,10 +12,9 @@
"templateHtml": "<tb-knob [ctx]='ctx'></tb-knob>",
"templateCss": "",
"controllerScript": "self.onInit = function() {\n}\n\nself.onResize = function() {\n}\n\nself.onDestroy = function() {\n}\n",
"settingsSchema": "",
"dataKeySettingsSchema": "{}\n",
"dataKeySettingsForm": [],
"settingsDirective": "tb-knob-control-widget-settings",
"defaultConfig": "{\"targetDeviceAliases\":[],\"showTitle\":false,\"backgroundColor\":\"#e6e7e8\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"requestTimeout\":500,\"maxValue\":100,\"initialValue\":50,\"minValue\":0,\"title\":\"Knob control\",\"getValueMethod\":\"getValue\",\"setValueMethod\":\"setValue\"},\"title\":\"Knob Control\",\"dropShadow\":true,\"enableFullscreen\":false,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{},\"decimals\":2}"
"defaultConfig": "{\"targetDeviceAliases\":[],\"showTitle\":false,\"backgroundColor\":\"#e6e7e8\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{},\"title\":\"Knob Control\",\"dropShadow\":true,\"enableFullscreen\":false,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{},\"decimals\":2,\"units\":null}"
},
"tags": [
"dial",

View File

@ -27,9 +27,6 @@
<div #knobTopPointer class="top-pointer"></div>
</div>
<div class="top"></div>
<div #knobErrorContainer class="error-container flex flex-row items-center justify-center" [style.background]="error?.length ? 'rgba(255,255,255,0.25)' : 'none'">
<span #knobError class="knob-error" [innerHTML]="error"></span>
</div>
<div #knobTitleContainer class="title-container flex flex-row items-center justify-center">
<span #knobTitle class="knob-title">{{ title }}</span>
</div>

View File

@ -14,37 +14,27 @@
/// limitations under the License.
///
import { Component, ElementRef, Input, OnDestroy, OnInit, ViewChild } from '@angular/core';
import { PageComponent } from '@shared/components/page.component';
import { ChangeDetectorRef, Component, ElementRef, Input, OnDestroy, OnInit, ViewChild } from '@angular/core';
import { WidgetContext } from '@home/models/widget-component.models';
import { UtilsService } from '@core/services/utils.service';
import { Store } from '@ngrx/store';
import { AppState } from '@core/core.state';
import { isDefined, isNumber } from '@core/utils';
import { deepClone, isDefined } from '@core/utils';
import { CanvasDigitalGaugeOptions } from '@home/components/widget/lib/canvas-digital-gauge';
import tinycolor from 'tinycolor2';
import { ColorProcessor, gradientColor } from '@shared/models/widget-settings.models';
import { getSourceTbUnitSymbol } from '@shared/models/unit.models';
import { ColorProcessor, gradientColor, ValueFormatProcessor } from '@shared/models/widget-settings.models';
import {
KnobSettings,
knobWidgetDefaultSettings,
prepareKnobSettings
} from '@shared/models/widget/rpc/knob.component.models';
import { ValueType } from '@shared/models/constants';
import { BasicActionWidgetComponent, ValueSetter } from '@home/components/widget/lib/action/action-widget.models';
import GenericOptions = CanvasGauges.GenericOptions;
interface KnobSettings {
minValue: number;
maxValue: number;
initialValue: number;
title: string;
getValueMethod: string;
setValueMethod: string;
requestTimeout: number;
requestPersistent: boolean;
persistentPollingInterval: number;
}
@Component({
selector: 'tb-knob',
templateUrl: './knob.component.html',
styleUrls: ['./knob.component.scss']
})
export class KnobComponent extends PageComponent implements OnInit, OnDestroy {
export class KnobComponent extends BasicActionWidgetComponent implements OnInit, OnDestroy {
@ViewChild('knob', {static: true}) knobRef: ElementRef<HTMLElement>;
@ViewChild('knobContainer', {static: true}) knobContainerRef: ElementRef<HTMLElement>;
@ -52,8 +42,6 @@ export class KnobComponent extends PageComponent implements OnInit, OnDestroy {
@ViewChild('knobTopPointer', {static: true}) knobTopPointerRef: ElementRef<HTMLElement>;
@ViewChild('knobValueContainer', {static: true}) knobValueContainerRef: ElementRef<HTMLElement>;
@ViewChild('knobValue', {static: true}) knobValueRef: ElementRef<HTMLElement>;
@ViewChild('knobErrorContainer', {static: true}) knobErrorContainerRef: ElementRef<HTMLElement>;
@ViewChild('knobError', {static: true}) knobErrorRef: ElementRef<HTMLElement>;
@ViewChild('knobTitleContainer', {static: true}) knobTitleContainerRef: ElementRef<HTMLElement>;
@ViewChild('knobTitle', {static: true}) knobTitleRef: ElementRef<HTMLElement>;
@ViewChild('knobMinmaxContainer', {static: true}) knobMinmaxContainerRef: ElementRef<HTMLElement>;
@ -64,7 +52,6 @@ export class KnobComponent extends PageComponent implements OnInit, OnDestroy {
ctx: WidgetContext;
value = '0';
error = '';
title = '';
minValue: number;
maxValue: number;
@ -79,13 +66,6 @@ export class KnobComponent extends PageComponent implements OnInit, OnDestroy {
private minDeg = -45;
private maxDeg = 225;
private isSimulated: boolean;
private requestTimeout: number;
private requestPersistent: boolean;
private persistentPollingInterval: number;
private getValueMethod: string;
private setValueMethod: string;
private executingUpdateValue: boolean;
private scheduledValue: number;
private rpcValue: number;
@ -98,8 +78,6 @@ export class KnobComponent extends PageComponent implements OnInit, OnDestroy {
private knobValue: JQuery<HTMLElement>;
private knobTitleContainer: JQuery<HTMLElement>;
private knobTitle: JQuery<HTMLElement>;
private knobErrorContainer: JQuery<HTMLElement>;
private knobError: JQuery<HTMLElement>;
private knobMinmaxContainer: JQuery<HTMLElement>;
private minmaxLabel: JQuery<HTMLElement>;
private textMeasure: JQuery<HTMLElement>;
@ -109,9 +87,11 @@ export class KnobComponent extends PageComponent implements OnInit, OnDestroy {
private knobResize$: ResizeObserver;
constructor(private utils: UtilsService,
protected store: Store<AppState>) {
super(store);
private valueSetter: ValueSetter<number>;
private valueFormat: ValueFormatProcessor;
constructor(protected cd: ChangeDetectorRef) {
super(cd);
}
ngOnInit(): void {
@ -123,8 +103,6 @@ export class KnobComponent extends PageComponent implements OnInit, OnDestroy {
this.knobValue = $(this.knobValueRef.nativeElement);
this.knobTitleContainer = $(this.knobTitleContainerRef.nativeElement);
this.knobTitle = $(this.knobTitleRef.nativeElement);
this.knobErrorContainer = $(this.knobErrorContainerRef.nativeElement);
this.knobError = $(this.knobErrorRef.nativeElement);
this.knobMinmaxContainer = $(this.knobMinmaxContainerRef.nativeElement);
this.minmaxLabel = this.knobMinmaxContainer.find<HTMLElement>('.minmax-label');
this.textMeasure = $(this.textMeasureRef.nativeElement);
@ -145,7 +123,31 @@ export class KnobComponent extends PageComponent implements OnInit, OnDestroy {
}
private init() {
const settings: KnobSettings = this.ctx.settings;
const settings: KnobSettings = prepareKnobSettings({...deepClone(knobWidgetDefaultSettings), ...this.ctx.settings});
let initialValue = isDefined(settings.initialValue) ? settings.initialValue : this.minValue;
const getInitialStateSettings =
{...settings.initialState, actionLabel: this.ctx.translate.instant('widgets.slider.initial-value')};
this.createValueGetter(getInitialStateSettings, ValueType.DOUBLE, {
next: (value) => {
if (this.canvasBar) {
this.setValue(value);
} else {
initialValue = value;
}
}
});
const valueChangeSettings = {...settings.valueChange,
actionLabel: this.ctx.translate.instant('widgets.slider.on-value-change')};
this.valueSetter = this.createValueSetter(valueChangeSettings);
this.valueFormat = ValueFormatProcessor.fromSettings(this.ctx.$injector, {
units: this.ctx.units,
decimals: this.ctx.decimals,
showZeroDecimals: true
});
this.minValue = isDefined(settings.minValue) ? settings.minValue : 0;
this.maxValue = isDefined(settings.maxValue) ? settings.maxValue : 100;
@ -279,44 +281,10 @@ export class KnobComponent extends PageComponent implements OnInit, OnDestroy {
});
const subscription = this.ctx.defaultSubscription;
const rpcEnabled = subscription.rpcEnabled;
this.isSimulated = this.utils.widgetEditMode;
this.requestTimeout = 500;
if (settings.requestTimeout) {
this.requestTimeout = settings.requestTimeout;
}
this.requestPersistent = false;
if (settings.requestPersistent) {
this.requestPersistent = settings.requestPersistent;
}
this.persistentPollingInterval = 5000;
if (settings.persistentPollingInterval) {
this.persistentPollingInterval = settings.persistentPollingInterval;
}
this.getValueMethod = 'getValue';
if (settings.getValueMethod && settings.getValueMethod.length) {
this.getValueMethod = settings.getValueMethod;
}
this.setValueMethod = 'setValue';
if (settings.setValueMethod && settings.setValueMethod.length) {
this.setValueMethod = settings.setValueMethod;
}
import('@home/components/widget/lib/canvas-digital-gauge').then(
(gauge) => {
this.canvasBar = new gauge.CanvasDigitalGauge(canvasBarData).draw();
const initialValue = isDefined(settings.initialValue) ? settings.initialValue : this.minValue;
this.setValue(initialValue);
if (!rpcEnabled) {
this.onError('Target device is not set!');
} else {
if (!this.isSimulated) {
this.rpcRequestValue();
}
}
}
);
@ -348,7 +316,6 @@ export class KnobComponent extends PageComponent implements OnInit, OnDestroy {
this.canvasBar.update({width: size, height: size} as GenericOptions);
}
this.setFontSize(this.knobTitle, this.title, this.knobTitleContainer.height(), this.knobTitleContainer.width());
this.setFontSize(this.knobError, this.error, this.knobErrorContainer.height(), this.knobErrorContainer.width());
const minmaxHeight = this.knobMinmaxContainer.height();
this.minmaxLabel.css({fontSize: minmaxHeight + 'px', lineHeight: minmaxHeight + 'px'});
this.checkValueSize();
@ -408,27 +375,7 @@ export class KnobComponent extends PageComponent implements OnInit, OnDestroy {
}
private formatValue(value: any): string {
return this.ctx.utils.formatValue(value, this.ctx.decimals, getSourceTbUnitSymbol(this.ctx.units), true);
}
private rpcRequestValue() {
this.error = '';
this.ctx.controlApi.sendTwoWayCommand(this.getValueMethod, null, this.requestTimeout,
this.requestPersistent, this.persistentPollingInterval).subscribe(
(responseBody) => {
if (isNumber(responseBody)) {
const numValue = Number(Number(responseBody).toFixed(this.ctx.decimals));
this.setValue(numValue);
} else {
const errorText = `Unable to parse response: ${responseBody}`;
this.onError(errorText);
}
},
() => {
const errorText = this.ctx.defaultSubscription.rpcErrorText;
this.onError(errorText);
}
);
return this.valueFormat.format(value);
}
private rpcUpdateValue(value: number) {
@ -440,27 +387,19 @@ export class KnobComponent extends PageComponent implements OnInit, OnDestroy {
this.rpcValue = value;
this.executingUpdateValue = true;
}
this.error = '';
this.ctx.controlApi.sendOneWayCommand(this.setValueMethod, value, this.requestTimeout,
this.requestPersistent, this.persistentPollingInterval).subscribe(
() => {
this.executingUpdateValue = false;
if (this.scheduledValue != null && this.scheduledValue !== this.rpcValue) {
this.rpcUpdateValue(this.scheduledValue);
if (!this.ctx.isEdit && !this.ctx.isPreview) {
this.updateValue(this.valueSetter, value, {
next: () => {
this.executingUpdateValue = false;
if (this.scheduledValue != null && this.scheduledValue !== this.rpcValue) {
this.rpcUpdateValue(this.scheduledValue);
}
},
error: () => {
this.executingUpdateValue = false;
}
},
() => {
this.executingUpdateValue = false;
const errorText = this.ctx.defaultSubscription.rpcErrorText;
this.onError(errorText);
}
);
}
private onError(error: string) {
this.error = error;
this.setFontSize(this.knobError, this.error, this.knobErrorContainer.height(), this.knobErrorContainer.width());
this.ctx.detectChanges();
});
}
}
}

View File

@ -15,66 +15,66 @@
limitations under the License.
-->
<section class="tb-widget-settings flex flex-col" [formGroup]="knobControlWidgetSettingsForm">
<fieldset class="fields-group">
<legend class="group-title" translate>widgets.rpc.common-settings</legend>
<mat-form-field class="mat-block flex-1">
<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 class="mat-block flex-1">
<mat-label translate>widgets.rpc.initial-value</mat-label>
<input matInput type="number" formControlName="initialValue">
</mat-form-field>
<section class="flex flex-row xs:flex-col gt-xs:gap-2">
<mat-form-field class="mat-block flex-1">
<mat-label translate>widgets.rpc.min-value</mat-label>
<input required matInput type="number" formControlName="minValue">
<ng-container [formGroup]="knobControlWidgetSettingsForm">
<div class="tb-form-panel">
<div class="tb-form-panel-title" translate>widgets.knob.behavior</div>
<div class="tb-form-row">
<div class="fixed-title-width" tb-hint-tooltip-icon="{{'widgets.knob.initial-value-hint' | translate}}" translate>widgets.knob.initial-value</div>
<tb-get-value-action-settings class="flex-1"
panelTitle="{{ 'widgets.knob.initial-value' | translate }}"
[valueType]="valueType.DOUBLE"
[aliasController]="aliasController"
[targetDevice]="targetDevice"
[widgetType]="widgetType"
formControlName="initialState"></tb-get-value-action-settings>
</div>
<div class="tb-form-row space-between">
<div class="fixed-title-width" tb-hint-tooltip-icon="{{'widgets.knob.on-value-change-hint' | translate}}" translate>widgets.knob.on-value-change</div>
<tb-set-value-action-settings class="flex-1"
panelTitle="{{ 'widgets.knob.on-value-change' | translate }}"
[valueType]="valueType.DOUBLE"
[aliasController]="aliasController"
[targetDevice]="targetDevice"
[widgetType]="widgetType"
formControlName="valueChange"></tb-set-value-action-settings>
</div>
</div>
<div class="tb-form-panel">
<div class="tb-form-panel-title" translate>widget-config.appearance</div>
<div class="tb-form-row column-xs">
<div class="fixed-title-width" translate>widgets.rpc.knob-title</div>
<mat-form-field class="flex" appearance="outline" subscriptSizing="dynamic">
<input matInput formControlName="title" placeholder="{{ 'widget-config.set' | translate }}">
</mat-form-field>
<mat-form-field class="mat-block flex-1">
<mat-label translate>widgets.rpc.max-value</mat-label>
<input required matInput type="number" formControlName="maxValue">
</div>
<div class="tb-form-row space-between column-xs">
<div>{{ 'widgets.knob.range' | translate }}</div>
<div class="flex flex-row items-center justify-start gap-2">
<div class="tb-small-label" translate>widgets.knob.min</div>
<mat-form-field appearance="outline" class="number" subscriptSizing="dynamic">
<input matInput formControlName="minValue" type="number" placeholder="{{ 'widget-config.set' | translate }}">
</mat-form-field>
<div class="tb-small-label" translate>widgets.knob.max</div>
<mat-form-field appearance="outline" class="number" subscriptSizing="dynamic">
<input matInput formControlName="maxValue" type="number" placeholder="{{ 'widget-config.set' | translate }}">
</mat-form-field>
</div>
</div>
<div class="tb-form-row column-xs">
<div class="fixed-title-width">{{ 'widgets.knob.value' | translate }}</div>
<div class="flex flex-1 flex-row items-center justify-start gap-2">
<tb-unit-input class="flex" formControlName="valueUnits" supportsUnitConversion></tb-unit-input>
<mat-form-field appearance="outline" class="number flex" subscriptSizing="dynamic">
<input matInput formControlName="valueDecimals" type="number" min="0" max="15" step="1" placeholder="{{ 'widget-config.set' | translate }}">
<div matSuffix class="lt-md:!hidden" translate>widget-config.decimals-suffix</div>
</mat-form-field>
</div>
</div>
<div class="tb-form-row space-between column-xs">
<div class="fixed-title-width" translate>widgets.knob.fallback-initial-value</div>
<mat-form-field class="number" appearance="outline" subscriptSizing="dynamic">
<input matInput formControlName="initialValue" type="number" placeholder="{{ 'widget-config.set' | translate }}">
</mat-form-field>
</section>
<mat-form-field class="mat-block flex-1">
<mat-label translate>widgets.rpc.get-value-method</mat-label>
<input required matInput formControlName="getValueMethod">
</mat-form-field>
<mat-form-field class="mat-block flex-1">
<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 class="mat-block flex-1">
<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 class="flex flex-row flex-wrap">
<mat-panel-title>
<mat-slide-toggle formControlName="requestPersistent" (click)="$event.stopPropagation()"
class="flex items-stretch justify-center">
{{ 'widgets.rpc.request-persistent' | translate }}
</mat-slide-toggle>
</mat-panel-title>
<mat-panel-description class="flex items-center justify-end xs:!hidden" translate>
widget-config.advanced-settings
</mat-panel-description>
</mat-expansion-panel-header>
<ng-template matExpansionPanelContent>
<mat-form-field class="mat-block flex-1">
<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>
</div>
</div>
</ng-container>

View File

@ -15,10 +15,13 @@
///
import { Component } from '@angular/core';
import { WidgetSettings, WidgetSettingsComponent } from '@shared/models/widget.models';
import { TargetDevice, WidgetSettings, WidgetSettingsComponent, widgetType } from '@shared/models/widget.models';
import { UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms';
import { Store } from '@ngrx/store';
import { AppState } from '@core/core.state';
import { knobWidgetDefaultSettings, prepareKnobSettings } from '@shared/models/widget/rpc/knob.component.models';
import { ValueType } from '@shared/models/constants';
import { deepClone } from '@core/utils';
@Component({
selector: 'tb-knob-control-widget-settings',
@ -27,6 +30,16 @@ import { AppState } from '@core/core.state';
})
export class KnobControlWidgetSettingsComponent extends WidgetSettingsComponent {
get targetDevice(): TargetDevice {
return this.widgetConfig?.config?.targetDevice;
}
get widgetType(): widgetType {
return this.widgetConfig?.widgetType;
}
valueType = ValueType;
knobControlWidgetSettingsForm: UntypedFormGroup;
constructor(protected store: Store<AppState>,
@ -39,17 +52,25 @@ export class KnobControlWidgetSettingsComponent extends WidgetSettingsComponent
}
protected defaultSettings(): WidgetSettings {
return {
title: '',
minValue: 0,
maxValue: 100,
initialValue: 50,
getValueMethod: 'getValue',
setValueMethod: 'setValue',
requestTimeout: 500,
requestPersistent: false,
persistentPollingInterval: 5000
};
return knobWidgetDefaultSettings;
}
protected prepareInputSettings(settings: WidgetSettings): WidgetSettings {
const knobSettings = prepareKnobSettings(deepClone(settings) as any) as WidgetSettings;
knobSettings.valueDecimals = this.widgetConfig?.config?.decimals ?? 2;
knobSettings.valueUnits = deepClone(this.widgetConfig?.config?.units);
return super.prepareInputSettings(knobSettings);
}
protected prepareOutputSettings(settings: any): WidgetSettings {
const newSettings = deepClone(settings);
if (this.widgetConfig?.config) {
this.widgetConfig.config.units = settings.valueUnits;
this.widgetConfig.config.decimals = settings.valueDecimals;
}
delete newSettings.valueUnits;
delete newSettings.valueDecimals;
return super.prepareOutputSettings(newSettings);
}
protected onSettingsSet(settings: WidgetSettings) {
@ -61,35 +82,16 @@ export class KnobControlWidgetSettingsComponent extends WidgetSettingsComponent
// Value settings
initialValue: [settings.initialValue, []],
initialState: [settings.initialState, []],
valueChange: [settings.valueChange, []],
minValue: [settings.minValue, [Validators.required]],
maxValue: [settings.maxValue, [Validators.required]],
getValueMethod: [settings.getValueMethod, [Validators.required]],
setValueMethod: [settings.setValueMethod, [Validators.required]],
valueUnits: [settings.valueUnits, []],
valueDecimals: [settings.valueDecimals, []],
initialValue: [settings.initialValue, []],
// 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});
}
}

View File

@ -0,0 +1,120 @@
///
/// Copyright © 2016-2025 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 {
DataToValueType,
GetValueAction,
GetValueSettings,
SetValueAction,
SetValueSettings,
ValueToDataType
} from '@shared/models/action-widget-settings.models';
import { AttributeScope } from '@shared/models/telemetry/telemetry.models';
import { isDefinedAndNotNull } from '@core/utils';
export interface KnobSettings {
initialState: GetValueSettings<number>;
valueChange: SetValueSettings;
minValue: number;
maxValue: number;
initialValue: number;
title: string;
getValueMethod?: string; //deprecated
setValueMethod?: string; //deprecated
requestTimeout?: number; //deprecated
requestPersistent?: boolean; //deprecated
persistentPollingInterval?: number; //deprecated
}
export const knobWidgetDefaultSettings: KnobSettings = {
initialState: {
action: GetValueAction.EXECUTE_RPC,
defaultValue: 50,
executeRpc: {
method: 'getValue',
requestTimeout: 500,
requestPersistent: false,
persistentPollingInterval: 5000
},
getAttribute: {
key: 'value',
scope: null
},
getTimeSeries: {
key: 'value'
},
getAlarmStatus: {
severityList: null,
typeList: null
},
dataToValue: {
type: DataToValueType.NONE,
compareToValue: true,
dataToValueFunction: '/* Should return double value */\nreturn data;'
}
},
valueChange: {
action: SetValueAction.EXECUTE_RPC,
executeRpc: {
method: 'setValue',
requestTimeout: 500,
requestPersistent: false,
persistentPollingInterval: 5000
},
setAttribute: {
key: 'value',
scope: AttributeScope.SERVER_SCOPE
},
putTimeSeries: {
key: 'value'
},
valueToData: {
type: ValueToDataType.VALUE,
constantValue: 0,
valueToDataFunction: '/* Convert input double value to RPC parameters or attribute/time-series value */\nreturn value;'
}
},
title: '',
minValue: 0,
maxValue: 100,
initialValue: 50
}
export const prepareKnobSettings = (settings: KnobSettings): KnobSettings => {
if (isDefinedAndNotNull(settings.getValueMethod)) {
settings.initialState.executeRpc.method = settings.getValueMethod;
}
if (isDefinedAndNotNull(settings.setValueMethod)) {
settings.valueChange.executeRpc.method = settings.setValueMethod;
}
if (isDefinedAndNotNull(settings.requestPersistent)) {
settings.initialState.executeRpc.requestPersistent = settings.requestPersistent;
settings.valueChange.executeRpc.requestPersistent = settings.requestPersistent;
}
if (isDefinedAndNotNull(settings.persistentPollingInterval)) {
settings.initialState.executeRpc.persistentPollingInterval = settings.persistentPollingInterval;
settings.valueChange.executeRpc.persistentPollingInterval = settings.persistentPollingInterval;
}
if (isDefinedAndNotNull(settings.requestTimeout)) {
settings.initialState.executeRpc.requestTimeout = settings.requestTimeout;
settings.valueChange.executeRpc.requestTimeout = settings.requestTimeout;
}
return settings;
}

View File

@ -7960,6 +7960,17 @@
"fill-area-opacity": "Fill area opacity",
"range-chart-style": "Range chart style"
},
"knob": {
"behavior": "Behavior",
"initial-value": "Initial value",
"initial-value-hint": "Action to get the initial value of the knob.",
"on-value-change": "On value change",
"on-value-change-hint": "Action triggered when the knob value is changed.",
"range": "Range",
"min": "min",
"max": "max",
"fallback-initial-value": "Fallback initial value"
},
"rpc": {
"value-settings": "Value settings",
"initial-value": "Initial value",
@ -8015,10 +8026,7 @@
"led-status-value-attribute": "Device attribute containing led status value",
"led-status-value-timeseries": "Device time series containing led status value",
"check-status-method": "RPC check device status method",
"parse-led-status-value-function": "Parse led status value function",
"knob-title": "Knob title",
"min-value": "Minimum value",
"max-value": "Maximum value"
"parse-led-status-value-function": "Parse led status value function"
},
"maps": {
"map-type": {