diff --git a/ui/src/app/api/entity.service.js b/ui/src/app/api/entity.service.js index 5ce4fd7108..51336ee374 100644 --- a/ui/src/app/api/entity.service.js +++ b/ui/src/app/api/entity.service.js @@ -848,7 +848,50 @@ function EntityService($http, $q, $filter, $translate, $log, userService, device return deferred.promise; } + function getEntityFieldKeys (entityType, searchText) { + let entityFieldKeys = []; + let query = searchText.toLowerCase(); + switch(entityType) { + case types.entityType.user: + entityFieldKeys.push(types.entityField.name.keyName); + entityFieldKeys.push(types.entityField.email.keyName); + entityFieldKeys.push(types.entityField.firstName.keyName); + entityFieldKeys.push(types.entityField.lastName.keyName); + break; + case types.entityType.tenant: + case types.entityType.customer: + entityFieldKeys.push(types.entityField.title.keyName); + entityFieldKeys.push(types.entityField.email.keyName); + entityFieldKeys.push(types.entityField.country.keyName); + entityFieldKeys.push(types.entityField.state.keyName); + entityFieldKeys.push(types.entityField.city.keyName); + entityFieldKeys.push(types.entityField.address.keyName); + entityFieldKeys.push(types.entityField.address2.keyName); + entityFieldKeys.push(types.entityField.zip.keyName); + entityFieldKeys.push(types.entityField.phone.keyName); + break; + case types.entityType.entityView: + entityFieldKeys.push(types.entityField.name.keyName); + entityFieldKeys.push(types.entityField.type.keyName); + break; + case types.entityType.device: + case types.entityType.asset: + entityFieldKeys.push(types.entityField.name.keyName); + entityFieldKeys.push(types.entityField.type.keyName); + entityFieldKeys.push(types.entityField.label.keyName); + break; + case types.entityType.dashboard: + entityFieldKeys.push(types.entityField.title.keyName); + break; + } + + return query ? entityFieldKeys.filter((entityField) => entityField.toLowerCase().indexOf(query) === 0) : entityFieldKeys; + } + function getEntityKeys(entityType, entityId, query, type, config) { + if (type === types.dataKeyType.entityField) { + return $q.when(getEntityFieldKeys(entityType, query)); + } var deferred = $q.defer(); var url = '/api/plugins/telemetry/' + entityType + '/' + entityId + '/keys/'; if (type === types.dataKeyType.timeseries) { diff --git a/ui/src/app/api/subscription.js b/ui/src/app/api/subscription.js index 4ad374bfe4..86ab823294 100644 --- a/ui/src/app/api/subscription.js +++ b/ui/src/app/api/subscription.js @@ -350,6 +350,11 @@ export default class Subscription { dataKey: dataKey, data: [] }; + if (dataKey.type === this.ctx.types.dataKeyType.entityField) { + if(datasource.entity && datasource.entity[this.ctx.types.entityField[dataKey.name].value]){ + datasourceData.data.push([Date.now(), datasource.entity[this.ctx.types.entityField[dataKey.name].value]]); + } + } this.data.push(datasourceData); this.hiddenData.push({data: []}); if (this.displayLegend) { @@ -878,8 +883,14 @@ export default class Subscription { }; } + var entityFieldKey = false; + for (var a = 0; a < datasource.dataKeys.length; a++) { - this.data[index + a].data = []; + if (datasource.dataKeys[a].type !== this.ctx.types.dataKeyType.entityField) { + this.data[index + a].data = []; + } else { + entityFieldKey = true; + } } index += datasource.dataKeys.length; @@ -891,7 +902,7 @@ export default class Subscription { } var forceUpdate = false; - if (datasource.unresolvedStateEntity || + if (datasource.unresolvedStateEntity || entityFieldKey || !datasource.dataKeys.length || (datasource.type === this.ctx.types.datasourceType.entity && !datasource.entityId) ) { diff --git a/ui/src/app/app.config.js b/ui/src/app/app.config.js index c38deae93b..d441d87d8b 100644 --- a/ui/src/app/app.config.js +++ b/ui/src/app/app.config.js @@ -78,7 +78,8 @@ export default function AppConfig($provide, $mdIconProvider.iconSet('mdi', mdiIconSet); ngMdIconServiceProvider - .addShape('alpha-a-circle-outline', ''); + .addShape('alpha-a-circle-outline', '') + .addShape('alpha-e-circle-outline', ''); configureTheme(); @@ -170,4 +171,4 @@ export default function AppConfig($provide, return aliases; } -} \ No newline at end of file +} diff --git a/ui/src/app/common/types.constant.js b/ui/src/app/common/types.constant.js index e6e65bbecf..6b11af555f 100644 --- a/ui/src/app/common/types.constant.js +++ b/ui/src/app/common/types.constant.js @@ -322,7 +322,8 @@ export default angular.module('thingsboard.types', []) timeseries: "timeseries", attribute: "attribute", function: "function", - alarm: "alarm" + alarm: "alarm", + entityField: "entityField" }, contentType: { "JSON": { @@ -467,6 +468,84 @@ export default angular.module('thingsboard.types', []) list: 'entity.type-current-customer' } }, + entityField: { + createdTime: { + keyName: 'createdTime', + name: 'entity-field.created-time', + value: 'createdTime', + time: true + }, + name: { + keyName: 'name', + name: 'entity-field.name', + value: 'name' + }, + type: { + keyName: 'type', + name: 'entity-field.type', + value: 'type' + }, + firstName: { + keyName: 'firstName', + name: 'entity-field.first-name', + value: 'firstName' + }, + lastName: { + keyName: 'lastName', + name: 'entity-field.last-name', + value: 'lastName' + }, + email: { + keyName: 'email', + name: 'entity-field.email', + value: 'email' + }, + title: { + keyName: 'title', + name: 'entity-field.title', + value: 'title' + }, + country: { + keyName: 'country', + name: 'entity-field.country', + value: 'country' + }, + state: { + keyName: 'state', + name: 'entity-field.state', + value: 'state' + }, + city: { + keyName: 'city', + name: 'entity-field.city', + value: 'city' + }, + address: { + keyName: 'address', + name: 'entity-field.address', + value: 'address' + }, + address2: { + keyName: 'address2', + name: 'entity-field.address2', + value: 'address2' + }, + zip: { + keyName: 'zip', + name: 'entity-field.zip', + value: 'zip' + }, + phone: { + keyName: 'phone', + name: 'entity-field.phone', + value: 'phone' + }, + label: { + keyName: 'label', + name: 'entity-field.label', + value: 'label' + } + }, entitySearchDirection: { from: "FROM", to: "TO" diff --git a/ui/src/app/components/datakey-config.tpl.html b/ui/src/app/components/datakey-config.tpl.html index 0b5b11d690..f8da179141 100644 --- a/ui/src/app/components/datakey-config.tpl.html +++ b/ui/src/app/components/datakey-config.tpl.html @@ -16,9 +16,7 @@ --> - - \ No newline at end of file + diff --git a/ui/src/app/components/datasource-entity.directive.js b/ui/src/app/components/datasource-entity.directive.js index 9ed05b8812..844e60e97e 100644 --- a/ui/src/app/components/datasource-entity.directive.js +++ b/ui/src/app/components/datasource-entity.directive.js @@ -124,7 +124,7 @@ function DatasourceEntity($compile, $templateCache, $q, $mdDialog, $window, $doc var alarmDataKeys = []; for (var d in ngModelCtrl.$viewValue.dataKeys) { var dataKey = ngModelCtrl.$viewValue.dataKeys[d]; - if ((dataKey.type === types.dataKeyType.timeseries) || (dataKey.type === types.dataKeyType.attribute)) { + if ((dataKey.type === types.dataKeyType.timeseries) || (dataKey.type === types.dataKeyType.attribute) || (dataKey.type === types.dataKeyType.entityField)) { dataKeys.push(dataKey); } else if (dataKey.type === types.dataKeyType.alarm) { alarmDataKeys.push(dataKey); @@ -219,7 +219,7 @@ function DatasourceEntity($compile, $templateCache, $q, $mdDialog, $window, $doc w.triggerHandler('resize'); } }).then(function (newDataKey) { - if ((newDataKey.type === types.dataKeyType.timeseries) || (newDataKey.type === types.dataKeyType.attribute)) { + if ((newDataKey.type === types.dataKeyType.timeseries) || (newDataKey.type === types.dataKeyType.attribute) || (newDataKey.type === types.dataKeyType.entityField)) { let index = scope.dataKeys.indexOf(dataKey); scope.dataKeys[index] = newDataKey; } else if (newDataKey.type === types.dataKeyType.alarm) { @@ -246,10 +246,16 @@ function DatasourceEntity($compile, $templateCache, $q, $mdDialog, $window, $doc items.push({ name: dataKeys[i], type: types.dataKeyType.timeseries }); } if (scope.widgetType == types.widgetType.latest.value) { - scope.fetchEntityKeys({entityAliasId: scope.entityAlias.id, query: searchText, type: types.dataKeyType.attribute}) - .then(function (dataKeys) { + var keysType = [types.dataKeyType.attribute, types.dataKeyType.entityField]; + var promises = []; + keysType.forEach((type) => { + promises.push(scope.fetchEntityKeys({entityAliasId: scope.entityAlias.id, query: searchText, type: type})); + }); + $q.all(promises).then(function (dataKeys) { for (var i = 0; i < dataKeys.length; i++) { - items.push({ name: dataKeys[i], type: types.dataKeyType.attribute }); + for (var j = 0; j < dataKeys[i].length; j++) { + items.push({name: dataKeys[i][j], type: keysType[i]}); + } } deferred.resolve(items); }, function (e) { diff --git a/ui/src/app/components/datasource-entity.tpl.html b/ui/src/app/components/datasource-entity.tpl.html index fe0d23eef7..a167d313ee 100644 --- a/ui/src/app/components/datasource-entity.tpl.html +++ b/ui/src/app/components/datasource-entity.tpl.html @@ -43,6 +43,10 @@ {{'datakey.attributes' | translate }} + + {{'datakey.entityField' | translate }} + + {{'datakey.timeseries' | translate }} @@ -60,6 +64,10 @@ {{'datakey.attributes' | translate }} + + {{'datakey.entityField' | translate }} + + {{'datakey.timeseries' | translate }} @@ -81,6 +89,10 @@ {{'datakey.attributes' | translate }} + + + {{'datakey.entityField' | translate }} + {{'datakey.timeseries' | translate }} diff --git a/ui/src/app/components/widget/widget-config.directive.js b/ui/src/app/components/widget/widget-config.directive.js index 470726a835..2fe97ce6d6 100644 --- a/ui/src/app/components/widget/widget-config.directive.js +++ b/ui/src/app/components/widget/widget-config.directive.js @@ -423,10 +423,10 @@ function WidgetConfig($compile, $templateCache, $rootScope, $translate, $timeout } var label = chip; - if (type === types.dataKeyType.alarm) { - var alarmField = types.alarmFields[chip]; - if (alarmField) { - label = $translate.instant(alarmField.name)+''; + if (type === types.dataKeyType.alarm || type === types.dataKeyType.entityField) { + var keyField = type === types.dataKeyType.alarm ? types.alarmFields[chip] : types.entityField[chip]; + if (keyField) { + label = $translate.instant(keyField.name)+''; } } label = scope.genNextLabel(label); diff --git a/ui/src/app/locale/locale.constant-el_GR.json b/ui/src/app/locale/locale.constant-el_GR.json index b590c70e47..f3051e98c1 100644 --- a/ui/src/app/locale/locale.constant-el_GR.json +++ b/ui/src/app/locale/locale.constant-el_GR.json @@ -1102,6 +1102,22 @@ "copyId": "Αντιγραφή ID ομάδας οντοτήτων", "idCopiedMessage": "Το ID της ομάδας οντοτήτων έχει αντιγραφεί στο πρόχειρο" }, + "entity-field": { + "created-time": "Δημιουργήθηκε", + "name": "Όνομα", + "type": "Τύπος", + "first-name": "Όνομα", + "last-name": "Επίθετο", + "email": "Email", + "title": "Τίτλος", + "country": "Χώρα", + "state": "Νομός", + "city": "Πόλη", + "address": "Διεύθυνση", + "address2": "Διεύθυνση 2", + "zip": "Τ.Κ.", + "phone": "Τηλέφωνο" + }, "entity-view": { "entity-view": "Όψη Οντότητας", "entity-view-required": "Απαιτείται προβολή οντότητας.", @@ -2603,4 +2619,4 @@ "el_GR": "Ελληνικά" } } -} \ No newline at end of file +} diff --git a/ui/src/app/locale/locale.constant-en_US.json b/ui/src/app/locale/locale.constant-en_US.json index 67f3fb09de..6310438128 100644 --- a/ui/src/app/locale/locale.constant-en_US.json +++ b/ui/src/app/locale/locale.constant-en_US.json @@ -815,6 +815,23 @@ "no-data": "No data to display", "columns-to-display": "Columns to Display" }, + "entity-field": { + "created-time": "Created time", + "name": "Name", + "type": "Type", + "first-name": "First name", + "last-name": "Last name", + "email": "Email", + "title": "Title", + "country": "Country", + "state": "State", + "city": "City", + "address": "Address", + "address2": "Address 2", + "zip": "Zip", + "phone": "Phone", + "label": "Label" + }, "entity-view": { "entity-view": "Entity View", "entity-view-required": "Entity view is required.", diff --git a/ui/src/app/locale/locale.constant-es_ES.json b/ui/src/app/locale/locale.constant-es_ES.json index d3d1c4042f..5cebe22ef6 100644 --- a/ui/src/app/locale/locale.constant-es_ES.json +++ b/ui/src/app/locale/locale.constant-es_ES.json @@ -808,6 +808,22 @@ "no-data": "No hay datos para mostrar", "columns-to-display": "Columnas a mostrar" }, + "entity-field": { + "created-time": "Tiempo de creación", + "name": "Nombre", + "type": "Tipo", + "first-name": "Nombre", + "last-name": "Apellido", + "email": "Correo electrónico", + "title": "Título", + "country": "País", + "state": "Estado", + "city": "Ciudad", + "address": "Dirección", + "address2": "Dirección 2", + "zip": "Código postal", + "phone": "Teléfono" + }, "entity-view": { "entity-view": "Vista de entidad", "entity-view-required": "Vista de entidad es requerido.", diff --git a/ui/src/app/locale/locale.constant-fr_FR.json b/ui/src/app/locale/locale.constant-fr_FR.json index b25a532927..5c922bfd49 100644 --- a/ui/src/app/locale/locale.constant-fr_FR.json +++ b/ui/src/app/locale/locale.constant-fr_FR.json @@ -809,6 +809,22 @@ "use-entity-name-filter": "Utiliser un filtre", "user-name-starts-with": "Utilisateurs dont les noms commencent par '{{prefix}}'" }, + "entity-field": { + "address": "Adresse", + "address2": "Adresse 2", + "city": "Ville", + "country": "Pays", + "created-time": "Heure de création", + "email": "Email", + "first-name": "Prénom", + "last-name": "Nom de famille", + "name": "Nom", + "phone": "Téléphone", + "state": "Prov", + "title": "Titre", + "type": "Type", + "zip": "Code postal" + }, "entity-view": { "add": "Ajouter une vue d'entité", "add-alias": "Ajouter un alias de vue d'entité", diff --git a/ui/src/app/locale/locale.constant-ru_RU.json b/ui/src/app/locale/locale.constant-ru_RU.json index 3aaa3042c5..ca4570638a 100644 --- a/ui/src/app/locale/locale.constant-ru_RU.json +++ b/ui/src/app/locale/locale.constant-ru_RU.json @@ -814,6 +814,23 @@ "no-data": "Нет данных для отображения", "columns-to-display": "Отобразить следующие колонки" }, + "entity-field": { + "created-time": "Время создания", + "name": "Название", + "type": "Тип", + "first-name": "Имя", + "last-name": "Фамилия", + "email": "Электронная почта", + "title": "Название", + "country": "Страна", + "state": "Штат/Область", + "city": "Город", + "address": "Адрес", + "address2": "Адрес 2", + "zip": "Индекс", + "phone": "Телефон", + "label": "Метка" + }, "entity-view": { "entity-view": "Представление Объекта", "entity-view-required": "Представление объекта обязательно.", diff --git a/ui/src/app/locale/locale.constant-uk_UA.json b/ui/src/app/locale/locale.constant-uk_UA.json index cbbaabd666..49fd45a409 100644 --- a/ui/src/app/locale/locale.constant-uk_UA.json +++ b/ui/src/app/locale/locale.constant-uk_UA.json @@ -956,6 +956,23 @@ "list-of-integrations": "{ count, plural, 1 {Одна інтеграція} other {Список # інтеграцій} }", "integration-name-starts-with": "Інтеграції, імена яких починаються з '{{prefix}}'" }, + "entity-field": { + "created-time": "Час створення", + "name": "Ім'я", + "type": "Тип", + "first-name": "Ім'я", + "last-name": "Прізвище", + "email": "Електронна пошта", + "title": "Назва", + "country": "Країна", + "state": "Штат", + "city": "Місто", + "address": "Адреса", + "address2": "Адреса 2", + "zip": "Zip", + "phone": "Телефон", + "label": "Мітка" + }, "entity-group": { "entity-group": "Група сутності", "details": "Деталі",