Commit 83ea3f083686a9de050ca6d54fe55ccea25e51e6

Authored by Igor Kulikov
1 parent 43596ec5

Move from device type to device profile

... ... @@ -47,6 +47,7 @@ import org.thingsboard.server.common.data.exception.ThingsboardErrorCode;
47 47 import org.thingsboard.server.common.data.exception.ThingsboardException;
48 48 import org.thingsboard.server.common.data.id.CustomerId;
49 49 import org.thingsboard.server.common.data.id.DeviceId;
  50 +import org.thingsboard.server.common.data.id.DeviceProfileId;
50 51 import org.thingsboard.server.common.data.id.TenantId;
51 52 import org.thingsboard.server.common.data.page.PageData;
52 53 import org.thingsboard.server.common.data.page.PageLink;
... ... @@ -315,6 +316,7 @@ public class DeviceController extends BaseController {
315 316 @RequestParam int pageSize,
316 317 @RequestParam int page,
317 318 @RequestParam(required = false) String type,
  319 + @RequestParam(required = false) String deviceProfileId,
318 320 @RequestParam(required = false) String textSearch,
319 321 @RequestParam(required = false) String sortProperty,
320 322 @RequestParam(required = false) String sortOrder) throws ThingsboardException {
... ... @@ -323,6 +325,9 @@ public class DeviceController extends BaseController {
323 325 PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder);
324 326 if (type != null && type.trim().length() > 0) {
325 327 return checkNotNull(deviceService.findDeviceInfosByTenantIdAndType(tenantId, type, pageLink));
  328 + } else if (deviceProfileId != null && deviceProfileId.length() > 0) {
  329 + DeviceProfileId profileId = new DeviceProfileId(toUUID(deviceProfileId));
  330 + return checkNotNull(deviceService.findDeviceInfosByTenantIdAndDeviceProfileId(tenantId, profileId, pageLink));
326 331 } else {
327 332 return checkNotNull(deviceService.findDeviceInfosByTenantId(tenantId, pageLink));
328 333 }
... ... @@ -379,6 +384,7 @@ public class DeviceController extends BaseController {
379 384 @RequestParam int pageSize,
380 385 @RequestParam int page,
381 386 @RequestParam(required = false) String type,
  387 + @RequestParam(required = false) String deviceProfileId,
382 388 @RequestParam(required = false) String textSearch,
383 389 @RequestParam(required = false) String sortProperty,
384 390 @RequestParam(required = false) String sortOrder) throws ThingsboardException {
... ... @@ -390,6 +396,9 @@ public class DeviceController extends BaseController {
390 396 PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder);
391 397 if (type != null && type.trim().length() > 0) {
392 398 return checkNotNull(deviceService.findDeviceInfosByTenantIdAndCustomerIdAndType(tenantId, customerId, type, pageLink));
  399 + } else if (deviceProfileId != null && deviceProfileId.length() > 0) {
  400 + DeviceProfileId profileId = new DeviceProfileId(toUUID(deviceProfileId));
  401 + return checkNotNull(deviceService.findDeviceInfosByTenantIdAndCustomerIdAndDeviceProfileId(tenantId, customerId, profileId, pageLink));
393 402 } else {
394 403 return checkNotNull(deviceService.findDeviceInfosByTenantIdAndCustomerId(tenantId, customerId, pageLink));
395 404 }
... ...
... ... @@ -27,6 +27,7 @@ import org.thingsboard.server.common.data.AdminSettings;
27 27 import org.thingsboard.server.common.data.Customer;
28 28 import org.thingsboard.server.common.data.DataConstants;
29 29 import org.thingsboard.server.common.data.Device;
  30 +import org.thingsboard.server.common.data.DeviceProfile;
30 31 import org.thingsboard.server.common.data.Tenant;
31 32 import org.thingsboard.server.common.data.TenantProfile;
32 33 import org.thingsboard.server.common.data.TenantProfileData;
... ... @@ -34,6 +35,7 @@ import org.thingsboard.server.common.data.User;
34 35 import org.thingsboard.server.common.data.asset.Asset;
35 36 import org.thingsboard.server.common.data.id.CustomerId;
36 37 import org.thingsboard.server.common.data.id.DeviceId;
  38 +import org.thingsboard.server.common.data.id.DeviceProfileId;
37 39 import org.thingsboard.server.common.data.id.TenantId;
38 40 import org.thingsboard.server.common.data.kv.BaseAttributeKvEntry;
39 41 import org.thingsboard.server.common.data.kv.BooleanDataEntry;
... ... @@ -48,6 +50,7 @@ import org.thingsboard.server.dao.asset.AssetService;
48 50 import org.thingsboard.server.dao.attributes.AttributesService;
49 51 import org.thingsboard.server.dao.customer.CustomerService;
50 52 import org.thingsboard.server.dao.device.DeviceCredentialsService;
  53 +import org.thingsboard.server.dao.device.DeviceProfileService;
51 54 import org.thingsboard.server.dao.device.DeviceService;
52 55 import org.thingsboard.server.dao.exception.DataValidationException;
53 56 import org.thingsboard.server.dao.relation.RelationService;
... ... @@ -102,6 +105,9 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService {
102 105 private DeviceService deviceService;
103 106
104 107 @Autowired
  108 + private DeviceProfileService deviceProfileService;
  109 +
  110 + @Autowired
105 111 private AttributesService attributesService;
106 112
107 113 @Autowired
... ... @@ -213,16 +219,18 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService {
213 219 createUser(Authority.CUSTOMER_USER, demoTenant.getId(), customerB.getId(), "customerB@thingsboard.org", CUSTOMER_CRED);
214 220 createUser(Authority.CUSTOMER_USER, demoTenant.getId(), customerC.getId(), "customerC@thingsboard.org", CUSTOMER_CRED);
215 221
216   - createDevice(demoTenant.getId(), customerA.getId(), DEFAULT_DEVICE_TYPE, "Test Device A1", "A1_TEST_TOKEN", null);
217   - createDevice(demoTenant.getId(), customerA.getId(), DEFAULT_DEVICE_TYPE, "Test Device A2", "A2_TEST_TOKEN", null);
218   - createDevice(demoTenant.getId(), customerA.getId(), DEFAULT_DEVICE_TYPE, "Test Device A3", "A3_TEST_TOKEN", null);
219   - createDevice(demoTenant.getId(), customerB.getId(), DEFAULT_DEVICE_TYPE, "Test Device B1", "B1_TEST_TOKEN", null);
220   - createDevice(demoTenant.getId(), customerC.getId(), DEFAULT_DEVICE_TYPE, "Test Device C1", "C1_TEST_TOKEN", null);
  222 + DeviceProfile defaultDeviceProfile = this.deviceProfileService.findOrCreateDeviceProfile(demoTenant.getId(), DEFAULT_DEVICE_TYPE);
221 223
222   - createDevice(demoTenant.getId(), null, DEFAULT_DEVICE_TYPE, "DHT11 Demo Device", "DHT11_DEMO_TOKEN", "Demo device that is used in sample " +
  224 + createDevice(demoTenant.getId(), customerA.getId(), defaultDeviceProfile.getId(), "Test Device A1", "A1_TEST_TOKEN", null);
  225 + createDevice(demoTenant.getId(), customerA.getId(), defaultDeviceProfile.getId(), "Test Device A2", "A2_TEST_TOKEN", null);
  226 + createDevice(demoTenant.getId(), customerA.getId(), defaultDeviceProfile.getId(), "Test Device A3", "A3_TEST_TOKEN", null);
  227 + createDevice(demoTenant.getId(), customerB.getId(), defaultDeviceProfile.getId(), "Test Device B1", "B1_TEST_TOKEN", null);
  228 + createDevice(demoTenant.getId(), customerC.getId(), defaultDeviceProfile.getId(), "Test Device C1", "C1_TEST_TOKEN", null);
  229 +
  230 + createDevice(demoTenant.getId(), null, defaultDeviceProfile.getId(), "DHT11 Demo Device", "DHT11_DEMO_TOKEN", "Demo device that is used in sample " +
223 231 "applications that upload data from DHT11 temperature and humidity sensor");
224 232
225   - createDevice(demoTenant.getId(), null, DEFAULT_DEVICE_TYPE, "Raspberry Pi Demo Device", "RASPBERRY_PI_DEMO_TOKEN", "Demo device that is used in " +
  233 + createDevice(demoTenant.getId(), null, defaultDeviceProfile.getId(), "Raspberry Pi Demo Device", "RASPBERRY_PI_DEMO_TOKEN", "Demo device that is used in " +
226 234 "Raspberry Pi GPIO control sample application");
227 235
228 236 Asset thermostatAlarms = new Asset();
... ... @@ -231,8 +239,10 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService {
231 239 thermostatAlarms.setType("AlarmPropagationAsset");
232 240 thermostatAlarms = assetService.saveAsset(thermostatAlarms);
233 241
234   - DeviceId t1Id = createDevice(demoTenant.getId(), null, "thermostat", "Thermostat T1", "T1_TEST_TOKEN", "Demo device for Thermostats dashboard").getId();
235   - DeviceId t2Id = createDevice(demoTenant.getId(), null, "thermostat", "Thermostat T2", "T2_TEST_TOKEN", "Demo device for Thermostats dashboard").getId();
  242 + DeviceProfile thermostatDeviceProfile = this.deviceProfileService.findOrCreateDeviceProfile(demoTenant.getId(), "thermostat");
  243 +
  244 + DeviceId t1Id = createDevice(demoTenant.getId(), null, thermostatDeviceProfile.getId(), "Thermostat T1", "T1_TEST_TOKEN", "Demo device for Thermostats dashboard").getId();
  245 + DeviceId t2Id = createDevice(demoTenant.getId(), null, thermostatDeviceProfile.getId(), "Thermostat T2", "T2_TEST_TOKEN", "Demo device for Thermostats dashboard").getId();
236 246
237 247 relationService.saveRelation(thermostatAlarms.getTenantId(), new EntityRelation(thermostatAlarms.getId(), t1Id, "ToAlarmPropagationAsset"));
238 248 relationService.saveRelation(thermostatAlarms.getTenantId(), new EntityRelation(thermostatAlarms.getId(), t2Id, "ToAlarmPropagationAsset"));
... ... @@ -308,14 +318,14 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService {
308 318
309 319 private Device createDevice(TenantId tenantId,
310 320 CustomerId customerId,
311   - String type,
  321 + DeviceProfileId deviceProfileId,
312 322 String name,
313 323 String accessToken,
314 324 String description) {
315 325 Device device = new Device();
316 326 device.setTenantId(tenantId);
317 327 device.setCustomerId(customerId);
318   - device.setType(type);
  328 + device.setDeviceProfileId(deviceProfileId);
319 329 device.setName(name);
320 330 if (description != null) {
321 331 ObjectNode additionalInfo = objectMapper.createObjectNode();
... ...
... ... @@ -22,6 +22,7 @@ import org.thingsboard.server.common.data.EntitySubtype;
22 22 import org.thingsboard.server.common.data.device.DeviceSearchQuery;
23 23 import org.thingsboard.server.common.data.id.CustomerId;
24 24 import org.thingsboard.server.common.data.id.DeviceId;
  25 +import org.thingsboard.server.common.data.id.DeviceProfileId;
25 26 import org.thingsboard.server.common.data.id.TenantId;
26 27 import org.thingsboard.server.common.data.page.PageData;
27 28 import org.thingsboard.server.common.data.page.PageLink;
... ... @@ -56,6 +57,8 @@ public interface DeviceService {
56 57
57 58 PageData<DeviceInfo> findDeviceInfosByTenantIdAndType(TenantId tenantId, String type, PageLink pageLink);
58 59
  60 + PageData<DeviceInfo> findDeviceInfosByTenantIdAndDeviceProfileId(TenantId tenantId, DeviceProfileId deviceProfileId, PageLink pageLink);
  61 +
59 62 ListenableFuture<List<Device>> findDevicesByTenantIdAndIdsAsync(TenantId tenantId, List<DeviceId> deviceIds);
60 63
61 64 void deleteDevicesByTenantId(TenantId tenantId);
... ... @@ -68,6 +71,8 @@ public interface DeviceService {
68 71
69 72 PageData<DeviceInfo> findDeviceInfosByTenantIdAndCustomerIdAndType(TenantId tenantId, CustomerId customerId, String type, PageLink pageLink);
70 73
  74 + PageData<DeviceInfo> findDeviceInfosByTenantIdAndCustomerIdAndDeviceProfileId(TenantId tenantId, CustomerId customerId, DeviceProfileId deviceProfileId, PageLink pageLink);
  75 +
71 76 ListenableFuture<List<Device>> findDevicesByTenantIdCustomerIdAndIdsAsync(TenantId tenantId, CustomerId customerId, List<DeviceId> deviceIds);
72 77
73 78 void unassignCustomerDevices(TenantId tenantId, CustomerId customerId);
... ...
... ... @@ -90,6 +90,16 @@ public interface DeviceDao extends Dao<Device> {
90 90 PageData<DeviceInfo> findDeviceInfosByTenantIdAndType(UUID tenantId, String type, PageLink pageLink);
91 91
92 92 /**
  93 + * Find device infos by tenantId, deviceProfileId and page link.
  94 + *
  95 + * @param tenantId the tenantId
  96 + * @param deviceProfileId the deviceProfileId
  97 + * @param pageLink the page link
  98 + * @return the list of device info objects
  99 + */
  100 + PageData<DeviceInfo> findDeviceInfosByTenantIdAndDeviceProfileId(UUID tenantId, UUID deviceProfileId, PageLink pageLink);
  101 +
  102 + /**
93 103 * Find devices by tenantId and devices Ids.
94 104 *
95 105 * @param tenantId the tenantId
... ... @@ -140,6 +150,16 @@ public interface DeviceDao extends Dao<Device> {
140 150 */
141 151 PageData<DeviceInfo> findDeviceInfosByTenantIdAndCustomerIdAndType(UUID tenantId, UUID customerId, String type, PageLink pageLink);
142 152
  153 + /**
  154 + * Find device infos by tenantId, customerId, deviceProfileId and page link.
  155 + *
  156 + * @param tenantId the tenantId
  157 + * @param customerId the customerId
  158 + * @param deviceProfileId the deviceProfileId
  159 + * @param pageLink the page link
  160 + * @return the list of device info objects
  161 + */
  162 + PageData<DeviceInfo> findDeviceInfosByTenantIdAndCustomerIdAndDeviceProfileId(UUID tenantId, UUID customerId, UUID deviceProfileId, PageLink pageLink);
143 163
144 164 /**
145 165 * Find devices by tenantId, customerId and devices Ids.
... ...
... ... @@ -87,6 +87,7 @@ import static org.thingsboard.server.dao.service.Validator.validateString;
87 87 public class DeviceServiceImpl extends AbstractEntityService implements DeviceService {
88 88
89 89 public static final String INCORRECT_TENANT_ID = "Incorrect tenantId ";
  90 + public static final String INCORRECT_DEVICE_PROFILE_ID = "Incorrect deviceProfileId ";
90 91 public static final String INCORRECT_PAGE_LINK = "Incorrect page link ";
91 92 public static final String INCORRECT_CUSTOMER_ID = "Incorrect customerId ";
92 93 public static final String INCORRECT_DEVICE_ID = "Incorrect deviceId ";
... ... @@ -303,6 +304,15 @@ public class DeviceServiceImpl extends AbstractEntityService implements DeviceSe
303 304 }
304 305
305 306 @Override
  307 + public PageData<DeviceInfo> findDeviceInfosByTenantIdAndDeviceProfileId(TenantId tenantId, DeviceProfileId deviceProfileId, PageLink pageLink) {
  308 + log.trace("Executing findDeviceInfosByTenantIdAndDeviceProfileId, tenantId [{}], deviceProfileId [{}], pageLink [{}]", tenantId, deviceProfileId, pageLink);
  309 + validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
  310 + validateId(deviceProfileId, INCORRECT_DEVICE_PROFILE_ID + deviceProfileId);
  311 + validatePageLink(pageLink);
  312 + return deviceDao.findDeviceInfosByTenantIdAndDeviceProfileId(tenantId.getId(), deviceProfileId.getId(), pageLink);
  313 + }
  314 +
  315 + @Override
306 316 public ListenableFuture<List<Device>> findDevicesByTenantIdAndIdsAsync(TenantId tenantId, List<DeviceId> deviceIds) {
307 317 log.trace("Executing findDevicesByTenantIdAndIdsAsync, tenantId [{}], deviceIds [{}]", tenantId, deviceIds);
308 318 validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
... ... @@ -357,6 +367,16 @@ public class DeviceServiceImpl extends AbstractEntityService implements DeviceSe
357 367 }
358 368
359 369 @Override
  370 + public PageData<DeviceInfo> findDeviceInfosByTenantIdAndCustomerIdAndDeviceProfileId(TenantId tenantId, CustomerId customerId, DeviceProfileId deviceProfileId, PageLink pageLink) {
  371 + log.trace("Executing findDeviceInfosByTenantIdAndCustomerIdAndDeviceProfileId, tenantId [{}], customerId [{}], deviceProfileId [{}], pageLink [{}]", tenantId, customerId, deviceProfileId, pageLink);
  372 + validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
  373 + validateId(customerId, INCORRECT_CUSTOMER_ID + customerId);
  374 + validateId(deviceProfileId, INCORRECT_DEVICE_PROFILE_ID + deviceProfileId);
  375 + validatePageLink(pageLink);
  376 + return deviceDao.findDeviceInfosByTenantIdAndCustomerIdAndDeviceProfileId(tenantId.getId(), customerId.getId(), deviceProfileId.getId(), pageLink);
  377 + }
  378 +
  379 + @Override
360 380 public ListenableFuture<List<Device>> findDevicesByTenantIdCustomerIdAndIdsAsync(TenantId tenantId, CustomerId customerId, List<DeviceId> deviceIds) {
361 381 log.trace("Executing findDevicesByTenantIdCustomerIdAndIdsAsync, tenantId [{}], customerId [{}], deviceIds [{}]", tenantId, customerId, deviceIds);
362 382 validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
... ...
... ... @@ -106,6 +106,18 @@ public interface DeviceRepository extends PagingAndSortingRepository<DeviceEntit
106 106 @Param("textSearch") String textSearch,
107 107 Pageable pageable);
108 108
  109 + @Query("SELECT new org.thingsboard.server.dao.model.sql.DeviceInfoEntity(d, c.title, c.additionalInfo, p.name) " +
  110 + "FROM DeviceEntity d " +
  111 + "LEFT JOIN CustomerEntity c on c.id = d.customerId " +
  112 + "LEFT JOIN DeviceProfileEntity p on p.id = d.deviceProfileId " +
  113 + "WHERE d.tenantId = :tenantId " +
  114 + "AND d.deviceProfileId = :deviceProfileId " +
  115 + "AND LOWER(d.searchText) LIKE LOWER(CONCAT(:textSearch, '%'))")
  116 + Page<DeviceInfoEntity> findDeviceInfosByTenantIdAndDeviceProfileId(@Param("tenantId") UUID tenantId,
  117 + @Param("deviceProfileId") UUID deviceProfileId,
  118 + @Param("textSearch") String textSearch,
  119 + Pageable pageable);
  120 +
109 121 @Query("SELECT d FROM DeviceEntity d WHERE d.tenantId = :tenantId " +
110 122 "AND d.customerId = :customerId " +
111 123 "AND d.type = :type " +
... ... @@ -130,6 +142,20 @@ public interface DeviceRepository extends PagingAndSortingRepository<DeviceEntit
130 142 @Param("textSearch") String textSearch,
131 143 Pageable pageable);
132 144
  145 + @Query("SELECT new org.thingsboard.server.dao.model.sql.DeviceInfoEntity(d, c.title, c.additionalInfo, p.name) " +
  146 + "FROM DeviceEntity d " +
  147 + "LEFT JOIN CustomerEntity c on c.id = d.customerId " +
  148 + "LEFT JOIN DeviceProfileEntity p on p.id = d.deviceProfileId " +
  149 + "WHERE d.tenantId = :tenantId " +
  150 + "AND d.customerId = :customerId " +
  151 + "AND d.deviceProfileId = :deviceProfileId " +
  152 + "AND LOWER(d.searchText) LIKE LOWER(CONCAT(:textSearch, '%'))")
  153 + Page<DeviceInfoEntity> findDeviceInfosByTenantIdAndCustomerIdAndDeviceProfileId(@Param("tenantId") UUID tenantId,
  154 + @Param("customerId") UUID customerId,
  155 + @Param("deviceProfileId") UUID deviceProfileId,
  156 + @Param("textSearch") String textSearch,
  157 + Pageable pageable);
  158 +
133 159 @Query("SELECT DISTINCT d.type FROM DeviceEntity d WHERE d.tenantId = :tenantId")
134 160 List<String> findTenantDeviceTypes(@Param("tenantId") UUID tenantId);
135 161
... ...
... ... @@ -157,6 +157,16 @@ public class JpaDeviceDao extends JpaAbstractSearchTextDao<DeviceEntity, Device>
157 157 }
158 158
159 159 @Override
  160 + public PageData<DeviceInfo> findDeviceInfosByTenantIdAndDeviceProfileId(UUID tenantId, UUID deviceProfileId, PageLink pageLink) {
  161 + return DaoUtil.toPageData(
  162 + deviceRepository.findDeviceInfosByTenantIdAndDeviceProfileId(
  163 + tenantId,
  164 + deviceProfileId,
  165 + Objects.toString(pageLink.getTextSearch(), ""),
  166 + DaoUtil.toPageable(pageLink, DeviceInfoEntity.deviceInfoColumnMap)));
  167 + }
  168 +
  169 + @Override
160 170 public PageData<Device> findDevicesByTenantIdAndCustomerIdAndType(UUID tenantId, UUID customerId, String type, PageLink pageLink) {
161 171 return DaoUtil.toPageData(
162 172 deviceRepository.findByTenantIdAndCustomerIdAndType(
... ... @@ -179,6 +189,17 @@ public class JpaDeviceDao extends JpaAbstractSearchTextDao<DeviceEntity, Device>
179 189 }
180 190
181 191 @Override
  192 + public PageData<DeviceInfo> findDeviceInfosByTenantIdAndCustomerIdAndDeviceProfileId(UUID tenantId, UUID customerId, UUID deviceProfileId, PageLink pageLink) {
  193 + return DaoUtil.toPageData(
  194 + deviceRepository.findDeviceInfosByTenantIdAndCustomerIdAndDeviceProfileId(
  195 + tenantId,
  196 + customerId,
  197 + deviceProfileId,
  198 + Objects.toString(pageLink.getTextSearch(), ""),
  199 + DaoUtil.toPageable(pageLink, DeviceInfoEntity.deviceInfoColumnMap)));
  200 + }
  201 +
  202 + @Override
182 203 public ListenableFuture<List<EntitySubtype>> findTenantDeviceTypesAsync(UUID tenantId) {
183 204 return service.submit(() -> convertTenantDeviceTypesToDto(tenantId, deviceRepository.findTenantDeviceTypes(tenantId)));
184 205 }
... ...
... ... @@ -46,12 +46,24 @@ export class DeviceService {
46 46 defaultHttpOptionsFromConfig(config));
47 47 }
48 48
  49 + public getTenantDeviceInfosByDeviceProfileId(pageLink: PageLink, deviceProfileId: string = '',
  50 + config?: RequestConfig): Observable<PageData<DeviceInfo>> {
  51 + return this.http.get<PageData<DeviceInfo>>(`/api/tenant/deviceInfos${pageLink.toQuery()}&deviceProfileId=${deviceProfileId}`,
  52 + defaultHttpOptionsFromConfig(config));
  53 + }
  54 +
49 55 public getCustomerDeviceInfos(customerId: string, pageLink: PageLink, type: string = '',
50 56 config?: RequestConfig): Observable<PageData<DeviceInfo>> {
51 57 return this.http.get<PageData<DeviceInfo>>(`/api/customer/${customerId}/deviceInfos${pageLink.toQuery()}&type=${type}`,
52 58 defaultHttpOptionsFromConfig(config));
53 59 }
54 60
  61 + public getCustomerDeviceInfosByDeviceProfileId(customerId: string, pageLink: PageLink, deviceProfileId: string = '',
  62 + config?: RequestConfig): Observable<PageData<DeviceInfo>> {
  63 + return this.http.get<PageData<DeviceInfo>>(`/api/customer/${customerId}/deviceInfos${pageLink.toQuery()}&deviceProfileId=${deviceProfileId}`,
  64 + defaultHttpOptionsFromConfig(config));
  65 + }
  66 +
55 67 public getDevice(deviceId: string, config?: RequestConfig): Observable<Device> {
56 68 return this.http.get<Device>(`/api/device/${deviceId}`, defaultHttpOptionsFromConfig(config));
57 69 }
... ...
... ... @@ -30,7 +30,7 @@
30 30 (click)="clear()">
31 31 <mat-icon class="material-icons">close</mat-icon>
32 32 </button>
33   - <button *ngIf="selectDeviceProfileFormGroup.get('deviceProfile').value && !disabled"
  33 + <button *ngIf="selectDeviceProfileFormGroup.get('deviceProfile').value && !disabled && editProfileEnabled"
34 34 type="button"
35 35 matSuffix mat-button mat-icon-button aria-label="Edit"
36 36 matTooltip="{{ 'device-profile.edit' | translate }}"
... ... @@ -40,6 +40,7 @@
40 40 </button>
41 41 <mat-autocomplete
42 42 class="tb-autocomplete"
  43 + (closed)="onPanelClosed()"
43 44 #deviceProfileAutocomplete="matAutocomplete"
44 45 [displayWith]="displayDeviceProfileFn">
45 46 <mat-option *ngFor="let deviceProfile of filteredDeviceProfiles | async" [value]="deviceProfile">
... ...
... ... @@ -14,12 +14,21 @@
14 14 /// limitations under the License.
15 15 ///
16 16
17   -import { Component, ElementRef, EventEmitter, forwardRef, Input, OnInit, Output, ViewChild } from '@angular/core';
  17 +import {
  18 + Component,
  19 + ElementRef,
  20 + EventEmitter,
  21 + forwardRef,
  22 + Input, NgZone,
  23 + OnInit,
  24 + Output,
  25 + ViewChild
  26 +} from '@angular/core';
18 27 import { ControlValueAccessor, FormBuilder, FormGroup, NG_VALUE_ACCESSOR } from '@angular/forms';
19 28 import { Observable } from 'rxjs';
20 29 import { PageLink } from '@shared/models/page/page-link';
21 30 import { Direction } from '@shared/models/page/sort-order';
22   -import { map, mergeMap, share, startWith, tap } from 'rxjs/operators';
  31 +import { map, mergeMap, share, tap } from 'rxjs/operators';
23 32 import { Store } from '@ngrx/store';
24 33 import { AppState } from '@app/core/core.state';
25 34 import { TranslateService } from '@ngx-translate/core';
... ... @@ -39,6 +48,7 @@ import {
39 48 } from '@shared/models/device.models';
40 49 import { DeviceProfileService } from '@core/http/device-profile.service';
41 50 import { DeviceProfileDialogComponent, DeviceProfileDialogData } from './device-profile-dialog.component';
  51 +import { MatAutocomplete } from '@angular/material/autocomplete';
42 52
43 53 @Component({
44 54 selector: 'tb-device-profile-autocomplete',
... ... @@ -59,6 +69,12 @@ export class DeviceProfileAutocompleteComponent implements ControlValueAccessor,
59 69 @Input()
60 70 selectDefaultProfile = false;
61 71
  72 + @Input()
  73 + displayAllOnEmpty = false;
  74 +
  75 + @Input()
  76 + editProfileEnabled = true;
  77 +
62 78 private requiredValue: boolean;
63 79 get required(): boolean {
64 80 return this.requiredValue;
... ... @@ -79,12 +95,23 @@ export class DeviceProfileAutocompleteComponent implements ControlValueAccessor,
79 95
80 96 @ViewChild('deviceProfileInput', {static: true}) deviceProfileInput: ElementRef;
81 97
  98 + @ViewChild('deviceProfileAutocomplete', {static: true}) deviceProfileAutocomplete: MatAutocomplete;
  99 +
82 100 filteredDeviceProfiles: Observable<Array<DeviceProfileInfo>>;
83 101
84 102 searchText = '';
85 103
86 104 private dirty = false;
87 105
  106 + private ignoreClosedPanel = false;
  107 +
  108 + private allDeviceProfile: DeviceProfileInfo = {
  109 + name: this.translate.instant('device-profile.all-device-profiles'),
  110 + type: DeviceProfileType.DEFAULT,
  111 + transportType: DeviceTransportType.DEFAULT,
  112 + id: null
  113 + };
  114 +
88 115 private propagateChange = (v: any) => { };
89 116
90 117 constructor(private store: Store<AppState>,
... ... @@ -92,6 +119,7 @@ export class DeviceProfileAutocompleteComponent implements ControlValueAccessor,
92 119 public truncate: TruncatePipe,
93 120 private deviceProfileService: DeviceProfileService,
94 121 private fb: FormBuilder,
  122 + private zone: NgZone,
95 123 private dialog: MatDialog) {
96 124 this.selectDeviceProfileFormGroup = this.fb.group({
97 125 deviceProfile: [null]
... ... @@ -115,9 +143,25 @@ export class DeviceProfileAutocompleteComponent implements ControlValueAccessor,
115 143 } else {
116 144 modelValue = value;
117 145 }
118   - this.updateView(modelValue);
  146 + if (!this.displayAllOnEmpty || modelValue) {
  147 + this.updateView(modelValue);
  148 + }
  149 + }),
  150 + map(value => {
  151 + if (value) {
  152 + if (typeof value === 'string') {
  153 + return value;
  154 + } else {
  155 + if (this.displayAllOnEmpty && value === this.allDeviceProfile) {
  156 + return '';
  157 + } else {
  158 + return value.name;
  159 + }
  160 + }
  161 + } else {
  162 + return '';
  163 + }
119 164 }),
120   - map(value => value ? (typeof value === 'string' ? value : value.name) : ''),
121 165 mergeMap(name => this.fetchDeviceProfiles(name) ),
122 166 share()
123 167 );
... ... @@ -150,6 +194,9 @@ export class DeviceProfileAutocompleteComponent implements ControlValueAccessor,
150 194 this.deviceProfileChanged.emit(profile);
151 195 }
152 196 );
  197 + } else if (this.displayAllOnEmpty) {
  198 + this.modelValue = null;
  199 + this.selectDeviceProfileFormGroup.get('deviceProfile').patchValue(this.allDeviceProfile, {emitEvent: false});
153 200 } else {
154 201 this.modelValue = null;
155 202 this.selectDeviceProfileFormGroup.get('deviceProfile').patchValue(null, {emitEvent: false});
... ... @@ -165,8 +212,20 @@ export class DeviceProfileAutocompleteComponent implements ControlValueAccessor,
165 212 }
166 213 }
167 214
  215 + onPanelClosed() {
  216 + if (this.ignoreClosedPanel) {
  217 + this.ignoreClosedPanel = false;
  218 + } else {
  219 + if (this.displayAllOnEmpty && !this.selectDeviceProfileFormGroup.get('deviceProfile').value) {
  220 + this.zone.run(() => {
  221 + this.selectDeviceProfileFormGroup.get('deviceProfile').patchValue(this.allDeviceProfile, {emitEvent: true});
  222 + }, 0);
  223 + }
  224 + }
  225 + }
  226 +
168 227 updateView(deviceProfile: DeviceProfileInfo | null) {
169   - const idValue = deviceProfile ? new DeviceProfileId(deviceProfile.id.id) : null;
  228 + const idValue = deviceProfile && deviceProfile.id ? new DeviceProfileId(deviceProfile.id.id) : null;
170 229 if (!entityIdEquals(this.modelValue, idValue)) {
171 230 this.modelValue = idValue;
172 231 this.propagateChange(this.modelValue);
... ... @@ -186,12 +245,17 @@ export class DeviceProfileAutocompleteComponent implements ControlValueAccessor,
186 245 });
187 246 return this.deviceProfileService.getDeviceProfileInfos(pageLink, {ignoreLoading: true}).pipe(
188 247 map(pageData => {
189   - return pageData.data;
  248 + let data = pageData.data;
  249 + if (this.displayAllOnEmpty) {
  250 + data = [this.allDeviceProfile, ...data];
  251 + }
  252 + return data;
190 253 })
191 254 );
192 255 }
193 256
194 257 clear() {
  258 + this.ignoreClosedPanel = true;
195 259 this.selectDeviceProfileFormGroup.get('deviceProfile').patchValue(null, {emitEvent: true});
196 260 setTimeout(() => {
197 261 this.deviceProfileInput.nativeElement.blur();
... ... @@ -204,7 +268,7 @@ export class DeviceProfileAutocompleteComponent implements ControlValueAccessor,
204 268 }
205 269
206 270 deviceProfileEnter($event: KeyboardEvent) {
207   - if ($event.keyCode === ENTER) {
  271 + if (this.editProfileEnabled && $event.keyCode === ENTER) {
208 272 $event.preventDefault();
209 273 if (!this.modelValue) {
210 274 this.createDeviceProfile($event, this.searchText);
... ...
... ... @@ -15,9 +15,9 @@
15 15 limitations under the License.
16 16
17 17 -->
18   -<tb-entity-subtype-select
19   - [showLabel]="true"
20   - [entityType]="entityType.DEVICE"
21   - [ngModel]="entitiesTableConfig.componentsData.deviceType"
22   - (ngModelChange)="deviceTypeChanged($event)">
23   -</tb-entity-subtype-select>
  18 +<tb-device-profile-autocomplete
  19 + [ngModel]="entitiesTableConfig.componentsData.deviceProfileId"
  20 + (ngModelChange)="deviceProfileChanged($event)"
  21 + [displayAllOnEmpty]="true"
  22 + [editProfileEnabled]="false">
  23 +</tb-device-profile-autocomplete>
... ...
... ... @@ -23,7 +23,7 @@
23 23 }
24 24
25 25 :host ::ng-deep {
26   - tb-entity-subtype-select {
  26 + tb-device-profile-autocomplete {
27 27 width: 100%;
28 28
29 29 mat-form-field {
... ...
... ... @@ -20,6 +20,7 @@ import { AppState } from '@core/core.state';
20 20 import { EntityTableHeaderComponent } from '../../components/entity/entity-table-header.component';
21 21 import { DeviceInfo } from '@app/shared/models/device.models';
22 22 import { EntityType } from '@shared/models/entity-type.models';
  23 +import { DeviceProfileId } from '../../../../shared/models/id/device-profile-id';
23 24
24 25 @Component({
25 26 selector: 'tb-device-table-header',
... ... @@ -34,8 +35,8 @@ export class DeviceTableHeaderComponent extends EntityTableHeaderComponent<Devic
34 35 super(store);
35 36 }
36 37
37   - deviceTypeChanged(deviceType: string) {
38   - this.entitiesTableConfig.componentsData.deviceType = deviceType;
  38 + deviceProfileChanged(deviceProfileId: DeviceProfileId) {
  39 + this.entitiesTableConfig.componentsData.deviceProfileId = deviceProfileId;
39 40 this.entitiesTableConfig.table.resetSortAndFilter(true);
40 41 }
41 42
... ...
... ... @@ -90,12 +90,6 @@
90 90 (deviceProfileUpdated)="onDeviceProfileUpdated()"
91 91 (deviceProfileChanged)="onDeviceProfileChanged($event)">
92 92 </tb-device-profile-autocomplete>
93   - <tb-entity-subtype-autocomplete
94   - formControlName="type"
95   - [required]="true"
96   - [entityType]="entityType.DEVICE"
97   - >
98   - </tb-entity-subtype-autocomplete>
99 93 <mat-form-field class="mat-block">
100 94 <mat-label translate>device.label</mat-label>
101 95 <input matInput formControlName="label">
... ...
... ... @@ -80,7 +80,6 @@ export class DeviceComponent extends EntityComponent<DeviceInfo> {
80 80 {
81 81 name: [entity ? entity.name : '', [Validators.required]],
82 82 deviceProfileId: [entity ? entity.deviceProfileId : null, [Validators.required]],
83   - type: [entity ? entity.type : null, [Validators.required]],
84 83 label: [entity ? entity.label : ''],
85 84 deviceData: [entity ? entity.deviceData : null, [Validators.required]],
86 85 additionalInfo: this.fb.group(
... ... @@ -96,7 +95,6 @@ export class DeviceComponent extends EntityComponent<DeviceInfo> {
96 95 updateForm(entity: DeviceInfo) {
97 96 this.entityForm.patchValue({name: entity.name});
98 97 this.entityForm.patchValue({deviceProfileId: entity.deviceProfileId});
99   - this.entityForm.patchValue({type: entity.type});
100 98 this.entityForm.patchValue({label: entity.label});
101 99 this.entityForm.patchValue({deviceData: entity.deviceData});
102 100 this.entityForm.patchValue({additionalInfo:
... ...
... ... @@ -113,7 +113,7 @@ export class DevicesTableConfigResolver implements Resolve<EntityTableConfig<Dev
113 113 const routeParams = route.params;
114 114 this.config.componentsData = {
115 115 deviceScope: route.data.devicesType,
116   - deviceType: ''
  116 + deviceProfileId: null
117 117 };
118 118 this.customerId = routeParams.customerId;
119 119 return this.store.pipe(select(selectAuthUser), take(1)).pipe(
... ... @@ -152,14 +152,13 @@ export class DevicesTableConfigResolver implements Resolve<EntityTableConfig<Dev
152 152 configureColumns(deviceScope: string): Array<EntityTableColumn<DeviceInfo>> {
153 153 const columns: Array<EntityTableColumn<DeviceInfo>> = [
154 154 new DateEntityTableColumn<DeviceInfo>('createdTime', 'common.created-time', this.datePipe, '150px'),
155   - new EntityTableColumn<DeviceInfo>('name', 'device.name', '20%'),
156   - new EntityTableColumn<DeviceInfo>('deviceProfileName', 'device-profile.device-profile', '20%'),
157   - new EntityTableColumn<DeviceInfo>('type', 'device.device-type', '20%'),
158   - new EntityTableColumn<DeviceInfo>('label', 'device.label', '20%')
  155 + new EntityTableColumn<DeviceInfo>('name', 'device.name', '25%'),
  156 + new EntityTableColumn<DeviceInfo>('deviceProfileName', 'device-profile.device-profile', '25%'),
  157 + new EntityTableColumn<DeviceInfo>('label', 'device.label', '25%')
159 158 ];
160 159 if (deviceScope === 'tenant') {
161 160 columns.push(
162   - new EntityTableColumn<DeviceInfo>('customerTitle', 'customer.customer', '20%'),
  161 + new EntityTableColumn<DeviceInfo>('customerTitle', 'customer.customer', '25%'),
163 162 new EntityTableColumn<DeviceInfo>('customerIsPublic', 'device.public', '60px',
164 163 entity => {
165 164 return checkBoxCell(entity.customerIsPublic);
... ... @@ -178,11 +177,15 @@ export class DevicesTableConfigResolver implements Resolve<EntityTableConfig<Dev
178 177 configureEntityFunctions(deviceScope: string): void {
179 178 if (deviceScope === 'tenant') {
180 179 this.config.entitiesFetchFunction = pageLink =>
181   - this.deviceService.getTenantDeviceInfos(pageLink, this.config.componentsData.deviceType);
  180 + this.deviceService.getTenantDeviceInfosByDeviceProfileId(pageLink,
  181 + this.config.componentsData.deviceProfileId !== null ?
  182 + this.config.componentsData.deviceProfileId.id : '');
182 183 this.config.deleteEntity = id => this.deviceService.deleteDevice(id.id);
183 184 } else {
184 185 this.config.entitiesFetchFunction = pageLink =>
185   - this.deviceService.getCustomerDeviceInfos(this.customerId, pageLink, this.config.componentsData.deviceType);
  186 + this.deviceService.getCustomerDeviceInfosByDeviceProfileId(this.customerId, pageLink,
  187 + this.config.componentsData.deviceProfileId !== null ?
  188 + this.config.componentsData.deviceProfileId.id : '');
186 189 this.config.deleteEntity = id => this.deviceService.unassignDeviceFromCustomer(id.id);
187 190 }
188 191 }
... ...
... ... @@ -755,6 +755,7 @@
755 755 "device-profile": {
756 756 "device-profile": "Device profile",
757 757 "device-profiles": "Device profiles",
  758 + "all-device-profiles": "All",
758 759 "add": "Add device profile",
759 760 "edit": "Edit device profile",
760 761 "device-profile-details": "Device profile details",
... ...