thingsboard-aplcache

Changes

Details

diff --git a/application/src/main/java/org/thingsboard/server/controller/AssetController.java b/application/src/main/java/org/thingsboard/server/controller/AssetController.java
index 24497d4..dd43e1c 100644
--- a/application/src/main/java/org/thingsboard/server/controller/AssetController.java
+++ b/application/src/main/java/org/thingsboard/server/controller/AssetController.java
@@ -21,6 +21,7 @@ import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.web.bind.annotation.*;
 import org.thingsboard.server.common.data.Customer;
 import org.thingsboard.server.common.data.asset.Asset;
+import org.thingsboard.server.common.data.asset.TenantAssetType;
 import org.thingsboard.server.common.data.id.AssetId;
 import org.thingsboard.server.common.data.id.CustomerId;
 import org.thingsboard.server.common.data.id.TenantId;
@@ -136,13 +137,18 @@ public class AssetController extends BaseController {
     @ResponseBody
     public TextPageData<Asset> getTenantAssets(
             @RequestParam int limit,
+            @RequestParam(required = false) String type,
             @RequestParam(required = false) String textSearch,
             @RequestParam(required = false) String idOffset,
             @RequestParam(required = false) String textOffset) throws ThingsboardException {
         try {
             TenantId tenantId = getCurrentUser().getTenantId();
             TextPageLink pageLink = createPageLink(limit, textSearch, idOffset, textOffset);
-            return checkNotNull(assetService.findAssetsByTenantId(tenantId, pageLink));
+            if (type != null && type.trim().length()>0) {
+                return checkNotNull(assetService.findAssetsByTenantIdAndType(tenantId, type, pageLink));
+            } else {
+                return checkNotNull(assetService.findAssetsByTenantId(tenantId, pageLink));
+            }
         } catch (Exception e) {
             throw handleException(e);
         }
@@ -167,6 +173,7 @@ public class AssetController extends BaseController {
     public TextPageData<Asset> getCustomerAssets(
             @PathVariable("customerId") String strCustomerId,
             @RequestParam int limit,
+            @RequestParam(required = false) String type,
             @RequestParam(required = false) String textSearch,
             @RequestParam(required = false) String idOffset,
             @RequestParam(required = false) String textOffset) throws ThingsboardException {
@@ -176,7 +183,11 @@ public class AssetController extends BaseController {
             CustomerId customerId = new CustomerId(toUUID(strCustomerId));
             checkCustomerId(customerId);
             TextPageLink pageLink = createPageLink(limit, textSearch, idOffset, textOffset);
-            return checkNotNull(assetService.findAssetsByTenantIdAndCustomerId(tenantId, customerId, pageLink));
+            if (type != null && type.trim().length()>0) {
+                return checkNotNull(assetService.findAssetsByTenantIdAndCustomerIdAndType(tenantId, customerId, type, pageLink));
+            } else {
+                return checkNotNull(assetService.findAssetsByTenantIdAndCustomerId(tenantId, customerId, pageLink));
+            }
         } catch (Exception e) {
             throw handleException(e);
         }
@@ -231,4 +242,18 @@ public class AssetController extends BaseController {
             throw handleException(e);
         }
     }
+
+    @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
+    @RequestMapping(value = "/asset/types", method = RequestMethod.GET)
+    @ResponseBody
+    public List<TenantAssetType> getAssetTypes() throws ThingsboardException {
+        try {
+            SecurityUser user = getCurrentUser();
+            TenantId tenantId = user.getTenantId();
+            ListenableFuture<List<TenantAssetType>> assetTypes = assetService.findAssetTypesByTenantId(tenantId);
+            return checkNotNull(assetTypes.get());
+        } catch (Exception e) {
+            throw handleException(e);
+        }
+    }
 }
diff --git a/application/src/main/java/org/thingsboard/server/controller/DeviceController.java b/application/src/main/java/org/thingsboard/server/controller/DeviceController.java
index 7cd381c..8257767 100644
--- a/application/src/main/java/org/thingsboard/server/controller/DeviceController.java
+++ b/application/src/main/java/org/thingsboard/server/controller/DeviceController.java
@@ -21,6 +21,7 @@ import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.web.bind.annotation.*;
 import org.thingsboard.server.common.data.Customer;
 import org.thingsboard.server.common.data.Device;
+import org.thingsboard.server.common.data.TenantDeviceType;
 import org.thingsboard.server.common.data.id.CustomerId;
 import org.thingsboard.server.common.data.id.DeviceId;
 import org.thingsboard.server.common.data.id.TenantId;
@@ -166,13 +167,18 @@ public class DeviceController extends BaseController {
     @ResponseBody
     public TextPageData<Device> getTenantDevices(
             @RequestParam int limit,
+            @RequestParam(required = false) String type,
             @RequestParam(required = false) String textSearch,
             @RequestParam(required = false) String idOffset,
             @RequestParam(required = false) String textOffset) throws ThingsboardException {
         try {
             TenantId tenantId = getCurrentUser().getTenantId();
             TextPageLink pageLink = createPageLink(limit, textSearch, idOffset, textOffset);
-            return checkNotNull(deviceService.findDevicesByTenantId(tenantId, pageLink));
+            if (type != null && type.trim().length()>0) {
+                return checkNotNull(deviceService.findDevicesByTenantIdAndType(tenantId, type, pageLink));
+            } else {
+                return checkNotNull(deviceService.findDevicesByTenantId(tenantId, pageLink));
+            }
         } catch (Exception e) {
             throw handleException(e);
         }
@@ -197,6 +203,7 @@ public class DeviceController extends BaseController {
     public TextPageData<Device> getCustomerDevices(
             @PathVariable("customerId") String strCustomerId,
             @RequestParam int limit,
+            @RequestParam(required = false) String type,
             @RequestParam(required = false) String textSearch,
             @RequestParam(required = false) String idOffset,
             @RequestParam(required = false) String textOffset) throws ThingsboardException {
@@ -206,7 +213,11 @@ public class DeviceController extends BaseController {
             CustomerId customerId = new CustomerId(toUUID(strCustomerId));
             checkCustomerId(customerId);
             TextPageLink pageLink = createPageLink(limit, textSearch, idOffset, textOffset);
-            return checkNotNull(deviceService.findDevicesByTenantIdAndCustomerId(tenantId, customerId, pageLink));
+            if (type != null && type.trim().length()>0) {
+                return checkNotNull(deviceService.findDevicesByTenantIdAndCustomerIdAndType(tenantId, customerId, type, pageLink));
+            } else {
+                return checkNotNull(deviceService.findDevicesByTenantIdAndCustomerId(tenantId, customerId, pageLink));
+            }
         } catch (Exception e) {
             throw handleException(e);
         }
@@ -261,4 +272,19 @@ public class DeviceController extends BaseController {
             throw handleException(e);
         }
     }
+
+    @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
+    @RequestMapping(value = "/device/types", method = RequestMethod.GET)
+    @ResponseBody
+    public List<TenantDeviceType> getDeviceTypes() throws ThingsboardException {
+        try {
+            SecurityUser user = getCurrentUser();
+            TenantId tenantId = user.getTenantId();
+            ListenableFuture<List<TenantDeviceType>> deviceTypes = deviceService.findDeviceTypesByTenantId(tenantId);
+            return checkNotNull(deviceTypes.get());
+        } catch (Exception e) {
+            throw handleException(e);
+        }
+    }
+
 }
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 b9647e5..435f9a6 100644
--- a/application/src/test/java/org/thingsboard/server/controller/AbstractControllerTest.java
+++ b/application/src/test/java/org/thingsboard/server/controller/AbstractControllerTest.java
@@ -98,13 +98,15 @@ import static org.springframework.test.web.servlet.setup.MockMvcBuilders.webAppC
 @IntegrationTest("server.port:0")
 public abstract class AbstractControllerTest {
 
+    protected static final String TEST_TENANT_NAME = "TEST TENANT";
+
     protected static final String SYS_ADMIN_EMAIL = "sysadmin@thingsboard.org";
     private static final String SYS_ADMIN_PASSWORD = "sysadmin";
     
-    protected static final String TENANT_ADMIN_EMAIL = "tenant@thingsboard.org";
+    protected static final String TENANT_ADMIN_EMAIL = "testtenant@thingsboard.org";
     private static final String TENANT_ADMIN_PASSWORD = "tenant";
 
-    protected static final String CUSTOMER_USER_EMAIL = "customer@thingsboard.org";
+    protected static final String CUSTOMER_USER_EMAIL = "testcustomer@thingsboard.org";
     private static final String CUSTOMER_USER_PASSWORD = "customer";
     
     protected MediaType contentType = new MediaType(MediaType.APPLICATION_JSON.getType(),
@@ -147,7 +149,7 @@ public abstract class AbstractControllerTest {
         loginSysAdmin();
 
         Tenant tenant = new Tenant();
-        tenant.setTitle("Tenant");
+        tenant.setTitle(TEST_TENANT_NAME);
         Tenant savedTenant = doPost("/api/tenant", tenant, Tenant.class);
         Assert.assertNotNull(savedTenant);
         tenantId = savedTenant.getId();
diff --git a/application/src/test/java/org/thingsboard/server/controller/AssetControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/AssetControllerTest.java
new file mode 100644
index 0000000..10a2ff7
--- /dev/null
+++ b/application/src/test/java/org/thingsboard/server/controller/AssetControllerTest.java
@@ -0,0 +1,659 @@
+/**
+ * Copyright © 2016-2017 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.thingsboard.server.controller;
+
+import static org.hamcrest.Matchers.containsString;
+import static org.thingsboard.server.dao.model.ModelConstants.NULL_UUID;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import org.apache.commons.lang3.RandomStringUtils;
+import org.thingsboard.server.common.data.*;
+import org.thingsboard.server.common.data.asset.Asset;
+import org.thingsboard.server.common.data.asset.TenantAssetType;
+import org.thingsboard.server.common.data.id.CustomerId;
+import org.thingsboard.server.common.data.id.AssetId;
+import org.thingsboard.server.common.data.page.TextPageData;
+import org.thingsboard.server.common.data.page.TextPageLink;
+import org.thingsboard.server.common.data.security.Authority;
+import org.thingsboard.server.dao.model.ModelConstants;
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+
+import com.datastax.driver.core.utils.UUIDs;
+import com.fasterxml.jackson.core.type.TypeReference;
+
+public class AssetControllerTest extends AbstractControllerTest {
+
+    private IdComparator<Asset> idComparator = new IdComparator<>();
+
+    private Tenant savedTenant;
+    private User tenantAdmin;
+
+    @Before
+    public void beforeTest() throws Exception {
+        loginSysAdmin();
+
+        Tenant tenant = new Tenant();
+        tenant.setTitle("My tenant");
+        savedTenant = doPost("/api/tenant", tenant, Tenant.class);
+        Assert.assertNotNull(savedTenant);
+
+        tenantAdmin = new User();
+        tenantAdmin.setAuthority(Authority.TENANT_ADMIN);
+        tenantAdmin.setTenantId(savedTenant.getId());
+        tenantAdmin.setEmail("tenant2@thingsboard.org");
+        tenantAdmin.setFirstName("Joe");
+        tenantAdmin.setLastName("Downs");
+
+        tenantAdmin = createUserAndLogin(tenantAdmin, "testPassword1");
+    }
+
+    @After
+    public void afterTest() throws Exception {
+        loginSysAdmin();
+
+        doDelete("/api/tenant/"+savedTenant.getId().getId().toString())
+                .andExpect(status().isOk());
+    }
+
+    @Test
+    public void testSaveAsset() throws Exception {
+        Asset asset = new Asset();
+        asset.setName("My asset");
+        asset.setType("default");
+        Asset savedAsset = doPost("/api/asset", asset, Asset.class);
+
+        Assert.assertNotNull(savedAsset);
+        Assert.assertNotNull(savedAsset.getId());
+        Assert.assertTrue(savedAsset.getCreatedTime() > 0);
+        Assert.assertEquals(savedTenant.getId(), savedAsset.getTenantId());
+        Assert.assertNotNull(savedAsset.getCustomerId());
+        Assert.assertEquals(NULL_UUID, savedAsset.getCustomerId().getId());
+        Assert.assertEquals(asset.getName(), savedAsset.getName());
+
+        savedAsset.setName("My new asset");
+        doPost("/api/asset", savedAsset, Asset.class);
+
+        Asset foundAsset = doGet("/api/asset/" + savedAsset.getId().getId().toString(), Asset.class);
+        Assert.assertEquals(foundAsset.getName(), savedAsset.getName());
+    }
+
+    @Test
+    public void testFindAssetById() throws Exception {
+        Asset asset = new Asset();
+        asset.setName("My asset");
+        asset.setType("default");
+        Asset savedAsset = doPost("/api/asset", asset, Asset.class);
+        Asset foundAsset = doGet("/api/asset/" + savedAsset.getId().getId().toString(), Asset.class);
+        Assert.assertNotNull(foundAsset);
+        Assert.assertEquals(savedAsset, foundAsset);
+    }
+
+    @Test
+    public void testFindAssetTypesByTenantId() throws Exception {
+        List<Asset> assets = new ArrayList<>();
+        for (int i=0;i<3;i++) {
+            Asset asset = new Asset();
+            asset.setName("My asset B"+i);
+            asset.setType("typeB");
+            assets.add(doPost("/api/asset", asset, Asset.class));
+        }
+        for (int i=0;i<7;i++) {
+            Asset asset = new Asset();
+            asset.setName("My asset C"+i);
+            asset.setType("typeC");
+            assets.add(doPost("/api/asset", asset, Asset.class));
+        }
+        for (int i=0;i<9;i++) {
+            Asset asset = new Asset();
+            asset.setName("My asset A"+i);
+            asset.setType("typeA");
+            assets.add(doPost("/api/asset", asset, Asset.class));
+        }
+        List<TenantAssetType> assetTypes = doGetTyped("/api/asset/types",
+                new TypeReference<List<TenantAssetType>>(){});
+
+        Assert.assertNotNull(assetTypes);
+        Assert.assertEquals(3, assetTypes.size());
+        Assert.assertEquals("typeA", assetTypes.get(0).getType());
+        Assert.assertEquals("typeB", assetTypes.get(1).getType());
+        Assert.assertEquals("typeC", assetTypes.get(2).getType());
+    }
+
+    @Test
+    public void testDeleteAsset() throws Exception {
+        Asset asset = new Asset();
+        asset.setName("My asset");
+        asset.setType("default");
+        Asset savedAsset = doPost("/api/asset", asset, Asset.class);
+
+        doDelete("/api/asset/"+savedAsset.getId().getId().toString())
+                .andExpect(status().isOk());
+
+        doGet("/api/asset/"+savedAsset.getId().getId().toString())
+                .andExpect(status().isNotFound());
+    }
+
+    @Test
+    public void testSaveAssetWithEmptyType() throws Exception {
+        Asset asset = new Asset();
+        asset.setName("My asset");
+        doPost("/api/asset", asset)
+                .andExpect(status().isBadRequest())
+                .andExpect(statusReason(containsString("Asset type should be specified")));
+    }
+
+    @Test
+    public void testSaveAssetWithEmptyName() throws Exception {
+        Asset asset = new Asset();
+        asset.setType("default");
+        doPost("/api/asset", asset)
+                .andExpect(status().isBadRequest())
+                .andExpect(statusReason(containsString("Asset name should be specified")));
+    }
+
+    @Test
+    public void testAssignUnassignAssetToCustomer() throws Exception {
+        Asset asset = new Asset();
+        asset.setName("My asset");
+        asset.setType("default");
+        Asset savedAsset = doPost("/api/asset", asset, Asset.class);
+
+        Customer customer = new Customer();
+        customer.setTitle("My customer");
+        Customer savedCustomer = doPost("/api/customer", customer, Customer.class);
+
+        Asset assignedAsset = doPost("/api/customer/" + savedCustomer.getId().getId().toString()
+                + "/asset/" + savedAsset.getId().getId().toString(), Asset.class);
+        Assert.assertEquals(savedCustomer.getId(), assignedAsset.getCustomerId());
+
+        Asset foundAsset = doGet("/api/asset/" + savedAsset.getId().getId().toString(), Asset.class);
+        Assert.assertEquals(savedCustomer.getId(), foundAsset.getCustomerId());
+
+        Asset unassignedAsset =
+                doDelete("/api/customer/asset/" + savedAsset.getId().getId().toString(), Asset.class);
+        Assert.assertEquals(ModelConstants.NULL_UUID, unassignedAsset.getCustomerId().getId());
+
+        foundAsset = doGet("/api/asset/" + savedAsset.getId().getId().toString(), Asset.class);
+        Assert.assertEquals(ModelConstants.NULL_UUID, foundAsset.getCustomerId().getId());
+    }
+
+    @Test
+    public void testAssignAssetToNonExistentCustomer() throws Exception {
+        Asset asset = new Asset();
+        asset.setName("My asset");
+        asset.setType("default");
+        Asset savedAsset = doPost("/api/asset", asset, Asset.class);
+
+        doPost("/api/customer/" + UUIDs.timeBased().toString()
+                + "/asset/" + savedAsset.getId().getId().toString())
+                .andExpect(status().isNotFound());
+    }
+
+    @Test
+    public void testAssignAssetToCustomerFromDifferentTenant() throws Exception {
+        loginSysAdmin();
+
+        Tenant tenant2 = new Tenant();
+        tenant2.setTitle("Different tenant");
+        Tenant savedTenant2 = doPost("/api/tenant", tenant2, Tenant.class);
+        Assert.assertNotNull(savedTenant2);
+
+        User tenantAdmin2 = new User();
+        tenantAdmin2.setAuthority(Authority.TENANT_ADMIN);
+        tenantAdmin2.setTenantId(savedTenant2.getId());
+        tenantAdmin2.setEmail("tenant3@thingsboard.org");
+        tenantAdmin2.setFirstName("Joe");
+        tenantAdmin2.setLastName("Downs");
+
+        tenantAdmin2 = createUserAndLogin(tenantAdmin2, "testPassword1");
+
+        Customer customer = new Customer();
+        customer.setTitle("Different customer");
+        Customer savedCustomer = doPost("/api/customer", customer, Customer.class);
+
+        login(tenantAdmin.getEmail(), "testPassword1");
+
+        Asset asset = new Asset();
+        asset.setName("My asset");
+        asset.setType("default");
+        Asset savedAsset = doPost("/api/asset", asset, Asset.class);
+
+        doPost("/api/customer/" + savedCustomer.getId().getId().toString()
+                + "/asset/" + savedAsset.getId().getId().toString())
+                .andExpect(status().isForbidden());
+
+        loginSysAdmin();
+
+        doDelete("/api/tenant/"+savedTenant2.getId().getId().toString())
+                .andExpect(status().isOk());
+    }
+
+    @Test
+    public void testFindTenantAssets() throws Exception {
+        List<Asset> assets = new ArrayList<>();
+        for (int i=0;i<178;i++) {
+            Asset asset = new Asset();
+            asset.setName("Asset"+i);
+            asset.setType("default");
+            assets.add(doPost("/api/asset", asset, Asset.class));
+        }
+        List<Asset> loadedAssets = new ArrayList<>();
+        TextPageLink pageLink = new TextPageLink(23);
+        TextPageData<Asset> pageData = null;
+        do {
+            pageData = doGetTypedWithPageLink("/api/tenant/assets?",
+                    new TypeReference<TextPageData<Asset>>(){}, pageLink);
+            loadedAssets.addAll(pageData.getData());
+            if (pageData.hasNext()) {
+                pageLink = pageData.getNextPageLink();
+            }
+        } while (pageData.hasNext());
+
+        Collections.sort(assets, idComparator);
+        Collections.sort(loadedAssets, idComparator);
+
+        Assert.assertEquals(assets, loadedAssets);
+    }
+
+    @Test
+    public void testFindTenantAssetsByName() throws Exception {
+        String title1 = "Asset title 1";
+        List<Asset> assetsTitle1 = new ArrayList<>();
+        for (int i=0;i<143;i++) {
+            Asset asset = new Asset();
+            String suffix = RandomStringUtils.randomAlphanumeric(15);
+            String name = title1+suffix;
+            name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase();
+            asset.setName(name);
+            asset.setType("default");
+            assetsTitle1.add(doPost("/api/asset", asset, Asset.class));
+        }
+        String title2 = "Asset title 2";
+        List<Asset> assetsTitle2 = new ArrayList<>();
+        for (int i=0;i<75;i++) {
+            Asset asset = new Asset();
+            String suffix = RandomStringUtils.randomAlphanumeric(15);
+            String name = title2+suffix;
+            name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase();
+            asset.setName(name);
+            asset.setType("default");
+            assetsTitle2.add(doPost("/api/asset", asset, Asset.class));
+        }
+
+        List<Asset> loadedAssetsTitle1 = new ArrayList<>();
+        TextPageLink pageLink = new TextPageLink(15, title1);
+        TextPageData<Asset> pageData = null;
+        do {
+            pageData = doGetTypedWithPageLink("/api/tenant/assets?",
+                    new TypeReference<TextPageData<Asset>>(){}, pageLink);
+            loadedAssetsTitle1.addAll(pageData.getData());
+            if (pageData.hasNext()) {
+                pageLink = pageData.getNextPageLink();
+            }
+        } while (pageData.hasNext());
+
+        Collections.sort(assetsTitle1, idComparator);
+        Collections.sort(loadedAssetsTitle1, idComparator);
+
+        Assert.assertEquals(assetsTitle1, loadedAssetsTitle1);
+
+        List<Asset> loadedAssetsTitle2 = new ArrayList<>();
+        pageLink = new TextPageLink(4, title2);
+        do {
+            pageData = doGetTypedWithPageLink("/api/tenant/assets?",
+                    new TypeReference<TextPageData<Asset>>(){}, pageLink);
+            loadedAssetsTitle2.addAll(pageData.getData());
+            if (pageData.hasNext()) {
+                pageLink = pageData.getNextPageLink();
+            }
+        } while (pageData.hasNext());
+
+        Collections.sort(assetsTitle2, idComparator);
+        Collections.sort(loadedAssetsTitle2, idComparator);
+
+        Assert.assertEquals(assetsTitle2, loadedAssetsTitle2);
+
+        for (Asset asset : loadedAssetsTitle1) {
+            doDelete("/api/asset/"+asset.getId().getId().toString())
+                    .andExpect(status().isOk());
+        }
+
+        pageLink = new TextPageLink(4, title1);
+        pageData = doGetTypedWithPageLink("/api/tenant/assets?",
+                new TypeReference<TextPageData<Asset>>(){}, pageLink);
+        Assert.assertFalse(pageData.hasNext());
+        Assert.assertEquals(0, pageData.getData().size());
+
+        for (Asset asset : loadedAssetsTitle2) {
+            doDelete("/api/asset/"+asset.getId().getId().toString())
+                    .andExpect(status().isOk());
+        }
+
+        pageLink = new TextPageLink(4, title2);
+        pageData = doGetTypedWithPageLink("/api/tenant/assets?",
+                new TypeReference<TextPageData<Asset>>(){}, pageLink);
+        Assert.assertFalse(pageData.hasNext());
+        Assert.assertEquals(0, pageData.getData().size());
+    }
+
+    @Test
+    public void testFindTenantAssetsByType() throws Exception {
+        String title1 = "Asset title 1";
+        String type1 = "typeA";
+        List<Asset> assetsType1 = new ArrayList<>();
+        for (int i=0;i<143;i++) {
+            Asset asset = new Asset();
+            String suffix = RandomStringUtils.randomAlphanumeric(15);
+            String name = title1+suffix;
+            name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase();
+            asset.setName(name);
+            asset.setType(type1);
+            assetsType1.add(doPost("/api/asset", asset, Asset.class));
+        }
+        String title2 = "Asset title 2";
+        String type2 = "typeB";
+        List<Asset> assetsType2 = new ArrayList<>();
+        for (int i=0;i<75;i++) {
+            Asset asset = new Asset();
+            String suffix = RandomStringUtils.randomAlphanumeric(15);
+            String name = title2+suffix;
+            name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase();
+            asset.setName(name);
+            asset.setType(type2);
+            assetsType2.add(doPost("/api/asset", asset, Asset.class));
+        }
+
+        List<Asset> loadedAssetsType1 = new ArrayList<>();
+        TextPageLink pageLink = new TextPageLink(15);
+        TextPageData<Asset> pageData = null;
+        do {
+            pageData = doGetTypedWithPageLink("/api/tenant/assets?type={type}&",
+                    new TypeReference<TextPageData<Asset>>(){}, pageLink, type1);
+            loadedAssetsType1.addAll(pageData.getData());
+            if (pageData.hasNext()) {
+                pageLink = pageData.getNextPageLink();
+            }
+        } while (pageData.hasNext());
+
+        Collections.sort(assetsType1, idComparator);
+        Collections.sort(loadedAssetsType1, idComparator);
+
+        Assert.assertEquals(assetsType1, loadedAssetsType1);
+
+        List<Asset> loadedAssetsType2 = new ArrayList<>();
+        pageLink = new TextPageLink(4);
+        do {
+            pageData = doGetTypedWithPageLink("/api/tenant/assets?type={type}&",
+                    new TypeReference<TextPageData<Asset>>(){}, pageLink, type2);
+            loadedAssetsType2.addAll(pageData.getData());
+            if (pageData.hasNext()) {
+                pageLink = pageData.getNextPageLink();
+            }
+        } while (pageData.hasNext());
+
+        Collections.sort(assetsType2, idComparator);
+        Collections.sort(loadedAssetsType2, idComparator);
+
+        Assert.assertEquals(assetsType2, loadedAssetsType2);
+
+        for (Asset asset : loadedAssetsType1) {
+            doDelete("/api/asset/"+asset.getId().getId().toString())
+                    .andExpect(status().isOk());
+        }
+
+        pageLink = new TextPageLink(4);
+        pageData = doGetTypedWithPageLink("/api/tenant/assets?type={type}&",
+                new TypeReference<TextPageData<Asset>>(){}, pageLink, type1);
+        Assert.assertFalse(pageData.hasNext());
+        Assert.assertEquals(0, pageData.getData().size());
+
+        for (Asset asset : loadedAssetsType2) {
+            doDelete("/api/asset/"+asset.getId().getId().toString())
+                    .andExpect(status().isOk());
+        }
+
+        pageLink = new TextPageLink(4);
+        pageData = doGetTypedWithPageLink("/api/tenant/assets?type={type}&",
+                new TypeReference<TextPageData<Asset>>(){}, pageLink, type2);
+        Assert.assertFalse(pageData.hasNext());
+        Assert.assertEquals(0, pageData.getData().size());
+    }
+
+    @Test
+    public void testFindCustomerAssets() throws Exception {
+        Customer customer = new Customer();
+        customer.setTitle("Test customer");
+        customer = doPost("/api/customer", customer, Customer.class);
+        CustomerId customerId = customer.getId();
+
+        List<Asset> assets = new ArrayList<>();
+        for (int i=0;i<128;i++) {
+            Asset asset = new Asset();
+            asset.setName("Asset"+i);
+            asset.setType("default");
+            asset = doPost("/api/asset", asset, Asset.class);
+            assets.add(doPost("/api/customer/" + customerId.getId().toString()
+                    + "/asset/" + asset.getId().getId().toString(), Asset.class));
+        }
+
+        List<Asset> loadedAssets = new ArrayList<>();
+        TextPageLink pageLink = new TextPageLink(23);
+        TextPageData<Asset> pageData = null;
+        do {
+            pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/assets?",
+                    new TypeReference<TextPageData<Asset>>(){}, pageLink);
+            loadedAssets.addAll(pageData.getData());
+            if (pageData.hasNext()) {
+                pageLink = pageData.getNextPageLink();
+            }
+        } while (pageData.hasNext());
+
+        Collections.sort(assets, idComparator);
+        Collections.sort(loadedAssets, idComparator);
+
+        Assert.assertEquals(assets, loadedAssets);
+    }
+
+    @Test
+    public void testFindCustomerAssetsByName() throws Exception {
+        Customer customer = new Customer();
+        customer.setTitle("Test customer");
+        customer = doPost("/api/customer", customer, Customer.class);
+        CustomerId customerId = customer.getId();
+
+        String title1 = "Asset title 1";
+        List<Asset> assetsTitle1 = new ArrayList<>();
+        for (int i=0;i<125;i++) {
+            Asset asset = new Asset();
+            String suffix = RandomStringUtils.randomAlphanumeric(15);
+            String name = title1+suffix;
+            name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase();
+            asset.setName(name);
+            asset.setType("default");
+            asset = doPost("/api/asset", asset, Asset.class);
+            assetsTitle1.add(doPost("/api/customer/" + customerId.getId().toString()
+                    + "/asset/" + asset.getId().getId().toString(), Asset.class));
+        }
+        String title2 = "Asset title 2";
+        List<Asset> assetsTitle2 = new ArrayList<>();
+        for (int i=0;i<143;i++) {
+            Asset asset = new Asset();
+            String suffix = RandomStringUtils.randomAlphanumeric(15);
+            String name = title2+suffix;
+            name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase();
+            asset.setName(name);
+            asset.setType("default");
+            asset = doPost("/api/asset", asset, Asset.class);
+            assetsTitle2.add(doPost("/api/customer/" + customerId.getId().toString()
+                    + "/asset/" + asset.getId().getId().toString(), Asset.class));
+        }
+
+        List<Asset> loadedAssetsTitle1 = new ArrayList<>();
+        TextPageLink pageLink = new TextPageLink(15, title1);
+        TextPageData<Asset> pageData = null;
+        do {
+            pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/assets?",
+                    new TypeReference<TextPageData<Asset>>(){}, pageLink);
+            loadedAssetsTitle1.addAll(pageData.getData());
+            if (pageData.hasNext()) {
+                pageLink = pageData.getNextPageLink();
+            }
+        } while (pageData.hasNext());
+
+        Collections.sort(assetsTitle1, idComparator);
+        Collections.sort(loadedAssetsTitle1, idComparator);
+
+        Assert.assertEquals(assetsTitle1, loadedAssetsTitle1);
+
+        List<Asset> loadedAssetsTitle2 = new ArrayList<>();
+        pageLink = new TextPageLink(4, title2);
+        do {
+            pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/assets?",
+                    new TypeReference<TextPageData<Asset>>(){}, pageLink);
+            loadedAssetsTitle2.addAll(pageData.getData());
+            if (pageData.hasNext()) {
+                pageLink = pageData.getNextPageLink();
+            }
+        } while (pageData.hasNext());
+
+        Collections.sort(assetsTitle2, idComparator);
+        Collections.sort(loadedAssetsTitle2, idComparator);
+
+        Assert.assertEquals(assetsTitle2, loadedAssetsTitle2);
+
+        for (Asset asset : loadedAssetsTitle1) {
+            doDelete("/api/customer/asset/" + asset.getId().getId().toString())
+                    .andExpect(status().isOk());
+        }
+
+        pageLink = new TextPageLink(4, title1);
+        pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/assets?",
+                new TypeReference<TextPageData<Asset>>(){}, pageLink);
+        Assert.assertFalse(pageData.hasNext());
+        Assert.assertEquals(0, pageData.getData().size());
+
+        for (Asset asset : loadedAssetsTitle2) {
+            doDelete("/api/customer/asset/" + asset.getId().getId().toString())
+                    .andExpect(status().isOk());
+        }
+
+        pageLink = new TextPageLink(4, title2);
+        pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/assets?",
+                new TypeReference<TextPageData<Asset>>(){}, pageLink);
+        Assert.assertFalse(pageData.hasNext());
+        Assert.assertEquals(0, pageData.getData().size());
+    }
+
+    @Test
+    public void testFindCustomerAssetsByType() throws Exception {
+        Customer customer = new Customer();
+        customer.setTitle("Test customer");
+        customer = doPost("/api/customer", customer, Customer.class);
+        CustomerId customerId = customer.getId();
+
+        String title1 = "Asset title 1";
+        String type1 = "typeC";
+        List<Asset> assetsType1 = new ArrayList<>();
+        for (int i=0;i<125;i++) {
+            Asset asset = new Asset();
+            String suffix = RandomStringUtils.randomAlphanumeric(15);
+            String name = title1+suffix;
+            name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase();
+            asset.setName(name);
+            asset.setType(type1);
+            asset = doPost("/api/asset", asset, Asset.class);
+            assetsType1.add(doPost("/api/customer/" + customerId.getId().toString()
+                    + "/asset/" + asset.getId().getId().toString(), Asset.class));
+        }
+        String title2 = "Asset title 2";
+        String type2 = "typeD";
+        List<Asset> assetsType2 = new ArrayList<>();
+        for (int i=0;i<143;i++) {
+            Asset asset = new Asset();
+            String suffix = RandomStringUtils.randomAlphanumeric(15);
+            String name = title2+suffix;
+            name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase();
+            asset.setName(name);
+            asset.setType(type2);
+            asset = doPost("/api/asset", asset, Asset.class);
+            assetsType2.add(doPost("/api/customer/" + customerId.getId().toString()
+                    + "/asset/" + asset.getId().getId().toString(), Asset.class));
+        }
+
+        List<Asset> loadedAssetsType1 = new ArrayList<>();
+        TextPageLink pageLink = new TextPageLink(15);
+        TextPageData<Asset> pageData = null;
+        do {
+            pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/assets?type={type}&",
+                    new TypeReference<TextPageData<Asset>>(){}, pageLink, type1);
+            loadedAssetsType1.addAll(pageData.getData());
+            if (pageData.hasNext()) {
+                pageLink = pageData.getNextPageLink();
+            }
+        } while (pageData.hasNext());
+
+        Collections.sort(assetsType1, idComparator);
+        Collections.sort(loadedAssetsType1, idComparator);
+
+        Assert.assertEquals(assetsType1, loadedAssetsType1);
+
+        List<Asset> loadedAssetsType2 = new ArrayList<>();
+        pageLink = new TextPageLink(4);
+        do {
+            pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/assets?type={type}&",
+                    new TypeReference<TextPageData<Asset>>(){}, pageLink, type2);
+            loadedAssetsType2.addAll(pageData.getData());
+            if (pageData.hasNext()) {
+                pageLink = pageData.getNextPageLink();
+            }
+        } while (pageData.hasNext());
+
+        Collections.sort(assetsType2, idComparator);
+        Collections.sort(loadedAssetsType2, idComparator);
+
+        Assert.assertEquals(assetsType2, loadedAssetsType2);
+
+        for (Asset asset : loadedAssetsType1) {
+            doDelete("/api/customer/asset/" + asset.getId().getId().toString())
+                    .andExpect(status().isOk());
+        }
+
+        pageLink = new TextPageLink(4);
+        pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/assets?type={type}&",
+                new TypeReference<TextPageData<Asset>>(){}, pageLink, type1);
+        Assert.assertFalse(pageData.hasNext());
+        Assert.assertEquals(0, pageData.getData().size());
+
+        for (Asset asset : loadedAssetsType2) {
+            doDelete("/api/customer/asset/" + asset.getId().getId().toString())
+                    .andExpect(status().isOk());
+        }
+
+        pageLink = new TextPageLink(4);
+        pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/assets?type={type}&",
+                new TypeReference<TextPageData<Asset>>(){}, pageLink, type2);
+        Assert.assertFalse(pageData.hasNext());
+        Assert.assertEquals(0, pageData.getData().size());
+    }
+
+}
diff --git a/application/src/test/java/org/thingsboard/server/controller/DeviceControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/DeviceControllerTest.java
index 69bb761..5d40a79 100644
--- a/application/src/test/java/org/thingsboard/server/controller/DeviceControllerTest.java
+++ b/application/src/test/java/org/thingsboard/server/controller/DeviceControllerTest.java
@@ -24,10 +24,7 @@ import java.util.Collections;
 import java.util.List;
 
 import org.apache.commons.lang3.RandomStringUtils;
-import org.thingsboard.server.common.data.Customer;
-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.*;
 import org.thingsboard.server.common.data.id.CustomerId;
 import org.thingsboard.server.common.data.id.DeviceCredentialsId;
 import org.thingsboard.server.common.data.id.DeviceId;
@@ -83,6 +80,7 @@ public class DeviceControllerTest extends AbstractControllerTest {
     public void testSaveDevice() throws Exception {
         Device device = new Device();
         device.setName("My device");
+        device.setType("default");
         Device savedDevice = doPost("/api/device", device, Device.class);
         
         Assert.assertNotNull(savedDevice);
@@ -114,16 +112,49 @@ public class DeviceControllerTest extends AbstractControllerTest {
     public void testFindDeviceById() throws Exception {
         Device device = new Device();
         device.setName("My device");
+        device.setType("default");
         Device savedDevice = doPost("/api/device", device, Device.class);
         Device foundDevice = doGet("/api/device/" + savedDevice.getId().getId().toString(), Device.class);
         Assert.assertNotNull(foundDevice);
         Assert.assertEquals(savedDevice, foundDevice);
     }
+
+    @Test
+    public void testFindDeviceTypesByTenantId() throws Exception {
+        List<Device> devices = new ArrayList<>();
+        for (int i=0;i<3;i++) {
+            Device device = new Device();
+            device.setName("My device B"+i);
+            device.setType("typeB");
+            devices.add(doPost("/api/device", device, Device.class));
+        }
+        for (int i=0;i<7;i++) {
+            Device device = new Device();
+            device.setName("My device C"+i);
+            device.setType("typeC");
+            devices.add(doPost("/api/device", device, Device.class));
+        }
+        for (int i=0;i<9;i++) {
+            Device device = new Device();
+            device.setName("My device A"+i);
+            device.setType("typeA");
+            devices.add(doPost("/api/device", device, Device.class));
+        }
+        List<TenantDeviceType> deviceTypes = doGetTyped("/api/device/types",
+                new TypeReference<List<TenantDeviceType>>(){});
+
+        Assert.assertNotNull(deviceTypes);
+        Assert.assertEquals(3, deviceTypes.size());
+        Assert.assertEquals("typeA", deviceTypes.get(0).getType());
+        Assert.assertEquals("typeB", deviceTypes.get(1).getType());
+        Assert.assertEquals("typeC", deviceTypes.get(2).getType());
+    }
     
     @Test
     public void testDeleteDevice() throws Exception {
         Device device = new Device();
         device.setName("My device");
+        device.setType("default");
         Device savedDevice = doPost("/api/device", device, Device.class);
         
         doDelete("/api/device/"+savedDevice.getId().getId().toString())
@@ -132,10 +163,20 @@ public class DeviceControllerTest extends AbstractControllerTest {
         doGet("/api/device/"+savedDevice.getId().getId().toString())
         .andExpect(status().isNotFound());
     }
-    
+
+    @Test
+    public void testSaveDeviceWithEmptyType() throws Exception {
+        Device device = new Device();
+        device.setName("My device");
+        doPost("/api/device", device)
+                .andExpect(status().isBadRequest())
+                .andExpect(statusReason(containsString("Device type should be specified")));
+    }
+
     @Test
     public void testSaveDeviceWithEmptyName() throws Exception {
         Device device = new Device();
+        device.setType("default");
         doPost("/api/device", device)
         .andExpect(status().isBadRequest())
         .andExpect(statusReason(containsString("Device name should be specified")));
@@ -145,6 +186,7 @@ public class DeviceControllerTest extends AbstractControllerTest {
     public void testAssignUnassignDeviceToCustomer() throws Exception {
         Device device = new Device();
         device.setName("My device");
+        device.setType("default");
         Device savedDevice = doPost("/api/device", device, Device.class);
         
         Customer customer = new Customer();
@@ -170,6 +212,7 @@ public class DeviceControllerTest extends AbstractControllerTest {
     public void testAssignDeviceToNonExistentCustomer() throws Exception {
         Device device = new Device();
         device.setName("My device");
+        device.setType("default");
         Device savedDevice = doPost("/api/device", device, Device.class);
         
         doPost("/api/customer/" + UUIDs.timeBased().toString() 
@@ -203,6 +246,7 @@ public class DeviceControllerTest extends AbstractControllerTest {
         
         Device device = new Device();
         device.setName("My device");
+        device.setType("default");
         Device savedDevice = doPost("/api/device", device, Device.class);
         
         doPost("/api/customer/" + savedCustomer.getId().getId().toString()
@@ -219,6 +263,7 @@ public class DeviceControllerTest extends AbstractControllerTest {
     public void testFindDeviceCredentialsByDeviceId() throws Exception {
         Device device = new Device();
         device.setName("My device");
+        device.setType("default");
         Device savedDevice = doPost("/api/device", device, Device.class);
         DeviceCredentials deviceCredentials = 
                 doGet("/api/device/" + savedDevice.getId().getId().toString() + "/credentials", DeviceCredentials.class); 
@@ -229,6 +274,7 @@ public class DeviceControllerTest extends AbstractControllerTest {
     public void testSaveDeviceCredentials() throws Exception {
         Device device = new Device();
         device.setName("My device");
+        device.setType("default");
         Device savedDevice = doPost("/api/device", device, Device.class);
         DeviceCredentials deviceCredentials = 
                 doGet("/api/device/" + savedDevice.getId().getId().toString() + "/credentials", DeviceCredentials.class); 
@@ -255,6 +301,7 @@ public class DeviceControllerTest extends AbstractControllerTest {
     public void testSaveDeviceCredentialsWithEmptyCredentialsType() throws Exception {
         Device device = new Device();
         device.setName("My device");
+        device.setType("default");
         Device savedDevice = doPost("/api/device", device, Device.class);
         DeviceCredentials deviceCredentials = 
                 doGet("/api/device/" + savedDevice.getId().getId().toString() + "/credentials", DeviceCredentials.class);
@@ -268,6 +315,7 @@ public class DeviceControllerTest extends AbstractControllerTest {
     public void testSaveDeviceCredentialsWithEmptyCredentialsId() throws Exception {
         Device device = new Device();
         device.setName("My device");
+        device.setType("default");
         Device savedDevice = doPost("/api/device", device, Device.class);
         DeviceCredentials deviceCredentials = 
                 doGet("/api/device/" + savedDevice.getId().getId().toString() + "/credentials", DeviceCredentials.class);
@@ -281,6 +329,7 @@ public class DeviceControllerTest extends AbstractControllerTest {
     public void testSaveNonExistentDeviceCredentials() throws Exception {
         Device device = new Device();
         device.setName("My device");
+        device.setType("default");
         Device savedDevice = doPost("/api/device", device, Device.class);
         DeviceCredentials deviceCredentials = 
                 doGet("/api/device/" + savedDevice.getId().getId().toString() + "/credentials", DeviceCredentials.class);
@@ -298,6 +347,7 @@ public class DeviceControllerTest extends AbstractControllerTest {
     public void testSaveDeviceCredentialsWithNonExistentDevice() throws Exception {
         Device device = new Device();
         device.setName("My device");
+        device.setType("default");
         Device savedDevice = doPost("/api/device", device, Device.class);
         DeviceCredentials deviceCredentials = 
                 doGet("/api/device/" + savedDevice.getId().getId().toString() + "/credentials", DeviceCredentials.class);
@@ -307,9 +357,10 @@ public class DeviceControllerTest extends AbstractControllerTest {
     }
     
     @Test
-    public void testSaveDeviceCredentialsWithInvalidCredemtialsIdLength() throws Exception {
+    public void testSaveDeviceCredentialsWithInvalidCredentialsIdLength() throws Exception {
         Device device = new Device();
         device.setName("My device");
+        device.setType("default");
         Device savedDevice = doPost("/api/device", device, Device.class);
         DeviceCredentials deviceCredentials = 
                 doGet("/api/device/" + savedDevice.getId().getId().toString() + "/credentials", DeviceCredentials.class);
@@ -325,6 +376,7 @@ public class DeviceControllerTest extends AbstractControllerTest {
         for (int i=0;i<178;i++) {
             Device device = new Device();
             device.setName("Device"+i);
+            device.setType("default");
             devices.add(doPost("/api/device", device, Device.class));
         }
         List<Device> loadedDevices = new ArrayList<>();
@@ -355,6 +407,7 @@ public class DeviceControllerTest extends AbstractControllerTest {
             String name = title1+suffix;
             name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase();
             device.setName(name);
+            device.setType("default");
             devicesTitle1.add(doPost("/api/device", device, Device.class));
         }
         String title2 = "Device title 2";
@@ -365,6 +418,7 @@ public class DeviceControllerTest extends AbstractControllerTest {
             String name = title2+suffix;
             name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase();
             device.setName(name);
+            device.setType("default");
             devicesTitle2.add(doPost("/api/device", device, Device.class));
         }
         
@@ -423,6 +477,89 @@ public class DeviceControllerTest extends AbstractControllerTest {
         Assert.assertFalse(pageData.hasNext());
         Assert.assertEquals(0, pageData.getData().size());
     }
+
+    @Test
+    public void testFindTenantDevicesByType() throws Exception {
+        String title1 = "Device title 1";
+        String type1 = "typeA";
+        List<Device> devicesType1 = new ArrayList<>();
+        for (int i=0;i<143;i++) {
+            Device device = new Device();
+            String suffix = RandomStringUtils.randomAlphanumeric(15);
+            String name = title1+suffix;
+            name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase();
+            device.setName(name);
+            device.setType(type1);
+            devicesType1.add(doPost("/api/device", device, Device.class));
+        }
+        String title2 = "Device title 2";
+        String type2 = "typeB";
+        List<Device> devicesType2 = new ArrayList<>();
+        for (int i=0;i<75;i++) {
+            Device device = new Device();
+            String suffix = RandomStringUtils.randomAlphanumeric(15);
+            String name = title2+suffix;
+            name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase();
+            device.setName(name);
+            device.setType(type2);
+            devicesType2.add(doPost("/api/device", device, Device.class));
+        }
+
+        List<Device> loadedDevicesType1 = new ArrayList<>();
+        TextPageLink pageLink = new TextPageLink(15);
+        TextPageData<Device> pageData = null;
+        do {
+            pageData = doGetTypedWithPageLink("/api/tenant/devices?type={type}&",
+                    new TypeReference<TextPageData<Device>>(){}, pageLink, type1);
+            loadedDevicesType1.addAll(pageData.getData());
+            if (pageData.hasNext()) {
+                pageLink = pageData.getNextPageLink();
+            }
+        } while (pageData.hasNext());
+
+        Collections.sort(devicesType1, idComparator);
+        Collections.sort(loadedDevicesType1, idComparator);
+
+        Assert.assertEquals(devicesType1, loadedDevicesType1);
+
+        List<Device> loadedDevicesType2 = new ArrayList<>();
+        pageLink = new TextPageLink(4);
+        do {
+            pageData = doGetTypedWithPageLink("/api/tenant/devices?type={type}&",
+                    new TypeReference<TextPageData<Device>>(){}, pageLink, type2);
+            loadedDevicesType2.addAll(pageData.getData());
+            if (pageData.hasNext()) {
+                pageLink = pageData.getNextPageLink();
+            }
+        } while (pageData.hasNext());
+
+        Collections.sort(devicesType2, idComparator);
+        Collections.sort(loadedDevicesType2, idComparator);
+
+        Assert.assertEquals(devicesType2, loadedDevicesType2);
+
+        for (Device device : loadedDevicesType1) {
+            doDelete("/api/device/"+device.getId().getId().toString())
+                    .andExpect(status().isOk());
+        }
+
+        pageLink = new TextPageLink(4);
+        pageData = doGetTypedWithPageLink("/api/tenant/devices?type={type}&",
+                new TypeReference<TextPageData<Device>>(){}, pageLink, type1);
+        Assert.assertFalse(pageData.hasNext());
+        Assert.assertEquals(0, pageData.getData().size());
+
+        for (Device device : loadedDevicesType2) {
+            doDelete("/api/device/"+device.getId().getId().toString())
+                    .andExpect(status().isOk());
+        }
+
+        pageLink = new TextPageLink(4);
+        pageData = doGetTypedWithPageLink("/api/tenant/devices?type={type}&",
+                new TypeReference<TextPageData<Device>>(){}, pageLink, type2);
+        Assert.assertFalse(pageData.hasNext());
+        Assert.assertEquals(0, pageData.getData().size());
+    }
     
     @Test
     public void testFindCustomerDevices() throws Exception {
@@ -435,6 +572,7 @@ public class DeviceControllerTest extends AbstractControllerTest {
         for (int i=0;i<128;i++) {
             Device device = new Device();
             device.setName("Device"+i);
+            device.setType("default");
             device = doPost("/api/device", device, Device.class);
             devices.add(doPost("/api/customer/" + customerId.getId().toString() 
                             + "/device/" + device.getId().getId().toString(), Device.class));
@@ -473,6 +611,7 @@ public class DeviceControllerTest extends AbstractControllerTest {
             String name = title1+suffix;
             name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase();
             device.setName(name);
+            device.setType("default");
             device = doPost("/api/device", device, Device.class);
             devicesTitle1.add(doPost("/api/customer/" + customerId.getId().toString() 
                     + "/device/" + device.getId().getId().toString(), Device.class));
@@ -485,6 +624,7 @@ public class DeviceControllerTest extends AbstractControllerTest {
             String name = title2+suffix;
             name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase();
             device.setName(name);
+            device.setType("default");
             device = doPost("/api/device", device, Device.class);
             devicesTitle2.add(doPost("/api/customer/" + customerId.getId().toString() 
                     + "/device/" + device.getId().getId().toString(), Device.class));
@@ -546,4 +686,96 @@ public class DeviceControllerTest extends AbstractControllerTest {
         Assert.assertEquals(0, pageData.getData().size());
     }
 
+    @Test
+    public void testFindCustomerDevicesByType() throws Exception {
+        Customer customer = new Customer();
+        customer.setTitle("Test customer");
+        customer = doPost("/api/customer", customer, Customer.class);
+        CustomerId customerId = customer.getId();
+
+        String title1 = "Device title 1";
+        String type1 = "typeC";
+        List<Device> devicesType1 = new ArrayList<>();
+        for (int i=0;i<125;i++) {
+            Device device = new Device();
+            String suffix = RandomStringUtils.randomAlphanumeric(15);
+            String name = title1+suffix;
+            name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase();
+            device.setName(name);
+            device.setType(type1);
+            device = doPost("/api/device", device, Device.class);
+            devicesType1.add(doPost("/api/customer/" + customerId.getId().toString()
+                    + "/device/" + device.getId().getId().toString(), Device.class));
+        }
+        String title2 = "Device title 2";
+        String type2 = "typeD";
+        List<Device> devicesType2 = new ArrayList<>();
+        for (int i=0;i<143;i++) {
+            Device device = new Device();
+            String suffix = RandomStringUtils.randomAlphanumeric(15);
+            String name = title2+suffix;
+            name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase();
+            device.setName(name);
+            device.setType(type2);
+            device = doPost("/api/device", device, Device.class);
+            devicesType2.add(doPost("/api/customer/" + customerId.getId().toString()
+                    + "/device/" + device.getId().getId().toString(), Device.class));
+        }
+
+        List<Device> loadedDevicesType1 = new ArrayList<>();
+        TextPageLink pageLink = new TextPageLink(15);
+        TextPageData<Device> pageData = null;
+        do {
+            pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/devices?type={type}&",
+                    new TypeReference<TextPageData<Device>>(){}, pageLink, type1);
+            loadedDevicesType1.addAll(pageData.getData());
+            if (pageData.hasNext()) {
+                pageLink = pageData.getNextPageLink();
+            }
+        } while (pageData.hasNext());
+
+        Collections.sort(devicesType1, idComparator);
+        Collections.sort(loadedDevicesType1, idComparator);
+
+        Assert.assertEquals(devicesType1, loadedDevicesType1);
+
+        List<Device> loadedDevicesType2 = new ArrayList<>();
+        pageLink = new TextPageLink(4);
+        do {
+            pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/devices?type={type}&",
+                    new TypeReference<TextPageData<Device>>(){}, pageLink, type2);
+            loadedDevicesType2.addAll(pageData.getData());
+            if (pageData.hasNext()) {
+                pageLink = pageData.getNextPageLink();
+            }
+        } while (pageData.hasNext());
+
+        Collections.sort(devicesType2, idComparator);
+        Collections.sort(loadedDevicesType2, idComparator);
+
+        Assert.assertEquals(devicesType2, loadedDevicesType2);
+
+        for (Device device : loadedDevicesType1) {
+            doDelete("/api/customer/device/" + device.getId().getId().toString())
+                    .andExpect(status().isOk());
+        }
+
+        pageLink = new TextPageLink(4);
+        pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/devices?type={type}&",
+                new TypeReference<TextPageData<Device>>(){}, pageLink, type1);
+        Assert.assertFalse(pageData.hasNext());
+        Assert.assertEquals(0, pageData.getData().size());
+
+        for (Device device : loadedDevicesType2) {
+            doDelete("/api/customer/device/" + device.getId().getId().toString())
+                    .andExpect(status().isOk());
+        }
+
+        pageLink = new TextPageLink(4);
+        pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/devices?type={type}&",
+                new TypeReference<TextPageData<Device>>(){}, pageLink, type2);
+        Assert.assertFalse(pageData.hasNext());
+        Assert.assertEquals(0, pageData.getData().size());
+    }
+
 }
diff --git a/application/src/test/java/org/thingsboard/server/controller/TenantControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/TenantControllerTest.java
index 5d72076..dc4a422 100644
--- a/application/src/test/java/org/thingsboard/server/controller/TenantControllerTest.java
+++ b/application/src/test/java/org/thingsboard/server/controller/TenantControllerTest.java
@@ -130,7 +130,7 @@ public class TenantControllerTest extends AbstractControllerTest {
         Assert.assertEquals(tenants, loadedTenants);
         
         for (Tenant tenant : loadedTenants) {
-            if (!tenant.getTitle().equals("Tenant")) {
+            if (!tenant.getTitle().equals(TEST_TENANT_NAME)) {
                 doDelete("/api/tenant/"+tenant.getId().getId().toString())
                 .andExpect(status().isOk());        
             }
diff --git a/application/src/test/java/org/thingsboard/server/controller/UserControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/UserControllerTest.java
index 7c00049..4fafd61 100644
--- a/application/src/test/java/org/thingsboard/server/controller/UserControllerTest.java
+++ b/application/src/test/java/org/thingsboard/server/controller/UserControllerTest.java
@@ -182,7 +182,7 @@ public class UserControllerTest extends AbstractControllerTest {
         Tenant savedTenant = doPost("/api/tenant", tenant, Tenant.class);
         Assert.assertNotNull(savedTenant);
         
-        String email = "tenant@thingsboard.org";
+        String email = TENANT_ADMIN_EMAIL;
         User user = new User();
         user.setAuthority(Authority.TENANT_ADMIN);
         user.setTenantId(savedTenant.getId());
diff --git a/application/src/test/java/org/thingsboard/server/system/HttpDeviceApiTest.java b/application/src/test/java/org/thingsboard/server/system/HttpDeviceApiTest.java
index b65a5a6..af8b50f 100644
--- a/application/src/test/java/org/thingsboard/server/system/HttpDeviceApiTest.java
+++ b/application/src/test/java/org/thingsboard/server/system/HttpDeviceApiTest.java
@@ -47,6 +47,7 @@ public class HttpDeviceApiTest extends AbstractControllerTest {
         loginTenantAdmin();
         device = new Device();
         device.setName("My device");
+        device.setType("default");
         device = doPost("/api/device", device, Device.class);
 
         deviceCredentials =
diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/asset/TenantAssetType.java b/common/data/src/main/java/org/thingsboard/server/common/data/asset/TenantAssetType.java
new file mode 100644
index 0000000..bd23f2a
--- /dev/null
+++ b/common/data/src/main/java/org/thingsboard/server/common/data/asset/TenantAssetType.java
@@ -0,0 +1,79 @@
+/**
+ * Copyright © 2016-2017 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.thingsboard.server.common.data.asset;
+
+import org.thingsboard.server.common.data.id.TenantId;
+
+public class TenantAssetType {
+
+    private static final long serialVersionUID = 8057290243855622101L;
+
+    private String type;
+    private TenantId tenantId;
+
+    public TenantAssetType() {
+        super();
+    }
+
+    public TenantAssetType(String type, TenantId tenantId) {
+        this.type = type;
+        this.tenantId = tenantId;
+    }
+
+    public String getType() {
+        return type;
+    }
+
+    public void setType(String type) {
+        this.type = type;
+    }
+
+    public TenantId getTenantId() {
+        return tenantId;
+    }
+
+    public void setTenantId(TenantId tenantId) {
+        this.tenantId = tenantId;
+    }
+
+    @Override
+    public int hashCode() {
+        int result = type != null ? type.hashCode() : 0;
+        result = 31 * result + (tenantId != null ? tenantId.hashCode() : 0);
+        return result;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+
+        TenantAssetType that = (TenantAssetType) o;
+
+        if (type != null ? !type.equals(that.type) : that.type != null) return false;
+        return tenantId != null ? tenantId.equals(that.tenantId) : that.tenantId == null;
+
+    }
+
+    @Override
+    public String toString() {
+        final StringBuilder sb = new StringBuilder("TenantAssetType{");
+        sb.append("type='").append(type).append('\'');
+        sb.append(", tenantId=").append(tenantId);
+        sb.append('}');
+        return sb.toString();
+    }
+}
diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/TenantDeviceType.java b/common/data/src/main/java/org/thingsboard/server/common/data/TenantDeviceType.java
new file mode 100644
index 0000000..d611a25
--- /dev/null
+++ b/common/data/src/main/java/org/thingsboard/server/common/data/TenantDeviceType.java
@@ -0,0 +1,79 @@
+/**
+ * Copyright © 2016-2017 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.thingsboard.server.common.data;
+
+import org.thingsboard.server.common.data.id.TenantId;
+
+public class TenantDeviceType {
+
+    private static final long serialVersionUID = 8057240243859922101L;
+
+    private String type;
+    private TenantId tenantId;
+
+    public TenantDeviceType() {
+        super();
+    }
+
+    public TenantDeviceType(String type, TenantId tenantId) {
+        this.type = type;
+        this.tenantId = tenantId;
+    }
+
+    public String getType() {
+        return type;
+    }
+
+    public void setType(String type) {
+        this.type = type;
+    }
+
+    public TenantId getTenantId() {
+        return tenantId;
+    }
+
+    public void setTenantId(TenantId tenantId) {
+        this.tenantId = tenantId;
+    }
+
+    @Override
+    public int hashCode() {
+        int result = type != null ? type.hashCode() : 0;
+        result = 31 * result + (tenantId != null ? tenantId.hashCode() : 0);
+        return result;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+
+        TenantDeviceType that = (TenantDeviceType) o;
+
+        if (type != null ? !type.equals(that.type) : that.type != null) return false;
+        return tenantId != null ? tenantId.equals(that.tenantId) : that.tenantId == null;
+
+    }
+
+    @Override
+    public String toString() {
+        final StringBuilder sb = new StringBuilder("TenantDeviceType{");
+        sb.append("type='").append(type).append('\'');
+        sb.append(", tenantId=").append(tenantId);
+        sb.append('}');
+        return sb.toString();
+    }
+}
diff --git a/dao/src/main/java/org/thingsboard/server/dao/asset/AssetDao.java b/dao/src/main/java/org/thingsboard/server/dao/asset/AssetDao.java
index 81f95b4..c2ffd1f 100644
--- a/dao/src/main/java/org/thingsboard/server/dao/asset/AssetDao.java
+++ b/dao/src/main/java/org/thingsboard/server/dao/asset/AssetDao.java
@@ -16,12 +16,11 @@
 package org.thingsboard.server.dao.asset;
 
 import com.google.common.util.concurrent.ListenableFuture;
-import org.thingsboard.server.common.data.Device;
 import org.thingsboard.server.common.data.asset.Asset;
 import org.thingsboard.server.common.data.page.TextPageLink;
 import org.thingsboard.server.dao.Dao;
 import org.thingsboard.server.dao.model.AssetEntity;
-import org.thingsboard.server.dao.model.DeviceEntity;
+import org.thingsboard.server.dao.model.TenantAssetTypeEntity;
 
 import java.util.List;
 import java.util.Optional;
@@ -51,6 +50,16 @@ public interface AssetDao extends Dao<AssetEntity> {
     List<AssetEntity> findAssetsByTenantId(UUID tenantId, TextPageLink pageLink);
 
     /**
+     * Find assets by tenantId, type and page link.
+     *
+     * @param tenantId the tenantId
+     * @param type the type
+     * @param pageLink the page link
+     * @return the list of asset objects
+     */
+    List<AssetEntity> findAssetsByTenantIdAndType(UUID tenantId, String type, TextPageLink pageLink);
+
+    /**
      * Find assets by tenantId and assets Ids.
      *
      * @param tenantId the tenantId
@@ -70,6 +79,17 @@ public interface AssetDao extends Dao<AssetEntity> {
     List<AssetEntity> findAssetsByTenantIdAndCustomerId(UUID tenantId, UUID customerId, TextPageLink pageLink);
 
     /**
+     * Find assets by tenantId, customerId, type and page link.
+     *
+     * @param tenantId the tenantId
+     * @param customerId the customerId
+     * @param type the type
+     * @param pageLink the page link
+     * @return the list of asset objects
+     */
+    List<AssetEntity> findAssetsByTenantIdAndCustomerIdAndType(UUID tenantId, UUID customerId, String type, TextPageLink pageLink);
+
+    /**
      * Find assets by tenantId, customerId and assets Ids.
      *
      * @param tenantId the tenantId
@@ -87,4 +107,12 @@ public interface AssetDao extends Dao<AssetEntity> {
      * @return the optional asset object
      */
     Optional<AssetEntity> findAssetsByTenantIdAndName(UUID tenantId, String name);
+
+    /**
+     * Find tenants asset types.
+     *
+     * @return the list of tenant asset type objects
+     */
+    ListenableFuture<List<TenantAssetTypeEntity>> findTenantAssetTypesAsync();
+
 }
diff --git a/dao/src/main/java/org/thingsboard/server/dao/asset/AssetDaoImpl.java b/dao/src/main/java/org/thingsboard/server/dao/asset/AssetDaoImpl.java
index ed08c6d..5dba18b 100644
--- a/dao/src/main/java/org/thingsboard/server/dao/asset/AssetDaoImpl.java
+++ b/dao/src/main/java/org/thingsboard/server/dao/asset/AssetDaoImpl.java
@@ -15,7 +15,12 @@
  */
 package org.thingsboard.server.dao.asset;
 
+import com.datastax.driver.core.ResultSet;
+import com.datastax.driver.core.ResultSetFuture;
 import com.datastax.driver.core.querybuilder.Select;
+import com.datastax.driver.mapping.Result;
+import com.google.common.base.Function;
+import com.google.common.util.concurrent.Futures;
 import com.google.common.util.concurrent.ListenableFuture;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.stereotype.Component;
@@ -23,7 +28,9 @@ import org.thingsboard.server.common.data.asset.Asset;
 import org.thingsboard.server.common.data.page.TextPageLink;
 import org.thingsboard.server.dao.AbstractSearchTextDao;
 import org.thingsboard.server.dao.model.AssetEntity;
+import org.thingsboard.server.dao.model.TenantAssetTypeEntity;
 
+import javax.annotation.Nullable;
 import java.util.*;
 
 import static com.datastax.driver.core.querybuilder.QueryBuilder.*;
@@ -60,6 +67,16 @@ public class AssetDaoImpl extends AbstractSearchTextDao<AssetEntity> implements 
     }
 
     @Override
+    public List<AssetEntity> findAssetsByTenantIdAndType(UUID tenantId, String type, TextPageLink pageLink) {
+        log.debug("Try to find assets by tenantId [{}], type [{}] and pageLink [{}]", tenantId, type, pageLink);
+        List<AssetEntity> assetEntities = findPageWithTextSearch(ASSET_BY_TENANT_BY_TYPE_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME,
+                Arrays.asList(eq(ASSET_TYPE_PROPERTY, type),
+                        eq(ASSET_TENANT_ID_PROPERTY, tenantId)), pageLink);
+        log.trace("Found assets [{}] by tenantId [{}], type [{}] and pageLink [{}]", assetEntities, tenantId, type, pageLink);
+        return assetEntities;
+    }
+
+    @Override
     public ListenableFuture<List<AssetEntity>> findAssetsByTenantIdAndIdsAsync(UUID tenantId, List<UUID> assetIds) {
         log.debug("Try to find assets by tenantId [{}] and asset Ids [{}]", tenantId, assetIds);
         Select select = select().from(getColumnFamilyName());
@@ -82,6 +99,19 @@ public class AssetDaoImpl extends AbstractSearchTextDao<AssetEntity> implements 
     }
 
     @Override
+    public List<AssetEntity> findAssetsByTenantIdAndCustomerIdAndType(UUID tenantId, UUID customerId, String type, TextPageLink pageLink) {
+        log.debug("Try to find assets by tenantId [{}], customerId [{}], type [{}] and pageLink [{}]", tenantId, customerId, type, pageLink);
+        List<AssetEntity> assetEntities = findPageWithTextSearch(ASSET_BY_CUSTOMER_BY_TYPE_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME,
+                Arrays.asList(eq(ASSET_TYPE_PROPERTY, type),
+                        eq(ASSET_CUSTOMER_ID_PROPERTY, customerId),
+                        eq(ASSET_TENANT_ID_PROPERTY, tenantId)),
+                pageLink);
+
+        log.trace("Found assets [{}] by tenantId [{}], customerId [{}], type [{}] and pageLink [{}]", assetEntities, tenantId, customerId, type, pageLink);
+        return assetEntities;
+    }
+
+    @Override
     public ListenableFuture<List<AssetEntity>> findAssetsByTenantIdCustomerIdAndIdsAsync(UUID tenantId, UUID customerId, List<UUID> assetIds) {
         log.debug("Try to find assets by tenantId [{}], customerId [{}] and asset Ids [{}]", tenantId, customerId, assetIds);
         Select select = select().from(getColumnFamilyName());
@@ -101,4 +131,24 @@ public class AssetDaoImpl extends AbstractSearchTextDao<AssetEntity> implements 
         return Optional.ofNullable(findOneByStatement(query));
     }
 
+    @Override
+    public ListenableFuture<List<TenantAssetTypeEntity>> findTenantAssetTypesAsync() {
+        Select statement = select().distinct().column(ASSET_TYPE_PROPERTY).column(ASSET_TENANT_ID_PROPERTY).from(ASSET_TYPES_BY_TENANT_VIEW_NAME);
+        statement.setConsistencyLevel(cluster.getDefaultReadConsistencyLevel());
+        ResultSetFuture resultSetFuture = getSession().executeAsync(statement);
+        ListenableFuture<List<TenantAssetTypeEntity>> result = Futures.transform(resultSetFuture, new Function<ResultSet, List<TenantAssetTypeEntity>>() {
+            @Nullable
+            @Override
+            public List<TenantAssetTypeEntity> apply(@Nullable ResultSet resultSet) {
+                Result<TenantAssetTypeEntity> result = cluster.getMapper(TenantAssetTypeEntity.class).map(resultSet);
+                if (result != null) {
+                    return result.all();
+                } else {
+                    return Collections.emptyList();
+                }
+            }
+        });
+        return result;
+    }
+
 }
diff --git a/dao/src/main/java/org/thingsboard/server/dao/asset/AssetService.java b/dao/src/main/java/org/thingsboard/server/dao/asset/AssetService.java
index 7a61bd8..25baeda 100644
--- a/dao/src/main/java/org/thingsboard/server/dao/asset/AssetService.java
+++ b/dao/src/main/java/org/thingsboard/server/dao/asset/AssetService.java
@@ -17,6 +17,7 @@ package org.thingsboard.server.dao.asset;
 
 import com.google.common.util.concurrent.ListenableFuture;
 import org.thingsboard.server.common.data.asset.Asset;
+import org.thingsboard.server.common.data.asset.TenantAssetType;
 import org.thingsboard.server.common.data.id.AssetId;
 import org.thingsboard.server.common.data.id.CustomerId;
 import org.thingsboard.server.common.data.id.TenantId;
@@ -34,7 +35,7 @@ public interface AssetService {
 
     Optional<Asset> findAssetByTenantIdAndName(TenantId tenantId, String name);
 
-    Asset saveAsset(Asset device);
+    Asset saveAsset(Asset asset);
 
     Asset assignAssetToCustomer(AssetId assetId, CustomerId customerId);
 
@@ -44,16 +45,21 @@ public interface AssetService {
 
     TextPageData<Asset> findAssetsByTenantId(TenantId tenantId, TextPageLink pageLink);
 
+    TextPageData<Asset> findAssetsByTenantIdAndType(TenantId tenantId, String type, TextPageLink pageLink);
+
     ListenableFuture<List<Asset>> findAssetsByTenantIdAndIdsAsync(TenantId tenantId, List<AssetId> assetIds);
 
     void deleteAssetsByTenantId(TenantId tenantId);
 
     TextPageData<Asset> findAssetsByTenantIdAndCustomerId(TenantId tenantId, CustomerId customerId, TextPageLink pageLink);
 
+    TextPageData<Asset> findAssetsByTenantIdAndCustomerIdAndType(TenantId tenantId, CustomerId customerId, String type, TextPageLink pageLink);
+
     ListenableFuture<List<Asset>> findAssetsByTenantIdCustomerIdAndIdsAsync(TenantId tenantId, CustomerId customerId, List<AssetId> assetIds);
 
     void unassignCustomerAssets(TenantId tenantId, CustomerId customerId);
 
     ListenableFuture<List<Asset>> findAssetsByQuery(AssetSearchQuery query);
 
+    ListenableFuture<List<TenantAssetType>> findAssetTypesByTenantId(TenantId tenantId);
 }
diff --git a/dao/src/main/java/org/thingsboard/server/dao/asset/BaseAssetService.java b/dao/src/main/java/org/thingsboard/server/dao/asset/BaseAssetService.java
index c63e5d7..fce0c5e 100644
--- a/dao/src/main/java/org/thingsboard/server/dao/asset/BaseAssetService.java
+++ b/dao/src/main/java/org/thingsboard/server/dao/asset/BaseAssetService.java
@@ -26,6 +26,7 @@ import org.springframework.stereotype.Service;
 import org.springframework.util.StringUtils;
 import org.thingsboard.server.common.data.EntityType;
 import org.thingsboard.server.common.data.asset.Asset;
+import org.thingsboard.server.common.data.asset.TenantAssetType;
 import org.thingsboard.server.common.data.id.AssetId;
 import org.thingsboard.server.common.data.id.CustomerId;
 import org.thingsboard.server.common.data.id.EntityId;
@@ -36,10 +37,7 @@ import org.thingsboard.server.common.data.relation.EntityRelation;
 import org.thingsboard.server.dao.customer.CustomerDao;
 import org.thingsboard.server.dao.entity.BaseEntityService;
 import org.thingsboard.server.dao.exception.DataValidationException;
-import org.thingsboard.server.dao.model.AssetEntity;
-import org.thingsboard.server.dao.model.CustomerEntity;
-import org.thingsboard.server.dao.model.TenantEntity;
-import org.thingsboard.server.dao.relation.EntityRelationsQuery;
+import org.thingsboard.server.dao.model.*;
 import org.thingsboard.server.dao.relation.EntitySearchDirection;
 import org.thingsboard.server.dao.service.DataValidator;
 import org.thingsboard.server.dao.service.PaginatedRemover;
@@ -132,7 +130,18 @@ public class BaseAssetService extends BaseEntityService implements AssetService 
         validatePageLink(pageLink, "Incorrect page link " + pageLink);
         List<AssetEntity> assetEntities = assetDao.findAssetsByTenantId(tenantId.getId(), pageLink);
         List<Asset> assets = convertDataList(assetEntities);
-        return new TextPageData<Asset>(assets, pageLink);
+        return new TextPageData<>(assets, pageLink);
+    }
+
+    @Override
+    public TextPageData<Asset> findAssetsByTenantIdAndType(TenantId tenantId, String type, TextPageLink pageLink) {
+        log.trace("Executing findAssetsByTenantIdAndType, tenantId [{}], type [{}], pageLink [{}]", tenantId, type, pageLink);
+        validateId(tenantId, "Incorrect tenantId " + tenantId);
+        validateString(type, "Incorrect type " + type);
+        validatePageLink(pageLink, "Incorrect page link " + pageLink);
+        List<AssetEntity> assetEntities = assetDao.findAssetsByTenantIdAndType(tenantId.getId(), type, pageLink);
+        List<Asset> assets = convertDataList(assetEntities);
+        return new TextPageData<>(assets, pageLink);
     }
 
     @Override
@@ -159,7 +168,19 @@ public class BaseAssetService extends BaseEntityService implements AssetService 
         validatePageLink(pageLink, "Incorrect page link " + pageLink);
         List<AssetEntity> assetEntities = assetDao.findAssetsByTenantIdAndCustomerId(tenantId.getId(), customerId.getId(), pageLink);
         List<Asset> assets = convertDataList(assetEntities);
-        return new TextPageData<Asset>(assets, pageLink);
+        return new TextPageData<>(assets, pageLink);
+    }
+
+    @Override
+    public TextPageData<Asset> findAssetsByTenantIdAndCustomerIdAndType(TenantId tenantId, CustomerId customerId, String type, TextPageLink pageLink) {
+        log.trace("Executing findAssetsByTenantIdAndCustomerIdAndType, tenantId [{}], customerId [{}], type [{}], pageLink [{}]", tenantId, customerId, type, pageLink);
+        validateId(tenantId, "Incorrect tenantId " + tenantId);
+        validateId(customerId, "Incorrect customerId " + customerId);
+        validateString(type, "Incorrect type " + type);
+        validatePageLink(pageLink, "Incorrect page link " + pageLink);
+        List<AssetEntity> assetEntities = assetDao.findAssetsByTenantIdAndCustomerIdAndType(tenantId.getId(), customerId.getId(), type, pageLink);
+        List<Asset> assets = convertDataList(assetEntities);
+        return new TextPageData<>(assets, pageLink);
     }
 
     @Override
@@ -207,6 +228,25 @@ public class BaseAssetService extends BaseEntityService implements AssetService 
         return assets;
     }
 
+    @Override
+    public ListenableFuture<List<TenantAssetType>> findAssetTypesByTenantId(TenantId tenantId) {
+        log.trace("Executing findAssetTypesByTenantId, tenantId [{}]", tenantId);
+        validateId(tenantId, "Incorrect tenantId " + tenantId);
+        ListenableFuture<List<TenantAssetTypeEntity>> tenantAssetTypeEntities = assetDao.findTenantAssetTypesAsync();
+        ListenableFuture<List<TenantAssetType>> tenantAssetTypes = Futures.transform(tenantAssetTypeEntities,
+                (Function<List<TenantAssetTypeEntity>, List<TenantAssetType>>) assetTypeEntities -> {
+                    List<TenantAssetType> assetTypes = new ArrayList<>();
+                    for (TenantAssetTypeEntity assetTypeEntity : assetTypeEntities) {
+                        if (assetTypeEntity.getTenantId().equals(tenantId.getId())) {
+                            assetTypes.add(assetTypeEntity.toTenantAssetType());
+                        }
+                    }
+                    assetTypes.sort((TenantAssetType o1, TenantAssetType o2) -> o1.getType().compareTo(o2.getType()));
+                    return assetTypes;
+                });
+        return tenantAssetTypes;
+    }
+
     private DataValidator<Asset> assetValidator =
             new DataValidator<Asset>() {
 
@@ -232,6 +272,9 @@ public class BaseAssetService extends BaseEntityService implements AssetService 
 
                 @Override
                 protected void validateDataImpl(Asset asset) {
+                    if (StringUtils.isEmpty(asset.getType())) {
+                        throw new DataValidationException("Asset type should be specified!");
+                    }
                     if (StringUtils.isEmpty(asset.getName())) {
                         throw new DataValidationException("Asset name should be specified!");
                     }
diff --git a/dao/src/main/java/org/thingsboard/server/dao/device/DeviceDao.java b/dao/src/main/java/org/thingsboard/server/dao/device/DeviceDao.java
index b8d395c..241759c 100644
--- a/dao/src/main/java/org/thingsboard/server/dao/device/DeviceDao.java
+++ b/dao/src/main/java/org/thingsboard/server/dao/device/DeviceDao.java
@@ -24,6 +24,7 @@ import org.thingsboard.server.common.data.Device;
 import org.thingsboard.server.common.data.page.TextPageLink;
 import org.thingsboard.server.dao.Dao;
 import org.thingsboard.server.dao.model.DeviceEntity;
+import org.thingsboard.server.dao.model.TenantDeviceTypeEntity;
 
 /**
  * The Interface DeviceDao.
@@ -49,6 +50,16 @@ public interface DeviceDao extends Dao<DeviceEntity> {
     List<DeviceEntity> findDevicesByTenantId(UUID tenantId, TextPageLink pageLink);
 
     /**
+     * Find devices by tenantId, type and page link.
+     *
+     * @param tenantId the tenantId
+     * @param type the type
+     * @param pageLink the page link
+     * @return the list of device objects
+     */
+    List<DeviceEntity> findDevicesByTenantIdAndType(UUID tenantId, String type, TextPageLink pageLink);
+
+    /**
      * Find devices by tenantId and devices Ids.
      *
      * @param tenantId the tenantId
@@ -68,6 +79,18 @@ public interface DeviceDao extends Dao<DeviceEntity> {
     List<DeviceEntity> findDevicesByTenantIdAndCustomerId(UUID tenantId, UUID customerId, TextPageLink pageLink);
 
     /**
+     * Find devices by tenantId, customerId, type and page link.
+     *
+     * @param tenantId the tenantId
+     * @param customerId the customerId
+     * @param type the type
+     * @param pageLink the page link
+     * @return the list of device objects
+     */
+    List<DeviceEntity> findDevicesByTenantIdAndCustomerIdAndType(UUID tenantId, UUID customerId, String type, TextPageLink pageLink);
+
+
+    /**
      * Find devices by tenantId, customerId and devices Ids.
      *
      * @param tenantId the tenantId
@@ -85,4 +108,11 @@ public interface DeviceDao extends Dao<DeviceEntity> {
      * @return the optional device object
      */
     Optional<DeviceEntity> findDevicesByTenantIdAndName(UUID tenantId, String name);
+
+    /**
+     * Find tenants device types.
+     *
+     * @return the list of tenant device type objects
+     */
+    ListenableFuture<List<TenantDeviceTypeEntity>> findTenantDeviceTypesAsync();
 }
diff --git a/dao/src/main/java/org/thingsboard/server/dao/device/DeviceDaoImpl.java b/dao/src/main/java/org/thingsboard/server/dao/device/DeviceDaoImpl.java
index 81fc0bc..590f7b7 100644
--- a/dao/src/main/java/org/thingsboard/server/dao/device/DeviceDaoImpl.java
+++ b/dao/src/main/java/org/thingsboard/server/dao/device/DeviceDaoImpl.java
@@ -22,7 +22,12 @@ import static org.thingsboard.server.dao.model.ModelConstants.*;
 
 import java.util.*;
 
+import com.datastax.driver.core.ResultSet;
+import com.datastax.driver.core.ResultSetFuture;
 import com.datastax.driver.core.querybuilder.Select;
+import com.datastax.driver.mapping.Result;
+import com.google.common.base.Function;
+import com.google.common.util.concurrent.Futures;
 import com.google.common.util.concurrent.ListenableFuture;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.stereotype.Component;
@@ -32,6 +37,9 @@ import org.thingsboard.server.dao.AbstractSearchTextDao;
 import org.thingsboard.server.dao.model.DeviceEntity;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
+import org.thingsboard.server.dao.model.TenantDeviceTypeEntity;
+
+import javax.annotation.Nullable;
 
 @Component
 @Slf4j
@@ -64,6 +72,16 @@ public class DeviceDaoImpl extends AbstractSearchTextDao<DeviceEntity> implement
     }
 
     @Override
+    public List<DeviceEntity> findDevicesByTenantIdAndType(UUID tenantId, String type, TextPageLink pageLink) {
+        log.debug("Try to find devices by tenantId [{}], type [{}] and pageLink [{}]", tenantId, type, pageLink);
+        List<DeviceEntity> deviceEntities = findPageWithTextSearch(DEVICE_BY_TENANT_BY_TYPE_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME,
+                Arrays.asList(eq(DEVICE_TYPE_PROPERTY, type),
+                              eq(DEVICE_TENANT_ID_PROPERTY, tenantId)), pageLink);
+        log.trace("Found devices [{}] by tenantId [{}], type [{}] and pageLink [{}]", deviceEntities, tenantId, type, pageLink);
+        return deviceEntities;
+    }
+
+    @Override
     public ListenableFuture<List<DeviceEntity>> findDevicesByTenantIdAndIdsAsync(UUID tenantId, List<UUID> deviceIds) {
         log.debug("Try to find devices by tenantId [{}] and device Ids [{}]", tenantId, deviceIds);
         Select select = select().from(getColumnFamilyName());
@@ -75,7 +93,7 @@ public class DeviceDaoImpl extends AbstractSearchTextDao<DeviceEntity> implement
 
     @Override
     public List<DeviceEntity> findDevicesByTenantIdAndCustomerId(UUID tenantId, UUID customerId, TextPageLink pageLink) {
-        log.debug("Try to find devices by tenantId [{}], customerId[{}] and pageLink [{}]", tenantId, customerId, pageLink);
+        log.debug("Try to find devices by tenantId [{}], customerId [{}] and pageLink [{}]", tenantId, customerId, pageLink);
         List<DeviceEntity> deviceEntities = findPageWithTextSearch(DEVICE_BY_CUSTOMER_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME,
                 Arrays.asList(eq(DEVICE_CUSTOMER_ID_PROPERTY, customerId),
                         eq(DEVICE_TENANT_ID_PROPERTY, tenantId)),
@@ -86,6 +104,19 @@ public class DeviceDaoImpl extends AbstractSearchTextDao<DeviceEntity> implement
     }
 
     @Override
+    public List<DeviceEntity> findDevicesByTenantIdAndCustomerIdAndType(UUID tenantId, UUID customerId, String type, TextPageLink pageLink) {
+        log.debug("Try to find devices by tenantId [{}], customerId [{}], type [{}] and pageLink [{}]", tenantId, customerId, type, pageLink);
+        List<DeviceEntity> deviceEntities = findPageWithTextSearch(DEVICE_BY_CUSTOMER_BY_TYPE_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME,
+                Arrays.asList(eq(DEVICE_TYPE_PROPERTY, type),
+                              eq(DEVICE_CUSTOMER_ID_PROPERTY, customerId),
+                              eq(DEVICE_TENANT_ID_PROPERTY, tenantId)),
+                pageLink);
+
+        log.trace("Found devices [{}] by tenantId [{}], customerId [{}], type [{}] and pageLink [{}]", deviceEntities, tenantId, customerId, type, pageLink);
+        return deviceEntities;
+    }
+
+    @Override
     public ListenableFuture<List<DeviceEntity>> findDevicesByTenantIdCustomerIdAndIdsAsync(UUID tenantId, UUID customerId, List<UUID> deviceIds) {
         log.debug("Try to find devices by tenantId [{}], customerId [{}] and device Ids [{}]", tenantId, customerId, deviceIds);
         Select select = select().from(getColumnFamilyName());
@@ -105,4 +136,24 @@ public class DeviceDaoImpl extends AbstractSearchTextDao<DeviceEntity> implement
         return Optional.ofNullable(findOneByStatement(query));
     }
 
+    @Override
+    public ListenableFuture<List<TenantDeviceTypeEntity>> findTenantDeviceTypesAsync() {
+        Select statement = select().distinct().column(DEVICE_TYPE_PROPERTY).column(DEVICE_TENANT_ID_PROPERTY).from(DEVICE_TYPES_BY_TENANT_VIEW_NAME);
+        statement.setConsistencyLevel(cluster.getDefaultReadConsistencyLevel());
+        ResultSetFuture resultSetFuture = getSession().executeAsync(statement);
+        ListenableFuture<List<TenantDeviceTypeEntity>> result = Futures.transform(resultSetFuture, new Function<ResultSet, List<TenantDeviceTypeEntity>>() {
+            @Nullable
+            @Override
+            public List<TenantDeviceTypeEntity> apply(@Nullable ResultSet resultSet) {
+                Result<TenantDeviceTypeEntity> result = cluster.getMapper(TenantDeviceTypeEntity.class).map(resultSet);
+                if (result != null) {
+                    return result.all();
+                } else {
+                    return Collections.emptyList();
+                }
+            }
+        });
+        return result;
+    }
+
 }
diff --git a/dao/src/main/java/org/thingsboard/server/dao/device/DeviceService.java b/dao/src/main/java/org/thingsboard/server/dao/device/DeviceService.java
index 4715435..3e7b6af 100644
--- a/dao/src/main/java/org/thingsboard/server/dao/device/DeviceService.java
+++ b/dao/src/main/java/org/thingsboard/server/dao/device/DeviceService.java
@@ -17,6 +17,7 @@ package org.thingsboard.server.dao.device;
 
 import com.google.common.util.concurrent.ListenableFuture;
 import org.thingsboard.server.common.data.Device;
+import org.thingsboard.server.common.data.TenantDeviceType;
 import org.thingsboard.server.common.data.id.CustomerId;
 import org.thingsboard.server.common.data.id.DeviceId;
 import org.thingsboard.server.common.data.id.TenantId;
@@ -44,16 +45,22 @@ public interface DeviceService {
 
     TextPageData<Device> findDevicesByTenantId(TenantId tenantId, TextPageLink pageLink);
 
+    TextPageData<Device> findDevicesByTenantIdAndType(TenantId tenantId, String type, TextPageLink pageLink);
+
     ListenableFuture<List<Device>> findDevicesByTenantIdAndIdsAsync(TenantId tenantId, List<DeviceId> deviceIds);
 
     void deleteDevicesByTenantId(TenantId tenantId);
 
     TextPageData<Device> findDevicesByTenantIdAndCustomerId(TenantId tenantId, CustomerId customerId, TextPageLink pageLink);
 
+    TextPageData<Device> findDevicesByTenantIdAndCustomerIdAndType(TenantId tenantId, CustomerId customerId, String type, TextPageLink pageLink);
+
     ListenableFuture<List<Device>> findDevicesByTenantIdCustomerIdAndIdsAsync(TenantId tenantId, CustomerId customerId, List<DeviceId> deviceIds);
 
     void unassignCustomerDevices(TenantId tenantId, CustomerId customerId);
 
     ListenableFuture<List<Device>> findDevicesByQuery(DeviceSearchQuery query);
 
+    ListenableFuture<List<TenantDeviceType>> findDeviceTypesByTenantId(TenantId tenantId);
+
 }
diff --git a/dao/src/main/java/org/thingsboard/server/dao/device/DeviceServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/device/DeviceServiceImpl.java
index ab6fa4e..d01f154 100644
--- a/dao/src/main/java/org/thingsboard/server/dao/device/DeviceServiceImpl.java
+++ b/dao/src/main/java/org/thingsboard/server/dao/device/DeviceServiceImpl.java
@@ -26,6 +26,7 @@ import org.springframework.stereotype.Service;
 import org.springframework.util.StringUtils;
 import org.thingsboard.server.common.data.Device;
 import org.thingsboard.server.common.data.EntityType;
+import org.thingsboard.server.common.data.TenantDeviceType;
 import org.thingsboard.server.common.data.id.CustomerId;
 import org.thingsboard.server.common.data.id.DeviceId;
 import org.thingsboard.server.common.data.id.EntityId;
@@ -40,6 +41,7 @@ import org.thingsboard.server.dao.entity.BaseEntityService;
 import org.thingsboard.server.dao.exception.DataValidationException;
 import org.thingsboard.server.dao.model.CustomerEntity;
 import org.thingsboard.server.dao.model.DeviceEntity;
+import org.thingsboard.server.dao.model.TenantDeviceTypeEntity;
 import org.thingsboard.server.dao.model.TenantEntity;
 import org.thingsboard.server.dao.relation.EntitySearchDirection;
 import org.thingsboard.server.dao.service.DataValidator;
@@ -47,9 +49,7 @@ import org.thingsboard.server.dao.service.PaginatedRemover;
 import org.thingsboard.server.dao.tenant.TenantDao;
 
 import javax.annotation.Nullable;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Optional;
+import java.util.*;
 import java.util.stream.Collectors;
 
 import static org.thingsboard.server.dao.DaoUtil.*;
@@ -148,7 +148,18 @@ public class DeviceServiceImpl extends BaseEntityService implements DeviceServic
         validatePageLink(pageLink, "Incorrect page link " + pageLink);
         List<DeviceEntity> deviceEntities = deviceDao.findDevicesByTenantId(tenantId.getId(), pageLink);
         List<Device> devices = convertDataList(deviceEntities);
-        return new TextPageData<Device>(devices, pageLink);
+        return new TextPageData<>(devices, pageLink);
+    }
+
+    @Override
+    public TextPageData<Device> findDevicesByTenantIdAndType(TenantId tenantId, String type, TextPageLink pageLink) {
+        log.trace("Executing findDevicesByTenantIdAndType, tenantId [{}], type [{}], pageLink [{}]", tenantId, type, pageLink);
+        validateId(tenantId, "Incorrect tenantId " + tenantId);
+        validateString(type, "Incorrect type " + type);
+        validatePageLink(pageLink, "Incorrect page link " + pageLink);
+        List<DeviceEntity> deviceEntities = deviceDao.findDevicesByTenantIdAndType(tenantId.getId(), type, pageLink);
+        List<Device> devices = convertDataList(deviceEntities);
+        return new TextPageData<>(devices, pageLink);
     }
 
     @Override
@@ -176,7 +187,19 @@ public class DeviceServiceImpl extends BaseEntityService implements DeviceServic
         validatePageLink(pageLink, "Incorrect page link " + pageLink);
         List<DeviceEntity> deviceEntities = deviceDao.findDevicesByTenantIdAndCustomerId(tenantId.getId(), customerId.getId(), pageLink);
         List<Device> devices = convertDataList(deviceEntities);
-        return new TextPageData<Device>(devices, pageLink);
+        return new TextPageData<>(devices, pageLink);
+    }
+
+    @Override
+    public TextPageData<Device> findDevicesByTenantIdAndCustomerIdAndType(TenantId tenantId, CustomerId customerId, String type, TextPageLink pageLink) {
+        log.trace("Executing findDevicesByTenantIdAndCustomerIdAndType, tenantId [{}], customerId [{}], type [{}], pageLink [{}]", tenantId, customerId, type, pageLink);
+        validateId(tenantId, "Incorrect tenantId " + tenantId);
+        validateId(customerId, "Incorrect customerId " + customerId);
+        validateString(type, "Incorrect type " + type);
+        validatePageLink(pageLink, "Incorrect page link " + pageLink);
+        List<DeviceEntity> deviceEntities = deviceDao.findDevicesByTenantIdAndCustomerIdAndType(tenantId.getId(), customerId.getId(), type, pageLink);
+        List<Device> devices = convertDataList(deviceEntities);
+        return new TextPageData<>(devices, pageLink);
     }
 
     @Override
@@ -224,6 +247,25 @@ public class DeviceServiceImpl extends BaseEntityService implements DeviceServic
         return devices;
     }
 
