thingsboard-memoizeit

TB-61: Improve Aliases management. Implement Asset type and

6/9/2017 2:26:19 PM

Details

diff --git a/ui/src/app/api/alias-controller.js b/ui/src/app/api/alias-controller.js
index b6d29a9..60e8a31 100644
--- a/ui/src/app/api/alias-controller.js
+++ b/ui/src/app/api/alias-controller.js
@@ -95,8 +95,7 @@ export default class AliasController {
                 this.entityService.resolveAlias(entityAlias, this.stateController.getStateParams()).then(
                     function success(aliasInfo) {
                         aliasCtrl.resolvedAliases[aliasId] = aliasInfo;
-                        if (entityAlias.filter.stateEntity) {
-                            aliasInfo.stateEntity = true;
+                        if (aliasInfo.stateEntity) {
                             aliasCtrl.resolvedAliasesToStateEntities[aliasId] =
                                 aliasCtrl.stateController.getStateParams().entityId;
                         }
diff --git a/ui/src/app/api/entity.service.js b/ui/src/app/api/entity.service.js
index 91c8df3..e647d9c 100644
--- a/ui/src/app/api/entity.service.js
+++ b/ui/src/app/api/entity.service.js
@@ -31,6 +31,7 @@ function EntityService($http, $q, $filter, $translate, $log, userService, device
         resolveAliasFilter: resolveAliasFilter,
         checkEntityAlias: checkEntityAlias,
         filterAliasByEntityTypes: filterAliasByEntityTypes,
+        getAliasFilterTypesByEntityTypes: getAliasFilterTypesByEntityTypes,
         getEntityKeys: getEntityKeys,
         createDatasourcesFromSubscriptionsInfo: createDatasourcesFromSubscriptionsInfo,
         getRelatedEntities: getRelatedEntities,
@@ -223,17 +224,21 @@ function EntityService($http, $q, $filter, $translate, $log, userService, device
         return promise;
     }
 
-    function getEntitiesByNameFilter(entityType, entityNameFilter, limit, config, subType) {
-        var deferred = $q.defer();
-        var pageLink = {limit: limit, textSearch: entityNameFilter};
+    function getEntitiesByPageLink(entityType, pageLink, config, subType, data, deferred) {
         var promise = getEntitiesByPageLinkPromise(entityType, pageLink, config, subType);
         if (promise) {
             promise.then(
                 function success(result) {
-                    if (result.data && result.data.length > 0) {
-                        deferred.resolve(result.data);
+                    data = data.concat(result.data);
+                    if (result.hasNext) {
+                        pageLink = result.nextPageLink;
+                        getEntitiesByPageLink(entityType, pageLink, config, subType, data, deferred);
                     } else {
-                        deferred.resolve(null);
+                        if (data && data.length > 0) {
+                            deferred.resolve(data);
+                        } else {
+                            deferred.resolve(null);
+                        }
                     }
                 },
                 function fail() {
@@ -243,6 +248,34 @@ function EntityService($http, $q, $filter, $translate, $log, userService, device
         } else {
             deferred.resolve(null);
         }
+    }
+
+    function getEntitiesByNameFilter(entityType, entityNameFilter, limit, config, subType) {
+        var deferred = $q.defer();
+        var pageLink = {limit: limit, textSearch: entityNameFilter};
+        if (limit == -1) { // all
+            var data = [];
+            pageLink.limit = 100;
+            getEntitiesByPageLink(entityType, pageLink, config, subType, data, deferred);
+        } else {
+            var promise = getEntitiesByPageLinkPromise(entityType, pageLink, config, subType);
+            if (promise) {
+                promise.then(
+                    function success(result) {
+                        if (result.data && result.data.length > 0) {
+                            deferred.resolve(result.data);
+                        } else {
+                            deferred.resolve(null);
+                        }
+                    },
+                    function fail() {
+                        deferred.resolve(null);
+                    }
+                );
+            } else {
+                deferred.resolve(null);
+            }
+        }
         return deferred.promise;
     }
 
@@ -261,11 +294,12 @@ function EntityService($http, $q, $filter, $translate, $log, userService, device
     function resolveAlias(entityAlias, stateParams) {
         var deferred = $q.defer();
         var filter = entityAlias.filter;
-        resolveAliasFilter(filter, stateParams, 100).then(
+        resolveAliasFilter(filter, stateParams, -1).then(
             function (result) {
                 var entities = result.entities;
                 var aliasInfo = {
                     alias: entityAlias.alias,
+                    stateEntity: result.stateEntity,
                     resolveMultiple: filter.resolveMultiple
                 };
                 var resolvedEntities = entitiesToEntitiesInfo(entities);
@@ -291,39 +325,68 @@ function EntityService($http, $q, $filter, $translate, $log, userService, device
         };
         switch (filter.type) {
             case types.aliasFilterType.entityList.value:
-                if (filter.stateEntity) {
-                    result.stateEntity = true;
-                    if (stateParams && stateParams.entityId) {
-                        getEntity(stateParams.entityId.entityType, stateParams.entityId.id).then(
-                            function success(entity) {
-                                result.entities = [entity];
-                                deferred.resolve(result);
-                            },
-                            function fail() {
-                                deferred.reject();
-                            }
-                        );
-                    } else {
-                        deferred.resolve(result);
+                getEntities(filter.entityType, filter.entityList).then(
+                    function success(entities) {
+                        if (entities && entities.length) {
+                            result.entities = entities;
+                            deferred.resolve(result);
+                        } else {
+                            deferred.reject();
+                        }
+                    },
+                    function fail() {
+                        deferred.reject();
                     }
-                } else {
-                    getEntities(filter.entityType, filter.entityList).then(
-                        function success(entities) {
-                            if (entities && entities.length) {
-                                result.entities = entities;
-                                deferred.resolve(result);
-                            } else {
-                                deferred.reject();
-                            }
+                );
+                break;
+            case types.aliasFilterType.entityName.value:
+                getEntitiesByNameFilter(filter.entityType, filter.entityNameFilter, maxItems).then(
+                    function success(entities) {
+                        if (entities && entities.length) {
+                            result.entities = entities;
+                            deferred.resolve(result);
+                        } else {
+                            deferred.reject();
+                        }
+                    },
+                    function fail() {
+                        deferred.reject();
+                    }
+                );
+                break;
+            case types.aliasFilterType.stateEntity.value:
+                result.stateEntity = true;
+                if (stateParams && stateParams.entityId) {
+                    getEntity(stateParams.entityId.entityType, stateParams.entityId.id).then(
+                        function success(entity) {
+                            result.entities = [entity];
+                            deferred.resolve(result);
                         },
                         function fail() {
                             deferred.reject();
                         }
                     );
+                } else {
+                    deferred.resolve(result);
                 }
                 break;
-            case types.aliasFilterType.entityName.value:
-                getEntitiesByNameFilter(filter.entityType, filter.entityNameFilter, maxItems).then(
+            case types.aliasFilterType.assetType.value:
+                getEntitiesByNameFilter(types.entityType.asset, filter.assetNameFilter, maxItems, null, filter.assetType).then(
+                    function success(entities) {
+                        if (entities && entities.length) {
+                            result.entities = entities;
+                            deferred.resolve(result);
+                        } else {
+                            deferred.reject();
+                        }
+                    },
+                    function fail() {
+                        deferred.reject();
+                    }
+                );
+                break;
+            case types.aliasFilterType.deviceType.value:
+                getEntitiesByNameFilter(types.entityType.device, filter.deviceNameFilter, maxItems, null, filter.deviceType).then(
                     function success(entities) {
                         if (entities && entities.length) {
                             result.entities = entities;
@@ -337,27 +400,81 @@ function EntityService($http, $q, $filter, $translate, $log, userService, device
                     }
                 );
                 break;
-            //TODO:
+
+            //TODO: Alias filter
         }
         return deferred.promise;
     }
 
     function filterAliasByEntityTypes(entityAlias, entityTypes) {
         var filter = entityAlias.filter;
-        switch (filter.type) {
-            case types.aliasFilterType.entityList.value:
-                if (filter.stateEntity) {
-                    return true;
-                } else {
+        if (filterAliasFilterTypeByEntityTypes(filter.type, entityTypes)) {
+            switch (filter.type) {
+                case types.aliasFilterType.entityList.value:
                     return entityTypes.indexOf(filter.entityType) > -1 ? true : false;
-                }
+                case types.aliasFilterType.entityName.value:
+                    return entityTypes.indexOf(filter.entityType) > -1 ? true : false;
+                case types.aliasFilterType.stateEntity.value:
+                    return true;
+                case types.aliasFilterType.assetType.value:
+                    return entityTypes.indexOf(types.entityType.asset)  > -1 ? true : false;
+                case types.aliasFilterType.deviceType.value:
+                    return entityTypes.indexOf(types.entityType.device)  > -1 ? true : false;
+            }
+        }
+        //TODO: Alias filter
+        return false;
+    }
+
+    function filterAliasFilterTypeByEntityType(aliasFilterType, entityType) {
+        switch (aliasFilterType) {
+            case types.aliasFilterType.entityList.value:
+                return true;
             case types.aliasFilterType.entityName.value:
-                return entityTypes.indexOf(filter.entityType) > -1 ? true : false;
+                return true;
+            case types.aliasFilterType.stateEntity.value:
+                return true;
+            case types.aliasFilterType.assetType.value:
+                return entityType === types.entityType.asset;
+            case types.aliasFilterType.deviceType.value:
+                return entityType === types.entityType.device;
+            case types.aliasFilterType.relationsQuery.value:
+                return true;
+            case types.aliasFilterType.assetSearchQuery.value:
+                return entityType === types.entityType.asset;
+            case types.aliasFilterType.deviceSearchQuery.value:
+                return entityType === types.entityType.device;
         }
-        //TODO:
         return false;
     }
 
+    function filterAliasFilterTypeByEntityTypes(aliasFilterType, entityTypes) {
+        if (!entityTypes || !entityTypes.length) {
+            return true;
+        }
+        var valid = false;
+        entityTypes.forEach(function(entityType) {
+            valid = valid || filterAliasFilterTypeByEntityType(aliasFilterType, entityType);
+        });
+        return valid;
+    }
+
+    function getAliasFilterTypesByEntityTypes(entityTypes) {
+        var allAliasFilterTypes = types.aliasFilterType;
+        if (!entityTypes || !entityTypes.length) {
+            return allAliasFilterTypes;
+        }
+        var result = {};
+        for (var type in allAliasFilterTypes) {
+            var aliasFilterType = allAliasFilterTypes[type];
+            if (filterAliasFilterTypeByEntityTypes(aliasFilterType.value, entityTypes)) {
+                result[type] = aliasFilterType;
+            }
+        }
+        return result;
+    }
+
+
     function checkEntityAlias(entityAlias) {
         var deferred = $q.defer();
         resolveAliasFilter(entityAlias.filter, null, 1).then(
@@ -380,50 +497,6 @@ function EntityService($http, $q, $filter, $translate, $log, userService, device
         return deferred.promise;
     }
 
-    /*function processEntityAlias(index, aliasIds, entityAliases, stateParams, resolution, deferred) {
-        if (index < aliasIds.length) {
-            var aliasId = aliasIds[index];
-            var entityAlias = entityAliases[aliasId];
-            var alias = entityAlias.alias;
-            var filter = entityAlias.filter;
-            resolveAliasFilter(filter, stateParams).then(
-                function (entities) {
-                    if (entities && entities.length) {
-                        var entity = entities[0];
-                        var resolvedAlias = {alias: alias, entityType: entity.id.entityType, entityId: entity.id.id};
-                        resolution.aliasesInfo.entityAliases[aliasId] = resolvedAlias;
-                        resolution.aliasesInfo.entityAliasesInfo[aliasId] = entitiesToEntitiesInfo(entities);
-                        index++;
-                        processEntityAlias(index, aliasIds, entityAliases, stateParams, resolution, deferred);
-                    } else {
-                        if (!resolution.error) {
-                            resolution.error = 'dashboard.invalid-aliases-config';
-                        }
-                        index++;
-                        processEntityAlias(index, aliasIds, entityAliases, stateParams, resolution, deferred);
-                    }
-                }
-            );
-        } else {
-            deferred.resolve(resolution);
-        }
-    }*/
-
-    /*function processEntityAliases(entityAliases, stateParams) {
-        var deferred = $q.defer();
-        var resolution = {
-            aliasesInfo: {}
-        };
-        var aliasIds = [];
-        if (entityAliases) {
-            for (var aliasId in entityAliases) {
-                aliasIds.push(aliasId);
-            }
-        }
-        processEntityAlias(0, aliasIds, entityAliases, stateParams, resolution, deferred);
-        return deferred.promise;
-    }*/
-
     function getEntityKeys(entityType, entityId, query, type) {
         var deferred = $q.defer();
         var url = '/api/plugins/telemetry/' + entityType + '/' + entityId + '/keys/';
@@ -889,4 +962,4 @@ function EntityService($http, $q, $filter, $translate, $log, userService, device
         }
     }
 
-}
\ No newline at end of file
+}
diff --git a/ui/src/app/api/subscription.js b/ui/src/app/api/subscription.js
index 4cb382c..1d4eb30 100644
--- a/ui/src/app/api/subscription.js
+++ b/ui/src/app/api/subscription.js
@@ -258,17 +258,17 @@ export default class Subscription {
                         } else {
                             subscription.rpcEnabled = subscription.ctx.$scope.widgetEditMode ? true : false;
                         }
-                        subscription.callbacks.rpcStateChanged(this);
+                        subscription.callbacks.rpcStateChanged(subscription);
                         deferred.resolve();
                     } else {
                         subscription.rpcEnabled = false;
-                        subscription.callbacks.rpcStateChanged(this);
+                        subscription.callbacks.rpcStateChanged(subscription);
                         deferred.resolve();
                     }
                 },
                 function fail () {
                     subscription.rpcEnabled = false;
-                    subscription.callbacks.rpcStateChanged(this);
+                    subscription.callbacks.rpcStateChanged(subscription);
                     deferred.resolve();
                 }
             );
diff --git a/ui/src/app/common/dashboard-utils.service.js b/ui/src/app/common/dashboard-utils.service.js
index fe8aa93..915d550 100644
--- a/ui/src/app/common/dashboard-utils.service.js
+++ b/ui/src/app/common/dashboard-utils.service.js
@@ -110,14 +110,12 @@ function DashboardUtils(types, utils, timeService) {
                 deviceAlias.deviceFilter.useFilter ? types.aliasFilterType.entityName.value : types.aliasFilterType.entityList.value;
             if (entityAlias.filter.type == types.aliasFilterType.entityList.value) {
                 entityAlias.filter.entityList = deviceAlias.deviceFilter.deviceList;
-                entityAlias.filter.stateEntity = false;
             } else {
                 entityAlias.filter.entityNameFilter = deviceAlias.deviceFilter.deviceNameFilter;
             }
         } else {
             entityAlias.filter.type = types.aliasFilterType.entityList.value;
             entityAlias.filter.entityList = [deviceAlias.deviceId];
-            entityAlias.filter.stateEntity = false;
         }
         return entityAlias;
     }
@@ -132,7 +130,6 @@ function DashboardUtils(types, utils, timeService) {
             }
             if (entityAlias.filter.type == types.aliasFilterType.entityList.value) {
                 entityAlias.filter.entityList = entityAlias.entityFilter.entityList;
-                entityAlias.filter.stateEntity = false;
             } else {
                 entityAlias.filter.entityNameFilter = entityAlias.entityFilter.entityNameFilter;
             }
@@ -342,7 +339,6 @@ function DashboardUtils(types, utils, timeService) {
     function createSingleEntityFilter(entityType, entityId) {
         return {
             type: types.aliasFilterType.entityList.value,
-            stateEntity: false,
             entityList: [entityId],
             entityType: entityType,
             resolveMultiple: false
diff --git a/ui/src/app/common/types.constant.js b/ui/src/app/common/types.constant.js
index 40b6639..522abff 100644
--- a/ui/src/app/common/types.constant.js
+++ b/ui/src/app/common/types.constant.js
@@ -74,6 +74,10 @@ export default angular.module('thingsboard.types', [])
                     value: 'entityName',
                     name: 'alias.filter-type-entity-name'
                 },
+                stateEntity: {
+                    value: 'stateEntity',
+                    name: 'alias.filter-type-state-entity'
+                },
                 assetType: {
                     value: 'assetType',
                     name: 'alias.filter-type-asset-type'
@@ -139,6 +143,53 @@ export default angular.module('thingsboard.types', [])
                 dashboard: "DASHBOARD",
                 alarm: "ALARM"
             },
+            entityTypeTranslations: {
+                "DEVICE": {
+                    type: 'entity.type-device',
+                    list: 'entity.list-of-devices',
+                    nameStartsWith: 'entity.device-name-starts-with'
+                },
+                "ASSET": {
+                    type: 'entity.type-asset',
+                    list: 'entity.list-of-assets',
+                    nameStartsWith: 'entity.asset-name-starts-with'
+                },
+                "RULE": {
+                    type: 'entity.type-rule',
+                    list: 'entity.list-of-rules',
+                    nameStartsWith: 'entity.rule-name-starts-with'
+                },
+                "PLUGIN": {
+                    type: 'entity.type-plugin',
+                    list: 'entity.list-of-plugins',
+                    nameStartsWith: 'entity.plugin-name-starts-with'
+                },
+                "TENANT": {
+                    type: 'entity.type-tenant',
+                    list: 'entity.list-of-tenants',
+                    nameStartsWith: 'entity.tenant-name-starts-with'
+                },
+                "CUSTOMER": {
+                    type: 'entity.type-customer',
+                    list: 'entity.list-of-customers',
+                    nameStartsWith: 'entity.customer-name-starts-with'
+                },
+                "USER": {
+                    type: 'entity.type-user',
+                    list: 'entity.list-of-users',
+                    nameStartsWith: 'entity.user-name-starts-with'
+                },
+                "DASHBOARD": {
+                    type: 'entity.type-dashboard',
+                    list: 'entity.list-of-dashboards',
+                    nameStartsWith: 'entity.dashboard-name-starts-with'
+                },
+                "ALARM": {
+                    type: 'entity.type-alarm',
+                    list: 'entity.list-of-alarms',
+                    nameStartsWith: 'entity.alarm-name-starts-with'
+                }
+            },
             entitySearchDirection: {
                 from: "FROM",
                 to: "TO"
diff --git a/ui/src/app/common/utils.service.js b/ui/src/app/common/utils.service.js
index 8bf76c4..8d2e565 100644
--- a/ui/src/app/common/utils.service.js
+++ b/ui/src/app/common/utils.service.js
@@ -109,8 +109,7 @@ function Utils($mdColorPalette, $rootScope, $window, types) {
         cleanCopy: cleanCopy,
         isLocalUrl: isLocalUrl,
         validateDatasources: validateDatasources,
-        createKey: createKey,
-        entityTypeName: entityTypeName
+        createKey: createKey
     }
 
     return service;
@@ -358,27 +357,4 @@ function Utils($mdColorPalette, $rootScope, $window, types) {
         return dataKey;
     }
 
-    function entityTypeName (type) {
-        switch (type) {
-            case types.entityType.device:
-                return 'entity.type-device';
-            case types.entityType.asset:
-                return 'entity.type-asset';
-            case types.entityType.rule:
-                return 'entity.type-rule';
-            case types.entityType.plugin:
-                return 'entity.type-plugin';
-            case types.entityType.tenant:
-                return 'entity.type-tenant';
-            case types.entityType.customer:
-                return 'entity.type-customer';
-            case types.entityType.user:
-                return 'entity.type-user';
-            case types.entityType.dashboard:
-                return 'entity.type-dashboard';
-            case types.entityType.alarm:
-                return 'entity.type-alarm';
-        }
-    }
-
 }
diff --git a/ui/src/app/dashboard/add-widget.controller.js b/ui/src/app/dashboard/add-widget.controller.js
index 586fb46..a9a68c7 100644
--- a/ui/src/app/dashboard/add-widget.controller.js
+++ b/ui/src/app/dashboard/add-widget.controller.js
@@ -15,7 +15,7 @@
  */
 /* eslint-disable import/no-unresolved, import/default */
 
-import entityAliasesTemplate from '../entity/entity-aliases.tpl.html';
+import entityAliasDialogTemplate from '../entity/entity-alias-dialog.tpl.html';
 
 /* eslint-enable import/no-unresolved, import/default */
 
@@ -130,17 +130,14 @@ export default function AddWidgetController($scope, widgetService, entityService
         var singleEntityAlias = {id: null, alias: alias, filter: {}};
 
         $mdDialog.show({
-            controller: 'EntityAliasesController',
+            controller: 'EntityAliasDialogController',
             controllerAs: 'vm',
-            templateUrl: entityAliasesTemplate,
+            templateUrl: entityAliasDialogTemplate,
             locals: {
-                config: {
-                    entityAliases: angular.copy(vm.dashboard.configuration.entityAliases),
-                    widgets: null,
-                    isSingleEntityAlias: true,
-                    singleEntityAlias: singleEntityAlias,
-                    allowedEntityTypes: allowedEntityTypes
-                }
+                isAdd: true,
+                allowedEntityTypes: allowedEntityTypes,
+                entityAliases: vm.dashboard.configuration.entityAliases,
+                alias: singleEntityAlias
             },
             parent: angular.element($document[0].body),
             fullscreen: true,
diff --git a/ui/src/app/dashboard/edit-widget.directive.js b/ui/src/app/dashboard/edit-widget.directive.js
index 4bd4e3d..5b788cc 100644
--- a/ui/src/app/dashboard/edit-widget.directive.js
+++ b/ui/src/app/dashboard/edit-widget.directive.js
@@ -15,7 +15,7 @@
  */
 /* eslint-disable import/no-unresolved, import/default */
 
-import entityAliasesTemplate from '../entity/entity-aliases.tpl.html';
+import entityAliasDialogTemplate from '../entity/entity-alias-dialog.tpl.html';
 import editWidgetTemplate from './edit-widget.tpl.html';
 
 /* eslint-enable import/no-unresolved, import/default */
@@ -98,17 +98,14 @@ export default function EditWidgetDirective($compile, $templateCache, types, wid
             var singleEntityAlias = {id: null, alias: alias, filter: {}};
 
             $mdDialog.show({
-                controller: 'EntityAliasesController',
+                controller: 'EntityAliasDialogController',
                 controllerAs: 'vm',
-                templateUrl: entityAliasesTemplate,
+                templateUrl: entityAliasDialogTemplate,
                 locals: {
-                    config: {
-                        entityAliases: angular.copy(scope.dashboard.configuration.entityAliases),
-                        widgets: null,
-                        isSingleEntityAlias: true,
-                        singleEntityAlias: singleEntityAlias,
-                        allowedEntityTypes: allowedEntityTypes
-                    }
+                    isAdd: true,
+                    allowedEntityTypes: allowedEntityTypes,
+                    entityAliases: scope.dashboard.configuration.entityAliases,
+                    alias: singleEntityAlias
                 },
                 parent: angular.element($document[0].body),
                 fullscreen: true,
diff --git a/ui/src/app/entity/entity-alias-dialog.scss b/ui/src/app/entity/entity-alias-dialog.scss
new file mode 100644
index 0000000..96df6fd
--- /dev/null
+++ b/ui/src/app/entity/entity-alias-dialog.scss
@@ -0,0 +1,27 @@
+/**
+ * Copyright © 2016-2017 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+.tb-entity-alias-dialog {
+  .tb-resolve-multiple-switch {
+    padding-left: 10px;
+    .resolve-multiple-switch {
+      margin: 0;
+    }
+    .resolve-multiple-label {
+      margin: 5px 0;
+    }
+  }
+}
\ No newline at end of file
diff --git a/ui/src/app/entity/entity-aliases.controller.js b/ui/src/app/entity/entity-aliases.controller.js
index 50bbfd9..b72b9ec 100644
--- a/ui/src/app/entity/entity-aliases.controller.js
+++ b/ui/src/app/entity/entity-aliases.controller.js
@@ -15,22 +15,27 @@
  */
 import './entity-aliases.scss';
 
+/* eslint-disable import/no-unresolved, import/default */
+
+import entityAliasDialogTemplate from './entity-alias-dialog.tpl.html';
+
+/* eslint-enable import/no-unresolved, import/default */
+
 /*@ngInject*/
 export default function EntityAliasesController(utils, entityService, toast, $scope, $mdDialog, $document, $q, $translate,
                                                   types, config) {
 
     var vm = this;
 
-    vm.isSingleEntityAlias = config.isSingleEntityAlias;
-    vm.singleEntityAlias = config.singleEntityAlias;
+    vm.types = types;
     vm.entityAliases = [];
     vm.title = config.customTitle ? config.customTitle : 'entity.aliases';
     vm.disableAdd = config.disableAdd;
     vm.aliasToWidgetsMap = {};
     vm.allowedEntityTypes = config.allowedEntityTypes;
 
-    vm.onFilterEntityChanged = onFilterEntityChanged;
     vm.addAlias = addAlias;
+    vm.editAlias = editAlias;
     vm.removeAlias = removeAlias;
 
     vm.cancel = cancel;
@@ -79,42 +84,61 @@ export default function EntityAliasesController(utils, entityService, toast, $sc
             }
         }
 
-        if (vm.isSingleEntityAlias) {
-            checkEntityAlias(vm.singleEntityAlias);
-        }
-
         for (aliasId in config.entityAliases) {
             var entityAlias = config.entityAliases[aliasId];
-            var result = {id: aliasId, alias: entityAlias.alias, filter: entityAlias.filter, changed: true};
-            checkEntityAlias(result);
+            var filter = entityAlias.filter;
+            if (!filter) {
+                filter = {
+                    resolveMultiple: false
+                };
+            }
+            if (!filter.resolveMultiple) {
+                filter.resolveMultiple = false;
+            }
+            var result = {id: aliasId, alias: entityAlias.alias, filter: filter};
             vm.entityAliases.push(result);
         }
     }
 
-    function checkEntityAlias(entityAlias) {
-        if (!entityAlias.filter || entityAlias.filter == null) {
-            entityAlias.filter = {};
-        }
+    function addAlias($event) {
+        openAliasDialog($event);
     }
 
-    function onFilterEntityChanged(entity, stateEntity, entityAlias) {
-        if (entityAlias) {
-            if (!entityAlias.alias || entityAlias.alias.length == 0) {
-                entityAlias.changed = false;
-            }
-            if (!entityAlias.changed && entityAlias.filter && entityAlias.filter.type) {
-                if (stateEntity) {
-                    entityAlias.alias =  $translate.instant('alias.state-entity');
-                } else {
-                    entityAlias.alias = entity.name;
-                }
-            }
-        }
+    function editAlias($event, entityAlias) {
+        openAliasDialog($event, entityAlias);
     }
 
-    function addAlias() {
-        var entityAlias = {id: utils.guid(), alias: '', filter: {}, changed: false};
-        vm.entityAliases.push(entityAlias);
+    function openAliasDialog($event, entityAlias) {
+        var isAdd = entityAlias ? false : true;
+        var aliasIndex;
+        if (!isAdd) {
+            aliasIndex = vm.entityAliases.indexOf(entityAlias);
+        }
+        $mdDialog.show({
+            controller: 'EntityAliasDialogController',
+            controllerAs: 'vm',
+            templateUrl: entityAliasDialogTemplate,
+            locals: {
+                isAdd: isAdd,
+                allowedEntityTypes: vm.allowedEntityTypes,
+                entityAliases: vm.entityAliases,
+                alias: isAdd ? null : angular.copy(entityAlias)
+            },
+            parent: angular.element($document[0].body),
+            fullscreen: true,
+            skipHide: true,
+            targetEvent: $event
+        }).then(function (alias) {
+            if (isAdd) {
+                vm.entityAliases.push(alias);
+            } else {
+                vm.entityAliases[aliasIndex] = alias;
+            }
+            if ($scope.theForm) {
+                $scope.theForm.$setDirty();
+            }
+        }, function () {
+        });
     }
 
     function removeAlias($event, entityAlias) {
@@ -157,43 +181,30 @@ export default function EntityAliasesController(utils, entityService, toast, $sc
         var uniqueAliasList = {};
 
         var valid = true;
-        var aliasId;
-        var alias;
-        var i;
-
-        if (vm.isSingleEntityAlias) {
-            if (!vm.singleEntityAlias.id) {
-                vm.singleEntityAlias.id = utils.guid();
-            }
-            for (i = 0; i < vm.entityAliases.length; i ++) {
-                alias = vm.entityAliases[i].alias;
-                if (alias === vm.singleEntityAlias.alias) {
-                    valid = false;
-                    break;
-                }
-            }
-        } else {
-            for (i = 0; i < vm.entityAliases.length; i++) {
-                aliasId = vm.entityAliases[i].id;
-                alias = vm.entityAliases[i].alias;
-                if (!uniqueAliasList[alias]) {
-                    uniqueAliasList[alias] = alias;
-                    entityAliases[aliasId] = {id: aliasId, alias: alias, filter: vm.entityAliases[i].filter};
-                } else {
-                    valid = false;
-                    break;
-                }
+        var message, aliasId, alias, filter;
+
+        for (var i = 0; i < vm.entityAliases.length; i++) {
+            aliasId = vm.entityAliases[i].id;
+            alias = vm.entityAliases[i].alias;
+            filter = vm.entityAliases[i].filter;
+            if (uniqueAliasList[alias]) {
+                valid = false;
+                message = $translate.instant('entity.duplicate-alias-error', {alias: alias});
+                break;
+            } else if (!filter || !filter.type) {
+                valid = false;
+                message = $translate.instant('entity.missing-entity-filter-error', {alias: alias});
+                break;
+            } else {
+                uniqueAliasList[alias] = alias;
+                entityAliases[aliasId] = {id: aliasId, alias: alias, filter: filter};
             }
         }
         if (valid) {
             $scope.theForm.$setPristine();
-            if (vm.isSingleEntityAlias) {
-                $mdDialog.hide(vm.singleEntityAlias);
-            } else {
-                $mdDialog.hide(entityAliases);
-            }
+            $mdDialog.hide(entityAliases);
         } else {
-            toast.showError($translate.instant('entity.duplicate-alias-error', {alias: alias}));
+            toast.showError(message);
         }
     }
 
diff --git a/ui/src/app/entity/entity-aliases.scss b/ui/src/app/entity/entity-aliases.scss
index 4180bce..9803be1 100644
--- a/ui/src/app/entity/entity-aliases.scss
+++ b/ui/src/app/entity/entity-aliases.scss
@@ -17,6 +17,7 @@
 .tb-aliases-dialog {
   .md-dialog-content {
     padding-bottom: 0px;
+    padding-top: 0px;
   }
   .tb-aliases-header {
     min-height: 40px;
@@ -33,5 +34,16 @@
     md-input-container {
       margin: 0px;
     }
+    .tb-resolve-multiple-switch {
+      padding-left: 10px;
+      .resolve-multiple-switch {
+        margin: 0;
+      }
+    }
+    .md-button {
+      &.md-icon-button {
+        margin: 0px;
+      }
+    }
   }
 }
diff --git a/ui/src/app/entity/entity-aliases.tpl.html b/ui/src/app/entity/entity-aliases.tpl.html
index 4fd33d9..40010a2 100644
--- a/ui/src/app/entity/entity-aliases.tpl.html
+++ b/ui/src/app/entity/entity-aliases.tpl.html
@@ -19,7 +19,7 @@
 	<form name="theForm" ng-submit="vm.save()">
 		<md-toolbar>
 			<div class="md-toolbar-tools">
-				<h2>{{ vm.isSingleEntityAlias ? ('entity.configure-alias' | translate:vm.singleEntityAlias ) : (vm.title | translate) }}</h2>
+				<h2>{{ vm.title | translate }}</h2>
 				<span flex></span>
 				<md-button class="md-icon-button" ng-click="vm.cancel()">
 					<ng-md-icon icon="close" aria-label="{{ 'dialog.close' | translate }}"></ng-md-icon>
@@ -28,66 +28,72 @@
 		</md-toolbar>
 		<md-progress-linear class="md-warn" md-mode="indeterminate" ng-disabled="!loading" ng-show="loading"></md-progress-linear>
 		<span style="min-height: 5px;" flex="" ng-show="!loading"></span>
-		<div class="tb-aliases-header" ng-show="!vm.isSingleEntityAlias" flex layout="row" layout-align="start center">
+		<div class="tb-aliases-header" flex layout="row" layout-align="start center">
 			<span flex="5"></span>
 			<div flex layout="row" layout-align="start center">
-				<span class="tb-header-label" translate flex="20" style="min-width: 150px;">entity.alias</span>
-				<div flex="80" layout="row" layout-align="start center" style="min-width: 240px; padding-left: 10px;">
-					<span class="tb-header-label" translate flex="70">alias.entity-filter</span>
-					<span class="tb-header-label" translate flex="30" style="padding-left: 10px;" >alias.resolve-multiple</span>
-				</div>
-				<span style="min-width: 40px; margin: 0 6px;"></span>
+				<span class="tb-header-label" translate flex="20" style="min-width: 150px;">alias.name</span>
+				<span class="tb-header-label" translate flex="70" style="padding-left: 10px;">alias.entity-filter</span>
+				<span class="tb-header-label" translate flex="10" style="padding-left: 10px; min-width: 120px;">alias.resolve-multiple</span>
+				<span style="min-width: 80px;"></span>
 			</div>
 		</div>
-		<md-divider ng-show="!vm.isSingleEntityAlias"></md-divider>
+		<md-divider></md-divider>
 		<md-dialog-content>
 			<div class="md-dialog-content">
 				<fieldset ng-disabled="loading">
-					<div ng-show="vm.isSingleEntityAlias" layout="row">
-						<tb-entity-filter flex
-										  allowed-entity-types="vm.allowedEntityTypes"
-										  ng-model="vm.singleEntityAlias.filter">
-						</tb-entity-filter>
-					</div>
-					<div ng-show="!vm.isSingleEntityAlias" style="height: 100%; overflow: auto; padding-bottom: 20px;">
-						<div ng-form name="aliasForm" flex layout="row" layout-align="start center" ng-repeat="entityAlias in vm.entityAliases track by $index">
-							<span flex="5">{{$index + 1}}.</span>
-							<div class="md-whiteframe-4dp tb-alias" flex layout="row" layout-align="start center">
-								<md-input-container flex="20" style="min-width: 150px;" md-no-float class="md-block">
-									<input required ng-change="entityAlias.changed=true" name="alias" placeholder="{{ 'entity.alias' | translate }}" ng-model="entityAlias.alias">
-									<div ng-messages="aliasForm.alias.$error">
-										<div translate ng-message="required">entity.alias-required</div>
-									</div>
-								</md-input-container>
-								<tb-entity-filter flex="80" style="min-width: 240px; padding-left: 10px;"
-												  hide-labels
-												  allowed-entity-types="vm.allowedEntityTypes"
-												  ng-model="entityAlias.filter"
-												  on-matching-entity-change="vm.onFilterEntityChanged(entity, stateEntity, entityAlias)">
-								</tb-entity-filter>
-								<md-button ng-disabled="loading" class="md-icon-button md-primary" style="min-width: 40px;"
-										   ng-click="vm.removeAlias($event, entityAlias)" aria-label="{{ 'action.remove' | translate }}">
-									<md-tooltip md-direction="top">
-										{{ 'entity.remove-alias' | translate }}
-									</md-tooltip>
-									<md-icon aria-label="{{ 'action.delete' | translate }}" class="material-icons">
-										close
-									</md-icon>
-								</md-button>
-							</div>
-						</div>
+					<div ng-form name="aliasForm" flex layout="row" layout-align="start center" ng-repeat="entityAlias in vm.entityAliases track by $index">
+						<span flex="5">{{$index + 1}}.</span>
+						<di class="md-whiteframe-4dp tb-alias" flex layout="row" layout-align="start center">
+							<md-input-container flex="20" style="min-width: 150px;" md-no-float class="md-block">
+								<input required name="alias" placeholder="{{ 'entity.alias' | translate }}" ng-model="entityAlias.alias">
+								<div ng-messages="aliasForm.alias.$error">
+									<div translate ng-message="required">entity.alias-required</div>
+								</div>
+							</md-input-container>
+							<tb-entity-filter-view
+									flex="70" style="padding-left: 10px;"
+									ng-model="entityAlias.filter">
+							</tb-entity-filter-view>
+							<section flex="10" style="padding-left: 10px; min-width: 120px;"
+									 class="tb-resolve-multiple-switch"
+									 layout="column"
+									 layout-align="center center">
+								<md-switch class="resolve-multiple-switch"
+										   ng-model="entityAlias.filter.resolveMultiple"
+										   aria-label="resolve-multiple-switcher">
+								</md-switch>
+							</section>
+							<md-button ng-disabled="loading" class="md-icon-button md-primary" style="min-width: 40px;"
+									   ng-click="vm.editAlias($event, entityAlias)" aria-label="{{ 'action.edit' | translate }}">
+								<md-tooltip md-direction="top">
+									{{ 'alias.edit' | translate }}
+								</md-tooltip>
+								<md-icon aria-label="{{ 'alias.edit' | translate }}" class="material-icons">
+									edit
+								</md-icon>
+							</md-button>
+							<md-button ng-disabled="loading" class="md-icon-button md-primary" style="min-width: 40px;"
+									   ng-click="vm.removeAlias($event, entityAlias)" aria-label="{{ 'action.remove' | translate }}">
+								<md-tooltip md-direction="top">
+									{{ 'entity.remove-alias' | translate }}
+								</md-tooltip>
+								<md-icon aria-label="{{ 'action.delete' | translate }}" class="material-icons">
+									close
+								</md-icon>
+							</md-button>
+						</di>
 					</div>
 				</fieldset>
 			</div>
 		</md-dialog-content>
 		<md-dialog-actions layout="row">
-			<md-button ng-show="!vm.isSingleEntityAlias && !vm.disableAdd" ng-disabled="loading" class="md-primary md-raised"
+			<md-button ng-show="!vm.disableAdd" ng-disabled="loading" class="md-primary md-raised"
 					   ng-click="vm.addAlias($event)"
-					   aria-label="{{ 'action.add' | translate }}">
+					   aria-label="{{ 'alias.add' | translate }}">
 				<md-tooltip md-direction="top">
-					{{ 'entity.add-alias' | translate }}
+					{{ 'alias.add' | translate }}
 				</md-tooltip>
-				<span translate>action.add</span>
+				<span translate>alias.add</span>
 			</md-button>
 			<span flex></span>
 			<md-button ng-disabled="loading || theForm.$invalid || !theForm.$dirty" type="submit" class="md-raised md-primary">
diff --git a/ui/src/app/entity/entity-filter.directive.js b/ui/src/app/entity/entity-filter.directive.js
index 0f9cf5c..9566716 100644
--- a/ui/src/app/entity/entity-filter.directive.js
+++ b/ui/src/app/entity/entity-filter.directive.js
@@ -17,16 +17,13 @@
 /* eslint-disable import/no-unresolved, import/default */
 
 import entityFilterTemplate from './entity-filter.tpl.html';
-import entityFilterDialogTemplate from './entity-filter-dialog.tpl.html';
 
 /* eslint-enable import/no-unresolved, import/default */
 
-import EntityFilterDialogController from './entity-filter-dialog.controller';
-
 import './entity-filter.scss';
 
 /*@ngInject*/
-export default function EntityFilterDirective($compile, $templateCache, $q, $document, $mdDialog, types) {
+export default function EntityFilterDirective($compile, $templateCache, $q, $document, $mdDialog, types, entityService) {
 
     var linker = function (scope, element, attrs, ngModelCtrl) {
 
@@ -35,66 +32,59 @@ export default function EntityFilterDirective($compile, $templateCache, $q, $doc
 
         scope.ngModelCtrl = ngModelCtrl;
         scope.types = types;
-        scope.hideLabels = angular.isDefined(attrs.hideLabels);
+        scope.aliasFilterTypes = entityService.getAliasFilterTypesByEntityTypes(scope.allowedEntityTypes);
 
-        scope.updateValidity = function() {
-            if (ngModelCtrl.$viewValue) {
-                var value = ngModelCtrl.$viewValue;
-                ngModelCtrl.$setValidity('filter', value.type ? true : false);
+        scope.$watch('filter.type', function (newType, prevType) {
+            if (newType && newType != prevType) {
+                updateFilter();
             }
-        }
+        });
 
-        ngModelCtrl.$render = function () {
-            if (ngModelCtrl.$viewValue) {
-                scope.model = angular.copy(ngModelCtrl.$viewValue);
-            } else {
-                scope.model = {
-                    type: null,
-                    resolveMultiple: false
-                }
+        function updateFilter() {
+            var filter = {};
+            filter.type = scope.filter.type;
+            filter.resolveMultiple = scope.filter.resolveMultiple;
+            switch (filter.type) {
+                case types.aliasFilterType.entityList.value:
+                    filter.entityType = null;
+                    filter.entityList = [];
+                    break;
+                case types.aliasFilterType.entityName.value:
+                    filter.entityType = null;
+                    filter.entityNameFilter = '';
+                    break;
+                case types.aliasFilterType.stateEntity.value:
+                    break;
+                case types.aliasFilterType.assetType.value:
+                    filter.assetType = null;
+                    filter.assetNameFilter = '';
+                    break;
+                case types.aliasFilterType.deviceType.value:
+                    filter.deviceType = null;
+                    filter.deviceNameFilter = '';
+                    break;
+                //TODO: Alias filter
             }
+            scope.filter = filter;
         }
 
-        scope.$watch('model.resolveMultiple', function () {
-            if (ngModelCtrl.$viewValue) {
-                var value = ngModelCtrl.$viewValue;
-                value.resolveMultiple = scope.model.resolveMultiple;
-                ngModelCtrl.$setViewValue(value);
-                scope.updateValidity();
-            }
+        scope.$watch('filter', function () {
+            scope.updateView();
         });
 
-        scope.editFilter = function($event) {
-            openEntityFilterDialog($event, false);
+        scope.updateView = function() {
+            ngModelCtrl.$setViewValue(scope.filter);
         }
 
-        scope.createFilter = function($event) {
-            openEntityFilterDialog($event, true);
-        }
-
-        function openEntityFilterDialog($event, isAdd) {
-            $mdDialog.show({
-                controller: EntityFilterDialogController,
-                controllerAs: 'vm',
-                templateUrl: entityFilterDialogTemplate,
-                locals: {
-                    isAdd: isAdd,
-                    allowedEntityTypes: scope.allowedEntityTypes,
-                    filter: angular.copy(scope.model)
-                },
-                parent: angular.element($document[0].body),
-                fullscreen: true,
-                skipHide: true,
-                targetEvent: $event
-            }).then(function (result) {
-                scope.model = result.filter;
-                ngModelCtrl.$setViewValue(result.filter);
-                scope.updateValidity();
-                if (scope.onMatchingEntityChange) {
-                    scope.onMatchingEntityChange({entity: result.entity, stateEntity: result.stateEntity});
+        ngModelCtrl.$render = function () {
+            if (ngModelCtrl.$viewValue) {
+                scope.filter = ngModelCtrl.$viewValue;
+            } else {
+                scope.filter = {
+                    type: null,
+                    resolveMultiple: false
                 }
-            }, function () {
-            });
+            }
         }
 
         $compile(element.contents())(scope);
@@ -106,8 +96,8 @@ export default function EntityFilterDirective($compile, $templateCache, $q, $doc
         require: "^ngModel",
         link: linker,
         scope: {
-            allowedEntityTypes: '=?',
-            onMatchingEntityChange: '&'
+            theForm: '=',
+            allowedEntityTypes: '=?'
         }
     };
 
diff --git a/ui/src/app/entity/entity-filter.scss b/ui/src/app/entity/entity-filter.scss
index 10ddcd3..ebbea3d 100644
--- a/ui/src/app/entity/entity-filter.scss
+++ b/ui/src/app/entity/entity-filter.scss
@@ -13,22 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 .tb-entity-filter {
-  .tb-filter-switch {
-    padding-left: 10px;
-    .filter-switch {
-      margin: 0;
-    }
-    .filter-label {
-      margin: 5px 0;
-    }
-  }
-  .tb-error-messages {
-    margin-top: -11px;
-    height: 35px;
-    .tb-error-message {
-      padding-left: 8px;
-      padding-top: 14px;
-    }
-  }
+
 }
\ No newline at end of file
diff --git a/ui/src/app/entity/entity-filter.tpl.html b/ui/src/app/entity/entity-filter.tpl.html
index 6ce95fa..77b6d3c 100644
--- a/ui/src/app/entity/entity-filter.tpl.html
+++ b/ui/src/app/entity/entity-filter.tpl.html
@@ -15,37 +15,77 @@
     limitations under the License.
 
 -->
-<section layout='row' class="tb-entity-filter">
-    <section layout="row" flex="70">
-        <section flex layout="column" layout-align="center start">
-            <div ng-if="model.type">{{ types.aliasFilterType[model.type].name | translate }}</div>
-            <md-button ng-if="!model.type"
-                       ng-disabled="loading" class="md-primary"
-                       ng-click="createFilter($event)"
-                       aria-label="{{ 'alias.create-entity-filter' | translate }}">
-                <md-icon aria-label="{{ 'alias.create-entity-filter' | translate }}"
-                         class="material-icons">
-                    add
-                </md-icon>
-                {{ 'alias.create-entity-filter' | translate }}
-            </md-button>
-        </section>
-        <md-button ng-if="model.type" ng-disabled="loading" class="md-icon-button md-primary"
-                   style="min-width: 40px;"
-                   ng-click="editFilter($event)"
-                   aria-label="{{ 'alias.edit-entity-filter' | translate }}">
-            <md-tooltip md-direction="top">
-                {{ 'alias.edit-entity-filter' | translate }}
-            </md-tooltip>
-            <md-icon aria-label="{{ 'alias.edit-entity-filter' | translate }}"
-                     class="material-icons">
-                edit
-            </md-icon>
-        </md-button>
+<div layout='column' class="tb-entity-filter">
+    <md-input-container class="md-block">
+        <label>{{ 'alias.filter-type' | translate }}</label>
+        <md-select required name="filterType"
+                   ng-model="filter.type" aria-label="{{ 'alias.filter-type' | translate }}">
+            <md-option ng-repeat="type in aliasFilterTypes" ng-value="type.value">
+                {{type.name | translate}}
+            </md-option>
+        </md-select>
+        <div ng-messages="theForm.filterType.$error">
+            <div ng-message="required" translate>alias.filter-type-required</div>
+        </div>
+    </md-input-container>
+    <section layout="column" ng-if="filter.type == types.aliasFilterType.entityList.value" id="entityListFilter">
+        <tb-entity-type-select
+                ng-model="filter.entityType"
+                the-form="theForm"
+                tb-required="true"
+                allowed-entity-types="allowedEntityTypes">
+        </tb-entity-type-select>
+        <tb-entity-list
+                ng-model="filter.entityList"
+                tb-required="true"
+                entity-type="filter.entityType">
+        </tb-entity-list>
     </section>
-    <section class="tb-filter-switch" layout="column" flex="30" layout-align="center center">
-        <label ng-if="!hideLabels" class="tb-small filter-label" translate>alias.resolve-multiple</label>
-        <md-switch class="filter-switch" ng-model="model.resolveMultiple" aria-label="resolve-multiple-switcher">
-        </md-switch>
+    <section flex layout="column" ng-if="filter.type == types.aliasFilterType.entityName.value" id="entityNameFilter">
+        <tb-entity-type-select
+                ng-model="filter.entityType"
+                the-form="theForm"
+                tb-required="true"
+                allowed-entity-types="allowedEntityTypes">
+        </tb-entity-type-select>
+        <md-input-container class="md-block">
+            <label translate>entity.name-starts-with</label>
+            <input required name="entityNameFilter"
+                   ng-model="filter.entityNameFilter"
+                   aria-label="{{ 'entity.name-starts-with' | translate }}">
+            <div ng-messages="theForm.entityNameFilter.$error">
+                <div ng-message="required" translate>entity.entity-name-filter-required</div>
+            </div>
+        </md-input-container>
     </section>
-</section>
+    <section layout="column" ng-if="filter.type == types.aliasFilterType.stateEntity.value" id="stateEntityFilter">
+    </section>
+    <section layout="column" ng-if="filter.type == types.aliasFilterType.assetType.value" id="assetTypeFilter">
+        <tb-entity-subtype-autocomplete
+                tb-required="true"
+                the-form="theForm"
+                ng-model="filter.assetType"
+                entity-type="types.entityType.asset">
+        </tb-entity-subtype-autocomplete>
+        <md-input-container class="md-block">
+            <label translate>asset.name-starts-with</label>
+            <input name="assetNameFilter"
+                   ng-model="filter.assetNameFilter"
+                   aria-label="{{ 'asset.name-starts-with' | translate }}">
+        </md-input-container>
+    </section>
+    <section layout="column" ng-if="filter.type == types.aliasFilterType.deviceType.value" id="deviceTypeFilter">
+        <tb-entity-subtype-autocomplete
+                tb-required="true"
+                the-form="theForm"
+                ng-model="filter.deviceType"
+                entity-type="types.entityType.device">
+        </tb-entity-subtype-autocomplete>
+        <md-input-container class="md-block">
+            <label translate>device.name-starts-with</label>
+            <input name="deviceNameFilter"
+                   ng-model="filter.deviceNameFilter"
+                   aria-label="{{ 'device.name-starts-with' | translate }}">
+        </md-input-container>
+    </section>
+</div>
diff --git a/ui/src/app/entity/entity-filter-view.directive.js b/ui/src/app/entity/entity-filter-view.directive.js
new file mode 100644
index 0000000..6a358b2
--- /dev/null
+++ b/ui/src/app/entity/entity-filter-view.directive.js
@@ -0,0 +1,106 @@
+/*
+ * Copyright © 2016-2017 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* eslint-disable import/no-unresolved, import/default */
+
+import entityFilterViewTemplate from './entity-filter-view.tpl.html';
+
+/* eslint-enable import/no-unresolved, import/default */
+
+import './entity-filter-view.scss';
+
+/*@ngInject*/
+export default function EntityFilterViewDirective($compile, $templateCache, $q, $document, $mdDialog, $translate, types/*, entityService*/) {
+
+    var linker = function (scope, element, attrs, ngModelCtrl) {
+
+        var template = $templateCache.get(entityFilterViewTemplate);
+        element.html(template);
+
+        scope.ngModelCtrl = ngModelCtrl;
+        scope.types = types;
+        scope.filterDisplayValue = '';
+
+        scope.$watch('filter', function () {
+            scope.updateDisplayValue();
+        });
+
+        scope.updateDisplayValue = function() {
+            if (scope.filter && scope.filter.type) {
+                var entityType;
+                var prefix;
+                switch (scope.filter.type) {
+                    case types.aliasFilterType.entityList.value:
+                        entityType = scope.filter.entityType;
+                        var count = scope.filter.entityList.length;
+                        scope.filterDisplayValue = $translate.instant(types.entityTypeTranslations[entityType].list, {count: count}, 'messageformat');
+                        break;
+                    case types.aliasFilterType.entityName.value:
+                        entityType = scope.filter.entityType;
+                        prefix = scope.filter.entityNameFilter;
+                        scope.filterDisplayValue = $translate.instant(types.entityTypeTranslations[entityType].nameStartsWith, {prefix: prefix});
+                        break;
+                    case types.aliasFilterType.stateEntity.value:
+                        scope.filterDisplayValue = $translate.instant('alias.filter-type-state-entity-description');
+                        break;
+                    case types.aliasFilterType.assetType.value:
+                        var assetType = scope.filter.assetType;
+                        prefix = scope.filter.assetNameFilter;
+                        if (prefix && prefix.length) {
+                            scope.filterDisplayValue = $translate.instant('alias.filter-type-asset-type-and-name-description', {assetType: assetType, prefix: prefix});
+                        } else {
+                            scope.filterDisplayValue = $translate.instant('alias.filter-type-asset-type-description', {assetType: assetType});
+                        }
+                        break;
+                    case types.aliasFilterType.deviceType.value:
+                        var deviceType = scope.filter.deviceType;
+                        prefix = scope.filter.deviceNameFilter;
+                        if (prefix && prefix.length) {
+                            scope.filterDisplayValue = $translate.instant('alias.filter-type-device-type-and-name-description', {deviceType: deviceType, prefix: prefix});
+                        } else {
+                            scope.filterDisplayValue = $translate.instant('alias.filter-type-device-type-description', {deviceType: deviceType});
+                        }
+                        break;
+                    //TODO: Alias filter
+                    default:
+                        scope.filterDisplayValue = scope.filter.type;
+                        break;
+                }
+            } else {
+                scope.filterDisplayValue = '';
+            }
+        }
+
+        ngModelCtrl.$render = function () {
+            if (ngModelCtrl.$viewValue) {
+                scope.filter = ngModelCtrl.$viewValue;
+            } else {
+                scope.filter = null;
+            }
+        }
+
+        $compile(element.contents())(scope);
+
+    }
+
+    return {
+        restrict: "E",
+        require: "^ngModel",
+        link: linker,
+        scope: true
+    };
+
+}
diff --git a/ui/src/app/entity/entity-filter-view.scss b/ui/src/app/entity/entity-filter-view.scss
new file mode 100644
index 0000000..437f296
--- /dev/null
+++ b/ui/src/app/entity/entity-filter-view.scss
@@ -0,0 +1,33 @@
+/**
+ * Copyright © 2016-2017 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+.tb-entity-filter-view {
+  .entity-filter-empty {
+    color: rgba(221, 44, 0, 0.87);
+    font-size: 14px;
+    line-height: 16px;
+  }
+  .entity-filter-type {
+    font-size: 14px;
+    line-height: 16px;
+    color: rgba(0, 0, 0, 0.570588);
+  }
+  .entity-filter-value {
+    font-size: 14px;
+    line-height: 16px;
+    color: rgba(0, 0, 0, 0.570588);
+  }
+}
\ No newline at end of file
diff --git a/ui/src/app/entity/entity-filter-view.tpl.html b/ui/src/app/entity/entity-filter-view.tpl.html
new file mode 100644
index 0000000..84c5405
--- /dev/null
+++ b/ui/src/app/entity/entity-filter-view.tpl.html
@@ -0,0 +1,24 @@
+<!--
+
+    Copyright © 2016-2017 The Thingsboard Authors
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+        http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+
+-->
+
+<div layout='column' class="tb-entity-filter-view">
+    <div ng-if="!filter || !filter.type" class="entity-filter-empty" translate>alias.no-entity-filter-specified</div>
+    <div ng-if="filter && filter.type" layout="column">
+        <div class="entity-filter-value">{{ filterDisplayValue }}</div>
+    </div>
+</div>
diff --git a/ui/src/app/entity/entity-type-select.directive.js b/ui/src/app/entity/entity-type-select.directive.js
index 1dc6741..bca2ee0 100644
--- a/ui/src/app/entity/entity-type-select.directive.js
+++ b/ui/src/app/entity/entity-type-select.directive.js
@@ -71,7 +71,7 @@ export default function EntityTypeSelect($compile, $templateCache, utils, userSe
         }
 
         scope.typeName = function(type) {
-            return utils.entityTypeName(type);
+            return type ? types.entityTypeTranslations[type].type : '';
         }
 
         scope.updateValidity = function () {
diff --git a/ui/src/app/entity/index.js b/ui/src/app/entity/index.js
index 6be118f..2fc1d63 100644
--- a/ui/src/app/entity/index.js
+++ b/ui/src/app/entity/index.js
@@ -15,6 +15,7 @@
  */
 
 import EntityAliasesController from './entity-aliases.controller';
+import EntityAliasDialogController from './entity-alias-dialog.controller';
 import EntityTypeSelectDirective from './entity-type-select.directive';
 import EntitySubtypeSelectDirective from './entity-subtype-select.directive';
 import EntitySubtypeAutocompleteDirective from './entity-subtype-autocomplete.directive';
@@ -22,6 +23,7 @@ import EntityAutocompleteDirective from './entity-autocomplete.directive';
 import EntityListDirective from './entity-list.directive';
 import EntitySelectDirective from './entity-select.directive';
 import EntityFilterDirective from './entity-filter.directive';
+import EntityFilterViewDirective from './entity-filter-view.directive';
 import AliasesEntitySelectPanelController from './aliases-entity-select-panel.controller';
 import AliasesEntitySelectDirective from './aliases-entity-select.directive';
 import AddAttributeDialogController from './attribute/add-attribute-dialog.controller';
@@ -32,6 +34,7 @@ import RelationTypeAutocompleteDirective from './relation/relation-type-autocomp
 
 export default angular.module('thingsboard.entity', [])
     .controller('EntityAliasesController', EntityAliasesController)
+    .controller('EntityAliasDialogController', EntityAliasDialogController)
     .controller('AliasesEntitySelectPanelController', AliasesEntitySelectPanelController)
     .controller('AddAttributeDialogController', AddAttributeDialogController)
     .controller('AddWidgetToDashboardDialogController', AddWidgetToDashboardDialogController)
@@ -42,6 +45,7 @@ export default angular.module('thingsboard.entity', [])
     .directive('tbEntityList', EntityListDirective)
     .directive('tbEntitySelect', EntitySelectDirective)
     .directive('tbEntityFilter', EntityFilterDirective)
+    .directive('tbEntityFilterView', EntityFilterViewDirective)
     .directive('tbAliasesEntitySelect', AliasesEntitySelectDirective)
     .directive('tbAttributeTable', AttributeTableDirective)
     .directive('tbRelationTable', RelationTableDirective)
diff --git a/ui/src/app/entity/relation/relation-table.directive.js b/ui/src/app/entity/relation/relation-table.directive.js
index 729ff36..f0c192b 100644
--- a/ui/src/app/entity/relation/relation-table.directive.js
+++ b/ui/src/app/entity/relation/relation-table.directive.js
@@ -218,9 +218,9 @@ function RelationTableController($scope, $q, $mdDialog, $document, $translate, $
             function success(allRelations) {
                 allRelations.forEach(function(relation) {
                     if (vm.direction == vm.types.entitySearchDirection.from) {
-                        relation.toEntityTypeName = $translate.instant(utils.entityTypeName(relation.to.entityType));
+                        relation.toEntityTypeName = $translate.instant(types.entityTypeTranslations[relation.to.entityType].type);
                     } else {
-                        relation.fromEntityTypeName = $translate.instant(utils.entityTypeName(relation.from.entityType));
+                        relation.fromEntityTypeName = $translate.instant(types.entityTypeTranslations[relation.from.entityType].type);
                     }
                 });
                 vm.allRelations = allRelations;
diff --git a/ui/src/app/import-export/import-export.service.js b/ui/src/app/import-export/import-export.service.js
index 9b36c38..8d4a098 100644
--- a/ui/src/app/import-export/import-export.service.js
+++ b/ui/src/app/import-export/import-export.service.js
@@ -365,7 +365,6 @@ export default function ImportExport($log, $translate, $q, $mdDialog, $document,
             alias = aliasInfo.aliasName;
             filter = {
                 type: types.aliasFilterType.entityList.value,
-                stateEntity: false,
                 entityType: types.entityType.device,
                 entityList: [aliasInfo.deviceId],
                 resolveMultiple: false
@@ -378,7 +377,6 @@ export default function ImportExport($log, $translate, $q, $mdDialog, $document,
                 resolveMultiple: false
             }
             if (filter.type == types.aliasFilterType.entityList.value) {
-                filter.stateEntity = false;
                 filter.entityList = aliasInfo.deviceFilter.deviceList
             } else {
                 filter.entityNameFilter = aliasInfo.deviceFilter.deviceNameFilter;
@@ -391,7 +389,6 @@ export default function ImportExport($log, $translate, $q, $mdDialog, $document,
                 resolveMultiple: false
             }
             if (filter.type == types.aliasFilterType.entityList.value) {
-                filter.stateEntity = false;
                 filter.entityList = aliasInfo.entityFilter.entityList;
             } else {
                 filter.entityNameFilter = aliasInfo.entityFilter.entityNameFilter;
@@ -662,8 +659,6 @@ export default function ImportExport($log, $translate, $q, $mdDialog, $document,
                     entityAliases: missingEntityAliases,
                     widgets: widgets,
                     isSingleWidget: isSingleWidget,
-                    isSingleEntityAlias: false,
-                    singleEntityAlias: null,
                     customTitle: customTitle,
                     disableAdd: true
                 }
diff --git a/ui/src/app/locale/locale.constant.js b/ui/src/app/locale/locale.constant.js
index 9c6695b..88a6021 100644
--- a/ui/src/app/locale/locale.constant.js
+++ b/ui/src/app/locale/locale.constant.js
@@ -113,23 +113,30 @@ export default angular.module('thingsboard.locale', [])
                     "alarm-required": "Alarm is required"
                 },
                 "alias": {
+                    "add": "Add alias",
+                    "edit": "Edit alias",
+                    "name": "Alias name",
+                    "name-required": "Alias name is required",
+                    "duplicate-alias": "Alias with same name is already exists.",
                     "filter-type-entity-list": "Entity list",
                     "filter-type-entity-name": "Entity name",
+                    "filter-type-state-entity": "Entity from dashboard state",
+                    "filter-type-state-entity-description": "Entity taken from dashboard state parameters",
                     "filter-type-asset-type": "Asset type",
+                    "filter-type-asset-type-description": "Assets of type '{{assetType}}'",
+                    "filter-type-asset-type-and-name-description": "Assets of type '{{assetType}}' and with name starting with '{{prefix}}'",
                     "filter-type-device-type": "Device type",
+                    "filter-type-device-type-description": "Devices of type '{{deviceType}}'",
+                    "filter-type-device-type-and-name-description": "Devices of type '{{deviceType}}' and with name starting with '{{prefix}}'",
                     "filter-type-relations-query": "Relations query",
                     "filter-type-asset-search-query": "Asset search query",
                     "filter-type-device-search-query": "Device search query",
                     "entity-filter": "Entity filter",
-                    "create-entity-filter": "Create entity filter",
-                    "edit-entity-filter": "Edit entity filter",
-                    "entity-filter-required": "Entity filter is required.",
                     "resolve-multiple": "Resolve as multiple entities",
                     "filter-type": "Filter type",
                     "filter-type-required": "Filter type is required.",
-                    "use-state-entity": "Use state entity",
-                    "state-entity": "State entity",
                     "entity-filter-no-entity-matched": "No entities matching specified filter were found.",
+                    "no-entity-filter-specified": "No entity filter specified"
                 },
                 "asset": {
                     "asset": "Asset",
@@ -185,7 +192,8 @@ export default angular.module('thingsboard.locale', [])
                     "idCopiedMessage": "Asset Id has been copied to clipboard",
                     "select-asset": "Select asset",
                     "no-assets-matching": "No assets matching '{{entity}}' were found.",
-                    "asset-required": "Asset is required"
+                    "asset-required": "Asset is required",
+                    "name-starts-with": "Asset name starts with"
                 },
                 "attribute": {
                     "attributes": "Attributes",
@@ -463,7 +471,7 @@ export default angular.module('thingsboard.locale', [])
                     "alias-required": "Device alias is required.",
                     "remove-alias": "Remove device alias",
                     "add-alias": "Add device alias",
-                    "name-starts-with": "Name starts with",
+                    "name-starts-with": "Device name starts with",
                     "device-list": "Device list",
                     "use-device-name-filter": "Use filter",
                     "device-list-empty": "No devices selected.",
@@ -549,6 +557,7 @@ export default angular.module('thingsboard.locale', [])
                     "unable-delete-entity-alias-title": "Unable to delete entity alias",
                     "unable-delete-entity-alias-text": "Entity alias '{{entityAlias}}' can't be deleted as it used by the following widget(s):<br/>{{widgetsList}}",
                     "duplicate-alias-error": "Duplicate alias found '{{alias}}'.<br>Entity aliases must be unique whithin the dashboard.",
+                    "missing-entity-filter-error": "Filter is missing for alias '{{alias}}'.",
                     "configure-alias": "Configure '{{alias}}' alias",
                     "alias": "Alias",
                     "alias-required": "Entity alias is required.",
@@ -562,24 +571,42 @@ export default angular.module('thingsboard.locale', [])
                     "entity-name-filter-required": "Entity name filter is required.",
                     "entity-name-filter-no-entity-matched": "No entities starting with '{{entity}}' were found.",
                     "all-subtypes": "All",
+                    "select-entities": "Select entities",
+                    "no-aliases-found": "No aliases found.",
+                    "no-alias-matching": "'{{alias}}' not found.",
+                    "create-new-alias": "Create a new one!",
+                    "no-keys-found": "No keys found.",
+                    "no-key-matching": "'{{key}}' not found.",
+                    "create-new-key": "Create a new one!",
                     "type": "Type",
                     "type-required": "Entity type is required.",
                     "type-device": "Device",
+                    "list-of-devices": "{ count, select, 1 {One device} other {List of # devices} }",
+                    "device-name-starts-with": "Devices whose names start with '{{prefix}}'",
                     "type-asset": "Asset",
+                    "list-of-assets": "{ count, select, 1 {One asset} other {List of # assets} }",
+                    "asset-name-starts-with": "Assets whose names start with '{{prefix}}'",
                     "type-rule": "Rule",
+                    "list-of-rules": "{ count, select, 1 {One rule} other {List of # rules} }",
+                    "rule-name-starts-with": "Rules whose names start with '{{prefix}}'",
                     "type-plugin": "Plugin",
+                    "list-of-plugins": "{ count, select, 1 {One plugin} other {List of # plugins} }",
+                    "plugin-name-starts-with": "Plugins whose names start with '{{prefix}}'",
                     "type-tenant": "Tenant",
+                    "list-of-tenants": "{ count, select, 1 {One tenant} other {List of # tenants} }",
+                    "tenant-name-starts-with": "Tenants whose names start with '{{prefix}}'",
                     "type-customer": "Customer",
+                    "list-of-customers": "{ count, select, 1 {One customer} other {List of # customers} }",
+                    "customer-name-starts-with": "Customers whose names start with '{{prefix}}'",
                     "type-user": "User",
+                    "list-of-users": "{ count, select, 1 {One user} other {List of # users} }",
+                    "user-name-starts-with": "Users whose names start with '{{prefix}}'",
                     "type-dashboard": "Dashboard",
+                    "list-of-dashboards": "{ count, select, 1 {One dashboard} other {List of # dashboards} }",
+                    "dashboard-name-starts-with": "Dashboards whose names start with '{{prefix}}'",
                     "type-alarm": "Alarm",
-                    "select-entities": "Select entities",
-                    "no-aliases-found": "No aliases found.",
-                    "no-alias-matching": "'{{alias}}' not found.",
-                    "create-new-alias": "Create a new one!",
-                    "no-keys-found": "No keys found.",
-                    "no-key-matching": "'{{key}}' not found.",
-                    "create-new-key": "Create a new one!"
+                    "list-of-alarms": "{ count, select, 1 {One alarms} other {List of # alarms} }",
+                    "alarm-name-starts-with": "Alarms whose names start with '{{prefix}}'"
                 },
                 "event": {
                     "event-type": "Event type",