Commit cc018f8876dce90cc8aeee9229e744656d30e5e0

Authored by Igor Kulikov
1 parent 73a988b5

UI: Device profiles

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 +}
... ...
  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>
... ...
  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 { }
... ...
  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!"
... ...