thingsboard-aplcache

Changes

pom.xml 8(+1 -7)

Details

diff --git a/application/pom.xml b/application/pom.xml
index c7d3240..e5a4a4e 100644
--- a/application/pom.xml
+++ b/application/pom.xml
@@ -126,10 +126,6 @@
             <artifactId>jjwt</artifactId>
         </dependency>
         <dependency>
-            <groupId>joda-time</groupId>
-            <artifactId>joda-time</artifactId>
-        </dependency>
-        <dependency>
             <groupId>org.apache.velocity</groupId>
             <artifactId>velocity</artifactId>
         </dependency>
diff --git a/application/src/main/data/json/system/widget_bundles/cards.json b/application/src/main/data/json/system/widget_bundles/cards.json
index 0fc0085..ddb90db 100644
--- a/application/src/main/data/json/system/widget_bundles/cards.json
+++ b/application/src/main/data/json/system/widget_bundles/cards.json
@@ -112,9 +112,9 @@
         "templateHtml": "<tb-timeseries-table-widget \n    table-id=\"tableId\"\n    ctx=\"ctx\">\n</tb-timeseries-table-widget>",
         "templateCss": "",
         "controllerScript": "self.onInit = function() {\n    var scope = self.ctx.$scope;\n    var id = self.ctx.$scope.$injector.get('utils').guid();\n    scope.tableId = \"table-\"+id;\n    scope.ctx = self.ctx;\n}\n\nself.onDataUpdated = function() {\n    self.ctx.$scope.$broadcast('timeseries-table-data-updated', self.ctx.$scope.tableId);\n}\n\nself.onDestroy = function() {\n}",
-        "settingsSchema": "{\n    \"schema\": {\n        \"type\": \"object\",\n        \"title\": \"TimeseriesTableSettings\",\n        \"properties\": {\n            \"showTimestamp\": {\n                \"title\": \"Display timestamp column\",\n                \"type\": \"boolean\",\n                \"default\": true\n            }\n        },\n        \"required\": []\n    },\n    \"form\": [\n        \"showTimestamp\"\n    ]\n}",
+        "settingsSchema": "{\n    \"schema\": {\n        \"type\": \"object\",\n        \"title\": \"TimeseriesTableSettings\",\n        \"properties\": {\n            \"showTimestamp\": {\n                \"title\": \"Display timestamp column\",\n                \"type\": \"boolean\",\n                \"default\": true\n            },\n            \"displayPagination\": {\n                \"title\": \"Display pagination\",\n                \"type\": \"boolean\",\n                \"default\": true\n            },            \n            \"defaultPageSize\": {\n                \"title\": \"Default page size\",\n                \"type\": \"number\",\n                \"default\": 10\n            }\n        },\n        \"required\": []\n    },\n    \"form\": [\n        \"showTimestamp\",\n        \"displayPagination\",\n        \"defaultPageSize\"\n    ]\n}",
         "dataKeySettingsSchema": "{\n    \"schema\": {\n        \"type\": \"object\",\n        \"title\": \"DataKeySettings\",\n        \"properties\": {\n            \"useCellStyleFunction\": {\n                \"title\": \"Use cell style function\",\n                \"type\": \"boolean\",\n                \"default\": false\n            },\n            \"cellStyleFunction\": {\n                \"title\": \"Cell style function: f(value)\",\n                \"type\": \"string\",\n                \"default\": \"\"\n            },\n            \"useCellContentFunction\": {\n                \"title\": \"Use cell content function\",\n                \"type\": \"boolean\",\n                \"default\": false\n            },\n            \"cellContentFunction\": {\n                \"title\": \"Cell content function: f(value, rowData, filter)\",\n                \"type\": \"string\",\n                \"default\": \"\"\n            }\n        },\n        \"required\": []\n    },\n    \"form\": [\n        \"useCellStyleFunction\",\n        {\n            \"key\": \"cellStyleFunction\",\n            \"type\": \"javascript\"\n        },\n        \"useCellContentFunction\",\n        {\n            \"key\": \"cellContentFunction\",\n            \"type\": \"javascript\"\n        }\n    ]\n}",
-        "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Temperature  °C\",\"color\":\"#2196f3\",\"settings\":{\"useCellStyleFunction\":true,\"cellStyleFunction\":\"if (value) {\\n    var percent = (value + 60)/120 * 100;\\n    var color = tinycolor.mix('blue', 'red', amount = percent);\\n    color.setAlpha(.5);\\n    return {\\n      paddingLeft: '20px',\\n      color: '#ffffff',\\n      background: color.toRgbString(),\\n      fontSize: '18px'\\n    };\\n} else {\\n    return {};\\n}\"},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 40 - 20;\\nvar multiplier = Math.pow(10, 1 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -60) {\\n\\tvalue = -60;\\n} else if (value > 60) {\\n\\tvalue = 60;\\n}\\nreturn value;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Humidity, %\",\"color\":\"#ffc107\",\"settings\":{\"useCellStyleFunction\":true,\"cellStyleFunction\":\"if (value) {\\n    var percent = value;\\n    var backgroundColor = tinycolor('blue');\\n    backgroundColor.setAlpha(value/100);\\n    var color = 'blue';\\n    if (value > 50) {\\n        color = 'white';\\n    }\\n    \\n    return {\\n      paddingLeft: '20px',\\n      color: color,\\n      background: backgroundColor.toRgbString(),\\n      fontSize: '18px'\\n    };\\n} else {\\n    return {};\\n}\",\"useCellContentFunction\":false},\"_hash\":0.12775350966079668,\"funcBody\":\"var value = prevValue + Math.random() * 20 - 10;\\nvar multiplier = Math.pow(10, 1 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 5) {\\n\\tvalue = 5;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"interval\":1000,\"timewindowMs\":60000},\"aggregation\":{\"type\":\"NONE\",\"limit\":200}},\"showTitle\":true,\"backgroundColor\":\"rgb(255, 255, 255)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"showTimestamp\":true},\"title\":\"Timeseries table\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":false,\"showLegend\":false}"
+        "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Temperature  °C\",\"color\":\"#2196f3\",\"settings\":{\"useCellStyleFunction\":true,\"cellStyleFunction\":\"if (value) {\\n    var percent = (value + 60)/120 * 100;\\n    var color = tinycolor.mix('blue', 'red', amount = percent);\\n    color.setAlpha(.5);\\n    return {\\n      paddingLeft: '20px',\\n      color: '#ffffff',\\n      background: color.toRgbString(),\\n      fontSize: '18px'\\n    };\\n} else {\\n    return {};\\n}\"},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 40 - 20;\\nvar multiplier = Math.pow(10, 1 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -60) {\\n\\tvalue = -60;\\n} else if (value > 60) {\\n\\tvalue = 60;\\n}\\nreturn value;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Humidity, %\",\"color\":\"#ffc107\",\"settings\":{\"useCellStyleFunction\":true,\"cellStyleFunction\":\"if (value) {\\n    var percent = value;\\n    var backgroundColor = tinycolor('blue');\\n    backgroundColor.setAlpha(value/100);\\n    var color = 'blue';\\n    if (value > 50) {\\n        color = 'white';\\n    }\\n    \\n    return {\\n      paddingLeft: '20px',\\n      color: color,\\n      background: backgroundColor.toRgbString(),\\n      fontSize: '18px'\\n    };\\n} else {\\n    return {};\\n}\",\"useCellContentFunction\":false},\"_hash\":0.12775350966079668,\"funcBody\":\"var value = prevValue + Math.random() * 20 - 10;\\nvar multiplier = Math.pow(10, 1 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 5) {\\n\\tvalue = 5;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"interval\":1000,\"timewindowMs\":60000},\"aggregation\":{\"type\":\"NONE\",\"limit\":200}},\"showTitle\":true,\"backgroundColor\":\"rgb(255, 255, 255)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"showTimestamp\":true,\"displayPagination\":true,\"defaultPageSize\":10},\"title\":\"Timeseries table\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":false,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{}}"
       }
     }
   ]
