diff --git a/ui-ngx/src/app/modules/home/components/profile/device/coap-device-profile-transport-configuration.component.ts b/ui-ngx/src/app/modules/home/components/profile/device/coap-device-profile-transport-configuration.component.ts
index 09fa6320ec..67966c981f 100644
--- a/ui-ngx/src/app/modules/home/components/profile/device/coap-device-profile-transport-configuration.component.ts
+++ b/ui-ngx/src/app/modules/home/components/profile/device/coap-device-profile-transport-configuration.component.ts
@@ -101,9 +101,9 @@ export class CoapDeviceProfileTransportConfigurationComponent implements Control
}),
clientSettings: this.fb.group({
powerMode: [PowerMode.DRX, Validators.required],
- edrxCycle: [{disabled: true, value: 0}, [Validators.required, Validators.min(0), Validators.pattern('[0-9]*')]],
- psmActivityTimer: [{disabled: true, value: 0}, [Validators.required, Validators.min(0), Validators.pattern('[0-9]*')]],
- pagingTransmissionWindow: [{disabled: true, value: 0}, [Validators.required, Validators.min(0), Validators.pattern('[0-9]*')]]
+ edrxCycle: [{disabled: true, value: 0}, Validators.required],
+ psmActivityTimer: [{disabled: true, value: 0}, Validators.required],
+ pagingTransmissionWindow: [{disabled: true, value: 0}, Validators.required]
})}
);
this.coapTransportConfigurationFormGroup.get('coapDeviceTypeConfiguration.coapDeviceType').valueChanges.pipe(
diff --git a/ui-ngx/src/app/modules/home/components/profile/device/common/device-profile-common.module.ts b/ui-ngx/src/app/modules/home/components/profile/device/common/device-profile-common.module.ts
index 81bdcc49af..0ddcb507bb 100644
--- a/ui-ngx/src/app/modules/home/components/profile/device/common/device-profile-common.module.ts
+++ b/ui-ngx/src/app/modules/home/components/profile/device/common/device-profile-common.module.ts
@@ -15,13 +15,15 @@
///
import { NgModule } from '@angular/core';
-import { PowerModeSettingComponent } from '@home/components/profile/device/common/power-mode-setting.component';
+import { PowerModeSettingComponent } from './power-mode-setting.component';
import { SharedModule } from '@shared/shared.module';
import { CommonModule } from '@angular/common';
+import { TimeUnitSelectComponent } from './time-unit-select.component';
@NgModule({
declarations: [
- PowerModeSettingComponent
+ PowerModeSettingComponent,
+ TimeUnitSelectComponent
],
imports: [
CommonModule,
diff --git a/ui-ngx/src/app/modules/home/components/profile/device/common/power-mode-setting.component.html b/ui-ngx/src/app/modules/home/components/profile/device/common/power-mode-setting.component.html
index 8920001535..a61a2a3a09 100644
--- a/ui-ngx/src/app/modules/home/components/profile/device/common/power-mode-setting.component.html
+++ b/ui-ngx/src/app/modules/home/components/profile/device/common/power-mode-setting.component.html
@@ -26,38 +26,27 @@
-
- {{ 'device-profile.psm-activity-timer' | translate }}
-
-
- {{ 'device-profile.psm-activity-timer-required' | translate }}
-
-
- {{ 'device-profile.psm-activity-timer-pattern' | translate }}
-
-
+
+
diff --git a/ui-ngx/src/app/modules/home/components/profile/device/common/power-mode-setting.component.ts b/ui-ngx/src/app/modules/home/components/profile/device/common/power-mode-setting.component.ts
index cae224ad2e..366fa9a9ca 100644
--- a/ui-ngx/src/app/modules/home/components/profile/device/common/power-mode-setting.component.ts
+++ b/ui-ngx/src/app/modules/home/components/profile/device/common/power-mode-setting.component.ts
@@ -16,7 +16,12 @@
import { Component, Input, OnDestroy, OnInit } from '@angular/core';
import { FormGroup } from '@angular/forms';
-import { PowerMode, PowerModeTranslationMap } from '@home/components/profile/device/lwm2m/lwm2m-profile-config.models';
+import {
+ DEFAULT_EDRX_CYCLE,
+ DEFAULT_PAGING_TRANSMISSION_WINDOW, DEFAULT_PSM_ACTIVITY_TIMER,
+ PowerMode,
+ PowerModeTranslationMap
+} from '@home/components/profile/device/lwm2m/lwm2m-profile-config.models';
import { takeUntil } from 'rxjs/operators';
import { Subject } from 'rxjs';
@@ -63,13 +68,13 @@ export class PowerModeSettingComponent implements OnInit, OnDestroy {
private disablePSKMode() {
this.parentForm.get('psmActivityTimer').disable({emitEvent: false});
- this.parentForm.get('psmActivityTimer').reset(0, {emitEvent: false});
+ this.parentForm.get('psmActivityTimer').reset(DEFAULT_PSM_ACTIVITY_TIMER, {emitEvent: false});
}
private disableEdrxMode() {
this.parentForm.get('edrxCycle').disable({emitEvent: false});
- this.parentForm.get('edrxCycle').reset(0, {emitEvent: false});
+ this.parentForm.get('edrxCycle').reset(DEFAULT_EDRX_CYCLE, {emitEvent: false});
this.parentForm.get('pagingTransmissionWindow').disable({emitEvent: false});
- this.parentForm.get('pagingTransmissionWindow').reset(0, {emitEvent: false});
+ this.parentForm.get('pagingTransmissionWindow').reset(DEFAULT_PAGING_TRANSMISSION_WINDOW, {emitEvent: false});
}
}
diff --git a/ui-ngx/src/app/modules/home/components/profile/device/common/time-unit-select.component.html b/ui-ngx/src/app/modules/home/components/profile/device/common/time-unit-select.component.html
new file mode 100644
index 0000000000..1a71676bf8
--- /dev/null
+++ b/ui-ngx/src/app/modules/home/components/profile/device/common/time-unit-select.component.html
@@ -0,0 +1,40 @@
+
+
diff --git a/ui-ngx/src/app/modules/home/components/profile/device/common/time-unit-select.component.ts b/ui-ngx/src/app/modules/home/components/profile/device/common/time-unit-select.component.ts
new file mode 100644
index 0000000000..b868d8c49e
--- /dev/null
+++ b/ui-ngx/src/app/modules/home/components/profile/device/common/time-unit-select.component.ts
@@ -0,0 +1,194 @@
+///
+/// Copyright © 2016-2021 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, OnDestroy, OnInit } from '@angular/core';
+import {
+ ControlValueAccessor,
+ FormBuilder,
+ FormGroup,
+ NG_VALIDATORS,
+ NG_VALUE_ACCESSOR,
+ ValidationErrors,
+ Validator,
+ Validators
+} from '@angular/forms';
+import { Subject } from 'rxjs';
+import { takeUntil } from 'rxjs/operators';
+import {
+ FullTimeUnit,
+ HOUR,
+ MINUTE,
+ SECOND,
+ TimeUnit,
+ TimeUnitMilli,
+ timeUnitTranslationMap
+} from '@shared/models/time/time.models';
+import { isDefinedAndNotNull, isNumber } from '@core/utils';
+
+interface FormGroupModel {
+ time: number;
+ unit: FullTimeUnit;
+}
+
+@Component({
+ selector: 'tb-time-unit-select',
+ templateUrl: './time-unit-select.component.html',
+ styleUrls: [],
+ providers: [
+ {
+ provide: NG_VALUE_ACCESSOR,
+ useExisting: forwardRef(() => TimeUnitSelectComponent),
+ multi: true
+ },
+ {
+ provide: NG_VALIDATORS,
+ useExisting: forwardRef(() => TimeUnitSelectComponent),
+ multi: true
+ }
+ ]
+})
+export class TimeUnitSelectComponent implements OnInit, OnDestroy, ControlValueAccessor, Validator {
+
+ timeUnitSelectFormGroup: FormGroup;
+
+ timeUnits = Object.values({...TimeUnitMilli, ...TimeUnit}).filter(item => item !== TimeUnit.DAYS);
+ timeUnitTranslations = timeUnitTranslationMap;
+
+ private destroy$ = new Subject();
+
+ private timeUnitToTimeMap = new Map(
+ [
+ [TimeUnitMilli.MILLISECONDS, 1],
+ [TimeUnit.SECONDS, SECOND],
+ [TimeUnit.MINUTES, MINUTE],
+ [TimeUnit.HOURS, HOUR]
+ ]
+ );
+
+ private timeToTimeUnitMap = new Map(
+ [
+ [SECOND, TimeUnitMilli.MILLISECONDS],
+ [MINUTE, TimeUnit.SECONDS],
+ [HOUR, TimeUnit.MINUTES]
+ ]
+ );
+
+ @Input()
+ disabled: boolean;
+
+ @Input()
+ labelText: string;
+
+ @Input()
+ requiredText: string;
+
+ @Input()
+ patternText: string;
+
+ @Input()
+ minTime = 0;
+
+ @Input()
+ minText: string;
+
+ private propagateChange = (v: any) => {
+ }
+
+ constructor(private fb: FormBuilder) {
+ }
+
+ ngOnInit() {
+ this.timeUnitSelectFormGroup = this.fb.group({
+ time: [0, [Validators.required, Validators.min(this.minTime), Validators.pattern('[0-9]*')]],
+ unit: [TimeUnitMilli.MILLISECONDS]
+ });
+ this.timeUnitSelectFormGroup.valueChanges.pipe(
+ takeUntil(this.destroy$)
+ ).subscribe((value) => {
+ this.updateModel(value);
+ });
+ this.timeUnitSelectFormGroup.get('unit').valueChanges.pipe(
+ takeUntil(this.destroy$)
+ ).subscribe((unit: FullTimeUnit) => {
+ if (this.minTime > 0) {
+ const unitTime = this.timeUnitToTimeMap.get(unit);
+ const validationTime = Math.ceil(this.minTime / unitTime);
+ this.timeUnitSelectFormGroup.get('time').setValidators([Validators.required, Validators.min(validationTime), Validators.pattern('[0-9]*')]);
+ this.timeUnitSelectFormGroup.get('time').updateValueAndValidity({emitEvent: false});
+ }
+ });
+ }
+
+ ngOnDestroy() {
+ this.destroy$.next();
+ this.destroy$.complete();
+ }
+
+ registerOnChange(fn: any) {
+ this.propagateChange = fn;
+ }
+
+ registerOnTouched(fn: any) {
+ }
+
+ setDisabledState(isDisabled: boolean) {
+ this.disabled = isDisabled;
+ if (this.disabled) {
+ this.timeUnitSelectFormGroup.disable({emitEvent: false});
+ } else {
+ this.timeUnitSelectFormGroup.enable({emitEvent: false});
+ }
+ }
+
+ writeValue(value: number) {
+ const formValue: FormGroupModel = {
+ time: 0,
+ unit: TimeUnitMilli.MILLISECONDS
+ };
+ if (isDefinedAndNotNull(value) && isNumber(value) && value >= 0) {
+ formValue.unit = this.calculateTimeUnit(value);
+ formValue.time = value / this.timeUnitToTimeMap.get(formValue.unit);
+ }
+ this.timeUnitSelectFormGroup.reset(formValue, {emitEvent: false});
+ this.timeUnitSelectFormGroup.get('unit').updateValueAndValidity({onlySelf: true});
+ }
+
+ validate(): ValidationErrors | null {
+ return this.timeUnitSelectFormGroup.valid ? null : {
+ timeUnitSelect: false
+ };
+ }
+
+ private updateModel(value: FormGroupModel) {
+ const time = value.time * this.timeUnitToTimeMap.get(value.unit);
+ this.propagateChange(time);
+ }
+
+ private calculateTimeUnit(value: number): FullTimeUnit {
+ if (value === 0) {
+ return TimeUnitMilli.MILLISECONDS;
+ }
+ const iterators = this.timeToTimeUnitMap[Symbol.iterator]();
+ let iterator = iterators.next();
+ while (!iterator.done) {
+ if (!Number.isInteger(value / iterator.value[0])) {
+ return iterator.value[1];
+ }
+ iterator = iterators.next();
+ }
+ return TimeUnit.HOURS;
+ }
+}
diff --git a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.ts b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.ts
index 7dfa93da5a..2495a16eff 100644
--- a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.ts
+++ b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.ts
@@ -18,7 +18,8 @@ import { Component, forwardRef, Input, OnDestroy } from '@angular/core';
import {
ControlValueAccessor,
FormBuilder,
- FormGroup, NG_VALIDATORS,
+ FormGroup,
+ NG_VALIDATORS,
NG_VALUE_ACCESSOR,
ValidationErrors,
Validator,
@@ -30,11 +31,14 @@ import {
BingingMode,
BingingModeTranslationsMap,
DEFAULT_BINDING,
+ DEFAULT_EDRX_CYCLE,
DEFAULT_FW_UPDATE_RESOURCE,
DEFAULT_ID_SERVER,
DEFAULT_LIFE_TIME,
DEFAULT_MIN_PERIOD,
DEFAULT_NOTIF_IF_DESIBLED,
+ DEFAULT_PAGING_TRANSMISSION_WINDOW,
+ DEFAULT_PSM_ACTIVITY_TIMER,
DEFAULT_SW_UPDATE_RESOURCE,
getDefaultBootstrapServerSecurityConfig,
getDefaultLwM2MServerSecurityConfig,
@@ -120,9 +124,9 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro
fwUpdateResource: [{value: '', disabled: true}, []],
swUpdateResource: [{value: '', disabled: true}, []],
powerMode: [PowerMode.DRX, Validators.required],
- edrxCycle: [{disabled: true, value: 0}, [Validators.required, Validators.min(0), Validators.pattern('[0-9]*')]],
- psmActivityTimer: [{disabled: true, value: 0}, [Validators.required, Validators.min(0), Validators.pattern('[0-9]*')]],
- pagingTransmissionWindow: [{disabled: true, value: 0}, [Validators.required, Validators.min(0), Validators.pattern('[0-9]*')]],
+ edrxCycle: [{disabled: true, value: 0}, Validators.required],
+ psmActivityTimer: [{disabled: true, value: 0}, Validators.required],
+ pagingTransmissionWindow: [{disabled: true, value: 0}, Validators.required],
compositeOperationsSupport: [false]
})
});
@@ -247,9 +251,10 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro
fwUpdateResource: this.configurationValue.clientLwM2mSettings.fwUpdateResource || '',
swUpdateResource: this.configurationValue.clientLwM2mSettings.swUpdateResource || '',
powerMode: this.configurationValue.clientLwM2mSettings.powerMode || PowerMode.DRX,
- edrxCycle: this.configurationValue.clientLwM2mSettings.edrxCycle || 0,
- pagingTransmissionWindow: this.configurationValue.clientLwM2mSettings.pagingTransmissionWindow || 0,
- psmActivityTimer: this.configurationValue.clientLwM2mSettings.psmActivityTimer || 0,
+ edrxCycle: this.configurationValue.clientLwM2mSettings.edrxCycle || DEFAULT_EDRX_CYCLE,
+ pagingTransmissionWindow:
+ this.configurationValue.clientLwM2mSettings.pagingTransmissionWindow || DEFAULT_PAGING_TRANSMISSION_WINDOW,
+ psmActivityTimer: this.configurationValue.clientLwM2mSettings.psmActivityTimer || DEFAULT_PSM_ACTIVITY_TIMER,
compositeOperationsSupport: this.configurationValue.clientLwM2mSettings.compositeOperationsSupport || false
}
},
diff --git a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-profile-config.models.ts b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-profile-config.models.ts
index 2eb83a49c0..5e31251786 100644
--- a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-profile-config.models.ts
+++ b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-profile-config.models.ts
@@ -42,7 +42,9 @@ export const INSTANCES_ID_VALUE_MAX = 65535;
export const DEFAULT_OTA_UPDATE_PROTOCOL = 'coap://';
export const DEFAULT_FW_UPDATE_RESOURCE = DEFAULT_OTA_UPDATE_PROTOCOL + DEFAULT_LOCAL_HOST_NAME + ':' + DEFAULT_PORT_SERVER_NO_SEC;
export const DEFAULT_SW_UPDATE_RESOURCE = DEFAULT_OTA_UPDATE_PROTOCOL + DEFAULT_LOCAL_HOST_NAME + ':' + DEFAULT_PORT_SERVER_NO_SEC;
-
+export const DEFAULT_PSM_ACTIVITY_TIMER = 10000;
+export const DEFAULT_EDRX_CYCLE = 81000;
+export const DEFAULT_PAGING_TRANSMISSION_WINDOW = 10000;
export enum BingingMode {
U = 'U',
diff --git a/ui-ngx/src/app/modules/home/pages/device/data/coap-device-transport-configuration.component.ts b/ui-ngx/src/app/modules/home/pages/device/data/coap-device-transport-configuration.component.ts
index 046585e00e..38e14e3fe6 100644
--- a/ui-ngx/src/app/modules/home/pages/device/data/coap-device-transport-configuration.component.ts
+++ b/ui-ngx/src/app/modules/home/pages/device/data/coap-device-transport-configuration.component.ts
@@ -71,9 +71,9 @@ export class CoapDeviceTransportConfigurationComponent implements ControlValueAc
ngOnInit() {
this.coapDeviceTransportForm = this.fb.group({
powerMode: [null],
- edrxCycle: [{disabled: true, value: 0}, [Validators.required, Validators.min(0), Validators.pattern('[0-9]*')]],
- psmActivityTimer: [{disabled: true, value: 0}, [Validators.required, Validators.min(0), Validators.pattern('[0-9]*')]],
- pagingTransmissionWindow: [{disabled: true, value: 0}, [Validators.required, Validators.min(0), Validators.pattern('[0-9]*')]]
+ edrxCycle: [{disabled: true, value: 0}, Validators.required],
+ psmActivityTimer: [{disabled: true, value: 0}, Validators.required],
+ pagingTransmissionWindow: [{disabled: true, value: 0}, Validators.required]
});
this.coapDeviceTransportForm.valueChanges.pipe(
takeUntil(this.destroy$)
diff --git a/ui-ngx/src/app/modules/home/pages/device/data/lwm2m-device-transport-configuration.component.ts b/ui-ngx/src/app/modules/home/pages/device/data/lwm2m-device-transport-configuration.component.ts
index 8046dde486..f891166cd0 100644
--- a/ui-ngx/src/app/modules/home/pages/device/data/lwm2m-device-transport-configuration.component.ts
+++ b/ui-ngx/src/app/modules/home/pages/device/data/lwm2m-device-transport-configuration.component.ts
@@ -71,9 +71,9 @@ export class Lwm2mDeviceTransportConfigurationComponent implements ControlValueA
ngOnInit() {
this.lwm2mDeviceTransportConfigurationFormGroup = this.fb.group({
powerMode: [null],
- edrxCycle: [{disabled: true, value: 0}, [Validators.required, Validators.min(0), Validators.pattern('[0-9]*')]],
- psmActivityTimer: [{disabled: true, value: 0}, [Validators.required, Validators.min(0), Validators.pattern('[0-9]*')]],
- pagingTransmissionWindow: [{disabled: true, value: 0}, [Validators.required, Validators.min(0), Validators.pattern('[0-9]*')]]
+ edrxCycle: [{disabled: true, value: 0}, Validators.required],
+ psmActivityTimer: [{disabled: true, value: 0}, Validators.required],
+ pagingTransmissionWindow: [{disabled: true, value: 0}, Validators.required]
});
this.lwm2mDeviceTransportConfigurationFormGroup.valueChanges.pipe(
takeUntil(this.destroy$)
diff --git a/ui-ngx/src/app/shared/models/time/time.models.ts b/ui-ngx/src/app/shared/models/time/time.models.ts
index c9520f09df..561f3a30fc 100644
--- a/ui-ngx/src/app/shared/models/time/time.models.ts
+++ b/ui-ngx/src/app/shared/models/time/time.models.ts
@@ -812,8 +812,15 @@ export enum TimeUnit {
DAYS = 'DAYS'
}
-export const timeUnitTranslationMap = new Map(
+export enum TimeUnitMilli {
+ MILLISECONDS = 'MILLISECONDS'
+}
+
+export type FullTimeUnit = TimeUnit | TimeUnitMilli;
+
+export const timeUnitTranslationMap = new Map(
[
+ [TimeUnitMilli.MILLISECONDS, 'timeunit.milliseconds'],
[TimeUnit.SECONDS, 'timeunit.seconds'],
[TimeUnit.MINUTES, 'timeunit.minutes'],
[TimeUnit.HOURS, 'timeunit.hours'],
diff --git a/ui-ngx/src/assets/locale/locale.constant-cs_CZ.json b/ui-ngx/src/assets/locale/locale.constant-cs_CZ.json
index 7d894d8178..b0fc26abe2 100644
--- a/ui-ngx/src/assets/locale/locale.constant-cs_CZ.json
+++ b/ui-ngx/src/assets/locale/locale.constant-cs_CZ.json
@@ -1229,7 +1229,7 @@
"drx": "Přerušovaný přenos (DRX)",
"edrx": "Rozšířený přerušovaný přenos (eDRX)"
},
- "edrx-cycle": "eDRX cyklus v milisekudnách",
+ "edrx-cycle": "eDRX cyklus",
"edrx-cycle-required": "eDRX cyklus je povinný.",
"edrx-cycle-pattern": "eDRX cyklus musí být kladné číslo.",
"lwm2m": {
diff --git a/ui-ngx/src/assets/locale/locale.constant-en_US.json b/ui-ngx/src/assets/locale/locale.constant-en_US.json
index 3d01ac6dfb..eceef34751 100644
--- a/ui-ngx/src/assets/locale/locale.constant-en_US.json
+++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json
@@ -1232,15 +1232,18 @@
"drx": "Discontinuous Reception (DRX)",
"edrx": "Extended Discontinuous Reception (eDRX)"
},
- "edrx-cycle": "eDRX cycle in milliseconds",
+ "edrx-cycle": "eDRX cycle",
"edrx-cycle-required": "eDRX cycle is required.",
"edrx-cycle-pattern": "eDRX cycle must be a positive integer.",
- "paging-transmission-window": "Paging Transmission Window in milliseconds",
+ "edrx-cycle-min": "Minimum number of eDRX cycle is {{ min }} seconds.",
+ "paging-transmission-window": "Paging Transmission Window",
"paging-transmission-window-required": "Paging transmission window is required.",
"paging-transmission-window-pattern": "Paging transmission window must be a positive integer.",
- "psm-activity-timer": "PSM Activity Timer in milliseconds",
+ "paging-transmission-window-min": "Minimum number ofpPaging transmission window is {{ min }} seconds.",
+ "psm-activity-timer": "PSM Activity Timer",
"psm-activity-timer-required": "PSM activity timer is required.",
"psm-activity-timer-pattern": "PSM activity timer must be a positive integer.",
+ "psm-activity-timer-min": "Minimum number of PSM activity timer is {{ min }} seconds.",
"lwm2m": {
"object-list": "Object list",
"object-list-empty": "No objects selected.",
@@ -2736,6 +2739,7 @@
}
},
"timeunit": {
+ "milliseconds": "Milliseconds",
"seconds": "Seconds",
"minutes": "Minutes",
"hours": "Hours",