Commit 688a272035747cf0a411157eef82d67b906a59ee

Authored by Andrew Shvayka
Committed by GitHub
2 parents 4e5f4851 f4bf9569

Merge pull request #4780 from vvlladd28/refactoring/lwm2m/device-profile

UI: Refactoring LwM2M device profile
Showing 18 changed files with 646 additions and 539 deletions
@@ -21,18 +21,23 @@ import { defaultHttpOptionsFromConfig, RequestConfig } from './http-utils'; @@ -21,18 +21,23 @@ import { defaultHttpOptionsFromConfig, RequestConfig } from './http-utils';
21 import { Observable, of, throwError } from 'rxjs'; 21 import { Observable, of, throwError } from 'rxjs';
22 import { PageData } from '@shared/models/page/page-data'; 22 import { PageData } from '@shared/models/page/page-data';
23 import { DeviceProfile, DeviceProfileInfo, DeviceTransportType } from '@shared/models/device.models'; 23 import { DeviceProfile, DeviceProfileInfo, DeviceTransportType } from '@shared/models/device.models';
24 -import { isDefinedAndNotNull, isEmptyStr } from '@core/utils';  
25 -import { ObjectLwM2M, ServerSecurityConfig } from '@home/components/profile/device/lwm2m/lwm2m-profile-config.models'; 24 +import { deepClone, isDefinedAndNotNull, isEmptyStr } from '@core/utils';
  25 +import {
  26 + ObjectLwM2M,
  27 + securityConfigMode,
  28 + ServerSecurityConfig,
  29 + ServerSecurityConfigInfo
  30 +} from '@home/components/profile/device/lwm2m/lwm2m-profile-config.models';