diff --git a/application/src/main/java/org/thingsboard/server/actors/plugin/PluginProcessingContext.java b/application/src/main/java/org/thingsboard/server/actors/plugin/PluginProcessingContext.java
index a2dd53b..d2d9b7a 100644
--- a/application/src/main/java/org/thingsboard/server/actors/plugin/PluginProcessingContext.java
+++ b/application/src/main/java/org/thingsboard/server/actors/plugin/PluginProcessingContext.java
@@ -62,6 +62,7 @@ public final class PluginProcessingContext implements PluginContext {
     private static final Executor executor = Executors.newSingleThreadExecutor();
     public static final String CUSTOMER_USER_IS_NOT_ALLOWED_TO_PERFORM_THIS_OPERATION = "Customer user is not allowed to perform this operation!";
     public static final String SYSTEM_ADMINISTRATOR_IS_NOT_ALLOWED_TO_PERFORM_THIS_OPERATION = "System administrator is not allowed to perform this operation!";
+    public static final String DEVICE_WITH_REQUESTED_ID_NOT_FOUND = "Device with requested id wasn't found!";
 
     private final SharedPluginProcessingContext pluginCtx;
     private final Optional<PluginApiCallSecurityContext> securityCtx;
@@ -309,7 +310,7 @@ public final class PluginProcessingContext implements PluginContext {
             ListenableFuture<Device> deviceFuture = pluginCtx.deviceService.findDeviceByIdAsync(new DeviceId(entityId.getId()));
             Futures.addCallback(deviceFuture, getCallback(callback, device -> {
                 if (device == null) {
-                    return ValidationResult.entityNotFound("Device with requested id wasn't found!");
+                    return ValidationResult.entityNotFound(DEVICE_WITH_REQUESTED_ID_NOT_FOUND);
                 } else {
                     if (!device.getTenantId().equals(ctx.getTenantId())) {
                         return ValidationResult.accessDenied("Device doesn't belong to the current Tenant!");
diff --git a/application/src/main/java/org/thingsboard/server/config/WebSocketConfiguration.java b/application/src/main/java/org/thingsboard/server/config/WebSocketConfiguration.java
index 35d1773..9f7c08f 100644
--- a/application/src/main/java/org/thingsboard/server/config/WebSocketConfiguration.java
+++ b/application/src/main/java/org/thingsboard/server/config/WebSocketConfiguration.java
@@ -47,8 +47,8 @@ public class WebSocketConfiguration implements WebSocketConfigurer {
     @Bean
     public ServletServerContainerFactoryBean createWebSocketContainer() {
         ServletServerContainerFactoryBean container = new ServletServerContainerFactoryBean();
-        container.setMaxTextMessageBufferSize(8192);
-        container.setMaxBinaryMessageBufferSize(8192);
+        container.setMaxTextMessageBufferSize(32768);
+        container.setMaxBinaryMessageBufferSize(32768);
         return container;
     }
 
diff --git a/application/src/main/java/org/thingsboard/server/controller/CustomerController.java b/application/src/main/java/org/thingsboard/server/controller/CustomerController.java
index c54973f..d87ea5c 100644
--- a/application/src/main/java/org/thingsboard/server/controller/CustomerController.java
+++ b/application/src/main/java/org/thingsboard/server/controller/CustomerController.java
@@ -82,7 +82,7 @@ public class CustomerController extends BaseController {
 
     @PreAuthorize("hasAuthority('TENANT_ADMIN')")
     @RequestMapping(value = "/customer", method = RequestMethod.POST)
-    @ResponseBody 
+    @ResponseBody
     public Customer saveCustomer(@RequestBody Customer customer) throws ThingsboardException {
         try {
             customer.setTenantId(getCurrentUser().getTenantId());
@@ -107,7 +107,7 @@ public class CustomerController extends BaseController {
     }
 
     @PreAuthorize("hasAuthority('TENANT_ADMIN')")
-    @RequestMapping(value = "/customers", params = { "limit" }, method = RequestMethod.GET)
+    @RequestMapping(value = "/customers", params = {"limit"}, method = RequestMethod.GET)
     @ResponseBody
     public TextPageData<Customer> getCustomers(@RequestParam int limit,
                                                @RequestParam(required = false) String textSearch,
@@ -122,4 +122,16 @@ public class CustomerController extends BaseController {
         }
     }
 
+    @PreAuthorize("hasAuthority('TENANT_ADMIN')")
+    @RequestMapping(value = "/tenant/customers", params = {"customerTitle"}, method = RequestMethod.GET)
+    @ResponseBody
+    public Customer getTenantCustomer(
+            @RequestParam String customerTitle) throws ThingsboardException {
+        try {
+            TenantId tenantId = getCurrentUser().getTenantId();
+            return checkNotNull(customerService.findCustomerByTenantIdAndTitle(tenantId, customerTitle));
+        } catch (Exception e) {
+            throw handleException(e);
+        }
+    }
 }
diff --git a/application/src/main/java/org/thingsboard/server/controller/plugin/PluginWebSocketHandler.java b/application/src/main/java/org/thingsboard/server/controller/plugin/PluginWebSocketHandler.java
index d1df671..727fd30 100644
--- a/application/src/main/java/org/thingsboard/server/controller/plugin/PluginWebSocketHandler.java
+++ b/application/src/main/java/org/thingsboard/server/controller/plugin/PluginWebSocketHandler.java
@@ -77,7 +77,6 @@ public class PluginWebSocketHandler extends TextWebSocketHandler implements Plug
                 log.warn("[{}] Failed to find session", session.getId());
                 session.close(CloseStatus.SERVER_ERROR.withReason("Session not found!"));
             }
-            session.sendMessage(message);
         } catch (IOException e) {
             log.warn("IO error", e);
         }
diff --git a/application/src/main/java/org/thingsboard/server/service/mail/DefaultMailService.java b/application/src/main/java/org/thingsboard/server/service/mail/DefaultMailService.java
index c069ba6..a6c4f43 100644
--- a/application/src/main/java/org/thingsboard/server/service/mail/DefaultMailService.java
+++ b/application/src/main/java/org/thingsboard/server/service/mail/DefaultMailService.java
@@ -96,7 +96,7 @@ public class DefaultMailService implements MailService {
         javaMailProperties.put(MAIL_PROP + protocol + ".port", jsonConfig.get("smtpPort").asText());
         javaMailProperties.put(MAIL_PROP + protocol + ".timeout", jsonConfig.get("timeout").asText());
         javaMailProperties.put(MAIL_PROP + protocol + ".auth", String.valueOf(StringUtils.isNotEmpty(jsonConfig.get("username").asText())));
-        javaMailProperties.put(MAIL_PROP + protocol + ".starttls.enable", jsonConfig.get("enableTls"));
+        javaMailProperties.put(MAIL_PROP + protocol + ".starttls.enable", jsonConfig.has("enableTls") ? jsonConfig.get("enableTls").asText() : "false");
         return javaMailProperties;
     }
     
diff --git a/application/src/main/java/org/thingsboard/server/service/security/model/token/JwtTokenFactory.java b/application/src/main/java/org/thingsboard/server/service/security/model/token/JwtTokenFactory.java
index 20de6d8..7e5fba5 100644
--- a/application/src/main/java/org/thingsboard/server/service/security/model/token/JwtTokenFactory.java
+++ b/application/src/main/java/org/thingsboard/server/service/security/model/token/JwtTokenFactory.java
@@ -20,7 +20,6 @@ import io.jsonwebtoken.Jws;
 import io.jsonwebtoken.Jwts;
 import io.jsonwebtoken.SignatureAlgorithm;
 import org.apache.commons.lang3.StringUtils;
-import org.joda.time.DateTime;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Component;
 import org.thingsboard.server.common.data.id.CustomerId;
@@ -31,7 +30,9 @@ import org.thingsboard.server.config.JwtSettings;
 import org.thingsboard.server.service.security.model.SecurityUser;
 import org.thingsboard.server.service.security.model.UserPrincipal;
 
-import java.util.Arrays;
+import java.time.ZonedDateTime;
+import java.util.Collections;
+import java.util.Date;
 import java.util.List;
 import java.util.UUID;
 import java.util.stream.Collectors;
@@ -81,13 +82,13 @@ public class JwtTokenFactory {
             claims.put(CUSTOMER_ID, securityUser.getCustomerId().getId().toString());
         }
 
-        DateTime currentTime = new DateTime();
+        ZonedDateTime currentTime = ZonedDateTime.now();
 
         String token = Jwts.builder()
                 .setClaims(claims)
                 .setIssuer(settings.getTokenIssuer())
-                .setIssuedAt(currentTime.toDate())
-                .setExpiration(currentTime.plusSeconds(settings.getTokenExpirationTime()).toDate())
+                .setIssuedAt(Date.from(currentTime.toInstant()))
+                .setExpiration(Date.from(currentTime.plusSeconds(settings.getTokenExpirationTime()).toInstant()))
                 .signWith(SignatureAlgorithm.HS512, settings.getTokenSigningKey())
                 .compact();
 
@@ -129,11 +130,11 @@ public class JwtTokenFactory {
             throw new IllegalArgumentException("Cannot create JWT Token without username/email");
         }
 
-        DateTime currentTime = new DateTime();
+        ZonedDateTime currentTime = ZonedDateTime.now();
 
         UserPrincipal principal = securityUser.getUserPrincipal();
         Claims claims = Jwts.claims().setSubject(principal.getValue());
-        claims.put(SCOPES, Arrays.asList(Authority.REFRESH_TOKEN.name()));
+        claims.put(SCOPES, Collections.singletonList(Authority.REFRESH_TOKEN.name()));
         claims.put(USER_ID, securityUser.getId().getId().toString());
         claims.put(IS_PUBLIC, principal.getType() == UserPrincipal.Type.PUBLIC_ID);
 
@@ -141,8 +142,8 @@ public class JwtTokenFactory {
                 .setClaims(claims)
                 .setIssuer(settings.getTokenIssuer())
                 .setId(UUID.randomUUID().toString())
-                .setIssuedAt(currentTime.toDate())
-                .setExpiration(currentTime.plusSeconds(settings.getRefreshTokenExpTime()).toDate())
+                .setIssuedAt(Date.from(currentTime.toInstant()))
+                .setExpiration(Date.from(currentTime.plusSeconds(settings.getRefreshTokenExpTime()).toInstant()))
                 .signWith(SignatureAlgorithm.HS512, settings.getTokenSigningKey())
                 .compact();
 
diff --git a/application/src/test/java/org/thingsboard/server/controller/AbstractControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/AbstractControllerTest.java
index 19e4329..8d68bf8 100644
--- a/application/src/test/java/org/thingsboard/server/controller/AbstractControllerTest.java
+++ b/application/src/test/java/org/thingsboard/server/controller/AbstractControllerTest.java
@@ -106,6 +106,11 @@ public abstract class AbstractControllerTest {
     protected static final String CUSTOMER_USER_EMAIL = "testcustomer@thingsboard.org";
     private static final String CUSTOMER_USER_PASSWORD = "customer";
 
+    /** See {@link org.springframework.test.web.servlet.DefaultMvcResult#getAsyncResult(long)}
+     *  and {@link org.springframework.mock.web.MockAsyncContext#getTimeout()}
+     */
+    private static final long DEFAULT_TIMEOUT = -1L;
+
     protected MediaType contentType = new MediaType(MediaType.APPLICATION_JSON.getType(),
             MediaType.APPLICATION_JSON.getSubtype(),
             Charset.forName("utf8"));
@@ -336,7 +341,7 @@ public abstract class AbstractControllerTest {
     }
 
     protected <T> T doPost(String urlTemplate, T content, Class<T> responseClass, ResultMatcher resultMatcher, String... params) throws Exception {
-        return readResponse(doPost(urlTemplate, params).andExpect(resultMatcher), responseClass);
+        return readResponse(doPost(urlTemplate, content, params).andExpect(resultMatcher), responseClass);
     }
 
     protected <T> T doPost(String urlTemplate, T content, Class<T> responseClass, String... params) throws Exception {
@@ -344,7 +349,11 @@ public abstract class AbstractControllerTest {
     }
 
     protected <T> T doPostAsync(String urlTemplate, T content, Class<T> responseClass, ResultMatcher resultMatcher, String... params) throws Exception {
-        return readResponse(doPostAsync(urlTemplate, content, params).andExpect(resultMatcher), responseClass);
+        return readResponse(doPostAsync(urlTemplate, content, DEFAULT_TIMEOUT, params).andExpect(resultMatcher), responseClass);
+    }
+
+    protected <T> T doPostAsync(String urlTemplate, T content, Class<T> responseClass, ResultMatcher resultMatcher, Long timeout, String... params) throws Exception {
+        return readResponse(doPostAsync(urlTemplate, content, timeout, params).andExpect(resultMatcher), responseClass);
     }
 
     protected <T> T doDelete(String urlTemplate, Class<T> responseClass, String... params) throws Exception {
@@ -366,12 +375,13 @@ public abstract class AbstractControllerTest {
         return mockMvc.perform(postRequest);
     }
 
-    protected <T> ResultActions doPostAsync(String urlTemplate, T content, String... params)  throws Exception {
+    protected <T> ResultActions doPostAsync(String urlTemplate, T content, Long timeout, String... params)  throws Exception {
         MockHttpServletRequestBuilder postRequest = post(urlTemplate);
         setJwtToken(postRequest);
         String json = json(content);
         postRequest.contentType(contentType).content(json);
         MvcResult result = mockMvc.perform(postRequest).andReturn();
+        result.getAsyncResult(timeout);
         return mockMvc.perform(asyncDispatch(result));
     }
 
@@ -384,8 +394,8 @@ public abstract class AbstractControllerTest {
 
     protected void populateParams(MockHttpServletRequestBuilder request, String... params) {
         if (params != null && params.length > 0) {
-            Assert.assertEquals(params.length % 2, 0);
-            MultiValueMap<String, String> paramsMap = new LinkedMultiValueMap<String, String>();
+            Assert.assertEquals(0, params.length % 2);
+            MultiValueMap<String, String> paramsMap = new LinkedMultiValueMap<>();
             for (int i = 0; i < params.length; i += 2) {
                 paramsMap.add(params[i], params[i + 1]);
             }
diff --git a/application/src/test/java/org/thingsboard/server/mqtt/rpc/AbstractMqttServerSideRpcIntegrationTest.java b/application/src/test/java/org/thingsboard/server/mqtt/rpc/AbstractMqttServerSideRpcIntegrationTest.java
index ac474b8..8b4332c 100644
--- a/application/src/test/java/org/thingsboard/server/mqtt/rpc/AbstractMqttServerSideRpcIntegrationTest.java
+++ b/application/src/test/java/org/thingsboard/server/mqtt/rpc/AbstractMqttServerSideRpcIntegrationTest.java
@@ -15,21 +15,23 @@
  */
 package org.thingsboard.server.mqtt.rpc;
 
+import java.util.Arrays;
+
+import com.datastax.driver.core.utils.UUIDs;
+import com.fasterxml.jackson.core.type.TypeReference;
 import lombok.extern.slf4j.Slf4j;
 import org.apache.commons.lang3.StringUtils;
 import org.eclipse.paho.client.mqttv3.*;
 import org.junit.*;
-import org.springframework.http.HttpStatus;
-import org.springframework.web.client.HttpClientErrorException;
+import org.thingsboard.server.actors.plugin.PluginProcessingContext;
 import org.thingsboard.server.common.data.Device;
 import org.thingsboard.server.common.data.Tenant;
 import org.thingsboard.server.common.data.User;
+import org.thingsboard.server.common.data.page.TextPageData;
+import org.thingsboard.server.common.data.plugin.PluginMetaData;
 import org.thingsboard.server.common.data.security.Authority;
 import org.thingsboard.server.common.data.security.DeviceCredentials;
 import org.thingsboard.server.controller.AbstractControllerTest;
-import org.thingsboard.server.dao.service.DaoNoSqlTest;
-
-import java.util.UUID;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
@@ -42,15 +44,19 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
 public abstract class AbstractMqttServerSideRpcIntegrationTest extends AbstractControllerTest {
 
     private static final String MQTT_URL = "tcp://localhost:1883";
-    private static final String FAIL_MSG_IF_HTTP_CLIENT_ERROR_NOT_ENCOUNTERED = "HttpClientErrorException expected, but not encountered";
+    private static final Long TIME_TO_HANDLE_REQUEST = 500L;
 
     private Tenant savedTenant;
     private User tenantAdmin;
+    private Long asyncContextTimeoutToUseRpcPlugin;
+
 
     @Before
     public void beforeTest() throws Exception {
         loginSysAdmin();
 
+        asyncContextTimeoutToUseRpcPlugin = getAsyncContextTimeoutToUseRpcPlugin();
+
         Tenant tenant = new Tenant();
         tenant.setTitle("My tenant");
         savedTenant = doPost("/api/tenant", tenant, Tenant.class);
@@ -70,8 +76,7 @@ public abstract class AbstractMqttServerSideRpcIntegrationTest extends AbstractC
     public void afterTest() throws Exception {
         loginSysAdmin();
         if (savedTenant != null) {
-            doDelete("/api/tenant/" + savedTenant.getId().getId().toString())
-                    .andExpect(status().isOk());
+            doDelete("/api/tenant/" + savedTenant.getId().getId().toString()).andExpect(status().isOk());
         }
     }
 
@@ -102,7 +107,6 @@ public abstract class AbstractMqttServerSideRpcIntegrationTest extends AbstractC
     }
 
     @Test
-    @Ignore // TODO: figure out the right error code for this case. Ignored due to failure: expected 408 but was: 200
     public void testServerMqttOneWayRpcDeviceOffline() throws Exception {
         Device device = new Device();
         device.setName("Test One-Way Server-Side RPC Device Offline");
@@ -115,29 +119,19 @@ public abstract class AbstractMqttServerSideRpcIntegrationTest extends AbstractC
 
         String setGpioRequest = "{\"method\":\"setGpio\",\"params\":{\"pin\": \"23\",\"value\": 1}}";
         String deviceId = savedDevice.getId().getId().toString();
-        try {
-            doPost("/api/plugins/rpc/oneway/" + deviceId, setGpioRequest, String.class, status().is(408));
-            Assert.fail(FAIL_MSG_IF_HTTP_CLIENT_ERROR_NOT_ENCOUNTERED);
-        } catch (HttpClientErrorException e) {
-            log.error(e.getMessage(), e);
-            Assert.assertEquals(HttpStatus.REQUEST_TIMEOUT, e.getStatusCode());
-            Assert.assertEquals("408 null", e.getMessage());
-        }
+
+        doPostAsync("/api/plugins/rpc/oneway/" + deviceId, setGpioRequest, String.class, status().isRequestTimeout(),
+                asyncContextTimeoutToUseRpcPlugin);
     }
 
     @Test
-    @Ignore // TODO: figure out the right error code for this case. Ignored due to failure: expected 400 (404?) but was: 401
     public void testServerMqttOneWayRpcDeviceDoesNotExist() throws Exception {
         String setGpioRequest = "{\"method\":\"setGpio\",\"params\":{\"pin\": \"23\",\"value\": 1}}";
-        String nonExistentDeviceId = UUID.randomUUID().toString();
-        try {
-            doPostAsync("/api/plugins/rpc/oneway/" + nonExistentDeviceId, setGpioRequest, String.class, status().is(400));
-            Assert.fail(FAIL_MSG_IF_HTTP_CLIENT_ERROR_NOT_ENCOUNTERED);
-        } catch (HttpClientErrorException e) {
-            log.error(e.getMessage(), e);
-            Assert.assertEquals(HttpStatus.BAD_REQUEST, e.getStatusCode());
-            Assert.assertEquals("400 null", e.getMessage());
-        }
+        String nonExistentDeviceId = UUIDs.timeBased().toString();
+
+        String result = doPostAsync("/api/plugins/rpc/oneway/" + nonExistentDeviceId, setGpioRequest, String.class,
+                status().isNotFound());
+        Assert.assertEquals(PluginProcessingContext.DEVICE_WITH_REQUESTED_ID_NOT_FOUND, result);
     }
 
     @Test
@@ -168,7 +162,6 @@ public abstract class AbstractMqttServerSideRpcIntegrationTest extends AbstractC
     }
 
     @Test
-    @Ignore // TODO: figure out the right error code for this case. Ignored due to failure: expected 408 but was: 200
     public void testServerMqttTwoWayRpcDeviceOffline() throws Exception {
         Device device = new Device();
         device.setName("Test Two-Way Server-Side RPC Device Offline");
@@ -181,29 +174,19 @@ public abstract class AbstractMqttServerSideRpcIntegrationTest extends AbstractC
 
         String setGpioRequest = "{\"method\":\"setGpio\",\"params\":{\"pin\": \"23\",\"value\": 1}}";
         String deviceId = savedDevice.getId().getId().toString();
-        try {
-            doPost("/api/plugins/rpc/twoway/" + deviceId, setGpioRequest, String.class, status().is(408));
-            Assert.fail(FAIL_MSG_IF_HTTP_CLIENT_ERROR_NOT_ENCOUNTERED);
-        } catch (HttpClientErrorException e) {
-            log.error(e.getMessage(), e);
-            Assert.assertEquals(HttpStatus.REQUEST_TIMEOUT, e.getStatusCode());
-            Assert.assertEquals("408 null", e.getMessage());
-        }
+
+        doPostAsync("/api/plugins/rpc/twoway/" + deviceId, setGpioRequest, String.class, status().isRequestTimeout(),
+                asyncContextTimeoutToUseRpcPlugin);
     }
 
     @Test
-    @Ignore // TODO: figure out the right error code for this case. Ignored due to failure: expected 400 (404?) but was: 401
     public void testServerMqttTwoWayRpcDeviceDoesNotExist() throws Exception {
         String setGpioRequest = "{\"method\":\"setGpio\",\"params\":{\"pin\": \"23\",\"value\": 1}}";
-        String nonExistentDeviceId = UUID.randomUUID().toString();
-        try {
-            doPostAsync("/api/plugins/rpc/oneway/" + nonExistentDeviceId, setGpioRequest, String.class, status().is(400));
-            Assert.fail(FAIL_MSG_IF_HTTP_CLIENT_ERROR_NOT_ENCOUNTERED);
-        } catch (HttpClientErrorException e) {
-            log.error(e.getMessage(), e);
-            Assert.assertEquals(HttpStatus.BAD_REQUEST, e.getStatusCode());
-            Assert.assertEquals("400 null", e.getMessage());
-        }
+        String nonExistentDeviceId = UUIDs.timeBased().toString();
+
+        String result = doPostAsync("/api/plugins/rpc/twoway/" + nonExistentDeviceId, setGpioRequest, String.class,
+                status().isNotFound());
+        Assert.assertEquals(PluginProcessingContext.DEVICE_WITH_REQUESTED_ID_NOT_FOUND, result);
     }
 
     private Device getSavedDevice(Device device) throws Exception {
@@ -214,6 +197,13 @@ public abstract class AbstractMqttServerSideRpcIntegrationTest extends AbstractC
         return doGet("/api/device/" + savedDevice.getId().getId().toString() + "/credentials", DeviceCredentials.class);
     }
 
+    private Long getAsyncContextTimeoutToUseRpcPlugin() throws Exception {
+        TextPageData<PluginMetaData> plugins = doGetTyped("/api/plugin/system?limit=1&textSearch=system rpc plugin",
+                new TypeReference<TextPageData<PluginMetaData>>(){});
+        Long systemRpcPluginTimeout = plugins.getData().iterator().next().getConfiguration().get("defaultTimeout").asLong();
+        return systemRpcPluginTimeout + TIME_TO_HANDLE_REQUEST;
+    }
+
     private static class TestMqttCallback implements MqttCallback {
 
         private final MqttAsyncClient client;
@@ -228,10 +218,10 @@ public abstract class AbstractMqttServerSideRpcIntegrationTest extends AbstractC
 
         @Override
         public void messageArrived(String requestTopic, MqttMessage mqttMessage) throws Exception {
-            log.info("Message Arrived: " + mqttMessage.getPayload().toString());
+            log.info("Message Arrived: " + Arrays.toString(mqttMessage.getPayload()));
             MqttMessage message = new MqttMessage();
             String responseTopic = requestTopic.replace("request", "response");
-            message.setPayload("{\"value1\":\"A\", \"value2\":\"B\"}".getBytes());
+            message.setPayload("{\"value1\":\"A\", \"value2\":\"B\"}".getBytes("UTF-8"));
             client.publish(responseTopic, message);
         }
 
diff --git a/dao/src/main/java/org/thingsboard/server/dao/customer/CustomerService.java b/dao/src/main/java/org/thingsboard/server/dao/customer/CustomerService.java
index 4600d9f..1d1abda 100644
--- a/dao/src/main/java/org/thingsboard/server/dao/customer/CustomerService.java
+++ b/dao/src/main/java/org/thingsboard/server/dao/customer/CustomerService.java
@@ -22,20 +22,24 @@ import org.thingsboard.server.common.data.id.TenantId;
 import org.thingsboard.server.common.data.page.TextPageData;
 import org.thingsboard.server.common.data.page.TextPageLink;
 
+import java.util.Optional;
+
 public interface CustomerService {
 
     Customer findCustomerById(CustomerId customerId);
 
+    Optional<Customer> findCustomerByTenantIdAndTitle(TenantId tenantId, String title);
+
     ListenableFuture<Customer> findCustomerByIdAsync(CustomerId customerId);
 
     Customer saveCustomer(Customer customer);
-    
+
     void deleteCustomer(CustomerId customerId);
 
     Customer findOrCreatePublicCustomer(TenantId tenantId);
 
     TextPageData<Customer> findCustomersByTenantId(TenantId tenantId, TextPageLink pageLink);
-    
+
     void deleteCustomersByTenantId(TenantId tenantId);
 
 }
diff --git a/dao/src/main/java/org/thingsboard/server/dao/customer/CustomerServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/customer/CustomerServiceImpl.java
index e8ec21f..f76d654 100644
--- a/dao/src/main/java/org/thingsboard/server/dao/customer/CustomerServiceImpl.java
+++ b/dao/src/main/java/org/thingsboard/server/dao/customer/CustomerServiceImpl.java
@@ -52,6 +52,7 @@ public class CustomerServiceImpl extends AbstractEntityService implements Custom
 
     private static final String PUBLIC_CUSTOMER_TITLE = "Public";
     public static final String INCORRECT_CUSTOMER_ID = "Incorrect customerId ";
+    public static final String INCORRECT_TENANT_ID = "Incorrect tenantId ";
 
     @Autowired
     private CustomerDao customerDao;
@@ -79,6 +80,13 @@ public class CustomerServiceImpl extends AbstractEntityService implements Custom
     }
 
     @Override
+    public Optional<Customer> findCustomerByTenantIdAndTitle(TenantId tenantId, String title) {
+        log.trace("Executing findCustomerByTenantIdAndTitle [{}] [{}]", tenantId, title);
+        validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
+        return customerDao.findCustomersByTenantIdAndTitle(tenantId.getId(), title);
+    }
+
+    @Override
     public ListenableFuture<Customer> findCustomerByIdAsync(CustomerId customerId) {
         log.trace("Executing findCustomerByIdAsync [{}]", customerId);
         validateId(customerId, INCORRECT_CUSTOMER_ID + customerId);

pom.xml 8(+1 -7)

diff --git a/pom.xml b/pom.xml
index e45fc3e..4b77abb 100755
--- a/pom.xml
+++ b/pom.xml
@@ -33,8 +33,7 @@
         <spring.version>4.3.4.RELEASE</spring.version>
         <spring-security.version>4.2.0.RELEASE</spring-security.version>
         <jjwt.version>0.7.0</jjwt.version>
-        <joda-time.version>2.4</joda-time.version>
-        <json-path.version>2.2.0</json-path.version>
+         <json-path.version>2.2.0</json-path.version>
         <junit.version>4.12</junit.version>
         <slf4j.version>1.7.7</slf4j.version>
         <logback.version>1.2.3</logback.version>
@@ -484,11 +483,6 @@
                 <version>${jjwt.version}</version>
             </dependency>
             <dependency>
-                <groupId>joda-time</groupId>
-                <artifactId>joda-time</artifactId>
-                <version>${joda-time.version}</version>
-            </dependency>
-            <dependency>
                 <groupId>org.apache.velocity</groupId>
                 <artifactId>velocity</artifactId>
                 <version>${velocity.version}</version>
diff --git a/tools/src/main/java/org/thingsboard/client/tools/RestClient.java b/tools/src/main/java/org/thingsboard/client/tools/RestClient.java
index 7000496..0baf1db 100644
--- a/tools/src/main/java/org/thingsboard/client/tools/RestClient.java
+++ b/tools/src/main/java/org/thingsboard/client/tools/RestClient.java
@@ -29,13 +29,12 @@ import org.springframework.web.client.RestTemplate;
 import org.thingsboard.server.common.data.Customer;
 import org.thingsboard.server.common.data.Device;
 import org.thingsboard.server.common.data.alarm.Alarm;
-import org.thingsboard.server.common.data.alarm.AlarmSeverity;
-import org.thingsboard.server.common.data.alarm.AlarmStatus;
 import org.thingsboard.server.common.data.asset.Asset;
 import org.thingsboard.server.common.data.id.AssetId;
 import org.thingsboard.server.common.data.id.CustomerId;
 import org.thingsboard.server.common.data.id.DeviceId;
 import org.thingsboard.server.common.data.id.EntityId;
+import org.thingsboard.server.common.data.relation.EntityRelation;
 import org.thingsboard.server.common.data.security.DeviceCredentials;
 
 import java.io.IOException;
@@ -78,6 +77,36 @@ public class RestClient implements ClientHttpRequestInterceptor {
         }
     }
 
+    public Optional<Customer> findCustomer(String title) {
+        Map<String, String> params = new HashMap<String, String>();
+        params.put("customerTitle", title);
+        try {
+            ResponseEntity<Customer> customerEntity = restTemplate.getForEntity(baseURL + "/api/tenant/customers?customerTitle={customerTitle}", Customer.class, params);
+            return Optional.of(customerEntity.getBody());
+        } catch (HttpClientErrorException exception) {
+            if (exception.getStatusCode() == HttpStatus.NOT_FOUND) {
+                return Optional.empty();
+            } else {
+                throw exception;
+            }
+        }
+    }
+
+    public Optional<Asset> findAsset(String name) {
+        Map<String, String> params = new HashMap<String, String>();
+        params.put("assetName", name);
+        try {
+            ResponseEntity<Asset> assetEntity = restTemplate.getForEntity(baseURL + "/api/tenant/assets?assetName={assetName}", Asset.class, params);
+            return Optional.of(assetEntity.getBody());
+        } catch (HttpClientErrorException exception) {
+            if (exception.getStatusCode() == HttpStatus.NOT_FOUND) {
+                return Optional.empty();
+            } else {
+                throw exception;
+            }
+        }
+    }
+
     public Customer createCustomer(String title) {
         Customer customer = new Customer();
         customer.setTitle(title);
@@ -112,6 +141,14 @@ public class RestClient implements ClientHttpRequestInterceptor {
                 customerId.toString(), assetId.toString()).getBody();
     }
 
+    public EntityRelation makeRelation(String relationType, EntityId idFrom, EntityId idTo) {
+        EntityRelation relation = new EntityRelation();
+        relation.setFrom(idFrom);
+        relation.setTo(idTo);
+        relation.setType(relationType);
+        return restTemplate.postForEntity(baseURL + "/api/relation", relation, EntityRelation.class).getBody();
+    }
+
     public DeviceCredentials getCredentials(DeviceId id) {
         return restTemplate.getForEntity(baseURL + "/api/device/" + id.getId().toString() + "/credentials", DeviceCredentials.class).getBody();
     }
diff --git a/tools/src/main/shell/client.keygen.sh b/tools/src/main/shell/client.keygen.sh
index 2e83ef0..bb59077 100755
--- a/tools/src/main/shell/client.keygen.sh
+++ b/tools/src/main/shell/client.keygen.sh
@@ -74,13 +74,13 @@ echo "Generating SSL Key Pair..."
 
 keytool -genkeypair -v \
   -alias $CLIENT_KEY_ALIAS \
-  -dname "CN=$DOMAIN_SUFFIX, OU=$ORGANIZATIONAL_UNIT, O=$ORGANIZATION, L=$CITY, ST=$STATE_OR_PROVINCE, C=$TWO_LETTER_COUNTRY_CODE" \ 
   -keystore $CLIENT_FILE_PREFIX.jks \
   -keypass $CLIENT_KEY_PASSWORD \
   -storepass $CLIENT_KEYSTORE_PASSWORD \
   -keyalg RSA \
   -keysize 2048 \
-  -validity 9999
+  -validity 9999 \
+  -dname "CN=$DOMAIN_SUFFIX, OU=$ORGANIZATIONAL_UNIT, O=$ORGANIZATION, L=$CITY, ST=$STATE_OR_PROVINCE, C=$TWO_LETTER_COUNTRY_CODE"
 
 echo "Converting keystore to pkcs12"
 keytool -importkeystore  \
diff --git a/tools/src/main/shell/keygen.properties b/tools/src/main/shell/keygen.properties
index 8dd11f2..a01b782 100644
--- a/tools/src/main/shell/keygen.properties
+++ b/tools/src/main/shell/keygen.properties
@@ -17,7 +17,7 @@
 DOMAIN_SUFFIX="$(hostname)"
 ORGANIZATIONAL_UNIT=Thingsboard
 ORGANIZATION=Thingsboard
-CITY=San Francisco
+CITY=SF
 STATE_OR_PROVINCE=CA
 TWO_LETTER_COUNTRY_CODE=US
 
diff --git a/ui/src/app/admin/general-settings.tpl.html b/ui/src/app/admin/general-settings.tpl.html
index f628651..50342d3 100644
--- a/ui/src/app/admin/general-settings.tpl.html
+++ b/ui/src/app/admin/general-settings.tpl.html
@@ -22,11 +22,11 @@
            		<span translate class="md-headline">admin.general-settings</span>
           </md-card-title-text>
         </md-card-title>	
-   	    <md-progress-linear md-mode="indeterminate" ng-disabled="!loading" ng-show="loading"></md-progress-linear>
-  	    <span style="min-height: 5px;" flex="" ng-show="!loading"></span>
+   	    <md-progress-linear md-mode="indeterminate" ng-disabled="!$root.loading" ng-show="$root.loading"></md-progress-linear>
+  	    <span style="min-height: 5px;" flex="" ng-show="!$root.loading"></span>
         <md-card-content>	        
 			<form name="vm.settingsForm" ng-submit="vm.save()" tb-confirm-on-exit confirm-form="vm.settingsForm">
-				<fieldset ng-disabled="loading">
+				<fieldset ng-disabled="$root.loading">
 					<md-input-container class="md-block">
 						<label translate>admin.base-url</label>
 						<input required name="baseUrl" ng-model="vm.settings.jsonValue.baseUrl">
@@ -35,7 +35,7 @@
 	       				</div>				
 					</md-input-container>
 					<div layout="row" layout-align="end center" width="100%" layout-wrap>
-						<md-button ng-disabled="loading || vm.settingsForm.$invalid || !vm.settingsForm.$dirty" type="submit" class="md-raised md-primary">{{'action.save' | translate}}</md-button>
+						<md-button ng-disabled="$root.loading || vm.settingsForm.$invalid || !vm.settingsForm.$dirty" type="submit" class="md-raised md-primary">{{'action.save' | translate}}</md-button>
 					</div>
 				</fieldset>
 			</form>
diff --git a/ui/src/app/admin/outgoing-mail-settings.tpl.html b/ui/src/app/admin/outgoing-mail-settings.tpl.html
index fd8476a..3c59593 100644
--- a/ui/src/app/admin/outgoing-mail-settings.tpl.html
+++ b/ui/src/app/admin/outgoing-mail-settings.tpl.html
@@ -24,11 +24,11 @@
 			  <div id="help-container"></div>
           </md-card-title-text>
         </md-card-title>	
-   	    <md-progress-linear md-mode="indeterminate" ng-disabled="!loading" ng-show="loading"></md-progress-linear>
-  	    <span style="min-height: 5px;" flex="" ng-show="!loading"></span>
+   	    <md-progress-linear md-mode="indeterminate" ng-disabled="!$root.loading" ng-show="$root.loading"></md-progress-linear>
+  	    <span style="min-height: 5px;" flex="" ng-show="!$root.loading"></span>
         <md-card-content>	        		
 			<form name="vm.settingsForm" ng-submit="vm.save()" tb-confirm-on-exit confirm-form="vm.settingsForm">
-				<fieldset ng-disabled="loading">
+				<fieldset ng-disabled="$root.loading">
 					<md-input-container class="md-block">
 						<label translate>admin.mail-from</label>
 						<input required name="mailFrom" ng-model="vm.settings.jsonValue.mailFrom">
@@ -38,7 +38,7 @@
 					</md-input-container>
 					<md-input-container class="md-block">
 						<label translate>admin.smtp-protocol</label>
-			            <md-select ng-disabled="loading" ng-model="vm.settings.jsonValue.smtpProtocol">
+			            <md-select ng-disabled="$root.loading" ng-model="vm.settings.jsonValue.smtpProtocol">
 			              	<md-option ng-repeat="smtpProtocol in vm.smtpProtocols" value="{{smtpProtocol}}">
 			                	{{smtpProtocol.toUpperCase()}}
 			             	 </md-option>
@@ -78,7 +78,7 @@
 				              <div translate ng-message="md-maxlength">admin.timeout-invalid</div>
 	       				</div>				
 					</md-input-container>	
-					<md-checkbox ng-disabled="loading" ng-true-value="'true'" ng-false-value="'false'"
+					<md-checkbox ng-disabled="$root.loading" ng-true-value="'true'" ng-false-value="'false'"
 								 aria-label="{{ 'admin.enable-tls' | translate }}" ng-model="vm.settings.jsonValue.enableTls">{{ 'admin.enable-tls' | translate }}</md-checkbox>
 					<md-input-container class="md-block">
 						<label translate>common.username</label>
@@ -89,8 +89,8 @@
 						<input name="password" placeholder="{{ 'common.enter-password' | translate }}" type="password" ng-model="vm.settings.jsonValue.password">
 					</md-input-container>				
 					<div layout="row" layout-align="end center" width="100%" layout-wrap>
-						<md-button ng-disabled="loading || vm.settingsForm.$invalid" ng-click="vm.sendTestMail()" class="md-raised">{{'admin.send-test-mail' | translate}}</md-button>
-						<md-button ng-disabled="loading || vm.settingsForm.$invalid || !vm.settingsForm.$dirty" type="submit" class="md-raised md-primary">{{'action.save' | translate}}</md-button>
+						<md-button ng-disabled="$root.loading || vm.settingsForm.$invalid" ng-click="vm.sendTestMail()" class="md-raised">{{'admin.send-test-mail' | translate}}</md-button>
+						<md-button ng-disabled="$root.loading || vm.settingsForm.$invalid || !vm.settingsForm.$dirty" type="submit" class="md-raised md-primary">{{'action.save' | translate}}</md-button>
 					</div>
 				</fieldset>
 			</form>
diff --git a/ui/src/app/alarm/alarm-details-dialog.tpl.html b/ui/src/app/alarm/alarm-details-dialog.tpl.html
index b090052..cc213d1 100644
--- a/ui/src/app/alarm/alarm-details-dialog.tpl.html
+++ b/ui/src/app/alarm/alarm-details-dialog.tpl.html
@@ -87,7 +87,7 @@
         <md-button ng-if="vm.allowAcknowledgment && (vm.alarm.status==vm.types.alarmStatus.activeUnack ||
                           vm.alarm.status==vm.types.alarmStatus.clearedUnack)"
                    class="md-raised md-primary"
-                   ng-disabled="loading"
+                   ng-disabled="$root.loading"
                    ng-click="vm.acknowledge()"
                    style="margin-right:20px;">{{ 'alarm.acknowledge' |
             translate }}
@@ -95,12 +95,12 @@
         <md-button ng-if="vm.allowClear && (vm.alarm.status==vm.types.alarmStatus.activeAck ||
                           vm.alarm.status==vm.types.alarmStatus.activeUnack)"
                    class="md-raised md-primary"
-                   ng-disabled="loading"
+                   ng-disabled="$root.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' |
+        <md-button ng-disabled="$root.loading" ng-click="vm.close()" style="margin-right:20px;">{{ 'action.close' |
             translate }}
         </md-button>
     </md-dialog-actions>
diff --git a/ui/src/app/alarm/alarm-table.tpl.html b/ui/src/app/alarm/alarm-table.tpl.html
index c32e39a..98d42a2 100644
--- a/ui/src/app/alarm/alarm-table.tpl.html
+++ b/ui/src/app/alarm/alarm-table.tpl.html
@@ -19,7 +19,7 @@
     <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-select ng-model="alarmSearchStatus" ng-disabled="$root.loading">
                 <md-option ng-repeat="searchStatus in types.alarmSearchStatus" ng-value="searchStatus">
                     {{ ('alarm.search-status.' + searchStatus) | translate }}
                 </md-option>
@@ -31,8 +31,8 @@
         <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-progress-linear style="max-height: 0px;" md-mode="indeterminate" ng-disabled="!$root.loading"
+                                ng-show="$root.loading"></md-progress-linear>
             <md-divider></md-divider>
             <span translate layout-align="center center"
                   style="margin-top: 25px;"
diff --git a/ui/src/app/api/asset.service.js b/ui/src/app/api/asset.service.js
index 2fbb11d..51ea107 100644
--- a/ui/src/app/api/asset.service.js
+++ b/ui/src/app/api/asset.service.js
@@ -265,10 +265,10 @@ function AssetService($http, $q, customerService, userService) {
         return deferred.promise;
     }
 
-    function getAssetTypes() {
+    function getAssetTypes(config) {
         var deferred = $q.defer();
         var url = '/api/asset/types';
-        $http.get(url).then(function success(response) {
+        $http.get(url, config).then(function success(response) {
             deferred.resolve(response.data);
         }, function fail() {
             deferred.reject();
diff --git a/ui/src/app/api/attribute.service.js b/ui/src/app/api/attribute.service.js
index 3f4b971..6e1aeec 100644
--- a/ui/src/app/api/attribute.service.js
+++ b/ui/src/app/api/attribute.service.js
@@ -35,7 +35,7 @@ function AttributeService($http, $q, $filter, types, telemetryWebsocketService) 
 
     return service;
 
-    function getEntityKeys(entityType, entityId, query, type) {
+    function getEntityKeys(entityType, entityId, query, type, config) {
         var deferred = $q.defer();
         var url = '/api/plugins/telemetry/' + entityType + '/' + entityId + '/keys/';
         if (type === types.dataKeyType.timeseries) {
@@ -43,7 +43,7 @@ function AttributeService($http, $q, $filter, types, telemetryWebsocketService) 
         } else if (type === types.dataKeyType.attribute) {
             url += 'attributes';
         }
-        $http.get(url, null).then(function success(response) {
+        $http.get(url, config).then(function success(response) {
             var result = [];
             if (response.data) {
                 if (query) {
diff --git a/ui/src/app/api/customer.service.js b/ui/src/app/api/customer.service.js
index b36b44a..b520a09 100644
--- a/ui/src/app/api/customer.service.js
+++ b/ui/src/app/api/customer.service.js
@@ -32,7 +32,7 @@ function CustomerService($http, $q, types) {
 
     return service;
 
-    function getCustomers(pageLink) {
+    function getCustomers(pageLink, config) {
         var deferred = $q.defer();
         var url = '/api/customers?limit=' + pageLink.limit;
         if (angular.isDefined(pageLink.textSearch)) {
@@ -44,7 +44,7 @@ function CustomerService($http, $q, types) {
         if (angular.isDefined(pageLink.textOffset)) {
             url += '&textOffset=' + pageLink.textOffset;
         }
-        $http.get(url, null).then(function success(response) {
+        $http.get(url, config).then(function success(response) {
             deferred.resolve(response.data);
         }, function fail() {
             deferred.reject();
@@ -52,10 +52,10 @@ function CustomerService($http, $q, types) {
         return deferred.promise;
     }
 
-    function getCustomer(customerId) {
+    function getCustomer(customerId, config) {
         var deferred = $q.defer();
         var url = '/api/customer/' + customerId;
-        $http.get(url, null).then(function success(response) {
+        $http.get(url, config).then(function success(response) {
             deferred.resolve(response.data);
         }, function fail(response) {
             deferred.reject(response.data);
diff --git a/ui/src/app/api/dashboard.service.js b/ui/src/app/api/dashboard.service.js
index 362a539..3082bd3 100644
--- a/ui/src/app/api/dashboard.service.js
+++ b/ui/src/app/api/dashboard.service.js
@@ -43,7 +43,7 @@ function DashboardService($rootScope, $http, $q, $location, customerService) {
 
     return service;
 
-    function getTenantDashboardsByTenantId(tenantId, pageLink) {
+    function getTenantDashboardsByTenantId(tenantId, pageLink, config) {
         var deferred = $q.defer();
         var url = '/api/tenant/' + tenantId + '/dashboards?limit=' + pageLink.limit;
         if (angular.isDefined(pageLink.textSearch)) {
@@ -55,7 +55,7 @@ function DashboardService($rootScope, $http, $q, $location, customerService) {
         if (angular.isDefined(pageLink.textOffset)) {
             url += '&textOffset=' + pageLink.textOffset;
         }
-        $http.get(url, null).then(function success(response) {
+        $http.get(url, config).then(function success(response) {
             deferred.resolve(response.data);
         }, function fail() {
             deferred.reject();
@@ -63,7 +63,7 @@ function DashboardService($rootScope, $http, $q, $location, customerService) {
         return deferred.promise;
     }
 
-    function getTenantDashboards(pageLink, applyCustomersInfo) {
+    function getTenantDashboards(pageLink, applyCustomersInfo, config) {
         var deferred = $q.defer();
         var url = '/api/tenant/dashboards?limit=' + pageLink.limit;
         if (angular.isDefined(pageLink.textSearch)) {
@@ -75,7 +75,7 @@ function DashboardService($rootScope, $http, $q, $location, customerService) {
         if (angular.isDefined(pageLink.textOffset)) {
             url += '&textOffset=' + pageLink.textOffset;
         }
-        $http.get(url, null).then(function success(response) {
+        $http.get(url, config).then(function success(response) {
             if (applyCustomersInfo) {
                 customerService.applyAssignedCustomersInfo(response.data.data).then(
                     function success(data) {
@@ -95,7 +95,7 @@ function DashboardService($rootScope, $http, $q, $location, customerService) {
         return deferred.promise;
     }
 
-    function getCustomerDashboards(customerId, pageLink, applyCustomersInfo) {
+    function getCustomerDashboards(customerId, pageLink, applyCustomersInfo, config) {
         var deferred = $q.defer();
         var url = '/api/customer/' + customerId + '/dashboards?limit=' + pageLink.limit;
         if (angular.isDefined(pageLink.textSearch)) {
@@ -107,7 +107,7 @@ function DashboardService($rootScope, $http, $q, $location, customerService) {
         if (angular.isDefined(pageLink.textOffset)) {
             url += '&textOffset=' + pageLink.textOffset;
         }
-        $http.get(url, null).then(function success(response) {
+        $http.get(url, config).then(function success(response) {
             if (applyCustomersInfo) {
                 customerService.applyAssignedCustomerInfo(response.data.data, customerId).then(
                     function success(data) {
@@ -158,10 +158,10 @@ function DashboardService($rootScope, $http, $q, $location, customerService) {
         return deferred.promise;
     }
 
-    function getDashboardInfo(dashboardId) {
+    function getDashboardInfo(dashboardId, config) {
         var deferred = $q.defer();
         var url = '/api/dashboard/info/' + dashboardId;
-        $http.get(url, null).then(function success(response) {
+        $http.get(url, config).then(function success(response) {
             deferred.resolve(response.data);
         }, function fail() {
             deferred.reject();
diff --git a/ui/src/app/api/device.service.js b/ui/src/app/api/device.service.js
index b1d1bb6..bb4249e 100644
--- a/ui/src/app/api/device.service.js
+++ b/ui/src/app/api/device.service.js
@@ -293,10 +293,10 @@ function DeviceService($http, $q, attributeService, customerService, types) {
         return deferred.promise;
     }
 
-    function getDeviceTypes() {
+    function getDeviceTypes(config) {
         var deferred = $q.defer();
         var url = '/api/device/types';
-        $http.get(url).then(function success(response) {
+        $http.get(url, config).then(function success(response) {
             deferred.resolve(response.data);
         }, function fail() {
             deferred.reject();
diff --git a/ui/src/app/api/entity.service.js b/ui/src/app/api/entity.service.js
index 0022607..df1c3e0 100644
--- a/ui/src/app/api/entity.service.js
+++ b/ui/src/app/api/entity.service.js
@@ -56,22 +56,22 @@ function EntityService($http, $q, $filter, $translate, $log, userService, device
                 promise = assetService.getAsset(entityId, true, config);
                 break;
             case types.entityType.tenant:
-                promise = tenantService.getTenant(entityId);
+                promise = tenantService.getTenant(entityId, config);
                 break;
             case types.entityType.customer:
-                promise = customerService.getCustomer(entityId);
+                promise = customerService.getCustomer(entityId, config);
                 break;
             case types.entityType.rule:
-                promise = ruleService.getRule(entityId);
+                promise = ruleService.getRule(entityId, config);
                 break;
             case types.entityType.plugin:
-                promise = pluginService.getPlugin(entityId);
+                promise = pluginService.getPlugin(entityId, config);
                 break;
             case types.entityType.dashboard:
-                promise = dashboardService.getDashboardInfo(entityId);
+                promise = dashboardService.getDashboardInfo(entityId, config);
                 break;
             case types.entityType.user:
-                promise = userService.getUser(entityId);
+                promise = userService.getUser(entityId, true, config);
                 break;
             case types.entityType.alarm:
                 $log.error('Get Alarm Entity is not implemented!');
@@ -136,22 +136,28 @@ function EntityService($http, $q, $filter, $translate, $log, userService, device
                 promise = assetService.getAssets(entityIds, config);
                 break;
             case types.entityType.tenant:
-                promise = getEntitiesByIdsPromise(tenantService.getTenant, entityIds);
+                promise = getEntitiesByIdsPromise(
+                    (id) => tenantService.getTenant(id, config), entityIds);
                 break;
             case types.entityType.customer:
-                promise = getEntitiesByIdsPromise(customerService.getCustomer, entityIds);
+                promise = getEntitiesByIdsPromise(
+                    (id) => customerService.getCustomer(id, config), entityIds);
                 break;
             case types.entityType.rule:
-                promise = getEntitiesByIdsPromise(ruleService.getRule, entityIds);
+                promise = getEntitiesByIdsPromise(
+                    (id) => ruleService.getRule(id, config), entityIds);
                 break;
             case types.entityType.plugin:
-                promise = getEntitiesByIdsPromise(pluginService.getPlugin, entityIds);
+                promise = getEntitiesByIdsPromise(
+                    (id) => pluginService.getPlugin(id, config), entityIds);
                 break;
             case types.entityType.dashboard:
-                promise = getEntitiesByIdsPromise(dashboardService.getDashboardInfo, entityIds);
+                promise = getEntitiesByIdsPromise(
+                    (id) => dashboardService.getDashboardInfo(id, config), entityIds);
                 break;
             case types.entityType.user:
-                promise = getEntitiesByIdsPromise(userService.getUser, entityIds);
+                promise = getEntitiesByIdsPromise(
+                    (id) => userService.getUser(id, true, config), entityIds);
                 break;
             case types.entityType.alarm:
                 $log.error('Get Alarm Entity is not implemented!');
@@ -178,11 +184,11 @@ function EntityService($http, $q, $filter, $translate, $log, userService, device
         return deferred.promise;
     }
 
-    function getSingleTenantByPageLinkPromise(pageLink) {
+    function getSingleTenantByPageLinkPromise(pageLink, config) {
         var user = userService.getCurrentUser();
         var tenantId = user.tenantId;
         var deferred = $q.defer();
-        tenantService.getTenant(tenantId).then(
+        tenantService.getTenant(tenantId, config).then(
             function success(tenant) {
                 var tenantName = tenant.name;
                 var result = {
@@ -202,11 +208,11 @@ function EntityService($http, $q, $filter, $translate, $log, userService, device
         return deferred.promise;
     }
 
-    function getSingleCustomerByPageLinkPromise(pageLink) {
+    function getSingleCustomerByPageLinkPromise(pageLink, config) {
         var user = userService.getCurrentUser();
         var customerId = user.customerId;
         var deferred = $q.defer();
-        customerService.getCustomer(customerId).then(
+        customerService.getCustomer(customerId, config).then(
             function success(customer) {
                 var customerName = customer.name;
                 var result = {
@@ -247,29 +253,29 @@ function EntityService($http, $q, $filter, $translate, $log, userService, device
                 break;
             case types.entityType.tenant:
                 if (user.authority === 'TENANT_ADMIN') {
-                    promise = getSingleTenantByPageLinkPromise(pageLink);
+                    promise = getSingleTenantByPageLinkPromise(pageLink, config);
                 } else {
-                    promise = tenantService.getTenants(pageLink);
+                    promise = tenantService.getTenants(pageLink, config);
                 }
                 break;
             case types.entityType.customer:
                 if (user.authority === 'CUSTOMER_USER') {
-                    promise = getSingleCustomerByPageLinkPromise(pageLink);
+                    promise = getSingleCustomerByPageLinkPromise(pageLink, config);
                 } else {
-                    promise = customerService.getCustomers(pageLink);
+                    promise = customerService.getCustomers(pageLink, config);
                 }
                 break;
             case types.entityType.rule:
-                promise = ruleService.getAllRules(pageLink);
+                promise = ruleService.getAllRules(pageLink, config);
                 break;
             case types.entityType.plugin:
-                promise = pluginService.getAllPlugins(pageLink);
+                promise = pluginService.getAllPlugins(pageLink, config);
                 break;
             case types.entityType.dashboard:
                 if (user.authority === 'CUSTOMER_USER') {
-                    promise = dashboardService.getCustomerDashboards(customerId, pageLink, false);
+                    promise = dashboardService.getCustomerDashboards(customerId, pageLink, false, config);
                 } else {
-                    promise = dashboardService.getTenantDashboards(pageLink, false);
+                    promise = dashboardService.getTenantDashboards(pageLink, false, config);
                 }
                 break;
             case types.entityType.user:
@@ -426,7 +432,7 @@ function EntityService($http, $q, $filter, $translate, $log, userService, device
         var stateEntityId = getStateEntityId(filter, stateParams);
         switch (filter.type) {
             case types.aliasFilterType.singleEntity.value:
-                getEntity(filter.singleEntity.entityType, filter.singleEntity.id).then(
+                getEntity(filter.singleEntity.entityType, filter.singleEntity.id, {ignoreLoading: true}).then(
                     function success(entity) {
                         result.entities = entitiesToEntitiesInfo([entity]);
                         deferred.resolve(result);
@@ -437,7 +443,7 @@ function EntityService($http, $q, $filter, $translate, $log, userService, device
                 );
                 break;
             case types.aliasFilterType.entityList.value:
-                getEntities(filter.entityType, filter.entityList).then(
+                getEntities(filter.entityType, filter.entityList, {ignoreLoading: true}).then(
                     function success(entities) {
                         if (entities && entities.length || !failOnEmpty) {
                             result.entities = entitiesToEntitiesInfo(entities);
@@ -452,7 +458,7 @@ function EntityService($http, $q, $filter, $translate, $log, userService, device
                 );
                 break;
             case types.aliasFilterType.entityName.value:
-                getEntitiesByNameFilter(filter.entityType, filter.entityNameFilter, maxItems).then(
+                getEntitiesByNameFilter(filter.entityType, filter.entityNameFilter, maxItems, {ignoreLoading: true}).then(
                     function success(entities) {
                         if (entities && entities.length || !failOnEmpty) {
                             result.entities = entitiesToEntitiesInfo(entities);
@@ -469,7 +475,7 @@ function EntityService($http, $q, $filter, $translate, $log, userService, device
             case types.aliasFilterType.stateEntity.value:
                 result.stateEntity = true;
                 if (stateEntityId) {
-                    getEntity(stateEntityId.entityType, stateEntityId.id).then(
+                    getEntity(stateEntityId.entityType, stateEntityId.id, {ignoreLoading: true}).then(
                         function success(entity) {
                             result.entities = entitiesToEntitiesInfo([entity]);
                             deferred.resolve(result);
@@ -483,7 +489,7 @@ function EntityService($http, $q, $filter, $translate, $log, userService, device
                 }
                 break;
             case types.aliasFilterType.assetType.value:
-                getEntitiesByNameFilter(types.entityType.asset, filter.assetNameFilter, maxItems, null, filter.assetType).then(
+                getEntitiesByNameFilter(types.entityType.asset, filter.assetNameFilter, maxItems, {ignoreLoading: true}, filter.assetType).then(
                     function success(entities) {
                         if (entities && entities.length || !failOnEmpty) {
                             result.entities = entitiesToEntitiesInfo(entities);
@@ -498,7 +504,7 @@ function EntityService($http, $q, $filter, $translate, $log, userService, device
                 );
                 break;
             case types.aliasFilterType.deviceType.value:
-                getEntitiesByNameFilter(types.entityType.device, filter.deviceNameFilter, maxItems, null, filter.deviceType).then(
+                getEntitiesByNameFilter(types.entityType.device, filter.deviceNameFilter, maxItems, {ignoreLoading: true}, filter.deviceType).then(
                     function success(entities) {
                         if (entities && entities.length || !failOnEmpty) {
                             result.entities = entitiesToEntitiesInfo(entities);
@@ -533,7 +539,7 @@ function EntityService($http, $q, $filter, $translate, $log, userService, device
                         filters: filter.filters
                     };
                     searchQuery.parameters.maxLevel = filter.maxLevel && filter.maxLevel > 0 ? filter.maxLevel : -1;
-                    entityRelationService.findInfoByQuery(searchQuery).then(
+                    entityRelationService.findInfoByQuery(searchQuery, {ignoreLoading: true}).then(
                         function success(allRelations) {
                             if (allRelations && allRelations.length || !failOnEmpty) {
                                 if (angular.isDefined(maxItems) && maxItems > 0 && allRelations) {
@@ -577,10 +583,10 @@ function EntityService($http, $q, $filter, $translate, $log, userService, device
                     var findByQueryPromise;
                     if (filter.type == types.aliasFilterType.assetSearchQuery.value) {
                         searchQuery.assetTypes = filter.assetTypes;
-                        findByQueryPromise = assetService.findByQuery(searchQuery, false);
+                        findByQueryPromise = assetService.findByQuery(searchQuery, false, {ignoreLoading: true});
                     } else if (filter.type == types.aliasFilterType.deviceSearchQuery.value) {
                         searchQuery.deviceTypes = filter.deviceTypes;
-                        findByQueryPromise = deviceService.findByQuery(searchQuery, false);
+                        findByQueryPromise = deviceService.findByQuery(searchQuery, false, {ignoreLoading: true});
                     }
                     findByQueryPromise.then(
                         function success(entities) {
@@ -762,7 +768,7 @@ function EntityService($http, $q, $filter, $translate, $log, userService, device
         return deferred.promise;
     }
 
-    function getEntityKeys(entityType, entityId, query, type) {
+    function getEntityKeys(entityType, entityId, query, type, config) {
         var deferred = $q.defer();
         var url = '/api/plugins/telemetry/' + entityType + '/' + entityId + '/keys/';
         if (type === types.dataKeyType.timeseries) {
@@ -770,7 +776,7 @@ function EntityService($http, $q, $filter, $translate, $log, userService, device
         } else if (type === types.dataKeyType.attribute) {
             url += 'attributes';
         }
-        $http.get(url, null).then(function success(response) {
+        $http.get(url, config).then(function success(response) {
             var result = [];
             if (response.data) {
                 if (query) {
diff --git a/ui/src/app/api/entity-relation.service.js b/ui/src/app/api/entity-relation.service.js
index 742aec7..79f80da 100644
--- a/ui/src/app/api/entity-relation.service.js
+++ b/ui/src/app/api/entity-relation.service.js
@@ -175,10 +175,10 @@ function EntityRelationService($http, $q) {
         return deferred.promise;
     }
 
-    function findInfoByQuery(query) {
+    function findInfoByQuery(query, config) {
         var deferred = $q.defer();
         var url = '/api/relations/info';
-        $http.post(url, query).then(function success(response) {
+        $http.post(url, query, config).then(function success(response) {
             deferred.resolve(response.data);
         }, function fail() {
             deferred.reject();
diff --git a/ui/src/app/api/plugin.service.js b/ui/src/app/api/plugin.service.js
index 55429ce..28f63de 100644
--- a/ui/src/app/api/plugin.service.js
+++ b/ui/src/app/api/plugin.service.js
@@ -50,11 +50,11 @@ function PluginService($http, $q, $rootScope, $filter, componentDescriptorServic
         tenantPlugins = undefined;
     }
 
-    function loadPluginsCache() {
+    function loadPluginsCache(config) {
         var deferred = $q.defer();
         if (!allPlugins) {
             var url = '/api/plugins';
-            $http.get(url, null).then(function success(response) {
+            $http.get(url, config).then(function success(response) {
                 componentDescriptorService.getComponentDescriptorsByType(types.componentType.plugin).then(
                     function success(pluginComponents) {
                         allPlugins = response.data;
@@ -93,9 +93,9 @@ function PluginService($http, $q, $rootScope, $filter, componentDescriptorServic
         return deferred.promise;
     }
 
-    function getSystemPlugins(pageLink) {
+    function getSystemPlugins(pageLink, config) {
         var deferred = $q.defer();
-        loadPluginsCache().then(
+        loadPluginsCache(config).then(
             function success() {
                 utils.filterSearchTextEntities(systemPlugins, 'name', pageLink, deferred);
             },
@@ -106,9 +106,9 @@ function PluginService($http, $q, $rootScope, $filter, componentDescriptorServic
         return deferred.promise;
     }
 
-    function getTenantPlugins(pageLink) {
+    function getTenantPlugins(pageLink, config) {
         var deferred = $q.defer();
-        loadPluginsCache().then(
+        loadPluginsCache(config).then(
             function success() {
                 utils.filterSearchTextEntities(tenantPlugins, 'name', pageLink, deferred);
             },
@@ -119,9 +119,9 @@ function PluginService($http, $q, $rootScope, $filter, componentDescriptorServic
         return deferred.promise;
     }
 
-    function getAllActionPlugins(pageLink) {
+    function getAllActionPlugins(pageLink, config) {
         var deferred = $q.defer();
-        loadPluginsCache().then(
+        loadPluginsCache(config).then(
             function success() {
                 utils.filterSearchTextEntities(allActionPlugins, 'name', pageLink, deferred);
             },
@@ -132,9 +132,9 @@ function PluginService($http, $q, $rootScope, $filter, componentDescriptorServic
         return deferred.promise;
     }
 
-    function getAllPlugins(pageLink) {
+    function getAllPlugins(pageLink, config) {
         var deferred = $q.defer();
-        loadPluginsCache().then(
+        loadPluginsCache(config).then(
             function success() {
                 utils.filterSearchTextEntities(allPlugins, 'name', pageLink, deferred);
             },
@@ -156,10 +156,10 @@ function PluginService($http, $q, $rootScope, $filter, componentDescriptorServic
         return deferred.promise;
     }
 
-    function getPlugin(pluginId) {
+    function getPlugin(pluginId, config) {
         var deferred = $q.defer();
         var url = '/api/plugin/' + pluginId;
-        $http.get(url, null).then(function success(response) {
+        $http.get(url, config).then(function success(response) {
             deferred.resolve(response.data);
         }, function fail(response) {
             deferred.reject(response.data);
diff --git a/ui/src/app/api/rule.service.js b/ui/src/app/api/rule.service.js
index ee166f6..df37305 100644
--- a/ui/src/app/api/rule.service.js
+++ b/ui/src/app/api/rule.service.js
@@ -47,11 +47,11 @@ function RuleService($http, $q, $rootScope, $filter, types, utils) {
         tenantRules = undefined;
     }
 
-    function loadRulesCache() {
+    function loadRulesCache(config) {
         var deferred = $q.defer();
         if (!allRules) {
             var url = '/api/rules';
-            $http.get(url, null).then(function success(response) {
+            $http.get(url, config).then(function success(response) {
                 allRules = response.data;
                 systemRules = [];
                 tenantRules = [];
@@ -100,9 +100,9 @@ function RuleService($http, $q, $rootScope, $filter, types, utils) {
         return deferred.promise;
     }
 
-    function getAllRules(pageLink) {
+    function getAllRules(pageLink, config) {
         var deferred = $q.defer();
-        loadRulesCache().then(
+        loadRulesCache(config).then(
             function success() {
                 utils.filterSearchTextEntities(allRules, 'name', pageLink, deferred);
             },
@@ -124,10 +124,10 @@ function RuleService($http, $q, $rootScope, $filter, types, utils) {
         return deferred.promise;
     }
 
-    function getRule(ruleId) {
+    function getRule(ruleId, config) {
         var deferred = $q.defer();
         var url = '/api/rule/' + ruleId;
-        $http.get(url, null).then(function success(response) {
+        $http.get(url, config).then(function success(response) {
             deferred.resolve(response.data);
         }, function fail(response) {
             deferred.reject(response.data);
diff --git a/ui/src/app/api/telemetry-websocket.service.js b/ui/src/app/api/telemetry-websocket.service.js
index f84d66d..b0e8ea9 100644
--- a/ui/src/app/api/telemetry-websocket.service.js
+++ b/ui/src/app/api/telemetry-websocket.service.js
@@ -23,6 +23,8 @@ export default angular.module('thingsboard.api.telemetryWebsocket', [thingsboard
 const RECONNECT_INTERVAL = 2000;
 const WS_IDLE_TIMEOUT = 90000;
 
+const MAX_PUBLISH_COMMANDS = 10;
+
 /*@ngInject*/
 function TelemetryWebsocketService($rootScope, $websocket, $timeout, $window, types, userService) {
 
@@ -75,19 +77,40 @@ function TelemetryWebsocketService($rootScope, $websocket, $timeout, $window, ty
     return service;
 
     function publishCommands () {
-        if (isOpened && (cmdsWrapper.tsSubCmds.length > 0 ||
-            cmdsWrapper.historyCmds.length > 0 ||
-            cmdsWrapper.attrSubCmds.length > 0)) {
-            dataStream.send(angular.copy(cmdsWrapper)).then(function () {
+        while(isOpened && hasCommands()) {
+            dataStream.send(preparePublishCommands()).then(function () {
                 checkToClose();
             });
-            cmdsWrapper.tsSubCmds = [];
-            cmdsWrapper.historyCmds = [];
-            cmdsWrapper.attrSubCmds = [];
         }
         tryOpenSocket();
     }
 
+    function hasCommands() {
+        return cmdsWrapper.tsSubCmds.length > 0 ||
+            cmdsWrapper.historyCmds.length > 0 ||
+            cmdsWrapper.attrSubCmds.length > 0;
+    }
+
+    function preparePublishCommands() {
+        var preparedWrapper = {};
+        var leftCount = MAX_PUBLISH_COMMANDS;
+        preparedWrapper.tsSubCmds = popCmds(cmdsWrapper.tsSubCmds, leftCount);
+        leftCount -= preparedWrapper.tsSubCmds.length;
+        preparedWrapper.historyCmds = popCmds(cmdsWrapper.historyCmds, leftCount);
+        leftCount -= preparedWrapper.historyCmds.length;
+        preparedWrapper.attrSubCmds = popCmds(cmdsWrapper.attrSubCmds, leftCount);
+        return preparedWrapper;
+    }
+
+    function popCmds(cmds, leftCount) {
+        var toPublish = Math.min(cmds.length, leftCount);
+        if (toPublish > 0) {
+            return cmds.splice(0, toPublish);
+        } else {
+            return [];
+        }
+    }
+
     function onError (/*message*/) {
         isOpening = false;
     }
diff --git a/ui/src/app/api/tenant.service.js b/ui/src/app/api/tenant.service.js
index 594b543..be04ae9 100644
--- a/ui/src/app/api/tenant.service.js
+++ b/ui/src/app/api/tenant.service.js
@@ -29,7 +29,7 @@ function TenantService($http, $q) {
 
     return service;
 
-    function getTenants (pageLink) {
+    function getTenants (pageLink, config) {
         var deferred = $q.defer();
         var url = '/api/tenants?limit=' + pageLink.limit;
         if (angular.isDefined(pageLink.textSearch)) {
@@ -41,7 +41,7 @@ function TenantService($http, $q) {
         if (angular.isDefined(pageLink.textOffset)) {
             url += '&textOffset=' + pageLink.textOffset;
         }
-        $http.get(url, null).then(function success(response) {
+        $http.get(url, config).then(function success(response) {
             deferred.resolve(response.data);
         }, function fail() {
             deferred.reject();
@@ -49,10 +49,10 @@ function TenantService($http, $q) {
         return deferred.promise;
     }
 
-    function getTenant (tenantId) {
+    function getTenant (tenantId, config) {
         var deferred = $q.defer();
         var url = '/api/tenant/' + tenantId;
-        $http.get(url, null).then(function success(response) {
+        $http.get(url, config).then(function success(response) {
             deferred.resolve(response.data);
         }, function fail(response) {
             deferred.reject(response.data);
diff --git a/ui/src/app/api/user.service.js b/ui/src/app/api/user.service.js
index d09387b..1a146b4 100644
--- a/ui/src/app/api/user.service.js
+++ b/ui/src/app/api/user.service.js
@@ -421,10 +421,14 @@ function UserService($http, $q, $rootScope, adminService, dashboardService, logi
         return deferred.promise;
     }
 
-    function getUser(userId, ignoreErrors) {
+    function getUser(userId, ignoreErrors, config) {
         var deferred = $q.defer();
         var url = '/api/user/' + userId;
-        $http.get(url, { ignoreErrors: ignoreErrors }).then(function success(response) {
+        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();
diff --git a/ui/src/app/api/widget.service.js b/ui/src/app/api/widget.service.js
index 3c74ecb..fe95729 100644
--- a/ui/src/app/api/widget.service.js
+++ b/ui/src/app/api/widget.service.js
@@ -298,11 +298,11 @@ function WidgetService($rootScope, $http, $q, $filter, $ocLazyLoad, $window, $tr
         tenantWidgetsBundles = undefined;
     }
 
-    function loadWidgetsBundleCache() {
+    function loadWidgetsBundleCache(config) {
         var deferred = $q.defer();
         if (!allWidgetsBundles) {
             var url = '/api/widgetsBundles';
-            $http.get(url, null).then(function success(response) {
+            $http.get(url, config).then(function success(response) {
                 allWidgetsBundles = response.data;
                 systemWidgetsBundles = [];
                 tenantWidgetsBundles = [];
@@ -326,9 +326,9 @@ function WidgetService($rootScope, $http, $q, $filter, $ocLazyLoad, $window, $tr
     }
 
 
-    function getSystemWidgetsBundles() {
+    function getSystemWidgetsBundles(config) {
         var deferred = $q.defer();
-        loadWidgetsBundleCache().then(
+        loadWidgetsBundleCache(config).then(
             function success() {
                 deferred.resolve(systemWidgetsBundles);
             },
@@ -339,9 +339,9 @@ function WidgetService($rootScope, $http, $q, $filter, $ocLazyLoad, $window, $tr
         return deferred.promise;
     }
 
-    function getTenantWidgetsBundles() {
+    function getTenantWidgetsBundles(config) {
         var deferred = $q.defer();
-        loadWidgetsBundleCache().then(
+        loadWidgetsBundleCache(config).then(
             function success() {
                 deferred.resolve(tenantWidgetsBundles);
             },
@@ -352,9 +352,9 @@ function WidgetService($rootScope, $http, $q, $filter, $ocLazyLoad, $window, $tr
         return deferred.promise;
     }
 
-    function getAllWidgetsBundles() {
+    function getAllWidgetsBundles(config) {
         var deferred = $q.defer();
-        loadWidgetsBundleCache().then(
+        loadWidgetsBundleCache(config).then(
             function success() {
                 deferred.resolve(allWidgetsBundles);
             },
diff --git a/ui/src/app/asset/add-asset.tpl.html b/ui/src/app/asset/add-asset.tpl.html
index ce22e4e..71901e4 100644
--- a/ui/src/app/asset/add-asset.tpl.html
+++ b/ui/src/app/asset/add-asset.tpl.html
@@ -27,8 +27,8 @@
                 </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-progress-linear class="md-warn" md-mode="indeterminate" ng-disabled="!$root.loading" ng-show="$root.loading"></md-progress-linear>
+        <span style="min-height: 5px;" flex="" ng-show="!$root.loading"></span>
         <md-dialog-content>
             <div class="md-dialog-content">
                 <tb-asset asset="vm.item" is-edit="true" the-form="theForm"></tb-asset>
@@ -36,10 +36,10 @@
         </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">
+            <md-button ng-disabled="$root.loading || theForm.$invalid || !theForm.$dirty" type="submit" class="md-raised md-primary">
                 {{ 'action.add' | translate }}
             </md-button>
-            <md-button ng-disabled="loading" ng-click="vm.cancel()" style="margin-right:20px;">{{ 'action.cancel' | translate }}</md-button>
+            <md-button ng-disabled="$root.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/asset/add-assets-to-customer.tpl.html b/ui/src/app/asset/add-assets-to-customer.tpl.html
index 18e23ce..64edcf8 100644
--- a/ui/src/app/asset/add-assets-to-customer.tpl.html
+++ b/ui/src/app/asset/add-assets-to-customer.tpl.html
@@ -26,8 +26,8 @@
                 </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-progress-linear class="md-warn" md-mode="indeterminate" ng-disabled="!$root.loading" ng-show="$root.loading"></md-progress-linear>
+        <span style="min-height: 5px;" flex="" ng-show="!$root.loading"></span>
         <md-dialog-content>
             <div class="md-dialog-content">
                 <fieldset>
@@ -65,11 +65,11 @@
         </md-dialog-content>
         <md-dialog-actions layout="row">
             <span flex></span>
-            <md-button ng-disabled="loading || vm.assets.selectedCount == 0" type="submit"
+            <md-button ng-disabled="$root.loading || vm.assets.selectedCount == 0" type="submit"
                        class="md-raised md-primary">
                 {{ 'action.assign' | translate }}
             </md-button>
-            <md-button ng-disabled="loading" ng-click="vm.cancel()" style="margin-right:20px;">{{ 'action.cancel' |
+            <md-button ng-disabled="$root.loading" ng-click="vm.cancel()" style="margin-right:20px;">{{ 'action.cancel' |
                 translate }}
             </md-button>
         </md-dialog-actions>
diff --git a/ui/src/app/asset/asset-fieldset.tpl.html b/ui/src/app/asset/asset-fieldset.tpl.html
index d921b2e..afa14ad 100644
--- a/ui/src/app/asset/asset-fieldset.tpl.html
+++ b/ui/src/app/asset/asset-fieldset.tpl.html
@@ -48,7 +48,7 @@
          ng-show="!isEdit && isPublic && (assetScope === 'customer' || assetScope === 'tenant')">
         {{ 'asset.asset-public' | translate }}
     </div>
-    <fieldset ng-disabled="loading || !isEdit">
+    <fieldset ng-disabled="$root.loading || !isEdit">
         <md-input-container class="md-block">
             <label translate>asset.name</label>
             <input required name="name" ng-model="asset.name">
@@ -57,7 +57,7 @@
             </div>
         </md-input-container>
         <tb-entity-subtype-autocomplete
-                ng-disabled="loading || !isEdit"
+                ng-disabled="$root.loading || !isEdit"
                 tb-required="true"
                 the-form="theForm"
                 ng-model="asset.type"
diff --git a/ui/src/app/asset/assign-to-customer.tpl.html b/ui/src/app/asset/assign-to-customer.tpl.html
index fba56ce..9030ec4 100644
--- a/ui/src/app/asset/assign-to-customer.tpl.html
+++ b/ui/src/app/asset/assign-to-customer.tpl.html
@@ -26,8 +26,8 @@
                 </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-progress-linear class="md-warn" md-mode="indeterminate" ng-disabled="!$root.loading" ng-show="$root.loading"></md-progress-linear>
+        <span style="min-height: 5px;" flex="" ng-show="!$root.loading"></span>
         <md-dialog-content>
             <div class="md-dialog-content">
                 <fieldset>
@@ -65,10 +65,10 @@
         </md-dialog-content>
         <md-dialog-actions layout="row">
             <span flex></span>
-            <md-button ng-disabled="loading || vm.customers.selection==null" type="submit" class="md-raised md-primary">
+            <md-button ng-disabled="$root.loading || vm.customers.selection==null" type="submit" class="md-raised md-primary">
                 {{ 'action.assign' | translate }}
             </md-button>
-            <md-button ng-disabled="loading" ng-click="vm.cancel()" style="margin-right:20px;">{{ 'action.cancel' |
+            <md-button ng-disabled="$root.loading" ng-click="vm.cancel()" style="margin-right:20px;">{{ 'action.cancel' |
                 translate }}
             </md-button>
         </md-dialog-actions>
diff --git a/ui/src/app/common/utils.service.js b/ui/src/app/common/utils.service.js
index 9d99781..085a28b 100644
--- a/ui/src/app/common/utils.service.js
+++ b/ui/src/app/common/utils.service.js
@@ -134,6 +134,8 @@ function Utils($mdColorPalette, $rootScope, $window, $translate, $q, $timeout, t
         defaultAlarmDataKeys.push(dataKey);
     }
 
+    var imageAspectMap = {};
+
     var service = {
         getDefaultDatasource: getDefaultDatasource,
         generateObjectFromJsonSchema: generateObjectFromJsonSchema,
@@ -159,7 +161,8 @@ function Utils($mdColorPalette, $rootScope, $window, $translate, $q, $timeout, t
         insertVariable: insertVariable,
         customTranslation: customTranslation,
         objToBase64: objToBase64,
-        base64toObj: base64toObj
+        base64toObj: base64toObj,
+        loadImageAspect: loadImageAspect
     }
 
     return service;
@@ -543,4 +546,34 @@ function Utils($mdColorPalette, $rootScope, $window, $translate, $q, $timeout, t
         return obj;
     }
 
+    function loadImageAspect(imageUrl) {
+        var deferred = $q.defer();
+        if (imageUrl && imageUrl.length) {
+            var urlHashCode = hashCode(imageUrl);
+            var aspect = imageAspectMap[urlHashCode];
+            if (angular.isUndefined(aspect)) {
+                var testImage = document.createElement('img'); // eslint-disable-line
+                testImage.style.visibility = 'hidden';
+                testImage.onload = function() {
+                    aspect = testImage.width / testImage.height;
+                    document.body.removeChild(testImage); //eslint-disable-line
+                    imageAspectMap[urlHashCode] = aspect;
+                    deferred.resolve(aspect);
+                };
+                testImage.onerror = function() {
+                    aspect = 0;
+                    imageAspectMap[urlHashCode] = aspect;
+                    deferred.resolve(aspect);
+                };
+                document.body.appendChild(testImage); //eslint-disable-line
+                testImage.src = imageUrl;
+            } else {
+                deferred.resolve(aspect);
+            }
+        } else {
+            deferred.resolve(0);
+        }
+        return deferred.promise;
+    }
+
 }
diff --git a/ui/src/app/component/component.tpl.html b/ui/src/app/component/component.tpl.html
index a9faa7f..d007ba6 100644
--- a/ui/src/app/component/component.tpl.html
+++ b/ui/src/app/component/component.tpl.html
@@ -24,7 +24,7 @@
         {{ componentTypeName }}
     </span>
     <span ng-if="readOnly" style="min-width: 40px; min-height: 40px; margin: 0 6px;"></br></span>
-    <md-button ng-disabled="loading" class="md-icon-button md-primary"
+    <md-button ng-disabled="$root.loading" class="md-icon-button md-primary"
                style="min-width: 40px;"
                ng-click="openComponent($event)"
                aria-label="{{ (readOnly ? 'action.view' : 'action.edit') | translate }}">
@@ -43,7 +43,7 @@
             edit
         </md-icon>
     </md-button>
-    <md-button ng-if="!readOnly" ng-disabled="loading" class="md-icon-button md-primary"
+    <md-button ng-if="!readOnly" ng-disabled="$root.loading" class="md-icon-button md-primary"
                style="min-width: 40px;"
                ng-click="onRemoveComponent({event: $event})"
                aria-label="{{ 'action.remove' | translate }}">
diff --git a/ui/src/app/component/component-dialog.tpl.html b/ui/src/app/component/component-dialog.tpl.html
index 271d818..841d7ef 100644
--- a/ui/src/app/component/component-dialog.tpl.html
+++ b/ui/src/app/component/component-dialog.tpl.html
@@ -27,11 +27,11 @@
                 </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-progress-linear class="md-warn" md-mode="indeterminate" ng-disabled="!$root.loading" ng-show="$root.loading"></md-progress-linear>
+        <span style="min-height: 5px;" flex="" ng-show="!$root.loading"></span>
         <md-dialog-content>
             <div class="md-dialog-content tb-filter">
-                <fieldset ng-disabled="loading || vm.isReadOnly">
+                <fieldset ng-disabled="$root.loading || vm.isReadOnly">
                     <section flex layout="row">
                         <md-input-container flex class="md-block">
                             <label translate>rule.component-name</label>
@@ -42,7 +42,7 @@
                         </md-input-container>
                         <md-input-container flex class="md-block">
                             <label translate>rule.component-type</label>
-                            <md-select required name="componentType" ng-model="vm.componentInfo.component.clazz" ng-disabled="loading || vm.isReadOnly">
+                            <md-select required name="componentType" ng-model="vm.componentInfo.component.clazz" ng-disabled="$root.loading || vm.isReadOnly">
                                 <md-option ng-repeat="componentDescriptor in vm.componentDescriptors" ng-value="componentDescriptor.clazz">
                                     {{componentDescriptor.name}}
                                 </md-option>
@@ -57,7 +57,7 @@
                             <tb-json-form schema="vm.componentDescriptor.configurationDescriptor.schema"
                                           form="vm.componentDescriptor.configurationDescriptor.form"
                                           model="vm.componentInfo.component.configuration"
-                                          readonly="loading || vm.isReadOnly"
+                                          readonly="$root.loading || vm.isReadOnly"
                                           form-control="theForm">
                             </tb-json-form>
                         </md-card-content>
@@ -67,11 +67,11 @@
         </md-dialog-content>
         <md-dialog-actions layout="row">
             <span flex></span>
-            <md-button ng-if="!vm.isReadOnly" ng-disabled="loading || theForm.$invalid || !theForm.$dirty" type="submit"
+            <md-button ng-if="!vm.isReadOnly" ng-disabled="$root.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' |
+            <md-button ng-disabled="$root.loading" ng-click="vm.cancel()" style="margin-right:20px;">{{ 'action.cancel' |
                 translate }}
             </md-button>
         </md-dialog-actions>
diff --git a/ui/src/app/components/dashboard-autocomplete.directive.js b/ui/src/app/components/dashboard-autocomplete.directive.js
index ce7e477..2235b82 100644
--- a/ui/src/app/components/dashboard-autocomplete.directive.js
+++ b/ui/src/app/components/dashboard-autocomplete.directive.js
@@ -48,19 +48,19 @@ function DashboardAutocomplete($compile, $templateCache, $q, dashboardService, u
             var promise;
             if (scope.dashboardsScope === 'customer' || userService.getAuthority() === 'CUSTOMER_USER') {
                 if (scope.customerId) {
-                    promise = dashboardService.getCustomerDashboards(scope.customerId, pageLink, false);
+                    promise = dashboardService.getCustomerDashboards(scope.customerId, pageLink, false, {ignoreLoading: true});
                 } else {
                     promise = $q.when({data: []});
                 }
             } else {
                 if (userService.getAuthority() === 'SYS_ADMIN') {
                     if (scope.tenantId) {
-                        promise = dashboardService.getTenantDashboardsByTenantId(scope.tenantId, pageLink);
+                        promise = dashboardService.getTenantDashboardsByTenantId(scope.tenantId, pageLink, {ignoreLoading: true});
                     } else {
                         promise = $q.when({data: []});
                     }
                 } else {
-                    promise = dashboardService.getTenantDashboards(pageLink, false);
+                    promise = dashboardService.getTenantDashboards(pageLink, false, {ignoreLoading: true});
                 }
             }
 
diff --git a/ui/src/app/components/dashboard-select.directive.js b/ui/src/app/components/dashboard-select.directive.js
index d7b32d8..ac5cd3d 100644
--- a/ui/src/app/components/dashboard-select.directive.js
+++ b/ui/src/app/components/dashboard-select.directive.js
@@ -48,12 +48,12 @@ function DashboardSelect($compile, $templateCache, $q, $mdMedia, $mdPanel, $docu
         var promise;
         if (scope.dashboardsScope === 'customer' || userService.getAuthority() === 'CUSTOMER_USER') {
             if (scope.customerId && scope.customerId != types.id.nullUid) {
-                promise = dashboardService.getCustomerDashboards(scope.customerId, pageLink, false);
+                promise = dashboardService.getCustomerDashboards(scope.customerId, pageLink, false, {ignoreLoading: true});
             } else {
                 promise = $q.when({data: []});
             }
         } else {
-            promise = dashboardService.getTenantDashboards(pageLink, false);
+            promise = dashboardService.getTenantDashboards(pageLink, false, {ignoreLoading: true});
         }
 
         promise.then(function success(result) {
diff --git a/ui/src/app/components/datakey-config-dialog.controller.js b/ui/src/app/components/datakey-config-dialog.controller.js
index ccaac21..c4e8594 100644
--- a/ui/src/app/components/datakey-config-dialog.controller.js
+++ b/ui/src/app/components/datakey-config-dialog.controller.js
@@ -43,7 +43,7 @@ function DatakeyConfigDialogController($scope, $mdDialog, $q, entityService, dat
             function success(aliasInfo) {
                 var entity = aliasInfo.currentEntity;
                 if (entity) {
-                    entityService.getEntityKeys(entity.entityType, entity.id, query, type).then(
+                    entityService.getEntityKeys(entity.entityType, entity.id, query, type, {ignoreLoading: true}).then(
                         function success(keys) {
                             deferred.resolve(keys);
                         },
diff --git a/ui/src/app/components/datakey-config-dialog.tpl.html b/ui/src/app/components/datakey-config-dialog.tpl.html
index a55c316..574a7af 100644
--- a/ui/src/app/components/datakey-config-dialog.tpl.html
+++ b/ui/src/app/components/datakey-config-dialog.tpl.html
@@ -26,8 +26,8 @@
 	        </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-progress-linear class="md-warn" md-mode="indeterminate" ng-disabled="!$root.loading" ng-show="$root.loading"></md-progress-linear>
+  	    <span style="min-height: 5px;" flex="" ng-show="!$root.loading"></span>
 	    <md-dialog-content>
 			<tb-datakey-config ng-model="vm.dataKey"
                                fetch-entity-keys="vm.fetchEntityKeys(entityAliasId, query, type)"
@@ -37,10 +37,10 @@
 	    </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">
+		  <md-button ng-disabled="$root.loading || theForm.$invalid || !theForm.$dirty" type="submit" class="md-raised md-primary">
 		  		{{ 'action.save' | translate }}
 		  </md-button>
-	      <md-button ng-disabled="loading" ng-click="vm.cancel()" style="margin-right:20px;">{{ 'action.cancel' | translate }}</md-button>
+	      <md-button ng-disabled="$root.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/components/details-sidenav.tpl.html b/ui/src/app/components/details-sidenav.tpl.html
index 24c2270..934aed3 100644
--- a/ui/src/app/components/details-sidenav.tpl.html
+++ b/ui/src/app/components/details-sidenav.tpl.html
@@ -35,7 +35,7 @@
 	        </div>
 		    <section ng-if="!isReadOnly" layout="row" layout-wrap
 				   class="tb-header-buttons md-fab">
-				<md-button ng-show="isEdit" ng-disabled="loading || theForm.$invalid || !theForm.$dirty"
+				<md-button ng-show="isEdit" ng-disabled="$root.loading || theForm.$invalid || !theForm.$dirty"
 							 class="tb-btn-header md-accent md-hue-2 md-fab md-fab-bottom-right"
 							 aria-label="{{ 'action.apply' | translate }}"
 							 ng-click="detailsApply()">
@@ -44,7 +44,7 @@
 					  </md-tooltip>
 					  <ng-md-icon icon="done"></ng-md-icon>
 				</md-button>
-				<md-button ng-disabled="loading || (isAlwaysEdit && !theForm.$dirty)" class="tb-btn-header md-accent md-hue-2 md-fab md-fab-bottom-right"
+				<md-button ng-disabled="$root.loading || (isAlwaysEdit && !theForm.$dirty)" class="tb-btn-header md-accent md-hue-2 md-fab md-fab-bottom-right"
 					 aria-label="{{ 'details.edit-mode' | translate }}"
 					 ng-click="toggleDetailsEditMode()">
 					<md-tooltip md-direction="top">
diff --git a/ui/src/app/components/grid.tpl.html b/ui/src/app/components/grid.tpl.html
index 24285d8..b334567 100644
--- a/ui/src/app/components/grid.tpl.html
+++ b/ui/src/app/components/grid.tpl.html
@@ -45,7 +45,7 @@
                                 <tb-grid-card-content flex grid-ctl="vm" parent-ctl="vm.parentCtl" item-controller="vm.itemCardController" item-template="vm.itemCardTemplate" item="rowItem[n]"></tb-grid-card-content>
                             </md-card-content>
                             <md-card-actions layout="row" layout-align="end end">
-                                <md-button ng-if="action.isEnabled(rowItem[n])" ng-disabled="loading" class="md-icon-button md-primary" ng-repeat="action in vm.actionsList"
+                                <md-button ng-if="action.isEnabled(rowItem[n])" ng-disabled="$root.loading" class="md-icon-button md-primary" ng-repeat="action in vm.actionsList"
                                            ng-click="action.onAction($event, rowItem[n])" aria-label="{{ action.name() }}">
                                     <md-tooltip md-direction="top">
                                         {{ action.details( rowItem[n] ) }}
@@ -81,28 +81,28 @@
 </section>
 
 <section layout="row" layout-wrap class="tb-footer-buttons md-fab " layout-align="start end">
-    <md-button ng-disabled="loading" ng-show="vm.items.selectedCount > 0" class="tb-btn-footer md-accent md-hue-2 md-fab" ng-repeat="groupAction in vm.groupActionsList"
+    <md-button ng-disabled="$root.loading" ng-show="vm.items.selectedCount > 0" class="tb-btn-footer md-accent md-hue-2 md-fab" ng-repeat="groupAction in vm.groupActionsList"
                ng-click="groupAction.onAction($event, vm.items)" aria-label="{{ groupAction.name() }}">
         <md-tooltip md-direction="top">
             {{ groupAction.details(vm.items.selectedCount) }}
         </md-tooltip>
         <ng-md-icon icon="{{groupAction.icon}}"></ng-md-icon>
     </md-button>
-    <md-button ng-disabled="loading" ng-show="vm.topIndex > 0" class="tb-btn-footer md-primary md-hue-1 md-fab" ng-click="vm.moveToTop()" aria-label="{{'grid.scroll-to-top' | translate}}" >
+    <md-button ng-disabled="$root.loading" ng-show="vm.topIndex > 0" class="tb-btn-footer md-primary md-hue-1 md-fab" ng-click="vm.moveToTop()" aria-label="{{'grid.scroll-to-top' | translate}}" >
         <md-tooltip md-direction="top">
             {{'grid.scroll-to-top' | translate}}
         </md-tooltip>
         <ng-md-icon icon="arrow_drop_up"></ng-md-icon>
     </md-button>
-    <md-button ng-disabled="loading" ng-if="vm.addItemAction.name() && vm.addItemActions.length == 0" class="tb-btn-footer md-accent md-hue-2 md-fab" ng-click="vm.addItemAction.onAction($event)" aria-label="{{ vm.addItemAction.name() }}" >
+    <md-button ng-disabled="$root.loading" ng-if="vm.addItemAction.name() && vm.addItemActions.length == 0" class="tb-btn-footer md-accent md-hue-2 md-fab" ng-click="vm.addItemAction.onAction($event)" aria-label="{{ vm.addItemAction.name() }}" >
         <md-tooltip md-direction="top">
             {{ vm.addItemAction.details() }}
         </md-tooltip>
         <ng-md-icon icon="{{ vm.addItemAction.icon }}"></ng-md-icon>
     </md-button>
-    <md-fab-speed-dial ng-disabled="loading" ng-if="vm.addItemAction.name() && vm.addItemActions.length > 0" md-open="vm.addItemActionsOpen" class="md-scale" md-direction="up" ng-if="vm.addItemAction.name()">
+    <md-fab-speed-dial ng-disabled="$root.loading" ng-if="vm.addItemAction.name() && vm.addItemActions.length > 0" md-open="vm.addItemActionsOpen" class="md-scale" md-direction="up" ng-if="vm.addItemAction.name()">
         <md-fab-trigger>
-            <md-button ng-disabled="loading" class="tb-btn-footer md-accent md-hue-2 md-fab" aria-label="{{ vm.addItemAction.name() }}" >
+            <md-button ng-disabled="$root.loading" class="tb-btn-footer md-accent md-hue-2 md-fab" aria-label="{{ vm.addItemAction.name() }}" >
                 <md-tooltip md-direction="top">
                     {{ vm.addItemAction.details() }}
                 </md-tooltip>
@@ -110,7 +110,7 @@
             </md-button>
         </md-fab-trigger>
         <md-fab-actions>
-            <md-button ng-disabled="loading" class="md-accent md-hue-2 md-fab" ng-repeat="addItemAction in vm.addItemActions"
+            <md-button ng-disabled="$root.loading" class="md-accent md-hue-2 md-fab" ng-repeat="addItemAction in vm.addItemActions"
                        ng-click="addItemAction.onAction($event)" aria-label="{{ addItemAction.name() }}" >
                 <md-tooltip md-direction="top">
                     {{ addItemAction.details() }}
diff --git a/ui/src/app/components/legend-config-panel.tpl.html b/ui/src/app/components/legend-config-panel.tpl.html
index c5538a1..fe64204 100644
--- a/ui/src/app/components/legend-config-panel.tpl.html
+++ b/ui/src/app/components/legend-config-panel.tpl.html
@@ -16,7 +16,7 @@
 
 -->
 <form name="theForm" ng-submit="vm.update()">
-    <fieldset ng-disabled="loading">
+    <fieldset ng-disabled="$root.loading">
         <md-content style="height: 100%" flex layout="column">
             <section layout="column">
                 <md-content class="md-padding" layout="column">
diff --git a/ui/src/app/components/material-icons-dialog.tpl.html b/ui/src/app/components/material-icons-dialog.tpl.html
index 8a2eb65..db1abda 100644
--- a/ui/src/app/components/material-icons-dialog.tpl.html
+++ b/ui/src/app/components/material-icons-dialog.tpl.html
@@ -32,15 +32,15 @@
                 </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-progress-linear class="md-warn" md-mode="indeterminate" ng-disabled="!$root.loading" ng-show="$root.loading"></md-progress-linear>
+        <span style="min-height: 5px;" flex="" ng-show="!$root.loading"></span>
         <div class="tb-absolute-fill tb-icons-load" ng-show="vm.loadingIcons" layout="column" layout-align="center center">
             <md-progress-circular md-mode="indeterminate" ng-disabled="!vm.loadingIcons" class="md-accent" md-diameter="40"></md-progress-circular>
         </div>
         <md-dialog-content>
             <div class="md-dialog-content">
                 <md-content class="md-padding" layout="column">
-                    <fieldset ng-disabled="loading">
+                    <fieldset ng-disabled="$root.loading">
                         <md-button ng-class="{'md-primary md-raised': icon == vm.selectedIcon}" class="tb-select-icon-button md-icon-button"
                                    ng-repeat="icon in vm.icons" ng-click="vm.selectIcon($event, icon)" tb-on-finish-render="iconsLoadFinished">
                             <md-icon class="material-icons">{{icon}}</md-icon>
@@ -54,7 +54,7 @@
         </md-dialog-content>
         <md-dialog-actions layout="row">
             <span flex></span>
-            <md-button ng-disabled="loading" ng-click="vm.cancel()">
+            <md-button ng-disabled="$root.loading" ng-click="vm.cancel()">
                 {{ 'action.cancel' | translate }}
             </md-button>
         </md-dialog-actions>
diff --git a/ui/src/app/components/plugin-select.directive.js b/ui/src/app/components/plugin-select.directive.js
index f20076f..0e52657 100644
--- a/ui/src/app/components/plugin-select.directive.js
+++ b/ui/src/app/components/plugin-select.directive.js
@@ -56,7 +56,7 @@ function PluginSelect($compile, $templateCache, $q, pluginService, types) {
 
             var deferred = $q.defer();
 
-            scope.pluginFetchFunction(pageLink).then(function success(result) {
+            scope.pluginFetchFunction(pageLink, {ignoreLoading: true}).then(function success(result) {
                 deferred.resolve(result.data);
             }, function fail() {
                 deferred.reject();
@@ -89,7 +89,7 @@ function PluginSelect($compile, $templateCache, $q, pluginService, types) {
 
         if (scope.selectFirstPlugin) {
             var pageLink = {limit: 1, textSearch: ''};
-            scope.pluginFetchFunction(pageLink).then(function success(result) {
+            scope.pluginFetchFunction(pageLink, {ignoreLoading: true}).then(function success(result) {
                 var plugins = result.data;
                 if (plugins.length > 0) {
                     scope.plugin = plugins[0];
diff --git a/ui/src/app/components/timewindow-panel.tpl.html b/ui/src/app/components/timewindow-panel.tpl.html
index 27187dc..0c80538 100644
--- a/ui/src/app/components/timewindow-panel.tpl.html
+++ b/ui/src/app/components/timewindow-panel.tpl.html
@@ -16,7 +16,7 @@
 
 -->
 <form name="theForm" ng-submit="vm.update()">
-	<fieldset ng-disabled="loading">
+	<fieldset ng-disabled="$root.loading">
 		<md-content style="height: 100%" flex layout="column">
 			<section layout="column">
 				<md-tabs ng-class="{'tb-headless': vm.historyOnly}" md-dynamic-height md-selected="vm.timewindow.selectedTab" md-border-bottom>
@@ -81,10 +81,10 @@
 			<span flex></span>
 			<section layout="row" layout-alignment="start center">
 				  <span flex></span>
-				  <md-button ng-disabled="loading || theForm.$invalid || !theForm.$dirty" type="submit" class="md-raised md-primary">
+				  <md-button ng-disabled="$root.loading || theForm.$invalid || !theForm.$dirty" type="submit" class="md-raised md-primary">
 				  		{{ 'action.update' | translate }}
 				  </md-button>
-			      <md-button ng-disabled="loading" ng-click="vm.cancel()" style="margin-right:20px;">
+			      <md-button ng-disabled="$root.loading" ng-click="vm.cancel()" style="margin-right:20px;">
 					  {{ 'action.cancel' | translate }}
 			      </md-button>
 			</section> 
diff --git a/ui/src/app/components/widget/action/widget-action-dialog.tpl.html b/ui/src/app/components/widget/action/widget-action-dialog.tpl.html
index ded4c96..91b4981 100644
--- a/ui/src/app/components/widget/action/widget-action-dialog.tpl.html
+++ b/ui/src/app/components/widget/action/widget-action-dialog.tpl.html
@@ -26,12 +26,12 @@
                 </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-progress-linear class="md-warn" md-mode="indeterminate" ng-disabled="!$root.loading" ng-show="$root.loading"></md-progress-linear>
+        <span style="min-height: 5px;" flex="" ng-show="!$root.loading"></span>
         <md-dialog-content>
             <div class="md-dialog-content">
                 <md-content class="md-padding" layout="column">
-                    <fieldset ng-disabled="loading" layout="column">
+                    <fieldset ng-disabled="$root.loading" layout="column">
                         <md-input-container class="md-block">
                             <label translate>widget-config.action-source</label>
                             <md-select name="actionSource" required aria-label="{{ 'widget-config.action-source' | translate }}" ng-model="vm.action.actionSourceId">
@@ -120,7 +120,7 @@
                         </div>
                         <tb-js-func ng-if="vm.action.type == vm.types.widgetActionTypes.custom.value"
                                     ng-model="vm.action.customFunction"
-                                    function-args="{{ ['$event', 'widgetContext', 'entityId'] }}"
+                                    function-args="{{ ['$event', 'widgetContext', 'entityId', 'entityName', 'additionalParams'] }}"
                                     validation-args="{{ [] }}">
                         </tb-js-func>
                     </fieldset>
@@ -129,11 +129,11 @@
         </md-dialog-content>
         <md-dialog-actions layout="row">
             <span flex></span>
-            <md-button ng-disabled="loading || theForm.$invalid || !theForm.$dirty" type="submit"
+            <md-button ng-disabled="$root.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;">
+            <md-button ng-disabled="$root.loading" ng-click="vm.cancel()" style="margin-right:20px;">
                 {{ 'action.cancel' | translate }}
             </md-button>
         </md-dialog-actions>
diff --git a/ui/src/app/components/widget/widget.controller.js b/ui/src/app/components/widget/widget.controller.js
index 7d91aef..a5faada 100644
--- a/ui/src/app/components/widget/widget.controller.js
+++ b/ui/src/app/components/widget/widget.controller.js
@@ -444,7 +444,7 @@ export default function WidgetController($scope, $state, $timeout, $window, $ele
         }
     }
 
-    function handleWidgetAction($event, descriptor, entityId, entityName) {
+    function handleWidgetAction($event, descriptor, entityId, entityName, additionalParams) {
         var type = descriptor.type;
         var targetEntityParamName = descriptor.stateEntityParamName;
         var targetEntityId;
@@ -485,8 +485,11 @@ export default function WidgetController($scope, $state, $timeout, $window, $ele
                 var customFunction = descriptor.customFunction;
                 if (angular.isDefined(customFunction) && customFunction.length > 0) {
                     try {
-                        var customActionFunction = new Function('$event', 'widgetContext', 'entityId', 'entityName', customFunction);
-                        customActionFunction($event, widgetContext, entityId, entityName);
+                        if (!additionalParams) {
+                            additionalParams = {};
+                        }
+                        var customActionFunction = new Function('$event', 'widgetContext', 'entityId', 'entityName', 'additionalParams', customFunction);
+                        customActionFunction($event, widgetContext, entityId, entityName, additionalParams);
                     } catch (e) {
                         //
                     }
diff --git a/ui/src/app/components/widget/widget-config.tpl.html b/ui/src/app/components/widget/widget-config.tpl.html
index b0084d7..c50aade 100644
--- a/ui/src/app/components/widget/widget-config.tpl.html
+++ b/ui/src/app/components/widget/widget-config.tpl.html
@@ -104,7 +104,7 @@
                                                        generate-data-key="generateDataKey(chip,type)"
                                                        fetch-entity-keys="fetchEntityKeys({entityAliasId: entityAliasId, query: query, type: type})"
                                                        on-create-entity-alias="onCreateEntityAlias({event: event, alias: alias})"></tb-datasource>
-                                        <md-button ng-disabled="loading" class="md-icon-button md-primary"
+                                        <md-button ng-disabled="$root.loading" class="md-icon-button md-primary"
                                                    style="min-width: 40px;"
                                                    ng-click="removeDatasource($event, datasource)"
                                                    aria-label="{{ 'action.remove' | translate }}">
@@ -121,7 +121,7 @@
                             </div>
                         </div>
                         <div flex layout="row" layout-align="start center">
-                            <md-button ng-show="typeParameters.maxDatasources == -1 || datasources.length < typeParameters.maxDatasources" ng-disabled="loading" class="md-primary md-raised"
+                            <md-button ng-show="typeParameters.maxDatasources == -1 || datasources.length < typeParameters.maxDatasources" ng-disabled="$root.loading" class="md-primary md-raised"
                                        ng-click="addDatasource($event)" aria-label="{{ 'action.add' | translate }}">
                                 <md-tooltip md-direction="top">
                                     {{ 'widget-config.add-datasource' | translate }}
diff --git a/ui/src/app/components/widgets-bundle-select.directive.js b/ui/src/app/components/widgets-bundle-select.directive.js
index b90d240..7e1ed71 100644
--- a/ui/src/app/components/widgets-bundle-select.directive.js
+++ b/ui/src/app/components/widgets-bundle-select.directive.js
@@ -48,7 +48,7 @@ function WidgetsBundleSelect($compile, $templateCache, widgetService, types) {
             }
         }
 
-        widgetsBundleFetchFunction().then(
+        widgetsBundleFetchFunction({ignoreLoading: true}).then(
             function success(widgetsBundles) {
                 scope.widgetsBundles = widgetsBundles;
                 if (scope.selectFirstBundle) {
diff --git a/ui/src/app/customer/add-customer.tpl.html b/ui/src/app/customer/add-customer.tpl.html
index c1da19d..69c3832 100644
--- a/ui/src/app/customer/add-customer.tpl.html
+++ b/ui/src/app/customer/add-customer.tpl.html
@@ -27,8 +27,8 @@
 	        </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-progress-linear class="md-warn" md-mode="indeterminate" ng-disabled="!$root.loading" ng-show="$root.loading"></md-progress-linear>
+  	    <span style="min-height: 5px;" flex="" ng-show="!$root.loading"></span>
 	    <md-dialog-content>
 	      <div class="md-dialog-content">
   	        	<tb-customer customer="vm.item" is-edit="true" the-form="theForm"></tb-customer>
@@ -36,10 +36,10 @@
 	    </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">
+		  <md-button ng-disabled="$root.loading || theForm.$invalid || !theForm.$dirty" type="submit" class="md-raised md-primary">
 		  		{{ 'action.add' | translate }}
 		  </md-button>
-	      <md-button ng-disabled="loading" ng-click="vm.cancel()" style="margin-right:20px;">{{ 'action.cancel' | translate }}</md-button>
+	      <md-button ng-disabled="$root.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/customer/customer-fieldset.tpl.html b/ui/src/app/customer/customer-fieldset.tpl.html
index 3facd0f..e367c88 100644
--- a/ui/src/app/customer/customer-fieldset.tpl.html
+++ b/ui/src/app/customer/customer-fieldset.tpl.html
@@ -32,7 +32,7 @@
 </div>
 
 <md-content class="md-padding" layout="column">
-	<fieldset ng-show="!isPublic" ng-disabled="loading || !isEdit">
+	<fieldset ng-show="!isPublic" ng-disabled="$root.loading || !isEdit">
 		<md-input-container class="md-block">
 			<label translate>customer.title</label>
 			<input required name="title" ng-model="customer.title">	
diff --git a/ui/src/app/dashboard/add-dashboard.tpl.html b/ui/src/app/dashboard/add-dashboard.tpl.html
index 4807522..79735e0 100644
--- a/ui/src/app/dashboard/add-dashboard.tpl.html
+++ b/ui/src/app/dashboard/add-dashboard.tpl.html
@@ -27,8 +27,8 @@
 	        </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-progress-linear class="md-warn" md-mode="indeterminate" ng-disabled="!$root.loading" ng-show="$root.loading"></md-progress-linear>
+  	    <span style="min-height: 5px;" flex="" ng-show="!$root.loading"></span>
 	    <md-dialog-content>
 	      <div class="md-dialog-content">
   	        	<tb-dashboard-details dashboard="vm.item" is-edit="true" the-form="theForm"></tb-dashboard-details>
@@ -36,10 +36,10 @@
 	    </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">
+		  <md-button ng-disabled="$root.loading || theForm.$invalid || !theForm.$dirty" type="submit" class="md-raised md-primary">
 			  {{ 'action.add' | translate }}
 		  </md-button>
-	      <md-button ng-disabled="loading" ng-click="vm.cancel()" style="margin-right:20px;">{{ 'action.cancel' | translate }}</md-button>
+	      <md-button ng-disabled="$root.loading" ng-click="vm.cancel()" style="margin-right:20px;">{{ 'action.cancel' | translate }}</md-button>
 	    </md-dialog-actions>
 	</form>    
 </md-dialog>
\ No newline at end of file
diff --git a/ui/src/app/dashboard/add-dashboards-to-customer.tpl.html b/ui/src/app/dashboard/add-dashboards-to-customer.tpl.html
index cfd6b6a..6ae2f8d 100644
--- a/ui/src/app/dashboard/add-dashboards-to-customer.tpl.html
+++ b/ui/src/app/dashboard/add-dashboards-to-customer.tpl.html
@@ -26,8 +26,8 @@
                 </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-progress-linear class="md-warn" md-mode="indeterminate" ng-disabled="!$root.loading" ng-show="$root.loading"></md-progress-linear>
+        <span style="min-height: 5px;" flex="" ng-show="!$root.loading"></span>
         <md-dialog-content>
             <div class="md-dialog-content">
                 <fieldset>
@@ -65,11 +65,11 @@
         </md-dialog-content>
         <md-dialog-actions layout="row">
             <span flex></span>
-            <md-button ng-disabled="loading || vm.dashboards.selectedCount == 0" type="submit"
+            <md-button ng-disabled="$root.loading || vm.dashboards.selectedCount == 0" type="submit"
                        class="md-raised md-primary">
                 {{ 'action.assign' | translate }}
             </md-button>
-            <md-button ng-disabled="loading" ng-click="vm.cancel()" style="margin-right:20px;">{{ 'action.cancel' |
+            <md-button ng-disabled="$root.loading" ng-click="vm.cancel()" style="margin-right:20px;">{{ 'action.cancel' |
                 translate }}
             </md-button>
         </md-dialog-actions>
diff --git a/ui/src/app/dashboard/add-widget.controller.js b/ui/src/app/dashboard/add-widget.controller.js
index c15492d..bae486b 100644
--- a/ui/src/app/dashboard/add-widget.controller.js
+++ b/ui/src/app/dashboard/add-widget.controller.js
@@ -110,7 +110,7 @@ export default function AddWidgetController($scope, widgetService, entityService
             function success(aliasInfo) {
                 var entity = aliasInfo.currentEntity;
                 if (entity) {
-                    entityService.getEntityKeys(entity.entityType, entity.id, query, type).then(
+                    entityService.getEntityKeys(entity.entityType, entity.id, query, type, {ignoreLoading: true}).then(
                         function success(keys) {
                             deferred.resolve(keys);
                         },
diff --git a/ui/src/app/dashboard/add-widget.tpl.html b/ui/src/app/dashboard/add-widget.tpl.html
index 4ca29f0..8d5e130 100644
--- a/ui/src/app/dashboard/add-widget.tpl.html
+++ b/ui/src/app/dashboard/add-widget.tpl.html
@@ -27,11 +27,11 @@
                 </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-progress-linear class="md-warn" md-mode="indeterminate" ng-disabled="!$root.loading" ng-show="$root.loading"></md-progress-linear>
+        <span style="min-height: 5px;" flex="" ng-show="!$root.loading"></span>
         <md-dialog-content>
             <div class="md-dialog-content" style="padding-top: 0px;">
-                <fieldset ng-disabled="loading" style="position: relative; height: 600px;">
+                <fieldset ng-disabled="$root.loading" style="position: relative; height: 600px;">
                     <tb-widget-config widget-type="vm.widget.type"
                                       type-parameters="vm.widgetInfo.typeParameters"
                                       action-sources="vm.widgetInfo.actionSources"
@@ -50,11 +50,11 @@
         </md-dialog-content>
         <md-dialog-actions layout="row">
             <span flex></span>
-            <md-button ng-disabled="loading || theForm.$invalid" type="submit"
+            <md-button ng-disabled="$root.loading || theForm.$invalid" type="submit"
                        class="md-raised md-primary">
                 {{ 'action.add' | translate }}
             </md-button>
-            <md-button ng-disabled="loading" ng-click="vm.cancel()" style="margin-right:20px;">{{ 'action.cancel' |
+            <md-button ng-disabled="$root.loading" ng-click="vm.cancel()" style="margin-right:20px;">{{ 'action.cancel' |
                 translate }}
             </md-button>
         </md-dialog-actions>
diff --git a/ui/src/app/dashboard/assign-to-customer.tpl.html b/ui/src/app/dashboard/assign-to-customer.tpl.html
index b08e44a..4d7ba85 100644
--- a/ui/src/app/dashboard/assign-to-customer.tpl.html
+++ b/ui/src/app/dashboard/assign-to-customer.tpl.html
@@ -26,8 +26,8 @@
                 </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-progress-linear class="md-warn" md-mode="indeterminate" ng-disabled="!$root.loading" ng-show="$root.loading"></md-progress-linear>
+        <span style="min-height: 5px;" flex="" ng-show="!$root.loading"></span>
         <md-dialog-content>
             <div class="md-dialog-content">
                 <fieldset>
@@ -65,10 +65,10 @@
         </md-dialog-content>
         <md-dialog-actions layout="row">
             <span flex></span>
-            <md-button ng-disabled="loading || vm.customers.selection==null" type="submit" class="md-raised md-primary">
+            <md-button ng-disabled="$root.loading || vm.customers.selection==null" type="submit" class="md-raised md-primary">
                 {{ 'action.assign' | translate }}
             </md-button>
-            <md-button ng-disabled="loading" ng-click="vm.cancel()" style="margin-right:20px;">{{ 'action.cancel' |
+            <md-button ng-disabled="$root.loading" ng-click="vm.cancel()" style="margin-right:20px;">{{ 'action.cancel' |
                 translate }}
             </md-button>
         </md-dialog-actions>
diff --git a/ui/src/app/dashboard/dashboard.tpl.html b/ui/src/app/dashboard/dashboard.tpl.html
index 87f2840..fab4634 100644
--- a/ui/src/app/dashboard/dashboard.tpl.html
+++ b/ui/src/app/dashboard/dashboard.tpl.html
@@ -110,7 +110,7 @@
     </section>
     <section class="tb-dashboard-container tb-absolute-fill"
              ng-class="{ 'is-fullscreen': forceFullscreen, 'tb-dashboard-toolbar-opened': vm.toolbarOpened, 'tb-dashboard-toolbar-closed': !vm.toolbarOpened }">
-        <section ng-show="!loading && vm.dashboardConfigurationError()" layout-align="center center"
+        <section ng-show="!$root.loading && vm.dashboardConfigurationError()" layout-align="center center"
                  ng-style="{'color': vm.dashboard.configuration.settings.titleColor}"
                  ng-class="{'tb-padded' : !vm.widgetEditMode}"
                  style="text-transform: uppercase; display: flex; z-index: 1;"
@@ -277,10 +277,10 @@
             </div>
         </tb-details-sidenav>
         <section layout="row" layout-wrap class="tb-footer-buttons md-fab" layout-align="start end">
-            <md-fab-speed-dial ng-disabled="loading" ng-show="!vm.isAddingWidget && vm.isEdit && !vm.widgetEditMode"
+            <md-fab-speed-dial ng-disabled="$root.loading" ng-show="!vm.isAddingWidget && vm.isEdit && !vm.widgetEditMode"
                                md-open="vm.addItemActionsOpen" class="md-scale" md-direction="up">
                 <md-fab-trigger>
-                    <md-button ng-disabled="loading"
+                    <md-button ng-disabled="$root.loading"
                                class="tb-btn-footer md-accent md-hue-2 md-fab"
                                aria-label="{{ 'dashboard.add-widget' | translate }}">
                         <md-tooltip md-direction="top">
@@ -290,7 +290,7 @@
                     </md-button>
                 </md-fab-trigger>
                 <md-fab-actions>
-                    <md-button ng-disabled="loading"
+                    <md-button ng-disabled="$root.loading"
                                class="tmd-accent md-hue-2 md-fab" ng-click="vm.addWidget($event)"
                                aria-label="{{ 'action.create' | translate }}">
                         <md-tooltip md-direction="top">
@@ -298,7 +298,7 @@
                         </md-tooltip>
                         <ng-md-icon icon="insert_drive_file"></ng-md-icon>
                     </md-button>
-                    <md-button ng-disabled="loading"
+                    <md-button ng-disabled="$root.loading"
                                class="tmd-accent md-hue-2 md-fab" ng-click="vm.importWidget($event)"
                                aria-label="{{ 'action.import' | translate }}">
                         <md-tooltip md-direction="top">
@@ -308,7 +308,7 @@
                     </md-button>
                 </md-fab-actions>
             </md-fab-speed-dial>
-            <md-button ng-if="(vm.isTenantAdmin() || vm.isSystemAdmin()) && !forceFullscreen" ng-show="vm.isEdit && !vm.isAddingWidget && !loading" ng-disabled="loading"
+            <md-button ng-if="(vm.isTenantAdmin() || vm.isSystemAdmin()) && !forceFullscreen" ng-show="vm.isEdit && !vm.isAddingWidget && !$root.loading" ng-disabled="$root.loading"
                        class="tb-btn-footer md-accent md-hue-2 md-fab"
                        aria-label="{{ 'action.apply' | translate }}"
                        ng-click="vm.saveDashboard()">
@@ -317,8 +317,8 @@
                 </md-tooltip>
                 <ng-md-icon icon="done"></ng-md-icon>
             </md-button>
-            <md-button ng-show="!vm.isAddingWidget && !loading"
-                       ng-if="(vm.isTenantAdmin() || vm.isSystemAdmin()) && !forceFullscreen" ng-disabled="loading"
+            <md-button ng-show="!vm.isAddingWidget && !$root.loading"
+                       ng-if="(vm.isTenantAdmin() || vm.isSystemAdmin()) && !forceFullscreen" ng-disabled="$root.loading"
                        class="tb-btn-footer md-accent md-hue-2 md-fab"
                        aria-label="{{ 'action.edit-mode' | translate }}"
                        ng-click="vm.toggleDashboardEditMode()">
diff --git a/ui/src/app/dashboard/dashboard-fieldset.tpl.html b/ui/src/app/dashboard/dashboard-fieldset.tpl.html
index 954d881..a54f477 100644
--- a/ui/src/app/dashboard/dashboard-fieldset.tpl.html
+++ b/ui/src/app/dashboard/dashboard-fieldset.tpl.html
@@ -59,7 +59,7 @@
 			</md-button>
 		</div>
 	</div>
-	<fieldset ng-disabled="loading || !isEdit">
+	<fieldset ng-disabled="$root.loading || !isEdit">
 		<md-input-container class="md-block">
 			<label translate>dashboard.title</label>
 			<input required name="title" ng-model="dashboard.title">	
diff --git a/ui/src/app/dashboard/dashboard-settings.tpl.html b/ui/src/app/dashboard/dashboard-settings.tpl.html
index 38e2af3..da2b254 100644
--- a/ui/src/app/dashboard/dashboard-settings.tpl.html
+++ b/ui/src/app/dashboard/dashboard-settings.tpl.html
@@ -26,11 +26,11 @@
                 </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-progress-linear class="md-warn" md-mode="indeterminate" ng-disabled="!$root.loading" ng-show="$root.loading"></md-progress-linear>
+        <span style="min-height: 5px;" flex="" ng-show="!$root.loading"></span>
         <md-dialog-content>
             <div class="md-dialog-content">
-                <fieldset ng-disabled="loading">
+                <fieldset ng-disabled="$root.loading">
                     <div ng-show="vm.settings">
                        <md-input-container class="md-block">
                             <label translate>dashboard.state-controller</label>
@@ -194,10 +194,10 @@
         </md-dialog-content>
         <md-dialog-actions layout="row">
             <span flex></span>
-            <md-button ng-disabled="loading || !theForm.$dirty || !theForm.$valid" type="submit" class="md-raised md-primary">
+            <md-button ng-disabled="$root.loading || !theForm.$dirty || !theForm.$valid" type="submit" class="md-raised md-primary">
                 {{ 'action.save' | translate }}
             </md-button>
-            <md-button ng-disabled="loading" ng-click="vm.cancel()" style="margin-right:20px;">{{ 'action.cancel' | translate }}</md-button>
+            <md-button ng-disabled="$root.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/dashboard/edit-widget.directive.js b/ui/src/app/dashboard/edit-widget.directive.js
index 9bf1548..9357812 100644
--- a/ui/src/app/dashboard/edit-widget.directive.js
+++ b/ui/src/app/dashboard/edit-widget.directive.js
@@ -75,7 +75,7 @@ export default function EditWidgetDirective($compile, $templateCache, types, wid
                 function success(aliasInfo) {
                     var entity = aliasInfo.currentEntity;
                     if (entity) {
-                        entityService.getEntityKeys(entity.entityType, entity.id, query, type).then(
+                        entityService.getEntityKeys(entity.entityType, entity.id, query, type, {ignoreLoading: true}).then(
                             function success(keys) {
                                 deferred.resolve(keys);
                             },
diff --git a/ui/src/app/dashboard/edit-widget.tpl.html b/ui/src/app/dashboard/edit-widget.tpl.html
index 1f724a8..5b225e2 100644
--- a/ui/src/app/dashboard/edit-widget.tpl.html
+++ b/ui/src/app/dashboard/edit-widget.tpl.html
@@ -15,7 +15,7 @@
     limitations under the License.
 
 -->
-<fieldset ng-disabled="loading">
+<fieldset ng-disabled="$root.loading">
 	<tb-widget-config widget-type="widget.type"
 					  type-parameters="typeParameters"
 					  action-sources="actionSources"
diff --git a/ui/src/app/dashboard/layouts/dashboard-layout.tpl.html b/ui/src/app/dashboard/layouts/dashboard-layout.tpl.html
index d9c7c23..c54901e 100644
--- a/ui/src/app/dashboard/layouts/dashboard-layout.tpl.html
+++ b/ui/src/app/dashboard/layouts/dashboard-layout.tpl.html
@@ -22,7 +22,7 @@
                     'background-attachment': 'scroll',
                     'background-size': vm.layoutCtx.gridSettings.backgroundSizeMode || '100%',
                     'background-position': '0% 0%'}">
-    <section ng-show="!loading && vm.noData()" layout-align="center center"
+    <section ng-show="!$root.loading && vm.noData()" layout-align="center center"
              ng-style="{'color': vm.layoutCtx.gridSettings.color}"
              style="text-transform: uppercase; display: flex; z-index: 1; pointer-events: none;"
              class="md-headline tb-absolute-fill">
diff --git a/ui/src/app/dashboard/layouts/manage-dashboard-layouts.tpl.html b/ui/src/app/dashboard/layouts/manage-dashboard-layouts.tpl.html
index d4d95db..335563d 100644
--- a/ui/src/app/dashboard/layouts/manage-dashboard-layouts.tpl.html
+++ b/ui/src/app/dashboard/layouts/manage-dashboard-layouts.tpl.html
@@ -26,11 +26,11 @@
                 </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-progress-linear class="md-warn" md-mode="indeterminate" ng-disabled="!$root.loading" ng-show="$root.loading"></md-progress-linear>
+        <span style="min-height: 5px;" flex="" ng-show="!$root.loading"></span>
         <md-dialog-content>
             <div class="md-dialog-content">
-                <fieldset ng-disabled="loading">
+                <fieldset ng-disabled="$root.loading">
                     <div layout="row" layout-align="start center">
                         <md-checkbox ng-disabled="true" flex aria-label="{{ 'layout.main' | translate }}"
                                      ng-model="vm.displayLayouts.main">{{ 'layout.main' | translate }}
@@ -56,10 +56,10 @@
         </md-dialog-content>
         <md-dialog-actions layout="row">
             <span flex></span>
-            <md-button ng-disabled="loading || !theForm.$dirty || !theForm.$valid" type="submit" class="md-raised md-primary">
+            <md-button ng-disabled="$root.loading || !theForm.$dirty || !theForm.$valid" type="submit" class="md-raised md-primary">
                 {{ 'action.save' | translate }}
             </md-button>
-            <md-button ng-disabled="loading" ng-click="vm.cancel()" style="margin-right:20px;">{{ 'action.cancel' | translate }}</md-button>
+            <md-button ng-disabled="$root.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/dashboard/layouts/select-target-layout.tpl.html b/ui/src/app/dashboard/layouts/select-target-layout.tpl.html
index f69f487..a84f4f2 100644
--- a/ui/src/app/dashboard/layouts/select-target-layout.tpl.html
+++ b/ui/src/app/dashboard/layouts/select-target-layout.tpl.html
@@ -26,11 +26,11 @@
                 </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-progress-linear class="md-warn" md-mode="indeterminate" ng-disabled="!$root.loading" ng-show="$root.loading"></md-progress-linear>
+        <span style="min-height: 5px;" flex="" ng-show="!$root.loading"></span>
         <md-dialog-content>
             <div class="md-dialog-content">
-                <fieldset ng-disabled="loading">
+                <fieldset ng-disabled="$root.loading">
                     <div layout="row" layout-align="start center">
                         <md-button flex class="tb-layout-button md-raised md-primary" layout="column"
                                    ng-click="vm.selectLayout($event, 'main')">
diff --git a/ui/src/app/dashboard/states/dashboard-state-dialog.tpl.html b/ui/src/app/dashboard/states/dashboard-state-dialog.tpl.html
index 4cf0a3d..3f9fb43 100644
--- a/ui/src/app/dashboard/states/dashboard-state-dialog.tpl.html
+++ b/ui/src/app/dashboard/states/dashboard-state-dialog.tpl.html
@@ -26,12 +26,12 @@
                 </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-progress-linear class="md-warn" md-mode="indeterminate" ng-disabled="!$root.loading" ng-show="$root.loading"></md-progress-linear>
+        <span style="min-height: 5px;" flex="" ng-show="!$root.loading"></span>
         <md-dialog-content>
             <div class="md-dialog-content">
                 <md-content class="md-padding" layout="column">
-                    <fieldset ng-disabled="loading">
+                    <fieldset ng-disabled="$root.loading">
                         <md-input-container class="md-block">
                             <label translate>dashboard.state-name</label>
                             <input name="name" required ng-model="vm.state.name">
@@ -57,11 +57,11 @@
         </md-dialog-content>
         <md-dialog-actions layout="row">
             <span flex></span>
-            <md-button ng-disabled="loading || theForm.$invalid || !theForm.$dirty" type="submit"
+            <md-button ng-disabled="$root.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;">
+            <md-button ng-disabled="$root.loading" ng-click="vm.cancel()" style="margin-right:20px;">
                 {{ 'action.cancel' | translate }}
             </md-button>
         </md-dialog-actions>
diff --git a/ui/src/app/dashboard/states/manage-dashboard-states.tpl.html b/ui/src/app/dashboard/states/manage-dashboard-states.tpl.html
index 9f86b9b..9f73652 100644
--- a/ui/src/app/dashboard/states/manage-dashboard-states.tpl.html
+++ b/ui/src/app/dashboard/states/manage-dashboard-states.tpl.html
@@ -26,11 +26,11 @@
                 </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-progress-linear class="md-warn" md-mode="indeterminate" ng-disabled="!$root.loading" ng-show="$root.loading"></md-progress-linear>
+        <span style="min-height: 5px;" flex="" ng-show="!$root.loading"></span>
         <md-dialog-content>
             <div class="md-dialog-content">
-                <fieldset ng-disabled="loading">
+                <fieldset ng-disabled="$root.loading">
                     <div class="manage-dashboard-states" layout="column">
                         <md-toolbar class="md-table-toolbar md-default" ng-show="vm.query.search === null">
                             <div class="md-toolbar-tools">
@@ -118,10 +118,10 @@
         </md-dialog-content>
         <md-dialog-actions layout="row">
             <span flex></span>
-            <md-button ng-disabled="loading || !theForm.$dirty || !theForm.$valid" type="submit" class="md-raised md-primary">
+            <md-button ng-disabled="$root.loading || !theForm.$dirty || !theForm.$valid" type="submit" class="md-raised md-primary">
                 {{ 'action.save' | translate }}
             </md-button>
-            <md-button ng-disabled="loading" ng-click="vm.cancel()" style="margin-right:20px;">{{ 'action.cancel' | translate }}</md-button>
+            <md-button ng-disabled="$root.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/dashboard/states/select-target-state.tpl.html b/ui/src/app/dashboard/states/select-target-state.tpl.html
index c874a07..412b818 100644
--- a/ui/src/app/dashboard/states/select-target-state.tpl.html
+++ b/ui/src/app/dashboard/states/select-target-state.tpl.html
@@ -26,11 +26,11 @@
                 </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-progress-linear class="md-warn" md-mode="indeterminate" ng-disabled="!$root.loading" ng-show="$root.loading"></md-progress-linear>
+        <span style="min-height: 5px;" flex="" ng-show="!$root.loading"></span>
         <md-dialog-content>
             <div class="md-dialog-content">
-                <fieldset ng-disabled="loading">
+                <fieldset ng-disabled="$root.loading">
                     <md-select required aria-label="{{ 'dashboard.state' | translate }}" ng-model="vm.stateId">
                         <md-option ng-repeat="(stateId, state) in vm.states" ng-value="stateId">
                             {{state.name}}
@@ -41,10 +41,10 @@
         </md-dialog-content>
         <md-dialog-actions layout="row">
             <span flex></span>
-            <md-button ng-disabled="loading || !theForm.$dirty || !theForm.$valid" type="submit" class="md-raised md-primary">
+            <md-button ng-disabled="$root.loading || !theForm.$dirty || !theForm.$valid" type="submit" class="md-raised md-primary">
                 {{ 'action.save' | translate }}
             </md-button>
-            <md-button ng-disabled="loading" ng-click="vm.cancel()" style="margin-right:20px;">{{ 'action.cancel' | translate }}</md-button>
+            <md-button ng-disabled="$root.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/device/add-device.tpl.html b/ui/src/app/device/add-device.tpl.html
index 4db792f..3e26d70 100644
--- a/ui/src/app/device/add-device.tpl.html
+++ b/ui/src/app/device/add-device.tpl.html
@@ -27,8 +27,8 @@
 	        </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-progress-linear class="md-warn" md-mode="indeterminate" ng-disabled="!$root.loading" ng-show="$root.loading"></md-progress-linear>
+  	    <span style="min-height: 5px;" flex="" ng-show="!$root.loading"></span>
 	    <md-dialog-content>
 	      <div class="md-dialog-content">
   	        	<tb-device device="vm.item" is-edit="true" the-form="theForm"></tb-device>
@@ -36,10 +36,10 @@
 	    </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">
+		  <md-button ng-disabled="$root.loading || theForm.$invalid || !theForm.$dirty" type="submit" class="md-raised md-primary">
 		  		{{ 'action.add' | translate }}
 		  </md-button>
-	      <md-button ng-disabled="loading" ng-click="vm.cancel()" style="margin-right:20px;">{{ 'action.cancel' | translate }}</md-button>
+	      <md-button ng-disabled="$root.loading" ng-click="vm.cancel()" style="margin-right:20px;">{{ 'action.cancel' | translate }}</md-button>
 	    </md-dialog-actions>
 	</form>    
 </md-dialog>
\ No newline at end of file
diff --git a/ui/src/app/device/add-devices-to-customer.tpl.html b/ui/src/app/device/add-devices-to-customer.tpl.html
index 0795f95..04b28fd 100644
--- a/ui/src/app/device/add-devices-to-customer.tpl.html
+++ b/ui/src/app/device/add-devices-to-customer.tpl.html
@@ -26,8 +26,8 @@
                 </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-progress-linear class="md-warn" md-mode="indeterminate" ng-disabled="!$root.loading" ng-show="$root.loading"></md-progress-linear>
+        <span style="min-height: 5px;" flex="" ng-show="!$root.loading"></span>
         <md-dialog-content>
             <div class="md-dialog-content">
                 <fieldset>
@@ -65,11 +65,11 @@
         </md-dialog-content>
         <md-dialog-actions layout="row">
             <span flex></span>
-            <md-button ng-disabled="loading || vm.devices.selectedCount == 0" type="submit"
+            <md-button ng-disabled="$root.loading || vm.devices.selectedCount == 0" type="submit"
                        class="md-raised md-primary">
                 {{ 'action.assign' | translate }}
             </md-button>
-            <md-button ng-disabled="loading" ng-click="vm.cancel()" style="margin-right:20px;">{{ 'action.cancel' |
+            <md-button ng-disabled="$root.loading" ng-click="vm.cancel()" style="margin-right:20px;">{{ 'action.cancel' |
                 translate }}
             </md-button>
         </md-dialog-actions>
diff --git a/ui/src/app/device/assign-to-customer.tpl.html b/ui/src/app/device/assign-to-customer.tpl.html
index b9736c6..7bc2a12 100644
--- a/ui/src/app/device/assign-to-customer.tpl.html
+++ b/ui/src/app/device/assign-to-customer.tpl.html
@@ -26,8 +26,8 @@
                 </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-progress-linear class="md-warn" md-mode="indeterminate" ng-disabled="!$root.loading" ng-show="$root.loading"></md-progress-linear>
+        <span style="min-height: 5px;" flex="" ng-show="!$root.loading"></span>
         <md-dialog-content>
             <div class="md-dialog-content">
                 <fieldset>
@@ -65,10 +65,10 @@
         </md-dialog-content>
         <md-dialog-actions layout="row">
             <span flex></span>
-            <md-button ng-disabled="loading || vm.customers.selection==null" type="submit" class="md-raised md-primary">
+            <md-button ng-disabled="$root.loading || vm.customers.selection==null" type="submit" class="md-raised md-primary">
                 {{ 'action.assign' | translate }}
             </md-button>
-            <md-button ng-disabled="loading" ng-click="vm.cancel()" style="margin-right:20px;">{{ 'action.cancel' |
+            <md-button ng-disabled="$root.loading" ng-click="vm.cancel()" style="margin-right:20px;">{{ 'action.cancel' |
                 translate }}
             </md-button>
         </md-dialog-actions>
diff --git a/ui/src/app/device/device-credentials.tpl.html b/ui/src/app/device/device-credentials.tpl.html
index 705f983..c050349 100644
--- a/ui/src/app/device/device-credentials.tpl.html
+++ b/ui/src/app/device/device-credentials.tpl.html
@@ -26,14 +26,14 @@
 	        </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-progress-linear class="md-warn" md-mode="indeterminate" ng-disabled="!$root.loading" ng-show="$root.loading"></md-progress-linear>
+  	    <span style="min-height: 5px;" flex="" ng-show="!$root.loading"></span>
 	    <md-dialog-content>
 	      <div class="md-dialog-content">
-	        <fieldset ng-disabled="loading || vm.isReadOnly">
+	        <fieldset ng-disabled="$root.loading || vm.isReadOnly">
 				<md-input-container class="md-block">
 					<label translate>device.credentials-type</label>
-		            <md-select ng-disabled="loading || vm.isReadOnly" ng-model="vm.deviceCredentials.credentialsType"
+		            <md-select ng-disabled="$root.loading || vm.isReadOnly" ng-model="vm.deviceCredentials.credentialsType"
 					    ng-change="vm.clear()">
 		              	<md-option ng-repeat="credentialsType in vm.credentialsTypes" value="{{credentialsType.value}}">
 		                	{{credentialsType.name}}
@@ -62,10 +62,10 @@
 	    </md-dialog-content>
 	    <md-dialog-actions layout="row">
 	      <span flex></span>
-		  <md-button ng-if="!vm.isReadOnly" ng-disabled="loading || theForm.$invalid || !theForm.$dirty || !vm.valid()" type="submit" class="md-raised md-primary">
+		  <md-button ng-if="!vm.isReadOnly" ng-disabled="$root.loading || theForm.$invalid || !theForm.$dirty || !vm.valid()" type="submit" class="md-raised md-primary">
 		  		{{ 'action.save' | translate }}
 		  </md-button>
-	      <md-button ng-disabled="loading" ng-click="vm.cancel()" style="margin-right:20px;">{{ (vm.isReadOnly ? 'action.close' : 'action.cancel') | translate }}</md-button>
+	      <md-button ng-disabled="$root.loading" ng-click="vm.cancel()" style="margin-right:20px;">{{ (vm.isReadOnly ? 'action.close' : 'action.cancel') | translate }}</md-button>
 	    </md-dialog-actions>
 	</form>    
 </md-dialog>
\ No newline at end of file
diff --git a/ui/src/app/device/device-fieldset.tpl.html b/ui/src/app/device/device-fieldset.tpl.html
index 767e47a..a4d15f3 100644
--- a/ui/src/app/device/device-fieldset.tpl.html
+++ b/ui/src/app/device/device-fieldset.tpl.html
@@ -58,7 +58,7 @@
          ng-show="!isEdit && isPublic && (deviceScope === 'customer' || deviceScope === 'tenant')">
         {{ 'device.device-public' | translate }}
     </div>
-	<fieldset ng-disabled="loading || !isEdit">
+	<fieldset ng-disabled="$root.loading || !isEdit">
 		<md-input-container class="md-block">
 			<label translate>device.name</label>
 			<input required name="name" ng-model="device.name">	
@@ -67,14 +67,14 @@
 	    	</div>				
 		</md-input-container>
         <tb-entity-subtype-autocomplete
-                ng-disabled="loading || !isEdit"
+                ng-disabled="$root.loading || !isEdit"
                 tb-required="true"
                 the-form="theForm"
                 ng-model="device.type"
                 entity-type="types.entityType.device">
         </tb-entity-subtype-autocomplete>
         <md-input-container class="md-block">
-            <md-checkbox ng-disabled="loading || !isEdit" flex aria-label="{{ 'device.is-gateway' | translate }}"
+            <md-checkbox ng-disabled="$root.loading || !isEdit" flex aria-label="{{ 'device.is-gateway' | translate }}"
                          ng-model="device.additionalInfo.gateway">{{ 'device.is-gateway' | translate }}
             </md-checkbox>
         </md-input-container>
diff --git a/ui/src/app/entity/alias/entity-alias-dialog.tpl.html b/ui/src/app/entity/alias/entity-alias-dialog.tpl.html
index b78a795..546f8bf 100644
--- a/ui/src/app/entity/alias/entity-alias-dialog.tpl.html
+++ b/ui/src/app/entity/alias/entity-alias-dialog.tpl.html
@@ -26,11 +26,11 @@
                 </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-progress-linear class="md-warn" md-mode="indeterminate" ng-disabled="!$root.loading" ng-show="$root.loading"></md-progress-linear>
+        <span style="min-height: 5px;" flex="" ng-show="!$root.loading"></span>
         <md-dialog-content>
             <div class="md-dialog-content">
-                <fieldset ng-disabled="loading">
+                <fieldset ng-disabled="$root.loading">
                     <div flex layout="column">
                         <div layout="row">
                             <md-input-container flex class="md-block">
@@ -64,10 +64,10 @@
         </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">
+            <md-button ng-disabled="$root.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-button ng-disabled="$root.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.tpl.html b/ui/src/app/entity/alias/entity-aliases.tpl.html
index 40010a2..227619b 100644
--- a/ui/src/app/entity/alias/entity-aliases.tpl.html
+++ b/ui/src/app/entity/alias/entity-aliases.tpl.html
@@ -26,8 +26,8 @@
 				</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-progress-linear class="md-warn" md-mode="indeterminate" ng-disabled="!$root.loading" ng-show="$root.loading"></md-progress-linear>
+		<span style="min-height: 5px;" flex="" ng-show="!$root.loading"></span>
 		<div class="tb-aliases-header" flex layout="row" layout-align="start center">
 			<span flex="5"></span>
 			<div flex layout="row" layout-align="start center">
@@ -40,7 +40,7 @@
 		<md-divider></md-divider>
 		<md-dialog-content>
 			<div class="md-dialog-content">
-				<fieldset ng-disabled="loading">
+				<fieldset ng-disabled="$root.loading">
 					<div ng-form name="aliasForm" flex layout="row" layout-align="start center" ng-repeat="entityAlias in vm.entityAliases track by $index">
 						<span flex="5">{{$index + 1}}.</span>
 						<di class="md-whiteframe-4dp tb-alias" flex layout="row" layout-align="start center">
@@ -63,7 +63,7 @@
 										   aria-label="resolve-multiple-switcher">
 								</md-switch>
 							</section>
-							<md-button ng-disabled="loading" class="md-icon-button md-primary" style="min-width: 40px;"
+							<md-button ng-disabled="$root.loading" class="md-icon-button md-primary" style="min-width: 40px;"
 									   ng-click="vm.editAlias($event, entityAlias)" aria-label="{{ 'action.edit' | translate }}">
 								<md-tooltip md-direction="top">
 									{{ 'alias.edit' | translate }}
@@ -72,7 +72,7 @@
 									edit
 								</md-icon>
 							</md-button>
-							<md-button ng-disabled="loading" class="md-icon-button md-primary" style="min-width: 40px;"
+							<md-button ng-disabled="$root.loading" class="md-icon-button md-primary" style="min-width: 40px;"
 									   ng-click="vm.removeAlias($event, entityAlias)" aria-label="{{ 'action.remove' | translate }}">
 								<md-tooltip md-direction="top">
 									{{ 'entity.remove-alias' | translate }}
@@ -87,7 +87,7 @@
 			</div>
 		</md-dialog-content>
 		<md-dialog-actions layout="row">
-			<md-button ng-show="!vm.disableAdd" ng-disabled="loading" class="md-primary md-raised"
+			<md-button ng-show="!vm.disableAdd" ng-disabled="$root.loading" class="md-primary md-raised"
 					   ng-click="vm.addAlias($event)"
 					   aria-label="{{ 'alias.add' | translate }}">
 				<md-tooltip md-direction="top">
@@ -96,10 +96,10 @@
 				<span translate>alias.add</span>
 			</md-button>
 			<span flex></span>
-			<md-button ng-disabled="loading || theForm.$invalid || !theForm.$dirty" type="submit" class="md-raised md-primary">
+			<md-button ng-disabled="$root.loading || theForm.$invalid || !theForm.$dirty" type="submit" class="md-raised md-primary">
 				{{ 'action.save' | translate }}
 			</md-button>
-			<md-button ng-disabled="loading" ng-click="vm.cancel()" style="margin-right:20px;">{{ 'action.cancel' | translate }}</md-button>
+			<md-button ng-disabled="$root.loading" ng-click="vm.cancel()" style="margin-right:20px;">{{ 'action.cancel' | translate }}</md-button>
 		</md-dialog-actions>
 	</form>
 </md-dialog>
\ No newline at end of file
diff --git a/ui/src/app/entity/attribute/add-attribute-dialog.tpl.html b/ui/src/app/entity/attribute/add-attribute-dialog.tpl.html
index 18f99fc..6df9343 100644
--- a/ui/src/app/entity/attribute/add-attribute-dialog.tpl.html
+++ b/ui/src/app/entity/attribute/add-attribute-dialog.tpl.html
@@ -26,12 +26,12 @@
                 </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-progress-linear class="md-warn" md-mode="indeterminate" ng-disabled="!$root.loading" ng-show="$root.loading"></md-progress-linear>
+        <span style="min-height: 5px;" flex="" ng-show="!$root.loading"></span>
         <md-dialog-content>
             <div class="md-dialog-content">
                 <md-content class="md-padding" layout="column">
-                    <fieldset ng-disabled="loading">
+                    <fieldset ng-disabled="$root.loading">
                         <md-input-container class="md-block">
                             <label translate>attribute.key</label>
                             <input required name="key" ng-model="vm.attribute.key">
@@ -42,7 +42,7 @@
                         <section layout="row">
                             <md-input-container flex="40" class="md-block" style="width: 200px;">
                                 <label translate>value.type</label>
-                                <md-select ng-model="vm.valueType" ng-disabled="loading()">
+                                <md-select ng-model="vm.valueType" ng-disabled="$root.loading">
                                     <md-option ng-repeat="type in vm.valueTypes" ng-value="type">
                                         <md-icon md-svg-icon="{{ type.icon }}"></md-icon>
                                         <span>{{type.name | translate}}</span>
@@ -83,11 +83,11 @@
         </md-dialog-content>
         <md-dialog-actions layout="row">
             <span flex></span>
-            <md-button ng-disabled="loading || theForm.$invalid || !theForm.$dirty" type="submit"
+            <md-button ng-disabled="$root.loading || theForm.$invalid || !theForm.$dirty" type="submit"
                        class="md-raised md-primary">
                 {{ 'action.add' | translate }}
             </md-button>
-            <md-button ng-disabled="loading" ng-click="vm.cancel()" style="margin-right:20px;">{{ 'action.cancel' |
+            <md-button ng-disabled="$root.loading" ng-click="vm.cancel()" style="margin-right:20px;">{{ 'action.cancel' |
                 translate }}
             </md-button>
         </md-dialog-actions>
diff --git a/ui/src/app/entity/attribute/add-widget-to-dashboard-dialog.tpl.html b/ui/src/app/entity/attribute/add-widget-to-dashboard-dialog.tpl.html
index d7fe890..d74aa34 100644
--- a/ui/src/app/entity/attribute/add-widget-to-dashboard-dialog.tpl.html
+++ b/ui/src/app/entity/attribute/add-widget-to-dashboard-dialog.tpl.html
@@ -26,18 +26,18 @@
                 </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-progress-linear class="md-warn" md-mode="indeterminate" ng-disabled="!$root.loading" ng-show="$root.loading"></md-progress-linear>
+        <span style="min-height: 5px;" flex="" ng-show="!$root.loading"></span>
         <md-dialog-content>
             <div class="md-dialog-content">
                 <md-content class="md-padding" layout="column">
-                    <fieldset ng-disabled="loading">
+                    <fieldset ng-disabled="$root.loading">
                         <md-radio-group ng-model="vm.addToDashboardType" class="md-primary">
                             <md-radio-button flex ng-value=0 class="md-primary md-align-top-left md-radio-interactive">
                                 <section flex layout="column" style="width: 300px;">
                                     <span translate style="padding-bottom: 10px;">dashboard.select-existing</span>
                                     <tb-dashboard-autocomplete the-form="theForm"
-                                                         ng-disabled="loading || vm.addToDashboardType != 0"
+                                                         ng-disabled="$root.loading || vm.addToDashboardType != 0"
                                                          tb-required="vm.addToDashboardType === 0"
                                                          ng-model="vm.dashboardId"
                                                          select-first-dashboard="false">
@@ -69,11 +69,11 @@
                     style="margin-bottom: 0px; padding-right: 20px;">
                 {{ 'dashboard.open-dashboard' | translate }}
             </md-checkbox>
-            <md-button ng-disabled="loading || theForm.$invalid || !theForm.$dirty" type="submit"
+            <md-button ng-disabled="$root.loading || theForm.$invalid || !theForm.$dirty" type="submit"
                        class="md-raised md-primary">
                 {{ 'action.add' | translate }}
             </md-button>
-            <md-button ng-disabled="loading" ng-click="vm.cancel()" style="margin-right:20px;">{{ 'action.cancel' |
+            <md-button ng-disabled="$root.loading" ng-click="vm.cancel()" style="margin-right:20px;">{{ 'action.cancel' |
                 translate }}
             </md-button>
         </md-dialog-actions>
diff --git a/ui/src/app/entity/attribute/attribute-table.scss b/ui/src/app/entity/attribute/attribute-table.scss
index cdf9ffd..3f40cdf 100644
--- a/ui/src/app/entity/attribute/attribute-table.scss
+++ b/ui/src/app/entity/attribute/attribute-table.scss
@@ -47,7 +47,7 @@ md-toolbar.md-table-toolbar.alternate {
 .widgets-carousel {
   position: relative;
   margin: 0px;
-
+  height: calc(100% - 100px);
   min-height: 150px !important;
 
   tb-dashboard {
diff --git a/ui/src/app/entity/attribute/attribute-table.tpl.html b/ui/src/app/entity/attribute/attribute-table.tpl.html
index 3b19e9d..f7075fb 100644
--- a/ui/src/app/entity/attribute/attribute-table.tpl.html
+++ b/ui/src/app/entity/attribute/attribute-table.tpl.html
@@ -19,7 +19,7 @@
     <section ng-show="!disableAttributeScopeSelection">
         <md-input-container class="md-block" style="width: 200px;">
             <label translate>attribute.attributes-scope</label>
-            <md-select ng-model="attributeScope" ng-disabled="loading() || attributeScopeSelectionReadonly">
+            <md-select ng-model="attributeScope" ng-disabled="$root.loading || attributeScopeSelectionReadonly">
                 <md-option ng-repeat="scope in attributeScopes" ng-value="scope">
                     {{scope.name | translate}}
                 </md-option>
diff --git a/ui/src/app/entity/entity-autocomplete.directive.js b/ui/src/app/entity/entity-autocomplete.directive.js
index 4610426..62f3d6d 100644
--- a/ui/src/app/entity/entity-autocomplete.directive.js
+++ b/ui/src/app/entity/entity-autocomplete.directive.js
@@ -38,7 +38,7 @@ export default function EntityAutocomplete($compile, $templateCache, $q, $filter
             if (scope.excludeEntityIds && scope.excludeEntityIds.length) {
                 limit += scope.excludeEntityIds.length;
             }
-            entityService.getEntitiesByNameFilter(scope.entityType, searchText, limit, null, scope.entitySubtype).then(function success(result) {
+            entityService.getEntitiesByNameFilter(scope.entityType, searchText, limit, {ignoreLoading: true}, scope.entitySubtype).then(function success(result) {
                 if (result) {
                     if (scope.excludeEntityIds && scope.excludeEntityIds.length) {
                         var entities = [];
diff --git a/ui/src/app/entity/entity-list.directive.js b/ui/src/app/entity/entity-list.directive.js
index 0863153..f0bd7d9 100644
--- a/ui/src/app/entity/entity-list.directive.js
+++ b/ui/src/app/entity/entity-list.directive.js
@@ -38,7 +38,7 @@ export default function EntityListDirective($compile, $templateCache, $q, $mdUti
 
         scope.fetchEntities = function(searchText, limit) {
              var deferred = $q.defer();
-             entityService.getEntitiesByNameFilter(scope.entityType, searchText, limit).then(
+             entityService.getEntitiesByNameFilter(scope.entityType, searchText, limit, {ignoreLoading: true}).then(
                  function success(result) {
                     if (result) {
                         deferred.resolve(result);
diff --git a/ui/src/app/entity/entity-subtype-autocomplete.directive.js b/ui/src/app/entity/entity-subtype-autocomplete.directive.js
index 57f2e04..511f275 100644
--- a/ui/src/app/entity/entity-subtype-autocomplete.directive.js
+++ b/ui/src/app/entity/entity-subtype-autocomplete.directive.js
@@ -93,9 +93,9 @@ export default function EntitySubtypeAutocomplete($compile, $templateCache, $q, 
             if (!scope.entitySubtypes) {
                 var entitySubtypesPromise;
                 if (scope.entityType == types.entityType.asset) {
-                    entitySubtypesPromise = assetService.getAssetTypes();
+                    entitySubtypesPromise = assetService.getAssetTypes({ignoreLoading: true});
                 } else if (scope.entityType == types.entityType.device) {
-                    entitySubtypesPromise = deviceService.getDeviceTypes();
+                    entitySubtypesPromise = deviceService.getDeviceTypes({ignoreLoading: true});
                 }
                 if (entitySubtypesPromise) {
                     entitySubtypesPromise.then(
diff --git a/ui/src/app/entity/entity-subtype-list.directive.js b/ui/src/app/entity/entity-subtype-list.directive.js
index c7d6329..4468f64 100644
--- a/ui/src/app/entity/entity-subtype-list.directive.js
+++ b/ui/src/app/entity/entity-subtype-list.directive.js
@@ -95,9 +95,9 @@ export default function EntitySubtypeListDirective($compile, $templateCache, $q,
             if (!scope.entitySubtypes) {
                 var entitySubtypesPromise;
                 if (scope.entityType == types.entityType.asset) {
-                    entitySubtypesPromise = assetService.getAssetTypes();
+                    entitySubtypesPromise = assetService.getAssetTypes({ignoreLoading: true});
                 } else if (scope.entityType == types.entityType.device) {
-                    entitySubtypesPromise = deviceService.getDeviceTypes();
+                    entitySubtypesPromise = deviceService.getDeviceTypes({ignoreLoading: true});
                 }
                 if (entitySubtypesPromise) {
                     entitySubtypesPromise.then(
diff --git a/ui/src/app/entity/entity-subtype-select.directive.js b/ui/src/app/entity/entity-subtype-select.directive.js
index 36d9729..72c4250 100644
--- a/ui/src/app/entity/entity-subtype-select.directive.js
+++ b/ui/src/app/entity/entity-subtype-select.directive.js
@@ -73,9 +73,9 @@ export default function EntitySubtypeSelect($compile, $templateCache, $translate
             scope.entitySubtypes = [];
             var entitySubtypesPromise;
             if (scope.entityType == types.entityType.asset) {
-                entitySubtypesPromise = assetService.getAssetTypes();
+                entitySubtypesPromise = assetService.getAssetTypes({ignoreLoading: true});
             } else if (scope.entityType == types.entityType.device) {
-                entitySubtypesPromise = deviceService.getDeviceTypes();
+                entitySubtypesPromise = deviceService.getDeviceTypes({ignoreLoading: true});
             }
             if (entitySubtypesPromise) {
                 entitySubtypesPromise.then(
diff --git a/ui/src/app/entity/relation/relation-dialog.tpl.html b/ui/src/app/entity/relation/relation-dialog.tpl.html
index 8799287..652b9d2 100644
--- a/ui/src/app/entity/relation/relation-dialog.tpl.html
+++ b/ui/src/app/entity/relation/relation-dialog.tpl.html
@@ -26,16 +26,16 @@
                 </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-progress-linear class="md-warn" md-mode="indeterminate" ng-disabled="!$root.loading" ng-show="$root.loading"></md-progress-linear>
+        <span style="min-height: 5px;" flex="" ng-show="!$root.loading"></span>
         <md-dialog-content>
             <div class="md-dialog-content">
                 <md-content class="md-padding" layout="column">
-                    <fieldset ng-disabled="loading">
+                    <fieldset ng-disabled="$root.loading">
                         <tb-relation-type-autocomplete ng-disabled="!vm.isAdd"
                                                        ng-model="vm.relation.type"
                                                        tb-required="true"
-                                                       ng-disabled="loading">
+                                                       ng-disabled="$root.loading">
                         </tb-relation-type-autocomplete>
                         <small>{{(vm.direction == vm.types.entitySearchDirection.from ?
                             'relation.to-entity' : 'relation.from-entity') | translate}}</small>
@@ -61,11 +61,11 @@
         </md-dialog-content>
         <md-dialog-actions layout="row">
             <span flex></span>
-            <md-button ng-disabled="loading || theForm.$invalid || !theForm.$dirty" type="submit"
+            <md-button ng-disabled="$root.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' |
+            <md-button ng-disabled="$root.loading" ng-click="vm.cancel()" style="margin-right:20px;">{{ 'action.cancel' |
                 translate }}
             </md-button>
         </md-dialog-actions>
diff --git a/ui/src/app/entity/relation/relation-filters.tpl.html b/ui/src/app/entity/relation/relation-filters.tpl.html
index 679a488..4f84fe1 100644
--- a/ui/src/app/entity/relation/relation-filters.tpl.html
+++ b/ui/src/app/entity/relation/relation-filters.tpl.html
@@ -37,7 +37,7 @@
                         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;"
+                <md-button ng-disabled="$root.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 }}
@@ -54,7 +54,7 @@
               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-button ng-disabled="$root.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>
diff --git a/ui/src/app/entity/relation/relation-table.tpl.html b/ui/src/app/entity/relation/relation-table.tpl.html
index 8599397..1553d93 100644
--- a/ui/src/app/entity/relation/relation-table.tpl.html
+++ b/ui/src/app/entity/relation/relation-table.tpl.html
@@ -19,7 +19,7 @@
     <section layout="row">
         <md-input-container class="md-block" style="width: 200px;">
             <label translate>relation.direction</label>
-            <md-select ng-model="vm.direction" ng-disabled="loading">
+            <md-select ng-model="vm.direction" ng-disabled="$root.loading">
                 <md-option ng-repeat="direction in vm.types.entitySearchDirection" ng-value="direction">
                     {{ ('relation.search-direction.' + direction) | translate}}
                 </md-option>
diff --git a/ui/src/app/event/event-content-dialog.tpl.html b/ui/src/app/event/event-content-dialog.tpl.html
index 7b4184c..9678616 100644
--- a/ui/src/app/event/event-content-dialog.tpl.html
+++ b/ui/src/app/event/event-content-dialog.tpl.html
@@ -35,7 +35,7 @@
         </md-dialog-content>
         <md-dialog-actions layout="row">
             <span flex></span>
-            <md-button ng-disabled="loading" ng-click="vm.close()" style="margin-right:20px;">{{ 'action.close' |
+            <md-button ng-disabled="$root.loading" ng-click="vm.close()" style="margin-right:20px;">{{ 'action.close' |
                 translate }}
             </md-button>
         </md-dialog-actions>
diff --git a/ui/src/app/event/event-table.tpl.html b/ui/src/app/event/event-table.tpl.html
index 82a921f..760b55b 100644
--- a/ui/src/app/event/event-table.tpl.html
+++ b/ui/src/app/event/event-table.tpl.html
@@ -19,7 +19,7 @@
     <section layout="row">
         <md-input-container class="md-block" style="width: 200px;">
             <label translate>event.event-type</label>
-            <md-select ng-model="eventType" ng-disabled="loading()">
+            <md-select ng-model="eventType" ng-disabled="$root.loading">
                 <md-option ng-repeat="type in eventTypes" ng-value="type.value">
                     {{type.name | translate}}
                 </md-option>
@@ -30,8 +30,8 @@
     <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()"
-                            ng-show="loading()"></md-progress-linear>
+        <md-progress-linear style="max-height: 0px;" md-mode="indeterminate" ng-disabled="!$root.loading"
+                            ng-show="$root.loading"></md-progress-linear>
         <md-divider></md-divider>
         <span translate layout-align="center center"
               style="margin-top: 25px;"
diff --git a/ui/src/app/extension/extension-dialog.tpl.html b/ui/src/app/extension/extension-dialog.tpl.html
index 73c33d8..50520d1 100644
--- a/ui/src/app/extension/extension-dialog.tpl.html
+++ b/ui/src/app/extension/extension-dialog.tpl.html
@@ -27,14 +27,14 @@
             </div>
         </md-toolbar>
 
-        <md-progress-linear class="md-warn" md-mode="indeterminate" ng-disabled="!loading" ng-show="loading"></md-progress-linear>
+        <md-progress-linear class="md-warn" md-mode="indeterminate" ng-disabled="!$root.loading" ng-show="$root.loading"></md-progress-linear>
 
-        <span style="min-height: 5px;" flex="" ng-show="!loading"></span>
+        <span style="min-height: 5px;" flex="" ng-show="!$root.loading"></span>
 
         <md-dialog-content>
             <div class="md-dialog-content">
                 <md-content class="md-padding" layout="column">
-                    <fieldset ng-disabled="loading">
+                    <fieldset ng-disabled="$root.loading">
                         <section flex layout="row">
                             <md-input-container flex="60" class="md-block" md-is-error="theForm.extensionId.$touched && theForm.extensionId.$invalid">
                                 <label translate>extension.extension-id</label>
@@ -74,7 +74,7 @@
                 {{ (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 ng-disabled="$root.loading" ng-click="vm.cancel()" style="margin-right:20px;">{{ 'action.cancel' | translate }}
             </md-button>
         </md-dialog-actions>
     </form>
diff --git a/ui/src/app/import-export/import-dialog.tpl.html b/ui/src/app/import-export/import-dialog.tpl.html
index c15fc23..8e0b32f 100644
--- a/ui/src/app/import-export/import-dialog.tpl.html
+++ b/ui/src/app/import-export/import-dialog.tpl.html
@@ -26,11 +26,11 @@
                 </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-progress-linear class="md-warn" md-mode="indeterminate" ng-disabled="!$root.loading" ng-show="$root.loading"></md-progress-linear>
+        <span style="min-height: 5px;" flex="" ng-show="!$root.loading"></span>
         <md-dialog-content>
             <div class="md-dialog-content">
-                <fieldset ng-disabled="loading">
+                <fieldset ng-disabled="$root.loading">
                     <div layout="column" layout-padding>
                         <div class="tb-container">
                             <label class="tb-label" translate>{{ vm.importFileLabel }}</label>
@@ -63,10 +63,10 @@
         </md-dialog-content>
         <md-dialog-actions layout="row">
             <span flex></span>
-            <md-button ng-disabled="loading || !theForm.$dirty || !theForm.$valid || !vm.importData" type="submit" class="md-raised md-primary">
+            <md-button ng-disabled="$root.loading || !theForm.$dirty || !theForm.$valid || !vm.importData" type="submit" class="md-raised md-primary">
                 {{ 'action.import' | translate }}
             </md-button>
-            <md-button ng-disabled="loading" ng-click="vm.cancel()" style="margin-right:20px;">{{ 'action.cancel' | translate }}</md-button>
+            <md-button ng-disabled="$root.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/layout/home.tpl.html b/ui/src/app/layout/home.tpl.html
index a35c370..b9d2c0a 100644
--- a/ui/src/app/layout/home.tpl.html
+++ b/ui/src/app/layout/home.tpl.html
@@ -76,7 +76,7 @@
 			  </tb-user-menu>
       	</div>
     </md-toolbar>
-   	<md-progress-linear class="md-warn" style="z-index: 10; max-height: 0px; width: 100%;" md-mode="indeterminate" ng-disabled="!loading" ng-show="loading"></md-progress-linear>
+   	<md-progress-linear class="md-warn" style="z-index: 10; max-height: 0px; width: 100%;" md-mode="indeterminate" ng-disabled="!$root.loading" ng-show="$root.loading"></md-progress-linear>
     
     <div flex layout="column" id="toast-parent" style="position: relative;">
     	<md-content ng-cloak flex layout="column" class="page-content" ui-view name="content"></md-content>
diff --git a/ui/src/app/locale/locale.constant.js b/ui/src/app/locale/locale.constant.js
index ab672de..7769b3d 100644
--- a/ui/src/app/locale/locale.constant.js
+++ b/ui/src/app/locale/locale.constant.js
@@ -1213,6 +1213,7 @@ export default angular.module('thingsboard.locale', [])
                     "remove-widget-title": "Are you sure you want to remove the widget '{{widgetTitle}}'?",
                     "remove-widget-text": "After the confirmation the widget and all related data will become unrecoverable.",
                     "timeseries": "Time series",
+                    "search-data": "Search data",
                     "latest-values": "Latest values",
                     "rpc": "Control widget",
                     "alarm": "Alarm widget",
diff --git a/ui/src/app/login/create-password.tpl.html b/ui/src/app/login/create-password.tpl.html
index 16ed81c..96dd8e6 100644
--- a/ui/src/app/login/create-password.tpl.html
+++ b/ui/src/app/login/create-password.tpl.html
@@ -23,7 +23,7 @@
             </md-card-title-text>
         </md-card-title>
         <md-progress-linear class="md-warn" style="z-index: 1; max-height: 5px; width: inherit; position: absolute"
-                            md-mode="indeterminate" ng-disabled="!loading" ng-show="loading"></md-progress-linear>
+                            md-mode="indeterminate" ng-disabled="!$root.loading" ng-show="$root.loading"></md-progress-linear>
         <md-card-content>
             <form class="create-password-form" ng-submit="vm.createPassword()">
                 <div layout="column" layout-padding="" id="toast-parent">
diff --git a/ui/src/app/login/login.tpl.html b/ui/src/app/login/login.tpl.html
index afe2a1c..05f9c89 100644
--- a/ui/src/app/login/login.tpl.html
+++ b/ui/src/app/login/login.tpl.html
@@ -23,7 +23,7 @@
             </md-card-title-text>
         </md-card-title>
         <md-progress-linear class="md-warn" style="z-index: 1; max-height: 5px; width: inherit; position: absolute"
-                            md-mode="indeterminate" ng-disabled="!loading" ng-show="loading"></md-progress-linear>
+                            md-mode="indeterminate" ng-disabled="!$root.loading" ng-show="$root.loading"></md-progress-linear>
         <md-card-content>
             <form class="login-form" ng-submit="vm.login()">
                 <div layout="column" layout-padding="" id="toast-parent">
diff --git a/ui/src/app/login/reset-password.tpl.html b/ui/src/app/login/reset-password.tpl.html
index 43b5803..52788dd 100644
--- a/ui/src/app/login/reset-password.tpl.html
+++ b/ui/src/app/login/reset-password.tpl.html
@@ -23,7 +23,7 @@
             </md-card-title-text>
         </md-card-title>
         <md-progress-linear class="md-warn" style="z-index: 1; max-height: 5px; width: inherit; position: absolute"
-                            md-mode="indeterminate" ng-disabled="!loading" ng-show="loading"></md-progress-linear>
+                            md-mode="indeterminate" ng-disabled="!$root.loading" ng-show="$root.loading"></md-progress-linear>
         <md-card-content>
             <form class="password-reset-form" ng-submit="vm.resetPassword()">
                 <div layout="column" layout-padding="" id="toast-parent">
diff --git a/ui/src/app/login/reset-password-request.tpl.html b/ui/src/app/login/reset-password-request.tpl.html
index a478927..51afe88 100644
--- a/ui/src/app/login/reset-password-request.tpl.html
+++ b/ui/src/app/login/reset-password-request.tpl.html
@@ -23,7 +23,7 @@
             </md-card-title-text>
         </md-card-title>
         <md-progress-linear class="md-warn" style="z-index: 1; max-height: 5px; width: inherit; position: absolute"
-                            md-mode="indeterminate" ng-disabled="!loading" ng-show="loading"></md-progress-linear>
+                            md-mode="indeterminate" ng-disabled="!$root.loading" ng-show="$root.loading"></md-progress-linear>
         <md-card-content>
             <form class="request-password-reset-form" ng-submit="vm.sendResetPasswordLink()">
                 <div layout="column" layout-padding="" id="toast-parent">
diff --git a/ui/src/app/plugin/add-plugin.tpl.html b/ui/src/app/plugin/add-plugin.tpl.html
index 8d99408..4060a62 100644
--- a/ui/src/app/plugin/add-plugin.tpl.html
+++ b/ui/src/app/plugin/add-plugin.tpl.html
@@ -27,8 +27,8 @@
                 </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-progress-linear class="md-warn" md-mode="indeterminate" ng-disabled="!$root.loading" ng-show="$root.loading"></md-progress-linear>
+        <span style="min-height: 5px;" flex="" ng-show="!$root.loading"></span>
         <md-dialog-content>
             <div class="md-dialog-content">
                 <tb-plugin plugin="vm.item" is-edit="true" the-form="theForm"></tb-plugin>
@@ -36,11 +36,11 @@
         </md-dialog-content>
         <md-dialog-actions layout="row">
             <span flex></span>
-            <md-button ng-disabled="loading || theForm.$invalid || !theForm.$dirty" type="submit"
+            <md-button ng-disabled="$root.loading || theForm.$invalid || !theForm.$dirty" type="submit"
                        class="md-raised md-primary">
                 {{ 'action.add' | translate }}
             </md-button>
-            <md-button ng-disabled="loading" ng-click="vm.cancel()" style="margin-right:20px;">{{ 'action.cancel' |
+            <md-button ng-disabled="$root.loading" ng-click="vm.cancel()" style="margin-right:20px;">{{ 'action.cancel' |
                 translate }}
             </md-button>
         </md-dialog-actions>
diff --git a/ui/src/app/plugin/plugin-fieldset.tpl.html b/ui/src/app/plugin/plugin-fieldset.tpl.html
index b5f8742..987040c 100644
--- a/ui/src/app/plugin/plugin-fieldset.tpl.html
+++ b/ui/src/app/plugin/plugin-fieldset.tpl.html
@@ -39,7 +39,7 @@
 </div>
 
 <md-content class="md-padding" layout="column" style="overflow-x: hidden">
-    <fieldset ng-disabled="loading || !isEdit || isReadOnly">
+    <fieldset ng-disabled="$root.loading || !isEdit || isReadOnly">
         <md-input-container class="md-block">
             <label translate>plugin.name</label>
             <input required name="name" ng-model="plugin.name">
@@ -61,7 +61,7 @@
             </md-input-container>
             <md-input-container flex class="md-block">
                 <label translate>plugin.type</label>
-                <md-select required name="pluginType" ng-model="plugin.clazz" ng-disabled="loading || !isEdit">
+                <md-select required name="pluginType" ng-model="plugin.clazz" ng-disabled="$root.loading || !isEdit">
                     <md-option ng-repeat="component in pluginComponents" ng-value="component.clazz">
                         {{component.name}}
                     </md-option>
@@ -74,14 +74,14 @@
         <md-card flex class="plugin-config" ng-if="showPluginConfig">
             <md-card-title>
                 <md-card-title-text>
-                    <span translate class="md-headline" ng-class="{'tb-readonly-label' : (loading || !isEdit || isReadOnly)}">plugin.configuration</span>
+                    <span translate class="md-headline" ng-class="{'tb-readonly-label' : ($root.loading || !isEdit || isReadOnly)}">plugin.configuration</span>
                 </md-card-title-text>
             </md-card-title>
             <md-card-content>
                 <tb-json-form schema="pluginComponent.configurationDescriptor.schema"
                               form="pluginComponent.configurationDescriptor.form"
                               model="pluginConfiguration.data"
-                              readonly="loading || !isEdit || isReadOnly"
+                              readonly="$root.loading || !isEdit || isReadOnly"
                               form-control="theForm">
                 </tb-json-form>
             </md-card-content>
diff --git a/ui/src/app/profile/change-password.tpl.html b/ui/src/app/profile/change-password.tpl.html
index 9057e8c..dd1c577 100644
--- a/ui/src/app/profile/change-password.tpl.html
+++ b/ui/src/app/profile/change-password.tpl.html
@@ -26,8 +26,8 @@
 	        </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-progress-linear class="md-warn" md-mode="indeterminate" ng-disabled="!$root.loading" ng-show="$root.loading"></md-progress-linear>
+  	    <span style="min-height: 5px;" flex="" ng-show="!$root.loading"></span>
 	    <md-dialog-content>
 	      <div class="md-dialog-content">
       		<md-input-container class="md-block">
@@ -55,10 +55,10 @@
 	    </md-dialog-content>
 	    <md-dialog-actions layout="row">
 	      <span flex></span>
-		  <md-button ng-disabled="loading || theForm.$invalid" type="submit" class="md-raised md-primary">
+		  <md-button ng-disabled="$root.loading || theForm.$invalid" type="submit" class="md-raised md-primary">
 		  		{{ 'profile.change-password' | translate }}
 		  </md-button>
-	      <md-button ng-disabled="loading" ng-click="vm.cancel()" style="margin-right:20px;">{{ 'action.cancel' | translate }}</md-button>
+	      <md-button ng-disabled="$root.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/profile/profile.tpl.html b/ui/src/app/profile/profile.tpl.html
index 39401f7..3849f9b 100644
--- a/ui/src/app/profile/profile.tpl.html
+++ b/ui/src/app/profile/profile.tpl.html
@@ -23,11 +23,11 @@
                 <span style='opacity: 0.7;'>{{ vm.profileUser.email }}</span>
             </md-card-title-text>
         </md-card-title>
-        <md-progress-linear md-mode="indeterminate" ng-disabled="!loading" ng-show="loading"></md-progress-linear>
-        <span style="min-height: 5px;" flex="" ng-show="!loading"></span>
+        <md-progress-linear md-mode="indeterminate" ng-disabled="!$root.loading" ng-show="$root.loading"></md-progress-linear>
+        <span style="min-height: 5px;" flex="" ng-show="!$root.loading"></span>
         <md-card-content>
             <form name="theForm" ng-submit="vm.save()" tb-confirm-on-exit confirm-form="theForm">
-                <fieldset ng-disabled="loading">
+                <fieldset ng-disabled="$root.loading">
                     <md-input-container class="md-block">
                         <label translate>user.email</label>
                         <input name="email" type="email" ng-model="vm.profileUser.email">
@@ -48,11 +48,11 @@
                             </md-option>
                         </md-select>
                     </md-input-container>
-                    <md-button ng-disabled="loading" ng-click="vm.changePassword($event)"
+                    <md-button ng-disabled="$root.loading" ng-click="vm.changePassword($event)"
                                class="md-raised md-primary">{{ 'profile.change-password' | translate }}
                     </md-button>
                     <div layout="row" layout-align="end center" width="100%" layout-wrap>
-                        <md-button ng-disabled="loading || theForm.$invalid || !theForm.$dirty" type="submit"
+                        <md-button ng-disabled="$root.loading || theForm.$invalid || !theForm.$dirty" type="submit"
                                    class="md-raised md-primary">{{ 'action.save' | translate }}
                         </md-button>
                     </div>
diff --git a/ui/src/app/rule/add-rule.tpl.html b/ui/src/app/rule/add-rule.tpl.html
index f0d4cca..cfb1f62 100644
--- a/ui/src/app/rule/add-rule.tpl.html
+++ b/ui/src/app/rule/add-rule.tpl.html
@@ -27,8 +27,8 @@
                 </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-progress-linear class="md-warn" md-mode="indeterminate" ng-disabled="!$root.loading" ng-show="$root.loading"></md-progress-linear>
+        <span style="min-height: 5px;" flex="" ng-show="!$root.loading"></span>
         <md-dialog-content>
             <div class="md-dialog-content">
                 <tb-rule rule="vm.item" is-edit="true" the-form="theForm"></tb-rule>
@@ -36,11 +36,11 @@
         </md-dialog-content>
         <md-dialog-actions layout="row">
             <span flex></span>
-            <md-button ng-disabled="loading || theForm.$invalid || !theForm.$dirty" type="submit"
+            <md-button ng-disabled="$root.loading || theForm.$invalid || !theForm.$dirty" type="submit"
                        class="md-raised md-primary">
                 {{ 'action.add' | translate }}
             </md-button>
-            <md-button ng-disabled="loading" ng-click="vm.cancel()" style="margin-right:20px;">{{ 'action.cancel' |
+            <md-button ng-disabled="$root.loading" ng-click="vm.cancel()" style="margin-right:20px;">{{ 'action.cancel' |
                 translate }}
             </md-button>
         </md-dialog-actions>
diff --git a/ui/src/app/rule/rule-fieldset.tpl.html b/ui/src/app/rule/rule-fieldset.tpl.html
index 835b783..ca63203 100644
--- a/ui/src/app/rule/rule-fieldset.tpl.html
+++ b/ui/src/app/rule/rule-fieldset.tpl.html
@@ -39,7 +39,7 @@
 </div>
 
 <md-content class="md-padding tb-rule" layout="column">
-    <fieldset ng-disabled="loading || !isEdit || isReadOnly">
+    <fieldset ng-disabled="$root.loading || !isEdit || isReadOnly">
         <md-input-container class="md-block">
             <label translate>rule.name</label>
             <input required name="name" ng-model="rule.name">
@@ -109,7 +109,7 @@
                         </div>
                     </div>
                     <div ng-if="isEdit && !isReadOnly" flex layout="row" layout-align="start center">
-                        <md-button ng-disabled="loading" class="md-primary md-raised"
+                        <md-button ng-disabled="$root.loading" class="md-primary md-raised"
                                    ng-click="addFilter($event)" aria-label="{{ 'action.add' | translate }}">
                             <md-tooltip md-direction="top">
                                 {{ 'rule.add-filter' | translate }}
@@ -149,7 +149,7 @@
                                     <span ng-if="!isEdit || isReadOnly" translate layout-align="center center"
                                           class="tb-prompt">rule.no-processor-configured</span>
                         <div ng-if="isEdit && !isReadOnly" flex layout="row" layout-align="start center">
-                            <md-button ng-disabled="loading" class="md-primary md-raised"
+                            <md-button ng-disabled="$root.loading" class="md-primary md-raised"
                                        ng-click="addProcessor($event)" aria-label="{{ 'action.create' | translate }}">
                                 <md-tooltip md-direction="top">
                                     {{ 'rule.create-processor' | translate }}
@@ -162,7 +162,7 @@
                 </v-pane-content>
             </v-pane>
         </v-accordion>
-        <fieldset ng-disabled="loading || !isEdit || isReadOnly">
+        <fieldset ng-disabled="$root.loading || !isEdit || isReadOnly">
             <md-input-container ng-if="!isEdit || isReadOnly" flex class="md-block">
                 <label translate>plugin.plugin</label>
                 <input name="name" ng-model="plugin.name">
@@ -203,7 +203,7 @@
                         <span translate layout-align="center center"
                               class="tb-prompt">rule.create-action-prompt</span>
                         <div ng-if="isEdit && !isReadOnly" flex layout="row" layout-align="start center">
-                            <md-button ng-disabled="loading" class="md-primary md-raised"
+                            <md-button ng-disabled="$root.loading" class="md-primary md-raised"
                                        ng-click="addAction($event)" aria-label="{{ 'action.create' | translate }}">
                                 <md-tooltip md-direction="top">
                                     {{ 'rule.create-action' | translate }}
diff --git a/ui/src/app/tenant/add-tenant.tpl.html b/ui/src/app/tenant/add-tenant.tpl.html
index d589e04..d7ee48e 100644
--- a/ui/src/app/tenant/add-tenant.tpl.html
+++ b/ui/src/app/tenant/add-tenant.tpl.html
@@ -27,8 +27,8 @@
                 </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-progress-linear class="md-warn" md-mode="indeterminate" ng-disabled="!$root.loading" ng-show="$root.loading"></md-progress-linear>
+        <span style="min-height: 5px;" flex="" ng-show="!$root.loading"></span>
         <md-dialog-content>
             <div class="md-dialog-content">
                 <tb-tenant tenant="vm.item" is-edit="true" the-form="theForm"></tb-tenant>
@@ -36,11 +36,11 @@
         </md-dialog-content>
         <md-dialog-actions layout="row">
             <span flex></span>
-            <md-button ng-disabled="loading || theForm.$invalid || !theForm.$dirty" type="submit"
+            <md-button ng-disabled="$root.loading || theForm.$invalid || !theForm.$dirty" type="submit"
                        class="md-raised md-primary">
                 {{ 'action.add' | translate }}
             </md-button>
-            <md-button ng-disabled="loading" ng-click="vm.cancel()" style="margin-right:20px;">{{ 'action.cancel' |
+            <md-button ng-disabled="$root.loading" ng-click="vm.cancel()" style="margin-right:20px;">{{ 'action.cancel' |
                 translate }}
             </md-button>
         </md-dialog-actions>
diff --git a/ui/src/app/tenant/tenant-fieldset.tpl.html b/ui/src/app/tenant/tenant-fieldset.tpl.html
index 76b0c3f..41e371b 100644
--- a/ui/src/app/tenant/tenant-fieldset.tpl.html
+++ b/ui/src/app/tenant/tenant-fieldset.tpl.html
@@ -29,7 +29,7 @@
 </div>
 
 <md-content class="md-padding" layout="column">
-	<fieldset ng-disabled="loading || !isEdit">
+	<fieldset ng-disabled="$root.loading || !isEdit">
 		<md-input-container class="md-block">
 			<label translate>tenant.title</label>
 			<input required name="title" ng-model="tenant.title">	
diff --git a/ui/src/app/user/add-user.tpl.html b/ui/src/app/user/add-user.tpl.html
index 4883394..a34f994 100644
--- a/ui/src/app/user/add-user.tpl.html
+++ b/ui/src/app/user/add-user.tpl.html
@@ -27,8 +27,8 @@
 	        </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-progress-linear class="md-warn" md-mode="indeterminate" ng-disabled="!$root.loading" ng-show="$root.loading"></md-progress-linear>
+  	    <span style="min-height: 5px;" flex="" ng-show="!$root.loading"></span>
 	    <md-dialog-content>
 	      <div class="md-dialog-content">
   	        	<tb-user user="vm.item" is-edit="true" the-form="theForm"></tb-user>
@@ -45,10 +45,10 @@
 	    </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">
+		  <md-button ng-disabled="$root.loading || theForm.$invalid || !theForm.$dirty" type="submit" class="md-raised md-primary">
 		  		{{ 'action.add' | translate }}
 		  </md-button>
-	      <md-button ng-disabled="loading" ng-click="vm.cancel()" style="margin-right:20px;">{{ 'action.cancel' | translate }}</md-button>
+	      <md-button ng-disabled="$root.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/user/user-fieldset.tpl.html b/ui/src/app/user/user-fieldset.tpl.html
index d03b462..b804813 100644
--- a/ui/src/app/user/user-fieldset.tpl.html
+++ b/ui/src/app/user/user-fieldset.tpl.html
@@ -26,7 +26,7 @@
 </md-button>
 
 <md-content class="md-padding" layout="column">
-    <fieldset ng-disabled="loading || !isEdit">
+    <fieldset ng-disabled="$root.loading || !isEdit">
         <md-input-container class="md-block">
             <label translate>user.email</label>
             <input required name="email"
@@ -50,11 +50,11 @@
             <textarea ng-model="user.additionalInfo.description" rows="2"></textarea>
         </md-input-container>
         <section class="tb-default-dashboard" flex layout="column" ng-if="user.id">
-            <span class="tb-default-dashboard-label" ng-class="{'tb-disabled-label': loading || !isEdit}" translate>user.default-dashboard</span>
+            <span class="tb-default-dashboard-label" ng-class="{'tb-disabled-label': $root.loading || !isEdit}" translate>user.default-dashboard</span>
             <section flex layout="column" layout-gt-sm="row">
                 <tb-dashboard-autocomplete ng-if="isTenantAdmin()"
                                            flex
-                                           ng-disabled="loading || !isEdit"
+                                           ng-disabled="$root.loading || !isEdit"
                                            the-form="theForm"
                                            ng-model="user.additionalInfo.defaultDashboardId"
                                            tenant-id="user.tenantId.id"
@@ -62,14 +62,14 @@
                 </tb-dashboard-autocomplete>
                 <tb-dashboard-autocomplete ng-if="isCustomerUser()"
                                      flex
-                                     ng-disabled="loading || !isEdit"
+                                     ng-disabled="$root.loading || !isEdit"
                                      the-form="theForm"
                                      ng-model="user.additionalInfo.defaultDashboardId"
                                      dashboards-scope="customer"
                                      customer-id="user.customerId.id"
                                      select-first-dashboard="false">
                 </tb-dashboard-autocomplete>
-                <md-checkbox ng-disabled="loading || !isEdit" flex aria-label="{{ 'user.always-fullscreen' | translate }}"
+                <md-checkbox ng-disabled="$root.loading || !isEdit" flex aria-label="{{ 'user.always-fullscreen' | translate }}"
                              ng-model="user.additionalInfo.defaultDashboardFullscreen">{{ 'user.always-fullscreen' | translate }}
                 </md-checkbox>
             </section>
diff --git a/ui/src/app/widget/add-widgets-bundle.tpl.html b/ui/src/app/widget/add-widgets-bundle.tpl.html
index 5544ddd..fcd4582 100644
--- a/ui/src/app/widget/add-widgets-bundle.tpl.html
+++ b/ui/src/app/widget/add-widgets-bundle.tpl.html
@@ -27,8 +27,8 @@
                 </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-progress-linear class="md-warn" md-mode="indeterminate" ng-disabled="!$root.loading" ng-show="$root.loading"></md-progress-linear>
+        <span style="min-height: 5px;" flex="" ng-show="!$root.loading"></span>
         <md-dialog-content>
             <div class="md-dialog-content">
                 <tb-widgets-bundle widgets-bundle="vm.item" is-edit="true" the-form="theForm"></tb-widgets-bundle>
@@ -36,11 +36,11 @@
         </md-dialog-content>
         <md-dialog-actions layout="row">
             <span flex></span>
-            <md-button ng-disabled="loading || theForm.$invalid || !theForm.$dirty" type="submit"
+            <md-button ng-disabled="$root.loading || theForm.$invalid || !theForm.$dirty" type="submit"
                        class="md-raised md-primary">
                 {{ 'action.add' | translate }}
             </md-button>
-            <md-button ng-disabled="loading" ng-click="vm.cancel()" style="margin-right:20px;">{{ 'action.cancel' |
+            <md-button ng-disabled="$root.loading" ng-click="vm.cancel()" style="margin-right:20px;">{{ 'action.cancel' |
                 translate }}
             </md-button>
         </md-dialog-actions>
diff --git a/ui/src/app/widget/lib/alarms-table-widget.js b/ui/src/app/widget/lib/alarms-table-widget.js
index 9e96d4d..00e4997 100644
--- a/ui/src/app/widget/lib/alarms-table-widget.js
+++ b/ui/src/app/widget/lib/alarms-table-widget.js
@@ -124,7 +124,7 @@ function AlarmsTableWidgetController($element, $scope, $filter, $mdMedia, $mdDia
     $scope.$on('alarms-table-data-updated', function(event, tableId) {
         if (vm.tableId == tableId) {
             if (vm.subscription) {
-                vm.allAlarms = vm.subscription.alarms;
+                vm.allAlarms = vm.subscription.alarms || [];
                 updateAlarms(true);
                 $scope.$digest();
             }
@@ -298,7 +298,7 @@ function AlarmsTableWidgetController($element, $scope, $filter, $mdMedia, $mdDia
                 entityId = vm.currentAlarm.originator;
                 entityName = vm.currentAlarm.originatorName;
             }
-            vm.ctx.actionsApi.handleWidgetAction($event, descriptors[0], entityId, entityName);
+            vm.ctx.actionsApi.handleWidgetAction($event, descriptors[0], entityId, entityName, { alarm: alarm });
         }
     }
 
@@ -312,7 +312,7 @@ function AlarmsTableWidgetController($element, $scope, $filter, $mdMedia, $mdDia
             entityId = alarm.originator;
             entityName = alarm.originatorName;
         }
-        vm.ctx.actionsApi.handleWidgetAction($event, actionDescriptor, entityId, entityName);
+        vm.ctx.actionsApi.handleWidgetAction($event, actionDescriptor, entityId, entityName, { alarm: alarm });
     }
 
     function isCurrent(alarm) {
diff --git a/ui/src/app/widget/lib/alarms-table-widget.tpl.html b/ui/src/app/widget/lib/alarms-table-widget.tpl.html
index 860128d..34c7c1b 100644
--- a/ui/src/app/widget/lib/alarms-table-widget.tpl.html
+++ b/ui/src/app/widget/lib/alarms-table-widget.tpl.html
@@ -77,7 +77,7 @@
                     </td>
                     <td md-cell ng-if="vm.displayDetails" class="tb-action-cell">
                         <md-button class="md-icon-button" aria-label="{{ 'alarm.details' | translate }}"
-                                   ng-click="vm.openAlarmDetails($event, alarm)">
+                                   ng-click="vm.openAlarmDetails($event, alarm)" ng-disabled="$root.loading">
                             <md-icon aria-label="{{ 'alarm.details' | translate }}" class="material-icons">more_horiz</md-icon>
                             <md-tooltip md-direction="top">
                                 {{ 'alarm.details' | translate }}
@@ -90,7 +90,7 @@
                                    width: vm.actionCellDescriptors.length*36+'px'}">
                         <md-button class="md-icon-button" ng-repeat="actionDescriptor in vm.actionCellDescriptors"
                                    aria-label="{{ actionDescriptor.displayName }}"
-                                   ng-click="vm.onActionButtonClick($event, alarm, actionDescriptor)">
+                                   ng-click="vm.onActionButtonClick($event, alarm, actionDescriptor)" ng-disabled="$root.loading">
                             <md-icon aria-label="{{ actionDescriptor.displayName }}" class="material-icons">{{actionDescriptor.icon}}</md-icon>
                             <md-tooltip md-direction="top">
                                 {{ actionDescriptor.displayName }}
diff --git a/ui/src/app/widget/lib/CanvasDigitalGauge.js b/ui/src/app/widget/lib/CanvasDigitalGauge.js
index bd3ff98..200430c 100644
--- a/ui/src/app/widget/lib/CanvasDigitalGauge.js
+++ b/ui/src/app/widget/lib/CanvasDigitalGauge.js
@@ -455,7 +455,7 @@ function barDimensions(context, options, x, y, w, h) {
         if (options.hideMinMax && options.label === '') {
             bd.labelY = bd.barBottom;
             bd.barLeft = bd.origBaseX + options.fontMinMaxSize/3 * bd.fontSizeFactor;
-            bd.barRight = bd.bd.origBaseX + w + /*bd.width*/ - options.fontMinMaxSize/3 * bd.fontSizeFactor;
+            bd.barRight = bd.origBaseX + w + /*bd.width*/ - options.fontMinMaxSize/3 * bd.fontSizeFactor;
         } else {
             context.font = canvasGauges.drawings.font(options, 'MinMax', bd.fontSizeFactor);
             var minTextWidth  = context.measureText(options.minValue+'').width;
diff --git a/ui/src/app/widget/lib/entities-table-widget.tpl.html b/ui/src/app/widget/lib/entities-table-widget.tpl.html
index 2587da1..7ef8c54 100644
--- a/ui/src/app/widget/lib/entities-table-widget.tpl.html
+++ b/ui/src/app/widget/lib/entities-table-widget.tpl.html
@@ -63,7 +63,7 @@
                                    width: vm.actionCellDescriptors.length*36+'px'}">
                         <md-button class="md-icon-button" ng-repeat="actionDescriptor in vm.actionCellDescriptors"
                                    aria-label="{{ actionDescriptor.displayName }}"
-                                   ng-click="vm.onActionButtonClick($event, entity, actionDescriptor)">
+                                   ng-click="vm.onActionButtonClick($event, entity, actionDescriptor)" ng-disabled="$root.loading">
                             <md-icon aria-label="{{ actionDescriptor.displayName }}" class="material-icons">{{actionDescriptor.icon}}</md-icon>
                             <md-tooltip md-direction="top">
                                 {{ actionDescriptor.displayName }}
diff --git a/ui/src/app/widget/lib/google-map.js b/ui/src/app/widget/lib/google-map.js
index e2b4dc6..af11ac3 100644
--- a/ui/src/app/widget/lib/google-map.js
+++ b/ui/src/app/widget/lib/google-map.js
@@ -13,16 +13,16 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-
 var gmGlobals = {
     loadingGmId: null,
     gmApiKeys: {}
 }
 
 export default class TbGoogleMap {
-    constructor($containerElement, initCallback, defaultZoomLevel, dontFitMapBounds, minZoomLevel, gmApiKey, gmDefaultMapType) {
+    constructor($containerElement, utils, initCallback, defaultZoomLevel, dontFitMapBounds, minZoomLevel, gmApiKey, gmDefaultMapType) {
 
         var tbMap = this;
+        this.utils = utils;
         this.defaultZoomLevel = defaultZoomLevel;
         this.dontFitMapBounds = dontFitMapBounds;
         this.minZoomLevel = minZoomLevel;
@@ -151,80 +151,97 @@ export default class TbGoogleMap {
 
     /* eslint-disable no-undef */
     updateMarkerColor(marker, color) {
-        var pinColor = color.substr(1);
-        var pinImage = new google.maps.MarkerImage("https://chart.apis.google.com/chart?chst=d_map_pin_letter&chld=%E2%80%A2|" + pinColor,
-            new google.maps.Size(21, 34),
-            new google.maps.Point(0,0),
-            new google.maps.Point(10, 34));
-        marker.setIcon(pinImage);
+        this.createDefaultMarkerIcon(marker, color, (iconInfo) => {
+            marker.setIcon(iconInfo.icon);
+        });
     }
     /* eslint-enable no-undef */
 
     /* eslint-disable no-undef */
-    updateMarkerImage(marker, settings, image, maxSize) {
-        var testImage = document.createElement('img'); // eslint-disable-line
-        testImage.style.visibility = 'hidden';
-        testImage.onload = function() {
-            var width;
-            var height;
-            var aspect = testImage.width / testImage.height;
-            document.body.removeChild(testImage); //eslint-disable-line
-            if (aspect > 1) {
-                width = maxSize;
-                height = maxSize / aspect;
-            } else {
-                width = maxSize * aspect;
-                height = maxSize;
-            }
-            var pinImage = {
-                url: image,
-                scaledSize : new google.maps.Size(width, height)
-            }
-            marker.setIcon(pinImage);
+    updateMarkerIcon(marker, settings) {
+        this.createMarkerIcon(marker, settings, (iconInfo) => {
+            marker.setIcon(iconInfo.icon);
             if (settings.showLabel) {
-                marker.set('labelAnchor', new google.maps.Point(100, height + 20));
+                marker.set('labelAnchor', new google.maps.Point(100, iconInfo.size[1] + 20));
             }
+        });
+    }
+    /* eslint-disable no-undef */
+
+    /* eslint-disable no-undef */
+    createMarkerIcon(marker, settings, onMarkerIconReady) {
+        var currentImage = settings.currentImage;
+        var gMap = this;
+        if (currentImage && currentImage.url) {
+            this.utils.loadImageAspect(currentImage.url).then(
+                (aspect) => {
+                    if (aspect) {
+                        var width;
+                        var height;
+                        if (aspect > 1) {
+                            width = currentImage.size;
+                            height = currentImage.size / aspect;
+                        } else {
+                            width = currentImage.size * aspect;
+                            height = currentImage.size;
+                        }
+                        var icon = {
+                            url: currentImage.url,
+                            scaledSize : new google.maps.Size(width, height)
+                        };
+                        var iconInfo = {
+                            size: [width, height],
+                            icon: icon
+                        };
+                        onMarkerIconReady(iconInfo);
+                    } else {
+                        gMap.createDefaultMarkerIcon(marker, settings.color, onMarkerIconReady);
+                    }
+                }
+            );
+        } else {
+            this.createDefaultMarkerIcon(marker, settings.color, onMarkerIconReady);
         }
-        document.body.appendChild(testImage); //eslint-disable-line
-        testImage.src = image;
     }
     /* eslint-enable no-undef */
 
     /* eslint-disable no-undef */
-    createMarker(location, settings, onClickListener, markerArgs) {
-        var height = 34;
-        var pinColor = settings.color.substr(1);
-        var pinImage = new google.maps.MarkerImage("https://chart.apis.google.com/chart?chst=d_map_pin_letter&chld=%E2%80%A2|" + pinColor,
-            new google.maps.Size(21, 34),
-            new google.maps.Point(0,0),
-            new google.maps.Point(10, 34));
-        var pinShadow = new google.maps.MarkerImage("https://chart.apis.google.com/chart?chst=d_map_pin_shadow",
+    createDefaultMarkerIcon(marker, color, onMarkerIconReady) {
+        var pinColor = color.substr(1);
+        var icon = new google.maps.MarkerImage("https://chart.apis.google.com/chart?chst=d_map_pin_letter_withshadow&chld=%E2%80%A2|" + pinColor,
             new google.maps.Size(40, 37),
-            new google.maps.Point(0, 0),
-            new google.maps.Point(12, 35));
+            new google.maps.Point(0,0),
+            new google.maps.Point(10, 37));
+        var iconInfo = {
+            size: [40, 37],
+            icon: icon
+        };
+        onMarkerIconReady(iconInfo);
+    }
+    /* eslint-enable no-undef */
+
+    /* eslint-disable no-undef */
+    createMarker(location, settings, onClickListener, markerArgs) {
         var marker;
         if (settings.showLabel) {
             marker = new MarkerWithLabel({
                 position: location,
-                map: this.map,
-                icon: pinImage,
-                shadow: pinShadow,
                 labelContent: '<div style="color: '+ settings.labelColor +';"><b>'+settings.labelText+'</b></div>',
-                labelClass: "tb-labels",
-                labelAnchor: new google.maps.Point(100, height + 20)
+                labelClass: "tb-labels"
             });
         } else {
             marker = new google.maps.Marker({
                 position: location,
-                map: this.map,
-                icon: pinImage,
-                shadow: pinShadow
             });
         }
-
-        if (settings.useMarkerImage) {
-            this.updateMarkerImage(marker, settings, settings.markerImage, settings.markerImageSize || 34);
-        }
+        var gMap = this;
+        this.createMarkerIcon(marker, settings, (iconInfo) => {
+            marker.setIcon(iconInfo.icon);
+            if (settings.showLabel) {
+                marker.set('labelAnchor', new google.maps.Point(100, iconInfo.size[1] + 20));
+            }
+            marker.setMap(gMap.map);
+        });
 
         if (settings.displayTooltip) {
             this.createTooltip(marker, settings.tooltipPattern, settings.tooltipReplaceInfo, settings.autocloseTooltip, markerArgs);
diff --git a/ui/src/app/widget/lib/image-map.js b/ui/src/app/widget/lib/image-map.js
index e9d9976..c130539 100644
--- a/ui/src/app/widget/lib/image-map.js
+++ b/ui/src/app/widget/lib/image-map.js
@@ -13,7 +13,6 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-
 import 'leaflet/dist/leaflet.css';
 import * as L from 'leaflet';
 
@@ -21,9 +20,10 @@ const maxZoom = 4;
 
 export default class TbImageMap {
 
-    constructor(ctx, $containerElement, initCallback, imageUrl, posFunction, imageEntityAlias, imageUrlAttribute) {
+    constructor(ctx, $containerElement, utils, initCallback, imageUrl, posFunction, imageEntityAlias, imageUrlAttribute) {
 
         this.ctx = ctx;
+        this.utils = utils;
         this.tooltips = [];
 
         this.$containerElement = $containerElement;
@@ -117,18 +117,15 @@ export default class TbImageMap {
         }
         this.imageUrl = imageUrl;
         var imageMap = this;
-        var testImage = document.createElement('img'); // eslint-disable-line
-        testImage.style.visibility = 'hidden';
-        testImage.onload = function() {
-            imageMap.aspect = testImage.width / testImage.height;
-            document.body.removeChild(testImage); //eslint-disable-line
-            imageMap.onresize(updateImage);
-            if (initCallback) {
-                setTimeout(initCallback, 0); //eslint-disable-line
+        this.utils.loadImageAspect(imageUrl).then(
+            (aspect) => {
+                imageMap.aspect = aspect;
+                imageMap.onresize(updateImage);
+                if (initCallback) {
+                    setTimeout(initCallback, 0); //eslint-disable-line
+                }
             }
-        }
-        document.body.appendChild(testImage); //eslint-disable-line
-        testImage.src = imageUrl;
+        );
     }
 
     onresize(updateImage) {
@@ -229,83 +226,97 @@ export default class TbImageMap {
     }
 
     updateMarkerColor(marker, color) {
-        var pinColor = color.substr(1);
-        var icon = L.icon({
-            iconUrl: 'https://chart.apis.google.com/chart?chst=d_map_pin_letter&chld=%E2%80%A2|' + pinColor,
-            iconSize: [21, 34],
-            iconAnchor: [10, 34],
-            popupAnchor: [0, -34],
-            shadowUrl: 'https://chart.apis.google.com/chart?chst=d_map_pin_shadow',
-            shadowSize: [40, 37],
-            shadowAnchor: [12, 35]
+        this.createDefaultMarkerIcon(marker, color, (iconInfo) => {
+            marker.setIcon(iconInfo.icon);
         });
-        marker.setIcon(icon);
-    }
-
-    updateMarkerImage(marker, settings, image, maxSize) {
-        var testImage = document.createElement('img'); // eslint-disable-line
-        testImage.style.visibility = 'hidden';
-        testImage.onload = function() {
-            var width;
-            var height;
-            var aspect = testImage.width / testImage.height;
-            document.body.removeChild(testImage);  //eslint-disable-line
-            if (aspect > 1) {
-                width = maxSize;
-                height = maxSize / aspect;
-            } else {
-                width = maxSize * aspect;
-                height = maxSize;
-            }
-            var icon = L.icon({
-                iconUrl: image,
-                iconSize: [width, height],
-                iconAnchor: [marker.offsetX * width, marker.offsetY * height],
-                popupAnchor: [0, -height]
-            });
-            marker.setIcon(icon);
+    }
+
+    updateMarkerIcon(marker, settings) {
+        this.createMarkerIcon(marker, settings, (iconInfo) => {
+            marker.setIcon(iconInfo.icon);
             if (settings.showLabel) {
                 marker.unbindTooltip();
-                marker.tooltipOffset = [0, -height * marker.offsetY + 10];
+                marker.tooltipOffset = [0, -iconInfo.size[1] * marker.offsetY + 10];
                 marker.bindTooltip('<div style="color: '+ settings.labelColor +';"><b>'+settings.labelText+'</b></div>',
                     { className: 'tb-marker-label', permanent: true, direction: 'top', offset: marker.tooltipOffset });
             }
+        });
+    }
+
+    createMarkerIcon(marker, settings, onMarkerIconReady) {
+        var currentImage = settings.currentImage;
+        var opMap = this;
+        if (currentImage && currentImage.url) {
+            this.utils.loadImageAspect(currentImage.url).then(
+                (aspect) => {
+                    if (aspect) {
+                        var width;
+                        var height;
+                        if (aspect > 1) {
+                            width = currentImage.size;
+                            height = currentImage.size / aspect;
+                        } else {
+                            width = currentImage.size * aspect;
+                            height = currentImage.size;
+                        }
+                        var icon = L.icon({
+                            iconUrl: currentImage.url,
+                            iconSize: [width, height],
+                            iconAnchor: [marker.offsetX * width, marker.offsetY * height],
+                            popupAnchor: [0, -height]
+                        });
+                        var iconInfo = {
+                            size: [width, height],
+                            icon: icon
+                        };
+                        onMarkerIconReady(iconInfo);
+                    } else {
+                        opMap.createDefaultMarkerIcon(marker, settings.color, onMarkerIconReady);
+                    }
+                }
+            );
+        } else {
+            this.createDefaultMarkerIcon(marker, settings.color, onMarkerIconReady);
         }
-        document.body.appendChild(testImage); //eslint-disable-line
-        testImage.src = image;
     }
 
-    createMarker(position, settings, onClickListener, markerArgs) {
-        var height = 34;
-        var pinColor = settings.color.substr(1);
+    createDefaultMarkerIcon(marker, color, onMarkerIconReady) {
+        var pinColor = color.substr(1);
         var icon = L.icon({
             iconUrl: 'https://chart.apis.google.com/chart?chst=d_map_pin_letter&chld=%E2%80%A2|' + pinColor,
             iconSize: [21, 34],
-            iconAnchor: [21 * settings.markerOffsetX, 34 * settings.markerOffsetY],
+            iconAnchor: [21 * marker.offsetX, 34 * marker.offsetY],
             popupAnchor: [0, -34],
             shadowUrl: 'https://chart.apis.google.com/chart?chst=d_map_pin_shadow',
             shadowSize: [40, 37],
             shadowAnchor: [12, 35]
         });
+        var iconInfo = {
+            size: [21, 34],
+            icon: icon
+        };
+        onMarkerIconReady(iconInfo);
+    }
 
+    createMarker(position, settings, onClickListener, markerArgs) {
         var pos = this.posFunction(position.x, position.y);
         var x = pos.x * this.width;
         var y = pos.y * this.height;
         var location = this.pointToLatLng(x, y);
-        var marker = L.marker(location, {icon: icon}).addTo(this.map);
+        var marker = L.marker(location, {});//.addTo(this.map);
         marker.position = position;
         marker.offsetX = settings.markerOffsetX;
         marker.offsetY = settings.markerOffsetY;
-
-        if (settings.showLabel) {
-            marker.tooltipOffset = [0, -height * marker.offsetY + 10];
-            marker.bindTooltip('<div style="color: '+ settings.labelColor +';"><b>'+settings.labelText+'</b></div>',
-                { className: 'tb-marker-label', permanent: true, direction: 'top', offset: marker.tooltipOffset });
-        }
-
-        if (settings.useMarkerImage) {
-            this.updateMarkerImage(marker, settings, settings.markerImage, settings.markerImageSize || 34);
-        }
+        var opMap = this;
+        this.createMarkerIcon(marker, settings, (iconInfo) => {
+            marker.setIcon(iconInfo.icon);
+            if (settings.showLabel) {
+                marker.tooltipOffset = [0, -iconInfo.size[1] * marker.offsetY + 10];
+                marker.bindTooltip('<div style="color: '+ settings.labelColor +';"><b>'+settings.labelText+'</b></div>',
+                    { className: 'tb-marker-label', permanent: true, direction: 'top', offset: marker.tooltipOffset });
+            }
+            marker.addTo(opMap.map);
+        });
 
         if (settings.displayTooltip) {
             this.createTooltip(marker, settings.tooltipPattern, settings.tooltipReplaceInfo, settings.autocloseTooltip, markerArgs);
diff --git a/ui/src/app/widget/lib/map-widget.js b/ui/src/app/widget/lib/map-widget.js
index f742514..ef86381 100644
--- a/ui/src/app/widget/lib/map-widget.js
+++ b/ui/src/app/widget/lib/map-widget.js
@@ -13,7 +13,6 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-
 import tinycolor from 'tinycolor2';
 
 import TbGoogleMap from './google-map';
@@ -75,7 +74,7 @@ export default class TbMapWidget {
         if (!$element) {
             $element = ctx.$container;
         }
-
+        this.utils = ctx.$scope.$injector.get('utils');
         this.drawRoutes = drawRoutes;
         this.markers = [];
         if (this.drawRoutes) {
@@ -110,9 +109,9 @@ export default class TbMapWidget {
         };
 
         if (mapProvider === 'google-map') {
-            this.map = new TbGoogleMap($element, initCallback, this.defaultZoomLevel, this.dontFitMapBounds, minZoomLevel, settings.gmApiKey, settings.gmDefaultMapType);
+            this.map = new TbGoogleMap($element, this.utils, initCallback, this.defaultZoomLevel, this.dontFitMapBounds, minZoomLevel, settings.gmApiKey, settings.gmDefaultMapType);
         } else if (mapProvider === 'openstreet-map') {
-            this.map = new TbOpenStreetMap($element, initCallback, this.defaultZoomLevel, this.dontFitMapBounds, minZoomLevel);
+            this.map = new TbOpenStreetMap($element, this.utils, initCallback, this.defaultZoomLevel, this.dontFitMapBounds, minZoomLevel);
         }
 
     }
@@ -274,9 +273,13 @@ export default class TbMapWidget {
                 if (!this.locationsSettings[i].useMarkerImageFunction &&
                     angular.isDefined(configuredLocationsSettings[i].markerImage) &&
                     configuredLocationsSettings[i].markerImage.length > 0) {
-                    this.locationsSettings[i].markerImage = configuredLocationsSettings[i].markerImage;
                     this.locationsSettings[i].useMarkerImage = true;
-                    this.locationsSettings[i].markerImageSize = configuredLocationsSettings[i].markerImageSize || 34;
+                    var url = this.ctx.settings.markerImage;
+                    var size = this.ctx.settings.markerImageSize || 34;
+                    this.locationSettings.currentImage = {
+                        url: url,
+                        size: size
+                    };
                 }
 
                 if (this.drawRoutes) {
@@ -380,17 +383,41 @@ export default class TbMapWidget {
             }
         }
 
-        function updateLocationMarkerImage(location, dataMap) {
+        function updateLocationMarkerIcon(location, dataMap) {
             var image = calculateLocationMarkerImage(location, dataMap);
-            if (image != null && (!location.settings.calculatedImage || !angular.equals(location.settings.calculatedImage, image))) {
-                tbMap.map.updateMarkerImage(location.marker, location.settings, image.url, image.size);
-                location.settings.calculatedImage = image;
+            if (image && (!location.settings.currentImage || !angular.equals(location.settings.currentImage, image))) {
+                location.settings.currentImage = image;
+                tbMap.map.updateMarkerIcon(location.marker, location.settings);
             }
         }
 
         function updateLocationStyle(location, dataMap) {
             updateLocationColor(location, dataMap);
-            updateLocationMarkerImage(location, dataMap);
+            updateLocationMarkerIcon(location, dataMap);
+        }
+
+        function createOrUpdateLocationMarker(location, markerLocation, dataMap) {
+            var changed = false;
+            if (!location.marker) {
+                var image = calculateLocationMarkerImage(location, dataMap);
+                if (image && (!location.settings.currentImage || !angular.equals(location.settings.currentImage, image))) {
+                    location.settings.currentImage = image;
+                }
+                location.marker = tbMap.map.createMarker(markerLocation, location.settings,
+                    function() {
+                        tbMap.callbacks.onLocationClick(location);
+                    }
+                );
+                tbMap.markers.push(location.marker);
+                changed = true;
+            } else {
+                var prevPosition = tbMap.map.getMarkerPosition(location.marker);
+                if (!prevPosition.equals(markerLocation)) {
+                    tbMap.map.setMarkerPosition(location.marker, markerLocation);
+                    changed = true;
+                }
+            }
+            return changed;
         }
 
         function updateLocation(location, data, dataMap) {
@@ -413,15 +440,7 @@ export default class TbMapWidget {
                         }
                         if (latLngs.length > 0) {
                             var markerLocation = latLngs[latLngs.length-1];
-                            if (!location.marker) {
-                                location.marker = tbMap.map.createMarker(markerLocation, location.settings,
-                                    function() {
-                                        tbMap.callbacks.onLocationClick(location);
-                                    }
-                                );
-                            } else {
-                                tbMap.map.setMarkerPosition(location.marker, markerLocation);
-                            }
+                            createOrUpdateLocationMarker(location, markerLocation, dataMap);
                         }
                         if (!location.polyline) {
                             location.polyline = tbMap.map.createPolyline(latLngs, location.settings);
@@ -439,18 +458,8 @@ export default class TbMapWidget {
                         lat = latData[latData.length-1][1];
                         lng = lngData[lngData.length-1][1];
                         latLng = tbMap.map.createLatLng(lat, lng);
-                        if (!location.marker) {
-                            location.marker = tbMap.map.createMarker(latLng, location.settings, function() {
-                                tbMap.callbacks.onLocationClick(location);
-                            });
-                            tbMap.markers.push(location.marker);
+                        if (createOrUpdateLocationMarker(location, latLng, dataMap)) {
                             locationChanged = true;
-                        } else {
-                            var prevPosition = tbMap.map.getMarkerPosition(location.marker);
-                            if (!prevPosition.equals(latLng)) {
-                                tbMap.map.setMarkerPosition(location.marker, latLng);
-                                locationChanged = true;
-                            }
                         }
                     }
                     updateLocationStyle(location, dataMap);
diff --git a/ui/src/app/widget/lib/map-widget2.js b/ui/src/app/widget/lib/map-widget2.js
index 44abb2b..6b589aa 100644
--- a/ui/src/app/widget/lib/map-widget2.js
+++ b/ui/src/app/widget/lib/map-widget2.js
@@ -13,7 +13,6 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-
 import tinycolor from 'tinycolor2';
 
 import TbGoogleMap from './google-map';
@@ -75,11 +74,11 @@ export default class TbMapWidgetV2 {
         });
 
         if (mapProvider === 'google-map') {
-            this.map = new TbGoogleMap($element, initCallback, this.defaultZoomLevel, this.dontFitMapBounds, minZoomLevel, settings.gmApiKey, settings.gmDefaultMapType);
+            this.map = new TbGoogleMap($element, this.utils, initCallback, this.defaultZoomLevel, this.dontFitMapBounds, minZoomLevel, settings.gmApiKey, settings.gmDefaultMapType);
         } else if (mapProvider === 'openstreet-map') {
-            this.map = new TbOpenStreetMap($element, initCallback, this.defaultZoomLevel, this.dontFitMapBounds, minZoomLevel, settings.mapProvider);
+            this.map = new TbOpenStreetMap($element, this.utils,  initCallback, this.defaultZoomLevel, this.dontFitMapBounds, minZoomLevel, settings.mapProvider);
         } else if (mapProvider === 'image-map') {
-            this.map = new TbImageMap(this.ctx, $element, initCallback,
+            this.map = new TbImageMap(this.ctx, $element, this.utils, initCallback,
                 settings.mapImageUrl,
                 settings.posFunction,
                 settings.imageEntityAlias,
@@ -159,9 +158,13 @@ export default class TbMapWidgetV2 {
         if (!this.locationSettings.useMarkerImageFunction &&
             angular.isDefined(this.ctx.settings.markerImage) &&
             this.ctx.settings.markerImage.length > 0) {
-            this.locationSettings.markerImage = this.ctx.settings.markerImage;
             this.locationSettings.useMarkerImage = true;
-            this.locationSettings.markerImageSize = this.ctx.settings.markerImageSize || 34;
+            var url = this.ctx.settings.markerImage;
+            var size = this.ctx.settings.markerImageSize || 34;
+            this.locationSettings.currentImage = {
+                url: url,
+                size: size
+            };
         }
 
         if (this.drawRoutes) {
@@ -235,10 +238,10 @@ export default class TbMapWidgetV2 {
             }
         }
 
-        function updateLocationMarkerImage(location, image) {
-            if (image && (!location.settings.calculatedImage || !angular.equals(location.settings.calculatedImage, image))) {
-                tbMap.map.updateMarkerImage(location.marker, location.settings, image.url, image.size);
-                location.settings.calculatedImage = image;
+        function updateLocationMarkerIcon(location, image) {
+            if (image && (!location.settings.currentImage || !angular.equals(location.settings.currentImage, image))) {
+                location.settings.currentImage = image;
+                tbMap.map.updateMarkerIcon(location.marker, location.settings);
             }
         }
 
@@ -247,7 +250,31 @@ export default class TbMapWidgetV2 {
             var color = calculateLocationColor(location, dataMap);
             var image = calculateLocationMarkerImage(location, dataMap);
             updateLocationColor(location, color, image);
-            updateLocationMarkerImage(location, image);
+            updateLocationMarkerIcon(location, image);
+        }
+
+        function createOrUpdateLocationMarker(location, markerLocation, dataMap) {
+            var changed = false;
+            if (!location.marker) {
+                var image = calculateLocationMarkerImage(location, dataMap);
+                if (image && (!location.settings.currentImage || !angular.equals(location.settings.currentImage, image))) {
+                    location.settings.currentImage = image;
+                }
+                location.marker = tbMap.map.createMarker(markerLocation, location.settings,
+                    function (event) {
+                        tbMap.callbacks.onLocationClick(location);
+                        locationRowClick(event, location);
+                    }, [location.dsIndex]);
+                tbMap.markers.push(location.marker);
+                changed = true;
+            } else {
+                var prevPosition = tbMap.map.getMarkerPosition(location.marker);
+                if (!prevPosition.equals(markerLocation)) {
+                    tbMap.map.setMarkerPosition(location.marker, markerLocation);
+                    changed = true;
+                }
+            }
+            return changed;
         }
 
         function locationRowClick($event, location) {
@@ -284,16 +311,7 @@ export default class TbMapWidgetV2 {
                         }
                         if (latLngs.length > 0) {
                             var markerLocation = latLngs[latLngs.length - 1];
-                            if (!location.marker) {
-                                location.marker = tbMap.map.createMarker(markerLocation, location.settings,
-                                    function (event) {
-                                        tbMap.callbacks.onLocationClick(location);
-                                        locationRowClick(event, location);
-                                    }, [location.dsIndex]
-                                );
-                            } else {
-                                tbMap.map.setMarkerPosition(location.marker, markerLocation);
-                            }
+                            createOrUpdateLocationMarker(location, markerLocation, dataMap);
                         }
                         if (!location.polyline) {
                             location.polyline = tbMap.map.createPolyline(latLngs, location.settings);
@@ -312,20 +330,8 @@ export default class TbMapWidgetV2 {
                         lng = lngData[lngData.length - 1][1];
                         if (angular.isDefined(lat) && lat != null && angular.isDefined(lng) && lng != null) {
                             latLng = tbMap.map.createLatLng(lat, lng);
-                            if (!location.marker) {
-                                location.marker = tbMap.map.createMarker(latLng, location.settings,
-                                    function (event) {
-                                        tbMap.callbacks.onLocationClick(location);
-                                        locationRowClick(event, location);
-                                    }, [location.dsIndex]);
-                                tbMap.markers.push(location.marker);
+                            if (createOrUpdateLocationMarker(location, latLng, dataMap)) {
                                 locationChanged = true;
-                            } else {
-                                var prevPosition = tbMap.map.getMarkerPosition(location.marker);
-                                if (!prevPosition.equals(latLng)) {
-                                    tbMap.map.setMarkerPosition(location.marker, latLng);
-                                    locationChanged = true;
-                                }
                             }
                         }
                     }
diff --git a/ui/src/app/widget/lib/openstreet-map.js b/ui/src/app/widget/lib/openstreet-map.js
index 1d1d3f7..8aafe69 100644
--- a/ui/src/app/widget/lib/openstreet-map.js
+++ b/ui/src/app/widget/lib/openstreet-map.js
@@ -13,15 +13,15 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-
 import 'leaflet/dist/leaflet.css';
 import * as L from 'leaflet';
 import 'leaflet-providers';
 
 export default class TbOpenStreetMap {
 
-    constructor($containerElement, initCallback, defaultZoomLevel, dontFitMapBounds, minZoomLevel, mapProvider) {
+    constructor($containerElement, utils, initCallback, defaultZoomLevel, dontFitMapBounds, minZoomLevel, mapProvider) {
 
+        this.utils = utils;
         this.defaultZoomLevel = defaultZoomLevel;
         this.dontFitMapBounds = dontFitMapBounds;
         this.minZoomLevel = minZoomLevel;
@@ -54,55 +54,62 @@ export default class TbOpenStreetMap {
     }
 
     updateMarkerColor(marker, color) {
-        var pinColor = color.substr(1);
-        var icon = L.icon({
-            iconUrl: 'https://chart.apis.google.com/chart?chst=d_map_pin_letter&chld=%E2%80%A2|' + pinColor,
-            iconSize: [21, 34],
-            iconAnchor: [10, 34],
-            popupAnchor: [0, -34],
-            shadowUrl: 'https://chart.apis.google.com/chart?chst=d_map_pin_shadow',
-            shadowSize: [40, 37],
-            shadowAnchor: [12, 35]
+        this.createDefaultMarkerIcon(marker, color, (iconInfo) => {
+            marker.setIcon(iconInfo.icon);
         });
-        marker.setIcon(icon);
-    }
-
-    updateMarkerImage(marker, settings, image, maxSize) {
-        var testImage = document.createElement('img'); // eslint-disable-line
-        testImage.style.visibility = 'hidden';
-        testImage.onload = function() {
-            var width;
-            var height;
-            var aspect = testImage.width / testImage.height;
-            document.body.removeChild(testImage); //eslint-disable-line
-            if (aspect > 1) {
-                width = maxSize;
-                height = maxSize / aspect;
-            } else {
-                width = maxSize * aspect;
-                height = maxSize;
-            }
-            var icon = L.icon({
-                iconUrl: image,
-                iconSize: [width, height],
-                iconAnchor: [width/2, height],
-                popupAnchor: [0, -height]
-            });
-            marker.setIcon(icon);
+    }
+
+    updateMarkerIcon(marker, settings) {
+        this.createMarkerIcon(marker, settings, (iconInfo) => {
+            marker.setIcon(iconInfo.icon);
             if (settings.showLabel) {
                 marker.unbindTooltip();
-                marker.tooltipOffset = [0, -height + 10];
+                marker.tooltipOffset = [0, -iconInfo.size[1] + 10];
                 marker.bindTooltip('<div style="color: '+ settings.labelColor +';"><b>'+settings.labelText+'</b></div>',
                     { className: 'tb-marker-label', permanent: true, direction: 'top', offset: marker.tooltipOffset });
             }
+        });
+    }
+
+    createMarkerIcon(marker, settings, onMarkerIconReady) {
+        var currentImage = settings.currentImage;
+        var opMap = this;
+        if (currentImage && currentImage.url) {
+            this.utils.loadImageAspect(currentImage.url).then(
+                (aspect) => {
+                    if (aspect) {
+                        var width;
+                        var height;
+                        if (aspect > 1) {
+                            width = currentImage.size;
+                            height = currentImage.size / aspect;
+                        } else {
+                            width = currentImage.size * aspect;
+                            height = currentImage.size;
+                        }
+                        var icon = L.icon({
+                            iconUrl: currentImage.url,
+                            iconSize: [width, height],
+                            iconAnchor: [width/2, height],
+                            popupAnchor: [0, -height]
+                        });
+                        var iconInfo = {
+                            size: [width, height],
+                            icon: icon
+                        };
+                        onMarkerIconReady(iconInfo);
+                    } else {
+                        opMap.createDefaultMarkerIcon(marker, settings.color, onMarkerIconReady);
+                    }
+                }
+            );
+        } else {
+            this.createDefaultMarkerIcon(marker, settings.color, onMarkerIconReady);
         }
-        document.body.appendChild(testImage); //eslint-disable-line
-        testImage.src = image;
     }
 
-    createMarker(location, settings, onClickListener, markerArgs) {
-        var height = 34;
-        var pinColor = settings.color.substr(1);
+    createDefaultMarkerIcon(marker, color, onMarkerIconReady) {
+        var pinColor = color.substr(1);
         var icon = L.icon({
             iconUrl: 'https://chart.apis.google.com/chart?chst=d_map_pin_letter&chld=%E2%80%A2|' + pinColor,
             iconSize: [21, 34],
@@ -112,18 +119,25 @@ export default class TbOpenStreetMap {
             shadowSize: [40, 37],
             shadowAnchor: [12, 35]
         });
+        var iconInfo = {
+            size: [21, 34],
+            icon: icon
+        };
+        onMarkerIconReady(iconInfo);
+    }
 
-        var marker = L.marker(location, {icon: icon}).addTo(this.map);
-
-        if (settings.showLabel) {
-            marker.tooltipOffset = [0, -height + 10];
-            marker.bindTooltip('<div style="color: '+ settings.labelColor +';"><b>'+settings.labelText+'</b></div>',
-                { className: 'tb-marker-label', permanent: true, direction: 'top', offset: marker.tooltipOffset });
-        }
-
-        if (settings.useMarkerImage) {
-            this.updateMarkerImage(marker, settings, settings.markerImage, settings.markerImageSize || 34);
-        }
+    createMarker(location, settings, onClickListener, markerArgs) {
+        var marker = L.marker(location, {});
+        var opMap = this;
+        this.createMarkerIcon(marker, settings, (iconInfo) => {
+            marker.setIcon(iconInfo.icon);
+            if (settings.showLabel) {
+                marker.tooltipOffset = [0, -iconInfo.size[1] + 10];
+                marker.bindTooltip('<div style="color: '+ settings.labelColor +';"><b>'+settings.labelText+'</b></div>',
+                    { className: 'tb-marker-label', permanent: true, direction: 'top', offset: marker.tooltipOffset });
+            }
+            marker.addTo(opMap.map);
+        });
 
         if (settings.displayTooltip) {
             this.createTooltip(marker, settings.tooltipPattern, settings.tooltipReplaceInfo, settings.autocloseTooltip, markerArgs);
diff --git a/ui/src/app/widget/lib/timeseries-table-widget.js b/ui/src/app/widget/lib/timeseries-table-widget.js
index 0c23e02..28ac811 100644
--- a/ui/src/app/widget/lib/timeseries-table-widget.js
+++ b/ui/src/app/widget/lib/timeseries-table-widget.js
@@ -47,9 +47,37 @@ function TimeseriesTableWidget() {
 /*@ngInject*/
 function TimeseriesTableWidgetController($element, $scope, $filter) {
     var vm = this;
+    let dateFormatFilter = 'yyyy-MM-dd HH:mm:ss';
 
     vm.sources = [];
     vm.sourceIndex = 0;
+    vm.defaultPageSize = 10;
+    vm.defaultSortOrder = '-0';
+    vm.query = {
+        "search": null
+    };
+
+    vm.enterFilterMode = enterFilterMode;
+    vm.exitFilterMode = exitFilterMode;
+
+    function enterFilterMode () {
+        vm.query.search = '';
+        vm.ctx.hideTitlePanel = true;
+    }
+
+    function exitFilterMode () {
+        vm.query.search = null;
+        vm.ctx.hideTitlePanel = false;
+    }
+
+    vm.searchAction = {
+        name: 'action.search',
+        show: true,
+        onAction: function() {
+            vm.enterFilterMode();
+        },
+        icon: 'search'
+    };
 
     $scope.$watch('vm.ctx', function() {
        if (vm.ctx) {
@@ -62,6 +90,7 @@ function TimeseriesTableWidgetController($element, $scope, $filter) {
     });
 
     function initialize() {
+        vm.ctx.widgetActions = [ vm.searchAction ];
         vm.showTimestamp = vm.settings.showTimestamp !== false;
         var origColor = vm.widgetConfig.color || 'rgba(0, 0, 0, 0.87)';
         var defaultColor = tinycolor(origColor);
@@ -108,6 +137,8 @@ function TimeseriesTableWidgetController($element, $scope, $filter) {
         cssParser.createStyleElement(namespace, cssString);
         $element.addClass(namespace);
 
+        vm.displayPagination = angular.isDefined(vm.settings.displayPagination) ? vm.settings.displayPagination : true;
+
         function hashCode(str) {
             var hash = 0;
             var i, char;
@@ -163,7 +194,7 @@ function TimeseriesTableWidgetController($element, $scope, $filter) {
 
     vm.cellContent = function(source, index, row, value) {
         if (index === 0) {
-            return $filter('date')(value, 'yyyy-MM-dd HH:mm:ss');
+            return $filter('date')(value, dateFormatFilter);
         } else {
             var strContent = '';
             if (angular.isDefined(value)) {
@@ -211,7 +242,7 @@ function TimeseriesTableWidgetController($element, $scope, $filter) {
                 source.data = [];
                 source.rawData = [];
                 source.query = {
-                    limit: 5,
+                    limit: vm.settings.defaultPageSize || 10,
                     page: 1,
                     order: '-0'
                 }
@@ -287,7 +318,30 @@ function TimeseriesTableWidgetController($element, $scope, $filter) {
     }
 
     function reorder(source) {
+        let searchRegExp = new RegExp(vm.query.search);
+
         source.data = $filter('orderBy')(source.data, source.query.order);
+        if (vm.query.search !== null) {
+            source.data = source.data.filter(function(item){
+                for (let i = 0; i < item.length; i++) {
+                    if (vm.showTimestamp) {
+                        if (i === 0) {
+                            if (searchRegExp.test($filter('date')(item[i], dateFormatFilter))) {
+                                return true;
+                            }
+                        } else {
+                            if (searchRegExp.test(item[i])) {
+                                return true;
+                            }
+                        }
+                    } else {
+                        if (searchRegExp.test(item[i])) {
+                            return true;
+                        }
+                    }
+                }
+            });
+        }
     }
 
     function convertData(data) {
diff --git a/ui/src/app/widget/lib/timeseries-table-widget.scss b/ui/src/app/widget/lib/timeseries-table-widget.scss
index 99a0653..da3ee81 100644
--- a/ui/src/app/widget/lib/timeseries-table-widget.scss
+++ b/ui/src/app/widget/lib/timeseries-table-widget.scss
@@ -26,4 +26,8 @@ tb-timeseries-table-widget {
     .md-table-pagination>* {
         height: 46px;
     }
+
+    .tb-data-table md-toolbar {
+        z-index: 10;
+    }
 }
diff --git a/ui/src/app/widget/lib/timeseries-table-widget.tpl.html b/ui/src/app/widget/lib/timeseries-table-widget.tpl.html
index 2b6d72c..2e7286c 100644
--- a/ui/src/app/widget/lib/timeseries-table-widget.tpl.html
+++ b/ui/src/app/widget/lib/timeseries-table-widget.tpl.html
@@ -15,29 +15,71 @@
     limitations under the License.
 
 -->
+<div class="tb-absolute-fill tb-entities-table tb-data-table timeseriesWidget" layout="column">
+    <div flex class="tb-absolute-fill" layout="column">
+        <md-toolbar class="md-table-toolbar md-default" ng-show="vm.query.search !== null">
+            <div class="md-toolbar-tools">
+                <md-button class="md-icon-button" aria-label="{{ 'action.search' | translate }}">
+                    <md-icon aria-label="{{ 'action.search' | translate }}" class="material-icons">search</md-icon>
+                    <md-tooltip md-direction="{{vm.ctx.dashboard.isWidgetExpanded ? 'bottom' : 'top'}}">
+                        {{'entity.search' | translate}}
+                    </md-tooltip>
+                </md-button>
+                <md-input-container flex>
+                    <label>&nbsp;</label>
+                    <input ng-model="vm.query.search" placeholder="{{'widget.search-data' | translate}}" md-autofocus/>
+                </md-input-container>
+                <md-button class="md-icon-button" aria-label="Close" ng-click="vm.exitFilterMode()">
+                    <md-icon aria-label="Close" class="material-icons">close</md-icon>
+                    <md-tooltip md-direction="{{vm.ctx.dashboard.isWidgetExpanded ? 'bottom' : 'top'}}">
+                        {{ 'action.close' | translate }}
+                    </md-tooltip>
+                </md-button>
+            </div>
+        </md-toolbar>
 
-<md-tabs md-selected="vm.sourceIndex" ng-class="{'tb-headless': vm.sources.length === 1}"
-         id="tabs" md-border-bottom flex class="tb-absolute-fill">
-    <md-tab ng-repeat="source in vm.sources" label="{{ source.datasource.name }}">
-        <md-table-container>
-            <table md-table>
-                <thead md-head md-order="source.query.order" md-on-reorder="vm.onReorder(source)">
-                <tr md-row>
-                    <th ng-show="vm.showTimestamp" md-column md-order-by="0"><span>Timestamp</span></th>
-                    <th md-column md-order-by="{{ h.index }}" ng-repeat="h in source.ts.header"><span>{{ h.dataKey.label }}</span></th>
-                </tr>
-                </thead>
-                <tbody md-body>
-                <tr md-row ng-repeat="row in source.ts.data">
-                    <td ng-show="$index > 0 || ($index === 0 && vm.showTimestamp)" md-cell ng-repeat="d in row track by $index" ng-style="vm.cellStyle(source, $index, d)" ng-bind-html="vm.cellContent(source, $index, row, d)">
-                    </td>
-                </tr>
-                </tbody>
-            </table>
-        </md-table-container>
-        <md-table-pagination md-limit="source.query.limit" md-limit-options="[5, 10, 15]"
-                             md-page="source.query.page" md-total="{{source.ts.count}}"
-                             md-on-paginate="vm.onPaginate(source)" md-page-select>
-        </md-table-pagination>
-    </md-tab>
-</md-tabs>
\ No newline at end of file
+        <md-tabs flex md-selected="vm.sourceIndex" ng-class="{'tb-headless': vm.sources.length === 1}"
+                 id="tabs" md-border-bottom flex>
+            <md-tab ng-repeat="source in vm.sources" label="{{ source.datasource.name }}">
+                <md-table-container>
+                    <table md-table>
+                        <thead md-head md-order="source.query.order" md-on-reorder="vm.onReorder(source)">
+                            <tr md-row>
+                                <th ng-show="vm.showTimestamp"
+                                    md-column md-order-by="0"
+                                >
+                                    <span>Timestamp</span>
+                                </th>
+                                <th md-column
+                                    md-order-by="{{ h.index }}"
+                                    ng-repeat="h in source.ts.header"
+                                >
+                                    <span>{{ h.dataKey.label }}</span>
+                                </th>
+                            </tr>
+                        </thead>
+
+                        <tbody md-body>
+                            <tr md-row ng-repeat="row in source.ts.data track by $index">
+                                <td ng-show="$index > 0 || ($index === 0 && vm.showTimestamp)"
+                                    md-cell
+                                    ng-repeat="d in row track by $index"
+                                    ng-style="vm.cellStyle(source, $index, d)"
+                                    ng-bind-html="vm.cellContent(source, $index, row, d)"
+                                ></td>
+                            </tr>
+                        </tbody>
+                    </table>
+                </md-table-container>
+                <md-table-pagination ng-if="vm.displayPagination"
+                                     md-limit="source.query.limit"
+                                     md-limit-options="vm.limitOptions"
+                                     md-page="source.query.page"
+                                     md-total="{{source.data.length}}"
+                                     md-on-paginate="vm.onPaginate(source)"
+                                     md-page-select>
+                </md-table-pagination>
+            </md-tab>
+        </md-tabs>
+    </div>
+</div>
\ No newline at end of file
diff --git a/ui/src/app/widget/save-widget-type-as.tpl.html b/ui/src/app/widget/save-widget-type-as.tpl.html
index 6bdc54d..87fb0cf 100644
--- a/ui/src/app/widget/save-widget-type-as.tpl.html
+++ b/ui/src/app/widget/save-widget-type-as.tpl.html
@@ -26,8 +26,8 @@
                 </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-progress-linear class="md-warn" md-mode="indeterminate" ng-disabled="!$root.loading" ng-show="$root.loading"></md-progress-linear>
+        <span style="min-height: 5px;" flex="" ng-show="!$root.loading"></span>
         <md-dialog-content>
             <div class="md-dialog-content">
                 <fieldset>
@@ -49,10 +49,10 @@
         </md-dialog-content>
         <md-dialog-actions layout="row">
             <span flex></span>
-            <md-button ng-disabled="loading || theForm.$invalid" type="submit" class="md-raised md-primary">
+            <md-button ng-disabled="$root.loading || theForm.$invalid" type="submit" class="md-raised md-primary">
                 {{ 'action.saveAs' | translate }}
             </md-button>
-            <md-button ng-disabled="loading" ng-click="vm.cancel()" style="margin-right:20px;">{{ 'action.cancel' |
+            <md-button ng-disabled="$root.loading" ng-click="vm.cancel()" style="margin-right:20px;">{{ 'action.cancel' |
                 translate }}
             </md-button>
         </md-dialog-actions>
diff --git a/ui/src/app/widget/select-widget-type.tpl.html b/ui/src/app/widget/select-widget-type.tpl.html
index b7ebcb1..ccae912 100644
--- a/ui/src/app/widget/select-widget-type.tpl.html
+++ b/ui/src/app/widget/select-widget-type.tpl.html
@@ -26,11 +26,11 @@
                 </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-progress-linear class="md-warn" md-mode="indeterminate" ng-disabled="!$root.loading" ng-show="$root.loading"></md-progress-linear>
+        <span style="min-height: 5px;" flex="" ng-show="!$root.loading"></span>
         <md-dialog-content>
             <div class="md-dialog-content">
-                <fieldset ng-disabled="loading">
+                <fieldset ng-disabled="$root.loading">
                     <div layout="column" layout-gt-sm="row" layout-align="center center">
                         <md-button class="tb-card-button md-raised md-primary" layout="column"
                                    ng-click="vm.typeSelected(vm.types.widgetType.timeseries.value)">
@@ -73,7 +73,7 @@
         </md-dialog-content>
         <md-dialog-actions layout="row">
             <span flex></span>
-            <md-button ng-disabled="loading" ng-click="vm.cancel()" style="margin-right:20px;">{{ 'action.cancel' |
+            <md-button ng-disabled="$root.loading" ng-click="vm.cancel()" style="margin-right:20px;">{{ 'action.cancel' |
                 translate }}
             </md-button>
         </md-dialog-actions>
diff --git a/ui/src/app/widget/widget-editor.tpl.html b/ui/src/app/widget/widget-editor.tpl.html
index 732cf8f..9d10c05 100644
--- a/ui/src/app/widget/widget-editor.tpl.html
+++ b/ui/src/app/widget/widget-editor.tpl.html
@@ -129,7 +129,7 @@
                                                 <input placeholder="{{ 'widget.resource-url' | translate }}"
                                                        ng-required="true" name="resource" ng-model="resource.url">
                                             </md-input-container>
-                                            <md-button ng-disabled="loading" class="md-icon-button md-primary"
+                                            <md-button ng-disabled="$root.loading" class="md-icon-button md-primary"
                                                        ng-click="vm.removeResource($index)"
                                                        aria-label="{{ 'action.remove' | translate }}">
                                                 <md-tooltip md-direction="top">
@@ -142,7 +142,7 @@
                                             </md-button>
                                         </div>
                                         <div>
-                                            <md-button ng-disabled="loading" class="md-primary md-raised"
+                                            <md-button ng-disabled="$root.loading" class="md-primary md-raised"
                                                        ng-click="vm.addResource()"
                                                        aria-label="{{ 'action.add' | translate }}">
                                                 <md-tooltip md-direction="top">
diff --git a/ui/src/app/widget/widget-library.tpl.html b/ui/src/app/widget/widget-library.tpl.html
index b2dd4a7..ad48131 100644
--- a/ui/src/app/widget/widget-library.tpl.html
+++ b/ui/src/app/widget/widget-library.tpl.html
@@ -15,7 +15,7 @@
     limitations under the License.
 
 -->
-<section ng-show="!loading && vm.noData()" layout-align="center center"
+<section ng-show="!$root.loading && vm.noData()" layout-align="center center"
 		 style="text-transform: uppercase; display: flex; z-index: 1;"
 		 class="md-headline tb-absolute-fill">
 	 <md-button ng-if="!vm.isReadOnly()" class="tb-add-new-widget" ng-click="vm.addWidgetType($event)">
@@ -42,10 +42,10 @@
 	on-init-failed="vm.dashboardInitFailed(e)">
 </tb-dashboard>
 <section layout="row" layout-wrap class="tb-footer-buttons md-fab ">
-	<md-fab-speed-dial ng-disabled="loading" ng-show="!vm.isReadOnly()"
+	<md-fab-speed-dial ng-disabled="$root.loading" ng-show="!vm.isReadOnly()"
 					   md-open="vm.addItemActionsOpen" class="md-scale" md-direction="up">
 		<md-fab-trigger>
-			<md-button ng-disabled="loading"
+			<md-button ng-disabled="$root.loading"
 					   class="tb-btn-footer md-accent md-hue-2 md-fab"
 					   aria-label="{{ 'widget.add-widget-type' | translate }}">
 				<md-tooltip md-direction="top">
@@ -55,7 +55,7 @@
 			</md-button>
 		</md-fab-trigger>
 		<md-fab-actions>
-			<md-button ng-disabled="loading"
+			<md-button ng-disabled="$root.loading"
 					   class="tmd-accent md-hue-2 md-fab" ng-click="vm.addWidgetType($event)"
 					   aria-label="{{ 'action.create' | translate }}">
 				<md-tooltip md-direction="top">
@@ -63,7 +63,7 @@
 				</md-tooltip>
 				<ng-md-icon icon="insert_drive_file"></ng-md-icon>
 			</md-button>
-			<md-button ng-disabled="loading"
+			<md-button ng-disabled="$root.loading"
 					   class="tmd-accent md-hue-2 md-fab" ng-click="vm.importWidgetType($event)"
 					   aria-label="{{ 'action.import' | translate }}">
 				<md-tooltip md-direction="top">
diff --git a/ui/src/app/widget/widgets-bundle-fieldset.tpl.html b/ui/src/app/widget/widgets-bundle-fieldset.tpl.html
index 15810d0..e4d8ea1 100644
--- a/ui/src/app/widget/widgets-bundle-fieldset.tpl.html
+++ b/ui/src/app/widget/widgets-bundle-fieldset.tpl.html
@@ -23,7 +23,7 @@
            class="md-raised md-primary">{{ 'widgets-bundle.delete' | translate }}</md-button>
 
 <md-content class="md-padding" layout="column">
-    <fieldset ng-disabled="loading || !isEdit">
+    <fieldset ng-disabled="$root.loading || !isEdit">
         <md-input-container class="md-block">
             <label translate>widgets-bundle.title</label>
             <input required name="title" ng-model="widgetsBundle.title">