Commit ccb04f34a1dd5af8cfa8a30bfd10ad9a449b6c3c

Authored by Igor Kulikov
Committed by GitHub
2 parents 786439f9 5a3dfec7

Merge pull request #4871 from vvlladd28/improvement/device-credential/select-type

UI: Improvement device credentials
Showing 19 changed files with 388 additions and 200 deletions
ui-ngx/src/app/modules/home/components/device/device-credentials-lwm2m-server.component.html renamed from ui-ngx/src/app/modules/home/components/device/security-config-lwm2m-server.component.html
ui-ngx/src/app/modules/home/components/device/device-credentials-lwm2m-server.component.ts renamed from ui-ngx/src/app/modules/home/components/device/security-config-lwm2m-server.component.ts
@@ -39,24 +39,24 @@ import { takeUntil } from 'rxjs/operators'; @@ -39,24 +39,24 @@ import { takeUntil } from 'rxjs/operators';
39 import { Subject } from 'rxjs'; 39 import { Subject } from 'rxjs';
40 40
41 @Component({ 41 @Component({
42 - selector: 'tb-security-config-lwm2m-server',  
43 - templateUrl: './security-config-lwm2m-server.component.html', 42 + selector: 'tb-device-credentials-lwm2m-server',
  43 + templateUrl: './device-credentials-lwm2m-server.component.html',
44 styleUrls: [], 44 styleUrls: [],
45 providers: [ 45 providers: [
46 { 46 {
47 provide: NG_VALUE_ACCESSOR, 47 provide: NG_VALUE_ACCESSOR,
48 - useExisting: forwardRef(() => SecurityConfigLwm2mServerComponent), 48 + useExisting: forwardRef(() => DeviceCredentialsLwm2mServerComponent),
49 multi: true 49 multi: true
50 }, 50 },
51 { 51 {
52 provide: NG_VALIDATORS, 52 provide: NG_VALIDATORS,
53 - useExisting: forwardRef(() => SecurityConfigLwm2mServerComponent), 53 + useExisting: forwardRef(() => DeviceCredentialsLwm2mServerComponent),
54 multi: true 54 multi: true
55 } 55 }
56 ] 56 ]
57 }) 57 })
58 58
59 -export class SecurityConfigLwm2mServerComponent implements OnDestroy, ControlValueAccessor, Validator { 59 +export class DeviceCredentialsLwm2mServerComponent implements OnDestroy, ControlValueAccessor, Validator {
60 60
61 serverFormGroup: FormGroup; 61 serverFormGroup: FormGroup;
62 securityConfigLwM2MType = Lwm2mSecurityType; 62 securityConfigLwM2MType = Lwm2mSecurityType;
ui-ngx/src/app/modules/home/components/device/device-credentials-lwm2m.component.html renamed from ui-ngx/src/app/modules/home/components/device/security-config-lwm2m.component.html
@@ -15,8 +15,8 @@ @@ -15,8 +15,8 @@
15 limitations under the License. 15 limitations under the License.
16 16
17 --> 17 -->
18 -<mat-tab-group>  
19 - <mat-tab label="{{ 'device.lwm2m-security-config.client-tab' | translate }}" [formGroup]="lwm2mConfigFormGroup"> 18 +<mat-tab-group [formGroup]="lwm2mConfigFormGroup">
  19 + <mat-tab label="{{ 'device.lwm2m-security-config.client-tab' | translate }}">
20 <ng-container formGroupName="client"> 20 <ng-container formGroupName="client">
21 <mat-form-field class="mat-block"> 21 <mat-form-field class="mat-block">
22 <mat-label translate>device.lwm2m-security-config.endpoint</mat-label> 22 <mat-label translate>device.lwm2m-security-config.endpoint</mat-label>
@@ -79,7 +79,7 @@ @@ -79,7 +79,7 @@
79 </mat-form-field> 79 </mat-form-field>
80 </ng-container> 80 </ng-container>
81 </mat-tab> 81 </mat-tab>
82 - <mat-tab label="{{ 'device.lwm2m-security-config.bootstrap-tab' | translate }}" [formGroup]="lwm2mConfigFormGroup"> 82 + <mat-tab label="{{ 'device.lwm2m-security-config.bootstrap-tab' | translate }}">
83 <div style="padding: 2px;" formGroupName="bootstrap"> 83 <div style="padding: 2px;" formGroupName="bootstrap">
84 <mat-accordion multi="true" class="mat-body-1"> 84 <mat-accordion multi="true" class="mat-body-1">
85 <mat-expansion-panel> 85 <mat-expansion-panel>
@@ -89,9 +89,9 @@ @@ -89,9 +89,9 @@
89 </mat-panel-title> 89 </mat-panel-title>
90 </mat-expansion-panel-header> 90 </mat-expansion-panel-header>
91 <ng-template matExpansionPanelContent> 91 <ng-template matExpansionPanelContent>
92 - <tb-security-config-lwm2m-server 92 + <tb-device-credentials-lwm2m-server
93 formControlName="bootstrapServer"> 93 formControlName="bootstrapServer">
94 - </tb-security-config-lwm2m-server> 94 + </tb-device-credentials-lwm2m-server>
95 </ng-template> 95 </ng-template>
96 </mat-expansion-panel> 96 </mat-expansion-panel>
97 <mat-expansion-panel> 97 <mat-expansion-panel>
@@ -101,23 +101,12 @@ @@ -101,23 +101,12 @@
101 </mat-panel-title> 101 </mat-panel-title>
102 </mat-expansion-panel-header> 102 </mat-expansion-panel-header>
103 <ng-template matExpansionPanelContent> 103 <ng-template matExpansionPanelContent>
104 - <tb-security-config-lwm2m-server 104 + <tb-device-credentials-lwm2m-server
105 formControlName="lwm2mServer"> 105 formControlName="lwm2mServer">
106 - </tb-security-config-lwm2m-server> 106 + </tb-device-credentials-lwm2m-server>
107 </ng-template> 107 </ng-template>
108 </mat-expansion-panel> 108 </mat-expansion-panel>
109 </mat-accordion> 109 </mat-accordion>
110 </div> 110 </div>
111 </mat-tab> 111 </mat-tab>
112 - <mat-tab label="{{ 'device.lwm2m-security-config.config-json-tab' | translate }}">  
113 - <ng-template matTabContent>  
114 - <tb-json-object-edit  
115 - [ngModel]="lwm2mConfigFormGroup.value"  
116 - label="{{ 'device.lwm2m-value' | translate }}"  
117 - readonly  
118 - [required]="true"  
119 - [fillHeight]="false">  
120 - </tb-json-object-edit>  
121 - </ng-template>  
122 - </mat-tab>  
123 </mat-tab-group> 112 </mat-tab-group>
ui-ngx/src/app/modules/home/components/device/device-credentials-lwm2m.component.scss renamed from ui-ngx/src/app/modules/home/components/device/security-config-lwm2m.component.scss
ui-ngx/src/app/modules/home/components/device/device-credentials-lwm2m.component.ts renamed from ui-ngx/src/app/modules/home/components/device/security-config-lwm2m.component.ts
@@ -40,24 +40,24 @@ import { takeUntil } from 'rxjs/operators'; @@ -40,24 +40,24 @@ import { takeUntil } from 'rxjs/operators';
40 import { isDefinedAndNotNull } from '@core/utils'; 40 import { isDefinedAndNotNull } from '@core/utils';
41 41
42 @Component({ 42 @Component({
43 - selector: 'tb-security-config-lwm2m',  
44 - templateUrl: './security-config-lwm2m.component.html',  
45 - styleUrls: ['./security-config-lwm2m.component.scss'], 43 + selector: 'tb-device-credentials-lwm2m',
  44 + templateUrl: './device-credentials-lwm2m.component.html',
  45 + styleUrls: ['./device-credentials-lwm2m.component.scss'],
46 providers: [ 46 providers: [
47 { 47 {
48 provide: NG_VALUE_ACCESSOR, 48 provide: NG_VALUE_ACCESSOR,
49 - useExisting: forwardRef(() => SecurityConfigLwm2mComponent), 49 + useExisting: forwardRef(() => DeviceCredentialsLwm2mComponent),
50 multi: true 50 multi: true
51 }, 51 },
52 { 52 {
53 provide: NG_VALIDATORS, 53 provide: NG_VALIDATORS,
54 - useExisting: forwardRef(() => SecurityConfigLwm2mComponent), 54 + useExisting: forwardRef(() => DeviceCredentialsLwm2mComponent),
55 multi: true 55 multi: true
56 } 56 }
57 ] 57 ]
58 }) 58 })
59 59
60 -export class SecurityConfigLwm2mComponent implements ControlValueAccessor, Validator, OnDestroy { 60 +export class DeviceCredentialsLwm2mComponent implements ControlValueAccessor, Validator, OnDestroy {
61 61
62 lwm2mConfigFormGroup: FormGroup; 62 lwm2mConfigFormGroup: FormGroup;
63 securityConfigLwM2MType = Lwm2mSecurityType; 63 securityConfigLwM2MType = Lwm2mSecurityType;
@@ -89,6 +89,7 @@ export class SecurityConfigLwm2mComponent implements ControlValueAccessor, Valid @@ -89,6 +89,7 @@ export class SecurityConfigLwm2mComponent implements ControlValueAccessor, Valid
89 this.lwm2mConfigFormGroup.disable({emitEvent: false}); 89 this.lwm2mConfigFormGroup.disable({emitEvent: false});
90 } else { 90 } else {
91 this.lwm2mConfigFormGroup.enable({emitEvent: false}); 91 this.lwm2mConfigFormGroup.enable({emitEvent: false});
  92 + this.securityConfigClientUpdateValidators(this.lwm2mConfigFormGroup.get('client.securityConfigClientMode').value);
92 } 93 }
93 } 94 }
94 95
@@ -126,21 +127,21 @@ export class SecurityConfigLwm2mComponent implements ControlValueAccessor, Valid @@ -126,21 +127,21 @@ export class SecurityConfigLwm2mComponent implements ControlValueAccessor, Valid
126 switch (mode) { 127 switch (mode) {
127 case Lwm2mSecurityType.NO_SEC: 128 case Lwm2mSecurityType.NO_SEC:
128 this.setValidatorsNoSecX509(); 129 this.setValidatorsNoSecX509();
129 - this.lwm2mConfigFormGroup.get('client.cert').disable(); 130 + this.lwm2mConfigFormGroup.get('client.cert').disable({emitEvent: false});
130 break; 131 break;
131 case Lwm2mSecurityType.X509: 132 case Lwm2mSecurityType.X509:
132 this.setValidatorsNoSecX509(); 133 this.setValidatorsNoSecX509();
133 - this.lwm2mConfigFormGroup.get('client.cert').enable(); 134 + this.lwm2mConfigFormGroup.get('client.cert').enable({emitEvent: false});
134 break; 135 break;
135 case Lwm2mSecurityType.PSK: 136 case Lwm2mSecurityType.PSK:
136 this.lenMaxKeyClient = LEN_MAX_PSK; 137 this.lenMaxKeyClient = LEN_MAX_PSK;
137 this.setValidatorsPskRpk(mode); 138 this.setValidatorsPskRpk(mode);
138 - this.lwm2mConfigFormGroup.get('client.identity').enable(); 139 + this.lwm2mConfigFormGroup.get('client.identity').enable({emitEvent: false});
139 break; 140 break;
140 case Lwm2mSecurityType.RPK: 141 case Lwm2mSecurityType.RPK:
141 this.lenMaxKeyClient = LEN_MAX_PUBLIC_KEY_RPK; 142 this.lenMaxKeyClient = LEN_MAX_PUBLIC_KEY_RPK;
142 this.setValidatorsPskRpk(mode); 143 this.setValidatorsPskRpk(mode);
143 - this.lwm2mConfigFormGroup.get('client.identity').disable(); 144 + this.lwm2mConfigFormGroup.get('client.identity').disable({emitEvent: false});
144 break; 145 break;
145 } 146 }
146 this.lwm2mConfigFormGroup.get('client.identity').updateValueAndValidity({emitEvent: false}); 147 this.lwm2mConfigFormGroup.get('client.identity').updateValueAndValidity({emitEvent: false});
  1 +<!--
  2 +
  3 + Copyright © 2016-2021 The Thingsboard Authors
  4 +
  5 + Licensed under the Apache License, Version 2.0 (the "License");
  6 + you may not use this file except in compliance with the License.
  7 + You may obtain a copy of the License at
  8 +
  9 + http://www.apache.org/licenses/LICENSE-2.0
  10 +
  11 + Unless required by applicable law or agreed to in writing, software
  12 + distributed under the License is distributed on an "AS IS" BASIS,
  13 + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14 + See the License for the specific language governing permissions and
  15 + limitations under the License.
  16 +
  17 +-->
  18 +<section [formGroup]="deviceCredentialsMqttFormGroup">
  19 + <mat-form-field class="mat-block">
  20 + <mat-label translate>device.client-id</mat-label>
  21 + <input matInput formControlName="clientId">
  22 + <mat-error *ngIf="deviceCredentialsMqttFormGroup.get('clientId').hasError('pattern')">
  23 + {{ 'device.client-id-pattern' | translate }}
  24 + </mat-error>
  25 + </mat-form-field>
  26 + <mat-form-field class="mat-block">
  27 + <mat-label translate>device.user-name</mat-label>
  28 + <input matInput formControlName="userName" [required]="!!deviceCredentialsMqttFormGroup.get('password').value">
  29 + <mat-error *ngIf="deviceCredentialsMqttFormGroup.get('userName').hasError('required')">
  30 + {{ 'device.user-name-required' | translate }}
  31 + </mat-error>
  32 + </mat-form-field>
  33 + <mat-form-field class="mat-block">
  34 + <mat-label translate>device.password</mat-label>
  35 + <input matInput formControlName="password"
  36 + autocomplete="new-password"
  37 + (ngModelChange)="passwordChanged()"
  38 + [type]="hidePassword ? 'password' : 'text'">
  39 + <button mat-icon-button matSuffix type="button"
  40 + (click)="hidePassword = !hidePassword"
  41 + [attr.aria-pressed]="hidePassword">
  42 + <mat-icon>{{hidePassword ? 'visibility_off' : 'visibility'}}</mat-icon>
  43 + </button>
  44 + </mat-form-field>
  45 + <tb-error style="margin-top: -12px; display: block;"
  46 + [error]="deviceCredentialsMqttFormGroup.hasError('atLeastOne') ?
  47 + ('device.client-id-or-user-name-necessary' | translate) : ''"></tb-error>
  48 +</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 +
  17 +import { Component, forwardRef, Input, OnDestroy } from '@angular/core';
  18 +import {
  19 + ControlValueAccessor,
  20 + FormBuilder,
  21 + FormGroup,
  22 + NG_VALIDATORS,
  23 + NG_VALUE_ACCESSOR,
  24 + ValidationErrors,
  25 + Validator,
  26 + ValidatorFn,
  27 + Validators
  28 +} from '@angular/forms';
  29 +import { Subject } from 'rxjs';
  30 +import { DeviceCredentialMQTTBasic } from '@shared/models/device.models';
  31 +import { takeUntil } from 'rxjs/operators';
  32 +import { isDefinedAndNotNull, isEmptyStr } from '@core/utils';
  33 +
  34 +@Component({
  35 + selector: 'tb-device-credentials-mqtt-basic',
  36 + templateUrl: './device-credentials-mqtt-basic.component.html',
  37 + providers: [
  38 + {
  39 + provide: NG_VALUE_ACCESSOR,
  40 + useExisting: forwardRef(() => DeviceCredentialsMqttBasicComponent),
  41 + multi: true
  42 + },
  43 + {
  44 + provide: NG_VALIDATORS,
  45 + useExisting: forwardRef(() => DeviceCredentialsMqttBasicComponent),
  46 + multi: true,
  47 + }],
  48 + styleUrls: []
  49 +})
  50 +export class DeviceCredentialsMqttBasicComponent implements ControlValueAccessor, Validator, OnDestroy {
  51 +
  52 + @Input()
  53 + disabled: boolean;
  54 +
  55 + deviceCredentialsMqttFormGroup: FormGroup;
  56 +
  57 + hidePassword = true;
  58 +
  59 + private destroy$ = new Subject();
  60 + private propagateChange = (v: any) => {};
  61 +
  62 + constructor(public fb: FormBuilder) {
  63 + this.deviceCredentialsMqttFormGroup = this.fb.group({
  64 + clientId: [null, [Validators.pattern(/^[A-Za-z0-9]+$/)]],
  65 + userName: [null],
  66 + password: [null]
  67 + }, {validators: this.atLeastOne(Validators.required, ['clientId', 'userName'])});
  68 + this.deviceCredentialsMqttFormGroup.valueChanges.pipe(
  69 + takeUntil(this.destroy$)
  70 + ).subscribe((value) => {
  71 + this.updateView(value);
  72 + });
  73 + }
  74 +
  75 + ngOnDestroy(): void {
  76 + this.destroy$.next();
  77 + this.destroy$.complete();
  78 + }
  79 +
  80 + registerOnChange(fn: any): void {
  81 + this.propagateChange = fn;
  82 + }
  83 +
  84 + registerOnTouched(fn: any): void {}
  85 +
  86 + setDisabledState(isDisabled: boolean) {
  87 + this.disabled = isDisabled;
  88 + if (this.disabled) {
  89 + this.deviceCredentialsMqttFormGroup.disable({emitEvent: false});
  90 + } else {
  91 + this.deviceCredentialsMqttFormGroup.enable({emitEvent: false});
  92 + }
  93 + }
  94 +
  95 + validate(): ValidationErrors | null {
  96 + return this.deviceCredentialsMqttFormGroup.valid ? null : {
  97 + deviceCredentialsMqttBasic: false
  98 + };
  99 + }
  100 +
  101 + writeValue(mqttBasic: string) {
  102 + if (isDefinedAndNotNull(mqttBasic) && !isEmptyStr(mqttBasic)) {
  103 + const value = JSON.parse(mqttBasic);
  104 + this.deviceCredentialsMqttFormGroup.patchValue(value, {emitEvent: false});
  105 + }
  106 + }
  107 +
  108 + updateView(value: DeviceCredentialMQTTBasic) {
  109 + const formValue = JSON.stringify(value);
  110 + this.propagateChange(formValue);
  111 + }
  112 +
  113 + passwordChanged() {
  114 + const value = this.deviceCredentialsMqttFormGroup.get('password').value;
  115 + if (value !== '') {
  116 + this.deviceCredentialsMqttFormGroup.get('userName').setValidators([Validators.required]);
  117 + } else {
  118 + this.deviceCredentialsMqttFormGroup.get('userName').setValidators([]);
  119 + }
  120 + this.deviceCredentialsMqttFormGroup.get('userName').updateValueAndValidity({emitEvent: false});
  121 + }
  122 +
  123 + private atLeastOne(validator: ValidatorFn, controls: string[] = null) {
  124 + return (group: FormGroup): ValidationErrors | null => {
  125 + if (!controls) {
  126 + controls = Object.keys(group.controls);
  127 + }
  128 + const hasAtLeastOne = group?.controls && controls.some(k => !validator(group.controls[k]));
  129 +
  130 + return hasAtLeastOne ? null : {atLeastOne: true};
  131 + };
  132 + }
  133 +}
