Commit d5b640d6021d4a3b57ae1b70cb1706084809a0af

Authored by Igor Kulikov
1 parent 89c1743f

Add image fields for dashboard and device profile entities. Introduce getAllAlar…

…ms and getCustomerAlarms API
Showing 31 changed files with 257 additions and 32 deletions
@@ -78,7 +78,11 @@ CREATE TABLE IF NOT EXISTS firmware ( @@ -78,7 +78,11 @@ CREATE TABLE IF NOT EXISTS firmware (
78 CONSTRAINT firmware_tenant_title_version_unq_key UNIQUE (tenant_id, title, version) 78 CONSTRAINT firmware_tenant_title_version_unq_key UNIQUE (tenant_id, title, version)
79 ); 79 );
80 80
  81 +ALTER TABLE dashboard
  82 + ADD COLUMN IF NOT EXISTS image varchar(1000000);
  83 +
81 ALTER TABLE device_profile 84 ALTER TABLE device_profile
  85 + ADD COLUMN IF NOT EXISTS image varchar(1000000),
82 ADD COLUMN IF NOT EXISTS firmware_id uuid, 86 ADD COLUMN IF NOT EXISTS firmware_id uuid,
83 ADD COLUMN IF NOT EXISTS software_id uuid; 87 ADD COLUMN IF NOT EXISTS software_id uuid;
84 88
@@ -197,6 +197,41 @@ public class AlarmController extends BaseController { @@ -197,6 +197,41 @@ public class AlarmController extends BaseController {
197 } 197 }
198 198
199 @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") 199 @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
  200 + @RequestMapping(value = "/alarms", method = RequestMethod.GET)
  201 + @ResponseBody
  202 + public PageData<AlarmInfo> getAllAlarms(
  203 + @RequestParam(required = false) String searchStatus,
  204 + @RequestParam(required = false) String status,
  205 + @RequestParam int pageSize,
  206 + @RequestParam int page,
  207 + @RequestParam(required = false) String textSearch,
  208 + @RequestParam(required = false) String sortProperty,
  209 + @RequestParam(required = false) String sortOrder,
  210 + @RequestParam(required = false) Long startTime,
  211 + @RequestParam(required = false) Long endTime,
  212 + @RequestParam(required = false) Boolean fetchOriginator
  213 + ) throws ThingsboardException {
  214 + accessControlService.checkPermission(getCurrentUser(), Resource.ALARM, Operation.READ);
  215 + AlarmSearchStatus alarmSearchStatus = StringUtils.isEmpty(searchStatus) ? null : AlarmSearchStatus.valueOf(searchStatus);
  216 + AlarmStatus alarmStatus = StringUtils.isEmpty(status) ? null : AlarmStatus.valueOf(status);
  217 + if (alarmSearchStatus != null && alarmStatus != null) {
  218 + throw new ThingsboardException("Invalid alarms search query: Both parameters 'searchStatus' " +
  219 + "and 'status' can't be specified at the same time!", ThingsboardErrorCode.BAD_REQUEST_PARAMS);
  220 + }
  221 + TimePageLink pageLink = createTimePageLink(pageSize, page, textSearch, sortProperty, sortOrder, startTime, endTime);
  222 +
  223 + try {
  224 + if (getCurrentUser().isCustomerUser()) {
  225 + return checkNotNull(alarmService.findCustomerAlarms(getCurrentUser().getTenantId(), getCurrentUser().getCustomerId(), new AlarmQuery(null, pageLink, alarmSearchStatus, alarmStatus, fetchOriginator)).get());
  226 + } else {
  227 + return checkNotNull(alarmService.findAlarms(getCurrentUser().getTenantId(), new AlarmQuery(null, pageLink, alarmSearchStatus, alarmStatus, fetchOriginator)).get());
  228 + }
  229 + } catch (Exception e) {
  230 + throw handleException(e);
  231 + }
  232 + }
  233 +
  234 + @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
200 @RequestMapping(value = "/alarm/highestSeverity/{entityType}/{entityId}", method = RequestMethod.GET) 235 @RequestMapping(value = "/alarm/highestSeverity/{entityType}/{entityId}", method = RequestMethod.GET)
201 @ResponseBody 236 @ResponseBody
202 public AlarmSeverity getHighestAlarmSeverity( 237 public AlarmSeverity getHighestAlarmSeverity(
@@ -128,6 +128,11 @@ public class DefaultAlarmSubscriptionService extends AbstractSubscriptionService @@ -128,6 +128,11 @@ public class DefaultAlarmSubscriptionService extends AbstractSubscriptionService
128 } 128 }
129 129
130 @Override 130 @Override
  131 + public ListenableFuture<PageData<AlarmInfo>> findCustomerAlarms(TenantId tenantId, CustomerId customerId, AlarmQuery query) {
  132 + return alarmService.findCustomerAlarms(tenantId, customerId, query);
  133 + }
  134 +
  135 + @Override
131 public AlarmSeverity findHighestAlarmSeverity(TenantId tenantId, EntityId entityId, AlarmSearchStatus alarmSearchStatus, AlarmStatus alarmStatus) { 136 public AlarmSeverity findHighestAlarmSeverity(TenantId tenantId, EntityId entityId, AlarmSearchStatus alarmSearchStatus, AlarmStatus alarmStatus) {
132 return alarmService.findHighestAlarmSeverity(tenantId, entityId, alarmSearchStatus, alarmStatus); 137 return alarmService.findHighestAlarmSeverity(tenantId, entityId, alarmSearchStatus, alarmStatus);
133 } 138 }
@@ -313,7 +313,7 @@ public abstract class BaseDeviceProfileControllerTest extends AbstractController @@ -313,7 +313,7 @@ public abstract class BaseDeviceProfileControllerTest extends AbstractController
313 Collections.sort(loadedDeviceProfileInfos, deviceProfileInfoIdComparator); 313 Collections.sort(loadedDeviceProfileInfos, deviceProfileInfoIdComparator);
314 314
315 List<DeviceProfileInfo> deviceProfileInfos = deviceProfiles.stream().map(deviceProfile -> new DeviceProfileInfo(deviceProfile.getId(), 315 List<DeviceProfileInfo> deviceProfileInfos = deviceProfiles.stream().map(deviceProfile -> new DeviceProfileInfo(deviceProfile.getId(),
316 - deviceProfile.getName(), deviceProfile.getType(), deviceProfile.getTransportType())).collect(Collectors.toList()); 316 + deviceProfile.getName(), deviceProfile.getImage(), deviceProfile.getType(), deviceProfile.getTransportType())).collect(Collectors.toList());
317 317
318 Assert.assertEquals(deviceProfileInfos, loadedDeviceProfileInfos); 318 Assert.assertEquals(deviceProfileInfos, loadedDeviceProfileInfos);
319 319
@@ -53,6 +53,8 @@ public interface AlarmService { @@ -53,6 +53,8 @@ public interface AlarmService {
53 53
54 ListenableFuture<PageData<AlarmInfo>> findAlarms(TenantId tenantId, AlarmQuery query); 54 ListenableFuture<PageData<AlarmInfo>> findAlarms(TenantId tenantId, AlarmQuery query);
55 55
  56 + ListenableFuture<PageData<AlarmInfo>> findCustomerAlarms(TenantId tenantId, CustomerId customerId, AlarmQuery query);
  57 +
56 AlarmSeverity findHighestAlarmSeverity(TenantId tenantId, EntityId entityId, AlarmSearchStatus alarmSearchStatus, 58 AlarmSeverity findHighestAlarmSeverity(TenantId tenantId, EntityId entityId, AlarmSearchStatus alarmSearchStatus,
57 AlarmStatus alarmStatus); 59 AlarmStatus alarmStatus);
58 60
@@ -30,6 +30,7 @@ public class DashboardInfo extends SearchTextBased<DashboardId> implements HasNa @@ -30,6 +30,7 @@ public class DashboardInfo extends SearchTextBased<DashboardId> implements HasNa
30 private TenantId tenantId; 30 private TenantId tenantId;
31 @NoXss 31 @NoXss
32 private String title; 32 private String title;
  33 + private String image;
33 @Valid 34 @Valid
34 private Set<ShortCustomerInfo> assignedCustomers; 35 private Set<ShortCustomerInfo> assignedCustomers;
35 36
@@ -45,6 +46,7 @@ public class DashboardInfo extends SearchTextBased<DashboardId> implements HasNa @@ -45,6 +46,7 @@ public class DashboardInfo extends SearchTextBased<DashboardId> implements HasNa
45 super(dashboardInfo); 46 super(dashboardInfo);
46 this.tenantId = dashboardInfo.getTenantId(); 47 this.tenantId = dashboardInfo.getTenantId();
47 this.title = dashboardInfo.getTitle(); 48 this.title = dashboardInfo.getTitle();
  49 + this.image = dashboardInfo.getImage();
48 this.assignedCustomers = dashboardInfo.getAssignedCustomers(); 50 this.assignedCustomers = dashboardInfo.getAssignedCustomers();
49 } 51 }
50 52
@@ -64,6 +66,14 @@ public class DashboardInfo extends SearchTextBased<DashboardId> implements HasNa @@ -64,6 +66,14 @@ public class DashboardInfo extends SearchTextBased<DashboardId> implements HasNa
64 this.title = title; 66 this.title = title;
65 } 67 }
66 68
  69 + public String getImage() {
  70 + return image;
  71 + }
  72 +
  73 + public void setImage(String image) {
  74 + this.image = image;
  75 + }
  76 +
