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 | 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 | 226 | name: 'entity-view.entity-views', |
220 | 227 | type: 'link', |
221 | 228 | path: '/entityViews', |
... | ... | @@ -283,6 +290,12 @@ export class MenuService { |
283 | 290 | name: 'device.devices', |
284 | 291 | icon: 'devices_other', |
285 | 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 | 88 | import { TenantProfileComponent } from './profile/tenant-profile.component'; |
89 | 89 | import { TenantProfileDialogComponent } from './profile/tenant-profile-dialog.component'; |
90 | 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 | 96 | @NgModule({ |
93 | 97 | declarations: |
... | ... | @@ -158,7 +162,11 @@ import { TenantProfileDataComponent } from './profile/tenant-profile-data.compon |
158 | 162 | TenantProfileAutocompleteComponent, |
159 | 163 | TenantProfileDataComponent, |
160 | 164 | TenantProfileComponent, |
161 | - TenantProfileDialogComponent | |
165 | + TenantProfileDialogComponent, | |
166 | + DefaultDeviceProfileConfigurationComponent, | |
167 | + DeviceProfileConfigurationComponent, | |
168 | + DeviceProfileDataComponent, | |
169 | + DeviceProfileComponent | |
162 | 170 | ], |
163 | 171 | imports: [ |
164 | 172 | CommonModule, |
... | ... | @@ -218,7 +226,11 @@ import { TenantProfileDataComponent } from './profile/tenant-profile-data.compon |
218 | 226 | TenantProfileAutocompleteComponent, |
219 | 227 | TenantProfileDataComponent, |
220 | 228 | TenantProfileComponent, |
221 | - TenantProfileDialogComponent | |
229 | + TenantProfileDialogComponent, | |
230 | + DefaultDeviceProfileConfigurationComponent, | |
231 | + DeviceProfileConfigurationComponent, | |
232 | + DeviceProfileDataComponent, | |
233 | + DeviceProfileComponent | |
222 | 234 | ], |
223 | 235 | providers: [ |
224 | 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 | 35 | |
36 | 36 | tenantProfileDataFormGroup: FormGroup; |
37 | 37 | |
38 | - modelValue: TenantProfileData | null; | |
39 | - | |
40 | 38 | private requiredValue: boolean; |
41 | 39 | get required(): boolean { |
42 | 40 | return this.requiredValue; |
... | ... | @@ -53,9 +51,6 @@ export class TenantProfileDataComponent implements ControlValueAccessor, OnInit |
53 | 51 | |
54 | 52 | constructor(private store: Store<AppState>, |
55 | 53 | private fb: FormBuilder) { |
56 | - this.tenantProfileDataFormGroup = this.fb.group({ | |
57 | - tenantProfileData: [null, Validators.required] | |
58 | - }); | |
59 | 54 | } |
60 | 55 | |
61 | 56 | registerOnChange(fn: any): void { |
... | ... | @@ -66,24 +61,33 @@ export class TenantProfileDataComponent implements ControlValueAccessor, OnInit |
66 | 61 | } |
67 | 62 | |
68 | 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 | 72 | setDisabledState(isDisabled: boolean): void { |
77 | 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 | 81 | writeValue(value: TenantProfileData | null): void { |
81 | - this.modelValue = value; | |
82 | 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 | 32 | import { TenantProfileModule } from './tenant-profile/tenant-profile.module'; |
33 | 33 | import { MODULES_MAP } from '@shared/public-api'; |
34 | 34 | import { modulesMap } from '../../common/modules-map'; |
35 | +import { DeviceProfileModule } from './device-profile/device-profile.module'; | |
35 | 36 | |
36 | 37 | @NgModule({ |
37 | 38 | exports: [ |
... | ... | @@ -40,6 +41,7 @@ import { modulesMap } from '../../common/modules-map'; |
40 | 41 | ProfileModule, |
41 | 42 | TenantProfileModule, |
42 | 43 | TenantModule, |
44 | + DeviceProfileModule, | |
43 | 45 | DeviceModule, |
44 | 46 | AssetModule, |
45 | 47 | EntityViewModule, | ... | ... |
... | ... | @@ -25,23 +25,38 @@ import { RuleChainId } from '@shared/models/id/rule-chain-id'; |
25 | 25 | import { EntityInfoData } from '@shared/models/entity.models'; |
26 | 26 | |
27 | 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 | 37 | export interface DefaultDeviceProfileConfiguration { |
33 | 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 | 43 | export interface DeviceProfileConfiguration extends DeviceProfileConfigurations { |
42 | 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 | 60 | export interface DeviceProfileData { |
46 | 61 | configuration: DeviceProfileConfiguration; |
47 | 62 | } |
... | ... | @@ -50,7 +65,7 @@ export interface DeviceProfile extends BaseData<DeviceProfileId> { |
50 | 65 | tenantId?: TenantId; |
51 | 66 | name: string; |
52 | 67 | description?: string; |
53 | - isDefault: boolean; | |
68 | + default: boolean; | |
54 | 69 | type: DeviceProfileType; |
55 | 70 | defaultRuleChainId?: RuleChainId; |
56 | 71 | profileData: DeviceProfileData; | ... | ... |
... | ... | @@ -754,10 +754,34 @@ |
754 | 754 | "device-profile": "Device profile", |
755 | 755 | "device-profiles": "Device profiles", |
756 | 756 | "add": "Add device profile", |
757 | + "edit": "Edit device profile", | |
757 | 758 | "device-profile-details": "Device profile details", |
758 | 759 | "no-device-profiles-text": "No device profiles found", |
759 | 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 | 786 | "dialog": { |
763 | 787 | "close": "Close dialog" |
... | ... | @@ -1700,7 +1724,7 @@ |
1700 | 1724 | "delete-tenant-profile-text": "Be careful, after the confirmation the tenant profile and all related data will become unrecoverable.", |
1701 | 1725 | "delete-tenant-profiles-title": "Are you sure you want to delete { count, plural, 1 {1 tenant profile} other {# tenant profiles} }?", |
1702 | 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 | 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 | 1729 | "no-tenant-profiles-found": "No tenant profiles found.", |
1706 | 1730 | "create-new-tenant-profile": "Create a new one!" | ... | ... |