Commit c9b83a4c88bfaafa3943232bd72266008c37767d
1 parent
74d2a3eb
UI: Refactoring LWM2M device profile transport configuration attributes settings
Showing
13 changed files
with
381 additions
and
355 deletions
common/data/src/main/java/org/thingsboard/server/common/data/device/data/lwm2m/ObjectAttributes.java
@@ -24,10 +24,10 @@ public class ObjectAttributes { | @@ -24,10 +24,10 @@ public class ObjectAttributes { | ||
24 | 24 | ||
25 | private Long dim; | 25 | private Long dim; |
26 | private String ver; | 26 | private String ver; |
27 | - private Long pmin; | ||
28 | - private Long pmax; | ||
29 | - private Double gt; | ||
30 | - private Double lt; | ||
31 | - private Double st; | 27 | + private Integer pmin; |
28 | + private Integer pmax; | ||
29 | + private Float gt; | ||
30 | + private Float lt; | ||
31 | + private Float st; | ||
32 | 32 | ||
33 | } | 33 | } |
@@ -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 { |
ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-attributes-key-list.component.html
@@ -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 |
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> |
ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-attributes-key-list.component.scss
0 → 100644
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 | } |
@@ -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'; |
@@ -74,44 +76,29 @@ export const BingingModeTranslationsMap = new Map<BingingMode, string>( | @@ -74,44 +76,29 @@ export const BingingModeTranslationsMap = new Map<BingingMode, string>( | ||
74 | [BingingMode.SQ, 'device-profile.lwm2m.binding-type.sq'] | 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', |
@@ -182,7 +169,7 @@ export interface ObservableAttributes { | @@ -182,7 +169,7 @@ export interface ObservableAttributes { | ||
182 | attribute: string[]; | 169 | attribute: string[]; |
183 | telemetry: string[]; | 170 | telemetry: string[]; |
184 | keyName: {}; | 171 | keyName: {}; |
185 | - attributeLwm2m: {}; | 172 | + attributeLwm2m?: AttributesNameValueMap; |
186 | } | 173 | } |
187 | 174 | ||
188 | export function getDefaultBootstrapServersSecurityConfig(): BootstrapServersSecurityConfig { | 175 | export function getDefaultBootstrapServersSecurityConfig(): BootstrapServersSecurityConfig { |
@@ -241,12 +228,12 @@ export interface ResourceLwM2M { | @@ -241,12 +228,12 @@ export interface ResourceLwM2M { | ||
241 | attribute: boolean; | 228 | attribute: boolean; |
242 | telemetry: boolean; | 229 | telemetry: boolean; |
243 | keyName: string; | 230 | keyName: string; |
244 | - attributeLwm2m?: {}; | 231 | + attributeLwm2m?: AttributesNameValueMap; |
245 | } | 232 | } |
246 | 233 | ||
247 | export interface Instance { | 234 | export interface Instance { |
248 | id: number; | 235 | id: number; |
249 | - attributeLwm2m?: {}; | 236 | + attributeLwm2m?: AttributesNameValueMap; |
250 | resources: ResourceLwM2M[]; | 237 | resources: ResourceLwM2M[]; |
251 | } | 238 | } |
252 | 239 | ||
@@ -257,12 +244,33 @@ export interface Instance { | @@ -257,12 +244,33 @@ export interface Instance { | ||
257 | * mandatory == false => Optional | 244 | * mandatory == false => Optional |
258 | */ | 245 | */ |
259 | export interface ObjectLwM2M { | 246 | export interface ObjectLwM2M { |
260 | - | ||
261 | id: number; | 247 | id: number; |
262 | keyId: string; | 248 | keyId: string; |
263 | name: string; | 249 | name: string; |
264 | multiple?: boolean; | 250 | multiple?: boolean; |
265 | mandatory?: boolean; | 251 | mandatory?: boolean; |
266 | - attributeLwm2m?: {}; | 252 | + attributeLwm2m?: AttributesNameValueMap; |
267 | instances?: Instance []; | 253 | instances?: Instance []; |
268 | } | 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,27 +1231,26 @@ | @@ -1231,27 +1231,26 @@ | ||
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", |
@@ -1318,7 +1317,16 @@ | @@ -1318,7 +1317,16 @@ | ||
1318 | "fw-update-recourse-required": "Firmware update CoAP recourse is required.", | 1317 | "fw-update-recourse-required": "Firmware update CoAP recourse is required.", |
1319 | "sw-update-recourse": "Software update CoAP recourse", | 1318 | "sw-update-recourse": "Software update CoAP recourse", |
1320 | "sw-update-recourse-required": "Software update CoAP recourse is required.", | 1319 | "sw-update-recourse-required": "Software update CoAP recourse is required.", |
1321 | - "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 | + } | ||
1322 | }, | 1330 | }, |
1323 | "snmp": { | 1331 | "snmp": { |
1324 | "add-communication-config": "Add communication config", | 1332 | "add-communication-config": "Add communication config", |