Commit 1e87c728181c94d586e389a8485be8812af47ed2

Authored by Igor Kulikov
1 parent 3adbb481

UI: Device profile autocomplete. Device profile data.

Showing 22 changed files with 1237 additions and 7 deletions
... ... @@ -94,6 +94,8 @@ import { DeviceProfileDataComponent } from './profile/device-profile-data.compon
94 94 import { DeviceProfileComponent } from './profile/device-profile.component';
95 95 import { DefaultDeviceProfileTransportConfigurationComponent } from './profile/device/default-device-profile-transport-configuration.component';
96 96 import { DeviceProfileTransportConfigurationComponent } from './profile/device/device-profile-transport-configuration.component';
  97 +import { DeviceProfileDialogComponent } from './profile/device-profile-dialog.component';
  98 +import { DeviceProfileAutocompleteComponent } from './profile/device-profile-autocomplete.component';
97 99
98 100 @NgModule({
99 101 declarations:
... ... @@ -165,12 +167,14 @@ import { DeviceProfileTransportConfigurationComponent } from './profile/device/d
165 167 TenantProfileDataComponent,
166 168 TenantProfileComponent,
167 169 TenantProfileDialogComponent,
  170 + DeviceProfileAutocompleteComponent,
168 171 DefaultDeviceProfileConfigurationComponent,
169 172 DeviceProfileConfigurationComponent,
170 173 DefaultDeviceProfileTransportConfigurationComponent,
171 174 DeviceProfileTransportConfigurationComponent,
172 175 DeviceProfileDataComponent,
173   - DeviceProfileComponent
  176 + DeviceProfileComponent,
  177 + DeviceProfileDialogComponent
174 178 ],
175 179 imports: [
176 180 CommonModule,
... ... @@ -231,12 +235,14 @@ import { DeviceProfileTransportConfigurationComponent } from './profile/device/d
231 235 TenantProfileDataComponent,
232 236 TenantProfileComponent,
233 237 TenantProfileDialogComponent,
  238 + DeviceProfileAutocompleteComponent,
234 239 DefaultDeviceProfileConfigurationComponent,
235 240 DeviceProfileConfigurationComponent,
236 241 DefaultDeviceProfileTransportConfigurationComponent,
237 242 DeviceProfileTransportConfigurationComponent,
238 243 DeviceProfileDataComponent,
239   - DeviceProfileComponent
  244 + DeviceProfileComponent,
  245 + DeviceProfileDialogComponent
240 246 ],
241 247 providers: [
242 248 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 +<mat-form-field [formGroup]="selectDeviceProfileFormGroup" class="mat-block">
  19 + <input matInput type="text" placeholder="{{ 'device-profile.device-profile' | translate }}"
  20 + #deviceProfileInput
  21 + formControlName="deviceProfile"
  22 + [required]="required"
  23 + (keydown)="deviceProfileEnter($event)"
  24 + (keypress)="deviceProfileEnter($event)"
  25 + [matAutocomplete]="deviceProfileAutocomplete">
  26 + <button *ngIf="selectDeviceProfileFormGroup.get('deviceProfile').value && !disabled"
  27 + type="button"
  28 + matSuffix mat-button mat-icon-button aria-label="Clear"
  29 + (click)="clear()">
  30 + <mat-icon class="material-icons">close</mat-icon>
  31 + </button>
  32 + <button *ngIf="selectDeviceProfileFormGroup.get('deviceProfile').value && !disabled"
  33 + type="button"
  34 + matSuffix mat-button mat-icon-button aria-label="Edit"
  35 + matTooltip="{{ 'device-profile.edit' | translate }}"
  36 + matTooltipPosition="above"
  37 + (click)="editDeviceProfile($event)">
  38 + <mat-icon class="material-icons">edit</mat-icon>
  39 + </button>
  40 + <mat-autocomplete
  41 + class="tb-autocomplete"
  42 + #deviceProfileAutocomplete="matAutocomplete"
  43 + [displayWith]="displayDeviceProfileFn">
  44 + <mat-option *ngFor="let deviceProfile of filteredDeviceProfiles | async" [value]="deviceProfile">
  45 + <span [innerHTML]="deviceProfile.name | highlight:searchText"></span>
  46 + </mat-option>
  47 + <mat-option *ngIf="!(filteredDeviceProfiles | async)?.length" [value]="null" class="tb-not-found">
  48 + <div class="tb-not-found-content" (click)="$event.stopPropagation()">
  49 + <div *ngIf="!textIsNotEmpty(searchText); else searchNotEmpty">
  50 + <span translate>device-profile.no-device-profiles-found</span>
  51 + </div>
  52 + <ng-template #searchNotEmpty>
  53 + <span>
  54 + {{ translate.get('device-profile.no-device-profiles-matching',
  55 + {entity: truncate.transform(searchText, true, 6, &apos;...&apos;)}) | async }}
  56 + </span>
  57 + </ng-template>
  58 + <span>
  59 + <a translate (click)="createDeviceProfile($event, searchText)">device-profile.create-new-device-profile</a>
  60 + </span>
  61 + </div>
  62 + </mat-option>
  63 + </mat-autocomplete>
  64 + <mat-error *ngIf="selectDeviceProfileFormGroup.get('deviceProfile').hasError('required')">
  65 + {{ 'device-profile.device-profile-required' | translate }}
  66 + </mat-error>
  67 +</mat-form-field>
... ...
  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, ElementRef, EventEmitter, forwardRef, Input, OnInit, Output, ViewChild } from '@angular/core';
  18 +import { ControlValueAccessor, FormBuilder, FormGroup, NG_VALUE_ACCESSOR } from '@angular/forms';
  19 +import { Observable } from 'rxjs';
  20 +import { PageLink } from '@shared/models/page/page-link';
  21 +import { Direction } from '@shared/models/page/sort-order';
  22 +import { map, mergeMap, startWith, tap } from 'rxjs/operators';
  23 +import { Store } from '@ngrx/store';
  24 +import { AppState } from '@app/core/core.state';
  25 +import { TranslateService } from '@ngx-translate/core';
  26 +import { coerceBooleanProperty } from '@angular/cdk/coercion';
  27 +import { entityIdEquals } from '@shared/models/id/entity-id';
  28 +import { TruncatePipe } from '@shared//pipe/truncate.pipe';
  29 +import { ENTER } from '@angular/cdk/keycodes';
  30 +import { MatDialog } from '@angular/material/dialog';
  31 +import { DeviceProfileId } from '@shared/models/id/device-profile-id';
  32 +import {
  33 + createDeviceProfileConfiguration,
  34 + createDeviceProfileTransportConfiguration,
  35 + DeviceProfile,
  36 + DeviceProfileInfo,
  37 + DeviceProfileType,
  38 + DeviceTransportType
  39 +} from '@shared/models/device.models';
  40 +import { DeviceProfileService } from '@core/http/device-profile.service';
  41 +import { DeviceProfileDialogComponent, DeviceProfileDialogData } from './device-profile-dialog.component';
  42 +
  43 +@Component({
  44 + selector: 'tb-device-profile-autocomplete',
  45 + templateUrl: './device-profile-autocomplete.component.html',
  46 + styleUrls: [],
  47 + providers: [{
  48 + provide: NG_VALUE_ACCESSOR,
  49 + useExisting: forwardRef(() => DeviceProfileAutocompleteComponent),
  50 + multi: true
  51 + }]
  52 +})
  53 +export class DeviceProfileAutocompleteComponent implements ControlValueAccessor, OnInit {
  54 +
  55 + selectDeviceProfileFormGroup: FormGroup;
  56 +
  57 + modelValue: DeviceProfileId | null;
  58 +
  59 + @Input()
  60 + selectDefaultProfile = false;
  61 +
  62 + private requiredValue: boolean;
  63 + get required(): boolean {
  64 + return this.requiredValue;
  65 + }
  66 + @Input()
  67 + set required(value: boolean) {
  68 + this.requiredValue = coerceBooleanProperty(value);
  69 + }
  70 +
  71 + @Input()
  72 + disabled: boolean;
  73 +
  74 + @Output()
  75 + deviceProfileUpdated = new EventEmitter<DeviceProfileId>();
  76 +
  77 + @Output()
  78 + deviceProfileChanged = new EventEmitter<DeviceProfileInfo>();
  79 +
  80 + @ViewChild('deviceProfileInput', {static: true}) deviceProfileInput: ElementRef;
  81 +
  82 + filteredDeviceProfiles: Observable<Array<DeviceProfileInfo>>;
  83 +
  84 + searchText = '';
  85 +
  86 + private propagateChange = (v: any) => { };
  87 +
  88 + constructor(private store: Store<AppState>,
  89 + public translate: TranslateService,
  90 + public truncate: TruncatePipe,
  91 + private deviceProfileService: DeviceProfileService,
  92 + private fb: FormBuilder,
  93 + private dialog: MatDialog) {
  94 + this.selectDeviceProfileFormGroup = this.fb.group({
  95 + deviceProfile: [null]
  96 + });
  97 + }
  98 +
  99 + registerOnChange(fn: any): void {
  100 + this.propagateChange = fn;
  101 + }
  102 +
  103 + registerOnTouched(fn: any): void {
  104 + }
  105 +
  106 + ngOnInit() {
  107 + this.filteredDeviceProfiles = this.selectDeviceProfileFormGroup.get('deviceProfile').valueChanges
  108 + .pipe(
  109 + tap((value: DeviceProfileInfo | string) => {
  110 + let modelValue: DeviceProfileInfo | null;
  111 + if (typeof value === 'string' || !value) {
  112 + modelValue = null;
  113 + } else {
  114 + modelValue = value;
  115 + }
  116 + this.updateView(modelValue);
  117 + }),
  118 + startWith<string | DeviceProfileInfo>(''),
  119 + map(value => value ? (typeof value === 'string' ? value : value.name) : ''),
  120 + mergeMap(name => this.fetchDeviceProfiles(name) )
  121 + );
  122 + }
  123 +
  124 + selectDefaultDeviceProfileIfNeeded(): void {
  125 + if (this.selectDefaultProfile && !this.modelValue) {
  126 + this.deviceProfileService.getDefaultDeviceProfileInfo().subscribe(
  127 + (profile) => {
  128 + if (profile) {
  129 + this.selectDeviceProfileFormGroup.get('deviceProfile').patchValue(profile, {emitEvent: false});
  130 + this.updateView(profile);
  131 + }
  132 + }
  133 + );
  134 + }
  135 + }
  136 +
  137 + setDisabledState(isDisabled: boolean): void {
  138 + this.disabled = isDisabled;
  139 + }
  140 +
  141 + writeValue(value: DeviceProfileId | null): void {
  142 + this.searchText = '';
  143 + if (value != null) {
  144 + this.deviceProfileService.getDeviceProfileInfo(value.id).subscribe(
  145 + (profile) => {
  146 + this.modelValue = new DeviceProfileId(profile.id.id);
  147 + this.selectDeviceProfileFormGroup.get('deviceProfile').patchValue(profile, {emitEvent: true});
  148 + }
  149 + );
  150 + } else {
  151 + this.modelValue = null;
  152 + this.selectDeviceProfileFormGroup.get('deviceProfile').patchValue(null, {emitEvent: true});
  153 + this.selectDefaultDeviceProfileIfNeeded();
  154 + }
  155 + }
  156 +
  157 + updateView(deviceProfile: DeviceProfileInfo | null) {
  158 + const idValue = deviceProfile ? new DeviceProfileId(deviceProfile.id.id) : null;
  159 + if (!entityIdEquals(this.modelValue, idValue)) {
  160 + this.modelValue = idValue;
  161 + this.propagateChange(this.modelValue);
  162 + this.deviceProfileChanged.emit(deviceProfile);
  163 + }
  164 + }
  165 +
  166 + displayDeviceProfileFn(profile?: DeviceProfileInfo): string | undefined {
  167 + return profile ? profile.name : undefined;
  168 + }
  169 +
  170 + fetchDeviceProfiles(searchText?: string): Observable<Array<DeviceProfileInfo>> {
  171 + this.searchText = searchText;
  172 + const pageLink = new PageLink(10, 0, searchText, {
  173 + property: 'name',
  174 + direction: Direction.ASC
  175 + });
  176 + return this.deviceProfileService.getDeviceProfileInfos(pageLink, {ignoreLoading: true}).pipe(
  177 + map(pageData => {
  178 + return pageData.data;
  179 + })
  180 + );
  181 + }
  182 +
  183 + clear() {
  184 + this.selectDeviceProfileFormGroup.get('deviceProfile').patchValue(null, {emitEvent: true});
  185 + setTimeout(() => {
  186 + this.deviceProfileInput.nativeElement.blur();
  187 + this.deviceProfileInput.nativeElement.focus();
  188 + }, 0);
  189 + }
  190 +
  191 + textIsNotEmpty(text: string): boolean {
  192 + return (text && text.length > 0);
  193 + }
  194 +
  195 + deviceProfileEnter($event: KeyboardEvent) {
  196 + if ($event.keyCode === ENTER) {
  197 + $event.preventDefault();
  198 + if (!this.modelValue) {
  199 + this.createDeviceProfile($event, this.searchText);
  200 + }
  201 + }
  202 + }
  203 +
  204 + createDeviceProfile($event: Event, profileName: string) {
  205 + $event.preventDefault();
  206 + const deviceProfile: DeviceProfile = {
  207 + id: null,
  208 + name: profileName,
  209 + type: DeviceProfileType.DEFAULT,
  210 + transportType: DeviceTransportType.DEFAULT,
  211 + profileData: {
  212 + configuration: createDeviceProfileConfiguration(DeviceProfileType.DEFAULT),
  213 + transportConfiguration: createDeviceProfileTransportConfiguration(DeviceTransportType.DEFAULT)
  214 + }
  215 + };
  216 + this.openDeviceProfileDialog(deviceProfile, true);
  217 + }
  218 +
  219 + editDeviceProfile($event: Event) {
  220 + $event.preventDefault();
  221 + this.deviceProfileService.getDeviceProfile(this.modelValue.id).subscribe(
  222 + (deviceProfile) => {
  223 + this.openDeviceProfileDialog(deviceProfile, false);
  224 + }
  225 + );
  226 + }
  227 +
  228 + openDeviceProfileDialog(deviceProfile: DeviceProfile, isAdd: boolean) {
  229 + this.dialog.open<DeviceProfileDialogComponent, DeviceProfileDialogData,
  230 + DeviceProfile>(DeviceProfileDialogComponent, {
  231 + disableClose: true,
  232 + panelClass: ['tb-dialog', 'tb-fullscreen-dialog'],
  233 + data: {
  234 + isAdd,
  235 + deviceProfile
  236 + }
  237 + }).afterClosed().subscribe(
  238 + (savedDeviceProfile) => {
  239 + if (!savedDeviceProfile) {
  240 + setTimeout(() => {
  241 + this.deviceProfileInput.nativeElement.blur();
  242 + this.deviceProfileInput.nativeElement.focus();
  243 + }, 0);
  244 + } else {
  245 + this.deviceProfileService.getDeviceProfileInfo(savedDeviceProfile.id.id).subscribe(
  246 + (profile) => {
  247 + this.modelValue = new DeviceProfileId(profile.id.id);
  248 + this.selectDeviceProfileFormGroup.get('deviceProfile').patchValue(profile, {emitEvent: true});
  249 + if (isAdd) {
  250 + this.propagateChange(this.modelValue);
  251 + } else {
  252 + this.deviceProfileUpdated.next(savedDeviceProfile.id);
  253 + }
  254 + this.deviceProfileChanged.emit(profile);
  255 + }
  256 + );
  257 + }
  258 + }
  259 + );
  260 + }
  261 +}
... ...
  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 (ngSubmit)="save()" style="min-width: 600px;">
  19 + <mat-toolbar color="primary">
  20 + <h2>{{ (isAdd ? 'device-profile.add' : 'device-profile.edit' ) | translate }}</h2>
  21 + <span fxFlex></span>
  22 + <button mat-icon-button
  23 + (click)="cancel()"
  24 + type="button">
  25 + <mat-icon class="material-icons">close</mat-icon>
  26 + </button>
  27 + </mat-toolbar>
  28 + <mat-progress-bar color="warn" mode="indeterminate" *ngIf="isLoading$ | async">
  29 + </mat-progress-bar>
  30 + <div style="height: 4px;" *ngIf="!(isLoading$ | async)"></div>
  31 + <div mat-dialog-content>
  32 + <tb-device-profile
  33 + #deviceProfileComponent
  34 + [standalone]="true"
  35 + [entity]="deviceProfile"
  36 + [isEdit]="true">
  37 + </tb-device-profile>
  38 + </div>
  39 + <div mat-dialog-actions fxLayoutAlign="end center">
  40 + <button mat-raised-button color="primary"
  41 + type="submit"
  42 + [disabled]="(isLoading$ | async) || deviceProfileComponent.entityForm?.invalid || !deviceProfileComponent.entityForm?.dirty">
  43 + {{ (isAdd ? 'action.add' : 'action.save') | translate }}
  44 + </button>
  45 + <button mat-button color="primary"
  46 + type="button"
  47 + cdkFocusInitial
  48 + [disabled]="(isLoading$ | async)"
  49 + (click)="cancel()">
  50 + {{ 'action.cancel' | translate }}
  51 + </button>
  52 + </div>
  53 +</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 {
  18 + AfterViewInit,
  19 + Component,
  20 + ComponentFactoryResolver,
  21 + Inject,
  22 + Injector,
  23 + SkipSelf,
  24 + ViewChild
  25 +} from '@angular/core';
  26 +import { ErrorStateMatcher } from '@angular/material/core';
  27 +import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
  28 +import { Store } from '@ngrx/store';
  29 +import { AppState } from '@core/core.state';
  30 +import { FormControl, FormGroupDirective, NgForm } from '@angular/forms';
  31 +import { DialogComponent } from '@shared/components/dialog.component';
  32 +import { Router } from '@angular/router';
  33 +import { DeviceProfile } from '@shared/models/device.models';
  34 +import { DeviceProfileComponent } from './device-profile.component';
  35 +import { DeviceProfileService } from '@core/http/device-profile.service';
  36 +
  37 +export interface DeviceProfileDialogData {
  38 + deviceProfile: DeviceProfile;
  39 + isAdd: boolean;
  40 +}
  41 +
  42 +@Component({
  43 + selector: 'tb-device-profile-dialog',
  44 + templateUrl: './device-profile-dialog.component.html',
  45 + providers: [{provide: ErrorStateMatcher, useExisting: DeviceProfileDialogComponent}],
  46 + styleUrls: []
  47 +})
  48 +export class DeviceProfileDialogComponent extends
  49 + DialogComponent<DeviceProfileDialogComponent, DeviceProfile> implements ErrorStateMatcher, AfterViewInit {
  50 +
  51 + isAdd: boolean;
  52 + deviceProfile: DeviceProfile;
  53 +
  54 + submitted = false;
  55 +
  56 + @ViewChild('deviceProfileComponent', {static: true}) deviceProfileComponent: DeviceProfileComponent;
  57 +
  58 + constructor(protected store: Store<AppState>,
  59 + protected router: Router,
  60 + @Inject(MAT_DIALOG_DATA) public data: DeviceProfileDialogData,
  61 + public dialogRef: MatDialogRef<DeviceProfileDialogComponent, DeviceProfile>,
  62 + private componentFactoryResolver: ComponentFactoryResolver,
  63 + private injector: Injector,
  64 + @SkipSelf() private errorStateMatcher: ErrorStateMatcher,
  65 + private deviceProfileService: DeviceProfileService) {
  66 + super(store, router, dialogRef);
  67 + this.isAdd = this.data.isAdd;
  68 + this.deviceProfile = this.data.deviceProfile;
  69 + }
  70 +
  71 + ngAfterViewInit(): void {
  72 + if (this.isAdd) {
  73 + setTimeout(() => {
  74 + this.deviceProfileComponent.entityForm.markAsDirty();
  75 + }, 0);
  76 + }
  77 + }
  78 +
  79 + isErrorState(control: FormControl | null, form: FormGroupDirective | NgForm | null): boolean {
  80 + const originalErrorState = this.errorStateMatcher.isErrorState(control, form);
  81 + const customErrorState = !!(control && control.invalid && this.submitted);
  82 + return originalErrorState || customErrorState;
  83 + }
  84 +
  85 + cancel(): void {
  86 + this.dialogRef.close(null);
  87 + }
  88 +
  89 + save(): void {
  90 + this.submitted = true;
  91 + if (this.deviceProfileComponent.entityForm.valid) {
  92 + this.deviceProfile = {...this.deviceProfile, ...this.deviceProfileComponent.entityFormValue()};
  93 + this.deviceProfileService.saveDeviceProfile(this.deviceProfile).subscribe(
  94 + (deviceProfile) => {
  95 + this.dialogRef.close(deviceProfile);
  96 + }
  97 + );
  98 + }
  99 + }
  100 +
  101 +}
... ...
... ... @@ -18,10 +18,10 @@
18 18 <div [formGroup]="deviceProfileTransportConfigurationFormGroup">
19 19 <div [ngSwitch]="transportType">
20 20 <ng-template [ngSwitchCase]="deviceTransportType.DEFAULT">
21   - <tb-default-device-profile-configuration
  21 + <tb-default-device-profile-transport-configuration
22 22 [required]="required"
23 23 formControlName="configuration">
24   - </tb-default-device-profile-configuration>
  24 + </tb-default-device-profile-transport-configuration>
25 25 </ng-template>
26 26 </div>
27 27 </div>
... ...
  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]="defaultDeviceConfigurationFormGroup" 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 + DefaultDeviceConfiguration,
  24 + DeviceConfiguration,
  25 + DeviceProfileType
  26 +} from '@shared/models/device.models';
  27 +
  28 +@Component({
  29 + selector: 'tb-default-device-configuration',
  30 + templateUrl: './default-device-configuration.component.html',
  31 + styleUrls: [],
  32 + providers: [{
  33 + provide: NG_VALUE_ACCESSOR,
  34 + useExisting: forwardRef(() => DefaultDeviceConfigurationComponent),
  35 + multi: true
  36 + }]
  37 +})
  38 +export class DefaultDeviceConfigurationComponent implements ControlValueAccessor, OnInit {
  39 +
  40 + defaultDeviceConfigurationFormGroup: 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.defaultDeviceConfigurationFormGroup = this.fb.group({
  69 + configuration: [null, Validators.required]
  70 + });
  71 + this.defaultDeviceConfigurationFormGroup.valueChanges.subscribe(() => {
  72 + this.updateModel();
  73 + });
  74 + }
  75 +
  76 + setDisabledState(isDisabled: boolean): void {
  77 + this.disabled = isDisabled;
  78 + if (this.disabled) {
  79 + this.defaultDeviceConfigurationFormGroup.disable({emitEvent: false});
  80 + } else {
  81 + this.defaultDeviceConfigurationFormGroup.enable({emitEvent: false});
  82 + }
  83 + }
  84 +
  85 + writeValue(value: DefaultDeviceConfiguration | null): void {
  86 + this.defaultDeviceConfigurationFormGroup.patchValue({configuration: value}, {emitEvent: false});
  87 + }
  88 +
  89 + private updateModel() {
  90 + let configuration: DeviceConfiguration = null;
  91 + if (this.defaultDeviceConfigurationFormGroup.valid) {
  92 + configuration = this.defaultDeviceConfigurationFormGroup.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 +<form [formGroup]="defaultDeviceTransportConfigurationFormGroup" style="padding-bottom: 16px;">
  19 + <tb-json-object-edit
  20 + [required]="required"
  21 + label="{{ 'device-profile.transport-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 + DefaultDeviceTransportConfiguration,
  24 + DeviceTransportConfiguration,
  25 + DeviceTransportType
  26 +} from '@shared/models/device.models';
  27 +
  28 +@Component({
  29 + selector: 'tb-default-device-transport-configuration',
  30 + templateUrl: './default-device-transport-configuration.component.html',
  31 + styleUrls: [],
  32 + providers: [{
  33 + provide: NG_VALUE_ACCESSOR,
  34 + useExisting: forwardRef(() => DefaultDeviceTransportConfigurationComponent),
  35 + multi: true
  36 + }]
  37 +})
  38 +export class DefaultDeviceTransportConfigurationComponent implements ControlValueAccessor, OnInit {
  39 +
  40 + defaultDeviceTransportConfigurationFormGroup: 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.defaultDeviceTransportConfigurationFormGroup = this.fb.group({
  69 + configuration: [null, Validators.required]
  70 + });
  71 + this.defaultDeviceTransportConfigurationFormGroup.valueChanges.subscribe(() => {
  72 + this.updateModel();
  73 + });
  74 + }
  75 +
  76 + setDisabledState(isDisabled: boolean): void {
  77 + this.disabled = isDisabled;
  78 + if (this.disabled) {
  79 + this.defaultDeviceTransportConfigurationFormGroup.disable({emitEvent: false});
  80 + } else {
  81 + this.defaultDeviceTransportConfigurationFormGroup.enable({emitEvent: false});
  82 + }
  83 + }
  84 +
  85 + writeValue(value: DefaultDeviceTransportConfiguration | null): void {
  86 + this.defaultDeviceTransportConfigurationFormGroup.patchValue({configuration: value}, {emitEvent: false});
  87 + }
  88 +
  89 + private updateModel() {
  90 + let configuration: DeviceTransportConfiguration = null;
  91 + if (this.defaultDeviceTransportConfigurationFormGroup.valid) {
  92 + configuration = this.defaultDeviceTransportConfigurationFormGroup.getRawValue().configuration;
  93 + configuration.type = DeviceTransportType.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]="deviceConfigurationFormGroup">
  19 + <div [ngSwitch]="type">
  20 + <ng-template [ngSwitchCase]="deviceProfileType.DEFAULT">
  21 + <tb-default-device-configuration
  22 + [required]="required"
  23 + formControlName="configuration">
  24 + </tb-default-device-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 { DeviceConfiguration, DeviceProfileType } from '@shared/models/device.models';
  23 +import { deepClone } from '@core/utils';
  24 +
  25 +@Component({
  26 + selector: 'tb-device-configuration',
  27 + templateUrl: './device-configuration.component.html',
  28 + styleUrls: [],
  29 + providers: [{
  30 + provide: NG_VALUE_ACCESSOR,
  31 + useExisting: forwardRef(() => DeviceConfigurationComponent),
  32 + multi: true
  33 + }]
  34 +})
  35 +export class DeviceConfigurationComponent implements ControlValueAccessor, OnInit {
  36 +
  37 + deviceProfileType = DeviceProfileType;
  38 +
  39 + deviceConfigurationFormGroup: 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.deviceConfigurationFormGroup = this.fb.group({
  70 + configuration: [null, Validators.required]
  71 + });
  72 + this.deviceConfigurationFormGroup.valueChanges.subscribe(() => {
  73 + this.updateModel();
  74 + });
  75 + }
  76 +
  77 + setDisabledState(isDisabled: boolean): void {
  78 + this.disabled = isDisabled;
  79 + if (this.disabled) {
  80 + this.deviceConfigurationFormGroup.disable({emitEvent: false});
  81 + } else {
  82 + this.deviceConfigurationFormGroup.enable({emitEvent: false});
  83 + }
  84 + }
  85 +
  86 + writeValue(value: DeviceConfiguration | null): void {
  87 + this.type = value?.type;
  88 + const configuration = deepClone(value);
  89 + if (configuration) {
  90 + delete configuration.type;
  91 + }
  92 + this.deviceConfigurationFormGroup.patchValue({configuration}, {emitEvent: false});
  93 + }
  94 +
  95 + private updateModel() {
  96 + let configuration: DeviceConfiguration = null;
  97 + if (this.deviceConfigurationFormGroup.valid) {
  98 + configuration = this.deviceConfigurationFormGroup.getRawValue().configuration;
  99 + configuration.type = this.type;
  100 + }
  101 + this.propagateChange(configuration);
  102 + }
  103 +}
... ...
  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]="deviceDataFormGroup" style="padding-bottom: 16px;">
  19 + <mat-accordion multi="true">
  20 + <mat-expansion-panel [expanded]="true">
  21 + <mat-expansion-panel-header>
  22 + <mat-panel-title>
  23 + <div translate>device.device-configuration</div>
  24 + </mat-panel-title>
  25 + </mat-expansion-panel-header>
  26 + <tb-device-configuration
  27 + formControlName="configuration"
  28 + required>
  29 + </tb-device-configuration>
  30 + </mat-expansion-panel>
  31 + <mat-expansion-panel [expanded]="true">
  32 + <mat-expansion-panel-header>
  33 + <mat-panel-title>
  34 + <div translate>device.transport-configuration</div>
  35 + </mat-panel-title>
  36 + </mat-expansion-panel-header>
  37 + <tb-device-transport-configuration
  38 + formControlName="transportConfiguration"
  39 + required>
  40 + </tb-device-transport-configuration>
  41 + </mat-expansion-panel>
  42 + </mat-accordion>
  43 +</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 { DeviceData } from '@shared/models/device.models';
  23 +
  24 +@Component({
  25 + selector: 'tb-device-data',
  26 + templateUrl: './device-data.component.html',
  27 + styleUrls: [],
  28 + providers: [{
  29 + provide: NG_VALUE_ACCESSOR,
  30 + useExisting: forwardRef(() => DeviceDataComponent),
  31 + multi: true
  32 + }]
  33 +})
  34 +export class DeviceDataComponent implements ControlValueAccessor, OnInit {
  35 +
  36 + deviceDataFormGroup: 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.deviceDataFormGroup = this.fb.group({
  65 + configuration: [null, Validators.required],
  66 + transportConfiguration: [null, Validators.required]
  67 + });
  68 + this.deviceDataFormGroup.valueChanges.subscribe(() => {
  69 + this.updateModel();
  70 + });
  71 + }
  72 +
  73 + setDisabledState(isDisabled: boolean): void {
  74 + this.disabled = isDisabled;
  75 + if (this.disabled) {
  76 + this.deviceDataFormGroup.disable({emitEvent: false});
  77 + } else {
  78 + this.deviceDataFormGroup.enable({emitEvent: false});
  79 + }
  80 + }
  81 +
  82 + writeValue(value: DeviceData | null): void {
  83 + this.deviceDataFormGroup.patchValue({configuration: value?.configuration}, {emitEvent: false});
  84 + this.deviceDataFormGroup.patchValue({transportConfiguration: value?.transportConfiguration}, {emitEvent: false});
  85 + }
  86 +
  87 + private updateModel() {
  88 + let deviceData: DeviceData = null;
  89 + if (this.deviceDataFormGroup.valid) {
  90 + deviceData = this.deviceDataFormGroup.getRawValue();
  91 + }
  92 + this.propagateChange(deviceData);
  93 + }
  94 +}
