Commit 83ea3f083686a9de050ca6d54fe55ccea25e51e6
1 parent
43596ec5
Move from device type to device profile
Showing
17 changed files
with
229 additions
and
44 deletions
... | ... | @@ -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 | } | ... | ... |
application/src/main/java/org/thingsboard/server/service/install/DefaultSystemDataLoaderService.java
... | ... | @@ -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> | ... | ... |
... | ... | @@ -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", | ... | ... |