Showing
16 changed files
with
255 additions
and
27 deletions
@@ -57,6 +57,14 @@ export class AppComponent implements OnInit { | @@ -57,6 +57,14 @@ export class AppComponent implements OnInit { | ||
57 | '0,10 0 0,1 2,12A10,10 0 0,1 12,2Z" /></svg>' | 57 | '0,10 0 0,1 2,12A10,10 0 0,1 12,2Z" /></svg>' |
58 | ) | 58 | ) |
59 | ); | 59 | ); |
60 | + this.matIconRegistry.addSvgIconLiteral( | ||
61 | + 'alpha-e-circle-outline', | ||
62 | + this.domSanitizer.bypassSecurityTrustHtml( | ||
63 | + '<svg viewBox="0 0 24 24"><path d="M9,7H15V9H11V11H15V13H11V15H15V17H9V7M12,2A10,10 0 0,'+ | ||
64 | + '1 22,12A10,10 0 0,1 12,22A10,10 0 0,1 2,12A10,10 0 0,1 12,2M12,4A8,8 0 ' + | ||
65 | + '0,0 4,12A8,8 0 0,0 12,20A8,8 0 0,0 20,12A8,8 0 0,0 12,4Z" /></svg>' | ||
66 | + ) | ||
67 | + ); | ||
60 | 68 | ||
61 | this.storageService.testLocalStorage(); | 69 | this.storageService.testLocalStorage(); |
62 | 70 |
@@ -50,6 +50,8 @@ import { AlarmSourceListener } from '@core/http/alarm.service'; | @@ -50,6 +50,8 @@ import { AlarmSourceListener } from '@core/http/alarm.service'; | ||
50 | import { DatasourceListener } from '@core/api/datasource.service'; | 50 | import { DatasourceListener } from '@core/api/datasource.service'; |
51 | import * as deepEqual from 'deep-equal'; | 51 | import * as deepEqual from 'deep-equal'; |
52 | import { EntityId } from '@app/shared/models/id/entity-id'; | 52 | import { EntityId } from '@app/shared/models/id/entity-id'; |
53 | +import { DataKeyType } from '@shared/models/telemetry/telemetry.models'; | ||
54 | +import { entityFields } from '@shared/models/entity.models'; | ||
53 | 55 | ||
54 | export class WidgetSubscription implements IWidgetSubscription { | 56 | export class WidgetSubscription implements IWidgetSubscription { |
55 | 57 | ||
@@ -342,6 +344,12 @@ export class WidgetSubscription implements IWidgetSubscription { | @@ -342,6 +344,12 @@ export class WidgetSubscription implements IWidgetSubscription { | ||
342 | dataKey, | 344 | dataKey, |
343 | data: [] | 345 | data: [] |
344 | }; | 346 | }; |
347 | + if (dataKey.type === DataKeyType.entityField && datasource.entity) { | ||
348 | + const propName = entityFields[dataKey.name] ? entityFields[dataKey.name].value : dataKey.name; | ||
349 | + if (datasource.entity[propName]) { | ||
350 | + datasourceData.data.push([Date.now(), datasource.entity[propName]]); | ||
351 | + } | ||
352 | + } | ||
345 | this.data.push(datasourceData); | 353 | this.data.push(datasourceData); |
346 | this.hiddenData.push({data: []}); | 354 | this.hiddenData.push({data: []}); |
347 | if (this.displayLegend) { | 355 | if (this.displayLegend) { |
@@ -682,8 +690,15 @@ export class WidgetSubscription implements IWidgetSubscription { | @@ -682,8 +690,15 @@ export class WidgetSubscription implements IWidgetSubscription { | ||
682 | }, | 690 | }, |
683 | datasourceIndex: index | 691 | datasourceIndex: index |
684 | }; | 692 | }; |
693 | + | ||
694 | + let entityFieldKey = false; | ||
695 | + | ||
685 | for (let a = 0; a < datasource.dataKeys.length; a++) { | 696 | for (let a = 0; a < datasource.dataKeys.length; a++) { |
686 | - this.data[index + a].data = []; | 697 | + if (datasource.dataKeys[a].type !== DataKeyType.entityField) { |
698 | + this.data[index + a].data = []; | ||
699 | + } else { | ||
700 | + entityFieldKey = true; | ||
701 | + } | ||
687 | } | 702 | } |
688 | index += datasource.dataKeys.length; | 703 | index += datasource.dataKeys.length; |
689 | this.datasourceListeners.push(listener); | 704 | this.datasourceListeners.push(listener); |
@@ -691,7 +706,7 @@ export class WidgetSubscription implements IWidgetSubscription { | @@ -691,7 +706,7 @@ export class WidgetSubscription implements IWidgetSubscription { | ||
691 | if (datasource.dataKeys.length) { | 706 | if (datasource.dataKeys.length) { |
692 | this.ctx.datasourceService.subscribeToDatasource(listener); | 707 | this.ctx.datasourceService.subscribeToDatasource(listener); |
693 | } | 708 | } |
694 | - if (datasource.unresolvedStateEntity || | 709 | + if (datasource.unresolvedStateEntity || entityFieldKey || |
695 | !datasource.dataKeys.length || | 710 | !datasource.dataKeys.length || |
696 | (datasource.type === DatasourceType.entity && !datasource.entityId) | 711 | (datasource.type === DatasourceType.entity && !datasource.entityId) |
697 | ) { | 712 | ) { |
@@ -45,11 +45,11 @@ export class AttributeService { | @@ -45,11 +45,11 @@ export class AttributeService { | ||
45 | defaultHttpOptionsFromConfig(config)); | 45 | defaultHttpOptionsFromConfig(config)); |
46 | } | 46 | } |
47 | 47 | ||
48 | - public deleteEntityTimeseries(entityId: EntityId, timeseries: Array<AttributeData>, | 48 | + public deleteEntityTimeseries(entityId: EntityId, timeseries: Array<AttributeData>, deleteAllDataForKeys = false, |
49 | config?: RequestConfig): Observable<any> { | 49 | config?: RequestConfig): Observable<any> { |
50 | const keys = timeseries.map(attribute => attribute.key).join(','); | 50 | const keys = timeseries.map(attribute => attribute.key).join(','); |
51 | return this.http.delete(`/api/plugins/telemetry/${entityId.entityType}/${entityId.id}/timeseries/delete` + | 51 | return this.http.delete(`/api/plugins/telemetry/${entityId.entityType}/${entityId.id}/timeseries/delete` + |
52 | - `?keys=${keys}`, | 52 | + `?keys=${keys}&deleteAllDataForKeys=${deleteAllDataForKeys}`, |
53 | defaultHttpOptionsFromConfig(config)); | 53 | defaultHttpOptionsFromConfig(config)); |
54 | } | 54 | } |
55 | 55 | ||
@@ -93,7 +93,7 @@ export class AttributeService { | @@ -93,7 +93,7 @@ export class AttributeService { | ||
93 | }); | 93 | }); |
94 | let deleteEntityTimeseriesObservable: Observable<any>; | 94 | let deleteEntityTimeseriesObservable: Observable<any>; |
95 | if (deleteTimeseries.length) { | 95 | if (deleteTimeseries.length) { |
96 | - deleteEntityTimeseriesObservable = this.deleteEntityTimeseries(entityId, deleteTimeseries, config); | 96 | + deleteEntityTimeseriesObservable = this.deleteEntityTimeseries(entityId, deleteTimeseries, true, config); |
97 | } else { | 97 | } else { |
98 | deleteEntityTimeseriesObservable = of(null); | 98 | deleteEntityTimeseriesObservable = of(null); |
99 | } | 99 | } |
@@ -20,7 +20,13 @@ import { Observable, ReplaySubject } from 'rxjs'; | @@ -20,7 +20,13 @@ import { Observable, ReplaySubject } from 'rxjs'; | ||
20 | import { HttpClient } from '@angular/common/http'; | 20 | import { HttpClient } from '@angular/common/http'; |
21 | import { PageLink } from '@shared/models/page/page-link'; | 21 | import { PageLink } from '@shared/models/page/page-link'; |
22 | import { PageData } from '@shared/models/page/page-data'; | 22 | import { PageData } from '@shared/models/page/page-data'; |
23 | -import { Device, DeviceCredentials, DeviceInfo, DeviceSearchQuery } from '@app/shared/models/device.models'; | 23 | +import { |
24 | + ClaimRequest, ClaimResult, | ||
25 | + Device, | ||
26 | + DeviceCredentials, | ||
27 | + DeviceInfo, | ||
28 | + DeviceSearchQuery | ||
29 | +} from '@app/shared/models/device.models'; | ||
24 | import { EntitySubtype } from '@app/shared/models/entity-type.models'; | 30 | import { EntitySubtype } from '@app/shared/models/entity-type.models'; |
25 | import { AuthService } from '@core/auth/auth.service'; | 31 | import { AuthService } from '@core/auth/auth.service'; |
26 | 32 | ||
@@ -127,4 +133,13 @@ export class DeviceService { | @@ -127,4 +133,13 @@ export class DeviceService { | ||
127 | return this.http.get<Device>(`/api/tenant/devices?deviceName=${deviceName}`, defaultHttpOptionsFromConfig(config)); | 133 | return this.http.get<Device>(`/api/tenant/devices?deviceName=${deviceName}`, defaultHttpOptionsFromConfig(config)); |
128 | } | 134 | } |
129 | 135 | ||
136 | + public claimDevice(deviceName: string, claimRequest: ClaimRequest, | ||
137 | + config?: RequestConfig): Observable<ClaimResult> { | ||
138 | + return this.http.post<ClaimResult>(`api/customer/device/${deviceName}/claim`, claimRequest, defaultHttpOptionsFromConfig(config)); | ||
139 | + } | ||
140 | + | ||
141 | + public unclaimDevice(deviceName: string, config?: RequestConfig) { | ||
142 | + return this.http.delete(`/api/customer/device/${deviceName}/claim`, defaultHttpOptionsFromConfig(config)); | ||
143 | + } | ||
144 | + | ||
130 | } | 145 | } |
@@ -44,7 +44,7 @@ import { AliasInfo, StateParams, SubscriptionInfo } from '@core/api/widget-api.m | @@ -44,7 +44,7 @@ import { AliasInfo, StateParams, SubscriptionInfo } from '@core/api/widget-api.m | ||
44 | import { Datasource, DatasourceType, KeyInfo } from '@app/shared/models/widget.models'; | 44 | import { Datasource, DatasourceType, KeyInfo } from '@app/shared/models/widget.models'; |
45 | import { UtilsService } from '@core/services/utils.service'; | 45 | import { UtilsService } from '@core/services/utils.service'; |
46 | import { AliasFilterType, EntityAlias, EntityAliasFilter, EntityAliasFilterResult } from '@shared/models/alias.models'; | 46 | import { AliasFilterType, EntityAlias, EntityAliasFilter, EntityAliasFilterResult } from '@shared/models/alias.models'; |
47 | -import { EntityInfo, ImportEntitiesResultInfo, ImportEntityData } from '@shared/models/entity.models'; | 47 | +import { entityFields, EntityInfo, ImportEntitiesResultInfo, ImportEntityData } from '@shared/models/entity.models'; |
48 | import { | 48 | import { |
49 | EntityRelationInfo, | 49 | EntityRelationInfo, |
50 | EntityRelationsQuery, | 50 | EntityRelationsQuery, |
@@ -503,8 +503,50 @@ export class EntityService { | @@ -503,8 +503,50 @@ export class EntityService { | ||
503 | return entityTypes; | 503 | return entityTypes; |
504 | } | 504 | } |
505 | 505 | ||
506 | + private getEntityFieldKeys (entityType: EntityType, searchText: string): Array<string> { | ||
507 | + const entityFieldKeys: string[] = []; | ||
508 | + const query = searchText.toLowerCase(); | ||
509 | + switch(entityType) { | ||
510 | + case EntityType.USER: | ||
511 | + entityFieldKeys.push(entityFields.name.keyName); | ||
512 | + entityFieldKeys.push(entityFields.email.keyName); | ||
513 | + entityFieldKeys.push(entityFields.firstName.keyName); | ||
514 | + entityFieldKeys.push(entityFields.lastName.keyName); | ||
515 | + break; | ||
516 | + case EntityType.TENANT: | ||
517 | + case EntityType.CUSTOMER: | ||
518 | + entityFieldKeys.push(entityFields.title.keyName); | ||
519 | + entityFieldKeys.push(entityFields.email.keyName); | ||
520 | + entityFieldKeys.push(entityFields.country.keyName); | ||
521 | + entityFieldKeys.push(entityFields.state.keyName); | ||
522 | + entityFieldKeys.push(entityFields.city.keyName); | ||
523 | + entityFieldKeys.push(entityFields.address.keyName); | ||
524 | + entityFieldKeys.push(entityFields.address2.keyName); | ||
525 | + entityFieldKeys.push(entityFields.zip.keyName); | ||
526 | + entityFieldKeys.push(entityFields.phone.keyName); | ||
527 | + break; | ||
528 | + case EntityType.ENTITY_VIEW: | ||
529 | + entityFieldKeys.push(entityFields.name.keyName); | ||
530 | + entityFieldKeys.push(entityFields.type.keyName); | ||
531 | + break; | ||
532 | + case EntityType.DEVICE: | ||
533 | + case EntityType.ASSET: | ||
534 | + entityFieldKeys.push(entityFields.name.keyName); | ||
535 | + entityFieldKeys.push(entityFields.type.keyName); | ||
536 | + entityFieldKeys.push(entityFields.label.keyName); | ||
537 | + break; | ||
538 | + case EntityType.DASHBOARD: | ||
539 | + entityFieldKeys.push(entityFields.title.keyName); | ||
540 | + break; | ||
541 | + } | ||
542 | + return query ? entityFieldKeys.filter((entityField) => entityField.toLowerCase().indexOf(query) === 0) : entityFieldKeys; | ||
543 | + } | ||
544 | + | ||
506 | public getEntityKeys(entityId: EntityId, query: string, type: DataKeyType, | 545 | public getEntityKeys(entityId: EntityId, query: string, type: DataKeyType, |
507 | config?: RequestConfig): Observable<Array<string>> { | 546 | config?: RequestConfig): Observable<Array<string>> { |
547 | + if (type === DataKeyType.entityField) { | ||
548 | + return of(this.getEntityFieldKeys(entityId.entityType as EntityType, query)); | ||
549 | + } | ||
508 | let url = `/api/plugins/telemetry/${entityId.entityType}/${entityId.id}/keys/`; | 550 | let url = `/api/plugins/telemetry/${entityId.entityType}/${entityId.id}/keys/`; |
509 | if (type === DataKeyType.timeseries) { | 551 | if (type === DataKeyType.timeseries) { |
510 | url += 'timeseries'; | 552 | url += 'timeseries'; |
@@ -588,7 +630,8 @@ export class EntityService { | @@ -588,7 +630,8 @@ export class EntityService { | ||
588 | if (filter.stateEntityParamName && filter.stateEntityParamName.length) { | 630 | if (filter.stateEntityParamName && filter.stateEntityParamName.length) { |
589 | result.entityParamName = filter.stateEntityParamName; | 631 | result.entityParamName = filter.stateEntityParamName; |
590 | } | 632 | } |
591 | - const stateEntityId = this.getStateEntityId(filter, stateParams); | 633 | + const stateEntityInfo = this.getStateEntityInfo(filter, stateParams); |
634 | + const stateEntityId = stateEntityInfo.entityId; | ||
592 | switch (filter.type) { | 635 | switch (filter.type) { |
593 | case AliasFilterType.singleEntity: | 636 | case AliasFilterType.singleEntity: |
594 | const aliasEntityId = this.resolveAliasEntityId(filter.singleEntity.entityType, filter.singleEntity.id); | 637 | const aliasEntityId = this.resolveAliasEntityId(filter.singleEntity.entityType, filter.singleEntity.id); |
@@ -697,7 +740,8 @@ export class EntityService { | @@ -697,7 +740,8 @@ export class EntityService { | ||
697 | parameters: { | 740 | parameters: { |
698 | rootId: relationQueryRootEntityId.id, | 741 | rootId: relationQueryRootEntityId.id, |
699 | rootType: relationQueryRootEntityId.entityType as EntityType, | 742 | rootType: relationQueryRootEntityId.entityType as EntityType, |
700 | - direction: filter.direction | 743 | + direction: filter.direction, |
744 | + fetchLastLevelOnly: filter.fetchLastLevelOnly | ||
701 | }, | 745 | }, |
702 | filters: filter.filters | 746 | filters: filter.filters |
703 | }; | 747 | }; |
@@ -741,10 +785,12 @@ export class EntityService { | @@ -741,10 +785,12 @@ export class EntityService { | ||
741 | parameters: { | 785 | parameters: { |
742 | rootId: searchQueryRootEntityId.id, | 786 | rootId: searchQueryRootEntityId.id, |
743 | rootType: searchQueryRootEntityId.entityType as EntityType, | 787 | rootType: searchQueryRootEntityId.entityType as EntityType, |
744 | - direction: filter.direction | 788 | + direction: filter.direction, |
789 | + fetchLastLevelOnly: filter.fetchLastLevelOnly | ||
745 | }, | 790 | }, |
746 | relationType: filter.relationType | 791 | relationType: filter.relationType |
747 | }; | 792 | }; |
793 | + searchQuery.parameters.maxLevel = filter.maxLevel && filter.maxLevel > 0 ? filter.maxLevel : -1; | ||
748 | let findByQueryObservable: Observable<Array<BaseData<EntityId>>>; | 794 | let findByQueryObservable: Observable<Array<BaseData<EntityId>>>; |
749 | if (filter.type === AliasFilterType.assetSearchQuery) { | 795 | if (filter.type === AliasFilterType.assetSearchQuery) { |
750 | const assetSearchQuery = searchQuery as AssetSearchQuery; | 796 | const assetSearchQuery = searchQuery as AssetSearchQuery; |
@@ -988,8 +1034,8 @@ export class EntityService { | @@ -988,8 +1034,8 @@ export class EntityService { | ||
988 | ); | 1034 | ); |
989 | } | 1035 | } |
990 | 1036 | ||
991 | - private getStateEntityId(filter: EntityAliasFilter, stateParams: StateParams): EntityId { | ||
992 | - let entityId = null; | 1037 | + private getStateEntityInfo(filter: EntityAliasFilter, stateParams: StateParams): {entityId: EntityId} { |
1038 | + let entityId: EntityId = null; | ||
993 | if (stateParams) { | 1039 | if (stateParams) { |
994 | if (filter.stateEntityParamName && filter.stateEntityParamName.length) { | 1040 | if (filter.stateEntityParamName && filter.stateEntityParamName.length) { |
995 | if (stateParams[filter.stateEntityParamName]) { | 1041 | if (stateParams[filter.stateEntityParamName]) { |
@@ -1005,7 +1051,7 @@ export class EntityService { | @@ -1005,7 +1051,7 @@ export class EntityService { | ||
1005 | if (entityId) { | 1051 | if (entityId) { |
1006 | entityId = this.resolveAliasEntityId(entityId.entityType, entityId.id); | 1052 | entityId = this.resolveAliasEntityId(entityId.entityType, entityId.id); |
1007 | } | 1053 | } |
1008 | - return entityId; | 1054 | + return {entityId}; |
1009 | } | 1055 | } |
1010 | 1056 | ||
1011 | private resolveAliasEntityId(entityType: EntityType | AliasEntityType, id: string): EntityId { | 1057 | private resolveAliasEntityId(entityType: EntityType | AliasEntityType, id: string): EntityId { |
@@ -133,6 +133,14 @@ | @@ -133,6 +133,14 @@ | ||
133 | </tb-entity-select> | 133 | </tb-entity-select> |
134 | </div> | 134 | </div> |
135 | </div> | 135 | </div> |
136 | + <div fxFlex fxLayout="row"> | ||
137 | + <section class="tb-root-state-entity-switch" fxLayout="row" fxLayoutAlign="start center" style="padding-left: 0px;"> | ||
138 | + <mat-slide-toggle class="root-state-entity-switch" | ||
139 | + formControlName="fetchLastLevelOnly"> | ||
140 | + </mat-slide-toggle> | ||
141 | + <label class="tb-small root-state-entity-label" translate>alias.last-level-relation</label> | ||
142 | + </section> | ||
143 | + </div> | ||
136 | <div fxFlex fxLayoutGap="8px" fxLayout="row"> | 144 | <div fxFlex fxLayoutGap="8px" fxLayout="row"> |
137 | <mat-form-field class="mat-block" style="min-width: 100px;"> | 145 | <mat-form-field class="mat-block" style="min-width: 100px;"> |
138 | <mat-label translate>relation.direction</mat-label> | 146 | <mat-label translate>relation.direction</mat-label> |
@@ -191,6 +199,14 @@ | @@ -191,6 +199,14 @@ | ||
191 | </tb-entity-select> | 199 | </tb-entity-select> |
192 | </div> | 200 | </div> |
193 | </div> | 201 | </div> |
202 | + <div fxFlex fxLayout="row"> | ||
203 | + <section class="tb-root-state-entity-switch" fxLayout="row" fxLayoutAlign="start center" style="padding-left: 0px;"> | ||
204 | + <mat-slide-toggle class="root-state-entity-switch" | ||
205 | + formControlName="fetchLastLevelOnly"> | ||
206 | + </mat-slide-toggle> | ||
207 | + <label class="tb-small root-state-entity-label" translate>alias.last-level-relation</label> | ||
208 | + </section> | ||
209 | + </div> | ||
194 | <div fxFlex fxLayoutGap="8px" fxLayout="row"> | 210 | <div fxFlex fxLayoutGap="8px" fxLayout="row"> |
195 | <mat-form-field class="mat-block" style="min-width: 100px;"> | 211 | <mat-form-field class="mat-block" style="min-width: 100px;"> |
196 | <mat-label translate>relation.direction</mat-label> | 212 | <mat-label translate>relation.direction</mat-label> |
@@ -165,6 +165,7 @@ export class EntityFilterComponent implements ControlValueAccessor, OnInit { | @@ -165,6 +165,7 @@ export class EntityFilterComponent implements ControlValueAccessor, OnInit { | ||
165 | rootEntity: [filter ? filter.rootEntity : null, (filter && filter.rootStateEntity) ? [] : [Validators.required]], | 165 | rootEntity: [filter ? filter.rootEntity : null, (filter && filter.rootStateEntity) ? [] : [Validators.required]], |
166 | direction: [filter ? filter.direction : EntitySearchDirection.FROM, [Validators.required]], | 166 | direction: [filter ? filter.direction : EntitySearchDirection.FROM, [Validators.required]], |
167 | maxLevel: [filter ? filter.maxLevel : 1, []], | 167 | maxLevel: [filter ? filter.maxLevel : 1, []], |
168 | + fetchLastLevelOnly: [filter ? filter.fetchLastLevelOnly : false, []] | ||
168 | }); | 169 | }); |
169 | this.filterFormGroup.get('rootStateEntity').valueChanges.subscribe((rootStateEntity: boolean) => { | 170 | this.filterFormGroup.get('rootStateEntity').valueChanges.subscribe((rootStateEntity: boolean) => { |
170 | this.filterFormGroup.get('rootEntity').setValidators(rootStateEntity ? [] : [Validators.required]); | 171 | this.filterFormGroup.get('rootEntity').setValidators(rootStateEntity ? [] : [Validators.required]); |
@@ -38,6 +38,12 @@ | @@ -38,6 +38,12 @@ | ||
38 | <mat-icon class="tb-mat-20" | 38 | <mat-icon class="tb-mat-20" |
39 | svgIcon="alpha-a-circle-outline"></mat-icon> | 39 | svgIcon="alpha-a-circle-outline"></mat-icon> |
40 | </span> | 40 | </span> |
41 | + <span *ngIf="key.type === dataKeyTypes.entityField" | ||
42 | + matTooltip="{{'datakey.entity-field' | translate }}" | ||
43 | + matTooltipPosition="above"> | ||
44 | + <mat-icon class="tb-mat-20" | ||
45 | + svgIcon="alpha-e-circle-outline"></mat-icon> | ||
46 | + </span> | ||
41 | <span *ngIf="key.type === dataKeyTypes.timeseries" | 47 | <span *ngIf="key.type === dataKeyTypes.timeseries" |
42 | matTooltip="{{'datakey.timeseries' | translate }}" | 48 | matTooltip="{{'datakey.timeseries' | translate }}" |
43 | matTooltipPosition="above"> | 49 | matTooltipPosition="above"> |
@@ -87,6 +93,12 @@ | @@ -87,6 +93,12 @@ | ||
87 | <mat-icon class="tb-mat-16" | 93 | <mat-icon class="tb-mat-16" |
88 | svgIcon="alpha-a-circle-outline"></mat-icon> | 94 | svgIcon="alpha-a-circle-outline"></mat-icon> |
89 | </span> | 95 | </span> |
96 | + <span *ngIf="key.type === dataKeyTypes.entityField" | ||
97 | + matTooltip="{{'datakey.entity-field' | translate }}" | ||
98 | + matTooltipPosition="above"> | ||
99 | + <mat-icon class="tb-mat-16" | ||
100 | + svgIcon="alpha-e-circle-outline"></mat-icon> | ||
101 | + </span> | ||
90 | <span *ngIf="key.type === dataKeyTypes.timeseries" | 102 | <span *ngIf="key.type === dataKeyTypes.timeseries" |
91 | matTooltip="{{'datakey.timeseries' | translate }}" | 103 | matTooltip="{{'datakey.timeseries' | translate }}" |
92 | matTooltipPosition="above"> | 104 | matTooltipPosition="above"> |
@@ -114,15 +126,22 @@ | @@ -114,15 +126,22 @@ | ||
114 | <span *ngIf="widgetType == widgetTypes.latest" | 126 | <span *ngIf="widgetType == widgetTypes.latest" |
115 | matTooltip="{{'datakey.attributes' | translate }}" | 127 | matTooltip="{{'datakey.attributes' | translate }}" |
116 | matTooltipPosition="above"> | 128 | matTooltipPosition="above"> |
117 | - <mat-icon (click)="createKey(searchText, dataKeyTypes.attribute)" | 129 | + <mat-icon (click)="createKey(searchText, dataKeyTypes.attribute)" |
118 | class="tb-mat-16" | 130 | class="tb-mat-16" |
119 | svgIcon="alpha-a-circle-outline"></mat-icon> | 131 | svgIcon="alpha-a-circle-outline"></mat-icon> |
120 | - </span> | 132 | + </span> |
133 | + <span *ngIf="widgetType == widgetTypes.latest" | ||
134 | + matTooltip="{{'datakey.entity-field' | translate }}" | ||
135 | + matTooltipPosition="above"> | ||
136 | + <mat-icon (click)="createKey(searchText, dataKeyTypes.entityField)" | ||
137 | + class="tb-mat-16" | ||
138 | + svgIcon="alpha-e-circle-outline"></mat-icon> | ||
139 | + </span> | ||
121 | <span matTooltip="{{'datakey.timeseries' | translate }}" | 140 | <span matTooltip="{{'datakey.timeseries' | translate }}" |
122 | matTooltipPosition="above"> | 141 | matTooltipPosition="above"> |
123 | - <mat-icon (click)="createKey(searchText, dataKeyTypes.timeseries)" | 142 | + <mat-icon (click)="createKey(searchText, dataKeyTypes.timeseries)" |
124 | class="tb-mat-16">timeline</mat-icon> | 143 | class="tb-mat-16">timeline</mat-icon> |
125 | - </span> | 144 | + </span> |
126 | </ng-template> | 145 | </ng-template> |
127 | </ng-template> | 146 | </ng-template> |
128 | </div> | 147 | </div> |
@@ -420,6 +420,7 @@ export class DataKeysComponent implements ControlValueAccessor, OnInit, AfterVie | @@ -420,6 +420,7 @@ export class DataKeysComponent implements ControlValueAccessor, OnInit, AfterVie | ||
420 | const dataKeyTypes = [DataKeyType.timeseries]; | 420 | const dataKeyTypes = [DataKeyType.timeseries]; |
421 | if (this.widgetType === widgetType.latest) { | 421 | if (this.widgetType === widgetType.latest) { |
422 | dataKeyTypes.push(DataKeyType.attribute); | 422 | dataKeyTypes.push(DataKeyType.attribute); |
423 | + dataKeyTypes.push(DataKeyType.entityField); | ||
423 | } | 424 | } |
424 | fetchObservable = this.callbacks.fetchEntityKeys(this.entityAliasId, this.searchText, dataKeyTypes); | 425 | fetchObservable = this.callbacks.fetchEntityKeys(this.entityAliasId, this.searchText, dataKeyTypes); |
425 | } else { | 426 | } else { |
@@ -14,7 +14,7 @@ | @@ -14,7 +14,7 @@ | ||
14 | /// limitations under the License. | 14 | /// limitations under the License. |
15 | /// | 15 | /// |
16 | 16 | ||
17 | -import { ChangeDetectionStrategy, Component, forwardRef, Input, OnInit } from '@angular/core'; | 17 | +import { Component, forwardRef, Input, OnInit } from '@angular/core'; |
18 | import { PageComponent } from '@shared/components/page.component'; | 18 | import { PageComponent } from '@shared/components/page.component'; |
19 | import { Store } from '@ngrx/store'; | 19 | import { Store } from '@ngrx/store'; |
20 | import { AppState } from '@core/core.state'; | 20 | import { AppState } from '@core/core.state'; |
@@ -22,12 +22,11 @@ import { | @@ -22,12 +22,11 @@ import { | ||
22 | DataKey, | 22 | DataKey, |
23 | Datasource, | 23 | Datasource, |
24 | DatasourceType, | 24 | DatasourceType, |
25 | - datasourceTypeTranslationMap, defaultLegendConfig, | ||
26 | - WidgetActionDescriptor, | 25 | + datasourceTypeTranslationMap, |
26 | + defaultLegendConfig, | ||
27 | widgetType | 27 | widgetType |
28 | } from '@shared/models/widget.models'; | 28 | } from '@shared/models/widget.models'; |
29 | import { | 29 | import { |
30 | - AbstractControl, | ||
31 | ControlValueAccessor, | 30 | ControlValueAccessor, |
32 | FormArray, | 31 | FormArray, |
33 | FormBuilder, | 32 | FormBuilder, |
@@ -58,7 +57,8 @@ import { MatDialog } from '@angular/material/dialog'; | @@ -58,7 +57,8 @@ import { MatDialog } from '@angular/material/dialog'; | ||
58 | import { EntityService } from '@core/http/entity.service'; | 57 | import { EntityService } from '@core/http/entity.service'; |
59 | import { JsonFormComponentData } from '@shared/components/json-form/json-form-component.models'; | 58 | import { JsonFormComponentData } from '@shared/components/json-form/json-form-component.models'; |
60 | import { WidgetActionsData } from './action/manage-widget-actions.component.models'; | 59 | import { WidgetActionsData } from './action/manage-widget-actions.component.models'; |
61 | -import { Dashboard, DashboardState } from '@shared/models/dashboard.models'; | 60 | +import { DashboardState } from '@shared/models/dashboard.models'; |
61 | +import { entityFields } from '@shared/models/entity.models'; | ||
62 | 62 | ||
63 | const emptySettingsSchema = { | 63 | const emptySettingsSchema = { |
64 | type: 'object', | 64 | type: 'object', |
@@ -621,10 +621,10 @@ export class WidgetConfigComponent extends PageComponent implements OnInit, Cont | @@ -621,10 +621,10 @@ export class WidgetConfigComponent extends PageComponent implements OnInit, Cont | ||
621 | return chip; | 621 | return chip; |
622 | } else { | 622 | } else { |
623 | let label: string = chip; | 623 | let label: string = chip; |
624 | - if (type === DataKeyType.alarm) { | ||
625 | - const alarmField = alarmFields[label]; | ||
626 | - if (alarmField) { | ||
627 | - label = this.translate.instant(alarmField.name); | 624 | + if (type === DataKeyType.alarm || type === DataKeyType.entityField) { |
625 | + const keyField = type === DataKeyType.alarm ? alarmFields[label] : entityFields[chip];; | ||
626 | + if (keyField) { | ||
627 | + label = this.translate.instant(keyField.name); | ||
628 | } | 628 | } |
629 | } | 629 | } |
630 | label = this.genNextLabel(label); | 630 | label = this.genNextLabel(label); |
@@ -91,6 +91,7 @@ export interface RelationsQueryFilter { | @@ -91,6 +91,7 @@ export interface RelationsQueryFilter { | ||
91 | direction?: EntitySearchDirection; | 91 | direction?: EntitySearchDirection; |
92 | filters?: Array<EntityTypeFilter>; | 92 | filters?: Array<EntityTypeFilter>; |
93 | maxLevel?: number; | 93 | maxLevel?: number; |
94 | + fetchLastLevelOnly?: boolean; | ||
94 | } | 95 | } |
95 | 96 | ||
96 | export interface EntitySearchQueryFilter { | 97 | export interface EntitySearchQueryFilter { |
@@ -100,6 +101,8 @@ export interface EntitySearchQueryFilter { | @@ -100,6 +101,8 @@ export interface EntitySearchQueryFilter { | ||
100 | rootEntity?: EntityId; | 101 | rootEntity?: EntityId; |
101 | relationType?: string; | 102 | relationType?: string; |
102 | direction?: EntitySearchDirection; | 103 | direction?: EntitySearchDirection; |
104 | + maxLevel?: number; | ||
105 | + fetchLastLevelOnly?: boolean; | ||
103 | } | 106 | } |
104 | 107 | ||
105 | export interface AssetSearchQueryFilter extends EntitySearchQueryFilter { | 108 | export interface AssetSearchQueryFilter extends EntitySearchQueryFilter { |
@@ -57,3 +57,18 @@ export interface DeviceCredentials extends BaseData<DeviceCredentialsId> { | @@ -57,3 +57,18 @@ export interface DeviceCredentials extends BaseData<DeviceCredentialsId> { | ||
57 | export interface DeviceSearchQuery extends EntitySearchQuery { | 57 | export interface DeviceSearchQuery extends EntitySearchQuery { |
58 | deviceTypes: Array<string>; | 58 | deviceTypes: Array<string>; |
59 | } | 59 | } |
60 | + | ||
61 | +export interface ClaimRequest { | ||
62 | + secretKey: string; | ||
63 | +} | ||
64 | + | ||
65 | +export enum ClaimResponse { | ||
66 | + SUCCESS = 'SUCCESS', | ||
67 | + FAILURE = 'FAILURE', | ||
68 | + CLAIMED = 'CLAIMED' | ||
69 | +} | ||
70 | + | ||
71 | +export interface ClaimResult { | ||
72 | + device: Device, | ||
73 | + response: ClaimResponse | ||
74 | +} |
@@ -53,3 +53,89 @@ export interface ImportEntitiesResultInfo { | @@ -53,3 +53,89 @@ export interface ImportEntitiesResultInfo { | ||
53 | entity: number; | 53 | entity: number; |
54 | }; | 54 | }; |
55 | } | 55 | } |
56 | + | ||
57 | +export interface EntityField { | ||
58 | + keyName: string; | ||
59 | + value: string; | ||
60 | + name: string; | ||
61 | + time?: boolean; | ||
62 | +} | ||
63 | + | ||
64 | +export const entityFields: {[fieldName: string]: EntityField} = { | ||
65 | + createdTime: { | ||
66 | + keyName: 'createdTime', | ||
67 | + name: 'entity-field.created-time', | ||
68 | + value: 'createdTime', | ||
69 | + time: true | ||
70 | + }, | ||
71 | + name: { | ||
72 | + keyName: 'name', | ||
73 | + name: 'entity-field.name', | ||
74 | + value: 'name' | ||
75 | + }, | ||
76 | + type: { | ||
77 | + keyName: 'type', | ||
78 | + name: 'entity-field.type', | ||
79 | + value: 'type' | ||
80 | + }, | ||
81 | + firstName: { | ||
82 | + keyName: 'firstName', | ||
83 | + name: 'entity-field.first-name', | ||
84 | + value: 'firstName' | ||
85 | + }, | ||
86 | + lastName: { | ||
87 | + keyName: 'lastName', | ||
88 | + name: 'entity-field.last-name', | ||
89 | + value: 'lastName' | ||
90 | + }, | ||
91 | + email: { | ||
92 | + keyName: 'email', | ||
93 | + name: 'entity-field.email', | ||
94 | + value: 'email' | ||
95 | + }, | ||
96 | + title: { | ||
97 | + keyName: 'title', | ||
98 | + name: 'entity-field.title', | ||
99 | + value: 'title' | ||
100 | + }, | ||
101 | + country: { | ||
102 | + keyName: 'country', | ||
103 | + name: 'entity-field.country', | ||
104 | + value: 'country' | ||
105 | + }, | ||
106 | + state: { | ||
107 | + keyName: 'state', | ||
108 | + name: 'entity-field.state', | ||
109 | + value: 'state' | ||
110 | + }, | ||
111 | + city: { | ||
112 | + keyName: 'city', | ||
113 | + name: 'entity-field.city', | ||
114 | + value: 'city' | ||
115 | + }, | ||
116 | + address: { | ||
117 | + keyName: 'address', | ||
118 | + name: 'entity-field.address', | ||
119 | + value: 'address' | ||
120 | + }, | ||
121 | + address2: { | ||
122 | + keyName: 'address2', | ||
123 | + name: 'entity-field.address2', | ||
124 | + value: 'address2' | ||
125 | + }, | ||
126 | + zip: { | ||
127 | + keyName: 'zip', | ||
128 | + name: 'entity-field.zip', | ||
129 | + value: 'zip' | ||
130 | + }, | ||
131 | + phone: { | ||
132 | + keyName: 'phone', | ||
133 | + name: 'entity-field.phone', | ||
134 | + value: 'phone' | ||
135 | + }, | ||
136 | + label: { | ||
137 | + keyName: 'label', | ||
138 | + name: 'entity-field.label', | ||
139 | + value: 'label' | ||
140 | + } | ||
141 | +}; |
@@ -64,6 +64,7 @@ export interface RelationsSearchParameters { | @@ -64,6 +64,7 @@ export interface RelationsSearchParameters { | ||
64 | direction: EntitySearchDirection; | 64 | direction: EntitySearchDirection; |
65 | relationTypeGroup?: RelationTypeGroup; | 65 | relationTypeGroup?: RelationTypeGroup; |
66 | maxLevel?: number; | 66 | maxLevel?: number; |
67 | + fetchLastLevelOnly?: boolean; | ||
67 | } | 68 | } |
68 | 69 | ||
69 | export interface EntityRelationsQuery { | 70 | export interface EntityRelationsQuery { |
@@ -26,7 +26,8 @@ export enum DataKeyType { | @@ -26,7 +26,8 @@ export enum DataKeyType { | ||
26 | timeseries = 'timeseries', | 26 | timeseries = 'timeseries', |
27 | attribute = 'attribute', | 27 | attribute = 'attribute', |
28 | function = 'function', | 28 | function = 'function', |
29 | - alarm = 'alarm' | 29 | + alarm = 'alarm', |
30 | + entityField = 'entityField' | ||
30 | } | 31 | } |
31 | 32 | ||
32 | export enum LatestTelemetry { | 33 | export enum LatestTelemetry { |