Showing
20 changed files
with
1046 additions
and
25 deletions
1 | +/// | ||
2 | +/// Copyright © 2016-2020 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 { Injectable } from '@angular/core'; | ||
18 | +import { HttpClient } from '@angular/common/http'; | ||
19 | +import { PageLink } from '@shared/models/page/page-link'; | ||
20 | +import { defaultHttpOptionsFromConfig, RequestConfig } from './http-utils'; | ||
21 | +import { Observable } from 'rxjs'; | ||
22 | +import { PageData } from '@shared/models/page/page-data'; | ||
23 | +import { DeviceProfile, DeviceProfileInfo } from '@shared/models/device.models'; | ||
24 | + | ||
25 | +@Injectable({ | ||
26 | + providedIn: 'root' | ||
27 | +}) | ||
28 | +export class DeviceProfileService { | ||
29 | + | ||
30 | + constructor( | ||
31 | + private http: HttpClient | ||
32 | + ) { } | ||
33 | + | ||
34 | + public getDeviceProfiles(pageLink: PageLink, config?: RequestConfig): Observable<PageData<DeviceProfile>> { | ||
35 | + return this.http.get<PageData<DeviceProfile>>(`/api/deviceProfiles${pageLink.toQuery()}`, defaultHttpOptionsFromConfig(config)); | ||
36 | + } | ||
37 | + | ||
38 | + public getDeviceProfile(deviceProfileId: string, config?: RequestConfig): Observable<DeviceProfile> { | ||
39 | + return this.http.get<DeviceProfile>(`/api/deviceProfile/${deviceProfileId}`, defaultHttpOptionsFromConfig(config)); | ||
40 | + } | ||
41 | + | ||
42 | + public saveDeviceProfile(deviceProfile: DeviceProfile, config?: RequestConfig): Observable<DeviceProfile> { | ||
43 | + return this.http.post<DeviceProfile>('/api/deviceProfile', deviceProfile, defaultHttpOptionsFromConfig(config)); | ||
44 | + } | ||
45 | + | ||
46 | + public deleteDeviceProfile(deviceProfileId: string, config?: RequestConfig) { | ||
47 | + return this.http.delete(`/api/deviceProfile/${deviceProfileId}`, defaultHttpOptionsFromConfig(config)); | ||
48 | + } | ||
49 | + | ||
50 | + public setDefaultDeviceProfile(deviceProfileId: string, config?: RequestConfig): Observable<DeviceProfile> { | ||
51 | + return this.http.post<DeviceProfile>(`/api/deviceProfile/${deviceProfileId}/default`, defaultHttpOptionsFromConfig(config)); | ||
52 | + } | ||
53 | + | ||
54 | + public getDefaultDeviceProfileInfo(config?: RequestConfig): Observable<DeviceProfileInfo> { | ||
55 | + return this.http.get<DeviceProfileInfo>('/api/deviceProfileInfo/default', defaultHttpOptionsFromConfig(config)); | ||
56 | + } | ||
57 | + | ||
58 | + public getDeviceProfileInfo(deviceProfileId: string, config?: RequestConfig): Observable<DeviceProfileInfo> { | ||
59 | + return this.http.get<DeviceProfileInfo>(`/api/deviceProfileInfo/${deviceProfileId}`, defaultHttpOptionsFromConfig(config)); | ||
60 | + } | ||
61 | + | ||
62 | + public getDeviceProfileInfos(pageLink: PageLink, config?: RequestConfig): Observable<PageData<DeviceProfileInfo>> { | ||
63 | + return this.http.get<PageData<DeviceProfileInfo>>(`/api/deviceProfileInfos${pageLink.toQuery()}`, defaultHttpOptionsFromConfig(config)); | ||
64 | + } | ||
65 | + | ||
66 | +} |
@@ -216,6 +216,13 @@ export class MenuService { | @@ -216,6 +216,13 @@ export class MenuService { | ||
216 | icon: 'devices_other' | 216 | icon: 'devices_other' |
217 | }, | 217 | }, |
218 | { | 218 | { |
219 | + name: 'device-profile.device-profiles', | ||
220 | + type: 'link', | ||
221 | + path: '/deviceProfiles', | ||
222 | + icon: 'mdi:alpha-d-box', | ||
223 | + isMdiIcon: true | ||
224 | + }, | ||
225 | + { | ||
219 | name: 'entity-view.entity-views', | 226 | name: 'entity-view.entity-views', |
220 | type: 'link', | 227 | type: 'link', |
221 | path: '/entityViews', | 228 | path: '/entityViews', |
@@ -283,6 +290,12 @@ export class MenuService { | @@ -283,6 +290,12 @@ export class MenuService { | ||
283 | name: 'device.devices', | 290 | name: 'device.devices', |
284 | icon: 'devices_other', | 291 | icon: 'devices_other', |
285 | path: '/devices' | 292 | path: '/devices' |
293 | + }, | ||
294 | + { | ||
295 | + name: 'device-profile.device-profiles', | ||
296 | + icon: 'mdi:alpha-d-box', | ||
297 | + isMdiIcon: true, | ||
298 | + path: '/deviceProfiles' | ||
286 | } | 299 | } |
287 | ] | 300 | ] |
288 | }, | 301 | }, |
@@ -88,6 +88,10 @@ import { TenantProfileAutocompleteComponent } from './profile/tenant-profile-aut | @@ -88,6 +88,10 @@ import { TenantProfileAutocompleteComponent } from './profile/tenant-profile-aut | ||
88 | import { TenantProfileComponent } from './profile/tenant-profile.component'; | 88 | import { TenantProfileComponent } from './profile/tenant-profile.component'; |
89 | import { TenantProfileDialogComponent } from './profile/tenant-profile-dialog.component'; | 89 | import { TenantProfileDialogComponent } from './profile/tenant-profile-dialog.component'; |
90 | import { TenantProfileDataComponent } from './profile/tenant-profile-data.component'; | 90 | import { TenantProfileDataComponent } from './profile/tenant-profile-data.component'; |
91 | +import { DefaultDeviceProfileConfigurationComponent } from './profile/device/default-device-profile-configuration.component'; | ||
92 | +import { DeviceProfileConfigurationComponent } from './profile/device/device-profile-configuration.component'; | ||
93 | +import { DeviceProfileDataComponent } from './profile/device-profile-data.component'; | ||
94 | +import { DeviceProfileComponent } from './profile/device-profile.component'; | ||
91 | 95 | ||
92 | @NgModule({ | 96 | @NgModule({ |
93 | declarations: | 97 | declarations: |
@@ -158,7 +162,11 @@ import { TenantProfileDataComponent } from './profile/tenant-profile-data.compon | @@ -158,7 +162,11 @@ import { TenantProfileDataComponent } from './profile/tenant-profile-data.compon | ||
158 | TenantProfileAutocompleteComponent, | 162 | TenantProfileAutocompleteComponent, |
159 | TenantProfileDataComponent, | 163 | TenantProfileDataComponent, |
160 | TenantProfileComponent, | 164 | TenantProfileComponent, |
161 | - TenantProfileDialogComponent | 165 | + TenantProfileDialogComponent, |
166 | + DefaultDeviceProfileConfigurationComponent, | ||
167 | + DeviceProfileConfigurationComponent, | ||
168 | + DeviceProfileDataComponent, | ||
169 | + DeviceProfileComponent | ||
162 | ], | 170 | ], |
163 | imports: [ | 171 | imports: [ |
164 | CommonModule, | 172 | CommonModule, |
@@ -218,7 +226,11 @@ import { TenantProfileDataComponent } from './profile/tenant-profile-data.compon | @@ -218,7 +226,11 @@ import { TenantProfileDataComponent } from './profile/tenant-profile-data.compon | ||
218 | TenantProfileAutocompleteComponent, | 226 | TenantProfileAutocompleteComponent, |
219 | TenantProfileDataComponent, | 227 | TenantProfileDataComponent, |
220 | TenantProfileComponent, | 228 | TenantProfileComponent, |
221 | - TenantProfileDialogComponent | 229 | + TenantProfileDialogComponent, |
230 | + DefaultDeviceProfileConfigurationComponent, | ||
231 | + DeviceProfileConfigurationComponent, | ||
232 | + DeviceProfileDataComponent, | ||
233 | + DeviceProfileComponent | ||
222 | ], | 234 | ], |
223 | providers: [ | 235 | providers: [ |
224 | WidgetComponentService, | 236 | WidgetComponentService, |
1 | +<!-- | ||
2 | + | ||
3 | + Copyright © 2016-2020 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 | +<div [formGroup]="deviceProfileDataFormGroup"> | ||
19 | + <mat-accordion multi="true"> | ||
20 | + <mat-expansion-panel [expanded]="true"> | ||
21 | + <mat-expansion-panel-header> | ||
22 | + <mat-panel-title> | ||
23 | + <div translate>device-profile.profile-configuration</div> | ||
24 | + </mat-panel-title> | ||
25 | + </mat-expansion-panel-header> | ||
26 | + <tb-device-profile-configuration | ||
27 | + formControlName="configuration" | ||
28 | + required> | ||
29 | + </tb-device-profile-configuration> | ||
30 | + </mat-expansion-panel> | ||
31 | + <mat-expansion-panel [expanded]="true"> | ||
32 | + <mat-expansion-panel-header> | ||
33 | + <mat-panel-title> | ||
34 | + <div translate>device-profile.transport-configuration</div> | ||
35 | + </mat-panel-title> | ||
36 | + </mat-expansion-panel-header> | ||
37 | + TODO | ||
38 | + </mat-expansion-panel> | ||
39 | + </mat-accordion> | ||
40 | +</div> |
1 | +/// | ||
2 | +/// Copyright © 2016-2020 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, OnInit } from '@angular/core'; | ||
18 | +import { ControlValueAccessor, FormBuilder, FormGroup, NG_VALUE_ACCESSOR, Validators } from '@angular/forms'; | ||
19 | +import { Store } from '@ngrx/store'; | ||
20 | +import { AppState } from '@app/core/core.state'; | ||
21 | +import { coerceBooleanProperty } from '@angular/cdk/coercion'; | ||
22 | +import { DeviceProfileData } from '@shared/models/device.models'; | ||
23 | + | ||
24 | +@Component({ | ||
25 | + selector: 'tb-device-profile-data', | ||
26 | + templateUrl: './device-profile-data.component.html', | ||
27 | + styleUrls: [], | ||
28 | + providers: [{ | ||
29 | + provide: NG_VALUE_ACCESSOR, | ||
30 | + useExisting: forwardRef(() => DeviceProfileDataComponent), | ||
31 | + multi: true | ||
32 | + }] | ||
33 | +}) | ||
34 | +export class DeviceProfileDataComponent implements ControlValueAccessor, OnInit { | ||
35 | + | ||
36 | + deviceProfileDataFormGroup: FormGroup; | ||
37 | + | ||
38 | + private requiredValue: boolean; | ||
39 | + get required(): boolean { | ||
40 | + return this.requiredValue; | ||
41 | + } | ||
42 | + @Input() | ||
43 | + set required(value: boolean) { | ||
44 | + this.requiredValue = coerceBooleanProperty(value); | ||
45 | + } | ||
46 | + | ||
47 | + @Input() | ||
48 | + disabled: boolean; | ||
49 | + | ||
50 | + private propagateChange = (v: any) => { }; | ||
51 | + | ||
52 | + constructor(private store: Store<AppState>, | ||
53 | + private fb: FormBuilder) { | ||
54 | + } | ||
55 | + | ||
56 | + registerOnChange(fn: any): void { | ||
57 | + this.propagateChange = fn; | ||
58 | + } | ||
59 | + | ||
60 | + registerOnTouched(fn: any): void { | ||
61 | + } | ||
62 | + | ||
63 | + ngOnInit() { | ||
64 | + this.deviceProfileDataFormGroup = this.fb.group({ | ||
65 | + configuration: [null, Validators.required] | ||
66 | + }); | ||
67 | + this.deviceProfileDataFormGroup.valueChanges.subscribe(() => { | ||
68 | + this.updateModel(); | ||
69 | + }); | ||
70 | + } | ||
71 | + | ||
72 | + setDisabledState(isDisabled: boolean): void { | ||
73 | + this.disabled = isDisabled; | ||
74 | + if (this.disabled) { | ||
75 | + this.deviceProfileDataFormGroup.disable({emitEvent: false}); | ||
76 | + } else { | ||
77 | + this.deviceProfileDataFormGroup.enable({emitEvent: false}); | ||
78 | + } | ||
79 | + } | ||
80 | + | ||
81 | + writeValue(value: DeviceProfileData | null): void { | ||
82 | + this.deviceProfileDataFormGroup.patchValue({configuration: value?.configuration}, {emitEvent: false}); | ||
83 | + } | ||
84 | + | ||
85 | + private updateModel() { | ||
86 | + let deviceProfileData: DeviceProfileData = null; | ||
87 | + if (this.deviceProfileDataFormGroup.valid) { | ||
88 | + deviceProfileData = this.deviceProfileDataFormGroup.getRawValue(); | ||
89 | + } | ||
90 | + this.propagateChange(deviceProfileData); | ||
91 | + } | ||
92 | +} |
1 | +<!-- | ||
2 | + | ||
3 | + Copyright © 2016-2020 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 | +<div class="tb-details-buttons" fxLayout.xs="column" *ngIf="!standalone"> | ||
19 | + <button mat-raised-button color="primary" | ||
20 | + [disabled]="(isLoading$ | async)" | ||
21 | + (click)="onEntityAction($event, 'setDefault')" | ||
22 | + [fxShow]="!isEdit && !entity?.default"> | ||
23 | + {{'device-profile.set-default' | translate }} | ||
24 | + </button> | ||
25 | + <button mat-raised-button color="primary" | ||
26 | + [disabled]="(isLoading$ | async)" | ||
27 | + (click)="onEntityAction($event, 'delete')" | ||
28 | + [fxShow]="!hideDelete() && !isEdit"> | ||
29 | + {{'device-profile.delete' | translate }} | ||
30 | + </button> | ||
31 | + <div fxLayout="row" fxLayout.xs="column"> | ||
32 | + <button mat-raised-button | ||
33 | + ngxClipboard | ||
34 | + (cbOnSuccess)="onDeviceProfileIdCopied($event)" | ||
35 | + [cbContent]="entity?.id?.id" | ||
36 | + [fxShow]="!isEdit"> | ||
37 | + <mat-icon svgIcon="mdi:clipboard-arrow-left"></mat-icon> | ||
38 | + <span translate>device-profile.copyId</span> | ||
39 | + </button> | ||
40 | + </div> | ||
41 | +</div> | ||
42 | +<div class="mat-padding" fxLayout="column"> | ||
43 | + <form [formGroup]="entityForm"> | ||
44 | + <fieldset [disabled]="(isLoading$ | async) || !isEdit"> | ||
45 | + <mat-form-field class="mat-block"> | ||
46 | + <mat-label translate>device-profile.name</mat-label> | ||
47 | + <input matInput formControlName="name" required/> | ||
48 | + <mat-error *ngIf="entityForm.get('name').hasError('required')"> | ||
49 | + {{ 'device-profile.name-required' | translate }} | ||
50 | + </mat-error> | ||
51 | + </mat-form-field> | ||
52 | + <mat-form-field class="mat-block"> | ||
53 | + <mat-label translate>device-profile.type</mat-label> | ||
54 | + <mat-select formControlName="type" required> | ||
55 | + <mat-option *ngFor="let type of deviceProfileTypes" [value]="type"> | ||
56 | + {{deviceProfileTypeTranslations.get(type) | translate}} | ||
57 | + </mat-option> | ||
58 | + </mat-select> | ||
59 | + <mat-error *ngIf="entityForm.get('type').hasError('required')"> | ||
60 | + {{ 'device-profile.type-required' | translate }} | ||
61 | + </mat-error> | ||
62 | + </mat-form-field> | ||
63 | + <tb-device-profile-data | ||
64 | + formControlName="profileData" | ||
65 | + required> | ||
66 | + </tb-device-profile-data> | ||
67 | + <tb-entity-autocomplete | ||
68 | + [entityType]="entityType.RULE_CHAIN" | ||
69 | + formControlName="defaultRuleChainId"> | ||
70 | + </tb-entity-autocomplete> | ||
71 | + <mat-form-field class="mat-block"> | ||
72 | + <mat-label translate>tenant-profile.description</mat-label> | ||
73 | + <textarea matInput formControlName="description" rows="2"></textarea> | ||
74 | + </mat-form-field> | ||
75 | + </fieldset> | ||
76 | + </form> | ||
77 | +</div> |
1 | +/// | ||
2 | +/// Copyright © 2016-2020 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, Inject, Input, Optional } from '@angular/core'; | ||
18 | +import { Store } from '@ngrx/store'; | ||
19 | +import { AppState } from '@core/core.state'; | ||
20 | +import { FormBuilder, FormGroup, Validators } from '@angular/forms'; | ||
21 | +import { ActionNotificationShow } from '@app/core/notification/notification.actions'; | ||
22 | +import { TranslateService } from '@ngx-translate/core'; | ||
23 | +import { EntityTableConfig } from '@home/models/entity/entities-table-config.models'; | ||
24 | +import { EntityComponent } from '../entity/entity.component'; | ||
25 | +import { | ||
26 | + createDeviceProfileConfiguration, | ||
27 | + DeviceProfile, | ||
28 | + DeviceProfileData, | ||
29 | + DeviceProfileType, | ||
30 | + deviceProfileTypeTranslationMap | ||
31 | +} from '@shared/models/device.models'; | ||
32 | +import { EntityType } from '@shared/models/entity-type.models'; | ||
33 | +import { RuleChainId } from '@shared/models/id/rule-chain-id'; | ||
34 | + | ||
35 | +@Component({ | ||
36 | + selector: 'tb-device-profile', | ||
37 | + templateUrl: './device-profile.component.html', | ||
38 | + styleUrls: [] | ||
39 | +}) | ||
40 | +export class DeviceProfileComponent extends EntityComponent<DeviceProfile> { | ||
41 | + | ||
42 | + @Input() | ||
43 | + standalone = false; | ||
44 | + | ||
45 | + entityType = EntityType; | ||
46 | + | ||
47 | + deviceProfileTypes = Object.keys(DeviceProfileType); | ||
48 | + | ||
49 | + deviceProfileTypeTranslations = deviceProfileTypeTranslationMap; | ||
50 | + | ||
51 | + constructor(protected store: Store<AppState>, | ||
52 | + protected translate: TranslateService, | ||
53 | + @Optional() @Inject('entity') protected entityValue: DeviceProfile, | ||
54 | + @Optional() @Inject('entitiesTableConfig') protected entitiesTableConfigValue: EntityTableConfig<DeviceProfile>, | ||
55 | + protected fb: FormBuilder) { | ||
56 | + super(store, fb, entityValue, entitiesTableConfigValue); | ||
57 | + } | ||
58 | + | ||
59 | + hideDelete() { | ||
60 | + if (this.entitiesTableConfig) { | ||
61 | + return !this.entitiesTableConfig.deleteEnabled(this.entity); | ||
62 | + } else { | ||
63 | + return false; | ||
64 | + } | ||
65 | + } | ||
66 | + | ||
67 | + buildForm(entity: DeviceProfile): FormGroup { | ||
68 | + const form = this.fb.group( | ||
69 | + { | ||
70 | + name: [entity ? entity.name : '', [Validators.required]], | ||
71 | + type: [entity ? entity.type : '', [Validators.required]], | ||
72 | + profileData: [entity && !this.isAdd ? entity.profileData : {}, []], | ||
73 | + defaultRuleChainId: [entity && entity.defaultRuleChainId ? entity.defaultRuleChainId.id : null, []], | ||
74 | + description: [entity ? entity.description : '', []], | ||
75 | + } | ||
76 | + ); | ||
77 | + form.get('type').valueChanges.subscribe(() => { | ||
78 | + this.deviceProfileTypeChanged(form); | ||
79 | + }); | ||
80 | + this.checkIsNewDeviceProfile(entity, form); | ||
81 | + return form; | ||
82 | + } | ||
83 | + | ||
84 | + private checkIsNewDeviceProfile(entity: DeviceProfile, form: FormGroup) { | ||
85 | + if (entity && !entity.id) { | ||
86 | + form.get('type').patchValue(DeviceProfileType.DEFAULT, {emitEvent: true}); | ||
87 | + } | ||
88 | + } | ||
89 | + | ||
90 | + private deviceProfileTypeChanged(form: FormGroup) { | ||
91 | + const deviceProfileType: DeviceProfileType = form.get('type').value; | ||
92 | + let profileData: DeviceProfileData = form.getRawValue().profileData; | ||
93 | + if (!profileData) { | ||
94 | + profileData = { | ||
95 | + configuration: null | ||
96 | + }; | ||
97 | + } | ||
98 | + profileData.configuration = createDeviceProfileConfiguration(deviceProfileType); | ||
99 | + this.entityForm.patchValue({profileData}); | ||
100 | + } | ||
101 | + | ||
102 | + updateForm(entity: DeviceProfile) { | ||
103 | + this.entityForm.patchValue({name: entity.name}); | ||
104 | + this.entityForm.patchValue({type: entity.type}); | ||
105 | + this.entityForm.patchValue({profileData: entity.profileData}); | ||
106 | + this.entityForm.patchValue({defaultRuleChainId: entity.defaultRuleChainId ? entity.defaultRuleChainId.id : null}); | ||
107 | + this.entityForm.patchValue({description: entity.description}); | ||
108 | + } | ||
109 | + | ||
110 | + prepareFormValue(formValue: any): any { | ||
111 | + if (formValue.defaultRuleChainId) { | ||
112 | + formValue.defaultRuleChainId = new RuleChainId(formValue.defaultRuleChainId); | ||
113 | + } | ||
114 | + return formValue; | ||
115 | + } | ||
116 | + | ||
117 | + onDeviceProfileIdCopied(event) { | ||
118 | + this.store.dispatch(new ActionNotificationShow( | ||
119 | + { | ||
120 | + message: this.translate.instant('device-profile.idCopiedMessage'), | ||
121 | + type: 'success', | ||
122 | + duration: 750, | ||
123 | + verticalPosition: 'bottom', | ||
124 | + horizontalPosition: 'right' | ||
125 | + })); | ||
126 | + } | ||
127 | + | ||
128 | +} |
1 | +<!-- | ||
2 | + | ||
3 | + Copyright © 2016-2020 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 | +<form [formGroup]="defaultDeviceProfileConfigurationFormGroup" style="padding-bottom: 16px;"> | ||
19 | + <tb-json-object-edit | ||
20 | + [required]="required" | ||
21 | + label="{{ 'device-profile.type-default' | translate }}" | ||
22 | + formControlName="configuration"> | ||
23 | + </tb-json-object-edit> | ||
24 | +</form> |
1 | +/// | ||
2 | +/// Copyright © 2016-2020 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, OnInit } from '@angular/core'; | ||
18 | +import { ControlValueAccessor, FormBuilder, FormGroup, NG_VALUE_ACCESSOR, Validators } from '@angular/forms'; | ||
19 | +import { Store } from '@ngrx/store'; | ||
20 | +import { AppState } from '@app/core/core.state'; | ||
21 | +import { coerceBooleanProperty } from '@angular/cdk/coercion'; | ||
22 | +import { | ||
23 | + DefaultDeviceProfileConfiguration, | ||
24 | + DeviceProfileConfiguration, | ||
25 | + DeviceProfileType | ||
26 | +} from '@shared/models/device.models'; | ||
27 | + | ||
28 | +@Component({ | ||
29 | + selector: 'tb-default-device-profile-configuration', | ||
30 | + templateUrl: './default-device-profile-configuration.component.html', | ||
31 | + styleUrls: [], | ||
32 | + providers: [{ | ||
33 | + provide: NG_VALUE_ACCESSOR, | ||
34 | + useExisting: forwardRef(() => DefaultDeviceProfileConfigurationComponent), | ||
35 | + multi: true | ||
36 | + }] | ||
37 | +}) | ||
38 | +export class DefaultDeviceProfileConfigurationComponent implements ControlValueAccessor, OnInit { | ||
39 | + | ||
40 | + defaultDeviceProfileConfigurationFormGroup: FormGroup; | ||
41 | + | ||
42 | + private requiredValue: boolean; | ||
43 | + get required(): boolean { | ||
44 | + return this.requiredValue; | ||
45 | + } | ||
46 | + @Input() | ||
47 | + set required(value: boolean) { | ||
48 | + this.requiredValue = coerceBooleanProperty(value); | ||
49 | + } | ||
50 | + | ||
51 | + @Input() | ||
52 | + disabled: boolean; | ||
53 | + | ||
54 | + private propagateChange = (v: any) => { }; | ||
55 | + | ||
56 | + constructor(private store: Store<AppState>, | ||
57 | + private fb: FormBuilder) { | ||
58 | + } | ||
59 | + | ||
60 | + registerOnChange(fn: any): void { | ||
61 | + this.propagateChange = fn; | ||
62 | + } | ||
63 | + | ||
64 | + registerOnTouched(fn: any): void { | ||
65 | + } | ||
66 | + | ||
67 | + ngOnInit() { | ||
68 | + this.defaultDeviceProfileConfigurationFormGroup = this.fb.group({ | ||
69 | + configuration: [null, Validators.required] | ||
70 | + }); | ||
71 | + this.defaultDeviceProfileConfigurationFormGroup.valueChanges.subscribe(() => { | ||
72 | + this.updateModel(); | ||
73 | + }); | ||
74 | + } | ||
75 | + | ||
76 | + setDisabledState(isDisabled: boolean): void { | ||
77 | + this.disabled = isDisabled; | ||
78 | + if (this.disabled) { | ||
79 | + this.defaultDeviceProfileConfigurationFormGroup.disable({emitEvent: false}); | ||
80 | + } else { | ||
81 | + this.defaultDeviceProfileConfigurationFormGroup.enable({emitEvent: false}); | ||
82 | + } | ||
83 | + } | ||
84 | + | ||
85 | + writeValue(value: DefaultDeviceProfileConfiguration | null): void { | ||
86 | + this.defaultDeviceProfileConfigurationFormGroup.patchValue({configuration: value}, {emitEvent: false}); | ||
87 | + } | ||
88 | + | ||
89 | + private updateModel() { | ||
90 | + let configuration: DeviceProfileConfiguration = null; | ||
91 | + if (this.defaultDeviceProfileConfigurationFormGroup.valid) { | ||
92 | + configuration = this.defaultDeviceProfileConfigurationFormGroup.getRawValue().configuration; | ||
93 | + configuration.type = DeviceProfileType.DEFAULT; | ||
94 | + } | ||
95 | + this.propagateChange(configuration); | ||
96 | + } | ||
97 | +} |
ui-ngx/src/app/modules/home/components/profile/device/device-profile-configuration.component.html
0 → 100644
1 | +<!-- | ||
2 | + | ||
3 | + Copyright © 2016-2020 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 | +<div [formGroup]="deviceProfileConfigurationFormGroup"> | ||
19 | + <div [ngSwitch]="type"> | ||
20 | + <ng-template [ngSwitchCase]="deviceProfileType.DEFAULT"> | ||
21 | + <tb-default-device-profile-configuration | ||
22 | + [required]="required" | ||
23 | + formControlName="configuration"> | ||
24 | + </tb-default-device-profile-configuration> | ||
25 | + </ng-template> | ||
26 | + </div> | ||
27 | +</div> |
ui-ngx/src/app/modules/home/components/profile/device/device-profile-configuration.component.ts
0 → 100644
1 | +/// | ||
2 | +/// Copyright © 2016-2020 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, OnInit } from '@angular/core'; | ||
18 | +import { ControlValueAccessor, FormBuilder, FormGroup, NG_VALUE_ACCESSOR, Validators } from '@angular/forms'; | ||
19 | +import { Store } from '@ngrx/store'; | ||
20 | +import { AppState } from '@app/core/core.state'; | ||
21 | +import { coerceBooleanProperty } from '@angular/cdk/coercion'; | ||
22 | +import { DeviceProfileConfiguration, DeviceProfileType } from '@shared/models/device.models'; | ||
23 | +import { deepClone } from '../../../../../core/utils'; | ||
24 | + | ||
25 | +@Component({ | ||
26 | + selector: 'tb-device-profile-configuration', | ||
27 | + templateUrl: './device-profile-configuration.component.html', | ||
28 | + styleUrls: [], | ||
29 | + providers: [{ | ||
30 | + provide: NG_VALUE_ACCESSOR, | ||
31 | + useExisting: forwardRef(() => DeviceProfileConfigurationComponent), | ||
32 | + multi: true | ||
33 | + }] | ||
34 | +}) | ||
35 | +export class DeviceProfileConfigurationComponent implements ControlValueAccessor, OnInit { | ||
36 | + | ||
37 | + deviceProfileType = DeviceProfileType; | ||
38 | + | ||
39 | + deviceProfileConfigurationFormGroup: FormGroup; | ||
40 | + | ||
41 | + private requiredValue: boolean; | ||
42 | + get required(): boolean { | ||
43 | + return this.requiredValue; | ||
44 | + } | ||
45 | + @Input() | ||
46 | + set required(value: boolean) { | ||
47 | + this.requiredValue = coerceBooleanProperty(value); | ||
48 | + } | ||
49 | + | ||
50 | + @Input() | ||
51 | + disabled: boolean; | ||
52 | + | ||
53 | + type: DeviceProfileType; | ||
54 | + | ||
55 | + private propagateChange = (v: any) => { }; | ||
56 | + | ||
57 | + constructor(private store: Store<AppState>, | ||
58 | + private fb: FormBuilder) { | ||
59 | + } | ||
60 | + | ||
61 | + registerOnChange(fn: any): void { | ||
62 | + this.propagateChange = fn; | ||
63 | + } | ||
64 | + | ||
65 | + registerOnTouched(fn: any): void { | ||
66 | + } | ||
67 | + | ||
68 | + ngOnInit() { | ||
69 | + this.deviceProfileConfigurationFormGroup = this.fb.group({ | ||
70 | + configuration: [null, Validators.required] | ||
71 | + }); | ||
72 | + this.deviceProfileConfigurationFormGroup.valueChanges.subscribe(() => { | ||
73 | + this.updateModel(); | ||
74 | + }); | ||
75 | + } | ||
76 | + | ||
77 | + setDisabledState(isDisabled: boolean): void { | ||
78 | + this.disabled = isDisabled; | ||
79 | + if (this.disabled) { | ||
80 | + this.deviceProfileConfigurationFormGroup.disable({emitEvent: false}); | ||
81 | + } else { | ||
82 | + this.deviceProfileConfigurationFormGroup.enable({emitEvent: false}); | ||
83 | + } | ||
84 | + } | ||
85 | + | ||
86 | + writeValue(value: DeviceProfileConfiguration | null): void { | ||
87 | + this.type = value?.type; | ||
88 | + const configuration = deepClone(value); | ||
89 | + if (configuration) { | ||
90 | + delete configuration.type; | ||
91 | + } | ||
92 | + this.deviceProfileConfigurationFormGroup.patchValue({configuration}, {emitEvent: false}); | ||
93 | + } | ||
94 | + | ||
95 | + private updateModel() { | ||
96 | + let configuration: DeviceProfileConfiguration = null; | ||
97 | + if (this.deviceProfileConfigurationFormGroup.valid) { | ||
98 | + configuration = this.deviceProfileConfigurationFormGroup.getRawValue().configuration; | ||
99 | + configuration.type = this.type; | ||
100 | + } | ||
101 | + this.propagateChange(configuration); | ||
102 | + } | ||
103 | +} |
@@ -35,8 +35,6 @@ export class TenantProfileDataComponent implements ControlValueAccessor, OnInit | @@ -35,8 +35,6 @@ export class TenantProfileDataComponent implements ControlValueAccessor, OnInit | ||
35 | 35 | ||
36 | tenantProfileDataFormGroup: FormGroup; | 36 | tenantProfileDataFormGroup: FormGroup; |
37 | 37 | ||
38 | - modelValue: TenantProfileData | null; | ||
39 | - | ||
40 | private requiredValue: boolean; | 38 | private requiredValue: boolean; |
41 | get required(): boolean { | 39 | get required(): boolean { |
42 | return this.requiredValue; | 40 | return this.requiredValue; |
@@ -53,9 +51,6 @@ export class TenantProfileDataComponent implements ControlValueAccessor, OnInit | @@ -53,9 +51,6 @@ export class TenantProfileDataComponent implements ControlValueAccessor, OnInit | ||
53 | 51 | ||
54 | constructor(private store: Store<AppState>, | 52 | constructor(private store: Store<AppState>, |
55 | private fb: FormBuilder) { | 53 | private fb: FormBuilder) { |
56 | - this.tenantProfileDataFormGroup = this.fb.group({ | ||
57 | - tenantProfileData: [null, Validators.required] | ||
58 | - }); | ||
59 | } | 54 | } |
60 | 55 | ||
61 | registerOnChange(fn: any): void { | 56 | registerOnChange(fn: any): void { |
@@ -66,24 +61,33 @@ export class TenantProfileDataComponent implements ControlValueAccessor, OnInit | @@ -66,24 +61,33 @@ export class TenantProfileDataComponent implements ControlValueAccessor, OnInit | ||
66 | } | 61 | } |
67 | 62 | ||
68 | ngOnInit() { | 63 | ngOnInit() { |
69 | - this.tenantProfileDataFormGroup.get('tenantProfileData').valueChanges.subscribe( | ||
70 | - tenantProfileData => { | ||
71 | - this.updateView(this.tenantProfileDataFormGroup.valid ? tenantProfileData : null); | ||
72 | - } | ||
73 | - ); | 64 | + this.tenantProfileDataFormGroup = this.fb.group({ |
65 | + tenantProfileData: [null, Validators.required] | ||
66 | + }); | ||
67 | + this.tenantProfileDataFormGroup.valueChanges.subscribe(() => { | ||
68 | + this.updateModel(); | ||
69 | + }); | ||
74 | } | 70 | } |
75 | 71 | ||
76 | setDisabledState(isDisabled: boolean): void { | 72 | setDisabledState(isDisabled: boolean): void { |
77 | this.disabled = isDisabled; | 73 | this.disabled = isDisabled; |
74 | + if (this.disabled) { | ||
75 | + this.tenantProfileDataFormGroup.disable({emitEvent: false}); | ||
76 | + } else { | ||
77 | + this.tenantProfileDataFormGroup.enable({emitEvent: false}); | ||
78 | + } | ||
78 | } | 79 | } |
79 | 80 | ||
80 | writeValue(value: TenantProfileData | null): void { | 81 | writeValue(value: TenantProfileData | null): void { |
81 | - this.modelValue = value; | ||
82 | this.tenantProfileDataFormGroup.get('tenantProfileData').patchValue(value, {emitEvent: false}); | 82 | this.tenantProfileDataFormGroup.get('tenantProfileData').patchValue(value, {emitEvent: false}); |
83 | } | 83 | } |
84 | 84 | ||
85 | - updateView(value: TenantProfileData | null) { | ||
86 | - this.modelValue = value; | ||
87 | - this.propagateChange(this.modelValue); | 85 | + private updateModel() { |
86 | + let tenantProfileData: TenantProfileData = null; | ||
87 | + if (this.tenantProfileDataFormGroup.valid) { | ||
88 | + tenantProfileData = this.tenantProfileDataFormGroup.getRawValue().profileData; | ||
89 | + } | ||
90 | + this.propagateChange(tenantProfileData); | ||
88 | } | 91 | } |
92 | + | ||
89 | } | 93 | } |
1 | +/// | ||
2 | +/// Copyright © 2016-2020 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 { RouterModule, Routes } from '@angular/router'; | ||
19 | + | ||
20 | +import { EntitiesTableComponent } from '../../components/entity/entities-table.component'; | ||
21 | +import { Authority } from '@shared/models/authority.enum'; | ||
22 | +import { DeviceProfilesTableConfigResolver } from './device-profiles-table-config.resolver'; | ||
23 | + | ||
24 | +const routes: Routes = [ | ||
25 | + { | ||
26 | + path: 'deviceProfiles', | ||
27 | + data: { | ||
28 | + breadcrumb: { | ||
29 | + label: 'device-profile.device-profiles', | ||
30 | + icon: 'mdi:alpha-d-box' | ||
31 | + } | ||
32 | + }, | ||
33 | + children: [ | ||
34 | + { | ||
35 | + path: '', | ||
36 | + component: EntitiesTableComponent, | ||
37 | + data: { | ||
38 | + auth: [Authority.TENANT_ADMIN], | ||
39 | + title: 'device-profile.device-profiles' | ||
40 | + }, | ||
41 | + resolve: { | ||
42 | + entitiesTableConfig: DeviceProfilesTableConfigResolver | ||
43 | + } | ||
44 | + } | ||
45 | + ] | ||
46 | + } | ||
47 | +]; | ||
48 | + | ||
49 | +@NgModule({ | ||
50 | + imports: [RouterModule.forChild(routes)], | ||
51 | + exports: [RouterModule], | ||
52 | + providers: [ | ||
53 | + DeviceProfilesTableConfigResolver | ||
54 | + ] | ||
55 | +}) | ||
56 | +export class DeviceProfileRoutingModule { } |
1 | +<!-- | ||
2 | + | ||
3 | + Copyright © 2016-2020 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 | +<mat-tab *ngIf="entity" | ||
19 | + label="{{ 'attribute.attributes' | translate }}" #attributesTab="matTab"> | ||
20 | + <tb-attribute-table [defaultAttributeScope]="attributeScopes.SERVER_SCOPE" | ||
21 | + [active]="attributesTab.isActive" | ||
22 | + [entityId]="entity.id" | ||
23 | + [entityName]="entity.name"> | ||
24 | + </tb-attribute-table> | ||
25 | +</mat-tab> | ||
26 | +<mat-tab *ngIf="entity" | ||
27 | + label="{{ 'attribute.latest-telemetry' | translate }}" #telemetryTab="matTab"> | ||
28 | + <tb-attribute-table [defaultAttributeScope]="latestTelemetryTypes.LATEST_TELEMETRY" | ||
29 | + disableAttributeScopeSelection | ||
30 | + [active]="telemetryTab.isActive" | ||
31 | + [entityId]="entity.id" | ||
32 | + [entityName]="entity.name"> | ||
33 | + </tb-attribute-table> | ||
34 | +</mat-tab> | ||
35 | +<mat-tab *ngIf="entity" | ||
36 | + label="{{ 'alarm.alarms' | translate }}" #alarmsTab="matTab"> | ||
37 | + <tb-alarm-table [active]="alarmsTab.isActive" [entityId]="entity.id"></tb-alarm-table> | ||
38 | +</mat-tab> | ||
39 | +<mat-tab *ngIf="entity" | ||
40 | + label="{{ 'tenant.events' | translate }}" #eventsTab="matTab"> | ||
41 | + <tb-event-table [defaultEventType]="eventTypes.ERROR" [active]="eventsTab.isActive" [tenantId]="nullUid" | ||
42 | + [entityId]="entity.id"></tb-event-table> | ||
43 | +</mat-tab> |
1 | +/// | ||
2 | +/// Copyright © 2016-2020 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 } from '@angular/core'; | ||
18 | +import { Store } from '@ngrx/store'; | ||
19 | +import { AppState } from '@core/core.state'; | ||
20 | +import { EntityTabsComponent } from '../../components/entity/entity-tabs.component'; | ||
21 | +import { DeviceProfile } from '@shared/models/device.models'; | ||
22 | + | ||
23 | +@Component({ | ||
24 | + selector: 'tb-device-profile-tabs', | ||
25 | + templateUrl: './device-profile-tabs.component.html', | ||
26 | + styleUrls: [] | ||
27 | +}) | ||
28 | +export class DeviceProfileTabsComponent extends EntityTabsComponent<DeviceProfile> { | ||
29 | + | ||
30 | + constructor(protected store: Store<AppState>) { | ||
31 | + super(store); | ||
32 | + } | ||
33 | + | ||
34 | + ngOnInit() { | ||
35 | + super.ngOnInit(); | ||
36 | + } | ||
37 | + | ||
38 | +} |
1 | +/// | ||
2 | +/// Copyright © 2016-2020 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 { HomeComponentsModule } from '@modules/home/components/home-components.module'; | ||
21 | +import { DeviceProfileTabsComponent } from './device-profile-tabs.component'; | ||
22 | +import { DeviceProfileRoutingModule } from './device-profile-routing.module'; | ||
23 | + | ||
24 | +@NgModule({ | ||
25 | + declarations: [ | ||
26 | + DeviceProfileTabsComponent | ||
27 | + ], | ||
28 | + imports: [ | ||
29 | + CommonModule, | ||
30 | + SharedModule, | ||
31 | + HomeComponentsModule, | ||
32 | + DeviceProfileRoutingModule | ||
33 | + ] | ||
34 | +}) | ||
35 | +export class DeviceProfileModule { } |
ui-ngx/src/app/modules/home/pages/device-profile/device-profiles-table-config.resolver.ts
0 → 100644
1 | +/// | ||
2 | +/// Copyright © 2016-2020 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 { Injectable } from '@angular/core'; | ||
18 | +import { Resolve } from '@angular/router'; | ||
19 | +import { | ||
20 | + checkBoxCell, | ||
21 | + DateEntityTableColumn, | ||
22 | + EntityTableColumn, | ||
23 | + EntityTableConfig | ||
24 | +} from '@home/models/entity/entities-table-config.models'; | ||
25 | +import { TranslateService } from '@ngx-translate/core'; | ||
26 | +import { DatePipe } from '@angular/common'; | ||
27 | +import { EntityType, entityTypeResources, entityTypeTranslations } from '@shared/models/entity-type.models'; | ||
28 | +import { EntityAction } from '@home/models/entity/entity-component.models'; | ||
29 | +import { DialogService } from '@core/services/dialog.service'; | ||
30 | +import { DeviceProfile, deviceProfileTypeTranslationMap } from '@shared/models/device.models'; | ||
31 | +import { DeviceProfileService } from '@core/http/device-profile.service'; | ||
32 | +import { DeviceProfileComponent } from '../../components/profile/device-profile.component'; | ||
33 | +import { DeviceProfileTabsComponent } from './device-profile-tabs.component'; | ||
34 | + | ||
35 | +@Injectable() | ||
36 | +export class DeviceProfilesTableConfigResolver implements Resolve<EntityTableConfig<DeviceProfile>> { | ||
37 | + | ||
38 | + private readonly config: EntityTableConfig<DeviceProfile> = new EntityTableConfig<DeviceProfile>(); | ||
39 | + | ||
40 | + constructor(private deviceProfileService: DeviceProfileService, | ||
41 | + private translate: TranslateService, | ||
42 | + private datePipe: DatePipe, | ||
43 | + private dialogService: DialogService) { | ||
44 | + | ||
45 | + this.config.entityType = EntityType.DEVICE_PROFILE; | ||
46 | + this.config.entityComponent = DeviceProfileComponent; | ||
47 | + this.config.entityTabsComponent = DeviceProfileTabsComponent; | ||
48 | + this.config.entityTranslations = entityTypeTranslations.get(EntityType.DEVICE_PROFILE); | ||
49 | + this.config.entityResources = entityTypeResources.get(EntityType.DEVICE_PROFILE); | ||
50 | + | ||
51 | + this.config.columns.push( | ||
52 | + new DateEntityTableColumn<DeviceProfile>('createdTime', 'common.created-time', this.datePipe, '150px'), | ||
53 | + new EntityTableColumn<DeviceProfile>('name', 'device-profile.name', '20%'), | ||
54 | + new EntityTableColumn<DeviceProfile>('type', 'device-profile.type', '20%', (deviceProfile) => { | ||
55 | + return this.translate.instant(deviceProfileTypeTranslationMap.get(deviceProfile.type)); | ||
56 | + }), | ||
57 | + new EntityTableColumn<DeviceProfile>('description', 'device-profile.description', '60%'), | ||
58 | + new EntityTableColumn<DeviceProfile>('isDefault', 'device-profile.default', '60px', | ||
59 | + entity => { | ||
60 | + return checkBoxCell(entity.default); | ||
61 | + }) | ||
62 | + ); | ||
63 | + | ||
64 | + this.config.cellActionDescriptors.push( | ||
65 | + { | ||
66 | + name: this.translate.instant('device-profile.set-default'), | ||
67 | + icon: 'flag', | ||
68 | + isEnabled: (deviceProfile) => !deviceProfile.default, | ||
69 | + onAction: ($event, entity) => this.setDefaultDeviceProfile($event, entity) | ||
70 | + } | ||
71 | + ); | ||
72 | + | ||
73 | + this.config.deleteEntityTitle = deviceProfile => this.translate.instant('device-profile.delete-device-profile-title', | ||
74 | + { deviceProfileName: deviceProfile.name }); | ||
75 | + this.config.deleteEntityContent = () => this.translate.instant('device-profile.delete-device-profile-text'); | ||
76 | + this.config.deleteEntitiesTitle = count => this.translate.instant('device-profile.delete-device-profiles-title', {count}); | ||
77 | + this.config.deleteEntitiesContent = () => this.translate.instant('device-profile.delete-device-profiles-text'); | ||
78 | + | ||
79 | + this.config.entitiesFetchFunction = pageLink => this.deviceProfileService.getDeviceProfiles(pageLink); | ||
80 | + this.config.loadEntity = id => this.deviceProfileService.getDeviceProfile(id.id); | ||
81 | + this.config.saveEntity = deviceProfile => this.deviceProfileService.saveDeviceProfile(deviceProfile); | ||
82 | + this.config.deleteEntity = id => this.deviceProfileService.deleteDeviceProfile(id.id); | ||
83 | + this.config.onEntityAction = action => this.onDeviceProfileAction(action); | ||
84 | + this.config.deleteEnabled = (deviceProfile) => deviceProfile && !deviceProfile.default; | ||
85 | + this.config.entitySelectionEnabled = (deviceProfile) => deviceProfile && !deviceProfile.default; | ||
86 | + } | ||
87 | + | ||
88 | + resolve(): EntityTableConfig<DeviceProfile> { | ||
89 | + this.config.tableTitle = this.translate.instant('device-profile.device-profiles'); | ||
90 | + | ||
91 | + return this.config; | ||
92 | + } | ||
93 | + | ||
94 | + setDefaultDeviceProfile($event: Event, deviceProfile: DeviceProfile) { | ||
95 | + if ($event) { | ||
96 | + $event.stopPropagation(); | ||
97 | + } | ||
98 | + this.dialogService.confirm( | ||
99 | + this.translate.instant('device-profile.set-default-device-profile-title', {deviceProfileName: deviceProfile.name}), | ||
100 | + this.translate.instant('device-profile.set-default-device-profile-text'), | ||
101 | + this.translate.instant('action.no'), | ||
102 | + this.translate.instant('action.yes'), | ||
103 | + true | ||
104 | + ).subscribe((res) => { | ||
105 | + if (res) { | ||
106 | + this.deviceProfileService.setDefaultDeviceProfile(deviceProfile.id.id).subscribe( | ||
107 | + () => { | ||
108 | + this.config.table.updateData(); | ||
109 | + } | ||
110 | + ); | ||
111 | + } | ||
112 | + } | ||
113 | + ); | ||
114 | + } | ||
115 | + | ||
116 | + onDeviceProfileAction(action: EntityAction<DeviceProfile>): boolean { | ||
117 | + switch (action.action) { | ||
118 | + case 'setDefault': | ||
119 | + this.setDefaultDeviceProfile(action.event, action.entity); | ||
120 | + return true; | ||
121 | + } | ||
122 | + return false; | ||
123 | + } | ||
124 | + | ||
125 | +} |
@@ -32,6 +32,7 @@ import { DashboardModule } from '@modules/home/pages/dashboard/dashboard.module' | @@ -32,6 +32,7 @@ import { DashboardModule } from '@modules/home/pages/dashboard/dashboard.module' | ||
32 | import { TenantProfileModule } from './tenant-profile/tenant-profile.module'; | 32 | import { TenantProfileModule } from './tenant-profile/tenant-profile.module'; |
33 | import { MODULES_MAP } from '@shared/public-api'; | 33 | import { MODULES_MAP } from '@shared/public-api'; |
34 | import { modulesMap } from '../../common/modules-map'; | 34 | import { modulesMap } from '../../common/modules-map'; |
35 | +import { DeviceProfileModule } from './device-profile/device-profile.module'; | ||
35 | 36 | ||
36 | @NgModule({ | 37 | @NgModule({ |
37 | exports: [ | 38 | exports: [ |
@@ -40,6 +41,7 @@ import { modulesMap } from '../../common/modules-map'; | @@ -40,6 +41,7 @@ import { modulesMap } from '../../common/modules-map'; | ||
40 | ProfileModule, | 41 | ProfileModule, |
41 | TenantProfileModule, | 42 | TenantProfileModule, |
42 | TenantModule, | 43 | TenantModule, |
44 | + DeviceProfileModule, | ||
43 | DeviceModule, | 45 | DeviceModule, |
44 | AssetModule, | 46 | AssetModule, |
45 | EntityViewModule, | 47 | EntityViewModule, |
@@ -25,23 +25,38 @@ import { RuleChainId } from '@shared/models/id/rule-chain-id'; | @@ -25,23 +25,38 @@ import { RuleChainId } from '@shared/models/id/rule-chain-id'; | ||
25 | import { EntityInfoData } from '@shared/models/entity.models'; | 25 | import { EntityInfoData } from '@shared/models/entity.models'; |
26 | 26 | ||
27 | export enum DeviceProfileType { | 27 | export enum DeviceProfileType { |
28 | - DEFAULT = 'DEFAULT', | ||
29 | - LWM2M = 'LWM2M' | 28 | + DEFAULT = 'DEFAULT' |
30 | } | 29 | } |
31 | 30 | ||
31 | +export const deviceProfileTypeTranslationMap = new Map<DeviceProfileType, string>( | ||
32 | + [ | ||
33 | + [DeviceProfileType.DEFAULT, 'device-profile.type-default'] | ||
34 | + ] | ||
35 | +); | ||
36 | + | ||
32 | export interface DefaultDeviceProfileConfiguration { | 37 | export interface DefaultDeviceProfileConfiguration { |
33 | [key: string]: any; | 38 | [key: string]: any; |
34 | } | 39 | } |
35 | -export interface Lwm2mDeviceProfileConfiguration { | ||
36 | - [key: string]: any; | ||
37 | -} | ||
38 | 40 | ||
39 | -export type DeviceProfileConfigurations = DefaultDeviceProfileConfiguration & Lwm2mDeviceProfileConfiguration; | 41 | +export type DeviceProfileConfigurations = DefaultDeviceProfileConfiguration; |
40 | 42 | ||
41 | export interface DeviceProfileConfiguration extends DeviceProfileConfigurations { | 43 | export interface DeviceProfileConfiguration extends DeviceProfileConfigurations { |
42 | type: DeviceProfileType; | 44 | type: DeviceProfileType; |
43 | } | 45 | } |
44 | 46 | ||
47 | +export function createDeviceProfileConfiguration(type: DeviceProfileType): DeviceProfileConfiguration { | ||
48 | + let configuration: DeviceProfileConfiguration = null; | ||
49 | + if (type) { | ||
50 | + switch (type) { | ||
51 | + case DeviceProfileType.DEFAULT: | ||
52 | + const defaultConfiguration: DefaultDeviceProfileConfiguration = {}; | ||
53 | + configuration = {...defaultConfiguration, type: DeviceProfileType.DEFAULT}; | ||
54 | + break; | ||
55 | + } | ||
56 | + } | ||
57 | + return configuration; | ||
58 | +} | ||
59 | + | ||
45 | export interface DeviceProfileData { | 60 | export interface DeviceProfileData { |
46 | configuration: DeviceProfileConfiguration; | 61 | configuration: DeviceProfileConfiguration; |
47 | } | 62 | } |
@@ -50,7 +65,7 @@ export interface DeviceProfile extends BaseData<DeviceProfileId> { | @@ -50,7 +65,7 @@ export interface DeviceProfile extends BaseData<DeviceProfileId> { | ||
50 | tenantId?: TenantId; | 65 | tenantId?: TenantId; |
51 | name: string; | 66 | name: string; |
52 | description?: string; | 67 | description?: string; |
53 | - isDefault: boolean; | 68 | + default: boolean; |
54 | type: DeviceProfileType; | 69 | type: DeviceProfileType; |
55 | defaultRuleChainId?: RuleChainId; | 70 | defaultRuleChainId?: RuleChainId; |
56 | profileData: DeviceProfileData; | 71 | profileData: DeviceProfileData; |
@@ -754,10 +754,34 @@ | @@ -754,10 +754,34 @@ | ||
754 | "device-profile": "Device profile", | 754 | "device-profile": "Device profile", |
755 | "device-profiles": "Device profiles", | 755 | "device-profiles": "Device profiles", |
756 | "add": "Add device profile", | 756 | "add": "Add device profile", |
757 | + "edit": "Edit device profile", | ||
757 | "device-profile-details": "Device profile details", | 758 | "device-profile-details": "Device profile details", |
758 | "no-device-profiles-text": "No device profiles found", | 759 | "no-device-profiles-text": "No device profiles found", |
759 | "search": "Search device profiles", | 760 | "search": "Search device profiles", |
760 | - "selected-device-profiles": "{ count, plural, 1 {1 device profile} other {# device profiles} } selected" | 761 | + "selected-device-profiles": "{ count, plural, 1 {1 device profile} other {# device profiles} } selected", |
762 | + "no-device-profiles-matching": "No device profile matching '{{entity}}' were found.", | ||
763 | + "device-profile-required": "Device profile is required", | ||
764 | + "idCopiedMessage": "Device profile Id has been copied to clipboard", | ||
765 | + "set-default": "Make device profile default", | ||
766 | + "delete": "Delete device profile", | ||
767 | + "copyId": "Copy device profile Id", | ||
768 | + "name": "Name", | ||
769 | + "name-required": "Name is required.", | ||
770 | + "type": "Type", | ||
771 | + "type-required": "Type is required.", | ||
772 | + "type-default": "Default", | ||
773 | + "description": "Description", | ||
774 | + "default": "Default", | ||
775 | + "profile-configuration": "Profile configuration", | ||
776 | + "transport-configuration": "Transport configuration", | ||
777 | + "delete-device-profile-title": "Are you sure you want to delete the device profile '{{deviceProfileName}}'?", | ||
778 | + "delete-device-profile-text": "Be careful, after the confirmation the device profile and all related data will become unrecoverable.", | ||
779 | + "delete-device-profiles-title": "Are you sure you want to delete { count, plural, 1 {1 device profile} other {# device profiles} }?", | ||
780 | + "delete-device-profiles-text": "Be careful, after the confirmation all selected device profiles will be removed and all related data will become unrecoverable.", | ||
781 | + "set-default-device-profile-title": "Are you sure you want to make the device profile '{{deviceProfileName}}' default?", | ||
782 | + "set-default-device-profile-text": "After the confirmation the device profile will be marked as default and will be used for new devices with no profile specified.", | ||
783 | + "no-device-profiles-found": "No device profiles found.", | ||
784 | + "create-new-device-profile": "Create a new one!" | ||
761 | }, | 785 | }, |
762 | "dialog": { | 786 | "dialog": { |
763 | "close": "Close dialog" | 787 | "close": "Close dialog" |
@@ -1700,7 +1724,7 @@ | @@ -1700,7 +1724,7 @@ | ||
1700 | "delete-tenant-profile-text": "Be careful, after the confirmation the tenant profile and all related data will become unrecoverable.", | 1724 | "delete-tenant-profile-text": "Be careful, after the confirmation the tenant profile and all related data will become unrecoverable.", |
1701 | "delete-tenant-profiles-title": "Are you sure you want to delete { count, plural, 1 {1 tenant profile} other {# tenant profiles} }?", | 1725 | "delete-tenant-profiles-title": "Are you sure you want to delete { count, plural, 1 {1 tenant profile} other {# tenant profiles} }?", |
1702 | "delete-tenant-profiles-text": "Be careful, after the confirmation all selected tenant profiles will be removed and all related data will become unrecoverable.", | 1726 | "delete-tenant-profiles-text": "Be careful, after the confirmation all selected tenant profiles will be removed and all related data will become unrecoverable.", |
1703 | - "set-default-tenant-profile-title": "Are you sure you want to make the tenant profile '{{tenantProfileName}}' root?", | 1727 | + "set-default-tenant-profile-title": "Are you sure you want to make the tenant profile '{{tenantProfileName}}' default?", |
1704 | "set-default-tenant-profile-text": "After the confirmation the tenant profile will be marked as default and will be used for new tenants with no profile specified.", | 1728 | "set-default-tenant-profile-text": "After the confirmation the tenant profile will be marked as default and will be used for new tenants with no profile specified.", |
1705 | "no-tenant-profiles-found": "No tenant profiles found.", | 1729 | "no-tenant-profiles-found": "No tenant profiles found.", |
1706 | "create-new-tenant-profile": "Create a new one!" | 1730 | "create-new-tenant-profile": "Create a new one!" |