@@ -16,7 +16,7 @@ @@ -16,7 +16,7 @@
16 16
17 --> 17 -->
18 <section [formGroup]="deviceCredentialsFormGroup"> 18 <section [formGroup]="deviceCredentialsFormGroup">
19 - <mat-form-field class="mat-block"> 19 + <mat-form-field class="mat-block" [fxShow]="credentialsTypes?.length > 1">
20 <mat-label translate>device.credentials-type</mat-label> 20 <mat-label translate>device.credentials-type</mat-label>
21 <mat-select formControlName="credentialsType"> 21 <mat-select formControlName="credentialsType">
22 <mat-option *ngFor="let credentialsType of credentialsTypes" [value]="credentialsType"> 22 <mat-option *ngFor="let credentialsType of credentialsTypes" [value]="credentialsType">
@@ -24,58 +24,35 @@ @@ -24,58 +24,35 @@
24 </mat-option> 24 </mat-option>
25 </mat-select> 25 </mat-select>
26 </mat-form-field> 26 </mat-form-field>
27 - <mat-form-field *ngIf="deviceCredentialsFormGroup.get('credentialsType').value === deviceCredentialsType.ACCESS_TOKEN"  
28 - class="mat-block">  
29 - <mat-label translate>device.access-token</mat-label>  
30 - <input matInput formControlName="credentialsId" required>  
31 - <mat-error *ngIf="deviceCredentialsFormGroup.get('credentialsId').hasError('required')">  
32 - {{ 'device.access-token-required' | translate }}  
33 - </mat-error>  
34 - <mat-error *ngIf="deviceCredentialsFormGroup.get('credentialsId').hasError('pattern')">  
35 - {{ 'device.access-token-invalid' | translate }}  
36 - </mat-error>  
37 - </mat-form-field>  
38 - <mat-form-field *ngIf="deviceCredentialsFormGroup.get('credentialsType').value === deviceCredentialsType.X509_CERTIFICATE"  
39 - class="mat-block">  
40 - <mat-label translate>device.rsa-key</mat-label>  
41 - <textarea matInput formControlName="credentialsValue" cols="15" rows="5" required></textarea>  
42 - <mat-error *ngIf="deviceCredentialsFormGroup.get('credentialsValue').hasError('required')">  
43 - {{ 'device.rsa-key-required' | translate }}  
44 - </mat-error>  
45 - </mat-form-field>  
46 - <section *ngIf="deviceCredentialsFormGroup.get('credentialsType').value === deviceCredentialsType.MQTT_BASIC" formGroupName="credentialsBasic">  
47 - <mat-form-field class="mat-block">  
48 - <mat-label translate>device.client-id</mat-label>  
49 - <input matInput formControlName="clientId">  
50 - <mat-error *ngIf="deviceCredentialsFormGroup.get('credentialsBasic.clientId').hasError('pattern')">  
51 - {{ 'device.client-id-pattern' | translate }}  
52 - </mat-error>  
53 - </mat-form-field>  
54 - <mat-form-field class="mat-block">  
55 - <mat-label translate>device.user-name</mat-label>  
56 - <input matInput formControlName="userName" [required]="!!deviceCredentialsFormGroup.get('credentialsBasic.password').value">  
57 - <mat-error *ngIf="deviceCredentialsFormGroup.get('credentialsBasic.userName').hasError('required')">  
58 - {{ 'device.user-name-required' | translate }}  
59 - </mat-error>  
60 - </mat-form-field>  
61 - <mat-form-field class="mat-block">  
62 - <mat-label translate>device.password</mat-label>  
63 - <input matInput formControlName="password"  
64 - autocomplete="new-password"  
65 - (ngModelChange)="passwordChanged()"  
66 - [type]="hidePassword ? 'password' : 'text'">  
67 - <button mat-icon-button matSuffix type="button"  
68 - (click)="hidePassword = !hidePassword"  
69 - [attr.aria-pressed]="hidePassword">  
70 - <mat-icon>{{hidePassword ? 'visibility_off' : 'visibility'}}</mat-icon>  
71 - </button>  
72 - </mat-form-field>  
73 - <tb-error style="margin-top: -12px; display: block;"  
74 - [error]="deviceCredentialsFormGroup.get('credentialsBasic').hasError('atLeastOne') ?  
75 - ('device.client-id-or-user-name-necessary' | translate) : ''"></tb-error>  
76 - </section>  
77 - <div *ngIf="deviceCredentialsFormGroup.get('credentialsType').value === deviceCredentialsType.LWM2M_CREDENTIALS">  
78 - <tb-security-config-lwm2m formControlName="credentialsValue">  
79 - </tb-security-config-lwm2m> 27 + <div [ngSwitch]="deviceCredentialsFormGroup.get('credentialsType').value">
  28 + <ng-template [ngSwitchCase]="deviceCredentialsType.ACCESS_TOKEN">
  29 + <mat-form-field class="mat-block">
  30 + <mat-label translate>device.access-token</mat-label>
  31 + <input matInput formControlName="credentialsId" required>
  32 + <mat-error *ngIf="deviceCredentialsFormGroup.get('credentialsId').hasError('required')">
  33 + {{ 'device.access-token-required' | translate }}
  34 + </mat-error>
  35 + <mat-error *ngIf="deviceCredentialsFormGroup.get('credentialsId').hasError('pattern')">
  36 + {{ 'device.access-token-invalid' | translate }}
  37 + </mat-error>
  38 + </mat-form-field>
  39 + </ng-template>
  40 + <ng-template [ngSwitchCase]="deviceCredentialsType.X509_CERTIFICATE">
  41 + <mat-form-field class="mat-block">
  42 + <mat-label translate>device.rsa-key</mat-label>
  43 + <textarea matInput formControlName="credentialsValue" cols="15" rows="5" required></textarea>
  44 + <mat-error *ngIf="deviceCredentialsFormGroup.get('credentialsValue').hasError('required')">
  45 + {{ 'device.rsa-key-required' | translate }}
  46 + </mat-error>
  47 + </mat-form-field>
  48 + </ng-template>
  49 + <ng-template [ngSwitchCase]="deviceCredentialsType.MQTT_BASIC">
  50 + <tb-device-credentials-mqtt-basic formControlName="credentialsValue">
  51 + </tb-device-credentials-mqtt-basic>
  52 + </ng-template>
  53 + <ng-template [ngSwitchCase]="deviceCredentialsType.LWM2M_CREDENTIALS">
  54 + <tb-device-credentials-lwm2m formControlName="credentialsValue">
  55 + </tb-device-credentials-lwm2m>
  56 + </ng-template>