67 public Set<ShortCustomerInfo> getAssignedCustomers() { 77 public Set<ShortCustomerInfo> getAssignedCustomers() {
68 return assignedCustomers; 78 return assignedCustomers;
69 } 79 }
@@ -43,6 +43,7 @@ public class DeviceProfile extends SearchTextBased<DeviceProfileId> implements H @@ -43,6 +43,7 @@ public class DeviceProfile extends SearchTextBased<DeviceProfileId> implements H
43 private String name; 43 private String name;
44 @NoXss 44 @NoXss
45 private String description; 45 private String description;
  46 + private String image;
46 private boolean isDefault; 47 private boolean isDefault;
47 private DeviceProfileType type; 48 private DeviceProfileType type;
48 private DeviceTransportType transportType; 49 private DeviceTransportType transportType;
@@ -74,6 +75,7 @@ public class DeviceProfile extends SearchTextBased<DeviceProfileId> implements H @@ -74,6 +75,7 @@ public class DeviceProfile extends SearchTextBased<DeviceProfileId> implements H
74 this.tenantId = deviceProfile.getTenantId(); 75 this.tenantId = deviceProfile.getTenantId();
75 this.name = deviceProfile.getName(); 76 this.name = deviceProfile.getName();
76 this.description = deviceProfile.getDescription(); 77 this.description = deviceProfile.getDescription();
  78 + this.image = deviceProfile.getImage();
