UI: Added device provision to device profile
This commit is contained in:
parent
2b1317b10f
commit
e8d8382c74
@ -107,6 +107,7 @@ import { AlarmRuleKeyFiltersDialogComponent } from './profile/alarm/alarm-rule-k
|
||||
import { FilterTextComponent } from './filter/filter-text.component';
|
||||
import { AddDeviceProfileDialogComponent } from './profile/add-device-profile-dialog.component';
|
||||
import { RuleChainAutocompleteComponent } from './rule-chain/rule-chain-autocomplete.component';
|
||||
import { DeviceProfileProvisionConfigurationComponent } from "./profile/device-profile-provision-configuration.component";
|
||||
|
||||
@NgModule({
|
||||
declarations:
|
||||
@ -196,7 +197,8 @@ import { RuleChainAutocompleteComponent } from './rule-chain/rule-chain-autocomp
|
||||
DeviceProfileComponent,
|
||||
DeviceProfileDialogComponent,
|
||||
AddDeviceProfileDialogComponent,
|
||||
RuleChainAutocompleteComponent
|
||||
RuleChainAutocompleteComponent,
|
||||
DeviceProfileProvisionConfigurationComponent
|
||||
],
|
||||
imports: [
|
||||
CommonModule,
|
||||
@ -275,7 +277,8 @@ import { RuleChainAutocompleteComponent } from './rule-chain/rule-chain-autocomp
|
||||
DeviceProfileComponent,
|
||||
DeviceProfileDialogComponent,
|
||||
AddDeviceProfileDialogComponent,
|
||||
RuleChainAutocompleteComponent
|
||||
RuleChainAutocompleteComponent,
|
||||
DeviceProfileProvisionConfigurationComponent
|
||||
],
|
||||
providers: [
|
||||
WidgetComponentService,
|
||||
|
||||
@ -93,6 +93,14 @@
|
||||
</tb-device-profile-alarms>
|
||||
</form>
|
||||
</mat-step>
|
||||
<mat-step [stepControl]="provisionConfigurationFormGroup">
|
||||
<form [formGroup]="provisionConfigurationFormGroup" style="padding-bottom: 16px;">
|
||||
<ng-template matStepLabel>{{'device-profile.device-provisioning' | translate }}</ng-template>
|
||||
<tb-device-profile-provision-configuration
|
||||
formControlName="provisionConfiguration">
|
||||
</tb-device-profile-provision-configuration>
|
||||
</form>
|
||||
</mat-step>
|
||||
</mat-horizontal-stepper>
|
||||
</div>
|
||||
<div mat-dialog-actions fxLayout="row wrap" fxLayoutAlign="space-between center">
|
||||
@ -107,7 +115,7 @@
|
||||
<button mat-raised-button
|
||||
[disabled]="(isLoading$ | async) || selectedForm().invalid"
|
||||
color="primary"
|
||||
(click)="nextStep()">{{ (selectedIndex === 2 ? 'action.add' : 'action.continue') | translate }}</button>
|
||||
(click)="nextStep()">{{ (selectedIndex === 3 ? 'action.add' : 'action.continue') | translate }}</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -77,6 +77,8 @@ export class AddDeviceProfileDialogComponent extends
|
||||
|
||||
alarmRulesFormGroup: FormGroup;
|
||||
|
||||
provisionConfigurationFormGroup: FormGroup;
|
||||
|
||||
constructor(protected store: Store<AppState>,
|
||||
protected router: Router,
|
||||
@Inject(MAT_DIALOG_DATA) public data: AddDeviceProfileDialogData,
|
||||
@ -111,6 +113,12 @@ export class AddDeviceProfileDialogComponent extends
|
||||
alarms: [null]
|
||||
}
|
||||
);
|
||||
|
||||
this.provisionConfigurationFormGroup = this.fb.group(
|
||||
{
|
||||
provisionConfiguration: [null]
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
private deviceProfileTransportTypeChanged() {
|
||||
@ -131,7 +139,7 @@ export class AddDeviceProfileDialogComponent extends
|
||||
}
|
||||
|
||||
nextStep() {
|
||||
if (this.selectedIndex < 2) {
|
||||
if (this.selectedIndex < 3) {
|
||||
this.addDeviceProfileStepper.next();
|
||||
} else {
|
||||
this.add();
|
||||
@ -146,6 +154,8 @@ export class AddDeviceProfileDialogComponent extends
|
||||
return this.transportConfigFormGroup;
|
||||
case 2:
|
||||
return this.alarmRulesFormGroup;
|
||||
case 3:
|
||||
return this.provisionConfigurationFormGroup;
|
||||
}
|
||||
}
|
||||
|
||||
@ -154,11 +164,17 @@ export class AddDeviceProfileDialogComponent extends
|
||||
name: this.deviceProfileDetailsFormGroup.get('name').value,
|
||||
type: this.deviceProfileDetailsFormGroup.get('type').value,
|
||||
transportType: this.transportConfigFormGroup.get('transportType').value,
|
||||
provisionType: this.provisionConfigurationFormGroup.get('provisionConfiguration').value.type,
|
||||
provisionDeviceKey: this.provisionConfigurationFormGroup.get('provisionConfiguration').value.provisionDeviceKey,
|
||||
description: this.deviceProfileDetailsFormGroup.get('description').value,
|
||||
profileData: {
|
||||
configuration: createDeviceProfileConfiguration(DeviceProfileType.DEFAULT),
|
||||
transportConfiguration: this.transportConfigFormGroup.get('transportConfiguration').value,
|
||||
alarms: this.alarmRulesFormGroup.get('alarms').value
|
||||
alarms: this.alarmRulesFormGroup.get('alarms').value,
|
||||
provisionConfiguration: {
|
||||
type: this.provisionConfigurationFormGroup.get('provisionConfiguration').value.type,
|
||||
provisionDeviceSecret: this.provisionConfigurationFormGroup.get('provisionConfiguration').value.provisionDeviceSecret
|
||||
}
|
||||
}
|
||||
};
|
||||
if (this.deviceProfileDetailsFormGroup.get('defaultRuleChainId').value) {
|
||||
|
||||
@ -51,5 +51,15 @@
|
||||
formControlName="alarms">
|
||||
</tb-device-profile-alarms>
|
||||
</mat-expansion-panel>
|
||||
<mat-expansion-panel [expanded]="true">
|
||||
<mat-expansion-panel-header>
|
||||
<mat-panel-title>
|
||||
<div translate>device-profile.device-provisioning</div>
|
||||
</mat-panel-title>
|
||||
</mat-expansion-panel-header>
|
||||
<tb-device-profile-provision-configuration
|
||||
formControlName="provisionConfiguration">
|
||||
</tb-device-profile-provision-configuration>
|
||||
</mat-expansion-panel>
|
||||
</mat-accordion>
|
||||
</div>
|
||||
|
||||
@ -72,7 +72,8 @@ export class DeviceProfileDataComponent implements ControlValueAccessor, OnInit
|
||||
this.deviceProfileDataFormGroup = this.fb.group({
|
||||
configuration: [null, Validators.required],
|
||||
transportConfiguration: [null, Validators.required],
|
||||
alarms: [null]
|
||||
alarms: [null],
|
||||
provisionConfiguration: [null]
|
||||
});
|
||||
this.deviceProfileDataFormGroup.valueChanges.subscribe(() => {
|
||||
this.updateModel();
|
||||
@ -98,6 +99,7 @@ export class DeviceProfileDataComponent implements ControlValueAccessor, OnInit
|
||||
this.deviceProfileDataFormGroup.patchValue({configuration: value?.configuration}, {emitEvent: false});
|
||||
this.deviceProfileDataFormGroup.patchValue({transportConfiguration: value?.transportConfiguration}, {emitEvent: false});
|
||||
this.deviceProfileDataFormGroup.patchValue({alarms: value?.alarms}, {emitEvent: false});
|
||||
this.deviceProfileDataFormGroup.patchValue({provisionConfiguration: value?.provisionConfiguration}, {emitEvent: false});
|
||||
}
|
||||
|
||||
private updateModel() {
|
||||
|
||||
@ -0,0 +1,46 @@
|
||||
<!--
|
||||
|
||||
Copyright © 2016-2020 The Thingsboard Authors
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
-->
|
||||
<div [formGroup]="provisionConfigurationFormGroup">
|
||||
<mat-form-field class="mat-block">
|
||||
<mat-label translate>device-profile.provision-strategy</mat-label>
|
||||
<mat-select formControlName="type" required>
|
||||
<mat-option *ngFor="let type of deviceProvisionTypes" [value]="type">
|
||||
{{deviceProvisionTypeTranslateMap.get(type) | translate}}
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
<mat-error *ngIf="provisionConfigurationFormGroup.get('type').hasError('required')">
|
||||
{{ 'device-profile.provision-strategy-required' | translate }}
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
<section *ngIf="provisionConfigurationFormGroup.get('type').value !== deviceProvisionType.DISABLED" fxLayoutGap.gt-xs="8px" fxLayout="row" fxLayout.xs="column">
|
||||
<mat-form-field fxFlex class="mat-block">
|
||||
<mat-label translate>device-profile.provision-device-secret</mat-label>
|
||||
<input matInput formControlName="provisionDeviceSecret" required/>
|
||||
<mat-error *ngIf="provisionConfigurationFormGroup.get('provisionDeviceSecret').hasError('required')">
|
||||
{{ 'device-profile.provision-device-secret-required' | translate }}
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
<mat-form-field fxFlex class="mat-block">
|
||||
<mat-label translate>device-profile.provision-device-key</mat-label>
|
||||
<input matInput formControlName="provisionDeviceKey" required/>
|
||||
<mat-error *ngIf="provisionConfigurationFormGroup.get('provisionDeviceKey').hasError('required')">
|
||||
{{ 'device-profile.provision-device-key-required' | translate }}
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
</section>
|
||||
</div>
|
||||
@ -0,0 +1,140 @@
|
||||
///
|
||||
/// Copyright © 2016-2020 The Thingsboard Authors
|
||||
///
|
||||
/// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
/// you may not use this file except in compliance with the License.
|
||||
/// You may obtain a copy of the License at
|
||||
///
|
||||
/// http://www.apache.org/licenses/LICENSE-2.0
|
||||
///
|
||||
/// Unless required by applicable law or agreed to in writing, software
|
||||
/// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
/// See the License for the specific language governing permissions and
|
||||
/// limitations under the License.
|
||||
///
|
||||
|
||||
import { Component, forwardRef, Input, OnInit } from "@angular/core";
|
||||
import {
|
||||
ControlValueAccessor,
|
||||
FormBuilder,
|
||||
FormControl,
|
||||
FormGroup,
|
||||
NG_VALIDATORS,
|
||||
NG_VALUE_ACCESSOR,
|
||||
ValidationErrors,
|
||||
Validator,
|
||||
Validators
|
||||
} from "@angular/forms";
|
||||
import { coerceBooleanProperty } from "@angular/cdk/coercion";
|
||||
import {
|
||||
DeviceProvisionConfiguration,
|
||||
DeviceProvisionType,
|
||||
deviceProvisionTypeTranslationMap
|
||||
} from "@shared/models/device.models";
|
||||
import { isDefinedAndNotNull } from "@core/utils";
|
||||
|
||||
@Component({
|
||||
selector: 'tb-device-profile-provision-configuration',
|
||||
templateUrl: './device-profile-provision-configuration.component.html',
|
||||
styleUrls: [],
|
||||
providers: [
|
||||
{
|
||||
provide: NG_VALUE_ACCESSOR,
|
||||
useExisting: forwardRef(() => DeviceProfileProvisionConfigurationComponent),
|
||||
multi: true
|
||||
},
|
||||
{
|
||||
provide: NG_VALIDATORS,
|
||||
useExisting: forwardRef(() => DeviceProfileProvisionConfigurationComponent),
|
||||
multi: true,
|
||||
}
|
||||
]
|
||||
})
|
||||
export class DeviceProfileProvisionConfigurationComponent implements ControlValueAccessor, OnInit, Validator {
|
||||
|
||||
provisionConfigurationFormGroup: FormGroup;
|
||||
|
||||
deviceProvisionType = DeviceProvisionType;
|
||||
deviceProvisionTypes = Object.keys(DeviceProvisionType);
|
||||
deviceProvisionTypeTranslateMap = deviceProvisionTypeTranslationMap;
|
||||
|
||||
private requiredValue: boolean;
|
||||
get required(): boolean {
|
||||
return this.requiredValue;
|
||||
}
|
||||
@Input()
|
||||
set required(value: boolean) {
|
||||
this.requiredValue = coerceBooleanProperty(value);
|
||||
}
|
||||
|
||||
@Input()
|
||||
disabled: boolean;
|
||||
|
||||
private propagateChange = (v: any) => { };
|
||||
|
||||
constructor(private fb: FormBuilder) {
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.provisionConfigurationFormGroup = this.fb.group({
|
||||
type: [DeviceProvisionType.DISABLED, Validators.required],
|
||||
provisionDeviceSecret: [{value: null, disabled: true}, Validators.required],
|
||||
provisionDeviceKey: [{value: null, disabled: true}, Validators.required]
|
||||
});
|
||||
this.provisionConfigurationFormGroup.get('type').valueChanges.subscribe((type) => {
|
||||
if(type === DeviceProvisionType.DISABLED) {
|
||||
this.provisionConfigurationFormGroup.get('provisionDeviceSecret').disable({emitEvent: false});
|
||||
this.provisionConfigurationFormGroup.get('provisionDeviceSecret').patchValue(null,{emitEvent: false});
|
||||
this.provisionConfigurationFormGroup.get('provisionDeviceKey').disable({emitEvent: false});
|
||||
this.provisionConfigurationFormGroup.get('provisionDeviceKey').patchValue(null);
|
||||
} else {
|
||||
this.provisionConfigurationFormGroup.get('provisionDeviceSecret').enable({emitEvent: false});
|
||||
this.provisionConfigurationFormGroup.get('provisionDeviceKey').enable({emitEvent: false});
|
||||
}
|
||||
});
|
||||
this.provisionConfigurationFormGroup.valueChanges.subscribe(() => {
|
||||
this.updateModel();
|
||||
});
|
||||
}
|
||||
|
||||
registerOnChange(fn: any): void {
|
||||
this.propagateChange = fn;
|
||||
}
|
||||
|
||||
registerOnTouched(fn: any): void {
|
||||
}
|
||||
|
||||
writeValue(value: DeviceProvisionConfiguration | null): void {
|
||||
if(isDefinedAndNotNull(value)){
|
||||
this.provisionConfigurationFormGroup.patchValue(value, {emitEvent: false});
|
||||
} else {
|
||||
this.provisionConfigurationFormGroup.patchValue({type: DeviceProvisionType.DISABLED});
|
||||
}
|
||||
}
|
||||
|
||||
setDisabledState(isDisabled: boolean){
|
||||
this.disabled = isDisabled;
|
||||
if(this.disabled){
|
||||
this.provisionConfigurationFormGroup.disable();
|
||||
} else {
|
||||
this.provisionConfigurationFormGroup.enable({emitEvent: false});
|
||||
}
|
||||
}
|
||||
|
||||
validate(c: FormControl): ValidationErrors | null {
|
||||
return (this.provisionConfigurationFormGroup.valid) ? null : {
|
||||
provisionConfiguration: {
|
||||
valid: false,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
private updateModel(): void {
|
||||
let deviceProvisionConfiguration: DeviceProvisionConfiguration = null;
|
||||
if (this.provisionConfigurationFormGroup.valid) {
|
||||
deviceProvisionConfiguration = this.provisionConfigurationFormGroup.getRawValue();
|
||||
}
|
||||
this.propagateChange(deviceProvisionConfiguration);
|
||||
}
|
||||
}
|
||||
@ -126,6 +126,10 @@ export class DeviceProfileComponent extends EntityComponent<DeviceProfile> {
|
||||
}
|
||||
|
||||
updateForm(entity: DeviceProfile) {
|
||||
if(entity?.profileData?.provisionConfiguration) {
|
||||
entity.profileData.provisionConfiguration.provisionDeviceKey = entity?.provisionDeviceKey;
|
||||
}
|
||||
|
||||
this.entityForm.patchValue({name: entity.name});
|
||||
this.entityForm.patchValue({type: entity.type}, {emitEvent: false});
|
||||
this.entityForm.patchValue({transportType: entity.transportType}, {emitEvent: false});
|
||||
@ -138,7 +142,11 @@ export class DeviceProfileComponent extends EntityComponent<DeviceProfile> {
|
||||
if (formValue.defaultRuleChainId) {
|
||||
formValue.defaultRuleChainId = new RuleChainId(formValue.defaultRuleChainId);
|
||||
}
|
||||
return formValue;
|
||||
formValue.provisionType = formValue.profileData.provisionConfiguration.type;
|
||||
formValue.provisionDeviceKey = formValue.profileData.provisionConfiguration.provisionDeviceKey;
|
||||
delete formValue.profileData.provisionConfiguration.provisionDeviceKey;
|
||||
|
||||
return super.prepareFormValue(formValue);
|
||||
}
|
||||
|
||||
onDeviceProfileIdCopied(event) {
|
||||
|
||||
@ -36,6 +36,12 @@ export enum DeviceTransportType {
|
||||
LWM2M = 'LWM2M'
|
||||
}
|
||||
|
||||
export enum DeviceProvisionType {
|
||||
DISABLED = 'DISABLED',
|
||||
ALLOW_CREATE_NEW_DEVICES = 'ALLOW_CREATE_NEW_DEVICES',
|
||||
CHECK_PRE_PROVISIONED_DEVICES = 'CHECK_PRE_PROVISIONED_DEVICES'
|
||||
}
|
||||
|
||||
export interface DeviceConfigurationFormInfo {
|
||||
hasProfileConfiguration: boolean;
|
||||
hasDeviceConfiguration: boolean;
|
||||
@ -67,6 +73,15 @@ export const deviceTransportTypeTranslationMap = new Map<DeviceTransportType, st
|
||||
]
|
||||
);
|
||||
|
||||
|
||||
export const deviceProvisionTypeTranslationMap = new Map<DeviceProvisionType, string>(
|
||||
[
|
||||
[DeviceProvisionType.DISABLED, 'device-profile.provision-strategy-disabled'],
|
||||
[DeviceProvisionType.ALLOW_CREATE_NEW_DEVICES, 'device-profile.provision-strategy-created-new'],
|
||||
[DeviceProvisionType.CHECK_PRE_PROVISIONED_DEVICES, 'device-profile.provision-strategy-check-pre-provisioned']
|
||||
]
|
||||
)
|
||||
|
||||
export const deviceTransportTypeConfigurationInfoMap = new Map<DeviceTransportType, DeviceConfigurationFormInfo>(
|
||||
[
|
||||
[
|
||||
@ -125,6 +140,12 @@ export interface DeviceProfileTransportConfiguration extends DeviceProfileTransp
|
||||
type: DeviceTransportType;
|
||||
}
|
||||
|
||||
export interface DeviceProvisionConfiguration {
|
||||
type: DeviceProvisionType;
|
||||
provisionDeviceSecret?: string;
|
||||
provisionDeviceKey?: string;
|
||||
}
|
||||
|
||||
export function createDeviceProfileConfiguration(type: DeviceProfileType): DeviceProfileConfiguration {
|
||||
let configuration: DeviceProfileConfiguration = null;
|
||||
if (type) {
|
||||
@ -220,6 +241,7 @@ export interface DeviceProfileData {
|
||||
configuration: DeviceProfileConfiguration;
|
||||
transportConfiguration: DeviceProfileTransportConfiguration;
|
||||
alarms?: Array<DeviceProfileAlarm>;
|
||||
provisionConfiguration?: DeviceProvisionConfiguration;
|
||||
}
|
||||
|
||||
export interface DeviceProfile extends BaseData<DeviceProfileId> {
|
||||
@ -229,6 +251,8 @@ export interface DeviceProfile extends BaseData<DeviceProfileId> {
|
||||
default?: boolean;
|
||||
type: DeviceProfileType;
|
||||
transportType: DeviceTransportType;
|
||||
provisionType: DeviceProvisionType;
|
||||
provisionDeviceKey?: string;
|
||||
defaultRuleChainId?: RuleChainId;
|
||||
profileData: DeviceProfileData;
|
||||
}
|
||||
|
||||
@ -840,7 +840,17 @@
|
||||
"alarm-details": "Alarm details",
|
||||
"alarm-rule-condition": "Alarm rule condition",
|
||||
"enter-alarm-rule-condition-prompt": "Please add alarm rule condition",
|
||||
"edit-alarm-rule-condition": "Edit alarm rule condition"
|
||||
"edit-alarm-rule-condition": "Edit alarm rule condition",
|
||||
"device-provisioning": "Device provisioning",
|
||||
"provision-strategy": "Provision strategy",
|
||||
"provision-strategy-required": "Provision strategy is required.",
|
||||
"provision-strategy-disabled": "Disabled",
|
||||
"provision-strategy-created-new": "Allow create new devices",
|
||||
"provision-strategy-check-pre-provisioned": "Check pre provisioned devices",
|
||||
"provision-device-key": "Provision device key",
|
||||
"provision-device-key-required": "Provision device key is required.",
|
||||
"provision-device-secret": "Provision device secret",
|
||||
"provision-device-secret-required": "Provision device secret is required."
|
||||
},
|
||||
"dialog": {
|
||||
"close": "Close dialog"
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user