... ...
  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]="deviceTransportConfigurationFormGroup">
  19 + <div [ngSwitch]="transportType">
  20 + <ng-template [ngSwitchCase]="deviceTransportType.DEFAULT">
  21 + <tb-default-device-transport-configuration
  22 + [required]="required"
  23 + formControlName="configuration">
  24 + </tb-default-device-transport-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 {
  23 + DeviceTransportConfiguration,
  24 + DeviceTransportType
  25 +} from '@shared/models/device.models';
  26 +import { deepClone } from '@core/utils';
  27 +
  28 +@Component({
  29 + selector: 'tb-device-transport-configuration',
  30 + templateUrl: './device-transport-configuration.component.html',
  31 + styleUrls: [],
  32 + providers: [{
  33 + provide: NG_VALUE_ACCESSOR,
  34 + useExisting: forwardRef(() => DeviceTransportConfigurationComponent),
  35 + multi: true
  36 + }]
  37 +})
  38 +export class DeviceTransportConfigurationComponent implements ControlValueAccessor, OnInit {
  39 +
  40 + deviceTransportType = DeviceTransportType;
  41 +
  42 + deviceTransportConfigurationFormGroup: FormGroup;
  43 +
  44 + private requiredValue: boolean;
  45 + get required(): boolean {
  46 + return this.requiredValue;
  47 + }
  48 + @Input()
  49 + set required(value: boolean) {
  50 + this.requiredValue = coerceBooleanProperty(value);
  51 + }
  52 +
  53 + @Input()
  54 + disabled: boolean;
  55 +
  56 + transportType: DeviceTransportType;
  57 +
  58 + private propagateChange = (v: any) => { };
  59 +
  60 + constructor(private store: Store<AppState>,
  61 + private fb: FormBuilder) {
  62 + }
  63 +
  64 + registerOnChange(fn: any): void {
  65 + this.propagateChange = fn;
  66 + }
  67 +
  68 + registerOnTouched(fn: any): void {
  69 + }
  70 +
  71 + ngOnInit() {
  72 + this.deviceTransportConfigurationFormGroup = this.fb.group({
  73 + configuration: [null, Validators.required]
  74 + });
  75 + this.deviceTransportConfigurationFormGroup.valueChanges.subscribe(() => {
  76 + this.updateModel();
  77 + });
  78 + }
  79 +
  80 + setDisabledState(isDisabled: boolean): void {
  81 + this.disabled = isDisabled;
  82 + if (this.disabled) {
  83 + this.deviceTransportConfigurationFormGroup.disable({emitEvent: false});
  84 + } else {
  85 + this.deviceTransportConfigurationFormGroup.enable({emitEvent: false});
  86 + }
  87 + }
  88 +
  89 + writeValue(value: DeviceTransportConfiguration | null): void {
  90 + this.transportType = value?.type;
  91 + const configuration = deepClone(value);
  92 + if (configuration) {
  93 + delete configuration.type;
  94 + }
  95 + this.deviceTransportConfigurationFormGroup.patchValue({configuration}, {emitEvent: false});
  96 + }
  97 +
  98 + private updateModel() {
  99 + let configuration: DeviceTransportConfiguration = null;
  100 + if (this.deviceTransportConfigurationFormGroup.valid) {
  101 + configuration = this.deviceTransportConfigurationFormGroup.getRawValue().configuration;
  102 + configuration.type = this.transportType;
  103 + }
  104 + this.propagateChange(configuration);
  105 + }
  106 +}