77 this.isDefault = deviceProfile.isDefault(); 79 this.isDefault = deviceProfile.isDefault();
78 this.defaultRuleChainId = deviceProfile.getDefaultRuleChainId(); 80 this.defaultRuleChainId = deviceProfile.getDefaultRuleChainId();
79 this.defaultQueueName = deviceProfile.getDefaultQueueName(); 81 this.defaultQueueName = deviceProfile.getDefaultQueueName();
@@ -30,21 +30,25 @@ import java.util.UUID; @@ -30,21 +30,25 @@ import java.util.UUID;
30 @ToString(callSuper = true) 30 @ToString(callSuper = true)
31 public class DeviceProfileInfo extends EntityInfo { 31 public class DeviceProfileInfo extends EntityInfo {
32 32
  33 + private final String image;
33 private final DeviceProfileType type; 34 private final DeviceProfileType type;
34 private final DeviceTransportType transportType; 35 private final DeviceTransportType transportType;
35 36
36 @JsonCreator 37 @JsonCreator
37 public DeviceProfileInfo(@JsonProperty("id") EntityId id, 38 public DeviceProfileInfo(@JsonProperty("id") EntityId id,
38 @JsonProperty("name") String name, 39 @JsonProperty("name") String name,
  40 + @JsonProperty("image") String image,
39 @JsonProperty("type") DeviceProfileType type, 41 @JsonProperty("type") DeviceProfileType type,
40 @JsonProperty("transportType") DeviceTransportType transportType) { 42 @JsonProperty("transportType") DeviceTransportType transportType) {
41 super(id, name); 43 super(id, name);
  44 + this.image = image;
42 this.type = type; 45 this.type = type;
43 this.transportType = transportType; 46 this.transportType = transportType;
44 } 47 }
45 48
46 - public DeviceProfileInfo(UUID uuid, String name, DeviceProfileType type, DeviceTransportType transportType) { 49 + public DeviceProfileInfo(UUID uuid, String name, String image, DeviceProfileType type, DeviceTransportType transportType) {
47 super(EntityIdFactory.getByTypeAndUuid(EntityType.DEVICE_PROFILE, uuid), name); 50 super(EntityIdFactory.getByTypeAndUuid(EntityType.DEVICE_PROFILE, uuid), name);
  51 + this.image = image;
48 this.type = type; 52 this.type = type;
49 this.transportType = transportType; 53 this.transportType = transportType;
50 } 54 }
@@ -48,6 +48,8 @@ public interface AlarmDao extends Dao<Alarm> { @@ -48,6 +48,8 @@ public interface AlarmDao extends Dao<Alarm> {
48 48
49 PageData<AlarmInfo> findAlarms(TenantId tenantId, AlarmQuery query); 49 PageData<AlarmInfo> findAlarms(TenantId tenantId, AlarmQuery query);
50 50
  51 + PageData<AlarmInfo> findCustomerAlarms(TenantId tenantId, CustomerId customerId, AlarmQuery query);
  52 +
51 PageData<AlarmData> findAlarmDataByQueryForEntities(TenantId tenantId, CustomerId customerId, 53 PageData<AlarmData> findAlarmDataByQueryForEntities(TenantId tenantId, CustomerId customerId,
52 AlarmDataQuery query, Collection<EntityId> orderedEntityIds); 54 AlarmDataQuery query, Collection<EntityId> orderedEntityIds);
53 55
@@ -292,26 +292,39 @@ public class BaseAlarmService extends AbstractEntityService implements AlarmServ @@ -292,26 +292,39 @@ public class BaseAlarmService extends AbstractEntityService implements AlarmServ
292 public ListenableFuture<PageData<AlarmInfo>> findAlarms(TenantId tenantId, AlarmQuery query) { 292 public ListenableFuture<PageData<AlarmInfo>> findAlarms(TenantId tenantId, AlarmQuery query) {
293 PageData<AlarmInfo> alarms = alarmDao.findAlarms(tenantId, query); 293 PageData<AlarmInfo> alarms = alarmDao.findAlarms(tenantId, query);
294 if (query.getFetchOriginator() != null && query.getFetchOriginator().booleanValue()) { 294 if (query.getFetchOriginator() != null && query.getFetchOriginator().booleanValue()) {
295 - List<ListenableFuture<AlarmInfo>> alarmFutures = new ArrayList<>(alarms.getData().size());  
296 - for (AlarmInfo alarmInfo : alarms.getData()) {  
297 - alarmFutures.add(Futures.transform(  
298 - entityService.fetchEntityNameAsync(tenantId, alarmInfo.getOriginator()), originatorName -> {  
299 - if (originatorName == null) {  
300 - originatorName = "Deleted";  
301 - }  
302 - alarmInfo.setOriginatorName(originatorName);  
303 - return alarmInfo;  
304 - }, MoreExecutors.directExecutor()  
305 - ));  
306 - }  
307 - return Futures.transform(Futures.successfulAsList(alarmFutures),  
308 - alarmInfos -> new PageData<>(alarmInfos, alarms.getTotalPages(), alarms.getTotalElements(),  
309 - alarms.hasNext()), MoreExecutors.directExecutor()); 295 + return fetchAlarmsOriginators(tenantId, alarms);
310 } 296 }
311 return Futures.immediateFuture(alarms); 297 return Futures.immediateFuture(alarms);
312 } 298 }
313 299
314 @Override 300 @Override
  301 + public ListenableFuture<PageData<AlarmInfo>> findCustomerAlarms(TenantId tenantId, CustomerId customerId, AlarmQuery query) {
  302 + PageData<AlarmInfo> alarms = alarmDao.findCustomerAlarms(tenantId, customerId, query);
  303 + if (query.getFetchOriginator() != null && query.getFetchOriginator().booleanValue()) {
  304 + return fetchAlarmsOriginators(tenantId, alarms);
  305 + }
  306 + return Futures.immediateFuture(alarms);
  307 + }
  308 +
  309 + private ListenableFuture<PageData<AlarmInfo>> fetchAlarmsOriginators(TenantId tenantId, PageData<AlarmInfo> alarms) {
  310 + List<ListenableFuture<AlarmInfo>> alarmFutures = new ArrayList<>(alarms.getData().size());
  311 + for (AlarmInfo alarmInfo : alarms.getData()) {
  312 + alarmFutures.add(Futures.transform(
  313 + entityService.fetchEntityNameAsync(tenantId, alarmInfo.getOriginator()), originatorName -> {
  314 + if (originatorName == null) {
  315 + originatorName = "Deleted";
  316 + }
  317 + alarmInfo.setOriginatorName(originatorName);
  318 + return alarmInfo;
  319 + }, MoreExecutors.directExecutor()
  320 + ));
  321 + }
  322 + return Futures.transform(Futures.successfulAsList(alarmFutures),
  323 + alarmInfos -> new PageData<>(alarmInfos, alarms.getTotalPages(), alarms.getTotalElements(),
  324 + alarms.hasNext()), MoreExecutors.directExecutor());
  325 + }
  326 +
  327 + @Override
315 public AlarmSeverity findHighestAlarmSeverity(TenantId tenantId, EntityId entityId, AlarmSearchStatus alarmSearchStatus, 328 public AlarmSeverity findHighestAlarmSeverity(TenantId tenantId, EntityId entityId, AlarmSearchStatus alarmSearchStatus,
316 AlarmStatus alarmStatus) { 329 AlarmStatus alarmStatus) {
317 Set<AlarmStatus> statusList = null; 330 Set<AlarmStatus> statusList = null;
@@ -170,6 +170,7 @@ public class ModelConstants { @@ -170,6 +170,7 @@ public class ModelConstants {
170 public static final String DEVICE_PROFILE_TENANT_ID_PROPERTY = TENANT_ID_PROPERTY; 170 public static final String DEVICE_PROFILE_TENANT_ID_PROPERTY = TENANT_ID_PROPERTY;
171 public static final String DEVICE_PROFILE_NAME_PROPERTY = "name"; 171 public static final String DEVICE_PROFILE_NAME_PROPERTY = "name";
172 public static final String DEVICE_PROFILE_TYPE_PROPERTY = "type"; 172 public static final String DEVICE_PROFILE_TYPE_PROPERTY = "type";
  173 + public static final String DEVICE_PROFILE_IMAGE_PROPERTY = "image";
173 public static final String DEVICE_PROFILE_TRANSPORT_TYPE_PROPERTY = "transport_type"; 174 public static final String DEVICE_PROFILE_TRANSPORT_TYPE_PROPERTY = "transport_type";
174 public static final String DEVICE_PROFILE_PROVISION_TYPE_PROPERTY = "provision_type"; 175 public static final String DEVICE_PROFILE_PROVISION_TYPE_PROPERTY = "provision_type";
175 public static final String DEVICE_PROFILE_PROFILE_DATA_PROPERTY = "profile_data"; 176 public static final String DEVICE_PROFILE_PROFILE_DATA_PROPERTY = "profile_data";
@@ -333,6 +334,7 @@ public class ModelConstants { @@ -333,6 +334,7 @@ public class ModelConstants {
333 public static final String DASHBOARD_COLUMN_FAMILY_NAME = "dashboard"; 334 public static final String DASHBOARD_COLUMN_FAMILY_NAME = "dashboard";
334 public static final String DASHBOARD_TENANT_ID_PROPERTY = TENANT_ID_PROPERTY; 335 public static final String DASHBOARD_TENANT_ID_PROPERTY = TENANT_ID_PROPERTY;
335 public static final String DASHBOARD_TITLE_PROPERTY = TITLE_PROPERTY; 336 public static final String DASHBOARD_TITLE_PROPERTY = TITLE_PROPERTY;
  337 + public static final String DASHBOARD_IMAGE_PROPERTY = "image";
336 public static final String DASHBOARD_CONFIGURATION_PROPERTY = "configuration"; 338 public static final String DASHBOARD_CONFIGURATION_PROPERTY = "configuration";
337 public static final String DASHBOARD_ASSIGNED_CUSTOMERS_PROPERTY = "assigned_customers"; 339 public static final String DASHBOARD_ASSIGNED_CUSTOMERS_PROPERTY = "assigned_customers";
338 340
@@ -58,7 +58,10 @@ public final class DashboardEntity extends BaseSqlEntity<Dashboard> implements S @@ -58,7 +58,10 @@ public final class DashboardEntity extends BaseSqlEntity<Dashboard> implements S
58 58
59 @Column(name = ModelConstants.DASHBOARD_TITLE_PROPERTY) 59 @Column(name = ModelConstants.DASHBOARD_TITLE_PROPERTY)
60 private String title; 60 private String title;
61 - 61 +
  62 + @Column(name = ModelConstants.DASHBOARD_IMAGE_PROPERTY)
  63 + private String image;
  64 +
62 @Column(name = ModelConstants.SEARCH_TEXT_PROPERTY) 65 @Column(name = ModelConstants.SEARCH_TEXT_PROPERTY)
63 private String searchText; 66 private String searchText;
64 67
@@ -82,6 +85,7 @@ public final class DashboardEntity extends BaseSqlEntity<Dashboard> implements S @@ -82,6 +85,7 @@ public final class DashboardEntity extends BaseSqlEntity<Dashboard> implements S
82 this.tenantId = dashboard.getTenantId().getId(); 85 this.tenantId = dashboard.getTenantId().getId();
83 } 86 }
84 this.title = dashboard.getTitle(); 87 this.title = dashboard.getTitle();
  88 + this.image = dashboard.getImage();
85 if (dashboard.getAssignedCustomers() != null) { 89 if (dashboard.getAssignedCustomers() != null) {
86 try { 90 try {
87 this.assignedCustomers = objectMapper.writeValueAsString(dashboard.getAssignedCustomers()); 91 this.assignedCustomers = objectMapper.writeValueAsString(dashboard.getAssignedCustomers());
@@ -110,6 +114,7 @@ public final class DashboardEntity extends BaseSqlEntity<Dashboard> implements S @@ -110,6 +114,7 @@ public final class DashboardEntity extends BaseSqlEntity<Dashboard> implements S
110 dashboard.setTenantId(new TenantId(tenantId)); 114 dashboard.setTenantId(new TenantId(tenantId));
111 } 115 }
112 dashboard.setTitle(title); 116 dashboard.setTitle(title);
  117 + dashboard.setImage(image);
113 if (!StringUtils.isEmpty(assignedCustomers)) { 118 if (!StringUtils.isEmpty(assignedCustomers)) {
114 try { 119 try {
115 dashboard.setAssignedCustomers(objectMapper.readValue(assignedCustomers, assignedCustomersType)); 120 dashboard.setAssignedCustomers(objectMapper.readValue(assignedCustomers, assignedCustomersType));
@@ -54,6 +54,9 @@ public class DashboardInfoEntity extends BaseSqlEntity<DashboardInfo> implements @@ -54,6 +54,9 @@ public class DashboardInfoEntity extends BaseSqlEntity<DashboardInfo> implements
54 @Column(name = ModelConstants.DASHBOARD_TITLE_PROPERTY) 54 @Column(name = ModelConstants.DASHBOARD_TITLE_PROPERTY)
55 private String title; 55 private String title;
56 56
  57 + @Column(name = ModelConstants.DASHBOARD_IMAGE_PROPERTY)
  58 + private String image;
  59 +
57 @Column(name = ModelConstants.SEARCH_TEXT_PROPERTY) 60 @Column(name = ModelConstants.SEARCH_TEXT_PROPERTY)
58 private String searchText; 61 private String searchText;
59 62
@@ -73,6 +76,7 @@ public class DashboardInfoEntity extends BaseSqlEntity<DashboardInfo> implements @@ -73,6 +76,7 @@ public class DashboardInfoEntity extends BaseSqlEntity<DashboardInfo> implements
73 this.tenantId = dashboardInfo.getTenantId().getId(); 76 this.tenantId = dashboardInfo.getTenantId().getId();
74 } 77 }
75 this.title = dashboardInfo.getTitle(); 78 this.title = dashboardInfo.getTitle();
  79 + this.image = dashboardInfo.getImage();
76 if (dashboardInfo.getAssignedCustomers() != null) { 80 if (dashboardInfo.getAssignedCustomers() != null) {
77 try { 81 try {
78 this.assignedCustomers = objectMapper.writeValueAsString(dashboardInfo.getAssignedCustomers()); 82 this.assignedCustomers = objectMapper.writeValueAsString(dashboardInfo.getAssignedCustomers());
@@ -104,6 +108,7 @@ public class DashboardInfoEntity extends BaseSqlEntity<DashboardInfo> implements @@ -104,6 +108,7 @@ public class DashboardInfoEntity extends BaseSqlEntity<DashboardInfo> implements
104 dashboardInfo.setTenantId(new TenantId(tenantId)); 108 dashboardInfo.setTenantId(new TenantId(tenantId));
105 } 109 }
106 dashboardInfo.setTitle(title); 110 dashboardInfo.setTitle(title);
  111 + dashboardInfo.setImage(image);
107 if (!StringUtils.isEmpty(assignedCustomers)) { 112 if (!StringUtils.isEmpty(assignedCustomers)) {
108 try { 113 try {
109 dashboardInfo.setAssignedCustomers(objectMapper.readValue(assignedCustomers, assignedCustomersType)); 114 dashboardInfo.setAssignedCustomers(objectMapper.readValue(assignedCustomers, assignedCustomersType));
@@ -60,6 +60,9 @@ public final class DeviceProfileEntity extends BaseSqlEntity<DeviceProfile> impl @@ -60,6 +60,9 @@ public final class DeviceProfileEntity extends BaseSqlEntity<DeviceProfile> impl
60 @Column(name = ModelConstants.DEVICE_PROFILE_TYPE_PROPERTY) 60 @Column(name = ModelConstants.DEVICE_PROFILE_TYPE_PROPERTY)
61 private DeviceProfileType type; 61 private DeviceProfileType type;
62 62
  63 + @Column(name = ModelConstants.DEVICE_PROFILE_IMAGE_PROPERTY)
  64 + private String image;
  65 +
63 @Enumerated(EnumType.STRING) 66 @Enumerated(EnumType.STRING)
64 @Column(name = ModelConstants.DEVICE_PROFILE_TRANSPORT_TYPE_PROPERTY) 67 @Column(name = ModelConstants.DEVICE_PROFILE_TRANSPORT_TYPE_PROPERTY)
65 private DeviceTransportType transportType; 68 private DeviceTransportType transportType;
@@ -110,6 +113,7 @@ public final class DeviceProfileEntity extends BaseSqlEntity<DeviceProfile> impl @@ -110,6 +113,7 @@ public final class DeviceProfileEntity extends BaseSqlEntity<DeviceProfile> impl
110 this.setCreatedTime(deviceProfile.getCreatedTime()); 113 this.setCreatedTime(deviceProfile.getCreatedTime());
111 this.name = deviceProfile.getName(); 114 this.name = deviceProfile.getName();
112 this.type = deviceProfile.getType(); 115 this.type = deviceProfile.getType();
  116 + this.image = deviceProfile.getImage();
113 this.transportType = deviceProfile.getTransportType(); 117 this.transportType = deviceProfile.getTransportType();
114 this.provisionType = deviceProfile.getProvisionType(); 118 this.provisionType = deviceProfile.getProvisionType();
115 this.description = deviceProfile.getDescription(); 119 this.description = deviceProfile.getDescription();
@@ -151,6 +155,7 @@ public final class DeviceProfileEntity extends BaseSqlEntity<DeviceProfile> impl @@ -151,6 +155,7 @@ public final class DeviceProfileEntity extends BaseSqlEntity<DeviceProfile> impl
151 } 155 }
152 deviceProfile.setName(name); 156 deviceProfile.setName(name);
153 deviceProfile.setType(type); 157 deviceProfile.setType(type);
  158 + deviceProfile.setImage(image);
154 deviceProfile.setTransportType(transportType); 159 deviceProfile.setTransportType(transportType);
155 deviceProfile.setProvisionType(provisionType); 160 deviceProfile.setProvisionType(provisionType);
156 deviceProfile.setDescription(description); 161 deviceProfile.setDescription(description);
@@ -94,6 +94,70 @@ public interface AlarmRepository extends CrudRepository<AlarmEntity, UUID> { @@ -94,6 +94,70 @@ public interface AlarmRepository extends CrudRepository<AlarmEntity, UUID> {
94 @Param("searchText") String searchText, 94 @Param("searchText") String searchText,
95 Pageable pageable); 95 Pageable pageable);
96 96
  97 + @Query(value = "SELECT new org.thingsboard.server.dao.model.sql.AlarmInfoEntity(a) FROM AlarmEntity a " +
  98 + "WHERE a.tenantId = :tenantId " +
  99 + "AND (:startTime IS NULL OR a.createdTime >= :startTime) " +
  100 + "AND (:endTime IS NULL OR a.createdTime <= :endTime) " +
  101 + "AND ((:alarmStatuses) IS NULL OR a.status in (:alarmStatuses)) " +
  102 + "AND (LOWER(a.type) LIKE LOWER(CONCAT(:searchText, '%')) " +
  103 + " OR LOWER(a.severity) LIKE LOWER(CONCAT(:searchText, '%')) " +
  104 + " OR LOWER(a.status) LIKE LOWER(CONCAT(:searchText, '%'))) ",
  105 + countQuery = "" +
  106 + "SELECT count(a) " +
  107 + "FROM AlarmEntity a " +
  108 + "WHERE a.tenantId = :tenantId " +
  109 + "AND (:startTime IS NULL OR a.createdTime >= :startTime) " +
  110 + "AND (:endTime IS NULL OR a.createdTime <= :endTime) " +
  111 + "AND ((:alarmStatuses) IS NULL OR a.status in (:alarmStatuses)) " +
  112 + "AND (LOWER(a.type) LIKE LOWER(CONCAT(:searchText, '%')) " +
  113 + " OR LOWER(a.severity) LIKE LOWER(CONCAT(:searchText, '%')) " +
  114 + " OR LOWER(a.status) LIKE LOWER(CONCAT(:searchText, '%'))) ")
  115 + Page<AlarmInfoEntity> findAllAlarms(@Param("tenantId") UUID tenantId,
  116 + @Param("startTime") Long startTime,
  117 + @Param("endTime") Long endTime,
  118 + @Param("alarmStatuses") Set<AlarmStatus> alarmStatuses,
  119 + @Param("searchText") String searchText,
  120 + Pageable pageable);
  121 +
  122 + @Query(value = "SELECT new org.thingsboard.server.dao.model.sql.AlarmInfoEntity(a) FROM AlarmEntity a " +
  123 + "WHERE a.tenantId = :tenantId " +
  124 + "AND (" +
  125 + "a.originatorId IN (SELECT d.id from DeviceEntity d WHERE d.customerId = :customerId) " +
  126 + "OR a.originatorId IN (SELECT asset.id from AssetEntity asset WHERE asset.customerId = :customerId) " +
  127 + "OR a.originatorId IN (SELECT u.id from UserEntity u WHERE u.customerId = :customerId) " +
  128 + "OR a.originatorId = :customerId" +
  129 + ") " +
  130 + "AND (:startTime IS NULL OR a.createdTime >= :startTime) " +
  131 + "AND (:endTime IS NULL OR a.createdTime <= :endTime) " +
  132 + "AND ((:alarmStatuses) IS NULL OR a.status in (:alarmStatuses)) " +
  133 + "AND (LOWER(a.type) LIKE LOWER(CONCAT(:searchText, '%')) " +
  134 + " OR LOWER(a.severity) LIKE LOWER(CONCAT(:searchText, '%')) " +
  135 + " OR LOWER(a.status) LIKE LOWER(CONCAT(:searchText, '%'))) "
  136 + ,
  137 + countQuery = "" +
  138 + "SELECT count(a) " +
  139 + "FROM AlarmEntity a " +
  140 + "WHERE a.tenantId = :tenantId " +
  141 + "AND (" +
  142 + "a.originatorId IN (SELECT d.id from DeviceEntity d WHERE d.customerId = :customerId) " +
  143 + "OR a.originatorId IN (SELECT asset.id from AssetEntity asset WHERE asset.customerId = :customerId) " +
  144 + "OR a.originatorId IN (SELECT u.id from UserEntity u WHERE u.customerId = :customerId) " +
  145 + "OR a.originatorId = :customerId" +
  146 + ") " +
  147 + "AND (:startTime IS NULL OR a.createdTime >= :startTime) " +
  148 + "AND (:endTime IS NULL OR a.createdTime <= :endTime) " +
  149 + "AND ((:alarmStatuses) IS NULL OR a.status in (:alarmStatuses)) " +
  150 + "AND (LOWER(a.type) LIKE LOWER(CONCAT(:searchText, '%')) " +
  151 + " OR LOWER(a.severity) LIKE LOWER(CONCAT(:searchText, '%')) " +
  152 + " OR LOWER(a.status) LIKE LOWER(CONCAT(:searchText, '%'))) ")
  153 + Page<AlarmInfoEntity> findCustomerAlarms(@Param("tenantId") UUID tenantId,
  154 + @Param("customerId") UUID customerId,
  155 + @Param("startTime") Long startTime,
  156 + @Param("endTime") Long endTime,
  157 + @Param("alarmStatuses") Set<AlarmStatus> alarmStatuses,
  158 + @Param("searchText") String searchText,
  159 + Pageable pageable);
  160 +
97 @Query(value = "SELECT a.severity FROM AlarmEntity a " + 161 @Query(value = "SELECT a.severity FROM AlarmEntity a " +
98 "LEFT JOIN RelationEntity re ON a.id = re.toId " + 162 "LEFT JOIN RelationEntity re ON a.id = re.toId " +
99 "AND re.relationTypeGroup = 'ALARM' " + 163 "AND re.relationTypeGroup = 'ALARM' " +
@@ -103,11 +103,46 @@ public class JpaAlarmDao extends JpaAbstractDao<AlarmEntity, Alarm> implements A @@ -103,11 +103,46 @@ public class JpaAlarmDao extends JpaAbstractDao<AlarmEntity, Alarm> implements A
103 } else if (query.getStatus() != null) { 103 } else if (query.getStatus() != null) {
104 statusSet = Collections.singleton(query.getStatus()); 104 statusSet = Collections.singleton(query.getStatus());
105 } 105 }
  106 + if (affectedEntity != null) {
  107 + return DaoUtil.toPageData(
  108 + alarmRepository.findAlarms(
  109 + tenantId.getId(),
  110 + affectedEntity.getId(),
  111 + affectedEntity.getEntityType().name(),
  112 + query.getPageLink().getStartTime(),
  113 + query.getPageLink().getEndTime(),
  114 + statusSet,
  115 + Objects.toString(query.getPageLink().getTextSearch(), ""),
  116 + DaoUtil.toPageable(query.getPageLink())
  117 + )
  118 + );
  119 + } else {
  120 + return DaoUtil.toPageData(
  121 + alarmRepository.findAllAlarms(
  122 + tenantId.getId(),
  123 + query.getPageLink().getStartTime(),
  124 + query.getPageLink().getEndTime(),
  125 + statusSet,
  126 + Objects.toString(query.getPageLink().getTextSearch(), ""),
  127 + DaoUtil.toPageable(query.getPageLink())
  128 + )
  129 + );
  130 + }
  131 + }
  132 +
  133 + @Override
  134 + public PageData<AlarmInfo> findCustomerAlarms(TenantId tenantId, CustomerId customerId, AlarmQuery query) {
  135 + log.trace("Try to find customer alarms by status [{}] and pageLink [{}]", query.getStatus(), query.getPageLink());
  136 + Set<AlarmStatus> statusSet = null;
  137 + if (query.getSearchStatus() != null) {
  138 + statusSet = query.getSearchStatus().getStatuses();
  139 + } else if (query.getStatus() != null) {
  140 + statusSet = Collections.singleton(query.getStatus());
  141 + }
106 return DaoUtil.toPageData( 142 return DaoUtil.toPageData(
107 - alarmRepository.findAlarms( 143 + alarmRepository.findCustomerAlarms(
108 tenantId.getId(), 144 tenantId.getId(),
109 - affectedEntity.getId(),  
110 - affectedEntity.getEntityType().name(), 145 + customerId.getId(),
111 query.getPageLink().getStartTime(), 146 query.getPageLink().getStartTime(),
112 query.getPageLink().getEndTime(), 147 query.getPageLink().getEndTime(),
113 statusSet, 148 statusSet,
@@ -28,7 +28,7 @@ import java.util.UUID; @@ -28,7 +28,7 @@ import java.util.UUID;
28 28
29 public interface DeviceProfileRepository extends PagingAndSortingRepository<DeviceProfileEntity, UUID> { 29 public interface DeviceProfileRepository extends PagingAndSortingRepository<DeviceProfileEntity, UUID> {
30 30
31 - @Query("SELECT new org.thingsboard.server.common.data.DeviceProfileInfo(d.id, d.name, d.type, d.transportType) " + 31 + @Query("SELECT new org.thingsboard.server.common.data.DeviceProfileInfo(d.id, d.name, d.image, d.type, d.transportType) " +
32 "FROM DeviceProfileEntity d " + 32 "FROM DeviceProfileEntity d " +
33 "WHERE d.id = :deviceProfileId") 33 "WHERE d.id = :deviceProfileId")
34 DeviceProfileInfo findDeviceProfileInfoById(@Param("deviceProfileId") UUID deviceProfileId); 34 DeviceProfileInfo findDeviceProfileInfoById(@Param("deviceProfileId") UUID deviceProfileId);
@@ -39,14 +39,14 @@ public interface DeviceProfileRepository extends PagingAndSortingRepository<Devi @@ -39,14 +39,14 @@ public interface DeviceProfileRepository extends PagingAndSortingRepository<Devi
39 @Param("textSearch") String textSearch, 39 @Param("textSearch") String textSearch,
40 Pageable pageable); 40 Pageable pageable);
41 41
42 - @Query("SELECT new org.thingsboard.server.common.data.DeviceProfileInfo(d.id, d.name, d.type, d.transportType) " + 42 + @Query("SELECT new org.thingsboard.server.common.data.DeviceProfileInfo(d.id, d.name, d.image, d.type, d.transportType) " +
43 "FROM DeviceProfileEntity d WHERE " + 43 "FROM DeviceProfileEntity d WHERE " +
44 "d.tenantId = :tenantId AND LOWER(d.searchText) LIKE LOWER(CONCAT(:textSearch, '%'))") 44 "d.tenantId = :tenantId AND LOWER(d.searchText) LIKE LOWER(CONCAT(:textSearch, '%'))")
45 Page<DeviceProfileInfo> findDeviceProfileInfos(@Param("tenantId") UUID tenantId, 45 Page<DeviceProfileInfo> findDeviceProfileInfos(@Param("tenantId") UUID tenantId,
46 @Param("textSearch") String textSearch, 46 @Param("textSearch") String textSearch,
47 Pageable pageable); 47 Pageable pageable);
48 48
49 - @Query("SELECT new org.thingsboard.server.common.data.DeviceProfileInfo(d.id, d.name, d.type, d.transportType) " + 49 + @Query("SELECT new org.thingsboard.server.common.data.DeviceProfileInfo(d.id, d.name, d.image, d.type, d.transportType) " +
50 "FROM DeviceProfileEntity d WHERE " + 50 "FROM DeviceProfileEntity d WHERE " +
51 "d.tenantId = :tenantId AND d.transportType = :transportType AND LOWER(d.searchText) LIKE LOWER(CONCAT(:textSearch, '%'))") 51 "d.tenantId = :tenantId AND d.transportType = :transportType AND LOWER(d.searchText) LIKE LOWER(CONCAT(:textSearch, '%'))")
52 Page<DeviceProfileInfo> findDeviceProfileInfos(@Param("tenantId") UUID tenantId, 52 Page<DeviceProfileInfo> findDeviceProfileInfos(@Param("tenantId") UUID tenantId,
@@ -58,7 +58,7 @@ public interface DeviceProfileRepository extends PagingAndSortingRepository<Devi @@ -58,7 +58,7 @@ public interface DeviceProfileRepository extends PagingAndSortingRepository<Devi
58 "WHERE d.tenantId = :tenantId AND d.isDefault = true") 58 "WHERE d.tenantId = :tenantId AND d.isDefault = true")
59 DeviceProfileEntity findByDefaultTrueAndTenantId(@Param("tenantId") UUID tenantId); 59 DeviceProfileEntity findByDefaultTrueAndTenantId(@Param("tenantId") UUID tenantId);
60 60
61 - @Query("SELECT new org.thingsboard.server.common.data.DeviceProfileInfo(d.id, d.name, d.type, d.transportType) " + 61 + @Query("SELECT new org.thingsboard.server.common.data.DeviceProfileInfo(d.id, d.name, d.image, d.type, d.transportType) " +
62 "FROM DeviceProfileEntity d " + 62 "FROM DeviceProfileEntity d " +
63 "WHERE d.tenantId = :tenantId AND d.isDefault = true") 63 "WHERE d.tenantId = :tenantId AND d.isDefault = true")
64 DeviceProfileInfo findDefaultDeviceProfileInfo(@Param("tenantId") UUID tenantId); 64 DeviceProfileInfo findDefaultDeviceProfileInfo(@Param("tenantId") UUID tenantId);
@@ -118,7 +118,8 @@ CREATE TABLE IF NOT EXISTS dashboard ( @@ -118,7 +118,8 @@ CREATE TABLE IF NOT EXISTS dashboard (
118 assigned_customers varchar(1000000), 118 assigned_customers varchar(1000000),
119 search_text varchar(255), 119 search_text varchar(255),
120 tenant_id uuid, 120 tenant_id uuid,
121 - title varchar(255) 121 + title varchar(255),
  122 + image varchar(1000000)
122 ); 123 );
123 124
124 CREATE TABLE IF NOT EXISTS rule_chain ( 125 CREATE TABLE IF NOT EXISTS rule_chain (
@@ -182,6 +183,7 @@ CREATE TABLE IF NOT EXISTS device_profile ( @@ -182,6 +183,7 @@ CREATE TABLE IF NOT EXISTS device_profile (
182 created_time bigint NOT NULL, 183 created_time bigint NOT NULL,
183 name varchar(255), 184 name varchar(255),
184 type varchar(255), 185 type varchar(255),
  186 + image varchar(1000000),
185 transport_type varchar(255), 187 transport_type varchar(255),
186 provision_type varchar(255), 188 provision_type varchar(255),
187 profile_data jsonb, 189 profile_data jsonb,
@@ -136,7 +136,8 @@ CREATE TABLE IF NOT EXISTS dashboard ( @@ -136,7 +136,8 @@ CREATE TABLE IF NOT EXISTS dashboard (
136 assigned_customers varchar(1000000), 136 assigned_customers varchar(1000000),
137 search_text varchar(255), 137 search_text varchar(255),
138 tenant_id uuid, 138 tenant_id uuid,
139 - title varchar(255) 139 + title varchar(255),
  140 + image varchar(1000000)
140 ); 141 );
141 142
142 CREATE TABLE IF NOT EXISTS rule_chain ( 143 CREATE TABLE IF NOT EXISTS rule_chain (
@@ -201,6 +202,7 @@ CREATE TABLE IF NOT EXISTS device_profile ( @@ -201,6 +202,7 @@ CREATE TABLE IF NOT EXISTS device_profile (
201 created_time bigint NOT NULL, 202 created_time bigint NOT NULL,
202 name varchar(255), 203 name varchar(255),
203 type varchar(255), 204 type varchar(255),
  205 + image varchar(1000000),
204 transport_type varchar(255), 206 transport_type varchar(255),
205 provision_type varchar(255), 207 provision_type varchar(255),
206 profile_data jsonb, 208 profile_data jsonb,
@@ -333,7 +333,7 @@ public class BaseDeviceProfileServiceTest extends AbstractServiceTest { @@ -333,7 +333,7 @@ public class BaseDeviceProfileServiceTest extends AbstractServiceTest {
333 333
334 List<DeviceProfileInfo> deviceProfileInfos = deviceProfiles.stream() 334 List<DeviceProfileInfo> deviceProfileInfos = deviceProfiles.stream()
335 .map(deviceProfile -> new DeviceProfileInfo(deviceProfile.getId(), 335 .map(deviceProfile -> new DeviceProfileInfo(deviceProfile.getId(),
336 - deviceProfile.getName(), deviceProfile.getType(), deviceProfile.getTransportType())).collect(Collectors.toList()); 336 + deviceProfile.getName(), deviceProfile.getImage(), deviceProfile.getType(), deviceProfile.getTransportType())).collect(Collectors.toList());
337 337
338 Assert.assertEquals(deviceProfileInfos, loadedDeviceProfileInfos); 338 Assert.assertEquals(deviceProfileInfos, loadedDeviceProfileInfos);
339 339
@@ -57,6 +57,8 @@ public interface RuleEngineAlarmService { @@ -57,6 +57,8 @@ public interface RuleEngineAlarmService {
57 57
58 ListenableFuture<PageData<AlarmInfo>> findAlarms(TenantId tenantId, AlarmQuery query); 58 ListenableFuture<PageData<AlarmInfo>> findAlarms(TenantId tenantId, AlarmQuery query);
59 59
  60 + ListenableFuture<PageData<AlarmInfo>> findCustomerAlarms(TenantId tenantId, CustomerId customerId, AlarmQuery query);
  61 +
60 AlarmSeverity findHighestAlarmSeverity(TenantId tenantId, EntityId entityId, AlarmSearchStatus alarmSearchStatus, AlarmStatus alarmStatus); 62 AlarmSeverity findHighestAlarmSeverity(TenantId tenantId, EntityId entityId, AlarmSearchStatus alarmSearchStatus, AlarmStatus alarmStatus);
61 63
62 PageData<AlarmData> findAlarmDataByQueryForEntities(TenantId tenantId, CustomerId customerId, AlarmDataQuery query, Collection<EntityId> orderedEntityIds); 64 PageData<AlarmData> findAlarmDataByQueryForEntities(TenantId tenantId, CustomerId customerId, AlarmDataQuery query, Collection<EntityId> orderedEntityIds);
@@ -60,6 +60,11 @@ @@ -60,6 +60,11 @@
60 {{ 'device-profile.type-required' | translate }} 60 {{ 'device-profile.type-required' | translate }}
61 </mat-error> 61 </mat-error>
62 </mat-form-field> 62 </mat-form-field>
  63 + <tb-image-input fxFlex
  64 + label="{{'device-profile.image' | translate}}"
  65 + maxSizeByte="524288"
  66 + formControlName="image">
  67 + </tb-image-input>
63 <mat-form-field class="mat-block"> 68 <mat-form-field class="mat-block">
64 <mat-label translate>device-profile.description</mat-label> 69 <mat-label translate>device-profile.description</mat-label>
65 <textarea matInput formControlName="description" rows="2"></textarea> 70 <textarea matInput formControlName="description" rows="2"></textarea>
@@ -106,6 +106,7 @@ export class AddDeviceProfileDialogComponent extends @@ -106,6 +106,7 @@ export class AddDeviceProfileDialogComponent extends
106 { 106 {
107 name: [data.deviceProfileName, [Validators.required]], 107 name: [data.deviceProfileName, [Validators.required]],
108 type: [DeviceProfileType.DEFAULT, [Validators.required]], 108 type: [DeviceProfileType.DEFAULT, [Validators.required]],
  109 + image: [null, []],
109 defaultRuleChainId: [null, []], 110 defaultRuleChainId: [null, []],
110 defaultQueueName: ['', []], 111 defaultQueueName: ['', []],
111 description: ['', []] 112 description: ['', []]
@@ -183,6 +184,7 @@ export class AddDeviceProfileDialogComponent extends @@ -183,6 +184,7 @@ export class AddDeviceProfileDialogComponent extends
183 const deviceProfile: DeviceProfile = { 184 const deviceProfile: DeviceProfile = {
184 name: this.deviceProfileDetailsFormGroup.get('name').value, 185 name: this.deviceProfileDetailsFormGroup.get('name').value,
185 type: this.deviceProfileDetailsFormGroup.get('type').value, 186 type: this.deviceProfileDetailsFormGroup.get('type').value,
  187 + image: this.deviceProfileDetailsFormGroup.get('image').value,
186 transportType: this.transportConfigFormGroup.get('transportType').value, 188 transportType: this.transportConfigFormGroup.get('transportType').value,
187 provisionType: deviceProvisionConfiguration.type, 189 provisionType: deviceProvisionConfiguration.type,
188 provisionDeviceKey, 190 provisionDeviceKey,
@@ -86,6 +86,11 @@ @@ -86,6 +86,11 @@
86 {{ 'device-profile.type-required' | translate }} 86 {{ 'device-profile.type-required' | translate }}
87 </mat-error> 87 </mat-error>
88 </mat-form-field> 88 </mat-form-field>
  89 + <tb-image-input fxFlex
  90 + label="{{'device-profile.image' | translate}}"
  91 + maxSizeByte="524288"
  92 + formControlName="image">
  93 + </tb-image-input>
89 <mat-form-field class="mat-block"> 94 <mat-form-field class="mat-block">
90 <mat-label translate>device-profile.description</mat-label> 95 <mat-label translate>device-profile.description</mat-label>
91 <textarea matInput formControlName="description" rows="2"></textarea> 96 <textarea matInput formControlName="description" rows="2"></textarea>
@@ -103,6 +103,7 @@ export class DeviceProfileComponent extends EntityComponent<DeviceProfile> { @@ -103,6 +103,7 @@ export class DeviceProfileComponent extends EntityComponent<DeviceProfile> {
103 { 103 {
104 name: [entity ? entity.name : '', [Validators.required]], 104 name: [entity ? entity.name : '', [Validators.required]],
105 type: [entity ? entity.type : null, [Validators.required]], 105 type: [entity ? entity.type : null, [Validators.required]],
  106 + image: [entity ? entity.image : null],
106 transportType: [entity ? entity.transportType : null, [Validators.required]], 107 transportType: [entity ? entity.transportType : null, [Validators.required]],
107 profileData: this.fb.group({ 108 profileData: this.fb.group({
108 configuration: [entity && !this.isAdd ? entity.profileData?.configuration : {}, Validators.required], 109 configuration: [entity && !this.isAdd ? entity.profileData?.configuration : {}, Validators.required],
@@ -178,6 +179,7 @@ export class DeviceProfileComponent extends EntityComponent<DeviceProfile> { @@ -178,6 +179,7 @@ export class DeviceProfileComponent extends EntityComponent<DeviceProfile> {
178 }; 179 };
179 this.entityForm.patchValue({name: entity.name}); 180 this.entityForm.patchValue({name: entity.name});
180 this.entityForm.patchValue({type: entity.type}, {emitEvent: false}); 181 this.entityForm.patchValue({type: entity.type}, {emitEvent: false});
  182 + this.entityForm.patchValue({image: entity.image}, {emitEvent: false});
181 this.entityForm.patchValue({transportType: entity.transportType}, {emitEvent: false}); 183 this.entityForm.patchValue({transportType: entity.transportType}, {emitEvent: false});
182 this.entityForm.patchValue({provisionType: entity.provisionType}, {emitEvent: false}); 184 this.entityForm.patchValue({provisionType: entity.provisionType}, {emitEvent: false});
183 this.entityForm.patchValue({provisionDeviceKey: entity.provisionDeviceKey}, {emitEvent: false}); 185 this.entityForm.patchValue({provisionDeviceKey: entity.provisionDeviceKey}, {emitEvent: false});
@@ -105,6 +105,11 @@ @@ -105,6 +105,11 @@
105 {{ 'dashboard.title-required' | translate }} 105 {{ 'dashboard.title-required' | translate }}
106 </mat-error> 106 </mat-error>
107 </mat-form-field> 107 </mat-form-field>
  108 + <tb-image-input fxFlex
  109 + label="{{'dashboard.image' | translate}}"
  110 + maxSizeByte="524288"
  111 + formControlName="image">
  112 + </tb-image-input>
108 <div formGroupName="configuration" fxLayout="column"> 113 <div formGroupName="configuration" fxLayout="column">
109 <mat-form-field class="mat-block"> 114 <mat-form-field class="mat-block">
110 <mat-label translate>dashboard.description</mat-label> 115 <mat-label translate>dashboard.description</mat-label>
@@ -80,6 +80,7 @@ export class DashboardFormComponent extends EntityComponent<Dashboard> { @@ -80,6 +80,7 @@ export class DashboardFormComponent extends EntityComponent<Dashboard> {
80 return this.fb.group( 80 return this.fb.group(
81 { 81 {
82 title: [entity ? entity.title : '', [Validators.required]], 82 title: [entity ? entity.title : '', [Validators.required]],
  83 + image: [entity ? entity.image : null],
83 configuration: this.fb.group( 84 configuration: this.fb.group(
84 { 85 {
85 description: [entity && entity.configuration ? entity.configuration.description : ''], 86 description: [entity && entity.configuration ? entity.configuration.description : ''],
@@ -92,6 +93,7 @@ export class DashboardFormComponent extends EntityComponent<Dashboard> { @@ -92,6 +93,7 @@ export class DashboardFormComponent extends EntityComponent<Dashboard> {
92 updateForm(entity: Dashboard) { 93 updateForm(entity: Dashboard) {
93 this.updateFields(entity); 94 this.updateFields(entity);
94 this.entityForm.patchValue({title: entity.title}); 95 this.entityForm.patchValue({title: entity.title});
  96 + this.entityForm.patchValue({image: entity.image});
95 this.entityForm.patchValue({configuration: {description: entity.configuration ? entity.configuration.description : ''}}); 97 this.entityForm.patchValue({configuration: {description: entity.configuration ? entity.configuration.description : ''}});
96 } 98 }
97 99
@@ -21,10 +21,10 @@ @@ -21,10 +21,10 @@
21 [flowConfig]="{singleFile: true, allowDuplicateUploads: true}"> 21 [flowConfig]="{singleFile: true, allowDuplicateUploads: true}">
22 <div class="tb-image-select-container"> 22 <div class="tb-image-select-container">
23 <div *ngIf="showPreview" class="tb-image-preview-container"> 23 <div *ngIf="showPreview" class="tb-image-preview-container">
24 - <div *ngIf="!safeImageUrl;else elseBlock" translate>dashboard.no-image</div> 24 + <div *ngIf="!safeImageUrl; else elseBlock">{{ (disabled ? 'dashboard.empty-image' : 'dashboard.no-image') | translate }}</div>
25 <ng-template #elseBlock><img class="tb-image-preview" [src]="safeImageUrl" /></ng-template> 25 <ng-template #elseBlock><img class="tb-image-preview" [src]="safeImageUrl" /></ng-template>
26 </div> 26 </div>
27 - <div *ngIf="showClearButton" class="tb-image-clear-container"> 27 + <div *ngIf="showClearButton && !disabled" class="tb-image-clear-container">
28 <button mat-button mat-icon-button color="primary" 28 <button mat-button mat-icon-button color="primary"
29 type="button" 29 type="button"
30 (click)="clearImage()" 30 (click)="clearImage()"
@@ -34,7 +34,7 @@ @@ -34,7 +34,7 @@
34 <mat-icon>close</mat-icon> 34 <mat-icon>close</mat-icon>
35 </button> 35 </button>
36 </div> 36 </div>
37 - <div class="drop-area tb-flow-drop" 37 + <div *ngIf="!disabled" class="drop-area tb-flow-drop"
38 flowDrop 38 flowDrop
39 [flow]="flow.flowJs"> 39 [flow]="flow.flowJs">
40 <label for="{{inputId}}" translate>dashboard.drop-image</label> 40 <label for="{{inputId}}" translate>dashboard.drop-image</label>
@@ -42,5 +42,5 @@ @@ -42,5 +42,5 @@
42 </div> 42 </div>
43 </div> 43 </div>
44 </ng-container> 44 </ng-container>
45 - <div class="tb-hint" *ngIf="maxSizeByte" translate [translateParams]="{ size: maxSizeByte | fileSize}">dashboard.maximum-upload-file-size</div> 45 + <div class="tb-hint" *ngIf="maxSizeByte && !disabled" translate [translateParams]="{ size: maxSizeByte | fileSize}">dashboard.maximum-upload-file-size</div>
46 </div> 46 </div>
@@ -26,6 +26,7 @@ import { Filters } from '@shared/models/query/query.models'; @@ -26,6 +26,7 @@ import { Filters } from '@shared/models/query/query.models';
26 export interface DashboardInfo extends BaseData<DashboardId> { 26 export interface DashboardInfo extends BaseData<DashboardId> {
27 tenantId?: TenantId; 27 tenantId?: TenantId;
28 title?: string; 28 title?: string;
  29 + image?: string;
29 assignedCustomers?: Array<ShortCustomerInfo>; 30 assignedCustomers?: Array<ShortCustomerInfo>;
30 } 31 }
31 32
@@ -492,6 +492,7 @@ export interface DeviceProfile extends BaseData<DeviceProfileId> { @@ -492,6 +492,7 @@ export interface DeviceProfile extends BaseData<DeviceProfileId> {
492 description?: string; 492 description?: string;
493 default?: boolean; 493 default?: boolean;
494 type: DeviceProfileType; 494 type: DeviceProfileType;
  495 + image?: string;
495 transportType: DeviceTransportType; 496 transportType: DeviceTransportType;
496 provisionType: DeviceProvisionType; 497 provisionType: DeviceProvisionType;
497 provisionDeviceKey?: string; 498 provisionDeviceKey?: string;
@@ -666,6 +666,7 @@ @@ -666,6 +666,7 @@
666 "no-widgets": "No widgets configured", 666 "no-widgets": "No widgets configured",
667 "add-widget": "Add new widget", 667 "add-widget": "Add new widget",
668 "title": "Title", 668 "title": "Title",
  669 + "image": "Dashboard image",
669 "select-widget-title": "Select widget", 670 "select-widget-title": "Select widget",
670 "select-widget-value": "{{title}}: select widget", 671 "select-widget-value": "{{title}}: select widget",
671 "select-widget-subtitle": "List of available widget types", 672 "select-widget-subtitle": "List of available widget types",
@@ -712,6 +713,7 @@ @@ -712,6 +713,7 @@
712 "background-image": "Background image", 713 "background-image": "Background image",
713 "background-size-mode": "Background size mode", 714 "background-size-mode": "Background size mode",
714 "no-image": "No image selected", 715 "no-image": "No image selected",
  716 + "empty-image": "No image",
715 "drop-image": "Drop an image or click to select a file to upload.", 717 "drop-image": "Drop an image or click to select a file to upload.",
716 "maximum-upload-file-size": "Maximum upload file size: {{ size }}", 718 "maximum-upload-file-size": "Maximum upload file size: {{ size }}",
717 "cannot-upload-file": "Cannot upload file", 719 "cannot-upload-file": "Cannot upload file",
@@ -1028,6 +1030,7 @@ @@ -1028,6 +1030,7 @@
1028 "type": "Profile type", 1030 "type": "Profile type",
1029 "type-required": "Profile type is required.", 1031 "type-required": "Profile type is required.",
1030 "type-default": "Default", 1032 "type-default": "Default",
  1033 + "image": "Device profile image",
1031 "transport-type": "Transport type", 1034 "transport-type": "Transport type",
1032 "transport-type-required": "Transport type is required.", 1035 "transport-type-required": "Transport type is required.",
1033 "transport-type-default": "Default", 1036 "transport-type-default": "Default",