Commit e31a95619c0cfffb9094c8be8ff37c8062dcc9cf

Authored by Vladyslav_Prykhodko
1 parent 45fc32ce

UI: Added firmware autocomplete component; Add firmware to device and device profile

@@ -74,6 +74,7 @@ import { @@ -74,6 +74,7 @@ import {
74 StringOperation 74 StringOperation
75 } from '@shared/models/query/query.models'; 75 } from '@shared/models/query/query.models';
76 import { alarmFields } from '@shared/models/alarm.models'; 76 import { alarmFields } from '@shared/models/alarm.models';
  77 +import { FirmwareService } from '@core/http/firmware.service';
77 78
78 @Injectable({ 79 @Injectable({
79 providedIn: 'root' 80 providedIn: 'root'
@@ -93,6 +94,7 @@ export class EntityService { @@ -93,6 +94,7 @@ export class EntityService {
93 private dashboardService: DashboardService, 94 private dashboardService: DashboardService,
94 private entityRelationService: EntityRelationService, 95 private entityRelationService: EntityRelationService,
95 private attributeService: AttributeService, 96 private attributeService: AttributeService,
  97 + private firmwareService: FirmwareService,
96 private utils: UtilsService 98 private utils: UtilsService
97 ) { } 99 ) { }
98 100
@@ -128,6 +130,9 @@ export class EntityService { @@ -128,6 +130,9 @@ export class EntityService {
128 case EntityType.ALARM: 130 case EntityType.ALARM:
129 console.error('Get Alarm Entity is not implemented!'); 131 console.error('Get Alarm Entity is not implemented!');
130 break; 132 break;
  133 + case EntityType.FIRMWARE:
  134 + observable = this.firmwareService.getFirmwareInfo(entityId, config);
  135 + break;
131 } 136 }
132 return observable; 137 return observable;
133 } 138 }
@@ -326,6 +331,10 @@ export class EntityService { @@ -326,6 +331,10 @@ export class EntityService {
326 case EntityType.ALARM: 331 case EntityType.ALARM:
327 console.error('Get Alarm Entities is not implemented!'); 332 console.error('Get Alarm Entities is not implemented!');
328 break; 333 break;
  334 + case EntityType.FIRMWARE:
  335 + pageLink.sortOrder.property = 'title';
  336 + entitiesObservable = this.firmwareService.getFirmwares(pageLink, true, config);
  337 + break;
329 } 338 }
330 return entitiesObservable; 339 return entitiesObservable;
331 } 340 }
@@ -23,8 +23,6 @@ import { PageData } from '@shared/models/page/page-data'; @@ -23,8 +23,6 @@ import { PageData } from '@shared/models/page/page-data';
23 import { Firmware, FirmwareInfo } from '@shared/models/firmware.models'; 23 import { Firmware, FirmwareInfo } from '@shared/models/firmware.models';
24 import { catchError, map, mergeMap } from 'rxjs/operators'; 24 import { catchError, map, mergeMap } from 'rxjs/operators';
25 import { deepClone, isDefinedAndNotNull } from '@core/utils'; 25 import { deepClone, isDefinedAndNotNull } from '@core/utils';
26 -import { InterceptorHttpParams } from '@core/interceptors/interceptor-http-params';  
27 -import { InterceptorConfig } from '@core/interceptors/interceptor-config';  
28 26
29 @Injectable({ 27 @Injectable({
30 providedIn: 'root' 28 providedIn: 'root'
@@ -37,10 +35,11 @@ export class FirmwareService { @@ -37,10 +35,11 @@ export class FirmwareService {
37 } 35 }
38 36
39 public getFirmwares(pageLink: PageLink, hasData?: boolean, config?: RequestConfig): Observable<PageData<FirmwareInfo>> { 37 public getFirmwares(pageLink: PageLink, hasData?: boolean, config?: RequestConfig): Observable<PageData<FirmwareInfo>> {
40 - let url = `/api/firmwares${pageLink.toQuery()}`; 38 + let url = `/api/firmwares`;
41 if (isDefinedAndNotNull(hasData)) { 39 if (isDefinedAndNotNull(hasData)) {
42 - url += `&hasData=${hasData}`; 40 + url += `/${hasData}`;
43 } 41 }
  42 + url += `${pageLink.toQuery()}`;
44 return this.http.get<PageData<FirmwareInfo>>(url, defaultHttpOptionsFromConfig(config)); 43 return this.http.get<PageData<FirmwareInfo>>(url, defaultHttpOptionsFromConfig(config));
45 } 44 }
46 45
  1 +<!--
  2 +
  3 + Copyright © 2016-2021 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]="firmwareFormGroup" class="mat-block">
  19 + <input matInput type="text" placeholder="{{ placeholderText | translate }}"
  20 + #firmwareInput
  21 + formControlName="firmwareId"
  22 + (focusin)="onFocus()"
  23 + [required]="required"
  24 + [matAutocomplete]="firmwareAutocomplete">
  25 + <button *ngIf="firmwareFormGroup.get('firmwareId').value && !disabled"
  26 + type="button"
  27 + matSuffix mat-button mat-icon-button aria-label="Clear"
  28 + (click)="clear()">
  29 + <mat-icon class="material-icons">close</mat-icon>
  30 + </button>
  31 + <mat-autocomplete class="tb-autocomplete"
  32 + #firmwareAutocomplete="matAutocomplete"
  33 + [displayWith]="displayFirmwareFn">
  34 + <mat-option *ngFor="let firmware of filteredFirmwares | async" [value]="firmware">
  35 + <span [innerHTML]="this.firmwareTitleText(firmware) | highlight:searchText"></span>
  36 + </mat-option>
  37 + <mat-option *ngIf="!(filteredFirmwares | async)?.length" [value]="null" class="tb-not-found">
  38 + <div class="tb-not-found-content" (click)="$event.stopPropagation()">
  39 + <div *ngIf="!textIsNotEmpty(searchText); else searchNotEmpty">
  40 + <span translate>firmware.no-firmware-text</span>
  41 + </div>
  42 + <ng-template #searchNotEmpty>
  43 + <span>
  44 + {{ translate.get('firmware.no-firmware-matching',
  45 + {entity: truncate.transform(searchText, true, 6, &apos;...&apos;)}) | async }}
  46 + </span>
  47 + </ng-template>
  48 + </div>
  49 + </mat-option>
  50 + </mat-autocomplete>
  51 + <mat-error *ngIf="firmwareFormGroup.get('firmwareId').hasError('required')">
  52 + {{ requiredErrorText | translate }}
  53 + </mat-error>
  54 +</mat-form-field>
  1 +///
  2 +/// Copyright © 2016-2021 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, forwardRef, Input, OnInit, ViewChild } from '@angular/core';
  18 +import { ControlValueAccessor, FormBuilder, FormGroup, NG_VALUE_ACCESSOR } from '@angular/forms';
  19 +import { Observable } from 'rxjs';
  20 +import { map, mergeMap, share, tap } from 'rxjs/operators';
  21 +import { Store } from '@ngrx/store';
  22 +import { AppState } from '@core/core.state';
  23 +import { TranslateService } from '@ngx-translate/core';
  24 +import { coerceBooleanProperty } from '@angular/cdk/coercion';
  25 +import { EntityId } from '@shared/models/id/entity-id';
  26 +import { EntityType } from '@shared/models/entity-type.models';
  27 +import { BaseData } from '@shared/models/base-data';
  28 +import { EntityService } from '@core/http/entity.service';
  29 +import { TruncatePipe } from '@shared/pipe/truncate.pipe';
  30 +import { MatAutocompleteTrigger } from '@angular/material/autocomplete';
  31 +import { FirmwareInfo } from '@shared/models/firmware.models';
  32 +import { FirmwareService } from '@core/http/firmware.service';
  33 +import { PageLink } from '@shared/models/page/page-link';
  34 +import { Direction } from '@shared/models/page/sort-order';
  35 +
  36 +@Component({
  37 + selector: 'tb-firmware-autocomplete',
  38 + templateUrl: './firmware-autocomplete.component.html',
  39 + styleUrls: [],
  40 + providers: [{
  41 + provide: NG_VALUE_ACCESSOR,
  42 + useExisting: forwardRef(() => FirmwareAutocompleteComponent),
  43 + multi: true
  44 + }]
  45 +})
  46 +export class FirmwareAutocompleteComponent implements ControlValueAccessor, OnInit {
  47 +
  48 + firmwareFormGroup: FormGroup;
  49 +
  50 + modelValue: string | null;
  51 +
  52 + @Input()
  53 + labelText: string;
  54 +
  55 + @Input()
  56 + requiredText: string;
  57 +
  58 + @Input()
  59 + useFullEntityId = false;
  60 +
  61 + private requiredValue: boolean;
  62 +
  63 + get required(): boolean {
  64 + return this.requiredValue;
  65 + }
  66 +
  67 + @Input()
  68 + set required(value: boolean) {
  69 + this.requiredValue = coerceBooleanProperty(value);
  70 + }
  71 +
  72 + @Input()
  73 + disabled: boolean;
  74 +
  75 + @ViewChild('firmwareInput', {static: true}) firmwareInput: ElementRef;
  76 + @ViewChild('firmwareInput', {read: MatAutocompleteTrigger}) firmwareAutocomplete: MatAutocompleteTrigger;
  77 +
  78 + filteredFirmwares: Observable<Array<FirmwareInfo>>;
  79 +
  80 + searchText = '';
  81 +
  82 + private dirty = false;
  83 +
  84 + private propagateChange = (v: any) => { };
  85 +
  86 + constructor(private store: Store<AppState>,
  87 + public translate: TranslateService,
  88 + public truncate: TruncatePipe,
  89 + private entityService: EntityService,
  90 + private firmwareService: FirmwareService,
  91 + private fb: FormBuilder) {
  92 + this.firmwareFormGroup = this.fb.group({
  93 + firmwareId: [null]
  94 + });
  95 + }
  96 +
  97 + registerOnChange(fn: any): void {
  98 + this.propagateChange = fn;
  99 + }
  100 +
  101 + registerOnTouched(fn: any): void {
  102 + }
  103 +
  104 + ngOnInit() {
  105 + this.filteredFirmwares = this.firmwareFormGroup.get('firmwareId').valueChanges
  106 + .pipe(
  107 + tap(value => {
  108 + let modelValue;
  109 + if (typeof value === 'string' || !value) {
  110 + modelValue = null;
  111 + } else {
  112 + modelValue = this.useFullEntityId ? value.id : value.id.id;
  113 + }
  114 + this.updateView(modelValue);
  115 + if (value === null) {
  116 + this.clear();
  117 + }
  118 + }),
  119 + map(value => value ? (typeof value === 'string' ? value : value.title) : ''),
  120 + mergeMap(name => this.fetchFirmware(name)),
  121 + share()
  122 + );
  123 + }
  124 +
  125 + ngAfterViewInit(): void {
  126 + }
  127 +
  128 + getCurrentEntity(): BaseData<EntityId> | null {
  129 + const currentRuleChain = this.firmwareFormGroup.get('firmwareId').value;
  130 + if (currentRuleChain && typeof currentRuleChain !== 'string') {
  131 + return currentRuleChain as BaseData<EntityId>;
  132 + } else {
  133 + return null;
  134 + }
  135 + }
  136 +
  137 + setDisabledState(isDisabled: boolean): void {
  138 + this.disabled = isDisabled;
  139 + if (this.disabled) {
  140 + this.firmwareFormGroup.disable({emitEvent: false});
  141 + } else {
  142 + this.firmwareFormGroup.enable({emitEvent: false});
  143 + }
  144 + }
  145 +
  146 + textIsNotEmpty(text: string): boolean {
  147 + return (text && text.length > 0);
  148 + }
  149 +
  150 + writeValue(value: string | EntityId | null): void {
  151 + this.searchText = '';
  152 + if (value != null && value !== '') {
  153 + let firmwareId = '';
  154 + if (typeof value === 'string') {
  155 + firmwareId = value;
  156 + } else if (value.entityType && value.id) {
  157 + firmwareId = value.id;
  158 + }
  159 + if (firmwareId !== '') {
  160 + this.entityService.getEntity(EntityType.FIRMWARE, firmwareId, {ignoreLoading: true, ignoreErrors: true}).subscribe(
  161 + (entity) => {
  162 + this.modelValue = entity.id.id;
  163 + this.firmwareFormGroup.get('firmwareId').patchValue(entity, {emitEvent: false});
  164 + },
  165 + () => {
  166 + this.modelValue = null;
  167 + this.firmwareFormGroup.get('firmwareId').patchValue('', {emitEvent: false});
  168 + if (value !== null) {
  169 + this.propagateChange(this.modelValue);
  170 + }
  171 + }
  172 + );
  173 + } else {
  174 + this.modelValue = null;
  175 + this.firmwareFormGroup.get('firmwareId').patchValue('', {emitEvent: false});
  176 + }
  177 + } else {
  178 + this.modelValue = null;
  179 + this.firmwareFormGroup.get('firmwareId').patchValue('', {emitEvent: false});
  180 + }
  181 + this.dirty = true;
  182 + }
  183 +
  184 + onFocus() {
  185 + if (this.dirty) {
  186 + this.firmwareFormGroup.get('firmwareId').updateValueAndValidity({onlySelf: true, emitEvent: true});
  187 + this.dirty = false;
  188 + }
  189 + }
  190 +
  191 + reset() {
  192 + this.firmwareFormGroup.get('firmwareId').patchValue('', {emitEvent: false});
  193 + }
  194 +
  195 + updateView(value: string | null) {
  196 + if (this.modelValue !== value) {
  197 + this.modelValue = value;
  198 + this.propagateChange(this.modelValue);
  199 + }
  200 + }
  201 +
  202 + displayFirmwareFn(firmware?: FirmwareInfo): string | undefined {
  203 + return firmware ? `${firmware.title} (${firmware.version})` : undefined;
  204 + }
  205 +
  206 + fetchFirmware(searchText?: string): Observable<Array<FirmwareInfo>> {
  207 + this.searchText = searchText;
  208 + const pageLink = new PageLink(50, 0, searchText, {
  209 + property: 'title',
  210 + direction: Direction.ASC
  211 + });
  212 + return this.firmwareService.getFirmwares(pageLink, true, {ignoreLoading: true}).pipe(
  213 + map((data) => data && data.data.length ? data.data : null)
  214 + );
  215 + }
  216 +
  217 + clear() {
  218 + this.firmwareFormGroup.get('firmwareId').patchValue('', {emitEvent: true});
  219 + setTimeout(() => {
  220 + this.firmwareInput.nativeElement.blur();
  221 + this.firmwareInput.nativeElement.focus();
  222 + }, 0);
  223 + }
  224 +
  225 + get placeholderText(): string {
  226 + return this.labelText || 'firmware.firmware';
  227 + }
  228 +
  229 + get requiredErrorText(): string {
  230 + return this.requiredText || 'firmware.firmware-required';
  231 + }
  232 +
  233 + firmwareTitleText(firmware: FirmwareInfo): string {
  234 + return `${firmware.title} (${firmware.version})`;
  235 + }
  236 +}
@@ -134,6 +134,7 @@ import { DashboardStateDialogComponent } from '@home/components/dashboard-page/s @@ -134,6 +134,7 @@ import { DashboardStateDialogComponent } from '@home/components/dashboard-page/s
134 import { EmbedDashboardDialogComponent } from '@home/components/widget/dialog/embed-dashboard-dialog.component'; 134 import { EmbedDashboardDialogComponent } from '@home/components/widget/dialog/embed-dashboard-dialog.component';
135 import { EMBED_DASHBOARD_DIALOG_TOKEN } from '@home/components/widget/dialog/embed-dashboard-dialog-token'; 135 import { EMBED_DASHBOARD_DIALOG_TOKEN } from '@home/components/widget/dialog/embed-dashboard-dialog-token';
136 import { DisplayWidgetTypesPanelComponent } from '@home/components/dashboard-page/widget-types-panel.component'; 136 import { DisplayWidgetTypesPanelComponent } from '@home/components/dashboard-page/widget-types-panel.component';
  137 +import { FirmwareAutocompleteComponent } from '@home/components/firmware/firmware-autocomplete.component';
137 138
138 @NgModule({ 139 @NgModule({
139 declarations: 140 declarations:
@@ -247,7 +248,8 @@ import { DisplayWidgetTypesPanelComponent } from '@home/components/dashboard-pag @@ -247,7 +248,8 @@ import { DisplayWidgetTypesPanelComponent } from '@home/components/dashboard-pag
247 ManageDashboardStatesDialogComponent, 248 ManageDashboardStatesDialogComponent,
248 DashboardStateDialogComponent, 249 DashboardStateDialogComponent,
249 EmbedDashboardDialogComponent, 250 EmbedDashboardDialogComponent,
250 - DisplayWidgetTypesPanelComponent 251 + DisplayWidgetTypesPanelComponent,
  252 + FirmwareAutocompleteComponent
251 ], 253 ],
252 imports: [ 254 imports: [
253 CommonModule, 255 CommonModule,
@@ -351,7 +353,8 @@ import { DisplayWidgetTypesPanelComponent } from '@home/components/dashboard-pag @@ -351,7 +353,8 @@ import { DisplayWidgetTypesPanelComponent } from '@home/components/dashboard-pag
351 ManageDashboardStatesDialogComponent, 353 ManageDashboardStatesDialogComponent,
352 DashboardStateDialogComponent, 354 DashboardStateDialogComponent,
353 EmbedDashboardDialogComponent, 355 EmbedDashboardDialogComponent,
354 - DisplayWidgetTypesPanelComponent 356 + DisplayWidgetTypesPanelComponent,
  357 + FirmwareAutocompleteComponent
355 ], 358 ],
356 providers: [ 359 providers: [
357 WidgetComponentService, 360 WidgetComponentService,
@@ -60,6 +60,10 @@ @@ -60,6 +60,10 @@
60 {{ 'device-profile.type-required' | translate }} 60 {{ 'device-profile.type-required' | translate }}
61 </mat-error> 61 </mat-error>
62 </mat-form-field> 62 </mat-form-field>
  63 + <tb-firmware-autocomplete
  64 + [useFullEntityId]="true"
  65 + formControlName="firmwareId">
  66 + </tb-firmware-autocomplete>
63 <mat-form-field class="mat-block"> 67 <mat-form-field class="mat-block">
64 <mat-label translate>device-profile.description</mat-label> 68 <mat-label translate>device-profile.description</mat-label>
65 <textarea matInput formControlName="description" rows="2"></textarea> 69 <textarea matInput formControlName="description" rows="2"></textarea>
@@ -48,7 +48,7 @@ import { MatHorizontalStepper } from '@angular/material/stepper'; @@ -48,7 +48,7 @@ import { MatHorizontalStepper } from '@angular/material/stepper';
48 import { RuleChainId } from '@shared/models/id/rule-chain-id'; 48 import { RuleChainId } from '@shared/models/id/rule-chain-id';
49 import { StepperSelectionEvent } from '@angular/cdk/stepper'; 49 import { StepperSelectionEvent } from '@angular/cdk/stepper';
50 import { deepTrim } from '@core/utils'; 50 import { deepTrim } from '@core/utils';
51 -import {ServiceType} from "@shared/models/queue.models"; 51 +import { ServiceType } from '@shared/models/queue.models';
52 52
53 export interface AddDeviceProfileDialogData { 53 export interface AddDeviceProfileDialogData {
54 deviceProfileName: string; 54 deviceProfileName: string;
@@ -72,13 +72,13 @@ export class AddDeviceProfileDialogComponent extends @@ -72,13 +72,13 @@ export class AddDeviceProfileDialogComponent extends
72 72
73 entityType = EntityType; 73 entityType = EntityType;
74 74
75 - deviceProfileTypes = Object.keys(DeviceProfileType); 75 + deviceProfileTypes = Object.values(DeviceProfileType);
76 76
77 deviceProfileTypeTranslations = deviceProfileTypeTranslationMap; 77 deviceProfileTypeTranslations = deviceProfileTypeTranslationMap;
78 78
79 deviceTransportTypeHints = deviceTransportTypeHintMap; 79 deviceTransportTypeHints = deviceTransportTypeHintMap;
80 80
81 - deviceTransportTypes = Object.keys(DeviceTransportType); 81 + deviceTransportTypes = Object.values(DeviceTransportType);
82 82
83 deviceTransportTypeTranslations = deviceTransportTypeTranslationMap; 83 deviceTransportTypeTranslations = deviceTransportTypeTranslationMap;
84 84
@@ -108,6 +108,7 @@ export class AddDeviceProfileDialogComponent extends @@ -108,6 +108,7 @@ export class AddDeviceProfileDialogComponent extends
108 type: [DeviceProfileType.DEFAULT, [Validators.required]], 108 type: [DeviceProfileType.DEFAULT, [Validators.required]],
109 defaultRuleChainId: [null, []], 109 defaultRuleChainId: [null, []],
110 defaultQueueName: ['', []], 110 defaultQueueName: ['', []],
  111 + firmwareId: [null],
111 description: ['', []] 112 description: ['', []]
112 } 113 }
113 ); 114 );
@@ -186,6 +187,7 @@ export class AddDeviceProfileDialogComponent extends @@ -186,6 +187,7 @@ export class AddDeviceProfileDialogComponent extends
186 transportType: this.transportConfigFormGroup.get('transportType').value, 187 transportType: this.transportConfigFormGroup.get('transportType').value,
187 provisionType: deviceProvisionConfiguration.type, 188 provisionType: deviceProvisionConfiguration.type,
188 provisionDeviceKey, 189 provisionDeviceKey,
  190 + firmwareId: this.deviceProfileDetailsFormGroup.get('firmwareId').value,
189 description: this.deviceProfileDetailsFormGroup.get('description').value, 191 description: this.deviceProfileDetailsFormGroup.get('description').value,
190 profileData: { 192 profileData: {
191 configuration: createDeviceProfileConfiguration(DeviceProfileType.DEFAULT), 193 configuration: createDeviceProfileConfiguration(DeviceProfileType.DEFAULT),
@@ -63,6 +63,10 @@ @@ -63,6 +63,10 @@
63 [queueType]="serviceType" 63 [queueType]="serviceType"
64 formControlName="defaultQueueName"> 64 formControlName="defaultQueueName">
65 </tb-queue-type-list> 65 </tb-queue-type-list>
  66 + <tb-firmware-autocomplete
  67 + [useFullEntityId]="true"
  68 + formControlName="firmwareId">
  69 + </tb-firmware-autocomplete>
66 <mat-form-field fxHide class="mat-block"> 70 <mat-form-field fxHide class="mat-block">
67 <mat-label translate>device-profile.type</mat-label> 71 <mat-label translate>device-profile.type</mat-label>
68 <mat-select formControlName="type" required> 72 <mat-select formControlName="type" required>
@@ -53,11 +53,11 @@ export class DeviceProfileComponent extends EntityComponent<DeviceProfile> { @@ -53,11 +53,11 @@ export class DeviceProfileComponent extends EntityComponent<DeviceProfile> {
53 53
54 entityType = EntityType; 54 entityType = EntityType;
55 55
56 - deviceProfileTypes = Object.keys(DeviceProfileType); 56 + deviceProfileTypes = Object.values(DeviceProfileType);
57 57
58 deviceProfileTypeTranslations = deviceProfileTypeTranslationMap; 58 deviceProfileTypeTranslations = deviceProfileTypeTranslationMap;
59 59
60 - deviceTransportTypes = Object.keys(DeviceTransportType); 60 + deviceTransportTypes = Object.values(DeviceTransportType);
61 61
62 deviceTransportTypeTranslations = deviceTransportTypeTranslationMap; 62 deviceTransportTypeTranslations = deviceTransportTypeTranslationMap;
63 63
@@ -109,6 +109,7 @@ export class DeviceProfileComponent extends EntityComponent<DeviceProfile> { @@ -109,6 +109,7 @@ export class DeviceProfileComponent extends EntityComponent<DeviceProfile> {
109 }), 109 }),
110 defaultRuleChainId: [entity && entity.defaultRuleChainId ? entity.defaultRuleChainId.id : null, []], 110 defaultRuleChainId: [entity && entity.defaultRuleChainId ? entity.defaultRuleChainId.id : null, []],
111 defaultQueueName: [entity ? entity.defaultQueueName : '', []], 111 defaultQueueName: [entity ? entity.defaultQueueName : '', []],
  112 + firmwareId: [entity ? entity.firmwareId : null],
112 description: [entity ? entity.description : '', []], 113 description: [entity ? entity.description : '', []],
113 } 114 }
114 ); 115 );
@@ -184,6 +185,7 @@ export class DeviceProfileComponent extends EntityComponent<DeviceProfile> { @@ -184,6 +185,7 @@ export class DeviceProfileComponent extends EntityComponent<DeviceProfile> {
184 }}, {emitEvent: false}); 185 }}, {emitEvent: false});
185 this.entityForm.patchValue({defaultRuleChainId: entity.defaultRuleChainId ? entity.defaultRuleChainId.id : null}, {emitEvent: false}); 186 this.entityForm.patchValue({defaultRuleChainId: entity.defaultRuleChainId ? entity.defaultRuleChainId.id : null}, {emitEvent: false});
186 this.entityForm.patchValue({defaultQueueName: entity.defaultQueueName}, {emitEvent: false}); 187 this.entityForm.patchValue({defaultQueueName: entity.defaultQueueName}, {emitEvent: false});
  188 + this.entityForm.patchValue({firmwareId: entity.firmwareId}, {emitEvent: false});
