20 changed files with 1046 additions and 25 deletions
@ -0,0 +1,66 @@ |
|||
///
|
|||
/// 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 { Injectable } from '@angular/core'; |
|||
import { HttpClient } from '@angular/common/http'; |
|||
import { PageLink } from '@shared/models/page/page-link'; |
|||
import { defaultHttpOptionsFromConfig, RequestConfig } from './http-utils'; |
|||
import { Observable } from 'rxjs'; |
|||
import { PageData } from '@shared/models/page/page-data'; |
|||
import { DeviceProfile, DeviceProfileInfo } from '@shared/models/device.models'; |
|||
|
|||
@Injectable({ |
|||
providedIn: 'root' |
|||
}) |
|||
export class DeviceProfileService { |
|||
|
|||
constructor( |
|||
private http: HttpClient |
|||
) { } |
|||
|
|||
public getDeviceProfiles(pageLink: PageLink, config?: RequestConfig): Observable<PageData<DeviceProfile>> { |
|||
return this.http.get<PageData<DeviceProfile>>(`/api/deviceProfiles${pageLink.toQuery()}`, defaultHttpOptionsFromConfig(config)); |
|||
} |
|||
|
|||
public getDeviceProfile(deviceProfileId: string, config?: RequestConfig): Observable<DeviceProfile> { |
|||
return this.http.get<DeviceProfile>(`/api/deviceProfile/${deviceProfileId}`, defaultHttpOptionsFromConfig(config)); |
|||
} |
|||
|
|||
public saveDeviceProfile(deviceProfile: DeviceProfile, config?: RequestConfig): Observable<DeviceProfile> { |
|||
return this.http.post<DeviceProfile>('/api/deviceProfile', deviceProfile, defaultHttpOptionsFromConfig(config)); |
|||
} |
|||
|
|||
public deleteDeviceProfile(deviceProfileId: string, config?: RequestConfig) { |
|||
return this.http.delete(`/api/deviceProfile/${deviceProfileId}`, defaultHttpOptionsFromConfig(config)); |
|||
} |
|||
|
|||
public setDefaultDeviceProfile(deviceProfileId: string, config?: RequestConfig): Observable<DeviceProfile> { |
|||
return this.http.post<DeviceProfile>(`/api/deviceProfile/${deviceProfileId}/default`, defaultHttpOptionsFromConfig(config)); |
|||
} |
|||
|
|||
public getDefaultDeviceProfileInfo(config?: RequestConfig): Observable<DeviceProfileInfo> { |
|||
return this.http.get<DeviceProfileInfo>('/api/deviceProfileInfo/default', defaultHttpOptionsFromConfig(config)); |
|||
} |
|||
|
|||
public getDeviceProfileInfo(deviceProfileId: string, config?: RequestConfig): Observable<DeviceProfileInfo> { |
|||
return this.http.get<DeviceProfileInfo>(`/api/deviceProfileInfo/${deviceProfileId}`, defaultHttpOptionsFromConfig(config)); |
|||
} |
|||
|
|||
public getDeviceProfileInfos(pageLink: PageLink, config?: RequestConfig): Observable<PageData<DeviceProfileInfo>> { |
|||
return this.http.get<PageData<DeviceProfileInfo>>(`/api/deviceProfileInfos${pageLink.toQuery()}`, defaultHttpOptionsFromConfig(config)); |
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,40 @@ |
|||
<!-- |
|||
|
|||
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]="deviceProfileDataFormGroup"> |
|||
<mat-accordion multi="true"> |
|||
<mat-expansion-panel [expanded]="true"> |
|||
<mat-expansion-panel-header> |
|||
<mat-panel-title> |
|||
<div translate>device-profile.profile-configuration</div> |
|||
</mat-panel-title> |
|||
</mat-expansion-panel-header> |
|||
<tb-device-profile-configuration |
|||
formControlName="configuration" |
|||
required> |
|||
</tb-device-profile-configuration> |
|||
</mat-expansion-panel> |
|||
<mat-expansion-panel [expanded]="true"> |
|||
<mat-expansion-panel-header> |
|||
<mat-panel-title> |
|||
<div translate>device-profile.transport-configuration</div> |
|||
</mat-panel-title> |
|||
</mat-expansion-panel-header> |
|||
TODO |
|||
</mat-expansion-panel> |
|||
</mat-accordion> |
|||
</div> |
|||
@ -0,0 +1,92 @@ |
|||
///
|
|||
/// 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, FormGroup, NG_VALUE_ACCESSOR, Validators } from '@angular/forms'; |
|||
import { Store } from '@ngrx/store'; |
|||
import { AppState } from '@app/core/core.state'; |
|||
import { coerceBooleanProperty } from '@angular/cdk/coercion'; |
|||
import { DeviceProfileData } from '@shared/models/device.models'; |
|||
|
|||
@Component({ |
|||
selector: 'tb-device-profile-data', |
|||
templateUrl: './device-profile-data.component.html', |
|||
styleUrls: [], |
|||
providers: [{ |
|||
provide: NG_VALUE_ACCESSOR, |
|||
useExisting: forwardRef(() => DeviceProfileDataComponent), |
|||
multi: true |
|||
}] |
|||
}) |
|||
export class DeviceProfileDataComponent implements ControlValueAccessor, OnInit { |
|||
|
|||
deviceProfileDataFormGroup: FormGroup; |
|||
|
|||
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 store: Store<AppState>, |
|||
private fb: FormBuilder) { |
|||
} |
|||
|
|||
registerOnChange(fn: any): void { |
|||
this.propagateChange = fn; |
|||
} |
|||
|
|||
registerOnTouched(fn: any): void { |
|||
} |
|||
|
|||
ngOnInit() { |
|||
this.deviceProfileDataFormGroup = this.fb.group({ |
|||
configuration: [null, Validators.required] |
|||
}); |
|||
this.deviceProfileDataFormGroup.valueChanges.subscribe(() => { |
|||
this.updateModel(); |
|||
}); |
|||
} |
|||
|
|||
setDisabledState(isDisabled: boolean): void { |
|||
this.disabled = isDisabled; |
|||
if (this.disabled) { |
|||
this.deviceProfileDataFormGroup.disable({emitEvent: false}); |
|||
} else { |
|||
this.deviceProfileDataFormGroup.enable({emitEvent: false}); |
|||
} |
|||
} |
|||
|
|||
writeValue(value: DeviceProfileData | null): void { |
|||
this.deviceProfileDataFormGroup.patchValue({configuration: value?.configuration}, {emitEvent: false}); |
|||
} |
|||
|
|||
private updateModel() { |
|||
let deviceProfileData: DeviceProfileData = null; |
|||
if (this.deviceProfileDataFormGroup.valid) { |
|||
deviceProfileData = this.deviceProfileDataFormGroup.getRawValue(); |
|||
} |
|||
this.propagateChange(deviceProfileData); |
|||
} |
|||
} |
|||
@ -0,0 +1,77 @@ |
|||
<!-- |
|||
|
|||
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 class="tb-details-buttons" fxLayout.xs="column" *ngIf="!standalone"> |
|||
<button mat-raised-button color="primary" |
|||
[disabled]="(isLoading$ | async)" |
|||
(click)="onEntityAction($event, 'setDefault')" |
|||
[fxShow]="!isEdit && !entity?.default"> |
|||
{{'device-profile.set-default' | translate }} |
|||
</button> |
|||
<button mat-raised-button color="primary" |
|||
[disabled]="(isLoading$ | async)" |
|||
(click)="onEntityAction($event, 'delete')" |
|||
[fxShow]="!hideDelete() && !isEdit"> |
|||
{{'device-profile.delete' | translate }} |
|||
</button> |
|||
<div fxLayout="row" fxLayout.xs="column"> |
|||
<button mat-raised-button |
|||
ngxClipboard |
|||
(cbOnSuccess)="onDeviceProfileIdCopied($event)" |
|||
[cbContent]="entity?.id?.id" |
|||
[fxShow]="!isEdit"> |
|||
<mat-icon svgIcon="mdi:clipboard-arrow-left"></mat-icon> |
|||
<span translate>device-profile.copyId</span> |
|||
</button> |
|||
</div> |
|||
</div> |
|||
<div class="mat-padding" fxLayout="column"> |
|||
<form [formGroup]="entityForm"> |
|||
<fieldset [disabled]="(isLoading$ | async) || !isEdit"> |
|||
<mat-form-field class="mat-block"> |
|||
<mat-label translate>device-profile.name</mat-label> |
|||
<input matInput formControlName="name" required/> |
|||
<mat-error *ngIf="entityForm.get('name').hasError('required')"> |
|||
{{ 'device-profile.name-required' | translate }} |
|||
</mat-error> |
|||
</mat-form-field> |
|||
<mat-form-field class="mat-block"> |
|||
<mat-label translate>device-profile.type</mat-label> |
|||
<mat-select formControlName="type" required> |
|||
<mat-option *ngFor="let type of deviceProfileTypes" [value]="type"> |
|||
{{deviceProfileTypeTranslations.get(type) | translate}} |
|||
</mat-option> |
|||
</mat-select> |
|||
<mat-error *ngIf="entityForm.get('type').hasError('required')"> |
|||
{{ 'device-profile.type-required' | translate }} |
|||
</mat-error> |
|||
</mat-form-field> |
|||
<tb-device-profile-data |
|||
formControlName="profileData" |
|||
required> |
|||
</tb-device-profile-data> |
|||
<tb-entity-autocomplete |
|||
[entityType]="entityType.RULE_CHAIN" |
|||
formControlName="defaultRuleChainId"> |
|||
</tb-entity-autocomplete> |
|||
<mat-form-field class="mat-block"> |
|||
<mat-label translate>tenant-profile.description</mat-label> |
|||
<textarea matInput formControlName="description" rows="2"></textarea> |
|||
</mat-form-field> |
|||
</fieldset> |
|||
</form> |
|||
</div> |
|||
@ -0,0 +1,128 @@ |
|||
///
|
|||
/// 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, Inject, Input, Optional } from '@angular/core'; |
|||
import { Store } from '@ngrx/store'; |
|||
import { AppState } from '@core/core.state'; |
|||
import { FormBuilder, FormGroup, Validators } from '@angular/forms'; |
|||
import { ActionNotificationShow } from '@app/core/notification/notification.actions'; |
|||
import { TranslateService } from '@ngx-translate/core'; |
|||
import { EntityTableConfig } from '@home/models/entity/entities-table-config.models'; |
|||
import { EntityComponent } from '../entity/entity.component'; |
|||
import { |
|||
createDeviceProfileConfiguration, |
|||
DeviceProfile, |
|||
DeviceProfileData, |
|||
DeviceProfileType, |
|||
deviceProfileTypeTranslationMap |
|||
} from '@shared/models/device.models'; |
|||
import { EntityType } from '@shared/models/entity-type.models'; |
|||
import { RuleChainId } from '@shared/models/id/rule-chain-id'; |
|||
|
|||
@Component({ |
|||
selector: 'tb-device-profile', |
|||
templateUrl: './device-profile.component.html', |
|||
styleUrls: [] |
|||
}) |
|||
export class DeviceProfileComponent extends EntityComponent<DeviceProfile> { |
|||
|
|||
@Input() |
|||
standalone = false; |
|||
|
|||
entityType = EntityType; |
|||
|
|||
deviceProfileTypes = Object.keys(DeviceProfileType); |
|||
|
|||
deviceProfileTypeTranslations = deviceProfileTypeTranslationMap; |
|||
|
|||
constructor(protected store: Store<AppState>, |
|||
protected translate: TranslateService, |
|||
@Optional() @Inject('entity') protected entityValue: DeviceProfile, |
|||
@Optional() @Inject('entitiesTableConfig') protected entitiesTableConfigValue: EntityTableConfig<DeviceProfile>, |
|||
protected fb: FormBuilder) { |
|||
super(store, fb, entityValue, entitiesTableConfigValue); |
|||
} |
|||
|
|||
hideDelete() { |
|||
if (this.entitiesTableConfig) { |
|||
return !this.entitiesTableConfig.deleteEnabled(this.entity); |
|||
} else { |
|||
return false; |
|||
} |
|||
} |
|||
|
|||
buildForm(entity: DeviceProfile): FormGroup { |
|||
const form = this.fb.group( |
|||
{ |
|||
name: [entity ? entity.name : '', [Validators.required]], |
|||
type: [entity ? entity.type : '', [Validators.required]], |
|||
profileData: [entity && !this.isAdd ? entity.profileData : {}, []], |
|||
defaultRuleChainId: [entity && entity.defaultRuleChainId ? entity.defaultRuleChainId.id : null, []], |
|||
description: [entity ? entity.description : '', []], |
|||
} |
|||
); |
|||
form.get('type').valueChanges.subscribe(() => { |
|||
this.deviceProfileTypeChanged(form); |
|||
}); |
|||
this.checkIsNewDeviceProfile(entity, form); |
|||
return form; |
|||
} |
|||
|
|||
private checkIsNewDeviceProfile(entity: DeviceProfile, form: FormGroup) { |
|||
if (entity && !entity.id) { |
|||
form.get('type').patchValue(DeviceProfileType.DEFAULT, {emitEvent: true}); |
|||
} |
|||
} |
|||
|
|||
private deviceProfileTypeChanged(form: FormGroup) { |
|||
const deviceProfileType: DeviceProfileType = form.get('type').value; |
|||
let profileData: DeviceProfileData = form.getRawValue().profileData; |
|||
if (!profileData) { |
|||
profileData = { |
|||
configuration: null |
|||
}; |
|||
} |
|||
profileData.configuration = createDeviceProfileConfiguration(deviceProfileType); |
|||
this.entityForm.patchValue({profileData}); |
|||
} |
|||
|
|||
updateForm(entity: DeviceProfile) { |
|||
this.entityForm.patchValue({name: entity.name}); |
|||
this.entityForm.patchValue({type: entity.type}); |
|||
this.entityForm.patchValue({profileData: entity.profileData}); |
|||
this.entityForm.patchValue({defaultRuleChainId: entity.defaultRuleChainId ? entity.defaultRuleChainId.id : null}); |
|||
this.entityForm.patchValue({description: entity.description}); |
|||
} |
|||
|
|||
prepareFormValue(formValue: any): any { |
|||
if (formValue.defaultRuleChainId) { |
|||
formValue.defaultRuleChainId = new RuleChainId(formValue.defaultRuleChainId); |
|||
} |
|||
return formValue; |
|||
} |
|||
|
|||
onDeviceProfileIdCopied(event) { |
|||
this.store.dispatch(new ActionNotificationShow( |
|||
{ |
|||
message: this.translate.instant('device-profile.idCopiedMessage'), |
|||
type: 'success', |
|||
duration: 750, |
|||
verticalPosition: 'bottom', |
|||
horizontalPosition: 'right' |
|||
})); |
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,24 @@ |
|||
<!-- |
|||
|
|||
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. |
|||
|
|||
--> |
|||
<form [formGroup]="defaultDeviceProfileConfigurationFormGroup" style="padding-bottom: 16px;"> |
|||
<tb-json-object-edit |
|||
[required]="required" |
|||
label="{{ 'device-profile.type-default' | translate }}" |
|||
formControlName="configuration"> |
|||
</tb-json-object-edit> |
|||
</form> |
|||
@ -0,0 +1,97 @@ |
|||
///
|
|||
/// 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, FormGroup, NG_VALUE_ACCESSOR, Validators } from '@angular/forms'; |
|||
import { Store } from '@ngrx/store'; |
|||
import { AppState } from '@app/core/core.state'; |
|||
import { coerceBooleanProperty } from '@angular/cdk/coercion'; |
|||
import { |
|||
DefaultDeviceProfileConfiguration, |
|||
DeviceProfileConfiguration, |
|||
DeviceProfileType |
|||
} from '@shared/models/device.models'; |
|||
|
|||
@Component({ |
|||
selector: 'tb-default-device-profile-configuration', |
|||
templateUrl: './default-device-profile-configuration.component.html', |
|||
styleUrls: [], |
|||
providers: [{ |
|||
provide: NG_VALUE_ACCESSOR, |
|||
useExisting: forwardRef(() => DefaultDeviceProfileConfigurationComponent), |
|||
multi: true |
|||
}] |
|||
}) |
|||
export class DefaultDeviceProfileConfigurationComponent implements ControlValueAccessor, OnInit { |
|||
|
|||
defaultDeviceProfileConfigurationFormGroup: FormGroup; |
|||
|
|||
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 store: Store<AppState>, |
|||
private fb: FormBuilder) { |
|||
} |
|||
|
|||
registerOnChange(fn: any): void { |
|||
this.propagateChange = fn; |
|||
} |
|||
|
|||
registerOnTouched(fn: any): void { |
|||
} |
|||
|
|||
ngOnInit() { |
|||
this.defaultDeviceProfileConfigurationFormGroup = this.fb.group({ |
|||
configuration: [null, Validators.required] |
|||
}); |
|||
this.defaultDeviceProfileConfigurationFormGroup.valueChanges.subscribe(() => { |
|||
this.updateModel(); |
|||
}); |
|||
} |
|||
|
|||
setDisabledState(isDisabled: boolean): void { |
|||
this.disabled = isDisabled; |
|||
if (this.disabled) { |
|||
this.defaultDeviceProfileConfigurationFormGroup.disable({emitEvent: false}); |
|||
} else { |
|||
this.defaultDeviceProfileConfigurationFormGroup.enable({emitEvent: false}); |
|||
} |
|||
} |
|||
|
|||
writeValue(value: DefaultDeviceProfileConfiguration | null): void { |
|||
this.defaultDeviceProfileConfigurationFormGroup.patchValue({configuration: value}, {emitEvent: false}); |
|||
} |
|||
|
|||
private updateModel() { |
|||
let configuration: DeviceProfileConfiguration = null; |
|||
if (this.defaultDeviceProfileConfigurationFormGroup.valid) { |
|||
configuration = this.defaultDeviceProfileConfigurationFormGroup.getRawValue().configuration; |
|||
configuration.type = DeviceProfileType.DEFAULT; |
|||
} |
|||
this.propagateChange(configuration); |
|||
} |
|||
} |
|||
@ -0,0 +1,27 @@ |
|||
<!-- |
|||
|
|||
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]="deviceProfileConfigurationFormGroup"> |
|||
<div [ngSwitch]="type"> |
|||
<ng-template [ngSwitchCase]="deviceProfileType.DEFAULT"> |
|||
<tb-default-device-profile-configuration |
|||
[required]="required" |
|||
formControlName="configuration"> |
|||
</tb-default-device-profile-configuration> |
|||
</ng-template> |
|||
</div> |
|||
</div> |
|||
@ -0,0 +1,103 @@ |
|||
///
|
|||
/// 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, FormGroup, NG_VALUE_ACCESSOR, Validators } from '@angular/forms'; |
|||
import { Store } from '@ngrx/store'; |
|||
import { AppState } from '@app/core/core.state'; |
|||
import { coerceBooleanProperty } from '@angular/cdk/coercion'; |
|||
import { DeviceProfileConfiguration, DeviceProfileType } from '@shared/models/device.models'; |
|||
import { deepClone } from '../../../../../core/utils'; |
|||
|
|||
@Component({ |
|||
selector: 'tb-device-profile-configuration', |
|||
templateUrl: './device-profile-configuration.component.html', |
|||
styleUrls: [], |
|||
providers: [{ |
|||
provide: NG_VALUE_ACCESSOR, |
|||
useExisting: forwardRef(() => DeviceProfileConfigurationComponent), |
|||
multi: true |
|||
}] |
|||
}) |
|||
export class DeviceProfileConfigurationComponent implements ControlValueAccessor, OnInit { |
|||
|
|||
deviceProfileType = DeviceProfileType; |
|||
|
|||
deviceProfileConfigurationFormGroup: FormGroup; |
|||
|
|||
private requiredValue: boolean; |
|||
get required(): boolean { |
|||
return this.requiredValue; |
|||
} |
|||
@Input() |
|||
set required(value: boolean) { |
|||
this.requiredValue = coerceBooleanProperty(value); |
|||
} |
|||
|
|||
@Input() |
|||
disabled: boolean; |
|||
|
|||
type: DeviceProfileType; |
|||
|
|||
private propagateChange = (v: any) => { }; |
|||
|
|||
constructor(private store: Store<AppState>, |
|||
private fb: FormBuilder) { |
|||
} |
|||
|
|||
registerOnChange(fn: any): void { |
|||
this.propagateChange = fn; |
|||
} |
|||
|
|||
registerOnTouched(fn: any): void { |
|||
} |
|||
|
|||
ngOnInit() { |
|||
this.deviceProfileConfigurationFormGroup = this.fb.group({ |
|||
configuration: [null, Validators.required] |
|||
}); |
|||
this.deviceProfileConfigurationFormGroup.valueChanges.subscribe(() => { |
|||
this.updateModel(); |
|||
}); |
|||
} |
|||
|
|||
setDisabledState(isDisabled: boolean): void { |
|||
this.disabled = isDisabled; |
|||
if (this.disabled) { |
|||
this.deviceProfileConfigurationFormGroup.disable({emitEvent: false}); |
|||
} else { |
|||
this.deviceProfileConfigurationFormGroup.enable({emitEvent: false}); |
|||
} |
|||
} |
|||
|
|||
writeValue(value: DeviceProfileConfiguration | null): void { |
|||
this.type = value?.type; |
|||
const configuration = deepClone(value); |
|||
if (configuration) { |
|||
delete configuration.type; |
|||
} |
|||
this.deviceProfileConfigurationFormGroup.patchValue({configuration}, {emitEvent: false}); |
|||
} |
|||
|
|||
private updateModel() { |
|||
let configuration: DeviceProfileConfiguration = null; |
|||
if (this.deviceProfileConfigurationFormGroup.valid) { |
|||
configuration = this.deviceProfileConfigurationFormGroup.getRawValue().configuration; |
|||
configuration.type = this.type; |
|||
} |
|||
this.propagateChange(configuration); |
|||
} |
|||
} |
|||
@ -0,0 +1,56 @@ |
|||
///
|
|||
/// 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 { NgModule } from '@angular/core'; |
|||
import { RouterModule, Routes } from '@angular/router'; |
|||
|
|||
import { EntitiesTableComponent } from '../../components/entity/entities-table.component'; |
|||
import { Authority } from '@shared/models/authority.enum'; |
|||
import { DeviceProfilesTableConfigResolver } from './device-profiles-table-config.resolver'; |
|||
|
|||
const routes: Routes = [ |
|||
{ |
|||
path: 'deviceProfiles', |
|||
data: { |
|||
breadcrumb: { |
|||
label: 'device-profile.device-profiles', |
|||
icon: 'mdi:alpha-d-box' |
|||
} |
|||
}, |
|||
children: [ |
|||
{ |
|||
path: '', |
|||
component: EntitiesTableComponent, |
|||
data: { |
|||
auth: [Authority.TENANT_ADMIN], |
|||
title: 'device-profile.device-profiles' |
|||
}, |
|||
resolve: { |
|||
entitiesTableConfig: DeviceProfilesTableConfigResolver |
|||
} |
|||
} |
|||
] |
|||
} |
|||
]; |
|||
|
|||
@NgModule({ |
|||
imports: [RouterModule.forChild(routes)], |
|||
exports: [RouterModule], |
|||
providers: [ |
|||
DeviceProfilesTableConfigResolver |
|||
] |
|||
}) |
|||
export class DeviceProfileRoutingModule { } |
|||
@ -0,0 +1,43 @@ |
|||
<!-- |
|||
|
|||
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. |
|||
|
|||
--> |
|||
<mat-tab *ngIf="entity" |
|||
label="{{ 'attribute.attributes' | translate }}" #attributesTab="matTab"> |
|||
<tb-attribute-table [defaultAttributeScope]="attributeScopes.SERVER_SCOPE" |
|||
[active]="attributesTab.isActive" |
|||
[entityId]="entity.id" |
|||
[entityName]="entity.name"> |
|||
</tb-attribute-table> |
|||
</mat-tab> |
|||
<mat-tab *ngIf="entity" |
|||
label="{{ 'attribute.latest-telemetry' | translate }}" #telemetryTab="matTab"> |
|||
<tb-attribute-table [defaultAttributeScope]="latestTelemetryTypes.LATEST_TELEMETRY" |
|||
disableAttributeScopeSelection |
|||
[active]="telemetryTab.isActive" |
|||
[entityId]="entity.id" |
|||
[entityName]="entity.name"> |
|||
</tb-attribute-table> |
|||
</mat-tab> |
|||
<mat-tab *ngIf="entity" |
|||
label="{{ 'alarm.alarms' | translate }}" #alarmsTab="matTab"> |
|||
<tb-alarm-table [active]="alarmsTab.isActive" [entityId]="entity.id"></tb-alarm-table> |
|||
</mat-tab> |
|||
<mat-tab *ngIf="entity" |
|||
label="{{ 'tenant.events' | translate }}" #eventsTab="matTab"> |
|||
<tb-event-table [defaultEventType]="eventTypes.ERROR" [active]="eventsTab.isActive" [tenantId]="nullUid" |
|||
[entityId]="entity.id"></tb-event-table> |
|||
</mat-tab> |
|||
@ -0,0 +1,38 @@ |
|||
///
|
|||
/// 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 } from '@angular/core'; |
|||
import { Store } from '@ngrx/store'; |
|||
import { AppState } from '@core/core.state'; |
|||
import { EntityTabsComponent } from '../../components/entity/entity-tabs.component'; |
|||
import { DeviceProfile } from '@shared/models/device.models'; |
|||
|
|||
@Component({ |
|||
selector: 'tb-device-profile-tabs', |
|||
templateUrl: './device-profile-tabs.component.html', |
|||
styleUrls: [] |
|||
}) |
|||
export class DeviceProfileTabsComponent extends EntityTabsComponent<DeviceProfile> { |
|||
|
|||
constructor(protected store: Store<AppState>) { |
|||
super(store); |
|||
} |
|||
|
|||
ngOnInit() { |
|||
super.ngOnInit(); |
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,35 @@ |
|||
///
|
|||
/// 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 { NgModule } from '@angular/core'; |
|||
import { CommonModule } from '@angular/common'; |
|||
import { SharedModule } from '@shared/shared.module'; |
|||
import { HomeComponentsModule } from '@modules/home/components/home-components.module'; |
|||
import { DeviceProfileTabsComponent } from './device-profile-tabs.component'; |
|||
import { DeviceProfileRoutingModule } from './device-profile-routing.module'; |
|||
|
|||
@NgModule({ |
|||
declarations: [ |
|||
DeviceProfileTabsComponent |
|||
], |
|||
imports: [ |
|||
CommonModule, |
|||
SharedModule, |
|||
HomeComponentsModule, |
|||
DeviceProfileRoutingModule |
|||
] |
|||
}) |
|||
export class DeviceProfileModule { } |
|||
@ -0,0 +1,125 @@ |
|||
///
|
|||
/// 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 { Injectable } from '@angular/core'; |
|||
import { Resolve } from '@angular/router'; |
|||
import { |
|||
checkBoxCell, |
|||
DateEntityTableColumn, |
|||
EntityTableColumn, |
|||
EntityTableConfig |
|||
} from '@home/models/entity/entities-table-config.models'; |
|||
import { TranslateService } from '@ngx-translate/core'; |
|||
import { DatePipe } from '@angular/common'; |
|||
import { EntityType, entityTypeResources, entityTypeTranslations } from '@shared/models/entity-type.models'; |
|||
import { EntityAction } from '@home/models/entity/entity-component.models'; |
|||
import { DialogService } from '@core/services/dialog.service'; |
|||
import { DeviceProfile, deviceProfileTypeTranslationMap } from '@shared/models/device.models'; |
|||
import { DeviceProfileService } from '@core/http/device-profile.service'; |
|||
import { DeviceProfileComponent } from '../../components/profile/device-profile.component'; |
|||
import { DeviceProfileTabsComponent } from './device-profile-tabs.component'; |
|||
|
|||
@Injectable() |
|||
export class DeviceProfilesTableConfigResolver implements Resolve<EntityTableConfig<DeviceProfile>> { |
|||
|
|||
private readonly config: EntityTableConfig<DeviceProfile> = new EntityTableConfig<DeviceProfile>(); |
|||
|
|||
constructor(private deviceProfileService: DeviceProfileService, |
|||
private translate: TranslateService, |
|||
private datePipe: DatePipe, |
|||
private dialogService: DialogService) { |
|||
|
|||
this.config.entityType = EntityType.DEVICE_PROFILE; |
|||
this.config.entityComponent = DeviceProfileComponent; |
|||
this.config.entityTabsComponent = DeviceProfileTabsComponent; |
|||
this.config.entityTranslations = entityTypeTranslations.get(EntityType.DEVICE_PROFILE); |
|||
this.config.entityResources = entityTypeResources.get(EntityType.DEVICE_PROFILE); |
|||
|
|||
this.config.columns.push( |
|||
new DateEntityTableColumn<DeviceProfile>('createdTime', 'common.created-time', this.datePipe, '150px'), |
|||
new EntityTableColumn<DeviceProfile>('name', 'device-profile.name', '20%'), |
|||
new EntityTableColumn<DeviceProfile>('type', 'device-profile.type', '20%', (deviceProfile) => { |
|||
return this.translate.instant(deviceProfileTypeTranslationMap.get(deviceProfile.type)); |
|||
}), |
|||
new EntityTableColumn<DeviceProfile>('description', 'device-profile.description', '60%'), |
|||
new EntityTableColumn<DeviceProfile>('isDefault', 'device-profile.default', '60px', |
|||
entity => { |
|||
return checkBoxCell(entity.default); |
|||
}) |
|||
); |
|||
|
|||
this.config.cellActionDescriptors.push( |
|||
{ |
|||
name: this.translate.instant('device-profile.set-default'), |
|||
icon: 'flag', |
|||
isEnabled: (deviceProfile) => !deviceProfile.default, |
|||
onAction: ($event, entity) => this.setDefaultDeviceProfile($event, entity) |
|||
} |
|||
); |
|||
|
|||
this.config.deleteEntityTitle = deviceProfile => this.translate.instant('device-profile.delete-device-profile-title', |
|||
{ deviceProfileName: deviceProfile.name }); |
|||
this.config.deleteEntityContent = () => this.translate.instant('device-profile.delete-device-profile-text'); |
|||
this.config.deleteEntitiesTitle = count => this.translate.instant('device-profile.delete-device-profiles-title', {count}); |
|||
this.config.deleteEntitiesContent = () => this.translate.instant('device-profile.delete-device-profiles-text'); |
|||
|
|||
this.config.entitiesFetchFunction = pageLink => this.deviceProfileService.getDeviceProfiles(pageLink); |
|||
this.config.loadEntity = id => this.deviceProfileService.getDeviceProfile(id.id); |
|||
this.config.saveEntity = deviceProfile => this.deviceProfileService.saveDeviceProfile(deviceProfile); |
|||
this.config.deleteEntity = id => this.deviceProfileService.deleteDeviceProfile(id.id); |
|||
this.config.onEntityAction = action => this.onDeviceProfileAction(action); |
|||
this.config.deleteEnabled = (deviceProfile) => deviceProfile && !deviceProfile.default; |
|||
this.config.entitySelectionEnabled = (deviceProfile) => deviceProfile && !deviceProfile.default; |
|||
} |
|||
|
|||
resolve(): EntityTableConfig<DeviceProfile> { |
|||
this.config.tableTitle = this.translate.instant('device-profile.device-profiles'); |
|||
|
|||
return this.config; |
|||
} |
|||
|
|||
setDefaultDeviceProfile($event: Event, deviceProfile: DeviceProfile) { |
|||
if ($event) { |
|||
$event.stopPropagation(); |
|||
} |
|||
this.dialogService.confirm( |
|||
this.translate.instant('device-profile.set-default-device-profile-title', {deviceProfileName: deviceProfile.name}), |
|||
this.translate.instant('device-profile.set-default-device-profile-text'), |
|||
this.translate.instant('action.no'), |
|||
this.translate.instant('action.yes'), |
|||
true |
|||
).subscribe((res) => { |
|||
if (res) { |
|||
this.deviceProfileService.setDefaultDeviceProfile(deviceProfile.id.id).subscribe( |
|||
() => { |
|||
this.config.table.updateData(); |
|||
} |
|||
); |
|||
} |
|||
} |
|||
); |
|||
} |
|||
|
|||
onDeviceProfileAction(action: EntityAction<DeviceProfile>): boolean { |
|||
switch (action.action) { |
|||
case 'setDefault': |
|||
this.setDefaultDeviceProfile(action.event, action.entity); |
|||
return true; |
|||
} |
|||
return false; |
|||
} |
|||
|
|||
} |
|||
Loading…
Reference in new issue