thingsboard-developers

Changes

Details

diff --git a/ui/src/app/api/alias-controller.js b/ui/src/app/api/alias-controller.js
new file mode 100644
index 0000000..7794363
--- /dev/null
+++ b/ui/src/app/api/alias-controller.js
@@ -0,0 +1,259 @@
+/*
+ * 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.
+ */
+
+const varsRegex = /\$\{([^\}]*)\}/g;
+
+export default class AliasController {
+
+    constructor($scope, $q, $filter, utils, types, entityService, stateController, entityAliases) {
+        this.$scope = $scope;
+        this.$q = $q;
+        this.$filter = $filter;
+        this.utils = utils;
+        this.types = types;
+        this.entityService = entityService;
+        this.stateController = stateController;
+        this.entityAliases = angular.copy(entityAliases);
+        this.resolvedAliases = {};
+        this.resolvedAliasesPromise = {};
+        this.resolvedAliasesToStateEntities = {};
+    }
+
+    updateEntityAliases(newEntityAliases) {
+        var changedAliasIds = [];
+        for (var aliasId in newEntityAliases) {
+            var newEntityAlias = newEntityAliases[aliasId];
+            var prevEntityAlias = this.entityAliases[aliasId];
+            if (!angular.equals(newEntityAlias, prevEntityAlias)) {
+                changedAliasIds.push(aliasId);
+                this.setAliasUnresolved(aliasId);
+            }
+        }
+        for (aliasId in this.entityAliases) {
+            if (!newEntityAliases[aliasId]) {
+                changedAliasIds.push(aliasId);
+                this.setAliasUnresolved(aliasId);
+            }
+        }
+        this.entityAliases = angular.copy(newEntityAliases);
+        if (changedAliasIds.length) {
+            this.$scope.$broadcast('entityAliasesChanged', changedAliasIds);
+        }
+    }
+
+    dashboardStateChanged() {
+        var newEntityId = this.stateController.getStateParams().entityId;
+        var changedAliasIds = [];
+        for (var aliasId in this.resolvedAliasesToStateEntities) {
+            var prevEntityId = this.resolvedAliasesToStateEntities[aliasId];
+            if (!angular.equals(newEntityId, prevEntityId)) {
+                changedAliasIds.push(aliasId);
+                this.setAliasUnresolved(aliasId);
+            }
+        }
+        if (changedAliasIds.length) {
+            this.$scope.$broadcast('entityAliasesChanged', changedAliasIds);
+        }
+    }
+
+    setAliasUnresolved(aliasId) {
+        delete this.resolvedAliases[aliasId];
+        delete this.resolvedAliasesPromise[aliasId];
+        delete this.resolvedAliasesToStateEntities[aliasId];
+    }
+
+    getEntityAliases() {
+        return this.entityAliases;
+    }
+
+    getAliasInfo(aliasId) {
+        var deferred = this.$q.defer();
+        var aliasInfo = this.resolvedAliases[aliasId];
+        if (aliasInfo) {
+            deferred.resolve(aliasInfo);
+            return deferred.promise;
+        } else if (this.resolvedAliasesPromise[aliasId]) {
+           return this.resolvedAliasesPromise[aliasId];
+        } else {
+            this.resolvedAliasesPromise[aliasId] = deferred.promise;
+            var aliasCtrl = this;
+            var entityAlias = this.entityAliases[aliasId];
+            if (entityAlias) {
+                this.entityService.resolveAlias(entityAlias, this.stateController.getStateParams()).then(
+                    function success(aliasInfo) {
+                        aliasCtrl.resolvedAliases[aliasId] = aliasInfo;
+                        if (entityAlias.filter.stateEntity) {
+                            aliasCtrl.resolvedAliasesToStateEntities[aliasId] =
+                                aliasCtrl.stateController.getStateParams().entityId;
+                        }
+                        deferred.resolve(aliasInfo);
+                    },
+                    function fail() {
+                        deferred.reject();
+                    }
+                );
+            } else {
+                deferred.reject();
+            }
+            return this.resolvedAliasesPromise[aliasId];
+        }
+    }
+
+    resolveDatasource(datasource) {
+        var deferred = this.$q.defer();
+        if (datasource.type === this.types.datasourceType.entity) {
+            if (datasource.entityAliasId) {
+                this.getAliasInfo(datasource.entityAliasId).then(
+                    function success(aliasInfo) {
+                        datasource.aliasName = aliasInfo.alias;
+                        if (aliasInfo.resolveMultiple) {
+                            var resolvedEntities = aliasInfo.resolvedEntities;
+                            if (resolvedEntities && resolvedEntities.length) {
+                                var datasources = [];
+                                for (var i=0;i<resolvedEntities.length;i++) {
+                                    var resolvedEntity = resolvedEntities[i];
+                                    var newDatasource = angular.copy(datasource);
+                                    newDatasource.entityId = resolvedEntity.id;
+                                    newDatasource.entityType = resolvedEntity.entityType;
+                                    newDatasource.entityName = resolvedEntity.name;
+                                    newDatasource.name = resolvedEntity.name;
+                                    newDatasource.generated = i > 0 ? true : false;
+                                    datasources.push(newDatasource);
+                                }
+                                deferred.resolve(datasources);
+                            } else {
+                                deferred.reject();
+                            }
+                        } else {
+                            var entity = aliasInfo.currentEntity;
+                            datasource.entityId = entity.id;
+                            datasource.entityType = entity.entityType;
+                            datasource.entityName = entity.name;
+                            datasource.name = entity.name;
+                            deferred.resolve([datasource]);
+                        }
+                    },
+                    function fail() {
+                        deferred.reject();
+                    }
+                );
+            } else { // entityId
+                datasource.aliasName = datasource.entityName;
+                datasource.name = datasource.entityName;
+                deferred.resolve([datasource]);
+            }
+        } else { // function
+            deferred.resolve([datasource]);
+        }
+        return deferred.promise;
+    }
+
+    resolveDatasources(datasources) {
+
+        function updateDataKeyLabel(dataKey, datasource) {
+            if (!dataKey.pattern) {
+                dataKey.pattern = angular.copy(dataKey.label);
+            }
+            var pattern = dataKey.pattern;
+            var label = dataKey.pattern;
+            var match = varsRegex.exec(pattern);
+            while (match !== null) {
+                var variable = match[0];
+                var variableName = match[1];
+                if (variableName === 'dsName') {
+                    label = label.split(variable).join(datasource.name);
+                } else if (variableName === 'entityName') {
+                    label = label.split(variable).join(datasource.entityName);
+                } else if (variableName === 'deviceName') {
+                    label = label.split(variable).join(datasource.entityName);
+                } else if (variableName === 'aliasName') {
+                    label = label.split(variable).join(datasource.aliasName);
+                }
+                match = varsRegex.exec(pattern);
+            }
+            dataKey.label = label;
+        }
+
+        function updateDatasourceKeyLabels(datasource) {
+            for (var dk = 0; dk < datasource.dataKeys.length; dk++) {
+                updateDataKeyLabel(datasource.dataKeys[dk], datasource);
+            }
+        }
+
+        var deferred = this.$q.defer();
+        var newDatasources = angular.copy(datasources);
+        var datasorceResolveTasks = [];
+        var aliasCtrl = this;
+        newDatasources.forEach(function (datasource) {
+            var resolveDatasourceTask = aliasCtrl.resolveDatasource(datasource);
+            datasorceResolveTasks.push(resolveDatasourceTask);
+        });
+        this.$q.all(datasorceResolveTasks).then(
+            function success(datasourcesArrays) {
+                var datasources = [].concat.apply([], datasourcesArrays);
+                datasources = aliasCtrl.$filter('orderBy')(datasources, '+generated');
+                var index = 0;
+                var functionIndex = 0;
+                datasources.forEach(function(datasource) {
+                    if (datasource.type === aliasCtrl.types.datasourceType.function) {
+                        var name;
+                        if (datasource.name && datasource.name.length) {
+                            name = datasource.name;
+                        } else {
+                            functionIndex++;
+                            name = aliasCtrl.types.datasourceType.function;
+                            if (functionIndex > 1) {
+                                name += ' ' + functionIndex;
+                            }
+                        }
+                        datasource.name = name;
+                        datasource.aliasName = name;
+                        datasource.entityName = name;
+                     }
+                     datasource.dataKeys.forEach(function(dataKey) {
+                         if (datasource.generated) {
+                             dataKey._hash = Math.random();
+                             dataKey.color = aliasCtrl.utils.getMaterialColor(index);
+                         }
+                         index++;
+                     });
+                     updateDatasourceKeyLabels(datasource);
+                });
+                deferred.resolve(datasources);
+            },
+            function fail() {
+                deferred.reject();
+            }
+        );
+        return deferred.promise;
+    }
+
+    getInstantAliasInfo(aliasId) {
+        return this.resolvedAliases[aliasId];
+    }
+
+    updateCurrentAliasEntity(aliasId, currentEntity) {
+        var aliasInfo = this.resolvedAliases[aliasId];
+        if (aliasInfo) {
+            var prevCurrentEntity = aliasInfo.currentEntity;
+            if (!angular.equals(currentEntity, prevCurrentEntity)) {
+                aliasInfo.currentEntity = currentEntity;
+                this.$scope.$broadcast('entityAliasesChanged', [aliasId]);
+            }
+        }
+    }
+
+}
\ No newline at end of file
diff --git a/ui/src/app/api/entity.service.js b/ui/src/app/api/entity.service.js
index 891f9d8..8c28a9b 100644
--- a/ui/src/app/api/entity.service.js
+++ b/ui/src/app/api/entity.service.js
@@ -27,10 +27,13 @@ function EntityService($http, $q, $filter, $translate, $log, userService, device
         getEntity: getEntity,
         getEntities: getEntities,
         getEntitiesByNameFilter: getEntitiesByNameFilter,
-        processEntityAliases: processEntityAliases,
+        resolveAlias: resolveAlias,
+        resolveAliasFilter: resolveAliasFilter,
+        filterAliasByEntityTypes: filterAliasByEntityTypes,
+        //processEntityAliases: processEntityAliases,
         getEntityKeys: getEntityKeys,
         checkEntityAlias: checkEntityAlias,
-        createDatasoucesFromSubscriptionsInfo: createDatasoucesFromSubscriptionsInfo,
+        createDatasourcesFromSubscriptionsInfo: createDatasourcesFromSubscriptionsInfo,
         getRelatedEntities: getRelatedEntities,
         saveRelatedEntity: saveRelatedEntity,
         getRelatedEntity: getRelatedEntity,
@@ -244,81 +247,151 @@ function EntityService($http, $q, $filter, $translate, $log, userService, device
         return deferred.promise;
     }
 
-    function entityToEntityInfo(entityType, entity) {
-        return { name: entity.name, entityType: entityType, id: entity.id.id };
+    function entityToEntityInfo(entity) {
+        return { name: entity.name, entityType: entity.id.entityType, id: entity.id.id };
     }
 
-    function entitiesToEntitiesInfo(entityType, entities) {
+    function entitiesToEntitiesInfo(entities) {
         var entitiesInfo = [];
         for (var d = 0; d < entities.length; d++) {
-            entitiesInfo.push(entityToEntityInfo(entityType, entities[d]));
+            entitiesInfo.push(entityToEntityInfo(entities[d]));
         }
         return entitiesInfo;
     }
 
-    function processEntityAlias(index, aliasIds, entityAliases, resolution, deferred) {
-        if (index < aliasIds.length) {
-            var aliasId = aliasIds[index];
-            var entityAlias = entityAliases[aliasId];
-            var alias = entityAlias.alias;
-            var entityFilter = entityAlias.entityFilter;
-            if (entityFilter.useFilter) {
-                var entityNameFilter = entityFilter.entityNameFilter;
-                getEntitiesByNameFilter(entityAlias.entityType, entityNameFilter, 100).then(
-                    function(entities) {
-                        if (entities && entities != null) {
-                            var resolvedAlias = {alias: alias, entityType: entityAlias.entityType, entityId: entities[0].id.id};
-                            resolution.aliasesInfo.entityAliases[aliasId] = resolvedAlias;
-                            resolution.aliasesInfo.entityAliasesInfo[aliasId] = entitiesToEntitiesInfo(entityAlias.entityType, entities);
-                            index++;
-                            processEntityAlias(index, aliasIds, entityAliases, resolution, deferred);
-                        } else {
-                            if (!resolution.error) {
-                                resolution.error = 'dashboard.invalid-aliases-config';
+    function resolveAliasFilter(filter, stateParams) {
+        var deferred = $q.defer();
+        var result = {
+            entities: [],
+            stateEntity: false
+        };
+        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();
                             }
-                            index++;
-                            processEntityAlias(index, aliasIds, entityAliases, resolution, deferred);
+                        );
+                    } else {
+                        deferred.resolve(result);
+                    }
+                } else {
+                    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 {
-                var entityList = entityFilter.entityList;
-                getEntities(entityAlias.entityType, entityList).then(
+                    );
+                }
+                break;
+            case types.aliasFilterType.entityName.value:
+                getEntitiesByNameFilter(filter.entityType, filter.entityNameFilter, 100).then(
                     function success(entities) {
-                        if (entities && entities.length > 0) {
-                            var resolvedAlias = {alias: alias, entityType: entityAlias.entityType, entityId: entities[0].id.id};
-                            resolution.aliasesInfo.entityAliases[aliasId] = resolvedAlias;
-                            resolution.aliasesInfo.entityAliasesInfo[aliasId] = entitiesToEntitiesInfo(entityAlias.entityType, entities);
-                            index++;
-                            processEntityAlias(index, aliasIds, entityAliases, resolution, deferred);
+                        if (entities && entities.length) {
+                            result.entities = entities;
+                            deferred.resolve(result);
                         } else {
-                            if (!resolution.error) {
-                                resolution.error = 'dashboard.invalid-aliases-config';
-                            }
-                            index++;
-                            processEntityAlias(index, aliasIds, entityAliases, resolution, deferred);
+                            deferred.reject();
                         }
                     },
                     function fail() {
+                        deferred.reject();
+                    }
+                );
+                break;
+            //TODO:
+        }
+        return deferred.promise;
+    }
+
+    function resolveAlias(entityAlias, stateParams) {
+        var deferred = $q.defer();
+        var filter = entityAlias.filter;
+        resolveAliasFilter(filter, stateParams).then(
+            function (result) {
+                var entities = result.entities;
+                var aliasInfo = {
+                    alias: entityAlias.alias,
+                    resolveMultiple: filter.resolveMultiple
+                };
+                var resolvedEntities = entitiesToEntitiesInfo(entities);
+                aliasInfo.resolvedEntities = resolvedEntities;
+                aliasInfo.currentEntity = null;
+                if (aliasInfo.resolvedEntities.length) {
+                    aliasInfo.currentEntity = aliasInfo.resolvedEntities[0];
+                }
+                deferred.resolve(aliasInfo);
+            },
+            function fail() {
+                deferred.reject();
+            }
+        );
+        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 {
+                    return entityTypes.indexOf(filter.entityType) > -1 ? true : false;
+                }
+            case types.aliasFilterType.entityName.value:
+                return entityTypes.indexOf(filter.entityType) > -1 ? true : false;
+        }
+        //TODO:
+        return false;
+    }
+
+    /*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, resolution, deferred);
+                        processEntityAlias(index, aliasIds, entityAliases, stateParams, resolution, deferred);
                     }
-                );
-            }
+                }
+            );
         } else {
             deferred.resolve(resolution);
         }
-    }
+    }*/
 
-    function processEntityAliases(entityAliases) {
+    /*function processEntityAliases(entityAliases, stateParams) {
         var deferred = $q.defer();
         var resolution = {
-            aliasesInfo: {
-                entityAliases: {},
-                entityAliasesInfo: {}
-            }
+            aliasesInfo: {}
         };
         var aliasIds = [];
         if (entityAliases) {
@@ -326,9 +399,9 @@ function EntityService($http, $q, $filter, $translate, $log, userService, device
                 aliasIds.push(aliasId);
             }
         }
-        processEntityAlias(0, aliasIds, entityAliases, resolution, deferred);
+        processEntityAlias(0, aliasIds, entityAliases, stateParams, resolution, deferred);
         return deferred.promise;
-    }
+    }*/
 
     function getEntityKeys(entityType, entityId, query, type) {
         var deferred = $q.defer();
@@ -354,8 +427,8 @@ function EntityService($http, $q, $filter, $translate, $log, userService, device
                 }
             }
             deferred.resolve(result);
-        }, function fail(response) {
-            deferred.reject(response.data);
+        }, function fail() {
+            deferred.reject();
         });
         return deferred.promise;
     }
@@ -387,7 +460,7 @@ function EntityService($http, $q, $filter, $translate, $log, userService, device
         return deferred.promise;
     }
 
-    function createDatasoucesFromSubscriptionsInfo(subscriptionsInfo) {
+    function createDatasourcesFromSubscriptionsInfo(subscriptionsInfo) {
         var deferred = $q.defer();
         var datasources = [];
         processSubscriptionsInfo(0, subscriptionsInfo, datasources, deferred);
diff --git a/ui/src/app/api/subscription.js b/ui/src/app/api/subscription.js
index fb774a1..471203c 100644
--- a/ui/src/app/api/subscription.js
+++ b/ui/src/app/api/subscription.js
@@ -39,6 +39,9 @@ export default class Subscription {
         this.cafs = {};
         this.registrations = [];
 
+        var subscription = this;
+        var deferred = this.ctx.$q.defer();
+
         if (this.type === this.ctx.types.widgetType.rpc.value) {
             this.callbacks.rpcStateChanged = this.callbacks.rpcStateChanged || function(){};
             this.callbacks.onRpcSuccess = this.callbacks.onRpcSuccess || function(){};
@@ -56,7 +59,11 @@ export default class Subscription {
             this.rpcEnabled = false;
             this.executingRpcRequest = false;
             this.executingPromises = [];
-            this.initRpc();
+            this.initRpc().then(
+                function() {
+                    deferred.resolve(subscription);
+                }
+            );
         } else {
             this.callbacks.onDataUpdated = this.callbacks.onDataUpdated || function(){};
             this.callbacks.onDataUpdateError = this.callbacks.onDataUpdateError || function(){};
@@ -103,11 +110,36 @@ export default class Subscription {
                 this.legendConfig.showMax === true ||
                 this.legendConfig.showAvg === true ||
                 this.legendConfig.showTotal === true);
-            this.initDataSubscription();
+            this.initDataSubscription().then(
+                function success() {
+                    deferred.resolve(subscription);
+                },
+                function fail() {
+                    deferred.reject();
+                }
+            );
         }
+
+        return deferred.promise;
     }
 
     initDataSubscription() {
+        var deferred = this.ctx.$q.defer();
+        var subscription = this;
+        this.ctx.aliasController.resolveDatasources(this.datasources).then(
+            function success(datasources) {
+                subscription.datasources = datasources;
+                subscription.configureData();
+                deferred.resolve();
+            },
+            function fail() {
+                deferred.reject();
+            }
+        );
+        return deferred.promise;
+    }
+
+    configureData() {
         var dataIndex = 0;
         for (var i = 0; i < this.datasources.length; i++) {
             var datasource = this.datasources[i];
@@ -199,21 +231,46 @@ export default class Subscription {
     }
 
     initRpc() {
+        var deferred = this.ctx.$q.defer();
         if (this.targetDeviceAliasIds && this.targetDeviceAliasIds.length > 0) {
             this.targetDeviceAliasId = this.targetDeviceAliasIds[0];
-            if (this.ctx.aliasesInfo.entityAliases[this.targetDeviceAliasId]) {
-                this.targetDeviceId = this.ctx.aliasesInfo.entityAliases[this.targetDeviceAliasId].entityId;
+            var subscription = this;
+            this.ctx.aliasController.getAliasInfo(this.targetDeviceAliasId).then(
+                function success(aliasInfo) {
+                    if (aliasInfo.currentEntity && aliasInfo.currentEntity.entityType == subscription.ctx.types.entityType.device) {
+                        subscription.targetDeviceId = aliasInfo.currentEntity.id;
+                        if (subscription.targetDeviceId) {
+                            subscription.rpcEnabled = true;
+                        } else {
+                            subscription.rpcEnabled = subscription.ctx.$scope.widgetEditMode ? true : false;
+                        }
+                        subscription.callbacks.rpcStateChanged(this);
+                        deferred.resolve();
+                    } else {
+                        subscription.rpcEnabled = false;
+                        subscription.callbacks.rpcStateChanged(this);
+                        deferred.resolve();
+                    }
+                },
+                function fail () {
+                    subscription.rpcEnabled = false;
+                    subscription.callbacks.rpcStateChanged(this);
+                    deferred.resolve();
+                }
+            );
+        } else  {
+            if (this.targetDeviceIds && this.targetDeviceIds.length > 0) {
+                this.targetDeviceId = this.targetDeviceIds[0];
             }
-        } else if (this.targetDeviceIds && this.targetDeviceIds.length > 0) {
-            this.targetDeviceId = this.targetDeviceIds[0];
-        }
-
-        if (this.targetDeviceId) {
-            this.rpcEnabled = true;
-        } else {
-            this.rpcEnabled = this.ctx.$scope.widgetEditMode ? true : false;
+            if (this.targetDeviceId) {
+                this.rpcEnabled = true;
+            } else {
+                this.rpcEnabled = this.ctx.$scope.widgetEditMode ? true : false;
+            }
+            this.callbacks.rpcStateChanged(this);
+            deferred.resolve();
         }
-        this.callbacks.rpcStateChanged(this);
+        return deferred.promise;
     }
 
     clearRpcError() {
@@ -319,11 +376,11 @@ export default class Subscription {
         this.onDataUpdated();
     }
 
-    onAliasesChanged() {
+    onAliasesChanged(aliasIds) {
         if (this.type === this.ctx.types.widgetType.rpc.value) {
-            this.checkRpcTarget();
+            return this.checkRpcTarget(aliasIds);
         } else {
-            this.checkSubscriptions();
+            return this.checkSubscriptions(aliasIds);
         }
     }
 
@@ -481,7 +538,7 @@ export default class Subscription {
             var datasource = this.datasources[i];
             if (angular.isFunction(datasource))
                 continue;
-            var entityId = null;
+           /* var entityId = null;
             var entityType = null;
             if (datasource.type === this.ctx.types.datasourceType.entity) {
                 var aliasName = null;
@@ -513,7 +570,7 @@ export default class Subscription {
             }
             for (var dk = 0; dk < datasource.dataKeys.length; dk++) {
                 updateDataKeyLabel(datasource.dataKeys[dk], datasource.name, entityName, aliasName);
-            }
+            }*/
 
             var subscription = this;
 
@@ -521,8 +578,8 @@ export default class Subscription {
                 subscriptionType: this.type,
                 subscriptionTimewindow: this.subscriptionTimewindow,
                 datasource: datasource,
-                entityType: entityType,
-                entityId: entityId,
+                entityType: datasource.entityType,
+                entityId: datasource.entityId,
                 dataUpdated: function (data, datasourceIndex, dataKeyIndex, apply) {
                     subscription.dataUpdated(data, datasourceIndex, dataKeyIndex, apply);
                 },
@@ -557,8 +614,13 @@ export default class Subscription {
         }
     }
 
-    checkRpcTarget() {
-        var deviceId = null;
+    checkRpcTarget(aliasIds) {
+        if (aliasIds.indexOf(this.targetDeviceAliasId) > -1) {
+            return true;
+        } else {
+            return false;
+        }
+        /*var deviceId = null;
         if (this.ctx.aliasesInfo.entityAliases[this.targetDeviceAliasId]) {
             deviceId = this.ctx.aliasesInfo.entityAliases[this.targetDeviceAliasId].entityId;
         }
@@ -570,14 +632,20 @@ export default class Subscription {
                 this.rpcEnabled = this.ctx.$scope.widgetEditMode ? true : false;
             }
             this.callbacks.rpcStateChanged(this);
-        }
+        }*/
     }
 
-    checkSubscriptions() {
+    checkSubscriptions(aliasIds) {
         var subscriptionsChanged = false;
         for (var i = 0; i < this.datasourceListeners.length; i++) {
             var listener = this.datasourceListeners[i];
-            var entityId = null;
+            if (listener.datasource.entityAliasId) {
+                if (aliasIds.indexOf(listener.datasource.entityAliasId) > -1) {
+                    subscriptionsChanged = true;
+                    break;
+                }
+            }
+            /*var entityId = null;
             var entityType = null;
             var aliasName = null;
             if (listener.datasource.type === this.ctx.types.datasourceType.entity) {
@@ -593,12 +661,13 @@ export default class Subscription {
                     subscriptionsChanged = true;
                     break;
                 }
-            }
+            }*/
         }
-        if (subscriptionsChanged) {
+        return subscriptionsChanged;
+        /*if (subscriptionsChanged) {
             this.unsubscribe();
             this.subscribe();
-        }
+        }*/
     }
 
     destroy() {
@@ -617,7 +686,7 @@ export default class Subscription {
 
 }
 
-const varsRegex = /\$\{([^\}]*)\}/g;
+/*const varsRegex = /\$\{([^\}]*)\}/g;
 
 function updateDataKeyLabel(dataKey, dsName, entityName, aliasName) {
     var pattern = dataKey.pattern;
@@ -638,7 +707,7 @@ function updateDataKeyLabel(dataKey, dsName, entityName, aliasName) {
         match = varsRegex.exec(pattern);
     }
     dataKey.label = label;
-}
+}*/
 
 function calculateMin(data) {
     if (data.length > 0) {
diff --git a/ui/src/app/common/dashboard-utils.service.js b/ui/src/app/common/dashboard-utils.service.js
index 78136df..51a0215 100644
--- a/ui/src/app/common/dashboard-utils.service.js
+++ b/ui/src/app/common/dashboard-utils.service.js
@@ -40,39 +40,75 @@ function DashboardUtils(types, utils, timeService) {
     return service;
 
     function validateAndUpdateEntityAliases(configuration) {
+        var aliasId, entityAlias;
         if (angular.isUndefined(configuration.entityAliases)) {
             configuration.entityAliases = {};
             if (configuration.deviceAliases) {
                 var deviceAliases = configuration.deviceAliases;
-                for (var aliasId in deviceAliases) {
+                for (aliasId in deviceAliases) {
                     var deviceAlias = deviceAliases[aliasId];
-                    var alias = deviceAlias.alias;
-                    var entityFilter = {
-                        useFilter: false,
-                        entityNameFilter: '',
-                        entityList: []
-                    }
-                    if (deviceAlias.deviceFilter) {
-                        entityFilter.useFilter = deviceAlias.deviceFilter.useFilter;
-                        entityFilter.entityNameFilter = deviceAlias.deviceFilter.deviceNameFilter;
-                        entityFilter.entityList = deviceAlias.deviceFilter.deviceList;
-                    } else if (deviceAlias.deviceId) {
-                        entityFilter.entityList = [deviceAlias.deviceId];
-                    }
-                    var entityAlias = {
-                        id: aliasId,
-                        alias: alias,
-                        entityType: types.entityType.device,
-                        entityFilter: entityFilter
-                    };
+                    entityAlias = validateAndUpdateDeviceAlias(aliasId, deviceAlias);
                     configuration.entityAliases[aliasId] = entityAlias;
                 }
                 delete configuration.deviceAliases;
             }
+        } else {
+            var entityAliases = configuration.entityAliases;
+            for (aliasId in entityAliases) {
+                entityAlias = entityAliases[aliasId];
+                entityAliases[aliasId] = validateAndUpdateEntityAlias(entityAlias);
+            }
         }
         return configuration;
     }
 
+    function validateAndUpdateDeviceAlias(aliasId, deviceAlias) {
+        var alias = deviceAlias.alias;
+        var entityAlias = {
+            id: aliasId,
+            alias: alias,
+            filter: {
+                type: null,
+                entityType: types.entityType.device,
+                resolveMultiple: false
+            },
+        }
+        if (deviceAlias.deviceFilter) {
+            entityAlias.filter.type =
+                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;
+    }
+
+    function validateAndUpdateEntityAlias(entityAlias) {
+        if (!entityAlias.filter) {
+            entityAlias.filter = {
+                type: entityAlias.entityFilter.useFilter ? types.aliasFilterType.entityName.value : types.aliasFilterType.entityList.value,
+                entityType: entityAlias.entityType,
+                resolveMultiple: false
+            }
+            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;
+            }
+            delete entityAlias.entityType;
+            delete entityAlias.entityFilter;
+        }
+        return entityAlias;
+    }
+
     function validateAndUpdateWidget(widget) {
         if (!widget.config) {
             widget.config = {};
diff --git a/ui/src/app/common/types.constant.js b/ui/src/app/common/types.constant.js
index 44e59a2..40b6639 100644
--- a/ui/src/app/common/types.constant.js
+++ b/ui/src/app/common/types.constant.js
@@ -65,6 +65,36 @@ export default angular.module('thingsboard.types', [])
                 clearedUnack: "CLEARED_UNACK",
                 clearedAck: "CLEARED_ACK"
             },
+            aliasFilterType: {
+                entityList: {
+                    value: 'entityList',
+                    name: 'alias.filter-type-entity-list'
+                },
+                entityName: {
+                    value: 'entityName',
+                    name: 'alias.filter-type-entity-name'
+                },
+                assetType: {
+                    value: 'assetType',
+                    name: 'alias.filter-type-asset-type'
+                },
+                deviceType: {
+                    value: 'deviceType',
+                    name: 'alias.filter-type-device-type'
+                },
+                relationsQuery: {
+                    value: 'relationsQuery',
+                    name: 'alias.filter-type-relations-query'
+                },
+                assetSearchQuery: {
+                    value: 'assetSearchQuery',
+                    name: 'alias.filter-type-asset-search-query'
+                },
+                deviceSearchQuery: {
+                    value: 'deviceSearchQuery',
+                    name: 'alias.filter-type-device-search-query'
+                }
+            },
             position: {
                 top: {
                     value: "top",
diff --git a/ui/src/app/components/dashboard.directive.js b/ui/src/app/components/dashboard.directive.js
index f26121c..965b838 100644
--- a/ui/src/app/components/dashboard.directive.js
+++ b/ui/src/app/components/dashboard.directive.js
@@ -52,7 +52,7 @@ function Dashboard() {
         bindToController: {
             widgets: '=',
             widgetLayouts: '=?',
-            aliasesInfo: '=',
+            aliasController: '=',
             stateController: '=',
             dashboardTimewindow: '=?',
             columns: '=',
@@ -329,10 +329,6 @@ function DashboardController($scope, $rootScope, $element, $timeout, $mdMedia, t
         $scope.$broadcast('toggleDashboardEditMode', vm.isEdit);
     });
 
-    $scope.$watch('vm.aliasesInfo.entityAliases', function () {
-        $scope.$broadcast('entityAliasListChanged', vm.aliasesInfo);
-    }, true);
-
     $scope.$on('gridster-resized', function (event, sizes, theGridster) {
         if (checkIsLocalGridsterElement(theGridster)) {
             vm.gridster = theGridster;
diff --git a/ui/src/app/components/dashboard.tpl.html b/ui/src/app/components/dashboard.tpl.html
index 69934ac..8d77a6f 100644
--- a/ui/src/app/components/dashboard.tpl.html
+++ b/ui/src/app/components/dashboard.tpl.html
@@ -89,7 +89,7 @@
 									<div flex tb-widget
 										 locals="{ visibleRect: vm.visibleRect,
 										 widget: widget,
-										 aliasesInfo: vm.aliasesInfo,
+										 aliasController: vm.aliasController,
 										 stateController: vm.stateController,
 										 isEdit: vm.isEdit,
 										 stDiff: vm.stDiff,
diff --git a/ui/src/app/components/datakey-config-dialog.controller.js b/ui/src/app/components/datakey-config-dialog.controller.js
index a8741f5..ccaac21 100644
--- a/ui/src/app/components/datakey-config-dialog.controller.js
+++ b/ui/src/app/components/datakey-config-dialog.controller.js
@@ -20,14 +20,14 @@ export default angular.module('thingsboard.dialogs.datakeyConfigDialog', [things
     .name;
 
 /*@ngInject*/
-function DatakeyConfigDialogController($scope, $mdDialog, entityService, dataKey, dataKeySettingsSchema, entityAlias, entityAliases) {
+function DatakeyConfigDialogController($scope, $mdDialog, $q, entityService, dataKey, dataKeySettingsSchema, entityAlias, aliasController) {
 
     var vm = this;
 
     vm.dataKey = dataKey;
     vm.dataKeySettingsSchema = dataKeySettingsSchema;
     vm.entityAlias = entityAlias;
-    vm.entityAliases = entityAliases;
+    vm.aliasController = aliasController;
 
     vm.hide = function () {
         $mdDialog.hide();
@@ -38,12 +38,28 @@ function DatakeyConfigDialogController($scope, $mdDialog, entityService, dataKey
     };
 
     vm.fetchEntityKeys = function (entityAliasId, query, type) {
-        var alias = vm.entityAliases[entityAliasId];
-        if (alias) {
-            return entityService.getEntityKeys(alias.entityType, alias.entityId, query, type);
-        } else {
-            return [];
-        }
+        var deferred = $q.defer();
+        vm.aliasController.getAliasInfo(entityAliasId).then(
+            function success(aliasInfo) {
+                var entity = aliasInfo.currentEntity;
+                if (entity) {
+                    entityService.getEntityKeys(entity.entityType, entity.id, query, type).then(
+                        function success(keys) {
+                            deferred.resolve(keys);
+                        },
+                        function fail() {
+                            deferred.resolve([]);
+                        }
+                    );
+                } else {
+                    deferred.resolve([]);
+                }
+            },
+            function fail() {
+                deferred.resolve([]);
+            }
+        );
+        return deferred.promise;
     };
 
     vm.save = function () {
diff --git a/ui/src/app/components/datasource.directive.js b/ui/src/app/components/datasource.directive.js
index 2c06a38..eb9eafc 100644
--- a/ui/src/app/components/datasource.directive.js
+++ b/ui/src/app/components/datasource.directive.js
@@ -76,7 +76,7 @@ function Datasource($compile, $templateCache, types) {
         restrict: "E",
         require: "^ngModel",
         scope: {
-            entityAliases: '=',
+            aliasController: '=',
             widgetType: '=',
             functionsOnly: '=',
             datakeySettingsSchema: '=',
diff --git a/ui/src/app/components/datasource.tpl.html b/ui/src/app/components/datasource.tpl.html
index 88ff247..0e91d98 100644
--- a/ui/src/app/components/datasource.tpl.html
+++ b/ui/src/app/components/datasource.tpl.html
@@ -37,7 +37,7 @@
                               ng-switch-when="entity"
                               ng-required="model.type === types.datasourceType.entity"
                               widget-type="widgetType"
-                              entity-aliases="entityAliases"
+                              alias-controller="aliasController"
                               generate-data-key="generateDataKey({chip: chip, type: type})"
                               fetch-entity-keys="fetchEntityKeys({entityAliasId: entityAliasId, query: query, type: type})"
                               on-create-entity-alias="onCreateEntityAlias({event: event, alias: alias})">
diff --git a/ui/src/app/components/datasource-entity.directive.js b/ui/src/app/components/datasource-entity.directive.js
index 97732b9..da43274 100644
--- a/ui/src/app/components/datasource-entity.directive.js
+++ b/ui/src/app/components/datasource-entity.directive.js
@@ -103,10 +103,9 @@ function DatasourceEntity($compile, $templateCache, $q, $mdDialog, $window, $doc
         ngModelCtrl.$render = function () {
             if (ngModelCtrl.$viewValue) {
                 var entityAliasId = ngModelCtrl.$viewValue.entityAliasId;
-                if (scope.entityAliases[entityAliasId]) {
-                    scope.entityAlias = {id: entityAliasId, alias: scope.entityAliases[entityAliasId].alias,
-                        entityType: scope.entityAliases[entityAliasId].entityType,
-                        entityId: scope.entityAliases[entityAliasId].entityId};
+                var entityAliases = scope.aliasController.getEntityAliases();
+                if (entityAliases[entityAliasId]) {
+                    scope.entityAlias = entityAliases[entityAliasId];
                 } else {
                     scope.entityAlias = null;
                 }
@@ -182,7 +181,7 @@ function DatasourceEntity($compile, $templateCache, $q, $mdDialog, $window, $doc
                     dataKey: angular.copy(dataKey),
                     dataKeySettingsSchema: scope.datakeySettingsSchema,
                     entityAlias: scope.entityAlias,
-                    entityAliases: scope.entityAliases
+                    aliasController: scope.aliasController
                 },
                 parent: angular.element($document[0].body),
                 fullscreen: true,
@@ -236,7 +235,7 @@ function DatasourceEntity($compile, $templateCache, $q, $mdDialog, $window, $doc
         require: "^ngModel",
         scope: {
             widgetType: '=',
-            entityAliases: '=',
+            aliasController: '=',
             datakeySettingsSchema: '=',
             generateDataKey: '&',
             fetchEntityKeys: '&',
diff --git a/ui/src/app/components/datasource-entity.tpl.html b/ui/src/app/components/datasource-entity.tpl.html
index 97ee1b5..2fb4608 100644
--- a/ui/src/app/components/datasource-entity.tpl.html
+++ b/ui/src/app/components/datasource-entity.tpl.html
@@ -18,7 +18,7 @@
 <section flex layout='column' layout-align="center" layout-gt-sm='row' layout-align-gt-sm="start center">
 	   <tb-entity-alias-select
 							  tb-required="true"
-							  entity-aliases="entityAliases"
+							  alias-controller="aliasController"
 							  ng-model="entityAlias"
 							  on-create-entity-alias="onCreateEntityAlias({event: event, alias: alias})">
 	   </tb-entity-alias-select>
diff --git a/ui/src/app/components/entity-alias-select.directive.js b/ui/src/app/components/entity-alias-select.directive.js
index 204b83f..ca2db85 100644
--- a/ui/src/app/components/entity-alias-select.directive.js
+++ b/ui/src/app/components/entity-alias-select.directive.js
@@ -31,7 +31,7 @@ export default angular.module('thingsboard.directives.entityAliasSelect', [])
     .name;
 
 /*@ngInject*/
-function EntityAliasSelect($compile, $templateCache, $mdConstant) {
+function EntityAliasSelect($compile, $templateCache, $mdConstant, entityService) {
 
     var linker = function (scope, element, attrs, ngModelCtrl) {
         var template = $templateCache.get(entityAliasSelectTemplate);
@@ -49,19 +49,18 @@ function EntityAliasSelect($compile, $templateCache, $mdConstant) {
             ngModelCtrl.$setValidity('entityAlias', valid);
         };
 
-        scope.$watch('entityAliases', function () {
+        scope.$watch('aliasController', function () {
             scope.entityAliasList = [];
-            for (var aliasId in scope.entityAliases) {
+            var entityAliases = scope.aliasController.getEntityAliases();
+            for (var aliasId in entityAliases) {
                 if (scope.allowedEntityTypes) {
-                    if (scope.allowedEntityTypes.indexOf(scope.entityAliases[aliasId].entityType) === -1) {
+                    if (!entityService.filterAliasByEntityTypes(entityAliases[aliasId], scope.allowedEntityTypes)) {
                         continue;
                     }
                 }
-                var entityAlias = {id: aliasId, alias: scope.entityAliases[aliasId].alias,
-                    entityType: scope.entityAliases[aliasId].entityType, entityId: scope.entityAliases[aliasId].entityId};
-                scope.entityAliasList.push(entityAlias);
+                scope.entityAliasList.push(entityAliases[aliasId]);
             }
-        }, true);
+        });
 
         scope.$watch('entityAlias', function () {
             scope.updateView();
@@ -141,7 +140,7 @@ function EntityAliasSelect($compile, $templateCache, $mdConstant) {
         link: linker,
         scope: {
             tbRequired: '=?',
-            entityAliases: '=',
+            aliasController: '=',
             allowedEntityTypes: '=?',
             onCreateEntityAlias: '&'
         }
diff --git a/ui/src/app/components/widget.controller.js b/ui/src/app/components/widget.controller.js
index 6fc4bba..103ad13 100644
--- a/ui/src/app/components/widget.controller.js
+++ b/ui/src/app/components/widget.controller.js
@@ -22,7 +22,7 @@ import Subscription from '../api/subscription';
 /*@ngInject*/
 export default function WidgetController($scope, $timeout, $window, $element, $q, $log, $injector, $filter, tbRaf, types, utils, timeService,
                                          datasourceService, entityService, deviceService, visibleRect, isEdit, stDiff, dashboardTimewindow,
-                                         dashboardTimewindowApi, widget, aliasesInfo, stateController, widgetType) {
+                                         dashboardTimewindowApi, widget, aliasController, stateController, widgetType) {
 
     var vm = this;
 
@@ -37,6 +37,7 @@ export default function WidgetController($scope, $timeout, $window, $element, $q
     $scope.executingRpcRequest = false;
 
     var gridsterItemInited = false;
+    var subscriptionInited = false;
 
     var cafs = {};
 
@@ -149,7 +150,7 @@ export default function WidgetController($scope, $timeout, $window, $element, $q
         dashboardTimewindowApi: dashboardTimewindowApi,
         types: types,
         stDiff: stDiff,
-        aliasesInfo: aliasesInfo
+        aliasController: aliasController
     };
 
     var widgetTypeInstance;
@@ -203,8 +204,13 @@ export default function WidgetController($scope, $timeout, $window, $element, $q
 
     vm.gridsterItemInitialized = gridsterItemInitialized;
 
-    initialize();
-
+    initialize().then(
+        function(){
+            if (checkSize()) {
+                onInit();
+            }
+        }
+    );
 
     /*
             options = {
@@ -233,28 +239,42 @@ export default function WidgetController($scope, $timeout, $window, $element, $q
             }
         }
 
-        entityService.createDatasoucesFromSubscriptionsInfo(subscriptionsInfo).then(
+        entityService.createDatasourcesFromSubscriptionsInfo(subscriptionsInfo).then(
             function (datasources) {
                 options.datasources = datasources;
-                var subscription = createSubscription(options, subscribe);
-                if (useDefaultComponents) {
-                    defaultSubscriptionOptions(subscription, options);
-                }
-                deferred.resolve(subscription);
+                createSubscription(options, subscribe).then(
+                    function success(subscription) {
+                        if (useDefaultComponents) {
+                            defaultSubscriptionOptions(subscription, options);
+                        }
+                        deferred.resolve(subscription);
+                    },
+                    function fail() {
+                        deferred.reject();
+                    }
+                );
             }
         );
         return deferred.promise;
     }
 
     function createSubscription(options, subscribe) {
+        var deferred = $q.defer();
         options.dashboardTimewindow = dashboardTimewindow;
-        var subscription =
-            new Subscription(subscriptionContext, options);
-        widgetContext.subscriptions[subscription.id] = subscription;
-        if (subscribe) {
-            subscription.subscribe();
-        }
-        return subscription;
+        new Subscription(subscriptionContext, options).then(
+            function success(subscription) {
+                widgetContext.subscriptions[subscription.id] = subscription;
+                if (subscribe) {
+                    subscription.subscribe();
+                }
+                deferred.resolve(subscription);
+            },
+            function fail() {
+                deferred.reject();
+            }
+        );
+
+        return deferred.promise;
     }
 
     function defaultComponentsOptions(options) {
@@ -310,8 +330,8 @@ export default function WidgetController($scope, $timeout, $window, $element, $q
     }
 
     function createDefaultSubscription() {
-        var subscription;
         var options;
+        var deferred = $q.defer();
         if (widget.type !== types.widgetType.rpc.value && widget.type !== types.widgetType.static.value) {
             options = {
                 type: widget.type,
@@ -319,16 +339,23 @@ export default function WidgetController($scope, $timeout, $window, $element, $q
             };
             defaultComponentsOptions(options);
 
-            subscription = createSubscription(options);
-
-            defaultSubscriptionOptions(subscription, options);
+            createSubscription(options).then(
+                function success(subscription) {
+                    defaultSubscriptionOptions(subscription, options);
 
-            // backward compatibility
+                    // backward compatibility
 
-            widgetContext.datasources = subscription.datasources;
-            widgetContext.data = subscription.data;
-            widgetContext.hiddenData = subscription.hiddenData;
-            widgetContext.timeWindow = subscription.timeWindow;
+                    widgetContext.datasources = subscription.datasources;
+                    widgetContext.data = subscription.data;
+                    widgetContext.hiddenData = subscription.hiddenData;
+                    widgetContext.timeWindow = subscription.timeWindow;
+                    widgetContext.defaultSubscription = subscription;
+                    deferred.resolve();
+                },
+                function fail() {
+                    deferred.reject();
+                }
+            );
 
         } else if (widget.type === types.widgetType.rpc.value) {
             $scope.loadingData = false;
@@ -356,24 +383,27 @@ export default function WidgetController($scope, $timeout, $window, $element, $q
                     $scope.rpcRejection = null;
                 }
             }
-            subscription = createSubscription(options);
+            createSubscription(options).then(
+                function success(subscription) {
+                    widgetContext.defaultSubscription = subscription;
+                    deferred.resolve();
+                },
+                function fail() {
+                    deferred.reject();
+                }
+            );
         } else if (widget.type === types.widgetType.static.value) {
             $scope.loadingData = false;
+            deferred.resolve();
+        } else {
+            deferred.resolve();
         }
-        if (subscription) {
-            widgetContext.defaultSubscription = subscription;
-        }
+        return deferred.promise;
     }
 
 
     function initialize() {
 
-        if (!vm.useCustomDatasources) {
-            createDefaultSubscription();
-        } else {
-            $scope.loadingData = false;
-        }
-
         $scope.$on('toggleDashboardEditMode', function (event, isEdit) {
             onEditModeChanged(isEdit);
         });
@@ -398,11 +428,14 @@ export default function WidgetController($scope, $timeout, $window, $element, $q
             onMobileModeChanged(newIsMobile);
         });
 
-        $scope.$on('entityAliasListChanged', function (event, aliasesInfo) {
-            subscriptionContext.aliasesInfo = aliasesInfo;
+        $scope.$on('entityAliasesChanged', function (event, aliasIds) {
+            var subscriptionChanged = false;
             for (var id in widgetContext.subscriptions) {
                 var subscription = widgetContext.subscriptions[id];
-                subscription.onAliasesChanged();
+                subscriptionChanged = subscriptionChanged || subscription.onAliasesChanged(aliasIds);
+            }
+            if (subscriptionChanged && !vm.useCustomDatasources) {
+                reInit();
             }
         });
 
@@ -410,6 +443,44 @@ export default function WidgetController($scope, $timeout, $window, $element, $q
             removeResizeListener(widgetContext.$containerParent[0], onResize); // eslint-disable-line no-undef
             onDestroy();
         });
+
+        var deferred = $q.defer();
+        if (!vm.useCustomDatasources) {
+            createDefaultSubscription().then(
+                function success() {
+                    subscriptionInited = true;
+                    deferred.resolve();
+                },
+                function fail() {
+                    subscriptionInited = true;
+                    deferred.reject();
+                }
+            );
+        } else {
+            $scope.loadingData = false;
+            subscriptionInited = true;
+            deferred.resolve();
+        }
+        return deferred.promise;
+    }
+
+    function reInit() {
+        onDestroy();
+        if (!vm.useCustomDatasources) {
+            createDefaultSubscription().then(
+                function success() {
+                    subscriptionInited = true;
+                    onInit();
+                },
+                function fail() {
+                    subscriptionInited = true;
+                    onInit();
+                }
+            );
+        } else {
+            subscriptionInited = true;
+            onInit();
+        }
     }
 
     function handleWidgetException(e) {
@@ -418,7 +489,9 @@ export default function WidgetController($scope, $timeout, $window, $element, $q
     }
 
     function onInit() {
-        if (!widgetContext.inited) {
+        if (!widgetContext.inited &&
+            subscriptionInited &&
+            gridsterItemInited) {
             widgetContext.inited = true;
             try {
                 widgetTypeInstance.onInit();
@@ -462,7 +535,7 @@ export default function WidgetController($scope, $timeout, $window, $element, $q
                         handleWidgetException(e);
                     }
                 });
-            } else if (gridsterItemInited) {
+            } else {
                 onInit();
             }
         }
@@ -544,6 +617,7 @@ export default function WidgetController($scope, $timeout, $window, $element, $q
             var subscription = widgetContext.subscriptions[id];
             subscription.destroy();
         }
+        subscriptionInited = false;
         widgetContext.subscriptions = [];
         if (widgetContext.inited) {
             widgetContext.inited = false;
diff --git a/ui/src/app/components/widget-config.directive.js b/ui/src/app/components/widget-config.directive.js
index c3793be..d5b4239 100644
--- a/ui/src/app/components/widget-config.directive.js
+++ b/ui/src/app/components/widget-config.directive.js
@@ -128,13 +128,9 @@ function WidgetConfig($compile, $templateCache, $rootScope, $timeout, types, uti
                     } else if (scope.widgetType === types.widgetType.rpc.value && scope.isDataEnabled) {
                         if (config.targetDeviceAliasIds && config.targetDeviceAliasIds.length > 0) {
                             var aliasId = config.targetDeviceAliasIds[0];
-                            if (scope.entityAliases[aliasId]) {
-                                scope.targetDeviceAlias.value = {
-                                    id: aliasId,
-                                    alias: scope.entityAliases[aliasId].alias,
-                                    entityType: scope.entityAliases[aliasId].entityType,
-                                    entityId: scope.entityAliases[aliasId].entityId
-                                };
+                            var entityAliases = scope.aliasController.getEntityAliases();
+                            if (entityAliases[aliasId]) {
+                                scope.targetDeviceAlias.value = entityAliases[aliasId];
                             } else {
                                 scope.targetDeviceAlias.value = null;
                             }
@@ -395,7 +391,7 @@ function WidgetConfig($compile, $templateCache, $rootScope, $timeout, types, uti
             widgetType: '=',
             widgetSettingsSchema: '=',
             datakeySettingsSchema: '=',
-            entityAliases: '=',
+            aliasController: '=',
             functionsOnly: '=',
             fetchEntityKeys: '&',
             onCreateEntityAlias: '&',
diff --git a/ui/src/app/components/widget-config.tpl.html b/ui/src/app/components/widget-config.tpl.html
index 808e07b..0500e91 100644
--- a/ui/src/app/components/widget-config.tpl.html
+++ b/ui/src/app/components/widget-config.tpl.html
@@ -60,7 +60,7 @@
                                          style="padding: 0 0 0 10px; margin: 5px;">
                                         <tb-datasource flex ng-model="datasource.value"
                                                        widget-type="widgetType"
-                                                       entity-aliases="entityAliases"
+                                                       alias-controller="aliasController"
                                                        functions-only="functionsOnly"
                                                        datakey-settings-schema="datakeySettingsSchema"
                                                        generate-data-key="generateDataKey(chip,type)"
@@ -104,7 +104,7 @@
                     <v-pane-content style="padding: 0 5px;">
                         <tb-entity-alias-select flex
                                                 tb-required="widgetType === types.widgetType.rpc.value && !widgetEditMode"
-                                                entity-aliases="entityAliases"
+                                                alias-controller="aliasController"
                                                 allowed-entity-types="[types.entityType.device]"
                                                 ng-model="targetDeviceAlias.value"
                                                 on-create-entity-alias="onCreateEntityAlias({event: event, alias: alias, allowedEntityTypes: allowedEntityTypes})">
diff --git a/ui/src/app/dashboard/add-widget.controller.js b/ui/src/app/dashboard/add-widget.controller.js
index 2f23cf9..586fb46 100644
--- a/ui/src/app/dashboard/add-widget.controller.js
+++ b/ui/src/app/dashboard/add-widget.controller.js
@@ -20,12 +20,13 @@ import entityAliasesTemplate from '../entity/entity-aliases.tpl.html';
 /* eslint-enable import/no-unresolved, import/default */
 
 /*@ngInject*/
-export default function AddWidgetController($scope, widgetService, entityService, $mdDialog, $q, $document, types, dashboard, aliasesInfo, widget, widgetInfo) {
+export default function AddWidgetController($scope, widgetService, entityService, $mdDialog, $q, $document, types, dashboard,
+                                            aliasController, widget, widgetInfo) {
 
     var vm = this;
 
     vm.dashboard = dashboard;
-    vm.aliasesInfo = aliasesInfo;
+    vm.aliasController = aliasController;
     vm.widget = widget;
     vm.widgetInfo = widgetInfo;
 
@@ -85,7 +86,7 @@ export default function AddWidgetController($scope, widgetService, entityService
     }
 
     function cancel () {
-        $mdDialog.cancel({aliasesInfo: vm.aliasesInfo});
+        $mdDialog.cancel();
     }
 
     function add () {
@@ -94,23 +95,39 @@ export default function AddWidgetController($scope, widgetService, entityService
             vm.widget.config = vm.widgetConfig.config;
             vm.widget.config.mobileOrder = vm.widgetConfig.layout.mobileOrder;
             vm.widget.config.mobileHeight = vm.widgetConfig.layout.mobileHeight;
-            $mdDialog.hide({widget: vm.widget, aliasesInfo: vm.aliasesInfo});
+            $mdDialog.hide({widget: vm.widget});
         }
     }
 
     function fetchEntityKeys (entityAliasId, query, type) {
-        var entityAlias = vm.aliasesInfo.entityAliases[entityAliasId];
-        if (entityAlias && entityAlias.entityId) {
-            return entityService.getEntityKeys(entityAlias.entityType, entityAlias.entityId, query, type);
-        } else {
-            return $q.when([]);
-        }
+        var deferred = $q.defer();
+        vm.aliasController.getAliasInfo(entityAliasId).then(
+            function success(aliasInfo) {
+                var entity = aliasInfo.currentEntity;
+                if (entity) {
+                    entityService.getEntityKeys(entity.entityType, entity.id, query, type).then(
+                        function success(keys) {
+                            deferred.resolve(keys);
+                        },
+                        function fail() {
+                            deferred.resolve([]);
+                        }
+                    );
+                } else {
+                    deferred.resolve([]);
+                }
+            },
+            function fail() {
+                deferred.resolve([]);
+            }
+        );
+        return deferred.promise;
     }
 
     function createEntityAlias (event, alias, allowedEntityTypes) {
 
         var deferred = $q.defer();
-        var singleEntityAlias = {id: null, alias: alias, entityType: types.entityType.device, entityFilter: null};
+        var singleEntityAlias = {id: null, alias: alias, filter: {}};
 
         $mdDialog.show({
             controller: 'EntityAliasesController',
@@ -130,16 +147,9 @@ export default function AddWidgetController($scope, widgetService, entityService
             skipHide: true,
             targetEvent: event
         }).then(function (singleEntityAlias) {
-            vm.dashboard.configuration.entityAliases[singleEntityAlias.id] =
-                { alias: singleEntityAlias.alias, entityType: singleEntityAlias.entityType, entityFilter: singleEntityAlias.entityFilter };
-            entityService.processEntityAliases(vm.dashboard.configuration.entityAliases).then(
-                function(resolution) {
-                    if (!resolution.error) {
-                        vm.aliasesInfo = resolution.aliasesInfo;
-                    }
-                    deferred.resolve(singleEntityAlias);
-                }
-            );
+            vm.dashboard.configuration.entityAliases[singleEntityAlias.id] = singleEntityAlias;
+            vm.aliasController.updateEntityAliases(vm.dashboard.configuration.entityAliases);
+            deferred.resolve(singleEntityAlias);
         }, function () {
             deferred.reject();
         });
diff --git a/ui/src/app/dashboard/add-widget.tpl.html b/ui/src/app/dashboard/add-widget.tpl.html
index 870e203..7617760 100644
--- a/ui/src/app/dashboard/add-widget.tpl.html
+++ b/ui/src/app/dashboard/add-widget.tpl.html
@@ -37,7 +37,7 @@
                                       ng-model="vm.widgetConfig"
                                       widget-settings-schema="vm.settingsSchema"
                                       datakey-settings-schema="vm.dataKeySettingsSchema"
-                                      entity-aliases="vm.aliasesInfo.entityAliases"
+                                      alias-controller="vm.aliasController"
                                       functions-only="vm.functionsOnly"
                                       fetch-entity-keys="vm.fetchEntityKeys(entityAliasId, query, type)"
                                       on-create-entity-alias="vm.createEntityAlias(event, alias, allowedEntityTypes)"
diff --git a/ui/src/app/dashboard/dashboard.controller.js b/ui/src/app/dashboard/dashboard.controller.js
index 31892a6..195184b 100644
--- a/ui/src/app/dashboard/dashboard.controller.js
+++ b/ui/src/app/dashboard/dashboard.controller.js
@@ -24,8 +24,10 @@ import selectTargetLayoutTemplate from './layouts/select-target-layout.tpl.html'
 
 /* eslint-enable import/no-unresolved, import/default */
 
+import AliasController from '../api/alias-controller';
+
 /*@ngInject*/
-export default function DashboardController(types, dashboardUtils, widgetService, userService,
+export default function DashboardController(types, utils, dashboardUtils, widgetService, userService,
                                             dashboardService, timeService, entityService, itembuffer, importExport, hotkeys, $window, $rootScope,
                                             $scope, $element, $state, $stateParams, $mdDialog, $mdMedia, $timeout, $document, $q, $translate, $filter) {
 
@@ -349,7 +351,13 @@ export default function DashboardController(types, dashboardUtils, widgetService
             dashboardService.getDashboard($stateParams.dashboardId)
                 .then(function success(dashboard) {
                     vm.dashboard = dashboardUtils.validateAndUpdateDashboard(dashboard);
-                    entityService.processEntityAliases(vm.dashboard.configuration.entityAliases)
+                    vm.dashboardConfiguration = vm.dashboard.configuration;
+                    vm.dashboardCtx.dashboard = vm.dashboard;
+                    vm.dashboardCtx.dashboardTimewindow = vm.dashboardConfiguration.timewindow;
+                    vm.dashboardCtx.aliasController = new AliasController($scope, $q, $filter, utils,
+                        types, entityService, vm.dashboardCtx.stateController, vm.dashboardConfiguration.entityAliases);
+
+                   /* entityService.processEntityAliases(vm.dashboard.configuration.entityAliases)
                         .then(
                             function(resolution) {
                                 if (resolution.error && !isTenantAdmin()) {
@@ -362,7 +370,7 @@ export default function DashboardController(types, dashboardUtils, widgetService
                                     vm.dashboardCtx.dashboardTimewindow = vm.dashboardConfiguration.timewindow;
                                 }
                             }
-                        );
+                        );*/
                 }, function fail() {
                     vm.configurationError = true;
                 });
@@ -373,6 +381,7 @@ export default function DashboardController(types, dashboardUtils, widgetService
         var layoutsData = dashboardUtils.getStateLayoutsData(vm.dashboard, state);
         if (layoutsData) {
             vm.dashboardCtx.state = state;
+            vm.dashboardCtx.aliasController.dashboardStateChanged();
             var layoutVisibilityChanged = false;
             for (var l in vm.layouts) {
                 var layout = vm.layouts[l];
@@ -916,7 +925,7 @@ export default function DashboardController(types, dashboardUtils, widgetService
                         templateUrl: addWidgetTemplate,
                         locals: {
                             dashboard: vm.dashboard,
-                            aliasesInfo: vm.dashboardCtx.aliasesInfo,
+                            aliasController: vm.dashboardCtx.aliasController,
                             widget: newWidget,
                             widgetInfo: widgetTypeInfo
                         },
@@ -930,10 +939,8 @@ export default function DashboardController(types, dashboardUtils, widgetService
                         }
                     }).then(function (result) {
                         var widget = result.widget;
-                        vm.dashboardCtx.aliasesInfo = result.aliasesInfo;
                         addWidget(widget);
-                    }, function (rejection) {
-                        vm.dashboardCtx.aliasesInfo = rejection.aliasesInfo;
+                    }, function () {
                     });
                 }
             }
@@ -1025,7 +1032,7 @@ export default function DashboardController(types, dashboardUtils, widgetService
         notifyDashboardUpdated();
     }
 
-    function showAliasesResolutionError(error) {
+/*    function showAliasesResolutionError(error) {
         var alert = $mdDialog.alert()
             .parent(angular.element($document[0].body))
             .clickOutsideToClose(true)
@@ -1037,20 +1044,10 @@ export default function DashboardController(types, dashboardUtils, widgetService
         alert._options.fullscreen = true;
 
         $mdDialog.show(alert);
-    }
+    }*/
 
     function entityAliasesUpdated() {
-        var deferred = $q.defer();
-        entityService.processEntityAliases(vm.dashboard.configuration.entityAliases)
-            .then(
-                function(resolution) {
-                    if (resolution.aliasesInfo) {
-                        vm.dashboardCtx.aliasesInfo = resolution.aliasesInfo;
-                    }
-                   deferred.resolve();
-                }
-            );
-        return deferred.promise;
+        vm.dashboardCtx.aliasController.updateEntityAliases(vm.dashboard.configuration.entityAliases);
     }
 
     function notifyDashboardUpdated() {
diff --git a/ui/src/app/dashboard/dashboard.tpl.html b/ui/src/app/dashboard/dashboard.tpl.html
index 8338612..202268f 100644
--- a/ui/src/app/dashboard/dashboard.tpl.html
+++ b/ui/src/app/dashboard/dashboard.tpl.html
@@ -57,8 +57,7 @@
                     </tb-timewindow>
                     <tb-aliases-entity-select ng-show="!vm.isEdit && vm.displayEntitiesSelect()"
                                               tooltip-direction="bottom"
-                                              ng-model="vm.dashboardCtx.aliasesInfo.entityAliases"
-                                              entity-aliases-info="vm.dashboardCtx.aliasesInfo.entityAliasesInfo">
+                                              alias-controller="vm.dashboardCtx.aliasController">
                     </tb-aliases-entity-select>
                     <md-button ng-show="vm.isEdit" aria-label="{{ 'entity.aliases' | translate }}" class="md-icon-button"
                                ng-click="vm.openEntityAliases($event)">
@@ -179,7 +178,7 @@
             <form name="vm.widgetForm" ng-if="vm.isEditingWidget">
                 <tb-edit-widget
                         dashboard="vm.dashboard"
-                        aliases-info="vm.dashboardCtx.aliasesInfo"
+                        alias-controller="vm.dashboardCtx.aliasController"
                         widget="vm.editingWidget"
                         widget-layout="vm.editingWidgetLayout"
                         the-form="vm.widgetForm">
diff --git a/ui/src/app/dashboard/edit-widget.directive.js b/ui/src/app/dashboard/edit-widget.directive.js
index 7cba4ee..4bd4e3d 100644
--- a/ui/src/app/dashboard/edit-widget.directive.js
+++ b/ui/src/app/dashboard/edit-widget.directive.js
@@ -68,18 +68,34 @@ export default function EditWidgetDirective($compile, $templateCache, types, wid
         });
 
         scope.fetchEntityKeys = function (entityAliasId, query, type) {
-            var entityAlias = scope.aliasesInfo.entityAliases[entityAliasId];
-            if (entityAlias && entityAlias.entityId) {
-                return entityService.getEntityKeys(entityAlias.entityType, entityAlias.entityId, query, type);
-            } else {
-                return $q.when([]);
-            }
+            var deferred = $q.defer();
+            scope.aliasController.getAliasInfo(entityAliasId).then(
+                function success(aliasInfo) {
+                    var entity = aliasInfo.currentEntity;
+                    if (entity) {
+                        entityService.getEntityKeys(entity.entityType, entity.id, query, type).then(
+                            function success(keys) {
+                                deferred.resolve(keys);
+                            },
+                            function fail() {
+                                deferred.resolve([]);
+                            }
+                        );
+                    } else {
+                        deferred.resolve([]);
+                    }
+                },
+                function fail() {
+                    deferred.resolve([]);
+                }
+            );
+            return deferred.promise;
         };
 
         scope.createEntityAlias = function (event, alias, allowedEntityTypes) {
 
             var deferred = $q.defer();
-            var singleEntityAlias = {id: null, alias: alias, entityType: types.entityType.device, entityFilter: null};
+            var singleEntityAlias = {id: null, alias: alias, filter: {}};
 
             $mdDialog.show({
                 controller: 'EntityAliasesController',
@@ -99,16 +115,9 @@ export default function EditWidgetDirective($compile, $templateCache, types, wid
                 skipHide: true,
                 targetEvent: event
             }).then(function (singleEntityAlias) {
-                scope.dashboard.configuration.entityAliases[singleEntityAlias.id] =
-                            { alias: singleEntityAlias.alias, entityType: singleEntityAlias.entityType, entityFilter: singleEntityAlias.entityFilter };
-                entityService.processEntityAliases(scope.dashboard.configuration.entityAliases).then(
-                    function(resolution) {
-                        if (!resolution.error) {
-                            scope.aliasesInfo = resolution.aliasesInfo;
-                        }
-                        deferred.resolve(singleEntityAlias);
-                    }
-                );
+                scope.dashboard.configuration.entityAliases[singleEntityAlias.id] = singleEntityAlias;
+                scope.aliasController.updateEntityAliases(scope.dashboard.configuration.entityAliases);
+                deferred.resolve(singleEntityAlias);
             }, function () {
                 deferred.reject();
             });
@@ -124,7 +133,7 @@ export default function EditWidgetDirective($compile, $templateCache, types, wid
         link: linker,
         scope: {
             dashboard: '=',
-            aliasesInfo: '=',
+            aliasController: '=',
             widget: '=',
             widgetLayout: '=',
             theForm: '='
diff --git a/ui/src/app/dashboard/edit-widget.tpl.html b/ui/src/app/dashboard/edit-widget.tpl.html
index 279d311..a9ff0b6 100644
--- a/ui/src/app/dashboard/edit-widget.tpl.html
+++ b/ui/src/app/dashboard/edit-widget.tpl.html
@@ -21,7 +21,7 @@
 					  is-data-enabled="isDataEnabled"
 					  widget-settings-schema="settingsSchema"
 					  datakey-settings-schema="dataKeySettingsSchema"
-					  entity-aliases="aliasesInfo.entityAliases"
+					  alias-controller="aliasController"
 					  functions-only="functionsOnly"
 					  fetch-entity-keys="fetchEntityKeys(entityAliasId, query, type)"
 					  on-create-entity-alias="createEntityAlias(event, alias, allowedEntityTypes)"
diff --git a/ui/src/app/dashboard/layouts/dashboard-layout.tpl.html b/ui/src/app/dashboard/layouts/dashboard-layout.tpl.html
index ea84858..4a4b25e 100644
--- a/ui/src/app/dashboard/layouts/dashboard-layout.tpl.html
+++ b/ui/src/app/dashboard/layouts/dashboard-layout.tpl.html
@@ -45,7 +45,7 @@
             widget-layouts="vm.layoutCtx.widgetLayouts"
             columns="vm.layoutCtx.gridSettings.columns"
             margins="vm.layoutCtx.gridSettings.margins"
-            aliases-info="vm.dashboardCtx.aliasesInfo"
+            alias-controller="vm.dashboardCtx.aliasController"
             state-controller="vm.dashboardCtx.stateController"
             dashboard-timewindow="vm.dashboardCtx.dashboardTimewindow"
             is-edit="vm.isEdit"
diff --git a/ui/src/app/entity/aliases-entity-select.directive.js b/ui/src/app/entity/aliases-entity-select.directive.js
index 7512409..7e174ef 100644
--- a/ui/src/app/entity/aliases-entity-select.directive.js
+++ b/ui/src/app/entity/aliases-entity-select.directive.js
@@ -29,7 +29,7 @@ import aliasesEntitySelectPanelTemplate from './aliases-entity-select-panel.tpl.
 /*@ngInject*/
 export default function AliasesEntitySelectDirective($compile, $templateCache, $mdMedia, types, $mdPanel, $document, $translate) {
 
-    var linker = function (scope, element, attrs, ngModelCtrl) {
+    var linker = function (scope, element, attrs) {
 
         /* tbAliasesEntitySelect (ng-model)
          * {
@@ -81,10 +81,8 @@ export default function AliasesEntitySelectDirective($compile, $templateCache, $
                 position: position,
                 fullscreen: false,
                 locals: {
-                    'entityAliases': angular.copy(scope.model),
-                    'entityAliasesInfo': scope.entityAliasesInfo,
-                    'onEntityAliasesUpdate': function (entityAliases) {
-                        scope.model = entityAliases;
+                    'aliasController': scope.aliasController,
+                    'onEntityAliasesUpdate': function () {
                         scope.updateView();
                     }
                 },
@@ -97,40 +95,31 @@ export default function AliasesEntitySelectDirective($compile, $templateCache, $
         }
 
         scope.updateView = function () {
-            var value = angular.copy(scope.model);
-            ngModelCtrl.$setViewValue(value);
             updateDisplayValue();
         }
 
-        ngModelCtrl.$render = function () {
-            if (ngModelCtrl.$viewValue) {
-                var value = ngModelCtrl.$viewValue;
-                scope.model = angular.copy(value);
-                updateDisplayValue();
-            }
-        }
-
         function updateDisplayValue() {
             var displayValue;
             var singleValue = true;
             var currentAliasId;
-            for (var aliasId in scope.model) {
-                if (!currentAliasId) {
-                    currentAliasId = aliasId;
-                } else {
-                    singleValue = false;
-                    break;
+            var entityAliases = scope.aliasController.getEntityAliases();
+            for (var aliasId in entityAliases) {
+                var entityAlias = entityAliases[aliasId];
+                if (!entityAlias.filter.resolveMultiple) {
+                    var resolvedAlias = scope.aliasController.getInstantAliasInfo(aliasId);
+                    if (resolvedAlias && resolvedAlias.currentEntity) {
+                        if (!currentAliasId) {
+                            currentAliasId = aliasId;
+                        } else {
+                            singleValue = false;
+                            break;
+                        }
+                    }
                 }
             }
             if (singleValue && currentAliasId) {
-                var entityId = scope.model[currentAliasId].entityId;
-                var entitiesInfo = scope.entityAliasesInfo[currentAliasId];
-                for (var i=0;i<entitiesInfo.length;i++) {
-                    if (entitiesInfo[i].id === entityId) {
-                        displayValue = entitiesInfo[i].name;
-                        break;
-                    }
-                }
+                var aliasInfo = scope.aliasController.getInstantAliasInfo(currentAliasId);
+                displayValue = aliasInfo.currentEntity.name;
             } else {
                 displayValue = $translate.instant('entity.entities');
             }
@@ -142,9 +131,8 @@ export default function AliasesEntitySelectDirective($compile, $templateCache, $
 
     return {
         restrict: "E",
-        require: "^ngModel",
         scope: {
-            entityAliasesInfo:'='
+            aliasController:'='
         },
         link: linker
     };
diff --git a/ui/src/app/entity/aliases-entity-select-panel.controller.js b/ui/src/app/entity/aliases-entity-select-panel.controller.js
index 4128f94..8439269 100644
--- a/ui/src/app/entity/aliases-entity-select-panel.controller.js
+++ b/ui/src/app/entity/aliases-entity-select-panel.controller.js
@@ -15,17 +15,30 @@
  */
 
 /*@ngInject*/
-export default function AliasesEntitySelectPanelController(mdPanelRef, $scope, types, entityAliases, entityAliasesInfo, onEntityAliasesUpdate) {
+export default function AliasesEntitySelectPanelController(mdPanelRef, $scope, types, aliasController, onEntityAliasesUpdate) {
 
     var vm = this;
     vm._mdPanelRef = mdPanelRef;
-    vm.entityAliases = entityAliases;
-    vm.entityAliasesInfo = entityAliasesInfo;
+    vm.aliasController = aliasController;
     vm.onEntityAliasesUpdate = onEntityAliasesUpdate;
+    vm.entityAliases = {};
+    vm.entityAliasesInfo = {};
 
-    $scope.$watch('vm.entityAliases', function () {
+    vm.currentAliasEntityChanged = currentAliasEntityChanged;
+
+    var allEntityAliases = vm.aliasController.getEntityAliases();
+    for (var aliasId in allEntityAliases) {
+        var aliasInfo = vm.aliasController.getInstantAliasInfo(aliasId);
+        if (aliasInfo && !aliasInfo.resolveMultiple && aliasInfo.currentEntity) {
+            vm.entityAliasesInfo[aliasId] = angular.copy(aliasInfo);
+        }
+    }
+
+    function currentAliasEntityChanged(aliasId, currentEntity) {
+        vm.aliasController.updateCurrentAliasEntity(aliasId, currentEntity);
         if (onEntityAliasesUpdate) {
-            onEntityAliasesUpdate(vm.entityAliases);
+            onEntityAliasesUpdate();
         }
-    }, true);
+    }
+
 }
diff --git a/ui/src/app/entity/aliases-entity-select-panel.tpl.html b/ui/src/app/entity/aliases-entity-select-panel.tpl.html
index d9c5f2f..edc8e5a 100644
--- a/ui/src/app/entity/aliases-entity-select-panel.tpl.html
+++ b/ui/src/app/entity/aliases-entity-select-panel.tpl.html
@@ -18,12 +18,12 @@
 <md-content flex layout="column">
     <section flex layout="column">
         <md-content flex class="md-padding" layout="column">
-            <div flex layout="row" ng-repeat="(aliasId, entityAlias) in vm.entityAliases">
+            <div flex layout="row" ng-repeat="(aliasId, entityAliasInfo) in vm.entityAliasesInfo">
                 <md-input-container flex>
-                    <label>{{entityAlias.alias}}</label>
-                    <md-select ng-model="vm.entityAliases[aliasId].entityId">
-                        <md-option ng-repeat="entityInfo in vm.entityAliasesInfo[aliasId]" ng-value="entityInfo.id">
-                            {{entityInfo.name}}
+                    <label>{{entityAliasInfo.alias}}</label>
+                    <md-select ng-model="entityAliasInfo.currentEntity" ng-change="vm.currentAliasEntityChanged(aliasId, entityAliasInfo.currentEntity)">
+                        <md-option ng-repeat="resolvedEntity in entityAliasInfo.resolvedEntities" ng-value="resolvedEntity">
+                            {{resolvedEntity.name}}
                         </md-option>
                     </md-select>
                 </md-input-container>
diff --git a/ui/src/app/entity/entity-aliases.controller.js b/ui/src/app/entity/entity-aliases.controller.js
index f1e2e38..088f8b9 100644
--- a/ui/src/app/entity/entity-aliases.controller.js
+++ b/ui/src/app/entity/entity-aliases.controller.js
@@ -85,32 +85,29 @@ export default function EntityAliasesController(utils, entityService, toast, $sc
 
         for (aliasId in config.entityAliases) {
             var entityAlias = config.entityAliases[aliasId];
-            var result = {id: aliasId, alias: entityAlias.alias, entityType: entityAlias.entityType, entityFilter: entityAlias.entityFilter, changed: true};
+            var result = {id: aliasId, alias: entityAlias.alias, filter: entityAlias.filter, changed: true};
             checkEntityAlias(result);
             vm.entityAliases.push(result);
         }
     }
 
     function checkEntityAlias(entityAlias) {
-        if (!entityAlias.entityType) {
-            entityAlias.entityType = types.entityType.device;
-        }
-        if (!entityAlias.entityFilter || entityAlias.entityFilter == null) {
-            entityAlias.entityFilter = {
-                useFilter: false,
-                entityNameFilter: '',
-                entityList: [],
-            };
+        if (!entityAlias.filter || entityAlias.filter == null) {
+            entityAlias.filter = {};
         }
     }
 
-    function onFilterEntityChanged(entity, entityAlias) {
+    function onFilterEntityChanged(entity, stateEntity, entityAlias) {
         if (entityAlias) {
             if (!entityAlias.alias || entityAlias.alias.length == 0) {
                 entityAlias.changed = false;
             }
-            if (!entityAlias.changed && entity && entityAlias.entityType) {
-                entityAlias.alias = entity.name;
+            if (!entityAlias.changed && entityAlias.filter && entityAlias.filter.type) {
+                if (stateEntity) {
+                    entityAlias.alias =  $translate.instant('alias.state-entity');
+                } else {
+                    entityAlias.alias = entity.name;
+                }
             }
         }
     }
@@ -121,8 +118,7 @@ export default function EntityAliasesController(utils, entityService, toast, $sc
             aliasId = Math.max(vm.entityAliases[a].id, aliasId);
         }
         aliasId++;
-        var entityAlias = {id: aliasId, alias: '', entityType: types.entityType.device,
-            entityFilter: {useFilter: false, entityNameFilter: '', entityList: []}, changed: false};
+        var entityAlias = {id: aliasId, alias: '', filter: {}, changed: false};
         vm.entityAliases.push(entityAlias);
     }
 
@@ -160,15 +156,6 @@ export default function EntityAliasesController(utils, entityService, toast, $sc
         $mdDialog.cancel();
     }
 
-    function cleanupEntityFilter(entityFilter) {
-        if (entityFilter.useFilter) {
-            entityFilter.entityList = [];
-        } else {
-            entityFilter.entityNameFilter = '';
-        }
-        return entityFilter;
-    }
-
     function save() {
 
         var entityAliases = {};
@@ -181,7 +168,6 @@ export default function EntityAliasesController(utils, entityService, toast, $sc
 
         if (vm.isSingleEntityAlias) {
             maxAliasId = 0;
-            vm.singleEntityAlias.entityFilter = cleanupEntityFilter(vm.singleEntityAlias.entityFilter);
             for (i = 0; i < vm.entityAliases.length; i ++) {
                 aliasId = vm.entityAliases[i].id;
                 alias = vm.entityAliases[i].alias;
@@ -199,7 +185,7 @@ export default function EntityAliasesController(utils, entityService, toast, $sc
                 alias = vm.entityAliases[i].alias;
                 if (!uniqueAliasList[alias]) {
                     uniqueAliasList[alias] = alias;
-                    entityAliases[aliasId] = {alias: alias, entityType: vm.entityAliases[i].entityType, entityFilter: cleanupEntityFilter(vm.entityAliases[i].entityFilter)};
+                    entityAliases[aliasId] = {id: aliasId, alias: alias, filter: vm.entityAliases[i].filter};
                 } else {
                     valid = false;
                     break;
diff --git a/ui/src/app/entity/entity-aliases.tpl.html b/ui/src/app/entity/entity-aliases.tpl.html
index bc9e269..ef8b032 100644
--- a/ui/src/app/entity/entity-aliases.tpl.html
+++ b/ui/src/app/entity/entity-aliases.tpl.html
@@ -32,20 +32,15 @@
 			<div class="md-dialog-content">
 				<fieldset ng-disabled="loading">
 					<div ng-show="vm.isSingleEntityAlias" layout="row">
-						<tb-entity-type-select style="min-width: 100px;"
-											   ng-model="vm.singleEntityAlias.entityType"
-											   allowed-entity-types="vm.allowedEntityTypes">
-						</tb-entity-type-select>
-						<tb-entity-filter flex entity-type="vm.singleEntityAlias.entityType" ng-model="vm.singleEntityAlias.entityFilter">
+						<tb-entity-filter flex allowed-entity-types="vm.allowedEntityTypes" ng-model="vm.singleEntityAlias.filter">
 						</tb-entity-filter>
 					</div>
 					<div ng-show="!vm.isSingleEntityAlias" flex layout="row" layout-align="start center">
 						<span flex="5"></span>
 						<div flex layout="row" layout-align="start center"
 							 style="padding: 0 0 0 10px; margin: 5px;">
-							<span translate flex="20" style="min-width: 100px;">entity.alias</span>
-							<span translate flex="20" style="min-width: 100px;">entity.type</span>
-							<span translate flex="60" style="min-width: 190px; padding-left: 10px;">entity.entities</span>
+							<span translate flex="20" style="min-width: 150px;">entity.alias</span>
+							<span translate flex="80" style="min-width: 240px; padding-left: 10px;">alias.entity-filter</span>
 							<span style="min-width: 40px;"></span>
 						</div>
 					</div>
@@ -53,23 +48,17 @@
 						<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: 100px;" md-no-float class="md-block">
+								<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>
-								<section flex="20" layout="column" style="min-width: 100px;" >
-									<tb-entity-type-select hide-label style="padding-left: 10px;"
-													  ng-model="entityAlias.entityType"
-													  allowed-entity-types="vm.allowedEntityTypes">
-									</tb-entity-type-select>
-								</section>
-								<section flex="60" layout="column">
+								<section flex="80" layout="column">
 									<tb-entity-filter style="padding-left: 10px;"
-													  entity-type="entityAlias.entityType"
-													  ng-model="entityAlias.entityFilter"
-													  on-matching-entity-change="vm.onFilterEntityChanged(entity, entityAlias)">
+													  allowed-entity-types="vm.allowedEntityTypes"
+													  ng-model="entityAlias.filter"
+													  on-matching-entity-change="vm.onFilterEntityChanged(entity, stateEntity, entityAlias)">
 									</tb-entity-filter>
 								</section>
 								<md-button ng-disabled="loading" class="md-icon-button md-primary" style="min-width: 40px;"
diff --git a/ui/src/app/entity/entity-filter.directive.js b/ui/src/app/entity/entity-filter.directive.js
index b1d92c6..beded8a 100644
--- a/ui/src/app/entity/entity-filter.directive.js
+++ b/ui/src/app/entity/entity-filter.directive.js
@@ -17,13 +17,16 @@
 /* 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, entityService) {
+export default function EntityFilterDirective($compile, $templateCache, $q, $document, $mdDialog, types) {
 
     var linker = function (scope, element, attrs, ngModelCtrl) {
 
@@ -31,8 +34,9 @@ export default function EntityFilterDirective($compile, $templateCache, $q, enti
         element.html(template);
 
         scope.ngModelCtrl = ngModelCtrl;
+        scope.types = types;
 
-        scope.fetchEntities = function(searchText, limit) {
+     /*   scope.fetchEntities = function(searchText, limit) {
             var deferred = $q.defer();
             entityService.getEntitiesByNameFilter(scope.entityType, searchText, limit).then(function success(result) {
                 if (result) {
@@ -44,13 +48,13 @@ export default function EntityFilterDirective($compile, $templateCache, $q, enti
                 deferred.reject();
             });
             return deferred.promise;
-        }
+        }*/
 
         scope.updateValidity = function() {
             if (ngModelCtrl.$viewValue) {
                 var value = ngModelCtrl.$viewValue;
-                var valid;
-                if (value.useFilter) {
+                ngModelCtrl.$setValidity('filter', value.type ? true : false);
+                /*if (value.useFilter) {
                     ngModelCtrl.$setValidity('entityList', true);
                     if (angular.isDefined(value.entityNameFilter) && value.entityNameFilter.length > 0) {
                         ngModelCtrl.$setValidity('entityNameFilter', true);
@@ -64,18 +68,22 @@ export default function EntityFilterDirective($compile, $templateCache, $q, enti
                     ngModelCtrl.$setValidity('entityNameFilterDeviceMatch', true);
                     valid = angular.isDefined(value.entityList) && value.entityList.length > 0;
                     ngModelCtrl.$setValidity('entityList', valid);
-                }
+                }*/
+
             }
         }
 
         ngModelCtrl.$render = function () {
-            destroyWatchers();
-            scope.model = {
-                useFilter: false,
-                entityList: [],
-                entityNameFilter: ''
-            }
+            //destroyWatchers();
             if (ngModelCtrl.$viewValue) {
+                scope.model = angular.copy(ngModelCtrl.$viewValue);
+            } else {
+                scope.model = {
+                    type: null,
+                    resolveMultiple: false
+                }
+            }
+           /* if (ngModelCtrl.$viewValue) {
                 var value = ngModelCtrl.$viewValue;
                 var model = scope.model;
                 model.useFilter = value.useFilter === true ? true: false;
@@ -96,10 +104,52 @@ export default function EntityFilterDirective($compile, $templateCache, $q, enti
                         }
                     }
                 )
+            }*/
+        }
+
+        scope.$watch('model.resolveMultiple', function () {
+            if (ngModelCtrl.$viewValue) {
+                var value = ngModelCtrl.$viewValue;
+                value.resolveMultiple = scope.model.resolveMultiple;
+                ngModelCtrl.$setViewValue(value);
+                scope.updateValidity();
             }
+        });
+
+        scope.editFilter = function($event) {
+            openEntityFilterDialog($event, false);
+        }
+
+        scope.createFilter = function($event) {
+            openEntityFilterDialog($event, true);
         }
 
-        function updateMatchingEntity() {
+        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});
+                }
+            }, function () {
+            });
+        }
+
+  /*      function updateMatchingEntity() {
             if (scope.model.useFilter) {
                 scope.model.matchingEntity = scope.model.matchingFilterEntity;
             } else {
@@ -206,7 +256,7 @@ export default function EntityFilterDirective($compile, $templateCache, $q, enti
                     }
                 }
             });
-        }
+        }*/
 
         $compile(element.contents())(scope);
 
@@ -217,8 +267,7 @@ export default function EntityFilterDirective($compile, $templateCache, $q, enti
         require: "^ngModel",
         link: linker,
         scope: {
-            entityType: '=',
-            isEdit: '=',
+            allowedEntityTypes: '=?',
             onMatchingEntityChange: '&'
         }
     };
diff --git a/ui/src/app/entity/entity-filter.tpl.html b/ui/src/app/entity/entity-filter.tpl.html
index 7e49ba9..9a5337a 100644
--- a/ui/src/app/entity/entity-filter.tpl.html
+++ b/ui/src/app/entity/entity-filter.tpl.html
@@ -17,7 +17,7 @@
 -->
 <section layout='column' class="tb-entity-filter">
     <section layout='row'>
-        <section layout="column" flex ng-show="!model.useFilter">
+        <!--section layout="column" flex ng-show="!model.useFilter">
             <md-chips flex
                       id="entity_list_chips"
                       ng-required="!useFilter"
@@ -51,17 +51,44 @@
                 <label translate>entity.name-starts-with</label>
                 <input ng-model="model.entityNameFilter" aria-label="{{ 'entity.name-starts-with' | translate }}">
             </md-input-container>
+        </section-->
+        <section layout="row" flex layout-align="start center">
+            <div flex ng-if="model.type">{{ types.aliasFilterType[model.type].name | translate }}</div>
+            <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 ng-if="!model.type" layout="row" layout-align="center start">
+                <md-button ng-disabled="loading" class="md-primary md-raised"
+                           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>
+            </div>
         </section>
         <section class="tb-filter-switch" layout="column" layout-align="center center">
-            <label class="tb-small filter-label" translate>entity.use-entity-name-filter</label>
-            <md-switch class="filter-switch" ng-model="model.useFilter" aria-label="use-filter-switcher">
+            <label 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>
     </section>
     <div class="tb-error-messages" ng-messages="ngModelCtrl.$error" role="alert">
-        <div translate ng-message="entityList" class="tb-error-message">entity.entity-list-empty</div>
+        <div translate ng-message="filter" class="tb-error-message">alias.entity-filter-required</div>
+        <!--div translate ng-message="entityList" class="tb-error-message">entity.entity-list-empty</div>
         <div translate ng-message="entityNameFilter" class="tb-error-message">entity.entity-name-filter-required</div>
         <div translate translate-values='{ entity: model.entityNameFilter }' ng-message="entityNameFilterEntityMatch"
-             class="tb-error-message">entity.entity-name-filter-no-entity-matched</div>
+             class="tb-error-message">entity.entity-name-filter-no-entity-matched</div-->
     </div>
 </section>
\ No newline at end of file
diff --git a/ui/src/app/entity/entity-filter-dialog.controller.js b/ui/src/app/entity/entity-filter-dialog.controller.js
new file mode 100644
index 0000000..2b1a30d
--- /dev/null
+++ b/ui/src/app/entity/entity-filter-dialog.controller.js
@@ -0,0 +1,104 @@
+/*
+ * 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.
+ */
+
+/*@ngInject*/
+export default function EntityFilterDialogController($scope, $mdDialog, $q, entityService, types, isAdd, allowedEntityTypes, filter) {
+
+    var vm = this;
+
+    vm.types = types;
+    vm.isAdd = isAdd;
+    vm.allowedEntityTypes = allowedEntityTypes;
+    vm.filter = filter;
+
+    vm.cancel = cancel;
+    vm.save = save;
+
+    $scope.$watch('vm.filter.type', function (newType, prevType) {
+        if (newType && newType != prevType) {
+            updateFilter();
+        }
+    });
+
+    $scope.$watch('theForm.$pristine', function() {
+        if ($scope.theForm && !$scope.theForm.$pristine) {
+            $scope.theForm.$setValidity('entityFilter', true);
+        }
+    });
+
+    function updateFilter() {
+        var filter = {};
+        filter.type = vm.filter.type;
+        filter.resolveMultiple = vm.filter.resolveMultiple;
+        switch (filter.type) {
+            case types.aliasFilterType.entityList.value:
+                filter.entityType = null;
+                filter.entityList = [];
+                filter.stateEntity = false;
+                break;
+            case types.aliasFilterType.entityName.value:
+                filter.entityType = null;
+                filter.entityNameFilter = '';
+                break;
+            //TODO:
+        }
+        vm.filter = filter;
+    }
+
+    function validate() {
+        var deferred = $q.defer();
+        var validationResult = {
+            entity: null,
+            stateEntity: false
+        }
+        entityService.resolveAliasFilter(vm.filter).then(
+            function success(result) {
+                validationResult.stateEntity = result.stateEntity;
+                var entities = result.entities;
+                if (entities.length) {
+                    validationResult.entity = entities[0];
+                }
+                deferred.resolve(validationResult);
+            },
+            function fail() {
+                deferred.reject();
+            }
+        );
+        return deferred.promise;
+    }
+
+    function cancel() {
+        $mdDialog.cancel();
+    }
+
+    function save() {
+        $scope.theForm.$setPristine();
+        validate().then(
+            function success(validationResult) {
+                $mdDialog.hide({
+                    filter: vm.filter,
+                    entity: validationResult.entity,
+                    stateEntity: validationResult.stateEntity
+                });
+            },
+            function fail() {
+                $scope.theForm.$setValidity('entityFilter', false);
+            }
+        )
+    }
+
+}
+
diff --git a/ui/src/app/entity/entity-filter-dialog.tpl.html b/ui/src/app/entity/entity-filter-dialog.tpl.html
new file mode 100644
index 0000000..4343b39
--- /dev/null
+++ b/ui/src/app/entity/entity-filter-dialog.tpl.html
@@ -0,0 +1,100 @@
+<!--
+
+    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.
+
+-->
+<md-dialog class="tb-entity-filter-dialog" style="width: 600px;" aria-label="{{ 'alias.entity-filter' | translate }}">
+    <form name="theForm" ng-submit="vm.save()">
+        <md-toolbar>
+            <div class="md-toolbar-tools">
+                <h2>{{ (vm.isAdd ? 'alias.create-entity-filter' : 'alias.edit-entity-filter') | 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>
+                </md-button>
+            </div>
+        </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>
+        <md-dialog-content>
+            <div class="md-dialog-content">
+                <fieldset ng-disabled="loading">
+                    <div flex layout="column">
+                        <md-input-container>
+                            <label>{{ 'alias.filter-type' | translate }}</label>
+                            <md-select required name="filterType"
+                                       ng-model="vm.filter.type" aria-label="{{ 'alias.filter-type' | translate }}">
+                                <md-option ng-repeat="type in vm.types.aliasFilterType" 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="vm.filter.type == vm.types.aliasFilterType.entityList.value" id="entityListFilter">
+                            <md-checkbox flex aria-label="{{ 'alias.use-state-entity' | translate }}"
+                                         ng-model="vm.filter.stateEntity">{{ 'alias.use-state-entity' | translate }}
+                            </md-checkbox>
+                            <tb-entity-type-select
+                                    ng-if="!vm.filter.stateEntity"
+                                    ng-model="vm.filter.entityType"
+                                    the-form="theForm"
+                                    ng-disabled="vm.filter.stateEntity"
+                                    tb-required="!vm.filter.stateEntity"
+                                    allowed-entity-types="vm.allowedEntityTypes">
+                            </tb-entity-type-select>
+                            <tb-entity-list
+                                    ng-if="!vm.filter.stateEntity"
+                                    ng-model="vm.filter.entityList"
+                                    ng-disabled="vm.filter.stateEntity"
+                                    tb-required="!vm.filter.stateEntity"
+                                    entity-type="vm.filter.entityType">
+                            </tb-entity-list>
+                        </section>
+                        <section flex layout="column" ng-if="vm.filter.type == vm.types.aliasFilterType.entityName.value" id="entityNameFilter">
+                            <tb-entity-type-select
+                                    ng-model="vm.filter.entityType"
+                                    the-form="theForm"
+                                    tb-required="true"
+                                    allowed-entity-types="vm.allowedEntityTypes">
+                            </tb-entity-type-select>
+                            <md-input-container flex>
+                                <label translate>entity.name-starts-with</label>
+                                <input required name="entityNameFilter"
+                                       ng-model="vm.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>
+                        <div class="tb-error-messages" ng-messages="theForm.$error" role="alert">
+                            <div translate ng-message="entityFilter" class="tb-error-message">alias.entity-filter-no-entity-matched</div>
+                        </div>
+                    </div>
+                </fieldset>
+            </div>
+        </md-dialog-content>
+        <md-dialog-actions layout="row">
+            <span flex></span>
+            <md-button ng-disabled="loading || theForm.$invalid || !theForm.$dirty" type="submit" class="md-raised md-primary">
+                {{ 'action.save' | translate }}
+            </md-button>
+            <md-button ng-disabled="loading" ng-click="vm.cancel()" style="margin-right:20px;">{{ 'action.cancel' | translate }}</md-button>
+        </md-dialog-actions>
+    </form>
+</md-dialog>
\ No newline at end of file
diff --git a/ui/src/app/entity/entity-list.directive.js b/ui/src/app/entity/entity-list.directive.js
new file mode 100644
index 0000000..0863153
--- /dev/null
+++ b/ui/src/app/entity/entity-list.directive.js
@@ -0,0 +1,130 @@
+/*
+ * 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 entityListTemplate from './entity-list.tpl.html';
+
+/* eslint-enable import/no-unresolved, import/default */
+
+import './entity-list.scss';
+
+/*@ngInject*/
+export default function EntityListDirective($compile, $templateCache, $q, $mdUtil, entityService) {
+
+    var linker = function (scope, element, attrs, ngModelCtrl) {
+
+        var template = $templateCache.get(entityListTemplate);
+        element.html(template);
+
+        scope.ngModelCtrl = ngModelCtrl;
+
+        scope.$watch('tbRequired', function () {
+            scope.updateValidity();
+        });
+
+        scope.fetchEntities = function(searchText, limit) {
+             var deferred = $q.defer();
+             entityService.getEntitiesByNameFilter(scope.entityType, searchText, limit).then(
+                 function success(result) {
+                    if (result) {
+                        deferred.resolve(result);
+                    } else {
+                        deferred.resolve([]);
+                    }
+                 },
+                 function fail() {
+                    deferred.reject();
+                 }
+             );
+             return deferred.promise;
+         }
+
+        scope.updateValidity = function() {
+            var value = ngModelCtrl.$viewValue;
+            var valid = !scope.tbRequired || value && value.length > 0;
+            ngModelCtrl.$setValidity('entityList', valid);
+        }
+
+        ngModelCtrl.$render = function () {
+            destroyWatchers();
+            var value = ngModelCtrl.$viewValue;
+            scope.entityList = [];
+            if (value && value.length > 0) {
+                entityService.getEntities(scope.entityType, value).then(function (entities) {
+                    scope.entityList = entities;
+                    initWatchers();
+                });
+            } else {
+                initWatchers();
+            }
+        }
+
+        function initWatchers() {
+            scope.entityTypeDeregistration = scope.$watch('entityType', function (newEntityType, prevEntityType) {
+                if (!angular.equals(newEntityType, prevEntityType)) {
+                    scope.entityList = [];
+                }
+            });
+            scope.entityListDeregistration = scope.$watch('entityList', function () {
+                var ids = [];
+                if (scope.entityList && scope.entityList.length > 0) {
+                    for (var i=0;i<scope.entityList.length;i++) {
+                        ids.push(scope.entityList[i].id.id);
+                    }
+                }
+                var value = ngModelCtrl.$viewValue;
+                if (!angular.equals(ids, value)) {
+                    ngModelCtrl.$setViewValue(ids);
+                }
+                scope.updateValidity();
+            }, true);
+        }
+
+        function destroyWatchers() {
+            if (scope.entityTypeDeregistration) {
+                scope.entityTypeDeregistration();
+                scope.entityTypeDeregistration = null;
+            }
+            if (scope.entityListDeregistration) {
+                scope.entityListDeregistration();
+                scope.entityListDeregistration = null;
+            }
+        }
+
+        $compile(element.contents())(scope);
+
+        $mdUtil.nextTick(function(){
+            var inputElement = angular.element('input', element);
+            inputElement.on('blur', function() {
+                scope.inputTouched = true;
+            } );
+        });
+
+    }
+
+    return {
+        restrict: "E",
+        require: "^ngModel",
+        link: linker,
+        scope: {
+            disabled:'=ngDisabled',
+            tbRequired: '=?',
+            entityType: '='
+        }
+    };
+
+}
diff --git a/ui/src/app/entity/entity-list.scss b/ui/src/app/entity/entity-list.scss
new file mode 100644
index 0000000..437e292
--- /dev/null
+++ b/ui/src/app/entity/entity-list.scss
@@ -0,0 +1,30 @@
+/**
+ * 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-list {
+  #entity_list_chips {
+    .md-chips {
+      padding-bottom: 1px;
+    }
+  }
+  .tb-error-messages {
+    margin-top: -11px;
+    height: 35px;
+    .tb-error-message {
+      padding-left: 1px;
+    }
+  }
+}*/
\ No newline at end of file
diff --git a/ui/src/app/entity/entity-list.tpl.html b/ui/src/app/entity/entity-list.tpl.html
new file mode 100644
index 0000000..6bbb920
--- /dev/null
+++ b/ui/src/app/entity/entity-list.tpl.html
@@ -0,0 +1,52 @@
+<!--
+
+    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.
+
+-->
+
+<section flex layout='column' class="tb-entity-list">
+    <md-chips flex
+              readonly="disabled"
+              id="entity_list_chips"
+              ng-required="tbRequired"
+              ng-model="entityList"
+              md-autocomplete-snap
+              md-require-match="true">
+        <md-autocomplete
+                md-no-cache="true"
+                id="entity"
+                md-selected-item="selectedEntity"
+                md-search-text="entitySearchText"
+                md-items="item in fetchEntities(entitySearchText, 10)"
+                md-item-text="item.name"
+                md-min-length="0"
+                placeholder="{{ 'entity.entity-list' | translate }}">
+            <md-item-template>
+                <span md-highlight-text="entitySearchText" md-highlight-flags="^i">{{item.name}}</span>
+            </md-item-template>
+            <md-not-found>
+                <span translate translate-values='{ entity: entitySearchText }'>entity.no-entities-matching</span>
+            </md-not-found>
+        </md-autocomplete>
+        <md-chip-template>
+            <span>
+                <strong>{{$chip.name}}</strong>
+            </span>
+        </md-chip-template>
+    </md-chips>
+    <div class="tb-error-messages" ng-messages="ngModelCtrl.$error" ng-if="inputTouched" role="alert">
+        <div translate ng-message="entityList" class="tb-error-message">entity.entity-list-empty</div>
+    </div>
+</section>
\ No newline at end of file
diff --git a/ui/src/app/entity/index.js b/ui/src/app/entity/index.js
index e8cc437..6be118f 100644
--- a/ui/src/app/entity/index.js
+++ b/ui/src/app/entity/index.js
@@ -19,6 +19,7 @@ import EntityTypeSelectDirective from './entity-type-select.directive';
 import EntitySubtypeSelectDirective from './entity-subtype-select.directive';
 import EntitySubtypeAutocompleteDirective from './entity-subtype-autocomplete.directive';
 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 AliasesEntitySelectPanelController from './aliases-entity-select-panel.controller';
@@ -38,6 +39,7 @@ export default angular.module('thingsboard.entity', [])
     .directive('tbEntitySubtypeSelect', EntitySubtypeSelectDirective)
     .directive('tbEntitySubtypeAutocomplete', EntitySubtypeAutocompleteDirective)
     .directive('tbEntityAutocomplete', EntityAutocompleteDirective)
+    .directive('tbEntityList', EntityListDirective)
     .directive('tbEntitySelect', EntitySelectDirective)
     .directive('tbEntityFilter', EntityFilterDirective)
     .directive('tbAliasesEntitySelect', AliasesEntitySelectDirective)
diff --git a/ui/src/app/locale/locale.constant.js b/ui/src/app/locale/locale.constant.js
index fcde13c..a23845e 100644
--- a/ui/src/app/locale/locale.constant.js
+++ b/ui/src/app/locale/locale.constant.js
@@ -112,6 +112,25 @@ export default angular.module('thingsboard.locale', [])
                     "no-alarms-matching": "No alarms matching '{{entity}}' were found.",
                     "alarm-required": "Alarm is required"
                 },
+                "alias": {
+                    "filter-type-entity-list": "Entity list",
+                    "filter-type-entity-name": "Entity name",
+                    "filter-type-asset-type": "Asset type",
+                    "filter-type-device-type": "Device type",
+                    "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": "Multiple",
+                    "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.",
+                },
                 "asset": {
                     "asset": "Asset",
                     "assets": "Assets",
diff --git a/ui/src/app/services/item-buffer.service.js b/ui/src/app/services/item-buffer.service.js
index afb63bf..3aa4809 100644
--- a/ui/src/app/services/item-buffer.service.js
+++ b/ui/src/app/services/item-buffer.service.js
@@ -264,14 +264,9 @@ function ItemBuffer($q, bufferStore, types, utils, dashboardUtils) {
         }
         dashboardUtils.addWidgetToLayout(theDashboard, targetState, targetLayout, widget, originalColumns, originalSize, row, column);
         if (callAliasUpdateFunction) {
-            onAliasesUpdateFunction().then(
-                function() {
-                    deferred.resolve(theDashboard);
-                }
-            );
-        } else {
-            deferred.resolve(theDashboard);
+            onAliasesUpdateFunction();
         }
+        deferred.resolve(theDashboard);
         return deferred.promise;
     }