... ...
... ... @@ -83,6 +83,13 @@
83 83 {{ 'device.name-required' | translate }}
84 84 </mat-error>
85 85 </mat-form-field>
  86 + <tb-device-profile-autocomplete
  87 + [selectDefaultProfile]="isAdd"
  88 + required
  89 + formControlName="deviceProfileId"
  90 + (deviceProfileUpdated)="onDeviceProfileUpdated()"
  91 + (deviceProfileChanged)="onDeviceProfileChanged($event)">
  92 + </tb-device-profile-autocomplete>
86 93 <tb-entity-subtype-autocomplete
87 94 formControlName="type"
88 95 [required]="true"
... ... @@ -93,6 +100,10 @@
93 100 <mat-label translate>device.label</mat-label>
94 101 <input matInput formControlName="label">
95 102 </mat-form-field>
  103 + <tb-device-data
  104 + formControlName="deviceData"
  105 + required>
  106 + </tb-device-data>
96 107 <div formGroupName="additionalInfo" fxLayout="column">
97 108 <mat-checkbox fxFlex formControlName="gateway" style="padding-bottom: 16px;">
98 109 {{ 'device.is-gateway' | translate }}
... ...
... ... @@ -19,7 +19,16 @@ import { Store } from '@ngrx/store';
19 19 import { AppState } from '@core/core.state';
20 20 import { EntityComponent } from '../../components/entity/entity.component';
21 21 import { FormBuilder, FormGroup, Validators } from '@angular/forms';
22   -import { DeviceInfo } from '@shared/models/device.models';
  22 +import {
  23 + createDeviceConfiguration,
  24 + createDeviceProfileConfiguration, createDeviceTransportConfiguration,
  25 + DeviceData,
  26 + DeviceInfo,
  27 + DeviceProfileData,
  28 + DeviceProfileInfo,
  29 + DeviceProfileType,
  30 + DeviceTransportType
  31 +} from '@shared/models/device.models';