187 this.entityForm.patchValue({description: entity.description}, {emitEvent: false}); 189 this.entityForm.patchValue({description: entity.description}, {emitEvent: false});
188 } 190 }
189 191
@@ -48,6 +48,10 @@ @@ -48,6 +48,10 @@
48 <mat-label translate>device.label</mat-label> 48 <mat-label translate>device.label</mat-label>
49 <input matInput formControlName="label"> 49 <input matInput formControlName="label">
50 </mat-form-field> 50 </mat-form-field>
  51 + <tb-firmware-autocomplete
  52 + [useFullEntityId]="true"
  53 + formControlName="firmwareId">
  54 + </tb-firmware-autocomplete>
51 <mat-form-field class="mat-block" style="padding-bottom: 14px;"> 55 <mat-form-field class="mat-block" style="padding-bottom: 14px;">
52 <mat-label translate>device-profile.transport-type</mat-label> 56 <mat-label translate>device-profile.transport-type</mat-label>
53 <mat-select formControlName="transportType" required> 57 <mat-select formControlName="transportType" required>
@@ -70,7 +70,7 @@ export class DeviceWizardDialogComponent extends @@ -70,7 +70,7 @@ export class DeviceWizardDialogComponent extends
70 70
71 entityType = EntityType; 71 entityType = EntityType;
72 72
73 - deviceTransportTypes = Object.keys(DeviceTransportType); 73 + deviceTransportTypes = Object.values(DeviceTransportType);
74 74
75 deviceTransportTypeTranslations = deviceTransportTypeTranslationMap; 75 deviceTransportTypeTranslations = deviceTransportTypeTranslationMap;
76 76
@@ -107,6 +107,7 @@ export class DeviceWizardDialogComponent extends @@ -107,6 +107,7 @@ export class DeviceWizardDialogComponent extends
107 this.deviceWizardFormGroup = this.fb.group({ 107 this.deviceWizardFormGroup = this.fb.group({
108 name: ['', Validators.required], 108 name: ['', Validators.required],
109 label: [''], 109 label: [''],
  110 + firmwareId: [null],
110 gateway: [false], 111 gateway: [false],
111 overwriteActivityTime: [false], 112 overwriteActivityTime: [false],
112 transportType: [DeviceTransportType.DEFAULT, Validators.required], 113 transportType: [DeviceTransportType.DEFAULT, Validators.required],
@@ -312,6 +313,7 @@ export class DeviceWizardDialogComponent extends @@ -312,6 +313,7 @@ export class DeviceWizardDialogComponent extends
312 const device = { 313 const device = {
313 name: this.deviceWizardFormGroup.get('name').value, 314 name: this.deviceWizardFormGroup.get('name').value,
314 label: this.deviceWizardFormGroup.get('label').value, 315 label: this.deviceWizardFormGroup.get('label').value,
  316 + firmwareId: this.deviceWizardFormGroup.get('firmwareId').value,
315 deviceProfileId: profileId, 317 deviceProfileId: profileId,
316 additionalInfo: { 318 additionalInfo: {
317 gateway: this.deviceWizardFormGroup.get('gateway').value, 319 gateway: this.deviceWizardFormGroup.get('gateway').value,
@@ -95,6 +95,10 @@ @@ -95,6 +95,10 @@
95 <mat-label translate>device.label</mat-label> 95 <mat-label translate>device.label</mat-label>
96 <input matInput formControlName="label"> 96 <input matInput formControlName="label">
97 </mat-form-field> 97 </mat-form-field>
  98 + <tb-firmware-autocomplete
  99 + [useFullEntityId]="true"
  100 + formControlName="firmwareId">
  101 + </tb-firmware-autocomplete>
98 <tb-device-data 102 <tb-device-data
99 formControlName="deviceData" 103 formControlName="deviceData"
100 required> 104 required>
@@ -79,6 +79,7 @@ export class DeviceComponent extends EntityComponent<DeviceInfo> { @@ -79,6 +79,7 @@ export class DeviceComponent extends EntityComponent<DeviceInfo> {
79 { 79 {
80 name: [entity ? entity.name : '', [Validators.required]], 80 name: [entity ? entity.name : '', [Validators.required]],
81 deviceProfileId: [entity ? entity.deviceProfileId : null, [Validators.required]], 81 deviceProfileId: [entity ? entity.deviceProfileId : null, [Validators.required]],
  82 + firmwareId: [entity ? entity.firmwareId : null],
82 label: [entity ? entity.label : ''], 83 label: [entity ? entity.label : ''],
83 deviceData: [entity ? entity.deviceData : null, [Validators.required]], 84 deviceData: [entity ? entity.deviceData : null, [Validators.required]],
84 additionalInfo: this.fb.group( 85 additionalInfo: this.fb.group(
@@ -95,6 +96,7 @@ export class DeviceComponent extends EntityComponent<DeviceInfo> { @@ -95,6 +96,7 @@ export class DeviceComponent extends EntityComponent<DeviceInfo> {
95 updateForm(entity: DeviceInfo) { 96 updateForm(entity: DeviceInfo) {
96 this.entityForm.patchValue({name: entity.name}); 97 this.entityForm.patchValue({name: entity.name});
97 this.entityForm.patchValue({deviceProfileId: entity.deviceProfileId}); 98 this.entityForm.patchValue({deviceProfileId: entity.deviceProfileId});
  99 + this.entityForm.patchValue({firmwareId: entity.firmwareId});
98 this.entityForm.patchValue({label: entity.label}); 100 this.entityForm.patchValue({label: entity.label});
99 this.entityForm.patchValue({deviceData: entity.deviceData}); 101 this.entityForm.patchValue({deviceData: entity.deviceData});
100 this.entityForm.patchValue({ 102 this.entityForm.patchValue({
@@ -28,6 +28,16 @@ @@ -28,6 +28,16 @@
28 [fxShow]="!hideDelete() && !isEdit"> 28 [fxShow]="!hideDelete() && !isEdit">
29 {{'resource.delete' | translate }} 29 {{'resource.delete' | translate }}
30 </button> 30 </button>
  31 + <div fxLayout="row" fxLayout.xs="column">
  32 + <button mat-raised-button
  33 + ngxClipboard
  34 + (cbOnSuccess)="onFirmwareIdCopied($event)"
  35 + [cbContent]="entity?.id?.id"
  36 + [fxShow]="!isEdit">
  37 + <mat-icon svgIcon="mdi:clipboard-arrow-left"></mat-icon>
  38 + <span translate>firmware.copyId</span>
  39 + </button>
  40 + </div>
31 </div> 41 </div>
32 <div class="mat-padding" fxLayout="column"> 42 <div class="mat-padding" fxLayout="column">
33 <form [formGroup]="entityForm"> 43 <form [formGroup]="entityForm">
@@ -24,6 +24,7 @@ import { FormBuilder, FormGroup, Validators } from '@angular/forms'; @@ -24,6 +24,7 @@ import { FormBuilder, FormGroup, Validators } from '@angular/forms';
24 import { EntityComponent } from '@home/components/entity/entity.component'; 24 import { EntityComponent } from '@home/components/entity/entity.component';
25 import { ChecksumAlgorithm, ChecksumAlgorithmTranslationMap, Firmware } from '@shared/models/firmware.models'; 25 import { ChecksumAlgorithm, ChecksumAlgorithmTranslationMap, Firmware } from '@shared/models/firmware.models';
26 import { distinctUntilChanged, map, takeUntil } from 'rxjs/operators'; 26 import { distinctUntilChanged, map, takeUntil } from 'rxjs/operators';
  27 +import { ActionNotificationShow } from '@core/notification/notification.actions';
27 28
28 @Component({ 29 @Component({
29 selector: 'tb-firmware', 30 selector: 'tb-firmware',
@@ -109,4 +110,15 @@ export class FirmwaresComponent extends EntityComponent<Firmware> implements OnI @@ -109,4 +110,15 @@ export class FirmwaresComponent extends EntityComponent<Firmware> implements OnI
109 } 110 }
110 }); 111 });
111 } 112 }
  113 +
  114 + onFirmwareIdCopied($event) {
  115 + this.store.dispatch(new ActionNotificationShow(
  116 + {
  117 + message: this.translate.instant('firmware.idCopiedMessage'),
  118 + type: 'success',
  119 + duration: 750,
  120 + verticalPosition: 'bottom',
  121 + horizontalPosition: 'right'
  122 + }));
  123 + }
112 } 124 }
@@ -27,6 +27,7 @@ import { KeyFilter } from '@shared/models/query/query.models'; @@ -27,6 +27,7 @@ import { KeyFilter } from '@shared/models/query/query.models';
27 import { TimeUnit } from '@shared/models/time/time.models'; 27 import { TimeUnit } from '@shared/models/time/time.models';
28 import * as _moment from 'moment'; 28 import * as _moment from 'moment';
29 import { AbstractControl, ValidationErrors } from '@angular/forms'; 29 import { AbstractControl, ValidationErrors } from '@angular/forms';
  30 +import { FirmwareId } from '@shared/models/id/firmware-id';
30 31
31 export enum DeviceProfileType { 32 export enum DeviceProfileType {
32 DEFAULT = 'DEFAULT' 33 DEFAULT = 'DEFAULT'
@@ -445,6 +446,7 @@ export interface DeviceProfile extends BaseData<DeviceProfileId> { @@ -445,6 +446,7 @@ export interface DeviceProfile extends BaseData<DeviceProfileId> {
445 provisionDeviceKey?: string; 446 provisionDeviceKey?: string;
446 defaultRuleChainId?: RuleChainId; 447 defaultRuleChainId?: RuleChainId;
447 defaultQueueName?: string; 448 defaultQueueName?: string;
  449 + firmwareId?: FirmwareId;
448 profileData: DeviceProfileData; 450 profileData: DeviceProfileData;
449 } 451 }
450 452
@@ -499,6 +501,7 @@ export interface Device extends BaseData<DeviceId> { @@ -499,6 +501,7 @@ export interface Device extends BaseData<DeviceId> {
499 name: string; 501 name: string;
500 type: string; 502 type: string;
501 label: string; 503 label: string;
  504 + firmwareId?: FirmwareId;
502 deviceProfileId?: DeviceProfileId; 505 deviceProfileId?: DeviceProfileId;
503 deviceData?: DeviceData; 506 deviceData?: DeviceData;
504 additionalInfo?: any; 507 additionalInfo?: any;
@@ -1698,6 +1698,7 @@ @@ -1698,6 +1698,7 @@
1698 "checksum": "Checksum", 1698 "checksum": "Checksum",
1699 "checksum-required": "Checksum is required.", 1699 "checksum-required": "Checksum is required.",
1700 "checksum-algorithm": "Checksum algorithm", 1700 "checksum-algorithm": "Checksum algorithm",
  1701 + "copyId": "Copy firmware Id",
1701 "description": "Description", 1702 "description": "Description",
1702 "delete": "Delete firmware", 1703 "delete": "Delete firmware",
1703 "delete-firmware-text": "Be careful, after the confirmation the firmware will become unrecoverable.", 1704 "delete-firmware-text": "Be careful, after the confirmation the firmware will become unrecoverable.",
@@ -1708,10 +1709,12 @@ @@ -1708,10 +1709,12 @@
1708 "drop-file": "Drop a firmware file or click to select a file to upload.", 1709 "drop-file": "Drop a firmware file or click to select a file to upload.",
1709 "empty": "Firmware is empty", 1710 "empty": "Firmware is empty",
1710 "export": "Export firmware", 1711 "export": "Export firmware",
1711 - "no-firmware-matching": "No firmware matching '{{firmware}}' were found.", 1712 + "idCopiedMessage": "Firmware Id has been copied to clipboard",
  1713 + "no-firmware-matching": "No firmware matching '{{entity}}' were found.",
1712 "no-firmware-text": "No firmwares found", 1714 "no-firmware-text": "No firmwares found",
1713 "firmware": "Firmware", 1715 "firmware": "Firmware",
1714 "firmware-details": "Firmware details", 1716 "firmware-details": "Firmware details",
  1717 + "firmware-required": "Firmware is required.",
1715 "search": "Search firmwares", 1718 "search": "Search firmwares",
1716 "selected-firmware": "{ count, plural, 1 {1 firmware} other {# firmwares} } selected", 1719 "selected-firmware": "{ count, plural, 1 {1 firmware} other {# firmwares} } selected",
1717 "title": "Title", 1720 "title": "Title",