thingsboard-memoizeit

Changes

Details

diff --git a/application/src/main/java/org/thingsboard/server/controller/AlarmController.java b/application/src/main/java/org/thingsboard/server/controller/AlarmController.java
index 7a2c273..19c75e7 100644
--- a/application/src/main/java/org/thingsboard/server/controller/AlarmController.java
+++ b/application/src/main/java/org/thingsboard/server/controller/AlarmController.java
@@ -22,10 +22,7 @@ import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.web.bind.annotation.*;
 import org.thingsboard.server.common.data.Customer;
 import org.thingsboard.server.common.data.Event;
-import org.thingsboard.server.common.data.alarm.Alarm;
-import org.thingsboard.server.common.data.alarm.AlarmId;
-import org.thingsboard.server.common.data.alarm.AlarmQuery;
-import org.thingsboard.server.common.data.alarm.AlarmStatus;
+import org.thingsboard.server.common.data.alarm.*;
 import org.thingsboard.server.common.data.asset.Asset;
 import org.thingsboard.server.common.data.id.*;
 import org.thingsboard.server.common.data.page.TextPageData;
@@ -61,6 +58,19 @@ public class AlarmController extends BaseController {
     }
 
     @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
+    @RequestMapping(value = "/alarm/info/{alarmId}", method = RequestMethod.GET)
+    @ResponseBody
+    public AlarmInfo getAlarmInfoById(@PathVariable("alarmId") String strAlarmId) throws ThingsboardException {
+        checkParameter("alarmId", strAlarmId);
+        try {
+            AlarmId alarmId = new AlarmId(toUUID(strAlarmId));
+            return checkAlarmInfoId(alarmId);
+        } catch (Exception e) {
+            throw handleException(e);
+        }
+    }
+
+    @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
     @RequestMapping(value = "/alarm", method = RequestMethod.POST)
     @ResponseBody
     public Alarm saveAlarm(@RequestBody Alarm alarm) throws ThingsboardException {
@@ -103,24 +113,31 @@ public class AlarmController extends BaseController {
     @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
     @RequestMapping(value = "/alarm/{entityType}/{entityId}", method = RequestMethod.GET)
     @ResponseBody
-    public TimePageData<Alarm> getAlarms(
+    public TimePageData<AlarmInfo> getAlarms(
             @PathVariable("entityType") String strEntityType,
             @PathVariable("entityId") String strEntityId,
+            @RequestParam(required = false) String searchStatus,
             @RequestParam(required = false) String status,
             @RequestParam int limit,
             @RequestParam(required = false) Long startTime,
             @RequestParam(required = false) Long endTime,
             @RequestParam(required = false, defaultValue = "false") boolean ascOrder,
-            @RequestParam(required = false) String offset
+            @RequestParam(required = false) String offset,
+            @RequestParam(required = false) Boolean fetchOriginator
     ) throws ThingsboardException {
         checkParameter("EntityId", strEntityId);
         checkParameter("EntityType", strEntityType);
         EntityId entityId = EntityIdFactory.getByTypeAndId(strEntityType, strEntityId);
+        AlarmSearchStatus alarmSearchStatus = StringUtils.isEmpty(searchStatus) ? null : AlarmSearchStatus.valueOf(searchStatus);
         AlarmStatus alarmStatus = StringUtils.isEmpty(status) ? null : AlarmStatus.valueOf(status);
+        if (alarmSearchStatus != null && alarmStatus != null) {
+            throw new ThingsboardException("Invalid alarms search query: Both parameters 'searchStatus' " +
+                    "and 'status' can't be specified at the same time!", ThingsboardErrorCode.BAD_REQUEST_PARAMS);
+        }
         checkEntityId(entityId);
         try {
             TimePageLink pageLink = createPageLink(limit, startTime, endTime, ascOrder, offset);
-            return checkNotNull(alarmService.findAlarms(new AlarmQuery(entityId, pageLink, alarmStatus)).get());
+            return checkNotNull(alarmService.findAlarms(new AlarmQuery(entityId, pageLink, alarmSearchStatus, alarmStatus, fetchOriginator)).get());
         } catch (Exception e) {
             throw handleException(e);
         }
diff --git a/application/src/main/java/org/thingsboard/server/controller/BaseController.java b/application/src/main/java/org/thingsboard/server/controller/BaseController.java
index 1feef4a..1040f3a 100644
--- a/application/src/main/java/org/thingsboard/server/controller/BaseController.java
+++ b/application/src/main/java/org/thingsboard/server/controller/BaseController.java
@@ -25,6 +25,7 @@ import org.thingsboard.server.actors.service.ActorService;
 import org.thingsboard.server.common.data.*;
 import org.thingsboard.server.common.data.alarm.Alarm;
 import org.thingsboard.server.common.data.alarm.AlarmId;
+import org.thingsboard.server.common.data.alarm.AlarmInfo;
 import org.thingsboard.server.common.data.asset.Asset;
 import org.thingsboard.server.common.data.id.*;
 import org.thingsboard.server.common.data.page.TextPageLink;
@@ -349,6 +350,17 @@ public abstract class BaseController {
         }
     }
 
+    AlarmInfo checkAlarmInfoId(AlarmId alarmId) throws ThingsboardException {
+        try {
+            validateId(alarmId, "Incorrect alarmId " + alarmId);
+            AlarmInfo alarmInfo = alarmService.findAlarmInfoByIdAsync(alarmId).get();
+            checkAlarm(alarmInfo);
+            return alarmInfo;
+        } catch (Exception e) {
+            throw handleException(e, false);
+        }
+    }
+
     protected void checkAlarm(Alarm alarm) throws ThingsboardException {
         checkNotNull(alarm);
         checkTenantId(alarm.getTenantId());
diff --git a/application/src/main/java/org/thingsboard/server/controller/EntityRelationController.java b/application/src/main/java/org/thingsboard/server/controller/EntityRelationController.java
index 3ddc597..4aa1a0a 100644
--- a/application/src/main/java/org/thingsboard/server/controller/EntityRelationController.java
+++ b/application/src/main/java/org/thingsboard/server/controller/EntityRelationController.java
@@ -250,6 +250,21 @@ public class EntityRelationController extends BaseController {
         }
     }
 
+    @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
+    @RequestMapping(value = "/relations/info", method = RequestMethod.POST)
+    @ResponseBody
+    public List<EntityRelationInfo> findInfoByQuery(@RequestBody EntityRelationsQuery query) throws ThingsboardException {
+        checkNotNull(query);
+        checkNotNull(query.getParameters());
+        checkNotNull(query.getFilters());
+        checkEntityId(query.getParameters().getEntityId());
+        try {
+            return checkNotNull(relationService.findInfoByQuery(query).get());
+        } catch (Exception e) {
+            throw handleException(e);
+        }
+    }
+
     private RelationTypeGroup parseRelationTypeGroup(String strRelationTypeGroup, RelationTypeGroup defaultValue) {
         RelationTypeGroup result = defaultValue;
         if (strRelationTypeGroup != null && strRelationTypeGroup.trim().length()>0) {
diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/alarm/Alarm.java b/common/data/src/main/java/org/thingsboard/server/common/data/alarm/Alarm.java
index 09695e5..e48cf5b 100644
--- a/common/data/src/main/java/org/thingsboard/server/common/data/alarm/Alarm.java
+++ b/common/data/src/main/java/org/thingsboard/server/common/data/alarm/Alarm.java
@@ -53,6 +53,22 @@ public class Alarm extends BaseData<AlarmId> implements HasName {
         super(id);
     }
 
+    public Alarm(Alarm alarm) {
+        super(alarm.getId());
+        this.createdTime = alarm.getCreatedTime();
+        this.tenantId = alarm.getTenantId();
+        this.type = alarm.getType();
+        this.originator = alarm.getOriginator();
+        this.severity = alarm.getSeverity();
+        this.status = alarm.getStatus();
+        this.startTs = alarm.getStartTs();
+        this.endTs = alarm.getEndTs();
+        this.ackTs = alarm.getAckTs();
+        this.clearTs = alarm.getClearTs();
+        this.details = alarm.getDetails();
+        this.propagate = alarm.isPropagate();
+    }
+
     @Override
     @JsonProperty(access = JsonProperty.Access.READ_ONLY)
     public String getName() {
diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/alarm/AlarmInfo.java b/common/data/src/main/java/org/thingsboard/server/common/data/alarm/AlarmInfo.java
new file mode 100644
index 0000000..ef24f1c
--- /dev/null
+++ b/common/data/src/main/java/org/thingsboard/server/common/data/alarm/AlarmInfo.java
@@ -0,0 +1,58 @@
+/**
+ * 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.
+ */
+package org.thingsboard.server.common.data.alarm;
+
+public class AlarmInfo extends Alarm {
+
+    private static final long serialVersionUID = 2807343093519543363L;
+
+    private String originatorName;
+
+    public AlarmInfo() {
+        super();
+    }
+
+    public AlarmInfo(Alarm alarm) {
+        super(alarm);
+    }
+
+    public String getOriginatorName() {
+        return originatorName;
+    }
+
+    public void setOriginatorName(String originatorName) {
+        this.originatorName = originatorName;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        if (!super.equals(o)) return false;
+
+        AlarmInfo alarmInfo = (AlarmInfo) o;
+
+        return originatorName != null ? originatorName.equals(alarmInfo.originatorName) : alarmInfo.originatorName == null;
+
+    }
+
+    @Override
+    public int hashCode() {
+        int result = super.hashCode();
+        result = 31 * result + (originatorName != null ? originatorName.hashCode() : 0);
+        return result;
+    }
+}
diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/alarm/AlarmQuery.java b/common/data/src/main/java/org/thingsboard/server/common/data/alarm/AlarmQuery.java
index 00ca6c3..55019c8 100644
--- a/common/data/src/main/java/org/thingsboard/server/common/data/alarm/AlarmQuery.java
+++ b/common/data/src/main/java/org/thingsboard/server/common/data/alarm/AlarmQuery.java
@@ -32,6 +32,8 @@ public class AlarmQuery {
 
     private EntityId affectedEntityId;
     private TimePageLink pageLink;
+    private AlarmSearchStatus searchStatus;
     private AlarmStatus status;
+    private Boolean fetchOriginator;
 
 }
diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/alarm/AlarmStatus.java b/common/data/src/main/java/org/thingsboard/server/common/data/alarm/AlarmStatus.java
index 0f1b346..a8704b2 100644
--- a/common/data/src/main/java/org/thingsboard/server/common/data/alarm/AlarmStatus.java
+++ b/common/data/src/main/java/org/thingsboard/server/common/data/alarm/AlarmStatus.java
@@ -30,4 +30,13 @@ public enum AlarmStatus {
         return this == CLEARED_ACK || this == CLEARED_UNACK;
     }
 
+    public AlarmSearchStatus getClearSearchStatus() {
+        return this.isCleared() ? AlarmSearchStatus.CLEARED : AlarmSearchStatus.ACTIVE;
+    }
+
+    public AlarmSearchStatus getAckSearchStatus() {
+        return this.isAck() ? AlarmSearchStatus.ACK : AlarmSearchStatus.UNACK;
+    }
+
+
 }
diff --git a/dao/src/main/java/org/thingsboard/server/dao/alarm/AlarmDao.java b/dao/src/main/java/org/thingsboard/server/dao/alarm/AlarmDao.java
index 712bbd1..0ab7620 100644
--- a/dao/src/main/java/org/thingsboard/server/dao/alarm/AlarmDao.java
+++ b/dao/src/main/java/org/thingsboard/server/dao/alarm/AlarmDao.java
@@ -17,6 +17,7 @@ package org.thingsboard.server.dao.alarm;
 
 import com.google.common.util.concurrent.ListenableFuture;
 import org.thingsboard.server.common.data.alarm.Alarm;
+import org.thingsboard.server.common.data.alarm.AlarmInfo;
 import org.thingsboard.server.common.data.alarm.AlarmQuery;
 import org.thingsboard.server.common.data.id.EntityId;
 import org.thingsboard.server.common.data.id.TenantId;
@@ -36,5 +37,5 @@ public interface AlarmDao extends Dao<Alarm> {
 
     Alarm save(Alarm alarm);
 
-    ListenableFuture<List<Alarm>> findAlarms(AlarmQuery query);
+    ListenableFuture<List<AlarmInfo>> findAlarms(AlarmQuery query);
 }
diff --git a/dao/src/main/java/org/thingsboard/server/dao/alarm/AlarmService.java b/dao/src/main/java/org/thingsboard/server/dao/alarm/AlarmService.java
index 5399d9d..3556d51 100644
--- a/dao/src/main/java/org/thingsboard/server/dao/alarm/AlarmService.java
+++ b/dao/src/main/java/org/thingsboard/server/dao/alarm/AlarmService.java
@@ -18,6 +18,7 @@ package org.thingsboard.server.dao.alarm;
 import com.google.common.util.concurrent.ListenableFuture;
 import org.thingsboard.server.common.data.alarm.Alarm;
 import org.thingsboard.server.common.data.alarm.AlarmId;
+import org.thingsboard.server.common.data.alarm.AlarmInfo;
 import org.thingsboard.server.common.data.alarm.AlarmQuery;
 import org.thingsboard.server.common.data.page.TimePageData;
 
@@ -34,6 +35,8 @@ public interface AlarmService {
 
     ListenableFuture<Alarm> findAlarmByIdAsync(AlarmId alarmId);
 
-    ListenableFuture<TimePageData<Alarm>> findAlarms(AlarmQuery query);
+    ListenableFuture<AlarmInfo> findAlarmInfoByIdAsync(AlarmId alarmId);
+
+    ListenableFuture<TimePageData<AlarmInfo>> findAlarms(AlarmQuery query);
 
 }
diff --git a/dao/src/main/java/org/thingsboard/server/dao/alarm/BaseAlarmService.java b/dao/src/main/java/org/thingsboard/server/dao/alarm/BaseAlarmService.java
index 262a11b..94f8fdb 100644
--- a/dao/src/main/java/org/thingsboard/server/dao/alarm/BaseAlarmService.java
+++ b/dao/src/main/java/org/thingsboard/server/dao/alarm/BaseAlarmService.java
@@ -17,6 +17,7 @@ package org.thingsboard.server.dao.alarm;
 
 
 import com.google.common.base.Function;
+import com.google.common.util.concurrent.AsyncFunction;
 import com.google.common.util.concurrent.Futures;
 import com.google.common.util.concurrent.ListenableFuture;
 import lombok.extern.slf4j.Slf4j;
@@ -24,15 +25,13 @@ import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 import org.springframework.util.StringUtils;
 import org.thingsboard.server.common.data.Tenant;
-import org.thingsboard.server.common.data.alarm.Alarm;
-import org.thingsboard.server.common.data.alarm.AlarmId;
-import org.thingsboard.server.common.data.alarm.AlarmQuery;
-import org.thingsboard.server.common.data.alarm.AlarmStatus;
+import org.thingsboard.server.common.data.alarm.*;
 import org.thingsboard.server.common.data.id.EntityId;
 import org.thingsboard.server.common.data.page.TimePageData;
 import org.thingsboard.server.common.data.relation.EntityRelation;
 import org.thingsboard.server.common.data.relation.RelationTypeGroup;
 import org.thingsboard.server.dao.entity.AbstractEntityService;
+import org.thingsboard.server.dao.entity.EntityService;
 import org.thingsboard.server.dao.exception.DataValidationException;
 import org.thingsboard.server.dao.relation.EntityRelationsQuery;
 import org.thingsboard.server.dao.relation.EntitySearchDirection;
@@ -44,6 +43,7 @@ import org.thingsboard.server.dao.tenant.TenantDao;
 import javax.annotation.Nullable;
 import javax.annotation.PostConstruct;
 import javax.annotation.PreDestroy;
+import java.util.ArrayList;
 import java.util.List;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.ExecutorService;
@@ -57,7 +57,6 @@ import static org.thingsboard.server.dao.service.Validator.validateId;
 public class BaseAlarmService extends AbstractEntityService implements AlarmService {
 
     public static final String ALARM_RELATION_PREFIX = "ALARM_";
-    public static final String ALARM_RELATION = "ALARM_ANY";
 
     @Autowired
     private AlarmDao alarmDao;
@@ -68,6 +67,9 @@ public class BaseAlarmService extends AbstractEntityService implements AlarmServ
     @Autowired
     private RelationService relationService;
 
+    @Autowired
+    private EntityService entityService;
+
     protected ExecutorService readResultsProcessingExecutor;
 
     @PostConstruct
@@ -114,11 +116,9 @@ public class BaseAlarmService extends AbstractEntityService implements AlarmServ
         query.setParameters(new RelationsSearchParameters(saved.getOriginator(), EntitySearchDirection.TO, Integer.MAX_VALUE));
         List<EntityId> parentEntities = relationService.findByQuery(query).get().stream().map(r -> r.getFrom()).collect(Collectors.toList());
         for (EntityId parentId : parentEntities) {
-            createRelation(new EntityRelation(parentId, saved.getId(), ALARM_RELATION, RelationTypeGroup.ALARM));
-            createRelation(new EntityRelation(parentId, saved.getId(), ALARM_RELATION_PREFIX + saved.getStatus().name(), RelationTypeGroup.ALARM));
+            createAlarmRelation(parentId, saved.getId(), saved.getStatus(), true);
         }
-        createRelation(new EntityRelation(alarm.getOriginator(), saved.getId(), ALARM_RELATION, RelationTypeGroup.ALARM));
-        createRelation(new EntityRelation(alarm.getOriginator(), saved.getId(), ALARM_RELATION_PREFIX + saved.getStatus().name(), RelationTypeGroup.ALARM));
+        createAlarmRelation(alarm.getOriginator(), saved.getId(), saved.getStatus(), true);
         return saved;
     }
 
@@ -197,12 +197,44 @@ public class BaseAlarmService extends AbstractEntityService implements AlarmServ
     }
 
     @Override
-    public ListenableFuture<TimePageData<Alarm>> findAlarms(AlarmQuery query) {
-        ListenableFuture<List<Alarm>> alarms = alarmDao.findAlarms(query);
-        return Futures.transform(alarms, new Function<List<Alarm>, TimePageData<Alarm>>() {
+    public ListenableFuture<AlarmInfo> findAlarmInfoByIdAsync(AlarmId alarmId) {
+        log.trace("Executing findAlarmInfoByIdAsync [{}]", alarmId);
+        validateId(alarmId, "Incorrect alarmId " + alarmId);
+        return Futures.transform(alarmDao.findAlarmByIdAsync(alarmId.getId()),
+                (AsyncFunction<Alarm, AlarmInfo>) alarm1 -> {
+                AlarmInfo alarmInfo = new AlarmInfo(alarm1);
+                return Futures.transform(
+                    entityService.fetchEntityNameAsync(alarmInfo.getOriginator()), (Function<String, AlarmInfo>)
+                        originatorName -> {
+                            alarmInfo.setOriginatorName(originatorName);
+                            return alarmInfo;
+                        }
+                );
+        });
+    }
+
+    @Override
+    public ListenableFuture<TimePageData<AlarmInfo>> findAlarms(AlarmQuery query) {
+        ListenableFuture<List<AlarmInfo>> alarms = alarmDao.findAlarms(query);
+        if (query.getFetchOriginator() != null && query.getFetchOriginator().booleanValue()) {
+            alarms = Futures.transform(alarms, (AsyncFunction<List<AlarmInfo>, List<AlarmInfo>>) input -> {
+                List<ListenableFuture<AlarmInfo>> alarmFutures = new ArrayList<>(input.size());
+                for (AlarmInfo alarmInfo : input) {
+                    alarmFutures.add(Futures.transform(
+                            entityService.fetchEntityNameAsync(alarmInfo.getOriginator()), (Function<String, AlarmInfo>)
+                                    originatorName -> {
+                                        alarmInfo.setOriginatorName(originatorName);
+                                        return alarmInfo;
+                                    }
+                    ));
+                }
+                return Futures.successfulAsList(alarmFutures);
+             });
+        }
+        return Futures.transform(alarms, new Function<List<AlarmInfo>, TimePageData<AlarmInfo>>() {
             @Nullable
             @Override
-            public TimePageData<Alarm> apply(@Nullable List<Alarm> alarms) {
+            public TimePageData<AlarmInfo> apply(@Nullable List<AlarmInfo> alarms) {
                 return new TimePageData<>(alarms, query.getPageLink());
             }
         });
@@ -243,17 +275,45 @@ public class BaseAlarmService extends AbstractEntityService implements AlarmServ
             query.setParameters(new RelationsSearchParameters(alarm.getOriginator(), EntitySearchDirection.TO, Integer.MAX_VALUE));
             List<EntityId> parentEntities = relationService.findByQuery(query).get().stream().map(r -> r.getFrom()).collect(Collectors.toList());
             for (EntityId parentId : parentEntities) {
-                deleteRelation(new EntityRelation(parentId, alarm.getId(), ALARM_RELATION_PREFIX + oldStatus.name(), RelationTypeGroup.ALARM));
-                createRelation(new EntityRelation(parentId, alarm.getId(), ALARM_RELATION_PREFIX + newStatus.name(), RelationTypeGroup.ALARM));
-            }
-            deleteRelation(new EntityRelation(alarm.getOriginator(), alarm.getId(), ALARM_RELATION_PREFIX + oldStatus.name(), RelationTypeGroup.ALARM));
-            createRelation(new EntityRelation(alarm.getOriginator(), alarm.getId(), ALARM_RELATION_PREFIX + newStatus.name(), RelationTypeGroup.ALARM));
+                updateAlarmRelation(parentId, alarm.getId(), oldStatus, newStatus);
+           }
+            updateAlarmRelation(alarm.getOriginator(), alarm.getId(), oldStatus, newStatus);
         } catch (ExecutionException | InterruptedException e) {
             log.warn("[{}] Failed to update relations. Old status: [{}], New status: [{}]", alarm.getId(), oldStatus, newStatus);
             throw new RuntimeException(e);
         }
     }
 
+    private void createAlarmRelation(EntityId entityId, EntityId alarmId, AlarmStatus status, boolean createAnyRelation) {
+        try {
+            if (createAnyRelation) {
+                createRelation(new EntityRelation(entityId, alarmId, ALARM_RELATION_PREFIX + AlarmSearchStatus.ANY.name(), RelationTypeGroup.ALARM));
+            }
+            createRelation(new EntityRelation(entityId, alarmId, ALARM_RELATION_PREFIX + status.name(), RelationTypeGroup.ALARM));
+            createRelation(new EntityRelation(entityId, alarmId, ALARM_RELATION_PREFIX + status.getClearSearchStatus().name(), RelationTypeGroup.ALARM));
+            createRelation(new EntityRelation(entityId, alarmId, ALARM_RELATION_PREFIX + status.getAckSearchStatus().name(), RelationTypeGroup.ALARM));
+        } catch (ExecutionException | InterruptedException e) {
+            log.warn("[{}] Failed to create relation. Status: [{}]", alarmId, status);
+            throw new RuntimeException(e);
+        }
+    }
+
+    private void deleteAlarmRelation(EntityId entityId, EntityId alarmId, AlarmStatus status) {
+        try {
+            deleteRelation(new EntityRelation(entityId, alarmId, ALARM_RELATION_PREFIX + status.name(), RelationTypeGroup.ALARM));
+            deleteRelation(new EntityRelation(entityId, alarmId, ALARM_RELATION_PREFIX + status.getClearSearchStatus().name(), RelationTypeGroup.ALARM));
+            deleteRelation(new EntityRelation(entityId, alarmId, ALARM_RELATION_PREFIX + status.getAckSearchStatus().name(), RelationTypeGroup.ALARM));
+        } catch (ExecutionException | InterruptedException e) {
+            log.warn("[{}] Failed to delete relation. Status: [{}]", alarmId, status);
+            throw new RuntimeException(e);
+        }
+    }
+
+    private void updateAlarmRelation(EntityId entityId, EntityId alarmId, AlarmStatus oldStatus, AlarmStatus newStatus) {
+        deleteAlarmRelation(entityId, alarmId, oldStatus);
+        createAlarmRelation(entityId, alarmId, newStatus, false);
+    }
+
     private <T> ListenableFuture<T> getAndUpdate(AlarmId alarmId, Function<Alarm, T> function) {
         validateId(alarmId, "Alarm id should be specified!");
         ListenableFuture<Alarm> entity = alarmDao.findAlarmByIdAsync(alarmId.getId());
diff --git a/dao/src/main/java/org/thingsboard/server/dao/alarm/CassandraAlarmDao.java b/dao/src/main/java/org/thingsboard/server/dao/alarm/CassandraAlarmDao.java
index 343d4cc..f5b6672 100644
--- a/dao/src/main/java/org/thingsboard/server/dao/alarm/CassandraAlarmDao.java
+++ b/dao/src/main/java/org/thingsboard/server/dao/alarm/CassandraAlarmDao.java
@@ -17,6 +17,7 @@ package org.thingsboard.server.dao.alarm;
 
 import com.datastax.driver.core.querybuilder.QueryBuilder;
 import com.datastax.driver.core.querybuilder.Select;
+import com.google.common.base.Function;
 import com.google.common.util.concurrent.AsyncFunction;
 import com.google.common.util.concurrent.Futures;
 import com.google.common.util.concurrent.ListenableFuture;
@@ -26,7 +27,9 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
 import org.springframework.stereotype.Component;
 import org.thingsboard.server.common.data.EntityType;
 import org.thingsboard.server.common.data.alarm.Alarm;
+import org.thingsboard.server.common.data.alarm.AlarmInfo;
 import org.thingsboard.server.common.data.alarm.AlarmQuery;
+import org.thingsboard.server.common.data.alarm.AlarmSearchStatus;
 import org.thingsboard.server.common.data.id.EntityId;
 import org.thingsboard.server.common.data.id.TenantId;
 import org.thingsboard.server.common.data.relation.EntityRelation;
@@ -86,15 +89,25 @@ public class CassandraAlarmDao extends CassandraAbstractModelDao<AlarmEntity, Al
     }
 
     @Override
-    public ListenableFuture<List<Alarm>> findAlarms(AlarmQuery query) {
-        log.trace("Try to find alarms by entity [{}], status [{}] and pageLink [{}]", query.getAffectedEntityId(), query.getStatus(), query.getPageLink());
+    public ListenableFuture<List<AlarmInfo>> findAlarms(AlarmQuery query) {
+        log.trace("Try to find alarms by entity [{}], searchStatus [{}], status [{}] and pageLink [{}]", query.getAffectedEntityId(), query.getSearchStatus(), query.getStatus(), query.getPageLink());
         EntityId affectedEntity = query.getAffectedEntityId();
-        String relationType = query.getStatus() == null ? BaseAlarmService.ALARM_RELATION : BaseAlarmService.ALARM_RELATION_PREFIX + query.getStatus().name();
+        String searchStatusName;
+        if (query.getSearchStatus() == null && query.getStatus() == null) {
+            searchStatusName = AlarmSearchStatus.ANY.name();
+        } else if (query.getSearchStatus() != null) {
+            searchStatusName = query.getSearchStatus().name();
+        } else {
+            searchStatusName = query.getStatus().name();
+        }
+        String relationType = BaseAlarmService.ALARM_RELATION_PREFIX + searchStatusName;
         ListenableFuture<List<EntityRelation>> relations = relationDao.findRelations(affectedEntity, relationType, RelationTypeGroup.ALARM, EntityType.ALARM, query.getPageLink());
-        return Futures.transform(relations, (AsyncFunction<List<EntityRelation>, List<Alarm>>) input -> {
-            List<ListenableFuture<Alarm>> alarmFutures = new ArrayList<>(input.size());
+        return Futures.transform(relations, (AsyncFunction<List<EntityRelation>, List<AlarmInfo>>) input -> {
+            List<ListenableFuture<AlarmInfo>> alarmFutures = new ArrayList<>(input.size());
             for (EntityRelation relation : input) {
-                alarmFutures.add(findAlarmByIdAsync(relation.getTo().getId()));
+                alarmFutures.add(Futures.transform(
+                        findAlarmByIdAsync(relation.getTo().getId()), (Function<Alarm, AlarmInfo>)
+                                alarm1 -> new AlarmInfo(alarm1)));
             }
             return Futures.successfulAsList(alarmFutures);
         });
diff --git a/dao/src/main/java/org/thingsboard/server/dao/relation/BaseRelationDao.java b/dao/src/main/java/org/thingsboard/server/dao/relation/BaseRelationDao.java
index 0fdd6ca..2f6fe73 100644
--- a/dao/src/main/java/org/thingsboard/server/dao/relation/BaseRelationDao.java
+++ b/dao/src/main/java/org/thingsboard/server/dao/relation/BaseRelationDao.java
@@ -179,9 +179,14 @@ public class BaseRelationDao extends CassandraAbstractAsyncDao implements Relati
                         eq(ModelConstants.RELATION_TYPE_GROUP_PROPERTY, typeGroup.name()),
                         eq(ModelConstants.RELATION_TYPE_PROPERTY, relationType),
                         eq(ModelConstants.RELATION_TO_TYPE_PROPERTY, childType.name())),
-                Arrays.asList(QueryBuilder.asc(ModelConstants.RELATION_TYPE_GROUP_PROPERTY),
-                        QueryBuilder.asc(ModelConstants.RELATION_TYPE_PROPERTY),
-                        QueryBuilder.asc(ModelConstants.RELATION_TO_TYPE_PROPERTY)),
+                Arrays.asList(
+                        pageLink.isAscOrder() ? QueryBuilder.desc(ModelConstants.RELATION_TYPE_GROUP_PROPERTY) :
+                                QueryBuilder.asc(ModelConstants.RELATION_TYPE_GROUP_PROPERTY),
+                        pageLink.isAscOrder() ? QueryBuilder.desc(ModelConstants.RELATION_TYPE_PROPERTY) :
+                                QueryBuilder.asc(ModelConstants.RELATION_TYPE_PROPERTY),
+                        pageLink.isAscOrder() ? QueryBuilder.desc(ModelConstants.RELATION_TO_TYPE_PROPERTY) :
+                                QueryBuilder.asc(ModelConstants.RELATION_TO_TYPE_PROPERTY)
+                ),
                 pageLink, ModelConstants.RELATION_TO_ID_PROPERTY);
         return getFuture(executeAsyncRead(query), rs -> getEntityRelations(rs));
     }
diff --git a/dao/src/main/java/org/thingsboard/server/dao/relation/BaseRelationService.java b/dao/src/main/java/org/thingsboard/server/dao/relation/BaseRelationService.java
index 36ec567..296874e 100644
--- a/dao/src/main/java/org/thingsboard/server/dao/relation/BaseRelationService.java
+++ b/dao/src/main/java/org/thingsboard/server/dao/relation/BaseRelationService.java
@@ -191,7 +191,7 @@ public class BaseRelationService implements RelationService {
 
     @Override
     public ListenableFuture<List<EntityRelation>> findByQuery(EntityRelationsQuery query) {
-        log.trace("Executing findByQuery [{}][{}]", query);
+        log.trace("Executing findByQuery [{}]", query);
         RelationsSearchParameters params = query.getParameters();
         final List<EntityTypeFilter> filters = query.getFilters();
         if (filters == null || filters.isEmpty()) {
@@ -224,6 +224,30 @@ public class BaseRelationService implements RelationService {
         }
     }
 
+    @Override
+    public ListenableFuture<List<EntityRelationInfo>> findInfoByQuery(EntityRelationsQuery query) {
+        log.trace("Executing findInfoByQuery [{}]", query);
+        ListenableFuture<List<EntityRelation>> relations = findByQuery(query);
+        EntitySearchDirection direction = query.getParameters().getDirection();
+        ListenableFuture<List<EntityRelationInfo>> relationsInfo = Futures.transform(relations,
+                (AsyncFunction<List<EntityRelation>, List<EntityRelationInfo>>) relations1 -> {
+                    List<ListenableFuture<EntityRelationInfo>> futures = new ArrayList<>();
+                    relations1.stream().forEach(relation ->
+                            futures.add(fetchRelationInfoAsync(relation,
+                                    relation2 -> direction == EntitySearchDirection.FROM ? relation2.getTo() : relation2.getFrom(),
+                                    (EntityRelationInfo relationInfo, String entityName) -> {
+                                        if (direction == EntitySearchDirection.FROM) {
+                                            relationInfo.setToName(entityName);
+                                        } else {
+                                            relationInfo.setFromName(entityName);
+                                        }
+                                    }))
+                    );
+                    return Futures.successfulAsList(futures);
+                });
+        return relationsInfo;
+    }
+
     protected void validate(EntityRelation relation) {
         if (relation == null) {
             throw new DataValidationException("Relation type should be specified!");
diff --git a/dao/src/main/java/org/thingsboard/server/dao/relation/RelationService.java b/dao/src/main/java/org/thingsboard/server/dao/relation/RelationService.java
index a810454..bd2e785 100644
--- a/dao/src/main/java/org/thingsboard/server/dao/relation/RelationService.java
+++ b/dao/src/main/java/org/thingsboard/server/dao/relation/RelationService.java
@@ -52,6 +52,8 @@ public interface RelationService {
 
     ListenableFuture<List<EntityRelation>> findByQuery(EntityRelationsQuery query);
 
+    ListenableFuture<List<EntityRelationInfo>> findInfoByQuery(EntityRelationsQuery query);
+
 //    TODO: This method may be useful for some validations in the future
 //    ListenableFuture<Boolean> checkRecursiveRelation(EntityId from, EntityId to);
 
diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/AlarmServiceTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/AlarmServiceTest.java
index 3b72574..b22df63 100644
--- a/dao/src/test/java/org/thingsboard/server/dao/service/AlarmServiceTest.java
+++ b/dao/src/test/java/org/thingsboard/server/dao/service/AlarmServiceTest.java
@@ -22,10 +22,7 @@ import org.junit.Before;
 import org.junit.Test;
 import org.thingsboard.server.common.data.EntityType;
 import org.thingsboard.server.common.data.Tenant;
-import org.thingsboard.server.common.data.alarm.Alarm;
-import org.thingsboard.server.common.data.alarm.AlarmQuery;
-import org.thingsboard.server.common.data.alarm.AlarmSeverity;
-import org.thingsboard.server.common.data.alarm.AlarmStatus;
+import org.thingsboard.server.common.data.alarm.*;
 import org.thingsboard.server.common.data.id.AssetId;
 import org.thingsboard.server.common.data.id.DeviceId;
 import org.thingsboard.server.common.data.id.TenantId;
@@ -117,7 +114,7 @@ public class AlarmServiceTest extends AbstractServiceTest {
         Alarm created = alarmService.createOrUpdateAlarm(alarm);
 
         // Check child relation
-        TimePageData<Alarm> alarms = alarmService.findAlarms(AlarmQuery.builder()
+        TimePageData<AlarmInfo> alarms = alarmService.findAlarms(AlarmQuery.builder()
                 .affectedEntityId(childId)
                 .status(AlarmStatus.ACTIVE_UNACK).pageLink(
                         new TimePageLink(1, 0L, System.currentTimeMillis(), false)
diff --git a/ui/src/app/alarm/alarm.scss b/ui/src/app/alarm/alarm.scss
new file mode 100644
index 0000000..aea5225
--- /dev/null
+++ b/ui/src/app/alarm/alarm.scss
@@ -0,0 +1,78 @@
+/**
+ * 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-alarm-container {
+  overflow-x: auto;
+}
+
+md-list.tb-alarm-table {
+  padding: 0px;
+  min-width: 700px;
+
+  md-list-item {
+    padding: 0px;
+  }
+
+  .tb-row {
+    height: 48px;
+    padding: 0px;
+    overflow: hidden;
+  }
+
+  .tb-row:hover {
+    background-color: #EEEEEE;
+  }
+
+  .tb-header:hover {
+    background: none;
+  }
+
+  .tb-header {
+    .tb-cell {
+      color: rgba(0,0,0,.54);
+      font-size: 12px;
+      font-weight: 700;
+      white-space: nowrap;
+      background: none;
+    }
+  }
+
+  .tb-cell {
+    padding: 0 24px;
+    margin: auto 0;
+    color: rgba(0,0,0,.87);
+    font-size: 13px;
+    vertical-align: middle;
+    text-align: left;
+    overflow: hidden;
+    .md-button {
+      padding: 0;
+      margin: 0;
+    }
+  }
+
+  .tb-cell.tb-number {
+    text-align: right;
+  }
+
+}
+
+#tb-alarm-content {
+  min-width: 400px;
+  min-height: 50px;
+  width: 100%;
+  height: 100%;
+}
diff --git a/ui/src/app/alarm/alarm-details-dialog.controller.js b/ui/src/app/alarm/alarm-details-dialog.controller.js
new file mode 100644
index 0000000..0cc05ef
--- /dev/null
+++ b/ui/src/app/alarm/alarm-details-dialog.controller.js
@@ -0,0 +1,134 @@
+/*
+ * 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.
+ */
+import 'brace/ext/language_tools';
+import 'brace/mode/json';
+import 'brace/theme/github';
+import beautify from 'js-beautify';
+
+import './alarm-details-dialog.scss';
+
+const js_beautify = beautify.js;
+
+/*@ngInject*/
+export default function AlarmDetailsDialogController($mdDialog, $filter, $translate, types, alarmService, alarmId, showingCallback) {
+
+    var vm = this;
+
+    vm.alarmId = alarmId;
+    vm.types = types;
+    vm.alarm = null;
+
+    vm.alarmUpdated = false;
+
+    showingCallback.onShowing = function(scope, element) {
+        updateEditorSize(element);
+    }
+
+    vm.alarmDetailsOptions = {
+        useWrapMode: false,
+        mode: 'json',
+        showGutter: false,
+        showPrintMargin: false,
+        theme: 'github',
+        advanced: {
+            enableSnippets: false,
+            enableBasicAutocompletion: false,
+            enableLiveAutocompletion: false
+        },
+        onLoad: function (_ace) {
+            vm.editor = _ace;
+        }
+    };
+
+    vm.close = close;
+    vm.acknowledge = acknowledge;
+    vm.clear = clear;
+
+    loadAlarm();
+
+    function updateEditorSize(element) {
+        var newWidth = 600;
+        var newHeight = 200;
+        angular.element('#tb-alarm-details', element).height(newHeight.toString() + "px")
+            .width(newWidth.toString() + "px");
+        vm.editor.resize();
+    }
+
+    function loadAlarm() {
+        alarmService.getAlarmInfo(vm.alarmId).then(
+            function success(alarm) {
+                vm.alarm = alarm;
+                loadAlarmFields();
+            },
+            function fail() {
+                vm.alarm = null;
+            }
+        );
+    }
+
+    function loadAlarmFields() {
+        vm.createdTime = $filter('date')(vm.alarm.createdTime, 'yyyy-MM-dd HH:mm:ss');
+        vm.startTime = null;
+        if (vm.alarm.startTs) {
+            vm.startTime = $filter('date')(vm.alarm.startTs, 'yyyy-MM-dd HH:mm:ss');
+        }
+        vm.endTime = null;
+        if (vm.alarm.endTs) {
+            vm.endTime = $filter('date')(vm.alarm.endTs, 'yyyy-MM-dd HH:mm:ss');
+        }
+        vm.ackTime = null;
+        if (vm.alarm.ackTs) {
+            vm.ackTime = $filter('date')(vm.alarm.ackTs, 'yyyy-MM-dd HH:mm:ss')
+        }
+        vm.clearTime = null;
+        if (vm.alarm.clearTs) {
+            vm.clearTime = $filter('date')(vm.alarm.clearTs, 'yyyy-MM-dd HH:mm:ss');
+        }
+
+        vm.alarmSeverity = $translate.instant(types.alarmSeverity[vm.alarm.severity].name);
+
+        vm.alarmStatus = $translate.instant('alarm.display-status.' + vm.alarm.status);
+
+        vm.alarmDetails = null;
+        if (vm.alarm.details) {
+            vm.alarmDetails = angular.toJson(vm.alarm.details);
+            vm.alarmDetails = js_beautify(vm.alarmDetails, {indent_size: 4});
+        }
+    }
+
+    function acknowledge () {
+        alarmService.ackAlarm(vm.alarmId).then(
+            function success() {
+                vm.alarmUpdated = true;
+                loadAlarm();
+            }
+        );
+    }
+
+    function clear () {
+        alarmService.clearAlarm(vm.alarmId).then(
+            function success() {
+                vm.alarmUpdated = true;
+                loadAlarm();
+            }
+        );
+    }
+
+    function close () {
+        $mdDialog.hide(vm.alarmUpdated ? vm.alarm : null);
+    }
+
+}
diff --git a/ui/src/app/alarm/alarm-details-dialog.scss b/ui/src/app/alarm/alarm-details-dialog.scss
new file mode 100644
index 0000000..9b923d0
--- /dev/null
+++ b/ui/src/app/alarm/alarm-details-dialog.scss
@@ -0,0 +1,27 @@
+/**
+ * Copyright © 2016-2017 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+.tb-alarm-details-panel {
+  margin-left: 15px;
+  border: 1px solid #C0C0C0;
+  height: 100%;
+  #tb-alarm-details {
+    min-width: 600px;
+    min-height: 200px;
+    width: 100%;
+    height: 100%;
+  }
+}
diff --git a/ui/src/app/alarm/alarm-details-dialog.tpl.html b/ui/src/app/alarm/alarm-details-dialog.tpl.html
new file mode 100644
index 0000000..c958201
--- /dev/null
+++ b/ui/src/app/alarm/alarm-details-dialog.tpl.html
@@ -0,0 +1,107 @@
+<!--
+
+    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 aria-label="{{ 'alarm.alarm-details' | translate }}">
+    <md-toolbar>
+        <div class="md-toolbar-tools">
+            <h2 translate>alarm.alarm-details</h2>
+            <span flex></span>
+            <md-button class="md-icon-button" ng-click="vm.close()">
+                <ng-md-icon icon="close" aria-label="{{ 'dialog.close' | translate }}"></ng-md-icon>
+            </md-button>
+        </div>
+    </md-toolbar>
+    <md-dialog-content>
+        <div class="md-dialog-content" layout="column">
+            <div layout="row">
+                <md-input-container class="md-block">
+                    <label translate>alarm.created-time</label>
+                    <input ng-model="vm.createdTime" readonly>
+                </md-input-container>
+                <md-input-container flex class="md-block">
+                    <label translate>alarm.originator</label>
+                    <input ng-model="vm.alarm.originatorName" readonly>
+                </md-input-container>
+            </div>
+            <div layout="row" ng-if="vm.startTime || vm.endTime">
+                <md-input-container ng-if="vm.startTime" flex class="md-block">
+                    <label translate>alarm.start-time</label>
+                    <input ng-model="vm.startTime" readonly>
+                </md-input-container>
+                <md-input-container ng-if="vm.endTime" flex class="md-block">
+                    <label translate>alarm.end-time</label>
+                    <input ng-model="vm.endTime" readonly>
+                </md-input-container>
+                <span flex ng-if="!vm.startTime || !vm.endTime"></span>
+            </div>
+            <div layout="row" ng-if="vm.ackTime || vm.clearTime">
+                <md-input-container ng-if="vm.ackTime" flex class="md-block">
+                    <label translate>alarm.ack-time</label>
+                    <input ng-model="vm.ackTime" readonly>
+                </md-input-container>
+                <md-input-container ng-if="vm.clearTime" flex class="md-block">
+                    <label translate>alarm.clear-time</label>
+                    <input ng-model="vm.clearTime" readonly>
+                </md-input-container>
+                <span flex ng-if="!vm.ackTime || !vm.clearTime"></span>
+            </div>
+            <div layout="row">
+                <md-input-container flex class="md-block">
+                    <label translate>alarm.type</label>
+                    <input ng-model="vm.alarm.type" readonly>
+                </md-input-container>
+                <md-input-container flex class="md-block">
+                    <label translate>alarm.severity</label>
+                    <input class="tb-severity" ng-class="vm.types.alarmSeverity[vm.alarm.severity].class"
+                           ng-model="vm.alarmSeverity" readonly>
+                </md-input-container>
+                <md-input-container flex class="md-block">
+                    <label translate>alarm.status</label>
+                    <input ng-model="vm.alarmStatus" readonly>
+                </md-input-container>
+            </div>
+            <div class="md-caption" style="padding-left: 3px; padding-bottom: 10px; color: rgba(0,0,0,0.57);" translate>alarm.details</div>
+            <div flex class="tb-alarm-details-panel" layout="column">
+                <div flex id="tb-alarm-details" readonly
+                     ui-ace="vm.alarmDetailsOptions"
+                     ng-model="vm.alarmDetails">
+                </div>
+            </div>
+        </div>
+    </md-dialog-content>
+    <md-dialog-actions layout="row">
+        <md-button ng-if="vm.alarm.status==vm.types.alarmStatus.activeUnack ||
+                          vm.alarm.status==vm.types.alarmStatus.clearedUnack"
+                   class="md-raised md-primary"
+                   ng-disabled="loading"
+                   ng-click="vm.acknowledge()"
+                   style="margin-right:20px;">{{ 'alarm.acknowledge' |
+            translate }}
+        </md-button>
+        <md-button ng-if="vm.alarm.status==vm.types.alarmStatus.activeAck ||
+                          vm.alarm.status==vm.types.alarmStatus.activeUnack"
+                   class="md-raised md-primary"
+                   ng-disabled="loading"
+                   ng-click="vm.clear()">{{ 'alarm.clear' |
+            translate }}
+        </md-button>
+        <span flex></span>
+        <md-button ng-disabled="loading" ng-click="vm.close()" style="margin-right:20px;">{{ 'action.close' |
+            translate }}
+        </md-button>
+    </md-dialog-actions>
+</md-dialog>
diff --git a/ui/src/app/alarm/alarm-header.directive.js b/ui/src/app/alarm/alarm-header.directive.js
new file mode 100644
index 0000000..b66a972
--- /dev/null
+++ b/ui/src/app/alarm/alarm-header.directive.js
@@ -0,0 +1,39 @@
+/*
+ * 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 alarmHeaderTemplate from './alarm-header.tpl.html';
+
+/* eslint-enable import/no-unresolved, import/default */
+
+/*@ngInject*/
+export default function AlarmHeaderDirective($compile, $templateCache) {
+
+    var linker = function (scope, element) {
+
+        var template = $templateCache.get(alarmHeaderTemplate);
+        element.html(template);
+        $compile(element.contents())(scope);
+
+    }
+
+    return {
+        restrict: "A",
+        replace: false,
+        link: linker,
+        scope: false
+    };
+}
diff --git a/ui/src/app/alarm/alarm-row.directive.js b/ui/src/app/alarm/alarm-row.directive.js
new file mode 100644
index 0000000..9cb9bed
--- /dev/null
+++ b/ui/src/app/alarm/alarm-row.directive.js
@@ -0,0 +1,67 @@
+/*
+ * 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 alarmDetailsDialogTemplate from './alarm-details-dialog.tpl.html';
+
+import alarmRowTemplate from './alarm-row.tpl.html';
+
+/* eslint-enable import/no-unresolved, import/default */
+
+/*@ngInject*/
+export default function AlarmRowDirective($compile, $templateCache, types, $mdDialog, $document) {
+
+    var linker = function (scope, element, attrs) {
+
+        var template = $templateCache.get(alarmRowTemplate);
+        element.html(template);
+
+        scope.alarm = attrs.alarm;
+        scope.types = types;
+
+        scope.showAlarmDetails = function($event) {
+            var onShowingCallback = {
+                onShowing: function(){}
+            }
+            $mdDialog.show({
+                controller: 'AlarmDetailsDialogController',
+                controllerAs: 'vm',
+                templateUrl: alarmDetailsDialogTemplate,
+                locals: {alarmId: scope.alarm.id.id, showingCallback: onShowingCallback},
+                parent: angular.element($document[0].body),
+                targetEvent: $event,
+                fullscreen: true,
+                skipHide: true,
+                onShowing: function(scope, element) {
+                    onShowingCallback.onShowing(scope, element);
+                }
+            }).then(function (alarm) {
+                if (alarm) {
+                    scope.alarm = alarm;
+                }
+            });
+        }
+
+        $compile(element.contents())(scope);
+    }
+
+    return {
+        restrict: "A",
+        replace: false,
+        link: linker,
+        scope: false
+    };
+}
diff --git a/ui/src/app/alarm/alarm-table.directive.js b/ui/src/app/alarm/alarm-table.directive.js
new file mode 100644
index 0000000..c93ea03
--- /dev/null
+++ b/ui/src/app/alarm/alarm-table.directive.js
@@ -0,0 +1,211 @@
+/*
+ * 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.
+ */
+import './alarm.scss';
+
+/* eslint-disable import/no-unresolved, import/default */
+
+import alarmTableTemplate from './alarm-table.tpl.html';
+
+/* eslint-enable import/no-unresolved, import/default */
+
+/*@ngInject*/
+export default function AlarmTableDirective($compile, $templateCache, $rootScope, types, alarmService) {
+
+    var linker = function (scope, element) {
+
+        var template = $templateCache.get(alarmTableTemplate);
+
+        element.html(template);
+
+        scope.types = types;
+
+        scope.alarmSearchStatus = types.alarmSearchStatus.any;
+
+        var pageSize = 20;
+        var startTime = 0;
+        var endTime = 0;
+
+        scope.timewindow = {
+            history: {
+                timewindowMs: 24 * 60 * 60 * 1000 // 1 day
+            }
+        }
+
+        scope.topIndex = 0;
+
+        scope.theAlarms = {
+            getItemAtIndex: function (index) {
+                if (index > scope.alarms.data.length) {
+                    scope.theAlarms.fetchMoreItems_(index);
+                    return null;
+                }
+                var item = scope.alarms.data[index];
+                if (item) {
+                    item.indexNumber = index + 1;
+                }
+                return item;
+            },
+
+            getLength: function () {
+                if (scope.alarms.hasNext) {
+                    return scope.alarms.data.length + scope.alarms.nextPageLink.limit;
+                } else {
+                    return scope.alarms.data.length;
+                }
+            },
+
+            fetchMoreItems_: function () {
+                if (scope.alarms.hasNext && !scope.alarms.pending) {
+                    if (scope.entityType && scope.entityId && scope.alarmSearchStatus) {
+                        var promise = alarmService.getAlarms(scope.entityType, scope.entityId,
+                            scope.alarms.nextPageLink, scope.alarmSearchStatus, null, true, false);
+                        if (promise) {
+                            scope.alarms.pending = true;
+                            promise.then(
+                                function success(alarms) {
+                                    scope.alarms.data = scope.alarms.data.concat(alarms.data);
+                                    scope.alarms.nextPageLink = alarms.nextPageLink;
+                                    scope.alarms.hasNext = alarms.hasNext;
+                                    if (scope.alarms.hasNext) {
+                                        scope.alarms.nextPageLink.limit = pageSize;
+                                    }
+                                    scope.alarms.pending = false;
+                                },
+                                function fail() {
+                                    scope.alarms.hasNext = false;
+                                    scope.alarms.pending = false;
+                                });
+                        } else {
+                            scope.alarms.hasNext = false;
+                        }
+                    } else {
+                        scope.alarms.hasNext = false;
+                    }
+                }
+            }
+        };
+
+        scope.$watch("entityId", function(newVal, prevVal) {
+            if (newVal && !angular.equals(newVal, prevVal)) {
+                resetFilter();
+                reload();
+            }
+        });
+
+
+
+        function destroyWatchers() {
+            if (scope.alarmSearchStatusWatchHandle) {
+                scope.alarmSearchStatusWatchHandle();
+                scope.alarmSearchStatusWatchHandle = null;
+            }
+            if (scope.timewindowWatchHandle) {
+                scope.timewindowWatchHandle();
+                scope.timewindowWatchHandle = null;
+            }
+        }
+
+        function initWatchers() {
+            scope.alarmSearchStatusWatchHandle = scope.$watch("alarmSearchStatus", function(newVal, prevVal) {
+                if (newVal && !angular.equals(newVal, prevVal)) {
+                    reload();
+                }
+            });
+            scope.timewindowWatchHandle = scope.$watch("timewindow", function(newVal, prevVal) {
+                if (newVal && !angular.equals(newVal, prevVal)) {
+                    reload();
+                }
+            }, true);
+        }
+
+        function resetFilter() {
+            destroyWatchers();
+            scope.timewindow = {
+                history: {
+                    timewindowMs: 24 * 60 * 60 * 1000 // 1 day
+                }
+            };
+            scope.alarmSearchStatus = types.alarmSearchStatus.any;
+            initWatchers();
+        }
+
+        function updateTimeWindowRange () {
+            if (scope.timewindow.history.timewindowMs) {
+                var currentTime = (new Date).getTime();
+                startTime = currentTime - scope.timewindow.history.timewindowMs;
+                endTime = currentTime;
+            } else {
+                startTime = scope.timewindow.history.fixedTimewindow.startTimeMs;
+                endTime = scope.timewindow.history.fixedTimewindow.endTimeMs;
+            }
+        }
+
+        function reload () {
+            scope.topIndex = 0;
+            scope.selected = [];
+            updateTimeWindowRange();
+            scope.alarms = {
+                data: [],
+                nextPageLink: {
+                    limit: pageSize,
+                    startTime: startTime,
+                    endTime: endTime
+                },
+                hasNext: true,
+                pending: false
+            };
+            scope.theAlarms.getItemAtIndex(pageSize);
+        }
+
+        scope.noData = function() {
+            return scope.alarms.data.length == 0 && !scope.alarms.hasNext;
+        }
+
+        scope.hasData = function() {
+            return scope.alarms.data.length > 0;
+        }
+
+        scope.loading = function() {
+            return $rootScope.loading;
+        }
+
+        scope.hasScroll = function() {
+            var repeatContainer = scope.repeatContainer[0];
+            if (repeatContainer) {
+                var scrollElement = repeatContainer.children[0];
+                if (scrollElement) {
+                    return scrollElement.scrollHeight > scrollElement.clientHeight;
+                }
+            }
+            return false;
+        }
+
+        reload();
+
+        initWatchers();
+
+        $compile(element.contents())(scope);
+    }
+
+    return {
+        restrict: "E",
+        link: linker,
+        scope: {
+            entityType: '=',
+            entityId: '='
+        }
+    };
+}
diff --git a/ui/src/app/alarm/alarm-table.tpl.html b/ui/src/app/alarm/alarm-table.tpl.html
new file mode 100644
index 0000000..c32e39a
--- /dev/null
+++ b/ui/src/app/alarm/alarm-table.tpl.html
@@ -0,0 +1,49 @@
+<!--
+
+    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-content flex class="md-padding tb-absolute-fill" layout="column">
+    <section layout="row">
+        <md-input-container class="md-block" style="width: 200px;">
+            <label translate>alarm.alarm-status</label>
+            <md-select ng-model="alarmSearchStatus" ng-disabled="loading()">
+                <md-option ng-repeat="searchStatus in types.alarmSearchStatus" ng-value="searchStatus">
+                    {{ ('alarm.search-status.' + searchStatus) | translate }}
+                </md-option>
+            </md-select>
+        </md-input-container>
+        <tb-timewindow flex ng-model="timewindow" history-only as-button="true"></tb-timewindow>
+    </section>
+    <div flex layout="column" class="tb-alarm-container md-whiteframe-z1">
+        <md-list flex layout="column" class="tb-alarm-table">
+            <md-list class="tb-row tb-header" layout="row" tb-alarm-header>
+            </md-list>
+            <md-progress-linear style="max-height: 0px;" md-mode="indeterminate" ng-disabled="!loading()"
+                                ng-show="loading()"></md-progress-linear>
+            <md-divider></md-divider>
+            <span translate layout-align="center center"
+                  style="margin-top: 25px;"
+                  class="tb-prompt" ng-show="noData()">alarm.no-alarms-prompt</span>
+            <md-virtual-repeat-container ng-show="hasData()" flex md-top-index="topIndex" tb-scope-element="repeatContainer">
+                <md-list-item md-virtual-repeat="alarm in theAlarms" md-on-demand flex ng-style="hasScroll() ? {'margin-right':'-15px'} : {}">
+                    <md-list class="tb-row" flex layout="row" tb-alarm-row alarm="{{alarm}}">
+                    </md-list>
+                    <md-divider flex></md-divider>
+                </md-list-item>
+            </md-virtual-repeat-container>
+        </md-list>
+    </div>
+</md-content>
diff --git a/ui/src/app/alarm/index.js b/ui/src/app/alarm/index.js
new file mode 100644
index 0000000..0ea4610
--- /dev/null
+++ b/ui/src/app/alarm/index.js
@@ -0,0 +1,27 @@
+/*
+ * Copyright © 2016-2017 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import AlarmDetailsDialogController from './alarm-details-dialog.controller';
+import AlarmHeaderDirective from './alarm-header.directive';
+import AlarmRowDirective from './alarm-row.directive';
+import AlarmTableDirective from './alarm-table.directive';
+
+export default angular.module('thingsboard.alarm', [])
+    .controller('AlarmDetailsDialogController', AlarmDetailsDialogController)
+    .directive('tbAlarmHeader', AlarmHeaderDirective)
+    .directive('tbAlarmRow', AlarmRowDirective)
+    .directive('tbAlarmTable', AlarmTableDirective)
+    .name;
diff --git a/ui/src/app/api/alarm.service.js b/ui/src/app/api/alarm.service.js
index d9ab4ff..34e6b59 100644
--- a/ui/src/app/api/alarm.service.js
+++ b/ui/src/app/api/alarm.service.js
@@ -21,6 +21,7 @@ export default angular.module('thingsboard.api.alarm', [])
 function AlarmService($http, $q, $interval, $filter) {
     var service = {
         getAlarm: getAlarm,
+        getAlarmInfo: getAlarmInfo,
         saveAlarm: saveAlarm,
         ackAlarm: ackAlarm,
         clearAlarm: clearAlarm,
@@ -46,6 +47,21 @@ function AlarmService($http, $q, $interval, $filter) {
         return deferred.promise;
     }
 
+    function getAlarmInfo(alarmId, ignoreErrors, config) {
+        var deferred = $q.defer();
+        var url = '/api/alarm/info/' + alarmId;
+        if (!config) {
+            config = {};
+        }
+        config = Object.assign(config, { ignoreErrors: ignoreErrors });
+        $http.get(url, config).then(function success(response) {
+            deferred.resolve(response.data);
+        }, function fail() {
+            deferred.reject();
+        });
+        return deferred.promise;
+    }
+
     function saveAlarm(alarm, ignoreErrors, config) {
         var deferred = $q.defer();
         var url = '/api/alarm';
@@ -91,7 +107,7 @@ function AlarmService($http, $q, $interval, $filter) {
         return deferred.promise;
     }
 
-    function getAlarms(entityType, entityId, pageLink, alarmStatus, ascOrder, config) {
+    function getAlarms(entityType, entityId, pageLink, alarmSearchStatus, alarmStatus, fetchOriginator, ascOrder, config) {
         var deferred = $q.defer();
         var url = '/api/alarm/' + entityType + '/' + entityId + '?limit=' + pageLink.limit;
 
@@ -104,9 +120,15 @@ function AlarmService($http, $q, $interval, $filter) {
         if (angular.isDefined(pageLink.idOffset)) {
             url += '&offset=' + pageLink.idOffset;
         }
+        if (alarmSearchStatus) {
+            url += '&searchStatus=' + alarmSearchStatus;
+        }
         if (alarmStatus) {
             url += '&status=' + alarmStatus;
         }
+        if (fetchOriginator) {
+            url += '&fetchOriginator=' + ((fetchOriginator===true) ? 'true' : 'false');
+        }
         if (angular.isDefined(ascOrder) && ascOrder != null) {
             url += '&ascOrder=' + (ascOrder ? 'true' : 'false');
         }
@@ -121,7 +143,8 @@ function AlarmService($http, $q, $interval, $filter) {
 
     function fetchAlarms(alarmsQuery, pageLink, deferred, alarmsList) {
         getAlarms(alarmsQuery.entityType, alarmsQuery.entityId,
-            pageLink, alarmsQuery.alarmStatus, false, {ignoreLoading: true}).then(
+            pageLink, alarmsQuery.alarmSearchStatus, alarmsQuery.alarmStatus,
+            alarmsQuery.fetchOriginator, false, {ignoreLoading: true}).then(
             function success(alarms) {
                 if (!alarmsList) {
                     alarmsList = [];
@@ -171,7 +194,9 @@ function AlarmService($http, $q, $interval, $filter) {
         var alarmsQuery = {
             entityType: entityType,
             entityId: entityId,
+            alarmSearchStatus: null,
             alarmStatus: alarmStatus,
+            fetchOriginator: false,
             interval: interval,
             limit: limit,
             onAlarms: onAlarms
diff --git a/ui/src/app/api/alias-controller.js b/ui/src/app/api/alias-controller.js
new file mode 100644
index 0000000..60e8a31
--- /dev/null
+++ b/ui/src/app/api/alias-controller.js
@@ -0,0 +1,279 @@
+/*
+ * 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 (aliasInfo.stateEntity) {
+                            aliasCtrl.resolvedAliasesToStateEntities[aliasId] =
+                                aliasCtrl.stateController.getStateParams().entityId;
+                        }
+                        aliasCtrl.$scope.$broadcast('entityAliasResolved', aliasId);
+                        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 newDatasource;
+                            var resolvedEntities = aliasInfo.resolvedEntities;
+                            if (resolvedEntities && resolvedEntities.length) {
+                                var datasources = [];
+                                for (var i=0;i<resolvedEntities.length;i++) {
+                                    var resolvedEntity = resolvedEntities[i];
+                                    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 {
+                                if (aliasInfo.stateEntity) {
+                                    newDatasource = angular.copy(datasource);
+                                    newDatasource.unresolvedStateEntity = true;
+                                    deferred.resolve([newDatasource]);
+                                } else {
+                                    deferred.reject();
+                                }
+                            }
+                        } else {
+                            var entity = aliasInfo.currentEntity;
+                            if (entity) {
+                                datasource.entityId = entity.id;
+                                datasource.entityType = entity.entityType;
+                                datasource.entityName = entity.name;
+                                datasource.name = entity.name;
+                                deferred.resolve([datasource]);
+                            } else {
+                                if (aliasInfo.stateEntity) {
+                                    datasource.unresolvedStateEntity = true;
+                                    deferred.resolve([datasource]);
+                                } else {
+                                    deferred.reject();
+                                }
+                            }
+                        }
+                    },
+                    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;
+                     } else if (datasource.unresolvedStateEntity) {
+                        datasource.name = "Unresolved";
+                        datasource.entityName = "Unresolved";
+                     }
+                     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..20157ce 100644
--- a/ui/src/app/api/entity.service.js
+++ b/ui/src/app/api/entity.service.js
@@ -27,10 +27,14 @@ function EntityService($http, $q, $filter, $translate, $log, userService, device
         getEntity: getEntity,
         getEntities: getEntities,
         getEntitiesByNameFilter: getEntitiesByNameFilter,
-        processEntityAliases: processEntityAliases,
-        getEntityKeys: getEntityKeys,
+        resolveAlias: resolveAlias,
+        resolveAliasFilter: resolveAliasFilter,
         checkEntityAlias: checkEntityAlias,
-        createDatasoucesFromSubscriptionsInfo: createDatasoucesFromSubscriptionsInfo,
+        filterAliasByEntityTypes: filterAliasByEntityTypes,
+        getAliasFilterTypesByEntityTypes: getAliasFilterTypesByEntityTypes,
+        prepareAllowedEntityTypesList: prepareAllowedEntityTypesList,
+        getEntityKeys: getEntityKeys,
+        createDatasourcesFromSubscriptionsInfo: createDatasourcesFromSubscriptionsInfo,
         getRelatedEntities: getRelatedEntities,
         saveRelatedEntity: saveRelatedEntity,
         getRelatedEntity: getRelatedEntity,
@@ -173,6 +177,54 @@ function EntityService($http, $q, $filter, $translate, $log, userService, device
         return deferred.promise;
     }
 
+    function getSingleTenantByPageLinkPromise(pageLink) {
+        var user = userService.getCurrentUser();
+        var tenantId = user.tenantId;
+        var deferred = $q.defer();
+        tenantService.getTenant(tenantId).then(
+            function success(tenant) {
+                var tenantName = tenant.name;
+                var result = {
+                    data: [],
+                    nextPageLink: pageLink,
+                    hasNext: false
+                };
+                if (tenantName.toLowerCase().startsWith(pageLink.textSearch)) {
+                    result.data.push(tenant);
+                }
+                deferred.resolve(result);
+            },
+            function fail() {
+                deferred.reject();
+            }
+        );
+        return deferred.promise;
+    }
+
+    function getSingleCustomerByPageLinkPromise(pageLink) {
+        var user = userService.getCurrentUser();
+        var customerId = user.customerId;
+        var deferred = $q.defer();
+        customerService.getCustomer(customerId).then(
+            function success(customer) {
+                var customerName = customer.name;
+                var result = {
+                    data: [],
+                    nextPageLink: pageLink,
+                    hasNext: false
+                };
+                if (customerName.toLowerCase().startsWith(pageLink.textSearch)) {
+                    result.data.push(customer);
+                }
+                deferred.resolve(result);
+            },
+            function fail() {
+                deferred.reject();
+            }
+        );
+        return deferred.promise;
+    }
+
     function getEntitiesByPageLinkPromise(entityType, pageLink, config, subType) {
         var promise;
         var user = userService.getCurrentUser();
@@ -193,10 +245,18 @@ function EntityService($http, $q, $filter, $translate, $log, userService, device
                 }
                 break;
             case types.entityType.tenant:
-                promise = tenantService.getTenants(pageLink);
+                if (user.authority === 'TENANT_ADMIN') {
+                    promise = getSingleTenantByPageLinkPromise(pageLink);
+                } else {
+                    promise = tenantService.getTenants(pageLink);
+                }
                 break;
             case types.entityType.customer:
-                promise = customerService.getCustomers(pageLink);
+                if (user.authority === 'CUSTOMER_USER') {
+                    promise = getSingleCustomerByPageLinkPromise(pageLink);
+                } else {
+                    promise = customerService.getCustomers(pageLink);
+                }
                 break;
             case types.entityType.rule:
                 promise = ruleService.getAllRules(pageLink);
@@ -221,17 +281,21 @@ function EntityService($http, $q, $filter, $translate, $log, userService, device
         return promise;
     }
 
-    function getEntitiesByNameFilter(entityType, entityNameFilter, limit, config, subType) {
-        var deferred = $q.defer();
-        var pageLink = {limit: limit, textSearch: entityNameFilter};
+    function getEntitiesByPageLink(entityType, pageLink, config, subType, data, deferred) {
         var promise = getEntitiesByPageLinkPromise(entityType, pageLink, config, subType);
         if (promise) {
             promise.then(
                 function success(result) {
-                    if (result.data && result.data.length > 0) {
-                        deferred.resolve(result.data);
+                    data = data.concat(result.data);
+                    if (result.hasNext) {
+                        pageLink = result.nextPageLink;
+                        getEntitiesByPageLink(entityType, pageLink, config, subType, data, deferred);
                     } else {
-                        deferred.resolve(null);
+                        if (data && data.length > 0) {
+                            deferred.resolve(data);
+                        } else {
+                            deferred.resolve(null);
+                        }
                     }
                 },
                 function fail() {
@@ -241,92 +305,418 @@ function EntityService($http, $q, $filter, $translate, $log, userService, device
         } else {
             deferred.resolve(null);
         }
+    }
+
+    function getEntitiesByNameFilter(entityType, entityNameFilter, limit, config, subType) {
+        var deferred = $q.defer();
+        var pageLink = {limit: limit, textSearch: entityNameFilter};
+        if (limit == -1) { // all
+            var data = [];
+            pageLink.limit = 100;
+            getEntitiesByPageLink(entityType, pageLink, config, subType, data, deferred);
+        } else {
+            var promise = getEntitiesByPageLinkPromise(entityType, pageLink, config, subType);
+            if (promise) {
+                promise.then(
+                    function success(result) {
+                        if (result.data && result.data.length > 0) {
+                            deferred.resolve(result.data);
+                        } else {
+                            deferred.resolve(null);
+                        }
+                    },
+                    function fail() {
+                        deferred.resolve(null);
+                    }
+                );
+            } else {
+                deferred.resolve(null);
+            }
+        }
         return deferred.promise;
     }
 
-    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 entityRelationInfoToEntityInfo(entityRelationInfo, direction) {
+        var entityId = direction == types.entitySearchDirection.from ? entityRelationInfo.to : entityRelationInfo.from;
+        var name = direction == types.entitySearchDirection.from ? entityRelationInfo.toName : entityRelationInfo.fromName;
+        return {
+            name: name,
+            entityType: entityId.entityType,
+            id: entityId.id
+        };
+    }
+
+    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);
+    function entityRelationInfosToEntitiesInfo(entityRelations, direction) {
+        var entitiesInfo = [];
+        for (var d = 0; d < entityRelations.length; d++) {
+            entitiesInfo.push(entityRelationInfoToEntityInfo(entityRelations[d], direction));
+        }
+        return entitiesInfo;
+    }
+
+
+    function resolveAlias(entityAlias, stateParams) {
+        var deferred = $q.defer();
+        var filter = entityAlias.filter;
+        resolveAliasFilter(filter, stateParams, -1).then(
+            function (result) {
+                var aliasInfo = {
+                    alias: entityAlias.alias,
+                    stateEntity: result.stateEntity,
+                    resolveMultiple: filter.resolveMultiple
+                };
+                aliasInfo.resolvedEntities = result.entities;
+                aliasInfo.currentEntity = null;
+                if (aliasInfo.resolvedEntities.length) {
+                    aliasInfo.currentEntity = aliasInfo.resolvedEntities[0];
+                }
+                deferred.resolve(aliasInfo);
+            },
+            function fail() {
+                deferred.reject();
+            }
+        );
+        return deferred.promise;
+    }
+
+    function resolveAliasFilter(filter, stateParams, maxItems) {
+        var deferred = $q.defer();
+        var result = {
+            entities: [],
+            stateEntity: false
+        };
+        switch (filter.type) {
+            case types.aliasFilterType.entityList.value:
+                getEntities(filter.entityType, filter.entityList).then(
+                    function success(entities) {
+                        if (entities && entities.length) {
+                            result.entities = entitiesToEntitiesInfo(entities);
+                            deferred.resolve(result);
                         } else {
-                            if (!resolution.error) {
-                                resolution.error = 'dashboard.invalid-aliases-config';
-                            }
-                            index++;
-                            processEntityAlias(index, aliasIds, entityAliases, resolution, deferred);
+                            deferred.reject();
                         }
-                    });
-            } else {
-                var entityList = entityFilter.entityList;
-                getEntities(entityAlias.entityType, entityList).then(
+                    },
+                    function fail() {
+                        deferred.reject();
+                    }
+                );
+                break;
+            case types.aliasFilterType.entityName.value:
+                getEntitiesByNameFilter(filter.entityType, filter.entityNameFilter, maxItems).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 = entitiesToEntitiesInfo(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;
+            case types.aliasFilterType.stateEntity.value:
+                result.stateEntity = true;
+                if (stateParams && stateParams.entityId) {
+                    getEntity(stateParams.entityId.entityType, stateParams.entityId.id).then(
+                        function success(entity) {
+                            result.entities = entitiesToEntitiesInfo([entity]);
+                            deferred.resolve(result);
+                        },
+                        function fail() {
+                            deferred.reject();
+                        }
+                    );
+                } else {
+                    deferred.resolve(result);
+                }
+                break;
+            case types.aliasFilterType.assetType.value:
+                getEntitiesByNameFilter(types.entityType.asset, filter.assetNameFilter, maxItems, null, filter.assetType).then(
+                    function success(entities) {
+                        if (entities && entities.length) {
+                            result.entities = entitiesToEntitiesInfo(entities);
+                            deferred.resolve(result);
+                        } else {
+                            deferred.reject();
                         }
                     },
                     function fail() {
-                        if (!resolution.error) {
-                            resolution.error = 'dashboard.invalid-aliases-config';
+                        deferred.reject();
+                    }
+                );
+                break;
+            case types.aliasFilterType.deviceType.value:
+                getEntitiesByNameFilter(types.entityType.device, filter.deviceNameFilter, maxItems, null, filter.deviceType).then(
+                    function success(entities) {
+                        if (entities && entities.length) {
+                            result.entities = entitiesToEntitiesInfo(entities);
+                            deferred.resolve(result);
+                        } else {
+                            deferred.reject();
                         }
-                        index++;
-                        processEntityAlias(index, aliasIds, entityAliases, resolution, deferred);
+                    },
+                    function fail() {
+                        deferred.reject();
                     }
                 );
+                break;
+            case types.aliasFilterType.relationsQuery.value:
+                result.stateEntity = filter.rootStateEntity;
+                var rootEntityType;
+                var rootEntityId;
+                if (result.stateEntity && stateParams && stateParams.entityId) {
+                    rootEntityType = stateParams.entityId.entityType;
+                    rootEntityId = stateParams.entityId.id;
+                } else if (!result.stateEntity) {
+                    rootEntityType = filter.rootEntity.entityType;
+                    rootEntityId = filter.rootEntity.id;
+                }
+                if (rootEntityType && rootEntityId) {
+                    var searchQuery = {
+                        parameters: {
+                            rootId: rootEntityId,
+                            rootType: rootEntityType,
+                            direction: filter.direction
+                        },
+                        filters: filter.filters
+                    };
+                    searchQuery.parameters.maxLevel = filter.maxLevel && filter.maxLevel > 0 ? filter.maxLevel : -1;
+                    entityRelationService.findInfoByQuery(searchQuery).then(
+                        function success(allRelations) {
+                            if (allRelations && allRelations.length) {
+                                if (angular.isDefined(maxItems) && maxItems > 0) {
+                                    var limit = Math.min(allRelations.length, maxItems);
+                                    allRelations.length = limit;
+                                }
+                                result.entities = entityRelationInfosToEntitiesInfo(allRelations, filter.direction);
+                                deferred.resolve(result);
+                            } else {
+                                deferred.reject();
+                            }
+                        },
+                        function fail() {
+                            deferred.reject();
+                        }
+                    );
+                } else {
+                    deferred.resolve(result);
+                }
+                break;
+            case types.aliasFilterType.assetSearchQuery.value:
+            case types.aliasFilterType.deviceSearchQuery.value:
+                result.stateEntity = filter.rootStateEntity;
+                if (result.stateEntity && stateParams && stateParams.entityId) {
+                    rootEntityType = stateParams.entityId.entityType;
+                    rootEntityId = stateParams.entityId.id;
+                } else if (!result.stateEntity) {
+                    rootEntityType = filter.rootEntity.entityType;
+                    rootEntityId = filter.rootEntity.id;
+                }
+                if (rootEntityType && rootEntityId) {
+                    searchQuery = {
+                        parameters: {
+                            rootId: rootEntityId,
+                            rootType: rootEntityType,
+                            direction: filter.direction
+                        },
+                        relationType: filter.relationType
+                    };
+                    searchQuery.parameters.maxLevel = filter.maxLevel && filter.maxLevel > 0 ? filter.maxLevel : -1;
+                    var findByQueryPromise;
+                    if (filter.type == types.aliasFilterType.assetSearchQuery.value) {
+                        searchQuery.assetTypes = filter.assetTypes;
+                        findByQueryPromise = assetService.findByQuery(searchQuery, false);
+                    } else if (filter.type == types.aliasFilterType.deviceSearchQuery.value) {
+                        searchQuery.deviceTypes = filter.deviceTypes;
+                        findByQueryPromise = deviceService.findByQuery(searchQuery, false);
+                    }
+                    findByQueryPromise.then(
+                        function success(entities) {
+                            if (entities && entities.length) {
+                                if (angular.isDefined(maxItems) && maxItems > 0) {
+                                    var limit = Math.min(entities.length, maxItems);
+                                    entities.length = limit;
+                                }
+                                result.entities = entitiesToEntitiesInfo(entities);
+                                deferred.resolve(result);
+                            } else {
+                                deferred.reject();
+                            }
+                        },
+                        function fail() {
+                            deferred.reject();
+                        }
+                    );
+                } else {
+                    deferred.resolve(result);
+                }
+                break;
+        }
+        return deferred.promise;
+    }
+
+    function filterAliasByEntityTypes(entityAlias, entityTypes) {
+        var filter = entityAlias.filter;
+        if (filterAliasFilterTypeByEntityTypes(filter.type, entityTypes)) {
+            switch (filter.type) {
+                case types.aliasFilterType.entityList.value:
+                    return entityTypes.indexOf(filter.entityType) > -1 ? true : false;
+                case types.aliasFilterType.entityName.value:
+                    return entityTypes.indexOf(filter.entityType) > -1 ? true : false;
+                case types.aliasFilterType.stateEntity.value:
+                    return true;
+                case types.aliasFilterType.assetType.value:
+                    return entityTypes.indexOf(types.entityType.asset)  > -1 ? true : false;
+                case types.aliasFilterType.deviceType.value:
+                    return entityTypes.indexOf(types.entityType.device)  > -1 ? true : false;
+                case types.aliasFilterType.relationsQuery.value:
+                    if (filter.filters && filter.filters.length) {
+                        var match = false;
+                        for (var f=0;f<filter.filters.length;f++) {
+                            var relationFilter = filter.filters[f];
+                            if (relationFilter.entityTypes && relationFilter.entityTypes.length) {
+                                for (var et=0;et<relationFilter.entityTypes.length;et++) {
+                                    if (entityTypes.indexOf(relationFilter.entityTypes[et]) > -1) {
+                                        match = true;
+                                        break;
+                                    }
+                                }
+                            } else {
+                                match = true;
+                                break;
+                            }
+                        }
+                        return match;
+                    } else {
+                        return true;
+                    }
+                case types.aliasFilterType.assetSearchQuery.value:
+                    return entityTypes.indexOf(types.entityType.asset)  > -1 ? true : false;
+                case types.aliasFilterType.deviceSearchQuery.value:
+                    return entityTypes.indexOf(types.entityType.device)  > -1 ? true : false;
             }
-        } else {
-            deferred.resolve(resolution);
         }
+        return false;
     }
 
-    function processEntityAliases(entityAliases) {
-        var deferred = $q.defer();
-        var resolution = {
-            aliasesInfo: {
-                entityAliases: {},
-                entityAliasesInfo: {}
+    function filterAliasFilterTypeByEntityType(aliasFilterType, entityType) {
+        switch (aliasFilterType) {
+            case types.aliasFilterType.entityList.value:
+                return true;
+            case types.aliasFilterType.entityName.value:
+                return true;
+            case types.aliasFilterType.stateEntity.value:
+                return true;
+            case types.aliasFilterType.assetType.value:
+                return entityType === types.entityType.asset;
+            case types.aliasFilterType.deviceType.value:
+                return entityType === types.entityType.device;
+            case types.aliasFilterType.relationsQuery.value:
+                return true;
+            case types.aliasFilterType.assetSearchQuery.value:
+                return entityType === types.entityType.asset;
+            case types.aliasFilterType.deviceSearchQuery.value:
+                return entityType === types.entityType.device;
+        }
+        return false;
+    }
+
+    function filterAliasFilterTypeByEntityTypes(aliasFilterType, entityTypes) {
+        if (!entityTypes || !entityTypes.length) {
+            return true;
+        }
+        var valid = false;
+        entityTypes.forEach(function(entityType) {
+            valid = valid || filterAliasFilterTypeByEntityType(aliasFilterType, entityType);
+        });
+        return valid;
+    }
+
+    function getAliasFilterTypesByEntityTypes(entityTypes) {
+        var allAliasFilterTypes = types.aliasFilterType;
+        if (!entityTypes || !entityTypes.length) {
+            return allAliasFilterTypes;
+        }
+        var result = {};
+        for (var type in allAliasFilterTypes) {
+            var aliasFilterType = allAliasFilterTypes[type];
+            if (filterAliasFilterTypeByEntityTypes(aliasFilterType.value, entityTypes)) {
+                result[type] = aliasFilterType;
             }
-        };
-        var aliasIds = [];
-        if (entityAliases) {
-            for (var aliasId in entityAliases) {
-                aliasIds.push(aliasId);
+        }
+        return result;
+    }
+
+    function prepareAllowedEntityTypesList(allowedEntityTypes) {
+        var authority = userService.getAuthority();
+        var entityTypes = {};
+        switch(authority) {
+            case 'SYS_ADMIN':
+                entityTypes.tenant = types.entityType.tenant;
+                entityTypes.rule = types.entityType.rule;
+                entityTypes.plugin = types.entityType.plugin;
+                break;
+            case 'TENANT_ADMIN':
+                entityTypes.device = types.entityType.device;
+                entityTypes.asset = types.entityType.asset;
+                entityTypes.tenant = types.entityType.tenant;
+                entityTypes.customer = types.entityType.customer;
+                entityTypes.rule = types.entityType.rule;
+                entityTypes.plugin = types.entityType.plugin;
+                entityTypes.dashboard = types.entityType.dashboard;
+                break;
+            case 'CUSTOMER_USER':
+                entityTypes.device = types.entityType.device;
+                entityTypes.asset = types.entityType.asset;
+                entityTypes.customer = types.entityType.customer;
+                entityTypes.dashboard = types.entityType.dashboard;
+                break;
+        }
+
+        if (allowedEntityTypes) {
+            for (var entityType in entityTypes) {
+                if (allowedEntityTypes.indexOf(entityTypes[entityType]) === -1) {
+                    delete entityTypes[entityType];
+                }
             }
         }
-        processEntityAlias(0, aliasIds, entityAliases, resolution, deferred);
+        return entityTypes;
+    }
+
+
+    function checkEntityAlias(entityAlias) {
+        var deferred = $q.defer();
+        resolveAliasFilter(entityAlias.filter, null, 1).then(
+            function success(result) {
+                if (result.stateEntity) {
+                    deferred.resolve(true);
+                } else {
+                    var entities = result.entities;
+                    if (entities && entities.length) {
+                        deferred.resolve(true);
+                    } else {
+                        deferred.resolve(false);
+                    }
+                }
+            },
+            function fail() {
+                deferred.resolve(false);
+            }
+        );
         return deferred.promise;
     }
 
@@ -354,40 +744,13 @@ 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;
     }
 
-    function checkEntityAlias(entityAlias) {
-        var deferred = $q.defer();
-        var entityType = entityAlias.entityType;
-        var entityFilter = entityAlias.entityFilter;
-        var promise;
-        if (entityFilter.useFilter) {
-            var entityNameFilter = entityFilter.entityNameFilter;
-            promise = getEntitiesByNameFilter(entityType, entityNameFilter, 1);
-        } else {
-            var entityList = entityFilter.entityList;
-            promise = getEntities(entityType, entityList);
-        }
-        promise.then(
-            function success(entities) {
-                if (entities && entities.length > 0) {
-                    deferred.resolve(true);
-                } else {
-                    deferred.resolve(false);
-                }
-            },
-            function fail() {
-                deferred.resolve(false);
-            }
-        );
-        return deferred.promise;
-    }
-
-    function createDatasoucesFromSubscriptionsInfo(subscriptionsInfo) {
+    function createDatasourcesFromSubscriptionsInfo(subscriptionsInfo) {
         var deferred = $q.defer();
         var datasources = [];
         processSubscriptionsInfo(0, subscriptionsInfo, datasources, deferred);
@@ -822,4 +1185,4 @@ function EntityService($http, $q, $filter, $translate, $log, userService, device
         }
     }
 
-}
\ No newline at end of file
+}
diff --git a/ui/src/app/api/entity-relation.service.js b/ui/src/app/api/entity-relation.service.js
index 875b2fa..351c252 100644
--- a/ui/src/app/api/entity-relation.service.js
+++ b/ui/src/app/api/entity-relation.service.js
@@ -30,7 +30,8 @@ function EntityRelationService($http, $q) {
         findByTo: findByTo,
         findInfoByTo: findInfoByTo,
         findByToAndType: findByToAndType,
-        findByQuery: findByQuery
+        findByQuery: findByQuery,
+        findInfoByQuery: findInfoByQuery
     }
 
     return service;
@@ -159,4 +160,15 @@ function EntityRelationService($http, $q) {
         return deferred.promise;
     }
 
+    function findInfoByQuery(query) {
+        var deferred = $q.defer();
+        var url = '/api/relations/info';
+        $http.post(url, query).then(function success(response) {
+            deferred.resolve(response.data);
+        }, function fail() {
+            deferred.reject();
+        });
+        return deferred.promise;
+    }
+
 }
diff --git a/ui/src/app/api/subscription.js b/ui/src/app/api/subscription.js
index fb774a1..1d4eb30 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(){};
@@ -66,6 +73,15 @@ export default class Subscription {
 
             this.datasources = this.ctx.utils.validateDatasources(options.datasources);
             this.datasourceListeners = [];
+
+            /*
+             *   data = array of datasourceData
+             *   datasourceData = {
+             *   			tbDatasource,
+             *   			dataKey,     { name, config }
+             *   			data = array of [time, value]
+             *   }
+             */
             this.data = [];
             this.hiddenData = [];
             this.originalTimewindow = null;
@@ -103,11 +119,41 @@ 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();
+        if (!this.ctx.aliasController) {
+            this.configureData();
+            deferred.resolve();
+        } else {
+            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 +245,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(subscription);
+                        deferred.resolve();
+                    } else {
+                        subscription.rpcEnabled = false;
+                        subscription.callbacks.rpcStateChanged(subscription);
+                        deferred.resolve();
+                    }
+                },
+                function fail () {
+                    subscription.rpcEnabled = false;
+                    subscription.callbacks.rpcStateChanged(subscription);
+                    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 +390,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,39 +552,6 @@ export default class Subscription {
             var datasource = this.datasources[i];
             if (angular.isFunction(datasource))
                 continue;
-            var entityId = null;
-            var entityType = null;
-            if (datasource.type === this.ctx.types.datasourceType.entity) {
-                var aliasName = null;
-                var entityName = null;
-                if (datasource.entityId) {
-                    entityId = datasource.entityId;
-                    entityType = datasource.entityType;
-                    datasource.name = datasource.entityName;
-                    aliasName = datasource.entityName;
-                    entityName = datasource.entityName;
-                } else if (datasource.entityAliasId) {
-                    if (this.ctx.aliasesInfo.entityAliases[datasource.entityAliasId]) {
-                        entityId = this.ctx.aliasesInfo.entityAliases[datasource.entityAliasId].entityId;
-                        entityType = this.ctx.aliasesInfo.entityAliases[datasource.entityAliasId].entityType;
-                        datasource.name = this.ctx.aliasesInfo.entityAliases[datasource.entityAliasId].alias;
-                        aliasName = this.ctx.aliasesInfo.entityAliases[datasource.entityAliasId].alias;
-                        entityName = '';
-                        var entitiesInfo = this.ctx.aliasesInfo.entityAliasesInfo[datasource.entityAliasId];
-                        for (var d = 0; d < entitiesInfo.length; d++) {
-                            if (entitiesInfo[d].id === entityId) {
-                                entityName = entitiesInfo[d].name;
-                                break;
-                            }
-                        }
-                    }
-                }
-            } else {
-                datasource.name = datasource.name || this.ctx.types.datasourceType.function;
-            }
-            for (var dk = 0; dk < datasource.dataKeys.length; dk++) {
-                updateDataKeyLabel(datasource.dataKeys[dk], datasource.name, entityName, aliasName);
-            }
 
             var subscription = this;
 
@@ -521,8 +559,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);
                 },
@@ -544,6 +582,10 @@ export default class Subscription {
 
             this.datasourceListeners.push(listener);
             this.ctx.datasourceService.subscribeToDatasource(listener);
+            if (datasource.unresolvedStateEntity) {
+                this.notifyDataLoaded();
+                this.onDataUpdated();
+            }
         }
     }
 
@@ -557,48 +599,26 @@ export default class Subscription {
         }
     }
 
-    checkRpcTarget() {
-        var deviceId = null;
-        if (this.ctx.aliasesInfo.entityAliases[this.targetDeviceAliasId]) {
-            deviceId = this.ctx.aliasesInfo.entityAliases[this.targetDeviceAliasId].entityId;
-        }
-        if (!angular.equals(deviceId, this.targetDeviceId)) {
-            this.targetDeviceId = deviceId;
-            if (this.targetDeviceId) {
-                this.rpcEnabled = true;
-            } else {
-                this.rpcEnabled = this.ctx.$scope.widgetEditMode ? true : false;
-            }
-            this.callbacks.rpcStateChanged(this);
+    checkRpcTarget(aliasIds) {
+        if (aliasIds.indexOf(this.targetDeviceAliasId) > -1) {
+            return true;
+        } else {
+            return false;
         }
     }
 
-    checkSubscriptions() {
+    checkSubscriptions(aliasIds) {
         var subscriptionsChanged = false;
         for (var i = 0; i < this.datasourceListeners.length; i++) {
             var listener = this.datasourceListeners[i];
-            var entityId = null;
-            var entityType = null;
-            var aliasName = null;
-            if (listener.datasource.type === this.ctx.types.datasourceType.entity) {
-                if (listener.datasource.entityAliasId &&
-                    this.ctx.aliasesInfo.entityAliases[listener.datasource.entityAliasId]) {
-                    entityId = this.ctx.aliasesInfo.entityAliases[listener.datasource.entityAliasId].entityId;
-                    entityType = this.ctx.aliasesInfo.entityAliases[listener.datasource.entityAliasId].entityType;
-                    aliasName = this.ctx.aliasesInfo.entityAliases[listener.datasource.entityAliasId].alias;
-                }
-                if (!angular.equals(entityId, listener.entityId) ||
-                    !angular.equals(entityType, listener.entityType) ||
-                    !angular.equals(aliasName, listener.datasource.name)) {
+            if (listener.datasource.entityAliasId) {
+                if (aliasIds.indexOf(listener.datasource.entityAliasId) > -1) {
                     subscriptionsChanged = true;
                     break;
                 }
             }
         }
-        if (subscriptionsChanged) {
-            this.unsubscribe();
-            this.subscribe();
-        }
+        return subscriptionsChanged;
     }
 
     destroy() {
@@ -617,29 +637,6 @@ export default class Subscription {
 
 }
 
-const varsRegex = /\$\{([^\}]*)\}/g;
-
-function updateDataKeyLabel(dataKey, dsName, entityName, aliasName) {
-    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(dsName);
-        } else if (variableName === 'entityName') {
-            label = label.split(variable).join(entityName);
-        } else if (variableName === 'deviceName') {
-            label = label.split(variable).join(entityName);
-        } else if (variableName === 'aliasName') {
-            label = label.split(variable).join(aliasName);
-        }
-        match = varsRegex.exec(pattern);
-    }
-    dataKey.label = label;
-}
-
 function calculateMin(data) {
     if (data.length > 0) {
         var result = Number(data[0][1]);
diff --git a/ui/src/app/asset/assets.tpl.html b/ui/src/app/asset/assets.tpl.html
index 11a118f..8370d3d 100644
--- a/ui/src/app/asset/assets.tpl.html
+++ b/ui/src/app/asset/assets.tpl.html
@@ -48,11 +48,16 @@
                                 disable-attribute-scope-selection="true">
             </tb-attribute-table>
         </md-tab>
+        <md-tab ng-if="!vm.grid.detailsConfig.isDetailsEditMode" label="{{ 'alarm.alarms' | translate }}">
+            <tb-alarm-table flex entity-type="vm.types.entityType.asset"
+                            entity-id="vm.grid.operatingItem().id.id">
+            </tb-alarm-table>
+        </md-tab>
         <md-tab ng-if="!vm.grid.detailsConfig.isDetailsEditMode" label="{{ 'asset.events' | translate }}">
             <tb-event-table flex entity-type="vm.types.entityType.asset"
                             entity-id="vm.grid.operatingItem().id.id"
                             tenant-id="vm.grid.operatingItem().tenantId.id"
-                            default-event-type="{{vm.types.eventType.alarm.value}}">
+                            default-event-type="{{vm.types.eventType.error.value}}">
             </tb-event-table>
         </md-tab>
         <md-tab ng-if="!vm.grid.detailsConfig.isDetailsEditMode" label="{{ 'relation.relations' | translate }}">
diff --git a/ui/src/app/asset/index.js b/ui/src/app/asset/index.js
index 62ab201..2426e85 100644
--- a/ui/src/app/asset/index.js
+++ b/ui/src/app/asset/index.js
@@ -15,7 +15,6 @@
  */
 import uiRouter from 'angular-ui-router';
 import thingsboardGrid from '../components/grid.directive';
-import thingsboardEvent from '../event';
 import thingsboardApiUser from '../api/user.service';
 import thingsboardApiAsset from '../api/asset.service';
 import thingsboardApiCustomer from '../api/customer.service';
@@ -29,7 +28,6 @@ import AssetDirective from './asset.directive';
 export default angular.module('thingsboard.asset', [
     uiRouter,
     thingsboardGrid,
-    thingsboardEvent,
     thingsboardApiUser,
     thingsboardApiAsset,
     thingsboardApiCustomer
diff --git a/ui/src/app/common/dashboard-utils.service.js b/ui/src/app/common/dashboard-utils.service.js
index 78136df..915d550 100644
--- a/ui/src/app/common/dashboard-utils.service.js
+++ b/ui/src/app/common/dashboard-utils.service.js
@@ -23,8 +23,10 @@ function DashboardUtils(types, utils, timeService) {
 
     var service = {
         validateAndUpdateDashboard: validateAndUpdateDashboard,
+        validateAndUpdateWidget: validateAndUpdateWidget,
         getRootStateId: getRootStateId,
         createSingleWidgetDashboard: createSingleWidgetDashboard,
+        createSingleEntityFilter: createSingleEntityFilter,
         getStateLayoutsData: getStateLayoutsData,
         createDefaultState: createDefaultState,
         createDefaultLayoutData: createDefaultLayoutData,
@@ -39,40 +41,104 @@ function DashboardUtils(types, utils, timeService) {
 
     return service;
 
-    function validateAndUpdateEntityAliases(configuration) {
+    function validateAndUpdateEntityAliases(configuration, datasourcesByAliasId, targetDevicesByAliasId) {
+        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
-                    };
-                    configuration.entityAliases[aliasId] = entityAlias;
+                    entityAlias = validateAndUpdateDeviceAlias(aliasId, deviceAlias, datasourcesByAliasId, targetDevicesByAliasId);
+                    configuration.entityAliases[entityAlias.id] = entityAlias;
                 }
                 delete configuration.deviceAliases;
             }
+        } else {
+            var entityAliases = configuration.entityAliases;
+            for (aliasId in entityAliases) {
+                entityAlias = entityAliases[aliasId];
+                entityAlias = validateAndUpdateEntityAlias(aliasId, entityAlias, datasourcesByAliasId, targetDevicesByAliasId);
+                if (aliasId != entityAlias.id) {
+                    delete entityAliases[aliasId];
+                }
+                entityAliases[entityAlias.id] = entityAlias;
+            }
         }
         return configuration;
     }
 
+    function validateAliasId(aliasId, datasourcesByAliasId, targetDevicesByAliasId) {
+        if (!aliasId || !angular.isString(aliasId) || aliasId.length != 36) {
+            var newAliasId = utils.guid();
+            var aliasDatasources = datasourcesByAliasId[aliasId];
+            if (aliasDatasources) {
+                aliasDatasources.forEach(
+                      function(datasource) {
+                          datasource.entityAliasId = newAliasId;
+                      }
+                );
+            }
+            var targetDeviceAliasIdsList = targetDevicesByAliasId[aliasId];
+            if (targetDeviceAliasIdsList) {
+                targetDeviceAliasIdsList.forEach(
+                    function(targetDeviceAliasIds) {
+                        targetDeviceAliasIds[0] = newAliasId;
+                    }
+                );
+            }
+            return newAliasId;
+        } else {
+            return aliasId;
+        }
+    }
+
+    function validateAndUpdateDeviceAlias(aliasId, deviceAlias, datasourcesByAliasId, targetDevicesByAliasId) {
+        aliasId = validateAliasId(aliasId, datasourcesByAliasId, targetDevicesByAliasId);
+        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;
+            } else {
+                entityAlias.filter.entityNameFilter = deviceAlias.deviceFilter.deviceNameFilter;
+            }
+        } else {
+            entityAlias.filter.type = types.aliasFilterType.entityList.value;
+            entityAlias.filter.entityList = [deviceAlias.deviceId];
+        }
+        return entityAlias;
+    }
+
+    function validateAndUpdateEntityAlias(aliasId, entityAlias, datasourcesByAliasId, targetDevicesByAliasId) {
+        entityAlias.id = validateAliasId(aliasId, datasourcesByAliasId, targetDevicesByAliasId);
+        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;
+            } else {
+                entityAlias.filter.entityNameFilter = entityAlias.entityFilter.entityNameFilter;
+            }
+            delete entityAlias.entityType;
+            delete entityAlias.entityFilter;
+        }
+        return entityAlias;
+    }
+
     function validateAndUpdateWidget(widget) {
         if (!widget.config) {
             widget.config = {};
@@ -166,7 +232,34 @@ function DashboardUtils(types, utils, timeService) {
                 states[firstStateId].root = true;
             }
         }
-        dashboard.configuration = validateAndUpdateEntityAliases(dashboard.configuration);
+
+        var datasourcesByAliasId = {};
+        var targetDevicesByAliasId = {};
+        for (var widgetId in dashboard.configuration.widgets) {
+            widget = dashboard.configuration.widgets[widgetId];
+            widget.config.datasources.forEach(function (datasource) {
+               if (datasource.entityAliasId) {
+                   var aliasId = datasource.entityAliasId;
+                   var aliasDatasources = datasourcesByAliasId[aliasId];
+                   if (!aliasDatasources) {
+                       aliasDatasources = [];
+                       datasourcesByAliasId[aliasId] = aliasDatasources;
+                   }
+                   aliasDatasources.push(datasource);
+               }
+            });
+            if (widget.config.targetDeviceAliasIds && widget.config.targetDeviceAliasIds.length) {
+                var aliasId = widget.config.targetDeviceAliasIds[0];
+                var targetDeviceAliasIdsList = targetDevicesByAliasId[aliasId];
+                if (!targetDeviceAliasIdsList) {
+                    targetDeviceAliasIdsList = [];
+                    targetDevicesByAliasId[aliasId] = targetDeviceAliasIdsList;
+                }
+                targetDeviceAliasIdsList.push(widget.config.targetDeviceAliasIds);
+            }
+        }
+
+        dashboard.configuration = validateAndUpdateEntityAliases(dashboard.configuration, datasourcesByAliasId, targetDevicesByAliasId);
 
         if (angular.isUndefined(dashboard.configuration.timewindow)) {
             dashboard.configuration.timewindow = timeService.defaultTimewindow();
@@ -243,6 +336,15 @@ function DashboardUtils(types, utils, timeService) {
         return dashboard;
     }
 
+    function createSingleEntityFilter(entityType, entityId) {
+        return {
+            type: types.aliasFilterType.entityList.value,
+            entityList: [entityId],
+            entityType: entityType,
+            resolveMultiple: false
+        };
+    }
+
     function getStateLayoutsData(dashboard, targetState) {
         var dashboardConfiguration = dashboard.configuration;
         var states = dashboardConfiguration.states;
diff --git a/ui/src/app/common/types.constant.js b/ui/src/app/common/types.constant.js
index 44e59a2..b281ad1 100644
--- a/ui/src/app/common/types.constant.js
+++ b/ui/src/app/common/types.constant.js
@@ -65,6 +65,69 @@ export default angular.module('thingsboard.types', [])
                 clearedUnack: "CLEARED_UNACK",
                 clearedAck: "CLEARED_ACK"
             },
+            alarmSearchStatus: {
+                any: "ANY",
+                active: "ACTIVE",
+                cleared: "CLEARED",
+                ack: "ACK",
+                unack: "UNACK"
+            },
+            alarmSeverity: {
+                "CRITICAL": {
+                    name: "alarm.severity-critical",
+                    class: "tb-critical"
+                },
+                "MAJOR": {
+                    name: "alarm.severity-major",
+                    class: "tb-major"
+                },
+                "MINOR": {
+                    name: "alarm.severity-minor",
+                    class: "tb-minor"
+                },
+                "WARNING": {
+                    name: "alarm.severity-warning",
+                    class: "tb-warning"
+                },
+                "INDETERMINATE": {
+                    name: "alarm.severity-indeterminate",
+                    class: "tb-indeterminate"
+                }
+            },
+            aliasFilterType: {
+                entityList: {
+                    value: 'entityList',
+                    name: 'alias.filter-type-entity-list'
+                },
+                entityName: {
+                    value: 'entityName',
+                    name: 'alias.filter-type-entity-name'
+                },
+                stateEntity: {
+                    value: 'stateEntity',
+                    name: 'alias.filter-type-state-entity'
+                },
+                assetType: {
+                    value: 'assetType',
+                    name: 'alias.filter-type-asset-type'
+                },
+                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",
@@ -109,6 +172,62 @@ export default angular.module('thingsboard.types', [])
                 dashboard: "DASHBOARD",
                 alarm: "ALARM"
             },
+            entityTypeTranslations: {
+                "DEVICE": {
+                    type: 'entity.type-device',
+                    typePlural: 'entity.type-devices',
+                    list: 'entity.list-of-devices',
+                    nameStartsWith: 'entity.device-name-starts-with'
+                },
+                "ASSET": {
+                    type: 'entity.type-asset',
+                    typePlural: 'entity.type-assets',
+                    list: 'entity.list-of-assets',
+                    nameStartsWith: 'entity.asset-name-starts-with'
+                },
+                "RULE": {
+                    type: 'entity.type-rule',
+                    typePlural: 'entity.type-rules',
+                    list: 'entity.list-of-rules',
+                    nameStartsWith: 'entity.rule-name-starts-with'
+                },
+                "PLUGIN": {
+                    type: 'entity.type-plugin',
+                    typePlural: 'entity.type-plugins',
+                    list: 'entity.list-of-plugins',
+                    nameStartsWith: 'entity.plugin-name-starts-with'
+                },
+                "TENANT": {
+                    type: 'entity.type-tenant',
+                    typePlural: 'entity.type-tenants',
+                    list: 'entity.list-of-tenants',
+                    nameStartsWith: 'entity.tenant-name-starts-with'
+                },
+                "CUSTOMER": {
+                    type: 'entity.type-customer',
+                    typePlural: 'entity.type-customers',
+                    list: 'entity.list-of-customers',
+                    nameStartsWith: 'entity.customer-name-starts-with'
+                },
+                "USER": {
+                    type: 'entity.type-user',
+                    typePlural: 'entity.type-users',
+                    list: 'entity.list-of-users',
+                    nameStartsWith: 'entity.user-name-starts-with'
+                },
+                "DASHBOARD": {
+                    type: 'entity.type-dashboard',
+                    typePlural: 'entity.type-dashboards',
+                    list: 'entity.list-of-dashboards',
+                    nameStartsWith: 'entity.dashboard-name-starts-with'
+                },
+                "ALARM": {
+                    type: 'entity.type-alarm',
+                    typePlural: 'entity.type-alarms',
+                    list: 'entity.list-of-alarms',
+                    nameStartsWith: 'entity.alarm-name-starts-with'
+                }
+            },
             entitySearchDirection: {
                 from: "FROM",
                 to: "TO"
@@ -118,10 +237,6 @@ export default angular.module('thingsboard.types', [])
                 manages: "Manages"
             },
             eventType: {
-                alarm: {
-                    value: "ALARM",
-                    name: "event.type-alarm"
-                },
                 error: {
                     value: "ERROR",
                     name: "event.type-error"
diff --git a/ui/src/app/common/utils.service.js b/ui/src/app/common/utils.service.js
index 7b4d65e..8d2e565 100644
--- a/ui/src/app/common/utils.service.js
+++ b/ui/src/app/common/utils.service.js
@@ -106,10 +106,10 @@ function Utils($mdColorPalette, $rootScope, $window, types) {
         isDescriptorSchemaNotEmpty: isDescriptorSchemaNotEmpty,
         filterSearchTextEntities: filterSearchTextEntities,
         guid: guid,
+        cleanCopy: cleanCopy,
         isLocalUrl: isLocalUrl,
         validateDatasources: validateDatasources,
-        createKey: createKey,
-        entityTypeName: entityTypeName
+        createKey: createKey
     }
 
     return service;
@@ -291,6 +291,16 @@ function Utils($mdColorPalette, $rootScope, $window, types) {
             s4() + '-' + s4() + s4() + s4();
     }
 
+    function cleanCopy(object) {
+        var copy = angular.copy(object);
+        for (var prop in copy) {
+            if (prop && prop.startsWith('$$')) {
+                delete copy[prop];
+            }
+        }
+        return copy;
+    }
+
     function genNextColor(datasources) {
         var index = 0;
         if (datasources) {
@@ -347,27 +357,4 @@ function Utils($mdColorPalette, $rootScope, $window, types) {
         return dataKey;
     }
 
-    function entityTypeName (type) {
-        switch (type) {
-            case types.entityType.device:
-                return 'entity.type-device';
-            case types.entityType.asset:
-                return 'entity.type-asset';
-            case types.entityType.rule:
-                return 'entity.type-rule';
-            case types.entityType.plugin:
-                return 'entity.type-plugin';
-            case types.entityType.tenant:
-                return 'entity.type-tenant';
-            case types.entityType.customer:
-                return 'entity.type-customer';
-            case types.entityType.user:
-                return 'entity.type-user';
-            case types.entityType.dashboard:
-                return 'entity.type-dashboard';
-            case types.entityType.alarm:
-                return 'entity.type-alarm';
-        }
-    }
-
 }
diff --git a/ui/src/app/components/dashboard.directive.js b/ui/src/app/components/dashboard.directive.js
index f26121c..a88e656 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: '=',
@@ -85,7 +85,7 @@ function Dashboard() {
 }
 
 /*@ngInject*/
-function DashboardController($scope, $rootScope, $element, $timeout, $mdMedia, timeService, types, utils) {
+function DashboardController($scope, $rootScope, $element, $timeout, $mdMedia, $mdUtil, timeService, types, utils) {
 
     var highlightedMode = false;
     var highlightedWidget = null;
@@ -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;
@@ -796,7 +792,7 @@ function DashboardController($scope, $rootScope, $element, $timeout, $mdMedia, t
     }
 
     function dashboardLoaded() {
-        $timeout(function () {
+        $mdUtil.nextTick(function () {
             if (vm.dashboardTimewindowWatch) {
                 vm.dashboardTimewindowWatch();
                 vm.dashboardTimewindowWatch = null;
@@ -806,14 +802,27 @@ function DashboardController($scope, $rootScope, $element, $timeout, $mdMedia, t
             }, true);
             adoptMaxRows();
             vm.dashboardLoading = false;
-            $timeout(function () {
-                var gridsterScope = gridsterElement.scope();
-                vm.gridster = gridsterScope.gridster;
-                if (vm.onInit) {
-                    vm.onInit({dashboard: vm});
+            if ($scope.gridsterScopeWatcher) {
+                $scope.gridsterScopeWatcher();
+            }
+            $scope.gridsterScopeWatcher = $scope.$watch(
+                function() {
+                    var hasScope = gridsterElement.scope() ? true : false;
+                    return hasScope;
+                },
+                function(hasScope) {
+                    if (hasScope) {
+                        $scope.gridsterScopeWatcher();
+                        $scope.gridsterScopeWatcher = null;
+                        var gridsterScope = gridsterElement.scope();
+                        vm.gridster = gridsterScope.gridster;
+                        if (vm.onInit) {
+                            vm.onInit({dashboard: vm});
+                        }
+                    }
                 }
-            }, 0, false);
-        }, 0, false);
+            );
+        });
     }
 
     function loading() {
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/datasource-func.directive.js b/ui/src/app/components/datasource-func.directive.js
index 0f66dca..fe54696 100644
--- a/ui/src/app/components/datasource-func.directive.js
+++ b/ui/src/app/components/datasource-func.directive.js
@@ -71,6 +71,13 @@ function DatasourceFunc($compile, $templateCache, $mdDialog, $window, $document,
             }
         }, true);
 
+        scope.$watch('datasourceName', function () {
+            if (ngModelCtrl.$viewValue) {
+                ngModelCtrl.$viewValue.name = scope.datasourceName;
+                scope.updateValidity();
+            }
+        });
+
         ngModelCtrl.$render = function () {
             if (ngModelCtrl.$viewValue) {
                 var funcDataKeys = [];
@@ -78,6 +85,7 @@ function DatasourceFunc($compile, $templateCache, $mdDialog, $window, $document,
                     funcDataKeys = funcDataKeys.concat(ngModelCtrl.$viewValue.dataKeys);
                 }
                 scope.funcDataKeys = funcDataKeys;
+                scope.datasourceName = ngModelCtrl.$viewValue.name;
             }
         };
 
diff --git a/ui/src/app/components/datasource-func.scss b/ui/src/app/components/datasource-func.scss
index 1a5dcc7..8739ed8 100644
--- a/ui/src/app/components/datasource-func.scss
+++ b/ui/src/app/components/datasource-func.scss
@@ -15,23 +15,29 @@
  */
 @import '../../scss/constants';
 
-.tb-func-datakey-autocomplete {
-  .tb-not-found {
-    display: block;
-    line-height: 1.5;
-    height: 48px;
-    .tb-no-entries {
-      line-height: 48px;
-    }
+.tb-datasource-func {
+  @media (min-width: $layout-breakpoint-gt-sm) {
+    padding-left: 8px;
   }
-  li {
-    height: auto !important;
-    white-space: normal !important;
+
+  md-input-container.tb-datasource-name {
+    .md-errors-spacer {
+        display: none;
+    }
   }
-}
 
-tb-datasource-func {
-  @media (min-width: $layout-breakpoint-gt-sm) {
-    padding-left: 8px;
+  .tb-func-datakey-autocomplete {
+    .tb-not-found {
+      display: block;
+      line-height: 1.5;
+      height: 48px;
+      .tb-no-entries {
+        line-height: 48px;
+      }
+    }
+    li {
+      height: auto !important;
+      white-space: normal !important;
+    }
   }
-}
\ No newline at end of file
+}
diff --git a/ui/src/app/components/datasource-func.tpl.html b/ui/src/app/components/datasource-func.tpl.html
index 1853b9d..e8e08f7 100644
--- a/ui/src/app/components/datasource-func.tpl.html
+++ b/ui/src/app/components/datasource-func.tpl.html
@@ -15,59 +15,68 @@
     limitations under the License.
 
 -->
-<section flex layout='column' style="padding-left: 4px;">
-   <md-chips flex
-			     id="function_datakey_chips"
-	   			 ng-required="true"
-	             ng-model="funcDataKeys" md-autocomplete-snap
-	             md-transform-chip="transformDataKeyChip($chip)"
-	             md-require-match="false">    
-		      <md-autocomplete 
-		    		md-no-cache="false"
-		    		id="dataKey"
-					md-selected-item="selectedDataKey"
-					md-search-text="dataKeySearchText"
-					md-items="item in dataKeysSearch(dataKeySearchText)"
-					md-item-text="item.name"
-					md-min-length="0"
-		            placeholder="{{ 'datakey.function-types' | translate }}"
-					md-menu-class="tb-func-datakey-autocomplete">
-		            <span md-highlight-text="dataKeySearchText" md-highlight-flags="^i">{{item}}</span>
-				    <md-not-found>
-						<div class="tb-not-found">
-							<div class="tb-no-entries" ng-if="!textIsNotEmpty(dataKeySearchText)">
-								<span translate>device.no-keys-found</span>
+<section class="tb-datasource-func" flex layout='column'
+		 layout-align="center" layout-gt-sm='row' layout-align-gt-sm="start center">
+	<md-input-container class="tb-datasource-name" md-no-float style="min-width: 200px;">
+		<input name="datasourceName"
+			   placeholder="{{ 'datasource.name' | translate }}"
+			   ng-model="datasourceName"
+			   aria-label="{{ 'datasource.name' | translate }}">
+	</md-input-container>
+    <section flex layout='column' style="padding-left: 4px;">
+	   <md-chips flex
+					 id="function_datakey_chips"
+					 ng-required="true"
+					 ng-model="funcDataKeys" md-autocomplete-snap
+					 md-transform-chip="transformDataKeyChip($chip)"
+					 md-require-match="false">
+				  <md-autocomplete
+						md-no-cache="false"
+						id="dataKey"
+						md-selected-item="selectedDataKey"
+						md-search-text="dataKeySearchText"
+						md-items="item in dataKeysSearch(dataKeySearchText)"
+						md-item-text="item.name"
+						md-min-length="0"
+						placeholder="{{ 'datakey.function-types' | translate }}"
+						md-menu-class="tb-func-datakey-autocomplete">
+						<span md-highlight-text="dataKeySearchText" md-highlight-flags="^i">{{item}}</span>
+						<md-not-found>
+							<div class="tb-not-found">
+								<div class="tb-no-entries" ng-if="!textIsNotEmpty(dataKeySearchText)">
+									<span translate>device.no-keys-found</span>
+								</div>
+								<div ng-if="textIsNotEmpty(dataKeySearchText)">
+									<span translate translate-values='{ key: "{{dataKeySearchText | truncate:true:6:&apos;...&apos;}}" }'>device.no-key-matching</span>
+									<span>
+										<a translate ng-click="createKey($event, '#function_datakey_chips')">device.create-new-key</a>
+									</span>
+								</div>
 							</div>
-							<div ng-if="textIsNotEmpty(dataKeySearchText)">
-								<span translate translate-values='{ key: "{{dataKeySearchText | truncate:true:6:&apos;...&apos;}}" }'>device.no-key-matching</span>
-								<span>
-		          					<a translate ng-click="createKey($event, '#function_datakey_chips')">device.create-new-key</a>
-						  		</span>
-							</div>
-						</div>
-				    </md-not-found>
-			  </md-autocomplete>
-		      <md-chip-template>
-				  <div layout="row" layout-align="start center" class="tb-attribute-chip">
-					  <div class="tb-color-preview" ng-click="showColorPicker($event, $chip, $index)" style="margin-right: 5px;">
-						  <div class="tb-color-result" ng-style="{background: $chip.color}"></div>
-					  </div>
-					  <div layout="row" flex>
-						  <div class="tb-chip-label">
-							  {{$chip.label}}
+						</md-not-found>
+				  </md-autocomplete>
+				  <md-chip-template>
+					  <div layout="row" layout-align="start center" class="tb-attribute-chip">
+						  <div class="tb-color-preview" ng-click="showColorPicker($event, $chip, $index)" style="margin-right: 5px;">
+							  <div class="tb-color-result" ng-style="{background: $chip.color}"></div>
 						  </div>
-						  <div class="tb-chip-separator">: </div>
-						  <div class="tb-chip-label">
-							  <strong>{{$chip.name}}</strong>
+						  <div layout="row" flex>
+							  <div class="tb-chip-label">
+								  {{$chip.label}}
+							  </div>
+							  <div class="tb-chip-separator">: </div>
+							  <div class="tb-chip-label">
+								  <strong>{{$chip.name}}</strong>
+							  </div>
 						  </div>
+						  <md-button ng-click="editDataKey($event, $chip, $index)" class="md-icon-button tb-md-32">
+							  <md-icon aria-label="edit" class="material-icons tb-md-20">edit</md-icon>
+						  </md-button>
 					  </div>
-					  <md-button ng-click="editDataKey($event, $chip, $index)" class="md-icon-button tb-md-32">
-						  <md-icon aria-label="edit" class="material-icons tb-md-20">edit</md-icon>
-					  </md-button>
-				  </div>
-		      </md-chip-template>
-	</md-chips>
-	<div class="tb-error-messages" ng-messages="ngModelCtrl.$error" role="alert">
-		<div translate ng-message="funcTypes" class="tb-error-message">datakey.function-types-required</div>
-	</div>
+				  </md-chip-template>
+		</md-chips>
+		<div class="tb-error-messages" ng-messages="ngModelCtrl.$error" role="alert">
+			<div translate ng-message="funcTypes" class="tb-error-message">datakey.function-types-required</div>
+		</div>
+   </section>
 </section>
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..08885b8 100644
--- a/ui/src/app/components/widget.controller.js
+++ b/ui/src/app/components/widget.controller.js
@@ -20,9 +20,9 @@ import Subscription from '../api/subscription';
 /* eslint-disable angular/angularelement */
 
 /*@ngInject*/
-export default function WidgetController($scope, $timeout, $window, $element, $q, $log, $injector, $filter, tbRaf, types, utils, timeService,
+export default function WidgetController($scope, $timeout, $window, $element, $q, $log, $injector, $filter, $compile, tbRaf, types, utils, timeService,
                                          datasourceService, entityService, deviceService, visibleRect, isEdit, stDiff, dashboardTimewindow,
-                                         dashboardTimewindowApi, widget, aliasesInfo, stateController, widgetType) {
+                                         dashboardTimewindowApi, widget, aliasController, stateController, widgetInfo, widgetType) {
 
     var vm = this;
 
@@ -37,23 +37,16 @@ export default function WidgetController($scope, $timeout, $window, $element, $q
     $scope.executingRpcRequest = false;
 
     var gridsterItemInited = false;
+    var subscriptionInited = false;
+    var widgetSizeDetected = false;
 
     var cafs = {};
 
-    /*
-     *   data = array of datasourceData
-     *   datasourceData = {
-     *   			tbDatasource,
-     *   			dataKey,     { name, config }
-     *   			data = array of [time, value]
-     *   }
-     */
-
     var widgetContext = {
         inited: false,
         $scope: $scope,
-        $container: $('#container', $element),
-        $containerParent: $($element),
+        $container: null,
+        $containerParent: null,
         width: 0,
         height: 0,
         isEdit: isEdit,
@@ -80,30 +73,6 @@ export default function WidgetController($scope, $timeout, $window, $element, $q
             createSubscription: function(options, subscribe) {
                 return createSubscription(options, subscribe);
             },
-
-
-    //      type: "timeseries" or "latest" or "rpc"
-    /*      subscriptionInfo = [
-            {
-                entityType: ""
-                entityId:   ""
-                entityName: ""
-                timeseries: [{ name: "", label: "" }, ..]
-                attributes: [{ name: "", label: "" }, ..]
-            }
-    ..
-    ]*/
-
-    //      options = {
-    //        timeWindowConfig,
-    //        useDashboardTimewindow,
-    //        legendConfig,
-    //        decimals,
-    //        units,
-    //        callbacks [ onDataUpdated(subscription, apply) ]
-    //      }
-    //
-
             createSubscriptionFromInfo: function (type, subscriptionsInfo, options, useDefaultComponents, subscribe) {
                 return createSubscriptionFromInfo(type, subscriptionsInfo, options, useDefaultComponents, subscribe);
             },
@@ -149,7 +118,7 @@ export default function WidgetController($scope, $timeout, $window, $element, $q
         dashboardTimewindowApi: dashboardTimewindowApi,
         types: types,
         stDiff: stDiff,
-        aliasesInfo: aliasesInfo
+        aliasController: aliasController
     };
 
     var widgetTypeInstance;
@@ -203,23 +172,11 @@ export default function WidgetController($scope, $timeout, $window, $element, $q
 
     vm.gridsterItemInitialized = gridsterItemInitialized;
 
-    initialize();
-
-
-    /*
-            options = {
-                 type,
-                 targetDeviceAliasIds,  // RPC
-                 targetDeviceIds,       // RPC
-                 datasources,
-                 timeWindowConfig,
-                 useDashboardTimewindow,
-                 legendConfig,
-                 decimals,
-                 units,
-                 callbacks
-            }
-     */
+    initialize().then(
+        function(){
+            onInit();
+        }
+    );
 
     function createSubscriptionFromInfo(type, subscriptionsInfo, options, useDefaultComponents, subscribe) {
         var deferred = $q.defer();
@@ -233,28 +190,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 +281,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 +290,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,30 +334,126 @@ 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 configureWidgetElement() {
 
-    function initialize() {
+        $scope.displayLegend = angular.isDefined(widget.config.showLegend) ?
+            widget.config.showLegend : widget.type === types.widgetType.timeseries.value;
 
-        if (!vm.useCustomDatasources) {
-            createDefaultSubscription();
+        if ($scope.displayLegend) {
+            $scope.legendConfig = widget.config.legendConfig ||
+                {
+                    position: types.position.bottom.value,
+                    showMin: false,
+                    showMax: false,
+                    showAvg: widget.type === types.widgetType.timeseries.value,
+                    showTotal: false
+                };
+            $scope.legendData = {
+                keys: [],
+                data: []
+            };
+        }
+
+        var html = '<div class="tb-absolute-fill tb-widget-error" ng-if="widgetErrorData">' +
+            '<span>Widget Error: {{ widgetErrorData.name + ": " + widgetErrorData.message}}</span>' +
+            '</div>' +
+            '<div class="tb-absolute-fill tb-widget-loading" ng-show="loadingData" layout="column" layout-align="center center">' +
+            '<md-progress-circular md-mode="indeterminate" ng-disabled="!loadingData" class="md-accent" md-diameter="40"></md-progress-circular>' +
+            '</div>';
+
+        var containerHtml = '<div id="container">' + widgetInfo.templateHtml + '</div>';
+        if ($scope.displayLegend) {
+            var layoutType;
+            if ($scope.legendConfig.position === types.position.top.value ||
+                $scope.legendConfig.position === types.position.bottom.value) {
+                layoutType = 'column';
+            } else {
+                layoutType = 'row';
+            }
+
+            var legendStyle;
+            switch($scope.legendConfig.position) {
+                case types.position.top.value:
+                    legendStyle = 'padding-bottom: 8px;';
+                    break;
+                case types.position.bottom.value:
+                    legendStyle = 'padding-top: 8px;';
+                    break;
+                case types.position.left.value:
+                    legendStyle = 'padding-right: 0px;';
+                    break;
+                case types.position.right.value:
+                    legendStyle = 'padding-left: 0px;';
+                    break;
+            }
+
+            var legendHtml = '<tb-legend style="'+legendStyle+'" legend-config="legendConfig" legend-data="legendData"></tb-legend>';
+            containerHtml = '<div flex id="widget-container">' + containerHtml + '</div>';
+            html += '<div class="tb-absolute-fill" layout="'+layoutType+'">';
+            if ($scope.legendConfig.position === types.position.top.value ||
+                $scope.legendConfig.position === types.position.left.value) {
+                html += legendHtml;
+                html += containerHtml;
+            } else {
+                html += containerHtml;
+                html += legendHtml;
+            }
+            html += '</div>';
         } else {
-            $scope.loadingData = false;
+            html += containerHtml;
         }
 
+        //TODO:
+        /*if (progressElement) {
+         progressScope.$destroy();
+         progressScope = null;
+
+         progressElement.remove();
+         progressElement = null;
+         }*/
+
+        $element.html(html);
+
+        var containerElement = $scope.displayLegend ? angular.element($element[0].querySelector('#widget-container')) : $element;
+        widgetContext.$container = $('#container', containerElement);
+        widgetContext.$containerParent = $(containerElement);
+
+        $compile($element.contents())($scope);
+
+        addResizeListener(widgetContext.$containerParent[0], onResize); // eslint-disable-line no-undef
+    }
+
+    function destroyWidgetElement() {
+        removeResizeListener(widgetContext.$containerParent[0], onResize); // eslint-disable-line no-undef
+        $element.html('');
+        widgetContext.$container = null;
+        widgetContext.$containerParent = null;
+    }
+
+    function initialize() {
+
         $scope.$on('toggleDashboardEditMode', function (event, isEdit) {
             onEditModeChanged(isEdit);
         });
 
-        addResizeListener(widgetContext.$containerParent[0], onResize); // eslint-disable-line no-undef
-
         $scope.$watch(function () {
             return widget.row + ',' + widget.col + ',' + widget.config.mobileOrder;
         }, function () {
@@ -398,18 +472,60 @@ 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();
             }
         });
 
         $scope.$on("$destroy", function () {
-            removeResizeListener(widgetContext.$containerParent[0], onResize); // eslint-disable-line no-undef
             onDestroy();
         });
+
+        configureWidgetElement();
+        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();
+        configureWidgetElement();
+        if (!vm.useCustomDatasources) {
+            createDefaultSubscription().then(
+                function success() {
+                    subscriptionInited = true;
+                    onInit();
+                },
+                function fail() {
+                    subscriptionInited = true;
+                    onInit();
+                }
+            );
+        } else {
+            subscriptionInited = true;
+            onInit();
+        }
     }
 
     function handleWidgetException(e) {
@@ -417,8 +533,15 @@ export default function WidgetController($scope, $timeout, $window, $element, $q
         $scope.widgetErrorData = utils.processWidgetException(e);
     }
 
-    function onInit() {
-        if (!widgetContext.inited) {
+    function isReady() {
+        return subscriptionInited && gridsterItemInited && widgetSizeDetected;
+    }
+
+    function onInit(skipSizeCheck) {
+        if (!skipSizeCheck) {
+            checkSize();
+        }
+        if (!widgetContext.inited && isReady()) {
             widgetContext.inited = true;
             try {
                 widgetTypeInstance.onInit();
@@ -443,6 +566,7 @@ export default function WidgetController($scope, $timeout, $window, $element, $q
                 widgetContext.width = width;
                 widgetContext.height = height;
                 sizeChanged = true;
+                widgetSizeDetected = true;
             }
         }
         return sizeChanged;
@@ -462,8 +586,8 @@ export default function WidgetController($scope, $timeout, $window, $element, $q
                         handleWidgetException(e);
                     }
                 });
-            } else if (gridsterItemInited) {
-                onInit();
+            } else {
+                onInit(true);
             }
         }
     }
@@ -472,9 +596,7 @@ export default function WidgetController($scope, $timeout, $window, $element, $q
         if (item && item.gridster) {
             widgetContext.isMobile = item.gridster.isMobile;
             gridsterItemInited = true;
-            if (checkSize()) {
-                onInit();
-            }
+            onInit();
             // gridsterItemElement = $(item.$element);
             //updateVisibility();
         }
@@ -544,6 +666,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;
@@ -559,6 +682,7 @@ export default function WidgetController($scope, $timeout, $window, $element, $q
                 handleWidgetException(e);
             }
         }
+        destroyWidgetElement();
     }
 
     //TODO: widgets visibility
diff --git a/ui/src/app/components/widget.directive.js b/ui/src/app/components/widget.directive.js
index c92e544..a0f1b83 100644
--- a/ui/src/app/components/widget.directive.js
+++ b/ui/src/app/components/widget.directive.js
@@ -28,7 +28,7 @@ export default angular.module('thingsboard.directives.widget', [thingsboardLegen
     .name;
 
 /*@ngInject*/
-function Widget($controller, $compile, types, widgetService) {
+function Widget($controller, widgetService) {
     return {
         scope: true,
         link: function (scope, elem, attrs) {
@@ -81,90 +81,9 @@ function Widget($controller, $compile, types, widgetService) {
 
                 elem.addClass(widgetNamespace);
 
-                var html = '<div class="tb-absolute-fill tb-widget-error" ng-if="widgetErrorData">' +
-                    '<span>Widget Error: {{ widgetErrorData.name + ": " + widgetErrorData.message}}</span>' +
-                    '</div>' +
-                    '<div class="tb-absolute-fill tb-widget-loading" ng-show="loadingData" layout="column" layout-align="center center">' +
-                    '<md-progress-circular md-mode="indeterminate" ng-disabled="!loadingData" class="md-accent" md-diameter="40"></md-progress-circular>' +
-                    '</div>';
-
-                scope.displayLegend = angular.isDefined(widget.config.showLegend) ?
-                    widget.config.showLegend : widget.type === types.widgetType.timeseries.value;
-
-
-                var containerHtml = '<div id="container">' + widgetInfo.templateHtml + '</div>';
-                if (scope.displayLegend) {
-                    scope.legendConfig = widget.config.legendConfig ||
-                        {
-                            position: types.position.bottom.value,
-                            showMin: false,
-                            showMax: false,
-                            showAvg: widget.type === types.widgetType.timeseries.value,
-                            showTotal: false
-                        };
-                    scope.legendData = {
-                        keys: [],
-                        data: []
-                    };
-
-                    var layoutType;
-                    if (scope.legendConfig.position === types.position.top.value ||
-                        scope.legendConfig.position === types.position.bottom.value) {
-                        layoutType = 'column';
-                    } else {
-                        layoutType = 'row';
-                    }
-
-                    var legendStyle;
-                    switch(scope.legendConfig.position) {
-                        case types.position.top.value:
-                            legendStyle = 'padding-bottom: 8px;';
-                            break;
-                        case types.position.bottom.value:
-                            legendStyle = 'padding-top: 8px;';
-                            break;
-                        case types.position.left.value:
-                            legendStyle = 'padding-right: 0px;';
-                            break;
-                        case types.position.right.value:
-                            legendStyle = 'padding-left: 0px;';
-                            break;
-                    }
-
-                    var legendHtml = '<tb-legend style="'+legendStyle+'" legend-config="legendConfig" legend-data="legendData"></tb-legend>';
-                    containerHtml = '<div flex id="widget-container">' + containerHtml + '</div>';
-                    html += '<div class="tb-absolute-fill" layout="'+layoutType+'">';
-                    if (scope.legendConfig.position === types.position.top.value ||
-                        scope.legendConfig.position === types.position.left.value) {
-                        html += legendHtml;
-                        html += containerHtml;
-                    } else {
-                        html += containerHtml;
-                        html += legendHtml;
-                    }
-                    html += '</div>';
-                } else {
-                    html += containerHtml;
-                }
-
-                //TODO:
-                /*if (progressElement) {
-                    progressScope.$destroy();
-                    progressScope = null;
-
-                    progressElement.remove();
-                    progressElement = null;
-                }*/
-
-                elem.html(html);
-
-                var containerElement = scope.displayLegend ? angular.element(elem[0].querySelector('#widget-container')) : elem;
-
-                $compile(elem.contents())(scope);
-
                 var widgetType = widgetService.getWidgetTypeFunction(widget.bundleAlias, widget.typeAlias, widget.isSystemType);
 
-                angular.extend(locals, {$scope: scope, $element: containerElement, widgetType: widgetType});
+                angular.extend(locals, {$scope: scope, $element: elem, widgetInfo: widgetInfo, widgetType: widgetType});
 
                 widgetController = $controller('WidgetController', locals);
 
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/customer/customers.tpl.html b/ui/src/app/customer/customers.tpl.html
index 6c70a70..a6521f0 100644
--- a/ui/src/app/customer/customers.tpl.html
+++ b/ui/src/app/customer/customers.tpl.html
@@ -48,11 +48,16 @@
 								disable-attribute-scope-selection="true">
 			</tb-attribute-table>
 		</md-tab>
+		<md-tab ng-if="!vm.grid.detailsConfig.isDetailsEditMode" label="{{ 'alarm.alarms' | translate }}">
+			<tb-alarm-table flex entity-type="vm.types.entityType.customer"
+							entity-id="vm.grid.operatingItem().id.id">
+			</tb-alarm-table>
+		</md-tab>
 		<md-tab ng-if="!vm.grid.detailsConfig.isDetailsEditMode" label="{{ 'customer.events' | translate }}">
 			<tb-event-table flex entity-type="vm.types.entityType.customer"
 							entity-id="vm.grid.operatingItem().id.id"
 							tenant-id="vm.grid.operatingItem().tenantId.id"
-							default-event-type="{{vm.types.eventType.alarm.value}}">
+							default-event-type="{{vm.types.eventType.error.value}}">
 			</tb-event-table>
 		</md-tab>
 		<md-tab ng-if="!vm.grid.detailsConfig.isDetailsEditMode" label="{{ 'relation.relations' | translate }}">
diff --git a/ui/src/app/dashboard/add-widget.controller.js b/ui/src/app/dashboard/add-widget.controller.js
index 2f23cf9..5372b62 100644
--- a/ui/src/app/dashboard/add-widget.controller.js
+++ b/ui/src/app/dashboard/add-widget.controller.js
@@ -15,17 +15,18 @@
  */
 /* eslint-disable import/no-unresolved, import/default */
 
-import entityAliasesTemplate from '../entity/entity-aliases.tpl.html';
+import entityAliasDialogTemplate from '../entity/alias/entity-alias-dialog.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,52 +95,58 @@ 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',
+            controller: 'EntityAliasDialogController',
             controllerAs: 'vm',
-            templateUrl: entityAliasesTemplate,
+            templateUrl: entityAliasDialogTemplate,
             locals: {
-                config: {
-                    entityAliases: angular.copy(vm.dashboard.configuration.entityAliases),
-                    widgets: null,
-                    isSingleEntityAlias: true,
-                    singleEntityAlias: singleEntityAlias,
-                    allowedEntityTypes: allowedEntityTypes
-                }
+                isAdd: true,
+                allowedEntityTypes: allowedEntityTypes,
+                entityAliases: vm.dashboard.configuration.entityAliases,
+                alias: singleEntityAlias
             },
             parent: angular.element($document[0].body),
             fullscreen: true,
             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 ac14f79..65eb833 100644
--- a/ui/src/app/dashboard/dashboard.controller.js
+++ b/ui/src/app/dashboard/dashboard.controller.js
@@ -15,7 +15,7 @@
  */
 /* eslint-disable import/no-unresolved, import/default */
 
-import entityAliasesTemplate from '../entity/entity-aliases.tpl.html';
+import entityAliasesTemplate from '../entity/alias/entity-aliases.tpl.html';
 import dashboardSettingsTemplate from './dashboard-settings.tpl.html';
 import manageDashboardLayoutsTemplate from './layouts/manage-dashboard-layouts.tpl.html';
 import manageDashboardStatesTemplate from './states/manage-dashboard-states.tpl.html';
@@ -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) {
 
@@ -342,6 +344,8 @@ export default function DashboardController(types, dashboardUtils, widgetService
             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);
             var parentScope = $window.parent.angular.element($window.frameElement).scope();
             parentScope.$root.$broadcast('widgetEditModeInited');
             parentScope.$root.$apply();
@@ -349,7 +353,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 +372,7 @@ export default function DashboardController(types, dashboardUtils, widgetService
                                     vm.dashboardCtx.dashboardTimewindow = vm.dashboardConfiguration.timewindow;
                                 }
                             }
-                        );
+                        );*/
                 }, function fail() {
                     vm.configurationError = true;
                 });
@@ -373,6 +383,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 +927,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 +941,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 +1034,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 +1046,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..6532914 100644
--- a/ui/src/app/dashboard/edit-widget.directive.js
+++ b/ui/src/app/dashboard/edit-widget.directive.js
@@ -15,7 +15,7 @@
  */
 /* eslint-disable import/no-unresolved, import/default */
 
-import entityAliasesTemplate from '../entity/entity-aliases.tpl.html';
+import entityAliasDialogTemplate from '../entity/alias/entity-alias-dialog.tpl.html';
 import editWidgetTemplate from './edit-widget.tpl.html';
 
 /* eslint-enable import/no-unresolved, import/default */
@@ -68,47 +68,53 @@ 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',
+                controller: 'EntityAliasDialogController',
                 controllerAs: 'vm',
-                templateUrl: entityAliasesTemplate,
+                templateUrl: entityAliasDialogTemplate,
                 locals: {
-                    config: {
-                        entityAliases: angular.copy(scope.dashboard.configuration.entityAliases),
-                        widgets: null,
-                        isSingleEntityAlias: true,
-                        singleEntityAlias: singleEntityAlias,
-                        allowedEntityTypes: allowedEntityTypes
-                    }
+                    isAdd: true,
+                    allowedEntityTypes: allowedEntityTypes,
+                    entityAliases: scope.dashboard.configuration.entityAliases,
+                    alias: singleEntityAlias
                 },
                 parent: angular.element($document[0].body),
                 fullscreen: true,
                 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 +130,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/device/devices.tpl.html b/ui/src/app/device/devices.tpl.html
index 3ff6c1c..1e467be 100644
--- a/ui/src/app/device/devices.tpl.html
+++ b/ui/src/app/device/devices.tpl.html
@@ -49,11 +49,16 @@
                                 disable-attribute-scope-selection="true">
             </tb-attribute-table>
         </md-tab>
+        <md-tab ng-if="!vm.grid.detailsConfig.isDetailsEditMode" label="{{ 'alarm.alarms' | translate }}">
+            <tb-alarm-table flex entity-type="vm.types.entityType.device"
+                            entity-id="vm.grid.operatingItem().id.id">
+            </tb-alarm-table>
+        </md-tab>
         <md-tab ng-if="!vm.grid.detailsConfig.isDetailsEditMode" label="{{ 'device.events' | translate }}">
             <tb-event-table flex entity-type="vm.types.entityType.device"
                             entity-id="vm.grid.operatingItem().id.id"
                             tenant-id="vm.grid.operatingItem().tenantId.id"
-                            default-event-type="{{vm.types.eventType.alarm.value}}">
+                            default-event-type="{{vm.types.eventType.error.value}}">
             </tb-event-table>
         </md-tab>
         <md-tab ng-if="!vm.grid.detailsConfig.isDetailsEditMode" label="{{ 'relation.relations' | translate }}">
diff --git a/ui/src/app/device/index.js b/ui/src/app/device/index.js
index ea42ee8..5a8adaf 100644
--- a/ui/src/app/device/index.js
+++ b/ui/src/app/device/index.js
@@ -15,7 +15,6 @@
  */
 import uiRouter from 'angular-ui-router';
 import thingsboardGrid from '../components/grid.directive';
-import thingsboardEvent from '../event';
 import thingsboardApiUser from '../api/user.service';
 import thingsboardApiDevice from '../api/device.service';
 import thingsboardApiCustomer from '../api/customer.service';
@@ -30,7 +29,6 @@ import DeviceDirective from './device.directive';
 export default angular.module('thingsboard.device', [
     uiRouter,
     thingsboardGrid,
-    thingsboardEvent,
     thingsboardApiUser,
     thingsboardApiDevice,
     thingsboardApiCustomer
diff --git a/ui/src/app/entity/alias/aliases-entity-select-panel.controller.js b/ui/src/app/entity/alias/aliases-entity-select-panel.controller.js
new file mode 100644
index 0000000..5365db7
--- /dev/null
+++ b/ui/src/app/entity/alias/aliases-entity-select-panel.controller.js
@@ -0,0 +1,49 @@
+/*
+ * 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 AliasesEntitySelectPanelController(mdPanelRef, $scope, $filter, types, aliasController, onEntityAliasesUpdate) {
+
+    var vm = this;
+    vm._mdPanelRef = mdPanelRef;
+    vm.aliasController = aliasController;
+    vm.onEntityAliasesUpdate = onEntityAliasesUpdate;
+    vm.entityAliases = {};
+    vm.entityAliasesInfo = {};
+
+    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);
+            vm.entityAliasesInfo[aliasId].selectedId = aliasInfo.currentEntity.id;
+        }
+    }
+
+    function currentAliasEntityChanged(aliasId, selectedId) {
+        var resolvedEntities = vm.entityAliasesInfo[aliasId].resolvedEntities;
+        var selected = $filter('filter')(resolvedEntities, {id: selectedId});
+        if (selected && selected.length) {
+            vm.aliasController.updateCurrentAliasEntity(aliasId, selected[0]);
+            if (onEntityAliasesUpdate) {
+                onEntityAliasesUpdate();
+            }
+        }
+    }
+
+}
diff --git a/ui/src/app/entity/alias/entity-alias-dialog.controller.js b/ui/src/app/entity/alias/entity-alias-dialog.controller.js
new file mode 100644
index 0000000..a5b221a
--- /dev/null
+++ b/ui/src/app/entity/alias/entity-alias-dialog.controller.js
@@ -0,0 +1,109 @@
+/*
+ * 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.
+ */
+
+import './entity-alias-dialog.scss';
+
+/*@ngInject*/
+export default function EntityAliasDialogController($scope, $mdDialog, $q, $filter, utils, entityService, types, isAdd, allowedEntityTypes, entityAliases, alias) {
+
+    var vm = this;
+
+    vm.types = types;
+    vm.isAdd = isAdd;
+    vm.allowedEntityTypes = allowedEntityTypes;
+    if (angular.isArray(entityAliases)) {
+        vm.entityAliases = entityAliases;
+    } else {
+        vm.entityAliases = [];
+        for (var aliasId in entityAliases) {
+            vm.entityAliases.push(entityAliases[aliasId]);
+        }
+    }
+    if (vm.isAdd && !alias) {
+        vm.alias = {
+            alias: '',
+            filter: {
+                resolveMultiple: false
+            }
+        };
+    } else {
+        vm.alias = alias;
+    }
+
+    vm.cancel = cancel;
+    vm.save = save;
+
+    $scope.$watch('vm.alias.alias', function (newAlias) {
+        if (newAlias) {
+            var valid = true;
+            var result = $filter('filter')(vm.entityAliases, {alias: newAlias}, true);
+            if (result && result.length) {
+                if (vm.isAdd || vm.alias.id != result[0].id) {
+                    valid = false;
+                }
+            }
+            $scope.theForm.aliasName.$setValidity('duplicateAliasName', valid);
+        }
+    });
+
+    $scope.$watch('theForm.$pristine', function() {
+        if ($scope.theForm && !$scope.theForm.$pristine) {
+            $scope.theForm.$setValidity('entityFilter', true);
+        }
+    });
+
+    function validate() {
+        var deferred = $q.defer();
+        var validationResult = {
+            entity: null,
+            stateEntity: false
+        }
+        entityService.resolveAliasFilter(vm.alias.filter, null, 1).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() {
+                if (vm.isAdd) {
+                    vm.alias.id = utils.guid();
+                }
+                $mdDialog.hide(vm.alias);
+            },
+            function fail() {
+                $scope.theForm.$setValidity('entityFilter', false);
+            }
+        )
+    }
+
+}
diff --git a/ui/src/app/entity/alias/entity-alias-dialog.tpl.html b/ui/src/app/entity/alias/entity-alias-dialog.tpl.html
new file mode 100644
index 0000000..b78a795
--- /dev/null
+++ b/ui/src/app/entity/alias/entity-alias-dialog.tpl.html
@@ -0,0 +1,73 @@
+<!--
+
+    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-alias-dialog" style="width: 600px;" aria-label="{{ (vm.isAdd ? 'alias.add' : 'alias.edit') | translate  }}">
+    <form name="theForm" ng-submit="vm.save()">
+        <md-toolbar>
+            <div class="md-toolbar-tools">
+                <h2>{{ (vm.isAdd ? 'alias.add' : 'alias.edit') | 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">
+                        <div layout="row">
+                            <md-input-container flex class="md-block">
+                                <label translate>alias.name</label>
+                                <input required name="aliasName"
+                                       ng-model="vm.alias.alias"
+                                       aria-label="{{ 'alias.name' | translate }}">
+                                <div ng-messages="theForm.aliasName.$error">
+                                    <div ng-message="required" translate>alias.name-required</div>
+                                    <div ng-message="duplicateAliasName" translate>alias.duplicate-alias</div>
+                                </div>
+                            </md-input-container>
+                            <section class="tb-resolve-multiple-switch" layout="column" layout-align="start center">
+                                <label class="tb-small resolve-multiple-label" translate>alias.resolve-multiple</label>
+                                <md-switch class="resolve-multiple-switch" ng-model="vm.alias.filter.resolveMultiple"
+                                           aria-label="{{ 'alias.resolve-multiple' | translate }}">
+                                </md-switch>
+                            </section>
+                        </div>
+                        <tb-entity-filter
+                            ng-model="vm.alias.filter"
+                            allowed-entity-types="vm.allowedEntityTypes"
+                            the-form="theForm">
+                        </tb-entity-filter>
+                        <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">
+                {{ (vm.isAdd ? 'action.add' : '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>
diff --git a/ui/src/app/entity/alias/entity-aliases.scss b/ui/src/app/entity/alias/entity-aliases.scss
new file mode 100644
index 0000000..9803be1
--- /dev/null
+++ b/ui/src/app/entity/alias/entity-aliases.scss
@@ -0,0 +1,49 @@
+/**
+ * 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-aliases-dialog {
+  .md-dialog-content {
+    padding-bottom: 0px;
+    padding-top: 0px;
+  }
+  .tb-aliases-header {
+    min-height: 40px;
+    padding: 0 34px 0 34px;
+    margin: 5px;
+    .tb-header-label {
+      font-size: 14px;
+      color: rgba(0, 0, 0, 0.570588);
+    }
+  }
+  .tb-alias {
+    padding: 0 0 0 10px;
+    margin: 5px;
+    md-input-container {
+      margin: 0px;
+    }
+    .tb-resolve-multiple-switch {
+      padding-left: 10px;
+      .resolve-multiple-switch {
+        margin: 0;
+      }
+    }
+    .md-button {
+      &.md-icon-button {
+        margin: 0px;
+      }
+    }
+  }
+}
diff --git a/ui/src/app/entity/attribute/add-widget-to-dashboard-dialog.controller.js b/ui/src/app/entity/attribute/add-widget-to-dashboard-dialog.controller.js
index e5822a4..bcf37dd 100644
--- a/ui/src/app/entity/attribute/add-widget-to-dashboard-dialog.controller.js
+++ b/ui/src/app/entity/attribute/add-widget-to-dashboard-dialog.controller.js
@@ -22,7 +22,8 @@ import selectTargetLayoutTemplate from '../../dashboard/layouts/select-target-la
 /* eslint-enable import/no-unresolved, import/default */
 
 /*@ngInject*/
-export default function AddWidgetToDashboardDialogController($scope, $mdDialog, $state, $q, $document, itembuffer, dashboardService, entityId, entityType, entityName, widget) {
+export default function AddWidgetToDashboardDialogController($scope, $mdDialog, $state, $q, $document, dashboardUtils,
+                                                             types, itembuffer, dashboardService, entityId, entityType, entityName, widget) {
 
     var vm = this;
 
@@ -125,13 +126,8 @@ export default function AddWidgetToDashboardDialogController($scope, $mdDialog, 
             targetDeviceAliases: {}
         };
         aliasesInfo.datasourceAliases[0] = {
-            aliasName: entityName,
-            entityType: entityType,
-            entityFilter: {
-                useFilter: false,
-                entityNameFilter: '',
-                entityList: [entityId]
-            }
+            alias: entityName,
+            filter: dashboardUtils.createSingleEntityFilter(entityType, entityId)
         };
         itembuffer.addWidgetToDashboard(theDashboard, targetState, targetLayout, vm.widget, aliasesInfo, null, 48, null, -1, -1).then(
             function(theDashboard) {
diff --git a/ui/src/app/entity/attribute/attribute-table.directive.js b/ui/src/app/entity/attribute/attribute-table.directive.js
index da7697d..62d2081 100644
--- a/ui/src/app/entity/attribute/attribute-table.directive.js
+++ b/ui/src/app/entity/attribute/attribute-table.directive.js
@@ -26,10 +26,12 @@ import editAttributeValueTemplate from './edit-attribute-value.tpl.html';
 /* eslint-enable import/no-unresolved, import/default */
 
 import EditAttributeValueController from './edit-attribute-value.controller';
+import AliasController from '../../api/alias-controller';
 
 /*@ngInject*/
 export default function AttributeTableDirective($compile, $templateCache, $rootScope, $q, $mdEditDialog, $mdDialog,
-                                                $document, $translate, $filter, utils, types, dashboardService, attributeService, widgetService) {
+                                                $mdUtil, $document, $translate, $filter, utils, types, dashboardUtils,
+                                                dashboardService, entityService, attributeService, widgetService) {
 
     var linker = function (scope, element, attrs) {
 
@@ -246,15 +248,19 @@ export default function AttributeTableDirective($compile, $templateCache, $rootS
         }
 
         scope.nextWidget = function() {
-            if (scope.widgetsCarousel.index < scope.widgetsList.length-1) {
-                scope.widgetsCarousel.index++;
-            }
+            $mdUtil.nextTick(function () {
+                if (scope.widgetsCarousel.index < scope.widgetsList.length - 1) {
+                    scope.widgetsCarousel.index++;
+                }
+            });
         }
 
         scope.prevWidget = function() {
-            if (scope.widgetsCarousel.index > 0) {
-                scope.widgetsCarousel.index--;
-            }
+            $mdUtil.nextTick(function () {
+                if (scope.widgetsCarousel.index > 0) {
+                    scope.widgetsCarousel.index--;
+                }
+            });
         }
 
         scope.enterWidgetMode = function() {
@@ -281,23 +287,28 @@ export default function AttributeTableDirective($compile, $templateCache, $rootS
             scope.firstBundle = true;
             scope.selectedWidgetsBundleAlias = types.systemBundleAlias.cards;
 
-            scope.aliasesInfo = {
-                entityAliases: {
-                    '1': {alias: scope.entityName, entityType: scope.entityType, entityId: scope.entityId}
-                },
-                entityAliasesInfo: {
-                    '1': [
-                        {name: scope.entityName, entityType: scope.entityType, id: scope.entityId}
-                    ]
+            var entityAlias = {
+                id: utils.guid(),
+                alias: scope.entityName,
+                filter: dashboardUtils.createSingleEntityFilter(scope.entityType, scope.entityId)
+            };
+            var entitiAliases = {};
+            entitiAliases[entityAlias.id] = entityAlias;
+
+            var stateController = {
+                getStateParams: function() {
+                    return {};
                 }
             };
+            scope.aliasController = new AliasController(scope, $q, $filter, utils,
+                types, entityService, stateController, entitiAliases);
 
             var dataKeyType = scope.attributeScope === types.latestTelemetry ?
                 types.dataKeyType.timeseries : types.dataKeyType.attribute;
 
             var datasource = {
                 type: types.datasourceType.entity,
-                entityAliasId: '1',
+                entityAliasId: entityAlias.id,
                 dataKeys: []
             }
             var i = 0;
diff --git a/ui/src/app/entity/attribute/attribute-table.tpl.html b/ui/src/app/entity/attribute/attribute-table.tpl.html
index 1afd1bb..2566a08 100644
--- a/ui/src/app/entity/attribute/attribute-table.tpl.html
+++ b/ui/src/app/entity/attribute/attribute-table.tpl.html
@@ -156,7 +156,7 @@
             rn-swipe-disabled="true">
             <li ng-repeat="widgets in widgetsList">
                 <tb-dashboard
-                        aliases-info="aliasesInfo"
+                        alias-controller="aliasController"
                         widgets="widgets"
                         get-st-diff="getServerTimeDiff()"
                         columns="20"
diff --git a/ui/src/app/entity/entity-filter.directive.js b/ui/src/app/entity/entity-filter.directive.js
index b1d92c6..b81f369 100644
--- a/ui/src/app/entity/entity-filter.directive.js
+++ b/ui/src/app/entity/entity-filter.directive.js
@@ -23,7 +23,7 @@ import entityFilterTemplate from './entity-filter.tpl.html';
 import './entity-filter.scss';
 
 /*@ngInject*/
-export default function EntityFilterDirective($compile, $templateCache, $q, entityService) {
+export default function EntityFilterDirective($compile, $templateCache, $q, $document, $mdDialog, types, entityService) {
 
     var linker = function (scope, element, attrs, ngModelCtrl) {
 
@@ -31,181 +31,76 @@ export default function EntityFilterDirective($compile, $templateCache, $q, enti
         element.html(template);
 
         scope.ngModelCtrl = ngModelCtrl;
+        scope.types = types;
+        scope.aliasFilterTypes = entityService.getAliasFilterTypesByEntityTypes(scope.allowedEntityTypes);
 
-        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() {
-            if (ngModelCtrl.$viewValue) {
-                var value = ngModelCtrl.$viewValue;
-                var valid;
-                if (value.useFilter) {
-                    ngModelCtrl.$setValidity('entityList', true);
-                    if (angular.isDefined(value.entityNameFilter) && value.entityNameFilter.length > 0) {
-                        ngModelCtrl.$setValidity('entityNameFilter', true);
-                        valid = angular.isDefined(scope.model.matchingFilterEntity) && scope.model.matchingFilterEntity != null;
-                        ngModelCtrl.$setValidity('entityNameFilterEntityMatch', valid);
-                    } else {
-                        ngModelCtrl.$setValidity('entityNameFilter', false);
-                    }
-                } else {
-                    ngModelCtrl.$setValidity('entityNameFilter', true);
-                    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: ''
+        scope.$watch('filter.type', function (newType, prevType) {
+            if (newType && newType != prevType) {
+                updateFilter();
             }
-            if (ngModelCtrl.$viewValue) {
-                var value = ngModelCtrl.$viewValue;
-                var model = scope.model;
-                model.useFilter = value.useFilter === true ? true: false;
-                model.entityList = [];
-                model.entityNameFilter = value.entityNameFilter || '';
-                processEntityNameFilter(model.entityNameFilter).then(
-                    function(entity) {
-                        scope.model.matchingFilterEntity = entity;
-                        if (value.entityList && value.entityList.length > 0) {
-                            entityService.getEntities(scope.entityType, value.entityList).then(function (entities) {
-                                model.entityList = entities;
-                                updateMatchingEntity();
-                                initWatchers();
-                            });
-                        } else {
-                            updateMatchingEntity();
-                            initWatchers();
-                        }
+        });
+
+        function updateFilter() {
+            var filter = {};
+            filter.type = scope.filter.type;
+            filter.resolveMultiple = scope.filter.resolveMultiple;
+            switch (filter.type) {
+                case types.aliasFilterType.entityList.value:
+                    filter.entityType = null;
+                    filter.entityList = [];
+                    break;
+                case types.aliasFilterType.entityName.value:
+                    filter.entityType = null;
+                    filter.entityNameFilter = '';
+                    break;
+                case types.aliasFilterType.stateEntity.value:
+                    break;
+                case types.aliasFilterType.assetType.value:
+                    filter.assetType = null;
+                    filter.assetNameFilter = '';
+                    break;
+                case types.aliasFilterType.deviceType.value:
+                    filter.deviceType = null;
+                    filter.deviceNameFilter = '';
+                    break;
+                case types.aliasFilterType.relationsQuery.value:
+                case types.aliasFilterType.assetSearchQuery.value:
+                case types.aliasFilterType.deviceSearchQuery.value:
+                    filter.rootStateEntity = false;
+                    filter.rootEntity = null;
+                    filter.direction = types.entitySearchDirection.from;
+                    filter.maxLevel = 1;
+                    if (filter.type === types.aliasFilterType.relationsQuery.value) {
+                        filter.filters = [];
+                    } else if (filter.type === types.aliasFilterType.assetSearchQuery.value) {
+                        filter.relationType = null;
+                        filter.assetTypes = [];
+                    } else if (filter.type === types.aliasFilterType.deviceSearchQuery.value) {
+                        filter.relationType = null;
+                        filter.deviceTypes = [];
                     }
-                )
+                    break;
             }
+            scope.filter = filter;
         }
 
-        function updateMatchingEntity() {
-            if (scope.model.useFilter) {
-                scope.model.matchingEntity = scope.model.matchingFilterEntity;
-            } else {
-                if (scope.model.entityList && scope.model.entityList.length > 0) {
-                    scope.model.matchingEntity = scope.model.entityList[0];
-                } else {
-                    scope.model.matchingEntity = null;
-                }
-            }
-        }
-
-        function processEntityNameFilter(entityNameFilter) {
-            var deferred = $q.defer();
-            if (angular.isDefined(entityNameFilter) && entityNameFilter.length > 0) {
-                scope.fetchEntities(entityNameFilter, 1).then(function (entities) {
-                    if (entities && entities.length > 0) {
-                        deferred.resolve(entities[0]);
-                    } else {
-                        deferred.resolve(null);
-                    }
-                });
-            } else {
-                deferred.resolve(null);
-            }
-            return deferred.promise;
-        }
+        scope.$watch('filter', function () {
+            scope.updateView();
+        });
 
-        function destroyWatchers() {
-            if (scope.entityTypeDeregistration) {
-                scope.entityTypeDeregistration();
-                scope.entityTypeDeregistration = null;
-            }
-            if (scope.entityListDeregistration) {
-                scope.entityListDeregistration();
-                scope.entityListDeregistration = null;
-            }
-            if (scope.useFilterDeregistration) {
-                scope.useFilterDeregistration();
-                scope.useFilterDeregistration = null;
-            }
-            if (scope.entityNameFilterDeregistration) {
-                scope.entityNameFilterDeregistration();
-                scope.entityNameFilterDeregistration = null;
-            }
-            if (scope.matchingEntityDeregistration) {
-                scope.matchingEntityDeregistration();
-                scope.matchingEntityDeregistration = null;
-            }
+        scope.updateView = function() {
+            ngModelCtrl.$setViewValue(scope.filter);
         }
 
-        function initWatchers() {
-
-            scope.entityTypeDeregistration = scope.$watch('entityType', function (newEntityType, prevEntityType) {
-                if (!angular.equals(newEntityType, prevEntityType)) {
-                    scope.model.entityList = [];
-                    scope.model.entityNameFilter = '';
-                }
-            });
-
-            scope.entityListDeregistration = scope.$watch('model.entityList', function () {
-                if (ngModelCtrl.$viewValue) {
-                    var value = ngModelCtrl.$viewValue;
-                    value.entityList = [];
-                    if (scope.model.entityList && scope.model.entityList.length > 0) {
-                        for (var i=0;i<scope.model.entityList.length;i++) {
-                            value.entityList.push(scope.model.entityList[i].id.id);
-                        }
-                    }
-                    updateMatchingEntity();
-                    ngModelCtrl.$setViewValue(value);
-                    scope.updateValidity();
-                }
-            }, true);
-            scope.useFilterDeregistration = scope.$watch('model.useFilter', function () {
-                if (ngModelCtrl.$viewValue) {
-                    var value = ngModelCtrl.$viewValue;
-                    value.useFilter = scope.model.useFilter;
-                    updateMatchingEntity();
-                    ngModelCtrl.$setViewValue(value);
-                    scope.updateValidity();
-                }
-            });
-            scope.entityNameFilterDeregistration = scope.$watch('model.entityNameFilter', function (newNameFilter, prevNameFilter) {
-                if (ngModelCtrl.$viewValue) {
-                    if (!angular.equals(newNameFilter, prevNameFilter)) {
-                        var value = ngModelCtrl.$viewValue;
-                        value.entityNameFilter = scope.model.entityNameFilter;
-                        processEntityNameFilter(value.entityNameFilter).then(
-                            function(entity) {
-                                scope.model.matchingFilterEntity = entity;
-                                updateMatchingEntity();
-                                ngModelCtrl.$setViewValue(value);
-                                scope.updateValidity();
-                            }
-                        );
-                    }
-                }
-            });
-
-            scope.matchingEntityDeregistration = scope.$watch('model.matchingEntity', function (newMatchingEntity, prevMatchingEntity) {
-                if (!angular.equals(newMatchingEntity, prevMatchingEntity)) {
-                    if (scope.onMatchingEntityChange) {
-                        scope.onMatchingEntityChange({entity: newMatchingEntity});
-                    }
+        ngModelCtrl.$render = function () {
+            if (ngModelCtrl.$viewValue) {
+                scope.filter = ngModelCtrl.$viewValue;
+            } else {
+                scope.filter = {
+                    type: null,
+                    resolveMultiple: false
                 }
-            });
+            }
         }
 
         $compile(element.contents())(scope);
@@ -217,9 +112,8 @@ export default function EntityFilterDirective($compile, $templateCache, $q, enti
         require: "^ngModel",
         link: linker,
         scope: {
-            entityType: '=',
-            isEdit: '=',
-            onMatchingEntityChange: '&'
+            theForm: '=',
+            allowedEntityTypes: '=?'
         }
     };
 
diff --git a/ui/src/app/entity/entity-filter.scss b/ui/src/app/entity/entity-filter.scss
index 1525842..7e998ca 100644
--- a/ui/src/app/entity/entity-filter.scss
+++ b/ui/src/app/entity/entity-filter.scss
@@ -13,33 +13,24 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 .tb-entity-filter {
-  #entity_list_chips {
-    .md-chips {
-      padding-bottom: 1px;
+
+  #relationsQueryFilter {
+    padding-top: 20px;
+    tb-entity-select {
+      min-height: 92px;
     }
   }
-  .entity-name-filter-input {
-    margin-top: 10px;
-    margin-bottom: 0px;
-    .md-errors-spacer {
-      min-height: 0px;
-    }
-  }
-  .tb-filter-switch {
+
+  .tb-root-state-entity-switch {
     padding-left: 10px;
-    .filter-switch {
+    .root-state-entity-switch {
       margin: 0;
     }
-    .filter-label {
+    .root-state-entity-label {
       margin: 5px 0;
     }
   }
-  .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-filter.tpl.html b/ui/src/app/entity/entity-filter.tpl.html
index 7e49ba9..4f5af00 100644
--- a/ui/src/app/entity/entity-filter.tpl.html
+++ b/ui/src/app/entity/entity-filter.tpl.html
@@ -15,53 +15,219 @@
     limitations under the License.
 
 -->
-<section layout='column' class="tb-entity-filter">
-    <section layout='row'>
-        <section layout="column" flex ng-show="!model.useFilter">
-            <md-chips flex
-                      id="entity_list_chips"
-                      ng-required="!useFilter"
-                      ng-model="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>
-        </section>
-        <section layout="row" flex ng-show="model.useFilter">
-            <md-input-container flex class="entity-name-filter-input">
-                <label translate>entity.name-starts-with</label>
-                <input ng-model="model.entityNameFilter" aria-label="{{ 'entity.name-starts-with' | translate }}">
+<div layout='column' class="tb-entity-filter">
+    <md-input-container class="md-block">
+        <label>{{ 'alias.filter-type' | translate }}</label>
+        <md-select required name="filterType"
+                   ng-model="filter.type" aria-label="{{ 'alias.filter-type' | translate }}">
+            <md-option ng-repeat="type in aliasFilterTypes" ng-value="type.value">
+                {{type.name | translate}}
+            </md-option>
+        </md-select>
+        <div ng-messages="theForm.filterType.$error">
+            <div ng-message="required" translate>alias.filter-type-required</div>
+        </div>
+    </md-input-container>
+    <section layout="column" ng-if="filter.type == types.aliasFilterType.entityList.value" id="entityListFilter">
+        <tb-entity-type-select
+                ng-model="filter.entityType"
+                the-form="theForm"
+                tb-required="true"
+                allowed-entity-types="allowedEntityTypes">
+        </tb-entity-type-select>
+        <tb-entity-list
+                ng-model="filter.entityList"
+                tb-required="true"
+                entity-type="filter.entityType">
+        </tb-entity-list>
+    </section>
+    <section flex layout="column" ng-if="filter.type == types.aliasFilterType.entityName.value" id="entityNameFilter">
+        <tb-entity-type-select
+                ng-model="filter.entityType"
+                the-form="theForm"
+                tb-required="true"
+                allowed-entity-types="allowedEntityTypes">
+        </tb-entity-type-select>
+        <md-input-container class="md-block">
+            <label translate>entity.name-starts-with</label>
+            <input required name="entityNameFilter"
+                   ng-model="filter.entityNameFilter"
+                   aria-label="{{ 'entity.name-starts-with' | translate }}">
+            <div ng-messages="theForm.entityNameFilter.$error">
+                <div ng-message="required" translate>entity.entity-name-filter-required</div>
+            </div>
+        </md-input-container>
+    </section>
+    <section layout="column" ng-if="filter.type == types.aliasFilterType.stateEntity.value" id="stateEntityFilter">
+    </section>
+    <section layout="column" ng-if="filter.type == types.aliasFilterType.assetType.value" id="assetTypeFilter">
+        <tb-entity-subtype-autocomplete
+                tb-required="true"
+                the-form="theForm"
+                ng-model="filter.assetType"
+                entity-type="types.entityType.asset">
+        </tb-entity-subtype-autocomplete>
+        <md-input-container class="md-block">
+            <label translate>asset.name-starts-with</label>
+            <input name="assetNameFilter"
+                   ng-model="filter.assetNameFilter"
+                   aria-label="{{ 'asset.name-starts-with' | translate }}">
+        </md-input-container>
+    </section>
+    <section layout="column" ng-if="filter.type == types.aliasFilterType.deviceType.value" id="deviceTypeFilter">
+        <tb-entity-subtype-autocomplete
+                tb-required="true"
+                the-form="theForm"
+                ng-model="filter.deviceType"
+                entity-type="types.entityType.device">
+        </tb-entity-subtype-autocomplete>
+        <md-input-container class="md-block">
+            <label translate>device.name-starts-with</label>
+            <input name="deviceNameFilter"
+                   ng-model="filter.deviceNameFilter"
+                   aria-label="{{ 'device.name-starts-with' | translate }}">
+        </md-input-container>
+    </section>
+    <section layout="column" ng-if="filter.type == types.aliasFilterType.relationsQuery.value" id="relationsQueryFilter">
+        <label class="tb-small">{{ 'alias.root-entity' | translate }}</label>
+        <div flex layout="row">
+            <tb-entity-select flex
+                              the-form="theForm"
+                              tb-required="!filter.rootStateEntity"
+                              ng-disabled="filter.rootStateEntity"
+                              ng-model="filter.rootEntity">
+            </tb-entity-select>
+            <section class="tb-root-state-entity-switch" layout="column" layout-align="start center">
+                <label class="tb-small root-state-entity-label" translate>alias.root-state-entity</label>
+                <md-switch class="root-state-entity-switch" ng-model="filter.rootStateEntity"
+                           aria-label="{{ 'alias.root-state-entity' | translate }}">
+                </md-switch>
+            </section>
+        </div>
+        <div flex layout="row">
+            <md-input-container class="md-block" style="min-width: 100px;">
+                <label translate>relation.direction</label>
+                <md-select required ng-model="filter.direction">
+                    <md-option ng-repeat="direction in types.entitySearchDirection" ng-value="direction">
+                        {{ ('relation.search-direction.' + direction) | translate}}
+                    </md-option>
+                </md-select>
+            </md-input-container>
+            <md-input-container flex class="md-block">
+                <label translate>alias.max-relation-level</label>
+                <input name="maxRelationLevel"
+                       type="number"
+                       min="1"
+                       step="1"
+                       placeholder="{{ 'alias.unlimited-level' | translate }}"
+                       ng-model="filter.maxLevel"
+                       aria-label="{{ 'alias.max-relation-level' | translate }}">
+            </md-input-container>
+        </div>
+        <div class="md-caption" style="padding-bottom: 10px; color: rgba(0,0,0,0.57);" translate>relation.relation-filters</div>
+        <tb-relation-filters
+                ng-model="filter.filters"
+                allowed-entity-types="allowedEntityTypes">
+        </tb-relation-filters>
+    </section>
+    <section layout="column" ng-if="filter.type == types.aliasFilterType.assetSearchQuery.value" id="assetSearchQueryFilter">
+        <label class="tb-small">{{ 'alias.root-entity' | translate }}</label>
+        <div flex layout="row">
+            <tb-entity-select flex
+                              the-form="theForm"
+                              tb-required="!filter.rootStateEntity"
+                              ng-disabled="filter.rootStateEntity"
+                              ng-model="filter.rootEntity">
+            </tb-entity-select>
+            <section class="tb-root-state-entity-switch" layout="column" layout-align="start center">
+                <label class="tb-small root-state-entity-label" translate>alias.root-state-entity</label>
+                <md-switch class="root-state-entity-switch" ng-model="filter.rootStateEntity"
+                           aria-label="{{ 'alias.root-state-entity' | translate }}">
+                </md-switch>
+            </section>
+        </div>
+        <div flex layout="row">
+            <md-input-container class="md-block" style="min-width: 100px;">
+                <label translate>relation.direction</label>
+                <md-select required ng-model="filter.direction">
+                    <md-option ng-repeat="direction in types.entitySearchDirection" ng-value="direction">
+                        {{ ('relation.search-direction.' + direction) | translate}}
+                    </md-option>
+                </md-select>
+            </md-input-container>
+            <md-input-container flex class="md-block">
+                <label translate>alias.max-relation-level</label>
+                <input name="maxRelationLevel"
+                       type="number"
+                       min="1"
+                       step="1"
+                       placeholder="{{ 'alias.unlimited-level' | translate }}"
+                       ng-model="filter.maxLevel"
+                       aria-label="{{ 'alias.max-relation-level' | translate }}">
+            </md-input-container>
+        </div>
+        <div class="md-caption" style="color: rgba(0,0,0,0.57);" translate>relation.relation-type</div>
+        <tb-relation-type-autocomplete flex
+                                       hide-label
+                                       the-form="theForm"
+                                       ng-model="filter.relationType"
+                                       tb-required="false">
+        </tb-relation-type-autocomplete>
+        <div class="md-caption tb-required" style="color: rgba(0,0,0,0.57);" translate>asset.asset-types</div>
+        <tb-entity-subtype-list
+                tb-required="true"
+                entity-type="types.entityType.asset"
+                ng-model="filter.assetTypes">
+        </tb-entity-subtype-list>
+    </section>
+    <section layout="column" ng-if="filter.type == types.aliasFilterType.deviceSearchQuery.value" id="deviceSearchQueryFilter">
+        <label class="tb-small">{{ 'alias.root-entity' | translate }}</label>
+        <div flex layout="row">
+            <tb-entity-select flex
+                              the-form="theForm"
+                              tb-required="!filter.rootStateEntity"
+                              ng-disabled="filter.rootStateEntity"
+                              ng-model="filter.rootEntity">
+            </tb-entity-select>
+            <section class="tb-root-state-entity-switch" layout="column" layout-align="start center">
+                <label class="tb-small root-state-entity-label" translate>alias.root-state-entity</label>
+                <md-switch class="root-state-entity-switch" ng-model="filter.rootStateEntity"
+                           aria-label="{{ 'alias.root-state-entity' | translate }}">
+                </md-switch>
+            </section>
+        </div>
+        <div flex layout="row">
+            <md-input-container class="md-block" style="min-width: 100px;">
+                <label translate>relation.direction</label>
+                <md-select required ng-model="filter.direction">
+                    <md-option ng-repeat="direction in types.entitySearchDirection" ng-value="direction">
+                        {{ ('relation.search-direction.' + direction) | translate}}
+                    </md-option>
+                </md-select>
+            </md-input-container>
+            <md-input-container flex class="md-block">
+                <label translate>alias.max-relation-level</label>
+                <input name="maxRelationLevel"
+                       type="number"
+                       min="1"
+                       step="1"
+                       placeholder="{{ 'alias.unlimited-level' | translate }}"
+                       ng-model="filter.maxLevel"
+                       aria-label="{{ 'alias.max-relation-level' | translate }}">
             </md-input-container>
-        </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">
-            </md-switch>
-        </section>
+        </div>
+        <div class="md-caption" style="color: rgba(0,0,0,0.57);" translate>relation.relation-type</div>
+        <tb-relation-type-autocomplete flex
+                                       hide-label
+                                       the-form="theForm"
+                                       ng-model="filter.relationType"
+                                       tb-required="false">
+        </tb-relation-type-autocomplete>
+        <div class="md-caption tb-required" style="color: rgba(0,0,0,0.57);" translate>device.device-types</div>
+        <tb-entity-subtype-list
+                tb-required="true"
+                entity-type="types.entityType.device"
+                ng-model="filter.deviceTypes">
+        </tb-entity-subtype-list>
     </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="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>
-    </div>
-</section>
\ No newline at end of file
+</div>
diff --git a/ui/src/app/entity/entity-filter-view.directive.js b/ui/src/app/entity/entity-filter-view.directive.js
new file mode 100644
index 0000000..66c1b66
--- /dev/null
+++ b/ui/src/app/entity/entity-filter-view.directive.js
@@ -0,0 +1,205 @@
+/*
+ * Copyright © 2016-2017 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* eslint-disable import/no-unresolved, import/default */
+
+import entityFilterViewTemplate from './entity-filter-view.tpl.html';
+
+/* eslint-enable import/no-unresolved, import/default */
+
+import './entity-filter-view.scss';
+
+/*@ngInject*/
+export default function EntityFilterViewDirective($compile, $templateCache, $q, $document, $mdDialog, $translate, types/*, entityService*/) {
+
+    var linker = function (scope, element, attrs, ngModelCtrl) {
+
+        var template = $templateCache.get(entityFilterViewTemplate);
+        element.html(template);
+
+        scope.ngModelCtrl = ngModelCtrl;
+        scope.types = types;
+        scope.filterDisplayValue = '';
+
+        scope.$watch('filter', function () {
+            scope.updateDisplayValue();
+        });
+
+        scope.updateDisplayValue = function() {
+            if (scope.filter && scope.filter.type) {
+                var entityType;
+                var prefix;
+                switch (scope.filter.type) {
+                    case types.aliasFilterType.entityList.value:
+                        entityType = scope.filter.entityType;
+                        var count = scope.filter.entityList.length;
+                        scope.filterDisplayValue = $translate.instant(types.entityTypeTranslations[entityType].list, {count: count}, 'messageformat');
+                        break;
+                    case types.aliasFilterType.entityName.value:
+                        entityType = scope.filter.entityType;
+                        prefix = scope.filter.entityNameFilter;
+                        scope.filterDisplayValue = $translate.instant(types.entityTypeTranslations[entityType].nameStartsWith, {prefix: prefix});
+                        break;
+                    case types.aliasFilterType.stateEntity.value:
+                        scope.filterDisplayValue = $translate.instant('alias.filter-type-state-entity-description');
+                        break;
+                    case types.aliasFilterType.assetType.value:
+                        var assetType = scope.filter.assetType;
+                        prefix = scope.filter.assetNameFilter;
+                        if (prefix && prefix.length) {
+                            scope.filterDisplayValue = $translate.instant('alias.filter-type-asset-type-and-name-description', {assetType: assetType, prefix: prefix});
+                        } else {
+                            scope.filterDisplayValue = $translate.instant('alias.filter-type-asset-type-description', {assetType: assetType});
+                        }
+                        break;
+                    case types.aliasFilterType.deviceType.value:
+                        var deviceType = scope.filter.deviceType;
+                        prefix = scope.filter.deviceNameFilter;
+                        if (prefix && prefix.length) {
+                            scope.filterDisplayValue = $translate.instant('alias.filter-type-device-type-and-name-description', {deviceType: deviceType, prefix: prefix});
+                        } else {
+                            scope.filterDisplayValue = $translate.instant('alias.filter-type-device-type-description', {deviceType: deviceType});
+                        }
+                        break;
+                    case types.aliasFilterType.relationsQuery.value:
+                        var rootEntityText;
+                        var directionText;
+                        var allEntitiesText = $translate.instant('alias.all-entities');
+                        var anyRelationText = $translate.instant('alias.any-relation');
+                        if (scope.filter.rootStateEntity) {
+                            rootEntityText = $translate.instant('alias.state-entity');
+                        } else {
+                            rootEntityText = $translate.instant(types.entityTypeTranslations[scope.filter.rootEntity.entityType].type);
+                        }
+                        directionText = $translate.instant('relation.direction-type.' + scope.filter.direction);
+                        var relationFilters = scope.filter.filters;
+                        if (relationFilters && relationFilters.length) {
+                            var relationFiltersDisplayValues = [];
+                            relationFilters.forEach(function(relationFilter) {
+                                var entitiesText;
+                                if (relationFilter.entityTypes && relationFilter.entityTypes.length) {
+                                    var entitiesNamesList = [];
+                                    relationFilter.entityTypes.forEach(function(entityType) {
+                                        entitiesNamesList.push(
+                                            $translate.instant(types.entityTypeTranslations[entityType].typePlural)
+                                        );
+                                    });
+                                    entitiesText = entitiesNamesList.join(', ');
+                                } else {
+                                    entitiesText = allEntitiesText;
+                                }
+                                var relationTypeText;
+                                if (relationFilter.relationType && relationFilter.relationType.length) {
+                                    relationTypeText = "'" + relationFilter.relationType + "'";
+                                } else {
+                                    relationTypeText = anyRelationText;
+                                }
+                                var relationFilterDisplayValue = $translate.instant('alias.filter-type-relations-query-description',
+                                    {
+                                        entities: entitiesText,
+                                        relationType: relationTypeText,
+                                        direction: directionText,
+                                        rootEntity: rootEntityText
+                                    }
+                                );
+                                relationFiltersDisplayValues.push(relationFilterDisplayValue);
+                            });
+                            scope.filterDisplayValue = relationFiltersDisplayValues.join(', ');
+                        } else {
+                            scope.filterDisplayValue = $translate.instant('alias.filter-type-relations-query-description',
+                                {
+                                    entities: allEntitiesText,
+                                    relationType: anyRelationText,
+                                    direction: directionText,
+                                    rootEntity: rootEntityText
+                                }
+                            );
+                        }
+                        break;
+                    case types.aliasFilterType.assetSearchQuery.value:
+                    case types.aliasFilterType.deviceSearchQuery.value:
+                        allEntitiesText = $translate.instant('alias.all-entities');
+                        anyRelationText = $translate.instant('alias.any-relation');
+                        if (scope.filter.rootStateEntity) {
+                            rootEntityText = $translate.instant('alias.state-entity');
+                        } else {
+                            rootEntityText = $translate.instant(types.entityTypeTranslations[scope.filter.rootEntity.entityType].type);
+                        }
+                        directionText = $translate.instant('relation.direction-type.' + scope.filter.direction);
+                        var relationTypeText;
+                        if (scope.filter.relationType && scope.filter.relationType.length) {
+                            relationTypeText = "'" + scope.filter.relationType + "'";
+                        } else {
+                            relationTypeText = anyRelationText;
+                        }
+
+                        var translationValues = {
+                            relationType: relationTypeText,
+                            direction: directionText,
+                            rootEntity: rootEntityText
+                        }
+
+                        if (scope.filter.type == types.aliasFilterType.assetSearchQuery.value) {
+                            var assetTypesQuoted = [];
+                            scope.filter.assetTypes.forEach(function(assetType) {
+                                assetTypesQuoted.push("'"+assetType+"'");
+                            });
+                            var assetTypesText = assetTypesQuoted.join(', ');
+                            translationValues.assetTypes = assetTypesText;
+                            scope.filterDisplayValue = $translate.instant('alias.filter-type-asset-search-query-description',
+                                translationValues
+                            );
+                        } else {
+                            var deviceTypesQuoted = [];
+                            scope.filter.deviceTypes.forEach(function(deviceType) {
+                                deviceTypesQuoted.push("'"+deviceType+"'");
+                            });
+                            var deviceTypesText = deviceTypesQuoted.join(', ');
+                            translationValues.deviceTypes = deviceTypesText;
+                            scope.filterDisplayValue = $translate.instant('alias.filter-type-device-search-query-description',
+                                translationValues
+                            );
+                        }
+                        break;
+                    default:
+                        scope.filterDisplayValue = scope.filter.type;
+                        break;
+                }
+            } else {
+                scope.filterDisplayValue = '';
+            }
+        }
+
+        ngModelCtrl.$render = function () {
+            if (ngModelCtrl.$viewValue) {
+                scope.filter = ngModelCtrl.$viewValue;
+            } else {
+                scope.filter = null;
+            }
+        }
+
+        $compile(element.contents())(scope);
+
+    }
+
+    return {
+        restrict: "E",
+        require: "^ngModel",
+        link: linker,
+        scope: true
+    };
+
+}
diff --git a/ui/src/app/entity/entity-filter-view.scss b/ui/src/app/entity/entity-filter-view.scss
new file mode 100644
index 0000000..437f296
--- /dev/null
+++ b/ui/src/app/entity/entity-filter-view.scss
@@ -0,0 +1,33 @@
+/**
+ * Copyright © 2016-2017 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+.tb-entity-filter-view {
+  .entity-filter-empty {
+    color: rgba(221, 44, 0, 0.87);
+    font-size: 14px;
+    line-height: 16px;
+  }
+  .entity-filter-type {
+    font-size: 14px;
+    line-height: 16px;
+    color: rgba(0, 0, 0, 0.570588);
+  }
+  .entity-filter-value {
+    font-size: 14px;
+    line-height: 16px;
+    color: rgba(0, 0, 0, 0.570588);
+  }
+}
\ No newline at end of file
diff --git a/ui/src/app/entity/entity-filter-view.tpl.html b/ui/src/app/entity/entity-filter-view.tpl.html
new file mode 100644
index 0000000..84c5405
--- /dev/null
+++ b/ui/src/app/entity/entity-filter-view.tpl.html
@@ -0,0 +1,24 @@
+<!--
+
+    Copyright © 2016-2017 The Thingsboard Authors
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+        http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+
+-->
+
+<div layout='column' class="tb-entity-filter-view">
+    <div ng-if="!filter || !filter.type" class="entity-filter-empty" translate>alias.no-entity-filter-specified</div>
+    <div ng-if="filter && filter.type" layout="column">
+        <div class="entity-filter-value">{{ filterDisplayValue }}</div>
+    </div>
+</div>
diff --git a/ui/src/app/entity/entity-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/entity-subtype-autocomplete.directive.js b/ui/src/app/entity/entity-subtype-autocomplete.directive.js
index 98110b0..76c15d3 100644
--- a/ui/src/app/entity/entity-subtype-autocomplete.directive.js
+++ b/ui/src/app/entity/entity-subtype-autocomplete.directive.js
@@ -114,6 +114,9 @@ export default function EntitySubtypeAutocomplete($compile, $templateCache, $q, 
                 scope.selectEntitySubtypeText = 'asset.select-asset-type';
                 scope.entitySubtypeText = 'asset.asset-type';
                 scope.entitySubtypeRequiredText = 'asset.asset-type-required';
+                scope.$on('assetSaved', function() {
+                    scope.entitySubtypes = null;
+                });
             } else if (scope.entityType == types.entityType.device) {
                 scope.selectEntitySubtypeText = 'device.select-device-type';
                 scope.entitySubtypeText = 'device.device-type';
diff --git a/ui/src/app/entity/entity-subtype-list.directive.js b/ui/src/app/entity/entity-subtype-list.directive.js
new file mode 100644
index 0000000..c7d6329
--- /dev/null
+++ b/ui/src/app/entity/entity-subtype-list.directive.js
@@ -0,0 +1,146 @@
+/*
+ * 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 entitySubtypeListTemplate from './entity-subtype-list.tpl.html';
+
+/* eslint-enable import/no-unresolved, import/default */
+
+import './entity-subtype-list.scss';
+
+/*@ngInject*/
+export default function EntitySubtypeListDirective($compile, $templateCache, $q, $mdUtil, $translate, $filter, types, assetService, deviceService) {
+
+    var linker = function (scope, element, attrs, ngModelCtrl) {
+
+        var template = $templateCache.get(entitySubtypeListTemplate);
+        element.html(template);
+
+        scope.ngModelCtrl = ngModelCtrl;
+
+
+        scope.entitySubtypesList = [];
+        scope.entitySubtypes = null;
+
+        if (scope.entityType == types.entityType.asset) {
+            scope.placeholder = scope.tbRequired ? $translate.instant('asset.enter-asset-type')
+                : $translate.instant('asset.any-asset');
+            scope.secondaryPlaceholder = '+' + $translate.instant('asset.asset-type');
+            scope.noSubtypesMathingText = 'asset.no-asset-types-matching';
+            scope.subtypeListEmptyText = 'asset.asset-type-list-empty';
+        } else if (scope.entityType == types.entityType.device) {
+            scope.placeholder = scope.tbRequired ? $translate.instant('device.enter-device-type')
+                : $translate.instant('device.any-device');
+            scope.secondaryPlaceholder = '+' + $translate.instant('device.device-type');
+            scope.noSubtypesMathingText = 'device.no-device-types-matching';
+            scope.subtypeListEmptyText = 'device.device-type-list-empty';
+        }
+
+        scope.$watch('tbRequired', function () {
+            scope.updateValidity();
+        });
+
+        scope.fetchEntitySubtypes = function(searchText) {
+            var deferred = $q.defer();
+            loadSubTypes().then(
+                function success(subTypes) {
+                    var result = $filter('filter')(subTypes, {'$': searchText});
+                    if (result && result.length) {
+                        deferred.resolve(result);
+                    } else {
+                        deferred.resolve([searchText]);
+                    }
+                },
+                function fail() {
+                    deferred.reject();
+                }
+            );
+            return deferred.promise;
+        }
+
+        scope.updateValidity = function() {
+            var value = ngModelCtrl.$viewValue;
+            var valid = !scope.tbRequired || value && value.length > 0;
+            ngModelCtrl.$setValidity('entitySubtypesList', valid);
+        }
+
+        ngModelCtrl.$render = function () {
+            scope.entitySubtypesList = ngModelCtrl.$viewValue;
+            if (!scope.entitySubtypesList) {
+                scope.entitySubtypesList = [];
+            }
+        }
+
+        scope.$watch('entitySubtypesList', function () {
+            ngModelCtrl.$setViewValue(scope.entitySubtypesList);
+            scope.updateValidity();
+        }, true);
+
+        function loadSubTypes() {
+            var deferred = $q.defer();
+            if (!scope.entitySubtypes) {
+                var entitySubtypesPromise;
+                if (scope.entityType == types.entityType.asset) {
+                    entitySubtypesPromise = assetService.getAssetTypes();
+                } else if (scope.entityType == types.entityType.device) {
+                    entitySubtypesPromise = deviceService.getDeviceTypes();
+                }
+                if (entitySubtypesPromise) {
+                    entitySubtypesPromise.then(
+                        function success(types) {
+                            scope.entitySubtypes = [];
+                            types.forEach(function (type) {
+                                scope.entitySubtypes.push(type.type);
+                            });
+                            deferred.resolve(scope.entitySubtypes);
+                        },
+                        function fail() {
+                            deferred.reject();
+                        }
+                    );
+                } else {
+                    deferred.reject();
+                }
+            } else {
+                deferred.resolve(scope.entitySubtypes);
+            }
+            return deferred.promise;
+        }
+
+        $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-subtype-list.scss b/ui/src/app/entity/entity-subtype-list.scss
new file mode 100644
index 0000000..bbb2a1c
--- /dev/null
+++ b/ui/src/app/entity/entity-subtype-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-subtype-list {
+  #entity_subtype_list_chips {
+    .md-chips {
+      padding-bottom: 1px;
+    }
+  }
+  .tb-error-messages {
+    margin-top: -11px;
+    height: 35px;
+    .tb-error-message {
+      padding-left: 1px;
+    }
+  }
+}*/
diff --git a/ui/src/app/entity/entity-subtype-list.tpl.html b/ui/src/app/entity/entity-subtype-list.tpl.html
new file mode 100644
index 0000000..2a1519a
--- /dev/null
+++ b/ui/src/app/entity/entity-subtype-list.tpl.html
@@ -0,0 +1,54 @@
+<!--
+
+    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-subtype-list">
+    <md-chips flex
+              readonly="disabled"
+              id="entity_subtype_list_chips"
+              ng-required="tbRequired"
+              ng-model="entitySubtypesList"
+              placeholder="{{placeholder}}"
+              secondary-placeholder="{{secondaryPlaceholder}}"
+              md-autocomplete-snap
+              md-require-match="false">
+        <md-autocomplete
+                md-no-cache="true"
+                id="entitySubtype"
+                md-selected-item="selectedEntitySubtype"
+                md-search-text="entitySubtypeSearchText"
+                md-items="item in fetchEntitySubtypes(entitySubtypeSearchText)"
+                md-item-text="item"
+                md-min-length="0"
+                placeholder="{{ (!entitySubtypesList || !entitySubtypesList.length) ? placeholder : secondaryPlaceholder }}">
+            <md-item-template>
+                <span md-highlight-text="entitySubtypeSearchText" md-highlight-flags="^i">{{item}}</span>
+            </md-item-template>
+            <md-not-found>
+                <span translate translate-values='{ entitySubtype: entitySubtypeSearchText }'>{{noSubtypesMathingText}}</span>
+            </md-not-found>
+        </md-autocomplete>
+        <md-chip-template>
+            <span>
+                <strong>{{$chip}}</strong>
+            </span>
+        </md-chip-template>
+    </md-chips>
+    <div class="tb-error-messages" ng-messages="ngModelCtrl.$error" ng-if="inputTouched && tbRequired" role="alert">
+        <div translate ng-message="entitySubtypesList" class="tb-error-message">{{subtypeListEmptyText}}</div>
+    </div>
+</section>
diff --git a/ui/src/app/entity/entity-type-list.directive.js b/ui/src/app/entity/entity-type-list.directive.js
new file mode 100644
index 0000000..cb07cfc
--- /dev/null
+++ b/ui/src/app/entity/entity-type-list.directive.js
@@ -0,0 +1,111 @@
+/*
+ * 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 entityTypeListTemplate from './entity-type-list.tpl.html';
+
+/* eslint-enable import/no-unresolved, import/default */
+
+import './entity-type-list.scss';
+
+/*@ngInject*/
+export default function EntityTypeListDirective($compile, $templateCache, $q, $mdUtil, $translate, $filter, types, entityService) {
+
+    var linker = function (scope, element, attrs, ngModelCtrl) {
+
+        var template = $templateCache.get(entityTypeListTemplate);
+        element.html(template);
+
+        scope.ngModelCtrl = ngModelCtrl;
+
+        scope.placeholder = scope.tbRequired ? $translate.instant('entity.enter-entity-type')
+                                : $translate.instant('entity.any-entity');
+        scope.secondaryPlaceholder = '+' + $translate.instant('entity.entity-type');
+
+        var entityTypes = entityService.prepareAllowedEntityTypesList(scope.allowedEntityTypes);
+        scope.entityTypesList = [];
+        for (var type in entityTypes) {
+            var entityTypeInfo = {};
+            entityTypeInfo.value = entityTypes[type];
+            entityTypeInfo.name = $translate.instant(types.entityTypeTranslations[entityTypeInfo.value].type) + '';
+            scope.entityTypesList.push(entityTypeInfo);
+        }
+
+        scope.$watch('tbRequired', function () {
+            scope.updateValidity();
+        });
+
+        scope.fetchEntityTypes = function(searchText) {
+            var deferred = $q.defer();
+            var entityTypes = $filter('filter')(scope.entityTypesList, {name: searchText});
+            deferred.resolve(entityTypes);
+            return deferred.promise;
+        }
+
+        scope.updateValidity = function() {
+            var value = ngModelCtrl.$viewValue;
+            var valid = !scope.tbRequired || value && value.length > 0;
+            ngModelCtrl.$setValidity('entityTypeList', valid);
+        }
+
+        ngModelCtrl.$render = function () {
+            scope.entityTypeList = [];
+            var value = ngModelCtrl.$viewValue;
+            if (value && value.length) {
+                value.forEach(function(type) {
+                    var entityTypeInfo = {};
+                    entityTypeInfo.value = type;
+                    entityTypeInfo.name = $translate.instant(types.entityTypeTranslations[entityTypeInfo.value].type) + '';
+                    scope.entityTypeList.push(entityTypeInfo);
+                });
+            }
+        }
+
+        scope.$watch('entityTypeList', function () {
+            var values = [];
+            if (scope.entityTypeList && scope.entityTypeList.length) {
+                scope.entityTypeList.forEach(function(entityType) {
+                    values.push(entityType.value);
+                });
+            }
+            ngModelCtrl.$setViewValue(values);
+            scope.updateValidity();
+        }, true);
+
+        $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: '=?',
+            allowedEntityTypes: '=?'
+        }
+    };
+
+}
diff --git a/ui/src/app/entity/entity-type-list.scss b/ui/src/app/entity/entity-type-list.scss
new file mode 100644
index 0000000..b94b992
--- /dev/null
+++ b/ui/src/app/entity/entity-type-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-type-list {
+  #entity_type_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-type-list.tpl.html b/ui/src/app/entity/entity-type-list.tpl.html
new file mode 100644
index 0000000..ff10a30
--- /dev/null
+++ b/ui/src/app/entity/entity-type-list.tpl.html
@@ -0,0 +1,54 @@
+<!--
+
+    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-type-list">
+    <md-chips flex
+              readonly="disabled"
+              id="entity_type_list_chips"
+              ng-required="tbRequired"
+              ng-model="entityTypeList"
+              placeholder="{{placeholder}}"
+              secondary-placeholder="{{secondaryPlaceholder}}"
+              md-autocomplete-snap
+              md-require-match="true">
+        <md-autocomplete
+                md-no-cache="true"
+                id="entityType"
+                md-selected-item="selectedEntityType"
+                md-search-text="entityTypeSearchText"
+                md-items="item in fetchEntityTypes(entityTypeSearchText)"
+                md-item-text="item.name"
+                md-min-length="0"
+                placeholder="{{ (!entityTypeList || !entityTypeList.length) ? placeholder : secondaryPlaceholder }}">
+            <md-item-template>
+                <span md-highlight-text="entityTypeSearchText" md-highlight-flags="^i">{{item.name}}</span>
+            </md-item-template>
+            <md-not-found>
+                <span translate translate-values='{ entityType: entityTypeSearchText }'>entity.no-entity-types-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 && tbRequired" role="alert">
+        <div translate ng-message="entityTypeList" class="tb-error-message">entity.entity-type-list-empty</div>
+    </div>
+</section>
diff --git a/ui/src/app/entity/entity-type-select.directive.js b/ui/src/app/entity/entity-type-select.directive.js
index 1dc6741..068c141 100644
--- a/ui/src/app/entity/entity-type-select.directive.js
+++ b/ui/src/app/entity/entity-type-select.directive.js
@@ -23,7 +23,7 @@ import entityTypeSelectTemplate from './entity-type-select.tpl.html';
 /* eslint-enable import/no-unresolved, import/default */
 
 /*@ngInject*/
-export default function EntityTypeSelect($compile, $templateCache, utils, userService, types) {
+export default function EntityTypeSelect($compile, $templateCache, utils, entityService, userService, types) {
 
     var linker = function (scope, element, attrs, ngModelCtrl) {
         var template = $templateCache.get(entityTypeSelectTemplate);
@@ -39,39 +39,10 @@ export default function EntityTypeSelect($compile, $templateCache, utils, userSe
 
         scope.ngModelCtrl = ngModelCtrl;
 
-        var authority = userService.getAuthority();
-        scope.entityTypes = {};
-        switch(authority) {
-            case 'SYS_ADMIN':
-                scope.entityTypes.tenant = types.entityType.tenant;
-                scope.entityTypes.rule = types.entityType.rule;
-                scope.entityTypes.plugin = types.entityType.plugin;
-                break;
-            case 'TENANT_ADMIN':
-                scope.entityTypes.device = types.entityType.device;
-                scope.entityTypes.asset = types.entityType.asset;
-                scope.entityTypes.customer = types.entityType.customer;
-                scope.entityTypes.rule = types.entityType.rule;
-                scope.entityTypes.plugin = types.entityType.plugin;
-                scope.entityTypes.dashboard = types.entityType.dashboard;
-                break;
-            case 'CUSTOMER_USER':
-                scope.entityTypes.device = types.entityType.device;
-                scope.entityTypes.asset = types.entityType.asset;
-                scope.entityTypes.dashboard = types.entityType.dashboard;
-                break;
-        }
-
-        if (scope.allowedEntityTypes) {
-            for (var entityType in scope.entityTypes) {
-                if (scope.allowedEntityTypes.indexOf(scope.entityTypes[entityType]) === -1) {
-                    delete scope.entityTypes[entityType];
-                }
-            }
-        }
+        scope.entityTypes = entityService.prepareAllowedEntityTypesList(scope.allowedEntityTypes);
 
         scope.typeName = function(type) {
-            return utils.entityTypeName(type);
+            return type ? types.entityTypeTranslations[type].type : '';
         }
 
         scope.updateValidity = function () {
diff --git a/ui/src/app/entity/index.js b/ui/src/app/entity/index.js
index e8cc437..2b8d434 100644
--- a/ui/src/app/entity/index.js
+++ b/ui/src/app/entity/index.js
@@ -14,34 +14,46 @@
  * limitations under the License.
  */
 
-import EntityAliasesController from './entity-aliases.controller';
+import EntityAliasesController from './alias/entity-aliases.controller';
+import EntityAliasDialogController from './alias/entity-alias-dialog.controller';
 import EntityTypeSelectDirective from './entity-type-select.directive';
+import EntityTypeListDirective from './entity-type-list.directive';
+import EntitySubtypeListDirective from './entity-subtype-list.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';
-import AliasesEntitySelectDirective from './aliases-entity-select.directive';
+import EntityFilterViewDirective from './entity-filter-view.directive';
+import AliasesEntitySelectPanelController from './alias/aliases-entity-select-panel.controller';
+import AliasesEntitySelectDirective from './alias/aliases-entity-select.directive';
 import AddAttributeDialogController from './attribute/add-attribute-dialog.controller';
 import AddWidgetToDashboardDialogController from './attribute/add-widget-to-dashboard-dialog.controller';
 import AttributeTableDirective from './attribute/attribute-table.directive';
+import RelationFiltersDirective from './relation/relation-filters.directive';
 import RelationTableDirective from './relation/relation-table.directive';
 import RelationTypeAutocompleteDirective from './relation/relation-type-autocomplete.directive';
 
 export default angular.module('thingsboard.entity', [])
     .controller('EntityAliasesController', EntityAliasesController)
+    .controller('EntityAliasDialogController', EntityAliasDialogController)
     .controller('AliasesEntitySelectPanelController', AliasesEntitySelectPanelController)
     .controller('AddAttributeDialogController', AddAttributeDialogController)
     .controller('AddWidgetToDashboardDialogController', AddWidgetToDashboardDialogController)
     .directive('tbEntityTypeSelect', EntityTypeSelectDirective)
+    .directive('tbEntityTypeList', EntityTypeListDirective)
+    .directive('tbEntitySubtypeList', EntitySubtypeListDirective)
     .directive('tbEntitySubtypeSelect', EntitySubtypeSelectDirective)
     .directive('tbEntitySubtypeAutocomplete', EntitySubtypeAutocompleteDirective)
     .directive('tbEntityAutocomplete', EntityAutocompleteDirective)
+    .directive('tbEntityList', EntityListDirective)
     .directive('tbEntitySelect', EntitySelectDirective)
     .directive('tbEntityFilter', EntityFilterDirective)
+    .directive('tbEntityFilterView', EntityFilterViewDirective)
     .directive('tbAliasesEntitySelect', AliasesEntitySelectDirective)
     .directive('tbAttributeTable', AttributeTableDirective)
+    .directive('tbRelationFilters', RelationFiltersDirective)
     .directive('tbRelationTable', RelationTableDirective)
     .directive('tbRelationTypeAutocomplete', RelationTypeAutocompleteDirective)
     .name;
diff --git a/ui/src/app/entity/relation/relation-filters.directive.js b/ui/src/app/entity/relation/relation-filters.directive.js
new file mode 100644
index 0000000..59821dd
--- /dev/null
+++ b/ui/src/app/entity/relation/relation-filters.directive.js
@@ -0,0 +1,85 @@
+/*
+ * 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.
+ */
+
+import './relation-filters.scss';
+
+/* eslint-disable import/no-unresolved, import/default */
+
+import relationFiltersTemplate from './relation-filters.tpl.html';
+
+/* eslint-enable import/no-unresolved, import/default */
+
+/*@ngInject*/
+export default function RelationFilters($compile, $templateCache) {
+
+    return {
+        restrict: "E",
+        require: "^ngModel",
+        scope: {
+            allowedEntityTypes: '=?'
+        },
+        link: linker
+    };
+
+    function linker( scope, element, attrs, ngModelCtrl ) {
+
+        var template = $templateCache.get(relationFiltersTemplate);
+        element.html(template);
+
+        scope.relationFilters = [];
+
+        scope.addFilter = addFilter;
+        scope.removeFilter = removeFilter;
+
+        ngModelCtrl.$render = function () {
+            if (ngModelCtrl.$viewValue) {
+                var value = ngModelCtrl.$viewValue;
+                value.forEach(function (filter) {
+                    scope.relationFilters.push(filter);
+                });
+            }
+            scope.$watch('relationFilters', function (newVal, prevVal) {
+                if (!angular.equals(newVal, prevVal)) {
+                    updateValue();
+                }
+            }, true);
+        }
+
+        function addFilter() {
+            var filter = {
+                relationType: null,
+                entityTypes: []
+            };
+            scope.relationFilters.push(filter);
+        }
+
+        function removeFilter($event, filter) {
+            var index = scope.relationFilters.indexOf(filter);
+            if (index > -1) {
+                scope.relationFilters.splice(index, 1);
+            }
+        }
+
+        function updateValue() {
+            var value = [];
+            scope.relationFilters.forEach(function (filter) {
+                value.push(filter);
+            });
+            ngModelCtrl.$setViewValue(value);
+        }
+        $compile(element.contents())(scope);
+    }
+}
diff --git a/ui/src/app/entity/relation/relation-filters.scss b/ui/src/app/entity/relation/relation-filters.scss
new file mode 100644
index 0000000..50d49af
--- /dev/null
+++ b/ui/src/app/entity/relation/relation-filters.scss
@@ -0,0 +1,77 @@
+/**
+ * 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-relation-filters {
+  .header {
+    padding-left: 5px;
+    padding-right: 5px;
+    padding-bottom: 5px;
+    .cell {
+      padding-left: 5px;
+      padding-right: 5px;
+      color: rgba(0,0,0,.54);
+      font-size: 12px;
+      font-weight: 700;
+      white-space: nowrap;
+    }
+  }
+  .body {
+    padding-left: 5px;
+    padding-right: 5px;
+    max-height: 300px;
+    overflow: auto;
+    padding-bottom: 20px;
+    .row {
+      padding-top: 5px;
+    }
+    .cell {
+      padding-left: 5px;
+      padding-right: 5px;
+
+      md-select {
+        margin: 0 0 24px;
+      }
+
+      md-input-container {
+        margin: 0;
+      }
+
+      md-chips-wrap {
+        padding: 0px;
+        margin: 0 0 24px;
+        .md-chip-input-container {
+          margin: 0;
+        }
+        md-autocomplete {
+          height: 30px;
+          md-autocomplete-wrap {
+            height: 30px;
+          }
+        }
+      }
+      .md-chips .md-chip-input-container input {
+        padding: 2px 2px 2px;
+        height: 26px;
+        line-height: 26px;
+      }
+
+    }
+
+    .md-button {
+      margin: 0;
+    }
+  }
+}
\ No newline at end of file
diff --git a/ui/src/app/entity/relation/relation-filters.tpl.html b/ui/src/app/entity/relation/relation-filters.tpl.html
new file mode 100644
index 0000000..679a488
--- /dev/null
+++ b/ui/src/app/entity/relation/relation-filters.tpl.html
@@ -0,0 +1,67 @@
+<!--
+
+    Copyright © 2016-2017 The Thingsboard Authors
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+        http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+
+-->
+<div class="tb-relation-filters">
+    <div class="header" ng-show="relationFilters.length">
+        <div layout="row" layout-align="start center">
+            <span class="cell" style="width: 200px; min-width: 200px;" translate>relation.type</span>
+            <span class="cell" flex translate>entity.entity-types</span>
+            <span class="cell" style="width: 40px; min-width: 40px;">&nbsp</span>
+        </div>
+    </div>
+    <div class="body" ng-show="relationFilters.length">
+        <div class="row" ng-form name="relationFilterForm" flex layout="row" layout-align="start center" ng-repeat="filter in relationFilters track by $index">
+            <div class="md-whiteframe-1dp" flex layout="row" layout-align="start center">
+                <tb-relation-type-autocomplete class="cell" style="width: 200px; min-width: 200px;"
+                                               hide-label
+                                               the-form="relationFilterForm"
+                                               ng-model="filter.relationType"
+                                               tb-required="false">
+                </tb-relation-type-autocomplete>
+                <tb-entity-type-list class="cell" flex
+                        ng-model="filter.entityTypes"
+                        allowed-entity-types="allowedEntityTypes"
+                        tb-required="false">
+                </tb-entity-type-list>
+                <md-button ng-disabled="loading" class="md-icon-button md-primary" style="width: 40px; min-width: 40px;"
+                           ng-click="removeFilter($event, filter)" aria-label="{{ 'action.remove' | translate }}">
+                    <md-tooltip md-direction="top">
+                        {{ 'relation.remove-relation-filter' | translate }}
+                    </md-tooltip>
+                    <md-icon aria-label="{{ 'action.remove' | translate }}" class="material-icons">
+                        close
+                    </md-icon>
+                </md-button>
+            </div>
+        </div>
+    </div>
+    <div class="any-filter" ng-show="!relationFilters.length">
+        <span layout-align="center center"
+              class="tb-prompt" translate>relation.any-relation</span>
+    </div>
+    <div>
+        <md-button ng-disabled="loading" class="md-primary md-raised" ng-click="addFilter($event)" aria-label="{{ 'action.add' | translate }}">
+            <md-tooltip md-direction="top">
+                {{ 'relation.add-relation-filter' | translate }}
+            </md-tooltip>
+            <md-icon aria-label="{{ 'action.add' | translate }}" class="material-icons">
+                add
+            </md-icon>
+            {{ 'action.add' | translate }}
+        </md-button>
+    </div>
+</div>
\ No newline at end of file
diff --git a/ui/src/app/entity/relation/relation-table.directive.js b/ui/src/app/entity/relation/relation-table.directive.js
index 729ff36..f0c192b 100644
--- a/ui/src/app/entity/relation/relation-table.directive.js
+++ b/ui/src/app/entity/relation/relation-table.directive.js
@@ -218,9 +218,9 @@ function RelationTableController($scope, $q, $mdDialog, $document, $translate, $
             function success(allRelations) {
                 allRelations.forEach(function(relation) {
                     if (vm.direction == vm.types.entitySearchDirection.from) {
-                        relation.toEntityTypeName = $translate.instant(utils.entityTypeName(relation.to.entityType));
+                        relation.toEntityTypeName = $translate.instant(types.entityTypeTranslations[relation.to.entityType].type);
                     } else {
-                        relation.fromEntityTypeName = $translate.instant(utils.entityTypeName(relation.from.entityType));
+                        relation.fromEntityTypeName = $translate.instant(types.entityTypeTranslations[relation.from.entityType].type);
                     }
                 });
                 vm.allRelations = allRelations;
diff --git a/ui/src/app/entity/relation/relation-type-autocomplete.directive.js b/ui/src/app/entity/relation/relation-type-autocomplete.directive.js
index a7e5ea4..4b5480d 100644
--- a/ui/src/app/entity/relation/relation-type-autocomplete.directive.js
+++ b/ui/src/app/entity/relation/relation-type-autocomplete.directive.js
@@ -29,6 +29,8 @@ export default function RelationTypeAutocomplete($compile, $templateCache, $q, $
         element.html(template);
 
         scope.tbRequired = angular.isDefined(scope.tbRequired) ? scope.tbRequired : false;
+        scope.hideLabel = angular.isDefined(attrs.hideLabel) ? true : false;
+
         scope.relationType = null;
         scope.relationTypeSearchText = '';
         scope.relationTypes = [];
diff --git a/ui/src/app/entity/relation/relation-type-autocomplete.tpl.html b/ui/src/app/entity/relation/relation-type-autocomplete.tpl.html
index f71c134..39f32d6 100644
--- a/ui/src/app/entity/relation/relation-type-autocomplete.tpl.html
+++ b/ui/src/app/entity/relation/relation-type-autocomplete.tpl.html
@@ -26,7 +26,7 @@
                  md-items="item in fetchRelationTypes(relationTypeSearchText)"
                  md-item-text="item"
                  md-min-length="0"
-                 md-floating-label="{{ 'relation.relation-type' | translate }}"
+                 md-floating-label="{{ tbRequired  ? ('relation.relation-type' | translate) : ( !relationType ? ('relation.any-relation-type' | translate) : ' ') }}"
                  md-select-on-match="true"
                  md-menu-class="tb-relation-type-autocomplete">
     <md-item-template>
diff --git a/ui/src/app/event/event.scss b/ui/src/app/event/event.scss
index 6fa3e67..8622b0d 100644
--- a/ui/src/app/event/event.scss
+++ b/ui/src/app/event/event.scss
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-md-list.tb-table {
+md-list.tb-event-table {
     padding: 0px;
 
     md-list-item {
@@ -64,7 +64,7 @@ md-list.tb-table {
 
 }
 
-#tb-content {
+#tb-event-content {
   min-width: 400px;
   min-height: 50px;
   width: 100%;
diff --git a/ui/src/app/event/event-content-dialog.controller.js b/ui/src/app/event/event-content-dialog.controller.js
index 19ddb8c..235cfcf 100644
--- a/ui/src/app/event/event-content-dialog.controller.js
+++ b/ui/src/app/event/event-content-dialog.controller.js
@@ -62,7 +62,7 @@ export default function EventContentDialogController($mdDialog, content, title, 
             }
             newWidth = 8 * maxLineLength + 16;
         }
-        $('#tb-content', element).height(newHeight.toString() + "px")
+        $('#tb-event-content', element).height(newHeight.toString() + "px")
             .width(newWidth.toString() + "px");
         vm.editor.resize();
     }
diff --git a/ui/src/app/event/event-content-dialog.tpl.html b/ui/src/app/event/event-content-dialog.tpl.html
index 4cf046b..7b4184c 100644
--- a/ui/src/app/event/event-content-dialog.tpl.html
+++ b/ui/src/app/event/event-content-dialog.tpl.html
@@ -27,7 +27,7 @@
         </md-toolbar>
         <md-dialog-content>
             <div class="md-dialog-content">
-                <div flex id="tb-content" readonly
+                <div flex id="tb-event-content" readonly
                      ui-ace="vm.contentOptions"
                      ng-model="vm.content">
                 </div>
diff --git a/ui/src/app/event/event-header.directive.js b/ui/src/app/event/event-header.directive.js
index 5e8cc7c..c43894e 100644
--- a/ui/src/app/event/event-header.directive.js
+++ b/ui/src/app/event/event-header.directive.js
@@ -18,7 +18,6 @@
 import eventHeaderLcEventTemplate from './event-header-lc-event.tpl.html';
 import eventHeaderStatsTemplate from './event-header-stats.tpl.html';
 import eventHeaderErrorTemplate from './event-header-error.tpl.html';
-import eventHeaderAlarmTemplate from './event-header-alarm.tpl.html';
 
 /* eslint-enable import/no-unresolved, import/default */
 
@@ -39,9 +38,6 @@ export default function EventHeaderDirective($compile, $templateCache, types) {
                 case types.eventType.error.value:
                     template = eventHeaderErrorTemplate;
                     break;
-                case types.eventType.alarm.value:
-                    template = eventHeaderAlarmTemplate;
-                    break;
             }
             return $templateCache.get(template);
         }
diff --git a/ui/src/app/event/event-row.directive.js b/ui/src/app/event/event-row.directive.js
index 9bff192..d1feb60 100644
--- a/ui/src/app/event/event-row.directive.js
+++ b/ui/src/app/event/event-row.directive.js
@@ -20,7 +20,6 @@ import eventErrorDialogTemplate from './event-content-dialog.tpl.html';
 import eventRowLcEventTemplate from './event-row-lc-event.tpl.html';
 import eventRowStatsTemplate from './event-row-stats.tpl.html';
 import eventRowErrorTemplate from './event-row-error.tpl.html';
-import eventRowAlarmTemplate from './event-row-alarm.tpl.html';
 
 /* eslint-enable import/no-unresolved, import/default */
 
@@ -41,9 +40,6 @@ export default function EventRowDirective($compile, $templateCache, $mdDialog, $
                 case types.eventType.error.value:
                     template = eventRowErrorTemplate;
                     break;
-                case types.eventType.alarm.value:
-                    template = eventRowAlarmTemplate;
-                    break;
             }
             return $templateCache.get(template);
         }
diff --git a/ui/src/app/event/event-table.tpl.html b/ui/src/app/event/event-table.tpl.html
index f44aeb3..82a921f 100644
--- a/ui/src/app/event/event-table.tpl.html
+++ b/ui/src/app/event/event-table.tpl.html
@@ -27,7 +27,7 @@
         </md-input-container>
         <tb-timewindow flex ng-model="timewindow" history-only as-button="true"></tb-timewindow>
     </section>
-    <md-list flex layout="column" class="md-whiteframe-z1 tb-table">
+    <md-list flex layout="column" class="md-whiteframe-z1 tb-event-table">
            <md-list class="tb-row tb-header" layout="row" tb-event-header event-type="{{eventType}}">
            </md-list>
         <md-progress-linear style="max-height: 0px;" md-mode="indeterminate" ng-disabled="!loading()"
diff --git a/ui/src/app/import-export/import-export.service.js b/ui/src/app/import-export/import-export.service.js
index af04687..86d5240 100644
--- a/ui/src/app/import-export/import-export.service.js
+++ b/ui/src/app/import-export/import-export.service.js
@@ -16,7 +16,7 @@
 /* eslint-disable import/no-unresolved, import/default */
 
 import importDialogTemplate from './import-dialog.tpl.html';
-import entityAliasesTemplate from '../entity/entity-aliases.tpl.html';
+import entityAliasesTemplate from '../entity/alias/entity-aliases.tpl.html';
 
 /* eslint-enable import/no-unresolved, import/default */
 
@@ -24,7 +24,7 @@ import entityAliasesTemplate from '../entity/entity-aliases.tpl.html';
 /* eslint-disable no-undef, angular/window-service, angular/document-service */
 
 /*@ngInject*/
-export default function ImportExport($log, $translate, $q, $mdDialog, $document, itembuffer, types, dashboardUtils,
+export default function ImportExport($log, $translate, $q, $mdDialog, $document, itembuffer, utils, types, dashboardUtils,
                                      entityService, dashboardService, pluginService, ruleService, widgetService, toast) {
 
 
@@ -359,30 +359,47 @@ export default function ImportExport($log, $translate, $q, $mdDialog, $document,
     }
 
     function prepareEntityAlias(aliasInfo) {
-        var entityFilter;
-        var entityType;
+        var alias;
+        var filter;
         if (aliasInfo.deviceId) {
-            entityFilter = {
-                useFilter: false,
-                entityNameFilter: '',
-                entityList: [aliasInfo.deviceId]
-            }
-            entityType = types.entityType.device;
+            alias = aliasInfo.aliasName;
+            filter = {
+                type: types.aliasFilterType.entityList.value,
+                entityType: types.entityType.device,
+                entityList: [aliasInfo.deviceId],
+                resolveMultiple: false
+            };
         } else if (aliasInfo.deviceFilter) {
-            entityFilter = {
-                useFilter: aliasInfo.deviceFilter.useFilter,
-                entityNameFilter: aliasInfo.deviceFilter.deviceNameFilter,
-                entityList: aliasInfo.deviceFilter.deviceList
+            alias = aliasInfo.aliasName;
+            filter = {
+                type: aliasInfo.deviceFilter.useFilter ? types.aliasFilterType.entityName.value : types.aliasFilterType.entityList.value,
+                entityType: types.entityType.device,
+                resolveMultiple: false
+            }
+            if (filter.type == types.aliasFilterType.entityList.value) {
+                filter.entityList = aliasInfo.deviceFilter.deviceList
+            } else {
+                filter.entityNameFilter = aliasInfo.deviceFilter.deviceNameFilter;
+            }
+        } else if (aliasInfo.entityFilter) {
+            alias = aliasInfo.aliasName;
+            filter = {
+                type: aliasInfo.entityFilter.useFilter ? types.aliasFilterType.entityName.value : types.aliasFilterType.entityList.value,
+                entityType: aliasInfo.entityType,
+                resolveMultiple: false
+            }
+            if (filter.type == types.aliasFilterType.entityList.value) {
+                filter.entityList = aliasInfo.entityFilter.entityList;
+            } else {
+                filter.entityNameFilter = aliasInfo.entityFilter.entityNameFilter;
             }
-            entityType = types.entityType.device;
         } else {
-            entityFilter = aliasInfo.entityFilter;
-            entityType = aliasInfo.entityType;
+            alias = aliasInfo.alias;
+            filter = aliasInfo.filter;
         }
         return {
-            aliasName: aliasInfo.aliasName,
-            entityType: entityType,
-            entityFilter: entityFilter
+            alias: alias,
+            filter: filter
         };
     }
 
@@ -395,6 +412,7 @@ export default function ImportExport($log, $translate, $q, $mdDialog, $document,
                     deferred.reject();
                 } else {
                     var widget = widgetItem.widget;
+                    widget = dashboardUtils.validateAndUpdateWidget(widget);
                     var aliasesInfo = prepareAliasesInfo(widgetItem.aliasesInfo);
                     var originalColumns = widgetItem.originalColumns;
                     var originalSize = widgetItem.originalSize;
@@ -405,20 +423,22 @@ export default function ImportExport($log, $translate, $q, $mdDialog, $document,
                         var entityAliases = {};
                         var datasourceAliasesMap = {};
                         var targetDeviceAliasesMap = {};
-                        var aliasId = 1;
+                        var aliasId;
                         var datasourceIndex;
                         if (datasourceAliases) {
                             for (datasourceIndex in datasourceAliases) {
+                                aliasId = utils.guid();
                                 datasourceAliasesMap[aliasId] = datasourceIndex;
                                 entityAliases[aliasId] = datasourceAliases[datasourceIndex];
-                                aliasId++;
+                                entityAliases[aliasId].id = aliasId;
                             }
                         }
                         if (targetDeviceAliases) {
                             for (datasourceIndex in targetDeviceAliases) {
+                                aliasId = utils.guid();
                                 targetDeviceAliasesMap[aliasId] = datasourceIndex;
                                 entityAliases[aliasId] = targetDeviceAliases[datasourceIndex];
-                                aliasId++;
+                                entityAliases[aliasId].id = aliasId;
                             }
                         }
 
@@ -435,12 +455,10 @@ export default function ImportExport($log, $translate, $q, $mdDialog, $document,
                                                     var datasourceIndex;
                                                     if (datasourceAliasesMap[aliasId]) {
                                                         datasourceIndex = datasourceAliasesMap[aliasId];
-                                                        datasourceAliases[datasourceIndex].entityType = entityAlias.entityType;
-                                                        datasourceAliases[datasourceIndex].entityFilter = entityAlias.entityFilter;
+                                                        datasourceAliases[datasourceIndex] = entityAlias;
                                                     } else if (targetDeviceAliasesMap[aliasId]) {
                                                         datasourceIndex = targetDeviceAliasesMap[aliasId];
-                                                        targetDeviceAliases[datasourceIndex].entityType = entityAlias.entityType;
-                                                        targetDeviceAliases[datasourceIndex].entityFilter = entityAlias.entityFilter;
+                                                        targetDeviceAliases[datasourceIndex] = entityAlias;
                                                     }
                                                 }
                                                 addImportedWidget(dashboard, targetState, targetLayoutFunction, $event, widget,
@@ -622,7 +640,7 @@ export default function ImportExport($log, $translate, $q, $mdDialog, $document,
                     checkNextEntityAliasOrComplete(index, aliasIds, entityAliases, missingEntityAliases, deferred);
                 } else {
                     var missingEntityAlias = angular.copy(entityAlias);
-                    missingEntityAlias.entityFilter = null;
+                    missingEntityAlias.filter = null;
                     missingEntityAliases[aliasId] = missingEntityAlias;
                     checkNextEntityAliasOrComplete(index, aliasIds, entityAliases, missingEntityAliases, deferred);
                 }
@@ -641,8 +659,6 @@ export default function ImportExport($log, $translate, $q, $mdDialog, $document,
                     entityAliases: missingEntityAliases,
                     widgets: widgets,
                     isSingleWidget: isSingleWidget,
-                    isSingleEntityAlias: false,
-                    singleEntityAlias: null,
                     customTitle: customTitle,
                     disableAdd: true
                 }
diff --git a/ui/src/app/layout/index.js b/ui/src/app/layout/index.js
index be63558..2a27c93 100644
--- a/ui/src/app/layout/index.js
+++ b/ui/src/app/layout/index.js
@@ -32,6 +32,8 @@ import thingsboardDashboardAutocomplete from '../components/dashboard-autocomple
 import thingsboardUserMenu from './user-menu.directive';
 
 import thingsboardEntity from '../entity';
+import thingsboardEvent from '../event';
+import thingsboardAlarm from '../alarm';
 import thingsboardTenant from '../tenant';
 import thingsboardCustomer from '../customer';
 import thingsboardUser from '../user';
@@ -61,6 +63,8 @@ export default angular.module('thingsboard.home', [
     thingsboardHomeLinks,
     thingsboardUserMenu,
     thingsboardEntity,
+    thingsboardEvent,
+    thingsboardAlarm,
     thingsboardTenant,
     thingsboardCustomer,
     thingsboardUser,
diff --git a/ui/src/app/locale/locale.constant.js b/ui/src/app/locale/locale.constant.js
index 6bc6e7e..a54eba4 100644
--- a/ui/src/app/locale/locale.constant.js
+++ b/ui/src/app/locale/locale.constant.js
@@ -108,9 +108,79 @@ export default angular.module('thingsboard.locale', [])
                 },
                 "alarm": {
                     "alarm": "Alarm",
+                    "alarms": "Alarms",
                     "select-alarm": "Select alarm",
                     "no-alarms-matching": "No alarms matching '{{entity}}' were found.",
-                    "alarm-required": "Alarm is required"
+                    "alarm-required": "Alarm is required",
+                    "alarm-status": "Alarm status",
+                    "search-status": {
+                        "ANY": "Any",
+                        "ACTIVE": "Active",
+                        "CLEARED": "Cleared",
+                        "ACK": "Acknowledged",
+                        "UNACK": "Unacknowledged"
+                    },
+                    "display-status": {
+                        "ACTIVE_UNACK": "Active Unacknowledged",
+                        "ACTIVE_ACK": "Active Acknowledged",
+                        "CLEARED_UNACK": "Cleared Unacknowledged",
+                        "CLEARED_ACK": "Cleared Acknowledged"
+                    },
+                    "no-alarms-prompt": "No alarms found",
+                    "created-time": "Created time",
+                    "type": "Type",
+                    "severity": "Severity",
+                    "originator": "Originator",
+                    "details": "Details",
+                    "status": "Status",
+                    "alarm-details": "Alarm details",
+                    "start-time": "Start time",
+                    "end-time": "End time",
+                    "ack-time": "Acknowledged time",
+                    "clear-time": "Cleared time",
+                    "severity-critical": "Critical",
+                    "severity-major": "Major",
+                    "severity-minor": "Minor",
+                    "severity-warning": "Warning",
+                    "severity-indeterminate": "Indeterminate",
+                    "acknowledge": "Acknowledge",
+                    "clear": "Clear"
+                },
+                "alias": {
+                    "add": "Add alias",
+                    "edit": "Edit alias",
+                    "name": "Alias name",
+                    "name-required": "Alias name is required",
+                    "duplicate-alias": "Alias with same name is already exists.",
+                    "filter-type-entity-list": "Entity list",
+                    "filter-type-entity-name": "Entity name",
+                    "filter-type-state-entity": "Entity from dashboard state",
+                    "filter-type-state-entity-description": "Entity taken from dashboard state parameters",
+                    "filter-type-asset-type": "Asset type",
+                    "filter-type-asset-type-description": "Assets of type '{{assetType}}'",
+                    "filter-type-asset-type-and-name-description": "Assets of type '{{assetType}}' and with name starting with '{{prefix}}'",
+                    "filter-type-device-type": "Device type",
+                    "filter-type-device-type-description": "Devices of type '{{deviceType}}'",
+                    "filter-type-device-type-and-name-description": "Devices of type '{{deviceType}}' and with name starting with '{{prefix}}'",
+                    "filter-type-relations-query": "Relations query",
+                    "filter-type-relations-query-description": "{{entities}} that have {{relationType}} relation {{direction}} {{rootEntity}}",
+                    "filter-type-asset-search-query": "Asset search query",
+                    "filter-type-asset-search-query-description": "Assets with types {{assetTypes}} that have {{relationType}} relation {{direction}} {{rootEntity}}",
+                    "filter-type-device-search-query": "Device search query",
+                    "filter-type-device-search-query-description": "Devices with types {{deviceTypes}} that have {{relationType}} relation {{direction}} {{rootEntity}}",
+                    "entity-filter": "Entity filter",
+                    "resolve-multiple": "Resolve as multiple entities",
+                    "filter-type": "Filter type",
+                    "filter-type-required": "Filter type is required.",
+                    "entity-filter-no-entity-matched": "No entities matching specified filter were found.",
+                    "no-entity-filter-specified": "No entity filter specified",
+                    "root-state-entity": "Use dashboard state entity as root",
+                    "root-entity": "Root entity",
+                    "max-relation-level": "Max relation level",
+                    "unlimited-level": "Unlimited level",
+                    "state-entity": "Dashboard state entity",
+                    "all-entities": "All entities",
+                    "any-relation": "any"
                 },
                 "asset": {
                     "asset": "Asset",
@@ -133,6 +203,11 @@ export default angular.module('thingsboard.locale', [])
                     "asset-type": "Asset type",
                     "asset-type-required": "Asset type is required.",
                     "select-asset-type": "Select asset type",
+                    "enter-asset-type": "Enter asset type",
+                    "any-asset": "Any asset",
+                    "no-asset-types-matching": "No asset types matching '{{entitySubtype}}' were found.",
+                    "asset-type-list-empty": "No asset types selected.",
+                    "asset-types": "Asset types",
                     "name": "Name",
                     "name-required": "Name is required.",
                     "description": "Description",
@@ -166,7 +241,8 @@ export default angular.module('thingsboard.locale', [])
                     "idCopiedMessage": "Asset Id has been copied to clipboard",
                     "select-asset": "Select asset",
                     "no-assets-matching": "No assets matching '{{entity}}' were found.",
-                    "asset-required": "Asset is required"
+                    "asset-required": "Asset is required",
+                    "name-starts-with": "Asset name starts with"
                 },
                 "attribute": {
                     "attributes": "Attributes",
@@ -417,6 +493,7 @@ export default angular.module('thingsboard.locale', [])
                 },
                 "datasource": {
                     "type": "Datasource type",
+                    "name": "Name",
                     "add-datasource-prompt": "Please add datasource"
                 },
                 "details": {
@@ -444,7 +521,7 @@ export default angular.module('thingsboard.locale', [])
                     "alias-required": "Device alias is required.",
                     "remove-alias": "Remove device alias",
                     "add-alias": "Add device alias",
-                    "name-starts-with": "Name starts with",
+                    "name-starts-with": "Device name starts with",
                     "device-list": "Device list",
                     "use-device-name-filter": "Use filter",
                     "device-list-empty": "No devices selected.",
@@ -497,6 +574,11 @@ export default angular.module('thingsboard.locale', [])
                     "device-type": "Device type",
                     "device-type-required": "Device type is required.",
                     "select-device-type": "Select device type",
+                    "enter-device-type": "Enter device type",
+                    "any-device": "Any device",
+                    "no-device-types-matching": "No device types matching '{{entitySubtype}}' were found.",
+                    "device-type-list-empty": "No device types selected.",
+                    "device-types": "Device types",
                     "name": "Name",
                     "name-required": "Name is required.",
                     "description": "Description",
@@ -530,41 +612,75 @@ export default angular.module('thingsboard.locale', [])
                     "unable-delete-entity-alias-title": "Unable to delete entity alias",
                     "unable-delete-entity-alias-text": "Entity alias '{{entityAlias}}' can't be deleted as it used by the following widget(s):<br/>{{widgetsList}}",
                     "duplicate-alias-error": "Duplicate alias found '{{alias}}'.<br>Entity aliases must be unique whithin the dashboard.",
+                    "missing-entity-filter-error": "Filter is missing for alias '{{alias}}'.",
                     "configure-alias": "Configure '{{alias}}' alias",
                     "alias": "Alias",
                     "alias-required": "Entity alias is required.",
                     "remove-alias": "Remove entity alias",
                     "add-alias": "Add entity alias",
                     "entity-list": "Entity list",
+                    "entity-type": "Entity type",
+                    "entity-types": "Entity types",
+                    "entity-type-list": "Entity type list",
+                    "any-entity": "Any entity",
+                    "enter-entity-type": "Enter entity type",
                     "no-entities-matching": "No entities matching '{{entity}}' were found.",
+                    "no-entity-types-matching": "No entity types matching '{{entityType}}' were found.",
                     "name-starts-with": "Name starts with",
                     "use-entity-name-filter": "Use filter",
                     "entity-list-empty": "No entities selected.",
+                    "entity-type-list-empty": "No entity types selected.",
                     "entity-name-filter-required": "Entity name filter is required.",
                     "entity-name-filter-no-entity-matched": "No entities starting with '{{entity}}' were found.",
                     "all-subtypes": "All",
+                    "select-entities": "Select entities",
+                    "no-aliases-found": "No aliases found.",
+                    "no-alias-matching": "'{{alias}}' not found.",
+                    "create-new-alias": "Create a new one!",
+                    "no-keys-found": "No keys found.",
+                    "no-key-matching": "'{{key}}' not found.",
+                    "create-new-key": "Create a new one!",
                     "type": "Type",
                     "type-required": "Entity type is required.",
                     "type-device": "Device",
+                    "type-devices": "Devices",
+                    "list-of-devices": "{ count, select, 1 {One device} other {List of # devices} }",
+                    "device-name-starts-with": "Devices whose names start with '{{prefix}}'",
                     "type-asset": "Asset",
+                    "type-assets": "Assets",
+                    "list-of-assets": "{ count, select, 1 {One asset} other {List of # assets} }",
+                    "asset-name-starts-with": "Assets whose names start with '{{prefix}}'",
                     "type-rule": "Rule",
+                    "type-rules": "Rules",
+                    "list-of-rules": "{ count, select, 1 {One rule} other {List of # rules} }",
+                    "rule-name-starts-with": "Rules whose names start with '{{prefix}}'",
                     "type-plugin": "Plugin",
+                    "type-plugins": "Plugins",
+                    "list-of-plugins": "{ count, select, 1 {One plugin} other {List of # plugins} }",
+                    "plugin-name-starts-with": "Plugins whose names start with '{{prefix}}'",
                     "type-tenant": "Tenant",
+                    "type-tenants": "Tenants",
+                    "list-of-tenants": "{ count, select, 1 {One tenant} other {List of # tenants} }",
+                    "tenant-name-starts-with": "Tenants whose names start with '{{prefix}}'",
                     "type-customer": "Customer",
+                    "type-customers": "Customers",
+                    "list-of-customers": "{ count, select, 1 {One customer} other {List of # customers} }",
+                    "customer-name-starts-with": "Customers whose names start with '{{prefix}}'",
                     "type-user": "User",
+                    "type-users": "Users",
+                    "list-of-users": "{ count, select, 1 {One user} other {List of # users} }",
+                    "user-name-starts-with": "Users whose names start with '{{prefix}}'",
                     "type-dashboard": "Dashboard",
+                    "type-dashboards": "Dashboards",
+                    "list-of-dashboards": "{ count, select, 1 {One dashboard} other {List of # dashboards} }",
+                    "dashboard-name-starts-with": "Dashboards whose names start with '{{prefix}}'",
                     "type-alarm": "Alarm",
-                    "select-entities": "Select entities",
-                    "no-aliases-found": "No aliases found.",
-                    "no-alias-matching": "'{{alias}}' not found.",
-                    "create-new-alias": "Create a new one!",
-                    "no-keys-found": "No keys found.",
-                    "no-key-matching": "'{{key}}' not found.",
-                    "create-new-key": "Create a new one!"
+                    "type-alarms": "Alarms",
+                    "list-of-alarms": "{ count, select, 1 {One alarms} other {List of # alarms} }",
+                    "alarm-name-starts-with": "Alarms whose names start with '{{prefix}}'"
                 },
                 "event": {
                     "event-type": "Event type",
-                    "type-alarm": "Alarm",
                     "type-error": "Error",
                     "type-lc-event": "Lifecycle event",
                     "type-stats": "Statistics",
@@ -724,6 +840,10 @@ export default angular.module('thingsboard.locale', [])
                         "FROM": "From",
                         "TO": "To"
                     },
+                    "direction-type": {
+                        "FROM": "from",
+                        "TO": "to"
+                    },
                     "from-relations": "Outbound relations",
                     "to-relations": "Inbound relations",
                     "selected-relations": "{ count, select, 1 {1 relation} other {# relations} } selected",
@@ -737,6 +857,7 @@ export default angular.module('thingsboard.locale', [])
                     "delete": "Delete relation",
                     "relation-type": "Relation type",
                     "relation-type-required": "Relation type is required.",
+                    "any-relation-type": "Any type",
                     "add": "Add relation",
                     "delete-to-relation-title": "Are you sure you want to delete relation to the entity '{{entityName}}'?",
                     "delete-to-relation-text": "Be careful, after the confirmation the entity '{{entityName}}' will be unrelated from the current entity.",
@@ -745,7 +866,11 @@ export default angular.module('thingsboard.locale', [])
                     "delete-from-relation-title": "Are you sure you want to delete relation from the entity '{{entityName}}'?",
                     "delete-from-relation-text": "Be careful, after the confirmation current entity will be unrelated from the entity '{{entityName}}'.",
                     "delete-from-relations-title": "Are you sure you want to delete { count, select, 1 {1 relation} other {# relations} }?",
-                    "delete-from-relations-text": "Be careful, after the confirmation all selected relations will be removed and current entity will be unrelated from the corresponding entities."
+                    "delete-from-relations-text": "Be careful, after the confirmation all selected relations will be removed and current entity will be unrelated from the corresponding entities.",
+                    "remove-relation-filter": "Remove relation filter",
+                    "add-relation-filter": "Add relation filter",
+                    "any-relation": "Any relation",
+                    "relation-filters": "Relation filters"
                 },
                 "rule": {
                     "rule": "Rule",
diff --git a/ui/src/app/locale/locale.constant-es.js b/ui/src/app/locale/locale.constant-es.js
index e4d4b49..b9076ba 100644
--- a/ui/src/app/locale/locale.constant-es.js
+++ b/ui/src/app/locale/locale.constant-es.js
@@ -411,7 +411,6 @@
         },
         "event": {
               "event-type": "Tipo de evento",
-              "type-alarm": "Alarma",
               "type-error": "Error",
               "type-lc-event": "Ciclo de vida",
               "type-stats": "Estadísticas",
diff --git a/ui/src/app/locale/locale.constant-ko.js b/ui/src/app/locale/locale.constant-ko.js
index 32dbf49..0caeab8 100644
--- a/ui/src/app/locale/locale.constant-ko.js
+++ b/ui/src/app/locale/locale.constant-ko.js
@@ -378,7 +378,6 @@ export default function addLocaleKorean(locales) {
         },
         "event": {
             "event-type": "이벤트 타입",
-            "type-alarm": "알람",
             "type-error": "에러",
             "type-lc-event": "주기적 이벤트",
             "type-stats": "통계",
diff --git a/ui/src/app/locale/locale.constant-ru.js b/ui/src/app/locale/locale.constant-ru.js
index d7734b4..cded7a6 100644
--- a/ui/src/app/locale/locale.constant-ru.js
+++ b/ui/src/app/locale/locale.constant-ru.js
@@ -411,7 +411,6 @@ export default function addLocaleRussian(locales) {
         },
         "event": {
             "event-type": "Тип события",
-            "type-alarm": "Аварийное оповещение",
             "type-error": "Ошибка",
             "type-lc-event": "Событие жизненного цикла",
             "type-stats": "Статистика",
diff --git a/ui/src/app/locale/locale.constant-zh.js b/ui/src/app/locale/locale.constant-zh.js
index 3246070..62746c7 100644
--- a/ui/src/app/locale/locale.constant-zh.js
+++ b/ui/src/app/locale/locale.constant-zh.js
@@ -411,7 +411,6 @@ export default function addLocaleChinese(locales) {
         },
         "event" : {
             "event-type": "事件类型",
-            "type-alarm": "报警",
             "type-error": "错误",
             "type-lc-event": "生命周期事件",
             "type-stats": "类型统计",
diff --git a/ui/src/app/plugin/index.js b/ui/src/app/plugin/index.js
index 1c9e731..6173d62 100644
--- a/ui/src/app/plugin/index.js
+++ b/ui/src/app/plugin/index.js
@@ -16,7 +16,6 @@
 import uiRouter from 'angular-ui-router';
 import thingsboardGrid from '../components/grid.directive';
 import thingsboardJsonForm from '../components/json-form.directive';
-import thingsboardEvent from '../event';
 import thingsboardApiPlugin from '../api/plugin.service';
 import thingsboardApiComponentDescriptor from '../api/component-descriptor.service';
 
@@ -28,7 +27,6 @@ export default angular.module('thingsboard.plugin', [
     uiRouter,
     thingsboardGrid,
     thingsboardJsonForm,
-    thingsboardEvent,
     thingsboardApiPlugin,
     thingsboardApiComponentDescriptor
 ])
diff --git a/ui/src/app/plugin/plugins.tpl.html b/ui/src/app/plugin/plugins.tpl.html
index 5b03506..73b0adc 100644
--- a/ui/src/app/plugin/plugins.tpl.html
+++ b/ui/src/app/plugin/plugins.tpl.html
@@ -48,12 +48,16 @@
                                 disable-attribute-scope-selection="true">
             </tb-attribute-table>
         </md-tab>
+        <md-tab ng-if="!vm.grid.detailsConfig.isDetailsEditMode && vm.isPluginEditable(vm.grid.operatingItem())" label="{{ 'alarm.alarms' | translate }}">
+            <tb-alarm-table flex entity-type="vm.types.entityType.plugin"
+                            entity-id="vm.grid.operatingItem().id.id">
+            </tb-alarm-table>
+        </md-tab>
         <md-tab ng-if="!vm.grid.detailsConfig.isDetailsEditMode && vm.isPluginEditable(vm.grid.operatingItem())" label="{{ 'plugin.events' | translate }}">
             <tb-event-table flex entity-type="vm.types.entityType.plugin"
                             entity-id="vm.grid.operatingItem().id.id"
                             tenant-id="vm.grid.operatingItem().tenantId.id"
-                            default-event-type="{{vm.types.eventType.lcEvent.value}}"
-                            disabled-event-types="{{vm.types.eventType.alarm.value}}">
+                            default-event-type="{{vm.types.eventType.lcEvent.value}}">
             </tb-event-table>
         </md-tab>
         <md-tab ng-if="!vm.grid.detailsConfig.isDetailsEditMode && vm.isPluginEditable(vm.grid.operatingItem())" label="{{ 'relation.relations' | translate }}">
diff --git a/ui/src/app/rule/index.js b/ui/src/app/rule/index.js
index 6481920..2700ec7 100644
--- a/ui/src/app/rule/index.js
+++ b/ui/src/app/rule/index.js
@@ -17,7 +17,6 @@ import uiRouter from 'angular-ui-router';
 import thingsboardGrid from '../components/grid.directive';
 import thingsboardPluginSelect from '../components/plugin-select.directive';
 import thingsboardComponent from '../component';
-import thingsboardEvent from '../event';
 import thingsboardApiRule from '../api/rule.service';
 import thingsboardApiPlugin from '../api/plugin.service';
 import thingsboardApiComponentDescriptor from '../api/component-descriptor.service';
@@ -31,7 +30,6 @@ export default angular.module('thingsboard.rule', [
     thingsboardGrid,
     thingsboardPluginSelect,
     thingsboardComponent,
-    thingsboardEvent,
     thingsboardApiRule,
     thingsboardApiPlugin,
     thingsboardApiComponentDescriptor
diff --git a/ui/src/app/rule/rules.tpl.html b/ui/src/app/rule/rules.tpl.html
index 098bbee..336fe63 100644
--- a/ui/src/app/rule/rules.tpl.html
+++ b/ui/src/app/rule/rules.tpl.html
@@ -48,12 +48,16 @@
                                 disable-attribute-scope-selection="true">
             </tb-attribute-table>
         </md-tab>
+        <md-tab ng-if="!vm.grid.detailsConfig.isDetailsEditMode && vm.isRuleEditable(vm.grid.operatingItem())" label="{{ 'alarm.alarms' | translate }}">
+            <tb-alarm-table flex entity-type="vm.types.entityType.rule"
+                            entity-id="vm.grid.operatingItem().id.id">
+            </tb-alarm-table>
+        </md-tab>
         <md-tab ng-if="!vm.grid.detailsConfig.isDetailsEditMode && vm.isRuleEditable(vm.grid.operatingItem())" label="{{ 'rule.events' | translate }}">
             <tb-event-table flex entity-type="vm.types.entityType.rule"
                             entity-id="vm.grid.operatingItem().id.id"
                             tenant-id="vm.grid.operatingItem().tenantId.id"
-                            default-event-type="{{vm.types.eventType.lcEvent.value}}"
-                            disabled-event-types="{{vm.types.eventType.alarm.value}}">
+                            default-event-type="{{vm.types.eventType.lcEvent.value}}">
             </tb-event-table>
         </md-tab>
         <md-tab ng-if="!vm.grid.detailsConfig.isDetailsEditMode && vm.isRuleEditable(vm.grid.operatingItem())" label="{{ 'relation.relations' | translate }}">
diff --git a/ui/src/app/services/item-buffer.service.js b/ui/src/app/services/item-buffer.service.js
index afb63bf..d29ad18 100644
--- a/ui/src/app/services/item-buffer.service.js
+++ b/ui/src/app/services/item-buffer.service.js
@@ -46,16 +46,14 @@ function ItemBuffer($q, bufferStore, types, utils, dashboardUtils) {
      aliasesInfo {
         datasourceAliases: {
             datasourceIndex: {
-                aliasName: "...",
-                entityType: "...",
-                entityFilter: "..."
+                alias: "...",
+                filter: "..."
             }
         }
         targetDeviceAliases: {
             targetDeviceAliasIndex: {
-                aliasName: "...",
-                entityType: "...",
-                entityFilter: "..."
+                alias: "...",
+                filter: "..."
             }
         }
         ....
@@ -64,9 +62,8 @@ function ItemBuffer($q, bufferStore, types, utils, dashboardUtils) {
 
     function prepareAliasInfo(entityAlias) {
         return {
-            aliasName: entityAlias.alias,
-            entityType: entityAlias.entityType,
-            entityFilter: entityAlias.entityFilter
+            alias: entityAlias.alias,
+            filter: entityAlias.filter
         };
     }
 
@@ -264,14 +261,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;
     }
 
@@ -293,8 +285,7 @@ function ItemBuffer($q, bufferStore, types, utils, dashboardUtils) {
     }
 
     function isEntityAliasEqual(alias1, alias2) {
-        return alias1.entityType === alias2.entityType &&
-            angular.equals(alias1.entityFilter, alias2.entityFilter);
+        return angular.equals(alias1.filter, alias2.filter);
     }
 
     function getEntityAliasId(entityAliases, aliasInfo) {
@@ -306,13 +297,9 @@ function ItemBuffer($q, bufferStore, types, utils, dashboardUtils) {
             }
         }
         if (!newAliasId) {
-            var newAliasName = createEntityAliasName(entityAliases, aliasInfo.aliasName);
-            newAliasId = 0;
-            for (aliasId in entityAliases) {
-                newAliasId = Math.max(newAliasId, aliasId);
-            }
-            newAliasId++;
-            entityAliases[newAliasId] = {alias: newAliasName, entityType: aliasInfo.entityType, entityFilter: aliasInfo.entityFilter};
+            var newAliasName = createEntityAliasName(entityAliases, aliasInfo.alias);
+            newAliasId = utils.guid();
+            entityAliases[newAliasId] = {id: newAliasId, alias: newAliasName, filter: aliasInfo.filter};
         }
         return newAliasId;
     }
diff --git a/ui/src/app/tenant/tenants.tpl.html b/ui/src/app/tenant/tenants.tpl.html
index 00350a4..e407291 100644
--- a/ui/src/app/tenant/tenants.tpl.html
+++ b/ui/src/app/tenant/tenants.tpl.html
@@ -46,11 +46,16 @@
 								disable-attribute-scope-selection="true">
 			</tb-attribute-table>
 		</md-tab>
+		<md-tab ng-if="!vm.grid.detailsConfig.isDetailsEditMode" label="{{ 'alarm.alarms' | translate }}">
+			<tb-alarm-table flex entity-type="vm.types.entityType.tenant"
+							entity-id="vm.grid.operatingItem().id.id">
+			</tb-alarm-table>
+		</md-tab>
 		<md-tab ng-if="!vm.grid.detailsConfig.isDetailsEditMode" label="{{ 'tenant.events' | translate }}">
 			<tb-event-table flex entity-type="vm.types.entityType.tenant"
 							entity-id="vm.grid.operatingItem().id.id"
 							tenant-id="vm.types.id.nullUid"
-							default-event-type="{{vm.types.eventType.alarm.value}}">
+							default-event-type="{{vm.types.eventType.error.value}}">
 			</tb-event-table>
 		</md-tab>
 		<md-tab ng-if="!vm.grid.detailsConfig.isDetailsEditMode" label="{{ 'relation.relations' | translate }}">
diff --git a/ui/src/scss/main.scss b/ui/src/scss/main.scss
index dbe3543..ab720c5 100644
--- a/ui/src/scss/main.scss
+++ b/ui/src/scss/main.scss
@@ -236,6 +236,15 @@ div {
   }
 }
 
+.md-caption {
+  &.tb-required:after {
+    content: ' *';
+    font-size: 10px;
+    vertical-align: top;
+    color: rgba(0,0,0,0.54);
+  }
+}
+
 pre.tb-highlight {
   background-color: #f7f7f7;
   display: block;
@@ -300,6 +309,24 @@ pre.tb-highlight {
   }
 }
 
+.tb-severity {
+  font-weight: bold;
+  &.tb-critical {
+    color: red !important;
+  }
+  &.tb-major {
+    color: orange !important;
+  }
+  &.tb-minor {
+    color: #ffca3d !important;
+  }
+  &.tb-warning {
+    color: #abab00 !important;
+  }
+  &.tb-indeterminate {
+    color: green !important;
+  }
+}
 
 /***********************
  * Flow