23 32 import { EntityType } from '@shared/models/entity-type.models';
24 33 import { NULL_UUID } from '@shared/models/id/has-uuid';
25 34 import { ActionNotificationShow } from '@core/notification/notification.actions';
... ... @@ -126,4 +135,36 @@ export class DeviceComponent extends EntityComponent<DeviceInfo> {
126 135 );
127 136 }
128 137 }
  138 +
  139 + onDeviceProfileUpdated() {
  140 + this.entitiesTableConfig.table.updateData(false);
  141 + }
  142 +
  143 + onDeviceProfileChanged(deviceProfile: DeviceProfileInfo) {
  144 + if (deviceProfile) {
  145 + const deviceProfileType: DeviceProfileType = deviceProfile.type;
  146 + const deviceTransportType: DeviceTransportType = deviceProfile.transportType;
  147 + let deviceData: DeviceData = this.entityForm.getRawValue().deviceData;
  148 + if (!deviceData) {
  149 + deviceData = {
  150 + configuration: createDeviceConfiguration(deviceProfileType),
  151 + transportConfiguration: createDeviceTransportConfiguration(deviceTransportType)
  152 + };
  153 + this.entityForm.patchValue({deviceData});
  154 + } else {
  155 + let changed = false;
  156 + if (deviceData.configuration.type !== deviceProfileType) {
  157 + deviceData.configuration = createDeviceConfiguration(deviceProfileType);
  158 + changed = true;
  159 + }
  160 + if (deviceData.transportConfiguration.type !== deviceTransportType) {
  161 + deviceData.transportConfiguration = createDeviceTransportConfiguration(deviceTransportType);
  162 + changed = true;
  163 + }
  164 + if (changed) {
  165 + this.entityForm.patchValue({deviceData});
  166 + }
  167 + }
  168 + }
  169 + }