+    @Override
+    public ListenableFuture<List<TenantDeviceType>> findDeviceTypesByTenantId(TenantId tenantId) {
+        log.trace("Executing findDeviceTypesByTenantId, tenantId [{}]", tenantId);
+        validateId(tenantId, "Incorrect tenantId " + tenantId);
+        ListenableFuture<List<TenantDeviceTypeEntity>> tenantDeviceTypeEntities = deviceDao.findTenantDeviceTypesAsync();
+        ListenableFuture<List<TenantDeviceType>> tenantDeviceTypes = Futures.transform(tenantDeviceTypeEntities,
+            (Function<List<TenantDeviceTypeEntity>, List<TenantDeviceType>>) deviceTypeEntities -> {
+                List<TenantDeviceType> deviceTypes = new ArrayList<>();
+                for (TenantDeviceTypeEntity deviceTypeEntity : deviceTypeEntities) {
+                    if (deviceTypeEntity.getTenantId().equals(tenantId.getId())) {
+                        deviceTypes.add(deviceTypeEntity.toTenantDeviceType());
+                    }
+                }
+                deviceTypes.sort((TenantDeviceType o1, TenantDeviceType o2) -> o1.getType().compareTo(o2.getType()));
+                return deviceTypes;
+            });
+        return tenantDeviceTypes;
+    }
+
     private DataValidator<Device> deviceValidator =
             new DataValidator<Device>() {
 
@@ -249,6 +291,9 @@ public class DeviceServiceImpl extends BaseEntityService implements DeviceServic
 
                 @Override
                 protected void validateDataImpl(Device device) {
+                    if (StringUtils.isEmpty(device.getType())) {
+                        throw new DataValidationException("Device type should be specified!");
+                    }
                     if (StringUtils.isEmpty(device.getName())) {
                         throw new DataValidationException("Device name should be specified!");
                     }
diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/AssetEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/AssetEntity.java
index 0444d11..0d477c0 100644
--- a/dao/src/main/java/org/thingsboard/server/dao/model/AssetEntity.java
+++ b/dao/src/main/java/org/thingsboard/server/dao/model/AssetEntity.java
@@ -49,12 +49,13 @@ public final class AssetEntity implements SearchTextEntity<Asset> {
     @Column(name = ASSET_CUSTOMER_ID_PROPERTY)
     private UUID customerId;
 
-    @Column(name = ASSET_NAME_PROPERTY)
-    private String name;
-
+    @PartitionKey(value = 3)
     @Column(name = ASSET_TYPE_PROPERTY)
     private String type;
 
+    @Column(name = ASSET_NAME_PROPERTY)
+    private String name;
+
     @Column(name = SEARCH_TEXT_PROPERTY)
     private String searchText;
 
diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/DeviceEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/DeviceEntity.java
index 69ed92c..a7fe243 100644
--- a/dao/src/main/java/org/thingsboard/server/dao/model/DeviceEntity.java
+++ b/dao/src/main/java/org/thingsboard/server/dao/model/DeviceEntity.java
@@ -49,12 +49,13 @@ public final class DeviceEntity implements SearchTextEntity<Device> {
     @Column(name = DEVICE_CUSTOMER_ID_PROPERTY)
     private UUID customerId;
 
-    @Column(name = DEVICE_NAME_PROPERTY)
-    private String name;
-
+    @PartitionKey(value = 3)
     @Column(name = DEVICE_TYPE_PROPERTY)
     private String type;
 
+    @Column(name = DEVICE_NAME_PROPERTY)
+    private String name;
+
     @Column(name = SEARCH_TEXT_PROPERTY)
     private String searchText;
     
diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java b/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java
index 9f78e27..1081311 100644
--- a/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java
+++ b/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java
@@ -124,8 +124,11 @@ public class ModelConstants {
     public static final String DEVICE_ADDITIONAL_INFO_PROPERTY = ADDITIONAL_INFO_PROPERTY;
 
     public static final String DEVICE_BY_TENANT_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME = "device_by_tenant_and_search_text";
+    public static final String DEVICE_BY_TENANT_BY_TYPE_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME = "device_by_tenant_by_type_and_search_text";
     public static final String DEVICE_BY_CUSTOMER_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME = "device_by_customer_and_search_text";
+    public static final String DEVICE_BY_CUSTOMER_BY_TYPE_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME = "device_by_customer_by_type_and_search_text";
     public static final String DEVICE_BY_TENANT_AND_NAME_VIEW_NAME = "device_by_tenant_and_name";
+    public static final String DEVICE_TYPES_BY_TENANT_VIEW_NAME = "device_types_by_tenant";
 
     /**
      * Cassandra asset constants.
@@ -138,8 +141,11 @@ public class ModelConstants {
     public static final String ASSET_ADDITIONAL_INFO_PROPERTY = ADDITIONAL_INFO_PROPERTY;
 
     public static final String ASSET_BY_TENANT_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME = "asset_by_tenant_and_search_text";
+    public static final String ASSET_BY_TENANT_BY_TYPE_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME = "asset_by_tenant_by_type_and_search_text";
     public static final String ASSET_BY_CUSTOMER_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME = "asset_by_customer_and_search_text";
+    public static final String ASSET_BY_CUSTOMER_BY_TYPE_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME = "asset_by_customer_by_type_and_search_text";
     public static final String ASSET_BY_TENANT_AND_NAME_VIEW_NAME = "asset_by_tenant_and_name";
+    public static final String ASSET_TYPES_BY_TENANT_VIEW_NAME = "asset_types_by_tenant";
 
     /**
      * Cassandra entity relation constants.
diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/TenantAssetTypeEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/TenantAssetTypeEntity.java
new file mode 100644
index 0000000..36361ef
--- /dev/null
+++ b/dao/src/main/java/org/thingsboard/server/dao/model/TenantAssetTypeEntity.java
@@ -0,0 +1,107 @@
+/**
+ * Copyright © 2016-2017 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.thingsboard.server.dao.model;
+
+import com.datastax.driver.mapping.annotations.Column;
+import com.datastax.driver.mapping.annotations.PartitionKey;
+import com.datastax.driver.mapping.annotations.Table;
+import com.datastax.driver.mapping.annotations.Transient;
+import org.thingsboard.server.common.data.asset.TenantAssetType;
+import org.thingsboard.server.common.data.id.TenantId;
+
+import java.util.UUID;
+
+import static org.thingsboard.server.dao.model.ModelConstants.*;
+
+@Table(name = ASSET_TYPES_BY_TENANT_VIEW_NAME)
+public class TenantAssetTypeEntity {
+
+    @Transient
+    private static final long serialVersionUID = -1268181161886910152L;
+
+    @PartitionKey(value = 0)
+    @Column(name = ASSET_TYPE_PROPERTY)
+    private String type;
+
+    @PartitionKey(value = 1)
+    @Column(name = ASSET_TENANT_ID_PROPERTY)
+    private UUID tenantId;
+
+    public TenantAssetTypeEntity() {
+        super();
+    }
+
+    public TenantAssetTypeEntity(TenantAssetType tenantAssetType) {
+        this.type = tenantAssetType.getType();
+        if (tenantAssetType.getTenantId() != null) {
+            this.tenantId = tenantAssetType.getTenantId().getId();
+        }
+    }
+
+    public String getType() {
+        return type;
+    }
+
+    public void setType(String type) {
+        this.type = type;
+    }
+
+    public UUID getTenantId() {
+        return tenantId;
+    }
+
+    public void setTenantId(UUID tenantId) {
+        this.tenantId = tenantId;
+    }
+
+    @Override
+    public int hashCode() {
+        int result = type != null ? type.hashCode() : 0;
+        result = 31 * result + (tenantId != null ? tenantId.hashCode() : 0);
+        return result;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+
+        TenantAssetTypeEntity that = (TenantAssetTypeEntity) o;
+
+        if (type != null ? !type.equals(that.type) : that.type != null) return false;
+        return tenantId != null ? tenantId.equals(that.tenantId) : that.tenantId == null;
+
+    }
+
+    @Override
+    public String toString() {
+        final StringBuilder sb = new StringBuilder("TenantAssetTypeEntity{");
+        sb.append("type='").append(type).append('\'');
+        sb.append(", tenantId=").append(tenantId);
+        sb.append('}');
+        return sb.toString();
+    }
+
+    public TenantAssetType toTenantAssetType() {
+        TenantAssetType tenantAssetType = new TenantAssetType();
+        tenantAssetType.setType(type);
+        if (tenantId != null) {
+            tenantAssetType.setTenantId(new TenantId(tenantId));
+        }
+        return tenantAssetType;
+    }
+}
diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/TenantDeviceTypeEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/TenantDeviceTypeEntity.java
new file mode 100644
index 0000000..dad954c
--- /dev/null
+++ b/dao/src/main/java/org/thingsboard/server/dao/model/TenantDeviceTypeEntity.java
@@ -0,0 +1,107 @@
+/**
+ * Copyright © 2016-2017 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.thingsboard.server.dao.model;
+
+import com.datastax.driver.mapping.annotations.Column;
+import com.datastax.driver.mapping.annotations.PartitionKey;
+import com.datastax.driver.mapping.annotations.Table;
+import com.datastax.driver.mapping.annotations.Transient;
+import org.thingsboard.server.common.data.TenantDeviceType;
+import org.thingsboard.server.common.data.id.TenantId;
+
+import java.util.UUID;
+
+import static org.thingsboard.server.dao.model.ModelConstants.*;
+
+@Table(name = DEVICE_TYPES_BY_TENANT_VIEW_NAME)
+public class TenantDeviceTypeEntity {
+
+    @Transient
+    private static final long serialVersionUID = -1268181166886910152L;
+
+    @PartitionKey(value = 0)
+    @Column(name = DEVICE_TYPE_PROPERTY)
+    private String type;
+
+    @PartitionKey(value = 1)
+    @Column(name = DEVICE_TENANT_ID_PROPERTY)
+    private UUID tenantId;
+
+    public TenantDeviceTypeEntity() {
+        super();
+    }
+
+    public TenantDeviceTypeEntity(TenantDeviceType tenantDeviceType) {
+        this.type = tenantDeviceType.getType();
+        if (tenantDeviceType.getTenantId() != null) {
+            this.tenantId = tenantDeviceType.getTenantId().getId();
+        }
+    }
+
+    public String getType() {
+        return type;
+    }
+
+    public void setType(String type) {
+        this.type = type;
+    }
+
+    public UUID getTenantId() {
+        return tenantId;
+    }
+
+    public void setTenantId(UUID tenantId) {
+        this.tenantId = tenantId;
+    }
+
+    @Override
+    public int hashCode() {
+        int result = type != null ? type.hashCode() : 0;
+        result = 31 * result + (tenantId != null ? tenantId.hashCode() : 0);
+        return result;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+
+        TenantDeviceTypeEntity that = (TenantDeviceTypeEntity) o;
+
+        if (type != null ? !type.equals(that.type) : that.type != null) return false;
+        return tenantId != null ? tenantId.equals(that.tenantId) : that.tenantId == null;
+
+    }
+
+    @Override
+    public String toString() {
+        final StringBuilder sb = new StringBuilder("TenantDeviceTypeEntity{");
+        sb.append("type='").append(type).append('\'');
+        sb.append(", tenantId=").append(tenantId);
+        sb.append('}');
+        return sb.toString();
+    }
+
+    public TenantDeviceType toTenantDeviceType() {
+        TenantDeviceType tenantDeviceType = new TenantDeviceType();
+        tenantDeviceType.setType(type);
+        if (tenantId != null) {
+            tenantDeviceType.setTenantId(new TenantId(tenantId));
+        }
+        return tenantDeviceType;
+    }
+}
diff --git a/dao/src/main/resources/demo-data.cql b/dao/src/main/resources/demo-data.cql
index 023cf1b..47d8de4 100644
--- a/dao/src/main/resources/demo-data.cql
+++ b/dao/src/main/resources/demo-data.cql
@@ -149,66 +149,73 @@ VALUES (
 
 /** Demo device **/
 
-INSERT INTO thingsboard.device ( id, tenant_id, customer_id, name, search_text)
+INSERT INTO thingsboard.device ( id, tenant_id, customer_id, type, name, search_text)
 VALUES (
 	minTimeuuid ( '2016-11-01 01:02:05+0000' ),
 	minTimeuuid ( '2016-11-01 01:02:01+0000' ),
 	minTimeuuid ( '2016-11-01 01:02:03+0000' ),
+	'default',
 	'Test Device A1',
 	'test device a1'
 );
 
-INSERT INTO thingsboard.device ( id, tenant_id, customer_id, name, search_text)
+INSERT INTO thingsboard.device ( id, tenant_id, customer_id, type, name, search_text)
 VALUES (
 	minTimeuuid ( '2016-11-01 01:02:05+0001' ),
 	minTimeuuid ( '2016-11-01 01:02:01+0000' ),
 	minTimeuuid ( '2016-11-01 01:02:03+0000' ),
+	'default',
 	'Test Device A2',
 	'test device a2'
 );
 
-INSERT INTO thingsboard.device ( id, tenant_id, customer_id, name, search_text)
+INSERT INTO thingsboard.device ( id, tenant_id, customer_id, type, name, search_text)
 VALUES (
 	minTimeuuid ( '2016-11-01 01:02:05+0002' ),
 	minTimeuuid ( '2016-11-01 01:02:01+0000' ),
 	minTimeuuid ( '2016-11-01 01:02:03+0000' ),
+	'default',
 	'Test Device A3',
 	'test device a3'
 );
 
-INSERT INTO thingsboard.device ( id, tenant_id, customer_id, name, search_text)
+INSERT INTO thingsboard.device ( id, tenant_id, customer_id, type, name, search_text)
 VALUES (
 	minTimeuuid ( '2016-11-01 01:02:05+0003' ),
 	minTimeuuid ( '2016-11-01 01:02:01+0000' ),
 	minTimeuuid ( '2016-11-01 01:02:03+0001' ),
+	'default',
 	'Test Device B1',
 	'test device b1'
 );
 
-INSERT INTO thingsboard.device ( id, tenant_id, customer_id, name, search_text)
+INSERT INTO thingsboard.device ( id, tenant_id, customer_id, type, name, search_text)
 VALUES (
 	minTimeuuid ( '2016-11-01 01:02:05+0004' ),
 	minTimeuuid ( '2016-11-01 01:02:01+0000' ),
 	minTimeuuid ( '2016-11-01 01:02:03+0002' ),
+	'default',
 	'Test Device C1',
 	'test device c1'
 );
 
-INSERT INTO thingsboard.device ( id, tenant_id, customer_id, name, search_text, additional_info)
+INSERT INTO thingsboard.device ( id, tenant_id, customer_id, type, name, search_text, additional_info)
 VALUES (
 	c8f1a6f0-b993-11e6-8a04-9ff4e1b7933c,
 	minTimeuuid ( '2016-11-01 01:02:01+0000' ),
 	minTimeuuid ( 0 ),
+	'default',
 	'DHT11 Demo Device',
 	'dht11 demo device',
 	'{"description":"Demo device that is used in sample applications that upload data from DHT11 temperature and humidity sensor"}'
 );
 
-INSERT INTO thingsboard.device ( id, tenant_id, customer_id, name, search_text, additional_info)
+INSERT INTO thingsboard.device ( id, tenant_id, customer_id, type, name, search_text, additional_info)
 VALUES (
 	c8f1a6f0-b993-11e6-8a04-9ff4e1b7933d,
 	minTimeuuid ( '2016-11-01 01:02:01+0000' ),
 	minTimeuuid ( 0 ),
+	'default',
 	'Raspberry Pi Demo Device',
 	'raspberry pi demo device',
 	'{"description":"Demo device that is used in Raspberry Pi GPIO control sample application"}'
diff --git a/dao/src/main/resources/schema.cql b/dao/src/main/resources/schema.cql
index c3e7cda..5ce3dc9 100644
--- a/dao/src/main/resources/schema.cql
+++ b/dao/src/main/resources/schema.cql
@@ -152,36 +152,57 @@ CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.customer_by_tenant_and_search
 	WITH CLUSTERING ORDER BY ( search_text ASC, id DESC );
 
 CREATE TABLE IF NOT EXISTS thingsboard.device (
-	id timeuuid,
-	tenant_id timeuuid,
-	customer_id timeuuid,
-	name text,
-	type text,
-	search_text text,
-	additional_info text,
-	PRIMARY KEY (id, tenant_id, customer_id)
+    id timeuuid,
+    tenant_id timeuuid,
+    customer_id timeuuid,
+    name text,
+    type text,
+    search_text text,
+    additional_info text,
+    PRIMARY KEY (id, tenant_id, customer_id, type)
 );
 
 CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.device_by_tenant_and_name AS
-	SELECT *
-	from thingsboard.device
-	WHERE tenant_id IS NOT NULL AND customer_id IS NOT NULL AND name IS NOT NULL AND id IS NOT NULL
-	PRIMARY KEY ( tenant_id, name, id, customer_id)
-	WITH CLUSTERING ORDER BY ( name ASC, id DESC, customer_id DESC);
+    SELECT *
+    from thingsboard.device
+    WHERE tenant_id IS NOT NULL AND customer_id IS NOT NULL AND type IS NOT NULL AND name IS NOT NULL AND id IS NOT NULL
+    PRIMARY KEY ( tenant_id, name, id, customer_id, type)
+    WITH CLUSTERING ORDER BY ( name ASC, id DESC, customer_id DESC);
 
 CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.device_by_tenant_and_search_text AS
-	SELECT *
-	from thingsboard.device
-	WHERE tenant_id IS NOT NULL AND customer_id IS NOT NULL AND search_text IS NOT NULL AND id IS NOT NULL
-	PRIMARY KEY ( tenant_id, search_text, id, customer_id)
-	WITH CLUSTERING ORDER BY ( search_text ASC, id DESC, customer_id DESC);
+    SELECT *
+    from thingsboard.device
+    WHERE tenant_id IS NOT NULL AND customer_id IS NOT NULL AND type IS NOT NULL AND search_text IS NOT NULL AND id IS NOT NULL
+    PRIMARY KEY ( tenant_id, search_text, id, customer_id, type)
+    WITH CLUSTERING ORDER BY ( search_text ASC, id DESC, customer_id DESC);
+
+CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.device_by_tenant_by_type_and_search_text AS
+    SELECT *
+    from thingsboard.device
+    WHERE tenant_id IS NOT NULL AND customer_id IS NOT NULL AND type IS NOT NULL AND search_text IS NOT NULL AND id IS NOT NULL
+    PRIMARY KEY ( tenant_id, type, search_text, id, customer_id)
+    WITH CLUSTERING ORDER BY ( type ASC, search_text ASC, id DESC, customer_id DESC);
 
 CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.device_by_customer_and_search_text AS
-	SELECT *
-	from thingsboard.device
-	WHERE tenant_id IS NOT NULL AND customer_id IS NOT NULL AND search_text IS NOT NULL AND id IS NOT NULL
-	PRIMARY KEY ( customer_id, tenant_id, search_text, id )
-	WITH CLUSTERING ORDER BY ( tenant_id DESC, search_text ASC, id DESC );
+    SELECT *
+    from thingsboard.device
+    WHERE tenant_id IS NOT NULL AND customer_id IS NOT NULL AND type IS NOT NULL AND search_text IS NOT NULL AND id IS NOT NULL
+    PRIMARY KEY ( customer_id, tenant_id, search_text, id, type )
+    WITH CLUSTERING ORDER BY ( tenant_id DESC, search_text ASC, id DESC );
+
+CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.device_by_customer_by_type_and_search_text AS
+    SELECT *
+    from thingsboard.device
+    WHERE tenant_id IS NOT NULL AND customer_id IS NOT NULL AND type IS NOT NULL AND search_text IS NOT NULL AND id IS NOT NULL
+    PRIMARY KEY ( customer_id, tenant_id, type, search_text, id )
+    WITH CLUSTERING ORDER BY ( tenant_id DESC, type ASC, search_text ASC, id DESC );
+
+CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.device_types_by_tenant AS
+    SELECT *
+    from thingsboard.device
+    WHERE tenant_id IS NOT NULL AND customer_id IS NOT NULL AND type IS NOT NULL AND id IS NOT NULL
+    PRIMARY KEY ( (type, tenant_id), id, customer_id)
+    WITH CLUSTERING ORDER BY ( id ASC, customer_id DESC);
 
 CREATE TABLE IF NOT EXISTS thingsboard.device_credentials (
 	id timeuuid PRIMARY KEY,
@@ -203,38 +224,58 @@ CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.device_credentials_by_credent
 	WHERE credentials_id IS NOT NULL AND id IS NOT NULL
 	PRIMARY KEY ( credentials_id, id );
 
-
 CREATE TABLE IF NOT EXISTS thingsboard.asset (
-	id timeuuid,
-	tenant_id timeuuid,
-	customer_id timeuuid,
-	name text,
-	type text,
-	search_text text,
-	additional_info text,
-	PRIMARY KEY (id, tenant_id, customer_id)
+    id timeuuid,
+    tenant_id timeuuid,
+    customer_id timeuuid,
+    name text,
+    type text,
+    search_text text,
+    additional_info text,
+    PRIMARY KEY (id, tenant_id, customer_id, type)
 );
 
 CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.asset_by_tenant_and_name AS
-	SELECT *
-	from thingsboard.asset
-	WHERE tenant_id IS NOT NULL AND customer_id IS NOT NULL AND name IS NOT NULL AND id IS NOT NULL
-	PRIMARY KEY ( tenant_id, name, id, customer_id)
-	WITH CLUSTERING ORDER BY ( name ASC, id DESC, customer_id DESC);
+    SELECT *
+    from thingsboard.asset
+    WHERE tenant_id IS NOT NULL AND customer_id IS NOT NULL AND type IS NOT NULL AND name IS NOT NULL AND id IS NOT NULL
+    PRIMARY KEY ( tenant_id, name, id, customer_id, type)
+    WITH CLUSTERING ORDER BY ( name ASC, id DESC, customer_id DESC);
 
 CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.asset_by_tenant_and_search_text AS
-	SELECT *
-	from thingsboard.asset
-	WHERE tenant_id IS NOT NULL AND customer_id IS NOT NULL AND search_text IS NOT NULL AND id IS NOT NULL
-	PRIMARY KEY ( tenant_id, search_text, id, customer_id)
-	WITH CLUSTERING ORDER BY ( search_text ASC, id DESC, customer_id DESC);
+    SELECT *
+    from thingsboard.asset
+    WHERE tenant_id IS NOT NULL AND customer_id IS NOT NULL AND type IS NOT NULL AND search_text IS NOT NULL AND id IS NOT NULL
+    PRIMARY KEY ( tenant_id, search_text, id, customer_id, type)
+    WITH CLUSTERING ORDER BY ( search_text ASC, id DESC, customer_id DESC);
+
+CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.asset_by_tenant_by_type_and_search_text AS
+    SELECT *
+    from thingsboard.asset
+    WHERE tenant_id IS NOT NULL AND customer_id IS NOT NULL AND type IS NOT NULL AND search_text IS NOT NULL AND id IS NOT NULL
+    PRIMARY KEY ( tenant_id, type, search_text, id, customer_id)
+    WITH CLUSTERING ORDER BY ( type ASC, search_text ASC, id DESC, customer_id DESC);
 
 CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.asset_by_customer_and_search_text AS
-	SELECT *
-	from thingsboard.asset
-	WHERE tenant_id IS NOT NULL AND customer_id IS NOT NULL AND search_text IS NOT NULL AND id IS NOT NULL
-	PRIMARY KEY ( customer_id, tenant_id, search_text, id )
-	WITH CLUSTERING ORDER BY ( tenant_id DESC, search_text ASC, id DESC );
+    SELECT *
+    from thingsboard.asset
+    WHERE tenant_id IS NOT NULL AND customer_id IS NOT NULL AND type IS NOT NULL AND search_text IS NOT NULL AND id IS NOT NULL
+    PRIMARY KEY ( customer_id, tenant_id, search_text, id, type )
+    WITH CLUSTERING ORDER BY ( tenant_id DESC, search_text ASC, id DESC );
+
+CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.asset_by_customer_by_type_and_search_text AS
+    SELECT *
+    from thingsboard.asset
+    WHERE tenant_id IS NOT NULL AND customer_id IS NOT NULL AND type IS NOT NULL AND search_text IS NOT NULL AND id IS NOT NULL
+    PRIMARY KEY ( customer_id, tenant_id, type, search_text, id )
+    WITH CLUSTERING ORDER BY ( tenant_id DESC, type ASC, search_text ASC, id DESC );
+
+CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.asset_types_by_tenant AS
+    SELECT *
+    from thingsboard.asset
+    WHERE tenant_id IS NOT NULL AND customer_id IS NOT NULL AND type IS NOT NULL AND id IS NOT NULL
+    PRIMARY KEY ( (type, tenant_id), id, customer_id)
+    WITH CLUSTERING ORDER BY ( id ASC, customer_id DESC);
 
 CREATE TABLE IF NOT EXISTS thingsboard.relation (
 	from_id timeuuid,
@@ -247,11 +288,11 @@ CREATE TABLE IF NOT EXISTS thingsboard.relation (
 );
 
 CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.reverse_relation AS
-SELECT *
-from thingsboard.relation
-WHERE from_id IS NOT NULL AND from_type IS NOT NULL AND relation_type IS NOT NULL AND to_id IS NOT NULL AND to_type IS NOT NULL
-PRIMARY KEY ((to_id, to_type), relation_type, from_id, from_type)
-WITH CLUSTERING ORDER BY ( relation_type ASC, from_id ASC, from_type ASC);
+    SELECT *
+    from thingsboard.relation
+    WHERE from_id IS NOT NULL AND from_type IS NOT NULL AND relation_type IS NOT NULL AND to_id IS NOT NULL AND to_type IS NOT NULL
+    PRIMARY KEY ((to_id, to_type), relation_type, from_id, from_type)
+    WITH CLUSTERING ORDER BY ( relation_type ASC, from_id ASC, from_type ASC);
 
 CREATE TABLE IF NOT EXISTS thingsboard.widgets_bundle (
     id timeuuid,
diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/AbstractServiceTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/AbstractServiceTest.java
index 903207c..1e03a97 100644
--- a/dao/src/test/java/org/thingsboard/server/dao/service/AbstractServiceTest.java
+++ b/dao/src/test/java/org/thingsboard/server/dao/service/AbstractServiceTest.java
@@ -40,6 +40,7 @@ import org.thingsboard.server.common.data.plugin.ComponentScope;
 import org.thingsboard.server.common.data.plugin.ComponentType;
 import org.thingsboard.server.common.data.plugin.PluginMetaData;
 import org.thingsboard.server.common.data.rule.RuleMetaData;
+import org.thingsboard.server.dao.asset.AssetService;
 import org.thingsboard.server.dao.component.ComponentDescriptorService;
 import org.thingsboard.server.dao.customer.CustomerService;
 import org.thingsboard.server.dao.dashboard.DashboardService;
@@ -88,6 +89,9 @@ public abstract class AbstractServiceTest {
     protected DeviceService deviceService;
 
     @Autowired
+    protected AssetService assetService;
+
+    @Autowired
     protected DeviceCredentialsService deviceCredentialsService;
 
     @Autowired
diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/BaseAssetServiceTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/BaseAssetServiceTest.java
new file mode 100644
index 0000000..9587703
--- /dev/null
+++ b/dao/src/test/java/org/thingsboard/server/dao/service/BaseAssetServiceTest.java
@@ -0,0 +1,634 @@
+/**
+ * Copyright © 2016-2017 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.thingsboard.server.dao.service;
+
+import com.datastax.driver.core.utils.UUIDs;
+import org.apache.commons.lang3.RandomStringUtils;
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.thingsboard.server.common.data.Customer;
+import org.thingsboard.server.common.data.Tenant;
+import org.thingsboard.server.common.data.asset.Asset;
+import org.thingsboard.server.common.data.asset.TenantAssetType;
+import org.thingsboard.server.common.data.id.CustomerId;
+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 org.thingsboard.server.dao.exception.DataValidationException;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import static org.thingsboard.server.dao.model.ModelConstants.NULL_UUID;
+
+public class BaseAssetServiceTest extends AbstractServiceTest {
+
+    private IdComparator<Asset> idComparator = new IdComparator<>();
+
+    private TenantId tenantId;
+
+    @Before
+    public void before() {
+        Tenant tenant = new Tenant();
+        tenant.setTitle("My tenant");
+        Tenant savedTenant = tenantService.saveTenant(tenant);
+        Assert.assertNotNull(savedTenant);
+        tenantId = savedTenant.getId();
+    }
+
+    @After
+    public void after() {
+        tenantService.deleteTenant(tenantId);
+    }
+
+    @Test
+    public void testSaveAsset() {
+        Asset asset = new Asset();
+        asset.setTenantId(tenantId);
+        asset.setName("My asset");
+        asset.setType("default");
+        Asset savedAsset = assetService.saveAsset(asset);
+
+        Assert.assertNotNull(savedAsset);
+        Assert.assertNotNull(savedAsset.getId());
+        Assert.assertTrue(savedAsset.getCreatedTime() > 0);
+        Assert.assertEquals(asset.getTenantId(), savedAsset.getTenantId());
+        Assert.assertNotNull(savedAsset.getCustomerId());
+        Assert.assertEquals(NULL_UUID, savedAsset.getCustomerId().getId());
+        Assert.assertEquals(asset.getName(), savedAsset.getName());
+
+        savedAsset.setName("My new asset");
+
+        assetService.saveAsset(savedAsset);
+        Asset foundAsset = assetService.findAssetById(savedAsset.getId());
+        Assert.assertEquals(foundAsset.getName(), savedAsset.getName());
+
+        assetService.deleteAsset(savedAsset.getId());
+    }
+
+    @Test(expected = DataValidationException.class)
+    public void testSaveAssetWithEmptyName() {
+        Asset asset = new Asset();
+        asset.setTenantId(tenantId);
+        asset.setType("default");
+        assetService.saveAsset(asset);
+    }
+
+    @Test(expected = DataValidationException.class)
+    public void testSaveAssetWithEmptyTenant() {
+        Asset asset = new Asset();
+        asset.setName("My asset");
+        asset.setType("default");
+        assetService.saveAsset(asset);
+    }
+
+    @Test(expected = DataValidationException.class)
+    public void testSaveAssetWithInvalidTenant() {
+        Asset asset = new Asset();
+        asset.setName("My asset");
+        asset.setType("default");
+        asset.setTenantId(new TenantId(UUIDs.timeBased()));
+        assetService.saveAsset(asset);
+    }
+
+    @Test(expected = DataValidationException.class)
+    public void testAssignAssetToNonExistentCustomer() {
+        Asset asset = new Asset();
+        asset.setName("My asset");
+        asset.setType("default");
+        asset.setTenantId(tenantId);
+        asset = assetService.saveAsset(asset);
+        try {
+            assetService.assignAssetToCustomer(asset.getId(), new CustomerId(UUIDs.timeBased()));
+        } finally {
+            assetService.deleteAsset(asset.getId());
+        }
+    }
+
+    @Test(expected = DataValidationException.class)
+    public void testAssignAssetToCustomerFromDifferentTenant() {
+        Asset asset = new Asset();
+        asset.setName("My asset");
+        asset.setType("default");
+        asset.setTenantId(tenantId);
+        asset = assetService.saveAsset(asset);
+        Tenant tenant = new Tenant();
+        tenant.setTitle("Test different tenant");
+        tenant = tenantService.saveTenant(tenant);
+        Customer customer = new Customer();
+        customer.setTenantId(tenant.getId());
+        customer.setTitle("Test different customer");
+        customer = customerService.saveCustomer(customer);
+        try {
+            assetService.assignAssetToCustomer(asset.getId(), customer.getId());
+        } finally {
+            assetService.deleteAsset(asset.getId());
+            tenantService.deleteTenant(tenant.getId());
+        }
+    }
+
+    @Test
+    public void testFindAssetById() {
+        Asset asset = new Asset();
+        asset.setTenantId(tenantId);
+        asset.setName("My asset");
+        asset.setType("default");
+        Asset savedAsset = assetService.saveAsset(asset);
+        Asset foundAsset = assetService.findAssetById(savedAsset.getId());
+        Assert.assertNotNull(foundAsset);
+        Assert.assertEquals(savedAsset, foundAsset);
+        assetService.deleteAsset(savedAsset.getId());
+    }
+
+    @Test
+    public void testFindAssetTypesByTenantId() throws Exception {
+        List<Asset> assets = new ArrayList<>();
+        try {
+            for (int i=0;i<3;i++) {
+                Asset asset = new Asset();
+                asset.setTenantId(tenantId);
+                asset.setName("My asset B"+i);
+                asset.setType("typeB");
+                assets.add(assetService.saveAsset(asset));
+            }
+            for (int i=0;i<7;i++) {
+                Asset asset = new Asset();
+                asset.setTenantId(tenantId);
+                asset.setName("My asset C"+i);
+                asset.setType("typeC");
+                assets.add(assetService.saveAsset(asset));
+            }
+            for (int i=0;i<9;i++) {
+                Asset asset = new Asset();
+                asset.setTenantId(tenantId);
+                asset.setName("My asset A"+i);
+                asset.setType("typeA");
+                assets.add(assetService.saveAsset(asset));
+            }
+            List<TenantAssetType> assetTypes = assetService.findAssetTypesByTenantId(tenantId).get();
+            Assert.assertNotNull(assetTypes);
+            Assert.assertEquals(3, assetTypes.size());
+            Assert.assertEquals("typeA", assetTypes.get(0).getType());
+            Assert.assertEquals("typeB", assetTypes.get(1).getType());
+            Assert.assertEquals("typeC", assetTypes.get(2).getType());
+        } finally {
+            assets.forEach((asset) -> { assetService.deleteAsset(asset.getId()); });
+        }
+    }
+
+    @Test
+    public void testDeleteAsset() {
+        Asset asset = new Asset();
+        asset.setTenantId(tenantId);
+        asset.setName("My asset");
+        asset.setType("default");
+        Asset savedAsset = assetService.saveAsset(asset);
+        Asset foundAsset = assetService.findAssetById(savedAsset.getId());
+        Assert.assertNotNull(foundAsset);
+        assetService.deleteAsset(savedAsset.getId());
+        foundAsset = assetService.findAssetById(savedAsset.getId());
+        Assert.assertNull(foundAsset);
+    }
+
+    @Test
+    public void testFindAssetsByTenantId() {
+        Tenant tenant = new Tenant();
+        tenant.setTitle("Test tenant");
+        tenant = tenantService.saveTenant(tenant);
+
+        TenantId tenantId = tenant.getId();
+
+        List<Asset> assets = new ArrayList<>();
+        for (int i=0;i<178;i++) {
+            Asset asset = new Asset();
+            asset.setTenantId(tenantId);
+            asset.setName("Asset"+i);
+            asset.setType("default");
+            assets.add(assetService.saveAsset(asset));
+        }
+
+        List<Asset> loadedAssets = new ArrayList<>();
+        TextPageLink pageLink = new TextPageLink(23);
+        TextPageData<Asset> pageData = null;
+        do {
+            pageData = assetService.findAssetsByTenantId(tenantId, pageLink);
+            loadedAssets.addAll(pageData.getData());
+            if (pageData.hasNext()) {
+                pageLink = pageData.getNextPageLink();
+            }
+        } while (pageData.hasNext());
+
+        Collections.sort(assets, idComparator);
+        Collections.sort(loadedAssets, idComparator);
+
+        Assert.assertEquals(assets, loadedAssets);
+
+        assetService.deleteAssetsByTenantId(tenantId);
+
+        pageLink = new TextPageLink(33);
+        pageData = assetService.findAssetsByTenantId(tenantId, pageLink);
+        Assert.assertFalse(pageData.hasNext());
+        Assert.assertTrue(pageData.getData().isEmpty());
+
+        tenantService.deleteTenant(tenantId);
+    }
+
+    @Test
+    public void testFindAssetsByTenantIdAndName() {
+        String title1 = "Asset title 1";
+        List<Asset> assetsTitle1 = new ArrayList<>();
+        for (int i=0;i<143;i++) {
+            Asset asset = new Asset();
+            asset.setTenantId(tenantId);
+            String suffix = RandomStringUtils.randomAlphanumeric(15);
+            String name = title1+suffix;
+            name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase();
+            asset.setName(name);
+            asset.setType("default");
+            assetsTitle1.add(assetService.saveAsset(asset));
+        }
+        String title2 = "Asset title 2";
+        List<Asset> assetsTitle2 = new ArrayList<>();
+        for (int i=0;i<175;i++) {
+            Asset asset = new Asset();
+            asset.setTenantId(tenantId);
+            String suffix = RandomStringUtils.randomAlphanumeric(15);
+            String name = title2+suffix;
+            name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase();
+            asset.setName(name);
+            asset.setType("default");
+            assetsTitle2.add(assetService.saveAsset(asset));
+        }
+
+        List<Asset> loadedAssetsTitle1 = new ArrayList<>();
+        TextPageLink pageLink = new TextPageLink(15, title1);
+        TextPageData<Asset> pageData = null;
+        do {
+            pageData = assetService.findAssetsByTenantId(tenantId, pageLink);
+            loadedAssetsTitle1.addAll(pageData.getData());
+            if (pageData.hasNext()) {
+                pageLink = pageData.getNextPageLink();
+            }
+        } while (pageData.hasNext());
+
+        Collections.sort(assetsTitle1, idComparator);
+        Collections.sort(loadedAssetsTitle1, idComparator);
+
+        Assert.assertEquals(assetsTitle1, loadedAssetsTitle1);
+
+        List<Asset> loadedAssetsTitle2 = new ArrayList<>();
+        pageLink = new TextPageLink(4, title2);
+        do {
+            pageData = assetService.findAssetsByTenantId(tenantId, pageLink);
+            loadedAssetsTitle2.addAll(pageData.getData());
+            if (pageData.hasNext()) {
+                pageLink = pageData.getNextPageLink();
+            }
+        } while (pageData.hasNext());
+
+        Collections.sort(assetsTitle2, idComparator);
+        Collections.sort(loadedAssetsTitle2, idComparator);
+
+        Assert.assertEquals(assetsTitle2, loadedAssetsTitle2);
+
+        for (Asset asset : loadedAssetsTitle1) {
+            assetService.deleteAsset(asset.getId());
+        }
+
+        pageLink = new TextPageLink(4, title1);
+        pageData = assetService.findAssetsByTenantId(tenantId, pageLink);
+        Assert.assertFalse(pageData.hasNext());
+        Assert.assertEquals(0, pageData.getData().size());
+
+        for (Asset asset : loadedAssetsTitle2) {
+            assetService.deleteAsset(asset.getId());
+        }
+
+        pageLink = new TextPageLink(4, title2);
+        pageData = assetService.findAssetsByTenantId(tenantId, pageLink);
+        Assert.assertFalse(pageData.hasNext());
+        Assert.assertEquals(0, pageData.getData().size());
+    }
+
+    @Test
+    public void testFindAssetsByTenantIdAndType() {
+        String title1 = "Asset title 1";
+        String type1 = "typeA";
+        List<Asset> assetsType1 = new ArrayList<>();
+        for (int i=0;i<143;i++) {
+            Asset asset = new Asset();
+            asset.setTenantId(tenantId);
+            String suffix = RandomStringUtils.randomAlphanumeric(15);
+            String name = title1+suffix;
+            name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase();
+            asset.setName(name);
+            asset.setType(type1);
+            assetsType1.add(assetService.saveAsset(asset));
+        }
+        String title2 = "Asset title 2";
+        String type2 = "typeB";
+        List<Asset> assetsType2 = new ArrayList<>();
+        for (int i=0;i<175;i++) {
+            Asset asset = new Asset();
+            asset.setTenantId(tenantId);
+            String suffix = RandomStringUtils.randomAlphanumeric(15);
+            String name = title2+suffix;
+            name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase();
+            asset.setName(name);
+            asset.setType(type2);
+            assetsType2.add(assetService.saveAsset(asset));
+        }
+
+        List<Asset> loadedAssetsType1 = new ArrayList<>();
+        TextPageLink pageLink = new TextPageLink(15);
+        TextPageData<Asset> pageData = null;
+        do {
+            pageData = assetService.findAssetsByTenantIdAndType(tenantId, type1, pageLink);
+            loadedAssetsType1.addAll(pageData.getData());
+            if (pageData.hasNext()) {
+                pageLink = pageData.getNextPageLink();
+            }
+        } while (pageData.hasNext());
+
+        Collections.sort(assetsType1, idComparator);
+        Collections.sort(loadedAssetsType1, idComparator);
+
+        Assert.assertEquals(assetsType1, loadedAssetsType1);
+
+        List<Asset> loadedAssetsType2 = new ArrayList<>();
+        pageLink = new TextPageLink(4);
+        do {
+            pageData = assetService.findAssetsByTenantIdAndType(tenantId, type2, pageLink);
+            loadedAssetsType2.addAll(pageData.getData());
+            if (pageData.hasNext()) {
+                pageLink = pageData.getNextPageLink();
+            }
+        } while (pageData.hasNext());
+
+        Collections.sort(assetsType2, idComparator);
+        Collections.sort(loadedAssetsType2, idComparator);
+
+        Assert.assertEquals(assetsType2, loadedAssetsType2);
+
+        for (Asset asset : loadedAssetsType1) {
+            assetService.deleteAsset(asset.getId());
+        }
+
+        pageLink = new TextPageLink(4);
+        pageData = assetService.findAssetsByTenantIdAndType(tenantId, type1, pageLink);
+        Assert.assertFalse(pageData.hasNext());
+        Assert.assertEquals(0, pageData.getData().size());
+
+        for (Asset asset : loadedAssetsType2) {
+            assetService.deleteAsset(asset.getId());
+        }
+
+        pageLink = new TextPageLink(4);
+        pageData = assetService.findAssetsByTenantIdAndType(tenantId, type2, pageLink);
+        Assert.assertFalse(pageData.hasNext());
+        Assert.assertEquals(0, pageData.getData().size());
+    }
+
+    @Test
+    public void testFindAssetsByTenantIdAndCustomerId() {
+        Tenant tenant = new Tenant();
+        tenant.setTitle("Test tenant");
+        tenant = tenantService.saveTenant(tenant);
+
+        TenantId tenantId = tenant.getId();
+
+        Customer customer = new Customer();
+        customer.setTitle("Test customer");
+        customer.setTenantId(tenantId);
+        customer = customerService.saveCustomer(customer);
+        CustomerId customerId = customer.getId();
+
+        List<Asset> assets = new ArrayList<>();
+        for (int i=0;i<278;i++) {
+            Asset asset = new Asset();
+            asset.setTenantId(tenantId);
+            asset.setName("Asset"+i);
+            asset.setType("default");
+            asset = assetService.saveAsset(asset);
+            assets.add(assetService.assignAssetToCustomer(asset.getId(), customerId));
+        }
+
+        List<Asset> loadedAssets = new ArrayList<>();
+        TextPageLink pageLink = new TextPageLink(23);
+        TextPageData<Asset> pageData = null;
+        do {
+            pageData = assetService.findAssetsByTenantIdAndCustomerId(tenantId, customerId, pageLink);
+            loadedAssets.addAll(pageData.getData());
+            if (pageData.hasNext()) {
+                pageLink = pageData.getNextPageLink();
+            }
+        } while (pageData.hasNext());
+
+        Collections.sort(assets, idComparator);
+        Collections.sort(loadedAssets, idComparator);
+
+        Assert.assertEquals(assets, loadedAssets);
+
+        assetService.unassignCustomerAssets(tenantId, customerId);
+
+        pageLink = new TextPageLink(33);
+        pageData = assetService.findAssetsByTenantIdAndCustomerId(tenantId, customerId, pageLink);
+        Assert.assertFalse(pageData.hasNext());
+        Assert.assertTrue(pageData.getData().isEmpty());
+
+        tenantService.deleteTenant(tenantId);
+    }
+
+    @Test
+    public void testFindAssetsByTenantIdCustomerIdAndName() {
+
+        Customer customer = new Customer();
+        customer.setTitle("Test customer");
+        customer.setTenantId(tenantId);
+        customer = customerService.saveCustomer(customer);
+        CustomerId customerId = customer.getId();
+
+        String title1 = "Asset title 1";
+        List<Asset> assetsTitle1 = new ArrayList<>();
+        for (int i=0;i<175;i++) {
+            Asset asset = new Asset();
+            asset.setTenantId(tenantId);
+            String suffix = RandomStringUtils.randomAlphanumeric(15);
+            String name = title1+suffix;
+            name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase();
+            asset.setName(name);
+            asset.setType("default");
+            asset = assetService.saveAsset(asset);
+            assetsTitle1.add(assetService.assignAssetToCustomer(asset.getId(), customerId));
+        }
+        String title2 = "Asset title 2";
+        List<Asset> assetsTitle2 = new ArrayList<>();
+        for (int i=0;i<143;i++) {
+            Asset asset = new Asset();
+            asset.setTenantId(tenantId);
+            String suffix = RandomStringUtils.randomAlphanumeric(15);
+            String name = title2+suffix;
+            name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase();
+            asset.setName(name);
+            asset.setType("default");
+            asset = assetService.saveAsset(asset);
+            assetsTitle2.add(assetService.assignAssetToCustomer(asset.getId(), customerId));
+        }
+
+        List<Asset> loadedAssetsTitle1 = new ArrayList<>();
+        TextPageLink pageLink = new TextPageLink(15, title1);
+        TextPageData<Asset> pageData = null;
+        do {
+            pageData = assetService.findAssetsByTenantIdAndCustomerId(tenantId, customerId, pageLink);
+            loadedAssetsTitle1.addAll(pageData.getData());
+            if (pageData.hasNext()) {
+                pageLink = pageData.getNextPageLink();
+            }
+        } while (pageData.hasNext());
+
+        Collections.sort(assetsTitle1, idComparator);
+        Collections.sort(loadedAssetsTitle1, idComparator);
+
+        Assert.assertEquals(assetsTitle1, loadedAssetsTitle1);
+
+        List<Asset> loadedAssetsTitle2 = new ArrayList<>();
+        pageLink = new TextPageLink(4, title2);
+        do {
+            pageData = assetService.findAssetsByTenantIdAndCustomerId(tenantId, customerId, pageLink);
+            loadedAssetsTitle2.addAll(pageData.getData());
+            if (pageData.hasNext()) {
+                pageLink = pageData.getNextPageLink();
+            }
+        } while (pageData.hasNext());
+
+        Collections.sort(assetsTitle2, idComparator);
+        Collections.sort(loadedAssetsTitle2, idComparator);
+
+        Assert.assertEquals(assetsTitle2, loadedAssetsTitle2);
+
+        for (Asset asset : loadedAssetsTitle1) {
+            assetService.deleteAsset(asset.getId());
+        }
+
+        pageLink = new TextPageLink(4, title1);
+        pageData = assetService.findAssetsByTenantIdAndCustomerId(tenantId, customerId, pageLink);
+        Assert.assertFalse(pageData.hasNext());
+        Assert.assertEquals(0, pageData.getData().size());
+
+        for (Asset asset : loadedAssetsTitle2) {
+            assetService.deleteAsset(asset.getId());
+        }
+
+        pageLink = new TextPageLink(4, title2);
+        pageData = assetService.findAssetsByTenantIdAndCustomerId(tenantId, customerId, pageLink);
+        Assert.assertFalse(pageData.hasNext());
+        Assert.assertEquals(0, pageData.getData().size());
+        customerService.deleteCustomer(customerId);
+    }
+
+    @Test
+    public void testFindAssetsByTenantIdCustomerIdAndType() {
+
+        Customer customer = new Customer();
+        customer.setTitle("Test customer");
+        customer.setTenantId(tenantId);
+        customer = customerService.saveCustomer(customer);
+        CustomerId customerId = customer.getId();
+
+        String title1 = "Asset title 1";
+        String type1 = "typeC";
+        List<Asset> assetsType1 = new ArrayList<>();
+        for (int i=0;i<175;i++) {
+            Asset asset = new Asset();
+            asset.setTenantId(tenantId);
+            String suffix = RandomStringUtils.randomAlphanumeric(15);
+            String name = title1+suffix;
+            name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase();
+            asset.setName(name);
+            asset.setType(type1);
+            asset = assetService.saveAsset(asset);
+            assetsType1.add(assetService.assignAssetToCustomer(asset.getId(), customerId));
+        }
+        String title2 = "Asset title 2";
+        String type2 = "typeD";
+        List<Asset> assetsType2 = new ArrayList<>();
+        for (int i=0;i<143;i++) {
+            Asset asset = new Asset();
+            asset.setTenantId(tenantId);
+            String suffix = RandomStringUtils.randomAlphanumeric(15);
+            String name = title2+suffix;
+            name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase();
+            asset.setName(name);
+            asset.setType(type2);
+            asset = assetService.saveAsset(asset);
+            assetsType2.add(assetService.assignAssetToCustomer(asset.getId(), customerId));
+        }
+
+        List<Asset> loadedAssetsType1 = new ArrayList<>();
+        TextPageLink pageLink = new TextPageLink(15);
+        TextPageData<Asset> pageData = null;
+        do {
+            pageData = assetService.findAssetsByTenantIdAndCustomerIdAndType(tenantId, customerId, type1, pageLink);
+            loadedAssetsType1.addAll(pageData.getData());
+            if (pageData.hasNext()) {
+                pageLink = pageData.getNextPageLink();
+            }
+        } while (pageData.hasNext());
+
+        Collections.sort(assetsType1, idComparator);
+        Collections.sort(loadedAssetsType1, idComparator);
+
+        Assert.assertEquals(assetsType1, loadedAssetsType1);
+
+        List<Asset> loadedAssetsType2 = new ArrayList<>();
+        pageLink = new TextPageLink(4);
+        do {
+            pageData = assetService.findAssetsByTenantIdAndCustomerIdAndType(tenantId, customerId, type2, pageLink);
+            loadedAssetsType2.addAll(pageData.getData());
+            if (pageData.hasNext()) {
+                pageLink = pageData.getNextPageLink();
+            }
+        } while (pageData.hasNext());
+
+        Collections.sort(assetsType2, idComparator);
+        Collections.sort(loadedAssetsType2, idComparator);
+
+        Assert.assertEquals(assetsType2, loadedAssetsType2);
+
+        for (Asset asset : loadedAssetsType1) {
+            assetService.deleteAsset(asset.getId());
+        }
+
+        pageLink = new TextPageLink(4);
+        pageData = assetService.findAssetsByTenantIdAndCustomerIdAndType(tenantId, customerId, type1, pageLink);
+        Assert.assertFalse(pageData.hasNext());
+        Assert.assertEquals(0, pageData.getData().size());
+
+        for (Asset asset : loadedAssetsType2) {
+            assetService.deleteAsset(asset.getId());
+        }
+
+        pageLink = new TextPageLink(4);
+        pageData = assetService.findAssetsByTenantIdAndCustomerIdAndType(tenantId, customerId, type2, pageLink);
+        Assert.assertFalse(pageData.hasNext());
+        Assert.assertEquals(0, pageData.getData().size());
+        customerService.deleteCustomer(customerId);
+    }
+
+}
diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/DeviceCredentialsServiceImplTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/DeviceCredentialsServiceImplTest.java
index efdab8a..4d9ef9f 100644
--- a/dao/src/test/java/org/thingsboard/server/dao/service/DeviceCredentialsServiceImplTest.java
+++ b/dao/src/test/java/org/thingsboard/server/dao/service/DeviceCredentialsServiceImplTest.java
@@ -58,6 +58,7 @@ public class DeviceCredentialsServiceImplTest extends AbstractServiceTest {
     public void testSaveDeviceCredentialsWithEmptyDevice() {
         Device device = new Device();
         device.setName("My device");
+        device.setType("default");
         device.setTenantId(tenantId);
         device = deviceService.saveDevice(device);
         DeviceCredentials deviceCredentials = deviceCredentialsService.findDeviceCredentialsByDeviceId(device.getId());
@@ -73,6 +74,7 @@ public class DeviceCredentialsServiceImplTest extends AbstractServiceTest {
     public void testSaveDeviceCredentialsWithEmptyCredentialsType() {
         Device device = new Device();
         device.setName("My device");
+        device.setType("default");
         device.setTenantId(tenantId);
         device = deviceService.saveDevice(device);
         DeviceCredentials deviceCredentials = deviceCredentialsService.findDeviceCredentialsByDeviceId(device.getId());
@@ -88,6 +90,7 @@ public class DeviceCredentialsServiceImplTest extends AbstractServiceTest {
     public void testSaveDeviceCredentialsWithEmptyCredentialsId() {
         Device device = new Device();
         device.setName("My device");
+        device.setType("default");
         device.setTenantId(tenantId);
         device = deviceService.saveDevice(device);
         DeviceCredentials deviceCredentials = deviceCredentialsService.findDeviceCredentialsByDeviceId(device.getId());
@@ -103,6 +106,7 @@ public class DeviceCredentialsServiceImplTest extends AbstractServiceTest {
     public void testSaveNonExistentDeviceCredentials() {
         Device device = new Device();
         device.setName("My device");
+        device.setType("default");
         device.setTenantId(tenantId);
         device = deviceService.saveDevice(device);
         DeviceCredentials deviceCredentials = deviceCredentialsService.findDeviceCredentialsByDeviceId(device.getId());
@@ -122,6 +126,7 @@ public class DeviceCredentialsServiceImplTest extends AbstractServiceTest {
     public void testSaveDeviceCredentialsWithNonExistentDevice() {
         Device device = new Device();
         device.setName("My device");
+        device.setType("default");
         device.setTenantId(tenantId);
         device = deviceService.saveDevice(device);
         DeviceCredentials deviceCredentials = deviceCredentialsService.findDeviceCredentialsByDeviceId(device.getId());
@@ -137,6 +142,7 @@ public class DeviceCredentialsServiceImplTest extends AbstractServiceTest {
     public void testSaveDeviceCredentialsWithInvalidCredemtialsIdLength() {
         Device device = new Device();
         device.setName("My device");
+        device.setType("default");
         device.setTenantId(tenantId);
         device = deviceService.saveDevice(device);
         DeviceCredentials deviceCredentials = deviceCredentialsService.findDeviceCredentialsByDeviceId(device.getId());
@@ -153,6 +159,7 @@ public class DeviceCredentialsServiceImplTest extends AbstractServiceTest {
         Device device = new Device();
         device.setTenantId(tenantId);
         device.setName("My device");
+        device.setType("default");
         Device savedDevice = deviceService.saveDevice(device);
         DeviceCredentials deviceCredentials = deviceCredentialsService.findDeviceCredentialsByDeviceId(savedDevice.getId());
         Assert.assertEquals(savedDevice.getId(), deviceCredentials.getDeviceId());
@@ -166,6 +173,7 @@ public class DeviceCredentialsServiceImplTest extends AbstractServiceTest {
         Device device = new Device();
         device.setTenantId(tenantId);
         device.setName("My device");
+        device.setType("default");
         Device savedDevice = deviceService.saveDevice(device);
         DeviceCredentials deviceCredentials = deviceCredentialsService.findDeviceCredentialsByDeviceId(savedDevice.getId());
         Assert.assertEquals(savedDevice.getId(), deviceCredentials.getDeviceId());
@@ -181,6 +189,7 @@ public class DeviceCredentialsServiceImplTest extends AbstractServiceTest {
         Device device = new Device();
         device.setTenantId(tenantId);
         device.setName("My device");
+        device.setType("default");
         Device savedDevice = deviceService.saveDevice(device);
         DeviceCredentials deviceCredentials = deviceCredentialsService.findDeviceCredentialsByDeviceId(savedDevice.getId());
         Assert.assertEquals(savedDevice.getId(), deviceCredentials.getDeviceId());
diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/DeviceServiceImplTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/DeviceServiceImplTest.java
index e9113d4..4f0bc8e 100644
--- a/dao/src/test/java/org/thingsboard/server/dao/service/DeviceServiceImplTest.java
+++ b/dao/src/test/java/org/thingsboard/server/dao/service/DeviceServiceImplTest.java
@@ -24,6 +24,7 @@ import org.junit.Test;
 import org.thingsboard.server.common.data.Customer;
 import org.thingsboard.server.common.data.Device;
 import org.thingsboard.server.common.data.Tenant;
+import org.thingsboard.server.common.data.TenantDeviceType;
 import org.thingsboard.server.common.data.id.CustomerId;
 import org.thingsboard.server.common.data.id.DeviceCredentialsId;
 import org.thingsboard.server.common.data.id.DeviceId;
@@ -37,6 +38,7 @@ import org.thingsboard.server.dao.exception.DataValidationException;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
+import java.util.concurrent.Executors;
 
 import static org.thingsboard.server.dao.model.ModelConstants.NULL_UUID;
 
@@ -65,6 +67,7 @@ public class DeviceServiceImplTest extends AbstractServiceTest {
         Device device = new Device();
         device.setTenantId(tenantId);
         device.setName("My device");
+        device.setType("default");
         Device savedDevice = deviceService.saveDevice(device);
         
         Assert.assertNotNull(savedDevice);
@@ -95,6 +98,7 @@ public class DeviceServiceImplTest extends AbstractServiceTest {
     @Test(expected = DataValidationException.class)
     public void testSaveDeviceWithEmptyName() {
         Device device = new Device();
+        device.setType("default");
         device.setTenantId(tenantId);
         deviceService.saveDevice(device);
     }
@@ -103,6 +107,7 @@ public class DeviceServiceImplTest extends AbstractServiceTest {
     public void testSaveDeviceWithEmptyTenant() {
         Device device = new Device();
         device.setName("My device");
+        device.setType("default");
         deviceService.saveDevice(device);
     }
     
@@ -110,6 +115,7 @@ public class DeviceServiceImplTest extends AbstractServiceTest {
     public void testSaveDeviceWithInvalidTenant() {
         Device device = new Device();
         device.setName("My device");
+        device.setType("default");
         device.setTenantId(new TenantId(UUIDs.timeBased()));
         deviceService.saveDevice(device);
     }
@@ -118,6 +124,7 @@ public class DeviceServiceImplTest extends AbstractServiceTest {
     public void testAssignDeviceToNonExistentCustomer() {
         Device device = new Device();
         device.setName("My device");
+        device.setType("default");
         device.setTenantId(tenantId);
         device = deviceService.saveDevice(device);
         try {
@@ -131,6 +138,7 @@ public class DeviceServiceImplTest extends AbstractServiceTest {
     public void testAssignDeviceToCustomerFromDifferentTenant() {
         Device device = new Device();
         device.setName("My device");
+        device.setType("default");
         device.setTenantId(tenantId);
         device = deviceService.saveDevice(device);
         Tenant tenant = new Tenant();
@@ -153,18 +161,56 @@ public class DeviceServiceImplTest extends AbstractServiceTest {
         Device device = new Device();
         device.setTenantId(tenantId);
         device.setName("My device");
+        device.setType("default");
         Device savedDevice = deviceService.saveDevice(device);
         Device foundDevice = deviceService.findDeviceById(savedDevice.getId());
         Assert.assertNotNull(foundDevice);
         Assert.assertEquals(savedDevice, foundDevice);
         deviceService.deleteDevice(savedDevice.getId());
     }
+
+    @Test
+    public void testFindDeviceTypesByTenantId() throws Exception {
+        List<Device> devices = new ArrayList<>();
+        try {
+            for (int i=0;i<3;i++) {
+                Device device = new Device();
+                device.setTenantId(tenantId);
+                device.setName("My device B"+i);
+                device.setType("typeB");
+                devices.add(deviceService.saveDevice(device));
+            }
+            for (int i=0;i<7;i++) {
+                Device device = new Device();
+                device.setTenantId(tenantId);
+                device.setName("My device C"+i);
+                device.setType("typeC");
+                devices.add(deviceService.saveDevice(device));
+            }
+            for (int i=0;i<9;i++) {
+                Device device = new Device();
+                device.setTenantId(tenantId);
+                device.setName("My device A"+i);
+                device.setType("typeA");
+                devices.add(deviceService.saveDevice(device));
+            }
+            List<TenantDeviceType> deviceTypes = deviceService.findDeviceTypesByTenantId(tenantId).get();
+            Assert.assertNotNull(deviceTypes);
+            Assert.assertEquals(3, deviceTypes.size());
+            Assert.assertEquals("typeA", deviceTypes.get(0).getType());
+            Assert.assertEquals("typeB", deviceTypes.get(1).getType());
+            Assert.assertEquals("typeC", deviceTypes.get(2).getType());
+        } finally {
+            devices.forEach((device) -> { deviceService.deleteDevice(device.getId()); });
+        }
+    }
     
     @Test
     public void testDeleteDevice() {
         Device device = new Device();
         device.setTenantId(tenantId);
         device.setName("My device");
+        device.setType("default");
         Device savedDevice = deviceService.saveDevice(device);
         Device foundDevice = deviceService.findDeviceById(savedDevice.getId());
         Assert.assertNotNull(foundDevice);
@@ -188,6 +234,7 @@ public class DeviceServiceImplTest extends AbstractServiceTest {
             Device device = new Device();
             device.setTenantId(tenantId);
             device.setName("Device"+i);
+            device.setType("default");
             devices.add(deviceService.saveDevice(device));
         }
         
@@ -216,7 +263,7 @@ public class DeviceServiceImplTest extends AbstractServiceTest {
         
         tenantService.deleteTenant(tenantId);
     }
-    
+
     @Test
     public void testFindDevicesByTenantIdAndName() {
         String title1 = "Device title 1";
@@ -228,6 +275,7 @@ public class DeviceServiceImplTest extends AbstractServiceTest {
             String name = title1+suffix;
             name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase();
             device.setName(name);
+            device.setType("default");
             devicesTitle1.add(deviceService.saveDevice(device));
         }
         String title2 = "Device title 2";
@@ -239,6 +287,7 @@ public class DeviceServiceImplTest extends AbstractServiceTest {
             String name = title2+suffix;
             name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase();
             device.setName(name);
+            device.setType("default");
             devicesTitle2.add(deviceService.saveDevice(device));
         }
         
@@ -291,6 +340,85 @@ public class DeviceServiceImplTest extends AbstractServiceTest {
         Assert.assertFalse(pageData.hasNext());
         Assert.assertEquals(0, pageData.getData().size());
     }
+
+    @Test
+    public void testFindDevicesByTenantIdAndType() {
+        String title1 = "Device title 1";
+        String type1 = "typeA";
+        List<Device> devicesType1 = new ArrayList<>();
+        for (int i=0;i<143;i++) {
+            Device device = new Device();
+            device.setTenantId(tenantId);
+            String suffix = RandomStringUtils.randomAlphanumeric(15);
+            String name = title1+suffix;
+            name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase();
+            device.setName(name);
+            device.setType(type1);
+            devicesType1.add(deviceService.saveDevice(device));
+        }
+        String title2 = "Device title 2";
+        String type2 = "typeB";
+        List<Device> devicesType2 = new ArrayList<>();
+        for (int i=0;i<175;i++) {
+            Device device = new Device();
+            device.setTenantId(tenantId);
+            String suffix = RandomStringUtils.randomAlphanumeric(15);
+            String name = title2+suffix;
+            name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase();
+            device.setName(name);
+            device.setType(type2);
+            devicesType2.add(deviceService.saveDevice(device));
+        }
+
+        List<Device> loadedDevicesType1 = new ArrayList<>();
+        TextPageLink pageLink = new TextPageLink(15);
+        TextPageData<Device> pageData = null;
+        do {
+            pageData = deviceService.findDevicesByTenantIdAndType(tenantId, type1, pageLink);
+            loadedDevicesType1.addAll(pageData.getData());
+            if (pageData.hasNext()) {
+                pageLink = pageData.getNextPageLink();
+            }
+        } while (pageData.hasNext());
+
+        Collections.sort(devicesType1, idComparator);
+        Collections.sort(loadedDevicesType1, idComparator);
+
+        Assert.assertEquals(devicesType1, loadedDevicesType1);
+
+        List<Device> loadedDevicesType2 = new ArrayList<>();
+        pageLink = new TextPageLink(4);
+        do {
+            pageData = deviceService.findDevicesByTenantIdAndType(tenantId, type2, pageLink);
+            loadedDevicesType2.addAll(pageData.getData());
+            if (pageData.hasNext()) {
+                pageLink = pageData.getNextPageLink();
+            }
+        } while (pageData.hasNext());
+
+        Collections.sort(devicesType2, idComparator);
+        Collections.sort(loadedDevicesType2, idComparator);
+
+        Assert.assertEquals(devicesType2, loadedDevicesType2);
+
+        for (Device device : loadedDevicesType1) {
+            deviceService.deleteDevice(device.getId());
+        }
+
+        pageLink = new TextPageLink(4);
+        pageData = deviceService.findDevicesByTenantIdAndType(tenantId, type1, pageLink);
+        Assert.assertFalse(pageData.hasNext());
+        Assert.assertEquals(0, pageData.getData().size());
+
+        for (Device device : loadedDevicesType2) {
+            deviceService.deleteDevice(device.getId());
+        }
+
+        pageLink = new TextPageLink(4);
+        pageData = deviceService.findDevicesByTenantIdAndType(tenantId, type2, pageLink);
+        Assert.assertFalse(pageData.hasNext());
+        Assert.assertEquals(0, pageData.getData().size());
+    }
     
     @Test
     public void testFindDevicesByTenantIdAndCustomerId() {
@@ -311,6 +439,7 @@ public class DeviceServiceImplTest extends AbstractServiceTest {
             Device device = new Device();
             device.setTenantId(tenantId);
             device.setName("Device"+i);
+            device.setType("default");
             device = deviceService.saveDevice(device);
             devices.add(deviceService.assignDeviceToCustomer(device.getId(), customerId));
         }
@@ -359,6 +488,7 @@ public class DeviceServiceImplTest extends AbstractServiceTest {
             String name = title1+suffix;
             name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase();
             device.setName(name);
+            device.setType("default");
             device = deviceService.saveDevice(device);
             devicesTitle1.add(deviceService.assignDeviceToCustomer(device.getId(), customerId));
         }
@@ -371,6 +501,7 @@ public class DeviceServiceImplTest extends AbstractServiceTest {
             String name = title2+suffix;
             name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase();
             device.setName(name);
+            device.setType("default");
             device = deviceService.saveDevice(device);
             devicesTitle2.add(deviceService.assignDeviceToCustomer(device.getId(), customerId));
         }
@@ -425,4 +556,94 @@ public class DeviceServiceImplTest extends AbstractServiceTest {
         Assert.assertEquals(0, pageData.getData().size());
         customerService.deleteCustomer(customerId);
     }
+
+    @Test
+    public void testFindDevicesByTenantIdCustomerIdAndType() {
+
+        Customer customer = new Customer();
+        customer.setTitle("Test customer");
+        customer.setTenantId(tenantId);
+        customer = customerService.saveCustomer(customer);
+        CustomerId customerId = customer.getId();
+
+        String title1 = "Device title 1";
+        String type1 = "typeC";
+        List<Device> devicesType1 = new ArrayList<>();
+        for (int i=0;i<175;i++) {
+            Device device = new Device();
+            device.setTenantId(tenantId);
+            String suffix = RandomStringUtils.randomAlphanumeric(15);
+            String name = title1+suffix;
+            name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase();
+            device.setName(name);
+            device.setType(type1);
+            device = deviceService.saveDevice(device);
+            devicesType1.add(deviceService.assignDeviceToCustomer(device.getId(), customerId));
+        }
+        String title2 = "Device title 2";
+        String type2 = "typeD";
+        List<Device> devicesType2 = new ArrayList<>();
+        for (int i=0;i<143;i++) {
+            Device device = new Device();
+            device.setTenantId(tenantId);
+            String suffix = RandomStringUtils.randomAlphanumeric(15);
+            String name = title2+suffix;
+            name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase();
+            device.setName(name);
+            device.setType(type2);
+            device = deviceService.saveDevice(device);
+            devicesType2.add(deviceService.assignDeviceToCustomer(device.getId(), customerId));
+        }
+
+        List<Device> loadedDevicesType1 = new ArrayList<>();
+        TextPageLink pageLink = new TextPageLink(15);
+        TextPageData<Device> pageData = null;
+        do {
+            pageData = deviceService.findDevicesByTenantIdAndCustomerIdAndType(tenantId, customerId, type1, pageLink);
+            loadedDevicesType1.addAll(pageData.getData());
+            if (pageData.hasNext()) {
+                pageLink = pageData.getNextPageLink();
+            }
+        } while (pageData.hasNext());
+
+        Collections.sort(devicesType1, idComparator);
+        Collections.sort(loadedDevicesType1, idComparator);
+
+        Assert.assertEquals(devicesType1, loadedDevicesType1);
+
+        List<Device> loadedDevicesType2 = new ArrayList<>();
+        pageLink = new TextPageLink(4);
+        do {
+            pageData = deviceService.findDevicesByTenantIdAndCustomerIdAndType(tenantId, customerId, type2, pageLink);
+            loadedDevicesType2.addAll(pageData.getData());
+            if (pageData.hasNext()) {
+                pageLink = pageData.getNextPageLink();
+            }
+        } while (pageData.hasNext());
+
+        Collections.sort(devicesType2, idComparator);
+        Collections.sort(loadedDevicesType2, idComparator);
+
+        Assert.assertEquals(devicesType2, loadedDevicesType2);
+
+        for (Device device : loadedDevicesType1) {
+            deviceService.deleteDevice(device.getId());
+        }
+
+        pageLink = new TextPageLink(4);
+        pageData = deviceService.findDevicesByTenantIdAndCustomerIdAndType(tenantId, customerId, type1, pageLink);
+        Assert.assertFalse(pageData.hasNext());
+        Assert.assertEquals(0, pageData.getData().size());
+
+        for (Device device : loadedDevicesType2) {
+            deviceService.deleteDevice(device.getId());
+        }
+
+        pageLink = new TextPageLink(4);
+        pageData = deviceService.findDevicesByTenantIdAndCustomerIdAndType(tenantId, customerId, type2, pageLink);
+        Assert.assertFalse(pageData.hasNext());
+        Assert.assertEquals(0, pageData.getData().size());
+        customerService.deleteCustomer(customerId);
+    }
+
 }