@ -14,201 +14,83 @@
/// limitations under the License.
///
import { Component , Inject , OnDestroy , SkipSelf , ViewChild } from '@angular/core' ;
import { MAT_DIALOG_DATA , M atDialogRef } from '@angular/material/dialog' ;
import { Component , ViewChild } from '@angular/core' ;
import { MatDialogRef } from '@angular/material/dialog' ;
import { Store } from '@ngrx/store' ;
import { AppState } from '@core/core.state' ;
import { Untyped FormBuilder, UntypedFormControl , Untyped FormGroup, FormGroupDirective , NgForm , Validators } from '@angular/forms' ;
import { FormBuilder , FormGroup , Validators } from '@angular/forms' ;
import { DialogComponent } from '@shared/components/dialog.component' ;
import { Router } from '@angular/router' ;
import {
createDeviceProfileConfiguration ,
createDeviceProfileTransportConfiguration ,
DeviceProfile ,
DeviceProfileInfo ,
DeviceProfileType ,
DeviceProvisionConfiguration ,
DeviceProvisionType ,
DeviceTransportType ,
deviceTransportTypeHintMap ,
deviceTransportTypeTranslationMap
} from '@shared/models/device.models' ;
import { MatStepper } from '@angular/material/stepper' ;
import { AddEntityDialogData } from '@home/models/entity/entity-component.models' ;
import { Device , DeviceProfileInfo , DeviceTransportType } from '@shared/models/device.models' ;
import { MatStepper , StepperOrientation } from '@angular/material/stepper' ;
import { BaseData , HasId } from '@shared/models/base-data' ;
import { EntityType } from '@shared/models/entity-type.models' ;
import { DeviceProfileService } from '@core/http/device-profile.service' ;
import { EntityId } from '@shared/models/id/entity-id' ;
import { Observable , of , Subscription , throwError } from 'rxjs' ;
import { catchError , map , mergeMap , tap } from 'rxjs/operators' ;
import { Observable , throwError } from 'rxjs' ;
import { catchError , map } from 'rxjs/operators' ;
import { DeviceService } from '@core/http/device.service' ;
import { ErrorStateMatcher } from '@angular/material/core' ;
import { StepperSelectionEvent } from '@angular/cdk/stepper' ;
import { BreakpointObserver , BreakpointState } from '@angular/cdk/layout' ;
import { BreakpointObserver } from '@angular/cdk/layout' ;
import { MediaBreakpoints } from '@shared/models/constants' ;
import { RuleChainId } from '@shared/models/id/rule-chain-id' ;
import { ServiceType } from '@shared/models/queue.models' ;
import { deepTrim } from '@core/utils' ;
import { CustomerId } from '@shared/models/id/customer-id' ;
import { HttpErrorResponse } from '@angular/common/http' ;
@Component ( {
selector : 'tb-device-wizard' ,
templateUrl : './device-wizard-dialog.component.html' ,
providers : [ ] ,
styleUrls : [ './device-wizard-dialog.component.scss' ]
} )
export class DeviceWizardDialogComponent extends
DialogComponent < DeviceWizardDialogComponent , boolean > implements OnDestroy , ErrorStateMatcher {
export class DeviceWizardDialogComponent extends DialogComponent < DeviceWizardDialogComponent , boolean > {
@ViewChild ( 'addDeviceWizardStepper' , { static : true } ) addDeviceWizardStepper : MatStepper ;
selectedIndex = 0 ;
showNext = true ;
createProfile = false ;
entityType = EntityType ;
deviceTransportTypes = Object . values ( DeviceTransportType ) ;
deviceTransportTypeTranslations = deviceTransportTypeTranslationMap ;
stepperOrientation : Observable < StepperOrientation > ;
deviceTransportTypeHints = deviceTransportTypeHintMap ;
stepperLabelPosition : Observable < 'bottom' | 'end' > ;
deviceWizardFormGroup : UntypedFormGroup ;
transportConfigFormGroup : UntypedFormGroup ;
alarmRulesFormGroup : UntypedFormGroup ;
selectedIndex = 0 ;
provisionConfigFormGroup : UntypedFormGroup ;
credentialsOptionalStep = true ;
credentialsFormGroup : UntypedFormGroup ;
showNext = true ;
customerFormGroup : UntypedFormGroup ;
entityType = EntityType ;
labelPosition : MatStepper [ 'labelPosition' ] = 'end' ;
deviceWizardFormGroup : FormGroup ;
serviceType = ServiceType . TB_RULE_ENGINE ;
credentialsFormGroup : FormGroup ;
private subscriptions : Subscription [ ] = [ ] ;
private currentDeviceProfileTransportType = DeviceTransportType . DEFAULT ;
constructor ( protected store : Store < AppState > ,
protected router : Router ,
@Inject ( MAT_DIALOG_DATA ) public data : AddEntityDialogData < BaseData < EntityId > > ,
@SkipSelf ( ) private errorStateMatcher : ErrorStateMatcher ,
public dialogRef : MatDialogRef < DeviceWizardDialogComponent , boolean > ,
private deviceProfileService : DeviceProfileService ,
private deviceService : DeviceService ,
private breakpointObserver : BreakpointObserver ,
private fb : Untyped FormBuilder) {
private fb : FormBuilder ) {
super ( store , router , dialogRef ) ;
this . stepperOrientation = this . breakpointObserver . observe ( MediaBreakpoints [ 'gt-sm' ] )
. pipe ( map ( ( { matches } ) = > matches ? 'horizontal' : 'vertical' ) ) ;
this . stepperLabelPosition = this . breakpointObserver . observe ( MediaBreakpoints [ 'gt-sm' ] )
. pipe ( map ( ( { matches } ) = > matches ? 'end' : 'bottom' ) ) ;
this . deviceWizardFormGroup = this . fb . group ( {
name : [ '' , [ Validators . required , Validators . maxLength ( 255 ) ] ] ,
label : [ '' , Validators . maxLength ( 255 ) ] ,
gateway : [ false ] ,
overwriteActivityTime : [ false ] ,
addProfileType : [ 0 ] ,
customerId : [ null ] ,
deviceProfileId : [ null , Validators . required ] ,
newDeviceProfileTitle : [ { value : null , disabled : true } ] ,
defaultRuleChainId : [ { value : null , disabled : true } ] ,
defaultQueueName : [ { value : null , disabled : true } ] ,
description : [ '' ]
}
) ;
this . subscriptions . push ( this . deviceWizardFormGroup . get ( 'addProfileType' ) . valueChanges . subscribe (
( addProfileType : number ) = > {
if ( addProfileType === 0 ) {
this . deviceWizardFormGroup . get ( 'deviceProfileId' ) . setValidators ( [ Validators . required ] ) ;
this . deviceWizardFormGroup . get ( 'deviceProfileId' ) . enable ( ) ;
this . deviceWizardFormGroup . get ( 'newDeviceProfileTitle' ) . setValidators ( null ) ;
this . deviceWizardFormGroup . get ( 'newDeviceProfileTitle' ) . disable ( ) ;
this . deviceWizardFormGroup . get ( 'defaultRuleChainId' ) . disable ( ) ;
this . deviceWizardFormGroup . get ( 'defaultQueueName' ) . disable ( ) ;
this . deviceWizardFormGroup . updateValueAndValidity ( ) ;
this . createProfile = false ;
} else {
this . deviceWizardFormGroup . get ( 'deviceProfileId' ) . setValidators ( null ) ;
this . deviceWizardFormGroup . get ( 'deviceProfileId' ) . disable ( ) ;
this . deviceWizardFormGroup . get ( 'newDeviceProfileTitle' ) . setValidators ( [ Validators . required ] ) ;
this . deviceWizardFormGroup . get ( 'newDeviceProfileTitle' ) . enable ( ) ;
this . deviceWizardFormGroup . get ( 'defaultRuleChainId' ) . enable ( ) ;
this . deviceWizardFormGroup . get ( 'defaultQueueName' ) . enable ( ) ;
this . deviceWizardFormGroup . updateValueAndValidity ( ) ;
this . createProfile = true ;
}
}
) ) ;
this . transportConfigFormGroup = this . fb . group (
{
transportType : [ DeviceTransportType . DEFAULT , Validators . required ] ,
transportConfiguration : [ createDeviceProfileTransportConfiguration ( DeviceTransportType . DEFAULT ) , Validators . required ]
}
) ;
this . subscriptions . push ( this . transportConfigFormGroup . get ( 'transportType' ) . valueChanges . subscribe ( ( transportType ) = > {
this . deviceProfileTransportTypeChanged ( transportType ) ;
} ) ) ;
this . alarmRulesFormGroup = this . fb . group ( {
alarms : [ null ]
}
) ;
this . provisionConfigFormGroup = this . fb . group (
{
provisionConfiguration : [ {
type : DeviceProvisionType . DISABLED
} as DeviceProvisionConfiguration , [ Validators . required ] ]
}
) ;
this . credentialsFormGroup = this . fb . group ( {
setCredential : [ false ] ,
credential : [ { value : null , disabled : true } ]
}
) ;
this . subscriptions . push ( this . credentialsFormGroup . get ( 'setCredential' ) . valueChanges . subscribe ( ( value ) = > {
if ( value ) {
this . credentialsFormGroup . get ( 'credential' ) . enable ( ) ;
} else {
this . credentialsFormGroup . get ( 'credential' ) . disable ( ) ;
}
} ) ) ;
this . customerFormGroup = this . fb . group ( {
customerId : [ null ]
credential : [ ]
}
) ;
this . labelPosition = this . breakpointObserver . isMatched ( MediaBreakpoints [ 'gt-sm' ] ) ? 'end' : 'bottom' ;
this . subscriptions . push ( this . breakpointObserver
. observe ( MediaBreakpoints [ 'gt-sm' ] )
. subscribe ( ( state : BreakpointState ) = > {
if ( state . matches ) {
this . labelPosition = 'end' ;
} else {
this . labelPosition = 'bottom' ;
}
}
) ) ;
}
ngOnDestroy() {
super . ngOnDestroy ( ) ;
this . subscriptions . forEach ( s = > s . unsubscribe ( ) ) ;
}
isErrorState ( control : UntypedFormControl | null , form : FormGroupDirective | NgForm | null ) : boolean {
const originalErrorState = this . errorStateMatcher . isErrorState ( control , form ) ;
const customErrorState = ! ! ( control && control . invalid ) ;
return originalErrorState || customErrorState ;
}
cancel ( ) : void {
@ -224,24 +106,11 @@ export class DeviceWizardDialogComponent extends
}
getFormLabel ( index : number ) : string {
if ( index > 0 ) {
if ( ! this . createProfile ) {
index += 3 ;
}
}
switch ( index ) {
case 0 :
return 'device.wizard.device-details' ;
case 1 :
return 'device-profile.transport-configuration' ;
case 2 :
return 'device-profile.alarm-rules' ;
case 3 :
return 'device-profile.device-provisioning' ;
case 4 :
return 'device.credentials' ;
case 5 :
return 'customer.customer' ;
}
}
@ -249,88 +118,30 @@ export class DeviceWizardDialogComponent extends
return this . addDeviceWizardStepper ? . _steps ? . length - 1 ;
}
private deviceProfileTransportTypeChanged ( deviceTransportType : DeviceTransportType ) : void {
this . transportConfigFormGroup . patchValue (
{ transportConfiguration : createDeviceProfileTransportConfiguration ( deviceTransportType ) } ) ;
const setCredentialBox = this . credentialsFormGroup . get ( 'setCredential' ) ;
if ( deviceTransportType === DeviceTransportType . LWM2M ) {
setCredentialBox . patchValue ( true ) ;
setCredentialBox . disable ( ) ;
} else {
setCredentialBox . patchValue ( false ) ;
setCredentialBox . enable ( ) ;
}
}
add ( ) : void {
if ( this . allValid ( ) ) {
this . createDeviceProfile ( ) . pipe (
mergeMap ( profileId = > this . createDevice ( profileId ) ) ,
mergeMap ( device = > this . saveCredentials ( device ) )
) . subscribe (
( created ) = > {
this . dialogRef . close ( created ) ;
}
this . createDevice ( ) . subscribe (
( ) = > this . dialogRef . close ( true )
) ;
}
}
get deviceTransportType ( ) : DeviceTransportType {
if ( this . deviceWizardFormGroup . get ( 'addProfileType' ) . value ) {
return this . transportConfigFormGroup . get ( 'transportType' ) . value ;
} else {
return this . currentDeviceProfileTransportType ;
}
return this . currentDeviceProfileTransportType ;
}
deviceProfileChanged ( deviceProfile : DeviceProfileInfo ) {
if ( deviceProfile ) {
this . currentDeviceProfileTransportType = deviceProfile . transportType ;
this . credentialsOptionalStep = this . currentDeviceProfileTransportType !== DeviceTransportType . LWM2M ;
}
}
private createDeviceProfile ( ) : Observable < EntityId > {
if ( this . deviceWizardFormGroup . get ( 'addProfileType' ) . value ) {
const deviceProvisionConfiguration : DeviceProvisionConfiguration = this . provisionConfigFormGroup . get ( 'provisionConfiguration' ) . value ;
const provisionDeviceKey = deviceProvisionConfiguration . provisionDeviceKey ;
delete deviceProvisionConfiguration . provisionDeviceKey ;
const deviceProfile : DeviceProfile = {
name : this.deviceWizardFormGroup.get ( 'newDeviceProfileTitle' ) . value ,
type : DeviceProfileType . DEFAULT ,
defaultQueueName : this.deviceWizardFormGroup.get ( 'defaultQueueName' ) . value ,
transportType : this.transportConfigFormGroup.get ( 'transportType' ) . value ,
provisionType : deviceProvisionConfiguration.type ,
provisionDeviceKey ,
profileData : {
configuration : createDeviceProfileConfiguration ( DeviceProfileType . DEFAULT ) ,
transportConfiguration : this.transportConfigFormGroup.get ( 'transportConfiguration' ) . value ,
alarms : this.alarmRulesFormGroup.get ( 'alarms' ) . value ,
provisionConfiguration : deviceProvisionConfiguration
}
} ;
if ( this . deviceWizardFormGroup . get ( 'defaultRuleChainId' ) . value ) {
deviceProfile . defaultRuleChainId = new RuleChainId ( this . deviceWizardFormGroup . get ( 'defaultRuleChainId' ) . value ) ;
}
return this . deviceProfileService . saveDeviceProfile ( deepTrim ( deviceProfile ) ) . pipe (
tap ( ( profile ) = > {
this . currentDeviceProfileTransportType = profile . transportType ;
this . deviceWizardFormGroup . patchValue ( {
deviceProfileId : profile.id ,
addProfileType : 0
} ) ;
} ) ,
map ( profile = > profile . id )
) ;
} else {
return of ( this . deviceWizardFormGroup . get ( 'deviceProfileId' ) . value ) ;
}
}
private createDevice ( profileId ) : Observable < BaseData < HasId > > {
const device = {
private createDevice ( ) : Observable < BaseData < HasId > > {
const device : Device = {
name : this.deviceWizardFormGroup.get ( 'name' ) . value ,
label : this.deviceWizardFormGroup.get ( 'label' ) . value ,
deviceProfileId : profileId ,
deviceProfileId : this.deviceWizardFormGroup.get ( 'deviceProfileId' ) . value ,
additionalInfo : {
gateway : this.deviceWizardFormGroup.get ( 'gateway' ) . value ,
overwriteActivityTime : this.deviceWizardFormGroup.get ( 'overwriteActivityTime' ) . value ,
@ -338,13 +149,22 @@ export class DeviceWizardDialogComponent extends
} ,
customerId : null
} ;
if ( this . customerFormGroup . get ( 'customerId' ) . value ) {
device . customerId = {
entityType : EntityType.CUSTOMER ,
id : this.customerFormGroup.get ( 'customerId' ) . value
} ;
if ( this . deviceWizardFormGroup . get ( 'customerId' ) . value ) {
device . customerId = new CustomerId ( this . deviceWizardFormGroup . get ( 'customerId' ) . value ) ;
}
if ( this . addDeviceWizardStepper . steps . last . completed || this . addDeviceWizardStepper . selectedIndex > 0 ) {
return this . deviceService . saveDeviceWithCredentials ( deepTrim ( device ) , deepTrim ( this . credentialsFormGroup . value . credential ) ) . pipe (
catchError ( ( e : HttpErrorResponse ) = > {
if ( e . error . message . include ( 'Device credentials' ) ) {
this . addDeviceWizardStepper . selectedIndex = 1 ;
} else {
this . addDeviceWizardStepper . selectedIndex = 0 ;
}
return throwError ( ( ) = > e ) ;
} )
) ;
}
return this . data . entitiesTableConfig . saveEntity ( deepTrim ( device ) ) . pipe (
return this . deviceService . saveDevice ( deepTrim ( device ) ) . pipe (
catchError ( e = > {
this . addDeviceWizardStepper . selectedIndex = 0 ;
return throwError ( e ) ;
@ -352,31 +172,8 @@ export class DeviceWizardDialogComponent extends
) ;
}
private saveCredentials ( device : BaseData < HasId > ) : Observable < boolean > {
if ( this . credentialsFormGroup . get ( 'setCredential' ) . value ) {
return this . deviceService . getDeviceCredentials ( device . id . id ) . pipe (
mergeMap (
( deviceCredentials ) = > {
const deviceCredentialsValue = { . . . deviceCredentials , . . . this . credentialsFormGroup . value . credential } ;
return this . deviceService . saveDeviceCredentials ( deviceCredentialsValue ) . pipe (
catchError ( e = > {
this . addDeviceWizardStepper . selectedIndex = 1 ;
return this . deviceService . deleteDevice ( device . id . id ) . pipe (
mergeMap ( ( ) = > {
return throwError ( e ) ;
}
) ) ;
} )
) ;
}
) ,
map ( ( ) = > true ) ) ;
}
return of ( true ) ;
}
allValid ( ) : boolean {
if ( this . addDeviceWizardStepper . steps . find ( ( item , index ) = > {
return ! this . addDeviceWizardStepper . steps . find ( ( item , index ) = > {
if ( item . stepControl . invalid ) {
item . interacted = true ;
this . addDeviceWizardStepper . selectedIndex = index ;
@ -384,19 +181,11 @@ export class DeviceWizardDialogComponent extends
} else {
return false ;
}
} ) ) {
return false ;
} else {
return true ;
}
} ) ;
}
changeStep ( $event : StepperSelectionEvent ) : void {
this . selectedIndex = $event . selectedIndex ;
if ( this . selectedIndex === this . maxStepperIndex ) {
this . showNext = false ;
} else {
this . showNext = true ;
}
this . showNext = this . selectedIndex !== this . maxStepperIndex ;
}
}