129 170 }
... ...
... ... @@ -24,9 +24,19 @@ import { DeviceCredentialsDialogComponent } from '@modules/home/pages/device/dev
24 24 import { HomeDialogsModule } from '../../dialogs/home-dialogs.module';
25 25 import { HomeComponentsModule } from '@modules/home/components/home-components.module';
26 26 import { DeviceTabsComponent } from '@home/pages/device/device-tabs.component';
  27 +import { DefaultDeviceConfigurationComponent } from './data/default-device-configuration.component';
  28 +import { DeviceConfigurationComponent } from './data/device-configuration.component';
  29 +import { DeviceDataComponent } from './data/device-data.component';
  30 +import { DefaultDeviceTransportConfigurationComponent } from './data/default-device-transport-configuration.component';
  31 +import { DeviceTransportConfigurationComponent } from './data/device-transport-configuration.component';
27 32
28 33 @NgModule({
29 34 declarations: [
  35 + DefaultDeviceConfigurationComponent,
  36 + DeviceConfigurationComponent,
  37 + DefaultDeviceTransportConfigurationComponent,
  38 + DeviceTransportConfigurationComponent,
  39 + DeviceDataComponent,
30 40 DeviceComponent,
31 41 DeviceTabsComponent,
32 42 DeviceTableHeaderComponent,
... ...
... ... @@ -86,6 +86,8 @@ export class DevicesTableConfigResolver implements Resolve<EntityTableConfig<Dev
86 86 this.config.entityTranslations = entityTypeTranslations.get(EntityType.DEVICE);
87 87 this.config.entityResources = entityTypeResources.get(EntityType.DEVICE);
88 88
  89 + this.config.addDialogStyle = {width: '600px'};
  90 +
89 91 this.config.deleteEntityTitle = device => this.translate.instant('device.delete-device-title', { deviceName: device.name });
90 92 this.config.deleteEntityContent = () => this.translate.instant('device.delete-device-text');
91 93 this.config.deleteEntitiesTitle = count => this.translate.instant('device.delete-devices-title', {count});
... ...
... ... @@ -91,6 +91,19 @@ export function createDeviceProfileConfiguration(type: DeviceProfileType): Devic
91 91 return configuration;
92 92 }
93 93
  94 +export function createDeviceConfiguration(type: DeviceProfileType): DeviceConfiguration {
  95 + let configuration: DeviceConfiguration = null;
  96 + if (type) {
  97 + switch (type) {
  98 + case DeviceProfileType.DEFAULT:
  99 + const defaultConfiguration: DefaultDeviceConfiguration = {};
  100 + configuration = {...defaultConfiguration, type: DeviceProfileType.DEFAULT};
  101 + break;
  102 + }
  103 + }
  104 + return configuration;
  105 +}
  106 +
94 107 export function createDeviceProfileTransportConfiguration(type: DeviceTransportType): DeviceProfileTransportConfiguration {
95 108 let transportConfiguration: DeviceProfileTransportConfiguration = null;
96 109 if (type) {
... ... @@ -112,6 +125,27 @@ export function createDeviceProfileTransportConfiguration(type: DeviceTransportT
112 125 return transportConfiguration;
113 126 }
114 127
  128 +export function createDeviceTransportConfiguration(type: DeviceTransportType): DeviceTransportConfiguration {
  129 + let transportConfiguration: DeviceTransportConfiguration = null;
  130 + if (type) {
  131 + switch (type) {
  132 + case DeviceTransportType.DEFAULT:
  133 + const defaultTransportConfiguration: DefaultDeviceTransportConfiguration = {};
  134 + transportConfiguration = {...defaultTransportConfiguration, type: DeviceTransportType.DEFAULT};
  135 + break;
  136 + case DeviceTransportType.MQTT:
  137 + const mqttTransportConfiguration: MqttDeviceTransportConfiguration = {};
  138 + transportConfiguration = {...mqttTransportConfiguration, type: DeviceTransportType.MQTT};
  139 + break;
  140 + case DeviceTransportType.LWM2M:
  141 + const lwm2mTransportConfiguration: Lwm2mDeviceTransportConfiguration = {};
  142 + transportConfiguration = {...lwm2mTransportConfiguration, type: DeviceTransportType.LWM2M};
  143 + break;
  144 + }
  145 + }
  146 + return transportConfiguration;
  147 +}
  148 +
115 149 export interface DeviceProfileData {
116 150 configuration: DeviceProfileConfiguration;
117 151 transportConfiguration: DeviceProfileTransportConfiguration;
... ... @@ -121,7 +155,7 @@ export interface DeviceProfile extends BaseData<DeviceProfileId> {
121 155 tenantId?: TenantId;
122 156 name: string;
123 157 description?: string;
124   - default: boolean;
  158 + default?: boolean;
125 159 type: DeviceProfileType;
126 160 transportType: DeviceTransportType;
127 161 defaultRuleChainId?: RuleChainId;
... ...
... ... @@ -748,7 +748,9 @@
748 748 "import": "Import device",
749 749 "device-file": "Device file",
750 750 "search": "Search devices",
751   - "selected-devices": "{ count, plural, 1 {1 device} other {# devices} } selected"
  751 + "selected-devices": "{ count, plural, 1 {1 device} other {# devices} } selected",
  752 + "device-configuration": "Device configuration",
  753 + "transport-configuration": "Transport configuration"
752 754 },
753 755 "device-profile": {
754 756 "device-profile": "Device profile",
... ...