80 </div> 57 </div>
81 </section> 58 </section>
@@ -22,16 +22,15 @@ import { @@ -22,16 +22,15 @@ import {
22 FormGroup, 22 FormGroup,
23 NG_VALIDATORS, 23 NG_VALIDATORS,
24 NG_VALUE_ACCESSOR, 24 NG_VALUE_ACCESSOR,
25 - ValidationErrors,  
26 Validator, 25 Validator,
27 - ValidatorFn,  
28 Validators 26 Validators
29 } from '@angular/forms'; 27 } from '@angular/forms';
30 import { 28 import {
31 credentialTypeNames, 29 credentialTypeNames,
32 - DeviceCredentialMQTTBasic, 30 + credentialTypesByTransportType,
33 DeviceCredentials, 31 DeviceCredentials,
34 - DeviceCredentialsType 32 + DeviceCredentialsType,
  33 + DeviceTransportType
35 } from '@shared/models/device.models'; 34 } from '@shared/models/device.models';
36 import { Subject } from 'rxjs'; 35 import { Subject } from 'rxjs';
37 import { takeUntil } from 'rxjs/operators'; 36 import { takeUntil } from 'rxjs/operators';
@@ -58,32 +57,40 @@ export class DeviceCredentialsComponent implements ControlValueAccessor, OnInit, @@ -58,32 +57,40 @@ export class DeviceCredentialsComponent implements ControlValueAccessor, OnInit,
58 @Input() 57 @Input()
59 disabled: boolean; 58 disabled: boolean;
60 59
  60 + private deviceTransportTypeValue = DeviceTransportType.DEFAULT;
  61 + get deviceTransportType(): DeviceTransportType {
  62 + return this.deviceTransportTypeValue;
  63 + }
  64 + @Input()
  65 + set deviceTransportType(type: DeviceTransportType) {
  66 + if (type) {
  67 + this.deviceTransportTypeValue = type;
  68 + this.credentialsTypes = credentialTypesByTransportType.get(type);
  69 + const currentType = this.deviceCredentialsFormGroup.get('credentialsType').value;
  70 + if (!this.credentialsTypes.includes(currentType)) {
  71 + this.deviceCredentialsFormGroup.get('credentialsType').patchValue(this.credentialsTypes[0], {onlySelf: true});
  72 + }
  73 + }
  74 + }
  75 +