26 import { SortOrder } from '@shared/models/page/sort-order'; 31 import { SortOrder } from '@shared/models/page/sort-order';
27 import { OtaPackageService } from '@core/http/ota-package.service'; 32 import { OtaPackageService } from '@core/http/ota-package.service';
28 -import { mergeMap, tap } from 'rxjs/operators'; 33 +import { map, mergeMap, tap } from 'rxjs/operators';
29 34
30 @Injectable({ 35 @Injectable({
31 providedIn: 'root' 36 providedIn: 'root'
32 }) 37 })
33 export class DeviceProfileService { 38 export class DeviceProfileService {
34 39
35 - private lwm2mBootstrapSecurityInfoInMemoryCache = new Map<boolean, ServerSecurityConfig>(); 40 + private lwm2mBootstrapSecurityInfoInMemoryCache = new Map<boolean, ServerSecurityConfigInfo>();
36 41
37 constructor( 42 constructor(
38 private http: HttpClient, 43 private http: HttpClient,
@@ -60,12 +65,12 @@ export class DeviceProfileService { @@ -60,12 +65,12 @@ export class DeviceProfileService {
60 return this.http.get<Array<ObjectLwM2M>>(url, defaultHttpOptionsFromConfig(config)); 65 return this.http.get<Array<ObjectLwM2M>>(url, defaultHttpOptionsFromConfig(config));
61 } 66 }
62 67
63 - public getLwm2mBootstrapSecurityInfo(isBootstrapServer: boolean, config?: RequestConfig): Observable<ServerSecurityConfig> { 68 + public getLwm2mBootstrapSecurityInfo(isBootstrapServer: boolean, config?: RequestConfig): Observable<ServerSecurityConfigInfo> {
64 const securityConfig = this.lwm2mBootstrapSecurityInfoInMemoryCache.get(isBootstrapServer); 69 const securityConfig = this.lwm2mBootstrapSecurityInfoInMemoryCache.get(isBootstrapServer);
65 if (securityConfig) { 70 if (securityConfig) {
66 return of(securityConfig); 71 return of(securityConfig);
67 } else { 72 } else {
68 - return this.http.get<ServerSecurityConfig>( 73 + return this.http.get<ServerSecurityConfigInfo>(
69 `/api/lwm2m/deviceProfile/bootstrap/${isBootstrapServer}`, 74 `/api/lwm2m/deviceProfile/bootstrap/${isBootstrapServer}`,
70 defaultHttpOptionsFromConfig(config) 75 defaultHttpOptionsFromConfig(config)
71 ).pipe( 76 ).pipe(
@@ -74,6 +79,31 @@ export class DeviceProfileService { @@ -74,6 +79,31 @@ export class DeviceProfileService {
74 } 79 }
75 } 80 }
76 81
  82 + public getLwm2mBootstrapSecurityInfoBySecurityType(isBootstrapServer: boolean, securityMode = securityConfigMode.NO_SEC,
  83 + config?: RequestConfig): Observable<ServerSecurityConfig> {
  84 + return this.getLwm2mBootstrapSecurityInfo(isBootstrapServer, config).pipe(
  85 + map(securityConfig => {
  86 + const serverSecurityConfigInfo = deepClone(securityConfig);
  87 + switch (securityMode) {
  88 + case securityConfigMode.PSK:
  89 + serverSecurityConfigInfo.port = serverSecurityConfigInfo.securityPort;
  90 + serverSecurityConfigInfo.host = serverSecurityConfigInfo.securityHost;
  91 + serverSecurityConfigInfo.serverPublicKey = '';
  92 + break;
  93 + case securityConfigMode.RPK:
  94 + case securityConfigMode.X509:
  95 + serverSecurityConfigInfo.port = serverSecurityConfigInfo.securityPort;
  96 + serverSecurityConfigInfo.host = serverSecurityConfigInfo.securityHost;
  97 + break;
  98 + case securityConfigMode.NO_SEC:
  99 + serverSecurityConfigInfo.serverPublicKey = '';
  100 + break;
  101 + }
  102 + return serverSecurityConfigInfo;
  103 + })
  104 + );
  105 + }
  106 +
77 public getLwm2mObjectsPage(pageLink: PageLink, config?: RequestConfig): Observable<Array<ObjectLwM2M>> { 107 public getLwm2mObjectsPage(pageLink: PageLink, config?: RequestConfig): Observable<Array<ObjectLwM2M>> {
78 return this.http.get<Array<ObjectLwM2M>>( 108 return this.http.get<Array<ObjectLwM2M>>(
79 `/api/resource/lwm2m/page${pageLink.toQuery()}`, 109 `/api/resource/lwm2m/page${pageLink.toQuery()}`,
@@ -89,9 +89,10 @@ export class DeviceProfileTransportConfigurationComponent implements ControlValu @@ -89,9 +89,10 @@ export class DeviceProfileTransportConfigurationComponent implements ControlValu
89 if (configuration) { 89 if (configuration) {
90 delete configuration.type; 90 delete configuration.type;
91 } 91 }
  92 + this.deviceProfileTransportConfigurationFormGroup.patchValue({configuration}, {emitEvent: false});
92 setTimeout(() => { 93 setTimeout(() => {
93 - this.deviceProfileTransportConfigurationFormGroup.patchValue({configuration}, {emitEvent: false});  
94 - }); 94 + this.deviceProfileTransportConfigurationFormGroup.updateValueAndValidity();
  95 + }, 0);
95 } 96 }
96 97
97 private updateModel() { 98 private updateModel() {
@@ -15,12 +15,11 @@ @@ -15,12 +15,11 @@
15 limitations under the License. 15 limitations under the License.
16 16
17 --> 17 -->
18 -<form [formGroup]="attributeLwm2mDialogFormGroup" (ngSubmit)="save()" style="width: 500px;"> 18 +<form [formGroup]="attributeFormGroup" (ngSubmit)="save()" style="min-width: 500px;">
19 <mat-toolbar color="primary"> 19 <mat-toolbar color="primary">
20 - <div fxFlex fxLayout="column" fxLayoutAlign="start">  
21 - <h2>{{ (readonly ? 'device-profile.lwm2m.attribute-lwm2m-toolbar-view' :  
22 - 'device-profile.lwm2m.attribute-lwm2m-toolbar-edit') | translate }}</h2>  
23 - </div> 20 + <h2>
  21 + {{ (readonly ? 'device-profile.lwm2m.view-attributes' : 'device-profile.lwm2m.edit-attributes') | translate : {name: name} }}
  22 + </h2>
24 <span fxFlex></span> 23 <span fxFlex></span>
25 <button mat-icon-button 24 <button mat-icon-button
26 (click)="cancel()" 25 (click)="cancel()"
@@ -32,8 +31,8 @@ @@ -32,8 +31,8 @@
32 </mat-progress-bar> 31 </mat-progress-bar>
33 <div mat-dialog-content> 32 <div mat-dialog-content>
34 <tb-lwm2m-attributes-key-list 33 <tb-lwm2m-attributes-key-list
35 - formControlName="keyFilters"  
36 - titleText="{{data.destName}}"> 34 + [isResource]="isResource"
  35 + formControlName="attributes">
37 </tb-lwm2m-attributes-key-list> 36 </tb-lwm2m-attributes-key-list>
38 </div> 37 </div>
39 <div mat-dialog-actions fxLayoutAlign="end center"> 38 <div mat-dialog-actions fxLayoutAlign="end center">
@@ -46,7 +45,7 @@ @@ -46,7 +45,7 @@
46 <button mat-raised-button color="primary" 45 <button mat-raised-button color="primary"
47 *ngIf="!readonly" 46 *ngIf="!readonly"
48 type="submit" 47 type="submit"
49 - [disabled]="(isLoading$ | async) || attributeLwm2mDialogFormGroup.invalid || !attributeLwm2mDialogFormGroup.dirty"> 48 + [disabled]="(isLoading$ | async) || attributeFormGroup.invalid || !attributeFormGroup.dirty">
50 {{ 'action.save' | translate }} 49 {{ 'action.save' | translate }}
51 </button> 50 </button>
52 </div> 51 </div>
@@ -14,7 +14,7 @@ @@ -14,7 +14,7 @@
14 /// limitations under the License. 14 /// limitations under the License.
15 /// 15 ///
16 16
17 -import { Component, Inject, OnInit, SkipSelf } from '@angular/core'; 17 +import { Component, Inject, SkipSelf } from '@angular/core';
18 import { ErrorStateMatcher } from '@angular/material/core'; 18 import { ErrorStateMatcher } from '@angular/material/core';
19 import { DialogComponent } from '@shared/components/dialog.component'; 19 import { DialogComponent } from '@shared/components/dialog.component';
20 import { Store } from '@ngrx/store'; 20 import { Store } from '@ngrx/store';
@@ -22,12 +22,13 @@ import { AppState } from '@core/core.state'; @@ -22,12 +22,13 @@ import { AppState } from '@core/core.state';
22 import { Router } from '@angular/router'; 22 import { Router } from '@angular/router';
23 import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; 23 import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
24 import { FormBuilder, FormControl, FormGroup, FormGroupDirective, NgForm } from '@angular/forms'; 24 import { FormBuilder, FormControl, FormGroup, FormGroupDirective, NgForm } from '@angular/forms';
25 -import { JsonObject } from '@angular/compiler-cli/ngcc/src/packages/entry_point'; 25 +import { AttributesNameValueMap } from '@home/components/profile/device/lwm2m/lwm2m-profile-config.models';
26 26
27 export interface Lwm2mAttributesDialogData { 27 export interface Lwm2mAttributesDialogData {
28 readonly: boolean; 28 readonly: boolean;
29 - attributeLwm2m: JsonObject;  
30 - destName: string; 29 + attributes: AttributesNameValueMap;
  30 + modelName: string;
  31 + isResource: boolean;
31 } 32 }
32 33
33 @Component({ 34 @Component({
@@ -36,42 +37,37 @@ export interface Lwm2mAttributesDialogData { @@ -36,42 +37,37 @@ export interface Lwm2mAttributesDialogData {
36 styleUrls: ['./lwm2m-attributes.component.scss'], 37 styleUrls: ['./lwm2m-attributes.component.scss'],
37 providers: [{provide: ErrorStateMatcher, useExisting: Lwm2mAttributesDialogComponent}], 38 providers: [{provide: ErrorStateMatcher, useExisting: Lwm2mAttributesDialogComponent}],
38 }) 39 })
39 -export class Lwm2mAttributesDialogComponent extends DialogComponent<Lwm2mAttributesDialogComponent, object>  
40 - implements OnInit, ErrorStateMatcher { 40 +export class Lwm2mAttributesDialogComponent
  41 + extends DialogComponent<Lwm2mAttributesDialogComponent, AttributesNameValueMap> implements ErrorStateMatcher {
41 42
42 - readonly = this.data.readonly;  
43 -  
44 - attributeLwm2m = this.data.attributeLwm2m;  
45 -  
46 - submitted = false; 43 + readonly: boolean;
  44 + name: string;
  45 + isResource: boolean;
47 46
48 - dirtyValue = false; 47 + private submitted = false;
49 48
50 - attributeLwm2mDialogFormGroup: FormGroup; 49 + attributeFormGroup: FormGroup;
51 50
52 constructor(protected store: Store<AppState>, 51 constructor(protected store: Store<AppState>,
53 protected router: Router, 52 protected router: Router,
54 - @Inject(MAT_DIALOG_DATA) public data: Lwm2mAttributesDialogData, 53 + @Inject(MAT_DIALOG_DATA) private data: Lwm2mAttributesDialogData,
55 @SkipSelf() private errorStateMatcher: ErrorStateMatcher, 54 @SkipSelf() private errorStateMatcher: ErrorStateMatcher,
56 - public dialogRef: MatDialogRef<Lwm2mAttributesDialogComponent, object>, 55 + public dialogRef: MatDialogRef<Lwm2mAttributesDialogComponent, AttributesNameValueMap>,
57 private fb: FormBuilder) { 56 private fb: FormBuilder) {
58 super(store, router, dialogRef); 57 super(store, router, dialogRef);
59 58
60 - this.attributeLwm2mDialogFormGroup = this.fb.group({  
61 - keyFilters: [{}, []]  
62 - });  
63 - this.attributeLwm2mDialogFormGroup.patchValue({keyFilters: this.attributeLwm2m});  
64 - this.attributeLwm2mDialogFormGroup.get('keyFilters').valueChanges.subscribe((attributes) => {  
65 - this.attributeLwm2m = attributes; 59 + this.readonly = data.readonly;
  60 + this.name = data.modelName;
  61 + this.isResource = data.isResource;
  62 +
  63 + this.attributeFormGroup = this.fb.group({
  64 + attributes: [data.attributes]
66 }); 65 });
67 if (this.readonly) { 66 if (this.readonly) {
68 - this.attributeLwm2mDialogFormGroup.disable({emitEvent: false}); 67 + this.attributeFormGroup.disable({emitEvent: false});
69 } 68 }
70 } 69 }
71 70
72 - ngOnInit(): void {  
73 - }  
74 -  
75 isErrorState(control: FormControl | null, form: FormGroupDirective | NgForm | null): boolean { 71 isErrorState(control: FormControl | null, form: FormGroupDirective | NgForm | null): boolean {
76 const originalErrorState = this.errorStateMatcher.isErrorState(control, form); 72 const originalErrorState = this.errorStateMatcher.isErrorState(control, form);
77 const customErrorState = !!(control && control.invalid && this.submitted); 73 const customErrorState = !!(control && control.invalid && this.submitted);
@@ -80,7 +76,7 @@ export class Lwm2mAttributesDialogComponent extends DialogComponent<Lwm2mAttribu @@ -80,7 +76,7 @@ export class Lwm2mAttributesDialogComponent extends DialogComponent<Lwm2mAttribu
80 76
81 save(): void { 77 save(): void {
82 this.submitted = true; 78 this.submitted = true;
83 - this.dialogRef.close(this.attributeLwm2m); 79 + this.dialogRef.close(this.attributeFormGroup.get('attributes').value);
84 } 80 }
85 81
86 cancel(): void { 82 cancel(): void {
@@ -15,69 +15,60 @@ @@ -15,69 +15,60 @@
15 limitations under the License. 15 limitations under the License.
16 16
17 --> 17 -->
18 -<section fxLayout="column" class="tb-kv-map" [formGroup]="kvListFormGroup">  
19 - <div>  
20 - <mat-label translate class="tb-title no-padding">device-profile.lwm2m.attribute-lwm2m-destination</mat-label>  
21 - <mat-label class="tb-editor-area-title-panel">{{ titleText }}</mat-label>  
22 - </div> 18 +<section fxLayout="column" class="name-value-map" [formGroup]="attributesValueFormGroup">
23 <div fxLayout="row" fxLayoutGap="8px" style="max-height: 40px; margin-top: 8px;"> 19 <div fxLayout="row" fxLayoutGap="8px" style="max-height: 40px; margin-top: 8px;">
24 - <mat-label fxFlex class="tb-title no-padding" translate>device-profile.lwm2m.attribute-lwm2m-name</mat-label>  
25 - <mat-label fxFlex class="tb-title no-padding" translate>device-profile.lwm2m.attribute-lwm2m-value</mat-label>  
26 - <div [fxShow]="!disabled" style="width: 40px;"></div> 20 + <label fxFlex="40" class="tb-title no-padding" style="min-width: 230px;" translate>device-profile.lwm2m.attribute-name</label>
  21 + <label fxFlex="60" class="tb-title no-padding" translate>device-profile.lwm2m.attribute-value</label>
  22 + <span [fxShow]="!disabled" style="width: 40px;"></span>
27 </div> 23 </div>
28 - <div fxLayout="column" formArrayName="keyVals"  
29 - *ngFor="let keyValControl of keyValsFormArray().controls; let $index = index">  
30 - <div fxLayout="row" fxLayoutAlign="start center" fxLayoutGap="8px">  
31 - <mat-form-field class="mat-block" style="max-heights: 400px"> 24 + <div fxLayout="column" class="map-list"
  25 + *ngFor="let nameValueControl of attributesValueFormArray().controls; let $index = index"
  26 + [formGroup]="nameValueControl">
  27 + <div fxLayout="row" fxLayoutAlign="start center" fxLayoutGap="8px">
  28 + <mat-form-field fxFlex="40" floatLabel="always" hideRequiredMarker>
32 <mat-label></mat-label> 29 <mat-label></mat-label>
33 - <mat-select [formControl]="keyValControl.get('key')">  
34 - <mat-option *ngFor="let attributeLwm2m of attrKeys"  
35 - [value]="attributeLwm2m">  
36 - {{ attributeLwm2mMap.get(attrKey[attributeLwm2m]) }} 30 + <mat-select formControlName="name" required>
  31 + <mat-option *ngFor="let attributeName of attributeNames" [value]="attributeName"
  32 + [disabled]="isDisabledAttributeName(attributeName, $index)">
  33 + {{ attributeNameTranslationMap.get(attributeName) | translate }}
37 </mat-option> 34 </mat-option>
38 </mat-select> 35 </mat-select>
  36 + <mat-error *ngIf="nameValueControl.get('name').hasError('required')">
  37 + {{ 'device-profile.lwm2m.attribute-name-required' | translate }}
  38 + </mat-error>
39 </mat-form-field> 39 </mat-form-field>
40 - <mat-form-field fxFlex floatLabel="always" hideRequiredMarker class="mat-block"  
41 - style="max-height: 40px;"> 40 + <mat-form-field fxFlex="60" floatLabel="always" hideRequiredMarker>
42 <mat-label></mat-label> 41 <mat-label></mat-label>
43 - <input [formControl]="keyValControl.get('value')" matInput  
44 - placeholder="{{ ('key-val.value') | translate }}"/> 42 + <input formControlName="value" matInput required type="number"
  43 + placeholder="{{ 'key-val.value' | translate }}">
  44 + <mat-error fxLayout="row" *ngIf="nameValueControl.get('value').hasError('required')">
  45 + {{ 'device-profile.lwm2m.attribute-value-required' | translate }}
  46 + </mat-error>
  47 + <mat-error fxLayout="row" *ngIf="nameValueControl.get('value').hasError('min') ||
  48 + nameValueControl.get('value').hasError('pattern')">
  49 + {{ 'device-profile.lwm2m.attribute-value-pattern' | translate }}
  50 + </mat-error>
45 </mat-form-field> 51 </mat-form-field>
46 - <button mat-button mat-icon-button color="primary"  
47 - [fxShow]="!disabled" 52 + <button *ngIf="!disabled"
  53 + mat-icon-button color="primary" style="min-width: 40px;"
48 type="button" 54 type="button"
49 (click)="removeKeyVal($index)" 55 (click)="removeKeyVal($index)"
50 - [disabled]="isLoading$ | async"  
51 - matTooltip="{{ 'device-profile.lwm2m.attribute-lwm2m-remove-tip' | translate }}" 56 + matTooltip="{{ 'device-profile.lwm2m.remove-attribute' | translate }}"
52 matTooltipPosition="above"> 57 matTooltipPosition="above">
53 <mat-icon>close</mat-icon> 58 <mat-icon>close</mat-icon>
54 </button> 59 </button>
55 </div> 60 </div>
56 - <mat-error *ngIf="keyValControl.get('key').hasError('required')" style="font-size: smaller">  
57 - {{ 'device-profile.lwm2m.key-name' | translate }}  
58 - <strong>{{ 'device-profile.lwm2m.required' | translate }}</strong>  
59 - </mat-error>  
60 - <mat-error fxLayout="row" *ngIf="keyValControl.get('key').hasError('validAttributeKey')"  
61 - style="font-size: smaller">  
62 - {{ 'device-profile.lwm2m.valid-attribute-lwm2m-key' | translate: {attrEnums: attrKeys} }}  
63 - </mat-error>  
64 - <mat-error fxLayout="row" *ngIf="keyValControl.get('value').hasError('validAttributeValue')"  
65 - style="font-size: smaller">  
66 - {{ 'device-profile.lwm2m.valid-attribute-lwm2m-value' | translate: {attrEnums: attrKeys} }}  
67 - </mat-error>  
68 </div> 61 </div>
69 - <span [fxShow]="!keyValsFormArray().length"  
70 - fxLayoutAlign="center center" [ngClass]="{'disabled': disabled}"  
71 - class="no-data-found" translate>{{noDataText ? noDataText : 'device-profile.lwm2m.no-data'}}</span>  
72 - <div style="margin-top: 8px;">  
73 - <button mat-button mat-raised-button color="primary"  
74 - [fxShow]="!disabled" 62 + <div [fxShow]="!attributesValueFormArray().length"
  63 + fxLayoutAlign="center center"
  64 + class="map-list" translate>device-profile.lwm2m.no-attributes-set</div>
  65 + <div style="margin-top: 9px;" *ngIf="!disabled && isAddEnabled">
  66 + <button mat-stroked-button color="primary"
75 [disabled]="isLoading$ | async" 67 [disabled]="isLoading$ | async"
76 - (click)="addKeyVal()"  
77 type="button" 68 type="button"
78 - matTooltip="{{ 'device-profile.lwm2m.attribute-lwm2m-add-tip' | translate }}"  
79 - matTooltipPosition="above">  
80 - {{ 'action.add' | translate }} 69 + (click)="addKeyVal()">
  70 + <mat-icon class="button-icon">add_circle_outline</mat-icon>
  71 + {{ 'device-profile.lwm2m.add-attribute' | translate }}
81 </button> 72 </button>
82 </div> 73 </div>
83 </section> 74 </section>
  1 +/**
  2 + * Copyright © 2016-2021 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +:host {
  17 + .name-value-map {
  18 + span.no-data-found {
  19 + position: relative;
  20 + display: flex;
  21 + height: 40px;
  22 +
  23 + &.disabled {
  24 + color: rgba(0, 0, 0, .38);
  25 + }
  26 + }
  27 +
  28 + .map-list{
  29 + height: 45px;
  30 + }
  31 + }
  32 +}
  33 +
  34 +:host ::ng-deep {
  35 + .mat-form-field-wrapper {
  36 + padding-bottom: 0;
  37 + }
  38 + .mat-form-field-infix {
  39 + border-top: 0;
  40 + }
  41 + .mat-form-field-underline {
  42 + bottom: 0;
  43 + }
  44 +
  45 + .button-icon{
  46 + font-size: 20px;
  47 + width: 20px;
  48 + height: 20px;
  49 + }
  50 +
  51 + .map-list {
  52 + mat-form-field {
  53 + .mat-form-field-wrapper {
  54 + padding-bottom: 0;
  55 + .mat-form-field-infix {
  56 + border-top-width: 0.2em;
  57 + width: auto;
  58 + min-width: auto;
  59 + }
  60 + .mat-form-field-underline {
  61 + bottom: 0;
  62 + }
  63 + .mat-form-field-subscript-wrapper{
  64 + margin-top: 1.8em;
  65 + }
  66 + }
  67 + }
  68 + }
  69 +}
@@ -14,35 +14,36 @@ @@ -14,35 +14,36 @@
14 /// limitations under the License. 14 /// limitations under the License.
15 /// 15 ///
16 16
17 -import { Component, forwardRef, Input, OnInit } from '@angular/core'; 17 +import { Component, forwardRef, Input, OnDestroy } from '@angular/core';
18 import { 18 import {
19 AbstractControl, 19 AbstractControl,
20 ControlValueAccessor, 20 ControlValueAccessor,
21 FormArray, 21 FormArray,
22 FormBuilder, 22 FormBuilder,
23 - FormControl,  
24 FormGroup, 23 FormGroup,
25 NG_VALIDATORS, 24 NG_VALIDATORS,
26 NG_VALUE_ACCESSOR, 25 NG_VALUE_ACCESSOR,
27 Validator, 26 Validator,
28 Validators 27 Validators
29 } from '@angular/forms'; 28 } from '@angular/forms';
30 -import { Subscription } from 'rxjs'; 29 +import { Subject, Subscription } from 'rxjs';
31 import { 30 import {
32 - ATTRIBUTE_KEYS,  
33 - ATTRIBUTE_LWM2M_ENUM,  
34 - ATTRIBUTE_LWM2M_MAP 31 + AttributeName,
  32 + AttributeNameTranslationMap,
  33 + AttributesNameValue,
  34 + AttributesNameValueMap,
  35 + valueValidatorByAttributeName
35 } from './lwm2m-profile-config.models'; 36 } from './lwm2m-profile-config.models';
36 -import { isDefinedAndNotNull, isEmpty, isEmptyStr, isUndefinedOrNull } from '@core/utils'; 37 +import { isUndefinedOrNull } from '@core/utils';
37 import { Store } from '@ngrx/store'; 38 import { Store } from '@ngrx/store';
38 import { AppState } from '@core/core.state'; 39 import { AppState } from '@core/core.state';
39 import { PageComponent } from '@shared/components/page.component'; 40 import { PageComponent } from '@shared/components/page.component';
40 - 41 +import { takeUntil } from 'rxjs/operators';
41 42
42 @Component({ 43 @Component({
43 selector: 'tb-lwm2m-attributes-key-list', 44 selector: 'tb-lwm2m-attributes-key-list',
44 templateUrl: './lwm2m-attributes-key-list.component.html', 45 templateUrl: './lwm2m-attributes-key-list.component.html',
45 - styleUrls: ['./lwm2m-attributes.component.scss'], 46 + styleUrls: ['./lwm2m-attributes-key-list.component.scss'],
46 providers: [ 47 providers: [
47 { 48 {
48 provide: NG_VALUE_ACCESSOR, 49 provide: NG_VALUE_ACCESSOR,
@@ -56,39 +57,46 @@ import { PageComponent } from '@shared/components/page.component'; @@ -56,39 +57,46 @@ import { PageComponent } from '@shared/components/page.component';
56 } 57 }
57 ] 58 ]
58 }) 59 })
59 -export class Lwm2mAttributesKeyListComponent extends PageComponent implements ControlValueAccessor, OnInit, Validator {  
60 -  
61 - attrKeys = ATTRIBUTE_KEYS;  
62 -  
63 - attrKey = ATTRIBUTE_LWM2M_ENUM; 60 +export class Lwm2mAttributesKeyListComponent extends PageComponent implements ControlValueAccessor, OnDestroy, OnDestroy, Validator {
64 61
65 - attributeLwm2mMap = ATTRIBUTE_LWM2M_MAP; 62 + attributeNames;
  63 + attributeNameTranslationMap = AttributeNameTranslationMap;
66 64
67 @Input() disabled: boolean; 65 @Input() disabled: boolean;
68 66
69 - @Input() titleText: string; 67 + @Input()
  68 + isResource = false;
70 69
71 - @Input() noDataText: string;  
72 -  
73 - kvListFormGroup: FormGroup; 70 + attributesValueFormGroup: FormGroup;
74 71
75 private propagateChange = null; 72 private propagateChange = null;
76 -  
77 - private valueChangeSubscription: Subscription = null; 73 + private valueChange$: Subscription = null;
  74 + private destroy$ = new Subject();
  75 + private usedAttributesName: AttributeName[] = [];
78 76
79 constructor(protected store: Store<AppState>, 77 constructor(protected store: Store<AppState>,
80 private fb: FormBuilder) { 78 private fb: FormBuilder) {
81 super(store); 79 super(store);
  80 + this.attributesValueFormGroup = this.fb.group({
  81 + attributesValue: this.fb.array([])
  82 + });
82 } 83 }
83 84
84 - ngOnInit(): void {  
85 - this.kvListFormGroup = this.fb.group({});  
86 - this.kvListFormGroup.addControl('keyVals',  
87 - this.fb.array([])); 85 + ngOnInit() {
  86 + if (this.isResource) {
  87 + this.attributeNames = Object.values(AttributeName);
  88 + } else {
  89 + this.attributeNames = Object.values(AttributeName)
  90 + .filter(item => ![AttributeName.lt, AttributeName.gt, AttributeName.st].includes(item));
  91 + }
88 } 92 }
89 93
90 - keyValsFormArray(): FormArray {  
91 - return this.kvListFormGroup.get('keyVals') as FormArray; 94 + ngOnDestroy() {
  95 + if (this.valueChange$) {
  96 + this.valueChange$.unsubscribe();
  97 + }
  98 + this.destroy$.next();
  99 + this.destroy$.complete();
92 } 100 }
93 101
94 registerOnChange(fn: any): void { 102 registerOnChange(fn: any): void {
@@ -101,127 +109,111 @@ export class Lwm2mAttributesKeyListComponent extends PageComponent implements Co @@ -101,127 +109,111 @@ export class Lwm2mAttributesKeyListComponent extends PageComponent implements Co
101 setDisabledState(isDisabled: boolean): void { 109 setDisabledState(isDisabled: boolean): void {
102 this.disabled = isDisabled; 110 this.disabled = isDisabled;
103 if (this.disabled) { 111 if (this.disabled) {
104 - this.kvListFormGroup.disable({emitEvent: false}); 112 + this.attributesValueFormGroup.disable({emitEvent: false});
105 } else { 113 } else {
106 - this.kvListFormGroup.enable({emitEvent: false}); 114 + this.attributesValueFormGroup.enable({emitEvent: false});
107 } 115 }
108 } 116 }
109 117
110 - writeValue(keyValMap: { [key: string]: string }): void {  
111 - if (this.valueChangeSubscription) {  
112 - this.valueChangeSubscription.unsubscribe(); 118 + writeValue(keyValMap: AttributesNameValueMap): void {
  119 + if (this.valueChange$) {
  120 + this.valueChange$.unsubscribe();
113 } 121 }
114 - const keyValsControls: Array<AbstractControl> = []; 122 + const attributesValueControls: Array<AbstractControl> = [];
115 if (keyValMap) { 123 if (keyValMap) {
116 - for (const property of Object.keys(keyValMap)) {  
117 - if (Object.prototype.hasOwnProperty.call(keyValMap, property)) {  
118 - keyValsControls.push(this.fb.group({  
119 - key: [property, [Validators.required, this.attributeLwm2mKeyValidator]],  
120 - value: [keyValMap[property], this.attributeLwm2mValueValidator(property)]  
121 - }));  
122 - }  
123 - } 124 + (Object.keys(keyValMap) as AttributeName[]).forEach(name => {
  125 + attributesValueControls.push(this.createdFormGroup({name, value: keyValMap[name]}));
  126 + });
124 } 127 }
125 - this.kvListFormGroup.setControl('keyVals', this.fb.array(keyValsControls));  
126 - this.valueChangeSubscription = this.kvListFormGroup.valueChanges.subscribe(() => {  
127 - // this.updateValidate();  
128 - this.updateModel();  
129 - }); 128 + this.attributesValueFormGroup.setControl('attributesValue', this.fb.array(attributesValueControls));
130 if (this.disabled) { 129 if (this.disabled) {
131 - this.kvListFormGroup.disable({emitEvent: false}); 130 + this.attributesValueFormGroup.disable({emitEvent: false});
132 } else { 131 } else {
133 - this.kvListFormGroup.enable({emitEvent: false}); 132 + this.attributesValueFormGroup.enable({emitEvent: false});
134 } 133 }
  134 + this.valueChange$ = this.attributesValueFormGroup.valueChanges.subscribe(() => {
  135 + this.updateModel();
  136 + });
  137 + this.updateUsedAttributesName();
  138 + }
  139 +
  140 + attributesValueFormArray(): FormArray {
  141 + return this.attributesValueFormGroup.get('attributesValue') as FormArray;
135 } 142 }
136 143
137 public removeKeyVal(index: number) { 144 public removeKeyVal(index: number) {
138 - (this.kvListFormGroup.get('keyVals') as FormArray).removeAt(index); 145 + this.attributesValueFormArray().removeAt(index);
139 } 146 }
140 147
141 public addKeyVal() { 148 public addKeyVal() {
142 - const keyValsFormArray = this.kvListFormGroup.get('keyVals') as FormArray;  
143 - keyValsFormArray.push(this.fb.group({  
144 - key: ['', [Validators.required, this.attributeLwm2mKeyValidator]],  
145 - value: ['', []]  
146 - }));  
147 - }  
148 -  
149 - public validate(c?: FormControl) {  
150 - const kvList: { key: string; value: string }[] = this.kvListFormGroup.get('keyVals').value;  
151 - let valid = true;  
152 - for (const entry of kvList) {  
153 - if (isUndefinedOrNull(entry.key) || isEmptyStr(entry.key) || !ATTRIBUTE_KEYS.includes(entry.key)) {  
154 - valid = false;  
155 - break;  
156 - }  
157 - if (entry.key !== 'ver' && isNaN(Number(entry.value))) {  
158 - valid = false;  
159 - break;  
160 - } 149 + this.attributesValueFormArray().push(this.createdFormGroup());
  150 + this.attributesValueFormGroup.updateValueAndValidity({emitEvent: false});
  151 + if (this.attributesValueFormGroup.invalid) {
  152 + this.updateModel();
161 } 153 }
162 - return (valid) ? null : {  
163 - keyVals: {  
164 - valid: false,  
165 - },  
166 - };  
167 } 154 }
168 155
169 - private updateValidate() {  
170 - const kvList = this.kvListFormGroup.get('keyVals') as FormArray;  
171 - kvList.controls.forEach(fg => {  
172 - if (fg.get('key').value === 'ver') {  
173 - fg.get('value').setValidators(null);  
174 - fg.get('value').setErrors(null);  
175 - }  
176 - else {  
177 - fg.get('value').setValidators(this.attributeLwm2mValueNumberValidator);  
178 - fg.get('value').setErrors(this.attributeLwm2mValueNumberValidator(fg.get('value')));  
179 - } 156 + private createdFormGroup(value?: AttributesNameValue): FormGroup {
  157 + if (isUndefinedOrNull(value)) {
  158 + value = {
  159 + name: this.getFirstUnusedAttributesName(),
  160 + value: null
  161 + };
  162 + }
  163 + const form = this.fb.group({
  164 + name: [value.name, Validators.required],
  165 + value: [value.value, valueValidatorByAttributeName(value.name)]
180 }); 166 });
  167 + form.get('name').valueChanges.pipe(
  168 + takeUntil(this.destroy$)
  169 + ).subscribe(name => {
  170 + form.get('value').setValidators(valueValidatorByAttributeName(name));
  171 + form.get('value').updateValueAndValidity();
  172 + });
  173 + return form;
  174 + }
  175 +
  176 + public validate() {
  177 + return this.attributesValueFormGroup.valid ? null : {
  178 + attributesValue: {
  179 + valid: false
  180 + }
  181 + };
181 } 182 }
182 183
183 private updateModel() { 184 private updateModel() {
184 - this.updateValidate();  
185 - if (this.validate() === null) {  
186 - const kvList: { key: string; value: string }[] = this.kvListFormGroup.get('keyVals').value;  
187 - const keyValMap: { [key: string]: string | number } = {};  
188 - kvList.forEach((entry) => {  
189 - if (isUndefinedOrNull(entry.value) || entry.key === 'ver' || isEmptyStr(entry.value.toString())) {  
190 - keyValMap[entry.key] = entry.value.toString();  
191 - } else {  
192 - keyValMap[entry.key] = Number(entry.value);  
193 - }  
194 - });  
195 - this.propagateChange(keyValMap);  
196 - }  
197 - else {  
198 - this.propagateChange(null);  
199 - } 185 + const value: AttributesNameValue[] = this.attributesValueFormGroup.get('attributesValue').value;
  186 + const attributesNameValueMap: AttributesNameValueMap = {};
  187 + value.forEach(attribute => {
  188 + attributesNameValueMap[attribute.name] = attribute.value;
  189 + });
  190 + this.updateUsedAttributesName();
  191 + this.propagateChange(attributesNameValueMap);
200 } 192 }
201 193
  194 + public isDisabledAttributeName(type: AttributeName, index: number): boolean {
  195 + const usedIndex = this.usedAttributesName.indexOf(type);
  196 + return usedIndex > -1 && usedIndex !== index;
  197 + }
202 198
203 - private attributeLwm2mKeyValidator = (control: AbstractControl) => {  
204 - const key = control.value as string;  
205 - if (isDefinedAndNotNull(key) && !isEmpty(key)) {  
206 - if (!ATTRIBUTE_KEYS.includes(key)) {  
207 - return {  
208 - validAttributeKey: true  
209 - }; 199 + private getFirstUnusedAttributesName(): AttributeName {
  200 + for (const attributeName of this.attributeNames) {
  201 + if (this.usedAttributesName.indexOf(attributeName) === -1) {
  202 + return attributeName;
210 } 203 }
211 } 204 }
212 return null; 205 return null;
213 } 206 }
214 207
215 - private attributeLwm2mValueNumberValidator = (control: AbstractControl) => {  
216 - if (isNaN(Number(control.value)) || Number(control.value) < 0) {  
217 - return {  
218 - validAttributeValue: true  
219 - };  
220 - }  
221 - return null; 208 + private updateUsedAttributesName() {
  209 + this.usedAttributesName = [];
  210 + const value: AttributesNameValue[] = this.attributesValueFormGroup.get('attributesValue').value;
  211 + value.forEach((attributesValue, index) => {
  212 + this.usedAttributesName[index] = attributesValue.name;
  213 + });
222 } 214 }
223 215
224 - private attributeLwm2mValueValidator = (property: string): object[] => {  
225 - return property === 'ver' ? [] : [this.attributeLwm2mValueNumberValidator]; 216 + get isAddEnabled(): boolean {
  217 + return this.attributesValueFormArray().length !== this.attributeNames.length;
226 } 218 }
227 } 219 }
@@ -15,18 +15,14 @@ @@ -15,18 +15,14 @@
15 limitations under the License. 15 limitations under the License.
16 16
17 --> 17 -->
18 -<div fxLayout="row" [formGroup]="attributeLwm2mFormGroup">  
19 - <div fxFlex fxLayout="column" class="resource-name-lw-end" fxLayoutAlign="center"  
20 - [matTooltip]="isToolTipLabel()" matTooltipPosition="above">  
21 - {{attributeLwm2mToString()}}  
22 - </div> 18 +<div fxLayout="row" [fxHide]="disabled && isEmpty()" fxLayoutAlign="end center" matTooltip="{{ tooltipSetAttributesTelemetry | translate }}" matTooltipPosition="above">
23 <button type="button" 19 <button type="button"
24 [disabled]="isDisableBtn()" 20 [disabled]="isDisableBtn()"
25 mat-button mat-icon-button 21 mat-button mat-icon-button
26 (click)="editAttributesLwm2m($event)" 22 (click)="editAttributesLwm2m($event)"
27 - [matTooltip]="(isIconView() ? 'action.view' : isIconEditAdd() ? 'action.edit' : 'action.add' ) | translate" 23 + matTooltip="{{ tooltipButton | translate }}"
28 matTooltipPosition="above"> 24 matTooltipPosition="above">
29 <mat-icon 25 <mat-icon
30 - class="material-icons">{{isIconView() ? 'visibility' : isIconEditAdd() ? 'edit' : 'add' }}</mat-icon> 26 + class="material-icons">{{ iconButton }}</mat-icon>
31 </button> 27 </button>
32 </div> 28 </div>
@@ -14,48 +14,20 @@ @@ -14,48 +14,20 @@
14 * limitations under the License. 14 * limitations under the License.
15 */ 15 */
16 :host { 16 :host {
17 - .tb-kv-map {  
18 - span.no-data-found {  
19 - position: relative;  
20 - display: flex;  
21 - height: 40px;  
22 -  
23 - &.disabled {  
24 - color: rgba(0, 0, 0, .38);  
25 - }  
26 - } 17 + .resource-name-lw-end{
  18 + white-space: nowrap;
  19 + overflow: hidden;
  20 + text-overflow: ellipsis;
  21 + text-align:end;
  22 + //width: 80px;
  23 + cursor: pointer;
27 } 24 }
28 -}  
29 25
30 -:host ::ng-deep {  
31 - .mat-form-field-wrapper {  
32 - padding-bottom: 0;  
33 - }  
34 - .mat-form-field-infix {  
35 - border-top: 0; 26 + .resource-name-lw{
  27 + white-space: nowrap;
  28 + overflow: hidden;
  29 + text-overflow: ellipsis;
  30 + cursor: pointer;
36 } 31 }
37 - .mat-form-field-underline {  
38 - bottom: 0;  
39 - }  
40 -}  
41 -  
42 -.vertical-padding {  
43 - padding: 0 0 10px 20px;  
44 -}  
45 -  
46 -.resource-name-lw-end{  
47 - white-space: nowrap;  
48 - overflow: hidden;  
49 - text-overflow: ellipsis;  
50 - text-align:end;  
51 - //width: 80px;  
52 - cursor: pointer;  
53 -}  
54 -  
55 -.resource-name-lw{  
56 - white-space: nowrap;  
57 - overflow: hidden;  
58 - text-overflow: ellipsis;  
59 - cursor: pointer;  
60 } 32 }
61 33
@@ -17,11 +17,10 @@ @@ -17,11 +17,10 @@
17 import { Component, EventEmitter, forwardRef, Input, Output } from '@angular/core'; 17 import { Component, EventEmitter, forwardRef, Input, Output } from '@angular/core';
18 import { ControlValueAccessor, FormBuilder, FormGroup, NG_VALUE_ACCESSOR } from '@angular/forms'; 18 import { ControlValueAccessor, FormBuilder, FormGroup, NG_VALUE_ACCESSOR } from '@angular/forms';
19 import { coerceBooleanProperty } from '@angular/cdk/coercion'; 19 import { coerceBooleanProperty } from '@angular/cdk/coercion';
20 -import { deepClone, isDefinedAndNotNull, isEmpty } from '@core/utils'; 20 +import { isEmpty, isUndefinedOrNull } from '@core/utils';
21 import { Lwm2mAttributesDialogComponent, Lwm2mAttributesDialogData } from './lwm2m-attributes-dialog.component'; 21 import { Lwm2mAttributesDialogComponent, Lwm2mAttributesDialogData } from './lwm2m-attributes-dialog.component';
22 import { MatDialog } from '@angular/material/dialog'; 22 import { MatDialog } from '@angular/material/dialog';
23 -import { TranslateService } from '@ngx-translate/core';  
24 -import { ATTRIBUTE_LWM2M_LABEL } from './lwm2m-profile-config.models'; 23 +import { AttributesNameValueMap } from './lwm2m-profile-config.models';
25 24
26 25
27 @Component({ 26 @Component({
@@ -36,22 +35,21 @@ import { ATTRIBUTE_LWM2M_LABEL } from './lwm2m-profile-config.models'; @@ -36,22 +35,21 @@ import { ATTRIBUTE_LWM2M_LABEL } from './lwm2m-profile-config.models';
36 }) 35 })
37 export class Lwm2mAttributesComponent implements ControlValueAccessor { 36 export class Lwm2mAttributesComponent implements ControlValueAccessor {
38 attributeLwm2mFormGroup: FormGroup; 37 attributeLwm2mFormGroup: FormGroup;
39 - attributeLwm2mLabel = ATTRIBUTE_LWM2M_LABEL;  
40 38
41 private requiredValue: boolean; 39 private requiredValue: boolean;
42 40
43 @Input() 41 @Input()
44 - attributeLwm2m: {};  
45 -  
46 - @Input()  
47 isAttributeTelemetry: boolean; 42 isAttributeTelemetry: boolean;
48 43
49 @Input() 44 @Input()
50 - destName: string; 45 + modelName: string;
51 46
52 @Input() 47 @Input()
53 disabled: boolean; 48 disabled: boolean;
54 49
  50 + @Input()
  51 + isResource = false;
  52 +
55 @Output() 53 @Output()
56 updateAttributeLwm2m = new EventEmitter<any>(); 54 updateAttributeLwm2m = new EventEmitter<any>();
57 55
@@ -64,8 +62,7 @@ export class Lwm2mAttributesComponent implements ControlValueAccessor { @@ -64,8 +62,7 @@ export class Lwm2mAttributesComponent implements ControlValueAccessor {
64 } 62 }
65 63
66 constructor(private dialog: MatDialog, 64 constructor(private dialog: MatDialog,
67 - private fb: FormBuilder,  
68 - private translate: TranslateService) {} 65 + private fb: FormBuilder) {}
69 66
70 registerOnChange(fn: any): void { 67 registerOnChange(fn: any): void {
71 this.propagateChange = fn; 68 this.propagateChange = fn;
@@ -85,63 +82,66 @@ export class Lwm2mAttributesComponent implements ControlValueAccessor { @@ -85,63 +82,66 @@ export class Lwm2mAttributesComponent implements ControlValueAccessor {
85 82
86 ngOnInit() { 83 ngOnInit() {
87 this.attributeLwm2mFormGroup = this.fb.group({ 84 this.attributeLwm2mFormGroup = this.fb.group({
88 - attributeLwm2m: [this.attributeLwm2m] 85 + attributes: [{}]
89 }); 86 });
90 } 87 }
91 88
92 - writeValue(value: {} | null): void {}  
93 -  
94 - attributeLwm2mToString = (): string => {  
95 - return this.isIconEditAdd () ? this.attributeLwm2mLabelToString() : this.translate.instant('device-profile.lwm2m.no-data'); 89 + writeValue(value: AttributesNameValueMap | null) {
  90 + this.attributeLwm2mFormGroup.patchValue({attributes: value}, {emitEvent: false});
96 } 91 }
97 92
98 - private attributeLwm2mLabelToString = (): string => {  
99 - let label = JSON.stringify(this.attributeLwm2m);  
100 - label = deepClone(label.replace('{', ''));  
101 - label = deepClone(label.replace('}', ''));  
102 - this.attributeLwm2mLabel.forEach((value: string, key: string) => {  
103 - const dest = '\"' + key + '\"\:';  
104 - label = deepClone(label.replace(dest, value));  
105 - });  
106 - return label; 93 + get attributesValueMap(): AttributesNameValueMap {
  94 + return this.attributeLwm2mFormGroup.get('attributes').value;
107 } 95 }
108 96
109 isDisableBtn(): boolean { 97 isDisableBtn(): boolean {
110 - return this.disabled || this.isAttributeTelemetry ? !(isDefinedAndNotNull(this.attributeLwm2m) &&  
111 - !isEmpty(this.attributeLwm2m) && this.disabled) : this.disabled; 98 + return !this.disabled && this.isAttributeTelemetry;
112 } 99 }
113 100
114 - isIconView(): boolean {  
115 - return this.isAttributeTelemetry || this.disabled; 101 + isEmpty(): boolean {
  102 + const value = this.attributesValueMap;
  103 + return isUndefinedOrNull(value) || isEmpty(value);
116 } 104 }
117 105
118 - isIconEditAdd(): boolean {  
119 - return isDefinedAndNotNull(this.attributeLwm2m) && !isEmpty(this.attributeLwm2m); 106 + get tooltipSetAttributesTelemetry(): string {
  107 + return this.isDisableBtn() ? 'device-profile.lwm2m.edit-attributes-select' : '';
120 } 108 }
121 109
122 - isToolTipLabel(): string {  
123 - return this.disabled ? this.translate.instant('device-profile.lwm2m.attribute-lwm2m-tip') :  
124 - this.isAttributeTelemetry ? this.translate.instant('device-profile.lwm2m.attribute-lwm2m-disable-tip') :  
125 - this.translate.instant('device-profile.lwm2m.attribute-lwm2m-tip'); 110 + get tooltipButton(): string {
  111 + if (this.disabled) {
  112 + return 'device-profile.lwm2m.view-attribute';
  113 + } else if (this.isEmpty()) {
  114 + return 'device-profile.lwm2m.add-attribute';
  115 + }
  116 + return 'device-profile.lwm2m.edit-attribute';
  117 + }
  118 +
  119 + get iconButton(): string {
  120 + if (this.disabled) {
  121 + return 'visibility';
  122 + } else if (this.isEmpty()) {
  123 + return 'add';
  124 + }
  125 + return 'edit';
126 } 126 }
127 127
128 public editAttributesLwm2m = ($event: Event): void => { 128 public editAttributesLwm2m = ($event: Event): void => {
129 if ($event) { 129 if ($event) {
130 $event.stopPropagation(); 130 $event.stopPropagation();
131 } 131 }
132 - this.dialog.open<Lwm2mAttributesDialogComponent, Lwm2mAttributesDialogData, object>(Lwm2mAttributesDialogComponent, { 132 + this.dialog.open<Lwm2mAttributesDialogComponent, Lwm2mAttributesDialogData, AttributesNameValueMap>(Lwm2mAttributesDialogComponent, {
133 disableClose: true, 133 disableClose: true,
134 panelClass: ['tb-dialog', 'tb-fullscreen-dialog'], 134 panelClass: ['tb-dialog', 'tb-fullscreen-dialog'],
135 data: { 135 data: {
136 readonly: this.disabled, 136 readonly: this.disabled,
137 - attributeLwm2m: this.disabled ? this.attributeLwm2m : deepClone(this.attributeLwm2m),  
138 - destName: this.destName 137 + attributes: this.attributesValueMap,
  138 + modelName: this.modelName,
  139 + isResource: this.isResource
139 } 140 }
140 }).afterClosed().subscribe((result) => { 141 }).afterClosed().subscribe((result) => {
141 if (result) { 142 if (result) {
142 - this.attributeLwm2m = result;  
143 - this.attributeLwm2mFormGroup.patchValue({attributeLwm2m: this.attributeLwm2m});  
144 - this.updateAttributeLwm2m.next(this.attributeLwm2m); 143 + this.attributeLwm2mFormGroup.patchValue({attributeLwm2m: result});
  144 + this.updateAttributeLwm2m.next(result);
145 } 145 }
146 }); 146 });
147 } 147 }
@@ -35,19 +35,33 @@ @@ -35,19 +35,33 @@
35 </mat-form-field> 35 </mat-form-field>
36 <mat-form-field fxFlex> 36 <mat-form-field fxFlex>
37 <mat-label>{{ 'device-profile.lwm2m.server-port' | translate }}</mat-label> 37 <mat-label>{{ 'device-profile.lwm2m.server-port' | translate }}</mat-label>
38 - <input matInput type="number" formControlName="port" required min="0"> 38 + <input matInput type="number" formControlName="port" required min="0" max="65535">
39 <mat-error *ngIf="serverFormGroup.get('port').hasError('required')"> 39 <mat-error *ngIf="serverFormGroup.get('port').hasError('required')">
40 {{ 'device-profile.lwm2m.server-port-required' | translate }} 40 {{ 'device-profile.lwm2m.server-port-required' | translate }}
41 </mat-error> 41 </mat-error>
  42 + <mat-error *ngIf="serverFormGroup.get('port').hasError('pattern')">
  43 + {{ 'device-profile.lwm2m.server-port-pattern' | translate }}
  44 + </mat-error>
  45 + <mat-error *ngIf="serverFormGroup.get('port').hasError('min') ||
  46 + serverFormGroup.get('port').hasError('max')">
  47 + {{ 'device-profile.lwm2m.server-port-range' | translate }}
  48 + </mat-error>
42 </mat-form-field> 49 </mat-form-field>
43 </div> 50 </div>
44 <div fxLayout="row" fxLayout.xs="column" fxLayoutGap="8px" fxLayoutGap.xs="0px"> 51 <div fxLayout="row" fxLayout.xs="column" fxLayoutGap="8px" fxLayoutGap.xs="0px">
45 <mat-form-field fxFlex> 52 <mat-form-field fxFlex>
46 <mat-label>{{ 'device-profile.lwm2m.short-id' | translate }}</mat-label> 53 <mat-label>{{ 'device-profile.lwm2m.short-id' | translate }}</mat-label>
47 - <input matInput type="number" formControlName="serverId" required min="0"> 54 + <input matInput type="number" min="1" max="65534" formControlName="serverId" required>
48 <mat-error *ngIf="serverFormGroup.get('serverId').hasError('required')"> 55 <mat-error *ngIf="serverFormGroup.get('serverId').hasError('required')">
49 {{ 'device-profile.lwm2m.short-id-required' | translate }} 56 {{ 'device-profile.lwm2m.short-id-required' | translate }}
50 </mat-error> 57 </mat-error>
  58 + <mat-error *ngIf="serverFormGroup.get('serverId').hasError('pattern')">
  59 + {{ 'device-profile.lwm2m.short-id-pattern' | translate }}
  60 + </mat-error>
  61 + <mat-error *ngIf="serverFormGroup.get('serverId').hasError('min') ||
  62 + serverFormGroup.get('serverId').hasError('max')">
  63 + {{ 'device-profile.lwm2m.short-id-range' | translate }}
  64 + </mat-error>
51 </mat-form-field> 65 </mat-form-field>
52 <mat-form-field fxFlex> 66 <mat-form-field fxFlex>
53 <mat-label>{{ 'device-profile.lwm2m.client-hold-off-time' | translate }}</mat-label> 67 <mat-label>{{ 'device-profile.lwm2m.client-hold-off-time' | translate }}</mat-label>
@@ -57,6 +71,10 @@ @@ -57,6 +71,10 @@
57 <mat-error *ngIf="serverFormGroup.get('clientHoldOffTime').hasError('required')"> 71 <mat-error *ngIf="serverFormGroup.get('clientHoldOffTime').hasError('required')">
58 {{ 'device-profile.lwm2m.client-hold-off-time-required' | translate }} 72 {{ 'device-profile.lwm2m.client-hold-off-time-required' | translate }}
59 </mat-error> 73 </mat-error>
  74 + <mat-error *ngIf="serverFormGroup.get('clientHoldOffTime').hasError('min') ||
  75 + serverFormGroup.get('clientHoldOffTime').hasError('pattern')">
  76 + {{ 'device-profile.lwm2m.client-hold-off-time-pattern' | translate }}
  77 + </mat-error>
60 </mat-form-field> 78 </mat-form-field>
61 <mat-form-field fxFlex> 79 <mat-form-field fxFlex>
62 <mat-label>{{ 'device-profile.lwm2m.account-after-timeout' | translate }}</mat-label> 80 <mat-label>{{ 'device-profile.lwm2m.account-after-timeout' | translate }}</mat-label>
@@ -66,6 +84,10 @@ @@ -66,6 +84,10 @@
66 <mat-error *ngIf="serverFormGroup.get('bootstrapServerAccountTimeout').hasError('required')"> 84 <mat-error *ngIf="serverFormGroup.get('bootstrapServerAccountTimeout').hasError('required')">
67 {{ 'device-profile.lwm2m.account-after-timeout-required' | translate }} 85 {{ 'device-profile.lwm2m.account-after-timeout-required' | translate }}
68 </mat-error> 86 </mat-error>
  87 + <mat-error *ngIf="serverFormGroup.get('bootstrapServerAccountTimeout').hasError('min') ||
  88 + serverFormGroup.get('bootstrapServerAccountTimeout').hasError('pattern')">
  89 + {{ 'device-profile.lwm2m.account-after-timeout-pattern' | translate }}
  90 + </mat-error>
69 </mat-form-field> 91 </mat-form-field>
70 </div> 92 </div>
71 <div *ngIf="serverFormGroup.get('securityMode').value === securityConfigLwM2MType.RPK || 93 <div *ngIf="serverFormGroup.get('securityMode').value === securityConfigLwM2MType.RPK ||
@@ -15,7 +15,16 @@ @@ -15,7 +15,16 @@
15 /// 15 ///
16 16
17 import { Component, forwardRef, Input, OnDestroy, OnInit } from '@angular/core'; 17 import { Component, forwardRef, Input, OnDestroy, OnInit } from '@angular/core';
18 -import { ControlValueAccessor, FormBuilder, FormGroup, NG_VALUE_ACCESSOR, Validators } from '@angular/forms'; 18 +import {
  19 + ControlValueAccessor,
  20 + FormBuilder,
  21 + FormGroup,
  22 + NG_VALIDATORS,
  23 + NG_VALUE_ACCESSOR,
  24 + ValidationErrors,
  25 + Validator,
  26 + Validators
  27 +} from '@angular/forms';
19 import { 28 import {
20 DEFAULT_PORT_BOOTSTRAP_NO_SEC, 29 DEFAULT_PORT_BOOTSTRAP_NO_SEC,
21 DEFAULT_PORT_SERVER_NO_SEC, 30 DEFAULT_PORT_SERVER_NO_SEC,
@@ -27,10 +36,9 @@ import { @@ -27,10 +36,9 @@ import {
27 ServerSecurityConfig 36 ServerSecurityConfig
28 } from './lwm2m-profile-config.models'; 37 } from './lwm2m-profile-config.models';
29 import { DeviceProfileService } from '@core/http/device-profile.service'; 38 import { DeviceProfileService } from '@core/http/device-profile.service';
30 -import { of, Subject } from 'rxjs';  
31 -import { map, mergeMap, takeUntil, tap } from 'rxjs/operators'; 39 +import { Subject } from 'rxjs';
  40 +import { mergeMap, takeUntil, tap } from 'rxjs/operators';
32 import { Observable } from 'rxjs/internal/Observable'; 41 import { Observable } from 'rxjs/internal/Observable';
33 -import { deepClone } from '@core/utils';  
34 42
35 @Component({ 43 @Component({
36 selector: 'tb-profile-lwm2m-device-config-server', 44 selector: 'tb-profile-lwm2m-device-config-server',
@@ -40,16 +48,21 @@ import { deepClone } from '@core/utils'; @@ -40,16 +48,21 @@ import { deepClone } from '@core/utils';
40 provide: NG_VALUE_ACCESSOR, 48 provide: NG_VALUE_ACCESSOR,
41 useExisting: forwardRef(() => Lwm2mDeviceConfigServerComponent), 49 useExisting: forwardRef(() => Lwm2mDeviceConfigServerComponent),
42 multi: true 50 multi: true
43 - } 51 + },
  52 + {
  53 + provide: NG_VALIDATORS,
  54 + useExisting: forwardRef(() => Lwm2mDeviceConfigServerComponent),
  55 + multi: true
  56 + },
44 ] 57 ]
45 }) 58 })
46 59
47 -export class Lwm2mDeviceConfigServerComponent implements OnInit, ControlValueAccessor, OnDestroy { 60 +export class Lwm2mDeviceConfigServerComponent implements OnInit, ControlValueAccessor, Validator, OnDestroy {
48 61
49 private disabled = false; 62 private disabled = false;
50 private destroy$ = new Subject(); 63 private destroy$ = new Subject();
51 64
52 - private securityDefaultConfig: ServerSecurityConfig; 65 + private isDataLoadedIntoCache = false;
53 66
54 serverFormGroup: FormGroup; 67 serverFormGroup: FormGroup;
55 securityConfigLwM2MType = securityConfigMode; 68 securityConfigLwM2MType = securityConfigMode;
@@ -70,12 +83,13 @@ export class Lwm2mDeviceConfigServerComponent implements OnInit, ControlValueAcc @@ -70,12 +83,13 @@ export class Lwm2mDeviceConfigServerComponent implements OnInit, ControlValueAcc
70 ngOnInit(): void { 83 ngOnInit(): void {
71 this.serverFormGroup = this.fb.group({ 84 this.serverFormGroup = this.fb.group({
72 host: ['', Validators.required], 85 host: ['', Validators.required],
73 - port: [this.isBootstrapServer ? DEFAULT_PORT_BOOTSTRAP_NO_SEC : DEFAULT_PORT_SERVER_NO_SEC, [Validators.required, Validators.min(0)]], 86 + port: [this.isBootstrapServer ? DEFAULT_PORT_BOOTSTRAP_NO_SEC : DEFAULT_PORT_SERVER_NO_SEC,
  87 + [Validators.required, Validators.min(1), Validators.max(65535), Validators.pattern('[0-9]*')]],
74 securityMode: [securityConfigMode.NO_SEC], 88 securityMode: [securityConfigMode.NO_SEC],
75 - serverPublicKey: ['', Validators.required],  
76 - clientHoldOffTime: ['', [Validators.required, Validators.min(0)]],  
77 - serverId: ['', [Validators.required, Validators.min(0)]],  
78 - bootstrapServerAccountTimeout: ['', [Validators.required, Validators.min(0)]], 89 + serverPublicKey: [''],
  90 + clientHoldOffTime: ['', [Validators.required, Validators.min(0), Validators.pattern('[0-9]*')]],
  91 + serverId: ['', [Validators.required, Validators.min(1), Validators.max(65534), Validators.pattern('[0-9]*')]],
  92 + bootstrapServerAccountTimeout: ['', [Validators.required, Validators.min(0), Validators.pattern('[0-9]*')]],
79 }); 93 });
80 this.serverFormGroup.get('securityMode').valueChanges.pipe( 94 this.serverFormGroup.get('securityMode').valueChanges.pipe(
81 tap(securityMode => this.updateValidate(securityMode)), 95 tap(securityMode => this.updateValidate(securityMode)),
@@ -101,7 +115,7 @@ export class Lwm2mDeviceConfigServerComponent implements OnInit, ControlValueAcc @@ -101,7 +115,7 @@ export class Lwm2mDeviceConfigServerComponent implements OnInit, ControlValueAcc
101 this.serverFormGroup.patchValue(serverData, {emitEvent: false}); 115 this.serverFormGroup.patchValue(serverData, {emitEvent: false});
102 this.updateValidate(serverData.securityMode); 116 this.updateValidate(serverData.securityMode);
103 } 117 }
104 - if (!this.securityDefaultConfig){ 118 + if (!this.isDataLoadedIntoCache){
105 this.getLwm2mBootstrapSecurityInfo().subscribe(value => { 119 this.getLwm2mBootstrapSecurityInfo().subscribe(value => {
106 if (!serverData) { 120 if (!serverData) {
107 this.serverFormGroup.patchValue(value); 121 this.serverFormGroup.patchValue(value);
@@ -159,43 +173,19 @@ export class Lwm2mDeviceConfigServerComponent implements OnInit, ControlValueAcc @@ -159,43 +173,19 @@ export class Lwm2mDeviceConfigServerComponent implements OnInit, ControlValueAcc
159 173
160 private propagateChangeState = (value: ServerSecurityConfig): void => { 174 private propagateChangeState = (value: ServerSecurityConfig): void => {
161 if (value !== undefined) { 175 if (value !== undefined) {
162 - if (this.serverFormGroup.valid) {  
163 - this.propagateChange(value);  
164 - } else {  
165 - this.propagateChange(null);  
166 - } 176 + this.propagateChange(value);
167 } 177 }
168 } 178 }
169 179
170 private getLwm2mBootstrapSecurityInfo(securityMode = securityConfigMode.NO_SEC): Observable<ServerSecurityConfig> { 180 private getLwm2mBootstrapSecurityInfo(securityMode = securityConfigMode.NO_SEC): Observable<ServerSecurityConfig> {
171 - if (this.securityDefaultConfig) {  
172 - return of(this.processingBootstrapSecurityInfo(this.securityDefaultConfig, securityMode));  
173 - }  
174 - return this.deviceProfileService.getLwm2mBootstrapSecurityInfo(this.isBootstrapServer).pipe(  
175 - map(securityInfo => {  
176 - this.securityDefaultConfig = securityInfo;  
177 - return this.processingBootstrapSecurityInfo(securityInfo, securityMode);  
178 - }) 181 + return this.deviceProfileService.getLwm2mBootstrapSecurityInfoBySecurityType(this.isBootstrapServer, securityMode).pipe(
  182 + tap(() => this.isDataLoadedIntoCache = true)
179 ); 183 );
180 } 184 }
181 185
182 - private processingBootstrapSecurityInfo(securityConfig: ServerSecurityConfig, securityMode: securityConfigMode): ServerSecurityConfig {  
183 - const config = deepClone(securityConfig);  
184 - switch (securityMode) {  
185 - case securityConfigMode.PSK:  
186 - config.port = config.securityPort;  
187 - config.host = config.securityHost;  
188 - config.serverPublicKey = '';  
189 - break;  
190 - case securityConfigMode.RPK:  
191 - case securityConfigMode.X509:  
192 - config.port = config.securityPort;  
193 - config.host = config.securityHost;  
194 - break;  
195 - case securityConfigMode.NO_SEC:  
196 - config.serverPublicKey = '';  
197 - break;  
198 - }  
199 - return config; 186 + validate(): ValidationErrors | null {
  187 + return this.serverFormGroup.valid ? null : {
  188 + serverFormGroup: true
  189 + };
200 } 190 }
201 } 191 }
@@ -34,53 +34,62 @@ @@ -34,53 +34,62 @@
34 </ng-template> 34 </ng-template>
35 </mat-tab> 35 </mat-tab>
36 <mat-tab label="{{ 'device-profile.lwm2m.servers' | translate }}"> 36 <mat-tab label="{{ 'device-profile.lwm2m.servers' | translate }}">
37 - <ng-template matTabContent>  
38 - <section [formGroup]="lwm2mDeviceProfileFormGroup" style="padding: 4px 2px"> 37 + <section [formGroup]="lwm2mDeviceProfileFormGroup">
  38 + <section formGroupName="bootstrap" style="padding: 4px 2px">
39 <mat-accordion multi="true"> 39 <mat-accordion multi="true">
40 <mat-expansion-panel> 40 <mat-expansion-panel>
41 <mat-expansion-panel-header> 41 <mat-expansion-panel-header>
42 <mat-panel-title>{{ 'device-profile.lwm2m.servers' | translate }}</mat-panel-title> 42 <mat-panel-title>{{ 'device-profile.lwm2m.servers' | translate }}</mat-panel-title>
43 </mat-expansion-panel-header> 43 </mat-expansion-panel-header>
44 - <ng-template matExpansionPanelContent> 44 + <ng-template matExpansionPanelContent formGroupName="servers">
45 <div fxLayout="row" fxLayout.xs="column" fxLayoutGap="8px" fxLayoutGap.xs="0px"> 45 <div fxLayout="row" fxLayout.xs="column" fxLayoutGap="8px" fxLayoutGap.xs="0px">
46 <mat-form-field fxFlex> 46 <mat-form-field fxFlex>
47 <mat-label>{{ 'device-profile.lwm2m.short-id' | translate }}</mat-label> 47 <mat-label>{{ 'device-profile.lwm2m.short-id' | translate }}</mat-label>
48 - <input matInput type="number" formControlName="shortId" required>  
49 - <mat-error *ngIf="lwm2mDeviceProfileFormGroup.get('shortId').hasError('required')">  
50 - {{ 'device-profile.lwm2m.short-id' | translate }}  
51 - <strong>{{ 'device-profile.lwm2m.required' | translate }}</strong> 48 + <input matInput type="number" min="1" max="65534" formControlName="shortId" required>
  49 + <mat-error *ngIf="lwm2mDeviceProfileFormGroup.get('bootstrap.servers.shortId').hasError('required')">
  50 + {{ 'device-profile.lwm2m.short-id-required' | translate }}
  51 + </mat-error>
  52 + <mat-error *ngIf="lwm2mDeviceProfileFormGroup.get('bootstrap.servers.shortId').hasError('min') ||
  53 + lwm2mDeviceProfileFormGroup.get('bootstrap.servers.shortId').hasError('max')">
  54 + {{ 'device-profile.lwm2m.short-id-range' | translate }}
  55 + </mat-error>
  56 + <mat-error *ngIf="lwm2mDeviceProfileFormGroup.get('bootstrap.servers.shortId').hasError('pattern')">
  57 + {{ 'device-profile.lwm2m.short-id-pattern' | translate }}
52 </mat-error> 58 </mat-error>
53 </mat-form-field> 59 </mat-form-field>
54 <mat-form-field fxFlex> 60 <mat-form-field fxFlex>
55 <mat-label>{{ 'device-profile.lwm2m.lifetime' | translate }}</mat-label> 61 <mat-label>{{ 'device-profile.lwm2m.lifetime' | translate }}</mat-label>
56 - <input matInput type="number" formControlName="lifetime" required>  
57 - <mat-error  
58 - *ngIf="lwm2mDeviceProfileFormGroup.get('lifetime').hasError('required')">  
59 - {{ 'device-profile.lwm2m.lifetime' | translate }}  
60 - <strong>{{ 'device-profile.lwm2m.required' | translate }}</strong> 62 + <input matInput type="number" min="0" formControlName="lifetime" required>
  63 + <mat-error *ngIf="lwm2mDeviceProfileFormGroup.get('bootstrap.servers.lifetime').hasError('required')">
  64 + {{ 'device-profile.lwm2m.lifetime-required' | translate }}
  65 + </mat-error>
  66 + <mat-error *ngIf="lwm2mDeviceProfileFormGroup.get('bootstrap.servers.lifetime').hasError('pattern') ||
  67 + lwm2mDeviceProfileFormGroup.get('bootstrap.servers.lifetime').hasError('min')">
  68 + {{ 'device-profile.lwm2m.lifetime-pattern' | translate }}
61 </mat-error> 69 </mat-error>
62 </mat-form-field> 70 </mat-form-field>
63 <mat-form-field fxFlex> 71 <mat-form-field fxFlex>
64 <mat-label>{{ 'device-profile.lwm2m.default-min-period' | translate }}</mat-label> 72 <mat-label>{{ 'device-profile.lwm2m.default-min-period' | translate }}</mat-label>
65 - <input matInput type="number" formControlName="defaultMinPeriod" required>  
66 - <mat-error  
67 - *ngIf="lwm2mDeviceProfileFormGroup.get('defaultMinPeriod').hasError('required')">  
68 - {{ 'device-profile.lwm2m.default-min-period' | translate }}  
69 - <strong>{{ 'device-profile.lwm2m.required' | translate }}</strong> 73 + <input matInput type="number" min="0" formControlName="defaultMinPeriod" required>
  74 + <mat-error *ngIf="lwm2mDeviceProfileFormGroup.get('bootstrap.servers.defaultMinPeriod').hasError('required')">
  75 + {{ 'device-profile.lwm2m.default-min-period-required' | translate }}
  76 + </mat-error>
  77 + <mat-error *ngIf="lwm2mDeviceProfileFormGroup.get('bootstrap.servers.defaultMinPeriod').hasError('pattern') ||
  78 + lwm2mDeviceProfileFormGroup.get('bootstrap.servers.defaultMinPeriod').hasError('min')">
  79 + {{ 'device-profile.lwm2m.default-min-period-pattern' | translate }}
70 </mat-error> 80 </mat-error>
71 </mat-form-field> 81 </mat-form-field>
72 </div> 82 </div>
73 <mat-form-field class="mat-block"> 83 <mat-form-field class="mat-block">
74 <mat-label>{{ 'device-profile.lwm2m.binding' | translate }}</mat-label> 84 <mat-label>{{ 'device-profile.lwm2m.binding' | translate }}</mat-label>
75 <mat-select formControlName="binding"> 85 <mat-select formControlName="binding">
76 - <mat-option *ngFor="let bindingMode of bindingModeTypes"  
77 - [value]="bindingMode">  
78 - {{ bindingModeTypeNamesMap.get(bindingModeType[bindingMode]) }} 86 + <mat-option *ngFor="let bindingMode of bindingModeTypes" [value]="bindingMode">
  87 + {{ bindingModeTypeNamesMap.get(bindingMode) | translate }}
79 </mat-option> 88 </mat-option>
80 </mat-select> 89 </mat-select>
81 </mat-form-field> 90 </mat-form-field>
82 <mat-checkbox formControlName="notifIfDisabled" color="primary"> 91 <mat-checkbox formControlName="notifIfDisabled" color="primary">
83 - {{ 'device-profile.lwm2m.notif-if-disabled' | translate }} 92 + {{ 'device-profile.lwm2m.notification-storing' | translate }}
84 </mat-checkbox> 93 </mat-checkbox>
85 </ng-template> 94 </ng-template>
86 </mat-expansion-panel> 95 </mat-expansion-panel>
@@ -90,7 +99,6 @@ @@ -90,7 +99,6 @@
90 </mat-expansion-panel-header> 99 </mat-expansion-panel-header>
91 <ng-template matExpansionPanelContent> 100 <ng-template matExpansionPanelContent>
92 <tb-profile-lwm2m-device-config-server 101 <tb-profile-lwm2m-device-config-server
93 - [required]="required"  
94 formControlName="bootstrapServer" 102 formControlName="bootstrapServer"
95 [isBootstrapServer]="true"> 103 [isBootstrapServer]="true">
96 </tb-profile-lwm2m-device-config-server> 104 </tb-profile-lwm2m-device-config-server>
@@ -102,7 +110,6 @@ @@ -102,7 +110,6 @@
102 </mat-expansion-panel-header> 110 </mat-expansion-panel-header>
103 <ng-template matExpansionPanelContent> 111 <ng-template matExpansionPanelContent>
104 <tb-profile-lwm2m-device-config-server 112 <tb-profile-lwm2m-device-config-server
105 - [required]="required"  
106 formControlName="lwm2mServer" 113 formControlName="lwm2mServer"
107 [isBootstrapServer]="false"> 114 [isBootstrapServer]="false">
108 </tb-profile-lwm2m-device-config-server> 115 </tb-profile-lwm2m-device-config-server>
@@ -110,11 +117,11 @@ @@ -110,11 +117,11 @@
110 </mat-expansion-panel> 117 </mat-expansion-panel>
111 </mat-accordion> 118 </mat-accordion>
112 </section> 119 </section>
113 - </ng-template> 120 + </section>
114 </mat-tab> 121 </mat-tab>
115 <mat-tab label="{{ 'device-profile.lwm2m.others-tab' | translate }}"> 122 <mat-tab label="{{ 'device-profile.lwm2m.others-tab' | translate }}">
116 - <ng-template matTabContent>  
117 - <section [formGroup]="lwm2mDeviceProfileFormGroup"> 123 + <ng-template matTabContent [formGroup]="lwm2mDeviceProfileFormGroup">
  124 + <section formGroupName="clientLwM2mSettings">
118 <fieldset class="fields-group"> 125 <fieldset class="fields-group">
119 <legend class="group-title" translate>device-profile.lwm2m.fw-update</legend> 126 <legend class="group-title" translate>device-profile.lwm2m.fw-update</legend>
120 <mat-form-field class="mat-block" fxFlex> 127 <mat-form-field class="mat-block" fxFlex>
@@ -128,7 +135,7 @@ @@ -128,7 +135,7 @@
128 <mat-form-field class="mat-block" fxFlex *ngIf="isFwUpdateStrategy"> 135 <mat-form-field class="mat-block" fxFlex *ngIf="isFwUpdateStrategy">
129 <mat-label>{{ 'device-profile.lwm2m.fw-update-recourse' | translate }}</mat-label> 136 <mat-label>{{ 'device-profile.lwm2m.fw-update-recourse' | translate }}</mat-label>
130 <input matInput formControlName="fwUpdateRecourse" required> 137 <input matInput formControlName="fwUpdateRecourse" required>
131 - <mat-error *ngIf="lwm2mDeviceProfileFormGroup.get('fwUpdateRecourse').hasError('required')"> 138 + <mat-error *ngIf="lwm2mDeviceProfileFormGroup.get('clientLwM2mSettings.fwUpdateRecourse').hasError('required')">
132 {{ 'device-profile.lwm2m.fw-update-recourse-required' | translate }} 139 {{ 'device-profile.lwm2m.fw-update-recourse-required' | translate }}
133 </mat-error> 140 </mat-error>
134 </mat-form-field> 141 </mat-form-field>
@@ -145,7 +152,7 @@ @@ -145,7 +152,7 @@
145 <mat-form-field class="mat-block" fxFlex *ngIf="isSwUpdateStrategy"> 152 <mat-form-field class="mat-block" fxFlex *ngIf="isSwUpdateStrategy">
146 <mat-label>{{ 'device-profile.lwm2m.sw-update-recourse' | translate }}</mat-label> 153 <mat-label>{{ 'device-profile.lwm2m.sw-update-recourse' | translate }}</mat-label>
147 <input matInput formControlName="swUpdateRecourse" required> 154 <input matInput formControlName="swUpdateRecourse" required>
148 - <mat-error *ngIf="lwm2mDeviceProfileFormGroup.get('swUpdateRecourse').hasError('required')"> 155 + <mat-error *ngIf="lwm2mDeviceProfileFormGroup.get('clientLwM2mSettings.swUpdateRecourse').hasError('required')">
149 {{ 'device-profile.lwm2m.sw-update-recourse-required' | translate }} 156 {{ 'device-profile.lwm2m.sw-update-recourse-required' | translate }}
150 </mat-error> 157 </mat-error>
151 </mat-form-field> 158 </mat-form-field>
@@ -182,6 +189,7 @@ @@ -182,6 +189,7 @@
182 <ng-template matTabContent> 189 <ng-template matTabContent>
183 <section [formGroup]="lwm2mDeviceConfigFormGroup" style="padding: 8px 0"> 190 <section [formGroup]="lwm2mDeviceConfigFormGroup" style="padding: 8px 0">
184 <tb-json-object-edit 191 <tb-json-object-edit
  192 + readonly
185 [required]="required" 193 [required]="required"
186 [sort]="sortFunction" 194 [sort]="sortFunction"
187 label="{{ 'device-profile.transport-type-lwm2m' | translate }}" 195 label="{{ 'device-profile.transport-type-lwm2m' | translate }}"
@@ -20,11 +20,19 @@ import { ControlValueAccessor, FormBuilder, FormGroup, NG_VALUE_ACCESSOR, Valida @@ -20,11 +20,19 @@ import { ControlValueAccessor, FormBuilder, FormGroup, NG_VALUE_ACCESSOR, Valida
20 import { coerceBooleanProperty } from '@angular/cdk/coercion'; 20 import { coerceBooleanProperty } from '@angular/cdk/coercion';
21 import { 21 import {
22 ATTRIBUTE, 22 ATTRIBUTE,
23 - BINDING_MODE,  
24 - BINDING_MODE_NAMES, 23 + BingingMode,
  24 + BingingModeTranslationsMap,
  25 + DEFAULT_BINDING,
25 DEFAULT_FW_UPDATE_RESOURCE, 26 DEFAULT_FW_UPDATE_RESOURCE,
  27 + DEFAULT_ID_SERVER,
  28 + DEFAULT_LIFE_TIME,
  29 + DEFAULT_MIN_PERIOD,
  30 + DEFAULT_NOTIF_IF_DESIBLED,
26 DEFAULT_SW_UPDATE_RESOURCE, 31 DEFAULT_SW_UPDATE_RESOURCE,
27 - getDefaultProfileConfig, 32 + getDefaultBootstrapServerSecurityConfig,
  33 + getDefaultBootstrapServersSecurityConfig, getDefaultLwM2MServerSecurityConfig,
  34 + getDefaultProfileClientLwM2mSettingsConfig,
  35 + getDefaultProfileObserveAttrConfig,
28 Instance, 36 Instance,
29 INSTANCES, 37 INSTANCES,
30 KEY_NAME, 38 KEY_NAME,
@@ -33,7 +41,7 @@ import { @@ -33,7 +41,7 @@ import {
33 ObjectLwM2M, 41 ObjectLwM2M,
34 OBSERVE, 42 OBSERVE,
35 OBSERVE_ATTR_TELEMETRY, 43 OBSERVE_ATTR_TELEMETRY,
36 - RESOURCES, 44 + RESOURCES, ServerSecurityConfig,
37 TELEMETRY 45 TELEMETRY
38 } from './lwm2m-profile-config.models'; 46 } from './lwm2m-profile-config.models';
39 import { DeviceProfileService } from '@core/http/device-profile.service'; 47 import { DeviceProfileService } from '@core/http/device-profile.service';
@@ -61,14 +69,10 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro @@ -61,14 +69,10 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro
61 private disabled = false; 69 private disabled = false;
62 private destroy$ = new Subject(); 70 private destroy$ = new Subject();
63 71
64 - bindingModeType = BINDING_MODE;  
65 - bindingModeTypes = Object.keys(BINDING_MODE);  
66 - bindingModeTypeNamesMap = BINDING_MODE_NAMES; 72 + bindingModeTypes = Object.values(BingingMode);
  73 + bindingModeTypeNamesMap = BingingModeTranslationsMap;
67 lwm2mDeviceProfileFormGroup: FormGroup; 74 lwm2mDeviceProfileFormGroup: FormGroup;
68 lwm2mDeviceConfigFormGroup: FormGroup; 75 lwm2mDeviceConfigFormGroup: FormGroup;
69 - bootstrapServers: string;  
70 - bootstrapServer: string;  
71 - lwm2mServer: string;  
72 sortFunction: (key: string, value: object) => object; 76 sortFunction: (key: string, value: object) => object;
73 isFwUpdateStrategy: boolean; 77 isFwUpdateStrategy: boolean;
74 isSwUpdateStrategy: boolean; 78 isSwUpdateStrategy: boolean;
@@ -90,45 +94,53 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro @@ -90,45 +94,53 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro
90 this.lwm2mDeviceProfileFormGroup = this.fb.group({ 94 this.lwm2mDeviceProfileFormGroup = this.fb.group({
91 objectIds: [null, Validators.required], 95 objectIds: [null, Validators.required],
92 observeAttrTelemetry: [null, Validators.required], 96 observeAttrTelemetry: [null, Validators.required],
93 - shortId: [null, Validators.required],  
94 - lifetime: [null, Validators.required],  
95 - defaultMinPeriod: [null, Validators.required],  
96 - notifIfDisabled: [true, []],  
97 - binding: [],  
98 - bootstrapServer: [null, Validators.required],  
99 - lwm2mServer: [null, Validators.required],  
100 - clientOnlyObserveAfterConnect: [1, []],  
101 - fwUpdateStrategy: [1, []],  
102 - swUpdateStrategy: [1, []],  
103 - fwUpdateRecourse: [{value: '', disabled: true}, []],  
104 - swUpdateRecourse: [{value: '', disabled: true}, []] 97 + bootstrap: this.fb.group({
  98 + servers: this.fb.group({
  99 + binding: [DEFAULT_BINDING],
  100 + shortId: [DEFAULT_ID_SERVER, [Validators.required, Validators.min(1), Validators.max(65534), Validators.pattern('[0-9]*')]],
  101 + lifetime: [DEFAULT_LIFE_TIME, [Validators.required, Validators.min(0), Validators.pattern('[0-9]*')]],
  102 + notifIfDisabled: [DEFAULT_NOTIF_IF_DESIBLED, []],
  103 + defaultMinPeriod: [DEFAULT_MIN_PERIOD, [Validators.required, Validators.min(0), Validators.pattern('[0-9]*')]],
  104 + }),
  105 + bootstrapServer: [null, Validators.required],
  106 + lwm2mServer: [null, Validators.required]
  107 + }),
  108 + clientLwM2mSettings: this.fb.group({
  109 + clientOnlyObserveAfterConnect: [1, []],
  110 + fwUpdateStrategy: [1, []],
  111 + swUpdateStrategy: [1, []],
  112 + fwUpdateRecourse: [{value: '', disabled: true}, []],
  113 + swUpdateRecourse: [{value: '', disabled: true}, []]
  114 + })
105 }); 115 });
106 this.lwm2mDeviceConfigFormGroup = this.fb.group({ 116 this.lwm2mDeviceConfigFormGroup = this.fb.group({
107 configurationJson: [null, Validators.required] 117 configurationJson: [null, Validators.required]
108 }); 118 });
109 - this.lwm2mDeviceProfileFormGroup.get('fwUpdateStrategy').valueChanges.pipe( 119 + this.lwm2mDeviceProfileFormGroup.get('clientLwM2mSettings.fwUpdateStrategy').valueChanges.pipe(
110 takeUntil(this.destroy$) 120 takeUntil(this.destroy$)
111 ).subscribe((fwStrategy) => { 121 ).subscribe((fwStrategy) => {
112 if (fwStrategy === 2) { 122 if (fwStrategy === 2) {
113 - this.lwm2mDeviceProfileFormGroup.get('fwUpdateRecourse').enable({emitEvent: false});  
114 - this.lwm2mDeviceProfileFormGroup.get('fwUpdateRecourse').patchValue(DEFAULT_FW_UPDATE_RESOURCE, {emitEvent: false}); 123 + this.lwm2mDeviceProfileFormGroup.get('clientLwM2mSettings.fwUpdateRecourse').enable({emitEvent: false});
  124 + this.lwm2mDeviceProfileFormGroup.get('clientLwM2mSettings.fwUpdateRecourse')
  125 + .patchValue(DEFAULT_FW_UPDATE_RESOURCE, {emitEvent: false});
115 this.isFwUpdateStrategy = true; 126 this.isFwUpdateStrategy = true;
116 } else { 127 } else {
117 - this.lwm2mDeviceProfileFormGroup.get('fwUpdateRecourse').disable({emitEvent: false}); 128 + this.lwm2mDeviceProfileFormGroup.get('clientLwM2mSettings.fwUpdateRecourse').disable({emitEvent: false});
118 this.isFwUpdateStrategy = false; 129 this.isFwUpdateStrategy = false;
119 } 130 }
120 this.otaUpdateFwStrategyValidate(true); 131 this.otaUpdateFwStrategyValidate(true);
121 }); 132 });
122 - this.lwm2mDeviceProfileFormGroup.get('swUpdateStrategy').valueChanges.pipe( 133 + this.lwm2mDeviceProfileFormGroup.get('clientLwM2mSettings.swUpdateStrategy').valueChanges.pipe(
123 takeUntil(this.destroy$) 134 takeUntil(this.destroy$)
124 ).subscribe((swStrategy) => { 135 ).subscribe((swStrategy) => {
125 if (swStrategy === 2) { 136 if (swStrategy === 2) {
126 - this.lwm2mDeviceProfileFormGroup.get('swUpdateRecourse').enable({emitEvent: false});  
127 - this.lwm2mDeviceProfileFormGroup.get('swUpdateRecourse').patchValue(DEFAULT_SW_UPDATE_RESOURCE, {emitEvent: false}); 137 + this.lwm2mDeviceProfileFormGroup.get('clientLwM2mSettings.swUpdateRecourse').enable({emitEvent: false});
  138 + this.lwm2mDeviceProfileFormGroup.get('clientLwM2mSettings.swUpdateRecourse')
  139 + .patchValue(DEFAULT_SW_UPDATE_RESOURCE, {emitEvent: false});
128 this.isSwUpdateStrategy = true; 140 this.isSwUpdateStrategy = true;
129 } else { 141 } else {
130 this.isSwUpdateStrategy = false; 142 this.isSwUpdateStrategy = false;
131 - this.lwm2mDeviceProfileFormGroup.get('swUpdateRecourse').disable({emitEvent: false}); 143 + this.lwm2mDeviceProfileFormGroup.get('clientLwM2mSettings.swUpdateRecourse').disable({emitEvent: false});
132 } 144 }
133 this.otaUpdateSwStrategyValidate(true); 145 this.otaUpdateSwStrategyValidate(true);
134 }); 146 });
@@ -168,12 +180,12 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro @@ -168,12 +180,12 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro
168 } 180 }
169 } 181 }
170 182
171 - writeValue(value: Lwm2mProfileConfigModels | null): void { 183 + async writeValue(value: Lwm2mProfileConfigModels | null) {
172 if (isDefinedAndNotNull(value)) { 184 if (isDefinedAndNotNull(value)) {
173 - if (Object.keys(value).length !== 0 && (value?.clientLwM2mSettings || value?.observeAttr || value?.bootstrap)) { 185 + if (value?.clientLwM2mSettings || value?.observeAttr || value?.bootstrap) {
174 this.configurationValue = value; 186 this.configurationValue = value;
175 } else { 187 } else {
176 - this.configurationValue = getDefaultProfileConfig(); 188 + this.configurationValue = await this.defaultProfileConfig();
177 } 189 }
178 this.lwm2mDeviceConfigFormGroup.patchValue({ 190 this.lwm2mDeviceConfigFormGroup.patchValue({
179 configurationJson: this.configurationValue 191 configurationJson: this.configurationValue
@@ -182,6 +194,29 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro @@ -182,6 +194,29 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro
182 } 194 }
183 } 195 }
184 196
  197 + private async defaultProfileConfig(): Promise<Lwm2mProfileConfigModels> {
  198 + let bootstrap: ServerSecurityConfig;
  199 + let lwm2m: ServerSecurityConfig;
  200 + try {
  201 + [bootstrap, lwm2m] = await Promise.all([
  202 + this.deviceProfileService.getLwm2mBootstrapSecurityInfoBySecurityType(true).toPromise(),
  203 + this.deviceProfileService.getLwm2mBootstrapSecurityInfoBySecurityType(false).toPromise()
  204 + ]);
  205 + } catch (e) {
  206 + bootstrap = getDefaultBootstrapServerSecurityConfig();
  207 + lwm2m = getDefaultLwM2MServerSecurityConfig();
  208 + }
  209 + return {
  210 + observeAttr: getDefaultProfileObserveAttrConfig(),
  211 + bootstrap: {
  212 + servers: getDefaultBootstrapServersSecurityConfig(),
  213 + bootstrapServer: bootstrap,
  214 + lwm2mServer: lwm2m
  215 + },
  216 + clientLwM2mSettings: getDefaultProfileClientLwM2mSettingsConfig()
  217 + };
  218 + }
  219 +
185 private initWriteValue = (): void => { 220 private initWriteValue = (): void => {
186 const modelValue = {objectIds: [], objectsList: []} as ModelValue; 221 const modelValue = {objectIds: [], objectsList: []} as ModelValue;
187 modelValue.objectIds = this.getObjectsFromJsonAllConfig(); 222 modelValue.objectIds = this.getObjectsFromJsonAllConfig();
@@ -209,18 +244,14 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro @@ -209,18 +244,14 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro
209 this.lwm2mDeviceProfileFormGroup.patchValue({ 244 this.lwm2mDeviceProfileFormGroup.patchValue({
210 objectIds: value, 245 objectIds: value,
211 observeAttrTelemetry: this.getObserveAttrTelemetryObjects(value.objectsList), 246 observeAttrTelemetry: this.getObserveAttrTelemetryObjects(value.objectsList),
212 - shortId: this.configurationValue.bootstrap.servers.shortId,  
213 - lifetime: this.configurationValue.bootstrap.servers.lifetime,  
214 - defaultMinPeriod: this.configurationValue.bootstrap.servers.defaultMinPeriod,  
215 - notifIfDisabled: this.configurationValue.bootstrap.servers.notifIfDisabled,  
216 - binding: this.configurationValue.bootstrap.servers.binding,  
217 - bootstrapServer: this.configurationValue.bootstrap.bootstrapServer,  
218 - lwm2mServer: this.configurationValue.bootstrap.lwm2mServer,  
219 - clientOnlyObserveAfterConnect: this.configurationValue.clientLwM2mSettings.clientOnlyObserveAfterConnect,  
220 - fwUpdateStrategy: this.configurationValue.clientLwM2mSettings.fwUpdateStrategy || 1,  
221 - swUpdateStrategy: this.configurationValue.clientLwM2mSettings.swUpdateStrategy || 1,  
222 - fwUpdateRecourse: fwResource,  
223 - swUpdateRecourse: swResource 247 + bootstrap: this.configurationValue.bootstrap,
  248 + clientLwM2mSettings: {
  249 + clientOnlyObserveAfterConnect: this.configurationValue.clientLwM2mSettings.clientOnlyObserveAfterConnect,
  250 + fwUpdateStrategy: this.configurationValue.clientLwM2mSettings.fwUpdateStrategy || 1,
  251 + swUpdateStrategy: this.configurationValue.clientLwM2mSettings.swUpdateStrategy || 1,
  252 + fwUpdateRecourse: fwResource,
  253 + swUpdateRecourse: swResource
  254 + }
224 }, 255 },
225 {emitEvent: false}); 256 {emitEvent: false});
226 this.configurationValue.clientLwM2mSettings.fwUpdateRecourse = fwResource; 257 this.configurationValue.clientLwM2mSettings.fwUpdateRecourse = fwResource;
@@ -249,21 +280,12 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro @@ -249,21 +280,12 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro
249 private updateDeviceProfileValue(config): void { 280 private updateDeviceProfileValue(config): void {
250 if (this.lwm2mDeviceProfileFormGroup.valid) { 281 if (this.lwm2mDeviceProfileFormGroup.valid) {
251 this.updateObserveAttrTelemetryFromGroupToJson(config.observeAttrTelemetry.clientLwM2M); 282 this.updateObserveAttrTelemetryFromGroupToJson(config.observeAttrTelemetry.clientLwM2M);
252 - this.configurationValue.bootstrap.bootstrapServer = config.bootstrapServer;  
253 - this.configurationValue.bootstrap.lwm2mServer = config.lwm2mServer;  
254 - const bootstrapServers = this.configurationValue.bootstrap.servers;  
255 - bootstrapServers.shortId = config.shortId;  
256 - bootstrapServers.lifetime = config.lifetime;  
257 - bootstrapServers.defaultMinPeriod = config.defaultMinPeriod;  
258 - bootstrapServers.notifIfDisabled = config.notifIfDisabled;  
259 - bootstrapServers.binding = config.binding;  
260 - this.configurationValue.clientLwM2mSettings.clientOnlyObserveAfterConnect = config.clientOnlyObserveAfterConnect;  
261 - this.configurationValue.clientLwM2mSettings.fwUpdateStrategy = config.fwUpdateStrategy;  
262 - this.configurationValue.clientLwM2mSettings.swUpdateStrategy = config.swUpdateStrategy;  
263 - this.configurationValue.clientLwM2mSettings.fwUpdateRecourse = config.fwUpdateRecourse;  
264 - this.configurationValue.clientLwM2mSettings.swUpdateRecourse = config.swUpdateRecourse;  
265 - this.upDateJsonAllConfig();  
266 } 283 }
  284 + this.configurationValue.bootstrap.bootstrapServer = config.bootstrap.bootstrapServer;
  285 + this.configurationValue.bootstrap.lwm2mServer = config.bootstrap.lwm2mServer;
  286 + this.configurationValue.bootstrap.servers = config.bootstrap.servers;
  287 + this.configurationValue.clientLwM2mSettings = config.clientLwM2mSettings;
  288 + this.upDateJsonAllConfig();
267 this.updateModel(); 289 this.updateModel();
268 } 290 }
269 291
@@ -539,20 +561,20 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro @@ -539,20 +561,20 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro
539 561
540 private otaUpdateFwStrategyValidate(updated = false): void { 562 private otaUpdateFwStrategyValidate(updated = false): void {
541 if (this.isFwUpdateStrategy) { 563 if (this.isFwUpdateStrategy) {
542 - this.lwm2mDeviceProfileFormGroup.get('fwUpdateRecourse').setValidators([Validators.required]); 564 + this.lwm2mDeviceProfileFormGroup.get('clientLwM2mSettings.fwUpdateRecourse').setValidators([Validators.required]);
543 } else { 565 } else {
544 - this.lwm2mDeviceProfileFormGroup.get('fwUpdateRecourse').clearValidators(); 566 + this.lwm2mDeviceProfileFormGroup.get('clientLwM2mSettings.fwUpdateRecourse').clearValidators();
545 } 567 }
546 - this.lwm2mDeviceProfileFormGroup.get('fwUpdateRecourse').updateValueAndValidity({emitEvent: updated}); 568 + this.lwm2mDeviceProfileFormGroup.get('clientLwM2mSettings.fwUpdateRecourse').updateValueAndValidity({emitEvent: updated});
547 } 569 }
548 570
549 private otaUpdateSwStrategyValidate(updated = false): void { 571 private otaUpdateSwStrategyValidate(updated = false): void {
550 if (this.isSwUpdateStrategy) { 572 if (this.isSwUpdateStrategy) {
551 - this.lwm2mDeviceProfileFormGroup.get('swUpdateRecourse').setValidators([Validators.required]); 573 + this.lwm2mDeviceProfileFormGroup.get('clientLwM2mSettings.swUpdateRecourse').setValidators([Validators.required]);
552 } else { 574 } else {
553 - this.lwm2mDeviceProfileFormGroup.get('swUpdateRecourse').clearValidators(); 575 + this.lwm2mDeviceProfileFormGroup.get('clientLwM2mSettings.swUpdateRecourse').clearValidators();
554 } 576 }
555 - this.lwm2mDeviceProfileFormGroup.get('swUpdateRecourse').updateValueAndValidity({emitEvent: updated}); 577 + this.lwm2mDeviceProfileFormGroup.get('clientLwM2mSettings.swUpdateRecourse').updateValueAndValidity({emitEvent: updated});
556 } 578 }
557 579
558 } 580 }
@@ -20,27 +20,24 @@ @@ -20,27 +20,24 @@
20 *ngFor="let resourceLwM2M of resourceFormArray.controls; let i = index; trackBy: trackByParams"> 20 *ngFor="let resourceLwM2M of resourceFormArray.controls; let i = index; trackBy: trackByParams">
21 <div class="vertical-padding" fxLayout="column" fxFill [formGroupName]="i"> 21 <div class="vertical-padding" fxLayout="column" fxFill [formGroupName]="i">
22 <div fxLayout="row" fxFill fxLayoutAlign="start center" [fxShow]="!i"> 22 <div fxLayout="row" fxFill fxLayoutAlign="start center" [fxShow]="!i">
23 - <div fxFlex="20"> 23 + <div fxFlex="30">
24 <mat-label translate>device-profile.lwm2m.resource-label</mat-label> 24 <mat-label translate>device-profile.lwm2m.resource-label</mat-label>
25 </div> 25 </div>
26 - <div fxFlex="10"> 26 + <div fxFlex="10" style="text-align: center">
27 <mat-label translate>device-profile.lwm2m.attribute-label</mat-label> 27 <mat-label translate>device-profile.lwm2m.attribute-label</mat-label>
28 </div> 28 </div>
29 - <div fxFlex="10"> 29 + <div fxFlex="10" style="text-align: center">
30 <mat-label translate>device-profile.lwm2m.telemetry-label</mat-label> 30 <mat-label translate>device-profile.lwm2m.telemetry-label</mat-label>
31 </div> 31 </div>
32 - <div fxFlex="10"> 32 + <div fxFlex="10" style="text-align: center">
33 <mat-label translate>device-profile.lwm2m.observe-label</mat-label> 33 <mat-label translate>device-profile.lwm2m.observe-label</mat-label>
34 </div> 34 </div>
35 <div fxFlex> 35 <div fxFlex>
36 <mat-label translate>device-profile.lwm2m.key-name-label</mat-label> 36 <mat-label translate>device-profile.lwm2m.key-name-label</mat-label>
37 </div> 37 </div>
38 - <div fxFlex="17" fxFlexOffset="0">  
39 - <mat-label translate>device-profile.lwm2m.attribute-lwm2m-label</mat-label>  
40 - </div>  
41 </div> 38 </div>
42 <div fxLayout="row" fxFill fxLayoutAlign="start center"> 39 <div fxLayout="row" fxFill fxLayoutAlign="start center">
43 - <div class="resource-name-lw" fxFlex="25" 40 + <div class="resource-name-lw" fxFlex="30"
44 matTooltip="{{'device-profile.lwm2m.resource-tip' | translate}}" matTooltipPosition="above"> 41 matTooltip="{{'device-profile.lwm2m.resource-tip' | translate}}" matTooltipPosition="above">
45 <<b>{{resourceLwM2M.get('id').value}}</b>> <b><i>{{resourceLwM2M.get('name').value}}</i></b> 42 <<b>{{resourceLwM2M.get('id').value}}</b>> <b><i>{{resourceLwM2M.get('name').value}}</i></b>
46 </div> 43 </div>
@@ -65,7 +62,7 @@ @@ -65,7 +62,7 @@
65 matTooltipPosition="above"> 62 matTooltipPosition="above">
66 </mat-checkbox> 63 </mat-checkbox>
67 </div> 64 </div>
68 - <mat-form-field fxFlex="25"> 65 + <mat-form-field fxFlex="33">
69 <mat-label *ngIf="resourceLwM2M.get('keyName').hasError('required')"> 66 <mat-label *ngIf="resourceLwM2M.get('keyName').hasError('required')">
70 {{ 'device-profile.lwm2m.key-name-label' | translate }}</mat-label> 67 {{ 'device-profile.lwm2m.key-name-label' | translate }}</mat-label>
71 <input class="resource-name-lw" matInput type="text" formControlName="keyName" required 68 <input class="resource-name-lw" matInput type="text" formControlName="keyName" required
@@ -77,12 +74,13 @@ @@ -77,12 +74,13 @@
77 <strong>{{ 'device-profile.lwm2m.required' | translate }}</strong> 74 <strong>{{ 'device-profile.lwm2m.required' | translate }}</strong>
78 </mat-error> 75 </mat-error>
79 </mat-form-field> 76 </mat-form-field>
80 - <div fxFlex="20" class="resource-name-lw-end" fxFlexOffset="5"> 77 + <span fxFlex></span>
  78 + <div class="resource-name-lw-end">
81 <tb-profile-lwm2m-attributes 79 <tb-profile-lwm2m-attributes
82 formControlName="attributeLwm2m" 80 formControlName="attributeLwm2m"
83 - [attributeLwm2m]="resourceLwM2M.get('attributeLwm2m').value"  
84 [isAttributeTelemetry]="disableObserve(i)" 81 [isAttributeTelemetry]="disableObserve(i)"
85 - [destName]="getNameResourceLwm2m(resourceLwM2M.value)" 82 + isResource="true"
  83 + [modelName]="getNameResourceLwm2m(resourceLwM2M.value)"
86 [disabled]="this.disabled" 84 [disabled]="this.disabled"
87 (updateAttributeLwm2m)="updateAttributeLwm2m($event, i)"> 85 (updateAttributeLwm2m)="updateAttributeLwm2m($event, i)">
88 </tb-profile-lwm2m-attributes> 86 </tb-profile-lwm2m-attributes>
@@ -26,17 +26,15 @@ @@ -26,17 +26,15 @@
26 <div fxFlex class="resource-name-lw-end"> 26 <div fxFlex class="resource-name-lw-end">
27 <tb-profile-lwm2m-attributes 27 <tb-profile-lwm2m-attributes
28 formControlName="attributeLwm2m" 28 formControlName="attributeLwm2m"
29 - [attributeLwm2m]="objectLwM2M.get('attributeLwm2m').value"  
30 [isAttributeTelemetry]="disableObserveObject(i)" 29 [isAttributeTelemetry]="disableObserveObject(i)"
31 - [destName]="getNameObjectLwm2m( objectLwM2M.get('name').value, objectLwM2M.get('keyId').value)" 30 + [modelName]="getNameObjectLwm2m( objectLwM2M.get('name').value, objectLwM2M.get('keyId').value)"
32 [disabled]="this.disabled" 31 [disabled]="this.disabled"
33 (updateAttributeLwm2m)="updateAttributeLwm2mObject($event, objectLwM2M.get('keyId').value)"> 32 (updateAttributeLwm2m)="updateAttributeLwm2mObject($event, objectLwM2M.get('keyId').value)">
34 </tb-profile-lwm2m-attributes> 33 </tb-profile-lwm2m-attributes>
35 </div> 34 </div>
36 </mat-panel-title> 35 </mat-panel-title>
37 - <mat-panel-description fxFlex="5" fxLayoutAlign="end center" *ngIf="!disabled"> 36 + <mat-panel-description fxFlex="5" fxLayoutAlign="end center" *ngIf="!disabled && objectLwM2M.get('multiple').value">
38 <button type="button" 37 <button type="button"
39 - [fxShow]="objectLwM2M.get('multiple').value"  
40 mat-button mat-icon-button (click)="addInstances($event, objectLwM2M.value)" 38 mat-button mat-icon-button (click)="addInstances($event, objectLwM2M.value)"
41 matTooltip="{{'device-profile.lwm2m.add-instances-tip' | translate}}" 39 matTooltip="{{'device-profile.lwm2m.add-instances-tip' | translate}}"
42 matTooltipPosition="above"> 40 matTooltipPosition="above">
@@ -56,7 +54,7 @@ @@ -56,7 +54,7 @@
56 <mat-panel-title> 54 <mat-panel-title>
57 <div class="tb-panel-title-height" fxFlex="100"> 55 <div class="tb-panel-title-height" fxFlex="100">
58 <div fxLayout="row" fxFill> 56 <div fxLayout="row" fxFill>
59 - <div fxFlex="22"> 57 + <div fxFlex="30">
60 {{'device-profile.lwm2m.instance-label' | translate}} <<b>{{instances.get('id').value}}</b>> 58 {{'device-profile.lwm2m.instance-label' | translate}} <<b>{{instances.get('id').value}}</b>>
61 </div> 59 </div>
62 <div class="checkbox-padding" fxFlex="10"> 60 <div class="checkbox-padding" fxFlex="10">
@@ -95,14 +93,13 @@ @@ -95,14 +93,13 @@
95 matTooltipPosition="above"> 93 matTooltipPosition="above">
96 </mat-checkbox> 94 </mat-checkbox>
97 </div> 95 </div>
98 - <div fxFlex="10"> 96 + <div fxFlex="7">
99 </div> 97 </div>
100 <div fxFlex="37" class="resource-name-lw-end" fxFlexOffset="5"> 98 <div fxFlex="37" class="resource-name-lw-end" fxFlexOffset="5">
101 <tb-profile-lwm2m-attributes 99 <tb-profile-lwm2m-attributes
102 formControlName="attributeLwm2m" 100 formControlName="attributeLwm2m"
103 - [attributeLwm2m]="instances.get('attributeLwm2m').value"  
104 [isAttributeTelemetry]="disableObserveInstance(instances)" 101 [isAttributeTelemetry]="disableObserveInstance(instances)"
105 - [destName]="getNameInstanceLwm2m(instances.value, objectLwM2M.get('keyId').value)" 102 + [modelName]="getNameInstanceLwm2m(instances.value, objectLwM2M.get('keyId').value)"
106 [disabled]="this.disabled" 103 [disabled]="this.disabled"
107 (updateAttributeLwm2m)="updateAttributeLwm2mInstance($event, y, objectLwM2M.get('keyId').value)"> 104 (updateAttributeLwm2m)="updateAttributeLwm2mInstance($event, y, objectLwM2M.get('keyId').value)">
108 </tb-profile-lwm2m-attributes> 105 </tb-profile-lwm2m-attributes>
@@ -14,6 +14,8 @@ @@ -14,6 +14,8 @@
14 /// limitations under the License. 14 /// limitations under the License.
15 /// 15 ///
16 16
  17 +import { ValidatorFn, Validators } from '@angular/forms';
  18 +
17 export const PAGE_SIZE_LIMIT = 50; 19 export const PAGE_SIZE_LIMIT = 50;
18 export const INSTANCES = 'instances'; 20 export const INSTANCES = 'instances';
19 export const INSTANCE = 'instance'; 21 export const INSTANCE = 'instance';
@@ -39,7 +41,7 @@ export const DEFAULT_BOOTSTRAP_SERVER_ACCOUNT_TIME_OUT = 0; @@ -39,7 +41,7 @@ export const DEFAULT_BOOTSTRAP_SERVER_ACCOUNT_TIME_OUT = 0;
39 export const LEN_MAX_PUBLIC_KEY_RPK = 182; 41 export const LEN_MAX_PUBLIC_KEY_RPK = 182;
40 export const LEN_MAX_PUBLIC_KEY_X509 = 3000; 42 export const LEN_MAX_PUBLIC_KEY_X509 = 3000;
41 export const KEY_REGEXP_HEX_DEC = /^[-+]?[0-9A-Fa-f]+\.?[0-9A-Fa-f]*?$/; 43 export const KEY_REGEXP_HEX_DEC = /^[-+]?[0-9A-Fa-f]+\.?[0-9A-Fa-f]*?$/;
42 -export const KEY_REGEXP_NUMBER = /^(\-?|\+?)\d*$/; 44 +export const KEY_REGEXP_NUMBER = /^(-?|\+?)\d*$/;
43 export const INSTANCES_ID_VALUE_MIN = 0; 45 export const INSTANCES_ID_VALUE_MIN = 0;
44 export const INSTANCES_ID_VALUE_MAX = 65535; 46 export const INSTANCES_ID_VALUE_MAX = 65535;
45 export const DEFAULT_OTA_UPDATE_PROTOCOL = 'coap://'; 47 export const DEFAULT_OTA_UPDATE_PROTOCOL = 'coap://';
@@ -47,7 +49,7 @@ export const DEFAULT_FW_UPDATE_RESOURCE = DEFAULT_OTA_UPDATE_PROTOCOL + DEFAULT_ @@ -47,7 +49,7 @@ export const DEFAULT_FW_UPDATE_RESOURCE = DEFAULT_OTA_UPDATE_PROTOCOL + DEFAULT_
47 export const DEFAULT_SW_UPDATE_RESOURCE = DEFAULT_OTA_UPDATE_PROTOCOL + DEFAULT_LOCAL_HOST_NAME + ':' + DEFAULT_PORT_SERVER_NO_SEC; 49 export const DEFAULT_SW_UPDATE_RESOURCE = DEFAULT_OTA_UPDATE_PROTOCOL + DEFAULT_LOCAL_HOST_NAME + ':' + DEFAULT_PORT_SERVER_NO_SEC;
48 50
49 51
50 -export enum BINDING_MODE { 52 +export enum BingingMode {
51 U = 'U', 53 U = 'U',
52 UQ = 'UQ', 54 UQ = 'UQ',
53 T = 'T', 55 T = 'T',
@@ -60,58 +62,43 @@ export enum BINDING_MODE { @@ -60,58 +62,43 @@ export enum BINDING_MODE {
60 TQS = 'TQS' 62 TQS = 'TQS'
61 } 63 }
62 64
63 -export const BINDING_MODE_NAMES = new Map<BINDING_MODE, string>( 65 +export const BingingModeTranslationsMap = new Map<BingingMode, string>(
64 [ 66 [
65 - [BINDING_MODE.U, 'U: UDP connection in standard mode'],  
66 - [BINDING_MODE.UQ, 'UQ: UDP connection in queue mode'],  
67 - [BINDING_MODE.US, 'US: both UDP and SMS connections active, both in standard mode'],  
68 - [BINDING_MODE.UQS, 'UQS: both UDP and SMS connections active; UDP in queue mode, SMS in standard mode'],  
69 - [BINDING_MODE.T, 'T: TCP connection in standard mode'],  
70 - [BINDING_MODE.TQ, 'TQ: TCP connection in queue mode'],  
71 - [BINDING_MODE.TS, 'TS: both TCP and SMS connections active, both in standard mode'],  
72 - [BINDING_MODE.TQS, 'TQS: both TCP and SMS connections active; TCP in queue mode, SMS in standard mode'],  
73 - [BINDING_MODE.S, 'S: SMS connection in standard mode'],  
74 - [BINDING_MODE.SQ, 'SQ: SMS connection in queue mode'] 67 + [BingingMode.U, 'device-profile.lwm2m.binding-type.u'],
  68 + [BingingMode.UQ, 'device-profile.lwm2m.binding-type.uq'],
  69 + [BingingMode.US, 'device-profile.lwm2m.binding-type.us'],
  70 + [BingingMode.UQS, 'device-profile.lwm2m.binding-type.uqs'],
  71 + [BingingMode.T, 'device-profile.lwm2m.binding-type.t'],
  72 + [BingingMode.TQ, 'device-profile.lwm2m.binding-type.tq'],
  73 + [BingingMode.TS, 'device-profile.lwm2m.binding-type.ts'],
  74 + [BingingMode.TQS, 'device-profile.lwm2m.binding-type.tqs'],
  75 + [BingingMode.S, 'device-profile.lwm2m.binding-type.s'],
  76 + [BingingMode.SQ, 'device-profile.lwm2m.binding-type.sq']
75 ] 77 ]
76 ); 78 );
77 -  
78 -export enum ATTRIBUTE_LWM2M_ENUM {  
79 - dim = 'dim',  
80 - ver = 'ver', 79 +// TODO: wait release Leshan for issues: https://github.com/eclipse/leshan/issues/1026
  80 +export enum AttributeName {
81 pmin = 'pmin', 81 pmin = 'pmin',
82 pmax = 'pmax', 82 pmax = 'pmax',
83 gt = 'gt', 83 gt = 'gt',
84 lt = 'lt', 84 lt = 'lt',
85 st = 'st' 85 st = 'st'
  86 + // epmin = 'epmin',
  87 + // epmax = 'epmax'
86 } 88 }
87 89
88 -export const ATTRIBUTE_LWM2M_LABEL = new Map<ATTRIBUTE_LWM2M_ENUM, string>( 90 +export const AttributeNameTranslationMap = new Map<AttributeName, string>(
89 [ 91 [
90 - [ATTRIBUTE_LWM2M_ENUM.dim, 'dim='],  
91 - [ATTRIBUTE_LWM2M_ENUM.ver, 'ver='],  
92 - [ATTRIBUTE_LWM2M_ENUM.pmin, 'pmin='],  
93 - [ATTRIBUTE_LWM2M_ENUM.pmax, 'pmax='],  
94 - [ATTRIBUTE_LWM2M_ENUM.gt, '>'],  
95 - [ATTRIBUTE_LWM2M_ENUM.lt, '<'],  
96 - [ATTRIBUTE_LWM2M_ENUM.st, 'st=']  
97 - ]  
98 -);  
99 -  
100 -export const ATTRIBUTE_LWM2M_MAP = new Map<ATTRIBUTE_LWM2M_ENUM, string>(  
101 - [  
102 - [ATTRIBUTE_LWM2M_ENUM.dim, 'Dimension'],  
103 - [ATTRIBUTE_LWM2M_ENUM.ver, 'Object version'],  
104 - [ATTRIBUTE_LWM2M_ENUM.pmin, 'Minimum period'],  
105 - [ATTRIBUTE_LWM2M_ENUM.pmax, 'Maximum period'],  
106 - [ATTRIBUTE_LWM2M_ENUM.gt, 'Greater than'],  
107 - [ATTRIBUTE_LWM2M_ENUM.lt, 'Lesser than'],  
108 - [ATTRIBUTE_LWM2M_ENUM.st, 'Step'],  
109 - 92 + [AttributeName.pmin, 'device-profile.lwm2m.attributes-name.min-period'],
  93 + [AttributeName.pmax, 'device-profile.lwm2m.attributes-name.max-period'],
  94 + [AttributeName.gt, 'device-profile.lwm2m.attributes-name.greater-than'],
  95 + [AttributeName.lt, 'device-profile.lwm2m.attributes-name.less-than'],
  96 + [AttributeName.st, 'device-profile.lwm2m.attributes-name.step'],
  97 + // [AttributeName.epmin, 'device-profile.lwm2m.attributes-name.min-evaluation-period'],
  98 + // [AttributeName.epmax, 'device-profile.lwm2m.attributes-name.max-evaluation-period']
110 ] 99 ]
111 ); 100 );
112 101
113 -export const ATTRIBUTE_KEYS = Object.keys(ATTRIBUTE_LWM2M_ENUM) as string[];  
114 -  
115 export enum securityConfigMode { 102 export enum securityConfigMode {
116 PSK = 'PSK', 103 PSK = 'PSK',
117 RPK = 'RPK', 104 RPK = 'RPK',
@@ -143,18 +130,20 @@ export interface BootstrapServersSecurityConfig { @@ -143,18 +130,20 @@ export interface BootstrapServersSecurityConfig {
143 130
144 export interface ServerSecurityConfig { 131 export interface ServerSecurityConfig {
145 host?: string; 132 host?: string;
146 - securityHost?: string;  
147 port?: number; 133 port?: number;
148 - securityPort?: number;  
149 securityMode: securityConfigMode; 134 securityMode: securityConfigMode;
150 - clientPublicKeyOrId?: string;  
151 - clientSecretKey?: string;  
152 serverPublicKey?: string; 135 serverPublicKey?: string;
153 clientHoldOffTime?: number; 136 clientHoldOffTime?: number;
154 serverId?: number; 137 serverId?: number;
155 bootstrapServerAccountTimeout: number; 138 bootstrapServerAccountTimeout: number;
156 } 139 }
157 140
  141 +export interface ServerSecurityConfigInfo extends ServerSecurityConfig {
  142 + securityHost?: string;
  143 + securityPort?: number;
  144 + bootstrapServerIs: boolean;
  145 +}
  146 +
158 interface BootstrapSecurityConfig { 147 interface BootstrapSecurityConfig {
159 servers: BootstrapServersSecurityConfig; 148 servers: BootstrapServersSecurityConfig;
160 bootstrapServer: ServerSecurityConfig; 149 bootstrapServer: ServerSecurityConfig;
@@ -180,7 +169,7 @@ export interface ObservableAttributes { @@ -180,7 +169,7 @@ export interface ObservableAttributes {
180 attribute: string[]; 169 attribute: string[];
181 telemetry: string[]; 170 telemetry: string[];
182 keyName: {}; 171 keyName: {};
183 - attributeLwm2m: {}; 172 + attributeLwm2m?: AttributesNameValueMap;
184 } 173 }
185 174
186 export function getDefaultBootstrapServersSecurityConfig(): BootstrapServersSecurityConfig { 175 export function getDefaultBootstrapServersSecurityConfig(): BootstrapServersSecurityConfig {
@@ -193,9 +182,9 @@ export function getDefaultBootstrapServersSecurityConfig(): BootstrapServersSecu @@ -193,9 +182,9 @@ export function getDefaultBootstrapServersSecurityConfig(): BootstrapServersSecu
193 }; 182 };
194 } 183 }
195 184
196 -export function getDefaultBootstrapServerSecurityConfig(hostname: string): ServerSecurityConfig { 185 +export function getDefaultBootstrapServerSecurityConfig(): ServerSecurityConfig {
197 return { 186 return {
198 - host: hostname, 187 + host: DEFAULT_LOCAL_HOST_NAME,
199 port: DEFAULT_PORT_BOOTSTRAP_NO_SEC, 188 port: DEFAULT_PORT_BOOTSTRAP_NO_SEC,
200 securityMode: securityConfigMode.NO_SEC, 189 securityMode: securityConfigMode.NO_SEC,
201 serverPublicKey: '', 190 serverPublicKey: '',
@@ -205,22 +194,14 @@ export function getDefaultBootstrapServerSecurityConfig(hostname: string): Serve @@ -205,22 +194,14 @@ export function getDefaultBootstrapServerSecurityConfig(hostname: string): Serve
205 }; 194 };
206 } 195 }
207 196
208 -export function getDefaultLwM2MServerSecurityConfig(hostname): ServerSecurityConfig {  
209 - const DefaultLwM2MServerSecurityConfig = getDefaultBootstrapServerSecurityConfig(hostname); 197 +export function getDefaultLwM2MServerSecurityConfig(): ServerSecurityConfig {
  198 + const DefaultLwM2MServerSecurityConfig = getDefaultBootstrapServerSecurityConfig();
210 DefaultLwM2MServerSecurityConfig.port = DEFAULT_PORT_SERVER_NO_SEC; 199 DefaultLwM2MServerSecurityConfig.port = DEFAULT_PORT_SERVER_NO_SEC;
211 DefaultLwM2MServerSecurityConfig.serverId = DEFAULT_ID_SERVER; 200 DefaultLwM2MServerSecurityConfig.serverId = DEFAULT_ID_SERVER;
212 return DefaultLwM2MServerSecurityConfig; 201 return DefaultLwM2MServerSecurityConfig;
213 } 202 }
214 203
215 -function getDefaultProfileBootstrapSecurityConfig(hostname: any): BootstrapSecurityConfig {  
216 - return {  
217 - servers: getDefaultBootstrapServersSecurityConfig(),  
218 - bootstrapServer: getDefaultBootstrapServerSecurityConfig(hostname),  
219 - lwm2mServer: getDefaultLwM2MServerSecurityConfig(hostname)  
220 - };  
221 -}  
222 -  
223 -function getDefaultProfileObserveAttrConfig(): ObservableAttributes { 204 +export function getDefaultProfileObserveAttrConfig(): ObservableAttributes {
224 return { 205 return {
225 observe: [], 206 observe: [],
226 attribute: [], 207 attribute: [],
@@ -230,15 +211,7 @@ function getDefaultProfileObserveAttrConfig(): ObservableAttributes { @@ -230,15 +211,7 @@ function getDefaultProfileObserveAttrConfig(): ObservableAttributes {
230 }; 211 };
231 } 212 }
232 213
233 -export function getDefaultProfileConfig(hostname?: any): Lwm2mProfileConfigModels {  
234 - return {  
235 - clientLwM2mSettings: getDefaultProfileClientLwM2mSettingsConfig(),  
236 - observeAttr: getDefaultProfileObserveAttrConfig(),  
237 - bootstrap: getDefaultProfileBootstrapSecurityConfig((hostname) ? hostname : DEFAULT_LOCAL_HOST_NAME)  
238 - };  
239 -}  
240 -  
241 -function getDefaultProfileClientLwM2mSettingsConfig(): ClientLwM2mSettings { 214 +export function getDefaultProfileClientLwM2mSettingsConfig(): ClientLwM2mSettings {
242 return { 215 return {
243 clientOnlyObserveAfterConnect: 1, 216 clientOnlyObserveAfterConnect: 1,
244 fwUpdateStrategy: 1, 217 fwUpdateStrategy: 1,
@@ -255,12 +228,12 @@ export interface ResourceLwM2M { @@ -255,12 +228,12 @@ export interface ResourceLwM2M {
255 attribute: boolean; 228 attribute: boolean;
256 telemetry: boolean; 229 telemetry: boolean;
257 keyName: string; 230 keyName: string;
258 - attributeLwm2m?: {}; 231 + attributeLwm2m?: AttributesNameValueMap;
259 } 232 }
260 233
261 export interface Instance { 234 export interface Instance {
262 id: number; 235 id: number;
263 - attributeLwm2m?: {}; 236 + attributeLwm2m?: AttributesNameValueMap;
264 resources: ResourceLwM2M[]; 237 resources: ResourceLwM2M[];
265 } 238 }
266 239
@@ -271,12 +244,33 @@ export interface Instance { @@ -271,12 +244,33 @@ export interface Instance {
271 * mandatory == false => Optional 244 * mandatory == false => Optional
272 */ 245 */
273 export interface ObjectLwM2M { 246 export interface ObjectLwM2M {
274 -  
275 id: number; 247 id: number;
276 keyId: string; 248 keyId: string;
277 name: string; 249 name: string;
278 multiple?: boolean; 250 multiple?: boolean;
279 mandatory?: boolean; 251 mandatory?: boolean;
280 - attributeLwm2m?: {}; 252 + attributeLwm2m?: AttributesNameValueMap;
281 instances?: Instance []; 253 instances?: Instance [];
282 } 254 }
  255 +
  256 +export type AttributesNameValueMap = {
  257 + [key in AttributeName]?: number;
  258 +};
  259 +
  260 +export interface AttributesNameValue {
  261 + name: AttributeName;
  262 + value: number;
  263 +}
  264 +
  265 +export function valueValidatorByAttributeName(attributeName: AttributeName): ValidatorFn[] {
  266 + const validators = [Validators.required];
  267 + switch (attributeName) {
  268 + case AttributeName.pmin:
  269 + case AttributeName.pmax:
  270 + // case AttributeName.epmin:
  271 + // case AttributeName.epmax:
  272 + validators.push(Validators.min(0), Validators.pattern('[0-9]*'));
  273 + break;
  274 + }
  275 + return validators;
  276 +}
@@ -1231,36 +1231,53 @@ @@ -1231,36 +1231,53 @@
1231 "attribute-label": "Attribute", 1231 "attribute-label": "Attribute",
1232 "telemetry-label": "Telemetry", 1232 "telemetry-label": "Telemetry",
1233 "key-name-label": "Key Name", 1233 "key-name-label": "Key Name",
1234 - "attribute-lwm2m-label": "AttrLwm2m",  
1235 "resource-tip": "ID & Original Name of the Resource (only Operations isReadable)", 1234 "resource-tip": "ID & Original Name of the Resource (only Operations isReadable)",
1236 "is-observe-tip": "Is Observe", 1235 "is-observe-tip": "Is Observe",
1237 "not-observe-tip": "To observe select telemetry or attributes first", 1236 "not-observe-tip": "To observe select telemetry or attributes first",
1238 "is-attr-tip": "Is Attribute", 1237 "is-attr-tip": "Is Attribute",
1239 "is-telemetry-tip": "Is Telemetry", 1238 "is-telemetry-tip": "Is Telemetry",
1240 "key-name-tip": "Key Name in Camel format", 1239 "key-name-tip": "Key Name in Camel format",
1241 - "attribute-lwm2m-tip": "Attributes Lwm2m",  
1242 - "attribute-lwm2m-disable-tip": "To edit Attributes Lwm2m select telemetry or attributes first",  
1243 - "valid-attribute-lwm2m-key": "Name have be only '{{attrEnums}}'",  
1244 - "valid-attribute-lwm2m-value": "Value have be Long, Double and greater than zero or null/empty",  
1245 - "no-data": "No attributes", 1240 + "edit-attributes-select": "To edit attributes select telemetry or attributes",
  1241 + "no-attributes-set": "No attributes set",
1246 "key-name": "Key Name", 1242 "key-name": "Key Name",
1247 - "attribute-lwm2m-name": "Name attribute",  
1248 - "attribute-lwm2m-value": "Value",  
1249 - "attribute-lwm2m-toolbar-edit": "Edit attributes Lwm2m",  
1250 - "attribute-lwm2m-toolbar-view": "View Attributes Lwm2m",  
1251 - "attribute-lwm2m-destination": "Destination:",  
1252 - "attribute-lwm2m-add-tip": "Add attribute lwm2m",  
1253 - "attribute-lwm2m-remove-tip": "Remove attribute lwm2m",  
1254 - "required": " value is required.", 1243 + "attribute-name": "Name attribute",
  1244 + "attribute-name-required": "Name attribute is required.",
  1245 + "attribute-value": "Attribute value",
  1246 + "attribute-value-required": "Attribute value is required.",
  1247 + "attribute-value-pattern": "Attribute value must be a positive integer.",
  1248 + "edit-attributes": "Edit attributes: {{ name }}",
  1249 + "view-attributes": "View attributes: {{ name }}",
  1250 + "add-attribute": "Add attribute",
  1251 + "edit-attribute": "Edit attribute",
  1252 + "view-attribute": "View attribute",
  1253 + "remove-attribute": "Remove attribute",
1255 "mode": "Security config mode", 1254 "mode": "Security config mode",
1256 "pattern_hex_dec": "{ count, plural, 0 {must be hex decimal format} other {must be # characters} }", 1255 "pattern_hex_dec": "{ count, plural, 0 {must be hex decimal format} other {must be # characters} }",
1257 "servers": "Servers", 1256 "servers": "Servers",
1258 "short-id": "Short ID", 1257 "short-id": "Short ID",
1259 "short-id-required": "Short ID is required.", 1258 "short-id-required": "Short ID is required.",
1260 - "lifetime": "Lifetime of the registration for this LwM2M client",  
1261 - "default-min-period": "Minimum Period between two notifications (sec)",  
1262 - "notif-if-disabled": "Notification Storing When Disabled or Offline", 1259 + "short-id-range": "Short ID should be in a range from 1 to 65534.",
  1260 + "short-id-pattern": "Short ID must be a positive integer.",
  1261 + "lifetime": "Client registration lifetime",
  1262 + "lifetime-required": "Client registration lifetime is required.",
  1263 + "lifetime-pattern": "Client registration lifetime must be a positive integer.",
  1264 + "default-min-period": "Minimum period between two notifications (s)",
  1265 + "default-min-period-required": "Minimum period is required.",
  1266 + "default-min-period-pattern": "Minimum period must be a positive integer.",
  1267 + "notification-storing": "Notification storing when disabled or offline",
1263 "binding": "Binding", 1268 "binding": "Binding",
  1269 + "binding-type": {
  1270 + "u": "U: UDP connection in standard mode",
  1271 + "uq": "UQ: UDP connection in queue mode",
  1272 + "us": "US: both UDP and SMS connections active, both in standard mode",
  1273 + "uqs": "UQS: both UDP and SMS connections active; UDP in queue mode, SMS in standard mode",
  1274 + "t": "T: TCP connection in standard mode",
  1275 + "tq": "TQ: TCP connection in queue mode",
  1276 + "ts": "TS: both TCP and SMS connections active, both in standard mode",
  1277 + "tqs": "TQS: both TCP and SMS connections active; TCP in queue mode, SMS in standard mode",
  1278 + "s": "S: SMS connection in standard mode",
  1279 + "sq": "SQ: SMS connection in queue mode"
  1280 + },
1264 "bootstrap-tab": "Bootstrap", 1281 "bootstrap-tab": "Bootstrap",
1265 "bootstrap-server": "Bootstrap Server", 1282 "bootstrap-server": "Bootstrap Server",
1266 "lwm2m-server": "LwM2M Server", 1283 "lwm2m-server": "LwM2M Server",
@@ -1268,15 +1285,19 @@ @@ -1268,15 +1285,19 @@
1268 "server-host-required": "Host is required.", 1285 "server-host-required": "Host is required.",
1269 "server-port": "Port", 1286 "server-port": "Port",
1270 "server-port-required": "Port is required.", 1287 "server-port-required": "Port is required.",
  1288 + "server-port-pattern": "Port must be a positive integer.",
  1289 + "server-port-range": "Port should be in a range from 1 to 65535.",
1271 "server-public-key": "Server Public Key", 1290 "server-public-key": "Server Public Key",
1272 "server-public-key-required": "Server Public Key is required.", 1291 "server-public-key-required": "Server Public Key is required.",
1273 "server-public-key-pattern": "Server Public Key must be hex decimal format.", 1292 "server-public-key-pattern": "Server Public Key must be hex decimal format.",
1274 "server-public-key-length": "Server Public Key must be {{ count }} characters.", 1293 "server-public-key-length": "Server Public Key must be {{ count }} characters.",
1275 "client-hold-off-time": "Hold Off Time", 1294 "client-hold-off-time": "Hold Off Time",
1276 "client-hold-off-time-required": "Hold Off Time is required.", 1295 "client-hold-off-time-required": "Hold Off Time is required.",
  1296 + "client-hold-off-time-pattern": "Hold Off Time must be a positive integer.",
1277 "client-hold-off-time-tooltip": "Client Hold Off Time for use with a Bootstrap-Server only", 1297 "client-hold-off-time-tooltip": "Client Hold Off Time for use with a Bootstrap-Server only",
1278 "account-after-timeout": "Account after the timeout", 1298 "account-after-timeout": "Account after the timeout",
1279 "account-after-timeout-required": "Account after the timeout is required.", 1299 "account-after-timeout-required": "Account after the timeout is required.",
  1300 + "account-after-timeout-pattern": "Account after the timeout must be a positive integer.",
1280 "account-after-timeout-tooltip": "Bootstrap-Server Account after the timeout value given by this resource.", 1301 "account-after-timeout-tooltip": "Bootstrap-Server Account after the timeout value given by this resource.",
1281 "others-tab": "Other settings", 1302 "others-tab": "Other settings",
1282 "client-strategy": "Client strategy when connecting", 1303 "client-strategy": "Client strategy when connecting",
@@ -1296,7 +1317,16 @@ @@ -1296,7 +1317,16 @@
1296 "fw-update-recourse-required": "Firmware update CoAP recourse is required.", 1317 "fw-update-recourse-required": "Firmware update CoAP recourse is required.",
1297 "sw-update-recourse": "Software update CoAP recourse", 1318 "sw-update-recourse": "Software update CoAP recourse",
1298 "sw-update-recourse-required": "Software update CoAP recourse is required.", 1319 "sw-update-recourse-required": "Software update CoAP recourse is required.",
1299 - "config-json-tab": "Json Config Profile Device" 1320 + "config-json-tab": "Json Config Profile Device",
  1321 + "attributes-name": {
  1322 + "min-period": "Minimum period",
  1323 + "max-period": "Maximum period",
  1324 + "greater-than": "Greater than",
  1325 + "less-than": "Less than",
  1326 + "step": "Step",
  1327 + "min-evaluation-period": "Minimum evaluation period",
  1328 + "max-evaluation-period": "Maximum evaluation period"
  1329 + }
1300 }, 1330 },
1301 "snmp": { 1331 "snmp": {
1302 "add-communication-config": "Add communication config", 1332 "add-communication-config": "Add communication config",