61 private destroy$ = new Subject(); 76 private destroy$ = new Subject();
62 77
63 deviceCredentialsFormGroup: FormGroup; 78 deviceCredentialsFormGroup: FormGroup;
64 79
65 deviceCredentialsType = DeviceCredentialsType; 80 deviceCredentialsType = DeviceCredentialsType;
66 81
67 - credentialsTypes = Object.values(DeviceCredentialsType); 82 + credentialsTypes = credentialTypesByTransportType.get(DeviceTransportType.DEFAULT);
68 83
69 credentialTypeNamesMap = credentialTypeNames; 84 credentialTypeNamesMap = credentialTypeNames;
70 85
71 - hidePassword = true;  
72 -  
73 private propagateChange = (v: any) => {}; 86 private propagateChange = (v: any) => {};
74 87
75 constructor(public fb: FormBuilder) { 88 constructor(public fb: FormBuilder) {
76 this.deviceCredentialsFormGroup = this.fb.group({ 89 this.deviceCredentialsFormGroup = this.fb.group({
77 credentialsType: [DeviceCredentialsType.ACCESS_TOKEN], 90 credentialsType: [DeviceCredentialsType.ACCESS_TOKEN],
78 credentialsId: [null], 91 credentialsId: [null],
79 - credentialsValue: [null],  
80 - credentialsBasic: this.fb.group({  
81 - clientId: [null, [Validators.pattern(/^[A-Za-z0-9]+$/)]],  
82 - userName: [null],  
83 - password: [null]  
84 - }, {validators: this.atLeastOne(Validators.required, ['clientId', 'userName'])}) 92 + credentialsValue: [null]
85 }); 93 });
86 - this.deviceCredentialsFormGroup.get('credentialsBasic').disable();  
87 this.deviceCredentialsFormGroup.valueChanges.pipe( 94 this.deviceCredentialsFormGroup.valueChanges.pipe(
88 takeUntil(this.destroy$) 95 takeUntil(this.destroy$)
89 ).subscribe(() => { 96 ).subscribe(() => {
@@ -109,18 +116,11 @@ export class DeviceCredentialsComponent implements ControlValueAccessor, OnInit, @@ -109,18 +116,11 @@ export class DeviceCredentialsComponent implements ControlValueAccessor, OnInit,
109 116
110 writeValue(value: DeviceCredentials | null): void { 117 writeValue(value: DeviceCredentials | null): void {
111 if (isDefinedAndNotNull(value)) { 118 if (isDefinedAndNotNull(value)) {
112 - let credentialsBasic = {clientId: null, userName: null, password: null};  
113 - let credentialsValue = null;  
114 - if (value.credentialsType === DeviceCredentialsType.MQTT_BASIC) {  
115 - credentialsBasic = JSON.parse(value.credentialsValue) as DeviceCredentialMQTTBasic;  
116 - } else {  
117 - credentialsValue = value.credentialsValue;  
118 - } 119 + const credentialsType = this.credentialsTypes.includes(value.credentialsType) ? value.credentialsType : this.credentialsTypes[0];
119 this.deviceCredentialsFormGroup.patchValue({ 120 this.deviceCredentialsFormGroup.patchValue({
120 - credentialsType: value.credentialsType, 121 + credentialsType,
121 credentialsId: value.credentialsId, 122 credentialsId: value.credentialsId,
122 - credentialsValue,  
123 - credentialsBasic 123 + credentialsValue: value.credentialsValue
124 }, {emitEvent: false}); 124 }, {emitEvent: false});
125 this.updateValidators(); 125 this.updateValidators();
126 } 126 }
@@ -128,10 +128,6 @@ export class DeviceCredentialsComponent implements ControlValueAccessor, OnInit, @@ -128,10 +128,6 @@ export class DeviceCredentialsComponent implements ControlValueAccessor, OnInit,
128 128
129 updateView() { 129 updateView() {
130 const deviceCredentialsValue = this.deviceCredentialsFormGroup.value; 130 const deviceCredentialsValue = this.deviceCredentialsFormGroup.value;
131 - if (deviceCredentialsValue.credentialsType === DeviceCredentialsType.MQTT_BASIC) {  
132 - deviceCredentialsValue.credentialsValue = JSON.stringify(deviceCredentialsValue.credentialsBasic);  
133 - }  
134 - delete deviceCredentialsValue.credentialsBasic;  
135 this.propagateChange(deviceCredentialsValue); 131 this.propagateChange(deviceCredentialsValue);
136 } 132 }
137 133
@@ -163,14 +159,12 @@ export class DeviceCredentialsComponent implements ControlValueAccessor, OnInit, @@ -163,14 +159,12 @@ export class DeviceCredentialsComponent implements ControlValueAccessor, OnInit,
163 credentialsTypeChanged(): void { 159 credentialsTypeChanged(): void {
164 this.deviceCredentialsFormGroup.patchValue({ 160 this.deviceCredentialsFormGroup.patchValue({
165 credentialsId: null, 161 credentialsId: null,
166 - credentialsValue: null,  
167 - credentialsBasic: {clientId: '', userName: '', password: ''} 162 + credentialsValue: null
168 }); 163 });
169 this.updateValidators(); 164 this.updateValidators();
170 } 165 }
171 166
172 updateValidators(): void { 167 updateValidators(): void {
173 - this.hidePassword = true;  
174 const credentialsType = this.deviceCredentialsFormGroup.get('credentialsType').value as DeviceCredentialsType; 168 const credentialsType = this.deviceCredentialsFormGroup.get('credentialsType').value as DeviceCredentialsType;
175 switch (credentialsType) { 169 switch (credentialsType) {
176 case DeviceCredentialsType.ACCESS_TOKEN: 170 case DeviceCredentialsType.ACCESS_TOKEN:
@@ -178,48 +172,13 @@ export class DeviceCredentialsComponent implements ControlValueAccessor, OnInit, @@ -178,48 +172,13 @@ export class DeviceCredentialsComponent implements ControlValueAccessor, OnInit,
178 this.deviceCredentialsFormGroup.get('credentialsId').updateValueAndValidity({emitEvent: false}); 172 this.deviceCredentialsFormGroup.get('credentialsId').updateValueAndValidity({emitEvent: false});
179 this.deviceCredentialsFormGroup.get('credentialsValue').setValidators([]); 173 this.deviceCredentialsFormGroup.get('credentialsValue').setValidators([]);
180 this.deviceCredentialsFormGroup.get('credentialsValue').updateValueAndValidity({emitEvent: false}); 174 this.deviceCredentialsFormGroup.get('credentialsValue').updateValueAndValidity({emitEvent: false});
181 - this.deviceCredentialsFormGroup.get('credentialsBasic').disable({emitEvent: false});  
182 break; 175 break;
183 - case DeviceCredentialsType.X509_CERTIFICATE:  
184 - case DeviceCredentialsType.LWM2M_CREDENTIALS: 176 + default:
185 this.deviceCredentialsFormGroup.get('credentialsValue').setValidators([Validators.required]); 177 this.deviceCredentialsFormGroup.get('credentialsValue').setValidators([Validators.required]);
186 this.deviceCredentialsFormGroup.get('credentialsValue').updateValueAndValidity({emitEvent: false}); 178 this.deviceCredentialsFormGroup.get('credentialsValue').updateValueAndValidity({emitEvent: false});
187 this.deviceCredentialsFormGroup.get('credentialsId').setValidators([]); 179 this.deviceCredentialsFormGroup.get('credentialsId').setValidators([]);
188 this.deviceCredentialsFormGroup.get('credentialsId').updateValueAndValidity({emitEvent: false}); 180 this.deviceCredentialsFormGroup.get('credentialsId').updateValueAndValidity({emitEvent: false});
189 - this.deviceCredentialsFormGroup.get('credentialsBasic').disable({emitEvent: false});  
190 - break;  
191 - case DeviceCredentialsType.MQTT_BASIC:  
192 - this.deviceCredentialsFormGroup.get('credentialsBasic').enable({emitEvent: false});  
193 - this.deviceCredentialsFormGroup.get('credentialsBasic').updateValueAndValidity({emitEvent: false});  
194 - this.deviceCredentialsFormGroup.get('credentialsId').setValidators([]);  
195 - this.deviceCredentialsFormGroup.get('credentialsId').updateValueAndValidity({emitEvent: false});  
196 - this.deviceCredentialsFormGroup.get('credentialsValue').setValidators([]);  
197 - this.deviceCredentialsFormGroup.get('credentialsValue').updateValueAndValidity({emitEvent: false});  
198 break; 181 break;
199 } 182 }
200 } 183 }
201 -  
202 - private atLeastOne(validator: ValidatorFn, controls: string[] = null) {  
203 - return (group: FormGroup): ValidationErrors | null => {  
204 - if (!controls) {  
205 - controls = Object.keys(group.controls);  
206 - }  
207 - const hasAtLeastOne = group?.controls && controls.some(k => !validator(group.controls[k]));  
208 -  
209 - return hasAtLeastOne ? null : {atLeastOne: true};  
210 - };  
211 - }  
212 -  
213 - passwordChanged() {  
214 - const value = this.deviceCredentialsFormGroup.get('credentialsBasic.password').value;  
215 - if (value !== '') {  
216 - this.deviceCredentialsFormGroup.get('credentialsBasic.userName').setValidators([Validators.required]);  
217 - } else {  
218 - this.deviceCredentialsFormGroup.get('credentialsBasic.userName').setValidators([]);  
219 - }  
220 - this.deviceCredentialsFormGroup.get('credentialsBasic.userName').updateValueAndValidity({  
221 - emitEvent: false,  
222 - onlySelf: true  
223 - });  
224 - }  
225 } 184 }
  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 +
  17 +import { NgModule } from '@angular/core';
  18 +import { CommonModule } from '@angular/common';
  19 +import { SharedModule } from '@shared/shared.module';
  20 +import { CopyDeviceCredentialsComponent } from './copy-device-credentials.component';
  21 +import { DeviceCredentialsComponent } from './device-credentials.component';
  22 +import { DeviceCredentialsLwm2mComponent } from './device-credentials-lwm2m.component';
  23 +import { DeviceCredentialsLwm2mServerComponent } from './device-credentials-lwm2m-server.component';
  24 +import { DeviceCredentialsMqttBasicComponent } from './device-credentials-mqtt-basic.component';
  25 +
  26 +@NgModule({
  27 + declarations: [
  28 + CopyDeviceCredentialsComponent,
  29 + DeviceCredentialsComponent,
  30 + DeviceCredentialsLwm2mComponent,
  31 + DeviceCredentialsLwm2mServerComponent,
  32 + DeviceCredentialsMqttBasicComponent
  33 + ],
  34 + imports: [
  35 + CommonModule,
  36 + SharedModule
  37 + ],
  38 + exports: [
  39 + CopyDeviceCredentialsComponent,
  40 + DeviceCredentialsComponent,
  41 + DeviceCredentialsLwm2mComponent,
  42 + DeviceCredentialsLwm2mServerComponent,
  43 + DeviceCredentialsMqttBasicComponent
  44 + ]
  45 +})
  46 +export class DeviceCredentialsModule { }
@@ -110,7 +110,6 @@ import { RuleChainAutocompleteComponent } from '@home/components/rule-chain/rule @@ -110,7 +110,6 @@ import { RuleChainAutocompleteComponent } from '@home/components/rule-chain/rule
110 import { DeviceProfileProvisionConfigurationComponent } from '@home/components/profile/device-profile-provision-configuration.component'; 110 import { DeviceProfileProvisionConfigurationComponent } from '@home/components/profile/device-profile-provision-configuration.component';
111 import { AlarmScheduleComponent } from '@home/components/profile/alarm/alarm-schedule.component'; 111 import { AlarmScheduleComponent } from '@home/components/profile/alarm/alarm-schedule.component';
112 import { DeviceWizardDialogComponent } from '@home/components/wizard/device-wizard-dialog.component'; 112 import { DeviceWizardDialogComponent } from '@home/components/wizard/device-wizard-dialog.component';
113 -import { DeviceCredentialsComponent } from '@home/components/device/device-credentials.component';  
114 import { AlarmScheduleInfoComponent } from '@home/components/profile/alarm/alarm-schedule-info.component'; 113 import { AlarmScheduleInfoComponent } from '@home/components/profile/alarm/alarm-schedule-info.component';
115 import { AlarmScheduleDialogComponent } from '@home/components/profile/alarm/alarm-schedule-dialog.component'; 114 import { AlarmScheduleDialogComponent } from '@home/components/profile/alarm/alarm-schedule-dialog.component';
116 import { EditAlarmDetailsDialogComponent } from '@home/components/profile/alarm/edit-alarm-details-dialog.component'; 115 import { EditAlarmDetailsDialogComponent } from '@home/components/profile/alarm/edit-alarm-details-dialog.component';
@@ -120,7 +119,6 @@ import { TenantProfileConfigurationComponent } from '@home/components/profile/te @@ -120,7 +119,6 @@ import { TenantProfileConfigurationComponent } from '@home/components/profile/te
120 import { SmsProviderConfigurationComponent } from '@home/components/sms/sms-provider-configuration.component'; 119 import { SmsProviderConfigurationComponent } from '@home/components/sms/sms-provider-configuration.component';
121 import { AwsSnsProviderConfigurationComponent } from '@home/components/sms/aws-sns-provider-configuration.component'; 120 import { AwsSnsProviderConfigurationComponent } from '@home/components/sms/aws-sns-provider-configuration.component';
122 import { TwilioSmsProviderConfigurationComponent } from '@home/components/sms/twilio-sms-provider-configuration.component'; 121 import { TwilioSmsProviderConfigurationComponent } from '@home/components/sms/twilio-sms-provider-configuration.component';
123 -import { CopyDeviceCredentialsComponent } from '@home/components/device/copy-device-credentials.component';  
124 import { Lwm2mProfileComponentsModule } from '@home/components/profile/device/lwm2m/lwm2m-profile-components.module'; 122 import { Lwm2mProfileComponentsModule } from '@home/components/profile/device/lwm2m/lwm2m-profile-components.module';
125 import { DashboardPageComponent } from '@home/components/dashboard-page/dashboard-page.component'; 123 import { DashboardPageComponent } from '@home/components/dashboard-page/dashboard-page.component';
126 import { DashboardToolbarComponent } from '@home/components/dashboard-page/dashboard-toolbar.component'; 124 import { DashboardToolbarComponent } from '@home/components/dashboard-page/dashboard-toolbar.component';
@@ -139,11 +137,10 @@ import { EdgeDownlinkTableComponent } from '@home/components/edge/edge-downlink- @@ -139,11 +137,10 @@ import { EdgeDownlinkTableComponent } from '@home/components/edge/edge-downlink-
139 import { EdgeDownlinkTableHeaderComponent } from '@home/components/edge/edge-downlink-table-header.component'; 137 import { EdgeDownlinkTableHeaderComponent } from '@home/components/edge/edge-downlink-table-header.component';
140 import { DisplayWidgetTypesPanelComponent } from '@home/components/dashboard-page/widget-types-panel.component'; 138 import { DisplayWidgetTypesPanelComponent } from '@home/components/dashboard-page/widget-types-panel.component';
141 import { AlarmDurationPredicateValueComponent } from '@home/components/profile/alarm/alarm-duration-predicate-value.component'; 139 import { AlarmDurationPredicateValueComponent } from '@home/components/profile/alarm/alarm-duration-predicate-value.component';
142 -import { SecurityConfigLwm2mComponent } from '@home/components/device/security-config-lwm2m.component';  
143 -import { SecurityConfigLwm2mServerComponent } from '@home/components/device/security-config-lwm2m-server.component';  
144 import { DashboardImageDialogComponent } from '@home/components/dashboard-page/dashboard-image-dialog.component'; 140 import { DashboardImageDialogComponent } from '@home/components/dashboard-page/dashboard-image-dialog.component';
145 import { WidgetContainerComponent } from '@home/components/widget/widget-container.component'; 141 import { WidgetContainerComponent } from '@home/components/widget/widget-container.component';
146 import { SnmpDeviceProfileTransportModule } from '@home/components/profile/device/snpm/snmp-device-profile-transport.module'; 142 import { SnmpDeviceProfileTransportModule } from '@home/components/profile/device/snpm/snmp-device-profile-transport.module';
  143 +import { DeviceCredentialsModule } from '@home/components/device/device-credentials.module';
147 144
148 @NgModule({ 145 @NgModule({
149 declarations: 146 declarations:
@@ -245,10 +242,6 @@ import { SnmpDeviceProfileTransportModule } from '@home/components/profile/devic @@ -245,10 +242,6 @@ import { SnmpDeviceProfileTransportModule } from '@home/components/profile/devic
245 AlarmScheduleComponent, 242 AlarmScheduleComponent,
246 AlarmDurationPredicateValueComponent, 243 AlarmDurationPredicateValueComponent,
247 DeviceWizardDialogComponent, 244 DeviceWizardDialogComponent,
248 - DeviceCredentialsComponent,  
249 - CopyDeviceCredentialsComponent,  
250 - SecurityConfigLwm2mComponent,  
251 - SecurityConfigLwm2mServerComponent,  
252 AlarmScheduleDialogComponent, 245 AlarmScheduleDialogComponent,
253 EditAlarmDetailsDialogComponent, 246 EditAlarmDetailsDialogComponent,
254 SmsProviderConfigurationComponent, 247 SmsProviderConfigurationComponent,
@@ -274,7 +267,8 @@ import { SnmpDeviceProfileTransportModule } from '@home/components/profile/devic @@ -274,7 +267,8 @@ import { SnmpDeviceProfileTransportModule } from '@home/components/profile/devic
274 SharedHomeComponentsModule, 267 SharedHomeComponentsModule,
275 Lwm2mProfileComponentsModule, 268 Lwm2mProfileComponentsModule,
276 SnmpDeviceProfileTransportModule, 269 SnmpDeviceProfileTransportModule,
277 - StatesControllerModule 270 + StatesControllerModule,
  271 + DeviceCredentialsModule
278 ], 272 ],
279 exports: [ 273 exports: [
280 EntitiesTableComponent, 274 EntitiesTableComponent,
@@ -353,10 +347,6 @@ import { SnmpDeviceProfileTransportModule } from '@home/components/profile/devic @@ -353,10 +347,6 @@ import { SnmpDeviceProfileTransportModule } from '@home/components/profile/devic
353 AddDeviceProfileDialogComponent, 347 AddDeviceProfileDialogComponent,
354 RuleChainAutocompleteComponent, 348 RuleChainAutocompleteComponent,
355 DeviceWizardDialogComponent, 349 DeviceWizardDialogComponent,
356 - DeviceCredentialsComponent,  
357 - CopyDeviceCredentialsComponent,  
358 - SecurityConfigLwm2mComponent,  
359 - SecurityConfigLwm2mServerComponent,  
360 AlarmScheduleInfoComponent, 350 AlarmScheduleInfoComponent,
361 AlarmScheduleComponent, 351 AlarmScheduleComponent,
362 AlarmScheduleDialogComponent, 352 AlarmScheduleDialogComponent,
@@ -64,7 +64,8 @@ @@ -64,7 +64,8 @@
64 [ngClass]="{invisible: deviceWizardFormGroup.get('addProfileType').value !== 0}" 64 [ngClass]="{invisible: deviceWizardFormGroup.get('addProfileType').value !== 0}"
65 [addNewProfile]="false" 65 [addNewProfile]="false"
66 [selectDefaultProfile]="true" 66 [selectDefaultProfile]="true"
67 - [editProfileEnabled]="false"> 67 + [editProfileEnabled]="false"
  68 + (deviceProfileChanged)="deviceProfileChanged($event)">
68 </tb-device-profile-autocomplete> 69 </tb-device-profile-autocomplete>
69 <mat-form-field fxFlex class="mat-block" 70 <mat-form-field fxFlex class="mat-block"
70 [ngClass]="{invisible: deviceWizardFormGroup.get('addProfileType').value !== 1}"> 71 [ngClass]="{invisible: deviceWizardFormGroup.get('addProfileType').value !== 1}">
@@ -154,6 +155,7 @@ @@ -154,6 +155,7 @@
154 <mat-checkbox style="padding-bottom: 16px;" formControlName="setCredential">{{ 'device.wizard.add-credentials' | translate }}</mat-checkbox> 155 <mat-checkbox style="padding-bottom: 16px;" formControlName="setCredential">{{ 'device.wizard.add-credentials' | translate }}</mat-checkbox>
155 <tb-device-credentials 156 <tb-device-credentials
156 [fxShow]="credentialsFormGroup.get('setCredential').value" 157 [fxShow]="credentialsFormGroup.get('setCredential').value"
  158 + [deviceTransportType]="deviceTransportType"
157 formControlName="credential"> 159 formControlName="credential">
158 </tb-device-credentials> 160 </tb-device-credentials>
159 </form> 161 </form>
@@ -25,6 +25,7 @@ import { @@ -25,6 +25,7 @@ import {
25 createDeviceProfileConfiguration, 25 createDeviceProfileConfiguration,
26 createDeviceProfileTransportConfiguration, 26 createDeviceProfileTransportConfiguration,
27 DeviceProfile, 27 DeviceProfile,
  28 + DeviceProfileInfo,
28 DeviceProfileType, 29 DeviceProfileType,
29 DeviceProvisionConfiguration, 30 DeviceProvisionConfiguration,
30 DeviceProvisionType, 31 DeviceProvisionType,
@@ -91,6 +92,7 @@ export class DeviceWizardDialogComponent extends @@ -91,6 +92,7 @@ export class DeviceWizardDialogComponent extends
91 serviceType = ServiceType.TB_RULE_ENGINE; 92 serviceType = ServiceType.TB_RULE_ENGINE;
92 93
93 private subscriptions: Subscription[] = []; 94 private subscriptions: Subscription[] = [];
  95 + private currentDeviceProfileTransportType = DeviceTransportType.DEFAULT;
94 96
95 constructor(protected store: Store<AppState>, 97 constructor(protected store: Store<AppState>,
96 protected router: Router, 98 protected router: Router,
@@ -265,6 +267,20 @@ export class DeviceWizardDialogComponent extends @@ -265,6 +267,20 @@ export class DeviceWizardDialogComponent extends
265 } 267 }
266 } 268 }
267 269
  270 + get deviceTransportType(): DeviceTransportType {
  271 + if (this.deviceWizardFormGroup.get('addProfileType').value) {
  272 + return this.transportConfigFormGroup.get('transportType').value;
  273 + } else {
  274 + return this.currentDeviceProfileTransportType;
  275 + }
  276 + }
  277 +
  278 + deviceProfileChanged(deviceProfile: DeviceProfileInfo) {
  279 + if (deviceProfile) {
  280 + this.currentDeviceProfileTransportType = deviceProfile.transportType;
  281 + }
  282 + }
  283 +
268 private createDeviceProfile(): Observable<EntityId> { 284 private createDeviceProfile(): Observable<EntityId> {
269 if (this.deviceWizardFormGroup.get('addProfileType').value) { 285 if (this.deviceWizardFormGroup.get('addProfileType').value) {
270 const deviceProvisionConfiguration: DeviceProvisionConfiguration = this.provisionConfigFormGroup.get('provisionConfiguration').value; 286 const deviceProvisionConfiguration: DeviceProvisionConfiguration = this.provisionConfigFormGroup.get('provisionConfiguration').value;
@@ -17,7 +17,7 @@ @@ -17,7 +17,7 @@
17 --> 17 -->
18 <form [formGroup]="deviceCredentialsFormGroup" (ngSubmit)="save()" style="min-width: 350px;"> 18 <form [formGroup]="deviceCredentialsFormGroup" (ngSubmit)="save()" style="min-width: 350px;">
19 <mat-toolbar color="primary"> 19 <mat-toolbar color="primary">
20 - <h2 translate>device.device-credentials</h2> 20 + <h2>{{ 'device.device-credentials' | translate }}</h2>
21 <span fxFlex></span> 21 <span fxFlex></span>
22 <button mat-icon-button 22 <button mat-icon-button
23 (click)="cancel()" 23 (click)="cancel()"
@@ -25,15 +25,26 @@ @@ -25,15 +25,26 @@
25 <mat-icon class="material-icons">close</mat-icon> 25 <mat-icon class="material-icons">close</mat-icon>
26 </button> 26 </button>
27 </mat-toolbar> 27 </mat-toolbar>
28 - <mat-progress-bar color="warn" mode="indeterminate" *ngIf="isLoading$ | async"> 28 + <mat-progress-bar color="warn" mode="indeterminate" *ngIf="(isLoading$ | async) && !loadingCredentials">
29 </mat-progress-bar> 29 </mat-progress-bar>
30 - <div style="height: 4px;" *ngIf="!(isLoading$ | async)"></div> 30 + <div style="height: 4px;" *ngIf="!(isLoading$ | async) || loadingCredentials"></div>
31 <div mat-dialog-content> 31 <div mat-dialog-content>
32 - <fieldset [disabled]="(isLoading$ | async) || isReadOnly">  
33 - <tb-device-credentials  
34 - formControlName="credential">  
35 - </tb-device-credentials>  
36 - </fieldset> 32 + <section *ngIf="!loadingCredentials; else loadCredentials">
  33 + <fieldset [disabled]="(isLoading$ | async) || isReadOnly">
  34 + <tb-device-credentials
  35 + [deviceTransportType]="deviceTransportType"
  36 + formControlName="credential">
  37 + </tb-device-credentials>
  38 + </fieldset>
  39 + </section>
  40 + <ng-template #loadCredentials>
  41 + <div fxLayout="column" fxLayoutAlign="center center">
  42 + <mat-spinner color="accent" diameter="65" strokeWidth="4" style="margin-bottom: 18px"></mat-spinner>
  43 + <span class="mat-subheading-2" style="margin-bottom: 0">
  44 + {{ 'device.loading-device-credentials' | translate }}
  45 + </span>
  46 + </div>
  47 + </ng-template>
37 </div> 48 </div>
38 <div mat-dialog-actions fxLayoutAlign="end center"> 49 <div mat-dialog-actions fxLayoutAlign="end center">
39 <button mat-button color="primary" 50 <button mat-button color="primary"
@@ -21,13 +21,16 @@ import { Store } from '@ngrx/store'; @@ -21,13 +21,16 @@ import { Store } from '@ngrx/store';
21 import { AppState } from '@core/core.state'; 21 import { AppState } from '@core/core.state';
22 import { FormBuilder, FormControl, FormGroup, FormGroupDirective, NgForm } from '@angular/forms'; 22 import { FormBuilder, FormControl, FormGroup, FormGroupDirective, NgForm } from '@angular/forms';
23 import { DeviceService } from '@core/http/device.service'; 23 import { DeviceService } from '@core/http/device.service';
24 -import { credentialTypeNames, DeviceCredentials, DeviceCredentialsType } from '@shared/models/device.models'; 24 +import { DeviceCredentials, DeviceProfileInfo, DeviceTransportType } from '@shared/models/device.models';
25 import { DialogComponent } from '@shared/components/dialog.component'; 25 import { DialogComponent } from '@shared/components/dialog.component';
26 import { Router } from '@angular/router'; 26 import { Router } from '@angular/router';
  27 +import { DeviceProfileService } from '@core/http/device-profile.service';
  28 +import { forkJoin } from 'rxjs';
27 29
28 export interface DeviceCredentialsDialogData { 30 export interface DeviceCredentialsDialogData {
29 isReadOnly: boolean; 31 isReadOnly: boolean;
30 deviceId: string; 32 deviceId: string;
  33 + deviceProfileId: string;
31 } 34 }
32 35
33 @Component({ 36 @Component({
@@ -40,25 +43,18 @@ export class DeviceCredentialsDialogComponent extends @@ -40,25 +43,18 @@ export class DeviceCredentialsDialogComponent extends
40 DialogComponent<DeviceCredentialsDialogComponent, DeviceCredentials> implements OnInit, ErrorStateMatcher { 43 DialogComponent<DeviceCredentialsDialogComponent, DeviceCredentials> implements OnInit, ErrorStateMatcher {
41 44
42 deviceCredentialsFormGroup: FormGroup; 45 deviceCredentialsFormGroup: FormGroup;
43 - 46 + deviceTransportType: DeviceTransportType;
44 isReadOnly: boolean; 47 isReadOnly: boolean;
  48 + loadingCredentials = true;
45 49
46 - deviceCredentials: DeviceCredentials;  
47 -  
48 - submitted = false;  
49 -  
50 - deviceCredentialsType = DeviceCredentialsType;  
51 -  
52 - credentialsTypes = Object.keys(DeviceCredentialsType);  
53 -  
54 - credentialTypeNamesMap = credentialTypeNames;  
55 -  
56 - hidePassword = true; 50 + private deviceCredentials: DeviceCredentials;
  51 + private submitted = false;
57 52
58 constructor(protected store: Store<AppState>, 53 constructor(protected store: Store<AppState>,
59 protected router: Router, 54 protected router: Router,
60 @Inject(MAT_DIALOG_DATA) public data: DeviceCredentialsDialogData, 55 @Inject(MAT_DIALOG_DATA) public data: DeviceCredentialsDialogData,
61 private deviceService: DeviceService, 56 private deviceService: DeviceService,
  57 + private deviceProfileService: DeviceProfileService,
62 @SkipSelf() private errorStateMatcher: ErrorStateMatcher, 58 @SkipSelf() private errorStateMatcher: ErrorStateMatcher,
63 public dialogRef: MatDialogRef<DeviceCredentialsDialogComponent, DeviceCredentials>, 59 public dialogRef: MatDialogRef<DeviceCredentialsDialogComponent, DeviceCredentials>,
64 public fb: FormBuilder) { 60 public fb: FormBuilder) {
@@ -84,14 +80,17 @@ export class DeviceCredentialsDialogComponent extends @@ -84,14 +80,17 @@ export class DeviceCredentialsDialogComponent extends
84 } 80 }
85 81
86 loadDeviceCredentials() { 82 loadDeviceCredentials() {
87 - this.deviceService.getDeviceCredentials(this.data.deviceId).subscribe(  
88 - (deviceCredentials) => {  
89 - this.deviceCredentials = deviceCredentials;  
90 - this.deviceCredentialsFormGroup.patchValue({  
91 - credential: deviceCredentials  
92 - }, {emitEvent: false});  
93 - }  
94 - ); 83 + const task = [];
  84 + task.push(this.deviceService.getDeviceCredentials(this.data.deviceId));
  85 + task.push(this.deviceProfileService.getDeviceProfileInfo(this.data.deviceProfileId));
  86 + forkJoin(task).subscribe(([deviceCredentials, deviceProfile]: [DeviceCredentials, DeviceProfileInfo]) => {
  87 + this.deviceTransportType = deviceProfile.transportType;
  88 + this.deviceCredentials = deviceCredentials;
  89 + this.deviceCredentialsFormGroup.patchValue({
  90 + credential: deviceCredentials
  91 + }, {emitEvent: false});
  92 + this.loadingCredentials = false;
  93 + });
95 } 94 }
96 95
97 cancel(): void { 96 cancel(): void {
@@ -33,6 +33,7 @@ import { MqttDeviceTransportConfigurationComponent } from './data/mqtt-device-tr @@ -33,6 +33,7 @@ import { MqttDeviceTransportConfigurationComponent } from './data/mqtt-device-tr
33 import { CoapDeviceTransportConfigurationComponent } from './data/coap-device-transport-configuration.component'; 33 import { CoapDeviceTransportConfigurationComponent } from './data/coap-device-transport-configuration.component';
34 import { Lwm2mDeviceTransportConfigurationComponent } from './data/lwm2m-device-transport-configuration.component'; 34 import { Lwm2mDeviceTransportConfigurationComponent } from './data/lwm2m-device-transport-configuration.component';
35 import { SnmpDeviceTransportConfigurationComponent } from './data/snmp-device-transport-configuration.component'; 35 import { SnmpDeviceTransportConfigurationComponent } from './data/snmp-device-transport-configuration.component';
  36 +import { DeviceCredentialsModule } from '@home/components/device/device-credentials.module';
36 37
37 @NgModule({ 38 @NgModule({
38 declarations: [ 39 declarations: [
@@ -55,6 +56,7 @@ import { SnmpDeviceTransportConfigurationComponent } from './data/snmp-device-tr @@ -55,6 +56,7 @@ import { SnmpDeviceTransportConfigurationComponent } from './data/snmp-device-tr
55 SharedModule, 56 SharedModule,
56 HomeComponentsModule, 57 HomeComponentsModule,
57 HomeDialogsModule, 58 HomeDialogsModule,
  59 + DeviceCredentialsModule,
58 DeviceRoutingModule 60 DeviceRoutingModule
59 ] 61 ]
60 }) 62 })
@@ -112,7 +112,8 @@ export class DevicesTableConfigResolver implements Resolve<EntityTableConfig<Dev @@ -112,7 +112,8 @@ export class DevicesTableConfigResolver implements Resolve<EntityTableConfig<Dev
112 )); 112 ));
113 }; 113 };
114 this.config.onEntityAction = action => this.onDeviceAction(action); 114 this.config.onEntityAction = action => this.onDeviceAction(action);
115 - this.config.detailsReadonly = () => (this.config.componentsData.deviceScope === 'customer_user' || this.config.componentsData.deviceScope === 'edge_customer_user'); 115 + this.config.detailsReadonly = () =>
  116 + (this.config.componentsData.deviceScope === 'customer_user' || this.config.componentsData.deviceScope === 'edge_customer_user');
116 117
117 this.config.headerComponent = DeviceTableHeaderComponent; 118 this.config.headerComponent = DeviceTableHeaderComponent;
118 119
@@ -528,6 +529,7 @@ export class DevicesTableConfigResolver implements Resolve<EntityTableConfig<Dev @@ -528,6 +529,7 @@ export class DevicesTableConfigResolver implements Resolve<EntityTableConfig<Dev
528 panelClass: ['tb-dialog', 'tb-fullscreen-dialog'], 529 panelClass: ['tb-dialog', 'tb-fullscreen-dialog'],
529 data: { 530 data: {
530 deviceId: device.id.id, 531 deviceId: device.id.id,
  532 + deviceProfileId: device.deviceProfileId.id,
531 isReadOnly: this.config.componentsData.deviceScope === 'customer_user' || this.config.componentsData.deviceScope === 'edge_customer_user' 533 isReadOnly: this.config.componentsData.deviceScope === 'customer_user' || this.config.componentsData.deviceScope === 'edge_customer_user'
532 } 534 }
533 }).afterClosed().subscribe(deviceCredentials => { 535 }).afterClosed().subscribe(deviceCredentials => {
@@ -682,6 +682,20 @@ export const credentialTypeNames = new Map<DeviceCredentialsType, string>( @@ -682,6 +682,20 @@ export const credentialTypeNames = new Map<DeviceCredentialsType, string>(
682 ] 682 ]
683 ); 683 );
684 684
  685 +export const credentialTypesByTransportType = new Map<DeviceTransportType, DeviceCredentialsType[]>(
  686 + [
  687 + [DeviceTransportType.DEFAULT, [
  688 + DeviceCredentialsType.ACCESS_TOKEN, DeviceCredentialsType.X509_CERTIFICATE, DeviceCredentialsType.MQTT_BASIC
  689 + ]],
  690 + [DeviceTransportType.MQTT, [
  691 + DeviceCredentialsType.ACCESS_TOKEN, DeviceCredentialsType.X509_CERTIFICATE, DeviceCredentialsType.MQTT_BASIC
  692 + ]],
  693 + [DeviceTransportType.COAP, [DeviceCredentialsType.ACCESS_TOKEN, DeviceCredentialsType.X509_CERTIFICATE]],
  694 + [DeviceTransportType.LWM2M, [DeviceCredentialsType.LWM2M_CREDENTIALS]],
  695 + [DeviceTransportType.SNMP, [DeviceCredentialsType.ACCESS_TOKEN]]
  696 + ]
  697 +);
  698 +
685 export interface DeviceCredentials extends BaseData<DeviceCredentialsId> { 699 export interface DeviceCredentials extends BaseData<DeviceCredentialsId> {
686 deviceId: DeviceId; 700 deviceId: DeviceId;
687 credentialsType: DeviceCredentialsType; 701 credentialsType: DeviceCredentialsType;
@@ -941,13 +941,13 @@ @@ -941,13 +941,13 @@
941 "unassign-devices-title": "Are you sure you want to unassign { count, plural, 1 {1 device} other {# devices} }?", 941 "unassign-devices-title": "Are you sure you want to unassign { count, plural, 1 {1 device} other {# devices} }?",
942 "unassign-devices-text": "After the confirmation all selected devices will be unassigned and won't be accessible by the customer.", 942 "unassign-devices-text": "After the confirmation all selected devices will be unassigned and won't be accessible by the customer.",
943 "device-credentials": "Device Credentials", 943 "device-credentials": "Device Credentials",
  944 + "loading-device-credentials": "Loading device credentials...",
944 "credentials-type": "Credentials type", 945 "credentials-type": "Credentials type",
945 "access-token": "Access token", 946 "access-token": "Access token",
946 "access-token-required": "Access token is required.", 947 "access-token-required": "Access token is required.",
947 "access-token-invalid": "Access token length must be from 1 to 20 characters.", 948 "access-token-invalid": "Access token length must be from 1 to 20 characters.",
948 "rsa-key": "RSA public key", 949 "rsa-key": "RSA public key",
949 "rsa-key-required": "RSA public key is required.", 950 "rsa-key-required": "RSA public key is required.",
950 - "lwm2m-value": "LwM2M Security config",  
951 "lwm2m-security-config": { 951 "lwm2m-security-config": {
952 "identity": "Client Identity", 952 "identity": "Client Identity",
953 "identity-required": "Client Identity is required.", 953 "identity-required": "Client Identity is required.",
@@ -971,7 +971,6 @@ @@ -971,7 +971,6 @@
971 "client-secret-key-required": "Client Secret Key is required.", 971 "client-secret-key-required": "Client Secret Key is required.",
972 "client-secret-key-pattern": "Client Secret Key must be hexadecimal format.", 972 "client-secret-key-pattern": "Client Secret Key must be hexadecimal format.",
973 "client-secret-key-length": "Client Secret Key must be {{ count }} characters.", 973 "client-secret-key-length": "Client Secret Key must be {{ count }} characters.",
974 - "config-json-tab": "Json Client Security Config",  
975 "client-public-key": "Client public key", 974 "client-public-key": "Client public key",
976 "client-public-key-hint": "If client public key is empty, the trusted certificate will be used" 975 "client-public-key-hint": "If client public key is empty, the trusted certificate will be used"
977 }, 976 },